创建自动侍酒师2019年8月,我投资了我的第一个自然语言处理(NLP)项目,并在我的网站上托管了Auto-Sommelier。使用TensorFlow 1和通用情感编码器,我允许用户描述他们理想的葡萄酒
2019年8月,我投资了我的第一个自然语言处理(NLP)项目,并在我的网站上托管了Auto-Sommelier。使用TensorFlow 1和通用情感编码器,我允许用户描述他们理想的葡萄酒,并返回类似于查询的描述的葡萄酒。该工具将葡萄酒评论和用户输入转换为向量,并计算用户输入和葡萄酒评论之间的余弦相似度,以找到最相似的结果。
余弦相似度是比较文档相似度的常用方法,因为它适用于词频等对分析非常重要的数据。它反映了单个向量维度的相对比较,而不是绝对比较。在本文中,我不会深究余弦相似性背后的数学,而是明白它是一个内积空中两个非零向量之间的相似性度量。
是时候改善了。
虽然模型仍然有效,但自2019年以来,自然语言处理已经取得了很大的进步。使用HuggingFace这样的工具将句子或段落转换成向量,可以用于语义相似度等自然语言处理任务,这是前所未有的简单。用最新的技术和语言模型重构我的代码,会让它的性能更好。
在本教程中,我将解释如何使用HuggingFace Transformers库、非公制空间库和Dash库来构建一个新的改进的自动侍酒师。完整的代码和GitHub链接可以在文章的底部找到。
数据这些葡萄酒数据来自kaggle.com葡萄酒评论的数据集。原始文档包含大约13万行数据,包括国家、描述、标题、品种、酒厂、价格和评级等列。。
将数据放入dataframe后,我删除了具有重复描述的行和具有空价格的行。我还将数据限制在有超过200条评论的葡萄酒品种上。
通过剔除评论少于200的品种,我得到了54个葡萄酒品种。在清除空数据和重复数据后,还剩100228行。通过谷歌搜索剩余的葡萄酒品种,我添加了一个“颜色”列,这样用户可以根据所需的葡萄酒颜色来限制搜索。
导入依赖项和数据
因为数据已经是一个sqlite文件,所以很容易连接和加载数据。按照三个步骤加载库、数据和数据框架。
导入pandas和sqlite3库。连接到sqlite文件。将数据加载到一个pandas DataFrame中。#Import dependencies import numpy as npimport pandas as pdimport sqlite3from sqlite3 import Errorimport texthero as herofrom texthero import preprocessingfrom sentence_transformers import SentenceTransformer, utilimport nmslibimport timeimport datetime#Establish connection to sqlite databaseconn = sqlite3.connect("wine_data.sqlite")#load the data into a pandas DataFramedf = pd.read_sql("select * from wine_data", conn)
我还导入了将在本教程中使用的其他库。我将更详细地介绍它们。使用pandas read_sql函数从原始sql生成一个df。数据集中有16列和100228行。
注意:把所有文本转换成向量可能需要一段时间,所以如果你只是想尝试一下,我建议只用2万条记录进行快速训练。
拥抱脸变形金刚如果你在过去的一年中参与了自然语言处理(NLP)领域,你可能听说过HuggingFace。HuggingFace是一个专注于自然语言处理的人工智能和深度学习平台,目标是普及人工智能技术。他们简化了应用程序,并对预先训练的语言模型进行了微调。
Transformer是一个带有模型的开源库,它允许用户实现基于BERT、XLM和DistilBert等通用架构的最先进的深度学习模型。它基于PyTorch、TensorFlow和Jax。众所周知,这些框架具有良好的互操作性。
pip install transformers
在这个例子中,我将使用distilBERT-base-uncase模型,因为它显示出与我们的用例和语义有很好的相似性。它将文本转换成768维的向量。如果不想用distilBERT,可以用所有HuggingFace模型来找句子相似度。这个模型是未知的,这意味着它不区分大小写。请参考官方文件了解该模型的详细信息。
要实施此模型,请遵循以下步骤:
用distilBERT-base-uncase模型实例化SentenceTransformer。
调用encode并向其传递葡萄酒描述。设置convert_to_tensor = True参数。
#load the distilbert model distilbert = SentenceTransformer('distilbert-base-uncased')#generate the embeddings for the wine reviewsembeddings = distilbert.encode(df['description'], convert_to_tensor=True)
注意:如果您以前从未下载过该模型,您将看到它已下载,并且可能会弹出一些消息。这很正常。
一旦该过程完成,文本描述将被转换成长度为768的向量。我们可以检查长度和嵌入,以确保它看起来像预期的那样:
为了使矢量更容易分析,使用numpy将数据从张量对象转换为列表对象,然后将列表添加到pandas数据帧中。
#add embeddings to dataframedf['distilbert'] = np.array(embeddings).tolist()#show the top rowdf.head(1)
创建搜索索引
当使用像Google或Bing这样的搜索引擎时,用户希望快速获得结果。为了以闪电般的速度搜索结果集,我们可以使用轻量级高效的非公制空跨库(NMSLIB)。
使用pip安装:
pip install nmslib
如前所述,我们希望使用余弦相似度作为衡量标准,将用户输入与葡萄酒描述进行比较。我们需要找到最接近搜索向量的向量。使用暴力循环技术对数据进行搜索和排序既昂贵又缓慢。相反,为数据点创建索引要快得多。
创建余弦相似性指数非常简单:
初始化一个新的索引,方法是hnsw,余弦在空之间。
使用addDataPointBatch方法将嵌入项添加到索引中。
使用createIndex方法创建使用数据点的索引。
# initialize a new index, using a HNSW index on Cosine Similaritydistilbert_index = nmslib.init(method='hnsw', space='cosinesimil')distilbert_index.addDataPointBatch(embeddings)distilbert_index.createIndex({'post': 2}, print_progress=True)
如果您想要保存索引并在以后加载它(比如在生产服务器上),请使用下面的样板代码:
#Save a meta index and the dataindex.saveIndex('index.bin', save_data=True)#Re-intitialize the library, specify the spacenewIndex = nmslib.init(method='hnsw', space='cosinesimil_sparse')#Re-load the index and the datanewIndex.loadIndex('sparse_index.bin', load_data=True)
创建搜索功能既然数据已经进行了矢量化,搜索索引也已经填充完毕,那么是时候创建一个接受用户查询并返回wine-like的函数了。
Search_wine函数将接受两个输入:DataFrame和UserQuery。使用encode将查询转换成一个向量,就像我们对葡萄酒描述所做的那样。然后,可以使用NMSLIB返回用户查询向量的k个最近邻。我把k设为20,但是你可以随意实验。
def search_wine(dataframe, userQuery):if dataframe is not None and userQuery is not None:df = dataframe.copy()query = distilbert.encode([userQuery], convert_to_tensor=True)ids, distances = distilbert_index.knnQuery(query, k=20) matches = [] for i, j in zip(ids, distances): matches.append({'country':df.country.values[i], 'winery' : df.winery.values[i], 'title' : df.title.values[i], 'variety': df.variety.values[i], 'color' : df.color.values[i], 'description': df.description.values[i], 'price': df.price.values[i], 'rating': df.rating.values[i], 'distance': j}) return pd.DataFrame(matches)
注意,返回的结果作为字典添加到列表中。这使得将结果转换回df变得很容易。对于距离值,越小越好。例如,距离为0意味着两个向量相同。
测试:
形象化
除了文本搜索,我们还可以使用降维技术在2D 空画酒。使用Texthero库,很容易应用t-SNE算法来降低向量的维数并将其可视化。实际上,Texthero使用Plotly制作交互式图表。
T-SNE (t-分布式随机邻域嵌入)是一种用于高维数据可视化的机器学习算法。T-SNE技术采用非线性降维。
T-SNE应用于数据中的distiller vector列。
df['tsnedistilbert'] = hero.tsne(df['distilbert'])
使用texthero创建散点图。
#create scatter plot of wines using the hero.scatterplot(df, col='tsnedistilbert', color='variety', title="Wine Explorer", hover_data = ['title','variety','price','description'])
数据中有许多不同类型的散点图,看起来像宇宙背景辐射,但这并不重要。将鼠标悬停在这些点上会显示更多信息。用户可以单击各种图标将它们从图表中删除。
有趣的是,我们可以看到一些品种是如何聚集在一起的,而另一些品种则分散在各处。
创建界面为了使用户能够与搜索功能进行交互,我们可以使用Plotly的Dash来构建一个简单的用户界面。Dash是一个基于Flask,plot的Python框架。js和React.js
安装Dash,Dash引导组件和jupyter- Dash,如果你想在jupyter笔记本中建立Dash应用程序。
pip install dashpip install dash-bootstrap-componentspip install jupyter-dash #if you want to build in a jupyter notebook
Dash应用程序由布局和回调组成:
布局:布局由一个组件树组成,它描述了应用程序的外观以及用户如何体验内容。
回调:回调函数使Dash应用程序具有交互性。回调函数是一个Python函数,只要输入属性发生变化,就会自动调用该函数。
这部分就不详细介绍了。有兴趣的读者可以阅读原文。
最后的想法和完整的代码。与我在2019年创建的原始自动侍酒师相比,这个版本更快、更简单。通过像HuggingFace这样的框架利用最先进的语言模型的力量,为像我这样的机器学习爱好者打开了一扇门,他们可以只用几行代码就构建一些伟大的应用程序。现在是时候做一些分析了,看看结果与原来的工具相比有什么改进!
本文代码
:github/bend game/mediumwinecomment 2