임의의 데이터에서 이진 분류 하기
필요한 모듈 : Numpy, OrderedDict, Matplotlib
추가 설명
OrderedDict 란?
OrderedDict 는 기본 딕셔너리(Dictionary)와 거의 비슷하지만, 입력된 아이템들(items)의 순서를 기억하는 Dictionary 클래스입니다. collections.OrderedDict의 자세한 내용은 docs.python.org 에서 확인할 수 있습니다.
간단하게 소개하자면 일반적인 dict 메소드를 지원하는 dict 서브 클래스의 인스턴트를 리턴합니다. OrderedDict 는 키가 먼저 삽입 된 순서를 기억 사전인가됩니다. 새로운 정렬된 사전은 항목이 삭제될 때 정렬 순서를 유지합니다. 그러나 새 키가 추가되면 키가 끝에 추가되고 정렬이 유지되지 않습니다. 키가 마지막으로 삽입된 순서를 기억하는 정렬된 사전 변형을 작성하는 것도 간단합니다. 새 항목이 기존의 학목을 덮어 쓰면 원래 삽입 위치가 변경되고 끝으로 이동합니다.
예입니다.
# regular unsorted dictionary
d = {'banana': 3, 'apple':4, 'pear': 1, 'orange': 2}
# dictionary sorted by key
OrderedDict(sorted(d.items(), key=lambda t: t[0]))
# Output Result
#OrderedDict([('apple', 4), ('banana', 3), ('orange', 2), ('pear', 1)])
# dictionary sorted by value
OrderedDict(sorted(d.items(), key=lambda t: t[1]))
# Output Result
# OrderedDict([('pear', 1), ('orange', 2), ('banana', 3), ('apple', 4)])
# dictionary sorted by length of the key string
OrderedDict(sorted(d.items(), key=lambda t: len(t[0])))
# Output Result
#OrderedDict([('pear', 1), ('apple', 4), ('orange', 2), ('banana', 3)])
0. 사전 준비 : 모듈 import
import numpy as np
import matplotlib.pyplot as plt
from collections import OrderedDict
1. 데이터 생성
1-1. Train / Test / Label 데이터 생성 코드 ( dataprocess.py )
# 사용한 라이브러리
import numpy as np
import matplotlib.pyplot as plt
from collections import OrderedDict
number_len = 50
mean_0 = (1, 1); mean_1 = (-1, -1)
cov = [[1, 0], [0, 1]]
class Data_create:
def __init__(self):
self.train_label = []
self.test_label = []
### 데이터 생성
def Train_data_create(self):
train_0 = np.random.multivariate_normal(mean_0, cov, number_len)
train_1 = np.random.multivariate_normal(mean_1, cov, number_len)
train_0_label = np.zeros((number_len, 1))
train_1_label = np.ones((number_len, 1))
train_data = np.concatenate((train_0, train_1), axis = 0)
self.train_label = np.concatenate((train_0_label, train_1_label), axis = 0)
print("label 0 data size :",train_0.shape)
print("label 1 data size :",train_1.shape)
print('all data size :', train_data.shape)
print('all label size :', self.train_label.shape)
plt.title("train")
plt.plot(train_0[:,0], train_0[:,1], 'r.', marker='x')
plt.plot(train_1[:,0], train_1[:,1], 'b.', marker='o')
plt.axis('equal')
#plt.show()
plt.pause(3)
plt.close()
# 데이터 확인
print("Train x, y location label 0\n", train_data[number_len*0:3])
print("Train x, y location label 1\n", train_data[number_len*1:number_len*1+3])
return train_data
def Test_data_create(self):
test_0 = np.random.multivariate_normal(mean_0, cov, number_len)
test_0_label = np.zeros((number_len, 1))
test_1 = np.random.multivariate_normal(mean_1, cov, number_len)
test_1_label = np.ones((number_len, 1))
test_data = np.concatenate((test_0, test_1), axis = 0)
self.test_label = np.concatenate((test_0_label, test_1_label), axis = 0)
print("label 0 data size :",test_0.shape)
print("label 1 data size :",test_1.shape)
print('all data size :',test_data.shape)
print('all label size :',self.test_label.shape)
plt.title("test")
plt.plot(test_0[:,0], test_0[:,1], 'r.', marker='x')
plt.plot(test_1[:,0], test_1[:,1], 'b.', marker='o')
plt.axis('equal')
#plt.show()
plt.pause(3)
plt.close()
# 데이터 확인
print("Test x, y location label 0\n", test_data[number_len*0:3])
print("Test x, y location label 1\n", test_data[number_len*1:number_len*1+3])
return test_data
### 라벨 생성
# 앞선 코드에서는 라벨을 0과 1로 분류해놓았고 분류해 놓은 라벨을 행렬로 만들어 줍니다.
# 행렬로 만들어주는 이유는 나중에 계산을 하기 편하게 하기 위해서입니다.
# 훈련 라벨 생성 기존 0 행렬을 생성했기 때문에 1만 추가
def Train_label_create(self):
Train_label = np.zeros((len(self.train_label),2))
for i in range(len(self.train_label)):
if self.train_label[i] == 0:
Train_label[i][0] = 1
if self.train_label[i] == 1:
Train_label[i][1] = 1
# 라벨 확인
print(Train_label.shape)
print("Train_label one-hot label 0\n", Train_label[number_len*0:3])
print("Train_label one-hot label 1\n", Train_label[number_len*1:number_len*1+3])
return Train_label
# 테스트 라벨 생성 기존 0 행렬을 생성했기 때문에 1만 추가
def Test_label_create(self):
Test_label = np.zeros((len(self.test_label),2))
for i in range(len(self.test_label)):
if self.test_label[i] == 0:
Test_label[i][0] = 1
if self.test_label[i] == 1:
Test_label[i][1] = 1
# 라벨 확인
print(Test_label.shape)
print("Test_label one-hot label 0\n", Test_label[number_len*0:3])
print("Test_label one-hot label 1\n", Test_label[number_len*1:number_len*1+3])
return Test_label
다음과 같이 라벨이 0일 때에는 0번째에 1로 표현, 라벨이 1일 때에는 1번째에 1로 표현해줍니다.
2. 모델 선언
2-1 필요한 함수 선언
1) Relu
ReLU의 식은 다음과 같습니다.
그리고 미분에 대한 식은 다음과 같습니다.
보기에는 1로 보이지만 이것은 그대로 넘겨준다는 의미를 가지고 있습니다.
아래는 코드입니다.
# Relu 함수 선언
class Relu:
def __init__(self):
self.mask = None
def forward(self, x):
self.mask = (x <= 0)
out = x.copy()
out[self.mask] = 0
return out
def backward(self, dout):
dout[self.mask] = 0
dx = dout
return dx
2) Mean Squared Error
# mean_squared_error_loss 함수 선언
class mean_squared_error_Loss:
def __init__(self):
self.loss = None # 손실함수
self.y = None # layer의 마지막 출력
self.t = None # 정답 레이블(원-핫 인코딩 형태)
def forward(self, x, t):
self.t = t
self.y = x
# loss = mean_squared_error
self.loss = 0.5 * np.sum((self.y-self.t)**2)
return self.loss
def backward(self, dout=1):
batch_size = self.t.shape[0]
if self.t.size == self.y.size: # 정답 레이블이 원-핫 인코딩 형태일 때
dx = (self.y - self.t) / batch_size
else:
dx = self.y.copy()
dx[np.arange(batch_size), self.t] -= 1
dx = dx / batch_size
return dx
2-2 Fully Connected layer 선언
class FC_Layer:
def __init__(self, W, b):
self.W = W
self.b = b
self.x = None
self.original_x_shape = None
# 가중치와 편향 매개변수의 미분
self.dW = None
self.db = None
def forward(self, x):
# 텐서 대응
self.original_x_shape = x.shape
x = x.reshape(x.shape[0], -1)
self.x = x
out = np.dot(self.x, self.W) + self.b
return out
def backward(self, dout):
dx = np.dot(dout, self.W.T)
self.dW = np.dot(self.x.T, dout)
self.db = np.sum(dout, axis=0)
# input 데이터 모양 변경(텐서 대응)
dx = dx.reshape(*self.original_x_shape)
return dx
2-3 Network 선언
# FC_Layer가 3개인 클래스
class Three_FC_Layer:
def __init__(self, input_size, hidden_size, output_size, weight_init_std=0.001):
# 가중치 선언 및 초기화
self.params = {}
self.params['W1'] = np.random.normal(0,weight_init_std,(input_size, hidden_size))
print('W1 shape :',self.params['W1'].shape)
self.params['b1'] = np.zeros(hidden_size)
print('b1 shape :',self.params['b1'].shape)
self.params['W2'] = np.random.normal(0,weight_init_std,(hidden_size, hidden_size))
print('W2 shape :',self.params['W2'].shape)
self.params['b2'] = np.zeros(hidden_size)
print('b2 shape :',self.params['b2'].shape)
self.params['W3'] = np.random.normal(0,weight_init_std,(hidden_size, output_size))
print('W3 shape :',self.params['W3'].shape)
self.params['b3'] = np.zeros(output_size)
print('b3 shape :',self.params['b3'].shape)
# layers 선언 {FC_Layer1, ReLU1, FC_Layer2, ReLU2, FC_Layer3}
self.layers = OrderedDict()
self.layers['FC_Layer1'] = \
FC_Layer(self.params['W1'], self.params['b1'])
self.layers['ReLU1'] = ReLU()
self.layers['FC_Layer2'] = \
FC_Layer(self.params['W2'], self.params['b2'])
self.layers['ReLU2'] = ReLU()
self.layers['FC_Layer3'] = \
FC_Layer(self.params['W3'], self.params['b3'])
# 마지막 Loss 계산
self.lastLayer = mean_squared_error_Loss()
# 순전파
def foreward_pass(self, x):
for layer in self.layers.values():
x = layer.forward(x)
return x
# x : 입력 데이터, t : 정답 레이블
def loss(self, x, t):
y = self.foreward_pass(x)
return self.lastLayer.forward(y,t)
def accuracy(self, x, t):
y = self.foreward_pass(x)
y = np.argmax(y, axis = 1)
if t.ndim != 1 : t = np.argmax(t, axis = 1)
accuracy = np.sum(y == t) / float(x.shape[0])
return accuracy
def gradient(self, x, t):
# 순전파
self.loss(x, t)
# 역전파
dout = 1
dout = self.lastLayer.backward(dout)
layers = list(self.layers.values())
layers.reverse()
for layer in layers:
dout = layer.backward(dout)
# W, b 결과 저장
grads={}
grads['W1'] = self.layers['FC_Layer1'].dW
grads['b1'] = self.layers['FC_Layer1'].db
grads['W2'] = self.layers['FC_Layer2'].dW
grads['b2'] = self.layers['FC_Layer2'].db
grads['W3'] = self.layers['FC_Layer3'].dW
grads['b3'] = self.layers['FC_Layer3'].db
return grads
전체적인 코드는 다음과 같습니다. ( model.py )
import numpy as np
from collections import OrderedDict
class ReLU:
def __init__(self):
self.mask = None
def forward(self, x):
self.mask = (x <= 0)
out = x.copy()
out[self.mask] = 0
return out
def backward(self, dout):
dout[self.mask] = 0
dx = dout
return dx
class FC_Layer:
def __init__(self, W, b):
self.W = W
self.b = b
self.x = None
self.original_x_shape = None
# 가중치와 편향 매개변수의 미분
self.dW = None
self.db = None
def forward(self, x):
# 텐서 대응
self.original_x_shape = x.shape
x = x.reshape(x.shape[0], -1)
self.x = x
out = np.dot(self.x, self.W) + self.b
return out
def backward(self, dout):
dx = np.dot(dout, self.W.T)
self.dW = np.dot(self.x.T, dout)
self.db = np.sum(dout, axis=0)
# input 데이터 모양 변경(텐서 대응)
dx = dx.reshape(*self.original_x_shape)
return dx
class mean_squared_error_Loss:
def __init__(self):
self.loss = None # 손실함수
self.y = None # last_layer 전의 출력
self.t = None # 정답 레이블(원-핫 인코딩 형태)
def forward(self, x, t):
self.t = t
self.y = x
# loss = mean_squared_error
self.loss = ((self.y-self.t)**2).mean(axis=None)
return self.loss
def backward(self, dout=1):
batch_size = self.t.shape[0]
# 정답 레이블이 원-핫 인코딩 형태일 때
dx = (self.y - self.t) / batch_size
return dx
# FC_Layer가 3개인 클래스
class Three_FC_Layer:
def __init__(self, input_size, hidden_size, output_size, weight_init_std=0.001):
# 가중치 선언 및 초기화
self.params = {}
self.params['W1'] = np.random.normal(0,weight_init_std,(input_size, hidden_size))
print('W1 shape :',self.params['W1'].shape)
self.params['b1'] = np.zeros(hidden_size)
print('b1 shape :',self.params['b1'].shape)
self.params['W2'] = np.random.normal(0,weight_init_std,(hidden_size, hidden_size))
print('W2 shape :',self.params['W2'].shape)
self.params['b2'] = np.zeros(hidden_size)
print('b2 shape :',self.params['b2'].shape)
self.params['W3'] = np.random.normal(0,weight_init_std,(hidden_size, output_size))
print('W3 shape :',self.params['W3'].shape)
self.params['b3'] = np.zeros(output_size)
print('b3 shape :',self.params['b3'].shape)
# layers 선언 {FC_Layer1, ReLU1, FC_Layer2, ReLU2, FC_Layer3}
self.layers = OrderedDict()
self.layers['FC_Layer1'] = \
FC_Layer(self.params['W1'], self.params['b1'])
self.layers['ReLU1'] = ReLU()
self.layers['FC_Layer2'] = \
FC_Layer(self.params['W2'], self.params['b2'])
self.layers['ReLU2'] = ReLU()
self.layers['FC_Layer3'] = \
FC_Layer(self.params['W3'], self.params['b3'])
# 마지막 Loss 계산
self.lastLayer = mean_squared_error_Loss()
# 순전파
def foreward_pass(self, x):
for layer in self.layers.values():
x = layer.forward(x)
return x
# x : 입력 데이터, t : 정답 레이블
def loss(self, x, t):
y = self.foreward_pass(x)
return self.lastLayer.forward(y,t)
def accuracy(self, x, t):
y = self.foreward_pass(x)
y = np.argmax(y, axis = 1)
if t.ndim != 1 : t = np.argmax(t, axis = 1)
accuracy = np.sum(y == t) / float(x.shape[0])
return accuracy
def gradient(self, x, t):
# 순전파
self.loss(x, t)
# 역전파
dout = 1
dout = self.lastLayer.backward(dout)
layers = list(self.layers.values())
layers.reverse()
for layer in layers:
dout = layer.backward(dout)
# W, b 결과 저장
grads={}
grads['W1'] = self.layers['FC_Layer1'].dW
grads['b1'] = self.layers['FC_Layer1'].db
grads['W2'] = self.layers['FC_Layer2'].dW
grads['b2'] = self.layers['FC_Layer2'].db
grads['W3'] = self.layers['FC_Layer3'].dW
grads['b3'] = self.layers['FC_Layer3'].db
return grads
2-4 학습 진행 코드입니다. (main.py)
from dataprocess import Data_create
from model import ReLU, FC_Layer, mean_squared_error_Loss, Three_FC_Layer
import numpy as np
import matplotlib.pyplot as plt
data_process = Data_create()
Train_data = data_process.Train_data_create()
Test_data = data_process.Test_data_create()
Train_label = data_process.Train_label_create()
Test_label = data_process.Test_label_create()
model = Three_FC_Layer(input_size = 2, hidden_size = 10, output_size = 2)
print('------------- W, b 초기화 -------------')
for key in ('W1', 'b1', 'W2', 'b2', 'W3', 'b3'):
print('{}'.format(key), model.params[key])
print('----------- 역전파 학습 전 Forward pass Accuracy -----------')
test_acc = model.accuracy(Test_data, Test_label)
print("Accuracy :", test_acc*100,"%")
# 학습 및 오차역전파법을 이용한 업데이트
iters_num = 500000
train_size = Train_data.shape[0]
batch_size = 2
learning_rate = 0.005
train_loss_list = []
train_acc_list = []
test_acc_list = []
for i in range(iters_num):
batch_mask = np.random.choice(train_size, batch_size)
x_batch = Train_data[batch_mask]
t_batch = Train_label[batch_mask]
# 오차역전파 기울기
grad = model.gradient(x_batch, t_batch)
# W, b 갱신
for key in ('W1', 'b1', 'W2', 'b2', 'W3', 'b3'):
model.params[key] -= learning_rate * grad[key]
loss = model.loss(x_batch, t_batch)
train_loss_list.append(loss)
# 5000 에폭마다 결과 확인
if i % 5000 == 0:
print('================================================')
print("iters_num :", i)
print("loss :", loss)
train_acc = model.accuracy(Train_data, Train_label)
test_acc = model.accuracy(Test_data, Test_label)
train_acc_list.append(train_acc)
test_acc_list.append(test_acc)
print('train acc : {:.3f} | test acc : {:.3f}'.format(train_acc, test_acc))
# 학습 후 W, b
print('------------- 업데이트 후 W, b -------------')
for key in ('W1', 'b1', 'W2', 'b2', 'W3', 'b3'):
print('{}'.format(key), model.params[key])
# 학습 후 정확도
print('----------- 역전파 학습 후 Forward pass Accuracy -----------')
test_acc = model.accuracy(Test_data, Test_label)
print("Accuracy :", test_acc*100,"%")
# 훈련 정확도 그래프
plt.title("Train Acc list")
plt.plot(train_acc_list)
#plt.show()
plt.pause(3)
plt.close()
# 테스트 정확도 그래프
plt.title("Test Acc list")
plt.plot(test_acc_list)
#plt.show()
plt.pause(3)
plt.close()
main.py를 실행하면 다음과 같이 결과가 출력됩니다.
학습 파라미터 사이즈
학습 파라미터
학습 전 정확도 측정
학습 진행
(중략)
학습 후 파라미터
학습 후 정확도 측정