|
本帖最后由 五折 于 2025-7-2 12:26 编辑
没啥可练的
DFL又是停止了更新
干脆整理一些偏向原理的东西跟大家分享
想到哪里写到哪里哈,随时补充修缮
架构概述
DF架构(DeepFake)
DF架构是一种较为简单的编码器-解码器架构,它包含:
- 一个共享的编码器(Encoder)
- 一个中间层(Inter)
- 两个独立的解码器(Decoder_src和Decoder_dst)
LIAE架构(Local-Image Adaptive Embedding)
LIAE架构是一种更复杂的架构,它包含:
- 一个共享的编码器(Encoder)
- 两个中间层(Inter_AB和Inter_B)
- 一个共享的解码器(Decoder)
详细训练步骤
1. 数据准备阶段- # 初始化样本生成器
- training_data_src_path = self.training_data_src_path if not
- self.pretrain else self.get_pretraining_data_path()
- training_data_dst_path = self.training_data_dst_path if not
- self.pretrain else self.get_pretraining_data_path()
- # 设置样本生成器,处理源图像和目标图像
- self.set_training_data_generators([
- SampleGeneratorFace(training_data_src_path, ...), # 源图
- 像生成器
- SampleGeneratorFace(training_data_dst_path, ...) # 目标
- 图像生成器
- ])
复制代码 这一阶段主要完成:
1. 设置训练数据路径
2. 初始化样本生成器,用于加载和处理源图像和目标图像
3. 配置样本处理选项,如随机翻转、随机扭曲、HSV偏移等数据增强
2. 模型初始化阶段
DF架构初始化- if 'df' in archi_type:
- # 初始化编码器、中间层和解码器
- self.encoder = model_archi.Encoder(in_ch=input_ch,
- e_ch=e_dims, name='encoder')
- encoder_out_ch = self.encoder.get_out_ch()*self.encoder.
- get_out_res(resolution)**2
-
- self.inter = model_archi.Inter(in_ch=encoder_out_ch,
- ae_ch=ae_dims, ae_out_ch=ae_dims, name='inter')
- inter_out_ch = self.inter.get_out_ch()
-
- # 为源图像和目标图像分别初始化解码器
- self.decoder_src = model_archi.Decoder
- (in_ch=inter_out_ch, d_ch=d_dims, d_mask_ch=d_mask_dims,
- name='decoder_src')
- self.decoder_dst = model_archi.Decoder
- (in_ch=inter_out_ch, d_ch=d_dims, d_mask_ch=d_mask_dims,
- name='decoder_dst')
复制代码
LIAE架构初始化- elif 'liae' in archi_type:
- # 初始化编码器
- self.encoder = model_archi.Encoder(in_ch=input_ch,
- e_ch=e_dims, name='encoder')
- encoder_out_ch = self.encoder.get_out_ch()*self.encoder.
- get_out_res(resolution)**2
-
- # 初始化两个中间层
- self.inter_AB = model_archi.Inter(in_ch=encoder_out_ch,
- ae_ch=ae_dims, ae_out_ch=ae_dims*2, name='inter_AB')
- self.inter_B = model_archi.Inter(in_ch=encoder_out_ch,
- ae_ch=ae_dims, ae_out_ch=ae_dims*2, name='inter_B')
-
- inter_out_ch = self.inter_AB.get_out_ch()
- inters_out_ch = inter_out_ch*2
-
- # 初始化共享解码器
- self.decoder = model_archi.Decoder(in_ch=inters_out_ch,
- d_ch=d_dims, d_mask_ch=d_mask_dims, name='decoder')
复制代码 这一阶段主要完成:
1. 根据选择的架构类型(DF或LIAE)初始化相应的模型组件
2. 设置各组件的通道数、维度等参数
3. 如果启用GAN,还会初始化判别器
3. 优化器初始化阶段- # 初始化优化器
- lr=5e-5
- if self.options['lr_dropout'] in ['y','cpu'] and not self.
- pretrain:
- lr_cos = 500
- lr_dropout = 0.3
- else:
- lr_cos = 0
- lr_dropout = 1.0
- OptimizerClass = nn.AdaBelief if adabelief else nn.RMSprop
- clipnorm = 1.0 if self.options['clipgrad'] else 0.0
- # 设置可训练权重
- if 'df' in archi_type:
- self.src_dst_saveable_weights = self.encoder.get_weights
- () + self.inter.get_weights() + self.decoder_src.
- get_weights() + self.decoder_dst.get_weights()
- self.src_dst_trainable_weights = self.
- src_dst_saveable_weights
- elif 'liae' in archi_type:
- self.src_dst_saveable_weights = self.encoder.get_weights
- () + self.inter_AB.get_weights() + self.inter_B.
- get_weights() + self.decoder.get_weights()
- if random_warp:
- self.src_dst_trainable_weights = self.
- src_dst_saveable_weights
- else:
- self.src_dst_trainable_weights = self.encoder.
- get_weights() + self.inter_B.get_weights() + self.
- decoder.get_weights()
- # 初始化优化器
- self.src_dst_opt = OptimizerClass(lr=lr,
- lr_dropout=lr_dropout, lr_cos=lr_cos, clipnorm=clipnorm,
- name='src_dst_opt')
- self.src_dst_opt.initialize_variables(self.
- src_dst_saveable_weights, vars_on_cpu=optimizer_vars_on_cpu,
- lr_dropout_on_cpu=self.options['lr_dropout']=='cpu')
复制代码 这一阶段主要完成:
1. 设置学习率、学习率衰减等优化器参数
2. 根据架构类型确定可训练权重
3. 初始化优化器(AdaBelief或RMSprop)
4. 如果启用GAN,还会初始化GAN判别器的优化器
4. 计算图构建阶段
DF架构的前向传播- if 'df' in archi_type:
- # 编码
- gpu_src_code = self.inter(self.encoder(gpu_warped_src))
- gpu_dst_code = self.inter(self.encoder(gpu_warped_dst))
-
- # 解码
- gpu_pred_src_src, gpu_pred_src_srcm = self.decoder_src
- (gpu_src_code) # 源到源的重建
- gpu_pred_dst_dst, gpu_pred_dst_dstm = self.decoder_dst
- (gpu_dst_code) # 目标到目标的重建
- gpu_pred_src_dst, gpu_pred_src_dstm = self.decoder_src
- (gpu_dst_code) # 目标到源的转换(换脸结果)
复制代码
LIAE架构的前向传播- elif 'liae' in archi_type:
- # 编码
- gpu_src_code = self.encoder(gpu_warped_src)
- gpu_src_inter_AB_code = self.inter_AB(gpu_src_code)
- gpu_src_code = tf.concat([gpu_src_inter_AB_code,
- gpu_src_inter_AB_code], nn.conv2d_ch_axis)
-
- gpu_dst_code = self.encoder(gpu_warped_dst)
- gpu_dst_inter_B_code = self.inter_B(gpu_dst_code)
- gpu_dst_inter_AB_code = self.inter_AB(gpu_dst_code)
- gpu_dst_code = tf.concat([gpu_dst_inter_B_code,
- gpu_dst_inter_AB_code], nn.conv2d_ch_axis)
- gpu_src_dst_code = tf.concat([gpu_dst_inter_AB_code,
- gpu_dst_inter_AB_code], nn.conv2d_ch_axis)
-
- # 解码
- gpu_pred_src_src, gpu_pred_src_srcm = self.decoder
- (gpu_src_code) # 源到源的重建
- gpu_pred_dst_dst, gpu_pred_dst_dstm = self.decoder
- (gpu_dst_code) # 目标到目标的重建
- gpu_pred_src_dst, gpu_pred_src_dstm = self.decoder
- (gpu_src_dst_code) # 目标到源的转换(换脸结果)
复制代码
这一阶段主要完成:
1. 构建模型的前向传播计算图
2. DF架构:通过共享编码器和中间层,但使用独立解码器实现换脸
3. LIAE架构:通过共享编码器和解码器,但使用两个中间层(Inter_AB和Inter_B)实现换脸
5. 损失计算阶段- # 计算源图像和目标图像的损失
- if resolution < 256:
- gpu_src_loss = tf.reduce_mean(10*nn.dssim
- (gpu_target_src_masked_opt, gpu_pred_src_src_masked_opt,
- max_val=1.0, filter_size=int(resolution/11.6)), axis=[1])
- else:
- gpu_src_loss = tf.reduce_mean(5*nn.dssim
- (gpu_target_src_masked_opt, gpu_pred_src_src_masked_opt,
- max_val=1.0, filter_size=int(resolution/11.6)), axis=[1])
- gpu_src_loss += tf.reduce_mean(5*nn.dssim
- (gpu_target_src_masked_opt, gpu_pred_src_src_masked_opt,
- max_val=1.0, filter_size=int(resolution/23.2)), axis=[1])
- gpu_src_loss += tf.reduce_mean(10*tf.square
- (gpu_target_src_masked_opt - gpu_pred_src_src_masked_opt),
- axis=[1,2,3])
- # 如果启用眼睛嘴巴优先级
- if eyes_mouth_prio:
- gpu_src_loss += tf.reduce_mean(300*tf.abs
- (gpu_target_src*gpu_target_srcm_em -
- gpu_pred_src_src*gpu_target_srcm_em), axis=[1,2,3])
- # 遮罩损失
- gpu_src_loss += tf.reduce_mean(10*tf.square(gpu_target_srcm -
- gpu_pred_src_srcm), axis=[1,2,3])
- # 风格损失
- face_style_power = self.options['face_style_power'] / 100.0
- if face_style_power != 0 and not self.pretrain:
- gpu_src_loss += nn.style_loss(...)
- # 背景风格损失
- bg_style_power = self.options['bg_style_power'] / 100.0
- if bg_style_power != 0 and not self.pretrain:
- gpu_src_loss += ...
- # 目标图像损失计算(类似源图像)
- ...
- # 总损失
- gpu_G_loss = gpu_src_loss + gpu_dst_loss
- # GAN损失(如果启用)
- if gan_power != 0:
- gpu_G_loss += gan_power*(DLoss(gpu_pred_src_src_d_ones,
- gpu_pred_src_src_d) + DLoss(gpu_pred_src_src_d2_ones,
- gpu_pred_src_src_d2))
复制代码
这一阶段主要完成:
1. 计算源图像和目标图像的重建损失(DSSIM和MSE)
2. 计算遮罩损失
3. 如果启用,计算风格损失、GAN损失等
4. 汇总总损失
6. 梯度计算和优化器更新阶段- # 计算梯度
- gpu_G_loss_gvs += [nn.gradients(gpu_G_loss, self.
- src_dst_trainable_weights)]
- # 在CPU上拼接不同GPU的结果
- with tf.devICE(f'/CPU:0'):
- pred_src_src = nn.concat(gpu_pred_src_src_list, 0)
- pred_dst_dst = nn.concat(gpu_pred_dst_dst_list, 0)
- pred_src_dst = nn.concat(gpu_pred_src_dst_list, 0)
- pred_src_srcm = nn.concat(gpu_pred_src_srcm_list, 0)
- pred_dst_dstm = nn.concat(gpu_pred_dst_dstm_list, 0)
- pred_src_dstm = nn.concat(gpu_pred_src_dstm_list, 0)
- # 平均梯度并创建优化器更新操作
- with tf.device(models_opt_device):
- src_loss = tf.concat(gpu_src_losses, 0)
- dst_loss = tf.concat(gpu_dst_losses, 0)
- src_dst_loss_gv_op = self.src_dst_opt.get_update_op(nn.
- average_gv_list(gpu_G_loss_gvs))
复制代码
这一阶段主要完成:
1. 计算损失相对于可训练权重的梯度
2. 在多GPU训练时,合并不同GPU上的结果
3. 创建优化器更新操作
7. 训练函数定义阶段- # 定义源图像和目标图像的训练函数
- def src_dst_train(warped_src, target_src, target_srcm,
- target_srcm_em, warped_dst, target_dst, target_dstm,
- target_dstm_em):
- s, d = nn.tf_sess.run([src_loss, dst_loss,
- src_dst_loss_gv_op],
- feed_dict={self.warped_src:
- warped_src,
- self.target_src:
- target_src,
- self.target_srcm:
- target_srcm,
- self.target_srcm_em:
- target_srcm_em,
- self.warped_dst:
- warped_dst,
- self.target_dst:
- target_dst,
- self.target_dstm:
- target_dstm,
- self.target_dstm_em:
- target_dstm_em})[:2]
- return s, d
- self.src_dst_train = src_dst_train
- # 如果启用GAN,定义判别器训练函数
- if gan_power != 0:
- def D_src_dst_train(warped_src, target_src, target_srcm,
- target_srcm_em, warped_dst, target_dst, target_dstm,
- target_dstm_em):
- nn.tf_sess.run([src_D_src_dst_loss_gv_op], feed_dict=
- {...})
- self.D_src_dst_train = D_src_dst_train
复制代码
这一阶段主要完成:
1. 定义实际的训练函数,用于执行前向传播、损失计算和反向传播
2. 如果启用GAN,定义判别器的训练函数
8. 单次迭代训练阶段- def onTrainOneIter(self):
- # 生成下一批样本
- ((warped_src, target_src, target_srcm, target_srcm_em),
- (warped_dst, target_dst, target_dstm, target_dstm_em)) =
- self.generate_next_samples()
-
- # 训练源图像和目标图像
- src_loss, dst_loss = self.src_dst_train(warped_src,
- target_src, target_srcm, target_srcm_em,
- warped_dst,
- target_dst,
- target_dstm,
- target_dstm_em)
-
- # 如果启用真脸判别器,训练判别器
- if self.options['true_face_power'] != 0 and not self.
- pretrain:
- self.D_train(warped_src, warped_dst)
-
- # 如果启用GAN,训练GAN判别器
- if self.gan_power != 0:
- self.D_src_dst_train(warped_src, target_src,
- target_srcm, target_srcm_em,
- warped_dst, target_dst,
- target_dstm, target_dstm_em)
-
- return (('src_loss', np.mean(src_loss)), ('dst_loss', np.
- mean(dst_loss)))
复制代码
这一阶段主要完成:
1. 生成下一批训练样本
2. 执行模型训练
3. 如果启用,训练判别器
4. 返回损失值用于显示
9. 模型保存和加载阶段- # 加载/初始化所有模型/优化器权重
- for model, filename in io.progress_bar_generator(self.
- model_filename_list, "初始化模型"):
- # 判断是否需要初始化
- do_init = self.is_first_run()
-
- # 尝试加载权重
- if not do_init:
- do_init = not model.load_weights(self.
- get_strpath_storage_for_file(filename))
-
- # 如果需要初始化,则初始化权重
- if do_init:
- model.init_weights()
- # 保存模型
- def onSave(self):
- for model, filename in io.progress_bar_generator(self.
- get_model_filename_list(), "Saving", leave=False):
- model.save_weights(self.get_strpath_storage_for_file
- (filename))
复制代码 这一阶段主要完成:
1. 加载预训练模型权重或初始化新权重
2. 定义模型保存函数
10. 预览生成阶段- def onGetPreview(self, samples, for_history=False):
- # 解包样本
- ((warped_src, target_src, target_srcm, target_srcm_em),
- (warped_dst, target_dst, target_dstm, target_dstm_em)) =
- samples
-
- # 生成预览图像
- S, D, SS, DD, DDM, SD, SDM = [np.clip(nn.to_data_format
- (x, "NHWC", self.model_data_format), 0.0, 1.0)
- for x in ([target_src,
- target_dst] + self.AE_view
- (target_src, target_dst))]
-
- # 生成预览结果
- result = []
- # ...
- return result
复制代码
这一阶段主要完成:
1. 生成预览图像,用于显示训练进度
2. 包括源图像、目标图像、重建图像和换脸结果
DF和LIAE架构的主要区别
1. 架构结构
- DF架构 :使用一个共享的编码器和中间层,但有两个独立的解码器(一个用于源图像,一个用于目标图像)
- LIAE架构 :使用一个共享的编码器和解码器,但有两个中间层(Inter_AB和Inter_B)
2. 编码和解码过程
- DF架构 :- 源图像 → 编码器 → 中间层 → 解码器_源 → 重建源图像
- 目标图像 → 编码器 → 中间层 → 解码器_目标 → 重建目标图像
- 目标图像 → 编码器 → 中间层 → 解码器_源 → 换脸结果
复制代码
- LIAE架构 :- 源图像 → 编码器 → Inter_AB → [Inter_AB, Inter_AB] → 解码器 → 重
- 建源图像
- 目标图像 → 编码器 → [Inter_B, Inter_AB] → 解码器 → 重建目标图像
- 目标图像 → 编码器 → [Inter_AB, Inter_AB] → 解码器 → 换脸结果
复制代码
3. 训练特点
- DF架构 :训练所有组件(编码器、中间层、两个解码器)
- LIAE架构 :如果启用随机扭曲,训练所有组件;否则,只训练编码器、Inter_B和解码器
4. 适用场景
- DF架构 :更适合源和目标人脸差异较小的情况
- LIAE架构 :更适合处理源和目标人脸差异较大的情况,能更好地保留身份特征
总结
deepfacelab中的DF和LIAE架构都是基于编码器-解码器结构的深度学习模型,但它们在架构设计和训练过程上有明显区别。DF架构使用独立的解码器来处理源和目标图像,而LIAE架构使用共享解码器但有两个中间层来处理不同的身份信息。这两种架构各有优势,用户可以根据具体的换脸需求选择合适的架构。
2025/07/02重新编辑了关于Liae模型删AB的原理解释:
在 `Model.py` 中,liae架构使用了双inter结构:
- inter_AB : 共享的中间层,同时处理src和dst特征
- inter_B : dst专用的中间层
- encoder : 统一的编码器
- decoder : 统一的解码器
问题1:长时间训练后人脸趋向DST的原因
从代码第410-420行可以看到liae的核心逻辑:- gpu_src_code = self.encoder(gpu_warped_src)
- gpu_src_inter_AB_code = self.inter_AB(gpu_src_code)
- gpu_src_code = tf.concat([gpu_src_inter_AB_code, gpu_src_inter_AB_code], nn.
- conv2d_ch_axis)
- gpu_dst_code = self.encoder(gpu_warped_dst)
- gpu_dst_inter_B_code = self.inter_B(gpu_dst_code)
- gpu_dst_inter_AB_code = self.inter_AB(gpu_dst_code)
- gpu_dst_code = tf.concat([gpu_dst_inter_B_code, gpu_dst_inter_AB_code], nn.
- conv2d_ch_axis)
- gpu_src_dst_code = tf.concat([gpu_dst_inter_AB_code, gpu_dst_inter_AB_code],
- nn.conv2d_ch_axis)
复制代码
关键问题 :长时间训练时, `inter_AB` 会过度学习dst的特征,因为:
1. inter_AB同时接收src和dst的编码特征
2. 在生成src→dst时,使用的是 gpu_dst_inter_AB_code 的双重拼接
3. 随着训练进行,inter_AB逐渐偏向dst的特征分布
问题2:删除inter_AB文件后的恢复现象
从代码第630-640行的模型初始化逻辑:
- if self.pretrain_just_disabled:
- do_init = False
- if 'liae' in archi_type:
- if model == self.inter_AB or model == self.inter_B:
- do_init = True
- else:
- do_init = self.is_first_run()
- if not do_init:
- do_init = not model.load_weights(self.get_strpath_storage_for_file
- (filename))
- if do_init:
- model.init_weights()
复制代码
恢复机制 :
1. 删除inter_AB.npy文件后,模型会重新初始化inter_AB权重
2. 新的inter_AB权重是随机的,打破了过度拟合dst的状态
3. encoder和decoder的权重保持不变,所以基础特征提取能力保留
4. 短暂模糊是因为inter_AB需要重新学习特征映射关系
问题3:循环现象的根本原因
这是liae架构的固有特性:
- inter_AB的双重角色(处理src和dst)导致特征空间的不稳定
- 没有有效的正则化机制防止inter_AB过度偏向某一方
- 训练目标函数中缺乏对特征平衡的约束
问题4:删除inter_AB对训练参数的影响分析
4.1 眼睛嘴巴优先(eyes_mouth_prio)
从代码第450-460行的损失计算:
- if eyes_mouth_prio:
- gpu_src_loss += tf.reduce_mean(300*tf.abs
- (gpu_target_src*gpu_target_srcm_em -
- gpu_pred_src_src*gpu_target_srcm_em), axis=[1,2,3])
复制代码
影响程度:中等遗失(约30-50%)
- 眼嘴区域的特征映射存储在inter_AB中
- 删除后需要重新学习眼嘴的精细特征对应关系
- 但encoder中的基础眼嘴特征检测能力保留
4.2 侧脸优化(uniform_yaw)
影响程度:高度遗失(约60-80%)
- 侧脸的角度变换特征主要存储在inter_AB的映射关系中
- 删除后侧脸的特征对应关系完全丢失
- 需要较长时间重新训练侧脸的特征映射
4.3 颜色转换模式(ct_mode)
从代码第650-670行的样本生成:
- random_ct_samples_path=training_data_dst_path if ct_mode is not None and not
- self.pretrain else None
复制代码
影响程度:低度遗失(约10-20%)
- 颜色转换主要在数据预处理阶段完成
- inter_AB主要影响颜色特征的高级映射关系
- 基础颜色转换逻辑保留在数据生成器中
4.4 其他参数影响:
- GAN相关参数 :完全保留(GAN判别器独立存储)
- 遮罩训练 :部分遗失(约20-30%)
- 风格迁移参数 :高度遗失(约70-90%)
- 真脸强度 :中等遗失(约40-60%)
解决建议
1. 降低学习率 :在长时间训练后适当降低学习率,减缓inter_AB的过度拟合
2. 定期保存检查点 :在训练效果较好时保存完整模型状态
3. 使用lr_dropout :启用学习率dropout来稳定训练后期的特征学习
4. 监控训练指标 :密切关注src_loss和dst_loss的平衡状态
这些现象反映了liae架构在特征分离和重组方面的复杂性,需要更精细的训练策略来维持稳定的训练效果。
|
评分
-
查看全部评分
Zhatv换脸论坛免责声明
全站默认解压密码:zhatv.cn
【Zhatv】论坛里的文章仅代表作者本人的观点,与本网站立场无关。
所有文章、内容、信息、资料,都不保证其准确性、完整性、有效性、时效性,请依据情况自身做出判断。
因阅读本站内容而被误导等其他因素所造成的损失责任自负,【Zhatv】不承担任何责任。
|