AIGC实战——生成对抗网络(Generative Adversarial Network)

AIGC实战——生成对抗网络

    • 0. 前言
    • 1. 生成对抗网络
      • 1.1 生成对抗网络核心思想
      • 1.2 深度卷积生成对抗网络
    • 2. 数据集分析
      • 3. 构建深度卷积生成对抗网络
      • 3.1 判别器
      • 3.2 生成器
      • 3.3 DCGAN 模型训练
    • 4. GAN 训练技巧
      • 4.1 判别器强于生成器
      • 4.2 生成器强于判别器
      • 4.3 信息量不足
      • 4.4 超参数
    • 小结
    • 系列链接

0. 前言

生成对抗网络 (Generative Adversarial Network, GAN) 是由 Ian Goodfellow 等人在 2014 年提出的一种强大的深度学习模型,可以用于生成新数据样本,比如图像、音频、文本等。GAN 包含两个神经网络:生成器和判别器。生成器根据输入的噪声信号生成一些伪造的数据样本,而判别器则负责判断该数据样本是真实的还是伪造的。在本节中,首先阐述生成对抗网络的理论基础,然后使用 Keras 构建生成对抗网络模型。

1. 生成对抗网络

1.1 生成对抗网络核心思想

生成对抗网络 (Generative Adversarial NetworkGAN) 可以通过类比为一个伪造专家与一个检测专家之间的博弈。生成器 (generator) 是伪造专家,目标是制造出逼真的假样本,以尽可能地欺骗检测专家。而鉴别器 (discriminator) 则是检测专家,旨在区分真实样本和生成器产生的假样本,并尽可能准确地识别它们。
在开始时,生成器只能制造出不完美的假样本,而鉴别器擅长辨别真伪并且准确率相对较高。然而,随着训练的进行,生成器变得更加熟练,并努力提高自己的技艺,从而创造逼真的样本。同时,鉴别器也在不断学习和调整自己的判别能力,以保持对假样本的敏感度。
通过反复的博弈和训练,生成器和鉴别器逐渐达到一种平衡状态。生成器学会了创造足以迷惑鉴别器的逼真作品,而鉴别器也不断提高自己的判别能力。最终,生成器能够创造出几乎无法与真实样本区分的逼真假样本,而鉴别器也变得越来越难以区分真伪。
GAN 的核心思想是生成器和判别器之间的对抗,生成器试图将随机噪声转换为看起来像是从原始数据集中采样而来的观测样本,而判别器则试图预测一个观测样本是来自原始数据集还是生成器的伪造品。下图展示了生成器与判别器的输入和输出的示例。

生成对抗网络架构

在开始时,生成器会输出充满噪声的图片,同样判别器的预测也是随机的。GAN 的关键在于交替训练这两个网络,才能让生成器越来越擅长欺骗判别器,而判别器必须通过训练保证其正确识别真伪的能力,这又会驱使生成器找到欺骗判别器的新方法,从而推动循环不断向前进行。

1.2 深度卷积生成对抗网络

为了更深入了解生成对抗网络的训练流程,我们将使用 Keras 构建深度卷积生成对抗网络 (Deep Convolutional GAN, DCGAN),来生成积木的图片。GAN 的原论文中没有使用卷积层,而是使用了全连接层,但实践已经证明,卷积层能够提供更强大的性能,因此现在基本上所有的 GAN 架构都包含卷积层。

2. 数据集分析

首先,需要下载训练数据,使用 Kaggle 上的乐高积木图像数据集。该数据集中包含了 40,000 张不同角度的 50 种不同玩具积木的照片,示例样本图片如下所示:

积木数据集

可以通过 Kaggle 官方网站下载数据集,下载完成后将这些图像和相关的元数据保存到 ./data 文件夹中。
使用 Kerasimage_dataset_from_directory 函数来创建一个指向存储图像目录的 TensorFlow 数据集,以便可以在需要时(例如,在训练期间)将图像批量读入内存,从而能够处理大型数据集,而无需担心将整个数据集都放入内存中导致内存不足的情况。同时将图像尺寸调整为 64 × 64,并在像素值之间进行插值处理:

train_data = image_dataset_from_directory(
    "data/lego_brick",
    label_mode=None,
    color_mode="grayscale",
    image_size=(IMAGE_SIZE, IMAGE_SIZE),
    batch_size=BATCH_SIZE,
    shuffle=True,
    interpolation="bilinear",
)

原始数据的像素强度范围为 [0, 255]。在训练 GAN 时,我们将数据缩放到范围 [-1, 1],以便在生成器的最后一层使用 tanh 激活函数。相对于 sigmoid 函数,tanh 激活函数的梯度更大,有助于更快地收敛:

def preprocess(img):
    """
    Normalize and reshape the images
    """
    img = (tf.cast(img, "float32") - 127.5) / 127.5
    return img

train = train_data.map(lambda x: preprocess(x)).as_numpy_iterator()

3. 构建深度卷积生成对抗网络

3.1 判别器

鉴别器的目标是预测图像是真实的还是伪造的,这属于图像分类问题,因此我们可以使用常见的深度神经网络架构,即先堆叠的卷积层,最后使用带有一个输出节点的全连接层。判别器的完整架构如下:

判别器

使用 Keras 实现判别器:

# 定义判别器的输入(图像)层
discriminator_input = layers.Input(shape=(IMAGE_SIZE, IMAGE_SIZE, CHANNELS))
# 堆叠 Conv2D 层,并在其间添加 BatchNormalization、LeakyReLU 激活和 Dropout 层
x = layers.Conv2D(64, kernel_size=4, strides=2, padding="same", use_bias=False)(
    discriminator_input
)
x = layers.LeakyReLU(0.2)(x)
x = layers.Dropout(0.3)(x)
x = layers.Conv2D(
    128, kernel_size=4, strides=2, padding="same", use_bias=False
)(x)
x = layers.BatchNormalization(momentum=0.9)(x)
x = layers.LeakyReLU(0.2)(x)
x = layers.Dropout(0.3)(x)
x = layers.Conv2D(
    256, kernel_size=4, strides=2, padding="same", use_bias=False
)(x)
x = layers.BatchNormalization(momentum=0.9)(x)
x = layers.LeakyReLU(0.2)(x)
x = layers.Dropout(0.3)(x)
x = layers.Conv2D(
    512, kernel_size=4, strides=2, padding="same", use_bias=False
)(x)
x = layers.BatchNormalization(momentum=0.9)(x)
x = layers.LeakyReLU(0.2)(x)
x = layers.Dropout(0.3)(x)
x = layers.Conv2D(
    1,
    kernel_size=4,
    strides=1,
    padding="valid",
    use_bias=False,
    activation="sigmoid",
)(x)
# 将最后一个卷积层展平,得到张量的形状为 1×1×1,因此不再需要 Dense 层
discriminator_output = layers.Flatten()(x)
# 定义判别器模型,该模型接受一个输入图像,并输出一个介于 0 和 1 之间的概率值
discriminator = models.Model(discriminator_input, discriminator_output)
print(discriminator.summary())

需要注意的是,为了缩小张量在网络中传递时的空间形状(从原始图像的 64 开始,然后是 321684,最后是 1),某些 Conv2D 层的步幅为 2,同时增加通道数(灰度输入图像中通道数为 1,然后变为 64128256,最后为 512),最后展平为单个预测值。
在最后一个 Conv2D 层上使用 sigmoid 激活函数,以确保输出值介于 01 之间,表示预测图像为真的概率。

3.2 生成器

接下来,构建生成器。生成器的输入是从多元标准正态分布中抽取的向量,输出是与原始训练数据中的图像大小相同的图像。GAN 的生成器和变分自编码器 (Variational Autoencoder, VAE) 的解码器具有完全相同的目标:将潜空间中的向量转换为图像。在生成模型中,从潜空间映射回原始域的概念非常常见,因为通过这个概念,我们能够在潜空间中操作向量以改变原始域中图像的高级特征。生成器架构如下所示:

生成器架构

使用 Keras 实现以上生成器模型:

# 定义生成器的输入层,长度为 100 的随机向量
generator_input = layers.Input(shape=(Z_DIM,))
# 使用 Reshape 层将输入向量转化为 1×1×100 的张量,以便应用转置卷积操作
x = layers.Reshape((1, 1, Z_DIM))(generator_input)
# 堆叠四个 Conv2DTranspose 层,并在其间添加 BatchNormalization 和 LeakyReLU 层
x = layers.Conv2DTranspose(
    512, kernel_size=4, strides=1, padding="valid", use_bias=False
)(x)
x = layers.BatchNormalization(momentum=0.9)(x)
x = layers.LeakyReLU(0.2)(x)
x = layers.Conv2DTranspose(
    256, kernel_size=4, strides=2, padding="same", use_bias=False
)(x)
x = layers.BatchNormalization(momentum=0.9)(x)
x = layers.LeakyReLU(0.2)(x)
x = layers.Conv2DTranspose(
    128, kernel_size=4, strides=2, padding="same", use_bias=False
)(x)
x = layers.BatchNormalization(momentum=0.9)(x)
x = layers.LeakyReLU(0.2)(x)
x = layers.Conv2DTranspose(
    64, kernel_size=4, strides=2, padding="same", use_bias=False
)(x)
x = layers.BatchNormalization(momentum=0.9)(x)
x = layers.LeakyReLU(0.2)(x)
# 最后一个 Conv2DTranspose 层使用 tanh 激活函数将输出转换到区间 [-1, 1],以匹配原始图像域
generator_output = layers.Conv2DTranspose(
    CHANNELS,
    kernel_size=4,
    strides=2,
    padding="same",
    use_bias=False,
    activation="tanh",
)(x)
# 定义生成器模型,其接受长度为100的向量并输出形状为 [64, 64, 1] 的张量
generator = models.Model(generator_input, generator_output)
print(generator.summary())

需要注意的是,为了增加张量在网络中传递时的空间形状(原始向量中为 1,然后是 481632,最后是 64),某些 Conv2DTranspose 层的步幅为 2,同时减少通道数(首先是 512,然后是 25612864,最后是 1,以匹配灰度图像输出)。

UpSampling2D 与 Conv2DTranspose
除了使用 Conv2DTranspose 层来增加图像尺寸外,还可以使用 UpSampling2D 层,并在 UpSampling2D 后添加一个步幅为 1 的普通 Conv2D 层:

x = layers.UpSampling2D(size = 2)(x)
x = layers.Conv2D(256, kernel_size=4, strides=1, padding="same")(x)

UpSampling2D 层简单地将输入的每一行和每一列重复,用以将张量的宽度和高度加倍。然后,利用步幅为 1Conv2D 层执行卷积操作。这种方法与转置卷积类似,但不同的是,上采样只是重复现有的像素值,而不是用零填充像素之间的空隙。
实践证明,Conv2DTranspose 方法会导致输出图像中出现伪影,如下图中所示的细小棋盘格形状,这会破坏输出的质量。然而,在许多性能优异的 GAN 中仍然使用转置卷积。

棋盘伪影

这两种方法,UpSampling2D + Conv2DConv2DTranspose,都可以用于将随即向量转换回原始图像域,在实践中,我们可以尝试不同方法,以获得最佳生成效果。

3.3 DCGAN 模型训练

DCGAN 中生成器和判别器的架构非常简单,与 VAE 模型并没有太大的区别。理解 GAN 的关键在于了解生成器和判别器的训练过程。
为了训练判别器,我们可以创建一个训练集,其中一些图像是来自训练集的真实观测样本,还有一些图像是生成器的输出结果,真实图像对应的标签为 1,生成图像对应点标签为 0。如果将其视为一个监督学习问题,那么就可以训练判别器如何区分原始图像和生成图像之间的差异,输出结果表示输入图像为真实图像的概率,使用二元交叉熵作为损失函数。
在训练生成器时,我们需要找到一种评价每个生成的图像的方法,以便它可以优化图像。判别器正是这种评价方法,生成器可以生成一批图像,并将其通过判别器以获取每个图像的真实性概率得分。为了让生成的图像欺骗判别器,即我们希望判别器对生成器生成的图片输出结果接近于 1,因此生成器的损失函数就是判别器的输出概率与一个全为1的向量之间的二元交叉熵。
因此,为了训练生成器,我们必须将生成器连接到判别器体,具体而言,我们将生成器的输出图像输入到判别器后,这个组合模型就可以根据判别器,输出生成图像为真的概率
为了训练这个组合模型,我们必须交替训练这两个网络,并确保一次只更新一个网络的权重。例如,在生成器训练过程中,我们必须冻结判别器的权重,只更新生成器的权重。如果我们也允许判别器的权重发生变化,那么判别器只会调整自己,以便更有可能将生成的图像预测为真实图像,而这不是我们期望的结果。我们希望生成的图像被预测接近 1 (真实)是由于生成器性能强,而不是因为判别器性能弱。下图展示了判别器和生成器的训练过程。

GAN 训练流程

Keras 中,可以通过自定义 train_step 函数实现以上训练过程:

class DCGAN(models.Model):
    def __init__(self, discriminator, generator, latent_dim):
        super(DCGAN, self).__init__()
        self.discriminator = discriminator
        self.generator = generator
        self.latent_dim = latent_dim

    def compile(self, d_optimizer, g_optimizer):
        super(DCGAN, self).compile()
        # 生成器和判别器的损失函数均为 BinaryCrossentropy
        self.loss_fn = losses.BinaryCrossentropy()
        self.d_optimizer = d_optimizer
        self.g_optimizer = g_optimizer
        self.d_loss_metric = metrics.Mean(name="d_loss")
        self.d_real_acc_metric = metrics.BinaryAccuracy(name="d_real_acc")
        self.d_fake_acc_metric = metrics.BinaryAccuracy(name="d_fake_acc")
        self.d_acc_metric = metrics.BinaryAccuracy(name="d_acc")
        self.g_loss_metric = metrics.Mean(name="g_loss")
        self.g_acc_metric = metrics.BinaryAccuracy(name="g_acc")

    @property
    def metrics(self):
        return [
            self.d_loss_metric,
            self.d_real_acc_metric,
            self.d_fake_acc_metric,
            self.d_acc_metric,
            self.g_loss_metric,
            self.g_acc_metric,
        ]

    def train_step(self, real_images):
        # Sample random points in the latent space
        batch_size = tf.shape(real_images)[0]
        # 从多元标准正态分布中随机采样一批向量
        random_latent_vectors = tf.random.normal(
            shape=(batch_size, self.latent_dim)
        )

        # Train the discriminator on fake images
        with tf.GradientTape() as gen_tape, tf.GradientTape() as disc_tape:
            # 将这些向量通过生成器生成一批图像
            generated_images = self.generator(
                random_latent_vectors, training=True
            )
            #  使用判别器预测真实图像与生成图像的真实性概率
            real_predictions = self.discriminator(real_images, training=True)
            fake_predictions = self.discriminator(generated_images, training=True)

            real_labels = tf.ones_like(real_predictions)
            real_noisy_labels = real_labels + NOISE_PARAM * tf.random.uniform(
                tf.shape(real_predictions)
            )
            fake_labels = tf.zeros_like(fake_predictions)
            fake_noisy_labels = fake_labels - NOISE_PARAM * tf.random.uniform(
                tf.shape(fake_predictions)
            )
            #  判别器损失是真实图像(标签为 1 )和生成图像(标签为 0 )的二元交叉熵的平均值
            d_real_loss = self.loss_fn(real_noisy_labels, real_predictions)
            d_fake_loss = self.loss_fn(fake_noisy_labels, fake_predictions)
            d_loss = (d_real_loss + d_fake_loss) / 2.0
            # 生成器损失是判别器对生成图像的预测与标签 1 之间的二元交叉熵
            g_loss = self.loss_fn(real_labels, fake_predictions)
        gradients_of_discriminator = disc_tape.gradient(d_loss, self.discriminator.trainable_variables)
        gradients_of_generator = gen_tape.gradient(g_loss, self.generator.trainable_variables)
        #  分别更新判别器和生成器的权重
        self.d_optimizer.apply_gradients(
            zip(gradients_of_discriminator, discriminator.trainable_variables)
        )
        self.g_optimizer.apply_gradients(
            zip(gradients_of_generator, generator.trainable_variables)
        )
        # Update metrics
        self.d_loss_metric.update_state(d_loss)
        self.d_real_acc_metric.update_state(real_labels, real_predictions)
        self.d_fake_acc_metric.update_state(fake_labels, fake_predictions)
        self.d_acc_metric.update_state(
            [real_labels, fake_labels], [real_predictions, fake_predictions]
        )
        self.g_loss_metric.update_state(g_loss)
        self.g_acc_metric.update_state(real_labels, fake_predictions)

        return {m.name: m.result() for m in self.metrics}

# Create a DCGAN
dcgan = DCGAN(
    discriminator=discriminator, generator=generator, latent_dim=Z_DIM
)

dcgan.compile(
    d_optimizer=optimizers.Adam(
        learning_rate=LEARNING_RATE, beta_1=ADAM_BETA_1, beta_2=ADAM_BETA_2
    ),
    g_optimizer=optimizers.Adam(
        learning_rate=LEARNING_RATE, beta_1=ADAM_BETA_1, beta_2=ADAM_BETA_2
    ),
)

dcgan.fit(train, epochs=EPOCHS)

在生成器训练期间,由于判别器被冻结,它的权重不会被更新,而生成器的权重将朝着能够更好地生成图像(更有可能骗过判别器的图像)的方向移动(也就是让判别器的预测值接近 1)。
判别器和生成器不断博弈可能导致 DCGAN 的训练过程不稳定。理想情况下,训练过程能够找到一个平衡点,使得生成器能够从判别器中学习到有意义的信息,并且图像的质量开始提高。经过足够多的 epochs 后,判别器往往会占优势,但生成器在此时可能已经学会生成足够高质量的图像了。
为标签添加一些微小的随机噪音是训练 GAN 时的一个有用技巧。这有助于提高训练过程的稳定性并增强生成的图像。这种标签平滑能够使判别器处理更具挑战性的任务,而不至于令生成器无法训练。
通过观察训练过程中生成器的生成图像,可以明显看出生成器生成的图像越来越接近从训练集中采样的图像。

生成图像

可以看到,神经网络有能力将随机噪音转化为有意义的内容,同时,除了原始图像外,我们没有为模型提供任何额外的特征,因此它必须独自学习高级特征,例如绘制阴影、立方体和圆圈。
成功的生成模型还需要满足一个要求,即它不能仅仅是复制训练集中已有的图像。为了测试这一点,我们可以从训练数据集中找到与生成图像最接近的图像,可以使用 L1 距离度量图像间的距离:

def compare_images(img1, img2):
    return np.mean(np.abs(img1 - img2))

下图展示了一些生成图像在训练集中与之最接近的观测样本。可以看出,虽然生成的图像和训练集之间存在一定程度的相似性,但它们并非完全相同。这表明生成器已经学习到训练数据集中的高级特征,并能够生成与训练数据集中图像不同的样本。

训练集中最接近生成样本的观测样本

4. GAN 训练技巧

虽然 GAN 是生成模型领域的重大突破,但也存在训练难度较大的问题。在本节中,我们将探讨训练 GAN 时遇到的一些最常见的问题,以及相应的解决方案。

4.1 判别器强于生成器

如果判别器变得过强,损失函数的信号就会变得过弱,无法推动生成器产生有意义的改进。在最糟糕的情况下,判别器完全学会了区分真实图像和虚假图像,导致梯度完全消失,从而无法进行任何训练。
如果判别器的损失函数失控,就需要弱判别器:

  • 增加判别器中 Dropout 层的丢弃率,减少信息通过网络的量
  • 降低判别器的学习率
  • 减少判别器中的卷积滤波器数量
  • 在训练判别器时,为标签添加噪声
  • 在训练判别器时,随机将一些图像的标签反转(从 0 变为 1,或从 1 变为 0)

4.2 生成器强于判别器

如果判别器不够强大,生成器会找到轻松欺骗判别器的方法,并只生成一小部分几乎相同的图像样本,这称为模式坍塌 (Mode Collapse)。
假设我们在不更新判别器的情况下对生成器进行了多次训练。生成器倾向于找到一个总是能够欺骗判别器的单个观测样本(也称为模式),并开始将潜在输入空间中的每个点映射到这个观测样本图像。此外,损失函数的梯度会趋近于零,因此无法从此状态恢复。
即使我们随后尝试重新训练判别器,以阻止其被这一个模式欺骗,生成器仍会找到另一个模式判别器的模式,因为它已经没有多样化输出的动力。
如果生成器出现模式崩溃的问题,可以尝试使用与上一小节相反的方法来加强判别器。此外,也可以尝试减小两个网络的学习率并增加批大小。

4.3 信息量不足

由于深度学习模型被编译为最小化损失函数,因此可能自然地认为生成器的损失函数越小,生成的图像质量就越好。然而,由于生成器只针对当前的判别器进行评估,而判别器在不断改进,我们无法比较在训练过程中不同时间点评估的损失函数。实际上,在训练过程中,尽管图像质量明显提高,生成器的损失也可能会随着时间的推移而增加。由于生成器损失与图像质量之间缺乏相关性,有时使得 GAN 的训练难以监控。

4.4 超参数

GAN 训练过程中,我们可以看到,即使是简单的 GAN 也有大量需要调整的超参数 (Hyperparameter)。除了判别器和生成器的整体架构外,还有一些参数控制着批归一化、Dropout、学习率、激活层、卷积核大小、步长、批大小和潜空间大小等。GAN 对这些参数的微小变化非常敏感,因此找到一组有效的参数通常需要反复实验,而没有一套既定的准则。
因此了解 GAN 的内部工作原理并知道如何解读损失函数至关重要,只有这样才能合理的进行超参数调整,以提高模型的稳定性。

小结

在本节中,我们首先介绍了生成对抗网络 (Generative Adversarial Network, GAN) 的基本原理,并学习了如何训练 DCGAN 生成玩具积木图像,GAN 能够学会以图像的形式真实地表示 3D 对象,包括阴影、形状和纹理;还探讨了 GAN 训练过程中可能会遇到的问题,及相应的解决方法和训练技巧。

系列链接

AIGC实战——生成模型简介
AIGC实战——深度学习 (Deep Learning, DL)
AIGC实战——卷积神经网络(Convolutional Neural Network, CNN)
AIGC实战——自编码器(Autoencoder)
AIGC实战——变分自编码器(Variational Autoencoder, VAE)
AIGC实战——使用变分自编码器生成面部图像

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【开源】基于Vue和SpringBoot的快递管理系统

项目编号: S 007 ,文末获取源码。 \color{red}{项目编号:S007,文末获取源码。} 项目编号:S007,文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、研究内容2.1 数据中心模块2.2 快递类型模块2.3 快…

【Linux】-信号-(信号的产生,保存,处理,以及os是怎么读取硬件的输入,硬件异常和coredump,定时器的原理简单的用户态和内核态的详细介绍)

💖作者:小树苗渴望变成参天大树🎈 🎉作者宣言:认真写好每一篇博客💤 🎊作者gitee:gitee✨ 💞作者专栏:C语言,数据结构初阶,Linux,C 动态规划算法🎄 如 果 你 …

Python datetime 字符串 相互转 datetime

字符串转 datetime from datetime import datetime# 定义要转换的日期时间字符串 dt_str "2021-09-30 15:48:36"# 使用datetime.strptime()函数进行转换 dt_obj datetime.strptime(dt_str, "%Y-%m-%d %H:%M:%S") print(dt_obj)datetime 转字符串 from …

(蓝桥杯)1125 第 4 场算法双周赛题解+AC代码(c++/java)

题目一&#xff1a;验题人的生日【算法赛】 验题人的生日【算法赛】 - 蓝桥云课 (lanqiao.cn) 思路&#xff1a; 1.又是偶数&#xff0c;又是质数&#xff0c;那么只有2喽 AC_Code:C #include <iostream> using namespace std; int main() {cout<<2;return 0; …

elasticsearch聚合、自动补全、数据同步

目录 一、数据聚合1.1 聚合的种类1.2 DSL实现聚合1.2.1 Bucket聚合语法1.2.2 聚合结果排序1.2.3 限定聚合范围1.2.4 Metric聚合语法 1.3 RestAPI实现聚合 二、自动补全2.1 拼音分词器2.2 自定义分词器2.3 自动补全查询2.4 RestAPI实现自动补全 三、数据同步3.1 思路分析3.1.1 同…

键盘打字盲打练习系列之指法练习——2

一.欢迎来到我的酒馆 盲打&#xff0c;指法练习&#xff01; 目录 一.欢迎来到我的酒馆二.开始练习 二.开始练习 前面一个章节简单地介绍了基准键位、字母键位和数字符号键位指法&#xff0c;在这个章节详细介绍指法。有了前面的章节的基础练习&#xff0c;相信大家对盲打也有了…

java设计模式学习之【适配器模式】

文章目录 引言适配器模式简介定义与用途&#xff1a;实现方式&#xff1a;类型 使用场景优势与劣势适配器模式在Spring中的应用多媒体播放器示例代码地址 引言 在我们的日常生活中&#xff0c;适配器无处不在&#xff1a;无论是将不同国家的插头转换成本地标准&#xff0c;还是…

golang WaitGroup的使用与底层实现

使用的go版本为 go1.21.2 首先我们写一个简单的WaitGroup的使用代码 package mainimport ("fmt""sync" )func main() {var wg sync.WaitGroupwg.Add(1)go func() {defer wg.Done()fmt.Println("xiaochuan")}()wg.Wait() }WaitGroup的基本使用场…

AI 文本转视频(视频生产工具分享)

AI 文本转视频&#xff08;视频生产工具分享&#xff09; 介绍 ​ 想要根据任何描述轻松创建有趣的视频吗&#xff1f;然后&#xff0c;您应该尝试使用人工智能视频生成工具。毫无疑问&#xff0c;人工智能是未来。人工智能视频生成器可以轻松地从任何文本制作视频。只需几分…

(一)Tiki-taka算法(TTA)求解无人机三维路径规划研究(MATLAB)

一、无人机模型简介&#xff1a; 单个无人机三维路径规划问题及其建模_IT猿手的博客-CSDN博客 参考文献&#xff1a; [1]胡观凯,钟建华,李永正,黎万洪.基于IPSO-GA算法的无人机三维路径规划[J].现代电子技术,2023,46(07):115-120 二、Tiki-taka算法&#xff08;TTA&#xf…

为何要隐藏IP地址?代理ip在网络安全和隐私保护中的作用是什么?

目录 前言 一、为何要隐藏IP地址&#xff1f; 1. 保护隐私。 2. 防止网络攻击。 3. 避免限制和审查。 二、网络上哪些行为需要隐藏IP和更换IP&#xff1f; 1. 下载种子文件。 2. 访问受限网站。 3. 保护网络隐私。 4. 避免被封禁。 三、代理IP在网络安全和隐私保护中…

数据结构-04-队列

1-队列的结构和特点 生活中我们排队买票&#xff0c;先来的先买&#xff0c;后来的人只能站末尾&#xff0c;不允许插队。先进者先出&#xff0c;这就是典型的"队列"。队列跟栈非常相似&#xff0c;支持的操作也很有限&#xff0c;最基本的操作也是两个&#xff1a;入…

Paraformer 语音识别原理

Paraformer(Parallel Transformer)非自回归端到端语音系统需要解决两个问题&#xff1a; 准确预测输出序列长度&#xff0c;送入预测语音信号判断包含多少文字。 如何从encoder 的输出中提取隐层表征&#xff0c;作为decoder的输入。 采用一个预测器&#xff08;Predictor&…

windows配置go调用python的编译环境

go是支持调用python代码的&#xff0c;之前写了几篇linux的部署教程&#xff0c;因为觉得windows的不复杂就没有写&#xff0c;结果今天新部署一个Windows的环境&#xff0c;有些步骤想不起来了&#xff0c;好记性不如烂笔头&#xff0c;还是记录一下吧。 这些是之前写的linux…

Vue3Element-plus编写一个简版的字典服务

之前公司有维护过一个内部的字典平台,基本步骤和页面如下 添加字典属性弹窗 添加枚举值弹窗 基本业务代码如下 核心代码 import { defineStore } from pinia export const useDictionary defineStore(dictionary, {state: () > ({dict: [],dictObj: {},}),actions: {s…

C语言-指针讲解(4)

在上一篇博客中&#xff1a; C语言-指针讲解(3) 我们给大家介绍了指针进阶的用法 让下面我们来回顾一下讲了什么吧&#xff1a; 1.字符指针变量类型以及用法 2.数组指针本质上是一个指针&#xff0c;里面存放数组的地址。而指针数组本质上是个数组&#xff0c;里面存放的是指针…

知识图谱最简单的demo实现

一、简介 知识图谱整个建立过程可以分为以下几点&#xff1a; #mermaid-svg-zJuLB8k8EgBQF8M0 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-zJuLB8k8EgBQF8M0 .error-icon{fill:#552222;}#mermaid-svg-zJuLB8k8E…

图片点击放大

在列表中添加插槽 <template slot-scope"scope">&#xff0c;获取当前点击的数据 在图片中添加点击事件的方法&#xff0c;用来弹出窗口 <vxe-columnfield"icon"title"等级图标"><template slot-scope"scope"><…

Kubernetes(K8s) Pod详解-05

Pod详解 Pod介绍 Pod结构 每个Pod中都可以包含一个或者多个容器&#xff0c;这些容器可以分为两类&#xff1a; 用户程序所在的容器&#xff0c;数量可多可少 Pause容器&#xff0c;这是每个Pod都会有的一个根容器&#xff0c;它的作用有两个&#xff1a; 可以以它为依据…

hadoop完全分布式搭建

文章目录 集群部署规划服务器准备Mobaxterm 远程登录实验前准备安装软件工具关闭防火墙 安装 JDK 和 Hadoop创建软件包目录解压软件包配置环境变量 集群搭建先创建 HDFS 工作目录和 LOG 目录配置集群配置环境配置 HDFS 主节点信息、持久化和数据文件的主目录配置 HDFS 默认的数…