05-02 06:32
Notice
Recent Posts
Recent Comments
관리 메뉴

Scientific Computing & Data Science

[Artificial Intelligence / TensorFlow] R-TensorFlow 예제 - Logistic Regression을 이용한 손글씨 인식 본문

Artificial Intelligence/TensorFlow

[Artificial Intelligence / TensorFlow] R-TensorFlow 예제 - Logistic Regression을 이용한 손글씨 인식

cinema4dr12 2017. 7. 9. 09:50

by Geol Choi | Jul.




이번 포스팅에서는 
Softmax Classsification이라고 불리우는 Logistic Regression을 방법을 이용하여 손글씨 숫자(Handwritten Digits)를 분류하는 TensorFlow 코드를 Python과 R에서 구현해 보도록 한다. 이와 관련된 TensorFlow의 페이지를 참고하면 이해하는데 큰 도움이 될 것이다.

1. 구현순서

TensorFlow의 구현순서가 딱히 정해진 것은 아니지만 구현을 위한 충분한 가이드라인이 될 수 있으리라 생각된다.



2. Python-TensorFlow

2.1. 라이브러리 불러오기

TensorFlow 라이브러리(tensorflow) 외에 numpy와 학습이 진행됨에 따른 Cost 함수의 변화를 그래프로 표시하기 위해 matplotlib을 불러온다:

1
2
3
4
5
## import required libraries
from __future__ import print_function
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
cs

2.2. 입력 데이터 준비

입력 데이터는 MNIST(Modified National Institute of Standards and Technology)의 손글씨 숫자(0-9)이다. TensorFlow 라이브러리는 이 데이터를 불러올 수 있는 도구를 제공한다:

1
2
3
## import MNIST data
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("/tmp/data/", one_hot=True)
cs


input_data.read_data_sets()의 입력 파라미터 중 one_hot이 보이는데, 이것은 크기 10(0-9)의 0과 1로 이루어진 Vector이다. 예를 들어 숫자 4를 나타내는 One Hot Vector(또는 One Hot Encoding)는 [0, 0, 0, 0, 1, 0, 0, 0, 0, 0]이며, 숫자 7을 나타내는 One Hot Vector는 [0, 0, 0, 0, 0, 0, 0, 1, 0, 0]이다.

2.3. 파라미터 정의

계산에 필요한 파라미터를 정의한다. 파라미터 learning_rate는 학습률(Learning Rate)을, training_epochs는 학습 횟수를, batch_size는 각 Mini-batch 크기를, display_step은 Training Epoch가 진행됨에 따라 결과를 표시할 Training Epoch 주기를 의미한다.

1
2
3
4
5
## define parameters
learning_rate = 0.01
training_epochs = 25
batch_size = 100
display_step = 1
cs

2.4. Placeholder 변수 정의

런타임에서 TensorFlow에 공급할 Placeholder 변수를 정의한다. x는 입력으로 받을 픽셀 데이터를 y는 출력결과인 0-9 숫자 분류(Classification)이다.

1
2
3
## tf graph input
= tf.placeholder(tf.float32, [None, 784]) # mnist data image of shape 28*28=784
= tf.placeholder(tf.float32, [None, 10]) # 0-9 digits recognition => 10 classes
cs

2.5. 모델 변수 정의

학습을 진행하면서(즉, 최적화를 진행하면서) 업데이트 할 모델 변수를 정의한다. 언제나 늘 그렇듯이 딥러닝의 변수는 Weights와 Biases이다:

1
2
3
## set model weights
= tf.Variable(tf.zeros([78410]))
= tf.Variable(tf.zeros([10]))
cs

2.6. 모델 정의

모델(또는 Predictor)은 Softmax 함수로 정의하였다.

1
2
## construct model
pred = tf.nn.softmax(tf.matmul(x, W) + b) # Softmax
cs

2.7. Cost 함수 정의

Cost 함수(또는 목적함수(Objective Function))를 Information Theory에 기반한 Cross entropy로 정의하였다.

1
2
## minimize error using cross entropy
cost = tf.reduce_mean(-tf.reduce_sum(y*tf.log(pred), reduction_indices=1))
cs

2.8. Optimizer 정의

Optimizer는 최적화하는 방법을 의미한다. Gradient Descent 방법으로 설정하였지만, TensorFlow는 AdadeltaOptimizer, AdagradOptimizer, AdamOptimizer 등 이외에도 다양한 Optimizer를 제공하고 있다.

1
2
## steepest gradient descent
optimizer = tf.train.GradientDescentOptimizer(learning_rate).minimize(cost)
cs

2.9. 세션 초기화 및 변수 초기화

TensorFlow 세션을 초기화하고,

1
2
## initializing the variables
init = tf.global_variables_initializer()
cs


Epoch에 따라 Cost 함수값의 변화를 시각화하기 위한 Data Frame 변수를 초기화한다:

1
2
## initialize variable for plotting
cost_res = np.zeros(shape=(training_epochs,2))
cs

2.10. 학습

일반적으로 데이터 사이즈가 너무 큰 경우 데이터를 통째로 메모리에 올려놓고 학습시키기가 불가능 경우가 있다. 이러한 경우 전체 Batch를 부분부분으로 잘라 학습시키는 미니-배치(Mini-batch) 방법을 쓰는데 사실 Full-batch가 가능하면 Full-batch를 하는 것이 가급적 좋다. 왜냐하면, 미니-배치를 하게 되면 각 배치 사이에 편차가 존재하게 되므로 학습 정확도를 떨어뜨릴 수가 있다. 이를 해소하기 위해 배치-노멀라이제이션(Batch Normalization) 방법이 있다.

학습 시, 미니-배치를 하되 모든 미니-배치에 대하여 학습을 한 것을 하나의 Training Cycle(Epoch)로 간주한다. 즉, 1회의 Epoch에 대하여 모든 미니-배치에 대한 학습을 시킨다. 다음 코드를 보자:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
## launch the graph
with tf.Session() as sess:
    sess.run(init)
 
    ## training cycle
    for epoch in range(training_epochs):
        avg_cost = 0.
        total_batch = int(mnist.train.num_examples/batch_size)
        # loop over all batches
        for i in range(total_batch):
            batch_xs, batch_ys = mnist.train.next_batch(batch_size)
            ## run optimization op (backprop) and cost op (to get loss value)
            _, c = sess.run([optimizer, cost], feed_dict={x: batch_xs,
                                                          y: batch_ys})
            # Compute average loss
            avg_cost += c / total_batch
        
        ## record cost value per epoch
        cost_res[epoch, 0= epoch
        cost_res[epoch, 1= np.log(avg_cost)
        
        ## display logs per epoch step
        if (epoch+1) % display_step == 0:
            print("Epoch:"'%04d' % (epoch+1), "cost=""{:.9f}".format(avg_cost))
cs


Line 6에서는 각 Epoch에 대한 Loop를 지정하고, Line 6에서는 해당 Epoch에 대하여 모든 배치(Batch)를 계산하여 전체 배치(Total Batch)에 대한 학습을 실행한다.

avg_cost는 각 미니-배치에 대한 Cost 함수값의 평균값을 계산하는 변수이며, 하나의 미니-배치 크기(batch_size)는 100개이므로, 전체 배치 세트의 개수는 전체 train 이미지 개수를 미치-배치 크기(100)으로 나눈 값이다.

Line 11을 보면, 
mnist.train.next_batch() 함수를 이용하여 미니-배치를 한 세트씩 불러오고, 불러온 하나의 미니-배치를 batch_xs와 batch_ys 변수에 나눠 담는데 각각 하나의 손글씨 숫자 이미지의 픽셀 데이터와 이에 대한 분류(0-9의 숫자값) 결과(또는 라벨)이다.

Line 14에서 현재 주어진 Epoch의 미니-배치에 대한 Optimizer를 실행(Feed-forward + Backpropagation)하되 feed_dict를 통해 런타임에서 두 개의 변수 
batch_xs와 batch_ys를 공급한다.

Line 16에서 전체 배치에 대한 Cost 함수의 평균값을 업데이트 한다.

Line 19-20은 Epoch의 진행에 따른 Cost 함수값의 배치(Batch) 평균값을 그래프로 표시하기 위해 값을 저장하는 코드이다.

Line 23-24는 Epoch의 진행에 따른 Cost 함수값의 배치(Batch) 평균값을 그래프로 표시하기 위해 값을 저장하는 코드이다.

2.11. 학습 모델 테스트

학습된 모델을 테스트하기 위해서는, 학습을 통해 얻은 Predictor와 Ground Truth Label와 비교하면 된다. 다음의 코드는 이 두 가지를 비교하는 것이다:

1
2
## test model
correct_prediction = tf.equal(tf.argmax(pred, 1), tf.argmax(y, 1))
cs


tf.equal()은 두 개의 값이 같은지를 계산하는 함수이며, 같으면 1 다르면 0의 Boolean 값을 리턴한다. tf.argmax()는 입력 텐서(Tensor)의 가장 큰 값을 갖는 인덱스를 얻는 함수이며,axis 파라미터에 따라 행 또는 열 방향으로 인덱스를 얻을 수 있다.

다음 코드는 정확도를 계산하는 것이다:

1
2
3
## calculate accuracy
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
print("Accuracy:", accuracy.eval({x: mnist.test.images, y: mnist.test.labels}))
cs


우선 tf.cast() 함수를 이용하여 앞서 계산된 Boolean 데이터 형(Type)의 correct_prediction 변수를 Float32 데이터 형으로 변환하고, tf.reduce_mean() 함수를 이용하여 변환된 correct_prediction의 평균값을 계산하여 정확도를 측정하여 이를 확면 출력한다.

2.12. 결과 출력

다음은 학습 진행에 따른 Cost 함수값과 정확도 결과를 출력한 것이다:

Epoch: 0001 cost= 1.182138959
Epoch: 0002 cost= 0.664646334
Epoch: 0003 cost= 0.552549829
Epoch: 0004 cost= 0.498513855
Epoch: 0005 cost= 0.465348552
Epoch: 0006 cost= 0.442497832
Epoch: 0007 cost= 0.425526976
Epoch: 0008 cost= 0.412120040
Epoch: 0009 cost= 0.401381235
Epoch: 0010 cost= 0.392368349
Epoch: 0011 cost= 0.384782740
Epoch: 0012 cost= 0.378128710
Epoch: 0013 cost= 0.372371823
Epoch: 0014 cost= 0.367274958
Epoch: 0015 cost= 0.362715922
Epoch: 0016 cost= 0.358564279
Epoch: 0017 cost= 0.354849277
Epoch: 0018 cost= 0.351473811
Epoch: 0019 cost= 0.348291780
Epoch: 0020 cost= 0.345419897
Epoch: 0021 cost= 0.342706732
Epoch: 0022 cost= 0.340241603
Epoch: 0023 cost= 0.337948542
Epoch: 0024 cost= 0.335729676
Epoch: 0025 cost= 0.333707092
Optimization Finished!
Accuracy: 0.9132


정확도는 91.3%로 그리 썩 만족할 만한 결과는 아닌 것 같다.


마지막으로, Epoch의 진행에 따른 Cost 함수값을 출력하는데, 이는 Cost 함수 값이 잘 수렴되는지 확인하기 위함이다 (잘 수렴되었다).

2.13. 전체 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
## import required libraries
from __future__ import print_function
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
 
## import MNIST data
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("/tmp/data/", one_hot=True)
 
## define parameters
learning_rate = 0.01
training_epochs = 25
batch_size = 100
display_step = 1
 
## tf graph input
= tf.placeholder(tf.float32, [None, 784]) # mnist data image of shape 28*28=784
= tf.placeholder(tf.float32, [None, 10]) # 0-9 digits recognition => 10 classes
 
## set model weights
= tf.Variable(tf.zeros([78410]))
= tf.Variable(tf.zeros([10]))
 
## construct model
pred = tf.nn.softmax(tf.matmul(x, W) + b) # Softmax
 
## minimize error using cross entropy
cost = tf.reduce_mean(-tf.reduce_sum(y*tf.log(pred), reduction_indices=1))
 
## steepest gradient descent
optimizer = tf.train.GradientDescentOptimizer(learning_rate).minimize(cost)
 
## initializing the variables
init = tf.global_variables_initializer()
 
## initialize variable for plotting
cost_res = np.zeros(shape=(training_epochs,2))
 
## launch the graph
with tf.Session() as sess:
    sess.run(init)
 
    ## training cycle
    for epoch in range(training_epochs):
        avg_cost = 0.
        total_batch = int(mnist.train.num_examples/batch_size)
        # loop over all batches
        for i in range(total_batch):
            batch_xs, batch_ys = mnist.train.next_batch(batch_size)
            ## run optimization op (backprop) and cost op (to get loss value)
            _, c = sess.run([optimizer, cost], feed_dict={x: batch_xs,
                                                          y: batch_ys})
            # Compute average loss
            avg_cost += c / total_batch
        
        ## record cost value per epoch
        cost_res[epoch, 0= epoch
        cost_res[epoch, 1= np.log(avg_cost)
        
        ## display logs per epoch step
        if (epoch+1) % display_step == 0:
            print("Epoch:"'%04d' % (epoch+1), "cost=""{:.9f}".format(avg_cost))
 
    print("Optimization Finished!")
 
    ## test model
    correct_prediction = tf.equal(tf.argmax(pred, 1), tf.argmax(y, 1))
    
    ## calculate accuracy
    accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
    print("Accuracy:", accuracy.eval({x: mnist.test.images, y: mnist.test.labels}))
    
    # graphic display
    plt.plot(cost_res[:,0], cost_res[:,1], 'ro', label='Cost Function Value as Epoch Proceeds')
    plt.show()
cs


3. R-TensorFlow

3.1. 라이브러리 패키지 불러오기

tensorflow 라이브러리와 Epoch에 따른 Cost 함수의 변화를 시각화 하기 위해 plotly 라이브러리를 불러온다.

1
2
3
4
5
6
## import libraries
if (! ("plotly" %in% rownames(installed.packages()))) { install.packages("plotly") }
base::require(plotly)
 
if (! ("tensorflow" %in% rownames(installed.packages()))) { install.packages("tensorflow") }
base::require(tensorflow)
cs

3.2. 입력 데이터 준비

tensorflow 라이브러리를 통해 MNIST 데이터를 불러온다:

1
2
3
## import MNIST data
datasets <- tensorflow::tf$contrib$learn$datasets
mnist <- datasets$mnist$read_data_sets("MNIST-data", one_hot = TRUE)
cs


MNIST 데이터는 train Dataset과 test Dataset의 두 가지 Dataset으로 구성되며, 각각 55,000개와 10,000개의 데이터로 구성된다:

> train_images <- mnist$train$images
> dim(train_images)
[1] 55000   784

> test_images <- mnist$test$images
> dim(test_images)
[1] 10000   784


784는 MNIST의 각 손글씨 숫자 이미지가 28
×28개의 픽셀 수에서 비롯된 것이다. 따라서, train_imagestest_images 모두 28×28개의 픽셀 데이터 행렬을 1개의 행을 갖는 행렬(또는 벡터)로 변환한 것이며, 픽셀 데이터는 픽셀의 밝기값을 의미하는데 범위는 0.0에서 1.0까지 분포한다. 만약 이 텍스트 형태의 데이터를 이미지 데이터로 변환해 보고 싶다면, 다음의 R코드를 실행해 보기 바란다:

1
2
3
4
5
6
7
8
9
10
11
12
## import EBImage library
if(!base::require(EBImage)) {
  base::source("https://bioconductor.org/biocLite.R")
  BiocInstaller::biocLite("EBImage")
}
base::require(EBImage)
 
test_images <- mnist$test$images
 
mat <- base::matrix(test_images[1,], nrow=28, ncol=28, byrow=FALSE)
img <- EBImage::Image(mat)
EBImage::display(img, method="raster")
cs


위의 코드의 실행결과로 다음 이미지를 출력할 수 있다:


labels
는 각 손글씨 이미지 데이터에 대한 One Hot Vector의 라벨이며, 섹션 2.2에서 설명한 바와 같다.

> train_labels <- mnist$train$labels
> dim(train_labels)
[1] 55000    10

> test_labels <- mnist$test$labels
> dim(test_labels)
[1] 10000    10

3.3. 파라미터 정의

계산에 필요한 파라미터를 정의한다. 파라미터 learning_rate는 학습률(Learning Rate)을, training_epochs는 학습 횟수를, batch_size는 각 Mini-batch 크기를, display_step은 Training Epoch가 진행됨에 따라 결과를 표시할 Training Epoch 주기를 의미한다.

1
2
3
4
5
## define parameters
learning_rate <- 0.01
training_epochs <- 150
batch_size <- 100
display_step <- 1
cs

3.4. Placeholder 변수 정의

런타임에서 TensorFlow에 공급할 Placeholder 변수를 정의한다. x는 입력으로 받을 픽셀 데이터를 y는 출력결과인 0-9 숫자 분류(Classification)이다.

1
2
3
4
5
## tf graph input
# mnist data image of shape 28*28=784
<- tensorflow::tf$placeholder(tensorflow::tf$float32, tensorflow::shape(NULL, 784L))
# 0-9 digits recognition => 10 classes
<- tensorflow::tf$placeholder(tensorflow::tf$float32, tensorflow::shape(NULL, 10L))
cs


이 때 주의할 점은, 
tensorflow::shape() 함수에 입력으로 784L10L이 들어가는데, L이 있고 없고의  차이는 각각 4-비트 정수와 8-비트 정수의 차이이다. 아마도 메모리를 조금 더 효율적으로 사용하려는 의도로 생각되는데, R-TensorFlow에서는 이 차이를 엄격이 구분하므로 명확히 이해하고 넘어가도록 한다.

3.5. 모델 변수 정의

학습을 진행하면서(즉, 최적화를 진행하면서) 업데이트 할 모델 변수를 정의한다. 언제나 늘 그렇듯이 딥러닝의 변수는 Weights와 Biases이다:

1
2
3
## Set model weights
<- tensorflow::tf$Variable(tensorflow::tf$zeros(shape(784L, 10L)))
<- tensorflow::tf$Variable(tensorflow::tf$zeros(shape(10L)))
cs

3.6. 모델 정의

모델(또는 Predictor)은 Softmax 함수로 정의하였다 - 본 포스팅은 Softmax의 이론 설명보다는 이를 TensorFlow에 어떻게 적용하는가에 초점을 맞추었으므로 상세한 이론 설명은 하지 않도록 한다.

1
2
## construct model : Softmax
pred <- tensorflow::tf$nn$softmax(logits = tensorflow::tf$matmul(x, W) + b)
cs


3.7. Cost 함수 정의

Cost 함수(또는 목적함수(Objective Function))를 Information Theory에 기반한 Cross entropy로 정의하였다.

1
2
## minimize error using cross entropy
cost <- tensorflow::tf$reduce_mean(input_tensor = -tensorflow::tf$reduce_sum(input_tensor = y * tensorflow::tf$log(pred), axis = 1L))
cs


코드가 조금 복잡해 보일수도 있으나, 다음 수식을 코드로 표현한 것이라고 생각하면 된다:


\(H = -\displaystyle{\sum_{i}{p_{i}\mathrm{log}(p_i)}}\)



여기서 HShanon Entropy이다.


3.8. Optimizer 정의

Optimizer는 최적화하는 방법을 의미한다. Gradient Descent 방법으로 설정하였지만, TensorFlow는 AdadeltaOptimizer, AdagradOptimizer, AdamOptimizer 등 이외에도 다양한 Optimizer를 제공하고 있다.

1
2
## gradient descent
optimizer <- tensorflow::tf$train$GradientDescentOptimizer(learning_rate)$minimize(cost)
cs

3.9. 세션 초기화 및 변수 초기화

TensorFlow 세션을 초기화하고,

1
2
3
4
## create session and initialize  variables
sess <- tensorflow::tf$Session()
init <- tensorflow::tf$global_variables_initializer()
sess$run(init)
cs


Epoch에 따라 Cost 함수값의 변화를 시각화하기 위한 Data Frame 변수를 초기화한다:

1
2
3
4
## initialize variable for plotting
cost_val <- base::matrix(data=0.0, nrow=training_epochs, ncol=2)
cost_val <- base::as.data.frame(cost_val)
base::names(cost_val) <- base::c("Epoch""Cost")
cs


3.10. 학습

일반적으로 데이터 사이즈가 너무 큰 경우 데이터를 통째로 메모리에 올려놓고 학습시키기가 불가능 경우가 있다. 이러한 경우 전체 Batch를 부분부분으로 잘라 학습시키는 미니-배치(Mini-batch) 방법을 쓰는데 사실 Full-batch가 가능하면 Full-batch를 하는 것이 가급적 좋다. 왜냐하면, 미니-배치를 하게 되면 각 배치 사이에 편차가 존재하게 되므로 학습 정확도를 떨어뜨릴 수가 있다. 이를 해소하기 위해 배치-노멀라이제이션(Batch Normalization) 방법이 있다.

학습 시, 미니-배치를 하되 모든 미니-배치에 대하여 학습을 한 것을 하나의 Training Cycle(Epoch)로 간주한다. 즉, 1회의 Epoch에 대하여 모든 미니-배치에 대한 학습을 시킨다. 다음 코드를 보자:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
## train
# training cycle
for(epoch in 1:training_epochs) {
  avg_cost <- 0.0
  
  total_batch <- base::as.integer(mnist$train$num_examples / batch_size)
  
  # loop over all batches
  for(i in 1:total_batch) {
    # load batch_size training examples in each training iteration
    batches <- mnist$train$next_batch(base::as.integer(batch_size))
    batch_xs <- batches[[1]]
    batch_ys <- batches[[2]]
    # Run optimization op (backprop) and cost op (to get loss value)
    sess$run(optimizer, feed_dict = dict(x = batch_xs, y = batch_ys))
    
    # Compute average loss
    avg_cost <- avg_cost + sess$run(cost, feed_dict = dict(x = batch_xs, y = batch_ys)) / total_batch
  }
  
  # record avg_cost for each epoch
  cost_val[epoch,] <- base::c(epoch, avg_cost)
  
  # Display logs per epoch step
  if((epoch %% display_step) == 0) {
    base::print(base::sprintf("Epoch: %04d, cost: %.9f", epoch, avg_cost))
  }
  
}
cs



Line 3에서는 각 Epoch에 대한 Loop를 지정하고, Line 9에서는 해당 Epoch에 대하여 모든 배치(Batch)를 계산하여 전체 배치(Total Batch)에 대한 학습을 실행한다.

avg_cost는 각 미니-배치에 대한 Cost 함수값의 평균값을 계산하는 변수이며, 하나의 미니-배치 크기(batch_size)는 100개이므로, 전체 배치 세트의 개수는 전체 train 이미지 개수를 미치-배치 크기(100)으로 나눈 값이다.

Line 11-13을 보면, 
mnist$train$next_batch() 함수를 이용하여 미니-배치를 한 세트씩 불러오고, 불러온 하나의 미니-배치를 batch_xs와 batch_ys 변수에 나눠 담는데 각각 하나의 손글씨 숫자 이미지의 픽셀 데이터와 이에 대한 분류(0-9의 숫자값) 결과(또는 라벨)이다.

Line 15에서 현재 주어진 Epoch의 미니-배치에 대한 Optimizer를 실행(Feed-forward + Backpropagation)하되 feed_dict를 통해 런타임에서 두 개의 변수 
batch_xs와 batch_ys를 공급한다.

Line 18에서 전체 배치에 대한 Cost 함수의 평균값을 업데이트 한다.

Line 21-27은 Epoch의 진행에 따른 Cost 함수값의 배치(Batch) 평균값을 그래프로 표시하기 위해 값을 저장하는 코드이다.

3.11. 학습 모델 테스트

학습된 모델을 테스트하기 위해서는, 학습을 통해 얻은 Predictor와 Ground Truth Label와 비교하면 된다. 다음의 코드는 이 두 가지를 비교하는 것이다:

1
2
## test trained model
correct_prediction <- tensorflow::tf$equal(tensorflow::tf$argmax(pred, 1L), tensorflow::tf$argmax(y, 1L))
cs


tensorflow::tf$equal()은 두 개의 값이 같은지를 계산하는 함수이며, 같으면 1 다르면 0의 Boolean 값을 리턴한다. tensorflow::tf$argmax()는 입력 텐서(Tensor)의 가장 큰 값을 갖는 인덱스를 얻는 함수이며, axis 파라미터에 따라 행 또는 열 방향으로 인덱스를 얻을 수 있다.

다음 코드는 정확도를 계산하는 것이다:

1
2
3
4
## calculate accuracy
accuracy <- tensorflow::tf$reduce_mean(tensorflow::tf$cast(correct_prediction, tensorflow::tf$float32))
res <- base::sprintf("Accuracy: %.5f", sess$run(accuracy, feed_dict = dict(x = mnist$test$images, y = mnist$test$labels)))
base::print(res)
cs



우선 tensorflow::tf$cast() 함수를 이용하여 앞서 계산된 Boolean 데이터 형(Type)의 correct_prediction 변수를 Float32 데이터 형으로 변환하고, tensorflow::tf$reduce_mean() 함수를 이용하여 변환된 correct_prediction의 평균값을 계산하여 정확도를 측정하여 이를 확면 출력한다.

3.12. 결과 출력

다음은 학습 진행에 따른 Cost 함수값과 정확도 결과를 출력한 것이다:

[1] "Epoch: 0002, cost: 1.176674819"
[1] "Epoch: 0003, cost: 0.662688853"
[1] "Epoch: 0004, cost: 0.550780631"
[1] "Epoch: 0005, cost: 0.496776847"
[1] "Epoch: 0006, cost: 0.463809400"
[1] "Epoch: 0007, cost: 0.440985793"
[1] "Epoch: 0008, cost: 0.423989474"
[1] "Epoch: 0009, cost: 0.410715057"
[1] "Epoch: 0010, cost: 0.399925265"
[1] "Epoch: 0011, cost: 0.390998873"
[1] "Epoch: 0012, cost: 0.383378068"
[1] "Epoch: 0013, cost: 0.376776672"
[1] "Epoch: 0014, cost: 0.371004207"
[1] "Epoch: 0015, cost: 0.365968397"
[1] "Epoch: 0016, cost: 0.361402140"
[1] "Epoch: 0017, cost: 0.357288929"
[1] "Epoch: 0018, cost: 0.353589379"
[1] "Epoch: 0019, cost: 0.350133665"
[1] "Epoch: 0020, cost: 0.347013338"
[1] "Epoch: 0021, cost: 0.344145885"
[1] "Epoch: 0022, cost: 0.341470155"
[1] "Epoch: 0023, cost: 0.339031004"
[1] "Epoch: 0024, cost: 0.336682313"
[1] "Epoch: 0025, cost: 0.334469428"
[1] "Epoch: 0026, cost: 0.332445566"
[1] "Epoch: 0027, cost: 0.330562046"
[1] "Epoch: 0028, cost: 0.328737216"
[1] "Epoch: 0029, cost: 0.327065752"
[1] "Epoch: 0030, cost: 0.325378193"
[1] "Epoch: 0031, cost: 0.323825220"
[1] "Epoch: 0032, cost: 0.322343128"
[1] "Epoch: 0033, cost: 0.320951068"
[1] "Epoch: 0034, cost: 0.319612642"
[1] "Epoch: 0035, cost: 0.318377537"
[1] "Epoch: 0036, cost: 0.317129654"
[1] "Epoch: 0037, cost: 0.315943289"
[1] "Epoch: 0038, cost: 0.314826836"
[1] "Epoch: 0039, cost: 0.313744427"
[1] "Epoch: 0040, cost: 0.312714915"
[1] "Epoch: 0041, cost: 0.311716420"
[1] "Epoch: 0042, cost: 0.310722528"
[1] "Epoch: 0043, cost: 0.309799654"
[1] "Epoch: 0044, cost: 0.308922845"
[1] "Epoch: 0045, cost: 0.308030141"
[1] "Epoch: 0046, cost: 0.307220032"
[1] "Epoch: 0047, cost: 0.306338867"
[1] "Epoch: 0048, cost: 0.305542054"
[1] "Epoch: 0049, cost: 0.304809569"
[1] "Epoch: 0050, cost: 0.304074923"
[1] "Epoch: 0051, cost: 0.303354174"
[1] "Epoch: 0052, cost: 0.302622716"
[1] "Epoch: 0053, cost: 0.301959208"
[1] "Epoch: 0054, cost: 0.301342288"
[1] "Epoch: 0055, cost: 0.300673644"
[1] "Epoch: 0056, cost: 0.300009817"
[1] "Epoch: 0057, cost: 0.299407484"
[1] "Epoch: 0058, cost: 0.298838066"
[1] "Epoch: 0059, cost: 0.298255253"
[1] "Epoch: 0060, cost: 0.297701366"
[1] "Epoch: 0061, cost: 0.297146869"
[1] "Epoch: 0062, cost: 0.296562747"
[1] "Epoch: 0063, cost: 0.296085377"
[1] "Epoch: 0064, cost: 0.295551029"
[1] "Epoch: 0065, cost: 0.295053764"
[1] "Epoch: 0066, cost: 0.294570291"
[1] "Epoch: 0067, cost: 0.294127900"
[1] "Epoch: 0068, cost: 0.293602210"
[1] "Epoch: 0069, cost: 0.293166315"
[1] "Epoch: 0070, cost: 0.292733971"
[1] "Epoch: 0071, cost: 0.292305171"
[1] "Epoch: 0072, cost: 0.291862522"
[1] "Epoch: 0073, cost: 0.291450885"
[1] "Epoch: 0074, cost: 0.291020991"
[1] "Epoch: 0075, cost: 0.290604452"
[1] "Epoch: 0076, cost: 0.290212372"
[1] "Epoch: 0077, cost: 0.289822317"
[1] "Epoch: 0078, cost: 0.289429561"
[1] "Epoch: 0079, cost: 0.289037043"
[1] "Epoch: 0080, cost: 0.288674752"
[1] "Epoch: 0081, cost: 0.288328036"
[1] "Epoch: 0082, cost: 0.287962073"
[1] "Epoch: 0083, cost: 0.287596185"
[1] "Epoch: 0084, cost: 0.287252902"
[1] "Epoch: 0085, cost: 0.286916003"
[1] "Epoch: 0086, cost: 0.286591835"
[1] "Epoch: 0087, cost: 0.286272991"
[1] "Epoch: 0088, cost: 0.285952695"
[1] "Epoch: 0089, cost: 0.285611840"
[1] "Epoch: 0090, cost: 0.285269383"
[1] "Epoch: 0091, cost: 0.285012409"
[1] "Epoch: 0092, cost: 0.284702703"
[1] "Epoch: 0093, cost: 0.284415961"
[1] "Epoch: 0094, cost: 0.284074592"
[1] "Epoch: 0095, cost: 0.283808219"
[1] "Epoch: 0096, cost: 0.283529083"
[1] "Epoch: 0097, cost: 0.283234856"
[1] "Epoch: 0098, cost: 0.282949207"
[1] "Epoch: 0099, cost: 0.282645282"
[1] "Epoch: 0100, cost: 0.282448824"
[1] "Epoch: 0101, cost: 0.282174024"
[1] "Epoch: 0102, cost: 0.281930929"
[1] "Epoch: 0103, cost: 0.281612589"
[1] "Epoch: 0104, cost: 0.281408253"
[1] "Epoch: 0105, cost: 0.281154505"
[1] "Epoch: 0106, cost: 0.280904331"
[1] "Epoch: 0107, cost: 0.280663378"
[1] "Epoch: 0108, cost: 0.280390052"
[1] "Epoch: 0109, cost: 0.280144265"
[1] "Epoch: 0110, cost: 0.279974264"
[1] "Epoch: 0111, cost: 0.279697798"
[1] "Epoch: 0112, cost: 0.279522895"
[1] "Epoch: 0113, cost: 0.279212141"
[1] "Epoch: 0114, cost: 0.279020427"
[1] "Epoch: 0115, cost: 0.278772991"
[1] "Epoch: 0116, cost: 0.278630993"
[1] "Epoch: 0117, cost: 0.278332736"
[1] "Epoch: 0118, cost: 0.278170448"
[1] "Epoch: 0119, cost: 0.277958147"
[1] "Epoch: 0120, cost: 0.277747987"
[1] "Epoch: 0121, cost: 0.277539022"
[1] "Epoch: 0122, cost: 0.277353257"
[1] "Epoch: 0123, cost: 0.277135962"
[1] "Epoch: 0124, cost: 0.276954268"
[1] "Epoch: 0125, cost: 0.276699428"
[1] "Epoch: 0126, cost: 0.276582284"
[1] "Epoch: 0127, cost: 0.276379032"
[1] "Epoch: 0128, cost: 0.276189598"
[1] "Epoch: 0129, cost: 0.275981041"
[1] "Epoch: 0130, cost: 0.275799117"
[1] "Epoch: 0131, cost: 0.275601775"
[1] "Epoch: 0132, cost: 0.275425451"
[1] "Epoch: 0133, cost: 0.275254494"
[1] "Epoch: 0134, cost: 0.275104181"
[1] "Epoch: 0135, cost: 0.274911886"
[1] "Epoch: 0136, cost: 0.274734422"
[1] "Epoch: 0137, cost: 0.274565224"
[1] "Epoch: 0138, cost: 0.274379729"
[1] "Epoch: 0139, cost: 0.274185561"
[1] "Epoch: 0140, cost: 0.274034458"
[1] "Epoch: 0141, cost: 0.273853060"
[1] "Epoch: 0142, cost: 0.273717669"
[1] "Epoch: 0143, cost: 0.273533332"
[1] "Epoch: 0144, cost: 0.273349637"
[1] "Epoch: 0145, cost: 0.273217370"
[1] "Epoch: 0146, cost: 0.273073061"
[1] "Epoch: 0147, cost: 0.272894343"
[1] "Epoch: 0148, cost: 0.272747951"
[1] "Epoch: 0149, cost: 0.272624211"
[1] "Epoch: 0150, cost: 0.272455436"
[1] "Epoch: 0151, cost: 0.272322160"
[1] "Optimization Finished!"
[1] "Accuracy: 0.92310"


정확도는 92.3%로 그리 썩 만족할 만한 결과는 아닌 것 같다.

마지막으로, Epoch의 진행에 따른 Cost 함수값을 출력하는데, 이는 Cost 함수 값이 잘 수렴되는지 확인하기 위함이다 (잘 수렴되었다).

3.13. 전체 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
#############################################################################
# logistic regression example using TensorFlow library.
#############################################################################
# @Author: Geol Choi, phD.(cinema4dr12@gmail.com)
# @Date: July 7, 2017
#############################################################################
 
## remove existing variables
base::rm(list = ls())
 
## import libraries
if (! ("plotly" %in% rownames(installed.packages()))) { install.packages("plotly") }
base::require(plotly)
 
if (! ("tensorflow" %in% rownames(installed.packages()))) { install.packages("tensorflow") }
base::require(tensorflow)
 
## import MNIST data
datasets <- tensorflow::tf$contrib$learn$datasets
mnist <- datasets$mnist$read_data_sets("MNIST-data", one_hot = TRUE)
 
## define parameters
learning_rate <- 0.01
training_epochs <- 150
batch_size <- 100
display_step <- 1
 
## tf graph input
# mnist data image of shape 28*28=784
<- tensorflow::tf$placeholder(tensorflow::tf$float32, tensorflow::shape(NULL, 784L))
# 0-9 digits recognition => 10 classes
<- tensorflow::tf$placeholder(tensorflow::tf$float32, tensorflow::shape(NULL, 10L))
 
## Set model weights
<- tensorflow::tf$Variable(tensorflow::tf$zeros(shape(784L, 10L)))
<- tensorflow::tf$Variable(tensorflow::tf$zeros(shape(10L)))
 
## construct model : Softmax
pred <- tensorflow::tf$nn$softmax(logits = tensorflow::tf$matmul(x, W) + b)
 
## minimize error using cross entropy
cost <- tensorflow::tf$reduce_mean(input_tensor = -tensorflow::tf$reduce_sum(input_tensor = y * tensorflow::tf$log(pred), axis = 1L))
 
## gradient descent
optimizer <- tensorflow::tf$train$GradientDescentOptimizer(learning_rate)$minimize(cost)
 
## create session and initialize  variables
sess <- tensorflow::tf$Session()
init <- tensorflow::tf$global_variables_initializer()
sess$run(init)
 
## initialize variable for plotting
cost_val <- base::matrix(data=0.0, nrow=training_epochs, ncol=2)
cost_val <- base::as.data.frame(cost_val)
base::names(cost_val) <- base::c("Epoch""Cost")
 
## train
# training cycle
for(epoch in 1:training_epochs) {
  avg_cost <- 0.0
  
  total_batch <- base::as.integer(mnist$train$num_examples / batch_size)
  
  # loop over all batches
  for(i in 1:total_batch) {
    # load batch_size training examples in each training iteration
    batches <- mnist$train$next_batch(base::as.integer(batch_size))
    batch_xs <- batches[[1]]
    batch_ys <- batches[[2]]
    # Run optimization op (backprop) and cost op (to get loss value)
    sess$run(optimizer, feed_dict = dict(x = batch_xs, y = batch_ys))
    
    # Compute average loss
    avg_cost <- avg_cost + sess$run(cost, feed_dict = dict(x = batch_xs, y = batch_ys)) / total_batch
  }
  
  # record avg_cost for each epoch
  cost_val[epoch,] <- base::c(epoch, avg_cost)
  
  # Display logs per epoch step
  if((epoch %% display_step) == 0) {
    base::print(base::sprintf("Epoch: %04d, cost: %.9f", epoch, avg_cost))
  }
  
}
 
base::print("Optimization Finished!")
 
## test trained model
correct_prediction <- tensorflow::tf$equal(tensorflow::tf$argmax(pred, 1L), tensorflow::tf$argmax(y, 1L))
 
## calculate accuracy
accuracy <- tensorflow::tf$reduce_mean(tensorflow::tf$cast(correct_prediction, tensorflow::tf$float32))
res <- base::sprintf("Accuracy: %.5f", sess$run(accuracy, feed_dict = dict(x = mnist$test$images, y = mnist$test$labels)))
base::print(res)
 
## plotting with Plotly
<- plotly::plot_ly(data = cost_val,
                     x = ~Epoch,
                     y = ~Cost,
                     type = 'scatter',
                     mode = 'lines+markers',
                     line = list(color = 'rgb(205, 12, 24)', width = 3)) %>%
  layout(title = "Cost Function Value as Epoch Proceeds",
         xaxis = list(title = "Epoch"),
         yaxis = list (title = "Cost"))
 
# print results
base::print(p)
cs


Comments