인공지능 응용 공학

구글 Colab에서 TensorFlow 버전 1.15를 사용한 MNIST 예제

coding art 2023. 1. 11. 12:45
728x90

구글의 머신러닝 지원 라이브러리인 텐서플로우는 1.x 버전에서 1.15까지 업그레이드 된 후 2.x 버전으로 넘어와 현재 2.92까지 진화하였다. 초기의 1.x 버전에서는 그래프 파트와 실행 파트를 명확하게 구분해서 코드 작업이 이루어졌던 반면 2.x 버전에서는 그러한 구분이 사라져버린 근본적인 차이를 보여준다.

 

초보자들이 Colab에서 처음으로 2.x 버전의 텐서플로우를 사용한 MNIST 코드를 갑자기 경험하는 경우 머신러닝의 진면목을 제대로 이해하기 어려운 상황을 겪을 수 있기 때문에, 버전 1.15에서 사용했던 MNIST 코드 그래프 구조를 살펴만 보고 2.x 버전에서 어떤 방식으로 진화했는지 직접 Colab에서의 코딩 작업을 통해 살펴보도록 하자.

 

1 !pip install tensorflow==1.15

현재 텐서플로우 2.92 버전이 Default 로 지원되기 때문에 한때 지원되었던 1.x 버전을 사용하기 위해서는 pip 설치 명령을 사용했어야 했다. 즉 과거의 1.x 버전 설치를 위해서는 현재의 2.92 버전을 uninstall 하여 설치 제거 후 1.x를 설치하는 방식이었으나 2023년부터 더 이상 지원이 되지 않는다.

 

MNIST 문제의 기본적인 설정은 train 학습과정에서 28X28 수기문자 이미지 6 만개를 사용하며 test 검증을 위해서 별도로 동일한 품질의 데이터 1만개를 사용한다. 물론 데이터들의 라벨은 one hot 코드화되어 제공된다.

 

MNIST tutorial을 위한 입력 데이터 input_data를 tensorflow.examples. tutorials.mnist 로부터 불러오자. 입력 데이터를 읽어서 현재 코드가 위치한 폴더 내에 MNIST_data 폴더를 생성 저장하고, 라벨값들을 one hot 코드로 변환한다.

 

2 import tensorflow as tf
import numpy as np
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)

 

아래에서 None은 없음이 아니라 개수가 실행을 통해 결정된다는 의미이다. MNIST 문제에서 학습을 위한 입력 데이터 개수는 60,000개인데 이 숫자가 None 으로 설정된다.

x, W, b, y_를 포함하는 tensorflow 변수들은 파이선 자체의 변수라기보다는 파이선 내 tensorflow 변수들로서 파이선 numpy 변수들과는 완전히 구별되며,

 

3 x = tf.placeholder(tf.float32, [None, 784]) #tensorflow 입력 데이터
W = tf.Variable(tf.zeros([784, 10])) #tensorflow 가중치 변수
b = tf.Variable(tf.zeros([10]))# tensorflow 편향 변수
y = tf.nn.softmax(tf.matmul(x, W) + b) # y는 hypothesis
y_ = tf.placeholder(tf.float32, [None, 10]) # y_는 정답 라벨 값 변수

 

이 graph 단계에서 직접적인 숫자로 출력이 되지 않는다는 점에 유의하자. shape을 출력해 보면 Tensor로 정의되어 있다는 점만 출력된다.

learning rate = 0,1 은 하이퍼 파라메터 설정이다.

cross entropy 는 앞서 Cost 함수에서 설명했던 그대로 코딩되었다.

reduction_indices=[1]은 과거의 표현으로서 지금은 axis=1에 해당 한다.

axis가 0과 1을 가진다면 이는 2차원 텐서로 볼 수 있다. 즉 성분별로 아마디르 곱 y_*tf.log(y)를 계산 후 axis=1을 기준으로 합산한다는 의미이다.

 

4 learning_rate = 0.1
cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y), reduction_indices=[1]))
train_step = tf.train.GradientDescentOptimizer(learning_rate= learning_rate).minimize(cross_entropy)

 

train_step은 tensorflow class인 tf.train.GradientDescentOptimizer를 인스턴스 연산을 위해 learning rate를 인수로 가지는 상속 가능한 모델로 정의하는 것이다. 여기까지가 graph 단계로서 연산 실행을 위한 변수 설정 및 Flow Chart 단계로 보면 된다.

 

5 sess = tf.Session()
sess.run(tf.global_variables_initializer())

 

tf.Session 부터가 실질적인 계산 수행 위치로서 sess.run 명령에 의해 계산 실행이 시작 된다.

for loop에서 range 의 값 법위가 600 이며, mnist.train.next_batch(100)에서 1 배치당 무작위로 선택된 100개의 배치 입력이 사용된다면 결국 60,000개의 입력 데이터를 학습에서 처리하는 셈이 된다.

sess.run 명령에서는 파이선 dictionary 형태로 key와 데이터가 짝을 이루는 방식으로 데이터 즉 batch_xs 는 28x28 픽셀 데이터이고, batch_ys 는 one hot 코드화된 정답 라벨를 읽어 들인다.

 

6 for i in range(600):
batch_xs, batch_ys = mnist.train.next_batch(100)
sess.run(train_step, feed_dict={x: batch_xs, y_: batch_ys})

 

학습이 완료된 후 얼마나 정확하게 예측했는지 즉 인식률을 계산할 필요가 있다.

 

7 correct_prediction = tf.equal(tf.argmax(y,1), tf.argmax(y_,1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
print(sess.run(accuracy, feed_dict={x: mnist.test.images, y_: mnist.test.labels}))

 

따라서 tf.argmax(y, 1)은 예측치 y의 axis=1 즉 y 라는 컬럼 벡터형 텐서 또는 어레이에서 최대값을 가지는 위치와 one hot 코드인 정답 라벨 y_의 axis=1 인 텐서에서 최대 값 1이 위치한 곳이 일치하면 즉 equal이 성립하면 인식률 계산에 1을 추가한다. 마지막 단계에서 인식 성공 시 추가한 값들을 다 더해 평균을 내면(reduce-mean) 인식률 즉 accuracy가 얻어진다. 이 작업을 검증용 데이터 mnist.test.images 10,000개에 대해서 시행한다.

 

7 correct_prediction = tf.equal(tf.argmax(y,1), tf.argmax(y_,1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
print(sess.run(accuracy, feed_dict={x: mnist.test.images, y_: mnist.test.labels}))
 

블로그에 제시된 코드는 현재의 Colab에서 실행이 불가능하다.

하지만 라즈베리파이라든지 또는 젯슨나노 보드의 경우 설치된 TensorFlow 버전이 2.x 미만이므로 실행이 가능할 수도 있다. 하지만 현재 TensorFlow 버전이 2.92 이므로 다음 블로그(http://ejleep1.tistory.com/1441)를 참조하여 상위버전으로 넘어가기 바란다.

 

※ 반드시 1.x 버전에서 MNIST 코드를 실행시키기 위해서는 Anaconda3에서 가상환경을 만들 필요가 있다. 예를 들어 가상 환경명을 tensorflow115라고 하자. 아울러 파이선 버전은 그리 높지 않은 3.7 정도가 좋을 것이다.

Open Terminal 기능을 사용하여 연 다음 다음의 설치 작업을 진행하자.

pip install tensorflow==1.15

pip install keras numpy matplotlib pandas sckit-learn

pip install protobuff==3.20.*

 

설치가 끝났으면 Open Terminal을 닿고 Anaconda3을 완전히 껏다가 새로 시작하도록 한다. Anaconda3 메뉴화면에서 tensorflow115를 선택 후 Spyder install 버튼을 클릭하자.

Spyder 버튼이 launch로 바뀌면 클릭하여 Spyder를 실행시킨다.

 

※ 첨부된 위 코드를 복사하여 실행시켜보자. 단 session 초반부의 minist.test_next_batch(5)는 test 데이터중 무작위로 5개를 선택한다는 의미이다.

함수 gen_image()를 사용하여 batch_xs[0])에 해당하는 문자 이미지를 show() 명령을 사용하여 출력해 보자.

# mnist_plot.py
import tensorflow as tf
import numpy as np
from matplotlib import pyplot as plt
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)

'''
gpus = tf.config.list_physical_devices('GPU')
if gpus:
  # 텐서플로가 첫 번째 GPU만 사용하도록 제한
  try:
    tf.config.set_visible_devices(gpus[0], 'GPU')
  except RuntimeError as e:
    # 프로그램 시작시에 접근 가능한 장치가 설정되어야만 합니다
    print(e)
'''

def gen_image(arr):
    two_d = (np.reshape(arr, (28, 28)) * 255).astype(np.uint8)
    print(two_d)
    plt.imshow(two_d, cmap='Greys')
    return plt

x = tf.placeholder(tf.float32, [None, 784])
W = tf.Variable(tf.zeros([784, 10]))
b = tf.Variable(tf.zeros([10]))
y = tf.nn.softmax(tf.matmul(x, W) + b)
y_ = tf.placeholder(tf.float32, [None, 10])

learning_rate = 0.1
cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y), reduction_indices=[1]))
train_step = tf.train.GradientDescentOptimizer(learning_rate=learning_rate).minimize(cross_entropy)

sess = tf.Session()
sess.run(tf.global_variables_initializer())
batch_xs, batch_ys = mnist.test.next_batch(5)
gen_image(batch_xs[0]).show()
#gen_image(batch_xs[1]).show()
#gen_image(batch_xs[2]).show()
#gen_image(batch_xs[3]).show()
#gen_image(batch_xs[4]).show()
for i in range(5000):
    batch_xs, batch_ys = mnist.train.next_batch(100)
    sess.run(train_step, feed_dict={x: batch_xs, y_: batch_ys})
correct_prediction = tf.equal(tf.argmax(y,1), tf.argmax(y_,1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
print(sess.run(accuracy, feed_dict={x: mnist.test.images, y_: mnist.test.labels}))