用卷積神經網路模型辨認不同服裝,四種方法對比
作者:James Le
編譯:Bing
在實體店買衣服的過程中,我們會被眼花繚亂的資訊轟炸。要兼顧某件衣服的款式、價格、商場優惠還不夠,商場的燈光、擁擠的過道,無時無刻不在考驗著人們分辨資訊的能力。
那麼,電腦能自動檢測襯衫、褲子、連衣裙或運動鞋的照片嗎?事實證明,如果有高質量的訓練資料,準確地對時尚單品圖片進行分類是很很容易做到的。在本文中,我們將講解如何用Fashion-MNIST資料集搭建一個用於辨認時尚單品的機器學習模型。我們會提到如何訓練模型、針對類別分類設計輸入和輸出以及每個模型的最終結果。
影象分類
這一任務中涉及到的問題包括視角的變化、尺度變化、同類的多種變化、照片形變、照片遮擋、光線條件、背景等問題。如何寫出一套可以區分圖片型別的演算法呢?計算機視覺研究人員提出了一種資料驅動的方法來解決。過去,人們會了解每一圖片類別的程式碼,從而進行分類。現在,研究人員從每類影象中都選出一些樣本,訓練深度學習演算法學習每種類別的特點。換句話說,他們首先收集帶標籤的訓練資料集,然後輸入到計算機中,讓計算機熟悉這些資料。

主要步驟如下:
- 輸入的是一個含有N張圖片的資料集,每張圖片都帶有類別標籤,資料集中共有K個不同的類別。
- 之後,我們用訓練集去訓練一個分類器,學習每種類別的樣式。
- 最後,讓分類器對陌生圖片進行標籤預測,從而對分類器質量進行評估。之後我們會比較真實標籤和分類器的預測標籤。
Fashion MNIST資料集
去年八月份,德國研究機構Zalando Research在GitHub上推出了一個全新的資料集,其中訓練集包含60000個樣例,測試集包含10000個樣例,分為10類,其中的樣本都來自日常穿著的衣褲鞋包,每個都是28×28的灰度影象,其中總共有10類標籤,每張影象都有各自的標籤。

10種標籤包括:
- 0:T-shirt/上衣
- 1:褲子
- 2:套頭衫
- 3:連衣裙
- 4:大衣
- 5:涼鞋
- 6:襯衣
- 7:運動鞋
- 8:包
- 9:高幫鞋
Fashion MNIST的視覺化嵌入
嵌入是一種將分散目標(影象、文字等等)對映到高維向量中的方法。這些向量中的每個維度通常都沒有內在意義,機器學習運用的是想兩件的距離和總體的模式。在這裡,我打算用TensorBoard表示高維的Fashion MNIST資料。閱讀了資料並建立測試標籤後,我用以下程式碼建立了TensorBoard的嵌入投射器:
from tensorflow.contrib.tensorboard.plugins import projector logdir = 'fashionMNIST-logs' # Creating the embedding variable with all the images defined above under X_test embedding_var = tf.Variable(X_test, name='fmnist_embedding') # Format: tensorflow/contrib/tensorboard/plugins/projector/projector_config.proto config = projector.ProjectorConfig() # You can add multiple embeddings. Here I add only one. embedding = config.embeddings.add() embedding.tensor_name = embedding_var.name # Link this tensor to its metadata file (e.g. labels). embedding.metadata_path = os.path.join(logdir, 'metadata.tsv') # Use this logdir to create a summary writer summary_writer = tf.summary.FileWriter(logdir) # The next line writes a projector_config.pbtxt in the logdir. TensorBoard will read this file during startup. projector.visualize_embeddings(summary_writer,config) # Periodically save the model variables in a checkpoint in logdir. with tf.Session() as sesh: sesh.run(tf.global_variables_initializer()) saver = tf.train.Saver() saver.save(sesh, os.path.join(logdir, 'model.ckpt')) # Create the sprite image rows = 28 cols = 28 label = ['T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat', 'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot'] sprite_dim = int(np.sqrt(X_test.shape[0])) sprite_image = np.ones((cols * sprite_dim, rows * sprite_dim)) index = 0 labels = [] for i in range(sprite_dim): for j in range(sprite_dim): labels.append(label[int(Y_test[index])]) sprite_image[ i * cols: (i + 1) * cols, j * rows: (j + 1) * rows ] = X_test[index].reshape(28, 28) * -1 + 1 index += 1 # After constructing the sprite, I need to tell the Embedding Projector where to find it embedding.sprite.image_path = os.path.join(logdir, 'sprite.png') embedding.sprite.single_image_dim.extend([28, 28]) # Create the metadata (labels) file with open(embedding.metadata_path, 'w') as meta: meta.write('Index\tLabel\n') for index, label in enumerate(labels): meta.write('{}\t{}\n'.format(index, label))
該投射器有三種對資料集降維的方法,兩種線性方法,一種非線性方法。每種方法都能用於建立二維或三維場景。
PCA :PCA是一種用於直接降維的技術,嵌入投射器計算前十位的主要成分,通過選單,我們可以將那些成分投射成任意兩種或三種的結合上。PCA是一個線性工具,通常在檢查全域性幾何結構上非常高效。

t-SNE :這是一種流行的非線性降維方法,嵌入投射器會同時提供二維和三維的t-SNE檢視。在客戶端可以看到平面圖,能展示出每一步演算法。由於t-SNE通常會保留一些區域性結構,這在探索區域性近鄰、找尋聚類中是很有用的。

Custom :我還可以基於文字搜尋建立特殊的線性投射器,用於在空間中尋找有意義的方向。程式可以計算這些點集的中心點,他們的標籤可以與搜尋匹配,利用向量之間的不同作為投射器的軸。

檢視完成的視覺化步驟程式碼,可點選此網址: ofollow,noindex">github.com/khanhnamle1994/fashion-mnist/blob/master/TensorBoard-Visualization.ipynb
在Fashion MNIST上訓練CNN模型
接下來,我會建立多個基於CNN的分類模型,在Fashion MNIST上評估模型效能。我會用Keras框架搭建模型,想了解框架的更多資訊,可以點選此網址:keras.io/。下面是我想要比較的四種模型:
- 有一層卷積層的CNN
- 有三層卷積層的CNN
- 有四層卷積層的CNN
- 經過與訓練的VGG-19模型
除了預訓練模型,我的方法如下:
- 將原始訓練資料(共60000張圖片)分成訓練集和驗證集兩部分,80%是訓練集,20%是驗證集,另外還有10000張測試圖片,用於最終測試模型在陌生影象上的表現。。這可以研究模型是否在訓練資料上過度擬合,以及是否應該降低學習速率。
- 用256的batch size訓練模型10個epoch,同時用categorical_crossentropy損失函式和Adam優化器共同編譯。
- 之後進行資料增強,可以通過旋轉、變化、縮放生成新的訓練樣本,再通過50個epoch對模型進行訓練。
以下是下載資料並分離資料的程式碼:
# Import libraries from keras.utils import to_categorical from sklearn.model_selection import train_test_split # Load training and test data into dataframes data_train = pd.read_csv('data/fashion-mnist_train.csv') data_test = pd.read_csv('data/fashion-mnist_test.csv') # X forms the training images, and y forms the training labels X = np.array(data_train.iloc[:, 1:]) y = to_categorical(np.array(data_train.iloc[:, 0])) # Here I split original training data to sub-training (80%) and validation data (20%) X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2, random_state=13) # X_test forms the test images, and y_test forms the test labels X_test = np.array(data_test.iloc[:, 1:]) y_test = to_categorical(np.array(data_test.iloc[:, 0]))
下載資料後,我會重新處理它們,讓它們的值處於0到1之間。此前,訓練資料都被儲存在一個(60000,28,28)的陣列中,其中的值在0到255之間。
# Each image's dimension is 28 x 28 img_rows, img_cols = 28, 28 input_shape = (img_rows, img_cols, 1) # Prepare the training images X_train = X_train.reshape(X_train.shape[0], img_rows, img_cols, 1) X_train = X_train.astype('float32') X_train /= 255 # Prepare the test images X_test = X_test.reshape(X_test.shape[0], img_rows, img_cols, 1) X_test = X_test.astype('float32') X_test /= 255 # Prepare the validation images X_val = X_val.reshape(X_val.shape[0], img_rows, img_cols, 1) X_val = X_val.astype('float32') X_val /= 255
一層卷積層CNN
下面是隻有一層卷積層的CNN程式碼:
from keras.models import Sequential from keras.layers import Dense, Dropout, Flatten from keras.layers import Conv2D, MaxPooling2D cnn1 = Sequential() cnn1.add(Conv2D(32, kernel_size=(3, 3), activation='relu', input_shape=input_shape)) cnn1.add(MaxPooling2D(pool_size=(2, 2))) cnn1.add(Dropout(0.2)) cnn1.add(Flatten()) cnn1.add(Dense(128, activation='relu')) cnn1.add(Dense(10, activation='softmax')) cnn1.compile(loss=keras.losses.categorical_crossentropy, optimizer=keras.optimizers.Adam(), metrics=['accuracy'])
訓練完模型後,下面是測試損失和測試精確度:

進行資料增強後的測試損失和測試精確度:

訓練和驗證精確度和損失的視覺化:


完整程式碼請點選: github.com/khanhnamle1994/fashion-mnist/blob/master/CNN-1Conv.ipynb
三層卷積層CNN
有三層卷積層的CNN程式碼:
from keras.models import Sequential from keras.layers import Dense, Dropout, Flatten from keras.layers import Conv2D, MaxPooling2D cnn3 = Sequential() cnn3.add(Conv2D(32, kernel_size=(3, 3), activation='relu', input_shape=input_shape)) cnn3.add(MaxPooling2D((2, 2))) cnn3.add(Dropout(0.25)) cnn3.add(Conv2D(64, kernel_size=(3, 3), activation='relu')) cnn3.add(MaxPooling2D(pool_size=(2, 2))) cnn3.add(Dropout(0.25)) cnn3.add(Conv2D(128, kernel_size=(3, 3), activation='relu')) cnn3.add(Dropout(0.4)) cnn3.add(Flatten()) cnn3.add(Dense(128, activation='relu')) cnn3.add(Dropout(0.3)) cnn3.add(Dense(10, activation='softmax')) cnn3.compile(loss=keras.losses.categorical_crossentropy, optimizer=keras.optimizers.Adam(), metrics=['accuracy'])
訓練模型後,測試損失和測試精確度:

資料增強後的測試損失和精確度:

訓練和驗證的精確度和損失:


完整程式碼請點選: github.com/khanhnamle1994/fashion-mnist/blob/master/CNN-3Conv.ipynb
四層卷積層的CNN
下面是四層卷積層的CNN程式碼:
from keras.models import Sequential from keras.layers import Dense, Dropout, Flatten from keras.layers import Conv2D, MaxPooling2D, BatchNormalization cnn4 = Sequential() cnn4.add(Conv2D(32, kernel_size=(3, 3), activation='relu', input_shape=input_shape)) cnn4.add(BatchNormalization()) cnn4.add(Conv2D(32, kernel_size=(3, 3), activation='relu')) cnn4.add(BatchNormalization()) cnn4.add(MaxPooling2D(pool_size=(2, 2))) cnn4.add(Dropout(0.25)) cnn4.add(Conv2D(64, kernel_size=(3, 3), activation='relu')) cnn4.add(BatchNormalization()) cnn4.add(Dropout(0.25)) cnn4.add(Conv2D(128, kernel_size=(3, 3), activation='relu')) cnn4.add(BatchNormalization()) cnn4.add(MaxPooling2D(pool_size=(2, 2))) cnn4.add(Dropout(0.25)) cnn4.add(Flatten()) cnn4.add(Dense(512, activation='relu')) cnn4.add(BatchNormalization()) cnn4.add(Dropout(0.5)) cnn4.add(Dense(128, activation='relu')) cnn4.add(BatchNormalization()) cnn4.add(Dropout(0.5)) cnn4.add(Dense(10, activation='softmax')) cnn4.compile(loss=keras.losses.categorical_crossentropy, optimizer=keras.optimizers.Adam(), metrics=['accuracy'])
訓練模型後的測試損失和精確度:

資料增強後的測試損失和精確度:

訓練和驗證的精確度和損失:


完整程式碼請點選: github.com/khanhnamle1994/fashion-mnist/blob/master/CNN-4Conv.ipynb
遷移學習
在小資料集上常用的一種高效深度學習方法就是使用預訓練網路。一種預訓練網路是此前在大型資料集上訓練過的網路,通常處理過大型影象分類任務。如果這樣的原始資料集足夠大並且足夠通用,那麼預訓練網路學習到的特徵空間分層可以用於視覺世界的普通模型,它的特徵可以解決很多不同的計算機視覺問題。
我嘗試使用VGG19預訓練模型,這在ImageNet中廣泛使用的ConvNets架構。以下是所用的程式碼:
import keras from keras.applications import VGG19 from keras.applications.vgg19 import preprocess_input from keras.layers import Dense, Dropout from keras.models import Model from keras import models from keras import layers from keras import optimizers # Create the base model of VGG19 vgg19 = VGG19(weights='imagenet', include_top=False, input_shape = (150, 150, 3), classes = 10) # Preprocessing the input X_train = preprocess_input(X_train) X_val = preprocess_input(X_val) X_test = preprocess_input(X_test) # Extracting features train_features = vgg19.predict(np.array(X_train), batch_size=256, verbose=1) test_features = vgg19.predict(np.array(X_test), batch_size=256, verbose=1) val_features = vgg19.predict(np.array(X_val), batch_size=256, verbose=1) # Flatten extracted features train_features = np.reshape(train_features, (48000, 4*4*512)) test_features = np.reshape(test_features, (10000, 4*4*512)) val_features = np.reshape(val_features, (12000, 4*4*512)) # Add Dense and Dropout layers on top of VGG19 pre-trained model = models.Sequential() model.add(layers.Dense(512, activation='relu', input_dim=4 * 4 * 512)) model.add(layers.Dropout(0.5)) model.add(layers.Dense(10, activation="softmax")) # Compile the model model.compile(loss=keras.losses.categorical_crossentropy, optimizer=keras.optimizers.Adam(), metrics=['accuracy'])
模型訓練後,測試損失和精確度如下:

視覺化:


結語
時尚領域是計算機視覺和機器學習應用的熱門,由於其中的予以複雜性,導致問題難度增加。希望這篇文章能對你在影象分類任務上有所啟發。