mirror of
https://github.com/iperov/DeepFaceLab.git
synced 2025-07-06 21:12:07 -07:00
SAEHD:
Changed help for “Learning rate dropout” option: When the face is trained enough, you can enable this option to get extra sharpness and reduce subpixel shake for less amount of iterations. Enabled it before “disable random warp” and before GAN. n disabled. y enabled cpu enabled on CPU. This allows not to use extra VRAM, sacrificing 20% time of iteration. Changed help for GAN option: Train the network in Generative Adversarial manner. Forces the neural network to learn small details of the face. Enable it only when the face is trained enough and don't disable. Typical value is 0.1 improved GAN. Now it produces better skin detail, less patterned aggressive artifacts, works faster. https://i.imgur.com/Nbh3mw1.png
This commit is contained in:
parent
aacd29269a
commit
e8b04053e4
2 changed files with 159 additions and 41 deletions
|
@ -1,7 +1,7 @@
|
|||
import numpy as np
|
||||
from core.leras import nn
|
||||
tf = nn.tf
|
||||
|
||||
|
||||
patch_discriminator_kernels = \
|
||||
{ 1 : (512, [ [1,1] ]),
|
||||
2 : (512, [ [2,1] ]),
|
||||
|
@ -75,3 +75,117 @@ class PatchDiscriminator(nn.ModelBase):
|
|||
return self.out_conv(x)
|
||||
|
||||
nn.PatchDiscriminator = PatchDiscriminator
|
||||
|
||||
class UNetPatchDiscriminator(nn.ModelBase):
|
||||
"""
|
||||
Inspired by https://arxiv.org/abs/2002.12655 "A U-Net Based Discriminator for Generative Adversarial Networks"
|
||||
"""
|
||||
def calc_receptive_field_size(self, layers):
|
||||
"""
|
||||
result the same as https://fomoro.com/research/article/receptive-field-calculatorindex.html
|
||||
"""
|
||||
rf = 0
|
||||
ts = 1
|
||||
for i, (k, s) in enumerate(layers):
|
||||
if i == 0:
|
||||
rf = k
|
||||
else:
|
||||
rf += (k-1)*ts
|
||||
ts *= s
|
||||
return rf
|
||||
|
||||
def find_archi(self, target_patch_size, max_layers=6):
|
||||
"""
|
||||
Find the best configuration of layers using only 3x3 convs for target patch size
|
||||
"""
|
||||
s = {}
|
||||
for layers_count in range(1,max_layers+1):
|
||||
val = 1 << (layers_count-1)
|
||||
while True:
|
||||
val -= 1
|
||||
|
||||
layers = []
|
||||
sum_st = 0
|
||||
for i in range(layers_count-1):
|
||||
st = 1 + (1 if val & (1 << i) !=0 else 0 )
|
||||
layers.append ( [3, st ])
|
||||
sum_st += st
|
||||
layers.append ( [3, 2])
|
||||
sum_st += 2
|
||||
|
||||
rf = self.calc_receptive_field_size(layers)
|
||||
|
||||
s_rf = s.get(rf, None)
|
||||
if s_rf is None:
|
||||
s[rf] = (layers_count, sum_st, layers)
|
||||
else:
|
||||
if layers_count < s_rf[0] or \
|
||||
( layers_count == s_rf[0] and sum_st > s_rf[1] ):
|
||||
s[rf] = (layers_count, sum_st, layers)
|
||||
|
||||
if val == 0:
|
||||
break
|
||||
|
||||
x = sorted(list(s.keys()))
|
||||
q=x[np.abs(np.array(x)-target_patch_size).argmin()]
|
||||
return s[q][2]
|
||||
|
||||
def on_build(self, patch_size, in_ch):
|
||||
class ResidualBlock(nn.ModelBase):
|
||||
def on_build(self, ch, kernel_size=3 ):
|
||||
self.conv1 = nn.Conv2D( ch, ch, kernel_size=kernel_size, padding='SAME')
|
||||
self.conv2 = nn.Conv2D( ch, ch, kernel_size=kernel_size, padding='SAME')
|
||||
|
||||
def forward(self, inp):
|
||||
x = self.conv1(inp)
|
||||
x = tf.nn.leaky_relu(x, 0.2)
|
||||
x = self.conv2(x)
|
||||
x = tf.nn.leaky_relu(inp + x, 0.2)
|
||||
return x
|
||||
|
||||
prev_ch = in_ch
|
||||
self.convs = []
|
||||
self.res = []
|
||||
self.upconvs = []
|
||||
self.upres = []
|
||||
layers = self.find_archi(patch_size)
|
||||
base_ch = 16
|
||||
|
||||
level_chs = { i-1:v for i,v in enumerate([ min( base_ch * (2**i), 512 ) for i in range(len(layers)+1)]) }
|
||||
|
||||
self.in_conv = nn.Conv2D( in_ch, level_chs[-1], kernel_size=1, padding='VALID')
|
||||
|
||||
for i, (kernel_size, strides) in enumerate(layers):
|
||||
self.convs.append ( nn.Conv2D( level_chs[i-1], level_chs[i], kernel_size=kernel_size, strides=strides, padding='SAME') )
|
||||
|
||||
self.res.append ( ResidualBlock(level_chs[i]) )
|
||||
|
||||
self.upconvs.insert (0, nn.Conv2DTranspose( level_chs[i]*(2 if i != len(layers)-1 else 1), level_chs[i-1], kernel_size=kernel_size, strides=strides, padding='SAME') )
|
||||
|
||||
self.upres.insert (0, ResidualBlock(level_chs[i-1]*2) )
|
||||
|
||||
self.out_conv = nn.Conv2D( level_chs[-1]*2, 1, kernel_size=1, padding='VALID')
|
||||
|
||||
self.center_out = nn.Conv2D( level_chs[len(layers)-1], 1, kernel_size=1, padding='VALID')
|
||||
self.center_conv = nn.Conv2D( level_chs[len(layers)-1], level_chs[len(layers)-1], kernel_size=1, padding='VALID')
|
||||
|
||||
|
||||
def forward(self, x):
|
||||
x = tf.nn.leaky_relu( self.in_conv(x), 0.1 )
|
||||
|
||||
encs = []
|
||||
for conv, res in zip(self.convs, self.res):
|
||||
encs.insert(0, x)
|
||||
x = tf.nn.leaky_relu( conv(x), 0.1 )
|
||||
x = res(x)
|
||||
|
||||
center_out, x = self.center_out(x), self.center_conv(x)
|
||||
|
||||
for i, (upconv, enc, upres) in enumerate(zip(self.upconvs, encs, self.upres)):
|
||||
x = tf.nn.leaky_relu( upconv(x), 0.1 )
|
||||
x = tf.concat( [enc, x], axis=nn.conv2d_ch_axis)
|
||||
x = upres(x)
|
||||
|
||||
return center_out, self.out_conv(x)
|
||||
|
||||
nn.UNetPatchDiscriminator = UNetPatchDiscriminator
|
||||
|
|
|
@ -135,11 +135,11 @@ Examples: df, liae, df-d, df-ud, liae-ud, ...
|
|||
if self.is_first_run() or ask_override:
|
||||
self.options['models_opt_on_gpu'] = io.input_bool ("Place models and optimizer on GPU", default_models_opt_on_gpu, help_message="When you train on one GPU, by default model and optimizer weights are placed on GPU to accelerate the process. You can place they on CPU to free up extra VRAM, thus set bigger dimensions.")
|
||||
|
||||
self.options['lr_dropout'] = io.input_str (f"Use learning rate dropout", default_lr_dropout, ['n','y','cpu'], help_message="When the face is trained enough, you can enable this option to get extra sharpness and reduce subpixel shake for less amount of iterations.\nn - disabled.\ny - enabled\ncpu - enabled on CPU. This allows not to use extra VRAM, sacrificing 20% time of iteration.")
|
||||
self.options['lr_dropout'] = io.input_str (f"Use learning rate dropout", default_lr_dropout, ['n','y','cpu'], help_message="When the face is trained enough, you can enable this option to get extra sharpness and reduce subpixel shake for less amount of iterations. Enabled it before `disable random warp` and before GAN. \nn - disabled.\ny - enabled\ncpu - enabled on CPU. This allows not to use extra VRAM, sacrificing 20% time of iteration.")
|
||||
|
||||
self.options['random_warp'] = io.input_bool ("Enable random warp of samples", default_random_warp, help_message="Random warp is required to generalize facial expressions of both faces. When the face is trained enough, you can disable it to get extra sharpness and reduce subpixel shake for less amount of iterations.")
|
||||
|
||||
self.options['gan_power'] = np.clip ( io.input_number ("GAN power", default_gan_power, add_info="0.0 .. 10.0", help_message="Train the network in Generative Adversarial manner. Accelerates the speed of training. Forces the neural network to learn small details of the face. Enable it only when the face is trained enough and don't disable. Typical value is 1.0"), 0.0, 10.0 )
|
||||
self.options['gan_power'] = np.clip ( io.input_number ("GAN power", default_gan_power, add_info="0.0 .. 10.0", help_message="Train the network in Generative Adversarial manner. Forces the neural network to learn small details of the face. Enable it only when the face is trained enough and don't disable. Typical value is 0.1"), 0.0, 10.0 )
|
||||
|
||||
if 'df' in self.options['archi']:
|
||||
self.options['true_face_power'] = np.clip ( io.input_number ("'True face' power.", default_true_face_power, add_info="0.0000 .. 1.0", help_message="Experimental option. Discriminates result face to be more like src face. Higher value - stronger discrimination. Typical value is 0.01 . Comparison - https://i.imgur.com/czScS9q.png"), 0.0, 1.0 )
|
||||
|
@ -261,10 +261,8 @@ Examples: df, liae, df-d, df-ud, liae-ud, ...
|
|||
|
||||
if self.is_training:
|
||||
if gan_power != 0:
|
||||
self.D_src = nn.PatchDiscriminator(patch_size=resolution//16, in_ch=input_ch, name="D_src")
|
||||
self.D_src_x2 = nn.PatchDiscriminator(patch_size=resolution//32, in_ch=input_ch, name="D_src_x2")
|
||||
self.model_filename_list += [ [self.D_src, 'D_src.npy'] ]
|
||||
self.model_filename_list += [ [self.D_src_x2, 'D_src_x2.npy'] ]
|
||||
self.D_src = nn.UNetPatchDiscriminator(patch_size=resolution//16, in_ch=input_ch, name="D_src")
|
||||
self.model_filename_list += [ [self.D_src, 'D_src_v2.npy'] ]
|
||||
|
||||
# Initialize optimizers
|
||||
lr=5e-5
|
||||
|
@ -287,8 +285,8 @@ Examples: df, liae, df-d, df-ud, liae-ud, ...
|
|||
|
||||
if gan_power != 0:
|
||||
self.D_src_dst_opt = nn.RMSprop(lr=lr, lr_dropout=lr_dropout, clipnorm=clipnorm, name='D_src_dst_opt')
|
||||
self.D_src_dst_opt.initialize_variables ( self.D_src.get_weights()+self.D_src_x2.get_weights(), vars_on_cpu=optimizer_vars_on_cpu, lr_dropout_on_cpu=self.options['lr_dropout']=='cpu')
|
||||
self.model_filename_list += [ (self.D_src_dst_opt, 'D_src_dst_opt.npy') ]
|
||||
self.D_src_dst_opt.initialize_variables ( self.D_src.get_weights(), vars_on_cpu=optimizer_vars_on_cpu, lr_dropout_on_cpu=self.options['lr_dropout']=='cpu')#+self.D_src_x2.get_weights()
|
||||
self.model_filename_list += [ (self.D_src_dst_opt, 'D_src_v2_opt.npy') ]
|
||||
|
||||
if self.is_training:
|
||||
# Adjust batch size for multiple GPU
|
||||
|
@ -360,7 +358,10 @@ Examples: df, liae, df-d, df-ud, liae-ud, ...
|
|||
gpu_target_dstm_eyes = tf.clip_by_value (gpu_target_dstm_all-1, 0, 1)
|
||||
|
||||
gpu_target_srcm_blur = nn.gaussian_blur(gpu_target_srcm, max(1, resolution // 32) )
|
||||
gpu_target_srcm_blur = tf.clip_by_value(gpu_target_srcm_blur, 0, 0.5) * 2
|
||||
|
||||
gpu_target_dstm_blur = nn.gaussian_blur(gpu_target_dstm, max(1, resolution // 32) )
|
||||
gpu_target_dstm_blur = tf.clip_by_value(gpu_target_dstm_blur, 0, 0.5) * 2
|
||||
|
||||
gpu_target_dst_masked = gpu_target_dst*gpu_target_dstm_blur
|
||||
gpu_target_dst_anti_masked = gpu_target_dst*(1.0 - gpu_target_dstm_blur)
|
||||
|
@ -431,27 +432,30 @@ Examples: df, liae, df-d, df-ud, liae-ud, ...
|
|||
gpu_D_code_loss_gvs += [ nn.gradients (gpu_D_code_loss, self.code_discriminator.get_weights() ) ]
|
||||
|
||||
if gan_power != 0:
|
||||
gpu_pred_src_src_d = self.D_src(gpu_pred_src_src_masked_opt)
|
||||
gpu_pred_src_src_d, \
|
||||
gpu_pred_src_src_d2 = self.D_src(gpu_pred_src_src_masked_opt)
|
||||
|
||||
gpu_pred_src_src_d_ones = tf.ones_like (gpu_pred_src_src_d)
|
||||
gpu_pred_src_src_d_zeros = tf.zeros_like(gpu_pred_src_src_d)
|
||||
gpu_target_src_d = self.D_src(gpu_target_src_masked_opt)
|
||||
gpu_target_src_d_ones = tf.ones_like(gpu_target_src_d)
|
||||
|
||||
gpu_pred_src_src_x2_d = self.D_src_x2(gpu_pred_src_src_masked_opt)
|
||||
gpu_pred_src_src_x2_d_ones = tf.ones_like (gpu_pred_src_src_x2_d)
|
||||
gpu_pred_src_src_x2_d_zeros = tf.zeros_like(gpu_pred_src_src_x2_d)
|
||||
gpu_target_src_x2_d = self.D_src_x2(gpu_target_src_masked_opt)
|
||||
gpu_target_src_x2_d_ones = tf.ones_like(gpu_target_src_x2_d)
|
||||
gpu_pred_src_src_d2_ones = tf.ones_like (gpu_pred_src_src_d2)
|
||||
gpu_pred_src_src_d2_zeros = tf.zeros_like(gpu_pred_src_src_d2)
|
||||
|
||||
gpu_target_src_d, \
|
||||
gpu_target_src_d2 = self.D_src(gpu_target_src_masked_opt)
|
||||
|
||||
gpu_target_src_d_ones = tf.ones_like(gpu_target_src_d)
|
||||
gpu_target_src_d2_ones = tf.ones_like(gpu_target_src_d2)
|
||||
|
||||
gpu_D_src_dst_loss = (DLoss(gpu_target_src_d_ones , gpu_target_src_d) + \
|
||||
DLoss(gpu_pred_src_src_d_zeros , gpu_pred_src_src_d) ) * 0.5 + \
|
||||
(DLoss(gpu_target_src_x2_d_ones , gpu_target_src_x2_d) + \
|
||||
DLoss(gpu_pred_src_src_x2_d_zeros, gpu_pred_src_src_x2_d) ) * 0.5
|
||||
(DLoss(gpu_target_src_d2_ones , gpu_target_src_d2) + \
|
||||
DLoss(gpu_pred_src_src_d2_zeros , gpu_pred_src_src_d2) ) * 0.5
|
||||
|
||||
gpu_D_src_dst_loss_gvs += [ nn.gradients (gpu_D_src_dst_loss, self.D_src.get_weights()+self.D_src_x2.get_weights() ) ]
|
||||
|
||||
gpu_G_loss += 0.5*gan_power*( DLoss(gpu_pred_src_src_d_ones, gpu_pred_src_src_d) + DLoss(gpu_pred_src_src_x2_d_ones, gpu_pred_src_src_x2_d))
|
||||
gpu_D_src_dst_loss_gvs += [ nn.gradients (gpu_D_src_dst_loss, self.D_src.get_weights() ) ]#+self.D_src_x2.get_weights()
|
||||
|
||||
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))
|
||||
|
||||
gpu_G_loss_gvs += [ nn.gradients ( gpu_G_loss, self.src_dst_trainable_weights ) ]
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue