【人工智能Ⅱ】实验8:生成对抗网络

实验8:生成对抗网络

一:实验目的

1:理解生成对抗网络的基本原理。

2:学会构建改进的生成对抗网络,如DCGAN、WGAN、WGAN-GP等。

3:学习在更为真实的数据集上应用生成对抗网络的方法。

二:实验要求

1:理解生成对抗网络改进算法的改进内容和改进目的。

2:参考课程资源中的2024年春第八次实验代码,完成生成对抗网络改进算法WGAN或DCGAN网络的实现。

3:在Fashion MNIST数据集上验证生成对抗网络改进算法的效果,并对其进行调优。

4:在玉米数据集上验证生成对抗网络改进算法在真实场景下对复杂数据集的生成结果。(选做)

5:撰写实验报告,对结果进行分析。

三:实验环境

本实验所使用的环境条件如下表所示。

操作系统

Ubuntu(Linux)

程序语言

Python(3.11.4)

第三方依赖

torch, torchvision, matplotlib等

四:实验原理

1:GAN

GAN的中文名称为生成对抗网络。GAN是一种深度学习模型,由一个生成器网络和一个判别器网络组成,它们相互竞争地学习生成逼真的数据样本。GAN的核心思想是通过对抗训练来使生成器生成逼真的数据样本,同时训练判别器来区分真实数据样本和生成的数据样本。

【1】生成器网络

生成器网络的目标是接收一个随机噪声向量(通常服从某种先验分布,如均匀分布或正态分布)作为输入,并生成与真实数据样本相似的假数据样本。生成器网络通常由一系列反卷积层(也称为转置卷积层)组成,用于逐渐将输入噪声向量映射到数据空间。生成器网络的输出通常通过某种激活函数(如sigmoid、tanh等)进行转换,以确保生成的数据在合适的范围内。


生成器的训练过程如下图所示。

【2】判别器网络

判别器网络的目标是接收真实数据样本或生成的假数据样本作为输入,并输出一个标量值,表示输入数据是真实数据样本的概率。判别器网络通常由一系列卷积层组成,用于从输入数据中提取特征,并最终将这些特征映射到一个标量输出。判别器的输出通常通过sigmoid激活函数进行转换,将其限制在0到1之间,以表示输入数据是真实数据样本的概率。


判别器的训练过程如下图所示。

【3】对抗训练

在对抗训练中,生成器和判别器网络相互竞争,以改善其性能。生成器试图生成越来越逼真的假数据样本,以欺骗判别器,使其无法区分生成的假数据样本和真实数据样本。而判别器则试图提高其准确性,以尽可能准确地区分真实数据样本和生成的假数据样本。这种竞争的动态最终导致了生成器生成逼真的数据样本。

【4】损失函数

生成对抗网络使用两个损失函数来训练生成器和判别器。对于判别器,损失函数通常是二元交叉熵损失函数,用于衡量判别器在真实数据样本和生成的假数据样本上的分类性能。对于生成器,损失函数通常是判别器在生成的假数据样本上的输出与真实标签(即1)之间的二元交叉熵损失函数。通过最小化生成器和判别器的损失函数,可以实现对抗训练。

2:GAN的变种

目前GAN的变体主要从网络结构、条件生成网络、图像翻译、归一化和限制、损失函数、评价指标等方面进行改进。

变体的发展过程如下图所示。

其中,DCGAN(Deep Convolutional Generative Adversarial Networks)、WGAN(Wasserstein Generative Adversarial Networks)和 WGAN-GP(Wasserstein Generative Adversarial Networks with Gradient Penalty)都是GAN的常见变体,用于生成逼真的图像。

上述三种GAN变种的对比如下:

【1】损失函数

DCGAN:DCGAN使用交叉熵损失函数来训练生成器和判别器。

WGAN:WGAN引入了Wasserstein距离作为生成器和判别器之间的损失函数。Wasserstein距离能够更好地衡量生成分布和真实分布之间的差异,从而提高了训练的稳定性。

WGAN-GP:WGAN-GP在WGAN的基础上引入了梯度惩罚项,用于限制判别器的梯度,从而进一步提高了训练的稳定性和生成图像的质量。

【2】训练稳定性

DCGAN:DCGAN在训练过程中可能会出现训练不稳定的问题,例如模式崩溃(mode collapse)和梯度消失等。

WGAN:WGAN的损失函数设计使其更加稳定,可以缓解训练过程中出现的模式崩溃和梯度消失等问题,有助于生成更加高质量和多样化的图像。

WGAN-GP:WGAN-GP进一步改进了WGAN的稳定性,通过梯度惩罚项有效地限制了判别器的梯度,减轻了训练过程中的梯度爆炸和梯度消失问题。

五:算法流程

  1. 数据准备。准备训练所需的Fashion MNIST数据集。
  2. 网络构建。利用PyTorch框架搭建不同GAN的判别器和生成器。
  3. 可视化数据。导出生成器和判别器随着迭代次数所变化的损失曲线图像,同时利用生成器生成图像。

六:实验展示

1:DCGAN在Fashion MNIST数据集上的应用

DCGAN的生成器网络构建,如下图所示。

DCGAN的判别器网络构建,如下图所示。

DCGAN使用的损失函数为BCE损失函数,优化器使用Adam,优化器的学习率为0.0002,训练迭代次数为30次。

DCGAN中判别器和生成器的损失曲线,如下图所示。其中,橘色部分为生成器、蓝色部分为判别器。

DCGAN的生成效果,如下图所示。

2:WGAN在Fashion MNIST数据集上的应用

    WGAN的生成器网络构建,如下图所示。

WGAN的判别器网络构建,如下图所示。

WGAN使用的损失函数为Wasserstein距离损失函数,优化器使用RMSprop,优化器的学习率为0.00005,训练迭代次数为30次。

WGAN中判别器和生成器的损失曲线,如下图所示。其中,橘色部分为生成器、蓝色部分为判别器。

WGAN的生成效果,如下图所示。

3:DCGAN在大米数据集上的应用

DCGAN的超参数和训练过程与第1部分类似,但是迭代次数变为了500,加载数据集时将图像尺寸压缩为了28*28。


DCGAN中判别器和生成器的损失曲线,如下图所示。其中,橘色部分为生成器、蓝色部分为判别器。


DCGAN的生成效果,如下图所示。生成的图像中包含4粒大米。


源数据集的图像样本如下。

4:DCGAN在玉米数据集上的应用

DCGAN的超参数和训练过程与第1部分类似,但是迭代次数变为了500,加载数据集时将图像尺寸压缩为了28*28。


DCGAN中判别器和生成器的损失曲线,如下图所示。其中,橘色部分为生成器、蓝色部分为判别器。


DCGAN的生成效果,如下图所示。生成的图像中包含4个玉米。


源数据集的图像样本如下。

七:实验结论与心得

1:生成对抗网络利用生成器和判别器之间的对抗训练机制,能够有效地生成逼真的数据样本,在图像生成、文本生成、图像转换等领域广泛应用。

2:GAN的性能很大程度上受到超参数的影响,如学习率、潜在空间维度、网络结构等。

3:GAN相比较其他生成模型(玻尔兹曼机等),只用到了反向传播,不需要复杂的马尔科夫链,且可以产生更加清晰、真实的样本。

4:GAN采用无监督的学习方式训练,可以被广泛用在无监督学习和半监督学习领域。

5:如果生成器生成的样本,判别器判定为真实的,则说明生成器的效果是较好的,因而可以用判别器来评价生成器,即判别的性能越差,说明生成器的性能越好。

八:主要代码

1:DCGAN源代码

import torch

from torch import nn, optim

from torchvision import datasets, transforms, utils

from torch.utils.data import DataLoader

import matplotlib.pyplot as plt

# 设备配置

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# 数据预处理

transform = transforms.Compose([

    transforms.ToTensor(),

    transforms.Normalize((0.5,), (0.5,))

])

# 加载数据集

train_data = datasets.FashionMNIST(root='./data', train=True, download=True, transform=transform)

train_loader = DataLoader(train_data, batch_size=128, shuffle=True)

# 生成器

class Generator(nn.Module):

    def __init__(self):

        super(Generator, self).__init__()

        self.main = nn.Sequential(

            # 输入是一个 100 维的噪声向量,我们将其投射并reshape 1x1x128 的大小

            nn.Linear(100, 128*7*7),

            nn.BatchNorm1d(128*7*7),

            nn.ReLU(True),

            nn.Unflatten(1, (128, 7, 7)),

            nn.ConvTranspose2d(128, 64, 4, stride=2, padding=1),

            nn.BatchNorm2d(64),

            nn.ReLU(True),

            nn.ConvTranspose2d(64, 1, 4, stride=2, padding=1),

            nn.Tanh()

        )

    def forward(self, x):

        return self.main(x)

# 判别器

class Discriminator(nn.Module):

    def __init__(self):

        super(Discriminator, self).__init__()

        self.main = nn.Sequential(

            nn.Conv2d(1, 64, 4, stride=2, padding=1),

            nn.LeakyReLU(0.2, inplace=True),

            nn.Conv2d(64, 128, 4, stride=2, padding=1),

            nn.BatchNorm2d(128),

            nn.LeakyReLU(0.2, inplace=True),

            nn.Flatten(),

            nn.Linear(128*7*7, 1),

            nn.Sigmoid()

        )

    def forward(self, x):

        return self.main(x)

# 实例化模型

netG = Generator().to(device)

netD = Discriminator().to(device)

# 损失函数和优化器

criterion = nn.BCELoss()

optimizerD = optim.Adam(netD.parameters(), lr=0.0002, betas=(0.5, 0.999))

optimizerG = optim.Adam(netG.parameters(), lr=0.0002, betas=(0.5, 0.999))

# 训练

num_epochs = 30

lossD = []

lossG = []

for epoch in range(num_epochs):

    for i, (images, _) in enumerate(train_loader):

        # 更新判别器:maximize log(D(x)) + log(1 - D(G(z)))

        netD.zero_grad()

        real_data = images.to(device)

        batch_size = real_data.size(0)

        labels = torch.full((batch_size,), 1, dtype=torch.float, device=device)

        output = netD(real_data)

        labels = labels.view(-1, 1)

        lossD_real = criterion(output, labels)

        lossD_real.backward()

        noise = torch.randn(batch_size, 100, device=device)

        fake_data = netG(noise)

        labels.fill_(0)

        output = netD(fake_data.detach())

        lossD_fake = criterion(output, labels)

        lossD_fake.backward()

        optimizerD.step()

        # 更新生成器:minimize log(1 - D(G(z))) ≈ maximize log(D(G(z)))

        netG.zero_grad()

        labels.fill_(1)  # fake labels are real for generator cost

        output = netD(fake_data)

        loss_G = criterion(output, labels)

        loss_G.backward()

        optimizerG.step()

       

        lossD.append(lossD_real.item() + lossD_fake.item())

        lossG.append(loss_G.item())

        if (i+1) % 100 == 0:

            print(f'Epoch [{epoch+1}/{num_epochs}], Step [{i+1}/{len(train_loader)}], Loss_D: {lossD_real.item() + lossD_fake.item():.4f}, Loss_G: {loss_G.item():.4f}')

# 可视化一些生成的图片

noise = torch.randn(64, 100, device=device)

fake_images = netG(noise)

fake_images = fake_images.reshape(fake_images.size(0), 1, 28, 28)

fake_images = (fake_images + 1) / 2  # 调整像素值范围到 [0, 1]

img_grid = utils.make_grid(fake_images, normalize=True)

img_grid = img_grid.cpu()  # 首先将张量移至 CPU

plt.imshow(img_grid.permute(1, 2, 0).numpy())

plt.savefig("z.png")

plt.close()

# 绘制曲线

y_values1 = lossD

y_values2 = lossG

x_values = range(1, len(y_values1) + 1)

plt.plot(x_values, y_values1, label='Discriminator Loss')

plt.plot(x_values, y_values2, label='Generator Loss')

# 添加标题和标签

plt.title('loss for discriminator and generator')

plt.xlabel('Batch')

plt.ylabel('Loss')

plt.legend(['Discriminator', 'Generator'])

# 显示图形

plt.savefig('loss-DCGAN.png')

2:WGAN源代码

import torch

import torch.nn as nn

import torch.optim as optim

from torchvision import datasets, transforms

import matplotlib.pyplot as plt

# 设备配置

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# 定义生成器网络

class Generator(nn.Module):

    def __init__(self, latent_dim, img_shape):

        super(Generator, self).__init__()

        self.model = nn.Sequential(

            nn.Linear(latent_dim, 128),

            nn.LeakyReLU(0.2, inplace=True),

            nn.Linear(128, 256),

            nn.BatchNorm1d(256),

            nn.LeakyReLU(0.2, inplace=True),

            nn.Linear(256, img_shape),

            nn.Tanh()

        )

    def forward(self, z):

        z = z.to(next(self.parameters()).device)

        img = self.model(z)

        return img

# 定义判别器网络

class Discriminator(nn.Module):

    def __init__(self, img_shape):

        super(Discriminator, self).__init__()

        self.model = nn.Sequential(

            nn.Linear(img_shape, 256),

            nn.LeakyReLU(0.2, inplace=True),

            nn.Linear(256, 128),

            nn.BatchNorm1d(128),

            nn.LeakyReLU(0.2, inplace=True),

            nn.Linear(128, 1)

        )

    def forward(self, img):

        validity = self.model(img)

        return validity

# 参数

latent_dim = 100

img_shape = 28 * 28

batch_size = 64

# 准备数据

transform = transforms.Compose([

    transforms.ToTensor(),

    transforms.Normalize((0.5,), (0.5,))

])

train_dataset = datasets.FashionMNIST(root='./data', train=True, transform=transform, download=True)

train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=batch_size, shuffle=True)

# 初始化网络和优化器

generator = Generator(latent_dim, img_shape).to(device)

discriminator = Discriminator(img_shape).to(device)

optimizer_G = optim.RMSprop(generator.parameters(), lr=0.00005)

optimizer_D = optim.RMSprop(discriminator.parameters(), lr=0.00005)

# 训练模型

lossD = []

lossG = []

n_epochs = 30

for epoch in range(n_epochs):

    for i, (imgs, _) in enumerate(train_loader):

        # 训练判别器

        optimizer_D.zero_grad()

        # 真实图像

        real_imgs = imgs.view(-1, img_shape).to(device)

        validity_real = discriminator(real_imgs)

       

        # 生成潜在空间向量

        z = torch.randn(batch_size, latent_dim).to(device)

        fake_imgs = generator(z).detach()

        validity_fake = discriminator(fake_imgs)

        # 计算损失

        d_loss = -torch.mean(validity_real) + torch.mean(validity_fake)

        d_loss_item = d_loss.item()

       

        # 反向传播和优化

        d_loss.backward()

        optimizer_D.step()

        # 限制判别器参数

        for p in discriminator.parameters():

            p.data.clamp_(-0.01, 0.01)

        # 训练生成器

        optimizer_G.zero_grad()

        # 生成潜在空间向量

        z = torch.randn(batch_size, latent_dim)

        fake_imgs = generator(z)

        validity = discriminator(fake_imgs)

        # 计算损失

        g_loss = -torch.mean(validity)

        g_loss_item = g_loss.item()

        # 反向传播和优化

        g_loss.backward()

        optimizer_G.step()

        lossD.append(d_loss.item())

        lossG.append(g_loss.item())

       

        # 打印损失信息

        if i % 500 == 0:

            print(

                "[Epoch %d/%d] [Batch %d/%d] [D loss: %.4f] [G loss: %.4f]"

                % (epoch, n_epochs, i, len(train_loader), d_loss.item(), g_loss.item())

            )

# 生成图像

z = torch.randn(64, latent_dim, device=device)

generated_images = generator(z)

generated_images = generated_images.detach().cpu().numpy()

# 显示生成的图像

fig, axes = plt.subplots(nrows=8, ncols=8, figsize=(10, 10))

for i, ax in enumerate(axes.flatten()):

    ax.imshow(generated_images[i].reshape(28, 28), cmap='gray')

    ax.axis('off')

plt.tight_layout()

plt.savefig('zz.png')

plt.close()

# 绘制曲线

y_values1 = lossD

y_values2 = lossG

x_values = range(1, len(y_values1) + 1)

plt.plot(x_values, y_values1, label='Discriminator Loss')

plt.plot(x_values, y_values2, label='Generator Loss')

# 添加标题和标签

plt.title('loss for discriminator and generator')

plt.xlabel('Batch')

plt.ylabel('Loss')

plt.legend(['Discriminator', 'Generator'])

# 显示图形

plt.savefig('loss-WGAN.png')

3:玉米数据集上的DCGAN

import torch

from torch import nn, optim

from torchvision import transforms, utils

import matplotlib.pyplot as plt

import os

from PIL import Image

from torch.utils.data import Dataset, DataLoader

class CustomDataset(Dataset):

    def __init__(self, data_dir, transform=None):

        self.data_dir = data_dir

        self.transform = transform

        self.image_files = os.listdir(data_dir)

    def __len__(self):

        return len(self.image_files)

    def __getitem__(self, idx):

        img_name = os.path.join(self.data_dir, self.image_files[idx])

        image = Image.open(img_name).convert("RGB")  # 读取图像,并转换为RGB格式

        if self.transform:

            image = self.transform(image)

        return image

# 数据集文件夹路径

data_dir = r"/home/ubuntu/zz-test"

# 设备配置

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# 数据预处理

transform = transforms.Compose([

    transforms.Resize((28, 28)),  # 将图像调整为28x28大小

    transforms.ToTensor(),

    transforms.Normalize((0.5,), (0.5,))

])

# 加载数据集

train_data = CustomDataset(data_dir, transform=transform)

train_loader = DataLoader(train_data, batch_size=32, shuffle=True)

# 生成器

class Generator(nn.Module):

    def __init__(self):

        super(Generator, self).__init__()

        self.main = nn.Sequential(

            nn.Linear(100, 128*7*7),

            nn.BatchNorm1d(128*7*7),

            nn.ReLU(True),

            nn.Unflatten(1, (128, 7, 7)),

            nn.ConvTranspose2d(128, 64, 4, stride=2, padding=1),

            nn.BatchNorm2d(64),

            nn.ReLU(True),

            nn.ConvTranspose2d(64, 3, 4, stride=2, padding=1),  # 修改这里的输出通道数为3

            nn.Tanh()

        )

    def forward(self, x):

        return self.main(x)

# 判别器

class Discriminator(nn.Module):

    def __init__(self):

        super(Discriminator, self).__init__()

        self.main = nn.Sequential(

            nn.Conv2d(3, 64, 4, stride=2, padding=1),  # 修改这里的输入通道数为3

            nn.LeakyReLU(0.2, inplace=True),

            nn.Conv2d(64, 128, 4, stride=2, padding=1),

            nn.BatchNorm2d(128),

            nn.LeakyReLU(0.2, inplace=True),

            nn.Flatten(),

            nn.Linear(128*7*7, 1),

            nn.Sigmoid()

        )

    def forward(self, x):

        return self.main(x)

# 实例化模型

netG = Generator().to(device)

netD = Discriminator().to(device)

# 损失函数和优化器

criterion = nn.BCELoss()

optimizerD = optim.Adam(netD.parameters(), lr=0.0002, betas=(0.5, 0.999))

optimizerG = optim.Adam(netG.parameters(), lr=0.0002, betas=(0.5, 0.999))

# 训练

num_epochs = 500

lossD = []

lossG = []

for epoch in range(num_epochs):

    for i, images in enumerate(train_loader):

        # 更新判别器:maximize log(D(x)) + log(1 - D(G(z)))

        netD.zero_grad()

        real_data = images.to(device)

        batch_size = real_data.size(0)

        labels = torch.full((batch_size,), 1, dtype=torch.float, device=device)

        output = netD(real_data)

        labels = labels.view(-1, 1)

        lossD_real = criterion(output, labels)

        lossD_real.backward()

        noise = torch.randn(batch_size, 100, device=device)

        fake_data = netG(noise)

        labels.fill_(0)

        output = netD(fake_data.detach())

        lossD_fake = criterion(output, labels)

        lossD_fake.backward()

        optimizerD.step()

        # 更新生成器:minimize log(1 - D(G(z))) ≈ maximize log(D(G(z)))

        netG.zero_grad()

        labels.fill_(1)  # fake labels are real for generator cost

        output = netD(fake_data)

        loss_G = criterion(output, labels)

        loss_G.backward()

        optimizerG.step()

       

        lossD.append(lossD_real.item() + lossD_fake.item())

        lossG.append(loss_G.item())

        if (i+1) % 100 == 0:

            print(f'Epoch [{epoch+1}/{num_epochs}], Step [{i+1}/{len(train_loader)}], Loss_D: {lossD_real.item() + lossD_fake.item():.4f}, Loss_G: {loss_G.item():.4f}')

# 保存生成的图像

noise = torch.randn(4, 100, device=device)

fake_images = netG(noise)

fake_images = (fake_images + 1) / 2  # 调整像素值范围到 [0, 1]

img_grid = utils.make_grid(fake_images, nrow=2, normalize=True)  # 将图像网格化,并且每行显示8张图像

img_grid = img_grid.permute(1, 2, 0).cpu().numpy()  # 将张量转换为NumPy数组,并将通道维度放到最后

plt.imshow(img_grid)

plt.axis('off')  # 关闭坐标轴

plt.savefig("generated_images.png")

plt.close()

# 绘制损失曲线

plt.plot(lossD, label='Discriminator Loss')

plt.plot(lossG, label='Generator Loss')

# 添加标题和标签

plt.title('Loss for Discriminator and Generator')

plt.xlabel('Batch')

plt.ylabel('Loss')

plt.legend()

# 保存损失曲线图

plt.savefig('loss-DCGAN.png')

plt.close()

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

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

相关文章

Java反序列化-RMI流程分析

RMI 在反序列化里漏洞里面是很常用的,它是一个分布式的思想。 RMI概述 RMI 通常包含两个独立的程序,一个服务端 和 一个客户端。服务端通过绑定这个远程对象类,它可以封装网络操作。客户端层面上只需要传递一个名字,还有地址。 …

LNMP 环境下使用 Zstd 压缩优化网站备份脚本

网站的备份一直都是网站运营、服务器运维中很重要的一环,明月无论是在自己的服务器还是客户的代运维服务器上都是非常重视网站备份的,尤其热衷于优化网站备份这块儿,毕竟明月自己的服务器配置一直都是最低的 1H1G 呀,就这配置常年…

(函数)空格填充(C语言)

一、运行结果&#xff1b; 二、源代码&#xff1b; # define _CRT_SECURE_NO_WARNINGS # include <stdio.h>//声明空格填充函数&#xff1b; void space(char a[100]);int main() {//初始化变量值&#xff1b;char a[100] { 0 };//获取用户输入的数据&#xff1b;print…

人工智能学习笔记(1):了解sklearn

sklearn 简介 Sklearn是一个基于Python语言的开源机器学习库。全称Scikit-Learn&#xff0c;是建立在诸如NumPy、SciPy和matplotlib等其他Python库之上&#xff0c;为用户提供了一系列高质量的机器学习算法&#xff0c;其典型特点有&#xff1a; 简单有效的工具进行预测数据分…

Kafka生产者消息异步发送并返回发送信息api编写教程

1.引入依赖&#xff08;pox.xml文件&#xff09; <dependencies> <dependency> <groupId>org.apache.kafka</groupId> <artifactId>kafka-clients</artifactId> <version>3.6.2</version> </dependency> </depende…

泰中完成潜艇采购谈判:有三个亮点值得关注

泰中完成潜艇采购谈判&#xff1a;有三个亮点值得关注 --如何写好产品采购制造类合同&#xff08;或协议&#xff09;才能有效维护你的利益 据新华网消息&#xff1a;泰国和中国已经完成关于泰国皇家海军购买中国制造的S26T元级潜艇的谈判。这份交易正在等待内阁的最终批准。…

WALT算法简介

WALT(Windows-Assist Load Tracing)算法是由Qcom开发&#xff0c; 通过把时间划分为窗口&#xff0c;对 task运行时间和CPU负载进行跟踪计算的方法。为任务调度、迁移、负载均衡及CPU调频 提供输入。 WALT相对PELT算法&#xff0c;更能及时反映负载变化&#xff0c; 更适用于…

黄金期货与现货黄金有什么区别?

如今&#xff0c;黄金成为了众多投资者的热门选择。在黄金投资市场中&#xff0c;有多种形式可供选择&#xff0c;比如纸黄金、实物黄金、黄金期货以及现货黄金等。其中&#xff0c;纸黄金和实物黄金虽然具有其特点&#xff0c;但所需资金量较大且收益表现相对不明显。相对而言…

哈希重要思想续——布隆过滤器

布隆过滤器 一 概念1.1布隆过滤器的提出2.概念 二 模拟实现2.1 三个仿函数setTest 全代码三 实际应用 一 概念 1.1布隆过滤器的提出 我们在使用新闻客户端看新闻时&#xff0c;它会给我们不停地推荐新的内容&#xff0c;它每次推荐时要去重&#xff0c;去掉那些已经看过的内容…

Java面试八股之守护线程和普通线程的区别

守护线程和普通线程的区别 生命周期差异&#xff1a; 普通线程&#xff08;也称为用户线程&#xff09;&#xff1a;这类线程的生命周期与程序的生命周期独立。它们会一直运行直到完成自己的任务或主动结束&#xff0c;如果一个程序中只剩下普通线程在运行&#xff0c;即使主…

JavaScript、Kotlin、Flutter可以开发鸿蒙APP吗?

自从去年华为宣布推出「鸿蒙Next」版本开始&#xff0c;标志着其操作系统的全面革新。鸿蒙Next将摒弃所有基于AOSP的代码&#xff0c;与Android系统彻底分离&#xff0c;实现完全自主的研发路径。通过精简约40%的冗余代码&#xff0c;鸿蒙Next致力于构建一个更高效、更流畅的系…

混合动力电动汽车介绍(二)

接续前一章内容&#xff0c;本篇文章介绍混合动力汽车串联、并联和混联的系统组成和工作原理。 一、串联混合动力电动汽车的系统组成和工作原理 上图为串联混合动力电动汽车的结构简图。汽车由电动机-发电机驱动行驶&#xff0c;电机控制器的动力来自油箱-发动机-发电机-发电机…

Python画图(多图展示在一个平面)

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

上位机图像处理和嵌入式模块部署(f407 mcu vs f103)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 对于一部分嵌入式场景来说&#xff0c;f103其实已经足够了&#xff0c;特别是要求不高的低速场合。如果开发的代码比较多&#xff0c;还可以选用更…

Java面试八股之线程池中submit和execute方法的区别

线程池中submit和execute方法的区别 接口和返回值类型: execute()方法属于Executor接口&#xff0c;它接收一个实现了Runnable接口的任务&#xff0c;并不返回任何结果。它的主要目的是异步执行任务&#xff0c;不关心任务的执行结果。 submit()方法则是ExecutorService接口…

Vue渲染函数与JSX指南

title: Vue渲染函数与JSX指南 date: 2024/6/3 下午6:43:53 updated: 2024/6/3 下午6:43:53 categories: 前端开发 tags:Vue渲染JSX基础性能优化组件对比React JSX大项目测试策略 第1章&#xff1a;Vue.js入门 Vue.js的历史和背景 Vue.js是一个用于构建用户界面的JavaScript框…

模拟堆-java

模拟堆也是对堆的一次深入理解和一些其它操作&#xff0c;可以了解一下。 文章目录 前言 一、模拟堆 二、算法思路 1.结点上移 2.结点下移 3.插入一个数 4.输出当前集合的最小值 5.删除当前集合的最小值&#xff08;数据保证此时的最小值唯一&#xff09; 6.删除第k个插入的数 …

初识STM32单片机-ADC和DMA

初识STM32单片机-ADC和DMA 一、ADC(模拟数字转换器)简介二、ADC基本结构三、DMA(直接存储器读取)简介四、DMA框图和基本结构五、DMA应用实例5.1 数据转运DMA5.2 ADC扫描DMA 六、程序编码6.1 ADC单通道-电位器6.2 ADC多通道-电位器和光敏\热敏\反射红外传感器6.3 DMA数据转运6.4…

代码随想录算法训练Day28|LeetCode93-复原IP地址、LeetCode78-子集问题、LeetCode90-子集2

复原IP地址 题目描述 力扣93-复原IP地址 有效 IP 地址 正好由四个整数&#xff08;每个整数位于 0 到 255 之间组成&#xff0c;且不能含有前导 0&#xff09;&#xff0c;整数之间用 . 分隔。 例如&#xff1a;"0.1.2.201" 和 "192.168.1.1" 是 有效 …

贝锐花生壳DDNS:远程访问数据库,仅需简单3步

在当今数字化时代&#xff0c;数据的远程访问和管理变得至关重要。无论是企业还是个人开发者&#xff0c;都需要一种简单、安全的方式来远程访问和管理本地部署的数据库&#xff0c;如MySQL、PostgreSQL、MongoDB等。贝锐花生壳DDNS服务提供了一个完美的解决方案&#xff0c;通…