使用Pytorch从零开始构建CGAN (conditional GAN)

news2025/1/12 20:03:25

GAN和DCGAN生成随机图像。因此,我们几乎无法控制生成哪些图像。然而,CGAN 可以让我们指定一个条件,以便我们可以告诉它要生成哪些图像。诀窍是使用可学习层将标签值转换为特征向量,以便生成器可以学习要生成什么图像。鉴别器还利用标签条件。现阶段您可能还不清楚,但不用担心。本文将通过简单的代码介绍整个过程的工作原理。

条件作为特征向量

标签到 One-hot 编码标签

我们将使用包含许多数字图像的 MNIST 数据集。标签是 0 到 9 之间的整数(含 0 和 9)。通过将标签转换为特征向量,我们可以将目标标签(作为条件)输入到生成器和随机值向量中,以便生成的图像具有一些变化。

首先,我们使用 PyTorch F.one_hot 将数字转换为 one-hot 编码。

import torch
from torch.nn import functional as F

# Labels (i.e., 1 and 3)
labels = torch.LongTensor([1, 3])

# Create one-hot encoded labels
encoded = F.one_hot(labels, num_classes=10)

print(encoded)

输出是:

tensor([[0, 1, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 1, 0, 0, 0, 0, 0, 0]])

因此,我们将 1 和 3 编码为具有 10 个元素的向量。我们指定num_classes=10标签是从 0 到 9(10 个数字),并且我们需要 10 个元素来唯一标识 one-hot 编码中的每个数字。我们使用 one-hot 编码,因为数字的值没有像等级或顺序这样的含义。它是一个类(分类)值,使用 one-hot 编码来表示分类值是机器学习中的一种行之有效的做法。

特征向量的 One-hot 编码标签

生成器将学习从 one-hot 编码标签中提取特征(条件)。我们将创建一个类,通过全连接层将单热编码标签转换为特征向量。生成器和鉴别器都将使用该类。

# Coverts conditions into feature vectors
class Condition(nn.Module):
    def __init__(self, alpha: float):
        super().__init__()

        # From one-hot encoding to features: 10 => 784
        self.fc = nn.Sequential(
            nn.Linear(10, 784),
            nn.BatchNorm1d(784),
            nn.LeakyReLU(alpha))

    def forward(self, labels: torch.Tensor):
        # One-hot encode labels
        x = F.one_hot(labels, num_classes=10)

        # From Long to Float
        x = x.float()

        # To feature vectors
        return self.fc(x)

Reshape Helper

我们定义了一个辅助类,用于重塑生成器和鉴别器中的编码条件值。

# Reshape helper
class Reshape(nn.Module):
    def __init__(self, *shape):
        super().__init__()

        self.shape = shape

    def forward(self, x):
        return x.reshape(-1, *self.shape)

生成器网络定义

该生成器与我们使用的 DCGAN 生成器类似。和之前一样,我们生成随机值向量,并使用全连接层将维度增加到 784。我们还使用条件层将输入标签转换为 784 维度的特征向量。然后,我们对随机向量和标签特征求和。这是一个逐元素的加法运算。我们可以这样做,因为随机值和标签特征向量具有相同数量的元素。

生成器网络处理随机值向量和标签特征向量的总和,以生成指定标签类别的随机图像。让我们看看如何将该Condition类集成到 DCGAN 生成器类中。

# Generator network
class Generator(nn.Module):
    def __init__(self, sample_size: int, alpha: float):
        super().__init__()

        # sample_size => 784
        self.fc = nn.Sequential(
            nn.Linear(sample_size, 784),
            nn.BatchNorm1d(784),
            nn.LeakyReLU(alpha))

        # 784 => 16 x 7 x 7
        self.reshape = Reshape(16, 7, 7)

        # 16 x 7 x 7 => 32 x 14 x 14
        self.conv1 = nn.Sequential(
            nn.ConvTranspose2d(16, 32,
                               kernel_size=5, stride=2, padding=2,
                               output_padding=1, bias=False),
            nn.BatchNorm2d(32),
            nn.LeakyReLU(alpha))

        # 32 x 14 x 14 => 1 x 28 x 28
        self.conv2 = nn.Sequential(
            nn.ConvTranspose2d(32, 1,
                               kernel_size=5, stride=2, padding=2,
                               output_padding=1, bias=False),
            nn.Sigmoid())

        # Random value sample size
        self.sample_size = sample_size

        # To convert labels into feature vectors
        self.cond = Condition(alpha)

    def forward(self, labels: torch.Tensor):
        # Labels as feature vectors
        c = self.cond(labels)

        # Batch size is the number of labels
        batch_size = len(labels)

        # Generate random inputs
        z = torch.randn(batch_size, self.sample_size)

        # Inputs are the sum of random inputs and label features
        x = self.fc(z)        # => 784
        x = self.reshape(x+c) # => 16 x 7 x 7
        x = self.conv1(x)     # => 32 x 14 x 14
        x = self.conv2(x)     # => 1 x 28 x 28
        return x

正如您所看到的,代码将标签转换为与随机值向量相同维度的特征向量,并执行逐元素加法操作(x+c)。换句话说,随机值和标签特征向量表达在同一空间中生成图像所需的信息。

我们来详细看看这部分代码。

# Label feature vectors (784)
c = self.cond(labels)

# Random value vectors (784)
z = torch.randn(batch_size, self.sample_size)
x = self.fc(z)

# Element-wise addition and reshape from 784 into 16x7x7
x = self.reshape(x+c)

如果我们没有随机值向量并且仅使用标签特征向量来训练生成器,它将学习为每个标签输入生成一张图像。拥有随机值向量对于向生成的图像添加变化至关重要。

在上面的代码中,我们使用了逐元素加法运算,但这并不是组合随机值向量和标签特征向量的唯一方法。我们可以将两个向量连接成一个。在这种情况下,我们不需要使两个向量具有相同数量的元素。或者,我们可以连接单热编码标签和随机值向量,并通过全连接层将它们提供给生成输入特征。我们需要调整全连接层中的参数数量以适应不同的输入向量大小。

在本文中,我们使用逐元素加法操作,因为它实现起来很简单,但您可能想尝试其他方法来看看它是如何工作的。

鉴别器网络定义

我们使用Condition鉴别器网络内的类来根据给定条件预测输入图像是真实的还是虚假的。例如,当条件指示图像是数字3时,鉴别器分类该图像是否是数字“3”的真实图像。与生成器一样,鉴别器也有条件层,可以通过训练学习为每个标签生成特征。

# Discriminator network
class Discriminator(nn.Module):
    def __init__(self, alpha: float):
        super().__init__()
        
        # 1 x 28 x 28 => 32 x 14 x 14
        self.conv1 = nn.Sequential(
            nn.Conv2d(1, 32,
                      kernel_size=5, stride=2, padding=2, bias=False),
            nn.LeakyReLU(alpha))

        # 32 x 14 x 14 => 16 x 7 x 7
        self.conv2 = nn.Sequential(
            nn.Conv2d(32, 16, 
                      kernel_size=5, stride=2, padding=2, bias=False),
            nn.BatchNorm2d(16),
            nn.LeakyReLU(alpha))

        # 16 x 7 x 7 => 784
        self.fc = nn.Sequential(
            nn.Flatten(),
            nn.Linear(784, 784),
            nn.BatchNorm1d(784),
            nn.LeakyReLU(alpha),
            nn.Linear(784, 1))

        # Reshape label features: 784 => 16 x 7 x 7 
        self.cond = nn.Sequential(
            Condition(alpha),
            Reshape(16, 7, 7))

    def forward(self, images: torch.Tensor,
                      labels: torch.Tensor,
                      targets: torch.Tensor):
        # Label features
        c = self.cond(labels)

        # Image features + Label features => real or fake?
        x = self.conv1(images)    # => 32 x 14 x 14
        x = self.conv2(x)         # => 16 x 7 x 7
        prediction = self.fc(x+c) # => 1

        loss = F.binary_cross_entropy_with_logits(prediction, targets)
        return loss

CGAN训练

在训练时,我们将标签提供给鉴别器和生成器。每个网络都会为给定的标签生成对其目标有用的特征。CGAN 训练循​​环轮流训练鉴别器和生成器。

鉴别器训练

它与 DCGAN 判别器训练相同,只是我们提供标签。

# Train loop
for epoch in range(100):

    d_losses = []
    g_losses = []

    for images, labels in tqdm(dataloader):

        #===============================
        # Disciminator Network Training
        #===============================

        # Images from MNIST are considered as real
        d_loss = discriminator(images, labels, real_targets)

        # Images from Generator are considered as fake
        d_loss += discriminator(generator(labels), labels, fake_targets)

        # Discriminator paramter update
        d_optimizer.zero_grad()
        d_loss.backward()
        d_optimizer.step()

        ...

生成器训练

它与 DCGAN 生成器训练相同,只是我们提供标签。

# Training Loop
for epoch in range(100):

    for images, labels in tqdm(dataloader):

        ...

        #===============================
        # Generator Network Training
        #===============================

        # Images from Generator should be as real as ones from MNIST
        g_loss = discriminator(generator(labels), labels, true_targets)

        ...

完整的源代码可在本文末尾找到。现在,让我们看看训练的结果。

训练结果

测试图像生成

在每个epoch之后,我们使用以下代码为从 0 到 9 的每个数字生成八张图像。

# 0 to 9 in a list
labels = list(range(10))

# Convert to long tensor
labels = torch.LongTensor(labels)

# Repeat each digit eight times
labels = labels.repeat(8)

# Flatten (10x8 => 80)
labels = labels.flatten()

# Generate 80 images
generated_images = generator(labels)

# Save the results in a grid layout
save_image_grid(epoch, generated_images, ncol=10)

第 1 epoch

在第一个epoch之后,生成的图像看起来像根据条件的数字。在我看来,添加条件使网络更容易学习。
在这里插入图片描述

第 50 epoch

我想说,输出看起来已经令人满意了。
在这里插入图片描述

第 100 epoch

与 Epoch 50 相比没有太大区别。它可能会稍微好看一些。很难说。无论如何,训练成功了,因为生成器可以根据给定条件生成各种图像。
在这里插入图片描述
CGAN 在生成针对特定图像的合成训练数据方面非常有用。

CGAN 为何有效

因此,CGAN 与 DCGAN 相同,只是将标签特征添加到输入向量中。仅此而已,但它会根据给定的标签条件生成。为什么它会这么有效?

生成器和鉴别器不共享其Condition层,因此每个网络都独立且对抗地学习。生成器尝试生成尽可能真实的图像,使得判别器计算的损失变得更小。在此过程中,该Condition层必须学会尽可能区分不同的标签特征,因为知道生成什么以实现更低的损失取决于预测它应该生成什么数字。鉴别器Condition层还学习区分不同的数字,使二元分类(真或假)决策变得更容易。因此,区分生成器和鉴别器的标签输入至关重要。

例如,当标签为数字 1 时,生成器必须生成尽可能真实的 1 图像(尽可能像 MNIST 图像),如果生成的图像不符合,则判别器需要给生成器带来显着的损失。看起来根本就像数字“1”。从生成器的角度来看,鉴别器是一个也从输入中学习的损失函数。

因此,只要我们对所有 0 到 9 位数字的生成器和鉴别器进行同样的训练,生成器就可以理解为给定标签生成真实(类似 MNIST)图像的条件。

源代码

源代码与DCGAN几乎相同,只是我们现在有了条件处理代码。

import numpy as np
import matplotlib.pyplot as plt
import torch
from torch import nn
from torch.nn import functional as F
from torch.utils.data import DataLoader
from torchvision import datasets, transforms
from torchvision.utils import make_grid
from tqdm import tqdm

# Common config
batch_size  = 64

# Generator config
sample_size = 100    # Random sample size
g_alpha     = 0.01   # LeakyReLU alpha
g_lr        = 1.0e-4 # Learning rate

# Discriminator config
d_alpha     = 0.01   # LeakyReLU alpha
d_lr        = 1.0e-4 # Learning rate

# Data Loader for MNIST
transform = transforms.ToTensor()
dataset = datasets.MNIST(root='./data', train=True, download=True, transform=transform)
dataloader = DataLoader(dataset, batch_size=batch_size, drop_last=True)

# Coverts conditions into feature vectors
class Condition(nn.Module):
    def __init__(self, alpha: float):
        super().__init__()

        # From one-hot encoding to features: 10 => 784
        self.fc = nn.Sequential(
            nn.Linear(10, 784),
            nn.BatchNorm1d(784),
            nn.LeakyReLU(alpha))
        
    def forward(self, labels: torch.Tensor):
        # One-hot encode labels
        x = F.one_hot(labels, num_classes=10)

        # From Long to Float
        x = x.float()

        # To feature vectors
        return self.fc(x)

# Reshape helper
class Reshape(nn.Module):
    def __init__(self, *shape):
        super().__init__()

        self.shape = shape

    def forward(self, x):
        return x.reshape(-1, *self.shape)

# Generator network
class Generator(nn.Module):
    def __init__(self, sample_size: int, alpha: float):
        super().__init__()

        # sample_size => 784
        self.fc = nn.Sequential(
            nn.Linear(sample_size, 784),
            nn.BatchNorm1d(784),
            nn.LeakyReLU(alpha))

        # 784 => 16 x 7 x 7 
        self.reshape = Reshape(16, 7, 7)

        # 16 x 7 x 7 => 32 x 14 x 14
        self.conv1 = nn.Sequential(
            nn.ConvTranspose2d(16, 32,
                               kernel_size=5, stride=2, padding=2,
                               output_padding=1, bias=False),
            nn.BatchNorm2d(32),
            nn.LeakyReLU(alpha))

        # 32 x 14 x 14 => 1 x 28 x 28
        self.conv2 = nn.Sequential(
            nn.ConvTranspose2d(32, 1,
                               kernel_size=5, stride=2, padding=2,
                               output_padding=1, bias=False),
            nn.Sigmoid())
            
        # Random value sample size
        self.sample_size = sample_size

        # To convert labels into feature vectors
        self.cond = Condition(alpha)

    def forward(self, labels: torch.Tensor):
        # Labels as feature vectors
        c = self.cond(labels)

        # Batch size is the number of labels
        batch_size = len(labels)

        # Generate random inputs
        z = torch.randn(batch_size, self.sample_size)

        # Inputs are the sum of random inputs and label features
        x = self.fc(z)        # => 784
        x = self.reshape(x+c) # => 16 x 7 x 7
        x = self.conv1(x)     # => 32 x 14 x 14
        x = self.conv2(x)     # => 1 x 28 x 28
        return x

# Discriminator network
class Discriminator(nn.Module):
    def __init__(self, alpha: float):
        super().__init__()
        
        # 1 x 28 x 28 => 32 x 14 x 14
        self.conv1 = nn.Sequential(
            nn.Conv2d(1, 32,
                      kernel_size=5, stride=2, padding=2, bias=False),
            nn.LeakyReLU(alpha))

        # 32 x 14 x 14 => 16 x 7 x 7
        self.conv2 = nn.Sequential(
            nn.Conv2d(32, 16, 
                      kernel_size=5, stride=2, padding=2, bias=False),
            nn.BatchNorm2d(16),
            nn.LeakyReLU(alpha))

        # 16 x 7 x 7 => 784
        self.fc = nn.Sequential(
            nn.Flatten(),
            nn.Linear(784, 784),
            nn.BatchNorm1d(784),
            nn.LeakyReLU(alpha),
            nn.Linear(784, 1))

        # Reshape label features: 784 => 16 x 7 x 7 
        self.cond = nn.Sequential(
            Condition(alpha),
            Reshape(16, 7, 7))

    def forward(self, images: torch.Tensor,
                      labels: torch.Tensor,
                      targets: torch.Tensor):
        # Label features
        c = self.cond(labels)

        # Image features + Label features => real or fake?
        x = self.conv1(images)    # => 32 x 14 x 14
        x = self.conv2(x)         # => 16 x 7 x 7
        prediction = self.fc(x+c) # => 1

        loss = F.binary_cross_entropy_with_logits(prediction, targets)
        return loss

# To save grid images
def save_image_grid(epoch: int, images: torch.Tensor, ncol: int):
    image_grid = make_grid(images, ncol)     # Into a grid
    image_grid = image_grid.permute(1, 2, 0) # Channel to last
    image_grid = image_grid.cpu().numpy()    # Into Numpy

    plt.imshow(image_grid)
    plt.xticks([])
    plt.yticks([])
    plt.savefig(f'generated_{epoch:03d}.jpg')
    plt.close()

# Real / Fake targets
real_targets = torch.ones(batch_size, 1)
fake_targets = torch.zeros(batch_size, 1)

# Generator and discriminator
generator = Generator(sample_size, g_alpha)
discriminator = Discriminator(d_alpha)

# Optimizers
d_optimizer = torch.optim.Adam(discriminator.parameters(), lr=d_lr)
g_optimizer = torch.optim.Adam(generator.parameters(), lr=g_lr)

# Train loop
for epoch in range(100):

    d_losses = []
    g_losses = []

    for images, labels in tqdm(dataloader):

        #===============================
        # Disciminator Network Training
        #===============================

        # Images from MNIST are considered as real
        d_loss = discriminator(images, labels, real_targets)
       
        # Images from Generator are considered as fake
        d_loss += discriminator(generator(labels), labels, fake_targets)

        # Discriminator paramter update
        d_optimizer.zero_grad()
        d_loss.backward()
        d_optimizer.step()

        #===============================
        # Generator Network Training
        #===============================

        # Images from Generator should be as real as ones from MNIST
        g_loss = discriminator(generator(labels), labels, real_targets)

        # Generator parameter update
        g_optimizer.zero_grad()
        g_loss.backward()
        g_optimizer.step()

        # Keep losses for logging
        d_losses.append(d_loss.item())
        g_losses.append(g_loss.item())
        
    # Print loss
    print(epoch, np.mean(d_losses), np.mean(g_losses))

    # Save generated images
    labels = torch.LongTensor(list(range(10))).repeat(8).flatten()
    save_image_grid(epoch, generator(labels), ncol=10)

参考文献

条件生成对抗网络, 2014年。

本文译自KikabeN博文

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1240460.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

时序预测 | MATLAB实现基于BiLSTM-AdaBoost双向长短期记忆网络结合AdaBoost时间序列预测

时序预测 | MATLAB实现基于BiLSTM-AdaBoost双向长短期记忆网络结合AdaBoost时间序列预测 目录 时序预测 | MATLAB实现基于BiLSTM-AdaBoost双向长短期记忆网络结合AdaBoost时间序列预测预测效果基本介绍模型描述程序设计参考资料 预测效果 基本介绍 1.Matlab实现BiLSTM-Adaboost…

【OpenCV实现图像:使用OpenCV进行图像处理之透视变换】

文章目录 概要计算公式举个栗子实际应用小结 概要 透视变换(Perspective Transformation)是一种图像处理中常用的变换手段,它用于将图像从一个视角映射到另一个视角,常被称为投影映射。透视变换可以用于矫正图像中的透视畸变&…

ChatGPT规模化服务的经验与教训

2022年11月30日,OpenAI发布ChatGPT,以很多人未曾预料的速度迅速走红。与此同时,由于短时间内用户量的暴涨,导致服务器过载,迫使OpenAI停止新用户的注册。 ChatGPT发布这一年,同样的情景发生了好几次。在最近…

【追求卓越12】算法--堆排序

引导 前面几节,我们介绍了有关树的数据结构,我们继续来介绍一种树结构——堆。堆的应用场景有很多,比如从大量数据中找出top n的数据;根据优先级处理网络请求;这些情景都可以使用堆数据结构来实现。 什么是堆&#xf…

[论文笔记] Scaling Laws for Neural Language Models

概览: 一、总结 计算量、数据集大小、模型参数量大小的幂律 与 训练损失呈现 线性关系。 三个参数同时放大时,如何得到最佳的性能? 更大的模型 需要 更少的样本 就能达到相同的效果。 </

Doris数据模型的选择建议(十三)

Doris 的数据模型主要分为 3 类&#xff1a;Aggregate、Uniq、Duplicate Aggregate: Doris 数据模型-Aggregate 模型 Uniq&#xff1a;Doris 数据模型-Uniq 模型 Duplicate&#xff1a;Doris 数据模型-Duplicate 模型 因为数据模型在建表时就已经确定&#xff0c;且无法修改…

鸿蒙4.0开发笔记之DevEco Studio页面操作router的pushUrl页面跳转与back返回上一页(五)

一、认识组件 关于HarmonyOS中ArkTS的基础组件请参见文章鸿蒙4.0开发笔记之ArkTs语言基础与基本组件结构&#xff08;四&#xff09; 二、实现页面跳转pushUrl 1、操作说明 实现页面跳转的核心便是router.pushUrl的调用&#xff0c;操作起来也很简单&#xff0c;总共就四步…

CSDN等级权益概览

文章目录 一、[权益概览](https://blog.csdn.net/SoftwareTeacher/article/details/114499372)二、权益详情&#xff08;更新中...&#xff09;2.1、等级权益2.2、原创保护2.3、推广管理2.4、博客皮肤 一、权益概览 级别对应分数解释权益未定级0这类用户没有做任何贡献。或者曾…

【done+重点】剑指Offer56-I:找出数组中2个只出现1次的整数

力扣&#xff0c;https://leetcode.cn/problems/shu-zu-zhong-shu-zi-chu-xian-de-ci-shu-lcof/description/ 题目&#xff1a;一个整型数组nums里除两个数字之外&#xff0c;其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n)&#xff0c;空间…

二叉搜索树java实现

顾名思义&#xff0c;二叉搜索树是一棵二叉树&#xff0c;每个节点就是一个对象&#xff0c;这个对象包含属性left、right和parent。left指向节点的左孩子&#xff0c;right指向节点的右孩子&#xff0c;parent指向节点的父节点&#xff08;双亲&#xff09;。如果某个孩子节点…

《安富莱嵌入式周报》第327期:Cortex-A7所有外设单片机玩法LL/HAL库全面上线,分享三款GUI, PX5 RTOS推出网络协议栈,小米Vela开源

周报汇总地址&#xff1a;嵌入式周报 - uCOS & uCGUI & emWin & embOS & TouchGFX & ThreadX - 硬汉嵌入式论坛 - Powered by Discuz! 1、2023 Hackaday大赛胸牌开源 Vectorscope-main.zip (66.83MB) GitHub - Hack-a-Day/Vectorscope: Vectorscope badg…

Android Spannable 使用​注意事项

1、当前示例中间的 "评论"&#xff0c;使用SpannableStringBuilder实现&#xff0c;点击评论会有高亮效果加粗&#xff0c;但再点击其它Bar时无法恢复默认样式。 2、因为SpannableString或SpannableStringBuilder中的效果是叠加的&#xff0c;恢复默认样式需要先移除…

如何用java的虚拟线程连接数据库

我觉得这个很简单 首先确保你idea支持jdk21. 然后把idea编译成的目标字节码设置为21版本的 然后编写代码。 创建虚拟线程的方式有&#xff1a; Runnable runnable () -> {System.out.println("Hello, world!"); };// 创建虚拟线程 Thread virtualThread Thre…

前缀和及差分数组

前缀和 原数组x0x1x2x3x4x5前缀和数组x0x0x1x0x1x2x0x1x2x3x0x1x2x3x4x0x1x2x3x4x5前缀和数组代数形式x0’x1’x2’x3’x4’x5’ 计算原数组某区间的和 sum[x1,x2,x3] 利用前缀和计算 x3-x0 x0x1x2x3-x0 x1x2x3 差分数组 x0x1x2x3x4x5原数组x0x1x2x3x4x5差分数组x0x1-x0x…

使用PySpark 结合Apache SystemDS 进行信号处理分析 (离散傅立叶变换)的简单例子

文章大纲 简介 :什么是 SystemDS ?环境搭建与数据 准备数据预处理模型训练 与 结果评估参考文献简介 :什么是 SystemDS ? SystemDS is an open source ML system for the end-to-end data science lifecycle from data integration, cleaning, and feature engineering, ov…

Android设计模式--模板方法模式

一&#xff0c;定义 定义一个操作中的算法的框架&#xff0c;而将一些步骤延迟到子类中&#xff0c;使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。 在面向对象的开发过程中&#xff0c;通常会遇到这样一个问题&#xff0c;我们知道一个算法所需的关键步…

C语言矩阵乘积(ZZULIOJ1127:矩阵乘积)

题目描述 计算两个矩阵A和B的乘积。 输入第一行三个正整数m、p和n&#xff0c;0<m,n,p<10&#xff0c;表示矩阵A是m行p列&#xff0c;矩阵B是p行n列&#xff1b;接下来的m行是矩阵A的内容&#xff0c;每行p个整数&#xff0c;用空格隔开&#xff1b;最后的p行是矩阵B的内…

开发上门送桶装水小程序要考虑哪些业务场景

上门送水业务已经有很长一段时间了&#xff0c;但是最开始都是给用户发名片、贴小广告&#xff0c;然后客户电话订水&#xff0c;水站工作人员再上门去送&#xff0c;这种人工记单和派单效率并不高&#xff0c;并且电话沟通中也比较容易出现偏差&#xff0c;那么根据这个情况就…

在AWS VPC中运行Nagios检查时指定自定义DNS解析器的选项

在AWS VPC中运行Nagios检查&#xff0c;并希望能够指定自定义DNS解析器来处理请求。我想使用Python requests库来实现这个目标。 根据问题描述&#xff0c;您想在AWS VPC中运行Nagios检查&#xff0c;并希望使用Python的requests库来指定自定义DNS解析器。 要解决这个问题&…

C语言——结构体的应用

归纳编程学习的感悟&#xff0c; 记录奋斗路上的点滴&#xff0c; 希望能帮到一样刻苦的你&#xff01; 如有不足欢迎指正&#xff01; 共同学习交流&#xff01; &#x1f30e;欢迎各位→点赞 &#x1f44d; 收藏⭐ 留言​&#x1f4dd; 路还在继续&#xff0c;梦还在期…