5.4 常見問題及對策
學(xué)完多層感知機(jī),知道了什么是前向傳播、反向傳播,其實(shí)你已經(jīng)窺見了深度學(xué)習(xí)的精髓。為什么這么說呢?因?yàn)檎麄€深度學(xué)習(xí)后面所涉及的內(nèi)容其實(shí)全是在這個基礎(chǔ)上發(fā)展起來的。說白了,都沒有離開這個小圈圈。那咱們?yōu)樯哆€要學(xué)后面那么多章節(jié)內(nèi)容呢?原因啊,就在于實(shí)際情況千奇百怪十分復(fù)雜,遠(yuǎn)不是簡單的多層感知機(jī)就能解決好的。因此,想學(xué)好更復(fù)雜的深層神經(jīng)網(wǎng)絡(luò)知識,先要明確要解決的問題是什么。各種看似高級的技術(shù),其實(shí)只不過在不停解決各種問題。本節(jié)咱們就來看數(shù)據(jù)和模型匹配方面的常見問題。先來看看模型復(fù)雜度。
5.4.1?模型復(fù)雜度
有了多層感知機(jī)這樣的神經(jīng)網(wǎng)絡(luò),你有沒有好奇到底該怎么決定網(wǎng)絡(luò)結(jié)構(gòu)啊?該用多少層,每層用多少個神經(jīng)元,是越多越好嗎?選擇的原則是什么?這些問題就涉及到了模型復(fù)雜度,也就是神經(jīng)網(wǎng)絡(luò)的復(fù)雜程度。模型復(fù)雜度指的是模型的表示能力,即模型可以擬合的數(shù)據(jù)的復(fù)雜度程度。模型復(fù)雜度越高,模型就能表示的數(shù)據(jù)就越復(fù)雜,但同時也會增加過擬合的風(fēng)險。
在深度學(xué)習(xí)中,模型復(fù)雜度主要取決于網(wǎng)絡(luò)的層數(shù)和神經(jīng)元數(shù)量。隨著網(wǎng)絡(luò)層數(shù)的增加或神經(jīng)元數(shù)量的增加,模型的復(fù)雜度也會增加。當(dāng)模型復(fù)雜度過高時,模型容易過擬合訓(xùn)練數(shù)據(jù),導(dǎo)致在測試數(shù)據(jù)上的表現(xiàn)不佳。因此,在訓(xùn)練深度學(xué)習(xí)模型時,需要適當(dāng)控制模型的復(fù)雜度,以避免過擬合問題。常用的方法包括使用正則化技術(shù)(如L1 和 L2 正則化)、使用 K 折交叉驗(yàn)證選擇最佳模型、使用數(shù)據(jù)增強(qiáng)技術(shù)等。此外,還可以通過減小網(wǎng)絡(luò)層數(shù)或神經(jīng)元數(shù)量、使用 dropout 等方法來限制模型的復(fù)雜度。
這里面一下子提到了不少新詞:過擬合、正則化、K折交叉驗(yàn)證、數(shù)據(jù)增強(qiáng)、dropout等,看不懂別捉急,咱們一個個講清楚。
5.4.2?過擬合問題
過擬合是指在訓(xùn)練深度學(xué)習(xí)模型時,模型在訓(xùn)練數(shù)據(jù)上表現(xiàn)良好,但在測試數(shù)據(jù)上表現(xiàn)不佳的情況。這意味著模型對訓(xùn)練數(shù)據(jù)進(jìn)行了過度擬合,從而無法適用于真實(shí)世界中的數(shù)據(jù)。
過擬合通常是由模型復(fù)雜度過高導(dǎo)致的。當(dāng)模型復(fù)雜度增加時,它可以更好地?cái)M合訓(xùn)練數(shù)據(jù)中的噪聲和細(xì)節(jié)。但是,這同時也會使模型對真實(shí)世界中的數(shù)據(jù)過度擬合,因此表現(xiàn)不佳。
為了避免過擬合,可以使用正則化技術(shù)來限制模型的復(fù)雜度。常用的正則化技術(shù)包括 L1 和 L2 正則化。這些技術(shù)通過在損失函數(shù)中添加懲罰項(xiàng)來減少模型的復(fù)雜度。此外,還可以通過使用更少的訓(xùn)練數(shù)據(jù)、減小網(wǎng)絡(luò)層數(shù)或神經(jīng)元數(shù)量、使用 dropout 等方法來防止過擬合。
在實(shí)際應(yīng)用中,過擬合是一個常見問題,因此需要通過合適的方法來避免。過擬合不僅會導(dǎo)致模型在測試數(shù)據(jù)上的表現(xiàn)不佳,還會使模型對真實(shí)世界中的數(shù)據(jù)不具有泛化能力,因此需要謹(jǐn)慎對待。
5.4.3?欠擬合問題
欠擬合是指在訓(xùn)練深度學(xué)習(xí)模型時,模型在訓(xùn)練數(shù)據(jù)上的表現(xiàn)不佳,即模型沒有很好地?cái)M合訓(xùn)練數(shù)據(jù)。這意味著模型的復(fù)雜度過低,無法很好地捕捉數(shù)據(jù)中的特征。
欠擬合常常是由模型復(fù)雜度過低導(dǎo)致的。當(dāng)模型復(fù)雜度過低時,它無法很好地?cái)M合訓(xùn)練數(shù)據(jù)中的細(xì)節(jié)和特征。因此,模型在訓(xùn)練數(shù)據(jù)上的表現(xiàn)不佳。
為了避免欠擬合,可以通過增加模型的復(fù)雜度來提高模型的表示能力。常用的方法包括增加網(wǎng)絡(luò)層數(shù)或神經(jīng)元數(shù)量、使用更復(fù)雜的模型結(jié)構(gòu)(如卷積神經(jīng)網(wǎng)絡(luò)或循環(huán)神經(jīng)網(wǎng)絡(luò))等。此外,還可以使用數(shù)據(jù)增強(qiáng)技術(shù)來增加訓(xùn)練數(shù)據(jù)的數(shù)量和多樣性,從而提高模型的泛化能力。正確地處理欠擬合問題可以提高模型的性能和泛化能力。
5.4.4?相互關(guān)系
模型復(fù)雜度、過擬合、欠擬合三者之間存在一定依存關(guān)聯(lián),下面這張圖能清晰表達(dá)此消彼長的關(guān)系。

中間是最佳分界線,橫軸是模型復(fù)雜度,越往右我們可以看出越容易過擬合,越往左越容易欠擬合。模型越復(fù)雜,訓(xùn)練誤差越小,這很容易理解。但是泛化誤差就不一定了,無論過擬合,還是欠擬合都不好,兩種情況下泛化誤差,也就是測試誤差都會變大。只有在中間線這個地方泛化誤差最小,因此這就是我們模型選擇的目標(biāo)了。
這里涉及到的訓(xùn)練誤差和泛化誤差兩個概念,稍微多說兩句。
在訓(xùn)練深度學(xué)習(xí)模型時,泛化誤差指的是模型在真實(shí)世界中的數(shù)據(jù)上的誤差。泛化誤差是我們最終關(guān)心的誤差,因?yàn)樗从沉四P蛯φ鎸?shí)世界數(shù)據(jù)的預(yù)測能力。訓(xùn)練誤差指的是模型在訓(xùn)練數(shù)據(jù)上的誤差。訓(xùn)練誤差可以作為訓(xùn)練過程中的一個指標(biāo),但最終我們關(guān)心的是模型的泛化能力,即在真實(shí)世界中的數(shù)據(jù)上的表現(xiàn)。在訓(xùn)練時,通常會使用訓(xùn)練數(shù)據(jù)和測試數(shù)據(jù)來評估模型的性能。訓(xùn)練數(shù)據(jù)用于訓(xùn)練模型,測試數(shù)據(jù)用于評估模型的泛化能力。如果模型在訓(xùn)練數(shù)據(jù)上表現(xiàn)良好,但在測試數(shù)據(jù)上表現(xiàn)不佳,則可能存在過擬合問題。
舉個例子,假設(shè)我們要建立一個機(jī)器學(xué)習(xí)模型來預(yù)測房屋價格。我們收集了一些歷史房屋交易數(shù)據(jù),并用這些數(shù)據(jù)來訓(xùn)練模型。這些數(shù)據(jù)就是我們的訓(xùn)練數(shù)據(jù)。在訓(xùn)練過程中,我們希望模型能夠準(zhǔn)確地?cái)M合訓(xùn)練數(shù)據(jù),也就是希望訓(xùn)練誤差盡可能小。但是,我們也希望模型能夠很好地適用于新數(shù)據(jù),也就是說,希望模型的泛化誤差盡可能小。
在實(shí)際應(yīng)用中,我們通常希望泛化誤差盡可能小,因?yàn)檫@意味著模型能夠很好地適用于新數(shù)據(jù)。但是,訓(xùn)練誤差和泛化誤差之間通常存在一定的關(guān)系,即訓(xùn)練誤差越小,泛化誤差就越小。但是,如果訓(xùn)練誤差過小,模型就可能出現(xiàn)過擬合的情況,即模型過于復(fù)雜,只能很好地?cái)M合訓(xùn)練數(shù)據(jù),但是對新數(shù)據(jù)的預(yù)測能力較差。因此,在訓(xùn)練機(jī)器學(xué)習(xí)模型時,我們需要盡量減小泛化誤差,同時避免過擬合的情況。
5.4.5?應(yīng)對策略
面對一不小心就過擬合、欠擬合這些問題,怎么辦呢?可以從數(shù)據(jù)復(fù)雜度、模型復(fù)雜度、訓(xùn)練策略三方面下手。與之對應(yīng)的就是數(shù)據(jù)集大小選擇、數(shù)據(jù)增強(qiáng)、增加驗(yàn)證集;模型選擇、K折交叉驗(yàn)證;正則化、dropout方法了。咱們分別來看看。
數(shù)據(jù)集大小選擇
在訓(xùn)練深度學(xué)習(xí)模型時,數(shù)據(jù)集的大小可能會對過擬合問題產(chǎn)生影響。通常來說,如果數(shù)據(jù)集較小,模型很容易就會出現(xiàn)過擬合的情況。因?yàn)樵谳^小的數(shù)據(jù)集上,模型很容易就能夠很好地?cái)M合訓(xùn)練數(shù)據(jù),但是在新數(shù)據(jù)上的表現(xiàn)卻很差。這是因?yàn)槟P驮谟?xùn)練過程中學(xué)習(xí)到的特征可能只針對訓(xùn)練數(shù)據(jù)有效,對新數(shù)據(jù)并沒有太大的意義。
相反,如果數(shù)據(jù)集較大,模型很難出現(xiàn)過擬合的情況。這是因?yàn)樵谳^大的數(shù)據(jù)集上,模型有更多的數(shù)據(jù)可以學(xué)習(xí),因此模型很難將訓(xùn)練數(shù)據(jù)完全擬合。這樣,模型就可以學(xué)習(xí)到更加泛化的特征,對新數(shù)據(jù)的表現(xiàn)就會更好。然而,數(shù)據(jù)集過大也可能導(dǎo)致模型的訓(xùn)練效率降低。因此,我們在選擇數(shù)據(jù)集大小時,需要權(quán)衡這些因素,并選擇合適的數(shù)據(jù)集大小。
數(shù)據(jù)增強(qiáng)是什么
在訓(xùn)練深度學(xué)習(xí)模型時,數(shù)據(jù)增強(qiáng)是指通過對訓(xùn)練數(shù)據(jù)進(jìn)行變換,以增加訓(xùn)練數(shù)據(jù)的數(shù)量和多樣性的一種技術(shù)。數(shù)據(jù)增強(qiáng)通常是通過對訓(xùn)練數(shù)據(jù)進(jìn)行旋轉(zhuǎn)、翻轉(zhuǎn)、剪切、改變亮度等操作來實(shí)現(xiàn)的。
使用數(shù)據(jù)增強(qiáng)可以有效解決過擬合問題。同時,使用數(shù)據(jù)增強(qiáng)還可以提高模型在新數(shù)據(jù)上的泛化能力。因?yàn)橥ㄟ^數(shù)據(jù)增強(qiáng)生成的新數(shù)據(jù)可以更好地代表真實(shí)數(shù)據(jù)的分布,因此模型在新數(shù)據(jù)上的表現(xiàn)也會更好。
為什么要用驗(yàn)證集
在訓(xùn)練深度學(xué)習(xí)模型時,通常會使用訓(xùn)練集、驗(yàn)證集和測試集進(jìn)行模型訓(xùn)練和評估。其中,訓(xùn)練集用于訓(xùn)練模型,測試集用于最終評估模型的性能,而驗(yàn)證集則是輔助模型選擇和調(diào)參的一種工具。
使用驗(yàn)證集的好處在于,我們可以在訓(xùn)練過程中就對模型進(jìn)行評估,而不需要等到訓(xùn)練結(jié)束才進(jìn)行評估。這樣,我們就可以及時發(fā)現(xiàn)模型的問題,并可以及時調(diào)整模型的超參數(shù)或模型結(jié)構(gòu)。同時,使用驗(yàn)證集進(jìn)行模型評估可以避免測試集的偏差,從而使得模型的評估更加準(zhǔn)確。
假設(shè)我們要訓(xùn)練一個深度學(xué)習(xí)模型來進(jìn)行圖像分類。我們收集了一些圖像數(shù)據(jù),共有1000張圖片,將這些圖片分為訓(xùn)練集、驗(yàn)證集和測試集,每個集合各占圖片總數(shù)的60%、20%和20%。
首先,我們使用訓(xùn)練集來訓(xùn)練模型。我們設(shè)定了兩種不同的模型,分別是模型A和模型B。在訓(xùn)練過程中,我們使用驗(yàn)證集來評估模型的性能。我們發(fā)現(xiàn),模型A在驗(yàn)證集上的性能略優(yōu)于模型B。因此,我們決定選擇模型A進(jìn)行進(jìn)一步訓(xùn)練。
在進(jìn)一步訓(xùn)練模型A的過程中,我們發(fā)現(xiàn)模型A的訓(xùn)練誤差逐漸降低,但是驗(yàn)證集上的誤差卻逐漸升高。這種情況通常是由于模型A出現(xiàn)了過擬合的情況。因此,我們決定停止模型A的訓(xùn)練,并使用最終訓(xùn)練好的模型A來在測試集上進(jìn)行評估。
最終,我們使用測試集評估模型A的性能,發(fā)現(xiàn)模型A的性能較優(yōu)。因此,我們選擇使用模型A來進(jìn)行實(shí)際的圖像分類任務(wù)。
模型選擇
在訓(xùn)練深度學(xué)習(xí)模型時,模型選擇是很重要的一環(huán)。模型選擇的好壞直接影響到模型的性能,因此選擇合適的模型是很重要的。
常用的模型選擇方法有以下幾種:
網(wǎng)格搜索:通過對模型的超參數(shù)進(jìn)行網(wǎng)格搜索,找到最優(yōu)的超參數(shù)組合。
隨機(jī)搜索:隨機(jī)選擇模型的超參數(shù)進(jìn)行搜索,找到最優(yōu)超參數(shù)。
交叉驗(yàn)證:將訓(xùn)練數(shù)據(jù)分為訓(xùn)練集和驗(yàn)證集,利用驗(yàn)證集來選擇最優(yōu)的模型。
集成方法:利用多個模型的輸出結(jié)果進(jìn)行集成,得到最終的模型。
在選擇模型時,還需要考慮模型的泛化能力、訓(xùn)練時間、模型復(fù)雜度、計(jì)算資源、預(yù)測效率等因素。因此,選擇合適的模型是很重要的。
K折交叉驗(yàn)證
K 折交叉驗(yàn)證(K-fold cross-validation)是一種在機(jī)器學(xué)習(xí)中常用的評估模型性能的方法。它的基本思想是將訓(xùn)練數(shù)據(jù)劃分為 K 份,然后進(jìn)行 K 次訓(xùn)練和驗(yàn)證。K 折交叉驗(yàn)證在深度學(xué)習(xí)中也是常用的方法。它可以有效評估模型的性能,并且可以在訓(xùn)練過程中發(fā)現(xiàn)模型的過擬合情況。
使用 K 折交叉驗(yàn)證的步驟如下:
將訓(xùn)練數(shù)據(jù)劃分為 K 份。
對于每一份數(shù)據(jù),將其作為驗(yàn)證集,剩余的 K-1 份數(shù)據(jù)作為訓(xùn)練集,進(jìn)行訓(xùn)練和驗(yàn)證。
計(jì)算 K 次驗(yàn)證的平均值。
K 折交叉驗(yàn)證的優(yōu)點(diǎn)是可以充分利用訓(xùn)練數(shù)據(jù),提高模型的泛化能力。但是,它的計(jì)算時間較長,因此在實(shí)際應(yīng)用中需要權(quán)衡時間和精度的折中。
梗直哥提示:我們看到模型選擇、驗(yàn)證集使用和K折交叉驗(yàn)證幾個策略息息相關(guān),如果你對這方面的知識不是很熟悉,歡迎補(bǔ)足,來聽聽過來人怎么說。“解一卷而眾篇明”之機(jī)器學(xué)習(xí)核心概念精講
正則化和dropout稍微復(fù)雜些,咱們下面兩節(jié)單獨(dú)講。這里先來看個過擬合的實(shí)際例子,感受一下。
5.4.6?代碼示例
為了讓大家有更加感性的認(rèn)知,我們用一個 PyTorch 的例子,演示不同模型情況下欠擬合、正常和過擬合三種情況,并可視化訓(xùn)練和測試誤差。
首先,導(dǎo)入所需庫,并用 PyTorch 的 torch.utils.data.DataLoader 類來生成一些虛擬數(shù)據(jù)。這些數(shù)據(jù)將滿足一個線性方程?y = x^2 + 1,但是我們將會增加一些噪聲來使它不完全符合這個方程。
import?torch
import?torch.nn?as?nn
import?torch.optim?as?optim
from?torch.utils.data?import?DataLoader, TensorDataset
from?sklearn.model_selection?import?train_test_split
import?matplotlib.pyplot?as?plt
import?numpy?as?np
np.random.seed(32)
#?生成滿足?y = x^2 + 1?的數(shù)據(jù)
num_samples?=?100
X?=?np.random.uniform(-5,?5, (num_samples,?1))
Y?=?X?**?2?+?1?+?5?*?np.random.normal(0,?1, (num_samples,?1))
#?將?NumPy?變量轉(zhuǎn)化為浮點(diǎn)型?PyTorch?變量
X?=?torch.from_numpy(X).float()
Y?=?torch.from_numpy(Y).float()
#?繪制數(shù)據(jù)點(diǎn)
plt.scatter(X, Y)
plt.show()

#?將數(shù)據(jù)拆分為訓(xùn)練集和測試集
train_X, test_X, train_Y, test_Y?=?train_test_split(X, Y, test_size=0.3, random_state=0)
#?將數(shù)據(jù)封裝成數(shù)據(jù)加載器
train_dataloader?=?DataLoader(TensorDataset(train_X, train_Y), batch_size=32, shuffle=True)
test_dataloader?=?DataLoader(TensorDataset(test_X, test_Y), batch_size=32, shuffle=False)
接下來,定義三種不同的模型,分別演示欠擬合、正常和過擬合三種情況。
欠擬合可以使用一個線性回歸模型,但是輸入維度為?1,輸出維度也為 1。因?yàn)榫€性回歸模型沒有隱藏層,所以它可能無法很好地?cái)M合復(fù)雜的數(shù)據(jù)。
正常情況使用一個包含一個隱藏層的多層感知機(jī)(MLP)。這個模型的輸入維度仍為 1,輸出維度也為 1。
過擬合用一個比上述模型更復(fù)雜的?MLP,它具有更多的隱藏層和更多的隱藏單元。
#?定義線性回歸模型(欠擬合)
class?LinearRegression(nn.Module):
????def?__init__(self):
????????super().__init__()
????????self.linear?=?nn.Linear(1,?1)
????def?forward(self, x):
????????return?self.linear(x)
#?定義多層感知機(jī)(正常)
class?MLP(nn.Module):
????def?__init__(self):
????????super().__init__()
????????self.hidden?=?nn.Linear(1,?8)
????????self.output?=?nn.Linear(8,?1)
????def?forward(self, x):
????????x?=?torch.relu(self.hidden(x))
????????return?self.output(x)
#?定義更復(fù)雜的多層感知機(jī)(過擬合)
class?MLPOverfitting(nn.Module):
????def?__init__(self):
????????super().__init__()
????????self.hidden1?=?nn.Linear(1,?256)
????????self.hidden2?=?nn.Linear(256,?256)
????????self.output?=?nn.Linear(256,?1)
????def?forward(self, x):
????????x?=?torch.relu(self.hidden1(x))
????????x?=?torch.relu(self.hidden2(x))
????????return?self.output(x)
接下來,我們可以使用這三種模型來訓(xùn)練模型并計(jì)算訓(xùn)練和測試誤差。為了計(jì)算誤差,我們可以使用均方誤差(MSE)作為損失函數(shù)。
def?plot_errors(models, num_epochs, train_dataloader, test_dataloader):
????#?定義損失函數(shù)
????loss_fn?=?nn.MSELoss()
????#?定義訓(xùn)練和測試誤差數(shù)組
????train_losses?=?[]
????test_losses?=?[]
????#?迭代訓(xùn)練
????for?model?in?models:
????????#?初始化模型和優(yōu)化器
????????optimizer?=?torch.optim.SGD(model.parameters(), lr=0.01)
????????train_losses_per_model?=?[]
????????test_losses_per_model?=?[]
????????for?epoch?in?range(num_epochs):
????????????#?在訓(xùn)練數(shù)據(jù)上迭代
????????????model.train()
????????????train_loss?=?0
????????????for?inputs, targets?in?train_dataloader:
????????????????optimizer.zero_grad()
????????????????outputs?=?model(inputs)
????????????????loss?=?loss_fn(outputs, targets)
????????????????loss.backward()
????????????????optimizer.step()
????????????????train_loss?+=?loss.item()
????????????train_loss?/=?len(train_dataloader)
????????????train_losses_per_model.append(train_loss)
????????????#?在測試數(shù)據(jù)上評估
????????????model.eval()
????????????test_loss?=?0
????????????with?torch.no_grad():
????????????????for?inputs, targets?in?test_dataloader:
????????????????????outputs?=?model(inputs)
????????????????????loss?=?loss_fn(outputs, targets)
????????????????????test_loss?+=?loss.item()
????????????????test_loss?/=?len(test_dataloader)
????????????????test_losses_per_model.append(test_loss)
????????train_losses.append(train_losses_per_model)
????????test_losses.append(test_losses_per_model)
????return?train_losses, test_losses
最后,我們可以使用?Matplotlib 繪制訓(xùn)練和測試誤差曲線,以便我們可以清楚地看到三種模型的變化趨勢。
#?獲取訓(xùn)練和測試誤差曲線
num_epochs?=?300
models?=?[LinearRegression(), MLP(), MLPOverfitting()]
train_losses, test_losses?=?plot_errors(models, num_epochs, train_dataloader, test_dataloader)
#?繪制訓(xùn)練和測試誤差曲線
for?i, model?in?enumerate(models):
????plt.figure()
????plt.plot(range(num_epochs), train_losses[i], label=f"Train?{model.__class__.__name__}")
????plt.plot(range(num_epochs), test_losses[i], label=f"Test?{model.__class__.__name__}")
????plt.legend()
????plt.ylim((0,?200))
????plt.show()



從上圖中可以看到,欠擬合的訓(xùn)練和測試誤差曲線都呈現(xiàn)出較高的損失值,這表明模型在訓(xùn)練數(shù)據(jù)和測試數(shù)據(jù)上的表現(xiàn)都不佳,也就是說模型無法很好地?cái)M合數(shù)據(jù)。在正常的情況下,訓(xùn)練曲線和驗(yàn)證曲線通常會呈現(xiàn)出較低的損失值,并且損失值隨著訓(xùn)練的進(jìn)行而逐漸下降。而過擬合則是在測試集上的表現(xiàn)較差,出現(xiàn)不穩(wěn)定的情況甚至隨著訓(xùn)練次數(shù)的增加反而逐漸增大。
廣而告之:如果你對實(shí)戰(zhàn)感興趣,比如上述代碼的深度分析,如何調(diào)參等更加進(jìn)階的話題,歡迎入群討論(微信:gengzhige99),請告知我“我想選修梗直哥課程《深度學(xué)習(xí)必修課:Python實(shí)戰(zhàn)》”