深度学习Day-32:CycleGAN实战

devtools/2024/9/19 17:00:30/ 标签: 深度学习, 人工智能

  🍨 本文为:[🔗365天深度学习训练营] 中的学习记录博客
 🍖 原作者:[K同学啊 | 接辅导、项目定制]

一、 基础配置

  • 语言环境:Python3.8
  • 编译器选择:Pycharm
  • 深度学习环境:
    • torch==1.12.1+cu113
    • torchvision==0.13.1+cu113

二、 相关代码

1. 文件结构

  • CycleGAN 
    • data
      •  monet2photo
        • testA
        • testB
        • trainA
        • trainB
    • datasets.py  
    • models.py
    • cyclegan.py(主文件)
    • utils.py
    • images(放运行结果)
    • saved_models(放模型)

2. 代码细节

datasets.py

import glob
import random
import osfrom torch.utils.data import Dataset
from PIL import Image
import torchvision.transforms as transformsdef to_rgb(image):rgb_image = Image.new("RGB", image.size)rgb_image.paste(image)return rgb_imageclass ImageDataset(Dataset):def __init__(self, root, transforms_=None, unaligned=False, mode="train"):self.transform = transforms.Compose(transforms_)self.unaligned = unalignedself.files_A = sorted(glob.glob(os.path.join(root, "%sA" % mode) + "/*.*"))self.files_B = sorted(glob.glob(os.path.join(root, "%sB" % mode) + "/*.*"))def __getitem__(self, index):image_A = Image.open(self.files_A[index % len(self.files_A)])if self.unaligned:image_B = Image.open(self.files_B[random.randint(0, len(self.files_B) - 1)])else:image_B = Image.open(self.files_B[index % len(self.files_B)])# Convert grayscale images to rgbif image_A.mode != "RGB":image_A = to_rgb(image_A)if image_B.mode != "RGB":image_B = to_rgb(image_B)item_A = self.transform(image_A)item_B = self.transform(image_B)return {"A": item_A, "B": item_B}def __len__(self):return max(len(self.files_A), len(self.files_B))

models.py

import torch.nn as nn
import torch.nn.functional as F
import torchdef weights_init_normal(m):classname = m.__class__.__name__if classname.find("Conv") != -1:torch.nn.init.normal_(m.weight.data, 0.0, 0.02)if hasattr(m, "bias") and m.bias is not None:torch.nn.init.constant_(m.bias.data, 0.0)elif classname.find("BatchNorm2d") != -1:torch.nn.init.normal_(m.weight.data, 1.0, 0.02)torch.nn.init.constant_(m.bias.data, 0.0)##############################
#           RESNET
##############################class ResidualBlock(nn.Module):def __init__(self, in_features):super(ResidualBlock, self).__init__()self.block = nn.Sequential(nn.ReflectionPad2d(1),nn.Conv2d(in_features, in_features, 3),nn.InstanceNorm2d(in_features),nn.ReLU(inplace=True),nn.ReflectionPad2d(1),nn.Conv2d(in_features, in_features, 3),nn.InstanceNorm2d(in_features),)def forward(self, x):return x + self.block(x)class GeneratorResNet(nn.Module):def __init__(self, input_shape, num_residual_blocks):super(GeneratorResNet, self).__init__()channels = input_shape[0]# Initial convolution blockout_features = 64model = [nn.ReflectionPad2d(channels),nn.Conv2d(channels, out_features, 7),nn.InstanceNorm2d(out_features),nn.ReLU(inplace=True),]in_features = out_features# Downsamplingfor _ in range(2):out_features *= 2model += [nn.Conv2d(in_features, out_features, 3, stride=2, padding=1),nn.InstanceNorm2d(out_features),nn.ReLU(inplace=True),]in_features = out_features# Residual blocksfor _ in range(num_residual_blocks):model += [ResidualBlock(out_features)]# Upsamplingfor _ in range(2):out_features //= 2model += [nn.Upsample(scale_factor=2),nn.Conv2d(in_features, out_features, 3, stride=1, padding=1),nn.InstanceNorm2d(out_features),nn.ReLU(inplace=True),]in_features = out_features# Output layermodel += [nn.ReflectionPad2d(channels), nn.Conv2d(out_features, channels, 7), nn.Tanh()]self.model = nn.Sequential(*model)def forward(self, x):return self.model(x)##############################
#        Discriminator
##############################class Discriminator(nn.Module):def __init__(self, input_shape):super(Discriminator, self).__init__()channels, height, width = input_shape# Calculate output shape of image discriminator (PatchGAN)self.output_shape = (1, height // 2 ** 4, width // 2 ** 4)def discriminator_block(in_filters, out_filters, normalize=True):"""Returns downsampling layers of each discriminator block"""layers = [nn.Conv2d(in_filters, out_filters, 4, stride=2, padding=1)]if normalize:layers.append(nn.InstanceNorm2d(out_filters))layers.append(nn.LeakyReLU(0.2, inplace=True))return layersself.model = nn.Sequential(*discriminator_block(channels, 64, normalize=False),*discriminator_block(64, 128),*discriminator_block(128, 256),*discriminator_block(256, 512),nn.ZeroPad2d((1, 0, 1, 0)),nn.Conv2d(512, 1, 4, padding=1))def forward(self, img):return self.model(img)

utils.py

import random
import time
import datetime
import sysfrom torch.autograd import Variable
import torch
import numpy as npfrom torchvision.utils import save_imageclass ReplayBuffer:def __init__(self, max_size=50):assert max_size > 0, "Empty buffer or trying to create a black hole. Be careful."self.max_size = max_sizeself.data = []def push_and_pop(self, data):to_return = []for element in data.data:element = torch.unsqueeze(element, 0)if len(self.data) < self.max_size:self.data.append(element)to_return.append(element)else:if random.uniform(0, 1) > 0.5:i = random.randint(0, self.max_size - 1)to_return.append(self.data[i].clone())self.data[i] = elementelse:to_return.append(element)return Variable(torch.cat(to_return))class LambdaLR:def __init__(self, n_epochs, offset, decay_start_epoch):assert (n_epochs - decay_start_epoch) > 0, "Decay must start before the training session ends!"self.n_epochs = n_epochsself.offset = offsetself.decay_start_epoch = decay_start_epochdef step(self, epoch):return 1.0 - max(0, epoch + self.offset - self.decay_start_epoch) / (self.n_epochs - self.decay_start_epoch)

cyclegan.py

import argparse
import itertools
from torchvision.utils import save_image, make_grid
from torch.utils.data import DataLoader
from models import *
from datasets import *
from utils import *
import torchimport warnings
warnings.filterwarnings('ignore')parser = argparse.ArgumentParser()
parser.add_argument("--epoch", type=int, default=0, help="epoch to start training from")
parser.add_argument("--n_epochs", type=int, default=4, help="number of epochs of training")
parser.add_argument("--dataset_name", type=str, default="monet2photo", help="name of the dataset")
parser.add_argument("--batch_size", type=int, default=1, help="size of the batches")
parser.add_argument("--lr", type=float, default=0.0002, help="adam: learning rate")
parser.add_argument("--b1", type=float, default=0.5, help="adam: decay of first order momentum of gradient")
parser.add_argument("--b2", type=float, default=0.999, help="adam: decay of first order momentum of gradient")
parser.add_argument("--decay_epoch", type=int, default=2, help="epoch from which to start lr decay")
parser.add_argument("--n_cpu", type=int, default=4, help="number of cpu threads to use during batch generation")
parser.add_argument("--img_height", type=int, default=256, help="size of image height")
parser.add_argument("--img_width", type=int, default=256, help="size of image width")
parser.add_argument("--channels", type=int, default=3, help="number of image channels")
parser.add_argument("--sample_interval", type=int, default=100, help="interval between saving generator outputs")
parser.add_argument("--checkpoint_interval", type=int, default=1, help="interval between saving model checkpoints")
parser.add_argument("--n_residual_blocks", type=int, default=9, help="number of residual blocks in generator")
parser.add_argument("--lambda_cyc", type=float, default=10.0, help="cycle loss weight")
parser.add_argument("--lambda_id", type=float, default=5.0, help="identity loss weight")
opt = parser.parse_args()
print(opt)# Create sample and checkpoint directories
os.makedirs("images/%s" % opt.dataset_name, exist_ok=True)
os.makedirs("saved_models/%s" % opt.dataset_name, exist_ok=True)# Losses
criterion_GAN = torch.nn.MSELoss()
criterion_cycle = torch.nn.L1Loss()
criterion_identity = torch.nn.L1Loss()cuda = torch.cuda.is_available()input_shape = (opt.channels, opt.img_height, opt.img_width)# 初始化生成器鉴别器
G_AB = GeneratorResNet(input_shape, opt.n_residual_blocks)
G_BA = GeneratorResNet(input_shape, opt.n_residual_blocks)
D_A = Discriminator(input_shape)
D_B = Discriminator(input_shape)if cuda:G_AB = G_AB.cuda()G_BA = G_BA.cuda()D_A = D_A.cuda()D_B = D_B.cuda()criterion_GAN.cuda()criterion_cycle.cuda()criterion_identity.cuda()if opt.epoch != 0:# 加载预训练模型G_AB.load_state_dict(torch.load("saved_models/%s/G_AB_%d.pth" % (opt.dataset_name, opt.epoch)))G_BA.load_state_dict(torch.load("saved_models/%s/G_BA_%d.pth" % (opt.dataset_name, opt.epoch)))D_A.load_state_dict(torch.load("saved_models/%s/D_A_%d.pth" % (opt.dataset_name, opt.epoch)))D_B.load_state_dict(torch.load("saved_models/%s/D_B_%d.pth" % (opt.dataset_name, opt.epoch)))
else:# 初始化权重G_AB.apply(weights_init_normal)G_BA.apply(weights_init_normal)D_A.apply(weights_init_normal)D_B.apply(weights_init_normal)# Optimizers
optimizer_G = torch.optim.Adam(itertools.chain(G_AB.parameters(), G_BA.parameters()), lr=opt.lr, betas=(opt.b1, opt.b2)
)
optimizer_D_A = torch.optim.Adam(D_A.parameters(), lr=opt.lr, betas=(opt.b1, opt.b2))
optimizer_D_B = torch.optim.Adam(D_B.parameters(), lr=opt.lr, betas=(opt.b1, opt.b2))# Learning rate update schedulers
lr_scheduler_G = torch.optim.lr_scheduler.LambdaLR(optimizer_G, lr_lambda=LambdaLR(opt.n_epochs, opt.epoch, opt.decay_epoch).step
)
lr_scheduler_D_A = torch.optim.lr_scheduler.LambdaLR(optimizer_D_A, lr_lambda=LambdaLR(opt.n_epochs, opt.epoch, opt.decay_epoch).step
)
lr_scheduler_D_B = torch.optim.lr_scheduler.LambdaLR(optimizer_D_B, lr_lambda=LambdaLR(opt.n_epochs, opt.epoch, opt.decay_epoch).step
)Tensor = torch.cuda.FloatTensor if cuda else torch.Tensor# Buffers of previously generated samples
fake_A_buffer = ReplayBuffer()
fake_B_buffer = ReplayBuffer()# Image transformations
transforms_ = [transforms.Resize(int(opt.img_height * 1.12), Image.BICUBIC),transforms.RandomCrop((opt.img_height, opt.img_width)),transforms.RandomHorizontalFlip(),transforms.ToTensor(),transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)),
]# Training data loader
dataloader = DataLoader(ImageDataset("./data/%s/" % opt.dataset_name, transforms_=transforms_, unaligned=True),batch_size=opt.batch_size,shuffle=True,num_workers=opt.n_cpu,
)
# Test data loader
val_dataloader = DataLoader(ImageDataset("./data/%s/" % opt.dataset_name, transforms_=transforms_, unaligned=True, mode="test"),batch_size=5,shuffle=True,num_workers=1,
)def sample_images(batches_done):"""Saves a generated sample from the test set"""imgs = next(iter(val_dataloader))G_AB.eval()G_BA.eval()real_A = Variable(imgs["A"].type(Tensor))fake_B = G_AB(real_A)real_B = Variable(imgs["B"].type(Tensor))fake_A = G_BA(real_B)# Arange images along x-axisreal_A = make_grid(real_A, nrow=5, normalize=True)real_B = make_grid(real_B, nrow=5, normalize=True)fake_A = make_grid(fake_A, nrow=5, normalize=True)fake_B = make_grid(fake_B, nrow=5, normalize=True)# Arange images along y-axisimage_grid = torch.cat((real_A, fake_B, real_B, fake_A), 1)save_image(image_grid, "images/%s/%s.png" % (opt.dataset_name, batches_done), normalize=False)# ----------
#  Training
# ----------if __name__ == '__main__':prev_time = time.time()for epoch in range(opt.epoch, opt.n_epochs):for i, batch in enumerate(dataloader):# Set model inputreal_A = Variable(batch["A"].type(Tensor))real_B = Variable(batch["B"].type(Tensor))# Adversarial ground truthsvalid = Variable(Tensor(np.ones((real_A.size(0), *D_A.output_shape))), requires_grad=False)fake  = Variable(Tensor(np.zeros((real_A.size(0), *D_A.output_shape))), requires_grad=False)# ------------------#  Train Generators# ------------------G_AB.train()G_BA.train()optimizer_G.zero_grad()# Identity lossloss_id_A = criterion_identity(G_BA(real_A), real_A)loss_id_B = criterion_identity(G_AB(real_B), real_B)loss_identity = (loss_id_A + loss_id_B) / 2# GAN lossfake_B = G_AB(real_A)loss_GAN_AB = criterion_GAN(D_B(fake_B), valid)fake_A = G_BA(real_B)loss_GAN_BA = criterion_GAN(D_A(fake_A), valid)loss_GAN = (loss_GAN_AB + loss_GAN_BA) / 2# Cycle lossrecov_A = G_BA(fake_B)loss_cycle_A = criterion_cycle(recov_A, real_A)recov_B = G_AB(fake_A)loss_cycle_B = criterion_cycle(recov_B, real_B)loss_cycle = (loss_cycle_A + loss_cycle_B) / 2# Total lossloss_G = loss_GAN + opt.lambda_cyc * loss_cycle + opt.lambda_id * loss_identityloss_G.backward()optimizer_G.step()# -----------------------#  Train Discriminator A# -----------------------optimizer_D_A.zero_grad()# Real lossloss_real = criterion_GAN(D_A(real_A), valid)# Fake loss (on batch of previously generated samples)fake_A_ = fake_A_buffer.push_and_pop(fake_A)loss_fake = criterion_GAN(D_A(fake_A_.detach()), fake)# Total lossloss_D_A = (loss_real + loss_fake) / 2loss_D_A.backward()optimizer_D_A.step()# -----------------------#  Train Discriminator B# -----------------------optimizer_D_B.zero_grad()# Real lossloss_real = criterion_GAN(D_B(real_B), valid)# Fake loss (on batch of previously generated samples)fake_B_ = fake_B_buffer.push_and_pop(fake_B)loss_fake = criterion_GAN(D_B(fake_B_.detach()), fake)# Total lossloss_D_B = (loss_real + loss_fake) / 2loss_D_B.backward()optimizer_D_B.step()loss_D = (loss_D_A + loss_D_B) / 2# --------------#  Log Progress# --------------# Determine approximate time leftbatches_done = epoch * len(dataloader) + ibatches_left = opt.n_epochs * len(dataloader) - batches_donetime_left = datetime.timedelta(seconds=batches_left * (time.time() - prev_time))prev_time = time.time()# Print logsys.stdout.write("\r[Epoch %d/%d] [Batch %d/%d] [D loss: %f] [G loss: %f, adv: %f, cycle: %f, identity: %f] ETA: %s"% (epoch,opt.n_epochs,i,len(dataloader),loss_D.item(),loss_G.item(),loss_GAN.item(),loss_cycle.item(),loss_identity.item(),time_left,))# If at sample interval save imageif batches_done % opt.sample_interval == 0:sample_images(batches_done)# Update learning rateslr_scheduler_G.step()lr_scheduler_D_A.step()lr_scheduler_D_B.step()if opt.checkpoint_interval != -1 and epoch % opt.checkpoint_interval == 0:# Save model checkpointstorch.save(G_AB.state_dict(), "saved_models/%s/G_AB_%d.pth" % (opt.dataset_name, epoch))torch.save(G_BA.state_dict(), "saved_models/%s/G_BA_%d.pth" % (opt.dataset_name, epoch))torch.save(D_A.state_dict(), "saved_models/%s/D_A_%d.pth" % (opt.dataset_name, epoch))torch.save(D_B.state_dict(), "saved_models/%s/D_B_%d.pth" % (opt.dataset_name, epoch))

PS:该工程项目运行需根据自己的电脑配置或者服务器配置设置cyclegan.py中对应参数,如:

n_epochs(200→4)、decay_epoch(100→2)、n_cpu(8→4)

部分训练结果:

3. 总结

        CycleGAN的一个重要应用领域是Domain Adaptation,可以把一张普通的风景照变化成梵高化作,CycleGAN 由左右两个 GAN 网络组成 G(AB) 负责把 A 类物体 (斑马) 转换成 B 类物体 (正常的马). G(BA) 负责把 B 类物体 (正常的马) 还原成 A 类物体 (斑马),如下图所示。

三、理论基础

1. CycleGAN的原理结构

        CycleGAN(循环生成对抗网络)是一种生成对抗网络(GAN),它能够在没有成对训练样本的情况下,将一个域(比如照片中的马)转换成另一个域(比如照片中的斑马)。CycleGAN 主要由两部分组成:两个生成器和一个判别器。生成器的作用是在两个域之间进行转换,而判别器则用于判断输入的图像是真实的还是由生成器生成的。

1.1. 生成器(Generator)
CycleGAN 包含两个生成器,一个用于将图像从域A转换到域B,另一个则用于将图像从域B转换回域A。每个生成器都是一个神经网络,通常采用卷积神经网络(CNN)的结构。生成器的目标是学习如何将输入图像转换成目标域中的图像,同时欺骗判别器,使其认为生成的图像是真实的。

1.2. 判别器(Discriminator)
与生成器相对应,CycleGAN 也有两个判别器,一个用于判断图像是否属于域A,另一个用于判断图像是否属于域B。判别器同样通常采用卷积神经网络结构,其目标是能够准确地区分真实图像和由生成器生成的图像。

1.3. 循环一致性损失(Cycle Consistency Loss)
为了确保在没有成对训练样本的情况下,生成器能够学习到有效的映射,CycleGAN 引入了循环一致性损失。这个损失函数确保当图像从域A转换到域B,然后再转换回域A时,得到的图像与原始图像尽可能相似。同样地,从域B转换到域A,再转换回域B也应该保持循环一致性。

1.4. 对抗损失(Adversarial Loss)
对抗损失是GAN的核心,它确保生成器能够生成足以欺骗判别器的图像。对于CycleGAN,每个生成器都需要最小化对抗损失,使其生成的图像能够在相应的判别器上获得高分。

1.5. identity损失(Identity Loss)
除了上述两种损失函数,CycleGAN 还引入了identity损失,以确保当输入图像已经属于目标域时,生成器能够返回原始图像。这有助于保持生成器在训练过程中的稳定性,并防止过度拟合。

1.6.训练过程:
在训练过程中,CycleGAN 通过不断调整生成器和判别器的参数来最小化上述损失函数。生成器尝试生成越来越能欺骗判别器的图像,而判别器则尝试越来越准确地识别真实图像和生成图像。通过这种对抗过程,生成器最终能够学习到如何在两个域之间进行有效的转换。

1.7.应用:
CycleGAN 在计算机视觉领域有广泛的应用,如风格迁移、季节变换、照片增强等,它为那些没有成对训练数据的图像转换任务提供了一种有效的解决方案。

2.CycleGAN和传统GAN的对比

        CycleGAN与普通GAN相比,有几个特殊之处,这些特性使得CycleGAN适合于图像到图像的转换任务,尤其是在没有成对训练数据的情况下:

2.1.无配对数据要求:
普通GAN通常需要成对的训练数据,即每个输入图像都有一个对应的输出图像。而CycleGAN不需要这样的成对数据,它可以学习一个域(比如照片)到另一个域(比如画作)的转换,即使没有直接的成对映射。
2.2.循环一致性损失:
CycleGAN引入了循环一致性损失(Cycle Consistency Loss),这是其核心的创新之一。这个损失函数确保当图像从源域转换到目标域,然后再转换回源域时,能够尽可能地恢复到原始图像。这样的循环保证了即使在没有成对数据的情况下,转换过程也是合理的。
2.3.两个生成器和两个判别器:
CycleGAN包含两个生成器,每个生成器负责一个方向的转换(从域A到域B和从域B到域A)。同时,也有两个判别器,分别用于判断图像是否属于域A或域B。这种结构使得CycleGAN能够同时学习两个域之间的映射。
2.4.对抗性损失和身份损失:
除了循环一致性损失,CycleGAN还结合了对抗性损失和身份损失。对抗性损失确保生成器能够生成足以欺骗判别器的图像,而身份损失确保当输入图像已经属于目标域时,生成器能够返回原始图像,这有助于保持生成器的稳定性。
2.5.多样化的应用场景:
由于不需要成对数据,CycleGAN可以应用于多种不同的图像到图像的转换任务,如风格迁移、季节变换、艺术作品风格化等,而这些任务在普通GAN中很难实现。
2.6.更强的泛化能力:
由于循环一致性损失的设计,CycleGAN在训练过程中学习到了更加泛化的特征表示,这使得它在面对未见过的数据时,也能表现出较好的转换效果。


http://www.ppmy.cn/devtools/114197.html

相关文章

第五章 网络编程 TCP/UDP/Socket

第五章 网络编程 一、IP地址 唯一标识网络上的每一台计算机 32位&#xff0c;由4个8位二进制数组成1.IP地址的组成 IP地址 网络地址 主机地址 网络地址&#xff1a;标识计算机或网络设备所在的网段 主机地址&#xff1a;标识特定主机或网络设备A类地址通常分配给具有大量主…

汽车软件开发之敏捷开发

一、前言 目前汽车电子产品&#xff0c;特别是汽车几大域控&#xff08;如&#xff1a;智能座舱、智能驾驶、智能网联、车身控制&#xff09;市场竞争激烈&#xff0c;消费者对汽车的需求逐渐多元化和个性化&#xff0c;用户对座舱和智驾产品的要求也越来越高。他们不仅要求产…

在 Windows 上恢复已删除的 PDF 文件的最佳方法

如果您不小心删除了 PDF 文件或由于系统突然崩溃而无法再找到它们&#xff0c;本指南介绍了恢复已删除文件的最佳方法。 帖子中列出的方法简单、有效且可行。我们在列出它们之前对其进行了测试。 什么是 PDF&#xff0c;Adobe 将未保存的 PDF 存储在哪里&#xff1f; 自从 Ad…

家居小程序有什么用?

家居小程序在现代家居生活中扮演着越来越重要的角色&#xff0c;其功能和用途广泛且实用。以下是家居小程序的主要用途&#xff1a; 一、商品展示与购物 商品展示&#xff1a;家居小程序可以展示各种家居产品&#xff0c;包括家具、家电、装饰品等&#xff0c;用户可以通过分类…

【Linux下的cpp】编译调试(gcc、g++、gdb)

【Linux下的cpp】编译调试&#xff08;gcc、g、gdb&#xff09; 文章目录 【Linux下的cpp】编译调试&#xff08;gcc、g、gdb&#xff09;简述gcc、g、gdb编译过程g 编译参数命令行编译演练1、直接编译2、生成库文件并编译链接静态库并生成可执行文件链接动态库生成可执行文件 …

鸿蒙Harmony应用开发,数据驾驶舱页面的实现

先来看看我们要实现的驾驶舱的页面是什么样的 对于这种 响应式布局的页面构建&#xff0c;我们的脑子里面要有一个概念&#xff0c;就是"分而治之"。我们把这个页面进行分割&#xff0c;分割成不同的块然后再来逐个实现. 不难发现&#xff0c;我们可以将这个看到的效…

json格式互相转换

您提供的字符串已经是一个JSON格式的字符串&#xff0c;但是JSON标准要求键名必须用双引号括起来&#xff0c;而不是单引号。因此&#xff0c;您需要将字符串中的单引号替换为双引号。以下是转换后的JSON字符串&#xff1a; {"图片描述": "高速公路上发生了严重…

基于Spring Boot的学生社区故障维修预约系统的设计与实现(开题报告)

毕业论文(设计)开题报告 基于Spring Boot的学生社区故障维修预约系统设计与实现 姓 名 学 院 数学与数据科学学院 专业班级 信息与计算科学202 学 号 202021314223 校内指导教师 职称/职务 副教授 校外指导教师 职称/职务 技术经理 起始时间 2023年9月 教务部制 一、开…

go语言中的数组指针和指针数组的区别详解

1.介绍 大家知道C语言之所以强大&#xff0c;就是因为c语言支持指针&#xff0c;而且权限特别大&#xff0c;c语言可以对计算机中任何内存的指针进行操作&#xff0c;这样自然而然也会带来一些不安全的因素&#xff0c;所以在golang中&#xff0c;「取消了对指针的一些偏移&…

Day.js时间插件的安装引用与常用方法大全

&#x1f680; 个人简介&#xff1a;某大型国企资深软件研发工程师&#xff0c;信息系统项目管理师、CSDN优质创作者、阿里云专家博主&#xff0c;华为云云享专家&#xff0c;分享前端后端相关技术与工作常见问题~ &#x1f49f; 作 者&#xff1a;码喽的自我修养&#x1f9…

给一个web网站,如何开展测试?

前言 Web测试是指针对Web应用程序(网站或基于Web的系统)进行的测试活动&#xff0c;以确保其质量、性能、安全性、可用性和兼容性等方面符合预期标准。Web测试涵盖了从前端用户界面(UI)到后端逻辑和数据库的各个方面&#xff0c;确保Web应用程序在不同环境和条件下都能正常运行…

【算法】队列与BFS

【ps】本篇有 4 道 leetcode OJ。 目录 一、算法简介 二、相关例题 1&#xff09;N 叉树的层序遍历 .1- 题目解析 .2- 代码编写 2&#xff09;二叉树的锯齿形层序遍历 .1- 题目解析 .2- 代码编写 3&#xff09;二叉树最大宽度 .1- 题目解析 .2- 代码编写 4&#xf…

nanoGPT用红楼梦数据从头训练babyGPT-12.32M实现任意问答

1. 引入 大神karpathy从openai离职后&#xff0c;创办了AI教育公司Eureka Labs&#xff08;参考1&#xff09;&#xff0c;同时也创办了知名的nanoGPT项目。 目前&#xff0c;使用nanoGPT&#xff08;参考2&#xff09;&#xff0c;你可以在几分钟内训练出一个babyGPT&#xf…

python如何实现堆栈

栈的特点是后进先出&#xff0c;最先进入栈的数据放到栈底&#xff0c;最后一个出来&#xff0c;而最后进的数据反而有机会先出来。 python实现栈的思路是如果要在栈里添加元素&#xff0c;直接用append添加元素就可以了&#xff0c;直接在列表末位添加。从栈中取出元素&#…

python 虚拟环境多种创建方式

【一】说明介绍 &#xff08;1&#xff09;什么是虚拟环境 在Python中&#xff0c;虚拟环境&#xff08;Virtual Environment&#xff09;是一个独立的、隔离的Python运行环境&#xff0c;它拥有自己的Python解释器、第三方库和应用程序。通过创建虚拟环境&#xff0c;可以确…

【数据结构】排序算法---冒泡排序

文章目录 1. 定义2. 算法步骤3. 动图演示4. 性质5. 算法分析6. 代码实现C语言PythonJavaCGo 结语 1. 定义 冒泡排序&#xff08;英语&#xff1a;Bubble sort&#xff09;是一种简单的排序算法。它重复地走访过要排序的数列&#xff0c;一次比较两个元素&#xff0c;如果它们的…

letterSpacing导致TextView文本被截断

一.背景介绍 &#xff08;Android10 11目前有这个问题 Android15似乎有新的属性 但是没有可用的环境 没有验证&#xff09; 简介 android:maxLines"1" android:textAlignment"viewStart" android:letterSpacing"0.04" 多个属性同时作用情况下 …

python学习第十节:爬虫基于requests库的方法

python学习第十节&#xff1a;爬虫基于requests库的方法 requests模块的作用&#xff1a; 发送http请求&#xff0c;获取响应数据&#xff0c;requests 库是一个原生的 HTTP 库&#xff0c;比 urllib 库更为容易使用。requests 库发送原生的 HTTP 1.1 请求&#xff0c;无需手动…

门磁模块详解(防盗感应开关 STM32)

目录 一、介绍 二、程序设计 main.c文件 gate_guard.h文件 gate_guard.c文件 三、实验效果 四、资料获取 项目分享 一、介绍 MC-38常闭式门磁开关是作为IO开关输入数字信号的&#xff0c;原理是合在一起信号是导通的 , 配合有线主机使用 不能单独使用。适用于非铁质&a…

蓝桥杯1.确定字符串是否包含唯一字符

插播一句&#xff0c;博主转学python了&#xff0c;来写写算法题&#xff0c;若掌握好会考虑捐300块。 题目&#xff1a; 题目描述 实现一个算法来识别一个字符串的字符是否是唯一的&#xff08;忽略字母大小写&#xff09;。 若唯一&#xff0c;则输出YES&#xff0c;否则…