提高模型準確率:組合模型
各位朋友,新年好! 隨著春節假期的結束,想必大家陸陸續續返回工作崗位,開始新的一年的拼搏。我也會繼續努力,爭取在深度學習方面更進一步,接下來,我將繼續聊一聊深度學習在計算機視覺中的應用。
在前面的《站在巨人的肩膀上:遷移學習 》和《再談遷移學習:微調網路 》兩篇文章中,我們介紹了遷移學習的強大之處。然而,人們探索新知識總是永無止境,在提高深度學習模型準確率方面,仍在孜孜不倦的追求著。這篇文章將介紹一種提升模型準確率的方法:組合模型。
從字面上理解,組合模型並不難解釋,簡單說,就是為深度學習建立多個模型,然後用多個模型來預測,採取投票或平均法來決定最後的預測結果。稍微想一想,似乎比較好理解,俗話說,三個臭皮匠,頂個諸葛亮 。多個模型投票的結果,應該好於單個模型的準確率。當然,機器學習看起來有些不靠譜(拿概率說事),但還是建立在嚴密的理論基礎之上,組合模型提高準確率如果僅僅建立在一條諺語之上,不足以說服人,也沒辦法讓人接受。
事實上,組合模型是建立在一個稱為琴生不等式 (Jensen’s inequality)之上,該公式以丹麥數學家約翰·琴生 (Johan Jensen)命名,給出了積分的凸函式值和凸函式的積分值間的關係。有興趣的同學可以去Google一下,看看這個公式有何神奇之處,我也找了一些資料,然而…沒看懂,只瞭解其大意是說,可能某個的模型的誤差低於所有模型的平均值,但由於我們沒有可以用來“選擇”此模型的標準,所以我們可以確信所有模型的平均值不會比隨機選擇一個模型差。是不是還是有些暈乎?嗯,這個不重要,我們用實踐來檢驗一下是不是有效吧。
接下來,我們就要準備訓練多個機器學習模型。我們也不用把問題複雜化,設計多種網路結構的模型,最簡單的方法是,採用相同的網路結構,甚至使用相同的超引數,但訓練出不同的引數。閒話少說,直接上程式碼:
((trainX, trainY), (testX, testY)) = cifar10.load_data() trainX = trainX.astype("float") / 255.0 testX = testX.astype("float") / 255.0 lb = LabelBinarizer() trainY = lb.fit_transform(trainY) testY = lb.transform(testY) labelNames = ["airplane", "automobile", "bird", "cat", "deer", "dog", "frog", "horse", "ship", "trunk"] aug = ImageDataGenerator(rotation_range=10, width_shift_range=0.1, height_shift_range=0.1, horizontal_flip=True, fill_mode="nearest") for i in np.arange(0, args["num"]): print("[INFO] training model {}/{}".format(i + 1, args["num"])) opt = SGD(lr=0.01, decay=0.01/40, momentum=0.9, nesterov=True) model = MiniVGGNet.build(width=32, height=32, depth=3, classes=10) model.compile(loss="categorical_crossentropy", optimizer=opt, metrics=["accuracy"]) H = model.fit_generator(aug.flow(trainX, trainY, batch_size=64), validation_data=(testX, testY), epochs=40, steps_per_epoch=len(trainX)//64, verbose=1) p = [args["models"], "model_{}.model".format(i)] model.save(os.path.sep.join(p))
程式碼比較容易理解,採用cifar10資料集訓練,10種類別標籤,對輸入資料進行了資料擴充(data augmentation),這個資料擴充是隨機實時進行,加上訓練資料集和驗證資料集也是隨機劃分,所以最後訓練出的網路引數有所不同,訓練完成之後,將模型序列化到檔案,供後面使用。迴圈num遍,就產生了num個模型。
接下來就是依次載入個模型檔案,每個模型分別進行預測,然後取均值:
(testX, testY) = cifar10.load_data()[1] testX = testX.astype("float") / 255.0 labelNames = ["airplane", "automobile", "bird", "cat", "deer", "dog", "frog", "horse", "ship", "trunk"] lb = LabelBinarizer() testY = lb.fit_transform(testY) model_paths = os.path.sep.join(args["models"], "*.model") model_paths = list(glob.glob(model_paths)) models = [] for (i, model_path) in enumerate(model_paths): print("[INFO] loading model {}/{}".format(i + 1, len(model_paths))) models.append(load_model(model_path)) print("[INFO] evaluating ensemble ...") predictions = [] for model in models: predictions.append(model.predict(testX, batch_size=64)) predictions = np.average(predictions, axis=0) print(classification_report(testY.argmax(axis=1), predictions.argmax(axis=1), target_names=labelNames))
上面的程式碼,程式碼量相對於單個模型而言,並沒有增加多少,只是多了一個迴圈,多了一個取平均值,但訓練過程卻多了num倍,我用電腦訓練5個模型,結果花了一晚上還沒有訓練完:(
最後測試的結果如何呢?通過組合多個網路的輸出,成功將準確度從83%提高到84%,即使這些網路使用完全相同的超引數在同一資料集上進行訓練。有資料表明,採用組合模型,通常準確度有1-5%的提升。
看到這兒,你可能會有些失望,費了這麼大的勁,好像也沒啥提升,但是別忘了,在醫療領域、自動駕駛領域,即使費上好大的力氣,準確率能夠提升小數點後面幾位,都是值得的。就像每年度的kaggle競賽,人們依然在孜孜不倦的追求著準確率的提升。
以上例項均有完整的程式碼,點選閱讀原文,跳轉到我在github上建的示例程式碼。
另外,我在閱讀《Deep Learning for Computer Vision with Python》這本書,在微信公眾號後臺回覆“計算機視覺”關鍵字,可以免費下載這本書的電子版。