ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

import

import 一、引言在向用户推荐相关内容这件事上, 内容平台发展得极为迅速, 平台所提供的相关内容数目越多, 用户于网站之上停留的时长便会越长, 而这一般情况下会转变为公司所需的广告收入。假使你往昔曾访问过新闻网站, 或者电子出版社, 又或者博客平台, 那么你大概率已然接触过推荐引擎。每一个平台都会依据你的那阅读历史记录, 去推荐你有可能会喜欢的内容。罗列出一个较为简易的解决办法, 有一个平台能够达成一个基于标签现象的将物品呈现给用户为之推荐的引擎, 打比方说, 你阅读查看过一篇标明是「商业」范畴的文章, 其还会给你介绍推选五篇被标记住是「商业」领域特性的有相关性的文章。可是, 可以用来构架推举引擎的更为优良的办法路径是运用具备相似性的搜索以及借助机器学习的相关计算准则法子。于本文里, 我们要构建一个Flask应用程序, 此应用程序会借助一个相似性搜索的服务, 去创建属于我们自身的文章推荐引擎。二、源代码解析演示应用程序概览如下简短动画, 演示了我们应用程序的工作方式。最初, 页面上显示着十篇文章, 用户能够选择这十篇文章的任意组合, 来代表他们的阅读历史。当用户点击提交按钮时, 阅读历史会作为输入数据, 用于查询文章数据库, 随后再向用户显示十篇相关文章。能够瞧见, 所返回的相关文章极为精准于这个示例之中, 存在1,024种有可能性的阅读历史组合可被用作输入, 而且每一种组合都会生成有意义的成果。那么我们是如何做到的呢构建应用程序之际, 我们先是从某处找到了一个新闻文章数据集, 此数据集涵盖来自15个主要出版社的143,000篇新闻文章, 然而我们仅仅选用了前面的20,000篇, 完整数据集包含超过200万篇文章呢之后, 我们对数据集进行清理, 是通过重命名几列, 还删除了不必要的列来达成的。接下来, 我们把文章输入到模型当中, 目的是获取各文章的向量, 而这向量可是机器学习算法用来确定各种输入之间相似性的元数据。我们所使用的是平均词嵌入模型。然后, 我们把这些向量插入到由管理的向量索引里。向量被添加到索引之后, 我们便能够着手查找相关内容了。用户提交其阅读历史之际, 会向 API 端点发送一个请求, 此端点借助的 SDK 去查询向量的索引, 端点继而返回十篇类同的新闻文章并于应用程序的 UI 中予以显示, 如此而已这是不是相当简单呢?要是你打算亲自去尝试一回, 你能够在此处寻获该应用程序的源码。于其中涵盖了怎样在本地设备之上运行应用程序的指示以及说明。代码实现我们已然知晓了应用程序的内在工作原理, 然而我们实际上究竟是怎样构建它的呢。如前文所讲, 这是一个借助SDK的Flask应用程序。HTML运用模板文件, 前端剩下的部分借助静态CSS来构建。为了简便起见, 所有后端代码都在app.py文件里, 我把该文件完整地照抄在了下面:from dotenv import load_dotenv from flask import Flask from flask import render_template from flask import request from flask import url_for import json import os import pandas as pd import pinecone import re import requests from sentence_transformers import SentenceTransformer from statistics import mean import swifter app Flask(__name__) PINECONE_INDEX_NAME article-recommendation-service DATA_FILE articles.csv NROWS 20000 def initialize_pinecone(): load_dotenv() PINECONE_API_KEY os.environ[PINECONE_API_KEY] pinecone.init(api_keyPINECONE_API_KEY) def delete_existing_pinecone_index(): if PINECONE_INDEX_NAME in pinecone.list_indexes(): pinecone.delete_index(PINECONE_INDEX_NAME) def create_pinecone_index(): pinecone.create_index(namePINECONE_INDEX_NAME, metriccosine, shards1) pinecone_index pinecone.Index(namePINECONE_INDEX_NAME) return pinecone_index def create_model(): model SentenceTransformer(average_word_embeddings_komninos) return model def prepare_data(data): # 重命名 id 列并删除不必要的列 data.rename(columns{Unnamed: 0: article_id}, inplace True) data.drop(columns[date], inplace True) # 仅提取每篇文章的前几句话以加快向量的计算 data[content] data[content].fillna() data[content] data.content.swifter.apply(lambda x: .join(re.split(r(?[.:;])\s, x)[:4])) data[title_and_content] data[title] data[content] # 基于标题列和文章列创建 embedding 向量 encoded_articles model.encode(data[title_and_content], show_progress_barTrue) data[article_vector] pd.Series(encoded_articles.tolist()) return data def upload_items(data): items_to_upload [(row.id, row.article_vector) for i, row in data.iterrows()] pinecone_index.upsert(itemsitems_to_upload) def process_file(filename): data pd.read_csv(filename, nrowsNROWS) data prepare_data(data) upload_items(data) pinecone_index.info() return data def map_titles(data): return dict(zip(uploaded_data.id, uploaded_data.title)) def map_publications(data): return dict(zip(uploaded_data.id, uploaded_data.publication)) def query_pinecone(reading_history_ids): reading_history_ids_list list(map(int, reading_history_ids.split(,))) reading_history_articles uploaded_data.loc[uploaded_data[id].isin(reading_history_ids_list)] article_vectors reading_history_articles[article_vector] reading_history_vector [*map(mean, zip(*article_vectors))] query_results pinecone_index.query(queries[reading_history_vector], top_k10) res query_results[0] results_list [] for idx, _id in enumerate(res.ids): results_list.append({ id: _id, title: titles_mapped[int(_id)], publication: publications_mapped[int(_id)], score: res.scores[idx], }) return json.dumps(results_list) initialize_pinecone() delete_existing_pinecone_index() pinecone_index create_pinecone_index() model create_model() uploaded_data process_file(filenameDATA_FILE) titles_mapped map_titles(uploaded_data) publications_mapped map_publications(uploaded_data) app.route(/) def index(): return render_template(index.html) app.route(/api/search, methods[POST, GET]) def search(): if request.method POST: return query_pinecone(request.form.history) if request.method GET: return query_pinecone(request.args.get(history, )) return Only GET and POST methods are allowed for this endpoint app.run()我们来进行一下 app.py 文件重要部分的回顾, 从而使我们能够对其加以理解。在第一行到第十四行的范围之内, 我们将应用程序的那些依赖给引入进来 , 接下来要说的是, 我们的这个应用程序依附着以下这些包用于从 .env 文件中读取环境变量flask 用于设置 Web 应用程序json 用于处理 JSONos 也用于获取环境变量用于处理数据集用于使用 SDKre 用于处理正则表达式RegEx用于发送 API 请求以下载我们的数据集提供了一些方便的统计函数s 用于我们的嵌入模型用于处理 的, 在第16行, 我们给出了一些模板代码, 以此来告知Flask我们应用程序的名称。在第18行, 我们定义了一些常量, 这些常量会在应用程序里被使用其中有索引名称, 它是常量之一, 之后, 我们又定义了数据集的文件名, 这也是常量中的一部分, 并且, 我们明确了要从CSV文件中读取的行数, 这同样属于所定义的常量。在第22行, 我们的方法从.env文件中获取API密钥, 在第23行, 继续使用该密钥, 在第24行, 进行相关操作来初始化, 在第25行, 完成初始化相关事宜。从第27行开始数, 一直数到第29行, 在这个范围内, 我们所拥有的方法, 会在实例当中, 去搜寻那个跟我们当下正在运用的索引, 也就是“--”, 有着相同名字的索引。要是寻找到了已经存在的索引, 那么我们就要把它给删除掉。在第31行, 到第35行, 我们的x方法, 运用我们所挑选的名称“--”, 去创建一个全新的索引, 将余弦相似度用作指标, 数据分片设定为1。从第37行开始至40行这部分, 我们所运用的方法用以借助s库去处理平均词嵌入模型, 之后呢, 我们打算运用此模型针对我们的向量实施编码的操作。在第六十二行之处, 一直到第六十八行那里, 我们所拥有的方法, 会去读取CSV文件, 之后呢, 会调用某些方法, 这某些方法, 就是接下来要提及的两个方法, 这两个方法的介绍, 处于下方的位置。在第42行到第56行的范围里, 我们的那种方法, 是借助重命名第一个那种id列, 还有删除date列, 以此来对数据集实施调整的操作。随后, 它会去抓取每一篇文章的前面四行内容, 并且把这些内容跟文章标题相互结合起来, 进而创建出一个全新的字段, 将这个字段当作是要进行编码的数据。我们能够依据文章的整个正文部分去创建向量, 不过为了让编码的过程能够加快速度, 四行其实就已经足够了。在第58行, 到第60行, 处, 我们的方法, 借助使用我们的模型, 对其展开编码, 以此为每一篇文章, 创建一个向量, 随后, 把向量插入到索引里。在第70行, 到第71行, 再到第72 行, 以及第73行, 还有第74行, 我们的一种方法, 和另一种方法, 创立了一些字典, 这些字典里面包含着标题, 以及出版商, 这样做是为了往后, 能够更加便利地, 利用它们的ID, 去查找文章。上方所描述的每一个方法, 都会于后端应用程序启动这个时候, 在第98行至104行的位置进行调用。而这项工作, 使得我们为最后一步, 也就是依据用户输入去查询索引, 做好相应的准备。在第106行, 到第115行, 再到第116行, 我们给应用程序确定了两条路由, 一条是针对主页路, 另一条是向着API端点路, 主页给出index.html模板文件, 还有和CSS资产, API端点给出查询索引所具备的搜索功能。最后在第76行至96行, 我们的方法会获取用户的阅读历史输入, 接着把它转换为向量, 随后查询索引来查找相似文章。获取/api/端点的时候会调用此方法。每当用户提交新的搜索查询, 这个API都会被调用。对于视觉学习者这里提供一个概述应用程序如何工作的图表三、示例场景那么, 把所有的这些整合在一起, 用户体验究竟怎样呢? 我们来看三个场景, 是分别对体育、技术以及政治感兴趣的三群体用户。体育用户把前两篇围绕塞雷娜·威廉姆斯以及安迪·穆雷Andy这两位有名网球运动员的文章选为他们的阅读历史。在进行提交选择之后, 该应用程序会传回有关温布尔登网球锦标赛、美国公开赛、罗杰·费德勒Roger还有拉斐尔·纳达尔Nadal的文章。准确哟
返回列表