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

来源:互联网 发布:简阳市环保局何知云 编辑:程序博客网 时间:2024/05/16 13:51

总说

先看一段代码,一般来说传入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 imgend

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

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 convend

没错,直接用内置的卷积层进行操作!这启发我们一件事情:很多常见操作,我们都可以用内置的已有的各种层进行!而不是自己单独写,这就相当于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 = wprint('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('')endinput:-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:1input1  norm:7.0710678118655weight1 norm:7.6811457478686-0.3866413395173-0.38664133951719time:2input2  norm:7.0710678118655weight2 norm:7.2801098892805-0.34966291044862-0.34966291044852time:3input3  norm:7.0710678118655weight3 norm:9.1104335791443-0.54330536799443-0.5433053679943

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

inputDim: aweightDim: N*aouput: N  

基本Tensor变换

Identity

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

-- Returns a network that computes the CxC Gram matrix from inputs-- of size C x H x Wfunction 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 netend

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 rpos = 2: p x 1 x q x rpos = 3: p x q x 1 x rpos = 4: p x q x r x 1

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

b = 5 -- batch size 5input = torch.Tensor(b, 2, 4, 3) -- input: b x 2 x 4 x 3numInputDims = 3 -- input feature map should be the last 3 dimsm = nn.Unsqueeze(4, numInputDims)m:forward(input) -- output: b x 2 x 4 x 3 x 1m = nn.Unsqueeze(2):setNumInputDims(numInputDims)m:forward(input) -- output: b x 2 x 1 x 4 x 3

MM/Clamp等略

0 0