iT邦幫忙

2021 iThome 鐵人賽

DAY 16
0
AI & Data

30 天在 Colab 嘗試的 30 個影像分類訓練實驗系列 第 16

【16】如果把圖片從RGB轉成HSV和灰階再拿去訓練會怎樣

colab連結

普遍我們拿來訓練的圖片都是RGB,普遍都是機器學習的CNN層找到一些局部特徵來做分類,這些局部特徵對我們人來說,即使是轉成視覺化的特徵值,依然是個謎,既然這樣子,如果我們把圖片轉成HSV,機器一樣能夠完成任務嗎?畢竟對於機器來說,這些像素都是數值。

第二個問題是,其實我們今天把某些照片轉成灰階照片,我們仍然可以一眼看出圖片代表什麼(比如貓狗照片轉成灰階,你仍然可以認得哪張是貓哪張是狗),那在機器學習任務時,我們是不是也能夠教機器學習灰階圖片呢?這樣子訓練出來的模型和原先的RGB模型又會有什麼樣的差別呢?這就是我們今天要來實驗的主題。

在進行訓練之前,我們先來看看 tf.image.* 提供了哪些API供我們使用,很剛好地,轉成HSV和灰階都剛好有做好的API可以用。

import cv2
from google.colab.patches import cv2_imshow

def convert_img(image, label):
  image = tf.cast(image, tf.float32)
  normal = image / 255.
  hsv = tf.image.rgb_to_hsv(normal)
  hsv = hsv * 255.
  gray = tf.image.rgb_to_grayscale(image)
  return image, hsv, gray

ds_train = train_split.map(
    convert_img, num_parallel_calls=tf.data.experimental.AUTOTUNE)

為了確保我們使用 Tensorflow API 轉換是正確的,我另外寫了和 OpenCV 轉換的對照版本,來實測轉換是否一致。

首先是 tf.image.rgb_to_hsv,一般我們圖片讀近來是 uint8 的格式,數值落在 [0, 255],但根據 tensorflow 官方文檔,我們發現

The output is only well defined if the value in images are in [0,1]

輸入的 image 必須是 [0,+1] 的範圍,所以一開始我們必須先將 image / 255. ,轉完 hsv 之後,再乘上 255. 變回去 uint8 的數值。

而 tf.image.rgb_to_grayscale ,相對簡單,原先的 uint8 就可以直接讀進來。

for example in ds_train.take(2):
  ex = example
  origin = cv2.cvtColor(example[0].numpy().astype(np.uint8), cv2.COLOR_RGB2BGR)
  hsv = example[1].numpy().astype(np.uint8)
  gray = example[2].numpy().astype(np.uint8)

print('Origin:')
cv2_imshow(origin)
print('\nOpenCV HSV:')
cv2_imshow(cv2.cvtColor(origin, cv2.COLOR_BGR2HSV))
print('\nTF HSV:')
cv2_imshow(hsv)
print('\nOpenCV Gray:')
cv2_imshow(cv2.cvtColor(origin, cv2.COLOR_BGR2GRAY))
print('\nTF Gray:')
cv2_imshow(gray)

我們來看看和 OpenCV 的差異:

原圖:
https://ithelp.ithome.com.tw/upload/images/20210930/20107299rl1mkJbsJu.png

OpenCV 的 HSV:
https://ithelp.ithome.com.tw/upload/images/20210930/201072994KCdet9XFM.png

TF 的 HSV:
https://ithelp.ithome.com.tw/upload/images/20210930/20107299Wd6wpRQw7U.png

發現這兩者轉換的數值還是有些微的差異,但後面的訓練仍用 tensorflow 轉換即可,我們只想知道訓練最後的 loss 和準確度。

OpenCV 的 灰階:
https://ithelp.ithome.com.tw/upload/images/20210930/20107299Sr9xrDFxgx.png

TF 的 灰階:
https://ithelp.ithome.com.tw/upload/images/20210930/20107299GzHXCePNq2.png

兩者並無太大差異,灰階轉換是OK的。

實驗一:使用經過HSV轉換的圖片來訓練

def normalize_img(image, label):
  image = tf.cast(image, tf.float32)
  image = tf.image.resize(image, (224,224))
  image = image / 255.
  image = tf.image.rgb_to_hsv(image)
  return image, label

ds_train = train_split.map(
    normalize_img, num_parallel_calls=tf.data.experimental.AUTOTUNE)
ds_train = ds_train.cache()
ds_train = ds_train.shuffle(SHUFFLE_SIZE)
ds_train = ds_train.batch(BATCH_SIZE)
ds_train = ds_train.prefetch(tf.data.experimental.AUTOTUNE)

ds_test = test_split.map(
    normalize_img, num_parallel_calls=tf.data.experimental.AUTOTUNE)
ds_test = ds_test.batch(BATCH_SIZE)
ds_test = ds_test.cache()
ds_test = ds_test.prefetch(tf.data.experimental.AUTOTUNE)

base = tf.keras.applications.MobileNetV2(input_shape=(224, 224, 3), include_top=False, weights='imagenet')
net = tf.keras.layers.GlobalAveragePooling2D()(base.output)
net = tf.keras.layers.Dense(NUM_OF_CLASS)(net)

model = tf.keras.Model(inputs=[base.input], outputs=[net])

model.compile(
    optimizer=tf.keras.optimizers.SGD(LR),
    loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
    metrics=[tf.keras.metrics.SparseCategoricalAccuracy()],
)

history = model.fit(
    ds_train,
    epochs=EPOCHS,
    validation_data=ds_test,
    verbose=True)

產出:

loss: 4.5527e-04 - sparse_categorical_accuracy: 1.0000 - val_loss: 0.9874 - val_sparse_categorical_accuracy: 0.7402

https://ithelp.ithome.com.tw/upload/images/20210930/20107299aET3iarufC.png

實驗二:使用經過灰階轉換的圖片來訓練

def normalize_img(image, label):
  image = tf.cast(image, tf.float32)
  image = tf.image.resize(image, (224,224))
  image = tf.image.rgb_to_grayscale(image)
  image = tf.repeat(image, 3, axis=2)  # mobilenet input channel is 3
  return image / 255., label

ds_train = train_split.map(
    normalize_img, num_parallel_calls=tf.data.experimental.AUTOTUNE)
ds_train = ds_train.cache()
ds_train = ds_train.shuffle(SHUFFLE_SIZE)
ds_train = ds_train.batch(BATCH_SIZE)
ds_train = ds_train.prefetch(tf.data.experimental.AUTOTUNE)

ds_test = test_split.map(
    normalize_img, num_parallel_calls=tf.data.experimental.AUTOTUNE)
ds_test = ds_test.batch(BATCH_SIZE)
ds_test = ds_test.cache()
ds_test = ds_test.prefetch(tf.data.experimental.AUTOTUNE)

base = tf.keras.applications.MobileNetV2(input_shape=(224, 224, 3), include_top=False, weights='imagenet')
net = tf.keras.layers.GlobalAveragePooling2D()(base.output)
net = tf.keras.layers.Dense(NUM_OF_CLASS)(net)

model = tf.keras.Model(inputs=[base.input], outputs=[net])

model.compile(
    optimizer=tf.keras.optimizers.SGD(LR),
    loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
    metrics=[tf.keras.metrics.SparseCategoricalAccuracy()],
)

start = timeit.default_timer()
history = model.fit(
    ds_train,
    epochs=EPOCHS,
    validation_data=ds_test,
    verbose=True)

這邊有個比較麻煩的地方是,原先 mobilenetV2 輸入的 channel 是3個,但是灰階 channel 只有1,所以我透過 tf.repeat 複製成3個 channel,為了不變動模型這樣做其實有點冗贅。

產出:

loss: 4.2608e-04 - sparse_categorical_accuracy: 1.0000 - val_loss: 0.8360 - val_sparse_categorical_accuracy: 0.7931

https://ithelp.ithome.com.tw/upload/images/20210930/20107299bZoz6C3PQp.png

HSV的準確度和灰階的準確度分別是74%和79.3%,相較於昨天RGB實驗一的88.2%,看到這樣的結果,想了一下覺得畢竟今天照片呈現的物件是什麼,是由我們人的肉眼根據形狀和顏色等所定義,如果今天這個轉換去除了顏色這個因素,對機器來說就是個差異性很大的事情,今天只是個小實驗滿足我的好奇心,各位在做訓練任務時還是使用RGB吧。


上一篇
【15】圖片標準化 [0,1] 與 [-1,+1] 的差別實驗
下一篇
【17】訓練到一半遇到 nan 嗎? 梯度爆炸與梯度消失的測試實驗
系列文
30 天在 Colab 嘗試的 30 個影像分類訓練實驗31

尚未有邦友留言

立即登入留言