AI影像辨識

宣傳影片

0. 準備資料

0.1 影像資料

首先,準備好要用到的訓練與測試資料集。
我們使用貓與狗的圖片資料來訓練貓狗辨識模型。
範例資料採用貓與狗各4000張圖片來訓練,並以各1000張照片做測試。
範例資料格式如下:

我們可以先預覽圖片:

library(dplyr)
library(keras)
## Sample Images
par(mfrow = c(6, 12), pty = "s", mar = c(0.2, 0.2, 0, 0))
for(x in c("cat","dog")) for(i in 1:36)
  sprintf("dataset/training_set/%ss/%s.%d.jpg", x, x, i) %>% 
  image_load(target_size=c(150,150)) %>% 
  image_to_array %>% as.raster(255) %>% plot


可以發現很多難以辨識的圖片,但是透過訓練,電腦將可以快速且高準確率的辨識這些圖片唷。

0.2 資料讀取

因為資料集很大,我們沒有辦法一次讀取所有資料來訓練,我們設定函數一次提取特定數量的資料,並對其做處理。

img255 = image_data_generator(rescale = 1/255) #將圖片像素值標準化至0~1之間
Generate = function(x, gen=img255, sz=20) flow_images_from_directory(
  x, gen, 
  target_size = c(150, 150),              # 壓縮圖片成一致大小
  batch_size = sz,                        # 一次抽取的樣本數
  class_mode = "binary"                   # 圖片類別為二元類別(貓或狗)
  )

1. CNN模型(Convolutional Neural Network)

CNN模型是很常見的圖像識別模型,其概念可以參考2分鐘短片:CNN簡介

1.1 使用Keras建立CNN模型

cnn1 <- keras_model_sequential() %>% 
  layer_conv_2d(filters = 32, kernel_size = c(3, 3), activation = "relu",      # filters: filter個數
                input_shape = c(150, 150, 3)) %>%                              # kernel_size: filter的長寬
  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 = 128, kernel_size = c(3, 3), activation = "relu") %>% 
  layer_max_pooling_2d(pool_size = c(2, 2)) %>% 
  layer_conv_2d(filters = 128, kernel_size = c(3, 3), activation = "relu") %>% 
  layer_max_pooling_2d(pool_size = c(2, 2)) %>% 
  layer_flatten() %>%                                                          # 將特徵展開為一維向量
  layer_dense(units = 512, activation = "relu") %>% 
  layer_dense(units = 1, activation = "sigmoid")
summary(cnn1)

1.2 設定模型訓練參數

cnn1 %>% compile(
  loss = "binary_crossentropy",               #目標損失函數
  optimizer = optimizer_rmsprop(lr = 1e-4),   #訓練優化方法
  metrics = c("acc")                          #評價方式
  )

1.3 開始訓練

fit1 <- cnn1 %>% fit_generator(               #自訂輸入資料
  Generate("dataset/training_set"),           #先前定義好的函數
  steps_per_epoch = 200,                      #每次迭代輸入幾次資料
  epochs = 20,                                #迭代次數
  validation_data = Generate("dataset/test_set"),
  validation_steps = 50,
  verbose=2                                   #顯示訓練過程
  )

將模型訓練過程繪製成圖表

plot(fit1)


acc表示準確率。
經過20次迭代的訓練,模型已經可以有接近9成的準確率囉。

2. 資料擴充

常常我們所擁有的圖像資料集是不足的,這時可以將所擁有的資料做一些變化後當作新的資料,例如,將圖片反轉、切割、變形。這些內容都可以當作新的資料,幫助模型學會看懂不同方向、大小或有雜訊的圖片。

2.1 製作一個擴充資料的資料產生函數

Augment = image_data_generator(
  rescale = 1/255,
  rotation_range = 40,
  width_shift_range = 0.2,
  height_shift_range = 0.2,
  shear_range = 0.2,
  zoom_range = 0.2,
  horizontal_flip = TRUE,
  fill_mode = "nearest"
  )
# Load and Reshape an image for testing
img = image_load("dataset/training_set/cats/cat.28.jpg", target_size=c(150,150)) %>% 
  image_to_array %>% array_reshape(c(1,150,150,3))
# Test the Aug. Generator 
aug = flow_images_from_data(img, generator=Augment, batch_size=1)
par(mfrow = c(2, 6), pty = "s", mar = c(0.5, 0.5, 0, 0))
for (i in 1:12) generator_next(aug)[1,,,] %>% as.raster %>% plot

可由測試看出,我們將一張圖片做了許多旋轉與移動,成為了新的圖片。

2.2 訓練模型

這裡我們使用與上面相同的CNN模型

cnn2 <- keras_model_sequential() %>% 
  layer_conv_2d(filters = 32, kernel_size = c(3, 3), activation = "relu",
                input_shape = c(150, 150, 3)) %>% 
  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 = 128, kernel_size = c(3, 3), activation = "relu") %>% 
  layer_max_pooling_2d(pool_size = c(2, 2)) %>% 
  layer_conv_2d(filters = 128, kernel_size = c(3, 3), activation = "relu") %>% 
  layer_max_pooling_2d(pool_size = c(2, 2)) %>% 
  layer_flatten() %>% 
  layer_dropout(rate = 0.5) %>%    
  layer_dense(units = 512, activation = "relu") %>% 
  layer_dense(units = 1, activation = "sigmoid")  
cnn2 %>% compile(
  loss = "binary_crossentropy",
  optimizer = optimizer_rmsprop(lr = 1e-4),
  metrics = c("acc")
)
summary(cnn2)

使用資料擴充的方式來訓練。

fit2 <- cnn2 %>% fit_generator(
  Generate("dataset/training_set", Augment, 40),
  steps_per_epoch = 200,
  epochs = 20,
  validation_data = Generate("dataset/test_set"),
  validation_steps = 50,
  verbose=2
  )

最後一樣以圖表觀察訓練過程。

plot(fit2)


在這裡validation的資料及準確度反而較高,是因為對於validation資料我們並沒有做資料擴充,故模型並沒有預測經過變化的樣本。

3. 使用預訓練模型

訓練一個辨識模型需要很多的資料,且需要很多訓練時間才能達到好的效果。因此,我們可以直接使用網路上人家訓練好的模型(也叫做預訓練模型),減少自己訓練的成本。這裡我們實作調整 VGG16 預訓練模型,來達到快速訓練模型的效果。

3.1 固定預訓練模型

引入預訓練好的 VGG16 圖像辨識模型

conv_base <- application_vgg16(
  weights = "imagenet",
  include_top = FALSE,          #將最後的輸出層移除
  input_shape = c(150, 150, 3))
summary(conv_base)

可以看見 VGG16 辨識模型並沒有最後的輸出層,我們只要該模型前段對圖片特徵的萃取能力。
接著將我們要訓練的輸出層接上去。

cnn3 <- keras_model_sequential() %>% 
  conv_base %>% 
  layer_flatten() %>% 
  layer_dense(units = 256, activation = "relu") %>% 
  layer_dense(units = 1, activation = "sigmoid")  
summary(cnn3)

我們只要訓練輸出層即可,將 VGG16 的權重固定(待會訓練時不更新權重)。

length(cnn3$trainable_weights)

30
原先可訓練的層數是30層。

freeze_weights(conv_base)       # 將VGG16的權重改為不訓練
length(cnn3$trainable_weights)  # 4

4
現在只訓練最後4層。
開始訓練。

cnn3 %>% compile(
  loss = "binary_crossentropy",
  optimizer = optimizer_rmsprop(lr = 2e-5),
  metrics = c("accuracy"))
fit3 <- cnn3 %>% fit_generator(
  Generate("dataset/training_set",Augment,40),
  steps_per_epoch = 100,
  epochs = 30,
  validation_data = Generate("dataset/test_set"),
  validation_steps = 50,
  verbose = 2
  )
plot(fit3)

比起先前重新訓練的模型,可發現此模型很快就有一定的準確率,且準確率也更高,這就是預訓練模型的效果。

3.2 微調預訓練模型

CNN模型中,越靠近輸出層的層萃取的內容越高層,例如,前面層都抓取線條或顏色等資訊,在後面層可能就產生了人、動物、物品等資訊。所以我們可以微調後面層的權重,來達到對特定目標的辨識。
剛剛我們固定了預訓練模型的權重,現在我們將後面幾層的權重一起訓練。

unfreeze_weights(conv_base, from = "block5_conv1")
length(cnn3$trainable_weights)  # 10

10
原先是4層,現在又多訓練了6層。
開始訓練。

cnn3 %>% compile(
  loss = "binary_crossentropy",
  optimizer = optimizer_rmsprop(lr = 1e-5),
  metrics = c("accuracy")
  )
fit3a = cnn3 %>% fit_generator(
  Generate("dataset/training_set",Augment,40),
  steps_per_epoch = 200,
  epochs = 30,
  validation_data = Generate("dataset/test_set"),
  validation_steps = 50,
  verbose = 2
  )
plot(fit3a)

可看見準確率又有顯著上升,已經到達95%的準確率了,現在你也可以試試創作自己的影像辨識模型囉!