자자 이제 지금까지 공부했던 합성곱 계층과 풀링 계층을 이용해서 손글씨 숫자를 인식하는 CNN 합성곱 신경망을 본격적으로 구현해보도록 하겠습니다!!!!!!
합성곱 계층과 풀링 계층에 대해 개념적으로 공부하고 싶으신 분들은 아래 포스트를 참고하세요.
2020/07/10 - [Computer Science/Deep Learning] - [비전공자용]합성곱 신경망 (CNN) - 합성곱 계층 & 풀링 계층
아래 구조의 계층으로 구성되어 있는 CNN을 SimpleConvNet이라는 클래스로 구성해봅시다.
입력데이터 ----->
Convolution -> ReLU -> Pooling ----->
Affine -> ReLU ----->
Affine -> Softmax ------> 결과
SimpleConvNet 신경망을 Python으로 구성한 코드를 먼저 보고 세세히 살펴봅시다.
# 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
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
pool_output_size = int(filter_num * (conv_output_size/2) * (conv_output_size/2))
# 가중치 초기화
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)]
predict 메소드는 초기화 때 layers에 추가한 계층을 맨 앞에서부터 차례로 forward 메소드를 호출하며 결과를 다음 계층에 전달합니다.
loss 메소드는 predict 메소드의 결과를 인수로, 마지막 층의 forward 메소드를 호출합니다. 첫 계층부터 마지막 계층까지 forward를 처리합니다.
나머지는 코드의 주석을 자세히 보면 이해가 가리라 생각합니다.
이렇게 합성곱 신경망을 구현했으니 MNIST 데이터셋을 학습해보려고 합니다.
MNIST 데이터셋 학습을 위한 Python 코드입니다. 아래 링크를 통해 이전 포스트 설명을 참고하세요~
2020/07/08 - [Computer Science/Deep Learning] - 오차역전파법 Backpropagation 신경망 구현
# coding: utf-8
import sys, os
sys.path.append(os.pardir) # 부모 디렉터리의 파일을 가져올 수 있도록 설정
import numpy as np
import matplotlib.pyplot as plt
from dataset.mnist import load_mnist
from simple_convnet import SimpleConvNet
from common.trainer import Trainer
# 데이터 읽기
(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)
trainer = Trainer(network, x_train, t_train, x_test, t_test,
epochs=max_epochs, mini_batch_size=100,
optimizer='Adam', optimizer_param={'lr': 0.001},
evaluate_sample_num_per_epoch=1000)
trainer.train()
# 매개변수 보존
network.save_params("params.pkl")
print("Saved Network Parameters!")
# 그래프 그리기
markers = {'train': 'o', 'test': 's'}
x = np.arange(max_epochs)
plt.plot(x, trainer.train_acc_list, marker='o', label='train', markevery=2)
plt.plot(x, trainer.test_acc_list, marker='s', label='test', markevery=2)
plt.xlabel("epochs")
plt.ylabel("accuracy")
plt.ylim(0, 1.0)
plt.legend(loc='lower right')
plt.show()
위 코드를 실행하면 아래의 결과를 얻을 수 있습니다.
훈련 데이터에 대한 정확도는 99%, 시험 데이터에 대한 정확도는 95.83%가 되는 걸 확인할 수 있습니다. 이렇게 간단한 합성곱 신경망으로 학습시킨건데 이 정도 결과면 시험 데이터에 대한 정확도가 굉장히 높은 편이라고 볼 수 있습니다.
소스 코드는 Deep learning from scratch의 github 에서 자세히 확인하실 수 있습니다.
https://github.com/oreilly-japan/deep-learning-from-scratch/
합성곱 신경망 구현을 위해 합성곱 계층과 풀링 계층에 대해 공부하실 분들은 아래 포스트를 참고해주세요~
2020/07/28 - [Computer Science/Deep Learning] - [비전공자용] [Python] CNN(합성곱 신경망) - 풀링 계층 구현
2020/07/28 - [Computer Science/Deep Learning] - [비전공자용] [Python] CNN(합성곱 신경망) - 합성곱 계층 구현
[출처] Deep Learning from Scratch, ゼロ から作る
'Computer Science > Deep Learning' 카테고리의 다른 글
[비전공자용] 대표적인 CNN 네트워크 - LeNet과 AlexNet (0) | 2020.07.29 |
---|---|
[비전공자용] CNN (합성곱 신경망)에서 필터를 적용한다는 것의 의미 이해하기 (0) | 2020.07.29 |
[비전공자용] [Python] CNN(합성곱 신경망) - 풀링 계층 구현 (0) | 2020.07.28 |
[비전공자용] [Python] CNN(합성곱 신경망) - 합성곱 계층 구현 (2) | 2020.07.28 |
[비전공자용]합성곱 신경망 (CNN) - 합성곱 계층 & 풀링 계층 (2) | 2020.07.10 |