編者按:在人工智能的各個領(lǐng)域,深度學(xué)習框架正變得越來越高效,其中圖像分類更是得益于此。在這篇文章中,作者devforfu嘗試用Keras庫將現(xiàn)成的ImageNet和預(yù)訓(xùn)練深度模型應(yīng)用到圖像分類框架上。他搭建了一款模型,可以辨別圖片中狗狗的種類。
數(shù)據(jù)集
在這個項目中,數(shù)據(jù)集使用的Kaggle中辨別狗狗種類的競賽,其中包含了將近10000張經(jīng)過標記的圖像,大約涵蓋了120種狗狗,除此之外還有數(shù)量相當?shù)臏y試數(shù)據(jù)。一般來說,這些圖像的分辨率、縮放程度都是不同的,其中也可能不止有一只狗,光線也會有差別。下面是數(shù)據(jù)集中的幾張圖像。
數(shù)據(jù)集中各種種類的狗狗數(shù)量大致相同,平均每種狗狗有59張圖像。以下是數(shù)據(jù)集中各種類狗狗的數(shù)量分布:
正如我們簡單分析過的,經(jīng)過分析的數(shù)據(jù)集對深度學(xué)習框架來說并不復(fù)雜,并且有很多簡單結(jié)構(gòu)。所以我們可以從數(shù)據(jù)集中得到各種類精確的結(jié)果。
Bottleneck特征
運用預(yù)訓(xùn)練深度學(xué)習模型最直接的策略之一是將它們看作特征提取器。在現(xiàn)代神經(jīng)網(wǎng)絡(luò)架構(gòu)發(fā)展之前,圖像特征是手動過濾的,例如Canny edge detectors中的Sobel operator。運算符(operator)是一個3×3的矩陣,向其中輸入原始圖像后,它會將其轉(zhuǎn)化成一個正方形的圖塊,每個小方塊都會轉(zhuǎn)換成單一值。
Sober運算符在單一圖像通道上的大致表示
如今的技術(shù)可以從數(shù)據(jù)中直接自動提取特征。為此,我們用的是卷積層。每個卷積層都是具有隨機初始值的正方形矩陣堆棧。這些值在訓(xùn)練過程中被更新,最終會收斂到適合數(shù)據(jù)集的特殊過濾器上。
下圖展示了深度學(xué)習分類器作為特征模塊序列的大致表示,將圖像從原始的像素表示轉(zhuǎn)換成更加抽象的表示:
如上圖,一個沒有頂層圖層的深度學(xué)習網(wǎng)絡(luò)為每張通過網(wǎng)絡(luò)的圖像生成了一套高水平的特征。這些特征被稱為bottleneck特征。深度模型自動推理出這些抽象的圖像特征,我們可以用它們以及任意“經(jīng)典”的機器學(xué)習算法來預(yù)測目標。
注意:本文所提到的所有模型和特征提取器都是在單一1080Ti GPU上執(zhí)行的。如果用CPU的話,在有數(shù)千張圖片的數(shù)據(jù)集上提取特征可能要耗費好幾個小時。
因此,想從圖片集中提取特征,研究者需要下載一個有預(yù)訓(xùn)練權(quán)重的深度網(wǎng)絡(luò),但是沒有頂層,之后為這些圖像”做預(yù)測“。示例如下:
classFeaturesExtractor:
"""Runs pretrained model without top layers on dataset and saves generated
bottleneck features onto disk.
"""
def __init__(self, build_fn, preprocess_fn, source,
target_size=(299, 299, 3), batch_size=128):
self.build_fn = build_fn
self.preprocess_fn = preprocess_fn
self.source = source
self.target_size = target_size
self.batch_size = batch_size
def __call__(self, folder, filename, pool='avg'):
model = self.build_fn(weights='imagenet', include_top=False, pooling=pool)
stream = self.source(
folder=folder, target_size=self.target_size,
batch_size=self.batch_size, infinite=False)
batches = []
with tqdm.tqdm_notebook(total=stream.steps_per_epoch) as bar:
for x_batch, y_batch in stream:
x_preprocessed = self.preprocess_fn(x_batch)
batch = model.predict_on_batch(x_preprocessed)
batches.append(batch)
bar.update(1)
all_features = np.vstack(batches)
np.save(filename, all_features)
return filename
代碼的第15行創(chuàng)造了一個沒有頂層的Keras模型,但是具有預(yù)先安裝的ImageNet權(quán)重。之后,22—25行對所有圖片進行了迭代,并將它們轉(zhuǎn)化成特征數(shù)組。之后在第29行,數(shù)組被永久存儲。注意,我們并不一次性下載所有可用圖片,而是創(chuàng)造一個生成器,替代從硬盤中讀取文檔。這樣操作可以允許在沒有足夠RAM的情況下處理大型數(shù)據(jù)集,因為圖片從JPEG轉(zhuǎn)換成PNG格式會消耗很大內(nèi)存。
之后,我們可以用FeatureExtractor:
from keras.applications import inception_v3
extractor = FeatureExtractor(
build_fn=inception_v3.InceptionV3,
preprocess_fn=inception_v3.preprocess_fn,
source=create_files_iterator_factory())
extractor(folder_name, output_file)
Bootstrapped SGD
隨機梯度下降(SGD)是一種簡單高效的在凸形損失函數(shù)對線性分類器進行判別學(xué)習的方法。簡單地講,將圖片特征的數(shù)組和他們的標簽輸入算法中,訓(xùn)練一個多種類的分類器,從而判斷狗狗的種類。
雖然現(xiàn)在已有了一個特殊的SGD分類器,但是還不夠穩(wěn)定。對算法運行多次之后可能會得到不同精度的結(jié)果,因為算法在學(xué)習開始時對參數(shù)進行了隨機初始化。
為了讓訓(xùn)練過程更加穩(wěn)定并且可重復(fù),為了達到最佳精度,我們將用bagging擴展SGD,這種方法可以訓(xùn)練SGD分類器的集合,并且能從多個估算器中對反饋取平均值,得出最終的預(yù)測。下圖是這一過程的展示:
在SGD分類器上應(yīng)用bagging技術(shù)
注意,我們不僅僅是將原始數(shù)據(jù)集拆分成子集,而是將不同特征的自己放入不同分類器訓(xùn)練。另外,我們還應(yīng)用了一個“變量閾值轉(zhuǎn)換器(variance threshold transformer)”,將值接近于0的bottleneck特征過濾掉,因為深度學(xué)習網(wǎng)絡(luò)提取的特征向量似乎非常稀疏。
下面的代碼展示了如何創(chuàng)建一個SGD分類器,以及如何計算能反映模型質(zhì)量的預(yù)測標準:
def sgd(x_train, y_train, x_valid, y_valid, variance_threshold=0.1):
threshold = VarianceThreshold(variance_threshold)
sgd_classifier = SGDClassifier(
alpha=1./len(x_train),
class_weight='balanced',
loss='log', penalty='elasticnet',
fit_intercept=False, tol=0.001, n_jobs=-1)
bagging = BaggingClassifier(
base_estimator=sgd_classifier,
bootstrap_features=True,
n_jobs=-1, max_samples=0.5, max_features=0.5)
x_thresh = threshold.fit_transform(x_train)
bagging.fit(x_thresh, y_train)
train_metrics = build_metrics(bagging, x_thresh, y_train)
x_thresh = threshold.transform(x_valid)
valid_metrics = build_metrics(bagging, x_thresh, y_valid)
return bagging, train_metrics, valid_metrics
def build_metrics(model, X, y):
probs = model.predict_proba(X)
preds = np.argmax(probs, axis=1)
metrics = dict(
probs=probs,
preds=preds,
loss=log_loss(y, probs),
accuracy=np.mean(preds == y))
return namedtuple('Predictions', metrics.keys())(**metrics)
第四行是創(chuàng)建一個SGD分類器的示例,其中有一對正則化參數(shù)和運用CPU訓(xùn)練模型的權(quán)限。第十行創(chuàng)建了一組分類器,15—20行訓(xùn)練了分類器,并計算了幾個表現(xiàn)標準。
SGD基準
現(xiàn)在有很多可用的預(yù)訓(xùn)練深度學(xué)習結(jié)構(gòu),它們在我們的數(shù)據(jù)集上用作特征提取器時是否表現(xiàn)得同樣好?下面讓我們來看看。
我們選取了以下三種結(jié)構(gòu)來訓(xùn)練SGD分類器:
InceptionV3
InceptionResNetV2
Xception
Keras中都包含這三種架構(gòu),每個分類器在9200個樣本上進行訓(xùn)練并在1022個圖像上進行驗證。下表展示了訓(xùn)練和驗證自己的預(yù)測結(jié)果。
這個分數(shù)還不錯!對于如此簡單的實現(xiàn)過程,這個結(jié)果已經(jīng)很令人滿意了。
對預(yù)訓(xùn)練模型進行微調(diào)
在SGD分類器上對bottleneck特征的訓(xùn)練已經(jīng)表示,這些特征能達到良好的預(yù)測效果。然而,我們能夠通過重新訓(xùn)練頂層、對模型進行微調(diào)來提高分類器的精度呢?同樣,我們能否通過對訓(xùn)練集的預(yù)處理,讓模型在過擬合上更穩(wěn)定,并且提高它的泛化能力?
微調(diào)的目的是讓預(yù)訓(xùn)練模型適應(yīng)數(shù)據(jù)。大多數(shù)情況下,重新使用的模型都會在含有不同種類的數(shù)據(jù)集上訓(xùn)練。所以,你需要將網(wǎng)絡(luò)頂端的分類層替換掉。如圖所示:
微調(diào)過程示意
新頂層的訓(xùn)練過程和之前的并沒什么不同,我們只用了不同的分類器。但是,我們可以用數(shù)據(jù)增強的方式提高網(wǎng)絡(luò)的泛化能力。每個微調(diào)過的網(wǎng)絡(luò)都用稍微改動過的圖像訓(xùn)練(例如稍微旋轉(zhuǎn)、縮放等)。
微調(diào)基準
為了測試微調(diào)后的精確度,我們將添加一個結(jié)構(gòu):
ResNet50
InceptionV3
Xception
InceptionResNetV2
每個模型進行100次迭代測試,每次有128個樣本:
from keras.optimizers import SGD
sgd = SGD(lr=0.001, momentum=0.99, nesterov=True)
下面是數(shù)據(jù)增強中參數(shù)的選擇:
from keras.preprocessing.image importImageDataGenerator
transformer = ImageDataGenerator(
width_shift_range=0.2,
height_shift_range=0.2,
zoom_range=0.2,
rotation_range=30,
vertical_flip=False,
horizontal_flip=True)
最后,模型訓(xùn)練過程的實現(xiàn):
from keras.models importModel
from keras.layers importDense
base = create_model(include_top=False)
x = Dense(120, activation='softmax')(x)
model = Model(inputs=base.inputs, outputs=x)
model.compile(optimizer=sgd, loss='categorical_crossentropy')
train_gen = create_training_generator()
valid_gen = create_validation_generator()
model.fit_generator(train_gen, validation_data=valid_gen)
下表是訓(xùn)練后模型的表現(xiàn):
與之前的表現(xiàn)相比并沒有很大的提升,但是數(shù)據(jù)增強和預(yù)訓(xùn)練InceptionResNetV2網(wǎng)絡(luò)頂部單一密集層的表現(xiàn)卻是最好的。
實際案例
讓我們看看模型在陌生圖像上的表現(xiàn)到底如何吧,下圖是數(shù)據(jù)集之外的網(wǎng)絡(luò)圖片,模型預(yù)測了五種最可能的狗狗品種:
模型對有些貓或狗的種類并不完全肯定,也就是說我們可以用它當做狗狗探測器。
結(jié)論
現(xiàn)成的模型在這個項目中的表現(xiàn)十分不錯,在狗狗分類數(shù)據(jù)集上訓(xùn)練之后就能達到精確的結(jié)果。我相信如果再增加更多的頂層、“解鎖”更多隱藏層、加入正則化技術(shù)和更多優(yōu)化,模型的表現(xiàn)會更好。
但是這一實驗的缺點是選擇的數(shù)據(jù)集是從ImageNet上挑選的狗類圖片,也就是說我們的網(wǎng)絡(luò)可能之前就見過它們了。關(guān)于這一問題論智君也曾報道過數(shù)據(jù)集的重復(fù)使用所帶來的副作用,可能會引起一些偏差。
-
人工智能
+關(guān)注
關(guān)注
1791文章
47352瀏覽量
238775 -
數(shù)據(jù)集
+關(guān)注
關(guān)注
4文章
1208瀏覽量
24725 -
深度學(xué)習
+關(guān)注
關(guān)注
73文章
5504瀏覽量
121230
原文標題:拉布拉多還是巴哥?用Keras輕松識別狗子的品種
文章出處:【微信號:jqr_AI,微信公眾號:論智】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論