編譯模型
在創(chuàng)建了模型之后,我們必須要使用compile()
方法來指定損失方程和優(yōu)化器像屋。另外齿尽,你還可以指定在訓練和評估過程中計算出一系列其他的指標(作為一個列表輸入):
model.compile(loss="sparse_categorical_crossentropy",
optimizer="sgd",
metrics=["accuracy"])
這里使用下面的代碼也能達到完全一樣的效果:
model.compile(loss=keras.losses.sparse_categorical_crossentropy,
optimizer=keras.optimizers.SGD(),
metrics=[keras.metrics.sparse_categorical_accuracy])
未來我們還將使用更多其他的損失,優(yōu)化器和指標南用,如果想看一下完整的列表跳芳,可以訪問下面的網(wǎng)址:
我們來簡單解釋一下上面的代碼,首先竹勉,使用sparse_categorical_crossentropy損失函數(shù)是因為我們有離散的標簽(對于每個實例飞盆,只有一個目標類別,在這個模型中就是0到9),并且類別與類別之間是互斥的吓歇。如果我們對每個實例對每個類別都輸出一個概率(就像一位有效編碼one-hot vector一樣孽水,[0., 0., 0., 1., 0., 0.]表示3類別),那我們就用categorical_crossentropy損失就可以了城看。如果需要做二元分類(可能只有一個或者多個二元標簽)女气,那就使用sigmoid函數(shù),而不是softmax激活函數(shù)测柠,同時我們使用binary_crossentropy損失炼鞠。
如果你想將離散的標簽轉(zhuǎn)換成one-hot vector使用keras.utils.to_categorical()函數(shù)。
至于優(yōu)化器轰胁,sgd代表我們使用隨機梯度下降法(Stochastic Gradient Descent)來訓練模型谒主。換句話說,Keras會執(zhí)行之前的文章中所描述的反向傳播算法赃阀。我們未來還會介紹更多高效的優(yōu)化器(它們優(yōu)化了梯度下降的方法霎肯,而并不是自動微分)。使用SGD優(yōu)化器的時候榛斯,調(diào)整學習速度是十分重要的观游。所以一般會使用:
optimizer=keras.optimizers.SGD(lr=XXXXXX)
因為sgd的默認學習速度為0.01。
最后驮俗,既然這是一個分類器懂缕,那么衡量它的準確度accuracy是很有用的。
訓練與評估模型
現(xiàn)在我們的模型已經(jīng)準備好訓練了意述,我們只需調(diào)用fit()方法就可以了:
>>> history = model.fit(X_train, y_train, epochs=30,
... validation_data=(X_valid, y_valid))
...
Train on 55000 samples, validate on 5000 samples
Epoch 1/30
55000/55000 [======] - 3s 49us/sample - loss: 0.7218 - accuracy: 0.7660
- val_loss: 0.4973 - val_accuracy: 0.8366
Epoch 2/30
55000/55000 [======] - 2s 45us/sample - loss: 0.4840 - accuracy: 0.8327
- val_loss: 0.4456 - val_accuracy: 0.8480
[...]
Epoch 30/30
55000/55000 [======] - 3s 53us/sample - loss: 0.2252 - accuracy: 0.9192
- val_loss: 0.2999 - val_accuracy: 0.8926
我們輸入了特性(X_train)和目標類別(y_train)提佣,以及要訓練的epoch次數(shù)(不然的話epoch默認為1,這肯定無法收斂至一個好的結(jié)果)荤崇。所謂epoch就是使用訓練集中的全部數(shù)據(jù)對模型進行一次完整的訓練拌屏,稱為'一代訓練'。不過這個中文叫法實在拗口术荤,不如還是叫一個epoch倚喂。我們還輸入了一個驗證集(這是可選的)。Keras將在每個epoch結(jié)束時度量這些損失和其他指標瓣戚,這對于查看模型的實際執(zhí)行情況非常有用端圈。如果模型在訓練集上的表現(xiàn)比驗證集上的表現(xiàn)好得多,那么模型可能對訓練集進行了過擬合(或者可能存在bug子库,比如說訓練集和驗證集之間的數(shù)據(jù)不匹配)舱权。
這樣就可以了,我們模型已經(jīng)訓練好了仑嗅!如果訓練集和驗證集的數(shù)據(jù)不匹配預期的輸入形狀的話宴倍,就會得到一個異常张症。這可能是最常見的錯誤,我們應(yīng)該熟悉這個異常提示鸵贬,比如:如果嘗試使用一個包含扁平圖像的向量俗他,X_train.reshape(-1,784)。那么就會得到這樣的異常提醒:“ValueError: Error when checking input: expected flatten_input to have 3 dimensions, but got array with shape (60000, 784)”阔逼。
在訓練過程中的每一個epoch兆衅,Keras都會展示已經(jīng)處理的實例數(shù),以及一個處理進度條嗜浮,每個實例訓練的平均時間以及訓練集和驗證集的損失和準確度(或者還有其他設(shè)置好的指標)羡亩。你會看到訓練損失下降了(這是個好現(xiàn)象),在30個epoch之后周伦,驗證集的準確度達到了89.26%夕春,與訓練集的準確度沒有差距太大,所以看起來沒有太大的過擬合發(fā)生专挪。
上面的代碼使用validation_data參數(shù)輸入驗證集及志,我們還可以使用validation_split的方式,設(shè)置一定比例的訓練集用于驗證集寨腔。比如速侈,validation_split=0.1
那么Keras將會使用最后10%的數(shù)據(jù)(在打亂順序之前)用于驗證。
如果訓練集很不平衡迫卢,有些類別的實例過多而有些過少倚搬,那么應(yīng)該在fit()方法中設(shè)置class_weight參數(shù),這個參數(shù)會給實例較少的類別更大的權(quán)重乾蛤,給實例過多的類別較小的權(quán)重每界。Keras將會在計算損失時考慮這些權(quán)重。如果需要設(shè)置每個實例的權(quán)重家卖,可以設(shè)置sample_weight參數(shù)(它會取代class_weight)眨层。如果有些實例是由專家標記的,而其他實例是使用公共數(shù)據(jù)平臺標記的上荡,那么每個實例的權(quán)重可能會很有用:你可能希望給專家標記的數(shù)據(jù)更多的權(quán)重趴樱。你也可以在validation_data的tuple中添加第三項,來給驗證集設(shè)置實例權(quán)重酪捡。
fit()方法會返回一個History對象叁征,這個對象包含訓練參數(shù)(history.params),訓練經(jīng)過的epoch(history.history)以及最重要的逛薇,在每個epoch結(jié)束的時候?qū)τ柧毤万炞C集度量的損失和其他指標的結(jié)果字典(history.history)捺疼。如果你用字典創(chuàng)建了Pandas的DataFrame,并且調(diào)用plot()
方法永罚,那么就會得到圖1.12:
import pandas as pd
import matplotlib.pyplot as plt
pd.DataFrame(history.history).plot(figsize=(8, 5))
plt.grid(True)
plt.gca().set_ylim(0, 1) # set the vertical range to [0-1]
plt.show()
可以看到议薪,在訓練過程中,訓練準確度和驗證準確度都在穩(wěn)步提高媳友,而訓練損失和驗證損失都在減少。此外产捞,驗證曲線與訓練曲線接近醇锚,這意味著沒有太多的過擬合。在這個例子當中坯临,在訓練剛開始時模型在驗證集上的表現(xiàn)似乎比訓練集上要好焊唬。但事實并非如此:實際上,驗證錯誤是在每個epoch結(jié)束時計算的看靠,而訓練錯誤是在每個epoch期間使用平均值計算的赶促。所以訓練曲線應(yīng)該向左移動半個epoch。這樣挟炬,你就可以發(fā)現(xiàn)訓練和驗證曲線在訓練開始時幾乎完全重合鸥滨。
其實在繪制訓練曲線時,也應(yīng)當將其向左移動半個epoch谤祖。
通常情況下婿滓,如果訓練時間足夠長,訓練集的表現(xiàn)最終會超過驗證集的表現(xiàn)粥喜⊥怪鳎可以看出這個例子當中,模型還沒有完全收斂额湘,驗證損失仍然在下降卿吐,所以你應(yīng)該繼續(xù)訓練。這與再次調(diào)用fit()方法一樣簡單锋华,因為Keras會在它停止的地方繼續(xù)進行訓練(最終應(yīng)該能夠達到接近89%的驗證準確度)嗡官。
如果對模型的表現(xiàn)不滿意,那么應(yīng)該重新調(diào)整超參數(shù)供置。首先要檢查的是學習率谨湘。如果沒什么用,嘗試換一個優(yōu)化器(并總是在更改任何超參數(shù)后重新調(diào)整學習率)芥丧。如果表現(xiàn)仍然不是很好紧阔,那么可以嘗試調(diào)整模型的超參數(shù),例如層的數(shù)量续担、每層神經(jīng)元的數(shù)量以及每個隱藏層使用的激活函數(shù)擅耽。你還可以嘗試調(diào)整其他超參數(shù),比如批處理大形镉觥(可以在fit()方法中使用batch_size參數(shù)進行設(shè)置乖仇,默認值為32)憾儒。我們將在未來的文章當中再次講到超參數(shù)調(diào)優(yōu)。一旦您對模型的驗證準確度感到滿意乃沙,就應(yīng)該在測試集上對其進行評估起趾,以在將模型部署到生產(chǎn)環(huán)境之前估計泛化誤差。使用evaluate()方法就可以評估測試集的準確度了(它還支持其他參數(shù)警儒,比如batch_size以及sample_weight等):
>>> model.evaluate(X_test, y_test)
10000/10000 [==========] - 0s 29us/sample - loss: 0.3340 - accuracy: 0.8851
[0.3339798209667206, 0.8851]
通常模型在測試集中的表現(xiàn)要比驗證集中略差一些训裆,這是很正常的。因為超參數(shù)是根據(jù)驗證集的表現(xiàn)來調(diào)整的(不過在這個例子里我們沒有做任何超參的調(diào)整蜀铲,所以準確度低只是運氣不好)边琉。記住,一定要抵制在測試集中調(diào)超參的誘惑记劝,否則你會對泛化誤差的估計過于樂觀变姨。
使用模型進行預測
接下來,我們可以使用模型的predict()
方法對新的實例進行預測了厌丑。這里我們用測試集的前三個實例進行預測:
>>> X_new = X_test[:3]
>>> y_proba = model.predict(X_new)
>>> y_proba.round(2)
array([[0. , 0. , 0. , 0. , 0. , 0.03, 0. , 0.01, 0. , 0.96],
[0. , 0. , 0.98, 0. , 0.02, 0. , 0. , 0. , 0. , 0. ],
[0. , 1. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. ]],
dtype=float32)
對于每個實例定欧,模型對每個類別都生成了一個概率。例如蹄衷,對于第一個圖像忧额,它估計類別9(短靴)的概率是96%,類別5(涼鞋)的概率是3%愧口,類別7(運動鞋)是1%睦番,其他類別的概率忽略不計。
換句話說耍属,模型“相信”第一個圖片是鞋類托嚣,最有可能是短靴,但也不完全確定厚骗,可能是涼鞋或運動鞋示启。不過如果你只關(guān)心估計概率最高的類別(即使這個概率并不高),那么可以使用predict_classes()
方法:
>>> y_pred = model.predict_classes(X_new)
>>> y_pred
array([9, 2, 1])
>>> np.array(class_names)[y_pred]
array(['Ankle boot', 'Pullover', 'Trouser'], dtype='<U11')
那么模型實際上對這三個圖像都預測正確了(見圖1.13)领舰。
好的夫嗓,那么現(xiàn)在我們已經(jīng)學習了如何用Sequential API來搭建、訓練冲秽、評估和使用分類MLP舍咖,那么接下來的文章我們將講述如何搭建回歸MLP以及搭建更加復雜的模型。
敬請期待啦锉桑!