밑바닥부터 시작하는 딥러닝 - 5

Apic·약 11시간 전
0

코딩

목록 보기
27/28

MNIST로 구현하기

MNIST 데이터셋(손글씨 이미지)을 이용하여 추론 과정을 구현한다.(학습 과정 생략)
다른 말로는 신경망의 순전파라고 한다.

데이터셋 불러오기

MNIST 데이터는 28X28 크기의 흑백 이미지(1 채널) 각 픽셀은 0 ~ 255까지 값이 있다.

먼저 데이터를 확인하자

데이터 불러오기

# dataset/mnist.py

# coding: utf-8
try:
    import urllib.request
except ImportError:
    raise ImportError('You should use Python 3.x')
import os.path
import gzip
import pickle
import os
import numpy as np


url_base = 'http://yann.lecun.com/exdb/mnist/'
key_file = {
    'train_img':'train-images-idx3-ubyte.gz',
    'train_label':'train-labels-idx1-ubyte.gz',
    'test_img':'t10k-images-idx3-ubyte.gz',
    'test_label':'t10k-labels-idx1-ubyte.gz'
}

dataset_dir = os.path.dirname(os.path.abspath(__file__))
save_file = dataset_dir + "/mnist.pkl"

train_num = 60000
test_num = 10000
img_dim = (1, 28, 28)
img_size = 784


def _download(file_name):
    file_path = dataset_dir + "/" + file_name
    
    if os.path.exists(file_path):
        return

    print("Downloading " + file_name + " ... ")
    urllib.request.urlretrieve(url_base + file_name, file_path)
    print("Done")
    
def download_mnist():
    for v in key_file.values():
       _download(v)
        
def _load_label(file_name):
    file_path = dataset_dir + "/" + file_name
    
    print("Converting " + file_name + " to NumPy Array ...")
    with gzip.open(file_path, 'rb') as f:
            labels = np.frombuffer(f.read(), np.uint8, offset=8)
    print("Done")
    
    return labels

def _load_img(file_name):
    file_path = dataset_dir + "/" + file_name
    
    print("Converting " + file_name + " to NumPy Array ...")    
    with gzip.open(file_path, 'rb') as f:
            data = np.frombuffer(f.read(), np.uint8, offset=16)
    data = data.reshape(-1, img_size)
    print("Done")
    
    return data
    
def _convert_numpy():
    dataset = {}
    dataset['train_img'] =  _load_img(key_file['train_img'])
    dataset['train_label'] = _load_label(key_file['train_label'])    
    dataset['test_img'] = _load_img(key_file['test_img'])
    dataset['test_label'] = _load_label(key_file['test_label'])
    
    return dataset

def init_mnist():
    download_mnist()
    dataset = _convert_numpy()
    print("Creating pickle file ...")
    with open(save_file, 'wb') as f:
        pickle.dump(dataset, f, -1)
    print("Done!")

def _change_ont_hot_label(X):
    T = np.zeros((X.size, 10))
    for idx, row in enumerate(T):
        row[X[idx]] = 1
        
    return T
    

def load_mnist(normalize=True, flatten=True, one_hot_label=False):
    """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
    -------
    (훈련 이미지, 훈련 레이블), (시험 이미지, 시험 레이블)
    """
    if not os.path.exists(save_file):
        init_mnist()
        
    with open(save_file, 'rb') as f:
        dataset = pickle.load(f)
    
    if normalize:
        for key in ('train_img', 'test_img'):
            dataset[key] = dataset[key].astype(np.float32)
            dataset[key] /= 255.0
            
    if one_hot_label:
        dataset['train_label'] = _change_ont_hot_label(dataset['train_label'])
        dataset['test_label'] = _change_ont_hot_label(dataset['test_label'])    
    
    if not flatten:
         for key in ('train_img', 'test_img'):
            dataset[key] = dataset[key].reshape(-1, 1, 28, 28)

    return (dataset['train_img'], dataset['train_label']), (dataset['test_img'], dataset['test_label']) 


if __name__ == '__main__':
    init_mnist()

데이터 다운로드

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)

print(x_train.shape)
print(t_train.shape)
print(x_test.shape)
print(t_test.shape)
Converting train-images-idx3-ubyte.gz to NumPy Array ...
Done
Converting train-labels-idx1-ubyte.gz to NumPy Array ...
Done
Converting t10k-images-idx3-ubyte.gz to NumPy Array ...
Done
Converting t10k-labels-idx1-ubyte.gz to NumPy Array ...
Done
Creating pickle file ...
Done!
(60000, 784)
(60000,)
(10000, 784)
(10000,)

이제 이 MNIST 데이터를 (훈련 이미지, 훈련 레이블), (시험 이미지, 시험 레이블) 형식으로 변환한다.

인수로는 normalize(정규화), flatten(평탄화), one_hot_label(원 핫 인코딩)으로 설정한다.
3개 모두 bool 형태로 지정한다.

normalize: 입력 이미지의 픽셀값을 0.0 ~ 1.0 사이의 값으로 정규화 한다.
flatten: 입력 이미지를 1차원으로 만든다.
one_hot_label은 원-핫 인코딩 형태로 저장한다.

  • 숫자 4인 경우 [0 0 0 0 1 0 0 0 0 0] 이런 형식으로 지정한다.

이미지 확인하기

import numpy as np
from dataset.mnist import load_mnist
from PIL import Image

# 이미지 새로운 창으로 출력하기
def img_show(img):
    pil_img = Image.fromarray(np.uint8(img))
    pil_img.show()

# 이미지 데이터 분리하기
(x_train, t_train), (x_test, t_test) = load_mnist(flatten=True, normalize=False)
img = x_train[0]    # 첫 번째 이미지 데이터
label = t_train[0]
print(label)

print(img.shape)
img = img.reshape(28, 28)   # 28x28 크기로 변형
img_show(img)

여기서 flatten이 True로 되어있기 때문에 1차원 배열로 나온다.
이것을 원래 이미지 크기에 맞기 28x28 크기로 변형했다.

신경망 추론 처리

이제 신경망을 만들 차례다.
입력층 뉴런을 784개(이미지 크기가 28x28인데 이것을 1차원으로 바꾸면 784개가 나옴), 출력층을 10개(0~9까지)로 설정한다.

그리고 은늑층은 2개로 첫 번째 은닉층은 50개, 2 번째 은닉층은 100개로 정한다.
(은닉층의 뉴런 개수는 임의로 정한다.)

sample_weight.pkl

데이터 불러오기

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

신경망 가져오기

import pickle

def init_network():
    with open("sample_weight.pkl", "rb") as f:
        network = pickle.load(f)

    return network

여기서 sample_weight.pkl에 저장된 '학습된 가눛이 매개변수'를 읽는다.

예측하기

# 시그모이드 함수
def sigmoid(x):
    return 1 / (1 + np.exp(-x))

# 소프트맥스 함수
def softmax(a):
    c = np.max(a)
    exp_a = np.exp(a - c)
    sum_exp_a = np.sum(exp_a)
    y = exp_a / sum_exp_a
    return y

# 예측 함수
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("Accuracy:" + str(float(accuracy_cnt) / len(x)))  # 정확도 출력
# Accuracy:0.9207
  1. 데이터셋을 얻고 네트워크를 생성한다.
  2. for 문을 돌며 x에 저장된 이미지 1장씩 꺼내 pidict 함수로 분류한다.
  3. predict함수는 각 레이블의 확률을 넘파이로 반환한다.
  • [0.1, 0.3, 0.2 ...]
  • 여기서 순서대로 0일 확률, 1일 확률 ... 을 나타낸다.
  1. np.argmax()를 통해 그 중에 가장 높은 원소의 인덱스를 구한다.
  2. 신경망이 예측한 결과와 정답을 비교하여 맞추면 accuracy_cnt를 1씩 상승시켜 정확도를 구한다.

배치 처리

구현한 신경망에서 각 층의 가중치 형상을 출력한다.

x, _ = get_data()
network = init_network()
W1, W2, W3 = network["W1"], network["W2"], network["W3"]

print(x.shape)      # (10000, 784)
print(x[0].shape)   # (784,)
print(W1.shape)     # (784, 50)
print(W2.shape)     # (50, 100)
print(W3.shape)     # (100, 10)

결과를 보면 다음 층으로 갈 때 나오는 차원의 원소 수가 일치하는 것을 볼 수 있다.
1층에서 784개를 입력받고 50개로 출력한다.
2층에서 50개로 입력받고 100개로 출력한다.
3층에서 100개로 입력받고 10개로 출력한다.

입력이 2개인 퍼셉트론

신명망 각 층의 배열 형상의 추이

출처: https://github.com/youbeebee/deeplearning_from_scratch/tree/master

이것은 1장의 이미지에 대한 처리고 만약 100장의 이미지라면 입력층이 달라지게된다.

이것은 100장의 이미지에 대해서 처리할 때를 나타낸 것이다.

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

batch_size = 100
accuracy_cnt = 0
for i in range(0, len(x), batch_size):
    x_batch = x[i:i + batch_size]
    y_batch = predict(network, x_batch)
    p_batch = np.argmax(y_batch, axis=1)  # 각 배치에 대해 가장 확률이 높은 클래스의 인덱스
    accuracy_cnt += np.sum(p_batch == t[i:i + batch_size])  # 올바른 예측의 수를 누적

print("Accuracy: ", accuracy_cnt / len(x))	# Accuracy:  0.9207

방금은 1장씩 처리를 했는데 지금은 100장씩 처리하는 것을 볼 수 있다.

profile
코딩 공부하는 사람

0개의 댓글