Torch7入门续集(三)----Simple Layers的妙用

总说

先看一段代码,一般来说传入VGG的数据要先经过预处理,无非是先将BGR变成RGB,然后减去整个训练集的平均RGB值。写的最精简的可能是下面这样写。

function preprocess(img)
    local mean_pixel = torch.FloatTensor({103.939, 116.779, 123.68}):type(img:type())
    local perm = torch.LongTensor{3, 2, 1}
    img = img:index(1, perm):mul(255.0)
    mean_pixel = mean_pixel:view(3, 1, 1):expandAs(img)
    img:add(-1, mean_pixel)
    return img
end

然而。。请看下面的这种写法:

function getPreprocessConv()
    local mean_pixel = torch.Tensor({103.939, 116.779, 123.68})
    local conv = nn.SpatialConvolution(3,3, 1,1)
    conv.weight:zero()
    conv.weight[{1,3}] = 255
    conv.weight[{2,2}] = 255
    conv.weight[{3,1}] = 255
    conv.bias = -mean_pixel
    conv.gradBias = nil
    conv.gradWeight = nil
    conv.parameters = function() --[[nop]] end
    conv.accGradParameters = function() --[[nop]] end
    return conv
end

没错,直接用内置的卷积层进行操作!这启发我们一件事情:很多常见操作,我们都可以用内置的已有的各种层进行!而不是自己单独写,这就相当于nn层式编码!。。自创的词,大概知道就行。
Simple Layers就是一些常见的层操作,利用这些函数可以便捷的在某层后面加入各种操作层,这些操作层可以直接进行反向传播。

Simple Layers主要分为4个部分:
带参的Module—-> 基本Tensor变换——->数学Tensor运算——>其他Module
前面两篇写的就是Tensor和Math的,而在这里第二三正是利用这些简单层来替代Tensor变换和Math操作。

Parameterized Modules

  1. Linear
  2. Bilinear
  3. Dropout
  4. Abs
  5. Add
  6. CAdd
  7. Mul
  8. CMul
  9. Euclidean
  10. WeightedEuclidean
  11. Cosine

从名字就可以大概看出这些层的作用,比如Linear就是全连接层。Bilinear就是进行双线性插值。

Dropout

module= nn.Dropout(p)
训练前向:使得输入每个值按照p的概率丢弃,即使得该位置的值为0。同时输出大小的值被放大1/(1-p),主要是用来进行正规化的。
训练反向:被前向丢弃的位置仍旧为0,gradOutputinput都被放大1/(1-p)倍。

module = nn.Dropout()
> module:forward(x)
  0   4   0   0
 10  12   0  16
[torch.DoubleTensor of dimension 2x4]

> module:backward(x, x:clone():fill(1))
 0  2  0  0
 2  2  0  2
[torch.DoubleTensor of dimension 2x4]

evaluation时:直接将输入的东西原封不动传出

> module:evaluate()

> module:forward(x)
 1  2  3  4
 5  6  7  8
[torch.DoubleTensor of dimension 2x4]

Cosine

文档竟然这么简洁,Euclidean ,Cosine用法一个例子都没有。
module=nn.Cosine(inputSize, outputSize)

require "nn"
require "torch"

local t = torch.randn(6):mul(3):floor()
print('input:')
print(t)
local net = nn.Sequential()
modual = nn.Cosine(6,4)
net:add(modual)
local w = torch.randn(4,6):mul(3):floor()
net:get(1).weight = w
print('weight:')
print(net:get(1).weight)
local result = net:forward(t)
print('result:')
print(result)

for i = 1,(#w)[1] do
    print('time:'..i)
    local tm = t:norm()
    local wm = net:get(1).weight[i]:norm()
    print('input'..i..'\tnorm:'..tm)
    print('weight'..i..'\tnorm:'..wm)
    print(torch.dot(t,w[i])/(tm*wm))
    print(result[i])

    assert((torch.dot(t,w[i])/(tm*wm) - result[i]) < 1e-10)
    print('')
end

input:
-2
-5
-4
-2
 0
-1
[torch.DoubleTensor of size 6]

weight:
-2  6 -1 -1  4  1
 5  2  0 -2  4  2
 2  6  1 -4  1  5
 1  0  0 -2 -1 -3
[torch.DoubleTensor of size 4x6]

result:
-0.3866
-0.3497
-0.5433
 0.1826
[torch.DoubleTensor of size 4]

time:1
input1  norm:7.0710678118655
weight1 norm:7.6811457478686
-0.3866413395173
-0.38664133951719

time:2
input2  norm:7.0710678118655
weight2 norm:7.2801098892805
-0.34966291044862
-0.34966291044852

time:3
input3  norm:7.0710678118655
weight3 norm:9.1104335791443
-0.54330536799443
-0.5433053679943

Input是6x1,必须是vector; Cosine层的weight是N*6, 然后每个weight[i]与input进行内积,然后再normalize一下。简单来说就是:

inputDim: a
weightDim: N*a
ouput: N  

基本Tensor变换

Identity

这个是输入什么,直接输出什么。在用ParallelTable或是ConcatTable时特别有用。

-- Returns a network that computes the CxC Gram matrix from inputs
-- of size C x H x W
function GramMatrix()
  local net = nn.Sequential()
  net:add(nn.View(-1):setNumInputDims(2))
  local concat = nn.ConcatTable()
  concat:add(nn.Identity())
  concat:add(nn.Identity())
  net:add(concat)
  net:add(nn.MM(false, true))
  return net
end

Replicate

module = nn.Replicate(nFeature [, dim, ndim])
默认在第一维度进行复制,这个和以前的Tensor操作一样,没有内存的复制,只是将stride置为0了。

> x = torch.linspace(1, 5, 5)
 1
 2
 3
 4
 5
[torch.DoubleTensor of dimension 5]

> m = nn.Replicate(3)
> o = m:forward(x)
 1  2  3  4  5
 1  2  3  4  5
 1  2  3  4  5
[torch.DoubleTensor of dimension 3x5]

Reshape/View

Reshape可以被View代替

> x = torch.Tensor(4, 4)
> for i = 1, 4 do
>    for j = 1, 4 do
>       x[i][j] = (i-1)*4+j
>    end
> end
> print(x)

  1   2   3   4
  5   6   7   8
  9  10  11  12
 13  14  15  16
[torch.Tensor of dimension 4x4]

> print(nn.View(2, 8):forward(x))

  1   2   3   4   5   6   7   8
  9  10  11  12  13  14  15  16
[torch.DoubleTensor of dimension 2x8]

-- size可以是numbers或是LongStorage都行。
> print(nn.View(torch.LongStorage{8,2}):forward(x))

  1   2
  3   4
  5   6
  7   8
  9  10
 11  12
 13  14
 15  16
[torch.DoubleTensor of dimension 8x2]

> print(nn.View(16):forward(x))

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
[torch.DoubleTensor of dimension 16]

不想说什么了,Torch的文档真垃圾!写都写不明白!看的我火大!
View的功能更加强大,其中setNumInputDims()可以指定希望输入的维度的数量,一般是和view(-1)连用,这样可以实现minibatch 的输入

> input = torch.Tensor(2, 3)
> minibatch = torch.Tensor(5, 2, 3)

> m = nn.View(-1):setNumInputDims(2)
> print(#m:forward(input))

 6
[torch.LongStorage of size 1]

> print(#m:forward(minibatch))

 5
 6
[torch.LongStorage of size 2]

重点:
View(-1):setNumInputDims(2)这两个是连用的!
应该这样考虑,
1. 首先获取原始输入的维度,view(-1)是将输入全部变成1维的,相当于1*6.
2. 再根据setNumInputDims(2)知道,要把原始大小为2x3的input的倒数2个维度看成是一个sample。
3. 然后我input被拉成向量后总共就1x6个数,这里把倒数2个维度看成是一个sample,也就是每个sample是6个数,所以input就一个sample,或是说batchsize=1

再看看minibatch,minibatch是5x2x3,setNumInputDims(2)是将2x3看成一个sample,所以,这里有5个sample。输出的大小就变成 5x6。

Unsqueeze/Squeeze

module = nn.Unsqueeze(pos [, numInputDims])
unsqueeze是在pos位置插入1.

pos = 1: 1 x p x q x r
pos = 2: p x 1 x q x r
pos = 3: p x q x 1 x r
pos = 4: p x q x r x 1

一般来说在第一个位置进行Unsqueeze,同时要指定numInputDims才可以实现minibatch

b = 5 -- batch size 5
input = torch.Tensor(b, 2, 4, 3) -- input: b x 2 x 4 x 3
numInputDims = 3 -- input feature map should be the last 3 dims

m = nn.Unsqueeze(4, numInputDims)
m:forward(input) -- output: b x 2 x 4 x 3 x 1

m = nn.Unsqueeze(2):setNumInputDims(numInputDims)
m:forward(input) -- output: b x 2 x 1 x 4 x 3

MM/Clamp等略

你可能感兴趣的:(Lua,Torch7入门教程)