# 人人都是毕加索

版权声明：本文为 frendy 原创文章，可以随意转载，但请务必在明确位置注明出处。

welcome.png

### 为什么选 Pytorch

Matlab is so 2012.
Caffe is so 2013.
Theano is so 2014.
Torch is so 2015.
TensorFlow is so 2016.
‏It's 2017 now.


### 环境配置

http://pytorch.org/


frendy 这里是 Win10，侥幸找到一个别人编译好的版本，更侥幸的是他的电脑配置跟我的基本一样，可以直接使用（百度云下载地址）：

conda install pytorch-0.1.12-py36_0.1.12cu80.tar.bz2
pip install torchvision


### 原理分析

Image_A (content) + Image_B (style) = Image_C (result)

• 使得 A 和 C 的内容差异尽可能小；
• 使得 B 和 C 的风格差异尽可能小。

##### 内容差异

class ContentLoss(nn.Module):
def __init__(self, target, weight):
super(ContentLoss, self).__init__()
# we 'detach' the target content from the tree used
self.target = target.detach() * weight
# to dynamically compute the gradient: this is a stated value,
# not a variable. Otherwise the forward method of the criterion
# will throw an error.
self.weight = weight
self.criterion = nn.MSELoss()

def forward(self, inputs):
self.loss = self.criterion(inputs*self.weight, self.target)
self.outputs = inputs
return self.outputs

def backward(self, retain_variables=True):
self.loss.backward(retain_variables=retain_variables)
return self.loss


##### 风格差异

004.png

class GramMatrix(nn.Module):
def forward(self, input):
a, b, c, d = input.size()
# a=batch size(=1)
# b=number of feature maps
# (c,d)=dimensions of a f. map (N=c*d)

features = input.view(a * b, c * d)  # resise F_XL into \hat F_XL

G = torch.mm(features, features.t())  # compute the gram product

# we 'normalize' the values of the gram matrix
# by dividing by the number of element in each feature maps.
return G.div(a * b * c * d)

class StyleLoss(nn.Module):
def __init__(self, target, weight):
super(StyleLoss, self).__init__()
self.target = target.detach() * weight
self.weight = weight
self.gram = GramMatrix()
self.criterion = nn.MSELoss()

def forward(self, inputs):
self.output = inputs.clone()
self.G = self.gram(inputs)
self.G.mul_(self.weight)
self.loss = self.criterion(self.G, self.target)
return self.output

def backward(self, retain_variables=True):
self.loss.backward(retain_variables=retain_variables)
return self.loss

##### 模型搭建

def get_model_and_losses(style_img, content_img,
style_weight=1000, content_weight=1):

content_layers = ['conv_4']
style_layers = ['conv_1', 'conv_2', 'conv_3', 'conv_4', 'conv_5']

use_cuda = torch.cuda.is_available()

cnn = models.vgg19(pretrained=True).features
if use_cuda:
cnn = cnn.cuda()
cnn = copy.deepcopy(cnn)

content_losses = []
style_losses = []

model = nn.Sequential()
gram = GramMatrix()

if use_cuda:
model = model.cuda()
gram = gram.cuda()

i = 1
for layer in list(cnn):
if isinstance(layer, nn.Conv2d):
name = "conv_" + str(i)

if name in content_layers:
target = model(content_img).clone()
content_loss = ContentLoss(target, content_weight)
content_losses.append(content_loss)

if name in style_layers:
target_feature = model(style_img).clone()
target_feature_gram = gram(target_feature)
style_loss = StyleLoss(target_feature_gram, style_weight)
style_losses.append(style_loss)

if isinstance(layer, nn.ReLU):
name = "relu_" + str(i)

if name in content_layers:
target = model(content_img).clone()
content_loss = ContentLoss(target, content_weight)
content_losses.append(content_loss)

if name in style_layers:
target_feature = model(style_img).clone()
target_feature_gram = gram(target_feature)
style_loss = StyleLoss(target_feature_gram, style_weight)
style_losses.append(style_loss)

i += 1

if isinstance(layer, nn.MaxPool2d):
name = "pool_" + str(i)

return model, style_losses, content_losses

##### 模型训练

def get_input_param_optimizer(input_img):
# this line to show that input is a parameter that requires a gradient
input_param = nn.Parameter(input_img.data)
optimizer = optim.LBFGS([input_param])
return input_param, optimizer

def train():
...
model, style_losses, content_losses = get_model_and_losses(style_img, content_img, style_weight, content_weight)
input_param, optimizer = get_input_param_optimizer(input_img)

print('Optimizing..')
run = [0]
while run[0] <= num_steps:

def closure():
# correct the values of updated input image
input_param.data.clamp_(0, 1)

model(input_param)
style_score = 0
content_score = 0

for sl in style_losses:
style_score += sl.backward()
for cl in content_losses:
content_score += cl.backward()

run[0] += 1
if run[0] % 50 == 0:
print("run {}:".format(run))
print('Style Loss : {:4f} Content Loss: {:4f}'.format(
style_score.data[0], content_score.data[0]))
print()

return style_score + style_score

optimizer.step(closure)
input_param.data.clamp_(0, 1)
output = input_param.data


### 效果图

• 莫奈（900 次迭代的结果）

001.png

• 毕加索（900 次迭代的结果）

002.png

• 毕加索（300 次迭代的结果）

003.png

qrcode_card.png

machine-learning