TowardsDataScience 博客中文翻译 2019(九十)

原文:TowardsDataScience Blog

协议:CC BY-NC-SA 4.0

为 Slack 构建一个王座聊天机器人游戏:第 1 部分理解语言

原文:https://towardsdatascience.com/building-a-chatbot-for-slack-from-scratch-part-1-understanding-language-1f085b2eda6c?source=collection_archive---------30-----------------------

经验教训将深度学习应用于自然语言理解并结合问答

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

去年夏天,我决定测试一下我的 NLP 技能,着手开发一个聊天机器人。作为《权力的游戏》的忠实粉丝,我决定为《权力的游戏》开发一个聊天机器人。最初,我的目标只是提供一种从 Reddit、Watchers on the Wall、Los Siete Reinos 和 Twitter 等网站轻松获取各种新闻的方式。然而,我很快决定扩展到其他任务,我对如何整合现代 NLP 方法来丰富我的聊天机器人特别感兴趣。这第一篇文章将涵盖自然语言理解和问答组件;第二篇文章将更多地讨论平台和架构。

如果你想使用聊天机器人,那么你可以加入 Slack 上的 Citadel 社区(虽然,正如我将要描述的,我还没有将所有描述的功能添加到实际的产品版本中)。在未来,我计划增加支持,将它安装在您的工作区。还要注意的是,本文中的一些例子和机器人本身的内容包含了第七季的信息。

自然语言理解(NLU)

NLU 深度学习简介

在哪里以及如何将深度学习集成到聊天机器人中,实际上是一个有点棘手的问题。在聊天机器人的基础上,你可以使用基于规则的方法,机器学习,或者两者的结合。使用基于规则的方法,你通常可以保证只要用户以公式化和受限的方式书写,聊天机器人就会正确地响应用户的查询。通过机器学习(甚至是统计 NLP 方法),你可以打破刻板的公式,让用户更自然地打字。然而,这样做也引入了不确定性(即使是最好的模型)。此外,即使是 SOTA 模型通常也只适用于有限类型的对话。例如,如果用户开始参与聊天,为面向目标的对话训练的模型通常会崩溃。深度学习当然也需要数据,在这种情况下,我们经常会遇到冷启动问题。因此,你经常需要写你认为用户会问的样本数据。这个过程既费时又不准确。

形式化的方式

在看为什么我们可能需要基于机器学习的模型之前,让我们看看使用基于规则的方法的一些限制。使用聊天机器人的公式化方式,我们可以编写类似这样的代码:

Note this is purposefully simplfied. In my actual bot for my rule based methods I usually use a dict to map words to actions to avoid long if statement like these. Also you would obviously have to handle the DB operations etc

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Example users requests and responses using the formulaic approach

现在,用户必须用非常公式化的方式来使用聊天机器人。他们必须准确地写出Quote Jon SnowNews Reddit如果你只想包含简单的查询,这没问题。但是如果想要支持像Quote about Jon Snow甚至Quote from Jon Snow when he is talking about Stannis?这样的短语的功能,我们可以强迫用户以一种公式化的方式做事情,但是这对用户来说很快就变得复杂和累赘。对于新闻来说也是如此,支持像Get news from the past 24 Hours on Reddit甚至News about Jon Snow in Season 8这样的复杂查询在最好的情况下会变得很困难,在最坏的情况下也是不可能的。

槽填充和意图检测

这就把我们带到了机器学习的缝隙填充和意图检测。在聊天机器人中使用深度学习的一个关键领域是自动获取用户输入的字符串,并将相关的令牌映射到 API 的插槽(这被称为插槽填充或 SLU)。一个相关的领域是意图检测,其集中于将话语映射到意图。这是一个 SLU 注释的例子

Quote about Jon                 Snow
0       0   B-focus-character   I-focus-character
Intent: Quote 0 Quote from   Jon       Snow      when he is talking about Stannis
0       0   B-speaker  I-speaker  0   0   0   0       0   I-focus
Intent: Quote 0

在某种意义上,槽填充是命名实体识别(NER)的更细粒度版本。例如,在纯 NER 设置中Jon Snow可能总是有标签character,而对于槽填充,标签将基于他应该占据的槽而改变。注释的格式称为 IOB ,代表内-外开始。这意味着一起显示令牌的“块”。

由于机器人的响应将取决于插槽和用户的目标,许多论文集中在联合插槽填充和意图检测。此外,许多 NLU 库如 Rasa-NLU 框架提供了联合 SLU 意图检测。

一旦填充了插槽,我们仍然需要构建实际的查询。查询构造将取决于数据库的设置方式。因此,在大多数情况下,这些代码是您自己手工编写的。然而,有一些模型学习话语到 SQL 查询的直接映射。但是大多数时候,您会有一个现有的 API 或者想要构建一个。因此,让我们看看如何将它变成一个简单的 API 请求:

def process_user_text(user_text, model):
    # Let's assume model.predict() returns 
    # intent:str ents:dict (e.g. {"focus_character":"Jon Snow"})
    intent, ents = model.predict(user_text)
    # Assume this function combines multi-token ents an
    # normalizes them to how they appear in the database
    ents = combine_normalize_ents(ents) 
    url = "https://random_site.com/" + intent
    requests.post(url, data=ents)

Note although this code resembles the code in the GOT-Bot APIs I have not personally tested this code. I plan on doing so in the next few days. But if you run into errors in the interim let me know.

现在我们可以使用这个简单的 Flask API 来处理这些请求。

回到我们之前的新闻示例,我们将使用以下格式标记数据,以便与 API 一起工作:

News about Jon          Snow          in    Season       8
0     0    B-character  I-character    0    B-season  I-season
Intent: News 1

正如您所看到的,这种格式允许我们更容易地构造 API 调用和 SQL 查询。现在我们可以定义一个函数(假设我们已经运行了一个 NER 并标记了新闻故事)。

有限数据场景

这种方法的问题,当然还有一般深度学习的问题,是需要大量带标签的训练数据。我目前正在研究的一种方法是在许多带注释的对话数据集上使用元学习,以便使模型能够快速适应少数几个例子。

插槽对齐是另一种有趣的(尽管有些局限)方法。面向域缩放的零镜头帧语义解析,谷歌研究人员 2017 年的一篇文章描述了在 API 中使用槽的名称和/或槽的文档来有效地执行零镜头填充。其想法是,如果模型已经接受了预订航空公司的培训,那么它也应该能够预订公共汽车,因为插槽通常应该重叠(即,两者都有一个start_citydestination_city)。更进一步,基于餐馆的对话系统可能具有restaurant_city (即,在芝加哥为我预订一家餐馆),而酒店可能具有hotel_city。通过利用短语之间的相似语义,一个模型可以学习有效地填充restaurant_city,即使它只在机票预订数据上受过训练。当然,这种方法也有局限性:(1)它不能在几乎没有重叠的完全不同的领域上工作;(2)在某些情况下,实际上可能存在负迁移(例如,在出租车预订上表现更差;它混淆了drop_offpickup_spot,因为它们是依赖于上下文的;即使这些可以与start_citydestination_city对齐,它们的表示也不相似)。对于我的用例,这种方法可能不会工作,因为在大型公共槽填充数据集和我的聊天机器人之间很少有重叠的语义槽。

超越关节槽填充和意图检测模型

但是,即使是联合 NLU 模型也有其局限性,因为它们不使用上下文。例如,假设用户说Quote from Robert Baratheon,然后说Get me another quote from him.,在这种情况下,之前描述的 NLU 模型之一不知道该做什么,因为它不使用对话历史。类似地,用户可能会问这个问题Who is Jon Snow's mother?,机器人将(希望)返回Lyanna Stark,那么如果用户问When did she run off with Rhaegar?,它甚至可能不会将her投给一个槽。有时,我们可能需要更新或请求关于某些插槽的附加信息。例如,如果用户要求News from the past 24 hours about Season 8?,但 API 要求指定新闻来源,机器人可能会回复From what source?,或者如果用户声明Get the scene from episode 2?,,机器人可能会回复from what season?

端到端对话模型应该能够处理这些任务。为测量该任务的进度而创建的一个挑战是对话状态跟踪。特别是挑战的第二个版本 DSTC2 ,测量了模型在需要时发布和更新 API 调用以及向用户请求额外信息的能力。在这个挑战中做得很好的第一个模型是用于目标导向对话的记忆网络。这是由脸书的研究人员在论文《学习端到端的面向目标的对话》中完成的。他们表明,记忆网络远远胜过其他机器学习方法。

最近出现了像 Mem2Seq 这样的论文,它们积极地将对话历史与知识库结合起来,并在响应生成中使用它们。具体来说,Mem2Seq 有两个部分,一个是对对话历史进行编码的内存编码器,另一个是使用编码的 dialogue/KB 生成用户响应的解码器。Mem2Seq 在 DSTC2 challenge、BABI 和车载 stanford 数据集上获得了 SOTA 结果。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

The architecture of Mem2Seq notice how both dialog history and the knowledge base are encoded that is utilized at each turn.

为 GOT-Bot 训练 Mem2Seq 需要三样东西:知识库、带注释的意图和带注释的对话历史。这使得更难适应 GOT-Bot,因为知识库需要转换成三元组,如(person,person2,relation)。

问题解答

从回答问题开始到填充位置结束之间的界限通常很模糊。在研究术语中,我们通常将 QA 视为基于非结构化文本数据的问题回答。(它也可以基于一个结构化的知识库,但是在这种情况下,填隙结束和 QA 开始的确切位置特别混乱)。在前一种情况下,这通常意味着从文本数据中搜索和提取答案,而不是计算查询数据库时要填充哪些槽。在《权力的游戏》机器人的上下文中,这意味着接受一个用户问题,在 ElasticSearch 上搜索适当的索引,然后从返回的结果中提取正确的答案。在讨论具体如何之前,我们先来看看用户可能会问的不同类型的问题:

基本上有三类问题:

(1)通过查询知识图可以回答的问题。

Who has the Hound killed?

Who is Jon Snow's father?

What is the motto of house Glover?

Who was Margeary married to?

What region is Harrenhall in?

这些问题都有已知的答案,可以在结构化知识图中找到。问题是我们需要将用户查询转换成 SQL 或 API 请求。这类似于我们需要做的槽填充。在许多情况下,我们实际上可以通过将问题作为另一个意图来表达,从而将这个问题作为一个填充问题。举个例子,

Who has the Hound               killed 
0   0    0  I-focus_character   I-attribute 
Intent kb_question`

或者在以下问题的情况下:

What region             is   Harrenhall       in?
0    I-location-region   0  I-focus_castle     0 
Intent 

然后,我们可以用类似的方式构造一个 API 请求。然而,有大量的数据集对 SQL 有疑问,所以在这种情况下,使用其中一个数据集可能是有意义的。

(2)不在知识图中但是仍然有已知答案并且可以从 MediaWiki 页面或其他 GOT 站点提取的问题。

How did the war of the five king's start?

What happened during Harrenhal tourney?

What was the war of the five kings?

How did Robert's rebellion end?

Who got the Tyrell's to support the Lannisters?

与此任务最相关的数据集/模型是像 MARCO 女士和 TriviaQA 这样的数据集。尽管许多研究人员对 SQUAD 进行评估,但在现实中,你几乎永远不会得到给你的准确的上下文段落。这使得在 MARCO 女士身上表现良好的模型非常理想,因为它们被给予一个完整的排名结果列表,并且必须从中提取正确的答案。

QuAC 数据集或上下文中的问答类似于前面提到的问答的“端到端”对话模型。它包含涉及多次对话的问题和跟进问题。像 FlowQA 这样的模型可以很好地完成对话式 QA 任务,因为它们将对话历史添加到基础模型中。

(3)问题,其中答案是主观的或推测性的,并且需要找到相似的问题或可替换地执行多跳推理。

Why did Sansa trust Joffery?

Who will survive season 8 and why?

If Robb Stark hadn't broken his marriage pack would've the Freys betrayed him?

Who will kill Cersei?

Is Jon the prince that was promised?

这些问题没有明确的答案,需要分析或推测。因此,最好的解决方案是找到已经回答过的类似问题。这可以通过刮掉的 Quora 索引来实现。但是,这里我们不会使用 QA 模型,而是使用问题相似度模型。可以使用多种方法来完成问题相似性。我目前生产的模型使用一个基本的弹性搜索,然后使用通用句子编码器 +余弦相似度对结果进行重新排序。为了收集更多的数据来提高排名,机器人目前向用户显示所有前十名的结果。然后,我们可以根据用户的选择重新训练模型。然而,这种方法有几个问题。首先,在许多情况下,最初的弹性搜索通常不会返回好的问题。第二,用户可能会返回另一个有趣的答案,而不是直接回答他们的问题。然而,这种“弱监督”意味着人们可以在以后更快地手工注释例子。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Example questions and bot returned answers. In the first panel (from the left) the correct answered is returned as (1) when it should be returned as 0 (it is likely some type of bug as that question seems very out of place). In 2/3 the correct answers are not even found by ElasticSearch but the related results

创建良好的入职流程

创建良好的入职流程对于获得用户也至关重要。你的机器人需要立即给人留下积极的印象,否则人们会离开。出于这个原因,为了建立一个良好的入职流程,我决定编写一个基于规则的对话。机器人首先用欢迎他们来到城堡的直接信息来介绍自己。在整个入职过程中,Redis 会跟踪用户的状态。在每个响应的结尾,用户的状态在 Redis 中被更新。这里我决定使用简单的字典将用户的状态映射到动作,以避免冗长的 if 语句。

入门过程旨在以有趣和友好的方式让用户熟悉机器人的基本功能。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Maester bot message to new users

手动定义规则的一个问题是,如果用户说了一些意想不到的话,甚至与您硬编码的略有不同,机器人就会失败。当我不小心让一个 bug 从我的单元测试和手动测试中溜走时,我发现了这一点。我期望用户对问题Would you like me to show you around the Citadel做出yes的回应,但是用户经常用yes thanksyes please 这样的话来回应,这是一个我没有发现的非常简单的错误。这就是为什么我建议让不同的人测试你的聊天机器人,因为你可能会不可避免地错过一些东西。

响应生成

在本文中,我没有过多讨论实际的响应生成。在很大程度上,这是通过用基本短语重新组合先前描述的元素的响应来完成的。当然,还有许多更复杂的方法来生成独特的、随时间变化的响应。然而,现在我仍然使用简单的短语来组合 API 中 NLU 调用的结果。

闲聊和无目标的互动呢?

这是一个我没有研究太多的领域,但我希望能够深入到后续的补充。本质上,这是当用户不想完成一个特定的任务,而只是想从总体上谈论《权力的游戏》的元素,并听到机器人机智/有趣的回应。

机器人的当前状态和未来改进

目前,聊天机器人仍处于公式化状态。我还没有能够对足够的训练数据进行注释,或者有效地结合元/无监督学习,以使槽填充始终如一地执行。然而,我训练的模型正在变得更好,我希望很快推出一个包含它们的更新。我也在考虑通过元学习训练 Mem2Seq 来处理整个对话过程,不过这是在更遥远的将来。

在问答方面,Quora 索引的搜索仍然很差,并且不支持查询知识库。我希望通过使用在 MARCO 女士身上预先训练的 BERT Reranker 来提高 Quora 索引的问答问题排名。我希望重写新闻系统,这样你就可以询问“关于第八季的最新消息”或“来自 Reddit 的新琼恩·雪诺迷因”最后,我添加了一些基于规则的对话流,用于更真实的聊天序列。在本系列的第二部分中,我将深入到聊天机器人更实际的方面,比如使用的平台和工具。

利用点击流数据构建协同过滤推荐系统

原文:https://towardsdatascience.com/building-a-collaborative-filtering-recommender-system-with-clickstream-data-dffc86c8c65?source=collection_archive---------3-----------------------

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Photo credit: Paxabay

如何实现一个基于先验隐式反馈的推荐算法?

推荐系统无处不在,帮助你找到一切,从书籍到浪漫约会,从酒店到餐馆。

针对各种情况有各种推荐系统,这取决于你的需求和可用的数据。

显性 vs 隐性

让我们面对现实吧,明确的反馈很难收集,因为它们需要用户的额外输入。只有当用户选择这样做时,他们才会给出明确的反馈。结果,大多数时候,人们根本不提供评分(我自己在亚马逊上对此完全有罪!).因此,收集到的显性数据量极其稀少。

另一方面,隐式数据易于大量收集,无需用户付出任何努力。目标是通过观察用户行为,将用户行为转化为间接反映意见的用户偏好。例如,一个为同一作者的许多文章添加书签的用户可能喜欢那个作者。

数据

我们今天的目标是开发一个具有隐式数据收集的推荐系统,在我们的例子中,隐式数据收集是点击流数据。

很难找到这个项目的公开可用数据。我使用的数据来自文章分享和 CI & T DeskDrop 阅读。 Deskdrop 是一个内部交流平台,允许公司员工与同事分享相关文章,并围绕他们进行协作。

该数据包含了约 73k 个用户在平台上分享的超过 3k 篇公共文章上的互动,更重要的是,它包含了丰富的隐性反馈,记录了不同的互动类型,使得推断用户对文章的兴趣程度成为可能。

我们将使用隐式库,一个隐式数据集的快速 Python 协作过滤,用于我们的矩阵分解。

数据预处理

  • 删除我们不需要的列。
  • articles_df上移除eventType == 'CONTENT REMOVED'
  • interactions_dfarticles_df合并。

implicit_rec_preprocess.py

这是让我们开始的数据集:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Table 1

这告诉我们每个人对每个内容有什么事件类型。有许多重复的记录,我们将很快删除它们。

df['eventType'].value_counts()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure 1

eventType 值为:

  • 查看:用户已经打开文章。内容站点中的页面视图可能意味着许多事情。这可能意味着用户感兴趣,或者用户只是迷路或随机点击。
  • 喜欢:用户喜欢这篇文章。
  • 书签:用户已经将文章做了书签,方便日后归还。这强烈表明用户找到了感兴趣的东西。
  • 评论已创建:用户对文章发表了评论。
  • 跟在后面:用户选择在关于文章的任何新评论上得到通知。

我们将把每个事件类型与权重或强度相关联。合理的假设是,例如,文章上的书签指示用户对该文章的兴趣比类似的更高。

event_type_strength = {
   'VIEW': 1.0,
   'LIKE': 2.0, 
   'BOOKMARK': 3.0, 
   'FOLLOW': 4.0,
   'COMMENT CREATED': 5.0,  
}df['eventStrength'] = df['eventType'].apply(lambda x: event_type_strength[x])

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Table 2

  • 删除重复的记录。
  • 将 eventStrength 与人员和内容组合在一起。
df = df.drop_duplicates()
grouped_df = df.groupby(['personId', 'contentId', 'title']).sum().reset_index()
grouped_df.sample(10)
  • 我们得到分组事件强度的最终结果。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Table 3

交替最小二乘推荐模型拟合

eventStrength 可以代表交互有多强的“置信度”,而不是表示明确的评级。一个人的事件强度越大,文章在我们的事件强度评级矩阵中的权重就越大。

  • 为了避开“负整数”警告,我必须创建数字列person_idcontent_id
  • 创建两个矩阵,一个用于拟合模型(内容-人),另一个用于推荐(人-内容)。
  • 初始化交替最小二乘(ALS)推荐模型。
  • 使用稀疏内容-人矩阵来拟合模型。
  • 我们将矩阵的类型设置为 double,以便 ALS 函数正常运行。

implicit_als_model.py

寻找相似的文章

我们要为 content_id = 450 找出前 10 篇最相似的文章,标题为“ Google 的合理使用胜利对开源有好处 ”,这篇文章似乎在谈论 Google 和开源。

  • 从我们训练好的模型中获取人物和内容向量。
  • 计算向量范数。
  • 计算相似性得分。
  • 获取前 10 个内容。
  • 创建与本文最相似的文章的内容分数元组列表。

similar_content.py

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure 2

第一条就是它自己。另外 9 篇文章是关于谷歌、开源软件、云、人工智能或其他科技公司的。我相信你会同意我的看法,它们都与第一部有些相似之处!

向人们推荐文章

下面的函数将返回根据人/内容向量选择的前 10 个推荐,这些内容是从未与任何给定的人交互的内容。

  • 从稀疏人员内容矩阵中获取互动得分。
  • 每样东西都加 1,这样没有交互作用的文章就等于 1。
  • 让文章已经互动为零。
  • 得到人物向量和所有内容向量的点积。
  • 在 0 和 1 之间调整这个推荐向量。
  • 已经互动的内容的推荐乘以零。
  • 按照最佳推荐的顺序对内容的索引进行排序。
  • 开始空列表来存储标题和分数。
  • 将标题和分数添加到列表中。
  • 获取经过培训的人员和内容向量。我们将它们转换成 csr 矩阵。
  • 为 id 为 50 的人员创建推荐。

implicit_rec_als_id_50.py

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure 3

这里我们有 person_id = 50 的前 10 个建议。它们有意义吗?让我们看看此人互动过的前 10 篇文章。

grouped_df.loc[grouped_df['person_id'] == 50].sort_values(by=['eventStrength'], ascending=False)[['title', 'person_id', 'eventStrength']].head(10)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Table 4

显然,此人对 Drupal 等开源 CMS 上的文章感兴趣,她也阅读软件开发和业务相关的文章,即“Google”、“Slack”或“Johnson Johnson”。

我们向她推荐的文章包括 Drupal for digital experience、信息技术与人类、软件开发以及关于 Google 的商业文章。

相当令人印象深刻!让我们再试一次。

我们向 person_id = 1 推荐了以下文章:

person_id = 1recommendations = recommend(person_id, sparse_person_content, person_vecs, content_vecs)print(recommendations)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure 4

以下是 person_id = 1 互动过的文章:

grouped_df.loc[grouped_df['person_id'] == 1].sort_values(by=['eventStrength'], ascending=False)[['title', 'eventStrength', 'person_id']]

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Table 5

显然,这个人只和 5 篇文章有过互动,她似乎对此兴趣不大。她互动的文章是关于学习日语和/或 android 开发的。

我们向她推荐的文章包括学习日语、android 开发和用户界面设计。酷!

评估推荐系统

以上抽查看起来都不错。但是对推荐系统最好的评价标准是该系统给最终用户和/或企业增加了多少价值,该系统是否增加了页面浏览量、点赞数、书签数、关注数和评论数。我们希望进行一些在线 A/B 测试来评估这些指标。

然而,在我们将推荐系统推向在线之前,还有其他一些单独评估推荐系统性能的通用指标。通过遵循这个教程,我们能够计算出我们的训练集中至少有一篇文章被屏蔽的每个人的 AUC 。和 AUC 最受欢迎的文章,供人们比较。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Jupyter 笔记本可以在 Github 上找到。复活节快乐!

参考资料:

[## alternatinglestsquares-隐式 0.3.8 文档

基于论文“隐式反馈的协同过滤”中描述的算法的推荐模型…

implicit.readthedocs.io](https://implicit.readthedocs.io/en/latest/als.html) [## ALS 隐式协同过滤

继续我的二进制数据协同过滤示例中的协同过滤主题,我将…

medium.com](https://medium.com/radon-dev/als-implicit-collaborative-filtering-5ed653ba39fe) [## Python 101 中的推荐系统

使用文章中的数据共享和从 CI&T 桌面阅读 Drop

www.kaggle.com](https://www.kaggle.com/gspmoreira/recommender-systems-in-python-101)

用 TensorFlow 构建协同过滤推荐系统

原文:https://towardsdatascience.com/building-a-collaborative-filtering-recommender-system-with-tensorflow-82e63d27b420?source=collection_archive---------2-----------------------

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Source: Pixabay

推荐是建立在其他用户的现有评级基础上的,这些用户与我们想要推荐的用户具有相似的评级。矩阵分解

协同过滤是一种被推荐系统广泛使用的技术,当你有足够大的用户项目数据时。它会根据相似用户的内容偏好进行推荐。

因此,协同过滤不是处理冷启动问题的合适模型,在这种情况下,协同过滤不能对尚未收集到足够信息的用户或项目做出任何推断。

但是一旦你有了相对大的用户-项目交互数据,那么协同过滤就是最广泛使用的推荐方法。我们将学习如何使用 TensorFlow 构建一个协同过滤推荐系统。

数据

我们再次使用预订交叉数据集,可以在这里找到。数据预处理步骤执行以下操作:

  • 合并用户、评级和图书数据。
  • 移除未使用的列。
  • 过滤至少有 25 个评分的书籍。
  • 过滤至少给出 20 个评级的用户。记住,协同过滤算法通常需要用户的积极参与。

recSys_preprocessing.py

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

因此,我们的最终数据集包含 5,850 本书的 3,192 个用户。并且每个用户给出了至少 20 个评级,每本书获得了至少 25 个评级。如果你没有图形处理器,这将是一个很好的大小。

协同过滤方法专注于寻找对同一本书给出相似评价的用户,从而在用户之间建立一个链接,向他们推荐被正面评价的书籍。这样,我们寻找的是用户之间的关联,而不是书籍之间的关联。因此,协同过滤仅依靠观察到的用户行为来做出推荐,不需要简档数据或内容数据。

我们的技术将基于以下观察:

  • 以类似方式对书籍进行评级的用户共享一个或多个隐藏的偏好。
  • 具有共享偏好的用户可能会以相同的方式对相同的书籍进行评级。

张量流中的过程

首先,我们将标准化评级功能。

scaler = MinMaxScaler()
combined['Book-Rating'] = combined['Book-Rating'].values.astype(float)
rating_scaled = pd.DataFrame(scaler.fit_transform(combined['Book-Rating'].values.reshape(-1,1)))
combined['Book-Rating'] = rating_scaled

然后,构建具有三个特征的用户、预订矩阵:

combined = combined.drop_duplicates(['User-ID', 'Book-Title'])
user_book_matrix = combined.pivot(index='User-ID', columns='Book-Title', values='Book-Rating')
user_book_matrix.fillna(0, inplace=True)users = user_book_matrix.index.tolist()
books = user_book_matrix.columns.tolist()user_book_matrix = user_book_matrix.as_matrix()

tf.placeholder仅在 v1 中可用,所以我必须这样做:

import tensorflow.compat.v1 as tf
tf.disable_v2_behavior()

在下面的代码脚本中

  • 我们设置了一些网络参数,比如每个隐层的维数。
  • 我们将初始化 TensorFlow 占位符。
  • 权重和偏差是随机初始化的。
  • 以下代码摘自《Python 机器学习烹饪书——第二版》

placeholder.py

现在,我们可以建立编码器和解码器模型。

encoder_decoder.py

现在,我们构建模型和预测

encoder_op = encoder(X)
decoder_op = decoder(encoder_op)y_pred = decoder_opy_true = X

在下面的代码中,我们定义损失函数和优化器,最小化平方误差,并定义评估指标。

loss = tf.losses.mean_squared_error(y_true, y_pred)
optimizer = tf.train.RMSPropOptimizer(0.03).minimize(loss)
eval_x = tf.placeholder(tf.int32, )
eval_y = tf.placeholder(tf.int32, )
pre, pre_op = tf.metrics.precision(labels=eval_x, predictions=eval_y)

因为 TensorFlow 使用计算图形进行运算,所以占位符和变量必须在拥有值之前进行初始化。因此,在下面的代码中,我们初始化变量,然后创建一个空数据框来存储结果表,这将是对每个用户的前 10 项建议。

init = tf.global_variables_initializer()
local_init = tf.local_variables_initializer()
pred_data = pd.DataFrame()

我们终于可以开始训练我们的模型了。

  • 我们把训练数据分成几批,然后把它们输入网络。
  • 我们用用户评级的向量来训练我们的模型,每个向量代表一个用户,每个列代表一本书,条目是用户给书的评级。
  • 经过几次尝试,我发现批量大小为 35 的 100 个时期的训练模型将消耗足够的内存。这意味着整个训练集将馈给我们的神经网络 100 次,每次使用 35 个用户。
  • 最后,我们必须确保删除训练集中的用户评级。也就是说,我们不能向一个用户推荐他(或她)已经评价过的书籍。

recSys_train.py

最后,让我们看看我们的模型是如何工作的。我随机选择了一个用户,看看我们应该向他(或她)推荐什么书。

top_ten_ranked.loc[top_ten_ranked['User-ID'] == 278582]

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Table 2

以上是该用户的前 10 个结果,按标准化预测评分排序。

我们来看看他(或她)评价过哪些书,按评价排序。

book_rating.loc[book_rating['User-ID'] == 278582].sort_values(by=['Book-Rating'], ascending=False)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Table 2

这位用户喜欢的书籍类型有:历史推理小说、惊悚悬疑小说、科幻小说、奇幻小说等。

这个用户的前 10 个结果是:谋杀幻想小说,神秘惊悚小说等等。

结果并不令人失望。

Jupyter 笔记本可以在 Github 上找到。星期五快乐!

参考资料:

Python 机器学习烹饪书—第二版

https://cloud . Google . com/solutions/machine-learning/recommendation-system-tensor flow-overview

为数据科学文章构建基于内容的推荐器

原文:https://towardsdatascience.com/building-a-content-based-recommender-for-data-science-articles-728e5ec7d63d?source=collection_archive---------15-----------------------

一步一步的教程

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

介绍

博客在数据科学社区中很流行,这已经不是什么秘密了。通过这种方式,这个领域反映了它在开源运动中的根源。在找到一个问题的创新解决方案后,似乎没有什么比写关于它的文章更让数据科学家喜欢的了。数据科学社区内的博客是一个双赢的局面,作者从曝光中受益,读者从获得的知识中受益。

在本教程中,我将使用主题建模来描述与数据科学相关的媒体文章的内容,然后使用主题模型输出来构建一个基于内容的推荐器。作为我的语料库,我将使用 Kaggle 数据集 Medium Articles(带内容),它包含大约 70,000 篇中型文章,这些文章被标记为数据科学、机器学习、AI 或人工智能。这是一个很好的数据集,因为除了文章的全文之外,它还包含了大量的信息:点击次数、作者、url 等。该数据集包含最近在 2018 年 10 月发布的文章。这意味着我们的推荐者不会推荐最近的帖子,但这没关系。

加载数据

首先,让我们导入我们的库,将数据集加载到 pandas 数据框中,然后查看前几行。

import numpy as np
import pandas as pd
import re
import stringfrom sklearn.decomposition import NMF
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.decomposition import TruncatedSVDimport gensim
from gensim.parsing.preprocessing import STOPWORDS
from gensim import corpora, models
from gensim.utils import simple_preprocessfrom nltk.stem.porter import PorterStemmermedium = pd.read_csv(‘Medium_AggregatedData.csv’)
medium.head()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

看起来未经处理的数据集包含大量冗余信息。事实上,分配给一篇文章的每个标签都有一行,所以每篇文章最多有 5 行。让我们通过压缩标记信息然后消除重复行来解决这个问题。为了进一步减少我们数据集的大小,并确保我们产生高质量的推荐,让我们也删除不是用英语写的文章和少于 25 次鼓掌的文章。最后,我们将删除所有不会继续使用的列。

# Filter articles
medium = medium[medium['language'] == 'en']
medium = medium[medium['totalClapCount'] >= 25]def findTags(title):
    '''
    Function extracts tags for an input title
    '''
    rows = medium[medium['title'] == title]
    tags = list(rows['tag_name'].values)
    return tags# Get all the titles
titles = medium['title'].unique()tag_dict = {'title': [], 'tags': []} # Dictionary to store tagsfor title in titles:
    tag_dict['title'].append(title)
    tag_dict['tags'].append(findTags(title))tag_df = pd.DataFrame(tag_dict)  # Dictionary to data frame# Now that tag data is extracted the duplicate rows can be dropped
medium = medium.drop_duplicates(subset = 'title', keep = 'first')def addTags(title):
    '''
    Adds tags back into medium data frame as a list
    '''
    try:
        tags = list(tag_df[tag_df['title'] == title]['tags'])[0]
    except:
        # If there's an error assume no tags
        tags = np.NaN
    return tags# Apply addTags
medium['allTags'] = medium['title'].apply(addTags)# Keep only the columns we're interested in for this project
keep_cols = ['title', 'url', 'allTags', 'readingTime',
             'author', 'text']
medium = medium[keep_cols]# Drop row with null title
null_title = medium[medium['title'].isna()].index
medium.drop(index = null_title, inplace = True)medium.reset_index(drop = True, inplace = True)print(medium.shape)
medium.head()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

太好了!数据集现在已经缩减到只有 24,576 行,标记信息保留在“allTags”列中。这将更容易向前推进。

文本清理

现在让我们把注意力转移到预处理文章文本,为主题建模做准备。首先,我们将删除链接、非字母数字字符和标点符号。我们还会将所有字符转换成小写字母。

def clean_text(text):
    '''
    Eliminates links, non alphanumerics, and punctuation.
    Returns lower case text.
    '''

    # Remove links
    text = re.sub('(?:(?:https?|ftp):\/\/)?[\w/\-?=%.]+
                  \.[\w/\-?=%.]+','', text)
    # Remove non-alphanumerics
    text = re.sub('\w*\d\w*', ' ', text)
    # Remove punctuation and lowercase
    text = re.sub('[%s]' % re.escape(string.punctuation),
                  ' ', text.lower())
    # Remove newline characters
    text = text.replace('\n', ' ')

    return textmedium['text'] = medium['text'].apply(clean_text)

预处理管道中的下一步是消除停用词,这些词非常常见并且没有信息。对于许多 NLP 任务来说,这些单词是混淆我们试图寻找的任何信号的噪音。标准英语停用词的几个例子是“the”、“is”和“you”。此外,考虑特定领域的停用词通常也很重要。对于这个项目,我们将从 Gensim 的预定义停用词集开始,然后添加数据科学专用停用词和一些由我们的预处理步骤生成的词片段。

stop_list = STOPWORDS.union(set(['data', 'ai', 'learning', 'time', 'machine', 'like', 'use', 'new', 'intelligence', 'need', "it's", 'way', 'artificial', 'based', 'want', 'know', 'learn', "don't", 'things', 'lot', "let's", 'model', 'input', 'output', 'train', 'training', 'trained', 'it', 'we', 'don', 'you', 'ce', 'hasn', 'sa', 'do', 'som', 'can']))# Remove stopwords
def remove_stopwords(text):
    clean_text = []
    for word in text.split(' '):
        if word not in stop_list and (len(word) > 2):
            clean_text.append(word)
    return ' '.join(clean_text)medium['text'] = medium['text'].apply(remove_stopwords)

在你的语料库上运行单词计数(在移除标准停用词之后)可以快速识别一些更明显的领域特定停用词,但是通常这些停用词列表需要通过反复试验来精炼。

作为最后的预处理步骤,我们将对文档应用词干分析器,将各种单词时态和词尾变化转换成标准化的词干。这将产生一些词干,看起来像是被屠宰了的(例如,image → imag 和 busy→busi),但是人们通常很容易识别出真正的词根。

# Apply stemmer to processedText
stemmer = PorterStemmer()def stem_text(text):
    word_list = []
    for word in text.split(' '):
        word_list.append(stemmer.stem(word))
    return ' '.join(word_list)medium['text'] = medium['text'].apply(stem_text)

我们几乎准备好继续主题建模了,但是让我们首先将当前的数据框保存到一个 csv 文件中。

medium.to_csv('pre-processed.csv')

主题建模

预处理完成后,我们终于可以享受主题建模的乐趣了。主题建模的想法是将我们的文档转换成稀疏的词向量,然后应用降维技术来找到有意义的词分组。为此,我们将使用不同的方法构建多个模型,并比较结果。我们将寻找能产生最清晰、最有凝聚力、最具差异化主题的模型。这是无监督学习的领域,对结果的评估是主观的,需要良好的人工判断。

构建主题模型的第一步是将我们的文档转换成单词向量。有两种常用的方法,BOW(单词袋)和 TFIDF(术语频率,逆文档频率)。BOW 只是统计一个单词在文档中出现的次数。如果单词“president”在一个文档中出现 5 次,那么它将被翻译成该文档的稀疏单词向量的适当位置中的数字 5。

另一方面,TFIDF 基于这样的假设运行,即出现在每个文档中的单词对于任何一个单独的文档都不太重要。例如,考虑与 2020 年总统选举相关的文档集。显然,“president”这个词会出现在几乎每一篇关于这个主题的文章中,并且“president”不是一个特别有用的词来分析这个上下文中的任何单个文档。更多关于 TFIDF 的信息可以在这里找到

为了简洁起见,我将只关注 TFIDF 主题模型的实现,除了 LDA 算法只适用于 BOW 的情况。根据我的经验,TFIDF 通常在提取清晰、有凝聚力和有区别的主题方面做得更好。首先,让我们将文档集转换为 TFIDF 稀疏向量表示,并将 SVD(单值分解)应用于稀疏集矩阵。

vectorizer = TfidfVectorizer(stop_words = stop_list,
                             ngram_range = (1,1))
doc_word = vectorizer.fit_transform(medium['text'])svd = TruncatedSVD(8)
docs_svd = svd.fit_transform(doc_word)

这将从我们的语料库中提取 8 个主题(根据我的判断,8 是这个语料库的最佳主题数,但可以尝试使用不同的数量),并将我们的文档转换为 8 维向量,这些向量表示该文档中每个主题的存在。现在让我们编写一个函数来打印出每个主题中最突出的单词,这样我们就可以评估 SVD 算法的执行情况。

def display_topics(model, feature_names, no_top_words, no_top_topics, topic_names=None):
    count = 0
    for ix, topic in enumerate(model.components_):
        if count == no_top_topics:
            break
        if not topic_names or not topic_names[ix]:
            print("\nTopic ", (ix + 1))
        else:
            print("\nTopic: '",topic_names[ix],"'")
        print(", ".join([feature_names[i]
                        for i in topic.argsort()[:-no_top_words - 
                                                 1:-1]]))
        count += 1display_topics(svd, vectorizer.get_feature_names(), 15, 8)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

还不错,但是让我们看看我们是否能做得更好。下一个要尝试的算法是 NMF(非负矩阵分解)。这种算法与奇异值分解非常相似。有时会产生更好的结果,有时会更糟。现在让我们自己来看看。

nmf = NMF(8)
docs_nmf = nmf.fit_transform(doc_word)display_topics(nmf, vectorizer.get_feature_names(), 15, 8)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这看起来很不错。在我看来,这些主题比用 SVD 产生的主题更好区分。

最后,让我们试试 LDA(潜在狄利克雷分配)。这种算法最近在主题建模中非常流行,被许多人认为是最先进的。也就是说,评估仍然是高度主观的,不能保证结果比奇异值分解或 NMF 更好。有关算法如何工作的更多细节,请参见本文这里。为了实现 LDA,我们将使用 Gensim 库,这意味着我们的代码看起来会有点不同。

tokenized_docs = medium['text'].apply(simple_preprocess)
dictionary = gensim.corpora.Dictionary(tokenized_docs)
dictionary.filter_extremes(no_below=15, no_above=0.5, keep_n=100000)
corpus = [dictionary.doc2bow(doc) for doc in tokenized_docs]# Workers = 4 activates all four cores of my CPU, 
lda = models.LdaMulticore(corpus=corpus, num_topics=8, 
                          id2word=dictionary, passes=10,
                          workers = 4)lda.print_topics()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这些题目都挺好的。也就是说,我认为从 NMF 那里获得的数据稍微有些不同。对于我们的基于内容的推荐器,主题之间的区别是至关重要的。这使得推荐者能够将一篇文章与用户的口味相匹配。考虑到上述情况,让我们继续使用 NMF 的话题。

接下来,让我们命名我们的 NMF 主题,并将文档主题向量连接回包含文章元数据其余部分的数据框架。然后,我们将数据帧保存到它自己的 csv 文件中,以便以后访问。

# Define column names for dataframe
column_names = ['title', 'url', 'allTags', 'readingTime', 'author', 
                'Tech', 'Modeling', 'Chatbots', 'Deep Learning', 
                'Coding', 'Business', 'Careers', 'NLP', 'sum']# Create topic sum for each article
# Later remove all articles with sum 0
topic_sum = pd.DataFrame(np.sum(docs_nmf, axis = 1))# Turn our docs_nmf array into a data frame
doc_topic_df = pd.DataFrame(data = docs_nmf)# Merge all of our article metadata and name columns
doc_topic_df = pd.concat([medium[['title', 'url', 'allTags', 
                         'readingTime', 'author']], doc_topic_df,
                         topic_sum], axis = 1)doc_topic_df.columns = column_names# Remove articles with topic sum = 0, then drop sum column
doc_topic_df = doc_topic_df[doc_topic_df['sum'] != 0]doc_topic_df.drop(columns = 'sum', inplace = True)# Reset index then save
doc_topic_df.reset_index(drop = True, inplace = True)
doc_topic_df.to_csv('tfidf_nmf_8topics.csv', index = False)

构建推荐引擎

最后,是时候构建我们推荐器的后端了。作为输入,推荐器将采用主题的分布;然后,它会找到一篇与该分布非常匹配的文章。为了多样化,引入一点随机性也是个不错的主意。这将允许系统从更大数量的文章中进行选择,同时仍然产生质量建议。

在实践中,计算我们的输入分布和任何文章之间的相似性的一个简单方法是使用余弦距离。当两个向量指向同一方向时,余弦距离最大,并且与向量的比例无关。后一个属性非常好,因为它允许我们忽略向量缩放,这对于欧几里德距离来说是不正确的。

至于随机性,这可以通过在输入中添加一个随机的 8 维向量来实现。为了稳定随机性的大小,这个随机向量应该与用户输入向量的距离成比例。

最后要考虑的一件事。使用 for 循环来计算我们的输入和每个可能的输出之间的余弦距离会非常慢。显然我们不能让用户等待 30 秒的推荐。解决方案是矢量化,或者换句话说,使用线性代数进行并行计算。我们将在 Numpy 中使用矩阵和向量运算来实现这一点。这将使我们的代码运行速度提高几个数量级,并几乎立即产生建议。让我们看看这一切是如何工作的。

topic_names = ['Tech', 'Modeling', 'Chatbots', 'Deep Learning', 
               'Coding', 'Business', 'Careers', 'NLP']
topic_array = np.array(doc_topic_df[topic_names])
norms = np.linalg.norm(topic_array, axis = 1)def compute_dists(top_vec, topic_array):
    '''
    Returns cosine distances for top_vec compared to every article
    '''
    dots = np.matmul(topic_array, top_vec)
    input_norm = np.linalg.norm(top_vec)
    co_dists = dots / (input_norm * norms)
    return co_distsdef produce_rec(top_vec, topic_array, doc_topic_df, rand = 15):
    '''
    Produces a recommendation based on cosine distance.
    rand controls magnitude of randomness.
    '''
    top_vec = top_vec + np.random.rand(8,)/
                        (np.linalg.norm(top_vec)) * rand
    co_dists = compute_dists(top_vec, topic_array)
    return doc_topic_df.loc[np.argmax(co_dists)]

让我们创建一些样本用户输入,看看会有什么结果。

tech = 5
modeling = 5
chatbots = 0
deep = 0
coding = 0
business = 5
careers = 0
nlp = 0top_vec = np.array([tech, modeling, chatbots, deep, coding, business, careers, nlp])rec = produce_rec(top_vec, topic_array, doc_topic_df)
rec

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

呜!成功了。我们的推荐者基于我们的输入产生了一篇有趣的文章,我们也获得了一大堆相关的元数据。此时,我们可以将工作移交给前端工程团队。如果前端是你的东西,你可以继续自己建立一个。使用 python flask 应用程序的一个例子是这里的(警告:这个应用程序在移动设备上是一个巨大的集群。当我有空的时候,我会解决这个问题。从不)

结论

最后,我们讨论了文本预处理、主题建模,以及使用我们的主题构建推荐引擎。还真不少!如果你已经做到了这一步,祝贺并感谢你的坚持。我希望这是信息丰富和有用的。

祝您的数据科学之旅好运!

这个项目的笔记本托管在 Github 这里

linkedIn 上与我联系。

查看我的网站datascienceodyssey.com了解更多内容。

为西雅图的酒店建立基于内容的推荐系统

原文:https://towardsdatascience.com/building-a-content-based-recommender-system-for-hotels-in-seattle-d724f0a32070?source=collection_archive---------3-----------------------

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Photo Credit: Pixabay

如何使用酒店描述来推荐相似的酒店?

对于推荐系统来说,冷启动问题是一个众所周知且经过充分研究的问题,其中系统不能向用户推荐项目。由于三种不同的情况,即新用户、新产品和新网站。

基于内容的过滤就是解决这个问题的方法。我们的系统在创建推荐时首先使用新产品的元数据,而在一定时间内访问者的行为是次要的。并且我们的系统基于产品的类别和描述向用户推荐产品。

基于内容的推荐系统可以用于从推荐网页、新闻文章、餐馆、电视节目和酒店的各种领域。基于内容的过滤的优点是它没有冷启动问题。如果你刚开始做一个新网站,或者有什么新产品可以马上推荐。

假设我们正在创办一家新的在线旅行社(OTA ),我们已经签约了数千家愿意在我们的平台上销售的酒店,我们开始看到来自我们网站用户的流量,但我们没有任何用户历史记录,因此,我们将建立一个基于内容的推荐系统来分析酒店描述,以确定用户特别感兴趣的酒店。

我们希望根据用户已经预订或使用余弦相似度查看的酒店来推荐酒店。我们会推荐与用户之前预订、查看或表示出兴趣的酒店最相似的酒店。我们的推荐系统高度依赖于定义一个合适的相似性度量。最后,我们选择酒店的子集显示给用户,或者确定酒店的显示顺序。

数据

很难找到公开可用的酒店描述数据,因此,我自己从西雅图地区 150 多家酒店的主页上收集了这些数据,其中包括市中心的商务酒店、精品酒店和住宿加早餐酒店、机场商务酒店、大学附近的客栈、偏远地区的汽车旅馆等等。数据可以在这里找到。

import pandas as pd
import numpy as np
from nltk.corpus import stopwords
from sklearn.metrics.pairwise import linear_kernel
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.decomposition import LatentDirichletAllocation
import re
import random
import plotly.graph_objs as go
import plotly.plotly as py
import cufflinks
pd.options.display.max_columns = 30
from IPython.core.interactiveshell import InteractiveShell
import plotly.figure_factory as ff
InteractiveShell.ast_node_interactivity = 'all'
from plotly.offline import iplot
cufflinks.go_offline()
cufflinks.set_config_file(world_readable=True, theme='solar')df = pd.read_csv('Seattle_Hotels.csv', encoding="latin-1")
df.head()
print('We have ', len(df), 'hotels in the data')

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Table 1

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

看一些酒店名称和描述对。

print_description.py

print_description(10)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure 1

print_description(100)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure 2

电子设计自动化(Electronic Design Automation)

去除停用词之前的令牌(词汇)频率分布

unigram_distribution.py

Figure 3

去除停用词后的标记(词汇)频率分布

unigram_distribution_stopwords_removed.py

Figure 4

去除停用词前的二元模型频率分布

bigrams_distribution.py

Figure 5

去除停用词后的二元模型频率分布

bigrams_distribution_stopwords_removed.py

Figure 6

去除停用词前的三元模型频率分布

trigrams_distribution.py

Figure 7

去除停用词后的三元模型频率分布

trigrams_distribution_stopwords_removed.py

Figure 8

每个人都知道西雅图的派克市场,它不仅仅是一个公共农贸市场。这是一个充满活力的历史旅游景点,由数百名农民、手工艺者和小企业组成。酒店行业因位置而繁荣,游客寻找可能离市中心和/或城市必游景点最近的酒店。所以,每个酒店如果离酒店不太远,都会吹嘘一番。

酒店描述字数分布

df['word_count'] = df['desc'].apply(lambda x: len(str(x).split()))
desc_lengths = list(df['word_count'])print("Number of descriptions:",len(desc_lengths),
      "\nAverage word count", np.average(desc_lengths),
      "\nMinimum word count", min(desc_lengths),
      "\nMaximum word count", max(desc_lengths))

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

word_count_distribution.py

Figure 9

许多酒店充分利用描述,知道如何利用吸引人的描述来吸引游客的情绪,从而推动直接预订。他们的描述可能比其他人更长。

文本预处理

测试很简单,我们没什么可做的,但以防万一。

description_preprocessing.py

建模

  • 为每家酒店创建一个包含一元、二元和三元模型的 TF-IDF 矩阵。
  • 使用 sklearn 的 linear_kernel 计算所有酒店之间的相似度(相当于我们案例中的余弦相似度)。
  • 定义一个函数,将酒店名称作为输入,并返回推荐的前 10 家酒店。

hotel_rec_model.py

推荐

大家来做一些推荐吧!

recommendations('Hilton Seattle Airport & Conference Center')

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

对我们的相似性是否起作用的一个好的测试是,当机场酒店是种子时,基于内容的推荐器返回所有机场酒店。

我们也可以问问谷歌。以下是谷歌为“西雅图希尔顿机场和会议中心”推荐的:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure 10

Google 推荐的四分之三也是我们推荐的。

以下是 tripadvisor 为“西雅图希尔顿机场&会议中心”推荐的:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure 11

也不错。

试试住宿加早餐。

recommendations("The Bacon Mansion Bed and Breakfast")

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

以下是谷歌推荐的《培根公馆民宿》:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure 12

酷!

以下是 tripadvisor 推荐的《培根公馆民宿》,我印象不深。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure 13

Jupyter 笔记本可以在 Github 上找到,如果你喜欢,这是一个 nbviewer 版本

祝你一周工作顺利!

使用 Rasa 和 Python 为 Slack 构建对话聊天机器人——第 1 部分

原文:https://towardsdatascience.com/building-a-conversational-chatbot-for-slack-using-rasa-and-python-part-1-bca5cc75d32f?source=collection_archive---------4-----------------------

使用 Rasa 堆栈和 Python 创建聊天机器人指南。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Source

注意:这篇文章是很久以前写的,RASA 可能会有变化,但这些变化并没有纳入本文。建议访问他们的官方文档网站,了解最新的详细信息。

现在是时候超越“对不起,我没有得到那个”类型的机器人,并使用机器学习建立人工智能助手:Rasa

对话式人工智能系统正在成为人类生态系统不可或缺的一部分。对话式人工智能的知名例子包括苹果的 Siri、亚马逊的 Alexa 和微软的 Cortana。对话聊天机器人已经从基于规则的前辈那里走了很长一段路,如今几乎每个科技公司都雇佣了一个或多个健谈的助手。

对话聊天机器人理解对话的上下文,可以优雅地处理任何用户目标,并帮助尽可能好地完成它。这并不总是意味着机器人能够回答所有问题,但它可以很好地处理对话。

目标

在本文中,我们将构建一个名为“ Robo 的聊天机器人,它能够检查人们的情绪,并采取必要的行动让他们振作起来。然后我们将它部署到 Slack。这将是一个功能齐全的 Slackbot 能够监听和响应您的请求。下面的演示截图应该足以激励你建立一个自己的。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

本文来源于一篇 精彩演讲 工程主管 Tom Bocklisch @ Rasa 在柏林 Pydata 上的演讲。

要求

我们主要要求安装 Rasa 栈语言模型。语言模型将用于解析传入的文本消息并提取必要的信息。我们将使用空间语言模型。

Rasa 堆栈

Rasa Stack 是一套开源的机器学习工具,供开发者创建上下文 AI 助手和聊天机器人。它是领先的开源机器学习工具包,允许开发人员使用最少的训练数据来扩展机器人,而不仅仅是回答简单的问题。这些机器人基于在示例对话中训练的机器学习模型。它由两个框架组成:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

source

Rasa NLU :一个自然语言理解库,具有意图分类和实体提取功能。这有助于聊天机器人理解用户在说什么。

Rasa Core :基于机器学习的对话管理聊天机器人框架,根据来自 NLU 的输入、对话历史和训练数据预测下一个最佳行动**。**

Rasa 有很棒的 文档 包括一些互动的例子来轻松掌握主题。

装置

我们将使用 Jupyter 笔记本来运行代码。然而,我会推荐使用谷歌的合作实验室,因为它不需要任何设置,代码是在你的账户专用的虚拟机中执行的。唯一的问题是,虚拟机在空闲一段时间后会被回收,并且有一个由系统强制执行的最大生命周期。

主要安装

你需要一个 Rasa NLU,Rasa 核心和一个 spaCy 语言模型。

#Rasa NLU
python -m pip install rasa_nlu[spacy] ([https://rasa.com/docs/nlu/installation/](https://rasa.com/docs/nlu/installation/))#Rasa Core
python -m pip install -U rasa_core == 0.9.6
([https://rasa.com/docs/core/installation/](https://rasa.com/docs/core/installation/))#Language Modelpython -m spacy download en_core_web_md
python -m spacy link en_core_web_md en --force;

本文使用的 Rasa 核心版本不是最新的。使用最新版本会在执行代码时抛出错误,所以我不得不使用旧版本。我目前正在努力让代码在最新版本上运行。

我们将按照他们的要求安装其他依赖项。

1.使用 Rasa NLU 教机器人理解用户输入

NLU 教聊天机器人如何理解用户输入。这是一个我们希望与机器人进行对话的例子。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

为了能够实现这一点,我们将建立一个拉沙 NLU 模型,并输入用户必须准备的训练数据。然后,模型会将数据转换成由实体和意图组成的结构化格式。

1.准备 NLU 培训数据

训练数据由一个预期从机器人接收的消息列表组成。这些数据被注释了意图和实体,这是拉莎·NLU 应该学会提取的。让我们用一个例子来理解意图和实体的概念。

  • **意图:**意图描述了消息的内容。例如,对于一个天气预测机器人来说,句子:What’s the weather like tomorrow?”有一个request_weather意图。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • **实体:**通过识别句子中的结构化数据,帮助聊天机器人理解用户具体在问什么的信息。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在这里,美食和地点都是提取的实体。

以下是训练数据的摘录。你也可以添加一些拼写错误或俚语,因为这将给机器人带来口语的味道。有关全部训练数据,请参考笔记本

nlu_md = """
## intent:greet
- hey
- hello there
- hi
- hello there
## intent:goodbye
- cu
- good by
- cee you later
## intent:mood_unhappy
- my day was horrible
- I am sad
- I don't feel very well
- I am disappointed
%store nlu_md > nlu.md

训练数据将被写入 *nlu.md* 文件,并保存在与笔记本相同的目录下。训练数据通常存储在减价文件中。

2.定义 NLU 模型配置

Rasa NLU 有许多不同的组成部分,它们共同构成了一个管道。一旦训练数据准备好了,我们就可以把它输入 NLU 模型管道。在管道中列出的所有组件将被一个接一个地训练。你可以在这里阅读更多关于管道的信息。

该文件包含将在 nlu 模型中使用的配置。配置文件对于模型训练很重要,因为它将提供相当多的重要参数,这些参数将在训练模型时使用。

3.训练 NLU 模型。

是时候训练我们的模型识别用户输入了,这样当你向你的机器人发送“你好”这样的消息时,它会识别这是一个greet意图,当你发送“再见”时,它会识别这是一个goodbye意图。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

训练好的模型文件将存储在路径:‘./models/nlu/current’.

4。评估 NLU 模型

是时候检验我们的模型表现如何了。让我们传递一些随机消息。

# small helper to make dict dumps a bit prettier
def pprint(o):
   print(json.dumps(o, indent=2))pprint(interpreter.parse("I am unhappy"))

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们的模型表现良好。现在让我们在一个测试数据集上评估它。然而,出于我们的目的,让我们根据手头的数据对其进行评估,即nlu.md

**from** **rasa_nlu.evaluate** **import** run_evaluationrun_evaluation("nlu.md", model_directory)

我们得到一个带有各种评估结果的意图混淆矩阵。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们已经成功地创造了一个基本的机器人,它只能理解自然语言,但没有对话。是时候给我们的机器人添加对话功能了。

2.教导机器人使用 Rasa 核心进行响应

我们的机器人现在能够理解用户在说什么,即我们的心情是怎样的,是快乐还是悲伤。现在,下一个任务是让机器人响应消息。在我们的例子中,将根据用户的选择获取一只狗、猫或鸟的图像来让它们高兴起来。我们将通过使用 Rasa Core 训练一个对话管理模型来教’robo’做出回应。

1.写故事

对话管理模型的训练数据称为stories.故事由用户和机器人之间发生的实际对话组成。用户的输入被表示为意图以及相应的实体,聊天机器人的响应被表示为动作。

让我们看看一个典型的故事是什么样的。这只是摘录,完整数据请参考笔记本

stories_md = """## happy path               
* greet              
  - utter_greet
* mood_great              
  - utter_happy
* mood_affirm
  - utter_happy
* mood_affirm
  - utter_goodbye

## sad path          
* greet
  - utter_greet             
* mood_unhappy
  - utter_ask_picture
* inform{"animal":"dog"}  
  - action_retrieve_image
  - utter_did_that_help
* mood_affirm
  - utter_happy
"""%store stories_md > stories.md

典型故事的格式如下:

## 表示一个故事的开始,你可以给它起一个名字,比如happy pathsad path等等。

*表示用户以意图的形式发出的信息。

- 表示机器人采取的行动。

2.定义域

这个领域就像一个机器人生活和运作的宇宙。这包括它应该期望得到什么用户输入,它应该能够预测什么动作,如何响应以及存储什么信息。该域由五个关键部分组成,分别是intentsslotsentitiesactionstemplates。我们意识到了前两个,让我们来理解其他的。

  • slots : slots 就像是值的占位符,使机器人能够跟踪对话。
  • 动作:我们的机器人会说或做的事情。
  • 模板:机器人要说的东西的模板字符串

我们以生命的形式定义领域。这是我们的机器人的一个示例域:

3.自定义操作

因为我们希望我们的机器人进行 API 调用来检索狗、猫或鸟的照片,这取决于用户指定的照片,所以我们需要创建一个自定义操作。机器人将通过检索插槽group的值来知道应该接收哪种类型的图片。

4.训练对话模型

最后,我们将引用应该用于训练对话管理模型的政策来训练对话管理模型。对于我们的例子,我们将在 Keras 中实现一个神经网络,它可以预测下一步要采取的行动。

该模型的主要组件是一个递归神经网络(LSTM),它从原始对话历史直接映射到系统动作的分布。

有时你想回到一个后备的行动,比如说“对不起,我不明白”。为此,将FallbackPolicy添加到您的策略集合中。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

The fitted keras policy model

模型将保存在路径:‘models/dialogue’

4.聊天时间到了

是时候和我们的机器人聊天了。执行下面的代码并开始聊天。

import warnings
warnings.simplefilter('ignore', ruamel.yaml.error.UnsafeLoaderWarning)from rasa_core.agent import Agent
from rasa_core.interpreter import NaturalLanguageInterpreter
interpreter = NaturalLanguageInterpreter.create(model_directory)
agent = Agent.load('models/dialogue', interpreter=interpreter)print("Your bot is ready to talk! Type your messages here or send 'stop'")
while True:
    a = input()
    if a == 'stop':
        break
    responses = agent.handle_text(a)
    for response in responses:
        print(response["text"])

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

《恋恋笔记本》

你既可以从 Github 访问笔记本,也可以看看下面:

结论

我们的第一部到此结束。我们创建了一个聊天机器人,它能够监听用户的输入并根据上下文做出响应。在第二部分中,我们将在 Slack 上部署这个机器人。

我们利用 NLU Rasa 和 Rasa Core 的能力,用最少的训练数据创建了一个机器人。在下一部分中,我们将利用创建的模型在 slack 上部署 bot。Rasa 让用户可以很容易地试验聊天机器人,并轻松地创建它们。所以是时候开始用 Rasa 为你的用例创建一个机器人了。

在这里阅读第 2 部分。

使用 Rasa 和 Python 为 Slack 构建对话聊天机器人——第 2 部分

原文:https://towardsdatascience.com/building-a-conversational-chatbot-for-slack-using-rasa-and-python-part-2-ce7233f2e9e7?source=collection_archive---------2-----------------------

在 Slack 上部署 Rasa 聊天机器人的指南

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

注意:这篇文章是很久以前写的,RASA 可能会有变化,但这些变化并没有纳入本文。建议访问他们的官方文档网站,了解最新的详细信息。

“松弛度又下降了,所以同事们不得不互相交谈”。

当 slack 在 2018 年年中暂时关闭时,整个科技世界陷入了混乱和混乱。互联网上充斥着关于 slack 的讨论,Twitter 上也出现了 Slack #的流行趋势。嗯,slack 已经成为许多公司不可或缺的一部分,人们经常用它来交流和讨论事情。一些远程工作的公司一直使用 slack 进行实时通信。

我们离开的地方

这是文章的结论部分: 使用 Rasa 和 Python 为 Slack 构建对话聊天机器人——第 1 部分 **。**在第一部分中,我们详细讨论了关于 Rasa Stack :一个开源的机器学习工具包,它让开发人员可以扩展机器人,而不仅仅是回答简单的问题。然后,我们使用 Rasa 的两个模块,即 Rasa NLURasa Cor e,构建了一个功能齐全的聊天机器人,能够检查人们的情绪,并采取必要的行动让他们振作起来。这些动作包括根据用户的选择向用户展示狗、猫或鸟的图像。

目标

在本文中,我们将利用所有生成的文件在 slack 上部署 Bot。我强烈建议你在尝试第二部分之前,先阅读第一部分

Rasa 装置

  • 与前一部分不同,我们将不得不安装最新版本的 Rasa 核心。强烈建议您创建一个虚拟环境,然后继续安装
#**creating a virtual environment**
conda create --name bot python=3.6**#Activate the new environment to use it**WINDOWS: activate bot
LINUX, macOS: source activate bot#**Install latest Rasa stack**
**#Rasa NLU**
python -m pip install rasa_nlu[spacy] ([https://rasa.com/docs/nlu/installation/](https://rasa.com/docs/nlu/installation/))**#Rasa Core**
python -m pip install -U rasa_core 
([https://rasa.com/docs/core/installation/](https://rasa.com/docs/core/installation/))**#Language Model**python -m spacy download en_core_web_md
python -m spacy link en_core_web_md en --force;

存储库包含第 1 部分中生成的文件。但是,由于 Rasa API w.r.t 版本的一些变化,函数和命令也有一些变化。

设置

让我们通过创建一个 slack 应用程序来创建一个 slack 集成。让我们来看看创建一个 Slack 应用程序的过程。

  • 创建一个呆滞账户并转到https://api.slack.com/。现在选择一个现有的开发松弛工作空间(如果你有)或者创建一个新的。将其命名为 DemoWorkplace 或者任何你喜欢的名字。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

DemoWorkplace creation

  • 现在在 DemoWorkplace 中创建一个 Slack 应用,并给它命名。我们把自己的 app 叫做 Robo
  • 开始向应用程序添加特性和功能。我们将首先在 **Bots Tab**下创建一个**bot** **user**,并将其集成到应用程序中。这将使应用程序对话。由于这个机器人将被创建为用户,我们可以选择让它一直在线。保存所做的所有更改。
  • 为了确保机器人已经被集成,导航到Basic Information Tab下的 Add Features and Functionality,并确保BotsPermissions选项卡处于活动状态。我们的机器人已经集成到应用程序中。
  • 接下来,我们可以添加一些关于我们机器人的信息,包括图片,描述和背景颜色。滚动到Display Information并添加信息。
  • 保存您进行的所有更改。
  • 最后,我们需要将这个应用程序安装到我们之前定义的工作场所中。授权它,我们已经准备好我们的应用程序,并集成到工作场所。

这个过程总结在下面的 gif 中。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Adding Robo App to the DemoWorkplace

Ngrok

Ngrok 是一种多平台隧道、反向代理软件,可建立从公共端点(如互联网)到本地运行的网络服务的安全隧道。简而言之,这意味着,它可以从互联网上访问您的本地应用程序。

  • 这里下载 ngrok。确保您首先登录。
  • 通过$ unzip /path/to/ngrok.zip解压安装
  • 通过$ ./ngrok <authtoken>连接您的账户

导航到解压 ngrok 的目录,并在控制台中键入$ ngrok <authtoken> 。令牌可以从这里的访问

  • 点燃它。

首先告诉它我们想要向公共互联网公开哪个端口:./ngrok http 5004

如果一切顺利,您应该会看到以下屏幕:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这里的http://-. ngrok . io是 ngrok 的网址。

在空闲时部署机器人

  • 创建一个 Python 脚本

既然我们已经完成了所有的需求,是时候部署我们的机器人了。为此,我们将需要编写一个名为**run_app.py**的 Python 脚本,它将把我们的聊天机器人与我们上面创建的 slack 应用程序集成在一起。我们将首先为我们的 Rasa 聊天机器人创建一个 slack 连接器。我们将使用 RasaNLU 解释器直接从 python 脚本加载 NLU 模型。

我们将再次训练我们的模型,以确保一切正常运行。

培训 NLU 模型

python nlu_model.py

培训 Rasa 核心模型

我们在第 1 部分中创建的 actions 文件现在需要在单独的服务器上运行。这是 Rasa Core 最新版本的一个变化。有关更多详细信息,请阅读文档。

  • 启动自定义操作服务器
python -m rasa_core_sdk.endpoint --actions actions
  • 打开一个新的终端并训练 Rasa 核心模型
python dialogue_management_model.py
  • 通过运行**run_app.py**文件启动代理。确保在脚本中提供松弛令牌。可以如下获得松弛令牌。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 在端口 5004 上启动 ngrok 并获取您的 ngrok_url。
  • 将 URL:https:///web hooks/slack/web hook提供给 Slack 配置的“事件订阅”页面。等待验证。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 最后,**subscribe to some Workplace event**s喜欢:

当有人提到它的名字时,我们的机器人会做出反应

message_im 允许用户直接向机器人发送消息。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 请确保保存您进行的所有更改

我们谈谈吧

Ensure the custom actions server is runningEnsure ngrok is running on port 5004Navigate to Slack interface and talk to your bot

这听起来可能是一项艰巨的任务,但如果你一步一步地遵循它,你最终将能够立即创建一个名为 Robo 的工作 slackbot。你可以和你的机器人聊天,就像我一样:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

结论

这是一个相当全面的教程,但事实上,我们能够建立一个全功能的 Slackbot 使得所有的努力都是值得的。Rasa 是一个非常有用的库,你可以试验和修改它来创建一些真正有用的聊天机器人。

在此阅读第 1 部分

Github 知识库链接

构建自定义搜索相关性训练集

原文:https://towardsdatascience.com/building-a-custom-search-relevance-training-set-38a4fb90044f?source=collection_archive---------40-----------------------

使用 bing 数据和文本分类器生成搜索引擎训练/评估集

科尔·塞恩斯合著,看看我们的 Github

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Photo by Markus Winkler on Unsplash

在构建可训练的语义搜索平台 NBoost 的过程中,我意识到缺乏大规模、易于使用、带标签的数据集来评估特定领域的搜索引擎,例如生物医学信息的定制搜索。

**本文包括生成搜索查询和段落的代码,这些查询和段落被标记为与特定领域或知识领域相关。**我们通过选择微软流行的 MS Marco 数据集的子集来实现这一点。微软发布的完整标签集太大了,无法完整使用。

为健康/生物学生成的子集中的查询和相关段落的示例:

查询: 按年龄划分正常血压是多少?

相关段落:……对于 25-29 岁的成年人来说,正常的血压读数是 120/80 毫米汞柱……

用谷歌自然语言 API 标注 10k 段落

使用谷歌的语言 API ,高达 30k/月的文档分类是免费的。它可以用来将段落分为 700+个类别,还可以报告置信度得分。

您需要先 注册 Google Cloud 并认证您的客户端 **,参见。**然后运行:

为集合的其余部分创建文本分类器

我们使用 vowpal-wabbit (VW)来构建一个二进制文本分类器,它可以非常快速且免费地对集合的其余部分进行分类。请确保它已安装。(在 bash 上键入vw --help)。

定义一个从 Google NLP 类别中提取二进制标签的函数。在我们的案例中,我们使用健康/科学:

然后用它来建立一个大众培训集:

然后使用此数据训练分类器,并将其保存为 bio_model:

vw input.vw -f bio_model

分类 MSMarco

  • 下载 MsMarco 收藏+查询:wget [https://msmarco.blob.core.windows.net/msmarcoranking/collectionandqueries.tar.gz](https://msmarco.blob.core.windows.net/msmarcoranking/collectionandqueries.tar.gz)
  • 摘录:tar -xvzf collectionandqueries.tar.gz
  • 从这个 repo 构建 porter stmr cli,并确保它在您的路径中。
  • 将你提取 collectionsandqueries.tar.gz 的目录设置为:马可女士的
    export DATA_DIR=./collectionandqueries
  • 运行以下代码,生成{passage_id} {score}格式的文件preds。分数越高,越有可能与特定领域相关:

⚠️如果你把这个复制粘贴到终端中,第一个 sed 命令中的标签 可能会变成一个空格。手动删除并按下 ctrl-v 然后 tab。

为子集构建段落和查询

从 MSMarco 加载集合和查询然后对它们进行分类的代码稍微复杂一些,所以我在这里就不赘述了。

如果您想要生产该集合,克隆这个回购并运行 python 脚本:

python3 build_dataset.py --data_dir <path to collectionsandqueries dir> --out_dir <bio-collectionsandqueries>

输出文件夹应包含:

  • collection.tsv
  • qrels.dev.small.tsv
  • qrels.train.tsv
  • queries.dev.small.tsv
  • queries.train.tsv

有关这些文件格式的更多细节,请参见此处的。

生物子集的评估结果

我使用了在 NBoost repo 上提供的预先训练的搜索相关性模型来对我为健康/生物学生成的数据集进行基准测试。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在此下载数据集或按照下面的指南构建数据集。查看此处了解更多关于文件格式的细节。

查看我们的主要项目 NBoost 下载并使用 ElasticSearch 部署模型。

[1] 对 BM25 的前 50 名结果进行重新排序

原载于 2019 年 12 月 3 日https://koursaros-ai . github . io

从产品所有者的角度构建客户服务聊天机器人

原文:https://towardsdatascience.com/building-a-customer-service-chatbot-from-a-product-owners-perspective-aa59dfbb2f65?source=collection_archive---------15-----------------------

在一家大型金融机构从事聊天机器人项目 1 年半之后,我已经了解了一两件关于构建虚拟助理的事情。如果您正在考虑启动一个类似的项目或正在进行的实现,我希望这篇文章可以在某种程度上帮助您。如果你想深入了解这件事,不要犹豫,留下评论或直接联系。

首先是一些不可避免的术语,以确保我们都在同一页上:

  • 意图:用户想要做的任务(获取信息,做一个(交易)动作,报告一个问题等等)。
  • 表情:用户如何表达他的意图(句子、问题、命令等等)。也称为话语。
  • 对话状态:对话树结构中的节点,对应于聊天机器人可以提供的答案。意图基本上是在特定对话状态下启动聊天机器人会话的“快捷方式”。
  • 机器人回复:用户将从聊天机器人那里收到的实际答案。
  • NLP :自然语言处理,人工智能内部的一个学科,专注于通过使用机器学习技术来处理人类语言。

构建业务案例

在任何项目开始之前,清楚地陈述目标是很重要的。该聊天机器人定位于客户服务环境,该项目有两个目标:

  1. 了解新技术:自然语言处理、人工智能和对话式用户界面
  2. 探索聊天机器人是否有潜力取代网站常见问题,成为头号数字自助服务支持渠道

你有足够的正确数据吗?

在当今的结构化和非结构化大数据世界中,在线分析、指标、仪表盘等。重要的是专注于手头任务有用的东西。在构建客服聊天机器人时,你基本上需要一样东西:对话。

当我们开始我们的项目时,几乎没有可用的文本对话数据。绝大多数的客户互动都是电话。wav 录音存储在银行的服务器上。因此,我们必须根据 FAQ 文章浏览量和关键字搜索分析来选择我们的初始范围。回想起来,这并不是建立聊天机器人的理想起点。为什么?因为 FAQ 是包含大量信息的比较大的文章。有具体问题的人都可以在同一篇 FAQ 文章中找到答案。所以你可以认为这是一个“危险”的游戏,我们知道答案,我们需要猜测随之而来的问题。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在理想情况下,您可以访问联系中心代理和客户之间的大量聊天会话。这个对话数据集让您能够确定客户用自己的话表达的真正需求。然后你就可以开始聚集这些表达并识别意图。一旦你有了一套意图,你就可以继续构建一个对话树来构建对话状态并相应地链接意图。

如果你没有必要的会话数据,我会建议使用“机械土耳其人”原则。这基本上是在聊天机器人背后安排人力,在 POC/启动阶段提供答案。您需要一个模拟聊天机器人用户体验的对话式 UI,以及几个实时处理聊天机器人对话的客户服务代理。用户将获得顶级的用户体验,您将能够收集必要的对话数据。

意向库

因此,您现在可以访问必要的对话数据,并成功地将许多表达聚合成意图。现在最难做的事情之一是提出一个明确的、逻辑的和层次化的意图命名约定。我怎么强调这一点都不为过,因为你的整个聊天机器人团队都需要使用它,它是你的自然语言处理的核心。这是我的团队到目前为止学到的最重要的一课。

不含糊

对于任何给定的用户表达,你的 NLP 培训师应该 100%清楚它必须被归类到哪个意图下。如果有任何疑问,你就冒着多个人工智能训练者将相似的表达归类到不同意图下的风险= >这将导致意图混淆,从而降低你的整体 NLP 性能。

逻辑

随着你的聊天机器人的成长,被支持的意图的数量会大量增加。我们的聊天机器人目前有+/- 150 个意向,但我们正准备扩大到 1000–1500 个意向。为了做到这一点,你需要一个意图的逻辑结构。当人工智能训练器分析一个表达式进行意图匹配时,他(她)需要解构句子,并识别主语、动词、细节……这种逻辑应该反映在意图命名中,以便匹配可以有效地发生。

分等级

基于意图命名结构的逻辑方面,包含分层方法是一种合理的做法。这里的想法是建立在所谓的“基本意图”上,它包含非常基本的表达(非常短的句子)。用户在表达中提供的细节越多,NLP 训练器就能给出越详细的适当意图。一旦有信息丢失或解释的空间,NLP 训练者应该能够默认一个更高的意图,并让 DialogState 从那里开始对话。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

理想的团队构成

构建聊天机器人需要合适的团队来让它工作。以下角色已被证明是不可或缺的:

  • NLP 训练器:维护对话树,对话状态,bot 回复;为现有意图提供表达式;建议创造新的意图。
  • NLP 看门人:维护意图结构(CRUD)并测试意图混淆
  • 开发者:将聊天机器人整合到你的数字渠道(网站、应用、…)
  • 产品负责人:制定聊天机器人的总体战略,负责利益相关者的管理,并在团队中设定任务优先级。

UI/UX 提示和技巧

限制表达式长度

这对客服聊天机器人特别有用。人们倾向于在最初的表达中包含大量的信息。这使得聊天机器人很难理解,得到的机器人回复通常是可怕的“对不起,我不明白你的问题,请重新措辞”。为了预测这种情况,您可以限制用户可以键入的字符数。理想情况下,用户表达式应该局限于一个简洁的问题。瞄准麦克斯。100 个字符(但一定要检查您的对话数据集,并根据您的用户群调整该数字)。

和人类聊天:好的,但是不要这么快

你的聊天机器人经常会失败,让客户感到沮丧(尤其是在机器人开发的早期阶段)。当聊天机器人在翻译中迷路时,给人们提供与人类聊天的选项是一个最佳实践。但是,要确保这种“退出策略”不会太方便。如果有人可以通过一个简单的命令来寻求人类的帮助,他们会很快学会,这将成为回头客的标准。确保用户至少尝试两次来表达他们的需求,否则机器人就会过时。

给你的聊天机器人赋予个性,但不要试图成为一个人

一开始就明确表示人们正在和聊天机器人聊天。否则,当事情出错时,他们会感到困惑和恼怒。但是不要让它阻碍你创造机器人人格。这允许你(除了名字、头像之外……)选择一种“语调”,这是解释范围和联系目标受众的有效工具。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Example from Brussels Airport chatbot, BRUce

使用 NLP,但是尽可能使用按钮和媒体

尝试用 NLP 引擎捕捉用户的表情,但是在你的机器人回复中使用按钮和媒体(图片,视频)。它给用户信心和感觉,他们越来越接近他们的问题的答案。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Quick replies and visual carousels can enhance the UX when used properly

结束语💭

聊天机器人的使用目前仍处于试验阶段(2018 年至 2019 年)。有很多 NLP 提供商,所以确保你不要把自己过多地束缚在一个系统中。保持你的意图+表情目录和机器人回复的所有权。测试竞争的 NLP 提供商并比较结果。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在 AWS 上从头开始构建数据管道

原文:https://towardsdatascience.com/building-a-data-pipeline-from-scratch-on-aws-35f139420ebc?source=collection_archive---------10-----------------------

当你开始深入数据世界时,你会发现有很多方法可以选择,有很多工具可以使用。一开始可能会让你有点不知所措。

在这篇文章中,我将尝试帮助您了解如何选择合适的工具,以及如何基于我最近构建的管道,使用 AWS 堆栈在云上构建一个完全可用的数据管道。

这里讨论的管道将为所有数据阶段提供支持,从数据收集到数据分析。这里的目的是为您提供足够的信息,通过回顾我构建第一个数据管道的整个过程,以便在这篇文章结束时,您能够构建自己的架构并讨论您的选择。

我应该使用哪些工具?

让我们来解决你可能想到的第一个问题:**构建管道的合适工具是什么?**我在建矿时找到的答案是:

没有正确的工具或架构,它将永远取决于您的需求!

如果你需要处理流数据,也许 Kinesis 对你来说是一件好事,但如果你有一些预算限制,并且你不介意照顾基础设施,你可以选择 Kafka。如果你必须处理历史数据,你不会需要这些东西,但是另一方面,胶水是一个很好的朋友。换句话说,你的需求将决定什么最适合你。这里重要的是了解你的挑战,知道你的局限,以便做出正确的选择。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

挑战

等我进了公司,就出现了一个大问题:数据太孤立了。分析数据既慢又难,人们找不到做这件事的动机。挑战是:集中这些数据并在公司推动数据民主化,以便授权员工!很大的挑战,对吧?

风景怎么样?

我们当时的数据来源是多种多样的。我们必须从脸书广告 API、广告词 API、谷歌分析、谷歌表单和公司内部系统中收集一些数据。为了从这些来源收集数据,我构建了一个 Node JS 应用程序,因为 Node JS 具有异步运行的能力,并且在这种情况下收集数据时可以加快速度。

管道

为满足这些需求而提出的管道架构如下图所示,我们将讨论其中的一点改进。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

数据摄取

流水线的第一步是数据摄取。这个阶段将负责运行提取器,这些提取器将从不同的源收集数据,并将它们加载到数据湖中。

为了运行那些节点 JS 脚本,我们在 AWS 上使用了 EC2 实例,但是我推荐你做的一个很大的改进是使用 Lambda 来运行那些脚本。Lambda 是 AWS 提供的一个很棒的无服务器解决方案。通过使用 Lambda,您将不必担心维护服务器,也不必为只使用几个小时的 24 小时服务器付费。

数据存储

但是我应该在哪里加载这些数据呢?S3 是 AWS 提供的一项出色的存储服务。它具有高可用性和成本效益,是构建数据湖的完美解决方案。一旦脚本从不同的数据源提取数据,数据就被加载到 S3。

考虑如何组织数据湖是很重要的。对于这条管道,一旦我们没有科学家和分析师团队来处理这些数据,一旦我们的数据来自非常有组织的来源,我只在 S3 上创建了一个原始分区,在那里我以真实的形式(它们来自源的方式)存储数据,只在 Node JS 脚本中做了一些调整。

但是,如果您希望让数据科学家和分析师处理这些数据,我建议您创建其他分区,以便以适合每个用户的形式存储数据。您可以在这里创建三个目录,如下所示:

  • Raw:在这里,您将按照数据的真实形式存储数据,即它来自源的未经修改的形式。
  • Transformed:在转换数据后,处理可能的问题,如标准化、丢失值和诸如此类的问题,数据将被加载到这里。这些数据将对数据科学家有用。
  • 丰富:为了分析,你必须丰富数据。您可能希望创建一个适合您的业务规则的大表(OBT ),这样您就可以在一个地方获得分析师需要的所有信息。这是将存储在该层的数据。

现在你可能会问:我如何将数据从一个阶段转移到另一个阶段?而答案是:看情况!如果您的数据量很小,不超过 3008M 内存和 15 分钟的执行时间(这些是我写这篇文章时的限制,现在检查它是否仍然适用),一个好的解决方案可能是 Lambda。您可以创建转换和丰富函数,以便处理一个阶段的数据并将其加载到另一个阶段。然而,如果你的数据超过这个限制,你可能会去胶水。胶水是一个非常有用的工具。在这条管道上,我使用 Glue 对数据执行转换,但是因为我没有实现转换和丰富阶段,所以我使用它将数据直接加载到数据仓库。但是如果你需要这三个(或者更多)阶段,胶水也是一个不错的解决方案。但是,如果您需要处理大量数据,使用 EMR 集群可能是更好的解决方案。这将取决于你正在处理的数据量、你处理数据的速度以及你的花费。

数据仓库

现在,您的数据已经在您的数据湖上,经过转换和丰富,是时候将它发送到数据仓库了!我使用红移已经有一段时间了,我对它有很好的体验。这是一款性能卓越、价格合理的可靠解决方案。红移还提供了一个非常棒的资源,叫做红移光谱,它使得直接从 S3 的数据湖中查询数据成为可能。

对于我的解决方案,由于数据量不是问题,所以我将所有数据存储在红移上,从而提高了性能。但是,如果您有大量的数据,那么在红移中维护所有的历史数据会变得很昂贵,因此只存储最新的红移数据并保留 S3 的历史数据是很好的。除此之外,要记住的一件好事是将 S3 的历史数据以列格式存储,如 Parquet,因为这将大大降低使用红移光谱查询的成本。

数据可视化

如果人们无法访问数据,那么数据还有什么价值?最后一步,您需要将可视化工具集成到您的管道中。我选择使用的工具是 Metabase。

Metabase 是一个很棒的开源可视化工具。它提供了一个直观和用户友好的界面,因此没有查询、SQL 和那些东西的知识的用户将能够探索数据并创建图形和仪表板来可视化他们的结果。Metabase 还允许用户通过电子邮件和 slack 定义通知,接收通知已定义指标或分析的预定电子邮件,创建可以按公司部门分组数据的集合,创建显示分析的面板以限制对用户组的访问,等等。

包扎

在这篇文章中,我们讨论了如何使用 AWS 解决方案实现数据管道。我与你分享了我用来建立我的第一个数据管道的一些东西,以及我从中学到的一些东西。当然还有很多东西可以用来改进它,比如日志等等,但是这已经是开始的一大步了。我希望到现在为止,您已经非常清楚如何开始构建自己的管道了!

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

感谢您的阅读,如果您有任何问题或建议,请告诉我,我很乐意与您讨论:)

用 Docker Compose 构建数据科学开发环境

原文:https://towardsdatascience.com/building-a-data-science-development-environment-with-docker-compose-1407aa5147b9?source=collection_archive---------12-----------------------

学习 Docker、Docker Compose 和 Cookiecutter 用于项目管理

当你有了一个伟大的新想法时,你最不想做的事情就是把数据放在一边,腾出一个干净的工作空间。我们每个人都沉迷于在 IDE 中做“一件快速的事情”,而没有考虑到依赖管理和可再现性的最佳实践。如果我们对自己诚实,我们知道什么是“最佳实践”吗?

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Classic.

不管教程怎么说,虚拟环境并不容易。

有时候,我花在正确设置开发环境上的时间比处理数据和模型的时间还要多。

Anaconda 经常引发问题,尤其是 R 和 NLP 库(但这值得单独发布)。我也用过家酿、康达、pip、 venv 、pyenv、 virtualenv 和 virtualenvwrapper、 pyenv-virtualenv 和 pyenv-virtualenvwrapper 等工具管理包和 Python 版本。(我会再写一篇文章,详细介绍现有的各种工具,以及如何将它们应用到实际的工作流程中。)

如果您希望它们正常工作,它们要求您密切关注操作系统如何与软件、用户权限、路径变量和 shell 配置文件进行交互。

尽管有一个原始的设置,我仍然遇到了包冲突、$PATH 问题和其他各种神秘的功能故障。令人欣慰的是,我学到了很多关于 UNIX 的知识。

Docker 让您在一个隔离的环境中开发代码并与数据交互。

Docker 将项目的所有代码和依赖项打包在一个可执行的容器中,该容器可以保存和重用。一个容器映像是一个独立的软件,它包含了重建原始环境所需的一切:代码、运行时、系统工具、系统库和设置。

你可以通过 Dockerhub 分享图片;如果你写好了 docker 文件,你的项目每次都会以同样的方式在任何机器上运行。它仍然不是万无一失的,但由于它是自包含的,不良交互的机会更少,如果你搞砸了,你可以杀死容器并重启它。

Docker Compose 让你可以运行多容器映像(就像一个数据库服务器、一个 IDE 和一个 web 应用程序一起工作)。虽然可能有更好地优化容器化本身的工具,但 Docker 可以轻松快速地扩展,可以并行化应用程序,并广泛用于开发//部署和持续集成工作流。毫不奇怪,我见过的每一个工作中的数据科学家都认为 Docker 是不可或缺的。

为了学习如何使用 Docker 和 Docker Compose(并为自己做一些有用的东西),我制作了一个 cookiecutter ,用于在 Python 中为自然语言处理项目创建虚拟环境。(如果你愿意,你可以自己提取 repo并使用它来创建 Python 脚本或者使用 pandas、numpy、seaborn 和各种 NLP 库的 Jupyter 笔记本。)本质上,它是一个可定制、可编辑的项目模板,在您的计算机上创建一个目录,为 python 包编写 dockerfile、docker-compose 文件和 requirements.txt,并创建一个 Makefile,让您可以快速构建新的 Docker 映像并与之交互。(注意:这仍是一项正在进行的工作。)

在自学 Docker 创建 Python 包的过程中,您肯定会学到一些东西。这里有一个例子:

教训一:不一定要相信教程。

总是仔细检查你遇到的任何教程或资源。几乎所有时间,它们都是过时的。在线发布的快速迭代和很少甚至没有信息验证意味着在你开始实现任何代码之前,你必须从头到尾阅读。

几乎所有的代码都不适合你。有些码头文件是不好的做法;其他的语法已经过时或者无法运行;注意那些构建在 Python 或 Ubuntu 过时版本上的应用程序的讽刺之处,它们实际上应该使用不同的构建版本。(而“最新”标签的意思并不是你想的那样。)除非 docker-compose.yml 的语法精确,并且 docker 文件的组成是经过深思熟虑的,否则您无法成功地构建图像,或者获得您想要的结果。几乎可以肯定,你自己写比从要点或教程中复制粘贴要好。

仅仅因为它在互联网上并不意味着它是有效的。写文件。设置测试并测试您的代码。

第二课:你的项目需要的时间比你想象的要长。

获取高质量的信息需要时间。Docker 有很好的文档,但是它并不总是有很好的索引或者是最新的。(我在一个 GitHub 问题中的 docker-compose 文件中找到了一个 bug 的答案,其中一个 docker 开发人员回答说:“哎呀——是的,我们改变了语法,忘记更新文档了。”)

这些工具需要付出比简单的“Hello World”教程或博客文章更多的努力才能获得。一旦你越过了玩具的例子,想要使用你正在写的代码去做真正的工作,它很快就变得多面化了。为了我的项目,我必须非常熟悉计算机网络,Linux/Ubuntu,bash 脚本, GNU make ,阅读和编辑其他人的源代码,git 和 GitHub,以及 cookiecutter 包。添加 Docker 和 Docker Compose、Python 以及所有您希望包含在您的环境中的映像(例如 Redis、Flask、Django、Nvidia ),您会看到许多相互关联的部分。可能会让人不知所措。

迷失在一个复杂的话题上是令人沮丧的。很难保持简单,但是不要试图一次画出整个猫头鹰。得到一些最小的工作。我让 cookiecutter 开始工作,然后是 Makefile,然后是 Python 的 Docker 映像,然后是 docker-compose 文件,然后是所有的东西。

它仍然远非完美。我有弱点要修正,有更多的测试要写,有更好的结构要实现…等等。尽管如此,我通过自己构建这个项目的胆量学到了很多,并且我将通过一点一点地改进它来学到更多。

第三课:你不能“只做数据科学”

任何在学术界以外找工作的人都知道这一点,但对于那些没有找到工作的人来说,值得注意的是:如果你想找一份“数据”工作,你的技能需要证明不仅仅是“纯”数据科学。职称与职业甚至职责并没有清晰的对应关系,而且市场发展太快,使其成为一个安全的赌注。附有“科学家”或“工程师”的“数据科学”或“机器学习”的职位描述通常不仅仅描述善于发现、获取、清理、可视化和分析数据的候选人;他们还希望有人了解前沿的机器学习和经典统计模型、分布式计算、NoSQL 数据库、算法、高等数学、软件开发和测试,以及大量的领域知识。

你可能宁愿建立随机的森林和网络搜集酷数据,但如果你是一名求职者,花一些时间学习一些不那么性感的行业工具是值得的。了解您的同事正在使用什么,并能够谈论其利弊、优势和缺陷(更不用说实施这些工具了),会让您成为更好的合作者,从而提高工作效率,因为很少有数据专业人员会完全孤立地工作。

试一试,一步一个脚印。

下载 Docker 。拉一个简单的图片,习惯 Docker 的基本工作流程。(如果你不介意它有多初级,你可以拉我的回购——有一个自述,而且会是一个教程,当然你不能相信,TBA 很快。)使用 repo2docker 将你最喜欢的 GitHub repo 转换成一个容器,并运行它。拆开 Docker 文件,看看它是如何将基本代码转换成 Docker 映像的。

学习 Docker,让它为你工作。如果你想理解它,就去做一个你感兴趣的项目,不管是因为它的技术复杂性还是因为它的主题。对网络上的不良信息感到沮丧。构建、测试、中断、重复代码的时间比你认为需要的时间要长。然后,制作一个实用的应用程序——不像一个价值百万美元的软件,但基本上能完成工作。反思你所学的一切;相当满意。然后,在新容器的 ide 内的内置 seaborn 数据集上兜风之后,您可以考虑添加更多的功能并收紧您的测试。但也许不是今晚。

创建数据科学创业公司&进入数据科学领域

原文:https://towardsdatascience.com/building-a-data-science-startup-getting-into-data-science-2113ce2f4868?source=collection_archive---------10-----------------------

完整播客剧集:苹果 | 谷歌 | SPOTIFY | 其他

对 SharpestMinds 联合创始人杰米·哈里斯的播客采访

前几天,我为“走向数据科学”播客和 YouTube 频道采访了 SharpestMinds 的联合创始人杰里米·哈里斯(Jeremie Harris)。SharpestMinds 是一家初创公司,通过为寻找数据科学工作的人找到导师来帮助他们。

在我看来,他们的系统很有趣,因为导师只有在他们的学员获得数据科学工作时才能获得报酬。我想采访 Jeremie,因为我之前曾在不同的场合与他交谈过,我想亲自了解更多关于他的故事,以及他对当今数据科学就业市场的想法。

要想听完整集,你可以查看我们的播客或者在 YouTube 上观看采访。

问:你是如何开始创业的?

所以,我们整个团队都是前物理学家。我们都在差不多同一时间从研究生院退学或完成研究生学业。我们决定进入初创公司,特别是深度学习领域。

2015 年大概一年半的时间,我和我哥做这个深度学习创业公司。这真是一个愚蠢的想法——我们做了一个餐馆推荐引擎。事实证明,这是没有人真正想要的东西。但从某种程度上来说,我们学到了机器学习和深度学习背后的许多基础设施,这很酷。

最终,我们决定转向,我们基本上决定建立一个就业委员会。我们知道很多人想进入机器学习领域。很明显,我们正在到达事情会变得疯狂的转折点。

从那时起,我们开始尝试将公司与学生联系起来。所以,我们会试着帮助一个学生找到他们的第一个角色,做一些小项目。我们就是这样穿过 YC 的。我们用 Y Combinator 实现了这个想法。

现在,在这个模型中,我们会尝试将特定的公司与特定的候选人匹配起来。换句话说,这是一种招聘模式。这基本上就像试图成为一名招聘人员。在这种模式下,有一个问题,那就是我们实际上不能关心任何特定的学生或候选人。

相反,你在玩数字游戏。你把一大堆人和一大堆工作放在一起,希望能有所收获。所以,很快,我们意识到我们想要做的是能够投资于人。

我们总是擅长教学,善于与人交往,了解就业市场的来龙去脉。我们想向那个方向倾斜。这就是为什么我们最终决定采取收入分成的指导方向。

我们从求职公告栏和其他东西上看到,每个人都缺少同样的技能一致性。这就像——拥有一台 Jupyter 笔记本电脑,你可以输入熊猫和 scikit——学习,但你如何进入下一个级别——获得聘用所需的级别?

你需要考虑在云端部署模型、良好的 git 流程,并学习如何使用 Docker 之类的工具。正是这种软件工程方面的东西在数据科学中被低估了。这是公司倾向于寻找的。

我们不断发现这些共性,然后我们想,你知道谁会真正擅长教这个吗?在现实世界中使用数据科学的人——机器学习工程师和数据科学家。

所以,我们问自己,今天我们如何说服一个数据科学家与一个有抱负的数据科学家一起工作?

如果你仔细想想,那其实很难。数据科学家的报酬很高。所以,如果你要说服他们这么做——和一个还没准备好的人花大量时间在一起,你必须找到一种方法让一切都适应。收入份额立刻成为了自然的契合点——让那些试图在未来成功的交易中打破常规的人。

今天你可能没有价值,但是如果你付出一点努力,如果你得到指导,如果有人会告诉你应该把精力和时间放在哪里,你会突然发现所有的价值。每当你遇到这种情况时,收入分成就非常适合。这就是它的起源。

问:你为什么选择这种收入分享模式,而不是建立一个训练营?

问得好。我们实际上花了大约 2 个月的时间来思考我们是否应该进行一次训练营。这是天生的吗?我们没有这么做的原因最终是经济学和伦理学的结合。

我想退一步回答这个问题,给你讲一个过去几周发生在我身上的故事。我们开始收到很多申请,他们是大学数据科学硕士课程的助教,训练营的助教,甚至是一所知名大学的教授。

基本上所有的数据科学教育工作者都来找我们,因为他们正在努力进入这个领域。

所以,我们有点退缩,说,“这是一个很大的伦理问题。”

如果你想象人们正在进行数据科学的训练营或硕士学位,他们为什么要这样做?是因为他们想闯进去!但如果他们的助教和教授也试图闯入,这就像盲人摸象。

这就是我们从外面看到的情况。如果你是一名数据科学家,如果你有经验,你可以赚 15 万、20 万、25 万。如果你是大学教授,你会赚 10 万或 12 万。基本上是一半的量。因此,如果你足够优秀,可以成为一名数据科学家,那么问题就来了——如果你有那么高的技术,为什么你要做这个而不是数据科学?

所以对我们来说,问题是,如果你想提供高质量的数据科学教育,你如何说服那些足够优秀的人在大联盟中做这件事,足够优秀的人在奥林匹克水平上做这件事,谷歌,Facebooks,甚至 RBCs,你知道,在整个范围内,对吗?

你如何说服那些超级有价值的人投入他们的时间去指导一些更年轻、更缺乏经验的人?

那就是导师。那是一对一,收入共享。这就是为什么我们最终决定不举办训练营。

问:在过去四年中,数据科学市场发生了很大变化吗?

变化很大。事实上,也许我会回到 2015 年我鲁莽的日子,从研究生院辍学,进入这个领域。当时,我有几个朋友在 2015 年完成了转变。他们过渡的方式是他们有某种定量 STEM 的背景,数学,物理,诸如此类。

然后他们基本上就学会了两件事。他们学习了基本的 Python 和 scikit-learn,numpy,pandas。就这样。没有码头工人,没有 AWS,没有 Heroku,没有 Flask,什么都没有。那时候这就够了。

这就足够了的原因是数据科学处于蛮荒的西部阶段。因此,想象一下,所有公司都在寻找数据科学家,因为大肆宣传。我们知道有些公司会说,我们必须雇佣数据科学家,因为我们的投资者说我们应该雇佣数据科学家。这是个错误的决定。你不应该这样做,但它发生了。

因此,在 2015 年,你很可能只需要最低限度的技能就能被录用。2017 年左右,事情开始走向成熟。你开始看到人们提出越来越多的要求,因为人们开始意识到我们已经开始从谈论数据科学这个神奇的东西开始转变——它将帮助我们预测未来,做各种很酷的事情——这都是人工智能。

我们现在需要开始从中获得实际的商业价值,因为已经过去两年了。所以在那个阶段,人们开始要求更多的东西,比如,你能写干净的模块化代码吗?你能利用好的实用可视化库吗,比如 Seabourn 和 matplotlib?你仍然会看到对库的需求在不断扩大。这并不像软件工程方面已经获得了自己的地位。

现在你开始看到公司会问你,不是像你参加过什么 Kaggle 比赛,而是你懂什么软件工程,你能架设什么数据管道?所以现在更多的是关于实施。理论性要少得多,这只是因为该领域的自然进化过程,而且今天仍在变化。

当你有一个只有当有人被雇佣时你才能得到报酬的设置时,我们需要每个人都赢来使这个工作。因此,你开始真正关注跟踪就业市场的变化和需求的变化。现在事情的发展方向实际上非常有趣,现在出现了一些不同的趋势。数据科学的工作描述是一种分叉。

一方面,你几乎就像一个软件工程师,他也从事数据科学。另一个是专注于产品、精通数据的人。所以,关注产品的人会回答这样的问题,我们下一步应该做什么?我们的用户想要什么,我们如何使用数据来为这些决策提供信息?

其中很多现在正变得面向建模,你会看到更多的超参数调整,更多的预测建模,更多的无监督学习。所以这个角色有点向这两个方向转变。现在还很难说它会走向何方。

问:你对想进入数据科学的人有什么建议吗?

我认为人们会期待这个问题的技术性答案,但我认为答案是从一些非常非技术性的东西开始的。

这就是习惯的培养。比如说,在你一头扎进 MOOCs 之前,你需要意识到这一点。MOOCs 的完成率在 5%到 15%左右。因此,在 Udacity 上购买数据科学入门课程的普通人不会完成它。我们称 MOOCs 为欲望购买。基本上,它是为那些想成为完成 MOOC 的人而设计的,但他们实际上并没有这样做。就像新年决心一样。

所以,如果你试图闯入,你需要做的第一件事就是认识到你自己的冲动。它很深——它在每个人心中根深蒂固。我们所有人都开始读书,却没有读完。我们所有人都说我们会保持身材,然后在 1 月 14 日或其他时候停下来。

认识到这是现实,那么问题就变成了,你实际上如何开始培养习惯?

有一本非常著名的书叫做《原子习惯》,作者是詹姆斯·克利尔。我们的首席技术官 Russell 也非常喜欢养成习惯。SharpestMinds 基本上是习惯形成文化的重要组成部分。这是我们关注的很大一部分。

但是底线是选择你实际上可以实现的非常小的增量目标,然后给自己完成这些目标的荣誉。所以不要告诉自己你将在三个月内完成一个从零到英雄的 Python 转变。相反,告诉你自己你将会做一些非常简单直接的事情,并开始执行。

整个养成习惯的事情是一个巨大的空间,我不想在这上面纠缠太多,但它确实值得研究。

如果我要给出一个更技术性的答案,我个人认为 Jupiter 笔记本是一个很好的开始。然后,像 NumPy、pandas 和 scikit-learn 这样的库是您想要了解的关键库。

不要试图仅仅通过查找 API 或查找 scikit-learn 文档并阅读它们来学习。试着看看 Kaggle 比赛,看看博客帖子,然后复制它们。首先,你不需要知道每一件事——每一小部分在做什么。随着更多的实践和更多的经验,你会打开东西。但目标是让你的手变脏。不要羞于按 shift + enter 键,看看你的项目会产生什么结果。这是我的主要建议。

我会说有一个很棒的 MOOC 叫做 fast.ai,作者是杰瑞米·霍华德,他曾经是 Kaggle 的总裁。Kaggle 是数据科学领域的一个大型竞争网站,我认为他在很长一段时间内都是他们的顶级竞争对手之一。他创办了一家名为 Enlitic 的公司。他们是一家医疗技术公司,在计算机视觉领域很有影响力。

总之,他是一个非常非常好的解释者。Fast.ai 有一个很棒的关于深度学习和机器学习的课程。每年我都会重温一遍,以保持新鲜感。这实际上是我在理论和实现方面保持领先的主要工作之一。我建议你去确认一下。

问:你在 Y Combinator 的经历是怎样的?

太棒了。好难用一两句话概括。但我想我要说的是,YC 的变革有几个不同的原因。这是一个很棒的社区,他们提供的服务也很棒。真正奇怪的是,他们在内部给出的建议与他们在外部给出的建议完全相同。例如,如果你读了保罗·格拉厄姆的文章,或者听了他们的播客,你就在消费他们给每个人的建议。

这实际上一开始让我们很困惑,因为没有魔法。我想,像许多公司一样,我们真诚地期望,当我们进入 YC 时,我们会看到奇迹。

他们会打开潘多拉的盒子,突然间我们会说,这就是诀窍,这就是诀窍。我们在找黑客,但没有黑客。奇怪的是,这实际上是最大的攻击。

它意识到,嘿,这只是一个骗局。你只需要在某个时候去做。让 YC 的合伙人一遍又一遍地提醒你,嘿,你还记得我昨天给你的建议吗?我对那个建议很认真。我不知道你在做什么。

基本上,只是让他们告诉你,你应该执行,即使你已经知道了。这一点点额外的推动会带来很大的不同,最终你会开始内化它。我认为这是这次经历带给我们的最大收获。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

感谢你阅读这篇文章!

如果你想听完整的音频版本,可以在走向数据科学播客上听完整集。

使用 tf 构建深度图像搜索引擎。克拉斯

原文:https://towardsdatascience.com/building-a-deep-image-search-engine-using-tf-keras-6760beedbad?source=collection_archive---------17-----------------------

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Photo by Kayla Farmer on Unsplash

动机:

想象一下,有几十万到几百万张图像的数据集合,却没有描述每张图像内容的任何元数据。我们如何建立一个系统,能够找到这些图像的子集,最好地回答用户的搜索查询?我们基本上需要的是一个搜索引擎,它能够根据图像结果与搜索查询的对应程度对图像结果进行排序,搜索查询可以用自然语言或另一种查询图像来表达。
我们将在本文中解决这个问题的方法是通过训练一个深度神经模型,该模型学习任何输入图像和文本的固定长度表示(或嵌入),并使这些表示在欧几里得空间中接近,如果文本-图像或图像-图像对“相似”的话。

数据集:

我找不到一个足够大的搜索结果排名数据集,但我能够得到这个数据集:【http://jmcauley.ucsd.edu/data/amazon/】T2,它将电子商务项目图片链接到它们的标题和描述。我们将使用这些元数据作为监督源来学习有意义的联合文本-图像表示。为了管理计算和存储成本,实验仅限于时装(服装、鞋子和珠宝)和 500,000 张图像。

问题设置:

我们拥有的数据集将每张图片与用自然语言书写的描述联系起来。因此,我们定义了一个任务,在这个任务中,我们希望学习一个连接的、固定长度的图像和文本表示,以便每个图像表示都接近其描述的表示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

型号:

该模型采用 3 个输入:图像(即锚点)、图像标题+描述(正面示例),第三个输入是一些随机采样的文本(负面示例)。
然后我们定义两个子模型:

  • 图像编码器:Resnet50 在 ImageNet+GlobalMaxpooling2D 上预先训练
  • 文本编码器:GRU+GlobalMaxpooling1D

图像子模型产生锚点的嵌入 E_a ,文本子模型输出正面标题+描述的嵌入 E_p 和负面文本的嵌入 E_n

然后,我们通过优化以下三重损失进行训练:

L = max( d(E_a,E_p)-d(E_a,E_n)+alpha,0)

其中 d 是欧几里德距离,α是在该实验中等于 0.4 的超参数。

基本上这个损失允许做的就是把 d(E_a,E_p) 做小,把 d(E_a,E_n) 做大,这样每一个图像嵌入都是靠近其描述的嵌入而远离随机文本的嵌入。

可视化结果:

一旦我们学习了图像嵌入模型和文本嵌入模型,我们就可以通过使用 tsne(https://sci kit-learn . org/stable/modules/generated/sk learn . manifold . tsne . html)将它们投影到二维空间来可视化它们。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Test Images and their corresponding text description are linked by green lines

从图中我们可以看出,一般来说,在嵌入空间中,图像与其对应的描述是接近的。这正是我们所期望的训练损失。

文本图像搜索:

在这里,我们使用几个文本查询的例子在一组 70,000 张图像中搜索最佳匹配。我们为查询计算文本嵌入,然后为集合中的每个图像计算嵌入。我们最终选择嵌入空间中最接近查询的前 9 个图像。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这些例子表明,嵌入模型能够学习图像的有用表示和简单单词组合的嵌入。

图像-图像搜索:

这里我们将使用一个图像作为查询,然后在 70,000 个图像的数据库中搜索与其最相似的例子。使用欧几里德距离,通过每对图像在嵌入空间中的接近程度来确定排名。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

结果表明,所生成的嵌入是图像的高级表示,其捕捉了所表示的对象的最重要的特征,而不受方向、照明或微小的局部细节的过多影响,并且没有被明确地训练来这样做。

结论:

在这个项目中,我们致力于机器学习模块,使我们能够建立一个基于关键字和图像的搜索引擎,应用于图像集合。基本思想是为文本和图像学习一个有意义的联合嵌入函数,然后使用嵌入空间中项目之间的距离对搜索结果进行排序。

参考文献:

代码: 重现结果🔍=>https://github.com/CVxTz/image_search_engine

为机器学习和人工智能构建 DevOps 管道:评估 Sagemaker

原文:https://towardsdatascience.com/building-a-devops-pipeline-for-machine-learning-and-ai-evaluating-sagemaker-cf7fdd3632e7?source=collection_archive---------12-----------------------

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

构建和部署机器学习模型的热潮暴露了传统 DevOps 过程中的裂缝。

为部署传统应用程序而构建的基础架构没有针对大规模部署 AI/ML 的挑战进行优化。这一挑战来自数据科学家和工程师工作方式的固有差异。数据科学家工作流的迭代和实验性质,加上他们计算需求的高度可变性,使得构建 DevOps 机器学习(ML)管道(越来越多地被称为“MLOps”)变得非常困难。

MLOps 管道背后的目标是以一种仍然允许灵活实验的方式稳定和简化 ML 模型发布过程。然而,很难找到一个一致的框架来构建这个管道,因为 ML 团队实践和工具都不够成熟。这篇文章提供了一个有用的框架来评估 MLOps 管道组件,并深入探讨了这些管道组件的一些潜在解决方案,包括 AWS Sagemaker 和 Comet.ml。

目标和注意事项

将模型部署到生产环境只是 MLOps 管道的一部分。有效的 MLOps 管道还包括为持续培训、适当的版本控制、可扩展的服务基础设施以及持续的监控和警报建立数据管道。显然,这与传统的软件开发有相似之处,但是仍然有一些重要的开放性问题需要回答:

针对 DevOps 工程师

  • 我如何将它连接到现有系统?
  • 我如何使这些组件可伸缩?
  • 它会使模型部署更容易吗?
  • 它会让与数据科学家的合作变得更容易吗?

为数据科学家

  • 它是否像使用我自己的环境工具/设置一样灵活?
  • 它会使构建和训练模型变得更容易吗?
  • 它会让与其他数据科学家的合作变得更容易吗?
  • 这会让与 DevOps 的合作变得更容易吗?

这些问题可以归结为有效的 MLOps 渠道应该实现的三个主要目标:

  • 扩展基础设施
  • 灵活的团队协作
  • 再现性

不管你的团队的规模和组成如何,这些目标都很重要,但是它们的执行可能会因这些因素而有所不同。许多团队面临的问题是,是建立自己的定制环境,还是从 Amazon、Google 或 Microsoft 等云提供商那里购买托管服务。

购买与构建

对于 DevOps ML 管道,您可以考虑的一个选项是 AWS Sagemaker。AWS 宣布 Sagemaker 是“完全托管的端到端机器学习服务,使数据科学家、开发人员和机器学习专家能够快速构建、训练和托管大规模的机器学习模型。”

那么 Sagemaker 在整个管道中处于什么位置,它与定制环境有什么不同呢?

模型开发

Sagemaker 的价值归结为抽象和统一。Sagemaker 本质上是 AWS 中的一个托管 Jupyter notebook 实例,它提供了一个用于深度学习模型的简单分布式训练的 API。在引擎盖下,SageMaker 的主要组件是特定的亚马逊机器映像 (AMIs)和普通的 EC2 实例,其数据来自 S3 对象存储[

如果您自己设置了这些等价部分,那么您可以自己托管一个 EC2 实例。当您请求 EC2 实例时,您可以指定哪个 AMI 应该被用作它的模板(查看 AWS Marketplace 以获得预配置 AMI 的完整列表)。ami 从操作系统、库、应用程序等细节中捕捉环境的确切状态。对于深度学习项目,亚马逊提供了一个名为深度学习 ami 的 ami 子集,预装了开源的深度学习框架。

在开发阶段,使用 Sagemaker 与在深度学习 AMI 上运行 Jupyter 笔记本没有太大的不同,实际上可能更不灵活,因为您必须调整您的代码来显式地为训练、评估和预测模式提供 EstimatorSpec 对象,并使用支持的数据类型[ 来源 ]。

模特培训

对于分布式模型训练,Sagemaker 运行在一个完全托管的弹性计算服务器上,该服务器可以根据作业规模自动扩展。如果你使用 Sagemaker 的“优化”算法,你可以看到性能的提升,因为这些算法是专门设计来跨多个 EC2 和 GPU 实例扩展的[ Source ]。

模型部署

如果您想从 Sagemaker 外部部署一个定制模型,Sagemaker 的抽象实际上可能会成为部署过程中的一个缺点。对于定制模型,亚马逊允许你将 Docker 容器加载到弹性容器库(ECR)中进行生产,但你必须使用一个非常具体的 Docker 目录结构。对于他们的内置算法,Sagemaker 为每个算法提供了一致的训练和推理接口。【来源】

如果你要用 AWS 组件构建自己的管道来部署模型,你需要将 AWS API Gateway、AWS Lambda 和 Cloudwatch [ 来源 ]组合在一起。

对于没有专用开发运维或数据工程资源的团队来说,像 Sagemaker 这样的托管服务的额外成本可能值得在维护 EC2 实例(特别是如果您使用 Spot 实例)、更新软件包和在虚拟私有云(VPC)上进行复杂的网络配置方面节省的时间。

最终,采用像 Sagemaker 这样的托管服务可能会使基础设施的扩展更容易处理,但仍然没有解决 MLOps 管道的两个重要目标:灵活的团队协作和可复制性。

少了什么?

我们已经看到了 AWS SageMaker 如何提供方便可靠的基础设施来训练和部署机器学习模型,但我们如何才能在 DevOps ML 管道中建立可重复性?

就像适当的源代码控制使软件工程变得高效一样,机器学习中的可再现性可以防止瓶颈,即使有可靠的 DevOps 管道,这些瓶颈也会显著增加成本。当数据科学家和工程师试图:

  • 从离开公司的数据科学家那里恢复工作
  • 比较不同模型迭代的结果
  • 从合作者、研究论文甚至生产模型中复制模型结果
  • 追踪原始训练数据、相关性、超参数或实际模型,同时尝试有效地重新训练模型
  • 避免跨团队重复工作

使机器学习工作可重复并不容易,因为训练过程可能会充满大量的数据转换和分割、模型架构变化和超参数配置。在协作环境中尤其如此,在协作环境中,处理不同版本模型的数据科学家可能会对项目中的文件进行数百次更改。

Comet.ml 对机器学习的作用就像 Github 对代码的作用一样。我们允许机器学习开发者自动跟踪他们的数据集、代码更改、实验历史和模型。在成千上万的用户和多家财富 100 强公司的支持下,Comet 提供见解和数据来构建更好、更准确的模型,同时提高生产力、协作和可解释性。

通过在 Sagemaker、vanilla Jupyter 笔记本电脑或任何其他开发环境的基础上运行,Comet 可以作为生产中或仍在进行中的机器学习工作的单一真实来源。一旦数据科学家和工程师了解了要部署的有效模型的特征,更重要的是,了解了创建该模型所涉及的技术工作,围绕模型架构、超参数和优化的关键后期分析就成为可能。

连接 Comet.ml 和 Sagemaker

因为 Comet.ml 与您选择的基础设施和机器框架无关,所以您可以继续使用您当前的培训过程——无论是在 AWS Sagemaker 还是您自己的定制环境中。

如果你对测试 Comet.ml 如何与 Sagemaker 集成感兴趣,请参见这篇关于将 Comet.ml 与 AWS Sagemaker 的 Tensorflow Estimator API 集成的教程。我们将使用 Tensorflow 在 CIFAR10 数据集上调整运行 Resnet 模型。

利用自然语言处理构建假新闻分类器

原文:https://towardsdatascience.com/building-a-fake-news-classifier-using-natural-language-processing-83d911b237e1?source=collection_archive---------15-----------------------

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Photo by Tim Mossholder on Unsplash

听着,我不是来选边站的。唐纳德·特朗普(Donald Trump)只是我使用“假新闻”一词的替罪羊,但无论你是共和党人还是民主党人,是社交媒体爱好者还是森林隐士,你都必须承认,我们这个快速数字化、两极分化的世界正在遇到一个特定的问题:假新闻。无论是脸书还是所有的铁板一块的科技公司,只给我们提供我们想让听到的信息,以增加广告收入,我们正在失去对多样化信息的接触,作为消费者,我们正面临着这样的可能性:无论我们被提供什么,都可能不准确。这意义重大——根据 eMarketer 的数据,一般成年人每天花 3 小时 54 分钟在手机上。我们甚至不知道这些信息是否准确,这就需要很长时间。作为一名数据科学家,我的目标是利用信息时代的优势来催化社会变革,希望随着时间的推移,该领域的进步和志同道合的研究人员将减少负面影响。

这个项目,我自我戏称为“假新闻分类器”,将致力于使用自然语言处理来分类一个新闻是假还是不假。首先,我们来定义假新闻:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

https://www.pnas.org/content/114/48/12631. Inspired by Dawn Graham’s own iteration of this project.

正如我们在上表中看到的,假新闻在各种媒体上普遍存在。为了方便使用,我们将把重点放在对讽刺内容的分类上(令人尴尬的是,我不止一次地相信了《洋葱新闻》的文章)。

我们将从两个 Reddit subs,r/TheOnion 和 r/NotTheOnion 中抽取数据来构建我们的数据。洋葱满足了我们对讽刺新闻的要求,而不是洋葱是一个子编辑,它提供了一系列疯狂但真实的新闻。甚至经常光顾 Reddit 的人也经常把这两者搞混。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

The Onion even has to issue a warning!

从上面我们可以看到,r/TheOnion 有大约 100,000 名读者,而 r/NotTheOnion 有超过 1,500 万名读者(对于那些非数学迷来说,这大约是他们的 150 倍或者更多)。鉴于这一事实,我们可以期待更多来自《非洋葱报》的帖子,并可能在新闻来源方面更加多样化——鉴于《洋葱报》实际上是它自己的出版物。这可能会扭曲一点细节,我们稍后会看到,因为洋葱帖子可能是以某种同质的方式编写的,可能会影响我们的分析,但现在我们将继续前进!

数据收集和探索性数据分析

对于我们的数据收集,我们使用 Pushshift API (pushshift.io)从 r/TheOnion 收集了大约 15,500 个帖子(达到了上限),从 r/NotTheOnion 收集了 17,500 个帖子。

你可以看到我是如何使用 PSAW 包装器(基于 Pushshift API)来提取下面的帖子的:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

You could definitely set time.sleep to be less than 1 second. I was playing it way too safe.

两个子记录的时间跨度如下所示:

  1. 洋葱 subreddit 从 2013 年 10 月 3 日到 2019 年 7 月 5 日一路拉帖子。
  2. 不是洋葱subreddit(17500)帖子仅在 2019 年 3 月 29 日至 2019 年 7 月 6 日期间撤回帖子。

客观地说,两个子网站之间的时间跨度差异不仅是因为《洋葱》的读者数量比《洋葱》多 150 倍,还因为《洋葱》是它自己的独立新闻网站,而《洋葱》引用了各种各样的“可信新闻来源”。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Interestingly, theguardian.com is listed more than 500 times. Researching this, it’s due to the fact that they reference The Onion a lot.

正如我们在上面看到的,洋葱主要链接洋葱相关的网站,而不是洋葱链接相对平衡的新闻来源(令人惊讶的是卫报在两个子网站上都有链接)。看看作者的多样性,我们发现了一些相似之处。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者“dwaxe”发布了 4000 多次——从一开始就是一个相当荒谬的数字。看看洋葱和非洋葱的帖子频率之间的对比,非洋葱中的顶部海报只有 350 个帖子。考虑到更多的读者,这似乎令人惊讶,但当你考虑到洋葱子编辑的作者可能是出版物本身的编辑/海报时,这是有意义的。

所有这些都是说:由于时间差异很大(很多事情发生在 6 年的间隔中),并且洋葱帖子链接的域大多是同质的,所以我们的模型可能不会考虑当天的准确表示。这是我们在这个项目的未来构建中肯定会考虑的事情。

此外,由于这些帖子都链接到它们自己的独立新闻文章,因此没有帖子有正文,因此我们将把整个自然语言分析的重点放在文章的标题上。对于项目的其余部分,我们将输入 1 来引用洋葱和 0 来引用而不是洋葱。****

自然语言处理

情感分析

为了增加我们的功能列表,我使用了 VADER 和 TextBlob 软件包(是的,是的,我知道……)对这些标题进行情感分析。我将向您展示我是如何实现这两个包的——从 VADER 开始:

from nltk.sentiment.vader import SentimentIntensityAnalyzersia = SentimentIntensityAnalyzer()corpus = list(df['title'])
sentimentscores = []for i in corpus:
    score = sia.polarity_scores(i)
    score['title'] = i
    sentimentscores.append(score)

sentimentdf = pd.DataFrame(sentimentscores)
sentimentdf.drop(columns=['title'], inplace = True)

VADER 给你四个分数,正如它的文档所解释的:

  • 正、负和中性分数代表属于这些类别的文本的比例。这些分数加起来是 1。
  • 复合得分是一个指标,它计算在-1(最负面)和+1(最正面)之间标准化的所有评分的总和。

我们在下面看到了我们的 VADER 分数:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

VADER Sentiment Scores

正如我们所看到的,没有一个分数是显著不同的。但是我们可以看到而不是洋葱帖子的综合得分大约比洋葱的低 0.1 个单位——这表明标题更负面一点。

我们对 TextBlob 情感分析的实现也差不多,所以我将跳过它(如果你想看更多的文档,请参见https://textblob.readthedocs.io/en/dev/),直接进入正题!TextBlob 给你两个从 0 到 1 的分数:

  1. 极性在文中描述了积极和消极的情绪,其中 0 表示最消极,1 表示最积极。
  2. 主观句通常指个人观点、情感或判断,而客观句则指事实信息。范围从最客观 0 到最主观的 1。

我们在下面看到了我们的 TextBlob 情感得分:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

TextBlob Sentiment scores

我们从 VADER 的分析中得到了一些证实,洋葱网的帖子并没有更加负面,但也获得了一些新的信息:洋葱网帖子的主观性明显更高。稍后,我们将看到情感分析如何影响我们的模型。

相互关系

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

好吧。没有一个分数是相互关联的*——考虑到这是一个分类问题,这是有意义的。但是知道情感分析(尤其是 Textblob 的情感分析)有一些效果是很有帮助的。接下来,我们将使用 TextBlob 的情感分析作为我们的附加功能。*

字频率

在矢量化之前,我们使用 WordNetLemmatizer 和 RegexpTokenizer 来处理标题。我们可以在下面看到这一点:

for i in range(len(listoftitles)):
    tokenized = tokenizer.tokenize(listoftitles[i].lower())
    lemmatizelist = []
    for word in tokenizedi:
        lemmatizedword = lemmatizer.lemmatize(word)
        lemmatizelist.append(lemmatizedword)
    listoftitles[i] = " ".join(lemmatizelist)soupedtitles = [BeautifulSoup(title).get_text() for title in listoftitles]cleanedsoup = [re.sub("[^a-zA-Z]", " ", i) for i in soupedtitles]arraysoup = np.ravel(cleanedsoup)df['title'] = arraysoup

记号赋予器将把我们的单个标题分成一个单词列表,而词汇赋予器将把单词连到它的“根”上。然后,我们使用正则表达式删除任何非字母字符,并将标题数组替换为我们现在处理的标题数组!

使用 CountVectorizer,我们分析了洋葱式和非洋葱式子编辑中的单词频率:

Unigrams

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Top Unigrams in The Onion and Not The Onion

出现在洋葱中的前 5 个单字是“生活”、“新”、“人”、“哈”和“特朗普”。

出现在《不是洋葱》中的前 5 个单字是“男人”、“说”、“女人”、“特朗普”和“警察”。

二元模型

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Top Bigrams in The Onion and Not The Onion

出现在《洋葱》中的前 5 个双字母是“岁”、“白宫”、“唐纳德·特朗普”、“新闻来源”和“洋葱美国”。

出现在《不是洋葱》中的前五个大人物是“岁”、“佛罗里达人”、“白宫”、“分子自由”和“警察说”。

停止字

我取两个子词中出现的顶级单词和顶级双词的交集,即单词“onion ”,并创建一个定制的停用词列表,其中包括来自 Scikit-Learn 的原始英语停用词列表。这不仅在我们的建模过程中有用,而且在我们的系数分析后建模中也有用。

建模

我为训练集拟合了 7 个模型,模型的测试精度为:

  1. 使用计数向量机进行逻辑回归(86.9%)
  2. 使用计数矢量器+情感分析进行逻辑回归(80.6%)
  3. 用 tfidf 向量机进行逻辑回归(88.6%)
  4. 带有 tfidf 矢量器的 SVC 模型(88.4%)
  5. 带有 tfidf 矢量器的多项式朴素贝叶斯(87.8%)

通过反复试验,我发现最好的模型包含 TfidfVectorizer,而不包含情感分析。原始模型是相当过度拟合的(在训练集上具有> 98%的准确度)。对于我的最后两个模型,我想删除被 L1 惩罚的逻辑回归剔除的特征,并把它们放入更多的模型中,以潜在地修复过度拟合。

6.梯度增强分类器(76%)

7.使用 TfidfVectorizer 的逻辑回归(测试集上的 85.8%,训练集上的 91.9%,这是最佳模型,也是我选择的一个模型,考虑了偏差-方差权衡,也是具有最佳可解释性的模型)

型号选择

考虑到偏差-方差权衡,我们选择了我们的最佳模型作为我们的逻辑回归,使用 TfidfVectorizer,而不进行情感分析。利用具有 10%测试规模的训练测试分割,这在训练集上实现了 91.9%的准确度,在测试数据上实现了 85.8%的准确度。显然,这不是最好的测试精度,但是它确实减少了方差——这也是良好建模的一个方面。

我绘制了我们的模型与测试分数的混淆矩阵:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Confusion Matrix of Model

使用以下指标:

Sensitivity: 84.45%
Specificity: 87.03%
Precision: 85.16%
Accuracy: 85.82%
Misclassification Rate: 14.18%

很明显,在我们的测试集预测中有相当数量的假阴性(在这种情况下,这些帖子被预测为引用真实新闻,但实际上是假新闻)。这无疑是我们今后必须解决的问题,即使这可能会牺牲准确性。

系数分析

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Our most impactful features

绘制在任一方向上影响我们模型的 10 大特征,我们看到:

“生活”、“说”、“测验”、“国家”、“新闻”、“博客”和“不可思议”这些词增加了帖子出现在洋葱子编辑中的几率。

“被捕”、“警察”、“佛罗里达”、“穆勒报告”、“可卡因”和“监狱”这些词增加了《邮报》出现在“非洋葱”子编辑中的几率。

上面列出的特征极大地影响了模型的分类能力。例如,如果“生活”一词出现在帖子的标题中,相对于其他词,帖子出现在洋葱上的几率是 1531737 倍。正如我们在之前关于数据集的讨论中提到的,假设洋葱来自同质的海报和域(只有洋葱新闻文章),那么在任一子编辑中经常使用的某些词会极大地影响模型的分类能力,这是有道理的。

后续步骤和结论

这个模型并不完美。考虑到不能有效地检测假新闻的情况和影响,最佳模型仍然存在相当大的差异,具有足够高的偏差。然而,它肯定比基线要好,并且考虑到该模型可以以高达 88.5%的准确度检测 Not Onion 帖子,这使得我第一次进入自然语言处理和 API web 抓取时它是可以的。

如果我再做一次这个项目,我肯定会从比《洋葱》更广泛的来源收集更多的讽刺新闻。由于我们真正可以分析的唯一文本是帖子的标题(遗憾的是,这是公平的,因为我们大多数人在“互联网时代”实际上只浏览新闻文章的标题并从中做出判断),我希望能够创建一个新闻故事数据库,提取实际的新闻内容并基于此进行分析。

考虑到所有的事情,我确实从这个项目中学到了很多,我迫不及待地想继续提高我的技能!现在,我把这些留给你们:

[## 佛罗里达州众议院在否认突击步枪禁令后不久宣布色情是一种公共健康风险

佛罗里达州的立法者在周二通过了一项决议,宣布色情是公共健康风险,不到一个小时后…

thehill.com](https://thehill.com/homenews/state-watch/374816-florida-house-votes-to-declare-porn-a-public-health-risk-within-an-hour) [## 佛罗里达候选人说外星人绑架不能定义她

迈阿密(美联社)——美国众议院候选人贝蒂娜·罗德里格斯·阿奎莱拉有一长串成就来支持她的竞选…

apnews.com](https://apnews.com/5adaacef468642c4930d1ad76ff09b69) [## 经过 6 年的斗争,佛罗里达夫妇赢得了在前院种植蔬菜的权利

秋葵。甜椒。樱桃番茄。墨西哥胡椒和南瓜。这些是她最喜欢的蔬菜…

www.npr.org](https://www.npr.org/2019/07/02/738131948/after-6-year-battle-florida-couple-wins-the-right-to-plant-veggies-in-front-yard)

使用 SpaCy 构建 Flask API 来自动提取命名实体

原文:https://towardsdatascience.com/building-a-flask-api-to-automatically-extract-named-entities-using-spacy-2fd3f54ebbc6?source=collection_archive---------12-----------------------

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Photo credit: Pixabay

如何使用 spaCy 中的命名实体识别模块来识别文本中的人、组织或位置,然后使用 Flask 部署 Python API

如果数据是结构化的,那么今天大量的非结构化文本数据提供了丰富的信息来源。命名实体识别(NER )(也称为命名实体提取)是从半结构化和非结构化文本来源构建知识的第一步。

只有在 NER 之后,我们才能至少揭示出这些信息包含了什么人和什么内容。因此,数据科学团队将能够在语料库中看到人员、公司、地点等所有名称的结构化表示,这可以作为进一步分析和调查的出发点。

之前的文章中,我们已经学习并练习了如何使用 NLTK 和 spaCy 构建命名实体识别器。为了更进一步,创建一些有用的东西,本文将介绍如何使用 spaCy 开发和部署一个简单的命名实体提取器,并使用 python 中的 Flask API 为其提供服务*。*

烧瓶 API

我们的目标是构建一个 API,我们提供文本,例如,一篇纽约时报的文章(或任何文章)作为输入,然后我们的命名实体提取器将识别和提取四种类型的实体:组织、个人、位置和金钱。基本架构如下所示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure 1

为了构建 API,我们需要创建两个文件:

  1. index.html处理 API 的模板。
  2. app.py处理请求并返回输出文件。

最终的产品会是这样的:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure 2

让我们开始构建 API,并逐步创建两个文件。我们的项目文件夹结构如下所示:

  • 我们的项目位于 命名-实体-提取器 文件夹中。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure 3

  • templates目录与创建它的 app.py 在同一个文件夹中。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure 4

  • index.html 位于 templates 文件夹中。

index.html

  • 我们将我们的应用程序命名为“命名实体提取器”
  • 使用 BootstrapCDN ,在所有其他样式表加载我们的 CSS 之前,将样式表 <link>复制粘贴到我们的<head>中。
  • 获得 Bootstrap 的导航标题,一个简单信息网站的模板。它包括一个称为大屏幕的大标注和三个支持内容。
  • 从模板的源代码中复制粘贴导航栏代码。
  • Bootstrap 需要一个容器元素来包装站点内容并存放我们的网格系统。
  • 在我们的例子中,对于第一个容器,我们将创建一个带有两个输入字段的垂直表单,一个“Clear”按钮和一个“Submit”按钮。
  • 文本表单控件是用form-control类设计的。
  • 我们给我们的用户四个任务选项(又名命名实体提取任务)供选择,它们是: 组织地缘 & 金钱
  • 第二个容器为我们的用户操作提供上下文反馈消息,这是命名实体提取的结果。
  • 我们不仅希望向用户打印出命名实体提取结果,还希望打印出每次命名实体提取的结果数量。
  • 复制粘贴 html 页面末尾附近的<script>中的 JavaScript ,就在结束的</body>标签之前,

index.html

app.py

我们的app.py文件相当简单易懂。它包含将由 Python 解释器执行以运行 Flask web 应用程序的主要代码,还包含用于识别命名实体的空间代码。

  • 我们将应用程序作为一个单独的模块运行;因此,我们用参数__name__初始化了一个新的 Flask 实例,让 Flask 知道它可以在它所在的同一个目录中找到 HTML 模板文件夹(templates)。
  • 我们使用 route decorator ( @app.route('/'))来指定应该触发index函数执行的 URL。
  • 我们的index函数只是呈现了位于templates文件夹中的index.html HTML 文件。
  • process函数中,我们将 nlp 应用于用户将要输入的原始文本,并从原始文本中提取预先确定的命名实体( 组织地缘 & 金钱 )。
  • 我们使用POST方法在消息体中将表单数据传输到服务器。最后,通过在app.run方法中设置debug=True参数,我们进一步激活了 Flask 的调试器。
  • 我们使用run函数仅在 Python 解释器直接执行该脚本时在服务器上运行应用程序,这是通过使用带有 __name__ == '__main__'if语句来确保的。

app.py

我们快到了!

尝试我们的 API

  • 启动命令提示命令
  • 导航到我们的 命名实体-提取器 文件夹。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure 5

  • 打开您的 Web 浏览器,将“ http://127.0.0.1:5000/ ”复制粘贴到地址栏中,我们将看到以下表单:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure 6

  • 我复制粘贴了纽约时报的一篇文章的几段,这是一个加拿大的故事:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure 7

  • 在“选择任务”下选择“ 组织 ,然后点击“提交”,这是我们得到的结果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure 8

  • 很好。让我们试试 实体:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure 9

  • “实体:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure 10

  • 金钱 实体:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure 11

我们完了!

如果你按照上面的步骤做到了,那么恭喜你!您已经零成本地创建了一个简单而有效的命名实体提取器!回头看,我们只需要创建两个文件。我们所需要的是开源库和学习如何使用它们来创建这两个文件。

通过构建这样一个应用程序,你学到了新的技能,并利用这些技能创造出有用的东西。

完整的源代码可以从这个获得。周一快乐!

参考:

在 Dialogflow 上构建功能聊天机器人

原文:https://towardsdatascience.com/building-a-functional-chatbot-in-dialogflow-aeeba1260353?source=collection_archive---------14-----------------------

虽然 Dialogflow 被广泛认为是一个直观和简单的平台,但它的一些功能可能有点晦涩,我认为任何初学者都可以从迷你指南中受益。这篇博文旨在涵盖与 Dialogflow 相关的许多基本概念和词汇,但是因为这些 NLP 平台之间有许多重叠,所以这里讨论的许多概念也可以应用于其他地方。

就像任何其他项目一样,在开始构建聊天机器人之前有一个思维图总是好的。这将有助于确定你需要多少意图、跟进意图和背景。但是等等…什么是意图?

意图

意图基本上是对话流的核心和灵魂。它们旨在捕捉用户的意图,并触发聊天机器人内部的响应。换句话说,意图捕获用户的意图,对其进行处理,并将其映射到特定的响应。例如,如果用户说“我附近的咖啡店”,一个名为“咖啡店”的假设意图将被触发并响应用户的请求。

你的聊天机器人最终会有几个意图来处理用户和聊天机器人之间对话的所有场景。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

训练短语

训练短语是用户说出来触发特定意图的短语。使用上面的同一个咖啡店示例,用户可以通过多种方式要咖啡。

  1. 我附近的咖啡店
  2. 我现在真的很想喝杯咖啡
  3. 最近的星巴克或李丁丁在哪里?
  4. 我附近有咖啡馆吗?

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

显然,要想出一份详尽的清单列出所有说“我想要咖啡”的可能方式是不可能的。然而,训练短语的妙处在于你不需要提供一个详尽的列表。相当数量的表达相同意图的相似短语足以让自然语言理解引擎将任何输入映射到特定意图

实体

意图允许聊天机器人理解用户的意图,而实体则用于从自然语言输入中提取信息。从日期和时间到对象的价格,您想要从用户输入中提取的任何数据都有相应的实体。

Dialogflow 有一个预定义的实体列表,称为系统实体;它们包括基本值,如日期、时间、位置、单位等…回到咖啡店的例子,假设我们想在下午 4 点去喝咖啡

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我使用了 sys.time 实体,并提取了训练短语中的时间。现在,聊天机器人将寻找这个实体,并试图在用户的输入中提取它。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

当我测试聊天机器人时,它能够识别并提取上午 10 点的时间值。查看图像底部的时间参数。

反应

这是 Dialgflow 更容易和更直观的特性之一。每当用户说些什么,一个意图就会被触发,聊天机器人必须用适当的信息来回应——就像它在正常对话中的工作方式一样。

顾名思义,响应部分就是为了这个目的。response 部分很酷的一点是,您可以重复从用户输入中提取的实体值

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

如您所见,我从用户输入中提取了时间,并将其作为确认包含在响应中。

上下文

上下文表示用户请求的当前状态;可以把它们看作是用户在对话结构中所处位置的虚拟检查点。此外,上下文还确保会话以串行方式流动(可以说,不能从检查点 A 跳到检查点 D)。

有两种上下文:

输出上下文

每当用户有特定意图时,就会创建输出上下文。从更专业的角度来说,可以将输出上下文视为任何特定意图的布尔属性。每当用户有任何特定意图时,输出上下文返回 true 并保持活动状态,直到用户触发另一个意图;然后,另一个意图的输出上下文变为活动的。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

输入上下文

输入上下文是防止其他不需要的意图被触发的东西。我喜欢把输入上下文想象成“如果”条件。只有当 xyz 上下文处于活动状态时,才会触发 xyz 意图。当用户询问后续问题时,这尤其有用。例如,如果我想预约医生,我会先说“我想预约”,然后再提任何关于我喜欢的时间或日期的事情。时间和日期只有在我表示希望预约时才是相关的。在这种情况下,医生预约意图的输出上下文成为后续意图的输入上下文:日期和时间。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

后续意图

跟进意图是对话流程的关键部分——你会比你想象的更需要它们!几乎每个意向都有后续意向;无论是简单的“是”或“否”响应,还是更具体的请求,如日期或时间偏好。

如果你对输入和输出环境的工作原理有一个坚实的理解,那么后续的意图应该是轻而易举的。

更多资源

我希望这有所帮助;上面讨论的概念应该给你足够的背景知识来在 Dialogflow 上构建一个简单的聊天机器人。

如果你想更了解它,查看文档【https://cloud.google.com/dialogflow/docs/concepts

请继续关注集成、履行和分析等后端技术!

在 Neo4j 构建图形分析管道,探索罗马的交通系统

原文:https://towardsdatascience.com/building-a-graph-analytics-pipeline-in-neo4j-to-explore-the-transport-system-in-rome-281d05dfbf88?source=collection_archive---------21-----------------------

我正试着回到写作模式,每个月发布几篇新的博客文章。 Neo4j graph algorithms 中有很多很酷的新特性我还没有写出来,所以我会尽可能多的介绍一下。

其中一个新特性是升级了节点和关系类型投影,以支持加载多种关系类型。让我们来看看算法引擎是如何工作的,以及这个特性是如何派上用场的。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Image from https://neo4j.com/docs/graph-algorithms/current/projected-graph-model/

每当我们运行任何算法时,图形加载器都会从 Neo4j 中读取图形,并将其作为投影图形加载到内存中。一旦投影图存储在内存中,我们就可以对它运行任何算法。假设我们希望在由关系类型 REL _ 类型 1 描述的网络上运行 PageRank 算法,并在由 REL _ 类型 2 描述的网络上运行连通分量算法。如果你读过我以前的博客,你可能会看到这样的内容

CALL algo.pageRank('Node','REL_TYPE1');
CALL algo.unionFind('Node','REL_TYPE2');

我按顺序运行两个算法。这种方法效率不高,因为我们首先将图形投影到 PageRank 算法的内存中,存储结果,然后从内存中卸载图形。然后,我们对连通分量算法重复相同的过程。为了避免同一个图形多次加载到内存中,我们可以使用名为 graphs loading 的。它允许我们将考虑的图形存储到内存中,并能够在其上运行许多算法,而不必在每次运行时将图形加载到内存中。

命名图形加载示例:

CALL algo.graph.load('my-graph','Node',
'REL_TYPE1 | REL_TYPE2',
{ 
   duplicateRelationships:'min',
   relationshipProperties:{ 
      distance:{ 
         property:'distance'
      },
      duration:{ 
         property:'duration_avg',
         default_value:5
      }
   },
   nodeProperties:{ 
      seed_value:{ 
         property:'seed_value'
      }
   }
})

如您所见,我们已经将两种不同类型的关系加载到投影图中。我们还加载了关系的距离和持续时间属性以及节点的 seed_value 属性。运行图算法时,这些属性可用作权重或种子属性。

现在我们可以在同一个投影图上运行这两种算法。

CALL algo.pageRank('Node','REL_TYPE1',{graph:'my-graph'});
CALL algo.unionFind('Node','REL_TYPE2',{graph:'my-graph'});

当我们完成分析后,请记住使用以下命令从内存中删除图形:

CALL algo.graph.remove('my-graph')

关系重复数据删除策略

为了更好地理解重复数据消除战略,让我们看一下下面的示例。

CREATE (a:Loc)-[:ROAD{cost:4}]->(b:Loc),
           (a)-[:ROAD{cost:7}]->(b),
           (a)-[:RAIL{cost:5}]->(b),
           (a)-[:RAIL{cost:8}]->(b)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在这个例子中,我们有两个由四个关系连接的节点,它们都指向同一个方向。假设我们想要搜索最短的加权路径。为了理解当我们将 Neo4j 存储的图形投影到图形算法内存图形时会发生什么,最好看一下文档中的这段引文。

投影图模型不支持一对节点之间的多种关系。

关系重复数据消除策略弥补了 Neo4j 中多个存储关系与算法引擎中单一预测关系之间的差距。如果关系中不存在权重,则没有关系,因为所有存储的关系将被简化为单个投影关系。但是,如果关系中存在权重,那么我们可以从以下四种策略中选择一种来处理权重重复数据消除:

  • skip -保持第一次遇到的关系(以及相关的权重)。
  • sum -对所有遇到的关系的相关权重求和。
  • min -保持所有遇到的关系的最小权重。
  • max -保持所有遇到的关系的最大权重。

在图中搜索最短路径时,我们希望使用min 重复数据删除策略。我们分别加载铁路和公路关系类型。

CALL algo.graph.load('my-graph', 'Loc', 'RAIL | ROAD', {relationshipWeight: 'cost', duplicateRelationships: 'min' })

有了内存中的图形,我们可以开始搜索最短路径。

MATCH (start:Loc)-->(end:Loc)
WITH distinct start,end
CALL algo.shortestPath.stream(start,end,'cost',
  {graph:'my-graph',relationshipQuery:'RAIL'})
YIELD nodeId,cost
RETURN nodeId,cost

结果

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

正如我们所料,算法会从两个可能的铁路关系中选择成本最低的一个。

在关系型道路上尝试同样的事情。

MATCH (start:Loc)-->(end:Loc)
WITH distinct start,end
CALL algo.shortestPath.stream(start,end,'cost',
  {graph:'my-graph',relationshipQuery:'ROAD'})
YIELD nodeId,cost
RETURN nodeId,cost

结果

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

现在运行算法,不要指定任何关系类型。如果我们不指定关系类型,算法将遍历所有可用的关系类型。

MATCH (start:Loc)-->(end:Loc)
WITH distinct start,end
CALL algo.shortestPath.stream(start,end,'cost',
  {graph:'my-graph'})
YIELD nodeId,cost
RETURN nodeId,cost

结果

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

同样,我们得到了所有关系类型(公路和铁路)的网络中最短的可用路径。

您必须记住,决定如何删除重复关系权重的不是最短路径算法,而是图形加载器。例如,如果我们使用sum重复数据删除策略,公路和铁路示例中的最短路径将花费 24,因为这是所有关系权重的总和。

罗马交通系统分析

有了对投影图的新理解,让我们来看一个更实际的例子。我发现了这个极好的罗马交通网络数据集。它的信息非常丰富,包含五种不同的交通方式,如地铁、公交或步行。

图形模型

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

类似地,和以前一样,我们的节点只有一个标签。唯一的区别是,在这里,我们有五种不同的交通方式可用,并存储为一个关系类型。

创建约束

CREATE CONSTRAINT ON (s:Stop) ASSERT s.id IS UNIQUE;

导入

我们将首先导入网络的节点,然后导入关系。您需要在导入之前将数据复制到 $Neo4j/import 文件夹中。

导入节点

LOAD CSV WITH HEADERS FROM “file:///network_nodes.csv” as row FIELDTERMINATOR “;”
MERGE (s:Stop{id:row.stop_I})
SET s+=apoc.map.clean(row,[‘stop_I’],[])

导入关系

UNWIND ['walk','bus','tram','rail','subway'] as mode
LOAD CSV WITH HEADERS FROM "file:///network_" + mode + ".csv" as row FIELDTERMINATOR ";"
MATCH (from:Stop{id:row.from_stop_I}),(to:Stop{id:row.to_stop_I})
CALL apoc.create.relationship(
 from, toUpper(mode),
{distance:toInteger(row.d),
 duration_avg:toFloat(row.duration_avg)}, to) YIELD rel
RETURN distinct 'done'

步行是唯一缺乏平均持续时间属性的交通方式。幸运的是,如果我们假设一个人平均每小时走 5 公里,或者大约每秒 1.4 米,我们可以很容易地计算出来。

WITH 5 / 3.6 as walking_speed
MATCH (:Stop)-[w:WALK]->()
SET w.duration_avg = toFloat(w.distance) / walking_speed

图表分析管道

现在图形已经准备好了,我们可以通过将 Neo4j 存储的图形加载到投影的内存图形中来启动图形算法管道。我们用五种关系类型和两种关系属性加载图表。这两个属性可以被算法用作关系权重。

CALL algo.graph.load('rome','Stop',
    'BUS | RAIL | SUBWAY | TRAM | WALK',
    { 
       duplicateRelationships:'min',
       relationshipProperties:{ 
          distance:{ 
             property:'distance'
          },
          duration:{ 
             property:'duration_avg'
          }
       }
    })

PageRank 算法

为了开始分析,让我们使用 PageRank 算法在有轨电车交通网络中找到最著名的 graphfamous 车站。

CALL algo.pageRank.stream('Stop','TRAM',{graph:'rome'})
YIELD nodeId, score
WITH nodeId, score
ORDER BY score DESC LIMIT 5
RETURN algo.asNode(nodeId).name as name, score

结果

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图形加载器支持加载许多关系类型,算法也是如此。在本例中,我们在公共汽车、电车和铁路的组合网络中搜索最著名的 graphfamous 站点。

CALL algo.pageRank.stream('Stop','TRAM | RAIL | BUS',{graph:'rome'})
YIELD nodeId, score
WITH nodeId, score
ORDER BY score DESC LIMIT 5
RETURN algo.asNode(nodeId).name as name, score

结果

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

连通分量算法

Graph algorithms pipeline 也可以是批处理作业的一部分,在批处理作业中,您可以将图形加载到内存中,运行一些算法,将结果写回 Neo4j,然后卸载内存中的图形。让我们分别对所有交通模式网络运行连通分量算法,并写回结果。

UNWIND ["BUS","RAIL","SUBWAY","TRAM","WALK"] as mode
CALL algo.unionFind('Stop',mode,{writeProperty:toLower(mode) + "_component"})
YIELD computeMillis
RETURN distinct 'done'

探索电车网络中的连接组件。

MATCH (s:Stop)
RETURN s.subway_component as component,
       collect(s.name)[..3] as example_members,
       count(*) as size
ORDER BY size DESC
LIMIT 10

结果

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这些结果很奇怪。我没去过罗马,但我高度怀疑有六个不相连的电车部件。即使查看结果,您也可能想知道为什么组件 7848 和 7827 具有相同的成员。

您的组件 id 可能会不同,所以请确保使用正确的 id。

MATCH p = (s:Stop)-[:SUBWAY]-()
WHERE s.subway_component in [7848,7827]
RETURN p

结果

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我知道这很难看到,但网络上有同名的站点。虽然站点的名称可能相同,但站点 id 不同,因此被视为独立的节点。我们可以猜测,这是一条单向行驶的有轨电车线路,路的两边各有一条。由于每个方向的车站相距步行距离,因此该数据集对它们进行区分。

最短路径算法

我发现了一个用例,在这个用例中,您希望将投影图一直保存在内存中。假设我们正在构建一个应用程序,它将帮助我们找到罗马两点之间最短或最快的路径。我们不希望为每个查询将图形投影到内存中,而是希望一直将投影的图形保存在内存中。

我们可以搜索仅穿过特定关系类型的最短路径,或者在我们的情况下,搜索运输模式。

MATCH (start:Stop{name:’Parco Leonardo’}),(end:Stop{name:’Roma Trastevere’})
CALL algo.shortestPath.stream(start,end,’distance’,{graph:’rome’,relationshipQuery:’RAIL’})
YIELD nodeId,cost
RETURN algo.asNode(nodeId).name as name, cost as meters

结果

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

仅使用铁路网络的问题在于,大多数停靠点不在铁路网络中。为了能够找到网络中任何一对站点之间的最短路径,我们还必须允许算法遍历行走关系。

MATCH (start:Stop{name:'LABICANO/PORTA MAGGIORE'}),(end:Stop{name:'TARDINI'})
CALL algo.shortestPath.stream(start,end,'distance',{graph:'rome',relationshipQuery:'WALK | RAIL'})
YIELD nodeId, cost
RETURN algo.asNode(nodeId).name as name, cost as meters

结果

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

如果你还记得,我们在图形存储器中存储了两个关系属性。现在让我们使用持续时间属性作为权重。

MATCH (start:Stop{name:'LABICANO/PORTA MAGGIORE'}),(end:Stop{name:'TARDINI'})
CALL algo.shortestPath.stream(start,end,'duration',{graph:'rome',relationshipQuery:'WALK | RAIL'})
YIELD nodeId, cost
RETURN algo.asNode(nodeId).name as name, cost / 60 as minutes

结果

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

结论

我希望我已经给了你一些关于如何设计你的图形算法管道并从中获益的想法。

和往常一样,代码可以在 Github 上获得。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值