
이번 포스팅에서는 다중분류 다층신경망을 Python으로 구현해본다. 입력데이터는 텐서플로에서 제공하는 패션 MNIST 데이터셋을 사용한다.
Install Tensorflow
! pip install tensorflow_gpu==2.0.0-rc1
Import Tensorflow
import tensorflow as tf
load Data
(x_train_all, y_train_all),(x_test, y_test) = tf.keras.datasets.fashion_mnist.load_data()
class_names = ['티셔츠/윗도리','바지','스웨터','드레스','코트','샌들','셔츠','스니커즈','가방','앵글부츠']
데이터를 로드하고 클래스명을 지정한다. 클래스명은 패션 MNIST 데이터 세트 깃허브(https://github.com/zalandoresearch/fashion-mnist)에 접속하면 확인할 수 있다.

Preprocess Data
from sklearn.model_selection import train_test_split
x_train, x_val, y_train, y_val = train_test_split(x_train_all, y_train_all, stratify=y_train_all, test_size=0.2, random_state=42)
# x 전처리
x_train = x_train /255
x_val = x_val / 255
x_train = x_train.reshape(-1,784)
x_val = x_val.reshape(-1,784)
# y 전처리
y_train_encoded = tf.keras.utils.to_categorical(y_train)
y_val_encoded = tf.keras.utils.to_categorical(y_val)
x는 이미지 데이터로 픽셀마다 0~255 사이의 값을 가지므로 각 값을 255로 나눠 0~1사이로 맞춘다. 또한 내가 구현할 신경망은 1차원 배열의 샘플을 입력데이터로 사용하므로 reshape() 메소드를 사용하여 훈련세트와 검증세트를 1차원 배열로 바꿔준다. y는 1~9사이의 1개 정수값을 가지므로 10개의 출력층 뉴런값과 비교할 수 있도록 원-핫 인코딩한다.
Define Model
class MultiClassNetwork :
def __init__(self,l1=0, l2=0, learning_rate=0.1, batch_size=32, units=10) :
self.w1 = None
self.b1 = None
self.w2 = None
self.b2 = None
self.a1 = None
self.l1 = l1
self.l2 = l2
self.lr = learning_rate
self.batch_size = batch_size
self.losses = []
self.val_losses = []
self.units = units
def forpass(self, x) :
z1 = np.dot(x, self.w1) + self.b1
self.a1 = self.sigmoid(z1)
z2 = np.dot(self.a1, self.w2) + self.b2
return z2
def sigmoid(self, z) :
a = 1 / (1+np.exp(-z))
return a
def softmax(self, z) :
exp_z = np.exp(z)
return exp_z / np.sum(exp_z, axis=1).reshape(-1,1)
def backprop(self,x,err) :
m = len(x)
w2_grad = np.dot(self.a1.T, err) / m
b2_grad = np.sum(err) / m
err_to_hidden = np.dot(err, self.w2.T) * self.a1 * (1-self.a1)
w1_grad = np.dot(x.T, err_to_hidden) / m
b1_grad = np.sum(err_to_hidden, axis=0) / m
return w1_grad, b1_grad, w2_grad, b2_grad
def fit(self,x, y, epochs, x_val=None, y_val=None) :
np.random.seed(42)
self.init_weights(x.shape[1], y.shape[1])
for i in range(epochs) :
loss = 0
print('.', end='')
for x_batch, y_batch in self.gen_batch(x,y) :
a = self.training(x_batch, y_batch)
a = np.clip(a, 1e-10, 1-1e-10)
loss += np.sum(-y_batch*np.log(a))
self.losses.append((loss + self.reg_loss()) / len(x))
self.update_val_loss(x_val, y_val)
def init_weights(self, n_features, n_classes) :
self.w1 = np.random.normal(0,1, (n_features, self.units))
self.b1 = np.zeros(self.units)
self.w2 = np.random.normal(0,1, (self.units, n_classes))
self.b2 = np.zeros(n_classes)
def update_val_loss(self, x_val, y_val) :
z = self.forpass(x_val)
a = self.softmax(z)
a = np.clip(a, 1e-10, 1-1e-10)
val_loss = np.sum(-y_val*np.log(a))
self.val_losses.append((val_loss + self.reg_loss()) / len(y_val))
def reg_loss(self) :
return self.l1 * (np.sum(np.abs(self.w1)) + np.sum(np.abs(self.w2))) + self.l2/2 * (np.sum(self.w1**2) + np.sum(self.w2**2))
def gen_batch(self, x, y) :
length = len(x)
bins = length // self.batch_size
if length % self.batch_size :
bins += 1
indexes = np.random.permutation(np.arange(len(x)))
x = x[indexes]
y = y[indexes]
for i in range(bins) :
start = self.batch_size * i
end = self.batch_size * (i+1)
yield x[start:end], y[start:end]
def training(self, x, y) :
m = len(x)
z = self.forpass(x)
a = self.softmax(z)
err = -(y-a)
w1_grad, b1_grad, w2_grad, b2_grad = self.backprop(x,err)
w1_grad += (self.l1*np.sign(self.w1) + self.l2*self.w1) / m
w2_grad += (self.l1*np.sign(self.w2) + self.l2*self.w2) /m
self.w1 -= self.lr * w1_grad
self.b1 -= self.lr * b1_grad
self.w2 -= self.lr * w2_grad
self.b2 -= self.lr * b2_grad
return a
def predict(self, x) :
z = self.forpass(x)
return np.argmax(z, axis=1)
def score(self, x, y) :
return np.mean(self.predict(x) == np.argmax(y, axis=1))
이중분류 모델에서 activation()을 sigmoid()와 softmax()로 나눠서 수정했다. 또한 predict() 메소드에서 argmax()를 이용해 가장 큰 값의 인덱스를 반환할 수 있도록 하고, score() 메소드에서 이를 타깃 열 벡터의 가장 큰 값과 비교하여 True의 비율을 반환했다.
Fit Model
fc = MultiClassNetwork(batch_size=256, units=100)
fc.fit(x_train, y_train_encoded, x_val = x_val, y_val = y_val_encoded, epochs=40)
배치사이즈를 256, 은닉층 뉴런 수를 100, 에포크 횟수를 40으로 세팅하여 학습시켰다.
Loss Graph
plt.plot(fc.losses)
plt.plot(fc.val_losses)
plt.ylabel('loss')
plt.xlabel('iteration')
plt.legend(['train_loss','val_loss'])
plt.show()

손실 그래프가 초기에는 빠르게 감소하다가 완만하게 수렴하고 있다.
Score
fc.score(x_val, y_val_encoded)
# 0.8150833333333334
검증세트에 대한 이 분류모델의 정확도는 81% 이다. 무작위로 의류 이미지를 예측하면 약 10% 정도의 정확도를 기대할 수 있으므로 확실히 더 높은 점수이다. 하지만 오차가 19%나 되기 때문에 실전에서 사용하기는 어렵다. 정확도를 높이기 위해서는 tensorflow 같은 전문 딥러닝 패키지를 이용해야한다. 다음 포스팅에서는 tensorflow 패키지를 이용하여 다중분류 모델을 구현해본다.
끝.
Reference.
도서 <Do it! 정직하게 코딩하며 배우는 딥러닝 입문> 이지스 퍼블리싱, 박해선 지음
'Deep Learning > [Books] Do it! 정직하게 코딩하며 배우는 딥러닝 입문' 카테고리의 다른 글
Keras를 활용하여 다중분류 신경망 구현하기 (0) | 2020.11.02 |
---|---|
[모델 구축] 사이킷런 SGDClassifier 클래스 (1) | 2020.10.31 |
다중분류 다층신경망 구현하기 - 상 (0) | 2020.10.19 |
미니배치경사하강법을 이용하는 로지스틱 회귀 모델 구현하기 (0) | 2020.10.15 |
Python으로 다층신경망 구현하기 (0) | 2020.10.12 |