站在巨人的肩膀上:遷移學習

在上一篇文章《使用數據增強技術提升模型泛化能力》中,我們針對訓練數據不足的問題,提出採用數據增強(data augmentation)技術,提升模型的準確率。最終結果是:在17flowers數據集上,我們將準確率從60%多增加到70%,取得了不錯的效果。然而,對於一個商業應用來說,70%多的準確率還是有些拿不出手。我們還有更好的手段嗎?

在這篇文章中,我將介紹一種深度學習的利器:遷移學習(transfer learning),來幫助我們提高深度學習的準確率。

遷移學習

對於深度學習而言,遷移學習並不是一個高深的概念和技術。顧名思義,遷移學習就是把已經訓練好的模型參數遷移到新的模型上,幫助新模型訓練。這也近似於人類的學習過程,即所謂的舉一反三。

在我之前寫的一篇文章《TensorFlow Hub:探索機器學習組件化》中,我憧憬了未來的機器學習組件化的場景,這其中最核心的就是遷移學習,我們能夠在其他人訓練的模型的基礎上,根據業務需求,訓練滿足特定需求的機器學習模型。

遷移學習領域有一篇公認的比較好的綜述:A Survey on Transfer Learning,有興趣可以找來看看。當然這篇論文是很早以前的(2013),裏面沒有介紹最新的研究。如果你對理論沒啥興趣,沒有關係,不影響閱讀後面的內容。

遷移學習是如何做到改善模型的呢?這要從特徵提取說起。

特徵提取

所謂特徵,就是一事物異於其他事物的特點。比如,我們判斷動物是否昆蟲,有一個簡單的原則:少於三對或多於三對足的動物都不是昆蟲。再比如我們識別貓和狗,也一定是從某些特徵入手,雖然有些時候我們並不能清晰的描述出特徵。

在深度學習流行之前,人們通常手工提取特徵,這通常面臨着特徵提取困難、效率低下等問題。到了深度學習階段,我們通常採用端到端的訓練方式,也就是由計算機自動識別特徵(
爲了提高效率,訓練前,我們也可能會對數據進行預處理,比如歸一化、圖片縮放等等,但這和以前的特徵提取並不是一回事)。

在計算機視覺領域,卷積神經網絡是應用得最廣泛的模型,瞭解卷積神經網絡的同學可能知道,卷積運算實際上是進行圖像特徵的提取,到最後一層,纔是進行分類(softmax、logistic),所以如果我們獲得最後一層的輸入,也就得到了提取的特徵。

這個在keras中很容易做到,以VGG16爲例:

model = VGG16(weights="imagenet", include_top=False)

以上代碼構造VGG16模型,採用imagenet數據集訓練出的權重,include_top參數決定是否包含最後的輸出層,因爲我們的目的是提取特徵,所以該參數設爲False。

下面的代碼對數據集進行特徵提取,並保存到hdf5格式的文件中,雖然我們無法形象化的看出到底提取到什麼特徵,但這個數據對下一步的遷移學習有用。

image_paths = list(paths.list_images(args["dataset"]))
random.shuffle(image_paths)

labels = [p.split(os.path.sep)[-2] for p in image_paths]
le = LabelEncoder()
labels = le.fit_transform(labels)

model = VGG16(weights="imagenet", include_top=False)

dataset = HDF5DatasetWriter((len(image_paths), 512 * 7 * 7), args["output"], data_key="features", buf_size=args["buffer_size"])
dataset.store_class_labels(le.classes_)

for i in np.arange(0, len(image_paths), bs):
  batch_paths = image_paths[i : i+bs]
  batch_labels = labels[i : i + bs]
  batch_images = []

  for (j, image_path) in enumerate(batch_paths):
    image = load_img(image_path, target_size=(224, 224))
    image = img_to_array(image)

    image = np.expand_dims(image, axis=0)
    image = imagenet_utils.preprocess_input(image)
    batch_images.append(image)

  batch_images = np.vstack(batch_images)
  features = model.predict(batch_images, batch_size=bs)
  features = features.reshape((features.shape[0], 512 * 7 * 7))
  dataset.add(features, batch_labels)

dataset.close()

遷移學習實例

在上一步的特徵提取中,使用的權重數據是來自imagenet數據集訓練出的,imagenet屬於超大規模數據集,包含1500萬張圖片,對應2萬多種類別。這樣的數據集,和17flowers數據集的差別很大,那我們能否使用VGG16提取17flowers的有效特徵,然後進行分類呢?

我們使用上一步驟提取的特徵,然後應用簡單的Logistic迴歸算法進行分類:

db = h5py.File(args["db"], "r")
i = int(db["labels"].shape[0] * 0.75)

print("[INFO] tuning hyperparameters ...")
params = {"C": [0.1, 1.0, 10.0, 100.0, 1000.0, 10000.0]}
model = GridSearchCV(LogisticRegression(), params, cv=3, n_jobs=args["jobs"])
model.fit(db["features"][:i], db["labels"][:i])
print("[INFO] best hyperparameters: {}".format(model.best_params_))

print("[INFO] evaluating ...")
preds = model.predict(db["features"][i:])
print(classification_report(db["labels"][i:], preds, target_names=db["label_names"]))

print("[INFO] saving model ...")
f = open(args["model"], "w")
f.write(pickle.dumps(model.best_estimator_))
f.close()

db.close()

結果如下:

image

有沒有感覺到意外,準確率達到了不可思議的93%,要知道我們使用的VGG16模型是使用imagenet數據集訓練出的權重,其類別和17flowers有天壤之別,但這種遷移學習效果就是這麼明顯。這也證明了,諸如VGG之類的網絡能夠進行遷移學習,將其判別特徵編碼爲輸出,我們可以使用它來訓練我們自己的自定義圖像分類器。

總結

通常,遷移學習應用於深度學習和計算機視覺時,有兩種方法:

  1. 將網絡視爲特徵提取器,將圖像向前傳播到給定層,將它們視爲特徵向量。
  2. 通過向網絡頭部添加一組全新的全連接層並調整這些FC層以識別新類(同時仍使用相同的基礎CONV過濾器)來微調網絡。

本文探討的是第一種方法,我們將VGG、Inception、ResNet作爲強大的特徵提取器,可以讓我們在數量有限的數據集也能訓練出效果不錯的模型。

以上實例均有完整的代碼,點擊閱讀原文,跳轉到我在github上建的示例代碼。

另外,我在閱讀《Deep Learning for Computer Vision with Python》這本書,在微信公衆號後臺回覆“計算機視覺”關鍵字,可以免費下載這本書的電子版。

往期回顧

  1. 使用數據增強技術提升模型泛化能力
  2. 計算機視覺與深度學習,看這本書就夠了
  3. 提高模型性能,你可以嘗試這幾招…
  4. TensorFlow Hub:探索機器學習組件化

image

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章