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

Scientific Computing & Data Science

[Artificial Intelligence / TensorFlow] Convolutional Neural Network를 이용한 손글씨 숫자 인식 본문

Artificial Intelligence/TensorFlow

[Artificial Intelligence / TensorFlow] Convolutional Neural Network를 이용한 손글씨 숫자 인식

cinema4dr12 2017. 7. 15. 20:14

Written by Geol Choi | 


이번 포스팅에서는 회선신경망(Convolutional Neural Network; CNN)을 이용하여 손글씨 숫자를 학습시키는 코드를 Pytnon과 R 각각에 대하여 TensorFlow에서 어떻게 구현할 수 있는지 알아보도록 한다.

1. Python-TensorFlow

준비 중...


2. R-TensorFlow

2.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

2.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


MINST 데이터세트와 관련하여서는 지난 번 포스팅에 좀 더 자세히 설명하였다.

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.001
training_iters <- 200000
batch_size <- 128L
display_step <- 10
cs


다음은 회선신경망의 네트워크와 관련된 파라미터를 정의한 코드이다:


1
2
3
4
## network parameters
n_input <- 784 # MNIST data input (img shape: 28*28)
n_classes <- 10L # MNIST total classes (0-9 digits)
dropout <- 0.75 # Dropout, probability to keep units
cs


n_input은 입력의 크기, 즉 MNIST 손글씨 숫자 이미지 한 장 당 픽셀 개수(= 28×28 = 784), n_classes는 Class 개수(0-9의 숫자), dropout은 과적합(Overfitting)을 방지하기 위해 캐나다 Toronto 대학교의 G. Hinton교수팀이 고안한 방법(확률에 따라 랜덤으로 몇 개의 Neuron Network 연결을 끊는 방식으로 학습)에 대한 확률값을 의미한다.
주의할 점은, R-TensorFlow의 함수의 파라미터 중 Integer Type의 4-bit와 8-bit를 정수 뒤에 "L"을 붙여서 구분하는 것들이 있으므로, batch_sizen_classes에 숫자 뒤에 "L"을 붙여 4-bit 형의 정수로 정의하였다.

2.4. weights & biases 변수 정의

weightsbiases 변수를  R의 list() 타입으로 정의하였다. 특히, weights는 CNN에서 Filter의 개념을 가지므로 Filter의 사이즈, 입력, 출력(Depth)을 정의해야 한다.
다음 코드는 weights와 biases 변수를 정의한 것이다:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
## store layers weight & bias
weights <- base::list(
  # 5x5 conv, 1 input, 32 outputs
  'wc1'= tensorflow::tf$Variable(tf$random_normal(base::c(5L, 5L, 1L, 32L)), dtype=tf$float32),
  # 5x5 conv, 32 inputs, 64 outputs
  'wc2'= tensorflow::tf$Variable(tf$random_normal(base::c(5L, 5L, 32L, 64L)), dtype=tf$float32),
  # fully connected, 7*7*64 inputs, 1024 outputs
  'wd1'= tensorflow::tf$Variable(tf$random_normal(base::c(7L*7L*64L, 1024L)), dtype=tf$float32),
  # 1024 inputs, 10 outputs (class prediction)
  'out'= tensorflow::tf$Variable(tf$random_normal(base::c(1024L, n_classes)), dtype=tf$float32))
 
biases <- base::list(
  'bc1'= tensorflow::tf$Variable(stats::rnorm(n=32), dtype=tf$float32),
  'bc2'= tensorflow::tf$Variable(stats::rnorm(n=64), dtype=tf$float32),
  'bd1'= tensorflow::tf$Variable(stats::rnorm(n=1024), dtype=tf$float32),
  'out'= tensorflow::tf$Variable(stats::rnorm(n=n_classes), dtype=tf$float32))
cs


weights의  list 항목은 'wc1', 'wc2', 'wd1', 'out'는 
tf$random_normal() 함수를 이용하여 정의된 Dimension(또는 Shape)으로  정규분포의 난수(Random Number)를 발생시킨다. 가령, 'wc1'은 [5, 5, 1, 32] 크기의 난수인데 각각 Filter의 높이(Height), 폭(Width), 입력 크기, 출력 크기를 의미한다.
한편, biaseslist 항목은 각 레이어(Layer)의 바이어스이며 각 해당 레이어의 출력 크기와 동일한 크기로 정규분포의 난수를 갖는다.

2.5. Placeholder 변수 정의

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

1
2
3
4
## tf Graph input
<- tensorflow::tf$placeholder(tensorflow::tf$float32, tensorflow::shape(NULL, n_input))
<- tensorflow::tf$placeholder(tensorflow::tf$float32, tensorflow::shape(NULL, n_classes))
keep_prob <- tensorflow::tf$placeholder(tensorflow::tf$float32) #dropout (keep probability)
cs

2.6. conv2d 함수 정의

우선 작성된 conv2d() 함수를 살펴보도록 한다:

1
2
3
4
5
6
7
## function : create some wrappers for simplicity
conv2d <- function(x, W, b, strides=1L) {
  # conv2D wrapper, with bias and relu activation
  x <- tensorflow::tf$nn$conv2d(x, W, strides=base::c(1L, strides, strides, 1L), padding='SAME')
  x <- tensorflow::tf$nn$bias_add(x, b)
  return(tensorflow::tf$nn$relu(x))
}
cs


입력으로 x(Input), W(Weights, 회선신경망에서는 Filter라고도 함), b(Biases), strides(회선 Filter의 이동 간격, 함수 파라미터를 정의하지 않을 경우 기본값으로 1을 지정)를 받는다.

Line 4에서 TensorFlow의 tensorflow::tf$nn$conv2d() 함수로 파라미터 값을 넘겨주는데, 마지막 파라미터인 padding="SAME"은 입력 이미지와 회선 필터링 후의 이미지의 크기가 동일하게 유지할 수 있도록 Zero-padding의 크기를 정한다는 의미이다.


가령, 입력 이미지 크기(W), 필터 크기(F), Stride(S), Zero-padding(P), 출력 이미지 크기(V) 사이에는 다음과 같은 관계가 성립하므로,

V = (W - F + 2P)/S + 1

이 식을 이용하여 P 값을 계산할 수도 있다.


Line 5는 Line 4에서 계산된 결과에 Bias(b)를 더한 후, Line 6에서는 이 값에 ReLU(Rectified Linear Unit)를 적용한 결과를 함수 결과값으로 리턴한다.

2.7. maxpool2d 함수 정의

maxpool2d() 함수는 TensorFlow의 tensorflow::tf$nn$max_pool() 함수에 필요한 값을 넘겨주기 위한 Wrapper 함수이다:

1
2
3
4
## function : maxPool2D wrapper
maxpool2d <- function(x, k=2) {
  return(tensorflow::tf$nn$max_pool(x, ksize=base::c(1, k, k, 1), strides=base::c(1, k, k, 1), padding='SAME'))
}
cs


Max Pooling을 위한 Filter 크기는 일반적으로 2
×2이나, 필요에 따라 얼마든지 확장이 가능하다.

2.8. conv_net 함수 정의

conv_net() 함수는 전체 코드 중 가장 핵심이 되는 함수인데, 회선신경망의 전체 네트워크 구조를 이 함수에서 정의하기 때문이다.
우선, 해당 코드를 살펴보자:

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
## function : create model
conv_net <- function(x, weights, biases, dropout) {
  # reshape input picture
  x <- tensorflow::tf$reshape(x, shape=base::c(-1L, 28L, 28L, 1L))
  
  # convolution layer
  conv1 <- conv2d(x, weights$wc1, biases$bc1)
  
  # max pooling (down-sampling)
  conv1 <- maxpool2d(conv1, k=2)
  
  # convolution layer
  conv2 <- conv2d(conv1, weights$wc2, biases$bc2)
  
  # max pooling (down-sampling)
  conv2 <- maxpool2d(conv2, k=2)
  
  # fully connected layer
  # reshape conv2 output to fit fully connected layer input
  fc1 <- tensorflow::tf$reshape(conv2, shape=base::c(-1L, weights$wd1$get_shape()$as_list()[1]))
  
  fc1 <- tensorflow::tf$add(tf$matmul(fc1, weights$wd1), biases$bd1)
  fc1 <- tensorflow::tf$nn$relu(fc1)
  
  # apply dropout
  fc1 <- tensorflow::tf$nn$dropout(fc1, dropout)
  
  # Output, class prediction
  out <- tensorflow::tf$add(tf$matmul(fc1, weights$out), biases$out)
  return(out)
}
cs


Line 4: 784개의 일렬로 배열된 픽셀 밝기값(입력 데이터)을 tensorflow::tf$reshape() 함수를 이용하여 28(가로 해상도)×28(세로 해상도)×1(컬러 Depth)로 재배열한다. 첫번째 크기(-1L)은 Dimension을 정해지지 않아 그 값을 알 수 없을 경우 사용한다.


Line 7: 본 코드에서 정의한 첫번째 레이어는 Convolution Layer(conv1)으로, 섹션 2.6에서 정의한 함수 conv2d()의 입력 파라미터 x, weights$wc1, biases$bc1의 텐서 차원(Tensor Dimension)은 다음과 같다:

  • x: [-1, 28, 28, 1]

  • weights$wc1: [5, 5, 1, 32]

  • biases$bc1: [32]

만약 차원을 알고 싶을 경우, 콘솔에서 변수를 입력하면 된다:

> print(weights$wc1)
Variable(shape=(5, 5, 1, 32), dtype=float32_ref)

> print(biases$bc1)
Variable(shape=(32,), dtype=float32_ref)


Line 10: Line 7에서 계산된 conv1을 2×2 Window Size로 Max Pooling한다. 2×2 Max Pooling을 한 결과 가로, 세로 1/2씩 다운-샘플링(Down-sampling)되므로 conv1의 차원은 [?, 14, 14, 32]가 된다. 즉, 14는 원래 해상도 28의 1/2이 된 것이며 32는 'wc1'과의 Convolution 연산에 의한 출력 크기이다.


Line 4~16의 일련의 과정을 계산 과정에서 얻는 변수의 차원은 다음과 같다:

> x <- tensorflow::tf$placeholder(tensorflow::tf$float32, tensorflow::shape(NULL, n_input)) > print(x) Tensor("Placeholder_6:0", shape=(?, 784), dtype=float32) > x <- tensorflow::tf$reshape(x, shape=base::c(-1L, 28L, 28L, 1L)) > print(x) Tensor("Reshape_11:0", shape=(?, 28, 28, 1), dtype=float32) > conv1 <- conv2d(x, weights$wc1, biases$bc1) > print(conv1) Tensor("Relu_7:0", shape=(?, 28, 28, 32), dtype=float32) > conv1 <- maxpool2d(conv1, k=2) > print(conv1) Tensor("MaxPool_5:0", shape=(?, 14, 14, 32), dtype=float32) > conv2 <- conv2d(conv1, weights$wc2, biases$bc2) > print(conv2) Tensor("Relu_8:0", shape=(?, 14, 14, 64), dtype=float32) > conv2 <- maxpool2d(conv2, k=2) > print(conv2) Tensor("MaxPool_6:0", shape=(?, 7, 7, 64), dtype=float32)

Line 20: Line 4~16을 통해 얻은 conv2를 Fully Connected Layer의 입력값으로 변환한다. 즉, 차원 [?, 7, 7, 64]를 7×7×64(=3136)개의 입력값으로 변환하여 Fully Connected Layer인 fc1에 공급한다.
weights$wd1의 차원은 [3136, 1024]인데,

> print(weights$wd1)
Variable(shape=(3136, 1024), dtype=float32_ref)


weights$wd1$get_shape()$as_list()[1]은 weights$wd1의 첫번째 차원값인 3136이다:

> print(weights$wd1$get_shape()$as_list()[1])
[1] 3136


Line 22: Fully Connected Layer는 일반적인 Multi-Layer Perceptron(MLP)와 같이 주어진 입력과 가중치(Weights)의 곱에 바이어스(Biases)를 더하는 방식으로 계산한다. fc1, weights$wd1, biases$bd1의 차원은 다음과 같으며:

> print(fc1)
Tensor("Reshape_13:0", shape=(?, 3136), dtype=float32)

> print(weights$wd1)
Variable(shape=(3136, 1024), dtype=float32_ref)

> print(biases$bd1)
Variable(shape=(1024,), dtype=float32_ref)


fc1 <- tensorflow::tf$add(tf$matmul(fc1, weights$wd1), biases$bd1)의 계산결과 fc1의 차원은:

> fc1 <- tensorflow::tf$add(tf$matmul(fc1, weights$wd1), biases$bd1)
> print(fc1)
Tensor("Add_4:0", shape=(?, 1024), dtype=float32)

과 같이 1024가 된다.


Line 23: 앞서 계산한 Fully Connected Layer, fc1에 ReLU 연산을 한 것이며 차원은 동일하게 유지된다:


> fc1 <- tensorflow::tf$nn$relu(fc1)
> print(fc1)
Tensor("Relu_9:0", shape=(?, 1024), dtype=float32)


Line 26: Line 23에서 계산한 결과에 Dropout을 적용하여 Overfitting을 방지한다. Dropout 연산 후에도 fc1의 차원 유지된다:

> fc1 <- tensorflow::tf$nn$dropout(fc1, dropout)
> print(fc1)
Tensor("dropout_2/mul:0", shape=(?, 1024), dtype=float32)


Line 29: conv_net()의 마지막 단계로 Class Prediction을 하는데 이 역시 Fully Connected Layer이다:

> out <- tensorflow::tf$add(tf$matmul(fc1, weights$out), biases$out)
> print(out)
Tensor("Add_5:0", shape=(?, 10), dtype=float32)


0~9의 손글씨 숫자를 분류하는 것이므로 out의 차원은 10개가 되는 것이 맞다.

지금까지 언급한 네트워크를 정리하여 도식화하면 다음 이미지와 같다:


2.9. 모델 세우기

예측 모델(Predictor)는 섹션 2.7의 conv_net() 함수에 정의된 네트워크와 같다:

1
2
## construct model
pred <- conv_net(x, weights, biases, keep_prob)
cs

2.10. 손실함수 및 Optmizer 정의

손실함수(또는 Cost)는 Log Probability와 Ground Truth Label 간의 Softmax Cross-entropy를 계산하여 정의하며, 함수 tensorflow::tf$nn$softmax_cross_entropy_with_logits()를 이용한다. 

Optmizer는 AdamOptmizer로 정했으며 함수 tensorflow::tf$train$AdamOptimizer()를 이용하여 설정한다:

1
2
3
## define loss and optimizer
cost <- tensorflow::tf$reduce_mean(tensorflow::tf$nn$softmax_cross_entropy_with_logits(logits=pred, labels=y))
optimizer <- tensorflow::tf$train$AdamOptimizer(learning_rate=learning_rate)$minimize(cost)
cs

2.11. 모델 평가

tensorflow::tf$argmax() 함수는 주어진 Input Tensor의 최대값이 위치하는 인덱스(Index)를 반환한다. 이를 이용하여 모델(또는 Predictor)을 통해 얻은 분류값과 Ground Truth Label의 값을 tensorflow::tf$equal() 함수를 이용하여 비교한다:

1
correct_pred <- tensorflow::tf$equal(tensorflow::tf$argmax(pred, 1L), tensorflow::tf$argmax(y, 1L))
cs


tensorflow::tf$equal() 함수는 Boolean 데이터를 결과값으로 반환하며, 정확도 계산을 위해 tensorflow::tf$cast() 함수를 통해 Boolean 데이터형을 float32 데이터 형으로 변환한다(즉, true는 1.0, false는 0.0으로 변환된다):


1
tensorflow::tf$cast(correct_pred, tensorflow::tf$float32)
cs

 
그리고 이 결과를 tensorflow::tf$reduce_mean() 함수를 이용하여 float32로 변환된 값들의 평균값을 구하여 정확도를 계산한다:

1
accuracy <- tensorflow::tf$reduce_mean(tensorflow::tf$cast(correct_pred, tensorflow::tf$float32))
cs

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

학습 Epoch에 따라 정확도(Accuracy)와 손실(Loss)값을 그래프로 시각화하기 위해 Data Frame 변수를 초기화한다:

1
2
3
4
## initialize variable for plotting
learning_val <- base::matrix(data=0.0, nrow=1, ncol=3)
learning_val <- base::as.data.frame(learning_val)
base::names(learning_val) <- base::c("Step""Loss""Accuracy")
cs

 
글로벌 변수 초기화를 하고,

1
2
## initializing the variables
init <- tensorflow::tf$global_variables_initializer()
cs


TensorFlow 세션을 초기화한다.


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

2.13. 학습

일반적으로 데이터 사이즈가 너무 큰 경우 데이터를 통째로 메모리에 올려놓고 학습시키기가 불가능 경우가 있다. 이러한 경우 전체 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
## launch the graph
step <- 1
# Keep training until reach max iterations
while((step * batch_size) < training_iters) {
  batches <- mnist$train$next_batch(batch_size)
  batch_x <- batches[[1L]]
  batch_y <- batches[[2L]]
  
  # Run optimization op (backprop)
  sess$run(optimizer, feed_dict= dict(x=batch_x, y=batch_y, keep_prob=dropout))
  
  if(step %% display_step == 0) {
    # Calculate batch loss and accuracy
    loss <- sess$run(cost, feed_dict=dict(x=batch_x, y=batch_y, keep_prob=1.))
    acc <- sess$run(accuracy, feed_dict=dict(x=batch_x, y=batch_y, keep_prob=1.))
    
    disp_res <- base::sprintf("Iter: %d, Minibatch Loss= %f, Training Accuracy= %f", (step*batch_size), loss, acc)
    base::print(disp_res)
  }
  
  # record loss & acc for each epoch
  loss <- sess$run(cost, feed_dict=dict(x=batch_x, y=batch_y, keep_prob=1.))
  acc <- sess$run(accuracy, feed_dict=dict(x=batch_x, y=batch_y, keep_prob=1.))
  learning_val <- base::rbind(learning_val, base::c(step, loss, acc))
  
  step <- step + 1
}
cs



Line 5-7: batch_size는 128로 지정되어 있으므로 매 step에서 MNIST 데이터세트의 이미지 데이터를 128개씩 불러온 후, batch_x(이미지 픽셀 데이터 벡터)와 batch_y(클래스 라벨)에 분배한다.

Line 10: optimizer를 실행한다.

Line 12-19: 일정한 주기(display_step)로 loss(손실함수 값), acc(정확도)를 계산을 실행하고, 이를 콘솔에 출력한다.

Line 21-24: 매 Epoch에 대한 lossacc를 저장한다.

Epoch가 진행됨에 따라 계산된 출력 결과는 다음과 같았다:


[1] "Iter: 1280, Minibatch Loss= 20335.136719, Training Accuracy= 0.250000"
[1] "Iter: 2560, Minibatch Loss= 11962.062500, Training Accuracy= 0.445312"
[1] "Iter: 3840, Minibatch Loss= 5404.224609, Training Accuracy= 0.703125"
[1] "Iter: 5120, Minibatch Loss= 4878.337891, Training Accuracy= 0.703125"
[1] "Iter: 6400, Minibatch Loss= 2902.394775, Training Accuracy= 0.828125"
[1] "Iter: 7680, Minibatch Loss= 2034.631714, Training Accuracy= 0.828125"
[1] "Iter: 8960, Minibatch Loss= 1791.225830, Training Accuracy= 0.867188"
[1] "Iter: 10240, Minibatch Loss= 3175.451172, Training Accuracy= 0.820312"
[1] "Iter: 11520, Minibatch Loss= 3337.278320, Training Accuracy= 0.867188"
[1] "Iter: 12800, Minibatch Loss= 1615.411377, Training Accuracy= 0.867188"
[1] "Iter: 14080, Minibatch Loss= 1375.206055, Training Accuracy= 0.898438"
[1] "Iter: 15360, Minibatch Loss= 2572.112793, Training Accuracy= 0.898438"
[1] "Iter: 16640, Minibatch Loss= 1403.623779, Training Accuracy= 0.898438"
[1] "Iter: 17920, Minibatch Loss= 1326.962402, Training Accuracy= 0.906250"
[1] "Iter: 19200, Minibatch Loss= 1349.029053, Training Accuracy= 0.929688"
[1] "Iter: 20480, Minibatch Loss= 784.393921, Training Accuracy= 0.914062"
[1] "Iter: 21760, Minibatch Loss= 1443.853638, Training Accuracy= 0.890625"
[1] "Iter: 23040, Minibatch Loss= 1365.083252, Training Accuracy= 0.914062"
[1] "Iter: 24320, Minibatch Loss= 1655.800293, Training Accuracy= 0.929688"
[1] "Iter: 25600, Minibatch Loss= 2039.616333, Training Accuracy= 0.882812"
[1] "Iter: 26880, Minibatch Loss= 520.331726, Training Accuracy= 0.945312"
[1] "Iter: 28160, Minibatch Loss= 1112.807739, Training Accuracy= 0.898438"
[1] "Iter: 29440, Minibatch Loss= 641.026428, Training Accuracy= 0.929688"
[1] "Iter: 30720, Minibatch Loss= 1955.473999, Training Accuracy= 0.859375"
[1] "Iter: 32000, Minibatch Loss= 643.833740, Training Accuracy= 0.945312"
[1] "Iter: 33280, Minibatch Loss= 1357.251343, Training Accuracy= 0.914062"
[1] "Iter: 34560, Minibatch Loss= 1194.265259, Training Accuracy= 0.945312"
[1] "Iter: 35840, Minibatch Loss= 804.138489, Training Accuracy= 0.937500"
[1] "Iter: 37120, Minibatch Loss= 835.673584, Training Accuracy= 0.945312"
[1] "Iter: 38400, Minibatch Loss= 1095.207642, Training Accuracy= 0.921875"
[1] "Iter: 39680, Minibatch Loss= 1187.257324, Training Accuracy= 0.953125"
[1] "Iter: 40960, Minibatch Loss= 701.013062, Training Accuracy= 0.914062"
[1] "Iter: 42240, Minibatch Loss= 900.857788, Training Accuracy= 0.929688"
[1] "Iter: 43520, Minibatch Loss= 406.125610, Training Accuracy= 0.960938"
[1] "Iter: 44800, Minibatch Loss= 240.219833, Training Accuracy= 0.976562"
[1] "Iter: 46080, Minibatch Loss= 668.026001, Training Accuracy= 0.945312"
[1] "Iter: 47360, Minibatch Loss= 985.974487, Training Accuracy= 0.929688"
[1] "Iter: 48640, Minibatch Loss= 456.339355, Training Accuracy= 0.960938"
[1] "Iter: 49920, Minibatch Loss= 535.215149, Training Accuracy= 0.960938"
[1] "Iter: 51200, Minibatch Loss= 251.570831, Training Accuracy= 0.953125"
[1] "Iter: 52480, Minibatch Loss= 461.323486, Training Accuracy= 0.960938"
[1] "Iter: 53760, Minibatch Loss= 872.417358, Training Accuracy= 0.937500"
[1] "Iter: 55040, Minibatch Loss= 1141.435425, Training Accuracy= 0.921875"
[1] "Iter: 56320, Minibatch Loss= 480.865845, Training Accuracy= 0.968750"
[1] "Iter: 57600, Minibatch Loss= 104.679344, Training Accuracy= 0.976562"
[1] "Iter: 58880, Minibatch Loss= 710.835938, Training Accuracy= 0.929688"
[1] "Iter: 60160, Minibatch Loss= 395.271912, Training Accuracy= 0.960938"
[1] "Iter: 61440, Minibatch Loss= 449.800873, Training Accuracy= 0.953125"
[1] "Iter: 62720, Minibatch Loss= 600.673584, Training Accuracy= 0.945312"
[1] "Iter: 64000, Minibatch Loss= 333.473541, Training Accuracy= 0.960938"
[1] "Iter: 65280, Minibatch Loss= 932.910522, Training Accuracy= 0.929688"
[1] "Iter: 66560, Minibatch Loss= 620.210327, Training Accuracy= 0.960938"
[1] "Iter: 67840, Minibatch Loss= 526.523132, Training Accuracy= 0.968750"
[1] "Iter: 69120, Minibatch Loss= 630.549194, Training Accuracy= 0.953125"
[1] "Iter: 70400, Minibatch Loss= 489.275299, Training Accuracy= 0.921875"
[1] "Iter: 71680, Minibatch Loss= 1040.607178, Training Accuracy= 0.937500"
[1] "Iter: 72960, Minibatch Loss= 443.261841, Training Accuracy= 0.953125"
[1] "Iter: 74240, Minibatch Loss= 678.192566, Training Accuracy= 0.953125"
[1] "Iter: 75520, Minibatch Loss= 510.402588, Training Accuracy= 0.953125"
[1] "Iter: 76800, Minibatch Loss= 319.151276, Training Accuracy= 0.960938"
[1] "Iter: 78080, Minibatch Loss= 648.258911, Training Accuracy= 0.953125"
[1] "Iter: 79360, Minibatch Loss= 321.074341, Training Accuracy= 0.945312"
[1] "Iter: 80640, Minibatch Loss= 294.397339, Training Accuracy= 0.968750"
[1] "Iter: 81920, Minibatch Loss= 120.339096, Training Accuracy= 0.976562"
[1] "Iter: 83200, Minibatch Loss= 171.686920, Training Accuracy= 0.976562"
[1] "Iter: 84480, Minibatch Loss= 402.902832, Training Accuracy= 0.960938"
[1] "Iter: 85760, Minibatch Loss= 543.468628, Training Accuracy= 0.937500"
[1] "Iter: 87040, Minibatch Loss= 213.812210, Training Accuracy= 0.945312"
[1] "Iter: 88320, Minibatch Loss= 387.859314, Training Accuracy= 0.968750"
[1] "Iter: 89600, Minibatch Loss= 141.311829, Training Accuracy= 0.968750"
[1] "Iter: 90880, Minibatch Loss= 56.128281, Training Accuracy= 0.992188"
[1] "Iter: 92160, Minibatch Loss= 563.708374, Training Accuracy= 0.953125"
[1] "Iter: 93440, Minibatch Loss= 268.617188, Training Accuracy= 0.968750"
[1] "Iter: 94720, Minibatch Loss= 549.013489, Training Accuracy= 0.937500"
[1] "Iter: 96000, Minibatch Loss= 775.831116, Training Accuracy= 0.937500"
[1] "Iter: 97280, Minibatch Loss= 649.539307, Training Accuracy= 0.945312"
[1] "Iter: 98560, Minibatch Loss= 556.840210, Training Accuracy= 0.960938"
[1] "Iter: 99840, Minibatch Loss= 74.274635, Training Accuracy= 0.953125"
[1] "Iter: 101120, Minibatch Loss= 390.300446, Training Accuracy= 0.945312"
[1] "Iter: 102400, Minibatch Loss= 484.180542, Training Accuracy= 0.929688"
[1] "Iter: 103680, Minibatch Loss= 463.259613, Training Accuracy= 0.945312"
[1] "Iter: 104960, Minibatch Loss= 253.442520, Training Accuracy= 0.960938"
[1] "Iter: 106240, Minibatch Loss= 138.249908, Training Accuracy= 0.984375"
[1] "Iter: 107520, Minibatch Loss= 494.293640, Training Accuracy= 0.945312"
[1] "Iter: 108800, Minibatch Loss= 54.204742, Training Accuracy= 0.976562"
[1] "Iter: 110080, Minibatch Loss= 230.673004, Training Accuracy= 0.953125"
[1] "Iter: 111360, Minibatch Loss= 153.589233, Training Accuracy= 0.976562"
[1] "Iter: 112640, Minibatch Loss= 513.414246, Training Accuracy= 0.953125"
[1] "Iter: 113920, Minibatch Loss= 201.940323, Training Accuracy= 0.976562"
[1] "Iter: 115200, Minibatch Loss= 404.528259, Training Accuracy= 0.953125"
[1] "Iter: 116480, Minibatch Loss= 420.162598, Training Accuracy= 0.960938"
[1] "Iter: 117760, Minibatch Loss= 305.276550, Training Accuracy= 0.968750"
[1] "Iter: 119040, Minibatch Loss= 132.262375, Training Accuracy= 0.976562"
[1] "Iter: 120320, Minibatch Loss= 267.570801, Training Accuracy= 0.953125"
[1] "Iter: 121600, Minibatch Loss= 52.275009, Training Accuracy= 0.976562"
[1] "Iter: 122880, Minibatch Loss= 291.248230, Training Accuracy= 0.960938"
[1] "Iter: 124160, Minibatch Loss= 114.250519, Training Accuracy= 0.976562"
[1] "Iter: 125440, Minibatch Loss= 80.128906, Training Accuracy= 0.992188"
[1] "Iter: 126720, Minibatch Loss= 203.230560, Training Accuracy= 0.968750"
[1] "Iter: 128000, Minibatch Loss= 210.270920, Training Accuracy= 0.976562"
[1] "Iter: 129280, Minibatch Loss= 632.585876, Training Accuracy= 0.937500"
[1] "Iter: 130560, Minibatch Loss= 213.884750, Training Accuracy= 0.984375"
[1] "Iter: 131840, Minibatch Loss= 570.522522, Training Accuracy= 0.929688"
[1] "Iter: 133120, Minibatch Loss= 86.986229, Training Accuracy= 0.984375"
[1] "Iter: 134400, Minibatch Loss= 96.486275, Training Accuracy= 0.984375"
[1] "Iter: 135680, Minibatch Loss= 555.334351, Training Accuracy= 0.945312"
[1] "Iter: 136960, Minibatch Loss= 198.607025, Training Accuracy= 0.960938"
[1] "Iter: 138240, Minibatch Loss= 192.673523, Training Accuracy= 0.960938"
[1] "Iter: 139520, Minibatch Loss= 278.940277, Training Accuracy= 0.953125"
[1] "Iter: 140800, Minibatch Loss= 178.594879, Training Accuracy= 0.960938"
[1] "Iter: 142080, Minibatch Loss= 246.488937, Training Accuracy= 0.960938"
[1] "Iter: 143360, Minibatch Loss= 237.367874, Training Accuracy= 0.976562"
[1] "Iter: 144640, Minibatch Loss= 381.476562, Training Accuracy= 0.945312"
[1] "Iter: 145920, Minibatch Loss= 325.190857, Training Accuracy= 0.937500"
[1] "Iter: 147200, Minibatch Loss= 76.517715, Training Accuracy= 0.984375"
[1] "Iter: 148480, Minibatch Loss= 366.191833, Training Accuracy= 0.945312"
[1] "Iter: 149760, Minibatch Loss= 228.685226, Training Accuracy= 0.976562"
[1] "Iter: 151040, Minibatch Loss= 291.989990, Training Accuracy= 0.960938"
[1] "Iter: 152320, Minibatch Loss= 61.041199, Training Accuracy= 0.976562"
[1] "Iter: 153600, Minibatch Loss= 0.000000, Training Accuracy= 1.000000"
[1] "Iter: 154880, Minibatch Loss= 169.244202, Training Accuracy= 0.968750"
[1] "Iter: 156160, Minibatch Loss= 87.681366, Training Accuracy= 0.968750"
[1] "Iter: 157440, Minibatch Loss= 342.857117, Training Accuracy= 0.976562"
[1] "Iter: 158720, Minibatch Loss= 213.146194, Training Accuracy= 0.968750"
[1] "Iter: 160000, Minibatch Loss= 173.320709, Training Accuracy= 0.976562"
[1] "Iter: 161280, Minibatch Loss= 427.177460, Training Accuracy= 0.945312"
[1] "Iter: 162560, Minibatch Loss= 52.099319, Training Accuracy= 0.984375"
[1] "Iter: 163840, Minibatch Loss= 197.401703, Training Accuracy= 0.960938"
[1] "Iter: 165120, Minibatch Loss= 137.070557, Training Accuracy= 0.976562"
[1] "Iter: 166400, Minibatch Loss= 1.327835, Training Accuracy= 0.992188"
[1] "Iter: 167680, Minibatch Loss= 89.374939, Training Accuracy= 0.976562"
[1] "Iter: 168960, Minibatch Loss= 281.052307, Training Accuracy= 0.984375"
[1] "Iter: 170240, Minibatch Loss= 88.095436, Training Accuracy= 0.992188"
[1] "Iter: 171520, Minibatch Loss= 42.090000, Training Accuracy= 0.984375"
[1] "Iter: 172800, Minibatch Loss= 170.906906, Training Accuracy= 0.976562"
[1] "Iter: 174080, Minibatch Loss= 48.270493, Training Accuracy= 0.992188"
[1] "Iter: 175360, Minibatch Loss= 398.049744, Training Accuracy= 0.960938"
[1] "Iter: 176640, Minibatch Loss= 18.061783, Training Accuracy= 0.984375"
[1] "Iter: 177920, Minibatch Loss= 63.081963, Training Accuracy= 0.976562"
[1] "Iter: 179200, Minibatch Loss= 193.382446, Training Accuracy= 0.984375"
[1] "Iter: 180480, Minibatch Loss= 100.950073, Training Accuracy= 0.960938"
[1] "Iter: 181760, Minibatch Loss= 150.346512, Training Accuracy= 0.960938"
[1] "Iter: 183040, Minibatch Loss= 98.533684, Training Accuracy= 0.976562"
[1] "Iter: 184320, Minibatch Loss= 304.908112, Training Accuracy= 0.968750"
[1] "Iter: 185600, Minibatch Loss= 155.834137, Training Accuracy= 0.976562"
[1] "Iter: 186880, Minibatch Loss= 100.850540, Training Accuracy= 0.968750"
[1] "Iter: 188160, Minibatch Loss= 107.417297, Training Accuracy= 0.976562"
[1] "Iter: 189440, Minibatch Loss= 53.540146, Training Accuracy= 0.976562"
[1] "Iter: 190720, Minibatch Loss= 270.730225, Training Accuracy= 0.960938"
[1] "Iter: 192000, Minibatch Loss= 234.051926, Training Accuracy= 0.968750"
[1] "Iter: 193280, Minibatch Loss= 20.662758, Training Accuracy= 0.984375"
[1] "Iter: 194560, Minibatch Loss= 200.396622, Training Accuracy= 0.960938"
[1] "Iter: 195840, Minibatch Loss= 135.467194, Training Accuracy= 0.984375"
[1] "Iter: 197120, Minibatch Loss= 39.573303, Training Accuracy= 0.984375"
[1] "Iter: 198400, Minibatch Loss= 107.404747, Training Accuracy= 0.953125"
[1] "Iter: 199680, Minibatch Loss= 323.573303, Training Accuracy= 0.953125"
[1] "Optimization Finished!"
[1] "Testing Accuracy: 0.976562"

2.14. 결과 출력

계산된 Predictor를 이용하여 테스트 데이터세트에 대한 정확도를 평가하고 이를 출력한다:

1
2
3
4
## calculate accuracy for 256 mnist test images
acc <- sess$run(accuracy, feed_dict=dict(x=mnist$test$images[1:256,], y=mnist$test$labels[1:256,], keep_prob=1.))
acc_res <- base::sprintf("Testing Accuracy: %f", acc)
base::print(acc_res)
cs


마지막으로, Epoch의 진행에 따른 lossacc 계산 결과를 그래프로 출력한다:

1
2
3
4
5
6
7
8
9
10
11
12
13
## plotting with Plotly
learning_val <- learning_val[-1,]
 
<- learning_val %>%
  tidyr::gather(variable, value, -Step) %>%
  transform(id = as.integer(factor(variable))) %>%
  plot_ly(x = ~Step, y = ~value, color = ~variable, colors = "Dark2",
          yaxis = ~paste0("y", id)) %>%
  add_lines() %>%
  subplot(nrows = 2, shareX = TRUE)
 
# print results
base::print(p)
cs


Epoch vs Loss


Epoch vs Accuracy

2.15. 전체 코드

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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
#############################################################################
# convolutional Neural Network for Treatment Head State
#############################################################################
# @Author: Geol Choi, phD.(cinema4dr12@gmail.com)
# @Date: July 28, 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 image data
base::load("./Param.RData")
train.pixels <- as.matrix(param$train.pixels)
train.labels <- as.matrix(param$train.labes)
test.pixels <- as.matrix(param$test.pixels)
test.labels <- as.matrix(param$test.labels)
 
## define parameters
learning_rate <- 0.001
#training_iters <- 200000
training_iters <- 20000
 
batch_size <- 100L
display_step <- 10
 
## network parameters
n_input <- 4096L # data input (img shape: 64*64)
n_classes <- 3L # MNIST total classes (0-9 digits)
dropout <- 0.75 # Dropout, probability to keep units
early_stopping <- 0.99
 
## tf Graph input
<- tensorflow::tf$placeholder(tensorflow::tf$float32, tensorflow::shape(NULL, n_input))
<- tensorflow::tf$placeholder(tensorflow::tf$float32, tensorflow::shape(NULL, n_classes))
keep_prob <- tensorflow::tf$placeholder(tensorflow::tf$float32) #dropout (keep probability)
 
## store layers weight & bias
weights <- base::list(
  # 5x5 conv, 1 input, 32 outputs
  'wc1'= tensorflow::tf$Variable(tf$random_normal(base::c(5L, 5L, 1L, 32L)), dtype=tf$float32),
  # 5x5 conv, 32 inputs, 64 outputs
  'wc2'= tensorflow::tf$Variable(tf$random_normal(base::c(5L, 5L, 32L, 64L)), dtype=tf$float32),
  # fully connected, 16*16*64 inputs, 1024 outputs
  'wd1'= tensorflow::tf$Variable(tf$random_normal(base::c(16L*16L*64L, 1024L)), dtype=tf$float32),
  # 1024 inputs, 10 outputs (class prediction)
  'out'= tensorflow::tf$Variable(tf$random_normal(base::c(1024L, n_classes)), dtype=tf$float32))
 
biases <- base::list(
  'bc1'= tensorflow::tf$Variable(stats::rnorm(n=32), dtype=tf$float32),
  'bc2'= tensorflow::tf$Variable(stats::rnorm(n=64), dtype=tf$float32),
  'bd1'= tensorflow::tf$Variable(stats::rnorm(n=1024), dtype=tf$float32),
  'out'= tensorflow::tf$Variable(stats::rnorm(n=n_classes), dtype=tf$float32))
 
## function : create some wrappers for simplicity
conv2d <- function(x, W, b, strides=1L) {
  # conv2D wrapper, with bias and relu activation
  x <- tensorflow::tf$nn$conv2d(x, W, strides=base::c(1L, strides, strides, 1L), padding='SAME')
  x <- tensorflow::tf$nn$bias_add(x, b)
  return(tensorflow::tf$nn$relu(x))
}
 
## function : maxPool2D wrapper
maxpool2d <- function(x, k=2) {
  return(tensorflow::tf$nn$max_pool(x, ksize=base::c(1, k, k, 1), strides=base::c(1, k, k, 1), padding='SAME'))
}
 
## function : create model
conv_net <- function(x, weights, biases, dropout) {
  # reshape input picture
  x <- tensorflow::tf$reshape(x, shape=base::c(-1L, 64L, 64L, 1L))
  
  # convolution layer
  conv1 <- conv2d(x, weights$wc1, biases$bc1)
  
  # max pooling (down-sampling)
  conv1 <- maxpool2d(conv1, k=2)
  
  # convolution layer
  conv2 <- conv2d(conv1, weights$wc2, biases$bc2)
  
  # max pooling (down-sampling)
  conv2 <- maxpool2d(conv2, k=2)
  
  # fully connected layer
  # reshape conv2 output to fit fully connected layer input
  fc1 <- tensorflow::tf$reshape(conv2, shape=base::c(-1L, weights$wd1$get_shape()$as_list()[1]))
  
  fc1 <- tensorflow::tf$add(tf$matmul(fc1, weights$wd1), biases$bd1)
  fc1 <- tensorflow::tf$nn$relu(fc1)
  
  # apply dropout
  fc1 <- tensorflow::tf$nn$dropout(fc1, dropout)
  
  # Output, class prediction
  out <- tensorflow::tf$add(tf$matmul(fc1, weights$out), biases$out)
  return(out)
}
 
## construct model
pred <- conv_net(x, weights, biases, keep_prob)
 
## define loss and optimizer
cost <- tensorflow::tf$reduce_mean(tensorflow::tf$nn$softmax_cross_entropy_with_logits(logits=pred, labels=y))
optimizer <- tensorflow::tf$train$AdamOptimizer(learning_rate=learning_rate)$minimize(cost)
 
## evaluate model
correct_pred <- tensorflow::tf$equal(tensorflow::tf$argmax(pred, 1L), tensorflow::tf$argmax(y, 1L))
accuracy <- tensorflow::tf$reduce_mean(tensorflow::tf$cast(correct_pred, tensorflow::tf$float32))
 
## initialize variable for plotting
learning_val <- base::matrix(data=0.0, nrow=1, ncol=3)
learning_val <- base::as.data.frame(learning_val)
base::names(learning_val) <- base::c("Step""Loss""Accuracy")
 
## initializing the variables
init <- tensorflow::tf$global_variables_initializer()
 
## create session and initialize  variables
sess <- tensorflow::tf$Session()
sess$run(init)
 
## launch the graph
step <- 1
# Keep training until reach max iterations
while((step * batch_size) < training_iters) {
  batch_x <- train.pixels[(1+batch_size*(step-1)):(batch_size*step),]
  batch_y <- train.labels[(1+batch_size*(step-1)):(batch_size*step),]
  
  # Run optimization op (backprop)
  sess$run(optimizer, feed_dict= dict(x=batch_x, y=batch_y, keep_prob=dropout))
  
  if(step %% display_step == 0) {
    # Calculate batch loss and accuracy
    loss <- sess$run(cost, feed_dict=dict(x=batch_x, y=batch_y, keep_prob=1.))
    acc <- sess$run(accuracy, feed_dict=dict(x=batch_x, y=batch_y, keep_prob=1.))
    
    disp_res <- base::sprintf("Iter: %d, Minibatch Loss= %f, Training Accuracy= %f", (step*batch_size), loss, acc)
    base::print(disp_res)
  }
  
  # record avg_cost for each epoch
  loss <- sess$run(cost, feed_dict=dict(x=batch_x, y=batch_y, keep_prob=1.))
  acc <- sess$run(accuracy, feed_dict=dict(x=batch_x, y=batch_y, keep_prob=1.))
  learning_val <- base::rbind(learning_val, base::c(step, loss, acc))
  
  # early stopping
  if(acc >= early_stopping) {
    break
  }
  
  step <- step + 1
}
 
base::print("Optimization Finished!")
 
## calculate accuracy for test image dataset
acc <- sess$run(accuracy, feed_dict=dict(x=test.pixels, y=test.labels, keep_prob=1.))
acc_res <- base::sprintf("Testing Accuracy: %f", acc)
base::print(acc_res)
 
## plotting with Plotly
learning_val <- learning_val[-1,]
 
<- learning_val %>%
  tidyr::gather(variable, value, -Step) %>%
  transform(id = as.integer(factor(variable))) %>%
  plot_ly(x = ~Step, y = ~value, color = ~variable, colors = "Dark2", yaxis = ~paste0("y", id)) %>%
  add_lines() %>%
  subplot(nrows = 2, shareX = TRUE)
 
# print results
base::print(p)
cs


보너스

이제 이쯤에서 호기심이 발동할 수 있다. 과연 내가 손으로 쓴 숫자를 학습한 결과를 이용하여 인식할 수 있는가이다. 그래서 다음과 같이 검은 배경에 마우스로 하얀색 숫자를 썼다.



위의 이미지 이름을 "digit.png"로 하였고, R에서 이미지를 불러오고 Color Mode를 회색조(Grayscale)로  ("digit.png"는 현재의 Working Directory 내에 존재하야여 한다):

1
2
3
4
5
6
## import libraries
if (! ("EBImage" %in% rownames(installed.packages()))) { install.packages("EBImage") }
base::require(EBImage)
 
img <- EBImage::readImage("digit.png")
img <- EBImage::channel(x=img, mode="gray")
cs


이 이미지를 가로, 세로 28-by-28 크기로 크기를 재조정해야 하는데, 그냥 재조정할 경우 픽셀 간
계단 패턴으로 둔탁하게 나올 수 있으므로 우선 어느 정도 Blurring 처리를 하도록한다:

1
img_blurred <- EBImage::gblur(x=img, sigma = 20, radius = 9)
cs


이제 이미지 사이즈를 28-by-28로 조정한다:

1
img_resized <- EBImage::resize(x=img_blurred, w=28, h=28)
cs


크기를 조정한 이미지는 다음과 같다 (아래에 이미지는 포스팅 첨부용으로 실제 28-by-28 크기는 아니다):



조정된 이미지의 크기를 확인해 보자:

> length(img_vec)
[1] 784


784(=28 × 28)로 의도된 사이즈로 조정되었음을 알 수 있다. 이제 최종적으로 결과를 확인하는 코드를  보자:

1
2
3
4
5
image <- matrix(img_vec, nrow=1, ncol=784)
label <- matrix(0.0, nrow=1, ncol=10)
 
pred_res <- sess$run(pred, feed_dict=dict(x=image, y=label, keep_prob=1.))
print(which.max(pred_res) - 1)
cs


Line 1: 28-by-28 크기를 갖는 이미지를 1차원 벡터로 변환한다.
Line 2: labelpred에 공급할 변수로 미리 정의되어 있는 변수이다. 지금 과정에서는 아무 의미 없으므로 0.0의 값으로 1-by-10의 벡터로 정의하였다.
Line 3: 앞서 정의한 코드
 pred <- conv_net(x, weights, biases, keep_prob)에 대하여 해당 변수를 공급하고 예측 모델을 계산한다. 주의할 점은, Dropout에 관련되 파라미터인 keep_prob는 학습 단계에서는 0.7로 설정했으나, 테스트 단계에서는 1.0으로 설정해야 한다.
Line 4: Line 3에서 계산된 결과를 출력한다. 숫자가 0부터 시작되므로 계산된 결과값에 1을 빼었다. 예를 들어 예측 모델에 의해 계산된 결과가 8이면 이는 '7'을 의미한다.

위의 코드를 실행하였더니, 다음과 같은 결과가 출력되었다:

> print(which.max(pred_res) - 1)
[1] 4


기분좋게도 필자가 손으로 쓴 숫자인 '4'가 학습한 모델을 통해 제대로 '4'로 인식되었다!

Comments