반응형


뉴럴 네트워크(Neural Network)를 사용하여 직접 촬영한 손글씨 이미지로부터 숫자를 인식시켜 보았습니다.  



지난번 포스팅과 데이터 훈련 시키는 방식만 다르기 때문에 1 ~ 6 번까지는 동일한 내용입니다.



Tensorflow 강좌 - Logistic Regression를 이용하여 실제 손글씨 숫자 인식해보기(MNIST)

http://webnautes.tistory.com/1238







텐서플로우 2.0에서 텐서플로우 1.x 코드를 실행하는 방법을 설명합니다. 


Tensorflow 2.0에서 Tensorflow 1.x 코드 실행하기

https://webnautes.tistory.com/1393



1. A4용지에 0 ~ 9까지 숫자를 적은 후, 카메라로 찍었습니다. 그리고나서 이미지를 10개로 나누어서 저장했습니다.

이미지 크기는 똑같을 필요없지만 중앙에 숫자가 오도록 이미지를 잘라줘야 합니다.






2. 세션을 시작하면서 디스크에 저장했던 모델 파라미터를 가져옵니다. 이미지로부터 숫자를 예측할 때 사용되어집니다.


with tf.Session() as sess:

   # 변수 초기화
   sess.run(tf.global_variables_initializer())

   # 저장된 모델 파라미터를 가져옵니다.
   model_path = "/tmp/model.saved"
   saver = tf.train.Saver()

   saver.restore(sess, model_path)
   print("Model restored from file: %s" % model_path)



모델 파라미터를 저장했다가 불러오는 것은 다음 포스팅을 참고하세요.



예제로 배우는 텐서플로우 강좌 - 7. 훈련된 모델 파라미터 저장하기

http://webnautes.tistory.com/1237





3. 흑백 이미지로 읽어서 28 x 28 크기의 이미지로 변환합니다. 현재 흰바탕에 검은 글씨입니다.


   gray = cv2.imread(str(no)+".png", 0)
   gray = cv2.resize(gray, (28, 28), interpolation=cv2.INTER_AREA)




4. 검은색 바탕의 흰글씨로 이진화한 후, 숫자 영역외의 공백을 모두 제거합니다.


   (thresh, gray) = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV  | cv2.THRESH_OTSU)

   while np.sum(gray[0]) == 0:
       gray = gray[1:]

   while np.sum(gray[:,0]) == 0:
       gray = np.delete(gray,0,1)

   while np.sum(gray[-1]) == 0:
       gray = gray[:-1]

   while np.sum(gray[:,-1]) == 0:
       gray = np.delete(gray,-1,1)

   rows,cols = gray.shape

 



5. 숫자가 중앙에 오도록 공백을 다시 추가합니다.


   if rows > cols:
       factor = 20.0/rows
       rows = 20
       cols = int(round(cols*factor))
       # first cols than rows
       gray = cv2.resize(gray, (cols,rows))
   else:
       factor = 20.0/cols
       cols = 20
       rows = int(round(rows*factor))
       # first cols than rows
       gray = cv2.resize(gray, (cols, rows))

   colsPadding = (int(math.ceil((28-cols)/2.0)),int(math.floor((28-cols)/2.0)))
   rowsPadding = (int(math.ceil((28-rows)/2.0)),int(math.floor((28-rows)/2.0)))
   gray = np.lib.pad(gray,(rowsPadding,colsPadding),'constant')

   shiftx,shifty = getBestShift(gray)
   shifted = shift(gray,shiftx,shifty)
   gray = shifted





6. 이미지를  0 ~ 1 사이 값을 갖는 크기 784( = 28 x 28 )의 일차원 배열로 변환합니다.

   flatten = gray.flatten() / 255.0
   images[i] = flatten




7. 뉴럴 네트워크를 사용하여 입력 이미지로부터 예측한 숫자를 출력합니다.


   print(sess.run(tf.argmax(prediction, 1), feed_dict={X: images}))



Logistic regression에서는 8을 5로 인식하는 오류가 있었는데 문제 없이 모두 인식되었습니다.


[0 1 2 3 4 5 6 7 8 9]




8. 전체 코드입니다. 모델을 훈련시키는 코드와 모델을 사용하여 이미지를 인식하는 두개의 코드로 구성됩니다.

이 코드를 먼저 실행하여 훈련 결과물인 모델 파라미터를 디스크에 저장합니다.


# 수정 및 주석 : webnautes
# 원본 코드 https://github.com/aymericdamien/TensorFlow-Examples/blob/master/examples/3_NeuralNetworks/neural_network_raw.py

from __future__ import print_function
import tensorflow as tf


#---------------------------------------------------------------------------------------------------- 1. MNIST 데이터를 가져옵니다.
# MNIST 데이터 관련 내용은 다음 포스팅 참고
#
# Tensorflow 예제 - MNIST 데이터 출력해보기 ( http://webnautes.tistory.com/1232 )
from tensorflow.examples.tutorials.mnist import input_data

# one_hot을 True로 설정하면 라벨을 one hot 벡터로 합니다. False이면 정수형 숫자가 라벨이 됩니다.
# /tmp/data/ 폴더를 생성하고 MNIST 데이터 압축파일을 다운로드 받아 압축을 풀고 데이터를 읽어옵니다.
# 이후에는 다운로드 되어있는 압축파일의 압축을 풀어서 데이터를 읽어옵니다.
mnist = input_data.read_data_sets("/tmp/data/", one_hot=True)



#---------------------------------------------------------------------------------------------------- 2. 파라미터 지정
# 뉴럴 네트워크 파라미터
n_hidden_1 = 256 # 첫번째 히든 레이어의 뉴런 개수
n_hidden_2 = 256 # 두번째 히든 레이어의 뉴런 개수
num_input = 784 # MNIST 데이터 입력( 28 x 28 이미지를 크기 784인 1차원 배열로 변환해서 사용)
num_classes = 10 # MNIST 데이터의 전체 클래스 개수 10개 ( 0 ~ 9 숫자 )

# 뉴럴 네트워크의 입력과 라벨
X = tf.placeholder(tf.float32, [None, num_input])
Y = tf.placeholder(tf.float32, [None, num_classes])



# 레이어(layer)의 weight와 bias를 저장할 변수를 선언합니다.
# 코드를 간결하게 하기 위해서 딕셔너리를 이용합니다.
weights = {
   'h1': tf.Variable(tf.random_normal([num_input, n_hidden_1])),
   'h2': tf.Variable(tf.random_normal([n_hidden_1, n_hidden_2])),
   'out': tf.Variable(tf.random_normal([n_hidden_2, num_classes]))
}
biases = {
   'b1': tf.Variable(tf.random_normal([n_hidden_1])),
   'b2': tf.Variable(tf.random_normal([n_hidden_2])),
   'out': tf.Variable(tf.random_normal([num_classes]))
}



#---------------------------------------------------------------------------------------------------- 3. 모델 생성
def neural_net(x):
   # Hidden fully connected layer with 256 neurons
   layer_1 = tf.add(tf.matmul(x, weights['h1']), biases['b1'])
   # Hidden fully connected layer with 256 neurons
   layer_2 = tf.add(tf.matmul(layer_1, weights['h2']), biases['b2'])
   # Output fully connected layer with a neuron for each class
   out_layer = tf.matmul(layer_2, weights['out']) + biases['out']
   return out_layer

logits = neural_net(X)
prediction = tf.nn.softmax(logits)


#---------------------------------------------------------------------------------------------------- 4.loss와 optimizer 정의
loss_op = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits_v2(logits=logits, labels=Y))
optimizer = tf.train.AdamOptimizer(learning_rate=0.01).minimize(loss_op)



# 정확도 측정할 모델
correct_prediction = tf.equal(tf.argmax(prediction, 1), tf.argmax(Y, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))



with tf.Session() as sess:


   # 변수 초기화
   sess.run(tf.global_variables_initializer())

   #---------------------------------------------------------------------------------------------------------- 5. 훈련 시작
   for step in range(1, 1000+1):  # 변수 step은 1 ~ 1000 까지 값을 가지게 됨

       # 전체 훈련 데이터(mnist.train)에서 128개씩 데이터를 가져옵니다.
       batch_x, batch_y = mnist.train.next_batch(128)

       # optimizer 오퍼레이션을 실행합니다.
       sess.run(optimizer, feed_dict={X: batch_x, Y: batch_y})

       # 첫번째 훈련 후 그리고 100번 배수로 할때 마다 중간 결과 출력
       if step % 100 == 0 or step == 1:
           # Calculate batch loss and accuracy
           loss, acc = sess.run([loss_op, accuracy], feed_dict={X: batch_x, Y: batch_y})
           print("Step " + str(step) + ", Minibatch Loss= " + \
                 "{:.4f}".format(loss) + ", Training Accuracy= " + \
                 "{:.3f}".format(acc))

   print("최적화 완료")


   # 정확도 측정을 위해서 훈련 데이터(mnist.train) 대신에 별도의 테스트 데이터(mnist.test)를 사용해야 합니다.
   print("Testing Accuracy:", \
       sess.run(accuracy, feed_dict={X: mnist.test.images,
                                     Y: mnist.test.labels}))


   #---------------------------------------------------------------------------------------------------- 6. 모델 파라미터 저장
   model_path = "/tmp/model.saved" # 모델 파라미터를 저장할 경로와 파일 이름
   saver = tf.train.Saver()

   save_path = saver.save(sess, model_path)
   print("Model saved in file: %s" % save_path)



..


..


9. 훈련된 모델을 사용하여 이미지로부터 숫자를 예측하는 코드입니다.


import tensorflow as tf
import cv2
import numpy as np
import math
from scipy import ndimage


def getBestShift(img):
   cy,cx = ndimage.measurements.center_of_mass(img)

   rows,cols = img.shape
   shiftx = np.round(cols/2.0-cx).astype(int)
   shifty = np.round(rows/2.0-cy).astype(int)

   return shiftx,shifty


def shift(img,sx,sy):
   rows,cols = img.shape
   M = np.float32([[1,0,sx],[0,1,sy]])
   shifted = cv2.warpAffine(img,M,(cols,rows))
   return shifted




# 뉴럴 네트워크 파라미터
n_hidden_1 = 256 # 첫번째 히든 레이어의 뉴런 개수
n_hidden_2 = 256 # 두번째 히든 레이어의 뉴런 개수
num_input = 784 # MNIST 데이터 입력( 28 x 28 이미지를 크기 784인 1차원 배열로 변환해서 사용)
num_classes = 10 # MNIST 데이터의 전체 클래스 개수 10개 ( 0 ~ 9 숫자 )

# 뉴럴 네트워크의 입력과 라벨
X = tf.placeholder(tf.float32, [None, num_input])


# 레이어(layer)의 weight와 bias를 저장할 변수를 선언합니다.
# 코드를 간결하게 하기 위해서 딕셔너리를 이용합니다.
weights = {
   'h1': tf.Variable(tf.random_normal([num_input, n_hidden_1])),
   'h2': tf.Variable(tf.random_normal([n_hidden_1, n_hidden_2])),
   'out': tf.Variable(tf.random_normal([n_hidden_2, num_classes]))
}
biases = {
   'b1': tf.Variable(tf.random_normal([n_hidden_1])),
   'b2': tf.Variable(tf.random_normal([n_hidden_2])),
   'out': tf.Variable(tf.random_normal([num_classes]))
}



def neural_net(x):
   # Hidden fully connected layer with 256 neurons
   layer_1 = tf.add(tf.matmul(x, weights['h1']), biases['b1'])
   # Hidden fully connected layer with 256 neurons
   layer_2 = tf.add(tf.matmul(layer_1, weights['h2']), biases['b2'])
   # Output fully connected layer with a neuron for each class
   out_layer = tf.matmul(layer_2, weights['out']) + biases['out']
   return out_layer

logits = neural_net(X)
prediction = tf.nn.softmax(logits)



with tf.Session() as sess:

   # 변수 초기화
   sess.run(tf.global_variables_initializer())

   # 저장된 모델 파라미터를 가져옵니다.
   model_path = "/tmp/model.saved"
   saver = tf.train.Saver()

   saver.restore(sess, model_path)
   print("Model restored from file: %s" % model_path)



   # 10개의 이미지를 위한 배열을 생성
   images = np.zeros((10, 784))

   i = 0
   for no in range(10):  # 10개의 이미지를 입력 받음

       gray = cv2.imread(str(no) + ".png", 0)
       gray = cv2.resize(gray, (28, 28), interpolation=cv2.INTER_AREA)

       (thresh, gray) = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)

       while np.sum(gray[0]) == 0:
           gray = gray[1:]

       while np.sum(gray[:, 0]) == 0:
           gray = np.delete(gray, 0, 1)

       while np.sum(gray[-1]) == 0:
           gray = gray[:-1]

       while np.sum(gray[:, -1]) == 0:
           gray = np.delete(gray, -1, 1)

       rows, cols = gray.shape

       cv2.imwrite("b_" + str(no) + ".png", gray)

       if rows > cols:
           factor = 20.0 / rows
           rows = 20
           cols = int(round(cols * factor))
           # first cols than rows
           gray = cv2.resize(gray, (cols, rows))
       else:
           factor = 20.0 / cols
           cols = 20
           rows = int(round(rows * factor))
           # first cols than rows
           gray = cv2.resize(gray, (cols, rows))

       colsPadding = (int(math.ceil((28 - cols) / 2.0)), int(math.floor((28 - cols) / 2.0)))
       rowsPadding = (int(math.ceil((28 - rows) / 2.0)), int(math.floor((28 - rows) / 2.0)))
       gray = np.lib.pad(gray, (rowsPadding, colsPadding), 'constant')

       shiftx, shifty = getBestShift(gray)
       shifted = shift(gray, shiftx, shifty)
       gray = shifted

       cv2.imwrite("image_" + str(no) + ".png", gray)

       flatten = gray.flatten() / 255.0
       images[i] = flatten

       i += 1

   print(sess.run(tf.argmax(prediction, 1), feed_dict={X: images}))





참고한 코드


https://github.com/aymericdamien/TensorFlow-Examples/blob/master/examples/3_NeuralNetworks/neural_network_raw.py


https://github.com/opensourcesblog/tensorflow-mnist/blob/master/mnist.py



마지막 업데이트 - 2018. 9. 15



반응형

문제 발생시 지나치지 마시고 댓글 남겨주시면 가능한 빨리 답장드립니다.

도움이 되셨다면 토스아이디로 후원해주세요.
https://toss.me/momo2024


제가 쓴 책도 한번 검토해보세요 ^^

+ Recent posts