Pytorch从零开始实战22

Pytorch从零开始实战——CycleGAN实战

本系列来源于365天深度学习训练营

原作者K同学

内容介绍

CycleGAN是一种无监督图像到图像转换模型,它的一个重要应用领域是域迁移,比如可以把一张普通的风景照变化成梵高化作,或者将游戏画面变化成真实世界画面,将一匹正常肤色的马转为斑马等等。

CycleGAN 主要解决的问题是将一个域中的图像转换到另一个域中的图像,而无需成对的训练数据。这种转换是双向的,即可以从一个域转换到另一个域,也可以反过来转换。

生成器: CycleGAN 包含两个生成器,分别用于将两个不同域的图像进行转换。例如,在从马到斑马的转换中,一个生成器负责将马的图像转换为斑马的图像,另一个生成器负责将斑马的图像转换为马的图像。生成器学习将输入图像从一个域映射到另一个域的转换函数。

判别器: CycleGAN 包含两个判别器,用于区分生成的图像和真实的图像。一个判别器用于区分生成的源图像和真实的源图像,另一个判别器用于区分生成的生成图像和真实的生成图像。判别器帮助生成器生成更逼真的图像。

损失函数:CycleGAN的Loss由三部分组成,分别为LossGAN(保证生成器和判别器相互进化,进而保证生成器能产生更真实的图片)、LossCycle(保证生成器的输出图片与输入图片只是风格不同,而内容相同)和LossIdentity(是映射损失, 即用真实的 A 当做输入, 查看生成器是否会原封不动的输出)。

数据集类

自定义的 PyTorch 数据集类 ,用于加载图像数据集并进行预处理。

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))

模型实现

遍历模型中的每一层,初始化神经网络模型中的权重。

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)

定义了一个残差块。每个残差块包含两个卷积层,使用反射填充)进行填充,然后进行卷积、实例归一化和 ReLU 激活操作。最后通过残差连接将输入和残差块的输出相加得到最终的输出。

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)

定义了基于 ResNet 结构的生成器。它通过堆叠多个残差块、卷积层和上采样层来生成图像。首先是一个初始的卷积块,然后进行下采样、残差块、上采样,最后输出目标图像。

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)

定义了判别器,这个判别器由多个卷积层组成,逐渐减小特征图的大小,最后输出一个单通道的结果,表示输入图像是真实图像的概率。

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)

开始训练

Util工具类,ReplayBuffer 用于创建一个缓冲区,用于存储历史数据,并在训练过程中可能会用到。LambdaLR 则用于在训练过程中根据指定的规则调整学习率。

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)

设置训练参数,包括 epoch 数、数据集名称、批量大小、学习率。接着定义模型和优化器,包括生成器、判别器、损失函数和优化器。加载数据集并进行数据预处理,设置训练和测试数据加载器。

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 torchparser = argparse.ArgumentParser()
parser.add_argument("--epoch", type=int, default=0, help="epoch to start training from")
parser.add_argument("--n_epochs", type=int, default=200, 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=100, help="epoch from which to start lr decay")
parser.add_argument("--n_cpu", type=int, default=8, 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_models2/%s/G_AB_%d.pth" % (opt.dataset_name, epoch))torch.save(G_BA.state_dict(), "saved_models2/%s/G_BA_%d.pth" % (opt.dataset_name, epoch))torch.save(D_A.state_dict(), "saved_models2/%s/D_A_%d.pth" % (opt.dataset_name, epoch))torch.save(D_B.state_dict(), "saved_models2/%s/D_B_%d.pth" % (opt.dataset_name, epoch))

本次实验设备较差,算力不够。请读者在GPU机器上自行运行。
在这里插入图片描述

总结

CycleGAN 可以用于学习两个不同图像域之间的映射关系,使得在两个域之间进行图像转换成为可能。通过训练,模型可以学习到如何将一个图像从一个域转换到另一个域,而无需配对的训练数据,降低了数据收集和标注的成本。其提出的不同角度的损失函数,也是值得我们去学习。

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

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

相关文章

vue3封装Element导航菜单

1. 导航外层布局 AsideView.vue <template><el-menu:default-active"defaultActive"class"my-menu":collapse"isCollapse":collapse-transition"false"open"handleOpen"close"handleClose"><menu…

Netty核心原理剖析与RPC实践6-10

Netty核心原理剖析与RPC实践6-10 06-粘包拆包问题&#xff1a;如何获取一个完整的网络包 本节课开始我们将学习 Netty 通信过程中的编解码技术。编解码技术这是实现网络通信的基础&#xff0c;让我们可以定义任何满足业务需求的应用层协议。在网络编程中&#xff0c;我们经常…

QT资源添加调用

添加资源文件&#xff0c;新建资源文件夹&#xff0c;命名resource&#xff0c;然后点下一步&#xff0c;点完成 资源&#xff0c;右键add Prefix 添加现有文件 展示的label图片切换 QLabel *led_show; #include "mainwindow.h" #include<QLabel> #include&l…

mysql 常见运算符

学习了mysql数据类型&#xff0c;接下来学习mysql常见运算符。 2&#xff0c;常见运算符介绍 运算符连接表达式中各个操作数&#xff0c;其作用是用来指明对操作数所进行的运算。运用运算符 可以更加灵活地使用表中的数据&#xff0c;常见的运算符类型有&#xff1a;算…

增强现实(AR)的开发工具

增强现实&#xff08;AR&#xff09;的开发工具涵盖了一系列的软件和平台&#xff0c;它们可以帮助开发者创造出能够将虚拟内容融入现实世界的应用程序。以下是一些在AR领域内广泛使用的开发工具。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎…

【最新版RabbitMQ3.13】Linux安装基于源码构建的RabbitMQ教程

前言 linux环境 安装方式有三种&#xff0c;我们这里使用源码安装 Linux下rpm、yum和源码三种安装方式简介 个人语雀首发教程&#xff1a;https://www.yuque.com/wzzz/java/kl2zn22b42svsc6b csdn地址: https://blog.csdn.net/u013625306/article/details/137151862 安装版本…

三菱Q系列PLC以太网TCP通讯FB块源码

三菱Q系列PLC的tcp通讯&#xff0c;客户端和服务器两个变量好用的FB块&#xff0c;调用块就可以实现通讯连接&#xff0c;不需要自己写程序&#xff0c;简单配置引脚就可以。该块还集成了断网&#xff0c;连接错误&#xff0c;发送接收数据错误报警等功能。具体功能见下面介绍.…

C语言最大公约数(辗转相除法)

输入两个整数&#xff0c;求他们的最大公约数&#xff1a; 如果我们不用辗转相除法的话&#xff0c;两个整数的最大公约数&#xff0c;我们就可以定义一个整数为两个整数中最小的那个数&#xff0c;然后两个整数一起除我们新定义的整数&#xff0c;如果都除尽了&#xff0c;这…

UE5数字孪生系列笔记(三)

C创建Pawn类玩家 创建一个GameMode蓝图用来加载我们自定义的游戏Mode新建一个Pawn的C&#xff0c;MyCharacter类作为玩家&#xff0c;新建一个相机组件与相机臂组件&#xff0c;box组件作为根组件 // Fill out your copyright notice in the Description page of Project Set…

备考ICA----Istio实验13---使用 Istio Ingress 暴露应用

备考ICA----Istio实验13—使用Istio Ingress TLS暴露应用 1. 环境部署 清理之前实验遗留,并重新部署httpbin服务进行测试 # 清理之前的环境 kubectl delete vs httpbin kubectl delete gw mygateway # 部署httpbin kubectl apply -f istio/samples/httpbin/httpbin.yaml 确认…

从vivo X Fold3看vivo“质”变

撰文 | 何玺 排版 | 叶媛 vivo的气质变了&#xff01;虽然依旧内敛、低调&#xff0c;但更自信、从容&#xff0c;气场也更强大。这是玺哥在本次vivo X Fold3系列新品发布会上的一个直观感受。 是什么改变了vivo的气质&#xff1f;产品&#xff1f;技术&#xff1f;又或是其他…

衰老抑制剂原知因起源金NMN热销,“海弗里克极限”将被打破?

美国著名生物学家列奥纳多 海弗里克 , 在 1961 年研究人类胎儿的细胞群体分裂次数时提出了著名的 " 海弗里克极限 " 理论。该理论认为 , 正常细胞分裂的周期是 2-3 年 , 分裂次数大概是 50 次 , 得出人类的极限寿命高达 150 岁。半个世纪后 , 世界上最长寿的人 , 打…

医院信管系统的设计与实现|Springboot+ Mysql+Java+ B/S结构(可运行源码+数据库+设计文档)

本项目包含可运行源码数据库LW&#xff0c;文末可获取本项目的所有资料。 推荐阅读100套最新项目持续更新中..... 2024年计算机毕业论文&#xff08;设计&#xff09;学生选题参考合集推荐收藏&#xff08;包含Springboot、jsp、ssmvue等技术项目合集&#xff09; 1. 系统功能…

阿里云实时计算Flink的产品化思考与实践【下】

摘要&#xff1a;本文整理自阿里云高级产品专家黄鹏程和阿里云技术专家陈婧敏在 FFA 2023 平台建设专场中的分享。内容主要为以下五部分&#xff1a; 阿里云实时计算 Flink 产品化思考 产品化实践 SQL 产品化思考及实践 展望 接上篇&#xff1a;阿里云实时计算Flink的产品…

SD 修复 Midjourney 有瑕疵照片

Midjourney V6 生成的照片在质感上有了一个巨大的提升。下面4张图就是 Midjourney V6 生成的。 如果仔细观察人物和老虎的面部&#xff0c;细节真的很丰富。 但仔细观察上面四张图的手部细节&#xff0c;就会发现至少有两只手是有问题的。这也是目前所有 AI 绘图工具面临的问题…

DARTS-PT: RETHINKING ARCHITECTURE SELECTION IN DIFFERENTIABLE NAS

Rethinking Architecture Selection in Differentiable NAS 论文链接&#xff1a;https://arxiv.org/abs/2108.04392v1 项目链接&#xff1a;https://github.com/ruocwang/darts-pt ABSTRACT 可微架构搜索(Differentiable Neural Architecture Search, NAS)是目前最流行的网…

鸿蒙OS开发实例:【应用级别文件浏览器】

介绍 HarmonyOS的沙盒机制完全屏蔽了应用对手机公共存储空间的访问&#xff0c;安全性提高已不言而喻。 本篇文章的主要目的是为了能通过一个简单工具&#xff0c;可视化的让一个新手能相对轻松的学习文件&数据存储。HarmonyOS 应用开发工具DevEco Studio也没有提供读取存…

新能源汽车充电桩常见类型及充电桩站场的智能监管方案设计

随着新能源汽车市场的迅猛发展&#xff0c;充电桩作为支持其运行的基础设施&#xff0c;也呈现出多样化的类型。这些充电桩不仅在外形和功能上存在差异&#xff0c;更在充电速度、充电方式以及使用场景等方面展现出独特的优势。 一、充电桩类型及区别 1、慢充桩&#xff08;交…

GenMedicalEval:医疗大语言模型综合评测框架

推荐一个开源的医疗大语言模型综合评价框架。 项目链接 https://github.com/MediaBrain-SJTU/GenMedicalEval 项目简介 我们提出了一个医疗大语言模型的综合评测框架&#xff0c;具有以下三大特点&#xff1a; 1.大规模综合性能评测&#xff1a;GenMedicalEval构建了一个覆盖…

stm32定时器中断函数回调函数

方式一&#xff1a;stm32定时器中断可以直接在硬件中断函数TIM3_IRQHandler执行。 在HAL库中可以注册回调函数&#xff0c;在定时器中断发生时调用注册的函数&#xff0c;这样可以统一接口&#xff0c;大大提高函数可读性&#xff0c;和硬件解耦提高程序可移植性。 使用过程如…