def conv(in_f, out_f, kernel_size=3, stride=1, actn=True, pad=None, bn=True):
if pad is None: pad = kernel_size//2
layers = [nn.Conv2d(in_f, out_f, kernel_size, stride, pad, bias=not bn)]
if actn: layers.append(nn.ReLU(inplace=True))
if bn: layers.append(nn.BatchNorm2d(out_f))
return nn.Sequential(*layers)
Создайте блок Conv-ReLU-BN, который дополняет входной тензор таким образом, чтобы выходной тензор блока имел ту же форму (высоту/ширину), что и входной тензор.
class ResSequentialCenter(nn.Module):
def __init__(self, layers):
super().__init__()
self.m = nn.Sequential(*layers)
def forward(self, x):
return x[:, :, 2:-2, 2:-2] + self.m(x)
Модуль Residual Sequential Center принимает на вход массив слоев (блоки conv-relu-bn) и создает последовательный блок, используя слои.
В прямой функции модуль возвращает выходной тензор входного тензора (без заполнения) + вывод последовательного блока с входным тензором.
def res_block(num_f):
return ResSequentialCenter([conv(num_f, num_f, pad=0), conv(num_f, num_f, pad=0)])
Остаточный блок создает модуль ResSequentialCenter с 2 блоками conv-relu-bn.
def upsample(in_f, out_f):
return nn.Sequential(nn.Upsample(scale_factor=2), conv(in_f, out_f))
Блок Upsample создает модуль Sequential слоя Upsample с блоком conv-relu-bn.
class StyleResnet(nn.Module):
def __init__(self):
super().__init__()
layers = [nn.ReflectionPad2d(40), nn.Conv2d(3, 32, 9),
nn.Conv2d(32, 64, 3, stride=2), nn.Conv2d(64, 128, 3, stride=2)]
for i in range(5): layers.append(res_block(128))
layers += [upsample(128, 64), upsample(64, 32), conv(32, 3, 9, actn=False)]
self.features = nn.Sequential(*layers)
def forward(self, x): return self.features(x)
Модуль Style Resnet инициализирует последовательный блок, который начинается со слоя заполнения Reflection 40, 32-мерного слоя Conv с размером ядра 9 и 2 слоев Conv с размером ядра 3, оба с шагом 2 (уменьшает форму тензора на 2 каждый раз ).
Затем добавляются 5 128-дневных остаточных блоков, заканчивающихся 2 блоками повышающей дискретизации и конвекторным блоком размера ядра 9.
Функция Forward возвращает выходной тензор блока Sequential из входного тензора.
class SaveFeatures():
features=None
def __init__(self, m): self.features = m.register_forward_hook(self.hook_fn)
def hook_fn(self, module, input, output): self.features = output
def close(self): self.hook.remove()
Объект SaveFeatures сохраняет выходные данные входного слоя для использования в функции потерь.
def ct_loss(input, targ): return F.mse_loss(input, targ)
Потеря содержимого возвращает среднеквадратичную ошибку ввода и цели.
def gram(x):
b,c,h,w = x.size()
x = x.view(b, c, -1)
return torch.bmm(x, x.transpose(1, 2))/(c*h*w)*1e6
Функция Gram возвращает матрицу Gram входных данных (умножение матрицы тензора на себя с последующим делением количества элементов для нахождения среднего значения)
def gram_loss(input, targ):
return F.mse_loss(gram(input), gram(targ[:input.size(0)]))
Потеря грамма возвращает среднеквадратичную ошибку потери матрицы Грама входного тензора и матрицы Грама цели.
class CombinedLoss(nn.Module):
def __init__(self, m, layer_ids, style_im, ct_weight, style_weights):
self.m,self.ct_weight,self.style_weights=m,ct_weight,style_weights
self.sfs = SaveFeatures(self.m[i] for i in layer_ids)
m(VV(style_im))
self.style_feats = [V(sf.features.data.clone()) for sf in self.sfs]
Параметры комбинированных потерь: m (модель для определения потери стиля/контента), layer_ids (идентификаторы слоев стилей), style_im (целевое изображение для получения стиля), ct_weight (вес потери контента), style_weights (веса потерь каждого слоя)
Сохраните функции стиля изображения целевого стиля, которые будут использоваться при потере стиля.
def forward(self, input, targ):
self.m(VV(targ))
targ_feat = self.sfs[2].features.data.clone()
self.m(input)
inp_feats = [sf.features for sf in self.sfs]
ct_loss = [ct_loss(inp_feats[2], V(targ_feat))*self.ct_weight]
style_loss = [gram_loss(inp, sty)*weight for inp, sty, weight in zip(inp_feats, self.style_feats, self.style_weights)]
loss = sum(ct_loss + style_loss)
return loss
В прямой функции получите признаки из изображения содержимого (признаки из 2-го слоя модели для потерь) и получите признаки из входного тензора (признаки от каждого объекта SaveFeatures модели для потерь)
ct_loss = [ct_loss(inp_feats[2], V(targ_feat))*self.ct_weight]
Потеря контента рассчитывается с использованием функции ct_loss со вторым входным параметром и функциями изображения контента в качестве входных данных, умноженных на вес.
[gram_loss(inp, sty)*weight for inp, sty, weight in zip(inp_feats, self.style_feats, self.style_weights)]
loss = sum(ct_loss + style_loss)
Потеря стиля рассчитывается путем суммирования потерь в граммах каждого входного элемента и элемента стиля, умноженных на вес:
РЕЗЮМЕ
Модель вынуждена создавать изображение, которое будет давать такие же функции контента, как и обучающее изображение, и функции стиля, что и целевое изображение стиля, в результате чего получается модель, которая может брать изображение из обучающего набора и создавать изображение с аналогичным контентом. тренировочное изображение, но со стилем изображения целевого стиля