rm(list=ls(all=T))
options(digits=4, scipen=40)
library(dplyr)
library(keras)



1. Read & Prepare Data

Reading & Examining data …

mnist = dataset_mnist()
par(mfrow = c(6, 8), pty = "s", mar = c(0.5, 0.5, 0, 0))
for(p in 1:48) mnist$train$x[p,,] %>% as.raster(max=255) %>% plot

Reshape the data …

train_images = array_reshape(mnist$train$x, c(60000, 28 * 28))
train_images = train_images / 255                    # normalization
test_images = array_reshape(mnist$test$x, c(10000, 28 * 28))
test_images = normalization= test_images / 255       # normalization
train_labels = to_categorical(mnist$train$y)
test_labels = to_categorical(mnist$test$y)



2. Traditional Neural Network (MLP)

2.1 Netwrok Parameters

mlp = keras_model_sequential() %>% 
  layer_dense(units = 512,               # number of perceptron
              activation = "relu",       # activation function
              input_shape = c(784)       # dimensions of input tensor
              ) %>% 
  layer_dense(units = 10,                # one output neuron per class
              activation = "softmax"     # activate the largest one
              )
summary(mlp)  # summary of the network spec
_____________________________________________________________________________________________________________________
Layer (type)                                        Output Shape                                   Param #           
=====================================================================================================================
dense_1 (Dense)                                     (None, 512)                                    401920            
_____________________________________________________________________________________________________________________
dense_2 (Dense)                                     (None, 10)                                     5130              
=====================================================================================================================
Total params: 407,050
Trainable params: 407,050
Non-trainable params: 0
_____________________________________________________________________________________________________________________

No. Coefficients (Param #) in Each Layer

  • dense_1: (28 * 28 + 1) * 512 = 401,920
  • dense_2: (512 + 1) * 10 = 5,130


2.2 Training Parameters

mlp %>% compile(                     # specify
  optimizer = "rmsprop",               # optimizer
  loss = "categorical_crossentropy",   # loss function
  metrics = c("accuracy")              # accuracy metrice  
  )

2.3 Fitting Model

fit1 = mlp %>% fit(
  train_images,         # train data
  train_labels,         # label of train data
  epochs=10,            # no. epochs
  batch_size=128,       # no. input per mini-batch
  verbose=2
  )
2018-09-26 10:11:37.886758: I tensorflow/core/platform/cpu_feature_guard.cc:140] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2 FMA
2018-09-26 10:11:37.975990: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:898] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2018-09-26 10:11:37.976425: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1356] Found device 0 with properties: 
name: Tesla K80 major: 3 minor: 7 memoryClockRate(GHz): 0.8235
pciBusID: 0000:00:04.0
totalMemory: 11.17GiB freeMemory: 11.09GiB
2018-09-26 10:11:37.976474: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1435] Adding visible gpu devices: 0
2018-09-26 10:11:38.263229: I tensorflow/core/common_runtime/gpu/gpu_device.cc:923] Device interconnect StreamExecutor with strength 1 edge matrix:
2018-09-26 10:11:38.263297: I tensorflow/core/common_runtime/gpu/gpu_device.cc:929]      0 
2018-09-26 10:11:38.263309: I tensorflow/core/common_runtime/gpu/gpu_device.cc:942] 0:   N 
2018-09-26 10:11:38.263683: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1053] Created TensorFlow device (/job:localhost/replica:0/task:0/device:GPU:0 with 10750 MB memory) -> physical GPU (device: 0, name: Tesla K80, pci bus id: 0000:00:04.0, compute capability: 3.7)
Epoch 1/10
 - 3s - loss: 0.2601 - acc: 0.9245
Epoch 2/10
 - 2s - loss: 0.1057 - acc: 0.9687
Epoch 3/10
 - 2s - loss: 0.0690 - acc: 0.9798
Epoch 4/10
 - 2s - loss: 0.0509 - acc: 0.9842
Epoch 5/10
 - 2s - loss: 0.0381 - acc: 0.9885
Epoch 6/10
 - 2s - loss: 0.0291 - acc: 0.9914
Epoch 7/10
 - 2s - loss: 0.0220 - acc: 0.9937
Epoch 8/10
 - 2s - loss: 0.0173 - acc: 0.9949
Epoch 9/10
 - 2s - loss: 0.0136 - acc: 0.9960
Epoch 10/10
 - 2s - loss: 0.0101 - acc: 0.9971
plot(fit1)

2.4 Validation & Predictione

# Evaluation
mlp %>% evaluate(test_images, test_labels, verbose=2)
$loss
[1] 0.07375

$acc
[1] 0.9811
# Prediction - Classes
mlp %>% predict_classes(test_images[1:10,])
 [1] 7 2 1 0 4 1 4 9 5 9
# Prediction Probability
mlp %>% predict_proba(test_images[1:10,]) %>% round(4)
      [,1]   [,2] [,3]   [,4]   [,5]   [,6]   [,7]   [,8] [,9]  [,10]
 [1,]    0 0.0000    0 0.0000 0.0000 0.0000 0.0000 1.0000    0 0.0000
 [2,]    0 0.0000    1 0.0000 0.0000 0.0000 0.0000 0.0000    0 0.0000
 [3,]    0 0.9999    0 0.0000 0.0000 0.0000 0.0000 0.0000    0 0.0000
 [4,]    1 0.0000    0 0.0000 0.0000 0.0000 0.0000 0.0000    0 0.0000
 [5,]    0 0.0000    0 0.0000 0.9999 0.0000 0.0000 0.0000    0 0.0001
 [6,]    0 0.9997    0 0.0000 0.0000 0.0000 0.0000 0.0003    0 0.0000
 [7,]    0 0.0000    0 0.0000 1.0000 0.0000 0.0000 0.0000    0 0.0000
 [8,]    0 0.0000    0 0.0008 0.0000 0.0000 0.0000 0.0000    0 0.9992
 [9,]    0 0.0000    0 0.0000 0.0000 0.9874 0.0126 0.0000    0 0.0000
[10,]    0 0.0000    0 0.0000 0.0006 0.0000 0.0000 0.0000    0 0.9993



3. Convolutional Neural Network (CNN)

3.1 Load nad Reshape

# mnist <- dataset_mnist()
# c(c(train_images, train_labels), c(test_images, test_labels)) %<-% mnist
# train_images <- array_reshape(train_images, c(60000, 28, 28, 1))
# train_images <- train_images / 255
# test_images <- array_reshape(test_images, c(10000, 28, 28, 1))
# test_images <- test_images / 255
# train_labels <- to_categorical(train_labels)
# test_labels <- to_categorical(test_labels)
train_images = array_reshape(mnist$train$x, c(60000, 28, 28, 1))
train_images = train_images / 255                    # normalization
test_images = array_reshape(mnist$test$x, c(10000, 28, 28, 1))
test_images = normalization= test_images / 255       # normalization
train_labels = to_categorical(mnist$train$y)
test_labels = to_categorical(mnist$test$y)

3.2 Method and Training Parameters

cnn <- keras_model_sequential() %>% 
  layer_conv_2d(filters = 32, kernel_size = c(3, 3), activation = "relu",
                input_shape = c(28, 28, 1)) %>% 
  layer_max_pooling_2d(pool_size = c(2, 2)) %>% 
  layer_conv_2d(filters = 64, kernel_size = c(3, 3), activation = "relu") %>% 
  layer_max_pooling_2d(pool_size = c(2, 2)) %>% 
  layer_conv_2d(filters = 64, kernel_size = c(3, 3), activation = "relu") %>% 
  layer_flatten() %>% 
  layer_dense(units = 64, activation = "relu") %>% 
  layer_dense(units = 10, activation = "softmax")
summary(cnn)
_____________________________________________________________________________________________________________________
Layer (type)                                        Output Shape                                   Param #           
=====================================================================================================================
conv2d_1 (Conv2D)                                   (None, 26, 26, 32)                             320               
_____________________________________________________________________________________________________________________
max_pooling2d_1 (MaxPooling2D)                      (None, 13, 13, 32)                             0                 
_____________________________________________________________________________________________________________________
conv2d_2 (Conv2D)                                   (None, 11, 11, 64)                             18496             
_____________________________________________________________________________________________________________________
max_pooling2d_2 (MaxPooling2D)                      (None, 5, 5, 64)                               0                 
_____________________________________________________________________________________________________________________
conv2d_3 (Conv2D)                                   (None, 3, 3, 64)                               36928             
_____________________________________________________________________________________________________________________
flatten_1 (Flatten)                                 (None, 576)                                    0                 
_____________________________________________________________________________________________________________________
dense_3 (Dense)                                     (None, 64)                                     36928             
_____________________________________________________________________________________________________________________
dense_4 (Dense)                                     (None, 10)                                     650               
=====================================================================================================================
Total params: 93,322
Trainable params: 93,322
Non-trainable params: 0
_____________________________________________________________________________________________________________________

No. Coefficients (Param #) in Each Layer

  • conv2d_1: (3 * 3 * 1 + 1) * 32 = 320
  • conv2d_2: (3 * 3 * 32 + 1) * 64 = 18,496
  • conv2d_3: (3 * 3 * 64 + 1) * 64 = 36,928
  • dense_3: (576 + 1) * 64 = 36,928
  • dense_4: (64 + 1) * 10 = 650
cnn %>% compile(
  optimizer = "rmsprop",
  loss = "categorical_crossentropy",
  metrics = c("accuracy"))

3.2 Fitting Model

fit2 = cnn %>% fit(
  train_images, train_labels, 
  epochs = 5,        # 5 epochs
  batch_size=64,     # 64 samples per mini-batch
  verbose = 2
  )
Epoch 1/5
 - 8s - loss: 0.1718 - acc: 0.9467
Epoch 2/5
 - 8s - loss: 0.0480 - acc: 0.9855
Epoch 3/5
 - 8s - loss: 0.0336 - acc: 0.9895
Epoch 4/5
 - 8s - loss: 0.0246 - acc: 0.9926
Epoch 5/5
 - 8s - loss: 0.0200 - acc: 0.9937
plot(fit2)

3.3 Evaluation

cnn %>% evaluate(test_images, test_labels, verbose=2)
$loss
[1] 0.0297

$acc
[1] 0.9915





LS0tCnRpdGxlOiAiTU5JU1Q6IFNpbXBsZSBQYXR0ZXJuIFJlY29nbml0aW9uIgphdXRob3I6ICJ0b255Y2h1b0BtYWlsLm5zeXN1LmVkdS50dyIKZGF0ZTogImByIFN5cy50aW1lKClgIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgo8YnI+CmBgYHtyIHNldC1vcHRpb25zLCBlY2hvPUZBTFNFLCBjYWNoZT1GQUxTRX0KbGlicmFyeShrbml0cikKb3B0aW9ucyh3aWR0aD0xMDApCm9wdHNfY2h1bmskc2V0KGNvbW1lbnQgPSBOQSkKYGBgCgpgYGB7ciB3YXJuaW5nPUYsIG1lc3NhZ2U9RiwgY2FjaGU9RiwgZXJyb3I9Rn0Kcm0obGlzdD1scyhhbGw9VCkpCm9wdGlvbnMoZGlnaXRzPTQsIHNjaXBlbj00MCkKbGlicmFyeShkcGx5cikKbGlicmFyeShrZXJhcykKYGBgCjxicj4KCi0gLSAtCgojIyMgMS4gUmVhZCAmIFByZXBhcmUgRGF0YQpSZWFkaW5nICYgRXhhbWluaW5nIGRhdGEgLi4uCmBgYHtyIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTZ9Cm1uaXN0ID0gZGF0YXNldF9tbmlzdCgpCgpwYXIobWZyb3cgPSBjKDYsIDgpLCBwdHkgPSAicyIsIG1hciA9IGMoMC41LCAwLjUsIDAsIDApKQpmb3IocCBpbiAxOjQ4KSBtbmlzdCR0cmFpbiR4W3AsLF0gJT4lIGFzLnJhc3RlcihtYXg9MjU1KSAlPiUgcGxvdApgYGAKClJlc2hhcGUgdGhlIGRhdGEgLi4uCmBgYHtyfQp0cmFpbl9pbWFnZXMgPSBhcnJheV9yZXNoYXBlKG1uaXN0JHRyYWluJHgsIGMoNjAwMDAsIDI4ICogMjgpKQp0cmFpbl9pbWFnZXMgPSB0cmFpbl9pbWFnZXMgLyAyNTUgICAgICAgICAgICAgICAgICAgICMgbm9ybWFsaXphdGlvbgp0ZXN0X2ltYWdlcyA9IGFycmF5X3Jlc2hhcGUobW5pc3QkdGVzdCR4LCBjKDEwMDAwLCAyOCAqIDI4KSkKdGVzdF9pbWFnZXMgPSBub3JtYWxpemF0aW9uPSB0ZXN0X2ltYWdlcyAvIDI1NSAgICAgICAjIG5vcm1hbGl6YXRpb24KdHJhaW5fbGFiZWxzID0gdG9fY2F0ZWdvcmljYWwobW5pc3QkdHJhaW4keSkKdGVzdF9sYWJlbHMgPSB0b19jYXRlZ29yaWNhbChtbmlzdCR0ZXN0JHkpCmBgYAo8YnI+CgotIC0gLQoKIyMjIDIuIFRyYWRpdGlvbmFsIE5ldXJhbCBOZXR3b3JrIChNTFApCgojIyMjIDIuMSBOZXR3cm9rIFBhcmFtZXRlcnMKYGBge3J9Cm1scCA9IGtlcmFzX21vZGVsX3NlcXVlbnRpYWwoKSAlPiUgCiAgbGF5ZXJfZGVuc2UodW5pdHMgPSA1MTIsICAgICAgICAgICAgICAgIyBudW1iZXIgb2YgcGVyY2VwdHJvbgogICAgICAgICAgICAgIGFjdGl2YXRpb24gPSAicmVsdSIsICAgICAgICMgYWN0aXZhdGlvbiBmdW5jdGlvbgogICAgICAgICAgICAgIGlucHV0X3NoYXBlID0gYyg3ODQpICAgICAgICMgZGltZW5zaW9ucyBvZiBpbnB1dCB0ZW5zb3IKICAgICAgICAgICAgICApICU+JSAKICBsYXllcl9kZW5zZSh1bml0cyA9IDEwLCAgICAgICAgICAgICAgICAjIG9uZSBvdXRwdXQgbmV1cm9uIHBlciBjbGFzcwogICAgICAgICAgICAgIGFjdGl2YXRpb24gPSAic29mdG1heCIgICAgICMgYWN0aXZhdGUgdGhlIGxhcmdlc3Qgb25lCiAgICAgICAgICAgICAgKQoKc3VtbWFyeShtbHApICAjIHN1bW1hcnkgb2YgdGhlIG5ldHdvcmsgc3BlYwpgYGAKCk5vLiBDb2VmZmljaWVudHMgKGBQYXJhbSAjYCkgaW4gRWFjaCBMYXllcgoKKyBgZGVuc2VfMTogKDI4ICogMjggKyAxKSAqIDUxMiA9IDQwMSw5MjBgIAorIGBkZW5zZV8yOiAoNTEyICsgMSkgKiAxMCA9IDUsMTMwYCAKCjxicj4KCiMjIyMgMi4yIFRyYWluaW5nIFBhcmFtZXRlcnMKYGBge3J9Cm1scCAlPiUgY29tcGlsZSggICAgICAgICAgICAgICAgICAgICAjIHNwZWNpZnkKICBvcHRpbWl6ZXIgPSAicm1zcHJvcCIsICAgICAgICAgICAgICAgIyBvcHRpbWl6ZXIKICBsb3NzID0gImNhdGVnb3JpY2FsX2Nyb3NzZW50cm9weSIsICAgIyBsb3NzIGZ1bmN0aW9uCiAgbWV0cmljcyA9IGMoImFjY3VyYWN5IikgICAgICAgICAgICAgICMgYWNjdXJhY3kgbWV0cmljZSAgCiAgKQpgYGAKCiMjIyMgMi4zIEZpdHRpbmcgTW9kZWwKYGBge3J9CmZpdDEgPSBtbHAgJT4lIGZpdCgKICB0cmFpbl9pbWFnZXMsICAgICAgICAgIyB0cmFpbiBkYXRhCiAgdHJhaW5fbGFiZWxzLCAgICAgICAgICMgbGFiZWwgb2YgdHJhaW4gZGF0YQogIGVwb2Nocz0xMCwgICAgICAgICAgICAjIG5vLiBlcG9jaHMKICBiYXRjaF9zaXplPTEyOCwgICAgICAgIyBuby4gaW5wdXQgcGVyIG1pbmktYmF0Y2gKICB2ZXJib3NlPTIKICApCmBgYAoKYGBge3J9CnBsb3QoZml0MSkKYGBgCgojIyMjIDIuNCBWYWxpZGF0aW9uICYgUHJlZGljdGlvbmUKYGBge3J9CiMgRXZhbHVhdGlvbgptbHAgJT4lIGV2YWx1YXRlKHRlc3RfaW1hZ2VzLCB0ZXN0X2xhYmVscywgdmVyYm9zZT0yKQpgYGAKCmBgYHtyfQojIFByZWRpY3Rpb24gLSBDbGFzc2VzCm1scCAlPiUgcHJlZGljdF9jbGFzc2VzKHRlc3RfaW1hZ2VzWzE6MTAsXSkKYGBgCgpgYGB7cn0KIyBQcmVkaWN0aW9uIFByb2JhYmlsaXR5Cm1scCAlPiUgcHJlZGljdF9wcm9iYSh0ZXN0X2ltYWdlc1sxOjEwLF0pICU+JSByb3VuZCg0KQpgYGAKCjxicj4KCi0gLSAtCgojIyMgMy4gQ29udm9sdXRpb25hbCBOZXVyYWwgTmV0d29yayAoQ05OKQoKIyMjIyAzLjEgTG9hZCBuYWQgUmVzaGFwZQpgYGB7cn0KIyBtbmlzdCA8LSBkYXRhc2V0X21uaXN0KCkKIyBjKGModHJhaW5faW1hZ2VzLCB0cmFpbl9sYWJlbHMpLCBjKHRlc3RfaW1hZ2VzLCB0ZXN0X2xhYmVscykpICU8LSUgbW5pc3QKIyB0cmFpbl9pbWFnZXMgPC0gYXJyYXlfcmVzaGFwZSh0cmFpbl9pbWFnZXMsIGMoNjAwMDAsIDI4LCAyOCwgMSkpCiMgdHJhaW5faW1hZ2VzIDwtIHRyYWluX2ltYWdlcyAvIDI1NQojIHRlc3RfaW1hZ2VzIDwtIGFycmF5X3Jlc2hhcGUodGVzdF9pbWFnZXMsIGMoMTAwMDAsIDI4LCAyOCwgMSkpCiMgdGVzdF9pbWFnZXMgPC0gdGVzdF9pbWFnZXMgLyAyNTUKIyB0cmFpbl9sYWJlbHMgPC0gdG9fY2F0ZWdvcmljYWwodHJhaW5fbGFiZWxzKQojIHRlc3RfbGFiZWxzIDwtIHRvX2NhdGVnb3JpY2FsKHRlc3RfbGFiZWxzKQoKdHJhaW5faW1hZ2VzID0gYXJyYXlfcmVzaGFwZShtbmlzdCR0cmFpbiR4LCBjKDYwMDAwLCAyOCwgMjgsIDEpKQp0cmFpbl9pbWFnZXMgPSB0cmFpbl9pbWFnZXMgLyAyNTUgICAgICAgICAgICAgICAgICAgICMgbm9ybWFsaXphdGlvbgp0ZXN0X2ltYWdlcyA9IGFycmF5X3Jlc2hhcGUobW5pc3QkdGVzdCR4LCBjKDEwMDAwLCAyOCwgMjgsIDEpKQp0ZXN0X2ltYWdlcyA9IG5vcm1hbGl6YXRpb249IHRlc3RfaW1hZ2VzIC8gMjU1ICAgICAgICMgbm9ybWFsaXphdGlvbgp0cmFpbl9sYWJlbHMgPSB0b19jYXRlZ29yaWNhbChtbmlzdCR0cmFpbiR5KQp0ZXN0X2xhYmVscyA9IHRvX2NhdGVnb3JpY2FsKG1uaXN0JHRlc3QkeSkKCmBgYAoKIyMjIyAzLjIgTWV0aG9kIGFuZCBUcmFpbmluZyBQYXJhbWV0ZXJzCmBgYHtyfQpjbm4gPC0ga2VyYXNfbW9kZWxfc2VxdWVudGlhbCgpICU+JSAKICBsYXllcl9jb252XzJkKGZpbHRlcnMgPSAzMiwga2VybmVsX3NpemUgPSBjKDMsIDMpLCBhY3RpdmF0aW9uID0gInJlbHUiLAogICAgICAgICAgICAgICAgaW5wdXRfc2hhcGUgPSBjKDI4LCAyOCwgMSkpICU+JSAKICBsYXllcl9tYXhfcG9vbGluZ18yZChwb29sX3NpemUgPSBjKDIsIDIpKSAlPiUgCiAgbGF5ZXJfY29udl8yZChmaWx0ZXJzID0gNjQsIGtlcm5lbF9zaXplID0gYygzLCAzKSwgYWN0aXZhdGlvbiA9ICJyZWx1IikgJT4lIAogIGxheWVyX21heF9wb29saW5nXzJkKHBvb2xfc2l6ZSA9IGMoMiwgMikpICU+JSAKICBsYXllcl9jb252XzJkKGZpbHRlcnMgPSA2NCwga2VybmVsX3NpemUgPSBjKDMsIDMpLCBhY3RpdmF0aW9uID0gInJlbHUiKSAlPiUgCiAgbGF5ZXJfZmxhdHRlbigpICU+JSAKICBsYXllcl9kZW5zZSh1bml0cyA9IDY0LCBhY3RpdmF0aW9uID0gInJlbHUiKSAlPiUgCiAgbGF5ZXJfZGVuc2UodW5pdHMgPSAxMCwgYWN0aXZhdGlvbiA9ICJzb2Z0bWF4IikKCnN1bW1hcnkoY25uKQpgYGAKCk5vLiBDb2VmZmljaWVudHMgKGBQYXJhbSAjYCkgaW4gRWFjaCBMYXllcgoKKyBgY29udjJkXzE6ICgzICogMyAqICAxICsgMSkgKiAzMiA9IDMyMGAgCisgYGNvbnYyZF8yOiAoMyAqIDMgKiAzMiArIDEpICogNjQgPSAxOCw0OTZgIAorIGBjb252MmRfMzogKDMgKiAzICogNjQgKyAxKSAqIDY0ID0gMzYsOTI4YCAKKyBgZGVuc2VfMzogICg1NzYgKyAxKSAqIDY0ID0gMzYsOTI4YCAKKyBgZGVuc2VfNDogICg2NCAgKyAxKSAqIDEwID0gNjUwYCAKCgpgYGB7cn0KY25uICU+JSBjb21waWxlKAogIG9wdGltaXplciA9ICJybXNwcm9wIiwKICBsb3NzID0gImNhdGVnb3JpY2FsX2Nyb3NzZW50cm9weSIsCiAgbWV0cmljcyA9IGMoImFjY3VyYWN5IikpCmBgYAoKIyMjIyAzLjIgRml0dGluZyBNb2RlbApgYGB7cn0KZml0MiA9IGNubiAlPiUgZml0KAogIHRyYWluX2ltYWdlcywgdHJhaW5fbGFiZWxzLCAKICBlcG9jaHMgPSA1LCAgICAgICAgIyA1IGVwb2NocwogIGJhdGNoX3NpemU9NjQsICAgICAjIDY0IHNhbXBsZXMgcGVyIG1pbmktYmF0Y2gKICB2ZXJib3NlID0gMgogICkKYGBgCgpgYGB7cn0KcGxvdChmaXQyKQpgYGAKCgojIyMjIDMuMyBFdmFsdWF0aW9uCmBgYHtyfQpjbm4gJT4lIGV2YWx1YXRlKHRlc3RfaW1hZ2VzLCB0ZXN0X2xhYmVscywgdmVyYm9zZT0yKQpgYGAKCjxicj48YnI+PGJyPjxicj4KCjxzdHlsZT4KLmNhcHRpb24gewogIGNvbG9yOiAjNzc3OwogIG1hcmdpbi10b3A6IDEwcHg7Cn0KcCBjb2RlIHsKICB3aGl0ZS1zcGFjZTogaW5oZXJpdDsKfQpwcmUgewogIHdvcmQtYnJlYWs6IG5vcm1hbDsKICB3b3JkLXdyYXA6IG5vcm1hbDsKICBsaW5lLWhlaWdodDogMTsKfQpwcmUgY29kZSB7CiAgd2hpdGUtc3BhY2U6IGluaGVyaXQ7Cn0KcCxsaSB7CiAgZm9udC1mYW1pbHk6ICJUcmVidWNoZXQgTVMiLCAi5b6u6Luf5q2j6buR6auUIiwgIk1pY3Jvc29mdCBKaGVuZ0hlaSI7Cn0KCi5yewogIGxpbmUtaGVpZ2h0OiAxLjI7Cn0KCi5xaXogewogIGxpbmUtaGVpZ2h0OiAxLjc1OwogIGJhY2tncm91bmQ6ICNmMGYwZjA7CiAgYm9yZGVyLWxlZnQ6IDEycHggc29saWQgI2NjZmZjYzsKICBwYWRkaW5nOiA0cHg7CiAgcGFkZGluZy1sZWZ0OiAxMHB4OwogIGNvbG9yOiAjMDA5OTAwOwp9Cgp0aXRsZXsKICBjb2xvcjogI2NjMDAwMDsKICBmb250LWZhbWlseTogIlRyZWJ1Y2hldCBNUyIsICLlvq7ou5/mraPpu5Hpq5QiLCAiTWljcm9zb2Z0IEpoZW5nSGVpIjsKfQoKYm9keXsKICBmb250LWZhbWlseTogIlRyZWJ1Y2hldCBNUyIsICLlvq7ou5/mraPpu5Hpq5QiLCAiTWljcm9zb2Z0IEpoZW5nSGVpIjsKfQoKaDEsaDIsaDMsaDQsaDV7CiAgY29sb3I6ICMwMDY2ZmY7CiAgZm9udC1mYW1pbHk6ICJUcmVidWNoZXQgTVMiLCAi5b6u6Luf5q2j6buR6auUIiwgIk1pY3Jvc29mdCBKaGVuZ0hlaSI7Cn0KCgpoM3sKICBjb2xvcjogIzAwODgwMDsKICBiYWNrZ3JvdW5kOiAjZTZmZmU2OwogIGxpbmUtaGVpZ2h0OiAyOwogIGZvbnQtd2VpZ2h0OiBib2xkOwp9CgpoNXsKICBjb2xvcjogIzAwNjAwMDsKICBiYWNrZ3JvdW5kOiAjZjhmOGY4OwogIGxpbmUtaGVpZ2h0OiAxLjU7CiAgZm9udC13ZWlnaHQ6IGJvbGQ7Cn0KPC9zdHlsZT4KCgoKCg==