iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 15
3

前言

因應新冠病毒的防堵,企業及公共場所多強制戴口罩,這一篇就來實作如何利用AI偵測是否戴口罩。

前一篇談到預先訓練好的模型(Keras Applications),套用的方式有三種方式:

  1. 模型完全採用:可辨識ImageNet提供1000種事物的辨識。
  2. 模型部分採用:只擷取特徵,不作辨識。
  3. 模型部分採用,並接上自己的input及辨識層:可辨識1000種事物以外的東西。

前一篇已經介紹前兩種方式,接著我們就來使用自建的資料集實作戴口罩偵測。

實作說明

程式名稱為 15_02_Mask_Detection.ipynb,分段說明如下:

  1. 引進套件
from tensorflow.keras.applications.vgg16 import VGG16
from tensorflow.keras.preprocessing import image
from tensorflow.keras.applications.vgg16 import preprocess_input
from tensorflow.keras import layers
from tensorflow.keras.models import Model
import numpy as np
  1. 使用 VGG16 預先訓練好的模型,不含後三層,再自接三個完全連接層,注意,之前要先接一個 GlobalAveragePooling2D 層,照原模型應該接 Flatten 層,但是,測試會有錯誤,於是,照官網範例接 GlobalAveragePooling2D 就 OK 了,原因待查。
# 預先訓練好的模型 -- VGG16, 不含後三層(辨識層)
base_model = VGG16(weights='imagenet', include_top=False)

# 連接自訂層
x = base_model.output
#x = layers.Flatten()(x)
x = layers.GlobalAveragePooling2D()(x)
#x = layers.Dropout(0.2)(x)
x = layers.Dense(256, activation='relu')(x)
#x = layers.Dropout(0.2)(x)
x = layers.Dense(64, activation='relu')(x)
x = layers.Dense(2, activation='sigmoid')(x)

# 設定新模型的 inputs/outputs
model = Model(inputs=base_model.input, outputs=x)

# VGG16 原有的層均不重新訓練
for layer in base_model.layers:
    layer.trainable = False
    
model.compile(optimizer='adam', loss='binary_crossentropy')    
  1. 【這裡】 取得訓練資料,experiements/data/with_mask/ 目錄含戴口罩的圖檔,反之,experiements/data/without_mask/ 目錄含戴口罩的圖檔,先利用下列程式讀取 with_mask、 without_mask 目錄下所有圖片檔名。
# 讀取 data/with_mask 目錄下所有圖片檔名
img_path = './data/with_mask/'
image_files = np.array([join(img_path, f) for f in listdir(img_path) if isfile(join(img_path, f)) and f[-3:] == 'jpg'])

# 讀取 data/without_mask 目錄下所有圖片檔名
img_path = './data/without_mask/'
image_files2 = np.array([join(img_path, f) for f in listdir(img_path) if isfile(join(img_path, f)) and f[-3:] == 'jpg'])
  1. 讀取每個圖檔,合併為訓練資料的 X。
X = np.array([])
# 讀取 data/without_mask 目錄下所有圖檔
for f in image_files2:
    # 載入圖檔,並縮放寬高為 (224, 224) 
    img = image.load_img(f, target_size=(224, 224))
    # 加一維,變成 (1, 224, 224, 3),最後一維是色彩
    img2 = image.img_to_array(img)
    img2 = np.expand_dims(img2, axis=0)
    if len(X.shape) == 1:
        X = img2
    else:
        X = np.concatenate((X, img2), axis=0)
        
# 讀取 data/with_mask 目錄下所有圖檔
for f in image_files:
    # 載入圖檔,並縮放寬高為 (224, 224) 
    img = image.load_img(f, target_size=(224, 224))
    # 加一維,變成 (1, 224, 224, 3),最後一維是色彩
    img2 = image.img_to_array(img)
    img2 = np.expand_dims(img2, axis=0)
    if len(X.shape) == 1:
        X = img2
    else:
        X = np.concatenate((X, img2), axis=0)

X = preprocess_input(X)
  1. 產生 label, with_mask = 1, without_mask = 0, 並作 one-hot encoding。
from tensorflow.keras.utils import to_categorical

y = np.concatenate((np.zeros(image_files2.shape[0]), np.ones(image_files.shape[0])))
# one-hot encoding
y = to_categorical(y, num_classes=2)
  1. 模型訓練。
model.fit(X, y, epochs=5, validation_split=0.2, verbose=2)
  1. 使用任意兩張圖片測試,with-mask.jpg、without-mask.jpg
# 任意一張圖片測試
img_path = './0_images/21-with-mask.jpg'
# 載入圖檔,並縮放寬高為 (224, 224) 
img = image.load_img(img_path, target_size=(224, 224))
# 加一維,變成 (1, 224, 224, 3),最後一維是色彩
x = image.img_to_array(img)
x = np.expand_dims(x, axis=0)
x = preprocess_input(x)

print(f'戴口罩機率:{round(model.predict(x)[0][1] * 100, 2):.2f}')

from IPython.display import Image
Image(img_path, width=200)

結果如下:
https://ithelp.ithome.com.tw/upload/images/20200915/20001976CAB2tTopo3.jpg

https://ithelp.ithome.com.tw/upload/images/20200915/20001976i4MpTUlq6I.jpg

結論

預先訓練好的模型使用上百萬張的圖片(ImageNet)作為訓練資料,又有很多層的模型結構,提供通用的特徵擷取,我們可以善用他們訓練出來的參數,不必耗費大量的時間調校超參數,實在是一件很幸福的事。這就是所謂的轉移學習(Transfer Learning)。

本篇範例包括 15_01_Keras_applications3.ipynb、15_02_Mask_Detection.ipynb,可自【這裡】下載,其中 15_01_Keras_applications3.ipynb 提供暖身的練習,囿於篇幅,就不說明了。


上一篇
Day 14:預先訓練好的模型(Keras Applications)
下一篇
Day 16:TensorFlow 2 Object Detection API 安裝
系列文
輕鬆掌握 Keras 及相關應用30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言