北科電子大三上機器學習 使用 CNN 對 The Simpons 中 50 個人物進行辨識

台北科大深度學習 - 使用 CNN 對 The Simpons 中 50 個人物進行辨識

題目說明

使用 CNN 算法來針對 The Simpons 中的 50 人物進行辨識

將 kaggle 資料進行解壓縮

1
!unzip ./data/machine-learningntut-2021-autumn-classification.zip

將 50 個人物名稱透過 dict 紀錄

  • 先將所有的人物先存在文字檔再透過 py 一個一個進行讀取
1
2
3
4
5
6
7
8
9
10
11
12
13
#%cd /content/drive/My Drive/NTUT/大三上-深度學習/Predict_Simposons/ #t108AB0008@ntut.org.tw
#other email
%cd /content/drive/.shortcut-targets-by-id/1EmT_yvIKvgl-iNKBU0Wu2XOFjOPsxhyu/大三上-深度學習/Predict_Simposons


characters = dict()
characters.clear()
f = open("characters.txt", "r")
cnt = 0
for line in f.readlines():
characters[cnt] = line.strip()
cnt += 1
print(characters)

決定圖像大小、訓練集、測試集大小

  • 由於圖像大小會影響深度學習中的模型細節,還有時間,由於使用 colab 進行訓練因此將圖像 size 設定為非常小
1
2
3
4
5
6
pic_width = 42
pic_height = 42

num_class = len(characters)
print(num_class)
test_size = 0.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

import cv2
import numpy as np
import glob
from google.colab.patches import cv2_imshow

#imgpath = "/content/drive/My Drive/NTUT/大三上-深度學習/Predict_Simposons/data/theSimpsons-train/train"
imgpath = "/content/drive/.shortcut-targets-by-id/1EmT_yvIKvgl-iNKBU0Wu2XOFjOPsxhyu/大三上-深度學習/Predict_Simposons/theSimpsons-train/train"
def load_picture():
pics = []
labels = []

for k, v in characters.items(): # k 是編號, v 是名稱
origin_pics = [k for k in glob.glob(imgpath + "/" + v + "/*")]
print(v + " : " + str(len(origin_pics)))

for i, filename in enumerate(origin_pics):
tmp_img = cv2.imread(filename)

# opencv 讀圖片時用 BGR,而非 RGB 因此做轉換
tmp_ing = cv2.cvtColor(tmp_img, cv2.COLOR_BGR2RGB)
tmp_img = cv2.resize(tmp_img, (pic_height, pic_width))
pics.append(tmp_img)
labels.append(k)
return np.array(pics), np.array(labels)

#load_picture()

分割成訓練集、測試集,並透過 h5py 存成資料集

由於老師是給全部的資料,因此我們就需要進行分割。

那下面這個程式中的參數用意如下

  • save 則是將讀進來的全部資料分成訓練集、測試集,並存取
  • load 將之前分好的訓練集、測試集讀取
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
import keras
from sklearn.model_selection import train_test_split
import h5py
def get_dataset(save=False, load=False):
if load:
# 讀檔
h5f = h5py.File("train60.h5", "r")
x_train = h5f["x_train"][:]
x_test = h5f["x_test"][:]
h5f.close()

h5f = h5py.File("valid60.h5", "r")
y_train = h5f["y_train"][:]
y_test = h5f["y_test"][:]
h5f.close()

else:
#從最原始圖像進行處理
x, y= load_picture()
y = keras.utils.np_utils.to_categorical(y, num_class) #將分類轉換成 numpy https://blog.csdn.net/moyu123456789/article/details/83444140
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size = test_size)
if save: #將資料集、測試集存到 google drive
h5f = h5py.File("train60.h5", "w")
h5f.create_dataset("x_train", data=x_train)
h5f.create_dataset("x_test", data=x_test)
h5f.close()

h5f = h5py.File("valid60.h5", "w")
h5f.create_dataset("y_train", data=y_train)
h5f.create_dataset("y_test", data=y_test)
h5f.close()

#資料歸一化處理
x_train = x_train.astype("float32") /255
x_test = x_test.astype("float32") /255
print("Train", x_train.shape, y_train.shape)
print("valid", x_test.shape, y_test.shape)

return x_train, x_test, y_train, y_test


x_train, x_test, y_train, y_test = get_dataset(save = True, load = False)

完成示意圖

增強訓練習的資料

如果覺得自己的訓練集資料不夠多時,可以考慮透過 from keras.preprocessing.image import ImageDataGenerator 透過圖像偏移、角度轉換的方式來補充訓練集資料,加強模型

但我在這邊沒有加入,因為我認為訓練集資料已經足夠

1
2
3
4
5
6
from keras.preprocessing.image import ImageDataGenerator
datagen = ImageDataGenerator(
shear_range = 0.2,
zoom_range = 0.2,
horizontal_flip = True
)

模型設定

Sequential 是在機器學習中最簡單、也最好用的模型,且在大部分的機器學習中她都可以應用(但她不一定是最好的),因此我在這邊就使用 Sequential 來做出簡單的 CNN 模型

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
from keras.layers.convolutional_recurrent import ConvLSTM2D
import tensorflow.keras.optimizers
import keras

from keras.models import Sequential
from keras.layers import Dense, Dropout, Activation, Flatten, BatchNormalization, MaxPool2D
from keras.layers import Conv2D, MaxPooling2D
from keras.applications.resnet import ResNet50
from keras.applications.vgg16 import VGG16

def createModel2():
input_shape = (pic_height, pic_width, 3)
model = Sequential()

# 由於我們是圖像,加入 Convolution Layer 必須用 "2D",
# kernel_size 用 (3,3) 則是因為我圖像已經相對小了,因此要對細節好好處理
# padding 對超出邊界的視窗直接補 0 即可
model.add(Conv2D(32, kernel_size = (3,3), padding = "Same",
activation="relu", input_shape = input_shape))
model.add(Conv2D(32, kernel_size=(3,3), padding = "Same", activation="relu"))

# 在一般情況下 pool_size 設定成 2 就有不錯效果
model.add(MaxPool2D(pool_size=(2,2)))
model.add(BatchNormalization())
model.add(Conv2D(64, kernel_size=(3,3), padding="Same", activation="relu"))
model.add(Conv2D(64, kernel_size=(3,3), padding="Same", activation="relu"))
model.add(MaxPool2D(pool_size=(2,2)))

# 將前面每一層的 layer 的 feature 都做 Normalization,確保不會讓前面的 layer 權重太大,
# 由於每一層 layer 都會影響到下一層,因此會容易讓前面一層的 layer 影響被放大,就有可能 overfitting
#可以參考 [ML筆記] Batch Normalization by 陳雲濤的部落格
model.add(BatchNormalization())
model.add(Conv2D(86, kernel_size = (3,3), padding = "Same", activation="relu"))
model.add(Conv2D(86, kernel_size=(3,3), padding = "Same", activation="relu"))
model.add(MaxPool2D(pool_size=(2,2)))
model.add(BatchNormalization())

# 將多維的輸入轉化成一維
model.add(Flatten())
# 將資料變成 1024 個特徵
model.add(Dense(1024, activation="relu"))
model.add(Dense(512, activation="relu"))

# 由於我們最後辨識 50 個腳色,因此大小就是 50
model.add(Dense(50, activation="softmax")) #因為我們資料是機率,所以用 softmax,可以顯現出 50 個 character 機率

#define the optimizer
optimizer = "Adam"
model.compile(optimizer=optimizer, loss="categorical_crossentropy", metrics=["accuracy"])
return model

開始執行 model

這邊就開始執行 model

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from keras.callbacks import ModelCheckpoint, LearningRateScheduler, EarlyStopping

model = createModel2()
model.summary()

lr = 0.001

batch_size = 32
epoch = 50
def lr_schedule(epoch):
return lr*(0.1**(int(epoch/10)))

# 會將最好的 model 儲存,並且如果 epoch 沒有更好時,就會讓此 model 暫停
history = model.fit(x_train, y_train,
batch_size = batch_size,
epochs = epoch,
validation_data = (x_test, y_test),
shuffle = True,
callbacks=[LearningRateScheduler(lr_schedule),
ModelCheckpoint("model2.h5", save_best_only=True, period=10),
EarlyStopping(monitor="val_accuracy", patience=3, mode= "auto")])

執行畫面

對準確率、還有錯誤率進行畫圖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import matplotlib.pyplot as plt

def plot_train_history(history, train_metrics, val_metrics):
plt.plot(history.history.get(train_metrics), '-o')
plt.plot(history.history.get(val_metrics, '-o'))
plt.ylabel(train_metrics)
plt.xlabel("epoch")
plt.legend(["train", "validation"])

plt.figure(figsize=(12,4))
plt.subplot(1,2,1)
plot_train_history(history, "loss", "val_loss")

plt.subplot(1,2,2)
plot_train_history(history, "accuracy", "val_accuracy")
plt.show()

驗證辨識結果

如果送上去 kaggle 之前可以先看看,模型對每一個角色的辨識率。
對結果大概有個譜XD

1
2
3
4
5
import sklearn
predict = model.predict(x_test)
predict = np.argmax(predict, axis=1)
ans = np.argmax(y_test, axis = 1)
print("\n", sklearn.metrics.classification_report(ans, predict, target_names=list(characters.values())), sep='')

對老師給的測試集進行辨識

  • 將測試資料讀入,並呼教我們的 model 來進行辨識
  • 要注意的是,我們 model 設定最後一層是呼叫機率,但是老師希望我們輸出最大的機率角色即可,因此就找第一項即可
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from keras.models import load_model
from google.colab.patches import cv2_imshow
def read_images(path):
images=[]
for i in range(10791):
#for i in range(3):
print(path+str(i+1)+'.jpg')
image = cv2.resize(cv2.imread(path+str(i+1)+'.jpg'), (pic_height, pic_width))
#image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
#cv2_imshow(image)
images.append(image)
images = np.array(images, dtype=np.float32) / 255
return images

model = load_model("model2.h5")
#imgpath = "/content/drive/My Drive/NTUT/大三上-深度學習/Predict_Simposons/theSimpsons-test/test/" #t108AB0008@ntut.org.tw
imgpath = "/content/drive/.shortcut-targets-by-id/1EmT_yvIKvgl-iNKBU0Wu2XOFjOPsxhyu/大三上-深度學習/Predict_Simposons/theSimpsons-test/test/" #others

test_images = read_images(imgpath)
print(test_images.shape)
predict = model.predict(test_images)
predict = np.argmax(predict, axis=1) #找第一個機率最大的
print(characters[int(predict[2])])

將辨識結果輸出 csv

再來就從 google drive 下載 csv 然後上傳 kaggle

1
2
3
4
5
with open("predict2.csv", "w") as f:
f.write("id,character\n")
for i in range(len(predict)):
text = str(i+1) + "," + characters[int(predict[i])] + "\n"
f.write(text)

參考連結

北科電子系機器學習教授廖元甫老師的學生教學,教得非常好
Day 08:CNN 模型設計 by I code so I am
[ML筆記] Batch Normalization by 陳雲濤的部落格

  • 版權聲明: 本部落格所有文章除有特別聲明外,均採用 Apache License 2.0 許可協議。轉載請註明出處!
  • © 2020-2024 John Doe
  • Powered by Hexo Theme Ayer
  • PV: UV: