你能把美元價(jià)格和 “優(yōu)雅,細(xì)膩的單寧”,“成熟的醋栗香氣” 或 “醇厚的烤面包香氣” 聯(lián)系在一起嗎? 事實(shí)證明機(jī)器學(xué)習(xí)模型可以。 在這篇文章中,我將解釋如何使用 Keras(tf.keras)建立一個(gè) Wide & Deep 網(wǎng)絡(luò)來(lái)預(yù)測(cè)其描述中的葡萄酒價(jià)格。對(duì)于那些剛接觸 Keras 的人來(lái)說(shuō),它是用于構(gòu)建 ML 模型的更高級(jí)別的 TensorFlow API。 如果您想直接跳到代碼,可以在 GitHub 上找到它。 你也可以直接在瀏覽器中使用 Colab 進(jìn)行零設(shè)置運(yùn)行模型。
向 Francois,Josh 和 Yufeng 致敬,感謝他們對(duì)這篇文章的幫助和意見(jiàn)。
模型:Wide & Deep 與 Keras 結(jié)合
我最近一直在使用 Sequential Model API 構(gòu)建了許多 Keras 模型(這里有一些例子),但我想嘗試使用 Functional API。Sequential API 是開(kāi)始使用 Keras 的最佳方式,它可以讓你輕松地將模型定義為圖層堆棧。Functional API 則更加靈活,它最適合具有多個(gè)輸入或組合模型的模型。Functional API 的一個(gè)很好的實(shí)例是在 Keras 中執(zhí)行 wide and deep 網(wǎng)絡(luò)。關(guān)于學(xué)習(xí) wide and deep 有很多很好的資源,因此我不會(huì)著重于細(xì)節(jié),但如果你有興趣了解更多,我推薦這篇文章。
在通過(guò) wide & deep 網(wǎng)絡(luò)解決ML問(wèn)題之前,你最好確保它非常適合您嘗試預(yù)測(cè)的內(nèi)容。如果你有一個(gè)預(yù)測(cè)任務(wù),輸入和輸出之間存在相對(duì)直接的關(guān)系,那么 wide 模型可能就足夠了。 Wide 模型是具有稀疏特征向量的模型,或具有大多數(shù)零值的向量。另一方面,已知多層深度網(wǎng)絡(luò)在諸如圖像或語(yǔ)音識(shí)別之類的任務(wù)上表現(xiàn)良好,其中輸入和輸出之間可能存在意外關(guān)系。如果你有一個(gè)可以從這兩個(gè)模型中受益的預(yù)測(cè)任務(wù)(推薦模型或帶有文本輸入的模型都是很好的例子),那么 wide & deep 可能是一個(gè)很好的選擇。在這種情況下,我分別嘗試了 Wide 模型和 Deep 模型,然后將它們組合在一起,結(jié)果發(fā)現(xiàn) Wide & Deep 在一起的情況下精確度表現(xiàn)最好。讓我們深入了解一下吧。
數(shù)據(jù)集:預(yù)測(cè)葡萄酒的價(jià)格
我們將使用 Kaggle 葡萄酒數(shù)據(jù)集 this wine dataset 來(lái)查看:
我們可以從描述和品種中預(yù)測(cè)出一瓶葡萄酒的價(jià)格嗎?
這個(gè)問(wèn)題非常適合 Wide & Deep 的學(xué)習(xí),因?yàn)樗婕暗轿谋据斎耄⑶移咸丫频拿枋雠c其價(jià)格之間并沒(méi)有明顯的相關(guān)性。我們無(wú)法斬釘截鐵地說(shuō),描述中帶有 “果味” 一詞的葡萄酒更貴,或者有 “柔和的單寧” 描述的葡萄酒更便宜。此外,當(dāng)我們將文本提供給模型時(shí),有很多種方式來(lái)表示文本,兩者都可以導(dǎo)致不同類型的見(jiàn)解。有 Wide 表示(詞袋)和 Deep 表達(dá)(嵌入)兩種方式,將兩者結(jié)合起來(lái)可以讓我們從文本中提取更多的含義。這個(gè)數(shù)據(jù)集有很多不同的功能可能性,但我們只使用描述以及品種來(lái)進(jìn)行相對(duì)簡(jiǎn)化。以下是此數(shù)據(jù)集的示例輸入和預(yù)測(cè):
輸入
描述: 馥郁的香草氣息從杯中升起,盡管是處于這個(gè)葡萄生長(zhǎng)艱難的年份,果味也立即出現(xiàn)。 它的酸味和尖銳,帶著濃烈的草藥香,葡萄酒迅速成熟,果味,酸味,單寧,草本植物和香草味道的比例相當(dāng)。 這款葡萄酒醇厚而緊實(shí),它還很年輕,需要醒酒和/或更長(zhǎng)時(shí)間的醒酒瓶才能展現(xiàn)出最佳效果。
品種: 黑皮諾
預(yù)測(cè)
價(jià)格?—?45美元
首先,以下是我們構(gòu)建此模型所需的所有導(dǎo)入:
1import os
2import numpy as np
3import pandas as pd
5
6from sklearn.preprocessing import LabelEncoder
7
8from tensorflow import keras
9layers = keras.layers
10
11 # This code was tested with TensorFlow v1.7
12print("You have TensorFlow version", tf.__version__)
由于我們的模型輸出(預(yù)測(cè))是數(shù)字的價(jià)格,我們將價(jià)格值直接提供給我們的模型進(jìn)行訓(xùn)練和評(píng)估。 GitHub 上提供了此模型的完整代碼。 在這里,我將重點(diǎn)介紹關(guān)鍵點(diǎn)。
首先,讓我們下載數(shù)據(jù)并將其轉(zhuǎn)換為 Pandas 數(shù)據(jù)結(jié)構(gòu):
1 !wget -q https://storage.googleapis.com/sara-cloud-ml/wine_data.csv
2data = pd.read_csv("wine_data.csv")
接下來(lái),我們將其拆分為訓(xùn)練和測(cè)試集并提取功能和標(biāo)簽:
1train_size = int(len(data) * .8)
2
3# Train features
4description_train = data['description'][:train_size]
5variety_train = data['variety'][:train_size]
6
7# Train labels
8labels_train = data['price'][:train_size]
9
10# Test features
11description_test = data['description'][train_size:]
12variety_test = data['variety'][train_size:]
13
14# Test labels
15labels_test = data['price'][train_size:]
Part 1:Wide 模型
特征1:葡萄酒描述
為了創(chuàng)建我們的文本描述的 Wide 表示,我們將使用一個(gè)詞袋模型。 Here 有更多相關(guān)內(nèi)容,但在這里我們快速回顧一下:一個(gè)詞袋模型會(huì)在模型的每個(gè)輸入中查找單詞的存在。你可以將每個(gè)輸入視為一袋 Scrabble 圖塊,其中每個(gè)圖塊包含一個(gè)單詞而不是一個(gè)字母。該模型沒(méi)有考慮描述中單詞的順序,僅考慮單詞的存在與否。
注:Here 鏈接
https://en.wikipedia.org/wiki/Bag-of-words_model
將一袋單詞模型的輸入想象為 Scrabble tiles,其中每個(gè) tile 包含來(lái)自輸入的單詞(而不是字母)
我們不會(huì)去逐一查看數(shù)據(jù)集中每個(gè)描述里的每一個(gè)單詞,而是將我們的單詞數(shù)量限制在數(shù)據(jù)集中的前 12,000 個(gè)單詞中(不用擔(dān)心,還有一個(gè)用于創(chuàng)建此詞匯表的內(nèi)置 Keras 實(shí)用程序)。這就被認(rèn)為是 “Wide”,因?yàn)槲覀兊哪P蛯?duì)每個(gè)描述的輸入將是 12k 元素 Wide 的向量,其中 1 和 0 表示在特定描述中存在來(lái)自詞匯表的單詞。
Keras 有一些方便的文本預(yù)處理實(shí)用程序,我們將用它們將文本描述轉(zhuǎn)換成一個(gè)詞袋。使用詞袋模型,我們通常只希望在詞匯表中包含我們數(shù)據(jù)集中找到的總單詞的子集。 在這個(gè)例子中,我使用了 12,000 個(gè)單詞,但這是一個(gè)可以調(diào)整的超參數(shù)(嘗試一些值并查看數(shù)據(jù)集的最佳效果)。 我們可以使用 Keras Tokenizer 類創(chuàng)建我們的詞袋詞匯:
1vocab_size = 12000
2tokenize = keras.preprocessing.text.Tokenizer(num_words=vocab_size, char_level=False)
3tokenize.fit_on_texts(description_train) # only fit on train
然后我們將使用 texts_to_matrix 函數(shù)將每個(gè)描述轉(zhuǎn)換為詞袋向量:
1description_bow_train = tokenize.texts_to_matrix(description_train)
2description_bow_test = tokenize.texts_to_matrix(description_test)
特征2: 葡萄酒品種
在原始的 Kaggle 數(shù)據(jù)集中,共有 632 種葡萄酒品種。 為了讓我們的模型更容易提取模式,我做了一些預(yù)處理,只保留了前 40 個(gè)品種(約占原始數(shù)據(jù)集的 65%,總共 96k 大小的范例)。 我們將使用 Keras 實(shí)用程序?qū)⑦@些變量中的每一個(gè)轉(zhuǎn)換為整數(shù)表示,然后我們將為每個(gè)輸入創(chuàng)建 40 個(gè)元素寬度的的單熱矢量以指示變化:
1# Use sklearn utility to convert label strings to numbered index
2encoder = LabelEncoder()
3encoder.fit(variety_train)
4variety_train = encoder.transform(variety_train)
5variety_test = encoder.transform(variety_test)
6num_classes = np.max(variety_train) + 1
7
8# Convert labels to one hot
9variety_train = keras.utils.to_categorical(variety_train, num_classes)
10variety_test = keras.utils.to_categorical(variety_test, num_classes)
現(xiàn)在我們準(zhǔn)備搭建 Wide 模型。
用 Keras functional API 搭建 Wide 模型
Keras 有兩個(gè)用于構(gòu)建模型的 API:Sequential API 和 Functional API。 Functional API 為我們定義圖層的方式提供了更多的靈活性,并允許我們將多個(gè)特征輸入組合到一個(gè)圖層中。 當(dāng)一切就緒,它還可以輕松地將我們的 Wide 模型和 Deep 模型組合二為一。 使用 Functional API,我們只使用幾行代碼就能輕松定義 Wide 模型。首先,我們將輸入層定義為 12k 元素向量(對(duì)于詞匯表中的每個(gè)單詞)。然后我們將它連接到我們的 Dense 輸出層以生成價(jià)格預(yù)測(cè):
1bow_inputs = layers.Input(shape=(vocab_size,))
2variety_inputs = layers.Input(shape=(num_classes,))
3merged_layer = layers.concatenate([bow_inputs, variety_inputs])
4merged_layer = layers.Dense(256, activation='relu')(merged_layer)
5predictions = layers.Dense(1)(merged_layer)
6wide_model = Model(inputs=[bow_inputs, variety_inputs], outputs=predictions)
然后我們編譯模型,這樣就可以使用:
1wide_model.compile(loss='mse', optimizer='adam', metrics=['accuracy'])
如果我們自己使用 Wide 模型,那么我們將使用 fit() 和 evaluate() 進(jìn)行評(píng)估。由于我們稍后會(huì)將它與我們的 Deep 模型相結(jié)合,我們可以暫停訓(xùn)練,直到兩個(gè)模型結(jié)合起來(lái)。現(xiàn)在是時(shí)候建立我們的 Deep 模型了!
Part 2:Deep 模型
為了創(chuàng)建葡萄酒描述的 Deep 表示,我們將其表示為嵌入。關(guān)于單詞嵌入有很多資源,簡(jiǎn)而言之是它們提供了一種將單詞映射到向量的方法,以便相似的單詞在向量空間中更加靠近。
將描述表示為單詞嵌入
要將我們的文本描述轉(zhuǎn)換為嵌入層,我們首先需要將每個(gè)描述轉(zhuǎn)換為與詞匯表中每個(gè)單詞對(duì)應(yīng)的整數(shù)向量。我們可以用方便的 Keras text_to_sequencesmethod 來(lái)做到這一點(diǎn):
1train_embed = tokenize.texts_to_sequences(description_train)
2test_embed = tokenize.texts_to_sequences(description_test)
現(xiàn)在我們已經(jīng)有了整數(shù)描述向量,我們需要確保它們的長(zhǎng)度都相同,以便將它們輸入到我們的模型中。 Keras 也有一個(gè)方便的方法。我們將使用 pad_sequ 為了創(chuàng)建葡萄酒描述的深層表示,我們將其表示為嵌入。關(guān)于單詞嵌入有很多資源,但簡(jiǎn)短的版本是它們提供了一種將單詞映射到向量的方法,以便相似的單詞在向量空間中更加接近。為每個(gè)描述向量添加零以使它們的長(zhǎng)度相同(我使用 170 作為最大長(zhǎng)度,這樣就沒(méi)有任何描述被縮短:
1max_seq_length = 170
2train_embed = keras.preprocessing.sequence.pad_sequences(train_embed, maxlen=max_seq_length)
3test_embed = keras.preprocessing.sequence.pad_sequences(test_embed, maxlen=max_seq_length)
將我們的描述轉(zhuǎn)換為長(zhǎng)度相同的矢量,我們已準(zhǔn)備好創(chuàng)建嵌入層并將其輸入 Deep 模型。
創(chuàng)建 Deep 模型
要?jiǎng)?chuàng)建嵌入層有兩種方法 - 我們可以使用預(yù)訓(xùn)練嵌入的權(quán)重(有許多開(kāi)源詞嵌入),或者我們可以從詞匯表中學(xué)習(xí)嵌入。最好先試驗(yàn)這兩種方法,看看哪一個(gè)在數(shù)據(jù)集上表現(xiàn)更好。在這里,我們將使用學(xué)習(xí)嵌入。
首先,我們將定義 Deep 模型輸入的形狀。然后我們將它提供給嵌入層。 這里我使用的是 8 維的嵌入圖層(您可以嘗試調(diào)整嵌入圖層的維度)。嵌入層的輸出將是具有形狀的三維矢量:[批量大小,序列長(zhǎng)度(在該示例中為 170),嵌入維度(在該示例中為 8)]。為了將我們的嵌入層連接到密集,完全連接的輸出層,我們需要先將其展平:
1deep_inputs = layers.Input(shape=(max_seq_length,))
2embedding = layers.Embedding(vocab_size, 8, 3input_length=max_seq_length)(deep_inputs)
embedding = layers.Flatten()(embedding)
一旦嵌入層變平,就可以將其輸入模型并進(jìn)行編譯:
1embed_out = layers.Dense(1, activation='linear')(embedding)
2deep_model = Model(inputs=deep_inputs, outputs=embed_out)
3deep_model.compile(loss='mse', optimizer='adam', metrics=['accuracy'])
Part 3:Wide 和 Deep
一旦我們定義了兩個(gè)模型,將它們組合起來(lái)就很容易。 我們只需要?jiǎng)?chuàng)建一個(gè)連接每個(gè)模型的輸出圖層,然后將它們合并到一個(gè)完全連接的 Dense 圖層中,最后定義一個(gè)組合模型,它將每個(gè)模型的輸入和輸出結(jié)合起來(lái)。顯然,由于每個(gè)模型都預(yù)測(cè)相同的事情(價(jià)格),因此每個(gè)模型的輸出或標(biāo)簽都將是相同的。另請(qǐng)注意,由于我們的模型輸出是一個(gè)數(shù)值,我們不需要進(jìn)行任何預(yù)處理 - 它已經(jīng)是正確的格式:
1merged_out = layers.concatenate([wide_model.output, deep_model.output])
2merged_out = layers.Dense(1)(merged_out)
3combined_model = Model(wide_model.input + [deep_model.input], merged_out)
4combined_model.compile(loss='mse',optimizer='adam', metrics=['accuracy'])
有了這個(gè),就該進(jìn)行培訓(xùn)和評(píng)估了。你可以嘗試最適合您的數(shù)據(jù)集的訓(xùn)練時(shí)期和批量大小的訓(xùn)練次數(shù):
1# Training
2combined_model.fit([description_bow_train, variety_train] + [train_embed], labels_train, epochs=10, batch_size=128)
3
4# Evaluation
5combined_model.evaluate([description_bow_test, variety_test] + [test_embed], labels_test, batch_size=128)
在我們訓(xùn)練模型中生成預(yù)測(cè)
到了最關(guān)鍵部分的時(shí)間了。了解我們的模型對(duì)之前從未見(jiàn)過(guò)的數(shù)據(jù)如何表現(xiàn)。 為此,我們可以在我們訓(xùn)練的模型上調(diào)用 predict(),并將測(cè)試數(shù)據(jù)集傳遞給它(在以后的文章中我將介紹如何從純文本輸入中獲取預(yù)測(cè)):
1predictions = combined_model.predict([description_bow_test, variety_test] + [test_embed])
然后我們將預(yù)測(cè)與我們測(cè)試數(shù)據(jù)集中前 15 種葡萄酒的實(shí)際價(jià)格進(jìn)行比較:
1for i in range(15):
2val = predictions[i]
3print(description_test[i])
4print(val[0], 'Actual: ', labels_test.iloc[i], ' ')
模型是怎么做的?我們來(lái)看看測(cè)試集中的三個(gè)例子:
馥郁的香草氣息從杯中升起,盡管是處于葡萄生長(zhǎng)艱難的年份,果味也立即出現(xiàn)。它的酸味和尖銳,帶著濃烈的草藥香,葡萄酒迅速成熟,果味,酸味,單寧,草本植物和香草味道的比例相當(dāng)。這款葡萄酒醇厚而緊實(shí),它還很年輕,需要醒酒和/或更長(zhǎng)時(shí)間的醒酒瓶才能展現(xiàn)出最佳效果。
預(yù)測(cè)價(jià)格: 46.233624 實(shí)際價(jià)格: 45.0
一款美味的日常酒。它是干型的,濃郁,足夠的漿果櫻桃香,包裹成光滑的質(zhì)地。
預(yù)測(cè)價(jià)格: 9.694958 實(shí)際價(jià)格: 10.0
這是一款現(xiàn)代,圓潤(rùn),天鵝絨般的巴羅羅(來(lái)自 Monforte d'Alba),適合那些喜歡醇厚多汁的葡萄酒的人。香氣包含薰衣草,五香粉,肉桂,白巧克力和香草。 酸味漿果口味附帶著酸甜的口感和結(jié)實(shí)的單寧賦予了肯定和堅(jiān)韌的口感。
預(yù)測(cè)價(jià)格: 41.028854 實(shí)際價(jià)格: 49.0
很不錯(cuò)! 事實(shí)證明,葡萄酒的描述與其價(jià)格之間存在某種關(guān)系。我們可能無(wú)法本能地看到它,但我們的 ML 模型可以。
-
模型
+關(guān)注
關(guān)注
1文章
3268瀏覽量
48937 -
代碼
+關(guān)注
關(guān)注
30文章
4803瀏覽量
68757 -
keras
+關(guān)注
關(guān)注
2文章
20瀏覽量
6089
原文標(biāo)題:用 Keras Functional API 和 TensorFLow 預(yù)測(cè)葡萄酒的價(jià)格
文章出處:【微信號(hào):tensorflowers,微信公眾號(hào):Tensorflowers】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論