|
本帖最后由 五折 于 2025-6-12 22: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架构使用共享解码器但有两个中间层来处理不同的身份信息。这两种架构各有优势,用户可以根据具体的换脸需求选择合适的架构。
补充一下,关于Liae模型删AB的原理解释:
inter_AB.npy文件的作用
在LIAE(Local Identity and Appearance Encoder)架构中, inter_AB.npy 是一个关键组件的权重文件,它存储了 inter_AB 模块的参数。从代码中可以看出:
1. LIAE架构使用了两个中间编码器: inter_B 和 inter_AB
- inter_B :处理身份特征(identity features)
- inter_AB :处理外观特征(appearance features)
2. 在前向传播过程中,LIAE架构的工作流程是:- # 编码
- gpu_dst_code = self.encoder(self.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_B_code,
- gpu_dst_inter_AB_code], nn.conv2d_ch_axis)
-
- # 解码生成结果
- gpu_pred_src_dst, gpu_pred_src_dstm = self.decoder
- (gpu_src_dst_code)
复制代码
删除inter_AB.npy的影响
当你删除 inter_AB.npy 文件时,会产生以下影响:
1. 模型重新初始化 :根据代码中的加载逻辑,当模型无法加载权重文件时,它会重新初始化相应组件的权重:- if not do_init:
- do_init = not model.load_weights(self.
- get_strpath_storage_for_file(filename))
-
- if do_init:
- model.init_weights()
复制代码
2. 外观特征重置 :由于 inter_AB 模块负责处理外观特征,删除其权重文件会导致这部分特征被重置为随机初始化的状态。
3. 抑制DST脸型特征 :这就是为什么网上教程说删除 inter_AB.npy 可以抑制生成的人脸过于趋向DST(目标人脸)。因为:
- inter_AB 模块主要负责捕获和转移外观特征(如肤色、纹理等)
- 当这些特征被重置时,模型会减少对DST脸型特征的依赖
- 重新训练后,模型会重新学习外观特征,但此时可能会更多地保留SRC(源人脸)的结构特征
4. 训练进度部分丢失 :删除文件后,模型需要重新学习外观特征的转换,这意味着在这方面的训练进度会部分丢失。
为什么这种方法有效
在LIAE架构中,身份和外观特征是分开处理的:
- inter_B 保留了身份信息(脸型结构)
- inter_AB 处理外观信息(肤色、纹理等)
当你删除 inter_AB.npy 时,模型会通过 inter_B保留已学习的身份特征,但重置外观特征的处理。这样在继续训练时,模型会在保留原有身份特征的基础上重新学习外观特征,从而可能减少DST脸型对结果的影响。
|
Zhatv换脸论坛免责声明
全站默认解压密码:zhatv.cn
【Zhatv】论坛里的文章仅代表作者本人的观点,与本网站立场无关。
所有文章、内容、信息、资料,都不保证其准确性、完整性、有效性、时效性,请依据情况自身做出判断。
因阅读本站内容而被误导等其他因素所造成的损失责任自负,【Zhatv】不承担任何责任。
|