본문 바로가기

취업준비/인공지능

[AI Tech] 5주차 24차시 프로젝트 8

24차시: 프로젝트 8

[ 프로젝트 8 ] 2 레이어 인공신경망 구현하기 (2)

1. 목표

v 손실함수 만들기

v 순방향 연산 구하기

v 역방향 그레디언트 계산하기

 

2. 프로젝트

코드 구현: 손실함수 만들기

# 모델 세팅
import numpy as np # numpy import
X = np.array([0, 0, 1, 1, 0, 1, 0, 1]).reshape(2,4) # 입력
Y = np.array([0, 1, 1, 0]).reshape(1,4) # 정답

print(X)
print(Y)

# 가중치 초기화 함수
def init_parameters(num_hidden_units = 2):
  W1 = np.random.randn(2, num_hidden_units) # 첫번째 레이어 가중치
  B1 = np.zeros((num_hidden_units,1)) # 첫번째 레이어 바이어스
  W2 = np.random.randn(num_hidden_units, 1) # 두번째 레이어 가중치
  B2 = np.zeros((1, 1)) # 두번째 레이어의 바이어스
  return W1, B1, W2, B2 # 가중치 파라미터 리턴

# 포워딩 연산 : 어파인 변환 연산과 시그모이드 함수

# (프로젝트 7) 미션 코드 작성 : Affine transform을 구현하세요.
def affine(W, X, B):
  return W.T@X+B # '...'에 코드를 채워주세요.

# (프로젝트 7) 미션 코드 작성 : sigmoid function을 구현하세요.
def sigmoid(z):
  return 1/(1+np.exp(-z)) # '...'에 코드를 채워주세요.



# 임의로 w, b, x를 만들고 affine 함수를 테스트 해봅니다
w = np.arange(4).reshape(2,2)
b = [[1],[2]]
x = [[1],[1]]

print(affine(w, x, b)) # affine test -> [[3],[6]]
print(sigmoid(0.1)) # sigmoide test -> 0.524979
## 코드시작 ##
# 미션 1 코드 작성 : 손실함수(이진 크로스 엔트로피)를 구현하세요.
def binary_cross_entropy(Y, YHat):
  N = Y.shape[1] # 총 샘플의 수
  loss = ... # '...'에 코드를 채워주세요
  return loss

## 코드종료 ##
# 정답 확인
Y = np.array([0, 1, 1, 0]).reshape(1, 4) # 정답
YHat = np.array([0.5, 0.5, 0.5, 0.5]).reshape(1, 4) # 추정값

loss = binary_cross_entropy(Y, YHat)
print("2진 크로스엔트로피 비용:", loss)
더보기

정답

## 코드시작 ##
# 미션 1 코드 작성 : 손실함수(이진 크로스 엔트로피)를 구현하세요.
def binary_cross_entropy(Y, YHat):
  N = Y.shape[1] # 총 샘플의 수
  loss = -1/N*(Y*np.log(YHat)+(1-Y)*np.log(1-YHat)).sum() # '...'에 코드를 채워주세요
  return loss

## 코드종료 ##

 

코드 구현: 순방향 연산 구현하기

## 코드시작 ##
# 미션 2~4 코드 작성 : 2레이어 순방향 연산을 구현해보세요.
def forward_loss(X, Y, _params):
  W1, B1, W2, B2 = _params

  # 첫번째 레이어연산
  Z1 = ... # 1) affine 함수  - '...'에 채워주세요
  H = ... # 2) sigmoid 함수 - '...'에 채워주세요

  # 두번째 레이어 연산
  Z2 = ... # 3) affine 함수 - '...'에 채워주세요
  YHat = ... # 4) sigmoid 함수  - '...'에 채워주세요

  # 손실함수 계산
  loss = ... # 5) 이진크로스 엔트로피 함수 - '...'에 채워주세요

  return Z1, H, Z2, YHat, loss

## 코드종료 ##
np.random.seed(42) # random seed로 고정
W1, B1, W2, B2 = init_parameters(num_hidden_units = 2) # 파라미터 초기화
forward_loss(X, Y, [W1, B1, W2, B2])[-1] # loss출력 : 0.70492209
더보기

정답

## 코드시작 ##
# 미션 2~4 코드 작성 : 2레이어 순방향 연산을 구현해보세요.
def forward_loss(X, Y, _params):
  W1, B1, W2, B2 = _params

  # 첫번째 레이어연산
  Z1 = affine(W1,X,B1) # 1) affine 함수  - '...'에 채워주세요
  H = sigmoid(Z1) # 2) sigmoid 함수 - '...'에 채워주세요

  # 두번째 레이어 연산
  Z2 = affine(W2,H,B2) # 3) affine 함수 - '...'에 채워주세요
  YHat = sigmoid(Z2) # 4) sigmoid 함수  - '...'에 채워주세요

  # 손실함수 계산
  loss = binary_cross_entropy(Y, YHat) # 5) 이진크로스 엔트로피 함수 - '...'에 채워주세요

  return Z1, H, Z2, YHat, loss

## 코드종료 ##

 

코드 구현: 역방향 그레디언트 계산하기

## 코드시작 ##
# 미션 5~9 코드 작성 : 역방향 그레디언트를 구현하세요.

def get_gradients(X, Y, _params):
  W1, B1, W2, B2 = _params
  m = X.shape[1] # 샘플의 수
  # 포워드 함수 통과 후 출력
    # - Z1 : 첫번재 레이어 affine 결과
    # - H : 첫번재 레이어 sigmoid 통과한 결과
    # - Z2 : 두번재 레이어 affine 통과한 결과
    # - YHat : 두번재 레이어 sigmoid 통과한 결과
    # - loss : 크로스엔트로피 손실값
  Z1, H, Z2, YHat, loss = forward_loss(X, Y, _params)

  # 1) dLoss/dZ2 구현. 손실함수가 각 샘플 손실의 평균으로 계산되기 때문에 그대로 구현하였습니다.
  dLdZ2 = (1/m)*(YHat-Y) # 그림에서 1의 구현

  # 2) dLoss/dW2의 구현 - '...'을 구현하세요.
  dLdW2 = ... # 그림에서 2의 구현 (초록색 2번 참고)

  # 3) dLoss/dB2의 구현 - 샘플마다 gradient가 있음. 따라서 합쳐줘야 함.
  dLdB2 = np.sum(dLdZ2, axis=1, keepdims=True) # 그림에서 3의 구현

  # 4) dLoss/dH의 구현 - '...'을 구현하세요.
  dLdH = ... #  그림에서 4의 구현

  # 5) dLoss/dZ1의 구현 - '...'을 구현하세요.
  dLdZ1 = ... # 그림에서 5의 구현

  # 6) dLoss/dW1의 구현 - '...'을 구현하세요.
  dLdW1 = ... # 그림에서 6의 구현

  # 7) dLoss/dB2의 구현 - '...'을 구현하세요.
  dLdB1 = ...

  return [dLdW1, dLdB1, dLdW2, dLdB2], loss
## 코드종료 ##
# 모델 학습하기
def optimize (X, Y, _params, learning_rate = 0.1, iteration = 1000):

    params = np.copy(_params) # 파라미터 복사
    loss_trace = [] # 손실 값 저장

    for epoch in range(iteration): # 학습 반복
        dparams, loss = get_gradients(X, Y, params) # 그레디언트 추출
        for param, dparam in zip(params, dparams):
            param += - learning_rate * dparam # 경사하강법 구현

        if (epoch % 100 == 0): # 손실값 저장
            loss_trace.append(loss)

    _, _, _, Y_hat_predict, _ = forward_loss(X, Y, params) # 학습된 모델로 추론

    return params,loss_trace, Y_hat_predict


X = np.array([0, 0, 1, 1, 0, 1, 0, 1]).reshape(2,4) # 입력
Y = np.array([0, 1, 1, 0]).reshape(1,4) # 정답

params = init_parameters(2) # 파라미터 세팅
new_params, loss_trace, Y_hat_predict = optimize(X, Y, params, 0.1, 150000) # 학습 및 추론

print(Y_hat_predict) # 정답 Y와 유사한 값이 나왔다면 학습이 잘 진행된 것 입니다.
# 손실함수 값 출력하기
import matplotlib.pyplot as plt

# Plot learning curve (with costs)
plt.plot(loss_trace)
plt.ylabel('loss')
plt.xlabel('iterations (per hundreds)')
plt.show()
더보기

정답

## 코드시작 ##
# 미션 5~9 코드 작성 : 역방향 그레디언트를 구현하세요.

def get_gradients(X, Y, _params):
  W1, B1, W2, B2 = _params
  m = X.shape[1] # 샘플의 수
  # 포워드 함수 통과 후 출력
    # - Z1 : 첫번재 레이어 affine 결과
    # - H : 첫번재 레이어 sigmoid 통과한 결과
    # - Z2 : 두번재 레이어 affine 통과한 결과
    # - YHat : 두번재 레이어 sigmoid 통과한 결과
    # - loss : 크로스엔트로피 손실값
  Z1, H, Z2, YHat, loss = forward_loss(X, Y, _params)

  # 1) dLoss/dZ2 구현. 손실함수가 각 샘플 손실의 평균으로 계산되기 때문에 그대로 구현하였습니다.
  dLdZ2 = (1/m)*(YHat-Y) # 그림에서 1의 구현

  # 2) dLoss/dW2의 구현 - '...'을 구현하세요.
  dLdW2 = H@dLdZ2.T # 그림에서 2의 구현 (초록색 2번 참고)

  # 3) dLoss/dB2의 구현 - 샘플마다 gradient가 있음. 따라서 합쳐줘야 함.
  dLdB2 = np.sum(dLdZ2, axis=1, keepdims=True) # 그림에서 3의 구현

  # 4) dLoss/dH의 구현 - '...'을 구현하세요.
  dLdH = W2@dLdZ2 #  그림에서 4의 구현

  # 5) dLoss/dZ1의 구현 - '...'을 구현하세요.
  dLdZ1 = dLdH*H*(np.ones((2,1))-H) # 그림에서 5의 구현

  # 6) dLoss/dW1의 구현 - '...'을 구현하세요.
  dLdW1 = X@dLdZ1.T# 그림에서 6의 구현

  # 7) dLoss/dB2의 구현 - '...'을 구현하세요.
  dLdB1 = np.sum(dLdZ1)

  return [dLdW1, dLdB1, dLdW2, dLdB2], loss
## 코드종료 ##

 

cf)

dLdB1 = np.sum(dLdZ1)

은 스칼라를 결과로 출력한 것이고,

dLdB1 = np.sum(dLdZ1, axis=1, keepdims=True)

는 벡터를 결과로 뽑아낸 것이다.

정답 코드에서는 스칼라를 결과로 사용하였기에 원래 나와야 하는 값과 차이가 많이 발생하게 되었다.

만약, 코드를 벡터를 결과로 뽑아내도록 수정한다면 더 좋은 결과를 얻을 수 있을 것이다.

 

 

 

 

© NAVER Connect Foundation. All Rights Reserved