반응형

 

 

안녕하세요~

 

2018년 올해도 벌써 약 3달 남지 않았네요~

 

 

컴퓨터가 2018이라는 필기체 숫자를 인식하려면 어떻게 해야할까요?

 

딥러닝을 공부한다면 꼭 해야하는 코스죠?

 

가장 많은 사랑받고 있는 MNIST 데이터 셋을 이용하여

 

CNN 신경망을 통한 이미지 인식 훈련을 해보도록 하겠습니다!

 


오늘은 python으로 구현한 CNN코드를 통해 알아보겠습니다.

 

CNN 이론은 Part1에 있습니다!

 

 

 

아래의 코드는 크게 3가지 부분으로 나눌 수 있습니다.

 

1. 합성곱 신경망(CNN) class 구현하기

 

2. 훈련 & 테스트 하기

 

3. 그래프 그리기

 

 

 

1. 합성곱 신경망(CNN) class 구현하기

 

CNN 신경망은 단순한 합성곱 신경망으로 아래와 같이 구성하였습니다.

 

CNN신경망_source:everyday-deeplearning

 

각각의 계층에 대해 궁금하시면 이론편을 먼저 보고오세요~

 

(각 계층과 활성함수는 자유롭게 구성할 수 있습니다!)

 

# coding: utf-8
import sys, os

sys.path.append(os.pardir)  # 부모 디렉터리의 파일을 가져올 수 있도록 설정
import pickle
import numpy as np
from collections import OrderedDict
from common.layers import *
from common.gradient import numerical_gradient # 수치미분 함수
import matplotlib.pyplot as plt
from dataset.mnist import load_mnist
from common.trainer import Trainer

class SimpleConvNet:
"""단순한 합성곱 신경망
    conv - relu - pool - affine - relu - affine - softmax
    Parameters    ---------
    input_size : 입력 크기(MNIST의 경우엔784)
    hidden_size_list : 각 은닉층의 뉴런 수를 담은 리스트(e.g. [100, 100, 100])
    output_size : 출력 크기(MNIST의 경우엔10)
    activation : 활성화 함수- 'relu' 혹은'sigmoid'
    weight_init_std : 가중치의 표준편차 지정(e.g. 0.01)
        'relu'나'he'로 지정하면'He 초깃값'으로 설정
        'sigmoid'나'xavier'로 지정하면'Xavier 초깃값'으로 설정
    """

    def __init__(self, input_dim=(1, 28, 28),
                 conv_param={'filter_num': 30, 'filter_size': 5, 'pad': 0, 'stride': 1},
                 hidden_size=100, output_size=10, weight_init_std=0.01):
        filter_num = conv_param['filter_num']
        filter_size = conv_param['filter_size']
        filter_pad = conv_param['pad']
        filter_stride = conv_param['stride']
        input_size = input_dim[1]
        conv_output_size = (input_size - filter_size + 2 * filter_pad) / filter_stride + 1 #conv공식
        pool_output_size = int(filter_num * (conv_output_size / 2) * (conv_output_size / 2)) #pooling공식

        # 가중치 초기화
        self.params = {}
        self.params['W1'] = weight_init_std * np.random.randn(filter_num, input_dim[0], filter_size, filter_size)
        self.params['b1'] = np.zeros(filter_num)
        self.params['W2'] = weight_init_std * np.random.randn(pool_output_size, hidden_size)
        self.params['b2'] = np.zeros(hidden_size)
        self.params['W3'] = weight_init_std * np.random.randn(hidden_size, output_size)
        self.params['b3'] = np.zeros(output_size)

        # 계층 생성
        self.layers = OrderedDict()
        self.layers['Conv1'] = Convolution(self.params['W1'], self.params['b1'], conv_param['stride'], conv_param['pad'])
        self.layers['Relu1'] = Relu()
        self.layers['Pool1'] = Pooling(pool_h=2, pool_w=2, stride=2)
        self.layers['Affine1'] = Affine(self.params['W2'], self.params['b2'])
        self.layers['Relu2'] = Relu()
        self.layers['Affine2'] = Affine(self.params['W3'], self.params['b3'])

        self.last_layer = SoftmaxWithLoss()

    def predict(self, x):
        for layer in self.layers.values():
            x = layer.forward(x)
        return x

    def loss(self, x, t):
        """손실 함수를 구한다
        Parameters
        ---------
        x : 입력 데이터
        t : 정답 레이블
        """
        y = self.predict(x)
        return self.last_layer.forward(y, t)

    def accuracy(self, x, t, batch_size=100):
        if t.ndim != 1: t = np.argmax(t, axis=1)
        acc = 0.0
        for i in range(int(x.shape[0] / batch_size)):
            tx = x[i * batch_size:(i + 1) * batch_size]
            tt = t[i * batch_size:(i + 1) * batch_size]
            y = self.predict(tx)
            y = np.argmax(y, axis=1)
            acc += np.sum(y == tt)

        return acc / x.shape[0]

    def numerical_gradient(self, x, t):
        """기울기를 구한다(수치미분)
        Parameters
        ---------
        x : 입력 데이터
        t : 정답 레이블
        Returns
        ------
        각 층의 기울기를 담은 사전(dictionary) 변수
            grads['W1']、grads['W2']、... 각 층의 가중치
            grads['b1']、grads['b2']、... 각 층의 편향
        """
        loss_w = lambda w: self.loss(x, t)

        grads = {}
        for idx in (1, 2, 3):
            grads['W' + str(idx)] = numerical_gradient(loss_w, self.params['W' + str(idx)])
            grads['b' + str(idx)] = numerical_gradient(loss_w, self.params['b' + str(idx)])

        return grads

    def gradient(self, x, t):
        """기울기를 구한다(오차역전파법)
        Parameters
        ---------
        x : 입력 데이터
        t : 정답 레이블
        Returns
        ------
        각 층의 기울기를 담은 사전(dictionary) 변수
            grads['W1']、grads['W2']、... 각 층의 가중치
            grads['b1']、grads['b2']、... 각 층의 편향
        """
        # forward
        self.loss(x, t)

        # backward
        dout = 1
        dout = self.last_layer.backward(dout)

        layers = list(self.layers.values())
        layers.reverse()
        for layer in layers:
            dout = layer.backward(dout)

        # 결과 저장
        grads = {}
        grads['W1'], grads['b1'] = self.layers['Conv1'].dW, self.layers['Conv1'].db
        grads['W2'], grads['b2'] = self.layers['Affine1'].dW, self.layers['Affine1'].db
        grads['W3'], grads['b3'] = self.layers['Affine2'].dW, self.layers['Affine2'].db

        return grads

    #가중치와 바이너리 저장
    def save_params(self, file_name="params.pkl"):
        params = {}
        for key, val in self.params.items():
            params[key] = val
        with open(file_name, 'wb') as f:
            pickle.dump(params, f)

    #가중치와 바이너리 불러오기
    def load_params(self, file_name="params.pkl"):
        with open(file_name, 'rb') as f:
            params = pickle.load(f)
        for key, val in params.items():
            self.params[key] = val

        for i, key in enumerate(['Conv1', 'Affine1', 'Affine2']):
            self.layers[key].W = self.params['W' + str(i + 1)]
            self.layers[key].b = self.params['b' + str(i + 1)]

 

 

 

2. 훈련 & 테스트 하기

 

위에서 언급했던 것과 같이 아래의 코드는 MNIST 데이터 셋을 이용합니다!

 

 

그렇다면 머신러닝 입문자들은 왜 MNIST 데이터 셋을 이용하여 학습할까요?

 

우선, MNIST 데이터란?

 

필기(손으로 쓰여진 이미지) 숫자들의 그레이스케일 28x28 픽셀 이미지를 보고,

 

0부터 9까지 모든 숫자들에 대해 이미지가 어떤 숫자를 나타내는지 판별하는 것입니다.

 

출처: Tensorflowkorea

 

즉, 이미 가공된 데이터 셋으로 전처리가 크게 필요하지 않기때문에

 

쉽게 훈련시킬 수 있으며 정확도가 높게 나옵니다.

 

 

위에서 만든 합성곱 신경망(CNN)을 이용하여 MNIST 데이터 셋 훈련을 하겠습니다.

 

# 데이터 읽기
(x_train, t_train), (x_test, t_test) = load_mnist(flatten=False)

# 시간이 오래 걸릴 경우 데이터를 줄인다
# x_train, t_train = x_train[:5000], t_train[:5000]
# x_test, t_test = x_test[:1000], t_test[:1000]

max_epochs = 20

network = SimpleConvNet(input_dim=(1, 28, 28), conv_param={'filter_num': 30, 'filter_size': 5, 'pad': 0, 'stride': 1}, hidden_size=100, output_size=10, weight_init_std=0.01)

# 매개변수 보존
network.save_params("params.pkl")
print("Saved Network Parameters!")

# 하이퍼파라미터
iters_num = 10000 # 반복 횟수를 적절히 설정한다
train_size = x_train.shape[0] # 60000 개
batch_size = 100  # 미니배치 크기
learning_rate = 0.1
train_loss_list = []
train_acc_list = []
test_acc_list = []

# 1에폭당 반복 수
iter_per_epoch = max(train_size / batch_size, 1)
print(iter_per_epoch) # 600

for i in range(iters_num): # 10000
    # 미니배치 획득  # 랜덤으로100개씩 뽑아서10000번을 수행하니까 백만번
    batch_mask = np.random.choice(train_size, batch_size) # 100개 씩 뽑아서10000번 백만 번
    x_batch = x_train[batch_mask]
    t_batch = t_train[batch_mask]

    # 기울기 계산
    #grad = network.numerical_gradient(x_batch, t_batch)
    grad = network.gradient(x_batch, t_batch)

    # 매개변수 갱신
    for key in ('W1', 'b1', 'W2', 'b2'):
        network.params[key] -= learning_rate * grad[key]

    # 학습 경과 기록
    loss = network.loss(x_batch, t_batch)
    train_loss_list.append(loss) # cost 가 점점 줄어드는것을 보려고
    # 1에폭당 정확도 계산# 여기는 훈련이 아니라1에폭 되었을때 정확도만 체크

    if i % iter_per_epoch == 0: # 600 번마다 정확도 쌓는다
        print(x_train.shape) # 60000,784
        train_acc = network.accuracy(x_train, t_train)
        test_acc = network.accuracy(x_test, t_test)
        train_acc_list.append(train_acc) # 10000/600 개  16개# 정확도가 점점 올라감
        test_acc_list.append(test_acc)  # 10000/600 개16개# 정확도가 점점 올라감
        print("train acc, test acc | " + str(train_acc) + ", " + str(test_acc))
 

 

 

 

3. 그래프 그리기

 

위에서 만든 train과 test의 정확도를 비교하여

 

overfitting, underfitting이 발생하는지 확인하고

 

신경망의 효율과 정확도를 올리기위해 다양한 파라미터를 조정할 수 있습니다.

 

이 블로그의 포스팅 " 신경망의 효율과 정확도를 올리기위한 방법 "에서 확인할 수 있습니다.

 

# 그래프 그리기
markers = {'train': 'o', 'test': 's'}
x = np.arange(len(train_acc_list))
plt.plot(x, train_acc_list, label='train acc')
plt.plot(x, test_acc_list, label='test acc', linestyle='--')
plt.xlabel("epochs")
plt.ylabel("accuracy")
plt.ylim(0, 1.0)
plt.legend(loc='lower right')
plt.show()

 

코드출처: R과 Python을 활용한 빅데이터 머신러닝 전문가 양성


 

오늘의 포스팅은 여기까지!!

 

궁금한 점은 댓글로 남겨주세요

 

 

Tensorflow2로 구현하는 방법도 포스팅했습니다!

현업에서 많이 사용하는 Python 모듈 | Tensorflow 2

 

현업에서 많이 사용하는 Python 모듈 | Tensorflow 2 구현하기

오늘은 딥러닝 모델을 생성할 때 많이 사용하는 Python 라이브러리 중 Tensorflow의 기본 동작 원리와 코드, Tensorflow 2.0에서는 어떻게 변화하였는지에 대해 살펴보도록 하겠습니다! 텐서플로 1 코드

everyday-deeplearning.tistory.com

 

반응형

+ Recent posts