CycleGAN图像转换中的那些坑:如何解决训练不稳定和模式崩溃问题

news2026/3/14 17:06:50
CycleGAN实战避坑指南从训练崩溃到稳定出图的进阶策略如果你已经尝试过用CycleGAN做图像转换大概率经历过这样的场景模型训练了几个epoch生成器输出的图片要么模糊一片要么颜色诡异甚至干脆“摆烂”输出几乎相同的图像无论输入什么内容。损失曲线像过山车一样剧烈波动判别器的准确率要么直奔100%要么跌到50%以下整个训练过程充满了不确定性。这不仅仅是你的问题而是CycleGAN这类无监督图像转换模型固有的挑战——训练不稳定和模式崩溃。我最初接触CycleGAN时也被这些问题折磨得不轻。当时想做一个简单的季节转换项目把夏天的风景照转换成秋天的色调。按照论文里的默认参数跑起来前几百步看起来还挺有希望生成器开始学着把绿叶染上一点黄边。但到了2000步左右画面突然就糊了所有的树都变成了棕黄色的色块细节全无。更糟糕的是无论输入什么夏天的照片输出的都是几乎相同的“秋天模板”。这就是典型的模式崩溃——生成器找到了一个能骗过判别器的“万能答案”然后就开始偷懒了。经过多次实验和调整我发现CycleGAN的训练稳定性其实取决于几个关键因素的微妙平衡。这篇文章不会重复那些基础教程而是聚焦于实战中真正会遇到的问题分享一些让CycleGAN训练稳定下来的具体策略。无论你是想把马变成斑马还是想把照片变成油画风格这些经验都能帮你少走弯路。1. 诊断训练不稳定的根源不只是超参数的问题很多人一遇到训练不稳定第一反应就是调整学习率。这当然没错但学习率只是冰山一角。CycleGAN的训练动态比标准GAN复杂得多因为它有两组生成器和判别器在同时博弈还有循环一致性损失和身份损失在中间“拉架”。要真正解决问题得先搞清楚问题出在哪一层。1.1 判别器过强GAN训练中最常见的“杀手”判别器太强是导致训练崩溃的最直接原因。当判别器能轻易区分真假图像时生成器得到的梯度信号就变得非常微弱甚至带有大量噪声。在CycleGAN中这个问题会被放大——因为有两个判别器只要其中一个变得过强整个训练平衡就会被打破。怎么判断判别器是否过强看这几个指标判别器损失快速趋近于0而生成器损失持续上升或剧烈波动判别器对真实图像和生成图像的预测概率如果真实图像的概率持续高于0.9生成图像的概率持续低于0.1就是明显过拟合生成图像的质量在几个epoch内没有明显改善甚至越来越差我在一个建筑风格转换的项目中遇到过这种情况。判别器DX负责判断是否为真实建筑A在训练到第5个epoch时对真实图像的判断准确率达到了98%对生成图像的判断准确率只有3%。结果就是生成器G从建筑B到建筑A完全学不到有用的特征输出的图像全是模糊的色块。判别器的“强度”不是绝对的而是相对于生成器而言的。一个在初期表现“合适”的判别器随着生成器变强可能会突然变得“过强”。解决判别器过强有几个实用策略策略一给判别器“减速”最简单的办法就是降低判别器的学习率或者减少判别器的更新频率。在CycleGAN的原始实现中作者使用了“每5步更新一次生成器每1步更新一次判别器”的策略。但根据我的经验这个比例需要根据具体任务调整。# 实际训练循环中的调整示例 for epoch in range(total_epochs): for batch_idx, (real_A, real_B) in enumerate(dataloader): # 每2步更新一次生成器每1步更新一次判别器 # 这样可以给生成器更多时间“追赶” if batch_idx % 2 0: # 更新生成器G和F optimizer_G.zero_grad() loss_G compute_generator_loss(real_A, real_B) loss_G.backward() optimizer_G.step() # 总是更新判别器但可以控制学习率 optimizer_D.zero_grad() loss_D compute_discriminator_loss(real_A, real_B) loss_D.backward() optimizer_D.step()策略二使用梯度惩罚Gradient PenaltyWGAN-GP中的梯度惩罚项能有效防止判别器变得“太自信”。虽然CycleGAN原论文没用这个技巧但在实践中加入梯度惩罚能让训练稳定很多。def compute_gradient_penalty(D, real_samples, fake_samples): 计算WGAN-GP中的梯度惩罚项 # 在真实样本和生成样本之间随机插值 alpha torch.rand(real_samples.size(0), 1, 1, 1).to(real_samples.device) interpolates (alpha * real_samples (1 - alpha) * fake_samples).requires_grad_(True) d_interpolates D(interpolates) fake torch.ones(real_samples.size(0), 1).to(real_samples.device) # 计算梯度 gradients torch.autograd.grad( outputsd_interpolates, inputsinterpolates, grad_outputsfake, create_graphTrue, retain_graphTrue, only_inputsTrue )[0] gradients gradients.view(gradients.size(0), -1) gradient_penalty ((gradients.norm(2, dim1) - 1) ** 2).mean() return gradient_penalty # 在判别器损失中加入梯度惩罚 gradient_penalty compute_gradient_penalty(discriminator, real_imgs, fake_imgs) loss_D loss_D_original lambda_gp * gradient_penalty策略三历史缓冲池History Buffer这是CycleGAN论文中提到的技巧但很多人低估了它的重要性。缓冲池通过混合当前生成的图像和历史生成的图像来训练判别器防止判别器对当前生成器的“最新把戏”过拟合。缓冲池大小优点缺点适用场景50默认平衡新旧样本稳定训练需要额外内存大多数场景0禁用节省内存训练更快容易导致模式崩溃数据集简单风格差异小100更加稳定防止过拟合内存占用大训练慢复杂转换任务我在实践中发现对于高分辨率图像256x256以上缓冲池大小需要相应增加。一个经验法则是缓冲池大小 ≈ batch_size × 10。1.2 损失函数权重失衡CycleGAN的“隐形杀手”CycleGAN的总损失函数由多个部分组成对抗损失Adversarial Loss让生成图像看起来真实循环一致性损失Cycle Consistency Loss保证转换可逆身份损失Identity Loss保持输入图像的内容结构这些损失的相对权重λ_cycle和λ_identity对训练稳定性影响巨大。原论文推荐λ_cycle10λ_identity0.5但这只是个起点。循环一致性损失权重λ_cycle的影响λ_cycle太小比如1-5生成器可能会忽视内容保持导致输出图像虽然风格对了但内容面目全非。我试过把λ_cycle设为5做风景照的季节转换结果树的位置都变了山也变形了。λ_cycle太大比如20-50生成器会过于保守不敢做明显的风格转换。在油画风格转换任务中λ_cycle20时生成器几乎不敢改变颜色和笔触输出的还是接近照片的效果。调整λ_cycle时要同时监控两个指标循环一致性损失值应该稳步下降和生成图像的视觉质量。如果一致性损失降得太快而图像质量没改善说明权重可能太大了。身份损失权重λ_identity的微妙作用身份损失经常被误解为“可选”的但实际上它对训练稳定性有重要作用稳定训练初期在训练早期身份损失能防止生成器做出过于激进的改变保持颜色一致性对于需要保持整体色调的任务如白天转夜晚身份损失能帮助保留亮度信息防止模式崩溃当生成器开始“偷懒”时身份损失能提供额外的监督信号但λ_identity也不能太大否则生成器会倾向于输出与输入完全相同的图像。我建议的调整策略是# 动态调整身份损失权重 def adjust_identity_weight(current_epoch, total_epochs, base_weight0.5): 随着训练进行逐渐减小身份损失的影响 # 前1/3的训练保持较高的身份损失稳定训练 if current_epoch total_epochs // 3: return base_weight # 中间1/3逐渐减小 elif current_epoch 2 * total_epochs // 3: decay_factor 1.0 - (current_epoch - total_epochs // 3) / (total_epochs // 3) return base_weight * decay_factor # 最后1/3保持较小的权重让生成器更自由 else: return base_weight * 0.1 # 在训练循环中使用 lambda_identity adjust_identity_weight(epoch, total_epochs)2. 识别与破解模式崩溃当生成器开始“偷懒”模式崩溃是GAN训练中的经典问题但在CycleGAN中表现形式更加多样。识别模式崩溃的早期迹象比等到完全崩溃后再补救要有效得多。2.1 模式崩溃的几种表现形式类型一输出多样性丧失这是最常见的模式崩溃。生成器找到一两个能骗过判别器的“模板”然后对所有输入都输出类似的图像。在人物肖像风格化任务中我遇到过生成器把所有脸都变成同一种卡通风格失去了人物的个体特征。检测方法随机选择一批输入图像比较生成结果的差异。如果所有输出在颜色、纹理、结构上都高度相似就是模式崩溃的迹象。类型二颜色偏移Color Shift生成器学会改变整体色调来欺骗判别器但忽略了更细微的风格特征。比如在照片转莫奈风格的任务中生成器只是把图像整体调成偏蓝绿色调加上一些模糊效果但没有学会印象派的笔触和光影处理。类型三内容丢失生成器过度关注风格转换导致原始图像的内容信息严重丢失。在建筑风格转换中生成器可能会把窗户、门等结构特征都抹掉只保留大概的轮廓和颜色。2.2 破解模式崩溃的实战技巧技巧一多样化判别器的“视角”标准的PatchGAN判别器只看图像局部区域这有时候会让生成器钻空子——只要局部看起来像目标风格就行。可以尝试混合不同感受野的判别器class MultiScaleDiscriminator(nn.Module): 多尺度判别器从不同尺度判断图像真实性 def __init__(self, input_channels3, num_scales3): super().__init__() self.discriminators nn.ModuleList() # 创建不同尺度的判别器 for i in range(num_scales): # 第一个判别器看原图 # 后续判别器看下采样后的图像 scale_factor 2 ** i if i 0: self.discriminators.append( PatchDiscriminator(input_channels) ) else: self.discriminators.append( nn.Sequential( nn.AvgPool2d(scale_factor), PatchDiscriminator(input_channels) ) ) def forward(self, x): outputs [] for disc in self.discriminators: outputs.append(disc(x)) return outputs # 返回多个尺度的判别结果 # 在损失计算中综合多个尺度的结果 def compute_discriminator_loss_multi_scale(discriminator, real_imgs, fake_imgs): losses [] for scale_idx, (real_out, fake_out) in enumerate(zip( discriminator(real_imgs), discriminator(fake_imgs) )): # 不同尺度可以有不同的权重 weight 1.0 / (2 ** scale_idx) # 越大的尺度权重越小 loss_real torch.mean((real_out - 1) ** 2) loss_fake torch.mean(fake_out ** 2) losses.append(weight * (loss_real loss_fake)) return sum(losses)技巧二引入内容感知损失当身份损失不足以保持内容一致性时可以加入感知损失Perceptual Loss或风格损失Style Loss。这些损失基于预训练网络如VGG的特征能更好地保持高级语义信息。class PerceptualLoss(nn.Module): 基于VGG的感知损失保持内容结构 def __init__(self, layer_indices[3, 8, 15, 22]): super().__init__() # 加载预训练的VGG只取前面几层 vgg torchvision.models.vgg16(pretrainedTrue).features self.layers nn.ModuleList() # 提取指定层的特征 for i in range(max(layer_indices) 1): self.layers.append(vgg[i]) if i in layer_indices: # 冻结这些层的参数 for param in self.layers[-1].parameters(): param.requires_grad False self.layer_indices layer_indices self.criterion nn.L1Loss() def forward(self, input_img, target_img): # 归一化到ImageNet的统计量 mean torch.tensor([0.485, 0.456, 0.406]).view(1, 3, 1, 1).to(input_img.device) std torch.tensor([0.229, 0.224, 0.225]).view(1, 3, 1, 1).to(input_img.device) input_normalized (input_img - mean) / std target_normalized (target_img - mean) / std loss 0 x_input, x_target input_normalized, target_normalized for i, layer in enumerate(self.layers): x_input layer(x_input) x_target layer(x_target) if i in self.layer_indices: # 计算该层的特征差异 loss self.criterion(x_input, x_target) return loss # 在CycleGAN损失中加入感知损失 perceptual_loss PerceptualLoss() loss_perceptual perceptual_loss(fake_B, real_B) # 让生成图像在感知上接近真实目标 lambda_perceptual 0.1 # 需要小心调整通常比循环一致性损失小技巧三课程学习Curriculum Learning策略不要一开始就追求完美的转换。让模型先从简单的任务学起逐步增加难度第一阶段前25%训练主要学习颜色和整体色调的转换可以使用较小的λ_cycle比如5较大的λ_identity比如1.0第二阶段25%-75%逐渐增加纹理和细节的学习λ_cycle增加到10λ_identity减小到0.5第三阶段最后25%专注于细节精炼和风格融合可以尝试加入感知损失这种渐进式训练能显著降低模式崩溃的概率。我在一个动漫风格转换项目中用了这个方法第一阶段只学颜色映射第二阶段学线条风格第三阶段学阴影和高光处理最终效果比直接端到端训练稳定得多。3. 网络架构与超参数调优从理论到实践CycleGAN的默认架构ResNet-based生成器 PatchGAN判别器是个不错的起点但未必适合所有任务。根据我的经验调整网络架构往往比单纯调参更有效。3.1 生成器架构的选择与调整ResNet块的数量更多不等于更好原论文使用9个ResNet块但这个数字需要根据任务复杂度调整任务类型推荐ResNet块数理由颜色/亮度转换如白天转夜晚6-9相对简单的映射不需要太深的网络纹理/风格转换如照片转油画9-12需要学习更复杂的纹理特征结构/形状转换如马转斑马12-18需要改变局部结构需要更强的表达能力高分辨率转换512x5126-9 注意力机制太深容易导致训练不稳定需要注意力机制辅助我在512x512的人像风格化任务中发现使用12个ResNet块反而比9个块效果差——训练更不稳定更容易模式崩溃。后来改回9个块但加入了自注意力机制效果就好多了。实例归一化 vs 批归一化CycleGAN默认使用实例归一化Instance Normalization这对于风格迁移任务确实更好因为它能去除对比度信息专注于风格。但对于需要保持内容结构的任务如物体转换批归一化Batch Normalization可能更合适。class AdaptiveNormLayer(nn.Module): 自适应归一化层根据任务选择 def __init__(self, num_features, norm_typeinstance): super().__init__() self.norm_type norm_type if norm_type instance: self.norm nn.InstanceNorm2d(num_features, affineTrue) elif norm_type batch: self.norm nn.BatchNorm2d(num_features) elif norm_type layer: self.norm nn.LayerNorm(num_features) else: raise ValueError(f不支持的归一化类型: {norm_type}) def forward(self, x): return self.norm(x) # 在生成器中使用 class ResNetBlockWithAdaptiveNorm(nn.Module): def __init__(self, dim, norm_typeinstance): super().__init__() self.conv1 nn.Conv2d(dim, dim, 3, 1, 1) self.norm1 AdaptiveNormLayer(dim, norm_type) self.relu nn.ReLU(inplaceTrue) self.conv2 nn.Conv2d(dim, dim, 3, 1, 1) self.norm2 AdaptiveNormLayer(dim, norm_type) def forward(self, x): residual x out self.conv1(x) out self.norm1(out) out self.relu(out) out self.conv2(out) out self.norm2(out) return residual out3.2 判别器设计的艺术Patch大小的影响PatchGAN的“Patch大小”决定了判别器关注的范围。原论文使用70x70的Patch但这个大小需要根据图像分辨率和任务调整小Patch16x16-34x34关注局部纹理适合风格细节丰富的任务中Patch70x70平衡局部和全局通用性较好大Patch142x142-286x286关注整体结构适合需要保持全局一致性的任务我做过一个实验在256x256的建筑风格转换中比较不同Patch大小的效果Patch大小训练稳定性风格一致性内容保持推荐场景34x34高局部好全局差一般纹理转换70x70中等平衡好通用任务142x142较低全局好很好结构转换286x286低易模式崩溃全局一致优秀简单任务多尺度判别器的实际配置对于复杂任务我推荐使用3个尺度的判别器原图尺度256x256关注整体结构和颜色1/2下采样128x128关注中等尺度特征1/4下采样64x64关注局部纹理和细节每个尺度的判别器可以有不同的架构复杂度。通常越小的尺度可以用越简单的网络因为需要判断的特征更局部。3.3 学习率策略不仅仅是衰减CycleGAN对学习率非常敏感。原论文使用固定的学习率0.0002但在实践中动态调整学习率能显著改善训练稳定性。余弦退火 热重启这是我发现最有效的学习率调度策略之一。它允许学习率周期性上升和下降帮助模型跳出局部最优。from torch.optim.lr_scheduler import CosineAnnealingWarmRestarts # 初始化优化器 optimizer_G torch.optim.Adam(generator.parameters(), lr0.0002, betas(0.5, 0.999)) optimizer_D torch.optim.Adam(discriminator.parameters(), lr0.0002, betas(0.5, 0.999)) # 余弦退火热重启调度器 # T_0: 第一次重启的周期长度epoch数 # T_mult: 每次重启后周期长度的乘数 scheduler_G CosineAnnealingWarmRestarts(optimizer_G, T_050, T_mult2) scheduler_D CosineAnnealingWarmRestarts(optimizer_D, T_050, T_mult2) # 在每个epoch后调用 for epoch in range(total_epochs): # 训练代码... scheduler_G.step() scheduler_D.step() current_lr scheduler_G.get_last_lr()[0] print(fEpoch {epoch}: 学习率 {current_lr:.6f})这种策略的好处是在每次重启时学习率会突然增大帮助模型跳出当前的局部最小值然后随着余弦函数衰减让模型在新的区域精细搜索。判别器和生成器的不同学习率生成器和判别器通常需要不同的学习率。判别器学得更快所以应该用稍小的学习率生成器学得慢可以保持较大的学习率。# 不对称的学习率设置 optimizer_G torch.optim.Adam(generator.parameters(), lr0.0002, # 生成器学习率稍大 betas(0.5, 0.999)) optimizer_D torch.optim.Adam(discriminator.parameters(), lr0.0001, # 判别器学习率稍小 betas(0.5, 0.999))4. 数据预处理与增强被忽视的关键因素很多人把注意力都放在模型架构和损失函数上却忽略了数据预处理的重要性。对于CycleGAN这种无监督方法数据质量直接影响训练稳定性。4.1 输入归一化的正确姿势CycleGAN对输入图像的统计量很敏感。常见的错误是使用ImageNet的均值和标准差mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]来归一化所有数据。但对于风格迁移任务这可能不合适。更好的做法是根据自己的数据集计算统计量def compute_dataset_stats(dataset_path): 计算数据集的均值和标准差 means [] stds [] for img_path in glob.glob(os.path.join(dataset_path, *.jpg)): img Image.open(img_path).convert(RGB) img_np np.array(img) / 255.0 # 计算每个通道的均值和标准差 mean img_np.mean(axis(0, 1)) std img_np.std(axis(0, 1)) means.append(mean) stds.append(std) # 计算整个数据集的统计量 overall_mean np.mean(means, axis0) overall_std np.mean(stds, axis0) return overall_mean, overall_std # 对两个域分别计算 mean_A, std_A compute_dataset_stats(path/to/domain_A) mean_B, std_B compute_dataset_stats(path/to/domain_B) # 使用自定义的归一化 transform_A transforms.Compose([ transforms.Resize((256, 256)), transforms.ToTensor(), transforms.Normalize(meanmean_A, stdstd_A) ]) transform_B transforms.Compose([ transforms.Resize((256, 256)), transforms.ToTensor(), transforms.Normalize(meanmean_B, stdstd_B) ])4.2 数据增强的谨慎使用数据增强能提高模型的泛化能力但对于CycleGAN有些增强操作可能会破坏循环一致性。安全的增强操作随机水平翻转对大多数任务都安全小幅度的旋转±5度颜色抖动轻微调整亮度、对比度、饱和度需要小心的增强操作随机裁剪可能破坏图像内容的一致性大幅度旋转可能让循环一致性损失难以收敛透视变换同样可能破坏内容结构我的实践经验对于风景照转换我通常只使用随机水平翻转。对于人脸风格化可以加上轻微的颜色抖动。但对于建筑转换这种需要保持直线和透视的任务最好不用任何空间变换的增强。4.3 批大小Batch Size的选择批大小影响模型看到的数据分布。太小的批大小如1-4会导致梯度估计噪声大训练不稳定太大的批大小如64可能让模型难以学习细节特征。图像分辨率推荐批大小GPU内存占用训练稳定性128x1288-16低高256x2564-8中等中等512x5121-2高较低1024x10241很高低需要梯度累积对于高分辨率图像可以使用梯度累积来模拟更大的批大小accumulation_steps 4 # 累积4步的梯度 optimizer_G.zero_grad() optimizer_D.zero_grad() for i, (real_A, real_B) in enumerate(dataloader): # 前向传播和损失计算 loss_G compute_generator_loss(real_A, real_B) loss_D compute_discriminator_loss(real_A, real_B) # 反向传播累积梯度 loss_G.backward() loss_D.backward() # 每accumulation_steps步更新一次参数 if (i 1) % accumulation_steps 0: optimizer_G.step() optimizer_D.step() optimizer_G.zero_grad() optimizer_D.zero_grad()4.4 监控与调试建立有效的评估体系训练CycleGAN时不能只看损失曲线还需要建立一套视觉评估体系。我通常会在训练过程中定期生成样本图像保存到TensorBoard或本地目录。关键监控指标损失曲线生成器损失、判别器损失、循环一致性损失、身份损失生成样本质量每N个epoch保存一批生成图像循环一致性可视化输入图像 → 生成图像 → 重建图像 的对比特征统计量生成图像和真实图像在颜色分布、亮度、对比度上的差异def visualize_training_progress(generator, dataloader, epoch, save_dir): 可视化训练进度 generator.eval() with torch.no_grad(): # 取一批测试数据 real_A, real_B next(iter(dataloader)) # 生成图像 fake_B generator(real_A) fake_A generator(real_B) # 重建图像 rec_A generator(fake_B) rec_B generator(fake_A) # 保存对比图 fig, axes plt.subplots(4, 4, figsize(16, 16)) for i in range(4): # 第一行真实A - 生成B - 重建A axes[0, i].imshow(tensor_to_image(real_A[i])) axes[0, i].set_title(fReal A {i}) axes[0, i].axis(off) axes[1, i].imshow(tensor_to_image(fake_B[i])) axes[1, i].set_title(fFake B {i}) axes[1, i].axis(off) axes[2, i].imshow(tensor_to_image(rec_A[i])) axes[2, i].set_title(fReconstructed A {i}) axes[2, i].axis(off) # 计算重建误差 mse torch.mean((real_A[i] - rec_A[i]) ** 2).item() axes[2, i].set_xlabel(fMSE: {mse:.4f}) plt.tight_layout() plt.savefig(f{save_dir}/epoch_{epoch:04d}.png, dpi150, bbox_inchestight) plt.close() generator.train()早停策略Early StoppingCycleGAN训练中损失曲线可能一直波动所以不能单纯根据验证损失来早停。我通常结合多个指标生成图像质量的FID分数每5个epoch计算一次FID如果连续3次没有改善考虑停止循环一致性误差如果重建误差开始上升说明模型可能过拟合视觉评估人工检查生成样本如果质量明显下降就停止训练def compute_fid(real_features, fake_features): 计算FID分数简化版 mu_real, sigma_real real_features.mean(0), torch.cov(real_features.T) mu_fake, sigma_fake fake_features.mean(0), torch.cov(fake_features.T) diff mu_real - mu_fake covmean torch.sqrt(sigma_real sigma_fake) if torch.isnan(covmean).any(): covmean torch.zeros_like(covmean) fid diff diff torch.trace(sigma_real sigma_fake - 2 * covmean) return fid.item() # 在训练循环中监控FID if epoch % 5 0: real_features extract_features(real_images, feature_extractor) fake_features extract_features(fake_images, feature_extractor) fid_score compute_fid(real_features, fake_features) fid_scores.append(fid_score) # 如果FID连续3次上升考虑早停 if len(fid_scores) 4 and all(fid_scores[-i] fid_scores[-(i1)] for i in range(1, 4)): print(fFID连续上升考虑早停) break训练CycleGAN确实是个技术活需要耐心调试和多次实验。我自己的经验是与其追求一次训练出完美模型不如准备多个实验配置并行跑起来比较。每次只调整一个变量比如学习率、损失权重、或者数据增强策略记录下每个配置的表现。这样经过几轮实验你就能对自己的任务和数据集有更深入的理解知道哪些调整是有效的哪些是无效的。最让我有成就感的一次是调试一个老照片上色的任务。一开始模型总是把黑白照片染成奇怪的色调要么太鲜艳要么太暗淡。后来发现是身份损失权重设得太高模型不敢改变颜色。把λ_identity从0.5降到0.1同时加入了感知损失来保持内容结构效果立刻就好了很多。训练稳定后生成的照片颜色自然细节保留得也很好。这种从崩溃到稳定的过程虽然痛苦但最终看到好结果的时候感觉所有的调试都是值得的。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…