본문 바로가기
AI/딥러닝 프레임워크 개발

1~5단계) Variable 클래스 , Function 클래스 , 수치 미분 , 역전파 이론

by 채채씨 2021. 5. 10.
728x90
반응형
1. Variable 클래스 구현

함수 __init__에 주어진 인수를 인스턴스 변수에 해당 데이터에 대입하여 다른 곳에서도 접근할 수 있도록 한다.

class Variable:
	def __init__(self, data):
    	self.data = data

예를 들어,

import numpy as np

data = np.array(1.0)
x = Variable(data)

print(x.data) #1.0

 

머신러닝 시스템은 기본 데이터 구조로 다차원 배열을 사용하며 넘파이의 다차원 배열 클래스는 numpy.ndarray이다.

배열의 차원을 확인할 때는 n.dim을 사용할 수 있다. 

import numpy as np

x = np.array(1)
print(x.dim) #0

x = np.array([1, 2, 3])
print(x.dim) #1

x = np.array([[1, 2, 3], [4, 5, 6]])
print(x.dim) #2

 

 

2. Function 클래스 구현

Function 클래스는 Variable 인스턴스를 입력받아 Variable 인스턴스를 출력한다. 

Variable 인스턴스의 실제 데이터는 인스턴스 변수인 data에 있다.

여기서 인수 input은 Variable 인스턴스라고 가정한다. 즉, input = Variable(data)

class Function:
	def __call_(self, input):
    	x = input.data
        y = x ** 2
        output = Variable(y)
        return output

예를 들어,

x = Variable(np.array(10))
f = Function()
y = f(x)

print(type(y)) #<class '__main__.Variable>
#객체의 클래스를 알려주는 함수: type()

print(y.data) #100
#Function 클래스에서 최종적으로 Variable 형태로 돌려서 반환했으므로 위처럼 y.data로 사용

 

 

위의 Function의 용도는 입력값의 제곱을 구하는 함수로 고정되어 있다. 앞으로 Square뿐만 아니라, Sin, Exp 등 다양한 함수가 필요하므로 Function 클래스는 기반 클래스로 모든 함수가 공통적으로 제공하는 기능만 담아두고 구체적인 함수는 Function 클래스를 상속한 클래스에서 구현한다.

class Function:
	def __call__(self, input):
    	x = input.data
        y = self.forward(x) #구체적인 계산은 forward 메서드에서 함
        output = Variable(y)
        return output
        
	def forward(self, x):
		raise NotImplementedError()

raise NotImplementedError()은 forward 함수가 작성되지 않았을 때, '이 메서드는 상속하여 구현해야 한다'는 사실을 알려주기 위해 예외를 발생시킨 것이다.

 

 

■Square 함수 클래스

Function 클래스를 상속하여 입력값을 제곱하는 클래스를 만들어 forward 메서드를 작성하면 다음과 같다.

class Square(Function):
	def forward(self, x):
    	return x**2

 Square 클래스는 Function 클래스를 상속하므로 __call__ 메서드는 그대로 계승되므로 forward 메서드에 구체적인 로직만 작성해 넣으면 된다.

 

아래와 같이 잘 작동한다.

x = Variable(np.array(10))
f = Square()
y = f(x)

print(type(y)) #<class '__main__.Variable'>
print(y.data) #100

 

■Exp 함수 클래스

마찬가지로 Function 클래스를 상속한 클래스로 입력값에 자연로그의 밑을 취하여 반환하는 forward 메서드를 가진다.

import numpy as np

class Exp(Function):
	def forward(self, x):
    	return np.exp(x)

 

■함수 연결

Function 클래스의 __call__ 메서드는 입출력 모두 Variable 인스턴스이므로 위에서 구현한 Square, Exp함수를 연이어(=합성함수 계산 과정으로) 사용할 수 있다. 아래 함수를 계산해볼 것이다.

 

 

A = Square()
B = Exp()
C = Square()

x = Variable(np.array(0.5))
a = A(x)
b = B(a)
c = C(b)

print(y.data) #1.648721270700128

 

 

3. 수치 미분

amber-chaeeunk.tistory.com/11?category=946195

 

딥러닝) 수치 미분 , 해석적 미분 , 편미분

1. 수치 미분 미분이란 한 점에서의 기울기를 의미한다. 기울기는 두 점 사이에서 발생하는 경사인데, 미분을 '한 점에서의 기울기'라고 하는 이유는 그 두 점 사이의 거리를 매우 좁혀서 한 점

amber-chaeeunk.tistory.com

 

중앙차분을 이용하여 수치 미분을 계산하는 함수 numerical_diff(f, x, eps=1e-4)를 구현하면 아래와 같다. 여기서 f는 앞에서 구현한 Function의 인스턴스이고, x는 미분을 계산하는 변수로 Variable 인스턴스이다.

 

def numerical_diff(f, x, eps=1e-4):
	x0 = Variable(x.data - eps)
    x1 = Variable(x.data + eps)
    y0 = f(x0)
    y1 = f(x1)
    return (y1.data - y0.data) / (2 * eps)

 

예시로 Square 클래스를 대상으로 미분해보면 아래와 같다.

f = Square()
x = Variable(np.array(2.0))
dy = numerical_diff(f, x)

print(dy) #4.00000000004

해석적 미분을 한다면 4.0이라는 정확한 값이 나오겠지만, 중앙차분을 이용한 수치미분도 오차가 매우 작다는 것을 확인하였다.

 

아래는 합성 함수 미분에 대한 예시이다.

 

 

def f(x):
	A = Square()
    B = Exp()
    C = Square()
    return C(B(A(x)))
    
x = Variable(np.array(0.5))
dy = numerical_diff(f, x)

print(dy) #3.2974426293330694

결과를 해석하면 x를 0.5로부터 작은 값만큼 변화시키면 y는 그 작은 값의 3.279⋯배 만큼 변한다는 의미이다.

 

 

 

그러나, 수치 미분의 경우는 오차는 작지만, 변수가 매우 많은 계산을 미분할 때는 변수 각각에 대해 미분해야하므로 계산량이 많아진다. 신경망에서는 수백만개 이상의 변수를 사용하기도 하는데 이때 모든 변수에 대해 수치 미분으로 미분하는 것은 현실적이지 않다. 그래서 등장한 것이 역전파이다. 수치 미분은 역전파를 정확하게 구현했는지를 검증(gradient checking)하기 위해 사용된다.

 

 

 

4. 역전파 이론

amber-chaeeunk.tistory.com/18?category=946195

 

딥러닝) 오차 역전파 backpropagation , 연쇄법칙 chain rule , 기울기 효율적으로 구하기

신경망 모델을 최적화 하기 위해서는 손실 함수의 값을 최소화하는 가중치 매개변수를 찾아야 한다. 복잡한 손실 함수에서 더 좋은 가중치를 찾기 위해 경사하강법(Gradient Descent)을 이용하여 가

amber-chaeeunk.tistory.com

 

 

역전파 과정

 

 

 

계산 순서를 출력에서 입력 방향으로 하는 이유는 y의 미분값을 전파하기 위해서이다. 만약 입력에서 출력 방향으로 계산한다면 x가 중요 요소가 되어 x에 대한 미분이 전파된다. 역전파에서 가장 첫 값은 y의 y에 대한 미분이다. y가 변하면 자기 자신인 y도 같은 크기만큼 변하므로 항상 1이된다. 

728x90
반응형

댓글