torch7学习(一)——Tensor
Torch7学习(二) —— Torch与Matlab的语法对比
Torch7学习(三)——学习神经网络包的用法(1)
Torch7学习(四)——学习神经网络包的用法(2)
Torch7学习(五)——学习神经网路包的用法(3)
Torch7学习(六)——学习神经网络包的用法(4)——利用optim进行训练
Torch7学习(七)——Neural-Style代码解析
Neural-style用的可能是最容易入门的代码之一吧。比较简单,写的很清晰。只涉及到最简单的网络构建方式,同时也是最基本最重要的写法。直接看neural-style的代码吧。代码附有大量的注释,而在最后也有一定的分析。
值得注意的是,最新的该论文的代码已经是多GPU版本的了。可以指定不同层放入不同的GPU进行训练。,主要是加入了 Controlling Perceptual Factors in Neural Style Transfer代码的支持。个人觉得下面的版本比较简单,看懂了再看最新的版本的吧。https://github.com/jcjohnson/neural-style
代码
require 'torch'
require 'nn'
require 'image'
require 'optim'
require 'loadcaffe'
--------------------------------------------------------------------------------
local cmd = torch.CmdLine()
-- Basic options
cmd:option('-style_image', 'examples/inputs/starry_night.jpg',
'Style target image')
cmd:option('-style_blend_weights', 'nil')
cmd:option('-content_image', 'examples/inputs/5.jpg',
'Content target image')
cmd:option('-image_size', 512, 'Maximum height / width of generated image')
cmd:option('-gpu', 0, 'Zero-indexed ID of the GPU to use; for CPU mode set -gpu = -1')
-- Optimization options
cmd:option('-content_weight', 5e0)
cmd:option('-style_weight', 1e2)
cmd:option('-tv_weight', 1e-3)
cmd:option('-num_iterations', 1000)
cmd:option('-normalize_gradients', false)
cmd:option('-init', 'random', 'random|image')
cmd:option('-optimizer', 'lbfgs', 'lbfgs|adam')
cmd:option('-learning_rate', 1e1)
-- Output options
cmd:option('-print_iter', 50)
cmd:option('-save_iter', 100)
cmd:option('-output_image', 'out.png')
-- Other options
cmd:option('-style_scale', 1.0)
cmd:option('-pooling', 'max', 'max|avg')
cmd:option('-proto_file', 'models/VGG_ILSVRC_19_layers_deploy.prototxt')
cmd:option('-model_file', 'models/VGG_ILSVRC_19_layers.caffemodel')
cmd:option('-backend', 'nn', 'nn|cudnn|clnn')
cmd:option('-cudnn_autotune', false)
cmd:option('-seed', -1)
cmd:option('-content_layers', 'relu4_2', 'layers for content')
cmd:option('-style_layers', 'relu1_1,relu2_1,relu3_1,relu4_1,relu5_1', 'layers for style')
local function main(params)
if params.gpu >= 0 then
if params.backend ~= 'clnn' then
require 'cutorch'
require 'cunn'
cutorch.setDevice(params.gpu + 1)
else
require 'clnn'
require 'cltorch'
cltorch.setDevice(params.gpu + 1)
end
else
params.backend = 'nn'
end
if params.backend == 'cudnn' then
require 'cudnn'
if params.cudnn_autotune then
cudnn.benchmark = true
end
cudnn.SpatialConvolution.accGradParameters = nn.SpatialConvolutionMM.accGradParameters -- ie: nop
end
local loadcaffe_backend = params.backend
if params.backend == 'clnn' then loadcaffe_backend = 'nn' end
local cnn = loadcaffe.load(params.proto_file, params.model_file, loadcaffe_backend):float()
if params.gpu >= 0 then
if params.backend ~= 'clnn' then
cnn:cuda()
else
cnn:cl()
end
end
local content_image = image.load(params.content_image, 3)
content_image = image.scale(content_image, params.image_size, 'bilinear')
local content_image_caffe = preprocess(content_image):float()
local style_size = math.ceil(params.style_scale * params.image_size)
local style_image_list = params.style_image:split(',')
local style_images_caffe = {}
for _, img_path in ipairs(style_image_list) do
local img = image.load(img_path, 3)
img = image.scale(img, style_size, 'bilinear')
local img_caffe = preprocess(img):float()
table.insert(style_images_caffe, img_caffe)
end
-- Handle style blending weights for multiple style inputs
local style_blend_weights = nil
if params.style_blend_weights == 'nil' then
-- Style blending not specified, so use equal weighting
style_blend_weights = {}
-- #表示长度
for i = 1, #style_image_list do
table.insert(style_blend_weights, 1.0)
end
else
style_blend_weights = params.style_blend_weights:split(',')
assert(#style_blend_weights == #style_image_list,
'-style_blend_weights and -style_images must have the same number of elements')
end
-- Normalize the style blending weights so they sum to 1
local style_blend_sum = 0
for i = 1, #style_blend_weights do
style_blend_weights[i] = tonumber(style_blend_weights[i])
style_blend_sum = style_blend_sum + style_blend_weights[i]
end
for i = 1, #style_blend_weights do
style_blend_weights[i] = style_blend_weights[i] / style_blend_sum
end
if params.gpu >= 0 then
if params.backend ~= 'clnn' then
content_image_caffe = content_image_caffe:cuda()
for i = 1, #style_images_caffe do
style_images_caffe[i] = style_images_caffe[i]:cuda()
end
else
content_image_caffe = content_image_caffe:cl()
for i = 1, #style_images_caffe do
style_images_caffe[i] = style_images_caffe[i]:cl()
end
end
end
local content_layers = params.content_layers:split(",")
local style_layers = params.style_layers:split(",")
-- Set up the network, inserting style and content loss modules
local content_losses, style_losses = {}, {}
local next_content_idx, next_style_idx = 1, 1
local net = nn.Sequential()
if params.tv_weight > 0 then
local tv_mod = nn.TVLoss(params.tv_weight):float()
if params.gpu >= 0 then
if params.backend ~= 'clnn' then
tv_mod:cuda()
else
tv_mod:cl()
end
end
net:add(tv_mod)
end
for i = 1, #cnn do
if next_content_idx <= #content_layers or next_style_idx <= #style_layers then
local layer = cnn:get(i)
local name = layer.name
local layer_type = torch.type(layer)
local is_pooling = (layer_type == 'cudnn.SpatialMaxPooling' or layer_type == 'nn.SpatialMaxPooling')
if is_pooling and params.pooling == 'avg' then
assert(layer.padW == 0 and layer.padH == 0)
local kW, kH = layer.kW, layer.kH
local dW, dH = layer.dW, layer.dH
local avg_pool_layer = nn.SpatialAveragePooling(kW, kH, dW, dH):float()
if params.gpu >= 0 then
if params.backend ~= 'clnn' then
avg_pool_layer:cuda()
else
avg_pool_layer:cl()
end
end
local msg = 'Replacing max pooling at layer %d with average pooling'
print(string.format(msg, i))
net:add(avg_pool_layer)
else
-- 如果不是pooling层,直接add这一层
net:add(layer)
end
if name == content_layers[next_content_idx] then
print("Setting up content layer", i, ":", layer.name)
-- 如果这一层是content的话,那么就要加入loss_module,而loss_module则需要:content_weight, target, norm来进行初始化类。
-- target就是“输入”经过前面网络所有层得到的输出。因此target = net:forward(content_image_caffe):clone.
local target = net:forward(content_image_caffe):clone()
local norm = params.normalize_gradients
local loss_module = nn.ContentLoss(params.content_weight, target, norm):float()
if params.gpu >= 0 then
if params.backend ~= 'clnn' then
loss_module:cuda()
else
loss_module:cl()
end
end
net:add(loss_module)
table.insert(content_losses, loss_module)
next_content_idx = next_content_idx + 1
end
if name == style_layers[next_style_idx] then
print("Setting up style layer ", i, ":", layer.name)
local gram = GramMatrix():float()
if params.gpu >= 0 then
if params.backend ~= 'clnn' then
gram = gram:cuda()
else
gram = gram:cl()
end
end
local target = nil
-- style_images_caffe是众多style_images的Tensor组成的table。
-- 因此要将每个style_images_caffe[i]送入net中得到相应的输出。每个输出要经过gram处理,
-- 得到相应的grams值,每张style图片所附的权值得到target
for i = 1, #style_images_caffe do
local target_features = net:forward(style_images_caffe[i]):clone()
local target_i = gram:forward(target_features):clone()
target_i:div(target_features:nElement())
target_i:mul(style_blend_weights[i])
if i == 1 then
target = target_i
else
target:add(target_i)
end
end
l