AutoGrad & Loss Function

TEMP·2021년 9월 17일
0

Pytorch

목록 보기
3/11

Loss Function

Train을 시킬려면 기준이 필요하다. 그때 loss function을 사용하는데 이는 작을 수록 좋다. 즉 loss value가 작아지는 방향으로 parameter를 이동시킨다.

그래서 진짜 간단하게 다음과 같은 식을 쓸수있다.

loss  valueloss\space\space value == Lw(output,target)L_{w}(output,target)
outputoutput == model(input)model(input)

이제 여기서 code과 수식사이에서 엄청 헷갈렸다.
예를들어 y=f(x)y=f(x)라는 곡선에서 x=5x=5일때 grad를 구한다고 해보자.
그럼 너무 당연하게 f(5)f'(5)를 계산할 것이다.
여기서 두가지 방법이 있는데 첫번째는 미분의 정의 즉, 극한을 사용하여 계산하는 것이고 두번째는 y=f(x)y=f'(x)라는 기울기 함수를 구한뒤 x=5x=5를 넣는 방식이다.
결론부터 말하면 torch code에서는 숫자를 미분한다.라고 생각하면 편하다.







긴말 필요없고 code를 보자.

import torch
import torch.nn as nn

x=torch.rand(5,5,requires_grad=True)
y=torch.rand(5,5)

def myloss(x,y):
    return torch.sqrt((x-y)*(x-y)).sum()/25
    
loss=myloss(x,y)

print(x.requires_grad)
print(y.requires_grad)
print(loss.requires_grad)
--------------------------------------------------------
True
False
True

Code를 해석해보자.
처음에 x와 y를 모두 상수로 설정해줬는데 x에는 requires_grad=True를 추가했다.
이건 말 그대로 gradient가 필요하다는 말이다.

x,y가 모두 상수이지만 x는 미분이 가능한 변수로 생각하겠다는 것이이다.
여기서 수학을 조금이라도 아는 사람은 다음과 같은 의문이 생길것이다.

'아니 상수를 변수로 본다고? 변수는 일단 문자여야 할텐데? 그래 그렇다 치고 미분을 하겠다고? 숫자를? 그럼 뭘로 미분하는지 상관없이 0나올텐데?'

이 생각이 맞다. 상수를 미분하면 뭐하나 어차피 0나오는데.

그럼 해보자.

loss.backward()

print(x.grad)
print(y.grad)
print(loss.grad)
----------------------------------------------------------------
tensor([[-0.0400,  0.0400, -0.0400,  0.0400,  0.0400],
        [-0.0400, -0.0400,  0.0400,  0.0400,  0.0400],
        [-0.0400,  0.0400, -0.0400, -0.0400,  0.0400],
        [-0.0400,  0.0400,  0.0400, -0.0400,  0.0400],
        [-0.0400, -0.0400,  0.0400,  0.0400, -0.0400]])
None
None

다시 처음부터 이해 해 보자면 x와 y를 둘다 상수로 설정했고 정의한 함수로 둘을 계산 한 다음 그 값을 loss라고 했다.
그리고 loss.backward()를 하여 미분을 했다?
엥? loss는 분명 value이다. 직접 print로 찍으면 분명 값이 나온다.
아니 여기서 .backward()는 미분이고 이게 되네?

그렇다 앞에서 수학적으로 말이 안되어 잘못된 설명이라 했지만 x는 변수가 맞다.
그리고 y는 상수이고 x와 y의 연산 값이 loss도 변수이다.

맨 앞의 수식을 빌려오면 이게 이해가 된다.
loss  valueloss\space\space value == Lx(x,y)L_{x}(x,y)
이 식은 아래첨자 x가 x가 변수임을 뜻하므로 x로 미분이 가능하다.
여기서 왜 헷갈리는지 알 수 있는데 x는 변수이기도 숫자이기도 하다.

x와 y의 차이는 requires_grad=True이다.
즉 딥러닝을 생각해본다면 x는 parameter이다. 그리고 저 x의 값은 초기값 정도로 생각하면 될거 같다.
즉, requires_grad=True이렇게 해주는 순간 x단순한 숫자가 아니라 변수로 바뀐다.
그럼 숫자는 뭘까?
숫자는 처음에는 initial weight정도로 생각 하면 되고 x.grad를 찍어 나온 값은 x와 y로 만들어진 함수를 x로 편미분하여 얻은 gradient라고 생각 하면된다.

더 자세하게 보기위해 다음 code를 보자.

layer=nn.Linear(in_features=10, out_features=3, bias=True) 
for p in layer.parameters(): 
    print(p)

-----------------------------------------------------------
Parameter containing:
tensor([[ 0.1214, -0.2244, -0.0422,  0.0757,  0.0083,  0.0284,  0.1755,  0.2073,
         -0.1873, -0.2041],
        [ 0.2295, -0.0761,  0.0372,  0.2181, -0.2456,  0.0504,  0.0611, -0.0782,
         -0.2651, -0.2106],
        [ 0.2030, -0.0865,  0.3149,  0.0542,  0.1684,  0.1735, -0.1822, -0.1932,
          0.1359,  0.1701]], requires_grad=True)
Parameter containing:
tensor([0.2283, 0.2497, 0.1928], requires_grad=True)

pytorch로 layer를 만들고 parameter를 보면 단순한 initial weight뿐 아니라
requires_grad=True가 있는것을 볼 수 있다.

즉 보통 trainer에서 보면 loss.backward()가 있을텐데 이부분이 가장 헷갈렸던 부분이다.
이유는 다음 내가 직접 겪었던 상황을 알게되면 이해가 될거다.

  1. 논문을 구현하는데 loss function을 직접 만들고 싶었다.
  2. 대부분 loss.backward()으로 미분을 하는데 일단 수학처럼 생각해서 저 loss를 함수라고 생각했다.
  3. 그래서 loss function을 custom하는 것이 당순히 함수만 만들어서 되는 것이 아니고 torch에서 상속을 받아와야 하는 줄 알았다.
  4. 일단 code를 한줄한줄 해석 해 보니 loss가 value임을 알았다.
  5. 위의 auto grad에 대한 설명을 읽고 이것이 왜 value여도 grad를 구할수 있는지 알게 되었다.
  6. 결론은 loss function을 custom할때 단순히 함수만 만들어 주면 된다는 것을 알았다.

결론은 간단하게 정리하자면 nn.layer들을 불러오면 이미 weight가 변수로 설정되어 있어서 loss를 그냥 연산 함수로 만들어 주면 역전파가 잘된다는 것이다.

추가적으로 이거에 대해 엄청 찾아보다 알게된 사실이 있는데
앞서 설명한 부분 때문인지 2018년 정도를 기준으로 Variable이 사라졌다.

Varialbe은 옛날 코드에서 종종 보이는데 보통은 loss에 들어가기전 output과 target을 tensor에서 variable로 바꿔주는데 사용되었고 이를 사용해야했던 이유는 Variable만 grad를 구할 수 있기 때문 이었다.

이후 Variable과 tensor가 합쳐지면서 아무 작접 없이 requires_grad=True를 해놓았다면 어떻한 연산이 추가되는 끝에서 시작되는 역전파가 가능해졌다.

추가적으로 torch.nn.functional은 함수고 torch.nn은 클래스이다. 따라서 trainer에서 들어가는 모양이 조금 다르다.
이는 Pytorch의 train부분에서 설명한다.

0개의 댓글