如何使用Keras建立一个 Wide & Deep网络来预测其描述中的葡萄酒价格

网友投稿 252 2022-10-27

如何使用Keras建立一个 Wide & Deep网络来预测其描述中的葡萄酒价格

向 Francois,Josh 和 Yufeng 致敬,感谢他们对这篇文章的帮助和意见。

模型:Wide & Deep 与 Keras 结合

数据集:预测葡萄酒的价格

我们将使用 Kaggle 葡萄酒数据集 this wine dataset 来查看:

我们可以从描述和品种中预测出一瓶葡萄酒的价格吗?

这个问题非常适合 Wide & Deep 的学习,因为它涉及到文本输入,并且葡萄酒的描述与其价格之间并没有明显的相关性。我们无法斩钉截铁地说,描述中带有 “果味” 一词的葡萄酒更贵,或者有 “柔和的单宁” 描述的葡萄酒更便宜。此外,当我们将文本提供给模型时,有很多种方式来表示文本,两者都可以导致不同类型的见解。有 Wide 表示(词袋)和 Deep 表达(嵌入)两种方式,将两者结合起来可以让我们从文本中提取更多的含义。这个数据集有很多不同的功能可能性,但我们只使用描述以及品种来进行相对简化。以下是此数据集的示例输入和预测:

输入

描述: 馥郁的香草气息从杯中升起,尽管是处于这个葡萄生长艰难的年份,果味也立即出现。 它的酸味和尖锐,带着浓烈的草药香,葡萄酒迅速成熟,果味,酸味,单宁,草本植物和香草味道的比例相当。 这款葡萄酒醇厚而紧实,它还很年轻,需要醒酒和/或更长时间的醒酒瓶才能展现出最佳效果。

品种: 黑皮诺

预测

价格 — 45美元

首先,以下是我们构建此模型所需的所有导入:

1import os

2import numpy as np

3import pandas as pd

5

6from sklearn.preprocessing import LabelEncoder

7

8from tensorflow import keras

10

11 # This code was tested with TensorFlow v1.7

12print("You have TensorFlow version", tf.__version__)

由于我们的模型输出(预测)是数字的价格,我们将价格值直接提供给我们的模型进行训练和评估。 GitHub 上提供了此模型的完整代码。 在这里,我将重点介绍关键点。

接下来,我们将其拆分为训练和测试集并提取功能和标签:

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:葡萄酒描述

为了创建我们的文本描述的 Wide 表示,我们将使用一个词袋模型。 Here 有更多相关内容,但在这里我们快速回顾一下:一个词袋模型会在模型的每个输入中查找单词的存在。你可以将每个输入视为一袋 Scrabble 图块,其中每个图块包含一个单词而不是一个字母。该模型没有考虑描述中单词的顺序,仅考虑单词的存在与否。

注:Here 链接

将一袋单词模型的输入想象为 Scrabble tiles,其中每个 tile 包含来自输入的单词(而不是字母)

我们不会去逐一查看数据集中每个描述里的每一个单词,而是将我们的单词数量限制在数据集中的前 12,000 个单词中(不用担心,还有一个用于创建此词汇表的内置 Keras 实用程序)。这就被认为是 “Wide”,因为我们的模型对每个描述的输入将是 12k 元素 Wide 的向量,其中 1 和 0 表示在特定描述中存在来自词汇表的单词。

Keras 有一些方便的文本预处理实用程序,我们将用它们将文本描述转换成一个词袋。使用词袋模型,我们通常只希望在词汇表中包含我们数据集中找到的总单词的子集。 在这个例子中,我使用了 12,000 个单词,但这是一个可以调整的超参数(尝试一些值并查看数据集的最佳效果)。 我们可以使用 Keras Tokenizer 类创建我们的词袋词汇:

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 函数将每个描述转换为词袋向量:

1description_bow_train = tokenize.texts_to_matrix(description_train)

2description_bow_test = tokenize.texts_to_matrix(description_test)

特征2: 葡萄酒品种

在原始的 Kaggle 数据集中,共有 632 种葡萄酒品种。 为了让我们的模型更容易提取模式,我做了一些预处理,只保留了前 40 个品种(约占原始数据集的 65%,总共 96k 大小的范例)。 我们将使用 Keras 实用程序将这些变量中的每一个转换为整数表示,然后我们将为每个输入创建 40 个元素宽度的的单热矢量以指示变化:

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)

现在我们准备搭建 Wide 模型。

用 Keras functional API 搭建 Wide 模型

Keras 有两个用于构建模型的 API:Sequential API 和 Functional API。 Functional API 为我们定义图层的方式提供了更多的灵活性,并允许我们将多个特征输入组合到一个图层中。 当一切就绪,它还可以轻松地将我们的 Wide 模型和 Deep 模型组合二为一。 使用 Functional API,我们只使用几行代码就能轻松定义 Wide 模型。首先,我们将输入层定义为 12k 元素向量(对于词汇表中的每个单词)。然后我们将它连接到我们的 Dense 输出层以生成价格预测:

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() 进行评估。由于我们稍后会将它与我们的 Deep 模型相结合,我们可以暂停训练,直到两个模型结合起来。现在是时候建立我们的 Deep 模型了!

Part 2:Deep 模型

为了创建葡萄酒描述的 Deep 表示,我们将其表示为嵌入。关于单词嵌入有很多资源,简而言之是它们提供了一种将单词映射到向量的方法,以便相似的单词在向量空间中更加靠近。

将描述表示为单词嵌入

要将我们的文本描述转换为嵌入层,我们首先需要将每个描述转换为与词汇表中每个单词对应的整数向量。我们可以用方便的 Keras text_to_sequencesmethod 来做到这一点:

1train_embed = tokenize.texts_to_sequences(description_train)

2test_embed = tokenize.texts_to_sequences(description_test)

现在我们已经有了整数描述向量,我们需要确保它们的长度都相同,以便将它们输入到我们的模型中。 Keras 也有一个方便的方法。我们将使用 pad_sequ 为了创建葡萄酒描述的深层表示,我们将其表示为嵌入。关于单词嵌入有很多资源,但简短的版本是它们提供了一种将单词映射到向量的方法,以便相似的单词在向量空间中更加接近。为每个描述向量添加零以使它们的长度相同(我使用 170 作为最大长度,这样就没有任何描述被缩短:

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)

将我们的描述转换为长度相同的矢量,我们已准备好创建嵌入层并将其输入 Deep 模型。

创建 Deep 模型

要创建嵌入层有两种方法 - 我们可以使用预训练嵌入的权重(有许多开源词嵌入),或者我们可以从词汇表中学习嵌入。最好先试验这两种方法,看看哪一个在数据集上表现更好。在这里,我们将使用学习嵌入。

首先,我们将定义 Deep 模型输入的形状。然后我们将它提供给嵌入层。 这里我使用的是 8 维的嵌入图层(您可以尝试调整嵌入图层的维度)。嵌入层的输出将是具有形状的三维矢量:[批量大小,序列长度(在该示例中为 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)

一旦嵌入层变平,就可以将其输入模型并进行编译:

2deep_model = Model(inputs=deep_inputs, outputs=embed_out)

3deep_model.compile(loss='mse', optimizer='adam', metrics=['accuracy'])

Part 3:Wide 和 Deep

一旦我们定义了两个模型,将它们组合起来就很容易。 我们只需要创建一个连接每个模型的输出图层,然后将它们合并到一个完全连接的 Dense 图层中,最后定义一个组合模型,它将每个模型的输入和输出结合起来。显然,由于每个模型都预测相同的事情(价格),因此每个模型的输出或标签都将是相同的。另请注意,由于我们的模型输出是一个数值,我们不需要进行任何预处理 - 它已经是正确的格式:

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'])

有了这个,就该进行培训和评估了。你可以尝试最适合您的数据集的训练时期和批量大小的训练次数:

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)

在我们训练模型中生成预测

到了最关键部分的时间了。了解我们的模型对之前从未见过的数据如何表现。 为此,我们可以在我们训练的模型上调用 predict(),并将测试数据集传递给它(在以后的文章中我将介绍如何从纯文本输入中获取预测):

1predictions = combined_model.predict([description_bow_test, variety_test] + [test_embed])

然后我们将预测与我们测试数据集中前 15 种葡萄酒的实际价格进行比较:

1for i in range(15):

2val = predictions[i]

3print(description_test[i])

4print(val[0], 'Actual: ', labels_test.iloc[i], '')

模型是怎么做的?我们来看看测试集中的三个例子:

馥郁的香草气息从杯中升起,尽管是处于葡萄生长艰难的年份,果味也立即出现。它的酸味和尖锐,带着浓烈的草药香,葡萄酒迅速成熟,果味,酸味,单宁,草本植物和香草味道的比例相当。这款葡萄酒醇厚而紧实,它还很年轻,需要醒酒和/或更长时间的醒酒瓶才能展现出最佳效果。

预测价格: 46.233624 实际价格: 45.0

一款美味的日常酒。它是干型的,浓郁,足够的浆果樱桃香,包裹成光滑的质地。

预测价格: 9.694958 实际价格: 10.0

这是一款现代,圆润,天鹅绒般的巴罗罗(来自 Monforte d'Alba),适合那些喜欢醇厚多汁的葡萄酒的人。香气包含薰衣草,五香粉,肉桂,白巧克力和香草。 酸味浆果口味附带着酸甜的口感和结实的单宁赋予了肯定和坚韧的口感。

预测价格: 41.028854 实际价格: 49.0

很不错! 事实证明,葡萄酒的描述与其价格之间存在某种关系。我们可能无法本能地看到它,但我们的 ML 模型可以。

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:在spring中实例化bean无效的问题
下一篇:Gitlab+Jenkins+K8s+Kuboard+Harbor实现自动化CICD
相关文章

 发表评论

暂时没有评论,来抢沙发吧~