使用Pytorch从零开始构建DCGAN

news2025/1/11 20:40:08

在本文中,我们将深入研究生成建模的世界,并使用流行的 PyTorch 框架探索 DCGAN(生成对抗网络 (GAN) 的一种变体)的实现。具体来说,我们将使用 CelebA 数据集(名人面部图像的集合)来生成逼真的合成面部。在深入了解实现细节之前,我们首先了解 GAN 是什么以及 DCGAN 与它们有何不同,并详细探讨其架构。
在这里插入图片描述
那么,什么是 GAN?

简而言之,生成对抗网络(GAN)是一种令人着迷的机器学习模型,其中两个玩家(生成器和鉴别器)参与竞争性游戏。生成器的目的是创建真实的合成样本,例如图像或文本,而鉴别器的工作是区分真实样本和假样本。通过这场猫鼠游戏,GAN 学会生成高度令人信服且真实的输出,最终突破人工智能在创建新的多样化数据方面的界限。

在这里插入图片描述
理解DCGAN:

DCGAN(深度卷积生成对抗网络)是一种令人兴奋的机器学习模型,可以创建极其逼真和详细的图像。想象一下,一个系统可以通过分析数千个示例来学习生成全新的图片,例如面孔或风景。DCGAN 通过巧妙地结合两个网络来实现这一目标——一个网络生成图像,另一个网络试图区分真假图像。通过竞争过程,DCGAN 成为生成令人信服且难以与真实图像区分开的图像的大师,展示了人工智能的巨大创造潜力。

DCGAN的架构:

DCGAN(深度卷积生成对抗网络)的架构​​由两个基本组件组成:生成器和判别器。

生成器将随机噪声作为输入,并逐渐将其转换为类似于训练数据的合成样本。它通过一系列层来实现这一点,包括转置卷积、批量归一化和激活函数。这些层使生成器能够学习复杂的模式和结构,从而生成捕获真实数据的复杂细节的高维样本。

另一方面,鉴别器充当二元分类器,区分真实样本和生成样本。它接收输入样本并将其传递给卷积层、批量归一化和激活函数。鉴别器的作用是评估样本的真实性并向生成器提供反馈。通过对抗性训练过程,生成器和鉴别器不断竞争并提高性能,最终生成越来越真实的样本。

让我们开始实现我们的第一个 DCGAN:

假设您的环境中安装了 PyTorch 和 CUDA,我们首先导入必要的库。

import torch
import torch.nn as nn
import torch.optim as optim
import torchvision
from tqdm import tqdm
import torchvision.datasets as datasets
from torchvision.datasets import ImageFolder
from torchvision.utils import make_grid
import torchvision.utils as vutils
import torchvision.transforms as transforms
from torch.utils.data import DataLoader
import matplotlib.pyplot as plt
from torch.utils.data import Subset
import numpy as np

导入这些库后,您就可以使用 CelebA 数据集在 PyTorch 中继续实施 DCGAN。

为了设置训练 DCGAN 模型所需的配置,我们可以定义设备、学习率、批量大小、图像大小、输入图像中的通道数、潜在空间维度、历元数以及特征数判别器和生成器。以下是配置:

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
LEARNING_RATE = 2e-4  # could also use two lrs, one for gen and one for disc
BATCH_SIZE = 128
IMAGE_SIZE = 64
CHANNELS_IMG = 3
Z_DIM = 100
NUM_EPOCHS = 5
FEATURES_DISC = 64
FEATURES_GEN = 64

“device”变量确保代码在可用的 GPU 上运行,否则返回到 CPU。“LEARNING_RATE”决定了模型在优化过程中学习的速率。“BATCH_SIZE”表示每次迭代中处理的样本数量。“IMAGE_SIZE”表示输入图像的所需尺寸。“CHANNELS_IMG”指定输入图像中颜色通道的数量(例如,RGB 为 3)。“Z_DIM”表示潜在空间的维度,它是生成器的输入。“NUM_EPOCHS”决定了训练期间遍历整个数据集的次数。“FEATURES_DISC”和“FEATURES_GEN”分别表示鉴别器和生成器网络中的特征数量。

这些配置对于训练 DCGAN 模型至关重要,可以根据具体要求和可用资源进行调整。

要加载 CelebA 数据集并准备进行训练,我们可以定义数据转换、创建数据集对象并设置数据加载器:

transform = transforms.Compose([
    transforms.Resize((IMAGE_SIZE, IMAGE_SIZE)),
    transforms.ToTensor(),
    transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
])

dataset = datasets.ImageFolder('<path_to_celeba_dataset_in_your_directoty>', transform=transform)
dataloader = DataLoader(dataset=dataset, batch_size=BATCH_SIZE, shuffle=True)

要可视化 CelebA 数据集中的一批训练图像:

real_batch = next(iter(dataloader))
plt.figure(figsize=(7,7))
plt.axis("off")
plt.title("Training Images")
plt.imshow(np.transpose(vutils.make_grid(real_batch[0].to(device)[:49], padding=2, normalize=True).cpu(),(1,2,0)))

在这里插入图片描述
创建生成器网络:
在这里插入图片描述
在 DCGAN 架构中,噪声数据最初表示为形状为 100x1x1 的张量,经过一系列转置卷积运算,将其转换为大小为 3x64x64 的输出图像。

以下是重塑过程的逐步分解:

  1. 输入噪声:100x1x1
  2. 第一个转置卷积层:
    — 输出大小:1024x4x4
    — 内核大小:4x4,步长:1,填充:0
  3. 第二个转置卷积层:
    — 输出大小:512x8x8
    — 内核大小:4x4,步长:2,填充:1
  4. 第三转置卷积层:
    — 输出大小:256x16x16
    — 内核大小:4x4,步长:2,填充:1 5.
  5. 第四转置卷积层:
    — 输出大小:128x32x32
    — 内核大小:4x4,步长:2,填充:1
  6. 最终转置卷积层:
    — 输出大小:3x64x64
    — 内核大小:4x4,步幅:2,填充:1

通过将噪声数据传递到这些转置卷积层,生成器逐渐将低维噪声放大为与所需大小 3x64x64 匹配的高维图像。重塑过程涉及增加空间维度,同时减少通道数量,从而产生具有代表 RGB 颜色通道的三个通道和 64x64 像素尺寸的最终输出图像。

class Generator(nn.Module):
    def __init__(self, z_dim, channels_img, features_g):
        super(Generator, self).__init__()
        self.net = nn.Sequential(
            self._block(z_dim, features_g * 16, 4, 1, 0),
            self._block(features_g * 16, features_g * 8, 4, 2, 1),
            self._block(features_g * 8, features_g * 4, 4, 2, 1),
            self._block(features_g * 4, features_g * 2, 4, 2, 1),
            nn.ConvTranspose2d(
                features_g * 2, channels_img, kernel_size=4, stride=2, padding=1
            ),
            nn.Tanh()
        )

    def _block(self, in_channels, out_channels, kernel_size, stride, padding):
        return nn.Sequential(
            nn.ConvTranspose2d(
                in_channels,
                out_channels,
                kernel_size,
                stride,
                padding,
                bias=False
            ),
            nn.BatchNorm2d(out_channels),
            nn.ReLU()
        )

    def forward(self, x):
        return self.net(x)

Generator 类代表 DCGAN 架构中的生成器网络。它将潜在空间的维度 (z_dim)、输出图像中的通道数 (channels_img) 和特征数 (features_g) 作为输入。生成器被定义为顺序模块。

该_block方法在生成器中定义了一个块,其中包含转置卷积层、批量归一化和 ReLU 激活函数。该块重复多次,逐渐增加输出图像的空间维度。

在该forward方法中,输入噪声 (x) 通过生成器的连续层,从而生成生成的图像。

生成器的架构旨在将低维潜在空间转换为高维图像,并逐渐将其放大到所需的输出大小。然后输出图像通过双曲正切激活函数 ( nn.Tanh()) 以确保其像素值在 [-1, 1] 范围内。

通过以这种方式定义生成器,当提供随机噪声作为输入时,它学会生成类似于训练数据的合成图像。

创建鉴别器网络:
在这里插入图片描述
在 DCGAN 架构中,判别器采用大小为 3x64x64 的输入图像,并通过一系列卷积层对其进行处理,从而产生 1x1x1 的输出。以下是重塑过程的逐步分解:

  1. 输入图像:3x64x64
  2. 第一个卷积层:
    — 输出大小:64x32x32
    — 内核大小:4x4,步长:2,填充:1
  3. 第二个卷积层:
    — 输出大小:128x16x16
    — 内核大小:4x4,步长:2 ,填充:1
  4. 第三个卷积层:
    — 输出大小:256x8x8
    — 内核大小:4x4,步长:2,填充:1 5.
  5. 第四个卷积层:
    — 输出大小:512x4x4
    — 内核大小:4x4,步长:2,填充:1
  6. 最终卷积层:
    — 输出大小:1x1x1
    — 内核大小:4x4,步长:2,填充:0

通过将输入图像传递给这些卷积层,鉴别器逐渐减小空间维度,同时增加通道数量。这种转换允许鉴别器评估图像并对其真实性做出决定。1x1x1 的输出大小表示单个值,该值表示输入图像为真或假的概率。

class Discriminator(nn.Module):
    def __init__(self, channels_img, features_d):
        super(Discriminator, self).__init__()
        self.disc = nn.Sequential(
            nn.Conv2d(channels_img, features_d, kernel_size=4, stride=2, padding=1),
            nn.LeakyReLU(0.2),
            self._block(features_d, features_d * 2, 4, 2, 1),
            self._block(features_d * 2, features_d * 4, 4, 2, 1),
            self._block(features_d * 4, features_d * 8, 4, 2, 1),
            nn.Conv2d(features_d * 8, 1, kernel_size=4, stride=2, padding=0),
            nn.Sigmoid()
        )

    def _block(self, in_channels, out_channels, kernel_size, stride, padding):
        return nn.Sequential(
            nn.Conv2d(
                in_channels,
                out_channels,
                kernel_size,
                stride,
                padding,
                bias=False
            ),
            nn.BatchNorm2d(out_channels),
            nn.LeakyReLU(0.2)
        )

    def forward(self, x):
        return self.disc(x)

Discriminator 类代表 DCGAN 架构中的鉴别器网络。它将输入图像中的通道数 (channels_img) 和特征数 (features_d) 作为输入。鉴别器被定义为一个顺序模块。

该_block方法在判别器内定义了一个块,由卷积层、批量归一化和 LeakyReLU 激活组成。该块重复多次,逐渐增加特征数量并减少输入图像的空间维度。

在该forward方法中,输入图像 (x) 通过鉴别器的连续层,由于最后的 sigmoid 激活,输出表示输入为真 (1) 或假 (0) 的概率。

鉴别器的架构使其能够区分真假图像,使其成为 DCGAN 对抗训练过程的重要组成部分。

创建一个函数来初始化权重:

def initialize_weights(model):
    classname = model.__class__.__name__
    if classname.find('Conv') != -1:
        nn.init.normal_(model.weight.data, 0.0, 0.02)
    elif classname.find('BatchNorm') != -1:
        nn.init.normal_(model.weight.data, 1.0, 0.02)
        nn.init.constant_(model.bias.data, 0)

在此函数中,我们接收 amodel作为输入,它可以引用生成器或鉴别器网络。我们迭代模型的各层并根据层类型初始化权重。这些权重是由 DCGAN 论文建议的。

对于卷积层 ( Conv),我们使用nn.init.normal_平均值 0 和标准差 0.02 来初始化权重。

对于批量归一化层 ( BatchNorm),我们使用 来初始化权重,平均值为 1,标准差为 0.02 nn.init.normal_,并使用 来将偏差设置为 0 nn.init.constant_。

通过调用此函数并将生成器和鉴别器网络作为参数传递,您可以确保网络的权重得到适当初始化以训练 DCGAN 模型。

gen = Generator(Z_DIM, CHANNELS_IMG, FEATURES_GEN).to(device)
disc = Discriminator(CHANNELS_IMG, FEATURES_DISC).to(device)

initialize_weights(gen)
initialize_weights(disc)

gen现在,我们通过传递潜在空间的维度 ( Z_DIM)、输出图像中的通道数 ( CHANNELS_IMG) 以及生成器中的特征数量 ( FEATURES_GEN)来创建生成器网络 ( ) 的实例。disc类似地,我们通过指定输入通道的数量 ( CHANNELS_IMG) 和鉴别器中的特征数量 ( )创建鉴别器网络 ( ) 的实例FEATURES_DISC。

创建网络实例后,我们调用initialize_weights生成器和鉴别器网络上的函数,根据 DCGAN 论文中建议的权重初始化技术来初始化它们的权重。

通过执行此代码,您将准备好生成器和鉴别器网络,并正确初始化它们的权重,用于训练 DCGAN 模型。

opt_gen = optim.Adam(gen.parameters(), lr=LEARNING_RATE, betas=(0.5, 0.999))
opt_disc = optim.Adam(disc.parameters(), lr=LEARNING_RATE, betas=(0.5, 0.999))
criterion = nn.BCELoss()

fixed_noise = torch.randn(32, Z_DIM, 1, 1).to(device)

我们为生成器和鉴别器网络定义了优化器。我们使用模块中的 Adam 优化器torch.optim。生成器优化器 ( opt_gen) 使用生成器参数、学习率LEARNING_RATE以及 Adam 优化器的 beta 参数(0.5 和 0.999)进行初始化。

类似地,判别器优化器 ( opt_disc) 使用判别器的参数、相同的学习率和 beta 参数进行初始化。

接下来,我们定义对抗训练过程的损失标准。在这里,我们使用二元交叉熵损失 (Binary Cross Entropy Loss nn.BCELoss()),它在 GAN 中常用来将判别器的预测与真实标签进行比较。

最后,我们创建一个固定噪声张量 ( fixed_noise),用于在训练过程中生成样本图像。该torch.randn函数根据正态分布生成随机数。

通过设置优化器、损失准则和固定噪声张量,您就可以开始训练 DCGAN 模型了。

def show_tensor_images(image_tensor, num_images=32, size=(1, 64, 64)):
    image_tensor = (image_tensor + 1) / 2
    image_unflat = image_tensor.detach().cpu()
    image_grid = make_grid(image_unflat[:num_images], nrow=4)
    plt.imshow(image_grid.permute(1, 2, 0).squeeze())
    plt.show()

“show_tensor_images”函数是一个实用函数,它获取图像张量,对它们进行标准化,创建图像网格,并使用 matplotlib 显示它们,以便在训练过程中轻松可视化。

训练模型:

gen.train()
disc.train()

for epoch in range(NUM_EPOCHS):
    for batch_idx, (real, _ ) in enumerate(dataloader):
        real = real.to(device)
        ### create noise tensor
        noise = torch.randn((BATCH_SIZE, Z_DIM, 1, 1)).to(device)
        fake = gen(noise)

        ### Train Discriminator: max log(D(x)) + log(1 - D(G(z)))
        disc_real = disc(real).reshape(-1)
        loss_disc_real = criterion(disc_real, torch.ones_like(disc_real))
        disc_fake = disc(fake.detach()).reshape(-1)
        loss_disc_fake = criterion(disc_fake, torch.zeros_like(disc_fake))
        loss_disc = (loss_disc_real + loss_disc_fake) / 2
        disc.zero_grad()
        loss_disc.backward()
        opt_disc.step()

        ### Train Generator: min log(1 - D(G(z))) <-> max log(D(G(z))
        output = disc(fake).reshape(-1)
        loss_gen = criterion(output, torch.ones_like(output))
        gen.zero_grad()
        loss_gen.backward()
        opt_gen.step()

        ### Print losses occasionally and fake images occasionally
        if batch_idx % 50 == 0:
            print(
                f"Epoch [{epoch}/{NUM_EPOCHS}] Batch {batch_idx}/{len(dataloader)} \
                  Loss D: {loss_disc:.4f}, loss G: {loss_gen:.4f}"
            )
            with torch.no_grad():
                fake = gen(fixed_noise)
                img_grid_real = torchvision.utils.make_grid(real[:32], normalize=True)
                img_grid_fake = torchvision.utils.make_grid(fake[:32], normalize=True)
                show_tensor_images(img_grid_fake)

我们可以将本次培训分为四个部分以便更好地理解。

  1. 噪声生成:随机噪声 ( noise) 使用形状为 的正态分布生成(BATCH_SIZE, Z_DIM, 1, 1)。
  2. 判别器训练:判别器网络是通过评估真实图像 ( ) 并根据判别器与地面真实标签(全部)的预测real计算损失 ( ) 来训练的。loss_disc_real假图像 ( fake) 是通过将噪声传递到生成器网络来生成的。评估鉴别器对假图像 ( ) 的预测,并根据与地面真实标签(全零)相比的预测来计算disc_fake损失 ( ) 。loss_disc_fake平均损失 ( ) 计算为和loss_disc的平均值。判别器参数的梯度设置为零,反向传播损失,并更新判别器的优化器。loss_disc_realloss_disc_fake
  3. 生成器训练:生成器网络是通过fake使用噪声生成假图像( )来训练的。获得判别器对假图像 ( ) 的预测,并根据与地面真实标签(全部)相比的预测来计算output损失 ( )。loss_gen生成器参数的梯度设置为零,反向传播损失,并更新生成器的优化器。
  4. 进度跟踪和图像可视化:每 50 个批次后,打印当前纪元、批次索引、鉴别器损失 ( loss_disc) 和生成器损失 ( )。loss_gen样本图像是通过将固定噪声传递到生成器网络来生成的。真实图像和生成图像都被转换为图像网格,然后显示图像网格以可视化生成器网络的学习进度。

每个时期生成的假图像:

Starting of the first epoch:Starting of the first epoch
After first epoch:
在这里插入图片描述
After second epoch:
在这里插入图片描述
After third epoch:
在这里插入图片描述
After fourth epoch:
在这里插入图片描述
End results ( After 5 epochs):
在这里插入图片描述

结论: 如果您能够获得更好的结果,

  1. 增加模型容量:在生成器和鉴别器网络中添加更多层或增加滤波器数量,以增强模型学习复杂模式并生成更高质量图像的能力。
  2. 利用更深的卷积层:实施ResNet 或 DenseNet 等更深层次的架构来捕获更复杂的特征和纹理,从而提高图像质量。
  3. 使用更大的数据集:在更大、更多样化的数据集上训练 DCGAN 可以帮助模型学习更广泛的图像变化,并提高其生成高分辨率图像的能力。
  4. 调整训练参数:试验学习率、批量大小和训练迭代等超参数,以优化训练过程并提高模型生成更高分辨率图像的能力。

博文译自Manohar Kedamsetti的博客。

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

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

相关文章

微服务学习|初识Docker、使用Docker、自定义镜像、DockerCompose、Docker镜像仓库

初识Docker 项目部署的问题 大型项目组件较多&#xff0c;运行环境也较为复杂&#xff0c;部署时会碰到一些问题 依赖关系复杂&#xff0c;容易出现兼容性问题 开发、测试、生产环境有差异 Docker如何解决依赖的兼容问题的? 将应用的Libs (函数库)、Deps (依赖)配置与应用…

c语言:用迭代法解决递归问题

题目&#xff1a; 解释&#xff1a;题目的意思就是用迭代法的空间和时间复杂的太高了&#xff0c;需要我们减小空间与时间的复杂度&#xff0c;我就想到了迭代法&#xff0c;思路和代码如下&#xff1a; #include <stdio.h> //这里是递归法转迭代法 int main() {int x,i…

Spark---转换算子、行动算子、持久化算子

一、转换算子和行动算子 1、Transformations转换算子 1&#xff09;、概念 Transformations类算子是一类算子&#xff08;函数&#xff09;叫做转换算子&#xff0c;如map、flatMap、reduceByKey等。Transformations算子是延迟执行&#xff0c;也叫懒加载执行。 2)、Transf…

Leetcode——121 买卖股票的最佳时机

(超时。。。。。。&#xff09;除了暴力法我是真的。。。。。。 class Solution {public int maxProfit(int[] prices) {int len prices.length;int max0;for(int i0;i<len-1;i){for(int ji1;j<len;j){int income prices[j] - prices[i];if(income>max){maxincome;…

路由的控制与转发原理

场景1&#xff1a;路由器收到数据包后&#xff0c;会根据数据包的目标IP地址&#xff0c;计算出目标网段&#xff0c;再确定终端设备的具体位置。这个过程中&#xff0c;还需要计算出接口&#xff0c;或数据包下一跳的地址。最终会生成一条路由&#xff0c;即路径&#xff0c;存…

外部中断为什么会误触发?

今天在写外部中断的程序的时候&#xff0c;发现中断特别容易受到干扰&#xff0c;我把手放在对应的中断引脚上&#xff0c;中断就一直触发&#xff0c;没有停过。经过一天的学习&#xff0c;找到了几个解决方法&#xff0c;所以写了这篇笔记。如果你的中断也时不时会误触发&…

基于JavaWeb+SSM+Vue教学辅助微信小程序系统的设计和实现

基于JavaWebSSMVue教学辅助微信小程序系统的设计和实现 源码获取入口前言主要技术系统设计功能截图Lun文目录订阅经典源码专栏Java项目精品实战案例《500套》 源码获取 源码获取入口 前言 1.1 概述 随着信息时代的快速发展&#xff0c;互联网的优势和普及&#xff0c;人们生活…

二、防火墙-安全策略

学习防火墙之前&#xff0c;对路由交换应要有一定的认识 1、安全策略基本概念2、匹配顺序3、缺省包过滤4、安全策略发展史4.1.基于ACL包过滤4.2.融合UTM安全策略4.3.一体化安全策略 5、Local区域安全策略5.1.针对OSPF协议配置Local区域安全策略5.2.其他协议 6、ASPF6.1.帮助FTP…

PCIE链路训练-状态跳转1

A&#xff1a;12ms超时&#xff0c;或者再任何lane上检测到Electrical Idle Exit&#xff1b; B&#xff1a; 1.发送“receiver detection”之后没有一个lane的接收逻辑被rx检测到 2.不满足条件c&#xff0c;比如两次detection出现差别&#xff1b; C&#xff1a;发送端在没…

文本分析:NLP 魔法!

一、说明 这是一个关于 NLP 和分类项目的博客。NLP 是自然语言处理&#xff0c;目前需求量很大。让我们了解如何利用 NLP。我们将通过编码来理解流程和概念。我将在本博客中介绍 BagOfWords 和 n-gram 以及朴素贝叶斯分类模型。这个博客的独特之处&#xff08;这使得它很长&…

竞赛选题 车道线检测(自动驾驶 机器视觉)

0 前言 无人驾驶技术是机器学习为主的一门前沿领域&#xff0c;在无人驾驶领域中机器学习的各种算法随处可见&#xff0c;今天学长给大家介绍无人驾驶技术中的车道线检测。 1 车道线检测 在无人驾驶领域每一个任务都是相当复杂&#xff0c;看上去无从下手。那么面对这样极其…

Oracle 的 Java SE、OpenJDK、Database 链接

1 访问主站 Oracle | Cloud Applications and Cloud Platform 2 开发者 2.1 OpenJDK (这里的不用登录&#xff0c;就可以下载) JDK Builds from Oracle 2.2 JavaSE (需要登录&#xff0c;才可以下载) Java Downloads | Oracle 2.3 DataBase (MySQL为例) MySQL :: MySQL Dow…

排序算法--快速排序

实现逻辑 ① 从数列中挑出一个元素&#xff0c;称为 “基准”&#xff08;pivot&#xff09;&#xff0c; ② 重新排序数列&#xff0c;所有元素比基准值小的摆放在基准前面&#xff0c;所有元素比基准值大的摆在基准的后面&#xff08;相同的数可以到任一边&#xff09;。在这…

DataFunSummit:2023年OLAP引擎架构峰会-核心PPT资料下载

一、峰会简介 OLAP技术是当前大数据领域的热门方向&#xff0c;该领域在各个行业都有广泛的使用场景&#xff0c;对OLAP引擎的功能有丰富多样的需求。同时&#xff0c;在性能、稳定性和成本方面&#xff0c;也有诸多挑战。目前&#xff0c;OLAP技术没有形成统一的事实标准&…

系统移植-交叉编译工具链

不同架构的机器码 与 汇编语言 都不可移植&#xff0c; 且二者一一对应 c语言中三种成分&#xff1a; 1.分号结尾的叫做语句 语句可以让CPU执行&#xff0c;可以进行预处理&#xff0c;编译等生成机器码 2.#开头的为预处理指令 不带分号 CPU无法执行 3.注释&#xff0c;…

AR道具特效制作工具

AR&#xff08;增强现实&#xff09;技术已经逐渐渗透到各个行业&#xff0c;为企业带来了全新的营销方式和用户体验。在这个背景下&#xff0c;美摄科技凭借其强大的技术实力和创新精神&#xff0c;推出了一款专为企业打造的美摄AR特效制作工具&#xff0c;旨在帮助企业轻松实…

Eclipse常用设置-乱码

在用Eclipse进行Java代码开发时&#xff0c;经常会遇到一些问题&#xff0c;记录下来&#xff0c;方便查看。 一、properties文件乱码 常用的配置文件properties里中文的乱码&#xff0c;不利于识别。 处理流程&#xff1a;Window -> Preferences -> General -> Ja…

stm32定时器输入捕获模式

频率测量 频率测量有两种方法 测频法&#xff1a;在闸门时间T内&#xff0c;对上升沿或下降沿计次&#xff0c;得到N&#xff0c;则评率fxN/T测周法&#xff1a;两个上升沿内&#xff0c;以标准频率fc计次得到N&#xff0c;则频率fx fc/N中界频率&#xff1a;测频法和测周法误…

Altium Designer学习笔记8

创建原理图元件&#xff1a; 画出原理图&#xff1a; 根据规则书画出原理图&#xff1a; 根据规则书画出封装图&#xff1a; 参照&#xff1a; 确认下过孔的内径和外径的最小允许值。

设计模式-创建型模式-工厂方法模式

一、什么是工厂方法模式 工厂模式又称工厂方法模式&#xff0c;是一种创建型设计模式&#xff0c;其在父类中提供一个创建对象的方法&#xff0c; 允许子类决定实例化对象的类型。工厂方法模式是目标是定义一个创建产品对象的工厂接口&#xff0c;将实际创建工作推迟到子类中。…