Torch 7入門続編(八)---終結編---Torchブログはもう書かない、どうせつらい


これは最終編で、いくつかの穴や間違い点などを記録しましょう.以前も少し記録していました.Torchコードを書くときに遭遇する可能性のあるいくつかの問題に注意すべき点があり、ゆっくりとここに加えて参考にします.
  • 初回更新:2017-4-20
  • 第2回更新:2017-4-21
  • 第3更新:2017-4-24:mask操作を追加する3次元例
  • 第4回更新:2017-10-31:「attempt to index field‘THNN’(a nil value)」を追加して解決.

  • 1.カスタムレイヤーはreturn 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を1つ譲るのです.この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)
    --  [b]   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)

    このときoutput 2の値はまたランダムになります
    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クラスには2つの状態変数があります:outputgradInputです.
    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の3つをリロードする.ネットワークforward関数が呼び出されると、各レイヤのupdateOutput関数が自動的に呼び出されます.ネットワーク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

    各レイヤは、主な3つの機能ではないことは明らかです.「更新」出力(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/66970563backwardは、各層の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

    回答:–なぜbackwardではなくupdateGradInputを使っているのかと聞かれるかもしれません.
    これは,ここでGが生成したダミーパターンの勾配は,Dにできるだけダミーパターンが真図であると思わせることによってGを更新するためである.これはD対Gの一種の役割である.しかし、backwardを呼び出すとupdateGradInputだけでなくaccGradParametersも呼び出され、Dの制約が変更され、問題が発生します.だからupdateGradInputだけでもっと合います.