前一篇:《人工智慧同樣也會讀死書----“過擬合”》
序言:你看,人工智慧領域的專家都在做什麼?他們其實只是在不斷試錯,因為並沒有一種“萬能藥”——一種萬能的演算法可以一次性設計出任何人工智慧大模型來實現客戶的需求。所有的模型在設計和訓練過程中都是——驗證結構——修改架構——再驗證新結構——再修改……最終達到設計的目的。
說難聽一點,在當前的技術背景下,從事人工智慧模型的設計就是動手實踐的經驗主義。這個行業需要的,就是您擁有足夠的背景知識,能夠快速理解並做出反應,以應對這個行業中各種概念的融合。這些概念大部分來自數學、統計學、人文學、社會學、語言學等多個寬泛的領域,也正是這個行業招募碩士、博士、博士後學歷背景人才的原因,因為這個行業需要有足夠的理論背景來支援。好了,讓我們回到重點,本節知識實際上是人為地透過對訓練資料集和驗證資料集中詞彙的相互關係以及維度的大小,迭代模型的架構,最終達到收斂,完成人工智慧模型的設計。能理解今天的知識並獨立付諸實踐的朋友,您就是人工智慧領域的專家。大家一起來測試測試吧。
探索詞彙大小
Sarcasm 資料集處理的是單詞,所以如果你去研究資料集中的單詞,特別是它們的頻率,你可能會找到一些可以幫助解決過擬合問題的線索。tokenizer 提供了一種方法,可以透過它的 word_counts 屬性做到這一點。如果你列印出來,你會看到類似這樣的結果,這是一個包含單詞和詞頻元組的 OrderedDict:
wc = tokenizer.word_counts
print(wc)
OrderedDict([('former', 75), ('versace', 1), ('store', 35), ('clerk', 8),
('sues', 12), ('secret', 68), ('black', 203), ('code', 16),...])
這些單詞的順序是由它們在資料集中出現的順序決定的。如果你檢視訓練集中的第一條標題,它是一條諷刺性的新聞,關於一位 Versace 前店員的故事。停用詞(stopwords)已經被移除了,否則你會看到很多像 “a” 和 “the” 這樣頻率很高的詞。
因為這是一個 OrderedDict,你可以按詞頻降序對它進行排序,程式碼如下:
from collections import OrderedDict
newlist = OrderedDict(sorted(wc.items(), key=lambda t: t[1], reverse=True))
print(newlist)
OrderedDict([('new', 1143), ('trump', 966), ('man', 940), ('not', 555),
('just', 430), ('will', 427), ('one', 406), ('year', 386),...])
如果你想把這些資料畫成圖表,你可以遍歷列表中的每一項,把 x 軸的值設為單詞在列表中的位置(第 1 項對應 x=1,第 2 項對應 x=2,以此類推),y 軸的值設為 newlist[item] 的詞頻。然後你可以用 Matplotlib 繪製圖表,程式碼如下:
xs = []
ys = []
curr_x = 1
for item in newlist:
xs.append(curr_x)
curr_x += 1
ys.append(newlist[item])
plt.plot(xs, ys)
plt.show()
繪製出的結果如圖 6-6 所示。
圖 6-6:探索單詞的頻率
這條“冰球棒”曲線告訴我們,只有很少的單詞被大量使用,而大多數單詞只被使用了很少的次數。但實際上,每個單詞在嵌入中都被賦予了相同的權重,因為每個單詞在嵌入表中都有一個“條目”。由於訓練集的規模相對驗證集來說較大,我們會遇到一種情況:訓練集中有許多單詞,而這些單詞並未出現在驗證集中。
你可以透過修改圖表的座標軸來放大資料,在呼叫 plt.show 之前調整座標軸。例如,如果你想檢視 x 軸上單詞 300 到 10,000 的範圍,以及 y 軸上從 0 到 100 的頻率範圍,可以使用以下程式碼:
plt.plot(xs, ys)
plt.axis([300, 10000, 0, 100])
plt.show()
結果如圖 6-7 所示。
圖 6-7:單詞 300–10,000 的頻率
雖然語料庫中包含了超過 20,000 個單詞,但程式碼僅設定為訓練 10,000 個單詞。然而,如果我們檢視位置 2,000 到 10,000 的單詞(這部分佔我們詞彙表的 80% 以上),可以發現它們每個單詞在整個語料庫中出現的次數都少於 20 次。
這可能解釋了過擬合的原因。現在想象一下,如果你將詞彙表大小改為兩千並重新訓練,會發生什麼?圖 6-8 顯示了準確率指標。此時訓練集的準確率約為 82%,驗證集的準確率約為 76%。兩者之間的差距變小了,而且沒有分離開,這表明我們已經解決了大部分的過擬合問題,這是一個好跡象。
圖 6-8:使用兩千詞彙表時的準確率
這一點在圖 6-9 的損失曲線中得到了進一步的驗證。驗證集的損失確實在上升,但速度比之前慢得多。所以,將詞彙表大小減少,防止訓練集過擬合那些可能只在訓練集中出現的低頻詞,似乎起到了作用。
圖 6-9:使用兩千詞彙表時的損失
值得嘗試不同的詞彙表大小,但要記住,詞彙表也可能太小,導致模型過擬合到過少的詞彙上。你需要找到一個平衡點。在這個案例中,我選擇只保留出現 20 次或更多的單詞,這只是一個完全隨機的選擇。
探索嵌入維度
在這個例子中,嵌入維度被隨意地設定為 16。這意味著單詞會被編碼成 16 維空間中的向量,其方向表示它們的整體意義。但 16 是個合適的數字嗎?在詞彙表只有兩千個單詞的情況下,這個維度可能有點高了,導致方向上的高稀疏性。
嵌入大小的最佳實踐是讓它等於詞彙表大小的四次方根。兩千的四次方根是 6.687,因此我們可以嘗試將嵌入維度改為 7,並重新訓練模型 100 個訓練週期。
你可以在圖 6-9 中看到準確率的結果。訓練集的準確率穩定在大約 83%,而驗證集的準確率在大約 77%。儘管有些波動,但曲線總體上是平的,說明模型已經收斂了。雖然這和圖 6-6 的結果沒有太大區別,但減少嵌入維度讓模型訓練速度快了大約 30%
圖 6-10:七維嵌入的訓練與驗證準確率對比
圖 6-11 展示了訓練和驗證的損失曲線。雖然在大約第 20 個訓練週期時損失看起來有所上升,但很快就趨於平穩了。這也是一個好訊號!
圖 6-11:七維嵌入的訓練與驗證損失對比
現在我們已經降低了嵌入維度,可以對模型架構進行進一步的調整了。
探索模型架構
在經過前面部分對模型進行最佳化之後,模型架構現在是這樣的:
model = tf.keras.Sequential([
tf.keras.layers.Embedding(2000, 7),
tf.keras.layers.GlobalAveragePooling1D(),
tf.keras.layers.Dense(24, activation='relu'),
tf.keras.layers.Dense(1, activation='sigmoid')
])
model.compile(loss='binary_crossentropy',
optimizer='adam', metrics=['accuracy'])
第一眼看過去,模型的維度設計讓人感到有點問題——GlobalAveragePooling1D 層現在只輸出 7 個維度的資料,但這些資料被直接傳入一個有 24 個神經元的全連線層,這顯得有點過頭了。那麼,如果把這個全連線層的神經元數量減少到 8 個,並訓練 100 個週期,會發生什麼呢?
你可以在圖 6-12 中看到訓練和驗證的準確率對比。和使用 24 個神經元的圖 6-7 比較,整體結果差不多,但波動明顯減少了(線條看起來不那麼“鋸齒狀”了)。而且,訓練速度也有所提升。
圖 6-12:減少全連線層神經元后的準確率結果
同樣,圖 6-13 中的損失曲線顯示了類似的結果,不過“鋸齒感”也有所減弱。
圖 6-13:減少全連線層神經元后的損失結果
總結:本篇所講述的知識,對於人工智慧領域的工程師或專家來說,會顯得很基礎;但對於普通大眾而言,如果能夠理解並親手實踐,完成文中知識所描述的目標,那您已經具備了人工智慧領域專家的能力。再次強調,人工智慧並不神秘,它本質上是利用廣泛的知識教會機器模擬人類的思維方式和行為模式,用機器來為人類造福,同時利用機器所具備的人類不具備的優勢,提升人類的生產力與文明。接下來,我們將為大家介紹一種常用的解決模型“讀死書”問題的方法——“隨機失活”(Dropout)。