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

雲水木石發表於2019-01-23

在上一篇文章《使用資料增強技術提升模型泛化能力》中,我們針對訓練資料不足的問題,提出採用資料增強(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()複製程式碼

結果如下:

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

有沒有感覺到意外,準確率達到了不可思議的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

來源:https://juejin.im/post/5c48580e6fb9a049a42f63e0

相關文章