Lua판 제로로부터 만드는 Deep Learning 그 11[구배의 산출]

과거 기사 요약



Lua판 제로로부터 만드는 Deep Learning 정리

소개



이번은 원서 4장의 경사의 부분을 실장합니다. 이번 부분은 기계 학습보다는 기초적인 설명이므로 날려도 괜찮습니다.
스크립트는 다음과 같습니다.

gradient_2d.lua
require 'gnuplot'

---勾配算出関数.
-- 入力値に対する多変数関数の勾配を求める
-- @param f 多変数関数 (Type:function)
-- @param x 入力値 (Type:Tensor ※1D Tensor)
-- @return 入力値に対する勾配の値 (Type:Tensor)
function _numerical_gradient_no_batch(f, x)
    local h = 1e-4 -- 0.0001
    grad = x:clone():zero()

    for idx = 1, x:size()[1] do
        local tmp_val = x:float()[idx]
        x[idx] = tmp_val + h --一つの要素だけ動かす
        local fxh1 = f(x) -- f(x+h)

        x[idx] = tmp_val - h 
        local fxh2 = f(x) -- f(x-h)
        grad[idx] = (fxh1 - fxh2) / (2*h)

        x[idx] = tmp_val -- 値を元に戻す
    end

    return grad
end

---勾配算出関数.
-- 入力値(複数)に対する多変数関数の勾配を求める
-- @param f 多変数関数 (Type:function)
-- @param x 入力値 (Type:Tensor)
-- @return 入力値に対する勾配の値 (Type:Tensor)
function numerical_gradient(f, X)
    if X:dim() == 1 then
        return _numerical_gradient_no_batch(f, X)
    else
        local grad = X:clone():zero()

        for idx = 1, X:size()[1] do
            grad[idx] = _numerical_gradient_no_batch(f, X[idx]) --1Dずつ勾配を計算
        end

        return grad
    end
end

---(Σxi^2)他変数関数.
-- @param x 入力値 (Type:Tensor)
-- @return 出力値 (Type:Tensor)
function function_2(x)
    if x:dim() == 1 then
        return torch.sum(torch.pow(x,2))
    else
        return torch.sum(torch.pow(x,2), 2)
    end
end

--{3.0, 4.0}での勾配
print(numerical_gradient(function_2, torch.Tensor({3.0,4.0})))
--{0.0, 2.0}での勾配
print(numerical_gradient(function_2, torch.Tensor({0.0,2.0})))
--{3.0, 0.0}での勾配
print(numerical_gradient(function_2, torch.Tensor({3.0,0.0})))


local x0 = torch.range(-2, 2.5, 0.25)
local x1 = torch.range(-2, 2.5, 0.25)

--{x0[i], x1[j]}での各値に対する勾配(gradx,grady)と関数の値(surface)を計算
local gradx = torch.Tensor(x0:size()[1],x1:size()[1]):zero()
local grady = torch.Tensor(x0:size()[1],x1:size()[1]):zero()
local surface = torch.Tensor(x0:size()[1],x1:size()[1]):zero()
for xi = 1, x1:size()[1] do
    for i = 1, x0:size()[1] do
        gradx[{i,xi}] = numerical_gradient(function_2, torch.Tensor({x0[i], x1[xi]}))[1]
        grady[{i,xi}] = numerical_gradient(function_2, torch.Tensor({x0[i], x1[xi]}))[2]
        surface[{i,xi}] = function_2(torch.Tensor({x0[i], x1[xi]}))
    end
end

--グラフ描写 ※x,y軸の目盛りは適当
gnuplot.figure(1)
gnuplot.splot(torch.sqrt(gradx:pow(2)+grady:pow(2))) --勾配の二乗平均
gnuplot.figure(2)
gnuplot.splot(surface) --関数の値

  
실행 결과는 다음과 같습니다.

실행 결과
$ th gradient_2d.lua
 6.0000
 8.0000
[torch.DoubleTensor of size 2]

 0.0000
 4.0000
[torch.DoubleTensor of size 2]

 6.0000
 0.0000
[torch.DoubleTensor of size 2]


torch의 표준으로 따라오는 gnuplot에서는 quiver를 묘사 할 수 없기 때문에 대신 x, y의 기울기의 뿌리 제곱 평균을 산출하고 플롯 해 보았습니다. 다만 죄송합니다만, 수고가의 관계로부터 x, y축의 눈금은 적당으로 하겠습니다. 10 정도가 본래의 원점이라고 생각해 보세요.
 


  다음에 함수의 값에 대한 플롯도 나타냅니다. 이쪽이 알기 쉬울지도 모릅니다.
 


제대로 기울기를 계산할 수 있을 것 같네요.

결론



이번은 이상입니다.

  다음번에는 구배법의 경우를 보고 싶습니다. 드디어 기계 학습인 것 같은 이야기가 되기 때문에 동기가 되겠네요.
 
고맙습니다.

좋은 웹페이지 즐겨찾기