Torch7入门续集(八)---终结篇----不再写Torch博客了,反正就是难受
2017-04-20 17:14
267 查看
这是终篇,记录一些坑吧以及易错点之类的吧,以前也记录了一点儿。
Torch代码书写时可能碰到的一些问题
有些重要的值得注意的地方,会慢慢加到这里,作为参考。
第一次更新:2017-4-20
第二次更新:2017-4-21
第三次更新:2017-4-24 :增加mask操作的三维示例
第四次更新: 2017-10-31: 增加“attempt to index field ‘THNN’ (a nil value)”解决。
输出
因此不能直接return tmp 或是return gradOutput。否则网络的输出实际上是0!即使是在updateGradInput直接返回gradOutput,或是updateOutput直接输出input, 都必须要转换为self.gradInput 和 self.output。值得注意的是,返回值也可以是多个的。比如
但是有时候你看见
这就是三元运算符,类似C中的 k = a ? b : c
用
用
有时候我们希望在tensor的某些位置提取出来,这些位置不要求是矩形,可以是任意形状。一般是用让一个mask。要求这个mask必须只能是填充
此时output2的值又变成随机的了
所以正确写法:
另外,还有就是这一层的权值信息
[b]调用网络backward函数时,也会自动调用每一层的
显然,每一层不就是主要三个功能:“更新”输出(updateOutput), “更新”输入的梯度(updateGradInput),“计算”本层的参数梯度(accGradParameters)。
其实就是按照英文来翻译,就是最精确的意思。如果我们自定义的层没有参数或是只是简单的用已有的层组合形成的,没有什么特殊的要求,那么就没必要
不过一般我们用的是
另外,
总结:只有调用updateParameters后,权值才更新。调用backward只是计算每一层的gradInput和每层参数的梯度,当然如今大家都用optim包了,所以一般也不会手动调用
再次强调:backward只是调用每一层的 updateGradInput以及accGradParameters,并不会更新权值参数,只是计算参数的梯度,以及更新每一层的输入的梯度。
updateGradInput还会调用accGradParameters, 那么更改了D的约束,这就会出现问题。所以只用updateGradInput会更加符合。
Torch代码书写时可能碰到的一些问题
有些重要的值得注意的地方,会慢慢加到这里,作为参考。
第一次更新:2017-4-20
第二次更新:2017-4-21
第三次更新:2017-4-24 :增加mask操作的三维示例
第四次更新: 2017-10-31: 增加“attempt to index field ‘THNN’ (a nil value)”解决。
1. 自定义层必须return self.output或是self.gradInput形式。
function CustomizedLayer:updateOutput(input) print('updateGradInput'..self.cls) -- other code and get tmp self.output = tmp return self.output end function CustomizedLayer:updateGradInput(input, gradOutput) print('updateGradInput'..self.cls) self.gradInput = gradOutput return self.gradInput end
输出
updateOutput..output 208554.015625 #然而在net:forward(input)后,得到的 ..latent after customizedLayer: 0
因此不能直接return tmp 或是return gradOutput。否则网络的输出实际上是0!即使是在updateGradInput直接返回gradOutput,或是updateOutput直接输出input, 都必须要转换为self.gradInput 和 self.output。值得注意的是,返回值也可以是多个的。比如
... function cLayer:updateOutput(input) ... return self.ouput, self.k end
2. lua的三元运算符
lua默认参数当然很简单function myFun(a) p = a or 1 -- 用or来 end
但是有时候你看见
k = a and b or c
这就是三元运算符,类似C中的 k = a ? b : c
a = 1 b = a == 1 and 2 or 0 --输出2 a = 3 b = a == 1 and 2 or 0 --输出0
用[]
进行Tensor取值补充
a = torch.Tensor(6,3,4,5) --下面4种写法等价 b = a[{{1},{1,3},{1,4},{1,5}] -- 1*3*4*5 b = a[{{1},{},{},{}] -- 1*3*4*5 b = a[1] -- 取第一行 3*4*5 会减维
用[ByteTensor]
进行mask操作
有时候我们希望在tensor的某些位置提取出来,这些位置不要求是矩形,可以是任意形状。一般是用让一个mask。要求这个mask必须只能是填充0或1的ByteTensor类型。mask的某个位置为1,那么被mask的tensor的对应位置的值被置成自定义数字.
-- 用bernoulli()可以设置只有0和1的tensor -- b = torch.ByteTensor(3,4):bernoulli() b = torch.Tensor(3,4):apply(function() if i > 0 then i = i - 1 else i = i + 1 end return i end) --[[ b 1 0 1 0 1 0 1 0 1 0 1 0 [torch.DoubleTensor of size 3x4] ]] b = b:byte() a = torch.Tensor(2,3,4) -- 用来进行mask a[1][b] = 3 --[[ th>a (1,.,.) = 3.0000e+00 6.9521e-310 3.0000e+00 0.0000e+00 3.0000e+00 7.8762e-114 3.0000e+00 3.6017e+227 3.0000e+00 4.6197e+281 3.0000e+00 8.2678e+140 (2,.,.) = 7.0981e+194 7.4861e-114 4.0622e-66 7.5656e-307 1.2946e+214 1.0740e-152 9.0870e+223 2.1724e-153 1.3085e+180 2.2462e-57 2.1724e-153 1.3085e+180 [torch.DoubleTensor of size 2x3x4] ]] -- 三维示例 a = torch.rand(2,3,4):mul(4):floor():int() th> a (1,.,.) = 0 0 3 3 3 2 2 0 3 0 3 2 (2,.,.) = 1 2 3 3 3 3 3 3 0 3 2 3 [torch.IntTensor of size 2x3x4] mask = torch.Tensor(2,3,4):bernoulli():byte() th> mask (1,.,.) = 0 1 0 1 1 0 1 1 0 0 1 1 (2,.,.) = 0 1 0 1 0 1 0 1 0 0 1 1 [torch.ByteTensor of size 2x3x4] th> a[mask] = 0 -- 相应位置置为0 [0.0000s] th> a (1,.,.) = 0 0 3 0 0 2 0 0 3 0 0 0 (2,.,.) = 1 0 3 0 3 0 3 0 0 3 0 0 [torch.DoubleTensor of size 2x3x4]
一些小细节
Tensor构造时,“赋值操作”最好放在最后
local input = torch.Tensor(3,4) local output2 = torch.Tensor():zero():typeAs(input):resizeAs(input)
此时output2的值又变成随机的了
output2 6.9316e-310 6.9316e-310 0.0000e+00 0.0000e+00 0.0000e+00 2.1724e-153 5.4104e-67 8.0109e-307 8.4880e-314 1.0748e+160 2.1724e-153 9.5896e-308 [torch.DoubleTensor of size 3x4]
所以正确写法:
local input = torch.Tensor(3,4) local output2 = torch.Tensor():typeAs(input):resizeAs(input):zero()
获得网络的某一层的信息
其实module类中有两个状态变量:output和
gradInput, 所以
net.modules[i].output net.modules[i].gradInput
另外,还有就是这一层的权值信息
net.modules[i].weight net.modules[i].bias net.modules[i].gradWeight net.modules[i].gradBias
attempt to index field ‘THNN’ (a nil value)
我不想说什么。。其实就是你忘了加require 'cunn'
再次说说网络更新权值的方式
常用函数
自定义层一般是重载__init__,
updateOuput和
updateGradInput三个。调用网络forward函数时,其会自动调用每一层的
updateOutput函数。
[b]调用网络backward函数时,也会自动调用每一层的
updateGradInput(input,gradOutput)和
accGradParameters(input,gradOuput,scale)。
function Module:backward(input, gradOutput, scale) scale = scale or 1 self:updateGradInput(input, gradOutput) self:accGradParameters(input, gradOutput, scale) return self.gradInput end
显然,每一层不就是主要三个功能:“更新”输出(updateOutput), “更新”输入的梯度(updateGradInput),“计算”本层的参数梯度(accGradParameters)。
其实就是按照英文来翻译,就是最精确的意思。如果我们自定义的层没有参数或是只是简单的用已有的层组合形成的,没有什么特殊的要求,那么就没必要
accGradParameters了,会自动调用内部的这个函数实现计算。
updateParameters(learningRate)
accGradParameters计算好梯度后,我们需要更新一下本层的参数,此时调用
updateParameters(learningRate)就可以了。可以看到updateParameters(learningRate)就是先用
parameters()获得内部的参数以及参数的梯度。然后对每个参数进行更新。
function Module:updateParameters(learningRate) local params, gradParams = self:parameters() if params then for i=1,#params do params[i]:add(-learningRate, gradParams[i]) end end end
不过一般我们用的是
getParameters,返回的是经过扁平的参数。
function Module:getParameters() -- get parameters local parameters,gradParameters = self:parameters() local p, g = Module.flatten(parameters), Module.flatten(gradParameters) assert(p:nElement() == g:nElement(), 'check that you are sharing parameters and gradParameters') if parameters then for i=1,#parameters do assert(parameters[i]:storageOffset() == gradParameters[i]:storageOffset(), 'misaligned parameter at ' .. tostring(i)) end end return p, g end
另外,
zeroGradParameters()可以置零参数的梯度。
function Module:zeroGradParameters() local _,gradParams = self:parameters() if gradParams then for i=1,#gradParams do gradParams[i]:zero() end end end
总结:只有调用updateParameters后,权值才更新。调用backward只是计算每一层的gradInput和每层参数的梯度,当然如今大家都用optim包了,所以一般也不会手动调用
updateParameters。,至于 optim包如何使用,参照 http://blog.csdn.net/hungryof/article/details/66970563
再次强调:backward只是调用每一层的 updateGradInput以及accGradParameters,并不会更新权值参数,只是计算参数的梯度,以及更新每一层的输入的梯度。
local fDx = function(x) netD:apply(function(m) if torch.type(m):find('Convolution') then m.bias:zero() end end) netG:apply(function(m) if torch.type(m):find('Convolution') then m.bias:zero() end end) gradParametersD:zero() -- Real -- train netD with (real, real_label) local output = netD:forward(real_AB) local label = torch.FloatTensor(output:size()):fill(real_label) if opt.gpu>0 then label = label:cuda() end local errD_real = criterion:forward(output, label) local df_do = criterion:backward(output, label) -- 这里对针对real的图,计算netD的网络的输入的梯度,以及网络参数的梯度。 netD:backward(real_AB, df_do) -- Fake -- train netD with (fake_AB, fake_label) local output = netD:forward(fake_AB) label:fill(fake_label) local errD_fake = criterion:forward(output, label) local df_do = criterion:backward(output, label) -- 这里的netD的参数没有置0,所以梯度是累加! -- 当netD经过real和fake的计算后,梯度“中和”后,再将我们需要优化 -- 的网络的参数的梯度 gradParametersD输出。 netD:backward(fake_AB, df_do) errD = (errD_real + errD_fake)/2 return errD, gradParametersD end
local fGx = function(x) netD:apply(function(m) if torch.type(m):find('Convolution') then m.bias:zero() end end) netG:apply(function(m) if torch.type(m):find('Convolution') then m.bias:zero() end end) gradParametersG:zero() -- GAN loss local df_dg = torch.zeros(fake_B:size()) if opt.gpu>0 then df_dg = df_dg:cuda(); end if opt.use_GAN==1 then local output = netD.output -- netD:forward{input_A,input_B} was already executed in fDx, so save computation local label = torch.FloatTensor(output:size()):fill(real_label) -- fake labels are real for generator cost if opt.gpu>0 then label = label:cuda(); end errG = criterion:forward(output, label) local df_do = criterion:backward(output, label) -- 你可能要问,为什么这里用的是updateGradInput而不是backward呢? df_dg = netD:updateGradInput(fake_AB, df_do):narrow(2,fake_AB:size(2)-output_nc+1, output_nc) else errG = 0 end -- unary loss local df_do_AE = torch.zeros(fake_B:size()) if opt.gpu>0 then df_do_AE = df_do_AE:cuda(); end if opt.use_L1==1 then errL1 = criterionAE:forward(fake_B, real_B) df_do_AE = criterionAE:backward(fake_B, real_B) else errL1 = 0 end netG:backward(real_A, df_dg + df_do_AE:mul(opt.lambda)) return errG, gradParametersG end
回答:– 你可能要问,为什么这里用的是updateGradInput而不是backward呢?
这是因为,这里的G生成的假图的梯度是通过,让D尽量认为假图是真图,这种梯度来更新G。这是D对G的一种作用。但是我们要获得这个梯度,如果调用backward, 那么就会不仅调用updateGradInput还会调用accGradParameters, 那么更改了D的约束,这就会出现问题。所以只用updateGradInput会更加符合。
相关文章推荐
- Torch7入门续集(二)---- 更好的使用Math函数
- Torch7入门续集(四)----利用Table layers构建灵活的网络
- torch入门笔记19:关于一些torch博客的索引
- Torch7入门续集(五)----进一步了解optim
- Torch7入门续集补充(2)--- 每一层设置不同的学习率(finetuning有用)
- Torch7入门续集(三)----Simple Layers的妙用
- Torch7入门续集补充--- nngraph包的使用
- Torch7入门续集(七)--- clone与net替换某一层
- Torch7入门续集(一)----- 更加深入理解Tensor
- Torch7入门续集(六)----多GPU运行程序
- FCKEditor使用说明(Csdn博客就是用的这个编辑器)
- 所谓博客搬家, 其实就是博客备份, 有备才能无患!
- Wss3入门(0):使用Word2007撰写Wss3搭建的博客
- 博客就是一种妓院文化
- 我的新博客http://blog.csdn.net/ExSystem 本博客不再更新,谢谢!
- 声明:不再更新本站博客。
- CSDN博客编辑器就是个傻X
- VC++ MFC入门简明教程,我当时就是看了它一天入门的
- [转?反正就是那首有名的诗]世界上最遥远的距离
- 这就是传说中的博客?