본문 바로가기
Study 공부할레나

[스터디/밑시딥3] 제 4고지 신경망 만들기_43. 신경망

by LENA-cfg 2023. 10. 10.

43.1 DeZero의 linear 함수

아핀 변환은 선형 변환(행렬 곱셈)에 평행이동(벡터 덧셈)을 추가한 변환입니다.

DeZero에서 보면 입력 x의 매개변수 W 사이에서 행렬 곱을 구하고 b를 더해주는 것입니다.

y = F.matmul(x, W) + b로 구현했었습니다.

 

선형 변환은 신경망에서 완전연결계층(fully connected layer)에 해당하고 매개변수 W는 가중치, 매개변수 b는 편향이라고 합니다.

 

선형 변환을 linear 함수로 구현하는데 Function 클래스를 상속하여 새롭게 linear라는 함수 클래스를 구현합니다.

왼쪽 그림은 Dezero의 matmul 함수와 add 함수를 이용한 방식이고 matmul의 출력이 Variable 인슽너스이므로 계산 그래프에 기록됩니다.

계산 그래프가 존재하는동안 Variable 인스턴스, 그안에 데이터가  메모리에 유지됩니다.

 

오른쪽 그림은 Function 클래스를 상속하여 linear 클래스를 구현하는 방식으로

중간 결과가 Variable 인스턴스로 보존되지 않아서 순전파 시 사용한 중간 데이터가 순전파 끝나면 바로 삭제됩니다.

 

메모리 효율은 오른쪽이 좋으나 왼쪽은 메모리 효율을 개선할 수 있는 방법이 있습니다.

 

그 방법은 덧셈 역전파 시 기울기를 단순히 흘려보내면 되고 matmul 역전파는 x W b만 사용해서 t의 데이터를 사용하지 않습니다. 

def linear_simple(x, W, b=None):
    t = matmul(x, W)
    if b is None:
        return t

    y = t + b
    t.data = None  # t 데이터 삭제
    return y

x W 는 Variable 인스턴스 혹은 ndarray 인스턴스일때

matmul 함수(Function 클래스 __call__ 메서드) 안에서 Variable 인스턴스로 변환됩니다.

b=None이면 행렬 곱셈만 계산해서 결과를 반환합니다.

편향이 주어지면 더해주고 t는 역전파때 필요없으니 None으로 처리해 메모리에서 삭제해줍니다.

* 참조 카운트가 0이 되어 파이썬 인터프리터에의해 삭제됩니다.

43.2 비선형 데이터셋

if '__file__' in globals():
    import os, sys
    sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
import numpy as np
import matplotlib.pyplot as plt
from dezero import Variable
import dezero.functions as F


np.random.seed(0)
x = np.random.rand(100, 1)
y = np.sin(2 * np.pi * x) + np.random.rand(100, 1)

plt.figure(figsize=(10, 6))
plt.scatter(x, y, color='blue', marker='o', label='Data Points')
plt.xlabel('x')
plt.ylabel('y')
plt.title('Generated Data and True Function')
plt.legend()
plt.grid(True)
plt.show()

뭔가 익숙한 sin 함수 그래프인데 x,y가 비선형 관계로 해당 데이터는 선형 회귀가 아닌 신경망으로 풀어야합니다.

43.3 활성화 함수와 신경망

신경망은 선형 변환의 출력에 비선형 변환을 수행합니다. 

Relu 함수와 시그모이드 함수가 있습니다. 

 

시그모이드 함수는 0과 1 사이의 값을 반환합니다. 시그모이드의 단점으로 신경망이 깊어지면 그래디언트 소실 문제가 잇습니다. 

def sigmoid_simple(x):
    x = as_variable(x)
    y = 1 / (1 + exp(-x))
    return y

시그모이드는 구현이 쉽습니다. 

 

43.4 신경망 구현

신경망은 선형변환 활성화 함수를 순서대로 적용하고 학습합니다.

학습은 추론을 처리한 후 손실 함수를 추가하고, 손실 함수의 출력을 최소화하는 매개변수를 찾습니다.

if '__file__' in globals():
    import os, sys
    sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
import numpy as np
import matplotlib.pyplot as plt
from dezero import Variable
import dezero.functions as F


np.random.seed(0) # 시드 고정
x = np.random.rand(100, 1)
y = np.sin(2 * np.pi * x) + np.random.rand(100, 1)

# 1. 가중치 초기화
I, H, O = 1, 10, 1
W1 = Variable(0.01 * np.random.randn(I, H))
b1 = Variable(np.zeros(H))
W2 = Variable(0.01 * np.random.randn(H, O))
b2 = Variable(np.zeros(O))

# 2. 신경망 추론
def predict(x):
    y = F.linear(x, W1, b1) # 선형으로 펴주고
    y = F.sigmoid(y) # 시그모이드로 0 또는 1 값 빼주고
    y = F.linear(y, W2, b2) # 시그모이드가 비선형으로 만든거기때문에 다시 선형으로 펴줌
    return y


lr = 0.2
iters = 10000

# 3. 신경망 학습
for i in range(iters):
    y_pred = predict(x)
    loss = F.mean_squared_error(y, y_pred) 

    W1.cleargrad() 
    b1.cleargrad()
    W2.cleargrad()
    b2.cleargrad()
    loss.backward()

    W1.data -= lr * W1.grad.data
    b1.data -= lr * b1.grad.data
    W2.data -= lr * W2.grad.data
    b2.data -= lr * b2.grad.data
    if i % 1000 == 0:
        print(loss)

 

# 1. 가중치 초기화
I, H, O = 1, 10, 1
W1 = Variable(0.01 * np.random.randn(I, H))
b1 = Variable(np.zeros(H))
W2 = Variable(0.01 * np.random.randn(H, O))
b2 = Variable(np.zeros(O))

I는 입력층의 차원 수, H는 은닉층의 차원 수, O는 출력층의 차원 수 

H는 하이퍼 파라미터로 1이상의 정수로 설정할 수 있습니다.

b는 편향인데 0 벡터로 초기화, 가중치는 작은 랜덤값으로 초기화합니다.

 

# 2. 신경망 추론
def predict(x):
    y = F.linear(x, W1, b1) # 선형으로 펴주고
    y = F.sigmoid(y) # 시그모이드로 0 또는 1 값 빼주고
    y = F.linear(y, W2, b2) # 시그모이드가 비선형으로 만든거기때문에 다시 선형으로 펴줌
    return y

신경망 추론은 주석으로 설명을 달아놨습니다.

# 3. 신경망 학습
for i in range(iters):
    y_pred = predict(x)
    loss = F.mean_squared_error(y, y_pred)

    W1.cleargrad()
    b1.cleargrad()
    W2.cleargrad()
    b2.cleargrad()
    loss.backward()

    W1.data -= lr * W1.grad.data
    b1.data -= lr * b1.grad.data
    W2.data -= lr * W2.grad.data
    b2.data -= lr * b2.grad.data
    if i % 1000 == 0:
        print(loss)

 

학습은 iters를 반복하면서 예측값과 정답값의 mse를 구해(로스를 구해) 로스가 줄어드는 방식으로 학습합니다. 

선형 회귀의 구현에 활성화 함수와 선형 변환을 거듭 적용하여 비선형 데이터를 학습하였습니다.