貓還是狗?讓AI瞅瞅!
來自專欄景略集智
有沒有開過一個腦洞,自己動手搭建一個模型可以分辨阿貓阿狗?別害羞了,現在就教你,快上車!
阿貓還是阿狗?
在 Kaggle 上有個叫 Dogs VS Cats 的挑戰賽(https://www.kaggle.com/c/dogs-vs-cats),我們今天所有的數據就來自這個挑戰賽的數據集,包含25000張標記了的阿狗阿貓的圖像,如下所示:
我們看看具體的圖像。每張圖像都可以用 3 維數組表示出來,我們會將所有的訓練圖像重新調整為 50 X 50 像素大小。舉個栗子:
此外,我們還會去掉圖像的所有色彩,將它們變成黑白照片。
首先最要緊的是準備我們需要的編程環境。
設置
從 Kaggle(https://www.kaggle.com/c/dogs-vs-cats/data)上下載訓練和測試模型用的圖像壓縮文件,並將它們提取到我們當前的工作目錄中。
import cv2import numpy as npimport os from random import shuffle from tqdm import tqdm import tensorflow as tfimport matplotlib.pyplot as pltimport tflearnfrom tflearn.layers.conv import conv_2d, max_pool_2dfrom tflearn.layers.core import input_data, dropout, fully_connectedfrom tflearn.layers.estimator import regression%matplotlib inlineTRAIN_DIR = trainTEST_DIR = testIMG_SIZE = 50LR = 1e-3MODEL_NAME = dogs-vs-cats-convnet
圖像預處理
我們有 25000 張圖像用於訓練模型,12500 張圖像用於測試模型。我們創建一個函數,用它來編碼訓練圖像的標籤:
def create_label(image_name): """ Create an one-hot encoded vector from image name """ word_label = image_name.split(.)[-3] if word_label == cat: return np.array([1,0]) elif word_label == dog: return np.array([0,1])
現在我們看看訓練和測試數據的實際讀數。每張圖像會被重新調整為 50 X 50 像素大小,讀取為灰度模式:
def create_train_data(): training_data = [] for img in tqdm(os.listdir(TRAIN_DIR)): path = os.path.join(TRAIN_DIR, img) img_data = cv2.imread(path, cv2.IMREAD_GRAYSCALE) img_data = cv2.resize(img_data, (IMG_SIZE, IMG_SIZE)) training_data.append([np.array(img_data), create_label(img)]) shuffle(training_data) np.save(train_data.npy, training_data) return training_datadef create_test_data(): testing_data = [] for img in tqdm(os.listdir(TEST_DIR)): path = os.path.join(TEST_DIR,img) img_num = img.split(.)[0] img_data = cv2.imread(path, cv2.IMREAD_GRAYSCALE) img_data = cv2.resize(img_data, (IMG_SIZE, IMG_SIZE)) testing_data.append([np.array(img_data), img_num]) shuffle(testing_data) np.save(test_data.npy, testing_data)return testing_data
現在我們分拆數據。24500 張圖像用於訓練模型,500 張圖像用於測試模型。我們還需要重新調整數據大小,以適應 TensorFlow:
# 如果沒有創建數據集:train_data = create_train_data()test_data = create_test_data()# 如果已創建數據集:# train_data = np.load(train_data.npy)# test_data = np.load(test_data.npy)train = train_data[:-500]test = train_data[-500:]X_train = np.array([i[0] for i in train]).reshape(-1, IMG_SIZE, IMG_SIZE, 1)y_train = [i[1] for i in train]X_test = np.array([i[0] for i in test]).reshape(-1, IMG_SIZE, IMG_SIZE, 1)y_test = [i[1] for i in test]
卷積神經網路
我們該怎麼分辨汪星人和喵星人呢?是不是很難?別擔心,我們有神器——卷積神經網路 !
在以前,人們不得不考慮編寫可能和手頭工作相關的不同的特徵。在我們識別阿貓阿狗的例子里,就是耳朵、尾巴、毛髮、鬍鬚等這些特徵。不過,現在我們有了卷積神經網路,這些都不再是事兒,它能夠從初始數據中學習。
關於卷積網路的工作原理,如果不是很了解,牆裂推薦我站的這篇講解:
https://jizhi.im/blog/post/intuitive_explanation_cnn
應該能明白卷積神經網路是怎麼一回事了吧?你可以把卷積當成一些很小的可以滑動的晶體(比方說 5 X 5 大小),當它們放在與它們相似的一些特徵上面時,就會被「激活」。這樣以來,卷積可以弄明白圖像更大範圍的內容,而不僅僅只有一個像素。
搭建我們的模型
最終,有趣的地方到了!我們會用 tflearn(http://tflearn.org/)搭建我們的卷積神經網路,會額外再用一個丟棄層。這是我們的模型:
tf.reset_default_graph()convnet = input_data(shape=[None, IMG_SIZE, IMG_SIZE, 1], name=input)convnet = conv_2d(convnet, 32, 5, activation=relu)convnet = max_pool_2d(convnet, 5)convnet = conv_2d(convnet, 64, 5, activation=relu)convnet = max_pool_2d(convnet, 5)convnet = fully_connected(convnet, 1024, activation=relu)convnet = dropout(convnet, 0.8)convnet = fully_connected(convnet, 2, activation=softmax)convnet = regression(convnet, optimizer=adam, learning_rate=LR, loss=categorical_crossentropy, name=targets)model = tflearn.DNN(convnet, tensorboard_dir=log, tensorboard_verbose=0)model.fit({input: X_train}, {targets: y_train}, n_epoch=10, validation_set=({input: X_test}, {targets: y_test}), snapshot_step=500, show_metric=True, run_id=MODEL_NAME)
結果為:
Training Step: 3829 | total loss: [1m[32m11.45499[0m[0m | time: 35.818s| Adam | epoch: 010 | loss: 11.45499 - acc: 0.5025 -- iter: 24448/24500Training Step: 3830 | total loss: [1m[32m11.49676[0m[0m | time: 36.938s| Adam | epoch: 010 | loss: 11.49676 - acc: 0.5007 | val_loss: 11.60503 - val_acc: 0.4960 -- iter: 24500/24500--
我們將圖像大小重新調整為 50 X 50 X 1 矩陣,這是我們在用的輸入圖像的尺寸。
接著,創建一個有 32 個過濾器和步幅為 5 的卷積層,激活函數為 ReLU。然後,添加一個最大池化層。以 64 個過濾器重複這個過程。
接下來,添加一個有 1024 個神經元的全連接層。最後使用一個概率保持為 0.8 的丟棄層,完成模型的搭建。
我們使用 Adam 為優化器,學習率設為 0.001。我們的損失函數為分類交叉熵損失。
最終,我們將搭建的深度學習模型訓練 10 個周期。
一切進展不錯,但我們的驗證準確率似乎不是很好。所以我們接著搭建一個更大更好一點的模型。
搭建更好的模型
tf.reset_default_graph()convnet = input_data(shape=[None, IMG_SIZE, IMG_SIZE, 1], name=input)convnet = conv_2d(convnet, 32, 5, activation=relu)convnet = max_pool_2d(convnet, 5)convnet = conv_2d(convnet, 64, 5, activation=relu)convnet = max_pool_2d(convnet, 5)convnet = conv_2d(convnet, 128, 5, activation=relu)convnet = max_pool_2d(convnet, 5)convnet = conv_2d(convnet, 64, 5, activation=relu)convnet = max_pool_2d(convnet, 5)convnet = conv_2d(convnet, 32, 5, activation=relu)convnet = max_pool_2d(convnet, 5)convnet = fully_connected(convnet, 1024, activation=relu)convnet = dropout(convnet, 0.8)convnet = fully_connected(convnet, 2, activation=softmax)convnet = regression(convnet, optimizer=adam, learning_rate=LR, loss=categorical_crossentropy, name=targets)model = tflearn.DNN(convnet, tensorboard_dir=log, tensorboard_verbose=0)model.fit({input: X_train}, {targets: y_train}, n_epoch=10, validation_set=({input: X_test}, {targets: y_test}), snapshot_step=500, show_metric=True, run_id=MODEL_NAME)
結果為:
Training Step: 3829 | total loss: [1m[32m0.34434[0m[0m | time: 44.501s| Adam | epoch: 010 | loss: 0.34434 - acc: 0.8466 -- iter: 24448/24500Training Step: 3830 | total loss: [1m[32m0.35046[0m[0m | time: 45.619s| Adam | epoch: 010 | loss: 0.35046 - acc: 0.8432 | val_loss: 0.50006 - val_acc: 0.7860 -- iter: 24500/24500--
這一步搭建的模型和之前的模型非常像,不同之處就是我們添加了更多的卷積層和最大池化層。所以我們的模型有了更多的參數,可以學習更複雜的函數。我們也可以看到驗證準確率達到了 80 %。現在讓模型開始幹活!
我們看看模型分辨一張圖像的結果:
d = test_data[0]img_data, img_num = ddata = img_data.reshape(IMG_SIZE, IMG_SIZE, 1)prediction = model.predict([data])[0]fig = plt.figure(figsize=(6, 6))ax = fig.add_subplot(111)ax.imshow(img_data, cmap="gray")print(f"cat: {prediction[0]}, dog: {prediction[1]}")
cat: 0.8773844838142395, dog: 0.12261549383401871
看起來不對,那再讓它看看更多的圖像:
fig=plt.figure(figsize=(16, 12))for num, data in enumerate(test_data[:16]): img_num = data[1] img_data = data[0] y = fig.add_subplot(4, 4, num+1) orig = img_data data = img_data.reshape(IMG_SIZE, IMG_SIZE, 1) model_out = model.predict([data])[0] if np.argmax(model_out) == 1: str_label=Dog else: str_label=Cat y.imshow(orig, cmap=gray) plt.title(str_label) y.axes.get_xaxis().set_visible(False) y.axes.get_yaxis().set_visible(False)plt.show()
我們可以看到,模型正確的分辨出了上面的阿貓阿狗!
結語
本文我們用卷積神經網路搭建了一個 AI 模型,只用原始像素(也就是只經過了少許預處理)就能分辨出圖像中是貓還是狗。重要的是,在比較老舊的機器上,模型的訓練速度也很快!
可以試著自己操作一番,看看 AI 能否認出你家的汪星人和喵星人!
參考資料:
https://medium.com/@curiousily/tensorflow-for-hackers-part-iii-convolutional-neural-networks-c077618e590b
推薦閱讀:
※如何用900秒搭建一個深度學習模型?
※細思恐極:Google Home和Alexa相互勾搭聊天
※怎樣用深度學習發現一首歌屬於哪個流派?
※開局一張圖,人臉全靠編:AI學會實時人臉3維重建
※我們離《黑鏡》中的黑科技還有多遠?