在上一篇文章《使用數據加強技術提高模型泛化能力》中,咱們針對訓練數據不足的問題,提出採用數據加強(data augmentation)技術,提高模型的準確率。最終結果是:在17flowers數據集上,咱們將準確率從60%多增長到70%,取得了不錯的效果。然而,對於一個商業應用來講,70%多的準確率仍是有些拿不出手。咱們還有更好的手段嗎?git
在這篇文章中,我將介紹一種深度學習的利器:遷移學習(transfer learning),來幫助咱們提升深度學習的準確率。github
對於深度學習而言,遷移學習並非一個高深的概念和技術。顧名思義,遷移學習就是把已經訓練好的模型參數遷移到新的模型上,幫助新模型訓練。這也近似於人類的學習過程,即所謂的觸類旁通。web
在我以前寫的一篇文章《TensorFlow Hub:探索機器學習組件化》中,我憧憬了將來的機器學習組件化的場景,這其中最核心的就是遷移學習,咱們可以在其餘人訓練的模型的基礎上,根據業務需求,訓練知足特定需求的機器學習模型。算法
遷移學習領域有一篇公認的比較好的綜述:A Survey on Transfer Learning,有興趣能夠找來看看。固然這篇論文是很早之前的(2013),裏面沒有介紹最新的研究。若是你對理論沒啥興趣,沒有關係,不影響閱讀後面的內容。bash
遷移學習是如何作到改善模型的呢?這要從特徵提取提及。微信
所謂特徵,就是一事物異於其餘事物的特色。好比,咱們判斷動物是否昆蟲,有一個簡單的原則:少於三對或多於三對足的動物都不是昆蟲。再好比咱們識別貓和狗,也必定是從某些特徵入手,雖然有些時候咱們並不能清晰的描述出特徵。網絡
在深度學習流行以前,人們一般手工提取特徵,這一般面臨着特徵提取困難、效率低下等問題。到了深度學習階段,咱們一般採用端到端的訓練方式,也就是由計算機自動識別特徵( 爲了提升效率,訓練前,咱們也可能會對數據進行預處理,好比歸一化、圖片縮放等等,但這和之前的特徵提取並非一回事)。app
在計算機視覺領域,卷積神經網絡是應用得最普遍的模型,瞭解卷積神經網絡的同窗可能知道,卷積運算其實是進行圖像特徵的提取,到最後一層,纔是進行分類(softmax、logistic),因此若是咱們得到最後一層的輸入,也就獲得了提取的特徵。dom
這個在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之類的網絡可以進行遷移學習,將其判別特徵編碼爲輸出,咱們可使用它來訓練咱們本身的自定義圖像分類器。
一般,遷移學習應用於深度學習和計算機視覺時,有兩種方法:
本文探討的是第一種方法,咱們將VGG、Inception、ResNet做爲強大的特徵提取器,可讓咱們在數量有限的數據集也能訓練出效果不錯的模型。
以上實例均有完整的代碼,點擊閱讀原文,跳轉到我在github上建的示例代碼。
另外,我在閱讀《Deep Learning for Computer Vision with Python》這本書,在微信公衆號後臺回覆「計算機視覺」關鍵字,能夠免費下載這本書的電子版。