파이톨치

[밑바닥부터 시작하는 딥러닝] 신경망 구현하기 본문

AI&ML/밑바닥부터 시작하는 딥러닝

[밑바닥부터 시작하는 딥러닝] 신경망 구현하기

파이톨치 2022. 12. 27. 16:09
728x90

간단한 신경망 구현하기

그림 1. 구현할 신경망

이렇게 생긴 신경망을 구현해봅시다. 우선 가중치는 학습을 해야하지만 우리는 아직 학습을 모른다고 가정하고 하기에 임의의 가중치를 부여하겠습니다. 위의 모양에서 입력이 2개 출력이 2개가 나오는 구조라 아마 2가지 중 한가지로 분류하는 신경망일 것입니다.

 

일단 은닉층에서 사용할 활성함수는 시그모이드 함수를 사용할 것 입니다. 

def sigmoid(x):
  return 1 / (1+np.exp(-x))

출력층은 항등함수를 사용할 것인데, 사실 함수를 만들 필요는 없습니다. y = x 함수니까요.

 

아까 말했듯이, 가중치를 학습하는 방법을 모르니 임의의 가중치를 부여해줄 것 입니다. 이를 함수로 만들면 다음과 같습니다.

def init_network():
  network = {}
  network['W1'] = np.array([[0.1, 0.3, 0.5], [0.2, 0.4, 0.6]])
  network['b1'] = np.array([0.1, 0.2, 0.3])
  network['W2'] = np.array([[0.1, 0.4], [0.2, 0.5], [0.3, 0.6]])
  network['b2'] = np.array([0.1, 0.2])
  network['W3'] = np.array([[0.1, 0.3], [0.2, 0.4]])
  network['b3'] = np.array([0.1, 0.2])
  
  return network

위와 같은 방식으로 화살표가 앞으로 향해 순차적으로 계산하는 것을 순전파라고 부릅니다. 이러한 순전파를 함수로 만들면 다음과 같습니다.

def forward(network, x):
  W1, W2, W3 = network['W1'], network['W2'], network['W3']
  b1, b2, b3 = network['b1'], network['b2'], network['b3']
  a1 = np.dot(x, W1) + b1
  z1 = sigmoid(a1)
  a2 = np.dot(z1, W2) + b2
  z2 = sigmoid(a2)
  a3 = np.dot(z2, W3) + b3
  y = a3 # 항등함수

  return y

이를 잘 보면 "활성함수(행렬의 곱 + 편향)" 의 꼴을 반복해서 하는 것입니다.

network = init_network()
x = np.array([1.0, 0.5])
y = forward(network, x)
y = softmax(y)
print(y)

다음과 같은 코드로 이를 실행해볼 수 있습니다. 마지막 출력층은 softmax로 만들었습니다. 이러한 원리를 이용하여 손글씨를 분류하는 법을 배워봅시다.

 

우선 데이터가 필요합니다. 데이터는 mnist라는 유명한 데이터를 사용합니다. 이 데이터를 불러오는 방법은 복잡하기 때문에 책의 저자가 만들어 두었습니다. 이 파일을 다운 받아 사용하면 됩니다. 

그림 2. mnist 데이터 예시

이를 보면 출력층이 0~9까지라는 것을 알 수 있습니다. 

dataset.zip
11.07MB

여기에 만들어져 있는 함수를 사용하는데 이는 다음과 같이 사용하면 됩니다.

import sys, os
sys.path.append(os.pardir)
from dataset.mnist import load_mnist
(x_train, t_train), (x_test, t_test) = load_mnist(flatten=True, normalize=False)

여기서 load_mnnist라는 함수가 의문인데 dataset에 있는 mnist.py파일을 찾아보면 상세한 내용을 알 수 있습니다. 함수의 간략한 설명은 다음과 같습니다.

"""
MNIST 데이터셋 읽기

Parameters
----------
normalize : 이미지의 픽셀 값을 0.0~1.0 사이의 값으로 정규화할지 정한다.
one_hot_label : one_hot_label이 True면、레이블을 원-핫(one-hot) 배열로 돌려준다. one-hot 배열은 예를 들어 [0,0,1,0,0,0,0,0,0,0]처럼 한 원소만 1인 배열이다.
flatten : 입력 이미지를 1차원 배열로 만들지를 정한다.

Returns
-------
(훈련 이미지, 훈련 레이블), (시험 이미지, 시험 레이블)
"""

 

이제 데이터의 모양을 확인해봅시다.

print(x_train.shape) # (60000, 784)
print(t_train.shape) # (60000,)
print(x_test.shape)  # (10000, 784)
print(t_test.shape)  # (10000,)

이를 보면 60000개의 학습데이터가 있는 것을 알 수 있습니다. 784라는 숫자는 어디서 나온 것일까요? 바로 이미지를 표현한 것입니다. 28 * 28의 픽셀 이미지를 flatten을 통해서 폈기 때문에 784가 된 것입니다. 즉, 60000개의 이미지 데이터가 입력으로 들어가는 것입니다. 참고로 t_train에는 실제로 어떤 데이터인지 정답이 들어있습니다. 

from PIL import Image

def img_show(img):
  pil_img = Image.fromarray(np.uint8(img))
  pil_img.show()

img = x_train[0]
label = t_train[0] # 5
print(label)
print(img.shape)
img=img.reshape(28, 28)
print(img.shape)
img_show(img)

 

이미지를 일자로 만들어서 이미지를 출력하지 못하니까 다시 정사각형으로 만들어줍니다. 그렇게 해서 라벨이 5인 데이터를 확인하면 다음과 같습니다.

그림 3. 라벨 5인 이미지

가중치는 우리가 학습시킬 수 없으니, 이 데이터를 사용해서 신경망을 만들어봅시다.

sample_weight.pkl
0.17MB

from PIL import Image
import pickle

def img_show(img):
  pil_img = Image.fromarray(np.uint8(img))
  pil_img.show()

def get_data():
  (x_train, t_train), (x_test, t_test) = load_mnist(flatten=True, normalize=True, one_hot_label=False)
  return x_test, t_test

def init_network():
  with open("/Users/jhw/Desktop/AI:ML/밑바닥 딥러닝/sample_weight.pkl", 'rb') as f:
    network = pickle.load(f)
  return network

def predict(network, x):
  W1, W2, W3 = network['W1'], network['W2'], network['W3']
  b1, b2, b3 = network['b1'], network['b2'], network['b3']
  a1 = np.dot(x, W1) + b1
  z1 = sigmoid(a1)
  a2 = np.dot(z1, W2) + b2
  z2 = sigmoid(a2)
  a3 = np.dot(z2, W3) + b3
  y = softmax(a3)

  return y
x, t = get_data()
network = init_network()

accuracy_cnt = 0
for i in range(len(x)):
  y = predict(network, x[i])
  p = np.argmax(y)
  if p == t[i]:
    accuracy_cnt += 1
print("정확도: ", str(float(accuracy_cnt / len(x))))

이 코드를 통해 데이터의 정확도를 측정할 수 있습니다. predict함수를 실행하면 어떤 이미지인지 예측하는 확률 값을 가진 배열이 나오는데 여기서 가장 큰 확률을 가진 인덱스가 실제 예측하는 숫자입니다. 무슨 얘기냐면 [0, 0, 0.3, 0.7, 0, 0, 0, 0, 0, 0] 이면 이를 3으로 예측하는 것입니다. 이런 예측이 정답과 맞다면 accuracy_cnt 에 1을 더해주는 형식입니다. 정확도:  0.9352 라는 결과가 나옵니다. 뒤에서 이를 어떻게 높일지 살펴볼 예정이라고 합니다. 

728x90