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.5return imgtrain = 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 = discriminatorself.generator = generatorself.latent_dim = latent_dimdef compile(self, d_optimizer, g_optimizer):super(DCGAN, self).compile()# 生成器和判别器的损失函数均为 BinaryCrossentropyself.loss_fn = losses.BinaryCrossentropy()self.d_optimizer = d_optimizerself.g_optimizer = g_optimizerself.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")@propertydef 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 spacebatch_size = tf.shape(real_images)[0]# 从多元标准正态分布中随机采样一批向量random_latent_vectors = tf.random.normal(shape=(batch_size, self.latent_dim))# Train the discriminator on fake imageswith 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 metricsself.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实战——使用变分自编码器生成面部图像

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

leetcode面试经典150题——34 有效的数独(矩阵)

题目: 有效的数独 描述: 请你判断一个 9 x 9 的数独是否有效。只需要 根据以下规则 ,验证已经填入的数字是否有效即可。 数字 1-9 在每一行只能出现一次。 数字 1-9 在每一列只能出现一次。 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出…

机器学习入门(第六天)——支持向量机(升维打击)

Support vector machines 知识树 Knowledge tree 苹果表示重点 间隔:使用了几何间隔,保证w b的度量,感知机则是函数间隔 间隔最大化思想:则是支持向量机的独有,这使得它找到最优超平面 核函数:面试当中可…

C语言-指针讲解(4)

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

⭐ Unity 开发bug —— 打包后shader失效或者bug (我这里用Shader做两张图片的合并发现了问题)

1.这里我代码没啥问题~~~编辑器里也没毛病 void Start(){// 加载底图和上层图片string backgroundImagePath Application.streamingAssetsPath "/background.jpg";Texture2D backgroundTexture new Texture2D(2, 2);byte[] backgroundImageData System.IO.File.R…

超大规模集成电路设计----学习框架(一)

本文仅供学习,不作任何商业用途,严禁转载。绝大部分资料来自----数字集成电路——电路、系统与设计(第二版)及中国科学院段成华教授PPT 超大规模集成电路设计----学习框架(一) 这门课在学什么?这门课该怎么学&#xf…

能源企业管理ERP系统都有哪些?可以帮助企业解决哪些难点

能源企业在不同的发展阶段面对的经营压力以及遇到的管理问题各异,随着部分产品结构的复杂化,日常经营管理工作也愈加繁琐。 有些能源企业内部存在信息传递不畅、经营数据统计不及时、部门协作效率低、多仓库和多平台数据不统一等情况,而这些…

科技平权,哪吒汽车又双叒OTA了

OTA升级“学霸”再有新动作。11月30日,平均一、两个月就会OTA升级一次的哪吒汽车再度“进化”,同时启动哪吒S和哪吒GT的新一轮OTA升级。此轮OTA升级,哪吒汽车不但将百万豪车才有的赛道级技术,价值数万元的哪吒卫士、一键遥控泊车功…

PHPExcel 导出Excel报错:PHPExcel_IOFactory::load()

背景 近期在做 excel文件数据导出时,遇到如下报错: iconv(): Detected an illegal character in input string场景:计划任务后台,分步导出 大数据 excel文件发现在加载文件时,会有报错 报错信息 如下: {&q…

(2)(2.2) Lightware SF45/B(350度)

文章目录 前言 1 安装SF45/B 2 连接自动驾驶仪 3 通过地面站进行配置 4 参数说明 前言 Lightware SF45/B 激光雷达(Lightware SF45/B lidar)是一种小型扫描激光雷达(重约 50g),扫描度可达 350 度,扫描范围 50m。 1 安装SF45…

【网络安全】用永恒之蓝(Eternal blue)测试windows系统的安全性

一、kali默认账户和密码都为kali 攻击机:Linux 的 kali 目标机:Windows7 x64 二、kali、metasploit、metasploit 攻击 windows操作系统、metasploit 攻击 永恒之蓝 全流程 ①kali:是黑客攻击机。开源免费的Linux操作系统,含有300…

iPhone苹果手机如何将词令网页添加到苹果iPhone手机桌面快捷打开?

iPhone苹果手机如何将词令网页添加到苹果iPhone手机桌面快捷打开? 1、在iPhone苹果手机上找到「Safari浏览器」,并点击打开; 2、打开Safari浏览器后,输入词令官方网站地址:ciling.cn ; 3、打开词令官网后,点击Safari…

⭐ Unity 里让 Shader 动画在 Scene 面板被持续刷新

写 Unity Shader的时候,只有播放状态下的 Game 面板能看到Shader 顺畅的动态效果,不方便。 想要带有动态效果的 Shader 在 Scene 面板持续更新动画,只需要打开一个开关就能让 Scene 持续刷新动画了。 感谢大家的观看,您的点赞和关…