TowardsDataScience-博客中文翻译-2019-十二-

龙哥盟 / 2024-10-14 / 原文

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

原文:TowardsDataScience Blog

协议:CC BY-NC-SA 4.0

推荐系统简介

原文:https://towardsdatascience.com/brief-on-recommender-systems-b86a1068a4dd?source=collection_archive---------6-----------------------

行业中使用的不同类型的推荐方法。

如今,人们过去更多地在网上购买产品,而不是从商店购买。以前,人们习惯于根据亲戚或朋友的评论来购买产品,但现在随着选择的增加,我们可以通过数字方式购买任何东西,我们需要向人们保证产品是好的,他们会喜欢。为了增强购买产品的信心,推荐系统应运而生。

Recommendation techniques

推荐系统是机器学习技术在商业中最成功和最广泛的应用之一。推荐系统有助于增加商业收入,帮助顾客购买最适合他们的产品。

什么是推荐引擎?

推荐引擎根据特定客户以前的购买历史筛选出他/她感兴趣或愿意购买的产品。关于客户的可用数据越多,推荐就越准确。

但是,如果客户是新客户,这种方法将会失败,因为我们没有该客户以前的数据。因此,为了解决这个问题,使用了不同的方法;比如经常推荐最受欢迎的产品。这些建议不一定准确,因为它们不依赖于客户,对所有新客户都一样。一些企业会询问新客户的兴趣,以便他们能够更准确地推荐。

现在,我们将关注推荐引擎使用的不同类型的过滤。

基于内容的过滤

这种过滤基于为该产品提供的描述或一些数据。系统根据产品的上下文或描述找到产品之间的相似性。用户的先前历史被考虑在内,以找到用户可能喜欢的类似产品。

例如,如果用户喜欢像《碟中谍》这样的电影,那么我们可以向他推荐《汤姆·克鲁斯》或《动作片》。

Content-based filtering

在这种过滤中,使用了两种类型的数据。首先,用户的喜好,用户的兴趣,用户的个人信息,如年龄,有时用户的历史。该数据由用户向量表示。第二,与产品相关的信息被称为项目向量。项目向量包含所有项目的特征,基于这些特征可以计算它们之间的相似性。

使用余弦相似度来计算推荐。如果“A”是用户向量,“B”是项目向量,则余弦相似度由下式给出

cosine similarity formula

cosine similarity calculation

余弦相似性矩阵中计算的值按降序排序,并且为该用户推荐顶部的项目。

优势

  1. 向用户推荐他们喜欢的商品类型。
  2. 用户对推荐的类型感到满意。
  3. 可以推荐新品;只需要该项目的数据。

不足之处

  1. 用户将永远不会被推荐不同的项目。
  2. 由于用户不尝试不同类型的产品,业务无法扩展。
  3. 如果用户矩阵或项目矩阵被改变,余弦相似性矩阵需要被再次计算。

协同过滤

推荐是基于用户的行为来完成的。用户的历史起着重要的作用。例如,如果用户 A 喜欢“酷玩”、“林肯公园”和“布兰妮·斯皮尔斯”,而用户 B 喜欢“酷玩”、“林肯公园”和“泰勒·斯威夫特”,则他们具有相似的兴趣。因此,很有可能用户 A 会喜欢“泰勒·斯威夫特”,而用户 B 会喜欢“布兰妮·斯皮尔斯”。这就是协同过滤的工作方式。

user-user Collaborative filtering

使用两种类型的协同过滤技术:

  1. 用户-用户协同过滤
  2. 项目-项目协同过滤

用户-用户协同过滤

在这种情况下,用户向量包括用户购买的所有商品以及对每个特定产品给出的评级。使用 n*n 矩阵计算用户之间的相似性,其中 n 是在场用户的数量。使用相同的余弦相似性公式计算相似性。现在,计算推荐矩阵。在这种情况下,评级乘以已经购买了该项目的用户和必须向其推荐该项目的用户之间的相似性。该值针对该用户的所有新项目进行计算,并按降序排序。则向该用户推荐顶部的项目。

Collaborative filtering

如果新用户到来或者老用户改变他或她的评级或者提供新的评级,那么推荐可能改变。

项目-项目协同过滤

在这种情况下,不考虑相似的用户,而是考虑相似的项目。如果用户“A”喜欢“盗梦空间”,他可能喜欢“火星人”,因为主角是相似的。这里,推荐矩阵是 m*m 矩阵,其中 m 是存在的项目的数量。

Item-Item collaborative filtering

优势

  1. 新产品可以介绍给用户。
  2. 生意可以扩大,可以推广新产品。

不足之处

  1. 根据所用协作方法的类型,需要用户以前的历史记录或产品数据。
  2. 如果没有用户购买该新项目或对其评级,则不能推荐该新项目。

两种推荐算法各有优缺点。如今,为了进行更精确的推荐,使用了混合推荐算法;也就是说,同时使用基于内容的过滤和协同过滤来推荐产品。混合推荐算法更高效、更有用。

聪明的混蛋,疯狂的辣妹,和其他范围限制的文物

原文:https://towardsdatascience.com/brilliant-jerks-crazy-hotties-and-other-artifacts-of-range-restriction-c53785ae249f?source=collection_archive---------3-----------------------

当人们写史蒂夫·乔布斯时,他们会提到他才华横溢但刻薄:他可以立即解决困扰他的团队数月的设计问题,但他会立即解雇犯了小错误的人。因为很多人都想成为史蒂夫·乔布斯那样的人,而且成为天才很难,所以一些雄心勃勃的人夸大了他们的鲁莽行为。

但是乔布斯在很多方面都是个异类。他也是这样的异类吗?

无聊的答案是肯定的:智商和宜人性之间没有很强的联系,所以在科学上可以问聪明人是不是混蛋,答案是“不超过平均水平。”

当然,怪人和天才的关联并不是唯一的民间关联。有热疯规模,运动员/书呆子二分法,等等。如果有什么不同的话,美丽和精神稳定应该有更好的相关性,因为足够疯狂的人在基本卫生方面都有问题,而突变负荷会影响面部对称和精神疾病。从经验上来看,成为运动员和成为书呆子确实相关,因为锻炼是益智的和聪明人更健康。从经验上来说,傻瓜/铅笔脖子连续体实际上是健康/聪明的人和不健康/迟钝的人之间不公平的连续体。[1]

那么,为什么我们有这么多关于特征之间权衡的民间智慧,而这些权衡并不普遍存在呢?

答案并不令人惊讶;这只是统计数据。在第一个近似值中,期望性状之间的任何强负相关都是由范围限制驱动的。

让我们测试一下

直觉很简单:如果你在看 A 和 B 的特征如何相关,但你只是在看一个人的子集,而这个子集是由人们的 A 和 B 的水平决定的,子集内的相关性将低于子集外的相关性。

考虑标准化考试分数和 GPA,两种不同的衡量学术能力的方法。总的来说,它们之间有很好的相关性是有道理的:如果你在 SAT 考试中得了满分,你可能在其他考试中也表现不错;如果你在一个问题上挣扎,你在两个问题上都挣扎。但是大学综合考虑两者,因为他们测量不同的东西。SAT 是对原始推理能力的更好的衡量;GPA 也是衡量你愿意努力学习的一个标准。如果学校根据学生的 GPA SAT 来录取学生,那么这种正相关关系在一所学校内就消失了:SAT 高分的学生 GPA 较低,反之亦然。

我们可以模拟一下。

我们将从产生一群假设的学生开始。我们通过获取每个人的 SAT 分数的标准差并应用一些噪声来确定每个人的 GPA:

import pandas as pd
import numpy as np
import scipy.stats
import matplotlib.pyplot as pltdef gen_students(noise=0.5):
    """Create a set of students with a hypothetical GPA and SAT distribution"""
    scores = np.random.normal(1000,195,100)
    scores = [400 if x < 400 else 1600 if x > 1600 else x for x in scores]
    zs = scipy.stats.zscore(scores)
    gpas_raw = [3.0 + # median GPA
                x * 0.75 # GPA std dev
                + np.random.normal(0, noise) # some normally-distributed noise
                for x in zs]
    gpas = [0 if x < 0 else 4.5 if x > 4.5 else x for x in gpas_raw]
    df = pd.DataFrame(list(zip(scores,gpas)), columns = ['SAT','GPA'])
    df['GPA_Percentile'] = df.GPA.rank(pct=True)
    df['SAT_Percentile'] = df.SAT.rank(pct=True)
    df['Ranking'] = (df.SAT_Percentile + df.GPA_Percentile) / 2
    return df

如果我们运行df['SAT'].corr(df['GPA']),我们看到相关系数是 0.80。换句话说,sat 和 GPA 紧密相关。

现在,让我们送这些孩子去学校:

def choose_schools(df, count=10):
    quantiles = pd.qcut(df.Ranking, count)
    key = dict(zip(list(quantiles.cat.categories), range(10)))
    df['Quantiles'] = pd.qcut(df.Ranking, count)
    df['School'] = [key[x] for x in df.Quantiles]
    return df

这只是根据学生的 SAT 分数+ GPA 分数将他们分成不同的组。我刚刚给学校编号,从 0 到 9。学校 0 的 SAT 平均成绩约为 700,高中平均绩点为 1.6,而学校 9 的 SAT 平均成绩为 1325,平均绩点为 4.15。

但是真正有趣的是在学校中观察 SAT/GPA 相关性的结果。我们可以通过以下方式快速运行:

for i in range(10):
    print(df[df['School']==i]['GPA'].corr(df[df['School']==i]['SAT']))

对大多数学校来说,这种相关性是非常负的。例如,在第五学校,GPA 和 SAT 之间有-0.98 的相关性。以下是总体总结:

SAT-GPA correlation by school

和学生的视觉表现,由学校用颜色编码:

SAT vs GPA, color-coded by school

该图的一个显著特征是,在范围的高端和低端,相关性更接近真实世界的相关性。这在一定程度上是我使用的分布函数,因为 sat 和 GPA 是有上限的。但是是真实世界的反映。有些特征衡量起来微不足道,比如身高,但是对于 SAT 分数和 GPA 到底衡量什么却存在严重分歧。(GPA 是衡量你有多努力,还是衡量你有多愿意为毫无意义的任务努力?SAT 测试的是你有多聪明,还是你擅长做选择题?在分布的中间,很容易概括,但在极端情况下,测试是不完美的。)

所有这些的结果是,如果你处于某个分布的极端——上哈佛,参加奥运会,在死囚区——我们测量的绝对下限和上限意味着样本实际上较少受到范围限制。然而,我们其他人必须敏锐地意识到范围限制。

使用范围限制

一个令人高兴的结果是,它给你一个更好的方法来处理冒名顶替综合症。或许,你擅长的事情比你不擅长的事情更不容易被你发现。“大家都比我聪明!”你认为,在公司里,每个人都被你的职业道德所震撼。“这里的每个人都比我有更好的时间管理技巧,”公司最聪明的人说。“每个人都比我工作努力,也比我聪明得多,”办公室里最擅长社交的人说。

事实上,唯一应该感到骗子综合症的人是那些最擅长推销自己的人。但是因为推销自己需要一些自我欺骗,他们自然会认为自己被懒惰的白痴包围。

另一个要点是,如果你在一个按照一个指标积极排序的团队中,但是团队中的成就依赖于其他指标,那么这些指标将是预测排序后成功的唯一指标。因此,在顶级科技公司, STEM 专业知识是最不可预测的成功衡量标准,但这只是因为他们已经把成为世界 STEM 专家作为招聘的条件。(更黑暗的一点是,“人际交往能力”是反社会者绝对具备的;被认为是一个有效的领导者的一个方法是,对进展顺利的事情邀功,对进展不顺利的事情有效地推卸责任。突然,你“领导”的一切都成功了!)

不过,你应该从范围限制中得出的主要结论是,与你互动的人不是一个代表性样本,但显而易见的统计工具——无论是正式的还是非正式的观察——都隐含地假设了一个随机样本。

有些心地善良的人头脑异常敏锐。有漂亮的人也有精神稳定的。但是对于我们中的绝大多数人来说,这些人是如此的与我们格格不入,以至于我们视而不见。随着软件蚕食世界,根据人们可观察到的特征对他们进行分类变得越来越容易,所以你越来越多地被那些总体上和你一样优秀的人所包围,不管哪种版本的“优秀”最经得起数字运算。

如果你在本行业最好的公司工作,或者在最高级的约会场所,你会发现好的品质之间的相关性通常是积极的。如果你没有,坏消息是:你不是百分之 99.99。不过,你可以对此保持冷静:如果你在你所做的事情上不是世界上最好的,你观察到你的同龄人之间可取的特质之间的权衡,至少你知道这个世界是相当公平的。

不要错过下一个故事。报名参加我的 邮箱快讯 。或者查看 关于/常见问题 页面了解更多。

[1]当然,这两种情况都有很大的可变性;我说的是非常宽泛的平均值,而不是对任何一个人都有意义的东西。此外,这些概括很难做出,因为人们经常选择夸大人格特征。作为一个在许多不同方面都略高于平均水平的人,很难让人印象深刻,所以通常的模式是在一两个特质上全押。

用于交通管理的空间数据分析

原文:https://towardsdatascience.com/brilliant-sim-cards-traffic-management-90982691fa94?source=collection_archive---------15-----------------------

SIM 卡有助于车辆交通管理吗?简短的回答是是的。在这篇文章中,我将总结我在空间数据分析的一次最好的经历。事实上,感谢沙特电信公司(STC)的业务团队,他们在利雅得发起了交通管理数据通活动。他们邀请了 50 个数据极客团队来展示他们的肌肉,只需给他们模拟数据来进行分析和解决。我和我的兄弟穆罕默德·卡西姆(Mohammed Qassim)与其他 49 支队伍一起组队参赛。幸运的是,我们队在其他十支队伍中进入了决赛,并获得了比赛的第二名。

在这里,我将介绍什么是数据集,我们的方法和策略,然后展示一些分析和结论。

资料组

  1. 第一个文件:四列,包含 9868 辆独特车辆的记录和 2,011,774 个相关联的位置报告,跨越 24 小时的时间段。这些列是:
    SAM:报告位置的时间(以午夜后的秒为单位)
    经度:以十进制度表示的经度坐标
    纬度:以十进制度表示的纬度坐标
    ID:加密 ID
  2. Roads 数据来自三个文件:
    Roads.shp
    Roads.dbf 和
    Roads.shx

我们的战略和方法

对于比赛,我们使用了三个工具:微软 SQL Server 2017、微软 Power BI 和带有 Python 3 设置的 Jupyter Notebook。每个工具发生的一些活动有:

My brother and I holding the second prize

分析和结果

在许多清理和特性工程步骤之后,我们最终得到了 11 列和 9805 行(ID)。主要功能有:计算每辆车的行驶时间、距离,从而计算速度。主要的挑战是处理坐标或 GPS 漂移中的一些噪声!但是,这里我将展示一些对数据集进行的绘图和分析:

绘制“距离”和“平均速度”栏的直方图

24 小时内 ID 在各条道路上的计数和分配

条形图很有意义。早上是道路交通的高峰时间。交通活动在早上 6:00 学校值班时开始,在早上 8:00 工人值班时达到高峰。

计算 ID 的一般速度

只要把它们分成五大类:非常高速、高速、正常速度、低速和非常低速。

在速度谱上绘制每小时的数据

上午 8:00 交通拥堵最严重,上午 11:00 违章率最高。令人惊讶的是,大多数汽车在午夜和午夜后都安全行驶。

可视化两条主干道起点处的每小时交通密度:

使用交互式小部件,让您的 Jupyter 笔记本栩栩如生

原文:https://towardsdatascience.com/bring-your-jupyter-notebook-to-life-with-interactive-widgets-bc12e03f0916?source=collection_archive---------0-----------------------

现实世界中的数据科学

如何使用 ipywidgets 创建动态仪表板

扩展 Jupyter 的用户界面

传统上,每次需要修改笔记本单元格的输出时,都需要更改代码并重新运行受影响的单元格。这可能是麻烦的、低效的和容易出错的,并且在非技术用户的情况下,这甚至是不可行的。这就是 ipywidgets 发挥作用的地方:它们可以嵌入到笔记本中,并提供一个用户友好的界面来收集用户输入,并查看更改对数据/结果的影响,而不必与代码进行交互;您的笔记本可以从静态文档转变为动态仪表盘,非常适合展示您的数据故事!

⚠️范围:ipywidgets 上的资源是有限的,现有的极少数教程要么不完整,要么专注于interact函数/装饰器。这是一个完整的教程,告诉你如何完全控制小部件来创建强大的仪表板。

我们将从基础开始:添加一个小部件并解释事件如何工作,我们将逐步开发一个仪表板。
我将一步一步地指导你,在我们进行的过程中,以示例为基础。

什么是 widget?

如果你曾经创建过图形用户界面 (GUI),那么你就已经知道什么是小部件了。但是让我们快速定义一下:
widget 是一个 GUI 元素,比如按钮、下拉菜单或文本框,它驻留在浏览器中,允许我们通过响应事件和调用指定的处理程序来控制代码和数据。

这些 GUI 元素可以被组装和定制以创建复杂的仪表板。

Demo: A few of the most popular widgets

在整篇文章中,我们将看到它们中的一些在起作用。

准备好了吗?🏁

❶入门

要开始使用这个库,我们需要安装 ipywidgets 扩展。如果使用 conda,我们在终端中键入以下命令:

conda install -c conda-forge ipywidgets

对于 pip,这将是一个两步过程:1 .安装和 2。启用:

pip install ipywidgetsjupyter nbextension enable --py widgetsnbextension

添加小部件

为了将小部件合并到笔记本中,我们必须导入模块,如下所示:

import ipywidgets as widgets

要添加滑块,我们可以定义最小值和最大值、间隔大小(步长)、描述和初始值:

widgets.IntSlider(
    min=0,
    max=10,
    step=1,
    description='Slider:',
    value=3
)

Demo: Slider

展示它

display()函数在输入单元格中呈现一个小部件对象。
首次导入:

from IPython.display import display

然后在 display()函数中将小部件作为参数传递:

slider = widgets.IntSlider()
display(slider)

获取/设置其值

为了读取小部件的值,我们将查询它的value属性。类似地,我们可以设置小部件的值:

Demo: Value

链接两个小部件

我们可以通过使用jslink()函数来同步两个小部件的值。

slider = widgets.IntSlider()
text = widgets.IntText()
display(slider, text)widgets.jslink((slider, 'value'), (text, 'value'))

Demo: Linking

小组件列表

要获得小部件的完整列表,您可以查看文档,或者运行以下命令:

print(dir(widgets))

❷处理窗口小部件事件

小部件可以响应事件,这些事件是在用户与它们交互时引发的。一个简单的例子是点击一个按钮——我们期待一个动作发生。

让我们看看这是如何工作的…

根据其具体特性,每个小部件公开不同的事件。每次触发事件时,都会执行一个事件处理程序

事件处理程序是响应事件的回调函数,它异步运行并处理接收到的输入。

这里我们将创建一个简单的按钮,名为btn。点击按钮时会调用on_click方法。

我们的事件处理程序btn_eventhandler将打印一条带有按钮标题的短消息——注意处理程序的输入参数obj是按钮对象本身,它允许我们访问它的属性。

为了将事件与处理程序绑定,我们将后者分配给按钮的on_click方法。

btn = widgets.Button(description='Medium')
display(btn)def btn_eventhandler(obj):
    print('Hello from the {} button!'.format(obj.description))btn.on_click(btn_eventhandler)

Demo: Button Event Handler

让我们进入下一部分的是,输出出现在按钮本身所在的单元格中。因此,让我们继续看看如何为我们的笔记本电脑增加更多的灵活性!

❸控制小部件输出

在这一节中,我们将探索如何使用小部件来控制数据帧。我选择的样本数据集是“伦敦国际游客数量”,它显示了伦敦游客在住宿、参观和消费方面的总数,按年份、季度、目的、持续时间、方式和国家细分。

最初,我们将获取数据并将其加载到数据帧中:

import pandas as pd
import numpy as npurl = "[https://data.london.gov.uk/download/number-international-visitors-london/b1e0f953-4c8a-4b45-95f5-e0d143d5641e/international-visitors-london-raw.csv](https://data.london.gov.uk/download/number-international-visitors-london/b1e0f953-4c8a-4b45-95f5-e0d143d5641e/international-visitors-london-raw.csv)"df_london = pd.read_csv(url)

df_london.sample(5)

假设我们想按过滤数据帧。我们将首先定义一个下拉列表,并用唯一年份值的列表填充它。

为了做到这一点,我们将创建一个通用函数unique_sorted_values_plus_ALL,它将找到唯一的值,对它们进行排序,然后在开头添加ALL项,这样用户就可以删除过滤器。

ALL = 'ALL'def unique_sorted_values_plus_ALL(array):
    unique = array.unique().tolist()
    unique.sort()
    unique.insert(0, ALL)
    return unique

现在我们将初始化下拉菜单:

dropdown_year = widgets.Dropdown(options =    unique_sorted_values_plus_ALL(df_london.year))

dropdown 小部件公开了observe方法,该方法采用一个函数,当 dropdown 的值改变时,该函数将被调用。因此,我们接下来将创建 observer 处理程序,通过所选的值来过滤数据帧——注意,处理程序的输入参数change包含关于所发生的更改的信息,这允许我们访问new值(change.new)。
如果新值是ALL,我们移除过滤器,否则我们应用它:

def dropdown_year_eventhandler(change):
    if (change.new == ALL):
        display(df_london)
    else:
        display(df_london[df_london.year == change.new])

然后,我们将处理程序绑定到下拉列表:

dropdown_year.observe(dropdown_year_eventhandler, names='value')

Using a dropdown to filter a dataframe

到目前为止还不错,但是所有查询的输出都是在同一个单元格中累积;也就是说,如果我们从下拉列表中选择一个新的年份,一个新的数据框架将呈现在第一个数据框架的下面,在同一个单元格中。
理想的行为是每次刷新数据帧的内容。

捕获小部件输出

对此的解决方案是在一种特殊的小部件中捕获单元格输出,即Output,然后在另一个单元格中显示它。

我们将稍微调整一下代码:

  • 创建一个新的Output实例
output_year = widgets.Output()
  • 调用事件处理程序中的clear_output方法来清除每次迭代中的前一个选择,并在with块中捕获 dataframe 的输出。
def dropdown_year_eventhandler(change):
    output_year.clear_output()
    with output_year:
        display(df_london[df_london.year == change.new])

然后,我们将在新的单元格中显示输出:

display(output_year)

它是这样工作的:

Demo: Capturing output in a new cell

正如您所看到的,输出呈现在一个新的单元格中,过滤工作正常!👍

❹链接小部件输出

继续前面的例子,让我们假设我们也想通过目的进行过滤。

如果我们继续添加另一个下拉列表,我们将很快意识到数据帧只响应最近更改的下拉列表的过滤器。我们需要做的是将两者链接在一起,这样它就可以同时处理两个值(即年份和目的)。

让我们看看它应该如何工作:

首先,我们需要两个下拉菜单的公共输出:

output = widgets.Output()

这是两个下拉菜单:

dropdown_year = widgets.Dropdown(options =    unique_sorted_values_plus_ALL(df_london.year))dropdown_purpose = widgets.Dropdown(options = unique_sorted_values_plus_ALL(df_london.purpose))

然后我们创建一个新函数common_filtering,它将被两个事件处理程序调用。该函数将对两年的数据帧应用过滤器*AND*目的:
我们正在清除输出,然后我们检查是否有任何值为ALL,在这种情况下,我们认为相应的过滤器已被移除。当两个过滤器都存在时,在else语句中,我们在两个过滤器中应用&操作。最后,我们捕获输出:

def common_filtering(year, purpose):
    output.clear_output()

    if (year == ALL) & (purpose == ALL):
        common_filter = df_london
    elif (year == ALL):
        common_filter = df_london[df_london.purpose == purpose]
    elif (purpose == ALL):
        common_filter = df_london[df_london.year == year]
    else:
        common_filter = df_london[(df_london.year == year) & 
                                  (df_london.purpose == purpose)]

    with output:
        display(common_filter)

我们修改事件处理程序来调用common_filtering函数并传递change.new值以及另一个下拉菜单的当前value:

def dropdown_year_eventhandler(change):
    common_filtering(change.new, dropdown_purpose.value)def dropdown_purpose_eventhandler(change):
    common_filtering(dropdown_year.value, change.new)

我们将处理程序绑定到下拉菜单,就这样!

dropdown_year.observe(
dropdown_year_eventhandler, names='value')dropdown_purpose.observe(
dropdown_purpose_eventhandler, names='value')

代码片段:

Filter a dataframe based on two values

这是演示:

Demo: Filter a dataframe based on two values

❺创建仪表板

到目前为止,我们已经通过过滤和显示伦敦数据集的数据为我们的仪表板奠定了基础。我们将继续根据用户选择的值对数值进行着色。

一个有用的数字小部件是BoundedFloatText;我们将赋予它一个minmax和初始value,以及增量step

bounded_num = widgets.BoundedFloatText(
min=0, max=100000, value=5, step=1)

为了给数据框单元格着色,我们将定义此函数:

def colour_ge_value(value, comparison):
    if value >= comparison:
        return 'color: red'
    else:
        return 'color: black'

现在我们将最低限度地修改common_filtering函数为:

  • 添加新的num输入参数:
def common_filtering(year, purpose, num):
  • 通过为三个数字列调用colour_ge_value函数来应用样式:
with output:
        display(common_filter
                .style.applymap(
                    lambda x: colour_ge_value(x, num),
                    subset=['visits','spend', 'nights']))

需要调整现有的事件处理程序以通过bounded_num.value:

def dropdown_year_eventhandler(change):
    common_filtering(change.new, dropdown_purpose.value,
                     bounded_num.value)def dropdown_purpose_eventhandler(change):
    common_filtering(dropdown_year.value, change.new, 
                     bounded_num.value)

最后,我们将插入新部件的事件处理程序:

def bounded_num_eventhandler(change):
    common_filtering(dropdown_year.value, dropdown_purpose.value, 
                     change.new)bounded_num.observe(bounded_num_eventhandler, names='value')

代码片段:

Colour dataframe values

这是演示:

Demo: Colour dataframe values

测绘

接下来,我们将添加一个新的图表来绘制访问次数的基本单变量密度(KDE →核密度估计)。我们将使用 seaborn,所以让我们导入库:

import seaborn as sns
import matplotlib.pyplot as plt

继续之前的用例,我们将在一个新的输出变量中捕获图形:

plot_output = widgets.Output()

我们现在将修改common_filtering函数来绘制新图表:

  • 首先,我们清除输出:
plot_output.clear_output()
  • 然后我们通过传递访问次数来调用 seaborn 的kdeplot方法:
with plot_output:
    sns.kdeplot(common_filter['visits'], shade=True)
    plt.show()

最后,我们需要做的唯一一件事是在一个新的单元格中显示输出:

display(output)
display(plot_output)

代码片段:

Controlling a graph

这是演示:

Demo: Controlling a graph

❻仪表板布局

到目前为止,我们的用户界面是功能性的,但是占用了很多空间。

我们将首先水平排列输入部件。HBox将从左到右一次添加一个小部件:

input_widgets = widgets.HBox(
[dropdown_year, dropdown_purpose, bounded_num])display(input_widgets)

HBox

接下来我们将为输出创建一个容器Tab对此大有裨益。第一个选项卡将存放数据框架,第二个选项卡将存放图表。

tab = widgets.Tab([output, plot_output])
tab.set_title(0, 'Dataset Exploration')
tab.set_title(1, 'KDE Plot')display(tab)

Tab

最后,我们将用一个VBox将输入部件和选项卡堆叠在一起。

dashboard = widgets.VBox([input_widgets, tab])display(dashboard)

VBox

感觉有点“拥挤”,所以作为最后一步,我们将通过添加一些空间来打磨我们的仪表板。我们将定义一个Layout,在项目之间给出 50px 的边距。

item_layout = widgets.Layout(margin='0 0 50px 0')

我们将每个项目的布局称为:

input_widgets = widgets.HBox(
[dropdown_year, dropdown_purpose, bounded_num],
layout=item_layout)tab = widgets.Tab([output, plot_output],
layout=item_layout)

还有哒哒…我们的成品仪表板:

Dashboard

最终演示

Demo: Final Dashboard

PS :出于演示的目的,在一些演示中,我使用了数据集的一个子集,即:df_london = *df_london*.sample(250)

更进一步

您也可以使用一些第三方小部件,其中最流行的是:
二维图表: bqplot
三维可视化:pytreejs和 ipyvolume
映射: ipyleaflet 和 gmaps 。

您还可以构建自己的自定义小部件!欲了解更多信息,请点击查看。

概述

我们看到了各种各样的小部件,但是我们仍然只是触及了表面——我们可以使用 ipywidgets 构建非常复杂和广泛的 GUI。我希望你们都同意,它们应该在任何数据科学家的工具箱中占有一席之地,因为它们提高了我们的生产力,并在数据探索过程中增加了很多价值。

感谢阅读!

我定期在媒体上写关于技术的&数据——如果你想阅读我未来的帖子,请‘关注’我

将数据引入社区科学:《我的世界》版

原文:https://towardsdatascience.com/bringing-big-data-to-the-science-of-community-minecraft-edition-25938f43961a?source=collection_archive---------34-----------------------

Credit

看看今天的互联网,很容易让人产生疑问:对民主有利的梦想到底去了哪里?好吧,看看过去的大型社交媒体的丑闻和独裁政府的黑客的可怕表演,我认为仍然有希望的空间。网络上仍然充斥着自我管理的小实验。它仍在发生,也许悄无声息,但规模如此之大,以至于我们有机会,不仅仅是复兴网络的创始梦想,而是将现代科学方法引入到关于自我管理及其如何运作的基本千年问题中。

《我的世界》?《我的世界》。

这就是为什么我花了五年时间研究《我的世界》。《我的世界》,你或你的孩子或侄女在 10 年到 5 分钟前的任何时间玩的游戏,包括加入数百万个无限虚拟世界中的一个,并用立方体积木建造东西。《我的世界》没有情节,但叙事厌恶真空,所以人们使用游戏的基本机制来创造自己的情节,并在这个过程中使游戏成为有史以来最畅销的视频游戏。比俄罗斯方块还大。

《我的世界》的玩家和他们的作品一直是游戏中最引人注目的方面,但他们得到了一群自封的业余官员的支持,这些业余官员让《我的世界》因为一个非常不同的原因而变得特别。这些人是“运营”和管理员,他们做着吃力不讨好的工作,运行《我的世界》世界的每一个副本,以便它能足够好地为所有创造者创造。

事实证明,《我的世界》的特殊之处不仅在于它的开放式游戏,还在于它是“自我托管的”:当你与其他人一起玩一个世界时,它很可能不是由微软这样的大公司来维护,而是由一个业余玩家来维护,他们以某种方式将自己束缚在各种不酷的、非立方的工作中,编写规则、解决冲突、解决问题和养猫。我们习惯于将关键的挑战留给专业人员,事实上,您使用的大多数 web 服务都是由专门公开提供 CPU、RAM 和带宽的人管理的。但是有一个由业余爱好者运营的服务器社区的整个地下世界,在这个世界中,没有接受过治理培训的人,没有薪水,可能更喜欢做其他事情,接受建立和维护一个拥有共同愿景并为之共同努力的人的社区的挑战。当这种方式奏效时,不管这种愿景是否只是极客粉丝的一个小众产物——比如说,一个星舰企业号的逐块复制品——它都是鼓舞人心的。这些人没有接受过治理方面的培训,他们在自学建立治理机构。他们创造的每个世界都是政治实验。根据我的计算,20 个中有 19 个失败了,每一个成功和失败都是探索自我管理成为一门科学的奇迹数据。

Creating an inspiring Minecraft creation requires coordination and governance. Credit.

这是互联网的梦想在行动,特别是如果我们能把成功率从 1/20 提高到 5%。为了真正理解健康制度的决定因素,我们必须能够观察地球上 10 万个国家的兴衰。可惜地球只有几百个国家。在线社区是下一个最好的东西:他们给了我们进行大规模比较甚至实验的规模。管理他们并不像看上去那么简单。

作为资源治理机构的在线社区

《我的世界》服务器是一个有趣的例子:公共网络服务器。web 服务器是指某人用来提供 web 服务的计算机,可以是计算机游戏、网站、邮件列表、wiki 或论坛。作为计算机,web 服务器有其局限性:有限的处理能力(以千兆赫兹为单位)、内存(以千兆字节为单位)、带宽(以每秒千兆字节为单位)和电力(以每月$$$为单位)。未能充分提供这些意味着未能提供你的社区可以依赖的服务。作为一个几乎对任何人开放的无限 3D 多人虚拟世界,《我的世界》是资源密集型的,这使得这些挑战尤为严峻。

Commons depletion in Minecraft. Communities must manage CPU, RAM, bandwidth, vandalism, and also in-game resources. Credit.

任何能够在这种情况下茁壮成长的系统,尽管整个人类都可以使用,从冲动控制能力差的匿名青少年到专业黑客团队,都是在做一些特殊的事情。默认情况下,公共 web 服务器是“公共”的。加入你的小世界的每一个额外的用户或玩家都会增加它的负担。即使你的所有用户都是善意的,如果太多人做得太多,你的服务器也会停止工作,你的社区也会受到影响。当一种有价值的有限资源可供所有人使用时,我们称之为公共池资源,我们密切关注经典的公地悲剧:太多人获取太多,直到每个人都一无所有。

信息时代与市场交易的全球主导地位的巧合是,几乎每一项先进技术的应用都在走向让共有资源灭绝。任何能让小玩意变得更小或更便宜的东西,都会让私人更容易拥有,也更容易被那些把商品理解为你拥有和买卖的东西的系统识别。这可以追溯到像铁丝网这样的技术,它将西部荒野从造就牛仔的巨大牧场变成了一个可行的栅栏,将大片以前的荒地隔开,并允许私有财产的想法。(牛仔是常见的池资源管理者,他们在西部游荡,通过围捕将牛群带回给它们的主人。).像《我的世界》那样的私人服务器与这种说法正好相反。随着现代技术对公共领域的不利影响,每次你偶然发现由技术创造的公共领域时都很有趣。好像他们不会离开。

这带来了一个大问题。公地会消失吗?它们能被私有化和科技化吗?这是加密货币背后的自由主义意识形态的一个基础。但是赌注比最新的技术更高。

几乎每一位民主哲学家都说过,成功的自治不仅取决于良好的规则,还取决于拥有持有关键规范和价值观的成员。民主有几个众所周知的弱点,规范和价值观是其免受煽动家、独裁者、精英或暴民统治的唯一可靠保护。这种对文化的敏感性使得民主制度与市场、等级制度和专制制度形成对比,后者对胡萝卜和大棒的依赖使它们更加独立于价值体系。经济学家萨姆·鲍尔斯(Sam Bowles)区分了马基雅维利式和亚里士多德式的制度,即对最坏的公民稳健的制度,以及创造良好制度的制度。愤世嫉俗与文化驱动的机构。

让愤世嫉俗的机构变得愤世嫉俗的同样的事情也让它们变得容易分析、设计和工程化。我们已经变得善于建设它们,它们已经占据了世界秩序的顶端。这是他们的合法地位吗?在培养我的传统中,只有文化驱动的机构才能在不诉诸独裁的情况下应对管理公地的挑战。如果技术不能从我们的未来中获取公共资源,我们需要像工程市场和指挥链一样擅长工程文化驱动的机构。《我的世界》看起来只是一场游戏,但在它成功的背后是定义民主在 21 世纪角色的紧张局势。

不幸的是,那些让愤世嫉俗的机构容易建立和研究的因素同样让文化驱动的机构变得困难。有可能复制成千上万个层次结构并测试其变化:这就是特许经营:星巴克、麦当劳、复制、粘贴。相比之下,你在生活中发现的每一个鼓舞人心的参与性社区都是一片独特的雪花,其精华可能无法复制,无论是好是坏。

通过研究互联网上的自组织社区,无论它们出现在哪里,我们都利用了一个历史性的机会,将“科学”放在“政治科学”中,达到了一个曾经不可想象的程度。当你记录一个人试图在他们自己的小社会沙盒里扮演上帝时,你就是在实践历史。当你记录了一百万,你就是在实践社会物理学。我们可以看着数百万人试图建立他们自己的小乌托邦,看着他们成功和失败,区分坏选择和坏运气,确定在大多数情况下一个坏主意在其他地方什么时候会是好主意,并根据所有这些经验,建立制度有效性的一般理论。

有几个特点使得在线社区非常适合研究文化驱动的机构。他们的低准入门槛意味着他们的数量会更多。业余服务器也更透明,它们较小的规模使它们更简单,它们较短的数字记录的历史允许洞察制度变化的过程,并且它们提供已知软件的相同副本的事实使得进行苹果到苹果的比较成为可能,这使得地球上的国家的比较通过比较看起来像苹果到大象。

正式治理的产生研究

伟大的想法是好的,但是你必须把它们固定在某个地方。我开始了我的研究,问了一个更狭隘的问题:社区如何以及为什么在增加整合和正规化的方向上发展他们的治理系统?这是一个国家从哪里来的问题,官僚主义和规则。我们需要规则吗?有没有一个正确的方法来利用他们来治理国家?大人口和小人口有区别吗?为了回答这个问题,我写了一个程序,在两年的时间里每隔几个小时扫描一次互联网,访问社区以获得关于它们如何运行、谁访问它们以及这些访问者多久回来一次的信息。我将社区成功定义为核心群体的出现,即在一个月内每周至少一次返回特定服务器的玩家数量,从他们可能访问过的数千个社区中选择该服务器。因为一台服务器的典型寿命是 9 周,所以可以观察超过 15 万个社区的整个生命周期。每一个都从本质上相同的初始条件开始,一个矛盾的“无政府状态”,只有一个统治者,没有规则。每一个都是根据一个主权管理者对于是什么把人们聚集在一起的天真想法而演变的。随着这种意识的发展,管理员可以安装一些软件来实现治理的各个方面,包括私有财产权、同行监督、社会等级、贸易、通信等等。大多数人失败,一些人成功。

根据我的分析,大型社区越积极地应对各种各样的资源管理挑战,而且有趣的是,他们越是授权给唯一的管理员,它们似乎就越成功。领导力是成功社区的重要组成部分,尤其是在社区成长的过程中。当我们把焦点转向小社区时,这个故事变得更难与最喜欢的意识形态保持一致。事实证明,如果你的目标是管理一个 4 人社区,而不是 400 名普通用户,那么没有哪种治理方式明显比其他方式更有效:做一个专制者,做一个社会主义者,使用共识或骰子,在参与人数足够少的情况下,理论上不可能的安排在实践中也行之有效。

今后

这个项目表明,对大量有据可查的政治实验进行严格的比较,有可能理解治理成功的预测因素。这对参与性的、赋权的治理机构的未来非常重要。除非有效的社区建设可以简化为一个公式,否则有效的社区将是罕见的,我们人类将继续无法挖掘互联网的全部潜力,使文化驱动的机构成为主导我们互动的愤世嫉俗的机构的可扩展、可复制、可行的竞争对手。

每天都有更多关于侵犯我们的隐私和操纵我们的观点的坏消息,很难对互联网以及它将对我们机构的健康做出的贡献感到乐观。但是在后台勤奋工作的是整整一代年轻人,他们一直在训练自己设计和领导成功的社区。他们对于是什么让人们走到一起的感觉并不是来自于一个有魅力的人的高谈阔论,而是对他们自己过去没能让所爱的人走到一起的回应。他们能够识别新生独裁者的警告信号,不是因为他们读过过去的独裁统治,而是因为他们亲身经历了对一个小小虚拟王国的绝对权力的诱惑。随着科学家间接地学习这些经验,大规模的在线自治不仅有望培养出更多精明的民主捍卫者,还能为现实世界中健康、知情的参与式文化的设计和发展提供信息。

Seth Frey 是计算社会科学家,也是这项工作的主要作者。他在的发博客,发微博@ enf ascing

将 Colab 带到您身边的 Jupyter 笔记本电脑上…

原文:https://towardsdatascience.com/bringing-colab-to-a-jupyter-notebook-near-you-3f326f89fae7?source=collection_archive---------13-----------------------

Google 在 Colab 中构建了一些不错的功能。现在你可以从 Jupyter 笔记本中受益。

一直以来都是错误

当用 Python 编程时,当您在嵌套函数调用的堆栈中遇到错误时,这可能会令人不知所措。Python 的优势之一是它有大量高质量的可用库,并且这些库的源代码很容易获得。有时候,为了能够理解正在发生的事情,能够深入了解它是很有用的。

为了帮助您做到这一点,Google Colab 会在堆栈跟踪中自动显示指向源文件的可点击链接,以及一个方便的按钮来搜索堆栈溢出中的错误。如果 Jupyter 笔记本也能这样不是很好吗?我也是这么想的,所以我决定创建一个笔记本扩展来实现这个功能。

转到错误笔记本扩展

如果你还没有安装 Jupyter 笔记本扩展,你可以通过输入

pip install jupyter_contrib_nbextensions

从 GitHub 下载 Goto 错误代码

git clone git://github.com/teticio/nbextension-gotoerror

像这样安装它

jupyter nbextension install nbextension-gotoerror

最后,让它如此

jupyter nbextension enable nbextension-gotoerror/main

如果一切按计划进行,您应该能够在 Jupyter 记事本的 nbextensions 选项卡中配置 Goto 错误扩展。

由于 Jupyter 服务器只能访问运行它的目录或子目录中的文件,为了使笔记本能够打开源文件,有必要提供一个到源文件目录的软链接。例如,如果您不使用虚拟环境,在 Jupyter 启动目录中创建到您的 Python 安装的 site-packages 目录的软链接(例如~/lib/python3.6/site-packages),并将其称为site-packages。然后将 nbextension 配置中的前缀参数设置为~/lib/python3.6

如果您确实使用虚拟环境,那么将软链接指向envs目录,并相应地设置前缀参数。

要在 Linux 中创建软链接:

ln -s ~/.local/lib/python3.6/site-packages site-packages

要在 Windows 中创建软链接:

mklink -d envs C:\users\teticio\Anaconda\python\envs

它现在应该像下面的例子一样工作。

你还要吗?

Google Colab 还有其他一些很棒的功能(除了免费访问 GPU 和 TPU!).例如,如果你在行尾添加一个#@param注释,它会自动创建一个允许用户输入值的表单。例如

fred = 123 #@param {type : 'number'}

弹出一个有“弗雷德”和数字 123 的表格。例如,如果您将表单中的数字更改为 456,代码将更改为

fred = 456 #@param {type : 'number'}

我想为 Jupyter Notebook 开发一个做同样事情的扩展,当我在做的时候,把这两个扩展都移植到 Jupyter 实验室。请让我知道,如果这是你会使用的东西。

将数据带入生活—让他们通过数据可视化讲述他们的故事

原文:https://towardsdatascience.com/bringing-data-to-life-let-them-tell-their-story-with-data-visualisation-13e71f6553a6?source=collection_archive---------7-----------------------

有人可能会认为,数据科学,由于其名称以及与统计学和软件开发的密切关系,是一个纯粹的技术领域。但是当一个人深入挖掘现代数据分析的应用时,他们可能会发现清晰而公正地呈现分析结果几乎和分析本身一样重要。正确的数据可视化不仅需要一些高级图表类型的技术技能和知识,还需要绘制图片和用数据讲述故事的能力,这使它成为数据科学的一个“艺术”部分。

数据可视化在商业中起着特别重要的作用,在商业中数据分析被广泛应用。数据建模和分析的结果通常会影响利益相关者的想象力并激发他们的决策,因此他们正确理解分析的结果是至关重要的。然而,最高管理层很少对预测模型和统计有深入的了解(或者有时间去钻研它们的数学细节)。以清晰、全面和吸引人的方式直观地呈现数据,对他们以数据为导向的决策过程有很大帮助。在这篇短文中,我们提出了一些如何将数据转化为有效的、发人深省的故事的技巧。

了解业务

在商业领域,数据科学家的工作是通过数据分析帮助公司发展和解决问题。数据科学家可以对战略业务决策和公司发展方式产生巨大影响。随着数据科学对公司运营方式的影响越来越大,其对财务结果的影响也将增加。这意味着数据科学家了解他们工作的行业并了解他们公司的当前目标至关重要。他们需要知道最关键的问题是什么,以及可以应用什么实际手段来解决这些问题。这样,数据科学家的注意力将总是最有效地集中,他们的工作成果将在实践中适用。

消除杂乱

在一张图上放太多的视觉元素会使它变得复杂和难以理解。如果一个图表太混乱,你可能会失去你的听众的注意力,他们会专注于“解读”它,而不是听你想告诉他们的故事。这样,非但不会强化,反而会模糊你的信息。因此,你应该再三考虑你想在你的图表上放什么,以及每一项的目的是什么。确保你演讲的每一个元素都有助于观众理解你想告诉他们的信息。

Chart 1. The number of subscribers of two versions of a mobile app (illustrative data). Additional elements can help tell the story of the data. The vertical line in the graph above splits its area into two parts corresponding to time periods before and after the release of a new version of an application. Displaying number values only on the right-hand side of the line focuses the attention on what happened after the upgrade, and eliminates unnecessary clutter on the left-hand side.

将注意力集中在你想要的地方

为了集中你的观众的注意力,重要的是你要思考他们是谁,他们的背景是什么,以及他们如何看待这个世界。对于一个非常技术性的工程师小组,以及一个教室里的一群中学生,你可能会使用不同的图表。您可以使用大小、颜色、形状和在页面上的位置等属性,使图表引人入胜,并将受众的注意力吸引到您希望他们看到的区域。这将有助于你以你认为最好的方式引导他们完成你的演示。不要害怕偏离一些“标准”的数据呈现方式,或者添加额外的元素,如箭头、圆圈甚至文本框。如果有东西可以讲述你的数据的故事,那就使用它!

Chart 2. The share of opinions of 5 selected features of a mobile app (illustrative data). A stacked horizontal bar is a good choice for presenting percentage data in groups. Choosing the right order and position of groups (‘Features’ in the above graph), as well as color and order of bar segments (‘Dislike’/’Neutral’/’Dislike’), we can focus the audience’s attention on the selected elements and — more importantly — facts they represent. Note that in the above graph, using contrasting colors, and specifying numerical values highlights extreme bar segments, and tells the central message at first glance at the chart.

像设计师一样思考

在进行数据可视化的时候,把它当成你想卖给观众的产品。像设计师一样思考,并回答你的观众将如何与你的演示互动的问题。布局是否清晰?图表中是否有可能引起混淆的内容?随后的图表是否按逻辑顺序排列,从而创建一个易于理解的演示文稿?另一方面,观众是会对他们看到的提出问题,还是只听你的解释?他们可能会对你的工作进行评论或补充吗?

Chart 3. The number of subscribers, user satisfaction level, and average time spent daily in an application for selected cities, along with information about the cities’ population (illustrative data, except population values). Bubble chart can help tell a comprehensive data story with only one picture. Adding colors based on one of the data features to each of the bubbles, increases the dimensionality of the graph, broadening and enriching its message. Just remember to keep it all visually clear, and to add a complete legend, to keep your audience from getting lost in the abyss of the presented data.

讲故事

故事是神奇的。用一个故事与你的听众交流,并从情感上吸引他们。通过在演示开始时给你的听众一点提示,让他们对你将要演示的结果感到兴奋。但是不要一开始就告诉他们一切!悬念是抓住和保持注意力的关键。因此,在此基础上,让你的观众对接下来的内容感到好奇和兴奋。

此外,从更实际的角度来说,事先想想你的听众在看你的演讲时可能会有什么问题,然后把这些问题的答案融入到你的叙述中。这样你的故事会更完整,你的陈述会更少被打断。

总结:

每个数据集中都有一个隐藏的故事。你讲这个故事讲得有多好,你如何激励你的观众采取行动,都取决于你自己。请记住,要格外注意你在故事中绘制数据的方式,因为“一张图胜过千言万语”。伟大的故事激发伟大的思想。确保你的听众不仅记得他们所听到的,而且被数据可视化的神奇魅力所激励。

参考资料:

科尔努斯鲍默克纳弗利克,用数据讲故事。面向商业专业人士的数据可视化指南

作者:michahrabia,Miquido
的数据科学家,合著者/致谢: Karolina Holewa ,Miquido 的初级数据科学家

如何在亚马逊红移中模糊匹配数据集

原文:https://towardsdatascience.com/bringing-fuzzy-matching-to-redshift-d487ce98d170?source=collection_archive---------11-----------------------

使用 Python UDF 实现 Amazon 红移中的模糊匹配连接

Fuzzy Merging — Photo by Markus Spiske on Unsplash

如果幸运的话,当在数据仓库中处理多个数据集时,会有某种类型的连接列可用于您想要放在一起的表。

当这种情况发生时,生活是美好的。然而,现代大数据解决方案正在开辟一个用例,将来自不同来源的各种数据整合在一起。

虽然我们可以轻松地将这些数据存储在一个地方,但是将它们连接起来进行分析并不总是那么简单,因为这些数据集通常不是由同一个源系统生成的,所以要连接的干净的 ID 列并不总是可用的。即使为您提供相同信息的列也不总是以相同的格式提供,如果是用户捕获的,您永远无法保证一致性。

基于相似性将数据集连接在一起的一种方法是模糊匹配,特别是当您知道每个数据集中有基于文本的字段几乎相似时,例如用户输入的公司名称或产品名称。

这篇文章不会详细讨论模糊匹配的所有细节,但是会向你展示如何在 Redshift 中使用 Python 实现。

模糊匹配

在最简单的层面上,模糊匹配看起来产生两个事物有多相似的相似性分数。我将着重比较字符串来解释这个概念。

作为人类,我们很容易发现打字错误,或者在概念上理解两个相似的东西是相同的。模糊匹配算法试图帮助计算机做到这一点。两个字符串之间的匹配不是布尔真或假,即完全相同或不相同,模糊匹配给出的是接近度分数。

以“布鲁克林大桥”和“布鲁克林大桥”为例。即使在第二个字符串中有拼写错误,人类也很容易发现这是同一个东西。

一种模糊匹配算法,例如给出相似性百分比分数的 Levenshtein distance ,可能会将这两个字符串评分为至少 90%相似。我们可以使用它来设置我们希望“相似”的阈值,即任何两个模糊分数超过 80%的字符串都是匹配的。

Python 实现

这些天你可以找到很多 Python 包,所以我不打算重新发明轮子。一个体面的使用 Levenshtein 实现模糊匹配的 python 包是 fuzzy wuzzy

按照文档中说明的安装过程,您最终会得到一堆用于比较字符串的函数。

我在这个例子中使用的是一个简单的比率函数,它接受两个字符串并给出一个接近比率。这里有一个示例实现,展示了前面的布鲁克林大桥示例。

>from fuzzywuzzy import fuzz
>fuzz.ratio(“brooklyn bridge”, “brooklin bridge”)> 93

正如所料,这返回了一个相当高的分数,因为两者非常相似。

红移 UDF

用户定义的函数允许您使用 SQL 或 Python 向 Redshift 添加可重复的代码块。python 支持将允许我们采用上一节中的实现并添加到 Redshift 中,这样我们就可以像调用任何其他原生 SQL 函数一样简单地调用它。

首先,我们需要添加 fuzzywuzzy 库到红移。有一些完整的文档,但是我将在下面概述基本步骤。

  1. 从 github 下载 fuzzywuzzy 回购
  2. 在回购中复制一份 fuzzywuzzy 文件夹,并将其压缩。
  3. 将此压缩文件夹复制到 S3 桶中
  4. 在 Redshift 中运行以下命令来导入 fuzzywuzzy 库
CREATE LIBRARY fuzzywuzzy LANGUAGE plpythonu FROM 's3://<bucket_name>/fuzzywuzzy.zip' CREDENTIALS 'aws_access_key_id=<access key id>;aws_secret_access_key=<secret key>'

完成后,我们现在可以继续使用红移中的这个库来创建函数。

CREATE FUNCTION fuzzy_test (string_a TEXT,string_b TEXT) RETURNS FLOAT IMMUTABLE
AS
$$
  FROM fuzzywuzzy import fuzz 
  RETURN fuzz.ratio (string_a,string_b) 
$$ LANGUAGE plpythonu;

我们现在可以测试它,并检查我们看到的结果是否与我们在本地看到的结果相同。

SELECT fuzzy_test('brooklyn bridge', 'brooklin bridge');> 93

包裹

就这么简单。这是一个很好的特性,由于 Python 中可用的库的范围,Python UDF 给了你很大的灵活性。

由于红移星团的力量,这意味着大规模的模糊匹配是可能的,这可能永远不会在笔记本电脑上完成。然而…

如果您打算将它用于连接,需要考虑的一件事是,它显然会比通常慢,因为在连接上不会发生太多优化。因此,如果您要匹配大型字符串数据集,那么请做好等待的准备:)

脆弱的人工智能:渴望和僵化之间的联系

原文:https://towardsdatascience.com/brittle-ai-the-connection-between-eagerness-and-rigidity-23ea7c70cb9f?source=collection_archive---------21-----------------------

PatiencePhoto by Michel Porro on Unsplash

AI 为什么脆?

今天困扰人工智能的是脆性。有人推出了这个神奇的人工智能程序,它在一些任务上表现出超人的性能,但对输入的微小调整就让它屈服了。在识别图像中的物体方面表现出色的人工智能系统可以被欺骗,通过改变单个像素来看到不存在的长颈鹿。自动驾驶汽车,它尖叫着“AI!!!"比其他任何人都更容易被仅仅是贴纸的东西蒙骗,在错误的道路上行驶。

是什么让 AI 变脆了?我提出了一个被忽视的原因,我认为这是这种脆弱性的关键:对热切方法的严重依赖,这是我从编程语言中借用的术语。渴望是尽早决定,除了关闭选择和混合多种选择的大门。渴望是板球击球手丑陋的预先思考的镜头,是新生的高斯林将第一个移动的物体视为妈妈的“决定”,是阿尔伯特·爱因斯坦哀叹的信念诱导的认知固定性,当他提出 19 世纪物理学停滞的发生是因为物理学家“在经典力学中看到了所有物理学的坚实和最终的基础,是的,确实是所有自然科学的基础”。

在构建计算机系统的过程中,渴望表现为一种信念,让人联想到认知的固定性,即系统的某些方面是固有的,是一成不变的,因此我们必须在一开始就设计好,然后冻结。一些这样的假设是没有根据的,现有的系统没有这样的假设。例子包括“一种编程语言有固定的语法”“一个深度学习系统的神经网络有固定的形状”;现代编程语言和 AutoML 分别显示了这种假设的谬误。其他假设弥漫在今天的人工智能中,有些是因为它们看起来如此明显地固定,例如 backprop 中的损失函数,有些是因为将某些东西视为固定是方便的,有些是因为我们还没有想出如何改变这些。此外,我们急切地将假设融入到问题的定义中,正如我们希望我们的情感分类系统将电影评论分为积极和消极两类时所做的那样(但是,唉,评论作者不合作,他们添加细微差别,列出好和坏的方面,而不是一劳永逸地给我们一个好-坏光谱上的单点)。

脆性源于对语义不连续性的错误处理。社会学家 Eviatar Zerubavel 的优秀著作《细线》充满了任意不连续的例子:如果两个点相隔几英寸,它们之间有不同的法律,如果相隔几分钟,相隔几分钟的两个时间看起来不一样。加里·马库斯认为,常识是人工智能中缺失的成分。常识往往依赖于这种不连续性:动物在死亡前几分钟还活着,但在死亡后几分钟还活着,在鸡肉盘中放蔬菜是可以的,但在素食盘中放鸡肉却不行。

一种使用其对类似情况的了解来推理一种情况的技术——基于案例的推理和想到的向量空间表示——最好对相似性有高超的掌握。脆性将是一种技术,这种技术将两种食谱之间的相似性建立在共享成分的比例上,因为这种技术对单一成分越过的重要界限不敏感:围绕素食、围绕犹太食品、围绕无花生食品的界限。健康食品和不健康食品之间的界限更模糊,也更典型,但即使在这里,重叠的成分列表也可能与这种粗略划分两边的食品相同。

如果一个计算机系统对分界线两侧的两点使用相同的表示,我们说该表示对该边界不敏感,至少在那里是如此。渴望导致了这种代表性的崩溃,抹去了界限,如果它建立了什么样的区别需要关心,因此什么样的区别可以安全地忽略的话。

在这篇文章中,我从显著性决策和随之而来的代表性崩溃(1)开始,接着是渴望和僵化在两个领域的相互作用:动物行为中的固定动作模式(2)和编程语言(3)。并非所有的人工智能都渴望,许多最近的发展,如 BERT,AutoML 和 AlphaZero,比他们改进的系统更有耐心(4),但许多人工智能链,包括这三个,保留了大量的渴望和随之而来的脆弱性(5)。下一篇文章将描述展现出某种耐心的算法,从我与丹·罗斯和锡德·米塔尔的合作,到我与道格拉斯·霍夫施塔特在一个名为的认知架构上的合作。

我应该强调,我的目标不是诋毁渴望;渴望有它的用处,如果我们试图消除它,我们的工具箱会变得更穷。我的目标是指出效率和刚性之间的权衡,并建议如果我们理解脆性在哪里蔓延,那么在保持效率的同时保持灵活性是可能的。

1。显著性决策和表征崩溃

我们并不代表世界的全部细节——要代表的东西太多了,我们忽略了大多数微小的差异。对变化视而不见的现象证明了我们表征的简单性:我们不会注意到根本性的变化,甚至是如此彻底的变化,以至于我们刚才与之交谈的人被另一个人取代了。在一个变化盲的例子中,一名实验者向一名路人询问去一栋建筑的方向。在路人完成他的回答之前,另外两个实验者扛着一扇大门闯进了他们中间,在混乱中,其中一个扛门的人和原来的实验者交换了位置。令人惊讶的是,过路人并没有察觉到这种转换,从而强调了他所形成的不完整的表象。变化盲是一个经过彻底测试的强健现象,你可以在这里观看视频。

简而言之,我们关注对我们重要的事情。威利安·詹姆士很好地抓住了这一点:

让四个人去欧洲旅行。一个人只能带回家如画的印象——服装和颜色、公园和风景、建筑作品、图画和雕像。对另一个人来说,这一切都不存在;距离和价格,人口和排水系统的安排,门和窗户的紧固,和其他有用的统计数据将取代它们的位置。第三个会给一个丰富的剧院,餐厅,公共舞会,除此之外什么也没有;而第四将可能是如此包裹在自己的主观沉思,告诉比几个他通过的地方的名字。每个人都从大量呈现的对象中选择了适合自己兴趣的对象,并由此获得了经验。

通过选择重要的东西,我们含蓄地决定了我们可以忽略什么。仅仅在这些不重要的细节上不一致的截然不同的情况因此可以被一致地表示出来。一个普通的袖珍计算器的“记忆”功能就是一个极好的例子。计算器的内存只够存储三个数字,但它可以“跟踪”数百万个数字,并报告它们的总和、平均值和标准偏差。它实现这一壮举的方式是,它不是跟踪所有的数字——这些数字太多了,无法跟踪——而是记住三个量:它看到了多少个数字,它们的总和,以及它们的平方和。这三个总结足以计算出现在已经被遗忘很久的序列的标准差。
因此,不同的数字序列可能导致相同的状态。因此,如果它看到了数字 1、5、6、8 和 8,它会记住这五个数字,它们的和是 28,它们的平方和是 190。相反,如果它看到 3、4、4、7 和 10,它会达到相同的状态。因此,这两个不同的序列合并成一个单一的表示。注意,对于它支持的计算,这两个序列是等价的。对于不同的任务,比如说计算几何平均值,汇总表示是不够的。

这种极端的减少在计算器的正常使用中很难察觉,但也不是完全没有问题。它会导致计算器支持的一个操作出现奇怪的行为。为了告诉计算器要计算的一组数字,我们对每个数字按“M+”。另一个标有“M-”的按钮会删除一个数字,以防不小心输入了一个数字。在上面的例子中,如果我们从集合{1,5,6,8,8}中去掉 20(事实上它并不在那里,这个事实必然会在计算器上丢失),新的状态是:四个数字,它们的和是 8,它们的平方和是-210。奇怪的结果,因为平方和总是正的。稍微超出正常的使用范围,我们就会看到这个优雅而高效的算法中令人想起的漏洞。

原始的一组数字将永久丢失,并且无法从压缩版本中恢复。生成表示(即数字序列)所消耗的成分已经完全融合到最终的表示(三个摘要)中。这种不可逆转的崩溃对我的论点很重要,我们稍后将看到人工智能系统如何做出急切的显著决策,并丢弃“无关紧要的细微差别”,这些细微差别最终证明并不那么无关紧要。

在崩溃的“等价”情况下隐藏的危险是,尽管这种崩溃成为一种节俭的表现是有效的,但在现实世界中,等价是短暂的。在一个世界中,两个国际象棋位置可能是等价的——比如说,在标准国际象棋的世界中——但在一个略有不同的世界中,这些位置可能相距数英里——比如说,等级膨胀国际象棋,这是我发明的一种国际象棋变体,棋子不是在第八行提升,而是在第七行提升。或者,对于一个不需要改变规则的更轻微的变化,对于一个专业棋手来说,当与一个新手(他天生缺乏利用微妙弱点的专业知识)对弈时,两个位置实际上可能是相等的,但是当与卡斯帕罗夫对弈时,同样的两个位置可能是截然不同的。

2。极度渴望:基因中的选择

舞台魔术师兼哲学家丹尼尔·丹尼特喜欢指出,魔术对观众来说很难理解,因为当观众认为魔术开始时,魔术师已经完成了魔术。我们的一些“决定”和选择也可能发生同样的情况,这些决定和选择可能早在出生之前就已经为我们做出了,动物世界提供了一些很好的例证。

Sphex 黄蜂有一个奇怪的仪式,很好地适应了它的环境。道格拉斯·霍夫施塔特在哥德尔、埃舍尔和巴赫中引用了这种无脑者和智者的奇怪组合:

当产卵的时间到了,黄蜂 Sphex 为此建造了一个洞穴,并找到了一只蟋蟀,她用这种方式刺它,使它瘫痪但不杀死它。她把蟋蟀拖进洞穴,在旁边产下卵,关上洞穴,然后飞走,再也没有回来。在适当的时候,卵孵化,黄蜂幼虫以瘫痪的蟋蟀为食,它没有腐烂,一直被保存在黄蜂相当于深度冷冻的地方。对人类来说,这样一个精心组织的、看似有目的的例行程序传达了一种令人信服的逻辑和思考的味道——直到更多的细节被检验。例如,黄蜂的惯例是把瘫痪的蟋蟀带到洞穴,放在门槛上,进去看看一切正常,出来,然后把蟋蟀拖进去。如果蟋蟀被移动了几英寸远,而黄蜂正在里面进行初步检查,黄蜂从洞穴出来后,会把蟋蟀带回门槛,但不是在里面,然后会重复进入洞穴的准备程序,以确保一切正常。如果当黄蜂在里面时,蟋蟀又被移开几英寸,她会再一次把蟋蟀移到门槛上,重新进入洞穴做最后的检查。黄蜂从没想过把蟋蟀直接拉进来。有一次,这个过程重复了四十次,总是得到同样的结果。

黄蜂在这件事上没有真正的选择。它不能选择不验证陋居的安全性——这个决定在她出生前几千年就已经做出了,编码在她的基因中(或者更准确地说,是基因与黄蜂的宏观和微观环境的相互作用,但这并没有改变我的论点)。

这种硬编码行为很常见。在斑尾鹟养父母的巢中,一只新生的早期孵出的布谷鸟,通过有目的地滚动和投掷巢中的蛋来杀死它未孵化的鹟“兄弟姐妹”。与其说它选择这样做,不如说它感到懊悔。另一个著名的例子是灰雁本能地与它们看到的第一个移动物体结合——同样不是有意识的选择。

我们可以说这种固定的行动模式在动物的大脑中是根深蒂固的。它们不同寻常之处在于它们相对来说不受环境的影响。不变行为是可预测的,这可能导致它们被人类或其他动物利用。如果环境改变了,这样的行为就变得不适应了。因此,在复杂动物中生存下来的固定行为对于生命来说是必不可少的,或者在速度(即缺乏学习)至关重要的地方。一个这样的例子是人类新生儿的吮吸反射,当父母的贿赂和哄骗不太可能有效时,它保持良好的喂养。

如前一节所述,等效是短暂的,我们看到情况的不良影响被隐含地视为等效,因此值得同样的反应。正如诺贝尔奖得主尼科·丁伯根用银鸥展示的那样,银鸥的父母喙上有一个红点,当被雏鸟啄时,父母会给雏鸟喂食,雏鸟会强迫性地啄任何红点甚至黑点,即使那些点是在棍子上,而不是在喙上。上面画有圆点的棍子在自然界是不存在的,没有理由增加任何机械来处理那种情况。

在基因中编码行为是热切的。进化预测到了——很有能力,但没有理解——一个物种的动物可能面临的一些情况,并将某些触发因素与特定行为联系起来。这种行为可以拯救生命,并且可以有效地实施,但对于我们人类乐于称之为智能的东西来说,这是不够的。

对大脑发育和性别决定有耐心

这些例子来自《依赖基因》一书(感谢@freerecall 推荐!).

在胚胎成长为成人的过程中,大脑与各种“外围设备”相连:眼睛、耳朵、四肢等等。但是对于每种有多少,信息是如何存储的呢?令人惊讶的答案是,也许不是,这种联系是机会主义的。大脑抓住一切可以利用的东西,通过它们的化学特征吸引潜在的目标;没有连接起来的神经元得不到“营养因子”化学物质并枯萎。如果更少(因为意外或实验操纵),它们被连接起来,每个得到更多的大脑处理;如果有更多,那也很好。不需要硬编码“分支数量”——这可以是运行时的决定。

同样,我们认为个体的性别是固定的和固有的。然而,对于珊瑚鱼来说,一只雄性鱼控制着一群雌性鱼。当雄性死去,最大的雌性,在几个小时内,变成雄性,完成精液的产生。

软件解决方案

与硬编码动作模式的盲目能力相比,“软件解决方案”提供了一个更灵活的系统来组织从刺激到动作的映射。这里的类比是,计算机硬件可以支持不同的软件。如果我们可以将触发器与一些内部符号挂钩,并将它们与动作挂钩,而不是将触发器和结果动作硬连接在一起,这种松散耦合将像在计算机编程中一样实现间接性,我们将在下一节中看到这一点。人类婴儿并不是生来就被硬编码为英语、汉语或原始印欧语,但是我们可以在这些设置中的任何一个或几个设置中对其进行配置。这样的配置并不急切——其环境的特殊性将允许婴儿说英语或克林贡语。一个出生时就有任何特殊天赋的婴儿在当今世界将会是一个不适应的人。

进化并没有预料到当今世界所需要的许多职业能力:小鸡性别鉴定师、爵士乐教练、迪斯尼世界的公主、保险承保人等等。目前还不清楚如何将基因和蛋白质表达的正确序列连接起来,以产生这些职业所必需的行动模式。这种灵活性似乎在很大程度上归功于语言和形成抽象、命名具体和抽象事物以及引用这些抽象的能力。接下来,我们将看到一组几乎相同的附加功能是如何在编程语言中实现灵活性的。

3。耐心使编程语言更加灵活

我们倾向于假设任何编程语言都有固定的语法。事实上,传统编程语言的语法被正式指定为语法,任何不符合这种严格语法的程序都是无效的,不会被编译。由于其固定的语法,即使研究 C++程序的一个很小的片段,将它分割成记号,并将这些记号分类为变量、函数调用或其他东西,都是可能的。

这种“一种语言一种语法”的假设被一些所谓的动态编程语言打破了——或者说粉碎了。在 Perl 这种语言中,程序员可以切换到完全不同的语法,甚至在一个文件中混合几种语法。标准的 Perl 语法充斥着符号%、$、和@,而且词序很重要:像任何其他编程语言一样,语句“a=b”不同于“b=a”。虽然 Perl 的设计者没有预见到用格式良好的语法拉丁语(通过-um 和-o 这样的大小写结尾实现词序的流动性)编码的渴望,但他们预见到了 Perl 的灵活性。漫威在达米安·康威的大师展示了这个完全合法的 Perl 程序的拉丁语编程,它使用被称为厄拉多塞筛的算法一个接一个地打印所有的质数。正如康威博士指出的,如果你必须问为什么有人可能希望用拉丁语编程,答案对你来说不太可能有意义。

use Lingua::Romana::Perligata;
maximum inquementum tum biguttam egresso scribe.
meo maximo vestibulo perlegamentum da.
da duo tum maximum conscribementa meis listis.
dum listis decapitamentum damentum nexto
fac sic
    nextum tum novumversum scribe egresso.
    lista sic hoc recidementum nextum cis
    vannementa da listis.
cis.

在我们考虑是什么实现了这种灵活性,这种中途改变语法的能力之前,让我们看两个进一步的例子,对比传统语言的刚性和 Perl 的流动性。

首先是变量的类型的概念(即变量是否存储整数、字符串等等),以及传统语言坚持在运行程序之前(用技术术语来说,“在编译时”)知道每个变量的类型,这种坚持导致它还要求知道每个数组的类型,并且该数组的所有成员共享一个类型。相比之下,Perl 是悠闲的,在编译时不需要知道变量的类型。当它运行那一行代码时,它只需要知道类型,如果一个数组包含不相关类型的混合,没有问题。

第二个区别要深刻得多,因为它涉及到语言如何在内存中表示对象,如何为这些对象分派方法,如何构造一个类的新对象。这些问题是语言设计的核心,对于任何语言来说都是不可避免的。但是对于像 Perl 和 Python 这样的语言,使用所谓的元类,即使这样我们也可以改变,我们可以在一个程序中自由地混合几种类型的对象。在这里,我们也看到,我们认为对于特定语言来说是铁板一块的东西,实际上是可以混合搭配的。

所有这些因素使得用动态语言编写代码变得更加简单。这种灵活性可能会有问题,但这不是我们在这里关心的问题。问题是,对于由数百名程序员执行的超大型项目,如果不同的程序员选择不同的方法来实现相同的目标,这种灵活性可能是灾难性的。正如 Perl 的创造者拉里·沃尔喜欢说的那样,Perl 给了你足够的绳索来搬起石头砸自己的脚。但是,我们不要让这分散我们对手头业务的注意力,理解灵活性的起源。

顺便说一下,在编程语言中,早和晚的区别有好几个名称。“早期”通常被称为急切或编译类型或静态,而“后期”被称为懒惰或运行时或动态。

刚性来源

传统编程语言的大部分刚性来自于静态分析——在编译过程中,在将程序翻译成机器代码的同时对其进行分析。这个过程自然是“早期”的——在它运行任何代码之前。这个过程删除了“不相关的”信息——它通过将“等同的”程序映射到相同的机器代码来折叠它们。

正如您可能猜到的,不相关的信息对于实现上面讨论的灵活性是必不可少的。那么这个不相关的信息是什么呢?名字。变量名、函数名和类名。除了替换这些名称之外,两个无法区分的程序将产生相同的机器代码。

但是等等。只有当我们不能以任何方式引用变量名时,两个具有不同变量名的程序才是等价的。那么,在这些编程语言中,我们不能写“对于每个名字包含 x、的变量,将值增加 1”。

这里的刚性来自于强加的等价。通过将这种等价性分割开来,仅仅通过存储变量名和数据类型并允许通过名称引用它们,新的编程技术就变得可用,包括“动态分派”,它选择在运行时而不是在编译时调用函数。

使用变量、函数和类的名称来定位它们的能力打开了一个全新的可能性领域:现在可以在运行时创建新的函数和类,反过来又可以创建更多的函数和类,令人厌烦。在我们的日常生活中,我们创造新的概念,随着概念在语义空间的任何区域迅速增长,我们也看到新的单词在那里出现,因为如果我们要使用它们,能够引用概念是必要的。我们也在“运行时”创造概念和新词。

有一个普遍的原则,有时被称为“软件工程的基本定理”,并归功于大卫惠勒:“我们可以通过引入一个额外的间接层来解决任何问题。”有时这句话会被修改,加上“除了过多间接引起的问题”。在这里,间接指的是两个事物是如何联系在一起的:直接,或者通过一个中间标签,或者一系列这样的标签。

我们在这里看到的灵活性来自于间接性:我们不是将一个对象(比如一个名为 lion 的变量)连接到我们可以调用这个对象的方法(比如 roareat 等等),而是将这个对象连接到一个中间抽象(比如 animal ),然后连接到这些方法。这样,我们可以通过单个中介将几个对象连接到这些方法,并且改变中介的特性是影响许多变量的一种廉价方式。这种添加单词、名称或符号的好处的概念在安迪·克拉克的论文 Magic Words 中得到了呼应,他在论文中指出了单词可以增强我们计算能力的几种方式,包括基于其他概念构建概念的能力。

4。今天耐心等待 AI

最近的三项人工智能进展之所以超过了它们的前辈,是因为它们推迟了某些选择。

从 W2V 到 Elmo 和 BERT

所谓的“语义嵌入”将单词映射到向量空间中的点,将相似的单词映射到附近的点。直到几年前,最先进的系统还在使用上下文无关的嵌入:它将单词 jaguar 的每个实例映射到同一点。尽管一个是动物,另一个是汽车,但每个句子中的美洲虎都接收到相同的向量:“美洲虎幼崽咕噜咕噜”和“美洲虎经销商咕噜咕噜”。

ELMo 是最近推出的一个提供上下文嵌入的 NLP 系统,它更有耐心。它一直等到在句子中看到这个单词,才产生一个向量。因此,这两个捷豹将获得不同的表现。W2V 将所有的美洲虎折叠成相同的表示,心照不宣地忽略了各种美洲虎之间的“无关紧要的差异”——动物、汽车、吉他、战斗机、阿兹特克战士、杰克逊维尔的足球运动员等等——而 ELMo 跳过了这种急切的折叠。

如果我们相信 NLP 基准,ELMo 和它的继任者 BERT 在各种各样的任务上表现得非常好。

从香草深度学习到 AutoML

普通的深度学习项目从精确定位要使用的架构开始。架构的选择是一个早期的决定,还有几个超参数的设置。现在,通过参数网格搜索,甚至通过模型空间的搜索,我们可以推迟这样的决定。

从启发式计算机象棋到 AlphaZero

深度学习的早期承诺之一是学习功能。无人监管的方法,如自动编码器,是承诺的乌托邦,将把我们从手动识别特征和手动调整权重中解放出来,通过允许“数据自己说话”,提供最稳健的权重集。特征工程是昂贵的和繁重的,并且需要技巧,如果有成千上万的问题我们想要解决,为每一个设计特征是行不通的。

现在最好的“传统”国际象棋系统是 Stockfish。它利用启发式方法来评估棋盘位置:它评估每个位置对白棋的有利程度,以厘泊为单位,领先 100 厘泊相当于拥有一个额外的棋子。为了评估一个位置,它会检查一些方面,如物质优势,兵的发展,国王的安全,两个主教是否幸存,打开的文件,一个骑士是否在前哨,等等——有数百个组成部分。在许多世纪的游戏中,人类玩家已经认识到这些方面的重要性。

Stockfish 是一个非常强大的玩家。它与中村光对弈,后者是 FIDE 国际象棋比赛中闪电战和快速棋类最高级别的棋手。电脑玩的时候有一个障碍:它在缺了一个棋子的情况下下黑棋,但却打败了人类冠军。

尽管它很强大,但在 2017 年,一个更先进更强大的 Stockfish 版本严重输给了谷歌的 AlphaZero。AlphaZero 没有从启发式规则开始,而是使用神经网络作为评估函数。人们可以把手工调整的试探法列表看作是急切的——对重要问题的早期决定。相比之下,AlphaZero 将重要的事情推迟到以后选择,从而使游戏更加灵活,更好地适应它在训练中探索的国际象棋位置的空间区域。

这些耐心的方法结出了果实,但这些(和其他深度学习解决方案)有一个共同的热切核心,我们接下来将讨论这个核心。

5。今日 AI 急切

让我们暂停一下,考虑一下前面讨论过的这些系统表现出的耐心:编程语言 Perl ( 3)、ELMo、AutoML 和 AlphaZero ( 5)。每一个都取代了系统中看似内在的东西,由于这种内在的东西在整个系统中是一致的,因此必须在一开始就决定。这些不可侵犯的单色外观被一种色彩丰富的拼布所取代,其选择可能会被推迟。Perl 不同意语法是固定的,或者整个程序用一种语法编写,或者只有一种最好的方法来表示对象和方法分派的工作方式。被认真对待的 Perl 的座右铭贯穿了它的设计:“有不止一种方法可以做到”,缩写为 TIMTOWDI,拉里·沃尔的 Twitter 昵称是@TimToady。AutoML 不相信网络的形状是固定的(因此我们不必在任何工作开始之前决定形状),AlphaZero 不相信一个单一的象棋位置评估函数(因此可以跳过选择),ELMo 不相信对某个单词的所有实例使用相同的向量。

从优秀的老式人工智能到现代深度学习系统,人工智能系统已经做出了急切的决定,这些决定被硬编码到他们的程序中。这些不胜枚举,我仅列举几个。

热切选择的表征和语义原子主义

有意义的原子可以结合起来形成,或者一个人被引导去相信,任何和所有的意义。我最早接触 AI 的一个领域是罗杰·尚克(Roger Schank)的“概念依赖”(Conceptual Dependency)系统,该系统试图用少量的原语(如 PTRANS(物理运动)、MTRANS(信息的运动)和 ATRANS(抽象转移,如给予)来表示所有的意义,包括完整的故事。那么,这是一个不到 100 个有意义原子的清单。在更大的范围内,今天的 WSD 系统倾向于也是意义的库存,并且每个术语有一组固定的含义。他们可能会为某个术语列出 4 个意义,而为另一个术语列出 10 个意义,但通常每个术语都有一个固定的集合,并且该集合的成员是离散的。另一个中等大小的感官库存是 WordNet。如今,也有免费的基础和知识图。

人类的概念不是这样工作的,将几个原子结合到我们所拥有的感官的细微层次中。我很熟悉“太阳底下没有新东西”这句话,以及所有思想都是先前思想的混合物或类似的说法,但新概念可以而且确实会产生。这是如何发生的将是另一篇文章的主题,重点是亚瑟·库斯勒、迈克尔·阿加尔、吉勒·福康尼耶和马克·特纳等人的想法。

当像 WordNet 这样的系统被用作人工智能系统的目标输出时,我们已经对可能的含义集做出了早期的决定。更糟糕的是,这些意义不符合人类的意义,人类的意义显示出灰色和等级隶属关系的阴影,以及在适当的时候通过隐喻、转喻或类比延伸意义的显著能力。

一组有限的意义意味着有限数量的歧视,许多有意义的边界被抹去。

监督与强化:固定损失函数还是固定效用函数

人们的一生,甚至一个小时,都不会有固定的损失函数。我们的目标变化很快,有些受饥渴等因素的影响,有些受吸引我们注意力的事情的影响,有时我们只关注正在进行的任务中的一个子问题。随着目标的改变,成功的标准也会改变。

我没有在训练中替换损失函数的建议,但我不认为应该只有一个。

固定输出类

我把我认为 NLP 中最大的问题留到最后:期望输出的固定和有限的范围。许多任务是分类任务,实际上是问“输入属于这三个类中的哪一个?”。具体的任务可能是“将句子分类为正确的情感,可以是这四种中的一种”或者唯一稍微开放一点的“在输入句子中找到一个双关语(顺便说一句,在 WordNet 中是双关语)”。

为什么我认为这些限制是急切的,为什么我们会变得脆弱?考虑将产品评论分为两类的任务,正面的负面的,想象你已经训练了一个系统来处理这个问题。但是由于负面并不是一个铁板一块的类别,它实际上是由许多重叠的原因形成的,这些原因使评论变得糟糕:也许产品很糟糕,也许卖家不知何故搞砸了,很粗鲁,等等,这根本不会反映在产品上。系统没有压力分别表示否定的这两个子类——这两个之间的表示会崩溃。在一个一切都是积极或消极的世界里,消极的子类之间的区别无关紧要。消除这些区别会使系统概括得很糟糕,并阻止它学习抽象概念,例如“如果一个评论在一个维度上是正面的,但在另一个维度上是负面的,我们就称之为正面的。”

我能坚持下去

上面的例子仅仅触及了表面。我们还没有讨论我的导师道格拉斯·霍夫施塔特(Douglas Hofstadter)在认知的计算模型方面的工作,他在那里展示了认知和识别是如何相互交织、相互依存的,这是一个论点,即表征不应该是先验固定的。例如,当我们着手一个研究项目时,我们可能甚至不知道我们应该向自己提出的精确问题,而要测量的精确数量只有随着时间的推移才会变得清楚。同样,这篇博文一开始是一个非常简单的想法,经过六次重大修改,抛弃了许多文本,杀死了许多宠儿,它达到了目前的形式,与我想象的相去甚远。

如果我们希望我们的计算机最终具有创造力并能够深刻理解,我们还有很长的路要走,而这段旅程将需要许多层面来解开我们目前认为是港口甚至是固体陆地的锚。

布鲁克林九九遇上数据科学

原文:https://towardsdatascience.com/brooklyn-nine-nine-meets-data-science-d846a901e12c?source=collection_archive---------24-----------------------

探索布鲁克林九九的试播集的 Seaborn 可视化

这份工作(数据科学家)正在活生生地吞噬我。我不能再呼吸了。这些年来我一直想做个好人,戴白帽子的人。我没有变得像他们一样…我就是他们——杰克·佩拉尔塔,飞行员

我最近疯狂地看了网飞的一个名为的节目,我真的很喜欢。当我急切地等待下一季的发布时,我认为对《T4》试播集进行探索性的数据分析和情感分析会很有趣。

我在网上找到了脚本,并将文本提取为 CSV 文件格式。您可以在我的 Github 资源库中找到本文的所有代码。

我们开始吧!

试播里哪个角色台词最多?

Of course, Jake has the most lines

Image of Jake Peralta, played by Andy Samberg

考虑到杰克·佩拉尔塔是我们的主要主角和主角,他在该剧的试播集里拥有最多台词是有道理的。

接下来,我们将探索并可视化这一集的单词分布。

试播中哪些人物平均用词最多

Gina… What does this mean?

非常有趣…虽然 Gina 的台词比 Jake 少,但当她有机会说话时,她平均使用更多的单词。

试播中哪些人物使用最多的词语**

现在我们已经很好地理解了单词在试验中的分布,我们可以继续下一步了。我们应该清理我们的文本,看看我们的角色在对话中是否使用了有意义的词语。

Image of Gina Linetti, played Chelsea Peretti

clean过程包括:

  • 标记和降低文本
  • 删除标点符号
  • 删除字母数字字符
  • 移除停用字词(不提供意义的常用字词,如'或' a' )

每个字符的总字数与干净字数

有意思。即使采用我们的clean流程,吉娜平均仍在使用更多的单词。

她的秘密是什么?

我们应该通过探索情感分析来更深入地研究文本分析。情感分析是一种非常流行的文本分析方法,用于确定文本中单词的态度或情感。

Image of Captain Raymond Holt, played by Andre Braugher

一些流行的情感分析工具包括:

  • 维德情绪
  • AFINN 词典
  • 文本 Blob

有关情感分析的更多信息,请查看下面的链接:

** [## 情感和情绪分析:NLP - KDnuggets 实践指南

情感分析被广泛使用,特别是作为任何领域的社交媒体分析的一部分,无论是商业,还是文化

www.kdnuggets.com](https://www.kdnuggets.com/2018/08/emotion-sentiment-analysis-practitioners-guide-nlp-5.html)

试播中普遍情绪如何?

我们人物台词的感悟是什么?

最后一步,我们现在可以深入单词本身。我们来看看杰克、吉娜和霍尔特船长使用的最常见的词语。

杰克最常用的单词是什么?

吉娜最常说的话是什么?

霍尔特最常用的词是什么?

耶,我们完成了!如果你喜欢这个,请随时查看我下面的参考资料。

参考资料:

【飞行员】布鲁克林九九。2013 年 9 月 17 日。电视。

[## 如何用 Python 清理机器学习的文本

你不能直接从原始文本去拟合机器学习或深度学习模型。你必须清理你的文字…

machinelearningmastery.com](https://machinelearningmastery.com/clean-text-machine-learning-python/) [## 水平条形图- seaborn 0.9.0 文档

编辑描述

seaborn.pydata.org](https://seaborn.pydata.org/examples/horizontal_barplot.html) [## 飞行员成绩单

我们从杰克·佩拉尔塔走出汽车开始。我们听到了他的画外音,当他跳过一些磁带,走向…

brooklyn99.fandom.com](https://brooklyn99.fandom.com/wiki/Pilot_Transcript) [## 非常简单的 Python 脚本,用于从故事中提取最常见的单词

人们经常需要快速检查和绘制像故事这样的大型文本文件中最常用的单词。怎么做?

towardsdatascience.com](/very-simple-python-script-for-extracting-most-common-words-from-a-story-1e3570d0b9d0)**

分类问题中变量选择的强力技术

原文:https://towardsdatascience.com/brute-force-variable-selection-techniques-for-classification-problems-5bca328977e5?source=collection_archive---------7-----------------------

变量选择是建立准确可靠的预测模型的重要一步,需要大量的创造力、直觉和经验。你一定看到过数百篇讨论变量选择技术的文章,其中很多都是关于理解数据,从统计学的角度感受数据。检查哪些变量具有低方差,哪些变量彼此之间以及与目标变量之间高度相关,等等。所有这些都是有用的技术,除非您遇到一个包含大约 20k 个变量的数据集,而没有全面的元数据!我以数据科学家的身份为一家大型汽车公司工作,并试图使用 ADAS(自动驾驶辅助系统)生成的内部信号建立一个模型。这些信号通常包括不同的速度、加速度、与被检测物体的距离、制动踏板状态和系统的许多状态变量。

现在,我有许多需要预测建模的用例,但我们将考虑其中一个,以便我们可以更好地关联。ADAS 有一个碰撞警告系统,每当它检测到可能与附近的物体发生碰撞时,就会发出警告。该警告通常是正确的或真实的,但有时它给出错误的警告,这对驾驶员来说是不方便的。我们的目标是建立一个模型,可以预测碰撞警告是否是假的。让我们不要被问题所左右,把注意力集中在数据上。我们的目标变量是二元的,我们有大约 20,000 个变量,包括连续变量、分类变量和离散变量。

因此,基于我们的需求,我们的变量选择技术将有两个方向——我们需要能够向最终用户详细解释我们的模型,并陈述我们的模型所基于的变量吗?还是我们只想得到最大的准确性?如果是前者,我们需要根据变量的预测能力或对目标变量的影响来筛选变量。

点双列相关

如果目标变量是二元的,点双列相关是选择变量的一个好方法。点双列相关用于测量一个连续变量和一个二分变量之间的关联强度和方向。这是皮尔逊积差相关的一个特例,适用于有两个连续变量的情况,而在这种情况下,其中一个变量是名义二进制变量。和其他相关系数一样,点双列的范围从 0 到 1,其中 0 表示没有关系,1 表示完全关系。

我们用目标变量计算每个连续变量的系数,然后根据阈值系数(例如 0.5)选择最高的高度相关变量。使用这个,我能够从我的数据集中的大约 15,000 个变量中过滤出 50 个变量。

卡方

下一项技术是卡方检验——用于检验两个事件的独立性。如果给定两个事件的数据集,我们可以得到观察到的计数和预期的计数,这个测试测量两个计数相互之间的偏差。卡方统计通常用于测试分类变量之间的关系。卡方检验的无效假设是总体中的分类变量之间不存在关系。你可以在这里阅读更多关于如何进行测试的信息:https://www . statistics solutions . com/using-chi-square-statistic-in-research。所以,我不会深入测试的细节,但这个想法是使用测试过滤独立的分类变量。

特性差异

另一种用于过滤掉不会给我们的模型增加价值的变量的技术是方差阈值方法,该方法移除方差不满足某个阈值的所有特征。该方法可以在上述任何方法之前使用,作为一个起点,并且与目标变量没有太大关系。通常,它会移除所有零方差特征,这意味着所有样本中具有相同值的所有特征。

特征重要性

我们可以使用基于树的分类器模型的特征重要性特征来获得每个特征相对于目标变量的重要性:

我们可以基于特征重要性选择前 n 个特征,这对于我们可能想要使用的任何分类算法都会给出好的结果。

线性判别分析

接下来,让我们看看一些转换技术,在这些技术中,我们不能保留原始变量,但可以获得更高的精度。LDA 和 PCA 是两种这样的技术,尽管已知 PCA 在许多情况下表现良好,但我们将主要关注 LDA 及其应用。但是首先,让我们快速看一下 LDA 和 PCA 之间的一些主要区别:

当类别标签已知时,线性判别分析在多类别分类任务中通常优于 PCA。然而,在某些情况下,PCA 表现得更好。这通常是当每个类的样本量相对较小时。一个很好的例子是图像识别技术中使用的分类精度之间的比较。线性判别分析 Python 有助于将高维数据集降低到低维空间。我们的目标是做到这一点,同时在类之间有一个体面的分离,并减少资源和计算成本。我们可以使用 LDA 类的解释方差比属性来决定最适合我们需要的组件数量。看一看:

如果我们对保留原始特征集不感兴趣,LDA 提供了一种很好的方法来将我们的数据转换到选定数量的新维度空间,这是分类算法的更好输入。

现在,让我们把我们为非转换情况讨论的所有技术放在一起,作为代码形式的一系列步骤:

函数 variable_selection 返回一个新的 dataframe,其中只包含通过各种过滤器后选择的列。应该仔细选择函数的输入,例如方差阈值、点双列系数阈值、卡方统计阈值和特征重要性阈值,以获得期望的输出。

请注意,这只是一个变量选择的框架,而本质上在很大程度上是一个手动练习。我写这段代码是因为我觉得很多过程可以自动化或半自动来节省时间。我的建议是使用这个函数作为起点,对特定的数据集和问题陈述进行任何您认为合适的更改,然后使用它。

祝你好运!

布宜诺斯艾利斯自行车道:让我们一起去兜风

原文:https://towardsdatascience.com/buenos-aires-bicycle-lanes-ii-1a40b13ccc25?source=collection_archive---------18-----------------------

Riding a bike in BA. Isn’t the Women’s Bridge beautiful?

谁不喜欢在公园里骑自行车呢?布宜诺斯艾利斯人(“porteos”,这个词指的是我们的港口),显然喜欢它。

在本文中,我们将继续进行一些分析,目的是识别和理解隐藏在布宜诺斯艾利斯 Ecobicis 公开数据中的社会模式。

该数据集提供了自行车站点的地理定位和以下方面的数据点:

  • 谁(当然是匿名的)骑了一辆自行车。
  • 他们从哪里拿走的,又放在哪里。
  • 确切的时间。
  • 全程旅行。

我的好朋友费德·卡塔拉诺谈了很多关于这个数据集的特征,并展示了一些不错的统计分布。它是西班牙语的,但你不需要懂这种语言就能欣赏美丽的情节。

他关注的是上下班的人。我们要分析一些隐藏在数据中的社会聚类。也就是说,我们将尝试检测一起使用自行车的人群。

考虑到这个目标,我们应该将数据限制在人们将自行车归还给他们从那里拿走的同一个站点的事件上。通过这种方式,我们清理了数据,只考虑与人们骑自行车出行相关的事件。

一个人去兜风很有趣,但我也很喜欢朋友和我一起去。为了检查其他人是否同意我的观点,我们建立了一个图表,其中两个用户共享一个链接,当且仅当他们中的至少一个人在几乎相同的时间从同一个车站骑走了一辆自行车,并一起归还(也是到同一个车站)。

5%的事件是人们把自行车归还到他们把自行车拿走的地方。那些事件。90%的人是一个人去的。但是另外 10%的人是一起去的。由此,模式开始出现。

网络具有幂律度分布,就像大多数真实世界的网络一样。

The person with 15 different friends to go for a ride with must be having a great time!

我们还可以检查人们的年龄与友谊的关系。不出所料,年龄相同的人更有可能一起出去玩:

Heat map for relations based on age and gender.

也许你希望看到家庭,但不幸的是,孩子们很难为这项服务创建用户。在我们的实验中,最小的人是 15 岁。

我们从图表中可以观察到的是 40 岁以上的男女关系的强烈模式,这是同性友谊所缺乏的。也许,由于异性性关系在统计上比其他性关系更丰富,我们所看到的是已婚夫妇在一起的快乐?我打赌我们是!

团体

如果我们把网络分成相连的部分,结果是——正如你可能怀疑的那样——许多人只是成对外出,但也有许多团体!我们找到了 300 个 5 人以上的小组。连通分量的大小分布也遵循幂律,最大的组是大小为 195 的异常值。这是:

Bicycle users and friendships.

  • 节点颜色等于性别,红色为雌性,蓝色为雄性。
  • 节点大小与年龄成正比。
  • 边缘宽度与共享的行程量成比例。

查看我的笔记本(即将推出)了解其他小组。好,再来一个:

That pair are really good friends!

然而,连接组件并不是检测社区的好方法(也许我会在另一篇文章中详细讨论)。

检测社区的一个好方法是卢万算法。对于这个项目,我们使用了简单有效的 Python 实现。结果如下——你可以想象成群结队的骑自行车者在布宜诺斯艾利斯各处享受他们的闲暇时光:

Cyclists’ communities — social interaction is everywhere

这些社区在地理上是如何分布的?它们是如何随着时间进化的?更多将在新的职位。

附录

最后一件事。费德里科是社会科学家,我是数学家。但是,为了这个项目,我们交换了帽子。如你所见,他处理了数据集的大部分数字分析——他在这方面很棒——而我处理了隐藏的社会模式。

永远不要害怕换帽子,只是去兜风。

Credit: gastonroel

通过机器学习构建免佣金的 Algo 交易机器人季度收益报告[完整指南]

原文:https://towardsdatascience.com/build-a-commission-free-algo-trading-bot-by-machine-learning-quarterly-earnings-reports-full-b414e5d759e8?source=collection_archive---------11-----------------------

简介

以下是一个完整的指南,将教你如何创建自己的算法交易机器人,它将根据美国上市公司提交给 SEC 的季度收益报告(10-Q)进行交易。我们将涵盖从下载历史 10-Q 文件,清理文本,并建立您的机器学习模型的一切。ML 模型的训练集使用来自这些历史归档的文本和归档后的第二天价格动作作为特征标签。

然后,我们下载并清理每天的 10-Q 文件,并使用我们训练有素的模型对每个文件进行预测。根据预测,我们将自动在该公司的股票代码上执行免佣金交易。

属国

0.我们的项目代码:

https://github . com/plator solutions/quarterly-income-machine-learning-algo

1.基础依赖:Python 3.4,Pandas,BeautifulSoup,yfinance,fuzzywuzzy,ntlk

对于这样的项目,Python 一直是我首选的语言,原因与许多其他人选择它的原因相同——快速的开发、可读的语法和大量可用于大量任务的高质量库。

Pandas 是任何数据科学项目存储、操作和分析数据框架表中的数据的经典之作。

yfinance 是一个 python 库,用于从 Yahoo Finance 检索股票价格。

fuzzywuzzy 为创建我们的 10-Q 差异提供了模糊文本相似性结果。

Ntlk 允许我们根据句子拆分 10-Q 报告的文本。

2.羊驼免佣金交易 API(https://alpaca.markets/)

我研究并尝试了几个股票经纪人的解决方案,这些经纪人声称为散户交易者提供 API。羊驼是最容易使用的,文档最清晰。(我与他们没有关系)我测试的其他经纪人有:

互动经纪人

在网上搜索,这些家伙似乎有一个在该领域的“黄金标准”的声誉。然而,在测试他们的软件时,它真的让大多数零售交易 API 的可悲状态大开眼界。事实证明,他们没有你所期望的实际 API,而是一个令人震惊的古老桌面应用程序,供你安装,然后库自动控制桌面应用程序。

他们的文件杂乱而复杂。在几次尝试运行他们的示例代码和尝试一些测试交易后,我可以说,将 IB 作为 algo 交易机器人的稳定部分是可能的,但这本身就是一个重要的项目。

TD Ameritrade 思考或游泳

很明显,ToS 的 API 比互动经纪人的更新更好用。然而,同样明显的是,这不是一个成熟的解决方案。虽然我确实让它工作了,但是甚至使用 API 的初始认证过程也很奇怪,并且需要在各种论坛上找到的未记录的信息才能让它工作。交易执行 API 本身看起来很容易使用,但是关于它们的书面文档非常少。

3.谷歌云 AutoML 自然语言

由于其作为搜索引擎的业务性质,Google 自然在 ML 自然语言处理方面拥有丰富的经验和投资。在尝试了其他几种 ML 技术和解决方案后,Google 的商业解决方案产生了模型的最佳准确性,同时提供了一种足够容易使用的解决方案,该项目不会陷入对各种 ML 算法进行无休止的手动调整和测试的学术练习中。

测试了其他 ML 库:最初我尝试了以下 ML 库,并从归档文本中创建了一个二元模型包用作特性集:h2o.ai、Keras、auto-sklearn 和 AWS Sagemaker。

这种技术的一个大挑战是,对来自二元模型包的文本进行矢量化,会为训练集的每个数据点创建大量的特征。有各种技术可用于处理这种情况,但预测质量可能会或不会有不同程度的损失。

4.Python-edgar :一个整洁的小 Python 库,用于批量下载 SEC 历史文件列表(https://github.com/edouardswiac/python-edgar/)

第一步。下载历史 10-Q SEC 文件列表

为此,我们将从 https://github.com/edouardswiac/python-edgar/下载并安装 python-edgar

使用 run.py 将目录切换到文件夹,并使用以下命令下载历史归档:

>python run . py-y 2010

我选择下载近 10 年的数据(从 2010 年开始)来构建我们的 ML 模型。您可以一直下载到 1993 年,也可以用不同的年份参数少下载一些。

完成后,我们可以将结果编译成一个主文件:

>猫。tsv > master.tsv*

现在使用 quarterly-earnings-machine-learning-algo/download _ raw _ html . py 下载我们刚刚创建的索引中列出的所有原始 html 10-Q 文件:

>python download _ raw _ html . py path/to/master . tsv

这将需要大量的时间来运行,因为它将从 SEC.gov 网站下载许多 GB 的数据。每个文件平均有几十兆的数据。当它完成时,我们将有一个文件夹"./filings" 包含所有文件的原始 html。

文件名的格式为: _ _ _ 。超文本标记语言

第二步。清理锉屑

我们将运行 quarterly-earnings-machine-learning-algo 项目中的以下命令,将每个文件中的 html 清理为准备好的文本:

>python filing _ cleaner . py

这将使用上一步中创建的“归档”目录中的 html 文件,并输出到包含已清理文本文件的“清理 _ 归档”目录中。

这将进行以下清理,为自然语言处理准备文本:

  1. 条带 HTML
  2. 删除通常包含定量财务数据的大部分数字表格
  3. 移除链接
  4. 去除空白
  5. 删除有限数量的停用字词(特定日期、数字等)
  6. 规范化文本

这也将根据美国证券交易委员会用来识别公司的 CIK 号码来查找交易的股票代码。

文件名的格式为: _ _ _ _ 。文本文件(textfile)

第三步。下载财务数据

在这一步中,我们下载公司在每次 SEC 备案后当天的股票市场开盘价,然后下载备案后两天的开盘价。我们使用这个百分比价格变化作为我们想要为我们的机器学习数据点预测的目标标签。最初,我创建了一个算法,以开盘价和当天收盘价进行交易。然而,在实时交易中使用它需要一个日内交易账户,对于一些读者来说,25,000 美元的最低账户可能是遥不可及的。

此外,我们将使用的 SEC 备案文件限制在收盘后提交的文件。在盘后发布季度收益是大多数公司普遍采用的做法。我们有大量的数据点可以使用,所以我们将把它限制在这些数据点上,因为对市场时段发布的季度收益使用价格行为会在我们的数据样本中产生异质性。

如果在运行这个命令之前您还没有安装 yfinance,请使用 pip 来安装它。

>python add _ financial . py

它从上一步创建的“cleaned_filings”目录中读取带有股票代码的文件名,并输出 financials.pkl,这是一个 Pandas 数据帧,包含每个文件的所有次日价格变化。

第四步。从公司最近的 10 季度报告中产生每个季度收益的文本增量

在这一步中,我们将获取每一份清理过的季度收益报告,并逐句对该公司上一份 10-Q 文件进行模糊区分,以删除也出现在他们上一份文件中的文本。这是一个重要的步骤,它去除了大量多余的文本,并创建了一个清晰的报告,说明该公司自上一季度收益报告以来增加了什么。

这为我们建立机器学习模型创造了一个非常干净的信号,因为只有公司认为足够重要的信息才会添加到他们的最新文件中,才会成为我们训练数据的一部分。

记得在运行之前使用 pip 安装 nltk 和 fuzzywuzzy 依赖项。

>python diff _ cleaned _ filings . py

该命令将从“cleaned_filings”目录中获取已清理的文本文件,并输出“whole_file_diffs”目录中每个已清理文本文件的文本增量。

第五步。准备我们的训练数据的 CSV 以上传到 Cloud AutoML 自然语言

现在,我们将整理 10-Q diffs(培训功能),并将其与第二天的价格变化(培训标签)一起编译成 CSV。我们将通过在每个第 20 百分位将价格变化分成 5 个时段来创建谨慎的培训标签。因此,我们的 0 桶将有底部的 20%(最显著的价格下降),我们的 4 桶将有顶部的 20%(最显著的价格上升)。

python cloudml _ prepare _ local _ CSV . py

这将在当前目录中输出一个名为“training_data.csv”的文件,准备上传到 Google。

第六步。在 Cloud AutoML 自然语言上上传、训练和评估我们的 ML 模型

如果你还没有谷歌云平台账户,你可以在这里注册:【https://cloud.google.com/free/

一旦你有了一个帐户,我们就可以在这里访问 Cloud AutoML 自然语言控制台:https://console.cloud.google.com/natural-language/dashboard

在这里,我们将点击“自动情感分析”。虽然我们不是在分析文本的情绪,但本质上,我们将使用股票价格反应作为季度收益报告“情绪”的衡量标准,将此建模为情绪分析问题。

单击“new dataset”,选择情绪分析,并将最大情绪得分设置为 4,因为我们有在上一步中创建的 5%价格变化时段。

我们现在将导入在上一步中创建的 CSV 文件,然后单击 import。导入完成后,您将收到一封电子邮件。然后,您可以返回到仪表板,并单击培训。如果你有任何问题,你可以参考谷歌的文件在这里:https://cloud . Google . com/natural-language/automl/sensation/docs/

该模型将需要几个小时的训练,当它完成时,我们会收到一封电子邮件。然后我们可以分析结果:

这里我们看到了模型的混淆矩阵。如果你不确定如何解释,你可以谷歌一下“困惑矩阵”。我们可以看到,我们实现了近 30%的准确率和召回率,而由于我们有 5 个存储桶,随机机会应该是 20%。那比随机几率好 50%!

如果我们看一下情绪得分为 0 的结果(记住这是价格变化最负的 20 个百分点),我们会看到我们得到了最好的准确性。当我们使用我们的模型预测季度收益报告将产生最剧烈的价格下跌时,我们将有 35.63%的时间是正确的。这比随机机会好了 75%以上!

我们用这个桶做得最好是有道理的。它告诉我们,当一份季度收益报告包含某些负面指标语言时,它将更可预见地产生戏剧性的股价下跌。这符合一句古老的谚语“卖新闻”。

第七步。下载今天的 10-Q 文件,进行在线预测,并开始交易!

注册羊驼经纪人账户开始交易:【https://alpaca.markets/

他们允许你快速方便地在纸币账户上交易,而不需要输入任何银行信息,这非常好。我们可以用这个纸币账户开始。

注册后,您需要从仪表板中检索您的 API 密钥:

我们还需要从谷歌控制台检索我们的模型名称。在与上一步相同的仪表板上,单击您训练过的机器学习模型。

转到“测试和使用”选项卡。单击“使用您的自定义模型”下的“Python”选项卡。记下上面用黑色圈出的型号名称。

我们现在可以使用以下命令行参数运行我们的命令:

> python MakeTrades.py <羊驼 API 密钥 ID > <羊驼秘钥> <谷歌型号名称>

该命令将:

1.从美国证券交易委员会网站https://www.sec.gov/cgi-bin/current?q1=0&Q2 = 1&Q3 =下载最新的上市日 10-Q 文件

这应该只在交易日晚些时候运行,因为这是当天所有文件都可用的时候。如果你想早点运行它,它会给你昨天的文件。

2.清理每一份 10-Q 文件,并与公司最近的 10-Q 文件进行比较,就像我们在培训准备中所做的那样。如果该公司在过去 3 个月左右没有 10-Q 文件,它将跳过它。

3.提交文本 delta,用我们的 ML 模型做一个在线预测。

4.如果我们的模型返回的预测值为 0(它预测的是最剧烈的价格下跌类别),那么它将使用羊驼 API 为该股票下一个空头订单,该订单将在第二天的开市时执行。

你应该记得在持有空头头寸一天后平仓。如果你愿意,你可以为此写一个脚本。您还可以将该命令与 cron 作业一起安排在每个交易日结束时运行,以实现完全自动化。

第八步。翻转它来生活,让金钱滚滚而来

希望,这个指南是有价值的,可以被使用,并扩大到现场交易盈利。如果有什么不清楚或者你需要任何帮助,请联系我。

注来自《走向数据科学》的编辑: 虽然我们允许独立作者根据我们的 规则和指导方针 发表文章,但我们不认可每个作者的贡献。你不应该在没有寻求专业建议的情况下依赖一个作者的作品。详见我们的 读者术语

原载:【https://www.platorsolutions.com/】T21

我可以为贵公司的任何项目提供软件咨询。点击此处免费预约初诊:https://www.platorsolutions.com/book-online

为快乐和利润建立一个公司 R 包

原文:https://towardsdatascience.com/build-a-corporate-r-package-for-pleasure-and-profit-78b73ce4ff4b?source=collection_archive---------12-----------------------

我们的经济正在进行“大重组”。这是我们所知道的正在发生的事情的官方名称:最好的正在上升到顶端,而平庸的正在沉入底部。这是马太效应在起作用。

在 Brynjolfsson 和 McAfee 2011 年的著作与机器赛跑中,他们详细描述了这种新经济将如何有利于那些拥有技能或资本来对接和投资深度学习和机器人等新技术的人,这些新技术正变得越来越普遍。

Cal Newport 的深度作品概述了在新经济中蓬勃发展的两个核心能力:

1.能够快速掌握难的东西

2.能够在质量和速度方面达到精英水平。这种对速度的需求(抱歉)将是我们关注的重点。

不要重复自己(干)是软件开发中众所周知的格言,大多数 R 程序员都遵循这个规则,构建函数以避免重复代码。但是你多久会:

  • 在不同的分析中引用相同的数据集
  • 创建到数据库的相同 ODBC 连接
  • 在 ggplot 中修补相同的颜色和主题
  • 从同一模板生成降价文档

诸如此类?注意到模式了吗?“同”字撒在每一个要点里。我嗅到了干敷的机会!

如果你像我一样在公司或学术机构工作,你可能会经常做这些事情。我将向你展示如何将所有这些任务打包成一个极简的 R 包来节省你的时间,正如我们所知,这是你在新经济中取得成功的关键之一。

工具

首先做一些基础工作。如果你用 R 工作,我假设你用的是 RStudio,这是必须的。我用的是 Windows 10 机器上的 R 版本 3.5.1(啊,美国公司…)。请注意,我们将要开发的包是极简主义的,这是一种说我们要抄近路来制造最小可行产品的方式。我们不会深入讨论文档和依赖项,因为我们在新包中需要的包很可能已经在您的本地机器上了。

创建空的包项目

我们将为咨询公司 Ketchbrook Analytics 创建一个包,这是一家来自康涅狄格州的精品店,他们比任何人都更了解自己的道路。

打开 RStudio 并在新目录中创建一个项目:

选择 R 包并命名。我叫我的凯彻。

RStudio 现在将使用示例“hello”函数启动一个新会话。看来我们准备好进入正题了。

自定义功能

让我们从向我们的包中添加一个函数开始。Ketchbrook 的一项常见任务是将客户数据与市场区域或足迹的轮廓对应起来。我们可以很容易地把它包装成一个简单的函数。

创建一个新的 R 文件,并将其命名为 ketchR.R。我们将把所有的函数放在这里。

所以我们所做的是创建一个函数,利用 tigris 包来获取我们足迹中各州的 shapefiles。然后,该函数将这些状态合并成一个连续的多边形,这样我们就可以很容易地使用传单、ggmap 等来覆盖它。

尝试你的新功能:

你会看到一个漂亮的西部小海岸:

您可以在包中添加的自定义函数的种类没有限制。机器学习算法,客户细分,无论你想要什么,你都可以在你的包中放入一个易于访问的功能。

数据集

让我们继续我们的地理空间爱好。分公司或商店级别的分析在分布于一个大的地理区域的公司中很常见。在我们的例子中,Ketchbrook 的客户从提华纳到西雅图有八家分行。每次我们需要引用这些位置时,不用手动存储和导入 CSV 或 R 数据文件,我们可以简单地将数据集保存到我们的包中。

为了将数据集添加到我们的包中,我们首先需要通过读取 csv 或从其他地方获取数据集,将它放入我们的本地环境中。我只是从本地电脑上读取了一个 csv 文件:

这是数据集的样子:

现在,我们必须将这些数据放在一个非常具体的地方,否则我们的包将无法找到它。就像当我妻子把洗碗机藏起来的时候,我被迫不情愿地把脏盘子放在柜台上。

首先,在当前目录中创建一个名为“data”的文件夹顺便说一句,你的目录现在应该是这样的:

额外好处:使用 RStudio 中的终端功能可以轻松创建目录:

现在我们需要将这个分支数据集作为。RData 文件:

现在,我们建造

让我们测试一下这个包,因为我们很有可能没有搞砸任何事情。当我们构建这个包时,我们正在把它编译成我们所知道的实际的包。在 RStudio 中,这非常简单。导航到“构建”选项卡,然后单击“安装并重启”

如果您已经完成了,您应该不会看到任何错误,但是如果您确实看到了错误,请尝试更新您的本地包。

现在,我们应该能够直接调用我们的包并使用我们的分支数据集:

酷,这很有效。现在让我们用传单快速绘制我们的分支,以确保 footprint_poly()有效:

尼利斯。

数据库连接

数据科学中最常见的任务之一是从数据库中提取数据。假设 Ketchbrook 将数据存储在 SQL 服务器中。与其手动复制和粘贴连接脚本或依赖 RStudio 会话来缓存连接字符串,不如让我们直接创建一个该死的函数。

在这里,我们构建了一个函数,让我们可以输入任何我们想要针对这个 SQL Server 的查询。该函数创建连接,每次都提示我们输入密码(我们不在代码中存储密码……),并在连接完成时关闭连接。

让我们更进一步。很多时候,您可能会使用一个通用的 SELECT *查询来利用 dplyr 进行真正的数据管理。在这种情况下,更简单的方法就是创建一个这样的函数。

让我们创建另一个从客户那里提取 SELECT *的函数。

啊,当我开始在自己的实践中使用它时,仅这一项就为我每周节省了 15 分钟。仔细想想你可能经常复制和粘贴的任何一段代码——那是你的函数包的候选者。

品牌 ggplot 可视化

好了,现在我们进入了最重要的部分,真正节省时间的部分,我们一揽子计划中给分析师留下深刻印象的部分。我们将使产生一致的数据可视化变得容易,这些数据可视化用定制的颜色和主题来反映公司的形象。

虽然我个人认为绿色调色板是有史以来最好的配色方案,但它并不一定符合凯奇布鲁克公司的调色板。所以让我们自己制作一套函数来使用凯奇布鲁克的调色板是一种‘懒惰’的方式。(非常感谢这篇 Simon Jackson 的伟大文章)。

获取颜色

让我们直接从他们的网站上下载颜色。我们可以使用 Chrome 插件 Colorzilla 来获取我们需要的颜色。

将这些十六进制颜色代码粘贴到这个块中,就像这样:

这将给我们一个很好的调色板,对于分类数据有足够不同的颜色,对于连续数据有足够相似的颜色。为此,我们甚至可以将它分成两个单独的子调色板:

创建函数

我不打算一行一行地介绍这些函数;如果您有任何问题,请发邮件至 bradley . lindblad[at]Gmail[dot]com 联系我,在 Github repo 上创建一个问题。以下是完整的代码片段:

让我们来测试一下:

生产:

很好。在 Ketchbrook 的视觉效果中使用一致的颜色将有助于为他们的品牌建立知名度,并在市场上更快地得到认可。

降价模板

现在我们已经更快地获取数据并绘制数据,最后一步是交流我们的分析结果。同样,我们希望能够快速、一致地做到这一点。定制降价模板已准备就绪。

我发现这一部分是最难做对的,因为所有的东西都需要在文件结构中的正确位置,所以请密切关注。(大部分功劳归于切斯特·伊斯梅的这篇文章。)

1.创建框架目录

这将创建一个嵌套目录来保存我们的模板。Rmd 和。yaml 文件。您的目录中应该有一个名为“ketchbrookTemplate”的新文件夹:

2.创建骨架。中矢状直径之比

接下来,我们创建一个新的 RMarkdown 文件:

这将为我们提供一个基本的 RMarkdown 文件,如下所示:

现在,让我们修改模板来满足我们的需求。首先,我会用一个我觉得很适合我的主题来替换顶部的内容,你可以随意撕掉它:

我喜欢遵循分析模板,因此这是结合我的基本 EDA 模板的首要问题:

将这个文件保存在 skeleton 文件夹中,我们就完成了。

3.创建 yaml 文件

接下来,我们需要创建一个 yaml 文件。只需在 RStudio 中创建一个名为“template.yaml”的新文本文档,并保存它,如下图所示:

重新构建包并打开一个新的 RMarkdown 文档,选择“From Template ”,您应该会看到您的新模板可用:

太好了。您现在可以编织 html pretty,并得到如下甜美的输出:

如果遇到问题,请确保您的文件结构与此匹配:

 ├───inst
│   └───rmarkdown
│       └───templates
│           └───ketchbrookTemplate
│               │   template.yaml
│               │
│               └───skeleton
│                       skeleton.nb.html
│                       skeleton.Rmd

下一步是什么?

因此,我们基本上做了一个炸弹包,让你做任何事情都快一点,好一点:拉数据,引用公共数据,创建数据和交流结果。

从这里,您可以在本地使用这个包,或者将它推送到一个远程 Github 仓库,在您的团队中传播代码。

这个包的完整代码可以在为它设立的 Github repo 获得。随意分叉,让它成为自己的。我不擅长说再见,所以我要走了。

我可以在有限的基础上提供数据科学咨询。通过 gmail[dot]com 联系我

感谢阅读!

在 AWS 上构建一个数据管道来收集 PUBG 数据

原文:https://towardsdatascience.com/build-a-data-pipeline-on-aws-to-collect-pubg-data-5b222ba4e2cc?source=collection_archive---------11-----------------------

Photo by Quinten de Graaf on Unsplash

我在与 PUBG 相关的 Kaggle 竞赛中开始了这个项目,目标是根据一些比赛结束时的统计数据(如比赛中跑的距离等)来预测比赛中的球员排名。我想了解数据源(看看是否有一些故意遗漏的统计数据/信息),并尝试亚马逊的一些服务,如 AWS Glue 和 Athena,以在 AWS 中建立一个数据管道。

在这篇文章中,我将解释:

  • 什么是 PUBG,游戏的原理等等
  • 项目的方法
  • 数据的收集和清理

PUBG 的描述

PUBG 是一款名为 battle royale 的视频游戏类型的旗舰之一(首先也是最受欢迎的之一),主要是 100 名玩家(或多或少)被丢在一个岛上,没有装备,他们必须通过收集材料,武器,装备和车辆在这个岛上生存。

为了促使人们去战斗,随着游戏的进行,可用的区域变得越来越小,所以这种形式促使人们互相战斗,因为最终只能有一个幸存者。有两个非常受欢迎的游戏支持这一运动,它们是 PUBG 和堡垒之夜,它们可以在所有可能的平台上使用(主机上的 PUBG 不好),现在你可以看到所有其他受欢迎的游戏(使命召唤,战场)都想在里面添加自己的皇家战役模式,但让我们诚实地说,堡垒之夜正在击败所有人(查看谷歌趋势 PUBG VS Fornite)

我对这类游戏的看法是,原则很酷,你可以在游戏过程中建立一些紧张的令人惊讶的情况,但我认为你不能出售(全价)一个只是皇家战役的游戏。Epic 对堡垒之夜很聪明,因为他们通过提出一个免费的独立应用程序,将一个制作时间长且销售情况不好的项目变成了一个非常有利可图的项目,该应用程序使用的资产与原始游戏相同,但只专注于皇家战役(我希望有想法的人能加薪)。

让我们看看数据收集的项目。

项目方法

这个项目背后的想法是:建立一个系统,从 PUBG 公司提供的 API 收集数据清洁数据,使所有的数据可用,而不是下载我的机器上的一切

为了建立管道,我决定使用 AWS,因为与微软、谷歌等相比,我更喜欢他们的服务,但我确信同样的系统也可以在这些平台上建立。

为了完成任务,我决定使用以下服务:

  • EC2 实例运行一台机器来进行收集
  • S3 存储收集到的数据并进行处理
  • 粘合使数据清晰可用
  • Athena 是 S3 数据存储的接口

这是 AWS 上不同步骤的流程部署的高级视图。

我们来详细看看过程。

从 PUBG API 收集数据

PUBG 公司已经建立了一个非常好的开放的 API,有多个端点开放不同的数据源。为了简单起见,您可以访问与以下内容相关的数据:

  • 由他们的帐户 id 定义的 PUBG 播放器
  • 由其 matchid 定义的匹配

有来自不同地区(美洲、欧洲和亚洲)的多个平台(pc、xbox 和 ps4)的数据。在这篇文章中,我将重点介绍 PC 平台和北美地区。这个 API 有多个端点,但让我们把重点放在:

  • 样本端点:提供对每天更新的一些 matchid 的访问
  • 匹配端点:给出了匹配的细节,比如最终结果,更重要的是下载事件压缩包的链接。

你可以找到一个仓库,里面有我构建的收集、清理和发送数据的函数。这个笔记本能让你深入了解收集到的数据。有三种类型的数据:

  • 结束比赛统计数据,它们存储在 s3 存储桶的“文件夹”中
  • 存储在另一个文件夹中的完整事件
  • 存储在单独文件夹中的分解事件

关于事件包,API 上有多个可用的事件,有一个模式包含这些事件的详细信息。

我计划每天在 EC2 实例上运行一个脚本,该脚本触发了从样本中获取新数据,并将其存储在正确的文件夹和 day 文件夹中。

为了获得一般信息,比赛的细节被收集到一个 dynamodb 表中(比如人数,地图名称等)。

当你存储数据时,我能给你的一些建议是:

  • 妥善保存。gz 文件
  • 明确分隔符、空格符和引号符
  • 删除索引
  • 对于 Dynamodb,将浮点数据转换为十进制数据

这是关于我如何收集数据的简单概述,现在让我们看看云部分。

用胶水抓取 S3 和 Dynamodb 中的数据数据(这么多名字掉线)

AWS glue setup 仪表板分为两个部分,分别是数据目录部分和 ETL 部分(我将重点介绍仪表板的上部)。

让我们首先关注数据目录。

数据目录和爬虫部分

在这一部分中,您可以找到一个 database 选项卡,它包含所有数据库和您用 Glue 创建的相关表。为了这个项目,我创建了我的第一个数据库。有趣的部分是爬虫标签,在那里你可以设置爬虫在 S3 导航,Dynamodb 和 AWS 的一些其他服务(像 RDS)。在上图中,有一个我为这个项目创建的所有爬虫的列表。让我们看看爬虫里面有什么。

因此,当您设置爬虫时,需要:

  • 为你的爬虫取一个名字
  • 定义数据源,在本例中,我将关注三个事件和我的 Dynamodb 表
  • 为您的 crawler 定义一个 AWS 角色,使其能够访问所有要被爬网的数据源
  • 定义爬虫的执行频率,我决定让它每周运行一次
  • 定义爬虫的输出

我强烈建议激活复选框,用表中的元数据更新所有新的和现有的分区,以避免在读取数据期间出现一些分区问题,如果你不像我一样控制你正在接收的内容。

瞧,你只需要从 AWS Glue 的主页运行爬虫程序,现在你就可以通过 Athena 中的爬虫程序访问你的数据摘录(SQL 方式访问数据)。

但是数据仍然很原始,所以我决定给爬虫添加一个新的层来清理数据和添加一些信息。

ETL 部分

对于 ETL 部分,这是管道的数据处理部分。我本来可以在数据到达 S3 存储桶时对其进行分析,但是我注意到一些记录不太好(比如说损坏了),所以我想为这些数据应用一个转换状态:

  • 删除损坏的记录(带有错误的 matchid 格式)
  • 使用 dynamodb 表中收集的匹配细节进行连接(并在之前进行爬网)
  • 计算事件和比赛开始之间的时间差(秒和分)
  • 在事件函数中进行一些简单的字符串操作(比如清除武器的名称)

这里有代码的插图,你可以在库中找到这段代码。

我尝试了代码方法,但有一个选项 ETL 配置,您可以有更多的图表方法来进行数据操作,因为我想使用 rdd 映射和 spark 的数据帧,我决定不使用此功能,但我做了一些简单的测试,它工作得很好。所以现在只需要构建一个爬虫来用 ETL 部分生成的新数据更新数据目录。

在下面的模式中,有 AWS Glue 中操作的时间表。

现在让我们在 AWS 上做一个快速测试,从 web 界面请求流程数据。

这些数据可以从 Athena 访问,但你可以连接 AWS quicksight,它有更像 Tableau 的体验(我不喜欢的是,我不喜欢所有这些对我来说太黑的 BI 工具),更重要的是,你可以从笔记本上访问这些数据(在库中有一个 python 环境的副本)。

这是对我从 PUBG API 收集数据的数据管道的描述,系统每天收集大约 1000 个匹配,这不是一个很大的数量,但我想从小的开始。

我是一个好人,你可以在这个 kaggle 数据集 中找到我为这个项目建立的表格的摘录(这是 2019 年 1 月 27 日的数据摘录)。

我希望你喜欢阅读,如果你有任何意见,请不要犹豫。

原载于 2019 年 2 月 1 日 the-odd-dataguy.com

用你的机器学习模型构建一个 Docker 容器

原文:https://towardsdatascience.com/build-a-docker-container-with-your-machine-learning-model-3cf906f5e07e?source=collection_archive---------3-----------------------

Docker 初学者模板脚本完全指南

Photo by Chris Leipelt on Unsplash

作为一名数据科学家,我没有太多的软件工程经验,但是我肯定听过很多关于容器的很棒的评论。我听说过与传统虚拟机相比,它们是多么的轻量级,它们在确保您的代码有一个安全一致的环境方面有多好,以及我的 devops 工作如何节省,以便您可以专注于您的代码。

然而,当我试图将我自己的模型分类时,我很快意识到它并不那么直观。这一点也不像在 EC2 引导脚本前面加上RUN那么简单。我发现不一致和不可预测的行为经常发生,学习调试一个新工具可能会令人沮丧。

所有这些促使我创建了这篇文章,其中包含了将 Python 中的 ML 模型分解到 Docker 容器所需的所有代码片段。我将指导您安装您需要的所有 pip 包,并构建您的第一个容器映像。在这篇文章的第二部分的中,我们将设置所有必要的 AWS 环境,并在这个系列的第三部分也是最后一部分开始批处理容器。

免责声明 1:我这里说的模型是单个实例上的批处理作业,不是有 API 端点的 web 服务,不是分布式并行作业。如果您遵循本教程,将代码放入容器的整个过程应该不会超过 25 分钟。

免责声明 2:如果这是你第一次阅读关于容器的文章,这篇文章可能不会提供理解容器如何工作的必要信息,我强烈建议你在继续之前查阅一些在线教程。

先决条件

  • AWS 账户
  • 安装了 AWS CLI
  • Docker 已安装,账户设置在 DockerHub
  • Python 3 已安装

创建 Dockerfile 文件👷🏻‍♀️

要将您的代码放入容器,您需要创建一个Dockerfile,它告诉 Docker 您在应用程序中需要什么。

一个Dockerfile是一个文本文档,它包含了用户可以在命令行上调用的所有命令来组合一个图像。

(您可以从Dockerfiledocker-compose.yml构建 Docker 映像。如果你的代码可以被重构为一个多容器 Docker 应用程序,你可能想看看docker compose,但是现在一个Dockerfile应该足够了。)

Docker 映像从基础映像开始,由只读层构建而成,每一层都添加了一些依赖关系。最后,您告诉容器如何触发您的模型。

minimal Dockerfile for a Python application

在上面的 other 文件中,我从基本的 Python 3.6 伸展映像开始,apt-get更新了系统库,安装了一些makebuild的东西,检查了我的 Python 和pip版本以确保它们是好的,建立了我的工作目录,将requirements.txt复制到容器中,pip 在其中安装了所有的库,最后将所有其他代码文件复制到容器中,列出了所有文件以确保我需要的都在那里,并触发了我的入口点main.py文件。

如果你的代码文件夹结构是这样的,这个Dockerfile应该对你有用。

- app-name
     |-- src
          |-- main.py
          |-- other_module.py
     |-- requirements.txt
     |-- Dockerfile

如果您的代码还没有一个main.py或 shell 脚本来触发模型训练/推理,那么您可能想要首先重构您的代码。还记得用pipreqs path/to/project将库依赖关系冻结到一个requirements.txt文件中。我推荐使用pipreqs而不是pip freeze,因为当你运行pip freeze > requirements.txt时,它会输出该环境中所有已安装的包,而pipreqs只给你这个项目实际导入的包。(如果还没有pip(3) install pipreqs安装pipreqs

如果你的代码已经有了一个入口点,你需要做的就是把<app- name>改成你的应用程序的名字,我们就可以把它构建成一个图像了。

有很多让Dockerfile变得更小更高效的最佳实践,但是大多数都超出了本文的范围。但是,您可能需要注意以下几点:

1.1 使用 Python 拉伸作为基础图像

人们说不要从一个普通的 Ubuntu 镜像开始,而是使用一个官方的基础镜像,比如 Alpine Python。Alpine Python 是一个基于 Alpine Linux 的非常小的 Python Docker 映像,比默认的 docker python 映像小得多,但仍然拥有最常见的 Python 项目所需的一切。但是我发现使用它非常困难,尤其是在安装包的时候。

另一方面,Ubuntu 基础映像将提供更可预测的行为,但是您需要自己安装所有 Python 的东西。

所以我建议你从 Python 3.6 stretch 开始,这是基于 Debian 9(又名 stretch)的官方 Python 镜像。Python stretch 附带了 Python 环境和 pip 安装,并且是最新的,如果你选择 Ubuntu,你需要弄清楚如何安装所有这些。

1.2 仅安装您需要的东西

复制和粘贴一些网上发布的Dockerfile模板也很有诱惑力,尤其是如果这是你的第一个 Docker 项目。但是建议只安装你实际需要的东西来控制图像的大小。如果你看到其他人安装了一大堆makebuild的东西,试着先不要包括它们,看看你的容器是否能工作。较小的映像通常意味着构建和部署速度更快。(你应该试试我上面的极简主义模板的另一个原因!)

同样为了保持图像尽可能的简洁,使用.dockerignore,它的工作方式和.gitignore完全一样,忽略那些不会影响模型的文件。

an example .dockerignore file

1.3 在代码前增加requirements.txt

在您的Dockerfile中,总是在复制源代码之前添加您的requirements.txt文件。这样,当您更改代码并重新构建容器时,Docker 将重用缓存层,直到安装了包,而不是在每次构建时执行pip install命令,即使库依赖关系从未更改。没有人愿意仅仅因为你在代码中添加了一个空行就多等几分钟。

如果你有兴趣了解更多关于Dockerfile的信息,在附录 I 中有我们使用的一些基本命令的快速总结。或者你可以在这里查看 Dockerfile 文档。

现在可以随意跳到第 2 步,用刚刚创建的Dockerfile构建一个容器。

第二步——用你的 docker 文件建立一个图像👩🏻‍🍳

准备好Dockerfile之后,就该构建容器映像了。docker build根据Dockerfile中给出的指令创建图像。你所需要做的就是给你的图像一个名字(一个可选的版本标签)。

$ docker build -t IMAGE_NAME:TAG .
$ # or
$ docker build -t USERNAME/IMAGE_NAME:TAG .

这两个命令的唯一区别是图像名称前是否有一个USERNAME/。映像名称由斜杠分隔的名称组成,通常前缀是注册表主机名。USERNAME/IMAGE_NAME一般不是指定图像名称的强制格式。但是亚马逊 ECR 存储库中的图像确实遵循完整的REGISTRY/REPOSITORY:TAG命名约定。比如aws_account_id.dkr.ecr.region.amazonaws.com/my-web-app:latest

上面的命令中发生了一些事情。

首先,我们告诉 Docker 守护进程获取当前目录中的Dockerfile(这就是末尾的.所做的)。

接下来,我们告诉 Docker 守护进程构建图像并给它指定标签。标记对我来说一直很困惑。通常标记图像就像给它一个别名。这与给现有图像指定另一个名称来引用它是一样的。这有助于区分 Docker 图像的版本。

指定标记名不是强制性的。在没有指定特定标签的情况下运行构建时,:latest标签是默认标签。因此,如果您想维护一个好的版本历史,建议您在每次构建之后都明确地标记您的映像。

$ docker build -t USERNAME/IMAGE_NAME .
$ docker tag USERNAME/IMAGE_NAME USERNAME/IMAGE_NAME:1.0
$ ...
$ docker build -t USERNAME/IMAGE_NAME .
$ docker tag USERNAME/IMAGE_NAME USERNAME/IMAGE_NAME:2.0
$ ...

您可以在使用docker build -t USERNAME/IMAGE_NAME:TAG .构建图像时标记图像,或者在构建后像docker tag SOURCE_IMAGE:TAG TARGET_IMAGE:TAG一样显式标记图像。

现在,如果您运行docker images,您应该看到本地存在一个图像,它的存储库是USERNAME/IMAGE_NAME,标签是TAG

$ docker images

我还建议您此时在本地机器上测试您的容器,以确保一切正常。

$ docker run USERNAEM/IMAGE_NAME:TAG

请随意查看附录 II,获取一些基本 Docker CLI 命令的快速摘要,或在此处查看官方文档。

恭喜你!您只是将您的模型放入一个可以在任何安装了 Docker 的地方运行的容器中。请加入我这篇文章的第二部分,来配置您的 AWS 环境,以满足调度批处理作业的先决条件。

附录 I — Dockerfile 命令👩🏻‍🏫

  • FROM启动Dockerfile。要求Dockerfile必须以FROM命令开始。图像是在层中创建的,这意味着您可以使用另一个图像作为您自己的基础图像。FROM命令定义了你的基础层。作为参数,它采用图像的名称。可选的,你可以添加维护者和镜像版本的 Docker Cloud 用户名,格式 username/imagename:version
  • RUN用于构建您正在创建的图像。对于每个RUN命令,Docker 将运行命令,然后创建一个新的图像层。这样,您可以轻松地将映像回滚到以前的状态。RUN指令的语法是将 shell 命令的全文放在RUN之后(例如RUN mkdir /user/local/foo)。这将在一个/bin/sh外壳中自动运行。您可以像这样定义一个不同的 shell:RUN /bin/bash -c 'mkdir /user/local/foo'
  • COPY将本地文件复制到容器中。
  • CMD定义启动时将在图像上运行的命令。与RUN不同,这不会为图像创建一个新层,而只是运行命令。每个 Dockerfile/Image 只能有一个CMD。如果您需要运行多个命令,最好的方法是让CMD运行一个脚本。CMD要求你告诉它在哪里运行命令,不像RUN
  • EXPOSE为镜像用户创建一个提示,提示哪些端口提供服务。它包含在可通过docker inspect <container-id>检索的信息中。
  • 注意:EXPOSE命令实际上并没有让主机访问任何端口!相反,这需要在使用docker run时通过-p标志发布端口。
  • 将您的映像推送到私有或云注册表。

附录 II — Docker CLI 命令👩🏻‍🏫

一些基本的 Docker CLI 命令包括:

  • docker build从 Dockerfile 文件构建图像
  • docker images显示机器上的所有 Docker 图像
  • 启动一个容器并在其中运行命令
  • docker run选项:
  • -p指定主机和 Docker 容器中的端口
  • -it打开交互式控制台
  • -d以守护模式启动容器(在后台运行)
  • -e设置环境变量
  • docker ps显示所有正在运行的容器
  • docker rmi删除一个或多个图像
  • docker rm移除一个或多个容器
  • docker kill杀死一个或多个运行中的容器
  • docker tag用别名标记图像,以便以后参考
  • docker login登录您的 Docker 注册表

关于作者

我是一名机器学习工程师,除了技术教程之外,我还撰写关于生产力和自我发展的文章。我在 7 月 4 日参观了黄石公园,这就是封面图片。

让我们建立一个时尚-MNIST 有线电视新闻网,PyTorch 风格

原文:https://towardsdatascience.com/build-a-fashion-mnist-cnn-pytorch-style-efb297e22582?source=collection_archive---------2-----------------------

如何使用 Google Colab 和 TensorBoard 从头开始构建 PyTorch ML 项目的逐行指南

当谈到技术中的框架时,一件有趣的事情是,从一开始,似乎总是有各种各样的选择。但随着时间的推移,竞争将演变为只剩下两个强有力的竞争者。典型的例子有“PC vs Mac”、“iOS vs Android”、“React.js vs Vue.js”等。而现在,我们在机器学习方面有了‘py torch vs tensor flow’。

由谷歌支持的 TensorFlow 无疑是这方面的领跑者。它于 2015 年作为开源机器学习框架发布,迅速获得了大量关注和接受,特别是在生产准备和部署非常关键的行业。 PyTorch 由脸书在 2017 年推出,但由于其动态计算图和'python 式'风格,很快获得了从业者和研究人员的喜爱。

Image from The Gradient

The Gradient 最近的研究表明,PyTorch 在研究人员中表现出色,TensorFlow 在业界占据主导地位:

2019 年,ML 框架的战争还有两个主要竞争者:PyTorch 和 TensorFlow。我的分析表明,研究人员正在放弃 TensorFlow,成群结队地涌向 PyTorch。与此同时,在行业中,Tensorflow 是目前的首选平台,但这种情况可能不会持续太久。— 坡度

最近发布的 PyTorch 1.3 引入了 PyTorch Mobile、quantization 和其他好东西,它们都朝着缩小差距的正确方向前进。如果你对神经网络基础有点熟悉,但想尝试 PyTorch 作为一种不同的风格,那么请继续阅读。我将尝试解释如何使用 PyTorch 为时尚-MNIST 数据集从头构建一个卷积神经网络分类器。如果你没有强大的本地环境,这里的代码可以在 Google Colab 和 Tensor Board 上使用。事不宜迟,我们开始吧。您可以在下面找到 Google Colab 笔记本和 GitHub 链接:

📙谷歌 Colab 笔记本

👽 GitHub

导入

首先,让我们导入必要的模块。

# import standard PyTorch modules
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torch.utils.tensorboard import SummaryWriter # TensorBoard support

# import torchvision module to handle image manipulation
import torchvision
import torchvision.transforms as transforms

# calculate train time, writing train data to files etc.
import time
import pandas as pd
import json
from IPython.display import clear_output

torch.set_printoptions(linewidth=120)
torch.set_grad_enabled(True)     # On by default, leave it here for clarity

PyTorch 模块非常简单。

火炬

torch是主模块,包含了张量计算所需的所有东西。您可以单独使用张量计算来构建一个全功能的神经网络,但这不是本文要讨论的内容。我们将利用更强大、更方便的torch.nntorch.optimtorchvision类来快速构建我们的 CNN。对于那些有兴趣知道如何从“从抓到开始做这件事的人,请访问这个奇妙的 PyTorch 官方 tutoria l 作者杰瑞米·霍华德。

torch.nn 和 torch.nn 功能

Photo by Alphacolor on Unsplash

torch.nn模块提供了许多构建神经网络的类和函数。你可以把它想象成神经网络的基本构件:模型、各种层、激活函数、参数类等等。它允许我们像组装乐高玩具一样来建造模型。

火炬. optim

torch.optim提供所有优化器,如 SGD、ADAM 等。,这样就不用从头开始写了。

火炬视觉

torchvision包含大量流行的数据集、模型架构和计算机视觉的常见图像转换。我们从中获取时尚 MNIST 数据集,并使用其变换。

摘要记录器(张量板)

SummaryWriter使 PyTorch 能够为张量板生成报告。我们将使用 Tensor Board 查看我们的训练数据,比较结果并获得直觉。张量板曾经是 TensorFlow 相对于 PyTorch 的最大优势,但是现在从 v1.2 开始 PyTorch 正式支持张量板。

我们还引入了其他一些实用模块,如timejsonpandas等。

资料组

torchvision已经有了时尚 MNIST 数据集。如果你不熟悉时尚 MNIST 数据集:

Fashion-MNIST是一个由 Zalando 的文章图像组成的数据集,由 60,000 个示例的训练集和 10,000 个示例的测试集组成。每个示例都是 28x28 灰度图像,与 10 个类别的标签相关联。我们打算将Fashion-MNIST作为原始 MNIST 数据集的直接替代,用于机器学习算法的基准测试。它共享训练和测试分割的相同图像大小和结构。— 来自 Github

Fashion MNIST Dataset — From GitHub

# Use standard FashionMNIST dataset
train_set = torchvision.datasets.FashionMNIST(
    root = './data/FashionMNIST',
    train = True,
    download = True,
    transform = transforms.Compose([
        transforms.ToTensor()                                 
    ])
)

这个不需要过多解释。我们指定根目录来存储数据集,抓取训练数据,如果本地机器上没有,允许下载它,然后应用transforms.ToTensor将图像转换成张量,这样我们就可以在我们的网络中直接使用它。数据集存储在名为train_set.dataset类中

网络

在 PyTorch 中构建实际的神经网络既有趣又简单。我假设你对卷积神经网络的工作原理有一些基本的概念。如果没有,可以参考 deeplizard 的这个视频:

时尚 MNIST 只有 28x28 px 大小,所以我们实际上不需要非常复杂的网络。我们可以像这样建立一个简单的 CNN:

我们有两个卷积层,每个卷积层有 5x5 个内核。在每个卷积层之后,我们有一个跨度为 2 的最大池层。这允许我们从图像中提取必要的特征。然后我们展平张量,将它们放入密集层,通过多层感知器(MLP)来执行我们的 10 个类别的分类任务。

现在我们已经清楚了网络的结构,让我们看看如何使用 PyTorch 来构建它:

# Build the neural network, expand on top of nn.Module
class Network(nn.Module):
  def __init__(self):
    super().__init__()

    # define layers
    self.conv1 = nn.Conv2d(in_channels=1, out_channels=6, kernel_size=5)
    self.conv2 = nn.Conv2d(in_channels=6, out_channels=12, kernel_size=5)

    self.fc1 = nn.Linear(in_features=12*4*4, out_features=120)
    self.fc2 = nn.Linear(in_features=120, out_features=60)
    self.out = nn.Linear(in_features=60, out_features=10)

  # define forward function
  def forward(self, t):
    # conv 1
    t = self.conv1(t)
    t = F.relu(t)
    t = F.max_pool2d(t, kernel_size=2, stride=2)

    # conv 2
    t = self.conv2(t)
    t = F.relu(t)
    t = F.max_pool2d(t, kernel_size=2, stride=2)

    # fc1
    t = t.reshape(-1, 12*4*4)
    t = self.fc1(t)
    t = F.relu(t)

    # fc2
    t = self.fc2(t)
    t = F.relu(t)

    # output
    t = self.out(t)
    # don't need softmax here since we'll use cross-entropy as activation.

    return t

首先 PyTorch 中所有的网络类都是在基类上扩展的:nn.Module。它包含了所有的基础知识:权重、偏差、正向方法以及一些实用属性和方法,如.parameters().zero_grad(),我们也将使用它们。

我们的网络结构在__init__ dunder 函数中定义。

def __init__(self): 
  super().__init__()   # define layers 
  self.conv1 = nn.Conv2d(in_channels=1, out_channels=6, kernel_size=5)
  self.conv2 = nn.Conv2d(in_channels=6, out_channels=12, kernel_size=5)
  self.fc1 = nn.Linear(in_features=12*4*4, out_features=120)
  self.fc2 = nn.Linear(in_features=120, out_features=60)
  self.out = nn.Linear(in_features=60, out_features=10)

nn.Conv2dnn.Linear是在torch.nn模块中定义的两个标准 PyTorch 层。这些都是不言自明的。需要注意的一点是,我们在这里只定义了实际的层。激活和最大池化操作包含在下面解释的转发功能中。

# define forward function  
def forward(self, t):  
  # conv 1  
  t = self.conv1(t)  
  t = F.relu(t)  
  t = F.max_pool2d(t, kernel_size=2, stride=2)     # conv 2  
  t = self.conv2(t)   
  t = F.relu(t)  
  t = F.max_pool2d(t, kernel_size=2, stride=2)     # fc1   
  t = t.reshape(-1, 12*4*4)  
  t = self.fc1(t)  
  t = F.relu(t)     # fc2  
  t = self.fc2(t)  
  t = F.relu(t)    # output  
  t = self.out(t)    # don't need softmax here since we'll use cross-entropy as activation.   
  return t

一旦定义了层,我们就可以使用层本身来计算每一层的转发结果,加上激活函数(ReLu)和最大池操作,我们可以很容易地编写我们的网络的转发函数,如上所述。注意,在fc1(完全连接第 1 层)上,我们使用 PyTorch 的张量运算t.reshape来展平张量,这样它就可以传递给后面的致密层。此外,我们没有在输出层添加 softmax 激活函数,因为 PyTorch 的 CrossEntropy 函数会为我们处理这个问题。

超参数

通常情况下,我们可以只手动选择一组超参数,并用它们做一些实验。在这个例子中,我们想通过引入一些结构化来做更多的事情。我们将构建一个系统来生成不同的超参数组合,并使用它们来执行训练“运行”。每次“运行”使用一组超参数组合。将每次跑步的训练数据/结果导出到 Tensor Board,以便我们可以直接比较并查看哪个超参数集表现最佳。

我们将所有的超参数存储在一个 有序指令 中:

# put all hyper params into a OrderedDict, easily expandable
params = OrderedDict(
    lr = [.01, .001],
    batch_size = [100, 1000],
    shuffle = [True, False]
)
epochs = 3

lr:学习率。我们想为我们的模型尝试 0.01 和 0.001。

batch_size:批量,加速训练过程。我们会用 100 和 1000。

shuffle : Shuffle toggle,我们是否在训练前洗牌。

一旦参数下降。我们使用两个助手类:RunBuilderRunManager来管理我们的超参数和训练过程。

运行生成器

RunBuilder的主要目的是提供一个静态方法get_runs。它将 OrderedDict(其中存储了所有超参数)作为一个参数,并生成一个名为元组 Run的,每个run元素代表超参数的一种可能组合。这个命名元组稍后由训练循环使用。代码很容易理解。

# import modules to build RunBuilder and RunManager helper classes
from collections  import OrderedDict
from collections import namedtuple
from itertools import product

# Read in the hyper-parameters and return a Run namedtuple containing all the 
# combinations of hyper-parameters
class RunBuilder():
  @staticmethod
  def get_runs(params):

    Run = namedtuple('Run', params.keys())

    runs = []
    for v in product(*params.values()):
      runs.append(Run(*v))

    return runs

运行管理器

RunManager 类有四个主要目的。

  1. 计算并记录每个时期和运行的持续时间。
  2. 计算每个历元和运行的训练损失和准确度。
  3. 记录训练数据(如损耗、准确度、重量、梯度、计算图等)。)并运行,然后将它们导出到张量板以供进一步分析。
  4. 将所有训练结果保存在csvjson中,以备将来参考或提取 API。

如你所见,它帮助我们处理后勤工作,这对我们成功训练模型也很重要。让我们看看代码。这有点长,请原谅:

# Helper class, help track loss, accuracy, epoch time, run time, 
# hyper-parameters etc. Also record to TensorBoard and write into csv, json
class RunManager():
  def __init__(self):

    # tracking every epoch count, loss, accuracy, time
    self.epoch_count = 0
    self.epoch_loss = 0
    self.epoch_num_correct = 0
    self.epoch_start_time = None

    # tracking every run count, run data, hyper-params used, time
    self.run_params = None
    self.run_count = 0
    self.run_data = []
    self.run_start_time = None

    # record model, loader and TensorBoard 
    self.network = None
    self.loader = None
    self.tb = None

  # record the count, hyper-param, model, loader of each run
  # record sample images and network graph to TensorBoard  
  def begin_run(self, run, network, loader):

    self.run_start_time = time.time()

    self.run_params = run
    self.run_count += 1

    self.network = network
    self.loader = loader
    self.tb = SummaryWriter(comment=f'-{run}')

    images, labels = next(iter(self.loader))
    grid = torchvision.utils.make_grid(images)

    self.tb.add_image('images', grid)
    self.tb.add_graph(self.network, images)

  # when run ends, close TensorBoard, zero epoch count
  def end_run(self):
    self.tb.close()
    self.epoch_count = 0

  # zero epoch count, loss, accuracy, 
  def begin_epoch(self):
    self.epoch_start_time = time.time()

    self.epoch_count += 1
    self.epoch_loss = 0
    self.epoch_num_correct = 0

  # 
  def end_epoch(self):
    # calculate epoch duration and run duration(accumulate)
    epoch_duration = time.time() - self.epoch_start_time
    run_duration = time.time() - self.run_start_time

    # record epoch loss and accuracy
    loss = self.epoch_loss / len(self.loader.dataset)
    accuracy = self.epoch_num_correct / len(self.loader.dataset)

    # Record epoch loss and accuracy to TensorBoard 
    self.tb.add_scalar('Loss', loss, self.epoch_count)
    self.tb.add_scalar('Accuracy', accuracy, self.epoch_count)

    # Record params to TensorBoard
    for name, param in self.network.named_parameters():
      self.tb.add_histogram(name, param, self.epoch_count)
      self.tb.add_histogram(f'{name}.grad', param.grad, self.epoch_count)

    # Write into 'results' (OrderedDict) for all run related data
    results = OrderedDict()
    results["run"] = self.run_count
    results["epoch"] = self.epoch_count
    results["loss"] = loss
    results["accuracy"] = accuracy
    results["epoch duration"] = epoch_duration
    results["run duration"] = run_duration

    # Record hyper-params into 'results'
    for k,v in self.run_params._asdict().items(): results[k] = v
    self.run_data.append(results)
    df = pd.DataFrame.from_dict(self.run_data, orient = 'columns')

    # display epoch information and show progress
    clear_output(wait=True)
    display(df)

  # accumulate loss of batch into entire epoch loss
  def track_loss(self, loss):
    # multiply batch size so variety of batch sizes can be compared
    self.epoch_loss += loss.item() * self.loader.batch_size

  # accumulate number of corrects of batch into entire epoch num_correct
  def track_num_correct(self, preds, labels):
    self.epoch_num_correct += self._get_num_correct(preds, labels)

  @torch.no_grad()
  def _get_num_correct(self, preds, labels):
    return preds.argmax(dim=1).eq(labels).sum().item()

  # save end results of all runs into csv, json for further analysis
  def save(self, fileName):

    pd.DataFrame.from_dict(
        self.run_data, 
        orient = 'columns',
    ).to_csv(f'{fileName}.csv')

    with open(f'{fileName}.json', 'w', encoding='utf-8') as f:
      json.dump(self.run_data, f, ensure_ascii=False, indent=4)

**__init__**:初始化必要的属性,如计数、丢失、正确预测数、开始时间等。

**begin_run**:记录运行开始时间,以便当运行结束时,可以计算运行的持续时间。创建一个SummaryWriter对象来存储我们在运行过程中想要导出到张量板的所有内容。将网络图和样本图像写入SummaryWriter对象。

**end_run**:运行完成后,关闭 SummaryWriter 对象,并将历元计数重置为 0(准备下一次运行)。

**begin_epoch**:记录历元开始时间,以便在历元结束时计算历元持续时间。复位epoch_lossepoch_num_correct

这个函数是大多数事情发生的地方。当一个时期结束时,我们将计算时期持续时间和运行持续时间(直到这个时期,而不是最后的运行持续时间,除非是运行的最后一个时期)。我们将计算这个时期的总损耗和精确度,然后将损耗、精确度、权重/偏差、梯度导出到张量板中。为了便于在 Jupyter 笔记本中跟踪,我们还创建了一个 OrderedDict 对象results,并将我们所有的运行数据(损失、准确度、运行计数、历元计数、运行持续时间、历元持续时间、所有超参数)放入其中。然后我们将使用熊猫来读取它,并以整洁的表格格式显示出来。

**track_loss** **track_num_correct** **_get_num_correct**:这些是累计损失的效用函数,每批的正确预测数,以便以后计算历元损失和精度。

**save**:将所有运行数据(所有运行的results OrderedDict 对象列表)保存为csvjson格式,以便进一步分析或 API 访问。

这门课有很多东西要学。恭喜你走到这一步!最困难的部分已经过去了。从现在开始,一切都会变得有意义。

培养

终于,我们准备好进行一些训练了!在我们的RunBuilderRunManager课程的帮助下,培训过程变得轻而易举:

m = RunManager()

# get all runs from params using RunBuilder class
for run in RunBuilder.get_runs(params):

    # if params changes, following line of code should reflect the changes too
    network = Network()
    loader = torch.utils.data.DataLoader(train_set, batch_size = run.batch_size)
    optimizer = optim.Adam(network.parameters(), lr=run.lr)

    m.begin_run(run, network, loader)
    for epoch in range(epochs):

      m.begin_epoch()
      for batch in loader:

        images = batch[0]
        labels = batch[1]
        preds = network(images)
        loss = F.cross_entropy(preds, labels)

        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        m.track_loss(loss)
        m.track_num_correct(preds, labels)

      m.end_epoch()
    m.end_run()

# when all runs are done, save results to files
m.save('results')

首先,我们使用RunBuilder创建超参数的迭代器,然后遍历每个超参数组合来执行我们的训练:

for run in RunBuilder.get_runs(params):

然后,我们从上面定义的Network类创建我们的network对象。network = Network()。这个network物体承载了我们需要训练的所有重量/偏差。

我们还需要创建一个DataLoader 对象。它是一个 PyTorch 类,保存我们的训练/验证/测试数据集,它将遍历数据集,并按指定的batch_size批量给我们训练数据。

loader = torch.utils.data.DataLoader(train_set, batch_size = run.batch_size)

之后,我们将使用torch.optim类创建一个优化器。optim类获取网络参数和学习率作为输入,将帮助我们逐步完成训练过程并更新梯度等。我们将使用 Adam 作为优化算法。

optimizer = optim.Adam(network.parameters(), lr=run.lr)

好的。现在我们已经创建了网络,准备好了数据加载器,选择了优化器。让我们开始训练吧!

我们将遍历所有我们想要训练的纪元(这里是 3 个),所以我们将所有东西都放在一个“纪元”循环中。我们还使用我们的RunManager类的begin_run方法来开始跟踪跑步训练数据。

m.begin_run(run, network, loader)    
for epoch in range(epochs):

对于每个时期,我们将遍历每批图像来进行训练。

m.begin_epoch()    
for batch in loader:              
  images = batch[0]      
  labels = batch[1]      
  preds = network(images)      
  loss = F.cross_entropy(preds, labels)

  optimizer.zero_grad()  
  loss.backward()      
  optimizer.step()

  m.track_loss(loss)      
  m.track_num_correct(preds, labels)

上面的代码是真正的训练发生的地方。我们从批处理中读入图像和标签,使用network类进行前向传播(还记得上面的forward方法吗?)并得到预测。通过预测,我们可以使用cross_entropy函数计算该批次的损失。一旦计算出损失,我们用.zero_grad()重置梯度(否则 PyTorch 会累积梯度,这不是我们想要的),使用loss.backward()方法进行一次反向传播,以计算权重/偏差的所有梯度。然后,我们使用上面定义的优化器来更新权重/偏差。现在网络已经为当前批次更新,我们将计算正确预测的损失和数量,并使用我们的RunManager类的track_losstrack_num_correct方法累积/跟踪它们。

一旦全部完成,我们将使用m.save('results')将结果保存在文件中。

笔记本中运行的输出如下所示:

张量板

Image from Tensorboard.org

传感器板是一个 TensorFlow 可视化工具,现在 PyTorch 也支持它。我们已经努力将所有内容输出到。/runs '文件夹,Tensor Board 将在其中查找要使用的记录。我们现在需要做的只是启动张量板并进行检查。由于我在 Google Colab 上运行这个模型,我们将使用一个名为ngrok的服务来代理和访问我们在 Colab 虚拟机上运行的张量板。先安装ngrok :

!wget https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-linux-amd64.zip!unzip ngrok-stable-linux-amd64.zip

然后,指定我们要从中运行张量板的文件夹,并启动张量板 web 界面。/runs 是默认值):

LOG_DIR = './runs'get_ipython().system_raw('tensorboard --logdir {} --host 0.0.0.0 --port 6006 &'.format(LOG_DIR))

启动ngrok代理:

get_ipython().system_raw('./ngrok http 6006 &')

生成一个 URL,以便我们可以从 Jupyter 笔记本中访问我们的张量板:

! curl -s http://localhost:4040/api/tunnels | python3 -c \"import sys, json; print(json.load(sys.stdin)['tunnels'][0]['public_url'])"

正如我们在下面看到的,TensorBoard 是一个非常方便的可视化工具,让我们可以深入了解我们的训练,并可以极大地帮助超参数调整过程。我们可以很容易地找出哪个超参数组件表现最好,然后用它来做我们真正的训练。

结论

正如你所看到的,PyTorch 作为一个机器学习框架是灵活的、强大的和富有表现力的。你只需要写 Python 代码。由于本文的主要重点是展示如何使用 PyTorch 构建一个卷积神经网络,并以结构化的方式对其进行训练,因此我没有完成整个训练时期,精度也不是最佳的。你可以自己试试,看看模型表现如何。

这篇文章很大程度上受到了 deeplizard 在 YouTube 上的 PyTorch 视频系列的启发。甚至大部分代码片段都是直接抄袭过来的。我想感谢他们的伟大内容,如果你觉得有必要深入研究,随时去看看,并订阅他们的频道。

觉得这篇文章有用?在 Medium 上关注我(李立伟)或者你可以在 Twitter @lymenlee 或者我的博客网站wayofnumbers.com上找到我。你也可以看看我下面最受欢迎的文章!

[## “这是 CS50”:开始数据科学教育的愉快方式

为什么 CS50 特别适合巩固你的软件工程基础

towardsdatascience.com](/this-is-cs50-a-pleasant-way-to-kick-off-your-data-science-education-d6075a6e761a) [## 一枚硬币的两面:杰瑞米·霍华德的 fast.ai vs 吴恩达的 deeplearning.ai

如何不通过同时参加 fast.ai 和 deeplearning.ai 课程来“过度适应”你的人工智能学习

towardsdatascience.com](/two-sides-of-the-same-coin-fast-ai-vs-deeplearning-ai-b67e9ec32133) [## 我完成了吴恩达的机器学习课程,感觉棒极了!

好的,坏的,美丽的

medium.com](https://medium.com/datadriveninvestor/thoughts-on-andrew-ngs-machine-learning-course-7724df76320f)

构建一个机器学习推荐器

原文:https://towardsdatascience.com/build-a-machine-learning-recommender-72be2a8f96ed?source=collection_archive---------8-----------------------

Background vector created by starline — www.freepik.com

用几行 Python 代码构建机器学习推荐器的快速入门指南

在这篇文章中,我将展示如何使用机器学习快速组装推荐器。我将使用 LightFM 推荐库训练一个预测模型,该库可以很好地扩展到 2000 万到 4000 万次交互的中小型数据集,并训练一个关于用户特征和项目特征的模型。然后,我将进一步介绍推荐系统的典型实现,并汇总单个用户的预测,为一个群体提供推荐。

关键定义

在深入研究代码之前,先快速回顾一下用来描述推荐系统机制的一些关键定义。

条目–这是评级或分数的内容(例如,一本书、一部电影等。)

标签 —这些是推荐器的可能的输出。(例如评级或分数)。我们会用带标签的项目来训练推荐器,也就是已经被用户评分过的项目。

交互–这是一个输入和相关输出值的列表,我们希望根据这些值来训练我们的推荐器。(例如,用户和项目参考以及相关评级。)

用户特征–这些值代表每个用户的不同特征、行为和属性(如年龄、位置、性别)。

物品特征–这些值代表物品的不同特征(如产品类别、颜色、尺寸)

我将使用的数据

我将使用 Goodbooks-10k 数据集来演示推荐器,可以从这里下载:http://www2.informatik.uni-freiburg.de/" " ~齐格勒/BX/BX-CSV-Dump.zip

Goodbooks-10K 数据集由三个文件组成:

BX-用户用户特征,具体为 user_id、年龄和位置。

BX-图书条目特征包括ISBN、Book_Author 等一堆特征。

BX-图书评级–用户给出的图书评级交互,特别是 user_id、ISBN 和评级。

导入库

首先,我们需要从 LightFM 库中导入一些库和一些库。

import numpy as np
from lightfm.data import Dataset
from lightfm import LightFM
from lightfm.evaluation import precision_at_k
from lightfm.evaluation import auc_score
from lightfm.cross_validation import random_train_test_split
from scipy.sparse import coo_matrix as sp

导入数据

我们将导入 GoodBooks-10K 数据,然后创建以下三个函数,以便在需要时获得数据的新副本。

#fetch the interactions data
def get_ratings():
return get_data()[0]#fetch the item features
def get_book_features():
return get_data()[1]#fetch the user features
def get_user_features():
return get_data()[2]

准备数据

为了使用 LightFM 训练模型,我们将提供以下内容:

[1] 交互作为一个 sparse . COO _ matrix–我们的交互将是 BX-图书-评级. csv 中提供的用户 ID 和 ISBN

[2] user_features 作为包含用户特征的可迭代字符串——我们将简单地包括 BX 用户. csv 中提供的年龄

[3] item_features 作为包含项目特性的可迭代字符串——我们将简单地包括 BX-Books.csv 中提供的图书作者

LightFM 有一个数据集构造器,它有许多方便的方法来准备好数据输入到模型中。由于我们希望在模型中包含用户和项目特征,因此准备数据将是一个两步过程。

步骤 1:创建特性映射

首先,我们在数据集对象上使用 fit()方法来创建 BX-图书-评级. csv 中的映射

dataset = Dataset()dataset.fit((x[‘User-ID’] for x in get_ratings()), (x[‘ISBN’] for x in get_ratings()))num_users, num_items = dataset.interactions_shape()
print(‘Num users: {}, num_items {}.’.format(num_users, num_items))

我们使用 dataset.fit_partial()方法为 user_features 和 model_features 创建特性映射。

dataset.fit_partial(users=(x[‘User-ID’] for x in get_user_features()), items=(x[‘ISBN’] for x in get_book_features()), item_features=(x[‘Book-Author’] for x in get_book_features()), user_features=(x[‘Age’] for x in get_user_features()))

第二步:建立交互、用户特征和物品特征矩阵

其次,可以在 dataset 对象上调用 build_interactions()方法来构建交互矩阵。

(interactions, weights) = dataset.build_interactions(((x[‘User-ID’], x[‘ISBN’]) for x in get_ratings()))

我们在数据集对象上调用 build_item_features()和 build_user_features()方法来构建 item_features 和 user_features。这些函数根据 LightFM 的要求返回 sparse.coo_matrix 类型的对象。

item_features = dataset.build_item_features(((x[‘ISBN’], [x[‘Book-Author’]]) for x in get_book_features()))user_features = dataset.build_user_features(((x[‘User-ID’], [x[‘Age’]]) for x in get_user_features()))

指定型号

为了训练推荐器,我们将使用 LightFM 库中提供的加权近似等级成对(WARP)损失函数。WARP 处理(用户,正项,负项)三元组。

LightFM 对 WARP 算法提供了以下解释:

[1]对于给定的(用户,正项目对),从所有剩余项目中随机抽取一个负项目。计算两项的预测值;如果负项的预测值超过了正项的预测值加上一个差值,则执行梯度更新,将正项的等级提高,将负项的等级降低。如果没有等级违规,则继续对负项目进行采样,直到发现违规为止。

[2]如果您在第一次尝试时发现了一个违反负面示例,请进行大梯度更新:这表明在给定模型的当前状态下,许多负面项目的排名高于正面项目,并且模型必须进行大量更新。如果需要大量的采样来找到一个违规的例子,执行一个小的更新:模型可能接近最优,应该以较低的速率更新。

下面是我们如何用翘曲损失函数指定模型:

 model = LightFM(loss=’warp’)

该模型可以用许多超参数来指定,但是我将在这里跳过这些。值得一提的是,LightFM 还允许贝叶斯个性化排名损失,尽管这通常表现不太好。

训练模型

此时,您可能希望拆分训练数据,以评估模型训练的性能。LightFM 有一个方法,random_train_test_split()为我们做了这件事。我们可以打电话。fit()将模型拟合到交互、项目和用户特征集。

(train, test) = random_train_test_split(interactions=interactions, test_percentage=0.2)model.fit(train, item_features=item_features, user_features=user_features, epochs=2)

我不打算在这里详细介绍模型训练和评估,尽管下面的代码块显示了如何使用我们使用上面的 random_train_test_split()方法创建的训练集和测试集来测量模型训练精度和以 k 间隔报告的 AUC 分数。

train_precision = precision_at_k(model, train,item_features=item_features, k=10).mean()test_precision = precision_at_k(model, test, item_features=item_features,k=10).mean()train_auc = auc_score(model, train,item_features=item_features).mean()test_auc = auc_score(model, test,item_features=item_features).mean()print(‘Precision: train %.2f, test %.2f.’ % (train_precision, test_precision))print(‘AUC: train %.2f, test %.2f.’ % (train_auc, test_auc))print(“testing testing testing”)print(‘printing labels’, get_ratings()[‘ISBN’])

使用模型为一个组进行预测

为了对小组进行预测,我们需要汇总单个用户的评分,以确定某个项目(书)对小组中所有用户的相对重要性。这里我们可以使用多种聚合策略,比如最小痛苦和混合方法。为了简单起见,我将简单地计算该组中所有用户的每个项目(书籍)的预测得分的平均值。

首先,我们创建一个函数来遍历给定组中的所有用户,并使用 item_features 和 user_features 调用 model.predict()来预测每个项目的得分。该函数将每个用户的分数堆叠到我们称为 all_scores 的数组中,以便我们执行一些聚合。

def sample_recommendation(model, data, user_ids):n_users, n_items = data.shapeall_scores = np.empty(shape=(0,n_items))for user_id in user_ids:scores =. model.predict(user_id,np.arange(n_items),item_features,user_features)all_scores = np.vstack((all_scores, scores))

现在让我们通过对每一项取平均来合计分数。然后,让我们对分数进行排序,并按照我们的模型对标签进行评分的相反顺序获取所有标签(ISBN)。

item_averages = np.mean(all_scores.astype(np.float), axis=0)labels = np.array([x[‘ISBN’] for x in get_ratings()])

top_items_for_group = labels[np.argsort(-item_averages)]

现在,我们可以打印本组的前 5 项。

print(“ Top Recommended ISBN for Group:”)for x in top_items_for_group[:5]:print(“ %s” % x)

取样组建议

最后,我们可以为一个组抽样推荐。值得一提的是,虽然 LightFM 的伸缩性很好,但采样建议不一定需要同步,就像我在这里演示的那样。相反,可以异步计算推荐值,并定期为所有组重新计算推荐值。

group = [3,26,451,23,24,25]sample_recommendation(model, interactions, group)

这就是如何构建一个机器学习推荐器,用不多的 Python 代码就能做出基于组的推荐。

有关完整的代码,请参见:https://github . com/jamesdhope/recommender/blob/master/recommender . py

如果你喜欢这个,请给我一些掌声。

参考

[1] LightFM,https://github.com/lyst/lightfm

[2] LightFM,【https://lyst.github.io/lightfm/docs/examples/dataset.html】T4

[3]Bluedatalab.com,客户项目

用 Keras 和 OpenCV 构建多位数检测器

原文:https://towardsdatascience.com/build-a-multi-digit-detector-with-keras-and-opencv-b97e3cd3b37?source=collection_archive---------2-----------------------

用数据做很酷的事情!

介绍

数字在我们身边无处不在。无论是闹钟、健身追踪器还是条形码,甚至是亚马逊精心包装的快递包裹,数字无处不在。通过 MNIST 数据集,机器学习被用来读取单个手写数字。现在我们可以扩展到读取多个数字,如下所示。底层神经网络既进行手指定位又进行手指检测。这在各种 ML 应用中非常有用,比如读取商店中的标签、车牌、广告等。

Reading multiple digits

但是为什么不仅仅使用 OCR 呢?是的,OCR 可以是自动检测数字的一个很好的起点,但 OCR 并不总是有效,有时我们需要为我们的特定任务训练一个神经网络。

数字检测流水线

数字检测问题可以分为两个部分

  1. 数字本地化
  2. 数字识别

数字本地化:

图像可以在任何位置包含数字,对于要检测的数字,我们需要首先找到包含这些数字的区域。数字可以有不同的大小和背景。

有多种方法可以检测手指的位置。我们可以利用简单的图像形态学操作,如二值化、腐蚀、膨胀来提取图像中的数字区域。然而,由于阈值、内核大小等调整参数的存在,这些可能变得对图像过于具体。我们还可以使用复杂的无监督特征检测器、深度模型等。

数字识别:

定位的手指区域用作手指识别过程的输入。 MNIST 数据集是手写数字识别的规范数据集。大多数数据科学家都用这个数据集做过实验。它包含大约 60,000 个用于训练的手写数字和 10,000 个用于测试的数字。一些例子看起来像:

MNIST Images

然而,现实生活场景中的数字通常非常不同。它们有不同的颜色,通常像下面的箱子一样印刷。

Day to day digit images

稍加研究,我们就可以看到另一个公共数据集SVHN——街景门牌号数据集 。该数据集由从谷歌街景中收集的门牌号图像组成,并附有注释。下面是来自 SVHN 的示例图像:

SVHN Images

该数据集具有针对许多背景的各种数字组合,并且将更好地用于通用模型。

在 Keras 中建模

我们选择这个 repo 来实现多位数检测器。它写得很好,容易理解。

使用作为稳定特征检测器的最大稳定极值区域(MSER) 方法进行手指定位。MSER 主要用于图像中的斑点检测。斑点是像素的连续集合,其外部边界像素强度比内部边界像素强度高(给定阈值)。如果这些区域在变化的强度范围内没有太大的变化,那么这些区域被认为是最稳定的。MSER 的运行时复杂度较低,为 O(nlog(log(n))),其中 n 是图像上的像素总数。该算法对模糊和缩放也是鲁棒的。这使得它成为提取文本/数字的良好候选。想了解更多关于 MSER 的信息,请点击链接。

使用具有卷积、最大池和 FC 层的 CNN 来完成数字识别,这些层将每个检测到的区域分类成 10 个不同的数字。该分类器在测试集上达到 95%的准确率。

我们在各种例子上测试了回购,发现它运行得相当好。见上面分享的例子。

有一些差距,要么是定位器没有完美地工作(没有检测到数字 1 的位置),要么是检测器失败了(检测为 5)。

结论

我们希望这篇博客能成为理解多位数检测管道如何工作的良好起点。我们分享了一个很好的 github 链接,可以用来在 SVHN 数据集上建立模型。如果这种模式效果不好。你可以收集自己的数据,对训练好的模型进行微调。

我有自己的深度学习咨询公司,喜欢研究有趣的问题。我已经帮助许多初创公司部署了基于人工智能的创新解决方案。请到 http://deeplearninganalytics.org/来看看我们。如果你有一个我们可以合作的项目,那么请通过我的网站或在info@deeplearninganalytics.org联系我

你也可以在https://medium.com/@priya.dwivedi看到我的其他作品

参考文献:

  • SVHN 数据集
  • 了解 MSER
  • 多位检测器代码

构建一个收集中顶级作者数据的渠道

原文:https://towardsdatascience.com/build-a-pipeline-for-harvesting-medium-top-author-data-c4d7ed73729f?source=collection_archive---------9-----------------------

如何使用 Luigi 和 Docker 为 Medium 构建简单的数据工程管道

介绍

当我为数据工程管道编写的一些 Python 脚本失去控制时,这个项目的想法开始了:

  • 管道需要数千个 API 调用 REST 服务,每个调用下载一个 JSON 文件。
  • 管道的某些部分需要很长时间来运行,并且该过程有时会失败。

我的过程是这样的:

这些累积的 API 调用累积起来:

  • 获取父对象( 1 请求
  • 对于每个父节点获取子节点( 150 个请求
  • 为每个孩子获取信息( 5000 个请求)
  • 对于每个 info fetch 配置项( 14,000 个请求……这已经很多了!)

这是我使用的 API 的一个怪癖;没有办法进行批量请求。有许多问题:

  • 这个序列运行了几个小时
  • 如果出现故障,很难确定从哪里重新开始
  • 没有办法检查进度;如果脚本已经运行了一个小时,它们是不是就要完成了?

咨询谷歌,我发现我的设计与这个不做事的例子非常相似:

这东西已经长成了一个怪物。

路易吉来救援了

我四处寻找一个轻量级的数据工程框架,它可以支持:

A famous plumber

  • 工作流程编排
  • 并行处理
  • 简单部署
  • 快速学习曲线

考虑到这些要求, Spotify Luigi 看起来刚刚好:

Luigi 是一个 Python 模块,帮助您构建批处理作业的复杂管道。它处理依赖性解析、工作流管理、可视化等。

Spotify 使用 Luigi 进行推荐音乐的数据工程批处理工作,例如 Discover Weekly 播放列表(尽管最近流行迪斯科,但我对 Discover Weekly 的选择还是很满意)。

My face when I saw Luigi do all the things

与 Luigi 一起工作令人惊讶地满足,我想知道如何在我的日常工作之外再次使用它。我想到在管道中重用这种方法来收集关于顶级媒体作者的数据,下面的文本描述了一个可以在其他领域重用的数据工程管道的简单框架。

这是面向数据工程和数据科学的,尽管 Luigi 的模块化架构应该可以直接添加分析组件。换句话说,这是一个用于提取数据的系统设计,而不是一种用于分析和从数据中获得洞察力的方法(可能是未来故事的主题)。

Luigi gets to work on the pipes

从媒体中获取有趣的顶级作者数据

我们的管道将收集出版物中顶级作者的数据:

  1. 这位顶级故事作者关注的作者列表
  2. 他们写的故事总数
  3. 每个故事获得了多少掌声
  4. 他们故事的平均标题长度
  5. 他们故事的平均字数
  6. 他们为之写作的出版物

关于 Medium API 的一个简短说明。API 本身相当有限。然而,您可以将?format=json附加到许多标准的中型 URL,这将返回通常显示在页面上的数据的 JSON 表示,以及一些额外的元数据。例如,《走向数据科学》的首页用?format=json参数渲染成这样:

{"success":true,"payload":{"collection":{"id":"7f60cf5620c9","name":"Towards Data Science","slug":"towards-data-science","tags":["DATA SCIENCE","MACHINE LEARNING","ARTIFICIAL INTELLIGENCE","BIG DATA","ANALYTICS"],"creatorId":"895063a310f4","description":"Sharing concepts, ideas, and codes.","shortDescription":"Sharing concepts, ideas, and codes.","image":{"imageId":"1*F0LADxTtsKOgmPa-_7iUEQ.jpeg","filter":"","backgroundSize":"","originalWidth":1275,"originalHeight":1275,"strategy":"resample","height":0,"width":0},"metadata":{"followerCount":171879,"...etc

感谢 Radu Raicea 的精彩文章我如何使用 Python 找到有趣的人来关注 Medium 为我指出了这个特性。

JSON 结果需要一点清理:

Remove invalid characters from the response

具体细节

一个关键需求是使我的 Luigi 工作流的部署非常简单。关于部署环境,我只想假设一件事;Docker 守护进程是可用的。有了 Docker,我就不需要担心 Python 版本不匹配或其他环境差异。

不过,我花了一点时间才弄明白如何在 Docker 中运行 Luigi。

第一步是在它自己的容器中启动中央调度程序。你可以用一张现有的图片来做这件事,比如这张。

您可以向调度程序提交作业,如下所示:

PYTHONPATH='.' luigi --module top_artists Top10Artists  --date-interval 2012-07

这是用 Luigi docs 里的一个例子。

所以对于部署,这是我采取的方法;一个 docker 编写文件,包括:

  • Luigi 中央调度程序
  • Luigi 任务的独立的基于 Python 的容器
  • 公开报告的 nginx 容器

tasks 容器的入口点休眠一段时间,然后启动 Luigi 管道,这个sleep代替了 cron 作业。

使用 Luigi 进行并行处理

我的脚本非常慢,我需要一种方法来并行运行多个 URL 获取过程,这导致了这样的设计:

  • 一个WrapperTask包装了管道的所有组件
  • 第一次 URL 获取获得需要单独查询的许多项目的列表(这是一个单独的请求)
  • 这些块在工人之间划分,他们的工作结果被放入一个以每个工人 id 命名的文件中(例如1.json)
  • 从这一点来看,这些文件被下游工人使用

该方法改编自这篇文章。

数据收集任务

对于中型数据采集管道,WrapperTask如下所示:

Pipeline wrapper

requires()方法完成了这里的大部分工作,建立了一个在PipelineTask被认为完成之前需要完成的任务列表。

流水线中的第一个任务是FetchUserList。这将从媒体出版物的首页获取媒体作者的列表。作者列表放在一个文件中,供下游任务使用。

Fetch a list of authors from a publication

在《走向数据科学》出版物上运行它,会给我们一个页面上提到的作者列表:

A list of publication authors

您可能会注意到,此结果中返回的作者列表与页面上显示的不匹配;那是怎么回事?原来,页面在加载时会向 Medium API 提交一系列请求,每个返回的 JSON 结果都包含一个指向该系列中下一组结果的指针。我们需要在获取数据时处理这种分页行为:

Handle Medium API paging

这个实现很大程度上借用了 Radu Raicea 的文章。

注意,在一个给定的管道中,只有一个FetchUserList任务将被执行,因为我们没有包含一个file_number参数。

下一个任务是FetchUserFollowings。这个任务将由许多工作器并发执行,并行化由workers配置参数控制。当任务开始执行时,它决定负责处理 JSON 文件的哪个部分。确定截面的逻辑由get_part_of_list()控制:

Split the whole list, so we can delegate a chunk to each worker

ExtractUserMetrics任务从帖子数据中挑选出一些有趣的数据点,例如每篇文章的总鼓掌次数:

Extract metrics

然后,我们可以从提取的故事数据中得出几个平均值:

Calculate averages

最后,这一行启动了这个过程:

luigi.build([PipelineTask()], workers=worker_count)

公开结果

您可能希望向最终用户公开这些报告,例如向您团队中的数据科学家公开。一个简单的方法是添加一个 nginx web 服务器,并列出输出目录的内容。这将允许任何人点击一个 URL 并下载报告,每天都有一个报告目录。

尝试一下

好吧,让我们踢轮胎这件事…

首先,通过在 luigi.conf 文件中指定一个collection_id来指定 URL 抓取的起点。

配置完成后,有几种方法可以运行代码:

  1. 开发时,可以直接运行__main__.py。如果你想这样做,你需要先启动 Luigi。
  2. 您还可以使用 docker-compose 运行整个应用程序堆栈:
docker-compose up -d

这将启动 Luigi、nginx 和任务容器,这将触发__main__.py

检查总结报告,我们可以获得一些关于目前为《走向数据科学》撰写文章的一些顶级作者的信息。例如,我们可以获得当前顶级作者之一 Will Koehrsen 的一些汇总统计数据:

Some example metrics

干得好,威尔!

单元测试

我选择了 pytest 作为测试框架。我喜欢 pytest 有两个主要原因:

  • 它需要更少的样板代码
  • 您可以使用常规的 assert 语句,而不需要记住特殊的语法

为了使用与生产版本不同的配置进行测试,您可以在测试中使用luigi.configuration.add_config_path()。这将从特定位置加载配置:

Find and load test configuration

我使用了MockTarget类,这样我可以以可重复的方式运行测试。我不希望一个测试依赖于前一个测试创建的数据,或者测试在完成运行后留下结果文件。MockTarget模拟一个 Luigi 目标,它将结果存储在内存中,而不是将它们写到文件系统中。

最后,为了允许在不要求端点可用的情况下测试 URL 请求,我使用了[requests_mock](https://pypi.org/project/requests-mock/)库。

Github 上提供了该示例管道的完整源代码:

[## 吕克拉塞尔/多克尔-路易吉

从 Medium 获取顶级作者数据的数据工程管道— lucrussell/docker-luigi

github.com](https://github.com/lucrussell/docker-luigi)

结论

这个项目是一个 Luigi 管道,用于从 Medium 收集顶级作者数据。该项目一开始只是一个简单的框架,用于编排一个有问题的数据工程过程。管道通过抓取出版物首页的 URL 来提取关于作者及其故事的信息,然后收集关于顶级作者的一些基本指标。它使用 Luigi 来协调 Python 逻辑,使用 Docker 来简化部署。

快速建立投资组合网站

原文:https://towardsdatascience.com/build-a-portfolio-website-in-a-flash-9d5ec30a73a7?source=collection_archive---------16-----------------------

使用 GitHub 页面和 Google 域名创建静态网站的 12 个步骤

如果你是一名开发人员、数据科学家或设计师,你可能会被建议建立一个作品集网站。你的作品集向其他人展示了你能做什么,以及为什么他们应该与你合作发展一项事业、一个副业或一项新的事业。

但是导航选项来创建一个安全、便宜、响应迅速的网站是一个障碍,它经常阻止人们将他们的网站投入使用。我已经做了研究,建立了一个网站,将与您分享如何快速制作作品集网站。好消息是只需要 12 个步骤。😁

这是我网站的截图:

https://jeffhale.net/

我不是说我的设计很壮观。😀我的重点是帮助你通过这个过程,让你可以在瞬间建立一个网站。然后,您可以对其进行样式化,并包含您喜欢的任何内容。

我们开始吧!🚀

12 步

  1. 制作 GitHub repo
  2. 克隆回购
  3. 做一个 gh-pages 分公司
  4. 创造 index.html
  5. 添加引导启动代码
  6. 添加内容
  7. 本地查看网站
  8. 推送至 GitHub
  9. 在网上查看
  10. 从谷歌域名购买自定义网址(可选)
  11. 将您的自定义域指向 GitHub 页面(可选)
  12. 启用 SSL(如果使用自定义 URL)

迭代!🔃

制作 GitHub Repo

如果您没有 GitHub 帐户,请参阅我的 GitHub 工作流文章了解如何获得一个。给你的回购命名。创造。gitignore、README.md 和许可证文件。

克隆回购

在本地机器上克隆您的 repo。如果您的本地机器上没有 git,请参考上面的文章获取它。

切换到您的本地存储库。创建一个名为 gh-pages 的新分支。

默认情况下,GitHub Pages 会根据名为 gh-pages 的分支中的内容自动更新您的站点。默认情况下,GitHub Pages 不会从您的主分支自动更新。

创造 index.html

移动到您的项目目录。用touch index.html做一个名为index.html的文件。如果你想学习 shell 常用命令,比如touch,请点击这里查看我的文章。

在代码编辑器中打开您的index.html文件。

添加引导启动代码

我们将使用超级流行的 Bootstrap 框架,以最小的努力提供响应性 CSS 功能。

从 Bootstrap 复制并粘贴 starter 模板代码。如果你不需要 JavaScript 功能,那就去掉这一部分来加速你的站点。我在下面的代码片段中省略了 JavaScript 代码。

Bootstrap starter code without JavaScript

引导文档非常好。如果你以前没有用过 Bootstrap,我建议你花几分钟时间使用 Bootstrap grid docs ,这样你就知道如何修改页面布局了。如果您需要提神剂(或清新剂😃)在 HTML 和 CSS 方面,建议你去看看 w3schools.com 的。

添加内容

标题标签和正文标签的内容更改为 Your_name 的作品集— 或您喜欢的任何内容。

添加您的 HTML。我使用了三栏布局,在移动设备上切换到一栏。

如果你想制作其他的 CSS 样式,创建 addedstyles.css 并在你的 index.htmlhead标签中包含<link rel=”stylesheet” href=”addedstyles.css”>

我的代码可在本回购获得。再说一次,我不是说我的设计很棒。我的重点是帮助你快速建立一个网站。

本地查看网站

您的网站可以在本地查看,而无需提供任何服务,因为这是一个静态网站。只要双击你电脑上的index.html文件,然后砰!你应该会在浏览器中看到你的投资组合。

推送至 GitHub

在 Git 中添加文件并提交您的工作。然后用git push origin HEAD:gh-pages推送到你的 gh-pages 分支。

GitHub Repo

在网上查看

导航到http://your _ git hug _ username . github . io/your _ repo _ name。例如,我的站点在 http://discdiver.github.io/show/(现在它被转发到一个自定义域名)。您应该会看到与您在本地看到的相同的网页。

如果你想要一个每年 12 美元(税前,在美国是美元)的个性化网址,你可以这么做。

从谷歌域名购买自定义网址(可选)

如果你想要一个自定义域名,而不是 GitHub 页面的网址,我建议你从谷歌域名购买一个域名。多年来,我尝试了很多域名注册商,发现谷歌域名是目前最好的。

用你的谷歌账户登录,开始搜索域名。

Probably not what you want for your portfolio URL. 🚗

完成您的交易。您将在一分钟后回来管理 DNS 设置。

将您的自定义 URL 指向 GitHub 页面(可选)

GitHub 最近使得创建一个 CNAME 文件来链接你的自定义域名变得更加容易。👍

转到您的存储库的设置选项卡。向下滚动到 GitHub 页面,在自定义域名字段输入你的域名,按保存。GitHub 现在会自动为你的回购协议中的 DNS 创建一个 CNAME 记录。

Adding a custom url. Note that the Enforce HTTPS checkbox will be unchecked after you save.

现在我们需要修改连接的 Google Domains 端的内容。

在您的 Google Domains 帐户中,选择 DNS 选项卡并向下滚动到自定义资源记录。留着@。在 IPv4 地址字段中键入以下内容:185.199.108.153

After updating Google Domains DNS tab.

然后添加一个 CNAME 记录,这样就可以解析你的 url 的版本。将 www 放入第一个字段,从下一个下拉字段中选择 CNAME ,并将 @ 放入 IPv4 地址字段。

应该可以了!拿一杯水🚰当您的更改传播时。然后导航到 http://your_custom_url 。你应该看看你的网站。酷!

启用 SSL(如果使用自定义 URL)

启用 SSL 是个好主意,这样你的站点会更安全,你的 https 前缀也会起作用。启用 SSL 只需在您的 repo 的设置选项卡中点击一下。我不知道为什么添加自定义域时设置会关闭。

在 GitHub 页面部分,点击强制 HTTPS 的复选框。请注意,选中复选框后,您不必保存任何内容。

Check the box for HTTPS

现在当你导航到 https://your_url 时,你应该会看到你的站点,而不是一个错误。你有一个功能正常的投资组合网站。酷毙了。😀

不要忘记更新你的 GitHub master 分支中的自述文件,这样人们就知道在哪里可以找到你的 live 站点。GitHub 上的编辑按钮也可以让你包含一个网站链接。👍

Note the Edit button on the right

是时候让你的网站变得更漂亮了。

重复

有许多关于如何建立你的作品集内容的文章。这是一个有很多好建议的人。

有些人推荐大项目,有些人推荐小项目,有些人推荐混合项目。我不打算参加这场辩论,但我将提出一些一般性建议。

  • 添加一些链接和图片。如果你不是设计师,你不需要走极端——只要确保你的作品集看起来干净专业。
  • 确保你的作品集在手机和桌面上都很好看。Bootstrap 的响应式设计让这变得相当简单。
  • 除非你在自述文件、博客帖子或 Jupyter 笔记本中讨论一个项目,否则我建议你在文件夹中谈一谈。
  • 如果你把你的投资组合放在一起,发现它有缺口,开始填补这些缺口。如果你正在做你的第一个项目,不要气馁。每个人都从第一个项目开始了他们的旅程。😁
  • 将你的作品集交给几个你信任的人,他们会给出诚实的反馈。
  • 当你准备好与全世界分享你的作品集时,用你作品集的 URL 更新你的 LinkedIn 个人资料。
  • 如果你怀疑自己有冒名顶替综合症,担心你的作品集永远不会准备好,当你认为已经完成 95%的时候,分享你的作品集。您可能会发现设置上线截止日期很有帮助。
  • 记住你的作品集是一项正在进行的工作。更新它,因为你有更多的项目要添加。

随意给你的网站添加更多的复杂性:杰基尔、T2、盖茨比。你可能想用 Sass 来管理风格。

我网站的代码可以在 GitHub repo 中找到。随意叉它去镇上。🍴

包装

在这篇文章中,你看到了如何用 12 个步骤建立你的投资组合网站。我希望这个指南对你有用。如果你有,请在你最喜欢的社交媒体上分享,这样其他人也可以找到它。😀

你可以用同样的方法建立任何静态网站,而不仅仅是作品集网站。

我撰写关于数据科学、Python、云计算和其他技术主题的文章。如果你对其中的任何一个感兴趣,请查看它们并在这里跟随我。

为了确保你不会错过精彩的内容,请加入我的邮件列表。👍

如果你用这个指南做一个项目或作品集——我很想看看你会做些什么——在 Twitter @discdiver 上给我加标签。

快乐组合!😁

用 GitHub API 构建一个 Python 爬虫来获取活动流

原文:https://towardsdatascience.com/build-a-python-crawler-to-get-activity-stream-with-github-api-d1e9f5831d88?source=collection_archive---------21-----------------------

从头做起

在我之前的文章用一个简单的 Python 爬虫下载课程资料中,我介绍了一个简单的 Python 爬虫来下载文件。要构建这样一个爬虫,我们必须自己找到文件的模式。但是如果我们想从著名的网站收集数据,比如 Twitter 和 GitHub,事情可能会简单一些。因为通常,这些网站都提供了 API,我们可以直接获取我们想要的数据。在这篇文章中,我将构建一个 Python 爬虫来用 GitHub API 获取活动流。

1 个目标

活动流显示用户最近的活动,例如下面显示的是我在 GitHub 中的活动。

activity stream in GitHub

我想得到如下这些活动

ShusenTang starred lyprince/sdtw_pytorch
chizhu starred markus-eberts/spert
Hexagram-King starred BrambleXu/knowledge-graph-learning
Yevgnen starred BrambleXu/knowledge-graph-learning
......

2 用 GitHub API 获取数据

2.1 GitHub API

首先,我们来看看 GitHub API 文档。如果您没有启用 双因素认证 ,您可以运行下面的命令来测试 API。输入密码后,您应该会看到响应。

$ curl -u '<usename>' [https://api.github.com](https://api.github.com)
Enter host password for user '<usename>': <password>{
  "current_user_url": "[https://api.github.com/user](https://api.github.com/user)",
  "current_user_authorizations_html_url": "[https://github.com/settings/connections/applications{/client_id](https://github.com/settings/connections/applications{/client_id)}",
  "authorizations_url": "[https://api.github.com/authorizations](https://api.github.com/authorizations)",
 ......

但是如果您已经启用了 双因素认证 ,我们需要使用 个人访问令牌 来进行认证。按照帮助页面创建个人令牌。至于访问权限/范围,因为我们只想获得活动流,选择通知就足够了。

如果一切顺利,我们现在应该有一个令牌。按照认证指令使用以下命令测试令牌。

$ curl -H "Authorization: token <TOKEN>" [https://api.github.com](https://api.github.com)

{
  "current_user_url": "[https://api.github.com/user](https://api.github.com/user)",
  "current_user_authorizations_html_url": "[https://github.com/settings/connections/applications{/client_id](https://github.com/settings/connections/applications{/client_id)}",
  "authorizations_url": "[https://api.github.com/authorizations](https://api.github.com/authorizations)",
  .......

2.2 获取激活流

在其他认证方式中,我们可以使用下面的命令直接获取用户数据。

$ curl -u <usename>:<TOKEN> [https://api.github.com/user](https://api.github.com/user){
  "login": "xxxx",
  "id": xxxxx,
  "node_id": "xxxx",
  "avatar_url": "[x](https://avatars1.gi)xxx",
  ......

接下来,我们需要浏览 API 文档来找到与活动相关的 API。在右侧的切换列表中,“活动”下有“事件”、“提要”、“通知”。但是我们需要确定哪个适合我们的需求。

浏览文档后,我们可以知道“事件”下的“列出用户收到的的事件是我们需要的。

我们可以用这个 API 命令测试curl命令。

$ curl -u <usename>:<TOKEN> https://api.github.com/users/<usename>/received_events

这会返回很多消息,所以最好将响应保存为 JSON 文件。

$ curl -u <usename>:<TOKEN> https://api.github.com/users/<usename>/received_events > github_stream.json

3 分析 JSON 文件

我们可以查看 JSON 数据来熟悉格式。

似乎有不同种类的事件类型。我们可以编写一个简单的脚本来获取各种事件类型。

import jsonwith open('github_stream.json', 'r') as f:
  data_string = f.read() # read object as string
  data = json.loads(data_string) # convert JSON (str, bytes or bytearray) to a Python object# Get all event types
events = set()
for event in data:
  events.add(event['type'])
print(events)# output
{'IssueCommentEvent', 'ForkEvent', 'PushEvent', 'PullRequestEvent', 'WatchEvent', 'IssuesEvent'}

通过比较我们在 GitHub 主页上看到的活动流,我们可以看到只存在三种事件类型,WatchEvent(星号)、ForkEvent(分叉)、PushEvent(推送)。

所以我们只需要从这三种事件类型中获取活动。下面是剧本。

# github-api-json-parse.pyimport jsonwith open('github_stream.json', 'r') as f:
  data_string = f.read() # read object as string
  data = json.loads(data_string) event_actions = {'WatchEvent': 'starred', 'PushEvent': 'pushed to'}for event in data:
  if event['type'] in event_actions:
    name = event['actor']['display_login']
    action = event_actions[event['type']] 
    repo = event['repo']['name'] 
    print('{name} {action} {repo}'.format(name=name, action=action, repo=repo))if event['type'] == 'ForkEvent':
    name = event['actor']['display_login']
    repo = event['repo']['name']  
    forked_repo = event['payload']['forkee']['full_name'] 
    print('{name} forked {forked_repo} from {repo}'.format(name=name, forked_repo=forked_repo,repo=repo))

运行脚本,我们可以得到下面的输出。

ShusenTang starred lyprince/sdtw_pytorch
chizhu starred markus-eberts/spert
Hexagram-King starred BrambleXu/knowledge-graph-learning
icoxfog417 pushed to arXivTimes/arXivTimes
icoxfog417 pushed to arXivTimes/arXivTimes
Yevgnen starred BrambleXu/knowledge-graph-learning
......

4 实时获取激活流

直到现在,我们都可以通过 API 获取 JSON 数据文件,解析 JSON 文件得到活动流。接下来,我们将消除 JSON 文件,直接获得活动流。

查看我的其他帖子 中等 一分类查看
GitHub:
荆棘徐 领英: 徐亮 博客:

参考

  • 用一个简单的 Python 爬虫下载课程资料
  • https://developer.github.com/v3/
  • https://developer . github . com/v3/auth/# via-oauth-and-personal-access-tokens
  • https://developer . github . com/v3/activity/events/# list-events-a-user-has-received

构建一个 React + Flask 应用程序,用 Python 图形推荐小说

原文:https://towardsdatascience.com/build-a-react-flask-app-that-suggests-novel-novels-with-a-python-graph-9491e714bbdf?source=collection_archive---------12-----------------------

Photo by Kourosh Qaffari on Unsplash

项目总结:

  • 建立一个用户和他们阅读的书籍的图表数据库
  • 开发一个 Flask 应用程序,根据用户提交的收藏夹向用户提供稀有、有趣的书籍
  • 实现一个与 Flask +我们的 Graph 集成的 React 应用程序,向用户展示他们下一本最喜欢的书

有很多学习数据科学技术的好资源:MOOCS、博客、教程和训练营都是学习的途径。就我个人而言,我通过从事我觉得有趣和吸引人的项目学习得最好。没有什么比解决一个有趣的问题更能激励我把自己的理解水平推向新的高度了。我如何确定我可以探索的新途径?一般靠看别人的作品!

这个项目的想法最初是通过阅读维基·博伊基斯在伟大的博客上发表的关于网飞推荐引擎性质变化的帖子而实现的。简而言之(抱歉,维基),通过矩阵分解或深度学习来最小化用户评级偏好的 RMSE 的日子已经一去不复返了。推荐在一定程度上仍然是一种艺术形式,上下文、细微差别和设计都在为用户提供良好体验方面发挥着重要作用。此外,推荐的商业价值不仅仅是为用户提供最好的内容。在网飞的情况下,可能是为了给用户提供最好的网飞内容。

我很高兴参加今年在哥本哈根举行的 RecSys 2019 大会。最佳论文的获胜者是而不是最先进的神经网络方法。相反,它是对以前的开源推荐系统、公共数据集以及它们在面对面比赛中的实际表现的仔细检查。结果,你可能会问?简单模型(基于图形、用户/项目最近邻居、热门项目)几乎总是优于最佳深度神经网络。

可以肯定地说,鉴于这些启示,我一直在重新思考推荐系统。作为一个用户,我想从 YouTube 的推荐引擎中得到什么?像 Ecosia /DuckDuckGo 这样的搜索引擎呢?在前一个例子中,我可能想看在那个时刻娱乐/通知我的视频。在后一个例子中,我希望快速找到最相关的信息,而不用担心利益冲突。在这两种情况下,返回的项目可能不是最受欢迎的,最高评级的,或有争议的。广告商有时发现病毒式传播是有利可图的,但我可能不是一个粉丝。

那么我真正想要的是什么?我怎样才能建立一个系统来优化这个难以实现的、无利可图的目标呢?有一天,当我步行去图书馆时,我的脑海中出现了一个具有里程碑意义的清晰时刻:当我找到一个我从未考虑过或听说过的新作者或新书时,我绝对喜欢。当我翻阅书页时,那个时刻的纯粹发现和新奇感可以激发几个星期的快乐。

花在阅读一本书上的时间是一种投资,因此尝试任何新事物(探索与利用)可能是一个困难的命题。一个仅仅推荐随机项目的系统可能会提供意想不到的结果,但它们可能不是好的结果。我想鱼和熊掌兼得。我想找到我以前从未考虑过的好书。

这个想法很简单:

  • 和我阅读和喜欢相同书籍的人可能和我有大致相似的品味
  • 这些邻居可能读过很多书,其中一些我没有读过
  • 这些未读的书中,有些可能是如此晦涩/冷门,以至于我不太可能自己去发现它们

以上陈述都不会让你感到惊讶,尤其是前两点。当然,最后一点对这项工作来说是最基本的。如果我建立一个图书、作者和读者的图形数据库,然后沿着图形的节点寻找这些隐藏的图书瑰宝,会怎么样?

GoodReads 网站已经发布了一个包含 10,000 本书、50,000+用户和近 600 万个书籍评级(1-5 星)的数据集。我肯定能在这个宝藏中找到一些有趣的东西!我从来没有建立过真正的图形,所以我认为这将是一个伟大的尝试。节点和边,能有多难?这个图的节点是UsersBooksAuthors:

边缘是与每个(User, Book, Author, rating)元组相关联的评级:

我首先创建了 4 个类来解释我的图形组件。每个节点还会有一个与之关联的列表:Users会有一个Books的书架,Authors会有他们的参考书目,Books会有他们的受众。Graph将把所有这些对象连接成一个可以被遍历的内聚单元。

上面的代码块很大,里面有很多方法,这里就不探讨了。我们将关注一个可以直接与一个简单的、面向消费者的 React 应用程序 API 一起使用的 API: _book2book。所以让我们说,我们现在可以绕着我们的图走,一切都编译好了。我们如何才能找到有意义的Books呈现给User?在我们看来,让我们假设一下:

  1. 作为一个User,你喜欢一个AuthorBook(例如,五星评级)
  2. 这个Author粉丝很多,她也写过类似的Books你可能也会喜欢。这些不是小说,你可能已经知道了。
  3. 这个Author有很多粉丝,有些可能和你差不多
  4. 这些Users中的一个被随机选中,将会有一个收藏Books的架子
  5. 从这个货架上选择最不受欢迎的五星级Book
  6. 要么将此提交给User,要么返回步骤 1,继续浏览图表

Simplistic example of our graph

现在我们只需要将这个Book返回给我们的User作为一个可能的伟大发现!不过,我们遗漏了这个项目的关键要素。我们需要应用程序来提供这些有趣的Book。为了熟悉起见,让我们从仍然基于 Python 的部分开始:我们需要构建处理 API 请求的应用程序后端,因此,当然,我们将使用 Flask 。Flask 允许我们用 Python 制作一个小型的 web 服务器,而且非常容易使用。我发现两个教程非常有用,它们很好地链接在一起:一个 Flask 教程和一个调用前面提到的 Flask 应用的 React 应用。

所以我们首先创建一个新目录,我们称之为api/。在这个文件夹中,我们将有两个 python 文件。__init__.py将建立我们的烧瓶应用程序的基础,导入必要的变量等。:

然后是一个app.py(或者随便你怎么称呼):

app.py Example

app.py里面,我们正在做一些重要的事情。首先,我们调用 python 代码来构建我们的图形数据库,以查找新书。它还设置了我们后端的Blueprint,所以当我们对这个服务进行 API 调用时,我们指向了正确的方向。让我们从最基本的层面解开这些东西:

  • 我们的应用程序名称被初始化为@main,但是我们也可以在这里有其他的东西
  • 我们在我们的@main应用中托管了两个.routes:/input_book/novel_novel
  • 你可以把它们想象成我们 Web 应用程序上的独立页面,它们都处理不同的行为
  • GETPOST的 HTTP 方法,它们从我们的后端获取一些东西或者向它发送一些东西(好术语!)

对于/input_book调用,我们正在做的是从网页上的User接收一个原始文本输入,将其打包成一个看起来像{"book": "book_title"}的 JSON,然后更新后端服务器。在这种情况下,我们使用这本书来寻找User可能喜欢的类似的稀有书籍。我们用这个输入调用 Graph,并将一个global python 变量设置为输出图书的图像 URL。

现在你可以想象 WebApp 会对 Flask App 进行第二次 API 调用,说“那个输入的输出是什么?”。这是我们在/novel_novel中的GET调用,它也返回一个 JSON。对你们大多数人来说,这听起来很合理,对我来说也是如此。你可能以前用过 Flask。但是你以前做过React App 吗?

React 是一个脸书赞助的开源 JavaScript 库,旨在使用户界面更容易编码。我仍然花了一些时间来学习一些 JavaScript 基础知识,理解 React 的语法,并试图让一切看起来体面。我使用的两个重要资源是之前提到的 YouTube 和这篇关于数据科学的文章。视频指南利用了第二个 React 库semantic-ui-react,这使得应用程序的构建更加容易,所以我基本上遵循了他们的建议。老实说,你也应该这样。安装必要的工具、构建样板文件App.js以及解释编写函数的基础工作最好留给 JavaScript 专业人员去做。

不过,我将详细介绍一下实际的 React 组件,这些组件是我为了与我们的图表集成在一起而组装的。这里的主要函数是App.js文件,所以我们将首先解包它。我们导入必要的库,初始化App(),然后返回一些类似 HTML/JavaScript 的实体(部门、容器、图像等)。):

App.js

你会注意到在Container里面我们有两个组件:<BookEntry/><GrabBook/>。这些对应于我们 Flask 应用程序中的两个 API 调用,这是我们的图形理解的标题POST,然后执行一个GET调用来为用户抓取我们感兴趣的新小说。让我们先来看看更复杂的组件BookEntry:

我们从这个组件{ Form, Input, Button }中的semantic-ui-react导入了一些额外的包。我们希望用户将文本输入到与他们喜欢的书名对应的Form中,然后点击提交Button。首先,我们初始化几个变量(const [title, setTitle])并设置默认值= useState('');,一个空字符串。

接下来,我们构建<Form.Field>,并添加一个placeholder值,为用户提供做什么的指示。一旦用户输入了一些文本,我们将titleuseState设置为输入的文本。轻松点。

接下来,我们添加一个形状为Button的新<Form.Field>,用户可以在文本输入的正下方点击它。单击按钮(<Button onClick= ...),我们通过await fetch("/input_book"调用对我们的 API 进行第一次调用。回想一下,在我们的 Flask 应用程序中,我们的蓝图有@main.route('/input_book',所以这个调用在这个接触点上到达我们的后端。这个POST调用的返回值只是一个response,表示当且仅当我们在图中找到标题时一切正常。否则我们返回一个400并要求用户提交一个新的标题。有可能我们的数据库没有标题,或者可能拼写不同,等等。,所以我们不希望我们的应用程序在这些情况下崩溃。

在进入下一个组件之前,我鼓励任何试图构建这些 web 应用程序的人在代码中到处使用console.log()调用。它们不仅有注释的双重作用,在调试代码时也很有帮助。回想一下,你只需右键点击 Chrome 中的任何网页,Inspect,然后打开console标签,你就可以看到正在发生的一切。

所以,让我们假设一切进展顺利,我们已经找到了相关的标题。现在我们实际上必须在我们的GrabBook组件中为用户返回一本新书:

这里我们通过一个fetch("/novel_novel")POST端点进行另一个 API 调用。请记住,我们正在返回一个类似于{"image_url": "http://image_of_book.png"}的 JSON 对象。然后,我们将它传递给一个<Image>块,该块呈现图像并将其返回给我们的应用程序。酷!

The input was Neuromancer by William Gibson. The output is a great book!

公平地说,这个页面看起来不怎么样。如果你是 React 专家,请告诉我如何才能让布局看起来更好:

  • 提交请求后,需要刷新页面以显示正确的图像。
  • 我也更喜欢背景图片覆盖整个页面,并且表单域在它的前景中。

这项工作并不打算成为一个商业化、生产就绪的系统。这是一次有趣的学习冒险,并且在它的范围内发人深省。如果你有兴趣查看的话,这里有一个回购的链接。

谢谢你的来访!欢迎在下面评论,求书。我个人已经从图表中找出了 10 多条建议,我几乎总是对结果感到惊讶。我很高兴我研究了这些建议,虽然有些不是我喜欢的,但其他的都是很棒的发现。

在 30 分钟内构建一个实时对象检测 Web 应用程序

原文:https://towardsdatascience.com/build-a-realtime-object-detection-web-app-in-30-minutes-7ad0cb2231fb?source=collection_archive---------7-----------------------

Image Credit: https://github.com/tensorflow/models/tree/master/research/object_detection

张量流。射流研究…

Tensorflow.js 是一个开源库,使我们能够使用 Javascript 在浏览器中定义、训练和运行机器学习模型。我将使用 Angular 中的 Tensorflow.js 框架来构建一个 Web 应用程序,该应用程序可以检测网络摄像头视频馈送上的多个对象。

COCO-SSD 型号

首先,我们必须选择我们将用于对象检测的预训练模型。Tensorflow.js 提供了几个预训练模型,用于分类、姿态估计、语音识别和对象检测目的。查看所有 Tensoflow.js 预训练模型了解更多信息。

COCO-SSD 模型是一种预训练的对象检测模型,旨在定位和识别图像中的多个对象,是我们将用于对象检测的模型。

Photo by Brooke Cagle on Unsplash

原 ssd_mobilenet_v2_coco 模型大小为 187.8 MB,可从 TensorFlow 模型动物园下载。与原始模型相比,Tensorflow.js 版本的模型非常轻量级,并针对浏览器执行进行了优化。

Tensorflow.js COCO-SSD 默认的物体检测模型是‘lite _ mobilenet _ v2’体积非常小,1MB 以下,推理速度最快。如果您想要更好的分类准确性,您可以使用‘mobilenet _ v2’,在这种情况下,模型的大小增加到 75 MB,这不适合 web 浏览器体验。

“model . detect”直接从 HTML 中获取图像或视频输入,因此在使用之前,您无需将输入转换为张量。它返回检测到的对象的类、概率分数以及边界框坐标的数组。

model.detect(
 img: tf.Tensor3D | ImageData | HTMLImageElement |
   HTMLCanvasElement | HTMLVideoElement, maxDetectionSize: number
)
[{
 bbox: [x, y, width, height],
 class: "person",
 score: 0.8380282521247864
}, {
 bbox: [x, y, width, height],
 class: "kite",
 score: 0.74644153267145157
}]

Photo by Marc Kleen on Unsplash

ANGULAR WEB 应用正在初始化

在我们清楚了模型之后,是时候使用 Angular 命令行界面来初始化 Angular web 应用程序了。

npm install -g @angular/cli
ng new TFJS-ObjectDetection
cd TFJS-ObjectDetection

然后我将使用 NMP 包管理器加载 Tensorflow.js 和 COCO-SSD 库。

TFJS-ObjectDetection **npm install @tensorflow/tfjs --save** TFJS-ObjectDetection **npm install @tensorflow-models/coco-ssd --save**

现在都准备好了。所以我们可以开始编码了。我先从‘app . component . ts’导入 COCO-SSD 模型开始。

import { Component, OnInit } from '@angular/core';**//import COCO-SSD model as cocoSSD**
import * as cocoSSD from '@tensorflow-models/coco-ssd';

开始网络摄像机馈送

然后,我将使用以下代码启动网络摄像头。

webcam_init()
{
  this.video = <HTMLVideoElement> document.getElementById("vid");
  navigator.mediaDevices
  .getUserMedia({
  audio: false,
  video: {facingMode: "user",}
  })
  .then(stream => {
  this.video.srcObject = stream;
  this.video.onloadedmetadata = () => {
  this.video.play();};
  });
}

物体检测功能

我们需要另一个函数来加载 COCO-SSD 模型,该模型也调用‘detect frame’函数来使用来自网络摄像头馈送的图像进行预测。

public async predictWithCocoModel()
{
  const model = await cocoSSD.load('lite_mobilenet_v2');
  this.detectFrame(this.video,model);

}

‘detect frame’函数使用 requestAnimationFrame 通过确保视频馈送尽可能平滑来反复循环预测。

detectFrame = (video, model) => {
  model.detect(video).then(predictions => {
  this.renderPredictions(predictions);
  requestAnimationFrame(() => {
  this.detectFrame(video, model);});
  });
}

Photo by Mikhail Vasilyev on Unsplash

渲染预测

同时,‘render predictions’函数将检测到的对象的边界框和类名绘制到屏幕上。

renderPredictions = predictions => {const canvas = <HTMLCanvasElement> document.getElementById     ("canvas");

  const ctx = canvas.getContext("2d");  
  canvas.width  = 300;
  canvas.height = 300;
  ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);  

  **// Fonts**
  const font = "16px sans-serif";
  ctx.font = font;
  ctx.textBaseline = "top";
  ctx.drawImage(this.video,0,0,300,300);

  predictions.forEach(prediction => {  

   **// Bounding boxes's coordinates and sizes**
   const x = prediction.bbox[0];
   const y = prediction.bbox[1];
   const width = prediction.bbox[2];
   const height = prediction.bbox[3];**// Bounding box style**
   ctx.strokeStyle = "#00FFFF";
   ctx.lineWidth = 2;**// Draw the bounding
**   ctx.strokeRect(x, y, width, height);  

   **// Label background**
   ctx.fillStyle = "#00FFFF";
   const textWidth = ctx.measureText(prediction.class).width;
   const textHeight = parseInt(font, 10); // base 10
   ctx.fillRect(x, y, textWidth + 4, textHeight + 4);
   });

   predictions.forEach(prediction => {
    **// Write prediction class names**
    const x = prediction.bbox[0];
    const y = prediction.bbox[1];  
    ctx.fillStyle = "#000000";
    ctx.fillText(prediction.class, x, y);});   
   };

现在,在浏览器上执行对象检测所需的所有功能都已准备就绪。

我们只需要在【ngOnInit】上调用‘web cam _ init’‘predictwithcocomoodel’就可以在启动时初始化 app。

ngOnInit()
{
  this.webcam_init();
  this.predictWithCocoModel();
}

Photo by Andrew Umansky on Unsplash

用于对象检测的 HTML 元素

剩下的最后一步是修改 'app.component.html' ,以包含上述功能工作所需的 < video >< canvas > HTML 元素。

<div style="text-align:center">
  <h1>Tensorflow.js Real Time Object Detection</h1>
  <video hidden id="vid" width="300" height="300"></video>
  <canvas id="canvas"></canvas>
</div>

完整代码

访问我的 GitHub 资源库获取该项目的完整代码。

演示 WEB 应用程序

访问现场演示应用程序,查看运行中的代码。该应用程序在 Google Chrome 浏览器中运行没有任何问题。如果您使用任何其他浏览器,请确保您使用的浏览器支持' requestAnimationFrame'

用 Python 和 Google Search 构建一个简单的聊天机器人

原文:https://towardsdatascience.com/build-a-simple-chatbot-with-python-and-google-search-c000aa3f73f0?source=collection_archive---------1-----------------------

Build a Simple Python ChatBot from Scratch Using Google Search

今天我们要构建一个 Python 3 聊天机器人 API 和 web 界面。聊天机器人很难构建,因为有无限多的输入。正因为如此,一个能够不断给出好答案的聊天机器人需要大量的知识。

开发人员通常会将机器学习算法、NLP 和预定义答案的语料库应用到他们的聊天机器人系统设计中。我们将保持我们的代码基本,所以我们将绕过为我们的聊天机器人创建一个复杂的“大脑”。

我们不会建立一个人工智能大脑,而是使用一个免费的和已经建立的大脑:谷歌搜索。

我们的聊天机器人将对用户的查询执行 Google 搜索,从第一个结果中抓取文本,并用该页面文本的第一句话回复用户。

我们开始吧!顺便说一下,所有提到的代码都在 Python ChatBot GitHub 库中。

用 Python 在 Google 中查询聊天机器人回复

为了用 omniscience(无限知识)编写简单的聊天机器人,我们将在 Python API 中进行 Google 搜索。幸运的是,有一个 Google search Python 库,我们可以用 pip 安装它。

在本地安装了 Google 库之后,您可以像这样编写 Python 代码:

一旦我们从搜索结果中获得了一个 URL 列表,我们就可以使用 Python 请求库对该网页进行 GET 请求。我们也可以通过使用来自 lxml 的html和 BeautifulSoup 来解析 HTML。

这个项目的所有 Python 依赖项都可以在 GitHub 存储库的[requirements.txt](https://github.com/lmzach09/Python_ChatBot_Google/blob/master/requirements.txt) 文件中找到。

这是一个完整的文件,我们的 HTTP 服务器可以将它作为一个依赖项导入。我制作了一个方法,它可以进行 Google 搜索,获取网页上的第一个<p>,并以字符串形式返回其内容。如果搜索以任何方式失败,聊天机器人将回复“对不起,我想不出答案。”

现在我们可以接受用户输入并进行谷歌搜索。我们将对搜索的第一个结果发出一个 HTTP GET 请求。然后我们解析返回的 HTML,分离出该页面上第一个<p>中的第一句话。这是我们聊天机器人的回复算法,不需要机器学习。

一个简单聊天机器人的 Python API

接下来,我们需要建立一个服务器应用程序,这将是我们的聊天机器人查询的 API。它将响应 HTTP 请求。首先,这些请求将来自一个简单的 HTML 页面,我们稍后会制作这个页面。

首先,我们将导入 Python 3 HTTP 服务器和 socket 服务器库,以及我们之前创建的 Google 搜索文件。

我们的 API 将在端口 8080 上提供服务,我们将从项目的父目录中名为public的文件夹中提供网页资产。接下来,我们将为 GET 和 POST 请求创建自己的处理程序。

HTTP GET 请求将尝试从public文件夹中返回相应的文件。这些将是我们 web 浏览器界面的 HTML、CSS 和 JavaScript 文件。帖子请求将用于聊天机器人查询。

最后,我们将启动服务器并使用我们的处理程序。这是整个文件,包括上面的代码片段。

我们可以使用 CURL 来测试带有 POST 请求的聊天机器人 API。

curl -d "how old is samuel l jackson" [http://localhost:8080](http://localhost:8080)

Screenshot of POST request output of the ChatBot API

接下来我们将制作一个可以查询这个 API 的 HTML 页面。最终,我们将拥有一个端到端的聊天机器人,提供复杂的答案。

构建聊天机器人网页

我们的网页将非常简单。它将包含一个机器人的图片,一个文本输入字段和一个提交按钮。每当用户提交一个输入,chatbot API 就会通过 POST 请求到达。从 API 返回的文本答案将被填入网页。

这是 HTML 页面。将此作为index.html保存在我们之前提到的public文件夹中。机器人的图像文件也在完整的 Python ChatBot GitHub 库中。

接下来,我们将为这个网页做一些快速的样式。将这个 CSS 文件也保存在public文件夹中。它已经在 HTML 文件的<head>部分被引用。

现在,您应该有一个简单的聊天机器人网页可供用户输入。以下是截图:

ChatBot Web Page Screenshot

网页还没有完全为用户准备好。它需要 JavaScript。

我们将编写一些 JS 来检测用户按下回车键并点击提交按钮。当这些事件中的任何一个发生时,我们将获得用户输入字段中的文本,并将其作为我们的 Python 服务器的 POST 主体。

我们将使用fetch方法向 Python API 服务器发出 HTTP POST 请求。现代网络浏览器现在默认包含了获取 API 。

这是我们与 3 个 HTML 元素交互的简单 JavaScript。将此作为app.js保存在公共文件夹中。

我们几乎准备好将我们的 Python 聊天机器人投入生产了。

运行我们从头开始制作的简单 Python 聊天机器人

现在我们已经写完了所有的代码,在运行全栈 Python 聊天机器人之前,我们还有一个步骤。如果还没有,在项目的父目录中创建一个requirements.txt 文件,与 2 个 Python 文件放在一起。该文件是一个 Python 范例,用于轻松安装项目的依赖项。

使用命令行转到 ChatBot 项目的父目录。运行 Python 库安装命令。

pip install -r requirements.txt

您的机器现在拥有运行聊天机器人所需的所有库!让我们运行全栈应用程序。

python server.py

接下来打开你的网络浏览器,进入 http://localhost:8080/ 。如果你看到聊天机器人的图像,它正在工作。

尝试一些输入!

谁扮演了钢铁侠

塞缪尔·杰克逊多大了

火星上的天气怎么样

你可以看到,我们的聊天机器人回复并不完美,但对于几分钟的工作来说已经很不错了。

我会继续用 Python 探索 AI 和机器学习,我会分享我的发现。查看我的媒体页面的 JavaScript、 Dart 和 Python 指南。

用 TensorFlow.js 构建一个简单的神经网络

原文:https://towardsdatascience.com/build-a-simple-neural-network-with-tensorflow-js-d434a30fcb8?source=collection_archive---------13-----------------------

创建一个神经网络模型来做出笔记本电脑购买决策

TL;DR 在 TensorFlow.js 中建立一个简单的神经网络模型,做出笔记本电脑购买决策。了解为什么神经网络需要激活函数,以及应该如何初始化它们的权重。

现在是午夜,你正微笑着做着一些令人惊恐的梦。突然,你的电话开始响了,相当国际化。你半睡半醒地拿起电话,听一些奇怪的事情。

你的一个朋友从地球的另一边打电话来,请求帮助挑选一台笔记本电脑。毕竟今天是黑色星期五!

这是你五年来第一次收到朋友的来信,这让你有点惊讶。不过,你是个好人,同意帮忙。也许是时候将你的 TensorFlow.js 技能付诸实践了?

不如你做一个模型来帮助你的朋友,这样你就可以继续睡觉了?你听说过神经网络现在非常热门。现在是凌晨 3 点,你不需要太多的劝说。这一次你将使用神经网络!

在你的浏览器中运行本教程的完整源代码:

GitHub 上的完整源代码

神经网络

什么是神经网络?按照经典的扣人心弦的方式,我们将从远离回答这个问题开始。

神经网络已经存在了一段时间(从 20 世纪 50 年代开始)?为什么它们最近才变得流行起来(最近 5-10 年)?由沃伦·麦卡洛克和沃尔特·皮茨于年首次提出的神经活动固有思想的逻辑演算神经网络直到 20 世纪 80 年代中期才真正流行起来,当时支持向量机和其他方法控制了整个社区。

通用逼近定理指出,神经网络可以逼近任何函数(在一些温和的假设下),即使只有一个隐藏层(稍后将详细介绍)。第一批证明之一是由乔治·西本科于 1989 年为乙状窦激活函数做的。

最近,深度学习领域越来越多的进展使得神经网络再次成为热门话题。为什么?我们稍后会讨论这个问题。首先,我们从基础开始!

感知器

最初的模型是由弗兰克·罗森布拉特在 20 世纪 50 年代提出的,旨在模拟人类大脑如何处理视觉数据和学习识别物体。感知器接受一个或多个二进制输入 x1,x2,…,xn,并产生一个二进制输出:

要计算输出,您必须:

  • 具有表示相应输入的重要性的权重 w1、w2、…、wn
  • 二进制输出(0 或 1)取决于加权和∑ wj xj 是大于还是小于某个阈值

让我们看一个例子。假设您需要决定是否需要一台新的笔记本电脑。最重要的特征是它的颜色和大小(她是这么说的)。因此,您有两个输入:

1.它是粉红色的吗?

2.它小吗(明白了)?

你可以用二元变量 x_ pink ,x_ small 来表示这些因素,并给每一个赋予权重/重要性 w_ pink ,w_ small 。根据你对每个因素的重视程度,你可以得到不同的模型。

我们可以进一步简化感知器。我们可以将∑ w_j x_j 重写为两个向量 w . x 的点积,接下来,我们将引入感知机的偏差,b = -threshold。使用它,我们可以将模型重写为:

偏差是衡量感知器输出 1(触发)的难易程度。较大的正偏置使输出 1 变得容易,而较大的负偏置使输出 1 变得困难。

让我们使用 TensorFlow.js 构建感知器模型:

有人出价购买笔记本电脑。它不是粉红色的,但它是小 x = [0,1]。你偏向于不买笔记本电脑,因为你破产了。你可以用负偏压来编码。你是一个比较聪明的用户,你更注重尺寸,而不是颜色 w = [0.5,0.9]:

1

是的,你必须买那台笔记本电脑!

乙状结肠神经元

为了使从数据中学习成为可能,我们希望在给出一个例子时,模型的权重只发生少量的变化。也就是说,每个示例都会导致输出发生微小的变化。

这样,人们可以在呈现新数据的同时不断调整权重,而不必担心单个示例会抹去模型迄今为止已经学习的所有内容。

感知器不是理想的,因为输入的小变化会线性传播到输出。我们可以使用一个s 形神经元来克服这个问题。

乙状结肠神经元具有输入 x1,x2,…,xn,其值可以在 0 和 1 之间。输出由σ(w . x + b)给出,其中σ为 sigmoid 函数,定义如下:

让我们用 TensorFlow.js 和 Plotly 来看看:

使用权重和输入,我们得到:

让我们更深入地了解乙状结肠神经元,并理解它与感知器的相似之处:

  • 假设 z 是一个大的正数。那么 e^{-z} ≈ 0,σ(z) ≈ 1
  • 假设 z 是一个大的负数。那么 e^{-z} → ∞,σ(z) ≈ 0
  • 当 z“有些适度”时,我们观察到与感知器相比的显著差异。

让我们使用 TensorFlow.js 构建 sigmoid 神经元模型:

另一个笔记本电脑的报价出现了。这一次你可以指定颜色接近粉红色的程度,以及它有多小。

颜色有些粉,大小刚好 x = [0.6,0.9]。其余的保持不变:

0.6479407548904419

是的,你还是想买这款笔记本电脑,但是这款也输出了其决策的自信。很酷,对吧?

构建神经网络

扩展上述模型的一个自然方法是以某种方式将它们分组。一种方法是制造多层神经元。这里有一个简单的神经网络,可以用来做出购买笔记本电脑的决定:

神经网络是神经元的集合,连接在一个非循环图中。一些神经元的输出被用作其他神经元的输入。它们被组织成层。我们的示例由全连接层(两个相邻层之间的所有神经元都是连接的)组成,它是一个 2 层神经网络(我们不计算输入层)。由于构建神经网络的神经元做出的简单决定的组合,神经网络可以做出复杂的决定。

当然,输出图层包含您正在寻找的答案。让我们来看看使训练神经网络成为可能的一些因素:

激活功能

感知器模型只是一个线性变换。将多个这样的神经元相互堆叠会导致矢量积和偏差相加。不幸的是,有很多函数不能通过线性变换来估计。

激活函数使模型能够逼近非线性函数(预测更复杂的现象)。好消息是,你已经遇到了一个激活函数 sigmoid:

Sigmoid 函数的一个主要缺点是,它在[-3,+3]范围之外变得非常平坦。这导致权重接近 0——没有学习发生。

ReLU

ReLU,在整流线性单元的神经网络环境中引入,改进受限玻尔兹曼机器,在大于 0 和 0 的值处具有线性输出。

让我们来看看:

ReLU 的一个缺点是负值“消亡”,停留在 0——没有学习。

泄漏的 ReLU

整流器非线性改善神经网络声学模型中引入的泄漏 ReLU 解决了 ReLU 引入的死值:

请注意,负值会被缩放,而不是清零。缩放可通过 tf.leakyRelu() 中的参数进行调整。

重量初始化

训练神经网络做出“合理”预测的过程包括多次调整神经元的权重。这些权重需要有初始值。你应该如何选择那些?

初始化过程必须考虑我们用来训练模型的算法。通常,这个算法是随机梯度下降(SGD) 。它的工作是搜索可能的参数/权重,并选择那些使我们的模型产生的误差最小的参数/权重。此外,该算法严重依赖于随机性和良好的起点(由权重给出)。

相同常数初始化

假设我们使用相同的常数(是的,包括 0)初始化权重。网络中的每个神经元将计算相同的输出,这导致相同的权重/参数更新。我们刚刚挫败了拥有多个神经元的目的。

过小/过大值初始化

让我们用一组小值初始化权重。将这些值传递给激活函数将使它们呈指数级下降,从而使每个权重都变得同等重要。

另一方面,用大值初始化将导致指数增长,使得权重同样不重要。

随机小数字初始化

我们可以使用平均值为 0、标准偏差为 1 的正态分布用小随机数初始化权重。

每个神经元会计算不同的输出,从而导致不同的参数更新。当然,还有其他多种方式。检查 TensorFlow.js 初始化器

你应该买笔记本电脑吗?

现在你已经知道了一些神经网络的功夫,我们可以使用 TensorFlow.js 来建立一个简单的模型,并决定你是否应该购买一台给定的笔记本电脑。

笔记本电脑数据

这么说吧,对于你的朋友来说,大小比粉嫩程度重要多了!你坐下来设计以下数据集:

干得好!你在整合朋友偏好方面做得很好。

建立模型

回想一下我们将要构建的神经网络:

让我们将其转换为 TensorFlow.js 模型:

我们有一个 2 层网络,输入层包含 2 个神经元,隐藏层包含 3 个神经元,输出层包含 2 个神经元。

注意,我们在隐藏层使用 ReLU 激活函数,在输出层使用 softmax。我们在输出层有 2 个神经元,因为我们想知道我们的神经网络在购买/不购买决策中有多确定。

我们使用二元交叉熵通过测量预测的“好”程度来衡量我们模型的当前权重/参数的质量。

我们的训练算法,随机梯度下降,试图找到使损失函数最小化的权重。对于我们的例子,我们将使用 Adam 优化器。

培训

既然我们的模型已经定义,我们可以使用我们的训练数据集来教授它关于我们的朋友偏好的信息:

我们在训练前重组数据,并在每个时期完成后记录进度:

Epoch 1
Loss: 0.703386664390564 accuracy: 0.5
Epoch 2
Loss: 0.6708164215087891 accuracy: 0.5555555820465088
Epoch 3
Loss: 0.6340110898017883 accuracy: 0.6666666865348816
Epoch 4
Loss: 0.6071969270706177 accuracy: 0.7777777910232544
...
Epoch 19
Loss: 0.08228953927755356 accuracy: 1
Epoch 20
Loss: 0.06922533363103867 accuracy: 1

在经历了大约 20 个时期之后,这个模型似乎已经了解了你朋友的偏好。

评价

你保存模型,并将其发送给你的朋友。连接到您朋友的计算机后,您找到一台合适的笔记本电脑,并将信息编码到模型中:

等待几毫秒后,您会收到一个回答:

0: 0.45
1: 0.55

这个模型适合你。它“认为”你的朋友应该购买笔记本电脑,但它对此并不确定。你做得很好!

结论

你的朋友似乎对结果很满意,而你正考虑通过将你的模型作为浏览器扩展来销售,从而赚上百万。不管怎样,你学到了很多:

  • 感知器模型
  • 为什么需要激活功能,使用哪一个
  • 如何初始化神经网络模型的权重
  • 建立一个简单的神经网络来解决一个(有点)实际的问题

在浏览器中运行本教程的完整源代码:

GitHub 上的完整源代码

躺在舒适的枕头上,你开始思考。我可以用深度学习来做这个吗?

参考

减少损失:梯度下降

梯度下降和随机梯度下降从零开始

初始化神经网络

重量初始化的类型

如果不使用神经网络中的任何激活函数会怎样?

原载于https://www.curiousily.com

建立机器学习模型(特别是深度神经网络),可以轻松地与现有或新的 web 应用程序集成。想想您的 ReactJs、Vue 或 Angular 应用程序通过机器学习模型的强大功能得到了增强:

[## JavaScript 黑客的深度学习

建立机器学习模型(特别是深度神经网络),您可以轻松地与现有或新的网络集成…

leanpub.com](https://leanpub.com/deep-learning-for-javascript-hackers)

用不到 50 行 Python 代码构建一个文本生成器 Web 应用程序

原文:https://towardsdatascience.com/build-a-text-generator-web-app-in-under-50-lines-of-python-9b63d47edabb?source=collection_archive---------9-----------------------

学习构建一个自动完成任何输入文本的 web 应用程序

我们将使用 OpenAI 的 GPT-2 作为模型,使用面板作为网络仪表板框架。本指南将分为两部分。在第一部分中,我们将加载我们的模型并编写一个预测函数。第二,我们将构建 web 应用程序。

Example text generation application. We will be building a simpler variation of this web app.

你需要什么

本教程假设你已经安装了 Python 3.7+ ,并且对语言模型有所了解。虽然相关步骤可以在 Jupyter 之外完成,但是强烈推荐使用 jupyter 笔记本。

我们将使用 PyTorch 作为我们深度学习库的选择。在 PyTorch 内,我们将使用 变形金刚 库导入预先训练好的 OpenGPT-2 模型。您可以通过在 bash 中单独输入以下命令来安装这些库:

pip install torchpip install transformers

对于我们的 web 应用程序,我们将利用 面板 ,这是一个很好的工具,可以从 jupyter 笔记本或常规 python 脚本轻松创建可服务的仪表板。使用以下命令安装 panel:

pip install panel

第一部分:建立模型

OpenAI 的 GPT 是一种变形金刚模型,它产生类似人类文本的能力引起了很多关注。如果你以前没有尝试过,在读完这篇文章后,你可能会有同样的想法。

加载模型

首先,我们需要导入所需的包。

import numpy as np
import torch
import torch.nn.functional as F
from transformers import GPT2Tokenizer, GPT2LMHeadModel
from random import choice

接下来,我们将加载 OpenGPT2 标记器和语言模型:(如果第一次运行,可能需要几分钟来下载预训练的模型)

tok = GPT2Tokenizer.from_pretrained("gpt2")
model = GPT2LMHeadModel.from_pretrained("gpt2")

预测功能

在这个阶段,大部分工作已经完成。由于我们的模型是预先训练好的,所以我们不需要训练它,也不需要做任何修改。我们只需要编写一个函数,它可以向模型输入文本并生成预测。

def get_pred(text, model, tok, p=0.7):
    input_ids = torch.tensor(tok.encode(text)).unsqueeze(0)
    logits = model(input_ids)[0][:, -1]
    probs = F.softmax(logits, dim=-1).squeeze()
    idxs = torch.argsort(probs, descending=True)
    res, cumsum = [], 0.
    for idx in idxs:
        res.append(idx)
        cumsum += probs[idx]
        if cumsum > p:
            pred_idx = idxs.new_tensor([choice(res)])
            break
    pred = tok.convert_ids_to_tokens(int(pred_idx))
    return tok.convert_tokens_to_string(pred)

这个函数中发生了很多事情。所以,我们来分解一下。首先,我们对来自 input_ids 的输入文本进行标记和编码。然后,我们要求我们的模型为下一个单词/单词生成一个 logits 向量。在应用 softmax 并按降序排列这些概率后,我们有了一个向量, idxs ,它按概率顺序列出了我们的 vocab 中每个令牌的索引。

在这个阶段,我们可以只选择概率最高的令牌。然而,我们希望能够混合我们的结果,以便相同的输入文本可以生成多种文本。为此,我们将添加一个随机元素,从最有可能的下一个令牌列表中选择一个随机令牌。这样,我们就不会每次都选择相同的预测标记。为此,我们利用 核(Top- p )采样

我们通过遍历每个概率来执行这个操作,直到我们遍历的所有概率之和大于 p,0 到 1 之间的任意数。在超过 p 之前迭代的所有令牌都存储在列表 res 中。一旦超过了 p ,我们就从这个列表中随机选择一个令牌。请记住,我们正在循环的概率列表包含按概率排序的索引。请注意,如果 p 更高,我们的列表中将包含更多的令牌。反之亦然。因此,如果每次想要相同的结果,可以将 p 设置为 0。

现在,让我们测试几次 pred 函数:

每一次,都有不同的结果,这正是我们所期待的。我们的预测函数现在已经准备好了。让我们构建我们的 web 应用程序吧!

第 2 部分:构建 Web 应用程序

面板概述

如果您不熟悉面板,它可以简化创建 web 仪表盘和应用程序的过程。乍一看,您需要知道它有三个主要组件:

  • 面板:可以包含一个或多个窗格(对象)的容器,如文本、图像、图表、小部件等。(它们也可以包含其他面板)
  • 窗格:任何单个对象,如文本、图像、数据帧等。
  • Widgets :用户可调整的项目,如文本输入、滑块、按钮、复选框,可以改变窗格的行为

为了我们的目的,你需要知道的下一件也是最后一件事是,我们有多种方法来定义不同的窗格和小部件如何相互交互。这些被称为“回调”例如,如果某个按钮被按下,其他窗格应该如何更新?稍后我们将定义一个回调函数来完成这个任务。

高级应用概述

我们的文本生成器应用程序将有一个输入用户输入他们想要的文本。接下来,用户应该能够通过按下按钮来生成新的令牌。此后,将使用我们在第 1 部分中定义的函数中的预测标记生成新文本。最后,用户应该能够在已经预测的标记之上继续生成新的文本。

履行

让我们首先导入面板并创建文本输入小部件:

import panel as pn
pn.extension() # loading panel's extension for jupyter compatibility text_input = pn.widgets.TextInput()

现在,如果我们在 jupyter 中执行 text_input ,我们会得到以下结果:

接下来,我们需要一个面板,它将在我们生成越来越多的令牌时存储整个文本:

generated_text = pn.pane.Markdown(object=text_input.value)

注意,我们将文本对象设置为 text_input 的值。我们希望 generated_text 的值与 text_input 的值相同,因为我们将在 generated_text 的顶部预测新文本。随着更多的令牌被添加到我们的序列中,我们将继续预测 generated_text ,直到用户更改 text_input 。在这种情况下,该过程将重新开始。

然而,我们还没有完全完成。虽然 generated_text 在初始化时会采用 text_input 的值,但如果 text_input 的值发生变化,它不会自行更新。为此,我们需要将这两个对象链接在一起,如下所示:

text_input.link(generated_text, value='object')

这里,我们在 text_inputgenerated_text 之间形成了一个单向链接。因此,每当 text_input 的值改变时, generated_text 的值也会改变为新值。参见:

observing linked behavior between text_input and generated_text in a panel. Note: pn.Row as a component is a panel i.e. container of panes and widgets

现在我们有了两个文本对象,让我们创建按钮小部件:

button = pn.widgets.Button(name="Generate",button_type="primary")

很好,现在我们有了一个按钮,我们只需要把它和我们想要的行为联系起来。为此,我们将编写一个回调函数,它将在每次单击按钮时运行:

def click_cb(event):
    pred = get_pred(generated_text.object, model, tok)
    generated_text.object += pred

这里发生了两件事。首先,我们将 generated_text 作为输入传递给我们之前编写的预测函数,该函数给出了一个新的令牌。其次,将此令牌添加到 generated_text 中。每当有新的按钮点击时,重复这个过程。

说到这里,我们还是要把按钮点击和回调函数捆绑起来。我们可以通过以下方式做到这一点:

button.on_click(click_cb)

我们现在已经创建了所有的小部件、窗格和功能。我们只需要把这些东西放在一个面板上,瞧:

app = pn.Column(text_input, button, generated_text); app

Note: pn.Column, similar to pn.Row is another type of panel i.e. container of widgets, panes and even other panels.

让我们添加一个标题和一个简短的描述,我们通过了!

title = pn.pane.Markdown("# **Text Generator**")
desc = pn.pane.HTML("<marquee scrollamount='10'><b>Welcome to the text generator! In order to get started, simply enter some starting input text below, click generate a few times and watch it go!</b></marquee>")final_app = pn.Column(title, desc ,app)

为应用服务

Panel 使服务应用程序变得非常容易。有两种方法可以用来做这件事。第一个是”。show()"命令。这通常用于调试,用法如下。这将启动一个新窗口,我们的 final_app 面板作为 web 应用程序运行。

final_app.show()

为了将其投入生产环境,您需要使用“.servable()"方法。但是,如果您以类似于 show 方法的方式运行它,那么在您当前的笔记本中不会发生任何不同。相反,您必须像这样通过计算机的 bash 来服务笔记本:

panel serve --show text_generation_app.ipynb

这将在本地端口上启动您的应用程序,只要您在笔记本中有以下代码:

final_app.servable()

完成了。

到目前为止,您已经有能力构建自己的文本生成应用程序了。您可以通过添加更多面板组件来进一步构建它。你甚至可以将这个应用嵌入到你的其他项目中。一如既往,你可以在 github 上找到我的代码库。注:题图中的 app 是我在教程笔记本中找到的高级变体:text _ generation _ app . ipynb

[## devkosal/gpt-panel-app

此时您不能执行该操作。您已使用另一个标签页或窗口登录。您已在另一个选项卡中注销,或者…

github.com](https://github.com/devkosal/gpt-panel-app)

额外资源

  • OpenAI GPT-2:通过可视化理解语言生成
  • 开始使用面板
  • 轻松可视化任何数据,从笔记本到仪表盘| Scipy 2019 教程| James Bednar(视频:第一个小时是关于面板)
  • 在服务器上部署 Panel 应用

在 GCP 建立一个有用的 ML 模型来预测披头士的听众

原文:https://towardsdatascience.com/build-a-useful-ml-model-in-hours-on-gcp-to-predict-the-beatles-listeners-1b2322486bdf?source=collection_archive---------23-----------------------

AutoML“自动化机器学习”最近受到了数据科学界的抨击。我在 Google Cloud ML 上发表了一些批评 Google Cloud 的 AutoML first look(Google AutoML 团队确实回答了我的一些请求,参见本文结尾的*** *** 事情)这篇文章将展示一个公民数据科学家在 ML 下处理整个机器学习生命周期是完全合理的。目标是:

  • 解决一个真正的商业问题
  • 在 GCP 托管数据
  • 使用 GCP 的云自动化
  • 进行一些 EDA“探索性数据分析”
  • EDA 如何影响结果
  • GCP AutoML 表上的数据处理
  • 利用内置的数据管道
  • 使用维持验证模型
  • 将模型投入生产
  • 随着时间的推移改进模型
      • Google 刚刚发布的 AutoML 表格中的新内容

启动你的计时器,准备,设置,开始…

解决一个真正的商业问题

目标是基于音乐听众的收听模式来确定他们是否会听披头士;我们将建立一个推荐系统。约翰·列侬难道不会对人工智能的力量印象深刻吗?!

当然,你不会想推荐披头士,所以有人可能不会听披头士。谁会不喜欢披头士呢?不管怎样,这个模型可以适用于任何有足够听力模式数据的艺术家。你只需要关掉目标变量就可以了。

在 GCP 托管数据

数据来源是 ListenBrainz 用户的音乐收听历史。

我们根据听众数量挑选出前 300 名最受欢迎的艺术家。由于 big query 1TB/mo 的自由层处理,这些数据是可用的。

创建数据集的代码可以在我的 github 上找到。如果你不耐烦,只想在这里下载数据(2.19 MB)。

使用 GCP 的云自动化

我已经在 Google Cloud 的 AutoML first look 中发布了更多关于如何开始使用 GCP 云 AutoML 表的细节

进行一些 EDA“探索性数据分析”

Select the Target “Like_The_Beatles”

Cloud AutoML Analyze Tab

通过 Cloud AutoML,我们可以更深入地研究数据。这与你作为一名在人工智能平台上工作的数据科学家所能做到的细节水平相去甚远。尽管如此,这里还是有一些值得观察的地方。例如,听得最多的艺术家是那些空值最少的艺术家。

EDA 如何影响结果

在这种情况下,我们如何处理空值:我们让它们为空。原因是 Null 确实意味着在我们的数据集中没有监听;因此等于零。这里的中间值是错误的。了解你的数据!

GCP AutoML 表上的数据处理

我们需要删除“披头士”栏,因为我们的目标是直接基于这一点。否则,我们总是会得到 100%准确的模型,咄。

利用内置的数据管道

最大的好处是你可以通过管道构建模型,而不需要很多代码。事实上,您可以使用支持多种语言(包括 Python )的客户端库来编写完整的训练代码。

使用维持验证模型

我们使用 AutoML 中的自动数据分割功能。他们说:

数据集在定型、验证和测试子集之间拆分的方式。默认情况下,AutoML 表随机选择 80%的数据行用于定型,10%用于测试,10%用于验证。

模型运行得非常好!我们看到了一个. 937 AUC ROC。

这是 50%分数阈值的混淆矩阵:

现在将模型投入生产

有这么简单吗:

这是有成本的。如果我将主持这个特定的模型,它将花费 0.126 美元每小时,或 91.98 美元/月(乘数 730 小时在一个月内假设)。

下面是我用的等式:0.005 * 2.8 * 9 = 0.126 美元。0.005 美元是机器的数量(他们使用 9 来实现低延迟,我相信这是硬编码的)。2.8 是因为我的型号是 2.8G。

import json
from google.cloud import automl_v1beta1 as automlproject_id = 'mwpmltr'
compute_region = 'us-central1'
model_display_name = 'beatles_machine_l_20191104025339'
input = ['usr', 5.0, None, None, 10, ... ] # list of valuesclient = automl.TablesClient(project=project_id, region=compute_region)response = client.predict(
    model_display_name=model_display_name,
    inputs=inputs)
response###############################################################payload {
  tables {
    score: 0.9896599650382996
    value {
      string_value: "True"
    }
  }
}
payload {
  tables {
    score: 0.010340098291635513
    value {
      string_value: "False"
    }
  }
}

随着时间的推移改进模型

通过使用标签系统在推荐中寻找模式,我们看到整体准确率有了 3.5%的提高

“诀窍”是纠正不良记录(改进地面真实数据)和引入新数据。

在这种情况下,这个新数据是专家应用于音乐的“标签”。这些标签很有预见性。例如,标签“60 年代”和“英国入侵”是高度预测性的。另一方面,与所有机器学习一样,它也可以预测艺术家不属于哪个标签;例如,那些高水平演奏被标记为“电子乐”的人,他们不太可能听甲壳虫乐队的音乐。

* * Google 刚刚发布的 AutoML 表格中的新内容

以下是谷歌添加到 AutoML 表格中的三个有意义的新功能:

  1. 您可以看到最终的模型架构是如何被训练的,以及在每一轮训练中尝试的实验。

详细信息存储在 Stackdriver 中。

我没有仔细看过这些日志。此时,我不知道日志是否包含了重新创建模型所需的所有信息。

2)在线预测可以得到预测级的特征属性。

TypeError: list_models() got an unexpected keyword argument 'feature_importance'

我可能需要更新我的软件包。

3)您可以导出您的模型容器(现在每个人都可以使用)

结论

踏上汽车之旅并不像我曾经想象的那么可怕。这位数据科学家抱怨说,AutoML 的黑箱性质正在慢慢被打开。易用性越来越强。错误和其他问题似乎正在得到解决。

我谨慎地建议人们开始把目光放在 AutoML 上,作为一个跳跃启动器。这并没有减轻对数据科学之外的数据建模的需求。它并没有减轻工具集之外的数据处理和 EDA 的需要(为此,看看 AI 平台笔记本吧!).同样,这并不意味着这些工具所针对的业务分析师类型不需要很好地理解预测建模理论、术语和最佳实践。

构建基于用户的动漫协同过滤推荐引擎

原文:https://towardsdatascience.com/build-a-user-based-collaborative-filtering-recommendation-engine-for-anime-92d35921f304?source=collection_archive---------9-----------------------

基于用户之间的统计相似性进行推荐

今天,我们将为动画建立一个推荐引擎,由基于用户的协同过滤提供动力。这只是推荐系统的几种不同方法中的一种

介绍

在基于用户的协同过滤中:
-如果用户喜欢相似的项目就被认为是相似的
-我们首先发现哪些用户是相似的
-然后推荐其他相似用户喜欢的项目

看看我在上面(煞费苦心)画的图。

桑尼喜欢莫奈、毕加索和达利的画。泰德喜欢莫奈和毕加索的画。

桑尼和泰德很相似,因为他们喜欢一些相同的艺术家。桑妮喜欢达利画,但泰德从未看过达利画。
所以让我们向 Ted 推荐 Dali。

清澈如泥?现在我们已经了解了它是如何工作的,让我们来构建一个推荐器。

构建推荐器

从 Kaggle 下载数据并加载到 2 个数据帧中。

anime . CSV—我们数据库中关于动漫的详细信息
rating . CSV—特定用户对特定动漫的评分

DIR = 'anime-recommendations-database/'import pandas as pdanimes = pd.read_csv(DIR + 'anime.csv')
ratings = pd.read_csv(DIR + 'rating.csv')

通过阅读这些文档,我知道评分值为-1意味着用户已经观看了这部电影,但还没有对它进行评分。我假设这不会给我们任何有用的信息并删除那些记录。

ratings = ratings[ratings.rating != -1]
ratings.head()

animes.head()

数据探索

没有人比我更不喜欢花 75%的时间在数据探索上,所以让我们先了解一下数据的大小和分布。

# number of ratings
len(ratings)
=> 6337241# number of users
len(ratings['user_id'].unique())
=> 69600# number of unique animes (in anime list, not ratings)
len(animes['anime_id'].unique())
#=> 11200# avg number of anime rated per user
import statistics
ratings_per_user = ratings.groupby('user_id')['rating'].count()
statistics.mean(ratings_per_user.tolist())
#=> 91.05231321839081# distribution of ratings per user
# (we may want to exclude users without many data points)
import matplotlib.pyplot as plt
%matplotlib inline
ratings_per_user.hist(bins=20, range=(0,500))

most users have rated fewer than 100 anime

# avg number of ratings given per anime
ratings_per_anime = ratings.groupby('anime_id')['rating'].count()
statistics.mean(ratings_per_anime.tolist())
=> 638.3843054296364# distribution of ratings per anime
import matplotlib.pyplot as plt
%matplotlib inline
ratings_per_anime.hist(bins=20, range=(0,2500))

most anime received less than 500 ratings

回到推荐者

在基于用户的协同过滤中,代表用户的向量实际上是他们给出的评级列表。所以我们宇宙中的动漫越多,每个用户的维度就越多。

让我们通过删除没有被很多用户评价的动画来减少数据量。根据 id 做一个动漫保留清单。

# counts of ratings per anime as a df
ratings_per_anime_df = pd.DataFrame(ratings_per_anime)# remove if < 1000 ratings
filtered_ratings_per_anime_df = ratings_per_anime_df[ratings_per_anime_df.rating >= 1000]# build a list of anime_ids to keep
popular_anime = filtered_ratings_per_anime_df.index.tolist()

以及没怎么评价过动漫的用户。

# counts ratings per user as a df
ratings_per_user_df = pd.DataFrame(ratings_per_user)# remove if < 500
filtered_ratings_per_user_df = ratings_per_user_df[ratings_per_user_df.rating >= 500]# build a list of user_ids to keep
prolific_users = filtered_ratings_per_user_df.index.tolist()

现在过滤掉那些列表之外的动漫和用户。

filtered_ratings = ratings[ratings.anime_id.isin(popular_anime)]
filtered_ratings = ratings[ratings.user_id.isin(prolific_users)]
len(filtered_ratings)
=> 1005314

我们的评级数据点从 600 万降至 100 万。很好。

我们来建立一个用户和动漫之间的评分矩阵。

rating_matrix = filtered_ratings.pivot_table(index='user_id', columns='anime_id', values='rating')# replace NaN values with 0
rating_matrix = rating_matrix.fillna(0)# display the top few rows
rating_matrix.head()

写一个函数,用余弦相似度找出与 current_user 最相似的用户。我们随意决定找出 3 个最相似的用户。

并选择“226”作为我们的当前用户,但我们可以选择任何人。

from sklearn.metrics.pairwise import cosine_similarity
import operatordef similar_users(user_id, matrix, k=3):
    # create a df of just the current user
    user = matrix[matrix.index == user_id]

    # and a df of all other users
    other_users = matrix[matrix.index != user_id]

    # calc cosine similarity between user and each other user
    similarities = cosine_similarity(user,other_users)[0].tolist()

    # create list of indices of these users
    indices = other_users.index.tolist()

    # create key/values pairs of user index and their similarity
    index_similarity = dict(zip(indices, similarities))

    # sort by similarity
    index_similarity_sorted = sorted(index_similarity.items(), key=operator.itemgetter(1))
    index_similarity_sorted.reverse()

    # grab k users off the top
    top_users_similarities = index_similarity_sorted[:k]
    users = [u[0] for u in top_users_similarities]

    return users current_user = 226# try it out
similar_user_indices = similar_users(current_user, rating_matrix)print(similar_user_indices)
#=> [30773, 39021, 45603]

现在写一个函数来做推荐。我们已经设置了返回 5 部热门推荐动漫的功能。

def recommend_item(user_index, similar_user_indices, matrix, items=5):

    # load vectors for similar users
    similar_users = matrix[matrix.index.isin(similar_user_indices)] # calc avg ratings across the 3 similar users
    similar_users = similar_users.mean(axis=0) # convert to dataframe so its easy to sort and filter
    similar_users_df = pd.DataFrame(similar_users, columns=['mean'])

    # load vector for the current user
    user_df = matrix[matrix.index == user_index] # transpose it so its easier to filter
    user_df_transposed = user_df.transpose() # rename the column as 'rating'
    user_df_transposed.columns = ['rating'] # remove any rows without a 0 value. Anime not watched yet
    user_df_transposed = user_df_transposed[user_df_transposed['rating']==0] # generate a list of animes the user has not seen
    animes_unseen = user_df_transposed.index.tolist()

    # filter avg ratings of similar users for only anime the current user has not seen
    similar_users_df_filtered = similar_users_df[similar_users_df.index.isin(animes_unseen)] # order the dataframe
    similar_users_df_ordered = similar_users_df.sort_values(by=['mean'], ascending=False) # grab the top n anime   
    top_n_anime = similar_users_df_ordered.head(items)
    top_n_anime_indices = top_n_anime.index.tolist() # lookup these anime in the other dataframe to find names
    anime_information = animes[animes['anime_id'].isin(top_n_anime_indices)]

    return anime_information #items # try it out
recommend_item(226, similar_user_indices, rating_matrix)

我们做到了!我们的当前用户尚未观看的来自最相似用户的最高评级的 5 部动画。

实际上,我们想要试验不同的相似性算法和不同数量的相似用户。但是我希望你把它当作一个基于用户的协作过滤的粗略框架。

下次见。

在 pySpark 中用 MLlib 搭建一个端到端的机器学习模型。

原文:https://towardsdatascience.com/build-an-end-to-end-machine-learning-model-with-mllib-in-pyspark-4917bdf289c5?source=collection_archive---------3-----------------------

对于具有不平衡类别的二元分类问题

photo credit: pexels

介绍

内存计算和并行处理是 Apache Spark 在大数据行业非常受欢迎的一些主要原因,以处理大规模的数据产品并执行更快的分析。 MLlib 建立在 Spark 之上,是一个可扩展的机器学习库,提供高质量的算法和超快的速度。它拥有优秀的 Java、 Python 和 Scala API,是数据分析师、数据工程师和数据科学家的首选。MLlib 由常见的学习算法和实用程序组成,包括分类、回归、聚类、协同过滤(矩阵分解)、降维等。

履行

在本文中,我们将在 pySpark 中使用 MLlib 构建一个端到端的机器学习模型。我们将使用 kaggle 上的家庭信用违约风险竞赛的真实数据集。这项竞赛的目的是根据从每个申请人那里收集的数据,确定贷款申请人是否有能力偿还贷款。目标变量为 0(有能力偿还贷款的申请人)或 1(没有能力偿还贷款的申请人)。这是一个目标标签高度不平衡的二元分类问题。分配比率接近 0.91 比 0.09,0.91 是能够偿还贷款的申请人的比率,0.09 是不能偿还贷款的申请人的比率。

让我们先来看看数据集的结构:

#we use the findspark library to locate spark on our local machineimport findspark
findspark.init('home Diredtory of Spark')from pyspark.sql import SparkSession# initiate our session and read the main CSV file, then we print the #dataframe schemaspark = SparkSession.builder.appName('imbalanced_binary_classification').getOrCreate()
new_df = spark.read.csv('application_train.csv', header=True, inferSchema=True)
new_df.printSchema()root
 |-- SK_ID_CURR: integer (nullable = true)
 |-- TARGET: integer (nullable = true)
 |-- NAME_CONTRACT_TYPE: string (nullable = true)
 |-- CODE_GENDER: string (nullable = true)
 |-- FLAG_OWN_CAR: string (nullable = true)
 |-- FLAG_OWN_REALTY: string (nullable = true)
 |-- CNT_CHILDREN: integer (nullable = true)
 |-- AMT_INCOME_TOTAL: double (nullable = true)
 |-- AMT_CREDIT: double (nullable = true)
 |-- AMT_ANNUITY: double (nullable = true)
 |-- AMT_GOODS_PRICE: double (nullable = true)
 |-- NAME_TYPE_SUITE: string (nullable = true)
 |-- NAME_INCOME_TYPE: string (nullable = true)
 |-- NAME_EDUCATION_TYPE: string (nullable = true)
 |-- NAME_FAMILY_STATUS: string (nullable = true)
 |-- NAME_HOUSING_TYPE: string (nullable = true)
 |-- REGION_POPULATION_RELATIVE: double (nullable = true)
...

printSchema()只显示了列名及其数据类型。我们将删除 SK_ID_CURR 列,将“TARGET”列重命名为“label ”,并查看我们的目标变量的分布:

# Sk_ID_Curr is the id column which we dont need it in the process #so we get rid of it. and we rename the name of our 
# target variable to "label"
drop_col = ['SK_ID_CURR']
new_df = new_df.select([column for column in new_df.columns if column not in drop_col])
new_df = new_df.withColumnRenamed('TARGET', 'label')
new_df.groupby('label').count().toPandas()

我们可以用 matplotlib 可视化标签的分布:

# let's have a look at the distribution of our target variable:
# to make it look better, we first convert our spark df to a Pandasimport matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
df_pd = new_df.toPandas()
print(len(df_pd))
plt.figure(figsize=(12,10))
sns.countplot(x='label', data=df_pd, order=df_pd['label'].value_counts().index)

以下是熊猫数据帧格式的数据:

# let's see how everything look in Pandasimport pandas as pd
pd.DataFrame(new_df.take(10), columns= new_df.columns)

数据争论

现在我们对数据集的一般结构有了一些想法,让我们继续一些数据争论。首先我们检查我们有多少分类数字特征。接下来,我们构建一个函数,输出数据集中缺失值的基本信息:

# now let's see how many categorical and numerical features we have:cat_cols = [item[0] for item in new_df.dtypes if item[1].startswith('string')] 
print(str(len(cat_cols)) + '  categorical features')num_cols = [item[0] for item in new_df.dtypes if item[1].startswith('int') | item[1].startswith('double')][1:]print(str(len(num_cols)) + '  numerical features')**16  categorical features
104  numerical features**

下面是我们如何得到缺失信息的表格:

# we use the below function to find more information about the #missing valuesdef info_missing_table(df_pd):
    """Input pandas dataframe and Return columns with missing value and percentage"""
    mis_val = df_pd.isnull().sum() #count total of null in each columns in dataframe#count percentage of null in each columns
    mis_val_percent = 100 * df_pd.isnull().sum() / len(df_pd) 
    mis_val_table = pd.concat([mis_val, mis_val_percent], axis=1) #join to left (as column) between mis_val and mis_val_percent
    mis_val_table_ren_columns = mis_val_table.rename(
    columns = {0 : 'Missing Values', 1 : '% of Total Values'}) #rename columns in table
    mis_val_table_ren_columns = mis_val_table_ren_columns[
    mis_val_table_ren_columns.iloc[:,1] != 0].sort_values('% of Total Values', ascending=False).round(1) 

    print ("Your selected dataframe has " + str(df_pd.shape[1]) + " columns.\n"    #.shape[1] : just view total columns in dataframe  
    "There are " + str(mis_val_table_ren_columns.shape[0]) +              
    " columns that have missing values.") #.shape[0] : just view total rows in dataframe return mis_val_table_ren_columns missings = info_missing_table(df_pd)
missings

121 列中有 67 列缺少值。它没有在图像中显示所有这些列,但总体而言,这 67 列中的大多数都有超过 50%的缺失值。所以我们正在处理大量缺失的值。我们将用每列的平均值填充数值缺失值,用每列最常见的类别填充分类缺失值。但首先,让我们统计每一列中缺失的值:

miss_counts = count_missings(new_df)
miss_counts[('AMT_ANNUITY', 12),
 ('AMT_GOODS_PRICE', 278),
 ('NAME_TYPE_SUITE', 1292),
 ('OWN_CAR_AGE', 202929),
 ('OCCUPATION_TYPE', 96391),
 ('CNT_FAM_MEMBERS', 2),
 ('EXT_SOURCE_1', 173378),
 ('EXT_SOURCE_2', 660),
 ('EXT_SOURCE_3', 60965),
 ('APARTMENTS_AVG', 156061),
 ('BASEMENTAREA_AVG', 179943),
 ('YEARS_BEGINEXPLUATATION_AVG', 150007),
 ('YEARS_BUILD_AVG', 204488),
...

我们将缺失值的分类列和数字列分开:

# here we seperate missing columns in our new_df based on #categorical and numerical typeslist_cols_miss=[x[0] for x in miss_counts]
df_miss= new_df.select(*list_cols_miss)
#categorical columns
catcolums_miss=[item[0] for item in df_miss.dtypes if item[1].startswith('string')]  #will select name of column with string data type
print("cateogrical columns_miss:", catcolums_miss)### numerical columns
numcolumns_miss = [item[0] for item in df_miss.dtypes if item[1].startswith('int') | item[1].startswith('double')] #will select name of column with integer or double data type
print("numerical columns_miss:", numcolumns_miss)

接下来,我们填充缺失的值:

# now that we have seperated the columns based on categorical and #numerical types, we will fill the missing categiracl 
# values with the most frequent categoryfrom pyspark.sql.functions import rank,sum,col
df_Nomiss=new_df.na.drop()
for x in catcolums_miss:                  mode=df_Nomiss.groupBy(x).count().sort(col("count").desc()).collect()[0][0] 
    print(x, mode) #print name of columns and it's most categories 
    new_df = new_df.na.fill({x:mode})# and we fill the missing numerical values with the average of each #columnfrom pyspark.sql.functions import mean, roundfor i in numcolumns_miss:
    meanvalue = new_df.select(round(mean(i))).collect()[0][0] 
    print(i, meanvalue) 
    new_df=new_df.na.fill({i:meanvalue})

既然我们的数据集中不再有缺失值,让我们来研究如何处理不平衡类。有不同的方法来缓解这个问题。一种方法是下采样多数阶级或者上采样少数阶级以取得更平衡的结果。另一种方法是为每个类分配权重,通过分配较小的权重来惩罚多数类,通过分配较大的权重来促进少数类。我们将在数据集中创建一个名为“weights”的新列,并将每个类的倒数 比率指定为权重。这是如何做到的:

# adding the new column weights and fill it with ratiosfrom pyspark.sql.functions import whenratio = 0.91
def weight_balance(labels):
    return when(labels == 1, ratio).otherwise(1*(1-ratio))new_df = new_df.withColumn('weights', weight_balance(col('label')))

下面是添加重量列后的样子:

特征工程

下一步是特征工程。pySpark 使得提取特征变得如此简单,我们不需要做太多工作。以下是步骤:

  1. 我们应用 StringIndexer()为分类列中的每个类别分配索引。
  2. 我们应用 OneHotEncoderEstimator()将分类列转换为 onehot 编码的向量。
  3. 我们应用 VectorAssembler()从所有分类和数字特征中创建一个特征向量,我们将最终向量称为“特征”。
# we use the OneHotEncoderEstimator from MLlib in spark to convert #aech v=categorical feature into one-hot vectors
# next, we use VectorAssembler to combine the resulted one-hot ector #and the rest of numerical features into a 
# single vector column. we append every step of the process in a #stages arrayfrom pyspark.ml.feature import OneHotEncoderEstimator, StringIndexer, VectorAssemblerstages = []
for categoricalCol in cat_cols:
    stringIndexer = StringIndexer(inputCol = categoricalCol, outputCol = categoricalCol + 'Index')
    encoder = OneHotEncoderEstimator(inputCols=[stringIndexer.getOutputCol()], outputCols=[categoricalCol + "classVec"])stages += [stringIndexer, encoder]assemblerInputs = [c + "classVec" for c in cat_cols] + num_cols
assembler = VectorAssembler(inputCols=assemblerInputs, outputCol="features")
stages += [assembler]

现在让我们把所有东西都放到一个管道里。这里我们执行一系列转换,因此我们使用管道一次完成所有转换:

# we use a pipeline to apply all the stages of transformationfrom pyspark.ml import Pipelinecols = new_df.columns
pipeline = Pipeline(stages = stages)
pipelineModel = pipeline.fit(new_df)
new_df = pipelineModel.transform(new_df)selectedCols = ['features']+cols
new_df = new_df.select(selectedCols)
pd.DataFrame(new_df.take(5), columns=new_df.columns)

以下是我们的新数据集在特征工程后的样子:

训练和超参数调整

对于训练,我们首先将数据集分成训练集和测试集。然后,我们开始使用逻辑回归进行训练,因为它对二元分类问题表现良好。

# split the data into trainign and testin setstrain, test = new_df.randomSplit([0.80, 0.20], seed = 42)
print(train.count())
print(test.count())# first we check how LogisticRegression perform 
from pyspark.ml.classification import LogisticRegressionLR = LogisticRegression(featuresCol = 'features', labelCol = 'label', maxIter=15)
LR_model = LR.fit(train)

我们将为训练数据绘制 ROC 曲线,以了解如何执行逻辑回归,然后我们将使用 ROC 曲线下面积(一种用于评估二元分类的标准度量)作为评估模型的度量:

#plotting the ROC CurvetrainingSummary = LR_model.summaryroc = trainingSummary.roc.toPandas()
plt.plot(roc['FPR'],roc['TPR'])
plt.ylabel('False Positive Rate')
plt.xlabel('True Positive Rate')
plt.title('ROC Curve')
plt.show()print('Training set ROC: ' + str(trainingSummary.areaUnderROC))

在测试集上检查模型的性能:

from pyspark.ml.evaluation import BinaryClassificationEvaluatorpredictions_LR = LR_model.transform(test)
evaluator = BinaryClassificationEvaluator()
print("Test_SET (Area Under ROC): " + str(evaluator.evaluate(predictions_LR, {evaluator.metricName: "areaUnderROC"})))**Test_SET (Area Under ROC): 0.7111434396856681**

0.711 对于逻辑回归来说并不是一个很差的结果。接下来,我们尝试另一个模型,梯度推进树(GBT)。这是一种非常流行的分类和回归方法,使用决策树的集合。

# next we checkout gradient boosting treesfrom pyspark.ml.classification import GBTClassifiergbt = GBTClassifier(maxIter=15)
GBT_Model = gbt.fit(train)
gbt_predictions = GBT_Model.transform(test)evaluator = BinaryClassificationEvaluator()
print("Test_SET (Area Under ROC): " + str(evaluator.evaluate(gbt_predictions, {evaluator.metricName: "areaUnderROC"})))**Test_SET (Area Under ROC): 0.7322019340889893**

我们使用 GBT 获得了更好的结果,0.732。作为这里的最后一个策略,我们将使用网格搜索实现超参数调整,然后我们运行交叉验证来更好地提高 GBT 的性能。

from pyspark.ml.tuning import ParamGridBuilder, CrossValidatorparamGrid = (ParamGridBuilder()
             .addGrid(gbt.maxDepth, [2, 4, 6])
             .addGrid(gbt.maxBins, [20, 30])
             .addGrid(gbt.maxIter, [10, 15])
             .build())cv = CrossValidator(estimator=gbt, estimatorParamMaps=paramGrid, evaluator=evaluator, numFolds=5)# Run cross validations.cvModel = cv.fit(train)
gbt_cv_predictions = cvModel.transform(test)
evaluator.evaluate(gbt_cv_predictions)**CV_GBT (Area Under ROC) = 0.7368288195372332**

结果有了一点改善,这意味着我们仍然可以通过超参数调整来看看我们是否可以进一步改善结果。

在这个项目中,我们建立了一个端到端的机器学习模型(具有不平衡类的二元分类)。我们展示了 Apache Spark 的 MLlib 的强大功能,以及它如何应用于端到端 ML 项目。

像往常一样,代码和 jupyter 笔记本在我的 Github 上可用。

非常感谢您的提问和评论。

参考资料:

  1. https://github.com/elsyifa/Classification-Pyspark
  2. https://spark . Apache . org/docs/2 . 3 . 0/ml-classification-regression . html

用 Plotly 和 Dash 创建交互式 Choropleth 地图

原文:https://towardsdatascience.com/build-an-interactive-choropleth-map-with-plotly-and-dash-1de0de00dce0?source=collection_archive---------8-----------------------

悉尼郊区房价中位数的可视化

L 上周,我完成了 IBM 数据科学课程的期末作业,就是根据位置数据,寻找一个理想的郊区,开一家意大利餐厅。在这个过程中,我在网上收集了悉尼每个郊区的房产中值价格(即房屋购买/租赁和单位购买/租赁),并分别绘制在 Choropleth 地图上。

Choropleth maps for different parameters

然而,我想知道是否有可能将所有这些地图组合在一起,并通过点击下拉菜单中的名称来选择其中之一。此外,我想在地图旁边再添加一个地块,以相应地显示中位数价格最高的前 10 个郊区。这些附件将使地图更加丰富和用户友好。在这篇文章中,我将分享我关于如何使用 Plotly 和 Dash 创建一个带有 choropleth 地图和条形图的交互式仪表板的笔记。此外,我假设你以前有与 Plotly 的经验。

先决条件

在系统上安装plotly, dashpandas。我使用conda创建了一个虚拟环境,以保持系统有序,避免与系统中的其他包混淆。如果你想了解更多关于conda env的信息,我已经在上一篇文章中介绍了康达虚拟环境。以下代码将创建一个虚拟环境,其中包含plotlydash所需的所有包。

conda create -n <whatever name you want for this env> -c plotly plotly=4.4.1 -c conda-forge dash pandas

为了能够在plotly中绘制地图,我们还需要一个来自 Mapbox 的令牌,它提供了各种漂亮的地图样式,最重要的是免费的。此外,在这个仪表板中使用了两个数据集,它们可以从我的 github 下载(你也可以在这里找到 dash 应用程序代码)。

子弹头列车路线

如果您现在想探索 dash 应用程序,在完成上述步骤后,您需要在这个脚本中将您的 Mapbox 令牌分配给mapbox_accesstoken,并在两个数据集所在的同一目录下运行它。一旦弹出以下消息,只需在您首选的浏览器中打开这个地址[http://127.0.0.1:8050/](http://127.0.0.1:8050/),dash 就会加载到那里。

$ python dash_project_medium.py
  Running on [http://127.0.0.1:8050/](http://127.0.0.1:8050/)
  Debugger PIN: 880-084-162
  * Serving Flask app "dash_project" (lazy loading)
  * Environment: production
    WARNING: This is a development server. Do not use it in a    production deployment.
  Use a production WSGI server instead.
  * Debug mode: on

蒸汽火车道

如下图所示,我已经标记了脚本中用于在仪表板中创建相应元素的关键函数。

Dissecting the dashboard

通过plotlydash构建这个仪表板的一般原则是 1)在一个定义好的画布上安排和组合不同的元素;2)将所有元素编译到一个容器中:fig=go.Figure(data=trace2 + trace1, layout=layout);3)将这个容器传递给dcc.Graph,其中dcc是 dash 核心组件,dash将为仪表盘创建一个基于 html 的 web 应用。

该仪表板的一个关键功能是通过下拉菜单在两个轨迹(choropleth 图和条形图)中显示特定参数(即House_buyHouse_rentUnit_buyUnit_rent)。我的方法是创建四个层,每个层有一个参数visible=False。然后使用updatemenusbuttons功能转动visible=True至给定参数。

Bar plot code

因此,每个轨迹中有四个图形层,只有一个是可见的,这取决于在给定的时间点点击了哪个按钮。

Drop down menu bar code

由于地图不是在笛卡尔坐标系(x/y)上绘制的,而笛卡尔坐标系是条形图中使用的坐标系,因此我在仪表板中为地图和条形图分别设置了两个坐标系。对于条形图(trace2),其坐标轴被分配到**xaxis='x2'****yaxis='y2'**。相反,地图(trace1)在Layout中有自己的特征,它被赋给变量mapbox1mapboxx/y后面的数字是任意的。话虽如此,你可以指定任意多的坐标系,只要确保你在Layout中将正确的系统锚定到它的轨迹上。

Choropleth map code

然后在Layout设置中,我们分别对这两个坐标系进行调整。如通过domain显示轨迹在仪表盘中的位置,通过showticklabels显示每个轴上的刻度,通过autorange显示条的升序。

Layout code

在连接了fig=go.Figure中的所有元素之后,我将fig分配给dcc.Graph,将所有代码打包成一个py文件,并运行它。嘣,我的第一个交互式仪表盘来了。

Dash app implementation

我应该指出,我在这里只使用了非常基本的破折号结构,大部分代码仍然是用 Plotly 编写的。我的方法的一个缺点是,将所有四层堆叠到同一轨迹上可能会降低应用程序的速度,这是因为所有数据都需要在 dash 应用程序初始化时加载。当点击下拉菜单事件发生时,进行实时更新会更有效。希望通过进一步学习高级 Dash 代码,我能找到一个解决方案。

以下是一些学习 Dash 的资源:

  • Dash 用户指南
  • 来自数据营的初学者 Dash
  • Dash 社区论坛
  • Github 上的牛逼 Dash 资源指南
  • 一张详细的仪表盘构建贴

和往常一样,我欢迎反馈、建设性的批评和倾听您的数据科学项目。你可以在 Linkedin 上找到我。

构建并比较 3 个模型— NLP 预测

原文:https://towardsdatascience.com/build-and-compare-3-models-nlp-sentiment-prediction-67320979de61?source=collection_archive---------8-----------------------

从零开始用 3 种算法预测情绪-初学者友好。

Python 上的自然语言处理(Jupyter)!

创建这个项目是为了学习和理解各种分类算法在自然语言处理模型中是如何工作的。自然语言处理,我现在称之为 NLP,是机器学习的一个分支,专注于使计算机能够解释和处理语音和文本形式的人类语言。

Photo by Patrick Tomasso on Unsplash

在这条流水线上,我经历了以下几个步骤:

  1. 导入所需的包和库
  2. 导入数据集
  3. 在数据集中的文本可以被计算机分析之前对其进行处理
  4. 创建一个单词袋模型
  5. 将数据集分成训练集和测试集
  6. 朴素贝叶斯算法
  7. 决策树算法
  8. 随机森林算法
  9. 比较准确度、精确度、召回率和 F1 分数

||二||问题

Photo by Zulmaury Saavedra on Unsplash

对于这个项目,我将使用来自 Kaggle 的数据集,其中包含不同用户对一家披萨店的 1000 条评论。|链接到数据集|

人类可以阅读评论,并判断它是积极的还是消极的。如果我们可以创建一个模型来将他们分为积极的或消极的呢?做这件事的最好方法是什么?

首先说一下流程。我们首先对数据进行预处理,删除对我们的预测没有帮助的不必要的词。然后,我们采用词干形式的重要单词(例如 lov 是 loved、loving 或 lovely 的词干)。然后,我们训练机器根据词干来学习哪些评论是正面的。之后,我们使用类似的信息测试数据,看看我们的机器能够多准确地预测评论是正面还是负面(1 或 0)。

|| III || 导入基本库

在这里,我们导入这个模型工作所需的所有库。在开始之前,请确保安装了所有的依赖项。我们将主要与pandas, numpy, re, nltk, matplotlib, and sci-kit learn.合作

确保在命令行上运行上面提到的所有库。

import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import re
import nltk
from nltk.corpus import stopwords
from nltk.stem.porter import PorterStemmer
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.model_selection import train_test_split
from sklearn.naive_bayes import GaussianNB
from sklearn.metrics import confusion_matrix
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier

|| IV ||导入数据集

dataset = pd.read_csv(‘Restaurant_Reviews.tsv’, delimiter = ‘\t’, quoting = 3)

在上面的代码中,我使用的是. tsv 文件,而不是. csv 文件。当我们分解缩略词时,区别就更容易分辨了。. tsv(制表符分隔的值)在文本文件中由空格分隔,而. csv(逗号分隔的值)使用逗号分隔不同的值。

pd.read_csv可用于两者,但为了指定 tsv 文件,我在分隔符中添加了' \t ',以告诉机器值由制表符而不是逗号分隔。引用被设置为 3,因为我们的评论包含一些双引号,如果我们不添加这一行,机器将无法将它们解释为常规文本。
下面我们可以看到我们数据集的前 10 个评论和结果:正面(1)或负面(0)

dataset.head(10)

|| V || 文本预处理

nltk.download(‘stopwords’)
corpus = []for i in range(0, 1000):
review = re.sub(‘[^a-zA-Z]’, ‘ ‘, dataset[‘Review’][i])
review = review.lower()
review = review.split()
ps = PorterStemmer()
review = [ps.stem(word) for word in review if not word in set(stopwords.words(‘english’))]
review = ‘ ‘.join(review)
corpus.append(review)

上面的代码显示了这个模型中最重要的一步。我从导入 Regex 作为re开始。这个类允许我们匹配和搜索字符串对象。我使用 re 的子功能来允许机器包含我们需要的数据元素,即大写和小写的字母 A-Z。

我还导入了代表自然语言工具包的nltk。从nltk中,我导入了两个类:stopwords类和PorterStemmer类。

stopwords允许我们删除对我们的模型没有帮助的单词(如“the”、“an”、“this”等)。stopwords 类已经包含了这些单词,所以我不必手动输入它们。我使用了一个 for 循环来告诉机器,如果这个单词不在 stopwords 类中,我们需要把它取出来。

PorterStemmer允许我们提取一个单词的词干,并将其归类为相似单词的常见预测值。例如,在第一个评论中,“Lov”是单词“loving”或“loved”的词干,这两个单词本质上都转化为积极的评论,因此允许我们的模型有更少的单词。

最后,为了将预处理步骤应用于我们数据集中的所有 1000 条评论,我在文本处理步骤之前添加了另一个 for 循环。

|| VI || 创建单词袋模型

cv = CountVectorizer(max_features = 1500)X = cv.fit_transform(corpus).toarray()
y = dataset.iloc[:, 1].values

单词袋模型允许我们从文本数据中提取特征,在本例中,我们从每个观察结果中提取所有单词,并将它们集中在一个“袋”中,通过不计算重复项来减少冗余。我是通过从sklearn导入CountVectorizer类来做到这一点的。

每个单词以某种方式形成自己的列,因为有这么多单词,我们可以有大量的列。然而,我使用CountVectorizer类的max_features参数指定了最大列数。

然后,我所要做的就是将单词列与 X(输入)变量相匹配,并将 y(输出)变量指定为数据集中的第二列,这将根据评论是正面还是负面分别给出 1 或 0。

|| VII || 分成训练集和测试集

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2, random_state = 0)

然后,我必须将数据集分为训练集和测试集,并使用 0.2 的测试大小,这样我们就有 800 个值来训练数据集,200 个值来测试数据集。

下面的代码向我们展示了训练和测试集的样子

X_train[0, 0:10] *#First 10 rows of the first column of X_train.***Output**: array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0], dtype=int64)X_test[0, 0:10] *#First 10 rows of the first column of X_test.***Output**: array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0], dtype=int64)y_train[:10] *#First 10 values of the first column of y_train.***Output**: array([1, 1, 1, 0, 1, 0, 1, 0, 0, 0])y_test[:10] *#First 10 values of the first column of y_test.***Output**: array([0, 0, 0, 0, 0, 0, 1, 0, 0, 1])

现在我已经完成了所有的预处理步骤,我将开始对 model 应用一些分类算法,以帮助它预测评论。

|| VIII || 朴素贝叶斯模型

我使用的第一个模型是朴素贝叶斯模型。在机器学习中,朴素贝叶斯分类器是一系列简单的“概率分类器”,基于应用贝叶斯定理,在特征之间具有强(朴素)独立性假设。

给定类变量,所有朴素贝叶斯分类器都假设特定特征的值独立于任何其他特征的值。在我们的模型中,朴素贝叶斯算法根据输出集,查看评论的特定关键字来描述它是正面还是负面的。

在下面的代码中,我导入了 GaussianNB 类,它假设我们的数据是正态分布的(具有高斯钟形曲线)。

VIII~i ||将朴素贝叶斯拟合到训练集

classifier = GaussianNB()
classifier.fit(X_train, y_train)**Output**: GaussianNB(priors=None, var_smoothing=1e-09)

VIII~ii ||预测测试集结果

我创建了y_pred_NB,这是我们的模型的预测将被存储的地方。下面的输出向我们展示了预测矩阵的样子。它是一堆 1 和 0,就像我们的y_train数据集。这些是我们的模型做出的预测。y_pred_NB可以与y_test进行比较,并确定我们的模型有多精确。

y_pred_NB = classifier.predict(X_test)
y_pred_NB**Output**: array([1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1])

VIII~iii ||制作混淆矩阵

朴素贝叶斯的混淆矩阵在左上角显示我们的真阴性,在右上角显示假阴性,在右下角显示真阳性,在左下角显示假阳性。

下面,我用缩写对每个数字进行了编码,T 代表真,F 代表假,N 代表负,P 代表正。

cm_NB = confusion_matrix(y_test, y_pred_NB) 
cm_NB**Output**: array([[55, 42],[12, 91]])TP_NB = 91 *#True Positives (Naive Bayes)*TN_NB = 55 *#True Negatives (Naive Bayes)*FP_NB = 12 *#False Positives (Naive Bayes)*FN_NB = 42 *#False Negatives (Naive Bayes)*

下面,我将使用真/假阳性和阴性来计算准确度、精确度、召回率和 F1 分数。

VIII~iv ||朴素贝叶斯算法的精度

准确性顾名思义。它通过将真实预测相加并除以预测总数来衡量准确性。

Accuracy_NB = (TP_NB + TN_NB) / (TP_NB + TN_NB + FP_NB + FN_NB) Accuracy_NB **Output**: 0.73

VIII~v || 朴素贝叶斯算法的精度

精度是指两个或多个测量值之间的接近程度。它是通过将真阳性除以总阳性来计算的

Precision_NB = TP_NB / (TP_NB + FP_NB)Precision_NB**Output**: 0.883495145631068

VIII~vi ||回忆朴素贝叶斯算法

回忆是正确预测的正面观察与实际类中所有观察的比率。将真阳性除以真阳性和假阴性之和。

Recall_NB = TP_NB / (TP_NB + FN_NB)Recall_NB**Output**: 0.6842105263157895

VIII~vii ||朴素贝叶斯算法的 F1 得分

F1 分数是精确度和召回率的加权平均值。如果我们需要在精确度和召回率之间寻求平衡,并且存在不均匀的类别分布,F1 分数可能是一个更好的衡量标准。它的计算方法是将精度和召回率相乘,将结果除以精度和召回率之和,然后将最终结果乘以 2。

F1_Score_NB = 2 * Precision_NB * Recall_NB / (Precision_NB + Recall_NB) F1_Score_NB**Output**: 0.7711864406779663

|| IX || 决策树

我使用的下一个算法是决策树。决策树允许您开发分类系统,该系统基于一组决策规则对未来的观察结果进行预测或分类。

IX~i || 将决策树分类拟合到训练集

classifier = DecisionTreeClassifier(criterion = ‘entropy’, random_state = 0)classifier.fit(X_train, y_train)**Output:** DecisionTreeClassifier(class_weight=None, criterion=’entropy’,max_depth=None,max_features=None,max_leaf_nodes=None,min_impurity_decrease=0.0,min_impurity_split=None,min_samples_leaf=1,min_samples_split=2,min_weight_fraction_leaf=0.0, presort=False, random_state=0,splitter=’best’)

IX~ii ||预测测试集结果

In [59]: y_pred_DT = classifier.predict(X_test)y_pred_DT**Output**: array([0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0,1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0,0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1,0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1,0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1,0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1,0, 0])

IX~iii ||制作混淆矩阵

cm_DT = confusion_matrix(y_test, y_pred_DT) cm_DT**Output**: array([[74, 23],[35, 68]])TP_DT = 68 *#True Positives (Decision Tree)*TN_DT = 74 *#True Negatives (Decision Tree)*FP_DT = 35 *#False Positives (Decision Tree)*FN_DT = 23 *#False Negatives (Decision Tree)*

IX~iv ||决策树算法的准确性

Accuracy_DT = (TP_DT + TN_DT) / (TP_DT + TN_DT + FP_DT + FN_DT)Accuracy_DT**Output**: 0.71

IX~v ||决策树算法的精度

Precision_DT = TP_DT / (TP_DT + FP_DT)Precision_DT**Output**: 0.6601941747572816

IX~vi ||召回决策树算法

Recall_DT = TP_DT / (TP_DT + FN_DT)Recall_DT**Output**: 0.7472527472527473

IX~vii ||决策树算法的 F1 得分

F1_Score_DT = 2 * Precision_DT * Recall_DT / (Precision_DT + Recall_DT)F1_Score_DT**Output**: 0.7010309278350515

|| X ||随机森林

最后,我使用了随机森林算法,这只是一些决策树的组合。在我的例子中,我选择使用 300 棵树,但是我可以根据我想要的模型精度来改变这个数字。

X~I | |将随机森林分类拟合到训练集

classifier = RandomForestClassifier(n_estimators = 300, criterion = ‘entropy’, random_state = 0)classifier.fit(X_train, y_train)**Output**: RandomForestClassifier(bootstrap=True, class_weight=None, criterion=’entropy’,max_depth=None,max_features=’auto’,max_leaf_nodes=None,min_impurity_decrease=0.0,min_impurity_split=None,min_samples_leaf=1, min_samples_split=2,min_weight_fraction_leaf=0.0, n_estimators=300, n_jobs=None,oob_score=False, random_state=0, verbose=0, warm_start=False)

X~ii ||预测测试集结果

y_pred_RF = classifier.predict(X_test)y_pred_RF**Output**: array([0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0])

X~iii ||制作混淆矩阵

cm_RF = confusion_matrix(y_test, y_pred_RF) cm_RF**Output**: array([[87, 10],[47, 56]])*# Calculating True/False Positives/Negatives* TP_RF = 56 *#True Positives (Random Forest)* TN_RF = 87 *#True Negatives (Random Forest)* FP_RF = 47 *#False Positives (Random Forest)* FN_RF = 10 *#False Negatives (Random Forest)*

X~iv ||随机森林的精度

Accuracy_RF = (TP_RF + TN_RF) / (TP_RF + TN_RF + FP_RF + FN_RF)Accuracy_RF**Output**: 0.715

x~v | |随机森林的精度

Precision_RF = TP_RF / (TP_RF + FP_RF)Precision_RF**Output**: 0.5436893203883495

X~vi ||随机森林的回忆

Recall_RF = TP_RF / (TP_RF + FN_RF)Recall_RF**Output**: 0.8484848484848485

X~vii ||随机森林 F1 得分

F1_Score_RF = 2 * Precision_RF * Recall_RF / (Precision_RF + Recall_RF)F1_Score_RF**Output**: 0.6627218934911243

XI ||对比模型

在最后一部分,我将比较我使用的每种算法的准确度、精确度、召回率和 F1 值。我将把它们绘制在条形图上,以图形方式展示不同型号之间的比较。

Xi~I | |比较模型精度

Accuracy = [Accuracy_RF, Accuracy_DT, Accuracy_NB]
Methods = [‘Random_Forest’, ‘Decision_Trees’, ‘Naive_Bayes’]
Accuracy_pos = np.arange(len(Methods))plt.bar(Accuracy_pos, Accuracy)
plt.xticks(Accuracy_pos, Methods)
plt.title(‘comparing the accuracy of each model’)plt.show()

正如我们在上面的柱状图中看到的,朴素贝叶斯在所有算法中具有最高的准确性,有 73%的正确预测。决策树和随机森林算法也很接近,准确率分别为 71%和 71.5%。

XI~ii ||比较模型精度

Precision = [Precision_RF, Precision_DT, Precision_NB]
Precision_pos = np.arange(len(Methods))plt.bar(Precision_pos, Precision)
plt.xticks(Precision_pos, Methods)
plt.title(‘comparing the precision of each model’)plt.show()

朴素贝叶斯是最精确的模型,精度为 88.35%,而决策树的精度为 66%。随机森林的准确率最低,约为 54.4%。

XI~iii ||对比车型召回

Recall = [Recall_RF, Recall_DT, Recall_NB]
Recall_pos = np.arange(len(Methods))plt.bar(Recall_pos, Recall)
plt.xticks(Recall_pos, Methods)
plt.title(‘comparing the recall of each model’)plt.show()

随机森林的召回率最高,约为 84.8%,而决策树的召回率为 74.7%。朴素贝叶斯的召回率最低,为 68.4%

XI~iv ||对比 F1 车型分数

F1_Score = [F1_Score_RF, F1_Score_DT, F1_Score_NB]F1_Score_pos = np.arange(len(Methods))plt.bar(F1_Score_pos, F1_Score)plt.xticks(F1_Score_pos, Methods)plt.title(‘comparing the F1 Score of each model’)plt.show()

  • 朴素贝叶斯的 F1 值最高,为 77.1%
  • 决策树的 F1 值为 70.1%。
  • 随机森林的 F1 得分最低,为 66.2%

十二||结论

平均而言,我们的模型大约有 71.8%的准确率。虽然这可能意味着机器无法准确预测每一篇评论,但它也向我们展示了我们的模型没有过度拟合数据的证据。过度拟合是一种建模错误,发生在函数与有限的一组数据点过于接近的时候。过度拟合模型通常发生在使用过于复杂的模型来解释数据中的特质时。然而,由于我们的模型正在被训练成像人脑一样思考,因此可以公平地假设,即使是人类也无法 100%地预测评论是正面还是负面的。这实际上取决于数据及其处理方式。

那么这个模型的最佳算法是什么呢?在本项目使用的 3 种算法中,最准确和精确的是朴素贝叶斯算法。

然而,我们的随机森林算法的召回率是最高的。这意味着随机森林算法实际上通过将它标记为阳性(真阳性)来计算我们的模型捕获了多少实际阳性。当存在与假阴性相关联的高成本时,这将是我们用来选择最佳模型的良好指标。

事实存在于我们的 F1 分数中,这实际上可能是在所选的三个模型中哪个模型最好的最佳预测器。朴素贝叶斯算法具有最高的 F1 分数,这意味着它定义了特定模型的召回率和精确度之间的关系。如果我们需要在精确度和召回率之间寻求平衡,并且如果存在不均匀的类别分布(大量实际否定),F1 分数可能是更好的衡量标准。

感谢阅读!希望你学到了有用的东西。

关注 Rohan Gupta,了解更多与数据科学和机器学习相关的内容

将人脸识别构建为 REST API

原文:https://towardsdatascience.com/build-face-recognition-as-a-rest-api-4c893a16446e?source=collection_archive---------4-----------------------

有很多开源的人脸识别包,比如 face_recognition ,你可以很容易地将它们安装在 Linux 服务器上。但是在移动和物联网设备上部署它们是非常困难或者不可能的。一种选择是使用机器学习移动框架,如 TensorFlow Lite 来调用预先训练好的模型。

但是有更简单的选择吗?是啊!随着 5G 的到来,上传 100KB 的图像只需 0.01 秒,速度约为 100Mbps ,因此我们可以部署几乎所有东西,包括服务器端的人脸识别服务和客户端的轻量级应用。这篇文章将展示如何使用 Python Flask 在 Linux 服务器上为 face_recognition 构建一个 RESTful API。

人脸识别项目

face_recognition 是一个基于 dlib 的非常棒的人脸识别开源项目,正如它自己所描述的:

世界上最简单的 Python 面部识别 api 和命令行

只需几行 Python 代码,就可以进行人脸比对、人脸检测、面部特征查找。

例如,运行示例find _ face _ features _ in _ picture . py(只有 40 行代码)获取一幅奥巴马的图像,您可以得到如下绘制的所有面部特征。

要在 Linux 服务器上安装它,你可以遵循 Github 上 face_recognition 项目中的步骤,或者只需下载预配置的虚拟机。对于 Windows 用户,Sasiwut Chaiyadecha 提供的步骤对我很管用。

构建 REST API

让我们使用 face_recognition 包定义两个 API。

  • 对比两张脸:上传两张图片,返回真/假进行匹配
  • 从已知的数据集中识别一张脸:上传一张图片并返回这个人的名字。

面部识别功能

在文件 face_util.py 中为这两个 API 定义了两个人脸识别函数作为 util。

第一个函数 compare_faces 比较两个图像文件,如果两个图像中的人脸是同一个人,则返回 True。

face_util_part1.py

第二个函数 face_rec 检查图像文件中的人脸是否是数据集中的已知人脸,并返回人名。在这个例子中有两个已知的面。

face_util_part2.py

带烧瓶的 REST API

有了上面的函数,我们可以很容易地用 Flask 定义 REST API,如下。

第一个 API 是 face_match 。它从 POST 请求的表单数据中获取两个图像文件,并调用 compare_faces 来检查它们是否匹配,然后返回一个 JSON 格式的结果。

flask_server_v1_part1.py

第二个 API 是 face_rec,它取一个图像文件作为输入,调用 face_rec 检查是否是已知人脸,然后以 JSON 格式返回人名。

flask_server_v1_part2.py

你可以在这里下载完整的 flask_server_v1.py 文件。

通过pip install -U flask安装烧瓶模块,然后运行python flask_server_v1.py启动服务器 API。启动输出如下所示。

* Serving Flask app "flask_server_v1" (lazy loading)
...
 * Debug mode: on
 * Running on [http://0.0.0.0:5001/](http://0.0.0.0:5001/) (Press CTRL+C to quit)

您可以使用以下 URL 以 POST 表单数据格式访问这两个 API:

  • 比较两张脸 : http:// <服务器 IP 地址> : 5001/face_match
  • 认个脸: http:// <服务器 IP 地址> : 5001/face_rec

REST API 客户端示例

您可以使用任何编程语言调用 API。我只是以 Python 为例。它需要请求模块,你可以通过pip install -U requests安装。

在第一个例子中,我们用同一个人的两幅图像调用 face_match API。

demo_client_part1.py

通过python demo_client_part1.py运行示例,您将得到如下响应:

{"match": true}

在第二个例子中,我们调用 face_rec API 来查找一个人的名字。

demo_client_part2_v1.py

运行脚本,您将得到如下响应:

{"name": "Obama"}

了解发布表单数据

如果你已经知道 HTML 表单数据,跳过这一节。

表单数据只不过是用户提交给服务器端的 HTML 表单。检查不同的 HTML 表单。例如,一个简单的输入表单如下所示。

当您单击 Submit 时,它实际上向 web 服务器发送一个 HTTP POST 请求,其中包含如下数据:

在我们的例子中,我们使用文件形式。为了更好地理解它,让我们修改 face_rec API,以便当用户通过 GET 访问 API 时返回一个 HTML 表单页面,即在 web 浏览器中键入 URL,当请求内容是 POST 时打印它。

首先,我们定义了一个新函数 print_request() 来打印 POST 请求的 URL、键头和正文内容。应用了一个技巧,用字符串'< raw image data >'替换 raw image data,这样我们就可以打印完整的 POST 请求。

flask_server_v2_part1.py

然后我们修改 face_rec API 如下:

  • 在方法中添加 GET 支持。(第一行)
  • 如果是 POST,打印请求。(第 3 行)
  • 如果是 GET,返回一个 HTML 文件表单页面。(第 12–20 行)

flask_server_v2_part2.py

你可以在这里下载完整的 flask_server_v2.py 。

运行新的服务器 API python flask_server_v2.py,然后打开 web 浏览器,输入 face_rec URL,例如 http://127.0.0.1: 5001/face_rec。

你会看到上面的网页,点击浏览选择一个图像文件,然后点击上传按钮。它将向服务器端发送一个包含所选文件的 POST 请求,即调用 face_rec API。因此您将在浏览器上获得 API 响应。

同时,POST 请求内容将在服务器端打印出来,如下所示。

Base64 图像编码

表单数据传输图像数据是有效的,因为数据是直接以原始二进制传输的。但是如果你想以 JSON body 格式传输图像, base64 编码是一种流行的解决方案,它将二进制数据转换成可打印的 ASCII 文本字符串,但是有 33%的开销,即(8–6)/6。

有了 Python base64 库,简直易如反掌。只需调用 b64encode 和 b64decode 函数。但是有一个技巧,你需要将 b64encode 的字节字符串输出转换为普通字符串,例如 b'abc '转换为' abc ',然后再将其传递给 JSON 对象,因为 JSON 不支持 Python 字节字符串。

我们可以如下使用 JSON 格式重新编写 face_rec API 客户端。

base64 encode client

你可以在这里下载完整的客户端脚本 v3 。

并相应地修改服务器端以支持 JSON 格式,如下所示。

base64 encode REST API server

我们将 base64 字符串解码回二进制数据,并将其保存为图像文件,然后调用我们在基于表单数据的 API 中使用的相同的 face_rec 函数。比较帖子内容长度,你会看到 33%的开销如下。

Peter2.jpg file size: 89KBPost form data content length: 91162Post base64 data content length: 121396(121396 - 91162) / 91162 = 33%

你可以在这里找到完整的服务器端脚本 v3。

进一步的工作

您可以向 API 添加更多功能,例如,返回面部位置和面部特征,这样您就可以在移动设备上实现与在 Linux 服务器上相同的功能。

我已经实现了一个使用 REST API 绘制面部位置和面部特征的版本,并带有额外的参数,如果感兴趣,你可以在这里下载完整的库。

感谢阅读到此结束。

领英

自己构建—使用 Keras/TensorFlow 模型的聊天机器人 API

原文:https://towardsdatascience.com/build-it-yourself-chatbot-api-with-keras-tensorflow-model-f6d75ce957a5?source=collection_archive---------0-----------------------

在 Keras/TensorFlow 模型上构建简单聊天机器人的分步解决方案及源代码

Source: Pixabay

构建自己的聊天机器人(或助手,这个词是聊天机器人的一个新的流行术语)并不像你想象的那么复杂。各种聊天机器人平台正在使用分类模型来识别用户意图。虽然很明显,在现有平台的基础上构建聊天机器人时,你会得到一个很好的提示,但研究背景概念并尝试自己构建它不会有什么坏处。为什么不自己用一个类似的模型。聊天机器人实施的主要挑战是:

  1. 对用户输入进行分类以识别意图(这可以通过机器学习来解决,我正在使用带有 TensorFlow 后端的 Keras)
  2. 保持上下文。这部分是编程,这里没有什么 ML 相关的。我使用 Node.js 后端逻辑来跟踪对话上下文(在上下文中,通常我们不需要对用户意图进行分类——用户输入被视为聊天机器人问题的答案)

我的 GitHub repo(开源)上提供了本文的完整源代码和自述说明。

这是实现中使用的 Python 库的列表。 Keras 深度学习库用于建立分类模型。Keras 在 TensorFlow 后端上运行培训。兰开斯特词干库用于折叠不同的单词形式:

import nltk
from nltk.stem.lancaster import LancasterStemmer
stemmer = LancasterStemmer()# things we need for Tensorflow
import numpy as np
from keras.models import Sequential
from keras.layers import Dense, Activation, Dropout
from keras.optimizers import SGD
import pandas as pd
import pickle
import random

聊天机器人的意图和学习模式在普通的 JSON 文件中定义。不需要有庞大的词汇量。我们的目标是建立一个特定领域的聊天机器人。也可以为小词汇量创建分类模型,它将能够识别为训练提供的一组模式:

chatbot training data

在我们开始分类模型训练之前,我们需要首先建立词汇。模式被处理以构建词汇表。每个单词被词干化以产生通用词根,这将有助于覆盖用户输入的更多组合:

words = []
classes = []
documents = []
ignore_words = ['?']
# loop through each sentence in our intents patterns
for intent in intents['intents']:
    for pattern in intent['patterns']:
        # tokenize each word in the sentence
        w = nltk.word_tokenize(pattern)
        # add to our words list
        words.extend(w)
        # add to documents in our corpus
        documents.append((w, intent['tag']))
        # add to our classes list
        if intent['tag'] not in classes:
            classes.append(intent['tag'])# stem and lower each word and remove duplicates
words = [stemmer.stem(w.lower()) for w in words if w not in ignore_words]
words = sorted(list(set(words)))# sort classes
classes = sorted(list(set(classes)))# documents = combination between patterns and intents
print (len(documents), "documents")
# classes = intents
print (len(classes), "classes", classes)
# words = all words, vocabulary
print (len(words), "unique stemmed words", words)

这是词汇创造的输出。共有 9 个意图(类别)和 82 个词汇:

45 documents
9 classes ['adverse_drug', 'blood_pressure', 'blood_pressure_search', 'goodbye', 'greeting', 'hospital_search', 'options', 'pharmacy_search', 'thanks']
82 unique stemmed words ["'s", ',', 'a', 'advers', 'al', 'anyon', 'ar', 'awesom', 'be', 'behavy', 'blood', 'by', 'bye', 'can', 'caus', 'chat', 'check', 'could', 'dat', 'day', 'detail', 'do', 'dont', 'drug', 'entry', 'find', 'for', 'giv', 'good', 'goodby', 'hav', 'hello', 'help', 'hi', 'hist', 'hospit', 'how', 'i', 'id', 'is', 'lat', 'list', 'load', 'loc', 'log', 'look', 'lookup', 'man', 'me', 'mod', 'nearby', 'next', 'nic', 'of', 'off', 'op', 'paty', 'pharm', 'press', 'provid', 'react', 'rel', 'result', 'search', 'see', 'show', 'suit', 'support', 'task', 'thank', 'that', 'ther', 'til', 'tim', 'to', 'transf', 'up', 'want', 'what', 'which', 'with', 'you']

训练不会基于单词的词汇来运行,单词对于机器来说是没有意义的。我们需要将单词翻译成包含 0/1 数组的单词包。数组长度将等于词汇大小,当当前模式中的一个单词位于给定位置时,将设置 1:

# create our training data
training = []
# create an empty array for our output
output_empty = [0] * len(classes)# training set, bag of words for each sentence
for doc in documents:
    # initialize our bag of words
    bag = []
    # list of tokenized words for the pattern
    pattern_words = doc[0]
    # stem each word - create base word, in attempt to represent related words
    pattern_words = [stemmer.stem(word.lower()) for word in pattern_words]
    # create our bag of words array with 1, if word match found in current pattern
    for w in words:
        bag.append(1) if w in pattern_words else bag.append(0)

    # output is a '0' for each tag and '1' for current tag (for each pattern)
    output_row = list(output_empty)
    output_row[classes.index(doc[1])] = 1

    training.append([bag, output_row])# shuffle our features and turn into np.array
random.shuffle(training)
training = np.array(training)# create train and test lists. X - patterns, Y - intents
train_x = list(training[:,0])
train_y = list(training[:,1])

训练数据— X(转换为数组[0,1,0,1…,0]的模式),Y(转换为数组[1,0,0,0,…,0]的 intents,intents 数组将只有一个 1)。模型是用 Keras 构建的,基于三层。根据我的实验,三层提供了良好的结果(但这都取决于训练数据)。分类输出将是多类数组,这将有助于识别编码意图。使用 softmax 激活生成多类分类输出(结果返回 0/1 的数组:[1,0,0,…,0] —该集合标识编码意图):

# Create model - 3 layers. First layer 128 neurons, second layer 64 neurons and 3rd output layer contains number of neurons
# equal to number of intents to predict output intent with softmaxmodel = Sequential()
model.add(Dense(128, input_shape=(len(train_x[0]),), activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(64, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(len(train_y[0]), activation='softmax'))

使用 SGD 优化器编译 Keras 模型:

# Compile model. Stochastic gradient descent with Nesterov accelerated gradient gives good results for this modelsgd = SGD(lr=0.01, decay=1e-6, momentum=0.9, nesterov=True)
model.compile(loss='categorical_crossentropy', optimizer=sgd, metrics=['accuracy'])

拟合模型—执行训练并构建分类模型。我在 200 次迭代中执行训练,批量大小= 5:

# Fit the modelmodel.fit(np.array(train_x), np.array(train_y), epochs=200, batch_size=5, verbose=1)

模型已建立。现在我们可以定义两个辅助函数。函数 bow 帮助将用户句子翻译成数组为 0/1 的单词包:

def clean_up_sentence(sentence):
    # tokenize the pattern - split words into array
    sentence_words = nltk.word_tokenize(sentence)
    # stem each word - create short form for word
    sentence_words = [stemmer.stem(word.lower()) for word in sentence_words]
    return sentence_words# return bag of words array: 0 or 1 for each word in the bag that exists in the sentence
def bow(sentence, words, show_details=True):
    # tokenize the pattern
    sentence_words = clean_up_sentence(sentence)
    # bag of words - matrix of N words, vocabulary matrix
    bag = [0]*len(words)  
    for s in sentence_words:
        for i,w in enumerate(words):
            if w == s: 
                # assign 1 if current word is in the vocabulary position
                bag[i] = 1
                if show_details:
                    print ("found in bag: %s" % w)return(np.array(bag))

看看这个例子——把这个句子翻译成一个单词包:

p = bow("Load blood pessure for patient", words)
print (p)
print (classes)

当该函数在 chatbot 词汇表的句子中找到一个单词时,它会在数组中的相应位置设置 1。该数组将被发送到模型进行分类,以识别其所属的意图:

found in bag: load
found in bag: blood
found in bag: for
found in bag: paty
[0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]

将训练好的模型保存到 pickle 文件中是一个很好的做法,以便能够重用它来通过 Flask REST API 发布:

# Use pickle to load in the pre-trained model
global graph
graph = tf.get_default_graph()with open(f'katana-assistant-model.pkl', 'rb') as f:
    model = pickle.load(f)

在通过 Flask REST API 发布模型之前,运行一个额外的测试总是好的。使用 model.predict 函数对用户输入进行分类,并根据计算出的概率返回意图(可以返回多个意图):

def classify_local(sentence):
    ERROR_THRESHOLD = 0.25

    # generate probabilities from the model
    input_data = pd.DataFrame([bow(sentence, words)], dtype=float, index=['input'])
    results = model.predict([input_data])[0]
    # filter out predictions below a threshold, and provide intent index
    results = [[i,r] for i,r in enumerate(results) if r>ERROR_THRESHOLD]
    # sort by strength of probability
    results.sort(key=lambda x: x[1], reverse=True)
    return_list = []
    for r in results:
        return_list.append((classes[r[0]], str(r[1])))
    # return tuple of intent and probability

    return return_list

句子分类示例:

classify_local('Fetch blood result for patient')

意图计算正确:

found in bag: blood
found in bag: result
found in bag: for
found in bag: paty[('blood_pressure_search', '1.0')]

为了通过 REST 端点发布相同的函数,我们可以将包装到 Flask API 中:

app = Flask(__name__)
CORS(app)[@app](http://twitter.com/app).route("/katana-ml/api/v1.0/assistant", methods=['POST'])
def classify():
    ERROR_THRESHOLD = 0.25

    sentence = request.json['sentence']

    # generate probabilities from the model
    input_data = pd.DataFrame([bow(sentence, words)], dtype=float, index=['input'])
    results = model.predict([input_data])[0]
    # filter out predictions below a threshold
    results = [[i,r] for i,r in enumerate(results) if r>ERROR_THRESHOLD]
    # sort by strength of probability
    results.sort(key=lambda x: x[1], reverse=True)
    return_list = []
    for r in results:
        return_list.append({"intent": classes[r[0]], "probability": str(r[1])})
    # return tuple of intent and probability

    response = jsonify(return_list)
    return response# running REST interface, port=5000 for direct test, port=5001 for deployment from PM2
if __name__ == "__main__":
    app.run(debug=False, host='0.0.0.0', port=5001)

我已经解释了如何实现分类部分。在帖子开头引用的 GitHub repo 中,你会找到如何维护上下文的完整例子。上下文由用 JavaScript 编写并运行在 Node.js 后端的逻辑维护。一旦意图被分类并且后端逻辑找到了上下文的起点,就必须在意图列表中定义上下文流——我们进入循环并询问相关的问题。上下文处理有多高级完全取决于后端实现(这超出了现阶段机器学习的范围)。

聊天机器人用户界面:

Chatbot UI implemented with Oracle JET

使用概率模型和 AWS Lambda 函数构建智能(er)应用程序

原文:https://towardsdatascience.com/build-smart-er-applications-with-probabilistic-models-and-aws-lambda-functions-da982d69cab1?source=collection_archive---------22-----------------------

用 WebPPL 概率程序为端点供电的快速、廉价和无服务器的方法。

哎呀!…我需要再次部署一个模型

"概率是生活的向导."―西塞罗

当今天的酷孩子忙着画张量和画 PyTorch 时,更酷的孩子越来越喜欢概率编程(PP)——例如参见 Pyro , Figaro , Stan ,Infer.NET(想了解更多关于 PP 的哲学倾向的读者可以从我们自己关于概念学习的观点文章开始

如果你是一名数据科学家/数据工程师/人工智能什么的,并且你正在考虑给你现有的数据管道添加一些概率魔法——而不打扰你的 devOps 团队和花费云资金——你可能会喜欢让微服务以一种快速、廉价和直接的方式公开模型的想法。

在这篇文章中,我们利用无服务器和 AWS lambda 功能在几分钟内部署一个 WebPPL 驱动的端点。正如我们将看到的,新部署的端点,加上其他无服务器代码(例如,一个单页 web 应用程序),成为一个可以在组织内共享的原型,用于快速反馈和快速迭代。

这是“懒惰开发”系列中之前帖子的续篇,该系列以带有 Tensorflow 模型和 Spark 决策树的 AWS Lambda 函数为特色。

先决条件

在深入研究代码之前,请确保:

  1. 拥有一个安装了 npm 的工作节点环境;
  2. 拥有一个 AWS 帐户来将代码部署到AWS Lambda;
  3. 设置无服务器(遵循此处的说明;记得设置你的 AWS 凭证);
  4. 如果你有一个 WebPPL 模型准备好了,太好了(100 奖励点!);如果没有,不用担心:我们将一起发货!

像往常一样,你可以在我们的 GitHub 页面中找到所有代码:让我们开始工作吧。

(玩具)概率应用程序

“所有的模型都是错的,但有些是有用的。”―G .盒子

如果说 Javascript 中程序的语义是从程序到执行的函数,那么它的 PP 表亲 WebPPL 的语义是从程序到执行的分发的函数。换句话说,当你运行这个程序时:

结果是结果的分布,通过 I)列举随机变量(die1die2)的可能值,ii)以die1 + die2之和的值(8)为条件,即拒绝条件为满足的所有运行:

Frequency distribution for the value of the first die, conditional on the sum of the two being 8.

由于这不是对概率规划的介绍,因此我们仅简单提及 PP 原则已经成功应用于各种各样的现象,例如技能评级、计算机视觉和基于图像的推理(参见此处和此处)、程序合成、不确定性下的决策, 自然语言语义学和语用学,以及显然在认知科学中模拟人类推理的棘手任务——非懒惰读者被强烈鼓励进一步探索理论景观并了解这一工具家族的许多优点(例如,在服务预测时量化模型不确定性)。

正如所料,我们将使用的语言是 WebPPL ,一个纯粹的函数 Javascript 子集,增加了概率原语,如sample(一个函数,不出所料,从分布中采样值)。为了展示 WebPPL 的概率性质,我们将从高斯过程中采样并提取数据点 ( GP ): GPs 非常酷,非常通用并在数据科学社区中得到越来越多的关注(显然,我们讨论的内容并不特定于 GPs ,因此可以应用于其他类型的 WebPPL 程序,例如,对计算机视觉的推理

Charting priors from GPs with the RBF kernel (mean=0, sigma=1).

预期的工作流程如下:

A lambda-powered web app for prototyping and data viz. with WebPPL and standard JS libraries.

  • 用户从公共 URL 请求 web 应用程序;
  • 一个λ函数被 API 网关调用;响应是简单的 HTML 代码,包含一个基本表单,要求用户输入一些变量;
  • 用户通过一个 HTML 按钮提交她的选择,这个按钮使用 JQuery 向模型端点发送一个 AJAX 请求;
  • API 网关调用 Lambda 函数;响应是在用户输入上运行所选择的 WebPPL 程序(下面将详细介绍)的输出;
  • 最后,一些 Chart.js 代码将端点响应映射到一个漂亮的数据可视化。

在下一节中,我们将看到如何在几秒钟内从一个存在于 powerpoint 中的想法变成一个有效的 web 应用程序。

空谈不值钱,给我看看代码!

“给一个人一个程序,让他沮丧一天。
教一个人编程,挫他一辈子。”―米·瓦西姆

我们的项目功能仅依赖于项目中的两个文件:

  • handler.js包含上述两个功能的代码;
  • serverless.yml是一个标准的 yml 文件,描述了运行这两个功能的 AWS 基础设施。

serverless.yml是一个普通的无服务器文件,它通过 API Gateway 将 lambda 函数公开为端点:这里没有什么特别的(例如,万维网上已经充满了关于这个设置的教程,包括我们自己的)。

所有神奇的事情都发生在这里。该结构确实相当简单,因此我们将重点介绍一些关键特性:

  • WebPPL 是用顶部的webppl = require('webppl')语法导入的;
  • app是一个简单的函数,返回一个完整的 HTML 页面,包括在末尾添加所需输入字段和基本交互的 JQuery 函数——HTML 代码简单地存储为一个字符串,并在响应中返回特定的headers
  • model函数是实际运行 WebPPL 代码的函数:它从一些简单的参数检查/验证/类型转换开始,然后使用webppl.run(CODE)执行在const code变量中指定的代码。请注意,为了向模型传递查询参数,我们使用了书中最简单的技巧,即构建一个包含脚本变量的代码片段(作为字符串),并确保code使用输入变量的正确名称。一个显而易见的选择是全球商店,但是如果没有特别的顾虑(就像在这个例子中),我们的简单策略就足够了。

要查看项目的运行情况,只需克隆 repo ,打开终端,cd进入文件夹,下载 WebPPL 依赖关系:

npm install webppl

然后最后键入:

serverless deploy

如果无服务器安装正确,第一个deploy触发 AWS 上所需资源的创建(未来的deploy命令会更快)。等待一段时间后,您应该会在终端中看到一条成功消息:

Congratulations, your lambdas are up and running!

如果你将浏览器指向在终端中找到的/app URL(见上图中的“端点”),一切都应该如预期的那样工作:

Our fantastic web app is up and running (original video here)!

当您对原型满意并准备好清理资源时,只需使用remove 命令来安全地移除堆栈:

serverless remove

后续步骤

“一千个模特的旅程从一个(几乎)老子开始”

这篇的简短帖子中,我们展示了如何将概率模型部署到 AWS,并通过标准 GET 请求用几行代码按需提供预测:

这种设置快速、可靠且廉价(如果不是免费的,因为前 100 万个请求/mo 完全在杰夫·贝索斯上)。

要使提供的模板适应您自己的原型,只需更改model函数中的 WebPPL 代码,然后相应地调整 HTML 输入字段、参数解析和数据可视化:在几分钟内,您将能够与任何人分享您非凡的贝叶斯建模的结果,或者生成有洞察力的可视化来帮助您的科学探索。

在可视化方面, WebPPL viz 模块可能会有一些启发来概括现在简单而杂乱的图表:使用 chart.js / d3 围绕 WebPPL 模型的主要用例构建一个可重用的包装器应该不会花费太长时间对于非懒惰读者的练习:对于线性回归案例,例如单一回归来说,什么可能是有趣且引人入胜的可视化].

在建模方面,我们选择了 GP s 来提供一个在浏览器窗口中看起来不错的端到端概率程序,但显然概率人工智能的世界是伟大的,并且——像宇宙一样——在不断扩展:如果你想要一些 PP 建模的想法,这里有一吨很酷的东西。

最后,如果你真的喜欢这个设置,你认为它已经准备好了,一定要添加测试,错误处理和所有其他重要的东西,这个教程太短了,无法包含。

快乐(概率)编码!

再见,太空牛仔

如果您有问题、反馈或评论,请与jacopo . taglia bue @ tooso . ai分享您的故事。

别忘了从 Linkedin 、 Twitter 和 Instagram 上的 Tooso 获取最新消息。

感谢

如果没有 Tooso 的高级工程师和节点向导 Luca,这一切都是不可能的:Luca 建立了我们的第一个 WebPPL-to-lambda 游乐场,并耐心地回答了我所有(愚蠢的)节点问题。

这篇文章中大多数好的想法都是卢卡的,剩下的错误都是我的。

构建正确的自动编码器——使用 PCA 原理进行调整和优化。第一部分

原文:https://towardsdatascience.com/build-the-right-autoencoder-tune-and-optimize-using-pca-principles-part-i-1f01f821999b?source=collection_archive---------2-----------------------

在这里,我们将学习自动编码器的期望属性,这些属性源自它与 PCA 的相似性。由此,我们将在第二部分中为自动编码器构建定制约束,以进行调整和优化。

<了解深度学习,了解更多> >

深度学习 API 的可用性,如 Keras 和 TensorFlow,使模型建立和实验变得极其容易。然而,对基本面缺乏清晰的理解可能会让我们在最佳模式的竞赛中迷失方向。在这样的比赛中达到最佳模式是靠运气的。

在这里,我们将了解自动编码器所需的基本属性。这将为自动编码器调整和优化提供一个很好的指导方法。在第一部分中,我们将重点学习这些属性及其优点。在第二部分,我们将开发自定义层和约束来合并属性。

我们将在这里学习的主要概念是,自动编码器与主成分分析(PCA) 直接相关,这将使我们能够构建一个正确的自动编码器。一个“正确的”自动编码器在数学上意味着一个适定的自动编码器。一个适定的模型更容易调整和优化。

自动编码器相对于PCA

  • 线性激活的自动编码器近似于 PCA。数学上,最小化 PCA 建模中的重建误差与单层线性自动编码器相同。
  • 自动编码器将 PCA 扩展到非线性空间。换句话说,自动编码器是 PCA 的非线性扩展。

因此,自动编码器应该理想地具有 PCA 的特性。这些属性是,

  • 绑定权重:编码器和相应解码器层上的权重相等(在下一节的图 1 中阐明)。
  • 正交权重:每个权重向量都是相互独立的。
  • 不相关特征:编码层的输出不相关。
  • 单位定额:一层上的权重有单位定额。

然而,大多数教程中解释的自动编码器,例如在 Keras【1】中构建自动编码器,没有这些属性。缺少这一点使它们不是最佳的。

因此,对于一个适定的自动编码器来说,结合这些属性是很重要的。通过合并它们,我们还将

  • 有正规化。正交性和单位范数约束作为正则化。此外,正如我们将在后面看到的,绑定权重将网络参数的数量减少了近一半,这是另一种正则化类型。
  • 解决爆炸和消失的渐变。单位范数约束防止权重变大,因此解决了爆炸梯度问题。此外,由于正交性约束,只有重要的/信息性的权重是非零的。因此,在反向传播期间,足够的信息流过这些非零权重,从而避免消失梯度。
  • 有更小的网络:没有正交性,编码器有冗余的权重和特征。为了补偿冗余,增加了编码器的尺寸。相反,正交性确保每个编码特征都有一条唯一的信息——独立于其他特征。这消除了冗余,并且我们可以用较小的编码器(层)编码相同数量的信息。

通过一个更小的网络,我们让 Autoencoder 更接近边缘计算

本文将通过展示

  1. PCA 和 Autoencoder 之间的架构相似性,以及
  2. 传统自动编码器的次优性。

本文将在第二部分中继续,详细介绍优化自动编码器的步骤。在第二部分的中,我们发现优化将自动编码器重构误差提高了 50%以上。

本文假设读者对 PCA 有基本的了解。如果不熟悉,请参考了解 PCA【2】。

PCA 和自动编码器之间的架构相似性

Figure 1. Single layer Autoencoder vis-à-vis PCA.

为简单起见,我们将线性单层自动编码器与 PCA 进行比较。PCA 建模有多种算法。其中之一是通过最小化重建误差进行估计(参见[ 3 ])。遵循该算法可以更清楚地理解 PCA 和自动编码器之间的相似之处。

图 1 显示了单层线性自动编码器。如图底部所示,编码过程类似于 PC 变换。PC 变换将原始数据投影到主成分上,以产生正交特征,称为主得分。类似地,解码过程类似于从主要分数重构数据。在自动编码器和 PCA 中,可以通过最小化重建误差来估计模型权重。

在下文中,我们将通过展示关键的自动编码器组件及其在 PCA 中的等效组件来进一步阐述图 1。

假设我们有带有 p 特征的数据。

输入层—数据样本。

  • 在自动编码器中,使用尺寸为 p.输入层输入数据
  • 在 PCA 中,数据作为样本输入。

编码——数据在主成分上的投影。

  • 编码层的大小为 k 。在 PCA 中, k 表示选择的主成分(PCs)的数量。
  • 在两者中,我们都有 k < p 进行降维。 k ≥ p 导致过代表模型,因此(接近)零重建误差。
  • 图 1 中编码层中的彩色单元是计算节点,其具有表示为p的权重,

也就是说,对于 1,…, k 中的每个编码节点,我们都有一个 p 维的权重向量。这相当于 PCA 中的一个特征向量。

  • 自动编码器中的编码层输出是,

x 是输入, W 是权重矩阵。功能 g 是一个激活功能。 g ( Wx )是编码层的输出。如果激活是线性的,这相当于 PCA 中的主要分数。

解码——从主要分数中重建数据。

  • 自动编码器和 PCA 重建中解码层的大小必须是输入数据的大小
  • 在解码器中,数据从编码中被重构为,

类似地,在 PCA 中,它被重构为,

注意,我们在等式中有W’。4 和等式中的 W 。5.这是因为,默认情况下,编码器和解码器的权重不同。如果编码器和解码器权重与绑定,解码器和 PCA 重建将是相同的,即

解码器单元中的多种颜色表示编码器中不同单元中的权重出现在解码器中的相同单元中。

这就把我们带到了自动编码器和 PCA 之间的数学比较。

数学上,线性自动编码器将类似于 PCA if,

  • 捆绑权重:在任何通用多层自动编码器中,编码器模块中的层 l 的权重等于解码器中从末端开始的层 l 的权重的转置。

  • 正交权重:编码层上的权重是正交的(参见等式。7b)。可以在中间编码器层上实施相同的正交性约束以进行规则化。

  • 不相关特征:PCA 的输出,即主分数,是不相关的。因此,编码器的输出应该具有,

  • 单位范数:PCA 中的特征向量被约束为具有单位范数。如果没有这个约束,我们将得不到一个合适的解,因为只要向量的范数增加,投影的方差就可能变得任意大。出于同样的原因,编码层上的权重应该是单位范数(参见等式 1)。7d)。这个约束也应该应用于其他中间层以进行正则化。

正则无约束自动编码器的次优性

这里我们将在随机数据集上实现 PCA 和一个典型的无约束自动编码器。我们将展示它们的输出在上述各个方面的不同。这导致了次优的自动编码器。在这个讨论之后,我们将展示如何为正确的估计(第二部分)约束自动编码器。

完整的代码可在这里获得。

加载库

**from** **numpy.random** **import** seed
seed(123)
**from** **tensorflow** **import** set_random_seed
set_random_seed(234)

**import** **sklearn**
**from** **sklearn** **import** datasets
**import** **numpy** **as** **np**
**from** **sklearn.model_selection** **import** train_test_split
**from** **sklearn.preprocessing** **import** StandardScaler, MinMaxScaler
**from** **sklearn** **import** decomposition
**import** **scipy**

**import** **tensorflow** **as** **tf**
**from** **keras.models** **import** Model, load_model
**from** **keras.layers** **import** Input, Dense, Layer, InputSpec
**from** **keras.callbacks** **import** ModelCheckpoint, TensorBoard
**from** **keras** **import** regularizers, activations, initializers, constraints, Sequential
**from** **keras** **import** backend **as** K
**from** **keras.constraints** **import** UnitNorm, Constraint

生成随机数据

我们生成多元相关正态数据。数据生成的步骤在 GitHub 库中有详细说明。

n_dim = 5
cov = sklearn.datasets.make_spd_matrix(n_dim, random_state=None)
mu = np.random.normal(0, 0.1, n_dim)n = 1000X = np.random.multivariate_normal(mu, cov, n)X_train, X_test = train_test_split(X, test_size=0.5, random_state=123)# *Data Preprocessing* scaler = MinMaxScaler()
scaler.fit(X_train)X_train_scaled = scaler.transform(X_train)X_test_scaled = scaler.transform(X_test)

测试数据集将在第二部分中用于比较自动编码器重建精度。

自动编码器和 PCA 型号

我们安装了一个单层线性自动编码器,编码维数为两个。我们还为 PCA 安装了两个组件

# *Fit Autoencoder*
nb_epoch = 100
batch_size = 16
input_dim = X_train_scaled.shape[1] *#num of predictor variables,* 
encoding_dim = 2
learning_rate = 1e-3

encoder = Dense(encoding_dim, activation="linear", input_shape=(input_dim,), use_bias = **True**) 
decoder = Dense(input_dim, activation="linear", use_bias = **True**)

autoencoder = Sequential()
autoencoder.add(encoder)
autoencoder.add(decoder)

autoencoder.compile(metrics=['accuracy'],
                    loss='mean_squared_error',
                    optimizer='sgd')
autoencoder.summary()

autoencoder.fit(X_train_scaled, X_train_scaled,
                epochs=nb_epoch,
                batch_size=batch_size,
                shuffle=**True**,
                verbose=0)# *Fit PCA*
pca = decomposition.PCA(n_components=2)pca.fit(X_train_scaled)

Figure 2. Structure of the single-layer Autoencoder.

从视觉上看,这里开发的编码器-解码器结构如下面的图 3 所示。该图有助于理解权重矩阵是如何排列的。

Figure 3. A simple linear Autoencoder to encode a 5-dimensional data into 2-dimensional features.

为了遵循 PCA 属性,图 3 中的自动编码器应该遵循等式中的条件。下面,我们将示出这种传统的自动编码器不满足它们中的任何一个。

1.捆绑重物

正如我们在下面看到的,编码器和解码器的权重是不同的。

w_encoder = np.round(autoencoder.layers[0].get_weights()[0], 2).T  *# W in Figure 3.*
w_decoder = np.round(autoencoder.layers[1].get_weights()[0], 2)  *# W' in Figure 3.*
print('Encoder weights **\n**', w_encoder)
print('Decoder weights **\n**', w_decoder)

2。权重正交性

如下所示,与 PCA 权重(即特征向量)不同,编码器和解码器上的权重不是正交的。

w_pca = pca.components_
np.round(np.dot(w_pca, w_pca.T), 3)

np.round(np.dot(w_encoder, w_encoder.T), 3)

np.round(np.dot(w_decoder, w_decoder.T), 3)

3。特征关联

在 PCA 中,特征是不相关的。

pca_features = pca.fit_transform(X_train_scaled)
np.round(np.cov(pca_features.T), 5)

但是编码特征是相关的。

encoder_layer = Model(inputs=autoencoder.inputs, outputs=autoencoder.layers[0].output)
encoded_features = np.array(encoder_layer.predict(X_train_scaled))
print('Encoded feature covariance\n', np.cov(encoded_features.T))

权重非正交性和特征相关性是不期望的,因为它带来了包含在编码特征中的信息的冗余。

4。单位定额

五氯苯甲醚重量的单位标准是 1。这是在 PCA 估计中应用的约束,以产生正确的估计

print('PCA weights norm, \n', np.sum(w_pca ** 2, axis = 1))
print('Encoder weights norm, \n', np.sum(w_encoder ** 2, axis = 1))
print('Decoder weights norm, \n', np.sum(w_decoder ** 2, axis = 1))

Github 库

完整的代码可在这里获得。

[## cran 2367/PCA-自动编码器-关系

了解 PCA 和自动编码器之间的关系-cran 2367/PCA-自动编码器-关系

github.com](https://github.com/cran2367/pca-autoencoder-relationship/blob/master/pca-autoencoder-relationship.ipynb)

结论

因此,自动编码器模型是不适定的。不适定模型没有稳健的估计。这不利地影响了它的测试精度,即新数据的重建误差。

最近的几项研究进展是建立和利用正交条件来提高深度学习模型的性能。一些研究方向参考[ 4 ]和[ 5 ]。

在续集第二部分中,我们将实现自定义约束,将上述从 PCA 得到的属性合并到自动编码器中。我们将会看到,添加约束改善了测试重构误差。

去看续集,下集。

参考

  1. 在 Keras 中构建自动编码器
  2. 理解主成分分析
  3. 主成分分析:利用重构误差的算法(第 15 页)。
  4. 黄,雷等.正交权重归一化:深度神经网络中多重相关 stiefel 流形上的优化解。第三十二届 AAAI 人工智能大会。2018.
  5. 使用内省对抗网络的神经照片编辑。arXiv 预印本 arXiv:1609.07093 (2016)。

构建正确的自动编码器——使用 PCA 原理进行调整和优化。第二部分

原文:https://towardsdatascience.com/build-the-right-autoencoder-tune-and-optimize-using-pca-principles-part-ii-24b9cca69bd6?source=collection_archive---------3-----------------------

在第一部分的延续中,我们将在这里定义并实现自定义约束,以构建一个适定的自动编码器。一个适定的自动编码器是一个正则化的模型,它改善了测试重建误差。

<了解深度学习,了解更多> >

去看前传第一部

在第一部分中,我们了解到 PCA 和自动编码器在架构上有相似之处。但是尽管如此,自动编码器本身并不具有 PCA 属性,例如正交性。我们知道,结合 PCA 属性将为自动编码器带来显著的好处,例如解决消失和爆炸梯度,以及通过正则化进行过拟合。

基于此,我们希望自动编码器继承的属性是:

  1. 捆绑重物,
  2. 正交权重,
  3. 不相关的特征,以及
  4. 单位定额。

在本文中,我们将

  • 实现自定义层和约束来合并它们。
  • 演示它们是如何工作的,以及它们带来的重构误差的改善。

这些实现将能够构造一个适定的自动编码器并对其进行优化。在我们的示例中,优化将重建误差提高了 50%以上。

注意:正则化技术,如辍学,是普遍使用的。但是没有一个适定的模型,这些方法需要更长的时间来优化。

下一节详细展示了该实现。读者可以跳至关键要点部分进行简要总结。

一个适配的自动编码器

Figure 1. Apply constraints for a well-posed Autoencoder.

我们将为随机生成的数据集开发一个具有五个特征的自动编码器。我们将数据集分为训练集和测试集。当我们添加约束时,我们将使用测试数据重构误差来评估性能。

本文包含实现细节,以帮助从业者尝试各种选择。完整的代码显示在这里。

导入库

from numpy.random import seed
seed(123)
from tensorflow import set_random_seed
set_random_seed(234)import sklearn
from sklearn import datasets
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler, MinMaxScaler
from sklearn import decomposition
import scipyimport tensorflow as tf
from keras.models import Model, load_model
from keras.layers import Input, Dense, Layer, InputSpec
from keras.callbacks import ModelCheckpoint, TensorBoard
from keras import regularizers, activations, initializers, constraints, Sequential
from keras import backend as K
from keras.constraints import UnitNorm, Constraint

生成和准备数据

n_dim = 5
cov = sklearn.datasets.make_spd_matrix(n_dim, random_state=None)mu = np.random.normal(0, 0.1, n_dim)n = 1000X = np.random.multivariate_normal(mu, cov, n)X_train, X_test = train_test_split(X, test_size=0.5, random_state=123)# *Scale the data between 0 and 1.*
scaler = MinMaxScaler()
scaler.fit(X_train)X_train_scaled = scaler.transform(X_train)X_test_scaled = scaler.transform(X_test)X_train_scaled

估算参数

nb_epoch = 100
batch_size = 16
input_dim = X_train_scaled.shape[1] *#num of predictor variables,* 
encoding_dim = 2
learning_rate = 1e-3

基线模型

encoder = Dense(encoding_dim, activation="linear", input_shape=(input_dim,), use_bias = **True**) 
decoder = Dense(input_dim, activation="linear", use_bias = **True**)

autoencoder = Sequential()
autoencoder.add(encoder)
autoencoder.add(decoder)

autoencoder.compile(metrics=['accuracy'],
                    loss='mean_squared_error',
                    optimizer='sgd')
autoencoder.summary()

autoencoder.fit(X_train_scaled, X_train_scaled,
                epochs=nb_epoch,
                batch_size=batch_size,
                shuffle=**True**,
                verbose=0)

Figure 2.1. Baseline Model Parameters.

基线重建误差

train_predictions = autoencoder.predict(X_train_scaled)
print('Train reconstrunction error**\n**', sklearn.metrics.mean_squared_error(X_train_scaled, train_predictions))
test_predictions = autoencoder.predict(X_test_scaled)
print('Test reconstrunction error**\n**', sklearn.metrics.mean_squared_error(X_test_scaled, test_predictions))

Figure 2.2. Baseline Autoencoder Reconstruction Error.

自动编码器优化

Keras 提供了各种层和约束。对于单位定额,我们有一个可用的约束。对于其他人,我们将构建自定义层和约束。

  1. 自定义图层:捆绑砝码。

有了这个自定义层,我们可以使编码器和解码器的权重相等。数学上,解码器权重的转置等于编码器权重(等式)。第一部分 7a)。

**class** **DenseTied**(Layer):
    **def** __init__(self, units,
                 activation=**None**,
                 use_bias=**True**,
                 kernel_initializer='glorot_uniform',
                 bias_initializer='zeros',
                 kernel_regularizer=**None**,
                 bias_regularizer=**None**,
                 activity_regularizer=**None**,
                 kernel_constraint=**None**,
                 bias_constraint=**None**,
                 tied_to=**None**,
                 **kwargs):
        self.tied_to = tied_to
        **if** 'input_shape' **not** **in** kwargs **and** 'input_dim' **in** kwargs:
            kwargs['input_shape'] = (kwargs.pop('input_dim'),)
        super().__init__(**kwargs)
        self.units = units
        self.activation = activations.get(activation)
        self.use_bias = use_bias
        self.kernel_initializer = initializers.get(kernel_initializer)
        self.bias_initializer = initializers.get(bias_initializer)
        self.kernel_regularizer = regularizers.get(kernel_regularizer)
        self.bias_regularizer = regularizers.get(bias_regularizer)
        self.activity_regularizer = regularizers.get(activity_regularizer)
        self.kernel_constraint = constraints.get(kernel_constraint)
        self.bias_constraint = constraints.get(bias_constraint)
        self.input_spec = InputSpec(min_ndim=2)
        self.supports_masking = **True**

    **def** build(self, input_shape):
        **assert** len(input_shape) >= 2
        input_dim = input_shape[-1]

        **if** self.tied_to **is** **not** **None**:
            self.kernel = K.transpose(self.tied_to.kernel)
            self._non_trainable_weights.append(self.kernel)
        **else**:
            self.kernel = self.add_weight(shape=(input_dim, self.units),
                                          initializer=self.kernel_initializer,
                                          name='kernel',
                                          regularizer=self.kernel_regularizer,
                                          constraint=self.kernel_constraint)
        **if** self.use_bias:
            self.bias = self.add_weight(shape=(self.units,),
                                        initializer=self.bias_initializer,
                                        name='bias',
                                        regularizer=self.bias_regularizer,
                                        constraint=self.bias_constraint)
        **else**:
            self.bias = **None**
        self.input_spec = InputSpec(min_ndim=2, axes={-1: input_dim})
        self.built = **True**

    **def** compute_output_shape(self, input_shape):
        **assert** input_shape **and** len(input_shape) >= 2
        output_shape = list(input_shape)
        output_shape[-1] = self.units
        **return** tuple(output_shape)

    **def** call(self, inputs):
        output = K.dot(inputs, self.kernel)
        **if** self.use_bias:
            output = K.bias_add(output, self.bias, data_format='channels_last')
        **if** self.activation **is** **not** **None**:
            output = self.activation(output)
        **return** output

带绑定解码器的自动编码器。

encoder = Dense(encoding_dim, activation="linear", input_shape=(input_dim,), use_bias = True) 
decoder = DenseTied(input_dim, activation="linear", tied_to=encoder, use_bias = True)autoencoder = Sequential()
autoencoder.add(encoder)
autoencoder.add(decoder)autoencoder.compile(metrics=['accuracy'],
                    loss='mean_squared_error',
                    optimizer='sgd')
autoencoder.summary()autoencoder.fit(X_train_scaled, X_train_scaled,
                epochs=nb_epoch,
                batch_size=batch_size,
                shuffle=True,
                verbose=0)

观察结果

1a。同等重量。

w_encoder = np.round(np.transpose(autoencoder.layers[0].get_weights()[0]), 3)
w_decoder = np.round(autoencoder.layers[1].get_weights()[0], 3)
print('Encoder weights**\n**', w_encoder)
print('Decoder weights**\n**', w_decoder)

1b。偏见是不同的。

b_encoder = np.round(np.transpose(autoencoder.layers[0].get_weights()[1]), 3)
b_decoder = np.round(np.transpose(autoencoder.layers[1].get_weights()[0]), 3)
print('Encoder bias\n', b_encoder)
print('Decoder bias\n', b_decoder)

2。自定义约束:权重正交性。

**class** **WeightsOrthogonalityConstraint** (Constraint):
    **def** __init__(self, encoding_dim, weightage = 1.0, axis = 0):
        self.encoding_dim = encoding_dim
        self.weightage = weightage
        self.axis = axis

    **def** weights_orthogonality(self, w):
        **if**(self.axis==1):
            w = K.transpose(w)
        **if**(self.encoding_dim > 1):
            m = K.dot(K.transpose(w), w) - K.eye(self.encoding_dim)
            **return** self.weightage * K.sqrt(K.sum(K.square(m)))
        **else**:
            m = K.sum(w ** 2) - 1.
            **return** m

    **def** __call__(self, w):
        **return** self.weights_orthogonality(w)

对编码器和解码器权重应用正交性。

encoder = Dense(encoding_dim, activation="linear", input_shape=(input_dim,), use_bias=**True**, kernel_regularizer=WeightsOrthogonalityConstraint(encoding_dim, weightage=1., axis=0)) 
decoder = Dense(input_dim, activation="linear", use_bias = **True**, kernel_regularizer=WeightsOrthogonalityConstraint(encoding_dim, weightage=1., axis=1))

autoencoder = Sequential()
autoencoder.add(encoder)
autoencoder.add(decoder)

autoencoder.compile(metrics=['accuracy'],
                    loss='mean_squared_error',
                    optimizer='sgd')
autoencoder.summary()

autoencoder.fit(X_train_scaled, X_train_scaled,
                epochs=nb_epoch,
                batch_size=batch_size,
                shuffle=**True**,
                verbose=0)

观察。

2a。编码器和解码器的权重接近正交。

w_encoder = autoencoder.layers[0].get_weights()[0]
print('Encoder weights dot product**\n**', np.round(np.dot(w_encoder.T, w_encoder), 2))

w_decoder = autoencoder.layers[1].get_weights()[0]
print('Decoder weights dot product**\n**', np.round(np.dot(w_decoder, w_decoder.T), 2))

3。自定义约束:不相关的编码特征。

对于不相关的特征,我们将对编码特征协方差的非对角元素的施加惩罚。

**class** **UncorrelatedFeaturesConstraint** (Constraint):

    **def** __init__(self, encoding_dim, weightage = 1.0):
        self.encoding_dim = encoding_dim
        self.weightage = weightage

    **def** get_covariance(self, x):
        x_centered_list = []

        **for** i **in** range(self.encoding_dim):
            x_centered_list.append(x[:, i] - K.mean(x[:, i]))

        x_centered = tf.stack(x_centered_list)
        covariance = K.dot(x_centered, K.transpose(x_centered)) / tf.cast(x_centered.get_shape()[0], tf.float32)

        **return** covariance

    *# Constraint penalty*
    **def** uncorrelated_feature(self, x):
        **if**(self.encoding_dim <= 1):
            **return** 0.0
        **else**:
            output = K.sum(K.square(
                self.covariance - tf.math.multiply(self.covariance, K.eye(self.encoding_dim))))
            **return** output

    **def** __call__(self, x):
        self.covariance = self.get_covariance(x)
        **return** self.weightage * self.uncorrelated_feature(x)

在自动编码器中应用约束。

encoder = Dense(encoding_dim, activation="linear", input_shape=(input_dim,), use_bias = **True**, activity_regularizer=UncorrelatedFeaturesConstraint(encoding_dim, weightage = 1.)) 
decoder = Dense(input_dim, activation="linear", use_bias = **True**)

autoencoder = Sequential()
autoencoder.add(encoder)
autoencoder.add(decoder)

autoencoder.compile(metrics=['accuracy'],
                    loss='mean_squared_error',
                    optimizer='sgd')
autoencoder.summary()

autoencoder.fit(X_train_scaled, X_train_scaled,
                epochs=nb_epoch,
                batch_size=batch_size,
                shuffle=**True**,
                verbose=0)

观察。

3a。我们有较少相关的编码特征。施加这种惩罚更加困难。可以探索更强的约束函数。

encoder_layer = Model(inputs=autoencoder.inputs, outputs=autoencoder.layers[0].output)
encoded_features = np.array(encoder_layer.predict(X_train_scaled))
print('Encoded feature covariance\n', np.round(np.cov(encoded_features.T), 3))

4。约束:单位定额。

UnitNorm约束是在 Keras 中预先构建的。我们将在编码器和解码器层应用这种约束。值得注意的是,我们为编码器层保留了axis=0,为解码器层保留了axis=1

encoder = Dense(encoding_dim, activation="linear", input_shape=(input_dim,), use_bias = True, kernel_constraint=UnitNorm(**axis=0**)) 
decoder = Dense(input_dim, activation="linear", use_bias = True, kernel_constraint=UnitNorm(**axis=1**))autoencoder = Sequential()
autoencoder.add(encoder)
autoencoder.add(decoder)autoencoder.compile(metrics=['accuracy'],
                    loss='mean_squared_error',
                    optimizer='sgd')
autoencoder.summary()autoencoder.fit(X_train_scaled, X_train_scaled,
                epochs=nb_epoch,
                batch_size=batch_size,
                shuffle=True,
                verbose=0)

观察。

4.a .编码器和解码器沿编码轴的权重范数为,

w_encoder = np.round(autoencoder.layers[0].get_weights()[0], 2).T  # W in Figure 3.
w_decoder = np.round(autoencoder.layers[1].get_weights()[0], 2)  # W' in Figure 3.print('Encoder weights norm, \n', np.round(np.sum(w_encoder ** 2, axis = 1),3))
print('Decoder weights norm, \n', np.round(np.sum(w_decoder ** 2, axis = 1),3))

如前所述,规范并不完全是 1.0,因为这不是一个硬约束。

把所有东西放在一起

这里我们将把上述属性放在一起。根据问题的不同,这些属性的某种组合会比其他组合更好。

同时应用几个约束有时会损害评估。例如,在此处使用的数据集中,结合联系层、权重正交性和单位范数效果最佳。

encoder = Dense(encoding_dim, activation="linear", input_shape=(input_dim,), use_bias = True, kernel_regularizer=WeightsOrthogonalityConstraint(encoding_dim, weightage=1., axis=0), kernel_constraint=UnitNorm(axis=0)) 
decoder = DenseTied(input_dim, activation="linear", tied_to=encoder, use_bias = False)autoencoder = Sequential()
autoencoder.add(encoder)
autoencoder.add(decoder)autoencoder.compile(metrics=['accuracy'],
                    loss='mean_squared_error',
                    optimizer='sgd')
autoencoder.summary()autoencoder.fit(X_train_scaled, X_train_scaled,
                epochs=nb_epoch,
                batch_size=batch_size,
                shuffle=True,
                verbose=0)train_predictions = autoencoder.predict(X_train_scaled)
print('Train reconstrunction error\n', sklearn.metrics.mean_squared_error(X_train_scaled, train_predictions))
test_predictions = autoencoder.predict(X_test_scaled)
print('Test reconstrunction error\n', sklearn.metrics.mean_squared_error(X_test_scaled, test_predictions))

GitHub 库

这里的完整地提到了模型调整的步骤和更多细节。

[## cran 2367/PCA-自动编码器-关系

了解 PCA 和自动编码器之间的关系-cran 2367/PCA-自动编码器-关系

github.com](https://github.com/cran2367/pca-autoencoder-relationship/blob/master/pca-autoencoder-relationship.ipynb)

关键要点

重建误差的改善

Table 1. Summary of reconstruction errors.

  • 基线模型测试数据的重建误差为 0.027。
  • 将每个属性添加到 Autoencoder 减少了测试错误。改进范围从 19%的重量正交性到 67%的束缚重量。
  • 不同的数据有不同的改进。
  • 在我们的问题中,结合束缚权重、权重正交性和单位范数产生了具有最佳重构误差的最优模型。
  • 尽管最佳模型中的误差大于仅具有束缚权重的模型,但这更稳定,因此更可取。

关键实施说明

绑重物

  • 在绑定权重层DenseTied中,编码器和解码器中的偏差会有所不同。
  • 要使所有重量完全相等,设置use_bias=False

权重正交

  • kernel_regularizer用于在层的权重上添加约束或正则化。
  • 正交轴对于编码器应该是按行的,axis=0,对于解码器应该是按列的,axis=1

不相关的编码特征

  • activity_regularizer用于对层的输出特征应用约束。因此,这里使用它来将编码特征的非对角协方差约束为零。
  • 这个约束不强。也就是说,它不会将非对角协方差元素推到非常接近零的程度。
  • 可以探索该约束的另一种定制。

单位定额

  • UnitNorm编码器和解码器应该在不同的轴上。
  • 类似于权重正交性,这适用于编码器的行axis=0和解码器的列axis=1

通用

  • 有两个类,RegularizerConstraints用于构建定制函数。实际上,对于我们的应用程序来说,两者是一样的。对于权重正交性和不相关特征,我们使用了Constraints类。
  • 所有三个约束——单位范数、不相关编码特征和权重正交性——都是软约束。也就是说,它们可以使模型权重和特征接近所需属性,但并不精确。例如,权重几乎是单位范数,而不是精确的。

在实践中

  • 合并这些属性的性能将因问题而异。
  • 在不同的设置下,例如有和没有偏差,以及不同层上正交性和不相关特征约束的不同权重下,分别探索每个属性。
  • 除此之外,在自动编码器中包括流行的正则化技术,例如丢弃层。

摘要

  • 在前传第一部分中,我们学习了自动编码器应该从 PCA 继承的重要属性。
  • 这里,我们实现了自定义层和约束来合并这些属性。
  • 在表 1 中,我们展示了这些特性显著改善了测试重构误差。
  • 正如在关键要点中提到的,我们需要反复试验来找到最佳设置。然而,这些试验是在一个具有解释意义的方向上进行的。

转到前传第一部

参考

  1. https://stack overflow . com/questions/53751024/tieng-auto encoder-weights-in-a-dense-keras-layer

用 Python 构建神经网络

原文:https://towardsdatascience.com/build-up-a-neural-network-with-python-7faea4561b31?source=collection_archive---------15-----------------------

机器学习和深度学习之旅

使用 NumPy 实现正向传播、反向传播

Figure 1: Neural Network

这个博客的目的是使用 python 中的包 NumPy 来建立一个神经网络。虽然像 Keras 和 Tensorflow 这样的成熟包使建立模型变得容易,但是值得自己编写向前传播、向后传播和梯度下降的代码,这有助于更好地理解该算法。

概述

Figure 2: Overview of forward propagation and backward propagation

上图显示了训练神经网络模型时,信息是如何流动的。输入 Xn 后,权重 W1 和偏差 B1 的线性组合被应用于 Xn 。接下来,应用激活函数进行非线性变换以得到 A1 。然后进入 A1 作为下一个隐藏层的输入。相同的逻辑应用于生成 A2A3 。产生 A1A2A3 的过程称为正向传播。 A3 也作为神经网络的输出,与自变量 y 比较计算成本。然后计算成本函数的导数,得到 dA3 。对 W3B3dA3 的偏导数,得到 dW3dB3 。同样的逻辑适用于得到 dA2dW2dB2dA1dW1dB1 。生成导数列表的过程称为反向传播。最后应用梯度下降并更新参数。然后,新一轮迭代从更新的参数开始。该算法不会停止,直到它收敛。

创建测试数据

创建一小组测试数据来验证所创建的功能。

初始化参数

在参数初始化阶段,权重被初始化为接近零的随机值。如果权重接近于零,则 sigmoid 的操作部分大致是线性的,因此神经网络崩溃为近似线性的模型[1]sigmoid 函数在零点附近的梯度较陡,利用梯度下降可以快速更新参数。不要使用零和大的权重,这会导致糟糕的解决方案。

我在 Excel 中手动计算了一次神经网络的迭代训练,这有助于您验证每一步创建的函数的准确性。下面是参数初始化的输出。

Table 1: Parameters Initialization Testing Result

正向传播

在神经网络中,输入 Xn 被输入,信息通过整个网络向前流动。输入 Xn 提供初始信息,该信息向上传播到每层的隐藏单元,并最终产生预测。这个过程称为正向传播。正向传播包括两个步骤。第一步是权重和来自最后一层的输出(或输入 Xn )的线性组合,以生成 Z 。第二步是应用激活函数进行非线性变换。

Table 2: Matrix Calculation in forward propagation

第一步,你需要注意投入和产出的维度。假设您有一个维度为[2,3]的输入矩阵 X ,矩阵中的一列代表一条记录。隐含层有 5 个隐含单元,所以权重矩阵 W 的维数为[5,2]。偏置 B 的尺寸为【5,1】。通过应用矩阵乘法,我们可以得到维数为[5,3]的输出矩阵 Z。计算的细节可以在上表中看到。

Table 3: How activation is applied in forward propagation

上表显示了激活函数是如何应用于 Z 的每个分量的。使用激活函数的原因是为了进行非线性变换。没有激活函数,无论模型有多少隐层,它仍然是一个线性模型。有几种流行且常用的激活函数,包括 ReLU、Leaky ReLU、sigmoid 和 tanh 函数。这些激活函数的公式和图形如下所示。

Figure 3: Activation Function

首先,你需要定义 sigmoid 和 ReLU 函数。然后为单层正向传播创建函数。最后,上一步中创建的函数被嵌套到名为完全正向传播的函数中。为简单起见,ReLU 函数用于前 N-1 个隐藏层,sigmoid 函数用于最后一个隐藏层(或输出层)。注意,在二分类问题的情况下,使用 sigmoid 函数;在多类分类问题的情况下,使用 softmax 函数。将每个隐藏层中计算的 ZA 保存到缓存中,用于反向传播。

这是测试数据的函数输出。

Table 4: Forward Propagation Testing Result

价值函数

正向传播的输出是二元事件的概率。然后将概率与响应变量进行比较,计算成本。在分类问题中使用交叉熵作为代价函数。在回归问题中,均方误差被用作成本函数。交叉熵的公式如下所示。

这是测试数据的函数输出。

Table 5: Cost Function Testing Result

反向传播

在训练期间,前向传播可以继续向前,直到它产生成本。反向传播是计算成本函数的导数,并使用微积分中的链式法则将所有信息返回到每一层。

假设

然后

链式法则指出

激活函数的导数如下所示。

类似于正向传播。首先,你需要为 sigmoid 和 ReLU 的导数创建一个函数。然后定义一个单层反向传播的函数,计算 dWdBdA_prevdA_prev 将被用作前一隐藏层反向传播的输入。最后,上一步中创建的函数被嵌套到名为完全反向传播的函数中。为了与正向传播对齐,前 N-1 个隐藏层使用 ReLU 函数,最后一个隐藏层或输出层使用 sigmoid 函数。你可以修改代码并添加更多的激活功能。将 dWdB 保存到另一个缓存中,用于更新参数。

这是测试数据的函数输出。

Table 6: Backward Propagation Testing Result

更新参数

一旦从反向传播计算出梯度,就通过学习速率*梯度来更新当前参数。然后,更新的参数被用于新一轮的正向传播。

这是测试数据的函数输出。

Table 7: Parameter Update Testing Result

梯度下降的解释可以在我的博客里看到。

[## 梯度下降导论

本博客将涵盖以下问题和主题:

towardsdatascience.com](/an-introduction-to-gradient-descent-c9cca5739307)

将功能堆叠在一起

为了训练神经网络模型,将前面步骤中创建的函数堆叠在一起。下表提供了功能摘要。

Table 8: Functions Summary

运行模式

首先使用 make_moons 函数创建两个交错的半圆数据。下面提供了数据的可视化。

Figure 4: Training Data

然后运行该函数来训练神经网络模型。下图显示了培训过程。成本在 8000 个时期后收敛,并且模型准确率收敛到 0.9。

Figure 5: Cost over Time

Figure 6: Accuracy over Time

下一步

图 5 和图 6 表明存在潜在的过拟合问题。您可以使用包括提前停止、退出和调整在内的方法来解决这个问题。除了 ReLU 和 sigmoid 函数之外,还可以通过添加其他激活函数来玩 model。本博客使用的是批量梯度下降,但也有很多改进的梯度下降算法如 Momentum、RMSprop、Adam 等。

摘要

虽然之前上过网上课程,读过书中的相关章节,但直到我亲自动手写博客,我才完全理解了这种奇特的方法。俗话说,教是最好的学习方法。希望你能从阅读这篇博客中受益。如果你有兴趣,请阅读我的其他博客。

[## 目录

这一系列博客将从理论和实现两个方面对深度学习进行介绍。

medium.com](https://medium.com/@songyangdetang_41589/table-of-contents-689c8af0c731)

参考

[1]特雷弗·哈斯蒂,罗伯特·蒂布拉尼,杰罗姆·弗里德曼,(2008),统计学习的要素

[2]伊恩·古德费勒,约舒阿·本吉奥,亚伦·库维尔,(2017) 深度学习

[3]https://www.coursera.org/specializations/deep-learning?

[4]https://en.wikipedia.org/wiki/Activation_function

[5]https://explained.ai/matrix-calculus/index.html

构建与购买——可扩展的机器学习基础设施

原文:https://towardsdatascience.com/build-vs-buy-a-scalable-machine-learning-infrastructure-88146cfcadb7?source=collection_archive---------10-----------------------

开源 ML 基础设施的路线图。

在这篇博客文章中,我们将看看机器学习平台由哪些部分组成,并将从头构建自己的基础设施与购买一个为您做所有事情的现成服务进行比较。

购买和构建基础架构的最终目标都是让您的数据科学团队尽可能多地花时间了解数据、构建模型并将其投入生产,从而提高他们的效率。相反,这意味着他们将在基础设施和样板代码上花费尽可能少的时间。为了实现这一点,大多数 ML 基础设施公司(如优步的米开朗基罗)都建立在以下原则之上:

  • 集成您的数据科学家已经喜欢使用的工具
  • 构建快速迭代实验的平台
  • 尽可能自动管理输入数据和输出数据
  • 通过提供自动审计跟踪,提供回滚实验的方法
  • 支持贵公司使用的环境,从不同的云提供商到内部硬件
  • 建立在抽象之上,这样你就可以在新技术出现时支持它们
  • 从团队的角度和资源的角度来设计可伸缩性,这样你就可以支持数百个项目、人员、数据源和计算单元。
  • 与任何地方的任何东西集成,因此平凡的任务都可以自动化。

记住这些原则,让我们看看如何通过从头开始构建来实现这一点。

构建可扩展的机器学习基础设施

有效的机器学习基础设施需要构建它的团队具备各种各样的能力。您将为您的数据科学团队解决特定需求,因此让我们来看看典型的机器学习基础架构由什么组成。

机器编排

机器编排是你的机器学习基础设施的主干。编排引擎在你的机器编排部分的 UI 中实现自己,无论是通过集成在 ide、笔记本、CLI、API 或 UI 中。最先进的引擎优化资源使用,预测需求,处理排队等。您可以将您的引擎基于虚拟机、Kubernetes 之类的平台,或者直接编排您自己的硬件。

记录保存、记录和版本控制

严格来说,版本控制并不是传统 ML 平台的一部分,但是由于 GDPR 等法规,版本控制变得越来越重要。为了让您的数据科学家只需点击一下鼠标就能重现实验,您需要对数据、参数、硬件和软件环境、日志等进行版本控制和记录。一个很好的起点是使用 Git 进行代码版本控制,使用 Docker 进行环境控制。在下面的图片中可以找到版本的起点。

项目和用户管理

您需要将实验纳入项目中,并管理用户和团队的权限。使用 Azure Active Directory 或 Hadoop Ranger 提供的访问控制,或者自己构建一些东西。访问控制还应该定义用户可以使用哪些资源、不同用户如何对作业进行排队、哪些数据对谁可用等等。您将希望消除孤岛并实现透明,以便您的数据科学家可以相互学习。

自动化 ML 管道

机器学习管道是一个概念,其中工作被分成不同的阶段,从探索开始,继续进行批处理、规范化、培训、部署和中间的许多其他步骤。您需要构建对将结果从一个步骤传递到下一个步骤的支持。当数据湖中的数据发生重大变化时,一些高级触发器可能会自动重新运行部分管道。从基于 Git 的低级方法到 Apache 气流和 ML 气流管道,有几种解决方案,您可能想要探索并组合在一起。

整合,整合,整合!

数据科学家应该能够继续使用他们喜欢的工具和框架,无论是他们最喜欢的 IDE、笔记本还是其他任何东西。根据具体情况,还应该有不同的与基础设施交互的方式。最佳实践是在开放 API 的基础上构建一切,然后可以从 CLI 和 web UI 访问,甚至直接从 Jupyter notebook 访问。对于超参数调优,您可能需要考虑外部优化器,比如 SigOpt 。

可视化正在进行的执行

当数据科学家使用模型时,他们需要了解各个步骤是如何进行的,无论是训练精度还是数据准备。为此,您需要构建一种以有意义的方式显示实时训练数据的方法。解决方案可以是定制的可视化库或例如 TensorBoard 或权重&偏差。

推理部署

ML 管道的最后一部分是部署。您可能不想在实际生产环境中进行部署,但要确保您的数据科学团队在为将预测模型集成到您的业务应用程序中的软件团队部署新模型时能够自给自足。由于开销和缓慢的模型开发周期,将模型交付给 IT 部门的旧方法从长远来看是行不通的。Kubernetes clusters 是业内事实上的云推理部署标准。也可以看看 AWS 的弹性推理,谢顿核心或 Azure 推理引擎与 ONNX 的支持。

购买可扩展的机器学习基础设施

不仅自己构建基础设施需要几年的时间(相信我,我们有历史可以证明这一点),而且随着技术的发展和新技术的支持,您还需要不断开发平台。

Valohai 是一个完整的可扩展的机器学习基础设施服务,可以为您的团队扩展,从 1 到 1000 名数据科学家。Valohai 的一切都是围绕项目和团队构建的,它可以从内部安装扩展到混合云和 Microsoft Azure、AWS 和 Google Cloud 中的全云解决方案。Valohai 为您处理机器编排,并自动跟踪您或您公司中的任何人曾经进行过的每个实验——从数据、代码、超参数、日志、软件环境和库、硬件等等。

Valohai 基于开放 API 构建,通过现成的 CLI、直观的 Web UI 和 Jupyter 笔记本集成,集成到您当前的工作流程中。借助自动化管道,您无需手动操作即可开始批量作业并运行培训流程的每一步。元数据的交互式可视化有助于您的团队了解其他人在做什么,并跟踪他们的模型,直到他们准备好将这些模型投入生产。

然而,有一点是肯定的,ML 基础设施是你的团队生产力中最重要的部分。在最好的情况下,它将减少多达 90%的模型开发时间。

最初发表于【blog.valohai.com】

在大型数据集上构建 XGBoost / LightGBM 模型——可能的解决方案是什么?

原文:https://towardsdatascience.com/build-xgboost-lightgbm-models-on-large-datasets-what-are-the-possible-solutions-bf882da2c27d?source=collection_archive---------4-----------------------

XGBoost 和 LightGBM 已经在许多表格数据集上被证明是性能最好的 ML 算法。但是当数据庞大时,我们如何使用它们呢?

Photo by Sean O. on Unsplash

XGBoost 和 LightGBM 在最近所有的 kaggle 表格数据竞争中占据了主导地位。只需进入任何竞赛页面(表格数据)并检查内核,你就会看到。在这些比赛中,数据并不“庞大”——好吧,不要告诉我你正在处理的数据是巨大的,如果它可以在你的笔记本电脑上训练的话。对于这些情况,Jupyter notebook 足够用于 XGBoost 和 LightGBM 模型构造。

当数据变得更大,但不是超级大,而你仍然想坚持使用 Jupyter 笔记本,比方说,来构建模型——一种方法是使用一些内存减少技巧(例如,ArjanGroen 的代码:https://www . ka ggle . com/arjanso/reducing-data frame-memory-size-by-65);或者使用云服务,比如在 AWS 上租一台 EC2。例如,r5.24xlarge 实例拥有 768 GiB 内存,成本为 6 美元/小时,我认为它已经可以处理大量数据,您的老板认为它们真的很“大”。

但是如果数据更大呢?

我们需要分布式机器学习工具。据我所知,如果我们想使用可伸缩的 XGBoost 或 LightGBM,我们有以下几种选择:

1 XGBoost 4j on Scala-Spark
2 light GBM on Spark(py Spark/Scala/R)
3 XGBoost with H2O . ai
4 XGBoost on Amazon SageMaker

我想根据我的个人经验指出每种工具的一些问题,如果您想使用它们,我会提供一些资源。如果你也有类似的问题/你是如何解决的,我也很高兴向你学习!请在下面评论。

在我开始使用这些工具之前,有一些事情需要事先了解。

XGBoost 与 LightGBM

XGBoost 是一种非常快速和准确的 ML 算法,但它现在受到了 LightGBM 的挑战,light GBM 运行得更快(对于某些数据集,根据其基准测试,它快了 10 倍),具有相当的模型准确性,并且有更多的超参数供用户调整。速度上的关键差异是因为 XGBoost 一次将树节点拆分一层,而 LightGBM 一次只拆分一个节点。

因此,XGBoost 开发人员后来改进了他们的算法,以赶上 LightGBM,允许用户也在逐叶分割模式下运行 XGBoost(grow _ policy = ' loss guide ')。现在,XGBoost 在这一改进下速度快了很多,但根据我在几个数据集上的测试,LightGBM 的速度仍然是 XGB 的 1.3-1.5 倍。(欢迎分享你的测试结果!)

读者可以根据自己的喜好选择任何一个选项。这里再补充一点:XGBoost 有一个 LightGBM 没有的特性——“单调约束”。这将牺牲一些模型精度并增加训练时间,但可以提高模型的可解释性。(参考:https://xgboost . readthe docs . io/en/latest/tutorials/monotonic . html和【https://github.com/dotnet/machinelearning/issues/1651】T2)

在渐变提升树中找到“甜蜜点”

对于随机森林算法,建立的树越多,模型的方差越小。但是在某种程度上,你不能通过添加更多的树来进一步改进这个模型。

XGBoost 和 LightGBM 不是这样工作的。当树的数量增加时,模型精度不断提高,但是在某个点之后,性能开始下降——过度拟合的标志;随着树越建越多,性能越差。

为了找到‘甜蜜点’,你可以做交叉验证或者简单的做训练-验证集分裂,然后利用提前停止时间找到它应该停止训练的地方;或者,你可以用不同数量的树(比如 50、100、200)建立几个模型,然后从中选出最好的一个。

如果你不在乎极端的性能,你可以设置一个更高的学习率,只构建 10-50 棵树(比如说)。它可能有点不合适,但你仍然有一个非常准确的模型,这样你可以节省时间找到最佳的树的数量。这种方法的另一个好处是模型更简单(构建的树更少)。

1.Scala-Spark 上的 XGBoost4j

如果读者打算选择这个选项,https://xgboost . readthedocs . io/en/latest/JVM/xgboost 4j _ spark _ tutorial . html是一个很好的起点。我想在这里指出几个问题(在本文发布时):

  1. XGBoost4j 不支持 Pyspark。
  2. XGBoost4j 不支持逐叶分割模式,因此速度较慢。https://github.com/dmlc/xgboost/issues/3724
  3. 因为是在 Spark 上,所以所有缺失值都要进行插补(vector assembler 不允许缺失值)。这可能会降低模型精度。http://docs . H2O . ai/H2O/latest-stable/H2O-docs/data-science/GBM-FAQ/missing _ values . html
  4. 早期停止可能仍然包含 bug。如果你关注他们在 https://github.com/dmlc/xgboost/releases的最新发布,你会发现他们最近仍在修复这些漏洞。

2.Spark 上的 light GBM(Scala/Python/R)

基于我个人经验的主要问题:

  1. 缺少文档和好的例子。https://github . com/Azure/mmspark/blob/master/docs/light GBM . MD
  2. 所有缺失的值都必须进行估算(类似于 XGBoost4j)
  3. 我在 spark cross validator 的“提前停止”参数上也有问题。(为了测试它是否正常工作,选择一个较小的数据集,选择一个非常大的回合数,提前停止= 10,并查看训练模型需要多长时间。训练完成后,将模型精度与使用 Python 构建的模型进行比较。如果过拟合得很差,很可能早期停止根本不起作用。)

一些示例代码(不包括矢量汇编程序):

from mmlspark import LightGBMClassifier
from pyspark.ml.evaluation import BinaryClassificationEvaluator
from pyspark.ml.tuning import CrossValidator, ParamGridBuilderlgb_estimator = LightGBMClassifier(learningRate=0.1, 
                                   numIterations=1000,
                                   earlyStoppingRound=10,
                                   labelCol="label")paramGrid = ParamGridBuilder().addGrid(lgb_estimator.numLeaves, [30, 50]).build()eval = BinaryClassificationEvaluator(labelCol="label",metricName="areaUnderROC")crossval = CrossValidator(estimator=lgb_estimator,
                          estimatorParamMaps=paramGrid, 
                          evaluator=eval, 
                          numFolds=3) cvModel  = crossval.fit(train_df[["features", "label"]])

3.H2O.ai 上的 XGBoost

这是我个人最喜欢的解决方案。该模型可以使用 H2O.ai 构建,集成在 py sparking Water(H2O . ai+py spark)管道中:
https://www . slide share . net/0x data/productionizing-H2O-models-using-sparking-Water-by-jakub-hava

很容易建立一个带有交叉验证的优化轮数的模型

# binary classificationfeatures = ['A', 'B', 'C']
train['label'] = train['label'].asfactor() # train is an H2O framecv_xgb = H2OXGBoostEstimator(
    ntrees = 1000,
    learn_rate = 0.1,
    max_leaves = 50,
    stopping_rounds = 10,
    stopping_metric = "AUC",
    score_tree_interval = 1,
    tree_method="hist",
    grow_policy="lossguide",
    nfolds=5, 
    seed=0)cv_xgb.train(x = features, y = 'label', training_frame = train)

并且 XGBoost 模型可以用cv_xgb.save_mojo()保存在 Python 中使用。如果您想以 h2o 格式保存模型,请使用h2o.save_model()

我唯一的抱怨是保存的模型(用save.mojo保存的那个)不能和 SHAP 包一起使用来生成 SHAP 特性重要性(但是 XGBoost 特性重要性,.get_fscore(),工作正常)。似乎最初的 XGBoost 包有一些问题。
https://github.com/slundberg/shap/issues/464
https://github.com/dmlc/xgboost/issues/4276

(更新:似乎他们刚刚在其最新版本中实现了 SHAP-https://github . com/h2oai/H2O-3/blob/373 ca 6 B1 BC 7d 194 C6 c 70 e 1070 F2 F6 f 416 f 56 B3 d 0/changes . MD)

4.SageMaker 上的 XGBoost

这是 AWS 的一个非常新的解决方案。两个主要特性是使用贝叶斯优化的自动超参数调整,并且该模型可以作为端点部署。在他们的 Github 上可以找到几个例子:https://Github . com/aw slabs/Amazon-sage maker-examples/tree/master/introduction _ to _ applying _ machine _ learning。以下是我对此的一些担忧:

  1. 与其他解决方案相比,参数调整工具对用户(数据科学家)不太友好:
    https://github . com/aw slats/Amazon-sagemaker-examples/blob/master/hyperparameter _ tuning/xgboost _ direct _ marketing/hpo _ xgboost _ direct _ marketing _ sagemaker _ APIs . ipynb和
    https://github . com/aw slats/Amazon-sagemaker-examples/blob/master/hyperparameter _ tuning/Analyze _ results/HPO
  2. 贝叶斯优化是否是调优 XGB 参数的最佳选择还是未知数。如果你检查了文件,梯度推进树没有被提及/测试。https://docs . AWS . Amazon . com/sage maker/latest/DG/automatic-model-tuning-how-it-works . html
  3. 该参数通过单个验证集进行调整,而不是交叉验证。
  4. 我还没想好如何在 Python XGBoost 中使用其内置的 XGBoost 算法训练出来的模型神器。

但是除了这些问题,我们仍然可以利用它的端点特性。你可以在任何地方训练你的 XGB 模型,从 Amazon ECR(Elastic Container Registry)把它放在 XGBoost 映像中,然后把它部署成一个端点。


XGBoost / LightGBM 是相当新的 ML 工具,它们都有潜力变得更强。开发人员已经做了出色的工作,创造了这些工具,使人们的生活变得更容易。我在这里指出我的一些观察和分享我的经验,希望它们能成为更好、更易用的工具。

建立你的第一个计算机视觉项目——狗的品种分类

原文:https://towardsdatascience.com/build-your-first-computer-vision-project-dog-breed-classification-a622d8fc691e?source=collection_archive---------10-----------------------

在不到 30 分钟的时间内开始构建您的第一个计算机视觉项目。

Photo by Joe Caione on Unsplash

对我们人类来说,区分不同品种的狗是很容易的。如果你说的是 10-20 种受欢迎的狗品种。当我们谈论 100 多种狗时,情况就完全不同了。对于一个人来说,要正确并一致地对大量品种进行分类,我们需要一种不同于单纯记忆的方法。我们需要开始提取与不同品种相对应的“特征”,如皮毛颜色、耳朵形状、面部形状、尾巴长度等。即使这样,我们也需要记住什么品种有什么特征,这不是一件容易或有趣的任务。

Image credit: It is hard to classify a large number of dog breeds

人类难以识别大量物种是计算机比我们更好的一个完美例子。这并不是说天网要来杀我们所有人;我认为我们现在还不应该为此担心。许多人认为,两个种族一起工作的混合方式比任何一个种族单独工作都要好,而不是用机器来代替人类。你可以在 Andrew McAfee 所著的《机器、平台、人群》一书中了解更多相关信息。

在这个项目中,我将带你经历建立和训练卷积神经网络(CNN)的步骤,该网络将对 133 种不同的狗进行分类。这个项目是我的数据科学家 Nanodegree 的一部分,你可以在这里找到更多关于它的信息。这个项目的代码可在我的 GitHub repo 上获得,如果你想跟着做,请确保你安装了所需的包。

你需要知道的是

计算机如何理解图像

由于数百万年的进化完善了我们的眼睛和大脑的视觉能力,我们非常擅长识别事物。计算机将图像“视为”一系列数字。屏幕上的一个像素由从 0 到 255 的三个数字表示。每个数字都表示为红色、绿色和蓝色的强度。一张 1024768 的图片可以表示为一个 1024768*3 的矩阵,相当于一系列 239,616 个数字。

Image credit: Image represented as numbers

传统上,计算机很难识别或分类图像中的对象。随着计算能力和神经网络研究的最新进展,计算机现在可以在许多特定任务上具有人类级别的视觉能力。

什么是神经网络

有许多很好的资源可以解释什么是神经网络。下面是我最喜欢的解释,借用这个帖子:

神经网络由以下组件组成

  • 一个输入层T3、x
  • 任意数量的隐藏层
  • 一个输出层ŷ
  • 一组权重在各层之间偏向W 和 b
  • 一个选择激活功能用于每个隐藏层,。在本教程中,我们将使用一个 Sigmoid 激活函数。

下图显示了 2 层神经网络的架构(注意,在计算神经网络的层数时,输入层通常被排除在外**

Image credit: A simplified neural network architecture

详细解释神经网络如何工作超出了这篇文章的范围。你可以在 Medium 上找到许多解释神经网络如何工作的优秀文章。对于我们的狗分类项目,我们的输入层将是用数字表示的狗图像。隐藏层将是许多具有权重和偏差的层,这些权重和偏差将在我们训练模型时更新。输出将是我们 133 个犬种的一系列概率。

虽然对于各种机器学习问题有不同的神经网络架构,但是卷积神经网络(CNN)是用于图像分类问题的最流行的一种。

什么是卷积神经网络

CNN 是通常用于图像分类问题的神经网络的架构。典型的架构包括一个输入层、几个卷积层、一个池层、一个全连接层(FC)和一个输出层。你可以在这里和这里阅读 CNN 更全面的解释。

Image credit: Architecture of a CNN

Image credit: Visualization of a convolutional layer

什么是迁移学习

迁移学习利用解决一个问题时获得的知识,并将其应用于另一个不同但相关的问题。

现代卷积网络是在 ImageNet 这样的大型数据集上训练的,需要数周才能完成。所以很多时候,你并不想自己去训练一个全新的 ConvNet。

有很多著名的预训练模型,比如 VGG-16,雷斯网-50,Inception,Xception 等。这些模型已经在大型数据集上进行了训练,您可以利用特征提取步骤,并将它们作为特征提取器应用于您的特定问题。你可以在这里阅读更多关于迁移学习的内容。

Image credit: Visualization of transfer learning architecture

准备数据

获取数据

你可以在这里下载数据集。该数据集包含 8,351 张狗图像,133 个狗品种,分为 6,680 张用于训练的图像,836 张用于测试的图像,以及 835 张用于验证的图像。

注意,如果你有一个 NVIDIA GPU,你应该遵循这个教程。关于 CPU 的培训需要很多时间。或者,你可以在这里免费使用谷歌实验室。

可以按如下方式加载标签和文件路径的键值对字典:

***from** **sklearn.datasets** **import** load_files       
**from** **keras.utils** **import** np_utils
**import** **numpy** **as** **np**
**from** **glob** **import** glob*# define function to load train, test, and validation datasets*
**def** load_dataset(path):
    data = load_files(path)
    dog_files = np.array(data['filenames'])
    dog_targets = np_utils.to_categorical(np.array(data['target']), 133)
    **return** dog_files, dog_targets*# load train, test, and validation datasets*
train_files, train_targets = load_dataset('dogImages/train')
valid_files, valid_targets = load_dataset('dogImages/valid')
test_files, test_targets = load_dataset('dogImages/test')*

不同的图像可能有不同的大小。以下代码将输入调整为 224224 像素的图像,并将其作为 numpy 系列加载到内存中:*

***from** **keras.preprocessing** **import** image                  
**from** **tqdm** **import** tqdm**def** path_to_tensor(img_path):
    *# loads RGB image as PIL.Image.Image type*
    img = image.load_img(img_path, target_size=(224, 224))
    *# convert PIL.Image.Image type to 3D tensor with shape (224, 224, 3)*
    x = image.img_to_array(img)
    *# convert 3D tensor to 4D tensor with shape (1, 224, 224, 3) and return 4D tensor*
    **return** np.expand_dims(x, axis=0)**def** paths_to_tensor(img_paths):
    list_of_tensors = [path_to_tensor(img_path) **for** img_path **in** tqdm(img_paths)]
    **return** np.vstack(list_of_tensors)*

预处理数据

我们需要标准化我们的数据,以消除测量单位。归一化可以帮助我们的模型更好地比较不同尺度的数据。

***from** **PIL** **import** ImageFile                            
ImageFile.LOAD_TRUNCATED_IMAGES = **True***# pre-process the data for Keras*
train_tensors = paths_to_tensor(train_files).astype('float32')/255
valid_tensors = paths_to_tensor(valid_files).astype('float32')/255
test_tensors = paths_to_tensor(test_files).astype('float32')/255*

培训用数据

**

It’s challenging even for humans to identify a dog breed

让我们来看看我们的一些数据。我们的例子表明,准确识别狗的品种是具有挑战性的,即使对人类来说也是如此。还有许多其他现实世界的因素会影响我们的模型:

  • 灯光条件:不同的灯光改变颜色的显示方式
  • 目标定位:我们的狗可以帮助很多不同的姿势
  • 相框:特写人像相框和全身照片非常不同
  • 缺失特征:照片中并没有显示狗狗的所有特征

因此,传统方法不可能在计算机视觉任务中取得进展。让我们看看 CNN 和转移学习能做些什么。

创建一个你自己的模型

模型架构

用 Keras 创建 CNN 很简单。您可以用下面的代码定义您的模型架构。

***from** **keras.layers** **import** Conv2D, MaxPooling2D, GlobalAveragePooling2D
**from** **keras.layers** **import** Dropout, Flatten, Dense
**from** **keras.models** **import** Sequentialmodel = Sequential()
model.add(Conv2D(filters=16, kernel_size=2, padding='same',activation='relu',input_shape=(224,224,3)))
model.add(MaxPooling2D())
model.add(Conv2D(filters=32, kernel_size=2, padding='same',activation='relu'))
model.add(MaxPooling2D())
model.add(Conv2D(filters=64, kernel_size=2, padding='same',activation='relu'))
model.add(MaxPooling2D())
model.add(GlobalAveragePooling2D())
model.add(Dense(133,activation='softmax'))model.summary()*

我们的原始模型架构有一个形状为 2242243 的输入层。然后,输入馈入两个卷积层,接着是最大池层(用于下采样)。然后,输出被馈送到全局平均池层,以通过减少参数的数量来最小化过拟合。输出层为我们的品种返回 133 个概率。

Image credit: Visualization of pooling layers such as MaxPooling and AveragePooling

Our primitive model summary

韵律学

在这个项目中,我们的目标是正确预测狗的品种。因此,在每次迭代之后,我们使用准确性作为选择的度量来改进我们的模型。准确性就是我们的模型正确预测犬种的次数。

编译和训练模型

然后需要编译我们的模型。我们使用 rmsprop 作为我们的优化器,category _ cross entropy,作为我们的损失函数,准确性作为我们的度量。

*model.compile(optimizer='rmsprop', loss='categorical_crossentropy', metrics=['accuracy'])*

然后,我们可以使用以下代码训练模型:

***from** **keras.callbacks** **import** ModelCheckpointepochs = 5checkpointer = ModelCheckpoint(filepath='saved_models/weights.best.from_scratch.hdf5', 
                               verbose=1, save_best_only=**True**)model.fit(train_tensors, train_targets, 
          validation_data=(valid_tensors, valid_targets),
          epochs=epochs, batch_size=20, callbacks=[checkpointer], verbose=1)*

五个纪元和大约八分钟,我们取得了 3.2297% 的准确率,比随机猜测(1/133 = 0.75%)要好。

虽然我们的模型比随机猜测的表现好得多,但 3.2297%的模型在现实中没有用。训练和调整我们的模型以达到有意义的准确性需要大量的时间、大量的数据集和大量的计算资源。幸运的是,我们可以使用迁移学习来减少训练时间,而不牺牲准确性。

使用迁移学习创建模型

获取瓶颈特征

Keras 提供了以下经过预先培训的最新架构,您可以在几分钟内使用它们:VGG-19、ResNet-50、Inception 和 Xception。在这个项目中,我们将使用 ResNet-50,但是您可以自己尝试其他体系结构。下面的代码下载预先训练好的模型并加载瓶颈特性。

***import** **requests**
url = 'https://s3-us-west-1.amazonaws.com/udacity-aind/dog-project/DogResnet50Data.npz'
r = requests.get(url)**with** open('bottleneck_features/DogResnet50Data.npz', 'wb') **as** f:
    f.write(r.content)bottleneck_features = np.load('bottleneck_features/DogResnet50Data.npz')
train_Resnet50 = bottleneck_features['train']
valid_Resnet50 = bottleneck_features['valid']
test_Resnet50 = bottleneck_features['test']*

模型架构

借助预训练的 ResNet-50 模型,我们使用以下代码创建全连接(FC)层。我们还增加了两个下降层,以防止过度拟合。

*Resnet50_model = Sequential()
Resnet50_model.add(GlobalAveragePooling2D(input_shape=train_Resnet50.shape[1:]))
Resnet50_model.add(Dropout(0.3))
Resnet50_model.add(Dense(1024,activation='relu'))
Resnet50_model.add(Dropout(0.4))
Resnet50_model.add(Dense(133, activation='softmax'))
Resnet50_model.summary()*

Our model architecture using transfer learning looks like this

模型性能

使用迁移学习(ResNet-50),在二十个历元不到两分钟的情况下,我们取得了 80.8612% 的测试准确率。这是对我们原始模型的巨大改进。

使用上面描述的模型,我创建了一个小的命令行应用程序。如果你输入一只狗的照片,这个应用程序会预测它的品种。如果你输入一个人的图像,比如你的一个朋友说,这个应用程序会预测最接近的匹配犬种。如果没有狗或人出现,应用程序将输出一个错误。你可以按照我的 GitHub repo 上的 README.md 文件中的说明进行操作。

结论

在这个项目中,我将带你了解一些构建你的第一个计算机视觉项目所需的核心概念。我解释了计算机如何理解图像,简要回顾了什么是神经网络卷积神经网络迁移学习

我们还从零开始构建并训练了一个 CNN 架构以及应用迁移学习来大幅提高我们模型的准确率从 3.2%到 81%。在做这个项目时,我对你如何利用迁移学习来提高准确性,同时显著减少培训时间印象深刻。以下是我们模型的一些预测:

Some predictions of our model

未来的改进

如果我有更多的时间,这里是这个项目的几个未来发展:

  • 扩充数据:当前的实现只接受标准图像作为输入。因此,该模型在现实环境中可能不够健壮。我可以增加训练数据,使我们的模型不容易过度拟合。我可以通过随机裁剪、翻转、旋转训练数据来创建更多的训练样本。
  • 调优模型:我还可以调优模型超参数,以达到更好的精度。一些用于调优的潜在超参数包括优化器、损失函数、激活函数、模型架构等。我也可以用 Keras 使用 Sklearn 的 GridSearch。
  • 尝试其他模型:在这个项目中,我尝试在 VGG-16 和 ResNet-50 上训练模型。下一步可能是尝试使用不同的架构来训练模型,以及改变我们完全连接的层的架构。
  • 创建一个 web/移动应用程序:我也可以用这个模型创建一个 web 应用程序或移动应用程序来取乐。

感谢你的阅读,希望你能学到东西:)

建立您的第一个时差数据警报

原文:https://towardsdatascience.com/build-your-first-data-alert-on-slack-e0768621e0fa?source=collection_archive---------30-----------------------

动手黑客

不仅要注重实践,还要更深入地融入你的工作流程

这个提醒极大地提高了我的工作效率,让我远离了多余的询问和工作,所以这篇文章不仅关注实践,也解释了我是如何将它融入到工作中的。

为了更容易使用,本文中的所有内容都已经打包到这个 Github 库中。

data alert 如何提高我的工作效率?

🔕对多余的询问保持沉默

你对利益相关者的提问感到很烦,他们也是。如果数据能够自己出现并解释这种情况,每个人都会很感激。

🏋️‍♂️立即追踪异常信号

异常信号出现时跟踪它比一个月后复查要容易得多。不要害怕转发信息并给你的同事/老板贴标签!

👨‍👧‍👧让您的团队充分参与进来!

一旦您注意到后端工程师和首席执行官正在讨论是什么原因导致警报发出异常信号,并为您标记营销经理…

你离成功很近了。

什么让数据预警清晰?

色彩管理

颜色是表明当前状态的最佳方式。红色代表警戒,黄色代表警告,蓝色代表沮丧…

Which style draws more attention with clarity?

情节很重要

一张图胜过千言万语,尤其对于没有统计学背景的同事来说,还是能体会到形势有多严峻的。

Don’t mess it up!

易接近

人类是最懒的动物,所以你的警报应该整合到原来的工作场所,比如 slack,而不是外部的网站或者仪表盘。

编码前:松弛应用设置

  1. 登录您的 slack 帐户。
  2. 在这里创建一个 Slack App 。
  3. 允许应用程序通过备用渠道上传文件(例如#图片存储)
  4. 允许在通道通知中发送传入的 webhook(例如#data-notify)
  5. 上传一个有趣的图标到你的 Slack 应用程序🤪

Make sure you have the same setting.

让我们编码

1。确保您的数据和绘图准备就绪

2.将绘图上传到时差

这是最有趣的(hack)部分。这个想法是:

  1. 将您的绘图保存为临时文件
  2. 将你的剧情发布到另一个 slack 频道,获得只能在 slack 上分享的私人网址!
  3. 重新分享到#general 或您喜欢的任何频道

3.🚥颜色+📊Plot +📝描述=📩完成的

你做到了,检查你的松弛频道!

如果没有,在这里处理您的错误代码。

替代方案:使用我开发的库

您可以通过克隆slacleoneir来完成数据预警,无需额外编码。

另外:用气流来安排你的剧本

Cronjob 简单而有用,但我个人建议用气流作为更好的替代品。

构建您的第一个开源 Python 项目

原文:https://towardsdatascience.com/build-your-first-open-source-python-project-53471c9942a7?source=collection_archive---------1-----------------------

工作包的逐步指南

每个软件开发人员和数据科学家都应该经历制作软件包的练习。一路上你会学到很多东西。确保你有时间。🕰

制作一个开源 Python 包可能听起来令人生畏,但是你不需要成为一个头发斑白的老手。你也不需要一个精心制作的产品创意。你确实需要坚持和时间。希望这个指南能帮助你减少这两者的需求。😃

Build something beautiful 😃

在本文中,我们将通过每个步骤来制作一个基本的 Python 包。在以后的文章中,我们将建立自动化测试和文档构建。我们还将集成其他有用的应用程序来改进您的包开发。然后,您可以扩展您构建的内容以满足您的需求。

如果你想学习 Python,看看我的令人难忘的 Python 书籍。

本指南适用于使用 Python 3.7 的 macOS。现在一切正常,但事情变化很快,所以不能保证你在 2030 年读到这篇文章。在下面的代码中替换我的 _ 包我的 _ 文件、等。用你们自己的名字。

我们开始吧!🚀

第一步:制定计划

我们最终计划在 Python 程序中使用一个非常简单的库。该软件包将允许用户轻松地将 Jupyter 笔记本转换成 HTML 文件或 Python 脚本。

我们的包的第一次迭代将允许用户调用打印语句的函数。

现在我们知道了我们想要做什么,我们需要决定如何命名这个包。

第二步:命名

给事物命名是很棘手的。名字应该是独一无二的,简短的,易记的。它们也应该全部小写,并且绝对不能有任何破折号或其他标点符号。不鼓励使用下划线。当你在构建你的包时,检查这个名字在 GitHub、Google 和 PyPI 上是否存在。

如果你非常希望你的包裹有一天会有 10,000 颗 GitHub 星,那么你可能需要检查这个名字在社交网络上是否可用。在这个例子中,我将把我的包命名为 notebookc ,因为它是可用的、简短的、半描述性的。😄

步骤 3:配置环境

确保安装并配置了 Python 3.7、GitHub 和 Homebrew。如果你需要这些,这里有详细信息:

计算机编程语言

在这里下载 Python 3.7 并安装。

开源代码库

如果你没有 GitHub 账户,请点击这里注册一个免费账户。点击查看如何安装和配置 Git 。你需要命令行工具。按照链接下载、安装它,设置您的用户名,并设置您的提交电子邮件地址。

公司自产自用

家酿是一个 Mac 专用的软件包管理器。这里的安装说明是。

Venv

从 Python 3.6 开始,推荐使用 venv 来创建你的虚拟环境进行包开发。使用 Python 管理虚拟环境有多种方法,并且这些建议也在不断发展。见此处的讨论,但小心掉进这个兔子洞。🕳

venv 从 Python 3.3 开始随 Python 一起安装。请注意,从 Python 3.4 开始, venvpipsetuptools 安装到虚拟环境中。

使用以下命令创建 Python 3.7 虚拟环境:

python3.7 -m venv my_env

用你喜欢的任何名字替换 my_env 。像这样激活您的虚拟环境:

source my_env/bin/activate

现在,您应该会在终端提示符的最左边看到(my_env)——或者您给虚拟环境起的名字。

当您完成开发后,使用deactivate停用您的虚拟环境。

现在让我们在 GitHub 上设置一些东西。

步骤 4:在 GitHub 上创建组织

GitHub 是版本控制注册中心的市场领导者。GitLab 和 Bitbucket 是其他流行的选项。我们将在本指南中使用 GitHub。

  • 你会经常用到 Git 和 GitHub,所以如果你不熟悉,可以看看我的文章这里。
  • 在 Github 上创建一个新组织。按照提示操作。我将我的组织命名为 notebooktoall。你可以在自己的个人账户下创建回购,但部分目标是学习如何为更大的社区建立一个开源项目。

步骤 5:设置 GitHub Repo

创建新的存储库。我把我的回购记录命名为 c。

从下拉列表中添加一个. gitignore 。为您的存储库选择 Python 。你的的内容。gitignore 文件将匹配要从 Git repo 中排除的文件夹和文件类型。你可以改变你的。gitignore 稍后排除其他不必要的或敏感的文件。

我建议你从添加许可证下拉框中选择一个许可证。许可证定义了存储库内容的用户可以做什么。有些许可证比其他的更宽松。如果没有选择许可证,则默认版权法适用。点击了解更多关于许可证的信息。

对于这个项目,我选择了 GNU 通用公共许可证 v3.0,因为它是流行的、合法的,并且“保证最终用户自由运行、学习、共享和修改软件”——来源。

步骤 6:克隆和添加目录

选择要在本地克隆存储库的位置,并运行以下命令:

git clone [https://github.com/notebooktoall/notebookc.git](https://github.com/notebooktoall/notebook_convert.git)

替代您的组织和回购。

使用桌面 GUI 或代码编辑器进入项目文件夹。或者使用带有cd my-project的命令行,然后用ls —a查看你的文件。您的初始文件夹和文件应该如下所示:

.git
.gitignore 
LICENSE
README.rst

为您的主项目文件创建一个子文件夹。我建议您将这个主子文件夹命名为与您的包相同的名称。确保名称中没有任何空格。🙂

创建一个名为 init 的文件。py 在您的主子文件夹中。该文件暂时将保持为空。要导入此文件夹中的文件,此文件是必需的。

创建另一个与主子文件夹同名的文件。py 追加到它后面。我的文件命名为 notebookc.py 。您可以随意命名这个 Python 文件。您的软件包用户在导入您的模块时将引用该文件的名称。

我的 notebookc 目录内容现在看起来是这样的:

.git
.gitignore 
LICENSE
README.rstnotebookc/__init__.py
notebookc/notebookc.py

步骤 7:创建并安装 requirements_dev.txt

在项目目录的顶层,创建一个 requirements_dev.txt 文件。通常这个文件被命名为 requirements.txt 。称之为 requirements_dev.txt 突出了这些包只由项目开发人员安装。

在 requirements_dev.txt 中,指定需要安装 pip 和轮。

pip==19.0.3
wheel==0.33.1

请注意,我们用双等号和完整的 major.minor.micro 版本号来指定这些包的确切版本。如果你将来读到这篇文章,你会希望使用更新的版本。检查https://pypi.org/以查看更新的软件包版本。

Pin your package versions in requirements_dev.txt

派生项目 repo 并使用 pip 安装钉住的 requirements_dev.txt 包的合作者将拥有与您相同的包版本。你知道他们会为他们工作。此外,阅读文档将使用这个文件来安装软件包时,它建立您的文档。

在您激活的虚拟环境中,使用以下命令安装 requirements_dev.txt 中的包:

pip install -r requirements_dev.txt

随着新版本的发布,您将希望保持这些包的更新。现在,你可以通过搜索 PyPI 来安装最新的版本。

我们将在以后的文章中安装一个工具来帮助完成这个过程。跟着我确保你不会错过它。

步骤 8:编码并提交

出于演示目的,让我们创建一个基本函数。您可以稍后创建自己的牛逼函数。👍

在主文件中输入以下内容(对我来说就是notebook c/notebook c/notebook c . py):

这是我们最重要的职能。😃

文档字符串以三个连续的双引号开始和结束。它们将在后面的文章中用于自动创建文档。

让我们提交我们的更改。如果你想复习 Git 工作流,请参阅本文。

第 9 步:创建 setup.py

setup.py 文件是您的包的构建脚本。Setuptools 的 setup 函数将构建您的包,并上传到 PyPI。Setuptools 包括关于您的软件包、您的版本号以及用户需要哪些其他软件包的信息。

以下是我的示例 setup.py 文件:

注意 long_description 被设置为 README.md 文件的内容。

setup tools . setup . install _ requires中指定的需求列表包含了您的软件包工作所需的所有软件包依赖项。

与 requirements_dev.txt 中开发所需的包列表不同,这个包列表应该尽可能宽松。点击阅读更多关于为什么的信息。

将这个 install_requires 包列表限制为只需要的——你不想让用户安装不必要的包。注意,您只需要列出不属于 Python 标准库的包。如果您的用户将使用您的包,他们将安装 Python。😄

我们的包不需要任何外部依赖,所以您可以排除上面例子中列出的四个包。

派生项目 repo 并使用 pip 安装固定包的合作者将拥有与您所使用的相同的包版本。这意味着事情应该工作。🤞

更改其他 setuptools 信息以匹配您的软件包信息。还有许多其他可选的关键字参数和分类器——参见这里的列表。更深入的 setup.py 指南可以在这里和这里找到。

将代码提交给本地 Git repo。让我们准备构建一个包吧!

步骤 10:构建第一个版本

Twine 是一个实用程序集合,用于在 PyPI 上安全地发布 Python 包。将捆绳包添加到 requirements_dev.txt 的下一个空行,如下所示:

twine==1.13.0

然后通过重新安装 requirements_dev.txt 包将 Twine 安装到您的虚拟环境中。

pip install -r requirements_dev.txt

然后运行以下命令创建您的包文件:

python setup.py sdist bdist_wheel

应该创建多个隐藏文件夹: distbuild 和——在我的例子中——notebookc . egg-info。让我们看看文件夹中的文件。的。whl 文件是车轮文件,即构建的分布。. tar.gz 文件是一个源归档文件。

Wheel

在用户的机器上,只要有可能,pip 就会把包安装成轮子。车轮安装更快。当 pip 不能安装一个轮子时,它就依靠源档案。

让我们准备上传我们的车轮和源档案。

步骤 11:创建 TestPyPI 帐户

PyPI 代表 Python 包索引。它是官方的 Python 包管理器。当库还没有安装在本地时,pip 从 PyPI 中获取库。

PyPI

TestPyPI 是 PyPI 的功能测试版本。在这里创建一个 TestPyPI 账户并确认你的电子邮件地址。请注意,上传到测试网站和官方网站时,您将有单独的密码。

步骤 12:发布到 TestPyPI

Twine

使用 Twine 将您的包安全地发布到 TestPyPI。输入以下命令—无需修改。

twine upload --repository-url [https://test.pypi.org/legacy/](https://test.pypi.org/legacy/) dist/*

系统会提示您输入用户名和密码。记住,TestPyPI 和 PyPI 有不同的密码!

如果需要,修复任何错误,在 setup.py 中创建一个新的版本号,并删除旧的构建工件: builddistegg 文件夹。用python setup.py sdist bdist_wheel重建并用 Twine 重新上传。TestPyPI 上的版本号没有任何意义,这没什么大不了的——您是唯一会使用这些包版本的人。😄

成功上传软件包后,让我们确保您可以安装和使用它。

步骤 13:验证安装并使用它

在您的终端 shell 中创建另一个选项卡,并创建另一个虚拟环境。

python3.7 -m venv my_env

激活它。

source my_env/bin/activate

如果你已经把你的包上传到官方的 PyPI 网站,你就会pip install your-package。我们可以从 TestPypPI 中检索这个包,并用修改后的命令安装它。

下面是从 TestPyPI 安装软件包的官方说明:

您可以通过指定— index-url 标志来告诉 pip 从 TestPyPI 而不是 PyPI 下载包

pip install --index-url [https://test.pypi.org/simple/](https://test.pypi.org/simple/) my_package

如果您想让 pip 也从 PyPI 获取其他包,您可以指定——extra-index-URL 来指向 PyPI。当您测试的包有依赖项时,这很有用:

pip install --index-url [https://test.pypi.org/simple/](https://test.pypi.org/simple/) --extra-index-url [https://pypi.org/simple](https://pypi.org/simple) my_package

如果您的包有依赖项,使用第二个命令并替换您的包名。

您应该会看到虚拟环境中安装了最新版本的软件包。

要验证您可以使用您的软件包,请在您的终端中使用以下命令启动 IPython 会话:

python

导入函数并用字符串参数调用函数。我的代码如下所示:

from notebookc.notebookc import convert

convert(“Jeff”)

然后我会看到以下输出:

I’ll convert a notebook for you some day, Jeff.

你当然会。😉

步骤 14:推送至 PyPI

将你的代码推送到真正的 PyPI 站点,这样人们可以在他们pip install my_package的时候下载它。

这是你上传的代码。

twine upload dist/*

请注意,如果您想将新版本推送到 PyPI,需要在 setup.py 中更新您的版本号。

好吧,让我们把我们的作品上传到 GitHub。

第 15 步:推送至 GitHub

确保您提交了代码。

我的记事本 c 项目文件夹看起来是这样的:

.git
.gitignore
LICENSE
README.md
requirements_dev.txt
setup.pynotebookc/__init__.py
notebookc/notebookc.py

排除任何不想上传的虚拟环境。巨蟒。我们在进行回购时选择的 gitignore 文件应该防止构建工件被索引。您可能需要删除虚拟环境文件夹。

git push origin my_branch推你当地的分支到 GitHub。

步骤 16:创建并合并采购申请

从浏览器中,导航到 GitHub。您应该会看到一个发出拉取请求的选项。继续按绿色按钮创建和合并您的 PR,并删除您的远程功能分支。

回到你的终端,用git branch -d my_feature_branch删除你的本地特征分支。

步骤 17:在 GitHub 上更新发布版本

通过点击资源库主页上的发布,在 GitHub 上创建一个新的发布版本。输入发布信息并保存。

那就暂时好了!🎉

我们将在以后的文章中添加更多的文件和文件夹。

让我们回顾一下我们的步骤。

总结:工作包的 17 个步骤

  1. 想办法
  2. 将其命名
  3. 配置环境
  4. 在 Github 上创建组织
  5. 设置 GitHub Repo
  6. 克隆和添加目录
  7. 创建并安装 requirements_dev.txt
  8. 编码并提交
  9. 创建 setup.py
  10. 构建第一个版本
  11. 创建 TestPyPI 帐户
  12. 推送至 TestPyPI
  13. 验证安装和使用
  14. 推送至 PyPI
  15. 推送至 GitHub
  16. 创建并合并请购单
  17. 在 GitHub 上更新发布版本

包装

我希望这篇关于制作和发布你的第一个 Python 包的指南对你有用。如果你有,请分享到你最喜欢的社交媒体渠道,这样其他人也可以找到它。👏

在本系列的下一部分,我们将添加测试、持续集成、代码覆盖等等。请点击这里查看:

[## 成功设置 Python 项目的 10 个步骤

如何添加测试、CI、代码覆盖率等等

towardsdatascience.com](/10-steps-to-set-up-your-python-project-for-success-14ff88b5d13)

我撰写关于 Python、Docker、数据科学和其他技术主题的文章。如果你对此感兴趣,请在这里阅读更多和关注我。😄

快乐大厦!

在创建一个好的回归模型时建立你的直觉

原文:https://towardsdatascience.com/build-your-intuition-in-creating-a-good-regression-model-90fd5aa97058?source=collection_archive---------29-----------------------

你可以通过分类来辨别那是猫还是不是猫。但是,要回答那只猫有多“猫”,回归是唯一的方法。

Photo by Erik-Jan Leusink on Unsplash

分类与回归

两个大问题,两种不同的原因。

热狗还是不是热狗,猫还是不是猫,诈骗还是不是诈骗。这些问题是分类的常见用例。

通过查看示例,您可以很容易地得出结论:分类问题就是将一些数据集划分为几个类或组。从某种意义上说,组的数量是有限的。

正如您所猜测的,另一方面,回归是将这些数据分成无限组。

例如,拥有一组关于房屋面积的数据。通过查看数据,你可以预测每套房子的价格。因为价格几乎是无限的,所以你可以根据这些数据建立一个回归模型。

那么,如何开始创建回归模型呢?

定义问题

用例子做任何事情总是更好,因为理论不会让你走那么远。

我们来发现一些问题。

我发现了这个:

纽约市出租车费用预测

这是一个很好的开始问题,可以训练你建立良好回归模型的直觉。

基本上,你会得到一堆关于在纽约从一个地方到另一个地方需要付多少钱的数据。根据这些数据,你需要预测你需要支付多少打车费用。

他们在问题中陈述的指标是,在不做所有必要的机器学习事情的情况下,你可以得到 5 到 8 美元的 RMSE(均方根误差)。所以,让我们建立一个能给你更少误差的东西。

开始吧!

数据

下载数据并打开 train.csv(是的,它很大),您将看到大约 5500 万行包含实际出租车费用的数据。

每行将由 7 列组成。

  1. 接送日期,当出租车开始行驶时
  2. 皮卡 _ 经度,出租车乘坐皮卡的经度
  3. 皮卡 _ 纬度,乘坐皮卡的纬度
  4. 落客 _ 经度,落客的经度
  5. 落客 _ 纬度,落客的纬度
  6. 乘客计数,船上乘客的数量
  7. fare_amount ,我们要预测的那个。

我们走吧!

逻辑

对于本教程,我将使用 Python 和几个标准库,如 Numpy、Pandas 和 LGBM

import numpy as np 
import pandas as pd
import scipy as scipy
import datetime as dt
from sklearn.model_selection import train_test_split
import lightgbm as lgbm
import os
import gc

我导入了 GC。那是一个用 python 收集垃圾的库。垃圾收集器的 GC。

为什么要导入?

看看数据,对于本教程,我的计算机无法加载文件中的所有行,我最多只能加载 2200 万行。甚至,手动使用垃圾收集器也有所帮助,这样我就可以释放内存来加载和处理所有这 2200 万行。

# Reading Data
train_df =  pd.read_csv('train.csv', nrows = 22000000)# Drop rows with null values
train_df = train_df.dropna(how = 'any', axis = 'rows')# Drop invalid rows
train_df = train_df[(train_df.fare_amount > 0)  & (train_df.fare_amount <= 500) & ((train_df.pickup_longitude != 0) & (train_df.pickup_latitude != 0) & (train_df.dropoff_longitude != 0) & (train_df.dropoff_latitude != 0))]

读取并删除所有缺少值或值无效的行。

直觉

现在你已经准备好了数据。下一步是什么?

像这样的问题,你需要几个新的特性(数据中只有 6 个可用)。你可以试着用这些数据来训练模型,但是你给的信息对模型来说太少了。

特征工程是解决方案。

它基本上是使用现有的功能创建或修改数据。这样做将增加您对数据的理解,最终在训练阶段帮助您的模型。

比如经纬度,直接看的话可能没有任何意义。但是,什么对你有意义呢?距离!正确。

让我们准备一个函数来计算拾取点和衰减点之间的距离。

# To Compute Haversine distance
def sphere_dist(pickup_lat, pickup_lon, dropoff_lat, dropoff_lon):
    """
    Return distance along great radius between pickup 
    and dropoff coordinates.
    """
    #Define earth radius (km)
    R_earth = 6371 #Convert degrees to radians
    pickup_lat, pickup_lon, dropoff_lat, dropoff_lon =
            map(np.radians, [pickup_lat, pickup_lon,dropoff_lat,        
                dropoff_lon]) #Compute distances along lat, lon dimensions
    dlat = dropoff_lat - pickup_lat
    dlon = dropoff_lon - pickup_lon

    #Compute haversine distance
    a = np.sin(dlat/2.0)**2 + np.cos(pickup_lat) * 
        np.cos(dropoff_lat) * np.sin(dlon/2.0)**2
    return 2 * R_earth * np.arcsin(np.sqrt(a))

这就是哈弗辛公式,一个计算两个纬度和经度之间距离的函数。

然而,在导航中,除了距离之外,你经常需要计算方位,也称为方位角或在北方和游乐设备之间移动的角度。

def sphere_dist_bear(pickup_lat, pickup_lon, dropoff_lat, dropoff_lon):
    """
    Return distance along great radius between pickup and dropoff 
    coordinates.
    """

    #Convert degrees to radians
    pickup_lat, pickup_lon, dropoff_lat, dropoff_lon = 
    map(np.radians, [pickup_lat, pickup_lon, dropoff_lat, 
        dropoff_lon]) #Compute distances along lat, lon dimensions
    dlat = dropoff_lat - pickup_lat
    dlon = pickup_lon - dropoff_lon

    #Compute the bearing
    a = np.arctan2(np.sin(dlon * 
        np.cos(dropoff_lat)),np.cos(pickup_lat) * 
        np.sin(dropoff_lat) - np.sin(pickup_lat) * 
        np.cos(dropoff_lat) * np.cos(dlon))
    return a

你可以在这里阅读哈弗森距离,在这里阅读方位。

让我们添加数据

train_df['distance'] = sphere_dist(train_df['pickup_latitude'], 
                                   train_df['pickup_longitude'], 
                                   train_df['dropoff_latitude'], 
                                   train_df['dropoff_longitude']) 

train_df['bearing'] = sphere_dist_bear(train_df['pickup_latitude'], 
                                       train_df['pickup_longitude'], 
                                       train_df['dropoff_latitude'], 
                                      train_df['dropoff_longitude'])

接下来,您可能想要将纬度和经度转换为弧度。因为用弧度计算会给你已经标准化的输入。弧度参数的最大值是 2π弧度。

def radian_conv(degree):
    """
    Return radian.
    """
    return  np.radians(degree)train_df['pickup_latitude'] =  
                       radian_conv(train_df['pickup_latitude'])
train_df['pickup_longitude'] = 
                       radian_conv(train_df['pickup_longitude'])
train_df['dropoff_latitude'] = 
                       radian_conv(train_df['dropoff_latitude'])
train_df['dropoff_longitude'] = 
                       radian_conv(train_df['dropoff_longitude'])

现在,还有什么?

您可能希望从 datetime 特性中提取详细的日期和时间数据。

def add_datetime_info(dataset):
    #Convert to datetime format
    dataset['pickup_datetime'] = pd.to_datetime(dataset['pickup_datetime'],format="%Y-%m-%d %H:%M:%S UTC")

    dataset['hour'] = dataset.pickup_datetime.dt.hour
    dataset['day'] = dataset.pickup_datetime.dt.day
    dataset['month'] = dataset.pickup_datetime.dt.month
    dataset['weekday'] = dataset.pickup_datetime.dt.weekday
    dataset['year'] = dataset.pickup_datetime.dt.year

    return datasettrain_df = add_datetime_info(train_df)

这些是您可能希望看到的常见功能,因为出租车费用可能是季节性的。让我们将所有这些要素放入数据集中。

开始变得有创造力

从这一点来说,您已经准备好了,因为您可能已经从初始特征中提取了所有可能的特征。

这一次,你可能想在餐桌上增加一些创意。

给自己一个问题。

为什么人们在纽约打车?

  1. 可能他们刚到机场就去市区了。在这种情况下,如果乘坐距离机场较近,而乘坐距离机场较远,情况可能会有所不同。
  2. 人们到处都乘出租车。市内,市外,靠近市中心,远离市中心。但是大概票价会因为你离纽约市中心有多远而有所不同。
  3. 自由女神像?

Photo by AussieActive on Unsplash

让我们计算一下乘车点和这些点之间的距离。

def add_airport_dist(dataset):
    """
    Return minumum distance from pickup or dropoff coordinates to 
    each airport.
    JFK: John F. Kennedy International Airport
    EWR: Newark Liberty International Airport
    LGA: LaGuardia Airport
    SOL: Statue of Liberty 
    NYC: Newyork Central
    """
    jfk_coord = (40.639722, -73.778889)
    ewr_coord = (40.6925, -74.168611)
    lga_coord = (40.77725, -73.872611)
    sol_coord = (40.6892,-74.0445) # Statue of Liberty
    nyc_coord = (40.7141667,-74.0063889) 

    pickup_lat = dataset['pickup_latitude']
    dropoff_lat = dataset['dropoff_latitude']
    pickup_lon = dataset['pickup_longitude']
    dropoff_lon = dataset['dropoff_longitude']

    pickup_jfk = sphere_dist(pickup_lat, pickup_lon, 
                       jfk_coord[0], jfk_coord[1]) 
    dropoff_jfk = sphere_dist(jfk_coord[0], jfk_coord[1], 
                       dropoff_lat, dropoff_lon) 
    pickup_ewr = sphere_dist(pickup_lat, pickup_lon, 
                       ewr_coord[0], ewr_coord[1])
    dropoff_ewr = sphere_dist(ewr_coord[0], ewr_coord[1], 
                       dropoff_lat, dropoff_lon) 
    pickup_lga = sphere_dist(pickup_lat, pickup_lon, 
                       lga_coord[0], lga_coord[1]) 
    dropoff_lga = sphere_dist(lga_coord[0], lga_coord[1], 
                       dropoff_lat, dropoff_lon)
    pickup_sol = sphere_dist(pickup_lat, pickup_lon, 
                       sol_coord[0], sol_coord[1]) 
    dropoff_sol = sphere_dist(sol_coord[0], sol_coord[1], 
                       dropoff_lat, dropoff_lon)
    pickup_nyc = sphere_dist(pickup_lat, pickup_lon, 
                       nyc_coord[0], nyc_coord[1]) 
    dropoff_nyc = sphere_dist(nyc_coord[0], nyc_coord[1], 
                       dropoff_lat, dropoff_lon)

    dataset['jfk_dist'] = pickup_jfk + dropoff_jfk
    dataset['ewr_dist'] = pickup_ewr + dropoff_ewr
    dataset['lga_dist'] = pickup_lga + dropoff_lga
    dataset['sol_dist'] = pickup_sol + dropoff_sol
    dataset['nyc_dist'] = pickup_nyc + dropoff_nyc

    return datasettrain_df = add_airport_dist(train_df) 

这取决于你的想象力,你的特征可能不像我创造的。你可能对影响出租车费用的因素有另一种直觉,比如离商业区或住宅区的距离。或者你想象的任何东西。

该训练了

现在,在您准备好数据之后,让我们来训练模型。

train_df.drop(columns=['key', 'pickup_datetime'], inplace=True)

y = train_df['fare_amount']
train_df = train_df.drop(columns=['fare_amount'])x_train,x_test,y_train,y_test = train_test_split(train_df, y, test_size=0.10)del train_df
del y
gc.collect()

将数据以 90:10 的比例分割成训练验证分割。获得训练数组后,不要忘记删除数据帧。它占用了大量资源。

params = {
        'boosting_type':'gbdt',
        'objective': 'regression',
        'nthread': 4,
        'num_leaves': 31,
        'learning_rate': 0.05,
        'max_depth': -1,
        'subsample': 0.8,
        'bagging_fraction' : 1,
        'max_bin' : 5000 ,
        'bagging_freq': 20,
        'colsample_bytree': 0.6,
        'metric': 'rmse',
        'min_split_gain': 0.5,
        'min_child_weight': 1,
        'min_child_samples': 10,
        'scale_pos_weight':1,
        'zero_as_missing': True,
        'seed':0,
        'num_rounds':50000
    }train_set = lgbm.Dataset(x_train, y_train, silent=False,categorical_feature=['year','month','day','weekday'])valid_set = lgbm.Dataset(x_test, y_test, silent=False,categorical_feature=['year','month','day','weekday'])model = lgbm.train(params, train_set = train_set, num_boost_round=10000,early_stopping_rounds=500,verbose_eval=500, valid_sets=valid_set)

它将永远训练,直到你的模型不能再优化结果。这将使你达到大约 25,000 次迭代,并给你 $3.47966 均方根误差。

这是一个巨大的提升,可以为你节省 1.5 美元到 4.5 美元的出租车费。你可以用它买一份简单的快餐。Lol。

Photo by Jen Theodore on Unsplash

结论

一开始直觉可能很难。但是你知道,通常是常识帮助你度过所有这些。通过了解基层的数据情况,可以让你比以前更直观一点。

别忘了,继续努力!

干杯

为分布式分类帐建立区块链协议

原文:https://towardsdatascience.com/build-your-own-blockchain-protocol-for-a-distributed-ledger-54e0a92e1f10?source=collection_archive---------10-----------------------

通过一个简单的编码例子揭开区块链的神秘面纱

Background vector created by freepik — www.freepik.com

在这篇文章中,我将使用区块链构建一个简单的分布式分类帐,扩展 CodeAcademy 和其他人的教程,解释区块链的一些关键机制以及它们如何在 Python 中实现。

区块链概览

区块链协议的基本思想是在一个由机器或节点组成的分布式网络中保存一个名为分类账的数据库。分类账可以用于任何东西,可以保存交易或数字货币,如比特币或以太坊。为了维护保存在网络不同地方的账本的完整性,区块链协议在网络参与者解决计算密集型问题时对他们进行奖励——这个过程被称为挖掘。正是网络上的参与者进入的这个过程,确保了公共网络上分布式账本的完整性。区块链本质上是网络上的参与者解决的问题的解决方案的证明,这些解决方案在一个链中链接在一起,并且随着每个新的解决方案的发现,一个新的块被创建,并且下一组记录被复制到该块中。

构建区块链类

首先,我们将构建 Blockchain 类构造函数方法,该方法创建一个初始空列表(存储我们的区块链)和另一个存储分类账(交易记录)的列表。区块链类对象将负责管理整个区块链。我们将创建以下方法来帮助我们管理区块链:

[1] register_node() — 该方法将在网络上注册一个新节点

[2] new_block() —此方法将在区块链中创建一个新块,将最近的事务复制到这个新块中,并清除事务集。

[3] valid_proof() —此方法将检查提交以添加到区块链的块是否解决了问题。

[4]proof _ of _ work()-此方法将检查采矿是否产生了在区块链创建下一个区块的正确证据。如果证明是正确的,我们返回证明,否则它会一直调用 valid_proof() 直到我们找到有效的证明。

[5] valid_chain() — 该方法将遍历整个区块链,调用 valid_proof()来检查后续块是否有效。

[6]new _ transaction()—此方法将向事务列表中添加一个新事务。

[7] last_block() —此方法将返回链中的最后一个块。

区块链类看起来是这样的:

解决网络上的冲突

由于区块链协议是在分布式网络上运行的,我们需要一个关于区块链类对象的方法来解决网络上的冲突。我们将简单地检查网络上是否有任何其他比我们更长的区块链,如果有,使其成为我们自己的链,否则我们可以继续挖掘我们正在为下一个块挖掘的知识,我们的努力是值得的。

与区块链互动

最后,为了让我们的区块链变得生动并与之交互,我们可以使用 flask 通过 web 应用程序接口公开许多方法。

在网络上注册节点

我们将允许用户在网络上注册一个节点,这样他们就可以参与到工作证明过程中,并为他们的努力获得回报。我们在区块链类对象上调用 register_node()方法注册一个节点。

我们还想让这些参与者检查他们的区块链是否有效,如果无效,就从网络上获取区块链。

向区块链添加交易

我们希望允许参与者向分类帐添加交易。为此,我们只需在区块链类对象上调用 new_transaction(),并添加交易细节。

采矿

最后一件重要的事情!我们需要创造互动,让网络上的参与者挖掘他们的区块链。

现在,我们可以实例化 flask 应用程序,创建区块链类对象,运行 flask 应用程序,并与我们的区块链进行交互。

要下载完整的代码文件,请参见:【https://github.com/jamesdhope/blockchain】

参考

[1]https://www.wikihow.com/Build-a-Blockchain-App

[2]https://www . codecademy . com/learn/introduction-to-区块链/modules/区块链-in-python

[3]丹尼尔·范·弗莱明,https://hacker noon . com/learn-区块链-by-building-one-117428612 f46。

[4]https://towards data science . com/building-a-minimal-区块链-in-python-4f2e9934101d

[5]奥默·戈德堡,https://hacker noon . com/building-a-区块链-the-grey-paper-5be 456018040

在 15 分钟内构建您自己的基于聚类的推荐引擎!!

原文:https://towardsdatascience.com/build-your-own-clustering-based-recommendation-engine-in-15-minutes-bdddd591d394?source=collection_archive---------1-----------------------

推荐引擎是机器学习技术在当前互联网时代最流行的应用之一。这些广泛用于电子商务网站推荐类似产品和电影推荐网站。他们负责为我们生成各种定制的新闻建议。这将推动用户参与更多内容,从而为组织带来更好的用户体验和更多收入。因此,它们在今天的工业中极其重要。

推荐引擎基本上过滤数据并向用户推荐最相关的结果。这些结果是以最大可能感兴趣的方式推荐的。现在,所有的推荐引擎都有用户数据和他们的历史记录,可以用来创建他们的过滤算法。这最终帮助他们为每个独特的用户生成非常准确的推荐。

User-based filtering is based on history of users and similarity b/w them from their purchase histories for example. But, Item-based recommendations are based on content based similarity. Like, “how many times few items are bought together”. Next time, most frequent of these purchases will be recommended together.

在协同过滤的情况下,“用户行为”被用于推荐项目。这些推荐可以利用用户-用户相似性或者基于项目-项目相似性来生成。并且基于该相似性度量,向用户提供建议。但是,让我们考虑一个场景,在这个场景中,我们没有可用的用户数据,但是我们仍然必须向用户推荐商品。

没有用户数据怎么办?我们的推荐引擎现在将如何工作?

生成推荐的问题现在被简单地转化为类似聚类的问题。其中相似性度量基于“在生成推荐时,两个项目有多接近?”。用于生成推荐的度量将基于两个项目的相似性,如这些项目之间的向量距离。我们将针对 Pluralsight 的在线课程文本数据进行讨论。让我们来做一个仅基于我们可用的项目数据的推荐引擎。

在线课程推荐系统

在本文中,我们将从 Pluralsight 的课程数据中构建一个推荐系统,并查看可以对我们基于聚类的解决方案进行的进一步改进。我们将按下述顺序讨论该项目的整个数据分析流程。为了节省时间,你可以直接参考项目库并遵循精心制作的 README.md 文件。此外,可以为提到的每个模块直接运行实用程序脚本。

1.简介:了解你的数据

2.架构设计:构建实用工具

3.预处理步骤

4.问题讨论、模型训练和优化

5.工作推荐系统

6.结论&主题建模的未来改进(特别是 LDA)

超级省时提示 :打开项目的 github 库,按照 README.md 文件运行代码即可😉

简介:了解你的数据

该项目使用的数据是 Pluralsight 网站上的课程列表和描述。要获取课程数据,只需运行下面提到的 ReST API 查询。但是,为了获得用户注册数据,让我们说一个基于协作过滤器的引擎。

首先,获取在文档中提到的 ReST api-token,然后进行 ReST 查询,以获取关于该网站上所有课程和注册该网站的各个用户的数据。如果您想要获取用户相关数据,则需要此键。否则,为了获得简单的课程相关数据,我们可以编写如下 ReST 查询。

# Input
http://api.pluralsight.com/api-v0.9/courses# Output: A *Courses.csv* file for download. It will be having below mentioned structure.CourseId,CourseTitle,DurationInSeconds,ReleaseDate,Description,AssessmentStatus,IsCourseRetiredabts-advanced-topics,BizTalk 2006 Business Process Management,22198,2008-10-25,"This course covers Business Process Management features in BizTalk Server 2006, including web services, BAM, hosting, and BTS 2009 features",Live,no
abts-fundamentals,BizTalk 2006
...

在本文中,我们仅限于发动机制造的课程数据。否则,这种方法将与其他推荐引擎文章非常相似。通过查看这些数据,我们发现以下几点在训练模型时非常重要。您也可以打开 Courses.csv 文件,自己进行如下观察。

  1. 课程数据文本描述针对课程 Id、课程标题和课程描述列呈现。因此,在构建我们的推荐引擎时,这些列是我们感兴趣的。利用这些列中的文本数据,我们将能够构建词向量,我们的模型将在预测结果时使用这些词向量。此外,大部分信息仅出现在【描述】栏中。因此,没有描述的课程将从培训中删除。
  2. “已退休”栏描述了网站上课程的当前状态,即网站上目前是否有该课程。因此,我们不想推荐我们训练有素的模型的退役课程。但是,我们绝对可以在我们的训练数据中使用它们。
  3. 并对该数据的预处理进行了讨论。数据中显然存在一些额外的'-'标记、不同的大小写和停用词。我们将相应地预处理我们的文本,只关注名词/名词短语。

在下一节中,我们将讨论正在开发的这个推荐实用程序的基本架构。有了这个架构,最终我们将拥有一个完整的机器学习工具,它将课程数据作为输入,并基于用户查询生成建议。

架构设计:构建实用工具

下图清楚地说明了我们在这个数据科学项目中的渠道。请在以从左到右的方式进一步阅读之前先看一下。

Three main components: 1. Pre-process & Train; 2. Optimizations; 3. Recommendation Utility Tool

这个实用工具主要分为三个组件,我们将在接下来的章节中详细讨论这些组件。主要是对模型进行训练和优化,减少误差。之后,我们将编写实用工具,该工具将基于唯一课程 id 的输入查询生成推荐。

记住上面的工具架构,让我们转到预处理步骤,并开始为我们的模型进行数据摄取步骤。

预处理步骤

按照下面的代码片段,我们将做一些小的文本预处理,如删除所有标点符号。此外,在大量的术语中,“ll”被用在诸如“we’ll”、“you’ll”等情况中。这些也从‘描述’文本中删除。我们还将消除停用词,并以适当的方式合并包含描述、课程 id、标题的列。请参考下面的代码片段,以遵循上述步骤。

import pandas as pd# 1\. read data, from source
# "Courses.csv" file has been renamed
course_df = pd.read_csv("data/courses.csv")# 2\. drop rows with NaN values for any column, specifically 'Description'
# Course with no description won't be of much use
course_df = course_df.dropna(how='any')# 3\. Pre-processing step: remove words like we'll, you'll, they'll etc.
course_df['Description'] = course_df['Description'].replace({"'ll": " "}, regex=True)# 4\. Another Pre-preprocessing step: Removal of '-' from the CourseId field
course_df['CourseId'] = course_df['CourseId'].replace({"-": " "}, regex=True)# 5\. Combine three columns namely: CourseId, CourseTitle, Description
comb_frame = course_df.CourseId.str.cat(" "+course_df.CourseTitle.str.cat(" "+course_df.Description))# 6\. Remove all characters except numbers & alphabets
# Numbers are retained as they are related to specific course series also
comb_frame = comb_frame.replace({"[^A-Za-z0-9 ]+": ""}, regex=True)

在对上述数据进行基本的清理步骤后,‘comb _ frame’包含了与课程相关的所有必要的文字描述。之后,让我们移动到这个文本的矢量化,并训练我们的模型。

问题讨论、模型训练和优化

现在,我们将所有需要的文本数据呈现在一个数据框中。但是,我们需要将其转换成有意义的表示。因此,它可以正确地输入到我们的机器学习模型中。

为此,我们使用 tf-idf 权重来表示术语在文档中的重要性。它是对文档中单词重要性的统计度量。该权重与单词在语料库中出现的次数相关,但是被语料库中单词的频率所抵消。

Tf 中的 tf-idf 权重衡量文档中的词频。以及 idf 测量给定语料库中给定术语重要性。这可以从下面提到的公式中推断出来。

**TF(**t**)** = (Number of times term *'t'* appears in a document) **/** (Total number of terms in the document)**IDF(**t**)** = **log_e(**Total number of documents **/** Number of documents with term *'t'* in it**)**

我们将使用 scikit learn 将我们的文本数据转换为上面公式中指定的向量矩阵乘积。按照下面的代码片段进行转换。

# Create word vectors from combined frames
# Make sure to make necessary importsfrom sklearn.cluster import KMeans
from sklearn import metrics
from sklearn.feature_extraction.text import TfidfVectorizervectorizer = TfidfVectorizer(stop_words='english')
X = vectorizer.fit_transform(comb_frame)

在此之后,我们可以将这些数据直接输入到我们的 k 均值学习算法中。但是,对于我们的 k-means 算法,我们将需要【k】的理想值,对此我们还没有讨论过。首先,我们可以使用 k=8 的值,因为 Pluralsight 有八种不同类型的课程类别,并检查我们的模型相应训练的预测能力。跟随下面提到的代码片段。

# true_k, derived from elbow method and confirmed from pluralsight's website
true_k = 8# Running model with 15 different centroid initializations & maximum iterations are 500
model = KMeans(n_clusters=true_k, init='k-means++', max_iter=500, n_init=15)
model.fit(X)

我们可以观察来自每个聚类的顶词,以查看所形成的聚类在质量上是否良好,或者它们在某种意义上是否需要改进。运行下面提到的片段,观察每个聚类中的热门词。

# Top terms in each clusters.print("Top terms per cluster:")
order_centroids = model.cluster_centers_.argsort()[:, ::-1]
terms = vectorizer.get_feature_names()
for i in range(true_k):
    print("Cluster %d:" % i),
    for ind in order_centroids[i, :15]:
        print(' %s' % terms[ind]),
    print

观察这些单词后,您可能会注意到所有形成的集群都不合适,一些课程类别在多个集群中重复出现(请参考 README.md 文件)。那现在还是好的(😉),我们的模型将数量巨大的课程类别细分为其他子类别。因此,给定类别的课程数量的基数问题暴露出来,我们的模型无法解决。

我们可以看到,细分类别平面艺术,电影设计,动画形成了母'创意-专业'类别。由于课程类别之间的数据分布不均,即数据基数的问题,因此形成了这个子类。因此,像“商务-专业”这样课程数量少的课程类别在我们的理想假设中迷失了,因为 k 等于 8。这很容易发生,因为在我们简单的机器学习模型训练中,不经常出现的业务相关术语很容易失去其 tf-idf 权重。

因此,从这种方法得到的聚类仍然可以通过进一步划分成其他聚类来改进,以用更少数量的课程得到这些更小的课程类别。因为,这些进一步的划分可以公式化为误差最小化的优化问题。我们不想因此过度拟合我们的模型,我们将使用 【肘测试】 方法来寻找 k 的理想值。这个想法是,每当给定的【k’的值的误差急剧下降时,该值对于形成聚类来说足够好。这些形成的集群将具有尖锐的误差极小值,并将为我们的模型给出令人满意的解决方案。按照下面提到的代码对我们的数据进行弯头测试。

**# Continuing after vectorization step# data-structure to store Sum-Of-Square-Errors
sse = {}# Looping over multiple values of k from 1 to 30
for k in range(1, 40):
    kmeans = KMeans(n_clusters=k, init='k-means++', max_iter=100).fit(X)
    comb_frame["clusters"] = kmeans.labels_
    sse[k] = kmeans.inertia_# Plotting the curve with 'k'-value vs SSE
plt.plot(list(sse.keys()), list(sse.values()))
plt.xlabel("Number of cluster")
plt.ylabel("SSE")
# Save the Plot in current directory
plt.savefig('elbow_method.png')**

运行上述代码后,我们得到了下图,在此基础上,我们为 k=30 训练了我们的模型。并为我们的推荐引擎工具实现了相对更好的聚类。

Slope is drastically diminishing after the value of k=30. Hence, we’ll opt for this value for our model.

最后,让我们保存我们的模型,继续我们的推荐实用程序脚本设计,并讨论未来的改进方法。所有这些提到的片段都以 model_train.py 脚本的形式提供,您可以参考它来直接执行。但是,在此之前,请提取 courses.csv 数据文件,并仔细阅读 README.md 。

**# Save machine learning model
filename = 'finalized_model.sav'
pickle.dump(model, open(filename, 'wb'))**

工作推荐系统

我们将为这个推荐模块创建几个实用函数。一个 cluster_predict 函数,它将预测输入其中的任何描述的分类。首选输入是我们之前在 model_train.py 文件的 comb_frame 中设计的类似“描述”的输入。

**def cluster_predict(str_input):
    Y = vectorizer.transform(list(str_input))
    prediction = model.predict(Y)
    return prediction**

之后,我们将根据新的 dataframe 列中的描述向量为每个课程分配类别,即‘cluster prediction’。见下文。

**# Create new column for storing predicted categories from our trained model.
course_df['ClusterPrediction'] = ""**

我们将为只有实时课程的数据框存储该聚类类别分数,即删除“无”实时条目的课程。之后,我们将在数据框中为每门课程运行预测函数实用程序,并存储聚类类别。这些存储的类别将在将来与输入查询及其预测类别进行匹配,以生成推荐。

**# load the complete data in a dataframe
course_df = pd.read_csv("data/courses.csv")# drop retired course from analysis. But, courses with no descriptions are kept.
course_df = course_df[course_df.IsCourseRetired == 'no']

# create new column in dataframe which is combination of (CourseId, CourseTitle, Description) in existing data-frame
course_df['InputString'] = course_df.CourseId.str.cat(" "+course_df.CourseTitle.str.cat(" "+course_df.Description))# Create new column for storing predicted categories from our trained model.
course_df['ClusterPrediction'] = ""# Cluster category for each live course
course_df['ClusterPrediction']=course_df.apply(lambda x: cluster_predict(course_df['InputString']), axis=0)**

最后,推荐实用函数将预测具有课程 id 的输入查询的课程类别,并且将从上面转换的数据帧‘course _ df’中推荐几个随机课程,该数据帧具有每个课程的预测值。

**def recommend_util(str_input):

    # match on the basis course-id and form whole 'Description' entry out of it.
    temp_df = course_df.loc[course_df['CourseId'] == str_input]
    temp_df['InputString'] = temp_df.CourseId.str.cat(" "+temp_df.CourseTitle.str.cat(" "+temp_df['Description']))
    str_input = list(temp_df['InputString'])
        # Predict category of input string category
    prediction_inp = cluster_predict(str_input)
    prediction_inp = int(prediction_inp) # Based on the above prediction 10 random courses are recommended from the whole data-frame
    # Recommendation Logic is kept super-simple for current implementation. temp_df = course_df.loc[course_df['ClusterPrediction'] == prediction_inp]
    temp_df = temp_df.sample(10)

    return list(temp_df['CourseId'])**

用下面给出的查询测试你训练过的推荐引擎。您也可以通过从 courses.csv 获取课程 id 来添加您的查询。

**queries = ['play-by-play-machine-learning-exposed', 'microsoft-cognitive-services-machine-learning', 'python-scikit-learn-building-machine-learning-models', 'pandas-data-wrangling-machine-learning-engineers', 'xgboost-python-scikit-learn-machine-learning']for query in queries:
    res = recommend_util(query)
    print(res)**

结论和未来改进

当前推荐引擎的实现本质上是非常原始的。用精确的硬步骤阈值来形成集群的方法是粗糙的,但是给出了用集群化算法实现这些引擎的想法。此外,生成的推荐本质上是随机的。可以采用更具体的方法(如基于最高得分的推荐方法)作为改进。目前,course-id 作为唯一的输入,而不是更好的自然语言输入。但是,这些只是基于实施的改进。

基本上,为了将来的改进,用于训练的类别分配机制和模型可以被改变。此外,可以采用来自主题建模的高级和复杂的机制,如潜在狄利克雷分配(LDA)。主题建模是自然语言处理的一个统计分支,它从文档集中提取摘要。我们将使用 LDA,它将一个特定的文档分配给一个特定的主题和一个实数权重分数,该分数将与相应主题的单词相关联。

只需运行 lda_train.py 来详细了解 lda 的实现,注释/控制台输出将解释关于正在执行的步骤的一切。

这些指定的主题及其与单词的关联分数可以作为上述 cluster_prediction 函数的预测逻辑基础。但是,这些预测将比当前由 k-means 聚类算法生成的任何推荐更精确。一个基于 gensim 的 LDA 实现在这里的同一个 github 仓库中可用。它的推荐实用程序脚本目前还没有添加,你可以作为家庭作业来尝试。

霍普,你喜欢读它,并得到了一个数据科学项目的小手。如果有任何改进,请在 github 上做一个公关或公开一个问题。

建立您自己的自定义热门词探测器零训练数据和$0!

原文:https://towardsdatascience.com/build-your-own-custom-hotword-detector-with-zero-training-data-and-0-35adfa6b25ea?source=collection_archive---------14-----------------------

TLDR : 谷歌 TTS - >噪音增强- > {wav 文件} - > SnowBoy - > {。pmdl 型号} - >树莓 Pi

好了,又到了一年中的这个时候了。你知道沙漠里有个东西。上一次,我装配了一个谷歌 AIY 视觉套件,并在芯片上添加了 espeak 和 Terra ,这是一群杂牌军的艺术装置,即ÿntelligent 技术解放的 bÿte:燃烧器。

结果是这样的:

这一次,我决定增加一种额外的感官能力:倾听和回应的能力,在机器学习中,通俗地说就是装配一个可以在设备上和离线工作的热词检测引擎。放松点,皮兹,不?把啤酒递给我好吗?在学徒上阅读…

因此,我开始四处寻找简单的现成解决方案,并偶然发现了令人敬畏的snow boy*离线热门词汇检测器。这当然是有限制的!你可以下载预先训练好的机器学习模型来检测特定的热门词汇,如 AlexaJarvis(见下图)…*

The off-the-shelf available hotword models

..但是,为了真正为精确的自定义热门词汇建立您自己的健壮模型,您需要大约 500 名志愿者每人贡献 3 个样本。尽管我在社交媒体上的影响力极其有限,但我还是召集了大约 5 名捐款人

5 donors from my social media campaign!

看到这毫无进展,我想到了生成自己的数据集。我最近从事了几个合成到真实单词转移学习项目,其中一个我在 https://arxiv.org/abs/1905.08633 ICLR 的深度生成模型研讨会上发表了论文(见和),我认为如果 WaveNet 在生成真实声音的文本到语音转换方面真的如此令人印象深刻,我可以使用 Goggle doles 提供的 300 美元免费 Google cloud credits 来为我收集数据,并通过一些漂亮的噪声增强将学习转移到现实世界中,是的,深度神经网络的泛化能力是不合理的!

第一阶段:使用 Google TTS 生成不同声音的合成热门词音频文件

于是,我创建了一个临时的 GC 账号,虔诚地按照文档写了一些 Python 代码(分享到这里:https://github . com/vinay prabhu/burning man 2019/blob/master/generate _ SYNTHETIC _ audio files . py)。在大约 5 分钟内,我有 189 个。wav 文件的热词我是针对的(这是 嘿芯片! BTW)不同的口音,或者更正式地称为的声音。你可以从这里下载整个宝藏。**

Using Google TTS to generate synthetic training data

我最喜欢的一些声音例子是:

Example sound files from the Google TTS engine!

现在我们有了 189 个。不同声音的 wav 文件,对于这些声音中的每一个,我执行了普通的加性白高斯噪声增强来获得(189 x 3) wav 文件。这里是与这个任务相关的 colab 笔记本。

阶段 2:使用合成的噪声增强 wav 文件训练热词检测模型

尽管这项技术很有前途,但它仍处于危险之中。用于以编程方式训练您自己的模型的 api 看起来相当严格:

*python training_service**.**py 1.wav 2.wav 3.wav saved_model**.**pmdl*

正如所见,您需要准确地输入 3 个 wav 文件来生成一个模型。所以,是的。我确实为每个声音生成了 189 个模型(我非常非常高兴在这方面被证明是错误的),并将它们逻辑“或”在一起。摄取 wav 文件训练 ML 模型的 colab 笔记本分享到这里:https://github . com/vinay prabhu/burning man 2019/blob/master/Colab _ Notebooks/model _ gen . ipynb

阶段 3:将所有的模型组合在一起,并在 Raspberry Pi 上运行它们

好的。所以,这个阶段有点棘手。请确保您耐心且虔诚地遵循本回购文件:

* [## wanleg/snowboyPi

从全新安装 Raspbian 开始(Lite 或 Regular,本指南假设是 Lite) sudo apt update && sudo apt -y…

github.com](https://github.com/wanleg/snowboyPi)

树莓 Pi 上音频项目的一个主要来源是 ALSA 的恶作剧,以及随之而来的 HDMI、USB 音频和本地音频输出端口之间的音频统治之争。为了避免这一点,我使用了良好的 ol' SAMSON mike in + audio-out 钻机(显然亚马逊兜售这些现在 29.99 美元!班加罗尔 SP 路的便宜多了。)

整个设置如下所示:

The raspberry pi-3 set-up with the Samson mike

在开始人体试验之前,我试图通过一个简单的测试案例来隔离使用麦克风的影响,我在笔记本电脑上播放谷歌 TTS 输出音频文件,并检查音频文件是否相关。运行在 Raspberry Pi 上的 pmdl 确实会被合成话语触发。结果非常好!

Synthetic data input trials

现在有了一些希望,我开始调整 snowboy.py 脚本(从这里:【https://github.com/wanleg/snowboyPi/blob/master/snowboy.py】T2)来包含所有的。我刚刚生成的 pmdl 模型文件,以便当现实世界中的智人说出关键字‘嘿,芯片!’时,至少有一个会被触发。原来,你需要做的只是在这里添加第 29 行的模特列表:https://github . com/wan leg/snowboyPi/blob/master/snowboy . py # L29

大结局:

现在所有的文件都打包好了(顺便说一下,你再也不用担心这些单个 DNN 模型的大小了。各有~ 10KB。令人印象深刻的作品 雪衣人 !),我最终决定用真实的人类语音输入进行最后的真实世界测试,结果是..鼓声

钽..大!有用!它与我正常的(印度口音)声音和我的亚当·莱文声音配合得天衣无缝。

第 4 阶段:片上部署和 Terra +套管+ playa 部署

我正屏息等待这一阶段。我一回到默认世界就更新这篇博文!*

构建您自己的数据仪表板

原文:https://towardsdatascience.com/build-your-own-data-dashboard-93e4848a0dcf?source=collection_archive---------0-----------------------

数据专业人员权威指南(商业智能)

使用 Python Dash 快速构建 dashboard web 应用程序

Source: Unsplash

问题是

The emergence of BI Dashboarding Tools. source: ezDataMunch

对于数据科学家来说,将我们的数据和结果传达给非技术用户是非常重要的。特别是以能够被理解和迅速反应的形式。这就是数据可视化非常重要的原因,尤其是在 Power BI、Tableau 和 Qlikview 等 BI 产品中。

虽然他们有一个易于使用的界面来产生惊人的可视化,这些技术许可证可能非常昂贵。对于 Tableau 来说,每月最高可达 50 美元。此外,对于像我这样的数据专业人员来说,我认为大多数 BI 工具不够通用,无法跟上 Python 用例的动态增长。嵌入它仍然非常笨拙,而不是与我们的 web 应用程序无缝集成。

因此,我们需要一个更好的方法来解决这个问题。

我们可以免费用 Python 构建仪表板 Web 应用吗?

令人惊讶的答案是肯定的!我将通过开源库——Dash Python 向您展示这一点。

见见达什。

简而言之,Dash 是一个开源 Python 库,用于构建针对数据可视化而优化的 web 应用程序。Dash 最棒的地方在于,它建立在 Plotly 和 Matplotlib 等数据可视化库、Web 应用程序库(Flask)以及最终通过 Pandas 实现的数据可移植性之上!由于 Javascript 层是通过 Plotly 和 Flask 处理的,因此您甚至不需要接触其他编程语言就可以创建一个令人惊叹的 web 应用程序。

最终结果是熟悉、惯例和实用性的完美结合。您可以快速地选择、开发和部署应用程序。严格来说,所有这些都是用 Python 编写的,不需要其他语言(尽管选项仍然可用)。

冲刺的力量

“Dash 应用程序代码是声明性和反应性的,这使得构建包含许多交互元素的复杂应用程序变得容易。” —阴谋破折号

最好的特性之一是 Dash 支持声明式编程。这允许您基于输入数据和输出属性构建 Dash 应用程序。你将只陈述你需要什么,而不是如何实现目标的细节。

Source: Unsplash

假设你想买鸡蛋。

声明式编程会说“给我找鸡蛋买鸡蛋。这是现金”

然而传统编程会说“去法院超市,去 6 号过道找到你右手角的鸡蛋,去收银台用 5 美元现金支付”。

显然,从这个例子中,声明式编程将卸载“如何”。与金钱和鸡蛋类似,您只需要提交输入数据和输出属性,可视化结果就会自动呈现。

你甚至不需要理解 Dash 是如何处理你的视觉化的。你只需要发出指令并接受结果。这就是 Dash 支持声明式编程的美妙之处。

其实这一点都不外国。许多语言也是用相同的概念构建的。声明性语言的一个例子是 SQL(结构化查询语言)和 Kubernetes yaml 。两者都是数据科学家优化数据检索和开发流程的重要工具。

希望我让你兴奋!!让我们开始吧

战斗中的冲刺

Our Application in Big Picture, today we are going to learn how to build Dashboard with Dash

在本教程中,您将学习如何使用 Dash Python 构建仪表板应用程序。

我们将访问我们以前在任务调度器到上的项目,从 Lazada (电子商务)网站抓取数据,并将其转储到 SQLite RDBMS 数据库中。然后,我们将生成数据可视化,以了解 Lazada 产品随时间和日期的价格变化。

请随意欣赏这篇文章或访问我的 Github Repo 获取完整代码。

在这种情况下,让我们想象 3 天内的价格变化。

Price Optimization Dashboard Scraped from Lazada over 3 days

让我们完成这 5 个重要步骤,生成您的第一个仪表板!

  1. 导入和激活仪表板
  2. 准备数据
  3. 可视化图表
  4. 下拉菜单和输入过滤器选择
  5. 造型和装饰

导入和激活仪表板

像往常一样,让我们在 Python 上导入 Dash 库。

import dash
from dash.dependencies import Input, Output
import dash_core_components as dcc
import dash_html_components as html

您可以用一个简单的 pip install 命令下载这些库。

pip install <library name e.g: dash>

我们还将用这段代码激活 Dash 服务器。

app = dash.Dash(__name__)
server = app.server
if __name__ == '__main__':
    app.run_server(debug=True)

一旦您运行这个 Python 脚本,它将运行一个服务器,您可以使用 http://127.0.0.1:8050/ 打开该服务器。这将打开一个 flask web 应用程序,您可以将它部署在任何地方,比如 Heroku。请注意,我们放置了 run_server 参数debug =True. ,这将允许您在脚本中保存任何更改后自动更新本地部署。非常整洁的时间节省技巧重新启动您的应用程序。

Info logs to run dash app on Command Prompt

准备数据

在这个领域,我们将从数据库中读取产品信息,并将它们转储到熊猫数据框架中。dbm 是我们之前在 SQLite RDBMS 项目中创建的一个模块。这将把一个 SQLite 表转储到名为product_df的 Pandas Dataframe 中。请随意从我的 Github 中提取模块。

global product_df
product_df = dbm.read()

关键字global 将全球化 product_df,以便所有回调函数都可以访问它来生成数据可视化。

可视化图表

Dash Graph with Component Details

我们将创建一个应用程序布局,它将在html 模块中封装 html 对象。这将是我们布局图形和调整相对尺寸到你的屏幕尺寸的主要途径。

app.layout = html.Div([
    html.Div([
        html.H1('Price Optimization Dashboard'),
        html.H2('Choose a product name'),
        dcc.Dropdown(
            id='product-dropdown',
            options=dict_products,
            multi=True,
            value = ["Ben & Jerry's Wake and No Bake Cookie Dough Core Ice Cream","Brewdog Punk IPA"]
        ),
        dcc.Graph(
            id='product-like-bar'
        )
    ], style={'width': '40%', 'display': 'inline-block'}),
    html.Div([
        html.H2('All product info'),
        html.Table(id='my-table'),
        html.P(''),
    ], style={'width': '55%', 'float': 'right', 'display': 'inline-block'}),
    html.Div([
        html.H2('price graph'),
        dcc.Graph(id='product-trend-graph'),
        html.P('')
    ], style={'width': '100%',  'display': 'inline-block'})

])

注意dcc 模块。这是 dash 核心组件,将存储 web 应用程序的基本可视化,如条形图、下拉列表和折线图。

剩下的简单明了,具体到html 模块。您可以创建 H1 或 H2 标题、div(包含 web 组件的框),甚至表格。把它想象成抽象的 html 代码,这样你甚至不需要看它。

现在我们来说说iddcc.Graph(id=’my-table’) 中的 id my-table 到底是什么意思?这显示了为某个图形输出调用哪个函数。通过插入代码,我们将调用下面的函数。

@app.callback(Output('my-table', 'children'), [Input('product-dropdown', 'value')])
def generate_table(selected_dropdown_value, max_rows=20):

product_df_filter = product_df[(product_df['product_title'].isin(selected_dropdown_value))]product_df_filter = product_df_filter.sort_values(['index','datetime'], ascending=True)

return [html.Tr([html.Th(col) for col in product_df_filter  .columns])] + [html.Tr([
        html.Td(product_df_filter.iloc[i][col]) for col in product_df_filter  .columns
    ]) for i in range(min(len(product_df_filter  ), max_rows))]

在功能代码的顶部,你会看到@app.callback ,它将运行魔法。这意味着您将返回函数准确地导出到 my-table 组件中。您也可以从下拉列表中指定输入选项。这将用于过滤 product_df。

注意,过滤后的 product_df 将用于填充 return 语句,在这里我们使用 html 模块设计表格。

它的美妙之处在于,如果您更改仪表板中的下拉输入,该函数将呈现过滤后的 product_df。

确切地说,你如何使用 Tableau,但免费和更通用(即插即用)!!

Source: Meme Crunch

下拉菜单和输入过滤器选择

注意到@app.callback 输入了吗?此时,您可以指定自己的过滤器来呈现可视化组件。

dcc.Dropdown(
    id='product-dropdown',
    options=dict_products,
    multi=True,
    value = ["Ben & Jerry's Wake and No Bake Cookie Dough Core Ice Cream","Brewdog Punk IPA"]
),

id 与回调函数中的输入注释相同。这指定了要调用的函数。

options 将插入所有可用选项的键值对。这可能是股票代号,如{'GOOG ':谷歌,' MSFT ':微软}或任何东西。对于我们的例子,我们将插入相同的键值对,它们是产品名称。

multi 属性将允许您选择一个以上的选项,这对于在一个图表中进行并排价格比较来说是完美的。

最后,value 属性将在服务器运行开始时存储你的下拉列表值。

Dropdown values to affect Python Dash Elements

造型和装饰

Dash 的造型很简单。默认情况下,Dash 已经预先配置了访问资产文件夹的设置。这是您可以覆盖样式的 css 和 web 行为的 js 的地方。你可以插入 stylesheet.css 来美化你的 Dash Web 应用。具体的改进空间是组件和表格边框之间的边距。

Inserting assets folder with stylesheet.css for styling

最终结果

恭喜你!!您已经创建了第一个交互式仪表板。如果你做得好,你会得到这个结果。如果没有,请随意查阅我的 Github 代码或者在这里提出你的问题。

Your First Python Price Optimization Dashboard!!!

现在,释放和创建自己的仪表板!

更多参考

如果你需要更多的例子和 Dash 能做什么的更好的见解。请随意访问以下链接。我向你保证,这些将提高你的仪表板设计技能,以解决现实生活中的业务问题。

  1. 价值投资仪表盘配 Python 美汤和 Dash Python
  2. 使用 Python Dash、主题分析和 Reddit Praw API 自动生成 FAQ
  3. 仪表盘图库

最后…

Source: Unsplash

我真的希望这是一本很棒的读物,是你发展和创新的灵感来源。

在下面评论出来建议和反馈。就像你一样,我也在学习如何成为一名更好的数据科学家和工程师。请帮助我改进,以便我可以在后续的文章发布中更好地帮助您。

谢谢大家,编码快乐:)

关于作者

Vincent Tatan 是一名数据和技术爱好者,拥有在 Visa Inc .和 Lazada 实施微服务架构、商业智能和分析管道项目的相关工作经验。

Vincent 是土生土长的印度尼西亚人,在解决问题方面成绩斐然,擅长全栈开发、数据分析和战略规划。

他一直积极咨询 SMU BI & Analytics Club,指导来自不同背景的有抱负的数据科学家和工程师,并为企业开发他们的产品开放他的专业知识。

请通过 LinkedIn Medium Youtube 频道 联系文森特

用 R 构建你自己的神经网络分类器

原文:https://towardsdatascience.com/build-your-own-neural-network-classifier-in-r-b7f1f183261d?source=collection_archive---------4-----------------------

导言

图像分类是计算机视觉中的一个重要领域,不仅因为许多应用都与它相关,而且许多计算机视觉问题都可以有效地归结为图像分类。图像分类中最先进的工具是卷积神经网络(CNN)。在这篇文章中,我将写一个简单的神经网络有 2 层(完全连接)。我将首先训练它对一组 4 类 2D 数据进行分类,并可视化决策边界。第二,我准备用著名的 MNIST 数据(可以在这里下载:https://www . ka ggle . com/c/digit-recognizer/download/train . CSV)来训练我的 NN,看看它的表现。第一部分受斯坦福提供的 CS 231n 课程启发:http://cs231n.github.io/,用 Python 授课。

数据集生成

首先,让我们创建一个螺旋数据集,每个数据集有 4 个类和 200 个示例。

Xy分别是 800×2 和 800×1 的数据帧,并且它们是以线性分类器不能将它们分开的方式创建的。由于数据是 2D,我们可以很容易地在图上看到它。它们的间距大致相等,实际上一条线并不是一个好的决策边界。

神经网络构建

现在,让我们构造一个两层的神经网络。但在此之前,我们需要将 X 转换成一个矩阵(用于后面的矩阵运算)。对于 Y 中的标签,创建新的矩阵 Y (800 乘 4),使得对于每个示例(Y 中的每一行),index==label 的条目为 1(否则为 0)。

接下来,让我们构建一个函数nnet,它采用两个矩阵XY,并返回一个包含 4 个矩阵的列表,其中有WbW2b2(每层的权重和偏差)。我可以指定step_size(学习率)和正则化强度(reg,有时符号化为λ)。

激活和损失(代价)函数的选择,分别选择 ReLU 和 softmax。如果你选修了吴恩达的 ML 课程(强烈推荐),在课程笔记和作业中会选择 sigmoid 和物流成本函数。它们看起来略有不同,但是只需修改下面的代码就可以很容易地实现。还要注意,下面的实现使用了看起来难以理解的矢量化运算。如果是这样,你可以写下每个矩阵的维数并检查乘法运算等等。通过这样做,你也知道神经网络的内部是什么。

预测函数和模型训练

接下来,创建一个预测函数,它将X(与训练X相同的列,但可能有不同的行)和层参数作为输入。输出是每行中最大得分的列索引。在这个例子中,输出只是每个类的标签。现在我们可以打印出训练精度。

判别边界

接下来,让我们绘制决策边界。我们还可以使用 caret 包,用数据训练不同的分类器,并可视化决策边界。看到不同的算法如何做决定是非常有趣的。这将是另一篇文章。

MNIST 数据和预处理

著名的 MNIST(“修正的国家标准与技术研究所”)数据集是机器学习社区中的经典,已经被广泛研究。它是分解成 csv 文件的手写数字的集合,每行代表一个示例,列值是每个像素从 0 到 255 的灰度。首先,让我们显示一个图像。

现在,让我们通过删除接近零方差的列并按max(X)缩放来预处理数据。为了交叉验证,数据也被一分为二。同样,我们需要通过K创建一个维度为NY矩阵。这一次,每行中的非零索引偏移 1:标签 0 将在索引 1 处具有条目 1,标签 1 将在索引 2 处具有条目 1,依此类推。最终,我们需要将其转换回来。(另一种方法是将 0 放在索引 10 处,其余标签没有偏移量。)

模型训练和 CV 准确性

现在我们可以用训练集训练模型了。注意即使删除了 nzv 列,数据仍然很大,所以结果可能需要一段时间才能收敛。在这里,我只为 3500 次迭代训练模型。您可以改变迭代次数、学习率和正则化强度,并绘制最佳拟合的学习曲线。​

随机图像的预测

最后,我们随机选择一张图片,预测标签。​

结论

如今,我们很少从头开始编写自己的机器学习算法。有成吨的软件包可用,他们最有可能胜过这个。但是,通过这样做,我真的对神经网络的工作原理有了深刻的理解。在一天结束时,看到你自己的模型产生一个相当好的准确性是一个巨大的满足。

建立你自己的熊猫(喜欢)图书馆

原文:https://towardsdatascience.com/build-your-own-pandas-like-library-7aac66c1f3d7?source=collection_archive---------25-----------------------

我主要通过泰德·彼得鲁的烹饪书了解熊猫。而现在,Ted 又想出了另一个有趣的项目: 建造我们自己的熊猫像库 。它包含一系列需要完成的步骤,以便构建一个类似于熊猫的全功能库,称为 pandas_cub 。图书馆将拥有熊猫最有用的特征。

我喜欢这个项目的地方

  1. 项目规模:该项目为熟悉 Python 的人提供了一个编写大代码的绝佳机会。
  2. 设置环境:这个项目帮助你学习如何为你的项目创建一个独立的环境。这是使用conda包管理器完成的。
  3. 测试驱动开发:你也学习测试驱动开发,这意味着你首先写测试,然后你写代码通过测试。在这个过程中你会学到 python 库pytest

先决条件和设置可以在项目的 Github 页面上找到,但我仍然会提供一个快速浏览。

步骤 1: 派生存储库

Git 和 GitHub 入门课程,以防你不熟悉。

第二步:克隆你的本地拷贝

没错。现在,我们可以填写我们的代码,并将其推送到我们的项目副本中。但是,如果项目的创建者修改了原来的项目呢?我们希望我们的分叉版本中也有这些修改。为此,我们可以将一个名为upstream的遥控器添加到原始存储库中,并在我们想要同步二者时从那里添加pull。然而,这并不是这个项目所必需的。

第三步:环境设置

这意味着下载构建这个项目所需的特定库和工具集。该项目有一个名为environment.yml的文件,其中列出了所有这些库。一个简单的conda env create -f environment.yml将为我们安装它们。我已经安装好了。

我们现在可以使用conda activate pandas_cubconda deactivate来激活和停用我们的环境。

步骤 4: 检查测试

所有的测试都包含在位于tests目录下的一个名为test_dataframe.py的文件中。

运行所有测试:

$ pytest tests/test_dataframe.py

运行特定类别的测试:

$ pytest tests/test_dataframe.py::ClassName

运行该类的特定函数:

$ pytest tests/test_dataframe.py::ClassName::FuncName

最后,您需要确保 Jupyter 运行在正确的环境中。

第五步:__init.py__

一旦一切都设置好了,让我们从检查__init.py__开始,这是我们将在整个项目中编辑的文件。第一个任务要求我们检查用户提供的输入是否是正确的格式。

先来举个TypeError如果data不是字典的话。我们的做法如下:

为了测试这个案例,我们将打开一个新的 Jupyter 笔记本(您也可以使用现有的测试笔记本)并执行以下操作:

注意:确保你的笔记本上有这些神奇的代码行。它们将确保无论何时您编辑库代码,它都将反映在您的笔记本中,而无需重新启动。

现在我们传一本字典,看看有没有用。

的确如此。一旦我们编写了所有的案例,我们就可以从test_dataframe.py开始运行测试,看看它们是否都通过了。总的来说,这似乎是一个非常有趣的项目,并且提供了很多可以学习的东西。浏览一遍,告诉我你喜不喜欢。也告诉我你做过的其他有趣的项目。

~快乐学习。

5 分钟内建立自己的推荐系统!

原文:https://towardsdatascience.com/build-your-own-recommender-system-within-5-minutes-30dd40388fbf?source=collection_archive---------12-----------------------

机器学习技术在商业中最成功和最广泛的应用是推荐系统。

Photo by Fixelgraphy on Unsplash

您正在浏览 Spotify 以收听歌曲,但无法决定是哪一首。你正在 YouTube 上浏览观看一些视频,但无法决定观看哪个视频。还有很多这样的例子,我们有丰富的数据,但我们不能决定我们想要什么。这就是推荐系统帮助我们的地方。

推荐系统在今天的市场中无处不在,并且具有巨大的商业重要性,正如大量公司销售推荐系统解决方案所证明的那样。推荐系统改变了无生命网站与用户交流的方式。推荐系统不是提供用户搜索和潜在购买产品的静态体验,而是增加交互以提供更丰富的体验。推荐系统基于过去的购买和搜索以及其他用户的行为,自主地为单个用户识别推荐。

今天,我们将通过推荐与特定项目(在本例中为电影)最相似的项目来重点介绍一个基本的推荐系统。请记住,这不是一个真正健壮的推荐系统,更准确地说,它只是告诉你哪些电影/项目与你的电影选择最相似。

我们开始吧!

导入库

**import** **numpy** **as** **np**
**import** **pandas** **as** **pd**

获取数据

您可以通过点击这里的获取数据集。名为“u.data”的数据集中的列表示用户 ID、项目 ID、评级和时间戳,如我们在下面的代码中所定义的。

column_names = ['user_id', 'item_id', 'rating', 'timestamp']
df = pd.read_csv('u.data', sep='**\t**', names=column_names)
df.head()

Our data should look something like this.

现在让我们来看看电影名称:

movie_titles = pd.read_csv("Movie_Id_Titles")
movie_titles.head()

我们可以将它们合并在一起:

df = pd.merge(df,movie_titles,on='item_id')
df.head()

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

让我们稍微研究一下这些数据,看看一些评分最高的电影。

可视化导入

**import** **matplotlib.pyplot** **as** **plt**
**import** **seaborn** **as** **sns**
sns.set_style('white')
%matplotlib inline

让我们用平均评分和评分数创建一个评分数据框架:

df.groupby('title')['rating'].mean().sort_values(ascending=**False**).head()title
Marlene Dietrich: Shadow and Light (1996)     5.0
Prefontaine (1997)                            5.0
Santa with Muscles (1996)                     5.0
Star Kid (1997)                               5.0
Someone Else's America (1995)                 5.0
Name: rating, dtype: float64 df.groupby('title')['rating'].count().sort_values(ascending=**False**).head()title
Star Wars (1977)             584
Contact (1997)               509
Fargo (1996)                 508
Return of the Jedi (1983)    507
Liar Liar (1997)             485
Name: rating, dtype: int64 ratings = pd.DataFrame(df.groupby('title')['rating'].mean())
ratings.head()

现在设置评级列的数量:

ratings['num of ratings'] = pd.DataFrame(df.groupby('title')['rating'].count())
ratings.head()

sns.jointplot(x='rating',y='num of ratings',data=ratings,alpha=0.5)

好吧!现在我们已经对数据有了一个大致的概念,让我们继续创建一个简单的推荐系统:

推荐类似电影

现在让我们创建一个矩阵,在一个访问上有用户 id,在另一个轴上有电影标题。每个单元格将包含用户对该电影的评价。注意会有很多南值,因为大部分人没看过大部分电影。

moviemat = df.pivot_table(index='user_id',columns='title',values='rating')
moviemat.head()

最受欢迎电影:

ratings.sort_values('num of ratings',ascending=**False**).head(10)

我们选两部电影:《星球大战》,科幻电影。和骗子骗子,一部喜剧。现在让我们来看看这两部电影的用户评分:

starwars_user_ratings = moviemat['Star Wars (1977)']
liarliar_user_ratings = moviemat['Liar Liar (1997)']
starwars_user_ratings.head()user_id
0    5.0
1    5.0
2    5.0
3    NaN
4    5.0
Name: Star Wars (1977), dtype: float64

然后,我们可以使用 corrwith()方法来获得两个熊猫系列之间的相关性:

similar_to_starwars = moviemat.corrwith(starwars_user_ratings)
similar_to_liarliar = moviemat.corrwith(liarliar_user_ratings)

在执行这个命令时,将会发出一个类似这样的警告。

/Users/marci/anaconda/lib/python3.5/site-packages/numpy/lib/function_base.py:2487: RuntimeWarning: Degrees of freedom <= 0 for slice
  warnings.warn("Degrees of freedom <= 0 for slice", RuntimeWarning)

让我们通过删除 NaN 值并使用数据帧而不是序列来清理这个问题:

corr_starwars = pd.DataFrame(similar_to_starwars,columns=['Correlation'])
corr_starwars.dropna(inplace=**True**)
corr_starwars.head()

现在,如果我们通过相关性对数据帧进行排序,我们应该会得到最相似的电影,但是请注意,我们会得到一些没有实际意义的结果。这是因为有很多电影只有看过一次的用户也看过《星球大战》(那是最受欢迎的电影)。

corr_starwars.sort_values('Correlation',ascending=**False**).head(10)

让我们通过过滤掉少于 100 条评论的电影来解决这个问题(这个值是根据前面的直方图选择的)。

corr_starwars = corr_starwars.join(ratings['num of ratings'])
corr_starwars.head()

现在对值进行排序,注意标题变得更有意义:

corr_starwars[corr_starwars['num of ratings']>100].sort_values('Correlation',ascending=**False**).head()

喜剧骗子骗子也一样:

corr_liarliar = pd.DataFrame(similar_to_liarliar,columns=['Correlation'])
corr_liarliar.dropna(inplace=**True**)
corr_liarliar = corr_liarliar.join(ratings['num of ratings'])
corr_liarliar[corr_liarliar['num of ratings']>100].sort_values('Correlation',ascending=**False**).head()

干得好!

你不需要通过市场调查来确定一个顾客是否愿意在一个能最大限度帮助他们找到合适产品的商店购买。他们将来也更有可能回到这样的商店。为了了解推荐系统的商业价值:几个月前,网飞估计其推荐引擎每年价值 10 亿美元。

有更先进和非传统的方法来驱动你的推荐过程。这些技术即深度学习、社会学习和张量分解是基于机器学习和神经网络的。这样的认知计算方法可以让你的推荐者的质量更上一层楼。可以肯定地说,产品推荐引擎将随着机器学习的使用而改进。并创建一个更好的客户满意度和保留流程。

最后,我要感谢我的教授——何塞·马西亚尔·波尔蒂利亚,他在 Udemy 上的课程“Python 用于数据科学和机器学习训练营”让我很容易理解所有这些概念。

在几分钟内构建您自己的强大深度学习环境

原文:https://towardsdatascience.com/build-your-own-robust-deep-learning-environment-in-minutes-354cf140a5a6?source=collection_archive---------8-----------------------

深度学习环境配置的不太理想方面指南

Photo by Kyle Hanson on Unsplash

介绍

多亏了更便宜、更大的存储,我们拥有了比几年前更多的数据。无论大数据制造了多少宣传,我们都应该感谢它。然而,这里真正的 MVP 是更快更好的计算,这使得 20 世纪 80 年代和 90 年代的论文更加相关(lstm 实际上是在 1997 年 )发明的!由于更好更快的 CPU 和 GPU,我们终于能够利用神经网络和深度学习的真正力量。不管我们喜欢与否,传统的统计和机器学习模型在处理高维、非结构化数据、更复杂和大量数据的问题时有严重的局限性。

Source: https://twitter.com/andrewyng/status/700086119333892096

深度学习已经开始在这些领域大放异彩,我们已经慢慢地看到它在整个行业中被大规模应用于几个现实世界的问题。著名的人工智能传奇人物吴恩达几年前也提到过同样的问题!

深度学习 的好处是,在构建解决方案时,我们有更好的计算、更多的数据和各种易于使用的开源框架,如**keras****tensorflow****pytorch**可供选择。

关于深度学习不好的部分? 从零开始建立自己的深度学习环境可能是一个巨大的痛苦,尤其是如果你迫不及待地开始编码和实现自己的深度学习模型。

经历了几次这个痛苦的过程,也在我的旅程中发现了易于使用的服务,本指南旨在让你轻松地完成建立自己的深度学习环境的不太理想的方面,以便你可以更快地建立你的深度学习模型和解决问题。我们将在本指南中涵盖以下方面。

  • 最小配置的基于云的深度学习环境
  • 设置你自己的基于云的深度学习环境
  • 内部设置提示

事不宜迟,让我们开始吧!

最小配置的基于云的深度学习环境

如果你真的想在不投资专用硬件的情况下开始建立深度学习模型,或者你想跳过所有那些讨厌的配置和设置命令,有几个选项只适合你!使用预先配置的基于云的深度学习环境是最好的。通常,有几个基于云的深度学习服务提供商。以下选项使您能够立即开始工作,只需最少的设置和配置。请注意,这不是我的全面列表,而是我已经试验过或从深度学习实践者那里听到的选项。

  • 谷歌合作实验室
  • 纸空间渐变
  • FloydHub 工作区
  • Lambda GPU 云
  • AWS 深度学习 AMIs
  • GCP 深度学习 VM 图片

我们将介绍这些提供商的基本知识,以便您有足够的知识开始使用它们。除此之外,我们鼓励你走出去,进一步探索它们的细节,并根据你的喜好选择一个!

谷歌联合实验室

也许是谷歌最好的(仍然)免费的选项之一,它让你能够在 GPU 甚至 TPU 支持的深度学习环境中运行交互式 Jupyter 笔记本。谷歌一直在各个领域积极使用和推广其应用,包括其广受欢迎的机器学习速成班。简而言之,Colaboratory 是一个免费的 Jupyter 笔记本环境,不需要任何设置,甚至可以免费使用 GPU 来运行深度学习模型。更多细节可以在这篇文章中找到。

默认情况下,您会获得一个 CPU 支持的深度学习环境,其中预安装了所有库,您可以使用以下代码验证这一点。

现在,对于工作负载和数据较小的相对简单的模型,使用 CPU 是很好的,但对于更复杂的问题,您肯定需要利用 GPU。将 Google Colab 中的运行时改为使用 GPU 只需要几秒钟,如下图所示。

Google Colab 然后为您分配一个新的支持 GPU 的深度学习后端,您可以使用以下代码查看 GPU 类型。

看起来你免费得到了一个 12 GB 内存的 Tesla K80 GPU!这就是 AWS **p2.xlarge**实例给你的惊人的 0.9 美元一小时。相当整洁!最后,您可以使用以下代码来确认您的深度学习库正在使用 GPU。

这应该可以让你在 Google Colab 上尝试自己的深度学习模型。当您使用 colab 时,请随意利用my Colab notebook来测试支持 CPU 和 GPU 的深度学习环境!

[## 谷歌联合实验室

测试 CPU 和 GPU 支持的环境

colab.research.google.com](https://colab.research.google.com/drive/1zSI8QrODgmbgwDflHr1pr4aMzeGMXsjF)

纸张空间渐变

Gradient 是 Paperspace 提供的产品,paper space 是一家专注于机器学习和数据科学的轻松基础设施的公司。它为开发人员提供了一整套工具,用于探索数据、训练深度学习模型和在 GPU 上运行计算作业。Gradient 包括一键式 jupyter 笔记本电脑,由 Paperspace GPU 云的全部功能提供支持。这个介绍视频更详细地谈到了它。以下是他们的一些基本计划(进一步的细节可以在这里找到)。

你确实需要按小时支付 GPU 的使用费用,但价格与其他服务提供商相比非常有竞争力,Quadro P4000 每小时 0.5 美元,Tesla K80 每小时约 0.59 美元,仍然比 AWS 上的类似选项相对便宜。

FloydHub 工作空间

FloydHub 提供的一个有趣的产品是 FloydHub Workspace,它旨在通过在云上为深度学习提供一个完全配置的开发环境,来减少设置自己的深度学习环境的麻烦。最精彩的部分?虽然它不是免费的,但你可以无缝地从 CPU 切换到 GPU 后端,只需按每秒使用量付费!

考虑到你可以以 12 美元的最便宜价格获得一个拥有 12 GB 内存、61 GB 内存和 200 GB 固态硬盘的特斯拉 K80 专用实例,它们的价格也很不错。

Lambda GPU 云

Lambda Labs 或 Lambda 是一家人工智能基础设施公司,提供计算来加速人类的进步。他们专注于深度学习工作站,最近推出了 Lambda GPU Cloud,目前仍处于封闭测试阶段。每个 Lambda GPU 云实例有 4 个 GPU,比 AWS 的 p2.8xlarge 实例快 2 倍。他们声称您可以简单地按下按钮,立即获得对 4-GPU 实例的 SSH 远程访问。然而,定价是每小时每 GPU 0.9 美元。可以在这里 报名私测

AWS 深度学习 AMIs

亚马逊网络服务(AWS)是亚马逊的子公司,提供基于付费订阅的按需云计算平台。最近,他们推出了深度学习 ami,这是亚马逊机器映像(AMIs),专用于 GPU 密集型工作负载,用于建立深度学习模型。AWS 深度学习 ami 为我们提供了必要的基础设施和预配置的工具和框架,以大规模加速云中的深度学习。它预先配置了所有最新和最好的深度学习框架。

你可以得到 Conda AMI,它为每个深度学习框架提供了单独的虚拟环境,或者是用于配置和使用定制构建的基本 AMI。

Virtual Environments in the Conda AMIs

随意查看深度学习 AMI 指南 这里 还有如何开始使用 Conda 和 Base AMI这里 。当您选择自己的 AWS 实例时要小心,因为您是按小时收费的。最便宜的选择是给你 12 GB 的 GPU,每小时 0.9 美元(T21)。

GCP 深度学习虚拟机图片

GCP 也称为谷歌云平台,为我们提供了一套云计算服务,包括运行深度学习模型和工作负载的基础设施。最棒的是,它运行在谷歌内部用于终端用户产品的同一基础设施上。如果你注册的话,GCP 还会在第一年提供价值 300 美元的免费积分,这很酷!

谷歌云深度学习虚拟机映像使开发人员能够在谷歌计算引擎实例上实例化包含流行的深度学习和机器学习框架的虚拟机映像。您可以启动预先安装了流行的 ML 框架(如 TensorFlow、PyTorch 或 scikit-learn)的计算引擎实例。你可以在这里 查看进一步详情 。最棒的是,您还可以通过单击添加云 TPU 和 GPU 支持。价格非常有竞争力,比 AWS 便宜得多。GCP 虚拟机让你以每小时仅 0.45 美元的价格访问 12 GB Tesla K80 GPU!查看定价板 点击 了解更多信息。

这些选项应该让您对潜在选项有一个好的想法,以最少的配置和设置启动您的深度学习之旅。

设置您自己的基于云的深度学习环境

虽然云上的预配置设置非常好用,但有时您希望构建自己定制的基于云或内部的深度学习环境。在这一部分,我们将看看如何利用任何流行的云平台服务提供商,在云中构建一个健壮的深度学习环境。涉及的主要步骤如下:

  • 选择云提供商
  • 创建您的虚拟服务器
  • 配置您的虚拟服务器
  • 设置你的深度学习环境
  • 访问您的深度学习环境
  • 验证 GPU 启用

现在,让我们详细了解一下如何设置基于云的深度学习环境。

选择云提供商

如今,有多家价格合理且具有竞争力的云提供商。在前一节中,我们已经看到了其中的一些。我们希望利用平台即服务(PaaS)功能,我们只需管理我们的数据、应用和基本配置,但使用 GPU 计算进行深度学习。下图显示了深度学习实践者利用的一些流行的云提供商。

Cloud Providers having Deep Learning Instances

受欢迎的供应商包括亚马逊的 AWS、微软的 Azure 和谷歌的谷歌云平台(GCP)。

创建虚拟服务器

选择云服务提供商后的下一步是创建虚拟机实例,这基本上是一个托管代码、数据和配置设置的服务器。创建虚拟机的步骤取决于您的云提供商的选择。以下分步教程深入指导您在 AWS 和 GCP 中创建和设置自己的实例。

  • 用 AWS 深度学习 AMI 创建并设置云实例
  • 使用 GCP 市场 创建并设置云实例

我在我的书《第二章中的 【用 Python 进行实际迁移学习】 中确实涵盖了在 AWS 上创建和实例化你自己的 VM 的逐步指南。整个代码库都是开源的,如果你感兴趣的话,进一步的细节在本书的 GitHub 资源库 中。

配置您的虚拟服务器

一旦创建了实例,就可以从云提供商的平台上启动实例。通常是 AWS 中的 EC2 用户界面或 GCP 中的 VM 实例页面。现在,通常您需要一个私钥来从本地终端使用 SSH 登录到您的服务器。通常 AWS 允许你在创建 VM 的最后一步设置你自己的密钥,并给你一个可下载的私有密钥。GCP 允许您通过 SSH 使用 GCP 实例页面直接登录到您的系统。然后,如果需要的话,你可以按照这个指南来创建你自己的 SSH 密钥,如果你没有的话。

请记住将您的私有 SSH 密钥保存在一个安全的地方,并从终端使用以下命令登录到您的服务器。

恭喜你!您现在已成功登录到深度学习服务器。我们深度学习设置的其余方面将在假设您在 Linux 服务器上的情况下进行。我们的 Linux 发行版是 Ubuntu 18.10 。您可以根据自己的喜好自由选择自己的操作系统!

由于我们将广泛使用 Jupyter 笔记本电脑进行原型制作和开发,因此为笔记本电脑服务器设置密码通常会有所帮助,这样即使陌生人以某种方式获得了您的公共 IP 地址,也无法使用它。如果您不想设置密码,可以跳过本节中的密码设置步骤。这里的第一件事是使用 Open SSL 创建一个新的 SSL 证书。

如果系统中没有安装 python,我们建议使用 Anaconda 发行版,它有一个很好的包管理系统,并附带了一套预安装的库。我们推荐下面的 官方指南 来安装 Anaconda python 发行版。

下一步是为我们的 Jupyter 笔记本服务器生成一个配置文件,以防它不存在。通常该文件位于您的主目录中的**~/.jupyter/jupyter_notebook_config.py** 处,如果它不存在,您可以使用以下命令创建它。

要为笔记本电脑启用基于密码的安全性,我们需要首先生成一个密码及其哈希。我们可以如下利用**Ipython.lib**中的**passwd()**函数:

一旦您输入了密码并验证了密码,该函数将返回一个哈希,这是您的密码的哈希(在这种情况下,我键入的密码键实际上是单词 password ,这是您绝对不应该使用的!).复制并保存哈希值,因为我们很快就会用到它。接下来,启动您最喜欢的文本编辑器来编辑 Jupyter 配置文件,如下所示:

我们现在准备设置我们的深度学习环境。

设置您的深度学习环境

我们现在将开始设置我们的深度学习环境所需的必要配置,以开始使用 GPU。如果已经为您的实例配置了 CUDA 和 cuDNN,您可以根据需要跳过下面的一些步骤。

1。安装图形驱动程序

这里的第一步是确保为您的 GPU 安装了图形驱动程序。接下来,我们假设您使用的是 NVIDIA GPU。测试是否安装了驱动程序的最佳方法是从终端运行**nvidia-smi** 命令。现在,如果这个命令不起作用,我们需要安装 GPU 驱动程序。

2。安装 CUDA

NVIDIA CUDA Toolkit 基本上是一个开发环境,用于创建可以最大限度地利用 NVIDIA GPUs 的应用程序和程序。GPU 加速的 CUDA 库支持跨多个领域的嵌入式加速,包括线性代数、图像和视频处理、深度学习和图形分析。假设我们使用的是基于 Ubuntu 的系统,你可以去NVIDIA CUDA 官方页面 下载必要的安装文件。在撰写本文时,CUDA 10 已经发布,但仍然相当新。因此我们将使用传统的 CUDA 9.0 版本,你可以从 传统发布页面 获得。如果您在服务器上,最好使用终端直接下载安装文件,并使用以下命令配置 CUDA。

3。安装 cuDNN

NVIDIA CUDA 深度神经网络库(cuDNN)是用于深度神经网络的 GPU 加速原语库。cuDNN 库为神经网络中的标准例程提供了高度优化的实现,包括前向和后向卷积、池化、规范化和激活层。深度学习实践者可以依赖 cuDNN,它可以在 GPU 上加速广泛使用的深度学习框架。你可以从 官方页面 下载 cuDNN,但是你需要为此注册一个 NVIDIA 账户!您将获得 cuDNN 的下载链接,然后您可以在终端中使用该链接直接下载到服务器中。

通常,这照顾到我们的 GPU 设置的大多数必要的依赖关系。

4。安装深度学习框架

现在,我们需要安装和设置我们的 Python 深度学习框架,以防它们没有安装。我们通常使用大量的 keras 和 tensorflow,下面的命令可以帮助我们在我们的环境中安装它们。

访问您的深度学习云环境

我们真的不想一直坐在服务器上的终端中编码。由于我们希望利用 Jupyter 笔记本进行交互式开发,我们将从本地系统访问云服务器上的笔记本。为此,我们首先需要在远程实例上启动 Jupyter 笔记本服务器。

现在,如果您有一个分配给实例的公共 IP,并且端口**8888**是公开的,那么您可以直接键入**http://<IP_Address>:8888**并开始从您的本地浏览器访问您的云 VM 中的 Jupyter 服务器!

另一个选项,特别是对于 AWS 实例,是在本地实例上启用端口转发,以便从本地机器的浏览器访问我们的云服务器笔记本。这也称为 SSH 隧道。

在端口转发的情况下,转到您的本地浏览器并导航到本地主机地址,例如 https://localhost:8890,,我们将它转发到我们虚拟服务器中的远程笔记本服务器。确保你在地址中使用了**https**,否则你会得到一个 SSL 错误。

验证 GPU 启用

最后一步是确保一切都在工作,并且我们的深度学习框架正在利用我们的 GPU(我们通常按小时付费!).下面的代码应该可以帮助我们验证这一点。

看起来我们的深度学习设置被配置为使用 GPU 进行深度学习,我们已经准备好了!

内部设置提示

通常,用户或组织可能不想利用云服务,尤其是如果他们的数据是敏感的,因此他们专注于构建内部深度学习环境。这里的主要重点应该是投资正确类型的硬件和软件,以实现最高性能,并利用正确的 GPU 来构建深度学习模型。关于硬件,特别强调以下几点:

  • 处理器:你可以购买 i5 或 i7 英特尔 CPU,或者如果你想宠坏自己,也可以购买英特尔至强处理器!
  • RAM: 为你的内存投资至少 32 GB 的 DDR4\DDR5 或者更好的 RAM。
  • 磁盘:1tb 的硬盘很不错,而且你还可以投资最少 128 GB 或 256 GB 的固态硬盘来实现快速数据访问!
  • GPU: 可能是深度学习最重要的组件。购买一个 NVIDIA GPU,任何高于 GTX 1070 的最低 8 GB。

你不应该忽视的其他东西包括主板、电源、坚固的外壳和冷却器。一旦你设置好你的装备,对于软件配置,你可以重复上一节的所有步骤,不包括云设置,你应该准备好了!

结论

这份详细的实践指南旨在让开发者、工程师和深度学习实践者在几分钟内从零到深度学习。我希望这个指南能帮助你建立自己的深度学习,而不是最终花几个小时打破你的脑袋阅读论坛上的无数帖子和栈溢出来建立自己的深度学习环境。现在出去开始‘深度学习’

我利用数据科学、人工智能、机器学习和深度学习来解决现实世界的问题。我也在业余时间做一些咨询、研究和指导。如果您需要集中咨询、培训课程,希望我在活动中发言,或者如果您想发表一篇关于【TDS】的文章,请随时通过LinkedIn联系我。

* [## Dipanjan Sarkar -数据科学家-红帽| LinkedIn

查看 Dipanjan Sarkar 在世界最大的职业社区 LinkedIn 上的个人资料。Dipanjan 有 9 份工作列在…

www.linkedin.com](https://www.linkedin.com/in/dipanzan/)*

构建自己的 Whatsapp 聊天分析器

原文:https://towardsdatascience.com/build-your-own-whatsapp-chat-analyzer-9590acca9014?source=collection_archive---------1-----------------------

最近,在完成了 Udacity 课程中的一些项目后,我开始寻找新的、熟悉的数据进行分析,还有什么地方比自己的手机更好呢?

Whatsapp 声称每天发送近 550 亿条信息。普通用户每周在 Whatsapp 上花费 195 分钟,并且是许多团体的成员。

有了这个就在我们眼皮底下的数据宝库,我们就必须着手一项任务,深入了解我们的手机被迫见证的信息。本文旨在逐步指导您构建自己的 whatsapp 对话分析器,分为以下 3 个主题:

  • 数据收集
  • 数据准备
  • 数据探索

先决条件

在您开始之前,确保您的 Python 环境中安装了以下包(我推荐使用 Jupyter,因为您可以在遵循本教程中的步骤时很容易地看到中间输出):

  • 熊猫
  • 海生的
  • Matplotlib
  • Jupyter(可选但有用,因为你可以在遵循本教程中的步骤时很容易地看到中间输出)或者如果你像我一样懒,不想安装这些包中的任何一个,只需前往谷歌协作实验室(【https://colab.research.google.com】T2),这是一个免费的 Jupyter 笔记本环境,预装了所有东西,并立即开始!

数据收集

首先,我们需要一个 whatsapp 对话来分析。打开您希望分析的 whatsapp 对话(最好是群聊,因为它们往往比较大),使用“导出聊天”功能将整个对话以文本格式发送到您的电子邮件 ID。

重要提示:当 whatsapp 提示时,确保不要导出任何媒体,否则可能需要很长时间才能导出。

从您的电子邮件收件箱下载导出的聊天内容。它应该如下所示:

18/06/17, 22:45 - Messages to this group are now secured with end-to-end encryption. Tap for more info.
25/09/16, 21:50 - Nick Fury created group "Avengers"
18/06/17, 22:45 - Nick Fury added you
18/06/17, 22:45 - Nick Fury added Hulk
18/06/17, 22:45 - Nick Fury added Thor
18/06/17, 22:45 - Nick Fury added Tony Stark
18/06/17, 22:29 - Tony Stark: Here are the details for tomorrow's picnic:The park is located at 123 Main Street. Bring your own snacks, we will also be grilling. It is going to be very warm so dress appropriately. We should be getting there at noon. See you then and don't forget the sunscreen.
18/06/17, 22:46 - Hulk: HULK NO CARE
18/06/17, 22:46 - Hulk: HULK NO FRIEND HERE
18/06/17, 22:46 - Hulk: HULK HATE LOKI
18/06/17, 22:46 - Hulk: GFCHGK
18/06/17, 22:47 - Thor: Stop pressing every button in there
18/06/17, 22:47 - Loki: Why do you have 2 numbers, Banner?
18/06/17, 22:48 - Hulk: HULK FIRST SMASH YOU THEN TELL YOU

数据准备

Background photo created by freepik — www.freepik.com

就像生蔬菜必须用各种香料烹饪和点缀才能使它们对人类可口一样,这个纯文本文件也必须以有意义的方式进行解析和标记,以便在熊猫数据帧中提供(存储):

让我们只考虑文本中的一行(我们称之为“raw text”),看看我们如何从中提取相关的列:

18/06/17, 22:47 - Loki: Why do you have 2 numbers, Banner?

此后,每当我希望把你的注意力吸引到字符串 s 中的不同记号时,我会向你展示两行。

  • 第一行将显示{花括号}中的标记名以及它们在 s 中的相对位置。
  • 第二行将是字符串 s ,它被修改以指示对应于每个标记的实际值,用花括号括起来。

例如,如果字符串 s 是“单词 1,随机单词单词 2:”,那么我将提供以下定义:

{Token 1}, random word {Token 2} {Token 3}
{Word 1}, random word {Word 2} {:)}

由此,您将能够推断出令牌 1 的值是“单词 1”,令牌 2 的值是“单词 2”,令牌 3 的值是“😃”。

在我们的示例文本行中,我们的主要目标是将原始消息自动分解成 4 个令牌,我们将在下一节中了解如何完成这项任务:

{Date}, {Time} - {Author}: {Message}
{18/06/17}, {22:47} - {Loki}: {Why do you have 2 numbers, Banner?}

步骤 1:检测{Date}和{Time}令牌

首先,为了检测一行文本是新消息还是属于多行消息,我们必须检查该行是否以日期和时间开始,为此我们需要一点正则表达式 (regex) 匹配(不要担心——一旦你把它分解,就没那么令人困惑了,特别是用一些我马上会展示给你的漂亮工具)。让我们定义一个名为 startsWithDateTime 的新方法:

下图显示了正则表达式匹配如何检测邮件中的日期和时间:

下图简要概述了在示例文本文件中检测到的所有消息:

我不会深究正则表达式实际上是如何工作的细节,但如果你感兴趣,你可以通过访问https://regex101.com/和https://medium . com/tech-taja wal/regular-expressions-the-last-guide-6800283 AC 034找到关于这种匹配是如何完成的更多解释。

回到我们的示例行,在我们运行 startsWithDateTime 方法之前,在我们的原始示例消息中没有检测到令牌:

{Raw Message}
{18/06/17, 22:47 - Loki: Why do you have 2 numbers, Banner?}

在我们运行了 startsWithDateTime 方法之后,在我们处理的示例消息中检测到了2令牌:

{Date}, {Time} -Message
{18/06/17}, {22:47} - Loki: Why do you have 2 numbers, Banner?

步骤 2:检测{Author}令牌

现在我们已经确定了包含带有日期和时间组件的新消息的行,让我们移动到消息的下一部分(hypen 之后的所有内容):

Loki: Why do you have 2 numbers, Banner?

同样,我们将需要更多的正则表达式匹配。我们的目标是找出这条信息的作者。虽然根据您在手机联系人应用程序中保存朋友姓名的方式,可能有多种模式,但我确定的最常用模式如下(您可以随意添加或删除您认为合适的任何规则):

记住这些规则,现在让我们定义一个名为 startsWithAuthor 的方法,该方法查找至少与上述规则之一匹配的字符串:

下图显示了正则表达式匹配如何检测邮件中的作者:

下图简要概述了示例文本文件中检测到的所有作者:

你可以通过访问https://regex101.com/找到更多关于如何匹配的解释

在我们运行 startsWithAuthor 方法之前,已经在我们处理的示例消息中检测到了2令牌 s:

{Date}, {Time} -Message
{18/06/17}, {22:47} - {Loki: Why do you have 2 numbers, Banner?}

在我们运行了 startsWithAuthor 方法之后,在我们处理过的示例消息中检测到了 4 个令牌

{Date}, {Time} - {Author}: {Message}
{18/06/17}, {22:47} - {Loki}: {Why do you have 2 numbers, Banner?}

注意:您可能想知道消息令牌是如何凭空出现的。好了,一旦我们检测到了日期、时间作者标记,我们剩下的就是字符串的剩余部分,也就是事实上的消息标记

步骤 3:提取和组合令牌

既然我们已经能够识别单个消息中的日期时间作者消息标记,那么是时候根据分隔符标记(如逗号(,)、连字符 (-)、冒号(:)和空格())来拆分每一行了,以便可以这一次,让我颠倒一下,突出显示分隔符标记,而不是日期、时间作者消息标记:

Date{Comma }Time{ Hyphen }Author{Colon }Message
18/06/17{, }22:47{ - }Loki{: }Why do you have 2 numbers, Banner?

让我们定义一个名为 getDataPoint 的新方法,用于根据分隔符标记分割字符串以提取感兴趣的标记:

示例输出值显示在每行旁边的注释(#)中。

注:计算作者令牌的值何时可以为 None 留给读者作为练习。

步骤 4:解析整个文件并处理多行消息

我们已经到了数据解析的最后阶段,为此我们必须读取整个 whatsapp 文本文件,从每一行中识别和提取标记,并在一个列表中以表格格式捕获所有数据:

使用以下代码初始化 pandas 数据帧:

你会发现你所有的数据列表如下(看起来很整洁,不是吗?):

数据探索

Image obtained from https://www.flocabulary.com/lesson/age-of-exploration/

最后,我们到达了旅程中最激动人心的部分之一——数据探索。是时候让我们挖掘这些数据试图告诉我们的有趣故事了。

描述数据框

首先,让我们看看熊猫对我们的数据帧 (df) 有什么看法:

该命令显示数据帧中每一列的条目数量(计数)、唯一条目、最频繁出现的条目(顶部)和最频繁出现的条目(频率)

输出可能如下所示:

非常健谈

谁是最饶舌的成员?我们来看看群里前 10 位作者的发消息量。

没有作者的神秘消息!

还记得几节之前,我给你做了一个练习,让你知道某些消息的作者可能是 None 吗?好吧,如果你还没有弄明白,你不必担心,因为这一部分的结果可能会给你一个线索。

让我们使用下面的代码找到所有没有作者的消息:

你在这些信息中看到了什么规律吗?

媒体超载

浏览原始文本文件或整个数据框时,您可能会注意到包含以下字符串的消息: " <媒体省略> " 。这些消息代表任何图片、视频或音频消息。

让我们使用以下代码找到所有媒体消息,并分析组中发送媒体消息的前 10 名作者发送的媒体消息的数量:

你能看出发送信息最多的作者和发送媒体信息最多的作者之间有什么不同吗?

我们不需要媒体也不需要鬼魂

由于我们仅限于分析组中朋友发送的纯文本消息,因此让我们创建一个新的数据帧 (messages_df) ,通过丢弃所有媒体消息或没有作者的消息,使用前两部分获得的数据帧:

这一步可以归类为数据清理。如果您还想深入了解整个非文本数据,可以跳过这一步。

如果你不收集任何指标,你就是盲目飞行。

计算每个作者在每条消息中使用的字母和单词的数量可能会很有趣。因此,让我们使用下面的代码,向名为“Letter _ Count”“Word _ Count”的数据帧添加 2 个新列:

这一步可以归类为数据扩充

现在,让我们描述清理和扩充的数据帧。这里需要注意的一个要点是包含连续值的列与包含离散值的列之间的区别:

尝试在不指定任何列的情况下对整个数据框运行 describe 命令。你观察到了什么?

“艺术的殿堂是用语言建造的”和“l”,“e”,“t”,“t”,“e”,“r”,“s”

让我们后退一步,看看整体情况。自从有时间以来(在这种情况下,恰好是从这个团体诞生的那一刻起),已经发送了多少单词和字母?

运行这段代码发现,复仇者联盟使用了多达 1,029,606 个字母和 183,485 个单词。

"当然,语言是人类使用的最有效的药物."

有史以来,每个作者总共发了多少字?

一条信息中最常见的字数是多少?

看起来大多数消息只包含一个单词。我想知道那个词是什么!

能够给别人写一封是多么美好的事情啊!

同样计算自时间开始以来每位作者发出的信件总数有意义吗?好吧,既然我们已经有了一个“信件数量”专栏,我看不出这样做有什么坏处。所以,现在开始:

信息中最常见的字母数是多少?

看起来大多数邮件只包含 1 或 2 个字母。嗯!很有意思!这些字母是来自英语还是其他符号?

记得,记得,十一月五日火药叛国和阴谋

你知道你们组历史上发送消息最多的日期吗?好了,不要再害怕了,因为你马上就会发现:

2017 年 9 月 22 日是最活跃的日期。这是灭霸袭击的日子吗,让每个人都惊慌失措,充满疑问?你得到了几号?你记得这一天有什么重要的事情发生吗?

时间是一种幻觉

你晚上睡不着觉,想知道一天中什么时候你的团队最活跃吗?真相将被揭露:

看起来这个组织大多在晚上 8:15 左右活动。请务必在此时发送消息,以便获得更快的回复。

当前的需要

一天中什么时候最适合发信息来增加你得到某人回复的机会?

为了回答这个问题,我们必须扩充数据框,以包含一个小时的新列(从“时间”列中提取),如下所示:

现在,您只需运行类似于获取热门日期和时间的代码:

看起来下午 6 点到 7 点之间发信息最有可能引起团队成员的回应。

结论

恭喜你!您现在对 whatsapp 对话更加了解了!那些是相当多的见解,不是吗?你从谈话中获得了什么样的见解?你觉得它们有用吗?你有没有发现这里没有提到的其他有用的见解?请随意在评论区添加您的想法,以便我改进本指南。

这种分析并不是探索和分析的全部。我还没有考虑到像表情符号这样的东西,它们在对话中使用得相当多。或者他们是?我应该抛弃标点符号吗?为什么大多数消息只包含一个单词?

似乎我们的分析在回答了很多问题的同时,也打开了新问题的潘多拉魔盒。嗯,这不是结束。请继续关注不久的将来的一篇文章,在那里我将尝试更深入地挖掘这些问题。

在那之前,再见了,朋友们,祝探索愉快!
来自我的问候……耶我!

打造您的个人语音助手

原文:https://towardsdatascience.com/build-your-personal-voice-assistant-cec7785508da?source=collection_archive---------10-----------------------

通过语音命令在计算机上执行任务

我一直对 Siri、Alexa、谷歌助手或任何其他语音助手的工作方式很着迷。它通过自动化任务使用户的生活变得更加容易。因此,我决定构建自己的语音助手,它将从我的笔记本电脑上的麦克风收集命令,并使用它来执行任务。

资料组

我选择了 5 个我觉得任何人都可能经常使用的简单命令,并决定录下自己说这些命令的过程。我假设当用户说出命令时不会有任何背景噪音。这使得事情变得容易得多,因为我不必担心从噪音中分离出实际的命令,这需要对音频进行一些预处理。

这五个命令是:

  • ‘降低音量’—降低系统音量
  • ‘增加音量’—增加系统音量
  • ‘打开谷歌’—在浏览器中打开谷歌网页
  • “显示 CPU 利用率”—显示正在运行的进程的性能详细信息
  • “拍照”—使用笔记本电脑摄像头点击照片

因为我将使用深度学习模型来预测命令,所以我将需要每个类的大量数据。我使用下面的脚本,通过我的笔记本电脑麦克风为每个命令录制了 20 个样本,其中节奏和音调略有不同。每个样本的长度为 2 秒,刚好大于任何人说出任何命令所需的平均时间。

https://gist.github.com/grohith327/deedd75d73070e7667eef1977162a390

我们可以从记录的 20 个样本中随机抽取 1 秒的作物,并附加 1 秒的静默。这将为我们提供每门课的大量数据。我们使用下面的脚本获取 200 个这样的作物。

https://gist.github.com/grohith327/2f9121157039840a77b5d527e2313f06

现在我们已经为每个类准备了大约 220 个样本(200 个裁剪样本和 20 个原始样本),我们可以考虑对音频样本进行一些预处理。

音频预处理

录制的语音命令存储为。wav 文件,如果我们能得到音频文件的声谱图,我们可以把它当作一个图像,并把它输入 CNN,对音频进行分类。幸运的是,python 库 librosa 让事情变得简单多了,我们可以很容易地用这个库生成音频的频谱图。以下脚本用于生成声谱图。

https://gist.github.com/grohith327/0e6b2071b8e3f57961a24d06c495fe33

为了生成声谱图,我们对音频数据进行短时傅立叶变换。傅立叶变换用于将信号从时域转换到频域,我们这样做是为了了解信号变化的程度。短时傅立叶变换是一种扩展,它采用小窗口,将其与信号进行卷积,并在卷积窗口内应用 DFT,现在我们在信号上移动窗口,迭代执行 DFT。

我们可以使用上面的脚本可视化所有五个命令的频谱图。

我们可以观察到命令“增大音量”和“减小音量”似乎具有相似的频谱图,因为单词“增大”和“减小”之间只有很小的差别。

我们现在可以为每个音频样本生成声谱图,并使用以下脚本将其转储到 numpy 数组中。(注意:我们填充光谱图,以便所有维度匹配)

https://gist.github.com/grohith327/ce30475cd508e0f196fb4face38f47ca

既然我们已经将音频样本及其相应的标签保存到了一个. npy 文件中,我们可以随时加载它们,而不是每次都生成频谱图。

构建模型

我们可以将频谱图视为图像,并尝试从这些图像中识别有助于我们识别音频样本类别的特征。我们可以使用卷积神经网络,它将图像作为输入,学习图像的空间特征并预测类别。

在构建模型之前,我们将数据分为训练集和测试集。我们还使用下面的脚本将标签转换成一个热编码。

https://gist.github.com/grohith327/3d3ab1759f6a7a41654cbedbcd5dd7e8

我们现在可以开始构建卷积神经网络,为此我们将使用 TF 2.0 作为我们的框架。要了解如何在 Tensorflow 2.0 中构建 CNN,请点击此处。

https://gist.github.com/grohith327/3ff5da311fe03e0a8cc615bf7435ae83

我们现在可以为 15 个时期训练我们的模型,我们评估我们的交叉熵损失模型,并使用 RMSProp 作为我们的优化器。

https://gist.github.com/grohith327/3ff5da311fe03e0a8cc615bf7435ae83

我们的模型学习频谱图的内在特征,并根据语音命令的类型区分它。经过 15 个时期的训练,我在测试数据上观察到了 96%的准确率。我们现在可以保存我们的模型,并使用它来预测我们的语音命令的类别并执行任务。

自动化任务

我们使用笔记本电脑的麦克风连续记录音频,并将其分成 2 秒钟的样本,然后输入到我们的模型中进行预测。如果我们的模型能够以高置信度对命令进行分类,我们就执行该任务。以下是通过 python 执行每个命令的脚本。(注意:根据操作系统,某些命令可能需要不同的库来执行,以下脚本适用于 macOS)

降低音量

https://gist.github.com/grohith327/9192644cc40d2d1a2c0cc963dc5520ca

增加量

https://gist.github.com/grohith327/5a7140d573b9416175d0b334da516260

打开谷歌

https://gist.github.com/grohith327/102a2ec2e16a81956a7bc2a7a0a248a5

显示 CPU 利用率

https://gist.github.com/grohith327/41c810a36097905747f0d5aad57c3500

照相

https://gist.github.com/grohith327/becb9a96274d04a8e97cac5a6e23abdf

我们现在可以编写一个脚本,它可以无限期地通过笔记本电脑麦克风监听语音命令,并将其分成 2 秒的间隔,然后使用该样本从 CNN 模型中获得预测。如果我们的 CNN 模型能够以高置信度对样本进行分类(这是因为我们不想在没有命令或静默的情况下执行任务),我们就执行该任务。代码如下。

https://gist.github.com/grohith327/27b51531eb7f27f3de26be4af53a45cb

https://gist.github.com/grohith327/27b51531eb7f27f3de26be4af53a45cb

演示

结论

这种方法有点天真,因此它并不完美,正如你在上面的视频中看到的,它两次都没有识别出命令,但是,这是我们可以在短时间内构建和测试的东西(也很有趣!).

这只是你能做的一个例子。如果你愿意,你可以选择通过不同的命令来自动完成不同的任务,甚至可以扩展命令的数量,并且仍然可以获得很好的准确性。

参考

[## 利布罗萨/利布罗萨

一个用于音乐和音频分析的 python 包。参见 http://librosa.github.io/librosa/的完整参考手册…

github.com](https://github.com/librosa/librosa)

用这些数据为好的项目想法建立你的投资组合

原文:https://towardsdatascience.com/build-your-portfolio-with-these-data-for-good-project-ideas-919254357a66?source=collection_archive---------8-----------------------

Credit: Unsplash

从数据可视化到数据科学,获得创造的灵感。

这篇文章的灵感来自于我在 数据中的女性*打造你的数据科学工具箱 研讨会上的一次演讲,题目是“数据(科学)为善”*

在我成为数据科学家的过程中,我建立了自己的项目组合,这些项目展示了我的硬技能,同时也为社会事业做出了贡献。例如,当我注意到 sports analytics 没有太多关于女子运动队的分析时,我使用了一种无监督的机器学习算法来建立 WNBA 球员的梦之队。或者当我不断听到人工智能中的偏见时,我决定建立一个机器学习模型来减轻 AI 中的偏见。在这两个项目中,我展示了作为一名数据科学家的艰苦技能,同时也为更大的利益做出了贡献并坚持了我的价值观。为了回馈数据专业人士社区,我想分享一些资源和技巧,以帮助您为优秀项目增加数据。

放下恐惧,开始吧

Credit: WOCinTech Chat.

我想与你分享的第一个技巧是,放下恐惧,开始使用你已经掌握的数据项目工作技能。在我成为数据科学家的早期,我最喜欢使用 Excel 来解释数据。当时,我不知道如何用 python 开发机器学习模型。利用我当时拥有的技能,我本可以开始从事数据驱动的项目——但唯一阻止我的是恐惧。如果您正处于学习成为数据专家的专业技能的初级阶段,您仍然可以使用现有的技能从事项目工作。我希望我能回到过去,告诉自己这些,但是因为我不能,所以我写这篇文章来告诉你——开始吧!我试着在这里包含一些资源,每个人都可以从中受到启发,不管你是新手还是专家。

创建项目工作流

Download the Data Science Workflow Canvas.

不管你是否知道你想从事哪种数据驱动的项目,或者你还不确定,开始集思广益吧。头脑风暴、制定战略、构思——这些词的一个共同点是租户认为没有所谓的“错误”想法。对于任何想要一种结构化方式来头脑风暴数据科学项目工作流的人,我设计了这幅画布(如上图)来帮助你开始。如果你需要其他工具来帮助你制定项目工作流程,你也可以尝试手绘或数字化思维导图,使用设计思维工具,或者简单地打开一个空白的 word 文档(或者如果你是模拟型的人,翻到笔记本的空白页)并开始写下你的想法。

良好数据集的数据

Credit: Unsplash

既然您已经有了如何制定项目工作流程战略的想法,您就可以开始使用这些数据换好的数据集了。或者你也可以收集你的数据,或者出去收集你自己的数据。我在下面列表中分享的大多数门户网站都提供易于下载的数据集。这些资源中的一些并不纯粹关注“数据的好处”,但仍然有相关的数据集可以下载。最后,这些只是众多数据集的一小部分!

  • 人道主义数据交换
  • 美国主要博物馆中艺术家的多样性
  • 民权数据收集
  • 政府开放数据库:美国开放数据,美国城市开放数据
  • 气候数据
  • 性别数据门户
  • 健康数据
  • Kaggle 的数据科学为好:洛杉矶市
  • 穿越时间线
  • 多样性数据
  • FiveThirtyEight:我们的数据
  • 或者你可以使用谷歌的数据集搜索来找到其他东西

数据可视化

Credit: Amazon

数据可视化是一个强大的工具,可以传达您从数据分析中收集到的见解,尤其是如果您正在进行一个有益于数据的项目。这些是我最喜欢的展示数据可视化的资源。

  • 杜波依斯的数据画像:可视化美国黑人
  • 莫娜·沙拉比
  • 乔尔吉亚·卢皮

出版物/博客

Source: Gayta Science

这些只是一些以数据驱动的文章集合为特色的博客和/或出版物。

  • 布丁
  • 盖塔科学
  • 她的篮圈统计数据
  • 空间研究中心
  • 数据女权主义 ( 帽尖米切尔·博曼与我分享这个!)
  • 数据&社会

介入

Credit: Unsplash

有很多方法可以参与进来,围绕“数据为善”的理念建立社区这里有一些你可以参与的方法,无论是通过为一个组织做志愿者还是参加一个会议。

  • 黑人生活数据
  • AI4All
  • LatinX in AI
  • 数据种类
  • 数据中的女性
  • AI 中的黑色
  • 机器学习和数据科学领域的女性
  • 数据为好:加拿大
  • R-女装
  • 皮拉迪斯
  • 酷儿在艾

结论

Credit: Unsplash

我希望这些资源能激励您创建数据为善的项目!请记住,这些资源只是可用资源的一个快照。如果你想分享更多你参与过的资源或项目,请在下面评论!

感谢 Anuva Kalawar 总是与我分享数据换好处的资源。

Jasmine Vasandani 是一名数据科学家和战略家。你可以在这里了解她的更多:【https://github.com/jasminevasandani/about_jasmine】。

为人工智能构建后端系统

原文:https://towardsdatascience.com/building-a-backend-system-for-artificial-intelligence-c404efade360?source=collection_archive---------23-----------------------

构建可扩展“人工智能”系统的挑战

让我们探讨一下构建一个后端系统来存储和检索高维数据向量所面临的挑战,这是使用“人工智能”的现代系统的典型特征,包括图像识别、文本理解、文档搜索、音乐推荐等

在我的上一篇文章中,我提到了一个我写的用来替代第三方 API 的系统。新系统由三部分组成:
1。领域逻辑服务—“人工智能”
2。存储检索服务
3。后备数据存储(Redis)

域逻辑服务处理输入数据,并生成代表数据的 1024 维规范化向量。然后传递给存储和检索服务进行持久化。
在检索时,域逻辑服务从输入中产生另一个向量,该向量使用相同的处理逻辑和存储进行转换&检索服务的任务是产生一组已经保存在系统中的与给定输入最相似的向量。

以这种方式分解系统的原因是因为存储和检索本身就是一个具有挑战性的问题。为了隔离问题,我将它放在一个自包含的服务中。下文旨在解释原因。

向量相似度

因为我们使用向量,所以让我们将两个向量的“相似性”定义如下:
如果
a) a 与 b 相同或者
b)向量 a 与 b 之间的距离小于向量 b 与数据集中任何其他向量之间的距离
并且该距离低于给定阈值,则向量 a 与输入向量 b 相似。

向量之间的距离有不同的计算方法:
1。欧几里德距离
2。余弦距离
3。笛卡儿及其思想的

Visual representation of euclidean distance (d) and cosine similarity (θ)

这是欧几里德距离(d)和余弦相似度(θ)的直观表示。余弦着眼于向量之间的角度(因此不考虑它们的重量或大小),欧几里得距离类似于使用尺子来实际测量距离。

当向量的大小无关紧要时,余弦相似性通常用作测量距离的度量。这正是我们在这里要用的。我们的向量标准化地进入存储和检索服务(所有向量都是长度为 1 的单位向量)。

既然我们已经确定了寻找“相似”向量意味着什么,那么让我们看看真正的挑战——检索相似向量集。

挑战

寻找相似向量的强力方法是获取输入并计算与数据集中每个其他向量的余弦相似性。将 vector 与“所有其他向量”进行比较并不能很好地扩展(它是线性的— O(n)),考虑到向量的维数,您可以看到当数据集增长时,这变得非常无效。如果你需要一个每秒处理大量查询的实时系统,那就不太现实了…

局部敏感散列法

相反,我们将使用本地敏感散列法(LSH)。在计算机科学中,LSH 是一种算法技术,它以很高的概率将相似的输入项散列到相同的“桶”中(桶的数量远小于可能的输入项的总数)。

LSH 主要不同于传统的散列法(又名加密),因为加密散列法试图避免冲突,而 LSH 的目标是最大化相似输入的冲突。

对于 LSH,彼此靠近的向量(“相似”)具有相同的散列值,而远离的向量具有不同的散列值(“不相似”)。
这为我们提供了一个潜在的解决方案——如果我们使用 LSH 散列我们的向量,我们可以减少匹配检索所需的向量比较次数。
我们可以将搜索限制在一个桶(或一组桶)中,以显著减少计算余弦距离的向量数量。
但是,如果我们考虑具有数百万个向量的数据集,那么即使在一个桶中,要搜索的向量的数量仍然很大。稍后会详细介绍。

然而,这是有代价的。在之前的 LSH 定义中,你应该关注的关键词是“概率”。LSH 不是一个精确的算法,它使用近似值。不能保证数据集中的相似向量最终会出现在同一个桶中。这取决于我们提供给算法的参数,即相似的向量具有相同散列的“可能性”有多大。

这里需要注意的一件重要事情是,对于我的用例,相似性截止(阈值)是余弦距离 0.36,这非常宽,这使得 LSH 的使用更加困难,因为你需要考虑更广泛的向量集。LSH 最擅长处理非常接近的向量。

幸运的是,我们有一个优势。我们不需要一个最相似的向量。我们可以近似。一旦找到第一个距离小于阈值的向量,我们就可以在桶内终止搜索。我们举个面部识别的例子。这里,系统中的每个向量代表一张脸。但是每个人都有多张相关的脸(他或她的多张照片)。假设我们正在使用另一张脸作为输入来搜索一个人。我们不需要检索单个最近的向量——最相似的脸。我们只需要一张足够“接近”的脸(低于指定的阈值)。根据定义,该阈值应该保证低于该阈值的每个这样的向量代表同一个人。

在为本文收集笔记时,我决定对我当前的实现进行测试,以了解我的系统的性能,并看看我是否可以对它进行改进。好家伙,我学到了很多东西!

基线

因为我的目标是观察(并希望改进)我当前的实现,所以我首先必须建立一种方法来测量被观察的系统,然后建立一个我们试图改进的基线。

我很幸运,因为我已经开发和测试我的系统有一段时间了,我有来自测试用户的“真实世界”数据可以使用。
为了设置我的实验,我手工挑选了大约 250 个向量的数据集作为我的系统的种子,并挑选了近 3000 个向量用于相似性检索
(即“250 个种子向量中有任何一个与 3000 个测试向量中的每一个相似吗?”).如果你要谈论生产规模,这不是一个巨大的数据集,但请记住,我们希望用它来观察我们算法的准确性,而不是硬件性能。为此目的,它应该是足够的。同样值得注意的是,这个数据集不能与用于训练人工智能子系统的数据集混淆。这已经在数百万个数据点上进行了预先训练,超出了我们今天讨论的范围。

为了建立准确性基线,我使用了简单的“蛮力”方法。这样,我总是保证得到最准确的结果,代价是将我的输入与“数据库”中的每一个向量进行比较。这就是可怕的 O(n)线性性能。

“匹配”总数为 673,相似性分布如下:

Similarity distribution histogram for baseline scenario (no approximation)

显而易见,在这种情况下找到“正确”匹配的概率总是 1。也就是说,如果一个相似的向量存在,它将被检索。查看不同算法的概率图将是理解其准确性的关键。

Probability of retrieving correct match in brute-force approach

接近

接下来,我决定测量我当前实现的算法。与 LSH 一起进入问题近似的世界。理想情况下,我们希望 LSH 降低搜索的复杂性(比线性更好),但保持准确率在——或接近——100%。为了实现这个圣杯,我们需要提出一个阶跃函数,将所有低于阈值的向量散列到同一个桶中,将所有其他向量散列到不同的桶中。如果我们在特定的桶中搜索向量,我们会大大减少搜索空间,但我们保证会找到相似的向量(假设它们存在于我们的数据集中)。

Ideal case — step function

首先,我们需要从输入向量中计算二进制散列。为此,我们将空间划分为 n 个随机平面。对于每一个平面,矢量都位于该平面分隔的两个区域之一。如果向量在正侧,我们赋值 1,如果向量在负侧,我们赋值 0。直觉上,两个文档越接近,它们越有可能在随机平面的同一区域。例如,如果两个文档几乎在彼此之上,那么对于你能想到的大多数平面来说,它们将在相同的区域结束。

对于随机绘制的平面,可以从数学上证明两个文档 x 和 y 以概率散列到相同的值:

这个表达式概括了我们的直觉:两个文档越接近(小θ),它们出现在同一区域的可能性就越大。

如果我们仅使用一个平面将我们的空间分成两半,则两个向量具有相同散列的概率将是:

Probability of two vectors having same single-bit binary hash

您可以将这解释为余弦相似度为 0.36(阈值)的两个向量被散列到同一个桶中的概率大约为 0.72 (72%)。这起初听起来不错,但这也意味着在 0.38 (38%)的情况下,相似的向量将散列到不同的桶(因此我们不会找到我们的匹配)。我们只减少了 50%的搜索空间。考虑到我们损失了相当多的准确性,这不是一个好的优化。

让我们添加更多的随机平面,最终得到一个如下所示的散列:

01110011

(通过将空间除以 8 个随机超平面构建的 8 位散列)

现在我们的概率降低了。向量不太可能散列到同一个桶中,除非它们几乎相同。如果你还记得的话,这正是我们早期关于 LSH 建立的——该算法最适合非常接近的匹配(向量之间非常小的余弦距离)。

Probability of two vectors having same 8bit binary hash

但是我们可以采用不同的技术来改善我们的处境。

  • 探索附近的桶
  • 考虑部分哈希匹配

附近的桶

为了探索附近的桶,我添加了汉明距离散列变量。这个想法很简单:一旦我们获得了 LSH 散列,我们就计算该散列在 n 个距离内的变化。这允许对根据我们的标准相似但最终在不同桶中的向量进行纠错。

如果我们取前面例子中的散列,汉明距离 1 的变化将是:

11110011
00110011
01010011
01100011
01111011
01110111
0111000【T19

对于汉明距离 2,我们得到:

10110011
11010011
11100011
11111011
11110111

00010011
0010011
0111011
00110111
00【T77

01000011
01011011010111
0101000
1
01

等等。

在我的例子中,我选择的汉明距离为 3,这总共产生 1+8+7 * 8/2+6 * 7/2+5 * 6/2+4 * 5/2+3 * 4/2+2 * 3/2+1 * 2/2 = 93 个变量。这给了我们找到正确匹配的概率:

8bit hash with Hamming distance 3

这比前一种情况好得多,曲线的形状开始类似于理想的阶跃函数。但是在准确性(近似值)和性能(获得结果所需的时间/资源)之间有一个明显的折衷。

与基线相比,这种配置给出了令人满意的 93%的准确度,并且子线性复杂度在搜索空间中提供了平均 63.7%的减少。让我解释一下:在一个假设的场景中,我们的数据库中有 100 万个均匀分布的向量,我们在 256 个桶的每个桶中保存了大约 3906 个
向量(8 位哈希= 256 个不同的桶)。我们每个查询需要搜索 93 个桶,这给了我们大约 363281 个向量要搜索(100 万个存储向量的 36.3%)。但是我必须强调的是,这些计算是理想化场景中的平均结果(均匀分布的向量),实际上,向量会更集中在一些桶中,从而为不同的查询产生非常不同的结果。

这是一个适度的改进,但也让我们损失了 7%的准确性。换句话说,在 7%的情况下,数据库中有一个相似的向量,但我们没有找到它。

为了完整起见,让我解释一下实际的数据持久性是如何实现的。我使用 Redis,每个散列都简单地转换成数据存储中的一个键。Value 是一个 Redis 向量列表,它将与查询响应中返回的元数据一起被搜索(例如,在音乐推荐服务中,元数据可以是一首歌曲的名称、一个艺术家等等)。

部分哈希匹配

作为最后一个实验,我尝试了一个略有不同的算法,这是我在研究 LSH 时发现的。

对于这个实验,我们生成更长的散列,但是将它分成多个组,并搜索每个桶,其中至少有一个散列组与查询中的一个组相匹配。

输入:011100111100100111001100

(24 位哈希分为 3 组)

如果我们有两个不同的向量存储在系统中,哈希为

答:00110010 11001001 10001100

b:110000111100100111001000

我们的输入向量将被认为类似于向量 b,因为第二组(11001001)匹配输入的第二组。

直到后来,当我开始分析我的结果时,
我才意识到我犯了一个错误,我只使用了 24 位散列分成 3 组。这第三个实验不可能超过我当前的实现。原因就摆在我面前:

24bit hash split in 3 groups

一旦我计算了概率图,我就明白了为什么与基线相比,我只看到了 71%的准确性。散列余弦距离为 0.36 的两个向量的概率下降到大约 22%。
虽然性能更好,但在我们拥有 100 万个向量的理想场景中,使用该算法我们只需比较(平均)约 12k 个向量,也就是说我们将搜索空间减少了 98.8%。

因为我们将散列分组,并且需要单独考虑每个组,所以我们需要不同的机制来查询 Redis 后端。

对于样本散列 01110011 11001001 11001100,我们将其分解为 3 个 Redis 键:

01110011 xx
x 11001001 x
xx 11001100

我们将向量存储在三个键的每个键下。这造成了数据的重复,但是当只有组成散列的一些组匹配时,允许我们找到向量。
在返回结果之前,我们还需要引入一个额外的步骤来过滤掉重复的结果。

我们能做得更好吗?

这是我一直在思考的事情。不仅仅是我——这是相当前沿的东西,许多比我聪明得多的人都在花大量时间开发新算法来查询大量高维数据。

我尝试了不同的配置来微调系统的精度和性能。但是到了最后,这仅仅是边际改善,虽然很重要,但并没有显著改变观察到的行为和(最好的)亚线性性能。

我也试着从不同的角度看待这个问题。我的系统存储了大量的向量,这些向量可能在存储后不久就会被使用,但更多的时候根本不会被使用。在大多数情况下,只有很小一部分向量最终会被返回为最相似的向量。

如果我们更积极地减少数据集的整体大小会怎么样?

我的计划是实现生存时间(TTL),这是缓存中众所周知的概念,只在特定的时间窗口内保持向量“已使用”(已检索)(并且在每次使用向量时重新创建 TTL,甚至可能使用滑动窗口—第一个 TTL 为 1 小时,向量“已使用”后,下一个 TTL 将为 1 天,然后是 1 周,依此类推)。结合禁用某些向量的 TTL 的能力,我应该可以从数据集中清除大量向量,而不必太担心算法的性能。

未来要考虑的另一件事是并行处理。我们可以将数据集划分到不同的节点上进行存储和查询,然后将部分结果组合成最终结果(map-reduce)。或许,这将是另一个有趣的话题?

用 Python 和 PyMC3 构建贝叶斯逻辑回归

原文:https://towardsdatascience.com/building-a-bayesian-logistic-regression-with-python-and-pymc3-4dd463bbb16?source=collection_archive---------6-----------------------

我认购定期存款的可能性有多大?后验概率,可信区间,优势比,WAIC

在本帖中,我们将探讨如何使用贝叶斯逻辑回归来预测客户在银行开展营销活动后是否会认购定期存款。

我们希望能够实现:

  • 客户认购定期存款的可能性有多大?
  • 变量选择技术实验。
  • 对变量 so 的探索是探索性数据分析的一个很好的例子,它可以指导模型的创建和选择过程。

我相信你熟悉数据集。不久前,我们使用标准的机器学习方法对这个数据集建立了一个逻辑回归模型。今天我们将应用贝叶斯方法来拟合逻辑回归模型,然后解释由此产生的模型参数。我们开始吧!

数据

此数据集的目标是创建一个二元分类模型,该模型根据许多指标预测客户在银行开展营销活动后是否会认购定期存款。目标变量被给定为y,如果客户已经订阅,则取值 1,否则取值 0。

这是一个类别不平衡的问题,因为没有认购定期存款的客户明显多于认购定期存款的客户。

import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
import pymc3 as pm
import arviz as az
import matplotlib.lines as mlines
import warnings
warnings.filterwarnings('ignore')
from collections import OrderedDict
import theano
import theano.tensor as tt
import itertools
from IPython.core.pylabtools import figsize
pd.set_option('display.max_columns', 30)
from sklearn.metrics import accuracy_score, f1_score, confusion_matrixdf = pd.read_csv('banking.csv')

作为 EDA 的一部分,我们将绘制一些可视化图形。

  • 使用 seaborn 的stripplot函数探索目标变量与客户年龄的关系:
sns.stripplot(x="y", y="age", data=df, jitter=True)
plt.show();

Figure 1

  • 使用 seaborn 的stripplot函数探索目标变量与 euribor3m 的对比:
sns.stripplot(x="y", y="euribor3m", data=df, jitter=True)
plt.show();

Figure 2

这里没什么特别有趣的。

下面是我使所有变量数值化的方法。你可能有更好的方法做这件事。

cat_to_num.py

单自变量逻辑回归

我们将从最简单的逻辑模型开始,仅使用一个独立变量或特征,即持续时间。

outcome = df['y']
data = df[['age', 'job', 'marital', 'education', 'default', 'housing', 'loan', 'contact', 'month', 'day_of_week', 'duration', 'campaign', 'pdays', 'previous', 'poutcome', 'euribor3m']]
data['outcome'] = outcome
data.corr()['outcome'].sort_values(ascending=False)

Figure 3

有了正确格式的数据,我们可以开始用 PyMC3 构建第一个也是最简单的逻辑模型:

  • 将数据居中有助于采样。
  • 确定性变量之一θ是应用于μ变量的逻辑函数的输出。
  • 另一个确定性变量 bd 是边界函数。
  • pm.math.sigmoid是同名的 Theano 函数。

logReg_model.simple.py

我们将绘制拟合的 sigmoid 曲线和决策边界:

logReg_viz.py

Figure 4

  • 上图显示了非订阅与订阅的对比(y = 0,y = 1)。
  • S 形(绿色)线是θ的平均值。这条线可以解释为订阅的概率,假设我们知道最后一次联系持续时间(持续时间的值)。
  • 边界决策表示为一条(黑色)垂直线。根据边界决策,左边的持续时间值对应于 y = 0(非订阅),右边的值对应于 y = 1(订阅)。

我们总结了推断的参数值,以便更容易地分析结果,并检查模型的表现如何:

az.summary(trace_simple, var_names=['α', 'β'])

Table 1

如您所见,α和β的值非常狭窄。这是完全合理的,因为我们正在将一条二元拟合线拟合到一组完全对齐的点上。

让我们运行一个后验预测检查来探索我们的模型捕捉数据的能力。我们可以让 PyMC3 为我们做从后面取样的艰苦工作:

ppc = pm.sample_ppc(trace_simple, model=model_simple, samples=500)
preds = np.rint(ppc['y_1'].mean(axis=0)).astype('int')print('Accuracy of the simplest model:', accuracy_score(preds, data['outcome']))
print('f1 score of the simplest model:', f1_score(preds, data['outcome']))

数据的相关性

我们绘制了一张热图来显示每个变量之间的相关性。

plt.figure(figsize=(15, 15))
corr = data.corr() 
mask = np.tri(*corr.shape).T 
sns.heatmap(corr.abs(), mask=mask, annot=True, cmap='viridis');

Figure 5

  • poutcome & previous 有很高的相关性,我们可以简单的去掉其中一个,我决定去掉 poutcome。
  • 与结果变量没有太多强相关性。最高正相关为 0.41。

使用具有多个独立变量的 PyMC3 GLM 方法定义逻辑回归模型

  • 我们假设订阅结果的概率是年龄、工作、婚姻、教育、违约、住房、贷款、联系人、月份、星期几、持续时间、活动、pdays、先前和 euribor3m 的函数。为了从后验样本中抽取样本,我们需要指定一个先验和一个似然。
  • 解释公式如下:

logit = β0 + β1(年龄)+ β2(年龄)2 + β3(工作)+ β4(婚姻)+ β5(教育)+ β6(默认)+ β7(住房)+ β8(贷款)+ β9(联系)+ β10(月)+ β11(星期几)+ β12(持续时间)+ β13(活动)+ β14(活动)+ β15(pdays) + β16(以前)+ β17(poutcome) + β18

  • 然后,对数赔率可以转换为输出的概率:

  • 对于我们的问题,我们感兴趣的是在给定所有活动的情况下,找出客户认购定期存款的概率:

  • 数学问题解决后,我们可以回到数据上来。PyMC3 有一个模块 glm,用于使用 patsy 风格的公式语法定义模型。这看起来真的很有用,特别是对于用更少的代码行定义模型。
  • 我们使用 PyMC3 从后部提取样本。使用的采样算法是 NUTS,其中的参数是自动调整的。
  • 我们将使用所有这 18 个变量,并使用上面定义的公式创建模型。添加年龄 2 的想法是从本教程中借用的,比较最近的模型也是很有趣的。
  • 我们也将年龄缩放 10,这有助于模型收敛。

logistic_model.py

Figure 6

以上我只展示了部分的痕迹图。

  • 该轨迹显示了为所有变量抽取的所有样本。在左侧,我们可以看到模型参数的最终近似后验分布。右边是采样过程中每一步的单个采样值。
  • 这个glm定义的模型表现得非常相似,并且找到了与我们之前创建的传统定义模型相同的参数值。

我希望能够回答这样的问题:

年龄和教育程度如何影响认购定期存款的概率?假设客户已婚

  • 为了回答这个问题,我们将展示几个不同教育水平的人订阅定期存款的概率如何随年龄变化,我们想研究已婚客户。
  • 我们将传入三个不同的线性模型:一个教育程度== 1(文盲),一个教育程度== 5(基础. 9y),一个教育程度== 8(大学.学位)。

age_education_marital.py

Figure 7

  • 对于所有三种教育水平,随着年龄的增长,定期存款的概率会降低,直到大约 40 岁时,这一概率才开始增加。
  • 每条曲线都是模糊的,这是因为我们为每个教育水平绘制了 100 条不同的曲线。每条曲线都是从我们的后验分布中抽取的。

让步比

  • 一个人的教育程度会影响他或她定期存款吗?为了做到这一点,我们将使用比值的概念,我们可以这样估计教育的比值比:
b = trace['education']
plt.hist(np.exp(b), bins=20, normed=True)
plt.xlabel("Odds Ratio")
plt.show();

Figure 8

  • 我们有 95%的信心,教育的优势比在以下区间内。
lb, ub = np.percentile(b, 2.5), np.percentile(b, 97.5)
print("P(%.3f < Odds Ratio < %.3f) = 0.95" % (np.exp(lb), np.exp(ub)))

  • 我们可以这样解释:“在概率为 0.95 的情况下,优势比大于 1.055,小于 1.108,所以教育效应发生了,因为在保持所有其他独立变量不变的情况下,教育程度较高的人比教育程度较低的人至少有 1.055 的概率订阅定期存款。”
  • 我们可以估计所有变量的优势比和百分比效应。
stat_df = pm.summary(trace)
stat_df['odds_ratio'] = np.exp(stat_df['mean'])
stat_df['percentage_effect'] = 100 * (stat_df['odds_ratio'] - 1)
stat_df

Table 2

  • 我们可以这样来解释 percentage_effect:“教育水平每提高一个单位,定期存款的可能性就会增加 8%。同样,在保持所有其他独立变量不变的情况下,euribor3m 每增加一个单位,认购定期存款的可能性就会降低 43%。

可信区间

Figure 9

很难展示整个森林地块,我只展示了其中的一部分,但这足以让我们说,存在订阅定期存款的基线概率。除此之外,年龄对订阅的影响最大,其次是联系方式。

使用广泛适用的信息标准比较模型(WAIC)

  • 如果你记得的话,我们增加了一个年龄 2 变量,它是年龄的平方。现在是时候问它对我们的模型有什么影响了。
  • WAIC 是模型拟合的一种度量,可应用于贝叶斯模型,并且在使用数字技术进行参数估计时有效。阅读这篇论文了解更多信息。
  • 我们将比较三个多项式复杂度递增的模型。在我们的例子中,我们对 WAIC 分数感兴趣。
  • 现在循环所有的模型,计算 WAIC。

compare_models.py

  • PyMC3 包括两个方便的函数,有助于比较不同型号的 WAIC。第一个函数是compare,它从一组轨迹和模型中计算 WAIC,并返回一个数据帧,该数据帧从最低到最高 WAIC 排序。
model_trace_dict = dict()
for nm in ['k1', 'k2', 'k3']:
    models_lin[nm].name = nm
    model_trace_dict.update({models_lin[nm]: traces_lin[nm]})dfwaic = pm.compare(model_trace_dict, ic='WAIC')
dfwaic

Table 3

  • 我们更喜欢 WAIC 较低的型号。
  • 第二个便利函数获取compare的输出并生成一个汇总图。
pm.compareplot(dfwaic);

Figure 10

  • 空心圆圈代表 WAIC 的值,与之相关的黑色误差线是 WAIC 的标准偏差值。
  • 最低 WAIC 值也用垂直灰色虚线表示,以便与其他 WAIC 值进行比较。
  • 实心黑点是每个模型的样本内偏差,对于 WAIC 来说,与相应的 WAIC 值相差 2 pWAIC。
  • 对于除排名第一的模型之外的所有模型,我们还得到一个三角形,指示该模型和排名第一的模型之间的 WAIC 差值,以及一个灰色误差条,指示每个模型的排名第一的 WAIC 和 WAIC 之间的差值的标准误差。

这证实了包含年龄平方的模型比不包含年龄平方的模型更好。

后验预测检查

与标准的机器学习不同,贝叶斯专注于预测的模型可解释性。但我很好奇,如果我们计算标准的机器学习指标,我们会得到什么。

我们将使用参数的平均值作为“最有可能”的估计值来计算指标。

bayesian_metrics.py

Figure 11

print('Accuracy of the full model: ', accuracy_score(preds, data['outcome']))
print('f1 score of the full model: ', f1_score(preds, data['outcome']))

Jupyter 笔记本可以在 Github 上找到。祝你一周愉快!

参考资料:

[## GLM:逻辑回归- PyMC3 3.6 文档

编辑描述

docs.pymc.io](https://docs.pymc.io/notebooks/GLM-logistic.html) [## 型号比较-pymc 3.6 文档

为了演示模型比较标准在 PyMC3 中的使用,我们实现了来自第 5.5 节的 8 所学校的示例…

docs.pymc.io](https://docs.pymc.io/notebooks/model_comparison.html)

这本书:用 Python 进行贝叶斯分析,第二版

用 scikit-learn 构建一个更好的亵渎检测库

原文:https://towardsdatascience.com/building-a-better-profanity-detection-library-with-scikit-learn-3638b2f2c4c2?source=collection_archive---------9-----------------------

为什么现有的库没有启发性,以及我如何建立一个更好的库。

几个月前,我需要一种方法来检测用户提交的文本字符串中的亵渎内容:

This shouldn’t be that hard, right?

为此,我最终构建并发布了自己的库,名为亵渎检查:

[## vzhou 842/亵渎性检查

一个快速、健壮的 Python 库,用于检查字符串中的攻击性语言。-vzhou 842/亵渎-检查

github.com](https://github.com/vzhou842/profanity-check)

当然,在我这么做之前,我在 Python 包索引 (PyPI)中寻找任何可以为我做这件事的现有库。搜索查询“亵渎”得到的结果只有一半还不错:

  • 脏话(理想的包名)
  • 更好——亵渎 : “灵感来自包 亵渎 本·弗里德兰 ,这个库比原来的快多了。”
  • 亵渎过滤器(有 31 颗 Github 星,比大多数其他结果多 30 颗)
  • 脏话过滤(采用机器学习,说够了吧?!)

然而,第三方库有时可能是粗略的,所以我对这 4 个结果做了尽职调查。

亵渎,更好的亵渎,亵渎过滤器

在快速浏览了一下profanity存储库之后,我找到了一个名为 wordlist.txt 的文件:

Sorry this image of profanities is so big…

整个profanity库只是这个 32 个单词列表的包装器!profanity只需查找其中一个单词就能检测出亵渎。

令我沮丧的是,better-profanityprofanityfilter都采取了同样的方法:

  • better-profanity使用一个 140 字的单词表
  • profanityfilter使用一个 418 字的单词表

这很糟糕,因为基于单词表的亵渎检测库非常主观。例如,better-profanity的单词列表中就包含了单词“suck”你愿意说任何包含“吸”字的句子都是亵渎吗?此外,任何硬编码的不良词汇列表将不可避免地是不完整的——你认为只有profanity的 32 个不良词汇吗?

Fucking Blue Shells. source: xkcd

已经排除了 3 个库,我把希望放在了第 4 个也是最后一个:profanity-filter

脏话过滤器

profanity-filter使用机器学习!太棒了。

原来,是 真的 慢。下面是我在 2018 年 12 月运行的一个基准测试,比较了(1) profanity-filter、(2)我的库profanity-check、(3) profanity(有 32 个单词列表的那个):

A human could probably do this faster than profanity-filter can

我需要能够实时执行许多预测,而profanity-filter甚至还不够快。但是,嘿,也许这是一个经典的速度与准确性的权衡,对不对?

没有。

At least profanity-filter is not dead last this time

我在 PyPI 上找到的库都不能满足我的需求,所以我自己建了一个。

建筑物亵渎检查第 1 部分:数据

我知道我希望profanity-check基于数据进行分类,以避免主观(理解为:可以说我使用了机器学习)。我从两个公开来源收集了一个综合数据集:

  • 来自t-Davidson/仇恨言论和攻击性语言的“推特”数据集,包含从推特上抓取的推文。
  • Alphabet 的 Conversation AI 团队发布的来自 this Kaggle competition 的“维基百科”数据集,其中包含来自维基百科 talk page edits 的评论。

这些数据集中的每一个都包含了文本样本,这些样本是由人们通过众包网站手工标注的,比如Figure 8。

我的数据集最终看起来是这样的:

Combined = Tweets + Wikipedia

Twitter 数据集有一个名为class的列,如果推文包含仇恨言论,则为 0,如果包含攻击性语言,则为 1,如果两者都不包含,则为 2。我将任何一条class为 2 的推文归类为“不冒犯”,而将所有其他推文归类为“冒犯”。

维基百科数据集有几个二进制列(如toxicthreat),表示该文本是否包含该类型的毒性。我将任何包含毒性类型的文本归类为“攻击性的”,而将所有其他文本归类为“非攻击性的”

建筑亵渎检查,第 2 部分:培训

现在有了一个干净的组合数据集(你可以在这里下载),我已经准备好训练这个模型了!

我跳过了我是如何清理数据集的,因为老实说,这很无聊——如果你有兴趣了解更多关于预处理文本数据集的信息,请查看这个或这个。

Are you also surprised the code is so short? Apparently scikit-learn does everything.

这里有两个主要步骤:(1)矢量化和(2)训练。

矢量化:单词包

我使用了scikit-learn的 CountVectorizer 类,它基本上通过计算每个给定单词出现的次数,将任何文本字符串转换成一个向量。这就是所谓的袋字(鞠躬)表示法。例如,如果英语中仅有的单词是thecatsathat,则句子the cat sat in the hat的可能矢量化结果可能是:

“the cat sat in the hat” -> [2, 1, 1, 1, 1]

???代表任何未知单词,对于这个句子来说就是in。任何句子都可以这样表示为thecatsathat???的计数!

A handy reference table for the next time you need to vectorize “cat cat cat cat cat”

当然,英语中有更多的单词,所以在上面的代码中我使用了fit_transform()方法,它做了两件事:

  • Fit: 通过查看数据集中出现的所有单词来学习词汇。
  • Transform :将数据集中的每个文本字符串转换成它的向量形式。

训练:线性 SVM

我决定使用的模型是线性支持向量机(SVM),它是由scikit-learn的 LinearSVC 类实现的。这个和这个如果你不知道什么是支持向量机,是很好的介绍。

上面代码中的 CalibratedClassifierCV 是一个包装器,为我提供了predict_proba()方法,它返回每个类的概率,而不仅仅是一个分类。不过,如果最后一句对你来说毫无意义,你几乎可以忽略它。

你可以用一种(简化的)方式来思考线性 SVM 的工作原理:在训练过程中,该模型学习哪些单词是“坏的”,以及它们有多“坏”,因为这些单词在攻击性文本中出现得更频繁。就好像训练过程是在为我挑出“不好的”单词,这比用我自己写的单词表好多了!

线性 SVM 结合了我发现的其他亵渎检测库的最佳方面:它足够快,可以实时运行,但又足够健壮,可以处理许多不同类型的亵渎。

警告

话虽如此,profanity-check远非完美。让我明确一点:对profanity-check的预测持保留态度,因为也会犯错。例如,它不擅长挑选不太常见的脏话变体,如“f4ck you”或“you b1tch ”,因为它们在训练数据中出现的频率不够高。你永远无法检测出所有的脏话(人们会想出新的方法来逃避过滤器),但是profanity-check在发现大多数方面做得很好。

亵渎-检查

profanity-check是开源的,可以在 PyPI 上获得!简单地说,使用它

$ pip install profanity-check

怎样才能更好?如有任何想法或建议,请随时联系我们或发表评论!

[## vzhou 842/亵渎性检查

一个快速、健壮的 Python 库,用于检查字符串中的攻击性语言。-vzhou 842/亵渎-检查

github.com](https://github.com/vzhou842/profanity-check)

也贴在【victorzhou.com】上。

构建更好的查询引擎

原文:https://towardsdatascience.com/building-a-better-query-engine-ff52cb6364cd?source=collection_archive---------33-----------------------

TL;博士 这篇文章讲述了我们如何引入一个自由文本查询引擎来取代传统的基于过滤器的方法,使用命名实体识别和一些其他技巧来查询数据。

所有企业都在以巨大的速度生成数据。当数据量很大时,我们会希望查看我们可能感兴趣的数据子集(应用过滤器)。所以传统上这些过滤器转化为数据库查询。对 IBM 中所有概要文件的简单查询如下所示

SELECT * from profiles where company = " IBM "

当以有意义的方式向用户呈现数据时,Draup 面临着非常类似的挑战。它有一个庞大的配置文件生态系统,用户可以根据几个过滤器参数缩小范围。您可以根据公司、位置、技能、业务职能等选择个人资料。

这是基于过滤器的方法的样子。

Filter-Based Approach

当每个类别只有几个选项时,过滤很容易,但随着每个类别的选项增加,复杂性就会不成比例。

另一种查询方法

在 Draup,我们希望摒弃这种过滤搜索结果的方式,改善我们的用户体验。所以我们研究了另一种查询平台的方法。用户现在可以输入自由文本查询,告别所有由过滤器造成的混乱。这里有几个例子

- Show me data engineers skilled in python
- Someone who is located at san francisco with the qualification of a data scientist.
- Show me top executives in Amazon
- Adam from Microsoft, Redmond

所以第一个查询应该转换成下面的 SQL 查询。

档案中选择 *,其中JOB _ TITLE = " data engineer "SKILL = "python"

我们从机器学习的角度来处理这个问题。这个问题在 ML 世界中被称为命名实体识别(NER)。

构建命名实体识别系统

命名实体识别是信息提取的一个子任务,旨在定位非结构化文本中的命名实体提及并将其分类为预定义的类别,例如个人和组织名称、位置日期、等。

NER 有出色的开源模型 ( 1 、 2 、 3 、 4 ),但它们本质上非常普通。这些模型适用于一般的实体类型,如人名、位置、组织、日期等,但是在 Draup 中,我们关心的要多得多。我们还有其他几个实体,如技能、子垂直行业、业务职能、组织级别等,这些都是这些预训练模型无法涵盖的。所以我们得出结论,我们必须建立自己的 NER 系统。

构建数据集

这通常是任何机器学习(ML)过程中最关键的部分。ML 遵循一个简单的规则,“垃圾入,垃圾出”。这意味着一个 ML 模型的好坏取决于它所训练的数据。牢记这一点,我们努力产生尽可能多的例子。我们可以达到大约 200 个可能的查询。这是用于训练模型的相对较小的数据集。对用户查询模式的仔细研究给了我们关于如何通过数据扩充生成更多数据的想法。人们似乎并不关心自由文本查询中重要单词的大写。许多用户没有注意到使用正确的标点符号,但是仍然期望模型能够工作。这些见解有助于我们建立一个快速的数据增强管道,为我们创建更多的训练示例。所有这些努力总共产生了 1000 个培训示例

建模技术的选择

构建 NER 系统有两大主题:

  1. 传统算法如条件随机场 (CRF)
  2. 基于深度学习的方法

如果你有一个大的数据集,基于深度学习的方法在文本数据领域非常有效。大约 1000 个例子还不够。最近,通用语言模型,如谷歌的伯特 T21 或 OpenAI 的 GPT-2 已经在较小的数据集上显示出有希望的结果。然而,这些模型体积庞大,我们觉得它们对我们的任务来说有点大材小用。与传统方法相比,深度学习的另一个重要缺点是很难解释和说明模型行为。

另一方面,条件随机场即使在数据有限的情况下也能很好地完成 NER 任务。

条件随机场模型

本节以一些直观的细节讨论了 CRF,但涉及一些数学问题。您可以选择跳过它。

当我们处理序列时,使用 条件随机场 模型。在我们的例子中,输入是一个单词序列,输出是一个实体标签序列。

Words and Tags sequence

让我们把单词序列叫做 x̄,把标签序列叫做 y̅.
同样,让我们定义所谓的特征函数:f(yᵢ₋₁、yᵢ、x̅,i)
这里,特征函数采用 4 个参数:-

1: i,当前索引在序列
2: x̄,整个输入序列
3: yᵢ₋₁,先前的输出标签索引在 i
4: yᵢ,当前输出标签索引在 I

为了让事情更清楚,让我们定义一个示例特征函数。

f(yᵢ₋₁、yᵢ、x̅、i) = { 1 如果 yᵢ₋₁和 yᵢ都是头衔并且现在的单词是‘工程师’,否则为 0}

正如您所看到的,这是一个非常具有描述性的特征函数,如果我们定义了很多这样的特征函数,我们就可以提取出很多关于文本数据的信息。这是另一个功能。

f(yᵢ₋₁、yᵢ、x̅、i) = { 1 如果 yᵢ₋₁是其他人,yᵢ是标题,当前单词是大写的,否则 0}

在收集了一系列特征函数之后,我们想要找到一个概率分布函数。这个函数应该告诉给定 x̅.,每个可能的 y̅的概率是多少下面的等式定义了这个概率。

Image Credit: Sameer Maskey slides

这里 exp 是指数函数。上述函数使用指数平滑地将概率分布在所有可能的标签序列上,并确保概率之和为 1。

如果你仔细观察,你会发现每个特征函数都被赋予了一个权重,这在直觉上是有意义的,因为不是所有的特征函数都具有同等的重要性。现在,给定数据,我们希望最大化我们的数据的条件似然并找到最佳的权重集。

Conditional Likelihood function

这里 L 是条件似然函数,k 迭代所有的训练例子。使用梯度下降,我们可以学习最适合我们数据集的参数(权重)。

嗯,这看起来需要做很多工作,但是我们使用了sklearn-CRF suite库,在大约 1000 个例子的数据集上训练 CRF 模型。

准确率、召回率和 F1 评分

下面用一个简单的例子来解释一下 精度和回忆一下 。假设我们建立了一个模型,可以预测/提取文本中提到的所有技能,我们的输入文本有 9 项技能。假设模型预测了 10 项技能,其中 6 项实际上是技能,另外 4 项不是技能。

A =相关记录数= 9
B =检索到的相关记录数= 6
C =检索到的记录数= 10
D =检索到的不相关记录数= 10–6 = 4

精度= B/C = 0.6
召回=B/A = 0.667

一个好的模型在精确度和召回率上都有很高的值。然而,有时用一个值来评估模型的好坏是有好处的。f1 分数由此而来。它将精确度和召回率和谐地结合在一起。

f1-score

所以在我们的例子中,f1 分数是 0.632

模型验证和改进

如果模型不能识别查询中的所有实体,这是可以接受的。但是如果模型错误地识别了实体,那么结果就会受到负面影响。例如,如果模型将一个人的名字识别为一家公司,那么很可能不会有搜索结果,甚至更糟的是,系统会产生错误的结果。因此,我们需要非常好的精确度和尽可能多的召回率。

Initial Model

我们最初的努力导致了以下结果。

精确度:0.790
召回率:0.635
F1-得分:0.702

这些结果是基于完全看不见的数据。

如果看召回,好像挺低的。这通常是 CRF 技术的一个问题。CRF 通常可以达到很好的精度,但是在召回率上有所欠缺。

为了解决低召回率的问题,我们引入了几个特征函数,它们使用我们的几个实体的管理列表。

f(yᵢ₋₁,yᵢ,x̅,我)=

假设我们的列表中有这样的技能:
('AutoCAD ',' python ',' java ',' MongoDB ',' nodejs ',……)

Value of skill lookup function for each index in the original sequence

在为其他一些实体定义了这样的特性之后,我们取得了巨大的进步。在引入这些特性后构建的模型给出了以下结果。

Improved Model

精确度:0.862
召回率:0.754
F1-得分:0.803

帮助我们的引擎在生产中表现良好的另一个技巧是使用 拼写纠正 。用户在向系统查询时似乎很匆忙。我们经常会遇到这样的查询:

向我展示amzon Java伦敦 的开发人员

拼写纠正过程帮助我们在很大程度上纠正这种情况,并使我们的系统更可用。

它看起来像什么

系统现在允许用户输入自由文本查询,然后提取实体并显示给用户,以及显示基于这些实体/过滤器的结果。

New Search Interface

上面的例子显示了微软中的查询“ Director”。系统正确地识别出实体董事微软,并为您应用这些过滤器。

这只是自由文本查询引擎的开始。我们的系统将随着我们获得更多数据的训练而改进。

参考文献:-

  • 条件随机场:用于分割和标记序列数据的概率模型
  • Web 对数线性模型的统计 NLP,MEMM,条件随机字段:same er Maskey
  • 自然语言工具包
  • Spacy:工业级自然语言处理
  • sk learn-CRF suite
  • 最大似然估计

利用矩阵分解和 SV 分解构建图书推荐系统

原文:https://towardsdatascience.com/building-a-book-recommendation-system-using-matrix-factorization-and-sv-decomposition-d3541112d53e?source=collection_archive---------21-----------------------

每周一,我都会点击“发现周刊”的列表,看看 Spotify 提供了哪些个性化服务,特别是针对我前一周上传的音乐定制的服务。有时我会遇到一个隐藏的宝石(有一些音乐跳过),有时我会完全失望。但事实是,我通常也有错,因为我根据情绪听完全不同的音乐风格,所以所发生的是推荐引擎没有区分情绪,我最终不得不做一些手动工作,以在给定的时间达到期望的精神状态。

然而,撇开情绪不谈(我强烈推荐 Spotify 团队考虑/测试的产品功能),我一直想知道 Spotify 是如何找出这些标题的,即使在其系统中除了“另存为收藏夹”按钮之外没有评级,该按钮发送的是分类信号而不是数量信号……直到我最近意识到他们的推荐引擎使用了不同架构的组合:

1。基于记忆的协同过滤推荐器:,它关注用户和所讨论的项目之间的关系(当数据包含对所提供的各种项目的评级时是理想的)。矩阵分解在这里是一个强大的数学工具,用来发现用户和物品之间潜在的交互。比方说,A 和 B 听歌曲 X,B 经常听歌曲 Y,那么 A 也很可能听宋立科 Y。

2。基于内容的推荐:关注项目本身的特征。因此,不是分析用户/客户与项目的主动交互,而是主要在后者的水平上进行分析,因此检查和测量项目特征的相似性。为了保持在音乐环境中,让我们举例说,你经常听歌曲 X 和 Y,而这两首歌恰好来自一位意大利音乐家,他使用独特的钢琴曲调,并且恰好属于歌曲标签中指定的音乐流派和时代。
这种推荐方法将使用不同的机器学习技术(例如,自然语言处理、音频建模等。)来确定具有相似属性的歌曲 Z。

我想尝试一下第一种推荐方式,因为它似乎比第二种更简单。最重要的是,我想了解算法背后的数学原理,并提供简单的直觉,或许还想在转向更复杂的模型之前,为推荐系统在实践中的工作方式打下基础。

10k Books 数据集 在本教程中,我选择了在 Kaggle 上找到的 Goodbooks-10k 数据集作为开始。我一直担心读完一本精彩的书后会感到失望,所以我认为这将解决个人的矛盾,总的来说,这可能只是一件有趣的事情,因为朋友们会问我接下来该读什么。
zip 文件包含多个数据集(book_tags、books、ratings、tags)。我们将只使用包含与我们的分析相关的列的书籍和评级数据集。

首先,让我们导入必要的库。

import pandas as pd
import numpy as np
import sklearn
from sklearn.decomposition import TruncatedSVD
import warnings

让我们上传数据集。“图书”数据集包含 23 列。我们将分割数据并删除变量,只保留感兴趣的列。我们将保持评级数据集不变。
接下来,我们将合并“book_id”上的两个数据集。Book_id 比 original_title 更可靠,因为某些标题的格式可能会有一些变化。在继续创建矩阵之前,我们将删除 user_id 和 book_id 以及 user_id 和 original_title 的成对组合中的重复项。

books = pd.read_csv('books.csv', sep=',')
books = books.iloc[:, :16]
books = books.drop(columns=['title', 'best_book_id', 'work_id', 'books_count', 'isbn', 'isbn13', 'original_publication_year','language_code','work_ratings_count','work_text_reviews_count'])
books.head(5)
ratings = pd.read_csv('ratings.csv', sep=',')
ratings.head(5)df = pd.merge(ratings, books, on="book_id")
df.head(5)df1= df.drop_duplicates(['user_id','original_title'])
df1= df.drop_duplicates(['user_id','book_id'])
df1.head(10) #went down from 79701 to 79531 
df1.shape #(79531, 8)

矩阵分解法& SVD —直觉

我们现在将使用矩阵分解方法和单值分解模型(SVD)来创建一个矩阵模型。您可以在网上找到许多优秀的技术资源,以更深入的方式描述这些模型,但是我将在这里用简单的术语把它分解给你。

我们接下来要做的是调用 pivot 函数来创建一个数据透视表,用户在这个数据透视表中采用不同的行,预订不同的列,并在这个表中用一个形状(m*n)来表示各自的评分值。

######################################
####MATRIX FACTORIZATION
######################################books_matrix = df1.pivot_table(index = 'user_id', columns = 'original_title', values = 'rating').fillna(0)
books_matrix.shape #(28554, 794)
books_matrix.head()

如果你看看下面的图形表示,你就会知道幕后发生了什么。首先,我们创建了形状为(md)=(booksuser_id)的 A 矩阵和形状为(dn)=(ratingsuser_id)的 B 矩阵。

Matrix graphical representation by Albertauyeung

结果是两个矩阵之间的乘积(矩阵因式分解),其数学计算值如下:

我们需要创建一组训练数据——我们的训练数据基本上由更小的矩阵组成,这些矩阵是我们想要预测的评级因素。为此,我们将设置另一个矩阵 X,它是上面创建的结果矩阵(“books_matrix”)的转置,也称为可逆矩阵。

X = books_matrix.values.T
X.shape#Fitting the Model
SVD = TruncatedSVD(n_components=12, random_state=0)
matrix = SVD.fit_transform(X)
matrix.shape #(812, 12)

你会注意到我们新创建的矩阵非常稀疏。根据您指定的随机状态,列数是 5 位数,这意味着一个 5 位数的维度空间。这就是 SVD 方法介入的地方。

就像我们在其他数据集上使用 PCA/核 PCA 特征提取方法一样,SVD 是我们在推荐应用中应用于矩阵的另一种方法。奇异值分解可以将我们的维数压缩成更小的数来描述数据中的方差。这里发生的事情是,SVD 将寻找潜在的特征,并从数据中提取它们,从 10.000 个特征减少到只有 10 个,并且将为我们节省大量的计算能力,此外还可以避免数据过度拟合。在这个练习中,我将 SVD 的组件数量设置为 12。为了应用该模型,现在剩下的是拟合和转换训练数据 x。

注: 一般来说,在应用 SVD 后,一个常见的做法是引入一个正则化项(下面右边的项),以避免对数据的过拟合:

左边的最小化术语是我们没有信息的评级误差的最小化(例如,未知的当前或未来评级)。我们可以在数学上用上述目标函数得出这些值,在实践中用随机梯度下降(SGD)等方法。
在本教程中,为了简单起见,我没有在实践中引入这两个术语,只是用空值(.菲尔娜(0))。

创建相关系数 接下来我们用 numpy 函数 np_corrcoef 为矩阵中的所有元素创建相关系数函数。我们称之为“corr”。
一旦我们将“corr”应用于我们非常喜欢的一本书,该函数将计算与其余书的所有相关系数,并将返回我们最有可能喜欢的所有书。

import warnings
warnings.filterwarnings("ignore",category =RuntimeWarning)#to avoid RuntimeWarning #Base class for warnings about dubious runtime behavior.corr = np.corrcoef(matrix)
corr.shape

相关系数的范围从 0 到 1,0 表示两个项目之间不存在相关性,1 表示相反。在我们的例子中,我们越接近 1,其他推荐的书籍就越有可能具有与您输入的书籍高度相关的特征,因此您更有可能喜欢这些书籍。

检查结果 现在我们来检查结果。我将创建一个名为“titles”的向量并列出条目。我会挑一本我喜欢的书作为索引。《艺妓回忆录》是我最喜欢的小说之一,所以我们就看这本吧。

title = books_matrix.columns
title_list = list(title)
samia = title_list.index('Memoirs of a Geisha')
corr_samia  = corr[samia]
list(title[(corr_samia >= 0.9)])

在我运行完全部代码后,这里是算法推荐的书籍列表:

Suggested Book list for Samia

这个看起来不错!我已经读了很多以上的书,可以证明他们中的一些曾经在我的清单上(例如《波斯波利斯》、《荒野》、《了不起的盖茨比》、《百年孤独》、《问题的核心》等)。).我仍然很好奇这个算法在这里使用了哪些潜在的特征来选择“思考和致富”,因为我会把它归类到另一个类别中(非小说+其他考虑),但这再次揭示了这个算法的一个限制,这可能与我们提供给它的独立变量的权重有关。

评估结果 我评估了这个模型,只是看了一下算法给出的书单,因为我已经阅读了(并且非常喜欢)一些推荐的书名,并且为了好玩,和其他人一起运行这个模型,看看他们是否会同意大部分内容——我想我今天就到此为止了。当然,如果您必须提供清晰的性能指标,这不是正确的方法。

为了更加准确,有许多方法来评估推荐系统,并且该方法将根据推荐器的类型而不同(例如,基于内容的与协同过滤的)。一种方法是应用交叉验证模型——将您的用户分成 k 个折叠组并循环进行:将(k-1)个折叠作为训练集,并对剩余的折叠进行测试,对所有结果进行平均。也许这将是后续文章的主题:)

免责声明&离开的想法:) 最后,这是我的第一个数据科学/机器学习实践帖子,所以我希望这是一个有用的教程,直观地解释了模型背后的数学原理。
祝您构建自己的推荐引擎愉快,不要忘记订阅我的频道或在下面提问以获得澄清!😃

关注我在 Linkedin
连接在 Twitter

为 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;
  • 克隆我的 GitHub 库。

存储库包含第 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/KinWaiCheuk/Triplet-net-keras/blob/master/Triplet % 20NN % 20 test % 20 on % 20 minist . ipynb

代码: 重现结果🔍=>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 上获得。

为 Twitch Streamers 构建一个关注增长的游戏推荐器

原文:https://towardsdatascience.com/building-a-growth-focused-game-recommender-for-twitch-streamers-7389e3868f2e?source=collection_archive---------21-----------------------

Photo by Caspar Camille Rubin on Unsplash

将流媒体推荐的传统重点从观众转移到流媒体,优先考虑其频道的增长。

witch 是一个视频流平台,用户可以在这里观看其他用户可以观看的内容,它在过去十年中取得了巨大的成功。2018 年,它共记录了 5600 亿分钟的观看时间,每月有 440 万条独特的流媒体。它可以被简洁地描述为众包直播电视。为了增加用户的保留,它有一个推荐系统,为用户提供与他们过去观看过的类似的频道。然而,Twitch 没有为那些希望继续流媒体播放,但可能对他们目前玩的游戏感到厌倦,但仍然希望扩大观众群的流媒体用户提供任何东西。这篇文章讲述了兰迪·马卡雷格、娜塔莎与之间的边界,我试图为这一鸿沟建立一个解决方案。

目标

这个项目的目标是根据流媒体用户以前的流媒体播放历史和观众的接受程度,为流媒体用户推荐游戏。该推荐系统将理想地为提供他们的名字作为输入的流播者输出游戏和理想时间。所有这一切,包括项目构思和数据收集,将在三周的时间内完成。

1.数据

在我们能说出想法之前,我们需要看看我们在做什么。通过 Twitch API ,我们可以抓取一些东西:

  1. 顶级游戏
  2. 每场比赛中彩带的身份
  3. 给定流媒体的观众计数
  4. 每场比赛的艺术框
  5. 每个游戏的类型

所有这些都是以小时为基础的,并且有每 30 秒 100 个电话的限制。对于我们的分析来说,这是一个限制因素,但是为了证明这个概念,我们向前推进了。

2.管道

为了在 API 限制和粒度之间取得平衡,我们选择对排名前 100 的游戏进行每小时一次的清理。我们的管道由以下步骤组成:

  1. 从 Amazon EC2 实例上托管的 Python 脚本调用 Twitch API。使用每小时一次的内置 Linux Cron 作业自动调用脚本和 API。
  2. 将 JSON 对象转换成 SQL 格式,并将数据推送到托管 PostgreSQL 数据库的 Amazon RDS 实例。
  3. 从 RDS 数据库中训练模型,并拥有一个在运行模型时引用此数据库的应用程序。

以下是 RDS 数据库模式:

Database schema, generated on https://dbdiagram.io/home

你也可以在 Tableau 上玩我们搜集的数据:

[## 随着时间的推移,Twitch 的游戏、类型、频道和收视率的趋势。GitHub…

随着时间的推移,Twitch 的游戏、类型、频道和收视率的趋势。GitHub…

随着时间的推移,Twitch 的游戏、类型、频道和收视率的趋势。GitHub…public.tableau.com](https://public.tableau.com/views/TwitchVisualizations/PickYourGame?:embed=y&:display_count=yes&:origin=viz_share_link&source=post_page-----e40d4a45e4ee----------------------&:showVizHome=no)

3.成长分数

一旦我们有了数据,我们想找到一种方法来对推荐的游戏进行排名。要做到这一点,我们需要考虑哪些可量化的属性可能使游戏有利于流媒体频道的增长。我们得出了以下每场比赛的时间函数指标:

  1. 流民亲和力—我们想根据流民以前的游戏推荐一款他们会喜欢的游戏。这通过使用推荐系统(惊喜包,使用相似用户矩阵)来量化
  2. 游戏增长——由于我们不想推荐一款垂死的游戏,我们想量化那些观众增长且得分较高的游戏。一个简单的表示可以通过查看一周前每个频道的观众数量来实现。
  3. 游戏受欢迎程度——因为 Twitch 默认按照观众数量降序排列游戏,所以一个更受欢迎的游戏可能有更好的机会获得新观众,因为它会更容易被寻找新内容的观众看到。我们可以用一场比赛的总观看人数占当时观看人数最多的比赛的观看人数的比例来量化这个。
  4. 市场渗透力 —有时观众会通过各种彩带被带到一个不知名或不太受欢迎的游戏,而彩带会随着忠实观众群从一个游戏跳到另一个游戏。除此之外,有时有些游戏没有人想看,但某个流是他们观看游戏的唯一原因,他们没有兴趣探索其他人。为了量化这一点,我们可以看看前 8 个 streamers 中的观众人数及其占该游戏观众总数的比例。

Readable form of our metrics equation

Hypothetical equation for Growth Score

4.推荐系统

我们使用的框架是 Python 的惊喜包。该软件包需要用户矩阵形式的输入,其中每个用户是一行,他们玩过的每个游戏是该行中的一列,值在 1-5 之间。根据这些分数,它可以使用各种算法(如 KNN、余弦相似度)向用户推荐相似的游戏。

因为我们没有每个流媒体播放者对他们玩的每个游戏的偏好,所以我们根据他们在每个游戏中的观众人数相对于他们播放的观众人数最高的游戏的比例来输入一个指标,然后在 1 到 5 之间进行缩放。这个指标将捕捉流媒体的观众更喜欢看什么,或者什么游戏对流媒体来说最成功。请注意,这个数字对异常值很敏感,可以通过在一段时间内取平均值来改善。

在将我们的矩阵通过惊喜包中可用的不同算法后,我们得到了以下测试 RMSE 结果。这些都是相对于惊喜预期的 1-5 级,因此在 10-20%的准确度范围内。利用这些数据,我们能够补充增长得分中的流光亲和力部分。

5.时机

基于观众和流媒体兴趣的推荐完成后,我们需要看看我们是否可以预测未来的游戏指标,以便提出流媒体播放时间的建议。为此,我们使用了长短期记忆(LSTM)神经网络。

LSTM 网络是一种递归神经网络(RNN ),具有使用 sigmoid 层“忘记”先前输入的额外能力。这与 RNNs 相比具有优势,因为它不需要在所有先前的(并且可能不重要的)数据点上浪费处理时间和空间,而是只关注具有有价值信号的数据点。你可以在这里了解更多关于 LSTM 的。

对于 LSTM 的输入,我们选择输入前 168 小时和过去 24 小时的模型状态,以便预测下周的性能。这些参数是在我们有限的 2 周数据集内制定的,因此它们肯定会根据建议的预期范围和可用数据进行更改。当预测游戏《英雄联盟》的观众人数时,LSTM 得出的样本内和样本外 RMSE 约为 10%,这意味着它可以很好地推广到未来的新数据。

通过 LSTMs 运行每场比赛的观众数量数据变化和每场比赛的频道数量变化,我们可以计算下周任何时间点的比赛分数,并相应地对比赛和时间进行排名。不幸的是,我们最终没有时间来实现这一点,因为它需要为 100 多个游戏中的每一个训练两个 LSTM 模型,因此超出了我们的时间范围。然而,基本的想法仍然是合理的,并且可以在未来实际实施。

6.事后思考

总之,像我们正在构建的系统将有助于提高流注保留率,同时,理想情况下,也有助于为那些忠实于流注的用户保留用户。

为了量化这种功能的影响,我们可以进行 A/B 测试,将该功能随机分配给一组流媒体工具(可能包括一项调查,看看他们是否会根据我们的推荐播放推荐的游戏)。我们可以添加一个简单的推荐系统,而不需要将增长指标作为另一个测试用例来计算。一段时间后,我们可以比较这些组之间的频道增长,以量化推荐者的效果。

7.附加链接

这是我们在 Heroku 上建立并托管的网站的一个快速演示:

[## 网站演示视频

drive.google.com](https://drive.google.com/file/d/1rEY_Flt0ieiVEc1oqZm-59ydiPmJGkh2/view?usp=sharing)

代码可以在这里找到。

感谢阅读!如果你喜欢这篇文章,看看我的其他项目文章,如量化聊天室毒性!

在 Python 中构建更高维度的有效边界

原文:https://towardsdatascience.com/building-a-higher-dimensional-efficient-frontier-in-python-ce66ca2eff7?source=collection_archive---------20-----------------------

现代投资组合理论认为,投资者要承担更多的风险,就必须有更高的预期回报。这一假设导致了有效边界的产生,它代表了对于给定的风险值具有最大预期回报的所有投资组合。许多投资组合经理使用这个工具来查看与其他可能的投资组合相比,他们承担了多少风险。然而,比较回报率和方差只是故事的一部分。由于股票市场具有大幅下跌的性质,了解你的投资组合的尾部风险,以及其他可能的投资组合的尾部风险是很重要的。出于这个原因,有效边界的当前构造可以通过包括更高的统计矩如偏度和峰度来修改。

获取数据

我将使用来自 quandl 的真实股票数据来构建这个投资组合。我将在我假设的投资组合中使用苹果、IBM 和亚马逊。

构建矩阵

包含每日日志回报的 log_ret 数据框架将用于构建有效边界。更高维的形式将包含预期收益、波动性、偏度和峰度。目前,python 不具备计算共生矩阵或峭度矩阵的能力,因此我们必须自己构建它们。提醒你一下,偏度和峰度的定义如下。

其中,w 是权重向量,r 是每日回报,M3 是共生矩阵,M4 是峰度矩阵。偏斜度代表分布的不对称程度。正偏斜意味着分布更倾向于正值。由于下跌幅度大于上涨幅度,每日股票回报数据往往呈现负偏态。峰度衡量分布的峰值程度。一个大的峰度意味着一个分布比正态分布有更尖锐的峰值,但是有更厚的尾部。股票回报数据有很大的峰度,因为尾部受大事件支配。为了计算共生矩阵,我们可以运行下面的代码。

我们可以对峰度做同样的事情。

绘制有效边界

现在我们有了需要的矩阵,我们可以建立有效边界。以下将运行 10,000 次随机选择权重的试验,并计算预期收益、方差、偏斜度和峰度。输出是所有试验的结果,在 3 个空间维度上有回报、波动性和峰度,偏度显示为彩色地图。

如果在 jupyter 笔记本中运行,该图可以旋转,从而可以更清楚地了解分布情况。我们可以复制有效边界的原始形式,但是现在用彩色的偏度和峰度图。

我们可以看到,投资组合可以有相同的预期收益和波动性,但有不同的偏度或峰度。

结论

投资组合经理需要对其投资组合中的风险有充分的了解。使用有效边界的标准方法不能为投资专业人士传递足够的信息。当与其他可能的权重分配相比时,将偏度和峰度添加到最优投资组合集合中可以更清楚地了解投资组合的情况。

建立语言毒性分类模型

原文:https://towardsdatascience.com/building-a-language-toxicity-classification-model-b006ae6981a4?source=collection_archive---------30-----------------------

使用谷歌云自动语言

Left: Jigsaw (Conservation AI). Right: AutoML for Natural Language

在线社区是互联网互动的重要组成部分。这种社区的兴起伴随着对社区节制的需要,以确保参与者遵守他们规定的指导方针,并避免“清楚和明显”的和客观的政策违反。

由于存在怀有恶意的不良行为者,节制工作带来了特殊的挑战。其中之一是由于不良行为者的规模,花费在调节在线社区上的时间。更重要的是,这些违规行为的数量可能会导致他们中的一些人越过版主,并且在特定的社区中可能是极其危险的。一个例子是一个淫秽文本在一个 K-12 儿童的在线社区中通过了版主。

此外,适度的工作可能会导致其他挑战,如判断的一致性,更重要的是,由于不断接触图形和淫秽材料,心理上的危险。

Google Cloud AutoML for Natural Language 为语言识别用例提供了设计和开发定制语言模型的平台。本文使用 Google Cloud AutoML for Natural Language 开发了一个端到端的语言毒性分类模型来识别淫秽文本。使用神经架构搜索和转移学习的概念来寻找最佳网络架构和最佳超参数设置,以提高模型的性能。

关于数据集

这个项目使用的数据来自 Jigsaw 和 Google 在 Kaggle 上发起的有毒评论分类挑战。数据被修改为具有 16,000 个有毒和 16,000 个无毒单词的样本作为输入,以在 AutoML NLP 上构建模型。

第 1 部分:在 GCP 上启用自动语言

(1).去云控制台:【https://cloud.google.com/

Google Cloud Homepage

(2).通过单击 GCP 仪表盘左上角的三连破折号打开 Cloud AutoML 自然语言。选择人工智能产品部分下的 自然语言

Open AutoML Natural Language

(3).选择自动自然语言下的文本分类。

Text Classification under AutoML Natural Language

(4).设置项目 API、权限和云存储桶,以存储用于建模和其他 AutoML 资产的文本文件。

Setup Project APIs and Permissions

(5).询问时,从下拉列表中选择您的 GCP 帐单项目。现在我们准备创建一个数据集,用于在 AutoML 上构建定制分类模型。在将原始数据集从 Kaggle 下载到云存储并准备好用于 AutoML 建模的数据后,我们将返回这里。

在这种情况下,自动创建的 bucket 被称为:g s://ekabasandbox-lcm

第 2 部分:将数据集下载到 Google 云存储中

(1).激活云外壳(红色圆圈中)以启动临时 VM 实例来存放从 Kaggle 下载的数据集,将其解压缩并上传到存储桶。

Activate Cloud Shell

(2).安装 Kaggle 命令行界面。这个工具将允许我们从 Kaggle 下载数据集。运行以下代码:

sudo pip install kaggle

但是,请注意,云外壳实例是短暂的,当会话结束时,它不会保存系统范围内的更改。此外,如果数据集特别大,还有其他选择,例如启动计算虚拟机,下载数据集,将其解压缩,然后上传到云存储。可以设计其他高级数据管道,将数据输入 GCP 进行分析/机器学习。

(3).下载 Kaggle API 令牌密钥,使 Kaggle CLI 能够针对 Kaggle 进行身份验证/授权,以下载所需的数据集。

  • 登录您的 Kaggle 帐户。
  • 前往:https://www . ka ggle . com/[ka ggle _ USER _ NAME]/账号
  • 点击:创建新的 API 令牌。

Create API Token

  • 将令牌下载到您的本地机器,并将其上传到云 shell。

  • 将上传的.json键移动到目录.kaggle中。使用下面的代码:
mv kaggle.json .kaggle/kaggle.json

(4).从 Kaggle 下载数据集到 Google 云存储。

kaggle competitions download -c jigsaw-toxic-comment-classification-challenge

(5).解压缩下载的数据集

unzip jigsaw-toxic-comment-classification-challenge.zip

(6).将数据集从临时云外壳实例移动到创建的云存储桶。在此插入您的存储桶名称。

gsutil -m cp -r train.csv test.csv gs://**ekabasandbox-lcm**/toxicity/

第 3 部分:为建模准备数据集

(1).在谷歌云 AI 平台上推出 Jupyter 笔记本。

Notebooks of GCP AI Platform

(2).创建新的笔记本实例。

Start a new JupyterLab instance

(3).选择实例名称并创建。

Choose an instance name and create

(4).打开 JupyterLab。

Open JupyterLab

(5).在使用 AutoML Cloud 自然语言构建自定义语言分类模型之前,必须以特定格式准备数据集:

  1. 培训输入可以是(.txt)格式或作为(.csv)文件。多个文本可以组合成一个压缩的(。zip)文件。
  2. 对于此项目,文本文件放置在子文件夹中,其输出标签作为文件夹名称。这在以后用于创建包含数据文件路径及其标签的 CSV 文件。文件夹结构示例如下所示:
    →【文件】
    — →【有毒】
    — →【干净】
  3. 接下来,生成一个指向文本路径及其相应标签的 CSV。云 NLP 使用 CSV 文件指向训练文档或单词的位置及其对应的标签。CSV 文件放在配置 AutoML NLP 时创建的同一个 GCS 存储桶中。在我们的例子中,这个桶被命名为 gs:// ekabasandbox-lcm

(6).从 Github 克隆预处理脚本。点击用红色圈出的图标,输入 Github URLhttps://Github . com/dvdbisong/automl-toxity-classification . git,用预处理代码克隆 repo。

Clone preprocessing script

(7).运行笔记本nlp_preprocessing.ipynb中的所有单元格,创建包含图像路径和标签的 CSV 文件,并将该文件上传到云存储。

Run notebook cells

第 4 部分:用 Cloud AutoML 自然语言建模

(1).从 AutoML 自然语言仪表板中单击“新建数据集”。

Create New Dataset

(2).填写数据集名称,并从 AutoML 创建的云存储桶中选择 CSV 文件。

Create Dataset

(3).现在,如果您看到错误消息“找到重复的文件”,您可以退出。据我所知,从文件名来看,情况并非如此。

Cloud AutoML Processed Text

(4).点击列车,如上图红色所示,启动 Cloud AutoML 建模。

Start AutoML Training

(5).开始模特训练。

Train the New Model

(6).模型训练完成后,单击“评估”查看模型的性能指标。

Evaluate model performance

(7).评估性能指标(精确度、召回率和混淆矩阵)。

Evaluation page. Left: Precision and Recall score. Right: Confusion matrix and precision, recall graphs

第 5 部分:测试模型

(1).点击预测选项卡测试模型。

Predict model

(2).我们可以通过从维基百科传入关于尼日利亚的介绍性文本来探索这个模型。我们期望模型预测是干净的。通过进一步探索,该模型在“清晰明显”的有毒样本上表现良好。

Model test: clean text prediction

定制语言分类模型也作为 REST 或 Python API 公开,用于集成到软件应用程序中,作为推理的预测服务。

第六部分:结论

本文提供了一个通过利用 Google 云平台 AutoML 自然语言为定制用例设计强大的语言分类模型的演练。此外,该模型托管在云上,用于作为预测服务进行推理。这是构建和部署语言分类产品的强大机制。我相信这个示例可以作为使用 AutoML 构建自然语言产品的模板。最后,确保删除不再需要的模型和数据集,这将节省云成本。

本文节选自 Apress (Springer Nature)出版的《在 Google 云平台上构建机器学习和深度学习模型》一书。

在 Go 中构建英雄联盟团队推荐器

原文:https://towardsdatascience.com/building-a-league-of-legends-champions-recommender-system-in-go-and-how-to-deploy-it-in-the-cloud-1ee7a4fb55ee?source=collection_archive---------13-----------------------

如何从头开始构建推荐系统,并部署在云中

自从机器学习的热潮开始以来,我们在线生活的许多方面都成为了它的目标。特别是,它的主要用例之一——推荐系统——在我们的生活中变得如此普遍,以至于有时我们会忘记它们的存在。这些智能平台试图向我们推荐一些东西——在许多情况下,我们甚至不知道它的存在——基于我们以前互动过的其他东西,即购买、选择、看到的东西。购物、选择吃什么、浏览电影库等日常活动是智能推荐功能增强的部分任务。

尽管推荐系统隐约出现在我们数字生活的几个方面,但一个或多或少没有被它触及的领域是视频游戏领域。

迄今为止,一些作者和研究人员已经撰写和开发了关于游戏推荐系统的论文和项目。他们中的大多数探索概念,例如一个人基于其先前的游戏选择购买游戏的可能性有多大,例如,喜欢口袋妖怪的人可能喜欢马里奥,或者基于特征和概念的选择,例如,喜欢运动的人可能喜欢最新的篮球或足球游戏。这些项目的细节是,它们是围绕视频游戏作为一种产品,作为你购买的东西,更像是一个推荐电影的系统,它们都不是游戏本身的实现功能。

很久以前,游戏只是一个简单的软件,你可以把它交上去,完成故事,然后继续前进。今天的游戏已经变得更加复杂互联社交。对于复杂这个术语,我指的是玩家在游戏中必须做出的高级决策选择。关于连接性,我指的是每天都有更多的游戏是“仅限在线”的,甚至提供购买、广告和实施被称为游戏即服务的商业模式。最后,今天的游戏更加社会化——有些游戏在同一个区域有 100 名玩家,有些大型在线游戏有成千上万的用户共享一个世界,甚至有些游戏可以让我们将其体验带到其他外部平台,如专有的类似社交的中心。

将这三种属性混合在一起,我们就可以把它看作是一个数据生成器。诸如玩家在游戏中做出的决定,或者玩家参与的每个队友,以及比赛的每个结果都是我们可以跟踪、分析并最终从中学习的数据点。

在这篇文章中,我介绍了一个我用 Go 为游戏英雄联盟 (LoL)从头开始构建的推荐系统。LoL 是一个多人在线战场游戏(MOBA ),有两个由五名玩家组成的队伍(默认模式)互相竞争。用通俗的话来说,我们可以把游戏的目标总结成下面这句话:每支队伍都必须在对方队伍消灭你的队伍之前到达对方队伍的中枢(被称为 Nexus)并摧毁它。这项任务并不容易,因为每一方都有防御建筑、物品和爪牙来阻碍你的每一步。然而,LoL 的真正复杂性在于战士,或者像他们在游戏中所说的冠军

Choices

英雄联盟有 141 个冠军(实验时),没有一个是完美的;他们有弱点,也有优点,有些擅长某些任务,有些则很糟糕。在每场比赛开始时,每个选手都要选出自己的冠军。然而,在游戏中选择冠军有特定的规则,更重要的是冠军不能被复制,也就是说每个冠军只能有一个副本。这个规则有一些罕见的例外,甚至更多的限制,例如每个玩家选择其冠军的顺序,但为了简单起见(在这个项目的第一个版本中),系统不会考虑这些情况。

我创建的推荐系统处理那些冠军,它的目的是推荐一个完整的团队组成,也就是每个玩家应该选择的五个冠军。此外,除了拥有一个推荐系统之外,我的目标是构建一个完整的程序,通过 web 服务提供推荐服务。

在整篇文章中,我解释了设计系统的所有步骤,包括数据收集过程、基准测试和推荐算法。此外,我还描述了如何将项目转移到 Docker 容器中,并使用部署在云中的 web 服务公开建议。

哦,我不属于暴乱游戏。这个作品呈现了我为了好玩而做的一个项目:)。

这些建议

这个项目的中心愿景是为英雄联盟比赛推荐一个完整的团队组成,即由五个不同的冠军组成的团队。这些建议,基于以前看到的训练数据,应该(希望!)好到可以用在真实的游戏里。

该算法

该系统采用了一种基于邻域的协同过滤算法,该算法使用 k-NN (k-nearest neighbors)来查找其最近的对象。在训练时,因为 k-NN 是一个懒惰的学习者,所以模型不会学习一个目标函数。相反,一旦向系统发出预测请求,它将“记忆”数据并从中进行归纳。在这个预测阶段,该算法从它的搜索空间循环通过每一个训练样本,并计算它们和输入特征向量之间的距离。一旦完成距离的计算,它们将按降序排列,算法最终返回前 N 个项目。

该算法的输入是一个不完整的团队组成,换句话说,是一个冠军名单,由其中的一个、两个、三个或四个组成。输出是一个完整的团队组成,由算法认为最合适的五个冠军组成。例如,输入向量[ashe, drmundo]可能会返回[ashe, drmundo, masteryi, yasuo, zyra]

The recommender system is wondering what’s the best recommendation. Champions images taken from https://na.leagueoflegends.com/en/game-info/champions/. Robot icon was taken from https://www.flaticon.com/authors/smashicons

数据

我通过 Python 包装库 Riot-Watcher 从 Riot Games 的英雄联盟 API 获得了用于训练系统的数据。获得最终的数据集并不是一个简单的过程,因为我需要的数据是获胜球队的组成,换句话说,我想知道哪五个冠军赢得了比赛。然而,除了返回当前正在进行的特征匹配的端点之外,没有从过去的随机匹配中获得数据的直接方法。那么,我们如何从这里着手呢?

这里的主要目标是获得许多比赛 id,这样我们就可以使用这些 id 来获得关于比赛的信息,特别是球队组成和比赛结果(谁赢了)。我的第一步是使用“特色游戏”端点来检索所述特色比赛的玩家的召唤师姓名。然后我调用了召唤师(玩家)端点,使用召唤师名字作为参数来获取它的匹配和参与者;这样做的目的是获得尽可能多的帐户 id。这些帐户 id 随后被用来获取更多的匹配 id,这些 id 最终在“match”端点中被用来获取我需要的数据。

顺便提一下,我应该提到 Riot API 有速率限制,所以收集数据的过程需要几天时间。我还想声明,我这样做是出于好意。在任何时候,我的目标都不是误用或滥用系统,所以这似乎是不道德的,或者如果有人——主要来自 Riot——认为我应该取消这个解释,请与我沟通。

所以,我们终于有了数据。然而,它不是有用的形状,因此作为所有与数据相关的东西,我不得不花一些时间处理我的数据,将它转换成一个由N行(每个观察)和M列组成的数据集,其中每一列代表一个冠军。如果所述冠军是团队配置的一部分,则这些行的值是01。我的最终数据集的形状是 110064 x 141。

推荐引擎

尽管推荐算法是该系统的核心,但其他几个功能改善了整个引擎。首先,在算法中,有距离的概念,这是一个对象(模型搜索空间中的一个团队)与另一个对象之间距离的度量。然后,我们有了我命名为拦截偶然发现推荐以及洗牌操作的概念。这三个是我在系统中实现的增强技术,用于修改推荐输出。最后,该平台还包含一个 web 服务形式的抽象层,用于向用户提供推荐。在接下来的小节中,我将详述这些设计。

距离

在预测期间,该算法计算训练样本和给定输入向量之间的距离,以找到最接近的团队配置。因为这是算法的重要部分,所以我实现了四种不同的距离方法,用户可以在执行预测之前选择这四种方法。这些是欧几里德曼哈顿距离、余弦相似度皮尔逊相关度。对于后一个,皮尔逊相关性,我修改了它,因为我想和其他的保持一致,最小值为零。

洗牌

shuffle 是我添加的一个可选操作,它打乱了算法在向量空间中循环计算距离的顺序。否则,向量空间总是按照加载每个训练示例的顺序来遍历,例如,第 1 行、第 2 行、第 3 行等等。我将在下一个例子中解释我为什么这样做。

假设我们有由三个观察值组成数据集:X,Y,Z(按照精确的顺序),且我们想要得到输入向量I的单个预测(用k=1)。系统所做的是计算(X, I)(Y,I)(Z,I)之间的距离。

现在想象一下(X, I)(Y, I)之间的距离完全相同。在这种情况下,在对距离排序后(如算法部分所述),算法总是推荐团队组成 X,因为它在 y 之前。

然而!如果我们打乱算法遍历训练集的顺序,我们可能会遇到算法首先计算(Y, I)而不是(X, I)之间的距离的情况,在这种情况下,推荐的团队是 y

这个有用吗?很难说。

拦截推荐

为了让推荐更容易阅读和理解,我实现了一个我称之为截取推荐的东西。通常,默认情况下,推荐的输出是 k 个团队组成的列表,其中 k 是所选邻居的数量。这个列表的问题是,它没有直接指出哪一个团队应该是最好的。相反,用户应该根据自己的知识最终决定应该使用 k 组中的哪一个。

截距推荐所做的是计算所有 k 个推荐团队的截距,以返回出现在所有 k 个推荐中的冠军,因此用户不是有几个可能的团队,而是收到一个可能的和最合适的冠军列表,这些冠军对于其团队来说应该是最佳的。

例如,假设一个带有k=3的普通推荐返回[1,2,3,4,5[29,10,2,3,8][3,4,2,22,81]。在截距推荐下,输出将是[2,3],因为这些冠军出现在三个原始推荐中。

偶然推荐

偶然发现的推荐是一种推荐模式,通过向算法的输出添加额外的团队组成(在预测步骤中计算)来增强。在推荐系统中,意外收获的概念代表了获得可能让用户感到惊讶的推荐的想法,或者如论文 通过协作方法引入意外收获 “找到好的或有用的东西,而
没有专门搜索它。”

该算法获得特殊的偶然推荐(在已经计算了正常推荐的项目之后)所采取的步骤如下:

  1. 获取下 k 个最接近的项目,其中 k 是邻居的数量。换句话说,如果是k=3,那么算法会在之前找到的推荐之后寻找三个推荐(有点像使用k=6)。
  2. 构建一个包含所有冠军的频率(出现次数)的数组,以了解每个冠军的计数。
  3. 根据频率使用前 5 名冠军创建新的推荐。

这个特性背后的直觉是增加搜索空间(通过上升k)来找到出现最多的冠军。这种方法的主要优点是,系统正在构建一个全新的团队,否则无法根据频率找到。然而,另一方面,配置可能是可怕的。

网络服务

这个项目的主要目标之一是向外界提供建议。因此,我编写了一个简单的 REST API,将系统的“推荐”组件包装在一个端点下。我命名为“推荐”的这个端点接收一个 JSON 作为输入,它应该遵循以下结构(这是 Go 代码):

type PredictionInput struct {
    Champions   []string `json:"champions"`
    Intercept   bool     `json:"intercept"`
    Shuffle     bool     `json:"shuffle"`
    Serendipity bool     `json:"serendipity"`
}

首先,JSON 需要一个冠军的列表,也就是我们当前的团队,或者我们希望使用推荐完成的团队。除此之外,JSON 接受三个布尔值,指定算法是否应该计算截距或意外发现推荐,或者执行洗牌。

为了编译和运行服务,从项目的根目录执行make run-service-local,或者如果你已经安装了 Docker ,命令make run-service-docker下载并运行图像。

当服务正在运行时(无论是在本地还是从 Docker),执行如下命令以获得建议。:

$ curl -d '{"champions":["jax", "ashe"], "intercept": true}' -H "Content-Type: application/json" -X POST [http://localhost:8080/recommend](http://localhost:8080/recommend)

云部署

有了文档化的推荐引擎和服务于推荐的 web 服务,我的下一步是让每个人都可以访问这个系统。因此我选择将它部署在云平台应用程序 Heroku 中。

在 Heroku 中部署 Docker 映像是一个相当简单的过程。创建新帐户后,转到项目的根目录(或任何包含 docker 文件的目录)并执行:

$ heroku container: login登录注册表容器

这个命令用一个随机生成的名字命名应用程序(我的名字是 evening-citadel

$ heroku container:push web -a name_of_app推送 Docker 图像

$ heroku container:release web -a name_of_app释放它。

如需进一步说明,请访问:https://dev center . heroku . com/articles/container-registry-and-runtime。

我的网络应用程序在 http://evening-citadel-74818.herokuapp.com的可用

要查询它,运行前面的命令,但是用地址替换 localhost。像这样:

$ curl -d '{"champions": ["ashe", "drmundo"], "intercept": false, "shuffle": true}' -H "Content-Type: application/json" -X POST [http://evening-citadel-74818.herokuapp.com:80/recommend](http://evening-citadel-74818.herokuapp.com:80/recommend)

注意:系统是在自由层机器上运行的,所以如果它关闭或变慢,请提前道歉。还有,不要滥用:)

基准

懒惰学习者算法,比如这个实验中使用的算法,通常比它的对手,渴望学习者要慢。这种系统不会从训练数据中进行归纳,因此出现了“懒惰”一词,直到收到预测请求。然后,在预测阶段,算法必须扫描完整的训练集来计算其结果。为了了解我的系统的延迟,我对一个 4000 行的测试数据集进行了测试,以收集关于预测每个建议所用时间的数据。在下图中,我们可以看到时间(以秒为单位)如何类似于右偏分布,这表明在一些情况下,预测时间远离平均值。然而,由于这些值本身非常小,几毫秒的差异是不明显的。

总结和结论

本文一步一步地解释了如何从头构建一个用 Golang 编写的基于邻居的推荐系统来预测英雄联盟冠军,以及如何在 Heroku 中部署它。首先,我提到了视频游戏中推荐系统的现状和这个项目的前景。接下来,我解释了算法、数据和获取数据的步骤。然后我们进入了系统的细节,了解了它的特性和云部署流程。最后,我展示了一些基准。

我应该提到的是,这个引擎既不完美也不完整,它是我们如何为视频游戏建立推荐系统的概念证明。英雄联盟和游戏的冠军选择组件涉及更复杂的过程,如禁止冠军(任何玩家都不能使用的冠军),以及每个玩家选择其冠军的顺序。这些过程需要高水平的专业知识,这是不容易自动化或学习的。

在未来,我想添加更多的功能,如自动更新数据集,按游戏类型过滤推荐,甚至尝试在算法中添加“球员位置”的概念。

关于如何使用该服务的代码、脚本、说明以及本文中介绍的所有内容都可以在我的 GitHub 上获得(见下面的链接),所以我想邀请人们贡献并试验这个项目或服务(同样,请不要滥用它)。

[## juandes/lol-推荐系统

用 Golang 写的基于邻居的推荐系统推荐英雄联盟的队伍…

github.com](https://github.com/juandes/lol-recommendation-system)

感谢阅读,希望你从中有所收获。如果你有任何问题,评论,疑问,或者想聊天,请在这里留下评论,或者在 Twitter 上给我写信,我很乐意帮助你。

[## 胡安·德迪奥斯·桑托斯(@ jdiosantos)|推特

胡安·德迪奥斯·桑托斯的最新推文(@ jdiossantos)。机器学习/数据工程师。还有,口袋妖怪大师,还有…

twitter.com](https://twitter.com/jdiossantos)

用 Python 构建一个最小的区块链

原文:https://towardsdatascience.com/building-a-minimal-blockchain-in-python-4f2e9934101d?source=collection_archive---------4-----------------------

通过编码了解区块链

Photo by Shaojie on Unsplash

区块链不仅仅是比特币或其他加密货币。它也用于数据库,如健康记录。

与某种流行的观念相反,区块链与数据加密无关。事实上,区块链中的所有数据都是透明的。它的特别之处在于它(在一定程度上)防止了回溯和数据篡改。让我们使用 Python 实现一个最小的区块链。下面是我如何构建一个最小的区块链,代码可以在 GitHub 上找到。

因为这是区块链的最小实现,所以在任何分布式网络上都没有算法或工作证明。

散列法

我们想要一个可以代表一个数据块的“键”。我们想要一个很难伪造或暴力破解的密钥,但是很容易验证。这就是哈希的用武之地。散列是满足以下性质的函数 H(x ):

  • 相同的输入x总是产生相同的输出H(x)
  • 不同(甚至相似)的输入x应该产生 完全 不同的输出H(x)
  • 从输入x中获得H(x)在计算上很容易,但要逆转这个过程却很难,即从已知散列H中获得输入x

这就是谷歌如何存储你的“密码”,而不是实际存储你的密码。他们存储你的密码散列H(password),这样他们可以通过散列你的输入和比较来验证你的密码。没有进入太多的细节,我们将使用 SHA-256 算法散列我们的块。

Photo by CMDR Shane on Unsplash

最小块

我们来做一个名为MinimalBlock()的对象类。它是通过提供一个index、一个timestamp、一些你想要存储的data和一个叫做previous_hash的东西来初始化的。前一个哈希是前一个块的哈希(键),它充当指针,这样我们就知道哪个块是前一个块,从而知道块是如何连接的。

换句话说,Block[x]包含索引x、时间戳、一些数据和前一个块 x-1 H(Block[x-1])的散列。现在这个块已经完成了,它可以被散列以生成H(Block[x])作为下一个块中的指针。

Photo by Aiden Marples on Unsplash

最小链

区块链本质上是一个区块链,通过存储前一个区块的哈希来建立连接。因此,可以使用 Python 列表实现一个链,而blocks[i]表示第{ i }个块。

当我们初始化一个链时,我们用函数get_genesis_block()自动分配一个第 0 块(也称为 Genesis 块)给这个链。这一块标志着你的链的开始。注意previous_hash在创世纪块中是任意的。添加块可以通过调用add_block()来实现。

Photo by Rustic Vegan on Unsplash

数据验证

数据完整性对数据库非常重要,区块链提供了一种验证所有数据的简单方法。在函数verify()中,我们检查以下内容:

  • blocks[i]中的索引是i,因此没有丢失或额外的块。
  • 计算块哈希H(blocks[i]),并与记录的哈希进行交叉检查。即使块中的单个位被改变,计算出的块散列也会完全不同。
  • 验证H(blocks[i])是否正确存储在下一个程序块的previous_hash中。
  • 通过查看时间戳来检查是否有回溯。

分支

在某些情况下,您可能想从一个链中分支出来。这被称为分叉,如代码中的fork()所示。你可以复制一个链(或者链的根),然后分道扬镳。在 Python 中使用deepcopy()至关重要,因为 Python 列表是可变的。

Photo by elCarito on Unsplash

外卖

对一个块进行哈希运算会创建一个块的唯一标识符,并且一个块的哈希构成下一个块的一部分,以在块之间建立链接。只有相同的数据才会创建相同的哈希。

如果您想修改第三个块中的数据,第三个块的散列值会改变,第四个块中的previous_hash也需要改变。previous_hash是第 4 个块的一部分,因此它的散列也会改变,依此类推。

相关文章

感谢您的阅读!如果您对 Python 感兴趣,请查看以下文章:

[## 我希望我能早点知道的 5 个 Python 特性

超越 lambda、map 和 filter 的 Python 技巧

towardsdatascience.com](/5-python-features-i-wish-i-had-known-earlier-bc16e4a13bf4) [## 使用交互式地图和动画可视化伦敦的自行车移动性

探索 Python 中的数据可视化工具

towardsdatascience.com](/visualizing-bike-mobility-in-london-using-interactive-maps-for-absolute-beginners-3b9f55ccb59)

最初发布于edenau . github . io

建立模型?这是你应该问的第一个问题

原文:https://towardsdatascience.com/building-a-model-heres-the-first-question-you-should-ask-828befec5ac?source=collection_archive---------12-----------------------

无论你的模型是解释性的还是预测性的,都会对它的设计产生深远的影响

有人正在某个地方建造一个模型。事实上很多很多人。无论是为了商业、学术研究还是个人兴趣,人们已经越来越多地使用数学来模拟现实世界的现象,以便产生洞察力或就如何控制或应对这些现象做出决策。

最近,由于更强大的计算能力,建模变得更加复杂。模型不是 Excel 电子表格中的几个单元格,而是建立在各种平台和各种编程语言上。有的是基于小数据,有的是基于大数据。创建它们的工作从几个小时到持续几个月甚至几年的迭代项目不等。

但往往这些模型的创建者在开始之前并没有问足够多的问题。他们可以不假思索地投入其中。获取一些数据,建立一些公式,你就可以开始了。我在数学和统计学领域工作多年,了解到模型的成功在很大程度上取决于在打开数据文件之前的预先思考。

特别是,有一个问题我总是在一开始就问——我相信分析师、数据科学家和其他建模者也应该总是问:我的模型应该是解释性的还是预测性的?

从文字上来看,这可能是显而易见的,但是一个解释性模型被创建来帮助理解为什么事情会发生。它可以帮助回答这样的问题:为什么这种疾病似乎发生在这些类型的人身上?是什么导致了温度激增?一个预测模型被创建来尽可能准确地预测将会发生什么——它将回答这样的问题:我们预计明天会有多少人来参观这个购物中心?在下次选举中,每个政党将获得多少选票?

说明这一点的一个非常简单的方法是使用柠檬水摊主的类比。柠檬水摊主会使用一个解释性模型来了解她的客户喜欢她的产品的原因,或者为什么她在中午比晚上有更多的客户-她基本上对柠檬水感兴趣,以及它为什么畅销。然而,如果她的主要目标是确保她在本周剩余的时间里有足够的柠檬,她会使用预测模型来帮助她。

模型很少能同时达到这两个目标。我想我从来没有建立过一个模型,既能很好地解释一种现象,又能很好地预测这种现象。这是有充分理由的。在本文中,我将阐述这种选择如何影响您构建模型的每个部分,从最初的数据输入开始,一直到您如何衡量它的有效性。

1.输入数据的选择(一次性或重复使用)

如果模型是解释性的,那么建模过程只发生一次,或者在将来偶尔发生。当务之急是尽可能深入地理解这个问题。因此,没有超出范围的数据源。格式不良且需要大量清理的数据可以放在这个列表中。即使是不存在电子版本且仍在档案柜中的旧数据,也可以考虑进行数字化处理,以尽可能做到详尽无遗。同样,为了挖掘更深层次的解释变量,某些数据可能会从模型中删除。在医学模型中,年龄可能会被删除,因为它是疾病易感性中的一个已知因素,并且它可能会主导该模型并掩盖其他重要因素。

预测模型设计为反复运行,以便可以利用定型集中确定的关系,根据输入模型的新数据进行预测。因此,数据的选择主要是基于将来在模型中运行的可用性。在许多现代环境中,这通常意味着预测模型被限制为仅使用连接源中的数据,这些数据容易获得并预先格式化以与模型一起工作。此外,通常主要目标是准确预测,因此任何有助于提高预测准确性的数据都在发挥作用(尽管通常应该对预测模型中准确性和归纳偏差之间的权衡进行有益的讨论)。

2.使用的建模技术(可解释的或“黑盒”)

对于解释性模型来说,适合解释的建模技术至关重要。洞察力的控制在解释模型中至关重要。在逻辑回归中,优势比可以帮助我们理解输入变量影响因变量的程度。更简单的决策树模型可能具有有用的解释目的,因为它们可以帮助识别和量化某些决策点对结果的影响。

预测模型很少考虑可解释性。你可能听说过“黑盒模型”这个术语,用来描述一个最大化预测能力的模型,但是这个模型本质上太复杂了,无法梳理出单个输入因素的影响。神经网络是非常常见的黑盒模型。它们在幕后非常复杂,并基于成百上千个模拟和互联的神经元做出决策,每个神经元都根据从训练集中学习到的行为采取行动。

3.衡量模型的性能(拟合与准确性)

解释性模型主要通过它们产生的洞察力和整体拟合度来判断。拟合优度是因变量的期望值和实际观察值之间接近程度的度量。即使整体拟合度很差,解释性模型也有可能产生有价值的见解,这确实很常见——例如,在我主要从事的社会科学领域,这很常见。解释性建模结果中使用的典型测量方法包括优势比、R 平方(包括伪 R 平方)、卡方检验和 G 检验。

预测模型的生死取决于它们的准确性。准确性测量通常涉及回归模型中误差的计算,或者分类模型中真阳性和假阳性之间的权衡。平均绝对误差和均方根误差等测量值通常用于描述回归模型的预测效果。精确度、回忆、ROC 曲线下面积或 F1 分数(对于不平衡模型)是用于评估预测准确性的更典型的度量。

多年来,我已经学会了设身处地为柠檬水摊主着想的习惯。我对柠檬水还是柠檬感兴趣?这是一个非常好的习惯,我希望你能养成。

最初我是一名纯粹的数学家,后来我成为了一名心理计量学家和数据科学家。我热衷于将所有这些学科的严谨性应用到复杂的人的问题上。我也是一个编码极客和日本 RPG 的超级粉丝。在 LinkedIn Twitter上找我。

使用使用谷歌图像创建的数据集的电影流派分类器

原文:https://towardsdatascience.com/building-a-movie-genre-classifier-using-a-dataset-created-using-google-images-4752f75a1d79?source=collection_archive---------20-----------------------

使用 fast-ai

source: wallpaperup

在为分类问题构建数据库时,“谷歌图片”是找到相关图片的绝佳来源。就拿电影海报按流派分类的问题来说吧。我们将选三门重叠最少的课:爱情、恐怖和超级英雄。

创建数据集

获取 URL 列表:

第一步是获取一个我们可以下载图片的 URL 列表。为此,进入谷歌图片,搜索你感兴趣的图片。向下滚动,直到你看到所有你想下载的图片,或者直到你看到一个按钮,上面写着“显示更多结果”。你滚动过的所有图片现在都可以下载了。要了解更多信息,请点击按钮,然后继续滚动。Google Images 显示的最大图片数量为 700 张。

在 Windows/Linux 中按 Ctrl+Shift+J,在 Mac 中按 Cmd+Opt+J,打开 JavaScript 控制台。使用以下几行 JS 代码获取 URL 并将它们保存到一个文件中:

urls **=** Array.from(document.querySelectorAll('.rg_di .rg_meta')).map(el**=>**JSON.parse(el.textContent).ou);
window.open('data:text/csv;charset=utf-8,' **+** escape(urls.join('\n')));

Source: Horror movie posters on Google images

然后,我们将 URL 的文件上传到我们的工作目录,在为我们的电影数据集创建的文件夹中:

下载图像和查看数据:

我们可以使用 fast.ai 提供的download_images函数下载所有使用 URL 文件的图像,并使用‘show _ batch’函数查看它们:

训练模型

既然我们的数据集已经准备好了,我们就可以训练模型了。我使用 ResNet-34 模型的预训练权重进行迁移学习。

我们现在有 80.5%的准确率。

解释

很多时候,低准确性是由于数据集的问题,例如训练或验证集中的错误标记的图像、可能属于多个高概率类的图像等。我们可以按如下方式查看混淆矩阵和最混淆的图像:

在大多数情况下,我们可以清楚地看到分类器混淆的原因。例如,第一张电影海报(银河护卫队)在我们的数据集中被错误地标记为“浪漫”,第二张图像没有任何恐怖的成分,因此被标记为“恐怖”,第五张海报(蝙蝠侠)看起来很像“恐怖”电影海报,第六张海报似乎有点不相关,第八张海报(侠探杰克)甚至对于一个人来说,仅仅看一眼海报(没有任何其他背景)并将其归类为“超级英雄”电影是不可能的。

清理

既然我们已经确定了导致问题的图像,我们可以通过删除问题图像来清理我们的数据集。我们可以使用 fast.ai 提供的“ ImageCleaner ”小部件,或者编写一个自定义函数来查看损失最大的图像,并删除我们认为有问题的图像(确保不要删除您认为分类器分类错误的图像,尽管它们显然属于给定的标签)。此外,创建原始数据集的副本,以防万一你想回到旧版本。

完成清洁后,再次训练模型,看看是否有任何改进。

以下是我清理数据集并再次训练后的结果:

测试我们的分类器

找到一个不属于数据集的图像来测试您的分类器。将其上传到您的工作目录,并按如下方式进行测试:

实验:

为了尝试和改进分类器,我想到并尝试了一些技术:

  1. 减少训练图片的数量:这背后的动机是当你在谷歌图片搜索中向下滚动时,图片变得不那么相关。因此,为了确保我不会在我的数据集中看到许多不相关的图片,我将每个类的图片数量限制为 80 张。解冻前我得到了 68.8%的准确率,解冻后得到了 82.2%的准确率。即使在清理之后,准确率也没有提高多少,事实上,因为图像的数量更少,所以准确率更低了。这清楚地表明,为了有一个训练有素的模型,我们需要有大量的数据。

2.增加训练图像的数量:通过使用 600 张图像,我注意到不解冻的准确率下降到 43%,解冻一次清理后,我能达到的最高准确率是 49.4%。低精度很可能是因为存在更多的不相关图像,如前一点所述。事实证明,最好有较少但相关的图片。

3.精选图片:尽管在大多数情况下非常不切实际,但尝试在每个类别中精选大约 100 张图片并观察结果会很有趣。

4.用 resnet50 代替 resnet34 进行迁移学习:在迁移学习中,我们使用的基础模型非常重要。我们可以尝试不同的模型,挑选最好的一个。

5.增加/改变课程:在这个实验中,我注意到恐怖和超级英雄电影海报有一个非常相似的主题(黑暗、阴郁、严肃),尝试更多互斥的课程可能会得到更好的结果。

观察

我们的分类器已经大致了解了这三种类型的典型电影海报的外观。虽然我们已经尽量减少重叠课,但事实证明,很多超级英雄电影海报看起来很恐怖,恐怖电影海报看起来很浪漫等等。然而,87.2%的准确率已经相当不错了,因为我们是从零开始构建数据集的,而且类别重叠的几率很高。

使用熊猫构建电影推荐引擎

原文:https://towardsdatascience.com/building-a-movie-recommendation-engine-using-pandas-e0a105ed6762?source=collection_archive---------9-----------------------

探索推荐引擎背后的基本直觉。

概观

推荐引擎是基本上计算两个实体之间相似性的程序,在此基础上,它们给我们有针对性的输出。如果我们看看任何推荐引擎的根源,它们都试图找出两个实体之间的相似性。然后,所计算的相似性可以用于推导各种类型的推荐以及它们之间的关系。

推荐引擎大多基于以下技术:

  1. 基于流行度的过滤。
  2. 协作过滤(基于用户/基于项目)。
  3. 基于混合用户项目的协同过滤。
  4. 基于内容的过滤。

基于人气的过滤

推荐引擎最基本的形式是引擎向所有用户推荐最受欢迎的项目。这将是普遍的,因为每个人都会得到类似的建议,因为我们没有个性化的建议。这些类型的推荐引擎是基于的基于流行度的过滤。这种模式的使用案例是新闻网站上当天的“头条新闻”部分,在这里,不管每个用户的兴趣如何,对每个人来说最受欢迎的新闻都是一样的,因为这符合逻辑,因为新闻是一种一般化的东西,与用户的兴趣无关。

协同过滤

在协同过滤中,两个实体基于它们之间的某些相似性来协作推断推荐。这些过滤技术大致有两种类型:

  1. 基于用户的协同过滤:在基于用户的协同过滤中,我们找出两个用户之间的相似性得分。基于相似性得分,我们将一个用户购买/喜欢的项目推荐给其他用户,假设他可能基于相似性喜欢这些项目。当我们着手实施时,这一点会更加清楚。主要的在线流媒体服务,网飞都有基于用户协同过滤的推荐引擎。
  2. 基于项目的协同过滤:在基于项目的协同过滤中,项目的相似度是用现有用户正在消费的现有项目来计算的。然后,基于相似性的数量,我们可以说,如果用户 X 喜欢项目 A,并且新的项目 P 与项目 A 最相似,那么我们向用户 X 推荐项目 P 是非常有意义的
  3. 基于混合用户项目的协同过滤:这种技术基本上是上述两种技术的适当混合,其中推荐不仅仅基于任一种。像亚马逊这样的电子商务网站采用这种技术向顾客推荐商品。
  4. 基于内容的过滤:在这种技术中,向用户推荐他们以前最用过/看过/喜欢的相似内容。例如,如果用户一直在听相似类型的歌曲(比特率、bps、曲调等)。),他将被推荐属于基于某些特征决定的相同类别的歌曲。这一类别最好的例子是 Pandora Radio ,这是一个音乐流媒体和自动音乐推荐网络电台服务。

编码和实现

我们有一个电影镜头数据库,我们的目标是使用 pandas 从头开始应用各种推荐技术,并找出用户、最受欢迎的电影和基于基于用户的协同过滤的目标用户个性化推荐之间的相似性。(我们只探索其中一种类型,因为这些文章是关于获得推荐引擎背后的基本直觉。)

我们正在从数学库中导入熊猫和一些基本的数学函数,并将数据集导入 dataframe 对象。

**# Importing the required libraries.**
import pandas as pd
from math import pow, sqrt**# Reading ratings dataset into a pandas dataframe object.**
r_cols = ['user_id', 'movie_id', 'rating', 'unix_timestamp']
ratings = pd.read_csv('data/ratings.dat', sep='::', names=r_cols,
 encoding='latin-1')**# Getting number of users and movies from the dataset.**
user_ids = ratings.user_id.unique().tolist()
movie_ids = ratings.movie_id.unique().tolist()
print('Number of Users: {}'.format(len(user_ids)))
print('Number of Movies: {}'.format(len(movie_ids)))**Output:** Number of Users: **6040**
Number of Movies: **3706**

这是我们的数据集的前 5 行的样子。

Ratings Dataset

在这个数据集中,我们有 4 列和大约 1M 行。除了, unix_timestamp,所有的列都是自解释的。无论如何,我们不会在代码中使用这个列。接下来,让我们看看我们的电影数据集是什么样子的。

**# Reading movies dataset into a pandas dataframe object.**
m_cols = ['movie_id', 'movie_title', 'genre']
movies = pd.read_csv('data/movies.dat', sep='::', names=m_cols, encoding='latin-1')

Movie Dataset

所有的列名都是不言自明的。如上面的数据帧所示,类型列包含带有管道分隔符的数据,这些数据不能作为建议进行处理。因此,我们需要为每个流派类型生成列,这样,如果电影属于该流派,其值将为 1,否则为 0(类似于一种热门编码)。此外,我们需要从 movie_title 列中分离出年度发布,并为其生成一个新列,这也是一个新的重要特性。

**# Getting series of lists by applying split operation.**
movies.genre = movies.genre.str.split('|')**# Getting distinct genre types for generating columns of genre type.**
genre_columns = list(set([j for i in movies['genre'].tolist() for j in i]))**# Iterating over every list to create and fill values into columns.**
for j in genre_columns:
    movies[j] = 0
for i in range(movies.shape[0]):
    for j in genre_columns:
        if(j in movies['genre'].iloc[i]):
            movies.loc[i,j] = 1**# Separting movie title and year part using split function.**
split_values = movies['movie_title'].str.split("(", n = 1, expand = True)**# setting 'movie_title' values to title part.**
movies.movie_title = split_values[0]**# creating 'release_year' column.**
movies['release_year'] = split_values[1]**# Cleaning the release_year series.**
movies['release_year'] = movies.release_year.str.replace(')','')**# dropping 'genre' columns as it has already been one hot encoded.**
movies.drop('genre',axis=1,inplace=True)

以下是数据帧处理后的样子:

Data Frame View for Movies Dataset After Pre-Processing

现在,让我们写下一些在我们的代码中经常使用的 getter 函数,这样我们就不需要一遍又一遍地编写它们,这也增加了代码的可读性和可重用性。

**# Getting the rating given by a user to a movie.**
**def get_rating_(userid,movieid):**
    return (ratings.loc[(ratings.user_id==userid) & (ratings.movie_id == movieid),'rating'].iloc[0])**# Getting the list of all movie ids the specified user has rated.**
**def get_movieids_(userid):**
    return (ratings.loc[(ratings.user_id==userid),'movie_id'].tolist())**# Getting the movie titles against the movie id.**
**def get_movie_title_(movieid):**
    return (movies.loc[(movies.movie_id == movieid),'movie_title'].iloc[0])

相似性得分

在该实现中,将基于两个用户之间的距离(即欧几里德距离)并通过计算两个用户之间的皮尔逊相关来计算两个用户之间的相似性。

我们将编写两个函数,一个基于欧几里得距离计算相似性,另一个基于皮尔逊相关,这样你就知道我们为什么要编写两个函数了。

**def distance_similarity_score(user1,user2):**
    '''
    user1 & user2 : user ids of two users between which similarity        score is to be calculated.
    '''
    # Count of movies watched by both the users.
    both_watch_count = 0
    for element in ratings.loc[ratings.user_id==user1,'movie_id'].tolist():
        if element in ratings.loc[ratings.user_id==user2,'movie_id'].tolist():
            both_watch_count += 1
    if both_watch_count == 0 :
        return 0

    # Calculating distance based similarity between both the users.
    distance = []
    for element in ratings.loc[ratings.user_id==user1,'movie_id'].tolist():
        if element in ratings.loc[ratings.user_id==user2,'movie_id'].tolist():
            rating1 = get_rating_(user1,element)
            rating2 = get_rating_(user2,element)
            distance.append(pow(rating1 - rating2, 2))
    total_distance = sum(distance)

    # Adding one to the denominator to avoid divide by zero error.
    return 1/(1+sqrt(total_distance))print('Distance based similarity between user ids 1 & 310: {}'.format(distance_similarity_score(1,310)))Output:
Distance based similarity between user ids 1 & 310: **0.14459058185587106**

基于距离计算相似性得分有一个固有的问题。我们没有阈值来决定在计算用户是否足够近或足够远时要考虑两个用户之间的距离。另一方面,皮尔逊相关方法解决了这个问题,因为它总是返回-1 & 1 之间的值,这清楚地为我们提供了我们喜欢的接近度的边界。

**def pearson_correlation_score(user1,user2):**
    '''
    user1 & user2 : user ids of two users between which similarity score is to be calculated.
    '''
    # A list of movies watched by both the users.
    both_watch_count = []

    # Finding movies watched by both the users.
    for element in ratings.loc[ratings.user_id==user1,'movie_id'].tolist():
        if element in ratings.loc[ratings.user_id==user2,'movie_id'].tolist():
            both_watch_count.append(element)

    # Returning '0' correlation for bo common movies.
    if len(both_watch_count) == 0 :
        return 0

    # Calculating Co-Variances.
    rating_sum_1 = sum([get_rating_(user1,element) for element in both_watch_count])
    rating_sum_2 = sum([get_rating_(user2,element) for element in both_watch_count])
    rating_squared_sum_1 = sum([pow(get_rating_(user1,element),2) for element in both_watch_count])
    rating_squared_sum_2 = sum([pow(get_rating_(user2,element),2) for element in both_watch_count])
    product_sum_rating = sum([get_rating_(user1,element) * get_rating_(user2,element) for element in both_watch_count])

    # Returning pearson correlation between both the users.
    numerator = product_sum_rating - ((rating_sum_1 * rating_sum_2) / len(both_watch_count))
    denominator = sqrt((rating_squared_sum_1 - pow(rating_sum_1,2) / len(both_watch_count)) * (rating_squared_sum_2 - pow(rating_sum_2,2) / len(both_watch_count)))

    # Handling 'Divide by Zero' error.
    if denominator == 0:
        return 0
    return numerator/denominatorprint('Pearson Corelation between user ids 11 & 30: {}'.format(pearson_correlation_score(11,30)))Output:
Pearson Corelation between user ids 11 & 30: **0.2042571684752679**

最相似的用户

目标是找出与目标用户最相似的用户。这里,我们有两个指标来计算分数,即距离和相关性。现在,我们将为此编写一个函数。

def most_similar_users_(user1,number_of_users,metric='pearson'):
    '''
    user1 : Targeted User
    number_of_users : number of most similar users you want to user1.
    metric : metric to be used to calculate inter-user similarity score. ('pearson' or else)
    '''
    # Getting distinct user ids.
    user_ids = ratings.user_id.unique().tolist()

    # Getting similarity score between targeted and every other suer in the list(or subset of the list).
    if(metric == 'pearson'):
        similarity_score = [(pearson_correlation_score(user1,nth_user),nth_user) for nth_user in user_ids[:100] if nth_user != user1]
    else:
        similarity_score = [(distance_similarity_score(user1,nth_user),nth_user) for nth_user in user_ids[:100] if nth_user != user1]

    # Sorting in descending order.
    similarity_score.sort()
    similarity_score.reverse()

    # Returning the top most 'number_of_users' similar users. 
    return similarity_score[:number_of_users]print(most_similar_users_(23,5))Output:
**[(0.936585811581694, 61), (0.7076731463403717, 41), (0.6123724356957956, 21), (0.5970863767331771, 25), (0.5477225575051661, 64)]**

正如我们所看到的,输出是元组列表,指示使用用户 id 查询的前 5 个相似数量的用户相对于目标用户的相似性得分。这里使用的指标是皮尔逊相关。

我不知道是否很少有人注意到,最相似的用户的逻辑可以通过考虑其他因素,如年龄、性别、职业等来加强。这里,我们仅根据一个特征(即评级)创建了我们的逻辑。

为目标用户获取电影推荐

概念很简单。首先,我们需要只迭代那些没有被目标用户观看(或评级)的电影,以及基于与目标用户高度相关的用户的子设置项。这里,我们使用了加权相似性方法,其中我们考虑了评级和分数的乘积,以确保高度相似的用户比不太相似的用户对推荐的影响更大。然后,我们根据分数和电影 id 对列表进行排序,并根据这些电影 id 返回电影标题。让我们为此写一个函数。

def get_recommendation_(userid):
    user_ids = ratings.user_id.unique().tolist()
    total = {}
    similariy_sum = {}

    # Iterating over subset of user ids.
    for user in user_ids[:100]:

        # not comparing the user to itself (obviously!)
        if user == userid:
            continue

        # Getting similarity score between the users.
        score = pearson_correlation_score(userid,user)

        # not considering users having zero or less similarity score.
        if score <= 0:
            continue

        # Getting weighted similarity score and sum of similarities between both the users.
        for movieid in get_movieids_(user):
            # Only considering not watched/rated movies
            if movieid not in get_movieids_(userid) or get_rating_(userid,movieid) == 0:
                total[movieid] = 0
                total[movieid] += get_rating_(user,movieid) * score
                similariy_sum[movieid] = 0
                similariy_sum[movieid] += score

    # Normalizing ratings
    ranking = [(tot/similariy_sum[movieid],movieid) for movieid,tot in total.items()]
    ranking.sort()
    ranking.reverse()

    # Getting movie titles against the movie ids.
    recommendations = [get_movie_title_(movieid) for score,movieid in ranking]
    return recommendations[:10]print(get_recommendation_(32))Output:
**['Invisible Man, The ', 'Creature From the Black Lagoon, The ', 'Hellraiser ', 'Almost Famous ', 'Way of the Gun, The ', 'Shane ', 'Naked Gun 2 1/2: The Smell of Fear, The ', "Kelly's Heroes ", 'Official Story, The ', 'Everything You Always Wanted to Know About Sex ']**

正如我们在输出中看到的,我们已经使用度量皮尔逊相关性为用户 id 为 32 的用户获得了前 10 部强烈推荐的电影。你可以用欧几里得距离做同样的练习,我相信结果会不同。

学习和结论

我们通过使用熊猫和基本的数学库函数实现了一个电影推荐引擎。此外,我们了解了推荐引擎背后的基本直觉。显然,推荐引擎的功能远不止这些,因为有多种功能和因素会影响推荐,而不仅仅是评级。此外,在下一篇博客中,我们还将基于用户和电影的其他特征来实现和推断我们的推荐,并探索推荐引擎的臭名昭著的技术,即使用 turicreate 库的矩阵分解

博客中代码的 GitHub 存储库可以在这里找到。

使用 BERT 和 TensorFlow 构建多标签文本分类器

原文:https://towardsdatascience.com/building-a-multi-label-text-classifier-using-bert-and-tensorflow-f188e0ecdc5d?source=collection_archive---------1-----------------------

多标签分类问题中,训练集由实例组成,每个实例可以被分配多个类别,这些类别被表示为一组目标标签,

  • 一条短信可以同时涉及宗教、政治、金融或教育中的任何一个,也可以什么都不涉及。
  • 一部电影可以根据其概要内容分为动作片、喜剧片和爱情片。一部电影有可能分成多种类型,比如浪漫喜剧。

source

多类分类问题有何不同?

多类分类中,每个样本被分配到一个且只有一个标签:水果可以是苹果或梨,但不能同时是两者。让我们考虑一个三类 C= ["太阳,"月亮,云"]的例子。在多类中,每个样本只能属于一个 C 类。在多标签情况下,每个样本可以属于一个或多个类别。

Source

资料组

对于我们的讨论,我们将使用 Kaggle 的 有毒评论分类挑战 数据集,该数据集由大量维基百科评论组成,这些评论已被人类评级者标记为有毒行为。毒性的类型有:

**toxic, severe_toxic, obscene, threat, insult, identity_hate**

示例:

“**Hi! I am back again! Last warning! Stop undoing my edits or die!**”

被标记为[1,0,0,1,0,0]。意思是它既是toxic 又是threat

关于数据集的详细 EDA,请参见此处的。

让我们简单地讨论一下伯特

2018 年 10 月,谷歌发布了一个新的语言表示模型,名为 BERT ,代表来自变形金刚的双向编码器表示。 BERT 基于最近在预训练上下文表示方面的工作,包括半监督序列学习、生成性预训练、 ELMo 和 ULMFit 。然而,与这些之前的模型不同,BERT 是第一个深度双向无监督语言表示,仅使用纯文本语料库(维基百科)进行预训练。

预先训练的表示可以是 上下文无关的上下文相关的

  1. 上下文无关模型,如 word2vec 或 GloVe 为词汇表中的每个单词生成单个单词嵌入表示。例如,单词“ bank ”在“ bank account ”和“ bank of the river”中具有相同的上下文无关的表示。
  2. 上下文模型代之以生成基于句子中其他单词的每个单词的表示。上下文表示还可以是单向的或双向的。例如,在句子“我访问了银行账户”中,基于“我访问了”而不是“账户”,单向上下文模型将表示“银行”然而,BERT 使用它的上一个和下一个上下文来表示“银行”——“我访问了 … 账户”——从一个深度神经网络的最底层开始,使它成为深度双向的。

基于双向 LSTM 的语言模型训练标准的从左到右的语言模型,并且还训练从右到左(反向)的语言模型,该语言模型从随后的单词预测先前的单词,如在 ELMO。在 ELMo 中,前向语言模型和后向语言模型各有一个 LSTM。关键的区别在于,LSTM 没有同时考虑前一个和后一个记号。

为什么 BERT 优于其他双向模型?

直观地说,深度双向模型比从左到右模型或者从左到右和从右到左模型的结合更强大。不幸的是,标准的条件语言模型只能从左到右或从右到左进行训练,因为双向条件反射将允许每个单词在多层上下文中间接“看到自己”。

为了解决这个问题,BERT 使用“屏蔽”技术屏蔽掉输入中的一些单词,然后双向调节每个单词以预测被屏蔽的单词。例如:

Forward, Backward, and Masked Language Modeling

BERT 还通过对一个非常简单的任务进行预训练来学习建立句子之间的关系模型,这个任务可以从任何文本语料库中生成:给定两个句子 A 和 B,B 是语料库中 A 后面的下一个句子,还是只是一个随机的句子?例如:

这只是对伯特的一个非常基本的概述。详细内容请参考最初的论文和一些参考文献【1】、【2】。

好消息:谷歌已经将 BERT 上传到 TensorFlow Hub 这意味着我们可以直接使用预先训练好的模型来解决我们的自然语言处理问题,无论是文本分类还是句子相似度等。

预测电影评论的例子,二进制分类问题作为示例代码提供在存储库中。在本文中,我们将重点讨论 BERT 在多标签文本分类问题中的应用。因此,我们将基本上修改示例代码,并应用必要的更改,使其适用于多标签场景。

设置

使用!pip install bert-tensorflow安装 BERT

下载预训练的 BERT 模型:这些是权重和其他必要的文件,用于表示 BERT 在预训练中学习到的信息。你需要选择你想要的 BERT 预训练重量。有两种方法可以下载和使用预训练的 BERT 模型:

  1. 直接使用 tensorflow-hub:

以下预先训练的模型可供选择。

  1. BERT-Base, Uncased : 12 层,768 隐,12 头,110M 参数
  2. BERT-Large, Uncased : 24 层,1024 隐,16 头,340 米参数
  3. BERT-Base, Cased : 12 层,768 隐,12 头,110M 参数
  4. BERT-Large, Cased : 24 层,1024 隐,16 头,340 米参数
  5. BERT-Base, Multilingual Case : 104 种语言,12 层,768 隐,12 头,110M 参数
  6. BERT-Base, Chinese:简体中文和繁体中文,12 层,768 隐藏,12 个头,110M 参数

我们将使用基本型号:‘未装箱 _ L-12 _ H-768 _ A-12’
**BERT_MODEL_HUB** = “[https://tfhub.dev/google/bert_uncased_L-12_H-768_A-12/1](https://tfhub.dev/google/bert_uncased_L-12_H-768_A-12/1)"

2.手动下载 BERT 模型文件:下载并保存到一个目录中,然后解压缩。以下是英文文件的链接:

  • [BERT-Base, Uncased](https://storage.googleapis.com/bert_models/2018_10_18/uncased_L-12_H-768_A-12.zip), [BERT-Base, Cased](https://storage.googleapis.com/bert_models/2018_10_18/cased_L-12_H-768_A-12.zip),
  • [BERT-Large, Cased](https://storage.googleapis.com/bert_models/2018_10_18/cased_L-24_H-1024_A-16.zip), [BERT-Large, Uncased](https://storage.googleapis.com/bert_models/2018_10_18/uncased_L-24_H-1024_A-16.zip)

您可以使用任何一种方式,但是让我们看看在预先训练的模型中实际上有哪些文件。当我下载[BERT-Base, Uncased](https://storage.googleapis.com/bert_models/2018_10_18/uncased_L-12_H-768_A-12.zip),时,有如下 3 个重要文件:

**BERT_VOCAB**= ‘uncased-l12-h768-a12/vocab.txt' **BERT_INIT_CHKPNT** = ‘uncased-l12-h768-a12/bert_model.ckpt’ **BERT_CONFIG** = ‘uncased-l12-h768-a12/bert_config.json’

**BERT_VOCAB** : 包含模型词汇【单词到索引的映射】

**BERT_INIT_CHKPNT** : 包含预训练模型的权重

**BERT_CONFIG** : 包含 BERT 模型架构。

标记化

标记化包括将输入文本分解成单个单词。为此,第一步是创建 tokenizer 对象。有两种方法可以做到这一点:

  1. 直接来自张量流中心

2.从手动下载的文件:

使用**BERT_INIT_CHKPNT & BERT_VOCAB files**

创建了记号赋予器之后,就该使用它了。让我们来修饰句子:“This here’s an example of using the BERT tokenizer”

词汇表的大小:~30K

数据预处理:

让我们首先阅读提供的数据集:

**train.head()**

我们需要将数据转换成伯特能理解的格式。为此提供了一些实用函数。

**create_examples()**,读取数据帧并将输入文本和相应的目标标签加载到**InputExample** 对象中。

使用 tokenizer,我们将在示例中调用**convert_examples_to_features** 方法,将它们转换成 BERT 理解的特性。这个方法添加了特殊的“CLS”和“SEP”标记,BERT 使用它们来标识句子的开始和结束。它还将“索引”和“段”标记附加到每个输入中。因此,根据 BERT 格式化输入的所有工作都由该函数完成。

BERT input representation. The input embeddings is the sum of the token embeddings, the segmentation embeddings and the position embeddings.

创建模型

这里,我们使用预训练的 BERT 模型,并针对我们的分类任务对其进行微调。基本上,我们加载预训练的模型,然后训练最后一层用于分类任务。

在多标签分类中,我们使用**sigmoid()**而不是**softmax()**来获得概率。

  • 在简单的二进制分类中,两者之间没有太大的区别,但是在多国分类的情况下,sigmoid 允许处理非排他性标签(又名多标签),而 softmax 处理排他性类别。
  • 在计算概率之前, logit (也称为分数)是与类别相关联的原始未缩放值。就神经网络架构而言,这意味着 logit 是密集(全连接)层的输出[3]。

因此,为了计算概率,我们做了如下改变:

**### multi-class case: probabilities = tf.nn.softmax(logits)** **### multi-label case: probabilities = tf.nn.sigmoid(logits)**

为了计算每个示例的损失,tensorflow 提供了另一种方法:

[**tf.nn.sigmoid_cross_entropy_with_logits**](https://www.tensorflow.org/api_docs/python/tf/nn/sigmoid_cross_entropy_with_logits)测量离散分类任务中的概率误差,其中每个类别都是独立的且不互斥。这适用于多标签分类问题[4]。

其余的代码大部分来自 BERT 参考文献[5]。完整的代码可以在 github 获得。

Kaggle 提交分数:

仅仅通过运行 2 个纪元,就获得了非常好的结果。这就是迁移学习的强大之处:使用在庞大数据集上训练过的预训练模型,然后针对特定任务对其进行微调。Kaggle 代码这里。

因此,在其他一些数据集上进行试验,运行几个时期[3–4]并查看结果。

感谢阅读。

参考

[1]https://ai . Google blog . com/2018/11/open-sourcing-Bert-state-of-art-pre . html

[2]https://ml explained . com/2019/01/07/paper-parsed-Bert-pre-training-of-deep-bidirectional-transformers-for-language-understanding-explained/

[3]https://stack overflow . com/questions/47034888/how-to-choose-cross-entropy-loss-in-tensor flow

[4]https://www . tensor flow . org/API _ docs/python/TF/nn/sigmoid _ cross _ entropy _ with _ logits

[5]https://github . com/Google-research/Bert/blob/master/run _ classifier . py

[6]https://www . depends-on-the-definition . com/guide-to-multi-label-class ification-with-neural-networks/

[7]https://towards data science . com/journey-to-the-centre-of-multi-label-class ification-384 c 40229 BFF

[8]https://gombru.github.io/2018/05/23/cross_entropy_loss/

用 PyTorch 构建概率矩阵分解的音乐推荐引擎

原文:https://towardsdatascience.com/building-a-music-recommendation-engine-with-probabilistic-matrix-factorization-in-pytorch-7d2934067d4a?source=collection_archive---------4-----------------------

推荐系统是现代社会中最广泛的机器学习形式之一。无论你是在网飞上寻找下一个节目,还是在 Spotify 上收听自动音乐播放列表,推荐系统几乎影响着现代用户体验的所有方面。构建推荐系统最常见的方法之一是使用矩阵分解,这种方法可以根据以前的评分和其他用户的偏好来预测用户对特定产品的评分。在本文中,我将使用一个音乐评级数据集,通过 PyTorch 构建一个音乐推荐引擎,试图阐明概率矩阵分解模型的理论和实现背后的细节。

推荐系统简介

在这一节中,在介绍音乐推荐项目的实际实现和结果之前,我将尝试快速而全面地介绍推荐系统和矩阵分解。

什么是推荐系统?

要创建一个推荐系统,我们需要一个包含用户、项目和评分的数据集。用户可以是从商店购买产品的顾客、从家里观看电影的家庭,或者在我们的例子中,甚至是听音乐的人。类似地,项目是用户正在购买、评级、收听等的产品。因此,推荐系统的数据集通常有许多条目,包括用户-项目对和表示用户对该项目的评级的值,例如下面看到的数据集:

Figure 1: Simple Data Set for Recommendation Systems

可以明确地收集用户评级(要求用户给出 5 星评级,要求用户是否喜欢某个产品,等等)。)或隐含地(检查购买历史、鼠标移动、收听历史等。).此外,评分值可以是二进制的(用户购买产品)、离散的(对产品的评分为 1-5 星)或几乎任何其他值。事实上,这个项目使用的数据集将评级表示为用户听一个艺术家的总分钟数(这是一个隐含的评级!).数据集中的每个条目代表具有已知评级的用户项目配对,并且从这些已知的用户项目配对中,推荐系统试图预测未知的用户项目评级。可以通过找到与用户喜欢的项目相似的项目(基于项目)、找到相似的用户并推荐他们喜欢的东西(基于用户)、或者找到用户-项目交互之间的整体关系来创建推荐。以这种方式,模型推荐它认为用户可能喜欢的、用户还没有看过的不同项目。

很容易理解一个好的推荐系统的目的和价值,但是这里有一个很好的图表,我认为它很好地总结了推荐系统:

Figure 2: Recommender systems aim to find items that a user enjoys, but has not yet seen [1]

什么是矩阵分解?

矩阵分解是实现推荐系统的一种常用而有效的方法。使用前面提到的用户项目数据集(图 1),矩阵分解试图学习在低维空间中定量表征用户和项目的方法(而不是查看用户曾经评级的每个项目),以便这些表征可以用于预测用户行为和对一组已知的可能项目的偏好。近年来,矩阵分解由于其准确性和可扩展性而变得越来越流行。

使用用户-项目配对的数据集,可以创建一个矩阵,使所有行代表不同的用户,所有列代表不同的项目,矩阵中位置(I,j)处的每个条目代表用户 I 对项目 j 的评级。该矩阵如下图所示:

Figure 3: A User-Item Recommendation Matrix

在上图中,每一行都包含一个用户对可能项目集中的每个项目的评分。但是,其中一些评级可能是空的(用“???").这些空白点代表未知的或者推荐系统试图预测的用户-项目配对。此外,这个被称为稀疏矩阵的部分填充矩阵可以被认为是两个矩阵的乘积。例如,上述 4×4 矩阵可以通过将两个 4×4 矩阵彼此相乘、4×10 矩阵与 10×4 矩阵相乘、4×2 矩阵与 2×4 矩阵相乘等等来创建。将原始矩阵分解为两个矩阵的乘积的过程称为矩阵分解。

推荐系统的矩阵分解

所以,我们知道我们可以通过找到两个可以相乘的矩阵来分解一个矩阵。但是,矩阵分解在推荐系统中实际上是如何工作的呢?

当这两个单独的矩阵被创建时,它们各自携带关于用户、项目以及它们之间的关系的单独信息。也就是说,其中一个矩阵将存储表征用户的信息,而另一个矩阵将存储关于项目的信息。事实上,用户(左)矩阵的每一行都是一个大小为 k 的向量,用于定量描述单个用户,而项目(右)矩阵的每一列都是一个大小为 k 的向量,用于描述单个项目。这些向量的大小 k 被称为潜在维度(或嵌入大小),是矩阵分解模型中必须调整的超参数,较大的潜在维度将允许模型捕捉更复杂的关系并存储更多信息,但也可能导致过度拟合。

这种想法乍一看似乎很奇怪,但如果您仔细研究矩阵如何相乘以创建用户-项目矩阵,它实际上很有意义—用户 I 对项目 j 的评级是通过从左矩阵中找到用户 I 的向量和从右矩阵中找到项目 j 的向量的内积来获得的。因此,如果每个用户和项目的这些矢量嵌入被训练来存储有用的特征信息,内积可以基于项目的特征和用户对这些特征的偏好来准确地预测用户-项目评级,这些都存储在嵌入矢量的值中。下图说明了这一概念:

Figure 4: Matrix Factorization with User (left) and Item (right) Matrices

在上图中,请注意产品矩阵的每个条目是如何导出的。例如,表示用户 0 对项目 0 的评分的用户项目矩阵的条目(0,0)是通过取左侧矩阵的第 0 行(用户 0 的嵌入向量)和右侧矩阵的第 0 列(项目 0 的嵌入向量)的内积获得的,这种模式对用户项目矩阵中的每个评分都是如此。因此,如前所述,通过取相应用户和项目嵌入向量的内积来预测每个评级,这是矩阵分解如何为推荐系统工作的核心思想。使用常见的训练方法,如随机梯度下降或交替最小二乘法,可以训练这两个矩阵以及其中的嵌入向量,以产生与原始用户项目矩阵非常相似的产品,从而创建准确的推荐模型。

但是等等…你不是说概率矩阵分解吗

是的,我做到了!别担心,概率矩阵分解和我到目前为止一直在解释的很像。正规矩阵分解和概率矩阵分解的主要区别在于生成矩阵的方式。对于正常的矩阵分解,所有未知值(我们试图预测的等级)被设置为某个常数(通常为 0 ),并且分解的矩阵被训练以再现整个矩阵,包括未知值。不难理解为什么这可能成为一个问题,因为矩阵分解模型预测的是我们实际上不知道的值,而这些值可能是完全错误的。此外,当我们已经训练我们的模型来预测所有用户定义的随机常数时,我们如何预测未知的用户项目评级?

为了解决这个问题,我们有概率矩阵分解。概率矩阵分解不是试图再现整个结果矩阵,而是仅试图再现训练集或已知评级集中的评级。因此,仅使用已知评级来训练模型,并且可以通过取用户和项目嵌入向量的内积来预测未知评级。

PyTorch 中的概率矩阵分解

现在您已经理解了推荐系统和概率矩阵分解背后的基础知识,我将概述如何使用 PyTorch 实现这种推荐系统的模型。如果您不熟悉 PyTorch,它是一个健壮的 python 框架,经常用于深度学习和科学计算。我鼓励任何对 PyTorch 感兴趣的人去查看一下,因为我在我的研究和个人项目中大量使用这个框架。

我们在努力实现什么?

如图 4 所示,我们的概率矩阵分解模型只有两个矩阵——多么简单!其中一个矩阵将代表每个用户的嵌入,而另一个矩阵将包含每个项目的嵌入。每个嵌入是描述用户或项目的 k 个值(k 是潜在维度)的向量。使用这两个矩阵,可以通过取用户的嵌入向量和项目的嵌入向量的内积来获得对用户-项目配对的评级预测,从而产生表示预测评级的单个值。下图显示了这样一个过程:

Figure 5: Predicting an unknown user-item rating

如上所述,可以通过从用户(左)和项目(右)矩阵中找到相应的用户和项目向量并计算它们的内积来确定单个评级。在这种情况下,结果显示选择的用户是冰淇淋的忠实粉丝——多么令人惊讶!然而,应该注意,这些矩阵中的每一个都是随机初始化的。因此,为了使这些预测和嵌入准确地表征用户和项目,必须使用一组已知的评级来训练它们,以便预测的准确性可以推广到未知的评级。

使用 PyTorch 中的嵌入类可以相对容易地实现这样一个模型,它创建了一个二维嵌入矩阵。使用这些嵌入中的两个,可以在 PyTorch 模块中创建概率矩阵分解模型,如下所示:

Figure 6: Simple matrix factorization implementation

矩阵分解模型包含两个嵌入矩阵,这两个嵌入矩阵在模型的构造函数中初始化,然后经过训练以准确预测未知的用户项目评分。在 forward 函数(用于预测评级)中,向模型传递一个小批量的索引值,这些值代表不同用户和项目的标识号(id)。使用这些作为用户-项目索引对传入的“cats”参数(一个 Nx2 矩阵)的索引,通过索引用户和项目嵌入矩阵并取相应向量的内积来获得预测的评级。

添加偏置

图 6 中的模型缺少一个创建强大的推荐系统所需要的重要特性——偏差项。偏差项基本上是分配给系统中每个用户和项目的常数值。当计算用户-项目配对的预测评级时,用户和项目的偏差都被添加到预测评级值中。这个过程可以从下面观察到:

Figure 7: Adding bias to the Probabilistic Matrix Factorization Model

从上面可以看出,向模型中添加偏差所需的更改量非常小。必须跟踪每个用户/项目的额外值,并将其添加到模型做出的每个预测的结果中。这些偏差值将与实际嵌入一起训练,以产生准确的预测。

向 PyTorch 中的模型添加偏差需要创建两个额外的嵌入——一个用于用户偏差,一个用于项目偏差。这些嵌入将有一个单独的列,每行将代表用户或项目的偏差值,其 ID 对应于该行索引。类似于嵌入向量,这些偏差值应该使用正向方法中“cats”参数中传递的索引来找到。实现如下所示:

Figure 8: Implementing a Probabilistic Matrix Factorization Model with bias

从上面的实现中可以看出,针对用户和项目偏好,在模型中添加了两个新的嵌入。然后将这些偏差向量的值添加到正向方法中每个预测的结果中。

现在,已经实现了一个完整的概率矩阵分解模型——这可以用来创建非常强大的推荐系统,可以为几乎任何业务增加价值。然而,你可能会想,这个模型看起来和上一个非常相似,那么我们为什么要在模型中加入偏见呢?这真的会让事情变得更好吗?

为什么我们甚至需要偏见?

我们将首先考虑每个用户的偏差。如果一个用户有非常高的偏差值,这意味着什么?通常,如果用户经常给项目高的评级,则他们被分配高的偏差值,而经常分配低评级的用户被分配较低的偏差值。这是有意义的,因为一个平均评价很高的用户应该倾向于更高的评价,反之亦然。反过来,这种偏差可以允许模型通过学习适当的偏差值来将用户对项目进行评级的倾向与他们的实际项目偏好分开。

类似地,通常被给予高评级的项目被赋予大的偏差,而具有低平均评级的项目被给予低值。这使得被用户高度评价的项目偏向于高评价,因为大多数用户倾向于喜欢这样的项目,反之亦然。这可以在下图中看到:

Figure 9: Bias values for different items. Highly-rated items are given high bias values and vice versa.

用户或项目的平均评级会在数据集中产生不必要的噪声。通过包括偏差,模型可以学习从这种噪声中分别表征用户和项目,允许用户和项目嵌入更准确地反映每个用户或项目的属性和偏好。

创建音乐推荐系统

现在模型已经创建好了,我准备用它来创建一个音乐推荐系统。在这个项目中,我利用了用户和音乐家的数据集,其中每个用户-音乐家配对都根据用户听一个艺术家的时间分配了一个值。然后,我使用图 8 中的概率矩阵分解模型,从这个数据集中创建一个音乐推荐模型。

简单 EDA

我通过使用熊猫对数据集做一些简单的探索性数据分析(EDA)来开始这个实验。我分析的第一步是检查数据样本并检查是否有空值。不存在空值,数据如下所示:

Figure 10: A sample from the original music data set

这个数据集看起来像一个普通的推荐系统数据集,它(显然)非常适合我们的模型。每个数据包含一个 userID、一个 artistID 和一个表示用户对该艺术家的评价的值。然而,这个数据集中代表用户-艺术家评级的权重值相当大,因为它们是由用户听一个艺术家所花的分钟数决定的。典型的推荐系统数据集包含 1-5 之间的评级、二进制评级(即 0 或 1)或类似的评级。因此,应该对该数据集中的评级值进行标准化,使其处于较小的范围内,具体做法如下:

Figure 11: Normalizing weight values within the data set

在权重值被标准化之后,我开始检查数据集的稀疏性,或者与可能的用户-项目组合的数量相比较的已知评级的数量。最初,数据集中只有 0.2%的可能评级是已知的,这是非常稀疏的,可能会对模型的准确性产生负面影响。因此,我选择过滤数据集,只包括至少评价了五位艺术家的用户。

Figure 12: Eliminating users with fewer than five ratings from data set

在数据集上执行上述操作后,它将数据集的密度增加到 1.3%,尽管仍然相对稀疏,但将允许矩阵分解模型做出更准确的预测。

完成这些更改后,在拟合模型之前,我还想做最后一项更改。在这个数据集中,每个用户和艺术家被分配一个唯一的 ID。然而,为了使推荐模型的使用更容易,我想让所有这些 id 都是连续的,这样它们就可以用于嵌入矩阵的索引。这可以通过下面的代码来实现,它确保所有用户和艺术家都有一个连续的、唯一的 ID:

Figure 13: Ensuring that user and item IDs are contiguous

该代码为用户和项目创建了从原始 ID 到新的连续 ID 的映射,使得所有 ID 都落在范围[0,用户/艺术家总数]内。通过执行这种转换,用户和艺术家的 id 可以用作嵌入矩阵的索引,以便于查找和快速预测。

超参数选择/调整

既然已经对数据进行了筛选和预处理,那么就可以实际训练推荐模型了。然而,训练模型有两个必须正确设置的超参数:学习速率和潜在维度。

为了确定最佳的学习速率,我利用了在 fast.ai 深度学习课程中描述的自适应学习速率选择技术。这种技术通过多次迭代训练模型,提高了每次迭代更新模型参数的学习率。然后记录每次迭代的损失,并显示在图表上,如下图所示:

Figure 14: Graph of the Loss (y-axis) vs. Learning Rate (x-axis)

在上图中,最佳初始学习率由损失开始增加之前的学习率的最大值表示,在本例中,该值约为 0.1。因此,在训练模型时,学习率最初被设置为. 1。

使用 20、30、40、50 和 60 的值通过网格搜索来确定最佳潜在维度。在使用这些嵌入大小中的每一个运行三个时期的训练之后,结果如下:

Figure 15: Grid Search for the Optimal Embedding Size

在执行网格搜索之后,为音乐推荐系统选择 40 的嵌入大小,因为它在三个时期的训练之后具有最小的验证损失。

拟合模型

现在已经选择了超参数,可以训练模型了。一次进行三个时期的训练,并且每三个时期学习率降低约 2 倍,直到模型收敛。通过逐渐降低学习率,创建了一个简单的学习率调度程序,允许模型微调其参数,并尽可能减少损失。模型在整个训练过程中的损失可以在下图中看到:

Figure 16: Training the Recommender Model with Multiple Learning Rates

该模型以 0.1、. 05、. 01、. 005 和 0.001 的学习率训练了 3 个时期,最终 MSE 损失为 0.75。换句话说,所有对用户-艺术家配对的预测平均误差约为 0.86。假设训练和测试数据集中的所有评级都在范围[0,~60]内,平均误差. 86 相对较低,这表明该模型与数据拟合得相对较好!

额外分析

虽然概率矩阵分解在预测用户项目评分方面效果很好,但该模型最有趣的方面之一是它创建的用于定量描述每个用户和项目的嵌入。这些嵌入可以用于获得关于用户或产品的见解,作为其他机器学习模型(如深度神经网络)的输入,甚至可以确定用户最喜欢哪些项目!只是为了好玩,我对通过训练这个模型创建的嵌入进行了一些额外的分析,以查看它们是否携带任何有趣的信息。更具体地说,我检查了为数据集中的每个艺术家产生的偏差值,这些偏差值概括了所有用户对每个艺术家的偏好。在对偏差值及其相关艺术家进行排序后,产生了以下结果:

Figure 17: Final Bias Values for Artists

可以看出,具有最高偏置值的艺术家是知名和成功的音乐家,如 Britney Spears 和 U2,从而证明了模型嵌入中包含的信息的有用性!

结论

非常感谢您的阅读,我希望您现在对推荐系统有了更好的理解,它们是如何工作的,以及您如何自己实现概率矩阵分解!如果你有兴趣探索这个项目的更多细节,我鼓励你去看看我创建的 GitHub 库,它包含了我在实现推荐系统中使用的所有代码的完整笔记本。此外,欢迎在 LinkedIn 或 Medium 上关注我,了解我未来的文章和工作。

来源/引用

[1]https://johnolamendy . WordPress . com/2015/10/14/collaborative-filtering-in-Apache-spark/

用 C#构建神经网络

原文:https://towardsdatascience.com/building-a-neural-network-framework-in-c-16ef56ce1fef?source=collection_archive---------1-----------------------

创建具有反向传播能力和基于进化的训练的神经网络。

介绍

我们将建立一个能够通过反向传播和进化进行学习的深度神经网络。代码将是可扩展的,以允许网络架构的改变,允许通过代码容易地修改网络执行的方式。

该网络是一个最低限度的可行产品,但可以很容易地扩大。你可以在 GitHub 上找到所有可用的代码,这包括突变和反向传播变体。

我将解释我们将如何设置前馈功能,设置所有需要的数组,并允许突变驱动的学习。

如果你想了解神经网络的工作原理,你需要熟悉一些基本的编码。对于反向传播,你需要熟悉梯度下降和微积分。除非您只想将代码用于您的项目。

概念时间!

我们的深度神经网络由一个输入层、任意数量的隐藏层和一个输出层组成,为了简单起见,我将只使用完全连接的层,但这些可以有许多不同的风格。

A simple neural network model

神经网络体系结构

上面的模型在输入层上有 5 个神经元,如由 5 个实心圆组成的第一列所示。第二层有 4 个隐藏的神经元,输出层有 3 个输出神经元。这些层的大小和隐藏神经元的数量是任意的。但对于这种视觉模型,我选择了较小的尺寸。

其思想是,来自每个输入节点的值通过每个树突(模型上节点之间的连接线)并乘以相应树突持有的权重,然后传递并保存在下一层的神经元中,然后对每一层继续这一循环,直到获得输出;

每个节点还有一个bias(用 b 表示),这有助于网络更好地运行。σ符号是这些乘积之和通过的激活函数。其中 w =来自树突的权重,a =前一层中每个神经元的激活。这个过程在每个神经元上进行,直到到达输出层。

当我们到达编码部分时,我们将更详细地讨论每一步,但是如果你对它没有一个基本的了解,我建议你去 youtube 或 Wikipedia 尝试并找到更多。 这个视频可能会有帮助

编码时间!

实现这种前馈功能的先决条件是一种存储所有数据的方法。我们将使用一系列阵列来存储所有数据,并确保网络性能始终处于最佳状态。

我们需要什么样的数据类型

一个输入数组来声明网络的大小,这将被标记为层。例如{5,4,3},这将创建一个类似于上图的网络模型。

我们还需要一个神经元阵列,用于保存前馈算法产生的值。这将是一个二维交错数组的形式

我们还需要另一个 2d 交错数组来存储每一层的偏差。

最后,我们需要一个 3d 交错数组来存储与每个树突相关的权重。偏差和权重是可以学习的,所有这些数组都是浮点数。

初始化功能

每当我们创建这个类时,我们需要确保网络的维度已经定义,所有适当的数组都已经初始化。

该函数采用网络维度的输入数组,并填充所有数组

private int[] layers;//layers    
private float[][] neurons;//neurons    
private float[][] biases;//biasses    
private float[][][] weights;//weights    
private int[] activations;//layerspublic float fitness = 0;//fitnesspublic NeuralNetwork(int[] layers)
{        
  this.layers = new int[layers.Length];        
  for (int i = 0; i < layers.Length; i++)        
  {            
    this.layers[i] = layers[i];        
  }        
  InitNeurons();        
  InitBiases();        
  InitWeights();    
}

首先,让我们来处理神经元,它们不需要分配任何值,我们只需要分配存储空间,因为数组的长度是静态的。我们使用列表作为创建数组的临时媒介。

//create empty storage array for the neurons in the network.
private void InitNeurons()
{        
  List<float[]> neuronsList = new List<float[]>();        
  for (int i = 0; i < layers.Length; i++)        
  {            
    neuronsList.Add(new float[layers[i]]);        
  }        
  neurons = neuronsList.ToArray();    
}

然后,我们可以在那里初始化偏差,偏差的大小与神经元的大小相同,只是我们需要填充每个槽,我将生成平均值为 0、标准偏差为 0.5 的每个偏差。当进行反向传播(消失和爆炸)时,有更好的初始化权重和偏差的方法来避免梯度问题,但是这对于遗传实现是不需要的。

//initializes and populates array for the biases being held within the network.
private void InitBiases()    
{        
  List<float[]> biasList = new List<float[]>();        
  for (int i = 0; i < layers.Length; i++)        
  {            
    float[] bias = new float[layers[i]];            
    for (int j = 0; j < layers[i]; j++)            
    {                
      bias[j] = UnityEngine.Random.Range(-0.5f, 0.5f);            
    }            
    biasList.Add(bias);        
  }        
  biases = biasList.ToArray();    
}

权重的生成类似于偏差,只是我们为前一层中的每个神经元的数组添加了另一个维度,如下所示:

the hierarchy for weight storage

//initializes random array for the weights being held in the network.
private void InitWeights()   
{        
  List<float[][]> weightsList = new List<float[][]>();        
  for (int i = 1; i < layers.Length; i++)        
  {            
    List<float[]> layerWeightsList = new List<float[]>();   
    int neuronsInPreviousLayer = layers[i - 1];            
    for (int j = 0; j < neurons[i].Length; j++)            
    {                 
      float[] neuronWeights = new float[neuronsInPreviousLayer];
      for (int k = 0; k < neuronsInPreviousLayer; k++)  
      {                                      
        neuronWeights[k] = UnityEngine.Random.Range(-0.5f, 0.5f); 
      }               
      layerWeightsList.Add(neuronWeights);            
    }            
    weightsList.Add(layerWeightsList.ToArray());        
  }        
  weights = weightsList.ToArray();    
}

前馈算法

前面所有的初始化函数都准备好了,是时候讨论实际的前馈算法和相关概念了。

如前所述,这是在网络的隐层和输出层中为每个神经元计算的内容。让我们解释一下每个术语。从激活函数** σ开始,这背后的想法是,你传入加权和并返回非线性结果,这提高了性能并保持网络神经元在控制之下,在现实世界中,没有多少事情遵循线性,因此非线性可以帮助近似非线性现象,并允许反向传播。**

****加权和,这是所有输入激活函数的数据。这是前一层中的每个节点乘以树突中的权重,数据通过该权重被传送到当前神经元。

****激活功能可由您选择,根据应用,不同的激活功能选择可能更有益。

该函数的常见变体包括:

  1. 身份
  2. 二进制步骤
  3. 乙状结肠的
  4. 双曲正切
  5. 热卢
  6. 泄漏 ReLU
  7. Softmax

现在,我将使用 Tanh 作为我选择的激活函数,因为它允许正值和负值。尽管其他的将适用于不同的应用。

Tanh 可以用以下格式表示

Tanh function

这个非线性函数返回以下结果

Tanh Graph

public float activate(float value)    
{        
  return (float)Math.Tanh(value);    
}
//Luckily Unity provides a built in function for Tanh

现在我们来看看前馈函数如何迭代神经元以产生输出。

该程序遍历每个神经元,获得前一层中每个神经元的值,并将该值乘以连接两者的权重,通过激活函数运行该值,然后将其值设置为激活值。这是节点 1 的一个示例

Example neuron calculation

//feed forward, inputs >==> outputs.
public float[] FeedForward(float[] inputs)    
{        
  for (int i = 0; i < inputs.Length; i++)        
  {            
    neurons[0][i] = inputs[i];        
  }        
  for (int i = 1; i < layers.Length; i++)        
  {            
    int layer = i - 1;            
    for (int j = 0; j < neurons[i].Length; j++)            
    {                
      float value = 0f;               
      for (int k = 0; k < neurons[i - 1].Length; k++)  
      {                    
        value += weights[i - 1][j][k] * neurons[i - 1][k];      
      }                
    neurons[i][j] = activate(value + biases[i][j]);            
    }        
  }        
  return neurons[neurons.Length - 1];    
}

有了它,我们就有了一个工作的神经网络,但目前它还没什么用,除非我们有一种方法来训练它。接下来让我们开始工作吧。

培养

对于这个网络的实现,我们将使用遗传算法。它们非常容易编码,并且很少涉及数学方面,但是,如果您对这个实现不感兴趣,我已经包含了一个反向传播代码示例,并且可能会使用强化学习方法。

遗传算法是训练神经网络很好地执行给定任务的一种方式。它工作得很好,因为你可以给它一个非常简单的适应度函数,来决定网络运行得有多好。它的缺点是在处理大型网络时需要相对较长的训练时间,与反向传播相比可能相当慢,但如果您有大量的计算能力,它可以返回比反向传播更好的结果,因为它们几乎总是能够达到全局最小值。

遗传算法背后的想法是基于达尔文主义的理论和生物进化是如何发生的,尽管我们将实现一个稍微简化的模型,但同样的总体概念也适用。一个物种的生物,他们有一个衡量他们“适合”的标准,从历史上看,这可能是他们的狩猎能力和繁殖能力,但有了我们的网络,这将是它能走多远,例如。然后我们整理种群,将它们分成两半,将上半部分克隆到下半部分,并对它们进行变异,这样网络的性能就趋向于全局最大值。

Evolution algorithm

进化驱动的学习实现

网络的大部分管理将通过另一个脚本来完成。因此,目前我们需要添加到网络中的只是一种对网络进行分类的方法,一种将一个网络克隆到另一个网络上的方法,以及一种改变网络的方法。

对于排序,我们让类型 IComparable 的代码,这将允许我们在引用它时直接对它进行排序。它使用网络适应度作为网络排序的值

//Comparing For NeuralNetworks performance.
public int CompareTo(NeuralNetwork other)    
{        
  if (other == null) 
    return 1;    
  if (fitness > other.fitness)            
    return 1;        
  else if (fitness < other.fitness)            
    return -1;        
  else            
    return 0;    
}

我们还需要能够将可学习的值(权重和偏差)克隆到另一个神经网络上。

//this loads the biases and weights from within a file into the neural network.
public void Load(string path)
{        
  TextReader tr = new StreamReader(path);        
  int NumberOfLines = (int)new FileInfo(path).Length;        
  string[] ListLines = new string[NumberOfLines];        
  int index = 1;        
  for (int i = 1; i < NumberOfLines; i++)        
  {            
    ListLines[i] = tr.ReadLine();        
  }        
  tr.Close();        
  if (new FileInfo(path).Length > 0)        
  {            
    for (int i = 0; i < biases.Length; i++)            
    {               
      for (int j = 0; j < biases[i].Length; j++)                
      {                    
        biases[i][j] = float.Parse(ListLines[index]); 
        index++;                                   
      }            
    }             
    for (int i = 0; i < weights.Length; i++)            
    {                
      for (int j = 0; j < weights[i].Length; j++)                
      {                    
        for (int k = 0; k < weights[i][j].Length; k++)
        {                        
          weights[i][j][k] = float.Parse(ListLines[index]);    
          index++;                                        
        }                
      }            
    }        
  }    
}

最后,我们将需要在网络中轻微推动每个可学习的值的能力,这是通过变异来完成的。

//used as a simple mutation function for any genetic implementations.
public void Mutate(int chance, float val)    
{        
  for (int i = 0; i < biases.Length; i++)        
  {            
    for (int j = 0; j < biases[i].Length; j++)            
    {                
      biases[i][j] = (UnityEngine.Random.Range(0f, chance) <= 5) ? biases[i][j] += UnityEngine.Random.Range(-val, val) : biases[i][j]; 
    }        
  }  

  for (int i = 0; i < weights.Length; i++)        
  {            
    for (int j = 0; j < weights[i].Length; j++)            
    {                
      for (int k = 0; k < weights[i][j].Length; k++)                
      {                    
        weights[i][j][k] = (UnityEngine.Random.Range(0f, chance) <= 5) ?  weights[i][j][k] += UnityEngine.Random.Range(-val, val) : weights[i]  [j][k];

      }            
    }        
  }    
}

所有这些代码实现后,我们应该有一个能够学习的工作网络。

在 Unity 中实现网络

设置场景,这是我创建的一条赛道,带有检查点,我们的神经网络车必须通过这些检查点才能提高体能。在这种情况下,网络的目标是在轨道上导航,并在不与墙壁碰撞的情况下尽可能走远。

在场景中,我们有一个管理器,它产生、变异克隆体并破坏网络。网络的意义在于提高网络的性能。

时间框架是每一代机器人训练的时间,群体大小是一次训练多少个机器人,突变机会是每个权重或偏差发生突变的变化,突变强度是微移的标准偏差,游戏速度是游戏内的滴答率,如果你有适当的硬件,增加网络的训练时间。

管理者创建一群使用神经网络的预设,然后在每个预设中部署一个神经网络。一段时间后,测试将完成,网络将被分类,以便只有表现最好的被保留,没有表现最好的将被最好的网络复制并变异。然后再次部署,训练循环继续进行。

最后,我们需要制作学习者预置,它的功能有点类似于汽车,可以前后移动,也可以转弯;汽车的这两个输入将是网络的输出。网络的输入将是 5 个距离传感器。

经过几分钟的训练,我们得到了这个结果,这意味着所有的代码都工作了!

所有代码都可以在 GitHub 上找到,我应该注意到 GitHub 上的代码也有保存和加载功能,这些功能保存和加载网络的权重,以允许网络从它停止的地方恢复。

****

Google Drive 上的 项目

Github 上的突变项目。

Github 上的反向传播项目。

非常感谢您的阅读,我希望它能有所帮助。我欢迎任何问题或反馈。敬请期待!

通过基于本体的知识搜索构建类似 Alexa 的个人助理

原文:https://towardsdatascience.com/building-a-personal-assistant-like-alexa-open-domain-question-answering-7e9aa1e8ed90?source=collection_archive---------24-----------------------

personal assistant battle

你有没有想过“如何打造一个私人助理?”像 Alexa 、谷歌个人助理或谷歌主页覆盖多个意图以及开放领域问答视角。

在这篇文章中,我将尝试在更高的层次上介绍基于本体的问答知识搜索。

有两种广泛知识搜索方法

  1. 基于本体的知识搜索(主要通过知识图)
  2. 基于开放领域问答的知识搜索

在这篇文章中,我们将在更广的层面上讨论基于本体的知识搜索。

构建像 alexa 这样的个人助理需要各种构建模块,我们将在本文中介绍核心组件的高级架构。并将在接下来的系列中深入探讨。

积木

  1. 可扩展的知识图
  2. 从结构化和非结构化来源到知识图的知识消耗
  3. 结构化查询的自然语言理解
  4. 自然语言生成来回答问题
  5. 语音转文本
  6. 文本到语音
  7. 文本 2 内容模块

问答模块的高级架构(还不包括家庭自动化等)。

Question Answering Architecture

该架构非常简单明了,目前还不包括可扩展性,但包含了问题回答模块的核心组件,如“谷歌个人助理”或“Alexa”。

NLU 层

NLU 层的主要目的是从文本中构建图形查询。这是基于本体的知识搜索的瓶颈之一,即从自由文本流中进行结构化查询(在 GQL 中)(开放领域问答试图以神经方式解决这个问题)

机器翻译块

在语言不可知知识图的情况下,这也不是强制性的。有两种方法可以建立语言不可知和语言感知的知识图。大多数公共知识图是语言不可知的(超出了本文的范围。

步伐

  1. 为支持的每种语言训练语音到文本模型
  2. 将结构化知识(主语、谓语、宾语)如维基数据或 dbpedia(抓取的维基百科)存储到知识图中
  3. 将来自非结构化新闻文章和其他来源的知识丰富并存储到知识图中。
  4. Text2Intent 层理解(分类)用户意图
  5. 构建 NLU (自然语言理解)层,以理解用户查询或命令或意图(查看 rasa )
  6. 面向意向图查询语言(GQL)的知识图查询适配器层
  7. 与家庭自动化或其他第三方应用程序的集成挂钩
  8. NLG(自然语言生成)层
  9. 对话聊天机器人设计
  10. 机器翻译层将英语翻译成支持的语言(可选)。
  11. 支持的每种语言的 Text2Speech 型号

参考故事“揭秘 wiki 本体 -part1”了解 wiki 本体,这有助于将 wiki 数据摄取到我们选择的知识图中。

个人助理的高级架构(基于本体的知识搜索)

Personal assistant architecture

上面是一个个人助理的高级架构,它不同于一个纯粹的开放领域的问题回答助理,因为它并不总是我们想问的问题。

我们还希望我们的助手“预订出租车”、“预订餐馆”、“告诉我我的约会”等。所以我们需要额外的一层意图分类。

让我们逐一探索每个模块,并深入了解可用的选项。前往揭开维基本体和知识图的神秘面纱——第一部分,这将有助于从维基数据中填充知识图。

NLU 层

从上面的架构可以看出,NLU 层仅仅是为了理解开放领域的问题而构建的。文本 2 内容层过滤并仅将开放域问题传递给 NLU 层。

让我们使用 Stanford CoreNLP 来探索一个基于模式的简单 NLU 层,这将是一个 grpc 微服务,并使用 google 协议缓冲区 来交换消息。

斯坦福大学 NLU 分校

让我们看看几个与电影相关的问题和相应的集集。

电影《阿拉达纳》的导演是谁?

电影《肖莱》的导演是谁?

电影《肖莱》的制片人是谁?

这类问题可以归为一组,我们问的是在电影中扮演特定角色的人。

对应 conll 文集

conll corpus (9 columns for each sentence)

正如我们在上面看到的,在 conll 语料库中的第二列是 NER 标签和使用 regexner 分类为标题(自定义 NER)的导演、制片人等。

Modified architecture add a layer before NLU

如果你把领域压缩得足够小,以理解每个类别中的问题,比如电影问题,regexner 就很棒,如图所示。

人们可以把 regexner 放到斯坦福 corenlp 管道中,如下所示。

Properties props = new Properties();
props.put("annotators", "tokenize, ssplit, pos, lemma, ner, regexner");
props.put("regexner.mapping", "org/foo/resources/jg-regexner.txt");
StanfordCoreNLP pipeline = new StanfordCoreNLP(props);

TokenRegex 文件

TokensRegex 是一个通用框架,包含在 Stanford CoreNLP 中,用于定义文本(标记序列)的模式,并将其映射到表示为 Java 对象的语义对象。(摘自 Stanford Corenlp token regex)

//author samrat.saha
//tokensregex.extractor.rulesner = { type: "CLASS", value: "edu.stanford.nlp.ling.CoreAnnotations$NamedEntityTagAnnotation" }tag = { type: "CLASS", value: "edu.stanford.nlp.ling.CoreAnnotations$PartOfSpeechAnnotation" }normalized = { type: "CLASS", value: "edu.stanford.nlp.ling.CoreAnnotations$NormalizedNamedEntityTagAnnotation" }ENV.defaultStringPatternFlags = 2{ruleType: "tokens",pattern: ( ( [ { tag:WP } ] ) ( [ { ner:O} ]* ) ([ { ner:TITLE} ]+ ) ( [ { ner:O} ]* ) ( [ { ner:MOVIE} ]+  ) ( [ { tag:NNP } ]+ )   ),result: Format( "MOVIE_ROLE %s %s %s %s", "##", $$6.text, "##", $$3.text),stage: 1}

Java 代码

**public** **static** **void** main(String[] args) **throws** Exception {String rules = "..../src/main/resources/tokenregexrule.txt";String exampleQuestions = IOUtils.*stringFromFile*("src/main/resources/question.txt");//pipeline have regexner as well as statistical nerProperties properties = PropertiesUtils.*asProperties*("annotators", "tokenize,ssplit,pos,lemma,ner,regexner");properties.setProperty("ssplit.eolonly", "true");properties.setProperty("regexner.mapping", "src/main/resources/movieregex.txt");//tokenregex rule file from above
properties.setProperty("tokensregex.extractor.rules", rules);StanfordCoreNLP pipeline = **new** StanfordCoreNLP(properties);Annotation annotation = **new** Annotation(exampleQuestions);*env* = TokenSequencePattern.*getNewEnv*();*extractor* = CoreMapExpressionExtractor.*createExtractorFromFiles*(*env*, properties.getProperty("tokensregex.extractor.rules"));pipeline.annotate(annotation);*extractConcepts*(annotation);}

使用 tokenregex 提取概念代码

代码会提取主语宾语和我们说的概念,这个叫做关系提取(主语、谓语、宾语)。

在知识图中,主体和客体将是实体,谓词将是关系。

**public** **static** **void** extractConcepts(Annotation annotation) {**boolean** flag = Boolean.***FALSE***;List<CoreMap> sentences = annotation.get(CoreAnnotations.SentencesAnnotation.**class**);**int** id = 0;**for** (CoreMap sentence : sentences) {System.***out***.println(sentence.toString());List<CoreLabel> tokens = sentence.get(CoreAnnotations.TokensAnnotation.**class**);id++;List<MatchedExpression> matchedExpressions = *extractor*.extractExpressions(sentence);**for** (MatchedExpression matched:matchedExpressions) {// Print out matched text and valueSystem.***out***.println("--------------------");//System.out.println(matched.getText());//System.out.println(matched.getValue().toString());String subj = "";String obj = "";CoreMap cm = matched.getAnnotation();String matchedText = matched.getValue().toString();String matchedTextMod = matchedText.replace("(", " ").replace(")", "").replace("STRING", "");StringTokenizer st = **new** StringTokenizer(matchedTextMod);String predicate = st.nextToken("##").trim();subj = st.nextToken("##").trim();obj = st.nextToken("##").trim().replace("-LRB-", "(").replace("-RRB-", ")");**if**(obj.substring(0, obj.length()/2).replaceAll("\\s|\\W", "").equalsIgnoreCase(obj.substring(obj.length()/2, obj.length()).replaceAll("\\s|\\W", ""))){obj = obj.substring(0, obj.length()/2);}System.***out***.println(subj + "->" + predicate + "->" + obj);System.***out***.println("--------------------\n");}}annotation = **null**;**return**;}

输出:

Who is the director of the film Aradhana ?--------------------Aradhana->MOVIE_ROLE->director--------------------Who is the director of the film Sholay ?--------------------Sholay->MOVIE_ROLE->director--------------------Who is the producer of the film Sholay?--------------------Sholay->MOVIE_ROLE->producer--------------------

可以注意到,这对于下面这样的复杂问题是绝对不够的。

多主题问题

汤姆·克鲁斯和布拉德·皮特主演的电影有哪些?

统计 CRF 关系提取器

Stanford coreNLP 还支持关系提取,可用于从简单问题中提取主语、宾语和谓语。

人们需要为他们感兴趣的关系注释 conll 语料库文件,例如参见下面的 conll 语料库。

movie role relation annotation

上面的注释说,在令牌 3 和 7 之间存在一个我们正在询问的 MOVIE_ROLE 关系。

也看到如何分类阿拉达纳被归类为我们需要为电影数据集定制 NER 模型的人。训练一个 NER 模型超出了这些博客的范围。

使用带注释的数据集训练一个定制模型,如这里提到的。

一旦模型训练完毕,加载模型并使用下面的代码从简单的问题中提取主客体谓词。

**public** CustomRelationExtractAnnotator(Properties props){ *verbose* = Boolean.*parseBoolean*(props.getProperty("sup.relation.verbose", "false"));//Load the model
String relationModel = props.getProperty("sup.relation.model", "model/simple-question-relation-model.ser");**try** {Extractor entityExtractor = **new** SimpleQuestionEntityExtractor();BasicRelationExtractor relationExtractor = BasicRelationExtractor.*load*(relationModel);System.***out***.println("Loading relation model " + relationModel + " and the features are " + relationExtractor.featureFactory);mr = MachineReading.*makeMachineReadingForAnnotation*(**new** SimpleQuestionConllReader(), entityExtractor, relationExtractor, **null**, **null**,**null**, **true**, *verbose*);} **catch**(Exception e){e.printStackTrace();**throw** **new** RuntimeException(e);}}@Override**public** **void** annotate(Annotation annotation) {// extract entities and relationsAnnotation output = mr.annotate(annotation);// transfer entities/relations back to the original annotationList<CoreMap> outputSentences = output.get(SentencesAnnotation.**class**);List<CoreMap> origSentences = annotation.get(SentencesAnnotation.**class**);**for**(**int** i = 0; i < outputSentences.size(); i ++){CoreMap outSent = outputSentences.get(i);CoreMap origSent = origSentences.get(i);// set entitiesList<EntityMention> entities = outSent.get(MachineReadingAnnotations.EntityMentionsAnnotation.**class**);origSent.set(MachineReadingAnnotations.EntityMentionsAnnotation.**class**, entities);**if**(*verbose* && entities != **null**){System.***err***.println("Extracted the following entities:");**for**(EntityMention e: entities){System.***err***.println("\t" + e);}}// set relationsList<RelationMention> relations = outSent.get(MachineReadingAnnotations.RelationMentionsAnnotation.**class**);origSent.set(MachineReadingAnnotations.RelationMentionsAnnotation.**class**, relations);**if**(*verbose* && relations != **null**){System.***err***.println("Extracted the following relations:");**for**(RelationMention r: relations){**if**(! r.getType().equals(RelationMention.***UNRELATED***)){System.***err***.println(r);}}}}}

以上是对 CoreNLP 如何用于从简单问题中提取关系的快速解释。

你会发现很多关于斯坦福 CoreNLP 的在线文章,如果有疑问,请在下面评论澄清。

在这篇文章中,我们使用 tokenregex 和Stanford relation extraction介绍了一些架构选项和基本 NLU 程序块,它们可以提取单个主语、单个谓语和单个宾语。

注意,单个问题可以用多个关系来注释,也可以从单个问题中提取多个关系,这可能导致精度问题。基于你的训练数据有多好,统计的 CRF 方法一般会有较高的召回较低的精度。

将在即将到来的博客中讨论高级 NLU 和问题理解。

感谢阅读..

Git Link :https://github.com/iitrsamrat/nlp-stanford

References:

[1] https://www.wikidata.org/wiki/Wikidata:Main_Page

[2] https://wiki.dbpedia.org/

[3] https://en.wikipedia.org/wiki/Knowledge_Graph

[4] https://en.wikipedia.org/wiki/Question_answering

[5] https://www.conll.org/2019

[6] https://stanfordnlp.github.io/CoreNLP/

构建基于照片的个性化推荐应用程序

原文:https://towardsdatascience.com/building-a-photo-based-personalized-recommendations-application-27029418d65e?source=collection_archive---------14-----------------------

使用 Neo4j、图形算法、Yelp 公共数据集和 React

The Applied Graph Algorithms online training course shows how to enhance the functionality of a web application using Neo4j Graph Algorithms.

在设计应用图算法在线培训课程时,我们认为展示如何构建利用图算法的应用程序以使用真实世界的数据集添加智能功能非常重要。这篇博文详细介绍了课程中一个练习背后的技术:构建一个基于照片的个性化推荐应用程序。

这只是练习之一,如果你对此感兴趣,请查看免费的应用图算法在线课程!你可以在这个概述视频中了解更多关于培训的信息。

图形算法

图形算法支持图形分析,分为 5 大类:寻路和搜索、中心性、社区检测、链接预测和相似性。你可以在这里了解更多 Neo4j 图算法。

The five basic categories of graph algorithms available in Neo4j.

Yelp 开放数据集

Yelp 开放数据集是 Yelp 公开提供的所有 Yelp 业务、评论和用户数据的子集,可以建模为图表并导入 Neo4j。对于熟悉构建应用程序和使用图表数据来说,这是一个很好的数据集。

The Yelp Open Dataset is a subset of Yelp data released publicly. This data can be modelled as a graph in Neo4j.

商业评论应用程序

我们从一个简单的 business reviews web 应用程序开始,目标是使用 Neo4j 图算法增强其功能。该应用程序的基本功能允许用户搜索企业和查看企业的评论。

A business reviews application using the Yelp Open Dataset. It allows the user to search for businesses and view reviews. We explore how to enhance the functionality of this application using Neo4j Graph Algorithms.

web 应用程序是一个 React 应用程序,它使用 Neo4j JavaScript 驱动程序对 Neo4j 数据库实例执行 Cypher 查询并处理结果。

Basic architecture of the business reviews application: a React frontend uses the Neo4j JavaScript driver to send Cypher queries to a Neo4j database instance.

个性化推荐

个性化推荐在许多行业和应用类型中都非常有用。通过推荐要购买的商品、要阅读的文章、要看的电影或要听的歌曲,用户很乐意拥有与他们的兴趣相关的内容,开发者也很乐意与他们的应用程序有更多的互动。

基于内容与协同过滤

基于内容的推荐和协同过滤是实现个性化推荐系统的两种基本方法。

基于内容的方法使用被推荐项目的属性(电影类型、菜肴类型等),并将这些属性与用户的偏好进行比较,从而做出推荐。计算项目之间相似性的相似性度量对于这种方法是有用的。

另一方面,协同过滤基于用户与项目的交互,例如购买或评级来产生推荐。

在本例中,我们使用基于内容的方法,使用照片标签和 Neo4j 图形算法进行个性化推荐。

基于照片的推荐

Yelp 开放数据集包括 20 万张用户上传照片的档案,与企业相关。这些照片将作为我们个性化推荐方法的基础。

我们的应用程序将向用户随机显示照片,允许用户选择他们喜欢的照片。然后,我们会找到与用户喜欢的照片相似的照片,并推荐与这些照片相关的商家。

具体来说,这些步骤是:

  1. 使用 Jaccard 相似度识别相似照片
  2. 使用标签传播聚类相似照片
  3. 通过遍历图表,推荐同一个社区中与照片相关的企业。

寻找相似的照片

我们将如何确定照片是否相似?Yelp 数据包括关于每张照片的最少元数据,但我们通过谷歌视觉 API 运行每张照片,该 API 使用机器学习来确定照片的标签。我们的脚本获取每张照片的标签,并在图中创建Label节点:

Adding photos labels to the graph.

Jaccard similarity is defined as the size of the intersection of two sets divided by the size of the union of two sets.

我们可以使用 Jaccard 相似度来计算给定的一对照片有多相似。通常用于查找相似项目的推荐以及链接预测的一部分 Jaccard 相似性度量集合之间的相似性(在我们的例子中是附加到照片的标签集合)。

Jaccard 相似性是一种集合比较算法,通过将两个集合的交集大小除以它们的并集大小来计算。Jaccard 相似性算法在 Neo4j 图形算法库中可用。下面是我们如何在一个 Cypher 查询中使用它,计算所有照片对的相似度。

MATCH (p:Photo)-[:HAS_LABEL]->(label)
WITH {item: id(p), categories: COLLECT(id(label))} AS userData
WITH COLLECT(userData) AS data
CALL algo.similarity.jaccard(data, 
  {topK: 3, similarityCutoff: 0.9, write: true})

这个查询将在图中创建SIMILAR_TO关系,将相似性值存储在一个score属性中:

让我们看两张标签重叠的照片,看看 Jaccard 相似度得分是如何计算的。在下图中,两张照片有 9 个重叠标签,其中一个标签仅与一张照片相关联:

因此,Jaccard 相似分数为 9/10 或 0.9

使用标签传播聚类相似照片

标签传播是一种社区检测算法,它通过为每个节点分配分区值来将图形分割成分区或社区。它的工作方式是用社区分配播种节点,然后迭代,在每次迭代中将社区分配给相邻节点,直到整个图被划分。

为了在 Neo4j 中运行标签传播,我们传递定义了要在其上运行算法的子图的节点标签和关系类型。在这种情况下,我们希望对由SIMILAR关系定义的子图使用 run 算法:

CALL algo.labelPropagation("Photo", "SIMILAR", "BOTH")

该查询将添加一个partition属性来按社区对照片进行分组。然后我们可以通过社区查询查看Photo节点的分布:

我们可以通过选择一个社区并查看分配给该社区的照片来验证照片的社区分配。这里我们随机选取一个社区,对比照片:

看这些照片,他们似乎都是比萨饼,所以这似乎是一个“比萨饼”集群。看起来我们的方法非常有效!

通过遍历图表,推荐同一社区中与照片相关的业务

现在,我们已经使用标签传播算法将照片分组到社区中,我们准备好生成我们的个性化商业建议。给定用户选择的一些照片,我们从这些照片遍历到他们指定的社区,然后到同一社区中的其他照片,然后遍历到与照片相关的企业,这成为我们的推荐。我们使用一个简单的 Cypher 查询来定义这种遍历:

请注意我们是如何在这个操作中结合全局和局部图操作的,利用了图算法以及图数据库的近实时性能,即无索引邻接。

最后的结果

这是我们的新功能:

  1. 向用户呈现随机照片的照片库
  2. 用户选择 5 张他们觉得有吸引力的照片
  3. 我们的应用程序遍历图表,找到所选照片的聚类,然后遍历相同聚类中的其他照片,最后遍历与这些照片相关的企业,这些照片成为推荐。

报名参加免费的应用图形算法课程

Neo4j 应用图形算法免费在线培训教授如何利用图形算法来增强 web 应用程序的功能,包括这个基于照片的推荐示例。立即注册了解更多有关 Neo4j 图算法的信息,如个性化页面排名、相似性度量、社区检测以及添加个性化、推荐和增强搜索结果等功能。

通过注册应用图算法课程,你可以找到这部分提到的所有数据和代码!

使用机器学习构建私人的本地照片搜索应用程序

原文:https://towardsdatascience.com/building-a-private-local-photo-search-app-using-machine-learning-8aeeef8d245c?source=collection_archive---------14-----------------------

这就是了。这是我做过的最棒的事。我通常不喜欢吹牛,但我为这一点感到非常自豪,我觉得我需要分享它。他们说这是不可能的(实际上没有人这么说),他们说这是不可能的(很多人说这是可能的),但我做到了,而且效果很好!

背景

My wife’s library has the other 40,000

自从有了孩子,我就有一个问题。嗯……很多问题,但我会把重点放在与技术相关的问题上。问题是我有 8 万张照片。这并不夸张。在我妻子和我之间,在我们的第一个人类出生后,我们在短短几年内从大约 3000 张照片增加到 80000 张。

很快,我意识到拍照几乎没有任何意义,因为以后再也不可能找到它们了。当时,我所有的照片都在 Dropbox 里。Dropbox 没有任何照片管理功能(在撰写本文时仍然没有),而这正是我迫切需要的。从我在机器学习领域的工作中,我知道你可以从一张照片中收集到很多信息,比如照片中的人是谁,照片是在哪里拍的,照片是什么样的场景;例如,未来的超级恶棍恐吓父母

所以我做了唯一符合逻辑的事。将所有 80,000 张照片移植到 Apple Photos,以便可以运行一些 ML,我可以再次找到我的照片。它非常成功——结束了。再见!

错误的

假的。这不是结局。可以说,现在我被困在苹果的围墙花园里。我真的很喜欢这个花园,那里有很多漂亮的花,比如的胭脂树的胭脂树。但并不是每个人都这样做,我的好朋友 Jaron Phillips 的一篇文章提醒我,总有另一种方法(除了谷歌有严重的隐私问题)。

在本地运行

让我们讨论一下为什么写这篇文章,以及我是如何以一种不可思议的方式解决了这个惊人的问题(以及你如何也能做到这一点,尽管是以一种远不那么有趣的方式)。

你的照片(可能)非常隐私,包含许多秘密,所以你可能想把它们存在你的本地计算机上。但是它们是不可搜索的!让我们改变这一切!为了使这个过程有效,它们需要在一个文件夹中,所以只需将它们全部导出到一个文件夹中(如果还没有的话)。如果你对电脑很在行,你可以修改我的脚本,让它也适用于许多子文件夹中的照片。但是最关键的部分是机器学习。

我们将使用 Tagbox ,它已经预先训练了成千上万的图像标签,例如,用照片中的内容来标记我们所有的照片;海洋、日落、沙滩、雾、狗、生日蛋糕、厄运、等。关于标签盒的伟大之处在于它在你的电脑上本地运行。没有云公司窃取你的秘密。

我们要把这些标签存储在该死的文件里。想象一下。!我们为什么要这么做?所以你可以搜索。嘭!冰!其他声音!是不是很神奇?

Where?

这些标签将进入 Spotlight 将索引的“评论”栏,以便您稍后可以搜索这些文件。

该过程

这是我如何在大约一个小时内让它工作起来的。我做的第一件事是把一堆照片移到我电脑上的一个文件夹里,来模拟我以前的照片目录。

然后,我下载、安装并运行了 Tagbox ,这花了几分钟,但那是因为它是我的公司的产品,我很了解它。如果你是一名开发人员或技术专家,这也不会花你太多时间。

接下来,我编写了一个 Go 脚本,该脚本遍历目录,将每个图像发送到标记框,取回标记,并将它们放入文件的注释字段。

然后我运行脚本,瞧——EXTEME EXCELLENCE。

现在,我可以通过 Mac 上的内容来搜索任何照片。没有必要把我的个人照片发到云上,或者发到谷歌上,或者发给……其他怪人。它在当地跑,跑得很快,跑得非常好。

这仅仅是一个开始——既然你是一个出色的开发人员,你可以把它带到下一个层次,构建一个具有其他特性的实际应用程序,或者探索在其他地方注入标签,比如 EXIF 数据或一个单独的 xml sidecar 文件。我在这里给你出主意,你在这里和他们一起做令人惊奇的事情,因为,让我们诚实地说,我是一个糟糕的开发者。

使用 Apache Airflow 构建生产级 ETL 管道平台

原文:https://towardsdatascience.com/building-a-production-level-etl-pipeline-platform-using-apache-airflow-a4cf34203fbd?source=collection_archive---------3-----------------------

使用 Apache Airflow 管理 CernerWorks 中的数据工作流

CernerWorks 企业系统管理团队负责从 Cerner 客户的系统中挖掘系统数据,为 Cerner 内部的各个团队提供所收集数据的可见性,并使用所收集的数据构建监控解决方案。我们的核心任务是帮助提高 Cerner 客户系统的可靠性和安全性。大约三年前,我们的团队开发了一个有效的系统数据收集遥测框架。与此同时,我们看到用例呈指数级增长,我们必须以各种方式转换收集的系统数据,以支持我们的可见性和监控工作。因此,我们迫切需要在我们的数据架构中引入一个专用的 ETL 管道平台。

经过一些研究,我们发现 Apache Airflow 开源框架非常适合我们的需求,因为它是为实现、调度和监控数据工作流而设计的。在 Airflow 上下文中,数据工作流由一个 DAG(有向无环图)表示,它是一组具有无环依赖关系的任务,如下所示。

DAG 中的每个任务都使用一个操作符来实现。Airflow 的开源代码库提供了一组通用的操作符,然而,该框架对我们的主要吸引力在于,我们可以实现专门适合 Cerner 数据工作流的自定义操作符。除了能够编写自定义操作符,Airflow 作为一个框架被设计成高度可定制的。

我们的团队在两个方面的需求驱动下进行定制工作:( I)最大限度地减少在我们的平台上安排 Cerner 数据工作流的开发开销,以及(ii)采用健壮可靠的生产架构。我们将我们定制的气流实现命名为 Jetstream。

Jetstream 高效支持 Cerner 数据工作流的设计模式。

在 Jetstream 中实现给定数据工作流的逻辑的核心是它的操作符和模块。这些模块的逻辑可能适用于各种数据工作流。例如,连接到各种数据库、连接到各种外部和内部 API、调度逻辑、批处理逻辑等等。操作符是一类任务的一般化表示。它们从任务 YAML 加载配置数据,并利用模块来实现给定的任务。例如,DatabaseTransferOperator 允许使用者将数据从一个数据库转换和移动到另一个数据库。任务 YAML 捕获与给定任务相关联的专门的和基本的业务逻辑。

我们团队的系统数据可见性和监控工作涉及到与 Cerner 各种团队的合作。我们转换系统数据的许多需求来自这些团队。通过抽象出所涉及的逻辑,我们能够让这些团队以最小的开发开销成为我们的 ETL 平台的消费者。这就是为什么该平台在过去三年中持续显著增长,目前有超过 3000 个日常任务。下面包括几个示例任务 YAMLs,

特别是因为我们在 Airflow 的开源之旅中很早就引入了 Jetstream,所以开发一个健壮可靠的产品架构一直是一个迭代的过程。让我们讨论一下 Jetstream 架构在过去三年中的一些重大变化的设计动机。

Jetstream 架构:提高调度元数据的可靠性并使用池

上图显示了我们对最初的概念验证设置所做的两个重要的架构更改。

(1)使用我们的数据仓库存储重要的任务调度元数据。

拥有可靠的定制调度元数据是我们团队的一个重要需求。我们特别希望消除对 Airflow 存储调度元数据的本地选项的依赖。作为气流的早期采用者,我们希望避免重大的气流设计变化,这些变化可能会影响我们利用的任何本机调度元数据逻辑。此外,由于 Cerner 的客户遍布世界各地,我们的许多任务严重依赖建立在我们的调度元数据之上的逻辑来正确处理。例如,Airflow 在其 1.9 版本中采用了 UTC 时间标准( AIRFLOW-289 ),如果我们非常依赖 AIRFLOW 的本地调度选项,这将打破我们的逻辑。

另一个重要的考虑是日程安排元数据的绝对价值。该元数据本质上捕获了与每个任务相关联的数据被处理的时间间隔。假设我们的调度元数据被破坏,这可能导致数据间隙(丢失时间间隔)或重复数据(重新处理的时间间隔)。在我们的 Jetstream 节点上存储如此重要的数据会导致高成本的单点故障。数据仓库是我们团队数据架构的核心组成部分(参见 Cerner 推进大数据分析能力。丹·沃克。企业系统管理总监。因此,我们的数据仓库集群受到严密监控,因此具有高可靠性。通过将这些元数据存储在我们的数据仓库中,即使我们的 Jetstream 节点出现故障,我们也可以重新启动它们并开始我们的工作,而不用担心损坏、不一致或缺少调度元数据。因此,添加这种依赖性,使我们能够显著提高平台的健壮性和可靠性。将这些数据存储在数据仓库中还可以让我们构建基于 SLA 的警报逻辑,并开发性能报告。

(2)利用气流池。

我们最初没有利用由 Airflow 框架提供的执行池支持。当 DAG(有向无环图)任务准备好被调度时,它将被添加到 Airflow 的默认执行池,由下一个可用的执行器执行。问题是,当我们添加一个有一千多个任务的 DAG 时,这个 DAG 的任务会挤掉其他 DAG 的任务。然后,我们开始使用命名执行池,这将限制每个 DAG 可用的工作插槽的数量。这让我们能够对执行并行性进行粒度控制,并防止 DAG 被无意中挤出。

Jetstream 架构:建立一个 Jetstream 集群并增加对近实时调度的支持

上图代表了 Jetstream 目前的制作架构。

(3)建立 Jetstream 集群。

我们使用 Airflow 的支持来利用芹菜来建立一个生产集群。这让我们能够利用并发处理的优势,从而提高平台的健壮性和效率。每个 DAG 都创建有一个关联的芹菜队列。每个工作节点都被分配了一组芹菜队列。这意味着来自每个 DAG 的任务可以在“监听”相应 DAG 队列的工作节点上执行。我们确保每个队列都有两个或更多分配的工作节点,以提高平台的可靠性,因为我们不想让工作节点成为潜在的单点故障。但是,请注意,主节点仍然是单点故障。请参见“使阿帕奇气流高度可用”以获得此问题的潜在解决方案。解决这个问题还不是设计的重点。

(4)支持接近实时的调度。

我们最近有一些重要的用例在我们的平台上进行接近实时的调度。我们意识到,为了支持近乎实时的数据工作流,我们需要将调度元数据存储在内存中。因此,对于我们的近实时 Dag,我们使用 Redis 而不是我们的数据仓库来存储调度元数据。

Jetstream 的主要开发者:

卡普勒,冈扬。 阿格纽,亚当。菲利普利,卡罗。 Pydi,Aakash 。

更多阅读请见阿帕奇气流资源权威汇编。

构建伪随机数发生器

原文:https://towardsdatascience.com/building-a-pseudorandom-number-generator-9bc37d3a87d5?source=collection_archive---------8-----------------------

在不到 50 行的 Python 代码中

在我的文章“ 如何从一个不平衡的 RNG 得到一个无偏的 T2中,我展示了如何从任何来源提取随机性。现在的目标是从头开始构建一个伪随机数生成器!

“为什么我需要一个随机数?”

随机数的重要性不在于数字本身(如果单独来看,它们是普通的数字),而在于它们产生的方式。

现代技术是基于这些数字:通信协议,加密,游戏大量使用它们,它们的整体安全性和不可预测性依赖于它们。

假设你想创建一个游戏,系统抛出一枚硬币,玩家在结果上下赌注(用真钱)。如果玩家是正确的,那么他有奖励(金钱),否则他失去了放置的金钱。如果你的硬币真的是公平的(或不可预测的),那么你就没什么好担心的。

但是,如果玩家能够以很高的准确率预测下一次投掷的结果,会发生什么呢?或者他们是否能根据最后三投预测下一个结果?答案显而易见。

自然发电机

大自然提供了一些随机性的来源,但是使用起来非常昂贵。

在宏观环境中,来源很少,如气候变化或宇宙微波背景。这种发电机非常昂贵:我想买一个系统,把你和测量 CMB 变化的卫星连接起来,并在几毫秒内发回结果。成本在 4 ~ 5 亿美元左右。

好吧,我举了一个极端的例子,但是总的来说,考虑到成本和性能,这一点都不方便。

在微观上,整个环境是由混沌驱动的:据我们所知,这是一个概率世界;但是,管理这种资源又是非常昂贵的。完美地平衡一个粒子的量子态这不是一个简单的任务。

伪随机发生器

由于这些原因,我们总是发现在我们的机器(电脑、智能手机、电视、等……)中内置发电机很方便。此外,有一个更简洁的方法来计算随机字符串总是好的:如果您的系统从以μK 为单位的本地温度中提取一个序列,任何人都可以通过在您的系统附近放置一个传感器来复制相同的序列;甚至任何人都可以操纵检测并控制你的序列。

计算机使用 CPU 来执行指令,而 CPU 是基于确定性机制的。我们如何从一个没有任何随机性来源的环境中创造随机性?

在我回答这个问题之前,让我们定义一个伪随机数发生器(PRNG)。从这里开始,我将处理使用位(0 和 1)的 PRNGs,但对于其他情况,验证其属性是非常容易的,因为可以用一个数来编码一个二进制序列。

背后的理论

给定一个初始种子,PRNG 产生一个比特序列,该序列与由真实随机源产生的序列无法区分。

不可区分是指在一台概率图灵机上没有可以在多项式时间内执行的算法可以决定给定的序列是随机的还是计算出来的。也就是说,没有随机算法能说 PRNG 产生的字符串是确定性计算的还是随机提取的。

因此,这是 PRNG 的一个定义:它是一个在多项式时间内在确定性图灵机上可执行的算法,计算一个函数 G 使得

l 作为单调递增函数。这意味着输出总是比输入(种子)长。

同样,对于属于 BPP 类的DD中的每个算法,对于每个多项式p和对于每个整数k足够大:****

这个庞大的公式可以这样理解:

概率多项式时间问题(BPP) 类中的算法能够区分真实随机源和 PRNG 之间的序列的概率随着种子长度的增加比任何多项式更快地趋向于零

因此,PRNG 是一种算法,它将种子作为输入,并返回一个更长的字符串,因此没有人能够轻易说出它是否被计算过。算法计算出的函数称为 G

G 的定义说,如果初始种子是一个 k 比特的序列,那么Gl(k)比特的更长序列。我们是否应该为每一个可能的函数 l 构建一个不同的函数 G ?**

从简单的 PRNG 号开始 H

有可能以 G 的形式构建任何 PRNG,如下所示:

其中,xᵢ λᵢ是位串,是位串 xᵢ和单个位λᵢ.串联的结果 H 函数从初始种子生成一个比原来长一位的序列。通过调用 H 函数 l(k) 次并从每次迭代中取出最后一位,我们已经生成了一系列的【l(k)位。显然这个功能就是 G

我们现在能够构建一个函数,该函数采用 k 位并返回 l(k) 位,使得没有算法能够决定 l(k) 位是否是从真实的随机源生成的。通过这个技巧,我们从构建输出【k】位的函数(使用 l( ) 通用多项式函数)的问题转移到只返回 k+1 位的函数。剩下的就是定义 H 函数。

单向置换的 h 函数

选择 H 作为单向置换是个好主意。

H 如果很难求逆就是单向排列:给定y,很难计算出x 使得 H(x) = y**

众所周知且广泛使用的单向置换是模幂运算。给定一个质数 p 和一个整数 x 使得
0<x<p-1

其中 g 是循环群的生成元

这个循环群的生成元的个数是

p 为奇素数时,其中 ϕ 为欧拉全等函数。现在求这样的 x 需要计算离散对数,这是一个著名的未解计算难题。也就是说,没有计算大整数的离散对数的有效方法是已知的。公钥密码学中的许多算法基于这样一种假设,即没有有效的方法来计算它的安全性。

最后一点

我们发现一个函数从 k 位开始,返回 k 位,并且很难反转。我们需要额外的一位,根据 H 的定义。该位是功能 f核心位。如果已知x,则 f 的核心位有点容易计算,但如果只给定 f(x) 则很难计算。

为了找到它,让我们定义两个 k 位串 xy 之间的新操作:

如果【x】是一个单向排列(就像上面定义的那样) g(x,y) = f(x) ‖ y ,那么 x,yg 的硬核位,其中这是由 Goldreich-Levin 定理陈述的。****

最后我们找到了 PRNG H 这是它的正式定义:

在哪里

****

PRNG

现在我们可以根据我们给出的 HG 的定义来构建一个好的 PRNG。对于那些错过的人, G 是一个函数(实际上是 PRNG 本身),给定输入中的一个 k 位串,输出一个 l(k) 位串,并且没有随机算法可以判断产生的字符串是否是由真正的随机源生成的; H 是一个帮助找到那些伪随机位的函数。

注意,我假设生成器 g = 2 。你应该找到一个依赖于 p 的生成器,因为这只是一个例子(即使统计上 2 是一个频繁的生成器)。如果你想为你的 p 找到一个生成器,你可以使用这个在线工具来计算给定素数的根基元模。

现在主要功能 G ,【PRNG:

其中 l( ) 可以是任意多项式函数。

由于 H 的输入是将被分成两半的位串,所以初始种子 x0 的长度必须是偶数。

请记住,根据上一节的定义,更长的种子会产生更难区分的序列。这意味着更长的种子将产生更不可预测的比特序列。

Python 实现

为了轻松管理位操作,算法的实现在字符串上工作,因此它可以更好地从上面显示的伪代码转换为 Python 代码。但是它可以通过直接处理位来增强,代价是可读性。

我用了模幂运算作为单向排列和
l(k)= k-2k+1

Link to the gist

用法:

唯一采用的参数是初始种子,它必须是一个二进制字符串,长度不能超过变量SEED_SIZE的值。

如果您想要生成一个数字序列,您可以使用输出的任何部分作为下一次迭代的种子。更大的种子会产生更好的输出,但在使用它之前,请记住重新定义一个合适的 l( ) ,如果您想保持模幂运算为单向变换,请选择一个新的GENERATOR,它是MODULUS的原始根。

构建用于比较数据的 Python UI

原文:https://towardsdatascience.com/building-a-python-ui-for-comparing-data-13c10693d9e4?source=collection_archive---------4-----------------------

如何快速让您的非技术团队能够比较数据

Photo by David Werbrouck on Unsplash

在分析或 IT 功能上花足够的时间,很快就会发现处理数据是必须的。收集数据,处理数据,当然还有比较数据。如今,所有这些数据的问题通常在于其数量之大。

除非人们的技术水平足够高,知道如何使用 Python、R 或类似的语言,否则当他们需要处理更大的数据集时,他们会很吃力。不幸(还是幸运?),Excel 就不剪了。这给人们制造了许多障碍。

当谈到数据操作,包括查看数据或比较数据时,我听到的最常见的抱怨之一是 Excel 根本不支持它。太大了,装不下。为此,我写了一些文章解释如何解决这个问题——但是我的大部分文章在本质上都是技术性的。它需要你写代码和使用命令行。

[## 用 Python 从 Excel 到数据库

了解如何使用 Python 进行快速数据分析

medium.com](https://medium.com/financeexplained/from-excel-to-databases-with-python-c6f70bdc509b)

除非你是一个技术人员,否则你不一定会觉得这很容易。同样,如果你想和一个非技术人员分享这个功能。

然而,在我上一篇文章中,我们介绍了如何快速地用 Python 开发 ui,然后与我们更广泛的团队或社区分享它们。反响非常好;你们中的许多人觉得它非常有趣。

[## 了解如何用 Python 快速创建 ui

最后,你可以在 10 分钟内找到一个图书馆

towardsdatascience.com](/learn-how-to-quickly-create-uis-in-python-a97ae1394d5)

在这篇博客中,我们将介绍 PySimpleGUI 的大量可用特性,同时我们还将构建一些东西,让我们的非技术朋友能够快速比较数据。

定义用户界面

我们需要做的第一件事是定义一个简单的 UI,允许用户选择两个文件。

Choosing the two files to display

一旦定义了这两个文件,我们应该执行一些基本的验证,以确保这两个文件是可比较的。寻找相同的列标题可能是一种方法。然后,我们可以提供这些标题作为潜在的关键字,用户可以选择用于数据比较。

使用此示例文件:

Screenshot of example file

将构建以下屏幕:

以下是包括输出在内的全部内容:

图书馆

构建用户界面

为了构建 UI,我们将使用基于 Tkinter、wxPython 和 PyQT 的 PySimpleGUI 库。这段代码再简单不过了。快速偷窥:

import PySimpleGUI as sgsupportedextensions = ['csv','xlsx', 'xlsm' ,'json']layoutprefile = [
    [sg.Text('Select two files to proceed')],
    [sg.Text('File 1'), sg.InputText(), sg.FileBrowse()],
    [sg.Text('File 2'), sg.InputText(), sg.FileBrowse()],
    # *list1,
    [sg.Output(size=(61, 5))],
    [sg.Submit('Proceed'), sg.Cancel('Exit')]
]window = sg.Window('File Compare', layoutprefile)while True:    # The Event Loop
    event, values = window.read()
    if event in (None, 'Exit', 'Cancel'):
        secondwindow = 0
        break
    elif event == 'Proceed':
  print('yay')

阅读文件

我们将使用著名的熊猫图书馆来阅读文件。这将允许我们快速支持 CSV、JSON 和 Excel 文件。一旦文件在数据框中,我们就可以进行必要的操作了。

import re
import pandas as pdfile1 = r"C:/temp/file.csv"
file2 = r"C:/temp/file1.csv" if re.findall('/.+?/.+\.(.+)',file1)[0] == 'csv':
 df1, df2 = pd.read_csv(file1), pd.read_csv(file2)
elif re.findall('/.+?/.+\.(.+)',file1)[0] == 'json':
 df1, df2 = pd.read_json(file1), pd.read_json(file2)
elif re.findall('/.+?/.+\.(.+)',file1)[0] in  ['xlsx', 'xlsm']:
 df1, df2 = pd.read_excel(file1), pd.read_excel(file2)

为了从文件路径中提取扩展名,我使用了 re 库,这是 Python 的正则表达式库。正则表达式给了我们一种模式匹配和提取信息的方法。

[## 正则表达式简介

使用 Python 逐步介绍正则表达式

medium.com](https://medium.com/better-programming/introduction-to-regex-8c18abdd4f70)

比较数据

为了进行比较,我们将使用 DataComPy 库,它为我们提供了一个很好的比较摘要。

[## 如何快速比较数据集

如何快速总结两个数据集之间的差异

towardsdatascience.com](/how-to-quickly-compare-data-sets-76a694f6868a)

代码还是非常简单:

compare = datacompy.Compare(
    df1,
    df2,
    join_columns=definedkey,
    abs_tol=0, #Optional, defaults to 0
    rel_tol=0, #Optional, defaults to 0
    df1_name='Original', #Optional, defaults to 'df1'
    df2_name='New' #Optional, defaults to 'df2'
)
print(compare.report())

共享用户界面

为了共享 UI,我们可以使用 PyInstaller。只需找到您的文件(本例中为 ComPyUI.py)并运行以下命令:

pyinstaller --onefile ComPyUI.py

代码

事不宜迟,只需在本地复制下面的代码,并运行它作为比较工具:

import PySimpleGUI as sg
import re, time
import datacompy
import pandas as pdsupportedextensions = ['csv','xlsx', 'xlsm' ,'json']layoutprefile = [
    [sg.Text('Select two files to proceed')],
    [sg.Text('File 1'), sg.InputText(), sg.FileBrowse()],
    [sg.Text('File 2'), sg.InputText(), sg.FileBrowse()],
    # *list1,
    [sg.Output(size=(61, 5))],
    [sg.Submit('Proceed'), sg.Cancel('Exit')]
]window = sg.Window('File Compare', layoutprefile)while True:    # The Event Loop
    event, values = window.read()
    # print(event, values)  # debug
    if event in (None, 'Exit', 'Cancel'):
        secondwindow = 0
        break
    elif event == 'Proceed':
        #do some checks if valid directories have been provided
        file1test = file2test = isitago = proceedwithfindcommonkeys = None
        file1, file2 = values[0], values[1]
        if file1 and file2:
            file1test = re.findall('.+:\/.+\.+.', file1)
            file2test = re.findall('.+:\/.+\.+.', file2)
            isitago = 1
            if not file1test and file1test is not None:
                print('Error: File 1 path not valid.')
                isitago = 0
            elif not file2test and file2test is not None:
                print('Error: File 2 path not valid.')
                isitago = 0
            #both files to have the same extension
            elif re.findall('/.+?/.+\.(.+)',file1) != re.findall('/.+?/.+\.(.+)',file2):
                print('Error: The two files have different file extensions. Please correct')
                isitago = 0
            #they need to be in a list of supported extensions
            elif re.findall('/.+?/.+\.(.+)',file1)[0] not in supportedextensions or re.findall('/.+?/.+\.(.+)',file2)[0] not in supportedextensions:
                print('Error: File format currently not supported. At the moment only csv, xlsx, xlsm and json files are supported.')
                isitago = 0
            elif file1 == file2:
                print('Error: The files need to be different')
                isitago = 0
            elif isitago == 1:
                print('Info: Filepaths correctly defined.')
                # check if files exist
                try:
                    print('Info: Attempting to access files.')
                    if re.findall('/.+?/.+\.(.+)',file1)[0] == 'csv':
                        df1, df2 = pd.read_csv(file1), pd.read_csv(file2)
                    elif re.findall('/.+?/.+\.(.+)',file1)[0] == 'json':
                        df1, df2 = pd.read_json(file1), pd.read_json(file2)
                    elif re.findall('/.+?/.+\.(.+)',file1)[0] in  ['xlsx', 'xlsm']:
                        df1, df2 = pd.read_excel(file1), pd.read_excel(file2)
                    else:
                        print('How did we get here?')
                    proceedwithfindcommonkeys = 1
                except IOError:
                    print("Error: File not accessible.")
                    proceedwithfindcommonkeys = 0
                except UnicodeDecodeError:
                    print("Error: File includes a unicode character that cannot be decoded with the default UTF decryption.")
                    proceedwithfindcommonkeys = 0
                except Exception as e:
                    print('Error: ', e)
                    proceedwithfindcommonkeys = 0
        else:
            print('Error: Please choose 2 files.')
        if proceedwithfindcommonkeys == 1:
            keyslist1 = [] #This will be the list of headers from first file
            keyslist2 = [] #This will be the list of headers from second file
            keyslist = [] #This will be the list of headers that are the intersection between the two files
            formlists = [] #This will be the list to be displayed on the UI
            for header in df1.columns:
                if header not in keyslist1:
                    keyslist1.append(header)
            for header in df2.columns:
                if header not in keyslist2:
                    keyslist2.append(header)
            for item in keyslist1:
                if item in keyslist2:
                    keyslist.append(item)
            if len(keyslist) == 0:
                print('Error: Files have no common headers.')
                secondwindow = 0
            else:
                window.close()
                secondwindow = 1
                break#################################################
# First screen completed, moving on to second one
if secondwindow != 1:
    exit()#To align the three columns on the UI, we need the max len
#Note: This could be made better by having the max len of each column
maxlen = 0
for header in keyslist:
    if len(str(header)) > maxlen:
        maxlen = len(str(header))if maxlen > 25:
    maxlen = 25
elif maxlen < 10:
    maxlen = 15#we need to split the keys to four columns
for index,item in enumerate(keyslist):
    if index == 0: i =0
    if len(keyslist) >= 4 and i == 0:
        formlists.append([sg.Checkbox(keyslist[i], size=(maxlen,None)),sg.Checkbox(keyslist[i+1], size=(maxlen,None)),sg.Checkbox(keyslist[i+2], size=(maxlen,None)),sg.Checkbox(keyslist[i+3], size=(maxlen,None))])
        i += 4
    elif len(keyslist) > i:
        if len(keyslist) - i - 4>= 0:
            formlists.append([sg.Checkbox(keyslist[i], size=(maxlen,None)),sg.Checkbox(keyslist[i+1], size=(maxlen,None)),sg.Checkbox(keyslist[i+2], size=(maxlen,None)),sg.Checkbox(keyslist[i+3], size=(maxlen,None))])
            i += 4
        elif len(keyslist) - i - 3>= 0:
            formlists.append([sg.Checkbox(keyslist[i], size=(maxlen,None)),sg.Checkbox(keyslist[i+1], size=(maxlen,None)),sg.Checkbox(keyslist[i+2], size=(maxlen,None))])
            i += 3
        elif len(keyslist)- i - 2>= 0:
            formlists.append([sg.Checkbox(keyslist[i], size=(maxlen,None)),sg.Checkbox(keyslist[i+1], size=(maxlen,None))])
            i += 2
        elif len(keyslist) - i - 1>= 0:
            formlists.append([sg.Checkbox(keyslist[i], size=(maxlen,None))])
            i += 1
        else:
            sg.Popup('Error: Uh-oh, something\'s gone wrong!')

#The second UI
layoutpostfile = [
    [sg.Text('File 1'), sg.InputText(file1,disabled = True, size = (75,2))],
    [sg.Text('File 2'), sg.InputText(file2,disabled = True, size = (75,2))],
    #[sg.Text('Select the data key for the comparison:')],
    [sg.Frame(layout=[
        *formlists],title = 'Select the Data Key for Comparison',relief=sg.RELIEF_RIDGE
    )],
    [sg.Output(size=(maxlen*6, 20))],
    [sg.Submit('Compare'), sg.Cancel('Exit')]
]window2 = sg.Window('File Compare', layoutpostfile)
datakeydefined = 0
definedkey = []while True:  # The Event Loop
    event, values = window2.read()
    # print(event, values)  # debug
    if event in (None, 'Exit', 'Cancel'):
        break
    elif event == 'Compare':
        definedkey.clear()
        file1test = file2test = isitago = None
        #print('Event', event, '\n', 'Values', values)
        for index, value in enumerate(values):
            if index not in [0,1]:
                if values[index] == True: 
                    datakeydefined = 1
                    definedkey.append(keyslist[index-2])
            #print(index, values[index], keyslist[index-2])
        if len(definedkey) > 0:
            compare = datacompy.Compare(
                    df1,
                    df2,
                    join_columns=definedkey,  #You can also specify a list of columns eg ['policyID','statecode']
                    abs_tol=0, #Optional, defaults to 0
                    rel_tol=0, #Optional, defaults to 0
                    df1_name='Original', #Optional, defaults to 'df1'
                    df2_name='New' #Optional, defaults to 'df2'
            )
            print('########################################################################################################')
            print(compare.report())
        else:
            print('Error: You need to select at least one attribute as a data key')

这种解决方案的局限性

这个解决方案有相当多的限制,但它是一个可以在未来非常容易和快速地增强的解决方案。最好拥有以下物品:

  1. 如果定义 CSV 定界符而不是现在假设一个','就好了
  2. 窗口的大小太不稳定,使用很长的标题会产生问题
  3. 使系统能够记住基于文件名的按键选择
  4. 完整的目录核对

我相信还有很多,但是请让我知道你的想法!

用机器学习构建哈利波特语录机器人

原文:https://towardsdatascience.com/building-a-quotebot-with-machine-learning-eca39272e3b7?source=collection_archive---------24-----------------------

使用 N-grams 和深度学习创建报价生成器应用程序的生成文本模型

语言模型如今被广泛使用。电子邮件、WhatsApp 文本、自动聊天机器人中的下一个单词预测都是基于语言模型的。

生成模型将一些文本作为输入,学习词汇和句子结构,并创建文本。《用 Python 进行深度学习》(Francois Chollet)这本书是理解 LSTMs 和神经网络如何学习文本的绝佳资源。这是理解 N 元语言模型的另一个很好的指南。

我使用生成模型创建了一个引用机器人,它从哈利波特宇宙中创建角色特定的引用!

数据

首先,我下载了《哈利·波特》系列的电影剧本。这给我带来了第一个挑战。在 python 中使用 pdf!幸运的是,我找到了一个包“pdf2txt ”,可以将这些 pdf 文件转换成我们以前的格式。txt 文件。

pdf2txt.py -o HP1_movie_script.txt HP1.pdf

这些更容易处理。
现在,继续前进。

备考

我的 QuoteBot 是特定于角色的,所以要学习一个特定角色的演讲风格,只需要相关的对话。我把每个角色的台词都分离出来,并把它们标记出来。

因为 QuoteBot 需要一个生成模型,所以创建一个语料库并对单词进行标记是我们唯一需要的数据准备。其他传统的 NLP 预处理步骤是不必要的,因为我们希望保持原始文本不变,以便模型学习序列。

模特们

N 元模型

ngrams 模型基于单词或字符出现的条件概率。对于我的 QuoteBot,我创建了一个单词级别的模型,在给定语料库中的一组单词的情况下,该模型查看下一个单词出现的概率。

单字和双字通常过于简单,无法创建连贯的文本。三元模型及以上可能非常有用。但是,我们应该谨慎使用更高阶的“克”。例如,10 克只会记住一个句子,而不是学习结构,导致模型“过度适应”。

三元模型比二元模型产生更一致的输出,例如

“不得不承认,我确实认为我可能看到了什么——当它被风吹走的时候?”—罗恩·韦斯莱

还有,4 克-

“真的吗?昨晚你不是不得不滚到床下以免被碎尸万段吗!做你的朋友会死人的,哈利!”—罗恩·韦斯莱

相当酷!有了这些 ngrams 模型,我在深度学习部分徘徊,看看递归神经网络是否会有帮助。特别是 LSTM 模型。

LSTM 模式

LSTM 模型也可以是字符级或单词级模型。对于我的 QuoteBot,我尝试了两种方法。LSTM 生成模型理解指南

人物级别

模型细节:1 层(有 Dropout),激活函数— softmax,优化器— RMSprop,损失函数—分类交叉熵。我让模型运行了 100 个时期,有了上面的这些细节,我的字符级模型给出了一个稍微不连贯的输出。

荣!你将带领进化者去赢得荣誉。那是什么意思,先生?我不知道发生了什么?他们可能会羡慕我。”—哈利

有些词不连贯,句子结构有点不对。

字级 LSTM

模型细节:3 层(有 Dropout),激活函数— ReLU & softmax,优化器— Adam,损失函数—分类交叉熵。
我让模型运行了 300 个时期(在 Google Colab 上),有了以上这些细节,我的单词级模型给出了比字符级更好的输出。

晚上哈利我有一个古老的迷宫继续说你也不会害怕哈利我想是伏地魔。 " —邓布利多

这样好多了!听起来这个角色和所有的单词都是连贯的。

创建 QuoteBot

基于每个生成模型的输出,我选择三元模型来创建 QuoteBot。我用 Flask 创建了一个应用程序,它将一个字符作为输入,并给出字符特定的引用!

学习

少即是多… 不是!深度学习模型需要一个大型数据集来从语料库中“学习”单词的顺序。给模型一个更大的语料库并调整更多的超参数肯定会提高模型的性能。

创建更大的语料库可以通过两种方式实现——
数据扩充——基于现有语料库为角色模拟更多文本
将范围从哈利波特电影系列扩展到我们希望模型学习的任何文本文档!

更多的训练对于优化模型输出总是更好的…斯内普同意!

LinkedIn , Github

参考资料:

N 克语言模型

使用 LSTMs 生成字符级文本

NLTK — ngram 建模

使用 Kafka 和 spaCy 构建实时 NLP 管道

原文:https://towardsdatascience.com/building-a-realtime-nlp-pipeline-using-kafka-and-spacy-d4ad636be702?source=collection_archive---------16-----------------------

在本教程中,我们将使用汇合 Kafka 、python 和一个名为 spaCy 的预训练 NLP 库来构建一个实时管道。

作为先决条件,我们应该在本地安装 docker,因为我们将在我们的机器上运行 kafka 集群,还有 python 包 spaCy 和 confluent_kafka - pip install spacy confluent_kafka.

步骤 1:构建 kafka 集群

为了构建集群,我们将使用一个docker-compose文件来启动所有需要的 docker 容器:zookeeper、一个代理和模式注册表。

现在简单地说,kafka 是一个分布式流媒体平台,能够处理大量的消息,这些消息被组织或分组到主题中。为了能够并行处理一个主题,必须将它分成多个分区,来自这些分区的数据存储在称为代理的独立机器中。最后,zookeeper 用于管理集群中代理的资源。这是卡夫卡经典版本中的元素。汇合平台添加了另一个元素,称为模式注册表。这是一种非常方便的方法,可以确保我们在写入和读取流中的数据时保持相同的模式。通常模式是以独立于平台的方式用avro格式编写的,它存储在模式注册表中。

为了读写 kafka 集群,我们需要一个代理地址、一个主题和模式注册中心的 url。

docker-compose将在2181端口启动zookeper,在9092端口启动kafka broker,在9999端口启动schema registry。除此之外,我们使用另一个 docker 容器kafka-create-topic的唯一目的是在 kafka 代理中创建一个主题(称为 test)。

要启动 kafka 集群,我们必须在定义 docker compose 文件的同一文件夹中运行以下命令行指令:

docker-compose up

这将启动所有带有日志的 docker 容器。我们应该在控制台中看到类似这样的内容:

步骤 2:启动 kafka 生成器,并在流中写入一些消息

我们将用 python 创建一个简单的 kafka producer 来发送消息。这将通过使用confluent_kafka库来实现。

卡夫卡的信息是键值对。该键通常用于对主题中的数据进行分区。我们将为我们的消息定义一个 avro 模式:

value_schema_str = """
{
   "namespace": "my.schema",
   "name": "value",
   "type": "record",
   "fields" : [
     {
       "name" : "data",
       "type" : "string"
     }
   ]
}
"""key_schema_str = """
{
   "namespace": "my.schema",
   "name": "key",
   "type": "record",
   "fields" : [
     {
       "name" : "key",
       "type" : "string"
     }
   ]
}
"""

key schema中,我们有一个名为key的字段,类型为string,在value schema(实际数据)中,我们也只有一个名为data的字段,类型为 string。

主要的 python 代码非常简单:

一旦我们用适当的配置定义了一个生产者,我们就可以向某个主题的代理异步发送大量消息。在教程的最后,我们有一个包含完整示例的 github 库的链接,包含配置和其他我们有意跳过的内容。

需要注意的一件重要事情是我们生成数据和密钥的方式:

value = {"data": random.choice(simple_messages)}        
key = {"key": str(uuid.uuid4())}

密钥是一个随机的 uuid 字符串。这将确保数据在集群中均匀分布。对于数据,我们只是从预定义的列表中随机选择一个句子。对于我们发送的每一句话,我们将在消费者端应用一些 nlp 规则。

接下来,我们将消息发送到一个缓冲区,一旦达到一定数量的消息,该缓冲区将被刷新:

try:            
    avroProducer.produce(topic=args.topic, value=value, key=key)        except BufferError as e:            
    messages_to_retry += 1

如果发送数据时出现问题,我们会再次重试。最后,我们只需要flush缓冲区,我们实际上是将消息发送到 kafka 集群。

要运行生成器,我们只需在终端中运行:

python3 producer.py

我们应该会看到日志we’ve sent 5 messages to 127.0.0.1:9092

producer.py也有额外的命令行参数。例如,如果我们想要发送 10 条消息,而不是默认数量的5,我们可以使用python3 producer.py -m 10

可选的命令行参数有:

  • -b代理 ip 和端口,默认127.0.0.1:9092
  • -s模式注册表 url,默认[http://127.0.0.1:9999](http://127.0.0.1:9999)
  • -t卡夫卡主题,默认test
  • -m消息数量,默认5

步骤 3:消费 kafka 消息并应用 nlp 处理

卡夫卡的消费者是拼图的最后一块。在这一部分中,我们创建了一个AvroConsumer并为其订阅了test主题。我们对主题进行投票,直到找到想要的消息数量,并跳过null或无效的消息。

一旦我们对一条消息进行了去序列化,我们就可以使用spacy来应用 nlp 管道,并从单词中提取一些信息。

spacy 很酷的一点是,它是根据英语中的一般单词预先训练的,所以我们几乎可以开箱即用。为了下载spacy的英语词汇,我们需要运行python3 -m spacy download en

这个spacy API 非常容易使用。我们只需要将我们的句子传递给nlp方法,这将运行一系列算法,如tokenization(将句子分解成单词)、lemmatisation(获得单词的基本形式)、part-of-speech tagging(从单词中获得词性,如动词、副词等。),named entity extraction(识别命名实体,如组织或地理实体)。一旦应用了算法,我们就可以从每个单词中提取我们需要的数据。

要运行使用者,请执行以下操作:

python3 consumer.py

使用与我们在生成器中相同的可选命令行参数。

输出应该类似于下图:

完整的代码示例可以在 github 上找到。

用 BERT 和 TensorFlow 构建搜索引擎

原文:https://towardsdatascience.com/building-a-search-engine-with-bert-and-tensorflow-c6fdc0186c8a?source=collection_archive---------2-----------------------

在这个实验中,我们使用一个预先训练好的 BERT 模型检查点来建立一个通用的文本特征提取器,并将其应用到最近邻搜索任务中。

T-SNE decomposition of BERT text representations (Reuters-21578 benchmark, 6 classes)

基于深度神经概率语言模型如伯特的特征提取器可以提取与大量下游 NLP 任务相关的特征。出于这个原因,它们有时被称为自然语言理解 (NLU)模块。

这些特征也可以用于计算文本样本之间的相似性,这对于基于实例的学习算法(例如 K-NN )是有用的。为了说明这一点,我们将为文本构建一个最近邻搜索引擎,使用 BERT 进行特征提取。

这个实验的计划是:

  1. 获取预训练的 BERT 模型检查点
  2. 提取为推理而优化的子图
  3. 用 tf 创建特征提取器。估计量
  4. 用 T-SNE 和嵌入式投影仪探索向量空间
  5. 实现最近邻搜索引擎
  6. 用数学加速搜索查询
  7. 示例:构建电影推荐系统

问题和答案

这本指南里有什么?

本指南包含两件事的实现:一个 BERT 文本特征提取器和一个最近邻搜索引擎。

这本指南是给谁的?

本指南对那些有兴趣使用 BERT 进行自然语言理解任务的研究人员应该是有用的。它也可以作为与 tf 接口的工作示例。估计器 API。

需要什么?

对于熟悉 TensorFlow 的读者来说,完成本指南大约需要 30 分钟。

给我看看代码。

这个实验的代码可以在 Colab 这里获得。另外,看看我为我的 BERT 实验建立的库:它包含额外的东西!

现在,我们开始吧。

步骤 1:获得预训练模型

我们从预先训练的 BERT 检查点开始。出于演示的目的,我将使用谷歌工程师预先训练的无外壳英文模型。要训练一个不同语言的模型,请查看我的其他指南。

为了配置和优化用于推理的图表,我们将利用令人敬畏的 bert-as-a-service 存储库。这个存储库允许通过 TCP 为远程客户端提供 BERT 模型。

在多主机环境中,拥有远程 BERT-server 是非常有益的。然而,在实验的这一部分,我们将着重于创建一个本地
(进程内)特征提取器。如果希望避免由客户端-服务器架构引入的额外延迟和潜在故障模式,这是有用的。

现在,让我们下载模型并安装软件包。

!wget [https://storage.googleapis.com/bert_models/2019_05_30/wwm_uncased_L-24_H-1024_A-16.zip](https://storage.googleapis.com/bert_models/2019_05_30/wwm_uncased_L-24_H-1024_A-16.zip)
!unzip wwm_uncased_L-24_H-1024_A-16.zip
!pip install bert-serving-server --no-deps

步骤 2:优化推理图

通常,要修改模型图,我们必须做一些低级的张量流编程。然而,由于 bert-as-a-service,我们可以使用简单的 CLI 界面来配置推理图。

有几个参数需要注意。

对于每个文本样本,BERT 编码层输出一个形状张量[ sequence_lenencoder_dim ],每个标记一个向量。如果我们要获得一个固定的表示,我们需要应用某种类型的池。

POOL_STRAT 参数定义应用于编码器层号 POOL_LAYER 的池策略。默认值' REDUCE_MEAN '对序列中所有记号的向量进行平均。当模型没有微调时,这种策略最适合大多数句子级任务。另一个选项是 NONE ,在这种情况下,根本不应用池。这对于单词级的任务很有用,例如命名实体识别或词性标注。关于这些选项的详细讨论,请查看晓寒的博客文章。

SEQ _ 莱恩影响模型处理的序列的最大长度。较小的值将几乎线性地提高模型推理速度。

运行上述命令会将模型图和权重放入一个 GraphDef 对象,该对象将在 GRAPH_OUT 序列化为一个 pbtxt 文件。该文件通常比预训练模型小,因为训练所需的节点和变量将被移除。这产生了一个非常可移植的解决方案:例如,英语模型在序列化后只需要 380 MB。

步骤 3:创建特征提取器

现在,我们将使用串行化图来构建一个使用 tf 的特征提取器。估计器 API。我们将需要定义两件事情:输入 _fn模型 _fn

input_fn 管理将数据放入模型。这包括执行整个文本预处理管道,并为 BERT 准备一个 feed_dict

首先,每个文本样本被转换成一个 tf。示例实例包含输入名称中列出的必要特性。bert_tokenizer 对象包含了单词表并执行文本预处理。之后,在一个 feed_dict 中,示例按特征名重新分组。

tf。估算器有一个有趣的特性,每次调用 predict 函数时,它都会重新构建和初始化整个计算图。因此,为了避免开销,我们将向 predict 函数传递一个生成器,该生成器将在一个永无止境的循环中向模型提供特征。哈哈。

model_fn 包含模型的规格。在我们的例子中,它是从我们在上一步中保存的 pbtxt 文件中加载的。这些特征通过输入映射明确映射到相应的输入节点。

现在我们几乎拥有了进行推理所需的一切。我们开始吧!

由于我们使用了生成器,对 bert_vectorizer 的连续调用不会触发模型重建。

>>> bert_vectorizer = build_vectorizer(estimator, build_input_fn)
>>> bert_vectorizer(64*['sample text']).shape
(64, 768)

上述特征提取器的独立版本可以在库中找到。

第四步:用投影仪探索向量空间

现在是展示的时候了!

使用矢量器,我们将为来自 Reuters-21578 基准语料库的文章生成嵌入。
为了在 3D 中可视化和探索嵌入向量空间,我们将使用一种叫做 T-SNE 的降维技术。

让我们首先获得文章嵌入。

在嵌入投影仪上可以获得生成嵌入的交互式可视化。

从链接中你可以自己运行 T-SNE 或者使用右下角的书签加载一个检查点(加载只在 Chrome 上有效)。

使用生成的特征构建监督模型非常简单:

第五步:构建搜索引擎

现在,假设我们有一个 50k 文本样本的知识库,我们需要根据这些数据快速回答查询。我们如何从文本数据库中检索与查询最相似的样本?答案是最近邻搜索。

形式上,我们要解决的搜索问题定义如下:
给定向量空间 M 中的一组点 S ,以及一个查询点Q****∈M,找出 SQ 中最近的点。在向量空间中有多种方法来定义“最近的”,我们将使用欧几里德距离。

因此,要构建文本搜索引擎,我们将遵循以下步骤:

  1. 向量化知识库中的所有样本——这给出了 S
  2. 向量化查询——这给出了 Q
  3. 计算 QS 之间的欧几里德距离 D
  4. 按升序排序D——提供最相似样本的索引
  5. 从知识库中检索所述样本的标签

为了让这个简单的实现变得更令人兴奋,我们将在纯张量流中实现它。

首先,我们为 QS 创建占位符

定义欧几里德距离计算

最后,得到最相似的样本指数

现在我们已经建立了一个基本的检索算法,问题是:
我们能让它运行得更快吗?通过一点点数学知识,我们可以做到。

第六步:用数学加速搜索

对于一对向量 pq,,欧几里德距离定义如下:

这正是我们在第四步中的计算方法。

然而,由于 pq 是向量,我们可以扩展并重写它:

其中⟨…⟩表示内积。

在张量流中,这可以写成如下形式:

顺便说一下,上面公式中的 PPQQ 实际上是各自向量的平方 L2 范数。如果两个向量都是 L2 归一化的,那么 PP = QQ = 1。这给出了内积和欧几里德距离之间一个有趣的关系:

然而,进行 L2 归一化丢弃了关于矢量幅度的信息,这在许多情况下是不期望的。

相反,我们可能注意到,只要知识库不变, PP,其平方向量范数也保持不变。因此,我们可以只做一次,然后使用预计算的结果进一步加速距离计算,而不是每次都重新计算。

现在让我们把它们放在一起。

当然,您可以将这种实现用于任何矢量器模型,而不仅仅是 BERT。它在最近邻检索方面非常有效,能够在双核 Colab CPU 上每秒处理几十个请求。

例子:电影推荐系统

对于这个例子,我们将使用来自 IMDB 的电影摘要数据集。使用 NLU 和检索器模块,我们将构建一个电影推荐系统,推荐具有相似情节特征的电影。

首先,让我们下载并准备好 IMDB 数据集。

用伯特·NLU 模块矢量化电影情节:

最后,使用 L2Retriever,找到与查询电影情节向量最相似的电影,并将其返回给用户。

我们去看看吧!

>>> recommend = buildMovieRecommender(names, X_vect)
>>> recommend("The Matrix")
Impostor 
Immortel 
Saturn 3 
Terminator Salvation 
The Terminator 
Logan's Run 
Genesis II 
Tron: Legacy 
Blade Runner

结论

在本指南中,我们构建了一个通用的 BERT 特征提取器。用从 BERT 中提取的特征建立的模型在分类和检索任务上表现充分。虽然可以通过微调来进一步提高它们的性能,但是所描述的文本特征提取方法为下游 NLP 解决方案提供了可靠的无监督基线。

本系列中的其他指南

  1. 用云 TPU 从头开始预训练伯特
  2. 用 BERT 和 Tensorflow 搭建搜索引擎【你在这里】
  3. 用 Keras 和 tf 微调 BERT。模块
  4. 使用 BERT 和表示学习改进句子嵌入

构建一个“无服务器”的 Chrome 扩展

原文:https://towardsdatascience.com/building-a-serverless-chrome-extension-f684740e1ffc?source=collection_archive---------8-----------------------

这是一个关于构建利用无服务器架构的 Chrome 扩展的教程。具体来说——我们将在 Chrome 扩展的后端使用谷歌云功能,为我们做一些奇特的 Python 魔术。

我们将构建的扩展是 SummarLight Chrome 扩展:

[## 介绍 summary light——一个 Chrome 扩展,它突出了

medium.com](https://medium.com/@btahir/introducing-summary-light-a-chrome-extension-that-highlights-the-most-important-parts-of-an-1666e10411a8)

SummarLight 扩展获取你当前所在网页的文本(大概是一个像我这样的媒体上的很酷的博客),并突出显示那个页面/文章最重要的部分。

为了做到这一点,我们将设置一个 UI(在本例中是一个按钮),它将把当前网页上的文本发送到我们的后端。在这种情况下,“后端”将是一个谷歌云功能,它将分析文本并返回其摘要(该文本中最重要的句子)。

体系结构

A Simple & Flexible Architecture

正如我们所见,该架构非常简单灵活。你可以设置一个简单的用户界面,比如一个应用程序,或者在这种情况下,一个 Chrome 扩展,然后把任何复杂的工作交给你的无服务器功能。您可以轻松地更改函数中的逻辑,并重新部署它来尝试其他方法。最后,您可以根据需要扩展它以支持任意多的 API 调用。

这不是一篇关于无服务器的好处的文章,所以我不会详细讨论使用它优于传统服务器的优点。但是通常情况下,无服务器解决方案会便宜得多并且可伸缩(但并不总是这样…取决于您的使用情况)。

Chrome 扩展

这里有一个关于 Chrome 扩展设置的很好的指南:

[## 如何在 20 分钟内创建并发布一个 Chrome 扩展

如何在 20 分钟内创建并发布 Chrome 扩展

www.freecodecamp.org](https://www.freecodecamp.org/news/how-to-create-and-publish-a-chrome-extension-in-20-minutes-6dc8395d7153/)

SummarLight 扩展的所有代码都可以在这里找到:

[## btahir/summary light

Summarlight Chrome 扩展突出了帖子/故事/文章中最重要的部分。-btahir/summary light

github.com](https://github.com/btahir/summarlight)

根目录中的 main.py 文件是我们定义 Google Cloud 函数的地方。extension_bundle 文件夹包含了创建 Chrome 扩展的所有文件。

谷歌云功能

我选择了谷歌而不是 AWS Lamba,因为我有一些免费的学分(感谢谷歌!)但是你也可以用 AWS 轻松做到。对我来说,这是一个巨大的优势,他们刚刚发布了 Python 的谷歌云功能,因为我用这种美丽的语言处理大部分数据。

您可以在此了解有关部署谷歌云功能的更多信息:

[## 从本地计算机部署|云功能文档| Google 云

无论您的企业是刚刚踏上数字化转型之旅,还是已经走上数字化转型之路,谷歌云的解决方案…

cloud.google.com](https://cloud.google.com/functions/docs/deploying/filesystem)

我强烈推荐使用 gcloud sdk,并从 hello_world 示例开始。您可以在他们提供的 main.py 文件中编辑您需要的函数。这是我的函数:

import sys
from flask import escape
from gensim.summarization import summarize
import requests
import json

def read_article(file_json):
    article = ''
    filedata = json.dumps(file_json)
    if len(filedata) < 100000:
        article = filedata
    return article

def generate_summary(request):

    request_json = request.get_json(silent=True)
    sentences =  read_article(request_json)

    summary = summarize(sentences, ratio=0.3)
    summary_list = summary.split('.')
    for i, v in enumerate(summary_list):
        summary_list[i] = v.strip() + '.'
    summary_list.remove('.')

    return json.dumps(summary_list)

相当直接。我通过 read_article()函数接收一些文本,然后使用 awesome Gensim 库返回这些文本的摘要。Gensim Summary 函数的工作原理是按照重要性对所有句子进行排序。在这种情况下,我选择返回最重要句子的前 30%。这将突出文章/博客的前三分之一。

替代方法:我尝试了不同的摘要方法,包括使用手套词嵌入,但结果并不比 Gensim 好多少(特别是考虑到由于加载这些大规模嵌入而增加的处理计算/时间)。尽管如此,这里仍有很大的改进空间。这是一个活跃的研究领域,并且正在开发更好的文本摘要方法:

[## 摘要

知识库跟踪自然语言处理(NLP)的进展,包括数据集和当前…

nlpprogress.com](http://nlpprogress.com/english/summarization.html)

一旦我们对这个功能很满意,我们就可以部署它,它将在一个 HTTP 端点上可用,我们可以从我们的应用程序/扩展调用它。

扩展包

现在是前端。首先,我们需要一份 popup.html 档案。这将处理 UI 部分。它将创建一个带有按钮的菜单。

<!DOCTYPE html>
<html>
  <head>
    <link rel="stylesheet" href="styles.css">
  </head>
  <body>
    <ul>
    	<li>
      <a><button id="clickme" class="dropbtn">Highlight Summary</button></a>
      <script type="text/javascript" src="popup.js" charset="utf-8"></script>
    	</li>
    </ul>
  </body>
</html>

正如我们所看到的,“Highlight Summary”按钮有一个触发 popup.js 文件的 onClick 事件。这将依次调用 summarize 函数:

function summarize() {
	chrome.tabs.executeScript(null, { file: "jquery-2.2.js" }, function() {
	    chrome.tabs.executeScript(null, { file: "content.js" });
	});
}
document.getElementById('clickme').addEventListener('click', summarize);

summarize 函数调用 content.js 脚本(是的是的,我知道我们可以避免这个额外的步骤……)。

alert("Generating summary highlights. This may take up to 30 seconds depending on length of article.");

function unicodeToChar(text) {
	return text.replace(/\\u[\dA-F]{4}/gi, 
	      function (match) {
	           return String.fromCharCode(parseInt(match.replace(/\\u/g, ''), 16));
	      });
}

// capture all text
var textToSend = document.body.innerText;

// summarize and send back
const api_url = 'YOUR_GOOGLE_CLOUD_FUNCTION_URL';

fetch(api_url, {
  method: 'POST',
  body: JSON.stringify(textToSend),
  headers:{
    'Content-Type': 'application/json'
  } })
.then(data => { return data.json() })
.then(res => { 
	$.each(res, function( index, value ) {
		value = unicodeToChar(value).replace(/\\n/g, '');
		document.body.innerHTML = document.body.innerHTML.split(value).join('<span style="background-color: #fff799;">' + value + '</span>');
	});
 })
.catch(error => console.error('Error:', error));

这里是我们解析当前页面的 html(document . body . innertext)的地方,在用 unicodeToChar 函数进行了一些预处理之后,我们通过 Fetch API 将它发送给我们的 Google Cloud 函数。为此,您可以在 api_url 变量中添加自己的 HTTP 端点 url。

同样,利用 Fetch,我们返回一个承诺,这是从我们的无服务器函数生成的摘要。一旦我们解决了这个问题,我们就可以通过页面的 html 内容解析这个循环,并突出显示摘要中的句子。

由于完成所有这些处理可能需要一点时间,我们在页面顶部添加了一个警告来表明这一点(“生成摘要突出显示。根据文章的长度,这可能需要 30 秒钟。”).

最后,我们需要创建一个 manifest.json 文件来发布扩展:

{
  "manifest_version": 2,
  "name": "SummarLight",
  "version": "0.7.5",
  "permissions": ["activeTab", "YOUR_GOOGLE_CLOUD_FUNCTION_URL"],
  "description": "Highlights the most important parts of posts/stories/articles!",
  "icons": {"16": "icon16.png",
            "48": "icon48.png",
            "128": "icon128.png" },
  "browser_action": {
   "default_icon": "tab-icon.png",
   "default_title": "Highlight the important stuff",
   "default_popup": "popup.html"
  }
}

注意“权限”选项卡。我们必须在这里添加我们的 Google Cloud 函数 URL,以确保当我们通过 Fetch API 调用我们的函数时不会出现 CORS 错误。我们还填写了名称/描述和图标等细节,以便在 Google Store 上显示我们的 Chrome 扩展。

就是这样!我们已经创建了一个 Chrome 扩展,它利用了一个无服务器的主干网,也就是谷歌云功能。最终效果是这样的:

A demo of SummarLight

这是一种简单而有效的方式来构建真正酷的应用程序/扩展。想想你用 Python 做过的一些东西。现在你可以把你的脚本挂在一个扩展/应用程序的按钮上,然后用它做一个好的产品。无需担心任何服务器或配置。

下面是 Github 回购:https://github.com/btahir/summarlight

你可以自己使用分机。这是谷歌商店的直播:

[## 摘要灯

突出文章最重要的部分!

chrome.google.com](https://chrome.google.com/webstore/detail/summarlight/ligjmagakdphdlenhhncfegpdbbendlg?hl=en-US&gl=US)

请在评论中分享你对利用谷歌云功能的扩展(或应用)的想法。😃

干杯。

为您的模型构建一个稳定且用户友好的 API

原文:https://towardsdatascience.com/building-a-stable-and-user-friendly-api-for-your-model-ef4b2167934e?source=collection_archive---------12-----------------------

Source

你的模型值得一个伟大的 API

大多数(如果不是全部)数据科学家最终都需要部署他们的模型,并通过 API 使其可用。在机器学习项目的端到端过程中,这通常是最容易被忽视的部分,因为大多数注意力都集中在建立模型或可怕的数据清理上。构建一个稳定且用户友好的 API 是让用户/产品团队开始消费你的服务并持续消费的关键。那么,如何着手构建一个稳定且用户友好的 API 呢?这就是这篇文章的来源,这些年来,我专门为数据科学模型构建了许多 API,我想与您分享我是如何着手构建它们的。本文假设了以下情况:

  • Python 知识
  • 烧瓶的基本知识

如果你还没有猜到,我们将使用 Python 和 Flask 来构建 API,所有代码都可以在我的 GitHub 账户上找到。

问题背景

我们建立了一个(非常粗糙的)随机森林模型来预测房价。这个模型有三个特点:is_househas_gardenn_bedroomsis_househas_garden都是二进制标志,而n_bedrooms是整数。向利益相关者展示该模型给他们留下了深刻的印象,现在他们希望进入下一阶段,即通过 API 进行质量保证。现在,您的任务是为您的模型构建一个 API。

文件夹结构

我的 API 通常遵循这种文件夹结构。我试图让它尽可能简单,同时也让它易于扩展:

  • common:保存常用的模块/类,可以与代码库的其他部分共享(例如,代码库中可能有多个 API)
  • endpoint_docs:保存端点文档的 YAML 文件(我们稍后会讲到)
  • views:保存 API 视图的模块
  • api.py:本地启动 API
  • blueprint.py:用于使您的应用程序模块化,并为您的端点生成 URL 前缀
  • config.yml.example:保存应用程序配置的示例文件
  • Pipfile:API 需要的包
  • settings.py:用于从您的配置文件导入设置
  • wsgi.py:用于启动生产服务器
  • .env.example:保存环境变量

要在本地安装并运行,请查看 repo 上的自述文件,其中给出了安装说明。

配置和设置文件

这可能看起来像是构建 API 的一个非常无聊的方面,确实如此,但是从一开始就正确地配置 API 确实有助于以后的工作。这里涉及的三个文件是 API 配置:

  • 配置. yml .示例
  • . env .示例
  • settings.py

config.yml.env文件包含应用程序的应用程序设置和环境变量,其中可能包含敏感信息。我们不希望这些信息出现在存储库中,所以我们创建了示例文件,其中省略了敏感信息。例如,config.yml文件可能包含一个数据库密码:DB_PASSWORD: XXXXX,但是在config.yml.example中这个密码将是:DB_PASSWORD:。密码被删除,但与之相关的变量被保留,这使得应用程序的新用户更容易启动和运行,因为这突出了应用程序所需的配置。要使用这些文件,你需要做的就是复制它们,但是去掉后缀.example重新命名它们。在我的config.yml文件中,我只有一个名为MODEL_NAME的变量,这只是我们希望使用的模型的名称。在config.yml文件中,用rf_classifier.pkl填写MODEL_NAME 。在.env文件中,将APP_SETTINGS_YAML分配给config.yml。最后,确保config.yml.env 文件都在您的.gitignore文件中,这样它们就不会被意外地推到存储库中。

settings.py文件是 API 对这些变量的访问点(如下所示)。以下代码部分的第 9-14 行加载到.env文件中,并有一个用于MODEL_NAME的占位符,默认为None:

接下来,获得APP_SETTING_YAML(在.env文件中定义)的值(第 17 行),该值为config.yml。然后读取该文件并提取键值对(第 18–20 行)。然后,config.yml中的这些键值对被用来更新settings.py文件中的值。占位符为None的变量是要更新的变量。

MODEL_NAME的初始值是None,但是在读入config.yml文件并更新全局值后,它现在被设置为rf_classifier.pkl。要访问这个变量,您在应用程序中要做的就是:

*import* settings
print(settings.MODEL_NAME)

这里需要注意的重要一点是settings.py中定义的应用变量名必须与config.yml中的键名完全匹配,否则settings.py中的变量不会被更新。

第 22–24 行的最后一段代码实际上查看了您的操作系统中定义的环境变量,并使用这些变量覆盖来自config.yml的派生变量。我听你说,你为什么要这么做?当您通常部署到生产环境时,可能会有不同的应用程序设置,如生产数据库用户名和密码或端口号等,您不会在本地使用这些设置。你需要做的不是有第二个config.yml文件,而是在你的操作系统环境中定义这些相同的变量。这允许您在用于本地开发的设置和用于生产的设置之间轻松切换,而无需更改任何应用程序代码。这里要记住的一个关键点是,在操作系统环境中定义的变量优先于在config.yml文件中定义的变量。

常用文件夹

该文件夹包含 python 模块,这些模块具有与其他 API 相同的功能。因此,如果您要为这个项目添加另一个 API,比如说为另一个模型添加 API,您就不必重复代码。让我们从schemas.py开始分解这个文件夹中的模块

schemas.py

该模块使用 marshmallow 包将数据加载到我们定义的模式中。Marshmallow 将根据模式检查数据,如果发现任何不一致,将会引发错误。通过对照模式验证数据,我们确保 API 的消费者符合模型被训练的特性范围。这很好,因为它防止 API 的消费者向模型传递意外的参数,从而获得奇怪的结果,这些结果可能会在以后的报告中使用。我们模型的模式如下所示:

我们首先创建一个从 marshmallow 继承了Schema类的类,这将允许我们创建自己的模式。接下来,模式的变量(模型的特征)被定义并被赋予字段对象。字段对象简单地定义了对象的类型,例如字符串,甚至是字段接受的另一个模式的整数。它们还可以通过传递带有布尔值的关键字参数required来声明一个字段是否是强制的。这里我们创建三个变量is_househas_garden,n_bedrooms,它们在第 13-15 行声明,并根据需要标记。这三个变量都定义了一个整数类型。接下来,我们创建三个函数来验证每一个特性,这些函数的名字前面都有前缀validate,这不是必须的,只是我自己命名的。这些函数是用@validates方法修饰的,该方法接受一个参数,该参数是您希望作为类型字符串进行验证的特性的名称。只有用@validates包装的函数用于验证数据,并且字符串输入必须是在类顶部定义的变量。作为修饰函数的结果,它们现在接受一个位置参数,这里定义为value。这是传递给每个特性的值,我们根据它执行验证。is_househas_garden都是二进制标志,具有完全相同的检查,只有错误消息不同(第 17-25 行)。

例如,如果validate_is_house中的 if 语句返回 true,就会产生一个错误,并显示一条特定的消息:is_house must be either 0 or 1。然后可以将错误消息返回给客户端。这种错误信息为用户提供了有用的调试信息,而不是一般的 HTTP 错误信息。n_bedrooms验证(第 27–30 行)验证该值是否大于或等于零,如果不大于或等于零则引发错误。

方法make_price_prediction_data_model在模式检查通过后运行。这是由@posts_loads装饰者定义的。该函数的执行返回包含特征的数据类对象。这是查看data_models.py中定义的数据类的好时机

data_models.py

如上所述,一旦模式检查通过,数据模型就会以数据类的形式返回。如果你不知道什么是数据类,我强烈推荐你查看这个帖子。数据类为我们提供了开箱即用的附加功能,还允许我们通过名称访问类的属性,就像普通的类一样。我们模型的数据类如下所示:

这里没什么事情。我们定义了一个由@dataclass包装的类,并在该类中定义了三个变量,它们是该类的属性。在我们的例子中,这些属性是模型的特征。我还定义了一个__repr__方法来给我们一个有用的类表示。就像普通的类一样,如果需要的话,你可以添加额外的方法。

logger.py

最后一个模块是common文件夹,是日志文件。该文件只包含几行设置记录器的代码:

在这里,我设置了一个带有StreamHandler的日志记录器来记录控制台。如果您愿意,您可以设置一个记录器来记录文件,请参考文档中的基础教程来告诉您如何操作。

时髦的

有了定义的模式,用户如何预先知道什么是模型可接受的输入?此外,用户如何为您的 API 探索其他端点?这就是霸气的用武之地。通过在 API 中实现 Swagger,我们为 API 提供了一个可视端点,列出了所有可用的端点以及它们接受的 HTTP 方法。此外,对于每个端点,您可以提供要运行的模式和示例,本质上是记录 API。下面是我们正在构建的 API 的 Swagger 页面的屏幕截图:

如何实现 swagger for out API?好消息是,有一些软件包可以帮我们做到这一点,比如 Flassager 和connection。对于我们的 API,我们将使用 Flassager。

招摇文件

为了让 swagger 显示信息,我们需要为它提供文档。该文件以yaml文件格式提供,位于endpoint_docs文件夹中。如果您导航到在/endpoint_docs/property_price_prediction_v1/get.yml找到的文件,您将找到上面显示的该端点的文档。该文件中包含端点的参数以及响应示例。有关记录端点的更多信息,请参见 swagger 文档

创建端点

现在我们来看 api 本身的代码。让我们从位于/views/property_price_prediction.py的 API 的唯一端点开始。该文件包含一个定义端点的类:

ProperyPricePrediction类从flassagger继承了SwaggerView,后者也从 Flask 继承了方法视图。方法视图允许我们使用同一个端点在一个类中接受不同的 HTTP 方法。要做到这一点,我们首先需要定义以它们所服务的方法命名的函数。例如,在上面的代码中,这个端点接受 GET 方法,所以我定义了一个名为get的函数。如果我想让这个端点也接受一个POST方法,我要做的就是在这个类中定义一个post方法,并将POST添加到在类顶部创建的methods变量中。这个变量对于 method 视图来说不是必需的,但是对于 swagger 来说是必需的,所以如果你没有使用 swagger,你可以省略它。如果你使用的是 swagger,那么它现在希望文档存在于一个名为get.yml的文件中,你可能已经猜到了,但是这些文件是以它们所服务的 HTTP 方法命名的。下面的蓝图部分解释了get.yml文件的目录结构。

转到def get方法。这个函数中的代码被封装在一个try/except块中,以防止在任何处理过程中出现错误时 API 关闭。这个方法获得模型的输入,作为第 21 行定义的 URL 参数,并将它转换成一个字典。接下来的第(22)行将这个特性加载到模式中,模式反过来验证输入。如果失败,验证错误出现,在try/except块中捕获并返回给用户。如果通过,数据将被加载到数据模型中,该模型现在可用于进行预测。下一行代码使用数据模型进行预测(第 23— 27 行)。请注意,我们现在可以通过名称来引用特性,这样在代码中看起来更好。然后,预测作为 JSON 对象返回给用户。

在文件的顶部,我导入了设置模块。在这个模块中定义了一个全局变量MODEL_NAME。该变量表示要使用的模型名称,该名称随后被分配给MODEL变量。我将MODEL_NAME定义为一个 API 配置,因为它可以通过简单地改变应用程序中的配置而不是源代码,轻松地在不同模型之间快速切换。MODEL变量是在类的外部定义的,因为我们只需要模型加载一次。如果这是在get方法中定义的,这将意味着每次请求进来时都要加载模型。这将大大降低请求的响应时间,尤其是在模型很大的情况下。

蓝图

蓝图允许我们模块化我们的应用程序路线,例如,我们可能有一个仅供管理员使用的蓝图和一个供用户使用的蓝图,因此分离路线。为了实现这一点,我们需要创建蓝图并向它们注册端点,在blueprints.py中的代码为我们做了这些:

一般来说,要创建一个蓝图,您所要做的就是创建一个Blueprint类的实例,并用add_url_rule方法向它注册端点。在上面的代码中,我通过创建自己的继承 Flask 的Blueprint类的BaseBlueprint类来扩展Blueprint类。我这样做是为了让日志记录更容易。Flask 的 Blueprint 类有两个方法before_requestafter_request,如果分配给方法,它们将分别在请求之前和之后被调用。在BaseBlueprint类的__init__方法中,我调用这两个方法,并分配_log_before_request在每个请求之前调用,而_log_after_request在每个请求之后调用。在_log_before_request方法中,我记录了与任何参数一起被调用的端点。_log_after_request方法记录 HTTP 响应状态和返回的 JSON 负载。对于单个端点来说,这似乎是一项繁重的工作,但是如果我们要向 API 添加新的端点,我们所要做的就是将新的视图作为一项规则添加到这个BaseBlueprint实例中(实际上只有一行代码),并且我们已经实现了日志记录!这使得扩展 API 的日志更加容易。我发现这个解决方案比在视图函数本身中实现日志记录要优雅得多。

现在需要做的就是创建一个BaseBlueprint类的实例,这是在第 31 行完成的。然后,我们获取该实例并向其添加一个 URL 规则(第 33–36 行),这意味着每次调用该 URL 时,都会调用分配的视图函数。add_url_rule方法接受一个位置参数,它是一个字符串,是这个端点的 URL。我还添加了一个关键字参数view_func,它接受我们想要分配给这个 URL 的MethodView(或者在我们的例子中是SwaggerView)。在这里,我将PropertyPricePrediction视图分配给这个 URL,并声明它是一个使用as_view方法的视图。这样做是为了将类转换成路由系统可以使用的视图函数。这个方法中的关键字参数name只是为这个视图指定一个名称。这里定义的名称也为 Swagger 搜索端点文档提供了参考。因为这个视图有一个get方法,所以到get文档的路径会有如下模式:<name of view>/<HTTP method>.yml并且会转换成:property_price_prediction_v1/get.yml

api.py

该文件是启动 API 的访问点,主要包含配置:

让我们一行一行地过一遍:

  • 1–5:所需进口。注意,我导入了 Swagger 和我们在上面创建的蓝图
  • 8 :创建一个 Flask 实例
  • 10–15:允许所有前缀为/api的 URL 上的跨来源请求
  • 17–20:注册我们在上面创建的蓝图,并为所有端点创建以/api开头的前缀
  • 21–23:有些霸气的配置。在这里,我告诉 Swagger 在哪里可以找到端点文档。
  • 24–34:围绕第 9 行创建的 Flask 实例创建一个 Swagger 实例。我将 parse 设置为False,这意味着不根据端点文档中提供的模式检查传入的请求,因为我更喜欢 Marshmallow 的灵活性。当然,您可以将其设置为true,所有传入的请求都将被检查。最后,我只是在 API 文档页面上提供一些元数据。
  • 36–41:只有当文件作为主程序运行时,才会启动 API。

wsgi.py

这是最后的文件。在生产设置中启动 API 的文件。这个文件只导入 API 实例(从 api.py)。我将这一点分离出来,这样我们就可以在生产中进行不同的设置,并在需要时进行任何额外的检查。你可以直接运行api.py,但是你将只使用 flask 的内部服务器,这不是我们想要的,并且不推荐用于生产。相反,我们将使用一个名为 Gunicorn 的包,它是一个用于 UNIX 的 Python WGSI 服务器,与许多 python web 框架兼容。如果你在 Windows 上,你可以使用女服务员。启动 API 的调用如下:

Unix / Mac : gunicorn -b 0.0.0.0:5000 -w 1 wsgi:api

视窗 : waitress-serve --listed=*5000 wsgi:api

如果您在 Unix / Mac 上运行,应该会看到类似这样的内容:

API 现在正在运行,如果您导航到http://localhost:5000/apidocs,您应该会看到:

这是显示 API 文档的页面。单击蓝色条将展开它,显示对模型执行查询的能力,如下所示:

单击“试用”将允许您输入特性值,输入以下特性值,然后点击“执行”(在您单击“试用”之前,按钮不会显示):

  • is_house : 5
  • had_garden : 1
  • n_bedrooms : 3

is_house的这个值对于我们的模型是无效的,当您点击 execute 时,它会抛出一个错误。再往下是示例 curl 请求,请求您刚刚输入的数据以及请求 url(示例 curl 请求直到执行查询后才会显示)。下面是来自服务器的实际响应。该示例如预期的那样应该返回 400 —错误:错误的请求以及该响应正文:

{
  "error": "{'is_house': ['is_house must be either 0 or 1']}"
}

这正是我们想要的,一个清晰的错误被返回给用户,解释请求失败的原因。现在输入一些应该有效的值:

  • is_house : 0
  • had_garden : 0
  • n_bedrooms : 2

然后点击执行。在点击 execute 之后,curl 请求和请求 url 应该被更新。您还应该有一个状态代码为 200 的新响应主体,表示查询成功。响应正文应该类似于:

{
  "data": {
    "predicted_price": [
      300000
    ]
  }
}

显示给定输入要素时模型对此属性的值。现在,如果我们看一下打印到控制台的日志,您应该会看到:

日志揭示了对模型的请求、传递的参数以及请求是否失败。我们可以看到第一个失败的请求,因为is_house必须是二进制值。接下来是我们的第二个请求,它满足模式并成功返回一个预测。

希望你能明白为什么使用 Swagger 是你的 API 的一个必要工具,因为很少的工作你就能得到很多额外的功能,这是完全值得的。这个工具允许用户使用你的 API,查看它的约束,这将允许他们相应地计划他们的资源消耗。

结论

这里,我们构建了一个 API 来处理请求,并优雅地将错误返回给用户,使 API 更加透明,不再是一个黑盒。数据模式是一个很好的约束,因为这可以防止模型在它被训练的特性范围之外的意外使用,从而防止一些尴尬的问题。

就是这样!希望在这篇文章中,我已经向您展示了构建一个稳定且用户友好的 API 的重要性。最后,感谢您的阅读,如果您感兴趣,我可能会写一些关于为 API 添加测试的帖子,以及一篇关于集装箱化的帖子。

用自然语言框架构建文本分类器

原文:https://towardsdatascience.com/building-a-text-classifier-with-naturallanguage-framework-5e4c1ce8e4e?source=collection_archive---------20-----------------------

了解如何为所有 Apple 平台上的应用程序构建文本分类器。

介绍

你可能知道或者至少听说过像 Siri、Cortana 和 Alexa 这样的虚拟助手。也许你和某个公司的聊天机器人聊过天?如果你不是像我一样以英语为母语,你可能会经常使用谷歌翻译来帮助你阅读像这样的开发材料。

这些都是自然语言处理的应用。NLP 有很多定义,但本质上,它指的是任何一种由计算机执行的自然语言操作,通常使用机器学习的技术。我所说的自然语言是指人类交流中使用的任何一种非结构化或无限制的语言。

去年,苹果发布了natural language Framework(NLF ),为那些愿意让他们的应用程序更具 NLP 智能的开发者提供更简单的体验。该框架提供了一个 API,它隐藏了文本处理任务中通常涉及的大部分复杂性。

NLF 是我在第 21 届佛罗里达会议上演讲的主题,在那里我详细介绍了 NLP 用例、其通用管道以及 NLF 的使用。根据这些建议,我决定写这篇博文来讨论这次演讲的主要部分。因此,我将介绍 NLP 的一些基本概念,并解释如何构建一个文本分类器。

自然语言框架

NLF 可以在所有苹果平台上使用,它是从 iOS 11 开始出现在Foundation中的实用程序类NSLinguisticTagger的演变。像它的前身一样,NLF 提供了以下内置功能:

  • 语言识别:该框架承诺识别近 60 种不同的语言。
  • 标记化:可以将文本分解成代表基本意义单位(即单词)的字符序列。NLF 处理像日语这样的语言,这个任务不仅仅是用空格分割句子。
  • 词性(PoS)标注:框架能够用来自某个方案的标签来标注每个标记。例如,我们可以根据词汇类别(如名词、动词、形容词等)对标记进行分类。
  • 词汇化:这其实是一种特殊的词性标注方案。它用于减少单词的屈折形式(例如,将复数形式变为单数)。
  • 命名实体识别(NER) :这是另一个词性标注方案。它用于识别感兴趣的实体,如位置、人员、组织、日期等。

这些内置函数允许我们执行许多很酷的文本处理任务,而无需任何进一步的机器学习算法。例如,我们可以通过一个基本的词汇化调整来改进文本搜索。

在这篇博客文章中,我不会详述语言识别和标记化(但是你可以在这里找到一篇关于它们的很棒的文章)。相反,我将集中讨论词性标注。

词性标注

NLTagger是负责词性标注任务的班级。它有一个enumerateTags(in:unit:scheme:options:block:)函数,用于列出给定文本中的标签,如清单 1 所示。block参数是一个闭包,每个令牌调用一次,传递它的范围和相应的标签。

Listing 1: Using NLTagger for PoS tagging.

为了实例化一个NLTagger对象,我们应该提供一个NLTagScheme数组来告诉标记者需要哪组标记。清单 1 使用了.nameTypeOrLexicalClass,因此只返回词法类和命名实体。您可以在输出 1 中看到结果。其他常用的方案有.lexicalClass.nameType.lemma

Output 1: Result of Listing 1.

unit是另一个重要参数。它告诉标记者应该标记哪个文本块。除了.word,我们还可以用.sentence.paragraph.document。但是,对于某些方案和单元的组合,标记器可能不会返回任何结果。例如,使用.lemma作为 scheme 和.sentence作为 unit 是没有意义的,因为您只能在每个单词的基础上进行词汇化。

最后一个参数是option。它只是告诉标记者补充信息,并不要求标记者工作。例如,我们可以用它来告诉标记者省略空格和标点符号,不要把粒子当作单独的标记。

定制模型

所有这些贴吧标签都很酷,但是如果我们想在文本中识别口袋妖怪的名字或者根据它们的类型对文本进行分类呢?好吧,那 NLF 默认做不到。我们需要建立一个自定义模型。

这可能是 NLF 最酷的部分:它可以与 CoreML 集成来构建定制的单词标签文本分类器。这还不是全部。苹果还发布了 CreateML 框架,用于构建直接进入 NLF 的定制 CoreML 模型。机器学习开始了。

NLP 通常是一个分类问题,目标是在有限的可能性集合中为给定的输入分配一个标签。它还通常使用监督学习算法,其中我们提供一个包含文本及其相应标签的训练集,算法使用该训练集进行学习。我不会深入讨论这些细节,因为这超出了讨论范围(不过,你可能想要查看这篇伟大的文章)。相反,让我们关注 NLF 如何通过构建一个定制的文本分类器来隐藏大部分的复杂性。

在下一节中,我将通过构建一个餐馆评论分类器来演示定制模型是如何工作的。目标是将输入文本标记为正面或负面评论。

构建分类器

培训工作流程如图 1 所示。我们将训练数据传递给 CreateML,这样它就可以使用 NLF 从这些数据中提取特征,学习模式并将这些知识保存为 CoreML 模型。

Image 1. Model training workflow.

清单 2 是我们将用来构建分类器的数据集的摘录。这是一个众所周知的数据集,它包含一千条评论及其相应的标签,可以是POSITIVENEGATIVE。你可以在这里找到完整的 JSON 文件。

Listing 2: Excerpt of the restaurant reviews dataset.

我们还需要一个 macOS 平台来训练模型,因为 CreateML 在其他平台上不可用。所以启动游戏并运行清单 3 中的代码。

Listing 3: Snippet for training the model.

第一步是将数据集加载到一个MLDataTable中,这是 CreateML 使用的一个表格表示。这个类实际上非常强大。您可以使用它在数据集中执行额外的预处理步骤,例如创建和删除列。

下一步,我们使用randomSplit(by:seed:)函数将数据集随机分成训练集和测试集。该函数需要一个随机操作的种子和一个比率来确定用于训练和测试的数据集的百分比。种子可以是我们想要的任何整数。这个比例通常在 70%到 90%之间。如果你用更多的数据进行训练,就不会有足够的数据来评估模型;如果您使用较少的数据进行训练,您将无法执行良好的训练,您的大型测试集将会显示这一点。这是一个权衡,80%通常是一个好的选择。

第 3 步是我们实际构建模型的地方。为此,我们初始化MLTextClassifier,传递训练集和对应于 JSON 文件的文本和标签列。请注意,这个过程可能需要很长时间,这取决于许多因素,如类的数量和训练集的大小。返回的对象是我们用来做预测的分类器。

下一步是评估我们的分类器的性能。我们通过将测试集传递给evaluation(on:)函数来做到这一点。它对测试集中的每个文本进行预测,并记录分类器是对还是错。然后,我们使用这些记录来计算一些指标,比如清单 3 中使用的准确性,给出了正确预测的百分比。CreateML 还提供了精度、召回和混淆矩阵,用于观察各个类的性能。评估是机器学习中一个非常广泛的话题,因为我们在这里仅仅触及了表面,我强烈推荐关于这个主题的进一步阅读。

评估过程是一个非常重要的步骤,因为它为如何改进模型提供了指导。例如,显示某个类的高错误率的混淆矩阵可能是一个警告,表明我们需要将该类的更多示例添加到训练集中。

最后,如果模型足够令人满意,我们就直接保存它。这是第 5 步,我们将.mlmodel输出到文件系统。

CoreML 集成

现在我们已经有了模型文件,我们可以将它拖放到 Xcode 项目中。这一次不再有仅限 macOS 的限制,因为 CoreML 和 NLF 可以在所有苹果平台上使用。

Xcode 自动生成一个与.mlmodel文件同名的MLModel子类。因此,可以在独立模式下使用该文件,但是我们实际上对 NLF 如何与它集成感兴趣,如清单 4 所示。

Listing 4: Integrating NLF with CoreML model.

该代码的主要部分位于tagger属性定义中,我们在前面部分创建的模型和一个定制标记方案之间建立了关联。这是必要的,这样标记者就知道何时调用我们的定制模型。

注意,我们不能直接使用 CoreML 模型。我们首先需要通过传递模型的 URL 将其包装在一个NLModel对象中。至于自定义标记方案,我们只需要提供一个标识符。并在标签上把我们称之为setModels(_:forTagScheme:)的人联系起来。

因此,当我们传递一个自定义方案来枚举标签时,标记器知道它应该使用自定义模型。该工作流程如图 2 所示。

Image 2: Text classification workflow.

最后,prediction(for:)使用tags(in:unit:scheme:options)来获得标签和范围元组的列表。因为我们正在评估整个文本,所以我们只为unit参数传递.document,导致 tagger 只返回一个标签,这个标签要么是POSITIVE要么是NEGATIVE

清单 5输出 1 描述了分类器的用法和结果。请注意,它正确地标记了大多数评论,除了最后一条,它预测POSITIVE甚至不是评论。由于机器学习的概率性质,这是意料之中的。为了解决这个问题,我们可以在我们的训练数据中增加非评论的例子,但是我会把这个留给读者。

Listing 5: Using the custom tagger.

Output 1: Print logs from Listing 5.

结论

在这篇博文中,我通过介绍 NaturalLanguage 框架的主要概念并展示如何构建一个定制的文本分类器,详细探讨了它。然而,还有更多需要了解的。在这个 Github 仓库中,你可以找到所有的文本分类器代码和许多其他使用内置函数的例子,组织数据集的不同方式,以及如何构建自定义单词标签(这与我在这里介绍的非常相似)。

我希望你喜欢这个,请离开你的掌声。再见!

[1]伯德、克莱因和洛珀(2009 年)。用 Python 进行自然语言处理:用自然语言工具包分析文本。奥莱利媒体公司。

[2]乔希特,H. (2017)。餐馆评论数据集。【在线】可在:https://www.kaggle.com/hj5992/restaurantreviews/version/1?[2019 年 4 月 14 日]

用 spaCy 和 Gensim 构建主题建模管道

原文:https://towardsdatascience.com/building-a-topic-modeling-pipeline-with-spacy-and-gensim-c5dc03ffc619?source=collection_archive---------1-----------------------

像大多数编程语言一样,Python 有大量优秀的库和模块可供选择。当然,总的来说,这绝对是个好主意,但这也意味着有时模块之间并不总是能很好地配合。在这个简短的教程中,我将向您展示如何将 spaCy 与 Gensim 连接起来,以创建一个连贯的主题建模管道。

是啊,太好了。什么是主题模型?

好问题。我想过重写维基百科的定义,然后我想我可能应该给你维基百科的定义:

在机器学习和自然语言处理中,主题模型是一种统计模型,用于发现文档集合中出现的抽象“主题”。

基本上,我们在寻找与讨论语料库的内容最相关的词或主题的集合。对于本教程,我们将使用潜在的狄利克雷分配(LDA)。

关于库的简短说明

对于那些不知道的人来说, Gensim 是主题建模的杰出库之一。同时, spaCy 是一个强大的自然语言处理库,在过去几年中赢得了许多崇拜者。

建造管道

首先,让我们导入我们的库:

这里需要注意一些事情:

  • 如果你是数据科学的新手,特别是 Python,注意你必须 pip 安装 Gensim 和 spaCy。
  • spaCy 有几种不同的型号可供选择。为了便于说明,我使用了大型的通用 web 模块,但是请使用对您有意义的内容。这需要通过命令行下载。查看模型页面点击了解更多信息。
  • 如果你以前没有使用过 tqdm,它是一个允许你创建进度条来跟踪你的代码处理时间的模块。
  • pprint 是为了让我们看的时候主题的格式更好一点。

我们将使用纽约时报头版文章的数据集。实际的文章本身在我们的数据框架的“内容”栏中。

现在是有趣的部分——我们将建立管道!默认空间管道的布局如下:

  • 记号赋予器:将全文分解成单独的记号。
  • 标记器:用词性来标记每个标记。
  • 解析器:解析成名词块等。
  • 命名实体识别器(NER) :标记命名实体,如美国

我们并不真的需要所有这些元素,因为我们最终不会传递 spaCy 的本机输出。文档由组成。令牌),因此标记的信息不会通过。无论如何,NER 将会对我们有用。现在我将把它们都留在这里,同样是为了说明的目的,尽管这会使管道的效率有所降低。

除了现成的功能之外,spaCy 还允许您使用自己的功能构建自定义管道,这就是我们将获得真正价值的地方。spaCy 有一个健壮的停用词列表和内置的 lemmatizer,但是我们需要将该功能添加到管道中。

对于那些不熟悉停用词的人来说,它们基本上是普通的词,不会真正为您的模型增加很多预测价值。例如,如果你不把单词“the”从你的语料库中删除,它可能会出现在你生成的每个主题中,考虑到“the”在英语中的使用频率。

请注意,没有一个词,即使是像“the”这样无处不在的词,会自动成为停用词。停用词通常由手头的特定任务决定。例如,我的任务是给《纽约时报》的头版文章做主题模型,我会用一些你在不同的上下文中不会想到的停用词。我可以将它们添加到 spaCy 的默认停用词列表中,如下所示:

我没有太疯狂地更新我的停用词,只是添加了一些精选的停用词,以向您展示我的思维过程:

  • 《纽约时报》通常在文章中使用敬语,所以我们会得到很多我们不需要的“先生”、“夫人”等。
  • 由于文章中会充满引用,我们也将“说”添加到我们的列表中。
  • 许多文章都有署名,因为大多数是政治性的,“华盛顿”经常出现。
  • 最后,spaCy 足够聪明,可以在词汇化时将一些缩写分解成它们的组成词(比如“不是”分解成“是”和“不是”),但“' s”却不是这样。所以那个也要拿走。
  • 另一个重要的警告是,我在这里没有做任何语法分析,这是当你在寻找文本中最常见的两个以上的单词短语时。在这种情况下,我可能会做出稍微不同的选择。

我们还需要对文本进行词汇化,也就是简单地将每个单词简化为它的词根。所以比如“going”和“goes”都简化为“go”。“更好”会被简化为“好”。

好了,现在让我们构建我们的管道,为词汇化和停用词移除添加函数:

这里重要的一点是要确保为最终输出返回 token.text。这将把每个令牌对象转换成一个字符串对象,因此它可以在 Gensim 中使用。

对于 Gensim,我最终需要创建一个列表列表:

下面是其中一篇文章在通过管道后的样子:

现在我们已经处理了文章,我们需要创建主题模型所需的输入:

最后,我们可以运行模型本身:

最后,一些示例主题(注意 pprint 以帮助格式化):

就是这样!希望对你有帮助!

用强化学习构建图灵机

原文:https://towardsdatascience.com/building-a-turing-machine-with-reinforcement-learning-9d06a4f0ce6?source=collection_archive---------13-----------------------

超越人工算法设计

Alan Turing. A name on which the beginning of an era is manifested. The Turing Award remembers each year on him, lastly to our »Fathers of Deep Learning«.

老实说,在计算机科学中,没有比另一种排序算法更无聊的东西了。但是,这个时间排序只是一个通用图灵机用高级强化学习框架自我学习的表示。

Thanks, Alec Baldwin!

事实上,想出一个高效的优化算法需要大量的时间、资源和努力。而且,你永远不知道是否还有更高效的版本。然而,直到 20 世纪 60 年代,在快速排序发明之前,排序被认为是一个需要花费大量时间的问题。更不用说优化矩阵乘法的有趣竞赛了,如下图所示…

Matrix multiplication is known to be in O(n^ω). Currently: ω=2.3728639.

这就是强化学习的用武之地:它不仅能够作为一个通用的启发式工具包,而且它还可以作为一种在优化领域中找出一种数学证明的新算法的方法。

在下文中,我将用排序的例子向您展示后者。然而,请随意应用这个框架来满足你自己的需要,也许你能够通过在多项式时间内解决一个 NP 完全问题来用矛盾证明P = NP-问题:我很乐意得到那 100 万美元价格的一部分。;)

我为什么要烦恼?

优化发生在任何行业。从本质上来说,没有人不能优化的地方。

然而,至关重要的是,在大多数情况下,给定所需的数据,我们不仅不知道一个合适的算法来优化某个问题,而且我们依赖于一个密集的思考过程,结合好的想法和长时间的试错过程。

将优化留给一个通用的框架,这样定义一个目标(比如一个最优策略)就足够了,这不是很好吗?对这种系统的要求将服从于:

  • 收敛到一个有效的广义解
  • 暴力的显著优势
  • 测试期间的时间效率。

通过利用强化学习——机器学习的最新进展——我们应该能够满足这些要求。如果我们在本文后面会谈到这一点。;)

好吧,很公平。有具体的用例吗?

当然可以。怎么样

  • 物流 : 先期运输,库存管理,高效的卡车运输甚至一个充分利用的 uber/cab/bus 驱动。
  • 架构 : 智能建筑如能源管理、电梯调度等。
  • 电子商务 : UX/I 优化,电影推荐,动态定价等。
  • 计算机科学 : 排序,神经组合优化(如旅行推销员),神经图灵机,数据中心冷却等。

深信不疑?现在关于技术…

为了便于说明,我选择学习一种排序算法,因为它很容易验证。然而,我的 GitHub 上提供的以下框架也可以用于其他优化技术:

[## moritzmoritz98/rlopt

通过强化学习实现任意优化问题的人类可读解决方案— moritzmoritz98/rlopt

github.com](https://github.com/moritzmoritz98/rlopt)

该算法

  1. 使用贝尔曼方程通过 Q 表学习任务。
  2. 在转换表中收集所有用于解决任务的状态。
  3. **通过
    • a 最终状态机(fsm)图(自动)、
    • a lambda 函数基于转换表(自动)或
    • a 简化的 fsm 和代码版本(手动)重构**学习到的算法,用于算法加速。

在学习阶段,我们的目标是学习解决排序目标的算法。为此,我选择了表格 Q-learning 算法作为强化学习的一个特性,因为它易于标记和解释。

对于那些不熟悉表格 Q-learning 的人来说,本质上我们跨越了行中的列和状态上的动作,其中每个单元格通过采取其动作来确定该状态下的预期未来回报。下图描述了学习过程(也称为贝尔曼备份)。

The Bellman equation: Q(s,a) = r + γ*max Q(s’, a’)

如果有进一步的兴趣,看一看我的要点关于表格 Q-learning 是如何工作的。

在强化学习中,表格 Q-Learning 动作状态奖励将被定义。因为我们的目标是一个通用的算法,我们必须特别注意如何选择它们。

本质上,我们正在设计一个多头(两个头: IJ )图灵机 M=⟨ Q,γ,s,b,f,δ ⟩处理其磁带上的一个待排序列表 L 。与 Alex Graves 的神经图灵机等现有技术相比,在这项工作中,我使用了一种更简单的方法,代价是可定制性更低,但可解释性更高。

Example Turing machine to compute the truncated subtraction (“monus”), after John E. Hopcroft and Jeffrey D. Ullman (1979). In our case, the finite control table corresponds to the learned Q-table applied on a numerical list on the tape.

Q —状态,324

这些状态必须与磁带无关,因此,我将它们称为 RL 相关变量。

  1. **L**[**I**] ≥ **L**[**J**]
  2. I 对比J:**I**<**J**|**I**==**J**|**I**>**J**∈{-1,0,1}****
  3. **I** == 0 |间| **I** == len(**L**) ∈ {-1,0,1}
  4. **J** == 0 | between | **J** == len(**L**) ∈ {-1,0,1}
  5. last_action ∈ {0,1,2,3,4,5}

具体来说,第一个状态是二进制的,而接下来的是三进制的,最后一个是六进制的,因此它们可以用一个布尔、三个三进制和一个六进制来表示,从而产生2¹×3³×6¹=324不同的状态。实际上是非常低维的,但仍然足以成功地学习一个排序算法。

γ-磁带字母符号:动作,6

在代码中我使用了以下字母:
终止
INCI
INCJ
RESETI
RESETJ
交换(值在 IJ )。

我们图灵机上基本上有两个头定位在 IJ 。通过增加其中一个或将其重置为零,机器能够在胶带内移动。交换是唯一的磁带修改动作,而终止模拟机器发出的暂停信号。有趣的是,希尔伯特的停顿问题也发生在我的一次实验中。

注意,你可以将磁带移动与实际程序分开,但是,出于实际考虑,我个人决定不采用这种形式主义。此外,为了确保暂停,'终止'动作也是一种变通方法。**

本质上,只有“SWAP”和“ NOOP ”应该是γ的一部分。

s ∈ Q —初始状态

i=0j=0;状态编码中的last_action = ‘*TERMINATE*’-1|-1|0|?|0

b∈γ—空白符号

然而,为了简单起见,没有包括空白符号,可以考虑添加一个' NOOP '操作。

f .⊆q——最终/接受国

这些是通过强化学习算法学习的。

δ:(q∖f)×γ→q×γ×{ i0,J0,I++,J++} —转移函数

通过算法了解到:来自 Q 的每个非最终状态与来自γ的动作配对具有确定性结果,该确定性结果包括通过采取来自γ的动作以及磁带移动(即 INCIINCJRESETIRESETJ 中的一个)而进入的下一个状态。

稍后,当我们讨论结果时,我将可视化这个转换表。

报酬

虽然不是图灵机的一部分,但奖励是通过强化学习来学习排序行为所需要的。为此,我决定让奖励函数尽可能稀疏和简单,以便为算法留出足够的自由空间:

  • 300:如果以排序状态终止
  • 10:如果交换正确

即使我没有指定禁止越界访问,程序自己也知道了这一点(一个无用的动作会减少可见的回报)。

还要注意,在这种情况下,相对于最终的表现,稀疏的奖励有它的机会,但是,它显著地增加了学习的时间,特别是在处理随机探索技术时。相反,通过应用直方图探索,每个状态根据其对算法的新颖性而被强制使用一个附加项。这种行为消除了奖励函数在开始阶段的稀疏性,并增加了达到全局最大值的机会。

在利用这种高级直方图探索技术之后,该算法能够在几个 1k 步骤中解决该列表,尽管如此,它仍然需要 5M 步骤来确定和优化其结果。

输入数据

关于输入数据的一个小注意:我生成了 3 到 6 个(870 个不同的)列表长度的所有可能排列,以减轻任何可以想到的过度匹配问题。

结果

恭喜你!你通过了艰难的技术部分。现在你想看到结果,对吗?

Looks brilliant, right?

好吧,你抓到我了。桌子看起来还不太好,对吧?

尽管如此,这里发生的事情还是值得一提。基本上,我在的 5,000,000 个步骤中学习了 (324,6) -Q 表,并通过预测列表(长度:3 到 6)的所有 870 个不同排列加上列表长度为 7 的 5,040 个排列来“测试”它,并在执行期间收集实际使用了哪些状态以及在这些状态下采取了什么样的动作。**

令人惊讶的是,没有很多唯一的状态(本质上是 20 ),而且,该算法确实很好地推广到了5040第七列表排列,没有任何错误。**

我真的想再次强调这一点:仅用 500 万步和所有无限可能列表的一个小子集的训练数据,结合一个简单的手工制作的奖励函数,我们就能够分别教会算法或宇宙图灵机如何排序。

你现在能想象一个人可以多么容易地把它推进到其他优化困境吗?

然而,我们仍然没有到达我们旅程的目的地,除非你能够从那些 20 行或最终从下面的 GraphViz 中阅读并主动理解我们学习的算法。

An astonishing transition graph, isn’t it?

完整的图灵机

在算法描述中,我省略了最终状态集和转移函数,因为它是由我们的 Q-learning 学习的。现在我们能够完全定义我们的图灵机了。

首先,最终的状态集很简单,它是状态16,如转换图所示。

第二,我们的转换函数变得有点棘手,因为它比上面的图表形式上正确的版本更具可读性。尽管不再是一个有效的转换函数,我真的很高兴把它缩短为 4 种不同的情况,其中一个问号代表不相关。

Initial values: j = 0 and i = 1.

最后,从简化的转换表来看,实际上静态编码是相当直观的:我们这篇文章的主要目标**

**j **<-** *0*
i **<-** *1*
**while** action **is not** *TERMINATE*:
    **if** i **==** *len*(l):
        action **<-** *TERMINATE*
    **elif** l[i] **>** l[j]:
        movement **<-** *INCJ*
    **elif** i **>** j:
        action **<-** SWAP
        movement **<-** *INCJ*
    **else**:
        movement **<-** *RESETJ*
        movement **<-** *INCI***

老实说,直到现在我都不知道强化学习在这种情况下是否真的能够找到一种新的排序算法,或者只是一种非常不受欢迎的算法。我唯一可以肯定的是,它实际上可以 100%正确地对任意列表进行排序!

也就是说,我真的很感激,如果你能留下一个关于你看起来像什么样的算法的笔记!

性能赋值

由于行动空间有限,学习比 O(n ) 更好的东西从定义上来说是不可能的。然而,可以获得下面的算法复杂性。

非简化/原始版本 w.r.t .所需步骤数:

  • 最小 : O(0.5n² + 1.5n + 1)
  • 最大值 : O(n² + n + 1)

然而在简化版上,它更快并且(令人惊讶地)稳定:

  • 最小值 / 最大值 : O(0.5n² + 0.5n)

注意,通过从实际的与算法相关的动作字母表(即交换终止无操作)中分离带移动(即 I0J0I++J++ ),可以提高算法性能,因为对于一个动作,大多数时候还需要带上的移动,反之亦然然而,我将把它留给以后的工作。****

RLSort vs. BubbleSort

不幸的是,我的 RLSort 算法不如冒泡排序算法快,这可能是由于开销以及 for 循环在 CPU 上比 while 循环在中止条件下优化得更好。此外,较低的性能界限是与快速的 O(n) 相比没有竞争力的O(n)——验证检查:他对一个已经排序的列表做了很多无意义的事情!**

自己试试吧!

我真的很想鼓励你尝试一下,并把这个框架应用到你自己的想法中。如果您正在优化类似于列表的东西,只需要三个简单的步骤和几分钟的 CPU 时间:

  1. 查看我的回购。
  2. 根据您的需求调整奖励功能。
  3. 耐心等待,直到算法找到你(希望)的广义解。

你不仅会得到你的训练数据的优化列表的结果,你还会得到一个重构的现成可用的 lambda 函数!

当然,请让我知道你的用例!我真的很有兴趣听听你和我的框架的独特故事。😃

修正案

下面你会发现一个调用 RLSort sorting [6 4 3 7 0]的例子,详细说明了学习排序算法是如何工作的:谜语玩得开心!

 ***2* *<<-* [6 4 3 7 0], **i**=*0*, **j**=*0*: *INCI*.
 *64* *<<-* [6 4 3 7 0], **i**=*1*, **j**=*0*: *SWAP*.
*281* *<<-* [4 6 3 7 0], **i**=*1*, **j**=*0*: *INCJ*.
*134* *<<-* [4 6 3 7 0], **i**=*1*, **j**=*1*: *RESETJ*.
*227* *<<-* [4 6 3 7 0], **i**=*1*, **j**=*0*: *INCI*.
 *64* *<<-* [4 6 3 7 0], **i**=*2*, **j**=*0*: *SWAP*.
*281* <<- [3 6 4 7 0], **i**=*2*, **j**=*0*: *INCJ*.
*136* *<<-* [3 6 4 7 0], **i**=*2*, **j**=*1*: *SWAP*.
*299* *<<-* [3 4 6 7 0], **i**=*2*, **j**=*1*: *INCJ*.
*134* *<<-* [3 4 6 7 0], **i**=*2*, **j**=*2*: *RESETJ*.
*227* *<<-* [3 4 6 7 0], **i**=*2*, **j**=*0*: *INCI*.
 *65* *<<-* [3 4 6 7 0], **i**=*3*, **j**=*0*: *INCJ*.
*137* *<<-* [3 4 6 7 0], **i**=*3*, **j**=*1*: *INCJ*.
*137* *<<-* [3 4 6 7 0], **i**=*3*, **j**=*2*: *INCJ*.
*134* *<<-* [3 4 6 7 0], **i**=*3*, **j**=*3*: *RESETJ*.
*227* *<<-* [3 4 6 7 0], **i**=*3*, **j**=*0*: *INCI*.
 *64* *<<-* [3 4 6 7 0], **i**=*4*, **j**=*0*: *SWAP*.
*281* *<<-* [0 4 6 7 3], **i**=*4*, **j**=*0*: *INCJ*.
*136* *<<-* [0 4 6 7 3], **i**=*4*, **j**=*1*: *SWAP*.
*299* *<<-* [0 3 6 7 4], **i**=*4*, **j**=*1*: *INCJ*.
*136* *<<-* [0 3 6 7 4], **i**=*4*, **j**=*2*: *SWAP*.
*299* *<<-* [0 3 4 7 6], **i**=*4*, **j**=*2*: *INCJ*.
*136* *<<-* [0 3 4 7 6], **i**=*4*, **j**=*3*: *SWAP*.
*299* *<<-* [0 3 4 6 7], **i**=*4*, **j**=*3*: *INCJ*.
*134* *<<-* [0 3 4 6 7], **i**=*4*, **j**=*4*: *RESETJ*.
*227* *<<-* [0 3 4 6 7], **i**=*4*, **j**=*0*: *INCI*.
 *70* *<<-* [0 3 4 6 7], **i**=*5*, **j**=*0*: *RESETJ*.
*232* *<<-* [0 3 4 6 7], **i**=*5*, **j**=*0*: *TERMINATE*.
Sorted list in *28* steps: **[0 3 4 6 7]****

利用计算机视觉构建视频搜索引擎

原文:https://towardsdatascience.com/building-a-video-search-engine-b93305126b59?source=collection_archive---------22-----------------------

使用萨尔萨舞视频的案例研究

Source

介绍

在图像分类任务取得前所未有的进展之后,计算机视觉领域的自然进展是朝着视频和视频理解的方向发展,尤其是它如何与识别人类对象和活动相关联。正在这一领域建立一些数据集和基准。

与此同时,与 2D 图像相关的计算机视觉任务正在取得进一步进展,如细粒度分类、图像分割、3D 图像构建、机器人视觉、场景流量估计和 人体姿态估计

作为我在 Metis bootcamp 的最终数据科学项目的一部分,我决定将这两个平行的领域结合起来——具体来说是视频和人体姿势估计——以创建一个基于内容的视频搜索引擎。由于将 2D 人体姿势估计应用于视频搜索是一个“没有概念证明”的新颖想法,所以我通过选择 Salsa 舞蹈视频的单个表演者、固定位置单个摄像机视频片段来简化我的方法。

录像

仅次于谷歌的第二大搜索引擎 YouTube 的用户每天观看超过 10 亿小时的视频。世界上最受欢迎的社交网站脸书的用户每天观看大约 1 亿小时的视频!!这些平台对向用户提供搜索和发现感兴趣的相关内容的工具感兴趣。

这些平台提供的搜索工具主要使用视频的元数据(位置、时间、内容创建者等。)、标题、描述、抄本(用户创建的或机器从音频生成的)、用户评级、用户评论等。检索“相似”的结果。这些搜索工具不会浏览视频本身的实际内容。对于搜索来说,视频是不可浏览或可索引的。

视频中的视觉特征太多了,索引它们的计算成本很高,检索起来又太慢。举个例子,如果你要搜索展览 1 中萨尔萨舞者的舞步,目前在 YoutTube 这样的平台上还没有可供用户使用的工具。salsa 步骤没有文本上下文来执行搜索。

Exhibit 1: Salsa Dance Video (Credit: World Salsa Summit 2016. Dancer: Valentino Sinatra, Italy)

作为我的 Metis 项目 5 的一部分,我构建的视频搜索引擎已经索引了来自 YouTube 的大约 70 分钟的萨尔萨舞视频,并将返回一个匹配,如下面的图表所示。Yeifren(中)和 Adriano(右)表演的萨尔萨舞步与 Valentino(左)相似。在这些片段中,表演者走回到舞台中央,并面向舞台左侧进行多次转身。

Exhibit 2: A search results from the Video Search Engine (Credit: World Salsa Summit 2016. Dancers: Valentino Sinatra, Italy; Yeifren Mata, Venezuela; Adriano Leropoli, Montreal)

Valentino(左)和 Yiefren(中)之间的一个非常微妙的动作是,他们都触摸/撞击他们的膝盖/腿!!它在剪辑中的时间是关闭的,但模型拿起这个!!这些片段我看了好多天都没注意到!我认为这是一个迷人的结果。

方法学

建立视频搜索引擎有两个步骤。第一步是下载和处理视频素材,并通过 OpenPose 进行特征提取。OpenPose 是卡内基梅隆大学研究人员(曹哲等人)开发的一种人体姿态估计算法。更多关于 OpenPose 的内容将在后面的章节中介绍。第二步是模型构建和测试查询指标。使用矩阵分解方法(PCA,LSA,NMF)建立模型。欧几里德距离和余弦相似性作为查询度量被测试。作为第二步的一部分,还进行了特征工程/特征表示练习,测试了每一帧的展平特征和特征的 Hu 矩。

所获得的“特征表示/维数减少/查询度量”的最佳组合是通过使用具有 LSA 和余弦相似性的简单平坦姿态估计。

Exhibit 3: Methodology for building a video search engine

数据

这个项目的数据来自处理 YouTube 萨尔萨视频(链接如下)。

为什么是莎莎?

除了是一种具有挑战性的舞蹈形式,涉及微妙和快速的动作,YouTube 上的许多频道还提供了独舞者的视频。对于多人视频帧,用于我的项目的特征提取的 Zhe Cao 等人的姿态估计是快速和准确的,但是那些算法不能逐帧跟踪一个人。当视频中的人交换位置时,这就产生了一个问题——比如一对夫妇在做转体/托举等动作。为了避免这种情况并简化我的问题,我选择了独舞视频。

我考虑过的另一种舞蹈形式是芭蕾。然而,女舞者的服装(读芭蕾短裙!)会导致假阳性,如在芭蕾中常见的某些舞蹈姿势的膝盖。

我收集的萨尔萨舞片段来自以下四个总时长为 160 分钟的视频。这个镜头被处理到 70 分钟,并被格式化成 3 秒钟的剪辑。处理后的视频由大约 30 名艺术家的大约 58 场表演组成。

这些视频是:

  • 【https://www.youtube.com/watch?v=4nHElVbT3HY
  • https://www.youtube.com/watch?v=ITNiqNcl6Mw
  • https://www.youtube.com/watch?v=L5mqL7ADEsY
  • https://www.youtube.com/watch?v=M_rPhEjym1o

总而言之,所用的视频包括:

  1. 单身舞者
  2. 固定位置的单个摄像机的镜头(没有移动的摄像机!).只有摄像机角度和缩放级别会反映在素材中。
  3. 视频帧速率从原来的 24 帧/秒降低到 8 帧/秒。
  4. 固定屏幕分辨率为 640(宽)X 360(高)像素。
  5. 大约 70 分钟的经过处理的镜头,并格式化为 3 秒钟的剪辑(超过 1,400 次观察)。

OpenPose:实时 2D 多人姿态估计算法

人体姿态估计是识别图像或视频中人体解剖“关键点”的计算机视觉问题。在过去的几年里,许多研究人员取得了令人难以置信的进展,并展示了实时性能⁴.卡内基梅隆大学机器人研究所的曹哲等人使用“自下而上”的方法赢得了首届 COCO 2016 关键点挑战。他们使用安装了 GPU 的笔记本电脑实现了实时速度。

姿态估计用于对图像/视频剪辑中的人类活动进行分类。它们主要应用于 2D 图像,但从未用于视频搜索目标。下面是 OpenPose 如何在 2D 图像中识别一个人的 18 个关键点的示例(为了简单起见,只显示了 14 个关键点)。

Exhibit 4: OpenPose on a 2D Image Identifying Keypoints

当识别出视频中每一帧的关键点时,结果将如下面的展示 5 & 6 所示。

Exhibit 5: OpenPose on a Salsa Dance Video Identifying Keypoints

Exhibit 6: Spatio-temporal features extracted from OpenPose

结果

下面显示了索引 salsa 视频并使用余弦相似度进行搜索的一些结果。右上方的视频是搜索片段,搜索结果要从左到右读,然后从上到下。

请注意,搜索引擎索引在检索舞者正走向他们的位置的视频剪辑时是充分有效的(展示 7 )。这本身就是一个有趣的结果,并展示了这种索引方法如何用于视频编辑。即使对于 2.5 小时的“小的”未处理的镜头,手动编辑这些剪辑也将花费数小时,而搜索引擎运行(后期索引)不到一秒钟,并且能够找到镜头中所有这种剪辑的时间戳。这使得视频编辑更加自动化和快速。

Exhibit 7: Search using a Clip of Dancers Making an Entry

更多的结果

Exhibit 8: Dancers Performing Turns

Exhibit 9: Turn while bearing to the left (& swoop down!)

Exhibit 10: Moving to the left and a turn

其他应用

像这样的视频搜索引擎有很多应用。下面列出了一些例子:

Exhibit 8: Other Applications for Video Search Engine

对于像 YouTube 和脸书这样的平台如何在他们当前的网站上使用视频搜索,我有更多的想法。一些概念性的想法如下所示:

Exhibit 9: A Conceptual Layout of Video Search on YouTube Salsa Videos

Exhibit 10: A Conceptual Layout of Video Search on YouTube Football Game Videos

参考

  1. 南 Abu-El-Haija 等人 YouTube-8M:大规模视频分类基准。 arXiv:1609.08675be ,2016
  2. 曹哲等人,利用局部亲和场的实时多人 2D 姿态估计(2017) ,CVPR
  3. 曹哲等, Github to OpenPose (2017)
  4. 德里克·姆维蒂,2019 年人体姿势估计指南 (2019),中等文章

最初发布于https://mlbhanuyerra . github . io

利用深度学习构建声音情感传感器

原文:https://towardsdatascience.com/building-a-vocal-emotion-sensor-with-deep-learning-bedd3de8a4a9?source=collection_archive---------14-----------------------

教机器更好地理解人类交流

人类的表达是多方面的,复杂的。例如,说话者不仅通过语言交流,还通过节奏、语调、面部表情和肢体语言交流。这就是为什么我们更喜欢面对面而不是电话会议来举行商务会议,也是为什么电话会议比电子邮件或短信更受欢迎。我们离得越近,通信带宽就越多。

语音识别软件近年来有了很大的进步。这项技术现在在识别语音并将这些语音拼凑起来以再现口语单词和句子方面做得非常好。然而,简单地将语音翻译成文本并不能完全概括说话者的信息。除了面部表情和肢体语言,与音频相比,文本在捕捉情感意图方面的能力非常有限。

最初,我选择构建一个声音情感传感器,因为这似乎是一个有趣的工作项目。尽管对这个问题想得更多,我意识到通过音频进行情感感应有一些真正有趣的应用。想象一下,如果你的智能家居设备可以播放符合你情绪的歌曲,例如当你悲伤时播放振奋人心的歌曲。客户服务部门可以使用情绪检测来培训员工,或者测量客户在服务电话过程中是否变得更开心。

数据

我用来构建我的情感分类器的数据集是拉夫德斯、苔丝和 SAVEE ,它们都是免费向公众开放的(SAVEE 需要一个非常简单的注册)。这些数据集包含七个常见类别的音频文件:中性、快乐、悲伤、愤怒、恐惧、厌恶和惊讶。我总共获得了 30 名男女演员制作的 4500 个带标签的音频文件中超过 160 分钟的音频。这些文件通常由男演员或女演员说的带有特定情感意图的简短短语组成。

Actors for the SAVEE dataset

特征抽出

接下来,我必须找到可以从音频中提取的有用特征。最初我想用短时傅立叶变换来提取频率信息。然而,对该主题的一些研究表明,当涉及到语音识别应用时,傅立叶变换是相当有缺陷的。原因是尽管傅立叶变换是声音极好的物理表示,但它并不表示人类如何感知声音。

Raw audio waveform. In this form it is useless for classification.

从音频中提取特征的更好方法是使用 Mel 频率倒谱系数,简称 MFCCs。这里提供了一个很好的解释,说明 MFCCs 是如何从音频中获得的。MFCCs 试图以更符合人类感知的方式来表示音频。

为了从音频中导出 MFCCs,需要决定使用多少频率仓以及分段的时间步长有多宽。这些决定决定了输出 MFCC 数据的粒度。语音识别应用的标准实践是应用 20Hz-20k Hz 之间的 26 个频率仓,并且仅使用前 13 个用于分类。大多数有用的信息都在较低的频率范围内,包含较高的频率范围通常会导致性能下降。对于时间步长,10 到 100 毫秒之间的值是常见的。我选择用 25 毫秒。

Mel filter banks used to bin audio frequency content.

导出的 MFCCs 可以绘制在热图上,并用于可视化音频。这样做并不能揭示情感类别之间的任何明显差异。这与其说是因为缺乏模式,不如说是因为人类没有受过训练,无法从视觉上识别这些微妙的情感差异。然而,从这些热图中很容易看出男性和女性说话者之间的差异。

Visualized MFCCs for Happy Male and Female Speakers. Women tend to have stronger high frequency components in their voices, as shown by the brighter colors towards the top of the heatmap.

训练卷积神经网络

通过推导 MFCCs,音频分类问题实质上被转化为图像识别问题。因此,在图像识别领域非常有效的工具、算法和技术在音频分类中也非常有效。为了解决情感分类问题,我选择使用卷积神经网络(CNN ),因为这些网络已经被证明在图像和音频识别方面都是有效的。

在训练 CNN 之前,我将数据集中的文件随机分配给 80/20 分割的训练集或测试集。然后,我对训练文件执行了一些预处理步骤。每个文件的流程如下:

  1. 切断所有的沉默。
  2. 随机选择若干个 0.4s 窗口。
  3. 确定每个窗口的 MFCCs,产生 13×16 阵列。
  4. 将 MFCCs 调整到 0 到 1 的范围。(这一步超级重要!它使模型无法适应录音的音量水平。)
  5. 将每个窗口与源文件的情感标签相关联。

完成预处理后,我生成了 75,000 个标记为 0.4s 的窗口用于训练,每个窗口由一个 13x16 的数组表示。然后我用这些数据训练了我的 CNN 25 个时代。

模型检验

为了对测试集上的模型进行基准测试,我应用了一个类似于用来创建训练数据的流程工作流。测试集中每个文件的流程是:

  1. 切断所有的沉默。
  2. 创建步长为 0.1s 的“滑动”0.4s 窗口。(例如,第一窗口的范围从 0.0s 到 0.4s,第二窗口的范围从 0.1s 到 0.5s,等等。)
  3. 确定每个窗口的 MFCCs,范围从 0 到 1。
  4. 对每个窗口进行分类并返回 softmax 输出。
  5. 聚合每个窗口的预测。
  6. 最终预测是聚合后的最大类。

将这个过程应用于测试集中的所有 889 个文件,产生了 83%的总体准确率。我非常怀疑自己能否以接近 83%的准确率给这些文件贴上标签。下面的条形图显示了每种特定情绪的准确度。

外卖食品

这篇博文可能会让构建、训练和测试模型看起来简单明了。我可以向你保证,事实并非如此。在达到 83%的准确率之前,有许多版本的模型表现相当差。在一次迭代中,我没有正确地缩放我的输入,这导致了测试集中的几乎每个文件都被预测为“令人惊讶”。那么我从这次经历中学到了什么呢?

首先,这个项目很好地展示了简单地收集更多的数据如何能够极大地改善结果。我第一次成功的模型迭代只使用了 RAVDESS 数据集,大约 1400 个音频文件。仅用这个数据集我能达到的最高准确率是 67%。为了达到 83%的准确率,我所做的就是将数据集的大小增加到 4500 个文件。

其次,我了解到对于音频分类来说,数据预处理是至关重要的。原始音频,甚至短时傅立叶变换,几乎完全没用。我从惨痛的教训中认识到,适当的缩放可以成就一个模型,也可以毁掉它。无法消除沉默是另一个简单的陷阱。一旦音频被适当地转化为信息特征,建立和训练深度学习模型就相对容易了。

总而言之,为语音情感检测建立一个分类模型是一次富有挑战性但却值得的经历。在不久的将来,我可能会再次访问这个项目,以扩大它。我想做的一些事情包括:针对更广泛的输入测试模型,使模型适应更广泛的情绪,并将模型部署到云上进行实时情绪检测。

附录

Github 、 LinkedIn 、个人博客

无需编码,在 5 分钟内构建一个 WhatsApp 聊天机器人

原文:https://towardsdatascience.com/building-a-whatsapp-chatbot-in-5-minutes-without-coding-f53fab8bfb96?source=collection_archive---------6-----------------------

聊天机器人正在兴起。已经有很多或多或少有用的聊天机器人,其中大多数基于 Facebook Messenger。然而,如果你居住在欧洲、印度、南美或非洲,很可能你正在使用 WhatsApp 作为你的首选沟通渠道( stats )。很好,那么对于 WhatsApp 上的聊天机器人来说,什么是有用的案例呢?由于 WhatsApp 目前专注于客户服务,正如他们在指南中所描述的那样,让我们构建一个机器人来帮助预先确认服务查询。

前提条件是什么?

通过 WhatsApp 商业应用编程接口商业使用 WhatsApp 的先决条件是要么直接从 WhatsApp 申请一个自己的账户,这目前极难获得,要么从官方解决方案提供商那里购买访问权限。我可以推荐第二个选项,因为从技术角度来看设置整个项目是很有挑战性的。顾名思义,它只是一个没有图形用户界面的 API。

当你有了这样一个商业账户,你可以给它添加一个电话号码,开始发送和接收信息。WhatsApp 的一个商业简介是这样的(德语):

建造机器人

在这个小展示中,我正在使用 MessengerPeople 的平台来构建聊天机器人。MessengerPeople 是 WhatsApp 官方解决方案提供商之一,为人工代理引导的聊天提供票证系统,并为自动化对话提供图形聊天机器人构建器界面。

简单说明一下:我们的目标并不是构建一个超级智能的数字助理,它可以回答你能想到的每一个问题!我们的目标是构建一个小助手,它可以服务于一个清晰和狭窄定义的用例。这就是为什么我们必须让机器人来主导对话。机器人总是需要给出对话如何进行的提示。最后但同样重要的是,让我提一下,条条大路通罗马。这只是一个功能非常有限的小例子。

好了,理论够了!让我们开始建设吧!MP 的聊天工具界面非常简单和直观。在左侧,你添加所谓的用户问题,这些是机器人应该做出反应的关键词和短语。在右边,你添加相应的机器人答案。

让我们以欢迎词和介绍开始。我们正在添加一些关键字,如你好,嗨,你好,作为用户问题。您还可以通过添加拼写错误的单词和短语来处理错别字。然后我们让机器人介绍自己,并立即给出选项供选择。WhatsApp 提供的格式选项非常有限。我们可以用两颗星把一个单词括起来,使它变得粗体。当然,我们也可以添加表情符号。

该机器人给用户 3 个选项:产品,新闻和与代理聊天。我们还增加了简化回复过程和优化用户体验的捷径。所以,现在我们可以将相应的对话添加到这三个选项中。先说产品信息。除了快捷方式“1”,我们还添加了关键字“产品”和“产品”。当然,你可以在这里添加任意多的同义词或短语。

然后我们以同样的方式为新闻部分添加第二个对话,快捷键为“2”。我们也可以在机器人回答中放一个链接到一个有更多细节的网页。一个链接缩短器将自动缩短所有链接,并使跟踪点击成为可能。请注意,你不能在 WhatsApp 中使用 HTML,所以你不能添加和样式,例如一个标签。唯一的办法就是加一个纯 URL。

对于快捷键为“3”的第三个对话,我们在机器人回答中增加了一个特殊动作“移交给代理”。这里发生了什么?到目前为止,整个对话是完全自动化的。此时,聊天机器人停止工作,并为人工代理打开一张票。我们称之为混合机器人。然后,代理可以拿起票据并手动回复客户。还可以定义自定义技能,如“销售”、“支持”、“人力资源”等。因此,机器人可以自动将请求路由到正确的部门。

我们的整个配置如下所示:

现在是考验我们小伙伴的时候了!我使用 WhatsApp Web 来开发和测试聊天机器人,因为这是一种在浏览器中使用 WhatsApp 的便捷方式。

很好,不是吗?最后,让我们看看这个请求到达的后端票务系统。这是代理视图:

当然,聊天机器人的功能远不止这种简单的问答游戏。例如,它可以收集数据并存储到用户配置文件中。它可以通过 API 从外部数据源获取信息。它可以处理依赖性并做出相应的响应,例如,“这个用户是否已经提供了他的名字,然后用他的名字称呼他”或“用户用 A 回答了问题 1,所以继续问题 4”。它还可以以定义的延迟发送多条消息,当然,它还可以发送图像、视频、音频和 pdf 等媒体。

用 WiGLE 和 R 建立一个你周围网络的 WiFi 点地图

原文:https://towardsdatascience.com/building-a-wifi-spots-map-of-networks-around-you-with-wigle-and-r-d01a73b1172d?source=collection_archive---------25-----------------------

Map generated using the below code

探索我们周围的世界总是很有趣——当你以数据科学家的 视角 探索世界时,那就更有趣了。

在此分析中,我们将确定我们周围开放的 WiFi 网络,并将它们映射到交互式地图上。

工具包

  • wig lrR 包,用于与 WiGLE API 接口
  • 用于数据收集的 WiGLE API
  • tidy verseR 包用于数据分析&操纵**
  • 传单 R 用于绘制互动地图的包(界面为 fleet . js)

wiglr 是由 Bob Rudis 开发的 R 包,他的名字是 hrbrmstr 。他是 R 的超级用户之一,同时也积极参与开发新的 R 包并写博客。

先决条件

在我们开始编码之前(不管怎样,只是一堆代码),我们需要做两件重要的事情。

安装所有需要的 R 包

wiglr在 CRAN 上尚不可用,因此必须根据您的喜好从 Github/Gitlab 安装。下面是代码,你如何使用devtools包从 Github 安装它

**# install.packages('devtools') # install devtools if not available
devtools::install_github(“hrbrmstr/wiglr”)**

上面提到的其他实用程序包:

**install.packages('tidyverse') # ideally dplyr is what we'd need
install.packages('leaflet')** 

正在获取 WiGLE API 令牌

  • 在 wigle.net创建您的账户
  • 登录后,从你的账户页面获取你的 API 令牌(点击显示令牌)
  • 打开 RStudio,将您的( "Encoded for use" ) API 令牌设置为环境变量,如下所示:
**Sys.setenv(WIGLE_API_KEY = "here_goes_my_encoded_token")**

至此,我们已经具备了先决条件,可以开始了!

起飞

加载库

和其他 R 项目一样,第一步是加载所有需要的库。

**library(wiglr)
library(tidyverse)
library(leaflet)**

我们周围景点的一般统计数据

在这一步,我们将给出边界框(以纬度和经度的形式)并提取我们周围的 WiFi 网络的细节。

**# This is for Bengaluru, India - change it according to where you're
wifi_box <- wigle_bbox_search(12.7585, 13.1105, 77.5477, 77.8431)** 

样本输出

**> wifi_box
$success
[1] TRUE$totalResults
[1] 252098$search_after
[1] 552772$first
[1] 1$last
[1] 100$resultCount
[1] 100$results
# A tibble: 100 x 25
   trilat trilong ssid    qos transid channel firsttime lasttime lastupdt housenumber
 *  <dbl>   <dbl> <chr> <int> <chr>     <int> <chr>     <chr>    <chr>    <chr>      
 1   12.9    77.6 oakd…     0 201701…       6 2017-01-… 2017-01… 2017-01… 305, 1st M…
 2   12.9    77.6 Sham…     0 201606…      11 2001-01-… 2016-06… 2016-06… ""         
 3   13.0    77.7 PPAN…     0 201303…       1 2013-03-… 2013-03… 2013-03… ""**

它只是一个列表(JSON 响应的输出),包含实际结果和一些元数据。正如您在上面的示例输出中看到的,我们的关注点是results

巡航

说完了,就该画我们承诺要画的交互图了(本文开头)。

下面是我们从wifi_box中取出results并创建一个新变量labs的代码(它将在弹出标签的后续行中使用)。这些数据被进一步输入leaflet()进行地图创建,我们添加一个基础图层,然后在给定的经度和纬度添加圆形标记。

颜色编码是为了让我们表示 WiFi 网络的安全性是(无安全性)—红色、未知— 黄色和其他(如 WEP、WPA) —蓝色。

**wifi_box$results %>% 
  mutate(labs = sprintf("SSID: <code>%s</code><br/>Encryption: %s", ssid, encryption)) %>% 
  leaflet() %>% 
  #addTiles() %>% 
  addProviderTiles(providers$Stamen.Toner) %>% 
  #addProviderTiles(providers$OpenStreetMap, options = providerTileOptions(opacity = 0.4)) %>% 
  addCircleMarkers(~trilong, ~trilat, 
                   radius = 10, 
                   popup = ~labs, 
                   color = ifelse(
                     wifi_box$results$encryption == "none","red",
                                  ifelse(
                                    wifi_box$results$encryption == "unknown",
                                    "yellow","blue")))**

这给了我们这个漂亮的情节(当你在 RStudio 中绘图时,它实际上是交互式的)

你可以使用不同的names(providers)值来选择你喜欢的基层/地图主题。

安全着陆

这样,我们就成功地绘制了我们周围的 WiFi 网络——以及它的 SSID 和安全加密。这在检测和通知可能被恶意方攻击的没有安全性的开放网络时非常有用。

参考

  • 在开发中:使用 wiglr 进入热点
  • 我的 github 上使用的完整代码
  • 威格尔官方网站

结束语: 如果您对开源感兴趣,请 贡献此包 以成功部署在 CRAN 上,使其能够面向大众

使用 AWS Lambda 和 API Gateway 构建葡萄酒推荐 API

原文:https://towardsdatascience.com/building-a-wine-recommender-api-using-aws-lambda-and-api-gateway-2fffe17c543a?source=collection_archive---------31-----------------------

在 RoboSomm 系列的一个章节中,我们构建了一个葡萄酒推荐模式 l。在下面的文章中,我们将探索如何将它转化为一个 API,该 API 可以从 180,000 种葡萄酒的存储库中返回葡萄酒推荐。我们将关注一个特定的用例:返回给定描述符列表的葡萄酒推荐列表。

事实上,让我们说,我的心情是一个大胆的红葡萄酒与黑莓,蓝莓,巧克力和甘草的香味。我希望这款酒单宁含量高,酒体饱满。现在告诉我:什么葡萄酒最符合这个描述?

我们将使用 AWS 服务套件来确保返回葡萄酒推荐的过程完全是无服务器的。

勾画出解决方案

构建 API 的第一步是勾画出提交带有葡萄酒描述符的 POST 请求和返回葡萄酒建议列表之间所需的所有步骤。

虽然在本文中我们将重点关注 Lambda 和 API Gateway 的使用,但我们也将利用 S3 和 SageMaker 中的一个模型端点。概括地说,该流程如下所示:

首先,让我们把注意力集中在 Lambda 函数上,我们将使用它来运行上面概述的过程。当设置 Lambda 函数时,我们需要小心选择一个执行角色,它将允许我们与 S3 和 SageMaker 进行交互。我们需要添加到 IAM 角色的 JSON 如下:

{
 “Sid”: “VisualEditor1”,
 “Effect”: “Allow”,
 “Action”: “sagemaker:InvokeEndpoint”,
 “Resource”: “*”
 },
 {
 “Sid”: “VisualEditor2”,
 “Effect”: “Allow”,
 “Action”: “s3:*”,
 “Resource”: “*”
 },

现在来看函数本身。因为我们将从函数内部的查找表中检索值,所以我们希望使用 Pandas 库。我们还将处理单词嵌入形式的向量,为此我们希望使用 Numpy。不幸的是,这些库不是 AWS Lambda 的原生库。为了利用这个功能,我们必须创建一个包含 Lambda 函数以及 Pandas 和 Numpy 的 Linux 二进制文件的 zip 文件。本文更详细地概述了如何做到这一点。

Lambda 函数的第一部分如下所示:

除了导入必要的包,我们还定义了 lambda_handler 函数。这是每当调用 Lambda 函数时运行的函数。它处理的事件是 json 文件,其中包含我们的示例葡萄酒描述符列表:

{ "实例":["大胆"、"黑莓"、"蓝莓"、"巧克力"、"甘草"、"高单宁"、"醇厚"]}

为了安全起见,这个 json 文件被解码和编码,以确保它具有适当的(json)格式。

接下来,我们希望从 S3 的查找表中检索这些描述符的 IDF 加权单词嵌入。该查找表(S3 的 CSV 文件)的生成过程在本文的中有详细描述。与本文中描述的过程的唯一区别是,我们将嵌入到查找文件中的每个单词乘以它的逆文档频率(IDF)分数。我们希望 Lambda 函数的这一部分获取以下单词嵌入,并将它们存储为一个列表。

代码如下:

将单词嵌入转换成单个葡萄酒嵌入的过程相当简单。通过取它们的平均值,我们最终得到一个单一的词频率逆文档频率(TF-IDF)加权向量。

现在我们已经嵌入了我们的葡萄酒,我们准备检索我们的推荐。为此,我们将调用一个 AWS SageMaker 端点,该端点托管一个 Scikit-Learn Nearest Neighbors 模型,该模型专门为此进行了培训。本文更详细地解释了这样做的过程。

我们的最近邻模型端点返回我们的葡萄酒推荐的索引,以及这些推荐和我们的输入葡萄酒嵌入之间的余弦距离。最终,我们希望我们的 Lambda 函数返回这些葡萄酒的名称和风味特征。这些附加信息存储在 S3 的 CSV 文件中,需要检索:

现在我们已经设置了 Lambda 函数,我们可以将注意力转向创建 API 了。这里详细描述了建立我们的 API 的步骤。我们创建了一个 POST 方法,它将接受一个带有我们的 wine 描述符的 JSON 文件,调用我们的 Lambda 函数,并返回一个推荐列表。

我们可以通过使用 Postman 运行测试来确保我们的 API 正常工作。

推荐的葡萄酒似乎主要包括坦普拉尼洛和赤霞珠。该去购物了!

使用熊猫构建远期汇率协议的零曲线

原文:https://towardsdatascience.com/building-a-zero-curve-with-forward-rate-agreements-using-pandas-1c86561c6d3c?source=collection_archive---------13-----------------------

Photo by Markus Spiske on Unsplash

在金融界,如果你想给一种工具定价,并计算出从 t0(现在)到 t(n)的未来价值,你需要使用现货收益率曲线。在专业交易者中,现货收益率曲线被称为零曲线。

如果你现在有 1000 美元可以投资,你可以去银行存一张一年期的存单,很容易得到即期汇率。CD 利率是你的基准。如果你投资了回报比 CD 低的东西(假设风险相对相同),你知道你最好把钱放在 CD 里。这很容易。

但是,如果你知道从现在起一年后你会有 1000 美元,并且想投资一年,那该怎么办呢?你不能走进一家银行,试图锁定一年后的利率。银行不会也不可能告诉你未来的利率是多少。也许银行的不同部门可以,但不是你走进的那个部门,因为目标客户是不同的。

事实上,银行确实知道未来的利率是多少。这就是 FRA。

FRA,或未来利率协议,是双方之间的协议,如果你借出你的钱,你将在期限结束时获得指定的利息和本金。

在本文中,我们将使用 Pandas 建立一个基于 FRAs(远期利率协议)的零曲线。有了这条零曲线,你可以很容易地对某样东西进行定价,从一天到未来十年的任何天数。

为简单起见,我们使用的 FRA 是一年期。实际上,欧洲美元期货(FRA)可以是一个月也可以是三个月。请注意,所有的利率,无论其期限,总是被称为年利率。

import pandas as pdfra = pd.DataFrame({
    'Description': ['2019', '2020', '2021', '2022', '2023'],
    'start_date': pd.date_range('2019-01-01', periods=5, freq='YS'),
    'end_date': pd.date_range('2019-01-01', periods=5, freq='Y'),
    'rate': [2.2375, 2.4594, 2.6818, 2.7422, 2.6625]
})

这里我们有截至 2019 年 1 月 1 日的 5 年 FRA。如果你在 2019-01-01 存入这笔钱,你将在第一年年底获得 2.2375%的利息,第二年获得 2.4594%,以此类推。

我们知道每年的利率,但如果我们想知道最终的复利,我可以向你保证答案不仅仅是把利率相加。下面我们要做的是,年复一年的复合增长。我们来分解一下步骤。

步骤 1:计算从 M 到 N 期间的增长,因此,mxn_growth

fra['mxn_growth'] = 1 + fra['rate'] / 100

第二步:复合增长是前期复合增长乘以本期增长。因为增长是从时间 0 开始复合的,所以我们称之为0xn_growth

fra['0xn_growth'] = fra['mxn_growth'].cumprod()

步骤 3:每个 FRA 都是一年期,但增长是前几年的复合增长。

fra['years'] = 1
fra['cummulative_years'] = fra['years'].cumsum()

第四步:最后我们知道总年数cummulative_years的总增长0xn_growth。我们只需要把它标准化为年率。正如我们前面指出的,利率总是被称为年利率。

fra['zero_rate'] = fra['0xn_growth'] ** (1 / fra['cummulative_years']) - 1

这就是了。你想知道 2020 年末你的回报是多少,2.3484%是你的利率。

等等!你可能会说,这太简单了。

是的,事实上,请记住,我们有意将每个 FRA 设为一年期。如果它们像欧洲美元期货一样是一个月或三个月呢?你会怎么做?我会把这个挑战留给你。如果你想直接得到答案,请阅读复利的数学没有你想象的那么简单(困难)。

这篇文章也有 Juypter 笔记本格式。随便玩吧。

构建一个能读懂你思想的人工智能

原文:https://towardsdatascience.com/building-an-ai-that-can-read-your-mind-8b22ad5a7f05?source=collection_archive---------17-----------------------

基于脑电数据的精神状态分类机器学习

(Source: Shutterstock)

能读懂你思想的人工智能

这听起来可能像科幻电影中的反面未来的情节。然而,随着最近的进展,这项技术正在从科幻小说向纯粹的科学飞跃。

大脑活动的自主非侵入式检测在多个领域都有潜在的用途,比如人类机器人互动和精神保健。它可以在用户和设备之间提供一个额外的交互维度,并使不依赖于语言交流的有形信息能够被导出。

这样的创新也意味着更好的脑机接口。这将为人机交流开辟全新的平台,包括帮助有身体或精神障碍的人。脑机接口可以让瘫痪的人移动机械臂,或者让脊髓损伤的人控制电动轮椅。

随着低成本脑电图(EEG) 设备的日益普及,脑波数据对于消费行业和研究来说变得越来越实惠,从而引入了在不需要专家在场的情况下进行自主分类的需求。

在本文中,我将介绍一个如何使用机器学习来分析大脑活动的示例。通过商业可用设备的脑电图记录,我展示了机器学习模型如何用于预测受试者的相应精神状态。

用于精神状态分类的机器学习

记录高质量的脑电图数据并不容易,除非你隶属于一个进行这种实验的实验室。然而,我最近看到了一篇有趣的文章,Jordan J. Bird、Luis J. Manso、Eduardo P. Ribiero、Anikó Ekárt 和 Diego R. Faria 撰写的文章“使用基于 EEG 的脑机接口进行精神状态分类的研究”。幸运的是,他们已经公开分享了他们研究中使用的数据,供其他人进行实验。在我看来,特别有趣的是消费级设备的使用,你只需花几百美元就可以在亚马逊上订购。他们研究中的数据记录和处理方法将在下一节介绍。

实验细节

这项研究通过一个商用 MUSE 脑电图头带采用了四个干式颅外电极。这是一种可穿戴的大脑传感设备,通过 4 个脑电图 (EEG)传感器测量大脑活动。

为了唤起不同的心理状态,实验使用了下表中显示的精选电影片段,代表正负价

Film Clips used as Stimuli for EEG Brainwave Data Collection. Source

从两个受试者(1 男 1 女,年龄 20-22 岁)的下表中找到的 6 个电影剪辑中的每一个记录 60 秒的数据,产生 12 分钟的大脑活动数据(每个情绪状态 6 分钟)。还收集了 6 分钟的中性脑波数据,导致从受试者记录的总共 36 分钟的 EEG 数据。(每天收集三分钟的数据,以减少静息情绪状态的干扰)。可变频率重采样至 150Hz,这产生了 324,000 个数据点的数据集。

建议的脑电信号特征集

脑电信号的特征提取和分类是脑机接口(BCI)应用的核心问题。当涉及到 EEG 特征提取时,一个具有挑战性的问题是信号的复杂性,因为它是非线性、 非平稳和随机的。信号仅在短时间间隔内被认为是稳定的,这就是为什么最佳实践是应用短时加窗技术来满足这一要求。然而,它仍然被认为是一个在正常大脑条件下成立的假设。

Example of a live EEG stream of the four Muse sensors, Right AUX did not have a device and was discarded due to it simply being noise. This live feed graph has a Y-Axis of measured microvolts at t=0 on each sensor, and an X-axis detailing the time reading. Source

这一小节描述了被认为能够充分区分不同类别的精神状态的一组特征。这些特征依赖于统计技术、基于快速傅立叶变换(FFT) 、香农熵、时间序列中的最大-最小特征等。根据给定时间窗口中信号的时间分布来计算被提议用于对精神状态进行分类的所有特征。该滑动窗口被定义为 1 秒的时间段,即所有特征都是在该时刻内计算的。关于特征提取的更详细介绍,请看原文

机器学习算法

按照上述从原始 EEG 数据中提取特征的方法,我们得到了包含 2548 个特征的数据集。对于数据集的每一行,我们都有相应的目标变量:'中性',''或''。我们的目标是训练一个机器学习模型,基于这组特征,成功地预测相应的精神状态。

在这个例子中,我从我的一个“定位”算法开始,即随机森林分类器,因为它设置简单,并且经常“开箱即用”地工作,不需要太多的超参数调整。

作为旁注:在原始时域数据上尝试一种卷积神经网络方法(而不是包括信号各种频率特征的提取特征集)也是很有趣的。由于时域中应用的卷积通过卷积定理与信号的频率特性密切相关,这可能是一种减少预处理和特征提取量的有前途的方法。

交叉验证

在应用 ML 时,您首先要了解的事情之一是交叉验证的重要性:在数据集的一部分上评估您的模型的性能,该数据集独立于您用来训练您的模型的数据集。一种方法是在训练您的模型时保持数据集的一部分,并使用例如以下方法来估计已训练模型的性能:

  • 用 70%的标注数据训练你的模型
  • 对剩余 30%的训练好的模型进行评估

K-fold 交叉验证在这方面有所改进,它允许您多次这样做,这样您就可以看到测试性能是否会根据您用来训练/测试的样本而变化。

Source: http://karlrosaen.com/ml/learning-log/2016-06-20/

通过多次运行训练/测试比较,您将获得对模型性能的更好的估计,并检查模型在对标记数据的不同部分进行训练后的性能是否有很大不同,这本身可能表明您的模型不稳定或样本集太小。

在我的例子中,我在训练模型时进行了 10 重交叉验证,并计算了不同数据段的精确度。模型的最终性能可以通过下面所示的混淆矩阵来可视化。

在机器学习领域,特别是统计分类问题中,混淆矩阵,也称为误差矩阵,是一种特定的表格布局,允许算法性能的可视化。矩阵的每一行代表实际类中的实例,而每一列代表预测类中的实例。该名称源于这样一个事实,即它很容易看出系统是否混淆了类别(即通常将一个类别错标为另一个类别)。

Confusion matrix: True vs. predicted emotional states

当在交叉验证期间评估 10 倍的模型预测时获得的最终结果是 0.987 (+/- 0.01)的相当令人印象深刻的准确度。这意味着,基于从原始 EEG 数据中提取的特征集,我们可以以接近 99%的准确度预测人的精神状态为“中性”、“消极”或“积极”。

结果摘要

虽然这个例子的结果在从脑电图记录中预测情绪状态时确实非常令人印象深刻,但在更高级的应用中还有一段路要走。此外,在实验记录期间,只有两个受试者的有限样本量提出了对新个体进行概括的问题。尽管如此,作为一个显示非常有希望的结果的例子,它代表了进一步调查的良好开端。

观点

那么,我们是否正在接近完全精神控制的反主题科幻未来,在那里,我们甚至放弃了自己思想的隐私?

人工智能读取人的大脑活动的能力引发了围绕隐私和安全的重大伦理问题,研究领导者需要认识到这一点。这项技术打开了一个新的恶意使用的潘多拉魔盒,例如,可以包括在操纵你的大脑思考敏感信息后从你的大脑中窃取这些信息。发表在《自然》杂志上的一篇文章描述了一个迫在眉睫的未来,“将有可能解码人们的心理过程,并直接操纵他们意图、情感和决定背后的大脑机制。”

尽管如此,这一领域的突破是解码思考背后的大脑模式的重要一步。这样的创新也意味着更好的脑机接口,这将为人机交流开辟全新的平台。这可能让瘫痪的人移动机器人手臂,或者让脊髓损伤的人控制电动轮椅。如下面的视频所示,诸如“智能 prostetics”这样的应用可能代表着全世界残疾人向前迈出的一大步。

“Beyond bionics”, The Guardian

虽然我认为自己是一个“技术乐观主义者”,但我仍然认为有必要制定规则和条例,以确保这项技术能够帮助那些需要它的人,而不会带来灾难性的后果。

未来会怎样谁也说不准,但可以肯定的是,我们会找到更多有意或无意地与计算机互动的方式。人工智能驱动的界面的前景很好,但挑战也很大。

(本文原为 原文发表于此 )

你觉得这篇文章有趣吗?如果是这样的话,你可能也会喜欢我的其他一些关于人工智能、机器学习、物理等主题的文章。,你可以在下面的链接和我的中型作者简介中找到:

* [## Vegard Flovik —中等

2018 年夏天我发表第一篇关于“走向数据科学”的文章时,数字背后的统计数据…

medium.com](https://medium.com/@vflovik)

而且,如果你想成为一个媒体会员,免费访问平台上的所有资料,你也可以使用下面我的推荐链接。(注意:如果您使用此链接注册,我也会收到一部分会员费)

[## 通过我的推荐链接加入 Medium—Vegard flo vik

作为一个媒体会员,你的会员费的一部分会给你阅读的作家,你可以完全接触到每一个故事…

medium.com](https://medium.com/@vflovik/membership)*

建立应用科学投资组合

原文:https://towardsdatascience.com/building-an-applied-science-portfolio-9f25da61fb45?source=collection_archive---------28-----------------------

A visualization of my Portfolio

我在 2019 年西 ODSC 会议上的概述

我很高兴今年有机会第一次出席 ODSC 大会!我是开源数据科学项目的忠实粉丝,一直致力于与分享我们在 Zynga 所做的一些工作。

本次会议的目标是强调如何建立一个出色的投资组合,向公司展示应用数据科学技能。应用科学家是一个在包括亚马逊、脸书和微软在内的科技公司中使用越来越多的职位名称。它处于数据科学和机器学习工程的交汇点,尽管行业对应用科学家的需求越来越多,但积累相关经验可能很困难。

在这次演讲中,我确定了在构建向公司展示的投资组合时需要关注的四个主题。我们的目标不仅是证明你可以做很棒的算法工作,而且还展示你可以扩大它的规模,并编写文档,使你的组织能够理解你的方法。以下是我在演讲中讨论的主要主题:

  • 生态系统:亲身体验云计算。
  • 规模:探索分布式计算。
  • 集成:将事物粘合在一起。
  • 创作:超越套牌!

一旦完成,我会更新这篇文章链接到幻灯片。

[## 构建应用数据科学角色组合|开放数据科学会议

摘要:应用科学家是一个越来越多的科技公司使用的职位,包括亚马逊、脸书…

odsc.com](https://odsc.com/training/portfolio/building-a-portfolio-for-applied-data-science-roles/)

我是 Zynga 的一名数据科学家,我的工作重点是构建投资组合规模的数据产品。这些都是应用科学项目,因为我们需要建立机器学习模型,这些模型可以扩展到数十亿条记录,在我们的游戏中跨不同的事件分类进行翻译,并改进手动功能工程工作。

我很高兴能在 ODSC 做一个关于建立应用科学投资组合的演讲,因为这是我之前为数据科学写的博客。我在 ODSC 演讲的目标是将这篇博文的建议扩展到一个更专业的角色。

[## 给有抱负的数据科学家的六条建议

找到工作前积累经验

towardsdatascience.com](/six-recommendations-for-aspiring-data-scientists-93d12aeb9b9)

在这次演讲中,我不想强调具体的技术,而是想概括应用科学家应该具备的能力。对于每个主题,我会先问一个可能会被问到的面试问题,然后展示我过去是如何回答这些问题的。

生态系统

你能告诉我你将一个模型投入生产的一次经历吗?

求职者在寻找新工作时面临的一个主要挑战是,他们没有雇主正在寻找的编码/建模/部署生态系统的经验,并且很难获得新平台的实践经验。

虽然许多公司确实锁定了对其数据平台的访问,但我建议有抱负的应用科学家积累云平台方面的经验,即使这意味着要用自己的时间来做。亚马逊网络服务(AWS)和谷歌云平台(GCP)都提供了构建体验的自由层解决方案,尝试不同的系统可以为如何构建生产级数据产品提供更广阔的视角。

Take advantage of the free cloud platform options.

目标是展示云环境的经验,并证明您可以通过利用这些平台来扩展方法。探索多个平台以了解平台之间的权衡也很有用。例如,我写了如何通过 AWS 和 GCP 使用无服务器函数来托管逻辑回归模型。

[## 作为无服务器功能的模型

“生产中的数据科学”第 3 章

towardsdatascience.com](/models-as-serverless-functions-7930a70193d4)

规模

你处理过的最大的数据集是什么?

第二个主题是在整合项目组合时使用大规模数据集。理想情况下,您应该突破单实例计算的极限,探索分布式计算方法来解决大规模问题。在 Kaggle 、 BigQuery 和开放数据上有各种开放数据集可供探索。

使用大数据集的目标是表明您的方法可以扩展到现实世界的工作负载,例如为数千万用户构建流失预测。探索解决大规模问题的不同方法很有用,比如使用 Spark 和 Cloud 数据流,甚至在 SQL 中执行复杂的逻辑。你的作品集的这一部分应该展示你可以采取分而治之的方法来解决问题,并且你可以有效地分配工作流。例如,我在博客中讨论了在 Spark 中并行化任务的不同方法。

[## Spark 中的 3 种并行化方法

提升数据科学任务的速度

towardsdatascience.com](/3-methods-for-parallelization-in-spark-6a1a4333b473)

集成

你能告诉我你不得不整合不同系统的时候吗?

在将原型代码翻译成产品代码时,经常会遇到挑战。例如,当使用 PySpark 和 MLlib 时,对训练 ML 模型有用的 scikit-learn 库可能在生产环境中不可用。投资组合应该展示当生产一个 ML 模型时,你是如何处理这些类型的挑战的,比如使用一个中间模型格式。展示您可以构建需要跨多个组件协调的端到端 ML 管道也很有用。

我在作品集中使用的一个例子是 AutoModel ,其中我使用了 Pandas UDFs 来扩展一个只支持 Pandas 数据框的库。Spark 数据帧和功能工具不兼容,用这些组件构建 ML 管道需要一些新的集成工作。我写过的另一个例子是使用云数据流构建一个完全托管的分析平台。

[## 简单且可扩展的分析管道

收集关于应用程序使用和用户行为的数据,如玩家在游戏中的进度,对于产品来说是无价的…

towardsdatascience.com](/a-simple-and-scalable-analytics-pipeline-53720b1dbd35)

对于你的投资组合中的这个主题,包含分析和机器学习之外的项目可能是有用的。例如,我想展示的集成挑战之一是我如何让我的顾问的反应式编程语言使用 Java 本地接口(JNI)与 C++ API 一起工作。

Writing a StarCraft bot using Java.

程序编写

你喜欢如何宣传你的项目?

强有力的书面沟通对于推动组织内数据产品的采用至关重要。虽然幻灯片对于提出想法和提供产品概述很有用,但它们通常缺乏传达数据产品如何工作所需的深度和细节。在 Twitch,我养成了为记录分析和 ML 模型而编写长格式内容的习惯,并且从那以后一直应用这种方法。在 Zynga,我提倡撰写白皮书,为数据产品提供执行摘要、产品概述和实施细节。关于我的作品集,我目前正在写一本名为生产中的数据科学的书。

Long-form content I’ve written on data science.

虽然将代码和文档放在 GitHub 这样的平台上对于共享项目来说很好,但是积累编写关于项目的长格式内容的经验也是很好的。这可以采取白皮书或博客帖子的形式,创作这种类型的内容对于展示书面交流技能很有用。

结论

当构建应用科学角色的文件夹时,展示您过去如何扩展方法以在生产规模下工作是很棒的。我从前三个主题谈起:生态系统、规模和整合。我强调的最后一个主题是为你的项目写作和宣传。

如果您无法到达 ODSC,活动结束后应提供会谈视频。我还将在旧金山湾区的以下活动中发言。

[## 2019 年 Zynga | AI 和大数据博览会上的自动化功能工程

开始时间:结束时间:白天:谈话:Zynga 是一家移动游戏出版商,其工作室遍布全球。我们有…

www.ai-expo.net](https://www.ai-expo.net/northamerica/talk/automated-engineering/) [## AIFOW -影响:AI 2020

2020 年 2 月 4 日和 5 日——旧金山影响:AI 2020 即将在加州旧金山举行。来学习、探索和创造…

aifow.com](https://aifow.com/ai2020)

本·韦伯是 Zynga 的杰出数据科学家。我们是招聘!

在 Python 中构建员工流失模型以制定战略性保留计划

原文:https://towardsdatascience.com/building-an-employee-churn-model-in-python-to-develop-a-strategic-retention-plan-57d5bd882c2d?source=collection_archive---------3-----------------------

Everybody’s working hard but who is most likely to hand in their resignation letter? (Photo by Alex Kotliarskyi on Unsplash)

内容

  1. 问题定义
  2. 数据分析
  3. EDA 结束语
  4. 预处理流水线
  5. 建立机器学习模型
  6. 结束语

1.问题定义

员工流动(也称为“员工流失”)对公司来说是一个代价高昂的问题。替换一名员工的真实成本通常会非常高。

美国进步中心(Center for American Progress)的一项研究发现,公司通常会支付员工工资的五分之一来替换该员工,如果高管或收入最高的员工被替换,成本会显著增加。

换句话说,对于大多数雇主来说,替换员工的成本仍然很高。这是由于面试和寻找替代者所花费的时间,签约奖金,以及新员工在适应新角色的几个月中生产力的损失。

了解员工最有可能离开的原因和时间可以导致提高员工保留率的行动,并可能提前计划新的招聘。我将使用一种循序渐进的系统方法,使用一种可以用于各种 ML 问题的方法。这个项目将属于通常所说的人力资源分析人员分析

Focused and minding their own business but where’s the collaborative spirit? (Photo by rawpixel on Unsplash)

在这项研究中,我们将试图解决以下陈述的问题:

  • 一名在职员工离开公司的可能性有多大?
  • 员工离开公司的关键指标是什么?
  • 根据调查结果,可以采取哪些策略来提高员工保留率?

假设我们有前雇员的数据,这是一个标准监督分类问题其中标签是一个二元变量,0(在职雇员),1(前雇员)。在这项研究中,我们的目标变量 Y 是员工离开公司的概率。

注意:完整代码请参考本GitHub repo和/或本ka ggle 内核

* [## hamzaben 86/员工流失预测模型

使用监督机器学习预测员工流失-hamzaben 86/员工流失预测模型

github.com](https://github.com/hamzaben86/Employee-Churn-Predictive-Model) [## 带有战略保留计划的员工流失模型| Kaggle

编辑描述

www.kaggle.com](https://www.kaggle.com/hamzaben/employee-churn-model-w-strategic-retention-plan)

2.数据分析

在本案例研究中,HR 数据集来自于 IBM HR Analytics 员工流失&绩效,其中包含 1,470 名员工的数据以及关于这些员工的各种信息。我将使用这个数据集,通过了解员工流失的主要驱动因素来预测员工何时会辞职。

正如 IBM 网站 : 上所述,“这是 IBM 数据科学家创造的虚构数据集。其主要目的是展示 IBM Watson 员工流失分析工具。

Let’s crunch some employee data! (Photo by rawpixel on Unsplash)

2.1 数据描述和探索性可视化

首先,我们导入数据集并为该分析制作源文件的副本。数据集包含 1,470 行和 35 列。

数据集包含几个数字列和分类列,提供关于雇员个人和雇佣详细信息的各种信息。

让我们按照列的类型(例如 int64、float64、object)来细分这些列:

2.2 数据来源

提供的数据没有缺失值。在人力资源分析中,员工数据不太可能出现大比例的缺失值,因为人力资源部门通常会将所有个人和雇佣数据存档。

但是,保存的文档数据类型(即,是否是基于纸张、Excel 电子表格、数据库等)对人力资源数据的准确性和易用性有很大影响。

2.3 数字特征概述

基于数字特征的信息和直方图,可以进行一些观察:

  • 几个数字特征是重尾的;事实上,有几个分布是右偏的(例如,MonthlyIncome DistanceFromHome,YearsAtCompany)。在将模型拟合到数据之前,可能需要数据转换方法来接近正态分布。
  • 年龄分布是一种略微向右倾斜的正态分布,大多数工作人员年龄在 25 至 45 岁之间。
  • EmployeeCount 和 StandardHours 是所有雇员的常数值。它们很可能是多余的功能。
  • 考虑到特征的准均匀分布,员工编号可能是员工的唯一标识符。

source code: df_HR.hist() — isn’t Python a beautiful thing?

2.4 按目标属性的特征分布

在本节中,将进行更详细的探索性数据分析。完整代码请参考这个 GitHub repo 和/或 Kaggle 内核

2.4.1 年龄

在职和离职雇员的年龄分布仅相差一岁;前员工的平均年龄为 33.6 岁,现任员工的平均年龄为 37.6 岁。

让我们创建一个由目标值着色的核密度估计(KDE)图。核密度估计(KDE)是一种估计随机变量概率密度函数的非参数方法。

2.4.2 性别

性别分布显示,该数据集中男性前雇员的相对比例高于女性前雇员,数据集中前雇员的标准化性别分布为男性 17.0%,女性 14.8%。

2.4.3 婚姻状况

该数据集包含三种婚姻状况:已婚(673 名员工)、单身(470 名员工)、离婚(327 名员工)。单身员工的离职比例最大,为 25%。

2.4.4 角色和工作条件

对商务旅行频率和流失状态之间的关系的初步观察表明,对于“经常”旅行的员工来说,存在最大的归一化离职比例。没有披露与商务旅行状态相关的旅行指标(即多少小时的旅行被视为“频繁”)。

数据集中列出了几种工作角色:销售主管、研究科学家、实验室技术员、制造总监、医疗代表、经理、销售代表、研究总监、人力资源。

在公司工作 2.4.5 年,自上次晋升以来

在职员工在公司的平均工作年限为 7.37 年,离职员工为 5.13 年。

2.4.6 年,现任经理

在职员工在现任经理手下工作的平均年限为 4.37 年,离职员工为 2.85 年。

2.4.7 加班

一些员工有加班承诺。数据清楚地表明,有相当大一部分加班的员工已经离开了公司。

2.4.8 月收入

员工月收入从 1009 美元到 19999 美元不等。

2.4.9 目标变量:减员

特征“损耗”就是这个机器学习问题的所在。我们试图通过使用与员工个人和职业历史相关的其他相关特征来预测特征“流失”的价值。

在提供的数据集中,在职员工的比例为 83.9%,离职员工的比例为 16.1%。因此,这是一个不平衡的阶级问题。

当每个类的实例数量大致相等时,机器学习算法通常工作得最好。在实现我们的机器学习算法之前,我们必须解决这个目标特征的不平衡。

2.5 相关性

让我们来看看一些最重要的相关性。值得记住的是,相关系数只测量线性相关性。

如上所示,“月工资”、“工作的公司数量”和“离家的距离”与自然减员正相关;而“总工作年数”、“工作级别”和“在当前职位的年数”与自然减员呈负相关。

3 EDA 结束语

  • 数据集没有任何缺失或错误的数据值,所有特征都是正确的数据类型。
  • 与目标特征最强的正相关是:绩效等级、月工资率、工作过的公司数量、离家的距离。
  • 与目标特征最强的负相关是:总工作年限、工作级别、在当前岗位的年限、月收入。
  • 数据集不平衡,大部分观察描述的是当前在职员工。
  • 与已婚和离异员工相比,单身员工的离职比例最大。
  • 大约 10%的离职者在他们在公司的两周年纪念日离开。
  • 与同事相比,住得离工作地点较远的人离职的比例更高。
  • 经常出差的人比他们的同行显示出更高的离职比例。
  • 不得不加班的人比他们的同事有更高的离职率。
  • 与同行相比,之前已经在几家公司工作过的员工(已经在工作场所之间“跳槽”)的离职率更高。

4.预处理流水线

在本节中,我们进行数据预处理步骤,为机器学习算法的实现准备数据集。完整代码请参考这个 GitHub repo 和/或 Kaggle 内核

4.1 编码

机器学习算法通常只能将数值作为它们的预测变量。因此标签编码变得必要,因为它们用数值编码分类标签。为了避免为具有大量唯一值的分类特征引入特征重要性,我们将同时使用标签编码一键编码,如下所示。

4.2 特征缩放

使用最小最大缩放器的特征缩放实质上缩小了范围,使范围现在介于 0 和 n 之间。当输入数值变量落在类似的范围内时,机器学习算法表现更好。在这种情况下,我们在 0 和 5 之间缩放。

4.3 将数据分成训练集和测试集

在实现或应用任何机器学习算法之前,我们必须将训练和测试数据帧与我们的主数据集分离。

5.构建机器学习模型

5.1 基线算法

在我们进入更复杂的解决方案之前,让我们先使用一系列基线算法(使用开箱即用的超参数)。本节考虑的算法有:逻辑回归随机森林SVMKNN决策树分类器高斯 NB。

让我们依次评估每个模型,并提供准确度标准差分数。完整代码请参考本 GitHub repo 和/或 Kaggle 内核

分类准确度是正确预测的数量占所有预测的比例。这是分类问题最常见的评估标准。

然而,经常被误用,因为它只有在每类中有相等数量的观测值,并且所有预测和预测误差同等重要时才真正适用。在这个项目中情况并非如此,所以不同的评分标准可能更合适。

ROC 曲线下面积(简称 AUC)是二元分类问题的性能度量。AUC 代表模型区分正负类别的能力,更适合本项目。面积为 1.0 表示模型能够完美地做出所有预测。面积为 0.5 表示模型与随机模型一样好。

基于我们的 ROC AUC 比较分析,逻辑回归随机森林显示最高的平均 AUC 分数。我们将这两种算法列入候选名单,以供进一步分析。关于这两种算法的更多细节见下文。

5.2 逻辑回归

GridSearchCV 允许通过搜索估计器的指定参数值来微调超参数。如下所示,GridSearchCV 的结果使用 ROC_AUC 作为评分标准,为我们提供了微调的超参数。

5.3 混淆矩阵

混淆矩阵为我们提供了更详细的准确度分数表示,以及我们的标签正在发生的事情——我们确切地知道哪些标签/如何被正确和错误地预测。Logistic 回归分类器在测试集上的准确率为 75.54。

5.4 标签概率

可以将概率与预测目标相关联,而不是获得二进制估计目标特征(0 或 1)。输出提供了第一个索引,该索引涉及数据属于类别 0 (员工未离职)的概率,第二个索引涉及数据属于类别 1 (员工离职)的概率。预测特定标签的概率为我们提供了一个衡量员工离开公司的可能性的方法。

5.5 随机森林分类器

让我们仔细看看如何使用随机森林算法。我将通过对 AUC 分数进行交叉验证来微调随机森林算法的超参数。

随机森林让我们知道在预测目标特性时哪些特性是最重要的(本项目中的“损耗”)。下面,我们按照重要性来划分特征。

Random Forest 帮助我们确定了 10 个最重要的指标(排列在下表中):(1)月收入,(2)加班,(3)年龄,(4)月工资,(5)离家的距离,(6)日工资,(7)总工作年数,(8)在公司的年数,(9)小时工资,(10)与经理的年数。

随机森林回归分类器在测试集上的准确率为 86.14。下面显示了相应的混淆矩阵。

预测特定标签的概率为我们提供了一个衡量员工离开公司的可能性的方法。使用 RandomForestClassifier 预测概率时的 AUC 为 0.818。

5.6 ROC 图

AUC-ROC 曲线是在各种阈值设置下对分类问题的性能测量。ROC 是概率曲线,AUC 代表可分性的程度或度量。它告诉我们模型在多大程度上能够区分不同的类。绿线代表纯随机分类器的 ROC 曲线;一个好的分类器尽可能远离那条线(朝向左上角)。

如上所示,与随机森林分类器相比,微调的逻辑回归模型显示了更高的 AUC 分数。

6.结束语

6.1 风险评分

随着公司生成更多关于其员工的数据(关于新加入者和最近离职者),该算法可以使用额外的数据进行重新训练,并在理论上生成更准确的预测,以根据算法分配给每个特征变量(即员工)的概率标签来识别高风险离职员工

员工可以根据预测的标签分配一个“风险分数,以便:

  • 标签为< 0.6 的员工的低风险
  • 中等风险适用于评分在 0.6 和 0.8 之间的员工
  • 高风险标签为> 0.8 的员工

Good work on reading the analysis — but what now? How does this help decision-makers? (Photo by rawpixel on Unsplash)

6.2 指标和战略保留计划

人们离开的更强指标包括:

  • 月收入:工资高的人不太可能离开公司。因此,应努力收集当前当地市场的行业基准信息,以确定公司是否提供有竞争力的工资。
  • 久而久之:加班的人更容易离开公司。因此,必须努力在前期适当确定项目范围,提供足够的支持和人力,以减少加班。
  • 年龄:相对年轻的 25-35 岁年龄段的员工更有可能离职。因此,应该努力清楚地阐明公司的长期愿景以及适合该愿景的年轻员工,并以明确的晋升途径等形式提供激励。
  • 离家远:住得离家远的员工更有可能离开公司。因此,应努力为离开同一地区的员工群提供公司交通或交通补贴形式的支持。基于员工家庭所在地的初步筛选可能不被推荐,因为只要员工每天按时上班,这将被视为一种歧视。
  • 总工作年限:越有经验的员工越不容易离开。拥有 5-8 年工作经验的员工应被视为潜在离职风险较高。
  • YearsAtCompany :忠诚的公司不太可能离开。满两周年的员工应被认定为具有较高离职风险的潜在员工。
  • YearsWithCurrManager :大量离职者在现任经理离职 6 个月后离开。通过使用每个员工的直线经理详细信息,可以确定哪个经理在过去一年中经历了最多数量的员工辞职。

这里可以使用几个指标来确定是否应该与直线经理一起采取行动:

  • 直线经理担任特定职位的年数:这可能表明员工可能需要管理培训或在组织中被指派一名导师(最好是高管)
  • 辞职员工的模式:这可能表明离职员工的重复模式,在这种情况下可以采取相应的措施。

6.3 最终想法

可以为每个风险分值组制定战略保留计划。除了上面列出的每个特征的建议步骤之外,人力资源代表和员工之间的面对面会议可以针对中风险员工高风险员工展开,以讨论工作条件。此外,与这些员工的直线经理会面可以讨论团队内部的工作环境,以及是否可以采取措施改善工作环境。*

我希望你能像我写这篇文章时一样喜欢它。
再次声明,完整代码请参考本GitHub repo和/或本 Kaggle 内核

*** [## hamzaben 86/员工流失预测模型

使用监督机器学习预测员工流失-hamzaben 86/员工流失预测模型

github.com](https://github.com/hamzaben86/Employee-Churn-Predictive-Model) [## 带有战略保留计划的员工流失模型| Kaggle

编辑描述

www.kaggle.com](https://www.kaggle.com/hamzaben/employee-churn-model-w-strategic-retention-plan)***

构建复合算法的实验框架

原文:https://towardsdatascience.com/building-an-experimentation-framework-for-composite-algorithms-19d0084cb24a?source=collection_archive---------13-----------------------

在现有 ML 库的基础上快速开发一个框架来改变模型超参数和混合多个模型以找到最佳执行算法通常非常有用。

Weka 提供了一套全面的库来快速试验不同的模型。在这里,我将演示如何轻松地使用 Weka Api 来构建一个实验平台,以比较不同 ML 算法的性能。

可以先编写一个包装类,比如 KNNTest,使用 maven 构建,创建 CLI 比如 MLExpFwk,然后如下调用它。

Weka maven dependency: {groupId: nz.ac.waikato.cms.weka,artifactId: weka-stable, version: 3.6.8}
Java version :  1.7.0_71
Weka Standalone: weka-3-6-12, Mac compatible Weka: weka-3-6-12-oracle-jvm.dmgEntry Point: MLExpFwk
Executable: MLExpFwk-1.0.0.jar 
Run the tool: java -jar MLExpFwk-1.0.0.jar CreditRating-train.arff CreditRating-test.arff* Output of  the program :
o Menu options 
Welcome to ML Programs Suite !

Which Algorithm do you want to run ? 

Type 0 to exit.
Type 1 for Decision Tree - J48
Type 2 for KNN
Type 3 for SVM
Type 4 for MLP
So on ...We can run any algo against any training and test datasets to compare and contrast the accuracy.

实验监督算法

使用不同的超参数生成不同的模型组合:

//**DTree Model properties**
tree.setMinNumObj(minNumObj); 
tree.setReducedErrorPruning(true); 
tree.setNumFolds(M); 
tree.setSubtreeRaising(true); 
tree.setUnpruned(false);//**KNN Model properties** IBk ibk = new IBk(); 
ibk.setKNN(3); 
ibk.setCrossValidate(true); 
ibk.setMeanSquared(true);//**SVM Model properties**
SMO smo = new SMO();   
NormalizedPolyKernel normalizedPolyKernel = new NormalizedPolyKernel();   
smo.setKernel(normalizedPolyKernel);   
smo.setC(2);//**ANN Attribute Variations** MultilayerPerceptron mlp = new MultilayerPerceptron();   mlp.setLearningRate(0.1);**//Create different Run options for different models to be tested** case 1: “Type 1 — to run AdaBoosting on J4.8 tree“); 
        testBoostingOnTree(); break; 
case 2: “Type 2 — to run AdaBoosting on SVM”)
        testBoostingOnSVM(); break; 
case 3: ...... 
default: printmenu(); break;

例如我们可以尝试提升 SVM 模式和 J48 树

AdaBoostM1 classifier = new AdaBoostM1(); 
SMO smo = new SMO(); boosting.setClassifier(smo); classifier.buildClassifier(MLExpFwk.TRAINING_DATA);Evaluation eval = new Evaluation(MLProgramSuite.TRAINING_DATA); eval.evaluateModel(classifier, MLExpFwk.TRAINING_DATA);System.out.println(eval.toSummaryString(); 
System.out.println(“ — — Now apply the Classifier to the test data — — “); 
//runAlgoOnTestData
Evaluation eval = new Evaluation(MLExpFwk.TRAINING_DATA);    eval.evaluateModel(classifier, MLExpFwk.TEST_DATA);     System.out.println(eval.toSummaryString("\nResults\n\n", false));

例如,让我们在电离层数据集(https://archive.ics.uci.edu/ml/datasets/Ionosphere 上运行不同的监督算法组合!!)

  • AdaBoostM1 / MultiBoostAB +/修剪过的树
  • SVM+/adaboosting m1+/多内核+/归一化多内核)
  • IBK +/ k=3 +/交叉验证)
  • MLP(具有非线性 sigmoid 传递函数)等等。

然后我们有了有趣的观察:

  • MLP 达到 99%的准确率,但耗时 1.45 秒。它对大量异质特征非常有用。由于函数逼近的全局性质,MLP 学习具有复杂局部结构的非线性函数较慢。在梯度下降过程中,参数在学习过程中陷入的平稳状态需要很长时间才能消除。
  • 使用归一化多核的 SVM在 0.10 秒内达到 97.14%的准确率。SMO 算法对过度拟合具有很强的弹性。看起来归一化的 PolyKernel 在测试数据集上比 PolyKernel 表现得更好,因为它归一化了特征向量中的一些稀疏性,并找到了更好的模型。
  • J48 的 numFolds = 5 和 reducedErrorPruning 提供了更高的精度,比如 91.22%。当应用 adaboostm 1/multiboosttab 升压时,精度进一步提高到 97.8%。
  • k = 3 且交叉验证的 KNN提供了非常好的结果。但是由于未适当处理的不平衡数据分布问题,显示出一些不一致性。

实验无监督算法

现在,让我们对数据集应用无监督算法,通过创建像 ICA、NN-ICA、Kmeans-ICA、NN-KMeans、EM-ICA 这样的包装器类来理解聚类技术和特征选择是如何工作的

让我们用下面的数据集来检查有趣的事实:

用户接受采访,以提供对大型产品品牌的购买行为的意见。

偏好 _ 电子邮件、质量 _ 产品 _ 定价 _ 高、咨询 _ 多个 _ 网站 _ 购买前、偏好 _ 电视 _ 广告、偏好 _ 移动 _ 广告、观看 _ 广告 _ 制作 _ 购买、偏好 _ 在线 _ 网络、女性 _ 主要 _ 决策者、偏好 _ 儿童 _ 友好 _ 广告、偏好 _ 大 _ 品牌、频繁 _ 访问 _ 商场、偏好 _ 在线 _ 优惠、偏好 _ 分期付款、偏好 _ 餐厅 _ 优惠券 _ 购物、偏好 _ 电影 _ 优惠券 _ 购物

用户反馈数据被捕获为数值:

1,3,1,2,3,1,2,2,3,2,2,1,1,1,1 
(1 = Strongly Agree, 2 = Agree, 3 = Neither Agree nor Disagree, 4 = Disagree, 5= Strongly Disagree)

让我们首先尝试应用 KMeans 算法,该算法提供基于迭代距离的不相交聚类集。

DataSet ds = CSVFilereader.read(“/resources/marketing_survey.csv”, skipHeader); 
KMeansClusterer km = new KMeansClusterer(6); km.estimate(ds);

Weka 输出很容易理解。显然,集群 0 是所有特性中最好的一个。我们还可以进行其他有趣的观察,比如第 5 类客户对“prefer_mobile_ads”的看法比其他类客户更强烈。

接下来让我们运行期望最大化算法,该算法利用先验概率找到聚类。由于需要计算任何实例的聚类成员概率,因此查找聚类需要相当长的时间。

We observe that Cluster 2 offers best market segment.

EMClusterer em = new EMClusterer(); em.estimate(ds);

现在让我们把重点放在降维算法上。

主成分分析:通过选择足够的特征向量来解释原始数据 95%的方差,从而实现降维。

PrincipalComponentAnalysis filter =new PrincipalComponentAnalysis(dataset); System.out.println(set.getDescription()); System.out.println(filter.getProjection()); Matrix reverse = filter2.getProjection().transpose(); 
for (int i = 0; i < set.size(); i++) 
{ Instance instance = set.get(i);   
instance.setData(reverse.times(instance.getData()).plus(filter.getMean())); 
}

一旦协方差矩阵被打印出来,我们就可以找到互特征相关性。PCA 回答了以下问题——这 M 个参数中的哪一个解释了数据集中包含的大量变化?

然后我们选择排名前 5 的属性

**0.864** 1 0.443**v5**+0.377**v4**+0.354**v11**+0.327**v9**+0.298v6…
**0.7509** 2 -0.461**v4**–0.422**v5**+0.382**v9**+0.377**v11**+0.339v10…
**0.6513** 3 0.519v8–0.505v12–0.415v11+0.388v9–0.294v13…
**0.5564** 4 0.529v6–0.473v3+0.361v5–0.327v10–0.318v4…
**0.4744** 5 -0.446v2–0.373v8–0.362v7–0.36v3+0.326v10…

类似地,我们可以运行独立分量分析:它在新生成的特征集中生成足够的特征向量,以提供关于具有较高变化的特征的想法,这些变化可以被选择用于进一步的分类。

System.out.println("Before ICA");        System.out.println(set.getDescription());        System.out.println(set);        
IndependentComponentAnalysis filter = new IndependentComponentAnalysis(set, 8);        
filter.filter(set);
System.out.println("After ICA");
System.out.println(set);

正如我们前面提到的,我们的目标是混合和匹配算法。

接下来,我们使用 PCA 运行 KMeans 聚类

PrincipalComponentAnalysis filter = new PrincipalComponentAnalysis(dataset, 15); filter.filter(dataset); System.out.println(dataset.getDescription()); 
KMeansClusterer km = new KMeansClusterer(4); km.estimate(dataset);

然后我们用 ICA 运行 KMeans 聚类

IndependentComponentAnalysis filter = new IndependentComponentAnalysis(set); filter.filter(set); System.out.println(set.getDescription()); 
KMeansClusterer km = new KMeansClusterer(3); km.estimate(set);java -cp unsupervised_algo.jar:lib/ABAGAIL.jar com.ml.unsupervised.tests.KMeansWithIndependentComponentAnalysisTest

我们还可以应用其他 Weka 降维技术,如不重要成分分析随机投影

随机投影通过使用具有单位长度列的随机矩阵将数据投影到更低维度的子空间来降低数据的维度(即,它将减少数据中的属性数量,同时保留其大部分变化,如 PCA,但计算成本低得多)。

在减少维度之前,它首先应用 NominalToBinary 过滤器将所有属性转换为数值。

理想情况下,我们应该将代码封装到合适的复合模型构建器包装器中,并测试所有不同的变体。为了更有趣,我们可以用 PCA 运行神经网络

**//Feature Names**
obs#, chk_acct,duration,history,new_car, used_car, furniture,radio_tv, education,retarining,crd_amount,sav_accnt,employment,install_rate,male_div, male_single, coapplication,guarantor, present_redisence, real_estate,prop_unknown,age,other_install,rent,own_res,num_credits,job,num_dependents,telephone,foreign,response**//Feature Values** 2,1,48,2,0,0,0,1,0,0,5951,0,2,2,0,0,0,0,0,2,1,0,22,0,0,1,1,2,1,0,0,0
5,0,24,3,1,0,0,0,0,0,4870,0,2,3,0,1,0,0,0,4,0,1,53,0,0,0,2,2,2,0,0,0PrincipalComponentAnalysis filter = new PrincipalComponentAnalysis(set, 15); filter.filter(set); network = factory.createClassificationNetwork( new int[] {inputLayer, hiddenLayer, outputLayer}); ConvergenceTrainer trainer = new ConvergenceTrainer( new BatchBackPropagationTrainer(set, network, new SumOfSquaresError(), new RPROPUpdateRule())); trainer.train(); System.out.println(“Convergence in “+trainer.getIterations()+”iterations”);Instance[] patterns = set.getInstances(); 
int true_positives_num = 0; int actual_class = 0; int predicted_class = 0; for (int i = 0; i < patterns.length; i++) { network.setInputValues(patterns[i].getData()); network.run(); actual_class = Math.round(Float.parseFloat(patterns[i].getLabel().toString())); predicted_class = Math.round(Float.parseFloat(network.getOutputValues().toString())); if(actual_class == predicted_class) { true_positives_num++; } }double true_positives = ((true_positives_num*100/(patterns.length)));

因此,本质上,我们可以非常容易地混合所有不同的技术,并构建一套非常强大的复合模型,如 Kmeans-ICAKMeans-PCANN-LDANN-ICANN-KMeansNN-EMC 等。这里没有特定的正确或错误的模型,这都是通过使用 Weka 或任何其他库(如 SciKit)进行实验的结果。

现在让我们换个话题,看看随机搜索并使用 ABAGAIL 和 Weka 库构建一个框架。

有时,MLP 无法收敛到复杂数据集的解决方案,这是由于:(1)无偏估计量,但可能具有高方差(由于过拟合)/ (2)刚性模型相反,导致小方差但高偏差/ (3)需要更长的时间,并且还存在陷入局部最优的风险。

因此,让我们在反向传播网络中应用随机优化而不是梯度下降

使用 ABAGAIL.jar 的本地随机搜索算法(在类路径中)。

这里,我们对训练样本应用 3 种类型的优化算法。我们可以结合多种技术,也可以用不同的超参数以孤立的方式进行测试。

oa[0] = new RandomizedHillClimbing(nnop[0]);
oa[1] = new SimulatedAnnealing(1E11, .1, nnop[1]); 
oa[2] = new StandardGeneticAlgorithm(100, 50, 10, nnop[2]);

随机爬山是“一个不断走向增值的循环”。它可以在最佳后继者的集合中随机选择,以避免局部最小值。

模拟退火是随机局部搜索方法的一个例子,以基于目标函数变化大小的概率,在开始时结合横向和下坡移动。这个想法是在早期对整个空间进行足够的探索,以便最终的解决方案对初始状态相对不敏感。这应该会降低在局部最大值、平台或山脊被捕获的机会。"

遗传算法是一种基于遗传学和自然选择原理的优化和搜索技术。GA 允许由许多个体组成的群体在指定的选择规则下进化到最大化“适应度”(即最小化成本函数)的状态。

train (OptimizationAlgorithm oa, BackPropagationNetwork network, String oaName) { // 
     for(int i = 0; i < trainingIterations; i++) { 
         oa.train(); double error = 0; 
         for(int j = 0; j < instances.length; j++) { 
         if(instances[j] == null || instances[j].getData() == 
                  null) continue;  
            network.setInputValues(instances[j].getData());  
            network.run(); Instance output = 
                 instances[j].getLabel(), example = new   
                Instance(network.getOutputValues()); 
            example.setLabel(new    
Instance(Double.parseDouble(network.getOutputValues().toString()))); error += measure.value(output, example); 
} } }

一旦我们运行算法的组合,我们观察到一些非常有趣的 w.r.t .当前数据集。

  • 遗传算法提供了一个最佳变量列表,而不仅仅是一个单一的解决方案。它的随机探索(通过交叉)技术可以找到其他局部搜索算法找不到的解决方案。随机突变避免了陷入局部最小值的问题。
  • 遗传算法同时从大范围的成本面中搜索,处理大量的变量。所以它非常适合并行计算,
  • GA 优化了具有极其复杂的成本表面的变量(它们可以跳出局部最小值)
  • 当样本量为 200,配对节点数为 100,变异节点数为 10 时,遗传算法需要更长的时间才能达到高精度
  • 如果我们将种群规模设置为 100,交配节点设置为 50,变异节点设置为 20,那么 GA 会工作得更快,并达到同样高的精度。
  • 在某些情况下,寻找性能良好的凸函数的解的基于微积分的导数可以优于遗传算法,因为遗传算法可能仍然需要花费周期来分析初始种群的成本,因为它需要为实值数据提供更多位。
  • 爬山搜索的主要好处是它只需要有限的内存。
  • 我们需要选择一个局部变化来提高目标函数的当前值。
  • 爬山搜索的主要好处是它只需要有限的内存。这里,我们需要选择一个局部变化,它应该提高目标函数的当前值。像连续空间中的梯度下降算法一样,它接近局部极小值,但是不使用梯度,爬山使用随机局部搜索。HA 不同于梯度下降算法,梯度下降算法根据山的梯度在每次迭代中调整随机变量 X 中的所有值。
  • 一旦迭代次数达到 100,RHC 就可以正确地分类 95%的实例,因为它可以更好地探索更多的邻居,并越过山脊和高原。
  • 模拟退火是随机局部搜索的一个例子。如果温度降低 0.1(慢得多),迭代次数为 10,模拟退火的性能提高很多。性能提高的主要原因是“探索非最佳路径的移动频率和这种非最佳空间(平台)的大小随着我们降低温度而减小”。
  • 因此,虽然 GA 在节点变异增加时表现更好,但 RHC 需要大量的迭代,而 SA 以较少的迭代次数和较慢的温度变化收敛。

到目前为止,我们已经使用 Weka 和 ABAGAIL 开发了一个基于 Java 的框架,用于监督、非监督和随机搜索算法的实验。在下一篇博客中,我们将讨论强化学习算法的实验方法。

使用 Flexdashboard 在 R 中构建 HR 仪表板

原文:https://towardsdatascience.com/building-an-hr-dashboard-in-r-using-flexdashboard-76d14ed3f32?source=collection_archive---------0-----------------------

The HR Dashboard

假设你是一名人力资源经理,你的老板要求你为首席执行官准备一份关于本年度公司员工招聘和流失的演示文稿。哦,你很兴奋!你已经在这个项目上工作了一段时间,并且已经整理好了所有的数据。你迫不及待地想用你的洞察力让 CEO 眼花缭乱。但是等一下,她非常忙,而你只有五分钟的时间。你想给她看的东西时间太少了!

你是做什么的?什么工具对你最有帮助?你会带着 25 张幻灯片参加 5 分钟的会议吗?您是否会将多个 Excel 工作簿投射给一个擅长分析简明数据的人?

请记住:

枪战时不要带刀

在这种情况下,我的朋友,你唯一需要的枪就是仪表板。当您希望可视化大量复杂数据,并向用户提供与特定目标或业务流程 相关的关键绩效指标的一览视图时,仪表板是完美的工具。

r 给了我们漂亮的软件包“flexdashboard ”,它为我们提供了一个易于创建的动态仪表板。我们将在一个 R markdown 文件中写出代码,这是一种在 R 中创建动态文件的文件格式

以下是我们将要介绍的内容:

  1. 仪表板结构概述
  2. 创建一个 R 降价文件
  3. 使用 flexdashboard 的仪表板布局
  4. 使用 dplyr 进行数据操作
  5. 使用 plotly 合并情节

仪表板结构概述

假设您的公司在三个地区拥有子公司:

  • 美洲
  • APAC(亚太地区)
  • 欧洲

因此,我们有三个区域文件(Excel 工作簿),其中包含相关的数据字段,如姓名、国家、电子邮件地址等。区域团队为每个新员工和离职(自然减员)输入数据。我们需要一个仪表板,它能有效地为执行管理层可视化这些信息。这包括月度趋势、跨国家和内部职能部门的划分等。

您可以在这里访问包含模拟数据的文件:https://github . com/sagarkulkarny/HR-Movement-Dashboard-using-flex Dashboard-

这个想法是读取单个文件,合并它们,然后操纵数据得到图。所以让我们开始吧!

创建一个 R 降价文件

打开 RStudio 并创建一个新的 R 脚本文件。使用install.packages(“flexdashboard”)安装 flexdashboard 库

软件包安装完成后,通过选择“文件”->“新建文件”->“R markdown”创建一个新的 R Markdown 文件,如下所示:

Fig. 2.1: Create a new R markdown file

系统会提示您输入文件名。现在,如果你想为任何目的创建一个降价文件,你可以点击 OK,你就可以开始了。

Fig. 2.2: Creating a new R markdown file

然而,Flex Dashboard 也为我们的仪表板提供了一个模板。如果您选择“来自模板”,然后选择“Flex Dashboard”,您将获得一个新的带有占位符的降价文件,如下所示:

Fig. 2.3: Selecting flex dashboard template

Fig. 2.4 flex dashboard template

您可以看到该文件没有标题,因此我们必须将它保存在我们的系统中。将它保存在与我们的区域文件相同的文件夹中是有意义的,但是您可以将它存储在您喜欢的任何地方。保存文件后,我们可以试着运行它,看看模板占位符有什么作用。

通常,为了运行 R 脚本,我们只需选择代码并在控制台中运行它。然而,R Markdown 文件在控制台之外运行,因为它们根据文件输出 HTML 内容或 PDF 或 word 文件。要查看这个输出,我们需要通过点击文件顶部附近的编织图标或按下Ctrl+Shift+K来"编织"文件。

Fig 2.5: Basic layout using the template place holders

恭喜你!我们刚刚创建了第一个仪表板布局!它什么都没有的事实应该无关紧要!

使用 flexdashboard 的仪表板布局

在这一步中,我们将决定仪表板的布局,并通过在 R markdown 文件中编码来实现它。

我们先概念化一下布局。我们可以有一个单页的仪表板,但这将意味着在一个小空间里塞满了大量的信息。Flexdashboard 为我们提供了一个选项,通过使用===标题或仅使用#来划分代码中的部分,让多页出现在我们的仪表板上。让我们在模板中尝试一下。让我们在三个位置添加多个页眉:第一个Column {}前的“第一页”,第二个Column{}前的“第二页”,最后的“第三页”。要命名页面,您只需在===标题上方的行中写下名称,如下所示:

编织代码现在在仪表板上显示三页,第一页包含图表 A,第二页包含图表 B 和 C,第三页是空白的。你能相信给仪表板添加“多页”功能如此简单吗?简单是 R Markdown 和 flex dashboard 的旗舰工具之一,使用 R Markdown 意味着您可以简单地完成许多令人惊叹的事情。

在仪表板上有一个主页来显示新员工和自然减员的要点,并为新员工和自然减员提供单独的页面是有意义的,如果我们想深入了解一下,可以去那里。让我们相应地重命名我们的页面,即第 1 页为“Dash”,第 2 页为“New Hires”,第 3 页为“Attrition”。

现在,在一个页面中,布局方向基于行或列(或行和列)。可以使用---标题或##添加新的行或列。在这个项目中,我将使用行布局。为此,我们将对代码进行两处修改:

  1. 将块封装在顶部的---中的 YAML 标题中的方向从列改为行
  2. 将列/行标题中的所有列更改为行

The YAML header

Column changes to Row

您可以在这里查看可能的选项:https://rmarkdown . r studio . com/flex dashboard/using . html # layout

布局的下一步是放置实际的图表。我们可以为地块创建空间,并使用###标题后跟空间名称来命名它们。这些空间被称为盒子,你可以在上面的代码中看到名为图表 A、图表 B 和图表 c 的例子。

总结布局标题:

  1. 多页页眉或一级页眉:===#
  2. 行列表头或二级表头:---##
  3. 箱式表头或三级表头:###

数据操作

现在我们已经有了布局,在开始在仪表板上绘制图表之前,我们需要将数据整理好。到目前为止,我们所有的代码都是 HTML 布局。但是,我们在哪里编写负责所有幕后工作的实际 R 脚本代码呢?

在 R markdown 中,这样的代码需要由分隔符````r{}` 和`````封装,也称为代码块分隔符。R markdown 的工作方式是将代码块末尾的结果嵌入到 HTML dashboard 输出中。

在您的新文件中,您可以看到有四个这样的代码块。第一个代码块在已经有library(flexdashboard){r setup}下。我们将使用这个块来编写代码,以读取和合并我们的区域文件。我们将使用其他块为情节编写代码。

让我们从调用所有必需的包并读取我们各自的数据文件开始:

Packages and reading the data files

上面的代码还读取一个文件“country coord ”,该文件包含数据中国家的经度和纬度数据。在绘制世界地图的时候会很有用。

rbind()函数合并来自三个文件的数据,并将它们一个贴在另一个之上,以创建一个全局数据集。建议去过一遍资料,熟悉一下结构。注意数据的类型(连续的、离散的)、列的值(例如,进入/退出在列“运动类型”中被捕获,我们将广泛使用该列来过滤数据)等。

现在我们已经有了必要的数据,我们可以开始用可视化填充我们的仪表板。我们第一页“Dash”上的第一个组件是一个值框。

一个值框就像一个标签,显示一个简单的值和一个图标。这些是我们仪表板中的价值箱:

Fig. 3.1 Value box

我们将在第一个代码块中编写这些代码,如下所示:

让我们一行一行地回顾一下。

  • 您会注意到 Dash 页面标题的标签旁边有一个小小的地球图标。那是由{data-icon=“fa-global”}获得的。“fa”代表字体牛逼这是一个免费图标的网站。您还可以查看所有受 Flex Dashboard 支持的免费选项。
  • 注意不同的输出使用了不同的标题(页面、列和代码块)。
  • 使用 dplyr 的过滤函数和管道运算符计算新雇员数
  • ValueBox()函数接受数值、图标、标题和颜色参数。注意页眉中类似于地球图标的图标的“fa-user-plus”值。
  • “净变化”值框的逻辑略有不同。由于净变化(新聘人数-离职人数)可能是正数,也可能是负数,我想相应地改变图标,即向上箭头表示正净值,向下箭头表示负净值。这是通过使用 r 中的if-else循环完成的

使用 plotly 合并情节

在值框之后是我们的两个图:趋势图和世界地图图。

Fig 3.2: Movement by month and by region plots

让我们看看下面的代码:

对于“按月移动”图,我们首先按“月”和“移动类型”对数据集进行分组,然后进行总结。这将创建下表:

Fig. 3.3: Tibble for “Movement by Region” plot

我们先试着在脑海中构建剧情。我们将月作为 x 轴,计数作为 y 轴,两条线显示趋势,一条代表进场,一条代表出场。一旦我们框定了这个顺序,使用 plotly(或 plot_ly()编写代码块)创建一个情节就非常简单了。

我们需要记住这一点:

  • 它试图建立在 ggplot2 所使用的图形语法之上。因此,每个后续函数都将前一个函数作为第一个参数。因此,我们需要为 plotly 函数使用管道操作符%>%来代替 ggplot2 中使用的+
  • 因此,创建上述情节的最简单方法是:

我们包含了hoverinfotext 参数,当鼠标指针悬停在图上时,该图显示计数。

此外,记住通过调用 plot 来结束代码块,在本例中是p1 ,否则仪表板输出上的绘图空间将保持空白。

我们“Dash”页面上的最后一个图是世界地图图。这个绘图函数将地理坐标(经度和纬度)作为 x 和 y 坐标。因此,我通过执行左连接将“country coord”excel 工作簿中的两列添加到全局数据集。对此的解释包含在代码中。

在 plotly 中指定世界地图的方法是将locationmode 参数设置为“world”。然后我们将 long 和 lat 值分别作为xy 坐标传递。圆的size 设置为count 的值,而color 设置为EntryExit

不知不觉中,您已经完成了我们仪表板的整个“Dash”页面!

Fig. 3.3: The “Dash” page

与 ggplot2 相比,使用 plotly 的一个好处是前者为我们提供了图形的动态表示,我们可以在其中切换标签,图形相应地做出响应。例如,点击“按月移动”图图例中的“退出”,取消选择它,只显示“进入”趋势。

Fig. 3.4: Dynamic nature of plotly plots (you can see that “Exit” has grayed out in the legend)

我填充了接下来的两页,在这两页中,我加入了几个甜甜圈图,作为对通常的值框和条形图的补充。逻辑非常简单,与上面解释的相似。

Fig. 3.5: New Hire page

Fig. 3.6: Attrition page

结论

祝贺您创建了第一个仪表板!我希望这是以后更多的开始!

如果您有兴趣进一步研究这个问题,您可以在此处访问整个rmd 文件和支持工作簿:https://github . com/sagarkulkarny/HR-Movement-Dashboard-using-flex Dashboard-

Flex Dashboard 包含了很多我在这篇文章中没有涉及到的组件,比如标尺、导航条、故事板等等。它是在 r 中创建仪表板的更易于使用和直观的包之一。

我希望我至少能够对所涉及的概念进行一些澄清。不言而喻,我们欢迎并感谢所有的建议和反馈。

参考

  • https://rmarkdown.rstudio.com/flexdashboard/using.html
  • https://rmarkdown.rstudio.com/lesson-1.html
  • 【https://plotly-r.com/

附:这是我的第一篇帖子,我感谢大卫·罗宾逊方差解释、马纳利·辛德、德里克·姆维蒂、朱利安·塔格尔和卡特诺里亚他们令人振奋的文章。如果你喜欢我的帖子,你也会喜欢他们的!

使用单词和句子嵌入构建新闻文章的图像推荐系统

原文:https://towardsdatascience.com/building-an-image-recommendation-system-for-news-articles-using-word-and-sentence-embeddings-a5acb485a445?source=collection_archive---------27-----------------------

Source: https://miro.medium.com/max/1200/1*9B7S9mipwLURUX9wdZN7Pw.jpeg

作者:Karina Huang、Dianne Lee、Abhimanyu Vasishth 和黄家仪(按姓氏字母顺序排列)

本文所表达的观点仅代表作者的观点,不代表美联社的观点。

简介

这篇博文展示了我们在哈佛 AC297r 课堂上与美联社 (AP)合作的顶点项目的一部分。我们为任何给定的新闻标题建立了一个文本到图像的推荐系统。该项目的目标如下:

给定一篇文章的标题,使用标题推荐一组最匹配的图片。

由于机器学习方法不能直接优化文本,我们需要一种方法来将文本转换为数字表示。对于这项任务,我们使用单词嵌入,这是一种可以将单词表示为数字向量的方法。

在下面的部分中,我们使用两种不同类型的嵌入来讨论我们的方法 1)平均手套单词嵌入,和 2)通用句子编码器(USE)来编码文章标题和图像标题,随后是推荐过程和例子。

数据

由于我们用于项目的数据是我们的行业合作伙伴的专有数据,为了这篇博文的目的,我们使用了一个包含新闻文章及其相关图像的公开数据集,以使我们的方法具有可重复性。我们下载了由 Biten,A. F .、Gomez,l .、Rusinol,m .、& Karatzas,D. (2019) 提供的数据。然后,我们使用下面的代码块提取文章标题和图片标题。图片由原作者在这里提供。

在通过排除标题中少于 5 个单词的文章和标题少于 5 个单词的图像来清理数据之后,最终的数据集有 276596 篇新闻文章,431841 幅相关图像。

我们使用了 WordCloud Python 库来创建文章标题(左)和图片标题(右)中的单词云。

这是数据集汇总统计数据的一些可视化效果。

手套词嵌入

来自斯坦福 NLP 网站关于手套嵌入:

“GloVe 是一种无监督学习算法,用于获得单词的矢量表示。在来自语料库的聚合的全局单词-单词共现统计上执行训练,并且产生的表示展示了单词向量空间的有趣的线性子结构。”

一种简单的单词嵌入方法是为英语中所有可能的单词创建一个长的 0 向量。然后,给定一个单词,在该单词对应的向量的索引中填入 1。这方面的一个例子如下:

使用这个例子,单词“water”的嵌入将是[0,1,0,0,…,0],单词“table”的嵌入将是[0,0,0,1,…,0]。但我们可以立即看出,这不是一个好主意,原因有多种:

  1. 向量好长啊!英语中有成千上万的单词(包括专有名词),将每个单词存储为如此长的向量将占用大量的内存,并使任何 ML 算法都非常慢。
  2. 语义信息不会被保留。这意味着对应于该单词的索引并不真正传达任何意义。“气球”和“水”相差一个位置,但这是非常不同的词。
  3. 同样,两个单词之间的距离并不能让你知道这两个单词在语义上有多相似。

由于这些原因,开发了手套嵌入物。这些是在一个巨大的单词语料库上以无监督的方式进行训练的,使用单词的共现,即不同的单词对一起出现的次数。这让你感觉到这两个词是如何相关的。生成的嵌入集具有一些简洁的属性,如下图所示:

来自手套镶嵌的创作者:

“区分男人和女人的基本概念,即性或性别,可以由各种其他词对等同地指定,例如国王和王后或兄弟和姐妹。为了从数学上说明这一观察结果,我们可能会认为,男人——女人、国王——王后和兄弟——姐妹之间的矢量差可能都大致相等。这类似于矢量强-更强、清晰-更清晰等的差异。”

我们下载了预训练的维基百科 2014 + Gigaword 5 手套单词嵌入,其词汇表中有超过 400000 个单词。预先训练好的嵌入可以从斯坦福 NLP 网站下载。叫做 glove.6B.zip. 在这个文件夹里解压后,你会发现几个文件。这些包含 50 维、100d、200d、& 300d 的嵌入,下载量为 822 MB。要加载下载的手套嵌入,请遵循以下代码:

平均手套单词嵌入以创建句子嵌入

现在的问题是:鉴于我们知道如何将单个单词作为数字向量嵌入,我们如何将其扩展到新闻标题,即由多个单词组成的句子?

对句子嵌入的直观期望是,它应该从句子中的所有单词中捕获隐藏的特征。因此,我们可以通过连接或平均单词的单词嵌入来为给定的句子生成嵌入。然而,取决于单词嵌入的维度,拼接可能并不理想。句子嵌入的维数将随着字数的增加而成倍增加。此外,连接将要求所有输入句子具有相同数量的单词,这可能会由于单词减少而导致重要信息的丢失。

我们通过平均预训练的手套单词嵌入来为文章标题和图像标题生成句子嵌入。我们在进行一些预处理后这样做,其中包括:

  1. 将句子中的所有单词转换成小写
  2. 去掉句子中不必要的标点
  3. 从句子中删除停用词。这些词包括“the”、“not”、“my”等。我们之前做词云的时候,已经从词云库下载了一个停用词列表。

有关实现,请参见下面的代码:

为了避免输入文章标题和图像标题之间的重复成对比较,我们在一个矩阵中检索并保存所有的图像标题嵌入。以下代码创建了带有手套嵌入的图像标题嵌入矩阵:

通用语句编码器

使用平均手套嵌入当然是获得句子嵌入的好方法,但是,你可能已经注意到这是一种上下文无关的方法。也就是说,我们不考虑句子中单词的顺序。

一句“老虎吃人”的平均手套嵌入和一句“人吃老虎”的平均手套嵌入是一样的!

因此,我们需要一种方法来使用单词在句子中的上下文,以便创建一个更全面的句子嵌入集。

谷歌研究在 2017 年推出了通用句子编码器(USE),它被训练成一个通用的语言理解模型。下面的流程图演示了使用深度平均网络(DAN)训练的训练过程,深度平均网络是一系列前馈神经网络。给定一个输入句子,DAN 被训练成将句子中所有单词的平均嵌入编码成 1×N 向量,这实际上是 DAN 的最后一个 softmax 层。通过评估不同语言任务(例如,情感分析)中的句子嵌入向量来更新网络权重。

实际上,网络可以学习仅从平均单词嵌入中捕获句法和语义信息。

Figure source (the paper on USE embeddings by Cer et al.)

我们嵌入了文章标题和图片标题,并预先训练了用法。该模型在 tensorflow hub 上开源。对于任意长度的输入文本,使用 outputs 语句嵌入的维度为 1 x 512。下面的代码显示了如何访问模型。用户端不需要任何预处理!

同样,我们可以检索和保存所有图像标题嵌入:

请注意,tensorflow hub 上有种不同的使用模型,以及不同版本的使用模型。模型随 tensorflow 版本更新而更新,并针对不同的语言任务进行了优化。我们使用的版本是版本 4 。或者,你也可以探索一种使用 Transformer encoder 训练的变体(版本 5 )。据报道,Transformer 编码器在迁移学习任务上的表现优于 DAN 编码器,但消耗了大量的内存和计算资源。

推荐

为了推荐图像,我们计算图像标题嵌入和文章标题嵌入之间的余弦相似度。然后根据计算的余弦相似度按降序排列推荐图像。余弦相似度越高,图像标题与文章标题的语义相似度越高。来自维基百科页面关于余弦相似度:

我们将嵌入向量归一化为单位范数,因此比较将只是点积。下面的代码显示了使用 GloVe 和 USE 嵌入的推荐过程:

结果

以下是手套和使用嵌入模型推荐的图片。因为这个数据集由实际的新闻文章组成,所以我们提供了相应新闻文章的 URL。

第 1 条标题:在瓦拉瓦拉的葡萄酒之乡重塑形象

手套预测:

使用预测:

正如您所看到的,两个模型都推荐与葡萄酒或葡萄酒葡萄园相关的图像,这些图像在语义上与新闻标题中的“葡萄酒”相似。

结论

在这篇博文中,我们演示了如何使用 GloVe 和嵌入来编码一个句子。使用句子嵌入,我们可以计算句子之间的语义相似度,并使用它来推荐新闻文章的图像。完整的实现在我们的 jupyter 笔记本上这里。

感谢

我们要感谢我们在哈佛的导师 Pavlos Protopapas 和 Isaac Slavitt,感谢他们整个学期的指导。我们还要感谢我们的合作者,美联社的韦罗妮卡·兹琳斯卡和戴维·福克斯,感谢他们的支持。

参考

Biten,A. F .,Gomez,l .,Rusinol,m .,& Karatzas,D. (2019)。好消息,各位!新闻图像的上下文驱动实体感知字幕。在IEEE 计算机视觉和模式识别会议记录(第 12466–12475 页)。

Cer,d .,Yang,y .,Kong,S. Y .,Hua,n .,Limtiaco,n .,John,R. S .,和 Sung,Y. H. (2018 年)。通用句子编码器。 arXiv 预印本 arXiv:1803.11175

在 AWS DeepLens 和 Slack 上用几个小时构建一个交互式计算机视觉演示

原文:https://towardsdatascience.com/building-an-interactive-computer-vision-demo-in-a-few-hours-on-aws-deeplens-38ff48c7ddbd?source=collection_archive---------16-----------------------

一步一步的指南来建立一个演示,可以帮助您解释您对孩子做了什么,给客户留下深刻印象,或通过检测人来自动操作

几个月前,我发表了一篇文章,向我女儿 3 岁的学前班解释我作为技术顾问的工作。这些天我所做的事情中比较容易理解的一部分是研究计算机视觉问题。人们(甚至是蹒跚学步的孩子)天生就知道识别你面前的东西。有趣的是,正如我通过这次经历所了解到的,计算机视觉对于一个蹒跚学步的孩子来说实际上比这篇文章的大多数读者更容易理解。我女儿这一代人将是第一代带着这样的期望长大的人,他们可以像与他人互动一样与电脑互动。当电脑能说出孩子们的名字时,他们一点也不惊讶,这让我很震惊。

随着今天深度学习、云平台和 IOT 相机技术的进步,我们能够快速轻松地将这种能力带到计算机上。在本文中,我将介绍我用来构建实时计算机视觉系统来问候人们的架构和代码。这个想法、架构和代码的大部分功劳归于桑德·范·德格拉夫和他的看门人参加 AWS DeepLens 挑战赛。我将假设您理解编码原则,我的目标是您可以遵循这些原则来构建自己的演示。让我们开始吧…

架构概述

这个系统使用了大量的 AWS 无服务器技术(这里不需要配置网络或操作系统)。 DeepLens 已经通过 GreenGrass 接口与 AWS Lambda 集成,该接口将功能代码运行到设备上。该架构的其余部分使用云原生的事件驱动处理。从 DeepLens 上传的人的图像在 S3 到处移动,这使用 Lambda 函数触发适当的动作。这有一个很好的副作用,就是为了下游调试和分析的目的对图像进行排序。类似地, API 网关处理来自 Slack API 的入站 HTTP 请求,再次委托 lambda 函数进行处理。实际的人检测功能由 AWS Rekognition 处理,它为面部识别提供全自动的机器学习服务。SNS 主题和 SQS 队列允许附加应用对检测到的人做出反应。在这个例子中,一个控制台应用程序使用 AWS Polly 与被发现的人对话。

实际的工作流程是这样的:

  1. DeepLens 正在制作一个 Find Person Lambda 函数,该函数通过一个模型循环每一帧,以检测一个人,然后识别图像中是否有人脸。一旦 DeepLens 在画面中识别出一个人,它就会将图像上传到一个加密的 S3 桶位置。
  2. Guess Lambda 函数被配置为在 S3 上传时触发,它将图像传递给 Rekognition,以使用亚马逊预先训练的面部识别算法来确定图像与哪个 Slack 用户匹配。
  3. 当一个新人的第一张图像到达时,Rekognition 很可能找不到匹配,因此 Lambda 函数将图像移动到 S3 的一个未知文件夹中进行进一步处理,设置适当的权限以便 Slack 能够显示它。
  4. 一旦图像被放入 S3,未知的λ函数被触发。它向附加未知图像的 Slack API 发送一条消息,Slack API 向预先配置的 Slack 通道发送一条消息,询问图像中是哪个 Slack 用户。
  5. 一旦团队中的某个人识别出与图像相关联的 slack 用户,Slack 就向 AWS API Gateway 发回一条消息,这将触发 Train Lambda 函数来告诉 Rekognition 用户是谁。
  6. 下一次 DeepLens 上传 Slack 中已识别的人的图像时,Rekognition 可能会在 Guess Lambda 函数中匹配该人,这两个函数都会将已识别的人发布到 Slack 频道,以允许用户在必要时纠正识别。
  7. 该应用还向 SNS 主题发送消息,包括用户信息和附加的情绪检测信息。订阅 SNS 主题的 SQS 队列允许应用程序处理这些信息。一个简单的控制台应用程序监听 SQS 队列,并使用 AWS Polly 来问候这个人,并记录他们表现出的情绪。

设置服务

首先,您的开发环境需要具备以下条件:

  1. Git 访问:您必须生成一个 SSH 密钥并将其添加到您的 GitHub 帐户中
  2. NodeJS&python 3(带 pip3 & pipenv)
  3. 无服务器框架 &无服务器-python-需求插件。
  4. AWS CLI ,以及它们的依赖项,安装在您的开发机器上。

以下说明应适用于 AWS Cloud9 。它预装了 NodeJS、Python 和 AWS CLI(更新到您的电子邮件):

#Setup SSH Key for Git Access
ssh-keygen -t rsa -b 4096 -C "[your_email@example.com](mailto:your_email@example.com)"
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_rsa
cat ~/.ssh/id_rsa.pub
#Copy paste the resulting token to add it to your account

您现在需要将生成的令牌复制粘贴到并添加到您的 GitHub 帐户中,这样您就可以认证您的 Cloud9 环境了。请注意,这将允许您的 Cloud9 环境中的任何人访问您的 GitHub 帐户,因此使用 Cloud9 共享功能时要小心。

# Clone Github repo
git clone [git@github.com](mailto:git@github.com):ryano144/doorman.git
cd doorman#Install Serverless and the AWS CLI
npm install -g serverless 
sudo yum install jq 
pip install --upgrade awscli

最后,您将需要访问 Slack 。如果你还没有在工作中使用它,你可以建立一个免费账户来测试这个功能(当我写这篇文章的时候,你只需要输入你的电子邮件,然后点击开始)。然后你需要在 Slack API 上创建一个与你的工作空间相关的应用。现在,您需要的只是一个名称和指向您将使用的工作区的指针。在本文的后面,您将配置 Slack 应用程序指向您的 AWS 后端。

您将需要激活传入的网络挂钩。这将允许应用程序发布到 Slack。

然后,您需要转到 OAuth 和 Permissions 部分,并选择以下范围(channels:read,chat:write:bot,incoming-webhook,users:read):

接下来,您需要将应用程序安装到您的工作空间:

这将把你带到一个可以安装应用程序的屏幕。你需要记住你为应用程序选择的 slack 频道。

安全说明:该应用程序将获得您工作区中所有用户个人资料的访问权限。这将允许您为 DeepLens 相机上传的每张图片选择一个用户。只有不透明的 Slack 用户 ID 将被发送到 AWS Rekognition,但是通过您的访问令牌,用户可以从 slack API 获得额外的配置文件信息。故事的寓意:一定要保护好你的 Slack API 令牌。为了增加安全性,如果 Slack 在 GitHub 这样的公共网站上发现你的访问令牌,他们会自动为你停用它。

一旦设置了 OAuth 权限,您就可以从安装应用程序或 OAuth & Permissions 页面复制您的访问令牌。

无服务器部署包使用几个环境变量来允许部署指向多个帐户的相同代码。为了简化部署,您可以创建一个 shell 脚本。我称之为 mine environment.sh,并将在下面的脚本中引用它。你会在许多脚本中看到对的引用

安全说明:不要将此文件提交给源代码管理。另外,如果您使用共享的 Cloud9 环境,那么您应该只在终端中直接输入 slack API token 环境变量。

export name=<your-slack-app-name>
export BUCKET_NAME=doorman-$name
export REKOGNITION_COLLECTION_ID=doorman-$name
export SLACK_API_TOKEN=<your-slack-api-token>  
export SLACK_CHANNEL_ID=guidepost-$name
export SLACK_TRAINING_CHANNEL_ID=guidepost-$name
export AWS_ACCOUNT_NUMBER=$(curl -s [http://169.254.169.254/latest/dynamic/instance-identity/document](http://169.254.169.254/latest/dynamic/instance-identity/document) | jq -r .accountId)

按照上面链接的指南,你应该有一个工作的开发环境。您可以通过运行以下命令将完整的架构部署到 AWS 来测试它:

source environment.sh
aws s3 mb s3://doorman-$name
aws rekognition create-collection --collection-id doorman-$namepip install --user pipenv
sls plugin install -n serverless-python-requirementssls deploy

一旦完成部署,您就可以浏览到 CloudFormation 控制台来查看资源。你现在需要更新你的 Slack 应用程序,并将 Lambda 函数部署到 DeepLens。对于 slack 应用程序,您需要捕获 API 网关 URL,它应该在成功部署后出现在控制台上:

然后,您需要在 Slack 应用程序中更新此 URL,并从安装应用程序屏幕重新安装它。

你需要的最后一件事是从亚马逊购买一台 DeepLens 相机。如果你参加了 re:Invent 2017 并参加了任何 ML 会议,你很可能有一个。如果你没有,你可以在亚马逊上花 250 美元买一个。相机是一个预包装的包,本质上是一台运行 AWS GreenGrass 的迷你电脑,连接到一台摄像机。这款相机可以轻松部署计算机视觉模型来运行直播视频,特别是如果你已经熟悉 AWS Lambda 的话。我假设你已经完成了设置指南,你的 DeepLens 现在已经连接到互联网。您肯定应该获取客户端证书,以便在浏览器中查看视频提要。

完成这些步骤后,您应该浏览到 DeepLens 控制台。您需要使用对象检测模板创建一个项目。一旦你选择了这个,你将点击创建应用程序,然后编辑它以关联查找人 lambda 函数。接下来,在项目列表中选择它,并单击 Deploy to Device。从那里,选择您注册的 DeepLens,并将其推送到设备。完成后,您会看到 DeepLens 控制台顶部有一个绿色条,让您知道项目已经成功部署。

恭喜你!此时,您应该拥有一个功能齐全的系统。此时,您可以连接到设备的本地视频源。为此,首先在设备详细信息页面上找到 IP 地址,然后浏览到 https:// :4000 查看视频馈送。如果一个人走到摄像机前,摄像机会在他们周围放一个盒子。一旦您看到这种情况发生,您可以浏览到您发送消息的 Slack 通道,并查找带有提示的图像上传,以选择要关联的用户。

That’s me testing detection in the dark (it works)

如果它对你不起作用,请在下面的评论中自由提问。如果有足够多的问题,我将发布后续文章,详细介绍如何在 AWS 上调试这些类型的应用程序。

在 R 中建立线性回归模型预测 AFL 人群

原文:https://towardsdatascience.com/building-an-linear-regression-model-in-r-to-predict-afl-crowds-735b16a1f7c6?source=collection_archive---------17-----------------------

介绍

现代 AFL 有很多关于群体行为和群体问题的讨论。我个人认为,人群问题是一个一切照旧的例子;近 30 年来,我一直断断续续地去看 AFL 的比赛,今天的观众行为与 20 年前没有什么不同……只是现在每个有手机和 Twitter 账户的人都是记者。

无论如何,那是我的两分钱。这种分析不是基于群体行为。这篇文章主要是关于建立一个模型来预测赛季期间 AFL 的人群。虽然数据是自 2000 赛季开始以来收集的,但该模型将建立在从 2013 赛季开始到 2018 赛季结束(总决赛系列除外)的训练数据基础上,然后用训练好的模型来预测 2019 赛季直到 2019 年 7 月 21 日周日结束的第 18 轮 AFL 比赛的出席人数。线性模型将在 RStudio 中使用 R(版本 3.5.3)构建。

这篇文章是我对跟风粉丝所做的介绍性分析的后续。这里可以找到。

这篇文章的灵感来自托尼·考克的惊人工作,特别是他在 AFL 人群的可预测性上的文章。

这个项目的所有代码都可以在 GitHub 这里找到

数据

该项目使用了三个数据来源:

  1. AFL 表格被用作主要信息来源。这无疑是任何想做 AFL 分析的人的首选
  2. 澳大利亚体育博彩用于获取 2013 赛季比赛的博彩数据
  3. 澳大利亚气象局用于气候数据(降雨和温度)。BOM 数据的一部分是从 James Day 的 fitzRoy R 包中取出的。非常感谢他。降雨和温度数据有一些遗漏的观测数据。缺失的雨量数据通过对之前缺失数据点的下一个读数取平均值来填充。缺失的温度用该月的平均温度来填充。

特征工程

AFL 表格中包含的数据是一个丰富的信息来源,但是需要一些步骤来从数据中收集一些额外的信息。

最初的三个数据源包含大约 35 个潜在的变量。然后进行特征工程(创建在预测模型中使用的额外解释变量的过程),总共产生约 95 个特征,以观察它们是否最终会被添加到最终模型中使用。

创建的功能

根据从众文章中得到的一些反馈和我想探索的其他东西,创建了一些新功能来看看它们是否有任何预测能力。

以游戏开始时间为例——有时间是很好的,但回归模型可能会发现将时间分类为一天中的一段时间更好,以减少一些噪音。因此,考虑到这一点,创建了一个特性(time_period)——如果游戏在下午 5 点开始,我将它归类为下午的游戏。在下午 5 点至 7 点之间开始的游戏被归类为晚间游戏,而在此之后的游戏是夜间游戏。

此外,星期几和time_period被加入到一个变量game_time中以在模型中使用。例如,如果是星期六下午 5:30 开始的比赛,则被归类为“星期六晚上”。

不经常使用的场馆,或者更准确地说,不是球队的主要“主场”体育场被标为“其他”。在这些场馆中共有 94 场比赛,其中包括像尤里卡体育场卡扎利体育场特雷格公园以及新西兰的国际场馆惠灵顿和中国的江湾体育场等场馆。

另一个特点是确定竞争游戏——当两个激烈的对手面对面时,观众就会来了。作为竞争游戏,有几种不同的选择。第一个包括四个最老的传统对手,都来自墨尔本——科林伍德、里士满、埃森登和卡尔顿(保留你的卡尔顿观点,我知道他们很糟糕,但他们的球迷仍然会去看这些对抗赛)。南美洲的德比(阿德莱德和阿德莱德港之间的对决)和西澳大利亚的德比(西海岸和弗里曼特尔之间的西部德比)也被归类为对抗赛。

几年前,我听到一位重要的 AFL 工作人员谈论博彩市场是上座率的一个强有力的预测因素,我收集了博彩数据来测试这一假设。除了收集的原始赔率和线数据之外,还创建了许多特征。这些包括计算主队和客队赔率之间的差异,以及计算一周内赌注的变化。

围绕主队和客场队的平均体验也创造了一些特色,里程碑式的比赛也是如此(每支球队的球员都参加了他们的第 100 场、200 场、250 场或 300 场比赛)。

计算每个队的最后一场比赛结果,以及每个队在前三场比赛中的获胜次数。此外,球队是否在前一个赛季打总决赛也被确定。

每轮都创建了一个阶梯,并探索了阶梯位置、百分比和得分作为最终模型的可能特征。

还创建了一个功能来识别来自同一州或不同州的团队所玩的游戏。

探索性数据分析

在我们开始构建模型之前,我们需要了解响应变量和预测变量。最有效的方法是通过可视化,什么包比ggplot2更好用!

这一部分将包含一些探索性的数据分析(通常称为“EDA”),这是任何数据分析项目中自然的第一步。这是一个极其重要的步骤,它允许分析师检查数据——目标变量和预测变量的形状,预测模型中可能包含的变量之间是否有任何相关性,数据中是否有任何异常值,或任何缺失的数据……您明白了,EDA 非常重要!

嗯,在所使用的数据集中没有丢失的数据……这些数据都已经在预处理阶段被清除,可以在这个 R 代码中找到。

响应变量— Attendance

让我们看看我们的反应变量——或者我们试图预测的东西——attendance在 AFL 超级联赛赛季比赛中。

我们可以看到出席人数多少有些偏正。我们将会看到一些高上座率的游戏是否会引起一些问题。这个变量可能需要进行某种形式的转换,但稍后会详细介绍。

自 2013 年以来,AFL 常规赛的平均上座率为 1340 场,略低于 32500 场。

一些关键预测

data_for_model <- afl_premiership_season %>%
  select(season, attendance, team1, team2, round, venue, game_time, home_median_experience, home_mean_experience, away_median_experience, away_mean_experience, home_300, home_milestone, away_300, away_milestone, count_milestones, rainfall_clean, min_temp, max_temp, last_five_days_rain, temperature_diff, HomeOddsOpen, AwayOddsOpen, HomeLineOpen, AwayLineOpen, odds_diff, rivalry_game, HomeTeamFav, team1_last_result, team2_last_result, split_round, home_team_finals_last_season, away_team_finals_last_season, season_stage, temperature, IsHomeMilestone, IsAwayMilestone, count_milestones, last_results, home_team_state, away_team_state, is_same_state, temperature_diff, home_odds_change, away_odds_change, home_line_change, away_line_change, second_home_game, home_season_points, home_score_for, home_percentage, home_ladder_pos, away_wins_last_three, away_season_points, away_score_for, away_percentage, away_ladder_pos, teams_in_eight, finals_last_season, home_wins_last_three, away_wins_last_three) %>% na.omit()

正如在本系列的第一篇文章中所看到的,参赛队伍之间的出席率有明显的差异,而且参赛队伍在之前的比赛中是赢是输。此外,第一个帖子显示,主队的偏袒地位似乎也与出勤率有关。

分类特征:

我们预计会对人群数据产生影响的一些关键分类变量如下所示。

一些观察:

  • 比赛地点和出席人数之间似乎有关系。被归类为Other的体育场包括第二主场地和 2019 年未使用的一些椭圆形场地。在这些体育场举行的 1340 场比赛中有 238 场
  • 去年是主队还是客场打总决赛,似乎和上座率关系不大。在我们的模型中包含以下内容可能是有用的
  • 玩游戏的时间和出勤率似乎是相关的。毫无疑问,Weekday AfternoonFriday Afternoon因为埃森登和科林伍德之间的澳新军团日冲突而高涨。周五和周日晚上的比赛似乎吸引了更多的观众。可能是一个有用的预测
  • 在一个赛季的不同轮次之间,出席人数似乎有一些差异。可能是一个有用的预测
  • 被归类为“竞争游戏”的游戏显然比非竞争游戏吸引了更多的观众。可能是一个有用的预测
  • 这一轮是否是分轮(有些队轮休,或者休息一周)似乎与出席率无关。可能不是一个有用的预测
  • 当主队有一名球员打一场里程碑比赛(无论是第 100 场、200 场、250 场还是第 300 场比赛)时,观众人数似乎略高,但这种关系显然不会很强。可能不是一个有用的预测
  • 当两支参赛队伍来自同一个州时,观众人数会更多。可能是一个有用的预测
  • 不出所料,主队在第二主场(塔斯马尼亚的霍桑和诺斯,墨尔本,达尔文的西部牛头犬队等)比赛的观众较少。这可能与体育场容量较低有关,但可能仍然是一个有用的功能

数字特征:

为了探索数字特征和attendance之间的关系,将使用皮尔逊相关。

使用 dplyr 仅选择数字特征,然后计算转换矩阵的相关性,得出以下与attendance的相关性。

numeric_features <- data_for_model %>% select_if(is.numeric)

numeric_features %>% 
  as.matrix() %>% 
  cor() %>% .[,"attendance"] %>% sort(decreasing = T)

虽然所有相关性似乎都相当弱,但可以得出以下结论:

  • 主队的平均经验(home_mean_experience)计算为每个球员在他们参加的每场比赛中参加的平均比赛,产生了与出勤率的最高皮尔逊相关性 0.264
  • 主队的百分比(得分/反对得分)也具有高于 0.2 的相关性,为 0.236
  • 主队在过去三场比赛中获胜的次数具有 0.175 的皮尔逊相关性,而客队平均比赛经验的相关性为 0.159
  • 奇怪的是,主队的阶梯位置呈负相关,皮尔逊相关系数为-0.273。客队的阶梯位置也有负相关-0.150
  • 投注数据显示了一些关系;随着HomeOddsOpen的增大,attendance趋于减小,这多少有些意料之中。odds_diff变量(主队开局赔率与客场球队的差距)也有微弱的负相关关系(相关性-0.168)
  • 天气数据显示了一些关系。不出所料,当天的最高气温和降雨量以及最近五天的累积降雨量与上座率呈负相关(分别为-0.091、-0.081 和-0.65)

构建模型

在任何旨在建立一个机器学习模型来预测一些响应变量的结果的项目中,在训练模型时保留数据的子集是很重要的。这两组通常被称为训练集和测试集。训练集是您传递给模型进行构建的内容,然后根据模型尚未“看到”的数据进行预测,以最好地模拟现实生活中的例子。

在这种情况下,模型将根据 2013 年至 2018 年赛季的数据进行训练(afl_pre_2019),然后根据模型尚未看到的 2019 年数据进行预测,afl_2019。这样做是为了确保模型不会“过度拟合”数据。过度拟合数据的模型通常在看不见的事件上表现更差。

尝试过但未包含在模型中的事物

以下特征被认为具有一定的有效性,但是当运行该模型时,发现没有预测能力:

  • 这两个队在过去三场比赛中的获胜次数并没有起作用
  • 当天的降雨量没有预测能力(但是比赛日之前五天的总降雨量有)
  • 里程碑游戏使模型表现更差

缺少什么/最好有什么

有一些信息是不公开的(或者说我没有试图去获取),这些信息可能会在未来的建模工作中派上用场。其中包括:

  • 每个赛季的门票定价,以探索门票定价对上座率的影响
  • 比赛门票是否发给了球迷,这是否对上座率有影响
  • 任何社交媒体帖子/报纸文本分析,涉及游戏中玩家的团队/话题等
afl_pre_2019 <- data_for_model %>% filter(season < 2019, season >= 2013)

afl_2019 <- data_for_model %>% filter(season == 2019)

基线模型

在建立一个预测游戏的模型之前,我们希望有一个基线模型可以比较。使用每场比赛的平均人群作为基线的诱惑是存在的,但是不同的场馆容量使得这不太相关。因此,为了计算基线模型,我将使用每个场馆的出席率中值(将标有“其他”的场馆的平均值作为一个整体),用于每个主客场球队组合。比如科林伍德(H) vs 埃森登(A)和埃森登(H) vs 科林伍德(A)是不一样的。使用中位数是因为 AFL 人群有轻微的偏斜。

median_attendances <- afl_pre_2019 %>% 
  group_by(team1, team2, venue) %>% 
  summarise(baseline_attendance = median(attendance)) %>% ungroup()

baseline <- afl_2019 %>% select(team1, team2, venue, attendance) %>% 
  left_join(median_attendances, by = c("team1", "team2", "venue")) %>% 
  filter(!is.na(baseline_attendance))

使用每个场地的平均上座率作为我们的基线,获得了 6816 的 RMSE 和 4958 的平均绝对误差。从这里建立的任何模型都需要比这更好。

转换响应变量

由于我们的响应变量有点倾斜的性质,进行某种转换以使其更加正常化可能是明智的。对数变换通常用于“消除”数据偏差。下面是 log 转换考勤变量的结果。

它看起来没有未转换的变量偏斜,然而(剧透),模型表现更差,出席率被对数转换。

使用逐步方法的特征选择

选择哪些特征进入模型的一种流行方法是使用逐步回归。这可以通过以下方式进行:

  • forward ,变量一个一个加,只保留那些提高模型可解释性(R 平方)的变量,
  • 向后,从所有变量开始,如果它们不能增加模型的可解释性,就删除它们,或者
  • both ,是前进后退特征选择的集合。

两个方向上逐步使用最大化模型调整后的 R 平方值为 0.87。

执行线性回归时,我们希望确保变量之间没有共线性。评估的一个方法是计算可变通货膨胀系数(VIF)。这通过car包中的vif函数变得很容易。高于 5 的 VIF 被认为是过高的。

虽然游戏进行的回合刚刚超过 5 (5.7),我们将保持这一点,但是odds_diffhome_score_for将从使用逐步选择的完整模型中删除并重新运行。

full_model <- lm(attendance ~ ., data = afl_pre_2019)

*# Set seed for reproducibility*
set.seed(123)

step.model <- MASS::stepAIC(full_model, direction = "both", 
                      trace = FALSE)car::vif(step.model)

*# the backward selection model include a variable with hi collinearity (VIF = 12). Will recreate model without odds_diff*
full_model_clean <- lm(attendance ~ team1 + team2 + round + venue + game_time + 
    home_milestone + rainfall_clean + min_temp + max_temp + last_five_days_rain + 
    HomeOddsOpen + AwayOddsOpen + HomeLineOpen + 
    rivalry_game + split_round + last_results + is_same_state + 
    home_season_points + away_season_points + 
    away_ladder_pos + teams_in_eight + home_team_finals_last_season, 
    data = afl_pre_2019)

car::vif(full_model_clean)

清洁模型的调整后 R 平方为 0.8652,而 RMSE 为 6,474,MAE 为 4,855。这些结果优于基线模型,但也仅仅如此。

最佳模特

确定“最佳”模型是一项有趣的工作。如果模型的可解释能力是我们所追求的,那么我们希望最大化调整后的 R 平方度量。这种分析旨在最大限度地成功预测人群,因此,均方根误差(RMSE)或平均绝对误差(MAE)是我们将重点关注的指标。这两种方法都可以追溯到响应变量——出勤率,区别在于 MAE 是模型的平均绝对误差(预测和实际之间的差异),而 RMSE 惩罚误差越大的模型。

对于 RMSE 和 MAE,当应用于 2019 赛季比赛时,以下模型导致最低的 RMSE 和 MAE。

set.seed(123)

fit_lm <- lm(attendance ~ team1 + team2 + round + venue + game_time + I(HomeLineOpen * home_ladder_pos) + rivalry_game + last_results + last_five_days_rain + max_temp + min_temp + home_mean_experience + away_mean_experience + is_same_state + home_score_for + I(home_percentage * home_ladder_pos) + I(home_wins_last_three * round) + round * finals_last_season, data = afl_pre_2019)

summary(fit_lm)$adj.r.squared

该模型的调整后 R 平方为 0.858,表明该模型可以解释训练集(2013-2018 年)中 AFL 出勤的几乎 86%的可变性。

交互术语

在这个模型中,特性不仅仅是单独使用。还采用了相互作用术语。以下特征组合在一起形成了一个特征,提高了模型的预测能力:

  • I(HomeLineOpen * home_ladder_pos)
  • I(home_percentage * home_ladder_pos)
  • I(home_wins_last_three * round)
  • round * finals_last_season

分析最佳模型

我们要做的第一件事是确保残差是正态分布的,以满足线性模型的正态性条件。

下面的直方图表明,误差呈正态分布,以零为中心。一个好迹象。

ggplot(data = data.frame(fit_lm$residuals), aes(x= fit_lm.residuals)) + 
  geom_histogram() +
  ggtitle("ERRORS ARE CENTRED AROUND ZERO") +
  scale_x_continuous(labels = comma, name = "Model Residuals")

然后我们可以看看每个预测变量的系数。

为了使检查更容易,使用了broom包。使用tidy()功能,汇总 lm 输出的结果可以很好地显示在 DF 中。

每个预测变量的系数(除了主队和客场队)如下所示。

a <- broom::tidy(fit_lm) %>% mutate(p.value = round(p.value, 5))

a %>% filter(!str_detect(term, "team1"), !str_detect(term, "team2")) %>%  
  kableExtra::kable(format = "html", escape = F) %>%
  kableExtra::kable_styling("striped", full_width = F)

actual_2019crowds <- afl_2019$attendance

*# fit linear model* 
afl_2019$predicted_attendance <- predict(fit_lm, afl_2019)

*# put a floor on AFL attendances - surely the AFL doesn't let the crowd fall below 6,000*
afl_2019$predicted_attendance <- ifelse(afl_2019$predicted_attendance < 6000, 6000, afl_2019$predicted_attendance)

*# calculate the errors*
error <- afl_2019$predicted_attendance - afl_2019$attendance

然后我们要分析模型与关系的拟合程度。这可以通过在散点图上绘制实际值和预测值来实现。

我可以看到,线性模型平均来说做得相当好。该模型确实低估了一些大型抽奖游戏,同时也预测了第八轮在 MCG 举行的山楂与 GWS 比赛的上座率超过 30,000 人,该比赛仅吸引了 14,636 名球迷。

afl_2019 %>% 
  ggplot(aes(x=attendance, y= predicted_attendance)) +
  geom_point() +
  geom_abline(slope = 1, intercept=0) +
  ggtitle("MODEL UNDER-PREDICTED SOME\nLARGE DRAWING GAMES") +
  scale_x_continuous(labels = comma, name = "Actual Attendance") +
  scale_y_continuous(labels = comma, name = "Predicted Attendance") +
  annotate(geom = "text", x=83000, y= 55000, label = "Model under-predicted\non these games")

我们还可以看看模型如何预测每个场馆的人群。

afl_2019 %>% 
  mutate(error = predicted_attendance - attendance) %>% 
  ggplot(aes(x= error)) +
  geom_density() +
  geom_vline(xintercept = 0, linetype = 2) +
  facet_wrap(~ venue, scales = "free_y", ncol = 2) +
  scale_x_continuous(labels = comma, name = "Error") +
  ggtitle("Exploring Errors by Venue") +
  theme(axis.text.y = element_blank(), axis.title.y = element_blank())

该模型倾向于高估加巴、珀斯体育场、悉尼展览场甚至 MCG 的出席人数,而低估卡拉拉和“其他”的出席人数。

模型的整体性能

最终选定的模型实现了 5953 的 RMSE 和 4466 的平均用户体验,这意味着在预测上座率时,模型平均误差不到 4500 个粉丝。与基线模型(RMSE 为 6,816,平均平均误差为 4,958)相比,该模型比基线模型平均高出 500 多人,但也没有太多的极端误差。

结论

希望你已经到这里了!

我很乐意在评论中听到您的反馈/更正或进入 touch 。

本帖原创,发布在不怪数据博客【https://www.dontblamethedata.com】