본문 바로가기

Programming/(Python)(Ubuntu)

임의의 데이터에서 이진 분류 하기

필요한 모듈 : 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를 실행하면 다음과 같이 결과가 출력됩니다.


학습 파라미터 사이즈

 

학습 파라미터

학습 전 정확도 측정

 

학습 진행

(중략)

학습 후 파라미터

학습 후 정확도 측정

 

 

 

 

 

반응형