인공지능 AI

[딥러닝/인공지능]MNIST(필기체 손글씨)

딥러닝구조를 이용하여 MNIST, 즉 필기체손글씨를 인식할 수 있습니다.

np.loadtxt()함수를 이용하여 트레이닝데이터행렬을 만들 수 있습니다.

Train Data SetTest Data Set 을 활용해보겠습니다.

학습데이터인 Train Data Set에는 총 6만개의 이미지데이터가 있습니다.

하나의 이미지는 28*28개로 총 784개의 숫자로 이루어져 있습니다.

 

위 Train Data Set 파일에서 처음 5라는 숫자는 정답 숫자에 대한 정보이고

그 이후로 784개의 숫자가 이미지를 나타내는 정보입니다. 픽셀 하나의 밝기값이라고 볼 수 있습니다.

0에 가까울수록 검은색이고 255에 가까울수록 밝은(흰)색입니다. 

 

학습데이터에서는 하나의 이미지가 784개의 숫자를 가진 1차원 배열로 표현되지만

실제이미지는 28*28의 숫자를 가진 2차원 배열로 표현됩니다. 

 

(입력층-은닉층-출력층)의 딥러닝 아키텍처로 다시 한번 복습해보겠습니다.

 

MNIST 인식을 위한 딥러닝 아키텍처

 

입력층 노드는 그림에서 보이듯 총 784개입니다.(이미지를 나타내는 정보의 숫자들) 

트레이닝데이터에서 하나의 레코드, 즉 하나의 행은 1열의 정답을 제외하면 2열부터 785열까지 784개입니다.

총 784개의 픽셀값이 정답 이미지 정보를 나타냅니다.( 위 내용과 같은 내용) 

 

은닉층 노드개수는 제한이 없습니다.(임의로 30개로 설정)

 

출력층 노드의 개수는 원핫인코딩 기법을 사용하기 위해 10개입니다. 정답은 0부터 9까지의 숫자 중 하나이므로 10개의 노드로 구성된 출력층이 필요합니다.

 

원핫인코딩(One-Hot Encoding)

출력층 노드에서 최댓값을 가지는 노드의 인덱스값을 정답으로 판단하는 기법입니다.

0~9 사이의 숫자 중 어느것인지 분류해내는 방법입니다. 

(선형회귀 Classification개념에서 확률값이 가장 높은 것=해당인덱스(숫자)일 확률이 가장 높은 것)

(이는 기존의 Logistic Regression에서 사용하는 0또는 1이라는 이분법적 Classification 방법을 보완했습니다.) 

그림에서 보이듯 1차원 배열에서 각각 숫자일 확률을 기록하고 그 확률이 가장 높은 것을 정답으로 취급합니다.

 

 

파이썬 코드로 구현해보겠습니다.

import numpy as np

train_data=np.loadtxt("mnist_train.csv",delimiter=',',dtype=np.float32)
test_data=np.loadtxt("mnist_test.csv",delimiter=',',dtype=np.float32)
print("load data")

def sigmoid(x):
    return 1/(1+np.exp(-x))
def numerical_derivative(f,input_data):
    delta_x=1e-4
    ret=np.zeros_like(input_data)
    it=np.nditer(input_data, flags=['multi_index'])

    while not it.finished:
        idx=it.multi_index
        tmp_val=input_data[idx]

        input_data[idx]=float(tmp_val)+delta_x
        fx1=f(input_data)

        input_data[idx]=float(tmp_val)-delta_x
        fx2=f(input_data)
        ret[idx]=(fx1-fx2)/(2*delta_x)

        input_data[idx]=tmp_val
        it.iternext()
    return ret

class NeuralNetwork:
    def init(self,input_nodes, hidden_nodes, output_nodes):
        self.input_nodes=input_nodes #784개
        self.hidden_nodes=hidden_nodes #100개
        self.output_nodes=output_nodes #10개

        self.learning_rate=1e-3

        #은닉층 가중치 W2 Xaiver/He방법으로 self.W2 가중치 초기화 
        self.W2=np.random.rand(input_nodes, hidden_nodes)/np.sqrt(self.input_nodes/2)
        self.b2=np.random.rand(hidden_nodes)

        #출력층 가중치 W3
        self.W3=np.random.rand(hidden_nodes,output_nodes)/np.sqrt(self.hidden_nodes/2)
        self.b2=np.random.rand(output_nodes)

        self.data=np.array([])
        self.one_hot=np.array([])

        print("MNIST_Test object is created !!!")

    def feed_forward(self):
        delta=1e-7

        z2=np.dot(self.data, self.W2)+self.b2
        a2=sigmoid(z2)

        z3=np.dot(a2, self.W3)+self.b3
        a3=sigmoid(z3)

        #cross-entropy
        return -np.sum(self.one_hot*np.log(y+delta)+(1-self.one_hot)*np.log(1-y+delta))

    def loss_val(self):
        delta=1e-7

        z2=np.dot(self.data, self.W2)+self.b2
        a2=sigmoid(z2)

        z3=np.dot(a2, self.W3)+self.b3
        a3=sigmoid(z3)

        return -np.sum(self.one_hot*np.log(y+delta)+(1-self.one_hot)*np.log(1-y+delta))

    def predict(self, input_data):
        z2=np.dot(input_data, self.W2)+self.b2
        a2=sigmoid(z2)

        z3=np.dot(a2,self.W3)+self.b3
        a3=sigmoid(z3)

        #MNIST one-hot encoding따라 0또는1이 아닌 argmax()통해 최대 인덱스 값을 넘겨줘야함 
        return np.argmax(y)

    #정확도 측정 함수 
    def accuracy(self, test_data):
        count=0
        for index in range(len(test_data)):
            label=int(test_data[index,0])
            #정규화 
            data=test_data[index,1:]
            predicted_val=self.predict(data)
            if predicted_val==label:
                count+=1

        print("Accuracy=",100*(count/float(len(test_data))))

    #수치미분 통해 loss값 최소될때까지 학습하는 함수 
    def train(self, train_data):
        label=int(train_data[0])
        self.data=(train_data[1:]/255.0*0.99)+0.01
        self.one_hot=np.zeros(self.output_nodes)+0.01
        self.one_hot[label]=0.99

        f=lambda x:self.feed_forward()
        self.W2=self.learning_rate*numerical_derivative(f,self.W2)
        self.b2=self.learning_rate*numerical_derivative(f,self.b2)
        self.W3=self.learning_rate*numerical_derivative(f,self.W3)
        self.b3=self.learning_rate*numerical_derivative(f,self.b3)

    mnist=NeuralNetwork(784,50,10)
    for step in range(30001):
        mnist.train(train_data[step])

        if step%400==0:
            print("step=",step,"loss_val=",mnist.loss_val())
    mnist.accuracy(test_data)

 

이미지로 직접 보고싶다면 Matplotlib 모듈을 사용합니다.

아래와 같이 코드를 사용합니다.

import numpy as np
import matplotlib.pyplot as plt

train_data = np.loadtxt('mnist_train.csv', delimiter=',', dtype=np.float32)
test_data = np.loadtxt('mnist_test.csv', delimiter=',', dtype=np.float32)
print('load datas')

img=train_data[0][1:].reshape(28,28)
plt.imshow(img, cmap='gray')
plt.show()

이미지 결과입니다!

학습결과

여기까지의 결과를 도출해내는 데에만 20시간이 넘는 학습시간이 소요되었습니다...

(중간에 컴퓨터가 배터리가 나가서 다시 학습을 하루종일 시켰어야 했습니다..)

엄청난 비효율성과 낭비죠.. 이를 보완하기 위해서 tensorflow(텐서플로)를 기반으로 한 딥러닝 아키텍처를 사용합니다.

훨씬 빠른 학습속도로 학습을 할 수 있도록 해줍니다. 

2020/08/26 - [인공지능 AI] - [인공지능/AI]텐서플로를 이용한 MNIST 딥러닝학습