TowardsDataScience-博客中文翻译-2020-五十二-
TowardsDataScience 博客中文翻译 2020(五十二)
Frontpage Slickdeals 与 Pandas 和 Plotly Express 进行数据分析
Slickdeals.com 是一个受欢迎的交易和优惠券网站。我将展示 web 抓取并使用 Slickdeals 数据集执行数据分析
来自 Pexels 的 Andrea Piacquadio 的照片
人们喜欢在购物时省钱。这些交易信息是有价值的用户行为数据,从中可以检索出有趣的问题和答案。Slickdeals.com 是最受欢迎的交易和优惠券网站之一。Frontpage Slickdeals 是社区成员或 Slickdeals 的编辑在过去 24 小时内推荐的交易。交易范围涵盖各种类别,价格从免费到数千美元不等。
在本文中,我们将探索来自 Slickdeals 的数据集,并使用 Pandas 和 Plotly Express 执行数据分析。我希望这篇文章可以提供一些收集原始数据的有趣例子,以及如何使用 Pandas 和 Plotly Express 进行数据分析和可视化的一些见解。
数据收集
作为一名数据专家,我们需要与数据打交道。执行任何类型的 web 分析的挑战性任务之一是时间。由于任何网站的可用数据都是最新的,因此很难追溯之前发生的事情或进行任何类型的时间旅行分析。例如,在 Slickdeals 主页上,如果我们看到一个有 46 个赞的热门交易,我们不知道它会多快到达这里。在这种情况下,web 抓取工具非常有助于为此目的收集数据。详细内容见文章中的一篇如何发现网络抓取的最佳交易
为了收集足够的数据,我以 15 分钟的间隔运行了两周的网络抓取程序,这应该能够捕捉到不同日期和时间的变化。你可以在这里找到两周的数据。
数据解析和清理
在从网络上收集数据一段时间后,我们处于解析和清理阶段。这里,我们添加了包含列名的原始数据,并将日期时间列的格式从字符串更改为日期格式。pandas 提供的一个便利功能是read_csv
功能,我们可以直接从网络上获取数据,并将其转换为 CSV 文件。
url=”https://raw.githubusercontent.com/ChengzhiZhao/jupyter-notebooks/master/slickdeals_data_file.csv"df=pd.read_csv(url,header=None,names=['datetime','store','title','item_url','deal_price','original_price', ‘like’, ‘comments’])df[‘datetime’] = pd.to_datetime(df[‘datetime’])
数据解析和清理(按作者)
收集数据的数据分析
顶级交易是什么?
在这个 Slickdeals 数据集上可以提出一些有趣的问题。其中一个项目是"在 frontpage 交易中最受欢迎的交易是什么?”。为了找到答案,从 Slickdeal 数据集,我们可以比较一笔交易在 Slickdeals 主页上出现的次数。由于网络抓取每 15 分钟发生一次,显示的时间越长,该项目成为最受欢迎的项目的可能性就越大。
然后,我们可以使用下面的代码对所有的标题进行分组并执行计数,然后按降序对计数进行排序,这里的排序将确保 Plotly Express 按顺序显示行。
frequency_frontdeal = df.groupby(['title'])['title'].agg(['count']).reset_index(drop=False).sort_values(by=['count'],ascending=False)
苹果 AirPods Pro
为了形象化,我们可以使用 Plotly Express 为前 20 项交易创建一个漂亮的条形图。
fig = px.bar(frequency_frontdeal.head(20), x=’title’, y=’count’)
fig.show()
从上面的结果来看,“ 苹果 AirPods Pro ”显然被认为是前两周头版头条中的最佳交易。但是我们算重复的了吗?接下来我们将揭晓答案。
顶级交易的趋势是什么样的?
由于“苹果 AirPods Pro ”是唯一的交易,我们选择这一个进行进一步分析,让我们深入研究并探索这种情况下的趋势。为了更好地了解这项交易的受欢迎程度,其中一个衡量标准是有多少人点击了“喜欢”按钮。Slickdeals 可能会使用“喜欢”按钮作为其推荐功能之一。探索“喜欢”的数量随着日期和时间的增加而增加可能是令人兴奋的。
airpod = df[df[‘title’]==’Apple AirPods Pro w/ Wireless Charging Case’].sort_values(by=[‘like’])
fig = px.line(airpod, x=’datetime’, y=’like’)
fig.show()
然而,在图表上绘制数据后,图表看起来很奇怪,我们原本预计会有一条完美的曲线表明增长,但上面的图表看起来像是数字跳回和强制。好像是在流行的起点,我们跟踪了两个数据点。那么问题来了,“有没有重复数据?
来源:网络来自 knowyourmeme
答案是“是的,原因是 Slickdeals 有两个来自不同商店但标题相同的交易。我们可以看到,威瑞森无线公司在 4 月 17 日第一次在头版刊登了这笔交易。几天后,Google Shopping 赶上来了,也在指定时间段内列在了首页。然后谷歌购物开始了。目前还不清楚 Slickdeal 执行重复数据删除流程的算法是为了优先处理第一笔交易,还是其中一笔交易刚刚输掉了竞争。但我们可以从下面的图表中看到,威瑞森的交易持续时间更长,也更受欢迎。为了验证我们的假设,我们可以在折线图中显示这两家商店,color
是一个参数,我们可以设置它来拆分数据,以显示在不同的组中。
airpod.groupby('store')['store'].count()
# store
# Google Shopping 96
# Verizon Wireless 473# With store
fig = px.line(airpod, x='datetime', y='like',color='store')
fig.show()
两个商店一个交易标题(按作者)
热门交易的点赞和评论之间有关联吗?
在frequency_frontdeal
中,我们可以获得前十行,并在图表中绘制它们。然后我们可以观察点赞数和评论数之间的关系。
fig = px.line(transformed_top10_df, x=’datetime’, y=’count’, color=’type’,facet_row=”title”)
fig.show()
喜欢和评论(按作者)
从上图中,我们可以看到评论和喜欢的两种情况都表现出相似的趋势。有些产品的评论甚至比赞还多,这可能是关于这些交易的多条讨论线索,也可能是这笔交易登上头版的另一个原因。
完整代码
最终想法
我们可以执行更多的分析,并从 web 抓取程序中收集更多的字段。从网上收集数据进行分析既简单又便宜。然而,收集数据进行详细分析需要一些时间。我们可以设置一个运行在树莓 Pi 上的网页抓取工具,收集数据。这种解决方案比从第三方购买数据更加灵活。这篇文章展示了使用 Pandas 和 Plotly Express 来分析我们从 web 上获取的数据并执行分析是多么容易,我希望它展示了一种不同的数据收集和分析方法。
希望这个故事对你有帮助。本文是我的工程&数据科学系列的部分,目前包括以下内容:
数据工程和数据科学故事
View list47 stories
你也可以 订阅我的新文章 或者成为 推荐媒介会员 可以无限制访问媒介上的所有故事。
如果有问题/评论,请不要犹豫,写下这个故事的评论或者通过 Linkedin 或 Twitter 直接联系我。
用循环学习率的力量给你的神经网络加油
永远不要限制你的神经网络的能力。让它探索自己的学习能力
来源:https://unsplash.com/photos/WE_Kv_ZB1l0
介绍
为训练神经网络选择最佳学习速率是一项单调乏味的任务,通常是通过反复试验来完成的。
但是,如果你能为你的神经网络提供一系列的学习率值呢?最好的部分是,有一种方法可以在甚至不开始实际训练你的神经网络的情况下,获得最佳的学习速率范围。
多酷啊。
这些很酷的技术是由 Leslie N. Smith 在他的论文 中介绍的,用于训练神经网络 的循环学习率。
通过使用本文中讨论的技术,我们可以在更少的迭代中获得更好的结果。
论文的摘要清楚地说明了这一点:
用循环学习率而不是固定值进行训练可以提高分类精度,而不需要调整,并且通常迭代次数更少
摘要中的陈述得到了针对自适应学习率等其他技术进行的几个实验的支持。
在 CIFAR-10 数据集上使用循环学习率进行训练时,获得了以下结果:
来源:https://arxiv.org/abs/1506.01186
通过循环学习率方法在大约 25,000 次迭代时实现了通过其他方法在 70,000 次迭代时获得的相同精度。
现在,让我们直接进入论文的细节。
这个所谓的循环学习率是什么?
大多数情况下,在传递学习率时,我们只是给神经网络一个固定的值。
但是在使用循环学习率时,我们传递的是最小学习率和最大学习率。
例如,考虑最小学习率为 0.001,最大学习率为 0.01。在训练过程中,学习率将从 0.001(最小学习率)变化到值 0.01(最大学习率),然后再次从 0.01(最大学习率)变化到 0.001(最小学习率),并且该过程继续,直到训练完成。
就像一个循环过程,从最小值开始到最大值,然后又回到最小值。就这么简单。
显然,你可能会有一个问题。
学习率从最小值到最大值需要多少次迭代或多少个时期,反之亦然?
这个问题的答案是步长。
如果步长为 100,那么学习率从最小值到最大值需要 100 次迭代,再需要 100 次迭代才能回到最小值。
作者图片
如上图所示,学习率从最小值 0.0001 开始,在 100 次迭代后达到最大值 0.001,并在接下来的 100 次迭代中再次返回到最小值。
一个完整的周期是返回最小值所需的时间。在上图中,等于 200 次迭代。
一个完整的周期= 2*(步长)
Torch7 代码中给出的实现循环学习的速率是:
Torch7 循环学习率代码
epochCounter —迭代次数
被占领土。LR —学习率的最小值
maxLR —学习率的最大值
但是让我们把上面的代码转换成 numpy:
循环学习率的数值实现
现在,让我们测试我们的 numpy 实现是否按预期工作。为此,让我们运行一个 For 循环,并检查学习率是否像前面讨论的那样从最小移动到最大。
测试我们功能的代码
通过运行上面的代码,我们得到一个如下所示的图:
作者图片
正如预期的那样,我们的学习率从最小值开始,在指定的步长内上下线性移动。
上述技术被称为三角策略。本文还介绍了另外两种技术:
- 三角形 2 —三角形策略和三角形策略之间的唯一区别在于,在每个完整周期结束时,基本学习率和最大学习率之间的差值减半。
- exp_range —这里的学习率从最小值到最大值变化,但唯一的区别是每个边界值(最小值和最大值)以 gamma^iteration(gamma 的指数因子下降,直到迭代。
使用循环学习率训练模型
既然你对什么是循环学习率有一个确切的概念,让我们用循环学习率来训练一个模型,看看它是否比一个单一学习率的模型表现得更好。
为了使我们的实验更快,我们将使用来自 MNIST(改进的国家标准和技术研究所)数据集的一个小子集。让我们从实验开始:
- 导入必要的模块。
导入必要的模块
2.下载 MNIST 数据集。
下载 mnist 数据集
正如我所说的,我们将只下载完整的 MNIST 数据集的一小部分。
3.现在,我们将创建一个自定义数据集类。
自定义数据集类
4.使用 PyTorch 数据加载器构建所需的转换并加载数据集。
加载数据
由于下载的 MNIST 数据集是张量形式的,PyTorch 数据类只接受 PIL(Python 图像库)图像,我们需要将数据集转换成 PIL 图像,并将其转换成张量,然后输入数据加载器。
数据集以这样的方式分割,8000 个数据点用于训练,其余的用于验证(大约 2000 个数据点)。
5.现在让我们创建一个验证函数来计算我们的模型在验证数据上的损失和准确性。
验证功能
6.现在,让我们创建我们的模型。
构建模型
我们将使用 resnet18(没有预训练的权重)作为具有交叉熵损失和 adam 优化器的模型。
7.现在我们都准备好训练我们的模型了。
使用循环学习率进行训练
在数据加载器的每次迭代中,我们将使用循环学习率函数来更新优化器中的学习率值,这个函数是我们之前使用 numpy 实现的。
在上面的代码中,我们使用的步长等于训练数据加载器的两倍,学习率边界在 1e-3 和 1e-2 之间。
我们还将在每次迭代后存储精度值,以便将结果与使用单一学习率训练的另一个模型进行比较。
9.现在,我们将快速创建和训练另一个模型,但只有一个学习率值。
我们将使用之前使用的相同数据集来训练模型。
8.现在,让我们比较我们的模型训练 4 个时期的结果和另一个模型训练 8 个时期的结果,但是具有单一学习率,即 0.001(Adam 优化器的默认值)。
用于比较结果的代码
在上述代码中,术语“acc1”是用单一学习率训练的模型的精度值,术语“acc”是用循环学习率训练的模型的精度值。
上面的代码给出了如下图:
作者图片
从上面的图中可以清楚地看出,使用循环学习率(红线)训练的模型比使用固定学习率(蓝线)训练的模型获得了更高的精度,即使迭代次数更少。
寻找最佳学习率范围
正如我之前说过的,有一种技术可以使用一种技术找到最佳的学习速度范围。这种技术被称为论文中提到的“LR 范围测试”。
有一种简单的方法来估计合理的最小和最大边界值,只需在几个时期内对网络进行一次训练。这是一个“LR 范围测试”。
这是通过将最小学习率设置为小值(如 1e-07)并将最大学习率设置为高值来实现的。然后对模型进行一些迭代训练,然后对每个学习率获得的损失进行绘图。
这在 fastai 库中很好地实现了,但是也有同样的 PyTorch 实现。
但是为此,我们需要使用 pip 安装一个名为 torch-lr-finder 的库,如下所示:
pip install torch-lr-finder
现在我们可以测试学习率的最佳范围,并将其传递到我们的模型中。
我们将通过我们的模型、损失函数、优化器和设备(cuda 或 cpu)来初始化学习率查找器。
我们通过训练数据加载器、验证数据加载器、最小学习率(非常低的值)和最大学习率(非常高的值)来开始学习率范围测试。
运行上面的代码后,我们得到一个带有学习率建议的图,如下所示:
作者图片
从图中,我们可以看到损失值从大约 3e-4 的值继续减小到 1e-3 的值,因此这些值可以用作我们的学习率的最小和最大值。学习率查找器建议的最佳学习率是 5.21e-04,也在此范围内,如果您希望以单一学习率训练模型,可以使用该学习率。
使用 PyTorch 的学习率计划程序
PyTorch 提供了一个学习率调度器来改变学习率,如上所述。
因此,让我们使用 PyTorch 的学习率调度程序来训练一个模型,该模型具有与我们之前使用的相同的架构、超参数、优化器和损失函数。
让我们从 PyTorch 导入学习率调度器,快速构建模型。
正在初始化 CyclicLR 计划程序
现在,我们将训练我们的模型,并使用学习率调度程序来更新学习率。
使用调度器训练模型并更新学习率
从上面的代码中可以看出,在数据加载器的每次迭代之后,学习率会使用调度器进行更新。
在 4 个时期之后,该模型给出了与使用我们构建的循环学习率函数训练的模型相同的准确度(98.2638)。
作者图片
📎需要记住的一些要点是:
- 将学习率从最小值(低值)增加到最大值(高值)可能会有短期的负面影响,但从长期来看,它会产生更好的结果。
- 循环学习率有助于在训练时摆脱鞍点。
- 最好将步长设置为数据加载器在单个历元内迭代次数(或训练数据加载器的长度)的 2-10 倍。
- 最好在周期结束时停止训练,也就是说,当学习率最低时。
- 一个经验法则是将最小学习速率保持在最大学习速率的 1/3 或 1/4。
- 无论何时开始一个新的数据集或架构,学习率范围测试都是获得最佳学习率值或最佳学习率范围的好方法。
结论
这是一个非常棒的技术,可以用在你日常的神经网络训练中。
如果你仍然怀疑循环学习率的能力,那么你一定要看看这篇论文的实验部分,你也应该尝试自己的循环学习率实验,以了解它的能力。
您可以将这种技术与其他方法(如自适应学习率技术)结合起来,以获得强大的模型。
有许多技术在深度学习爱好者中并不流行,这些技术可能会提高您的模型的泛化能力,或者减少训练模型的时间,从而节省您的大量宝贵时间。
如果您希望获得本文中讨论的完整代码,您可以在这个资源库中找到它。
fastText 和 Tensorflow 来执行 NLP 分类
www.camptocamp.org 登山路线分类
fastText 是脸书在 2017 年发布的最先进的开源库,用于计算单词嵌入或创建文本分类器。然而,嵌入和分类器只是数据科学工作中的构建模块。之前有许多准备任务,之后有许多验证任务,并且有许多基于这些工具的候选架构。让我们根据 CampToCamp.org、、、、的登山运动社区站点的数据,制定一个完整的用例。作为深度学习引擎,我们将使用 Tensorflow 2 / Keras。至于验证,我们将使用 UMAP,一个从高维流形到 2D 平面的投影工具。
任务
www.camptocamp.org 是一个山地运动的社区网站。这是一个生动的信息来源,并已成为法国和瑞士阿尔卑斯山的参考。该网站有四个主要部分:一个关于路点和路线的知识库,一个郊游的实时反馈,事故报告和一个论坛。
我们的任务是基于路线数据:给定文本描述,设计并训练一个分类器来标记与十种可能性中的路线相关的活动类型:徒步旅行、山地自行车、滑雪旅行、雪鞋、四种类型的攀登(岩石、冰、山地和混合)、via-ferrata 和 slack-lining。所有与飞行相关的活动(滑翔伞、跳伞……)都没有被列为路线,滑降/度假滑雪也不是该网站的目的。
CampToCamp.org 用户界面中,突出显示了要执行的分类任务
如果每条路线是一个组合,或者如果它有一个冬季和一个夏季变量,则可以用多个活动来标记。每条路线最多有三个活动标签。随着标签的设置,这是一个监督下的训练。
数据
我们将使用从www.camptocamp.org到法国阿尔卑斯山中北部的路线描述(上阿尔卑斯省、伊泽尔省、萨瓦省、上萨瓦省的行政区域)。由于 Camp To Camp 是国际性的,我们希望将重点放在法语版本的描述可能较长且准确的地区。
www.camptocamp.org有一个开放的 Web API [5],虽然没有文档说明,但是通过查看来自网页的 XHR 请求,很容易理解。
路线描述的字长直方图
总共提取了 14,074 条路线。富文本功能(HTML)被删除。610 被删除,因为它们没有法语描述。在用 fastText 或 Tensorflow 计算嵌入之前,标点符号被去掉。
可以进行更多的数据清理和清除,例如非常短的描述或英文描述(语言不匹配),但这是一个parti推进这种数据质量。
预先计算的法语语言模型
文本的嵌入表示,也称为矢量化,在 Mikolov 等人于 2013 年发起的自然语言处理(NLP)中非常常见[3]。它已经在 Word2Vec 这个术语下普及了。有几个著名的实现和预训练模型,例如 GloVe [4]。 fastText 是最先进的算法之一,也是矢量器的实现,它利用了许多改进,比如 Skip-grams,或者连续词包(CBOW)和子词元语法[1]。
预计算嵌入在名称语言模型下交付。鉴于人类语言的复杂性,语言模型训练需要数 GB 的数据。对于 fastText ,提供了 157 种语言模型【2】,这与之前的其他库非常不同。
让我们从法语开始,观察在登山活动中使用的术语的结果。
例如, corde (绳子)被发现靠近:
- cordes (复数形式)
- Corde (首字母大写)
- 科德。(带标点)
- 小绳(小绳子)
- 绳索(绳索套装)
- 菲塞尔(小绳子的同义词)
- 桑乐(扁绳、扁带)
- 记录者(动词)
- 菲林(同义词)
- 小母鸡(小母鸡)
这个单独的观察给了我们一些关于什么是接近的线索(同义词,其他形式,密切相关的对象),以及一些词汇的遗留问题:标点符号,大小写。这个案例没有简单的解决方法,因为一些常用词可能指某个特定的地点,如勃朗峰(即 w 白山)。
fastText 也从三元组中计算类比:计算前两个单词之间的距离,并从第三个单词的相同距离处提取词汇。它并不总是导致有意义的联想,但仍然是相当准确的。例如,用单词滑雪者(去滑雪)滑雪(运动或物体) vélo (自行车),类比输出为:
- 佩达勒(踩踏板,骑自行车)
- promener , balader (挂出)
- 佩达兰特(脚踏)
- 还有意义不大的那种带标点符号的同义词: vélo。a,韦洛。、佩达勒、自行车、韦洛。-,velo
嵌入的 2D 投影
关于嵌入的另一个观点是单词云从高维空间(对于法国模型是 300 维)到 2D 平面的投影。最常见的投影机制是主成分分析(PCA),但还有更先进的技术,如 t-SNE [6]和 UMAP [7]保留项目之间的局部距离。请注意,t-SNE 或 UMAP 的预测是不稳定的:对相同数据的多次运行会导致不同的输出
下面是帆船、自行车和登山运动中一些词汇的嵌入情况。登山和帆船运动被很好地分开,骑自行车在它们之间传播,就像 carte (地图)和 corde (绳索)一样。
现在让我们来设计从攀岩到滑雪的更具体的登山活动词汇:
这幅图像几乎没有结构。即使是看起来很接近的事物,如缆车变体( télésiège、téléski、télécabine )也不总是聚集在一起。
分类
现在让我们执行一个更完整的机器学习任务:根据活动对来自 CampToCamp.org 的路线描述进行分类:徒步旅行、爬山(在岩石上、冰上、混合、在山上、via-ferrata)、滑雪、山地自行车和雪鞋。
每条路线都可以标记多个活动,问题是多类多标签。实际上,大多数路线有 1 个活动,最多有 3 个活动。
比较了四种分类器模型:
-
fastText 法国计算嵌入模型,然后是卷积神经网络(CNN)
-
fastText 建立在语料库上的分类器(多项式逻辑)
-
fastText 建立在语料库上的嵌入,用于向 CNN 提供信息
4.CNN 在第一阶段计算嵌入
经过测试的分类器架构
实现提示
语料库序列(路线描述)在 300(超过 80%更短)或 600 个单词(超过 95%更短)处被填充和截断。
卷积在序列字和嵌入的二维平面上。该架构是经典的漏斗形状(特征空间在网络中越深越小),初始计算层是卷积的,最终层是密集的。主要参数是:
- 层数
- 盘旋的宽度
- 汇集或步幅参数
- 正规化,主要利用辍学
损失是在 sigmoid 激活的输出处应用的分类交叉熵。在训练期间,还通过分类准确度来评估表现。
挑战之一是过度拟合,因为与样本数量相比,参数总数仍然相当高。另一个挑战是,1 类(滑雪旅游)比其他类(占总数的 40%)大得多,而 4 / 10 类低于 5%。如下图所示,除了与一个或多个主要活动相关的活动之外,模型很好地处理了这个问题。例如,雪鞋旅行介于滑雪旅行和夏季徒步旅行之间(大多数人实际上更喜欢滑雪旅行而不是雪鞋旅行)。
真实活动密度(多标签合计> 100%)和使用模型 2(快速文本分类器)的前 1 名预测
测试和验证
与训练数据集相比,测试数据集的验证精度略有不同。它根据以下规则计算前 1 名、前 2 名和前 3 名的准确度:
- Top-1 基于具有最高概率的预测类别,并检查该类别是否在该序列的标签中
- Top-2 基于两个最高的预测概率,并与标签进行比较。在应用于给定序列的标签号是 1 的情况下,由于应用了两个候选类别,所以前 2 个匹配比前 1 个更容易。在将 2 个或更多个标记应用于序列的情况下,与前 1 个统计量的差异不如前一种情况有利。
- 前 3 名与前 2 名相似,但概率最高
根据这一指标,第一个模型表现稍差,而所有其他模型表现相似。这 3 个模型之间的排序取决于初始化和训练测试数据集的划分。这意味着由 fastText 生成的简单高效的分类器的性能与更复杂的 DNN 一样好。
4 个调查模型的前 1 到前 3 名准确度
被错误分类的路径的描述长度直方图类似于所有路径的描述长度直方图。
错误分类的路径描述长度的直方图
让我们检查模型 4 的最后一个 DNN 层输入端的估计值。维数是 64,与取出 10 个候选类的概率(sigmoid 激活)的最后一层的输出相比,它通常更有趣。
由预测的 top-1 活动着色的最后一个 CNN 图层输入投影
路线通常在对应于活动的群中被很好地分开。令人惊讶的是,有一个集群相当遥远,与“徒步旅行”有关。
将颜色编码改为最高精度的“通过”或“失败”,我们看到失败遍布各处,但在簇的薄连接处具有更高的浓度。
由预测的 top-1 活动的准确性着色的最后一个 CNN 图层输入投影
阅读路线描述的一些失败是显而易见的:它很短,不完整,不精确,分类器用纯粹的可能性分配活动。
分类失败,路线描述设置为“完成”
这一点通过观察奇怪的偏远的徒步旅行路线群得到了验证。它由描述为“Info”的路由组成,这可以很容易地被规则检测到,但是分类器已经将它们困住了。
描述设置为“信息”的徒步旅行路线聚类
其他一些更令人不安,如下面的一个,其中活动在描述中被明确命名:
使用包含要预测的活动名称的描述进行分类失败
如果分类器能够更好地理解这种语言的微妙之处,它还可以改进,就像这个旅程描述一样:
分类失败时,描述分析起来相当复杂,并且需要良好的语言模型
结论
对于这个中等复杂度的任务, fastText 简单分类器表现良好,并且计算成本远低于完整语言模型的训练、嵌入的计算和通过 CNN 的分类的关联。
如果任务更复杂,比如计算几个输出(例如:路线的活动和难度),这个分类器可能不够灵活。
哪种嵌入?
我们已经评估了三种构建和使用嵌入的方法,并提供给神经网络:
1.模型 1:使用带有快速文本的预先计算的语言模型
2.模型 3:使用 fastText 从语料库中构建模型并计算嵌入
3.模型 4:直接拟合神经网络中的嵌入
如果语料库的语言不具体,或者如果像偏见这样的伦理问题很重要,第一种解决方案是最好的。
第二种解决方案是同时利用语料库的特定语言和对 fastText 的优化(例如:子词 Ngrams,其对于词汇表之外的词更健壮,因此具有更好的泛化能力)
第三种解决方案是利用嵌入和分类器的组合优化,但是并不比其他方案执行得更好。它还表明 Tensorflow 和类似的工具正在迅速成为执行机器学习的通用工具。没有太多的工程复杂性,定制的解决方案被创建。然而,它缺乏可解释性,如下图所示,这些嵌入是在登山词汇上生成的。
Keras 对登山词汇的嵌入
笔记本电脑
参考
[1] Bag of Tricks for Efficient Text Classification, J. Armand, G. Edouard, B. Piotr, M. Tomas, 2017, Proceedings of the 15th Conference of the {E}uropean Chapter of the Association for Computational Linguistics: Volume 2, Short Papers ([https://www.aclweb.org/anthology/E17-2068/](https://www.aclweb.org/anthology/E17-2068/))[2] Learning Word Vectors for 157 Languages, G. Edouard, B. Piotr, G. Prakhar, J. Armand, M. Tomas, 2018, Proceedings of the International Conference on Language Resources and Evaluation (LREC 2018)} ([https://arxiv.org/abs/1802.06893](https://arxiv.org/abs/1802.06893), [https://fasttext.cc/docs/en/crawl-vectors.html](https://fasttext.cc/docs/en/crawl-vectors.html))[3] Efficient Estimation of Word Representations in Vector Space, T. Mikolov, K. Chen, G. Corrado, J. Dean, 2013 (https://arxiv.org/abs/1301.3781)[4] GloVe, Global Vectors for Word Representation , J. Pennington, R. Socher, C.D. Manning, 2014 ([http://www.aclweb.org/anthology/D14-1162](http://www.aclweb.org/anthology/D14-1162))[5] REST API for [www.camptocamp.org](http://www.camptocamp.org), Github ([https://github.com/c2corg/v6_api](https://github.com/c2corg/v6_api))[6] Visualizing Data using t-SNE, L. Van der Maaten, G. Hinton, Journal of Machine Learning Research, 2008 (http://www.jmlr.org/papers/volume9/vandermaaten08a/vandermaaten08a.pdf)[7] UMAP: Uniform Manifold Approximation and Projection for Dimension Reduction, McInnes, Healy, Melville, 2018, ([https://arxiv.org/abs/1802.03426](https://arxiv.org/abs/1802.03426))
用优化器优化神经网络训练综述
通过调整 Tensorflow 中的优化器来加速深度神经网络训练
图片由 Greg Rosenke 在 Upsplash 上拍摄
训练深度神经网络是一项极其耗时的任务,尤其是对于复杂的问题。对网络使用更快的优化器是加快训练速度的有效方法,而不是简单地使用常规的梯度下降优化器。下面,我将讨论并展示 5 种流行的优化器方法的训练结果/速度:动量梯度下降和内斯特罗夫加速梯度、AdaGrad、RMSProp、Adam 和 Nadam 优化。
使用不适当的优化器的一个危险是,模型需要很长时间才能收敛到全局最小值,否则它将停留在局部最小值,导致更差的模型。因此,知道哪个优化器最适合这个问题将会节省您大量的培训时间。
优化器调优的主要目的是加快训练速度,但它也有助于提高模型的性能。
1.梯度下降
计算关于每个θ的相关成本函数的梯度,并得到指向上坡的梯度向量,然后使用下面的等式在与向量方向相反的方向(下坡)前进:
作者图片
因此,梯度下降优化器的速度仅取决于学习率参数(eta)。在一个小的学习率下,GD 会在一个平缓的表面上以小而不变的步伐向下,在一个陡峭的表面上以稍快的步伐向下。因此,在一个大型神经网络中,它会重复数百万个缓慢的步骤,直到达到全局最小值(或陷入局部最小值)。因此,运行时间变得非常慢。
在每个历元之后,精确度慢慢增加
还有其他版本的梯度下降,如批量梯度下降(在完整数据集上运行)、小批量梯度下降(在数据集的随机子集上运行)、随机梯度下降— SGD (在每一步选择一个随机实例),并且都有优点和缺点。批量梯度下降可以以非常慢的速度达到全局最小值。小批量梯度下降比 BGD 更快地到达全局最小值,但是更容易陷入局部最小值,并且与其他两个相比,SGD 通常更难到达全局最小值。
2.动量优化
让我们想象一下,当一个球从山顶滚下斜坡到山脚时,它会开始缓慢,然后随着动量的增加而增加速度,最终以很快的速度达到最小值。这就是动量优化的工作原理。这是通过添加一个动量向量 m 并用来自动量向量(m) 的这个新权重来更新θ参数来实现的
梯度下降不考虑以前的梯度。通过添加动量向量,它在每次迭代后更新权重 m 。动量β 是控制终端速度有多快的参数,通常设置为 0.9,但应该从 0.5 调整到 0.9。因此,动量优化器比 SGD 收敛得更好更快。
# Implement Momentum optimizer in Tensorflow
optimizer=keras.optimizers.SGD(lr=0.001, momentum=0.99)
动量收敛更快,最终达到比 SGD 更好的结果
3.内斯特罗夫加速梯度
动量优化器的另一个变体是 NAG。
代价函数的梯度在位置θ+βm 处测量(而不是原始动量优化中的θ)。这背后的原因是动量优化已经指向了正确的方向,所以我们应该使用稍微靠前的位置(大约在θ的下一个位置)来适度加快收敛速度。
# Implement Nesterov Accelerated Gradient optimizer in Tensorflow
optimizer=keras.optimizers.SGD(lr=0.001, momentum=0.9,nesterov=True
NAG 仅取得了比最初势头稍好的结果
4.阿达格拉德
一种自适应学习速率方法,在这种方法中,算法在陡坡上比在缓坡上走得更快。 AdaGrad 在简单的二次问题中表现良好,但在训练神经网络时表现不佳,因为它往往会变慢得太快,并在达到全局最小值之前停止。由于这个缺点,我通常不使用 AdaGrad 用于神经网络,而是使用 RMSProp ,这是 AdaGrad 的替代方案。
5.RMSProp —均方根 Prop
这是最常用的优化器之一,它延续了阿达格拉德的想法,试图最小化垂直移动,并在水平方向朝着全局最小值更新模型。
Adagrad 对第一次迭代的梯度求和,这就是为什么它通常不会收敛到全局最小值,而 RMSProp 会累加前一次迭代的梯度:
# Implement RMSProp optimizer in Tensorflow
optimizer=keras.optimizers.RMSprop(lr=0.001, rho=0.9)
RMSProp 比 Adagrad 收敛得更好,Adagrad 在平稳状态下会丢失
6.圣经》和《古兰经》传统中)亚当(人类第一人的名字
Adam 优化器是 momentum 和 RMSProp 优化器的组合。换句话说,它考虑了过去梯度的指数衰减平均值和过去平方梯度的指数衰减平均值。
有了这些特性,Adam 适合处理具有复杂数据和大量特征的复杂问题上的稀疏梯度。
# Implement Adam optimizer in Tensorflow
optimizer=keras.optimizers.Adam(lr=0.001, beta_1=0.9, beta_2=0.999)
7.那达慕
亚当的另一个变化是那达慕(使用亚当优化与内斯特罗夫技术),导致比亚当稍快的训练时间。
# Implement Nadam optimizer in Tensorflow
optimizer=keras.optimizers.Nadam(lr=0.001, beta_1=0.9, beta_2=0.999)
Adagrad、RMSProp、Ada、Nadam 和 Adamax 是自适应学习率算法,对超参数的调整要求较少。如果模型的性能没有达到您的预期,您可以尝试换回动量优化器或内斯特罗夫加速梯度
最后的话🖖
总之,大多数时候,自适应学习速率算法在速度方面优于梯度下降及其变体,尤其是在深度神经网络中。然而,自适应学习率算法不能确保绝对收敛到全局最小值。
如果您的模型不是太复杂,只有少量的特征,并且训练时间不是您的优先事项,使用动量、内斯特罗夫加速梯度或 SGD 是最佳起点,然后调整学习率、激活函数、改变初始化技术来改进模型,而不是使用自适应学习率优化器,因为后者会阻碍不收敛到全局最小值的风险。
比较不同优化器模型的损失
- 常规 SGD 或常规梯度下降需要更多时间收敛到全局最小值。阿达格拉德经常在达到全局最小值之前过早停止,所以它最终成为最差的优化器。
- 利用时尚 MNIST 数据集,亚当/那达慕最终表现优于 RMSProp 和动量/内斯特罗夫加速梯度。这取决于型号,通常,那达慕比亚当表现更好,但有时 RMSProp 表现最好。
- 以我的经验,我发现 Momentum,RMSProp ,和 Adam (或 Nadam )应该是模型的初试。
摘要
你可以在这里找到源代码。
通过调整初始化、激活函数和批量归一化来提高深度神经网络性能和训练速度的详细说明在这里是。
全栈数据科学:下一代数据科学家
这篇博客文章讲述了如何成为一名机器学习数据科学家,以跟上行业不断变化的需求。
多年来,数据科学一直是一个引人注目的领域,吸引了受过正规教育的年轻人,他们拥有计算机科学、统计学、商业分析、工程管理、物理、数学或数据科学方面的学士、硕士或博士学位。然而,人们对数据科学有很多误解。不再只是机器学习和统计。这些年来,我和很多数据科学的有志之士谈论过进入这个领域。为什么会有关于数据科学的大肆宣传?还是统计学和机器学习能帮你打入这个领域?还会是未来吗?即使我和你们都在同一条船上,但我现在正经历着对进入这一领域的下一代数据科学家的需求是如何塑造的。我不打算教你如何进入数据科学,因为互联网上的许多人已经在做了。
图片来自 Datanami 的 shutterstock
为什么有这么多关于数据科学的宣传?
每个人都想进入数据科学领域。几年前,该领域存在一个供需问题:在 DJ Patil 博士和 Jeff Hammerbacher 提出数据科学这个术语后,数据科学家的供应减少了,需求增加了。但是现在,2020 年,情况有了转机。受过正式/mooc 教育的数据科学爱好者的流入量增加了,需求也增长了,但没有增长到那种程度。这个术语已经变得越来越宽泛,包含了从事数据科学所需的大多数支持功能。我想引用 KD nuggets 中我最喜欢的一句话:
“数据科学就像青少年性行为:每个人都在谈论它,没有人真正知道如何做,每个人都认为其他人都在做,所以每个人都声称自己在做。”
玩笑归玩笑,以下是我认为数据科学接管了所有炒作的一些原因:
- 数据科学家头衔背后的秘密
- 高工作满意度
- 巨大的业务影响
- 许多工作网站将其评为最热门的工作(Glassdoor 评选的美国最近 3 年最热门的工作)
- 前沿发展
- 不断涌入的数据生成
- 感谢许多优秀/不太优秀的学校和训练营提供数据科学学位
- 数据很美!(不是字面意思:p)
自称数据科学家的人?
有人要说了,那我就来撒点目前行业情况的真相吧。由于对闪亮的数据科学家头衔的需求和声望的增加,许多公司已经开始将数据科学家头衔与产品分析师、商业智能分析师、商业分析师、供应链分析师、数据分析师和统计学家交换,因为人们正在离开他们的工作,以获得公司提供的数据科学家头衔。这完全是因为这个词语上的微小变化,许多角色都得到了尊重。因此,公司已经开始扭曲头衔,以同样的方式,使它更闪亮,更受欢迎,比如数据科学家-分析,产品数据科学家,数据科学家-增长,数据科学家-供应链,数据科学家-可视化,或数据科学家-等等。。
大多数追求教育/在线培训的人都有一个误解,即所有数据科学家都建立了花哨的机器学习模型,但这并不总是正确的。至少这是我开始攻读应用数据科学硕士学位时的情况,我认为大多数数据科学家都在进行机器学习,但当我进入美国的实习和就业市场时,我才知道真正的真相。推动人们追求数据科学的力量是由于围绕人工智能及其商业影响的炒作。
下一代数据科学家—机器学习
对于那些想在 2020 年以数据科学家的身份从事应用机器学习的人来说,ML(这是我如何命名这个头衔的,因为它不是数据科学家-分析:p),没有博士学位,现在有更多的东西,而不仅仅是知道将机器学习应用于数据集,今天几乎任何人都可以做到这一点。根据我的经验,还有一些其他重要的事情,可以帮助你在面试过程中确定数据科学家的角色,甚至获得入围名单:
- 分布式数据处理/机器学习:获得技术实践经验,如 Apache Spark ,Apache Hadoop,Dask 等。可以帮助您证明您可以大规模创建数据/ML 管道。对它们中的任何一个都有经验应该是不错的,但是我推荐 Apache Spark(Python 或 Scala 版本)作为首选。
- 生产 ML/数据管道:如果你能获得使用 Apache Airflow 的实践经验,这是一款用于创建数据和机器学习管道的标准开源作业编排工具。这是目前在行业中使用的,所以,建议学习并获得一些围绕它的项目。
- DevOps/Cloud : DevOps 被大多数数据科学有志者所忽视。如果你没有一个基础设施,你将如何构建 ML 管道?在你的本地机器上构建笔记本或运行代码并不像我们在课程中做的那样容易。您编写的代码应该可以跨您或您团队中的其他人可能创建的基础设施进行伸缩。许多公司可能还没有布局好 ML 基础设施,可能正在找人开始。熟悉 Docker 、 Kubernetes ,用 Flask 这样的框架构建 ML 应用程序应该是你的标准练习,即使是在你的课程中。我喜欢 Docker,因为它是可扩展的,你可以构建基础设施映像,并在 Kubernetes 集群上的服务器/云上复制相同的东西。
- 数据库:了解数据库和查询语言是必须的。SQL 很容易被忽视,但它仍然是行业标准,无论是在任何云平台还是数据库上。开始在 leetcode 上练习复杂的 SQL,这将有助于您在 DS 概要文件中进行部分编码面试,因为您将负责从带有持续预处理的仓库中引入数据,这将减轻您在运行 ML 模型之前的预处理工作。大多数特征工程可以在使用 SQL 将数据导入模型的过程中完成,这是许多人忽略的一个方面。
- 编程语言:数据科学推荐的编程语言有 Python、R、Scala、Java。认识他们中的任何一个都没问题,可以做到这一点。对于 ML 类的角色,面试过程中会有现场编码环节,所以你需要在任何你觉得舒服的地方练习——leet code、Hackerrank 或者任何你喜欢的东西。
所以,这个时候,只知道机器学习或统计是不会让你进入数据科学做 ML 的,除非你很幸运,在行业中有一些很好的关系(你显然应该做网络,这非常重要!)或者你名下已经有了一份出色的研究记录。商业应用和领域知识往往来自经验,除了在相关行业实习之外,无法事先学到。
我到底怎么了?
两个月前,我作为一名数据科学家从研究生院一毕业就加入了媒体巨头 ViacomCBS,除了研究助理和实习之外,我没有任何全职的行业经验。我在这里的职责包括从构思-开发-生产构建 ML 产品,我使用上面列出的大部分东西。希望这能对所有有志的数据科学家和试图打入这个领域的机器学习工程师有所帮助。
在 gmail dot com 的[我的名字][我的名字]上提问,或者在 LinkedIn 上联系。
什么是全栈数据科学家?
角色范围和所需技能
图片由 FreeSVG 上的openclipbart提供。许可:公共领域
全栈数据科学家是一个百事通,从头到尾在数据科学生命周期的每个阶段进行设计和工作。
全栈数据科学家的范围涵盖了数据科学业务计划的每个组成部分,从识别到培训,再到部署为利益相关者带来好处的机器学习模型。
数据科学生命周期的基本阶段
全栈数据科学家可以拥有的数据科学生命周期的基本阶段:
- 商业问题。除非以研究为导向,否则所有数据科学项目都应该从一个问题开始,这个问题可以通过提高效率、自动化或新功能为企业增加价值。
- 数据收集/识别。机器学习需要高质量的数据来建立高质量的模型以供使用。
- 数据探索与分析。在建立模型之前,必须对数据进行分析和理解。
- 机器学习。根据给定的数据训练一个模型来解决业务问题。
- 模型分析和验收。分析模型结果和行为。与利益相关者分享以获得批准。
- 车型部署。让最终用户可以访问模型。
- 模式监控。确保模型在未来的行为符合预期。
万金油:技能组合
列出的高级技能也是成功的数据科学计划的关键。值得强调的是软技能,没有它,数据科学技术可能无法提供价值。
商业头脑
全栈数据科学家必须能够识别和理解可以使用数据科学工具包解决的业务问题(或机会)。
为了优先考虑对他们的组织最有价值的项目和过程流,他们必须理解他们的组织的需求和目标。
最终,如果一个模型不能提供价值,企业就不会在乎它有多酷或多准确。
合作
全栈数据科学家不在真空中工作。他们必须与利益相关方合作,找出可以通过数据科学解决的现有问题或低效之处。一旦发现问题,合作对于确保结果可接受并满足他们的需求是至关重要的。此外,与 SME(主题专家)的协作使他们能够快速工作,例如在组织中查找数据源。
沟通
通过口头和书面媒介与企业进行有效的沟通,可以更好地协作,并向最终用户“销售”模型。这意味着用通俗易懂的语言为非技术受众量身定制数据科学理念、结果和价值。在某些情况下,最终用户在选择使用该模型之前,必须理解并信任它。
识别数据源和 ETL
没有数据就无法训练模型。数据常常不容易获得;需要找到、提取、转换并加载到正确的位置。
编程;编排
一个完整的堆栈数据科学家必须能够编写干净、高效的面向对象的代码,并在生产中可靠地工作。理想情况下,这样的代码应该是模块化的,每个函数或类都要通过单元测试来验证。
数据分析和探索
这种技能是必不可少的,因为没有数据理解就无法建立有用的机器学习模型。
机器学习和统计
或许这是一个既定事实——没有机器学习或统计学,这项工作就不是“数据科学”。一个全栈数据科学家必须能够用合适的机器学习算法进行实验,来解决机器学习问题。
值得强调的是,尽管简单,但有时在机器学习解决方案上实现逻辑或业务规则会给业务带来直接价值。机器学习模型可能需要几周或几个月的时间才能正确,而业务规则目前可能“足够好”。
模型部署/数据工程
最后,全栈数据科学家必须具备将模型管道部署到生产中的技能。模型管道允许最终用户用数据查询模型或以期望的方式访问预先生成的模型结果。如果不存在部署机制,他们必须能够设计和设置这个管道。
如果一个模型没有被部署(或者可能在业务分析中被呈现),那么它是没有用的,并且不提供业务价值。
无主之物:挑战
上一节列出的技能多种多样。
面对如此多样的需求,一个完整的数据科学家不可能掌握所有的技能,尤其是随着技术、算法和工具的进步。相反,这个人必须挑选哪些元素对手头的项目最有用,最值得关注。
全栈数据科学家的两个关键基本技能是设计系统或流程的能力和快速掌握新技术的能力。
好处
另一方面,全栈数据科学家是一个(或两个)数据科学团队。
对于刚接触数据科学的组织来说,他们可以在不立即建立完整团队的情况下创造商业价值。为了最有效地工作,全栈数据科学家应该能够选择和应用正确的工具。
外卖食品
全栈数据科学家在两个方面超越了典型的数据科学家角色:
- 将业务需求与机器学习(或非机器学习)解决方案联系起来
- 将模型部署到“生产”
这两个要素是任何组织从数据科学中获取价值的关键——解决正确的问题并让最终用户能够访问它们。
进一步阅读
14 分钟阅读关于数据科学构成要素的市场观点,数据科学家将继续发展,并经常…
ckmanalytix.com](https://ckmanalytix.com/the-full-stack-data-scientist/)
全栈数据科学家还是大数据项目的超级英雄?
数据科学就业市场的新兴趋势
来源:Pexels.com/@picography
曾经有一段时间,拥有扎实的数学、概率统计、线性代数和一些机器学习背景,再加上出色的数据分析和演示技能,就足以在数据科学领域取得成功。商业智能专家的传统角色已经成熟为数据科学。然而,近年来,数据科学家的工作描述已经从纯粹的数据分析转变为包含更多来自大数据、机器学习和云技术的技能,而不是传统的分析。此外,拥有前端和后端软件开发和 DevOps 方面的丰富专业知识对于数据科学工具箱来说也越来越必要。
如今,以敏捷的方式交付由人工智能(AI)驱动的端到端软件正成为市场的新需求,而不仅仅是一项不错的技能。的确,数据科学不仅仅是数据分析;这是大数据、机器学习和软件工程的结合。两种主要类型的数据科学项目主要与商业分析或开发人工智能软件有关。
对于业务分析项目,数据科学家通常必须访问大数据基础架构并创建数据管道,以从数据中提供可操作的见解。通常,在这些项目中开发的软件是以分析仪表板的形式,为业务决策者即时提供可操作的见解,并帮助他们进行数据驱动的决策。对于软件开发,数据科学家需要使用他们的大数据工程、数据科学、机器学习工程和软件开发技能,在运营软件中部署预测模型。
随着数据科学领域越来越成熟,市场需求和数据科学家技能之间的差距正在扩大。公司高管不再满意每个季度都在 PowerPoint 演示文稿中向他们提供可操作见解的人。尽管如此,他们仍然需要每天访问他们的实时仪表盘,以进行数据驱动的决策。这可能就是为什么对分析仪表板的需求在过去几年中稳步增长的原因。
随着人工智能和大数据分析的现成云解决方案的进步,创建机器学习模型等数据科学日常任务有时会被精通数据的软件开发人员甚至非 IT 人员接管。有时,这种组合对企业来说甚至更好,因为最终,他们需要的是一个部署好的可操作的软件,而不是 Jupyter 笔记本中的高精度模型。
这些因素携手并进,使得数据科学家成长为“全栈”专家成为必要。面对将数据科学和机器学习解决方案部署到企业软件中以及使用大数据和 DevOps 框架的压力,创造了新的全栈数据科学家。如果一个数据科学家建立了一个准确率高达 99%的机器学习模型,这不是一个现成的软件,它对雇主来说已经不够好了!
自然选择选择可以向世界展示自己的人工智能算法,即使这些算法不太智能,也不太适合特定的用例。数据科学家必须学习这些技能中的大部分,才能在竞争中生存下来,并保持他们在行业中薪酬最高、最受尊敬的专家。未来,雇主认为的独角兽数据科学家将成为一种规范,数据科学家必须加快步伐,满足市场需求,以保持竞争优势。
引文: “全栈数据科学是新常态吗?”
关于作者:
Pouyan R. Fard 是 Fard Consulting &数据科学圈的创始人& CEO。Fard Consulting 是一家位于法兰克福的精品咨询公司,为各行业的公司提供服务。Pouyan 在数据科学、人工智能和营销分析方面拥有多年的公司咨询经验,从初创公司到全球公司。他曾与制药、汽车、航空、运输、金融、保险、人力资源和销售等行业的财富 500 强公司合作。
Pouyan 还带领数据科学圈团队在雇主和数据科学人才之间建立职业枢纽。DSC 的使命是通过职业培训培养下一代数据科学家,并帮助雇主找到大数据领域的顶尖人才。
Pouyan 已经完成了关于消费者决策预测建模的博士研究工作,并对开发机器学习和人工智能领域的最先进解决方案保持兴趣。
一个 Twitter 线程中的全栈开发
将你的想法变成现实的端到端步骤
你好,我是尼克🎞 on Unsplash
当形式追随功能
人有想法。很多想法。但是他们有多少次把这些想法变成现实呢?我们脑袋里的东西和创造的东西有明显的区别。
大部分人不做软件。这包括实际软件团队中的许多人。如果你现在不编码,你会认为创建工作软件是别人的工作。也许你想自己开始学习(或重新学习),但认为这需要太长时间,太具技术挑战性,或者只是你不感兴趣。
想法通常以图画、便利贴、模型和“可点击的应用程序”的形式存在,所有这些都可以捕捉想法,但很难验证可行的东西。在一个人的想法和自然允许发生的事情之间有一个过渡点,通过这个过渡,创新实际上发生了。在形式和功能之间有一个美丽的对应,只有一个真正的工作软件才能展示出来。
不做软件的人没有任何借口。不管你的兴趣是什么,如果你想看到你的想法变成现实,那么你需要开发软件。鉴于当今工具的抽象水平,完整的软件可以被快速地创建出来。
软件开发不是给计算机“编程”,而是制作一些真实的东西,与人互动,成为工作经济的一部分。
我最初在 Twitter 上用一个线程写了这篇文章,以展示任何人创建端到端的软件应用程序是多么容易。
有许多错误的机会守门人。你需要正规教育、在线课程或多年丰富经验的假设显然是错误的。每个人都有能力把他们的想法拼凑成实际可行的东西。
让我们开始吧。
这些碎片
你只需要一个浏览器,一个记事本,和你电脑的终端(PC 上的命令提示符)。构建所有应用程序需要几个高级步骤:
- 一页
- 风格
- 布局
- 互动元素
- 事件
- 计算机网络服务器
- 获取/存储数据
人们很少从头开始写程序了。那太慢了。今天我们使用图书馆。我们将使用 JavaScript 和 Python 库。我会用 Azle 做前端的东西(DISCLAIMER
: 我创建了 Azle,但是你可以用任何 JS 库,后端的东西用 Flask。
让我们从我们的页面开始。
页面总是有一个index.html
文件。进入 Azle 主页,点击左上角的 STARTER HTML 图标。复制和粘贴这个到记事本或者任何你想要的编辑器(我会用 TextWrangler ),然后保存文件为index.html
(在你的桌面上)。
从 Azle 中抓取启动 HTML 文件。
每个 web 应用程序都有一个index.html
文件。它是当你加载一个网站或网络应用程序时,你的浏览器指向的页面。让我们把这个文件放在一个叫做my_app
的文件夹中,然后把你的index.html
文件拖到这个文件夹中:
如果右击index.html
文件并选择您的浏览器,您可以查看该页面。这样做将弹出打开您的浏览器,并显示一个空白页。
让我们用样式页面。从 Azle 中获取style_body
代码,然后将粘贴到你的index.html
文件中(在create_azle
函数内):
不要像 Reddit 一样,因为自己丑就觉得自己很酷。去拿一些极简的十六进制代码,把你的页面设计成 2020 年的样子。没有什么比俄罗斯粉彩更能体现我的现代感:
点击上的选择你喜欢的颜色。用新颜色替换style_body
功能中的背景颜色。我会选择比斯开。我们也不想使用标准的浏览器字体,因为唯一比看起来像 Reddit 更糟糕的是看起来像 Craigslist。
谷歌字体 救场。我会选择 Ubuntu。好看又现代。
当然,我们需要让我们的谷歌字体可用。让我们在调用我们的style_body
函数之前加载。在文档中搜索中的“字体”找到load_font
功能):
az.load_font("Ubuntu")
我还将style_body
函数中的min-width
属性设置为 1150px,这样当我调整浏览器大小时页面不会挤压。因为我们只是原型制作一个应用程序,所以我们不关心响应度。
我们的代码现在应该是这样的:
因为这些都在我们的index.html
文件中,我们可以刷新浏览器并查看结果:
这里有 码笔 如果想玩代码的话。尝试更改背景的颜色。当然,改变字体不会有什么影响,因为我们没有显示任何文本。
每当你在本文中看到 CodePen 时,点击它打开一个标签,显示我们当前代码的实时交互版本。根据自己的喜好调整这些值。
我们正在取得进展…
当然,我们的页面上什么都没有。让我们改变这一点。我们将添加一个部分来保存我们的内容。使用add_sections
功能添加 1 个部分:
…刷新您的浏览器:
Azle 的默认部分颜色是蓝色,但是当然,我们可以根据需要改变它。
尝试更改部分的数量。
让我们回到俄罗斯,看看我们是否能找到比蓝色更好的颜色。使用style_sections
函数(位于add_sections
函数的正下方)来设置新部分的样式。为了样式化一个元素,我们必须使用它的类名和实例号来定位它。我们知道我们的部分的类名叫做“my_sections ”,因为我们只创建了一个部分,所以它一定是第一个实例。我们的style_sections
函数看起来像这样:
我将选择苹果谷作为我的部分背景色。我还加了一个6px
的border-radius
来圆角。我将height
设置为auto
,这允许 HTML 元素根据元素内部的内容进行增长和收缩。
因为我们想在页面上安排事情,我们将使用布局。这些只是网格;放置在页面上的框,我们用文本、按钮、滑块、输入等填充。我将使用add_layout
功能添加一个 2 行 1 列的布局:
看起来是这样的:
我希望第一排能守住我的头衔。我将通过使用另一个style_layout
函数将定位到我的布局的第一行来将第一行的高度更改为60px
,该函数直接位于上一个函数的下方:
注意我是如何定位行“my_layout_rows”和第一个实例(行 1)的类名的。刷新浏览器以查看不同之处:
我们在样式函数的花括号中添加的所有内容都是标准的 CSS 样式。当你试图找出如何实现你想要的风格时,只需在网上搜索合适的 CSS。随着时间的推移,你会学到许多造型技巧。
让我们继续展示我们之前加载的 Ubuntu 字体,为我们的应用程序添加一个标题。我们瞄准布局的第一个单元格,使用add_text
函数添加文本:
让增加标题的字体大小,并使居中对齐:
目前为止还不错。现在事情变得更有趣了,因为我们的下一步是向我们的应用程序添加交互元素。
让我们在第二个单元格中添加另一个布局来容纳我们的交互元素。复制我们之前使用的相同布局代码,粘贴在底部。目标我们最初布局的第二个单元格,使用 1 行 2 列,将背景涂成奶油色(或者任何你喜欢的颜色)。我们将add_layout
和style_layout
作为函数添加到前面代码的正下方,如下所示:
注意,我还使用了style_layout
函数的column_widths
属性在列宽之间进行 20/80 分割。
这款应用看起来太“线性”了。让我们通过将边框的border
设置为0
来从最近的布局中移除边框:
这样更干净。然而,我仍然希望我们内部布局的两个单元之间有某种分离。让我们从俄罗斯调色板中为第二个单元格“Squeaky”着色:
请注意,我们的应用程序不再需要任何边框,因为颜色单独划分了我们的布局单元格。让我们通过移除外部布局的(我们添加的第一个)边界来使它最小化和平滑。
这里是此时的密码本。
现在我们准备添加我们的交互元素。我们没有从我们想要创造的任何模型开始,这很好。但是现在可能是一个好时机去思考我们想要的是什么。
当今大多数有趣的应用程序都是数据驱动的。我们可以获取什么样的有趣数据,我们可以使用什么样的模型?当我写这篇文章时,每个人都在想着新冠肺炎。让我们获取新冠肺炎的数据,并使用一个模型来预测病例。
***通常的免责声明 * * * * *别傻了,用这个模型来做关于疫情的现实生活决策。这只是为了演示的目的。
我们想要公开可用的数据,理想情况下作为“ RESTful 服务”(“REST API”)交付。REST APIs 将数据和功能“通过网络”传递到我们的浏览器中,使得创建有趣的应用程序变得更加容易,而无需编写大量代码。
如果有人提供新冠肺炎数据作为 REST API,这意味着我们不必自己存储/管理数据;我们可以直接用它
我在这里找到了一个:https://about-corona.net它是免费的,不需要认证。
REST APIs 有“端点”,我们用浏览器指向这些端点来获取实际数据。查看文档后,我找到了我们将使用的端点:
[https://corona-api.com/timeline](https://t.co/pvWimRKAi3?amp=1)
这给出了全球死亡、、确诊和痊愈病例的总数。
任何时候你想看 REST API 的数据是什么样子,只需打开你的浏览器到端点:
为了在我们的应用程序中使用数据,我们不像上面那样访问它,而是使用 JavaScript摄取数据,并将结果解析成某种有用的形式。但首先,让我们回到我们的模型。现在我们已经看到了数据,我们可以考虑我们的应用程序可能的外观和行为。
一个肮脏的模型是我们锚定我们的方法所需要的。这是我用谷歌幻灯片创作的草图:
用户选择一种类型(死亡、已确认、已康复),结果显示在右侧的图表中。然后,用户选择一个“地平线”(未来几天),并单击 FORECAST 在后端运行一个模型,其结果在返回时也会可视化。
有了数据和模型,我们可以开始添加交互 UI 元素。我们需要一个下拉菜单,一个滑块,一个按钮,以及一个线图。先说前 3 个。我将在第一个内部布局单元格中添加一个新布局,以帮助定位我们的元素:
请注意,我将border
设置为1
,这样我就可以看到新的布局:
现在让我们在这些新的单元格中添加我们的 UI 元素。从 Azle 的文档中我们可以得到我们需要的代码。
添加 下拉菜单、滑块和按钮代码到我们的应用程序中:
嘣,现在我们有了 UI 元素:
让我们为我们的应用程序定制这些元素。我们知道下拉列表中需要的选项(死亡、已确认、已康复)。现在添加:
让我们为预测留出最多30
天的时间。将默认值设置为 1 周(7
天),最小值为1
天,最大值为30
天:
让我们将元素居中,将边框从最近的布局中移除。
我们将在新的style_layout
函数前使用all_
前缀,将中心对齐一次应用于所有 3 个单元格:
我还将布局上的边框设置为 0
,因为我们不再需要它。
开发时在 1 和 0 之间切换边框。
halign
和center
确保所有 3 个单元格的内容水平对齐。
现在让我们将添加我们的线图可视化。
现在开始看起来像一个真正的应用程序。对于折线图,我们将使用另一个名为 的库。Plotly 构建在 D3.js 之上,这是一个用 Javascript 编写的行业标准可视化库。
Telsa Motors 和 Standard & Poors 等组织将 Plotly.js 开发与 Chart Studio Enterprise 配对,后者…
t.co](https://t.co/ROsNy5974s?amp=1)
虽然您可以学习 D3.js 本身,但 Plotly 提供了一个抽象层,使开发(更)快速。点击 Plotly 网站上的折线图选项:
我们将将折线图代码复制到我们的应用程序中。首先,我们需要使这个库可用。使 JS 库对应用程序可用的最简单的方法是通过 CDN (内容交付网络)。我们只需将适当的 URL 添加到我们的index.html
文件的头中。我们从 Plotly 的入门页面找到了这个 URL:
现在我们可以在我们的应用程序中使用任何 Plotly 的视觉效果。从 Plotly 的网站上抓取的折线图代码并将其放入一个名为draw_line_chart
的函数中,就像这样:
将它放在index.html
文件中的任何地方,主create_azle
函数之外(所以在底部的任何地方,但是仍然在<script>
标签之内)。看看下一个密码本就知道了。
如果我们调用我们的draw_line_chart
函数,它将在 id 为my_div
的元素中绘制我们的折线图。当然,我们现在没有这样的元素,所以让我们创建一个 HTML 元素来存放我们的情节。
我们将使用 Azle 的add_html
函数。我将给 div 一个名为“hold_chart”的 id:
让我们回过头来,将前面的draw_line_chart
函数中的‘my div’改为’hold _ chart,这样它就能正确定位目标。
我们需要一种方法来调用我们的draw_line_chart
函数。让我们点击预测按钮来绘制我们的折线图。
为了将事件添加到 Azle 的 UI 元素中,我们使用了add_event
函数。让我们向预测按钮添加一个点击事件,如下所示:
如果我们现在单击“预测”按钮,我们将看到绘制的折线图:
看起来很棒。这是最新的密码本。
Plotly 提供了许多现成的东西。我们得到了工具提示、缩放、平移,以及一系列可定制的选项。在 raw D3 里自己做这些并不好玩。
折线图显示的数据只是 Plotly 提供的模拟数据。显然,我们想获得真实的数据。让我们现在做那件事。
我前面提到过,我们将使用 JavaScript 获取其余数据,并将结果解析成某种有用的形式。关于构建依赖提取数据的应用程序,一个重要的事实是数据必须在使用之前可用。
虽然这听起来很明显,但当一个人刚刚开始学习软件开发时,很容易忽略这一点。例如,当我们的用户第一次加载我们的应用程序时,我们希望显示折线图。但是这个线图依赖于可用的数据。
为了确保我们的应用程序的任何部分都可以使用数据,我们使用了所谓的“异步代码”异步代码在调用我们选择的函数之前会一直等待,直到发生了什么事情(例如,数据已经被获取)。
在我们的例子中,我们希望获取新冠肺炎数据,等待直到它在我们的应用程序中可用,然后绘制我们的折线图。JavaScript 通过它的“获取”API 使这一切成为可能。使用 fetch,我们只需指向 REST 服务提供的 URL,并告诉它一旦收到数据该做什么。
让我们使用 fetch 将我们的新冠肺炎数据引入我们的应用程序。我们如何使用它?一个简单的谷歌搜索把我带到了今年的,这很好地解释了这个问题。它告诉我们像这样使用 fetch:
让我们将粘贴到我们的代码中,使用我们在上面找到的新冠肺炎 URL(就在我们的 Plotly 代码之后):
刷新您的浏览器。应用程序本身看起来没有任何不同。但是如果我们打开“浏览器控制台,我们可以看到我们获取的新冠肺炎数据。通过在屏幕上的任意位置右击并点击检查,打开浏览器控制台。然后单击控制台。
你会看到一个物体坐在控制台上。这是我们获取的新冠肺炎数据(注意,我们在上面粘贴的获取 API 显示 console.log(data))。重复单击此对象以查看其结构:
这看起来比我们第一次指向新冠肺炎网址时出现在浏览器中的要好得多。现在我们可以解析这个数据对象,使用它的内容来填充我们的折线图。为此,我们需要对绘制折线图的方式做一点小小的改变。
检查我们之前添加的折线图代码,我们可以看到它使用“跟踪”将原始数据转换为线条。我们需要将获取结果中的数据放入跟踪对象的 x 和 y 属性中:
让我们编写一些 JavaScript 来解析获取的结果。我们需要:1。了解源结构;2.了解目的地结构。我们可以像前面一样,通过在浏览器控制台中检查数据来理解源结构。
目的结构是 Plotly 需要的 trace 对象。我编写了以下函数来获取从 fetch 检索的原始数据,并将其转换为 Plotly 所需的跟踪结构:
函数是我们在软件中对代码进行分组的方式。它们有一个名字,接受参数,并返回一些结果。函数有助于保持代码的模块化和可维护性。
这里重要的是理解这不是解析数据的和方式。这是一种方式。您必须尝试使用 JavaScript,直到找到合适的为止。在线搜索如何解析 JavaScript 对象、遍历对象并返回新的结构。
我的get_dates_and_cases
函数接受提取的数据,一个选择(例如死亡),循环遍历它的内容,提取我需要绘制的片段,并返回 Plotly 的对象。我将编写另一个函数,用我们准备好的数据绘制折线图。
我们可以删除我们添加的原始 Plotly 代码,并使用该函数来代替。它使用我们的第一个函数来准备跟踪数据,然后像往常一样绘制折线图。
请确保保留我们添加的“hold _ chart”div,以便绘图有地方可去。
回想一下,我们的获取代码只获取数据,但不处理数据。一旦数据到达,让我们的 fetch 函数绘制我们的折线图。将我们的原始获取代码更改为如下所示:
最后,移除对当前位于我们的add_event
函数中的draw_line_chart
的调用。我们一会儿再把它加回去。还有,现在还不用担心az.hold_value.fetched_data = data
线。我们稍后再解释。
您当前的代码应该是这样的: CodePen 。
注意,在 CodePen 中,我在 fetch 函数周围包装了一个
setTimeout
。这只是增加了一点延迟,以确保绘制图形时元素在屏幕上。一旦我们在下一步将 fetch 函数移到我们的add_event
内部,这就不是问题了。
我们的 covid 数据现在显示在折线图中。重要的是,只有从 REST API 中完全提取数据后,才会绘制折线图。概括地说,我们使用 JavaScript 中的异步代码从 API 获取数据,然后在数据准备好之后创建一个可视化。
我们还编写了两个函数来准备原始数据和绘制结果。我们越来越接近了:
…尽管我们还没有讨论数据存储。
我们的下一步是允许用户选择一个选择(死亡、确诊、痊愈)来相应地重新绘制折线图。让我们存储返回的数据,这样我们可以在需要时使用它,而不必每次都重新提取数据。
我将把返回的数据保存在一个 JavaScript 对象中。我将像这样使用 Azle 的名称空间:
在浏览器控制台中键入 az.hold_value.fetched_data
,然后点击回车。您可以看到,我们可以通过简单地使用这个对象随时访问我们的新冠肺炎数据。
首先,我们想在用户从下拉列表中做出选择时重新绘制折线图。让我们使用 Azle 的“change
”事件来实现这一点。
为了添加一个事件到我们的元素中,我们使用 Azle 的add_event
函数,就像我们之前对按钮所做的一样,目标是选择的元素,就像我们对样式所做的一样:
如果您刷新浏览器并从下拉列表中进行选择,您应该会看到提示选择。
现在我们只需要重绘折线图而不是调用 alert。我们之前写的draw_line_chart
函数已经准备好了。它已经将数据和选择作为参数,并相应地重新绘制图形。所以我们需要做的就是将draw_line_chart
添加到下拉菜单的add_event
函数中:
注意,我们使用的是存储在az.hold_value.fetched_data
中的数据。我们还使用了一个叫做grab_value
的新 Azle 函数;这允许我们获取用户在元素上选择的任何值(像往常一样,用class name
和class instance
定位)。
让我们看看它是否有效:
太美了。
这是目前的代号。
向其他元素添加事件也是同样的方式。但是这些元素涉及到调用一些后端模型来使用我们的新冠肺炎数据进行预测。因此,在添加其他事件之前,让我们开始处理后端模型。
对于“后端”,我们需要一台服务器来支持计算我们的预测所需的繁重工作。我们可以使用云提供商,如数字海洋或亚马逊网络服务,但是因为我们只是在原型制作想法,我们将使用我们自己的本地计算机。
让前端与后端通信需要一个 web 服务。web 服务将允许我们向服务器发送请求并接收一些回报。这就是我们所需要的,因为我们想从我们的前端给一个后端模型数据,并返回一个预测。
我们将使用一个名为 Flask 的轻量级 web 框架来用 Python 构建我们的 web 服务。让我们现在做那件事。一开始,我们创建了我们的index.html
文件。让我们将另一个文件添加到同一个文件夹中,将其命名为predict.py
:
我只是复制了 index.html 文件,并删除了内容,使一个空的predict.py
文件。
现在我们将添加一些基本的 Flask 代码到我们当前空白的predict.py
文件中。Flask 的文档中有一个快速入门指南,向我们展示了设置所需的最少代码:
我们将需要更多的东西,比如额外的库,以使我们的 web 服务适合我们的目的。下面是我们的predict.py
文件的样子:
在 JavaScript 中,我们使用“cdn”向应用程序添加额外的库,而在 Python 中,我们使用import
语句。上面我们正在导入 Flask,以及“request”和“jsonify”库,这将使我们能够从前端接收数据和向前端发送数据。
我们还设置了一个“路由”,它是我们服务的“端点”。回想一下我们在从 REST API 获取新冠肺炎数据时对端点的讨论。如果你认为我们正在开发自己的 REST API,那么你是对的:)
如今,企业软件中的许多后端机制都以服务的形式提供,作为 REST APIs 使用。这使得修补各种功能以及维护和扩展应用程序变得更加容易。
在我们的predict.py
文件中的“route”下面,我们创建了一个函数。Python 函数看起来和 Javascript 函数不一样,但思想是一样的;一段可以接受参数并返回值的模块化代码。我给我们的函数预测员打了电话。
最后,我们在文件末尾指定“端口”。端口是一个编程的停靠点,它允许外界访问我们的本地系统。我选择了 5000 端口。如果您愿意,您可以选择不同的号码(如果它已经被使用,您的电脑会告诉您)
让我们启动我们的 web 服务,看看它是否工作。如果一切顺利,我们将添加我们的预测模型,并开始为我们的应用程序提供真实的预测,以进行消费和可视化。
到目前为止,我们只是使用浏览器和记事本来创建我们的应用程序。但是现在我们需要直接与我们的操作系统对话,为此我们必须使用终端(PC 上的命令提示符)。
我用的是苹果电脑,所以你在这里看到的一切都会在苹果电脑上。但是同样的一般步骤也适用于 PC。开放终端。在 Mac 中,最快的方法是通过键入“command +空格键”和键入“终端”来使用 Spotlight 搜索:
在终端打开的情况下,通过运行以下命令进入我们的my_app
目录:
cd Desktop/my_app/
…然后按回车键。现在键入:
ls
…然后再次按回车键。
你应该可以看到 index.html 和predict.py
文件。很难相信我们的整个应用程序只有 2 个文件。
由于当今工具中可用的抽象级别,我们只需要最少的代码来创建一个完整的应用程序。
现在,通过运行以下命令来启动我们的 web 服务:
python predict.py
…然后按回车键。
您现在正在运行一个 web 服务,将您的 Python 代码公开给任何能够与之通信的应用程序。考虑到现在 Python 中有这么多高性能的库,这很酷。想想你能创造什么!
我们的后端在" localhost ", port 5000 上被服务,带有一个端点叫做" predict_covid ",接受参数叫做" x "和" y "。要“通过网络”传递所有这些信息,我们可以构建以下 URL:
[http://localhost:5000/predict_covid/?x=100&y=400](http://localhost:5000/predict_covid/?x=100&y=400)
这是在浏览器中使用 URL 时与 REST API 通信的标准方式。那个?标记在第一个参数名之前,而&在第二个参数名之前。在浏览器中打开一个新标签,将上面的 URL 添加到顶部,然后按回车键。您应该看到以下内容:
我们的 web 服务只是返回我们为“x”和“y”传递的值。不太令人兴奋,但它确实证明了我们的 web 服务是有效的。
让我们把它们端到端地连接起来,这样我们用 JavaScript (Azle)编写的前端应用程序就可以向我们的 web 服务(而不是浏览器)传递数据,并从其接收数据。如果我们成功了,剩下唯一要做的事情就是用 Python 构建一个好的预测模型。
我们需要将以下数据传递到我们的后端:
- 下拉选择
- 滑块值
- 日期和案件数量
注意:我们可以在后端获取 covid 数据,而不是通过网络传递它,但是这样我们会获取数据两次。因为我们只预测几个日期和值,所以使用已经从前端获取的数据更有意义。
我们已经知道如何使用 Azle 的grab_value
函数从 UI 元素中“获取”值。我们还在我们的az.hold_value.fetched_data
对象中很好地构造了我们获取的数据。
要将它发送给我们的 web 服务,我们可以再次使用 JavaScript 的fetch
API。相反,让我们在按钮的事件监听器中使用 Azle 的call_api
函数:
下面是正在发生的事情的分类:
我们在按钮上添加了一个“事件监听器”,就像我们之前在下拉菜单上做的一样。我们还在事件的函数属性中添加了 Azle 的call_api
,指定了 Flask 服务的 URL,“x”和“y”参数,以及数据返回时的警告。
刷新浏览器并点击预测按钮:
我们的应用程序正式与我们的后端服务进行交互,向我们的 Python 代码传递数据,并从它接收响应。让我们用我们需要发送的实际数据来替换参数“x”和“y ”,并从滑块值中添加预测范围。
在这里,我从现有的get_dates_and_cases
函数中获取滑块值以及“x”和“y”数据。
我们现在已经安排好了我们的活动:
最后,我们来建立一个预测模型,根据用户选择的类型(死亡、确诊、痊愈)和层位进行预测。经过一番搜索,我找到了一个名为 Prophet 的库,它是由脸书开源的。
脸书最近也发布了他们的神经先知版本。
Prophet 是一个时间序列预测库,有一个针对 Python 的快速入门指南,应该可以让我们开始运行。
我们最终的predict.py
文件从前端接收数据(我们已经知道如何做),准备 Prophet 库所需的数据(如他们的文档中所指定的),训练预测模型,进行预测,并返回结果。
所有这些都在大约 10 行代码中。还不错!(这种抽象又为我们工作了)。
在我们的index.html
文件中,我添加了两个新的函数来为第二次跟踪准备数据(显示预测)。您会看到它与我们为 trace 1 编写的非常相似。我还将新的draw_forecast
函数添加到了我们的call_api
函数的done
属性中。
看看最后的index.html
看看变化。您应该将最后的更改视为熟悉的代码。如果有什么东西看起来很陌生,在网上搜索一下,了解一下为什么它会被添加进来。
让我们最后一次刷新浏览器,看看完整的应用程序。
我们实现了最初清单上的所有目标:
在唱完之前,我想添加一个微调器,当用户点击预测时,他们就知道要等待结果。我还希望 Plotly 图表是透明的,这样我们就可以看到我们有漂亮的蓝色背景。最后,我要改变我们的应用程序的标题,因为它是真实的。
你可以在这里找到最终代码:
此时您不能执行该操作。您已使用另一个标签页或窗口登录。您已在另一个选项卡中注销,或者…
t.co](https://t.co/8xS6zvu9e3?amp=1)
而这里是 决赛 CodePen 。请注意,因为 CodePen 没有运行我们的后端 web 服务,所以您不会看到预测。但是在您的本地机器上一切都应该正常工作。
这些是构建任何应用程序的主要部分。有了这些技能,你就可以进入软件开发的世界(或者只是把它作为一种爱好来构建东西)。
就是这样。这篇文章可能看起来很长,但是考虑到你刚刚用所有的主要部分制作了一个完整的应用程序,还不算太糟糕。如果你遇到困难,可以在推特上联系我。我们将一起研究代码。不要停在这里。试试其他库。其他用例。其他任何东西。只是建造。
再说一次,机会的唯一看门人是你自己。
全栈熊猫
Python 数据分析
流行数据分析库的鲜为人知的功能
查尔斯·雷在 Unsplash 上的照片
我所知道的最有价值的商品是信息——戈登·盖柯
我确信我的大多数读者都熟悉 Python 中流行的 Pandas 库。该库提供数据结构来帮助操作关系数据。
Pandas 是一个在数据科学从业者中非常受欢迎的库。Hackr.io 被评为十大数据科学库之一。我本人在工作和个人数据科学项目中广泛使用熊猫。
本文将展示这个库的一些鲜为人知的功能。
注:如果你没有太多和熊猫相处的经历,我强烈推荐你阅读下面这本书:
获取在 Python 中操作、处理、清理和处理数据集的完整说明。针对 Python 进行了更新…
shop.oreilly.com](http://shop.oreilly.com/product/0636920050896.do)
用于数据分析的 Python是入门熊猫和 Numpy 的绝佳入门读物。这本书是由熊猫图书馆的创建者韦斯·麦金尼写的。
我个人收藏的 Python 数据科学电子书
内存使用
熊猫图书馆提供两种类型的数据结构,即系列和数据帧。
这两个都有一个方法,分别叫做[pandas.DataFrame.memory_usage(](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.memory_usage.html?highlight=memory_usage#pandas.DataFrame.memory_usage))
和[pandas.Series.memory_usage()](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.Series.memory_usage.html?highlight=memory_usage#pandas.Series.memory_usage)
。这些方法提供了这些数据结构的内存使用度量。
请注意,当我们添加了deep=True
选项时,这些数字是如何不同的。这是因为默认情况下,memory_usage()
方法忽略了对象的数据类型。通过设置deep=True
,我们为熊猫对象检索精确的内存占用度量。
在处理非常大的数据集时,跟踪内存使用情况非常关键。通常,机器学习研究人员会从事新型机器学习模型的原型制作。在这样的过程中,一个常见的错误是忽略硬件容量。跟踪内存消耗有助于压缩使用,从而确保顺利的原型开发周期。
表达式评估
Pandas 库支持对其数据结构进行非常快速的矢量化操作。通常,业余数据科学家会使用 Python 的标准运算符对大型数据集进行数值计算。
使用 Pandas 提供的[pandas.eval()](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.eval.html?highlight=eval#pandas.eval)
方法可以更有效地执行这样的计算。表达式是通过构造等效字符串来创建的。
为了说明这一点,让我们创建几个各有 100 万行 x 100 列的样本数据帧,并比较计算简单算术所需的时间。
如果我们观察使用 python 操作符与pandas.eval()
计算简单加法运算所花费的时间,Pandas 方法花费的时间几乎少了 5 倍。
与标准 Python 操作符相比,pandas.eval()
方法不仅耗时更少,而且执行计算时消耗的内存也更少。这是因为 Python 操作为每个中间子操作分配内存。例如,在上面的代码中,内存被分配给df_A + df_B
的输出,然后被添加到df_C
等等。
相反,在pandas.eval()
中,整个表达式一次求值。这有助于加速计算,尤其是对于非常大的数据。除了算术运算,pandas.eval()
还支持众多其他运算。
请随意尝试不同类型的操作,以加速您的数据分析项目。
询问
除了表达式求值器,Pandas dataframe 还有一个名为pandas.DataFrame.query()
的查询方法。当使用这种方法时,从 SQL 背景过渡到 Python 的数据科学家将会特别激动。
query()
方法为我们提供了过滤数据帧的非常有效的方法。类似于eval()
方法,query()
方法将字符串形式的表达式作为输入。
查询方法是一种极其简单但功能强大的处理数据的方法。它的好处与eval()
相似,花费的时间更少,消耗的内存也更少。
在传统方法中,上面的查询用 Python 写成这样:
df[(df[‘adults’] > 2) && (df[‘lead_time’] < 40)]
我们可以清楚地看到,与 query 相比,传统方法看起来相当丑陋。因此,在代码审查期间,查询方法也使审查者的生活更加轻松。
插入文字
熊猫有很多方法来处理缺失值。一些比较流行的方法包括isna()
、dropna()
、fillna()
法。
除了前面提到的方法,Pandas 还有一个pandas.interpolate()
方法,在丢失的数据点执行插值。
当需要对数值缺失值进行快速插补时,这种方法非常有用。此外,interpolate()
还可以帮助估计数组中其他两个数值项之间的值。插值在输入法序列分析中有重要的应用,我们不会在这篇文章中讨论。
插值前与插值后
interpolate()
拥有一套强大的数据插值方法。该方法充当流行的 Python 科学计算库 SciPy 的插值方法的包装器。
我强烈推荐阅读这个方法的 API 参考,以便更好地理解它是如何工作的。
测试
通常情况下,数据科学博客(包括我的博客)包含的内容与花哨的算法、库、数学/统计有关,很少提及软件工程原理,如设计和单元测试。因此,我惊喜地发现 Pandas 拥有专注于软件测试的方法。
像pandas.testing.assert_frame_equal
这样的单元测试方法可以形成数据分析的单元测试管道的一个组成部分。
在上面的例子中,我们得到一个断言错误作为输出,因为原始数据帧df
的副本中的一列的数据类型发生了变化。
同样,我们可以使用pandas.testing
方法来测试维度、索引、列名等。我们的数据。
下次你为你的个人/专业数据科学项目开发一个测试驱动的开发环境时,记得结合这些方法。😇
式样
Pandas 的另一个我直到最近才知道的特性是造型 API。对于涉及使用 html 笔记本渲染器(如 Jupyter Notebooks )交流发现(EDA)的数据科学项目,样式 API 尤其方便。Pandas 中的样式提供了一种创建演示就绪表格的方法,以有效地交流见解。
上表是从 Kaggle 上的 FIFA 19 数据库中随机抽取前 100 名足球运动员(基于“总体”栏)创建的。在这个子集上,我们应用pandas.DataFrame.style
的方法来呈现上面显示的漂亮输出。
代码如下:
我们看到,使用 style 中的方法,我们能够使用很少几行代码创建数据表的直观表示。
例如,值和工资列在每个单元格中都有水平条形图,显示一名足球运动员相对于其他运动员的价值和工资。
让我惊讶的是,在保留数据集原始结构的同时,突出显示这些关键信息是多么容易。每个像素呈现更多的信息,而无需添加单独的可视化。
结论
Pandas 是 Python 在数据科学中受欢迎的最大因素之一。这个图书馆的能力真是令人难以置信。
尽管我已经虔诚地使用 Pandas 两年多了,但我并不知道它提供了许多隐藏的功能。希望以后能进一步探索,发现更多俏皮的功能。
本文中使用的其他数据集可以在 Kaggle 这里找到。
代码发布在我的 Github 这里。
如果你喜欢这篇文章,请在 Medium 上关注我。另外,请随时在 LinkedIn 上与我联系。最后,在这些困难时期保持安全。下次见。✋
参考资料:
[1]https://pandas . pydata . org/pandas-docs/stable/reference/index . html # API
[2]https://pandas . pydata . org/pandas-docs/stable/user _ guide/index . html
[3]https://www.kaggle.com/karangadiya/fifa19/data
https://www.kaggle.com/jessemostipak/hotel-booking-demand
https://pythonspeed.com/datascience/
冠状病毒封锁下的儿童有趣的人工智能活动
发现人工智能如何轻松地让您的锁定不那么无聊!
你好,我是尼克🍌 on Unsplash
冠状病毒正使整个地球陷入停顿,既然孩子们将有充足的时间与父母呆在一起,为什么不利用这段时间尝试一些有趣的活动,同时教给他们一些人工智能(AI)呢?
我知道人工智能有时会令人望而生畏,但是下面的例子是为了便于父母使用而特别选择的。你还在等什么?
沃洛德梅尔·赫里先科在 Unsplash 拍摄的照片
liza 是第一批被创造出来的聊天机器人之一(1964)。聊天机器人的工作原理是输入一些东西,它会给你一个答复。答复有些有限;不过,和它聊天还是很好玩,也很安全。正因为如此,强烈推荐给还在学习读写的孩子。
伊莉莎可以在这里找到https://tinyurl.com/AIEx-Eliza
Mitsuku 是目前存在的最先进的聊天机器人之一。它获得了各种奖项,可以谈论大多数话题。由于它是一个先进的聊天机器人,建议年龄较大的儿童使用,因为它可以处理冠状病毒、世界历史等成熟的话题。因此,建议较小的儿童在监护人的陪同下使用。
在这里可以找到三月https://tinyurl.com/AIEx-Mitsuku
语句生成器获取一个图像作为输入,并为该图像创建一个标题。它通过理解组成图像的对象并给它们一个标签来做到这一点。然而,人工智能并不完美,有时它会犯一些错误。可以要求孩子们纠正句子生成器的标题。
句子生成器可以在这里找到https://tinyurl.com/AIEx-See
乔恩·泰森在 Unsplash 上的照片
T hing Translator 给一个物体拍照,并用自然语言(如西班牙语、意大利语等)给我们描述。)我们的选择。孩子们可以很容易地使用它来学习如何使用他们在学校学习的一种外语来发音家里发现的东西。
翻译的东西可以在这里找到https://tinyurl.com/AIEx-Translator
E 莫吉清道夫 Hun t 使用人工智能通过移动设备的摄像头来识别现实世界中的表情符号。它从展示一把钥匙的图片开始,孩子必须在房子里四处寻找一把真正的钥匙。然后,人工智能将使用移动设备的摄像头来验证他是否正确。这个游戏可以帮助小孩子了解房子周围发现的不同物体。
表情符号寻宝游戏可以在这里玩https://tinyurl.com/AIEx-Emoji
Quick,Draw!是一种游戏,用户必须画出某样东西,人工智能必须猜出它是什么。这个游戏适合任何年龄的孩子。
快速抽奖可以在这里玩https://tinyurl.com/AIEx-Draw
影子艺术可以让孩子们仅仅用手来学习如何制作影子木偶。然后,人工智能将通过你的移动设备的摄像头来看他们的手,并决定它是好还是不好!
这里可以使用皮影艺术https://tinyurl.com/AIEx-ShadowArt
桑迪·米勒在 Unsplash 上拍摄的照片
I 一张智能的纸展示了一个程序如何玩井字游戏,并通过遵循简单的规则赢得胜利。
Manuel n Geli 在 Unsplash 上拍摄的照片
emi-conductor 可以让你像真正的指挥一样管理一个真正的管弦乐队。一个人所要做的就是看着移动设备的摄像头并移动他的手!
reddiemeter 是一个卡拉 ok 人工智能,它允许你唱佛莱迪·摩克瑞的歌,然后评价你有多好。这个游戏不仅对正在学习阅读的孩子来说很有趣,对所有的家庭来说也很有趣!
可以在 https://tinyurl.com/AIEx-Freddy 找到测速仪
另一方面,成年人实际上可以通过玩游戏来帮助科学家打败新冠肺炎。点击此链接了解详情。
如果你不知所措,或者只是想联系我,请在下面留下评论,或者通过以下社交媒体账户之一给我发消息🐦推特,🔗 LinkedIn 或者😊脸书。
Alexei DingliProf 是马耳他大学的 AI 教授。二十多年来,他一直在人工智能领域进行研究和工作,协助不同的公司实施人工智能解决方案。他的工作被国际专家评为世界级,并赢得了几个当地和国际奖项(如欧洲航天局、世界知识产权组织和联合国等)。他已经出版了几本同行评审的出版物,并且是马耳他的成员。由马耳他政府成立的人工智能工作组,旨在使马耳他成为世界上人工智能水平最高的国家之一。
给 Python 程序员的有趣的情人节“礼物”创意
杰西·戈尔在 Unsplash 上的照片
写一些代码为你的情人做些酷的东西。
情人节就要到了。我还没决定给我妻子买什么礼物。挑选合适的礼物对我来说通常是一项非常具有挑战性的任务,因为我在爱情领域不是一个有创造力的人。大多数时候,我只是用礼品卡送她一些花,因为我只是不想再次犯下送她体重秤的错误,这让她真的很生气,尽管我认为一个支持蓝牙的数字不锈钢智能秤真的很酷。
今年,我想我会做一些不同的事情。在过去的几年里,我开始比以前更多地使用 Python,并且发现一些有趣的项目只需要几行代码就可以完成。在这篇文章中,我想分享三个很酷的迷你项目,它们有可能成为礼物的创意。然而,只是一个快速的免责声明,使用它们的风险由你自己承担,因为这是一个所谓的“书呆子”或“极客”的建议:)
展示你的心
你要做的第一件简单的事情是使用numpy
和mtaplotlib
库画一个心形。因此,首先请确保你已经在你的电脑上安装了这两个软件,比如使用 pip
工具安装它们。将以下代码复制并粘贴到您自己的 Python IDE 中。
如您所知,我们使用公式(x**2+y**2–1)**3-x**2*y**3=0
,其中**表示符号后数字的幂,以生成一系列构成心形的点。
运行上面的代码,你会看到一个如下的红心。你可以用代码来改变尺寸,分辨率,甚至颜色!如果你足够大胆地使用这个礼物的想法,我会把这些任务留给你:)
创建动画图像
另一件有趣的事情是使用imageio
库创建动画图像,它可以从单个静态图像生成动画图像。如上所述,您的计算机需要安装imageio
库。
在你的相册中,找到 10 张或更多适合情人节的图片,放在一个名为“source_images”的文件夹中。在您的 IDE 中运行以下代码。
实际上,您也可以从视频剪辑中创建动画图像。更详细的说明可以参考我的上一篇文章。
创建单词云
我想分享的最后一个想法是,你可以为你爱的人创建一个单词云。为此,我们将使用wordcloud
、imageio
和matplotlib
库。
我编写了一个函数,它接收一个字符串、一个为单词云提供蒙版的图像文件以及所创建的单词云的目标图像文件。此外,我还编写了另一个函数,可以生成色调、饱和度相同但亮度不同的随机红色。我选择了红色,因为这是情人节的主题颜色。
下面提供了完整代码。
就我个人而言,对于字符串,我只是列出了一堆我们已经访问过或计划在不久的将来访问的城市和国家,我创建了一个名为words
的变量来存储这些名称。为了验证概念,我选择了我们从第一个想法中创建的心形图像,保存图像名为plt.savefig(‘heart.png’)
。
>>> words = 'Paris, France, Houston, Vegas, Italy, America, Rome, Austin, Seattle, Miami, London, Boston, Beijing, Shanghai, Macau, Moscow, Venice, Germany, Australia, Netherlands, Detroit'
>>> generate_word_cloud(words, 'heart.png', 'word_cloud.png')
运行上述代码后,创建了如下图。
外卖食品
上述迷你项目只是向你展示了一些有趣的想法,这些想法可能会被用于你的情人节。请通过尝试不同的参数组合来探索这些选项。另外,你可以把这些想法结合在一起,就像我在第三个想法中做的那样。比如你可以制作多个字云,和一些真实的图像混合在一起,创造一些更加个性化的动画形象。
情人节快乐!
参考
有许多数学曲线可以产生心形,其中一些已经在上面说明过了。第一个…
mathworld.wolfram.com](http://mathworld.wolfram.com/HeartCurve.html) [## 使用 Python 创建动画图像
没有 Photoshop?没问题!了解如何从静态图像列表或视频中轻松创建动画图像…
medium.com](https://medium.com/swlh/python-animated-images-6a85b9b68f86) [## 使用自定义颜色-word cloud 1 . 6 . 0 . post 16+gf E0 b 6 ff 文档
编辑描述
amueller.github.io](https://amueller.github.io/word_cloud/auto_examples/a_new_hope.html)
ARMA、VAR 和 Granger 因果关系的乐趣
使用 Stata、R 和 Python
杰克·希尔斯在 Unsplash 上的照片
将你在日食期间看到的东西与夜晚的黑暗相比较,就像将海洋比作一滴泪珠。~温迪·马斯
温迪大概是在暗示,在昏暗的月蚀中,视觉的清晰度比普通的夜晚要清晰得多。或者,与裹尸布时期的黑暗相比,普通的夜晚算不了什么。同样,我觉得时间序列的概念会一直模糊不清,直到数学让它变得更容易理解。即使在日常生活中使用合法数学的重复出现实际上类似于模糊,然而通过数学证明澄清时间序列的强度与利用可测量的统计工具来对抗和设想一些信息相比不算什么。然而,在我的读者跳入大海之前,我希望他们今天能留下“泪珠”。解释是每个夜晚都有月亮,而在晚霞中漫步正是感性的,直到科学交出一些光来看透。这篇文章背后的动机是通过各种工具精确地看到一点时间序列(就像每个夜晚一样),期望数字澄清的模糊不清是不常见的和遥远的,但无疑是重要的,以另一种方式看一看天空。
议程
文章议程,图片来源:(图片来自作者)
白噪声
在文学术语中,白噪声是所有频率的声音同时产生的声音。这是可以听到的,我们也一定听到了。这里有一个链接如果我们还没有。在我们的例子中,白噪声是满足三个条件的时间序列;均值为零、方差不变且序列中两个连续元素之间不相关的序列。白噪声序列是不可预测的,因此表明它不需要任何进一步的分析。为了测试第三个条件,我们通常使用 ACF(自相关函数)图,它测试两个连续观察值之间的相关性。如果相关性不为零,那么该滞后处的棒线将穿过每一侧的边界线。
R 视图
白噪音系列,图片来源:(图片来自作者),图片工具:R
第一个系列具有近似零均值和 1 方差,而第二个系列具有近似 0.5 均值和 0.7 方差。ACF 图没有跨越阈值边界的波段,除了第 0 个滞后(这是观察值与其自身的相关性)。第二个系列的 ACF 图具有明显的滞后相关性。视觉上,我们也可以验证第二系列中的变化。
R代码
Python 视图
白噪音系列,图片来源:(图片来自作者),图片工具:Python
Python 代码
哪个看起来更简单?哪一个更像满月?
平稳性
所有的碱都是碱,但不是所有的碱都是碱。~学校化学
平稳时间序列遵循与白噪声序列几乎相似的条件。固定系列必须满足三个条件:具有恒定平均值、恒定方差且无季节性模式的序列。基本上,一个统计性质如均值、方差、自相关等都随时间恒定的序列。因此,所有白噪声序列基本上是平稳的,但所有平稳序列并不总是白噪声,因为均值可以是大于零的常数。任何时间序列建模的关键步骤是在尝试拟合模型之前将原始序列转换为平稳序列。无需等待日食,公平地说,这类似于在拟合 OLS 模型之前高斯马尔可夫假设是必要的。谢伊·帕拉奇在这里做了出色的工作解释了稳定的原因海洋确实是无限的。底线是用非平稳数据估计模型可能会产生不正确的模型估计。接下来我们来看一些非平稳序列的例子。
非平稳时间序列例子,图片来源:(图片来自作者),图片工具:R,图片代码:链接
流行的平稳性测试
在我们测试平稳性条件之前,最好的事情是了解可以将原始时间序列转换为平稳序列的流行方法。最传统和方便的方法被称为“差异”和“增长率”。还有更多定制的方法,如“去除趋势分量”、“电平移动检测”等,这些方法默认情况下都包含在当今高端时间序列特定的机器学习包中。
差异是从序列中去除随机趋势或季节性的一种非常常见的做法。一阶差分意味着在滞后 1 时简单地取当前和先前观测值的差,而二阶差分在滞后 2 时做同样的事情。增长率方法(有时称为一期简单回归)主要用于金融或宏观经济数据(如人均 GDP、消费支出)。它采用与前一时间段相关的当前观察值的对数(例如 log[xt÷xt-1])。
增广的 Dickey-Fuller (ADF)是最常用的平稳性检验,它遵循自己的增广的 Dickey-Fuller 分布,而不是常规的 t 分布或 z 分布。零假设是数列不平稳,而替代假设是数列平稳。
R 中的 ADF 测试
ADF 测试,图片来源:(图片来自作者),图片工具:R,链接到数据
R 代码
Python 中的 ADF 测试
ADF 测试,图片来源:(图片来自作者),图片工具:Python,链接到数据
Python 代码
我们是否注意到使用这两种工具我们得到了相同的 结果 ,然而在 R & Python 中从 ADF 测试中获得的 p 值仍然有一些差异?嗯,一个可能的答案是两个软件使用的默认延迟是不同的。
在 Stata (Dickey-Fuller)中进行 DF 测试
DF 测试,图片来源:(图片来自作者),图片工具:Stata,链接到数据
滞后=0 时的 ADF 检验成为 Dickey-Fuller 检验。结果可能或可能不总是相似的,但是,在这个特定的例子中,我们观察到相似性(即,原始序列不是静止的,而变换后的序列是静止的)。在使用 Stata 时,零假设是相同的,即数列不平稳,而替代假设是数列平稳。判定规则是,如果检验统计量的绝对值小于 5%临界(来自 ADF/DF 分布),那么我们不能拒绝零,并得出序列不是平稳的结论。因此,上图左侧的两个图表的绝对值小于检验统计量,因此我们无法拒绝零假设。
Stata 代码
ARMA 过程
自回归(AR)模型意味着序列的当前值取决于序列的过去值。对于一个简单的 AR(1)模型,假设序列的当前值取决于它的第一个滞后。参数系数值、相应的标准误差及其 p 值暗示了相关性的强度。假设误差项正态分布,均值为零,方差为σ平方常数。而移动平均(MA)模型意味着一个序列的当前值取决于该序列过去的误差。AR 模型可以表示为无限 MA 模型,反之亦然。ARMA 是一种混合模型,其中当前值考虑了序列中的 AR 和 MA 效应。
基本的 Box-Jenkins 流程图,图片来源:(图片来自作者)
George Box 和 Gwilym Jenkins 提供了一个工作流来预测时间序列数据。尽管该系统具有各种其他表示和定制,但是基本的概述将保持几乎相似。根据问题的性质和预测算法的复杂程度,此工作流可以扩展到分析工作台中的每个细节级别。上面的流程图是我的代表,只是为了记住主要步骤,我敦促我的读者在处理越来越多的时间序列问题时,也做出自己的流程图。
在我让我的读者更进一步之前,我想提一下,阿卡克信息准则(AIC)、贝叶斯信息准则(BIC)或汉南-奎因信息准则(HQIC)在帮助选择最佳模型方面做了同样的工作。我们总是努力寻找一个不太节俭的模型(具有较少的参数),但同时选择上述度量的相对较小的值。IC 越小,残差越小。虽然 AIC 是最常见的,但 BIC 或 HQIC 在惩罚更多参数方面对大数据更先进和更强大。
Stata 中的 ARMA
ARMA(2,1)的输出,图像源:(来自作者的图像),图像工具:Stata,链接到数据
模型迭代输出,ARMA,图片来源:(图片来自作者),图片工具:Stata,链接到数据
ARMA 过程的 Stata 代码
AIC,BIC 得分图,GDP 增长率,ARMA,图片来源:(图片来自作者),图片工具:Python,链接到数据
AIC 在 ARMA (4,2)最小,BIC 在 ARMA (2,0)最小。BIC 提出了一个不太节俭的模型,有 3 个参数,而 AIC 模型有 8 个参数。ARMA(2,0)的 sigma 系数为 0.0052,大于 ARMA(4,2)的 0.0049。ARMA(2,0)的最大对数似然为 461.51,低于 ARMA(4,2)的 466.40。然而,以五个额外参数为代价,我们可能选择 ARMA(2,0) 。适马是模型假设的恒定标准差,用于模型在幕后使用的创新或随机冲击(不相关的零均值随机变量)。在 Stata 中,它是 sigma,但在 R 中,同样的东西显示为 sigma 的平方。
ACF 和残差图,ARMA(2,0),图像源:(来自作者的图像),图像工具:Stata,链接到数据
用于检查模型残差中自相关的 Stata 代码
R 中的 ARMA
ACF 图,GDP 增长率残差,ARMA(p,q),图片来源:(图片来自作者),图片工具:R,链接到数据
AIC,BIC 得分图,GDP 增长率,ARMA,图片来源:(图片来自作者),图片工具:R,链接到数据
ARMA 适马估计,GDP 增长率,ARMA,图片来源:(图片来自作者),图片工具:R,链接到数据
虽然我们的目标是获得一个随机冲击方差最小的模型,但是选择红线意味着相对大量的参数。这样做的成本高于蓝线,因为蓝线需要维护的参数较少。
ARMA 过程的 r 代码
Python 中的 ARMA
ACF 图,GDP 增长率残差,ARMA(p,q),图片来源:(图片来自作者),图片工具:Python,链接到数据
AIC,BIC,HQIC 得分图,GDP 增长率,ARMA,图片来源:(图片来自作者),图片工具:Python,链接到数据
ARMA 过程的 Python 代码
对 ARMA 的观察
图片来源: (图片来自作者),图标来源:( Stata , R , Python )
易于访问:这一点非常清楚,因为 Stata 是一个经过授权的设备,R 和 Python 都是开源的。从今以后,接触任何开源工具都比坐等授权编程简单。
用户友好性:Stata 提供了一个图形用户界面(GUI ),这也是它对学徒或过渡人员更有吸引力的原因。它就像 SPSS、SAS 或 Eviews。它类似于 SPSS、SAS 或 Eviews。我以某种方式了解了我在学习上的差距,我需要学习的主题,以及可用技术的目录。相反,R 和 Python 都是脚本工具,除非我主动,否则它们不会交互。任何前端有图形用户界面的设备在开始的时候都不那么笨拙。结构变得更加精炼,不像在脚本工具中偶然发现的那样。焦点从解释输出转移到首先实际获得输出。我想这是任何此类工具价格高昂的主要原因。
易学性:上述用户友好性的优势无疑使通过 Stata 学习变得简单而有趣。然而,在这种情况下,R & Python 的顺序可能不是固定的,因为它也取决于一个人接触该工具的顺序。我先接触了 R,后来接触了 Python,然而,事情也可能与大多数人相反。
可视化:python 可视化工具的舒适性及其魅力是毋庸置疑的,因为它是一个开源的免费软件。相反,R 显然是接班人。然而,不知何故,我从这个以及其他一些练习中感觉到,尽管与默认的 plot 方法相比,ggplot 可以挽救很多次,但是一开始就利用 python 会更好。如果 python 中有类似于 R 时间序列绘图功能的东西,可以自动调整 x 轴标签,那就更好了。他们确实停用了 sns.ts plots 并引入了 sns.lineplot(),但它并不是每次都与 r 匹配。
ARMA 估计:Stata 在这种特定情况下估计得更好。警告消息“可能的收敛问题:最优给定码= 1”在通过 R 的 ARIMA/ARMA 估计中非常常见,但在大多数情况下,它确实会生成除 p 值和标准误差之外的大多数输出。除此之外,异方差校正的标准误差有些难以实现,除非手动编码。与 R 不同,在 python 中可以很容易地访问异方差校正的标准误差,但是,statsmodels.tsa.arima_model 中的 ARIMA 函数通常会令人惊讶地停止工作。它迫使我们改用 SARIMAX。这就是为什么我们得到的 ARMA(1,1)与 Stata 或 r 中的 ARMA(2,0)非常接近。普通 ARMA/ARIMA 不允许由于滞后多项式反演而覆盖强制平稳性或可逆性。理想情况下,ARMA 过程也可以转换为滞后多项式项,并且它不能放在此函数中,这与 SARIMAX 不同。详细讨论可以在这里找到。
可伸缩性:在大多数情况下,python 比任何其他工具都更受青睐。在这种特定情况下,我不知何故发现了在各阶之间迭代以估计模型系数的过程,并且在 5 乘 5 矩阵上绘制残差相对比 R 更容易、更轻便。老实说,在这个示例中使用 R 也很有趣,我无意贬低这个几乎同样强大的工具。
向量自动回归
乍一看,VAR 似乎是单变量自回归模型的简单多变量推广。乍一看,它们被证明是现代宏观经济学中的关键实证工具之一。~Del Negro 和 Schorfheide
研究人员发现,( p,q)阶的 ARMA 过程可以转化为(p)阶的 AR 过程。因此,任何两个或多个序列的 AR(p)过程都可以转化为 VAR(p)过程。在我们上面的例子中,我们看到了两个不同的序列,120 个时间点的人均 GDP 和消费支出。这两个单独的序列会有自己的 ARMA(p,q)过程。基于 Box-Jenkins,我们推导出,一旦我们在每个序列中实现平稳性,我们可以在 AR(p)和 MA(q)的不同值上迭代该序列,并选择 ARMA(p,q)的阶数,其中我们具有最小的 AIC、BIC 或 HQIC。在这种情况下,人均 GDP 的平稳序列是 GDP 增长率,根据我们使用的工具,它遵循 ARMA(2,0)或 ARMA(1,1)。此外,平稳的消费支出序列可以是其增长率遵循 ARMA(1,0)过程。
AIC,BIC 得分图,消费增长率,ARMA,图片来源:(图片来自作者),图片工具:R,链接到数据
当我们要处理多个线性时间序列时,需要向量自回归的基本框架。如果我们考虑两个序列 X 和 Y,那么 VAR 运行两个回归模型。第一个方程组根据 X 和 Y 的过去滞后值以及 X 的一些误差项来回归 X。类似地,在第二个方程组中,我们根据 Y 和 X 的过去滞后值以及 Y 的一些误差项来回归 Y。滞后的阶数可以从 0 阶到 p 阶,就像 AR(p)模型或 ARMA(p,q)模型一样。如果我们有 3 个多元线性时间序列要处理,那么我们将有 3 个回归方程组,如果我们有 4 个多元线性时间序列要处理,那么我们将有 4 个回归方程组,因此如果有 k 个多元线性时间序列要处理,那么我们将有 k 个回归方程组。
VAR(2)模型,图片来源:(图片来自作者),图片灵感:链接
格兰杰因果关系
1956 年,诺伯特·维纳引入了一个概念,即在测量两个线性时间序列时,如果一个人可以通过使用两个序列的过去信息来预测第一个序列,而不是仅使用第一个序列的过去信息而不使用第二个序列,那么他可以推断第二个序列导致了第一个序列。第一个实际工作是由克莱夫·格兰杰完成的,此后这种方法被命名为格兰杰因果关系。经济学家格韦克在 1982 年也做了进一步的发展,被称为格韦克-格兰杰因果关系。因此,这一概念进一步扩展了风险值模型的使用案例,人们可以从统计上检验一个时间序列是否是另一个时间序列的原因。如果我们有证据证明第一个数列格兰杰因果第二个数列,我们可以推断第一个数列是因,第二个数列是果。话虽如此,效果必须取决于原因的过去价值。此外,在存在结果的过去值的情况下,原因的过去值必须有助于识别结果的当前值。
Stata 中的 VAR
VAR(1)的输出,图像源:(来自作者的图像),图像工具:Stata,链接到数据
模型迭代输出,VAR,图片来源:(图片来自作者),图片工具:Stata,链接到数据
VAR 模型估计的 Stata 代码
AIC 在 VAR(1)最小,AIC = -1870.592。
BIC 在 VAR(1)最小,BIC = -1853.92。AIC 和 BIC 都提出了 VAR(1)模型。
VAR(1)上的拉格朗日乘数测试,图片来源:(图片来自作者),图片工具:Stata,链接到数据
上述测试类似于我们在发现 ARMA 模型后看到的 ACF 相关图。目标几乎类似于检查来自模型的误差之间是否有任何相关性。换句话说,我们不希望任何给定时间点的误差项与其滞后误差值之间有任何修正。这个检验的无效假设是错误没有序列相关性,而另一个假设是错误有序列相关性。决策规则是,如果 p>0.05,那么我们无法拒绝零假设,并得出错误确实没有序列相关性的结论。测试代码如下所示。
用于检查模型残差中自相关的 Stata 代码
Stata 中的格兰杰因果关系
一旦 VAR 模型被识别和估计,我们可能要测试 VAR(1)模型的因果关系假设。零假设是自变量和因变量之间没有短期因果关系。另一个假设是自变量和因变量之间存在短期因果关系。判定规则是如果 p <0.05 then reject the Null and conclude there is causality, else conclude there is no short-run causality.
VAR(1) Stability Test, Image Source : (Image from Author), Image Tool: Stata, 将链接到数据
建议在因果关系检验之前检查所选的 VAR 模型是否稳定。如果模型不稳定,那么它可能意味着要么进入模型的序列不是平稳的,要么需要进行长期因果关系的协整检验,这实质上意味着建立一个向量误差修正模型。然而,在这种情况下选择的 VAR(1)是稳定的。
VAR(1)的 Granger 检验,图片来源:(图片来自作者),图片工具:Stata,链接到数据
检验格兰杰因果关系的 Stata 代码
在 VAR(1)中,我们观察到有确凿的证据表明,总体而言,GDP 增长率与消费支出增长率之间存在一定的因果关系。有趣的是,反之亦然(从消费增长到 GDP 增长)并不成立。
因此,我们有证据表明,GDP 的增长率是消费支出增长率的格兰杰原因。话虽如此,效果(消费支出增长率)必须取决于原因(GDP 增长率)的过去值。此外,原因的过去值(国内生产总值增长率)必须有助于在存在结果的过去值(消费支出增长率)的情况下确定结果的当前值。
R 中的 VAR
VAR 模型选择,图片来源:(图片来自作者),图片工具:R,链接到数据
基于信息准则可变滞后阶数选择的 r 码
在 Stata 中,我们对 p 从 1 到 5 的范围迭代 VAR 模型,并逐个收集信息标准度量的值。然而,在 R 中,包“vars”允许我们提及我们想要使用的滞后的数量,它将根据信息标准为我们提供最佳顺序。我们在 ARMA 案例中已经看到,每个工具都不会评估相同的信息标准值,因此在某些度量的建议顺序上可能会有一些差异。在这种情况下,我们看到 AIC 提出了一个 VAR(3)模型,而不是 Stata 提出的 VAR(1)。然而,BIC(也称为 Schwarz IC)和 HQIC 提出了 r 中的 VAR(1)模型。因此,我们现在将 VAR(1)模型拟合到我们的联合收割机系列,并查看其输出。
VAR(1)的输出,图像源:(来自作者的图像),图像工具:R,链接到数据
风险值模型估计的 r 代码
一旦我们估计了 R 中的模型并简要查看了输出,就该测试模型产生的误差是否具有序列自相关了。这个检验的无效假设是错误没有序列相关性,而另一个假设是错误有序列相关性。决策规则是,如果 p>0.05,那么我们无法拒绝零假设,并得出错误确实没有序列相关性的结论。
VAR(1)上的序列相关检验,图片来源:(图片来自作者),图片工具:R,链接到数据
用于检查模型残差中自相关的 r 代码
我们现在知道 VAR 模型的误差没有序列相关性。因此,我们不需要重新考虑 VAR 的模型顺序,这意味着我们可以测试因果关系。
VAR(1)的 Granger 检验,图片来源:(图片来自作者),图片工具:R,链接到数据
在 VAR(1)中,我们发现有确凿的证据表明,总体而言,GDP 增长率与消费支出增长率之间存在一定的因果关系。有趣的是,反之亦然(从消费增长到 GDP 增长)并不成立。
注:即时因果关系是为了测试原因的未来值(国内生产总值增长率)是否必须有助于在存在效果的过去值(消费支出增长率)的情况下确定效果的当前值
检验格兰杰因果关系的 r 代码
Python 中的 VAR
VAR 模型选择,图片来源:(图片来自作者),图片工具:Python,链接到数据
基于信息准则可变滞后阶数选择的 Python 代码
我们得到了与 r 几乎相同的建议。我们现在将 VAR(1)模型拟合到我们的联合收割机系列,并查看其输出。
VAR(1)的输出,图片来源:(图片来自作者),图片工具:Python,链接到数据
用于 VAR 模型估计的 Python 代码
一旦我们在 Python 中估计了模型并简要查看了输出,就该测试模型产生的误差是否具有序列自相关了。这个检验的无效假设是错误没有序列相关性,而另一个假设是错误有序列相关性。决策规则是,如果 p>0.05,那么我们无法拒绝零假设,并得出错误确实没有序列相关性的结论。在 Python 的例子中,我们确实得到了 p 值 0.15,因此我们得出错误没有序列相关性的结论。
用于检查模型残差中自相关的 Python 代码
我们现在知道 VAR 模型的误差没有序列相关性。因此,我们不需要重新考虑 VAR 的模型顺序,这意味着我们可以测试因果关系。
Python 代码检查格兰杰因果关系
在 VAR(1)中,我们观察到有确凿的证据表明,总体而言,GDP 增长率与消费支出增长率之间存在一定的因果关系。有趣的是,反之亦然(从消费增长到 GDP 增长)并不成立。
我们也可以使用相同的函数和不同的参数在 Python 中执行即时因果关系测试。
对 VAR &因果关系的观察
风险值估计:在估计风险值(1)模型时,所有三种工具的反应相似。我没有发现哪一个能大幅度地战胜另一个。在几乎所有的工具中,选择风险值滞后阶数的基本结构,通过风险值模型估计建议的滞后阶数,从而测试自相关性、因果性和稳定性,似乎是轻量级的。
VAR 模型的输出:后模型估计 Stata 生成的输出或者 R 的汇总结果相对来说比 Python 更详细。例如,Python VAR 输出不提供相应系统方程的模型方程。为了从残差自相关结果、稳定性或因果关系中获得结果,我发现 Python 要求单独输入属性值,这与 Stata 或 r 相比有点麻烦。
这篇文章的目的不是传达数学证明的疏忽,而是更多地关注掌握 ARMA 或 VAR 技术的实践。在有经验的人员的帮助下探索正式的教科书,然后冒险进行实践练习是非常重要的。然而,我的意图是帮助我的读者在他们的计划阶段,我们一直在寻找一个起点,然后迷路。本文有趣的部分是通过不同的工具尝试样本数据,并注意其中的差异或相同之处。有趣的是,在发现这样的问题并决定对问题建模之前,我们的工具箱中应该有不止一个工具。
参考文献
瑞财金融时间序列分析
Shumway&Stoffer时间序列分析及其应用
分享时间序列分析知识的网络和开放视频平台
原始数据来源,经合组织数据库
anind ya s .Chakrabarti教授,经济领域,IIM-艾哈迈达巴德
如果你能看透时间的种子,说哪些谷物会生长,哪些不会,请告诉我。~威廉·莎士比亚
函数参数:默认值、关键字和任意值
函数参数。图片由作者
给 PYTHON 开发者的提示
定义一个接受可变数量参数的函数
假设您正在编写一个接受多个参数的函数,其中一些参数通常有一个公共值。例如,您希望能够在不显式定义每个参数的情况下调用函数。换句话说,您希望一些参数有默认参数。
接下来,将向您介绍如何编写一个带有默认参数和灵活参数的函数,它允许您向函数传递任意数量的参数。
先决条件
如果你不熟悉定义自己的函数,下面的文章会给你更多的信息。
数据科学家需要具有特定功能的函数
towardsdatascience.com](/writing-your-own-functions-40d381bd679) [## 变量范围和 LEGB 规则
变量的作用域指的是你可以看到或访问变量的地方
towardsdatascience.com](/scope-of-variable-and-legb-rule-4d44d4576df5)
具有默认参数值的函数。图片由作者
首先,定义一个带有默认参数值的函数。在函数头中,我们给感兴趣的参数加上一个等号和默认的参数值。请注意,此函数计算第一个参数的第二个参数的幂,默认的第二个参数值是 1。这意味着我们可以像你所期望的那样用两个参数来调用这个函数。但是,如果只使用一个参数,函数调用将使用默认参数 1 作为第二个参数。
通过本文,您将获得编写带有单个和多个默认参数的函数的专业知识。
现在让我们看看灵活的论点。假设您想编写一个函数,但是不确定用户想要传递多少个参数。例如,一个函数接受浮点数或整型数,并将它们相加。这就是所谓的灵活的论点。
带有可变参数(args)的函数。图片作者作者
在这个例子中,我们编写了一个函数,它总结了传递给它的所有参数。在函数定义中,我们使用参数*args
,然后将传递给函数调用的所有参数转换成函数体中的元组。如果我们使用这个方法,*args
调用函数时,参数没有最大值。
带有任意关键字参数(kwargs)的函数。图片作者作者
您还可以使用一个**
来传递任意数量的名为kwargs
的关键字参数。也就是说,前面有标识符的参数。我们将编写一个名为print_all
的函数,打印出标识符和传递给它们的参数。为了编写这样一个函数,我们使用前面带一个**
的参数kwargs
。这将标识符-关键字对转化为函数体内的字典。然后,在函数体中,我们需要打印字典kwargs
中存储的所有键值对。
供大家参考,我们可以组合使用 args 和 kwargs。让我们看看下面的例子。
args 和 kwargs 的组合。图片由作者
使用灵活参数时,名称args
和kwargs
并不重要。你可以定义任何你想要的变量名。但是,它们前面必须分别有一个*
和**
。我知道这很难接受,所以是时候自己解决了。
**Other Interesting Articles**#1 [Writing Your Own Functions](/writing-your-own-functions-40d381bd679)#2 [Scope of Variable and LEGB Rule](/scope-of-variable-and-legb-rule-4d44d4576df5)#3 [Python: Procedural or Object-Oriented Programming?](/python-procedural-or-object-oriented-programming-42c66a008676)#4 [Data Science with Python: How to Use NumPy Library](/data-science-with-python-how-to-use-numpy-library-5885aa83be6b)#5 [Do you have the Software Engineer and Data Scientist skills?](/do-you-have-the-software-engineer-and-data-scientist-skills-probably-not-7e8fb069e067)
关于作者
Wie Kiang 是一名研究人员,负责收集、组织和分析意见和数据,以解决问题、探索问题和预测趋势。
他几乎在机器学习和深度学习的每个领域工作。他正在一系列领域进行实验和研究,包括卷积神经网络、自然语言处理和递归神经网络。
连接上LinkedIn
张量流函数估计
使用优化器来估计函数变量和回归
梯度下降是微积分中的一种数学方法,它考虑空间中变量的导数,以实现值的最佳组合来最小化函数。在机器学习中,我们通常使用定义为 预测-实际 的损失函数来最小化,从而给出预测问题的一组变量。有兴趣可以看这里的。
在本文中,我们将看到如何在 Tensorflow 中使用梯度下降来估计我们在日常研究生活中碰巧看到的函数变量。此外,我将展示一个例子,说明如何使用张量流优化器来寻找函数的最小值及其参数的最小值。
函数估计的示例曲线
让我们考虑以下三种情况。前两个场景用于函数估计,最后一个场景将演示评估函数最小值的能力。我将使用下面的代码来生成数据。
# imports
import tensorflow as tf
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
from mpl_toolkits import mplot3d
np.random.seed(50) # For reproducability
集合 1
x = np.arange(10)
y = 4 * x + 3
noise = np.random.normal(0,1,10)
y = y + noisefig = plt.figure(figsize=(5,5))
ax = sns.lineplot(x, y)
plt.xlabel("X")
plt.ylabel("Y")
带噪声的线性函数
集合 2
x = np.arange(-10, 10)
y = x**3 + x**2 + 5
noise = np.random.normal(0,20,20)
y = y + noisefig = plt.figure(figsize=(5,5))
ax = sns.lineplot(x, y)
plt.xlabel("X")
plt.ylabel("Y")
带噪声的 3 次多项式
第三组
在这种情况下,我们将尝试在函数最小时估计 X 和 Y 的值。
def f(x, y):
return (np.sin(x) * np.cos(y)) ** 2x = np.linspace(0, 3, 100)
y = np.linspace(0, 3, 100)X, Y = np.meshgrid(x, y)
Z = f(X, Y)fig = plt.figure(figsize=(10, 10))
ax = plt.axes(projection='3d')
ax.contour3D(X, Y, Z, 100)
# ax.scatter(0,0,0, color='r', marker='^')
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z');
plt.xlim(-1, 4)
plt.ylim(-1, 5)
ax.set_zlim(-0.5,1)
ax.view_init(25, 20)
三维绘图
使用张量流优化器
根据文档,有几个优化器供我们使用。让我们来看看如何为集合 1 估计变量。
def init():
M = tf.Variable(5.0)
C = tf.Variable(0.0)
return M, CM, C = init()
opt = tf.keras.optimizers.Adam(learning_rate=0.001)for epoch in range(1000):
opt.minimize(lambda: sum(abs(y - M*x - C)), var_list=[M, C])
print(sum((y - M*x - C)**2).numpy(), M.numpy(), C.numpy(), end="\r")
print()print(M.numpy())
print(C.numpy())
在上面的代码中,我将尝试估计函数Y = Mx + C
的M
和C
参数。因此,作为第一步,我创建初始化函数来返回M
和C
作为张量流变量。这意味着优化器将计算 GRAD(多变量梯度),并使用提供的学习率将其应用于张量流变量。
注意损失函数是lambda: sum(abs(y — M*x — C))
。这转化为平均绝对误差,与使用均方误差的回归模型非常相似。经过 1000 次迭代后,对于 M 和 C ,我们得到值 4.4131455 和 0.29607454 。同一平面上的两个图如下所示。
原始和估计
请注意,我们有一个非常类似于原始数据集的估计。接下来,我们将看看如何解决第二个问题。
def init():
A = tf.Variable(1.0)
B = tf.Variable(1.0)
C = tf.Variable(1.0)
D = tf.Variable(1.0)
return A, B, C, DA, B, C, D = init()
opt = tf.keras.optimizers.Adam(learning_rate=0.01)for epoch in range(1000):
opt.minimize(lambda: sum(abs(y - (A*x**3+ B*x**2+C*x+D))), var_list=[A, B, C, D])
print(sum(abs(y - (A*x**3+ B*x**2+C*x+D))).numpy(), A.numpy(), B.numpy(), C.numpy(), D.numpy(), end="\r")
print()print(A.numpy())
print(B.numpy())
print(C.numpy())
print(D.numpy())
我们使用估计量Y = Ax^3 + Bx^2 + Cx + D
作为目标函数。因此,我们将有四个张量变量。与前面的场景类似,我们将使用平均绝对误差进行函数估计。我们分别得到 A,B,C 和 D 的系数 1.0356606,1.1082488,-2.7969947 和 8.258981 。我们的图如下所示。
原始和估计
计算最小值
def init():
X = tf.Variable(1.0)
Y = tf.Variable(1.0)
return X, YX, Y = init()
opt = tf.keras.optimizers.Adam(learning_rate=0.001)for epoch in range(1000):
opt.minimize(lambda: (tf.math.sin(X) * tf.math.cos(Y)) ** 2, var_list=[X, Y])
print(((tf.math.sin(X) * tf.math.cos(Y)) ** 2).numpy(), X.numpy(), Y.numpy(), end="\r")
print()print(X.numpy())
print(Y.numpy())minX = X.numpy()
minY = Y.numpy()
这里我们直接使用优化器的函数值,并要求它最小化。经过 1000 次迭代后,我们分别得到 X 和 Y 的值 0.5938998 和 1.5066066 。再次绘图给了我们下面标有最小值的图。
def f(x, y):
return (np.sin(x) * np.cos(y)) ** 2x = np.linspace(0, 3, 100)
y = np.linspace(0, 3, 100)X, Y = np.meshgrid(x, y)
Z = f(X, Y)fig = plt.figure(figsize=(10, 10))
ax = plt.axes(projection='3d')
ax.contour3D(X, Y, Z, 100)
ax.scatter(minX, minY, f(minX, minY), color='r', marker='o', s=100)
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z');
plt.xlim(-1, 4)
plt.ylim(-1, 5)
ax.set_zlim(-0.5,1)
ax.view_init(25, 150) # Rotate to see minima better
标有最小值的图
注意,这个图的最小值实际上是一个平面。然而,我们只计算点,因此,我们不能指望找到一个平面。一个数学上更合理的方法将为这项任务建立一个更好的近似模型。
笔记
- 可能存在多个最小值,尽管梯度下降法只报告一个最小值/最大值,该值在起始点附近。随机梯度下降试图减轻这种影响。
- 同样可以使用神经网络进行建模。参见文档。
我希望你喜欢阅读这篇关于梯度下降的实际应用的文章。这里,我们使用函数来获取数据进行演示。然而,这同样适用于只存在具有多个参数的数据的情况。
干杯!
Python 中的函数包装器
将包装器放在函数周围
软件编程的一个重要原则是 DRY 原则。DRY 是“不要重复自己”的缩写。DRY 的目标是避免软件编程中不必要的重复。DRY 的应用包括通过函数、类、装饰器、类装饰器和元类实现抽象。在这篇文章中,我们将使用一个函数装饰器来包装和添加额外的处理到现有的用于模型构建的函数中。
我们开始吧!
对于我们的例子,我们将定义一个装饰函数来报告输入函数的执行时间。作为一名数据科学家,我经常需要考虑 fit 的执行时间,并预测生产中的调用。让我们考虑这个用例。
我们将使用来自医疗费用个人数据集的合成医疗数据,可以在这里找到。我们将定义读取数据、拟合数据和进行预测的函数。然后我们将定义一个装饰函数,它将报告每个函数调用的执行时间。
首先,让我们将数据读入熊猫数据框:
import pandas as pd
df = pd.read_csv("insurance.csv")
让我们打印前五行数据:
print(df.head())
现在让我们来构思一下我们的预测问题。让我们使用“年龄”、“bmi”和“儿童”列作为输入特征,使用“费用”作为目标。让我们也拆分我们的数据用于训练和测试。首先,让我们导入一些必要的包:
import numpy as np
from sklearn.model_selection import train_test_split
接下来,让我们定义我们的输入和输出。让我们将数据分成训练集和测试集:
X = np.array(df[['children', 'bmi', 'age' ]])
y = np.array(df['charges'])
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2, random_state = 42)
这里,我们选择一个测试大小,对应于 20%数据的随机样本。现在,让我们将所有这些放入一个函数中:
def read_and_split(self, test_size):
df = pd.read_csv("insurance.csv")
print(df.head())
X = np.array(df[['children', 'bmi', 'age' ]])
y = np.array(df['charges'])
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2, random_state = 42)
return X_train, X_test, y_train, y_test
接下来,让我们定义一个函数“fit_model”,我们将使用它来使我们的模型符合我们的训练数据。让我们导入“线性回归”模块:
from sklearn.linear_models import LinearRegression
在我们的“fit_model”函数中,让我们定义一个“LinearRegression”对象,并根据训练数据拟合我们的模型:
def fit_model():
model = LinearRegression()
model = model.fit(X_train, y_train)
return model
最后,让我们定义一个对测试集进行预测的函数:
def predict(input_value):
result = model.predict(X_test)
return result
现在我们已经定义了函数,让我们定义将报告执行时间的装饰函数。我们的装饰函数将是一个计时器函数,称为“timethis ”,它将接受一个函数作为输入:
def timethis(func):
...
接下来,我们将在“timethis”函数中定义一个“wrapper”函数:
def timethis(func):
def wrapper(*args, **kwargs):
...
在我们的“包装器”函数中,我们将定义“开始”和“结束”变量,我们将使用它们来记录运行的开始和结束。在定义“开始”和“结束”变量之间,我们将调用输入函数,并将其存储在一个名为“结果”的变量中:
def timethis(func):
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
我们需要做的最后一件事是将“@ wraps”装饰符放在“wrapper”函数之前的行中:
def timethis(func):
[@wraps](http://twitter.com/wraps)(func)
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
return result
“@ wraps”装饰器获取传递给“@timethis”的函数,并复制函数名、文档字符串、参数列表等…
然后,我们将打印函数的名称和运行时间(' end' — 'start ')。我们还返回输入函数,它存储在“结果”变量中:
def timethis(func):
[@wraps](http://twitter.com/wraps)(func)
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(func.__name__, end-start)
return result
最后,“timethis”函数返回“包装器”:
def timethis(func):
[@wraps](http://twitter.com/wraps)(func)
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(func.__name__, end-start)
return result
return wrapper
现在我们可以在任何函数上使用“@timethis”装饰器。让我们将“@timethis”应用于我们的“read_split”方法。我们只需在我们想要包装的函数前面的行中放上“@timethis ”:
@timethis
def read_and_split(self, test_size):
df = pd.read_csv("insurance.csv")
X = np.array(df[['children', 'bmi', 'age' ]])
y = np.array(df['charges'])
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2, random_state = 42)
return X_train, X_test, y_train, y_test
现在,如果我们调用我们的“read_split”方法,我们的装饰器“@timethis”应该打印执行时间:
X_train, X_test, y_train, y_test = read_and_split(0.2)
让我们对我们的 fit 方法做同样的事情:
[@timethis](http://twitter.com/timethis)
def fit_model():
model = LinearRegression()
model = model.fit(X_train, y_train)
return modelmodel = fit_model()
对于我们预测方法:
[@timethis](http://twitter.com/timethis)
def predict():
result = model.predict(X_test)
return resultprediction = predict()
我将在这里停下来,但是您可以自己随意摆弄代码和数据。我鼓励您分析一些其他回归模型的执行时间,您可以使用这些数据构建这些模型,如随机森林或支持向量回归。
结论
总之,在这篇文章中,我们讨论了 python 中的函数包装器。首先,我们定义了三个函数来构建线性回归模型。我们定义了一些函数,用于读取和拆分我们的数据以进行训练,使我们的模型适合训练数据,并对我们的测试集进行预测。然后我们定义了一个函数包装器,允许我们报告每个函数调用的执行时间。我希望你觉得这篇文章有用/有趣。这篇文章中的代码可以在 GitHub 上找到。感谢您的阅读!
数据科学项目中的函数式编程
至少是一个我如何使用它和我学到了什么的小故事
我想分享一下我日常使用函数式编程(FP)的经验。我在这里的主要目的是挑战这种方法,并从数据科学界获得反馈。作为一名数据科学家,我大部分时间都花在 POCs(概念验证)和代码产品化之间。这种反复会带来很多麻烦,尤其是在 POC 阶段没有满足生产代码的约束时。当然,我的大部分代码并没有在生产中结束。如果我必须在调查期间编写一个生产就绪的代码,这将是一个巨大的开销。对我来说,有三种方法:
- 编写大块的代码。这很酷,因为你可以很容易地测试一个假设。我通常用这种方法来测试一个想法,我希望最多在一两天内得到答案。大多数时候,当我得到答案时,我会扔掉(99%)这段代码。如果我不得不在这段代码上工作更长的时间,复杂性将会每天增加,并且这段代码在进入生产阶段时会变成一场噩梦。我也称之为“笔记本方法”。
- 编写 OOP 代码。我喜欢 OOP 代码有很多原因。首先,当设计简单明了时,它易于使用。一个很好的例子是 Scikit-Learn 库。如果你理解了
fit
—predict
的概念,你基本上就理解了这个库的 90%。还有许多其他的库,其中的基本概念简单而有效。然而,当我开始一个新项目时,我不知道包装我的代码的最佳“概念”是什么。从一个概念开始,并在测试假设的同时维护它,这可能是对时间的极大浪费。 - 写入 FP 代码。我不是 FP 大师,我每天都在学习更多的东西。对我来说,FP 的主要优势是代码组织和可维护性。此外,当您需要加速代码时,并行化几乎是免费的。FP 在开始的时候可能有点难学,但是我认为这是更糟糕的投资。
无论如何,这三种方法各有利弊,根据你所从事的团队/项目的类型,任何一种都可能更适合你。我选择用第三种方法开始大部分时间,主要是因为开销非常低,并且回报是为项目的未来阶段赢得惊人的时间。正如我所说的,我不是一个 FP 大师,如果你认为我错了或者你觉得你可以用更好的方式来做,请添加评论和反馈。
数据科学家(至少我)花了很多时间来创建管道。换句话说,我们创建代码从数据到预测。管道中的步骤顺序可以在项目之间改变,但我或多或少总是做解析、清理、格式化、特征工程、培训等工作。在 POC 阶段,这些步骤中的每一步都可以分解为更小的步骤(格式化一些列、填充缺失值等),我们还希望有一个适当的测试步骤来评估管道。POC 的目标基本上是找到管道中最合适的步骤。在将 FP 用于我的研究并将代码转移到产品中之后,我学到了几个关键点,使得这个过渡更加平稳。
定义:什么是 FP?
函数式编程最简单的例子就是使用一个标准函数。
第 8 行,我使用函数map
将函数add_2
应用于列表中的所有值。这里没有什么新的东西,我们可以很容易地用 lambda 函数替换add_2
函数:lambda x: x+2
。
在这个例子中,有一个硬编码的值+2
要添加。那么,如果我想做同样的操作,但是要添加不同的值,会怎么样呢?然后,理论上,我需要创建一个不同的函数,比如说add_3()
。或者使用 lambda 风格:lambda x: x+3
。您还可以创建一个函数,该函数通过适当的操作返回一个函数:
在本例中,函数add(to_add)
返回一个函数,该函数可以使用map
以与上例相同的方式应用于列表。主要区别是值to_add
神奇地保存在创建的函数my_custom_add_function
中。这个概念对于分离参数和数据非常有用。在这个例子中,函数的参数是3
,数据对应于列表的值。
好的,但是 FP 和管道之间有什么联系呢?
让我们回到数据科学。在下面的例子中,我有一个数据集,我想在其中应用一些修改。
- 删除缺少太多值的列。
- 从 1 小时间隔到两个 2 小时间隔对数据进行重采样。
- 填充缺失值。
没有什么疯狂的复杂,只是时间序列的标准预处理步骤。您可以简单地编写如下代码:
如果你看一下preprocess
函数,你会发现我提到的 3 个步骤。这太棒了!您可以继续扩展preprocess
功能或使用您的特征工程添加新功能等
现在,让我们考虑使用函数式编程风格编写的相同代码:
没什么区别,对吧?只是多了几行代码…但是,您可以注意到,直到第 34 行才开始计算。还有一点,在这段代码中,你可以清楚地看到参数和数据的区别。参数被提供给create_...
函数,而数据稍后提供。第一个例子叫做命令式,第二个例子叫做声明式(以防万一你想谷歌一下)。
因此,现在考虑您想要更新您的代码,因为您发现 2 小时重采样不合适,并且您想要保留原始采样。轻松点。在命令式代码(example_pandas.py)中,您只需删除第 22 行并更改preprocess
签名。嗯,这可能是一件痛苦的事…尤其是如果preprocess
函数被许多其他代码共享的话。在声明性代码(example_pandas_FP.py)中,您可以删除第 32 行,将第 34 行改为fill_na(remover(random_df))
,这只会影响这段代码。直到这里,没有太多的差异。
现在,考虑这个函数create_pipeline
:
这个函数以一个函数列表作为参数,并返回一个函数,这个函数逐个调用每个函数。
现在,example_pandas_FP.py 变成了:
更干净的代码不是吗?步骤和参数被清楚地突出显示。您可以通过修改传递给create_pipeline
的函数列表来简单地添加或删除步骤。
当您要为每个单独的功能创建单元测试时,这种范例也会有所帮助。额外收获:如果你习惯了 FP 风格,你将会有一个清晰的模块化,可以跨项目重用。
Scikit-learn 还提供了一个管道对象。我经常使用它,我发现它很棒。我不再使用它的主要原因是它与fit
- transform
范式联系太紧密了。很多时候,我最终只是在fit
函数中传递数据,我发现这既没用又不方便。我发现我的实现更加灵活,但是,您仍然可以将这两种方法结合起来。
序列化
一般来说,管道和 FP 的一个很酷的技巧是序列化函数的能力。这是缓存中间计算的好方法,也是在生产中重用代码的简单方法。总的来说,我更喜欢用dill
而不是pickle
,因为它消除了很多麻烦,而且几乎总是对我有效。例如:
Spark 的(几乎)自由并行化
现在,如果我想将相同的管道应用于 1000 个数据集呢?一个快速的解决方案是并行执行。因为管线的参数化在创建管线时一次性完成,所以不需要传递参数。你只需要传递数据。如果不是 Spark 专家,您可以简单地使用 RDD 的map
— collect
功能。想法是使用 RDD 并行化我们的 1000 个数据集,并使用这些函数来获得结果。
这并不难:d .这对你来说非常有效。然而,在内存中加载 1000 个数据集可能不是一个好主意。为了避免这种情况,我在管道中加载数据集。所以sc.parallelize
不是用在数据集列表上,而是用在获取数据的键列表上。例如,CSV 文件名列表。然后,管道的第一个函数可以解释这些文件名,并在需要时加载数据。在这个例子中,为了简单起见,我展示了数据集的并行化,但是同样的概念也可以用于按列或按行并行化数据集。
函数估计量
你们中的一些人可能会想,“这里有一个明显的限制!如果管道不是线性的呢?”换句话说,如果您需要从一个数据集推断出一些结果,并将其应用到另一个数据集,该怎么办?例如,标准化或训练模型。我刚刚描述的范式打破了…事实上,并不完全是。这就是你想要使用函数估值器的地方。这是我从我在威盛科技的前任主管杰里米·泰勒那里学到的。这个想法是链函数创建。 F(参数)——>G(训练 _ 集)——>H(预测)。在 python 中:
现在,我们有 3 条管道可以协同工作。pipeline_preprocessor
是预处理数据的函数,pipeline_rf_creator
是创建随机森林预测器的管道,pipeline_create_prediction
是推断外部数据集预测的管道。最后一个管道可以序列化,并在以后用于推理。
业务逻辑= >组合
总的来说,一件棘手的事情是理清业务逻辑和计算。经验法则是避免在封装函数中使用if parameter == something
。需要的地方还是可以有if data == something
的。为了处理业务逻辑,您可以利用组合。当你使用create_pipeline
功能时就变得简单了。例如:
在本例中,管道的组成取决于use_sampling
参数。你可以想象各种各样的检查来定义管道。然而,从测试的角度来看,这个函数是最难测试的,因为您需要尝试所有的参数组合,以确保它不会在特定的用例中出错。这就是为什么我尽量保持简单的原因。
产品化
当我对我的调查感到满意时,我可以将我的代码转移到生产中。为此,我简单地删除了我创建的所有未使用的函数,并保留了相关的测试。过去,这一阶段需要我花费数周的时间,而现在,我可以在一两天内完成。没有债务要付,我的代码已经组织得很清楚了。事实上,我将代码集中于产生预期的结果,并且通过避免复杂的设计节省了大量时间。这种 FP 范式在我所有的代码中都是一样的,我的同事可以很容易地理解它并修改它。
无国籍的
转移到生产时,重要的一点是尽可能无状态。这确保了幂等性,这在云上部署时非常重要。我发现 FP 范型非常适合创建无状态代码,而且过了一段时间后,维护和调试变得容易多了。
我希望你喜欢阅读这篇文章。如果您有任何反馈或意见,请随时联系我或对本文发表评论。
我的 LinkedIn: 这里
Python 函数,解释过。
下面是理解 Python 函数的一种更直观的方式。
由布鲁纳托里诺通过坎瓦
什么是功能?
我们首先在高中数学中学习函数。数学中函数的一般概念是有一个输入(姑且称之为 x)和一个输出(姑且称之为 y)。我们表示这样一个数学函数:
y = f(x)
其中 f(x)可以是对我们称之为 x 的输入的任何变换(如加、减、乘、指数运算…),例如:
y = 2x + 1
其中 f 代表我们将输出的等式,乘以 2 再加 1,最重要的是,我们希望将它返回为 y。
为什么我要走这么远?编程语言总是使用基本的数学概念来构建它们的结构和逻辑。如果你理解了数学中的函数,你就已经理解了 Python(以及其他编程语言)中的函数。
我们如何在 Python 中表示函数?
让我们用上面的数学解释来帮助我们理解如何写函数。试着把编码想象成与计算机的对话,把 Python 想象成一种逐步表达命令的方式。
首先,我们需要定义函数的名称和输入 x(就像我们的数学例子一样)。我们从 define 中写出 def ,函数名(你可以把它想成数学中的 f),输入变量 x。我们称 x 为函数的参数。
def my_function_name(x):
在数学中,只写 f(x)会转换 x,但不会把 x 的值赋给任何我们以后可以引用的变量。所以我们需要添加一个返回语句到函数的末尾:
def my_function_name(x):
return y
我们正在使用 x 并返回 y,但是这两者之间有什么联系呢?我们如何计算 y?
如果你认为编码是一步一步地解释你的命令,那么在定义函数之后,在返回 y 值之前进行函数计算是有意义的。
def my_function_name(x):
y = 2*x + 1
return y
完事了吗?还没有!
定义函数和直接编写函数的区别在于:
y = 2*x + 1
print(y)
如果我们想在以后使用函数,我们可以用不同的 x 值保存它们。如果我们想在脚本中使用函数,我们需要调用它:
variable1 = my_function_name(4)
注意到这里有什么不同吗?当我们调用函数时,我们用 4 代替 x。实际上,此时我们希望 x 等于 4。我们称 4 为函数的自变量。函数的美妙之处在于我们从来不需要全局定义 x 是什么:当我们调用函数时,x 可以取我们写在括号中的任何值。
变量 1 呢?用 x 解释的同样的概念在这里也适用:在这一次我们调用函数,我们希望变量 1 是 y 。这意味着无论函数中的 y 是(2*4 + 1 = 9),我们都希望将该值赋给变量 1。
您可以继续在脚本的其余部分使用 variable1,就像您编写了以下代码一样:
variable1 = 9
酷,我还能用函数做什么?
回到数学,假设你有一个有两个输入的函数:
y = f(x,y)
你可以用 Python 函数做同样的事情,就像数学一样:
def my_function_name(x,y):
y = x + y
return y
调用函数时,需要与定义函数时最初使用的输入数量保持一致:
variable2 = my_function_name(1,2)
你也可以在定义你的函数时,通过写*arg 作为参数,不指定参数的数量。这就是它与数学有一点不同的地方:你可以在函数中调用任意多的参数。这里,我们创建一个列表,并将函数参数中的所有数字追加到列表中。
def my_function_name(*arg):
list_of_numbers = []
list_of_numbers.append(arg)
return list_of_numbers
例如,如果我们这样调用函数:
my_function_name(1,2,3,4,5,6,7,8,9)
您可以期待这样的输出:
Out: [1,2,3,4,5,6,7,8,9]
示例:矩阵计算器
为了说明我们如何在更高级的主题中使用函数,让我们看一个如何构建矩阵计算器的例子,它将返回 2x2 矩阵的行列式。
这里,我将函数名定义为 matrix_calculator ,并传递 4 个输入(也称为参数):x1、x2、y1 和 y2,它们用于创建名为 my_matrix 的矩阵。接下来,我计算行列式并返回它。
如果我用参数 1,2,3 和 4 调用函数:
matrix_calculator(1,2,3,4)
我将得到以下输出:
-2
让我们把计算器变得更先进。如果函数能够理解你是否想计算 2x2 或 3x3 矩阵的行列式,取决于你调用函数时使用的参数数量,那会怎样?
我们将通过 numpy 函数 np.linalg.det(A) 来计算行列式,因此我们将从导入 numpy 包开始。接下来,我将定义名为smart _ matrix _ calculator的函数,并通过使用*arg 作为参数向该函数传递尽可能多的参数。然后,我将把数字转换成一个列表,这样我们就可以对列表进行切片并创建矩阵。
如果我们传递的数字的长度是 9 ( if len(arg) == 9) 这意味着我们想要一个有 9 个元素的 3x3 矩阵。
我们通过以下方式对列表进行切片,将其转换为一个矩阵:
- 前三个数字分别是 x1、y1 和 z1
- 接下来的三个数字将是 x2、y2 和 z2
- 最后三个数字将是 x3、y3 和 z3
我们把它转换成同一行的数组,计算行列式,最后在函数结束时返回。
如果我们使用数字(1,2,3,4,1,2,2,1,9)调用函数来生成一个 3x3 矩阵:
smart_matrix_calculator(1,2,3,4,1,2,2,1,9)
我们可以期待它的回报:
Out: -51.0
如果我们使用数字(1,2,3,4)调用函数来生成一个 2x2 矩阵:
smart_matrix_calculator(1,2,3,4)
我们可以期待它的回报:
-2.0
感谢您的阅读。我希望我对什么是函数以及它们是如何工作的理解能够帮助你更好地理解这个基本的编程技巧。
如果你想要更多关于其他 Python 主题的教程,请在评论中告诉我!
Power BI 中的函数
这是什么?我们什么时候使用它?我们如何制造一个?
埃里克·克鲁尔在 Unsplash 上的照片
清洗,扯皮,总结。我不知道你怎么想,但是我喜欢这个数据分析的过程。Power Query 是我在这方面使用的主要工具。以后我会写更多关于权力查询的内容。
我清理是因为数据不干净。我打扫卫生是因为我需要好的数据来分析。我努力为连接找到正确的“形状”。如果您必须手动清理数据,那就太可怕了,而且很容易出现清理错误。想象错误之上的错误?你真正要分析的是什么?
别担心。功能——就像上面那些可爱的小机器人可以来救援一样。
作者图片
究竟什么是函数?谷歌快速搜索给出了这个定义。
“功能是一组有组织的、可重复使用的代码,用于执行单个相关的动作。…不同的编程语言对它们的命名不同,例如,函数、方法、子例程、过程等。”
这里的关键词是可重用。要成为一个功能,它必须是可重用的。这意味着您可以使用这段代码,并将其应用于不止一个文件,还可以应用于其他文件。我认为函数就是接受输入,执行一组定制的过程,然后给出输出。
你今天用的很多东西,比如,BI 中的 sum 函数到 joins 都是函数。
例如,看看 M(超级查询语言)中的这个函数
图片来自作者
问题是…有时我们没有可以满足我们需求的功能。我们希望对各种文件应用一组自定义的过程。这就是我们必须使用它的原因和时间。
让我们在 Power Query 中过一遍如何在 Power BI 中创建函数。生活中有那么多清洁和争吵,我相信这对我们会很有用。
正如简笔画所提到的,这些年来我们有几个不同的文件。我们需要对所有文件执行相同的操作。
作者图片
我们不想对每个文件都执行这个过程,但是我们希望函数为我们执行这个过程。
首先,让我们将所有文件放入一个文件夹中。
作者图片
加载完所有文件后,您应该会看到它们如下所示。
让我们从创建主文件的“副本”开始——我们想从这个副本构建一个函数。
作者图片
现在有了一个副本,我们只保留第一行来构建函数。
作者图片
Power BI 只保留第一排。很好。
作者图片
这里有一个巧妙而重要的技巧——您可以键入引用该列的[Content]和引用实际“单元格”本身的{0}。如果你有更多的技术理解,我引用列并返回一个列表,然后引用列表值 0 返回二进制文件。
如果你不喜欢打字,你也可以点击二进制文件本身的“向下钻取”。
作者图片
Power BI 干净利落地加载文件,不需要那些自动转换。
作者图片
现在,让我们使用分组方式进行快速总结。您可以在面板顶部的“变换”功能区下找到“分组依据”。
作者图片
这是我们对其中一个文件的结果。
作者图片
这正是我们所需要的。
嗯…简笔画。
作者图片
还没做,让我们把它做成一个实际的函数。
你可以通过转换功能区下的高级编辑器选项来实现。
Power BI 把所有的步骤都记录下来,变成 M 脚本给我们看。如果你熟悉 Excel,它就像一个录制的宏。我们只需要更改引用,以便它可以应用于其他文件。
你不需要知道如何把下面的 M 代码全部打出来。Power BI 的妙处在于,它为你记录了所有这些步骤。在不久的将来,我会写更多关于如何阅读 M 脚本的内容。
这是当前的脚本。
let
Source = Folder.Files("MY_DRIVE"),
#"Removed Top Rows" = Table.Skip(Source,1),
#"Removed Other Columns" = Table.SelectColumns(#"Removed Top Rows",{"Name", "Content"}),
#"Kept First Rows" = Table.FirstN(#"Removed Other Columns",1)[Content]{0},
#"Imported CSV" = Csv.Document(#"Kept First Rows",[Delimiter=",", Columns=5, Encoding=65001, QuoteStyle=QuoteStyle.None]),
#"Promoted Headers" = Table.PromoteHeaders(#"Imported CSV", [PromoteAllScalars=true]),
#"Changed Type" = Table.TransformColumnTypes(#"Promoted Headers",{{"Year", Int64.Type}, {"Sex", type text}, {"Name", type text}, {"Job Title", type text}, {"Salary", Int64.Type}}),
#"Grouped Rows" = Table.Group(#"Changed Type", {"Year", "Sex"}, {{"Average Salary", each List.Average([Salary]), type nullable number}}),
#"Rounded Off" = Table.TransformColumns(#"Grouped Rows",{{"Average Salary", each Number.Round(_, 0), type number}})
in
#"Rounded Off"
不要让这个压倒你,我们唯一关心的是 Csv。文档部分。
让我们删除 Csv 上面的所有内容。文档部分并声明我们的变量。我选择(X 为二进制)是因为我们希望这个函数有一个二进制输入。
还记得文件夹里的那些 csv 文件吗?它们是二进制文件。
作者图片
这是我们的功能!
作者图片
现在让我们将它应用到我们的文件中。让我们“调用”我们的自定义函数。您可以在“添加列”功能区下找到“调用”。
听起来像是《哈利·波特》里的情节,对吗?
作者图片
在这里!
作者图片
让我们点击“扩展”进入决赛桌。
作者图片
哒哒!
按年平均工资分列的男性和女性。
现在,这是一个简单的数据集,但你也可以将连接和其他更复杂的转换构建到一个函数中,并将其应用于所有文件。
TL:DR ?我为你做了这个。
作者图片
希望你喜欢这篇文章。
如果你想看视频——Curbal 是一个真正教会我很多关于功能的频道,当然还有 Power BI。
注意安全,希望这能帮助您的数据之旅!
幂 BI 中的函数—(表。FindText)
…使用表格。FindText 可在文件中的任意位置搜索任何内容。
Rabie Madaci 在 Unsplash 上拍摄的照片
我一直对简单的查找功能感到惊讶。您可以找到任何符合条件的内容,即使您的搜索项目隐藏在不同的工作表、打开的文本或表格中。
想象一下,你可以搜索原始数据中出现的任何内容。即使它隐藏在打开的文本字段中的某个地方。相当神奇!
这有什么帮助?如果你正在寻找一个关键词或一组重要的数字,但你不知道去哪里找,这将非常有帮助。该号码可以在开放文本字段中,也可以在名称字段中,等等。
一旦找到,你就可以把这些搜索结果结合起来,用它讲一个故事。有时,做一个发现只是出于好奇,但有时你可能会对你的发现感到惊讶。
我用过这张桌子。FindText 功能是一个欺诈检测练习,它非常方便,因为我的表很大,而我只是在寻找一个人。这个简单的函数还有其他用处。您可以使用它作为帐户记录的历史搜索,并回答您可能有的一些问题。
我们开始吧!
我们来看看表。微软的 FindText 函数。
作者图片
简单地说,这个函数使用一个表作为输入,并在该表中搜索一个条目。然后,它返回一个表,其中包含与您的条件匹配的行。
让我们试试这个。
拿桌子
第一步很棘手——也许你很幸运,已经为你设置了一列表格,但是我必须将文件夹中的二进制文件转换成表格格式。
这并不困难,我使用这个功能。
(FOLDER_PATH , FILENAME ) =>
let
Source = Csv.Document(File.Contents(FOLDER_PATH&FILENAME)),
#"Promoted Headers" = Table.PromoteHeaders(Source, [PromoteAllScalars=true])
in
#"Promoted Headers"
如果您使用 folders 选项连接 Power BI 并有一个二进制文件列表,您可以使用上述函数将这些二进制文件转换成表格。
要添加新功能,只需进入电力查询 > 新建空白查询 > 高级编辑 > 粘贴到脚本
这个查询中的 FOLDER_PATH 和 FILENAME 参数是 Power Query 在您连接到它时为您准备的列。
这里有一个例子。
作者图片
现在,我将使用调用函数选项来应用该函数。如果你对如何使用函数感到困惑,这里有一篇文章给你,帮助你入门。功能很棒!
在我应用这个函数之后,我得到了这个输出。
作者图片
太好了!现在我有一列表来应用我的表。FindText()。
应用函数
我将删除不需要的列,只反映表格列。
我们现在需要做的就是通过创建一个新列来应用我们的函数。我想看看“Apple”这个词是否出现在我的表中。
作者图片
现在,当你向下钻取时,你可以看到“苹果”出现在“口味”列下的表格中。
作者图片
如果我将它移动到另一个选项,您可以看到“Apple”也出现在“Appleton”下的“Stand”列中。
作者图片
现在,为了分析您的结果,您可以将所有文件组合起来,这些文件将包含只包含单词“Apple”的行。
是的,我的例子很简单,我只有 10 个表,但是这样想,如果你能够理解如何构建一个简单的原型,你可以在你的日常工作中使用任何数量的表:)
带走:
- 桌子。FindText()是一个简洁的函数。发挥你的想象力,你可以在任何列和表格中找到你的搜索选项。
2.删除一些开始时不需要的列,这样运行起来会快很多!
3.棘手的部分不是使用搜索功能,而是将二进制文件转换成表格。
你可能会发现还有其他有用的功能。
如果你已经知道你要找的关键词或数字,并且你知道要找哪一栏,这些功能将会很有用。他们是的表。包含(),表。ContainsAll() 表。ContainsAny() 。
我将来也会写一篇关于这些的文章。
注意安全,感谢您的阅读!
创建和使用 R 函数
关于如何使用它们的基础知识
你经历过下面这种情况吗?
作者图片
我似乎总是碰到它。也许只是我的运气…
我喜欢函数。我喜欢建造它们,和它们一起工作,创造它们。没有什么比有人说一项任务需要一周时间更好的了,但是实际上,你知道它可能在 10 分钟内就能完成。
功能很棒。就像小机器人一样,它们可以把你从错误和重复的工作中拯救出来。如果你还没有看过我写的关于在 Power BI 中使用函数的文章,在这里。它将为你提供使用函数的基本知识,如果你使用平面文件,它很可能会节省你大量的时间。
我们用的很多东西已经是函数了。我们只是不去想它。
以 R 中的均值函数为例,它先取函数名,然后取输入。所以 mean()是一个函数。与 Power BI 非常相似,AVERAGE([列名])是一个函数。
在这篇文章中,我希望我能向你展示如何用 r 中的函数做一些工作。
让我们开始吧!
作者图片
我主要处理大量平面文件(如果有一天我可以连接到数据仓库,那就太好了,但那是另一回事了)。
我想在 r 中对一组平面文件执行一组转换,我们该怎么做呢?
不难。这非常类似于我们如何创建 Power BI 函数。关键是我们需要对一个文件执行一组转换,并将其设置为构建我们功能的原型。
首先,让我们使用 setwd 设置我们的工作目录并加载我们的包。我们将使用 tidyverse 和 rlist 。
需要设置工作目录,因为 R 需要知道它正在工作的文件夹。r 需要知道将文件导出到哪里,以及从哪里读取文件。我们将把我们的工作目录分配给一个变量调用 File_Directory
library(tidyverse) #loading packages
library(rlist)File_Directory = setwd("Your folder containing your files")
#setting up the work directory
现在,让我们使用 rlist 的 package list.filter 函数和 str_detect 来过滤。csv”平面文件。我们将把它赋给变量 All_Files 。
library(tidyverse) #loading packages
library(rlist)File_Directory = setwd("Your folder containing your files")
#setting up the work directory All_Files = File_Directory %>%
list.files() %>%
list.filter(str_detect(., ".csv")) //just want the csv files and the "." here is a place holder for the output in the list.files.
list.files 返回 File_Directory 变量中所有文件的名称。然后 list.filter 用 str_detect 对 csv 文件进行过滤。此外,如果您不熟悉% > %,它被称为“管道”,它将结果从一行传输到另一行。
All_Files 的结果是我们想要清除的文件名的列表。
这是关键——我们需要手动加载其中一个文件,并应用我们的转换步骤。
我想按年份和性别分组,按平均工资汇总。这些是我想应用于所有文件的转换步骤。
Dataset_2007 %>% # I manually loaded this file from Import Dataset on R Studio
group_by(Year,Sex) %>%
summarize(Avg_Sal = mean(Salary, na.rm = T))
让我们检查它是否工作。您必须选择脚本并按下 cntrl+R 来运行脚本。
作者图片
它工作了。
既然原型起作用了,那就把它变成函数吧。
My_Function = function(X) {
read_csv(X) %>%
group_by(Year, Sex) %>%
summarize(Avg_Sal = mean(Salary))
}
这里我们添加了 read_csv ,因为我们希望我们的函数读取目录中的文件并应用转换。
这样做比把每个文件都加载到 R Studio 中更有效率。我们可以把这个函数叫做 My_Function 。我对函数名没什么想象力。
在这里,该函数采用“X”作为我们的输入,使用 read_csv 读取文件,然后将表传递到下一行,即 group_by、,然后根据平均工资汇总。
这里它读取 X,这是我们所有文件中的第一个文件,并执行集合转换,等等。
现在让我们使用这个函数并应用到 All_Files 变量中的所有文件。记住变量 All_Files 在一个列表中保存我们的文件名。
在 R 中,有一个简洁的小东西叫做 map ,基本上, map 接受你的函数并把它映射到你所有的输入,你也可以选择返回一个数据框、一个列表或其他东西中的所有结果。使用 map 超出了本文的范围,但是让我们在这里使用 map_df 。 map_df 代表地图数据帧。
map_df(All_Files, My_Function)
以下是数据框中的结果。
作者图片
现在您可以选择使用 r 中的 write.csv 调用将结果导出到 csv 格式的同事。
map_df(All_Files, My_Function) %>%
write.csv("My_Results.csv", row.names = F)
这是所有的一切。
library(tidyverse) #loading packages
library(rlist)File_Directory = setwd("Your folder containing your files")
#setting up the work directoryAll_Files = File_Directory %>%
list.files() %>%
list.filter(str_detect(., ".csv")) #filtering for csvDataset_2007 %>% # function prototype
group_by(Year,Sex) %>%
summarize(Avg_Sal = mean(Salary, na.rm = T))My_Function = function(X) {
read_csv(X) %>%
group_by(Year, Sex) %>%
summarize(Avg_Sal = mean(Salary))
} #creating the functionmap_df(All_Files, My_Function) %>%
write.csv("My_Results.csv", row.names = F) #mapping it exporting to a csv file call My_Results
这只是在 r 中使用函数的一个非常简单的例子。
老实说,生活中的事情从来不会这样发展。总会在某个地方打嗝。您今天可能会尝试这样做,并意识到您的数据集都在不同的列中,您必须首先清理它。也许一个包不会为你加载。可能是电脑的问题。
总会有问题需要解决。
我希望你保持鼓励,保持安全,并在你的旅程中继续前进!
强化学习的基本迭代方法
学习价值和策略迭代能掌握多少强化学习?很多。
本文着重于理解基本的 MDP(在这里简单回顾一下),并将其应用于与基本强化学习方法的关系。我将重点介绍的方法是值迭代和策略迭代。这两种方法支撑了 Q 值迭代,直接导致 Q 学习。
来源——加州大学伯克利分校 CS188。
可以在这之前,或者之后看的我的一些相关文章(有意独立):
- 什么是马尔可夫决策过程?
- 强化学习的隐藏线性代数。
Q-Learning 开启了我们正在进行的深度强化学习浪潮,因此它是强化学习学生手册中的一个重要部分。
回顾马尔可夫决策过程
马尔可夫决策过程是支持强化学习的随机模型。如果你熟悉,你可以跳过这一部分,但是我增加了解释为什么每个元素在强化学习环境中都很重要。
定义(对 RL 有影响)
- 一组状态 s ∈ S ,动作 a ∈ A 。状态和动作是代理所有可能的位置和动作的集合。 在高级强化学习 中,状态和动作变得连续,这就需要我们重新思考算法。
- 一个转移函数 T(s,a,s’)。给定当前位置和提供的动作,*决定下一个状态跟随的频率。 在强化学习 中,我们不再能够访问这个函数,所以这些方法试图近似它或者在采样数据上学习隐含。*
- 一个奖励函数 R(s,a,s’)。这个函数表示每一步获得多少奖励。 在强化学习 中,我们不再能访问这个函数,所以我们从采样值 r 中学习,引导算法探索环境,然后利用最优轨迹。
- [0,1]中的折扣因子 γ (gamma) ,将当前(下一步)的价值调整为未来奖励。 在强化学习 中,我们不再能够访问这个函数,γ(gamma)**通过类似贝尔曼的更新来控制大多数所有学习算法和规划优化器的收敛。**
- 起始状态 s0 ,也可能是终止状态。
MDP 就是一个例子。来源——我在 CS188 做的一个讲座。
重要的价值观
MDP 有两个重要的特征效用-状态值和机会节点的 q 值。任何 MDP 或 RL 值中的 ***** 表示一个 最优量 。
- 一个状态的价值:一个状态的价值是从一个状态出发的奖励的最优递归和。
- 一个状态、行动对的 Q 值:Q 值是与一个状态-行动对相关的折扣奖励的最优和。
最佳值与最佳动作条件 q 值相关。然后值和 q 值更新规则非常相似(加权转换、奖励和折扣因子)。顶部)值与 q 值的耦合;mid) Q 值递归,bot)值迭代。加州大学伯克利分校的 cs188。
来源—作者。
走向强化学习
价值迭代
了解所有状态的值,然后我们可以根据梯度采取行动。值迭代直接从贝尔曼更新中学习状态的值。在一些非限制性条件下,贝尔曼更新保证收敛到最优值。
学习一个政策可能比学习一个价值观更直接。学习一个值可能需要无限长的时间才能收敛到 64 位浮点数的数值精度(想想每次迭代中常数的移动平均,从估计值 0 开始,它将永远添加越来越小的非零值)。
策略迭代
学习与价值观一致的政策。策略学习递增地查看当前值并提取策略。因为动作空间是有限的,所以希望能比值迭代收敛得更快。从概念上讲,动作的最后一次改变将在小的滚动平均更新结束之前发生。策略迭代有两个步骤。
第一个称为策略提取,这是如何从一个值到一个策略——通过采取最大化超过期望值的策略。
策略提取步骤。
第二步是政策评估。策略评估采用一个策略,并根据策略运行价值迭代。样本永远与策略联系在一起,但是我们知道我们必须运行迭代算法来减少提取相关 动作 信息的步骤。**
政策评估步骤。
像值迭代一样,由于底层的贝尔曼更新,策略迭代保证收敛到最合理的 MDP。
q 值迭代
知道最优值的问题是很难从中提炼出政策。argmax
操作符明显是非线性的,难以优化,因此 Q 值迭代向直接策略提取迈进了一步。每个状态下的最优策略就是该状态下的最大 q 值。
MDP 的 q 学习。
大多数指令以值迭代开始的原因是,它更自然地嵌入到贝尔曼更新中。 Q 值迭代需要将两个关键 MDP 值关系替换在一起。这样做了之后,离 Q-learning 就差一步了,这个我们会了解的。
这些迭代算法是怎么回事?
让我们确保你理解所有的条款。本质上,每次更新由求和后的两项组成(可能还有一个选择动作的max
项)。让我们把括号去掉,并讨论它们与 MDP 的关系。
状态空间奖励映射。
第一项是对乘积 T(s,a,s’)R(s,a,s’)的求和。该术语代表给定状态和转换的潜在价值和可能性。 T 术语或转换,支配从转换中获得给定奖励的可能性(回忆,atuple,a,s’确定一个元组,其中一个动作将一个代理从状态带到状态s’【T42 这将做一些事情,比如用高回报来衡量低概率状态,用低回报来衡量频繁状态。****
来自数据的递归更新。****
下一项决定了这些算法的、。它是迭代算法最后一步数据的一个加权— V ,上面有项。这从邻近的州提取了关于价值的信息,这样我们就可以理解长期的转变。将这一项视为大多数递归更新发生的地方,第一项是由环境决定的加权先验。
收敛条件
所有的迭代算法都被告知“在某些条件下收敛到最优值或策略。”你问的那些条件是什么?
- ****总状态空间覆盖率。条件是在有条件的策略下达到所有状态、动作、next_state 元组。否则,来自 MDP 的一些信息将会丢失,值可能会停留在初始值。
- ****贴现因子γ < 1。这是因为任何可以重复的循环的值都可以并且将趋向于无穷大。
谢天谢地,在实践中,这些条件很容易满足。大多数探索都有一个ε贪婪性,包括随机行动的机会,总是(因此任何行动都是可行的),非 1 折扣因子导致更有利的性能。最终,这些算法可以在很多环境下工作,所以它们绝对值得一试。
强化学习
我们如何把我们所看到的变成一个强化学习的问题?我们需要使用样本,而不是真正的 T(s,a,s ')和 R(s,a,s ')函数。
基于样本的学习——如何解决隐藏的 MDP
MDPs 中的迭代方法与解决强化学习问题的基本方法之间的唯一区别是 RL 从 MDP 的底层转移和奖励函数中采样,而不是将其包含在更新规则中。我们需要更新两个东西,一个是对 T(s,a,s') 的替换,另一个是对 R(s,a,s') 的替换。
首先,让我们将转换函数近似为每个观察到的元组的平均动作条件转换。所有我们没见过的值都用随机值初始化。这是基于模型的强化学习(我的研究领域)最简单的形式。****
转移函数的近似。基于高级模型的强化学习研究的基础。
现在,剩下的就是记住如何处理奖励,对吗?但是,我们实际上对每一步都有奖励,所以我们可以侥幸逃脱(方法通过许多样本平均出正确的值)。考虑用一个采样奖励来近似 Q 值迭代方程,如下所示。
基于样本的 Q 学习(实际 RL)。
上式为 Q-learning 。我们从一些填充了随机值的向量 Q(s,a)开始,然后我们收集与世界的交互并调整 alpha。α是一个学习率,所以当我们认为我们的算法收敛时,我们会降低它。
结果表明,Q 学习的收敛与 Q 值迭代非常相似,但我们只是在用不完整的世界观运行算法。
机器人和游戏中使用的 Q 学习是在更复杂的特征空间中,用神经网络逼近所有状态-动作对的大表。关于深度 Q-Learning 如何震惊世界的总结,这里有一个很棒的视频,直到我写下自己的相关文章!
这些是如何融合的?
有什么简单的收敛界限吗?
towardsdatascience.com](/convergence-of-reinforcement-learning-algorithms-3d917f66b3b7)
更多?订阅我关于机器人、人工智能和社会的时事通讯!
一个关于机器人和人工智能的博客,让它们对每个人都有益,以及即将到来的自动化浪潮…
robotic.substack.com](https://robotic.substack.com/)**
基础营销分析:RFM 分析
威廉·艾文在 Unsplash 上的照片
通过最近频率货币模型和用 Python 实现的管理细分机器学习模型
如今,要成为一名成功的营销专业人士,营销分析是一项必备技能。因此,具有经济学和统计学背景的营销专家通常被认为比其他人更有优势。但是不管你的背景如何,仍然有一个简单的分析技术你可以利用——RFM(最近频率货币)模型。
RFM 分析是一种营销分析工具,用来确定你的客户是最好的。RFM 模型基于 3 个定量因素:首先是最近度(R ),它显示了客户购买的时间;然后是频率(F ),它告诉客户多久购买一次;最后,货币(M),它显示了客户花了多少钱。
在这篇短文中,我们将通过 Python 环境探索零售数据集,向您展示这种营销分析技巧。(将使用一个非常简单的 SQL 语句来实现这个目标)
作为一个读者,没有什么比弄脏自己的手更有收获的了,走吧!
数据
- 分析中使用的数据集可从这里下载。
- 我们没有使用所有的列。我们将主要关注客户 id、订单日期和销售额。
- 销售额:每笔订单的总金额。
RFM 分析和分割
- 像往常一样,首先要做的是导入必要的库。
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from pandasql import sqldf
from sklearn.preprocessing import scale
from scipy.cluster.hierarchy import linkage, dendrogram, cut_tree
- 让我们导入数据并检查它的前 5 行。
df = pd.read_csv('data.csv')
df.head()
检查数据的前 5 行
- 将 order_date 列转换为日期格式。
- 然后我们需要找出数据集的持续时间。
df["order_date"] = df["order_date"].astype('datetime64[ns]')
print(df["order_date"].max(),"\n",df["order_date"].min())
- 创建一个新列,并将其命名为 days_since。这是为了显示最后一次购买的日期。
df['days_since'] = (pd.Timestamp('2020-01-01') - df['order_date']).dt.days
个人认为这是文章最有趣的部分。我们将只使用一行 sql 代码来计算最近、频率和平均购买量。
- “GROUP BY 1”表示结果集的第一列。这与我们的 python 语言完全不同,如您所见,别名为 1 的列是不存在的。
- 然后我们检查新的数据集“rfm”。
rfm = sqldf("SELECT customer_id, MIN(days_since) AS 'recency', COUNT(*) AS 'frequency', AVG(sales) AS 'amount' FROM df GROUP BY 1", globals())rfm.describe()
数据帧“rfm”
- 现在将数据可视化。
rfm.recency.hist(bins=20)
崭新
customers.frequency.hist(bins=6)
频率
customers.amount.hist()
数量
观察:
- 大多数顾客是最近买的。
- 大多数顾客一年中只来购物一次,很少光顾超过两次,这意味着在顾客满意度方面需要做很多工作。
- 平均订单值位于图表(金额)的左侧,这是合理的。
- 现在,我们将客户数据复制到一个新的数据框中。
- 并将 customer_id 设置为索引
new_data = rfm
new_data.head()
new_data = new_data.set_index(new_data.customer_id).iloc[:,1:4]
new_data.head()
- 然后我们对数量和图形进行对数变换。
new_data.amount = np.log(new_data.amount)new_data.amount.hist(bins=7)
对数转换后的数量
- 标准化变量。
new_data = pd.DataFrame(scale(new_data), index=new_data.index, columns=new_data.columns)
new_data.head()
新数据数据框架
运行分层分段
- 取标准化数据的 10%样本。
new_data_sample = new_data.iloc[::10, :]
- 对距离度量执行分层聚类。
c = linkage(new_data_sample, method='ward')
- 将分段设置为 3,
- 并查看频率表。
members = pd.DataFrame(cut_tree(c, n_clusters = 3), index=new_data_sample.index, columns=['ClusterNumber'])members.ClusterNumber.value_counts(sort=False)
- 展示前 10 名顾客。
members.iloc[0:10]
客户表
结论:
是不是很简单,简单的脚本,简单的工作流程!
有了这个客户表(上表),营销经理就能够针对每个客户的细分市场和相关的营销活动。
例如,客户群 2 是那些具有较高购买频率和中等消费金额的客户群。营销人员可以通过积极的优惠券营销或频繁的激励电子邮件来瞄准这一客户群。你也可以用各种各样的参数,如折扣价值、持续时间、打折产品等,开展小规模的活动,来测试这群客户。
而那些在第一部分的人,他们是那些很少花钱的休眠者。要恢复它们并不容易,因此暂时跳过它们可能是个不错的选择。
最后,分析的 Jupyter 笔记本可以在 Github 上找到。
编码快乐!
要查看我的其他中国传统营销博客,这里有链接,
概率推理的基本问题
肖恩·辛克莱在 Unsplash 上拍摄的照片
如果你是机器学习从业者,为什么要关心采样?
到目前为止,我知道很多人从事 ML 研究或者研究机器学习算法。然而,他们中的大多数人不知何故没有意识到机器学习所基于的基本问题,即概率推理的问题。这篇文章的重点可能是将你的注意力转向你在编写机器学习算法时可能没有考虑到的问题。
为什么我们首先要讨论概率?随机性从何而来?真的有随机变量这种东西吗?最后,我们希望预测一些相对具体的东西,给定图像的类别标签,给定马尔可夫决策过程中某种状态描述的最佳行动。可以说,这些事情没有什么随机性。从随机的意义上来说,一个对象实际上并没有被赋予一个类标签。一头牛可能不是一头牛,它肯定是一头牛。
另一方面,我们有一些问题,比如不同风格的无监督学习,我们可能希望降低数据的维度,对数据进行聚类,学习反映数据概率分布的生成模型。所有这些味道都可以用概率来表示。但是,将一个潜在的低维表示分配给一个数据点并不是真正随机的。我们希望将输入数据点直接映射到潜在表示(一个聚类,一个低维的潜在变量)。
那么,我们最后为什么要用随机性和概率呢?主要论点是不存在随机性。当我们谈论机器学习世界中的概率分布、密度时,我们实际上谈论的是什么。我们实际上谈论的是信息或不确定性。概率度量反映了我们对给定事件有多少信息。让我们看一个监督的例子,一个包含一头牛的图像。当我说,图像包含一头牛的概率是 0.9。这并不意味着奶牛有时就在那里,这意味着我 90%确定这张照片里有一头奶牛。也许是因为图像没有足够的信息来完全确定它包含一头奶牛,或者也许是我的模型是错误的…谁知道呢。
随机性从何而来?笑点在于没有随机性。让我们讨论一下先验分布。这意味着我有一个模型参数的先验分布,或者说假设空间?这意味着我比其他人更确定特定的配置,特定的假设。这并不意味着模型是随机的,它可以根据潜在的随机过程而变化。
当我们进行机器学习时,我们的目标主要是(以不同的形式)以下,在最一般的意义上来说。给定某种类型的数据,我们希望推断出最能描述数据的模型的一些参数。如果你想成为贝叶斯,那么你将坚持这些不确定性的概念。下面是贝叶斯法则给定数据 D 来推断模型参数 θ 。
p(θ) 我们称之为先验, p(D|θ) 似然,LHS(左手边)就是后验。后验仅仅表达了我们对参数的确定程度。分母中是边际超过的 D 。请注意,我们不能神奇地访问这个边际,我们需要以某种方式评估它。为了得到分母的积分公式,我们可以从 θ 和 D 的联合分布中忽略θ。在这种情况下,我们有下面的贝叶斯规则公式:
等效地,我们可以在积分中分离分母中的项,使得它包含 θ 上的似然和先验。
为什么这是一个难题?ML 从业者说评估这个积分是困难的,但是理解它为什么困难有时可能是棘手的。首先,分母中的 θ 和分子中的不是同一个。积分基本上意味着我们对所有可能的参数 θ 的联合分布求和。现在,假设这个 θ 是神经网络的参数,它们是实数。分母中有无限多的参数配置需要评估。这显然很难处理。即使我们认为实数仅限于 32 位精度,可能的配置数量也是用于存储模型的位数的指数。此外,评估积分的过程涉及到采样,显然,我们的采样越好,我们就能更好地预期我们的估计。
当你继续阅读时,记住以下几点是很有用的:
在估计和采样的情况下,计算复杂度是重要的。
然而,情况并非没有希望。虽然有些积分不能求值,但是我们还是可以估计的。估计一个积分有不同的方法,其中一种是(最基本的一种)蒙特卡罗抽样(注意名字的抽样部分)。所以我们能做的就是画出一定数量的参数 θ 并在积分内对函数求值(注意我们假设我们有访问函数的权限对其求值),看起来是这样的:
这是一个相对简单的估计量,它是无偏的,这意味着它是正确的,并在无限数量的样本下产生精确的正确结果。然而,它也有一个缺点。
可以证明,对于蒙特卡罗估计量,估计误差随着样本数的平方根而渐近下降。
这意味着,如果我们想要对目标价值有一个大概的估计,这可能已经足够好了。但是如果我们想要一个高精度的估计,也许存在更好的具有更快收敛特性的选择。此外,我们可能不喜欢经常从参数分布中取样,因为取样可能很昂贵。
从分布中取样的计算考虑
到目前为止,我们已经讨论了计算后验概率的贝叶斯规则中分母的计算问题。在蒙特卡罗估计中,我们需要抽取θ的样本来估计积分。但是在 θ 上的分布到底是什么样的呢?你有没有想过采样实际上是如何工作的,当使用 Python 或 R 库时,我们如何得出一个“随机”数?即使我给你一个能够从均匀分布中采样的采样器,你如何使用它从更复杂的分布中采样呢?事实证明,采样问题本身就是一个迷人的问题,无论是计算上还是数学上。
取样时,我们想问自己的主要问题如下。如果我们有可能从一个简单的概率分布中有效地抽样,我们如何从一个更复杂的概率分布中抽样呢?事实证明,从复杂分布中采样是一个重要的问题,并且构成了 ML 中许多方法的基础。举几个例子:离线强化学习、规范化流程、变分推理……但是这个讨论我留到以后的文章中。
相关文章
信息论中的“信息”是什么 —了解更多不确定性和信息概念的关系。
基础统计学
探索时间序列建模
用 Python 代码进行时间序列建模
时间序列数据在我们身边无处不在,从股票市场价格到你所在城市的每日气温。顾名思义,时间序列数据的 x 轴就是时间。我们总是站在当前的时间点。在 x 轴的左侧,我们正在回顾过去。如果幸运的话,简单的一瞥,也许能发现过去的一些周期性模式。或者,如果我们付出更多的努力,并引入一些其他变量,我们可能会得到一些“相关性”来解释我们过去的数据。但是,我们能评估这些其他变量对数据建模的效果吗?这些“相关性”在未来会成立吗?我们能指望这些过去的“相关性”并做出预测吗?
如果你也有这些问题,让我们一起探索时间序列数据的分析和建模!我将使用 Python 进行一些实验和统计测试。使用 Python 学习统计学的一个好处是,我们可以使用已建立的库并绘制漂亮的图表来更好地理解复杂的统计概念。
未来,我还计划拥有以下职位:
用 Python 代码进行时间序列建模:如何分析单个时间序列变量 。
用 Python 代码进行时间序列建模:如何分析多个时间序列变量 。
用 Python 代码进行时间序列建模:如何用线性回归进行时间序列数据建模 。
由于我将从非常基本的统计概念开始,到更复杂的分析和建模,如果你是时间序列数据的初学者,我强烈建议你从这篇文章开始。
1.总体、样本和估计量
人口有一个潜在的分布过程,我们通常无法准确知道。我们能做的是从总体中抽样,并用样本来估计总体。但是如何选择合适的估计量呢?定义一个好的估计量有三个特性:无偏、一致和有效。
当样本参数的期望值等于总体参数时,估计量是无偏的:
如果样本参数的方差随着样本量的增加而减小,则估计量是一致的。
在样本容量相同的情况下,方差越小的估计量越有效。
一个好的评估者是无偏的、一致的和有效的。
2.概率密度分布
概率密度分布(PDF)用于指定随机变量落在特定值范围内的概率。某一 x 处的概率密度表示为 f(x)。通过在(x1,x2)的范围内对 f(x)应用积分函数,可以计算 x 落入(x1,x2)的概率。
正态分布的概率密度函数和概率密度函数
3.中心极限定理和大数定律
中心极限定理指出,当样本容量较大时,独立随机变量的样本均值服从正态分布。通常,当样本量大于 30 时,大样本量的要求被认为是满足的。独立随机变量可以服从任何分布,而这些独立随机变量的样本均值服从正态分布。
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import normmeanList = []num_trials = 10000
num_observations = 1000for i in range(num_trials):
# sample from uniform distribution
numList = np.random.randint(1,7,num_observations)
# sample from normal distribution
#numList = np.random.normal(loc=0,scale=1,size=num_observations)
# sample from poisson distribution
#numList = np.random.poisson(lam=1,size=num_observations)
meanList.append(np.mean(numList))mu, std = norm.fit(meanList)
fig, ax = plt.subplots()
ax.hist(meanList, bins=20, density=True, alpha=1, color='#4495c9')xmin, xmax = ax.get_xlim()
x = np.linspace(xmin, xmax, 100)
p = norm.pdf(x, mu, std)
ax.plot(x, p, 'k', linewidth=4)ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.spines['left'].set_visible(False)
ax.set_yticks([])
plt.show()
大数定律表明,给定大量试验,估计值的平均值会更接近理论值。对于上面的实验,如果我们只重复试验 10 次,分布将与图非常不同。如果您感兴趣,您可以快速测试结果,直观地了解大数定律是如何产生影响的。
4.假设检验
假设只能计算样本参数,我们需要使用假设检验对总体参数进行推断。在假设检验中,提出一组互补的假设,包括一个零假设和一个备择假设。当进行假设检验时,我们选择相信零假设成立。如果观测值很可能出现在原假设为真的条件下,那么我们不拒绝原假设。然而,如果观察值不可能出现,那么我们拒绝零假设,接受替代假设。
假设矩阵
5.显著性水平和 P 值
在进行假设检验之前,我们需要首先定义一个显著性水平。显著性水平决定了我们对零假设的置信水平。如果我们将显著性水平设置为 0.05,那么只要观察值的概率高于 5%,我们就不拒绝零假设。然而,如果观察的概率低于 5%,我们拒绝零假设,接受替代假设。在类型 I 和类型 II 误差之间有一个折衷。基本上,较高的显著性水平更容易拒绝零假设。虽然以这种方式,较高的显著性水平减少了类型 II 误差,但同时也导致了较高的类型 I 误差。减少 I 型和 II 型误差的唯一方法是增加样本量。
观察值的概率称为 p 值。较低的 p 值意味着在零假设成立的情况下,观察不太可能发生。当 p 值低于显著性水平时,我们拒绝零假设。但是,有一点需要注意,p 值应该解释为二进制:它只是大于或小于显著性水平。
如何解释显著性水平和 p 值
摘要
在这篇文章中,我强调了一些基本的统计概念,这些概念对于理解未来关于时间序列数据分析和建模的文章非常重要。希望您有信心在我们的期刊上更进一步,探索时间序列数据!敬请关注下面这篇关于如何分析单个时间序列变量的文章!
数据架构基础,帮助数据科学家更好地理解架构图
在假装你理解你聪明的同事给你看的图表之前。
贾里德·穆雷在 Unsplash 上的照片
介绍
在一家使用数据获取商业价值的公司中,尽管您可能并不总是被赏识您的数据科学技能,但当您管理好数据基础架构时,您总是会被赏识的。每个人都希望数据存储在一个可访问的位置,清理好,并定期更新。
在这些不引人注目但稳定的需求的支持下,数据架构师的工资与数据科学家一样高,甚至更高。事实上,根据 PayScale(https://www . PayScale . com/research/US/Country = United _ United States/Salary)进行的薪酬研究显示,美国数据架构师的平均薪酬为$ 121816,而数据科学家的平均薪酬为$ 96089。
并不是说所有的数据科学家都应该改变他们的工作,至少学习数据架构的基础知识对我们来说会有很多好处。实际上,有一个简单(但有意义)的框架可以帮助您理解任何类型的真实世界的数据架构。
目录
- 数据架构的三个组成部分:数据湖- >数据仓库- >数据集市
- 每个组件中使用的工具
- 案例研究—构建预定的&从 BigQuery(数据仓库)到 Google Sheets(数据集市)的自动数据馈送
- 结尾注释
数据架构的三个组成部分:数据湖->数据仓库->数据集市
“数据湖”、“数据仓库”和“数据集市”是数据平台架构中的典型组件。按照这种顺序,业务中产生的数据被处理和设置,以创建另一个数据含义。
作者使用 Irasuto-ya(https://www.irasutoya.com/)的材料制作的图表
三个组件负责三种不同的功能:
- 数据湖:保存业务中产生的数据的原始副本。来自原始数据的数据处理即使有也应该是最小的;否则,万一某些数据处理最终被证明是错误的,将不可能追溯性地修复错误。
- 数据仓库:保存由托管数据模型处理和结构化的数据,反映数据最终用途的全局(非特定)方向。在许多情况下,数据是表格形式的。
- 数据集市:保存特定业务功能使用的子部分和/或聚合数据集,例如特定业务单位或特定地理区域。一个典型的例子是当我们准备一个特定业务线的 KPI 摘要,然后在 BI 工具中可视化。特别是,当用户希望定期和频繁地更新数据集市时,在仓库之后准备这种单独和独立的组件是值得的。相反,在用户只想对某组数据进行一次特别分析的情况下,可以跳过这一部分。
三个数据架构组件的摘要(由作者创建的图表)
除了这种简单的描述之外,更多真实世界的例子,请享受谷歌“数据架构”来找到大量的数据架构图。
你所看到的,当你用“数据架构”谷歌一下。(图片由作者拍摄)
为什么我们需要分成这三个部分?
因为流程中的不同阶段有不同的要求。
在数据湖阶段,我们希望数据接近原始数据,而数据仓库旨在保持数据集更加结构化,通过清晰的维护计划进行管理,并具有清晰的所有权。在数据仓库中,我们也希望数据库类型是面向分析的,而不是面向事务的。另一方面,数据集市应该能够方便地接触到非技术人员,他们可能会使用数据旅程的最终输出。
不同用途的系统组件往往在不同的时间进行重新设计。然后,配置松散连接的组件在将来的维护和扩展中具有优势。
数据工程师和数据科学家如何处理这三个部分?
粗略地说,数据工程师涵盖了从业务中产生的数据抽取到数据仓库中的数据湖和数据建模以及建立 ETL 管道;而数据科学家则负责从数据仓库中提取数据,构建数据集市,并引导进一步的业务应用和价值创造。
当然,数据工程师和数据科学家之间的这种角色分配有些理想,许多公司不会为了符合这一定义而同时聘用两者。实际上,他们的工作描述有重叠的趋势。
超越三组分方法的新趋势
最后但并非最不重要的一点是,值得注意的是,这种由三部分组成的方法是二十多年来一直存在的传统方法,而且新技术一直在不断出现。例如, " 数据虚拟化%20of) " 是一种允许对数据源进行一站式数据管理和操作接口的思想,而不管它们的格式和物理位置。
每个组件中使用的工具
现在,我们了解了三个数据平台组件的概念。那么,人们用什么工具呢?基于这份“数据平台指南”(日语),以下是一些想法:
数据湖/仓库
数据湖和数据仓库有以下选项。
作者根据“数据平台指南”(日文)精心制作
ETL 工具
ETL 发生在数据到达数据湖并被处理以适应数据仓库的地方。数据实时到达,因此 ETL 更喜欢事件驱动的消息传递工具。
作者根据“数据平台指南”(日文)精心制作
工作流引擎
工作流引擎用于管理数据的整体流水线操作,例如,通过流程图可视化流程进行的位置,在出错时触发自动重试等。
作者根据“数据平台指南”(日文)精心制作
数据集市/商务智能工具
以下工具可以用作数据集市和/或 BI 解决方案。选择将取决于业务环境、贵公司熟悉的工具(例如,您是 Tableau 人还是 Power BI 人?),聚合数据的大小(例如,如果数据大小很小,为什么像 Excel 或 Google Sheets 这样的基本解决方案达不到目标?)、您使用什么数据仓库解决方案(例如,如果您的数据仓库在 BigQuery 上,Google DataStudio 可能是一个简单的解决方案,因为它在 Google circle 内有天然的链接)等等。
作者根据“数据平台指南”(日文)精心制作
案例研究—构建从 BigQuery(数据仓库)到 Google Sheets(数据集市)的预定和自动数据馈送
当数据大小保持在几十兆字节左右或不到几十兆字节,并且不依赖于其他大型数据集时,坚持使用基于电子表格的工具来存储、处理和可视化数据是很好的,因为它成本较低,并且每个人都可以使用它。
一旦数据变得更大,并开始与其他数据表有数据依赖性,那么从云存储开始作为一站式数据仓库是有益的。(当数据变得更大,达到数十 TB 时,使用本地解决方案来实现成本效益和可管理性是有意义的。)
在这一章中,我将演示一个案例,当数据作为数据仓库存储在 Google BigQuery 中时。 BigQuery 数据实时或短频率处理存储。最终用户仍然希望在电子表格上看到高度汇总的每日 KPI。这意味着数据集市可以很小,甚至适合电子表格解决方案。让我们在这里使用 Google Sheets 而不是 Excel,因为它可以与 BigQuery 中的数据源在同一个环境中。哦,顺便说一下,不要想着每天手动运行查询。尝试找到一种解决方案,让一切自动运行,无需你采取任何行动。
案例研究中的数据管道(作者使用 IRA suto-ya(https://www.irasutoya.com/)的材料制作的图表)
本案例研究中使用的数据
在本案例研究中,我将使用一个样本表数据,其中包含每次乘坐纽约出租车的乘客记录,包括以下数据字段:
- 汽车 ID
- 驱动程序 ID
- 乘车日期
- 乘客人数
- 票价金额
- 等等。
示例数据作为数据仓库存储在 BigQuery 中。
Google Sheets 可以从 BigQuery 表中提取数据吗?
从技术上来说是的,但目前这只能通过连接的表获得,并且您需要一个 G Suite Enterprise、Enterprise for Education 或 G Suite Enterprise Essentials 的帐户。
作者创建的图表。
Connected Sheets 允许用户操纵 BigQuery 表数据,就像在电子表格上播放一样。参见“BenCollins”博客文章本页的 GIF 演示。
使用 Google Sheets 通过 Connected Sheets 连接到 BigQuery 的示例(由作者捕获)
连接的工作表还允许自动调度和刷新工作表,这是作为数据集市的自然需求。
虽然这是一个很好的选择,但一个可能的问题是,欠 G Suite 帐户并不常见。
关于设置的更多细节,见这篇来自“Ben Collins”的博文。
我们可以做些什么来将数据从 BigQuery 推送到 Google Sheets?
要从 BigQuery 中提取数据并将其推送到 Google Sheets,单靠 BigQuery 是不够的,我们需要服务器功能的帮助来调用 API 向 BigQuery 发送查询,接收数据,并将其传递给 Google Sheets。
作者创建的图表。
服务器功能可以在 GCP 外部或内部的服务器机器上(例如 GCP 的“计算引擎”实例;或 AWS 上的“EC2”实例)。可以使用 unix-cron 作业来调度代码运行。但是这里的一个缺点是它需要实例的维护工作和成本,并且对于运行一个小程序来说太多了。
“谷歌云功能”是一种所谓的“无服务器”解决方案,在不启动服务器的情况下运行代码。将代码放入云函数并设置触发事件(例如,本案例研究中的预定时间,但也可以是来自一些互联网用户的 HTML 请求),GCP 会自动管理代码的运行。
我案例研究中的设置
使用纽约出租车数据配置我的案例研究有两个步骤。
第一步:设置调度——设置云调度器和发布/订阅来触发云功能。
这里,“发布/订阅”是一个消息服务,由云功能订阅,并在每天的某个时间触发其运行。“云调度程序”是基于 unix-cron 格式以用户定义的频率启动的功能。结合这两者,我们可以创建由云函数订阅的常规消息。参见这份关于如何做的官方说明。这是我在 GCP 拍摄的截图。
在云调度程序中设置(由作者捕获)
在发布/订阅中设置(由作者捕获)
第二步:设置代码——在云函数上准备代码,查询 BigQuery 表,推送给 Google Sheets。
下一步是设置云功能。在云函数中,您定义 1)什么是触发器(在这个案例研究中,从 Pub/Sub 发送的“cron-topic ”,链接到云调度程序,它在每天早上 6 点提取触发器)和 2)当检测到触发器时您想要运行的代码。
更多细节见这个官方说明,这里是我设置的截图。
在云函数中设置(由作者捕获)
云函数中的代码输入——在这里,您还可以设置 requirements.txt 来使用 mail.py 程序中的可安装库。(作者捕捉)
要运行的代码必须包含在一个函数中,这个函数的名称可以是任何您喜欢的名称(在我的例子中是“nytaxi_pubsub”)。)代码内容由两部分组成:第 1 部分在 BigQuery 上运行查询,以将原始 BigQuery 表简化为 KPI,并将其保存为 BigQuery 中的另一个数据表,以及使其成为 Pandas 数据框,第 2 部分将数据框推送到工作表。
以下是我实际使用的代码。重要的是,对 BigQuery 的认证是自动的,只要它与云函数驻留在同一个 GCP 项目中(参见本页了解解释)。)然而,Google Sheets 的情况并非如此,它至少需要一个通过服务帐户共享目标工作表的过程。更多细节参见gspread 库中的描述。
main.py(由作者编码)
requirements.txt(由作者编码)
Google Sheets 上的最终数据集市
最后,我在 Google Sheets 中得到这样的汇总数据:
经过漫长的设置过程后,自动更新数据集市。(作者捕捉)
该表每天早上自动更新,当数据仓库通过 ETL 从数据湖接收新数据时,我们可以轻松地跟踪纽约出租车 KPI。
结尾注释
在雇用数据工程师和/或数据架构师以及数据科学家的大公司中,数据科学家的主要角色不一定是准备数据基础设施并将其放置到位,但至少了解数据架构的要点将有助于了解我们在日常工作中所处的位置。
数据湖->数据仓库->数据集市是一个典型的平台框架,用于处理从起点到用例的数据。将过程分成三个系统组件对于维护和目的性有很多好处。
在工具的选择上有很多选项。应根据数据环境(大小、类型等)明智地选择它们。)和企业的目标。
最后,在这篇文章中,我讨论了一个案例研究,我们在 Google Sheets 上准备了一个小型数据集市,从 BigQuery 中提取数据作为数据仓库。通过使用云调度程序和发布/订阅,更新变得自动化。
参考
- “数据湖 vs 数据仓库 vs 数据集市”, Jatin Raisinghani ,整体博客(https://www . holistics . io/Blog/Data-Lake-vs-Data-Warehouse-vs-Data-Mart/)
- 一张幻灯片《数据平台指南》(日文),@yuzutas0(推特),https://speakerdeck.com/yuzutas0/20200715
- “连接的工作表:分析谷歌工作表中的大数据”,BenCollins,【https://www.benlcollins.com/spreadsheets/connected-sheets/
生成对抗网络的基础
入门
GANs——图解、解释和编码
由 GAN 生成的合成手写数字。在本教程中,我们将创建自己的 GAN,它可以像这样生成数字,以及创建上面这个动画的代码。请继续阅读!
介绍
2014 年,一位名不见经传的博士生 Ian Goodfellow 向世界介绍了生成性对抗网络(GANs)。GANs 不同于 AI 社区见过的任何东西,Yann LeCun 将其描述为“在 ML 的过去 10 年中最有趣的想法”。
从那时起,许多研究工作都倾注在 GANs 上,许多最先进的人工智能应用程序,如英伟达的超现实人脸生成器都源自 Goodfellow 对 GANs 的研究。
作者注:本文所有图片和动画均由作者创作。如果你想把这些图片用于教育目的,请在评论中给我留言。谢谢大家!
什么是甘,他们能做什么?
在高层次上,GANs 是一种神经网络,它学习如何生成真实的数据样本,并根据这些样本对其进行训练。例如,给定手写数字的照片,GANs 学习如何生成更多手写数字的逼真照片。更令人印象深刻的是,GANs 甚至可以学习生成人类的逼真照片,如下图所示。
GAN 生成的人脸。以上这些面孔都不是真实的。来源:https://thispersondoesnotexist.com/
那么 GANs 是如何工作的呢?从根本上说,GANs 学习兴趣主题的分布。比如说。受过手写数字训练的 GANs 学习数据的分布。一旦学习了数据的分布,GAN 可以简单地从分布中采样以产生真实的图像。
数据的分布
为了巩固我们对数据分布的理解,让我们考虑下面的例子。假设我们有下面的 6 张图片。
每个图像都是一个浅灰色的盒子,为了简单起见,让我们假设每个图像只包含一个像素。换句话说,每个图像中只有一个灰色像素。
现在,假设每个像素都有一个介于-1 和 1 之间的可能值,其中白色像素的值为-1,黑色像素的值为 1。因此,6 幅灰度图像将具有以下像素值:
关于像素值的分布,我们知道些什么?嗯,通过检查,我们知道大多数像素值都在 0 左右,只有少数值接近极值(-1 和 1)。因此,我们可以假设分布是高斯分布,平均值为 0。
注意:对于更多的样本,通过计算平均值和标准偏差来导出该数据的高斯分布是很简单的。然而,这不是我们的重点,因为计算复杂主题的数据分布是很困难的,不像这个简单的例子。
我们像素的基本分布是平均值为 0 的高斯分布
这种数据分布是有用的,因为它允许我们生成更多的灰色图像,就像上面的 6。为了生成更多相似的图像,我们可以从分布中随机取样。
从高斯分布中随机独立抽取 10 个像素。请注意,大多数像素值都接近平均值(0),只有极少数异常值(-1 和 1)。
虽然计算出灰色像素的基本分布可能是微不足道的,但计算猫、狗、汽车或任何其他复杂对象的分布通常是数学上难以处理的。
那么,我们如何学习复杂对象的底层分布呢?显而易见的答案是使用神经网络。给定足够的数据,我们可以训练神经网络来学习任何复杂的功能,例如数据的基本分布。
生成器——分布式学习模型
在 GAN 中,生成器是学习数据底层分布的神经网络。更具体地说,生成器将随机分布(在 GANs 文献中也称为“噪声”)作为输入,并学习将输入映射到期望输出的映射函数,期望输出是数据的实际底层分布。
但是,请注意,上面的体系结构中缺少一个关键组件。我们应该用什么损失函数来训练发电机?我们如何知道生成的图像实际上是否类似于实际的手写数字?一如既往,答案是“使用神经网络”。第二个网络被称为鉴别器。
鉴别器——生成器的对手
鉴别器的作用是判断和评估发生器输出图像的质量。从技术上讲,鉴别器是一个二元分类器。它接受图像作为输入,并输出图像是真实的(即实际的训练图像)还是虚假的(即来自生成器)的概率。
最初,生成器很难产生看起来真实的图像,鉴别器可以轻松区分真假图像,而不会犯太多错误。由于鉴别器是二进制分类器,我们可以使用二进制交叉熵(BCE)损失来量化鉴别器的性能。
鉴频器的 BCE 损耗是发生器的一个重要信号。回想一下,生成器本身并不知道生成的图像是否与真实图像相似。然而,发生器可以使用鉴别器的 BCE 损失作为信号来获得对其生成的图像的反馈。
它是这样工作的。我们将生成器输出的图像发送到鉴别器,它预测图像是真实的概率。最初,当生成器很差时,鉴别器可以很容易地将图像分类为假的,从而导致 BCE 损失很低。然而,生成器最终会改进,鉴别器开始犯更多的错误,将假图像误分类为真实图像,这导致了更高的 BCE 丢失。因此,鉴频器的 BCE 损耗表示发生器输出的图像质量,并且发生器寻求最大化该损耗。
鉴别器的 BCE 损失是发生器输出图像质量的指标
从上面的动画中我们可以看到,鉴频器的 BCE 损耗与发生器产生的图像质量相关。
发生器使用鉴频器的损耗作为其生成图像质量的指标。生成器的目标是调整其权重,使得来自鉴别器的 BCE 损失最大化,有效地“愚弄”鉴别器。
训练鉴别器
但是鉴别器呢?到目前为止,我们从一开始就假设我们有一个完美工作的鉴别器。然而,这个假设是不正确的,鉴别器也需要训练。
由于鉴别器是一个二元分类器,它的训练过程很简单。我们将向鉴别器提供一批标记的真实和虚假图像,并且我们将使用 BCE 损失来调整鉴别器的权重。我们训练鉴别器来识别真假图像,防止鉴别器被生成器“愚弄”。
GANs——两个网络的故事
现在让我们把所有的东西放在一起,看看 GANs 是如何工作的。
基本 GAN 的体系结构
到目前为止,您已经知道 GANs 由两个相互连接的网络组成,即生成器和鉴别器。在传统的 GANs 中,发生器和鉴别器是简单的前馈神经网络。
甘斯的独特之处在于,生成器和鉴别器轮流接受训练,彼此对立。
为了训练生成器,我们使用从随机分布中采样的噪声向量作为输入。在实践中,我们使用从高斯分布中抽取的长度为 100 的向量作为噪声向量。输入通过前馈神经网络中一系列完全连接的层。生成器的输出是一个图像,在我们的 MNIST 例子中,是一个28x28
数组。发生器将其输出传递给鉴频器,并使用鉴频器的 BCE 损耗来调整其权重,目的是最大化鉴频器的损耗。
为了训练鉴别器,我们使用来自生成器的标记图像以及实际图像作为输入。鉴别器学习将图像分类为真或假,并且使用 BCE 损失函数来训练。
在实践中,我们依次训练生成器和鉴别器。这种训练方案类似于两个玩家的 minimax 对抗游戏,因为生成器的目标是最大化鉴别器的损失,而鉴别器的目标是最小化它自己的损失。
创造我们自己的 GAN
现在我们已经理解了 GAN 背后的理论,让我们通过使用 PyTorch 从头开始创建我们自己的 GAN 来将其付诸实践!
首先,让我们引入 MNIST 数据集。torchvision
库让我们可以轻松获得 MNIST 数据集。在将28x28
MNIST 图像展平为784
张量之前,我们将对图像进行一些标准归一化。这种扁平化是必需的,因为网络中的层是完全连接的层。
接下来,让我们编写生成器类的代码。从我们前面看到的,生成器只是一个前馈神经网络,它接受一个100
长度张量并输出一个784
张量。在生成器中,密集层的大小通常会在每层之后翻倍(256、512、1024)。
那很容易,不是吗?现在,让我们为 discriminator 类编写代码。鉴别器也是一个前馈神经网络,它接受一个784
长度张量,并输出一个1
大小的张量,表示输入属于类别 1(真实图像)的概率。与生成器不同,我们在每个层(1024、512、256)之后将密集层的大小减半。
现在,我们将创建一个包含生成器和鉴别器类的 GAN 类。根据我们之前讨论的训练方案,这个 GAN 类将包含依次训练生成器和鉴别器的代码。为了简化我们的代码并减少样板代码,我们将使用 PyTorch Lightning 来实现这一点。
上面的代码是注释过的,根据我们到目前为止所讨论的内容,它是非常简单明了的。请注意,使用 PyTorch Lightning 将我们的代码模块化是如何让它看起来如此整洁和易读的!
我们现在可以训练我们的 GAN 了。我们将使用 GPU 训练它 100 个纪元。
可视化生成的图像
现在剩下的就是可视化生成的图像。在上面我们的 GAN 类的training_epoch_end()
函数中,我们在每个训练时期之后将生成器输出的图像保存到一个列表中。
我们可以把这些图像绘制在网格上,使之形象化。下面的代码随机选择了在第 100 个训练时期后生成的 10 幅图像,并将它们绘制在一个网格上。
这是输出结果:
那挺好的!输出类似于真正的手写数字。我们的发电机肯定学会了如何骗过鉴别器。
最后,正如承诺的那样,我们将创建帖子顶部显示的动画。使用matplotlib
中的FuncAnimation
函数,我们将一帧一帧地制作图上图像的动画。
下一步是什么?
恭喜你。您已经完成了本教程的学习。我希望你喜欢读这篇文章,就像我喜欢写这篇文章一样。幸运的是,这不是我们旅程的终点。在 Goodfellow 推出最初的 GAN 后不久,科学界在这一领域投入了巨大的努力,这导致了基于 GAN 的 AI 模型的激增。
我正在开始一系列这样的教程,在这些教程中,我将举例说明、解释和编码 GAN 的不同变体,包括一些重要的变体,如深度卷积 GAN (DCGAN) 和条件 GAN (CGAN) 。一定要关注我(如果你还没有!)以便在新教程发布时得到通知。
其他资源
这里的代码也可以在我的 Github 库中找到。我将不断更新这个库,在将来包含 GANs 的其他变体。
[## jamesloyys/py torch-Lightning-GAN
使用 py torch Lightning github.com 实现各种 GAN 架构](https://github.com/jamesloyys/PyTorch-Lightning-GAN)
数字基础
在 5 分钟内学习 NumPy 的基础知识
Emile Perron 在 Unsplash 上的照片
在本文中,我们将学习 Python 库 NumPy 的各种特性和功能
NumPy 是 Python 库之一,它支持多维、实体数组以及矩阵。它还支持大量的数学函数来操作这些数组。NumPy 为许多其他数据科学和数据可视化库提供了强大的基础。
在本文中,我们将使用 NumPy 创建、索引和操作数组。(你需要先了解如何在 Python 中使用元组和列表。)
首先,我们来看看 NumPy 数组。NumPy 数组由可以用切片索引的值组成,也可以用布尔或整数数组(掩码)索引。数组的形状表示数组在每个维度上的大小,也可以用整数元组来表示。对于标准的 2D 数组,形状给出了行数,后跟列数。
在我们开始之前,请确保您的系统中安装了所有必需的库。
使用 conda:
conda install numpy
使用画中画:
pip install numpy
使用 NumPy 创建数组
>>> import numpy as np
>>> a = np.array([1,22,333])
>>> print(a)
[1 22 333]
在深入了解 NumPy 的更多特性和功能之前,让我们先研究一下数组。我们可以在 Python 中使用。“形状”功能。
>>> print(a.shape)
(3,)
这里我们看到形状是(3),这意味着我们有一个大小为 3 的一维数组。如果我们想查看数组中的特定元素,我们使用与 Python 列表中相同的符号。
>>> print(a[0])
1>>> print(a[1])
22>>> print(a[2])
333
更改特定索引处的值:
>>> a[0] = 11
>>> print(a)
[ 11 22 333]
如您所见,第一个位置的元素现在已经从 1 变成了 11。
使用 NumPy 创建 2D 数组
>>> b = np.array([[4,5,6],[7,8,9]])
>>> print(b)
[[4 5 6]
[7 8 9]]
打印形状
>>> b.shape
(2, 3)
这表明我们有 2 行 3 列。
现在,为了查看 2D 数组中的各个元素,我们可以执行以下操作:
>>> print(b[0,0])
4
我们可以创建不同值的 numpy 数组,而不仅仅是整数
>>> c = np.array([9.0, 8.7, 6.5])
>>> print(c)
[9\. 8.7 6.5]
构造数组的其他 NumPy 函数
NumPy 提供了不同的函数来创建数组。例如,如果我想创建一个用零填充的数组,我可以很容易地使用 NumPy's。“零点”功能如下所示
>>> d = np.zeros((5,6))
>>> print(d)
[[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\. 0\. 0\. 0\. 0\. 0.]]
类似地,我们可以使用。某人的功能
>>> e = np.ones((5,6))
>>> print(e)
[[1\. 1\. 1\. 1\. 1\. 1.]
[1\. 1\. 1\. 1\. 1\. 1.]
[1\. 1\. 1\. 1\. 1\. 1.]
[1\. 1\. 1\. 1\. 1\. 1.]
[1\. 1\. 1\. 1\. 1\. 1.]]
此外,您可以通过使用。“full”函数,您需要在括号的第一部分提到数组的大小,在括号的第二部分提到常量值。下面显示了一个这样的例子,我们创建了一个填充了值“7.8”的 3x3 数组。
>>> f = np.full((3,3), 7.8)
>>> print(f)
[[7.8 7.8 7.8]
[7.8 7.8 7.8]
[7.8 7.8 7.8]]
NumPy 还提供了随机函数。使用它,我们可以创建一个由 0 到 1 之间的随机值组成的数组
>>> g= np.random.random((4,4))
>>> print(g)
[[0.21243056 0.4998238 0.46474266 0.24573327]
[0.80314845 0.94159578 0.65609858 0.0559475 ]
[0.80367609 0.35230391 0.91716958 0.03513166]
[0.37717325 0.00882003 0.82166044 0.7435783 ]]
使用 NumPy 索引
Numpy 提供了几种索引数组的方法。与 python 列表类似,NumPy 数组可以切片。由于数组可以是多维的,您需要为数组的每个维度指定切片。
让我们用随机值创建一个二维数组。
>>> i = np.array([[12,23,34], [45,56,67], [78,89,90]])
>>> print(i)
[[12 23 34]
[45 56 67]
[78 89 90]]
对于这个例子,让我们使用切片来提取数组的一部分,它由前 2 行和第 1、2 列组成
>>> j = i[:2, 1:3]
>>> print(j)
[[23 34]
[56 67]]
数组的切片(如上所示)是同一数据的视图。
>>> print(i[0,1])
23
您可以在数组中的任何位置更改该值。
>>> j[0,0]=75
>>> print(i[0,1])
75>>> print(i)
[[ 1 75 3]
[ 4 5 6]
[ 7 8 9]]
NumPy 中的布尔数组索引
布尔数组索引索引类型用于选择满足某些条件的数组元素。因此,让我们创建一个由一些随机值组成的数组,应用特定的条件,看看布尔数组索引是如何工作的。
>>> k = np.array([[26,78], [51,42], [30,89]])
>>> print(k)
[[26 78]
[51 42]
[30 89]]>>> print(k>50)
[[False True]
[ True False]
[False True]]
我们还可以使用布尔数组索引来创建一个新的一维数组,它包含所有实际上大于 50 的元素
>>> print(k[k>50])
[78 51 89]
NumPy 数学函数
基本数学函数对数组进行元素操作。这意味着一个数组中的一个元素对应于另一个数组中相同位置的一个元素。您可以同时使用数学运算符或 NumPy 提供的函数。两者给我们相同的输出。让我们对数组执行一些数学运算。
- 添加
>>> l = np.array([[1,2], [3,4]])
>>> m = np.array([[5,6], [7,8]])
>>> print(l+m)
[[ 6 8]
[10 12]]
这将根据元素在数组中的相应位置添加元素。我们可以使用 NumPy 函数获得类似的结果。
>>> print(np.add(l,m))
[[ 6 8]
[10 12]]
除此之外,NumPy 还具有减法、乘法、除法和计算数组和的功能。
2.减法
# Subtraction using math operators
>>> print(l-m)
[[ -4 -4]
[-4 -4]]# Subtraction using NumPy functions
>>> print(np.subtract(l,m))
[[ -4 -4]
[-4 -4]]
3.增加
# Multiplication using math operators
>>> print(l*m)
[[ 5 12]
[21 32]]# Multiplication using NumPy functions
>>> print(np.multiply(l,m))
[[ 5 12]
[21 32]]
4.分开
# Division using math operators
>>> print(l/m)
[[0.2 0.33333333]
[0.42857143 0.5 ]]# Division using NumPy functions
>>> print(np.divide(l,m))
[[0.2 0.33333333]
[0.42857143 0.5 ]]
5.总和
>>> print(l)
[[1 2]
[3 4]]#sum of all the elements
>>> print(np.sum(l))
10#sum of columns
>>> print(np.sum(l, axis=0))
[4 6]#sum of row
>>> print(np.sum(l, axis=1))
[3 7]
你可以在这里找到这个教程的代码。
我希望你喜欢这篇文章。谢谢你阅读它!
参考文献
[1] NumPy 文档:【https://numpy.org/doc/
回归的基础:什么,为什么和如何
回归技术的变体和真实世界用例
Katie Rodriguez 在 Unsplash 上的照片
凯文,如果你知道一个男孩的年龄,你能预测他的身高吗?
可以;如果你知道什么是回归以及它是如何工作的。
假设您有一个游泳班 5 名学生的记录,他们的年龄和身高如下。
然后一个新学生凯文(6 号学生)加入进来,他 5 岁。但他的身高未知。没问题!由于回归分析,可以根据班上其他学生的年龄-身高关系来估计他的未知身高。
回归只不过是两个变量之间的统计关系——在这种情况下是年龄和身高——这样,如果你知道另一个,你就可以预测其中一个。
在本文中,我将给出回归背后的高级统计直觉,并讨论它如何工作以及为什么工作。最后,我概述了一些真实世界的用例。
什么是回归?
让我们用一个正规的、教科书式的回归定义。
回归分析是一种调查变量之间关系的统计工具。通常,调查者试图确定一个变量对另一个变量的因果影响——例如,价格上涨对需求的影响,或者货币供应量的变化对通货膨胀率的影响。”资料来源:塞克斯(1993 年)。
里面装了不少东西。如果我们稍微打开包装,我们可以提取一些东西:
a)回归是一套工具/技术
b)回归确定两个或多个变量之间是否存在关系
c)回归测量关系的大小/强度
回归中的一些变化
我上面举的例子——年龄和身高——是两个变量之间的关系。身高是因变量,年龄是预测变量或自变量。从数学角度来说:
身高= f(年龄)
在统计学的行话中,它被称为“简单回归”,因为它只有一个预测因子(年龄)。但是如果需要几个预测器来预测一个因变量呢?我们来考虑一下房价,想想影响价格的因素有哪些?
回归模型的概念公式
房子的价格取决于几个因素——卧室和浴室的数量,建筑面积是其中的一部分(当然还有位置!).由于价格受到这些因素的影响,你需要所有这些因素作为预测因素。这种带有多个预测因子的回归称为“多元回归”。数学表示如下:
房价= f(#床位数、#卫生间数、建筑面积)
我们也可以把它写成回归方程的形式:
***房价= a+ b1 (床位数)+ b2 (浴池数)+ b3 (建筑面积)
回归的目的是通过某种统计程序找出 a,b1,b2 和 b3 的参数值,这样只需知道模型中的 3 个变量就可以预测未知房屋的价格。
刚刚讨论的简单和多重回归技术被称为“线性回归”——因为你用一条线性线来拟合数据。接下来,我将在下一节讨论一些非线性回归技术。
不同种类的回归
回归:统计与机器学习
取决于你是从事统计学还是机器学习,回归对你来说可能意味着稍微不同的东西。
很大一部分学统计学和计量经济学的学生倾向于认为回归是关于找出影响因变量的因素及其解释力的程度。这大概就是为什么在很多经典教材里,相关和回归是并行教授的。相关性告诉你一段关系的方向和强度,而回归则量化了效果。
然而,在机器学习中,一切都与应用有关。回归是许多进行预测的算法之一。
机器学习问题大致分为(1)分类和(2)回归问题。分类预测离散的类别(例如,狗对猫),回归预测数量(例如,在给定一些因素的情况下预测销售收入)。
机器学习中的回归算法
除了预测结果的线性回归技术之外,机器学习领域还使用了广泛的算法。以下是一些经常被引用的方法,并对每种方法进行了简要描述:
1)逻辑回归 是用来预测一个二元结果;例如对图片中的动物是狗还是猫等进行分类。
2)多项式回归 用于两个变量之间的关系不是线性的时候。换句话说,如果线性回归拟合数据不足(下图左),则拟合非线性线(右图)。
线性和多项式回归模型中的拟合线
3)岭回归 是一种降低模型复杂度的方法。如果有太多的预测因子,尤其是相关的预测因子,一些预测因子会被惩罚以减少它们的影响,而不是完全消除它们。
4)拉索 ,的简称最小绝对收缩和选择算子,在概念上类似于岭回归,但系数可以被罚以将它们设置为接近于零。
5)支持向量回归: 支持向量机是分类问题的一种流行算法,但在回归问题中使用了类似的原理。虽然正常回归使误差最小化,但是 SVR 拟合模型使得误差在阈值内。
支持向量回归
6)面板数据回归: 面板数据回归是一种非常专业化的回归技术,用于时间序列数据的建模。这是一种预测时间相关观测值的强大技术(无耻之徒:请看下面我的一篇旧文章)。
在数据科学和机器学习问题中应用计量经济学
towardsdatascience.com](/panel-data-regression-a-powerful-time-series-modeling-technique-7509ce043fa8)
一些使用案例
现在是讨论的最后也是最重要的部分——回归的用例。作为一名数据科学家,你懂多少算法,能建立多精密的模型都不重要,真正的价值在于现实世界的应用。这里是我们到目前为止讨论过的各种技术的一些用例。
- 确定二手车的价格:假设你是一名汽车经销商,你要给一辆刚刚入库的汽车贴上价格标签。传统上,价格是根据经销商的经验和市场趋势确定的。但数据科学改变了一切,你可以根据车型、品牌、年份、里程、颜色、车门数量等特征来设定汽车价格…..尽可能多的功能。
- 房屋定价:概念差不多。如果你曾经在房地产市场,你会注意到房价是如何起作用的——基于最重要的特征,如建造年份、建筑面积、位置、学区等。你可以进行回归拟合,从而轻松估算即将上市的房子的价格。
- 影响结果的因素:不是为了预测,而是在一些统计应用中,回归技术被用来找出影响特定结果的因素。例如,测量气候变化对 GDP 的影响。有一门完全不同的学科——计量经济学——专门研究这类问题。
数据科学家如何利用经济学家的工具箱
towardsdatascience.com](/econometrics-101-for-data-scientists-584f4f879c4f)
- 确定保险费:你有没有想过保险费是怎么定的?当你注册汽车保险时,你会被问及一系列关于汽车和司机的问题——汽车的品牌和型号、你的年龄、驾驶记录等等。所有的信息都用在回归模型中,以确定你将支付多少保费。
一锤定音
本文的目的是通过例子讨论回归背后的一些基本的统计直觉。我们还讨论了机器学习中使用的不同种类的线性和非线性回归技术。在最后一部分,我介绍了回归的一些实际用例。
当然,关于回归技术及其使用标准编程库的实现,还有很多可以说的。不过那个又搁置了一天,敬请关注!
【注:所有数字均由作者制作
监督情感分析的基础
NLP 预处理、BoW、TF-IDF、朴素贝叶斯、SVM、Spacy、Shapely、LSTM 等等
克里斯·j·戴维斯在 Unsplash 拍摄的照片
在这篇文章中,我将解释一些基本的机器学习方法来对推特情感进行分类,以及如何在 Python 中运行它们。
情感分析
情感分析用于识别数据的影响或情绪(积极的、消极的或中性的)。对于一个企业来说,这是一个简单的方法来确定客户对产品或服务的反应,并迅速发现任何可能需要立即关注的情绪变化。解决这个问题的最基本的方法是使用监督学习。我们可以让真正的人来确定和标记我们数据的情感,并像对待文本分类问题一样对待它。这正是我将在这篇文章中讨论的,并且将在后面的文章中重新讨论这个话题,讨论无监督的方法。
数据
- 你可能会在 data.world 上找到一些人为标注的推文数据。数据包含超过 8000 条被标记为积极、消极、中立或未知(“我不能说”)的推文。
- 斯坦福大学的一个团队提供了训练数据。此数据集的标签被自动标注。
自然语言处理预处理
在清理数据(删除带有缺失文本的观察结果、删除“未知”类、删除 RTs)并将我们的数据分成训练集、测试集和验证集之后,我们需要预处理文本,以便它们可以在我们的分析中正确量化。我将一个一个地检查它们。
1.仅保留 ASCII 字符
不同的编码会导致数据中出现一些奇怪的字符。因此,首先我们将确保我们只保留 ASCII 字符。下面的代码将过滤掉所有非 ASCII 字符。
**def** ascii_only(str_):
**return** str_.encode("ascii", "ignore").decode()
2.全部小写
这个很简单。
**def** make_lower(str_):
**return** str_.lower()
3.删除 HTML 符号、提及和链接
由于编码错误,一些推文包含 HTML 符号,如
。在我们删除标点符号之前,我们将首先删除这些单词,这样我们就不会在删除标点符号后留下胡言乱语。我们要删除的其他字母单词是用户名(例如@stereopickle)和超链接(在这个数据集中写成{link})。我们将使用 regex 来完成这项工作。
**import** **re
def** remove_nonwords(str_):
**return** re.sub("[^A-Za-z0-9 ]\w+[^A-Za-z0-9]*", ' ', str_)
这个表达式的意思是用空格替换所有不以字母数字字符开头的单词。这些单词可能以非字母数字字符结尾,也可能不以非字母数字字符结尾。
4.删除品牌词
这是特定于该数据集的步骤。因为我们的数据集包含关于产品的推文,所以有很多对实际品牌和产品名称的引用。我们不希望不同品牌或产品的总体情绪分布影响我们的模型,因此我们将删除其中一些。
**def** remove_brandwords(str_):
p = '''#?(iphone|ipad|sxsw|hcsm|google|apple|cisco|austin|
atari|intel|mac|pc|blackberry|android|linux|ubuntu)[a-z0-9]*'''
**return** re.sub(p, ' ', str_)
5.删除标点符号
又一次简单的清洗。请注意,为了简单起见,我在这里删除了所有标点符号,但在现实中,许多推文可能会使用标点符号来表达情绪,例如:)和:(。
**import** **string**
punctuations = string.punctuation
punctuations = punctuations + '�' + string.digits
**def** remove_punctuations(str_, punctuations):
table_ = str.maketrans('', '', punctuations)
**return** str_.translate(table_)
如果你对上面的方法感到好奇,请看看我的另一篇关于如何有效去除标点符号的帖子。
Python 中清理字符串的 8 种不同方法
towardsdatascience.com](/how-to-efficiently-remove-punctuations-from-a-string-899ad4a059fb)
6.词汇化&删除停用词
最后,我们将使用 NLTK 对我们的词汇表进行词汇化并删除停用词。总之,lemmatizer 将把词汇表还原成它的基本形式 lemma。
**from** **nltk.stem** **import** WordNetLemmatizer
**from nltk.corpus import** stopwordssw = stopwords.words('english')**def** lemmatize(str_, sw):
wnl = WordNetLemmatizer()
**return** ' '.join([wnl.lemmatize(w) **for** w **in** x.split() **if** w **not** **in** sw])
在这里阅读更多关于词汇化或词干化的内容。
使用 Python 预处理艺术描述数据
medium.com](https://medium.com/swlh/understanding-art-through-art-description-data-part-1-2682e899dfe5)
7.特征选择
在将上述函数应用到我们的文本数据之后,我们应该有一组非常干净的单词可以使用。但我喜欢在这里添加一个特征选择步骤,因为当每个词汇或二元或三元语法被标记化时,NLP 问题往往会以太多的特征结束。
当然,有许多方法可以清除特征,但是我喜欢使用 Spacy 来组合相似的单词。基本思想是遍历只出现几次的单词,并找到一个具有高相似性(在向量空间中更接近)的现有单词。
这个自定义函数返回一个字典,将出现频率低的单词作为关键字,将它们的替换词作为值。这一步通常会修复一些拼写错误,并纠正术语化可能遗漏的地方。我们可以使用这个替换词典将单词转换为更频繁出现的对应词,并删除出现次数少于指定次数的单词。
模型评估
估价
通常我们的数据会有很高的阶层不平衡问题,因为在大多数情况下,人们更可能写中立或积极的推文,而不是消极的推文。但对于大多数商业问题,我们的模型必须检测到这些负面推文。因此,我们将通过观察宏观平均 f1 分数以及精确召回曲线来关注这些问题。下面的函数将绘制 ROC 曲线和精度-召回曲线,并打印关键评估指标。
基线模型
我们可以使用 scikit-learn 的DummyClassifier
来首先查看我们的基线度量是什么,如果我们只是根据每个类出现的频率来分类的话。
**from sklearn.dummy import** DummyClassifier
dummy_classifier = **DummyClassifier**()
dummy_classifier.**fit**(tweets_train, labels_train)y_pred_p = dummy_classifier.predict_proba(tweets_validation)
y_pred = dummy_classifier.predict(tweets_validation)
词袋模型(计数向量)
量化文本数据的一个最简单的方法是计算每个单词的频率。scikit-learn 的CountVectorizer
可以轻松完成这项工作。
**from** **sklearn.feature_extraction.text** **import** CountVectorizer
countvec = **CountVectorizer**(ngram_range = (1, 2), min_df = 2)
count_vectors = countvec.**fit_transform**(tweets_train)
这将返回至少出现两次的单个词汇和二元模型的计数向量。然后我们可以使用这些计数向量来训练不同的分类算法。
TF-IDF 载体
计数向量的一个问题是,它只关注单个单词的频率,而不关心单词出现的上下文。没有办法评估一条推文中的特定单词有多重要。这就是术语频率-逆文档频率(TF-IDF)得分的由来。TF-IDF 得分对在一条推文中出现频率更高的词的权重大于在所有推文中出现频率更高的词。
**from** **sklearn.feature_extraction.text** **import** TfidfVectorizer
tfvec = **TfidfVectorizer**(ngram_range = (1, 2), min_df = 2)
tf_vectors = tfvec.fit_transform(tweets_train)
现在我们有了两个矢量化的文本,我们可以为每个矢量测试不同的分类器。
朴素贝叶斯
朴素贝叶斯是文本分类中比较流行的选择之一。这是对每个类和预测器的简单应用贝叶斯定理,并且它假设每个单独的特征(在我们的例子中是单词)是相互独立的。
比方说,我们有一条推特,上面写着… “我爱我的新手机。真的很快,很可靠,设计的很好!”。这条推文明显带有积极的情绪。在这种情况下,朴素贝叶斯模型假设像“爱”、“新”、“真的”、“快”、“可靠”这样的单个单词都独立地贡献给它的正类。换句话说,当使用“可靠”一词时,推文是正面的可能性不会因其他词而改变。这并不意味着这些词在外观上是独立的。一些单词可能经常一起出现,但这并不意味着每个单词对其类别的贡献是独立的。
当上述假设成立时,朴素贝叶斯算法使用简单且可靠。因为在我们的模型上测试需要矢量化,所以我们可以将管道构建到我们的模型中。
**from** **sklearn.naive_bayes** **import** MultinomialNB
**from** **sklearn.pipeline** **import** Pipelinemn_nb = **MultinomialNB**()# change countvec to tfvec for tf-idf
model = **Pipeline**([('vectorize', countvec), ('classify', mn_nb)])# fitting training count vectors (change to tf_vectors for tf-idf)
**model**['classify'].**fit**(count_vectors, labels_train)y_pred_p = **model**.predict_proba(tweets_validation)
y_pred = **model**.predict(tweets_validation)
**evaluating**(labels_validation, y_pred, y_pred_p)
由于朴素贝叶斯假设要素之间相互独立,因此它高估了每个要素对标注贡献的置信度,从而使其成为一个糟糕的估计器。所以对预测的概率要有所保留。
支持向量机(SVM)
文本分类算法的另一个流行选择是支持向量机(SVM)。简而言之,SVM 找到了一个超平面,这个超平面以最大的间距来划分这些类。在文本分类中 SVM 是首选的主要原因是我们倾向于以大量的特征结束。如果我们在这样一个拥有我们所有特征的高维空间中工作,就会导致一个被称为维度诅咒的问题。基本上,我们的空间太大了,我们的观察开始失去意义。但是 SVM 在处理大量特性时更加健壮,因为它使用了内核技巧。SVM 实际上并不在高维度中工作,它只是看着观察之间的成对距离,就好像它们在高维度中一样。做这项工作确实需要很长时间,但它很健壮。
**from** **sklearn.svm** **import** SVCsvm_classifier = SVC(class_weight = 'balanced', probability= **True**)
# don't forget to adjust the hyperparameters! # change countvec to tfvec for tf-idf
svm_model = **Pipeline**([('vectorize', countvec), ('classify', svm_classifier)])# fitting training count vectors (change to tf_vectors for tf-idf)
**svm_model**['classify'].**fit**(count_vectors, labels_train)y_pred_p = **svm_model**.predict_proba(tweets_validation)
y_pred = **svm_model**.predict(tweets_validation)
**evaluating**(labels_validation, y_pred, y_pred_p)
SHAP 评估
当 SVM 使用内核技巧时,就可解释性而言,事情就进入了一个灰色地带。但是我们可以使用 Shapley 值来解释单个特征是如何对分类起作用的。我们将使用 SHAP 的友好界面来可视化 Shapley 值。关于这方面的详细教程,我推荐阅读关于 SHAP 的文档。
**import** **shap** shap.initjs()
sample = shap.**kmeans**(count_vectors, 10)
e = shap.**KernelExplainer**(svm_model.predict_proba, sample, link = 'logit')
shap_vals = e.**shap_values**(X_val_tf, nsamples = 100)
shap.**summary_plot**(shap_vals,
feature_names = countvec.get_feature_names(),
class_names = svm_model.classes_)
唷,太多了。让我们休息一下。 freestocks 在 Unsplash 上拍照
LSTM
让我们再深入一点(字面上)。到目前为止,我们用两种不同的频率度量来量化我们的文本数据。但是每个单词的出现频率只讲述了故事的一小部分。理解语言及其意义需要理解句法,或者至少是单词的顺序。因此,我们将研究一种关心词汇序列的深度学习架构:长短期记忆(LSTM) 架构。
对于 LSTM,我们需要按顺序输入文本。以下步骤概述了运行和评估 LSTM 分类器的步骤。我解释了代码中的每一步。
单词嵌入(手套)
我们的 LSTM 模型的一个缺点是,它只包含我们训练数据中存在的信息,而词汇具有推文之外的语义。了解每个词汇在语义相似性方面的相互关系可能有助于我们的模型。我们可以基于预先训练的单词嵌入算法对我们的词汇应用权重。
为此,我们将使用由斯坦福大学的一个团队获得的矢量表示法 GloVe 。我用他们在 20 亿条推特上训练的 200 维词汇向量。你需要从他们的网站上下载载体。
然后,您可以将获得的向量矩阵作为嵌入权重添加到我们的 LSTM 架构的嵌入层中。
# adding the bolded part
model.add(Embedding(num_vocab, 200, **weights = [vector_matrix]**,
input_length = max_len))
通过使用单词嵌入和 LSTM,我的模型显示整体准确性提高了 20%,宏观平均 F1 分数提高了 16%。
我们回顾了使用 tweets 数据构建情感分析模型的基础。我将以一些需要思考和扩展的问题来结束这篇文章。
- 如果我们没有标签,我们如何解决同样的问题?(无监督学习)
- 在保持其可解释性的同时,还有哪些方法可以降低维度?
快乐学习!
照片由 Jonathan Daniels 在 Unsplash 上拍摄
高维数据聚类的基础(3D 点云)
3D 地理数据
为什么无监督的分割和聚类是“人工智能的主体”?使用它们时要注意什么?如何评价表演?三维点云数据的解释和说明。
聚类算法允许以无人监管的方式将数据划分为子组或聚类。直观上,这些片段将相似的观察结果组合在一起。因此,聚类算法在很大程度上取决于如何定义相似性这一概念,这通常是特定于应用领域的。
不同的聚类策略应用于这个房间的噪声点云。可以看出,空间邻近性似乎是定义这种相似性以构成片段的选择标准。弗洛伦特·普克斯博士
什么是集群?
聚类算法通常用于探索性数据分析。它们也构成了人工智能分类管道中的大部分过程,以无监督/自学的方式创建良好标记的数据集。
在 NIPS 2016 上展示的原始 LeCun 蛋糕模拟幻灯片,突出显示的区域现已更新。
在 3D 地理数据的范围内,聚类算法(也被定义为无监督分割)允许获得片段汤,该片段汤成为若干过程的主干,例如特征提取、分类或 3D 建模,如下图所示。
在这里,您可以看到一个自动建模过程,该过程利用分段信息来很好地提取 3D 网格。弗洛朗·普克斯博士
除了地理数据应用之外,它们还用于识别:
- 行为相似的客户(市场细分);
- 对某个工具有类似使用的用户;
- 社交网络中的社区;
- 金融交易中的循环模式。
它们通常是降维算法的补充,降维算法允许在二维或三维中查看不同的属性(称为维度)。如果“视图”表现出足够的去相关性,可以使用聚类算法来形成这些点的子组,即聚类,如下图所示。
通过创建一条线将数据集分成两个子组来查找两个分类的简单示例。
这样,点之间的关系可以直观地表示出来。或者,不是表示整个数据,而是每个聚类仅显示一个代表点。
一旦识别出聚类,也可以只使用每个聚类的一个代表来查看数据,而丢弃其他的。
确定两个聚类的质心作为新的基数据。弗洛伦特·普克斯博士
这为什么有用?
聚类算法在标记数据代价昂贵的常见情况下特别有用。以注释大型点云为例。根据每个点所代表的内容对其进行注释可能是一项漫长而乏味的工作(参见我在这里所做的)😄)这样做的人可能会由于疏忽或疲劳而无意中引入错误。让聚类算法将相似的点分组在一起,然后在给聚类分配标签时只需要人工操作,这样更便宜,甚至可能更有效。
语义分割工作流程中一个优势的简单说明。弗洛朗·普克斯博士
因此,聚类算法可以用于将同一聚类中的一个点的属性扩展到同一聚类中的所有点(在前面的例子中,表示的椅子对象。).
在地理数据范围之外,推断数据属性有助于:
- 找到相似的图像,可能代表相同的物体、相同的动物或相同的人;
- 提取可能谈论同一主题的相似文本;
- 在图像中搜索属于同一对象的像素(这被称为分割)。
在上面的例子中,主题(图像、文本、像素)被表示为 2D/3D/nD 点,然后被分组为簇。然后,足以推断,如果一个群集中的一个图像代表一只鸭子,则该群集中的所有图像部分都可能代表鸭子。
我们将定义几个需要优化的标准,以定义一个有趣的数据分区。这些然后被用来派生一些最著名的聚类算法,将在另一篇文章中讨论,否则阅读会有点密集😇。
如何知道聚类是否具有代表性?
在无监督算法的情况下,算法的目的不像在有监督算法的情况下那样明显,在有监督算法的情况下,有明确的任务要完成(例如分类或回归)。因此,该模式的成功更加主观。任务更难定义的事实并不妨碍我在下面详述的各种性能测量。
距离和相似性
聚类意味着将最接近或最相似的点组合在一起。聚类的概念很大程度上依赖于距离和相似性的概念。
这些概念对于形式化非常有用:
- (1)两个观测值彼此接近的程度;
- (2)观测值与群集的接近程度;
- (3)两个星团之间的距离。
简单说明两个观测值之间的一些距离(1),一个观测值和一个集群(2),两个集群(3)。弗洛伦特·普克斯博士
最常用的距离示例是欧几里德距离和曼哈顿距离。欧几里得距离是欧几里得空间中两点之间的“普通”直线距离。曼哈顿距离之所以被称为曼哈顿距离,是因为它在两个维度上对应于出租车在曼哈顿街道上行驶的距离,这两个维度要么相互平行,要么相互垂直。
欧几里德距离和曼哈顿距离的简单说明。弗洛朗·普克斯博士
因此,距离可以用来定义相似性:两个点离得越远,它们就越不相似,反之亦然。为了注入一点数学知识,我们可以非常简单地将 x 和 y 之间的距离 d 转换为相似性度量 s,例如:s(x,y)=1/1+d(x,y)。
定义相似性的另一种常见方法是使用皮尔逊相关,它测量当基础数据居中时由向量 x 和 y 形成的角度的余弦。
皮尔逊相关系数的简单说明。弗洛朗·普克斯博士
但不要太深入,重要的是要注意皮尔逊相关性将考虑分布的形状,而不是它们的振幅,欧几里德距离主要考虑的是振幅。因此,距离度量的选择是重要的。
集群形状
簇的形状是一个重要的元素,我们最初描述为:
- (1)自我收紧:两个接近的点必须属于同一个群集
- (2)相距较远:相距较远的两个点必定属于不同的簇。
紧密程度可以提示凝聚簇的形成。弗洛伦特·普克斯博士
通常,我们寻找自身紧密的星团。让我们用一个例子来解释这些性质,使用欧几里得距离。首先,我们可以很容易地计算一个簇的质心(这个簇的点的重心)。然后,聚类的均匀性可以定义为该聚类中包含的每个点到质心的距离的平均值。以这种方式,紧密的集群将比分散的点的集群具有更低的异质性。然后,为了表征数据集中的所有聚类,而不是一个聚类,我们可以计算每个聚类的同质性的平均值。
简单说明同质性如何提供直观的感觉来更好地表征集群。弗洛伦特·普克斯博士
其次,我们希望集群彼此远离。为了量化这一点,我们通常将两个聚类的间距定义为它们的质心之间的距离。再一次,我们可以计算得到的所有成对簇上这些量的平均值。
一个简单的例子展示了如何使用分离来获得一个好的聚类。弗洛朗·普克斯博士
我们现在有两个优化标准:同质性和分离性。为了方便起见,我们可以将它们归为一个标准,即戴维斯-波尔丁指数。该指数的思想是比较类内距离(同质性)——我们希望它低——和类间距离(分离性)——我们希望它高。对于一个给定的集群,这个指数更弱,因为所有的集群都是同类的,并且分离得很好。
另一种量化聚类满足这两个要求(同质性和分离性)的方式是测量所谓的轮廓系数。对于给定点 p,轮廓系数 s(p)用于评估该点是否属于“正确的”聚类。为此,我们尝试回答两个问题:
- p 靠近它所属的簇的点吗?我们可以计算 p 到它所属的簇中所有其他点的平均距离 a(p)。
- 这个点离其他点远吗?如果 p 被分配给另一个簇,我们计算 a(p)可以取的最小值 b(p)。如果 p 已经被正确赋值,那么 a(x) < b(x)。轮廓系数由 s(x)= b(x)-a(x)/max(a(x),b(x))给出,范围在-1 和 1 之间。它越接近 1,p 对其簇的分配就越令人满意。
💡 提示: 对一个聚类进行评估,其平均轮廓系数可以计算出来,例如使用 scikit-learn
和 sklearn.metrics.silhouette_score.
命令
集群稳定性
另一个重要的标准是聚类的稳定性:如果我用不同的初始化对相同的数据,或者对数据的不同子集,或者对相同的稍微有噪声的数据运行该算法几次,我会得到相同的结果吗?这个标准在选择分类数时特别重要:如果选择的分类数符合数据的自然结构,则分类将比不符合数据的自然结构更稳定。
寻找集群的“参数监督”及其影响的一个例子。弗洛朗·普克斯博士
在上面的例子中,试图确定 3 个聚类的算法将合理地找到我们看到的 3 个聚类。但是如果要求确定 4 个群,这 4 个群中的分布将更加随机,并且不一定是相同的两倍。这是确定 3 是比 4 更好的聚类数的一种方法。
与特定领域知识的兼容性
很多时候,我们还会“用眼睛”评估一个聚类算法,看看建议的聚类是否有意义。这个集群中分组的点都代表同一个对象吗?这两个集群中的点代表不同的对象吗?
请注意下图中的各个群集。它们有直观意义吗?中央灯柱是否应该用一簇来描述?3 簇?更多?弗洛朗·普克斯博士
为了更灵活地做到这一点,我们可以在一个数据集上工作,在这个数据集上我们知道数据的合理分区。然后,我们将这个分区与我们的聚类算法返回的分区进行比较。例如,我们可以处理由平面形状分割的点云。下一步是评估由聚类算法形成的组是否对应于那些预先定义的组。
获取点云的一部分,并创建“平面标记”数据集以与聚类结果进行比较的示例。弗洛朗·普克斯博士
很简单!就像评价一个多类分类算法。但没那么快:如果我们感兴趣的是相同的对象是否属于同一个集群,那么这个集群是第一个、第二个还是第 k 个集群都无关紧要。因此,必须使用特定的性能指标来评估数据集的两个分区的一致性。
💡 提示:**这些可以在* sklearn.metrics.
中找到*
这些措施的一个例子是兰德指数。Rand 指数是在两个分区中以相同方式分组的点对(p1,p2)的比例:或者因为在这两种情况下,P1 和 p2 属于相同的聚类,或者因为在这两种情况下,P1 和 p2 属于不同的聚类。
Rand 指数可以通过预测大量的聚类来人为地膨胀:属于不同聚类的点对将是众多的,并且很有可能两个标记不同的点将在两个不同的聚类中。调整后的 Rand 指数(ARI)通过归一化 Rand 指数(RI)来校正这种影响:ARI=RI-E(RI)/max(RI)-E(RI),其中 E(RI)是 Rand 指数的期望值,即通过随机划分数据获得的指数。对于随机聚类,该调整后的索引接近于 0,并且仅当聚类恰好对应于初始分区时,该索引才等于 1。
💡 提示: 在 scikit-learn
中可以算出感谢 sklearn.metrics.adjusted_rand_score
结论
无监督和自学习方法对于解决自动化挑战非常重要。特别是,在深度学习时代,手动创建标记数据集是乏味的,缓解这一过程的方法非常受欢迎。聚类算法为此提供了至关重要的解决方案,用于将数据集划分为相似观察值的子组:
- 它们可以用来更好地理解数据;
- 它们可用于促进数据可视化;
- 它们可用于推断数据属性。
然后,为了评估聚类算法,我们可以考虑:
- 它产生的簇的形状(它们是否密集、分离良好)。这里经常用到剪影系数;
- 算法的稳定性;
- 结果与特定领域知识的兼容性,可以使用丰富的度量来评估。
更进一步
在本文中,我介绍了集群的基本原理,特别是在 3D 点云上。下一步是非常自然的,包括直接进入代码,并查看可以用来开始的最突出的方法。这将在下一篇文章和使用 Python 中讨论。
如果您想扩展阅读范围并获得有关 Python 和使用 3D 数据(点云、网格)的基础知识,我建议您深入了解 3D 地理数据学院的信息:
编队学习先进的点云处理和三维自动化。开发新的 python 地理数据技能和开源…
learngeodata.eu](https://learngeodata.eu/point-cloud-processor-formation/)
或者你可以阅读以下内容:
被称为点云的离散空间数据集通常为决策应用奠定基础。但是他们能不能…
towardsdatascience.com](/the-future-of-3d-point-clouds-a-new-perspective-125b35b558b9) [## 使用 Python 探索 3D 点云处理
教程简单地设置 python 环境,开始处理和可视化 3D 点云数据。
towardsdatascience.com](/discover-3d-point-cloud-processing-with-python-6112d9ee38e7) [## 使用 Python 从点云生成 3D 网格的 5 步指南
生成 3D 网格的教程(。obj,。ply,。stl,。gltf)自动从三维点云使用 python。(奖金)…
towardsdatascience.com](/5-step-guide-to-generate-3d-meshes-from-point-clouds-with-python-36bad397d8ba)
参考文献和相关作品
集群应用和智能点云概念的简要概述
科技文章:
-
Poux,F. ,& Billen,R. (2019)。基于体素的三维点云语义分割:无监督的几何和关系特征与深度学习方法。 ISPRS 国际地理信息杂志。8(5), 213;https://doi.org/10.3390/ijgi8050213
-
Poux,F. ,纽维尔,r .,纽约,g .-a .&比伦,R. (2018)。三维点云语义建模:室内空间和家具的集成框架。遥感、 10 (9)、1412。https://doi.org/10.3390/rs10091412
-
Poux,F. ,Neuville,r .,Van Wersch,l .,Nys,g .-a .&Billen,R. (2017)。考古学中的 3D 点云:应用于准平面物体的获取、处理和知识集成的进展。地学, 7 (4),96。https://doi.org/10.3390/GEOSCIENCES7040096*
互联和自动驾驶汽车网络安全的未来
介绍自动驾驶汽车面临的不同安全挑战以及应对这些挑战的机器学习方法。
介绍
自动驾驶汽车利用传感器和复杂的算法来检测和响应周围的环境。自动驾驶汽车常用技术的一些例子有:计算机视觉、激光雷达、全球定位系统等
由于这些技术,自动驾驶汽车不需要司机来完成甚至复杂的旅程。此外,多辆自动驾驶汽车可以相互通信,以改善交通和避障。
图 1 总结了汽车自动化水平的发展。许多公司,如 Waymo 和特斯拉,现在都在大力投资于自动驾驶汽车(自动驾驶汽车)的未来领先地位。
图 1:自动驾驶汽车的自动化水平[1]
“交通部的研究人员估计,全自动驾驶汽车,也称为自动驾驶汽车,可以通过消除这些由于人为错误造成的事故,将交通死亡人数减少高达 94%。”
-兹德涅特,蒂娜·马多克斯[2]
网络安全
由查理·米勒和克里斯·沃洛塞克最近进行的一项研究表明,一辆吉普切诺基只需通过互联网连接就能被黑客攻击(让汽车停在高速公路上!).此外,其他类型的车辆已被证明对有线或无线攻击敏感(如丰田普锐斯、福特 Escape)。在这些情况下,黑客已经能够:激活/禁用车辆刹车、方向盘并提高车辆速度。
图 2:自动驾驶汽车中的网络安全[3]
黑客可以尝试以许多可能的方式利用自动驾驶汽车的漏洞[4]:
- 云计算:自动驾驶汽车每秒钟都会产生和存储新数据,并利用云计算进行快速存储/检索(例如识别 GPS 位置以预测交通流量)。如果黑客能够访问汽车的云数据库,就能够操纵汽车的许多功能(例如,关闭安全设备)。此外,因为信息传输必须尽可能快,所以很难对发送的信息进行高度加密。
- 多种编码语言:现在的汽车是由不同制造商制造的许多部件组装而成的。为了确保汽车完美运行,所有不同的部件都需要完美地相互通信。如果不能确保安全通信,黑客可能会试图利用这一点。汽车制造商通常会进行渗透测试来确保他们车辆的安全性。
黑客攻击自动驾驶汽车有两个主要风险:
- 黑客也许能远程控制这辆车。
- 黑客可能能够访问用户的个人信息。
像美国和英国这样的国家已经实施立法,以确保汽车制造商保持最低的网络安全标准。
确保自动驾驶汽车的高安全性也可以增加公众对投资这项新技术的信任。
如果你有兴趣了解更多关于自动驾驶汽车网络安全的信息,这篇研究论文是一个很好的起点。
机器学习
如前所述,车辆总是产生和存储大量数据,然后就轮到我们来决定如何充分利用这些数据。
机器学习有可能被用来识别和防止异常行为(例如,在高速公路上高速行驶时让汽车进入停车模式)[5]。通过这种方式,可以阻止黑客攻击的发生。
研究人员现在仍在进行,以提高预测的准确性。事实上,为了创建能够区分正常和异常驾驶行为的模型,必须考虑与汽车及其周围环境相关的许多因素。
此外,如果驾驶员根据自己的判断认为有必要,可以增加一个功能,使其允许“异常”行为。
图 3:自动驾驶汽车中的机器学习[6]
法律含义
随着自动驾驶汽车变得越来越受欢迎,机构现在开始担心是否有必要制定新的法律来规范它们的使用。
例如,2018 年在美国亚利桑那州,第一起自动驾驶汽车撞死行人的案件已经登记。根据警方的报告,行人可能有过错。在这种情况下,应该认定谁有过错?
- 是车里的司机吗?(事故发生时没有控制车辆的人)。
- 是软件开发商吗?(谁开发了处理这类情况的软件)。
- 是汽车制造商吗?(谁在设计和供应车辆时没有采取足够的预防措施)。
- 谁应该负责汽车保险?(因为在大多数情况下驾驶员不控制车辆)。
- 如果发生不可避免的碰撞,汽车应该优先考虑自身安全还是其他车辆/行人的安全?
这些问题中的大多数仍然没有答案,可能会采取不同的方法来解决它们(取决于监管国家和文化相对主义)。
联系人
如果你想了解我最新的文章和项目,请通过媒体关注我,并订阅我的邮件列表。以下是我的一些联系人详细信息:
文献学
[1]这就是自动驾驶汽车进化的样子。商业内幕。访问:https://www . business insider . com/what-the-different-levels-of-drilling-cars-2016-10?r=US & IR=T
[2]自动驾驶汽车如何在美国拯救超过 35 万人的生命,在全球拯救数百万人的生命。兹德涅特,蒂娜·马多克斯。访问:https://www . zdnet . com/article/how-autonomous-vehicles-can-save-over-35 万-lifes-in-the-us-and-millions-world wide/
[3]宾夕法尼亚州关于自动驾驶汽车的公众意见。可查阅:https://sites . PSU . edu/mihiryouthere/2017/03/03/关于自动驾驶汽车的公众意见/
[4]自动驾驶汽车:汽车制造商如何克服网络安全问题。杰迈玛·迈耶斯,绊网。可查阅:https://www . tripwire . com/state-security/featured/auto-drive-cars-cyber security-issues/
[5]机器学习如何增强自动驾驶汽车的网络安全。DINO CAUSEVIC,toptal。访问网址:https://www . top tal . com/insights/innovation/how-machine-learning-can-enhanced-cyber safety-for-autonomous-cars
[6]安全测试自动驾驶汽车需要考虑潜在的深层学习弱点,下一个大未来。可查阅:https://www . next big future . com/2016/10/safety-testing-auto-drive-cars-needs . html
基于向量自回归的未来价格预测
预测分析和时间序列数据
多步未来预测的简单步骤
作者图片
向量自回归(VAR)具有易于实施的优势。VAR 中的每个方程在右边有相同数量的变量,整个系统的系数{α1,α2,…,β11,β21,…, γ 11, γ 21,… }可以通过对每个方程单独应用(OLS)回归来容易地估计。我们可以使用普通的最小二乘(OLS)估计器从每个方程中分别计算出这个模型。由于 OLS 估计量具有标准的渐近性质,因此可以用标准的 t 和 F 统计量来检验一个方程或多个方程中的任何线性约束。VAR 模型相对于传统机器学习模型的优势在于,结果不会被庞大复杂的结构(“黑盒”)所隐藏,而是易于解释和获得。
格兰杰因果关系测试有助于了解一个或多个变量是否具有预测内容,脉冲响应函数和方差分解通常用于量化长期影响。我已经在过去的中写过关于 VAR 的以及多元时间序列如何被拟合以生成涵盖两者的预测。
在这里,我将讨论使用 VAR 预测未知未来的简单步骤。
stock = ['^RUT', '^GSPC', '^DJI', '^IXIC' ]
start = pd.to_datetime('1990-01-03')
df = web.DataReader(stock, data_source = 'yahoo', start = start);
print(df.tail());
这里,我们有来自罗素 2000 (^RUT)、标准普尔 500 (GSPC)、纳斯达克综合指数(IXIC)和道琼斯工业平均指数(^DJI).)的数据让我们为所有变量分离 Adj Close 列。
data = df['Adj Close']
data.tail()
让我们在应用标准化后在一个图表中直观地比较这些系列。我们可以看到这里选择的所有变量之间的高度相关性。这是多元 VAR 的一个很好的候选。
scaler = MinMaxScaler(feature_range=(0, 1))
sdf_np = scaler.fit_transform(data)
sdf = DataFrame(sdf_np, columns=data.columns, index=data.index)
plt.figure(figsize=(12,6))
plt.grid()
plt.plot(sdf)
plt.legend(sdf.columns)
plt.show()
我们的目标是预测^RUT 的预期未来价格,选择 DJI、GSPC 和 IXIC 的动机是它们与 RUT 的高度相关性。
我们还可以通过测量滚动窗口大小函数中的滚动窗口上的平均线性相关性来诊断相关性;在这里,我选择了 5 天和 20 天的窗口大小进行可视化显示。
blue, orange, red = '#1f77b4', '#ff7f0e', '#d62728' # color codes
plt.figure(figsize=(12,4))
plt.grid()
cor1, cor2, cor3 = list(), list(), list()
# average correlation for increasing rolling window size
for win in range(5, 20): # Days
cor1.append(data['^GSPC'].rolling(win).corr(data['^RUT']) \
.replace([np.inf, -np.inf], np.nan).dropna().mean())
cor2.append(data['^DJI'].rolling(win).corr(data['^RUT']) \
.replace([np.inf, -np.inf], np.nan).dropna().mean())
cor3.append(data['^IXIC'].rolling(win).corr(data['^RUT']) \
.replace([np.inf, -np.inf], np.nan).dropna().mean())plt.plot(range(5, 20), cor1, '.-', color=blue, label='RUT vs GSPC')
plt.plot(range(5, 20), cor2, '.-', color=orange, label='DJI vs RUT')
plt.plot(range(5, 20), cor3, '.-', color=red, label='IXIC vs RUT')
plt.legend()
plt.xlabel('Rolling Window Length [Days]', fontsize=12)
plt.ylabel('Average Correlation', fontsize=12)
plt.show()
在变量或特征选择的上下文中,我们需要决定将哪些变量包含到模型中。由于我们不能也不应该包括所有潜在利益的变量,我们在选择变量时必须有一个先验的想法。
ADFuller 测试平稳性
我们需要消除数据中的趋势,让模型进行预测。让我们检查一下数据集的平稳性。
def adfuller_test(series, signif=0.05, name='', verbose=False):
r = adfuller(series, autolag='AIC')
output = {'test_statistic':round(r[0], 4), 'pvalue':round(r[1], 4), 'n_lags':round(r[2], 4), 'n_obs':r[3]}
p_value = output['pvalue']
def adjust(val, length= 6): return str(val).ljust(length)print(f'Augmented Dickey-Fuller Test on "{name}"', "\n ", '-'*47)
print(f'Null Hypothesis: Data has unit root. Non-Stationary.')
print(f'Significance Level = {signif}')
print(f'Test Statistic = {output["test_statistic"]}')
print(f'No. Lags Chosen = {output["n_lags"]}')for key,val in r[4].items():
print(f' Critical value {adjust(key)} = {round(val, 3)}')
if p_value <= signif:
print(f" => P-Value = {p_value}. Rejecting Null Hypothesis.")
print(f" => Series is Stationary.")
else:
print(f" => P-Value = {p_value}. Weak evidence to reject the Null Hypothesis.")
print(f" => Series is Non-Stationary.")# ADF test on each column
for name, column in data.iteritems():
adfuller_test(column, name = column.name)
很明显,我们现有的数据集是不稳定的。让我们取一阶差分,再次检查平稳性。
nobs = int(10) # number of future steps to predict# differenced train data
data_diff = data.diff()
data_diff.dropna(inplace=True)
print('Glimpse of differenced data:')
print(data_diff.head())# plotting differenced data
data_diff.plot(figsize=(10,6), linewidth=5, fontsize=20)
plt.title('Differenced data')
plt.show()
从图中,我们可以评估一阶差分使数据稳定。然而,让我们运行 ADF 测试增益来验证。
# ADF Test on each column
for name, column in data_diff.iteritems():
adfuller_test(column, name=column.name)
我们的数据是平稳的,以适应回归模型。
向量自动回归
指定了一个模型,就必须决定 VAR 模型的适当滞后长度。在决定滞后的数量时,通常使用统计方法,如 Akaike 信息标准。
var_model = smt.VAR(data_diff)
res = var_model.select_order(maxlags=15)
print(res.summary())results = var_model.fit(maxlags=15, ic='aic')
print(results.summary())
未来预测
既然模型已经拟合好了,让我们来预测一下。
# make predictions
pred = results.forecast(results.y, steps=nobs)
pred = DataFrame(pred, columns = data.columns+ '_pred')
print(pred)
使用下面几行代码可以获得类似的观察结果。在这两种情况下,我们都得到了输出,但规模不同,因为我们的输入数据是不同的,以便稳定。
将转换数据反转为原始形状
pred = DataFrame(pred, columns=data.columns+ '_pred')def invert_transformation(data_diff, pred):
forecast = pred.copy()
columns = data.columns
for col in columns:
forecast[str(col)+'_pred'] = data[col].iloc[-1] + forecast[str(col) +'_pred'].cumsum()
return forecastoutput = invert_transformation(data_diff, pred)
print(output.loc[:, ['^RUT_pred']])
output = DataFrame(output['^RUT_pred'])
print(output)
以上是未来 10 天的预测;让我们为这些值指定未来的日期。
分配未来日期
d = data.tail(nobs)
d.reset_index(inplace = True)
d = d.append(DataFrame({'Date': pd.date_range(start = d.Date.iloc[-1], periods = (len(d)+1), freq = 'd', closed = 'right')}))
d.set_index('Date', inplace = True)
d = d.tail(nobs)
output.index = d.index
print(output)
所以,这里我们可以看到未来的预测。让我们用历史数据画一个图表来形象化。
fig = go.Figure()
n = output.index[0]
fig.add_trace(go.Scatter(x = data.index[-200:], y = data['^RUT'][-200:], marker = dict(color ="red"), name = "Actual close price"))
fig.add_trace(go.Scatter(x = output.index, y = output['^RUT_pred'], marker=dict(color = "green"), name = "Future prediction"))
fig.update_xaxes(showline = True, linewidth = 2, linecolor='black', mirror = True, showspikes = True,)
fig.update_yaxes(showline = True, linewidth = 2, linecolor='black', mirror = True, showspikes = True,)
fig.update_layout(title= "10 days days RUT Forecast", yaxis_title = 'RUTC (US$)', hovermode = "x", hoverdistance = 100, # Distance to show hover label of data point spikedistance = 1000,shapes = [dict( x0 = n, x1 = n, y0 = 0, y1 = 1, xref = 'x', yref = 'paper', line_width = 2)], annotations = [dict(x = n, y = 0.05, xref = 'x', yref = 'paper', showarrow = False, xanchor = 'left', text = 'Prediction')])
fig.update_layout(autosize = False, width = 1000, height = 400,)fig.show()
在这里,我们可以看到风险值预测未来价格的接近程度。
关键要点
VAR 很容易估计。具有良好的预测能力;VAR 模型能够捕捉时间序列变量的动态结构,通常将所有变量视为先验内生变量。然而,将标准风险值模型拟合到大维时间序列可能具有挑战性,主要是因为涉及大量参数。
我们在这里讨论了如何找到最大滞后,以及如何用转换后的数据拟合 VAR 模型。模型拟合后,下一步是预测多步未来价格,并将转换后的数据转换回原始形状,以查看实际预测价格。
在最后一个阶段,绘制历史价格和预测价格可以清晰直观地展示我们的预测。
注意: 此处描述的程序是实验性的,在用于任何商业目的时都应谨慎使用。所有此类使用风险自负。
面向未来的 2020 年分析工作:雇佣多元化团队
在之前的一篇文章中,我们讨论了人工智能领域一些最紧迫的问题,例如自动化系统对糟糕或不道德的决策缺乏人类问责,对偏见来源的理解不足,以及这些问题是如何因过度依赖“黑盒”方法和所谓的“客观”数据而加剧的。随着消费者越来越了解分析对他们生活的影响,行业将目光转向联邦和自我监管,以提供针对这些问题的安全网,任何分析企业都必须将这些问题放在战略的最前沿,以此为基础设施、方法和考虑事项的转变做好准备,一旦这些问题的影响完全确立,就必须考虑这些转变。
在这种情况下,我想重点谈谈上述文章末尾列出的建议之一;雇佣多元化团队。雇佣多元化团队的好处已经广为人知;麦肯锡在 2015 年发布的报告《多样性至关重要》(T4)中发现,性别多样性排名前四分之一的公司,其财务回报比全国行业中值高出 15%。考虑到种族/民族多样性,这一比例上升至 35%。与此同时,与数据集中的平均公司相比,性别和民族/种族均处于底部四分之一的公司在统计上不太可能实现高于平均水平的财务回报。
Sanford C. Bernstein & Co .领导和道德中心主任 Katherine W. Phillips 在《科学美国人》上发表的一篇文章强调了长达几十年的研究表明,多样性通过消除我们如何与人交往的隐藏偏见,如期望与我们有相似背景的人得出相同的结论,或分享对我们仅有的信息的看法,来促进创新、创造力和信息共享。
在分析领域,当涉及到处理数据和设计有偏见的决策系统时,很容易在上述问题和考虑多元化团队构成的好处之间划出一条线。作为一个领域,在真正理解和利用其方法和技术所需的专业知识领域(软件工程和架构、统计学、甚至社会学)中已经存在重要的交叉点,因此,考虑其他不太明显的多样性轴心(如性别、种族、民族和性取向)将导致创新、信息共享和事实核查的激增,这在行业的当前状态下是非常需要的。没有培育和培养多元化环境的公司,在处理数据、数据处理以及最终如何输入预测或分析系统方面,陷入偏见陷阱的风险更高。他们还错过了可能带来新机会的重要见解,特别是当这些数据来自一个异构和多样化的背景,团队甚至在分析的初始探索阶段都不准备识别或考虑这些背景时。
非多样化分析团队的高风险不仅仅是猜测。英伟达人工智能总监、加州理工学院教授阿尼玛·阿南德·库马尔(Anima Anandkumar)表示,当研究团队是同质的时候,人工智能系统对某些群体造成伤害的风险就更高:“(T4)多样化的团队更有可能在产品推出之前就发现可能产生负面社会后果的问题,”她说。在《与像我一样的人合作:美国的种族合作》一书中,作者理查德·弗里曼和黄伟分析了 1985 年至 2008 年间发表的 150 万篇科学论文,发现由不同种族的作者撰写的论文被引用的次数更多,影响因子也更高。证据很明显:多样性阻止偏见,鼓励好奇、怀疑和分析性思维;任何分析企业都会高度重视的属性。
来源:Kaggle 数据调查 2019 年数据集
然而,像 2018 年 Burtch Works 关于数据科学工作需求和薪酬的研究报告指出,只有 15%的数据科学家、22%的早期职业数据科学家和少得可怜的 10%的数据科学执行领导者是女性。 2019 年 Kaggle State of Data 调查显示了同样令人担忧的性别差距,84%的受访者认为自己是男性,这一数字与之前的调查相比几乎没有变化。对少数群体的研究更难获得;咨询公司 Priceonomics 的一篇 2017 年文章使用大会的学生入学数据发现,数据科学课程迄今为止西班牙裔/拉美裔和非裔美国学生的总比例最低。
哥斯达黎加 Granadilla 的 GAP 办事处
随着越来越多的证据表明多元化分析团队可以识别和处理偏见,产生强大的数据管道,并利用多元化数据集提供的文化和行为洞察,更令人失望的是,大多数企业数据工作仍未利用这些优势。
在我任职于 Growth Acceleration Partners 期间,我发现“投资于人”的价值不仅仅是其品牌材料中的一个要点。毫无疑问,我们的近岸性质鼓励并使我们能够与我有幸与之互动的极其多样化的员工接触,但偶然发现多样性的公司与真正理解多样性不仅是一种文化资产或“值得拥有的东西”,而且是其成长和创新框架不可或缺的一部分的公司之间是有区别的。在 Ideas to Invoices 播客中,首席执行官乔伊斯·德斯特(Joyce Durst)被引用说“让女性在公司各个级别的各个岗位上工作的关键在于思想的多样性。(……)我们可以有多样性,让最好的想法、创新和想法摆在桌面上(……),包括性别、年龄和多元文化的多样性。GAP 最近获得了全国妇女商业企业理事会颁发的妇女商业企业证书。
这种对多样性的理解和依赖使得我们构建和改进分析工作和产品的工作对于经验丰富的领导和新员工来说都更加容易。作为一家专注于软件的公司,建立一个强大的分析实践提出了在我们习惯的领域之外招聘的挑战。我们第一次接触到其他领域的精算学家、经济学家和专业人士,这些人与我们过去很少接触的学术机构的工作和项目有关。所有这些迫使我们修改我们的招聘和入职流程,但将它作为多元化思想的另一个来源被证明是一个成功的战略,并导致了一个强大的人才和机会渠道的创建,通过我们在行业内取得的有意义的成功提升了我们的地位。
对新员工如何融入公司其他部门的担忧,变成了一个例子,说明一家为欢迎各种风格的多样性而成立的公司如何能够轻松地融合来自意想不到背景的人,并发现自己在短期内获得技术和文化上的好处,不仅对他们受雇的特定团队,而且对整个公司都是如此。
多元化的价值已被充分研究和理解。鉴于上述优势以及分析行业及其消费者越来越意识到的关于偏见的特定陷阱,多元化团队已被证明是对这些偏见的有效威慑,这使得多元化必然是行业必须努力改善的领域之一这一结论成为定局。通过分析招聘实践中可能影响少数族裔招聘差距的偏见,从非常规背景中招聘,并将这些实践作为公司增长和创新战略的组成部分,我们不仅将开始理解和缓解这些问题的漫长道路,还将准备好应对和解决这十年不可避免地要面临的道德、问责和隐私挑战。在她为 2019 年最具影响力的 10 位女性领导人所做的简介中,阿尼玛·阿南德·库玛(Anima Anandkumar)对这一概念给予了肯定:“我希望这是人工智能革命的开始,它促进了多样性和包容性。我坚信这将是我们开发更好的人工智能技术的关键因素,这些技术可以促进公平性、可解释性、透明度和鲁棒性。”
模糊推理系统的 Python 实现
"一个故事在远处听起来总是很清楚,但你越靠近事件现场,它就变得越模糊。"― 乔治·奥威尔
照片由卡梅尔·加法拍摄
介绍
在以前的文章中,我们讨论了模糊集和模糊推理的基础。该报告还说明了使用模糊推理方法构建可能的控制应用程序。在本文中,我们将使用 Python 编程语言构建一个多输入/多输出模糊推理系统。假设读者对模糊推理有清晰的理解,并且已经阅读了前面提到的文章。
本文列出的所有代码都可以在 Github 上获得。
系统结构
下图说明了应用程序的结构。该设计基于对模糊推理系统的几个考虑,一些是:
- 模糊推理系统需要输入和输出变量以及一组模糊规则。
- 如果模糊推理系统是 Mamdani 类型的,则输入和输出变量都将包含模糊集的集合。
- 输入和输出变量非常相似,但是它们被模糊规则不同地使用。在执行过程中,输入变量使用系统的输入值来模糊化它们的集合,也就是说,它们确定该输入值属于变量的所有模糊集合的程度。每个规则在一定程度上对输出变量有贡献;这种贡献的总和将决定系统的输出。
- 模糊规则具有以下形式的结构:
**if** {antecedent clauses} **then** {consequent clauses}
因此,一个规则将包含几个先行类型的子句和一些结果类型的子句。条款将采用以下形式:
{variable name} **is** {set name}
系统图
我们将在以下部分讨论为该系统开发的类的一些实现细节:
模糊集类
模糊集 需要以下参数才能启动:
- 名称— 集合的名称
- 最小值— 集合的最小值
- 最大值— 集合的最大值
- 分辨率— 最小值和最大值之间的步数
因此,有可能通过使用两个 numpy 数组来表示模糊集;一个保存域值,另一个保存隶属度值。最初,所有隶属度值都将被设置为零。可以认为,如果最小值和最大值以及集合的分辨率可用,则不需要域 numpy 数组,因为可以计算相应的值。虽然这是完全正确的,但是在这个示例项目中,域数组是首选的,这样代码更加易读和简单。
模糊集初始化
在模糊变量的上下文中,所有集合将具有相同的最小值、最大值和分辨率值。
当我们处理一个离散化的域时,有必要将用于设置或检索隶属度的任何值调整为域数组中最接近的值。
模糊集域值调整
该类包含一些方法,通过这些方法,可以在给定相应数量的参数的情况下构造一组给定的形状。例如,在三角形集合的情况下,提供三个参数,两个定义集合的范围,一个定义顶点。通过使用这三个参数可以构建一个三角形集合,如下图所示。
三角模糊集方程
因为集合是基于 numpy 数组的,所以上面的等式可以直接转换成代码,如下所示。可以使用类似的方法构建具有不同形状的集合。
三角形集合创建
FuzzySet 类还包含联合、交集和非运算符,这些运算符是进行推理所必需的。所有运算符方法都返回一个新的模糊集,其中包含所发生运算的结果。
两个模糊集对象的并集
最后,我们实现了使用重心法从模糊集获得清晰结果的能力,这在上一篇文章中有详细介绍。值得一提的是,文献中有大量的去模糊化方法。尽管如此,由于重心法非常流行,所以在这个实现中使用了它。
重心去模糊化
模糊可变类
模糊可变类
如前所述,变量可以是输入或输出类型,其不同会影响模糊推理计算。 模糊变量 是保存在 python 字典中的集合的集合,该字典以集合名作为关键字。有一些方法可以将模糊集添加到变量中,这些模糊集将接受变量的限制和分辨率。
对于输入变量,通过检索给定域值的变量中所有集合的隶属度来进行模糊化。隶属度存储在集合中,因为规则在评估时需要它。
输入变量的模糊化
输出变量将最终产生模糊推理迭代的结果。这意味着,对于 Mamdani 类型的系统,正如我们在这里所构建的,输出变量将保存所有规则的模糊贡献的联合,并随后将该结果去模糊化,以获得可以在实际应用中使用的清晰值。
因此,输出变量将需要一个额外的 模糊集 属性来保存该变量的输出分布,其中的贡献是由每个规则产生的,并使用集合联合运算符相加。然后,通过调用输出分布集的重心法可以获得反模糊化结果。
模糊输出变量输出分布及反模糊化方法
模糊规则类
FuzzyClause 类需要两个属性;一个模糊变量和一个模糊集
variable **is** set
可以被创造出来。子句用于实现语句,这些语句可以链接在一起,形成规则的先行部分和后续部分。
当用作先行子句时, FuzzyClause 返回集合的最后一个隶属度值,该值是在模糊化阶段计算的,如前所述。
该规则将使用 min 运算符组合来自各种前提子句的隶属度值,获得规则激活,该规则激活然后与结果子句一起使用,以获得规则对输出变量的贡献。该操作分为两步:
- 使用最小操作符将激活值与结果 模糊集 相结合,这将作为模糊集的隶属度值的阈值。
- 使用联合运算符,将结果 模糊集 与从其他规则获得的 模糊集 组合,获得该变量的输出分布。
模糊子句作为前提或结果的执行方法
因此, 模糊规则 类需要两个属性:
- 包含先行子句和的列表
- 包含结果从句的列表
在执行 模糊规则 的过程中,执行上述程序。 模糊规则 通过适当地利用所有各种 模糊子句 来协调所有任务。
\规则执行
模糊系统类——将所有这些集合在一起。
在这个架构的最顶层,我们有 模糊系统 ,它协调模糊变量和模糊规则之间的所有活动。因此,系统包含输入和输出变量,它们存储在 python 字典中,使用变量名作为关键字和一个规则列表。
此阶段面临的挑战之一是最终用户将用来添加规则的方法,该方法应该理想地抽象出 FuzzyClause 类的实现细节。实现的方法包括提供两个 python 字典,其中包含以下格式的规则的前提和结果子句;
variable name : set name
一种更加用户友好的方法是以字符串的形式提供规则,然后解析该字符串来创建规则,但是对于演示应用程序来说,这似乎是一种不必要的开销。
向模糊系统添加新规则
推理过程的执行可以通过给定这种结构的几行代码来实现,其中执行以下步骤;
- 所有输出变量的输出分布集被清除。
- 系统的输入值被传递给相应的输入变量,以便变量中的每个集合可以确定其对于该输入值的隶属度。
- 模糊规则的执行发生了,这意味着所有输出变量的输出分布集现在将包含来自每个规则的贡献的并集。
- 使用重心去模糊化器对输出分布集进行去模糊化,以获得清晰的结果。
最后要注意的是,这里实现的模糊推理系统包含额外的函数来绘制模糊集和变量,并获取关于推理步骤执行的信息。
图书馆使用示例
在这一节中,我们将讨论模糊推理系统的使用。特别是,我们将实现在本系列的前一篇文章中设计的风扇速度案例研究。
模糊系统首先考虑输入和输出变量,然后设计模糊集来解释这些变量。
变量将需要一个下限和上限,正如我们将要处理的离散模糊集,系统的分辨率。因此,变量定义将如下所示
temp = FuzzyInputVariable('Temperature', 10, 40, 100)
其中变量'温度的范围在 10 到 40 度之间,并在 100 个箱中离散化。
根据变量的形状,为变量定义的模糊集需要不同的参数。例如,在三角形集合的情况下,需要三个参数,两个参数用于隶属度为 0 的上下极值,一个参数用于隶属度为 1 的顶点。因此,变量'温度'的三角形集合定义如下:
temp.add_triangular('Cold', 10, 10, 25)
其中被称为“T2”的集合“冷”在 10 度和 25 度具有极值,在 10 度具有顶点。在我们的系统中,我们考虑了两个输入变量,“T4”温度“T5”和“T6”湿度“T7”,以及一个输出变量“T8”速度“T9”。每个变量由三个模糊集描述。输出变量'速度'的定义如下:
motor_speed = FuzzyOutputVariable('Speed', 0, 100, 100) motor_speed.add_triangular('Slow', 0, 0, 50) motor_speed.add_triangular('Moderate', 10, 50, 90) motor_speed.add_triangular('Fast', 50, 100, 100)
正如我们之前看到的,模糊系统是包含这些变量和模糊规则的实体。因此,必须将变量添加到系统中,如下所示:
system = FuzzySystem()
system.add_input_variable(temp)
system.add_input_variable(humidity)
system.add_output_variable(motor_speed)
模糊规则
一个模糊系统执行模糊规则操作的形式
If x1 is S and x2 is M then y is S
其中,规则的 If 部分包含几个先行子句,then 部分将包含几个结果子句。为了简单起见,我们假设规则要求每个输入变量都有一个 antecedent 子句,并且只通过“and”语句连接在一起。可以用“or”连接语句,语句也可以包含集合上的运算符,如“not”。
向我们的系统添加模糊规则的最简单的方法是提供先行子句和结果子句的列表。一种方法是使用包含以下内容的 python 字典
Variable:Set
条款集的条目。因此,上述规则可以实现如下:
system.add_rule(
{ 'Temperature':'Cold',
'Humidity':'Wet' },{ 'Speed':'Slow'})
系统的执行包括输入所有输入变量的值,并获得输出值的值作为回报。同样,这是通过使用字典来实现的,字典使用变量的名称作为关键字。
output = system.evaluate_output({
'Temperature':18,
'Humidity':60 })
系统将返回一个字典,其中包含作为关键字的输出变量的名称,以及作为值的反模糊结果。
结论
在本文中,我们研究了模糊推理系统的实际实现。虽然这里介绍的库还需要进一步的工作,以便可以在实际项目中使用,包括验证和异常处理,但它可以作为需要模糊推理的项目的基础。还建议看看一些可用的开源项目,特别是 SciPy 的模糊逻辑工具箱 skfuzzy 。
在下一篇文章中,我们将研究从数据集创建模糊系统的方法,以便在机器学习场景中使用模糊逻辑。类似于模糊逻辑概念的介绍,接下来是一篇实用的文章。
使用 Splink 对数亿条记录进行模糊匹配和重复数据删除
支持 Python、PySpark 和 AWS Athena 的快速、准确和可扩展的记录链接
摘要
Splink 是一个用于概率记录链接(实体解析)的 Python 库。它支持使用Apache Spark
、AWS Athena
或DuckDB
后端运行记录链接工作负载。
其主要特点是:
- 它非常快。使用
DuckDB
后端,它能够在两分钟内连接现代笔记本电脑上的一百万条记录。 - 它非常精确,支持词频调整和复杂的模糊匹配逻辑。
- 它能够使用
Spark
或AWS Athena
后端链接非常大的数据集(超过 1 亿条记录)。 - 具有简单但高度可定制的界面,因此用户可以解决大多数记录链接和重复数据删除问题
- 不需要训练数据,因为可以使用无监督的方法来训练模型。
- 支持从探索性分析师到模型预测、诊断和质量保证的数据链接的完整工作流程。
- 是健壮的,有一套自动化的单元和集成测试。
问题陈述
一个常见的数据质量问题是有多个不同的记录引用同一个实体,但没有唯一的标识符将这些实体联系在一起。
例如,客户数据可能已经被多次输入到多个不同的计算机系统中,具有不同的姓名拼写、不同的地址和其他打字错误。缺乏唯一的客户标识符给数据分析的所有阶段带来了挑战,从计算唯一客户的数量等基本问题,到用于机器学习目的的客户详细信息的特征工程。
对这个问题有大量的理论和实证研究。该解决方案通常涉及使用统计估计、机器学习和/或基于规则的逻辑来计算新的唯一标识符列,该唯一标识符列允许实体被链接和分组。
然而,缺乏能够在数百万条记录的规模上解决这个问题的自由软件——这是大型组织中常见的规模。解决这个问题通常需要生成大量的记录比较,因此不适合 R 或 Python 中的内存解决方案。像 Apache Spark 这样的分布式计算框架,或者像 DuckDB 这样并行化且不受内存限制的后端,是更好的选择。
我们发布了一个名为splink
的免费开源库,实现了 fell egi-Sunter/期望最大化方法,这是数据链接文献的关键统计模型之一。这是一种无监督的学习算法,它为每对记录比较产生一个匹配分数。
尝试一下
你可以使用我们的活页夹链接在这里在 Jupyter 笔记本上试试这个图书馆。
这些演示说明了如何使用这个库,但是请注意,它们是在免费服务器上以本地模式运行的,所以不要期望有很好的性能。
您也可以访问我们的文档网站。
它是如何工作的
Splink 是 Fellegi-Sunter 模型的一个实现。该软件使用一种称为阻塞的方法生成成对记录比较,并为每对记录计算匹配分数,量化两个记录之间的相似性。
匹配分数由称为部分匹配权重的参数确定。这些量化了比较的不同方面的重要性。
例如,出生日期匹配比性别匹配更有利于两个记录匹配。邮政编码的不匹配可能提供不匹配的弱证据,因为人们搬家了,而出生日期的不匹配可能是不匹配记录的强有力证据。
这个简单的想法有很大的力量来构建高度微妙的模型。可以为任意数量的用户定义的场景计算部分匹配权重,而不仅仅是匹配或不匹配。例如,对于邮政编码不匹配但彼此相距在 10 英里以内的情况,可以估计部分匹配权重。
这些部分匹配权重被组合成一个总匹配分数,该分数表示两个记录匹配的证据的权重。
该库使用无监督学习(期望最大化算法)来估计这些匹配权重。你可以在我的互动培训材料中了解更多关于理论的内容。
关于这一切如何工作的更详细的视频描述可以在这里找到。
Splink 的一些图形输出
样本代码
我们试图设计一个简单的界面,但仍然可以适应大多数记录链接和重复数据删除问题。
在下面的代码中,我们:
- 指定数据链接模型
- 估计它的参数
- 使用该模型计算成对匹配分数
- 将匹配分数分组以产生估计的唯一个人 id
from splink.duckdb.duckdb_linker import DuckDBLinker
from splink.duckdb.duckdb_comparison_library import (
exact_match,
levenshtein_at_thresholds,
)
import pandas as pd
df = pd.read_csv("./tests/datasets/fake_1000_from_splink_demos.csv")
*# Specify a data linkage model*
settings = {
"link_type": "dedupe_only",
"blocking_rules_to_generate_predictions": [
"l.first_name = r.first_name",
"l.surname = r.surname",
],
"comparisons": [
levenshtein_at_thresholds("first_name", 2),
exact_match("surname"),
exact_match("dob"),
exact_match("city", term_frequency_adjustments=True),
exact_match("email"),
],
}
linker = DuckDBLinker(df, settings)
*# Estimate its parameters*
linker.estimate_u_using_random_sampling(target_rows=1e6)
blocking_rule_for_training = "l.first_name = r.first_name and l.surname = r.surname"
linker.estimate_parameters_using_expectation_maximisation(blocking_rule_for_training)
blocking_rule_for_training = "l.dob = r.dob"
linker.estimate_parameters_using_expectation_maximisation(blocking_rule_for_training)
*# Use the model to compute pairwise match scores*
pairwise_predictions = linker.predict()
*# Cluster the match scores into groups to produce an estimated unique person id*
clusters = linker.cluster_pairwise_predictions_at_threshold(pairwise_predictions, 0.95)
clusters.as_pandas_dataframe(limit=5)
反馈
我们感谢所有做出贡献并提供反馈的用户。请通过以下方式继续这样做:
或者我是推特上的 @robinlinacre 。
基于机器学习的模糊姓名匹配
用于语义名称匹配的堆叠语音算法、字符串度量和字符嵌入
在 GitHub 上查看完整代码
由 Thom Masat 在 Unsplash 上拍摄的照片
在处理外部数据时,通常情况下不存在一个公共标识符,如数字键。代替唯一标识符,一个人的全名可以用作链接数据的通用或复合密钥的一部分,但是,这不是一个万无一失的解决方案。
让我们以名字 艾伦图灵 为例;不同的数据源可能已经记录了呼叫名字。数据录入可能会无意中记录:艾伦、艾伦,或者更糟,未被发现的错别字( Alam Turing )到他们的数据库中。企业文档扫描解决方案(OCR)也充斥着误读。
通过应用软逻辑来近似拼写和语音(声音)特征的认知过程,人类代理可以直观地将这些变化分配给艾伦·图灵的同一实体。通常简称为的伪君子并不总是具有这些特征,而是代理人习得联想的一部分,即 Charles → Chip 。
接下来的研究是应用机器学习来实现替代名称识别的类人逻辑和语义的外观。
数据收集
我搜集了许多名字的常用替代拼法,大约有 17500 对。这些名字被限制为 ASCII 码,并包括许多 Unicode 编码的跨文化例子,以避免过度适应西方的命名惯例。
使用名字作为我们的模型的核心数据的直觉是在名字成分上集成集成方法,要求姓氏的精确或语音匹配,以确保更高的精确度/更少的误报,代价是一些回忆。
我决定使类不平衡(1:4 ),因为对负类的欠采样会导致对正类的明显的人为偏向。很难近似每一类的先验概率,但是假设这些类是不平衡的,有利于负类。
特征选择
有许多字符串度量和语音算法可用作特征,基本级别模型使用 20 多种特征,包括:
- 莱文斯坦距离
- 二元模型相似性
- Jaro 距离
- 编辑距离
- Soundex 编码
字符嵌入
深层的 LSTM 暹罗网络已经被证明在学习文本相似性方面是有效的。我使用 TensorFlow 在名字对上训练这些网络,并使用非折叠预测作为元模型的一个特征。
深层架构包括一个字符嵌入层,后面是一个 biLSTM 和能量损耗层。
原始变换
名称可以被转换,以帮助我们的模型从相同的数据中学习新的模式。转换包括:
- 将名称分割成音节以获得有意义的多记号串度量(例如,来自 fuzzywuzzy 包的记号排序和记号集)
- 移除高频名称结尾
- 去除元音
- 转换为国际音标
模型结构
我使用 AutoML 软件包 TPOT 来帮助选择一个优化的管道和超参数,以 F1 作为评分标准。
基本模型和字符嵌入网络通过分层 10 折交叉验证进行堆叠,以训练一个逻辑回归元模型。基础模型中的一些特性被包含进来,为元模型提供额外的上下文和维度。网格搜索用于选择最佳参数和特征,优先考虑精度。
估价
国际备选名测试集的评估指标:
这个模型是专门训练来处理替代名称的,但也能很好地正确分类所有上述变体,包括印刷错误。
所用的方法和由此产生的模型从此被戏称为 HMNI(你好我的名字是)。我已经开源了这个项目(处于测试阶段),作为一个 Python 包,使用了相同的名字。
如何在你的项目中使用 HMNI
通过 PyPI 使用 PIP 安装
pip install hmni
快速使用指南—配对相似性、记录关联、重复数据删除和标准化
更多即将推出……
我会在HMNI 的未来版本中更新这篇文章;包括最佳性能模型、特定语言配置和数据处理优化。
来自数据的模糊系统
从传感器信息中生成人类可读的知识
介绍
模糊逻辑理论引入了一个框架,通过这个框架,人类的知识可以被形式化,并由机器在从相机到火车的各种应用中使用。我们在以前的帖子中讨论的基本思想只涉及到使用基于模糊逻辑的系统的这一方面;这是人类经验在机器驱动的应用中的应用。虽然有许多这样的技术相关的例子;也有一些应用程序,在这些应用程序中,人类用户很难清楚地表达他们所掌握的知识。这些应用包括驾驶汽车或识别图像。机器学习技术在这种情况下提供了一个极好的平台,在这种情况下,输入和相应的输出集合是可用的,建立一个模型,使用可用的数据提供从输入数据到输出的转换。
在这篇文章中,我们将讨论一种算法,这种算法是由李教授和 Jerry Mendel 教授提出的从数据中构造模糊系统。这种技术和类似技术的一个令人兴奋的方面是能够从数据中以模糊集和规则的形式获得人类容易理解的知识。
程序
正如我们在简介中所解释的,本练习的目标是,给定一组输入/输出组合,我们将生成一个规则集,确定输入和输出之间的映射。在这个讨论中,我们将考虑一个双输入单输出系统。对于读者来说,将这个过程扩展到更复杂的系统应该是一项简单的任务。
步骤 1-将输入和输出空间划分为模糊区域。
我们首先给每个输入和输出空间分配一些模糊集。王和孟德尔指定了奇数个均匀间隔的模糊区域,由 2N+1 决定,其中 N 为整数。正如我们将在后面看到的, N 的值会影响我们模型的性能,有时会导致欠拟合/过拟合。 N 因此是我们用来调整该系统性能的超参数之一。
将输入空间划分成模糊区域,其中 N=2
第二步——从数据中生成模糊规则。
我们可以使用我们的输入和输出空间,以及我们刚刚定义的模糊区域和应用程序的数据集来生成以下形式的模糊规则:
**If** {antecedent clauses} **then** {consequent clauses}
我们首先确定数据集中每个样本对该空间中不同模糊区域的隶属度。例如,我们考虑下面描述的一个例子:
科雷斯
我们获得以下隶属度值。
样本 1 的隶属度值
然后,我们将具有最大隶属度的区域分配给空间,这由上表中突出显示的元素表示,以便可以获得一个规则:
sample 1 => **If** x1 is b1 and x2 is s1 **then** y is ce => Rule 1
下图显示了第二个示例,以及它生成的隶属度结果。
因此,该示例将产生以下规则:
sample 2=> **If** x1 is b1 **and** x2 is ce **then** y is b1 => Rule 2
步骤 3-为每个规则分配一个等级。
步骤 2 实现起来非常简单,但是它有一个问题;它将生成冲突的规则,即具有相同的前置子句但不同的后置子句的规则。Wang 和 Medel 通过给每个规则分配一个度来解决这个问题,使用乘积策略,使得度是来自形成规则的前件和后件空间的所有隶属度值的乘积。我们保留具有最重要程度的规则,而我们丢弃具有相同前提但具有较小程度的规则。
如果我们参考前面的例子,规则 1 的度数将等于:
对于规则 2,我们得到:
我们注意到,这个过程在实践中极大地减少了规则的数量。
还可以通过将人的因素引入到规则程度来将人的知识融合到从数据获得的知识中,这在实践中具有很高的适用性,因为人的监督可以评估数据的可靠性,因此可以直接从数据中生成规则。在不需要人工干预的情况下,对于所有规则,该因子都设置为 1。因此,规则 1 可以定义如下:
步骤 4-创建一个组合模糊规则库
组合模糊规则库的概念在之前的文章中已经讨论过了。它是一个矩阵,保存系统的模糊规则库信息。组合的模糊规则库可以包含使用上述过程以数字方式生成的规则,也可以包含从人类经验中获得的规则。
本系统的组合模糊规则库。注意规则 1 和规则 2。
步骤 5-基于组合的模糊规则库确定映射。
该过程的最后一步解释了用于确定给定的 y,(x1,x2)的值的去模糊化策略。王和孟德尔提出了一种不同于马姆达尼使用的最大最小计算方法。我们必须考虑到,在实际应用中,与通常使用模糊逻辑的典型控制应用相比,输入空间的数量是很大的。此外,此过程将生成大量规则,因此使用“正常”方法计算输出是不切实际的。
对于给定的输入组合(x1,x2),我们结合给定规则的前件,使用乘积运算符确定对应于(x1,x2)的输出控制程度。如果
是第 I 条规则的输出控制程度,
因此对于规则 1
**If** x1 is b1 and x2 is s1 **then** y is ce
我们现在将模糊区域的中心定义为在该区域的隶属函数等于 1 的所有点中具有最小绝对值的点,如下所示;
模糊区域的中心
因此,给定(x1,x2)组合的 y 值为
其中 K 是规则数。
测试
用 Python 开发了上述算法的一个(非常脏的)实现,用真实数据集进行测试。使用的代码和数据可以在 Github 中找到。对该系统的一些考虑包括。
- 模糊系统直接由测试数据生成。
- 这些集合是使用原始论文中的建议创建的,即均匀分布。然而,有趣的是看到改变这种方法的效果。一种想法是围绕数据集平均值创建集合,其分布与标准差相关,这可能会在未来的帖子中进行调查。
- 所创建的系统没有隐式地迎合分类数据,这是将来的改进,可以在现实生活场景中相当大地影响系统的性能。
测试指标
我们将使用决定系数(R 平方)来评估该系统的性能,并调整被识别的超参数,即生成的模糊集的数量。
要解释 R-Squared,首先要定义平方和 total 和平方和残差。
平方和总和是因变量(y)和观察到的因变量平均值之间的平方差总和。
残差平方和是因变量的实际值和估计值之差的平方和。
r 平方可以通过下式计算
我们注意到 R 平方的值在 0 和 1 之间,越大越好。如果 R 平方=1,则没有误差,估计值将等于实际值。
案例研究 1——噪声传感器
我们用一个简单的应用程序开始测试这个过程;一个单输入单输出系统,代表一个具有指数响应的假想传感器。让事情变得复杂一点,我们在传感器数据中添加了一些噪声,如下图所示:
我们首先检查 x 和 y 的 N 的不同值的各种结果。下图显示了在搜索最佳值的过程中获得的结果。
当 N_x 设置为 4,N_y 设置为 3 时,获得最佳响应,R 平方值为 0.985。由该系统生成的规则是特别令人感兴趣的,因为它们可以以容易理解的方式解释该系统如何工作;
If x is s4 then Y is s3
If x is s3 then Y is s3
If x is s2 then Y is s3
If x is s1 then Y is s3
If x is ce then Y is s2
If x is b1 then Y is s2
If x is b2 then Y is s1
If x is b3 then Y is ce
If x is b4 then Y is b3
我们注意到显示指数性质的系统的特征被清楚地解释。对于所有小的输入值,输出值保持最小,随着输入值的进一步增加,输出值迅速增加。
系统也容易出现过拟合,例如 N_x=5 和 N_y=2。
案例研究 2 —温度
对于第二个测试,我们使用了 2006-2016 年 Szeged 的天气。该数据集包含超过 96,000 个由 12 个特征组成的训练示例。
Formatted Date object
Summary object
Precip Type object
Temperature (C) float64
Apparent Temperature (C) float64
Humidity float64
Wind Speed (km/h) float64
Wind Bearing (degrees) float64
Visibility (km) float64
Loud Cover float64
Pressure (millibars) float64
Daily Summary object
在这个练习中,我们将放弃这些特征中的大部分,并评估我们是否可以预测给定月份的温度和湿度。
在检查数据时,我们注意到平均温度在 1 到 23 摄氏度之间变化,每月变化约 20 度。
平均湿度在 0.63 和 0.85 之间变化,但我们也注意到它总是可能达到 100%,与月份无关。
被测试的最佳模糊系统由用于输入变量的 3 个模糊空间( N =1)和用于温度的 9 个模糊空间( N =4)组成。系统生成了下面的模糊分布图中描述的九条规则,并使用 20%的测试样本获得了 0.75 的 R 平方值。
结论
上述系统从数据中产生人类可读的规则,可以帮助我们获得对复杂系统的洞察力。以下是对未来工作的几点看法和想法:
- 该系统需要一个相当大的数据集,需要覆盖所有可能产生的规则。因此,随着要素数量的增加,需要更大的数据集,因为所需的规则数量将呈指数增长。
- 也许可以通过检查相邻的规则来生成缺失的规则。如果一个不存在的规则可以在一个由相同的输出空间包围的组合模糊规则库中被说明,则该规则可能属于相同的空间。这一想法将在未来得到检验。
- 模糊空间的分布影响系统的性能,尽管像标准化和规范化这样的预处理有助于限制数据分布对系统的影响。
- 必须调查手工制定的规则的影响。验证人类经验是否真的可以增强机器生成系统的性能是一件有趣的事情。
- 检查负责规则的数据并使用信息来确定为什么测试样本会产生重大错误也是很有趣的。
FuzzyWuzzy:用 Python 在一列中查找相似的字符串
马特&克里斯·普阿在 Unsplash 上的照片
令牌排序比率与令牌集比率
使数据变脏的方法有很多种,数据录入不一致就是其中之一。不一致的值比重复的值更糟糕,有时很难检测到。本文介绍了我如何应用 FuzzyWuzzy 包在一个拉面评论数据集中查找相似的拉面品牌名称(完整的 Jupyter 笔记本可以在我的 GitHub 中找到)。你也可以在这里找到缩短代码的函数。
数据
import pandas as pd
import numpy as np
from fuzzywuzzy import process, fuzzramen = pd.read_excel('The-Ramen-Rater-The-Big-List-1-3400-Current- As-Of-Jan-25-2020.xlsx')
ramen.head()
数据集的前几行
ramen.info()**<class 'pandas.core.frame.DataFrame'>
RangeIndex: 3400 entries, 0 to 3399
Data columns (total 6 columns):
Review # 3400 non-null int64
Brand 3400 non-null object
Variety 3400 non-null object
Style 3400 non-null object
Country 3400 non-null object
Stars 3400 non-null object
dtypes: int64(1), object(5)
memory usage: 159.5+ KB**
这个数据集非常简单,容易理解。在搬到 FuzzyWuzzy 之前,我想再核实一些信息。首先,我删除了每一列中的前导空格和尾随空格(如果有的话),然后打印出它们的唯一值的数量。
for col in ramen[['Brand','Variety','Style','Country']]:
ramen[col] = ramen[col].str.strip()
print('Number of unique values in ' + str(col) +': ' + str(ramen[col].nunique()))**Number of unique values in Brand: 499
Number of unique values in Variety: 3170
Number of unique values in Style: 8
Number of unique values in Country: 48**
通过对独特的品牌名称进行排序,可以看出是否有相似的。结果只显示前 20 个品牌。
unique_brand = ramen['Brand'].unique().tolist()
sorted(unique_brand)[:20]**['1 To 3 Noodles',
'7 Select',
'7 Select/Nissin',
'7-Eleven / Nissin',
'A-One',
'A-Sha',
'A-Sha Dry Noodle',
'A1',
'ABC',
'Acecook',
'Adabi',
'Ah Lai',
'Aji-no-men',
'Ajinatori',
'Ajinomoto',
'Alhami',
'Amianda',
'Amino',
"Annie Chun's",
'Aroi']**
我们可以在一开始就看到一些可疑的名字。接下来,让我们用 FuzzyWuzzy 做一些测试。
快速模糊测试
FuzzyWuzzy 有四个记分员选项来查找两个字符串之间的 Levenshtein 距离。在本例中,我将检查标记排序比率和标记集比率,因为我认为它们更适合这个可能有混合单词顺序和重复单词的数据集。
我挑选了四个品牌名称,并在品牌栏中找到了它们相似的名称。因为我们将 Brand 列与其自身进行匹配,所以结果将始终包括得分为 100 的所选名称。
令牌排序比率
token sort ratio scorer 对字符串进行标记化,并通过将这些字符串还原为小写字母、删除标点符号,然后按字母顺序排序来清理它们。之后,它会找到 Levenshtein 距离并返回相似度百分比。
process.extract('7 Select', unique_brand, scorer=fuzz.token_sort_ratio)**[('7 Select', 100),
('7 Select/Nissin', 70),
('Jinbo Selection', 61),
('Seven & I', 53),
('Lele', 50)]**
这个结果意味着 7 Select/Nissin 在参考 7 Select 时有 70%的相似度。如果我将阈值设置为 70%以获得 7 Select-7 Select/Nissin 对,这还不错。
process.extract('A-Sha', unique_brand, scorer=fuzz.token_sort_ratio)**[('A-Sha', 100), ('Shan', 67), ('Nasoya', 55), ('Alhami', 55), ('Ah Lai', 55)]**
低于 70%就不是阿沙的对手了。
process.extract('Acecook', unique_brand, scorer=fuzz.token_sort_ratio)**[('Acecook', 100),
('Vina Acecook', 74),
('Yatekomo', 53),
('Sahmyook', 53),
('Panco', 50)]**
仍然擅长 70%的阈值。
process.extract("Chef Nic's Noodles", unique_brand, scorer=fuzz.token_sort_ratio)**[("Chef Nic's Noodles", 100),
("Mr. Lee's Noodles", 71),
('Fantastic Noodles', 69),
('1 To 3 Noodles', 62),
("Mom's Dry Noodle", 59)]**
现在,我们有一个问题。如果我设置 70%的阈值,令牌排序比率计分器将得到错误的一对厨师 Nic 的面条-李先生的面条。
process.extract('Chorip Dong', unique_brand, scorer=fuzz.token_sort_ratio)**[('Chorip Dong', 100),
('ChoripDong', 95),
('Hi-Myon', 56),
('Mr. Udon', 56),
('Maison de Coree', 54)]**
这个看起来足够好了。
令牌集比率
标记集比率计分器还对字符串进行标记化,并遵循与标记排序比率一样的处理步骤。然后,它收集两个字符串之间的公共标记,并执行成对比较以找到相似性百分比。
process.extract('7 Select', unique_brand, scorer=fuzz.token_set_ratio)**[('7 Select', 100),
('7 Select/Nissin', 100),
('The Ramen Rater Select', 86),
('Jinbo Selection', 61),
('Seven & I', 53)]**
由于令牌集比例更加灵活,7 Select — 7 Select/Nissin 的分数从 70%提高到了 100%。
process.extract('A-Sha', unique_brand, scorer=fuzz.token_set_ratio)**[('A-Sha', 100),
('A-Sha Dry Noodle', 100),
('Shan', 67),
('Nasoya', 55),
('Alhami', 55)]**
现在我们看到阿沙还有另外一个名字叫阿沙挂面。而我们只有通过使用令牌集比率才能看到这一点。
process.extract('Acecook', unique_brand, scorer=fuzz.token_set_ratio)**[('Acecook', 100),
('Vina Acecook', 100),
('Yatekomo', 53),
('Sahmyook', 53),
('Panco', 50)]**
这一个得到 100%就像 7 选择的情况。
process.extract("Chef Nic's Noodles", unique_brand, scorer=fuzz.token_set_ratio)**[("Chef Nic's Noodles", 100),
('S&S', 100),
('Mr. Noodles', 82),
("Mr. Lee's Noodles", 72),
('Tseng Noodles', 70)]**
当 Nic 主厨的面条 S&S 的令牌集比率返回 100%时,情况会变得更糟。
process.extract('Chorip Dong', unique_brand, scorer=fuzz.token_set_ratio)**[('Chorip Dong', 100),
('ChoripDong', 95),
('Hi-Myon', 56),
('Mr. Udon', 56),
('Maison de Coree', 54)]**
我们对这个有相同的结果。
尽管标记集比率比标记排序比率更灵活,并且可以检测到更多的相似字符串,但它也可能会带来更多的错误匹配。
应用 FuzzyWuzzy
在这一部分中,我首先使用了 token sort ratio,并创建了一个表来显示品牌名称、它们的相似性以及它们的得分。
*#Create tuples of brand names, matched brand names, and the score*
score_sort = [(x,) + i
for x in unique_brand
for i in process.extract(x, unique_brand, scorer=fuzz.token_sort_ratio)]*#Create a dataframe from the tuples*
similarity_sort = pd.DataFrame(score_sort, columns=['brand_sort','match_sort','score_sort'])
similarity_sort.head()
数据帧的第一行
因为我们在同一列中寻找匹配的值,所以一个值对会有另一个顺序相反的相同值对。例如,我们会找到一对江户包—高户,和另一对高户—江户包。为了稍后消除其中一个,我们需要为相同的对找到“代表”值。
similarity_sort['sorted_brand_sort'] = np.minimum(similarity_sort['brand_sort'], similarity_sort['match_sort'])similarity_sort.head()
基于上面的测试,我只关心那些至少有 80%相似度的配对。我也排除那些与自己匹配的(品牌价值和匹配价值完全相同)和那些重复的配对。
high_score_sort =
similarity_sort[(similarity_sort['score_sort'] >= 80) &
(similarity_sort['brand_sort'] != similarity_sort['match_sort']) &
(similarity_sort['sorted_brand_sort'] != similarity_sort['match_sort'])]high_score_sort = high_score_sort.drop('sorted_brand_sort',axis=1).copy()
现在,让我们看看结果。
high_score_sort.groupby(['brand_sort','score_sort']).agg(
{'match_sort': ', '.join}).sort_values(
['score_sort'], ascending=False)
使用令牌排序比的模糊匹配
从 95 分及以上的分数来看,一切都很好。在每一对中,这两个值可能有拼写错误、缺少一个字符或格式不一致,但总体来说,它们显然是相互引用的。低于 95 就不好说了。我们可以通过列出每一对的数据来看一些例子。
*#Souper - Super - 91%*
ramen[(ramen['Brand'] == 'Souper') | (ramen['Brand'] == 'Super')].sort_values(['Brand'])
汤和超级
对于这一对,我们看到这两个品牌来自不同的制造商/国家,他们的拉面类型或风格也没有相似之处。我会说这些品牌不一样。
*#Ped Chef - Red Chef - 88%*
ramen[(ramen['Brand'] == 'Ped Chef') | (ramen['Brand'] == 'Red Chef')].sort_values(['Brand'])
Ped 厨师和 Red 厨师
在这里,我们只有 Ped Chef 品牌的一个记录,我们也在它的品种名称中看到了与 Red Chef 品牌相同的模式。我很确定这两个牌子是一样的。
对于像这样的小数据集,我们可以通过相同的方法继续检查其他对。从 84%及以下的阈值开始,我们可以忽略一些明显不同的对,或者我们可以如上快速检查。
要应用令牌集比率,我可以只重复相同的步骤;这部分可以在我的 Github 上找到。
令牌排序比率与令牌集比率
比较结果包括姓名、使用标记排序比和标记集比的匹配以及相应的分数。
这不是完整的数据集
现在我们可以看到两个得分手之间有多么不同。不出所料,记号集比率匹配高分数的错误名字(例如 S&S,面条先生)。然而,与令牌排序比相比,它确实带来了更多的匹配(例如 7 Select/Nissin、Sugakiya Foods、Vina Acecook)。这意味着要得到最多的匹配,应该使用两个记分员。
以你的拉面知识和模糊的思维,你能挑出任何正确的搭配吗?
FuzzyWuzzy:Python 中的模糊字符串匹配,初学者指南
以及在真实世界数据集上的实践
介绍
如果您以前处理过文本数据,您会知道它的问题是最难处理的。对于文本问题,没有一个放之四海而皆准的解决方案,对于每个数据集,你必须想出新的方法来清理你的数据。在我之前的一篇文章中,我谈到了这类问题的最坏情况:
例如,考虑这种最坏的情况:您正在处理一个在美国进行的调查数据,数据集中每个观察的状态都有一个 state 列。美国有 50 个州,想象一下人们能想出的各种州名。如果数据收集者决定使用缩写:ca,Ca,CA,Caliphornia,Californa,California,calipornia,calipornia,CAL,CALI,你的问题就更大了……这样的栏目总是充满了错别字、错误和不一致。
由于数据收集过程中的自由文本,经常会出现与文本相关的问题。它们将充满错别字,不一致,无论你能说出什么。当然,最基本的问题可以使用简单的正则表达式或内置的 Python 函数来解决,但是对于上面这种经常发生的情况,您必须用更复杂的工具来武装自己。
今天的特色是fuzzywuzzy
,这是一个带有非常简单的 API 的包,可以帮助我们计算字符串相似度。
[## 通过我的推荐链接加入 Medium-BEXGBoost
获得独家访问我的所有⚡premium⚡内容和所有媒体没有限制。支持我的工作,给我买一个…
ibexorigin.medium.com](https://ibexorigin.medium.com/membership)
获得由强大的 AI-Alpha 信号选择和总结的最佳和最新的 ML 和 AI 论文:
留在循环中,不用花无数时间浏览下一个突破;我们的算法识别…
alphasignal.ai](https://alphasignal.ai/?referrer=Bex)
设置
字符串匹配是如何执行的
为了理解字符串匹配,让我们用最小编辑距离来帮助您加快速度。作为人类,如果两个或更多的字符串相似或不相似,我们没有任何问题。为了在计算机中创造这种能力,人们创造了许多算法,几乎所有的算法都依赖于最小编辑距离。
最小编辑距离(MED)是从一个字符串过渡到另一个字符串所需的最少可能的步数。MED 仅使用 4 种运算来计算:
- 插入
- 删除
- 代替
- 替换连续字符
考虑这两个词:程序和声谱图:
图片由 作者
从程序到声谱图,我们需要 3 个步骤:
- 在“Program”的开头加上字母“S”。
- 用 O 代替 P。
- 用 N 代替 R。
最小编辑距离为 3。图片由 作者 提供。
我说过,有很多算法可以计算 MED:
- 达默劳-莱文斯坦
- 莱文斯坦
- 加重平均
- Jaro 距离
此外,还有使用这些算法的软件包:nltk
、fuzzywuzzy
、textdistance
、difflib
,...
在本文中,我们将只涉及fuzzywuzzy
。
模糊不清:安装
尽管使用pip
可以轻松完成基本安装,但对于fuzzwuzzy
的安装还有一些其他选项或注意事项:
- 通过 PyPI 使用 PIP(标准):
pip install fuzzywuzzy
上述方法安装软件包的默认最新版本。起初,我用这种方法安装它。但是每当我导入它的时候,它就开始给出警告,说这个包本身很慢,我应该安装python-Levenshtein
包来加快速度。如果你像我一样讨厌 Jupyter 笔记本上的警告,下面是你安装额外依赖项的方法:
- 直接安装
python-Levenshtein
:
pip install python-Levenshtein
或者
pip install fuzzywuzzy[speedup]
对 Windows 用户的警告:如果没有安装 Microsoft Visual Studio 构建工具,安装python-Levenshtein
会失败。你可以从这里下载 MVS 构建工具。
FuzzyWuzzy:WRatio 的基础
为了从fuzzywuzzy
开始,我们首先导入fuzz
子模块:
from fuzzywuzzy import fuzz
在这个子模块中,有 5 个函数用于两个字符串之间不同的比较方法。日常使用最灵活最好的是WRatio
(加权比率)功能:
在这里,我们将“Python”与“Cython”进行比较。输出返回 0 到 100 之间的百分比,0 表示完全不相似,100 表示完全相同:
fuzzywuzzy
的所有函数都不区分大小写:
WRatio
对于不同排序的部分字符串也很好:
模糊:不同方法的比较
除了WRatio
之外,还有 4 个函数可以计算字符串相似度:
- 模糊比率
- 模糊.部分 _ 比率
- fuzz.token_sort_ratio
- fuzz.token_set_ratio
fuzz.ratio
非常适合长度和顺序相似的字符串:
对于不同长度的字符串,最好使用“fuzz.patial_ratio”:
如果字符串含义相同,但顺序不同,使用fuzz.token_sort_ratio
:
更多边缘情况,有fuzz.token_set_ratio
:
如你所见,这 5 个函数充满了警告。他们的比较完全是另一个话题,所以我给你留了一个由软件包创建者写的文章的链接,这篇文章很好地解释了他们的区别。
我想你已经看到了
WRatio
函数为fuzzywuzzy
的所有函数提供了中间基础。对于许多边缘情况和不同问题,最好使用WRatio
以获得最佳效果。
使用fuzzywuzzy.process
从选项列表中提取字符串的最佳匹配
现在我们对fuzzywuzzy
的不同功能有了一些了解,我们可以继续处理更复杂的问题。对于现实生活中的数据,大多数时候您必须从选项列表中找到与您的字符串最相似的值。考虑这个例子:
我们必须找到与Mercedez-Benz
最匹配的,用汽车的正确拼法替换它们。我们可以对每个值进行循环,但是如果有数百万个选项可供选择,这样的过程会花费很长时间。由于这个操作非常常用,fuzzywuzzy
为我们提供了一个有用的子模块:
from fuzzywuzzy import process
使用这个子模块,您可以从一系列字符串中提取与您的字符串最匹配的内容。让我们解决我们最初的问题:
process.extract
中感兴趣的参数有query
、choices
和limit
。该函数根据choices
中给出的一系列选项计算query
中给出的字符串的相似度,并返回一个元组列表。limit
控制返回的元组数量。每个元组包含两个元素,第一个是匹配字符串,第二个是相似性得分。
在引擎盖下,process.extract
使用默认的WRatio
功能。但是,根据您的情况和了解 5 个功能之间的差异,您可以使用scorer
更改评分功能:
如果你有很多选择,最好坚持使用WRatio
,因为它最灵活。
在process
模块中,有其他功能执行类似的操作。process.extractOne
仅返回一个包含最高匹配分数的字符串的输出:
真实数据集上的模糊文本清理
现在我们准备解决一个现实世界的问题。我将加载原始数据进行练习:
cars.shape(8504, 4)
我在我的一个个人项目中使用了这个数据集,任务是根据另一个文件中给出的正确值来纠正每个车辆品牌和型号的拼写:
加载 pickle 文件后,make_model
现在是一个字典,包含每个汽车品牌的正确拼写和每个关键字下的车型的正确拼写。
例如,让我们看看Toyota
汽车品牌和型号的拼法:
现在,让我们对Toyota
汽车的原始数据进行子集划分:
>>> cars[cars['vehicle_make'] == 'TOYOTA']
该数据集包含多达 100 个独特的汽车品牌,如奥迪、宾利、宝马,每一个都包含几个充满边缘案例的模型。我们不能把每一个都转换成大写或小写。我们也不知道这些是否包含任何拼写错误或不一致,视觉搜索对于这样的大数据集不是一个选项。也有一些情况下,使用多于一个单词的 make 标签用一个space
来分隔名称,而其他标签用一个dash
来分隔名称。如果有这么多的不一致,并且没有一个清晰的模式,请使用字符串匹配。
让我们从清理标签开始。为了便于比较,以下是两个数据集中的制作标签:
我认为差异是显而易见的。我们将使用process.extract
将每个品牌与正确的拼写相匹配:
如您所见,存在于make_model
中的品牌标签被转换成了正确的拼写。现在,是时候给模特贴标签了:
最后两个代码片段有点吓人。为了充分理解它们是如何工作的,你应该在
process.extract
上做一些练习。
这就对了。如果你不知道字符串匹配,这个任务就不可能完成,甚至正则表达式也不能帮助你。
如果你喜欢这篇文章,请分享并留下反馈。作为一名作家,你的支持对我来说意味着一切!
阅读更多与主题相关的文章:
用 Pandas 清理分类数据
towardsdatascience.com](/master-the-most-hated-task-in-ds-ml-3b9779276d7c) [## 认识熊猫最难的功能,第一部分
掌握 pivot_table()、stack()、unstack()的时机和方式
towardsdatascience.com](/meet-the-hardest-functions-of-pandas-part-i-7d1f74597e92)
牛虻. JL——来自你梦中的纯朱莉娅绘图库
看看朱莉娅对 2020 年统计绘图的最佳选择
(图片由作者提供)
介绍
如果您使用 Julia 的时间很短,您可能会熟悉一个名为 Plots.jl 的绘图库。Plots.jl 是一个简单的库,它通过 Plot()和 scatter()等简单的函数方法为 Plot.ly 和 Matplotlib 等 Python 库提供了端口。虽然这无疑是创建一个全新的用于 Julia 编程语言的库的好方法,但它也存在一些问题。
首先也是最重要的,用 Julia 解释 Python 会产生很多性能问题。从只是简单地导入 Plots.jl 到实际绘制值,Plots.jl 的性能绝对糟糕透顶!除了不是 Julian 之外,很多软件在 Julia 版本中是缺失的,并且很多特性完全被忽略了。虽然 GR 可能是 Plots.jl 最有前途的后端,但它缺少 Pythonic 库可能提供的许多优秀特性。尽管如此,我确实有一篇文章,你可以看看,以提高你的图形 GR 可视化技能。
没错,又是 Julia,我保证 R,Scala,Python 还在这个爱情圈里
towardsdatascience.com](/spruce-up-your-gr-visualizations-in-julia-88a964450a7)
因此,有了 Plots.jl 的这些严重的基本缺陷,很容易理解为什么科学家们在别处寻找 Julia 语言中的伟大的统计可视化。我看到的最好的选择之一是一个叫做
牛虻. jl。
首先,需要注意的是,牛虻与我为其创建的另一个名为 Hone.jl 的库非常相似。这两个库都使用相同的后端 Compose.jl 来在 Julia 中绘制矢量图形。相似之处可能不止于此,但它们之间也有很多关键的差异,例如 Hone 的面向对象和模块化方法与牛虻的根本不一致。尽管如此,牛虻本身确实采用了一种非常有趣的方法,并且与其他绘图库的工作方式非常不同,您可能会从中得出一些相似之处。
为什么用牛虻?
有了这些替代品,而且通常更受 Julia 用户的欢迎,你为什么还要使用牛虻呢?首先,牛虻是朱利安。这当然意味着它不仅速度快,而且非常适合 Julia 语言——这一点对于它的 Plots.jl 对应物来说就不一样了。对我来说,这是一个巨大的优势,因为你不仅不需要处理 Plots.jl 声称的性能问题,而且还变得相对简单。
牛虻的另一个伟大之处是它的开箱即用的交互性。情节本质上是互动的,可以与 Plot.ly 相提并论。这是一个在朱莉娅语言中很少见的概念,所以拥有它对牛虻来说是大胆而独特的。
牛虻简介
牛虻. jl 表面上遵循了大多数 Julia 用户熟悉的简单方法论。我们可以使用 plot()方法创建一个简单的绘图,带有两个关键字参数,X 和 y。
using Gadfly
data = Dict([:X => [5,10,32,31,51,43], :Y => [82, 33, 21, 26, 11, 22]])plot(x=data[:X], y=data[:Y])
(图片由作者提供)
这是事情变得有点不同的地方。在牛虻中,有一个名为“Geom.jl”的导出模块。与其使用单独的方法来创建线图,我们可以简单地使用相同的方法,只是从该模块添加几何图形。
plot(x=rand(10), y=rand(10), Geom.point, Geom.line)
(图片由作者提供)
同样,我们可以添加一个颜色关键字参数来改变几何图形的颜色。
plot(x=data[:X], y=data[:Y], color=data[:Y])
(图片由作者提供)
此外,我们可以使用 Geom.jl 模块中的更多几何图形,例如直方图:
data = Dict(:Location => ["Africa", "Europe", "Africa", "Americas", "Asia", "Europe"],
:Income => [29000, 83000, 56000, 76000, 52000, 46000])plot(x=data[:Income], color=data[:Location], Geom.histogram)
(图片由作者提供)
您还可以使用牛虻快速绘制语法结果,这样可以快速得到如下结果:
plot([sin, cos], 0, 25)
(图片由作者提供)
结论
jl 是一个非常棒的绘图库,它采用了一种有趣的方法来解决我们以前见过的用其他方法解决的问题。在我个人看来,该模块采用的方法使它比许多竞争对手更好用,也更容易使用。最重要的是,它当然是用纯 Julia 编写的,这给了它速度和易用性,这是该语言中类似包无法比拟的。
总的来说,我想说如果你正在用 Julia 语言处理大量的数据,那么牛虻绝对值得一个 Pkg.add()。它将为您提供查看数据的方法,这将大大提高您对 Plots.jl 等工具的理解和分析能力。虽然我肯定会说 Plots.jl 仍然可能有它的位置,而且肯定有它的用途,但我也想暗示牛虻作为朱莉娅梦想的绘图库完全超过了它。
乳腺癌风险的盖尔模型
乳腺癌是全世界女性中诊断最多的癌症,每年有超过 200 万新病例。患者的治疗通常会给妇女及其家人带来巨大的压力。这种疾病的存活率在世界范围内各不相同。晚期和转移性乳腺癌目前无法治愈,而早期诊断的局部癌症有 99%的存活率。因此,早期发现仍然是降低乳腺癌发病率和死亡率的最佳途径。
来源:http://mammalive . net/research/global-breast-cancer-incidence-2018/
成功预防乳腺癌的第一步是了解患者的风险。网上有很多工具,但并不是所有的工具都有高质量的研究支持。在这篇文章中,我展示了 Gail 模型的 Python 实现,该模型允许妇女估计她们在特定时期内患浸润性乳腺癌的风险。
盖尔模型基于乳腺癌筛查研究的数据,该研究涉及超过 280,000 名不同年龄和种族的女性。该工具根据以下个人和家庭信息评估患者的风险:
- 年龄
- 月经开始时的年龄
- 第一次活产婴儿的年龄
- 患乳腺癌的一级亲属人数
- 先前乳房活检的数量
- 活检中存在非典型增生
盖尔模型已经在大规模人群中进行了测试,并已被证明可以提供乳腺癌风险的准确估计,特别是在白人女性中。它也被美国国家乳腺癌研究所以及其他知名机构和医院使用。该模型的源代码可以从癌症流行病学和遗传学分部网站获得。然而,它们只提供了 R 和 SAS 宏实现,这对于所有 Python 爱好者来说都是一个遗憾。
因此,我把最新版本的 BRCA 包翻译成了 Python。这一更新版本包括一个基于旧金山湾区乳腺癌研究(SFBCS)的新的西班牙裔模型。据我所知,这是 Gail 模型扩展版本的第一个 Python 实现。这个包的代码可以在这个库中找到。
该软件包包含几个函数,用于计算乳腺癌的相对和绝对风险。这个包里的主要功能是absolute_risk
。它使用患者数据来预测特定时间段内患乳腺癌的概率。它还可以用来预测平均人群风险,并将其与患者的结果进行比较。recode_check
将输入数据重新编码为适合absolute_risk
的格式。relative_risk
功能估计风险因素组合的相对风险。它将年龄分为两组:50 岁以上和 50 岁以下。最后,risk_summary
创建一个包含所有相对风险和绝对风险的数据框架。其输出可用于进一步分析。
在工作中可以随意使用这个包!请注意,对软件包的任何更改/修改都将由您自己承担风险。我确保当前版本产生与官方 Python 实现相同的结果。如果您有任何问题,请查看 BRCA 官方文档或给我发邮件!
页(page 的缩写)虽然一个人的风险可能很高,但这并不一定意味着他们会患乳腺癌。一些没有患乳腺癌的女性比一些患乳腺癌的女性有更高的风险评估。因此,所有女性在 45 岁以后都必须每年进行一次乳房 x 光检查。保持健康!
获得对你的模型的信任,用石灰和 SHAP 做出解释
模型可解释性
使用机器学习来自动化流程在全球许多组织中被广泛采用。作为戴尔数据科学工厂团队的一员,我们与不同的业务部门合作,提供基于数据的创新解决方案来优化现有流程。
在我们与定价业务部门的一次合作中,我们专注于改进导致许多延迟的手动密集型流程。手动流程包括审计销售代表提交的交易(其中一些产品的价格低于某一点),并根据交易的特点决定是批准还是拒绝交易(您可以在此处阅读更多信息)。如果交易被拒绝,定价分析师应向销售代表提供解释。提供解释可以帮助销售代表调整交易,使其获得批准,这对双方都有利。
由于这非常耗时,并可能导致失去机会,我们希望通过减少需要手动审查的交易量来优化它。为了实现这一点,我们使用机器学习来自动批准和自动拒绝较简单的案例,这使得分析师可以专注于更复杂的交易,从而产生更多价值。
为了保持相同的质量水平,并在用户之间建立信任(这在实现机器学习模型时非常关键),我们必须了解决策是如何做出的。考虑到这一点,我寻找现有的工具来支持我们正在进行的转型,我遇到了莱姆和 SHAP。在整个过程中,我检查了两种方法的数据,以了解哪种方法更适合我,我获得了很多知识,我想分享。在这篇博文中,我将分享我所学到的东西,并分享一些帮助我更好地理解每个工具如何工作的细节。
这篇博文将涵盖以下内容:
1.SHAP 和莱姆-背景
2.例子
3.摘要
4.进一步阅读
1.SHAP 和莱姆-背景
LIME(局部可解释模型不可知解释)提供局部解释;它可以帮助解释为什么单个数据点被归类为特定的类。LIME 将模型视为黑盒;它不区分随机森林、决策树或神经网络。它使用线性模型来提供局部解释。围绕 LIME 的主要概念是,即使线性模型不能很好地解释复杂的数据/模型,但当试图解释局部观察时,它可以提供足够的解释。下面你可以看到一个解释石灰直觉的图。这是摘自论文:“我为什么要相信你?”解释任何分类器的预测。
LIME 提供了三种不同的解释器:表格解释器(将重点介绍)、图像解释器和文本解释器来处理不同类型的数据。
从上面提到的论文中,有一个例子证明了关于石灰的直觉
SHAP(SHapley Additive explaints)旨在通过计算每个特征对预测的贡献来解释单个数据点的预测。生成的解释是基于从联盟博弈论中计算 Shapley 值(你可以在这里阅读更多信息)。它基于下面的场景:假设你作为团队的一员玩某个游戏,团队赢了,结果赚了一些钱(支出)。在玩家之间分配奖金的最佳方式是什么,以最好地反映每个玩家对游戏的贡献?在我们的案例中,我们希望了解如何在模型中的不同特征(玩家)之间划分预测(支出),以最好地反映每个特征所做的贡献。
一般来说,要计算某个特征对预测的贡献,应该获取不包含该特征的所有子集,并计算添加该特征前后的预测之间的差异,然后对不同的子集进行平均。
所描述的过程可能计算量很大,而且运行一个没有某个特征的模型会创建一个全新的模型,这不是我们想要解释/理解的(你可以在 Adi Watzman 的这个精彩讲座中听到更多)。幸运的是,SHAP 使用不同的近似和优化方法克服了这些困难。
SHAP 提供了三种不同的解释器:KernalSHAP,它与 LIME 类似,是模型不可知的,TreeSHAP 是针对基于树的模型优化的,DeepSHAP 是深度学习模型中 SHAP 值的高速近似算法。
在这篇文章中,我将重点介绍树解释器,因为我们在项目中使用了基于树的模型。
一个展示 SHAP 输入和输出的例子,摘自 SHAP 的 GitHub 页面
2.例子
继续看一些基于我们使用案例的例子,使用石灰和 SHAP。
石灰
为了提供一个解释,首先我们必须创建一个解释器。解释者应该得到以下信息:
- 我们应该指定我们是在处理回归模型还是分类模型。
- 我们应该传递我们的训练数据、特性名和类名(这是可选的,默认为 0 和 1)。
- Verbose-表示我们是否希望在解释中提供更多的细节。
- 因为在这个过程中有一些随机性,为了能够重现结果,您还应该设置 random_state 变量。
from lime.lime_tabular import LimeTabularExplainerlime_explainer = LimeTabularExplainer(train_data.values,
mode = ’classification’, feature_names = new_feature_names, class_names = [‘Deny’, ’Approve’], verbose=True,
random_state = 42)
关于分类特征的补充说明 -如果你的模型包含分类变量,事情会变得复杂一些。这里有一个简短的解释:为了提供解释,LIME 使用原始数据集对我们想要解释的观察周围的数据点进行采样。如果我们提供的数据中的分类变量采用统一的编码格式,那么采样过程可能会创建不太可能出现在数据中的样本,并且生成的解释不会代表实际数据。为了克服这一点,分类特征应该被转换成整数标签,为了了解更多关于如何使用带有分类特征的 LIME,我鼓励你观看凯文·勒芒恩的演讲,或者访问他的 GitHub 页面。
解释器设置好之后,让我们生成一个解释。要获得解释,您应该提供以下信息:
- 你想解释的例子。
- 生成概率的函数(在分类模型的情况下),或者预测数据集的函数(在回归模型的情况下)(在我们的情况下是 predict_proba)。
- 您还可以指定用于构建局部线性模型的最大要素数,目前我们使用默认值 10。
exp = lime_explainer.explain_instance(instance, trained_model.predict_proba)exp.show_in_notebook(show_table=True)
接下来,让我们检查输出:
作者图片
在顶部,呈现了由 LIME 创建的线性模型的截距,随后是由线性模型生成的局部预测,以及来自我们的模型的实际预测(这是将 explainer 中的 verbose 设置为 True 的结果)。如您所见,线性模型生成的预测和我们的模型生成的结果非常接近。接下来,您可以看到每个类别的概率,就像原始模型预测的那样。
作者图片
在最右边,您可以看到特定观察的每个特性的值。这些特征根据其所属的类别进行颜色编码;橙色的特征促成了交易的批准,而蓝色的特征促成了交易的拒绝。此外,它们按对预测的影响进行排序,影响最大的要素位于顶部。在中间的图表中,您还可以看到每个类别中每个特征的大小。理解这一点也很重要,为了提供更直观的解释,数值被离散化成几组,这就是为什么在中间的图表中,特征沿着一定的范围提供。这是 LIME 中的默认值,可以通过将变量 dicretize_continuous 设置为 false 来更改。
作者图片
您可以通过运行以下代码获得有关生成的本地模型的更多信息:
print(‘R²: ‘+str(exp.score))
作者图片
这会给我们本地模型的 R 平方,我们可以用分数来决定我们是否可以相信某个解释。
此外,您可以使用以下代码访问模型中每个特性的权重:
exp.local_exp
作者图片
对权重和截距求和将导致由 LIME 生成的局部预测。
exp.intercept[1] + sum([weight[1] for weight in exp.local_exp[1]])
作者图片
SHAP
转到 TreeSHAP,让我们为我们用于 LIME 的同一个实例生成一个解释。
首先,与 LIME 类似,我们应该导入 SHAP,创建解释器并传入训练好的模型:
import shap
shap.initjs() #This is for us to be able to see the visualizations
explainer = shap.TreeExplainer(trained_model)
接下来,让我们生成一个解释并将其可视化。第一行计算我们提供的实例的 SHAP 值,下一行生成一个图,该图将显示每个特征对最终预测的贡献。SHAP 所做的计算是针对两个类别进行的,这里您应该使用索引选择您想要引用的类别(在回归模型中不需要),在这种情况下,我想探究批准类别(类别 1)的结果。
为了计算我们想要解释的实例的 SHAP 值,您应该提供实例本身,并且为了生成绘图,您应该提供以下内容:
- 解释器生成的期望值,本质上是训练集预测的平均值,也称为基值。
- 接下来,您应该提供在前一行中计算的 SHAP 值(这些是每个要素对模型预测的贡献)。
- 您要解释的实例和功能名称。
shap_values = explainer.shap_values(instance)shap.force_plot(base_value=explainer.expected_value[1], shap_values[1], features=instance, feature_names=new_feature_names)
下图中的粗体数字是我们的预测。在左侧,您可以看到基值,如前所述,它是我们训练集中预测的平均值。顾名思义,这是我们预测的起始值,将模型中各要素的贡献相加,将得到我们的最终预测。
作者图片
特征以这样的方式排列,使得促成交易批准(类别 1)的所有特征在左边,并且用红色着色,并且促成交易拒绝(类别 0)的所有特征在右边,用蓝色着色。请注意,每一侧的要素都是按大小排序的,这在每个要素所占的空间中也很明显。您可能还会注意到,在图表的边缘,有一些附加要素也对不同的类有所贡献,但它们的贡献远远小于所显示的那些,您可以将鼠标悬停在图中的边缘,您将能够看到这些要素的名称及其值。
TreeSHAP 利用树的结构,可以计算精确值而不是近似值。将预期/基础值相加将生成准确的预测,如下所示。
explainer.expected_value[1] + sum(shap_values[1])
作者图片
SHAP 还提供了不同的可视化效果,可以根据您选择的类为您提供每个功能行为的总体概述。为此,首先您应该计算整个测试数据的 SHAP 值(注意这可能需要一些时间),让我们看一个例子:
test_shap_values = explainer.shap_values(test_data.values)
shap.summary_plot(shap_values[1], test_data.values)
作者图片
让我们检查图表的不同方面:
- x 轴代表对预测的贡献,这些是 SHAP 值。
- 左侧的 y 轴显示影响最大的特征(您可以指定想要显示的特征数量-默认值为 20)。这些特征按其对预测的影响进行排序,其中影响最大的位于顶部。
- 右侧的 y 轴表示如何解释每个特征的原始值。在每一行中,数据点用蓝色或红色着色,蓝色表示该特征的低值,红色表示该特征的高值。
- 该图由一条水平线分隔,在图的右侧,您可以看到促成交易批准的数据点,在图的左侧,您可以看到促成交易否决的数据点。
- 例如,特征 x_29 中的低值将有助于批准类,而高值将有助于拒绝类。
这可以帮助您了解功能是否像 SME(主题专家)期望的那样运行,并且可以帮助我们找到以前没有注意到的与数据相关的问题。
在 SHAP 还有其他的全球可视化选项,查看作者的 GitHub 页面可以看到不同的选项。
3.摘要
在这篇博文中,我与你分享了我使用工具来解释 ML 模型的动机,我们回顾了莱姆和 SHAP 的基础知识,并给出了基于我们用例的例子。
回顾我们所做的工作,我觉得这些工具的使用在我们的业务合作伙伴之间产生了信任,这使得我们构建的模型的实施更加顺利。此外,使用这些工具确实帮助我们发现了一些与数据相关的问题,并最终得以解决。
莱姆和 SHAP 都是很好的工具,你可以用来建立信任,也可以用来解释你所做的某个决定。当我们在相同的情况下检查两者时,我们注意到大多数影响变量在石灰和 SHAP 中几乎是相同的。但是,两者之间的顺序有点不同。
LIME 的主要优点是速度更快(至少对于表格数据来说是这样),而且它的工作方式非常直观。然而,生成的解释有时可能不可靠,为了使用它,需要做一些预处理。SHAP 易于使用,还可以提供要素的总体视图,以及要素值对模型预测的影响。此外,TreeSHAP 利用了树的结构,并计算精确的值,而不是近似值,但是在向业务合作伙伴解释时不太直观。
如果你想了解更多关于这两个工具的知识,我鼓励你看看这篇文章中嵌入的链接,以及下面的链接。
4.进一步阅读
以下是一些额外的阅读推荐:
- 用 SHAP 价值观解释你的模型——这是一篇很棒的博客文章,用一个数字例子来解释 SHAP。
- 一个让黑盒模型变得可解释的指南 -提供了可解释模型、石灰、SHAP 等等的全面补偿!
- 本地可解释的模型不可知解释(LIME):介绍——LIME 作者写的博客文章。
特别感谢 Or Herman-Saffar 和 Idan Richman-Goshen 对这篇博文的评论和宝贵反馈。
从 16 行代码中获得神经网络的直觉
艾莉娜·格鲁布尼亚克在 Unsplash 上的照片
神经网络相当令人困惑,尤其是当你从头开始编程的时候。本文将通过创建和训练一个单变量回归模型(在输入和输出之间有一个突触),让您直观地了解神经网络工作所需的相关概念。
神经网络是如何工作的?
假设你是一名医生,你希望找到身高和体重之间的定量关系:也就是说,你希望能够可靠地预测某人的体重,给定他们的身高。
到目前为止,你有 5 个人的身高和体重数据。这是在笛卡尔平面上绘制数据时的图表:
作者图片
当我们看这个图表时,我们可以想象出一条线来最好地解释这些数据,但是我们想对此进行量化。这就是使用神经网络的地方,或者在这种情况下,我们的单变量回归模型。
神经网络保存一个奇异变量α,当乘以给定的身高时,给出预测的体重。让我们随机生成α,并将其绘制在我们的图表上:
我们可以看到这条线没有很好地包含数据。它对数据的量化有多糟糕?测量蓝点和这条线之间的距离,并将它们相加。平方这个值。为什么值是平方?两个原因:
- 为了防止负数不受惩罚
距离较远的负数(线下的点)仍然是不准确的预测。当我们平方它们时,它们变成正数
- 惩罚更大的距离
100–3 = 97,但是 100–3 = 9991。距离越大,网络受到的惩罚就越多。
这是均方误差损失函数,衡量网络预测数据的好坏。这个值越大,模型越差。
神经网络的目标是减少这种损失函数,从而使预测更加准确。我们如何做到这一点?我们不能改变数据,我们唯一能改变的值是α。
现在,我们不太确定α的最佳值是多少,所以我们测试了不同的α值,记录了损失值,并绘制在图表上。因为我们想找到α的哪个值使损失值最低,所以需要找到最低点。在这种情况下,介于 0 和 0.5 之间。
我们如何让计算机来做这件事?我们发现了一种叫做导数的东西。导数是图形在某一点的陡度。也是图形的梯度。我们要计算的是损失对α的导数,也就是说,当给定点 a 时,图有多陡。
但是我们不能把这两个变量直接联系起来,因为它们之间没有直接关系。我们用高度值乘以α,然后用它来计算损失。
为了计算α和损失之间的导数,我们必须计算损失和预测值之间的导数,然后计算预测值和α之间的导数。最后,我们将它们相乘,找出损失值与α之间的导数。
损失值和预测值(高度* α)之间的导数是 2 *(真实-预测),因为 x 的导数是 2x。预测值和 alpha 之间的导数就是数据,因为 alpha 要乘以数据。
为了简单起见,不使用列表,我将体重身高的例子改为只预测值 2,作为 alpha。代码如下:
import numpy as np
x = 2
y = x*2alpha = np.random.randn()for i in range(100):
pred = alpha*x
loss = np.square((y-pred))dloss_dpred = 2*(y-pred)
dpred_dalpha = x
dloss_dalpha = dloss_dpred * dpred_dalphaalpha += dloss_dalpha*0.1
alpha
第 1 行:Python 中的一个有用工具。在这种情况下,它用于生成随机值。
第 2–3 行:网络试图计算出 x 乘以多少得到 y。它应该趋向于 2。
第 5 行:这是变量 alpha 的随机生成。
第 7–12 行:这段代码计算导数并将它们相乘。
第 15 行:Alpha 向正确的方向变化,一点一点,直到趋于最佳值。
这个程序应该输出 2 作为结果。
直观了解精确度、召回率和曲线下面积
理解精确度和召回率的友好方法
在这篇文章中,我们将首先解释精确和召回的概念。我们将尝试不只是扔给你一个公式,而是使用一个更直观的方法。通过这种方式,我们希望对这两个概念有一个更直观的理解,并提供一个很好的记忆技巧,让你再也不会忘记它们。我们将以对精确回忆曲线的解释和曲线下面积的含义来结束这篇文章。这篇文章是为初学者和高级机器学习实践者准备的,他们想更新他们的理解。在这篇文章的最后,你应该对什么是精确和召回有一个清晰的理解。
我们将从二元分类任务的情况开始解释。二进制意味着每个样本只能属于两个类别,例如狗和猫。我们将用 0 标记属于第一类(猫)的样品,用 1 标记属于第二类(狗)的样品。
从https://www.pexels.com/获得的图像
在分类问题中,神经网络是在标记数据上训练的。这意味着来自训练、验证和测试集的每个样本都由人来标记。由人类赋予的标签被称为地面真相。
在分类任务中,数据成对提供:
- 输入数据
- 地面真实标签
输入数据不一定是图像,也可以是文本、声音、坐标等。
输入数据接下来被馈送到神经网络,并且我们获得样本属于哪个类别的预测。请注意,精确召回曲线只能为输出概率(也称为置信度)的神经网络类型(或更一般的分类器)计算。在二元分类任务中,输出样本属于类别 1 的概率就足够了。让我们称这个概率为 p 。样本属于 0 类的概率正好是:1 - p 。
因此,如果神经网络输出 p =0.99,则相当有把握,馈送的输入数据属于类别 1,即输入数据是狗的图像。如果网络输出 p =0.01,则可以确信输入数据不属于第 1 类。这相当于美联储输入数据属于概率 1 - p = 0.99 的 0 类。
为了获得给定样本的具体类别预测,我们通常使用阈值 0.5 来设定神经网络输出的阈值。这意味着高于 0.5 的值被认为属于类别 1,而低于 0.5 的值表示类别 0。
有了所有的定义,我们接下来可以定义真阳性、假阳性、真阴性和假阴性的概念。
真阳性、假阳性等。
现在基于神经网络的输出,我们得到以下四种情况:
情况 1 :真阳性
输入样本属于类别 1,并且神经网络正确地输出 1。
由于历史原因,这被称为真正的积极。记住:正= 1。
案例二:假阳性
输入样本属于类 0。然而,神经网络错误地输出 1。
情况 3 :真阴性
输入样本属于类别 0,并且神经网络正确地输出 0。
同样,由于历史原因,这被称为真正的否定。记住:负= 0。
情况 4 :假阴性
输入样本属于类别 1。然而,神经网络错误地输出 0。
我们将数据集中的真阳性数表示为 TP,假阳性数表示为 FP,依此类推。在继续阅读之前,请真正熟悉 TP、FP、TN 和 FN 的概念。
接下来,我们将定义精度的概念。
精确
我们有以下由 10 个样本组成的数据集:
从https://www.pexels.com/获得的图像
请注意,所选的数据集是不平衡的,即与具有 7 个实例的猫类相比,狗类仅具有 3 个实例。在处理真实数据集时,几乎总是会遇到数据不平衡的问题。相比之下,像 CIFAR-10 的 MNIST 这样的玩具数据集具有均等的类分布。精确度和召回率作为评估神经网络在不平衡数据集上的性能的度量特别有用。
我们将上述每个输入数据输入神经网络,并获得每个样本的概率/置信度:
接下来,我们根据预测的置信度对表格进行排序。我们将实际阳性样品涂上绿色,将实际阴性样品涂上红色。相同的颜色方案应用于正面和负面预测。我们再次假设阈值为 0.5。
阈值 0.5 →精度= 3/6 = 0.5
正面预测可以分成一组 TP 和一组 FP。
我们将精度定义为以下比率:
因此,精度衡量你的正面预测有多准确,也就是说,你的正面预测正确的百分比。请注意,总和 TP+FP 对应于被分类器预测为阳性的样本总数。
记住精度的定义是一件困难的事情,在几周内记住它就更难了。对我帮助很大的是记住了上面的彩色表格。请记住,精度是第一行上方箭头的长度(实际值)除以第二行下方箭头的长度(预测值),并且记住绿色代表 1(或正值)。
请注意,在精度的定义中没有使用 FN。
为了更深入地了解精确度的概念,让我们改变定义样本被分类为阳性还是阴性的阈值。
如果我们将阈值降低到 0.3,我们会得到 0.375 的精度。记住,阈值为 0.5 时,精度为 0.5。
阈值 0.3 →精度= 3/8 = 0.375
如果我们将阈值进一步降低到 0.0,我们会得到 0.3 的精度。重要提示:这对应于我们数据集中狗的比例,因为 10 个样本中有 3 个属于狗类。
我们观察到,降低阈值也会降低精度。
接下来,我们将阈值增加到 0.7,并获得 0.75 的精度。
阈值 0.7 →精度= 3/4 = 0.75
最后但同样重要的是,我们将阈值增加到 0.9,并获得 1.0 的精度。请注意,在这种情况下,我们没有任何误报。我们得到一个假阴性,如上所述,在精度计算中不考虑这个假阴性。
阈值 0.9 →精度= 2/2 = 1.0
收集所有的结果,我们得到下面的图表:
我们看到精度在 0 和 1 之间。当阈值增加时,它增加。我们还注意到,只要阈值足够大,精度可以变得任意好。因此,精度不能单独用来评估分类器的性能。我们需要第二个指标:召回率。
回忆
我们有以下由 10 个样本组成的数据集:
从https://www.pexels.com/获得的图像
请注意,为了解释清楚,我们使用了不同于以前的数据集。dog 类现在有 7 个实例,而 cat 类只有 3 个实例。
我们将上述每个输入数据输入神经网络,并获得以下置信度:
我们再次根据获得的置信度值对表格进行排序。我们使用与之前相同的颜色方案,并再次假设阈值为 0.5。
阈值 0.5 →回忆= 5/7 = 0.714
这一次,我们将实际阳性分为一组 TP 和一组 FN,并将召回率定义为以下比率:
因此,召回衡量的是你发现所有实际阳性样本有多好,即实际阳性样本被正确分类的百分比。请注意,总和 TP+FN 对应于数据集中实际阳性样本的总数。
为了记住回忆的定义,你可以再次使用上面的彩色表格。回忆是第二行下面箭头的长度(预测)除以第一行上面箭头的长度(实际),记住绿色代表 1(或正)。
为了更深入地理解回忆的概念,让我们再次改变定义样本被分类为阳性还是阴性的阈值。
如果我们将阈值降低到 0.3,我们将得到 1.0 的召回。请记住,对于 0.5 的阈值,召回率为 0.714。与精确度相反,当阈值降低时,回忆似乎增加。
阈值 0.3 →回忆= 7/7 = 1.0
请注意,在上述情况下,我们没有任何假阴性。我们得到一个误报,如上所述,在召回的计算中不考虑它。如果我们将阈值进一步降低到 0.0,我们仍然得到 1.0 的召回。这是因为对于阈值 0.3,所有实际阳性都被预测为阳性。我们还注意到,只要阈值足够小,回忆可以是任意好的。这与 precision 的行为是相反的,也是这两个指标如此好地一起工作的原因。
接下来,我们将阈值提高到 0.7,得到 0.571 的召回率。
阈值 0.7 →回忆= 4/7 = 0.571
最后但同样重要的是,我们将阈值提高到 0.9,并获得 0.285 的召回率。
阈值 0.9 →回忆= 2/7 = 0.285
收集所有的结果,我们得到下面的图表:
我们看到召回率在 0 和 1 之间。当阈值增加时,它减小。我们还注意到,只要阈值足够小,回忆可以是任意好的。
接下来,我们将结合精度和召回率,得出精度-召回率曲线(PR-curve)。
精确回忆曲线
对于 0 和 1 之间的所有阈值,通过在 y 轴上绘制精度,在 x 轴上绘制召回,获得精度-召回曲线。典型的(理想化的)精确召回曲线如下图所示:
我们已经看到,对于非常高的阈值(高意味着略小于 1.0),精度非常高,召回率非常低。这由图表左上角的红点表示。
对于非常低的阈值(略大于 0.0),我们已经表明,召回率几乎为 1.0,精度与数据集中阳性样本的比率相同。).图中右下角的红点显示了这一点。
完美分类器的精度-召回曲线如下所示:
一个非常差的分类器(随机分类器)的精确召回曲线看起来像这样:
有了这些知识,你现在应该能够判断一个任意的精确召回曲线属于一个好的还是一个坏的二元分类器。请注意,要正确解释精密度-召回率曲线,您还需要知道阳性样本与所有样本的比率。
1.1 精确召回曲线下面积(PR-AUC)
最后,我们得出度量 PR-AUC 的定义。PR-AUC 的一般定义是找到精确度-召回曲线下的面积:
如下图所示:
因此,PR-AUC 将精度-召回曲线总结为单个分数,并且可以用于容易地比较不同的二元神经网络模型。请注意,完美分类器的 PR-AUC 值总计为 1.0。随机分类器的 PR-AUC 值等于数据集中阳性样品与所有样品的比率。
在我们的下一篇文章中,我们将展示如何使用 Python 轻松实现 PR-AUC。
找到甘了吗?保持可解释性的灵活建模方法
在许多社会科学和商业问题中,解释现象发生的原因往往比提高模型对事件发生的可预测性更重要。因此,拥有一个可解释的模型对于理解不同因素如何与感兴趣的结果相互作用是至关重要的。
该模型的可解释性在高度监管的业务环境中也很重要,例如贷款审批决策。即使在预测准确性比“为什么”更重要的情况下,可解释的模型也可以帮助调试更复杂的模型,并指导特征工程和数据预处理的新方法。
游戏——引擎盖下
在这种情况下,广义加法模型(GAM)在简单模型(如我们用线性回归拟合的模型)和更复杂的机器学习模型(如神经网络)之间提供了一个中间地带,神经网络通常承诺比简单模型具有更好的预测性能。GAM 还可以用于各种任务:回归、分类、二元选择。
在线性回归中,我们将结果 y 建模为两个输入 X₁和 X₂的函数,如下所示:
y = β₁X₁ + β₂ X₂ + u
在 GAM 中,β Xᵢ被 f(Xᵢ代替,其中 f()可以是任意非线性函数。换句话说,GAM 由输入上的光滑函数 f()的和组成。想法仍然是,每个输入特征对响应做出单独的贡献,这些只是相加,但是这些贡献不必与输入严格成比例。这种方法的美妙之处在于,类似于线性回归中的β,部分响应函数 f()仍然捕捉结果 y 的变化以改变输入。预测的变化取决于 Xᵢ.的初始值
处理回归模型中非线性关系的一种常用方法是创建多项式要素。对于预测因子 Xᵢ,我们加入了二次项(Xᵢ)、三次项(Xᵢ)等,以获得更好的拟合。GAM 包含了这个想法,但是还包含了一个额外的方面:惩罚估计。这个想法类似于脊或套索回归,其中添加了惩罚项以帮助避免过度拟合。
创建变量变换的另一种方法是将变量切割成不同的区域,并分别适合这些区域。然而,不同的拟合可能是不相关的,导致有时对相近值的预测明显不同。GAM 允许用户在变量中指定knots
的数量,用于创建分段,在每个分段上分别拟合三次多项式,然后连接起来创建一条连续曲线。
那么应该用什么平滑函数呢?
在 R 中的 mgcv 包中有很多 smooth,可以通过 smooth.terms ( 链接)的帮助文件了解更多。默认的是薄板回归样条(TPRS),它在一般情况下性能很好。三次样条也是一种常见的基础,反映了为协变量添加多项式项。
在 R 中实现
我们使用“r”中的mgcv
包来实现 gam,并使用来自包[catdata](https://cran.r-project.org/web/packages/catdata/catdata.pdf)
的内置数据集medcare
来说明 gam,并使用逻辑回归与性能进行比较。
medcare
数据收集自 4406 名年龄在 66 岁及以上的个人,他们都有公共保险计划。结果变量是healthpoor
,这是一个二元变量,如果个人报告健康状况不佳,则等于 1,否则等于 0。我们很想知道ofp
,医生诊所就诊的次数,可能与健康结果有什么关联。人们可以假设这两个变量之间的非线性关系:一个健康的人去看医生的次数很少,但是看更多的医生也可以让病人变得更健康。所以我们对更灵活的建模方法感兴趣。
mgcv 将gam
视为glm
的一般化版本,因此可以直接调用gam
方法来使用glm
方法,例如线性和逻辑回归。
library(catdata)
library(mgcv)
library(Metrics)
library(ggplot2)
library(visreg)data(medcare)#### logit model (same as running glm with family set to "binomial")
lm <- gam(healthpoor ~ male+ age + ofp, family = binomial, data = medcare)
pred <- predict(lm, type="response")
lm_auc <- Metrics::auc(medcare$healthpoor, pred)
lm_auc#### generalized additive model
gam <- gam(healthpoor ~ male+s(age)+s(ofp), family = binomial, data = medcare)
pred <- predict(gam, type="response")
gam_auc <- Metrics::auc(medcare$healthpoor, pred)
gam_auc
gam 公式中的 s() 项表示要平滑的项。有几个选项可以传递给 s()术语—例如,您可以指定不同的平滑函数bs
和不同的结数k
。
如果唯一值的数量小于基数的数量(例如,当您试图将s()
添加到类似male
的二进制变量时),该函数将返回以下错误:
一个项的唯一协变量组合比指定的最大自由度少。
使用visreg
来可视化ofp
的拟合项,我们可以看到gam
产生了对ofp
更加细致入微的预测,而逻辑回归的预测更加线性。
visreg(gam, "ofp", jitter=TRUE, line=list(col="red"),
fill=list(col="green"))
gam
模型也产生了略好的 AUC,0.679,相比之下,逻辑回归模型的 AUC 为 0.673。
基于 ofp 的 I 预测:gam(左),logistic 回归(右)。作者法师。
解释
我们最终感兴趣的是估计ofp
在healthpoor
概率上的变化。为此,我们需要将其他特性的值固定为它们的平均值或模式(如果该特性是一个分类变量)。然后,我们创建一个测试数据帧,其中包含一系列可能的值ofp
。
# function to get mode of an array of values
getmode <- function(v) {
uniqv <- unique(v)
uniqv[which.max(tabulate(match(v, uniqv)))]
}testdata = data.frame(ofp = seq(0, 100, length = 101),
male = getmode(gam$model$male),
age = mean(gam$model$age))
使用predict
函数,我们可以将之前拟合的 gam 模型外推至测试数据。该图显示,在保持其他变量不变的情况下,ofp
与报告健康状况不佳的可能性呈非线性关系。此外,在 ofp 较高的地区,置信区间不太紧。
fits = predict(gam, newdata=testdata, type='response', se=T)### create a confidence interval for the fits
predicts = data.frame(testdata, fits) %>%
mutate(lower = fit - 1.96*se.fit,
upper = fit + 1.96*se.fit)ggplot(aes(x=ofp,y=fit), data=predicts) +
geom_ribbon(aes(ymin = lower, ymax=upper), fill='gray90') +
geom_line(color='#00aaff') +theme_bw()
图片作者。
参考
有关 GAM 的更多信息:
- https://m-clark.github.io/generalized-additive-models
- https://www . rdocumentation . org/packages/mgcv/versions/1.8-31/topics/gam
- https://noamross.github.io/gams-in-r-course/
用统计学家的大脑赌博。
利用蒙特卡洛模拟分析赌场的胜算
我在澳门威尼斯人赌场外徘徊(赌场内禁止拍照)
让我从我在 赌场的经历 说起,让它变得更有趣。2016 年,我在威尼斯人的世界第二大赌场澳门那里,在不同的游戏上试试运气,直到我到达轮盘老丨虎丨机。你可以想象,这里的气氛是超现实的,灯光闪烁,富人聚精会神地坐着,眼睛盯着老丨虎丨机和摆满“免费酒瓶”的桌子。
这是我的故事,我在赌场有 1000 美元(港币)可以花。在两个小时内,我在轮盘赌机器上赚了大约 3500 美元(港币),但是谁在赢了之后停下来。在看了一段时间人们玩不同的游戏后,我开始玩轮盘赌是在晚上 8 点(当地时间)。但是,我最终在赌场呆到了第二天早上 5 点,净赚了 1000 港币。我所有的钱都浪费在享受 9 个小时的“蒙特卡洛模拟”上了,赢的几率还不到 0.5%。玩笑归玩笑,我的一个统计学家朋友总是告诉我,赌博是零和/负和游戏,但是在赌场里,像我这样天真的人/游客很容易被显示器上显示的最后 20 个结果所诱惑(赌徒谬误)。
现在,至少我们有统计学和蒙特卡罗模拟来分析同样的问题。
现在,让我从解释赌场游戏的赔率开始。我将尝试解释一个游戏,并使它简单化,便于我们的计算。让我们选择游戏大和小(原文如此)。庄家将洗牌 3 个骰子,这可能导致从(1,1,1)到(6,6,6)的任何组合。有两个选项可供选择,小(4 到 10 之和)或大(11 到 17 之和)。我会解释为什么我有一段时间没有包括 3 和 18 的总和(类似的游戏在印度到处都很常见,被称为 7 上 7 下)。现在,我们可以选择小的或大的。让我们假设支出是 1:1,这意味着如果你赢了,你可以拿回你的赌注,如果你输了,你会失去同样的金额。到目前为止,你可能会认为小企业和大企业的几率是一样的,但这是几率发生变化的地方。对于三胞胎来说,支付模式有所调整,这使得游戏更有利于玩家。为了我们的计算,让我们假设在三胞胎的情况下我们不会得到报酬。选小选大和我们差不多。因此,让我们选择小的计算。
https://www . thoughtco . com/probabilities-for-rolling three-dice-3126558
如果我们选择小,我们的胜算将是 105/216(不包括三胞胎(1,1,1)和(2,2,2))。即 48.61%。所以,因此,房子赔率将是 51.39%。即使这样,你也会想,这么少的钱会对我有什么影响呢?对吗?嗯,我会通过蒙特卡洛模拟给你看。
事实:
- 欧洲轮盘赌的最低赔率是 2.7% ( 52.7%的人看好房子)。
- 与其他赌场游戏相比,二十一点是我们胜算较大的游戏之一。
进入蒙特卡洛世界
举例来说,我假设我有 50000 美元,我将在每场比赛中下注 500 美元。现在,首先,让我们考虑上面计算的赔率值为 48.6%,让我们计算一下,如果我分别玩 5 场、50 场、5000 场、1000 场和 10000 场游戏,我最终可能会有多少钱。
让我们导入库并创建一个输赢函数。我们将使用均匀随机生成一个介于 0 和 1 之间的概率,然后使用 win_odds 概率来决定我们是赢还是输(根据赔率)。
注意:即使你对 python 或编程不熟悉,也可以试着只阅读文本并理解故事。
#Import required librariesimport randomimport pandas as pdimport numpy as npimport matplotlib.pyplot as pltimport seaborn as sns#win_odds take the probability of winning, returns true if prob lies between 0 and win_oddsdef win_or_lose(win_odds):dice = random.uniform(0,1)if dice <=win_odds:return Trueelif dice > win_odds:return False
下面的 play 函数将接受输入—手中的筹码(此处为 50000 美元)、下注金额(500 美元)、总下注数和 win_odds 概率,并在游戏结束后返回最终更新的金额(使用赢/输赔率)。
#chips_in_hand is the amount of chips a player has in hand#betting amount is the amount a player bets in each game#total_bets is the number of bets in one seatingdef play(chips_in_hand, betting_amount, total_bets, win_odds):game_num = []updated_chips = []game = 1#lets bet for the number of bets we have in the argument and append the value of chips in hand after each bet in updated_chips listwhile game <= total_bets:#If we winif win_or_lose(win_odds):#Add the win amount to our chips valuechips_in_hand = chips_in_hand + betting_amountgame_num.append(game)#Append the updated_chips value after each gameupdated_chips.append(chips_in_hand)#If the house winselse:#Add the money to our fundschips_in_hand = chips_in_hand — betting_amountgame_num.append(game)#Append the updated_chips value after each gameupdated_chips.append(chips_in_hand)game = game + 1return(game_num,updated_chips,updated_chips[-1])
现在,我们将模拟和调用 play 5000 次(模拟次数),并分析最终金额值的统计(在 5000 次模拟中)。简单来说,由于我们是做 5000 次模拟,所以玩游戏后会有 5000 个不同的最终值。(1 场游戏基本上由投注数组成,如果投注数是 5,1 场游戏由 5 个投注组成)
ending_fund= []#lets try 5000 simulations to get a good mean value ( Monte Carlo Simulation)#Let’s start with $50000 and bet $500 bets each time for simplicity#Lets play 5 times and see the final chips amount possible through monte carlo simulationplt.figure(figsize=(12,6))for x in range(1,5001):game_num,updated_chips,fund = play(50000 ,500, 5, win_odds)ending_fund.append(fund)plt.plot(game_num,updated_chips)plt.title(‘Final chips value vs number of bets (After a player starts with $5000)’)plt.ylabel(‘Final chips value’)plt.xlabel(‘Number of bets’)plt.show()#Lets print the statistics of the final_chips valuemean_final = sum(ending_fund)/len(ending_fund)variance_final_funds = sum([((x — mean_final) ** 2) for x in ending_fund]) / len(ending_fund)res_final = variance_final_funds ** 0.5min_final = min(ending_fund)max_final = max(ending_fund)print(“The player starts the game with $50000 and ends with $” + str(mean_final))print(“The standard deviation is $” + str(res_final))print(‘The minimum anad maximum funds that a player can possible end with are: {} and {}’.format(min_final,max_final))
案例 1:下注数= 5
如果只是玩 5 次,不确定性就太高了,无法在一些统计上决定。即使这样,我们也可以看到玩家最终的平均金额是 49916 美元,比他实际拥有的要少。因为他每次下注只有$500,所以他在 5 次下注中只能输掉$2500。因此,让我们将下注数量增加到 50、500、1000 和 10000,并对其进行分析。没有人离开赌场时只下 4-5 次注。即使是我,在我的一生中也会下超过 500 次的赌注。
案例 2:下注数= 50
案例 3:下注数= 500
案例 4:下注数= 1000
案例 5:下注数= 10000
模拟图
分布图
从上面的模拟图和分布图中,你可以看到一个有趣的趋势。让我给你解释其中的一个,然后你就可以对正在发生的事情有直觉了。
在案例 5 中(下注数= 10,000),您可以在模拟图中看到,随着下注数的增加,该图向下。另外,在模拟图下方的分布图中,您可以看到平均最终金额约为-100,000 美元。想象一下,有人带着一袋 5 万美元来到这里,最后却给了房子 10 万美元。
现在你已经了解了赌博的赔率,让我们对最终金额与 odds_probability 的统计数据进行敏感性分析。
我们将从 0.45(对我们不利)到 0.495(良好的回报,如二十一点,但仍低于 0.505 的赔率)等间距小数,看看最终金额的平均值、标准差、最小值和最大值是什么样子的。
我们将赌注的数量定为 1000,这对于正常的赌徒来说是非常现实的。
win_odds_list = np.linspace(.45, .495, num=100)
col_names = ['win_odds', 'mean_final', 'std_final', 'min_final', 'max_final']final_coins = pd.DataFrame(columns = col_names)for odds in win_odds_list:mean_val, std_val, min_val, max_val = simulate_bets(50000, 500, 1000, odds, 5000)final_coins = final_coins.append({'win_odds': odds, 'mean_final': mean_val, 'std_final': std_val, 'min_final':min_val , 'max_final':max_val }, ignore_index=True)
在对我们上面生成的 100 个等间距赔率中的每一个进行 5000 次模拟后,我们得到了下面的数据框。
从上面 4 个图可以看出这么多有趣的趋势。即使赢了 50000 美元,玩了 1000 次,最大价值也只有 100000 美元左右(赔率为 0.495)。
下次当你去拉斯维加斯、大西洋城或澳门时,让你的统计员的大脑保持活跃。如果你打算待很久,至少试着玩赔率最高的游戏。
谢了。
在 Linkedin 上帮我联系:
T3【https://www.linkedin.com/in/saket-garodia/】T5
参考文献:
https://pythonprogramming.net/monte-carlo-simulator-python/
http://math.info/Misc/House_Edge/
https://wizardofodds . com/games/sic-bo/
https://towards data science . com/the-house-always-wins-Monte-Carlo-simulation-EB 82787 da 2 a 3
回到 2018 年,那是我第一次在澳门的赌场体验(是专门从香港过来的一日游)。我们选择了…
medium.com](https://medium.com/eatpredlove/casinos-big-and-small-using-monte-carlo-simulation-3936f9bcf4bf) [## 赌场总是赢家:蒙特卡洛模拟
赌场是怎么挣钱的?诀窍很简单——你玩的时间足够长,输钱的概率就会增加。让我们…
towardsdatascience.com](/the-house-always-wins-monte-carlo-simulation-eb82787da2a3)
注来自《走向数据科学》的编辑: 虽然我们允许独立作者根据我们的 规则和指导方针 发表文章,但我们不认可每个作者的贡献。你不应该在没有寻求专业建议的情况下依赖一个作者的作品。详见我们的 读者术语 。
PowerBI RS:在触发模式下设置数据刷新
使用 Power BI 报告服务器数据库可以做许多在 Power BI 服务中不能做的事情,其中之一就是在报告数据集上设置触发数据刷新。
什么是触发模式下的数据刷新?
顾名思义,它将触发您的 PBI 报表的数据集来刷新事件。例如,当报表引用的数据源发生更改(添加、更改或删除)时,触发条件将被激活,以允许 PBI 报表立即刷新数据,并使 PBI 报表数据保持最新。典型的应用过程如下:
作者图片
为什么使用触发模式来刷新您的报告数据?
在 PBIRS 中,PBI 报表有两种主要的数据刷新类型:计划刷新和 DirectQuery。DirectQuery 使用户在每次使用报表时都能持续获得最新的数据,但缺点是当报表查询逻辑复杂时,可能会严重影响报表的性能。此外,计算引擎不支持某些 DAX 函数(如时间智能函数)。在计划刷新模式下,理论上,您的报告性能不会受到刷新行为的影响,您可以使用任何支持 PowerBI 的 DAX 函数,这很好,但是,您不能保证用户在使用报告时会获得最新的数据。
所以,这就成了一个很尴尬的事实。假设你需要开发一个报表,这个报表需要使用 DAX 时间智能函数(而不是 SQL),你必须保证用户在使用它的时候能及时看到最新的数据。那么,如何为数据集选择刷新模式呢?也许你会设置你的报表每一分钟刷新一次,但是如果你的报表数据比较多,不仅会增加数据刷新失败的概率还会给服务器造成一定的负担。因此,触发模式下的数据刷新成为了最佳解决方案。它结合了 DirectQuery 和计划刷新的优点。只有当数据发生变化时,才会执行数据刷新。这不仅可以确保用户在使用报表时可以看到最新的数据,还可以在不影响报表性能的情况下减轻服务器的负担。这就是为什么您应该在触发器模式下刷新报告数据。
如何实现?
首先,作为示例,我在数据库中创建一个表。如下图,我创建了一个新表,插入了三行数据——中国古代战国时期(公元前 475-221)的三大名将:白起、廉颇、王建,简单的像这样:
CREATE TABLE [TriggerRefreshTest]
(
[No_] int,
[Name] nvarchar(10),
[Country] nvarchar(20)
)Insert Into [TriggerRefreshTest]
values(1, 'BaiQi', 'The Kingdom of Qin'),(2, 'LianPo', 'The Kingdom of Zhao'),(3, 'WangJian', 'The Kingdom of Qin')
将数据导入 Power BI Desktop 并按如下方式发布:
作者图片
我们现在要达到的效果是,当我在数据库中插入一行新的数据——(李牧,赵国),然后立即触发一个数据刷新事件,使报表数据保持最新。在 SSMS,创建一个新的触发事件,如下所示:
作者图片
然后我们需要用 SQL 写一个触发脚本。对于没有学过 SQL 的读者来说还是可以的。可以直接使用下面的代码。我已经简化并优化了脚本。您只需要用您的报告信息替换下面的代码(我已经做了注释):
SET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOIF OBJECT_ID
(
N'trigger_data_refresh'
) is not nullDROP TRIGGER trigger_data_refresh;GOCREATE TRIGGER trigger_data_refresh
ON [TriggerRefreshTest] --Replace with your table name hereAFTER INSERT
AS
SET NOCOUNT ON;DECLARE @REPORT_NAME NVARCHAR
(
50
),
@REPORT_ID VARCHAR
(
100
),
@SUBSCRIPTION_ID VARCHAR
(
100
)
SET @REPORT_NAME = 'WarringStates' --Replace with your PBI report name.
SET @REPORT_ID =
(
SELECT TOP 1 [ItemID]
FROM [ReportServer].[dbo].[Catalog]
WHERE [Name] = @REPORT_NAME
)
SET @SUBSCRIPTION_ID =
(
SELECT TOP 1 SubscriptionID
FROM [ReportServer].[dbo].[ReportSchedule]
WHERE [ReportID] = @REPORT_ID
)BEGIN
WAITFOR DELAY '0:0:3'
exec [ReportServer].dbo.AddEvent
@EventType='DataModelRefresh',
@EventData=@SUBSCRIPTION_ID
ENDGO
最后,运行代码,这样我们的触发器刷新就设置好了。让我们在表中插入一个新行—[triggerefreshtest]:
Insert Into [TriggerRefreshTest]
values (4, 'LiMu', 'The Kingdom of Zhao')
现在,我们的触发机制已经检测到数据更改,因此数据刷新事件已被激活。返回 Power BI RS 的报告页面,点击“刷新”刷新缓存,可以看到,刚刚插入的数据立即显示在报告中!
作者图片
考虑
如果数据源表由多个用户维护,那么他们可能会同时修改数据,这可能会导致问题(比如表锁定)。我认为解决方案之一是强制最小刷新间隔。这可以通过修改存储过程来实现。
结束~
(本文中文首发于 2019 年 12 月D-BI)
具有强化学习的游戏级设计
“PCGRL”论文概述,介绍了一种通过训练 RL 代理程序生成游戏关卡的新方法。
程序性内容生成(或 PCG) 是一种使用计算机算法在游戏中生成大量内容的方法,如巨大的开放世界环境、游戏关卡和许多其他用于创建游戏的资产。
PCGRL 全文
今天,我想和大家分享一篇名为PCGRL:通过强化学习的程序化内容生成的论文,它展示了我们如何使用自学习 AI 算法来程序化生成 2D 游戏环境。通常,我们熟悉使用称为强化学习的人工智能技术来训练人工智能代理玩游戏,但本文训练人工智能代理来设计游戏的级别。根据作者的说法,这是 RL 第一次用于 PCG 的任务。**
当前框架版本:0.4.0 通过强化学习生成程序内容的 OpenAI GYM 环境…
github.com](https://github.com/amidos2006/gym-pcgrl)
推箱子游戏环境
让我们看看论文的中心思想。考虑一个简单的游戏环境,比如游戏中的推箱子。
推箱子游戏关卡。
我们可以把这个地图或者游戏级别看作是代表游戏状态的 2D 整数数组。这种状态由可以编辑游戏环境的强化学习代理来观察。通过采取行动,如添加或删除游戏的某些元素(如固体盒,板条箱,球员,目标等。),它可以编辑这个环境给我们一个新的状态。
PCGRL 框架
现在,为了确保这个代理生成的环境是高质量的,我们需要某种反馈机制。在本文中,这种机制是通过使用手工制作的奖励计算器比较这个特定游戏的先前状态和更新状态来构建的。通过为游戏规则增加适当的奖励,我们可以训练 RL 代理生成特定类型的地图或关卡。这个框架的最大优点是,在训练完成后,我们可以通过点击一个按钮来生成几乎无限的独特游戏关卡,而不必手动设计任何东西。
RL 代理遍历和编辑游戏环境的三种方法。
本文还比较了 RL 代理可以用来遍历和编辑环境的不同方法。如果你想了解这些方法之间性能比较的更多细节,这里是研究结果的全文。
[ 来源通过训练有素的 RL 代理测试不同游戏的关卡设计。
一般研究方向
虽然本文实验中使用的游戏是简单的 2D 游戏,但这一研究方向令我兴奋,因为我们可以在这项工作的基础上创建大型开放世界 3D 游戏环境。
这有可能改变在线多人游戏体验。想象一下,如果在每个多人开放世界游戏的开始,我们可以每次都生成一个新的独特的战术地图。这意味着我们不需要等待游戏开发商每隔几个月或几年发布新地图,但我们可以在 AI 游戏中这样做,这真的很酷!**
感谢您的阅读。如果你喜欢这篇文章,你可以关注我在媒体、 GitHub 上的更多作品,或者订阅我的 YouTube 频道。
手机游戏。r 和“ggplot2 ”,看看亚马逊用户评论能告诉我们什么是最值得购买的智能手机
亚马逊用户的智能手机评论和一些可视化效果,看看人们最喜欢哪些品牌。
玛丽安·克罗恩在 Unsplash 上的照片
由于 web 服务的巨大增长和我们在过去十年中看到的数据爆炸,互联网上的大量评论数据令人震惊。像 IMDB 和 Yelp 这样的网站已经创建了完整的生态系统,对产品、电影、餐馆和个人进行比较和评论。大多数基于互联网的零售公司,如亚马逊或易趣,也已将评论纳入其采购平台,以创建一个以客户为中心的最佳产品层级。
数以千计甚至数百万计的评论散布在 Twitter 和脸书等社交媒体平台上,但也存在于零售网站和评论网站上。这些网站上有大量公开可用的数据,并且有巨大的空间来深入研究这些原始和非结构化的数据,以获得有可能为商业、市场研究和创新带来利润的见解。大多数评论也与评级相关联。一篇评论通常看起来像一个简短的段落,有一个标题,描述被评论的主题,然后是一个评级,通常是五分或十分。
网上有很多智能手机的原始消费者数据。与指派专家评论者来决定最好的手机相反,成千上万关于同一款手机的客户评论可以作为更平衡的最终用户意见挖掘的试金石。
数据
本项目使用的数据集可从 KaggleT5 这里 获得。
合并后的完整数据集包含 82,815 个条目,分为以下几列:
品牌:产品品牌:具有 10 个类别的分类变量
标题:产品标题:被评论产品的名称
url:产品 url:购买产品的 URL 链接
图像:产品图像 URL:产品图像的 URL 链接
评级:产品平均价格。评级:用户评论对产品的平均评级 Url:产品评论页面 URL:产品评论页面的 URL 总评论数:产品总评论数:产品价格总评论数:产品价格:产品的潜在价格范围
姓名:审核人姓名
评级:审核人评级(等级 1 至 5)
日期:审核日期:进行审核的日期
已验证:有效客户:客户是否为已验证的亚马逊客户标题:审核标题:审核的标题
正文:评论内容:评论的主要内容
有用的投票:有用的反馈:评论得到了多少反馈。
清洁和争论
初始清洗过程的第一步是移除被认为不需要的色谱柱。图片、网址、姓名、评论者网址、正文和有用的投票已被删除。标题栏(已经合并)和用户评分栏也被删除。
在检查了“价格”变量后,完成了初始清洁过程的下一步,由于 NA 的数量很大,并且许多行都有一个价格数字范围,因此决定不将该变量包括在研究中。
最初争论过程的第一步是获取两个数据集并执行“左连接”以创建一个合并的数据集。这是使用“ASIN”ID 作为合并的键来完成的。
下一步是将每个评论的标题与评论的实际内容结合起来,创建一个包含标题和评论正文的超级专栏。
观想主题
该项目涵盖了许多品牌的手机和一个解决方案的配色方案,将尊重所有手机的品牌完整性,同时表达了品牌的多样性。我们决定用一系列类似彩虹的颜色作为三个观想的主题,以保持它们之间的一致,并突出观想者对信息的中立性。
视觉呈现从微观到宏观。第一种视觉化聚焦于评论的总体概述;关于手机,最常见的词汇和情感是什么?
下一个可视化更深入一步,关注用户对特定品牌的感受。我想向用户展示市场上最受欢迎的品牌是什么。
一介词云
需要进行一些额外的清理,以使一些东西值得注意,所以一些常见的英语“停用词”(无助于理解情感的词,如连词和代词)和标点符号被从集合中删除,因为它们在这种情况下对分析情感没有用处。
创建前十个单词的术语文档矩阵,以评估哪些单词在集合中出现得最频繁。从 TDM 中可以清楚地看到,由于一些关于手机的显而易见的词频繁出现,需要将更多的词添加到停用词列表中。例如,单词“电话”是最流行的(不奇怪,考虑到它们是电话评论)
作者图片
前一万篇评论的样本(由于大量数据的内存错误,不可能包含所有评论)。然后,需要将这些列转换成文本文件,以便对它们进行适当的操作。下一步是将文档中所有不必要的字符变成空白。
从这里开始,所有的文本都被转换成小写,以使内容标准化,并在这种形式下看起来更具视觉吸引力。
见识
当人们评论手机时,什么想法和观点最流行?。这是亚马逊手机评论的总体视图,也是数据集内容的总体视图。
“随机顺序”参数被设置为 false,因此可以很容易地理解哪些词在评论中出现频率最高。单词的最大数量被设置为 200,以便不使图表过载,单词只需要出现一次就可能被包括在内。
选择了“Dark2”配色方案,限定为八种颜色。这符合一开始设定的彩虹配色方案,也最大限度地减少了试图理解一个有太多颜色的图表的心理负担。
这种可视化与评论内容的总体视图相关,旨在回答关于电话的总体情绪和语言使用的问题。
更深入的观察…
作者图片
这种可视化的第一个清理元素是标准化“标题”变量的名称,因为由于不可见的字符,功能正在减少。
扯皮:
对于这一个,有必要找到具有最佳评级的品牌,因此创建了一个新变量,它将为每个品牌保存一行,与此相关联的值将是所有品牌评论的平均值的组合。
使用“重新排序”功能从最低到最高显示评级也很重要,以明确显示等级中的最佳品牌。
见识
viz 试图回答亚马逊评论中关于顶级品牌的问题。用户评论认为哪些品牌是最好的。
绘制了所有品牌及其相应平均评级的条形图,并加入了彩虹配色方案。
嗯,有意思……..
在这里可以找到一些很酷的发现和见解,通过进一步研究数据集,还会有更多的发现。
在完成这个项目时,我想到的一件事是关于数据科学中的公平和代表性。由于探索的性质,如果我创造了自己的手机品牌;【我-艾伦】,给自己一个五星评价,那么我将拥有亚马逊上评价最高的手机,并将位于我自己的条形图顶部。
这个项目的所有 R 代码都可以在我的 Github 页面上的 这里 找到。😃
博弈论 101
单人决策问题
博弈论无处不在。它如此普遍,以至于我们常常意识不到它的存在。经济学、市场营销、政治学、游戏设计和人工智能的学生每天都在使用博弈论版本。在每一门学科中,主要目标是理解人们在面对特定情况或决定时的反应。一个细心的观察者会在任何政治选举或营销活动中看到这一幕。
虽然博弈论只是描绘了现实的图景,但它是一个非常有用的工具,而且效果惊人地好。博弈论利用一个框架来处理理性决策者之间的冲突和妥协。传统上,一个理性的决策是在遵守一组给定的约束条件的同时最大化一个人的收益。
和任何学科一样,博弈论有自己的词汇和结构。本文将通过最基本的决策问题——单人决策来介绍其术语和原则。
玩家
在单人决策中,只有一个参与人。玩家在博弈论中是指任何有能力做出决定的实体。博弈论的一部分是对玩家的行为做出假设。我们的结果和我们的假设一样好。因此,博弈论的一个重要部分要求我们挑战我们的假设。但是今天的课,我们会保持简单。
我们每天都面临着多种选择。国会议员决定如何投票表决一个特定的法案。一个产品经理决定在发布日期之前删除一个特性。甚至关于穿哪件衬衫的琐碎决定也是每天都会发生的事情。我们面临的每个决定都有三个部分。
行动、结果和偏好
- 行动是我们可以选择的所有选项。
- 结果是行动的结果。
- 偏好是对可能结果的排序,从最理想到最不理想。
让我们用之前提到的选择当天衬衫的小决定。你有两件衬衫,红色的没有图案,另一件是蓝色的有图案。
我们将集合动作表示为 A = { r , b },其中 r 代表选择你的红色衬衫, b 代表选择你的蓝色衬衫。
现在让我们定义一组结果。在这种情况下, X = { x , y }其中 x 穿着红色衬衫, y 穿着蓝色衬衫。
举这样一个简单的例子,行动和结果之间的差别是微妙的。行动是选择一件特定的衬衫,结果实际上是穿着一件特定的衬衫。但是你还是会明白的。
结果和行动并不总是如此一致。例如,一项行动可能是减少开支。结果将是大量裁员。虽然结果仍然是所选择的行动的结果,但它们彼此非常不同。
此外,可能的动作列表并不总是像我们的两件衬衫的例子那样分散。也许你有 5000 万美元的可用预算,但你并不需要花掉它。你的行动范围可能是 A = [\(0,\) 5000 万]。请注意,方括号表示 0 到 5000 万美元的区间,而不是表示离散行动的{}。
现在,我们来介绍一下偏好。想象你最喜欢的颜色是红色,因此,你更喜欢穿红色的衬衫。在博弈论中,我们用符号 x ≿ y 来表示这种偏好。这个符号结构读作“ x 至少和 y 一样好。”这只是博弈论表达你更喜欢你的红色球衣的公式化方式。
当然,“ x 至少和 y 一样好”这种偏好是很弱的。我们就是这么称呼它的。排名很弱。还有另外两种形式的排名,也是借鉴传统经济学和决策理论。一个是表示严格偏好的≻,“ x 严格优于 y ,”和~表示无所谓,“ x 和 y 一样好。”
在我们继续之前,让我们先介绍一下对玩家的要求。玩家不允许在任何两个结果之间犹豫不决。这叫做完备性公理。如果出现结果 x 和 y ,玩家必须选择 x ≿ y 或 y ≿ x 。
除了完整性公理之外,我们还强加了另一个叫做的规则,即传递性公理。它陈述了对于任何三个结果,如果 x ≿ y 和 y ≿ z ,那么 x ≿ z 。在真实的话语和具体的想法中,这意味着如果我们有第三件带有圆点的紫色衬衫,我们更喜欢红色衬衫而不是蓝色衬衫,我们也更喜欢蓝色衬衫而不是紫色衬衫,那么我们一定更喜欢红色衬衫而不是紫色衬衫。
总而言之,完备性公理保证了任何两个结果可以相互排序;比起蓝色的衬衫,我更喜欢红色的。传递性公理确保了这些排序之间不会有矛盾。如果没有这两条规则,我们可能会以一个不确定的场景结束。博弈论禁止使用优柔寡断的玩家。既完全又可传递的偏好关系称为理性偏好关系。
请记住,如果我们的模型中包含不止一个参与者,即使每个参与者都是理性的,也有可能以非理性群体告终。但是我们的单人决策问题不会遇到这种情况。
支付函数
在大多数情况下,偏好和结果一起形成了一个支付函数。通常,在商业中,支付函数返回一个美元值。但是唯一的要求是一个顺序值,这样值越高,收益越好。
当处理一些足够简单的事情时,比如为一天选择一件衬衫,我们的偏好通常是支付函数的同义词。在这些情况下,我们基于偏好为每个结果分配一个支付函数值,这样如果 x ≿ y ,那么支付函数为 x 返回的值大于为 y 返回的值。
回到我们的三件衬衫的例子,我们记得偏好是红色 ≿ 蓝色和蓝色 ≿ 紫色,因此,红色 ≿ 紫色。我们给红色的分配 10 的收益值,给蓝色的分配 5 的收益值,给紫色的分配 1 的收益值。这些数字是任意的,除了红色的值必须大于蓝色的值,蓝色的值必须大于紫色的值。对于红色、蓝色和紫色,我们可以分别使用 2000、1000 和 1。规模无关紧要。
如果我们将玩家、期权和每个收益之间的关系看作一棵决策树,它将类似于下图。
理性选择范式
最后,我们得出了完整的概念。记住,我们需要一个理性的代理人作为参与者。这被称为理性选择范式。它认为决策者会理性地选择自己的最佳行动。为了保持这一点,我们假设玩家知道所有可能的行动,所有可能的结果,准确地说选择每个行动将如何在结果中表现出来,以及他对所有结果的偏好。
这个假设列表可能看起来势不可挡,甚至不现实,但是如果我们牺牲其中任何一个,我们就不能断言理性选择的想法。本文中的决策示例保持简单,处于入门水平。支付函数的值很容易确定,并且清楚地表明了行动的最佳选择。然而,一些支付函数是需要微积分的数学公式。在这些情况下,你可能需要画出收益函数的结果来选择最佳行动。
博弈论是许多学科不可或缺的一部分,如果应用得当,它能提供巨大的价值。对它的理解,即使是最基本的理解,也会对在当今更复杂的商业决策中做出合理的选择大有帮助。
博弈论在起作用:意大利的“温和”封锁
为什么罗马会沦陷…今年秋天
加布里埃拉·克莱尔·马里诺在 Unsplash:罗马论坛上的照片
介绍:意大利更温和的封锁方式
由于 COVID 疫苗只有几个月的时间了,抗体治疗和其他治疗方法也获得了使用许可,人类对该病毒的战争即将结束。因此,各国政府决心在最后的战场上最大限度地减少伤亡,并尽可能多地将我们带到 COVID 的另一边。
尽管各国将公共卫生置于经济问题之上,但他们希望在这样做的同时,不要让太多的企业倒闭,也不要让太多的工人失业。为了达到微妙的平衡,意大利政府制定了一项全国封锁政策,根据 Rt 值捕捉到的感染传播速度调整限制。该政策根据当地 Rt 值将意大利的 21 个地区用颜色编码为黄色、橙色和红色,因此据称与 2020 年 3 月驯服第一波感染的一刀切封锁相比,在更大程度上保护了经济。
意大利政府没有理会意大利医学委员会提出的在全国范围内推广三月式封锁的请求。封锁国家造成的经济损失也必须考虑在内——这是政府将医生的担忧置于一边的论点。从医生的角度来说,他们认为封锁和释放的政策——T2——以色列政府走上了一条明显成功的道路——可能在保护企业和就业方面做得更好。医生们指出,对于食品旅游业来说,即使是宵禁和地区流动限制这样的小限制也可能太多,无法带来利润。在高风险行业运营的公司利润率非常低,需要规模来维持经营。
对不同病毒遏制政策的成本效益评估并不是我在这篇文章中要触及的主要话题——如果感兴趣的话,查看一下经济学家 Favero、Ichino 和 Rustichini 的概要。相反,我想确定的是,意大利政府宣称的通过更温和的方式让人们留在国内的目标,是否在政策本身的力所能及的范围之内。作为剧透或者免责声明,我给出的答案是,很可能不会。然而,我的主要目标是展示基本博弈理论能走多远,以获得对政府政策潜在后果的洞察。
为红区设想的主要规定是从晚上 10 点开始宵禁,不准跨地区移动,公共交通以 50%的能力运行,除了去看医生、慢跑、购物和很少的其他活动外,不准移动。黄色和橙色区域的限制更温和,但是我将在我的玩具示例中使用红色区域。意大利人出门时必须准备好自我声明,以证明他们没有加入其他家庭,没有因为不必要的原因外出,或者更糟的是,没有跨越地区边界。未能出示自我声明或出示的声明未列出外出的有效理由的犯罪者将被处以 400€的罚款,如果在声明发布后三天内被清除,罚款将减至 280€。
然而,更重要的是,与三月创纪录的 110,000 英镑罚款相比,立法者似乎采取了更宽容的态度,拒绝派遣军队,更多地依靠公民的合作。我们面临的问题是,软锁定和中等规模、宽松的罚款相结合,是否可能达到政府的底线。我将推敲一些基本的博弈论来证明它很可能不会,这源于 COVID 作为一种传染性很强、但并不致命的疾病的本质——如果你还没有听过的话,我恳求你听听这个彼得·阿提亚的播客。
博弈论:一种非常生疏的方法
请考虑以下设置。卡罗莱纳和马可代表了意大利人口的特征,位于最重要的人口统计数据(年龄、收入、教育等)的中间。).Carolina 是女性,45 岁,税后收入€20,000 英镑/年,拥有高中文凭,总体健康。同样,马尔科——他是“他”——45 岁,健康,过着意大利中产阶级的生活。他们住在一起,见面时会顺便拜访。在周六下午,他们可以选择看网飞的节目或者出去散步。他们真的不应该离开,但他们知道找到一个理由打破自我认证并不难。Carolina 上次去购物时忘记拿牙膏了,Marco 真的很想吃杂货店里那些美味的蔬菜,加上他最近没怎么锻炼,所以警察为什么要干涉他对健康的追求呢?这不正是政府想要扶植的吗?
当这些想法在他们的脑海中不断翻找时,意大利的秋季让他们脱颖而出,并诱使他们在立法者面前一试身手。这些是卡罗莱娜和马可的选择参数:
- U 是散步的效用,归一化为 1(U= 1);
- 如果它们相遇,效用减少一半;
- 得到 COVID 的负效用是 10U;
- 无 COVID 国家的效用设为无穷大(∞);
- 被罚款的负效用是 3 U (近似值)。3 天的有偿工作);
- c̅ 是散步时得到 COVID 的概率,它严格小于1 并且非常接近 e 为 0(0<c̅≪1);
- c 是待在家里得到 COVID 的概率,比c̅【0<c<c̅≪1 略少;
- f 是被警察拦下发现没有有效自行申报时被罚款的概率,比 c̅ 和c(0<f<c<c̅≪1).少
图 1 描绘了每种反应组合的收益。马可和卡罗来娜在两个选项中选择一个:呆在家里(逗留和去散步(去)。此外,它们不能通信,必须彼此独立行动。
Carolina 选择的期望是 Marco 选择去散步,这将她的选择集减少到图 2 所示的条件集;出于同样的原因,Marco 的选择集减少到如图 3 所示。世界的合作状态(停留,停留)要求马可和卡罗琳娜协调行动;然而,他们怀有相互的期望,认为对方会叛变;因此,当各方作为效用最大化的代理人独立行动时,最大化社会效用的世界状态就丧失了。
图 1:收益矩阵
图 2:卡罗莱纳州的收益矩阵
图 3:马可的收益矩阵
现在让我们更仔细地看看个人的选择。卡罗莱纳州必须在停留和出发之间选择一个,条件是马可要走。如前所述,每一方都期望另一方背叛,因此只有条件集是可行的。
一旦选择留下,Carolina 将以概率 c (即–10c)获得 COVID 的负效用。当选择去的时候,她会拿走去散步收益的一半(即 0.5),以概率 c̅ 得到柯维德的负效用(即–10c̅),以概率 f 得到罚款的负效用(即 3 f )。值得注意的是,呆在家里——通过家人、在当地杂货店等——获得 COVID 的概率。—大约等于散步时被感染的几率(即 c ≈ c̅ )。卡罗莱纳选择停留选项必须满足的条件是:
U ( 待)>U(走)
或-10ct64】0.5-10c̅3f。因为 c ≈ c̅ ,10 个 c 和 10 个 c̅ 条款相互抵消;求解 f 得到条件f1/6。这意味着,卡罗莱纳州选择留在的可能性必须至少高达 17%。这个数字是一个不切实际的高概率,甚至没有接近目前在意大利红区交易的 f。请注意,所需的概率越小,罚款越大(即,与罚款相关的负效用从 3U 增加到 4U,依此类推)。
因此,在当前的政策体制下,提高罚款可能是让意大利人留在国内的唯一可行选择,但这与支持更温和封锁的理由背道而驰。因此,意大利的卡罗莱纳州最终挤满了意大利的街道,而不是平坦的曲线。
同样,马尔科预计卡罗莱娜会叛变,这将他的选择限制为(去,留)和(去,去)。他的决策反映了卡罗莱纳州,他也选择去。总的来说,( go , go )是一个纳什均衡,这意味着意大利的温和封锁未能温和地推动传染曲线下降。
宽松的政策和 COVID 的性质相结合,COVID 是一种高传染性、低致死率的病毒,主要来自渐近感染,似乎正在将意大利置于不可避免的硬性封锁的轨道上。这可能是弥合现在和疫苗推出之间的差距,避免普遍医疗保健失败的唯一途径。
博弈论:一种生疏的方法
为了清楚起见,我在上面的例子中用博弈论“欺骗”了一点。毕竟,本文的目的是提供一些博弈论的直觉来强调软锁定策略的一些潜在缺点。然而,我们可以通过使用行为社会科学对无 COVID 国家的效用进行参数化,从而得到更好的结果。一般来说,我们贴现未来效用,因为它本来就不如现在效用有价值;因为我们没有意识到未来效用的真正价值。虽然我们确实知道今天把多余的钱存起来并投资到股票市场会给我们带来一些重大的终身回报,但我们的大脑被某种形式的预感所欺骗,夸张地贴现了这些未来的收益。我没有用“夸张地”这个词来炫耀,而是引入了双曲线贴现。为了对一项 30 天后到期的投资进行建模,我们需要考虑货币的时间价值——也就是说,明天一美元的价值比今天少了δ——以及我们现在的偏好——也就是说,我们只愿意对未来金额的估值乘以我们今天的估值。返回双曲线贴现效用函数:
u(30)=β(1-δ)⁰u
将此与我们的主要讨论联系起来,我们可以模拟卡罗莱纳州和马尔科对意大利封锁政策的反应,这种反应受到时间贴现和当前偏差的影响。这让我们可以参数化他们承诺的从现在起 30 天内无 COVID 国家的效用。我将使用一些有意义的数字,但是最终目标是更好理解,而不是扩展我们的数学肌肉。因为卡罗来纳和马可非常关心他们的国家,100 U 是他们对一个月后无 COVID 国家的估价。他们以 10% ( δ =0.10)折现未来效用,并受到当前偏差的影响( β =0.2)。因此,与该选择相关的效用由下式给出:
**u(30)= 0.2(0.9)⁰(100)≈0.85
请注意,当前偏差参数的值 0.2 远非不现实。想象一下,在一个现在没有 COVID 的国家和 30 天以后同样的情况之间,有一个选择。甚至那些否认 COVID 真实性的人也不会拒绝这笔交易。然而,有多少(中值)意大利人会购买第二种产品呢?无论是因为他们缺乏关于锁定政策有效性的信息,还是因为他们认为情况会比那更快好转,或者无论他们目前的偏见是什么,很可能会有 20%的人会接受这笔交易。
说到这里,让我们再次转向博弈论,看看图 4 中新的收益矩阵。我们这次不需要“作弊”来说明( go , go )是一个纳什均衡。让我们为卡罗琳娜做一下心算。当马可选择留时,她将在获得 1–10c̅–3f≈1 和去或获得 0.85–10c̅≈0.85 和留之间做出选择。因为去获得更大的效用,Carolina 愿意调整她的自我认证,去去走走。类似地,当马尔科选择离开时,她将在留下来获得消极效用或离开并获得 0.5–10c̅–3f≈0.5 之间做出选择;因此,她会选择去。Marco 会照着做,不管 Carolina 选不选,他都会选择去,这样就使得( go , go )成为这个博弈唯一的纳什均衡。
图 4:收益矩阵(双曲线贴现)
最重要的评论如下。与(停留、停留)均衡相关联的社会效用远大于与(进行、进行均衡相关联的私人效用最大化。不幸的是,当不协调的各方在市场上交易“公用事业”时,这种状态是无法实现的。
U ( 停留,停留)= 2(0.85–10c̅)≈1.7
u(go,go)= 2(0.5–10c̅–3f)≈1
总的来说,行为社会科学给了我们更多的视角,让我们了解以前被带走的东西。这绝不是博弈论的一次大跳水,而是展示了它的广泛应用。在我们的案例中,行为激励和信息缺乏的相互作用可能会致命地阻碍一项基于硬锁定和自由开放之间的妥协的政策。
结论
在这篇文章中,我应用了基本博弈论来揭示意大利 2020 年秋季停摆的潜在后果,这在很大程度上——“过度”可能是更好的词——取决于公民合作。由于 COVID 的性质,它是一种与埃博拉病毒甚至 SARS-CoV 非常不同的感染,其致死率低到不会让人们躲在家里,软封锁似乎相当无效,因为它的目标是平坦曲线和减轻对医疗保健系统的打击。
最有可能的是,意大利医学委员会建议的更短、更严格的封锁能够更好地实现这些目标,而不必牺牲更大的经济。这是因为在任何形式的封锁下,经济都无法以接近有效的方式运行。因此,今秋的几次理发和浓咖啡可能会使罗马沦陷。
AlphaGo 中的博弈论概念
DeepMind 利用了许多创新的人工智能方法,但如果没有博弈论的概念,它们可能不会如此有效地工作。
介绍
围棋可以说是世界上最古老的棋类游戏,起源于大约 4000 年前的东亚,是两个人玩的游戏。围棋通常是在一块由 19 条竖线和 19 条横线组成 361 个交叉点的方形木板上,用 181 颗黑棋和 180 颗白棋来下。每个玩家依次(黑棋先走)在任意两条线的交点上放置一颗石头,在此之后这颗石头不能被移动,使得这个游戏成为一个完全信息的连续游戏。目标是通过用他们自己的石头做的边界完全包围空点来征服领土,其中被包围的对手的石头被捕获并从棋盘上拿走。拥有最多点数(征服领域)的玩家赢得游戏(大英百科全书)。
DeepMind 是一个由科学家、工程师、机器学习专家等组成的团队,共同努力推动人工智能的发展。他们利用自己的技术实现广泛的公共利益和科学发现,并与他人合作应对重大挑战,确保安全和道德。他们的众多突破之一是 AlphaGo,它是第一个击败职业人类围棋选手的计算机程序,第一个击败围棋世界冠军的计算机程序,可以说是历史上最强的围棋选手(DeepMind,2017)。虽然 AlphaGo 的大部分成功可以归功于各种创新的人工智能方法,如深度学习和卷积神经网络,但允许这些模型工作的许多概念来自博弈论领域。在整篇论文中,我将实现 DeepMind 的科学家、工程师和机器学习专家用来构建 AlphaGo 的各种博弈论概念,alpha go 是近年来人工智能历史上最伟大的成就之一
扩展形式游戏
围棋是完全信息的序贯博弈。玩家按顺序移动,每个玩家在游戏结束时都有相同的信息。在显示和分析连续移动博弈时,我们以博弈树的形式显示和分析它,这通常被称为扩展形式博弈。一个广泛形式的游戏有许多关键方面,如玩家可能行动的顺序,他们在每个决策点的决策,每个玩家对另一个玩家的信息,以及每个节点结束时的收益(《策略的游戏》,2010)。以下是围棋游戏的新颖博弈树:
(来源: Livebook )
用于分析广泛形式博弈的一个常用策略是回退/逆向归纳,这是一个从问题(博弈树)的末端开始在时间上逆向推理以确定任何子博弈完美均衡的过程。在这些更新颖的广泛形式的游戏中,你可以使用回滚策略来确定任何均衡,但围棋游戏的问题是它有 250^150 可能的移动。如果人类可以使用逆向归纳法假设性地分析所有可能走法的博弈树,那么即使从宇宙开始以来的所有时间都不足以在围棋中进行完整的树搜索(DeepMind,2020)。DeepMind 的 AlphaGo 程序利用了一个深度强化学习卷积神经网络,该网络结合了蒙特卡罗树搜索。蒙特卡洛树搜索只预测每一步可能出现的可能序列的极小一部分,计算这些假设序列导致的输赢次数,并使用这些计数给每一步可能的移动打分(Melanie Mitchell,2019)。下图是一个假想的围棋博弈树:
(资料来源: Nikhil Cheerla ,2018 年)
效用最大化
原则上,当 AlphaGo 给自己的每一步棋打分时,我们实际上是在谈论最大化预期效用。利用深度强化神经网络和蒙特卡罗树搜索通过自我游戏进行学习,AlphaGo 可以独自玩数百万场游戏。在这些游戏中,程序会预测它可能采取的行动及其收益,以预测如果选择该行动的获胜概率。收益是代理人的效用,我们知道我们想要最大化我们的效用,而对手想要最小化我们的效用。因此,通过在可能的移动的树搜索中向前看(只是短暂地),什么决策将最大化代理的效用并同时增加获胜概率?
极大极小和阿尔法-贝塔剪枝
极大极小是博弈论中使用的一个决策规则,用来在假设你的对手最优/理性的情况下找到玩家的最优移动。两个玩家轮流成为最大化者和最小化者,在每个序列中,一个特定的移动可能会将对手置于最小化状态。当对手(最大化者)最小化你的效用时,最小最大算法是一个决策规则,用于最小化最坏情况下的潜在损失。但由于围棋的可能走法比宇宙中的原子更多,极大极小算法无法在完整的博弈树上成功完成其目标(Google AI,2016)。这就是修剪的用武之地。修剪是指你“剪掉”游戏树中“不”从第二个节点出现的分支。更简单地说,这是一种减少由极大极小算法在其搜索树中评估的节点数量的方法。还记得围棋的博弈树是如此之大,以至于从宇宙开始以来的所有时间都不足以进行完整的树搜索,因此极大极小算法不能成功地完成它的目标吗?阿尔法-贝塔剪枝法与蒙特卡洛树搜索相结合,可以扭转乾坤。回想一下,蒙特卡罗树搜索法只预测了每一步可能出现的序列中的极小一部分。通过并行运行 alpha-beta 剪枝算法,您可以获得一个高效的决策程序,该程序寻求效用最大化,并反过来增加获胜概率(或者至少我们希望如此)。
结论
DeepMind 的 AlphaGo 是近年来人工智能历史上最伟大的成就之一,这要归功于各种深度学习方法、计算机处理和计算机硬件的发展。随着人工智能、机器学习和强化学习等所有围绕 AlphaGo 的流行词汇的出现,一些对 AlphaGo 的成就做出贡献的博弈论概念经常被忽视。博弈论的概念和方法,如广泛形式的游戏,逆向归纳,效用最大化,极大极小和修剪,已经存在了几十年,看到它们在人工智能中的应用是令人振奋的。
参考文献
AlphaGo:目前为止的故事。(未注明)。检索于 2020 年 5 月 8 日,来自https://deep mind . com/research/case-studies/alpha go-the-story-迄今为止
第四章。用树搜索深度学习和围棋对弈。(未注明)。2020 年 5 月 8 日检索,来自https://live book . manning . com/book/deep-learning-and-the-game-of-go/chapter-4/39
新泽西州 cheer la(2018 年 1 月 1 日)。AlphaZero 解释道。2020 年 5 月 9 日检索,来自https://nikcheela . github . io/deep learning school/2018/01/01/alpha zero-Explained/
迪克西特,A. K .,斯凯斯,s .,,雷利,D. H. (2015)。战略游戏。纽约。:W. W .诺顿。
米切尔梅勒妮。(2020).人工智能:思考人类的指南。小号:皮卡多。
西尔弗博士和哈萨比斯博士(2016 年 1 月 27 日)。AlphaGo:用机器学习掌握围棋这个古老的游戏。2020 年 5 月 7 日检索,来自https://ai . Google blog . com/2016/01/alpha go-mastering-ancient-game-of-go . html
大英百科全书的编辑们。(2017 年 4 月 24 日)。走吧。2020 年 5 月 8 日从https://www.britannica.com/topic/go-game检索
游戏:数据集的金矿
如果算法可以被动学习,而不需要提供注释,会怎么样?—嗯,这正是游戏可以帮助我们的!
这篇文章讲述了我如何从一个项目中脱离出来,构建了一个东西或程序,而这个东西或程序是无人监督的(我猜同时也是有人监督的)。使用 GTA V mods,我为计算机视觉任务创建了一个数据集,由游戏中看到的对象组成。我还将展示一个小的物体检测器在现实世界中的表现。
TLDR,在游戏图像上训练的物体检测器,在真实世界的图像上概括得非常好。
本文分为以下几个部分:
- 机器学习导论
- 数据集对机器学习重要吗?
- 游戏和游戏玩家能帮助制作更好的数据集吗?
- 训练物体探测器
- 演示,显示对象检测的结果
我应该强调,这不是一个修改 GTAV 的教程,我不能解释和提供代码(也许以后,有一天当我有更多的时间)来使用 GTAV API 获取游戏世界信息。你可以在一个单独的类上玩物体检测器的小演示可以在这里找到。这个指南可以帮助你开始在侠盗猎车手 v 中进行改装,如果你想更深入地从游戏世界中提取信息,这个工具可以作为起点。
来源:https://img.youtube.com/vi/Usx-Cf8TlNI/0.jpg
机器学习导论
如果你有机器学习的先验知识,那么你可以跳过这一节。因为这篇文章不是关于学习机器学习,而是关于一个应用,所以我不能在这里深入讨论机器学习的概念。以下是一些很棒的文章,可以帮助你理解这个计算机科学领域:
如果我是 ELI5(像我五岁一样解释)机器学习是人工智能的一种形式,其中程序或算法被设计成能够自己学习。像人类一样,被称为神经元的小逻辑单元(下图中的圆圈)帮助算法从经验中学习(或从计算机、数据的角度来说)。算法中的参数会改变并学习数据集中的模式。
这个视频是学习机器学习的一个很好的起点。https://www.youtube.com/watch?v=aircAruvnKk&list = plzhqobowtqdnu 6 r 1 _ 67000 dx _ ZCJB-3pi
数据集对机器学习重要吗?
数据是任何学习形式的基本必需品。我们人类在经历或阅读/听到他们的经历后,学会了感知或理解行为的能力。数据集,不管有没有标记,都是所有机器学习任务的基础。
为了训练机器学习模型,需要大量数据。俗话说:“人越多越开心”,这句话再正确不过了。当前最先进的(SOTA)机器学习模型需要海量数据,例如,许多 NLP 任务的伯特、SOTA 都是在数十 GB 的数据上训练的。
Imagenet 一个标准的图像分类数据集大约有 144 千兆字节的压缩数据和> 300 千兆字节的未压缩数据。不用说,被标记的数据对于人工智能的进步是非常重要的。
在处理一个项目时,我遇到了一个问题,我使用的对象检测器无法识别图像帧中的所有对象。我试图对图片框中的所有对象进行索引,这将使图片搜索变得更加容易。但是所有的图像都被标记为人类,无法检测图像帧中的其他物体,搜索没有像我希望的那样工作。
这个问题的理想解决方案是收集这些对象的数据,并重新训练对象检测器来识别新的对象。这不仅无聊而且耗时。我可以使用 GANs ,这是一种机器学习算法,以其创建人工和类似的输入示例而闻名,在手动组织一些样本后创建更多样本,但这也很无聊,需要资源来训练 GANs 生成更多数据。
现在我唯一能做的就是使用互联网服务,比如 ScaleAI 和谷歌云平台,来创建一个数据集。但是这些服务对每张带注释的图片收费 0.05 美元。为 15 个类别创建一个 100 张图片的数据集意味着我将不得不为这个数据集花费 120 美元。仅仅为了一个简单的项目而花费这么多美元不是一个可行的选择。
游戏和游戏玩家能帮助制作更好的数据集吗?
在所有选项中,我开始希望如果有一个人造的世界,所有的物体都已经有了各自的标签。这样我就不必花时间搜索样本,然后小心翼翼地为它们生成注释。然后我意识到游戏世界是这种人工或虚拟世界的最好例子。当世界被创建时,游戏引擎拥有所有必要的信息,它知道哪些纹理位于哪里,并且知道所有其他物体的位置。看看游戏的当前状态,开发者正在挑战极限,创造与现实世界几乎没有区别的游戏环境。PS5 上的虚幻引擎 5 演示展示了下一代游戏机将夸耀的图形保真度。拥有一个与真实世界非常相似的虚拟世界,应该是测试和学习算法的一个很好的模拟。允许算法访问游戏世界的信息,让它了解角落和缝隙,这将是一件很棒的事情。
我想到的第一个游戏是神秘海域 4:盗贼末路。
F
一个令人惊叹的游戏,有一个很好的故事,它有所有的元素,我们想从测试环境。它拥有所有的自然地形类型:平原,高原,洞穴,海岸,它还拥有近乎照片般逼真的图形,这意味着物体探测器将学习更接近真实世界的特征和形状。但问题是,我在大学里没有 ps4,也没有办法以我喜欢的方式访问未知的资源。
游戏的另一个选择可以满足我的要求:
- 摄影现实主义
- 修改界面以允许访问游戏世界数据
《侠盗猎车手:v》的基地虽然已经有 7 年的历史了,但与大多数其他游戏相比,它更像真实世界。这个游戏有广泛的支持 mods 和作出改变/与游戏世界互动。这个游戏,在使用了一些 mods 之后,可以用它所有的灯光和倒影看起来非常惊人
GTA V 社区做出了一些令人愤慨的修改,其中默认的玩家模型被自定义模型所取代。这是一件非常非常棒的事情,我稍后会继续讨论。
摄影现实主义
大雄作为玩家模型,lol
我首先阅读了一些关于使用 Rage Engine(GTA 使用的引擎)的 API 的文章和指南。很少有好的例子或指南来帮助我实现我想要达到的目标。但幸运的是,有一个惊人的工具做了一些接近我试图实现的事情,它给出了用户当前图像帧中许多对象的位置。
使用地图工具打开用户界面
这个工具最棒的地方在于它的源代码可以在 Github 上找到。有了这个坚实的起点后,任务就归结为如何从游戏世界中访问对象信息,比如它的位置和类型。这是在长时间阅读代码、编译错误代码之后实现的。我可以从游戏屏幕上记录结果。我从简单的日志开始:
# the first log file that was created script running inside of GTA V
[{
"label": "car",
"location": [
[248.42533936651586, 173.41176470588235],
[442.5429864253394, 278.841628959276]
],
"group_id": "non-living"
},
{
"label": "trevor",
.....
"group_id": living"
},
{
"label": "cat",
.....
"group_id": living"
}]
有些事情很简单,比如获得命名对象的位置,比如使用哈希单独识别的字符。渐渐地,我学会了与 API 交互,以获取行人和附近行驶的汽车等物体的位置。我无法完全破解游戏世界来获得我想要的所有物体的位置信息,但这是一个很好的起点。最初的结果看起来很有希望,当检测到物体时,脚本会对游戏进行截图,并保存物体的位置。结果看起来像这样:
从游戏世界获得的图像坐标用于创建边界框。额外:我曾经用 CannyEdgeCropping 来使边界框更精确。
训练物体检测器
有了这些令人满意的结果,我继续创建更多的样本。我决定用 14 个物体:
- 类人:行人或人类、狗和猫(3)
- 有生命但不像人的东西:树(4)
- 非生物:汽车、卡车、自行车、火车、船、交通信号、广告牌(6)
在获得足够的数据来训练模型之后。我开始在我的大学服务器上训练一个 RetinaNet 模型,这是一个中等强大的机器,配有 Quadro P5000 和英特尔至强 G6132。选择 RetinaNet 没有特别的原因,我有使用它的经验。我从头开始训练网络,因为使用预先训练的权重会扼杀这个项目的想法,即使用游戏世界的知识并将其应用到现实世界中。
经过一小时又一小时的等待,我意识到看着缓慢的训练进度不会加快速度,决定今天到此为止,睡觉。在漫长的等待之后,训练结束了,表演时间到了。在由游戏图像组成的测试集上测试该模型。结果似乎是公平的:
结果似乎很好,但我不明白正确的汽车是如何检测出来的。绿色是汽车,红色是人。圆圈是坐标。
结果和预期的一样,现在是在真实对象上测试性能的时候了。我认为从结果来看,这个实验可以说是成功的:
我讨厌物体探测器没找到谢尔顿和拉杰什
模特再次表现良好
大多数物体都被很好地识别了,但是我注意到了一个错误:
我认为这是因为游戏中缺少猫和狗的不同玩家模型。该模型能够学习数据集中不同对象的形状,但由于猫和狗的样本种类有限,这些类的情况并不顺利。它不能学习让它区分狗和猫的特征。狗和猫的模型数量是有限的,修改和添加新的模型可以弥补这种情况。
演示
该模型在图像上表现良好,但不要相信我的话。我创造了一个 colab 笔记本,你可以在 5 分钟内训练你自己的模型从图像中识别人类。为了保持尽可能低的运行时间,笔记本只有来自游戏的 200 个人体样本的小数据集。这种低数量的样本足以显示这种模型的潜力。
测试集上的检测
物体的小贴图和它们的纹理位置,这些部分目前有点奇怪。仍然需要做一些数学方面的工作。
在启动并运行了对象检测之后,我想让语义分割工作起来,因为这是自动标注的真正潜力所在。利用游戏引擎的纹理知识来获得像素级分割。我还没想好怎么从 GTA V 用的游戏引擎 RAGE engine 获取基于纹理的信息,获取物体的位置是一回事,获取物体的位置和区域覆盖又是另一回事。后者是我目前的困境。
我相信这一点的潜力是巨大的,通过使用 mods,我们可以很容易地用那些我们想要建立探测器的东西来替换玩家模型。在这项技术中,我们可以替换一个简单的布娃娃的角色模型,该模型可以作为创建一个在各种环境中角色外观的大型数据库的基础。
示例:此处的玩家模型由海鸥模型替换而来。来源:维基百科
随着我的 GSoC 选择,我的时间减少了,开发环境也改变了。我希望将来能回到这个项目中来。我计划上传所有图片及其标签的 31GB 数据集,但目前这是不可能的,因为我大学的互联网服务慢得令人难以忍受。
我在这里的下一个博客将是我正在进行的另一个项目的开发日志,或者是 GSoC 博客的启动博客,我将在未来 3 个月内发布这些博客,以展示我在 CERN 的工作进展。
游戏新冠肺炎:让我们通过玩游戏打败病毒!
在一个封闭的世界里,无助感可能会压倒一切,但我们都可以在自己舒适的家中用电脑打败病毒
编者按: 走向数据科学 是一份以数据科学和机器学习研究为主的中型刊物。我们不是健康专家或流行病学家,本文的观点不应被解释为专业建议。想了解更多关于疫情冠状病毒的信息,可以点击 这里 。
照片由克里斯蒂娜@ wocintechchat.com在 Unsplash 上拍摄
全世界都在担心冠状病毒的传播。我们被锁在家里,感到无能为力。计算机让我们与外界保持联系,并让我们保持理智(点击此处,了解在禁闭期间可以做的有趣的人工智能活动)。但是我们能做更多来帮助科学界吗?
答案是,可以!每个人都可以尽自己的一份力量来帮助医学研究人员找到疫苗,阻止病毒的传播。好消息是我们不需要成为生物医学科学家。你所要做的就是成为一名公民科学家(公众参与和合作科学研究以增加科学知识的实践)。
为此,普通人自愿分享和贡献数据监测和收集项目。在现实中,一个人不需要太多的参与!在冠状病毒的研究中,我们所要做的就是玩一个免费的游戏,这个游戏在 https://tinyurl.com/BeatCOVID-19 叫做 Foldit。
这个游戏的工作原理是给用户一个难题,他们必须解决它。潜在的想法相当简单。我们都知道我们的身体由各种细胞组成,这些细胞需要蛋白质来分解食物,发送信号和其他东西。这些蛋白质是由成排的元素(如碳、氧、硫等)组成的。)都像链子一样互相牵着。然而,链条不是在一条直线上,而是折叠起来,使其紧凑。如果不同的蛋白质具有匹配的形状,它们就会相互作用。把它想象成一把锁和一把钥匙相互作用。只有正确的钥匙才能打开特定的锁。
冠状病毒 Foldit 游戏显示了冠状病毒蛋白质的一部分,该部分通常与人类相互作用。玩家必须设计一种折叠蛋白质来匹配冠状病毒,并阻止这些相互作用。这项任务在现实生活中可能相当复杂,因为一个蛋白质可能由数百种元素组成。尽管如此,游戏把它表现为一个简单的拖拽任务。Foldit 试图通过利用人类的解谜直觉来预测蛋白质的结构,并让人们竞争折叠最佳蛋白质。
然后,华盛顿大学将测试最有希望的解决方案,作为新冠肺炎可能的现实世界疫苗。一些人对这种方法表示怀疑,但这种方法在 2011 年 Foldit 播放器帮助研究人员解码艾滋病病毒时确实有效。
找出哪种可能的结构是最好的被认为是生物学中最困难的问题之一。目前的方法花费大量的时间和金钱,即使对计算机来说也是如此。如果游戏不适合你,你仍然可以通过允许研究人员使用你的电脑来帮助他们。人们只需下载一个名为 Folding @ home(https://tinyurl.com/BeatCOVID-19Home)的小应用程序,并让它在后台运行,就可以做到这一点。然后,这个程序将把你的计算机连接到一个国际网络上,这个网络使用分布式程序来完成大量的计算任务。你仍然可以在任何时候关闭应用程序,但是如果计算机处于闲置状态,也可以让科学家使用它的能量。
一旦他们拥有了这种计算能力,科学家们就可以使用人工智能来创建算法来解决折叠问题。这些公司中最新的一家是谷歌的 DeepMind,它刚刚发布了一个名为“自由建模”的工具,以帮助科学家预测以前从未见过的蛋白质的蛋白质结构。他们希望减少预测蛋白质结构的时间,因为这种实验通常需要在实验室进行数月。通过这样做,他们希望阻止病毒的传播并拯救一些生命。
Saneej Kallingal 在 Unsplash 上拍摄的照片
在一个封闭的世界里,无助感可能会压倒一切。但是今天的机器可以创造奇迹。它们可以帮助我们直接或间接地为科学的进步做出贡献。这是我们的选择。最棒的是它不需要任何医学专业知识。所以,当我们都在家的时候,为什么我们不通过下载这些应用程序来积极地帮助对抗新冠肺炎呢?
这篇文章最初发表于 https://www.businesstoday.com.mt请在下面留下你的想法,如果你喜欢这篇文章,请随时关注我🐦推特,🔗 LinkedIn 或者😊脸书
阿列克谢·丁力教授 是马耳他大学的 AI 教授。二十多年来,他一直在人工智能领域进行研究和工作,协助不同的公司实施人工智能解决方案。他的工作被国际专家评为世界级,并赢得了几个当地和国际奖项(如欧洲航天局、世界知识产权组织和联合国等)。他出版了几本同行评审的出版物,并且是马耳他的一部分。由马耳他政府成立的人工智能工作组,旨在使马耳他成为世界上人工智能水平最高的国家之一。
甘 2020 年要读的论文
生成性对抗网络的阅读建议。
甘游戏
生成对抗网络(GANs)是近十年来提出的最具创新性的想法之一。其核心是,GANs 是一个从一组相似元素生成新元素的无监督模型。例如,在给定人脸图像集合的情况下产生原始人脸图片,或者从预先存在的旋律中创建新的曲调。
GANs 已经发现了图像、文本和声音生成的应用,是人工智能音乐、深度假货和内容感知图像编辑等技术的核心。除了纯粹的生成,GANs 还被用于将图像从一个领域转换到另一个领域,并作为一种风格转移的手段。再增加一个应用,它们适合作为半监督学习的智能数据增强技术。
在这篇文章中,我选择了 10 本 2020 年前最令人大开眼界的读本。特别是,我选择了五篇论文,它们扩展了在哪里和如何使用 GANs 的视野,以及五篇解决训练 GANs 的具体技术挑战的论文。
至于我以前关于阅读建议的文章,我提供了每篇文章的简要概述和一系列阅读它的理由,加上一些关于相同或相关主题的进一步阅读的想法。
我们走吧。
排名第一的 GAN (2014)
伊恩·古德菲勒等人《生成性对抗性网络》 神经信息处理系统的进展。2014.
2014 年,Ian Goodfellow 和他的朋友在一家酒吧讨论如何使用人工智能合成图像。典型的边喝啤酒边聊。当他的朋友们在讨论统计方法时,他主张使用两个神经网络来共同学习“如何绘画”和“判断绘画”。前者在后者的反馈上训练,后者训练从假图像中识别出真实图像,就是为了痛击前者。
最初,这个想法遭到了批评:训练一个网络是困难的,训练两个是疯狂的。尽管如此,带着醉意,古德菲勒回到家,连夜编写了代码。令他惊讶的是,它成功了。
甘游戏
理由#1: 好点子随时随地都能冒出来。携带一个 notes 应用程序。
理由 2: 和任何“经典论文”一样,阅读它会把我们带回到一个并非我们今天使用的所有东西都存在的时刻。2014 年流行哪些深度学习技术?哪些不是?
原因#3: Goodfellow 使用 MNIST 和 CIFAR-10 数据集演示了 GANs。更大规模的数据集(和分辨率)需要一些时间才能被驯服。如果他从 ImageNet 开始,他会把 GANs 作为一个坏主意扔掉。这篇论文提醒我们永远不要低估玩具数据集上提出的创新也是玩具。任何想法都有可能在两三篇论文之外取得突破。
延伸阅读:虽然 gan 是生成性任务中最成功和最容易使用的方法,但它们不是唯一的方法。两个相关的选择是变分自动编码器(VAEs) 和自回归模型。两者都值得一试,而且都有其优劣势。
第二名 StyleGAN (2019 年)
卡拉斯、泰罗、萨穆利·莱恩和蒂莫·艾拉。“基于风格的生成式对抗网络生成器架构。”IEEE 计算机视觉与模式识别会议论文集。2019.
五年后,GANs 现在能够生成在大规模语料库上训练的高分辨率可定制画像。虽然从 2014 年到 2019 年提出了许多新颖的想法,它们当然启发了作者的许多决定,但这一切都归结于架构、AdaIN、Wasserstein loss 和小说数据集: Flickr-Faces-HQ 数据集(FFHQ) 。
该论文的核心思想是在每一级上采样中输入不同的噪声矢量。这与大多数以前的工作形成了鲜明的对比,以前的工作只把噪音作为第一步。通过调整每个分辨率的噪声向量,作者可以通过篡改较低级别的噪声来控制“高级细节”,并通过篡改较低级别的噪声来控制“低级细节”。
对斯泰尔根建筑的直觉。在不同的阶段应用不同的噪声来控制“风格”,而中央“主要”噪声被用作“本质”。由克里斯托弗·坎贝尔在 Unsplash 上拍摄的肖像
原因#1: 虽然每年在深度学习方面做出的贡献数量巨大,但一些论文显示,只需要其中的一小部分就可以实现最先进的结果。这篇论文就是一个很好的例子。
理由#2: 本质上,这项工作的主要贡献是一种运用噪音的新颖方式。不涉及任何重要的新算法或数学公式。事实上,从第三页开始就是评估页。
原因#3: 另一方面,他们必须收集整个数据集,并在八个 Tesla V100 GPUs 上运行整整一周。在 AWS 上, p3.16xlarge 实例的这种用法大约是 4000 美元。技术上的贡献可能是巧妙的,但达到这一点的计算成本相当高。
延伸阅读:2019 年底,一篇后续论文出炉,修剪了模型的几个边缘。然而,更令人印象深刻的是其他作者使用 StyleGAN 作为工具独立开发的工作。我最喜欢的是 Image2StyleGAN 。作者的想法是训练一个网络来找到一张脸的相应噪声,然后使用 StyleGAN 将其生成。通过这种方式,网络可以用来设计预先存在的图像,就像它们是任何其他生成的图片一样。
排名第三的 Pix2Pix 和 CycleGAN (2017 年)
Isola,Phillip,et al .使用条件对抗网络的图像到图像翻译IEEE 计算机视觉与模式识别会议论文集。2017.
朱,严军,等.“使用循环一致的对立网络进行不成对的图像到图像的翻译”IEEE 计算机视觉国际会议论文集。2017.
最初的 GAN 公式是无人监督的:图像是凭空产生的。然而,许多作者设计了监督公式,其中一些先验知识给网络。这些被称为“有条件的”方法。其中,Pix2Pix 和 CycleGAN 架构最为人所知。
Pix2Pix 模型处理诸如将线条画转换成成品画的问题,允许用户通过改进/改变他们的草图来进行某种程度的艺术控制。反过来,CycleGAN 模型放松了对成对训练样本的需要。它最著名的用途是用马代替斑马,用苹果代替橙子。然而,将堡垒之夜变成 PUBG 无疑对年轻一代更有吸引力。
与传统的无条件方法不同,Pix2Pix 算法支持“艺术控制”。
原因#1: 随着 GANs 质量的提高,艺术控制变得更加令人兴奋。条件模型为 GANs 在实践中变得有用提供了途径。
原因# 2:pix 2 pix 背后的主要组件是 U-Net 架构,它最初是为生物医学图像分割而提出的。这凸显了深度学习的许多应用如何相互促进。你可能不是 GAN 研究人员,但一些 GAN 创新可能正是你现在需要的。
原因 3:cycle gan 的论文特别展示了一个有效的损失函数是如何在解决难题时创造奇迹的。至于第二点,通过不同的连接方式可以改进很多。
延伸阅读:如果你从来没有编码过 GAN,我强烈推荐你。它们是深度学习深度学习的绝佳练习:)。TensorFlow 2 文档中有三篇关于构建原始 GAN 、pix 2 pix 模型和cycle GAN的优秀文章。
#4 半监督学习(2016)
Salimans,Tim,et al. “训练 gans 的改进技术” 神经信息处理系统进展。2016.
虽然生成东西通常是焦点,但是 GANs 也可以作为辅助工具来改进其他任务。其中一个用途是半监督学习:当大量未标记的数据可用,但只给出一小组标记的例子时。换句话说,如何利用未标记的数据。
本文给出了用 GAN 训练分类器的基本公式。本质上,分类器被联合训练来对图像进行分类并检测假货,而生成器则努力生成不被视为假货的可分类照片。这样,未标记的数据可以用于训练生成器,生成器进而为分类器学习提供更多数据。
理由#1: 半监督学习是降低数据成本最有前途的想法之一。许多领域,比如医学成像,注释起来非常昂贵。在这种情况下,这种提法可能会省钱。这个想法是由图灵奖获得者 Yann LeCun 倡导的。
原因 2: 我们大多数人都熟悉数据扩充的概念,比如添加随机裁剪、缩放、旋转和镜像。在这方面,GANs 可以作为“美食数据补充”
原因#3: 虽然这项工作并没有直接关注它的哲学,但据说“如果你能创造出某种东西,你就知道它的一切”。这是生成学习被认为对高级智能至关重要的主要原因之一。本文是 GANs 如何帮助其他任务的一个例子。
原因#4: 除了关注半监督学习,本文还分享了一套改进和评估 GANs 的技巧。其中,他们提出了广泛使用的 Inception Score,这是评估 GANs 的主要指标之一。
延伸阅读:强烈推荐观看三位转折奖获得者的 AAAI 20 主题演讲。主题是 CNN 的未来、半监督学习和神经网络推理。我一直在我的大多数文章中推荐这个演讲,我怎么强调观看顶级主题演讲的重要性都不为过。阅读论文很重要,但听作者说些什么是另一半。
#5 动漫甘(2017)
金,,等.“基于生成对抗网络的动漫角色自动生成研究” arXiv 预印本 arXiv:1708.05509 (2017)。
这个列表中的第五个条目,非正式地说,是 AnimeGAN。所有以前提出的方法都处理自然图像。不过,GANs 在其他领域也可以大有用武之地,比如外服生成、精灵完成、漫画风格化、深度假货等等。虽然这篇论文并不特别有名,或者不如这份名单中的其他论文那样知名,但它很好地展示了除了面孔和汽车之外,还有许多探索的途径。
作为艺术控制手段的半监督学习。用 make.girls.moe 生成
理由#1: 甘并不局限于自然的形象和好看的人。生成方法可以用于各种各样的任务(远远超出图像领域)。
原因#2: 本文展示了标记数据集的使用,可以利用它来指导生成器选择特定的发型或眼睛颜色。公式几乎与半监督学习中的公式相同。然而,在这种情况下,我们并不关心训练好的分类器。
延伸阅读:GANs 虽然进化很快,但很多成果都停留在学术界。当模型作为交互工具公之于众时,这是件好事。很多东西只能现场学习,比如把算法逼到极致,或者玩弄不寻常的选择。下面是一些有趣的链接: Pix2Pix , AnimeGAN , StyleTransfer , SketchRNN , AutoDraw ,以及art breader。
到目前为止,所展示的作品已经阐述了什么是 GANs,以及如何应用它们来解决计算机视觉中的几个任务。另一方面,下面的工作集中在训练和实现 GANs 的技术方面,例如新的损失和正则化技术
第六名沃瑟斯坦损失(2017 年)
Arjovsky、Martin、Soumith Chintala 和 Léon Bottou。《瓦塞尔斯坦甘》 arXiv 预印本 arXiv:1701.07875 (2017)。
自首次亮相以来,gan 就以难以训练而闻名:大多数时候,生成器无法生成任何有用的东西,或者一遍又一遍地生成相同的东西(模式崩溃)。虽然多年来已经提出了许多“技巧”,但瓦瑟斯坦损失是第一个成为主流并通过时间考验的原则性方法之一。
总的想法是用一个批评家来取代 de discriminator,它判断图像的真假,而批评家只是为每张图像给出一个分数。与法官不同,批评家是无界的;它可以给出任何分数:一分、负三分、一百分等等。在训练过程中,评论家训练尽可能地将假货与真货区分开来,而生成器则训练尽量缩小这种差距。
非正式地说,这种方法的主要优点是它提出了一个不稳定的问题。批评家可以改变自己的观点,降低真实图像的分数,或者提高虚假图像的分数。这是一场猫捉老鼠的游戏。生成器总是在追逐 reals 的分数,reals 总是在逃跑。
沃瑟斯坦损失取代了批评家的鉴别器。
原因#1: 大多数论文都是以温和的介绍开头;这张纸从 0 到 100 真的很快。它的大胆值得一读(如果你热爱数学,这本书正适合你)
理由#2: 没有什么是完美的。虽然许多报纸试图掩盖它们的缺点,但其他报纸热衷于强调和承认它们。这应该得到称赞,因为它邀请其他人做出贡献。引用论文:
重量剪裁显然是一种很糟糕的实施 Lipschitz 约束的方式
原因# 3:2017 年后出版的许多著名的 gan 使用了 Wasserstein loss 和一些现在在第二个版本中没有使用的内容。这无疑是该领域的一篇里程碑式的论文,任何从事这方面工作的人都应该阅读。
延伸阅读:虽然这份文件或多或少已经成为训练 GANs 的首选文件,但它几乎不是唯一使用的文件。两个值得特别注意的是感知和总方差损失。前者使用冻结的 VGG 网络作为感知相似性的度量,而后者惩罚图像梯度,鼓励平滑。
#7 光谱标准(2018)
Miyato,Takeru,et al. “生成对抗网络的谱归一化” arXiv 预印本 arXiv:1802.05957 (2018)。
Wasserstein 损失以要求梯度为 1-Lipschitz 为代价显著改善了收敛。为了加强这一点,作者建议将权重削减到[-0.01,0.01]。后来,Miyato 等人引入了一种更优雅的方法:光谱归一化。
本文的主要思想是将权重矩阵的最大特征值约束为 1,这反过来保证了 Lipschitz 要求。为了保持较低的计算成本,作者还建议通过使用幂迭代来近似特征值计算,这是非常有效的。
原因#1: 正如上一篇文章中提到的,突出你的弱点会吸引其他作者来投稿。虽然本文的措辞并非如此,但它是对重量削减的直接回应。
原因 2: 正常化是一个比大多数人意识到的要大得多的话题。许多选择属性可以通过专门的规范和仔细的激活功能设计来执行,例如 SELU 激活功能。
原因#3: 这除了是一种范数,也是一种正则化,是神经网络中经常被忽略的话题。除了辍学之外,阅读关于这个问题的成功论文令人耳目一新。
延伸阅读:归一化技术的其他最新进展是组归一化和自适应实例归一化技术。前者解决了小批量批量标准的一些缺点,而后者是任意风格转换的关键突破之一。后者也用于#2,以将数据流调节到噪声向量。
#8 自我关注甘(2018)
张,韩,等.“自我注意生成性对抗网络” arXiv 预印本 arXiv:1805.08318 (2018)。
尽管文本和图像处理的整体创新大相径庭,但偶尔也会相互启发。来自自然语言处理社区的注意力是你所需要的全部论文提出了注意力机制,这是一种向 CNN 添加全局级推理的方法。
本文将这一概念引入到 GAN 领域,作为提高图像质量的一种手段。概括地说,自我注意机制计算“每个像素对其他像素的看法”因此,它允许网络在全局级别的关系上进行推理。这是通过取展平图像自身的外积来实现的。
自关注机制计算从每个像素到每个其他像素的分数。
原因#1: 注意力正在深度学习领域掀起风暴。在 2020 年,每个领域都有一个基于注意力的解决方案。甘也不例外。
原因#2: 获取展平图像的外积非常耗费时间和内存,这限制了这种技术在一些低分辨率层上的应用。这在两代 GPU 之前是不可能的。从现在开始的两代人,我们将会看到哪些耗电的方法?
延伸阅读:我强烈推荐阅读关于注意力机制的原创论文,因为现在它几乎无处不在。从某种意义上说,它向世界展示了全球层面的推理可以带来什么。然而,它是非常昂贵的。出于这个原因,许多团队正在研究更有效的方法来吸引注意力。一种这样的方法是重整器,高效变压器。如果你对我对这两篇论文的看法感到好奇,我已经在我的人工智能论文(2020 年阅读)的文章中涉及了它们。
#9 边界平衡氮化镓
贝特洛、大卫、托马斯·舒姆和卢克·梅斯。“开始:边界平衡生成对抗网络。” arXiv 预印本 arXiv:1703.10717 (2017)。
大多数 GAN 问题归因于发生器和鉴别器网络之间缺乏平衡。在这篇论文中,谷歌的研究人员提出了一种方法来加强两个参与者之间的平衡,并提出了一种收敛度量来评估发电机在各个时代的演变。此外,通过调整平衡,作者能够权衡图像质量的多样性。
理由#1: 提议相当激进:鉴别器是自动编码器,我们比较编码真假图像的损失。
原因#2: 很难判断 GAN 是否在改进,因为在达到某个质量水平后,人工检查是不可靠的。有一个简单的计算收敛指标是非常方便的。
理由#3: 除了均衡思想,其他都很标准。这显示了如何准确地建模一个问题可以创造奇迹。不需要花哨的图层、规范,也不需要昂贵的操作。
延伸阅读:同样, BigGAN 的论文表明,仅仅使用更大的模型和四到八倍的批量就可以极大地提高图像质量。类似地, ProGAN 论文显示,先用小分辨率进行训练,然后逐步加倍,可以帮助达到更高的分辨率,而无需改变模型。
#10 甘人生而平等吗?(2018)
Lucic,Mario 等人“甘人生来平等吗?大规模的研究。” 神经信息处理系统的进展。2018.
我把这一篇留到最后是有原因的:这是迄今为止对 GANs 最广泛的比较研究之一。之前的九个参赛作品都因其高质量的成果和技术突破而备受赞誉。然而,还存在许多其他配方。在本文中,你可以看看不太为人所知的想法,如最小二乘甘或德拉甘。
理由 1: 如果你从这么远的地方来,你很可能会喜欢上甘斯。有什么比一份调查报告更值得继续阅读呢?
原因#2: 本文有一个方便的表格,列出了七种不同配方的发生器和鉴频器损耗。如果实现 GANs 不是你的计划,这个表仍然是一个数学阅读练习。
原因#3: 作为一项比较研究,本文中有几段专门介绍了如何对 gan 进行定量比较,以及进行公平比较所面临的挑战。
延伸阅读:关于比较 GANs 这个话题,初始得分 (IS)和弗雷歇初始距离 (FID)是广泛使用的图像集之间相似性的度量。Sajjadi 等人提出了一种不太为人所知的方法,该方法分别采用了质量和多样性的精度和召回分数度量。理解如何比较甘和甘本身一样重要。
我希望这本书对你和我来说都是令人兴奋的。请让我知道你认为符合这个列表的其他文件。我将很高兴阅读和考虑他们的未来列表。😃
如果你喜欢这篇文章,并想继续下去。我还写了 2020 年要读的 AI 论文和 2020 年要读的另外十篇 AI 论文。它们总共涵盖了 50 多篇其他文章(包括链接和延伸阅读)
欢迎评论或联系我。如果你刚接触媒体,我强烈推荐订阅。对于数据和 IT 专业人士来说,中型文章是 StackOverflow 的完美搭档,对于新手来说更是如此。注册时请考虑使用我的会员链接。你也可以直接支持我请我喝杯咖啡😃
感谢阅读:)
GAN Pix2Pix 生成模型
使用 Pix2Pix 模型进行图像到图像的翻译
Pix2Pix
Pix2Pix GAN:简介
我们听到很多关于深度学习的语言翻译,其中神经网络学习从一种语言到另一种语言的映射。事实上,谷歌翻译用它来翻译 100 多种语言。但是,我们能对图像做类似的工作吗?当然,是的!如果有可能捕捉错综复杂的语言,那就一定有可能将一幅图像翻译成另一幅图像。的确,这显示了深度学习的力量。
Pix2Pix GAN 论文早在 2016 年就由菲利普·伊索拉、、、周廷辉、阿列克谢·阿夫罗斯发表。在这里找到论文。后来在 2018 年进行了修订。当它发表后,互联网用户尝试了一些创造性的东西。他们将 pix2pix GAN 系统用于各种不同的场景,如模仿一个人的动作,将一个人的视频逐帧翻译给另一个人。很酷,不是吗?使用 pix2pix,我们可以将任何图像映射到任何其他图像,就像对象的边缘映射到对象的图像一样。此外,我们将详细探讨它的架构和工作原理。现在,让我们开始吧!
来源:GitHub
Pix2Pix GAN 如何工作?
cGAN:概述
听说过生成逼真合成图像的 GANs(生成对抗网络)吗?类似地,Pix2pix 属于一种称为条件 GAN 或 cGAN 的类型。他们有一些条件设置,并在这种条件下学习图像到图像的映射。而基本 GAN 从随机分布向量生成图像,不应用任何条件。迷茫?试着得到这个。
比方说,我们有一个用 MS-COCO 数据集的图像训练的 GAN。在 GANs 中,用发生器网络产生的输出图像是随机的。也就是说,它可以生成数据集中任何对象的图像。但是,有了 cGAN,我们可以生成我们想要的图像。如果我们想让它生成一个人,它会生成一个人的图像。这是通过调节 GAN 实现的。
Pix2Pix GAN:概述
让我们再举一个图像到图像转换任务的例子,即“黑白到彩色图像”的转换。在 pix2pix cGAN 中,B&W 图像作为生成器模型的输入。并且,所生成的模型的输出和给定的输入(B&W 图像)图像对是所生成的对(伪对)。B&W 输入图像和目标输出(即输入 B&W 图像的真实颜色版本)形成真实对。
鉴别器将给定的图像对分类为真实图像对或生成图像对。Pix2Pix 中使用的那个与我们通常期望的分类器输出不同。它生成一个输出分类,对输入图像对中的多个面片进行分类(patchGAN)。我将详细解释它。在下面的描述中,连接被表示为⊕.
Pix2Pix GAN 架构
Pix2Pix GAN 有一个生成器和一个鉴别器,就像普通 GAN 一样。对于我们的黑白图像彩色化任务,输入 B&W 由生成器模型处理,它生成输入的彩色版本作为输出。在 Pix2Pix 中,生成器是一个 U-net 架构的卷积网络。
它接收输入图像(B&W,单通道),将其通过一系列卷积和上采样层。最后,它产生一个输出图像,其大小与输入相同,但有三个通道(彩色)。但是在训练之前,生成器只产生随机输出。
在生成器之后,合成图像与输入 B&W 图像连接。因此,颜色通道的数量将是四个(高 x 宽 x 4)。该级联张量作为输入馈入鉴别器网络。在 Pix2Pix 中,作者采用了一种不同类型的鉴别器网络(patchGAN 类型)。patchGAN 网络采用连接的输入图像,并产生大小为 NxN 的输出。
损失函数
鉴频器损耗
鉴别器损失函数衡量鉴别器预测的好坏。鉴别器损失越小,识别合成图像对就越准确。
GANs 中使用的普通二进制分类器只产生一个输出神经元来预测真假。但是,patchGAN 的 NxN 输出预测输入图像中的许多重叠面片。例如,在 Pix2Pix 中,输出大小为 30x30x1,可预测输入的每个 70×70 面片。我们将在另一篇文章中看到更多关于 patchGANs 的内容。30×30 输出馈入对数损耗函数,该函数将其与 30×30 零矩阵进行比较(因为它是生成的而不是真实的)。
合成图像对丢失
这就是所谓的发电损失。从数据集中计算 B&W 对及其相应的彩色图像的真实损失。这是真的一对。因此,“实际损失”是 NxN 输出的 sigmoid 交叉熵和一个 NxN 大小的矩阵。
鉴频器总损耗是上述两种损耗的总和。损失函数的梯度是相对于鉴别器网络计算的,并且被反向传播以最小化损失。当鉴别器损耗反向传播时,发电机网络的权重被冻结。唷!现在我们差不多完成了。
鉴别器的损失函数
发电机损耗
发生器损耗衡量合成图像的真实程度。通过最小化这个,生成器可以产生更真实的图像。
发电机损耗功能
这种损耗几乎与生成损耗相同,只是它是 NxN 鉴频器输出的 sigmoid 交叉熵和一个矩阵。当这种损耗反向传播时,鉴频器网络的参数被冻结。并且只调整生成器的权重。
为了提高生成图像的美观性,pix2pix 论文的作者添加了一个 L1 损失项。它计算目标图像和生成图像之间的 L1 距离。然后乘以参数“λ”,并添加到发电机损耗中。
训练 Pix2Pix 模型
为了训练模型将 B&W 图像转换为彩色图像,我们必须向网络提供输入和目标图像。因此,可以使用任何带有彩色图像的数据集,如 ImageNet。数据集可以通过将彩色图像转换成 B&W 来构成输入。并且彩色图像本身形成目标。因此,可以通过迭代数据集、将图像一个接一个地或成批地馈送到 pix2pix 模型来训练网络。
就这样…完成了!
那是给你的 pix2pix!希望你对 pix2pix GAN 是什么以及它是如何工作的有一个清晰的认识。
了解如何在自定义数据集上训练 cGAN 模型。
1。如何用 5 个简单的步骤训练一个人工智能 cGAN 模型
安钢:如何培养大脑
~gan 是类比机器,它得到 meta ~
TL;博士——如果你有两个神经网络,每个用于一个不同的任务,那么你有时可以找到一个 GAN,它将一个任务 转换为另一个任务。类比!因此,找到GAN,它‘GAN’成对的 GAN,你就可以增长层次抽象。使用这些 gan 在观察到的数据分布之外做出预测——类比让你零射击新任务。
我应该从一个具体的例子开始:
扔石头
假设你有一个已经训练好的观看视频的神经网络。它观察了几千个小时的人们拿着、扔着和扔着棒球。你的网络很擅长预测抛物线轨迹。但是,只见过棒球做那种事情。你给它看一个有人扔石头的单视频,神经网络就丢了。可恶。
但是,等等!如果你能训练出一只能拍下扔石头视频每一帧的狗,然后 把石头转换成棒球 ,那么你就又是金牌了!当然,获得更多摇滚视频可能更容易,但当你试图解决在线学习时,这个问题变得不可克服,特别是对于长尾事件,以及更具适应性、更安全的神经网络所必需的泛化能力。
这里的关键概念是:采用一个众所周知的任务(棒球轨迹),将其与 GAN 翻译器(摇滚到棒球)结合,从而通过类比做出合理准确的预测。(实际上润色视频的每一帧,使摇滚看起来像棒球,可能太笨拙了。你只需要一个 GAN 来做任何让 rock-translator 网络具有预测性的事情;神经网络生成的训练机制的发展表明,在我们看来,最佳类比转换可能看起来。)
可以把甘的类比推理能力看作是他自己的一块小石头。因为我们正在用这些东西建造一座大教堂…
甘的甘
假设你一直在用这些 gan 在数百个不同的任务之间形成类比。每一个任务对,你都要检查是否有一个紧凑而准确的‘GANalogy’通常情况下,除非你让你的神经网络变得庞大,以便它能记住所有的东西,否则你无法获得一个像样的 GAN。那些是在感觉层面上实际上不同于的任务。忽略那些;如果 GAN 看起来像他们中的一员,就不要费心全程训练它了。
然而,即使 GAN 模拟网络缩减到几百个神经元,仍有一些任务运行得很好。那些甘人是守护者。一旦你把所有你能理解的 GAN 都弄懂了,你就可以说:“对于这些两个GAN,有 GAN 翻译吗?”这些 GanGANs 会是像“(摇滚到棒球)作品像(雕塑到人)”和“(电场到磁场)作品像(矩阵到矩阵)”这样的东西。 真实、有用的比喻 。包括任务到 GAN 和低层到高层 GAN。每一个对!**
一层一层,向上到更大的抽象,找到工作的 gan。当然,它们中的许多都是多余的——例如,你可能有一个棒球到岩石的 GAN、棒球到苗条和苗条到岩石的 GAN……冗余让你 检查 你的工作,类似于专家神经网络模型的混合。更重要的是,如果几个不同的答案路径都给了相同的结果,它们更有可能是正确的答案,因为**的错误往往会使与互不相同,而事实是与*** 一致。类似地,自我一致性作为唯一的约束最近仅从视频中生成了近 SOTA 无监督深度感知。***
永远记住细节
我有理由相信,你永远不会想要明确地组合原始的感觉网络,这是一个错误,希望“将众多任务集成到一个单一的端到端神经网络中,将提高性能”。第号保留所有微小的感觉网络,并保留每一个单独的和多余的干,只要它实际上起到类比的作用。这样,任何一个组件任务都有能力独立地与一个新任务相结合,形成一个专门且独特的类比。相比之下,已经集成到单片端到端网络中的任务将从新的 GAN 模拟形成中模糊,因为单片网络作为一个整体不是模拟的。**
随着安钢的进展,我会继续更新,我还不想过多地谈论细节。简而言之:有许多更抽象的损失函数可能值得添加到单个网络的损失函数,以有效地调节这些 gan 的生成和整合。此外,用于推理阶段的 GANs 架构显然有很大的作用;我受到了关于元神经元的工作的启发,这些元神经元用于组合 GAN 输出的不和谐声音。Hinton 的胶囊也是一个值得记住的好模型。请随意尝试——我很慢而且不稳定,所以你可能会在我之前完成。😃**
甘斯:不同的视角
一个直观和无障碍的介绍,以生成敌对网络
生成对抗网络是现代人工智能领域最热门的话题之一。在本文中,我们将从一个不同的角度来看 GANs,即,不把 GANs 看作美丽图像的生成器,而是看作概率分布转换函数。我们将探索 GAN 的核心思想,而不会被实现和复杂的数学所困扰。我们从分析我们手头的问题类型开始。然后,我们了解解决方案的需求如何塑造 GAN 理念。
欢迎来到游乐园!
假设我们拥有一个游乐园。在这个公园里,我们有一台机器,它接收 10 美元,然后随机返回一个价值在 1 美元到 100 美元之间的物品。这台机器在游客中很受欢迎,因为他们不时赢得非常酷和昂贵的东西。此外,这台机器对我们来说很有利可图。因此,机器的赠品选择逻辑准确地击中了甜蜜点,确保了我们和客户的满意度。
作者图片
因此,我们想增加更多的这种机器,以获得更多的利润。但是,有一个问题。这台机器超级贵。因此,我们有兴趣制造我们的机器。为此,我们需要弄清楚机器的项目选择逻辑。显然,选择一个项目的关键参数是它的价值。如果一个项目是昂贵的,它应该是不太可能被选中,以保证我们的利润。然而,如果我们降低选择昂贵物品的概率,就会导致游客的不满。因此,我们的目标是尽可能精确地了解物品价值的概率分布。首先,我们有一个以前机器的赠品列表及其相应的价格。首先,我们试图看看赠品的分布。如果分布类似于一个众所周知的概率分布,问题就解决了。我们使用概率分布作为我们新机器的项目选择逻辑的核心。我们从这个分布中抽取样本来决定返回哪个项目。
作者图片
复杂的机器,复杂的问题
然而,如果我们遇到一个复杂的赠品分布,我们需要设计一种方法来学习一个生成过程的概率分布,只给出分布中的样本。
作者图片
换句话说,我们需要一个模型来查看我们的数据,并找出机器逻辑。的主要观点是,学习数据的概率分布是数据生成的主要任务。
转型生成
让我们抽象地描述我们的目标。首先,我们有一组数据,从这一点开始,我们称之为真实数据。我们的目标是伪造与真实数据相似的人工数据。人工数据通常被称为假数据。因此,我们需要一个模型来查看真实数据并生成逼真的假数据。目标很明确。现在,我们需要从抽象的目标转向更详细的任务描述,并希望将它与更熟悉的东西联系起来。为此,我们需要改变对问题的看法。首先,我们需要熟悉一下变换函数。假设我们有一组来自概率分布的样本。通过应用变换函数,我们可以将这些样本从它们的原始分布变换成期望的目标分布。理论上,我们可以从任何源分布转换到任何目标分布。然而,计算这些变换函数并不总是解析可行的。
现在,让我们回到我们的问题。我们可以把我们的世代问题重新定义为一个转变的任务。我们从已知的分布开始。通常,我们选择均值为 0、标准差为 1 的高斯分布。我们称这种分布为“潜在空间”。现在,我们需要定义一个转换函数,将样本从我们的潜在空间转换到数据空间。换句话说,我们的变换函数从潜在空间中提取样本,并输出数据空间中的样本,也称为数据点。瞧!我们生成数据!只有一个问题。解析地定义这个函数是不可能的。但是,我们不是用神经网络来近似解析定义不了的复杂函数吗?是的,我们有,这正是我们要做的。我们使用神经网络来近似我们的转换函数。我们称这种神经网络为“生成器”,因为最终它会生成数据。挺懂事的。
作者图片
现在谈到使用神经网络,我们需要定义一个损失函数来训练我们的网络。损失函数是正确训练和真实数据生成的关键。因此,我们需要根据我们的目标精确地定义它。
鉴别器:非常有用
一般来说,损失函数评估我们的神经网络在我们的目标方面表现如何,并向模型提供反馈(以梯度的形式)以改进自身。这里,我们需要一个损失函数来衡量我们生成的数据遵循真实数据分布的程度。换句话说,我们想要一个损失函数,它可以告诉我们,我们的假数据有多真实。尽管如此,我们没有任何关于真实数据分布的信息。这从一开始就是我们的主要问题。然而,我们可以通过区分真实数据和虚假数据来达到同样的目的。
假设我们的损失函数可以区分真实数据和虚假数据。
作者图片
然后,我们可以将我们的假数据提供给这个函数。对于那些与真实数据难以区分的假样本,我们不需要做任何事情。对于其他假样本,损失函数将提供反馈来更新和改进我们的生成器。
作者图片
更具体地说,我们可以使用一个分类器作为损失函数,可以对真实数据和虚假数据进行分类。如果一个生成的数据点被分类为真实的,这意味着它类似于真实的数据,我们不需要采取任何进一步的行动。对于那些被识别为生成数据的假样本,我们会问损失函数,我们应该如何更新我们的生成器,以使这些样本看起来更真实。损失函数以梯度的形式提供答案,以更新我们的神经网络中的权重。
看来我们已经找到了解决方案的最后一部分!然而,我们必须注意另一个问题。虽然我们建议的损失函数满足我们的要求,但在实践中并不容易实现。因此,我们的损失函数是一个复杂的函数,我们可以定义它的特征,但我们不能直接实现它。看起来是个死胡同。但是,是什么阻碍了我们使用神经网络来近似这个损失函数呢?没什么!所以,我们开始吧。我们可以使用分类器神经网络作为我们的损失函数。我们称这个网络为“鉴别器”,因为它可以鉴别真实和虚假的数据。非常明智的命名。
作者图片
最重要的是,我们非常熟悉使用神经网络进行分类。我们知道如何训练它们,它们的损失函数,以及它们的输入和输出应该是什么样子。然而,同时训练两个神经网络不是常规的事情。现在,最后的问题是,我们应该如何一起训练所有这些网络。
让火车开始吧!
作者图片
如果我们在开始训练生成器之前有了完美的分类器,我们的训练将变得非常简单。不幸的是,在训练过程的开始,我们的鉴别器就像我们的生成器一样毫无头绪。更糟糕的是,我们不能在开始训练生成器之前训练鉴别器,因为我们需要假数据来训练鉴别器。如你所见,这两个网络在训练中相互依赖。发生器需要来自鉴别器的反馈来改进,并且鉴别器需要随着发生器的改进而保持更新。因此,我们交替训练它们。对于一个批次,我们训练鉴别器来区分真假样品。然后对于一个批次,我们训练生成器生成被鉴别器识别为真实的样本。这种方法被称为“对抗训练”。当我们使用对抗训练进行数据生成任务时,我们得到生成性对抗网络,简称 GAN。
然而,当我们看训练程序时,我们看不到“对手”本身。为了找出“对抗性训练”这个术语的来源,我们应该仔细看看两个网络的目标。鉴别器的目标是尽可能准确地对真实数据和虚假数据进行分类。因此,在鉴别器训练阶段,鉴别器试图正确地识别假样本。另一方面,我们训练生成器生成逼真的假数据。为了通过真实性测试,生成器应该让鉴别器相信它生成的数据是真实的。换句话说,生成器试图愚弄鉴别器,而鉴别器试图不被生成器愚弄。这些相互矛盾的目标启动了培训过程。
在培训期间,两个网络都在目标方面有所改进。最后,在某个点上,生成器变得如此之好,以至于鉴别器无法区分假数据和真实数据,这就是我们完成训练的点。
作者图片
陷阱。很多啊!
对于一个非常困难的问题,GANs 是一个美丽而复杂的解决方案。借助 GAN,我们可以快速、高效、精确地解决一个长期存在的问题,并为许多激动人心的应用铺平道路。然而,在开始应用之前,我们应该了解 GANs 的常见问题。首先,生成器是一个神经网络,根据定义,它是一个黑盒。虽然经过训练的生成器将关于真实数据分布的信息嵌入到其权重中,但我们无法显式访问它。当我们处理低维数据时,我们可以通过采样来检索这些信息,但对于高维数据,我们无能为力。此外,与其他神经网络不同,GANs 损失函数提供的关于训练进度的信息很少。在培训期间,我们需要手动检查发电机样品,以检查培训进度。最后,如前所述,训练发生在生成器和鉴别器之间的战斗中。如果他们停止互相争斗,训练过程就会加快,不幸的是,他们经常在一段时间后停止争斗。造成这个问题的原因有很多。例如,如果其中一个网络比另一个提高得快得多,它就会压倒另一个网络,训练就会停止。因此,网络架构应该是平衡的。但是平衡是什么意思呢?这个问题没有直接的答案。通常,人们应该通过反复试验来找到它们。所以 GAN 训练过程相当不稳定。对于稳定性问题有许多建议的解决方案,但是它们大多解决一个并增加另一个,或者需要满足一些特定的条件。简而言之,提高甘的训练进度仍然是一个未解决的问题,围绕这一问题的研究非常活跃。
结论:冰山一角
让我们回到我们的昂贵的机器开始。从时间和资源的角度来看,这台机器象征着所有昂贵的数据生成过程。假设我们有一个中等规模的人脸数据集,对于一个应用程序,我们需要一个更大的数据集。我们可以拿起相机,给人们拍照,然后把它们添加到数据集中。然而,这是一个耗时的过程。现在,如果我们在可用的图像上训练一个 GAN,我们可以在几秒钟内生成数百个图像。因此,数据扩充是 GANs 最突出的应用之一。
数据稀缺并不是唯一的动机。让我们回到人脸数据集。如果我们想使用这些照片,我们很可能会遇到隐私问题。但是如果我们使用实际上并不存在的人的假图像呢?非常好!没人会在意。因此,GANs 为数据隐私问题提供了一个简洁的解决方案。甘的研究团队现在非常活跃,每天都有新的应用或改进被提出。尽管如此,仍有许多东西有待发现。这只是冰山一角。
GANs 和低效映射
甘人如何把自己绑在绳结里,为什么这会损害训练和质量
图片来源:维基共享资源
给手机用户的一个警告:这篇文章中有一些厚的 gif 图片。
生成对抗网络(GANs)被誉为生成艺术中的下一个大 Thing™️,这是有充分理由的。新技术一直是艺术的驱动因素——从颜料的发明到照相机到 Photoshop——GANs 是自然的下一步。例如,考虑以下图片,发表在 2017 年埃尔加马尔等人的论文中。
图一:残创作的抽象艺术(来源: arXiv:1706.07068v1 )
如果你不熟悉 GANs,这篇文章包含了训练过程的简要概述。简而言之,GANs 将随机噪声作为输入,并且(如果训练顺利的话)产生与真实数据不可分辨的输出,真实数据实际上可以是任何东西(一组抽象画、名人的照片、手写数字等)。).
与之前的变分自动编码器一样,在 GAN 文献中,输入值通常与输出没有明确的关系,这是一个记录良好的问题。正如我前面提到的,GANs 接受随机噪声(通常是 100 个随机的正态分布值)作为输入,每个随机数都可以看作是控制输出的杠杆。理想情况下,每个杠杆对应一个特征——在生成人脸的情况下,将有一个杠杆用于微笑或皱眉,一个用于肤色,一个用于头发长度,等等;这种情况很少发生,这使得把 GANs 用于艺术有些冒险。作为这个问题的可视化,考虑下面的动画:
图 2:(上图)在 MNIST 数据集上训练的 GAN 的输出。当(底部)控制杆被调整时,输出无意识地改变,一次一个,而其余的保持不动。只有两个杆被调节,其他 12 个(静态)杆未显示。
在这里,我使用 16 维的潜在空间在 MNIST 手写数字数据集上训练了一个 GAN。我使用这个 GAN 生成了一个随机样本,然后举例说明了当一个输入值被调整而其他值固定不变时,输出是如何变化的。正如你所看到的,这两个杠杆都没有以一种人类可能会觉得直观或有用的方式改变输出;第一个杠杆控制数字是 7 还是 9 以及笔画角度,而第二个杠杆控制数字是 7 还是 9 以及笔画粗细。很蠢,对吧?人们可以想象生成“手写”数字的理想工具是什么样的:第一个杠杆控制生成哪个数字,从 0 到 9;第二杠杆控制笔画的粗细;第三个控制冲程角度;第四,循环……你明白了。相反,我们看到这些特征中的几个由单个杠杆控制,其中一个由多个杠杆控制。想象一下,如果 Photoshop 的旋转工具也通过色轮旋转图像的色调,那将是多么令人沮丧!
这里显而易见的问题是,对于图像生成来说,这是一个低效的、完全混乱的界面。然而,还有另一个不太明显的问题:输入和输出之间扭曲复杂的关系也阻碍了训练,限制了输出的整体质量。
问题一:螺旋
正如我在这篇文章中所解释的,GANs 本质上是一种建模一些数据分布的工具,无论是正态分布还是人脸分布。因此,GAN 是从某个潜在空间到某个样本空间的变换或映射。当 GANs 的学生一头扎进像图像生成这样的高维问题时,这一点经常被忽视。在这里,我打算使用简单的二维问题来演示低效的映射问题,第一个问题如下所示:
图 3:(左)将[[-1,1],[-1,1]]中的均匀噪声映射到螺线的简单函数的潜空间和(右)样本空间。色调和值用于演示潜在空间中的哪个区域映射到样本空间中的哪个区域
这是一个相当简单的函数,它将输入空间中的 x 轴映射到样本空间中沿螺旋的位置(角度和半径),将 y 轴映射到螺旋内的横向位置。出于可视化的目的,x 轴也映射到色调,y 轴映射到值(彩色对黑色)。为了进一步阐明此功能,请考虑以下动画:
图 4:图 3 中描述的函数的潜在空间和样本空间之间的线性插值
那么,问题是训练一个 GAN,它能够以这样一种方式从这个螺旋分布中采样点,使得来自 GAN 的一批和来自真实函数的一批是不可分辨的。注意,GAN 不必学习原始映射;只要输出分布相同,任何映射都可以。
结果:
图 5:(左)GAN 的潜在空间和(右)输出分布
使用典型的 GAN 训练技术(代码可在文章末尾获得)对 GAN 进行 60k 训练步骤的训练。如你所见,GAN 成功地学会了螺旋分布。然而,它有几个问题:
- 它比目标函数要细得多。尽管螺旋的某些部分具有一定的宽度,GAN 基本上将这种分布减少到 2 维空间中的 1 维流形。
- 输出很乱;注意散布在螺旋负空间的点。这些从来没有在目标函数中出现过,那么它们在这里做什么呢?
- 注意点(0.60,-0.63)和(0.45,0.17)的奇怪伪影;这些不连续性导致分布中的空洞。
- 将 GAN 产生的螺旋中的色调和值的分布与原始函数中的进行比较(图 3);它们的有序性要差得多,并且没有显示出潜在空间(杠杆)和产出之间的明确关系。
所有这四个问题在这个动画中都有说明:
图 6:潜在空间和 GAN 输出之间的线性插值
如你所见,这四个问题实际上都是同一个问题。比较图 6 和图 4,我们可以看到 GAN 已经学会了一种低效的映射。首先考虑潜伏空间右上角的撕裂;裂口上方的潜在空间区域被映射到螺旋的最外面部分,而紧靠裂口下方的区域被映射到螺旋的中心。这种撕裂行为解释了混乱(问题 2);位于和上的任何一点都被映射到这两个极端之间的某个地方,通常落在螺旋的负空间。它还解释了(0.60,-0.63)处的伪像(问题 3),因为在该区域中生成的点是从潜在空间中的远点映射的,这也是为什么颜色的色调和值也不一致(问题 4)。最后,通过映射的复杂性解释了学习分布(问题 1)的皮肤性;分布的大部分方差来自于沿着螺旋的位置,而在螺旋宽度内的位置不太重要。因此,甘人首先学会了如何制造螺旋。每当它试图扩大范围时,映射的复杂性会导致一些其他区域中断,就像新手开发人员的意大利面条代码一样(我们都经历过)。GAN 基本上把自己困在了一个无法逃脱的局部极小值中。如果你很好奇(如果不是,恕我直言,你为什么要读这篇文章?),这是 GAN 训练时的样子:
图 7:训练期间螺旋生成 GAN 的输出(彩色)和来自目标函数的样本(灰色)。
图 7 显示,GAN 很快了解到最外层和最内层区域的不兼容映射,其余分布被迫在它们之间进行协调。
问题二:八个高斯人
图 8:八高斯问题,左边是潜在空间,右边是样本空间。
该函数将 2.5 维空间映射到 2 维空间。潜在空间中的前两个维度是独立的、标准的正态分布值。剩余的“0.5”是具有八个可能值的离散维度,被编码为长度为八的向量,其中一个值被设置为一,而其余的值为零。在图 8 中,潜在空间中的随机样本通过在 x 轴和 y 轴上绘制两个连续维度来说明,而离散维度由颜色表示。目标函数基于潜在维度的值,通过以因子 0.2 重新缩放正态分布并将其移动到八个点之一,将该潜在空间映射到样本空间。这个过程在这里是动画:
图 9:八高斯问题的潜在空间和样本空间之间的线性插值
那么,问题是训练一个 GAN,它能够以这样一种方式从这八个高斯分布中采样点,使得来自 GAN 的一批和来自真实函数的一批是不可分辨的。注意,就像上面的螺旋问题一样,GAN 不必学习原始映射,但是有一个简单的映射显然是更可取的。
结果:
图 10:(左)在八高斯问题上训练的 GAN 的潜在空间和(右)输出分布
很糟糕。GAN 完全不能在两种模式下产生样本(模式崩溃),它在模式之间产生大量的点,它不能产生正态分布的模式,并且在潜在空间和样本空间之间明显没有合理的关系。这一点在下面的动画中更加明显:
图 11:潜在空间和 GAN 输出之间的线性插值
显而易见,两个连续潜值内的不同区域被剪切并映射到六个样本空间模式。这与螺旋问题中的撕裂提出了同样的问题;落在裂缝上的点被映射到模式之间的负空间。尽管解决方案很简单(即缩小连续维度,并将离散维度中的每个值映射到不同的模式),但 GAN 陷入了局部最小值,无法将自己挖出来。
问题三:一个高斯
图 12:“一个高斯”问题,潜在空间在左边,样本空间在右边。
八高斯问题显然太难了,所以这里有一个更简单的问题:将二维均匀噪声转换成二维标准正态噪声。如同在螺旋问题中,通过沿着 x 轴旋转色调和沿着 y 轴改变潜在空间的值来给点着色。最简单的映射很简单:独立扩展每个维度。这里举例说明了这一点:
图 13:一个高斯问题的潜在空间和样本空间之间的线性插值
结果:
图 14:(左)在一个高斯问题上训练的 GAN 的潜在空间和(右)输出分布
正如你所看到的,即使是从简单的二维正态分布中采样,GAN 仍然会打结。最值得注意的是,GAN 似乎将潜在空间折叠在自身上,导致样本空间右下角出现扭结、间隙和突起。以下是插值动画:
图 15:潜在空间和 GAN 输出之间的线性插值
结束语
可能我已经反复强调了这一点。然而,我希望上面的可视化已经清楚地表明,输入和输出特征之间的模糊关系不仅仅是一个简单的不便,而是一个更基本的问题的征兆。如果您对用于训练上述 GANs 的代码或可视化代码感兴趣,两者都可以在以下 github repo 中获得:
低效 GAN 映射的训练代码和可视化
github.com](https://github.com/ConorLazarou/medium/tree/master/12020/visualizing_gan_mapping)
推动生物研究的甘斯
图片来自【来源】
介绍
像 GANs 这样的生成网络对于其他深度学习模型来说是独一无二的,因为它们生成样本,而不是优化输出。这允许一定程度的创造性;科学家可以分析生殖模型的输出,以了解生物系统。
本文的目的是详细说明 GANs 在科学研究中的潜在应用,所以我将假设对 GANs 有一个初步的了解。基本上,GANs 包含一个生成器,它在鉴别器的帮助下学习数据集的分布,从而产生一个能够输出新样本的模型。该架构如下所示:
GANs 的一般架构。图片来自来源
我将 GANs 对科学研究的贡献分为三大类:准备、指导和建模。
准备
准备研究样本可能是生物学研究中最不吸引人的部分之一。当我们有一些数据,并且我们想把这些数据转换成图像时,GANs 是有用的(当我们输入数据时,我们使用条件 GANs,或 cGANS)。因此,我们需要分析在准备阶段我们可能需要在哪里生成图像。
一篇论文[1]使用 cGANs 作为用于计算分析的标准化染色组织细胞的方法。进行染色标准化是为了减少染色组织中的不一致性(例如,一些样本可能比其他样本染色更深),并为计算机辅助检测系统准备这些组织。
然而,执行染色标准化的标准技术通常会扭曲组织结构中的细微之处。作者利用 cGAN 来解决这个问题,因为 cGAN 可以更好地了解组织样本的底层结构,从而保留其结构。因此,cgan 可以用作组织分析管道中的预处理步骤。
标准标准化与 cGAN 染色到染色翻译(STST)。目标是使生成的染色与参考相似。图片来自【来源】
方向
GANs 对于在科学研究中建立潜在的方向特别有用:我们可以使用 GANs 生成分子或尝试潜在的蛋白质结构。
GANs 输出的分子很少是稳定的或潜在有用的,但我们可以随后使用其他深度学习模型来筛选数据集中少数有前途的分子。这将通过产出比我们用标准技术生产的更多的可行药物来推进药物发现(药物发现的标准技术意味着仅仅依靠高级化学家的想象力)[2]。
红圈代表 GANs 将影响的药物研发阶段。图片修改自【来源】
然而,药物发现的过程不仅仅是发现可能的药物。测试是严格的,一种药物通过这些标准的概率极低。对于一种有用的药物,它必须与预期的蛋白质或途径发生反应,以产生预期的效果,同时不与我们无数的其他身体系统发生反应。这样的壮举必然是困难的;甘斯只是让我们更快地失败。
GANs 还可以通过产生潜在的蛋白质设计来提出新的科学方向。然而,输出新的蛋白质比生成小分子要困难得多,因为无数的相互作用促成了蛋白质的复杂结构。因此,使用 GANs 来设计新的蛋白质并不像产生更小的分子那样发达。然而,由于新的蛋白质被用于工业(例如,洗衣液使用酶),蛋白质设计的任务不受生物系统中分子下游效应的限制。
建模
GAN 能够输出新的图像,因为它学习了特定种类的成像的分布。例如,如果 GAN 在猫的数据集上训练,它学习猫图像的分布,因此能够基于该分布输出图像(看起来像猫)。我们可以用同样的方法来模拟生物系统。
如果一种蛋白质影响整个细胞的结构,那么我们可以修改代表该蛋白质的特征向量,以生成细胞结构的图像。使用这种 GANs,我们可以通过在细胞发育的不同阶段取样来研究细胞结构的变化。然后,我们可以插入输入蛋白质的特征向量,从而产生细胞发育的连续模型[4]。
细胞结构取决于红色染色的蛋白质。图片来自【来源】
我们可以将相同的过程应用于过多的系统,例如组织发育建模[5]。随后,当我们进一步研究该系统时,我们可以导出影响该系统的附加变量。我们可以通过添加新发现的变量来重新训练我们的 GAN,然后 GAN 将成为生物系统的一个更加精确的模型。然后,当我们发现所有重要的变量时,GAN 实际上将是生物系统的完美代表:我们输入一些变量,然后它生成生物系统的确切条件。
鉴于实际上任何生物系统的复杂性,我们还远远没有实现这个理想,随着我们对它们的进一步研究,GANs 有可能代表生物系统的完美数学模型。
参考
- 萨利希,佩加等人。基于 Pix2Pix 的染色到染色翻译。
- 使用基于潜在向量的生成对抗网络的从头分子生成方法。
- 拉姆松达、巴拉思、等。面向生命科学的深度学习。
- 奥索金,安东,等人。生物图像合成。
- 韩,,等。用有监督的遗传神经网络学习组织的生成模型
每个人的 GANs
革命性人工智能概念的直观解释
来源。图片免费分享。
"对抗训练是自切片面包以来最酷的事情."— 脸书首席人工智能科学家 Yann LeCun
生成对抗网络(GAN)在机器学习领域已经显示出巨大的能力和潜力来创建逼真的图像和视频。除了其生成能力,对抗性学习的概念是一个框架,如果进一步探索,可能会导致机器学习的巨大突破。
本文将使用易懂的解释和图表,本能地解释 GANs 如何工作,他们在训练中遇到什么挑战,以及对抗性学习的力量。
GAN 是由两个模型组成的系统:发生器和鉴别器。
鉴别器只是一个分类器,它确定给定的图像是来自数据集的真实图像还是来自生成器的人工生成的图像。这个二元分类器将采用卷积神经网络的形式。
生成器的任务是接收随机输入值(噪声),并使用去卷积神经网络创建图像。可以把这个概念想象成为随机数发生器设置一个种子,同样的输入噪声会产生同样的输出。生成器使用随机噪声作为各种种子来产生图像。
作者创建的图像。
双模型系统的目标是让生成器欺骗鉴别器,但也让鉴别器以其所能达到的最高精度对生成器的图像进行分类。这两个有价值的对手(生成性对抗性网络)之间的持续战斗意味着两种模式都通过试图击败对方而变得更好。生成器使用鉴别器来获得关于其图像可信度的反馈,并且鉴别器从生成器获得更多数据来进行训练。
去进化神经网络(DNN)是 GAN 的心脏。它是负责生成图像的算法。顾名思义,卷积神经网络可以被认为是“反向运行 CNN”,但其机制要复杂得多。DNNs 也称为 deconvs 或转置卷积神经网络,它使用类似于 CNN 的层,但向后向上采样(与向下采样相反),使图像更大。
这比 CNN 还难。通过压缩或概括图像信息来缩小图像,比放大图像而不使其模糊且细节不变要容易得多。这个问题用转置卷积(反卷积)来解决。
在这个意义上,如果 DNN 是 GAN 的心脏,转置回旋就是动脉。理解反卷积首先需要理解卷积。例如,考虑以下卷积:
作者创建的图像。
假设步幅为 1,如下执行以下卷积。卷积将图像概括为一个较低的维度。
作者创建的图像。
去卷积层的目标与卷积层相反:获取一个简化的图像和一个可训练的内核,并预测“原始图像”在这种情况下,随机噪声将通过一系列反卷积进行转换,以产生更清晰的图像。
作者创建的图像。
基于鉴别器的结果训练这些转置卷积。如果鉴别器表现不佳,那么生成器的当前权重是可取的,并且适当地欺骗了鉴别器。但是,如果鉴别器性能良好,则需要大幅改变生成器的当前权重,以降低鉴别器的性能。换句话说,鉴别器是发电机的损失函数。
虽然这个想法是革命性的,但众所周知,GANs 很难训练。GANs 中的一个常见故障是鉴别器超过了生成器,以 100%的确定性识别生成的图像是假的。因为鉴别器用作调整反卷积层权重的损失函数,所以绝对确定性不会给生成器留下下降的梯度。
训练 GANs 的另一个常见故障是模式崩溃,即生成器发现并利用鉴别器中的弱点。当 GAN 生成相似的图像而不管随机输入的变化时,可以在 GAN 中识别模式崩溃。这意味着生成器只生成它知道几乎总是会欺骗鉴别器的图像。这意味着生成器没有改进,因为鉴别器给出的结果很差,这意味着生成器正在产生理想的结果。模式崩溃有时可以通过以某种方式“加强”鉴别器来纠正(例如,通过调整其训练速率或重新配置其层)。
用 GANs 生成手写数字。照片来自奥莱利。图片免费分享。
GANs 基于这样一种理念,即两个实力相当的个体之间的竞争会促进双方的发展,并要求双方都受到健康而谨慎的监管,以免一方剥削另一方。
生成对抗网络和对抗学习作为一个领域仍然非常年轻,并显示出巨大的潜力。他们有巨大的潜力来塑造数字世界——无论是好是坏。Deepfakes 有能力将人们的脸放在令人反感的视频上(通过指定图像作为生成器的输入噪声),可以摧毁生命并欺骗数百万人。危险的深度伪装的解决方案可能在于对抗性学习本身——加强和分析鉴别器。
甘斯从零开始。
生成对抗网络及其在 PyTorch 中的实现
去年,生成对抗网络(GANs)凭借那些令人印象深刻的人类面孔在人工智能领域掀起了一场风暴。他们真的很酷,不是吗?它们基本上是从无到有产生的。
没什么??。咳嗽炫耀咳嗽。你们用数据来训练模型。我们知道这些“机器学习”是如何工作的。这只是一个输入输出函数的近似值。没什么让人印象深刻的。这只是另一种算法。
不完全是。甘氏属于内隐学习方法。在显式学习模型中,模型直接从数据中学习其权重。而在隐式学习中,模型在没有数据直接通过网络的情况下进行学习。
啊!,所以是强化学习?
RL 和 GANs 之间有一些相似之处,因为他们都使用演员-批评家的方法。但学习的性质在甘是不同的。
好吧!我放弃了。所以,把甘解释成我 5 岁。
好吧。因此,在巴塞罗那市,一个新的警察被任命来验证驾驶执照的真实性。他的工作是分类合法的和假的。因为他是新人,所以不管他的分类是否正确,他都会从他的同事那里得到反馈。镇上还有一个新的伪造者,他的目标是制造假驾照。所以,伪造者打印任何他认为是执照的东西,然后提交给警察。警察然后接受/拒绝它。每当警察拒绝它时,伪造者从错误中吸取教训,并试图开发一个万无一失的许可证,当它被接受时,他生产更多类似的许可证。但是当警察接受假执照时,他被同事纠正了。通过相互学习,警察和伪造者都会做得更好。
咄!似乎应该由同事来验证
我以为你才 5 岁。好吧,这是一个更好的版本。在 GANs 中,有两个网络鉴别器(警察)和生成器(伪造者)。生成器创建假数据(图像),鉴别器对图像进行分类。基于来自鉴别器的结果,生成器开始学习创建越来越好的图像。因此,鉴别器和发生器都竞相提高性能。他们互相学习,每次跑步都越来越好。有趣的是,这两个网络都在学习过程中。所以,即使是鉴别器也不能一直正确分类。假设鉴别器将假图像分类为真图像,生成器从该结果得知生成的图像是好图像。当鉴别器通过从训练数据(同事)获得反馈来学习时,这将被修复。听起来他们两个肯定会融合。但是 GANs 的收敛性和稳定性是一个独立的话题。
嗯,我必须说 GANs 现在听起来很有趣。因此,基本上生成器在看不到数据的情况下学习数据的底层分布。但是它从鉴别器网络学习,鉴别器网络也随着发生器同时学习。令人印象深刻。现在我想实现 GANs 并创造这些新的人工智能人。
卡拉斯等人https://arxiv.org/abs/1812.04948
呃。没那么快。为了这个结果,你需要更好地了解 CNN,大量的超参数调整,8 特斯拉 GPU 和一周的培训时间。在笔记本电脑上训练 20 分钟的基本数据集上的香草甘怎么样?听起来不错?那我们就这么做吧。
我使用 PyTorch 来实现它们。是的,我已经能听到来自 Keras/Tensorflow 人的“嘘声”。保持冷静,抓住机会适应它,就像我一样。
数据集:
听说过 MNIST 数据集吗?。是的,我们会用到它。图像是灰度的,形状为 28×28 像素。让我们从torch.datasets
模块加载数据集。图像被展平,并且使用multiple_transforms
将值归一化为 0–1。DataLoader
功能帮助批量切片训练数据。
from torch.datasets import MNIST
from torchvision import transformstrans = transforms.Compose([transforms.ToTensor(), torch.flatten])
traindata = MNIST(root='./data', transform=trans, train=True, download=True)
trainloader = torch.utils.data.DataLoader(traindata, batch_size=6000, shuffle=True)
网络架构:
我们将使用具有以下配置的全连接网络。不要对层和神经元感到困惑。您可以根据需要删除/添加层。这种配置有一些最佳实践,我将在后面解释。发生器的输入是任意值的噪声。在这里,我选择了 128 个神经元。生成器的输出必须与训练数据值(784)的形状相匹配。鉴别器得到 784 个神经元的输入,输出单个值,不管是真(1)还是假(0)。
将整个架构转化为网络相当简单。实现nn.Modules
类并定义forward
函数。pytorch 最棒的地方就是亲笔签名的功能性。这意味着,我们不需要做背后的数学运算。自动计算所有神经元的梯度。
酷毙了。一旦定义了这两个类,我们就可以实例化它们了。别忘了定义成本函数和要使用的优化算法。
discriminator = Discriminator()
generator = Generator()criterion = nn.BCELoss()
discrim_optim = optim.Adam(discriminator.parameters(), lr= 0.0002)
generat_optim = optim.Adam(generator.parameters(), lr=0.0002)
为什么要进行这种 BCELoss 和 Adam 优化?
香草甘使用极大极小算法。最小最大损失通过生成器和鉴别器预测概率的对数损失来计算。BCELoss 是二元交叉熵损失,它是概率的对数损失。可以使用两个不同的损失函数来训练生成器和鉴别器,但是这里我们对两者使用一个损失函数。对于权重更新,我们使用 Adam optimizer,因为每个人都在使用它。哈哈,jk。你可以试试其他优化软件,比如 SGD,Adagrad。
这样,模型设计完成了。耶。现在,让我们训练模型。
模特培训:
无论会发生什么样的混乱,都发生在这里。我们一步一步来。所以我们必须同时训练鉴别器和发生器。这基本上意味着以下步骤。
- 向前通过鉴别器
- 反向传播鉴别器误差
- 更新鉴别器权重
- 向前通过发电机
- 反向传播生成器错误
- 更新发电机重量
重复一遍。
# Noise input for generator
def noise(x,y):
return torch.randn(x,y)for epoch in range(2000):
for pos_samples in trainloader:
# Training Discriminator network
discrim_optim.zero_grad()
pos_predicted = discriminator(pos_samples[0])
pos_error = criterion(pos_predicted, torch.ones(batches,1)) neg_samples = generator(noise(batches, 128))
neg_predicted = discriminator(neg_samples)
neg_error = criterion(neg_predicted, torch.zeros(batches,1)) discriminator_error = pos_error + neg_error
discriminator_error.backward()
discrim_optim.step()
# Training generator network
generat_optim.zero_grad()
gen_samples = generator(noise(batches, 128))
gen_predicted = discriminator(gen_samples)
generator_error = criterion(gen_predicted, torch.ones(batches, 1))
generator_error.backward()
generat_optim.step()
就是这样。搞定了。
哇哇哇。慢点。我有很多问题!
为什么只索引数据, *pos_samples[0]*
。训练数据的标签怎么了?
- 漂亮的斑点。在香草甘,我们不在乎标签。我们基本上给出所有的训练数据来训练网络,而不管它来自哪个类。因此,发生器网络必须拟合权重,以重现不同噪声输入的所有变化。也就是说,GANs 有几个变化,它考虑了像辅助 GANs 这样的标签。
这个 zero_grad()对于优化器来说是什么? -对于每个时期,我们希望梯度为零,以便在每次反向传播期间计算的梯度可以在神经元中没有剩余梯度的情况下出现。如果没有 zero_grad(),梯度将在每个时期累积,这在像 RNNs 这样的网络中很有用。
如何将两个错误相加并执行 backward()?
——好大的 pythonic 吧?这是 pytorch 的亲笔签名模块。它负责反向传播这两个错误。
好的,那么现在你如何知道网络何时被训练?。我应该观察哪个成本函数?
- 如前所述,收敛是 GAN 中一个有趣的问题。严格来说,当鉴别器和生成器都达到纳什均衡时,GAN 就被称为被训练。由于 GAN 是一个极小极大问题,当一个网络最大化其成本函数时,另一个网络试图最小化它。我们正在训练两者来提高。纳什均衡状态下,代理人不改变其行动的过程,不管其他代理人的决定。在训练过程中,一个网络从另一个网络开始训练,但是当它到达一个点,不管另一个网络的决定,鉴别器或生成器都不会变得更好时,它就达到了纳什均衡。实际上,给定一组相同的真实和伪造图像,鉴别器将检测每个真实和伪造图像为真实的,因此预测精度将为 50%。
最佳实践:
几乎没有关于更好的模型和更快收敛的最佳实践。我故意把这个放在最后,因为有些讨论可能会改变代码,如果在主要内容中解释,会导致混乱。
- 在最后一层,对鉴别器使用 sigmoid 激活函数,对生成器使用 tanh 函数。在这种情况下,发电机的输出将在范围(-1,1)内。因此,我们还必须将训练数据标准化到这个范围(-1,1)
- 不是为 1 训练真实图像,为 0 训练虚假图像,而是用 0.98 和 0.02 或类似的值训练它们
- 为了快速检查您的 GAN 设置是否正常工作,请将训练数据限制在单个类中,并检查它们的表现如何。在包含 10 个类的 MNIST 数据集上,对于一些未失真的图像,可能需要几个小时的时间,因此最好检查一下配置是否适用于有限的数据集。
- 由于生成器比鉴别器需要更多的训练时间,所以在鉴别器中使用丢弃层可以阻止过拟合。
以下是完整的代码:
感谢您阅读帖子。如果你发现了任何错误或有疑问,请在评论中告诉我。
欢迎通过 Github 、 Twitter 和 Linkedin 联系我。干杯!。
非常感谢文章作者迭戈·戈麦斯莫斯克拉。
生成对抗网络
深度学习领域的 GANs
深入深度学习的神奇世界,释放您机器的艺术能力。
“我们不断前进,打开新的大门,做新的事情,因为我们很好奇,好奇心不断引领我们走上新的道路。”—华特·迪士尼
机器学习是我们生活中不可或缺的一部分。从我们手机键盘顶部的东西到驱动自动驾驶汽车领域的东西,它无处不在。机器学习领域每天都在进步,不断有创新和新想法出现。机器学习的一个贡献,确切地说是深度学习领域,是 GAN 生成性对抗网络,在我看来,这是科学创造的一种纯粹的魔法形式。
GAN 是一类致力于创造新事物的深度学习框架。与用于检测各种事物的传统深度学习技术不同,GAN 用于产生新的事物。把甘想成一个画家;它喜欢画画。识别一个特定的物品,比如说一顶帽子,任何有文化的人都能做到。我们可以使用深度学习框架在计算机上轻松模拟这一点。而甘则是向创造力迈进了一步,一个接近智力的层次。
信用:此人不存在
上面显示的图像不是真人,而是由机器从头开始生成的。这就是甘的全部。
甘是伊恩·古德菲勒和他的同事在 2014 年提出的,当时伊恩 28 岁。作为甘的父亲,他被认为是赋予机器想象力的人。传说有一天晚上,伊恩的朋友在一家酒吧就一台生产图像的机器找到了他。现在,让我们仔细看看伊恩的甘斯。
在我们大致了解 GANs 如何工作之前,我请求您阅读一些关于深度学习的内容,所有这些都是通用算法,如反向传播和 CNN,以便更好地理解 GANs。以下是一些有用的链接。
1.反向传播[http://neuralnetworksanddeeplearning.com/chap2.html
2.CNN[https://brohrer . github . io/how _ convolatile _ neural _ networks _ work . html]
3.自适应反进化网络[https://ieeexplore.ieee.org/document/6126474
GAN 的两个主要支柱是发生器和鉴别器。这是两个复杂的神经网络,是 GAN 的神奇背后的工作手。
我们先用一个例子来试着理解这些。
把生成器和鉴别器分别看成是学生和助教(TA)。他们两个都是来这里接受检查的。让我们假设他们一开始什么都不知道。学生(生成者)的工作是写答案,助教(鉴别者)的作用是从答案本上检查答案。最初,学生们草草写出几个随机答案,然后交给助教。TA 随机给那些答案分配粗分,然后他接着用答题卡查答案。这样做的时候,助教会意识到自己的错误并从中吸取教训,同时给出正确的分数。
此外,一旦学生得到分数的反馈,他就会了解自己的错误,并努力写得更好,接近正确答案。这种双重循环的循环继续下去,直到产生的答案真正接近正确的答案。简单来说,这就是 GAN 的工作原理。
生成性对抗性网络框架由塔尔斯·席尔瓦
如上所述,生成器和鉴别器都是神经网络。生成器,顾名思义,生成新的数据实例;另一方面,discriminator 充当生成的数据实例的判断者。这两个网络都是从零开始训练的,同时它们在猜测的游戏中相互竞争。这个游戏是基于数据分布的标准。我们不会深究他们工作的数学原理。生成器从样本随机噪声中生成假图像(老实说,没有什么是真正随机的,所以让我们称之为伪随机),然后它将假图像传递给鉴别器,希望这些假图像被鉴别器接受为真实图像。鉴别器还传递地面实况,即真实的分类数据集。鉴别器试图通过首先给图像 0(假)和 1(真)之间的随机概率来识别真假照片。然后,它从错误中学习并反向传播错误以提供更好的概率。这个循环继续,直到它给所有图像接近正确的概率,即对于真实图像接近 1,对于伪图像接近 0。一旦完成,假图像的概率的反馈被反向传播到生成器,然后生成器试图创建具有更好概率的新图像。这种双循环一直持续到生成图像的可能性接近 1。简而言之,这就是甘的工作方式。
信用:生成对抗网络
TL;速度三角形定位法(dead reckoning)
- 生成器从随机噪声中生成图像,并将其向前发送。
- 鉴别器给出接收图像的概率。
- 鉴别器从真实数据集中学习,并提供更准确的概率。然后,它向后返回。
- 生成器从返回的概率中学习,并尝试创建具有更好概率的图像。
演职员表: CycleGAN
现在来说说 GAN 的一些好玩有趣的现实生活应用;这将帮助你认识到甘斯到底有多神奇。
下面是由杰森·布朗利列出的 GAN 的一些奇妙应用。请仔细阅读他的文章,了解所有这些应用。
- 生成图像数据集的示例
- 生成人脸照片
- 生成逼真的照片
- 生成卡通人物
- 图像到图像的翻译
- 文本到图像的翻译
- 语义图像到照片的翻译
- 人脸正面视图生成
- 生成新的人体姿态
- 照片到表情符号
- 照片编辑
- 面部老化
- 照片混合
- 超分辨率
- 照片修复
- 服装翻译
- 视频预测
- 3D 对象生成
你可以在这个神奇的网站上看到甘的行动,【thispersondoesnotexist.com】。该网站使用 StyleGAN2,每当您访问该网站时,它都会向您呈现一张计算机生成的随机图片,照片中的人在现实生活中并不存在。只是不断刷新。生成的图像逼真得令人震惊,这有助于展示 GANs 的真正力量。该网站由 Phillip Wang 创建,旨在展示 StyleGAN2 和 GANs 的总体潜力。这不是很神奇吗?!
现在,我们知道了 GAN 的能力,让我们尝试用 python 编写我们自己的 DCGAN。
这主要归功于哈德琳·德·庞特维斯。
首先,你需要由彩色图像组成的数据集。请访问此链接下载 CIFAR-10 python 版本数据集。
https://www.cs.toronto.edu/~kriz/cifar.html
提取数据集并将其放入“数据”文件夹中。此外,在同一目录中创建一个空文件夹来保存输出图像。在包含数据和空文件夹的目录中启动一个新的 python 文件。
注意:如果您不想手动下载数据集,或者在下载时遇到一些问题,请不要担心。下面 Colab 文件中的第四个代码单元格将为您完成这项工作。
Google Colab 上的 DCGAN
提示:你可以在 Google Colab 上编写完整的 DCGAN 代码。在你的 Goggle Colab 笔记本中,进入运行时>更改运行时类型,在硬件加速器下选择 GPU 。
Google Colab 笔记本链接—https://Colab . research . Google . com/drive/1 hnpkzucnm _ VM-A-Z _ lwxs 4c 9 lnva 9 ez?usp =共享
我们的模型每走 100 步后的输出图像。
有用的链接
1.生成敌对网络[【https://arxiv.org/abs/1406.2661】T2
2.反向传播[【http://neuralnetworksanddeeplearning.com/chap2.html】T4
3.CNN[https://brohrer . github . io/how _ convolatile _ neural _ networks _ work . html]
4.适应性去进化网络[https://ieeexplore.ieee.org/document/6126474
5.计算机视觉& GANs[https://www . superdata science . com/courses/computer-Vision-z-learn-opencv-GANs-cutting-edge-ai]
进一步阅读
1.迈克尔·乔丹
GANs:利用技术创造更美好的明天
下面的文章讨论了 GAN 的基础知识以及它的一些变种。
当有人只是把自己的脸实时换成一些已知人物的脸,是不是很神奇?或者,当你看到一幅时尚的肖像画仅仅通过提供一个小的布局就用电脑制作出来时,你会变得非常好奇和兴奋吗?这就是我们在这篇文章中要揭示的。所有这些很酷的想法大多是在机器学习中的一个现代想法的帮助下实现的,即 GANs 。
注意:上述想法也可以通过有效使用计算机图形包来实现,这超出了本文的范围,但是我在参考资料部分添加了一些链接。
GANs 或生成对抗网络是一类能够生成与真实图像相似的图像的网络。如果你熟悉风格转移的概念,那么 GANs 的想法对你来说并不陌生。然而,你可以从参考部分的链接中得到一个很好的风格转换的复习。GANs 有各种各样的应用,像图像-图像翻译,文本-图像翻译,实时视频模拟,生成卡通人物等等。我认为 GANs 也可以证明自己有用的一个想法是对数据进行上采样。(仔细想想!).
最近,我参加了一个由 IEEE 主办的技术写作比赛,该比赛旨在写任何技术的未来的潜在和有益的用例,因此我写下了关于 GANs 的内容。你可以在参考资料部分找到这篇文章的 PDF 文档!
目录:
1.定义:
基本上如上所述,GANs 或生成对抗网络是我们生成与真实世界图像相似的图像的网络。但是等等!到底是谁生成了这些图像?图像生成怎么可能呢?模型是如何训练的?模型如何学习映射来生成图像?我们不用卷积网络吗?你会在文章中找到所有这些问题的答案,所以请仔细阅读。
特定的 GAN 包含两个不同的网络。其中一个被称为发生器网络,另一个被称为鉴别器网络。生成器模型(作为艺术家)试图生成与真实图像相似的图像,鉴别器模型(作为艺术评论家)区分真实图像和生成的图像。
注意,鉴别器网络也被称为对抗网络,因此得名生成对抗网络。
图片来源:https://www.tensorflow.org/tutorials/generative/dcgan
图片来源:https://www.tensorflow.org/tutorials/generative/dcgan
GAN 网络的理想条件是鉴别器网络无法区分真实图像和发生器网络产生的图像。
在非常基础的水平上,发生器和鉴别器网络的工作可以概括为
生成器模型不会直接看到真实图像,并且只对观察到的图像(或 DCGAN 情况下的矢量)起作用。在生成图像之后,该图像被发送到鉴别器网络,该网络将其分类为真或假。鉴别器网络也能看到真实的图像,因此能从中获取一些信息。对于生成图像的输入,我们从鉴别器网络获得的输出包含一些关于真实图像的信息。然后,我们根据该输出计算发电机网络的损耗。通过这种方式,生成器网络获得关于真实图像的信息,然后在下一次尝试中,它试图生成与真实图像更相似的图像。通过不断的练习,生成器模型变得能够生成与鉴别器不能分类的真实图像非常相似的图像。
2.GANs 的当前使用案例:
对于技术社区来说,gan 并不是什么新鲜事物,几十年来,我们的技术社区已经发展到如此程度,以至于研究的速度比以前更快了。因此,我们已经在许多领域实施了 GANs,并且效果很好。让我们揭开其中的一些领域:
a)深度假:
DeepFake 是 GANs 的一个惊人的实现,其中图像或视频中的一个人被一个长相相似的人取代。不仅是图像在这里被替换,而且说话者的声音有时也会改变。
图片来源:https://en.wikipedia.org/wiki/Deepfake
然而,这种技术引入了各种负面用法,如名人色情视频、复仇色情、假新闻、恶作剧和金融诈骗。虽然 DeepFake 可以用于许多错误的任务,但它也有各种积极的实现,如电影配音,教育学生等。
b)生成肖像:
GANs 的一个非常有益的实现是从零开始生成美丽而有创意的肖像。我们可以以成对(其中存在 b/w 观测图像和真实图像的直接映射)和不成对(其中不存在 b/w 观测图像和真实图像的直接映射)的形式向这些模型提供训练样本。
c) ExGANs:
ExGANs 或范例生成对抗网络是脸书正在实施的一种方法。ExGANs 是一种有条件的 GANs,它利用样本信息来产生高质量、个性化的内部绘制结果。这种方法特别使用 ExGANs 将闭着眼睛的人的图像转换成睁着眼睛的相同图像。
还有其他领域也大量使用了 GANs。请参阅参考资料部分了解更多信息。
3.gan 的类型:
虽然 GAN 有多种变体,但今天我们将讨论三种常见类型的 GAN,即 DCGAN、Pix2Pix GAN 和 CyclicGAN。这些 GANs 的实现由 tensor flow发布为官方教程,所以不要忘记查看。好了,让我们开始吧:
a) DCGAN:
DCGANs 或深度卷积生成对抗网络是学习从噪声向量到图像的映射的 GANs 的一些基本形式。现在你可能会想,我们如何把一个矢量转换成图像?这个问题的答案很简单。它使用转置卷积层对输入执行反卷积。这与卷积层所做的正好相反。卷积层从输入图像中提取特征,使得通道的数量增加,但是在转置卷积层中,新的特征被添加到输入向量中,从而将它们转换成图像。
图片来源:作者
图片来源:作者
生成器网络建立在一系列[转置卷积+ BatchNorm + LeakyReLU 层]之上。鉴别器网络是一个正常的分类器,它使用卷积层来分类输入图像是真实的还是生成的。
鉴别器损耗是真实图像输出和生成图像输出的综合损耗。使用的损失函数是 BinaryCrossEntropy (但选择不受限制)。发生器损耗是根据我们从鉴频器网络获得的输出计算的,用于将生成的图像作为输入。
def discriminator_loss(real_op, gen_op):
loss = tf.keras.losses.BinaryCrossentropy(from_logits=True)
real_loss = loss(tf.ones_like(real_op), real_op)
gen_loss = loss(tf.zeros_like(gen_op), gen_op)
return real_loss + gen_lossdef generator_loss(gen_op):
loss = tf.keras.losses.BinaryCrossentropy(from_logits=True)
return loss(tf.ones_like(gen_op), gen_op)
b)pix 2 pix gan:
这些类别的任务属于条件任务的范畴。条件甘是那些基于输入图像中的条件生成图像的甘类。例如,考虑下面的图像-
图片来源:https://arxiv.org/pdf/1611.07004.pdf
在上面的图像中,我们必须使用左边的图像生成右边的图像。在这里,我们可以看到输入和输出之间存在直接关系,并且该模型非常容易学会基于这样的对来生成图像。这种数据也被称为成对数据。
现在,一个特定的 Pix2Pix GAN 也有一个生成器和一个鉴别器模型。但是在这里,它们和在 DCGANs 中使用的不一样。发生器网络利用U-Net架构,鉴别器网络利用 PatchGAN 架构。
U-Net 架构基本上是一个普通的编码器-解码器网络,增强了层间的跳跃连接。添加跳过连接的论点可能是模型将同等地学习每一层的编码和解码特征。Pix2Pix 网络中使用的 U-Net 架构(特别是在关于城市景观数据集的 tensorflow 教程中)可以被可视化为-
图片来源:作者
现在来描述鉴别器网络!鉴别器网络是一个 PatchGAN 网络。但是首先什么是 PatchGAN 网络呢?PatchGAN 网络属于机器学习分类器类,它学习对输入数据中的高频结构进行分类。他们在面片的尺度上惩罚结构。这些模型使用从图像中提取的小块用于分类任务。鉴别器使用一个 N×N 的小块来鉴别一幅图像是真是假。通常图像的一个 70x70 部分被用于分类,因为它产生更好的结果。这一点从原论文中的解释可见一斑。
图片来源:https://arxiv.org/pdf/1611.07004.pdf
鉴别器损耗通过计算真实图像和生成图像的输出的交叉熵之和来获得。生成器除了计算生成图像的损失外,还计算目标图像和生成图像之间距离的 L1 范数。
def discriminator_loss(disc_real_op, disc_gen_op):
loss = tf.keras.losses.BinaryCrossentropy(from_logits=True)
real_loss = loss(tf.ones_like(disc_real_op), disc_real_op)
gen_loss = loss(tf.zeros_like(disc_gen_op), disc_gen_op)
return real_loss + gen_lossdef generator_loss(disc_gen_op, generated_op, target, lambda=10):
loss = tf.keras.losses.BinaryCrossentropy(from_logits=True)
gan_loss = loss(tf.ones_like(disc_gen_op), disc_gen_op)
l1_loss = tf.reduce_mean(tf.abs(target - generated_op))
gen_loss = gan_loss + (lambda*l1_loss)
return gen_loss
c)循环甘斯:
环状氮化镓是应用最广泛的一类氮化镓。在深入循环 GANs 之前,让我们先谈谈未配对数据。考虑下面的图像-
图片来源:https://arxiv.org/pdf/1703.10593.pdf
左侧的一对图像在它们之间具有一定程度的对应性,正如我们已经讨论过的,它们被称为配对数据。右侧的图像在它们之间没有任何对应关系,被称为不成对数据。在许多情况下,我们只能得到不成对的数据,因为创建成对数据的成本更高,而且不容易获得。
现在你可能会问-
只针对不成对数据的训练,你在实现一个全新的架构?我们不能用以前的方法学习不成对的数据吗?
答案是肯定的,您可以使用以前的方法,但这在性能方面不会有太大的好处。因此,我们需要一种新的方法来处理这种数据。不仅是未配对的数据,我们还可以使用 CyclicGANs 从配对的数据中学习,并产生非常有希望的结果。
现在让我们来考虑一下使用周期因子的一些原因
我们考虑两组图像,即 X 和 Y,作为我们要在其间转换图像的两个域。
- 假设我们学会了从集合 x 中的图像“a”生成集合 Y 中的图像“b”。现在我们对图像“b”运行生成器模型。你能保证我们能找回 a 的形象吗?不,如果使用上述 GANs 的变体,我们不能保证这一点。
- 我们能自信地说集合 X 中的所有图像都将被映射到集合 Y(双射)中的所有图像吗?
这两个问题都已经被 CyclicGANs 解决了。现在让我们了解一下 CyclicGANs 的架构。
一个特定的循环 GAN 由两个发生器网络和两个鉴别器网络组成。
两个发电机网络即 G 和 F 互为逆。 G 从集合 X 到 Y , G:X →Y ,以及 F 从集合 Y 到 X , F:Y →X 中的图像学习映射。
图片来源:作者
为了将两个发生器网络的输出分类为真或假,我们有两个鉴别器网络,即 Dx 和 Dy 。 Dx 基本将 F 生成的图像分为真假, Dy 将 G 生成的图像分为真假。
图片来源:作者
鉴别器损耗(对抗性损耗)和发电机损耗实现为-
def discriminator_loss(real_op, gen_op):
loss = tf.keras.layers.BinaryCrossentropy(from_logits=True)
real_loss = loss(tf.ones_like(real_op), real_op)
gen_loss = loss(tf.zeros_like(gen_op), gen_op)
return real_loss + gen_lossdef generator_loss(gen_op):
loss = tf.keras.layers.BinaryCrossentropy(from_logits=True)
return loss(tf.ones_like(gen_op), gen_op)
请注意,我们通常将鉴频器损耗乘以 1/2,这是因为与发生器网络相比,它的学习速度较慢。
除了这些损失之外,还有一个循环一致性损失,它完成了循环一致性的目标函数。循环一致性丢失解决了我们前面遇到的反向映射问题。这种损失确保了从集合 X 映射到集合 Y 的图像具有到其自身的反向映射。让我们听听这篇论文中关于为什么要引入循环一致性损失的一些话
有了足够大的容量,网络可以将同一组输入图像映射到目标域中图像的任意随机排列,其中任何学习到的映射都可以导致与目标分布匹配的输出分布。因此,单独的对抗性损失不能保证学习的函数能够将单个输入【Xi】映射到期望的输出易。
敌对网络不能保证期望的输出,这导致我们引入循环一致性损失。考虑下面的图片-
图片来源:【https://arxiv.org/pdf/1703.10593.pdf
考虑图像的左侧,我们可以看到它使用 G 从图像 x 生成图像 y_hat ,我们再次传递这个生成的图像以使用 F 获得图像 x_hat 。恰恰相反的是在图像的右侧。
现在一致性损失基本上衡量的是 x 和 x_hat (左侧)之间的相似性,我们也称之为前向循环一致性损失,根据 y 和 y_hat (右侧)之间的相似性计算的损失也称为后向循环一致性损失。这个损失基本上是真实({x,y})和生成({x_hat,y_hat})图像之间的距离的 L1(或曼哈顿)范数。
循环一致性丢失可以实现为
def cyclic_consistency_loss(generator, cycled_image, real_image, lambda=10):
loss = tf.reduce_mean(tf.abs(cycled_image - real_image))
return loss*lambda
因此,[对抗性损失+λ*(前向循环一致性损失+后向循环一致性损失)+发电机损失]一起构成了循环性问题的完整目标函数。这里的λ是一个要调整的超参数。
注意,在上面讨论的任何方法中,用于模型建立的其他超参数的选择完全是场景特定的。但在我看来,原始文件中指定的值通常会有更好的结果。
4.结论:
虽然 GANs 在架构和工作方面都非常棒,但是还有更多值得探索的地方。我们不需要认为甘斯会接受工作或其他什么。甘人在他们的地方很酷,但仍然依赖人类完成各种任务。
5.参考资料:
- https://arxiv.org/abs/1705.04058
- https://arxiv.org/pdf/1703.10593.pdf
- https://drive . Google . com/file/d/1 kayaspozrkowruyvfygopody 3 ZD rd 74 x/view?usp =共享
- https://www.tensorflow.org/tutorials/generative/dcgan
- 【https://www.tensorflow.org/tutorials/generative/pix2pix
- https://www.tensorflow.org/tutorials/generative/cyclegan
- https://research . FB . com/WP-content/uploads/2018/06/Eye-In-Painting-with-Exemplar-Generative-Adversarial-networks . pdf
我希望我很成功地向你表达了我的观点。我已经尽力解释了原文中的观点。然而,如果你仍然对任何部分有疑问,或者觉得我没有正确地描述一件事,请随意在评论中写下。
快乐编码
GARCH 和谷歌相处融洽。GG!
使用 GARCH 过程预测谷歌股票收益的波动性。
G 在本文中,我们将关注 GARCH/ARCH 模型及其重要性,尤其是在金融相关领域。在这里,我们将对谷歌股票价格回报的波动性进行建模和预测,然后进行一些与未来几天回报值的波动性相关的推断。在应用中,如果一只股票的回报波动性较小,我们可能会投资于风险较小的股票,而不是在给定的时期内,同样的模型波动性更大或变化更大。
研究条件异方差的主要动机与金融中资产收益的波动性有关。波动性的重要性是显而易见的,因为它有广泛的应用,如期权定价、风险管理(风险价值计算)、波动指数(VIX)交易。因此,如果我们能有效地预测波动性/方差,我们就能创造更复杂的工具来制定交易策略。这里有一个快速的术语介绍,忽略这里的数学方程。更多信息,请参考。
广义自回归
AR 或自回归项突出了与序列中先前元素相关联的方差的线性组合。AR 滞后阶数, p 定义滞后版本的阶数,用于回归给定时间序列中的方差。换句话说,ARCH(p)过程遵循一个 AR(p) 过程。相关的问题是,我们是否可以应用 MA(q) 或移动平均模型来模拟我们的方差以及 AR 过程,这是 GARCH 或广义 ARCH 过程的动机,它试图考虑移动平均成分以及自回归成分。因此,GARCH(2,2)过程由 AR(2)和 MA(2)过程组成。而简单的 ARCH 过程不考虑移动平均项;ARCH(p)模型。
条件异方差
让我们先快速地看一下我们所说的异方差是什么意思,然后是条件部分。当在一个变量集合中,我们看到一个在某个区域(或子集)上相当大的增加或减少的方差分组时,比如在一段时间内,它通常不同于集合中的其余变量,并且我们可以看到变化方差的“群集”,那么这种形式的规则变化在数学上被称为异方差。这种不同形式的金融波动可以由各种原因触发,这些原因导致序列相关异方差,以变化方差的周期为条件。换句话说,方差的增加与方差的进一步增加相关。
现在,我们对正在进行的工作有了一个大致的了解,让我们开始实现,并通过预测一段时间内的值来做一些有趣的预测。
程序步骤:
- 从雅虎财经获取谷歌股票数据。
- 计算和分析资产回报。
- 确定阶数并拟合 GARCH 模型。
- 训练模型。
- 验证模型。
- 预测。
获取谷歌股票数据
Python 中的 yfinance 包支持直接从 yahoo finance 中轻松获取股票数据。我们必须提供正确的股票代码,如谷歌的“GOOG”或微软的“MSFT ”,以创建股票包装器。然后,我们使用。包装器的 history() 方法,通过指定数据的开始和结束日期来获取数据帧形式的数据(这里是 df_goog )。我们获取 2015 年到最近可用日期的数据。
today = datetime.today().strftime('%Y-%m-%d')
td1 = yf.Ticker('GOOG')
df_goog = td1.history(start = '2015-01-01', end = today, period = '1d')
df_goog.info()
计算和分析资产收益
我们通过将我们的数据帧移动一个周期,然后使用 NumPy 的,来计算对数回报(这里知道为什么对数回报优于正常回报)。【T21 日志()】法。我们的数据框中添加了一个相同的列。**
*df_goog_shifted = df_goog.Close.shift(1)
df_goog['Return'] = np.log(df_goog.Close/df_goog_shifted).mul(100)
df_goog.dropna(inplace = True)
df_goog*
接下来,我们通过绘制 2015 年以来每天的数据来可视化我们的资产回报。
*yr = pd.date_range(start = '2015', end = '2020' , freq = 'Y')
ret = df_goog['Return']
fig = plt.figure()
plt.plot(ret)
plt.title("Asset Returns")
for i in yr:
plt.axvline(i, linestyle = '--' , color = 'gray')*
为了检查回报是否平稳,我们应用了 增广迪基富勒 测试;零假设的统计检验,表明由于趋势分量,序列是非平稳的。从stats models . TSA . stat tools我们导入 adfuller API 作为 adf。检验的 p 值是检验结果的第二个要素。
*p_val = adf(ret)[1]
print("Since the pvalue:",p_val,"in the Augmented Dicky Fuller test is less than 5% we easily reject \
the null-hypothesis that returns are non-stationary")*
因此,我们可以得出结论,我们的回报是相当稳定的,可以建模。这是必要的,以确保平稳性,因为这种模型像 ARMA,ARIMA,GARCH 等。可以应用于平稳过程。(知道为什么吗?)此外,直观上看不出季节性。
确定阶次并拟合 GARCH 模型
通过绘制几个滞后的收益平方的偏自相关图,我们得到了 GARCH 过程的阶 p 和 q 的概念。stats models . graphics . TSA plots的 plot_pacf 就是用来实现这个目的的。
从 statsmodels.graphics.tsaplots 导入 plot_pacf
*fig = plot_pacf(ret**2, lags = 20, zero = False)*
我们观察到,在滞后 2 之后,该图似乎停止了,这表明了它的重要性。这表明我们可以尝试 GARCH(2,2)过程。现在,我们拟合模型,为此,我们使用了 arch API 的 arch_model 函数。假设标准化残差的分布函数是学生 t 的分布函数。
从 arch 导入 arch_model
*model = arch_model(ret , p = 2, q = 2, dist = 't').fit(disp='off')
model.summary()*
训练模型
为了训练我们的模型,我们使用固定 滚动窗口预测 (一种回溯测试)的技术,其中窗口大小是固定的。我们的想法是预测特定范围内的值,然后在我们知道该范围内的实际(真实)值时,移动窗口以包括这些预测值,从而更好地训练我们的模型,并再次预测下一个范围。这使预测适应新的观测结果,并避免回顾偏差。它也较少受到过度拟合的影响。在所取的窗口大小上重复该过程。
这里,我们取一个窗口大小为一年中交易天数的 5 倍(即 252),并利用历史数据来训练我们的模型。生成回报以及预测值的曲线图。在使用 arch_model 函数拟合一个模型之后,我们使用。forecast()* 方法通过设置参数 horizon = 1 来预测紧接的下一个值。准确地说,是进行 1 天滚动窗口预测。请记住,我们需要找到标准偏差,这就是为什么我们取预测方差的平方根。*
*rolling = []
window = 252*5
for i in range(window):
train_data = ret[:-(window-i)]
model = arch_model(train_data, p = 2, q = 2).fit(disp='off')
pred = model.forecast(horizon = 1)
rolling.append(np.sqrt(pred.variance.values)[-1,:][0])
rolling = pd.Series(rolling , index = ret.index[-window:])
plt.plot(ret[-window:])
plt.plot(rolling)
plt.legend(['Returns','Volatility'])
plt.title('Rolling Window Forecast')*
我们可以看到,我们的模型能够捕捉随时间变化的方差。有必要知道橙色曲线表明我们的股票回报有多波动。当收益偏离均值时,波动率曲线也会偏离,以表明方差变化的时期。橙色长尖峰表示高波动时期,而较短的尖峰表示资产回报波动较小的时期。从视觉上看,我们的模型似乎工作得还不错,但最好在其他统计测试的帮助下进行验证。
验证模型
数学上,标准残差是残差除以其标准差。它是对观察值和期望值之间差异强度的度量。标准残差应遵循一个白噪声过程;一个过程,其中变量是连续不相关的(在任何滞后时间都相互独立),具有零均值和有限方差。我们将在这里使用三种验证技术:
a)绘制标准化残差图,直观地查看残差是否遵循白噪声过程。这里,标准化残差 std_resid 通过使用来计算。resid* 和。我们的模型对象的 conditional_volatility 属性如下面的代码所示。*
b)绘制自动相关图,以验证标准残差在任何滞后时间都不显著相关。除了滞后零点之外,在图中看不到明显的滞后。
从stats models . graphics . TSA plots*导入 plot_acf*
*std_resid = model.resid/model.conditional_volatility
fig, ax = plt.subplots(nrows = 2, ncols = 0)
ax[0] = plt.plot(std_resid, color = ‘r’)
plt.title(‘Standardised Residuals’)
for i in yr:
plt.axvline(i, linestyle = ‘ — ‘ , color = ‘gray’)
ax[1] = plot_acf(std_resid,lags = 20)*
**
c)执行l 接线盒测试。这个测试的动机不仅仅是检查不同滞后的随机性,就像案例 b 一样,相反,它让我们了解基于指定滞后数的变量的总体随机性。零假设:数据是独立分布的。每个滞后的 p 值大于 5%,表明我们未能拒绝零假设。
从 statsmodels.stats.diagnostic 导入 acorr_ljungbox
*lb_test = acorr_ljungbox(std_resid, lags = 20)
lb_test[1] //gives us the p-values at each lag*
在上述所有三种情况下,可以得出合理的结论,因为我们的模型正在产生表示白噪声过程的标准化残差,变量是随机的,因此彼此不相关,我们拟合和训练的模型工作得相当好。
预测
最后,是时候做一些预测了!我们将使用我们的模型来获得未来七天的波动预测。为了实现这一点,我们指定视界参数等于 7。这里需要注意的是,这些预测逐渐趋向于长期平均值。
*future_index = pd.date_range(start = (df_goog.index[-1]+timedelta(1)).strftime("%Y-%m-%d"), periods = 7, freq = 'D')
predict = model.forecast(horizon = 7)
pred_vol = np.sqrt(predict.variance.values[-1:][0])
pred_vol = pd.Series(pred_vol,index = future_index)
plt.plot(pred_vol, marker = 'o', markerfacecolor = 'r', linestyle = '--', markeredgecolor = 'k', markersize = 6)
plt.title("Next seven day volatility forecast")
plt.grid(True)*
预测表明波动性变化会略有增加,然后最终下降。那么回报的波动性是高还是低呢?投资谷歌是不是一个风险很大的时候?你能有多自信?
**注:您可以选择更高阶的模型,如 GARCH(3,0)或 GARCH(3,3)模型,但请记住,模型摘要中的 omega 、 alpha、和 beta 参数的值应该非常重要。另外,不要忘记检查和验证相应的标准残差。
在我的 GitHub 上找到 jupyter 笔记本。
参考资料:-
* [## 时间序列的广义自回归条件异方差 GARCH(p,q)模型
在这篇文章中,我们将考虑著名的广义自回归条件异方差模型…
www.quantstart.com](https://www.quantstart.com/articles/Generalised-Autoregressive-Conditional-Heteroskedasticity-GARCH-p-q-Models-for-Time-Series-Analysis/)
领英:-
[## Swarnim Pandey — SRM 大学—印度北方邦勒克瑙| LinkedIn
查看世界上最大的职业社区 LinkedIn 上的 Swarnim Pandey 的个人资料。Swarnim 有 2 份工作列在…
www.linkedin.com](https://www.linkedin.com/in/swarn6/)*
加利福尼亚州的加登格罗夫是美国的波巴之都
罗莎琳德·张在 Unsplash 上的照片
搜集 300 多个美国城市的 Yelp 数据,了解泡泡茶的受欢迎程度
加登格罗夫——一座人口 17 万多一点的城市,位于洛杉机以南 35 英里处——是美国的波巴首都。截至 2020 年 7 月,该市共有 33 家波霸商店,即该市每 5200 名居民就有一家商店。
从这个角度来看——截至 2020 年 6 月,美国有15.3 万家星巴克门店,为近 3.3 亿美国人服务,或者说每 21.5 万美国人就有一家星巴克门店。这意味着加登格罗夫的人均波霸商店数量是美国人均星巴克商店数量的四倍多。
也就是说,与加登格罗夫相比,美国其他城市的排名如何?这里先睹为快:
- 每位居民最多的波霸商店:加利福尼亚州加登格罗夫
- 波霸商店密度最高:加利福尼亚州旧金山
- 最高 Yelp 评分:南卡罗来纳州查尔斯顿
但是首先…这些数据是从哪里来的?
我使用了两个数据集:
- 美国城市数据集:为此,我使用了维基百科的 美国城市人口列表 ,,其中列出了 317 个使用 2019 年人口普查估计值的美国人口最多的城市。这份名单中所有城市的人口都超过 10 万。这些城市在下面的地图中标出:
- Boba 商店数据集:一旦我有了上面的城市列表,我就使用 Yelp 的 API 遍历所有的城市。收集数据的过程非常快——我能够在不到三分钟的时间内收集 4200 家波霸商店的数据。
为了了解单个城市的这一过程,让我们以旧金山为例。如果我想找到旧金山附近的波霸商店,我会发送一个 API 请求返回市中心 25 英里半径内的所有波霸商店 (Yelp 有一个我用过的名为“bubbletea”的类别)。冲洗并重复我列表中剩余的 316 个城市。
就是这样!我们可以将 Yelp 数据集加入城市数据集,并以多种不同的方式聚合数据。
其他城市排名—近距离观察
有几种方法可以看出波霸商店在一个城市的流行程度。本节探索了查看数据的不同方法,揭示了可以与加登格罗夫相抗衡的其他城市。
人均波巴商店:这是用来宣布加登格罗夫为美国波巴之都的方法。这是衡量波霸商店普及率最简单的指标,因为它回答了以下问题:该城市的每家波霸商店服务多少人?
如果我们只看每个城市的波霸商店数量,你最终会发现人口更多的城市,所以我不认为这个指标抓住了这一分析的精神。
紧随加登格罗夫之后的是伯克利,位居第二,糖地和檀香山紧随其后。也很高兴看到一些来自非加州的州的代表进入前 5 名!
波霸店铺密度: 每平方英里有多少家波霸店铺?
事情变得有点有趣了。我们有三个明显的突出,前两名分别是旧金山和伯克利。这是因为这两个北方城市的人口密度更高。
最高 Yelp 评分: 哪个城市的居民最爱他们的波霸?
我们可以看看每个城市波霸店铺的平均评分。为此,我们希望删除商店数量较少的城市,以及没有足够评论的城市,因为这些结果可能会有噪音。对于一个城市的资格,我会设置两个门槛:该城市必须至少有 5 个波霸商店和至少 500 个总评论。
当我们查看这些结果时,我们看到南部和西南部的城市给出了最高的评价!南卡罗来纳州的 Charleston 是这里的明显赢家,其 5 家不同的波霸商店的平均评分为 4.63。接下来最接近的城市是佛罗里达州的杰克逊维尔,得分为 4.42,以及加利福尼亚州的默里塔和俄亥俄州的辛辛那提,均为 4.41。
从下图中,我们还看到拉斯维加斯和火奴鲁鲁是仅有的拥有大量波霸商店的城市,跻身前 30 名。有很多商店的城市的居民往往对他们的波霸更挑剔:有最多评论的旧金山的平均评分只有 3.82,而圣何塞为 3.86。
但是等等……纽约市呢?
乔纳森·赖利在 Unsplash 上拍摄的照片
这就是事情变得有点复杂的地方——因为我只对上面的城市感兴趣,纽约没有进入任何一个名单,因为它太大了,而且很杂乱。然而,任何去过纽约或在纽约生活过的人都知道,这里有很多不错的选择——纽约实际上是拥有最多波霸商店的城市,有 335 家商店。因此,这一部分致力于近距离观察纽约市。
从下面的地图来看,波霸的商店主要有三个集群:
- 皇后区法拉盛
- 曼哈顿唐人街
- 布鲁克林日落公园
因为乘坐公共交通工具,骑自行车,甚至步行都可以很容易地在纽约市四处走动,所以我认为将这些街区与我在上面看到的其他城市进行比较是不公平的。如果我这么做了,那么这三个街区将会横扫波霸人均商店和波霸商店密度的第一、第二和第三名。
弗拉辛
为了证实这一点,我们可以仔细看看 Flushing,因为 Yelp 实际上将 Flushing out 分解为自己的“城市”(比如你可以看看 Yelp 上的 虎糖)。
法拉盛是一个有 72000 人的社区,有 44 家商店,所以每 16000 名居民就有一家商店。此外,法拉盛每平方英里有 33 家商店,将旧金山打得落花流水 10 倍以上!
唐人街
日落公园
寻找美国最受欢迎的商店
总的来说,大多数评论:在这里,我们看到一些来自加州的 Boba 商店排在前 20 名,只有两家来自州外。
优于竞争对手:我们可以将波霸的每家店铺与其竞争对手进行比较,方法是将每家店铺的评分与该市所有波霸店铺的总体平均评分进行比较。与我在上一节中所做的类似,我只包括有足够多评论的商店——在这种情况下,我将截止值设为 500。这应该能让我们控制人们倾向于评分较低的城市(如旧金山)。
一些标注
在上面的分析中没有考虑到的事情很少——尽管肯定有可能找到解决它们的方法,但为了节省时间,我决定不把它们包括在分析中。从最重要到最不重要:
- 哪个城市将成为后 COVID 时代的第一名:随着餐馆经历一段艰难时期,一些波霸商店可能会在未来几个月内关闭。由于只有 1-2 家商店分布在这个列表中的顶级城市,如果我们在 2021 年重新审视这个问题,它们之间可能会有一些变化。
2。波霸商店的类型:该分析查看了所有在 Yelp 上有“Bubble Tea”类别的商店。然而,你也有像冰柱一样的商店,出售波霸,但主要出售其他东西,如冰淇淋。所以在分类时会有一些模糊性。
3。波霸商店的规模:该分析对所有波霸商店一视同仁,但是有些商店会比其他商店大得多,可以服务更多的顾客。
4。一些波霸商店不在 Yelp 上:并非所有的商家都在 Yelp 上列出。然而,如果你所在的城市有很多你的竞争对手在 Yelp 上,我想你可能也希望你的生意在那里。为了得到一个更明确的波霸商店列表,我可以看看其他来源,如谷歌或黄页。
总之…
如果你发现自己在上述城市之一,别忘了去看看他们众多的波霸商店中的一家!
参考
- Yelp API
- 美国人口数据
- 用于分析的代码(Github)
用 Python 收集所有冠状病毒相关数据的简单方法
简单是关键。
冠状病毒在全球的传播令人担忧。截至今日(2020 年 7 月 15 日),冠状病毒 确诊病例超 1300 万。****
约翰·霍普斯金地图
目标
这篇文章的目的是获得研究所需的数据,并通过将所有相关数据收集到一个Pandas数据框架中获得关于新冠肺炎的主动可见性。
步伐
- 步骤 1:设置技术先决条件
- 第二步:聚集新冠肺炎确诊病例
- 第三步:收集 COVID 的消息
- 第四步:收集财务和其他指标
- 第五步:将所有的数据混合在一起
第一步。先决条件
- 安装 Python 2.6+或 3.1+版本
- 安装 pandas,matplotlib,openblender 和 wordcloud(带 pip)
*$ pip install pandas OpenBlender matplotlib wordcloud*
第二步。收集新冠肺炎确诊病例
CSSE 正在做惊人的工作上传每日数据 这里。然而,它非常杂乱并且在许多不同的数据集上,所以我将它分类并上传为一个单独的 OpenBlender 数据集:
让我们通过运行以下脚本将数据拉入熊猫数据帧:
*from matplotlib import pyplot as plt
import OpenBlender
import pandas as pd
import json
%matplotlib inline action = 'API_getObservationsFromDataset'parameters = {
'token':'**YOUR_TOKEN_HERE**',
'id_dataset':'5e7a0d5d9516296cb86c6263',
'date_filter':{
"start_date":"2020-01-01T06:00:00.000Z",
"end_date":"2020-03-11T06:00:00.000Z"},
'consumption_confirmation':'on',
'add_date' : 'date'
}
df_confirmed = pd.read_json(json.dumps(OpenBlender.call(action, parameters)['sample']), convert_dates=False, convert_axes=False).sort_values('timestamp', ascending=False)
df_confirmed.reset_index(drop=True, inplace=True)
df_confirmed.head(10)*
- 注意:要获得令牌,您需要在 openblender.io (免费)上创建一个帐户,您可以在您个人资料图标的“帐户”选项卡中找到它。
因此,现在我们有了按日期和地点汇总的确诊病例数、死亡人数和康复人数。
这里我们可以看到伊朗、意大利和韩国确诊病例的爆发。我们还可以看到西班牙、法国和德国开始崛起。
第三步。收集新冠肺炎的消息
我们将从这些来源收集 COVID 新闻和文本:华尔街日报、 CNN 新闻、 ABC 新闻和今日美国推特(你可以寻找其他来源)
所以让我们获取数据。
*action = 'API_getOpenTextData'parameters = {
'token':'**YOUR_TOKEN_HERE**',
'consumption_confirmation':'on',
'date_filter':{"start_date":"2020-01-01T06:00:00.000Z",
"end_date":"2020-03-10T06:00:00.000Z"},
'sources':[
# Wall Street Journal
{'id_dataset' : '5e2ef74e9516294390e810a9',
'features' : ['text']},
# ABC News Headlines
{'id_dataset':"5d8848e59516294231c59581",
'features' : ["headline", "title"]},
# USA Today Twitter
{'id_dataset' : "5e32fd289516291e346c1726",
'features' : ["text"]},
# CNN News
{'id_dataset' : "5d571b9e9516293a12ad4f5c",
'features' : ["headline", "title"]}
],
'aggregate_in_time_interval' : {
'time_interval_size' : 60 * 60 * 24
},
'text_filter_search':['covid', 'coronavirus', 'ncov'],
'add_date' : 'date'
}
df_news = pd.read_json(json.dumps(OpenBlender.call(action, parameters)['sample']), convert_dates=False, convert_axes=False).sort_values('timestamp', ascending=False)df_news.reset_index(drop=True, inplace=True)*
以上,我们指定了以下内容:
- 我们选择了 4 个来源来收集数据。具体来说,带有文本的功能列
- 我们指定要从 1 月 1 日到今天(3 月 10 日)的数据
- 我们要求将新闻汇总成 24 小时小组或观察
- 我们过滤了提及“冠状病毒”、“冠状病毒”或“ncov”的新闻
*# Let's take a look
df_news.head(20)*
每个观察都是按天对新闻的聚合,我们有 source 和 source_lst ,前者是所有新闻的串联,后者是该时段的新闻列表。
时间戳(返回为 unix 时间戳)是指从前一个时间戳到当前时间戳(严格来说是之前)的间隔内发生的新闻:
现在让我们来看看一些感兴趣的国家的提及次数。
*interest_countries = ['China', 'Iran', 'Korea', 'Italy', 'France', 'Germany', 'Spain']for country in interest_countries:
df_news['count_news_' + country] = [len([text for text in daily_lst if country.lower() in text]) for daily_lst in df_news['source_lst']]df_news.reindex(index=df_news.index[::-1]).plot(x = 'timestamp', y = [col for col in df_news.columns if 'count' in col], figsize=(17,7), kind='area')*
**
我们来看一个来自最近 20 天新闻的词云。 可选步骤(如果要安装 wordcloud )😗
*from os import path
from PIL import Image
from wordcloud import WordCloud, STOPWORDS, ImageColorGeneratorplt.figure()
plt.imshow(WordCloud(max_font_size=50, max_words=80, background_color="white").generate(' '.join([val for val in df['source'][0: 20]])), interpolation="bilinear")
plt.axis("off")
plt.show()plt.figure()
plt.imshow(WordCloud(max_font_size=50, max_words=80, background_color="white").generate(' '.join([val for val in df['source'][0: 20]])), interpolation="bilinear")
plt.axis("off")
plt.show()*
*上面的代码生成了图像的最后一个 wordcloud。
早期新闻中的关键词如“神秘”、“正在调查”、“几十个”与后期关键词如“中国”、“冠状病毒”、“全球”形成对比。更有最近的:“新型冠状病毒”、“冠状病毒流行”、“爆发”、“巡航”、“新型冠状病毒”等..
第四步。收集财务和其他指标
为此,我们可以从道琼斯数据集中分离出来,混合其他几个数据,比如汇率(日元、欧元、英镑)材料价格(原油、玉米、铂、锡),或者股票(可口可乐、道琼斯)。
*action = 'API_getObservationsFromDataset'
parameters = {
'token':'**YOUR_TOKEN_HERE**',
'id_dataset':'5d4c14cd9516290b01c7d673', 'aggregate_in_time_interval':{"output":"avg","empty_intervals":"impute","time_interval_size":86400}, 'blends':[
#Yen vs USD
{"id_blend":"5d2495169516290b5fd2cee3","restriction":"None","blend_type":"ts","drop_features":[]}, # Euro Vs USD
{"id_blend":"5d4b3af1951629707cc1116b","restriction":"None","blend_type":"ts","drop_features":[]}, # Pound Vs USD
{"id_blend":"5d4b3be1951629707cc11341","restriction":"None","blend_type":"ts","drop_features":[]}, # Corn Price
{"id_blend":"5d4c23b39516290b01c7feea","restriction":"None","blend_type":"ts","drop_features":[]}, # CocaCola Price
{"id_blend":"5d4c72399516290b02fe7359","restriction":"None","blend_type":"ts","drop_features":[]}, # Platinum price
{"id_blend":"5d4ca1049516290b02fee837","restriction":"None","blend_type":"ts","drop_features":[]}, # Tin Price
{"id_blend":"5d4caa429516290b01c9dff0","restriction":"None","blend_type":"ts","drop_features":[]}, # Crude Oil Price
{"id_blend":"5d4c80bf9516290b01c8f6f9","restriction":"None","blend_type":"ts","drop_features":[]}],'date_filter':{"start_date":"2020-01-01T06:00:00.000Z","end_date":"2020-03-10T06:00:00.000Z"},
'consumption_confirmation':'on'
}df = pd.read_json(json.dumps(OpenBlender.call(action, parameters)['sample']), convert_dates=False, convert_axes=False).sort_values('timestamp', ascending=False)
df.reset_index(drop=True, inplace=True)print(df.shape)
df.head()*
所以现在我们有了一个单独的数据集,其中包含每日观察到的价格随时间的变化。如果我们想要比较它们,我们最好将它们归一化到 0 到 1 之间,这样我们就可以更好地欣赏这些模式:
*# Lets compress all into the (0, 1) domain
df_compress = df.dropna(0).select_dtypes(include=['int16', 'int32', 'int64', 'float16', 'float32', 'float64']).apply(lambda x: (x - x.min()) / (x.max() - x.min()))
df_compress['timestamp'] = df['timestamp']# Now we select the columns that interest us
cols_of_interest = ['timestamp', 'PLATINUM_PRICE_price', 'CRUDE_OIL_PRICE_price', 'COCACOLA_PRICE_price', 'open', 'CORN_PRICE_price', 'TIN_PRICE_price', 'PLATINUM_PRICE_price']
df_compress = df_compress[cols_of_interest]
df_compress.rename(columns={'open':'DOW_JONES_price'}, inplace=True)# An now let's plot them
from matplotlib import pyplot as plt
fig, ax = plt.subplots(figsize=(17,7))
plt = df_compress.plot(x='timestamp', y =['PLATINUM_PRICE_price', 'CRUDE_OIL_PRICE_price', 'COCACOLA_PRICE_price', 'DOW_JONES_price', 'CORN_PRICE_price', 'TIN_PRICE_price', 'PLATINUM_PRICE_price'], ax=ax)*
有趣的是,几乎所有的(除了锡价)都遵循相似的模式。
第五步。将所有数据混合在一起
现在,我们将把新冠肺炎确诊病例、冠状病毒新闻和经济指标数据排列成按照时间混合的单一数据集。**
为了混合数据,让我们将创建的数据集上传到 OpenBlender:
***# First the News Datasetaction = 'API_createDataset'parameters = {
'token':'**YOUR_TOKEN_HERE**',
'name':'Coronavirus News',
'description':'YOUR_DATASET_DESCRIPTION',
'visibility':'private',
'tags':[],
'insert_observations':'on',
'select_as_timestamp' : 'timestamp',
'dataframe':df_news.to_json()
}
OpenBlender.call(action, parameters)***
***# And now the Financial Indicatorsaction = 'API_createDataset'parameters = {
'token':'**YOUR_TOKEN_HERE**',
'name':'Financial Indicators for COVID',
'description':'YOUR_DATASET_DESCRIPTION',
'visibility':'private',
'tags':[],
'insert_observations':'on',
'select_as_timestamp' : 'timestamp',
'dataframe':df_compress.to_json()
}
OpenBlender.call(action, parameters)***
*注意:您将希望在下面使用每个数据集的“id_dataset”。
现在,我们只需提取初始新冠肺炎数据集,并通过将来自我们创建的数据集的“id _ dataset”s放置在“id _ blend”字段上,来混合我们创建的新数据集。**
***action = 'API_getObservationsFromDataset'# ANCHOR: 'COVID19 Confirmed Cases'
# BLENDS: 'Coronavirus News', 'Financial Indicators for COVID'
parameters = {
'token':'**YOUR_TOKEN_HERE**',
'id_dataset':'5e7a0d5d9516296cb86c6263',
'date_filter':{
"start_date":"2020-01-01T06:00:00.000Z",
"end_date":"2020-03-11T06:00:00.000Z"} ,'filter_select' : {'feature' : 'country_region', 'categories' : ['Italy']},'aggregate_in_time_interval':{"output":"avg","empty_intervals":"impute","time_interval_size":86400}, 'blends':[{"id_blend":"**YOUR_CORONA_NEWS_ID**","restriction":"None","blend_type":"ts","drop_features":[]},
{"id_blend":"**YOUR_FINANCIAL_INDICATORS_ID**","restriction":"None","blend_type":"ts","drop_features":[]}]
}df = pd.read_json(json.dumps(OpenBlender.call(action, parameters)['sample']), convert_dates=False, convert_axes=False).sort_values('timestamp', ascending=False)
df.reset_index(drop=True, inplace=True)***
上面我们选择了“意大利”、的观测值,按天以秒为单位(8640)进行聚合,混合了新数据。**
***df.head()***
现在我们有了一个数据集,它包含了所有数据按时间混合的日常信息!****
这有很大的发展空间。
请发布进一步的发现。
收集你的数据:常见的!
维克多·塔拉舒克在 Unsplash 上拍摄的照片
使用 Python 从 zip、csv、tsv、xlsx、txt
你知道哪个是最受欢迎的数据科学家模因吗? “收集数据是我的心脏”。 没错,的确如此!与数据科学领域的初学者所想的不同,数据很少以干净整洁的表格格式提供给你。
在现实世界中,数据需要从多个来源收集,如平面文件、zip 文件、数据库、API、网站等。不仅仅是源代码,它们可以是不同的格式、文件结构,如。csv,。tsv、json 等,并由不同的分隔符分隔。如何从这个烂摊子中理出头绪?
作者图片
要执行分析并从我们的数据中获得准确的结果,首先,您应该知道不同的格式,其次,知道如何将它们导入 Python。在这篇博客中,我将涉及一些您可能熟悉的基本文件结构,并提到用于导入数据的不同但通用的 python 库。
掌握这些文件格式对你在数据领域的成功至关重要。那么每个人都从哪里开始呢?哦,是的,无处不在的 csv 文件。
用 python 读取 CSV 文件
用于存储数据的最常见的平面文件结构是逗号分隔值(CSV)文件。它包含由逗号(,)分隔符分隔的纯文本格式的表格数据。要识别文件格式,通常可以查看文件扩展名。例如,以“CSV”格式保存的名为“datafile”的文件将显示为“datafile.csv”。
Pandas 库用于从 python 中的 csv 文件读取数据。Pandas 库中的 read_csv() 函数用于加载数据并将其存储在数据帧中。
在 python 中读取其他平面文件
平面文件是包含表格行列格式记录的数据文件,没有任何编号结构。CSV 是最常见的平面文件。但是也有其他平面文件格式,其中包含带有用户指定分隔符的数据,如制表符、空格、冒号、分号等。
制表符分隔值(TSV)文件是第二常见的平面文件结构。来自 Pandas 库中的相同 read_csv()函数用于读取这些文件,您只需要为 read_csv()函数的参数' sep '指定正确的分隔符。
用 python 读取 XLSX 文件
一个带有。xlsx 文件扩展名是由 Microsoft Excel 创建的 Microsoft Excel Open XML 电子表格(XLSX)文件。这些文件是在 Microsoft Excel 中使用的文件,Microsoft Excel 是一种电子表格应用程序,使用表格来组织、分析和存储数据。
电子表格的每个单元格由行和列定位,可以包含文本或数字数据,包括合并数学公式。在早期版本的 Excel 中制作的电子表格文件以 XLS 格式保存。
pandas 库中的 read_excel() 函数用于将 excel 文件数据加载到 python 中。
XLSX 文件将数据组织到存储在工作表中的单元格中,这些单元格又存储在工作簿(包含多个工作表的文件)中。为了读取所有的工作表,我们使用 pandas ExcelFile() 函数。现在,我们只需在 read_excel()函数的参数 sheet_name 中指定工作表的名称,就可以从工作簿中读取特定的工作表。
在 python 中提取 ZIP 文件
扩展名为 ZIP 的文件是 ZIP 压缩文件,是您将遇到的最广泛使用的归档格式。
与其他归档文件格式一样,ZIP 文件只是一个或多个文件和/或文件夹的集合,但为了便于传输和压缩,它被压缩成一个文件。
我们经常获得压缩格式的 csv 或 excel 文件,因为它节省了服务器上的存储空间,减少了您将文件下载到计算机的时间。python 中的 zipfile 模块用于提取。压缩文件。
使用 python 读取文本文件
纯文本格式的基本概念结构是数据按行排列,每行存储几个值。纯文本文件(也称为扩展名 TXT)由按特定字符编码顺序编码的字符组成。
Python 提供了各种函数来读取文本文件中的数据。每行文本都以一个称为 EOL(行尾)的特殊字符结束,这是 python 中默认的换行符(' \n ')。
Python 文件操作,具体来说就是打开文件、读取文件、关闭文件和其他各种文件方法,这些都是你在访问文本文件时应该知道的。内置 Python 函数 open()有模式、编码等参数。用于在 python 中正确打开文本文件。
Python 为我们提供了三种从文本文件中读取数据的内置方法,read([n])、readline([n])和 readlines()。这里的“n”是要读取的字节数。如果没有东西传递给 n,那么整个文件被认为是被读取的。
如果参数中没有给出字节数(n ),则 read() 方法只输出整个文件。
readline(n) 最多输出文件单行的 n 个字节。它只能读一行。
readlines() 方法维护了文件中每一行的列表,可以使用 for 循环对其进行迭代。
以上给出了用 python 读取文本文件的基本思路。但是,数据从来不会以一种易于访问的格式呈现在我们面前。假设你想根据你附近的评论找到 10 家最受欢迎的餐馆。
假设有 1000 家餐馆,其数据存在于 1000 个单独的文本文件中,您需要这些文件中存在的餐馆名称、评论 url 和评论文本。为了将多个文件中的数据读入一个数据帧,使用了 Python 的 glob 库。
Python 内置的 glob 模块用于检索与指定模式匹配的文件/路径名。glob 的模式规则遵循标准的 Unix 路径扩展规则。
/是 glob 语句中的通配符。星号()匹配一个名称段中的零个或多个字符。您需要始终在 open()函数中指定编码,以便正确读取文件。
这里,文本文件的第一行是餐馆名称,第二行是餐馆评论 url,其余行是评论文本。相应地使用 readline()和 read()函数来提取这些数据。
在这篇博客中,我介绍了数据行业中人们使用的最常见和最流行的文件结构。这应该为你在处理数据收集和混乱的文件结构时的下一次挫折做好准备。还有许多其他的,我打算在我接下来的博客中讨论。
如果你喜欢这篇博文,请在下面留下评论并与朋友分享!
收集您的数据:“不那么怪异”的 API!
照片由 Fotis Fotopoulos 在 Unsplash 上拍摄
当 python 处理互联网文件时。
数据分析周期从收集和提取开始。我希望我之前的博客给出了如何使用 python 从常见文件格式中收集数据的想法。在这篇博客中,我将着重于从那些并不常见但却有着最真实应用的文件中提取数据。
无论您是否是数据专业人员,您现在可能都已经遇到了术语 API。大多数人对这个相当常见的术语有一个相当模糊或不正确的概念。
使用 python 中的 API 提取数据
API 是应用编程接口的首字母缩写,它是一个软件中介(中间人),允许两个应用相互对话。
每次你使用像 Tinder 这样的应用程序,发送 WhatsApp 信息,在手机上查看天气,你都在使用 API。它们允许我们在设备、应用程序和个人之间共享重要的数据和公开实际的业务功能。虽然我们可能没有注意到它们,但 API 无处不在,在幕后为我们的生活提供动力。
我们可以把 API 比喻成银行的 ATM(自动柜员机)。银行让我们可以使用他们的自动提款机来查询余额、存款或提取现金。所以在这里,ATM 是帮助银行和我们这些顾客的中间人。
类似地,Web 应用程序使用 API 将面向用户的前端与所有重要的后端功能和数据连接起来。Spotify 和网飞等流媒体服务使用 API 来分发内容。像特斯拉这样的汽车公司通过 API 发送软件更新。更多的例子,你可以查看文章5我们日常生活中使用的 API 的例子。
在数据分析中,API 最常用于检索数据,这将是这篇博客的重点。
当我们想从一个 API 中检索数据时,我们需要发出一个请求。请求在网络上随处可见。例如,当您访问这篇博文时,您的 web 浏览器向面向数据科学的 web 服务器发出了一个请求,该服务器以这个网页的内容作为响应。
API 请求以同样的方式工作——你向 API 服务器请求数据,它响应你的请求。使用 API 主要有两种方式:
- 通过使用 URL 端点的命令终端,或者
- 通过编程语言特定的包装器。
例如, Tweepy 是一个著名的 Twitter API python 包装器,而 twurl 是一个命令行界面(CLI)工具,但两者可以实现相同的结果。
这里我们关注后一种方法,并将使用基于原始 MediaWiki API 的名为 wptools 的 Python 库(包装器)。 MediaWiki action API 是 Wikipedia 的 API ,它允许访问一些 Wiki 特性,如身份验证、页面操作和搜索。
Wptools 对 MediaWiki APIs 进行只读访问。你可以通过传统的 Mediawiki API 从任何语言的任何 Wikimedia 项目中获得关于 wiki 站点、类别和页面的信息。您可以从页面信息框中提取非结构化数据,或者通过 Wikidata API 获取关于页面的结构化、链接的开放数据,并从高性能 RESTBase API 获取页面内容。
在下面的代码中,我使用 python 的 wptools 库访问了圣雄甘地维基百科页面,并从该页面提取了一个图像文件。对于 Wikipedia URL ' https://en . Wikipedia . org/wiki/Mahatma _ Gandhi ',我们只需要传递 URL 的最后一位。
get 函数获取所有内容,包括摘录、图像、infobox 数据、wiki 数据等。呈现在那一页上。通过使用。data()函数我们可以提取所有需要的信息。我们对 API 的请求得到的响应很可能是 JSON 格式的。
用 python 从 JSON 读取数据
克里斯托夫·高尔在 Unsplash 上拍摄的照片
JSON 是 JavaScript 对象符号的首字母缩写。它是一种轻量级的数据交换格式。人类读&写就像机器解析&生成一样容易。JSON 已经迅速成为事实上的信息交换标准。
在浏览器和服务器之间交换数据时,数据只能是文本。JSON 是文本,我们可以将任何 JavaScript 对象转换成 JSON,并将 JSON 发送到服务器。
例如,你可以用浏览器直接访问 GitHub 的 API ,甚至不需要访问令牌。当你在你的浏览器https://api.github.com/users/divyanitin中访问一个 GitHub 用户的 API 路由时,你得到的 JSON 响应如下:
{
"login": "divyanitin",
"url": "https://api.github.com/users/divyanitin",
"html_url": "https://github.com/divyanitin",
"gists_url": "https://api.github.com/users/divyanitin/gists{/gist_id}",
"type": "User",
"name": "DivyaNitin",
"location": "United States",
}
浏览器似乎已经很好地显示了 JSON 响应。这样的 JSON 响应已经可以在您的代码中使用了。从这段文字中提取数据很容易。然后你可以对这些数据做任何你想做的事情。
Python 原生支持 JSON。它附带了一个 json 内置包,用于编码和解码 JSON 数据。JSON 文件在{}中存储数据,类似于字典在 Python 中存储数据的方式。类似地,JSON 数组被翻译成 python 列表。
在我上面的代码中,wiki_page.data['image'][0]访问 image 属性中的第一个图像,即一个 JSON 数组。使用 Python json 模块,您可以像读取简单的文本文件一样读取 json 文件。
read 函数 json.load()返回一个 json 字典,使用 pandas 可以很容易地将其转换为 Pandas 数据帧。DataFrame()函数。您甚至可以使用 pandas.read_json()函数将 JSON 文件直接加载到 dataframe 中。
从互联网上读取文件(HTTPS)
照片由 Edho Pratama 在 Unsplash 上拍摄
HTTPS 代表超文本传输协议安全。它是一种网络浏览器和网络服务器相互交流的语言。web 浏览器可能是客户端,而托管网站的计算机上的应用程序可能是服务器。
我们正在编写与远程 API 一起工作的代码。你的地图应用获取附近印度餐馆的位置,或者 OneDrive 应用启动云存储。所有这一切都是通过 HTTPS 的请求实现的。
Requests 是 python 中的一个多功能 HTTPS 库,具有各种应用。它作为客户端和服务器之间的请求-响应协议。它提供了通过 HTTPS 访问 Web 资源的方法。它的应用之一是使用文件 URL 从网上下载或打开文件。
要发出“GET”请求,我们将使用 requests.get()函数,该函数需要一个参数—我们要请求的 URL。
在下面的脚本中,open 方法用于将二进制数据写入本地文件。在这里,我们使用 python 的操作系统库在系统上创建一个文件夹并保存提取的 web 数据。
json 和 requests import 语句加载 Python 代码,允许我们使用 JSON 数据格式和 HTTPS 协议。我们使用这些库是因为我们对如何发送 HTTPS 请求或者如何解析和创建有效 JSON 的细节不感兴趣,我们只想用它们来完成这些任务。
一种流行的 web 架构风格叫做 REST(表述性状态转移) ,它允许用户通过 GET 和 POST 调用(两种最常用的调用)与 web 服务进行交互。
GET 一般用于获取一些已经存在的对象或记录的信息。相比之下, POST 通常用在你想创造一些东西的时候。
REST 本质上是一组用于构建 web API 的有用约定。我所说的“web API”指的是通过 HTTP 与您交互的 API,它向特定的 URL 发出请求,并经常在响应中获得相关的数据。
例如,Twitter 的 REST API 允许开发人员访问核心 Twitter 数据,Search API 为开发人员提供了与 Twitter 搜索和趋势数据进行交互的方法。
这个博客主要关注所谓的互联网文件。我介绍了使用简单的 API 提取数据。大多数 API 都需要认证,就像我之前的类比 ATM 一样,需要我们输入 pin 来认证我们对银行的访问。
你可以看看我的另一篇文章Twitter Analytics:“WeRateDogs”,这篇文章关注的是 twitter API 的数据争论和分析。我已经在这个项目中使用了所有上述脚本,你可以在我的 GitHub 上找到相同的代码。
众所周知,最常见的互联网文件之一是 HTML。从互联网上提取数据有一个通用术语叫网络搜集。在这种情况下,我们使用 HTML 直接访问网站数据。在我的下一篇博客中,我会涵盖同样的内容,从“基本”到“必须知道的”。
如果你喜欢这篇博文,请在下面留下评论,并与朋友分享!
仪表图和项目符号图
为什么&怎么样,用量具讲故事
图片由来自 Pixabay 的 Brett Hondow 提供
商业智能(BI)是商业环境中使用的一套方法和资源(理论概念、算法、软件、工具、技术),其基本目标是将信息转化为知识。商业智能的目的是支持更好的商业决策,以提高任何商业组织的生产力和绩效。
BI 在很大程度上依赖于一套核心的分析工具:平衡记分卡、仪表盘和关键绩效指标。
平衡记分卡 (BS)是一种管理工具,允许将短期运营控制与被研究公司的长期愿景和战略相结合。为此,BS 制定、实施、控制和管理组织的战略,有效地将战略规划、运营管理以及团队和个人绩效评估联系起来。
关键绩效指标(KPI)是任何组织为了实现其战略计划都需要衡量、监控和执行的最重要的指标。
商业智能仪表板是一种技术驱动的方法,它使用数据可视化工具来显示 KPI,以便跟踪有助于业务运营和成功的主要因素。它是在追求公司设定的目标过程中所涉及的主要指标的图形表示。
企业主和公司高管总是想要简单的解决方案。从这个意义上说,数据分析师必须让仪表板易于查看、导航和理解。用于在仪表板上显示 KPI 的两种传统图形是仪表图和项目符号图。
我们分别来分析一下。
1。-仪表图
又名:车速表图表、表盘图表、角度规图表
为什么:广泛用于商业智能(BI)可视化,特别是仪表板中,用于指示数据或测量值是否在某个值范围之内、之下或之上。它们是非常简单的图形,能够很快抓住观众的注意力,即使是非专业观众也是如此。它们提供关于一个单一定量测量的信息,将它的当前值与一个目标和一系列由不同颜色的条带表示的范围进行比较。下图表明当前销售值(420)在令人满意的范围内,但比预定目标低 30 个单位。
图 1:标准仪表图。目标是 450。阈值在 250 和 350。
如何:虽然有不同的格式,但最常见的是对应于车辆的速度表:一个径向数字刻度分成几个部分,每个部分用特定的颜色标识;指针或指针在径向标尺上移动;最小或下限值、最大或上限值和目标值。范围或扇区的数量是可变的,但通常有三个扇区,红色表示低于下限阈值的扇区,黄色表示阈值之间的扇区,绿色表示高于上限阈值的扇区。建议不要显示超过五个范围或扇区。
以下是与不同颜色相关的一些可能的定性值:差、满意和好;绿色表示满意,黄色表示小心,红色表示报警;差、一般、优秀;红色代表低性能,黄色代表正常性能,绿色代表高性能。这些定性值的范围广泛用于 BI 演示或仪表板上,以显示给定公司的业务绩效。目标值可以是要达到的目标、基准或要超过的先前值。
不要将仪表图与圆环图(圆环图)混淆。虽然前者仅报告定量测量,但环形图与可分为单个部分的整体相关,以及每个部分如何以相对或绝对方式与总量相关(整体的部分分析)。****
仪表图在用数据讲述故事时有几个优点😗***
它们非常简单,通俗易懂,迅速抓住观众的注意力;
观众对它们很熟悉:汽车速度计、电脑游戏中的指示器、烤箱或火炉中的温度指示等。不像更复杂的图形,没有必要“浪费时间”去解释它们;
颜色的顺序,从红色到绿色,是直观和立即理解的;
大多数商业和公司主管更喜欢它们,因为它们简单、清晰、即时地传输关键数据;
但是它们也充满了缺点,在讲故事的时候必须考虑到这些缺点:
它们的简单性不允许它们描绘背景;
他们经常通过省略关键信息来误导观众,导致糟糕的故事情节。
人类很难通过角度来比较数值;我们总是喜欢通过从共同基线开始的长度进行比较(项目符号图);
它们占据了大量的物理空间,而且它们的格式会分散观众的注意力。仪表板和记分卡上经常堆满了指标,这使得讲故事的过程变得更加复杂;
请记住色盲(色觉缺陷)大约影响十二分之一的男性和两百分之一的女性。最常见和最有问题的颜色缺乏形式被称为“红绿色盲”,准确地说是仪表中常用的两种颜色。由于这些原因,实际的趋势是将扇区编码为具有从暗到亮的不同强度的单一色调。较深的颜色强度用于低值或差值,而较浅的颜色强度表示满意或优秀值(图 2);
最后一个警告来自 Robert Kosara (#1):“在数据可视化中,仪表只是解决了错误的问题。业务决策不能只基于某个度量的当前值,而是需要历史和背景。数值通常也不会像加速器上的压力大小和汽车速度那样快速变化,也不会直接相关。
图 2:角度规图。用 Vizzlo 创建,有权限(#2)。
Matplotlib 库没有允许我们直接绘制仪表图的功能。在以下网站可以找到一个非常巧妙的解决问题的方法:https://water gramming . WordPress . com/2018/06/04/creating-shaded-dial-plots-in-python/。一个更简单的解决方案是基于 Plotly 图形库,它有一个指示器功能:https://plotly.com/python/gauge-charts/。
2。-项目符号图
又名:项目符号图
为什么:著名的数据可视化专家Stephen first开发了一种替代量规的方法,他将其命名为子弹图(#3)。关键的想法是通过添加补充措施来丰富信息,以描绘背景,使用更少的物理空间,没有分散观众注意力的装饰。项目符号图提供有关单个定量测量的信息,将它的当前值与目标值以及一系列由不同颜色的条带指示的范围进行比较。****
如何:它们类似于标准条形图(它们通过长度或高度对信息进行编码),但与标准条形图的不同之处在于,它们包括一个中间窄条,指示所报告的数值变量的当前值。它们也有一条垂直线显示目标或对象,以及不同的范围或扇区(通常是三个),用不同颜色的带或单一色调的不同强度来指示。与仪表图不同,不同的类别可以在单个图表中进行比较。****
图 3:标准的项目符号图。目标是 450。阈值在 250 和 350。
标准项目符号图有五个组成部分(#3):
指示测量变量和相应测量单位的文本标签;
沿着线性轴的定量刻度,其刻度和数字标识等间距的测量间隔;
用清晰可见的条形编码的主要或特色测量,其基线可能不为零;
垂直于图表方向绘制的细线形式的比较度量(如果代表主要度量的条是水平的,则为垂直线;如果代表主要度量的条是垂直的,则为水平线);
两到五个定性的绩效范围,表示主要衡量标准的定性状态。这些范围通过不同的颜色或单一色调的不同强度来区分。
项目符号图以垂直或水平方向呈现。水平项目符号图允许在垂直轴上显示不同的类别,而数字值由水平轴上的数量刻度表示。相反,垂直项目符号图允许在水平轴上显示不同的类别,而在垂直轴上用数量刻度显示数值。定量刻度允许您在线性轴上显示变量的测量值,比仪表的角轴更容易读取。
图 4:具有五个扇区的水平定向的项目符号图。目标是 78%。
与仪表图类似,范围或部门最常见的定性值有:差、满意和好;绿色表示满意,黄色表示小心,红色表示报警(图 4);差、一般、优秀;低于绩效、一般绩效和高于绩效。
仪表板上的项目符号图和其他 BI 可视化工具通常显示的数字变量是销售额、收入、利润、费用、客户数量和相关数据。
不要将项目符号图表与进度条 图表混淆。后者是一个图形控制元素,用于可视化某个操作的进度,如下载文件、传输文件或安装软件。一些可视化工具建议使用进度条作为业务指示器,如下图所示:
图 5:进度条形图。用 Vizzlo 创建,有权限(#2)。
与仪表图一样,Matplotlib 没有允许您立即绘制项目符号图的功能。同样,我发现了一个基于 Matplotlib 和 Seaborn 的非常巧妙的解决方案:【https://pbpython.com/bullet-graph.html。我还找到了一个更好的基于 Plotly 的解决方案:https://plotly.com/python/bullet-charts/
中国一所大学进行的一项科学研究比较了 40 名大学生在面对水平子弹图、垂直子弹图和角度规图(#4)时的情绪差异。每个图形备选方案所需的认知负荷、学生感知的满意度以及每个可视化备选方案的美学效果被用作主观指标。该研究得出结论,垂直方向的项目符号图产生最高的满意度和最低的认知负荷。相比之下,角度规图导致最低的满意度,最高的认知负荷,尽管它们是最具美感的。
我建议你将这些结果用于你的下一次商业演示。
在许多公司进行数字化转型的背景下,商业智能和数据可视化技术变得越来越重要。可视化和图表定制的可能性非常多:有几十种不同的图表在可视化数据时提供了很大的灵活性,但有时只需要简单、易于理解的图表,就可以迅速抓住观众的注意力。仪表和项目符号图提供了简单性和熟悉性,这是观众非常欣赏的。
特别是,我们建议使用项目符号图,因为它们能够在较小的空间内编码更多的信息。
如果你对这篇文章感兴趣,请阅读我以前的:
直方图,为什么&怎么样,讲故事,提示&扩展
讲故事、技巧和扩展
towardsdatascience.com](/histograms-why-how-431a5cfbfcd5)
平行坐标剧情,为什么&怎么样,用平行线讲故事
为什么&如何:用类比讲故事
towardsdatascience.com](/parallel-coordinates-plots-6fcfa066dcb3)
参考文献
# 1:https://eagreyes . org/crisis/data-display-vs-data-visualization
2:https://vizzlo.com/
# 3:https://www . perceptual edge . com/articles/misc/Bullet _ Graph _ Design _ spec . pdf
#4:,,郭,,牟力军,“子弹图与仪表图:基于眼动跟踪方法的工业可视化人类信息处理评价”,T. Z. Ahram 和 C. Falcã(编辑。):AHFE 2018,AISC 794,第 752–762 页,2019。https://doi.org/10.1007/978-3-319-94947-5_74****
高斯混合模型和期望最大化(完整解释)
美国宇航局在 Unsplash 拍摄的照片
在之前的文章中,我们描述了线性回归的贝叶斯框架,以及我们如何使用潜在变量来降低模型的复杂性。
在本帖中,我们将解释潜在变量是如何被用来构建分类问题的,即高斯混合模型(或简称 GMM),它允许我们执行软概率聚类。
这个模型是由一个名为期望最大化(简称 EM)的优化过程训练出来的,我们将对此进行全面的回顾。在本文的最后,我们还将看到为什么我们不使用传统的优化方法。
这篇文章包含一些数学符号和推导。我们不想吓唬任何人。我们相信,一旦直觉被赋予,深入数学去真正理解事物是很重要的。
这篇文章的灵感来自 Coursera 上的一门优秀课程:机器学习的贝叶斯方法。如果你对机器学习感兴趣,我绝对推荐这门课程。
高斯混合模型
该模型是一种软概率聚类模型,它允许我们使用混合高斯密度来描述点对于一组聚类的成员关系。这是一种软分类(与硬分类相反),因为它分配属于特定类别的概率,而不是确定的选择。本质上,每个观察将属于每个类,但是具有不同的概率。
我们以著名的虹膜分类问题为例。所以我们有 150 朵鸢尾花,分成 3 类。对于它们中的每一个,我们都有萼片的长度和宽度,花瓣的长度和宽度,以及类别。
让我们使用 seaborn 包的 pair 图快速浏览一下数据:
作者图片
高斯混合模型试图将数据描述为来自高斯分布的混合物。首先,如果我们只取一个维度,比如花瓣宽度,试着去适应 3 种不同的高斯分布,我们会得到这样的结果:
作者图片
该算法发现,最有可能代表数据生成过程的混合物由以下三种正态分布组成:
setosa 花瓣宽度更加集中,平均值为 0.26,方差为 0.04。另外两个班级相对来说更分散,但是位置不同。现在让我们看看二维的结果,比如花瓣宽度和花瓣长度。
作者图片
混合物的成分如下:
请注意,GMM 是一个非常灵活的模型。可以证明,对于足够大数量的混合物,并且适当地选择所涉及的参数,可以近似任意地接近任何连续的 pdf(这需要额外的计算成本)。
形式化——MLE
那么算法如何找到描述混合物的最佳参数集呢?
我们从定义概率模型开始。观察到任何观察值的概率,即概率密度,是 K 个高斯分布的加权和(如前一节所示) :
每个点是 K 个加权高斯分布的混合,这些加权高斯分布由平均值和协方差矩阵参数化。所以总体来说,我们可以把观察到一个特定观测的概率描述为一个混合物。为了确保您完全理解,在上面使用花瓣宽度的一维示例中,在区域[0.2,0.3]中观察到花瓣宽度的概率最高。我们也很有可能在区域[1.2,1.5]和[1.8,2.2]中观察到花瓣宽度。
注意,在 3 个集群的情况下,参数集将是:
然后,我们希望找到最大化数据集可能性的参数值。我们想找到参数的最大似然估计。也就是我们要找到最大化所有数据点一起观测到的概率(即联合概率)的参数,解决以下优化问题:
注意,数据集的完全联合概率可以被矢量化(即分解为单个概率的乘积,使用π运算符),仅在假设观察值是同分布(同独立分布)的情况下。当它们存在时,事件是独立的,观察一个数据点的概率不受其他概率的影响。
我们通常最大化对数似然,而不是似然,部分原因是它将概率的乘积转化为总和(更容易操作)。这是因为自然对数是单调递增的凹函数,不改变最大值的位置(导数为零的位置将保持不变)。
现在最大似然估计可以用许多不同的方法来完成。这可以通过直接优化(找到偏导数为零的点)或数值优化(如梯度下降)来实现。GMM 的 MLE 没有使用这些方法,原因如下,我将解释。但是我把它留在文章的最后,因为我想先了解最相关的材料。GMMs 的最大似然估计是使用期望最大化算法完成的。
期望最大化
GMM 训练直觉
首先,我们将直观地描述 GMM 模型训练过程中发生的事情,因为这将真正有助于为 EM 建立必要的直觉。假设我们又回到了一维例子,但是这次没有标签。试着想象我们如何给下面的观察分配聚类标签:
作者图片
好吧,如果我们已经知道高斯分布在上面的图中的位置,对于每个观察值,我们可以计算聚类概率。让我们画这张图,这样你就能记住它了。因此,我们将为下面的点分配一种颜色:
作者图片
直观地,对于一个选定的观察值和一个选定的高斯值,属于该聚类的观察值的概率将是高斯值和所有高斯值之和之间的比率。类似于:
好吧,但是你怎么知道高斯人在上面的图中的位置呢?我们如何找到高斯参数)?好吧,假设我们已经拥有了观察的标签,就像这样:
作者图片
现在我们可以很容易地找到参数值并画出高斯图。单独考虑这些点就足够了,比如说红点,然后找到最大似然估计。对于高斯分布,可以证明以下结果:
将上述公式应用于红色点、蓝色点和黄色点,我们得到以下正态分布:
作者图片
给定正态分布参数,我们可以找到观察标签,给定观察标签,我们可以找到正态分布参数。所以看起来我们有一个先有鸡还是先有蛋的问题,对吗?
事实上,解决这个问题并不难。我们只是需要从某个地方开始。所以我们可以将高斯参数设置为随机值。然后,我们通过迭代两个连续步骤直到收敛来执行优化:
- 我们使用当前的高斯参数为观察值分配标签
- 我们更新高斯参数,使拟合更有可能
这将产生以下结果:
作者图片
如你所见,当我们到达第四步时,我们已经处于最佳状态。
EM 直觉
期望值最大化算法的执行方式完全相同。事实上,我们上面描述的 GMMs 的优化过程是 EM 算法的一个具体实现。EM 算法只是被更一般和更正式地定义(因为它可以应用于许多其他优化问题)。
因此,总的想法是,我们试图最大化一个似然性(更常见的是对数似然性),也就是说,我们试图解决以下优化问题:
这一次我们不是说可能性 P(x _ I |θ)是高斯混合。它可以是任何东西。
现在,让我们把事情形象化!假设对数似然(log P(X |θ))是以下一维分布:
作者图片
使这种算法有效的主要技巧在于特定函数的定义和使用。该函数以这样的方式定义,即在参数空间中的任何给定点,我们肯定知道它将总是具有低于或等于对数似然的值。它被称为下界。我们称之为 L(下图红色部分):
作者图片
现在,事实上,我们不使用单个下界,而是使用由参数θ的向量和变分分布 q 参数化的一族下界。因此 L(θ,q)可以位于任何地方,只要它保持为似然性的下界:
作者图片
EM 算法从分配随机参数开始。假设我们从以下下限开始:
作者图片
该算法现在将执行两个连续的步骤:
- 固定θ并调整 q,使下限尽可能接近对数似然。例如,在第一步中,我们计算 q1:
作者图片
2.固定 q 并调整θ,使下限最大化。例如,在第二步中,我们计算θ1:
作者图片
因此,总结一下这种直觉,EM 算法将寻找最大可能性(或至少是局部最大值)的困难分解为一系列更容易处理的连续步骤。为了做到这一点,它引入了一个由向量θ参数化的下界,我们希望找到它的最优解,以及一个我们也可以随意修改的变分下界 q。
詹森不等式
这个不等式在某种程度上只是凹函数定义的重新表述。回想一下,对于任何凹函数 f,任何权重α和任意两点 x 和 y:
事实上,这个定义可以推广到两点以上,只要权重总和为 1:
如果权重总和为 1,那么我们可以说它们代表一种概率分布,这给出了詹森不等式的定义:
凹函数对期望值的投影总是大于或等于凹函数的期望值。
EM 形式化
期望值最大化算法用于利用潜在变量的模型。一般来说,我们定义一个潜在变量 t 来解释一个观察值 x。通过观察,有一个潜在变量的实例。所以我们可以画出下图:
作者图片
还因为 t 解释了观察值 x,所以它定义了观察值属于其中一个聚类的概率。所以我们可以写:
现在观察的完全可能性可以写成边际可能性(即通过排斥 t):
现在回想一下,我们正在尝试解决以下优化问题:
我们还介绍了詹森不等式,它可以写成:
我们想用这个不等式来帮助我们定义下界。我们希望这个下限取决于θ和一个变分分布 q,诀窍是这样引入 q:
通过乘以和除以 q,我们没有改变任何东西。现在我们可以利用詹森不等式:
我们成功地建立了一个依赖于θ和 q 的边际对数似然的下界。因此,我们现在可以通过交替执行以下两个步骤来最大化完整的下界:
期待步骤:
我们固定θ,并尝试使下限尽可能接近可能性,也就是说,我们尝试最小化:
通过进一步的额外步骤(我将在此拯救您),我们可以证明:
因此,为了找到变分分布 q 的下一个值(k+1 ),我们需要独立地考虑每个观察值 x_i,并且对于每个类,我们计算该观察值属于类 p(t_i|x_i,θ)的概率。回想一下,根据贝叶斯法则:
现在我们可以把高斯混合模型联系起来。我们在上面的公式中发现了我们直观推导出的结果,即:
最大化步骤:
我们固定 q,并最大化下限,其定义为:
现在减法中的第二项不依赖于θ,所以我们可以写成:
现在我们通常选择一个容易优化的凹函数。在高斯混合模型的情况下,我们使用高斯分布的最大似然估计。
如果你想看完整的推导,让我们得到 m-step 中参数更新的封闭表达式,我把它们写在了一篇专门的文章中(为了不使这篇文章超载)。
如果你红了这一点,恭喜你!你现在应该很好地掌握了 GMM 和 EM。或者,如果您想了解为什么我们不使用传统方法来找到最佳参数集,请继续阅读。
那么,如果我们从零开始,在高斯混合模型的情况下,如何进行最大似然估计呢?
直接优化:第一种方法
找到最大似然估计的一种方法是将对数似然相对于参数的偏导数设置为 0,并求解方程。我们称这种方法为直接优化。
正如您所看到的,这种方法是不切实际的,因为高斯分量的总和出现在测井曲线中,使得所有参数都联系在一起。例如,如果 K=2,我们试图求解α_1 的方程,我们有:
我们用链式法则完成了第一步……但是我们不能用其他参数来表达参数α_1。所以我们被困住了!我们不能解析地解这个方程组。这意味着我们不能使用解析表达式一步找到全局最优,或者至少是局部最优。
数值优化:第二种方法
好吧,我们还能做什么?嗯,我们必须依靠数值优化方法。例如,我们可以尝试使用我们最喜欢的随机梯度下降算法。它是如何工作的?
嗯,我们随机初始化参数θ的向量,并迭代数据集的观测值。在步骤 k,我们为一个选定的观察值计算似然的梯度。然后,我们通过在梯度的相反方向上采取一个步骤(使用特定的学习速率η)来更新参数值,即:
使用链式法则,我们可以计算其余参数的偏导数(就像我们上面对α_1 所做的那样)。例如,对于μ_1,我们将得到以下结果:
现在你要告诉我,参数仍然是相互依赖的。是的,但是我们不再解方程了。首先,我们用当前位置的值和当前的参数集来计算偏导数。这是一种迭代算法。
假设我们在计算θ。我们有θ⁰=(α_0,α_1,μ_0,μ_1,σ_ 0,σ_ 1),我们有第一个选择的观察值 x_0。给定偏导数的公式,我们就有了计算θ所需的一切。并且我们继续进行连续的步骤,直到收敛(也就是说,当前的步骤变得太小)。
作者图片
例如,我通过模拟优化带有两个参数(x 和 y)的 Beale 函数 B 来生成上面的动画。我们从一个随机点开始,大约在(-3.5,-3.5),在每一步,我们将参数更新到最小值。
注意,在现实中,由 Tensorflow 或 PyTorch 等数值框架执行的损失函数的微分并不像我们上面所做的那样执行。我们不像在高中时那样使用一套硬编码的数学规则(也就是所谓的手动微分)。甚至可能没有方便的导数公式。使用自动微分完成。
好的,这个看起来不错!所以我们结束了?别这么快!我们忘记了一件重要的事情。我们必须满足两个约束来解决这个优化问题。我们正在约束条件下执行优化。
第一个是混合权重的和α必须是非负的,并且总和为 1。这允许观察数据点的概率是适当的概率密度函数;那就是:
为了克服这一限制,我们可以使用拉格朗日乘数将其纳入优化问题;即把问题重新表述为:
λ作为附加参数添加到矢量θ中。然后,我们再次运行我们的随机优化程序,我们完成了!嗯,没那么快!真正的问题在于第二个约束。回想一下,多正态概率密度函数写为:
但是矩阵σ不能任意!它是一个协方差矩阵,因此应考虑某些属性:
- 对角项是方差,因此必须是正的
- 矩阵必须是对称的
- 对于两个不同的预测值,其协方差的平方必须小于其方差的乘积
- 矩阵必须是可逆的
- 行列式必须是正数
当矩阵是半正定的时,这些条件被满足。
这是一个非常难以满足的约束,并且仍然是一个活跃的研究领域。事实上,凸优化有一个专门的子领域,叫做半定规划。它真正成为一门学科是从 90 年代开始的,用的方法有内点或增广拉格朗日。但是混合模型在此之前已经出现,并导致了期望最大化算法。
如果您不理解以上所有内容(尤其是关于拉格朗日公式的部分),请不要担心。你需要明白的是,传统的求参数最大似然估计的方法不适用于高斯混合模型。
结论
在这篇文章中,我们已经对高斯混合模型和期望最大化进行了全面的回顾,从视觉上(给出一些见解)和更正式地给出了完整的数学推导。
这篇文章在数学上相当沉重,但我认为如果你设法花时间,它真的是值得的。对这种模型有更深入的了解会让你理解遍布机器学习和统计领域的许多不同技术。而下一次你试图去理解另一款同口径的车型,就简单多了。我向你保证!
在此期间,照顾好你自己和你所爱的人!
高斯混合模型(GMM)
理解 GMM:思想、数学、EM 算法和 python 实现
B rief :高斯混合模型是一种流行的无监督学习算法。GMM 方法类似于 K-Means 聚类算法,但是更健壮,因此由于其复杂性而更有用。在这篇文章中,我将给出一个鸟瞰图,数学( ba ye s ic maths,nothing ab normal ),python 从头实现以及使用 sklearn 库。
介绍
查看我关于 K-means 聚类的博客是一个好主意(3 分钟阅读),以获得聚类、无监督学习和 K-Means 技术的基本概念。在聚类中,给定一个未标记的数据集 X ,我们希望将样本分组到 K 个聚类中。在 GMMs 中,假设的不同子群体( K 共)遵循一个正态分布,虽然我们只有总体 X( 因此得名高斯混合模型)的概率分布信息。我们的任务是能够找到 K 高斯的的参数以便将数据*X进行探索性的数据分析或者对新的数据做出预测。*****
对 K-均值聚类的改进
K-means 使用欧几里德距离函数来发现数据中的聚类。只要数据相对于质心遵循圆形分布,这种方法就能很好地工作。但是如果数据是非线性的,椭圆形的呢?还是数据有非零协方差?如果聚类有不同的均值和协方差呢?
这就是高斯混合模型拯救世界的地方!
GMM 假设产生数据的是高斯分布的混合物。它使用数据点到聚类的软分配(即,概率性的,因此更好),与数据点到聚类的硬分配的 K-means 方法形成对比,假设数据围绕质心呈圆形分布。
简而言之,GMM 捕获工作得更好,因为 (A) 它通过使用软分配来捕获属于不同聚类的数据点的不确定性,并且 (B) 它对圆形聚类没有偏见。因此,即使对于非线性数据分布,它也能很好地工作。
戈-梅-莫三氏:男性假两性畸形综合征
GMM 的目标函数是最大化数据 X、 p(X) 的似然值或对数似然值 L (因为 log 是单调递增函数)。通过假设混合了 K 高斯分布来生成数据,我们可以将【p(X)写成边缘化概率,对所有数据点的所有 K 聚类求和。****
**********
似然值
对数似然值
利用上面对数函数内的求和,我们无法获得解析解。虽然看起来很龌龊,但这个问题有一个优雅的解决方案: 期望最大化(em)算法 。
数学
EM 算法 是一种迭代算法,用于寻找模型的最大似然估计(MLE) ,其中参数无法直接找到,就像我们这里的情况。它包括两个步骤:e 预期步骤和最大化步骤。
- 期望步骤 :计算隶属值 r _ ic。这是数据点 x_i 属于聚类 c 的概率。
2. 最大化步骤 :计算一个新的参数 mc ,该参数决定了属于不同聚类的点的分数。通过计算每个聚类 c 的 MLE 的来更新参数μ、π、σ
重复 E-M 步骤,直到对数似然值 L 收敛。
密码
让我们从头开始用 python 写一个 GMM 的基本实现。
生成一维数据。
初始化 GMM 的参数:μ,π,σ。
运行 EM 算法的第一次迭代。
EM 算法的单次迭代
将这段代码放在 for 循环中,并将其放入一个 class 对象中。现在我们正在谈话!
GMM-1D 级
**********
我们已经有了一个模型,可以运行一维数据。同样的原理也适用于更高维度(≥ 2D)。唯一不同的是,我们将在这种情况下使用多元高斯分布。让我们为 2D 模型编写代码。
让我们生成一些数据并编写我们的模型。
2D 斑点
让我们对这个模型做一些预测。
**********
使用 sklearn ,同样的任务可以在几行代码内完成。很圆滑,是吧?
因此,GMM 将样本分类为属于第二类。有用!
结论
实现高斯混合模型并不困难。一旦你对数学有了清晰的认识,就可以找到模型的最大似然估计,无论是 1D 还是更高维的数据。这种方法在执行聚类任务时是健壮且有用的。既然您已经熟悉了 GMMs 的 python 实现,那么您就可以使用数据集执行一些很酷的事情了。假设给你一个病人的数据集,包含两个参数:红细胞体积和红细胞血红蛋白浓度,没有病人和健康病人的标签。插入上面的模型来聚集数据将会给你两个不同的(几乎是)质量,你可以使用它们来进行进一步的分析和预测。
来源
- https://www.youtube.com/watch?v=qMTuMa86NzU
- https://www . python-course . eu/expectation _ maximization _ and _ Gaussian _ mixture _ models . PHP
高斯混合模型:从零开始实现
从机器学习和人工智能领域的兴起,概率论是一个强大的工具,它允许我们处理许多应用中的不确定性,从分类到预测任务。今天,我想和你们讨论更多关于概率和高斯分布在聚类问题中的应用,以及 GMM 模型的实现。所以让我们开始吧
什么是 GMM?
GMM(或高斯混合模型)是一种算法,它使用对数据集密度的估计来将数据集分成初步定义数量的聚类。为了更好地理解,我将同时解释这个理论,并展示实现它的代码。
对于这个实现,我将使用 EM(期望最大化)算法。
理论和代码是最好的结合。
首先让我们导入所有需要的库:
import numpy as np
import pandas as pd
我强烈建议在自己实现模型时遵循 sci-kit learn library 的标准。这就是为什么我们将 GMM 作为一个类来实现。让我们也来看看 __init_function。
class GMM:
def __init__(self, n_components, max_iter = 100, comp_names=None):
self.n_componets = n_components
self.max_iter = max_iter
if comp_names == None:
self.comp_names = [f"comp{index}" for index in range(self.n_componets)]
else:
self.comp_names = comp_names
# pi list contains the fraction of the dataset for every cluster
self.pi = [1/self.n_componets for comp in range(self.n_componets)]
简而言之, n_components 是我们想要拆分数据的集群数量。 max_iter 表示算法进行的迭代次数,comp_names 是具有 n_components 个元素的字符串列表,这些元素被解释为聚类的名称。
拟合函数。
因此,在我们使用 EM 算法之前,我们必须分割我们的数据集。之后,我们必须启动 2 个列表。一个包含每个子集的平均向量(向量的每个元素都是列的平均值)的列表。第二个列表包含每个子集的协方差矩阵。
def fit(self, X):
# Spliting the data in n_componets sub-sets
new_X = np.array_split(X, self.n_componets)
# Initial computation of the mean-vector and covarience matrix
self.mean_vector = [np.mean(x, axis=0) for x in new_X]
self.covariance_matrixes = [np.cov(x.T) for x in new_X]
# Deleting the new_X matrix because we will not need it anymore
del new_X
现在我们可以讨论 EM 算法了。
EM 算法。
顾名思义,EM 算法分为两步——E 和 m。
电子步骤:
在估计步骤中,我们计算 r 矩阵。它是使用下面的公式计算的。
计算 r(责任)矩阵的公式(来源——mathematis for Machine Learning Book)
r 矩阵也被称为‘职责’,可以用以下方式解释。行是来自数据集的样本,而列代表每个聚类,该矩阵的元素解释如下:rnk 是样本 n 成为聚类 k 的一部分的概率。当算法收敛时,我们将使用该矩阵来预测点聚类。
同样,我们计算 N 个列表,其中每个元素基本上是 r 矩阵中对应列的和。下面的代码就是这样做的。
for iteration in range(self.max_iter):
''' ---------------- E - STEP ------------------ '''
# Initiating the r matrix, evrey row contains the probabilities
# for every cluster for this row
self.r = np.zeros((len(X), self.n_componets))
# Calculating the r matrix
for n in range(len(X)):
for k in range(self.n_componets):
self.r[n][k] = self.pi[k] * self.multivariate_normal(X[n], self.mean_vector[k], self.covariance_matrixes[k])
self.r[n][k] /= sum([self.pi[j]*self.multivariate_normal(X[n], self.mean_vector[j], self.covariance_matrixes[j]) for j in range(self.n_componets)])
# Calculating the N
N = np.sum(self.r, axis=0)
指出多元正态只是应用于向量的正态分布公式,它用于计算向量在正态分布中的概率。
下面的代码实现了它,采用行向量、均值向量和协方差矩阵。
def multivariate_normal(self, X, mean_vector, covariance_matrix):
return (2*np.pi)**(-len(X)/2)*np.linalg.det(covariance_matrix)**(-1/2)*np.exp(-np.dot(np.dot((X-mean_vector).T, np.linalg.inv(covariance_matrix)), (X-mean_vector))/2)
看起来有点乱,但是你可以在那里找到完整的代码。
m 步:
在最大化步骤中,我们将逐步设置均值向量和协方差矩阵的值,用它们来描述聚类。为此,我们将使用以下公式。
M-step 的公式(来源-mathematis for Machine Learning Book)
在代码中,我希望:
''' --------------- M - STEP --------------- '''
# Initializing the mean vector as a zero vector
self.mean_vector = np.zeros((self.n_componets, len(X[0])))
# Updating the mean vector
for k in range(self.n_componets):
for n in range(len(X)):
self.mean_vector[k] += self.r[n][k] * X[n]
self.mean_vector = [1/N[k]*self.mean_vector[k] for k in range(self.n_componets)]
# Initiating the list of the covariance matrixes
self.covariance_matrixes = [np.zeros((len(X[0]), len(X[0]))) for k in range(self.n_componets)]
# Updating the covariance matrices
for k in range(self.n_componets):
self.covariance_matrixes[k] = np.cov(X.T, aweights=(self.r[:, k]), ddof=0)
self.covariance_matrixes = [1/N[k]*self.covariance_matrixes[k] for k in range(self.n_componets)]
# Updating the pi list
self.pi = [N[k]/len(X) for k in range(self.n_componets)]
我们已经完成了拟合函数。创造性地应用 EM 算法将使 GMM 最终收敛。
预测函数。
预测函数实际上非常简单,我们只需使用多元正态函数,该函数使用每个聚类的最佳均值向量和协方差矩阵,以找出哪个给出最大值。
def predict(self, X):
probas = []
for n in range(len(X)):
probas.append([self.multivariate_normal(X[n], self.mean_vector[k], self.covariance_matrixes[k])
for k in range(self.n_componets)])
cluster = []
for proba in probas:
cluster.append(self.comp_names[proba.index(max(proba))])
return cluster
结果。
为了测试该模型,我选择将其与 sci-kit 库中实现的 GMM 进行比较。我使用 sci-kit 学习数据集生成函数生成了 2 个数据集——使用不同的设置生成 _ blobs。这就是结果。
我们的模型与 sci-kit learn 模型的比较。
我们的模型和 sci-kit 模型的聚类几乎相同。很好的结果。完整的代码你可以在那里找到。
来源——tenor.com
这篇文章是西格蒙德和❤一起写的。
有用的链接:
- https://github . com/science kot/mysklearn/tree/master/Gaussian % 20 mixture % 20 models
- https://en . Wikipedia . org/wiki/Expectation % E2 % 80% 93 最大化 _ 算法
- 用于机器学习的数学由 Cheng Soon Ong、Marc Peter Deisenroth 和 A. Aldo Faisal 编写
高斯混合模型与 K-均值。选哪个?
两种流行聚类算法的性能比较
G.混合物对 k .平均(1957)。帆布油画。顺便说一句,没有明显的赢家。伯明翰博物馆信托基金会在 Unsplash 上拍摄的照片
K -Means 和高斯混合(GMs)都是聚类模型。然而,许多数据科学家倾向于选择更流行 K-Means 算法。即使 GMs 可以证明在某些聚类问题上是优越的。
在本文中,我们将看到这两种模型在速度和健壮性方面提供了不同的性能。我们还将看到,使用 K-Means 作为 GMs 的初始化器是可能的,这有助于提高聚类模型的性能。
它们是如何工作的
首先,让我们回顾一下这些算法的理论部分。这将有助于我们在文章的后面理解他们的行为。
k 均值
K-Means 是一种流行的非概率聚类算法。该算法的目标是最小化失真度量J。我们通过以下迭代程序实现这一点【1】:
- 选择集群的数量 K
- 初始化定义每个聚类中心点的向量 μ_k
- 将每个数据点 x 分配到最近的聚类中心
- 为和每个集群重新计算中心点 μ_k
- 重复 3–4,直到中心点停止移动
下面的 GIF 很好地说明了这个迭代过程:
K=3 时的 K 均值。重新计算每个聚类的数据点标签和平均值,直到收敛。【来源】
K-Means 算法将收敛,但它可能不是全局最小值。为了避免收敛到局部最小值的情况,K-Means 应该用不同的参数重新运行几次。
K-Means 执行硬分配,这意味着每个数据点必须属于某个类,并且没有分配给每个数据点的概率。
K 均值的计算成本是 O(KN),其中 K 是聚类的数量,N 是数据点的数量。
高斯混合
高斯混合基于 K 个独立的高斯分布,用于建模 K 个独立的聚类。提醒一下,多元高斯分布如下所示:
多元高斯分布。 μ 是 D 维均值向量, ∑ 是 DxD 协方差矩阵。修改自[1]
高斯混合的推导是相当复杂的,所以为了更深入的数学解释,我建议看看这篇文章。
关于 GMs 要知道的最重要的事情是,这个模型的收敛是基于 EM(期望最大化)算法的。它有点类似于 K-Means,可以总结如下[1]:
- 初始化 μ,∑,和混合系数 π 并评估对数似然 L 的初始值
- 使用当前参数评估责任函数
- 使用新获得的职责获得新的 μ、∑、和 π
- 再次计算对数似然 L。重复步骤 2–3,直到收敛。
高斯混合也将收敛到局部最小值。
通过下面的 GIF,我们可以很容易地看到高斯混合的收敛情况:
高斯混合的收敛性。【来源】
K-均值和高斯混合的第一个明显区别是决策边界的形状。GMs 更加灵活,使用协方差矩阵 ∑ 我们可以使边界为椭圆形,与使用 K-means 的圆形边界相反。
还有一点就是 GMs 是一个概率算法。通过将概率分配给数据点,我们可以表达我们对给定数据点属于特定聚类的信念有多强。
使用每个数据点属于某个簇的概率的簇的软分配。图片作者[1]
如果我们比较这两种算法,高斯混合似乎更稳健。然而,GMs 通常比 K-Means 慢,因为它需要 EM 算法的更多迭代来达到收敛。它们也可以快速收敛到局部最小值,这不是一个非常理想的解决方案。
在本文的剩余部分,我们将使用 Scikit-learn 库来研究这些模型在实践中的表现。
比较性能
我们将从为此任务创建一个合成数据集开始。为了使它更具挑战性,我们将创建 2 个重叠的高斯分布,并在边上添加一个均匀分布。
生成的数据集如下所示:
聚类形状
让我们动手用 K-Means 和高斯混合进行初始聚类。我们将使用从 Scikit-Learn 导入的模型。此外,我们将以相同的方式为两种模型设置参数。两个模型的最大迭代次数、聚类数和收敛容差设置相同。
从聚类数据的第一眼看,我们可以看出它们的表现并不太好。虽然 K-Means 以与真实聚类相似的方式对数据进行聚类,但是 GM 聚类看起来相当不可靠。
k 均值+高斯混合= ❤️
GMs 的问题是它们很快收敛到局部最小值,这对这个数据集来说不是很理想。为了避免这个问题, GMs 通常用 K-Means 初始化。这通常工作得很好,它改善了用 K-Means 生成的聚类。我们可以通过改变 GaussianMixture 类中的一个参数来创建带有 K-Means 初始化器的 GM:
我们还可以利用 GMs 的概率特性。通过添加阈值,在本例中为 0.33,我们能够标记模型不确定的带标签的数据点。这里变得非常有趣,因为对于普通 GM 来说,大多数数据点的概率都很低,如下图所示。
此外,具有 K 均值初始化器的 GMs 似乎表现最好,并且聚类几乎与原始数据相同。
计算时间
现在让我们看看这些算法的计算时间。结果相当令人惊讶。计算时间是用不同数量的聚类和上述所有 3 个模型来测量的。
结果是:
很奇怪,普通的 K-Means 比带有 K-Means 初始化器的 GM 要慢。在幕后,Scikit-Learn 似乎应用了 K-Means 的优化版本,它需要更少的迭代来收敛。
此外,香草转基因需要很短的时间。这是因为它很快就找到了一个局部最小值,而这个最小值甚至还没有接近全局最小值。
那么我应该选择哪种算法呢?
如果你寻找鲁棒性,带有 K-Means 初始化器的 GM 似乎是最好的选择。如果你用不同的参数进行实验,K-Means 理论上应该更快,但是从上面的计算图中我们可以看出,带有 K-Means 初始化器的 GM 是最快的。GM 本身并没有太大的用处,因为它对于这个数据集收敛得太快而不是最优解。
感谢您的阅读,希望您喜欢这篇文章!
关于我
我是阿姆斯特丹大学的人工智能硕士学生。在我的业余时间,你可以发现我摆弄数据或者调试我的深度学习模型(我发誓这很有效!).我也喜欢徒步旅行:)
如果你想了解我的最新文章和其他有用的内容,以下是我的其他社交媒体资料:
参考
GPflow 中分子的高斯过程回归
这篇文章演示了如何通过创建自定义的 Tanimoto 内核来操作摩根指纹,使用 GPflow 库训练高斯过程(GP)来预测分子属性。请访问我的 GitHub 回购Jupyter 笔记本!
在这个例子中,我们将尝试预测实验确定的分子光开关的电子跃迁波长,这类分子在光照射下会发生 E 和 Z 异构体之间的可逆转变。
图片来自光电开关数据集论文
要事第一,依赖!
conda create -n photoswitch python=3.7
conda install -c conda-forge rdkit
conda install scikit-learn pandas
pip install git+https://github.com/GPflow/GPflow.git@develop#egg=gpflow
我们将从导入我们将要使用的所有机器学习和化学库开始。
对于我们的分子表示,我们将使用广泛使用的摩根指纹。在这种表示下,分子被表示为位向量。因此,标准高斯过程核,如平方指数核或 Matern 核,在设计时会考虑到连续空间,因此不太理想。然而,我们可以定义一个自定义的“Tanimoto”或“Jaccard”内核,作为我们的位向量之间的相似性度量。
在代码方面,在 GPflow 中定义一个定制的 Tanimoto 内核相对简单
下面的定义与上面给出的等式略有不同,因为我们计算的是矩阵输入的 Tanimoto 相似性。
接下来,我们读入光开关分子,用微笑串表示。我们预测的性质是每个分子的 E 异构体的电子跃迁波长。一般来说,对于任何数据集,GP 只需要一个分子微笑列表和一个属性值数组。
我们使用键半径 3 将我们的分子转换成 2048 位摩根指纹。我们用 X 表示分子,用 y 表示属性值。我想这可能会更容易跟踪输入和输出是什么!
我们定义一个效用函数来标准化我们的输出值。在安装全科医生之前,这是典型的良好做法。请注意,虽然代码也支持输入的标准化,但我们稍后将选择不使用它。原因是标准化一个位向量,而不是一个实值向量,似乎没有太大的意义(至少对我来说!).
我们定义了 80/20 的训练/测试分割。
接下来我们安装 GP。我们可以检查学习到的核超参数,尽管在位向量的情况下这些参数可能不那么有用!
现在我们可以输出训练并测试均方根误差(RMSE)、平均绝对误差(MAE)和 R 值。
Train RMSE (Standardised): 0.036
Train RMSE: 2.422 nm
Test R^2: 0.916
Test RMSE: 17.997 nm
Test MAE: 11.333 nm
还不错!在我们的论文中,我们将 GP-Tanimoto 模型的预测与一群人类光开关化学家的预测进行了比较,在组成测试集的所有 5 种分子的情况下,这些预测实现了更低的测试误差:
MAEs 是在所有人类参与者的每个分子的基础上计算的。图片来自光电开关数据集论文
GPs 的一个定义特征是它们能够产生校准的不确定性估计。在分子发现活动中,这些不确定性估计有助于捕捉模型的可信度;如果测试分子与训练中看到的分子非常不同,模型可以告诉你这是猜测!
用程序术语来说,可以通过检查变量 y_var 来访问测试集的不确定性。人们可以通过例如以下方式获得预测的排序置信度列表
其输出一个列表,在该列表中分子(由它们的测试集索引表示)通过它们的预测置信度被排序。下面的脚本提供了进一步的细节
从图形上看,可以生成置信-误差曲线,以检查 GP 获得的不确定性实际上与测试集误差相关。
图片来自光开关数据集论文。
在 x 轴上方的图中,置信百分点可以简单地通过根据该测试输入位置处的预测方差对测试集的每个模型预测进行排序来获得。例如,位于第 80 个置信百分位数的分子将是具有最低模型不确定性的 20%测试组分子。然后,我们测量 200 个随机训练/测试分割的每个置信百分点的预测误差,以查看模型的置信与预测误差是否相关。我们观察到 GP-Tanimoto 模型的不确定性估计与预测误差正相关。
从该图中得到的实际收获是一个概念证明,即模型不确定性可以被结合到选择在实验室中物理合成哪些光开关分子的决策过程中;如果一个“未合成”分子的预测波长值是所期望的,并且这个预测的可信度很高,那么你可能想要实现它!
GPs 的另一个好处是:通过检查学习到的内核,有可能解释不同分子表示捕获的化学。更多详情请见我们的论文!
图片来自光电开关数据集论文。
图归功于艺术上不知疲倦的雷蒙德·塔瓦尼。请在 Twitter 上寻求评论/反馈/讨论!如果你觉得这篇文章中的数据或实现有用,请考虑引用以下文章:
GPflow 中的 Photoswitch 数据集和 Tanimoto GP 实现:
@article{Thawani2020,
author = "Aditya Thawani and Ryan-Rhys Griffiths and Arian Jamasb and Anthony Bourached and Penelope Jones and William McCorkindale and Alexander Aldrick and Alpha Lee",
title = "{The Photoswitch Dataset: A Molecular Machine Learning Benchmark for the Advancement of Synthetic Chemistry}",
year = "2020",
month = "7",
url = "https://chemrxiv.org/articles/preprint/The_Photoswitch_Dataset_A_Molecular_Machine_Learning_Benchmark_for_the_Advancement_of_Synthetic_Chemistry/12609899",
doi = "10.26434/chemrxiv.12609899.v1"
}
GPflow:
@article{GPflow2017,
author = {Matthews, Alexander G. de G. and {van der Wilk}, Mark and Nickson, Tom and
Fujii, Keisuke. and {Boukouvalas}, Alexis and {Le{\'o}n-Villagr{\'a}}, Pablo and
Ghahramani, Zoubin and Hensman, James},
title = "{{GP}flow: A {G}aussian process library using {T}ensor{F}low}",
journal = {Journal of Machine Learning Research},
year = {2017},
month = {apr},
volume = {18},
number = {40},
pages = {1-6},
url = {http://jmlr.org/papers/v18/16-537.html}
}
谷本内核的创始人
@article{ralaivola2005graph,
title={Graph kernels for chemical informatics},
author={Ralaivola, Liva and Swamidass, Sanjay J and Saigo, Hiroto and Baldi, Pierre},
journal={Neural networks},
volume={18},
number={8},
pages={1093--1110},
year={2005},
publisher={Elsevier}
}
此处的所有图像受 CC BY-NC 许可的版权保护。
高斯过程:制定你自己的人生目标
我们可以使用贝叶斯优化来调整我们的模型,但你知道它可以做得更多吗?
我在之前谈到过如何使用高斯过程来拟合模型超参数的替代函数,然后使用贝叶斯统计在少量迭代中找到最佳参数。这可以简单地用 sci-optimize 包中的几行代码( BayesSearchCV )来完成。如果你想要更多的数学链接,这是一个很好的选择。
但是,您可以更深入地挖掘,并相对容易地构建自己的目标函数。
我们为什么要这么做?
很高兴你问了。简单地说,如果你能想到一个优化问题和评估它的方法,你可以使用高斯过程(GP)来解决它。
唯一的警告是,它必须是昂贵的评估(无论是检索时间或成本)。如果你有一个只需要几秒钟就可以评估的东西,那么这个方法虽然很酷,但由于它增加了大量的计算开销,所以不是最有效的选择。这是因为每当你得到一个新点时,它必须重新计算并拟合一个代理分布到它已经评估过的所有点,因此它的性能随着迭代次数的增加而降低(这是一个 O(N)问题)。
因为我相信你会问,如果从随机抽样开始评估是不是既便宜又快捷。它通常会给出有竞争力的结果,因为它可以在 GP 只能做几个的时间内完成大量的评估(开销成为一个瓶颈)。random 在这里表现如此之好的原因纯粹是统计学上的,因为你可以执行的计算次数越多,你的猜测中至少有一个没有落在可接受区域内的概率就越低。
照片由юліявівчарик在 Unsplash 上拍摄
我们会在哪里使用它?
使用范围相当广泛,例如:
- 调整工厂生产线设置以获得最佳质量输出
- 从大量组合中选择最佳药物或化学成分
- 调整路段上的交通灯以辅助交通流量和行人行走
- 优化您的上班旅行时间
- 平衡您的散热器,使它们辐射相同的温度
最后一个听起来有点疯狂,但它是一个你可以使用它的应用程序,如果我发现我的房子里有一个寒冷的房间,我可能会在这个冬天使用它。
一般来说,在运行优化时,如果行为没有太大的变化,这种方法非常有效。这不同于噪声数据,因为你可以添加元素来处理它(比如一个白核
好吧,那我们怎么做?代码时间!
当我说高斯过程时,我的意思是我们将高斯函数拟合到我们的数据(替代模型),然后使用贝叶斯统计来预测基于该拟合的最可能的下一个值。
我将从一个问题开始,从一个挤压材料切割 4x4cm 厘米的碎片。我能做的就是移动两个刀具(一个上下,一个左右)。你会(正确地)猜测,如果我把每个切刀放在 4 厘米的地方,我就会得到我想要的。
如果我现在让一台计算机做这件事,但是我没有告诉它我想要什么或者怎么做(除了它能移动刀具之外),那么它会怎么做?我可以使用随机猜测,但我希望它在得到正确答案之前,通过切割最少的部分来找到正确的答案。进入高斯过程!
我不能只告诉它“否”或“是”,因为它需要知道自己做得有多好,好了多少。即每次它做出猜测时,它都需要学习一些东西。因此,对于这个例子,我将在一个尺度上评分,如果它完全正确,就给它 100%,否则我将在这一点周围的半径内评分(我如何做,我将在后面讨论)。
如果你想象一下我会如何给刀具位置评分,它会是这样的:
刀具位置如何分级(最佳值为 4x4cm)
正如你所看到的,如果两个切刀都在 4 厘米处,我会给满分,轮廓被标记为我如何分级,因为它越来越远。
因此,要设置代码,我们需要首先定义它可以搜索的参数空间:
from skopt.space import Real# Define the parameter space types and ranges
space = [Real(0, 10, prior='uniform', name='Cutter #1'),
Real(0, 10, prior='uniform', name='Cutter #2')]
我们在这里所做的是设置一个“1 号刀具”和“2 号刀具”的参数空间,并表示我们想要一个介于 0 和 10(这是刀具可以移动的厘米数)之间的实值(所以是浮点型)。我们设置的先验是“统一的”,因为我们希望它认为所有的值都具有同等的可能性。最后,我们将其命名为,这不是必需的,但是当稍后在目标函数中处理这些值时,这样会减少混淆。
注意:您也可以指定整数和类别,所以您不仅仅局限于浮点数。
现在我们有了这个,我们需要创建我们的目标函数。该函数的作用是从 GP 中获取每组建议的参数,然后对其进行评估/评分。GP 将使用这些分数来尝试最小化这个目标函数。
from skopt.utils import use_named_args
from scipy.stats import norm# Value is just used to standardise "value" to be from 0 to 100
max_value = norm(0).pdf(0)**2[@use_named_args](http://twitter.com/use_named_args)(space)
def objective(**params):
"""
Returns our quality judgement for each set of parameters given
""" # Calculate quality based on two gaussians
cutter_1 = norm(4,1).pdf(params['Cutter #1'])
cutter_2 = norm(4,1).pdf(params['Cutter #2'])
value = cutter_1*cutter_2
# Standardise the value to be from 0 to 100
value = value/max_value*100
# Because it is minimising we have to make value negative
value = -1*value
return value
这是怎么回事?嗯,内容有些随意(正如您将读到的),但这里最重要的部分是“@use_named_args(space)”。这意味着每当函数接收到参数(一组要评估的猜测值)时,它们将被存储在变量“params”中,我可以使用前面为“space”定义的名称来提取它们。
对于本例,我之前将这两个参数命名为“1 号刀具”和“2 号刀具”,我可以通过以下语句在我的目标函数中访问它们的值:
# Access value for 'Cutter #1'
params['Cutter #1']
# Access value for 'Cutter #1'
params['Cutter #2']
这对复杂空间特别有用,有助于跟踪变量。
这个函数的其余部分就是我如何计算要返回的“分数”。有趣的是,我决定用两个高斯值(平均值为 4,标准差为 1)对结果进行分级。通过给每个轴分配一个高斯值,然后将它们相乘,我得到了一个围绕我想要的值的漂亮的“圆锥”形状。然后我可以调整这些,使我的判断更严厉(非常尖锐)或不严厉。
我计算一个“max_value”在它之前是这两个高斯的峰值。这是因为我可以标准化它,给出 0 到 100 的质量分数。
我们必须将该值反转为负值,因为该过程将尝试最小化该值。如果我不这样做,优化将与我想要的相反(选择可能的最差切割)。
注意:我同样可以从正确的位置得到猜测的直线距离并返回。这也是可行的。
让一切运转起来
我们现在可以定义高斯过程并让它运行:
from skopt import gp_minimize# GP process settings and return results stored in "res"
res = gp_minimize(objective, space, n_initial_points=2, n_calls=20,
verbose=True)
这里的“目标”和“空间”就是我们刚刚定义的函数和参数空间。“n_initial_points”是在开始使用数据点进行拟合并做出明智选择之前,您希望它首先生成的随机点数。在这里,我将它设置为 2(这是最小值),以便您可以看到它更快地做出明智的选择,但在现实生活中,您通常希望这个值更高(10 左右),以便您可以很好地覆盖参数空间。这减少了你错过另一个更好的解决方案的机会,并且你不仅仅是陷入了你找到的第一个极小值。
“n_calls”是你想做的迭代次数。此外,值得注意的是“n_initial_points”也从这里获得其分配(因此只有 18 个点将从高斯分布中拟合和估计)。
“verbose”是我设置的最后一个参数,通过将其设置为“True ”,我可以获得关于优化进展情况的信息。
优化的结果存储在“res”中,我们可以在以后查询它。
结果
因为我们将函数设置为 verbose,所以我们得到以下输出:
前五次迭代的输出
如果你记得前两个步骤是随机的,但它随后使用这两个点作出明智的猜测。当我们查看前五次迭代时,我们可以看到它发现的当前最小值在变化,并且变得越来越负(记住我们将分数设为负,这样它会适当地最小化)。
如果我们绘制这五次迭代,它看起来像这样:
根据得分值按顺序显示前五次迭代
在上面的例子中,我们可以看到前两次猜测都是错误的,但是一旦它停止了随机猜测,它就开始返回并在开始的地方附近寻找。在第五步,它已经搜索到更远的地方,但稍微接近 4 厘米和 4 厘米的最佳位置。
我还画出了它在 20 次迭代中找到的最佳位置,我们看到它确实找到了这个最佳位置。想象我们得到的所有步骤:
最佳参数的 20 次迭代的可视化
这里我们可以看到,该算法相对较快地(20 步)找到了最佳位置。这意味着,在我得到一个近乎完美的切割之前,我只需要接受 19 次糟糕的切割。
因为我们将结果存储在“res”中,所以我们可以通过访问各种键来查询它:
# Print out the best position found
res.x
>>> [3.973487161623168, 3.9749779608581903]
# Print out the value for the best position found
res.fun
>>> -99.93357042213998
非常接近!你必须记住,它根本不知道我们想要 4x4cm 的尺寸。它只知道对自己的努力有多高兴或多不高兴。
我们还可以通过访问“x_iters”和“func_vals”来访问返回的所有步骤和质量度量(以及我是如何制作图表的):
# Find out how many iterations were done
len(res.x_iters)
>>> 20
正好 20,正如我们将“n_calls”设置为。这个值 20 是任意的,是我为了减少图表的混乱而设置的。如果我将 n_calls 设置得更高,这意味着一旦它达到一个特别好的值,该过程可能会检查参数空间的其他区域,如下所示:
允许超过 20 次迭代时的搜索路径
从逻辑上讲,它这样做是为了验证没有更好的方法可用。如果是,它将在那里搜索,否则,它可能会返回到它找到的最佳值附近,并在它附近搜索,看看它是否可以进一步改善这些值。
注意:如果您想要可重复的结果,您可以在“gp _ minimise”中设置一个“random_state”值。
你已经达到目的了!
因此,使用这个简单的例子,我们已经能够建立一个目标函数,并找到我的问题的最佳解决方案。
虽然这看起来工作量很大,但是当你遇到一个不像这样容易计算的问题时,知道如何做是很有用的。
例如,对于我的散热器问题,我可以去调整所有的散热器阀门,等待一个小时,然后读取每个温度。我可能放进去的值是标准差。然后,该函数将尝试将该值降至零,这将表示每个散热器的温度相同。我可能还想减去中间温度,以鼓励算法尽可能将散热器开得更高(而不是把它们都关了,这样才能达到相同的温度!).因此,可以做很多调整。
我希望这是有用的,并希望很快再次见到你!
在 Unsplash 上由 Veronika Nedelcu 拍摄的照片
高斯过程:更智能地调整您的 ML 模型
实践教程
通常情况下,模型的最佳性能取决于正确的参数,但测试每种组合的计算成本太高。使用高斯过程可以有所帮助。
照片由奥拉夫·阿伦斯·罗特内在 Unsplash 拍摄
我想我们都经历过。我们有一个基本的机器学习模型,但它的性能还不太好。幸运的是,许多机器学习算法都有许多旋钮和开关,你可以用来调整它,使其性能更好(例如,你在随机森林中构建的树的深度,或者你的梯度增强算法的学习速率)。
有多个现有函数可帮助您执行此调优,并且 SciKit learn 有几个(仅高层次解释,但给出链接):
- GridSearchCV —在给定的参数范围内尝试每个组合
- 参数网格 —从每个参数允许值的离散列表中构建参数组合
- 参数采样器 —从分配给每个参数的分布中采样参数
- RandomizedSearchCV —类似于 GridSearch,但只随机抽样这么多用户集组合
如果你和我一样,你可能已经用你自己的经验将参数空间限制在你认为可能给出好结果的区域,然后如果组合很小,使用 GridSearchCV,否则可能使用 RandomizedSearchCV 来获得你在剩下的时间里可以找到的最佳组合。
通常,我发现我做的很多项目都是全新的蓝天,所以数据集是不完整的,支离破碎的,非常脏。这意味着数据科学的格言“80%的数据清理和 20%的其他事情”在我的案例中经常是正确的。当训练模型的计算成本很高时,搜索 10,000 个(甚至 100 个)组合的大参数对于满足交付期限是不实际的。
蒂姆·高在 Unsplash 上拍摄的照片
我们能做什么?
我很高兴你问了。你可能已经看到或者没有看到关于使用 Scikit-Optimize 的信息,但是如果你还没有或者不确定如何使用它来调整你的模型,那么请继续阅读。
那么,是什么呢?
它的核心是一组函数,使您能够对评估代价高昂的问题进行建模。这种开销可能是因为获得参数组合的结果需要很长时间,也可能是因为彻底解决问题的计算成本很高。
在我的工作生涯中,我用它来解决这两个问题。
对于“评估每个组合的等待时间过长”的问题,它被用来收集数据,以建立生产线的机器学习模型。每次算法建议尝试一组新的参数时,都需要花费很长时间来返回结果(生产线需要达到稳定状态,然后需要有人取走样品,将其带到测试实验室并运行测试来分析产品输出,进一步增加了等待时间),由于测量的成本,只能获取这么多的数据点。由于这个限制,我们使用这个解决方案来获取最有价值的数据点。
我将谈到的第二种用法(无法测试每种可能的组合)是,尝试调整模型的参数组合在计算上太过昂贵,我会老死。
SciKit Optimise 为您提供了许多工具,但我将集中讨论我使用最多的一个工具,即 gp_minimize。它的主要描述是“使用高斯过程的贝叶斯优化”,这是一项了不起的任务,只需要向已经知道它是什么的人解释它是什么。
在高层次上,如果您知道从数据中获得的基本分布,您就可以预测某些事件发生的概率。如果你有一个均匀分布,那么你知道在指定范围内的任何值都是同样可能的,但在生活中,情况并不总是这样。如果我向靶心投掷飞镖,你会认为我的大部分命中目标可能离靶心更近而不是更远。但也许我不是最好的射手,我总是倾向于击中某个特定的点而不是靶心。那么我们如何辨别呢?
如果我拍了 100 张照片,我们只是测量了从靶心开始的水平距离,并制作了直方图,我们可能会得到这样的照片。
由此可能有点难以确定我们认为我可能会再次击中哪里。但是如果我们对它进行分布拟合,图形会变成这样:
来自 100 个样本的结果符合具有估计参数的高斯分布(真值在括号中)
在视觉上,它对用户来说变得更加清晰。此外,因为我们知道分布的数学定义,我们不仅可以询问我的平均值在哪里,还可以询问我投篮的分布。
有必要说明一下,如果我拍摄了 1000 张照片,图表看起来会是这样:
用估计参数拟合高斯分布的 1000 个样本的结果(括号中为真)
拟合的分布要精确得多,但是你也可以说你不需要它来直观地询问你得到的形状。
那你用的是什么形状?
我用的是高斯分布。这是一个常用的发行版,因为它不仅比其他发行版更容易使用,而且自然界中的许多过程都可以这样建模。
这有什么关系?
好吧好吧。因此,这里的关键点是,对于 100 点与 1000 点的图表,拟合分布使您更容易看到发生了什么,可以说,有了它,您可以用比 1000 点图表少 900 个数据点的方式很好地估计实际值。我们正在对数据进行分布拟合,以表明我们认为最可能的点在哪里。
高斯过程使你能够用最少的数据提取尽可能多的信息
高层次的方法是:
1.获取一组初始数据点
2.对其进行分布拟合(在本例中为高斯分布)
3.读出你认为将在你的分布的最大值的结果区域
4.测试它并返回结果
5.根据您的数据重新调整分布,包括这个新点
6.回到步骤 3
在机器学习中,我们可以用树木的数量来构建一个随机的森林。我们测试几个不同的参数,计算训练模型的准确性,并返回这些参数。高斯过程将适合这些点,并尝试计算出哪一个树值给你最大的准确性,并要求你尝试一下。这导致两种结果:
如果精度增加,那么高斯拟合将变得更清晰
如果它是错误的,那么拟合将变平,它将开始看得更远
机器学习的问题是
随着我们添加超参数,我们开始添加更多的维度,我们可以想象对于两个参数,我们最终得到一个显示模型精度的图,如下所示:
超参数设置对模型性能的影响。三个最小值是显而易见的。
在这个例子中,我们可能有不止一个区域显示出模型性能的改进。这一过程的巨大优势在于它有能力以一种智能的方式描绘出这一点。
我们可以看一些例子(取自这里)来展示这个拟合过程。
贝叶斯优化如何随着我们添加更多数据点而改变的例子(来源
在这里,我们可以看到正在探索空间,当它达到第一个最小值时,它需要几个样本来建立它,然后继续探索它认为可能会得到更好结果的地方。在这个过程的最后,它会给你在这个过程中找到的最佳点。
在模型调优中,考虑到您给定的搜索限制,这将是模型的最佳参数集。
如果你想了解更多这方面的数学知识,我建议你:
让我们来看一些代码
在那篇冗长的引子之后,我们可以看看一些代码。我们非常幸运,因为已经存在一个用于调整机器学习模型的简单实现( BayesSearchCV )。
首先,我们需要导入所需的模块和我们想要适应的 ML 算法(我使用的是 gbrtclassifier )。
from skopt import BayesSearchCV
from skopt.space import Real, Categorical, Integer
from sklearn.ensemble import GradientBoostingRegressor
BayesSearchCV 在这里做繁重的工作。它的功能就像一个管道,接收你想要搜索的所有参数值(以及你如何设置它们),你想要调整的机器学习算法,并有大量其他可以改变的设置。
# Initialise the algorithm, search area and other parametersn_data = len(X_train)
opt = BayesSearchCV(
GradientBoostingRegressor,
{
'max_depth': Integer(1, int(n_data / 2),
'learning_rate': Real(0.01, 0.2, prior='log-uniform'),
'subsample': Real(0.1, 0.8, prior='uniform')
},
n_iter=30)# Start the optimisation and return a fitted algorithm
opt.fit(X_train, y_train)
在这里,我们传递算法,并且对于每个超参数空间,我们可以定义范围(一个例子是动态的,因为‘n _ data’是训练数据的行数)以及范围的种类,例如离散、浮点甚至分类。这告诉高斯过程它如何能探索这个空间。“n_iter”告诉它可以采样多少个点。我通常发现我在 30+分后开始得到好的结果。这部分是由问题的复杂性决定的,但也取决于你在开始时随机抽取了多少样本(默认情况下,它随机抽取 10 个样本来提供初始拟合)。
瞧啊。你完了!
现在,您已经用几行代码安装并调整了一个机器学习算法。如果您已经定义了问题,那么(哪些参数和范围等。)与从头开始尝试每一个组合相比,您应该可以更快地(和更少的采样)获得更好的结果。
照片由 Camylla Battani 在 Unsplash 上拍摄
还有一件事…
这个预建的功能不是你唯一能做的。你可以定义自己的目标来最小化。这非常有趣,因为你可以用它来优化非常复杂的问题。
例如,如果您对机器学习算法的纯分数不感兴趣,而是对它如何影响另一个函数感兴趣,您可以编写一个返回此值的目标函数。基本的规则是,如果你能把它压缩成一个值,回到高斯过程,你就可以写一个函数来最小化它。
下次见!
GAVRO —托管大数据模式演进
构建一个适应变化的数据接收架构不是很好吗?更具体地说,对模式演变具有弹性。
图片来源Pixabay.com
对于架构师和软件工程师来说,管理模式变更总是很麻烦。构建大数据平台没有什么不同,管理模式演变仍然是一个需要解决的挑战。NoSQL、Hadoop 和读取模式的咒语在一定程度上缓解了严格的模式执行的束缚。然而,集成开发人员、分析师和数据科学家在从大数据中提取准确见解时,仍然会受到大量数据争论的阻碍。
“成功的业务”以加速和放大已知数据模式的易变的步伐成长和发展。数据集不是静态的,而且是不断发展的,因此了解业务事实数据在业务的当前和历史时期代表了什么对于做出自信的信息洞察至关重要。
通过这篇文章和附带的 GitHub repo ,我将展示如何使用微软 Azure 技术在大数据平台中管理模式演进。
这是一个在实践中容易被忽略的领域,直到您遇到第一个生产问题。如果没有仔细考虑数据管理和模式演化,人们通常会在以后付出更高的代价。
confluent . io(2020 年 4 月 29 日),模式演变和兼容性。https://docs . confluent . io/current/schema-registry/avro . html # schema-evolution-and-compatibility
作为一名作家,很难决定如何讲述你的故事。我是直接进入技术解决方案,以满足寻找简洁例子的工程师,还是从为什么和动机开始?所以我就交给读者了。如果您想直接进入技术示例,请访问 GitHub repo 。如果你想知道更详细的情况,请继续阅读…
模式管理
如果消费者理解用于写入数据的模式并且该模式从不改变,那么序列化要存储在文件中以供以后分析的数据是相当简单的。
但是,如果模式随着时间的推移而演变,会发生什么呢?它变得有点复杂。
当写模式由于新的业务需求而发展时,消费者(读者)必须理解新模式是何时引入的,以及新模式的定义,以便成功地反序列化数据。未能理解模式更改事件将影响数据处理管道,服务将出错,因为它们无法反序列化数据。
这个问题有几种解决方案……(这绝不是一个详尽的列表)。
发布协调
作者和读者协调他们的积压工作和软件发布。当编写器和读取器应用程序由同一个工程团队开发和维护时,这可能会工作得很好。然而,通常情况下,作者和读者在整个企业中致力于不同的目标和优先级。应该避免通过严格的接口依赖来临时耦合独立的团队积压工作,因为这会抑制敏捷性和交付速度。
模式版本管理
版本化写模式支持向前和向后兼容性管理。提供向前和向后的兼容性可以消除积压和优先级,允许工程团队独立地实现他们的目标。
版本控制通常在两个不同的子主题的上下文中讨论。
主要—主要版本变更通常会破坏系统之间的接口和契约。主要的模式更改通常会阻止读者读取新模式版本写入的数据。向前和向后兼容很困难或不可能。
次要—次要版本变更通常被视为低影响变更。向前和向后兼容通常是可能的。读取器通常会像以前一样继续操作,成功地对数据进行解序列化,而不会升级到模式的最新版本。
构建一个能够适应变化的数据接收架构不是很好吗?更具体地说,是对模式演变的弹性。
下面是我将用来描述如何成功管理模式进化的 Azure 架构。
模式注册表
Kafka 的 Schema Registry 提供了一个在流架构上管理模式演化的很好的例子。Azure Event Hubs 是微软的 Kafka 类产品,目前没有模式注册功能。发布到事件中心的事件被序列化为嵌套在事件中心 Avro 模式体中的二进制 blob(图 1)。我们将很快讨论细节,但本质上发布的事件数据是无模式的,任何下游读取器都需要通过在读取时断言模式来对二进制 blob 进行反序列化。有一些巧妙的变通方法,利用了 Confluent 的模式注册表和事件中心。我将在这些建议的基础上,提供一种模式进化弹性的替代方法。在我之前的故事(发展成为 Azure Cloud 中的大数据驱动业务:数据摄取)中,我描述了一个数据湖摄取架构,该架构利用事件中心和事件中心捕获来形成大数据分析的批处理层。我将使用这个架构作为处理模式演变的参考。
图一。
{"type":"record",
"name":"EventData",
"namespace":"Microsoft.ServiceBus.Messaging",
"fields":[
{"name":"SequenceNumber","type":"long"},
{"name":"Offset","type":"string"},
{"name":"EnqueuedTimeUtc","type":"string"},
{"name":"SystemProperties","type":{"type":"map","values":["long","double","string","bytes"]}},
{"name":"Properties","type":{"type":"map","values":["long","double","string","bytes"]}},
{"name":"**Body**","type":["null","bytes"]}
]
}
活动中心
多少?我应该有几个活动中心?或者换句话说,我应该为我的所有数据使用一个大管道,还是为每种消息类型使用许多小管道?关于卡夫卡的话题,也有人问过同样的问题,但没有明确的答案。有一件事很有可能,不同的用例会偏好不同的方法。如果您关心的只是从 A 到 B 获取消息,或者您正在与您控制之外的架构集成,那么消息可能会通过一个事件中心,一个大管道流动。如果您的一些数据高度敏感,并且您只希望某些订户读取和处理这些数据,或者您可能需要特定的分区策略,这将导致在一个名称空间中采用许多事件中心、许多更小的管道。
大烟斗
- 如果一个事件中心包含许多具有不同模式的消息类型,我们如何正确地识别和反序列化各种消息?
小管
- 当一个事件中心只包含一种消息类型,并且这种消息类型会随着时间的推移而演变时,消费者如何反序列化新版本的消息呢?
乍一看,这些问题似乎毫无关联。然而,它们是同一个核心问题的表现。如何管理数据的去序列化?
事件中心上的所有消息都是二进制的匿名 blobs。一种选择是让消费者推断模式。然而,这种方法是不确定的,并且是基于抽样的,所以推断出的模式只能是一种近似。另一种方法可能是断言消费模式。然而,这意味着使用消息的工程团队暂时与模式的发展相结合,即使是很小的变化。事件中心捕捉为我们提供了一个打破时间耦合的机会,让消费者能够以自己的节奏消费从 t0**开始的数据。但是,如果使用者想要读取和使用由事件中心捕获流程生成的所有 AVRO 文件,他们还需要知道在捕获事件期间使用了哪些写模式来写入二进制消息。这可能是几个月甚至几年的数据。作为消费者,我需要知道模式演变的时间线,否则我将很难利用这些数据。
**井至少从乞讨事件中枢捕获配置。
活动中心捕捉的早期印象可能会让您认为 AVRO 被用来帮助解决上述问题。然而,在阅读了 AVRO 规范之后,似乎只有微小的版本变化是可能的。因此,无法管理重大变更,也不可能有多种消息类型的 AVRO 文件。
事件中心允许我们在发布消息时添加额外的元数据。这些元数据是管理模式演变的关键。
有两种可能的选择。
1)写模式与每个消息一起存储在事件中心客户端属性字典中。这将严重抬高存储成本。
2)消息类型标识符存储在事件中心客户端属性字典中。然后,该标识符用于从中央存储中查找模式。
对于这两种解决方案,模式总是直接或间接地与数据存储在一起。Event Hub Capture 生成的文件总是有一种识别写模式的方法。此外,每个文件可以包含 x 个消息类型和 y 个消息版本。
让我们来看一个使用客户端 SDK 将消息发布到 Event Hub 的 Azure 函数。
模式标识符总是存储在数据旁边(第 17 行)。
Azure 函数
在上面的例子中,函数使用一个定时器触发器每 5 秒执行一次函数的新实例。函数触发器是不相关的,它可以很容易地成为一个 CosmosDB 变更提要处理绑定或任何其他生成待处理数据的绑定。而且,用功能 app 也无关紧要,重要的是你发布到事件中枢的内容。函数 app 本身就是一个简洁的例子。
需要注意的是,通过添加对 eventData.Properties 的引用,消息的模式版本与消息一起被持久化。当事件发布到事件中心时,模式标识符总是与数据一起存储。
事件中心捕获
我将事件中心捕获配置为每分钟或每 500mb 生成一个新的 AVRO 文件,以先到者为准。因此,我们现在有了模式标识符和在整齐分区的 AVRO 文件中捕获的数据,但是我们如何在大数据管道中处理它呢?在我之前的故事中,我讨论了维护模式存储库的主题,以获取所有企业模式的真实描述。这种回购是用来创造一个人工制品,将在数据处理管道消费。这个产品是一个简单的键值存储,将版本化的模式标识符与所使用的写模式连接起来。出于本文的目的,我将使用一个简单的 Databrick Python 笔记本来处理 AVRO 数据。
数据块—笔记本
我不会对完整的笔记本进行详细描述,而是关注最重要的单元(完整的笔记本在 GitHub repo 中)。
AVRO 非军事化
首先是读取事件中心数据捕获 AVRO。Spark 的 AVRO dataframeReader 用于从存储器中读取 AVRO 文件,并将它们解编为数据帧。我们可以让 Spark 在这一点上推断模式,因为我们知道它是非易失的(即 Azure Event Hub 模式)。properties 属性保存用于将数据写入二进制字段“Body”的模式版本信息。对数据进行简单的投影,以处理具有三列的精确数据帧。模式版本是从 properties 对象中提取的(序列化属性字典中的存储值存储在子属性 member2 中)。“Body”属性被转换成一个字符串,因为我们想在后面的笔记本中对它使用 spark 的 JSON 反序列化器。
**from** pyspark.sql.functions **import** colrawAvroDf = spark.read.format("avro").load("wasbs://" + containerName + "@" + storageAccName + ".blob.core.windows.net/gavroehnamespace/gavroeh/*/2020/*/*/*/*/*.avro")avroDf = rawAvroDf.select(col("Properties.SchemaVersion.member2").alias('SchemaVersion'), col("Body").cast("string"))display(avroDf)
模式查找
第二个是模式查找对象。出于简化示例的目的,我将手动创建一些模式,用于反序列化 AVRO 数据。然而,在实践中,这些模式将从模式存储库中生成,并作为运行时工件存储。自我提醒,需要将此作为后续文章来写。
存储在一维数组中的模式代表一个已经进化的实体。在这个理论上的例子中,企业已经成长并开始以新的货币进行海外交易。交易现在需要货币标识符,因此在销售订单数据模式中添加了一个新的属性“currency”。作为读者,我们需要能够成功地对新数据进行反序列化。
**from** pyspark.sql.types **import** StructType, StructField, LongType, StringType, ArrayType, DoubleTypesalesOrderV1 =StructType([StructField('OrderId',StringType(),**True**),StructField('OrderAmount',DoubleType(),**True**),StructField('OrderCreatedUTC',StringType(),**True**)])salesOrderV2 =StructType([StructField('OrderId',StringType(),**True**),StructField('OrderAmount',DoubleType(),**True**),StructField('Currency',StringType(),**False**),StructField('OrderCreatedUTC',StringType(),**True**)])salesOrderSchemaDictionary = { "v1.0":salesOrderV1, "v2.0":salesOrderV2 }salesOrderSchemaDictionary
JSON 去军事化
我想关注的第三个单元是实际读取数据并对数据进行反序列化的单元。先前读入数据帧的事件中枢数据捕获输出用于确定数据中存在的模式版本的不同列表。对于每个模式版本,将创建一个新的临时 SparkSQL 表来访问反序列化的数据。原始 AVRO 数据帧在“for”循环的每次迭代中被过滤,通过不同的模式版本对记录进行分组以产生数据子集。然后,使用 salesOrderSchemaDictionary 中的相应模式对每个子集进行反序列化。将创建许多新的临时表,该单元的输出将显示已创建对象的列表。
**from** pyspark.sql.functions **import** concat, lit, regexp_replacedistinctSchemaVersions = avroDf.select('SchemaVersion').distinct()objectToCreate = distinctSchemaVersions.withColumn('TableName', concat(lit('SalesOrder'),regexp_replace(col('SchemaVersion'), '[.]', '_'))).collect()display(objectToCreate)**for** record **in** objectToCreate:schemaVersion = record.SchemaVersionjsonRdd = avroDf.filter(col("SchemaVersion") == schemaVersion).select(avroDf.Body)objectJson = jsonRdd.rdd.map(**lambda** x: x[0])dataExtract = spark.read.schema(salesOrderSchemaDictionary[schemaVersion]).json(objectJson)dataExtract.registerTempTable(record.TableName)
最后
最后,SparkSQL 可用于探索临时表中成功的去序列化数据。
%sql
**select** * **from** SalesOrderv1_0%sql
**select** * **from** SalesOrderv2_0
结论
我应该先弄清楚伽弗洛是什么。抱歉让你失望了,但这不是你不知道的新 Apache 孵化器项目。我的同事给我在本文中描述的方法起了一个可爱的名字。我相信这是我的首字母和 AVRO 的组合,起初我发现他们对这种方法的昵称是团队友谊的产物,但后来它坚持下来了。
我不相信设计和规定方法是完全精确的,应该无条件地适用于每个企业,因为每个企业都是不同的。因此,如果你从阅读这篇文章中学到了什么,那么我希望它是思考你的大数据管道中糟糕管理的模式演变的内涵的动力。我们一次又一次地听到组织在从大数据中提取信息和可操作的洞察力方面遇到的困难,以及数据科学家浪费 80%的时间在数据准备上是多么昂贵。如果应用得当,模式管理是一种武器,可以用来加速数据理解和减少洞察时间。所以花时间投资它,你会收获健康的回报。
引文
[1]沃尔坎·西韦莱克,事件中心模式验证(2019 年 4 月 1 日),https://azure . Microsoft . com/en-GB/blog/Schema-validation-with-Event-Hubs/
[2]马丁·克莱普曼,是否应该把几个事件类型放在同一个卡夫卡主题里?(2018 年 1 月 18 日),https://Martin . kleppmann . com/2018/01/18/event-types-in-Kafka-topic . html
影响
雅虎的 Apache Pulsar 系统:https://Pulsar . Apache . org/docs/en/schema-evolution-compatibility/
Confluent.io 的 Schema-Registry:https://docs . confluent . io/current/Schema-Registry/index . html
杰伊·克雷普斯,《日志:每个软件工程师都应该知道的实时数据统一抽象》(2013 年 12 月 16 日),https://engineering . LinkedIn . com/distributed-systems/Log-What-every-a-software-engineer-should-know-on-real-time-datas-unified
GCP 无服务器设计模式:遵守云任务的速率和并发限制
尽管我认为自己了解与数据工程相关的多种 GCP 产品,但我之前从未听说过云任务的用例。
这篇文章旨在通过提出一个具体的问题,并从发布者与云任务的角度讨论它,来阐明云任务的用例。
挑战
作为客户数据细分项目的一部分,我遇到了将用户数据发送到 Google Ads 再营销受众 API 的挑战,该 API 有以下限制:
- 每个请求只能包含大约 50.000 条用户记录。随着每个请求的记录越来越多,性能会以指数级的速度增长。
- 每个 Google Ads 帐户一次只能处理一(1)个请求。同时提交另一个请求会导致所有其他正在进行的请求出错。
- 每个请求需要一到五分钟来处理。
项目背景:
需要发送给 API 的数据将以 CSV 文件的形式从 BigQuery 到达云存储。每个预期的 CSV 文件将包含 50,000 到 4,000,000 条记录。
编排引擎(Cloud Composer)只负责在 BigQuery 中运行业务逻辑,并将结果保存到云存储中。Cloud composer 不处理传出数据管道,因为传出数据管道应该是反应式的&无服务器的。
要细分的客户群包含大约 400 万客户,每个客户至少属于一个细分市场。编排引擎将在 BigQuery 中运行一个业务逻辑查询——每个片段将被推送到 Google Ads——导致 7 个不同的文件在几分钟内到达云存储。
局部解决
当一个新文件进入云存储时,一个事件可以被提交到 PubSub,或者立即反馈到云功能。更多关于这个的信息在这里。
通过设置从云存储桶触发的云功能,我们可以通过将文件分成更小的部分来解决 api 限制 1(每个请求最多 50k 条记录)。
但是现在呢?简单地循环原始文件并将部分记录推送到 Google Ads 中是一种非常脆弱的处理方式。只需考虑以下几点:
我们期望该函数运行多长时间?
4.000.000/50.000=80 份。假设 50.000 条记录的每个部分需要 5 分钟来传输。我们谈论的是近 7 小时的连续运行时间。这远远超出了云功能所支持的范围。
如果我们已经在传输一个文件了,而另一个文件到达了 GCS 怎么办?
如前一节所述,我们的编排引擎将为每个片段生成一个文件。这意味着,当我们开始向谷歌广告推送第一个片段的大块时,另一个片段将会出现,并并行触发云功能的执行。根据 API 限制的第二(2)点,这将导致两个请求都失败。如果没有某种方法来确保只有一个并发的调度发生,我们将不得不把到达云存储的数据段之间的时间分散开。
Cloud PubSub 的问题(针对本次挑战)
我最初的方法是遵循 Google 设计的这个解决方案中概述的架构: 一个 GMP 的无服务器集成解决方案。该解决方案结合使用了启动器和传输功能,以及 3 个 PubSub 主题。要了解他们提议的架构,请阅读解决方案的 架构概述 部分。
我认为该解决方案中使用的架构可以精简一点,解释如下:
- 新文件登陆云存储,触发云功能启动器
- Initiator 将文件分割成几个较小的部分,并将它们发布到 PubSub 主题操作日志。
- 完成(2)后,发起方向操作触发器发布主题发送一条空消息
- 操作触发器通过订阅操作执行器推送空消息
- 操作执行器执行并从操作日志中提取信息开始。 如果操作日志中没有消息,则不执行任何操作。循环到此结束。
- 如果从操作日志中检索到消息,操作执行器会尝试将其推送到 Google Ads。
- 如果上一步(6)成功,操作执行器确认之前从操作日志拉取的消息,并向操作触发器发布空消息。从 4 继续。
这种方法的问题
我认为这种方法有几个问题:
- 如果操作执行器出错或失败,整个循环可能会中断。如果错误是由 Google Ads API 端错误引起的,您需要在代码中捕捉到这一点,并向操作触发器发送一条空消息以继续循环。这并不符合快速失效的原则(下面会有更多的介绍),而且开发起来更加困难。
- 如果当操作日志中只剩下一(1)条消息时发生上述错误,则该消息不可从操作日志中提取,直到确认截止日期过去。随后执行的操作执行器会认为没有更多的消息,并结束循环。
- 为了遵守 Google Ads API 的限制 2(并发请求),在操作触发器和操作执行器之间只能有一条消息流动。当第二个段文件到达云存储时,这将被违反。
- 我相信调试和理解架构是复杂的。
如果我们没有输出 API (Google Ads)的限制,这个解决方案会更好。
由于这种方法不经过深思熟虑是不可行的,所以我决定寻找替代方法。
云任务简介
云任务是一个分布式任务队列。您可以定义一个或多个可以向其发送任务的队列。队列就像它们听起来的那样。任务是要做的事情,通常定义为“运行这个 HTTP-请求,等待直到你得到一个 200/OK-响应代码”,如果没有,在 X 时间内再试一次。
在一个队列级别上,你有以下(和更多) 设置 :
- 每秒最大分派数:这个队列处理新任务的速度有多快?
- 最大并发调度:可以同时运行/执行多少个任务?
- 最大尝试次数:一个任务在进入“失败”状态之前可以尝试多少次?
在一个任务级别上你有以下(和更多) 设置 :
- 任务类型:在本文中,我们将只讨论 HTTP 目标类型。
- HTTP Task HTTP Method:HTTP 方法(GET/POST)
- HTTP 任务请求体:HTTP 请求体。最大 100kb。
请注意,云任务不是一个消息队列(像 PubSub),它只是感兴趣的任务定义,并支持最大 100kb 的请求体来描述在哪里可以找到任何最终数据。
现实生活中的排队。图片来源:https://unsplash.com/photos/Xbh_OGLRfUM 亚历山大·波波夫
云任务的完整解决方案
为了将云任务与我们的部分解决方案集成,我们可以将到达云存储的大传输段文件分解成多个位,并为每个位创建一个任务。我们会将所有这些任务发送到一个任务队列,我们已经将 Max concurrent dispatches 设置为一(1),以避免溢出 Google Ads API。
由于云任务不执行任务(它们只调用 HTTP-endpoint 并等待 200/OK 代码返回),我们将实际的 Google Ads API 调用放在云函数中。
架构概述
利用云任务将一个大请求分解成多个小请求的架构图。
- BigQuery 将结果输出文件写入 GCS。
- 完成写入 GCS 后,云功能任务创建器从 GCS 触发。
- 任务创建者**任务创建者从 GCS 中的触发文件的文件名中获取目标 API 和其他属性,并将其与任务主体中的部分文件的路径一起发送。
- 段队列中的任务被逐一处理,每个调用云函数任务处理程序。
- 对于任务处理程序 的每次调用,它都会解码任务主体并检索 GCS 中部分文件的路径以及 API 配置。然后,它将部分文件推送到 Google Ads,并在完成后向云任务返回 200/OK 响应代码。
这个架构给了我们什么?
云任务充当我们的输入和输出之间的缓冲,确保输出符合 API 的速率限制。
它还通过提供重试层来帮助我们提高开发速度,使我们能够编写集成代码,而很快就会失败。如果任务处理器函数失败,就让它失败,默认发送一个 http-error-code 给云任务。云任务将在适当的时候重试该任务。只要错误是在谷歌广告方面,任务最终将成功执行。
向外扩展
它也很容易向外扩展,以支持谷歌广告中更多的帐户(假设一个广告商活跃在几个国家),我们只需要在云任务中创建额外的队列。我们向任务创建器添加了一些代码,以便它可以动态选择任务队列,还向任务处理器添加了一些代码,以便它可以动态获取 API 凭证/配置。除此之外,可以使用完全相同的功能。
最后的话
这个用例显然不是打算由 PubSub 解决的,我很高兴它不是,因为它教会了我很多关于 PubSub 的局限性。
云任务显然是针对这一领域的问题,这是一个非常容易上手的服务。我强烈建议每个人都尝试一下,因为它可能是处理低性能 API 的最佳(无服务器)服务之一。
我计划在这个集成挑战中结合云任务和 PubSub。我有一个他们如何合作的想法。
如果你想进一步讨论这个问题,或者不同意我提出的任何观点,请随意发表评论。我是来学习的👨💻🤓
性别和人工智能
当前的问题和挑战是什么?
社会对性别的看法正处于临界点。AI 也是如此。那么,这些问题是如何交叉的,我们面临着哪些挑战?
1.艾绝大多数是女性
https://peopleofdesign.ru/2018/04/ios-12-concept/
在美国,94.6%的秘书和行政助理是女性。不出所料,Siri 也是。几乎没有主要的人工智能辅助项目可以避免这个缺陷,也没有太多改进的动力。虽然一些人工智能团队专注于代表性,但其他人提出了进一步的争议。
这些团队正在转向发展“无性别”的声音,以避免批评,但在现实中,有一个“无性别”的声音意味着什么?是我们无法识别为人类的东西,还是属于非二元/酷儿群体的声音?我们是不是在不知不觉中把 LGBTQ+的人推到了二等公民的范畴?为了开发对社会负责的人工智能,我们必须努力解决这些问题。
另一方面,也有视觉人工智能,这里的问题不仅仅是大多数是女性,还有超性感化。毫不奇怪,这种性别化的基调是面向白人,异性恋男子。像前玛奇纳这样的虚构人工智能导致了像 Lil Miquela 这样的“现实世界”名人,Lil Miquela 是一个由人工智能制成的 Instagram 名人/流行明星。
Lil Miquela(取自 Instagram)
虽然她可能是一个经理的梦想,但这个行业的女性已经表示,这种“没有意愿的女人”是她们的噩梦。这是麦莉·赛勒斯《黑镜》那一集的情节,他们把她变成了一个机器人,这样她的经理就可以完全控制她了……男性 AI 名人在哪里?女人什么时候才能摆脱天真、顺从的仆人变成红颜祸水的叙事?AI 好像也没什么帮助。
2.偏见
填空:
男人之于国王,犹如女人之于王后。
父亲之于医生,犹如母亲之于 _ _ _ _ _ _ _ _ _ _。
我说医生,艾一般说护士。
这个问题是由偏见引起的。这个问题源于数据集质量差,没有充分代表或歪曲某个群体。当我们根据这些数据训练我们的模型时,模型也会变得有偏差。这里,我们有一个偏向女性的填空模型,因为它所训练的文本包含了以这种方式反映女性的数据。这个问题不仅在女性中普遍存在,在非二元种族和少数种族中也是如此。
CalArts
在人工智能时代之前,在历史上收集大量数据的领域,这是一个更大的问题,当时偏见没有像今天这样受到重视。例如,大多数过去的医学研究都集中在雄性身上,尤其是在像老鼠这样的动物身上。这是由无数的原因造成的。特别值得注意的是,科学家声称女性的荷尔蒙周期是一个额外的变量。他们说他们无法控制这一点,因此应该将女性排除在研究之外(好像男性没有荷尔蒙)。这种情况有所改善,因为一些现代期刊要求在研究中包括一定数量的女性。然而,关于女性健康的历史数据远远少于我们对男性健康的历史数据,而且许多医学研究继续以这种方式运作。
偏见的另一个流行例子是面部识别,女性面部的错误率更高。对于肤色较深的人来说,这一比例甚至更高。这些问题的答案似乎是收集更好的数据,更有代表性的数据。此外,我们应该继续分别评估不同人口统计的错误率,以便我们可以改进我们的人工智能模型。
然而,如果我们消除了这种偏见,问题就变成了:我们到底应该用性别来通知人工智能的决定吗?很多推荐系统会猜测你的性别(使用你的在线行为),然后根据猜测给出建议。可以吗?它是否强化了性别刻板印象?我们是否需要更好地理解性别,这样我们就可以对男性/女性身体进行分类,并以此为医学提供信息?我们能问同样的关于种族的问题吗?
我们才刚刚开始理解这些问题的重要性以及我们可以回答这些问题的方法。负责的人工智能团队倾向于专注于数据收集以消除偏见,但未能解决这些影响,我们必须将它们进一步置于聚光灯下。
3.工作场所的性别问题
在数据科学职位中,性别薪酬差距并不是特别糟糕,但在数据分析职位中(这些职位通常薪酬较低)。此外,由于该领域是新的,当涉及到就业市场的心理时,存在很大的差距。有人说数据科学领域的每个人都有冒名顶替综合症,但尤其是女性。此外,男性可能会申请符合 50%或以上要求的工作,而女性通常只会申请符合 90%或以上要求的工作。
其他常见的工作场所问题也进入了人工智能世界,如骚扰,尴尬,无意义的权力动态。虽然这些并不是人工智能所特有的,但它们伴随着不平等及其所有后果困扰着科技领域。
女性申请专利的数量也存在差异,产假政策也不恰当。如果我们从数字上看,人工智能的工作场所显然仍然是男性主导的。比如→
https://www . statista . com/chart/4467/female-employees-at-tech-companies/
更令人震惊的是风险投资→
只有 1-2%获得风投资金的初创公司是由女性创始人领导的,尽管女性领导的公司获得了 200%的投资回报。这是科技行业中性别偏见最严重的部分。-帕斯卡尔·冯
宣传手册
是的,你没看错,大约 2%的风投资助的初创企业,比如脸书,是由女性领导的。
宣传手册
数字说明一切。
4.用人工智能将现实生活中的女性商品化
你可能听说过 ***,这是 deep fakes 的一个特殊实例,它处理你选择的一个人的照片,并返回他们完全裸体的图像。这是对神经网络的一种反常使用,安妮·海瑟薇(Anne Hathaway)对市场对性行为的看法——“我们生活在一种将不情愿的参与者的性行为商品化的文化中”——被人工智能不和谐地深化了。
如果人工智能工作场所的差异不是如此极端,这种情况会发生吗?基于人工智能的骚扰或虐待在#MeToo 运动中有一席之地吗?我相信是的,女权主义,就像生活中的其他领域一样,仍然需要赶上科技来保持它的道德性。
来源:
[## 2018 年,所有女性创始人加起来比朱尔少了 100 亿美元
2018 年,女性创始人获得了所有风险投资美元的 2.2%,与 progress 前一年的比例完全相同…
fortune.com](https://fortune.com/2019/01/28/funding-female-founders-2018/) [## 这就是为什么人工智能有性别问题
“对不起,我不知道那个。”Alexa,Cortana,甚至公共交通工具上的自动公告——它们都有…
www.weforum.org](https://www.weforum.org/agenda/2019/06/this-is-why-ai-has-a-gender-problem/) [## 解决人工智能中性别偏见的 4 种方法
对人工智能中偏见的任何检查都需要认识到这样一个事实,即这些偏见主要来源于人类的…
hbr.org](https://hbr.org/2019/11/4-ways-to-address-gender-bias-in-ai)
性别和地域偏见
人工智能、偏见和行业分类
按名称分类的小型企业
目录
1。概述2。背景
3。数据集和数据准备
4.1 偏倚模型:具有性别和姓名来源偏倚的模型
4.2 替换训练数据中的姓名并在推理时
4.3 用附加的性别信息扩充训练数据
5 .分析
6。结论
7。参考文献
这篇文章花了很长时间准备。感谢渥太华大学的米奥德拉克·博利奇教授审阅本文并提供宝贵的反馈。
1。概述
小企业分类指的是查看企业名称,并在上面贴上标签。这是许多应用程序中的一项重要任务,在这些应用程序中,您希望以相似的方式对待相似的客户端,这是人类一直在做的事情。例如,将公司名称的文本作为输入(例如,“丹尼尔屋顶公司”),并预测企业的类型(例如,“屋顶工”)。这项技术有商业应用。例如,拿一份发票清单,将它们分组到假日电子邮件中。有哪些客户彼此相似?您可以使用发票金额作为业务类型的指标,但企业的名称可能非常有助于您了解企业与您的业务往来,因此它们与您的业务有什么关系,以及您希望如何传达它们。在本文中,只使用企业名称的小型企业分类是我们的目标。
在本文中,我开发了一个 FastText 模型,用于预测一家公司属于 66 种业务类型中的哪一种,只基于业务名称。我们稍后将看到,在小企业名称及其业务类型的数据集上训练机器学习模型将引入性别和地理来源偏见。
来源:XKCD “它是如何工作的”兰道尔·门罗
本文探讨了两种消除观察偏差的方法:
- 用占位符标记替换给定的名称。通过隐藏模型中的给定名称来减少模型中的偏差,但是偏差减少导致分类性能下降。
- 用性别互换的例子扩充训练数据。比如 鲍勃的食客 变成了 丽莎的食客 。在评估数据集上,使用性别交换样本增加训练数据被证明在减少偏差方面不如姓名隐藏方法有效。
2。背景
我们最初的目标是观察与给定姓名性别和地理来源相关的预测偏差。接下来,让我们努力消除偏见。
一个常见的问题是,企业知道小企业交易对手的名称,但不知道该企业的原型。在进行客户细分时,小企业提出了一个独特的挑战,因为小企业可能不会在包括元数据(如公司类型或分类代码)的大公司的数据库或分类系统中列出。除了销售和营销功能之外,客户细分对于为客户定制服务交付也很有用。公司的类型在预测销售和许多其他应用中也很有用。客户名单中各种类型公司的组合也可以揭示客户细分的趋势。最后,对企业类型进行分类可以揭示有用的股票交易信号[1] [2]。股权交易模型的分析通常可以包括行业分类。例如,在[4]和[5]中,全球行业分类标准(GICS) [3]被用于评估不同行业的交易模型性能。
现有的行业分类系统往往不能很好地涵盖小企业。广泛使用的行业分类系统中包含的标准偏向于大企业,这种偏向渗透到训练数据(即公司名称)中,成为对大实体的偏向。我想这种偏见是偏向于高收入和高员工的公司。例如,根据罗素 3000 指数[6]中的公司名称训练的模型在推断时将不能正确地准备来预测在“丹尼尔理发店”进行的业务类型。诸如 Russell 3000 成员的较大公司的名称中既没有反映小企业的企业类型(例如,理发师),也没有反映小企业的命名惯例(例如,商店)。
有几个人策划的行业分类系统,包括标准产业分类法(原文如此)[9],北美行业分类系统(NAICS) [10],全球行业分类标准(GICS) [3],行业分类基准(ICB) [11],等等。对这些分类方案的一项比较显示,“GICS 优势每年都是一致的,并且在大公司中最为明显”[12]。这进一步强调了这一点,即这些分类系统更擅长对大公司进行分类,而不是对小企业进行分类。
行业分类系统中的标签为监督学习提供了机会。一种补充方法是应用无监督学习来对数据建模。例如,用于行业分类的聚类已经在[13]中得到应用。
测量文本分类器中的偏差需要一个不是原始数据集的子集或分割的验证数据集。第二个数据集是必需的,因为没有它,测试和训练数据将可能包含相同的数据分布,隐藏评估的偏差[14]。第二个数据集的目的是在非分布条件下评估分类器,它不是专门为其训练的。
可以通过用性别交换样本[15]增加训练数据,用更大的偏差更小的数据集[16]微调偏差,或通过其他权重调整技术[17],或从模型中移除性别特定的因素[18],来减少单词嵌入模型中的性别偏差。结合应用这些技术中的几种可能会产生最好的结果[16]。在这项工作中,增加性别交换样本的训练,并从模型中隐藏性别特定的因素进行了评估。
机器学习模型通常使用 top-k(也称为 top-n)准确度进行评估,以显示响应的特异性如何影响精确度和召回率[19]。在 top-1 评估中,只有当模型输出中的最高概率标签与真实标签匹配时,推断才被视为正确。同样,在 top-2 评估中,如果模型输出中两个最高概率标签中的任何一个与真实标签匹配,则推断被视为正确。更宽松的约束通常会导致更高的召回率和更差的精确度。
训练模型仅根据企业名称来预测小型企业的类型有一个潜在的缺点:引入偏差。小企业名称通常包括性别和地理本地化的名字。这可能导致模型开始将名字与企业类型相关联,这可能导致监管机构因歧视对企业进行罚款。例如,在没有对这种偏差进行调整的情况下,训练有素的模型可能将 丹尼尔的宝石 分类为珠宝店,而将 桑迪的宝石 分类为家庭工艺品商店。更复杂的是,由于混淆分类器以挑选错误的标签,或者由于导致与基于名称的地理原型相关联的标签,词汇表外的名称也可能有偏差。
激发了对从企业名称预测小企业类型的无偏模型的需求,现在提出了开发这样的机器学习模型的方法。
模型预测的粒度会影响观察到的性能。具体来说,考虑使用随机数生成器将小企业分类为两个一般类别之一有 50%的机会正确分类,而相同的随机数生成器有 1%的机会将公司分类为 100 个更详细的类别之一。显然,对特异性的统计惩罚证明了根据小企业分类模型进行一般和特定预测的合理性。因此,本研究中提出的模型输出了关于整体企业类型的高级预测,以及关于确切的小型企业类型的更具体的预测。
3.数据集和数据准备
本文中使用的小企业分类标签来自温哥华市第 4450 号许可证条例[7]。用于模型开发的数据集来自该市的开放数据网站[8]。该数据集包括各种小企业,比常用的行业分类系统更适合小企业分类。
高级小企业类型预测中的标签是 B2C 、 B2B 、 PUB 和 B2BC 。B2C 代表企业对消费者,因此 B2C 公司向消费者销售产品。类似地,B2B 公司在企业对企业的基础上销售给其他公司。B2BC 公司向企业和消费者销售产品,公共事业由参与提供公共服务的政府实体组成(例如,学校、协会和政府实体)。温哥华市许可证条例 4450 [7]中的类别与上述 4 个高级别类别之间有一个映射,如本文后面部分所述。
对来自数据集的样本进行的人工数据审核显示,大约 30%的标签对我(人类)来说是难以预测的。下面的表 1 显示了 20 个记录的代表性样本,揭示了模型预测性能的一些限制。显然,当唯一可用的信息是公司名称时,小企业类型分类是具有挑战性的。一些公司的名字包含了独特的文字、街道地址,或者不是描述性的。其他的要么过于笼统,要么过于具体。该任务的合理性能预期是什么?存在不可解决的边缘情况,并且分类器的有用性(作为客户细分信号是否足够好)是主观的。结果对用于训练机器学习模型的数据集也非常敏感。在本文中,我将报告我观察到的结果,如果您试图将这些内容融入到您自己的工作中,则由您来决定定性的接受标准。
表 1:来自公司名称数据集的经过处理的随机样本,表明一些标签很难比偶然更好地正确预测,即使对人类来说也是如此。
数据集的准备始于阐明数据集中每个企业的企业名称和类型。为了从数据集中的 BusinessName 和 BusinessTradeName 字段中选择最具描述性的名称,BusinessTradeName 被用作企业名称,除非该字段为空,在这种情况下, BusinessName 字段被用作企业名称。接下来,从数据集中删除以圆括号开头和结尾的企业名称(表示企业名称是个人名称)。业务类型是从数据集的业务类型字段中提取的。接下来,删除业务名称和类型对中的重复项。这些重复可能是由于许可证续订等事件而存在的。接下来,从数据集中删除带有低频业务类型标签的项目。具体来说,少于 100 个样本的标签被丢弃。
数据集中的一般类别“办公室”已从数据集中删除,因为它不是用于构建企业名称分类模型的描述性类别,似乎是一个总括。这可能不是一个好的做法,因为在这一类别中可能有偷偷摸摸的例子,但我们继续。
当原始类别中的企业名称被认为过于相似而不能分开时,相似的类别被合并到更广泛的类别标签中。具体来说,房地产行业的几个标签(公寓房、1956 年前的住宅、非营利住房、公寓房层、二级套房-永久、多套住宅、复式和单户住宅)被通用标签住宅/商业所取代。“临时酒类许可证修订”、“酒类机构标准”、“酒类机构扩展”、“酒类许可证申请”和“酒类零售店”的标签被更通用的标签“酒类机构”所取代。U-Brew/U-Vin 类别中的项目与白酒设备类别标签合并。三个标签洗衣房-投币服务,洗衣房仓库,和洗衣房(w/设备)被更普遍的标签洗衣房所取代。标签有限公司服务食品机构、餐馆类别 1 和餐馆类别 2 被替换为更通用的标签餐馆。标签短期租赁,和汽车旅馆是结合在一起的类别酒店。承包商——特殊行业与承包商类别相结合。虽然不是所有的商业和贸易学校都是私立学校,但学校(商业和贸易)的标签被合并到学校(私立)类别中,因为这两个类别的名称相似。最后,标签艺人 Live/Work 工作室并入了品类工作室。
完成预处理后,数据集中跟踪的 66 种业务类型以及每个标签的样本数量显示在下面的表 2 中。
表 2:数据预处理后的业务类型及其样本数
表 3:高级业务类型,它们的样本数,以及数据预处理后映射到每个高级类的业务类型。可以观察到类别之间的样本不平衡。
我没有清理这个项目的代码,但我认为它会帮助你看一看我为撰写这篇文章而写的一些代码。你可以点击这里查看我用来争论数据集、训练模型等等的许多东西的要点。
4.1 偏倚模型:具有性别和姓名来源偏倚的模型
在上述预处理之后,使用随机权重初始化在数据集上训练快速文本监督学习模型[20]。学习率和训练迭代次数的超参数搜索导致选择 6 次训练迭代作为早期停止点,并且选择 0.2 的学习率。模型的嵌入维度宽度为 100 维,窗口大小设置为 5。模型性能记录在下面的表 4 中,以及接下来两节的一些剧透。
表 4:分类性能报告。这里的结果告诉我们分类器是否工作,但不告诉我们分类器是否有偏差。μ是平均值,σ是标准差。这些结果是 10 次训练和测试运行的平均值(测试 n=7,087)。涉及所有 66 个类别的预测被评估为前 1 和前 2 性能,而当预测四个高级标签之一时,仅报告前 1 性能。
这个初始模型是根据世界上一个地理区域(加拿大西部)的企业名称进行训练的。该数据集包括企业名称训练数据中的各种本地名和姓。数据集来自一个说英语的省份。这些因素代表了基于企业名称中给定名称的性别和企业名称中名称的地理来源的分类偏见的强大潜力。
我们想评估当模型仅仅基于一些有偏见的东西改变它的想法时,比如与名字相关的性别。查看表 6,有两种方法用于评估模型偏差。在第一种方法中,通过构建分布外数据来评估模型,分布外数据由随机选择的给定名称和随机字典单词(首字母大写)组成。比如《奥利维亚之镜》vs《诺亚之镜》。在第二种方法中,来自模型测试数据集的文本(从模型训练中保留的数据)被附加到随机选择的人的名字上。比如“丹尼尔的鲍勃杂货店”。为了检验模型中的偏差,这两种方法的生成过程都控制了人名的性别和地理来源。下面的表 5 显示了用于测试的按性别和地域划分的姓名列表。
表 5:用于模型评估的名称列表
我们之前注意到,这是一个具有挑战性的数据集,其中一个人(我)至少有 30%的采样数据有问题。我们可以在的表 4 中看到,这个初始模型的前 2 个预测是可以的,在 66 个类别中,给定的业务类型有大约 73%的机会被正确标记(回忆),应用的标签有 36%的机会是正确的(精确)。但是,我们还在表 6 的“初始模型”一栏中看到,该模型有相当大的偏差,从 1%到 10%不等,或者高达 19%的偏差,这取决于您如何测量。在接下来的几节中,我们试图从模型中消除这些偏见。
4.2 在训练数据中和推理时替换姓名
为了解决初始模型中已确定的偏差,进一步处理训练数据,用我们称为令牌的特定字符串替换公司名称数据集中的给定名称。这种方法的关键见解是,该模型可以了解较少的本地名及其相关性别,而不是试图了解不在分布范围内的“外国”名或性别名。使用 spaCy 的命名实体识别功能[23]首次尝试了给定名称替换任务,但最终证明基于字典的方法对于所讨论的数据集更有效。为了获得这项工作中报告的结果,从 python 库[24]中获得了一长串男性和女性姓名,而 python 库又从 1990 年美国人口普查中获得了姓名列表[25]。从企业名称中删除所有名称的一个问题是,如果企业的全名是一个名称,则企业名称可能被删除,导致分类失败。例如,名称“Denny's”只是消失在一个空字符串中,因为“Denny”也是一个给定的名称。出现这一问题的另一种情况是企业以个人名义注册,例如“John Smith”。使用一个标准化的字符串来表示被替换的名称。具体来说,字符“_”被用作替换标记。
在这个额外的预处理之后,在数据集上训练了新的 FastText 监督学习模型。学习率和训练迭代次数的超参数搜索导致选择 6 次训练迭代作为早期停止点,并且选择 0.2 的学习率。模型的嵌入维度宽度为 100 维,窗口大小设置为 5。这款车型的性能记录在上面的表 4 中。
观察到的 top-1 精度和召回率相对于原始模型下降了大约 4%,这是在为分类提供信号的数据集中消除偏差的一个可以理解的结果。这种下降表明,也许一些分类性能源于基于名称的偏见。
4.3 用额外的性别信息充实培训数据
解决初始模型中已识别偏差的第二种方法是用文本的性别交换副本来扩充训练数据。例如,可以观察到带有标签 Plumber 的企业名称“Alice and Associates Plumbing Ltd”包含上述女性名列表中的名,并且可以将带有标签 Plumber 的新培训记录添加到数据集中:“Bob and Associates Plumbing Ltd”。类似地,包含男性名字的训练记录可以用性别交换的版本来扩充。直觉是,每个标签性别信息的平衡可以抵消每个标签的性别偏见。
在训练数据扩充之后,在数据集上训练了新的 FastText 监督学习模型。学习率和训练迭代次数的超参数搜索导致选择 6 次训练迭代作为早期停止点,并且选择 0.2 的学习率。模型的嵌入维度宽度为 100 维,窗口大小设置为 5。模型性能记录在上面的表 4 中。
表 6:模型偏差评估:基于名字的性别和地理来源的预测分歧。μ是平均值,σ是标准差。对于方法 1 的行,将 10,000 个样本与 10,000 个其他样本进行比较。对于方法 2 的行,使用模型训练数据作为基础,将 7087 个样本与 7087 个其他样本进行比较。注:为了关注大规模偏倚,不考虑一个类别内少于 5 个样本的不平衡。每个预测都是 66 个可能标签中的一个。
5.分析
在表 6 中观察到,正如预期的那样,分布外数据(方法 1)的模型评估揭示了比分布内数据集(方法 2)测试更高的偏差。在训练和推断过程中用固定字符串替换姓名的方法消除了分布外测试中的偏差。具体来说,在方法 1 中,每个类别的分类不平衡从 11.35% (σ = 2.23%)下降到 0.01% (σ = 0.00%)。关于从模型训练中保留的测试数据的结果,观察到偏差从 1.54% (σ = 0.08%)下降到大约三分之一,为 0.47% (σ = 0.06%)。因此,我们可以看到,我们在本文中尝试的两种评估方法的偏差都有显著下降。
令人惊讶的是,使用性别交换样本增加训练数据在[15]中有效,但在这项工作中研究的数据集上并不有效。分布外偏倚结果(方法 1)为 3.32% (σ = 0.01%),显著低于原始模型。然而,结果不如名称替换方法强,并且以较低的模型性能和来自公司名称数据集 1.90% (σ = 0.19)的测试数据的更严重的不平衡为代价。
两种偏差减少方法都存在模型稳定性问题。这些模型对很小的变化都很敏感。例如,模型对企业名称中单词的大小写很敏感,例如“Bob's Plumbing”被归类为“Plumber ”,而“Bob's plumbing”被归类为“Restaurant”。这种敏感性是输入模型的样本之间分类不一致的主要因素。例如,在名称替换模型中,输入样本“Aria Lodge & Associates _ Ltd”和“_ Lodge & Associates _ Ltd”会产生不同的类预测,即使文本仅相差几个字符。
请注意,虽然结果暗示替换姓名在某种程度上完美地替换了训练和测试数据中的所有姓名,但事实并非如此。这些预测确实会收敛,导致基于给定姓名的分类差异非常小,但数据集本身仍包含 1990 年美国人口普查姓名列表中未包括的几个姓名。例如,加拿大女性姓名列表中的 Aria 和墨西哥女性姓名列表中的 Ximena 这两个名字都不是通过姓名替换方法删除的。此外,数据集中的许多名字,如 Ho 和 Fraser,没有被删除。这一观察意味着从训练数据中移除大多数给定的名字足以解决大多数问题。此外,还存在文本中的标记被错误替换的情况,删除了一些本可用于分类的信息。例如,企业名称“Lodge & Associates Investigations Ltd”丢失了单词“Investigations ”,因为它有一个子字符串“In ”,该子字符串与名称列表中的名称相匹配。从名称列表中删除这一个 case(“In”)并没有显著提高模型的性能,因此很可能存在一组这样的名称替换精度和召回改进,它们共同提高了模型的整体精度和召回。这一额外的改进方向将作为未来的工作。当一本书说“这是留给读者的一个练习”时,你难道不喜欢吗?这实际上意味着我有这个想法,但是没有花时间去编程和测试它。
6.结论
本文开发了一个小企业类型分类器,并在模型中观察到了性别和地理来源偏差。虽然通过隐藏模型中的给定名称减少了模型中的偏差,但这是以牺牲模型性能为代价的。用性别交换样本扩充训练数据不如姓名隐藏方法有效。在一个完整的项目中,名字隐藏方法的精确度和召回率可能会提高很多。在我写的关于这个主题的下一篇文章中,我计划看看用于文本分类的 sk-learn 管道,以及使用特征选择来消除偏见。
如果你喜欢这篇文章,那么看看我过去最常读的一些文章,比如“如何给一个人工智能项目定价”和“如何聘请人工智能顾问”还有嘿,加入快讯!
下次见!
——丹尼尔
Lemay.ai
丹尼尔@lemay.ai
7.参考
[1] Lamponi,d .:行业分类对预测美国股票价格联动有用吗?财富管理杂志 17(1)(2014)71–77
[2]kaku sadze,z .,Yu,w .:开源基础行业分类.数据 2(2) (2017) 20
[3] Barra,m .:全球工业分类标准(GICS)。标准普尔技术报告(2009 年)
[4] Fischer,t .,Krauss,c .:用于金融市场预测的具有长短期记忆网络的深度学习。欧洲运筹学杂志 270(2)(2018)654–669
[5]j . bro fos:股票收益分类和预测综合委员会(2014 年)
[6]罗素指数:罗素 3000 指数
[7]温哥华市:加拿大不列颠哥伦比亚省温哥华市许可法第 4450 号(2019)https://bylaws.vancouver.ca/4450c.PDF。
[8]温哥华市:营业执照—温哥华市开放数据门户(2019)https://open data . Vancouver . ca/explore/dataset/Business-licenses/information/?disaccessive . status disjuctive . business subtype。
[9]英国官方:经济活动标准工业分类(SIC) (2018),于 2019 年 25 月 12 日查阅 https://www . gov . UK/government/publications/Standard-industrial-class ification-of-economic-activities-SIC。
[10]美国:北美工业分类系统。标准,总统行政办公室——管理和预算办公室(2017 年)
[11]富时罗素:行业分类基准(ICB)2019 年 12 月 26 日访问https://www . ftserussell . com/data/Industry-Classification-Benchmark-ICB。
[12]博杰拉杰,s .,李,C.M .,奥莱尔,D.K .:我的台词是什么?资本市场研究的行业分类方案比较。会计研究杂志 41(5)(2003)745–774
[13]kaku sadze,z .,Yu,w .:统计行业分类。风险与控制杂志 3(1)(2016)17–65
[14] Dixon,l .、Li,j .、Sorensen,j .、Thain,n .、Vasserman,l .:测量和减轻文本分类中的非预期偏差。载于:2018 年 AAAI/ACM 人工智能、伦理与社会会议录,ACM(2018)67–73
[15]赵,j .,王,t .,Yatskar,m .,Ordonez,v .,Chang,K.W .:共指消解中的性别偏见:评估和去偏见方法. arXiv 预印本 arXiv:1804.06876 (2018)
[16] Park,J.H .,Shin,j .,Fung,p .:减少辱骂性语言检测中的性别偏见. arXiv 预印本 arXiv:1808.07231 (2018)
[17]蒋,h .,纳丘,o .:识别和纠正机器学习中的标签偏差. arXiv 预印本 arXiv:1901.04966 (2019)
[18]t . Bolukbasi,Chang,K.W .,邹,J.Y .,Saligrama,v .,Kalai,A.T .:男人对于计算机程序设计员就象女人对于家庭主妇一样?去偏置词嵌入。神经信息处理系统进展。(2016) 4349–4357
[19] KAMATH,U.L .,WHITAKER,j .:NLP 和语音识别的深度学习。施普林格(2019)
[20] Joulin,a .,Grave,e .,Bojanowski,p .,Mikolov,t .:《有效文本分类的锦囊妙计》。arXiv 预印本 arXiv:1607.01759 (2016)
[21] BabyCenter,L.L.C .:加拿大 2017 年最受欢迎的名字:2019 年 24 月 12 日访问的前 20 名和趋势(2018)https://www . baby center . ca/a 25024668/Canada-2017 年最受欢迎的名字-前 20 名和趋势。
[22] BabyCenter,l . l . c .:Los nombre m \u as comunes para beb es en El 2015(2015)于 2019 年 4 月 12 日通过网络档案查阅https://web . archive . org/web/20160611202700/http://vidayestilo . terra . com . MX/Mujer/Los-nombre-MAS-comunes-para
[23] Honnibal,m .,Montani,I.: spaCy 2:使用 Bloom 嵌入、卷积神经网络和增量解析的自然语言理解。出现(2017)
[24] Hunner,t .,Visser,s .:2019 年 12 月 25 日访问的随机名称生成器【https://github.com/treyhunner/names。
[25] census.gov:来自 1990 年人口普查的常见姓氏-2019 年 6 月 12 日访问的姓名文件(2014 年 9 月)https://www . census . gov/topics/population/genealogy/data/1990 census/1990 census name files . html。
认知人工智能中的性别和种族偏见。
什么是认知 AI 偏差,我们如何与之对抗?
我们无法理解人工智能中偏见的概念,除非我们首先理解“偏见”这个术语的含义。根据维基百科的定义,偏见是对一个想法或事物的或不相称的支持,通常是以一种封闭的、偏见的或不公平的方式。偏见可能是天生的,也可能是后天习得的。人们可能会对某个人、某个团体或某个信仰产生偏见。在科学和工程中,偏差是一种系统误差。统计偏差是由不公平的总体抽样或不能给出准确平均结果的估计过程造成的。**
关于图像 AI 的性别和种族偏见,也称为认知 AI 偏见,是一种由工程师构建的模型和算法未能为特定性别或种族的人提供最佳服务的概念。在图像人工智能中,最常见的情况是,即使识别系统也无法检测和识别代表性不足的性别和种族。这是一个非常严重的问题,尤其是对于那些为公众提供平等和透明服务的系统而言。在这个故事中,我们将讨论模型和算法描绘性别和种族偏见的一些典型实例,讨论由此造成的一些影响,并谈论我们如何帮助打击图像人工智能中的种族和性别偏见。
是什么导致了图像人工智能中的性别和种族偏见
智能模型的好坏取决于用来训练它的数据。— 德里克·德贝杜伊
模型和算法只能做它们被训练去做的事情;检测和识别他们被训练识别的事物(或人)。几乎每一个生产中的图像 AI 模型或算法都经过了非常大的数据的训练和测试。制作这样的数据集非常耗时且昂贵。如果模型或算法是为公众服务而开发的,它需要许多不同性别和种族的不同人的高质量头像照片。由于制作如此庞大的数据集所涉及的压力,大多数工程师更喜欢使用开源或闭源数据集,其中大部分数据收集已经为他们完成。他们没有认识到的是,即使他们的模型和算法会以非常高的精度通过,也必须有几乎每个性别和种族的人的足够数据。如果只使用白人的数据进行训练,你的模型和算法对黑人来说将会失败,反之亦然。这同样适用于性别。你的组织有唯一的责任确保你的训练和测试数据包括所有可能使用你的服务或产品的种族和性别。当这些事情都处理好了,我们终于可以在认知 AI 中拥有无偏的系统、模型或算法。
人工智能系统描绘认知人工智能偏差的典型例子。
过去几周,许多人发现了大多数人工智能系统描绘的认知偏见,并开始公开宣布这些偏见,以便公众知道,并因此迫使相关公司修复他们的人工智能系统。我将分享其中的一些推文,这样任何想了解公开测试期间发生的事情的人都可以找到答案。
-
Zoom 无法检测到黑人面孔并在使用虚拟背景时删除他们: Twitter 用户科林·马德兰注意到一名黑人教师在切换到虚拟背景时,他的面孔被 Zoom 的面部识别系统删除了。下面的图片捕捉到了发生的事情。
-
Twitter 在帖子中裁剪出黑人面孔: Twitter 运行一种面部检测算法,裁剪发布在平台上的图像,以关注发布图像中的面部。最近,很多用户注意到,当不同种族的人在一幅图像中有多张脸时,裁剪图像的算法会优先考虑白人脸。
上面嵌入的同一个用户, Colin Madland 注意到,在张贴了一张他和他的黑人教员的脸的图像后,twitter 算法在移动设备上的图像预览中裁剪出了他的黑人教员的脸。
许多用户还进行了社会实验,以测试对 twitter 算法的指控是否属实,结果仍然相同,得出的一般结论是,该算法确实存在种族偏见。这里有一些来自社会实验的推文。
这些只是成千上万个例子中的几个,twitter 算法描绘了认知人工智能在预览图像时从图像中剔除有色人种的偏见。
- Twitter 从图像预览中裁剪女性:早在 2019 年,VentureBeat 就发布了一条关于 Yann LeCun,Hilary Mason,吴恩达和 Rumman Chowdhury 对 2019 年人工智能预测的推文,作者是 Khari johnso n。在图像预览中注意到,帖子中涉及的女性的脸被裁剪出了图像预览。这是 twitter 算法对性别偏见的一个演示。以下是确切的推文。
人工智能中认知偏差的后果
多年来,人工智能积极融入我们的日常生活,随之而来的性别和种族偏见影响了许多有色人种和代表性不足的性别的生活。这些系统有很多错误的预测,导致很多人在监狱里度过一生,享受不到某些服务,甚至死亡。这里有几篇文章指出了人工智能中的认知偏差带来的一些不利后果。
一种在美国医院广泛使用的为病人分配医疗保健的算法已经被系统地识别…
www.nature.com](https://www.nature.com/articles/d41586-019-03228-6) [## 为什么这个皂液机不能识别深色皮肤?
周三,尼日利亚的一名脸书员工分享了一个小麻烦的镜头,他说这是对技术的…
gizmodo.com](https://gizmodo.com/why-cant-this-soap-dispenser-identify-dark-skin-1797931773) [## 被算法错误地指控
这可能是第一个已知的同类案件,一个错误的面部识别匹配导致了密歇根…
www.nytimes.com](https://www.nytimes.com/2020/06/24/technology/facial-recognition-arrest.html) [## 警察面部识别对毛利人的歧视只是时间问题——专家
毛利人因为面部识别的错误匹配而被错误逮捕只是时间问题…
www.rnz.co.nz](https://www.rnz.co.nz/news/te-manu-korihi/425081/police-facial-recognition-discrimination-against-maori-a-matter-of-time-expert) [## 摄影中固有的种族偏见
莎拉·刘易斯探讨了种族主义和照相机之间的关系。本周,哈佛大学的拉德克利夫…
www.nytimes.com](https://www.nytimes.com/2019/04/25/lens/sarah-lewis-racial-bias-photography.html) [## 语音识别仍然存在明显的种族和性别偏见
与面部识别、网络搜索甚至皂液机一样,语音识别是另一个…
hbr.org](https://hbr.org/2019/05/voice-recognition-still-has-significant-race-and-gender-biases)
我们如何在认知人工智能中对抗性别和种族偏见
在认知人工智能中打击性别和种族偏见的第一步也是最重要的一步是纠正我们在训练和测试我们的系统、模型和算法时使用的数据集。我们需要重新思考我们的数据收集和保留协议,并使我们的工程团队多样化。我们需要严格确保我们部署到生产中的系统、模型和算法已经通过了我们可以进行的所有测试,以确保系统是健壮的,即使它们考虑到了可能使用系统或模型的每个单一性别或种族的人。
克里斯蒂娜@ wocintechchat.com 在 Unsplash 上的照片
我希望我可以提高对认知人工智能中系统性性别和种族偏见的认识,并鼓励工程师和开发人员做得更好,建立比我们目前更好的系统。认知人工智能中种族和性别偏见的不利影响相当严重,我们不希望无辜的人经历他们目前正在经历的一些事情。
如果你想谈论与这个话题或数据科学、人工智能或人工智能相关的话题,请随时在 LinkedIn 或 Twitter 上与我联系。我很乐意和你聊天。为构建系统和算法干杯,为所有种族和性别的人创造一个更美好的世界。黑客快乐!🚀
上帝保佑 安娜·阿依库 为她花时间和耐心校对和纠正我写这篇❤️时犯的许多错误
机器翻译中的性别偏见
莱昂纳多·大久保俊郎在 Unsplash 上的照片
机器翻译中性别偏见的故事和谷歌解决它的方法
2016 年 5 月 ProPublica 发表了一项分析,显示 COMPAS,一种通过预测被告重新犯罪的风险来指导美国判决的人工智能工具,存在种族偏见。黑人被告更有可能被错误地标记为高风险,而白人被告更有可能被错误地标记为低风险。
2018 年 10 月,亚马逊承认在发现该模式重男轻女后,他们已经放弃了一个自动审核申请人简历的项目。
2018 年,谷歌宣布在谷歌翻译显示在将中性土耳其语翻译成英语时存在性别偏见后,他们正在采取第一步措施解决机器翻译中普遍存在的性别偏见。
机器学习中的偏差
偏见是当今围绕人工智能和机器学习的最大伦理问题之一。根据韦氏大词典的定义,偏见是
通过选择或鼓励一个结果或答案而引入抽样或测试的系统误差。
虽然机器学习模型可以是一个强大的工具,但它只能与它学习的数据一样好。因此,如果用于训练机器学习算法的数据中存在系统误差,那么产生的模型将会反映这一点。俗话说“垃圾进,垃圾出”。
paweczerwi ski 在 Unsplash 上的照片
在许多情况下,这并不是机器学习实践者在选择数据集或训练模型时主动偏向的结果。相反,固有的社会偏见,如性别或种族偏见,在本质上是特定社会历史记录的数据集中表现出来。反过来,这些数据集将它们的偏见传递给从中学习的机器学习模型。
作为一个例子,我们稍后将重新讨论,如果历史上担任医生的男性多于女性,那么基于历史数据训练的机器学习模型将学习到医生更可能是男性而不是女性,这与当前医生中的性别差异无关。
机器翻译中的性别偏见
机器翻译模型是在巨大的文本语料库上训练的,有成对的句子,一个句子是另一个句子的不同语言的翻译。然而,语言中的细微差别常常使得从一种语言到另一种语言提供准确和直接的翻译变得困难。
从英语翻译成法语或西班牙语等语言时,一些中性名词会被翻译成特定性别名词。比如“他的朋友是善良的”中的“朋友”这个词,在英语中是中性的。但在西班牙语中,它是有性别区分的,要么是“amiga”(阴性),要么是“amigo”(阳性)。
在西班牙语中,“朋友”一词是有性别区分的,可以是“amiga”或“amigo”
另一个例子是从土耳其语到英语的翻译。土耳其语几乎是一种完全中性的语言。土耳其语中的代词“o”在英语中可以翻译成“他”、“她”、“它”中的任何一个。谷歌声称他们 10%的土耳其语翻译查询是不明确的,可以正确翻译成任何性别。
在这两个例子中,我们可以看到一种语言中的短语如何正确地翻译成另一种语言,并根据性别有不同的变化。没有哪一个比另一个更正确,并且在没有提供进一步的上下文的情况下,具有相同翻译任务的人将面临相同的歧义。(唯一的区别是,也许人类会知道询问进一步的上下文,或者提供两种翻译。)这意味着当从一种语言翻译成另一种语言时,假设任何给定的单词、短语或句子总是有一个正确的翻译是不正确的。
现在很容易理解为什么谷歌翻译会有性别偏见的问题。如果社会偏见意味着历史上成为医生的男性比女性多,那么在培训数据中,男医生的例子就会比女医生多,这只是性别失衡的准确历史记录。该模型将从这些数据中学习,导致医生更可能是男性的偏见。
现在,当面临从土耳其语到英语为“o bir doktor”、“他/她是医生”找到一个单个翻译的任务时,模型会假设“o”应该被翻译为“他”,因为医生更可能是男性。
人们可能会看到护士会出现相反的情况。
对中性查询进行分类
2018 年,当谷歌承诺迈出解决谷歌翻译中性别偏见的第一步时,他们的解决方案涉及两个关键步骤。首先,他们创建了一个分类器来确定有资格被翻译成多种性别的查询。他们训练了他们的分类器,一个卷积神经网络,在数千个人类标记的例子中,人们被要求判断查询是否是性别中立的。
第二步是根据目标语言中是否包含阳性、阴性或中性词,将他们的训练集分成三组。然后,他们在查询的开头添加了一个额外的标记,以明确说明该查询应该转换成的性别:
- 他是一名医生
- 她是一名医生
这样,翻译者就知道将中性查询翻译成哪种性别。
在用户方面,这样做的结果是,如果分类器将查询标记为中性,并且没有明确要求,那么这个特性会将查询翻译成两种性别。
Google Translate 过去基于数据偏差假设中性查询的性别(左),现在它同时提供阳性和阴性翻译(右)
审查性别翻译后
当时,谷歌声称这个新系统将“在 99%的时间里可靠地产生阴性和阳性翻译”。然而,在今年早些时候的博客帖子中,他们收回了原话,称“随着这种方法被应用到更多的语言中,很明显在伸缩方面存在问题”。
他们声称有两个主要问题:
- 该模型无法在多达 40%被视为性别中立的查询中显示性别特定的翻译。这意味着,即使指定目标语言中所请求性别的标记是正确的,模型也无法返回基于该性别的正确翻译。
- 他们发现,用一个单独的分类器来确定每一种不同语言的性别中立性,数据过于密集。
相反,他们提出了一种新的方法,一种基于重写器的方法。在第一种情况下,不是将查询翻译成两种不同的性别,而是产生单个默认翻译。接下来是一个审查过程,以发现性别中立的查询导致特定性别翻译的实例。如果是这种情况,最初的翻译是用异性重写的。最后,为了确保准确性,将两种翻译进行相互比较,以确保唯一的差异是性别。
偏差减少
在设计新方法的过程中,谷歌还定义了一个新的指标,偏差减少,来衡量新系统相对于旧系统的改进。这是偏差减少的百分比,所以如果一个系统在 80%的时间里做出错误的特定性别选择,而新系统在 20%的时间里做出错误选择,偏差减少将是 75%。
根据谷歌博客的说法,在从土耳其语翻译成英语时,基于新重写器的系统的偏差减少了 95%,高于使用基于分类器的系统时的 60%。他们的性别翻译的精确度也是 97%,这意味着除了 3%的情况外,他们在所有情况下都正确显示了性别翻译。
结论
尽管谷歌不得不接受 2018 年 99%的数字,但它的新数字似乎更保守,也更有分寸。我们也希望他们能吸取教训,在发布指标时更加小心。但在现实中,谷歌将不得不等待,看看有多少愤怒的社交媒体帖子出现偏见谷歌翻译查询的截图!
与此同时,像谷歌这样的公司需要继续解决他们产品中不同的偏见问题。然而,他们不仅应该从过程的机器学习端解决这些问题,还应该考虑如何更好地收集数据,以避免一开始就出现偏差。
通用 Git 工作流程
使用 Git 时最常用的命令
Git 是最常用的代码版本跟踪工具,可能每个开发人员都使用它,无论你使用 Github、Gitlab 还是 Bitbucket,只要你想分享你的代码或学习他人的代码,或与他人合作开发一个项目,git 都是必备的能力。
那里有很多 git 使用教程、命令介绍文章和文档。但坦率地说,当我查看大多数官方文档时,我被太多的命令和太详细的介绍填满了,这使我很难找到我真正需要的东西。因此,在本文中,我将只介绍您在使用 git 时肯定会用到的命令行工具。
我将分为两个工作流程:
- 使用现有存储库
- 创建自己的存储库
使用现有存储库
当你在一家公司工作时,这是最常见的,在大多数情况下,你需要从其他人那里接管一个项目,或者只是在一个大型项目上与其他开发人员合作。一般的工作流程是:
给自己找一个工作文件夹
cd {YOUR_FOLDER}
克隆项目(使用 SSH 或 HTTPS)
git clone {PROJECT_PATH}
现在项目已经下载到你的本地计算机上,你就在主分支了。下一步是创建您自己的分支,并在该分支上开发您的特性。如果你不是项目的所有者或者你和许多其他开发人员一起工作,千万不要直接在主分支上进行变更。经验法则是,我们总是把母版制作得干净整洁,直到可以投入生产。
git checkout -b {YOUR_BRANCH_NAME}
如果你已经有一个分支,需要转移到那个分支,你只需要
git checkout {YOUR_EXISTING_BRANCH}
好了,现在你在你的分支上,你做了一些改变。一切看起来都很好,您认为您已经准备好向 master 更新您的更改了。然后,您需要首先将您的更改提交到本地:
# check what files you've changed
git status# add those files to staging
git add .# commit your files
git commit -m "what I have done"
这三个命令可能是 git 最常用的命令。除了添加所有你修改过的文件的git add .
,你还能做什么
git add {LIST OF SPECIFIC FILES}
并分别提交。
提示
运行 *git status*
后,您发现一些您不打算添加到提交中的系统文件,如 *.idea, __cache__*
等。然后你需要编辑你的 *.gitignore*
文件(或者如果根文件夹中不存在就直接添加)。
*.gitignore*
顾名思义就是告诉 git 故意忽略一些不必要的文件,这样 git 就会跟踪这些文件的变化。例如,您可以添加
*.idea
*/**/.idea*
变成你的 *.gitignore*
。
你可以做的另一件事是使用 *git rm*
,它从 git 中移除文件并停止跟踪它,而不是从你的电脑中!
现在您已经提交了您的文件,并且您对它们很有信心,所以让我们推进到远程并合并到主文件。
git push origin {YOUR_BRANCH_NAME}
在大多数情况下,你不能做git push origin master
,因为你的授权是受限的。但即使可以,也不应该这么做。这里的规则是,如果你在一个分支上工作,总是推到远程分支。上面的命令将您的本地分支推送到远程分支(如果远程分支不存在,git 将帮助您创建一个)。
当您执行上述命令时,通常它会在控制台中返回一个 url,引导您提交一个合并请求。请注意,这个合并请求是将您的远程分支合并到远程主服务器。您可以提交您的合并请求,并让回购所有者批准合并到主(可能需要单独解决冲突的方式)。
提示
假设远程主机非常活跃,并且有许多其他开发人员正在开发它。在提交合并请求之前,您希望将新的更改合并到远程主分支中,并可能解决本地主分支中的冲突。那么你可以这样做:
*# go to your local master
git checkout master**# merge your local master with remote master
git pull origin master**# go to your branch
git checkout {YOUR_BRANCH}**# merge master into your branch
git rebase master*
最后一步你还可以做 *git merge master*
。他们以不同的方式绑定了 git 提交的历史。你可以在这里 查看 的区别。
这些基本上都是关于与他人一起工作,使用最常用的命令,以及您可能遇到的问题。
创建自己的存储库
想想当你在笔记本电脑上做自己的项目时,突然它在你的脑海中闪现——这是一个多么棒的项目!我要和别人分享!
酷,让我们创建自己的回购协议,并把我们的代码放在网上。它分为两步:
创建远程存储库
转到您的 Github、GitLab 或 Bitbucket,并选择 create a new repository。这里我以 Github 为例:
给你的项目一个名字和一些描述,然后点击Create repository
按钮。
将克隆按钮中的 URL(SSH 或 HTTPS)复制到剪贴板,我们稍后会用到它。
现在回到你的牛逼项目文件夹根目录
cd {YOUR_PROJECT_ROOT}
用 git 初始化你的项目
git init
这会在你的文件夹中启动 git,如果你运行ls -a
,你将会在你的根文件夹中看到隐藏的文件.git
,它告诉你从现在开始,git 将会跟踪这个文件夹中的每一个变化。
接下来是将您的本地文件连接到您的远程 repo,如果您运行
git remote -v
这将列出您当前项目的远程 url,到目前为止它应该是空的,因为您创建了一个远程 repo,您还告诉 git 跟踪您的文件,但是您还没有告诉 git 如何将您的本地 repo 连接到远程。为此,请运行:
git remote add origin {URL_OF_YOUR_REPO}
这将您的本地项目与您刚刚创建的远程 repo 连接起来,并给它一个昵称origin
(您可以随意命名,但人们通常将其命名为 origin)。
提示
一个本地 repo 实际上可以连接多个不同 URL 的远程 repo。你只要做:
*git remote add upstream1 {SECOND_URL}**git remote add upstream2 {THIRD_URL}**...*
这样,当您在本地进行更改时,您可以同时更新多个远程回购。
现在再次运行git remote -v
,你会看到你的网址列在那里。让我们推出改变,分享我们的代码!
添加和提交代码的步骤是一样的:
# check what files you've changed
git status# add those files to staging
git add .# commit your files
git commit -m "what I have done"
要将您的更改推送到遥控器,有两种情况。
- 如果遥控器有一些文件不包含在你的本地,比方说,一个
README.md
的创建回购初始化,你需要:
git pull origin master --allow-unrelated-histories# resolve conflicts and then push to remotegit push origin master
allow-unrelated-histories
是在分支独立开始时合并它们,这里就像你的本地文件夹和你的远程 repo。
2.如果你的遥控器是干净的,那么就做git push origin master
。
提示
你不需要每次都输入回购名称。运行
*git push --set-upstream origin master*
这明确告诉我们 *origin*
将会被默认分支推送到,这样下次你只需要运行 *git push*
。
提示
当您在本地有多个分支,并且您需要在分支之间跳转时,您可能会遇到 git 告诉您在切换分支之前提交的问题,但是您不确定当前的更改,并且您不想提交,在这种情况下,您可以:
*git stash*
这将保存您的更改,并使您能够跳转到另一个分支而无需提交它们。当你回到这个分支时,你可以运行:
*git stash pop*
恢复您刚才所做的更改。
处理熊猫丢失数据的一般准则
图片由 Willi Heidelbach 来自 Pixabay
缺失值在现实生活的数据集中很常见。需要正确处理它们,以便进一步处理和正确解释数据。
作为一名成瘾科学家,我的研究包括从临床试验的社区参与者那里收集数据。一些参与者可能会跳过临床访视,因此数据可能会在整个访视中丢失。有时,参与者可以跳过某个问卷的一些问题,导致部分数据缺失。参与者在需要整数值的地方输入不相关的自由文本的情况也时有发生。由于这些不同的原因,临床数据中缺失值是很常见的。
因此,处理缺失值是处理临床数据的常规任务,这也适用于许多其他真实数据。在本文中,通过一个假设的例子,我将向您展示我们应该如何处理 Pandas 中丢失的数据 Pandas 是我最喜欢的数据处理 Python 包之一。出于本教程的目的,我在 GitHub 上创建了一个假想的数据集。当然,实际的临床数据或其他现实生活中的数据可能更混乱,因此也更复杂,但同样的原则也适用。
第一步。了解您的数据
基本原理:了解数据大小和数据类型的有效性(例如,预期的数据类型和范围)。
第一步是理解你正在处理的数据。例如,假设我们的数据是对调查的回答的集合,包括下面左面板中显示的四个问题。在右边的面板中,我们将数据作为熊猫的数据框导入,并快速查看数据框。我们知道数据帧有 20 行和 5 列,subject_id 是整数,而其余的字符串。
当然,在实际的数据处理中,我通常会运行一些分类变量的频率表和数值变量的单变量分析,以更深入地了解我们的数据。这些超出了本文的范围,我们可以在以后的单独文章中讨论它们。
假设调查数据
第二步。记录数据
理由:我们需要检查我们所有的数据是否有效。为了方便起见,我们将重新编码我们的数据。但是,如果某些值无效,我们将把它们重新编码为缺失。
对于这四个问题,我们希望 q1-q4 的回答遵循下面的验证规则。为了方便起见,我还列出了我们将为现有列重新编码的数据类型。
数据验证规则
因为我们确切地知道数据应该是什么样子,所以我们可以开发详细的验证规则,并在验证和重新编码过程中将这些规则应用到每一列。
下面的代码向您展示了我们将如何做。为清楚起见,这四列(即 q1-q4)中的每一列都单独重新编码。请注意,我们现在仍然希望保留旧的列,这样我们可以将它们与重新编码的列进行比较,以确保数据被正确地重新编码。
数据记录
第三步。检查丢失的数据
基本原理:在我们对缺失数据做任何事情之前,我们需要找出我们有多少缺失数据。
一旦我们完成了数据记录,我们就可以检查我们的数据集中有多少缺失的数据。因为我们现在只对重新编码的列感兴趣,所以我们可以简单地删除旧列,只检查新列的数据缺失。我们注意到的另一件事是,每条记录对于一个主题是唯一的,因此我们可以在删除旧列后使用 subject_id 作为索引创建一个新的数据框。
检查数据缺失
如上图所示,我们可以使用isnull()
来评估每个单元格是否缺失(True
)或不缺失(False
)。由于真值被评估为 1,当我们使用sum()
函数时,可以显示每一列缺失值的数量。为了找出总的缺失数,我们只需运行df_r.isnull().sum().sum()
,在我们的例子中,我们有 21 个缺失值。
与这个主题相关的是另一个方法isna()
,它做的事情和isnull()
一样。如果要检查非缺失数据,可以使用notna()
或notnull()
方法。
第四步。处理缺失值
基本原理:需要显式处理缺失值,以避免数据集的错误解释。
缺失数据的处理方式由几个因素决定,例如缺失的性质(例如,随机缺失或非随机缺失)和潜在的预期措施—生物、心理或物理。换句话说,缺失是在数据的大背景下逐案处理的。本节将向您展示一些处理缺失数据的常用方法。
删除缺失值的数据记录
第一种方法是删除缺少值的数据记录。下面的代码片段向您展示了我们如何使用dropna()
方法来删除丢失数据的行。当然,我们可以在列上做类似的操作,但是我们通常不这么做,因为列在大多数时候是研究兴趣的变量。下面使用的每种方法都有其预期应用的简要描述。
删除缺少值的记录
使用dropna()
方法的几个要点:
thresh
参数指定了指定轴的每个系列应该具有的非缺失值的最小数量。- 您可以使用
'any’
和‘all’
作为how
参数,这将删除丢失部分或全部值的记录。 - 您可以删除指定轴、行或列上的缺失值。
- 除非方法调用指定了
inplace=True
,否则dropna()
方法不会改变原始数据帧。
用缺失值填充数据记录
另一种方法是用一些值来填充缺失的值。为此,我们可以使用fillna()
方法。与dropna()
方法类似,您可以在两个轴上使用fillna()
。下面的代码片段提供了一些常见的用例。
用缺失值填充记录
使用fillna()
方法的几个要点:
limit
参数指定要填充的最大记录数。当设置了method
参数时,该限制适用于上一个和下一个可用有效值之间的间隔,当未设置该参数时,该限制适用于列。- 您可以使用
'ffill'
和'bfill'
作为method
参数,它们分别使用最后一个或下一个有效值来填补空白。 - 您可以填充指定轴、行或列上缺少的值。
- 除非方法调用指定了
inplace=True
,否则fillna()
方法不会改变原始数据帧。
外卖
处理缺失值并不像某些人想象的那么简单。一些研究人员经常忽略的部分实际上是我们数据处理的最早步骤——从内容角度理解我们数据的含义。例如,对于我自己作为一名成瘾科学家,我总是检查我的数据集对应哪些问题,基于这些问题,我可以正确处理缺失的数据。
因此,我不能再强调这一点,但最大的收获是在彻底理解数据内容含义的情况下处理数据。
数据科学和分析领域的通才与专家
在数据科学和分析社区,专家比通才更受青睐,这就是现实。我们固有地认为,更加专业化是保证在某个职位或业务成果上取得成功的必由之路。可惜没那么简单。虽然专家们在再造他们擅长的工作方面表现出色,但有时他们很难在规则没有很好定义的未知领域航行。具有讽刺意味的是,许多商业角色,尤其是小而灵活的公司中的角色,恰恰需要这样一种技能——处理模糊性、不确定性和缺乏明确规则的能力。多面手的闪光点就在于模糊性——他们兴趣广泛,可能从事过许多不同类型的工作和行业。但是,虽然他们的职业道路在纸面上看起来似乎很混乱,但事实上,它提供了大量可以用来解决问题的经验——这是数据科学和分析的面包和黄油。
让目标成为目标
参加过数据科学会议吗?我去过几个,我在每个地方观察到的重复模式是过度强调案例研究、解决方案和供应商,它们解决的问题只适用于前 5%的精英企业。这种模式反映在关于数据科学和数据工程实践的文献和思想领导中,并随后渗透到构建数据团队的几乎每一个组织策略中(以及此后的每一个工作列表)。不幸的是,我称之为“专家”的工作模型(这不是数据科学领域独有的,尽管可能更明显)虽然有时在超大型企业中有效或强制要求,但通常无法解决其他所有人(其他 95%的公司)的数据项目的底层需求,即向最终用户提供价值/行动/选择,或者换句话说,将一组技术交付成果与解决客户需求的解决方案联系起来(内部业务用户或面向外部的客户,同样的原则适用)。
数据驱动的解决方案开发周期
在上图中,我展示了我认为数据科学和分析团队开发业务解决方案的普遍适用周期。每个虚线框代表实现构建完整解决方案的最终目标所需的不同技能。在“专家”模式中,组织将雇佣一个人或一组人来完成每个发展阶段的任务。
“专家”工作模式
在大型组织中(前 5%)——这种可以起作用——通常大型公司有资源为开发的每个阶段有效地雇用超级专家,以及雇用和/或安排监督整个过程的领导层。注意我对 can、的强调。通常,交付解决方案所涉及的团队、部门和个人的数量会给过程带来不必要的复杂性和官僚主义,这可能会停止或完全搁置项目。在“专家”模型中,由于缺乏跨职能领域的知识或经验,不必要的复杂性也可能被无意地引入。一个很好的例子是业务部门和数据科学团队之间经常出现的脱节,因为它涉及到业务目标和建模响应变量。有时,数据科学团队花费数周时间构建一个旨在预测特定目标变量的模型,却发现该目标与业务实际试图实现的结果不匹配。
在较小的公司(其他 95%的业务),“专家”模式很少奏效。
在较小的组织中,雇佣专家的需要使得开发周期的重要部分暴露出来。
雇用专家来满足每一个独特需求的常见方法导致那些预算庞大的非大型企业在开发周期中缺少开发元素。为了解决这一问题,许多组织创建了不合理的职位列表,以寻找一直难以捉摸的数据科学“独角兽”。数据科学界的每个人都知道这种现象,并一直对此感到羞耻,但当你真正思考公司为什么这样做时,这完全有道理。任何地方的人力资源组织都采用专家招聘模式来填补几乎任何职位——这就是我们做事的方式。常识告诉我们,专业化等同于成功。当完成一个成功的数据科学项目需要如此多的组成部分,而预算有限时,人力资源团队会寻找能够填补每个空缺的候选人。众所周知,这种类型的候选人少之又少,而且很可能已经找到了加入团队的方法,或者已经在精英企业的简历中名列前茅。因此,公司最终会聘用一名专家,这位专家在其特定领域具有非凡的才华和丰富的经验,并期望他们神奇地将自己的技能扩展到与其超级专业无关的许多其他挑战上——结果往往很差(Epstein,Range,第 212 页)。这与候选人之前的成就无关,也与他或她的能力和资历无关。人力资源组织——以及许多招聘经理——不知道任何其他方法。他们生活的规则是过分强调经验的深度胜过所有其他因素。这种关注通常与被雇佣的数据角色的实际期望结果不匹配。当我们需要找到一个拥有某个领域最专业技能的候选人时,我们忽略了目标——解决独特的业务问题。将目标作为目标——如果你需要某人花 100%的时间为零售业务做时间序列预测或倾向建模,那么你可以雇佣一个在整个职业生涯中都在高水平做这些事情的人(即重现他们之前已经完成的业绩)。然而,在一家小公司,很少有人会把他们的每一秒钟都花在做一件单一的任务上,不可避免地,这个人会被要求扩展他们的高度专业化,以解决跨几个领域的挑战,这就是“专家”模式的不足之处。
多面手
这里有一个激进的想法:我建议 80%的情况下,你应该雇佣多面手而不是专家。这并不是说多面手比专家更好,只是根据我的个人经验,在我工作过的许多公司中,多面手通常对我遇到的大约 80%的案例更有用。什么是通才?我将它定义为在广泛的技能组合和专业领域具有经验的人,并且在一个或多个领域具有足够的(最低要求的)深度来履行工作的基本职能。从某种程度上来说,通才牺牲了职业生涯的深度,因为他们跨越了几个不同的角色、部门或工作类型,甚至可能超出了数据科学和分析的范畴,但作为回报,他们创造了一种独特的技能组合,使他们能够在工作中建立专家很难做到的联系。这种技能组合对于解决没有直接解决方案的实际问题特别有用。
“通才”模式
我将称之为“通才”模型,它并没有不必要的强调专业化,而是寻求将被雇佣的问题的性质与每个候选人的独特经历恰当地联系起来。因此,如果所寻求的结果是一套非常明确的规则,目标是重新创造一个非常具体的输出,候选人已经证明他/她已经掌握(像外科医生或职业高尔夫球手),那么你就要找专家。然而,如果这份工作的性质更加开放,新的方法不断被创造,新的规则不断被起草(“现实世界”的复杂性增加),那么你就要寻找多面手。
例如,你想在你的公司做“人工智能和机器学习”?太好了!不幸的是,你可能甚至没有资格讨论人工智能。首先,你需要有人可以帮助创建数据架构、管道和系统,这样你就有了可以开始查看的数据集。然后你可以开始思考简单的“如果-那么”场景,这些场景最终会导致“人工智能”从能为一个项目打下基础的多面手开始,然后,很可能是更久以后,雇佣能在业务需要时(且仅在业务需要时)增加单个组件深度的专家。
您是否希望为您的营销计划提供自动化仪表板、报告和统计数据?太好了!首先,你需要一个可以大规模支持你的项目的营销数据库架构。你可能需要一个有实际营销经验的人,但是他也有足够的技术知识来构建一个数据架构来支持你的目标。不要雇佣一个谷歌分析专家并期望他们建立一个程序,他们期望有数据来处理——而你没有。那么你的候选人就会很沮丧,你也得不到你想要的。这就是拥抱丰富经验的美妙之处,而不是将其视为负面因素(很多人都这么认为)——员工和候选人最终会更快乐、更有成就感,同时企业也会得到它想要的结果和成果。
在“多面手”模型中,你通过拥抱模糊性和“现实生活”的复杂性,而不是期望你的超级专家只是处理它,来定位个人和团队的成功。这在敏捷的组织中尤其重要,在这种组织中,变化经常发生,新的“东西”必须在运行中构建——在有很大程度的结构并且不需要变化的情况下就不那么重要了。
改变你的视角
虽然“多面手”的想法看起来很激进,但我在这篇文章中列出的一切都是有研究支持的,其中大部分已经在大卫·爱泼斯坦的书 Range 中揭示出来(阅读!).与普遍的看法相反,让一大群“专家”去解决问题并不是解决问题的最佳方式——最佳和最具创新性的解决问题的方法来自于拥有来自多个领域的广泛知识的个人。横向联系的经验,再加上足够的技术知识,是创建新的解决方案的最佳组合,这些解决方案可以解决真实的、实际的业务问题。
外卖:
- 与普遍的看法相反,跨许多领域的广泛经验并不会让你成为众所周知的“万金油”,相反,会提高你建立别人看不到的联系的能力,并提出别人想不到的解决方案。
- 在适当的情况下,超级专家非常重要,非常有价值。需要高技术重复表现的工作适合超级专家,对多面手来说可能不是个好选择。让目标成为目标,按照“通才”模式招聘。
- 大公司雇佣专家可能比小公司受益更多。这是因为大型企业已经扩大了他们的业务,以至于他们经常有多个个人在业务的极其有限的部分工作。也就是说,如果公司的目标是创造新的东西,或者以一种新的方式将系统结合在一起,多面手可能更适合。
- 如果你正在开始——不要决定成为多面手或专家——弄清楚你个人擅长什么,然后打造你自己独特的道路。如果你喜欢深入研究一两个主题,或者想成为某个主题的专家,你可能会喜欢成为专家,应该寻找能迎合你难以置信的专注技术能力的工作。如果你兴趣广泛,或者很快就厌倦了,那么你可能是个多面手。掌握足够的技术,以满足技术面试的最低要求,但又足够宽泛,以构建解决方案(例如,从头构建一个商业智能程序,或创建一个端到端的机器学习管道,或构建一个从多个来源获取数据的网络应用)。此外,如果你是一个多面手,花时间获取知识和技能,补充你的分析专业(有点像“辅修专业”)。商业、金融、营销、设计和产品是分析专家的几个重要倍增因素。
有问题吗?你可以给我发电子邮件,地址是 cwarren@stitcher.tech,或者在 Linkedin 上关注我,地址是 https://www.linkedin.com/in/cameronwarren/
广义最小二乘法(GLS):与 OLS 和 WLS 的关系
通过数学推导和直觉
背景和动机
对于任何从事统计学或机器学习研究的人来说,普通最小二乘(OLS)线性回归是第一个也是最“简单”的方法之一。但是,有一组数学限制,在这些限制下,OLS 估计量是最佳线性无偏估计量(BLUE),即具有最小采样方差的无偏估计量。 ( )关于 OLS、蓝色和高斯-马尔科夫定理的更全面的概述,请参见我以前关于这个主题的文章 )
如果 OLS 是蓝色的数学假设不成立呢?下一个“飞跃”是广义最小二乘(GLS) ,其中的 OLS 实际上是一个特例。这篇文章是对 GLS 的介绍,包括以下主题:
- 回顾 OLS 估计量及其为蓝色所需的条件
- 广义最小二乘法的数学设置(GLS)
- 恢复 GLS 估计量
- GLS 估计量无偏的证明
- 恢复 GLS 估计量的方差
- 浅谈加权最小二乘(WLS)的关系
请注意,在本文中,我是从频率主义范式(与贝叶斯范式相反)出发的,主要是为了方便起见。所以,让我们开始吧:
1.OLS 估计量及其为蓝色的条件评述
让我们先快速回顾一下 OLS 估计量
作者图片
作者图片
关于 OLS 估计量的更多信息和它的无偏性的证明,请参见我以前关于这个主题的文章。
2.广义最小二乘法的数学设置(GLS)
作者图片
3.恢复 GLS 估计量
作者图片
4.GLS 估计量无偏的证明
作者图片
5.恢复 GLS 估计量的方差
作者图片
6.浅谈加权最小二乘(WLS)的关系
作者图片
总结、最终想法和后续步骤
广义最小二乘(GLS)是一个大话题。这篇文章是一个简短的介绍,旨在从数学上为 GLS“设置场景”。还有更多的内容需要介绍,包括(但不限于):
- 对 WLS 更深入的探究
- 指定协方差矩阵 V 的方法和途径
- 可行广义最小二乘的主题
- 与迭代加权最小二乘法(IRLS)的关系
我计划在以后的文章中深入讨论这些主题。
希望以上有见地。正如我在以前的一些文章中提到的,我认为没有足够的人花时间去做这些类型的练习。对我来说,这种基于理论的洞察力让我在实践中更容易使用方法。我个人的目标是鼓励该领域的其他人采取类似的方法。我打算在未来写一些基础作品,所以请随时在【LinkedIn】和 上与我联系,并在 Medium 上关注我的更新!
广义线性模型
它们是什么?我们为什么需要它们?
广义线性模型(GLMs)诞生于将各种各样的回归模型纳入一个保护伞之下的愿望,这些模型涵盖了从用于实值数据的经典线性回归模型到基于计数的数据模型(如 Logit、Probit 和 Poisson)再到生存分析模型的范围。
GLM 伞下的模特
GLMs 为您提供了一种通用的方法,使用一个通用的过程来指定和训练以下类别的模型:
- 经典线性回归(CLR)模型,俗称实值(和潜在负值)数据集的线性回归模型。
- 方差分析模型。
- 计数比率模型。例如预测获胜几率、机器故障概率等的模型。这类模型的一些例子是 Logit 模型(用于逻辑回归)、概率单位和有序概率单位模型,以及非常强大的二项式回归模型。
- 用于解释(和预测)事件计数的模型。例如,用于预测超市、购物中心、急诊室的客流量的模型。这类模型的例子有泊松和负二项式回归模型,以及栅栏模型。
- 预测零件、机器(和人类)下次故障时间的模型。估算生物(和非生物)寿命的模型。
当上述看似不同的回归模型中的每一个都以广义线性模型的格式表达时(我们将很快解释这种格式是什么),它给建模者带来了为所有这样的模型应用公共训练技术的巨大好处。
对于广义线性模型,人们使用一种通用的训练技术来训练一组不同的回归模型。
此外,GLMs 允许建模者以线性和加法方式 表达回归变量(又称为协变量、又称为影响变量、又称为解释变量) X 和响应变量(又称为因变量) y 、之间的关系,即使基础关系可能既不是线性的也不是加法的。
广义线性模型让您以线性、相加的方式表达协变量 X 和响应 y 之间的关系。
与经典线性回归模型的关系
说到线性和可加性,线性回归模型是一种简单而强大的模型,可成功用于建模线性、可加性关系,如下所示:
回归变量 x_1,x_2,x_3,…x_p 是加性相关的(图片由作者
CLR 模型通常是“首选模型”:在为自己的问题选择复杂模型之前,应该仔细比较复杂模型。
CLR 模型具有明显的优势:
在满足某些条件的情况下,它们有一个简洁的“封闭形式”解,这意味着,它们可以通过简单地求解线性代数方程来拟合,即根据数据进行训练。
解释训练模型的系数也很容易。例如,如果训练的 CLR 模型由以下等式表示:
拟合的线性回归模型(图片由作者提供)
从这个等式中可以清楚地看出这个模型已经发现了什么:露营者人数每增加一个单位,捕获的鱼的数量就会增加 75%左右,而露营小组的儿童人数每增加一个单位,该小组捕获的鱼的数量就会减少相同的数量!此外,在捕捉到任何鱼之前,需要一个至少 3 人的野营小组(=roundup(2.49))。
但是经典的线性回归模型也有一些严格的要求,即:
- 加性关系:经典线性模型假设回归变量之间应该存在加性关系。
乘法和加法关系(图片由作者提供)
- 同方差数据:经典线性模型假设数据应该具有恒定方差,即数据应该是同方差的。在现实生活中,数据往往是而不是同质的。方差不是常数,有时它是平均值的函数。例如,方差随着平均值的增加而增加。这在货币数据集中很常见。
同方差和异方差数据(图片由作者提供)
- 正态分布误差:经典线性模型假设回归误差(也称为残差)正态分布,均值为零。这个条件在现实生活中也很难满足。
- 不相关变量:最后,假设回归变量之间不相关,最好是相互独立的。
因此,如果您的数据集是非线性的、异方差的并且残差不是正态分布的,这在现实世界的数据集中是常见的情况,那么需要对【y】和【X】进行适当的变换,以便使关系呈线性,同时稳定方差并使误差正常化。
平方根和对数变换通常用于实现这些效果,如下所示:
对数将乘法关系转换为加法关系(图片由作者提供)
不幸的是,没有一种可用的变换能同时达到所有三种效果,即使关系线性、使异方差最小化和使误差分布正常化。
转换方法还有另一个大问题,如下所示:
回想一下 y 是一个随机变量,它遵循某种概率分布。所以对于数据集中任何给定的 x 值的组合,现实世界很可能呈现给你几个 y 的随机值,而这些可能值中只有一部分会出现在你的训练样本中。在现实世界中, y 的这些值将围绕 y 的条件均值随机分布,给定x 的具体值,y 的条件均值由E(y|x)表示这种情况可以说明如下:**
条件期望 E( y|x) 如红点所示(图片由作者)
在变量转换方法中,我们做了一个不切实际的假设,即在使用 log()、sqrt() 等进行转换后, y 的每个单个值,即上面图中的每个蓝点。,最终会和 X 成线性关系。这显然是过高的期望。
似乎更现实的是,【y】的条件均值(又名期望),即E(y|X,)经过适当的变换,应该与 X 成线性关系。**
换句话说:
条件期望的对数,表示为协变量的线性组合(图片由作者提供)
广义线性模型做出了上述至关重要的假设,即转换后的条件期望 y 是回归变量 X 的线性组合。
该转换函数被称为 GLM 的链接函数,并由 g(.)
我们举例说明 g() 的动作如下:
广义线性模型的链接函数(图片由作者提供)
因此,GLMs 不是为每个转换 y 的每个单个值,而是只为每个 x 转换 y 的条件期望。所以不需要假设 y 的每一个单值都可以表示为回归变量的线性组合。**
在广义线性模型中,将因变量 y 的转换条件期望表示为回归变量 X 的线性组合。
链接功能 g(。)可以采取多种形式,我们根据什么形式得到不同的回归模型 g(。)不紧不慢。下面是一些流行的形式和它们导致的相应回归模型:
线性回归模型
在线性模型中, g(。)是下面的 身份 功能:
线性回归模型使用的身份函数(图片由作者)
逻辑回归模型(通常是二项式回归模型)
在逻辑回归模型中, g(。)是下面的 Logit 功能:
逻辑回归模型使用的 Logit(对数优势)函数(图片由作者提供)
泊松回归模型
泊松回归模型使用以下函数:
泊松回归模型使用的对数函数(图片由作者提供)
g 还有很多其他的变体(。)如泊松-伽马混合导致负二项式回归模型和正态分布的累积分布函数的逆,从而导致 probit 模型。
如何处理数据中的异方差?
最后,让我们看看 GLMs 如何处理异方差数据,即方差不恒定的数据,以及 GLMs 如何处理潜在的非正态残差。
glm 通过假设方差是均值的某个函数 V( ) ,或者更准确地说是条件均值|X = X来说明非恒定方差的可能性。
在上面提到的每个模型中,我们假设一个合适的方差函数V(|X=X)。
在广义线性模型中,将数据中的方差表示为平均值 的合适函数。
在线性回归模型中,我们假设 V( ) =某常数 ,即方差为常数。为什么?因为线性模型假设 y 是正态分布,一个正态分布有一个恒定的方差。
****Related Post:** [Robust Linear Regression Models for Nonlinear, Heteroscedastic data](/robust-linear-regression-models-for-nonlinear-heteroscedastic-data-14b1a87c1952)**
在 Logistic 和二项式回归模型 s 中,我们假设, V( ) = — /n 为一个数据集大小为 n 的样本,为一个 Logit 所要求分布的 y 值。
****Related Post:** [The Binomial Regression Model: Everything you need to know](/the-binomial-regression-model-everything-you-need-to-know-5216f1a483d3)**
在泊松回归模型中,我们假设 V( ) =。 这是因为,泊松回归模型假设y 具有泊松分布,在泊松分布中,方差=均值。
****Related Post:** [An Illustrated Guide to the Poisson Regression Model](/an-illustrated-guide-to-the-poisson-regression-model-50cccba15958)**
在负二项回归模型中,我们假设V()=+α*,其中 α 是一个分散参数,它允许我们处理过度分散或分散不足的数据。
****Related Post:** [Negative Binomial Regression Model: A Step by Step Guide](/negative-binomial-regression-f99031bb25b4)**
…其他型号也是如此。
在 GLMs 中,可以证明模型对残差的分布形式不敏感。简而言之,模型不关心模型的误差是正态分布还是以任何其他方式分布,只要您假设的均值-方差关系实际上满足您的数据。
广义线性模型不关心残差是否正态分布,只要数据满足指定的均值-方差关系。
这使得 GLMs 成为许多真实世界数据集的实际选择,这些数据集是非线性和异方差的,并且我们不能假设模型的误差总是正态分布的。
最后,提醒一句:与经典的线性回归模型类似,GLMs 也假设回归变量彼此不相关。因此,GLMs 不能用于建模通常包含大量自相关观测值的时间序列数据。
广义线性模型不应用于模拟自相关时间序列数据。
摘要
广义线性模型将各种不同的回归模型(如经典线性模型、各种数据计数模型和生存模型)汇集在一个估计伞下。
以下是关于 GLMs 需要记住的一些事情的概要:
- GLMs 巧妙地避开了经典线性模型的几个强要求,如效应的可加性、数据的同方差性和残差的正态性。
- GLMs 在 GLM 家族的所有模型上强加了一个通用的函数形式,它由一个链接函数g(|X=X)组成,允许您将因变量的转换条件均值表示为回归变量的线性组合。**
- GLMs 需要指定合适的方差函数V(|X=X),用于将数据中的条件方差表示为条件均值的函数。什么形式 V(。)取值取决于数据集中因变量 y 的概率分布。**
- glm 并不关心误差项的分布形式,因此对于许多真实世界的数据集来说,glm 是一个实用的选择。
- GLMs 确实假设回归变量 X 是不相关的,从而使得 GLMs 不适于对自相关时间序列数据建模。
快乐造型!
感谢阅读!我写关于数据科学的主题,重点是回归和时间序列分析和预测。
如果你喜欢这篇文章,请关注我的Sachin Date获取回归和时间序列分析方面的技巧、操作方法和编程建议。**
R 中的广义对图
用关联矩阵可视化您的数据
Artur Tumasjan 在 Unsplash 上拍摄的照片
您的创新想法可能来自对您的数据的详细探索。大多数情况下,数据将包含连续变量和分类变量。你需要从中找到可以解释的模式。如果它可以在一个图表中有条理地显示出来,并且您可以用简单而简短的代码行来实现它,那会怎么样?
为什么是 R?
R 是统计计算 R 基金会支持的统计计算和图形开源编程语言。使用其广泛使用的集成开发环境- RStudio 既易学又舒适。
安装软件包
首先,我们需要用下面的代码安装所需的包。
**install.packages("tidyverse")
install.packages("GGally")
install.packages("ISLR")**
‘tidy verse’包用于数据争论&数据可视化。广泛使用的‘gg plot 2’封装在‘tidy verse’中。 GGally 通过在【gg plot 2】中增加几个函数,降低了将几何对象与转换数据结合的复杂性。在本练习中,我们将使用大西洋中部工资数据 a,其中包含美国大西洋中部地区 3000 名男性工人的工资数据。“ISLR”加载此数据集需要包。
加载包
这些包需要安装一次,但是每次运行脚本时,你都需要用**library**
函数加载这些包。
**library(tidyverse)
library(GGally)
library(ISLR)**
浏览数据集
我们将使用**data**
函数加载数据集,并使用**glimpse**
和**head**
函数探索数据集。
**data(Wage)****glimpse(Wage)
head(Wage)**
作者图片
作者图片
让我们只选择**【年龄】【学历】【工作阶级】* & 【工资】几列来进行我们的工作。*
***Wage2 <- Wage %>%
subset(select = c("age", "education", "jobclass", "wage"))****glimpse(Wage2)
head(Wage2)***
作者图片
我们可以看到“年龄”和“工资”变量(列)是连续的(<int>, <dbl>
),“教育程度”&,“工作阶层”变量(列)是分类的(<fact>
)。
不同的绘图选项
我们这个练习的目的是看看“工资”是如何随着“年龄”、“教育程度”和“工作阶层”而变化的。让我们用 ggplot2 包的**ggplot**
&各自的 geom 功能尝试不同的绘图选项。
散点图
***# P1
ggplot(Wage2) +
geom_point(aes(x = age, y = wage,
color = education), size = 4) +
labs(x = 'AGE', y = "WAGE") +
ggtitle("wage ~ age, education ") +** **theme_bw() +
theme(axis.text.x = element_text(face = 'bold', size = 10),
axis.text.y = element_text(face = 'bold', size = 10))****# P2
ggplot(Wage2) +
geom_point(aes(x = age, y = wage,
color = jobclass), size = 4) +
labs(x = 'AGE', y = "WAGE") +
ggtitle("wage ~ age, jobclass ") +** **theme_bw() +
theme(axis.text.x = element_text(face = 'bold', size = 10),
axis.text.y = element_text(face = 'bold', size = 10))***
作者图片
作者图片
直方图
***# P3
ggplot(Wage2) +
geom_histogram(aes(x = wage,
fill = education), color = 'lightblue') +
labs(x = 'WAGE') +
ggtitle("wage ~ education ") +
theme_bw() +
theme(axis.text.x = element_text(face = 'bold', size = 10),
axis.text.y = element_text(face = 'bold', size = 10))****# P4
ggplot(Wage2) +
geom_histogram(aes(x = wage,
fill = jobclass), color = 'lightblue') +
labs(x = 'WAGE') +
ggtitle("wage ~ jobclass ") +
theme_bw() +
theme(axis.text.x = element_text(face = 'bold', size = 10),
axis.text.y = element_text(face = 'bold', size = 10))***
作者图片
作者图片
密度图
***# P5
ggplot(Wage2) +
geom_density(aes(x = wage,
fill = education)) +
labs(x = 'WAGE') +
ggtitle("wage ~ education ") +
theme_bw() +
theme(axis.text.x = element_text(face = 'bold', size = 10),
axis.text.y = element_text(face = 'bold', size = 10))****# P6
ggplot(Wage2) +
geom_density(aes(x = wage,
fill = jobclass)) +
labs(x = 'WAGE') +
ggtitle("wage ~ jobclass ") +
theme_bw() +
theme(axis.text.x = element_text(face = 'bold', size = 10),
axis.text.y = element_text(face = 'bold', size = 10))***
作者图片
作者图片
条形剧情
***# P7
ggplot(Wage2) +
geom_bar(aes(x = education,
fill = jobclass)) +
labs(x = 'WAGE') +
ggtitle("wage ~ education ") +
theme_bw() +
theme(axis.text.x = element_text(face = 'bold', size = 10),
axis.text.y = element_text(face = 'bold', size = 10))****# P8
ggplot(Wage2) +
geom_bar(aes(x = jobclass,
fill = education)) +
labs(x = 'WAGE') +
ggtitle("wage ~ jobclass ") +
theme_bw() +
theme(axis.text.x = element_text(face = 'bold', size = 10),
axis.text.y = element_text(face = 'bold', size = 10))***
作者图片
作者图片
方框图
***# P9
ggplot(Wage2) +
geom_boxplot(aes(x = education, y = wage,
fill = education)) +
labs(x = 'EDUCATION') +
ggtitle("wage ~ education ") +
theme_bw() +
theme(axis.text.x = element_text(face = 'bold', size = 10),
axis.text.y = element_text(face = 'bold', size = 10))****# P10
ggplot(Wage2) +
geom_boxplot(aes(x = jobclass, y = wage,
fill = jobclass)) +
labs(x = 'JOBCLASS') +
ggtitle("wage ~ jobclass ") +
theme_bw() +
theme(axis.text.x = element_text(face = 'bold', size = 10),
axis.text.y = element_text(face = 'bold', size = 10))****# P11
ggplot(Wage2) +
geom_boxplot(aes(x = jobclass, y = wage,
fill = education)) +
labs(x = 'JOBCLASS') +
ggtitle("wage ~ jobclass, education ") +
theme_bw() +
theme(axis.text.x = element_text(face = 'bold', size = 10),
axis.text.y = element_text(face = 'bold', size = 10))***
作者图片
作者图片
作者图片
关联矩阵同【GGally】的【ggpairs】
到目前为止,我们已经检查了不同的绘图选项-散点图,直方图,密度图,条形图和箱线图,以找到相对分布。现在该看 R 中的 广义对情节了。
我们已经加载了【GGally】包。函数**ggpairs**
会变魔术,把这些情节都带到一个页面里!
***# P12
ggpairs(Wage2) + theme_bw()***
作者图片
颜色特色由【教育】**
***# P13
ggpairs(Wage2, aes(color = education)) + theme_bw()***
作者图片
颜色特征由【job class】表示
***# P14
ggpairs(Wage2, aes(color = jobclass)) + theme_bw()***
作者图片
希望这篇实践文章对您有用。我建议你在自己的领域数据中应用这些容易编码的图,并为你的业务创新得出有意义和有趣的见解。
祝您的数据之旅顺利……
约书亚·厄尔在 Unsplash 上拍摄的照片
* [## Ahmed Yahya Khaled-EPMO 经理-企业战略- Robi Axiata 有限公司| LinkedIn
打破数据、战略、规划和执行之间的孤岛!这个简介是一个技术和人工智能专业人士目前…
www.linkedin.com](https://www.linkedin.com/in/aykhaled/)*
真实世界数据集的广义泊松回归
Phil Dolby 的照片来自 PxHere 下 CC BY 2.0
以及用 Python 进行广义泊松回归的分步指南
常规泊松回归模型通常是基于计数的数据集的首选模型。泊松回归模型的主要假设是计数的方差与其平均值相同,即数据是等分布的。不幸的是,现实世界的数据很少是等分布的,这促使统计学家使用其他模型进行计算,例如:
- 负二项式(NB)回归模型以及,
- 广义泊松回归模型
这两个模型都而不是做出关于数据集的方差=均值假设。这使得它们成为基于计数的数据建模的更好的实用选择。在之前的一篇文章中,我详细介绍了 NB 模型。在本文中,我们将介绍广义泊松回归模型。
文章的布局
我们将在本文中讨论以下主题:
- 什么是基于计数的数据集?
- 什么是等分散、分散不足、分散过度?我们将讨论泊松模型对于分散不足和分散过度的数据集的局限性。
- 领事的广义泊松回归(GP-1) 模型和 法莫耶的限制广义泊松回归(GP-2) 模型介绍。
- 一个基于 Python 的教程,用于构建和训练 GP-1 和 GP-2 模型,并将它们的性能与标准泊松回归模型进行比较。
什么是基于计数的数据?
基于计数的数据集是因变量 y 代表某个事件发生的次数的数据集。以下是一些例子:
- 每月发现的系外行星数量。
- 历年太阳黑子的数量。
- 每小时走进急诊室的人数。
在上述每一个数据集中,因变量 y 代表观察到的计数,而 X 的选择,解释变量的矩阵,即被认为解释 y 中的方差的一组变量,主要(可悲地)留给了统计建模者的判断。
等分散、欠分散、过分散和泊松模型的局限性
可以从尝试将相关计数变量建模为泊松过程开始。不幸的是,在许多真实世界的数据集中,泊松过程无法令人满意地解释观察计数的可变性。这主要是由于泊松回归模型对数据的方差=均值假设。换句话说,泊松模型错误地假设计数是等分布的。
泊松模型无法摆脱这一假设,因为该模型基于因变量是一个服从泊松概率分布的随机变量的假设,可以证明泊松分布的随机变量的方差等于其均值。
下面的公式表示了泊松分布随机变量的概率分布函数(也称为 P 概率MasF函数)。可以看出:
方差(X) =均值(X) = λ ,单位时间内发生的事件数。
假设每单位时间发生λ个事件,看到 k 个事件的概率(图片由作者提供)
下面是一些有趣的概率分布图,由上述公式针对不同的λ值生成:
不同λ值下看到 k 个事件的概率(图片由作者提供)
顺便提一下,在回归建模中,谈论无条件方差和无条件均值是不常见的。相反,人们更喜欢将方差称为 ,条件是解释变量 X 呈现某个特定值X _ I用于第 I 次观察 。
条件方差可以表示为方差(y|X = X _ I)。条件均值同上。
如前所述,现实世界的数据集很多都不是等分散的,所以 y 的条件方差不等于 y 的条件均值。其中一些的方差大于平均值,这种现象被称为过度分散,而在其他情况下,方差小于平均值,也称为分散不足。
总的来说:
当您的因变量 y 的方差大于您的模型假设的方差时,那么从您的模型的角度来看,您的数据集是过度分散的。
当您的因变量 y 的方差小于您的模型假设的方差时,那么从您的模型的角度来看,您的数据集是欠分散的。
过度(或不足)分散的影响是您的模型将无法充分解释观察到的计数的可变性。相应地,它的预测质量会很差。
解决这个问题的一个常见方法是假设方差是平均值的一般函数,即:
方差=f(均值)
常用的一种形式为 f(。) 如下:
一个常用的方差函数(图片由作者提供)
其中 p=0,1,2…
α 被称为离差参数,它代表由一些未知变量集合引入的【y】的额外可变性,这些变量集合导致y 的总体方差不同于您的回归模型预期的方差。**
注意,当 α=0 时,方差=均值你就得到了标准泊松回归模型。
当 α > 0 时,我们考虑两种有趣的情况,即当 p=1 和 p=2 时。在这两种情况下,我们得到了所谓的负二项(NB)回归模型。
在 NB 回归模型中,我们假设观察计数 y 是一个事件率为 λ 和 λ 本身是一个伽玛分布随机变量。
负二项式回归模型(通常称为泊松-伽马混合模型)被证明是许多真实世界计数数据集的优秀模型。我在下面的文章中对此做了更详细的介绍:
**[**Negative Binomial Regression: A Step by Step Guide**](/negative-binomial-regression-f99031bb25b4)**
还有许多其他方法可用于推广泊松回归模型,以将其扩展到过度分散和分散不足的数据。在本文中,我们将讨论两个这样的模型。
领事的广义泊松回归模型
1989 年,Prem C. Consul 在他的著作《广义泊松分布:性质和应用》,中提出了一种修正泊松分布的概率分布的方法,使其能够处理过分散和欠分散的数据。这个模型后来被称为 GP-1(广义泊松-1)模型。GP-1 模型假设因变量 y 是具有以下概率分布的随机变量:**
GP-1 模型的概率分布、方差和均值函数(图片由作者提供)
如果在上面的等式中设置分散参数 α 为 0,GP-1 的 PMF、均值和方差基本上减少到标准泊松分布。
法莫耶的限制性广义泊松分布
1993 年,Felix Famoye 引入了他所谓的受限广义泊松回归模型,作为一种将标准泊松模型的范围扩展到处理过度分散和分散不足数据集的方法。这个模型被称为 GP-2(广义泊松-2)模型。
GP-2 模型假设因变量 y 是具有以下概率分布的随机变量:
GP-2 模型的概率分布、方差和均值函数(图片由作者提供)
如前所述,当分散参数 α 被设置为 0 时,GP-2 的 PMF、均值和方差函数基本上减少到标准泊松分布的那些。
色散参数α使用以下公式估算:
计算α的公式(图片由作者提供)
在 statsmodels 库中, α 为您估算,作为 GP-1 和 GP-2 模型的模型拟合过程的一部分。
GP-1 和 GP-2 在 Python 和 Statsmodels 中的实现
statsmodels 库包含通过statsmodels.discrete.discrete_model.GeneralizedPoisson
类实现的 GP-1 和 GP-2 模型。
在本节中,我们将展示如何使用 GP-1 和 GP-2 对以下真实世界的计数数据集进行建模。
真实世界的计数数据集
下表包含了骑自行车穿过纽约市各种桥梁的人数。从 2017 年 4 月 1 日到 2017 年 10 月 31 日每天测量计数。
来源:东河大桥的自行车数量(纽约开放数据)(图片由作者提供)
这里是到原始数据集的链接。
我们将集中分析每天穿过布鲁克林大桥的骑自行车的人数。这是布鲁克林大桥上自行车数量的时间顺序图:
每天骑自行车的人在布鲁克林大桥上计数(背景来源:从曼哈顿岛看到的布鲁克林大桥
我们的回归目标
我们回归的目标是预测任意一天穿过布鲁克林大桥的骑自行车的人数。
我们的回归策略
我们将使用数据集中的一组回归变量,即日、星期几(从日期中得出)、月(从日期中得出)、高温、低温和降水量,来“解释”布鲁克林大桥上观测到的计数的变化。
观察到的自行车数量的回归矩阵和向量(图片由作者提供)
我们回归模型的训练算法会将观察到的计数 y 拟合到回归矩阵 X 。
一旦模型被训练,我们将在一个模型在训练期间根本没有见过的持续测试数据集上测试它的性能。
回归策略—循序渐进
- 我们将首先在这个数据集上训练标准泊松回归模型。标准泊松模型也将作为测试广义泊松模型有效性的“控制”模型。
- 如果数据集中的方差与平均值大致相同,那么除了检查泊松模型的拟合优度之外,就没什么可做的了。
- 如果方差大于或小于泊松均值,那么我们将在数据集上依次训练 GP-1 和 GP-2 模型,并将它们的性能与泊松模型进行比较。
我们将从导入所有需要的包开始。
**import pandas as pd
from patsy import dmatrices
import numpy as np
import statsmodels.api as sm
import matplotlib.pyplot as plt**
为计数数据集创建一个熊猫数据框架。这里是数据集的链接。
**df = pd.read_csv('nyc_bb_bicyclist_counts.csv', header=0, infer_datetime_format=True, parse_dates=[0], index_col=[0])**
我们将在 X 矩阵中添加一些派生的回归变量。
**ds = df.index.to_series()
df['MONTH'] = ds.dt.month
df['DAY_OF_WEEK'] = ds.dt.dayofweek
df['DAY'] = ds.dt.day**
让我们打印出数据集的前几行,看看它是什么样子:
**print(df.head(10))**
(图片由作者提供)
让我们创建训练和测试数据集。
**mask = np.random.rand(len(df)) < 0.8
df_train = df[mask]
df_test = df[~mask]
print('Training data set length='+str(len(df_train)))
print('Testing data set length='+str(len(df_test)))**
在 Patsy 符号中设置回归表达式。我们告诉 patsy,BB_COUNT 是我们的因变量 y ,它取决于回归变量 X : DAY、DAY_OF_WEEK、MONTH、HIGH_T、LOW_T 和 PRECIP。
**expr = 'BB_COUNT ~ DAY + DAY_OF_WEEK + MONTH + HIGH_T + LOW_T + PRECIP'**
让我们使用 Patsy 为训练和测试数据集绘制出 X 和 y 矩阵:
**y_train, X_train = dmatrices(expr, df_train, return_type='dataframe')y_test, X_test = dmatrices(expr, df_test, return_type='dataframe')**
使用 statsmodels GLM 类,对定型数据集定型泊松回归模型。
**poisson_training_results = sm.GLM(y_train, X_train, family=sm.families.Poisson()).fit()**
打印培训总结。
**print(poisson_training_results.summary())**
这会打印出以下内容:
泊松回归模型的训练总结(图片由作者提供)
我突出显示了输出中的重要部分。
我们可以看到,所有回归系数(回归术语中称为 β 向量)在 95%的置信水平下具有统计显著性,因为它们的 p 值为< 0.05。
稍后将使用最大对数似然值(-11872)来比较模型与 GP-1 和 GP-2 模型的性能。
让我们打印出数据集的方差和均值:
**print('variance='+str(df['BB_COUNT'].var()))print('mean='+str(df['BB_COUNT'].mean()))**
这会打印出以下内容:
**variance=730530.6601948135mean=2680.042056074766**
方差显然比平均值大得多。数据非常分散,泊松模型的主要假设不成立。
因此,我们接下来将构建和训练 GP-1 和 GP-2 模型,看看它们的表现是否会更好。
Statsmodels 让您用 3 行代码就可以做到这一点!
建立 Consul 的广义毒物回归模型,称为 GP-1:
**gen_poisson_gp1 = sm.GeneralizedPoisson(y_train, X_train, **p=1**)**
拟合(训练)模型:
**gen_poisson_gp1_results = gen_poisson_gp1.fit()**
打印结果:
**print(gen_poisson_gp1_results.summary())**
这会打印出以下内容(我在输出中突出显示了一些有趣的区域):
GP-1 模型的训练结果(图片由作者提供)
请注意,除了日变量的系数,所有其他回归系数在 95%的置信水平下具有统计显著性,即它们的 p 值> 0.05。回想一下,在泊松模型中,发现所有回归系数在 95%的置信水平下具有统计显著性。
GP-1 模型的拟合优度
GP-1 模型的最大似然估计为-1350.6,大于零模型的最大似然估计-1475.9。零模型是简单的仅截距模型,即穿过 y 截距的水平线。但是这种差异在统计学上有意义吗?似然比(LR)检验的 p 值显示为 3.12e-51,这是一个非常小的数字。因此,是的,GP-1 模型确实比简单的仅截取模型在数据建模方面做得更好。
但是 GP-1 比常规泊松模型做得更好吗?
回想一下,常规泊松模型的最大似然估计是-11872。与 GP-1 的 MLE-1350.6 相比。显然,GP-1 产生了更好的拟合优度(它有更大的最大似然估计)。如果我们愿意,我们可以使用两个似然估计值进行 LR 检验,并使用卡方检验评估显著性。在这种情况下,这是不必要的,因为 GP-1 的最大似然比常规泊松模型的大得多。
预言;预测;预告
让我们使用 GP-1 预测骑自行车的人数,使用模型在训练期间没有看到的测试数据集:
**gen_poisson_gp1_predictions = gen_poisson_gp1_results.predict(X_test)**
gen_poisson_gp1_predictions 是一个 pandas 系列对象,它包含 X_test 矩阵中每一行的预计骑自行车的人数。记住 y_test 包含实际观察到的计数。
让我们绘制预测和实际计数,以直观地评估预测的质量:
**predicted_counts=gen_poisson_gp1_predictions
actual_counts = y_test[**'BB_COUNT'**]fig = plt.figure()fig.suptitle(**'Predicted versus actual bicyclist counts on the Brooklyn bridge'**)predicted, = plt.plot(X_test.index, predicted_counts, **'go-'**, label=**'Predicted counts'**)actual, = plt.plot(X_test.index, actual_counts, **'ro-'**, label=**'Actual counts'**)plt.legend(handles=[predicted, actual])plt.show()**
我们得到了预测的和实际的骑自行车的人数的图表:
GP-1 模型中预测的和实际的骑自行车的人数对比图(图片由作者提供)
正如你所看到的,除了一些计数,GP-1 模型在预测骑自行车的人数方面做得相当好。
如果需要,我们可以通过比较 GP-1 预测的均方根误差(RMSE)与常规泊松模型对相同测试数据集的预测,来比较 GP-1 与常规泊松模型的预测质量。我将把它作为一个练习。
最后,让我们也尝试一下法莫耶的受限广义泊松回归模型,称为 GP-2:
我们将使用同样的 3 步方法来构建和训练模型。注意参数 p=2:
***#Build Famoye's Restricted Generalized Poison regression model, know as GP-2* gen_poisson_gp2 = sm.GeneralizedPoisson(y_train, X_train, **p=2**)
*#Fit the model* gen_poisson_gp2_results = gen_poisson_gp2.fit()
*#print the results* print(gen_poisson_gp2_results.summary())**
我们看到以下培训输出:
GP-2 模型的训练输出(图片由作者提供)
分析和比较 GP-2 的拟合优度和预测质量的方法与 GP-1 相同。但是请注意,在这种情况下,模型的训练算法无法收敛。因此,我们将不再研究这个模型,而是更倾向于使用 GP-1 而不是标准的泊松模型来对骑自行车的人计数数据进行建模。
源代码
以下是完整的源代码:
这里是本文中使用的数据集的链接。
摘要
- 标准泊松模型假设基于计数的数据的方差与平均值相同。这个假设经常被过度分散或分散不足的真实世界数据集所违背。
- 因此,我们需要对基于计数的数据使用其他模型,如负二项式模型或广义泊松回归模型,它们不假设数据是等分散的。这种模型假设方差是均值的函数。
- Consul 的广义泊松回归模型(称为 GP-1)和 Famoye 的受限广义泊松回归模型(GP-2)就是两个这样的 GP 模型,它们可用于对基于真实世界计数的数据集进行建模。
- Python 库 Statsmodels 恰好对构建和训练 GP-1 和 GP-2 模型提供了出色的支持。
感谢阅读!我撰写关于数据科学的主题,重点是时间序列分析和预测。
如果你喜欢这篇文章,请关注我的Sachin Date获取关于时间序列分析和预测主题的提示、操作方法和编程建议。**
用气流概括数据加载过程
构建数据仓库:基本架构原则
数据加载过程不应该写两遍,它们应该是通用的
我们使用 Airflow 作为我们的数据管道编排器,以便轻松地编排和监控数据流程。特别是,我们一直致力于 数据加载过程 以便使它们更容易。这些流程允许我们从不同的来源提取数据,并将它们集成到我们的数据湖中——在本例中是 AWS S3。
在我们体验气流的早期,我们会为应该集成到数据湖中的每个数据源创建一个 DAG。然而,过了一段时间,我们最终会得到许多重复的代码。基本上,创建用于提取数据的 Dag 的源代码对于它们中的每一个都是相同的。它们之间唯一的区别是数据库连接和将数据增量装载到数据湖的查询。
重复代码的问题是,当必须引入更改时,它们必须在执行数据加载的每个 Dag 中传播,这既耗时又容易出错。
我们决定通过推广我们为数据加载过程创建 Dag 的方式来解决这个问题。为此,我们构建了一个框架,在这个框架中,为编排数据加载过程创建这样的 Dag 更加容易。因此,在这篇文章中,我们想与你分享框架是如何构建的,以及它如何帮助我们简化数据加载过程。
我们如何解决它?
我们构建了一个旨在简化数据加载的框架。特别是,编写一个配置文件更容易。基于这样一个文件,框架动态生成 DAG ,允许从目标源增量提取数据并上传到 AWS S3。
该框架包括三个逻辑层次:配置文件、 DAG 工厂和加载器:
- 配置文件是 YAML 文件,其中提供了目标源的配置。这种配置可以包括 DAG 配置参数、数据库凭证、表规范、数据类型等等。这样,集成一个新的源代码就像编写一个简单的配置文件一样简单。
- DAG 工厂 基于一种我们称之为 DAG 工厂的模式。它们允许封装逻辑动态创建 Dag。 DAG 工厂模式包括 DAG 接口和工厂。 DAG 接口的功能是读取配置文件并将配置参数传递给适当的工厂,因此工厂可以创建 DAG 工作流,允许从目标源提取数据。我们集成了许多工厂,例如数据库引擎类型,例如,我们集成了来自 MySQL 和 MongoDB 引擎的数据。所以,在这一点上,我们只实现了 MySQL 工厂和 MongoDB 工厂。
- 一个 加载器 是一个 docker 映像,它实现了我们所说的加载器模式。基本上,加载器模式基于在数据加载过程中实现功能数据工程方法的。因此,给定一组参数,加载器能够从目标源提取数据并上传到 AWS S3。加载器是确定性的和幂等的,因此它们每次运行时都会产生相同的结果。
你可以在下一张图中找到我们的框架的逻辑结构的图示。
框架逻辑结构
配置文件
接下来,给出一个名为“ databasename ”的 MySQL 数据库的配置文件示例。在配置文件中,指定了一些配置,如 DAG 名称、默认参数、计划、数据库连接和表及其列数据类型。因此,它们可用于动态生成 DAG。
DAG 接口
在 DAG 界面中,读取配置文件信息。然后,根据数据库引擎类型,通过调用适当的工厂方法来创建 DAG 工作流。此外,必要的参数被传递给工厂,以便创建适当的 DAG 工作流。
因此,基于上面指定的配置文件,t
工厂
一个工厂允许动态生成一个 DAG 工作流,允许提取和加载给定目标源的数据。特别是,DAG 工作流将具有与配置文件中指定的表一样多的任务。任务基于通过使用 气流对接操作符 运行适当的加载器对接映像。为此,配置文件中提供的参数应该作为环境变量传递给 docker 映像。
请记住,该框架旨在执行每日增量加载。所以,它就是为此而设计的。为了支持其他用例,应该引入一些更改。
我们在下一段代码中展示了 MySQL 工厂。
最后,这是由 MySQL 工厂生成的 DAG 工作流的样子。记住每个任务都是一个运行 MySQL loader 的 airflow docker 操作员。
装货设备
一个加载器是一个 docker 镜像,允许从一个特定的数据库引擎提取数据并上传到我们的数据湖——AWS S3。
在下一段代码中,出现了 MySQL 加载器**。我们试图遵循 Maxime Beauchemin 在这篇文章中提出的功能数据工程方法。相应地,加载器被设计成上下文无关的;它们使用覆盖方法,并且它们的结果是不可变暂存区的一部分。这使得它们易于复制。
结论
我们构建这个框架是基于这样一个信念,即数据加载过程不应该写两次,它们应该是通用的。
我们的解决方案深受功能数据工程范例的影响,该范例由 Maxime Beauchemin 在这篇文章中提出。事实上,你可以在这篇帖子中找到更多关于这种范式如何塑造了装载机的设计方式的信息。
希望这些信息对你有用!
感谢您的阅读!😃
如果你想随时更新我的作品, 请加入我的 简讯 ! 我会努力为你提供信息和资源,让你成为更好的数据从业者!
使用 Python 的 Matplotlib 生成等值线图
制作等高线图的简易教程
我相信你以前见过等高线图。至少在天气数据上是这样。它被大量用于展示气象或地理数据。但它也用来表示密度、亮度和电势。它在数据分析和机器学习中被大量使用。它显示了一个响应变量与两个预测变量的关系。
它给出了一个二维视图,其中具有相同响应的所有点由一条线连接。那条线是轮廓线。
等高线图的三个主要元素:
- x 轴和 y 轴显示预测值
- 等高线来表示相同的响应值
- 提供一系列响应值的彩色条带
下面是如何绘制等高线图
首先导入必要的包。
import numpy as np
import matplotlib.pyplot as plt
import pylab
让我们生成一些 x 和 y 值
xp = np.arange(-8, 10, 2)
yp = np.arange(-8, 10, 2)
你大概可以想象‘XP’和‘yp’是什么样子。不过,还是看看吧。
xp
输出:
array([-8, -6, -4, -2, 0, 2, 4, 6, 8])
输入:
yp
输出:
array([-8, -6, -4, -2, 0, 2, 4, 6, 8])
x 和 y 的长度都是 9。
这个信息很重要,因为我们需要用这个数生成 z。
首先,初始化一个 9 x 9 形状的随机 z 点
zp = np.ndarray((9,9))
zp
输出:
array([[1.20117830e-311, 1.20117830e-311, 1.20120150e-311,
1.20120150e-311, 1.20120150e-311, 1.20120150e-311,
1.20118338e-311, 1.20119781e-311, 1.20120151e-311],
[1.20120150e-311, 1.20120151e-311, 1.20117830e-311,
1.20117830e-311, 1.20118045e-311, 1.20120150e-311,
1.20119863e-311, 1.20119863e-311, 1.20117830e-311],
[1.20119864e-311, 1.20118316e-311, 1.20117850e-311,
1.20117830e-311, 1.20118500e-311, 1.20118500e-311,
1.20118490e-311, 1.20118488e-311, 1.20118316e-311],
[1.20118500e-311, 1.20118493e-311, 1.20118493e-311,
1.20118493e-311, 1.20118493e-311, 1.20118339e-311,
1.20117862e-311, 1.20117835e-311, 1.20118337e-311],
[1.20118339e-311, 1.20118338e-311, 1.20118347e-311,
1.20118348e-311, 1.20118339e-311, 1.20118348e-311,
1.20118316e-311, 1.20118338e-311, 1.20118348e-311],
[1.20118339e-311, 1.20118348e-311, 1.20118347e-311,
1.20118348e-311, 1.20118348e-311, 1.20118348e-311,
1.20118339e-311, 1.20118339e-311, 1.20118333e-311],
[1.20118348e-311, 1.20118348e-311, 1.20118348e-311,
1.20117830e-311, 1.20117830e-311, 1.20117853e-311,
1.20117830e-311, 1.20117830e-311, 1.20117830e-311],
[1.20117830e-311, 1.20117830e-311, 1.20117830e-311,
1.20117830e-311, 1.20117830e-311, 1.20117830e-311,
1.20117830e-311, 1.20117830e-311, 1.20117830e-311],
[1.20117830e-311, 1.20117830e-311, 1.20117830e-311,
1.20117830e-311, 1.20117830e-311, 1.20117830e-311,
1.20117830e-311, 1.20117830e-311, 0.00000000e+000]])
现在,我将用自己选择的公式替换这些随机值。你可以使用你想要的或者你需要的公式。
for x in range(0, len(xp)):
for y in range(0, len(yp)):
zp[x][y] = xp[x]**2 + yp[y]**2
zp
输出:
array([[128., 100., 80., 68., 64., 68., 80., 100., 128.],
[100., 72., 52., 40., 36., 40., 52., 72., 100.],
[ 80., 52., 32., 20., 16., 20., 32., 52., 80.],
[ 68., 40., 20., 8., 4., 8., 20., 40., 68.],
[ 64., 36., 16., 4., 0., 4., 16., 36., 64.],
[ 68., 40., 20., 8., 4., 8., 20., 40., 68.],
[ 80., 52., 32., 20., 16., 20., 32., 52., 80.],
[100., 72., 52., 40., 36., 40., 52., 72., 100.],
[128., 100., 80., 68., 64., 68., 80., 100., 128.]])
因此,我们已经准备好了 x、y 和 z 数据。下面是如何做等高线图。
plt.figure(figsize=(7, 5))
plt.title('Contour Plot')
contours = plt.contour(xp, yp, zp)
plt.clabel(contours, inline=1, fontsize=12)
plt.show()
马鞍面等高线图
我将用一个稍微不同的 z 值公式来完成完全相同的过程。所以这次我就不一步步解释了:
xp = np.arange(-3, 4)
yp = np.arange(-3, 4)zp =np.ndarray((7,7))for x in range(0, len(xp)):
for y in range(0, len(yp)):
zp[x][y] = xp[x]*xp[x] - yp[y]*yp[y]plt.figure(figsize=(8, 6))
plt.title('Contour plot for saddle surface - Hyperbolic paraboloid')
contours = plt.contour(xp, yp, zp)
plt.clabel(contours, inline=1, fontsize=12)
plt.show()
密度图
这是一张密度图。因此,我将制作一个不同的数据集,其中数据点更接近。这是 x 和 y。
x = np.linspace(0, 5, 60)
y = np.linspace(0, 5, 48)
x 和 y 都是一样的。数据看起来是这样的:
array([0\. , 0.08474576, 0.16949153, 0.25423729, 0.33898305,
0.42372881, 0.50847458, 0.59322034, 0.6779661 , 0.76271186,
0.84745763, 0.93220339, 1.01694915, 1.10169492, 1.18644068,
1.27118644, 1.3559322 , 1.44067797, 1.52542373, 1.61016949,
1.69491525, 1.77966102, 1.86440678, 1.94915254, 2.03389831,
2.11864407, 2.20338983, 2.28813559, 2.37288136, 2.45762712,
2.54237288, 2.62711864, 2.71186441, 2.79661017, 2.88135593,
2.96610169, 3.05084746, 3.13559322, 3.22033898, 3.30508475,
3.38983051, 3.47457627, 3.55932203, 3.6440678 , 3.72881356,
3.81355932, 3.89830508, 3.98305085, 4.06779661, 4.15254237,
4.23728814, 4.3220339 , 4.40677966, 4.49152542, 4.57627119,
4.66101695, 4.74576271, 4.83050847, 4.91525424, 5\. ])
和之前一样,我需要一个 z 的函数。这是我选择的函数,
def fn(x, y):
return np.sin(x)**5 + np.cos(y+17)**8
现在,该准备 X、Y 和 Z 数据了。我将简单地在 x 和 y 上使用 np.meshgrid 函数,它从一维数组构建二维网格。
X, Y = np.meshgrid(x, y)
Z = fn(X, Y)
数据准备好了。这是最简单的黑白密度等值线图。
plt.contour(X, Y, Z, colors = 'black')
让我们添加一个色彩映射表,并指定我们希望在该范围内绘制 15 条等距线。
plt.figure(figsize=(8, 6))
contours = plt.contour(X, Y, Z, 15, cmap = 'RdGy')
plt.clabel(contours, inline=True, fontsize=12)
matplotlib 中还有一个函数是 contourf。现在,看一个 contourf 的例子。
plt.figure(figsize=(14, 10))
plt.contourf(X, Y, Z, 20, cmap = 'RdGy')
plt.colorbar()
“imshow”的一个例子:
plt.figure(figsize=(14, 10))
plt.imshow(Z, extent=[0,6,0,6], origin='upper', cmap='RdGy')
plt.colorbar()
我们甚至可以把黑白等高线图放在这个 imshow 上面。
plt.figure(figsize=(14, 10))
contours = plt.contour(X, Y, Z, 15, colors='black')
plt.clabel(contours, inline=True, fontsize=12)plt.imshow(Z, extent=[0,6,0,6], origin='upper', cmap='RdGy', alpha=0.5)
plt.colorbar()
我喜欢!你呢?
结论
如果你以前从未画过等高线图,我希望你现在能在必要的时候画出来。它提供了大量的信息,而且看起来也不错!如果您是机器学习建模者,请使用等值线图来可视化不同类别中的范围或数据中的异常。
更多阅读:
完整的可视化课程
towardsdatascience.com](/your-everyday-cheatsheet-for-pythons-matplotlib-c03345ca390d) [## Numpy 完全指南
日常工作中需要的所有数字方法
towardsdatascience.com](/a-complete-guide-to-numpy-fb9235fb3e9d) [## 练习数据科学技能和制作优秀投资组合所需的所有数据集
一些有趣的数据集提升你的技能和投资组合
towardsdatascience.com](/all-the-datasets-you-need-to-practice-data-science-skills-and-make-a-great-portfolio-857a348883b5) [## 逻辑回归模型拟合和寻找相关性,P 值,Z 值,置信度…
静态模型拟合和提取的结果从拟合模型使用 Python 的 Statsmodels 库与…
towardsdatascience.com](/logistic-regression-model-fitting-and-finding-the-correlation-p-value-z-score-confidence-8330fb86db19) [## 学习使用 Python 的 Scikit_learn 库通过项目开发 KNN 分类器
适合机器学习新手
towardsdatascience.com](/clear-understanding-of-a-knn-classifier-with-a-project-for-the-beginners-865f56aaf58f) [## 无限免费虚拟主机和域名
非常适合投资组合、开博客或创业
towardsdatascience.com](/unlimited-free-hosting-and-domain-5998e14f7bfe)
使用 Pylustrator 生成易于复制的科学图形
Python 科学绘图
使用基于 GUI 的程序生成可重复的 Python 绘图脚本
几个月前,我写了一篇关于用 Python 生成科学出版物图的文章。最近,我发现了这个名为 Pylustrator 的令人难以置信的新包,它大大简化了制作多面板图形的过程,而无需摆弄轴的大小。该软件包还会为您生成 Python 代码,因此您可以重现并与他人分享您的数据。在这篇文章中,我将做一些你可以用pylustrator
做的事情的基本演示——希望它对你的剧情制作有用!
安装
您可以使用以下命令使用pip
安装pylustrator
:
pip install pylustrator
生成地块
首先,我们将把matplotlib
、numpy
(创建一些虚拟日期)和pylustrator
导入到我们的脚本中:
**# Import packages** import matplotlib.pyplot as plt
import numpy as np
import pylustrator
我将使用我们在上一篇文章中开发的科学的matplotlib
样式文件作为起点,这样我们就不必重复地改变字体、刻度大小等属性。在pylustrator
中:
**# Use scientific plot style** plt.style.use('scientific')
现在,我们可以创建我们的图形——因为我们将在pylustrator
中完成大部分操作,我们只需要创建最基本的图形,而不考虑格式。首先,让我们创建一些虚拟数据:
**# Create dummy data**
x = np.linspace(0, 4*np.pi, 200)
y1 = np.sin(x)
y2 = np.cos(x)
在我们开始创建图之前,我们将调用pylustrator
函数:
**# Start pylustrator**
pylustrator.start()
现在让我们开始创建我们的基本图形。回想一下,当我们调用add_subplot(rows, cols, index)
时,三个参数如下:
rows
—我们的多点图中的行数
cols
—我们的多点图中的列数
index
—子情节的索引,从左上角开始,向右下方蜿蜒
**# Create panelled figure**
fig = plt.figure()
ax1 = fig.add_subplot(311)
ax2 = fig.add_subplot(312)
ax3 = fig.add_subplot(313)**# Plot data
# Subplot 1**
ax1.plot(x, y1, label='y1')
ax1.plot(x, y2, label='y2')
ax1.legend()**# Subplot 2**
ax2.plot(x, y1)**# Subplot 3**
ax3.plot(x, y2)
现在,我们最后必须在脚本末尾添加一个plt.show()
来显示我们的情节,这样我们就完成了代码的编写!
**# Show plot** plt.show()
运行 Pylustrator
因为我们已经添加了一行pylustrator.start()
,我们可以继续运行我们的脚本文件:
python <your_filename>.py
然后,您应该会看到弹出以下屏幕:
调整大小/重新排列图形
我们可以很容易地改变我们的情节安排,通过点击每个支线剧情,调整我们需要的大小,并四处拖动:
编辑刻度间隔
还有一些菜单可以调整我们的图的刻度间隔:
编辑图例
我们可以单击图例,切换一些选项,并像处理我们的图一样移动它:
添加文本注释
当创建面板图时,您需要用数字或字母标记每个子图,以便在文本中引用它。这也很简单!
救国新图
完成编辑后,我们可以通过使用Ctrl/Cmd + S
并关闭pylustrator
GUI 窗口来保存我们的图形。您会注意到代码将被添加到您原来的.py
文件中。
现在,我们可以从脚本中删除pylustrator.start()
命令,然后直接运行我们的文件来查看我们编辑过的图形!
酷!你现在可以分发这个.py
文件,而不需要pylustrator
,任何人都应该能够重现你的形象!
结论
我们仅仅触及了pylustrator
的一些特性,但是它看起来是一个不可思议的工具,可以为你的出版物/文章编辑你的 Python 图形。本文中的plot.py
文件可以在这个 Github 资源库中获得。
感谢您的阅读!我感谢任何反馈,你可以在 Twitter 上找到我,并在 LinkedIn 上与我联系,了解更多更新和文章。
参考文献
格勒姆河(2020 年)。py literator:用于出版的可复制图形的代码生成。开放源码软件杂志,5(51),1989 年。
使用 Python 生成虚假的唐纳德·特朗普推文
马尔可夫模型和 RNN 文本生成的比较
萨拉·库菲在 Unsplash 上拍摄的照片
让我们使用机器学习来生成模仿唐纳德·特朗普的语言和风格的虚假推文!许多介绍性的机器学习教程都专注于分类任务,如 MNIST,所以我希望你喜欢这篇关于生成模型的文章。
我们将首先使用一种相对简单的方法来完成这项任务,称为马尔可夫模型,我们将从头开始编写代码(我使用的唯一的库是 NumPy)。然后,我们将看一看更高级的递归神经网络实现。我的希望是,首先通过马尔可夫模型的简单例子,可以更容易理解 RNN 模型是如何工作的。
数据
特朗普推特档案包含唐纳德·特朗普的 3 万多条推文。访问网站以 CSV 或 JSON 文件的形式下载给定日期范围内的推文。有一个选项可以下载每条推文的元数据(赞数、转发数、时间戳等)。),但是现在,我们只需要原始文本。
特朗普的所有 30,000 多条推文都可以立即搜索到
www.trumptwitterarchive.com](http://www.trumptwitterarchive.com/archive)
方法 1: N 元马尔可夫模型
背景
Markov 模型查看文本中的 n-gram(n 个字符的子字符串),并计算每个 n-gram 出现在训练文本中时各种字母跟随它的频率。这是一种相当简单的语言表达方式,它很容易编程,在我上大学的时候,用 Java 做这件事是《CS 导论》中的一个作业。
为了更深入地理解这是如何工作的,假设我们的训练文本由一个随机的英语大样本组成,使用的 n 元语法大小为 3。马尔可夫模型一旦建立,就应该存储一个字典,其中的关键字是 n-gram(文本中出现的所有 3 个字符的子字符串),值是从字符到频率的映射(给定字符跟随给定 n-gram 的次数)。例如,如果为模型提供了 n 元单词“mac”,那么模型将知道下一个字母是“h”(像“machine”或“machete”这样的单词)的概率高于下一个字母是“z”(我想不出任何包含“macz”的单词)的概率。另一方面,给定 n-gram“piz”,模型应该给下一个字母是“z”(像“pizza”和“pizzeria”这样的词)比下一个字母是“h”更高的概率。
该模型本质上是一个将 n 字符序列映射到字符频率字典的字典。给定频率后,我们就可以归一化来寻找概率。
在语言语料库上对此进行训练会将训练数据的特征结合到模型中。如果你对它进行莎士比亚方面的训练,它将学会莎士比亚的词汇、表达和典型的莎士比亚戏剧的格式。如果你在 Twitter 数据集上训练它,你会看到标签、Twitter 句柄、URL 等。
n 元语法的大小越大,输出的文本就越“真实”。n 的值很小,就像一个人在到达末尾时忘记了他们是如何开始一个句子的。同时,具有大的 n 值将使模型更有可能从原始文本中再现精确的单词和短语,而不是产生新的句子。
加载数据
让我们直接进入马尔可夫模型的代码(完整的代码可以在这里找到)。首先,我们读入并稍微预处理特朗普的推文。我们通过将所有推文连接在一起并去掉换行符来创建一个庞大的字符串。我们将保留大写,因为这在 tweet 的上下文中通常是有意义的。
f = open("trump_tweets_all.txt")
text = " ".join(f.readlines())
text = " ".join(text.split())
text = text.encode("ascii", errors="ignore").decode()
text.replace("&", "&")
f.close()
构建模型
接下来,我们必须定义 MarkovModel 类。它的构造函数将接受 n-gram 大小n
和训练文本。然后,我们在文本中移动大小为n
的滑动窗口,并构建一个字典,其中每个 n 元语法都映射到它自己的字典,该字典包含从字符到计数的映射。当我们滑动窗口时,我们获取紧跟在当前 n-gram 后面的字符,并更新对应于给定 n-gram 字典中该字符的计数。你在 n-gram 'mac '后面看到一个' h '的次数越多,在' mac' n-gram 的字典中' h '的计数就越高。口头上解释这个有点混乱,那就用视觉形式试试吧。
上图说明了如何根据训练文本构建模型。在这个例子中,n-gram 的大小是 3(绿色字符的数量)。每个 n-gram 的字符计数字典根据下面的字符(用红色突出显示)进行更新。下图解释了我们试图创建的数据结构。当然,在通读了整个训练文本之后,这将是一部巨大的字典!
{
"The": {" ": 1, ...},
"he ": {"U": 1, ...},
"e U": {"n": 1, ...},
" Un": {"i": 1, ...},
...
}
马尔可夫模型有一个get_next_char()
方法,它采用一个 n-gram,并根据它在构造器阶段学习到的概率分布选择下一个字符。当然,我们不希望这个方法总是为同一个 n-gram 返回同一个字符,所以必须有一点随机性,但是它应该遵循从文本中学习到的权重。NumPy 函数numpy.random.choice()
在这里是个大帮手。
为了生成更长的字符串,我们反复调用get_next_char()
方法。每当方法返回一个字符时,我们从 n-gram 中截取第一个字符并追加返回的字符。
def get_n_chars(self, length, ngram):
*"""Returns a generated sequence of specified length,
using the given n-gram as a starting seed."""* s = []
for i in range(length):
nextchar = self.get_next_char(ngram)
ngram = ngram[1:]+nextchar
s.append(nextchar)
return ''.join(s)
结果
下面是模型输出的一些例子(我提供了第一个n
字符,其余的由它填充)。
n=13,start string = "Hillary Clint"
希拉里·克林顿一直在如此努力地工作,而且受了如此严重的伤。我们继续生产以下地面以上高度的产品@ NHC _ 大西洋:随着墨西哥湾沿岸危险条件的到来。请一定要投票给奥巴马。他不需要任何帮助——但是我正在给各种慈善机构捐款。
n=7,start string = "希拉里"
希拉里举了整整一个小时,这非常重要。明天中午。我会更好。今晚两分钟后加入我在加州的民主党)不要存在腐败!DJT 仍然没有证据表明天然气是为 TT 提供帮助的——但我们正在对如此小的马尔科鲁伯进行一项运动调查
观察
很酷地看到,这个模型能够识别所有没有以任何方式硬编码到系统中的语言特征。它只是查看 n-grams 后字符的统计分布,然而它能够正确拼写单词(大多数情况下),使用标点符号和大写字母,有时甚至创建伪语法句子。
这种方法的一个缺点是,特别是对于小的训练文本,这种方法可能导致从源文本复制长串内容,而不是创建新的组合。理想情况下,我们希望模型能够提取拼写和语法等语言特征,但产生新的单词字符串,而不是简单地重复训练数据的长块(即一次几个单词)。Markov 模型的另一个缺点是,我们提供给 generate 方法的起始字符串必须是在训练文本中找到的 n-gram。如果你喂它一个它从未见过的 n-gram,那么它就不知道如何处理。下一节将介绍一个更高级的文本生成模型。
完整的代码和数据:https://github.com/thomashikaru/faketrumptweets
方法 2:递归神经网络
背景
递归神经网络因其能够解决复杂的机器学习问题而受到广泛关注。RNNs 从根本上不同于其他类型的神经网络,如简单的前馈网络或卷积神经网络,因为网络的当前状态被“记住”并作为下一个时间步骤的输入。参见下面的文章,了解一些有趣的背景和例子。
递归神经网络(rnn)有一些神奇的东西。我仍然记得当我训练我的第一个循环…
karpathy.github.io](http://karpathy.github.io/2015/05/21/rnn-effectiveness/)
准备培训示例
我们需要做的第一件事是确定词汇表——训练文本中的独特字符集。如果您包括大写字母、小写字母、数字、空白和特殊字符,这将带给我们大约 90 个独特的字符。然后,文本必须从字符串转换为数字列表,数字和字符之间存在一对一的映射。字符串"abcba"
可能会变成类似于[1, 2, 3, 2, 1]
的东西。
每个训练示例将由一个输入序列和一个目标序列组成,每个序列都有 n 个字母。目标序列就是去掉第一个字符并添加下一个字符的输入序列。如果原始文本包含序列"Donald"
,那么我们可以形成一个输入-目标对("Donal", "onald")
。看到许多这样的字符对将允许模型学习什么字符通常遵循序列"Donal"
,并且希望任何其他序列。
我使用 Tensorflow 和 Keras 创建了 RNN,遵循了 Tensorflow 网站的教程中使用的结构。网络由以下几层组成:单词嵌入层、GRU 层和全连接(“密集”)层。
模型概述。
把...嵌入
嵌入层将输入的字符序列转化为数值,并嵌入到一个高维向量空间中。因此,每个字符首先被映射到一个整数(词汇表中的索引),然后每个整数被映射到一个向量(在我的代码中,是一个 64 维的向量)。
我们为什么要这样做?字符嵌入很重要,因为它创建了比仅使用索引更有意义的输入数字表示。说字母“a”对应于数字 1,字母“z”对应于数字 26 是完全武断的。理想情况下,出现在相似上下文中的字符应该有相似的向量。嵌入层允许模型在训练期间学习词汇字符的良好矢量表示。
苏军总参谋部情报总局
GRU 是基本 RNN 体系结构的一种变体,已经非常有效地应用于许多自然语言任务。在这里和这里可以看到关于 RNNs、LSTMs 和 GRUs 的精彩文章,它们很好地解释了它们是如何工作的以及为什么工作。
培养
为了训练这个模型,我们需要为我们的模型附加一个优化器和损失函数。损失函数计算预测输出和正确目标序列之间的分类交叉熵损失,如我们之前制作的训练样本中所定义的。请注意,模型的输出是词汇表中每个字符的概率序列(嗯,技术上是对数概率)。损失函数应该奖励将高对数几率分配给正确字符的模型,惩罚将高对数几率分配给错误字符的模型。
def loss(labels, logits):
return tf.keras.losses.sparse_categorical_crossentropy(labels, logits, from_logits=True)
完成后,我们可以建立一个检查点目录,并开始培训!我用笔记本电脑的 CPU 和 Google Colab 上提供的免费 GPU 进行了实验。GPU 的训练速度快了大约 10 倍。Tensorflow 教程建议训练大约 30 个周期,以获得不错的结果。
发生
一旦模型被训练,我们使用与马尔可夫模型相似的过程来生成虚假推文。像以前一样,我们需要提供一个开始字符串,尽管它可以是任意长度,不一定是训练文本中存在的子字符串。该模型被反复评估,每次生成一个新的字符。然后,这个新字符作为输入被输入到模型中,模型仍然“记住”它在上一步中看到的文本。最后,我们打印所有生成字符的连接。
def generate_text(model, start_string):
num_generate = 280
input_eval = [char_to_index[s] for s in start_string]
input_eval = tf.expand_dims(input_eval, 0)
text_generated = []
model.reset_states() for i in range(num_generate):
predictions = model(input_eval)
predictions = tf.squeeze(predictions, 0)
predicted_id = tf.random.categorical(predictions, num_samples=1)[-1, 0].numpy()
input_eval = tf.expand_dims([predicted_id], 0)
text_generated.append(index_to_char[predicted_id]) return start_string + ''.join(text_generated)
结果
以下是 RNN 的一些示例输出(我提供了起始字符串,它填充了其余部分):
开始弦乐:“美”
美国已经达成了一个代理,谁做了一个非常大的交易改变了男人&这将…纽约必须知道这本书,但它是关于他的。因为他们会找到任何抗议声明。关闭新的,我们给所有参与给我们带来新书十亿美元扔出一个骄傲的坏。伤心!
开始字符串:“希拉里·克林顿”
希拉里·克林顿在 CNBCNBC 的 today-RESSUDEMINT
上离开了夜晚,我们必须测试 200 亿美元将会建造的凯尔索的经济效益。
俄罗斯最愚蠢的愤怒和资助。
@ bassuckstmannieseal&@ dn some 1 z @ CNN 找链接人肯定是川普国际早安美国】http://t.co/AhYnMlbaement
观察
考虑到 RNN 比马尔可夫模型复杂得多,结果似乎并不令人印象深刻。然而,重要的是要记住,rnn 通常是在数百兆字节的数据上训练的,远远超过这个训练集中的大约 3 万条特朗普推文。此外,通过调整超参数或将多个 RNN 图层添加到模型中,您可能会获得更好的结果。您可以试验不同的序列长度值、嵌入维数或 gru 数。
RNNs 的一个缺点是训练速度很慢。出于许多实际目的,GPU 对于实现合理的训练时间是必要的,即使这样也需要一些耐心。考虑使用 Google Colab,其中包括对 GPU 的有限免费访问,来试验 RNNs。你可以在云端训练,让你的机器不会过热!
完整的代码和数据:https://github.com/thomashikaru/faketrumptweets
结论
如果你想自己学习这些话题,我建议你找一个像这样的迷你项目,你可以真正地投入其中并享受其中。这将有助于你保持动力,你将从自己的项目实践中学到比被动阅读或观看教程更多的东西。
在更严格的设置中,我们可能希望使用某种度量来评估我们的生成器,但这实际上是相当具有挑战性的。评估文本输出的质量比评估分类器复杂得多,在分类器中,您只需要将预测与标签进行比较。这篇伟大的文章讨论了一些用于评估 NLP 中文本输出的指标。
感谢阅读!欢迎留下您的反馈,祝您学习顺利!
参考资料:
[1] Andrej Karpathy,递归神经网络的不合理有效性 (2015)。
[2] Tensorflow,文本生成与 RNN (2020)。
[3]普林斯顿大学计算机科学系,自然语言的马尔可夫模型 e (2020)。
[4] M. Phi,LSTM 和 GRU 的图解指南:一步一步的解释 (2018),中等。
[5] M. Phi,循环神经网络图解指南 (2018),中。
通过深度学习为您最喜欢的类型生成新鲜的电影故事
微调 GPT 新协议,根据类型生成故事
在发现时间旅行后,地球居民现在生活在由政府控制的未来派城市中,为期十年。政府计划向该市派遣两个精英科学家小组,以便调查这些机器的起源并发现“上帝”的存在。
为你喜欢的类型创作故事不是很有趣吗?这就是我们今天要做的。我们将学习如何构建一个基于流派创建故事的故事生成器,就像上面创建科幻故事的生成器一样(在上面的故事中,用户提供的输入是粗体的)。
你也可以把这篇文章作为开发你自己的文本生成器的起点。例如,您可以生成科技、科学和政治等主题的标题。或者生成你喜欢的艺术家的歌词。
为什么是故事生成?
作为一个狂热的电影和电视剧迷,我喜欢故事生成器的想法,它可以根据类型、输入提示甚至标题生成故事。在了解了 GPT 2 号之后,我想把这个想法变成现实。这就是我建造这个模型的原因。
预期用途:寻找乐趣并测试融合讲故事的想法,我们可以通过混合我们的创造力(通过提供提示)和模型的创造力(通过使用提示生成故事的其余部分)来生成故事。
预告片时间:测试故事生成器!
Alex Litvin 在 Unsplash 上拍摄的照片
在深入构建生成器之前,让我们先尝试一下故事生成。在这个 Huggingface 链接 查看我的故事生成器或者运行这个 Colab 笔记本中的单元格来生成故事。模型输入格式的形式如下:
< BOS > <流派>可选提示……
例如
所属类型所属:超级英雄、科幻、动作、剧情、恐怖、惊悚
模型将使用这个提示生成它的故事。还有一种更直观的方式来生成故事:一个使用我的模型 的 网络应用(记住这个应用的生成比hugging face link慢)。
现在你已经完成了对模型的实验,让我们来探索这个想法:我们正在一个包含不同流派电影情节的数据集上微调 OpenAI GPT-2 模型。本演练遵循三幕结构:
- 第一幕:什么是新 GPT 协议?
- 第二幕:微调时间……
- 第三幕:生成时间!
第一幕:什么是 GPT-2?
马特·波波维奇在 Unsplash 上的照片
如果你熟悉 GPT 2 的关键思想,你可以快进到下一幕。否则,请快速复习下面的 GPT 新协议。
最近,我们看到了文本生成模型 GPT-3 背后的巨大宣传,以及它在使用零触发学习生成代码等任务中令人眼花缭乱的表现。GPT 2 号是 GPT 3 号的前身。
等等,为什么我们用 GPT 2 号而不是 GPT 3 号?嗯,还在测试阶段。还有…
正如其炒作一样,GPT-3 的尺寸也是 硕大 ( 传言要求350 GB RAM)。虽然体积仍然庞大,但 GPT-2 占用的空间少得多(最大的变体占用 6GB 磁盘空间 )。它甚至有不同的尺寸和。所以,我们可以在很多设备上使用它(包括iphone)。
如果你在寻找 GPT-2 的详细概述,你可以直接从马嘴那里得到。
但是,如果你只是想快速总结一下 GPT 2 号,这里有一个经过提炼的纲要:
- GPT-2 是一个文本生成** 语言模型 使用一个解码器专用变压器(一个变压器架构的变体)。如果这看起来像是胡言乱语,只需知道 Transformer 是用于 NLP 模型的最先进的架构。**
- 它被预先训练(具有 40GB 的文本数据 ),任务是预测作为输入的前一文本在每个时间步长的下一个单词(更正式地说,令牌)。
第二幕:微调时间…
照片由 Kushagra Kevat 在 Unsplash 上拍摄
我们可以在选定的数据集上微调(进一步训练)像 GPT-2 这样的预训练模型,以适应该数据集的性能。为了微调和使用 GPT-2 预训练模型,我们将使用 拥抱脸/变形金刚 库。它为我们做了所有繁重的工作。
出于这个想法,我通过清理、转换和组合 Kaggle 维基百科电影情节数据集 以及从维基百科搜集的超级英雄漫画情节来创建数据集文件。
培训文件有 3 万多个故事。文件中的每一行都是这种格式的故事:
<博斯> <流派>故事到此为止……
类型:超级英雄、科幻、恐怖、动作、剧情、惊悚
要为另一项任务创建自己的数据集,如基于主题生成研究论文摘要,每个示例的格式可以如下所示:
< BOS > <科目>摘要此处……
科目:物理、化学、计算机科学等。
我推荐你在 Google Colab 上训练模型(设置运行时为 GPU )。用于微调的 colab 笔记本的链接我们的型号是这里的。您可以创建此 笔记本 的副本,并为您自己的数据集进行修改。****
这里有6 _ genre _ clean _ training _ data . txt(训练文件)和6 _ genre _ eval _ data . txt(评估文件),你需要在运行代码之前上传到你的 Colab 笔记本的环境中。****
在这篇文章中,我将介绍 Colab 笔记本中的一些核心代码。
**!pip install transformers torch**
上面,我们正在安装核心库。我们需要变形金刚库来加载预先训练好的 GPT-2 检查点并对其进行微调。我们需要手电筒,因为它是用来训练的。
注意:我从这个变形金刚示例文件中取出代码,并对其进行了修改。
在上面的代码片段中,我们指定了模型的参数(模型参数)、数据参数(数据训练参数)和训练参数(训练参数)。让我们快速浏览一下这些参数的关键论点。要正确检查所有参数,查看这里的。
ModelArguments
- 型号名称或路径:用于指定型号名称或其路径。然后,模型被下载到我们的缓存目录中。查看此处的所有可用型号。这里,我们将模型名称或路径指定为 gpt2 。我们还有其他选项,如 gpt2-medium 或 gpt2-xl。****
- model_type :我们指定想要一个 gpt2 模型。这与上面的参数不同,因为,我们只指定型号类型,不指定名称(名称指 gpt2-xl、gpt2-medium 等。).
数据训练参数
- train_data_file :我们提供培训文件。
- eval_data_file :我们提供一个数据文件来评估模型(使用困惑)。困惑衡量我们的语言模型从 eval_data_file 生成文本的可能性。困惑度越低越好。
- 逐行:设置为 true,因为我们文件中的每一行都是一个新的故事。
- block_size : 将每个训练示例缩短为最多只有 block_size 个令牌。
训练参数
下面, n 是指这些参数的值。
- output_dir :微调后的最终模型检查点保存在哪里。
- do_train,do_eval :设置为 true,因为我们正在训练和评估。
- logging_steps :每经过 n 个优化步骤,记录模型的损耗。
- per _ device _ train _ batch _ size:每个训练优化步骤涉及 n 个训练样本。
- num_train_epochs :训练数据集的完整遍数。
- save_steps :在每个 n 优化步骤后保存中间检查点(如果 Colab 在几个小时后保持断开,建议使用)。
- save_total_limit :任意点存储的中间检查点数。
现在,是时候加载模型、它的配置和标记器了。
正在加载模型、标记器和配置
类 AutoConfig 、 AutoTokenizer 、 GPT2LMHeadModel 根据 model_name 加载各自的配置( GPT2Config )、标记器( GPT2Tokenizer )和模型( GPT2LMHeadModel )。
GPT2Tokenizer 对象对文本进行标记(将文本转换成标记列表)并将这些标记编码成数字。注意,令牌可以是单词,甚至是子单词(GPT-2 使用字节对编码来创建令牌)。下面是一个标记化的例子(没有编码)。
在此之后,我们必须将代币编码成数字,因为计算机只处理数字。
我们有 GPT2Config 对象用于根据型号名称加载 GPT-2 型号的配置。
最后,我们有GPT 2 lmheadmodel对象,它根据模型名和 GPT2Config 加载模型。
有些人可能想知道:到底什么是" LMHeadModel ?简单地说,对于文本生成,我们需要一个语言模型(一个为词汇中的标记分配概率分布的模型)。GPT2LMHeadModel 是一个语言模型(它为词汇表中的每个标记分配分数)。所以,我们可以用这个模型来生成文本。
下一个主要步骤是添加特殊的令牌来将模型与我们的数据集成在一起。
数据集有一些特殊的标记,我们需要向模型指定这些标记是什么。我们可以传入一个 special_tokens_dict ,它可以有一些类似“bos_token”的键。要查看哪些键是允许的,请访问此链接。
- bos_token (" < BOS >")是出现在每个故事开头的令牌。
- EOS _ token(“
”)是出现在每个故事结尾的 token。 - PAD _ token(“
”)是指将较短的输出填充为固定长度的填充令牌。
然而,我们有一些额外的特殊记号(在本例中是我们的流派记号)在我们的 special_tokens_dict 中没有它们自己的键。这些额外令牌可以作为列表放在一个集合密钥“附加 _ 特殊 _ 令牌”下。
如果您正在使用自己的数据集,请用自己的类别替换流派标记。
我们已经为训练做好了准备,所以让我们创建训练者对象。
训练器对象可用于训练和评估我们的模型。
为了创建教练对象,我们指定了模型、数据集以及训练参数(如上所示)。 data_collator 参数用于批量训练和评估示例。
然后,我们调用训练者的训练方法,并在训练后将模型保存到我们的输出目录中。是时候放松一下,让机器训练模型了。
训练完模型后,将模型检查点文件夹从 Colab 下载到您的计算机上。你可以将其部署为 Huggingface 社区模型 ,开发一个 web app ,甚至可以使用该模型开发一个 手机 app 。
第三幕:世代时间!
埃里克·维特索在 Unsplash 上的照片
激动人心的部分来了!如果你渴望创作故事,请在你的 Colab 笔记本中运行下面的单元格!
这里,我们使用了TextGenerationPipeline对象,它简化了我们模型的文本生成,因为它抽象了输入预处理和输出后处理步骤。
**text_generator = TextGenerationPipeline(model=model, tokenizer=tokenizer)**
text_generator 是 TextGenerationPipeline 类的一个对象。我们如何将这个对象作为一个函数来使用?这个类有一个 call method ,允许它的对象像函数一样工作(当使用对象作为函数时调用这个方法)。这里,我们像使用函数一样使用这个对象,通过提供 input_prompt 来生成文本。这就是全部了。
我们还没有解释一些文本生成参数,比如 top_p。如果你想了解这些参数,请跟在片尾字幕后面。
尾声和片尾字幕
概括地说,我们采用了各种类型的电影情节数据集,并将其馈送给 GPT2LMHeadModel 来微调我们的模型,以生成特定类型的故事。使用这些想法,您还可以创建其他数据集来基于这些数据集生成文本。
要测试我的电影故事生成器,请单击此处的(或使用 web app 此处的 )。********
非常感谢 Huggingface 团队 提供的变形金刚库和详细的例子,以及 Raymond Cheng 撰写的这篇 文章 帮助我创建了我的模型。
可选:探索文本生成参数
首先,让我们了解一下 GPT2LMHeadModel 的输出是什么:
当预测下一个标记时,该模型为其词汇表中所有可能的标记生成逻辑。为了简化起见,可以将这些逻辑看作每个标记的分数。具有较高分数(logits)的记号意味着它们有较高的概率成为合适的下一个记号。然后,我们对所有令牌的 logits 应用 softmax 操作。我们现在得到每个令牌的 softmax 分数,该分数在 0 和 1 之间。所有令牌的 softmax 分数总和为 1。这些 softmax 分数可以被认为是在给定一些先前文本的情况下,某个标记成为合适的下一个标记的概率(尽管它们不是)。
以下是参数的基本大纲:
- max_length [int]:指定要生成的令牌的最大数量。
- do _ sample【bool】:指定是否使用采样(比如 top_p 或者使用贪婪搜索)。贪婪搜索是在每个时间步选择最可能的单词(不推荐,因为文本会重复)。
- repetition _ penalty【float】:指定对重复的惩罚。增加重复 _ 惩罚参数以减少重复。****
- 温度【浮动】:用于增加或减少对高逻辑令牌的依赖。增加温度值会减少只有少数令牌具有非常高的 softmax 分数的机会。
- top _ p【float】:指定仅考虑概率(形式上,softmax)得分之和不超过 top_p 值的令牌
- top_k【int】:告诉我们在生成文本时只考虑 top _ k 个标记(按它们的 softmax 分数排序)。
仅此而已。
从现有空间数据生成十六进制地图只需不到 3 个步骤
空间可视化——结合使用 Turf 和 Hextile JavaScript 库来生成十六进制地图
看过许多文章后,我注意到许多人试图为他们当前的空间数据集生成十六进制地图,要么是为了美观(例如仪表板可视化),要么是为了最大限度地减少重叠几何特征造成的空间失真。嗯,我有好消息告诉这些人,因为我最近发现结合使用两个 JavaScript 库可以让用户立即呈现所需的输出,消除了在 Tableau 中添加任何额外计算字段的麻烦。为了方便用户和将来的使用,这个新功能已经被部署到我在 https://tableau-data-utility.onrender.com/的网络应用上。因此,下面是数据转换和结果可视化的快速预览:
图片作者| ( 左)美国各州多边形地图| ( 右)美国各州十六进制地图
上图不涉及 Tableau 中的任何计算。方法是简单地将 GeoJSON 格式的文件预处理成另一个 GeoJSON 文件,该文件渲染六边形而不是多边形。
只需导航至https://tableau-data-utility.onrender.com/并将您的空间数据输入 web 应用程序:
作者图片| US_States.geojson 文件已成功上传至浏览器工具
按作者分类的图像|如果您的空间数据集采用其他格式,如 SHP 或 KML,则在同一页面上还有一个实用工具可用于将您的空间数据输出为所需的 GeoJSON 格式
图片作者|最后,上传 GeoJSON 数据后,选择可用的形状选项-正方形、菱形、六边形和反六边形|继续调整宽度(以米为单位),然后选择“导出 GeoJSON 十六进制地图输出”
作为参考,US_States.geojson 的文件可以在我的 GitHub 上找到,带有上述参数的输出文件—倒六边形+宽度(500,000m)也可以在我的 GitHub 上找到。只需保存文件,并在 Tableau 或 Leafletjs(无论你喜欢哪个 GeoJSON 平台)中渲染它,这就是你得到十六进制地图可视化所要做的一切!
基本上,浏览器工具使用库 Turf 和 Hextile 在后端完成所有繁重的工作,生成十六进制格式的数据。需要注意的是,要确定没有映射到地图任何特定部分的六边形:
按作者分类的图像|空值是指未映射到原始空间文件任何部分的六边形
图片作者|因此,隐藏空值以最大限度地减少生成的地图中的冗余六边形是明智的做法|通常,空值的出现是因为计算出的六边形的质心都不在原始空间数据的任何多边形内
请访问https://tableau-data-utility.onrender.com/随意尝试其他国家省或州的其他数据集——请注意,目前 web 应用程序的标题上有几个选项卡:
作者图片|选择“空间数据到十六进制地图”以访问正确的工具
请随意使用其他 Tableau 数据实用工具——我发布的另一个教程中的网络图到 CSV,以及本教程中的空间数据到 CSV。
希望这有助于简化和减少 Tableau 数据处理中的麻烦!
感谢您的阅读。
获得李思欣·崔和其他作家在媒体上的所有帖子!😃您的会员费直接…
geek-cc.medium.com](https://geek-cc.medium.com/membership)
用 Python 生成有意义的词云
辅导的
从文本中提取有意义的单词并在 python 中创建单词云艺术的方法。
布莱恩·迪金森的插图
你肯定见过一个字云。一般来说,这是一张充满单词的照片,其大小取决于单词在你想要分析的文本中出现的频率。我对此并不知情,但很明显,有人(不确定)对文字云有强烈的反对和支持。我读到过他们是糟糕的、蹩脚的,把他们等同于现代的饼状图,甚至把他们比作新的乌鱼。哎哟。一条鲻鱼?走吧。
布莱恩·迪金森插图
我也看过一些改善 word 云的方法和有用的时候去使用它们。不管你属于哪个阵营,我发现在演示中利用引人注目的图形或视觉效果,可以吸引你的观众,促使他们做出反应,可以开始对话,可以产生影响,并为更详细的分析打开大门。视觉上引人注目的文字云艺术可以吸引读者。也就是说,我不会深入研究单词云的利弊,你可以查看上面的链接。
我们将使用几种不同的方法从文本中提取一些有意义的单词。然后,我们将使用 Python 库 WordCloud、pandas 和 NumPy 生成一些单词云。
WordCloud 是 Python 中的单词云生成器。您可以通过以下命令之一安装 WordCloud。
pip install wordcloud conda install -c conda-forge wordcloud
在这里查看安装细节,在这里可以通读 WordCloud 文档。
数据
我们将使用的数据是 2020 年总统大选的民主党初选辩论。你可以在这里通过 Kaggle 找到完整的数据集。数据已经被清理和过滤。Github repo 包含了清洁步骤。
1|导入库和数据
在创建词云之前,文本停用词应该专门更新到文本的领域。例如,如果我们正在为一家航空公司完成来自客户推文的词云,我们可能会得到像“飞机”、“飞行”、“旅行”这样的词,它们可能对您正在完成的任何分析都没有任何意义。
在这个数据集中,包含了额外的停用词,因为它们在文本中出现了很多,但对分析没有贡献。例如,像'参议员'、众议员'、人民'、事实这样的词都是许多候选人在句子中使用的词,如'我同意参议员桑德斯……'或'事实是……'、'美国人民想要..'并不一定有助于词义的词为词云。
2.1|方法 1 —使用 WordCloud 处理 创建文字云最简单快捷的方法就是简单地使用 word cloud 处理文字。
文本需要在一个长字符串中,以便 WordCloud 对其进行处理。我们将数据过滤到“ biden ”,创建一个他的回答列表,并连接该列表以创建一个长的文本字符串。然后我们创建单词云对象,使用 generate() 方法,并传递我们的文本字符串。最后,我们使用 plt.imshow 来显示图像。
我们来看看文档中的参数:
停用词 :该参数取一组将从词云中剔除的字符串(词)。我们在这里使用了更新的停用词列表。
背景 _ 颜色 :设置背景颜色,默认为黑色
结果…看起来有点像胡言乱语,看起来没有太多信息。单词' re '、表示'、 make '、表示似乎是出现频率最高的单词。这些在乔·拜登的信息中似乎并不太重要,而像'成本'、医疗保险'和'世界'这样的词在背景中。一种选择是继续更新停用词。这个过程的一个挑战是知道何时停止。什么时候你知道你有足够多的单词而不用去掉有用的单词?我并不是对这种方法不屑一顾,我以前用过。
我们可以使用 process_text() 和 words_ 方法分别显示文本中的字数和相对数。
# output
[('re', 148), ('make', 117), ('ve', 106), ('said', 95), ('able', 88)]
[('re', 1.0), ('make', 0.7905405405405406), ('ve', 0.7162162162162162), ('said', 0.6418918918918919), ('able', 0.5945945945945946)]
你可以瞥一眼其他候选词,你会注意到类似的结果,无意义的词出现在列表的较高位置。让我们调整一些额外的 WordCloud 参数来改善显示的单词。
min _ word _ length:是一个词必须包含的最小字母数
搭配 _threshold: 是一个催款似然得分。文本中的二元模型必须达到大于此参数的分数,才能算作二元模型。默认值设置为 30。
我们将添加最小单词长度 4 和 collocation _ threshold 3 来包含更多二元模型。最后,也许是最重要的,我将使用一组不同的停用词。原始设置是从 WordCloud 导入的。我将导入并使用 SpaCy 库中提供的停用词,并用我自己的停用词更新列表。没有真正的理由使用 SpaCy 的列表,除了我在过去使用过它,并取得了良好的效果。您可以使用许多不同的停用词库。
左:原始词云|右:更新的停用词和参数
我们看到了相当大的进步!我们得到了一些不同的词,包括像“唐纳德·特朗普”、“巴拉克·奥巴马”、“公共选择”和“中产阶级”这样的词。更多的调整/更新停用词可能会改善这一点。
2.2|方法 2——利用词频 前一种方法使用一串文本。如果不一定能接触到全文或者想直接使用词频怎么办?一个替代 generate() 方法的 WordCloud 是generate _ from _ frequency()方法,它将获取一个单词及其频率的字典,并根据计数创建一个单词云。让我们试一试。
我们仍然有全文,所以我们将利用 CountVectorizer 来创建一个字数矩阵。如果你已经有一本计数字典或者一袋单词矩阵,你可以跳过这一步。
单词包数据框的片段
现在我们只需要提取这个数据帧中的一行,创建一个字典,并将其放入 WordCloud 对象中。
左图:使用词云的旧词云|右图:使用词频的新词云
新的词云看起来和以前的版本有些相似。有相似的顶频词,也有一些区别。当使用generate _ from _ frequency方法时,它会忽略一些参数,包括搭配和停用词参数。CountVectorizer 处理文本,包括停用词和词汇化。如果我们想对单词做额外的调整,需要在将它们放入 WordCloud 对象之前完成。我们可以看到'唐纳德'被词条化为'唐',在这个版本中没有二元模型。
从 frequencies 创建单词云的另一个选项是利用 collections Counter 对象来创建一个字典,您可以在 word cloud 中使用它。这需要对文本进行预处理,包括对单词进行标记,然后使用 counter 对象对每个单词进行计数。如果您需要整个文本,那么使用 WordCloud 对象来完成这个任务会更容易。
2.3|方法 3—对数比值比 从最后两个词云中,我们得到了包含这些候选人所说内容的相当好的词组。如果你查看来自多个候选人的热门单词,你会发现相似的单词开始出现在所有人的面前——但是特定于某个人的单词或短语呢?例如,如果你看过这些辩论,你可能会注意到艾米·克洛布查尔提到“领先的民主党人”,拜登喜欢数他的分数(“第一”,“第二”)..)相当经常。我们可以看到某个候选人提到的重要单词或短语,但其他人没有。
回到我们为飞机公司分析客户推文的例子。我们可能希望捕捉客户提及的细分市场。与其他旅客相比,我们的商务旅客是怎么想的?许多词可能是相同的,如'座位'和'体验',但它们可能与同样提到'座位'和'体验'的紧急旅行者有不同的想法和优先顺序。区分这些词有助于更好地了解这些不同的客户群。
简而言之,我们希望看到某个候选人(比如说皮特·布蒂吉格)的哪些词相对于所有其他候选人来说最常见。我们会对每个候选人都这样做。
为此,我们将使用为每个单词计算的对数比值比的度量,如下所示:
加法平滑用于避免被零除
我们使用单词包数据帧,并使用上面的计算转换每一行。结果是一个数据框,显示了特定候选人说出的每个单词的对数概率。
转换后的单词包数据帧与对数比值比的片段
我们试图找出每个候选人最独特的词。以下是四个候选人的前 10 个单词,你能把它们和正确的候选人匹配起来吗?
从左至右:皮特·布蒂吉格、卡玛拉·哈里斯、伊丽莎白·沃伦、伯尼·桑德斯
按照上面通过词频生成词云的相同步骤,我们现在可以创建一个新的词云。
这个方法的伟大之处在于 n 元文法是用这个方法生成的。我们看到了拜登关于重塑美国灵魂的信息,以及他提到的针对女性的暴力法案。还有其他方法可以使用,比如加权对数优势比& tf-idf。
那么我们用哪个文本呢?前提是用文字创造出令人惊叹的视觉对比分析。我将两者结合起来,给出一个候选人常用词和常用词的一般概念。
2.4|组合词典 我们有两种不同的词典/词频(方法 1 & 3),我们可以单独使用或组合使用它们来创建一个无所不包的词云。合并后,我们将再做一个调整。你会注意到一个单词可能会有不同的拼写。像保健和保健。我们将把这些加在一起,以便更好地表达这个词。
这可能看起来有点疯狂。这段代码将第二个字典中最常用单词的频率设置为第一个字典中最常用单词的频率。
我们现在合并字典,将“healthcare”和“health care”合并成一个关键字,以便更好地表示。下面显示了前 5 个条目和词云。
[('number number', 56),
('argued', 44),
('special forces', 41),
('single solitary', 41),
('restore soul', 40)]
很好,我们看到了候选人频繁使用的两个世界的融合,以及候选人独有的常用词。我们有我们的文本接下来将创建我们的自定义单词云。
3 |准备照片 这一步不是用 python 完成的。你需要找到一个图像来使用。有很多免费的图片收藏网站可以选择,比如:Unsplash、Pixabay、Pexels 和 T10。
一旦你找到一张照片,它需要被转换成黑色&白色。照片的黑色部分将是文字显示的地方,白色区域将显示为白色。你可以在 Photoshop 等照片编辑软件中完成这一点,也可以在网上用免费的照片编辑软件如 Pixlr 来完成。我用这两种方法很容易地将背景从图片的主体中分离出来。有些照片比其他照片需要多一点时间。
左图由量具 Skidmore CC BY-SA 2.0 提供
专业提示:在创建照片时,确保背景确实是白色的,而不是透明的。透明像素值将被导入为 0 或黑色,不会给你一个蒙版图像。
3 |导入照片并创建单词云 有多种方法可以给你的单词云中的单词上色。我将详细介绍两个,您可以在 Github repo 中看到第三个。第一个是利用彩色地图。
好了,让我们来看一下这段代码。第一行导入您的黑白图像,第二行在创建图像时调整任何细微的颜色差异。我让 Adobe Illustrator 给我一个介于 240 和< 255 之间的更大范围的“白色”像素值,创建一个无法工作的图像。所以这条线把所有大于 3 的像素值都变成白色,剩下的都是原来的值。然后,我们用一些新参数创建 WordCloud 对象。
font_path :这是一个你想为单词使用的字体的路径。您可以将此路径指向操作系统的字体文件夹。(即。c:\ Windows \ Fonts \ font . TFF)
contour _ color:图像轮廓的颜色
max _ words:图像中使用的最大字数
mask :您的图像矩阵
color map:用于文字的颜色图
乔·拜登的单词云和蓝色地图
看起来棒极了!虽然浅色很难辨认。我们可以改变背景或选择不同的颜色或色彩映射表。另一个选择是选择我们想要使用的颜色图中较暗的部分。我们将添加两行,从 matplotlib 导入一个色图作为颜色矩阵,然后选择矩阵中较暗的部分。
用自定义颜色创建的左侧图像。用 matplotlib 色彩映射表创建的右图
你可以看到成品看起来相当不错。右边的图像是上面代码中颜色较深的图像。我也用它来创建标题图像。左边的图像是自定义颜色。你可以在 Github repo 中找到这个词云的代码。您可以使用 to_file() 方法保存图像,并传递一个保存文件的位置。wordcloud.to_file(“path_to\\wordcloud_image.png”)
3.1 |创建一个图像着色的文字云 另一个选择是使用照片本身的颜色给文字着色。你需要像以前一样准备照片,但是只去掉背景,留下你希望文字覆盖的图像部分。
左图由量具滑轨莫尔 CC BY-SA 2.0 提供
我们再次创建遮罩(不改变任何值)。对 WordCloud 的参数做了一些改动。为了增加可读性,对字体和背景的大小和颜色做了一些改变。
ImageColorGenerator用于为文字云创建颜色,而 recolor() 方法用于改变文字的颜色。
艾米·克洛布查尔的词汇云
我们探索了几种不同的方法,利用不同的停用词从全文和词频中提取一些有意义的文本。我们还研究了利用对数比值比从部分文本中寻找常用词。我们还使用不同的遮罩、字体和颜色绘制了几种类型的单词云。希望这能帮助你为项目创造一些有用的视觉效果。
点击这里查看 Github 库,上面所有例子的代码都在这里。
如果你对学习和实现其他 NLP 技术以从文本中提取见解感到好奇,请查看 Neptune.ai 的这篇博客文章,它涵盖了超过 7 种其他 NLP 技术,包括情感分析和词性标注。
如果这有帮助,让我知道!我很想听听你对词云的想法,看看你的一些成果。
两分钟内生成尼克·凯夫的歌词
在这篇文章中,我们将使用 GPT-2 语言模型来开发一个简单的歌曲歌词生成器,以尼克·凯夫为例,他是我最喜欢的艺术家之一。
GPT-2
GPT-2 是一个大型的基于转换器的语言模型,有 15 亿个参数。它的训练有一个简单的目标:给定某个文本中所有的前一个单词,预测下一个单词。由 800 万个网页组成的数据集的多样性,导致这个简单的目标包含跨不同领域的许多任务的自然发生的演示。有关详细信息,请访问open ai 出版物。让我们集中精力练习吧!
解析歌词
首先,我们需要使用众所周知的BeautifulSoup
库解析来自尼克·凯夫 官方网页的歌词。
解析尼克·凯夫的歌曲
运行上面的代码后,我们在songs.txt
文件中保存了来自 20 张专辑的 201 首歌曲。
火车模型
构建标记器
使用[aitextgen](https://github.com/minimaxir/aitextgen)
库在下载的歌曲上训练一个自定义标记器。这将保存两个文件:aitextgen-vocab.json
和aitextgen-merges.txt
,它们是重新构建分词器所需要的。
列车 GPT-2 变压器模型
使用创建的 tokenizer 启动 aitextgen
创建 TokenDatasets,构建用于训练的数据集,以适当的大小处理它们。
训练时间到了!
生成歌曲
从“我们淹死了”开始生成 3 个段落,并将它们保存到文件中。您可以修改max_length
和temperature
参数。温度是随机性相关的超参数。当其值较小时(如 0,2),GPT-2 模型更有信心,但也更保守。当温度是一个大值(例如 1)时,GPT-2 模型产生更多的差异,也产生更多的错误。在我看来,当你想生成一件艺术品时,第二种选择要好得多;).
下面你可以看到样本结果。看起来挺有前途的,尤其是考虑到需要多少编码量:)。
感谢阅读!
利用深度学习生成新颖的艺术作品
深度学习与艺术:神经类型转移
神经类型转移的一个例子
1.问题陈述
在这篇文章中,我将继续使用深度学习以另一幅图像的风格创作图像(曾经希望你能像毕加索或梵高一样画画吗?).这就是所谓的神经风格转移!这是一种在莱昂·A·加蒂斯的论文中概述的技术,一种艺术风格的神经算法,这是一本很棒的读物,你绝对应该去看看。
但是,什么是神经风格转移呢?
神经风格转移是一种优化技术,用于拍摄三幅图像,一幅内容图像,一幅风格参考图像(如著名画家的艺术作品),以及您想要设计风格的输入图像——并将它们混合在一起,以便输入图像被转换为看起来像内容图像,但以风格图像的风格“绘制”,从而桥接深度学习和艺术的轨道!
例如,让我们来看看这只海龟和葛饰北斋的神奈川外的巨浪:
绿海龟图片——P . Lindgren【CC By-SA 3.0(https://creative Commons . org/licenses/By-SA/3.0)】%5D),来自维基共享资源。
https://en.wikipedia.org/wiki/The_Great_Wave_off_Kanagawa
现在,如果 Hokusai 决定用这种风格来画这只海龟,会是什么样子呢?类似这样的?
这是魔法还是只是深度学习?幸运的是,这不涉及任何巫术:风格转移是一种有趣的技术,展示了神经网络的能力和内部表示。
神经风格转移的原理是定义两个距离函数,一个描述两幅图像的内容如何不同的𝐿𝑐𝑜𝑛𝑡𝑒𝑛𝑡,另一个描述两幅图像在风格方面的差异的𝐿𝑠𝑡𝑦𝑙𝑒.然后,给定三个图像,期望的样式图像、期望的内容图像和输入图像(用内容图像初始化),我们尝试变换输入图像,以最小化内容图像的内容距离和样式图像的样式距离。总之,我们将获取基本输入图像、我们想要匹配的内容图像和我们想要匹配的样式图像。我们将通过反向传播最小化内容和样式距离(损失)来转换基本输入图像,创建一个匹配内容图像的内容和样式图像的样式的图像。
在本文中,我们将生成一幅巴黎卢浮宫博物馆的图像(内容图像 C),混合了印象派运动领袖克洛德·莫内的一幅画(风格图像 S)。
2.迁移学习
神经风格转移(NST)使用先前训练的卷积网络,并在此基础上构建。使用在不同任务上训练的网络并将其应用于新任务的想法被称为迁移学习。
根据NST 的原始论文,我将使用 VGG 网络。具体来说, VGG-19 ,19 层版本的 VGG 网络。该模型已经在非常大的 ImageNet 数据库上被训练,因此已经学会识别各种低级特征(在较浅的层)和高级特征(在较深的层)。
下面的代码从 VGG 模型中加载参数(更多信息请参考 Github repo):
pp = pprint.PrettyPrinter(indent=4)
model = load_vgg_model(“pretrained-model/imagenet-vgg-verydeep-19.mat”)
pp.pprint(model)
模型存储在 python 字典中。python 字典包含每个图层的键-值对,其中“键”是该图层的变量名,“值”是该图层的张量。
3.神经类型转移
我们将分三步构建神经类型转移(NST)算法:
- 构建内容成本函数 J_content (C,G)。
- 构建风格成本函数 J_style (S,G)。
- 放在一起得到 J(G) = α * J_content (C,G) + β * J_style (S,G)。
神经风格转移算法的总成本函数
3.1 计算内容成本
在我们运行的例子中,内容图像 C 将是巴黎卢浮宫博物馆的图片(缩放到 400 x 300)
content_image = scipy.misc.imread(“images/louvre.jpg”)
imshow(content_image);
内容图片(C)显示了卢浮宫博物馆的金字塔,周围环绕着古老的巴黎建筑,背景是晴朗的天空和几朵云。
3.1.1 将生成的图像 G 的内容与图像 C 进行匹配
如前所述,ConvNet 的较浅层倾向于检测较低级别的特征,如边缘和简单纹理;更深的层倾向于检测更高级的特征,例如更复杂的纹理以及对象类别。
我们希望生成的图像 G 具有与输入图像 C 相似的内容。假设你已经选择了一些层的激活来代表图像的内容。实际上,如果你在网络的中间选择一个图层,你会得到最令人满意的视觉效果——既不太浅也不太深。
注意:在你完成了这篇文章的例子之后,你可以自由地尝试不同的层,看看结果如何变化。
首先,我们将图像 C 设置为预训练的 VGG 网络的输入,并运行前向传播。让一个 ᶜ 成为你选择的层中的隐藏层激活。这将是一个 nH × nW × nC 张量。
使用图像 G 重复此过程—将 G 设置为输入,并运行正向传播。让一个 ᴳ 被相应的隐藏层激活。
然后我们将定义内容成本函数为:
内容成本函数
这里, nH 、 nW、和 nC 分别是您选择的隐藏层的高度、宽度和通道数。这些术语出现在成本中的规范化术语中。
为了清楚起见,请注意, ᶜ 和 ᴳ 是对应于隐藏层激活的 3D 体积。为了计算成本 J_content (C,G),也可以方便地将这些 3D 体积展开成 2D 矩阵,如下所示。
将激活层的 3D 体积展开成 2D 矩阵。
从技术上来说,计算 J_content 并不需要这个展开步骤,但是当您稍后需要执行类似的操作来计算样式成本 J_style 时,这将是一个很好的实践。
实现
***compute_content_cost()***
函数使用 TensorFlow 计算内容成本。
实现该功能的 3 个步骤是:
- 从
a_G
中检索尺寸。 - 展开
a_C
和a_G
,如上图所示。 - 计算内容成本。
总之,内容成本采用神经网络的隐藏层激活,并测量 a ᶜ 和 a ᴳ 有多不同。当我们稍后最小化内容成本时,这将有助于确保 G 拥有与 C 相似的内容。
3.2 计算风格成本
对于我们的运行示例,我们将使用以下样式图像:
由印象派运动的领袖克洛德·莫内以 印象派 的风格作画。
风格矩阵
样式矩阵也被称为 Gram 矩阵。线性代数中,一组向量(v₁,…,v n )的格拉姆矩阵 g 是点积矩阵,其项为 G ij = vᵢᵀ vⱼ= np.dot(vᵢ,vⱼ)
换句话说,G ij 比较 vᵢ和 vⱼ.有多相似如果它们非常相似,你会期望它们有一个大的点积,因此 G ij 会很大。
请注意,这里使用的变量名中有一个不幸的冲突。我们遵循文献中使用的通用术语。 G 用于表示样式矩阵(或克矩阵); G 也表示生成的图像。对于这个例子,我们将用Ggram来指代 Gram 矩阵,用 G 来表示生成的图像。
在神经风格传递(NST)中,您可以通过将“展开的”过滤器矩阵与其转置相乘来计算风格矩阵:
GG测量两个滤波器之间的相关性:
结果是一个维数矩阵( nC,nC ),其中 nC 是滤波器(通道)的数量。值Ggram(I,j) 测量滤波器 i 的激活与滤波器 j 的激活有多相似。
克 克也衡量图案或纹理的流行程度:
对角线元素Ggram(I,i) 测量过滤器 i 的“活动”程度。例如,假设过滤器 i 正在检测图像中的垂直纹理。然后Ggram(I,i) 度量图像整体上垂直纹理有多常见。如果Ggram(I,i) 大,这意味着图像有很多垂直纹理。
实现
风格成本
目标是最小化样式图像 S 的 gram 矩阵和生成的图像 g 的 Gram 矩阵之间的距离。
现在,我们只使用一个单独的隐藏层一个 ˡ.该层的相应样式成本定义为:
款式成本
实施
**compute_layer_style_cost()**
实现该功能的 3 个步骤是:
- 从隐藏层激活中检索尺寸
a_G
。 - 将隐藏层激活
a_S
和a_G
展开成 2D 矩阵,如上图所示。 - 用我们之前写的函数计算图像 S 和 G 的样式矩阵。
- 计算风格成本。
样式重量
到目前为止,我们只从一层捕捉到了风格。如果我们从几个不同的层“合并”风格成本,我们会得到更好的结果。每一层将被赋予权重( λˡ ) ),以反映每一层对风格的贡献。默认情况下,我们会给每个层相同的权重,权重加起来是 1。完成这个例子后,可以随意试验不同的权重,看看它如何改变生成的图像 G 。
您可以组合不同图层的样式成本,如下所示:
其中 λˡ 的值在STYLE_LAYERS
中给出。
STYLE_LAYERS = [
(‘conv1_1’, 0.2),
(‘conv2_1’, 0.2),
(‘conv3_1’, 0.2),
(‘conv4_1’, 0.2),
(‘conv5_1’, 0.2)]
实现
**compute_style_cost()**
该函数多次调用compute_layer_style_cost(...)
函数,并使用STYLE_LAYERS
中的值对结果进行加权。
对的描述**compute_style_cost**
对于每一层:
- 选择当前层的激活(输出张量)。
- 从当前图层中获取样式图像 S 的样式。
- 从当前图层获取生成的图像 G 的样式。
- 计算当前层的样式成本
- 将加权风格成本加到总风格成本上( J_style )
完成循环后:
- 返还整体风格成本。
注意:在上面 for 循环的内循环中,a_G
是一个张量,还没有求值。当我们在下面的 model_nn()中运行张量流图时,它将在每次迭代中被评估和更新。
总之,图像的风格可以使用隐藏层激活的 Gram 矩阵来表示。通过组合来自多个不同层的这种表示,我们甚至可以获得更好的结果。这与内容表示形成对比,在内容表示中通常只使用一个隐藏层就足够了。此外,最小化样式成本会导致图像 G 跟随图像 S 的样式。
3.3 定义优化的总成本
最后,让我们创建一个最小化样式和内容成本的成本函数。公式是:
NST 的总成本函数
实现
总成本是内容成本 J_content (C,G)和样式成本 J_style (S,G)的线性组合。
α 和 β 是控制内容和风格之间相对权重的超参数。
4.解决最优化问题
最后,让我们把所有的东西放在一起实现神经风格转移!
以下是该程序必须要做的事情:
- 创建交互式会话
- 加载内容图像
- 加载样式图像
- 随机初始化要生成的图像
- 加载 VGG19 型号
- 构建张量流图:
- 通过 VGG19 模型运行内容映像,并计算内容成本
- 通过 VGG19 模型运行样式图像,并计算样式成本
- 计算总成本
- 定义优化器和学习率
7.初始化张量流图并运行它进行大量迭代,在每一步更新生成的图像。
让我们详细介绍一下各个步骤。
互动会议
我们之前已经实现了总成本 J(G) 。我们现在将设置 TensorFlow 来针对 G 进行优化。
为此,我们的程序必须重置图形并使用一个“交互会话”。与常规会话不同,“交互式会话”将自己安装为默认会话来构建图表。这允许我们运行变量,而不需要经常引用会话对象(调用sess.run()
),这简化了代码。
# Reset the graph
tf.reset_default_graph()# Start interactive session
sess = tf.InteractiveSession()
内容图像
让我们加载、重塑和规范化我们的内容图像(卢浮宫博物馆图片):
content_image = scipy.misc.imread(“images/w_hotel.jpg”)
content_image = reshape_and_normalize_image(content_image)
风格图像
让我们载入、重塑并正常化我们的风格形象(克洛德·莫内的画):
style_image = scipy.misc.imread(“images/starry_night.jpg”)
style_image = reshape_and_normalize_image(style_image)
生成的图像与内容图像相关
现在,我们将生成的图像初始化为从content_image
创建的噪声图像。
生成的图像与内容图像略有关联。通过将生成的图像的像素初始化为主要是噪声但与内容图像稍微相关,这将有助于生成的图像的内容更快速地匹配内容图像的内容。
请随意在nst_utils.py
中查看 Github 回购中的generate_noise_image(...)
细节。
generated_image = generate_noise_image(content_image)
imshow(generated_image[0]);
生成 _ 噪声 _ 图像(内容 _ 图像)
加载预训练的 VGG19 模型
接下来,如前所述,我们将加载 VGG19 模型。
model = load_vgg_model(“pretrained-model/imagenet-vgg-verydeep-19.mat”)
内容成本
为了让程序计算内容成本,我们现在将指定a_C
和a_G
为适当的隐藏层激活。我们将使用层conv4_2
来计算内容成本。下面的代码执行以下操作:
- 将内容图像指定为 VGG 模型的输入。
- 设置
a_C
为张量,激活conv4_2
层的隐藏层。 - 将
a_G
设置为激活同一层隐藏层的张量。 - 使用
a_C
和a_G
计算内容成本。
注:此时,a_G
是张量,还没有求值。当我们运行下面model_nn()
中的张量流图时,将在每次迭代中对其进行评估和更新。
# Assign the content image to be the input of the VGG model.
sess.run(model[‘input’].assign(content_image))# Select the output tensor of layer conv4_2
out = model[‘conv4_2’]# Set a_C to be the hidden layer activation from the layer we have selected
a_C = sess.run(out)# Set a_G to be the hidden layer activation from same layer. Here, a_G references model[‘conv4_2’]
# and isn’t evaluated yet. Later in the code, we’ll assign the image G as the model input, so that
# when we run the session, this will be the activations drawn from the appropriate layer, with G as input.
a_G = out# Compute the content cost
J_content = compute_content_cost(a_C, a_G)
风格成本
# Assign the input of the model to be the “style” image
sess.run(model[‘input’].assign(style_image))# Compute the style cost
J_style = compute_style_cost(model, STYLE_LAYERS)
总成本
现在我们有了内容成本 ( J_content) 和样式成本 ( J_style ),通过调用total_cost()
计算总成本 J。
J = total_cost(J_content, J_style, alpha=10, beta=40)
【计算机】优化程序
这里,我使用 Adam 优化器来最小化总成本J
。
# define optimizer
optimizer = tf.train.AdamOptimizer(2.0)# define train_step
train_step = optimizer.minimize(J)
实施
***model_nn()***
函数初始化张量流图的变量,将输入图像(初始生成的图像)分配给作为 VGG19 模型的输入,运行张量(它是在该函数上面的代码中创建的)大量步骤。
运行以下代码片段来生成艺术图像。每 20 次迭代应该花费大约 3 分钟的 CPU 时间,但是在大约 140 次迭代之后,您开始观察到有吸引力的结果。神经类型转移通常使用 GPU 来训练。
model_nn(sess, generated_image)
你完了!运行此程序后,您应该会看到下图右侧所示的内容:
以下是其他几个例子:
- 具有梵高风格(星夜)的波斯波利斯(伊朗)古城的美丽遗迹
- Pasargadae 的居鲁士大帝墓,采用来自伊斯法罕的陶瓷 Kashi 风格。
- 具有抽象蓝色流体绘画风格的湍流的科学研究。
6.结论
你现在能够使用神经风格转移来生成艺术图像。神经风格转移是一种算法,即给定一个内容图像 C 和一个风格图像 S ,可以生成一个艺术图像。
它使用基于预训练的 ConvNet 的表示(隐藏层激活)。使用一个隐藏层的激活来计算内容成本函数;一层的样式成本函数使用该层激活的 Gram 矩阵来计算。使用几个隐藏层获得整体风格成本函数。
最后,优化总成本函数导致合成新图像。
7.引用和参考文献
Github 回购:https://github.com/TheClub4/artwork-neural-style-transfer
特别感谢deep learning . ai。图片由 deeplearning.ai 提供。
神经风格转移算法源于 Gatys 等人(2015 年)。Harish Narayanan 和 Github 用户“log0”也有可读性很高的文章,我们从中获得了灵感。在该实现中使用的预训练网络是 VGG 网络,这是由于 Simonyan 和 Zisserman (2015)。预训练的权重来自 MathConvNet 团队的工作。
- 利昂·a·加蒂斯、亚历山大·s·埃克、马蒂亚斯·贝赫(2015 年)。艺术风格的神经算法
- 用于艺术风格转移的卷积神经网络。
- Log0, TensorFlow 实现的“一种艺术风格的神经算法”。
- 卡伦·西蒙扬和安德鲁·齐泽曼(2015)。用于大规模图像识别的极深度卷积网络
- MatConvNet。
用 Python 生成 5 行二维码
或许在你的简历中使用它来链接到你的网站或 LinkedIn 个人资料
米蒂亚·伊万诺夫在 Unsplash 上拍摄的照片
快速响应码或 QR 码是一种二维条形码,由白色背景上的黑色小方块组成。由于其存储更多信息的能力和快速可读性,它比由黑条和空白组成的传统条形码更受欢迎。
Python 提供了一个 QRCode 包,这使得实现这个功能变得非常容易。我们将从下载以下 python 包开始。
pip install pillow
pip install qrcode
为了这个演示的目的,我使用了我的一篇文章的页面链接(10 行中的人脸检测)。我们将创建一个二维码来存储这篇文章的链接。
现在,让我们看看实现它有多简单。
import qrcode# Link for website
input_data = "[https://towardsdatascience.com/face-detection-in-10-lines-for-beginners-1787aa1d9127](/face-detection-in-10-lines-for-beginners-1787aa1d9127)"#Creating an instance of qrcode
qr = qrcode.QRCode(
version=1,
box_size=10,
border=5)qr.add_data(input_data)
qr.make(fit=True)img = qr.make_image(fill='black', back_color='white')
img.save('qrcode001.png')
上面使用的 QRCode 函数接受以下参数:
版本 :此参数定义生成的二维码的大小。它接受 1 到 40 范围内的整数值。该值越高,生成的 QR 码图像的尺寸越大。
box_size :该参数定义每个方框的大小,以像素为单位。
边框 :边框定义了边框的粗细。在这个例子中,值 5 意味着边框等于 5 个小黑盒的厚度。
add_data 方法用于传递我们输入的文本,在我们的例子中是文章的超链接。将 make 函数与 (fit=True) 配合使用,可确保二维码的整个维度都得到利用,即使我们的输入数据可以放入更少的框中。
最后一步是将其转换为图像文件并存储。为此,使用 make_image 功能,我们可以指定前景和背景颜色。我分别使用了黑色和白色,但是你可以根据你的喜好来改变它们。 保存 功能将图像保存为当前目录下的. png 文件。这里的也给出了代码和依赖关系的详细信息,以供参考。
这是我们为链接生成的二维码!
图片:阿林多姆·巴塔查尔吉
总之,我们研究了如何将文本值转换成相应的 QR 码。也许你可以为你自己的网址或 LinkedIn 个人资料创建一个二维码,并把它放在你的名片或简历中。干杯!
使用 Python OpenCV 在图像和视频中检测人脸的介绍。
towardsdatascience.com](/face-detection-in-10-lines-for-beginners-1787aa1d9127) [## 绝对初学者的神经网络
用简单的英语介绍感知器
medium.com](https://medium.com/swlh/artificial-neural-networks-for-absolute-beginners-a75bc1522e1d)
参考文献&延伸阅读:
【1】https://pypi.org/project/qrcode/
【2】【https://en.wikipedia.org/wiki/QR_code】T21*
生成样本数据集—数据科学家的必备技能。
制作 powerpoint 幻灯片和从理论上谈论你将如何处理数据是一回事。但创建一个样本数据集并展示一个已经在工作的仪表板、可视化或数据模型是另一回事。虽然可以在互联网上找到许多样本数据集,但它们通常并不完全符合您的需求。因此,在本文结束时,您将知道如何为您的特定目的创建一个漂亮的样本 CSV 数据集。一旦在 Jupyter 笔记本中设置了代码,您就可以反复使用它。
我们将如何生成样本数据集?
作为最终结果,一个样本 CSV 数据集与各种变量,如数字,货币,日期,名称和字符串,是可取的。这些变量有不同的类型,彼此独立或相关。首先,理解我们如何使用基本的“随机”函数来生成样本数据集是至关重要的。之后,我们将在一个数据框中组合变量。出于我们的目的,数据帧将最终导出为 CSV 格式。CSV 是一种非常容易与各种工具连接的格式,足以用于采样目的。
我们使用的工具
创建样本数据集的工具。
好的方面是,我们主要需要访问 python,使用它的一些库和工具。首先,我们使用 Jupyter Notebook,这是一个用于实时编码的开源应用程序,它允许我们用代码讲述一个故事。在我们的例子中,它简化了修改的过程,并且以后只使用很少的代码元素。此外,我们使用 NumPy,尤其是 NumPy 的随机函数来生成我们的变量。虽然这是一种选择,但是一个非常方便的名为 pydbgen 的库可以帮助我们加速样本数据集的生成。此外,我们导入了熊猫,这将我们的数据放在一个易于使用的结构中,用于数据分析和数据转换。
随机数据的基本函数
- “随机”模块 用 Python 创建随机数用的最多的模块大概就是 random.random()函数的 random 模块了。当导入模块并调用函数时,将生成一个介于 0.0 和 1.0 之间的浮点数,如下面的代码所示。
>>>import random>>>random.random()
0.18215964678315466
此外,还有设置种子的选项。这使我们能够复制代码中生成的随机数。
>>> random.seed(123)
>>> random.random()
0.052363598850944326>>> random.seed(123)
>>> random.random()
0.052363598850944326
如果您想为浮点数或整数考虑一个特定的范围,您可以如下定义函数的参数。
>>> random.randint(10,100)
21>>> random.uniform(10,100)
79.20607497283636
2。日期 在熟悉 random 方法的同时,我们现在可以应用这些知识在我们的样本数据集中创建随机日期。
>>> import numpy as np>>> monthly_days = np.arange(0, 30)
>>> base_date = np.datetime64('2020-01-01')
>>> random_date = base_date + np.random.choice(monthly_days)>>> print(random_date)
2020-01-07
有无数的变化来调整这段代码。将月天数范围更改为双月或每年。当然,基准日期应该符合您的要求。
3。字符串 当我们的样本数据集有了随机数和日期后,是时候看看字符串了。当查看下面的 randomString 函数时,变量“letters”包含小写的整个字母表。在“随机”方法的帮助下,一个随机数被创建并分配给 letter。就这么简单!
import random
import stringdef randomString(stringLength=15):
letters = string.ascii_lowercase
return ''.join(random.choice(letters) for i in range(stringLength))print ("The generated string is", randomString())The generated string is celwcujpdcdppzx
4。名字 虽然我们的样本数据集以前是基于随机方法的,但是仅仅通过随机地把东西放在一起是不可能生成有意义的表达式的。因此,我们使用 PyPi 上可用的名称包。这个包非常方便,提供了各种选项,如下面的代码片段所示。
>>> import names
>>> names.get_full_name()
‘Margaret Wigfield’>>> names.get_first_name(gender='female')
'Patricia'
5。地点、电话号码、地址或文本 在简短介绍了随机化的原则后用“random”作为基本方法,可能值得注意的是,有完整的软件包可以满足您的大部分需求。通过 pip 安装很简单,你可以在 Github 库上找到所有需要的信息。有两个非常有用的方法绝对值得一提:
6。机器学习的样本数据怎么样 当涉及到机器学习问题时,这项工作并不是通过生成任何随机数据来完成的。大多数情况下,您希望样本数据集的变量/特征之间存在某种关联或关系。Scikit 让你在几秒钟内创建这样的数据集。看看下面的示例代码:
import pandas as pd
from sklearn.datasets import make_regression# Generate fetures, outputs, and true coefficient of 100 samples
features, output, coef = make_regression(n_samples = 100,
# three features
n_features = 3,
# two features are useful,
n_informative = 2,
# one target
n_targets = 1,
# 0.0 standard deviation
noise = 0.0,
# show the true coefficient
coef = True)
# View the features of the first 10 rows
pd.DataFrame(features, columns=['Customer A', 'Customer B', 'Customer C']).head(10)# View the output of the first 10 rows
pd.DataFrame(output, columns=['Sales']).head(10)# View the actual, true coefficients used to generate the data
pd.DataFrame(coef, columns=['Coefficient Values'])
生成并导出您的 CSV 样本数据集
在探索、试验和生成我们的样本数据集之后,我们最终想要导出并使用它们。虽然文件格式没有限制,但由于我们没有大量的数据,并且希望将数据集插入到各种工具中,因此简单的 CSV 文件非常方便。但是首先必须创建一个数据帧。
import pandas as pd
import numpy as np
import random#using numpy's randint
df = pd.DataFrame(np.random.randint(0,100,size=(15, 4)), columns=list('ABCD'))# create a new column
df['AminusB'] = df['A'] - (0.1 * df['B'])
df['names'] = 'Laura'df.head(5)
虽然我们已经生成了一些随机的相关和不相关的数字,但是每行中的名称列是相同的。为了获得随机名称,我们必须对行进行迭代,并使用我们的随机名称生成器。
import namesfor index, row in df.iterrows():
df.at[index,'names'] = names.get_first_name(gender='female')
df.head(5)
请注意,您可以使用任何其他包或方法来创建示例 CSV 数据集,而不是名称。没有任何限制。现在,让我们将其导出为 CSV 格式,我们可以开始了。
#Export to csvexport_csv = df.to_csv (r'/Users/SampleDataset.csv', header=True) #Don't forget to add '.csv' at the end of the path
你成功了——祝贺你!现在,更令人兴奋的部分开始了,您可以开始处理您生成的 CSV 样本数据集。祝你好运。
您可能也喜欢阅读的文章:
用 PolyGen 和 PyTorch 生成三维模型
(论文中的图 6)使用 top-p=0.9 的 nucleus 采样和地面真实网格(蓝色)生成的图像条件样本(黄色)。
介绍
有一个新兴的深度学习研究领域,专注于将 DL 技术应用于 3D 几何和计算机图形应用,正如这个冗长的近期研究集合所证明的那样。如果你对这个主题感兴趣,看看吧。真是个兔子洞。对于希望自己尝试一些 3D 深度学习的 PyTorch 用户来说,高岭土库值得一看。对于 TensorFlow 用户,还有 TensorFlow 图形。一个特别热门的子领域是 3D 模型的生成。创造性地组合 3D 模型,从图像中快速生成 3D 模型,以及为其他机器学习应用和模拟创建合成数据只是 3D 模型生成的无数用例中的一小部分。
然而,在 3D 深度学习研究领域,为您的数据选择一种合适的表示方法是成功的一半。在计算机视觉中,数据的结构非常简单:图像由密集的像素组成,这些像素整齐而均匀地排列成一个精确的网格。3D 数据的世界没有这样的一致性。3D 模型可以表示为体素、点云、网格、多视图图像集等等。这些输入表示也有各自的缺点。例如,体素尽管计算成本高,但输出分辨率低。点云没有对表面或它们的法线进行编码,所以拓扑不能仅仅从点云推断出来。网格也不唯一地编码拓扑,因为任何网格都可以细分以产生相似的表面。这些缺点促使 DeepMind 的研究人员创建了 PolyGen ,这是一种用于网格的神经生成模型,它联合估计模型的面和顶点,以直接生成网格。官方实现可在 DeepMind GitHub 上获得。
研究
现在经典的 PointNet 论文提供了建模点云数据的蓝图,比如 3D 模型的顶点。它是一种通用算法,不会对 3D 模型的面或占用进行建模,因此单独使用 PointNet 不可能生成唯一的防水网格。 3D-R2N2 采用的体素方法将我们都熟悉的 2D 卷积扩展到 3D,并从 RGB 图像中自然产生无缝网格。然而,在更高的空间分辨率下,体素表示在计算上变得昂贵,有效地限制了它可以产生的网格的大小。
Pixel2Mesh 可以通过变形模板网格(通常是椭球体)从单幅图像中预测 3D 模型的顶点和面。目标模型必须与模板网格同胚,因此使用凸模板网格(如椭球体)会在高度非凸的对象(如椅子和灯)上引入许多虚假面。拓扑修改网络 (TMN)通过引入两个新阶段对 Pixel2Mesh 进行迭代:一个拓扑修改阶段,用于修剪增加模型重建误差的错误面;一个边界细化阶段,用于平滑由面修剪引入的锯齿状边界。如果你感兴趣,我强烈建议你也去看看地图集和分层表面预测。
同胚现象的经典例子(来源:维基百科)
虽然变形和细化模板网格的常见方法表现良好,但它始于关于模型拓扑的主要假设。在它的核心,一个 3D 模型只是一个 3D 空间中的顶点的集合,由单独的面组合和连接在一起。有没有可能避开中间的表示,直接预测这些顶点和面?
多种价元素
PolyGen 架构(本文主要关注蓝色虚线框内的部分)。
PolyGen 采用了一种相当独特的方法来完成模型生成任务,将 3D 模型表示为严格有序的顶点和面序列,而不是图像、体素或点云。这种严格的排序使他们能够应用基于注意力的序列建模方法来生成 3D 网格,就像伯特或 GPT 模型对文本所做的那样。
PolyGen 的总体目标有两个:首先为 3D 模型生成一组可信的顶点(可能受图像、体素或类别标签的限制),然后逐个生成一系列面,这些面将顶点连接在一起,并为该模型提供一个可信的表面。组合模型将网格上的分布 p(M) 表示为两个模型之间的联合分布:顶点模型 p(V) 表示顶点,面部模型 p(F|V) 表示以顶点为条件的面部。
顶点模型是一个解码器,它试图预测序列中的下一个标记,以前面的标记为条件(并且可选地以图像、体素字段或类标签为条件)。人脸模型包括一个编码器,后面跟着一个解码器指针网络,表示顶点序列上的分布。该指针网络有效地一次“选择”一个顶点,以添加到当前的面序列中,并构建模型的面。该模型同时以先前的面序列和整个顶点序列为条件。因为 PolyGen 架构非常复杂,并且依赖于各种各样的概念,这篇文章将仅限于顶点模型。如果这篇文章获得了关注,我将在后续文章中介绍人脸模型。
预处理顶点
流行的 ShapeNetCore 数据集中的每个模型都可以表示为顶点和面的集合。每个顶点由一个(x,y,z)坐标组成,该坐标描述了 3D 网格中的一个点。每个面都是一个索引列表,这些索引指向构成该面的角的顶点。对于三角形面,这个列表有 3 个索引长。对于 n 边形面,该列表是可变长度的。原始数据集相当大,所以为了节省时间,我提供了数据集的一个更轻量级的预处理子集,这里是供您试验。该子集仅由来自 5 个形状类别的模型组成,并且在被转换成 n 边形(如下所述)后具有少于 800 个顶点。
为了使序列建模方法起作用,数据必须以一种受约束的、确定性的方式来表示,以尽可能地消除可变性。出于这个原因,作者对数据集进行了一些简化。首先,他们将所有输入模型从三角形(连接 3 个顶点的面)转换为 n 边形(连接 n 个顶点的面),通过使用 Blender 的平面抽取修改器合并面。由于大型网格并不总是具有唯一的三角剖分,因此这使得相同拓扑的表示更加紧凑,并减少了三角剖分的不确定性。出于篇幅原因,我不会在这篇文章中详细讨论 Blender 脚本,但是许多资源,包括官方文档和GitHub上的优秀示例集,都很好地涵盖了这个主题。我提供的数据集已经过预抽取。
在平面模式的 Blender 中应用抽取修改器前后的 3D 模型,角度限制为 1.0 度。
要继续学习,请随意下载这个样本 cube.obj 文件。这个模型是一个有 8 个顶点和 6 个面的基本立方体。下面的简单代码片段从单个。对象文件。
def load_obj(filename):
"""Load vertices from .obj wavefront format file."""
vertices = []
with open(filename, 'r') as mesh:
for line in mesh:
data = line.split()
if len(data) > 0 and data[0] == 'v':
vertices.append(data[1:])
return np.array(vertices, dtype=np.float32)verts = load_obj(cube_path)
print('Cube Vertices')
print(verts)
其次,顶点从 z 轴(本例中为垂直轴)开始按升序排序,然后是 y 轴,最后是 x 轴。这样,模型顶点是自下而上表示的。在普通多边形模型中,顶点然后被连接成一维序列向量,对于较大的模型来说,这可能会以非常长的序列向量结束。作者在论文的附录 E 中描述了几个减轻这一负担的修改。
为了对顶点序列进行排序,我们可以使用字典排序。这与在字典中对单词进行排序时采用的方法相同。要对两个单词进行排序,你应该先看第一个字母,如果有并列的话,再看第二个,依此类推。对于单词“aardvark”和“apple”,第一个字母是“a”和“a”,所以我们移到第二个字母“a”和“p”来告诉我们“aardvark”在“apple”之前。在这种情况下,我们的“字母”依次是 z、y 和 x 坐标。
verts_keys = [verts[..., i] for i in range(verts.shape[-1])]
sort_idxs = np.lexsort(verts_keys)
verts_sorted = verts[sort_idxs]
最后,将顶点坐标归一化,然后量化,将其转换为离散的 8 位值。这种方法已经在像素递归神经网络和波网中用于模拟音频信号,使它们能够在顶点值上施加分类分布。在最初的 WaveNet 论文中,作者评论说“分类分布更灵活,可以更容易地模拟任意分布,因为它不对它们的形状做任何假设。”这种品质对于建模复杂的依赖关系(如 3D 模型中顶点之间的对称性)非常重要。
# normalize vertices to range [0.0, 1.0]
lims = [-1.0, 1.0]
norm_verts = (verts - lims[0]) / (lims[1] - lims[0])# quantize vertices to integers in range [0, 255]
n_vals = 2 ** 8
delta = 1\. / n_vals
quant_verts = np.maximum(np.minimum((norm_verts // delta), n_vals - 1), 0).astype(np.int32)
顶点模型
顶点模型由一个解码器网络组成,该网络具有转换器模型的所有标准特征:输入嵌入、18 个转换器解码器层的堆栈、层标准化,以及最后在所有可能的序列令牌上表达的 softmax 分布。给定长度为 N 的展平顶点序列 Vseq ,其目标是在给定模型参数的情况下最大化数据序列的对数似然性:
与 LSTM 不同,transformer 模型能够以并行方式处理顺序输入,同时仍然支持来自序列一部分的信息为另一部分提供上下文。这都要归功于他们的注意力模块。3D 模型的顶点包含各种各样的对称性和远距离点之间的复杂依赖关系。例如,想象一个典型的桌子,模型对角的桌腿是彼此的镜像版本。注意力模块允许对这些类型的模式进行建模。
输入嵌入
嵌入层是序列建模中使用的一种常见技术,用于将有限数量的表征转换为特征集。在一个语言模型中,“国家”和“民族”这两个词的含义可能非常相似,但与“苹果”这两个词的含义非常遥远。当单词用独特的符号表示时,就没有固有的相似或不同的概念。嵌入层将这些标记转换成矢量表示,在矢量表示中可以模拟有意义的距离感。
多边形将同样的原理应用于顶点。该模型利用了三种类型的嵌入图层:坐标图层用于指示输入令牌是 x、y 还是 z 坐标,值图层用于指示令牌的值,位置图层用于编码顶点的顺序。每一个都向模型传递一条关于令牌的信息。因为我们的顶点是一次在一个轴上输入的,所以坐标嵌入为模型提供了重要的坐标信息,让它知道给定值对应哪种类型的坐标。
coord_tokens = np.concatenate(([0], np.arange(len(quant_verts)) % 3 + 1, (n_padding + 1) * [0]))
值嵌入对我们之前创建的量化顶点值进行编码。我们还需要一些序列控制点:额外的开始和停止标记来分别标记序列的开始和结束,以及填充标记直到最大序列长度。
TOKENS = {
'<pad>': 0,
'<sos>': 1,
'<eos>': 2
}max_verts = 12 # set low for prototyping
max_seq_len = 3 * max_verts + 2 # num coords + start & stop tokens
n_tokens = len(TOKENS)
seq_len = len(quant_verts) + 2
n_padding = max_seq_len - seq_lenval_tokens = np.concatenate((
[TOKENS['<sos>']],
quant_verts + n_tokens,
[TOKENS['<eos>']],
n_padding * [TOKENS['<pad>']]
))
由于并行化而丢失的给定序列位置 n 的位置信息通过位置嵌入来恢复。也可以使用位置编码,一种不需要学习的封闭形式的表达式。在经典的 transformer 论文“注意力是你所需要的全部”中,作者定义了一种由不同频率的正弦和余弦函数组成的位置编码。他们通过实验确定位置嵌入的表现和位置编码一样好,但是位置编码的优势是可以外推得到比训练中遇到的序列更长的序列。关于位置编码的精彩视觉解释,请看这篇博文。
pos_tokens = np.arange(len(quant_tokens), dtype=np.int32)
生成所有这些令牌序列后,最后要做的是创建一些嵌入层并组合它们。每个嵌入层都需要知道预期的输入字典的大小和要输出的嵌入维度。每一层的嵌入维数是 256,这意味着我们可以用加法将它们组合起来。字典的大小取决于一个输入可以拥有的唯一值的数量。对于值嵌入,它是量化值的数量加上控制标记的数量。对于坐标嵌入,对于每个坐标 x、y 和 z 是一个,对于以上任何一个都是一个(控制标记)。最后,对于每个可能的位置或最大序列长度,位置嵌入需要一个。
n_embedding_channels = 256# initialize value embedding layer
n_embeddings_value = 2 ** n_bits + n_tokens
value_embedding = torch.nn.Embedding(n_embeddings_value,
n_embedding_channels, padding_idx=TOKENS['<pad>'])# initialize coordinate embedding layer
n_embeddings_coord = 4
coord_embedding = torch.nn.Embedding(n_embeddings_coord,
n_embedding_channels)# initialize position embedding layer
n_embeddings_pos = max_seq_len
pos_embedding = torch.nn.Embedding(n_embeddings_pos,
n_embedding_channels)# pass through layers
value_embed = self.value_embedding(val_tokens)
coord_embed = self.coord_embedding(coord_tokens)
pos_embed = self.pos_embedding(pos_tokens)# merge
x = value_embed + coord_embed + pos_embed
序列屏蔽
transformer 模型如此并行化的另一个结果是什么?对于在时间 n 的给定输入令牌,模型实际上可以“看到”序列中稍后的目标值,当您试图仅根据先前的序列值来调整模型时,这就成了一个问题。为了防止模型关注无效的未来目标值,可以在自我关注层中的 softmax 步骤之前用-Inf
屏蔽未来位置。
n_seq = len(val_tokens)
mask_dims = (n_seq, n_seq)
target_mask = torch.from_numpy(
(val_tokens != TOKENS['<pad>'])[..., np.newaxis] \
& (np.triu(np.ones(mask_dims), k=1).astype('uint8') == 0))
PolyGen 还大量使用无效预测遮罩来确保它生成的顶点和面序列编码有效的 3D 模型。例如,必须执行诸如“z 坐标不递减”和“停止标记只能出现在完整的顶点(z、y 和 x 标记的三元组)”之类的规则,以防止模型产生无效的网格。作者在论文的附录 F 中提供了他们使用的掩蔽的详细列表。这些约束仅在预测时强制执行,因为它们实际上会损害训练性能。
细胞核取样
与许多序列预测模型一样,该模型是自回归的,这意味着给定时间步长的输出是下一个时间步长的可能值的分布。一次预测一个标记的整个序列,模型在每一步浏览来自先前时间步骤的所有标记以选择其下一个标记。解码策略决定了它如何从这个分布中选择下一个令牌。
如果使用次优解码策略,生成模型有时会陷入重复循环,或者会产生质量差的序列。我们都见过生成的文本看起来像废话。PolyGen 采用一种叫做核取样的解码策略来产生高质量的序列。原始论文在文本生成上下文中应用了这种方法,但是它也可以应用于顶点。前提很简单:仅从 softmax 分布中共享 top-p 概率质量的令牌中随机抽取下一个令牌。这在推理时应用,以产生网格,同时避免序列退化。关于核采样的 PyTorch 实现,请参考本要点。
条件输入
除了无条件生成模型,PolyGen 还支持使用类别标签、图像和体素的输入条件。这些可以指导生成具有特定类型、外观或形状的网格。类别标签通过嵌入进行投影,然后添加到每个关注块的自我关注层之后。对于图像和体素,编码器创建一组嵌入,然后用于与变换器解码器的交叉注意。
结论
PolyGen 模型描述了一个强大、高效和灵活的框架,用于有条件地生成 3D 网格。序列生成可以在各种条件和输入类型下完成,从图像到体素到简单的类标签,甚至除了起始标记什么都不是。顶点模型表示网格顶点上的分布,它只是联合分布难题的一部分。我打算在以后的文章中介绍人脸模型。与此同时,我鼓励您查看来自 DeepMind 的 TensorFlow 实现,并尝试一下条件模型生成!
使用 StyleGAN2 生成动画角色
了解如何生成这个很酷的动画人脸插值
生成的 StyleGAN 插值[图片由作者提供]
生成对抗网络
生成对抗网络是一种能够生成新内容的生成模型。由于其有趣的应用,如生成合成训练数据、创建艺术、风格转换、图像到图像的翻译等,该主题在机器学习社区中变得非常流行。
甘建筑[图片由作者提供]
GAN 由两个网络组成,发生器、和鉴别器。生成器将尝试生成假样本,并欺骗鉴别器,使其相信这是真样本。鉴别器将尝试从真样本和假样本中检测生成的样本。这个有趣的对抗性概念是由 Ian Goodfellow 在 2014 年提出的。已经有很多资源可用于学习 GAN,因此我将不解释 GAN 以避免冗余。
我推荐阅读约瑟夫·罗卡的这篇优美的文章来了解甘。
一步一步地建立导致 GANs 的推理。
towardsdatascience.com](/understanding-generative-adversarial-networks-gans-cd6e4651a29)
StyleGAN2
StyleGAN 论文《一种基于风格的 GANs 架构》,由 NVIDIA 于 2018 年发布。该论文为 GAN 提出了一种新的生成器架构,允许他们控制所生成样本的不同层次的细节,从粗略细节(如头部形状)到精细细节(如眼睛颜色)。
StyleGAN 还结合了来自 Progressive GAN 的想法,其中网络最初在较低的分辨率(4x4)上训练,然后在稳定后逐渐增加更大的层。这样做,训练时间变得快了很多,训练也稳定了很多。
渐进成长的甘【来源:莎拉·沃尔夫】
StyleGAN 通过添加一个映射网络对其进行了进一步改进,该映射网络将输入向量编码到一个中间潜在空间 w 中,然后这些潜在空间将有单独的值用于控制不同级别的细节。
StyleGAN 生成器架构[图片由作者提供]
为什么要添加映射网络?
甘的问题之一是它的纠缠潜在表象(输入向量, z )。例如,假设我们有二维潜在代码,它代表脸的大小和眼睛的大小。在这种情况下,脸的大小与眼睛的大小密切相关(眼睛越大,脸也越大)。另一方面,我们可以通过存储面部和眼睛的比例来简化这一点,这将使我们的模型更简单,因为非纠缠的表示更容易被模型解释。
对于纠缠表示,数据分布可能不一定遵循我们想要对输入向量 z 进行采样的正态分布。例如,数据分布会有一个像这样的缺角,它表示眼睛和脸的比例变得不真实的区域。
如果我们从正态分布中对 z 进行采样,我们的模型也会尝试生成缺失区域,其中的比率是不现实的,并且因为没有具有这种特征的训练数据,所以生成器生成的图像会很差。因此,映射网络旨在解开潜在表示并扭曲潜在空间,以便能够从正态分布中采样。
此外,在每个级别上具有单独的输入向量 w ,允许生成器控制不同级别的视觉特征。前几层(4x4,8x8)将控制更高层次(更粗糙)的细节,如头型、姿势和发型。最后几层(512x512,1024x1024)将控制细节的精细程度,如头发和眼睛的颜色。
粗糙层次细节的变化(头型、发型、姿势、眼镜)【来源:论文】
精细层次细节的变化(头发颜色)【来源:纸张】
关于 StyleGAN 架构的全部细节,我推荐你阅读 NVIDIA 关于其实现的官方论文。这是论文中完整架构的插图。
随机变化
StyleGAN 还允许您通过在相应的层给出噪声来控制不同细节级别的随机变化。随机变化是图像上的微小随机性,不会改变我们的感知或图像的身份,如不同的梳理头发,不同的头发放置等。你可以在下面的动画图像中看到变化的效果。
粗略随机变化【来源:论文】
精细随机变异【来源:论文】
StyleGAN 还做了其他几个改进,我不会在这些文章中讨论,比如 AdaIN 规范化和其他规范化。你可以阅读官方文件、乔纳森·惠的这篇文章,或者拉尼·霍雷夫的这篇文章来了解更多细节。
截断绝招
当训练样本中存在未充分表示的数据时,生成器可能无法学习样本,并且生成的样本很差。为了避免这种情况,StyleGAN 使用了一种“截断技巧”,即截断中间潜在向量 w ,迫使其接近平均值。
𝚿 (psi)是用于截断和重新采样高于阈值的潜在向量的阈值。因此,随着更高的𝚿,你可以在生成的图像上获得更高的多样性,但它也有更高的机会生成怪异或破碎的脸。根据 Gwern ,对于该网络,0.5 至 0.7 的𝚿值似乎给出了具有足够多样性的良好图像。不过,请随意试验阈值。
在 0.3 磅/平方英寸(左)和 0.7 磅/平方英寸(中)和 1.3 磅/平方英寸(右)下生成的 3×3 网格图像
生成动漫人物
我将使用 Aaron Gokaslan 预先训练的动画 StyleGAN2,这样我们可以直接加载模型并生成动画脸。所以,打开你的 Jupyter 笔记本或者 Google Colab,让我们开始编码吧。
注: 如果卡住了可以参考我的 Colab 笔记本
所以首先要克隆 styleGAN 回购。
*$ git clone https://github.com/NVlabs/stylegan2.git*
**如果你使用的是 Google Colab,你可以在命令前面加上“!”作为命令运行它:!https://github.com/NVlabs/stylegan2.gitgit 克隆
接下来,我们需要下载预先训练的权重并加载模型。当您使用 Google Colab 时,请确保您正在运行 GPU 运行时,因为模型被配置为使用 GPU。
该代码修改自本笔记本
现在,我们需要生成随机向量, z,作为生成器的输入。让我们创建一个函数来从给定的种子生成潜在代码 z。
然后,我们可以创建一个函数来获取生成的随机向量 z 并生成图像。
现在,我们可以尝试生成一些图像,看看结果。
该函数将返回一个数组PIL.Image
。在 Google Colab 中,你可以通过打印变量直接显示图像。这是第一个生成的图像。
作者图片
让我们用图像网格来显示它,这样我们就可以一次看到多个图像。
然后我们可以在一个 3x3 的网格中显示生成的图像。
作者图片
GAN 的一个优点是它有一个平滑连续的潜在空间,不像 VAE(自动变分编码器)那样有间隙。因此,当您在潜在空间中取两个点(这将生成两个不同的面)时,您可以通过在这两个点之间取一条线性路径来创建两个面的过渡或插值。
潜在空间的插值【来源:约瑟夫·罗卡】
让我们用代码实现它,并创建一个函数在 z 向量的两个值之间进行插值。
我们来看看插值结果。你可以看到第一个图像逐渐过渡到第二个图像。
作者图片
现在我们已经完成了插值。我们终于可以尝试制作上面缩略图中的插值动画了。我们将使用moviepy
库来创建视频或 GIF 文件。
当你运行代码时,它会生成一个插值的 GIF 动画。您还可以使用顶部的变量修改持续时间、网格大小或 fps。
生成的 StyleGAN2 插值 GIF[图片由作者提供]
如果你能走到这一步,恭喜你!您已经使用 StyleGAN2 生成了动画人脸,并学习了 GAN 和 StyleGAN 架构的基础知识。
接下来是什么?
现在我们已经完成了,你还能做些什么来进一步改进呢?这里有一些你可以做的事情。
其他数据集 显然,StyleGAN 不仅限于动漫数据集,还有许多可用的预训练数据集可供您使用,如真实人脸、猫、艺术和绘画的图像。查看此 GitHub repo 了解可用的预训练重量。另一方面,您也可以使用自己选择的数据集来训练 StyleGAN。
有条件的 GAN 目前,我们无法真正控制我们想要生成的特征,如头发颜色、眼睛颜色、发型和配饰。条件 GAN 允许您在输入向量 z 旁边给出一个标签,从而将生成的图像调整为我们想要的样子。或者,你可以试着通过回归或手动来理解潜在空间。如果你想往这个方向发展, Snow Halcy repo 也许能帮到你,就像他在这个 J upyter 笔记本里做的那样,甚至让它互动起来。
确认
我要感谢Gwern Branwen他的广泛的文章和我在文章中强烈提到的关于用 StyleGAN 生成动画脸的解释。我强烈建议你访问他的网站,因为他的作品是知识的宝库。此外,请访问这个网站,该网站托管了生成动画人脸的 StyleGAN 模型和生成动画情节的 GPT 模型。**
如果你喜欢我的文章,请随意查看我的其他文章!
先睹为快数字艺术的未来
towardsdatascience.com](/animating-yourself-as-a-disney-character-with-ai-78af337d4081) [## 在没有数据集的情况下生成新内容
重写 GAN 中的规则:上下文相关的复制和粘贴特性
towardsdatascience.com](/generating-novel-content-without-dataset-544107da4cc8)
参考
[1]t .卡拉斯、s .莱恩和 t .艾拉(2019 年)。一种基于风格的生成对抗网络生成器体系结构。在IEEE 计算机视觉和模式识别会议论文集(第 4401–4410 页)中。
[2]https://www . gwern . net/Faces # style gan-2
为 PyTorch 生成批处理数据
由肯尼斯·贝里奥斯·阿尔瓦雷斯在 Unsplash 上拍摄
实践中的深度学习
为 PyTorch 创建定制数据加载器——变得简单!
我正在创建一个定制的 PyTorch 培训模块,这个模块过于复杂,尤其是在生成培训批次并确保这些批次在培训期间不会重复的时候。“这是一个已解决的问题”,我在实验室的深处疯狂地编码时心想。
从数据集中选择项目时,不希望只增加索引是有原因的。1)这无法扩展到多个员工。2)你需要随机化你的序列以最大化训练效果。
这就是 Torch 的数据工具(torch.utils.data
)派上用场的地方。您不应该从头开始创建批处理生成器。
您可以采取两种方法。1)在创建数据集之前移动所有预处理,并仅使用数据集生成项目,或者 2)在数据集的初始化步骤中执行所有预处理(缩放、移位、整形等)。如果你只使用火炬,方法#2 是有意义的。我使用多个后端,所以我使用方法 1。
步伐
- 创建自定义数据集类。你重写了
__len__()
和__getitem__()
方法。 - 创建一个使用
torch.utils.data.dataloader
的迭代器 - 在训练循环中使用这个迭代器。
简单。瞧啊。
关于如何使用的示例,请参见附加的代码。
摘要
在本文中,我们回顾了向 PyTorch 训练循环提供数据的最佳方法。这打开了许多感兴趣的数据访问模式,有助于更轻松、更快速的培训,例如:
- 使用多个进程读取数据
- 更加标准化的数据预处理
- 在多台机器上使用数据加载器进行分布式培训
感谢阅读!
如果你喜欢这个,你可能会喜欢:
演示如何使用 LSTM 自动编码器分析多维时间序列
towardsdatascience.com](/using-lstm-autoencoders-on-multidimensional-time-series-data-f5a7a51b29a1)
使用 T5 文本到文本转换器模型从任何内容生成布尔型(是/否)问题
使用 BoolQ 数据集和 T5 文本到文本转换器模型的问题生成算法的训练脚本和预训练模型。
图片来自 Pixabay
投入
我们程序的输入将是任何一般的内容/段落
**Months earlier, Coca-Cola had begun “Project Kansas.” It sounds like a nuclear experiment but it was just a testing project for the new flavor. In individual surveys, they’d found that more than 75% of respondents loved the taste, 15% were indifferent, and 10% had a strong aversion to the taste to the point that they were angry.**
输出
输出将是从上述输入生成的布尔(是/否)问题。
布尔(是/否)从 T5 模型生成的问题:
**1: Does coca cola have a kansas flavor?
2: Is project kansas a new coca cola flavor?
3: Is project kansas the same as coca cola?**
今天我们将看看如何从 Huggingface 的变形金刚库中训练一个 T5 模型来生成这些布尔问题。我们还将看到如何使用提供的预训练模型来生成这些布尔(是/否)问题。
实际使用案例(学习聊天机器人)
来自平面图标的图标
我想象一个场景,作为一名学生,你和一个聊天机器人互动学习一个概念。聊天机器人会根据你的回答向你展示教科书章节中相关的字节大小的片段。它还想让实时评估你是否已经理解了提出的主题。为课本章节的每个片段手动预生成评估是不切实际的。聊天机器人可以利用这个算法实时生成布尔(是/否)问题,评估你对题目的理解。
让我们开始吧—
资料组
来自平面图标的图标
我用BoolQ数据集收集了段落、问题和答案三元组,准备了训练集和验证集。****
boolQ 数据集具有以下格式-
**{
**"question"**: "is france the same timezone as the uk",
**"passage"**: "At the Liberation of France in the summer of 1944, Metropolitan France kept GMT+2 as it was the time then used by the Allies (British Double Summer Time). In the winter of 1944--1945, Metropolitan France switched to GMT+1, same as in the United Kingdom, and switched again to GMT+2 in April 1945 like its British ally. In September 1945, Metropolitan France returned to GMT+1 (pre-war summer time), which the British had already done in July 1945\. Metropolitan France was officially scheduled to return to GMT+0 on November 18, 1945 (the British returned to GMT+0 in on October 7, 1945), but the French government canceled the decision on November 5, 1945, and GMT+1 has since then remained the official time of Metropolitan France."
**"answer"**: false,
**"title"**: "Time in France",
}**
有一段“短文,有对应的“问题和正确的布尔“答案”——对或错。
我们会详细讨论你如何-
- 使用我的预训练的模型为任何给定的内容生成布尔问题。
- 使用我的训练代码和数据集在你自己的 GPU 机器上复制结果。
训练算法— T5
用扁平图标生成的图标
T5 是 Google 的一个新的 transformer 模型,它以端到端的方式进行训练,将文本作为输入,将修改后的文本作为输出。你可以在这里了解更多。
它使用在大型文本语料库上训练的文本到文本转换器,在多个 NLP 任务上实现了最先进的结果,如摘要、问题回答、机器翻译等。
我将“通道”和“答案”作为输入给我的 T5 变压器模型,并训练它生成“问题”作为输出。
密码
使用预训练模型和训练具有给定数据的模型的所有代码可在-
**** [## ramsrigouthamg/generate _ boolean _ questions _ using _ T5 _ transformer
使用这个程序,您可以从任何内容中生成布尔型(是/否)问题。一篇详细的媒体博文解释了…
github.com](https://github.com/ramsrigouthamg/generate_boolean_questions_using_T5_transformer)
使用预先训练的模型
Python 文件 t5_inference.py 包含了下面给出的所有代码。
首先,安装必要的库-
!pip install torch==1.4.0
!pip install transformers==2.9.0
!pip install pytorch_lightning==0.7.5
以任何文本/段落作为输入运行推理,查看生成的布尔问题
上述代码的输出为-
**Context: ** Months earlier, Coca-Cola had begun “Project Kansas.” It sounds like a nuclear experiment but it was just a testing project for the new flavor. In individual surveys, they’d found that more than 75% of respondents loved the taste, 15% were indifferent, and 10% had a strong aversion to the taste to the point that they were angry.**Beam decoding [Most accurate questions] ::**Does coca cola have a kansas flavor?
Is project kansas the same as coca cola?
Is project kansas a new coca cola flavor?**TopKP decoding [Not very accurate but more variety in questions] ::**Does coca cola have a koala flavor?
Is kakao the same as project kansas?
Was project ksoda a real thing?**Time elapsed 1.2351574897766113**
训练你自己的模型
所有用于训练的训练代码和数据集都可以在提到的 Github repo 中获得。我们将经历我用来训练模型的步骤。
1.数据准备
文件boolQ _ prepare _ train _ validation _ dataset . ipynb包含准备训练和验证数据集的所有代码。我将 boolQ 数据集作为 JSON 文件,并将其转换为 csv 文件。
2.培养
感谢 Suraj Patil 给了我们一个神奇的 Colab 笔记本来训练 T5 完成任何文本到文本的任务。我从 Colab 笔记本上借用了大部分训练代码,只更改了数据集类和训练参数。我使 dataset 类适应了我们的 boolQ 数据集。
培训代码可作为培训使用。Github 回购中的 py。
你需要做的就是在任一台 GPU 机器上克隆repo,安装 requirements.txt ,运行 train.py 来训练 T5 模型。
在 p2.xlarge (AWS ec2)上训练该模型 4 个时期(默认)需要大约 5-6 个小时。
数据集类如下所示—
关键是我们如何向 T5 模型培训师提供我们的输入和输出。我将“通道”和“答案”作为输入给我的 T5 变压器模型,并训练它生成“问题”作为输出,如下所示-
输入格式到 T5 进行训练
**truefalse**: yes **passage**: At the Liberation of France in the summer of 1944, Metropolitan France kept GMT+2 as it was the time then used by the Allies (British Double Summer Time). In the winter of 1944--1945, Metropolitan France switched to GMT+1, same as in the United Kingdom, and switched again to GMT+2 in April 1945 like its British ally. In September 1945, Metropolitan France returned to GMT+1 (pre-war summer time), which the British had already done in July 1945\. Metropolitan France was officially scheduled to return to GMT+0 on November 18, 1945 (the British returned to GMT+0 in on October 7, 1945), but the French government canceled the decision on November 5, 1945, and GMT+1 has since then remained the official time of Metropolitan France. **</s>**
输出格式到 T5 进行训练
Is france the same timezone as the uk? **</s>**
注:文本" truefalse : yes "或" truefalse : no "应生成一个适当的布尔问题,其答案为文本中给出的" yes "或" no "。但是我尝试训练 T5 时,效果并不好。所以确保你不要依赖对生成的布尔问题给出的答案的初始标记“是”或“否”。
总数据集只有大约 ~10k 个样本,因此生成的问题质量有时不是很好。请注意。****
祝 NLP 探索愉快,如果你喜欢它的内容,请随时在 Twitter 上找到我。
如果你想学习使用变形金刚的现代自然语言处理,看看我的课程使用自然语言处理的问题生成
用神经网络产生啁啾
将模型嫁接在一起并迭代调用生成器
鸟鸣的声音变化多样,优美,令人放松。在前 Covid 时代,我制作了一个聚焦定时器,它会在休息时播放一些录制的鸟鸣声,我总是想知道是否能产生这样的声音。经过反复试验,我找到了一个概念验证的架构,它既可以成功地再现单个啁啾声,又可以调整参数来改变生成的声音。
由于生成鸟鸣似乎是一个有点新奇的应用,我认为这种方法值得分享。一路走来,我还学会了如何将 TensorFlow 模型拆开,并将它们的一部分嫁接在一起。下面的代码块展示了这是如何实现的。完整的代码可以在这里找到。
理论上的方法
发电机将由两部分组成。第一部分将把整个声音和关于其整体形状的关键信息编码成少量的参数。
第二部分将获取一小段声音,以及关于整体形状的信息,并预测下一小段声音。
第二部分可以使用调整后的参数对其自身进行迭代调用,以产生全新的啁啾!
编码参数
一个自动编码器结构用于导出声音的关键参数。在从一系列扩展(解码)层完全再现声音之前,这种结构采用整个声波,并通过一系列(编码)层将其减少到少量组件(腰部)。一旦经过训练,自动编码器模型在腰部被切断,因此它所做的只是将完整的声音降低到关键参数。
为了验证概念,使用了单个啁啾;这唧唧声:
使用啁啾的声波表示。
它来自康奈尔大学的鸟鸣指南:北美必备套装。用于鸟鸣铬实验的同一套。
仅使用单一声音的一个问题是,自动编码器可能会简单地在解码层的偏差中隐藏关于声音的所有信息,从而使腰部的权重全部为零。为了减轻这一点,在训练过程中通过改变声音的振幅和稍微移动来改变声音。
自动编码器的编码器部分由一系列卷积层组成,这些卷积层将 3000 多长的声波压缩成大约 20 个数字,有望保留沿途的重要信息。由于声音由许多不同的正弦波组成,允许许多不同大小的卷积滤波器通过声音在理论上可以捕获关于复合波的关键信息。选择 20 的腰围尺寸主要是因为这似乎是一个可调节参数的可克服的数目。
在第一种方法中,各层被顺序堆叠。在未来的版本中,使用类似于 inception-net 块的结构来并行运行不同大小的卷积可能是有利的。
模型的解码器部分由两个密集层组成,一个长度为 400,另一个长度为 3000,与输入声音的长度相同。最后一层的激活函数是 tanh,因为声波表示的值在-1 和 1 之间。
这看起来是这样的:
自动编码器网络的表示。用 PlotNeuralNet 制作。
这是代码:
培训发电机
发生器的结构从自动编码器网络的编码部分开始。腰部的输出与一些新的输入相结合,这些新的输入表示即将被预测的声波之前的声波比特。在这种情况下,声波的前 200 个值被用作输入,然后预测下 10 个值。
组合的输入被输入到一系列密集层中。连续的密集层允许网络学习先前的值、关于声音的整体形状的信息和随后的值之间的关系。最终的致密层长度为 10,并且用双曲正切函数激活。
这是这个网络的样子:
带有自动编码器网络嫁接部分的发电机网络。用 PlotNeuralNet 制作。
来自自动编码器网络的层被冻结,使得额外的训练资源不花费在它们上面。
发出一些声音
训练这个网络只需要几分钟,因为数据变化不大,因此相对容易学习,特别是对于自动编码器网络。最后一个亮点是从训练好的模型中产生两个新的网络。
第一个只是自动编码器的编码器部分,但现在是分开的。我们需要这部分来产生一些初始的好参数。
第二个模型与发生器网络相同,但自动编码器网络的部分被新的输入源所取代。这样做是为了使经过训练的发生器不再需要整个声波作为输入,而只需要捕获声音关键信息的编码参数。将这些分离出来作为新的输入,我们可以在产生啁啾时自由地操纵它们。
以下声音是在没有修改参数的情况下生成的,它们非常接近原始声音,但不是完美的再现。发电机网络只能达到 60%到 70%之间的精度,因此预计会有一些变化。
不修改编码参数而生成的声音。
修改参数
产生鸟鸣的好处部分在于可以产生主题的新变化。这可以通过修改编码器网络产生的参数来实现。在上面的例子中,编码器产生了这些参数:
不是所有的 20 个节点都产生非零参数,但是有足够多的节点可以实验。有 12 个可调参数,可以在两个方向上调整到任意程度,这是一个非常复杂的问题。由于这是一个概念验证,因此在每种情况下,只需调整一个参数,就可以产生一些选择声音:
每次修改其中一个编码参数后产生的声音。
以下是三个例子的声波表示:
产生啁啾的声波表示。
后续步骤
使用神经网络来产生鸟的声音似乎是可能的,尽管其可行性还有待观察。上述方法仅使用单一声音,因此下一步将尝试在多种不同声音上训练模型。从一开始就不清楚这是否可行。然而,如果构建的模型在多种声音上失败,仍然可以在不同的声音上训练不同的模型,并简单地将它们堆叠起来以产生不同的声音。
一个更大的问题是,并非所有产生的声音都是可行的,尤其是在修改参数时。相当一部分产生的声音更像电脑发出的哔哔声,而不是鸟鸣声。有些听起来像一台愤怒的计算机,它真的不想让你做你刚刚试图做的事情。减轻这种情况的一种方法是训练一个单独的模型来检测鸟的声音(也许沿着这些线),并使用它来拒绝或接受产生的输出。
计算成本也是当前方法的一个限制因素;生成啁啾声比播放声音需要多一个数量级的时间,如果是为了动态生成美丽的声景,这并不理想。这里想到的主要缓解措施是增加每次预测的长度,可能会以准确性为代价。当然,人们也可以简单地花时间预先生成可接受的声音场景。
结论
自动编码器网络和短期预测网络的组合可以被嫁接在一起,以产生具有一些可调节参数的鸟声发生器,这些参数可以被操纵以产生新的和有趣的鸟声。
和许多项目一样,部分动机是在过程中学习。特别是,我不知道如何把训练好的模型拆开,然后把它们的一部分移植到一起。上面使用的模型可以作为一个例子来指导其他想要尝试这种方法的学习者。
使用张量流和 LSTM 递归神经网络生成烹饪食谱:一步一步指南
照片由 home_full_of_recipes 拍摄(Instagram 频道)
TL;速度三角形定位法(dead reckoning)
我用 TensorFlow 在 ~100k 食谱数据集上训练了一个人物级别的 LSTM (长短期记忆)**,它建议我做“奶油苏打加洋葱”、、【松饼草莓汤】、、、、、【三文鱼牛肉慕斯和墨西哥胡椒斯蒂尔顿沙拉】**
在这里,你可能会找到更多我最后得到的例子:
- 🎨 烹饪食谱生成器演示——在你的浏览器中交互式地尝试该模型。
- 🏋🏻 LSTM 模型训练流程——看看模型是怎么训练出来的。
- 🤖交互式机器学习实验 知识库——查看更多“物体检测”、“草图识别”、“图像分类”等实验。
本文包含了 LSTM 模型如何在 Python 上使用 TensorFlow 2 和 Keras API 进行实际训练的细节。
来自机器学习实验的屏幕记录
我们的模型最终会学到什么
经过几个小时的训练,我们的角色级 RNN 模型将学习英语语法和标点符号的基本概念(我希望我能这么快学会英语!).它还将学习如何生成配方的不同部分,如📗【配方名称】,🥕【配方成分】和📝【食谱说明】。有时候食谱名称、配料和说明会很有趣,有时候很愚蠢,有时候很有趣。
下面是几个生成的配方示例:
*📗 [NAME]Orange Club Tea Sandwich Cookies🥕 [INGREDIENTS]• 1 cup (2 sticks) unsalted butter, softened
• 1 cup confectioners' sugar
• 1/2 cup flaxseed meal
• 1/2 cup shelled pumpkin seeds (pecans, blanched and sliced)
• 2 teaspoons vanilla extract📝 [INSTRUCTIONS]▪︎ Preheat oven to 350 degrees F.
▪︎ Combine cake mix, milk, egg and sugar in a large bowl. Stir until combined and smooth but not sticky. Using a spatula, sprinkle the dough biscuits over the bottom of the pan. Sprinkle with sugar, and spread evenly. Bake for 20 minutes. Remove from the oven and cool on a rack. To serve, add the chocolate.*
或者另一个:
*📗 [NAME]Mushrooms with Lentil Stewed Shallots and Tomatoes🥕 [INGREDIENTS]• 1 tablespoon olive oil
• 3 cloves garlic, smashed
• Kosher salt
• 1 1/2 pounds lean ground turkey
• 1 cup coarsely peeled tart apples
• 2 tablespoons chopped garlic
• 1 teaspoon ground cumin
• 1/2 teaspoon cayenne pepper
• 1 teaspoon chopped fresh thyme
• 3/4 cup chopped fresh basil
• 1/2 small carrot, halved lengthwise and cut into 1/2-inch pieces
• 1 roasted red pepper, halved and sliced vertically diced and separated into rough chops
• 3 tablespoons unsalted butter
• 2 cups shredded mozzarella
• 1/4 cup grated parmesan cheese
• 1/4 cup prepared basil pesto📝 [INSTRUCTIONS]▪︎ Stir the olive oil, garlic, thyme and 1 teaspoon salt in a saucepan; bring to a simmer over medium heat. Remove from the heat. Add the basil and toast the soup for 2 minutes.
▪︎ Meanwhile, heat 4 to 4 inches vegetable oil in the skillet over medium-high heat. Add the olive oil, garlic, 1/2 teaspoon salt and 1/2 teaspoon pepper and cook, stirring often, until cooked through, a*
⚠️ 本文中的食谱只是为了娱乐和学习目的而制作的。食谱是 而不是 用于实际烹饪!
先验知识
假设你已经熟悉递归神经网络的概念,特别是长短期记忆(LSTM) 架构。
ℹ️,如果这些概念对你来说是新的,我强烈推荐你参加 Coursera 的深度学习专业课程。浏览一下 Andrej Karpathy 的文章中的循环神经网络的不合理的有效性可能也是有益的。**
在高层次上,递归神经网络(RNN) 是一类深度神经网络,最常用于基于序列的数据,如语音、声音、文本或音乐。它们用于机器翻译、语音识别、语音合成等。rnn 的关键特征是它们是有状态的,并且它们具有内部存储器,其中可以存储序列的一些上下文。例如,如果序列的第一个单词是He
,RNN 可能向speaks
建议下一个单词,而不仅仅是speak
(以形成He speaks
短语),因为关于第一个单词He
的先验知识已经在内存中。
图片来源:递归神经网络维基百科上的文章
图片来源:LSTM 和 GRU关于数据科学的图文并茂的文章
令人兴奋的是,RNN(尤其是 LSTM)不仅能记住单词对单词的依存关系,还能记住字符对字符的依存关系!序列由什么组成并不重要:可能是单词,也可能是字符。重要的是它们形成了一个时间分布的序列。例如,我们有一个字符序列['H', 'e']
。如果我们问 LSTM 下一步可能会做什么,它可能会建议一个<stop_word>
(意思是,组成单词He
的序列已经完成,我们可以停止),或者它也可能会建议一个字符l
(意思是,它试图为我们建立一个Hello
序列)。这种类型的 rnn 被称为字符级 rnn(与单词级 rnn相对)。
在本教程中,我们将依靠 RNN 网络的记忆功能,我们将使用一个角色级别的 LSTM 版本来生成烹饪食谱。
探索数据集
让我们浏览几个可用的数据集,并探讨它们的优缺点。我希望数据集满足的一个要求是,它不仅应该有一个配料列表,还应该有烹饪说明。我还希望它有一个措施和每种成分的数量。
以下是我发现的几个烹饪食谱数据集:
- 🤷食谱配料数据集 (没有配料比例)
- 🤷 Recipe1M+ (食谱很多但需要注册才能下载)
- 🤷美食家——带评级和营养的食谱 (仅约 20k 份食谱,最好能找到更多)
- 👍🏻食谱框(~ 12.5 万份食谱搭配食材比例,不错)**
让我们尝试使用“配方箱”数据集。菜谱的数量看起来足够多,而且它既包含配料又包含烹饪说明。看看 RNN 是否能够了解配料和说明之间的联系是很有趣的。
为训练设置 TensorFlow/Python 沙盒
在本教程中,您可以使用几个选项来试验代码:
- 你可以在你的浏览器 (不需要本地设置)中使用 GoogleColab 进行实验。
- 你可以在你的浏览器中使用 Jupyter 笔记本 (不需要本地设置)。
- 你可以在本地建立一个 Jupyter 笔记本。
我会建议使用 GoogleColab 选项,因为它不需要对您进行任何本地设置(您可以在您的浏览器中进行实验),并且它还提供了强大的 GPU 支持,可以使模型训练更快。您也可以尝试训练参数。
导入依赖关系
让我们从导入一些我们以后会用到的包开始。
**# Packages for training the model and working with the dataset.*
**import** tensorflow **as** tf
**import** matplotlib.pyplot **as** plt
**import** numpy **as** np
**import** json*# Utility/helper packages.*
**import** platform
**import** time
**import** pathlib
**import** os*
首先,让我们确保我们的环境设置正确,并且我们使用的是 Tensorflow 的 2nd 版本。
*print('Python version:', platform.python_version())
print('Tensorflow version:', tf.__version__)
print('Keras version:', tf.keras.__version__)*
➔输出:
*Python version: 3.7.6
Tensorflow version: 2.1.0
Keras version: 2.2.4-tf*
加载数据集
让我们使用TF . keras . utils . get _ file加载数据集。使用get_file()
实用程序很方便,因为它为您处理现成的缓存。这意味着您将只下载一次数据集文件,即使您再次在笔记本中启动相同的代码块,它也会使用缓存,并且代码块的执行速度会更快。
如果缓存文件夹不存在,则创建它:
*CACHE_DIR = './tmp'
pathlib.Path(CACHE_DIR).mkdir(exist_ok=**True**)*
下载并解包数据集:
*dataset_file_name = 'recipes_raw.zip'
dataset_file_origin = 'https://storage.googleapis.com/recipe-box/recipes_raw.zip'dataset_file_path = tf.keras.utils.get_file(
fname=dataset_file_name,
origin=dataset_file_origin,
cache_dir=CACHE_DIR,
extract=**True**,
archive_format='zip'
)print(dataset_file_path)*
以下是下载后数据集文件的路径:
➔输出:
*./tmp/datasets/recipes_raw.zip*
让我们打印缓存文件夹,看看到底下载了什么:
*!ls -la ./tmp/datasets/*
➔输出:
*total 521128
drwxr-xr-x 7 224 May 13 18:10 .
drwxr-xr-x 4 128 May 18 18:00 ..
-rw-r--r-- 1 20437 May 20 06:46 LICENSE
-rw-r--r-- 1 53355492 May 13 18:10 recipes_raw.zip
-rw-r--r-- 1 49784325 May 20 06:46 recipes_raw_nosource_ar.json
-rw-r--r-- 1 61133971 May 20 06:46 recipes_raw_nosource_epi.json
-rw-r--r-- 1 93702755 May 20 06:46 recipes_raw_nosource_fn.json*
如您所见,数据集由 3 个文件组成。稍后,我们需要将这些文件中的信息合并成一个数据集。
让我们从json
文件中加载数据集数据,并从中预览示例。
***def** **load_dataset**(silent=False):
*# List of dataset files we want to merge.*
dataset_file_names = [
'recipes_raw_nosource_ar.json',
'recipes_raw_nosource_epi.json',
'recipes_raw_nosource_fn.json',
]
dataset = [] **for** dataset_file_name **in** dataset_file_names:
dataset_file_path = f'{CACHE_DIR}/datasets/{dataset_file_name}' **with** open(dataset_file_path) **as** dataset_file:
json_data_dict = json.load(dataset_file)
json_data_list = list(json_data_dict.values())
dict_keys = [key **for** key **in** json_data_list[0]]
dict_keys.sort()
dataset += json_data_list *# This code block outputs the summary for each dataset.*
**if** silent == **False**:
print(dataset_file_path)
print('===========================================')
print('Number of examples: ', len(json_data_list), '\n')
print('Example object keys:\n', dict_keys, '\n')
print('Example object:\n', json_data_list[0], '\n')
print('Required keys:\n')
print(' title: ', json_data_list[0]['title'], '\n')
print(' ingredients: ', json_data_list[0]['ingredients'], '\n')
print(' instructions: ', json_data_list[0]['instructions'])
print('\n\n') **return** dataset dataset_raw = load_dataset()*
➔输出:
*./tmp/datasets/recipes_raw_nosource_ar.json
===========================================
Number of examples: 39802 Example object keys:
['ingredients', 'instructions', 'picture_link', 'title'] Example object:
{'title': 'Slow Cooker Chicken and Dumplings', 'ingredients': ['4 skinless, boneless chicken breast halves ADVERTISEMENT', '2 tablespoons butter ADVERTISEMENT', '2 (10.75 ounce) cans condensed cream of chicken soup ADVERTISEMENT', '1 onion, finely diced ADVERTISEMENT', '2 (10 ounce) packages refrigerated biscuit dough, torn into pieces ADVERTISEMENT', 'ADVERTISEMENT'], 'instructions': 'Place the chicken, butter, soup, and onion in a slow cooker, and fill with enough water to cover.\nCover, and cook for 5 to 6 hours on High. About 30 minutes before serving, place the torn biscuit dough in the slow cooker. Cook until the dough is no longer raw in the center.\n', 'picture_link': '55lznCYBbs2mT8BTx6BTkLhynGHzM.S'} Required keys: title: Slow Cooker Chicken and Dumplings ingredients: ['4 skinless, boneless chicken breast halves ADVERTISEMENT', '2 tablespoons butter ADVERTISEMENT', '2 (10.75 ounce) cans condensed cream of chicken soup ADVERTISEMENT', '1 onion, finely diced ADVERTISEMENT', '2 (10 ounce) packages refrigerated biscuit dough, torn into pieces ADVERTISEMENT', 'ADVERTISEMENT'] instructions: Place the chicken, butter, soup, and onion in a slow cooker, and fill with enough water to cover.
Cover, and cook for 5 to 6 hours on High. About 30 minutes before serving, place the torn biscuit dough in the slow cooker. Cook until the dough is no longer raw in the center. ./tmp/datasets/recipes_raw_nosource_epi.json
===========================================
Number of examples: 25323 Example object keys:
['ingredients', 'instructions', 'picture_link', 'title'] Example object:
{'ingredients': ['12 egg whites', '12 egg yolks', '1 1/2 cups sugar', '3/4 cup rye whiskey', '12 egg whites', '3/4 cup brandy', '1/2 cup rum', '1 to 2 cups heavy cream, lightly whipped', 'Garnish: ground nutmeg'], 'picture_link': None, 'instructions': 'Beat the egg whites until stiff, gradually adding in 3/4 cup sugar. Set aside. Beat the egg yolks until they are thick and pale and add the other 3/4 cup sugar and stir in rye whiskey. Blend well. Fold the egg white mixture into the yolk mixture and add the brandy and the rum. Beat the mixture well. To serve, fold the lightly whipped heavy cream into the eggnog. (If a thinner mixture is desired, add the heavy cream unwhipped.) Sprinkle the top of the eggnog with the nutmeg to taste.\nBeat the egg whites until stiff, gradually adding in 3/4 cup sugar. Set aside. Beat the egg yolks until they are thick and pale and add the other 3/4 cup sugar and stir in rye whiskey. Blend well. Fold the egg white mixture into the yolk mixture and add the brandy and the rum. Beat the mixture well. To serve, fold the lightly whipped heavy cream into the eggnog. (If a thinner mixture is desired, add the heavy cream unwhipped.) Sprinkle the top of the eggnog with the nutmeg to taste.', 'title': 'Christmas Eggnog '} Required keys: title: Christmas Eggnog ingredients: ['12 egg whites', '12 egg yolks', '1 1/2 cups sugar', '3/4 cup rye whiskey', '12 egg whites', '3/4 cup brandy', '1/2 cup rum', '1 to 2 cups heavy cream, lightly whipped', 'Garnish: ground nutmeg'] instructions: Beat the egg whites until stiff, gradually adding in 3/4 cup sugar. Set aside. Beat the egg yolks until they are thick and pale and add the other 3/4 cup sugar and stir in rye whiskey. Blend well. Fold the egg white mixture into the yolk mixture and add the brandy and the rum. Beat the mixture well. To serve, fold the lightly whipped heavy cream into the eggnog. (If a thinner mixture is desired, add the heavy cream unwhipped.) Sprinkle the top of the eggnog with the nutmeg to taste.
Beat the egg whites until stiff, gradually adding in 3/4 cup sugar. Set aside. Beat the egg yolks until they are thick and pale and add the other 3/4 cup sugar and stir in rye whiskey. Blend well. Fold the egg white mixture into the yolk mixture and add the brandy and the rum. Beat the mixture well. To serve, fold the lightly whipped heavy cream into the eggnog. (If a thinner mixture is desired, add the heavy cream unwhipped.) Sprinkle the top of the eggnog with the nutmeg to taste../tmp/datasets/recipes_raw_nosource_fn.json
===========================================
Number of examples: 60039 Example object keys:
['ingredients', 'instructions', 'picture_link', 'title'] Example object:
{'instructions': 'Toss ingredients lightly and spoon into a buttered baking dish. Top with additional crushed cracker crumbs, and brush with melted butter. Bake in a preheated at 350 degrees oven for 25 to 30 minutes or until delicately browned.', 'ingredients': ['1/2 cup celery, finely chopped', '1 small green pepper finely chopped', '1/2 cup finely sliced green onions', '1/4 cup chopped parsley', '1 pound crabmeat', '1 1/4 cups coarsely crushed cracker crumbs', '1/2 teaspoon salt', '3/4 teaspoons dry mustard', 'Dash hot sauce', '1/4 cup heavy cream', '1/2 cup melted butter'], 'title': "Grammie Hamblet's Deviled Crab", 'picture_link': None} Required keys: title: Grammie Hamblet's Deviled Crab ingredients: ['1/2 cup celery, finely chopped', '1 small green pepper finely chopped', '1/2 cup finely sliced green onions', '1/4 cup chopped parsley', '1 pound crabmeat', '1 1/4 cups coarsely crushed cracker crumbs', '1/2 teaspoon salt', '3/4 teaspoons dry mustard', 'Dash hot sauce', '1/4 cup heavy cream', '1/2 cup melted butter'] instructions: Toss ingredients lightly and spoon into a buttered baking dish. Top with additional crushed cracker crumbs, and brush with melted butter. Bake in a preheated at 350 degrees oven for 25 to 30 minutes or until delicately browned.*
让我们统计一下合并文件后的示例总数:
*print('Total number of raw examples: ', len(dataset_raw))*
➔输出:
*Total number of raw examples: 125164*
预处理数据集
过滤掉不完整的例子
有可能有些菜谱没有一些必填字段(名称、配料或说明)。我们需要从这些不完整的例子中清理出我们的数据集。
以下函数将帮助我们筛选出既没有标题也没有配料或说明的食谱:
***def** **recipe_validate_required_fields**(recipe):
required_keys = ['title', 'ingredients', 'instructions']
**if** **not** recipe:
**return** **False**
**for** required_key **in** required_keys:
**if** **not** recipe[required_key]:
**return** **False**
**if** type(recipe[required_key]) == list **and** len(recipe[required_key]) == 0:
**return** **False**
**return** **True***
现在让我们使用recipe_validate_required_fields()
函数进行过滤:
*dataset_validated = [recipe **for** recipe **in** dataset_raw **if** recipe_validate_required_fields(recipe)]print('Dataset size BEFORE validation', len(dataset_raw))
print('Dataset size AFTER validation', len(dataset_validated))
print('Number of incomplete recipes', len(dataset_raw) - len(dataset_validated))*
➔输出:
*Dataset size BEFORE validation 125164
Dataset size AFTER validation 122938
Number of incomplete recipes 2226*
正如你可能看到的,我们的2226
食谱有些不完整。
将配方对象转换为字符串
RNN 不理解物体。因此,我们需要将 recipes 对象转换为字符串,然后转换为数字(索引)。让我们从将 recipes 对象转换成字符串开始。
为了帮助我们的 RNN 更快地学习文章的结构,让我们给它添加 3 个“地标”。我们将使用这些独特的“标题”、“配料”和“说明”标志来分隔每个食谱的逻辑部分。
*STOP_WORD_TITLE = '📗 '
STOP_WORD_INGREDIENTS = '\n🥕\n\n'
STOP_WORD_INSTRUCTIONS = '\n📝\n\n'*
下面的函数将 recipe 对象转换为字符串(字符序列),以便以后在 RNN 输入中使用。
***def** **recipe_to_string**(recipe):
*# This string is presented as a part of recipes so we need to clean it up.*
noize_string = 'ADVERTISEMENT'
title = recipe['title']
ingredients = recipe['ingredients']
instructions = recipe['instructions'].split('\n')
ingredients_string = ''
**for** ingredient **in** ingredients:
ingredient = ingredient.replace(noize_string, '')
**if** ingredient:
ingredients_string += f'• {ingredient}\n'
instructions_string = ''
**for** instruction **in** instructions:
instruction = instruction.replace(noize_string, '')
**if** instruction:
instructions_string += f'▪︎ {instruction}\n'
**return** f'{STOP_WORD_TITLE}{title}\n{STOP_WORD_INGREDIENTS}{ingredients_string}{STOP_WORD_INSTRUCTIONS}{instructions_string}'*
让我们将recipe_to_string()
函数应用于dataset_validated
:
*dataset_stringified = [recipe_to_string(recipe) **for** recipe **in** dataset_validated]print('Stringified dataset size: ', len(dataset_stringified))*
➔输出:
*Stringified dataset size: 122938*
让我们先来预览几个食谱:
***for** recipe_index, recipe_string **in** enumerate(dataset_stringified[:3]):
print('Recipe #{}\n---------'.format(recipe_index + 1))
print(recipe_string)
print('\n')*
➔输出:
*Recipe #1
---------
📗 Slow Cooker Chicken and Dumplings🥕• 4 skinless, boneless chicken breast halves
• 2 tablespoons butter
• 2 (10.75 ounce) cans condensed cream of chicken soup
• 1 onion, finely diced
• 2 (10 ounce) packages refrigerated biscuit dough, torn into pieces 📝▪︎ Place the chicken, butter, soup, and onion in a slow cooker, and fill with enough water to cover.
▪︎ Cover, and cook for 5 to 6 hours on High. About 30 minutes before serving, place the torn biscuit dough in the slow cooker. Cook until the dough is no longer raw in the center.Recipe #2
---------
📗 Awesome Slow Cooker Pot Roast🥕• 2 (10.75 ounce) cans condensed cream of mushroom soup
• 1 (1 ounce) package dry onion soup mix
• 1 1/4 cups water
• 5 1/2 pounds pot roast 📝▪︎ In a slow cooker, mix cream of mushroom soup, dry onion soup mix and water. Place pot roast in slow cooker and coat with soup mixture.
▪︎ Cook on High setting for 3 to 4 hours, or on Low setting for 8 to 9 hours.Recipe #3
---------
📗 Brown Sugar Meatloaf🥕• 1/2 cup packed brown sugar
• 1/2 cup ketchup
• 1 1/2 pounds lean ground beef
• 3/4 cup milk
• 2 eggs
• 1 1/2 teaspoons salt
• 1/4 teaspoon ground black pepper
• 1 small onion, chopped
• 1/4 teaspoon ground ginger
• 3/4 cup finely crushed saltine cracker crumbs 📝▪︎ Preheat oven to 350 degrees F (175 degrees C). Lightly grease a 5x9 inch loaf pan.
▪︎ Press the brown sugar in the bottom of the prepared loaf pan and spread the ketchup over the sugar.
▪︎ In a mixing bowl, mix thoroughly all remaining ingredients and shape into a loaf. Place on top of the ketchup.
▪︎ Bake in preheated oven for 1 hour or until juices are clear.*
出于好奇,让我们从数据集的中间预览一下菜谱,看看它是否具有预期的数据结构:
*print(dataset_stringified[50000])*
➔输出:
*📗 Herbed Bean Ragoût 🥕• 6 ounces haricots verts (French thin green beans), trimmed and halved crosswise
• 1 (1-pound) bag frozen edamame (soybeans in the pod) or 1 1/4 cups frozen shelled edamame, not thawed
• 2/3 cup finely chopped onion
• 2 garlic cloves, minced
• 1 Turkish bay leaf or 1/2 California bay leaf
• 2 (3-inch) fresh rosemary sprigs
• 1/2 teaspoon salt
• 1/4 teaspoon black pepper
• 1 tablespoon olive oil
• 1 medium carrot, cut into 1/8-inch dice
• 1 medium celery rib, cut into 1/8-inch dice
• 1 (15- to 16-ounces) can small white beans, rinsed and drained
• 1 1/2 cups chicken stock or low-sodium broth
• 2 tablespoons unsalted butter
• 2 tablespoons finely chopped fresh flat-leaf parsley
• 1 tablespoon finely chopped fresh chervil (optional)
• Garnish: fresh chervil sprigs📝▪︎ Cook haricots verts in a large pot of boiling salted water until just tender, 3 to 4 minutes. Transfer with a slotted spoon to a bowl of ice and cold water, then drain. Add edamame to boiling water and cook 4 minutes. Drain in a colander, then rinse under cold water. If using edamame in pods, shell them and discard pods. Cook onion, garlic, bay leaf, rosemary, salt, and pepper in oil in a 2- to 4-quart heavy saucepan over moderately low heat, stirring, until softened, about 3 minutes. Add carrot and celery and cook, stirring, until softened, about 3 minutes. Add white beans and stock and simmer, covered, stirring occasionally, 10 minutes. Add haricots verts and edamame and simmer, uncovered, until heated through, 2 to 3 minutes. Add butter, parsley, and chervil (if using) and stir gently until butter is melted. Discard bay leaf and rosemary sprigs.
▪︎ Cook haricots verts in a large pot of boiling salted water until just tender, 3 to 4 minutes. Transfer with a slotted spoon to a bowl of ice and cold water, then drain.
▪︎ Add edamame to boiling water and cook 4 minutes. Drain in a colander, then rinse under cold water. If using edamame in pods, shell them and discard pods.
▪︎ Cook onion, garlic, bay leaf, rosemary, salt, and pepper in oil in a 2- to 4-quart heavy saucepan over moderately low heat, stirring, until softened, about 3 minutes. Add carrot and celery and cook, stirring, until softened, about 3 minutes.
▪︎ Add white beans and stock and simmer, covered, stirring occasionally, 10 minutes. Add haricots verts and edamame and simmer, uncovered, until heated through, 2 to 3 minutes. Add butter, parsley, and chervil (if using) and stir gently until butter is melted. Discard bay leaf and rosemary sprigs.*
过滤掉大量食谱
食谱长短不一。在将配方序列输入 RNN 之前,我们需要有一个硬编码序列长度限制。我们需要找出什么样的配方长度可以覆盖大多数的配方用例,同时我们希望它尽可能的短,以加快训练过程。
*recipes_lengths = []
**for** recipe_text **in** dataset_stringified:
recipes_lengths.append(len(recipe_text))plt.hist(recipes_lengths, bins=50)
plt.show()*
➔输出:
配方长度(代码生成的图像)
大多数食谱的长度小于5000
个字符。让我们放大来看更详细的图片:
*plt.hist(recipes_lengths, range=(0, 8000), bins=50)
plt.show()*
➔输出:
配方长度(代码生成的图像)
看起来食谱的字符限制将覆盖大多数情况。我们可以试着用这个最大食谱长度限制来训练 RNN。
*MAX_RECIPE_LENGTH = 2000*
因此,我们来过滤掉所有长于MAX_RECIPE_LENGTH
的菜谱:
***def** **filter_recipes_by_length**(recipe_test):
**return** len(recipe_test) <= MAX_RECIPE_LENGTH dataset_filtered = [recipe_text **for** recipe_text **in** dataset_stringified **if** filter_recipes_by_length(recipe_text)]print('Dataset size BEFORE filtering: ', len(dataset_stringified))
print('Dataset size AFTER filtering: ', len(dataset_filtered))
print('Number of eliminated recipes: ', len(dataset_stringified) - len(dataset_filtered))*
➔输出:
*Dataset size BEFORE filtering: 122938
Dataset size AFTER filtering: 100212
Number of eliminated recipes: 22726*
在过滤过程中,我们丢失了22726
配方,但现在配方的数据更加密集。
汇总数据集参数
*TOTAL_RECIPES_NUM = len(dataset_filtered)print('MAX_RECIPE_LENGTH: ', MAX_RECIPE_LENGTH)
print('TOTAL_RECIPES_NUM: ', TOTAL_RECIPES_NUM)*
➔输出:
*MAX_RECIPE_LENGTH: 2000
TOTAL_RECIPES_NUM: 100212*
最后,我们以~100k
食谱告终。每个配方都有2000
字符长度。
创造词汇
递归神经网络不理解字符或单词。相反,它理解数字。因此,我们需要将食谱文本转换成数字。
在这个实验中,我们将使用基于多层 LSTM(长短期记忆)网络的字符级语言模型(与单词级语言模型相对)。这意味着我们将为字符创建唯一的索引,而不是为单词创建唯一的索引。通过这样做,我们让网络预测序列中的下一个字符,而不是下一个单词。
ℹ️你可以在 Andrej Karpathy 的文章中找到更多关于字符级 RNNs 的解释:
为了从食谱文本中创建词汇表,我们将使用TF . keras . preprocessing . text . tokenizer。
我们还需要来一些独特的字符,将被视为一个停止字符,将表明一个食谱的结束。我们需要它来生成菜谱,因为如果没有这个停止字符,我们就不知道正在生成的菜谱的结尾在哪里。
*STOP_SIGN = '␣'tokenizer = tf.keras.preprocessing.text.Tokenizer(
char_level=**True**,
filters='',
lower=**False**,
split=''
)*# Stop word is not a part of recipes, but tokenizer must know about it as well.*
tokenizer.fit_on_texts([STOP_SIGN])tokenizer.fit_on_texts(dataset_filtered)tokenizer.get_config()*
➔输出:
*{'num_words': None,
'filters': '',
'lower': False,
'split': '',
'char_level': True,
'oov_token': None,
'document_count': 100213, 'word_counts': '{"\\u2423": 1, "\\ud83d\\udcd7": 100212, " ": 17527888, "S": 270259, "l": 3815150, "o": 5987496, "w": 964459, "C": 222831, "k": 890982, "e": 9296022, "r": 4760887, "h": 2922100, "i": 4911812, "c": 2883507, "n": 5304396, "a": 6067157, "d": 3099679, "D": 63999, "u": 2717050, "m": 1794411, "p": 2679164, "g": 1698670, "s": 4704222, "\\n": 1955281, "\\ud83e\\udd55": 100212, "\\u2022": 922813, "4": 232607, ",": 1130487, "b": 1394803, "t": 5997722, "v": 746785, "2": 493933, "(": 144985, "1": 853931, "0": 145119, ".": 1052548, "7": 31098, "5": 154071, ")": 144977, "f": 1042981, "y": 666553, "\\ud83d\\udcdd": 100212, "\\u25aa": 331058, "\\ufe0e": 331058, "P": 200597, "6": 51398, "H": 43936, "A": 134274, "3": 213519, "R": 101253, "x": 201286, "/": 345257, "I": 81591, "L": 46138, "8": 55352, "9": 17697, "B": 123813, "M": 78684, "F": 104359, "j": 110008, "-": 219160, "W": 61616, "\\u00ae": 10159, "N": 12808, "q": 69654, "T": 101371, ";": 72045, "\'": 26831, "Z": 2428, "z": 115883, "G": 52043, ":": 31318, "E": 18582, "K": 18421, "X": 385, "\\"": 6445, "O": 28971, "Y": 6064, "\\u2122": 538, "Q": 3904, "J": 10269, "!": 3014, "U": 14132, "V": 12172, "&": 1039, "+": 87, "=": 113, "%": 993, "*": 3243, "\\u00a9": 99, "[": 30, "]": 31, "\\u00e9": 6727, "<": 76, ">": 86, "\\u00bd": 166, "#": 168, "\\u00f1": 891, "?": 327, "\\u2019": 111, "\\u00b0": 6808, "\\u201d": 6, "$": 84, "@": 5, "{": 8, "}": 9, "\\u2013": 1228, "\\u0096": 7, "\\u00e0": 26, "\\u00e2": 106, "\\u00e8": 846, "\\u00e1": 74, "\\u2014": 215, "\\u2044": 16, "\\u00ee": 415, "\\u00e7": 171, "_": 26, "\\u00fa": 48, "\\u00ef": 43, "\\u201a": 20, "\\u00fb": 36, "\\u00f3": 74, "\\u00ed": 130, "\\u25ca": 4, "\\u00f9": 12, "\\u00d7": 6, "\\u00ec": 8, "\\u00fc": 29, "\\u2031": 4, "\\u00ba": 19, "\\u201c": 4, "\\u00ad": 25, "\\u00ea": 27, "\\u00f6": 9, "\\u0301": 11, "\\u00f4": 8, "\\u00c1": 2, "\\u00be": 23, "\\u00bc": 95, "\\u00eb": 2, "\\u0097": 2, "\\u215b": 3, "\\u2027": 4, "\\u00e4": 15, "\\u001a": 2, "\\u00f8": 2, "\\ufffd": 20, "\\u02da": 6, "\\u00bf": 264, "\\u2153": 2, "|": 2, "\\u00e5": 3, "\\u00a4": 1, "\\u201f": 1, "\\u00a7": 5, "\\ufb02": 3, "\\u00a0": 1, "\\u01b0": 2, "\\u01a1": 1, "\\u0103": 1, "\\u0300": 1, "\\u00bb": 6, "`": 3, "\\u0092": 2, "\\u215e": 1, "\\u202d": 4, "\\u00b4": 2, "\\u2012": 2, "\\u00c9": 40, "\\u00da": 14, "\\u20ac": 1, "\\\\": 5, "~": 1, "\\u0095": 1, "\\u00c2": 2}', 'word_docs': '{"\\u2423": 1, "k": 97316, "0": 61954, "o": 100205, "r": 100207, "d": 100194, "u": 100161, "S": 89250, "\\u25aa": 100212, "D": 40870, "1": 99320, "g": 99975, "n": 100198, "b": 99702, "t": 100202, ".": 100163, " ": 100212, "7": 24377, "3": 79135, "\\ud83d\\udcd7": 100212, "i": 100207, "5": 65486, "f": 98331, "c": 100190, "4": 82453, "a": 100205, "2": 96743, "v": 97848, "C": 83328, "s": 100204, "\\n": 100212, "6": 35206, "\\ud83d\\udcdd": 100212, ",": 98524, "\\ufe0e": 100212, "l": 100206, "e": 100212, "y": 96387, ")": 67614, "p": 100046, "H": 31908, "\\ud83e\\udd55": 100212, "m": 99988, "w": 99227, "(": 67627, "A": 60900, "h": 100161, "\\u2022": 100212, "P": 79364, "R": 54040, "9": 14114, "8": 37000, "L": 32101, "x": 72133, "I": 46675, "/": 89051, "j": 47438, "F": 57940, "B": 64278, "M": 48332, "-": 74711, "T": 53758, "\\u00ae": 5819, "N": 9981, "W": 38981, "q": 36538, ";": 33863, "G": 35355, "\'": 18120, "z": 42430, "Z": 2184, ":": 18214, "E": 12161, "K": 14834, "X": 321, "\\"": 2617, "O": 20103, "Y": 5148, "\\u2122": 448, "Q": 3142, "J": 8225, "!": 2428, "U": 10621, "V": 9710, "&": 749, "+": 32, "=": 48, "%": 717, "*": 1780, "\\u00a9": 91, "]": 26, "[": 25, "\\u00e9": 2462, ">": 33, "<": 27, "\\u00bd": 81, "#": 139, "\\u00f1": 423, "?": 207, "\\u2019": 64, "\\u00b0": 3062, "\\u201d": 3, "@": 4, "$": 49, "{": 7, "}": 8, "\\u2013": 491, "\\u0096": 7, "\\u00e0": 22, "\\u00e2": 45, "\\u00e8": 335, "\\u00e1": 38, "\\u2014": 95, "\\u2044": 9, "\\u00ee": 122, "\\u00e7": 120, "_": 8, "\\u00fa": 25, "\\u00ef": 24, "\\u201a": 10, "\\u00fb": 29, "\\u00f3": 40, "\\u00ed": 52, "\\u25ca": 2, "\\u00f9": 6, "\\u00d7": 4, "\\u00ec": 4, "\\u00fc": 19, "\\u2031": 2, "\\u00ba": 9, "\\u201c": 2, "\\u00ad": 11, "\\u00ea": 4, "\\u00f6": 4, "\\u0301": 6, "\\u00f4": 5, "\\u00c1": 2, "\\u00be": 18, "\\u00bc": 55, "\\u00eb": 2, "\\u0097": 1, "\\u215b": 2, "\\u2027": 3, "\\u00e4": 8, "\\u001a": 1, "\\u00f8": 1, "\\ufffd": 4, "\\u02da": 3, "\\u00bf": 191, "\\u2153": 1, "|": 2, "\\u00e5": 1, "\\u00a4": 1, "\\u201f": 1, "\\u00a7": 3, "\\ufb02": 1, "\\u0300": 1, "\\u01a1": 1, "\\u00a0": 1, "\\u01b0": 1, "\\u0103": 1, "\\u00bb": 2, "`": 3, "\\u0092": 2, "\\u215e": 1, "\\u202d": 1, "\\u00b4": 1, "\\u2012": 1, "\\u00c9": 15, "\\u00da": 5, "\\u20ac": 1, "\\\\": 5, "~": 1, "\\u0095": 1, "\\u00c2": 1}', 'index_docs': '{"1": 100212, "165": 1, "25": 97316, "41": 61954, "5": 100205, "8": 100207, "11": 100194, "14": 100161, "33": 89250, "31": 100212, "58": 40870, "26": 99320, "18": 99975, "6": 100198, "19": 99702, "4": 100202, "21": 100163, "66": 24377, "37": 79135, "51": 100212, "7": 100207, "40": 65486, "22": 98331, "13": 100190, "34": 82453, "3": 100205, "29": 96743, "27": 97848, "35": 83328, "9": 100204, "16": 100212, "62": 35206, "53": 100212, "20": 98524, "32": 100212, "10": 100206, "2": 100212, "28": 96387, "43": 67614, "15": 100046, "64": 31908, "52": 100212, "17": 99988, "23": 99227, "42": 67627, "44": 60900, "12": 100161, "24": 100212, "39": 79364, "50": 54040, "71": 14114, "60": 37000, "63": 32101, "38": 72133, "54": 46675, "30": 89051, "47": 47438, "48": 57940, "45": 64278, "55": 48332, "36": 74711, "49": 53758, "76": 5819, "73": 9981, "59": 38981, "57": 36538, "56": 33863, "61": 35355, "68": 18120, "46": 42430, "84": 2184, "65": 18214, "69": 12161, "70": 14834, "92": 321, "79": 2617, "67": 20103, "80": 5148, "90": 448, "81": 3142, "75": 8225, "83": 2428, "72": 10621, "74": 9710, "86": 749, "105": 32, "100": 48, "87": 717, "82": 1780, "103": 91, "115": 26, "116": 25, "78": 2462, "106": 33, "108": 27, "98": 81, "97": 139, "88": 423, "93": 207, "101": 64, "77": 3062, "137": 3, "141": 4, "107": 49, "133": 7, "131": 8, "85": 491, "136": 7, "119": 22, "102": 45, "89": 335, "109": 38, "95": 95, "126": 9, "91": 122, "96": 120, "120": 8, "111": 25, "112": 24, "123": 10, "114": 29, "110": 40, "99": 52, "144": 2, "129": 6, "138": 4, "134": 4, "117": 19, "145": 2, "125": 9, "146": 2, "121": 11, "118": 4, "132": 4, "130": 6, "135": 5, "153": 2, "122": 18, "104": 55, "154": 2, "155": 1, "149": 2, "147": 3, "127": 8, "156": 1, "157": 1, "124": 4, "139": 3, "94": 191, "158": 1, "159": 2, "150": 1, "166": 1, "167": 1, "142": 3, "151": 1, "171": 1, "169": 1, "168": 1, "160": 1, "170": 1, "140": 2, "152": 3, "161": 2, "172": 1, "148": 1, "162": 1, "163": 1, "113": 15, "128": 5, "173": 1, "143": 5, "174": 1, "175": 1, "164": 1}', 'index_word': '{"1": " ", "2": "e", "3": "a", "4": "t", "5": "o", "6": "n", "7": "i", "8": "r", "9": "s", "10": "l", "11": "d", "12": "h", "13": "c", "14": "u", "15": "p", "16": "\\n", "17": "m", "18": "g", "19": "b", "20": ",", "21": ".", "22": "f", "23": "w", "24": "\\u2022", "25": "k", "26": "1", "27": "v", "28": "y", "29": "2", "30": "/", "31": "\\u25aa", "32": "\\ufe0e", "33": "S", "34": "4", "35": "C", "36": "-", "37": "3", "38": "x", "39": "P", "40": "5", "41": "0", "42": "(", "43": ")", "44": "A", "45": "B", "46": "z", "47": "j", "48": "F", "49": "T", "50": "R", "51": "\\ud83d\\udcd7", "52": "\\ud83e\\udd55", "53": "\\ud83d\\udcdd", "54": "I", "55": "M", "56": ";", "57": "q", "58": "D", "59": "W", "60": "8", "61": "G", "62": "6", "63": "L", "64": "H", "65": ":", "66": "7", "67": "O", "68": "\'", "69": "E", "70": "K", "71": "9", "72": "U", "73": "N", "74": "V", "75": "J", "76": "\\u00ae", "77": "\\u00b0", "78": "\\u00e9", "79": "\\"", "80": "Y", "81": "Q", "82": "*", "83": "!", "84": "Z", "85": "\\u2013", "86": "&", "87": "%", "88": "\\u00f1", "89": "\\u00e8", "90": "\\u2122", "91": "\\u00ee", "92": "X", "93": "?", "94": "\\u00bf", "95": "\\u2014", "96": "\\u00e7", "97": "#", "98": "\\u00bd", "99": "\\u00ed", "100": "=", "101": "\\u2019", "102": "\\u00e2", "103": "\\u00a9", "104": "\\u00bc", "105": "+", "106": ">", "107": "$", "108": "<", "109": "\\u00e1", "110": "\\u00f3", "111": "\\u00fa", "112": "\\u00ef", "113": "\\u00c9", "114": "\\u00fb", "115": "]", "116": "[", "117": "\\u00fc", "118": "\\u00ea", "119": "\\u00e0", "120": "_", "121": "\\u00ad", "122": "\\u00be", "123": "\\u201a", "124": "\\ufffd", "125": "\\u00ba", "126": "\\u2044", "127": "\\u00e4", "128": "\\u00da", "129": "\\u00f9", "130": "\\u0301", "131": "}", "132": "\\u00f6", "133": "{", "134": "\\u00ec", "135": "\\u00f4", "136": "\\u0096", "137": "\\u201d", "138": "\\u00d7", "139": "\\u02da", "140": "\\u00bb", "141": "@", "142": "\\u00a7", "143": "\\\\", "144": "\\u25ca", "145": "\\u2031", "146": "\\u201c", "147": "\\u2027", "148": "\\u202d", "149": "\\u215b", "150": "\\u00e5", "151": "\\ufb02", "152": "`", "153": "\\u00c1", "154": "\\u00eb", "155": "\\u0097", "156": "\\u001a", "157": "\\u00f8", "158": "\\u2153", "159": "|", "160": "\\u01b0", "161": "\\u0092", "162": "\\u00b4", "163": "\\u2012", "164": "\\u00c2", "165": "\\u2423", "166": "\\u00a4", "167": "\\u201f", "168": "\\u00a0", "169": "\\u01a1", "170": "\\u0103", "171": "\\u0300", "172": "\\u215e", "173": "\\u20ac", "174": "~", "175": "\\u0095"}', 'word_index': '{" ": 1, "e": 2, "a": 3, "t": 4, "o": 5, "n": 6, "i": 7, "r": 8, "s": 9, "l": 10, "d": 11, "h": 12, "c": 13, "u": 14, "p": 15, "\\n": 16, "m": 17, "g": 18, "b": 19, ",": 20, ".": 21, "f": 22, "w": 23, "\\u2022": 24, "k": 25, "1": 26, "v": 27, "y": 28, "2": 29, "/": 30, "\\u25aa": 31, "\\ufe0e": 32, "S": 33, "4": 34, "C": 35, "-": 36, "3": 37, "x": 38, "P": 39, "5": 40, "0": 41, "(": 42, ")": 43, "A": 44, "B": 45, "z": 46, "j": 47, "F": 48, "T": 49, "R": 50, "\\ud83d\\udcd7": 51, "\\ud83e\\udd55": 52, "\\ud83d\\udcdd": 53, "I": 54, "M": 55, ";": 56, "q": 57, "D": 58, "W": 59, "8": 60, "G": 61, "6": 62, "L": 63, "H": 64, ":": 65, "7": 66, "O": 67, "\'": 68, "E": 69, "K": 70, "9": 71, "U": 72, "N": 73, "V": 74, "J": 75, "\\u00ae": 76, "\\u00b0": 77, "\\u00e9": 78, "\\"": 79, "Y": 80, "Q": 81, "*": 82, "!": 83, "Z": 84, "\\u2013": 85, "&": 86, "%": 87, "\\u00f1": 88, "\\u00e8": 89, "\\u2122": 90, "\\u00ee": 91, "X": 92, "?": 93, "\\u00bf": 94, "\\u2014": 95, "\\u00e7": 96, "#": 97, "\\u00bd": 98, "\\u00ed": 99, "=": 100, "\\u2019": 101, "\\u00e2": 102, "\\u00a9": 103, "\\u00bc": 104, "+": 105, ">": 106, "$": 107, "<": 108, "\\u00e1": 109, "\\u00f3": 110, "\\u00fa": 111, "\\u00ef": 112, "\\u00c9": 113, "\\u00fb": 114, "]": 115, "[": 116, "\\u00fc": 117, "\\u00ea": 118, "\\u00e0": 119, "_": 120, "\\u00ad": 121, "\\u00be": 122, "\\u201a": 123, "\\ufffd": 124, "\\u00ba": 125, "\\u2044": 126, "\\u00e4": 127, "\\u00da": 128, "\\u00f9": 129, "\\u0301": 130, "}": 131, "\\u00f6": 132, "{": 133, "\\u00ec": 134, "\\u00f4": 135, "\\u0096": 136, "\\u201d": 137, "\\u00d7": 138, "\\u02da": 139, "\\u00bb": 140, "@": 141, "\\u00a7": 142, "\\\\": 143, "\\u25ca": 144, "\\u2031": 145, "\\u201c": 146, "\\u2027": 147, "\\u202d": 148, "\\u215b": 149, "\\u00e5": 150, "\\ufb02": 151, "`": 152, "\\u00c1": 153, "\\u00eb": 154, "\\u0097": 155, "\\u001a": 156, "\\u00f8": 157, "\\u2153": 158, "|": 159, "\\u01b0": 160, "\\u0092": 161, "\\u00b4": 162, "\\u2012": 163, "\\u00c2": 164, "\\u2423": 165, "\\u00a4": 166, "\\u201f": 167, "\\u00a0": 168, "\\u01a1": 169, "\\u0103": 170, "\\u0300": 171, "\\u215e": 172, "\\u20ac": 173, "~": 174, "\\u0095": 175}'}*
为了得到一个完整的词汇表,我们需要将+1
加到已经注册的字符数上,因为索引 [0](https://www.tensorflow.org/api_docs/python/tf/keras/preprocessing/text/Tokenizer)
是一个保留索引,不会分配给任何单词。
*VOCABULARY_SIZE = len(tokenizer.word_counts) + 1print('VOCABULARY_SIZE: ', VOCABULARY_SIZE)*
➔输出:
*VOCABULARY_SIZE: 176*
让我们尝试一下记号化器字典,看看如何将字符转换成索引,反之亦然:
*print(tokenizer.index_word[5])
print(tokenizer.index_word[20])*
➔输出:
*o
,*
让我们尝试将字符转换为索引:
*tokenizer.word_index['r']*
➔输出:
*8*
为了说明哪种字符构成了我们数据集中的所有食谱,我们可以将它们打印成一个数组:
*array_vocabulary = tokenizer.sequences_to_texts([[word_index] **for** word_index **in** range(VOCABULARY_SIZE)])
print([char **for** char **in** array_vocabulary])*
➔输出:
*['', ' ', 'e', 'a', 't', 'o', 'n', 'i', 'r', 's', 'l', 'd', 'h', 'c', 'u', 'p', '\n', 'm', 'g', 'b', ',', '.', 'f', 'w', '•', 'k', '1', 'v', 'y', '2', '/', '▪', '︎', 'S', '4', 'C', '-', '3', 'x', 'P', '5', '0', '(', ')', 'A', 'B', 'z', 'j', 'F', 'T', 'R', '📗', '🥕', '📝', 'I', 'M', ';', 'q', 'D', 'W', '8', 'G', '6', 'L', 'H', ':', '7', 'O', "'", 'E', 'K', '9', 'U', 'N', 'V', 'J', '®', '°', 'é', '"', 'Y', 'Q', '*', '!', 'Z', '–', '&', '%', 'ñ', 'è', '™', 'î', 'X', '?', '¿', '—', 'ç', '#', '½', 'í', '=', '’', 'â', '©', '¼', '+', '>', '$', '<', 'á', 'ó', 'ú', 'ï', 'É', 'û', ']', '[', 'ü', 'ê', 'à', '_', '\xad', '¾', '‚', '�', 'º', '⁄', 'ä', 'Ú', 'ù', '́', '}', 'ö', '{', 'ì', 'ô', '\x96', '”', '×', '˚', '»', '@', '§', '\\', '◊', '‱', '“', '‧', '\u202d', '⅛', 'å', 'fl', '`', 'Á', 'ë', '\x97', '\x1a', 'ø', '⅓', '|', 'ư', '\x92', '´', '‒', 'Â', '␣', '¤', '‟', '\xa0', 'ơ', 'ă', '̀', '⅞', '€', '~', '\x95']*
这些都是我们的 RNN 模型将要处理的角色。它将尝试学习如何将这些字符组合成看起来像食谱的序列。
让我们看看如何使用tokenizer
函数将文本转换成索引:
*tokenizer.texts_to_sequences(['📗 yes'])*
➔输出:
*[[51, 1, 28, 2, 9]]*
对数据集进行矢量化
现在,一旦我们有了词汇表(character --code
和code --character
关系),我们就可以将食谱从文本转换成数字(RNN 将数字作为输入,而不是文本)。
*dataset_vectorized = tokenizer.texts_to_sequences(dataset_filtered)print('Vectorized dataset size', len(dataset_vectorized))*
➔输出:
*Vectorized dataset size 100212*
这是第一个矢量化食谱的开头:
*print(dataset_vectorized[0][:10], '...')*
➔输出:
*[51, 1, 33, 10, 5, 23, 1, 35, 5, 5] ...*
让我们看看如何将矢量化的食谱转换回文本表示:
***def** **recipe_sequence_to_string**(recipe_sequence):
recipe_stringified = tokenizer.sequences_to_texts([recipe_sequence])[0]
print(recipe_stringified)recipe_sequence_to_string(dataset_vectorized[0])*
➔输出:
*📗 Slow Cooker Chicken and Dumplings🥕• 4 skinless, boneless chicken breast halves
• 2 tablespoons butter
• 2 (10.75 ounce) cans condensed cream of chicken soup
• 1 onion, finely diced
• 2 (10 ounce) packages refrigerated biscuit dough, torn into pieces 📝▪︎ Place the chicken, butter, soup, and onion in a slow cooker, and fill with enough water to cover.
▪︎ Cover, and cook for 5 to 6 hours on High. About 30 minutes before serving, place the torn biscuit dough in the slow cooker. Cook until the dough is no longer raw in the center.*
给序列添加填充
我们需要所有的食谱都有相同的训练长度。为此,我们将使用TF . keras . preprocessing . sequence . pad _ sequences实用程序在每个配方的末尾添加一个停止字,并使它们具有相同的长度。
让我们检查一下食谱的长度:
***for** recipe_index, recipe **in** enumerate(dataset_vectorized[:10]):
print('Recipe #{} length: {}'.format(recipe_index + 1, len(recipe)))*
➔输出:
*Recipe #1 length: 546
Recipe #2 length: 401
Recipe #3 length: 671
Recipe #4 length: 736
Recipe #5 length: 1518
Recipe #6 length: 740
Recipe #7 length: 839
Recipe #8 length: 667
Recipe #9 length: 1264
Recipe #10 length: 854*
让我们把所有的食谱都加上一个STOP_SIGN
:
*dataset_vectorized_padded_without_stops = tf.keras.preprocessing.sequence.pad_sequences(
dataset_vectorized,
padding='post',
truncating='post',
*# We use -1 here and +1 in the next step to make sure*
*# that all recipes will have at least 1 stops sign at the end,*
*# since each sequence will be shifted and truncated afterwards*
*# (to generate X and Y sequences).*
maxlen=MAX_RECIPE_LENGTH-1,
value=tokenizer.texts_to_sequences([STOP_SIGN])[0]
)dataset_vectorized_padded = tf.keras.preprocessing.sequence.pad_sequences(
dataset_vectorized_padded_without_stops,
padding='post',
truncating='post',
maxlen=MAX_RECIPE_LENGTH+1,
value=tokenizer.texts_to_sequences([STOP_SIGN])[0]
)**for** recipe_index, recipe **in** enumerate(dataset_vectorized_padded[:10]):
print('Recipe #{} length: {}'.format(recipe_index, len(recipe)))*
➔输出:
*Recipe #0 length: 2001
Recipe #1 length: 2001
Recipe #2 length: 2001
Recipe #3 length: 2001
Recipe #4 length: 2001
Recipe #5 length: 2001
Recipe #6 length: 2001
Recipe #7 length: 2001
Recipe #8 length: 2001
Recipe #9 length: 2001*
填充后,数据集中的所有食谱现在都具有相同的长度,RNN 也将能够知道每个食谱在哪里停止(通过观察STOP_SIGN
的出现)。
以下是填充后第一个配方的外观示例。
*recipe_sequence_to_string(dataset_vectorized_padded[0])*
➔输出:
*📗 Slow Cooker Chicken and Dumplings🥕• 4 skinless, boneless chicken breast halves
• 2 tablespoons butter
• 2 (10.75 ounce) cans condensed cream of chicken soup
• 1 onion, finely diced
• 2 (10 ounce) packages refrigerated biscuit dough, torn into pieces 📝▪︎ Place the chicken, butter, soup, and onion in a slow cooker, and fill with enough water to cover.
▪︎ Cover, and cook for 5 to 6 hours on High. About 30 minutes before serving, place the torn biscuit dough in the slow cooker. Cook until the dough is no longer raw in the center.
␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣*
现在所有的食谱都以一个或多个␣
符号结尾。我们希望我们的 LSTM 模型知道,每当它看到␣
停止字符时,它就意味着食谱结束了。一旦网络学会了这个概念,它就会在每个新生成的菜谱的末尾加上停止字符。
创建张量流数据集
到目前为止,我们是像处理 NumPy 数组一样处理数据集的。如果我们将数据集 NumPy 数组转换成 TensorFlow 数据集,在训练过程中会更方便。它将赋予我们使用batch()
、shuffle()
、repeat()
、prefecth()
等助手功能的能力。:
*dataset = tf.data.Dataset.from_tensor_slices(dataset_vectorized_padded)print(dataset)*
➔输出:
*<TensorSliceDataset shapes: (2001,), types: tf.int32>*
让我们这次通过使用 TensorFlow 数据集 API 来看看数据集中的第一个配方是什么样的:
***for** recipe **in** dataset.take(1):
print('Raw recipe:\n', recipe.numpy(), '\n\n\n')
print('Stringified recipe:\n')
recipe_sequence_to_string(recipe.numpy())*
➔输出:
*Raw recipe:
[ 51 1 33 ... 165 165 165] Stringified recipe:📗 Slow Cooker Chicken and Dumplings🥕• 4 skinless, boneless chicken breast halves
• 2 tablespoons butter
• 2 (10.75 ounce) cans condensed cream of chicken soup
• 1 onion, finely diced
• 2 (10 ounce) packages refrigerated biscuit dough, torn into pieces 📝▪︎ Place the chicken, butter, soup, and onion in a slow cooker, and fill with enough water to cover.
▪︎ Cover, and cook for 5 to 6 hours on High. About 30 minutes before serving, place the torn biscuit dough in the slow cooker. Cook until the dough is no longer raw in the center.
␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣*
input
和target
文本上的拆分示例
对于每个序列,我们需要复制并移动它,以形成input
和target
文本。例如,假设sequence_length
是4
,我们的文本是Hello
。输入序列是Hell
,目标序列是ello
。
***def** **split_input_target**(recipe):
input_text = recipe[:-1]
target_text = recipe[1:]
**return** input_text, target_textdataset_targeted = dataset.map(split_input_target)print(dataset_targeted)*
➔输出:
*<MapDataset shapes: ((2000,), (2000,)), types: (tf.int32, tf.int32)>*
您可能会从上面的行中注意到,现在数据集中的每个示例都由两个元组组成:输入和目标。让我们打印一个例子:
***for** input_example, target_example **in** dataset_targeted.take(1):
print('Input sequence size:', repr(len(input_example.numpy())))
print('Target sequence size:', repr(len(target_example.numpy())))
print()
input_stringified = tokenizer.sequences_to_texts([input_example.numpy()[:50]])[0]
target_stringified = tokenizer.sequences_to_texts([target_example.numpy()[:50]])[0]
print('Input: ', repr(''.join(input_stringified)))
print('Target: ', repr(''.join(target_stringified)))*
➔输出:
*Input sequence size: 2000
Target sequence size: 2000Input: '📗 S l o w C o o k e r C h i c k e n a n d D u m p l i n g s \n \n 🥕 \n \n • 4 s k i n l e'
Target: ' S l o w C o o k e r C h i c k e n a n d D u m p l i n g s \n \n 🥕 \n \n • 4 s k i n l e s'*
RNN 将这些向量的每个索引作为一个时间步长进行处理。对于时间步长0
的输入,模型接收📗
的索引,并尝试预测`` `(一个空格字符)的索引作为下一个字符。在下一个时间步,它做同样的事情,但是 RNN 除了考虑当前输入字符外,还考虑前一步的上下文。
***for** i, (input_idx, target_idx) **in** enumerate(zip(input_example[:10], target_example[:10])):
print('Step {:2d}'.format(i + 1))
print(' input: {} ({:s})'.format(input_idx, repr(tokenizer.sequences_to_texts([[input_idx.numpy()]])[0])))
print(' expected output: {} ({:s})'.format(target_idx, repr(tokenizer.sequences_to_texts([[target_idx.numpy()]])[0])))*
➔输出:
*Step 1
input: 51 ('📗')
expected output: 1 (' ')
Step 2
input: 1 (' ')
expected output: 33 ('S')
Step 3
input: 33 ('S')
expected output: 10 ('l')
Step 4
input: 10 ('l')
expected output: 5 ('o')
Step 5
input: 5 ('o')
expected output: 23 ('w')
Step 6
input: 23 ('w')
expected output: 1 (' ')
Step 7
input: 1 (' ')
expected output: 35 ('C')
Step 8
input: 35 ('C')
expected output: 5 ('o')
Step 9
input: 5 ('o')
expected output: 5 ('o')
Step 10
input: 5 ('o')
expected output: 25 ('k')*
将数据集分成几批
我们在数据集中有~100k
个食谱,每个食谱有两个由2000
字符组成的元组。
*print(dataset_targeted)*
➔输出:
*<MapDataset shapes: ((2000,), (2000,)), types: (tf.int32, tf.int32)>*
让我们打印常量值:
*print('TOTAL_RECIPES_NUM: ', TOTAL_RECIPES_NUM)
print('MAX_RECIPE_LENGTH: ', MAX_RECIPE_LENGTH)
print('VOCABULARY_SIZE: ', VOCABULARY_SIZE)*
➔输出:
*TOTAL_RECIPES_NUM: 100212
MAX_RECIPE_LENGTH: 2000
VOCABULARY_SIZE: 176*
如果我们在训练过程中将完整的数据集提供给模型,然后尝试一次对所有示例进行反向传播,我们可能会耗尽内存,并且每个训练时期可能需要太长时间来执行。为了避免这种情况,我们需要将数据集分成几批。
**# Batch size.*
BATCH_SIZE = 64*# Buffer size to shuffle the dataset (TF data is designed to work*
*# with possibly infinite sequences, so it doesn't attempt to shuffle*
*# the entire sequence in memory. Instead, it maintains a buffer in*
*# which it shuffles elements).*
SHUFFLE_BUFFER_SIZE = 1000dataset_train = dataset_targeted \
*# Shuffling examples first.*
.shuffle(SHUFFLE_BUFFER_SIZE) \
*# Splitting examples on batches.*
.batch(BATCH_SIZE, drop_remainder=**True**) \
*# Making a dataset to be repeatable (it will never ends).*
.repeat()print(dataset_train)*
➔输出:
*<RepeatDataset shapes: ((64, 2000), (64, 2000)), types: (tf.int32, tf.int32)>*
从上面的行中,您可能会注意到我们的数据集现在由相同的两个2000
字符元组组成,但是现在它们被64
分组到批处理中。
***for** input_text, target_text **in** dataset_train.take(1):
print('1st batch: input_text:', input_text)
print()
print('1st batch: target_text:', target_text)*
➔输出:
*1st batch: input_text: tf.Tensor(
[[ 51 1 54 ... 165 165 165]
[ 51 1 64 ... 165 165 165]
[ 51 1 44 ... 165 165 165]
...
[ 51 1 69 ... 165 165 165]
[ 51 1 55 ... 165 165 165]
[ 51 1 70 ... 165 165 165]], shape=(64, 2000), dtype=int32)1st batch: target_text: tf.Tensor(
[[ 1 54 4 ... 165 165 165]
[ 1 64 5 ... 165 165 165]
[ 1 44 6 ... 165 165 165]
...
[ 1 69 3 ... 165 165 165]
[ 1 55 3 ... 165 165 165]
[ 1 70 2 ... 165 165 165]], shape=(64, 2000), dtype=int32)*
建立模型
我们将使用 tf.keras.Sequential 来定义模型。在本实验中,我们将使用以下图层类型:
- TF . keras . layers . embedding—输入层(一个可训练的查找表,将每个字符的数字映射到一个具有
embedding_dim
维度的向量), - tf.keras.layers.LSTM —一种大小为
units=rnn_units
的 RNN(这里也可以使用 GRU 图层), - TF . keras . layers . dense—输出层,带有
VOCABULARY_SIZE
输出。
弄清楚嵌入层是如何工作的
让我们快速迂回一下,看看嵌入层是如何工作的。它接受几个字符索引序列(批处理)作为输入。它将每个序列的每个字符编码成一个长度为tmp_embedding_size
的向量。
*tmp_vocab_size = 10
tmp_embedding_size = 5
tmp_input_length = 8
tmp_batch_size = 2tmp_model = tf.keras.models.Sequential()
tmp_model.add(tf.keras.layers.Embedding(
input_dim=tmp_vocab_size,
output_dim=tmp_embedding_size,
input_length=tmp_input_length
))
*# The model will take as input an integer matrix of size (batch, input_length).*
*# The largest integer (i.e. word index) in the input should be no larger than 9 (tmp_vocab_size).*
*# Now model.output_shape == (None, 10, 64), where None is the batch dimension.*
tmp_input_array = np.random.randint(
low=0,
high=tmp_vocab_size,
size=(tmp_batch_size, tmp_input_length)
)
tmp_model.compile('rmsprop', 'mse')
tmp_output_array = tmp_model.predict(tmp_input_array)print('tmp_input_array shape:', tmp_input_array.shape)
print('tmp_input_array:')
print(tmp_input_array)
print()
print('tmp_output_array shape:', tmp_output_array.shape)
print('tmp_output_array:')
print(tmp_output_array)*
➔输出:
*tmp_input_array shape: (2, 8)
tmp_input_array:
[[2 4 7 5 1 6 9 7]
[3 6 8 1 4 0 1 2]]tmp_output_array shape: (2, 8, 5)
tmp_output_array:
[[[-0.02229502 -0.02800617 -0.0120693 -0.01681594 -0.00650246]
[-0.03046973 -0.03920818 0.04956308 0.04417323 -0.00446874]
[-0.0215276 0.01532575 -0.02229529 0.02834387 0.02725342]
[ 0.04567988 0.0141306 0.00877035 -0.02601192 0.00380837]
[ 0.02969306 0.02994296 -0.00233263 0.00716375 -0.00847433]
[ 0.04598364 -0.00704358 -0.01386416 0.01195388 -0.00309662]
[-0.00137572 0.01275543 -0.02348721 -0.04825885 0.00527108]
[-0.0215276 0.01532575 -0.02229529 0.02834387 0.02725342]] [[ 0.01082945 0.03824175 -0.00450991 -0.02865709 0.02502238]
[ 0.04598364 -0.00704358 -0.01386416 0.01195388 -0.00309662]
[ 0.02275398 0.03806095 -0.03491788 0.04705564 0.00167596]
[ 0.02969306 0.02994296 -0.00233263 0.00716375 -0.00847433]
[-0.03046973 -0.03920818 0.04956308 0.04417323 -0.00446874]
[-0.02909902 0.04426369 0.00150937 0.04579213 0.02559013]
[ 0.02969306 0.02994296 -0.00233263 0.00716375 -0.00847433]
[-0.02229502 -0.02800617 -0.0120693 -0.01681594 -0.00650246]]]*
LSTM 模型
让我们组装模型。
ℹ️您可以使用 TensorFlow 文档中的 RNN 笔记本来检查文本生成,以了解有关模型组件的更多详细信息。
***def** **build_model**(vocab_size, embedding_dim, rnn_units, batch_size):
model = tf.keras.models.Sequential() model.add(tf.keras.layers.Embedding(
input_dim=vocab_size,
output_dim=embedding_dim,
batch_input_shape=[batch_size, **None**]
)) model.add(tf.keras.layers.LSTM(
units=rnn_units,
return_sequences=**True**,
stateful=**True**,
recurrent_initializer=tf.keras.initializers.GlorotNormal()
)) model.add(tf.keras.layers.Dense(vocab_size))
**return** modelmodel = build_model(
vocab_size=VOCABULARY_SIZE,
embedding_dim=256,
rnn_units=1024,
batch_size=BATCH_SIZE
)model.summary()*
➔输出:
*Model: "sequential_13"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
embedding_13 (Embedding) (64, None, 256) 45056
_________________________________________________________________
lstm_9 (LSTM) (64, None, 1024) 5246976
_________________________________________________________________
dense_8 (Dense) (64, None, 176) 180400
=================================================================
Total params: 5,472,432
Trainable params: 5,472,432
Non-trainable params: 0
_________________________________________________________________*
让我们来看看这个模型:
*tf.keras.utils.plot_model(
model,
show_shapes=**True**,
show_layer_names=**True**,
to_file='model.png'
)*
➔输出:
网络架构(代码生成的映像)
对于每个字符,模型查找嵌入,以嵌入作为输入运行 LSTM 一个时间步长,并应用密集层来生成预测下一个字符的对数似然的逻辑:
图片来源:用 RNN 笔记本生成文本。
上图展示了 GRU 网络,但是你可以很容易地用 LSTM 代替 GRU。
在训练前尝试模型
让我们试验一下未经训练的模型,看看它的界面(我们需要什么样的输入,我们将有什么样的输出),并看看在训练之前模型预测了什么:
***for** input_example_batch, target_example_batch **in** dataset_train.take(1):
example_batch_predictions = model(input_example_batch)
print(example_batch_predictions.shape, "# (batch_size, sequence_length, vocab_size)")*
➔输出:
*(64, 2000, 176) # (batch_size, sequence_length, vocab_size)*
为了从模型中获得实际的预测,我们需要从输出分布中取样,以获得实际的字符索引。这种分布是由字符词汇表上的逻辑定义的。
*print('Prediction for the 1st letter of the batch 1st sequense:')
print(example_batch_predictions[0, 0])*
➔输出:
*Prediction for the 1st letter of the batch 1st sequense:
tf.Tensor(
[-9.0643829e-03 -1.9503604e-03 9.3381782e-04 3.7442446e-03
-2.0541784e-03 -7.4054599e-03 -7.1884273e-03 2.6014952e-03
4.8721582e-03 3.0045470e-04 2.6016519e-04 -4.1374690e-03
5.3856964e-03 2.6284808e-03 -5.6002503e-03 2.6019611e-03
-1.9491187e-03 -3.1097094e-04 6.3465843e-03 1.4640498e-03
2.4560774e-03 -3.1256995e-03 1.4104056e-03 2.5478401e-04
5.4266443e-03 -4.1188141e-03 3.6904984e-03 -5.8337618e-03
3.6372752e-03 -3.1899021e-05 3.2178329e-03 1.5033322e-04
5.2770867e-04 -8.1920059e-04 -2.2364906e-03 -2.3271297e-03
4.4109682e-03 4.2381673e-04 1.0532180e-03 -1.4208974e-03
-3.2446394e-03 -4.5869066e-03 4.3250201e-04 -4.3490473e-03
3.7889536e-03 -9.2122913e-04 7.8936084e-04 -9.7079907e-04
1.7070504e-03 -2.5260956e-03 6.7904620e-03 1.5470090e-03
-9.4337866e-04 -1.5072266e-03 6.8939931e-04 -1.0795534e-03
-3.1912089e-03 2.3665284e-03 1.7737487e-03 -2.3504677e-03
-6.8649277e-04 9.6421910e-04 -4.1204207e-03 -3.8750230e-03
1.9077851e-03 4.7145790e-05 -2.9846188e-03 5.8050319e-03
-5.6210475e-04 -2.5910907e-04 5.2890396e-03 -5.8653783e-03
-6.0040038e-06 2.3905798e-03 -2.9405006e-03 2.0132761e-03
-3.5594390e-03 4.0282350e-04 4.7719614e-03 -2.4438011e-03
-1.1028582e-03 2.0007135e-03 -1.6961874e-03 -4.2196750e-03
-3.5689408e-03 -4.1934610e-03 -8.5307617e-04 1.5773368e-04
-1.4612130e-03 9.5826073e-04 4.0543079e-04 -2.3562380e-04
-1.5394683e-03 3.6650903e-03 3.5997448e-03 2.2390878e-03
-6.8982318e-04 1.4068574e-03 -2.0531749e-03 -1.5443334e-03
-1.8235333e-03 -3.2099178e-03 1.6660831e-03 1.2230751e-03
3.8084832e-03 6.9559496e-03 5.7684043e-03 3.1751506e-03
7.4234616e-04 1.1971325e-04 -2.7798198e-03 2.1485630e-03
4.0362971e-03 6.4410735e-05 1.7432809e-03 3.2334479e-03
-6.1469898e-03 -2.2205685e-03 -1.0864032e-03 -2.0876178e-07
2.3065242e-03 -1.5816523e-03 -2.1492387e-03 -4.4033155e-03
1.1003019e-03 -9.7132073e-04 -6.3941808e-04 3.0277157e-03
2.9096641e-03 -2.4778468e-03 -2.9532036e-03 7.7463314e-04
2.7473709e-03 -7.6333171e-04 -8.1811845e-03 -1.3959130e-03
3.2840301e-03 6.0461317e-03 -1.3022404e-04 -9.4000692e-04
-2.0096730e-04 3.3895797e-03 2.9710699e-03 1.9046264e-03
2.5092331e-03 -2.0799250e-04 -2.2211851e-04 -3.4621451e-05
1.9962704e-03 -2.3159904e-03 2.9832027e-03 3.3852295e-03
3.4411502e-04 -1.9019389e-03 -3.6734296e-04 -1.4232489e-03
2.6938838e-03 -2.8015859e-03 -5.7366290e-03 8.0239226e-04
-6.2909431e-04 1.1508183e-03 -1.5899434e-04 -5.9326587e-04
-4.1618512e-04 5.2454891e-03 1.2823739e-03 -1.7550631e-03
-3.0120560e-03 -3.8433261e-03 -9.6873334e-04 1.9963509e-03
1.8154597e-03 4.7434499e-03 1.7146189e-03 1.1544267e-03], shape=(176,), dtype=float32)*
对于每个输入字符,example_batch_predictions
数组包含下一个字符可能是什么的概率向量。如果向量中位置15
处的概率是0.3
,位置25
处的概率是1.1
,这意味着我们最好选择索引为25
的字符作为下一个字符。
由于我们希望我们的网络生成不同的配方(即使对于相同的输入),我们不能只选择最大概率值。在这种情况下,我们将以网络一遍又一遍地预测相同的配方而告终。我们要做的是通过使用TF . random . categorial()函数从预测中提取样本(就像上面打印的一样)。会给网络带来一些模糊性。例如,假设我们有字符H
作为输入,然后,通过从分类分布中取样,我们的网络不仅可以预测单词He
,还可以预测单词Hello
和Hi
等。
了解tf.random.categorical
如何运作
**# logits is 2-D Tensor with shape [batch_size, num_classes].*
*# Each slice [i, :] represents the unnormalized log-probabilities for all classes.*
*# In the example below we say that the probability for class "0"*
*# (element with index 0) is low but the probability for class "2" is much higher.*
tmp_logits = [
[-0.95, 0, 0.95],
];*# Let's generate 5 samples. Each sample is a class index. Class probabilities*
*# are being taken into account (we expect to see more samples of class "2").*
tmp_samples = tf.random.categorical(
logits=tmp_logits,
num_samples=5
)print(tmp_samples)*
➔输出:
*tf.Tensor([[2 1 2 2 1]], shape=(1, 5), dtype=int64)*
从 LSTM 预测中取样
*sampled_indices = tf.random.categorical(
logits=example_batch_predictions[0],
num_samples=1
)sampled_indices = tf.squeeze(
input=sampled_indices,
axis=-1
).numpy()sampled_indices.shape*
➔输出:
*(2000,)*
让我们来看看菜谱前100
个字符的一些预测示例:
*sampled_indices[:100]*
➔输出:
*array([ 64, 21, 91, 126, 170, 42, 146, 54, 125, 164, 60, 171, 9,
87, 129, 28, 146, 103, 41, 101, 147, 3, 134, 171, 8, 170,
105, 5, 44, 173, 5, 105, 17, 138, 165, 32, 88, 96, 145,
83, 33, 65, 172, 162, 8, 29, 147, 58, 81, 153, 150, 56,
156, 38, 144, 134, 13, 40, 17, 50, 27, 35, 39, 112, 63,
139, 151, 133, 68, 29, 91, 2, 70, 112, 135, 31, 26, 156,
118, 71, 49, 104, 75, 27, 164, 41, 117, 124, 18, 137, 59,
160, 158, 119, 173, 50, 78, 45, 121, 118])*
我们现在可以看到我们未经训练的模型实际上预测了什么:
*print('Input:\n', repr(''.join(tokenizer.sequences_to_texts([input_example_batch[0].numpy()[:50]]))))
print()
print('Next char prediction:\n', repr(''.join(tokenizer.sequences_to_texts([sampled_indices[:50]]))))*
➔输出:
*Input:
'📗 R e s t a u r a n t - S t y l e C o l e s l a w I \n \n 🥕 \n \n • 1 ( 1 6 o u n c e ) p'Next char prediction:
'H . î ⁄ ă ( “ I º Â 8 ̀ s % ù y “ © 0 ’ ‧ a ì ̀ r ă + o A € o + m × ␣ ︎ ñ ç ‱ ! S : ⅞ ´ r 2 ‧ D Q Á'*
正如您可能看到的,该模型提出了一些无意义的预测,但这是因为它尚未经过训练。
训练模型
我们希望训练我们的模型来生成尽可能与真实食谱相似的食谱。我们将使用数据集中的所有数据进行训练。在这种情况下,不需要提取测试或验证子集。
附加一个优化器和一个损失函数
我们将使用TF . keras . optimizer . Adam优化器和TF . keras . loss . sparse _ categorial _ cross entropy()损失函数来训练模型:
**# An objective function.*
*# The function is any callable with the signature scalar_loss = fn(y_true, y_pred).*
**def** **loss**(labels, logits):
entropy = tf.keras.losses.sparse_categorical_crossentropy(
y_true=labels,
y_pred=logits,
from_logits=**True**
)
**return** entropyexample_batch_loss = loss(target_example_batch, example_batch_predictions)print("Prediction shape: ", example_batch_predictions.shape, " # (batch_size, sequence_length, vocab_size)")
print("scalar_loss.shape: ", example_batch_loss.shape)
print("scalar_loss: ", example_batch_loss.numpy().mean())*
➔输出:
*Prediction **shape: ** (64, 2000, 176) *# (batch_size, sequence_length, vocab_size)*
**scalar_loss.shape: ** (64, 2000)
**scalar_loss: ** 5.1618285*
让我们最后编译这个模型:
*adam_optimizer = tf.keras.optimizers.Adam(learning_rate=0.001)model.compile(
optimizer=adam_optimizer,
loss=loss
)*
配置回调
提前停止回调
对于模型训练过程,我们可以配置一个TF . keras . callbacks . early stopping回调。如果模型在几个时期内没有改善,它将自动停止训练:
*early_stopping_callback = tf.keras.callbacks.EarlyStopping(
patience=5,
monitor='loss',
restore_best_weights=**True**,
verbose=1
)*
模型检查点回调
让我们还配置一个TF . keras . callbacks . model check point检查点,它将允许我们定期将训练好的权重保存到文件中,以便我们可以在以后从权重中恢复模型。
**# Create a checkpoints directory.*
checkpoint_dir = 'tmp/checkpoints'
os.makedirs(checkpoint_dir, exist_ok=**True**)checkpoint_prefix = os.path.join(checkpoint_dir, 'ckpt_{epoch}')
checkpoint_callback=tf.keras.callbacks.ModelCheckpoint(
filepath=checkpoint_prefix,
save_weights_only=**True**
)*
执行培训
让我们为500
个时期训练我们的模型,每个时期有1500
个步骤。对于每个历元步,将取出一批64
配方,并对那些长度为2000
的64
配方逐步执行梯度下降。
如果您正在试验训练参数,可能有必要将历元数减少到,比如说20
以及每个历元的步数,然后看看模型在该条件下的表现。如果模型提高了性能,您可以向训练过程添加更多数据(步骤和时期)。当你调整参数时,它可能会节省你一些时间。
*EPOCHS = 500
INITIAL_EPOCH = 1
STEPS_PER_EPOCH = 1500print('EPOCHS: ', EPOCHS)
print('INITIAL_EPOCH: ', INITIAL_EPOCH)
print('STEPS_PER_EPOCH: ', STEPS_PER_EPOCH)*
➔输出:
*EPOCHS: 500
INITIAL_EPOCH: 1
STEPS_PER_EPOCH: 1500*
让我们开始培训:
*history = model.fit(
x=dataset_train,
epochs=EPOCHS,
steps_per_epoch=STEPS_PER_EPOCH,
initial_epoch=INITIAL_EPOCH,
callbacks=[
checkpoint_callback,
early_stopping_callback
]
)*# Saving the trained model to file (to be able to re-use it later).*
model_name = 'recipe_generation_rnn_raw.h5'
model.save(model_name, save_format='h5')*
可视化培训进度
***def** **render_training_history**(training_history):
loss = training_history.history['loss'] plt.title('Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.plot(loss, label='Training set')
plt.legend()
plt.grid(linestyle='--', linewidth=1, alpha=0.5)
plt.show()render_training_history(history)*
➔输出:
培训流程(代码生成的图像)
ℹ️ 在上面的图表中,只显示了前 10 个时期。
从图表中我们可以看出,在培训期间,模特的表现越来越好。这意味着该模型学习预测下一个字符,使得最终序列看起来类似于一些真实的食谱文本。
生成配方
从最新的检查点恢复模型
为了使这个预测步骤简单,我们将恢复保存的模型,并以批处理大小 1 重新构建它。由于 RNN 状态是从一个时间步长传递到另一个时间步长的,该模型一旦建立就只接受固定的批量大小。为了使用不同的batch_size
运行模型,我们需要重建模型并从检查点恢复权重。
*tf.train.latest_checkpoint(checkpoint_dir)*
➔输出:
*'tmp/checkpoints/ckpt_1'*
让我们重建批量为1
的模型,并向其加载训练好的权重:
*simplified_batch_size = 1model_simplified = build_model(vocab_size, embedding_dim, rnn_units, simplified_batch_size)
model_simplified.load_weights(tf.train.latest_checkpoint(checkpoint_dir))
model_simplified.build(tf.TensorShape([simplified_batch_size, **None**]))model_simplified.summary()*
➔输出:
*Model: "sequential_6"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
embedding_6 (Embedding) (1, None, 256) 45056
_________________________________________________________________
lstm_5 (LSTM) (1, None, 1024) 5246976
_________________________________________________________________
dense_5 (Dense) (1, None, 176) 180400
=================================================================
Total params: 5,472,432
Trainable params: 5,472,432
Non-trainable params: 0
_________________________________________________________________*
让我们仔细检查输入形状是否简化了:
*model_simplified.input_shape*
➔输出:
*(1, None)*
预测循环
为了使用我们训练过的模型来生成配方,我们需要实现一个所谓的预测循环。下面的代码块使用循环生成文本:
- 它首先选择一个起始字符串,初始化 RNN 状态,并设置要生成的字符数。
- 它使用起始字符串和 RNN 状态获得下一个字符的预测分布。
- 然后,它使用分类分布来计算预测字符的索引。它使用这个预测的字符作为模型的下一个输入。
- 模型返回的 RNN 状态被反馈到模型中,因此它现在有更多的上下文,而不是只有一个字符。预测下一个字符后,修改后的 RNN 状态再次反馈到模型中,这是它从先前预测的字符中获得更多上下文时的学习方式。
图片来源:用 RNN 笔记本生成文本。
这里的temperature
参数定义了生成的配方有多模糊或者有多出乎意料。温度越低,文本越容易预测。更高的温度导致更令人惊讶的文本。你需要尝试找到最佳设置。我们将在下面不同的温度下做一些实验。
***def** **generate_text**(model, start_string, num_generate = 1000, temperature=1.0):
*# Evaluation step (generating text using the learned model)*
padded_start_string = STOP_WORD_TITLE + start_string *# Converting our start string to numbers (vectorizing).*
input_indices = np.array(tokenizer.texts_to_sequences([padded_start_string])) *# Empty string to store our results.*
text_generated = [] *# Here batch size == 1.*
model.reset_states()
**for** char_index **in** range(num_generate):
predictions = model(input_indices)
*# remove the batch dimension*
predictions = tf.squeeze(predictions, 0) *# Using a categorical distribution to predict the character returned by the model.*
predictions = predictions / temperature
predicted_id = tf.random.categorical(
predictions,
num_samples=1
)[-1, 0].numpy() *# We pass the predicted character as the next input to the model*
*# along with the previous hidden state.*
input_indices = tf.expand_dims([predicted_id], 0)
next_character = tokenizer.sequences_to_texts(input_indices.numpy())[0] text_generated.append(next_character) **return** (padded_start_string + ''.join(text_generated))*
为预测回路计算出合适的温度
现在,让我们使用generate_text()
来实际生成一些新的食谱。generate_combinations()
功能会检查第一个配方字母和温度的所有可能组合。它生成56
不同的组合来帮助我们弄清楚模型的表现以及使用什么温度更好。
***def** **generate_combinations**(model):
recipe_length = 1000
try_letters = ['', '\n', 'A', 'B', 'C', 'O', 'L', 'Mushroom', 'Apple', 'Slow', 'Christmass', 'The', 'Banana', 'Homemade']
try_temperature = [1.0, 0.8, 0.4, 0.2] **for** letter **in** try_letters:
**for** temperature **in** try_temperature:
generated_text = generate_text(
model,
start_string=letter,
num_generate = recipe_length,
temperature=temperature
)
print(f'Attempt: "{letter}" + {temperature}')
print('-----------------------------------')
print(generated_text)
print('\n\n')*
为了避免这篇文章太长,下面将只列出一些56
的组合。
*generate_combinations(model_simplified)*
➔输出:
*Attempt: "A" + 1.0
-----------------------------------
📗 Azzeric Sweet Potato Puree🥕• 24 large baking potatoes, such as Carn or Marinara or 1 (14-ounce) can pot wine
• 1/4 pound unsalted butter, cut into small pieces
• 1/2 cup coarsely chopped scallions📝▪︎ Bring a large pot of water to a boil, place a large nonstick skillet over medium-high heat, add All Naucocal Volves. Reduce heat to medium and cook the potatoes until just cooked through, bubbles before adding the next layer, about 10 to 12 minutes. Remove ground beans and reserve. Reserve the crumb mixture for about 6 greased. Let cool 2 minutes. Strain soak into a glass pitcher. Let cool in ice. Add short-goodfish to the batter and stir to dissolve. Pour in the cheese mixture and whisk until smooth. Set aside for 20 seconds more. Remove dumplings and cheese curds. Spread 1/3 cup of the mixture on each circle for seal ballo. Transfer mixture into a greased 9-by-11-inch baking dish and chill for 20 minutes.
▪︎ Bake, covered, for 30 minutes. Serve warm.
␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣Attempt: "A" + 0.4
-----------------------------------
📗 Apricot "Cookie" Cakes🥕• 1 cup all-purpose flour
• 1 cup corn flour
• 1 cup sugar
• 1 tablespoon baking powder
• 1 teaspoon salt
• 1 teaspoon ground cinnamon
• 1 cup grated Parmesan
• 1 cup pecans, chopped
• 1/2 cup chopped pecans
• 1/2 cup raisins📝▪︎ Preheat oven to 350 degrees F.
▪︎ Butter and flour a 9 by 13-inch baking dish. In a medium bowl, whisk together the flour, sugar, baking powder, baking soda and salt. In a small bowl, whisk together the eggs, sugar, and eggs. Add the flour mixture to the butter mixture and mix until just combined. Stir in the raisins and pecans and transfer to the prepared pan. Spread the batter over the top of the crust. Bake for 15 minutes. Reduce the oven temperature to 350 degrees F, and bake until the cupcakes are set and the top is golden brown, about 20 minutes more. Transfer the cake to a wire rack to cool to room temperature. Refrigerate until ready to serve.
␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣Attempt: "A" + 0.2
-----------------------------------
📗 Alternative to the Fondant🥕• 1 cup sugar
• 1 cup water
• 1 cup heavy cream
• 1 teaspoon vanilla extract
• 1/2 cup heavy cream
• 1/2 cup heavy cream
• 1 teaspoon vanilla extract
• 1/2 cup chopped pecans📝▪︎ In a saucepan over medium heat, combine the sugar, sugar, and corn syrup. Cook over medium heat until the sugar is dissolved. Remove from the heat and stir in the vanilla. Refrigerate until cold. Stir in the chocolate chips and the chocolate chips. Serve immediately.
␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣Attempt: "B" + 0.4
-----------------------------------
📗 Battered French Toast with Bacon, Bacon, and Caramelized Onions and Pecorino🥕• 1/2 pound squid (shredded carrots)
• 1 small onion, diced
• 1 small green pepper, seeded and cut into strips
• 1 red bell pepper, stemmed, seeded and cut into 1/4-inch dice
• 1 small onion, chopped
• 1 green bell pepper, chopped
• 1 cup chicken stock
• 1 cup heavy cream
• 1/2 cup shredded sharp Cheddar
• 1 teaspoon ground cumin
• 1 teaspoon salt
• 1 teaspoon freshly ground black pepper📝▪︎ Preheat the oven to 350 degrees F.
▪︎ For the bacon mixture: In a large bowl, combine the cheese, sour cream, mustard, salt, pepper, and hot sauce. Stir together and mix well. Fold in the milk and set aside.
▪︎ For the filling: In a large bowl, mix the flour and salt and pepper, to taste. Add the beaten eggs and mix to combine. Set aside.
▪︎ For the topping: Mix the cream cheese with the mayonnaise, salt and pepper in a medium bowl. Add the chicken and toss to coat the other side. Transfer the mixture to the preparedAttempt: "C" + 1.0
-----------------------------------
📗 Crema battered Salmon🥕• 1 cup fresh cranberries (from 4 tablespoons left of 4 egg whites)
• 3 teaspoons sugar
• 1 tablespoon unsalted butter
• 2 tablespoons truffle oil
• Coarse salt
• Freshly ground black pepper📝▪︎ Place cornmeal in a small serving bowl, and combine it. Drizzle milk over the plums and season with salt and pepper. Let stand for about 5 minutes, until firm. Serve immediately.
␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣Attempt: "C" + 0.8
-----------------------------------
📗 Classic Iseasteroles🥕• 3 cups milk
• 3/4 cup coconut milk
• 1/2 cup malted maple syrup
• 1/2 teaspoon salt
• 3 cups sugar
• 4 1-inch strawberries, sliced into 1/4-inch pieces
• 1/2 teaspoon ground cinnamon📝▪︎ Place the cherries in a small saucepan; sprinkle with the sugar. Bring to a simmer over medium-low heat, then remove from the heat. Let stand until the coconut fluffy, about 15 to 20 minutes. Drain the coconut oil in a stream, whisking until combined. Add the cream, espresso and cocoa powder and stir to combine. Cover and refrigerate until ready to serve. Makes 10 to 12 small springs in the same fat from the surface of the bowl, which using paper colors, and freeze overnight.
▪︎ Meanwhile, combine the cream, sugar, vanilla and salt in a medium saucepan. Cook over medium heat until the sugar dissolves and the sugar melts and begins to boil, about 5 minutes. Remove from the heat and stir in the vanilla.
▪︎ To serve, carefully remove the pops from the casserole and put them inAttempt: "C" + 0.4
-----------------------------------
📗 Cinnamon Corn Cakes with Coconut Flour and Saffron Sauce🥕• 3 cups shredded sharp Cheddar
• 1 cup grated Parmesan
• 2 cups shredded sharp Cheddar
• 1 cup grated Parmesan
• 1 cup shredded part-skim mozzarella cheese
• 1 cup grated Parmesan
• 1 cup grated Parmesan
• 1 cup grated Parmesan
• 1 teaspoon kosher salt
• 1/2 teaspoon freshly ground black pepper📝▪︎ Preheat the oven to 400 degrees F. Line a baking sheet with a silpat and preheat the oven to 350 degrees F.
▪︎ In a large bowl, combine the masa harina, cumin, cayenne, and salt and pepper. Dredge the pasta in the flour and then dip in the egg mixture, then dip in the eggs, then dip in the egg mixture and then dredge in the breadcrumbs. Place the breaded cheese on a sheet tray. Bake until the crust is golden brown and the filling is bubbling, about 25 to 30 minutes. Remove from the oven and serve hot.
␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣ Attempt: "L" + 0.4
-----------------------------------
📗 Lighted Flan with Chocolate and Pecans🥕• 2 cups milk
• 1 cup sugar
• 1 teaspoon vanilla extract
• 1 cup heavy cream
• 1/2 cup heavy cream
• 1 tablespoon powdered sugar
• 1 teaspoon vanilla extract
• 1/2 cup heavy cream
• 1/2 teaspoon ground cinnamon
• 1/2 teaspoon ground nutmeg
• 1/2 cup chopped pecans📝▪︎ Watch how to make this recipe.
▪︎ In a small saucepan, combine the sugar, salt, and a pinch of salt. Cook over medium heat, stirring occasionally, until the sugar has dissolved. Remove from the heat and set aside to cool. Remove the cherries from the refrigerator and place in the freezer for 1 hour.
▪︎ In a blender, combine the milk, sugar, vanilla, salt and water. Blend until smooth. Pour the mixture into a 9-by-13-inch glass baking dish and set aside.
▪︎ In a small saucepan, combine the remaining 2 cups sugar, the vanilla, and 2 cups water. Bring the mixture to a boil, and then reduce the heat to low. Cook until the sugar is dissolved, about 5 minutes. Remove from the heat anAttempt: "L" + 0.2
-----------------------------------
📗 Lighted Fondanta with Chocolate and Cream Cheese Frosting🥕• 1 cup heavy cream
• 1 tablespoon sugar
• 1 tablespoon vanilla extract
• 1 teaspoon vanilla extract
• 1 cup heavy cream
• 1 cup heavy cream
• 1/2 cup sugar
• 1 teaspoon vanilla extract
• 1 teaspoon vanilla extract
• 1/2 cup chopped pistachios📝▪︎ Preheat the oven to 350 degrees F.
▪︎ In a large bowl, combine the cream cheese, sugar, eggs, vanilla, and salt. Stir until smooth. Pour the mixture into the prepared baking dish. Sprinkle with the remaining 1/2 cup sugar and bake for 15 minutes. Reduce the heat to 350 degrees F and bake until the crust is golden brown, about 15 minutes more. Remove from the oven and let cool completely. Spread the chocolate chips on the parchment paper and bake until the chocolate is melted and the top is golden brown, about 10 minutes. Set aside to cool.
▪︎ In a medium bowl, whisk together the egg yolks, sugar, and vanilla until smooth. Stir in the cream and continue to beat until the chocolateAttempt: "Mushroom" + 1.0
-----------------------------------
📗 Mushroom and Bacon Soup with Jumbo Sugar Coating🥕• 2 tablespoons vegetable oil
• 1 2/3 pounds red cabbage, shredded, about 4 cups of excess pasted dark ends of fat, and pocked or firm
• 2 red bell peppers, cored, seeded and diced
• 1 poblano pepper, chopped
• 3 medium carrots, finely chopped
• 1/2 medium pinch saffron
• 4 cups water
• 2 cups mushrooms or 1/2 cup frozen Sojo Bean red
• Salt and freshly ground black pepper
• 1 pound andouille sausage
• 1 gallon vegetable broth
• Chopped fresh parsley, cilantro leaves, for garnish📝▪︎ In a large Dutch oven for gas burner, heat oil over moderate heat. Add the leeks to the pot, scraping the bottom of the skillet. Add the beans and sausage and sprinkle the reserved potatoes with some orange juice cooked sausage (such as The Sauce.) Add roasted vegetables and pinto beans, mozzarella, basil and bamboo shoots. Simmer rice until soup is absorbed, 15 to 20 minutes.
▪︎ Bring another pan of water to a boil and cook shrimp for 5 minutes. While onionsAttempt: "Mushroom" + 0.8
-----------------------------------
📗 Mushrooms with Lentil Stewed Shallots and Tomatoes🥕• 1 tablespoon olive oil
• 3 cloves garlic, smashed
• Kosher salt
• 1 1/2 pounds lean ground turkey
• 1 cup coarsely peeled tart apples
• 2 tablespoons chopped garlic
• 1 teaspoon ground cumin
• 1/2 teaspoon cayenne pepper
• 1 teaspoon chopped fresh thyme
• 3/4 cup chopped fresh basil
• 1/2 small carrot, halved lengthwise and cut into 1/2-inch pieces
• 1 roasted red pepper, halved and sliced vertically diced and separated into rough chops
• 3 tablespoons unsalted butter
• 2 cups shredded mozzarella
• 1/4 cup grated parmesan cheese
• 1/4 cup prepared basil pesto📝▪︎ Stir the olive oil, garlic, thyme and 1 teaspoon salt in a saucepan; bring to a simmer over medium heat. Remove from the heat. Add the basil and toast the soup for 2 minutes.
▪︎ Meanwhile, heat 4 to 4 inches vegetable oil in the skillet over medium-high heat. Add the olive oil, garlic, 1/2 teaspoon salt and 1/2 teaspoon pepper and cook, stirring often, until cooked through, aAttempt: "Mushroom" + 0.4
-----------------------------------
📗 Mushroom Ravioli with Chickpeas and Shiitake Mushrooms and Sun-Dried Tomatoes🥕• 1 pound zucchini
• 1 cup chicken broth
• 1 cup fresh basil leaves
• 1/2 cup chopped fresh basil leaves
• 1/2 cup grated Parmesan
• 1 teaspoon salt
• 1/2 teaspoon freshly ground black pepper
• 1 teaspoon chopped fresh thyme
• 1 teaspoon fresh lemon juice
• 2 cups chicken broth
• 1/2 cup grated Parmesan
• 1/2 cup grated Parmigiano-Reggiano📝▪︎ Preheat oven to 450 degrees F.
▪︎ Place the bread cubes in a large bowl. Add the basil, parsley, olive oil, parsley, thyme, basil, salt and pepper and toss to coat. Spread the mixture out on a baking sheet and bake until the sausages are cooked through, about 20 minutes. Serve immediately.
▪︎ In a small saucepan, bring the chicken stock to a boil. Reduce the heat to low and cook the soup until the liquid is absorbed. Remove from the heat and stir in the parsley, shallots and season with salt and pepper. Serve immediately.
␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣Attempt: "Mushroom" + 0.2
-----------------------------------
📗 Mushroom and Spicy Sausage Stuffing🥕• 1 tablespoon olive oil
• 1 medium onion, chopped
• 2 cloves garlic, minced
• 1 cup frozen peas
• 1 cup frozen peas
• 1/2 cup chopped fresh parsley
• 1/2 cup grated Parmesan
• 1/2 cup grated Parmesan
• 1 teaspoon salt
• 1/2 teaspoon freshly ground black pepper
• 1 cup shredded mozzarella
• 1/2 cup grated Parmesan
• 1 cup shredded mozzarella
• 1 cup shredded mozzarella cheese📝▪︎ Preheat the oven to 350 degrees F.
▪︎ Bring a large pot of salted water to a boil. Add the pasta and cook until al dente, about 6 minutes. Drain and reserve.
▪︎ Meanwhile, heat the olive oil in a large skillet over medium-high heat. Add the shallots and saute until tender, about 3 minutes. Add the garlic and cook for 1 minute. Add the sausage and cook until the shallots are tender, about 3 minutes. Add the sausage and cook until tender, about 2 minutes. Add the garlic and cook, stirring, until the garlic is lightly browned, about 1 minute. Add the sausage and cook until the s*
交互式模型演示
你可以使用🎨 烹饪食谱生成器演示 使用这个模型,输入文本和温度参数,就可以在浏览器中生成一些随机的食谱。
来自机器学习实验的屏幕记录
需要改进的地方
这超出了本文的范围,但是模型仍然有以下问题需要解决:
- 我们需要删除配料部分的重复内容。
- 食谱部分(名称、配料和烹饪步骤)大部分时间是不连贯的,这意味着我们可能会在配料部分看到
mushrooms
,但它们不会在食谱名称或烹饪步骤中提及。
更多更新和新文章请在 Twitter 上关注我
使用神经网络生成 Eminem 歌词
我如何使用递归神经网络实现歌词生成器
深度学习不仅仅是对猫和狗进行分类或预测泰坦尼克号乘客是否幸存,这不是什么秘密。无论是将文本从一种语言翻译成另一种语言,同时保留语义(像人类一样),还是比以往任何时候都更准确地识别人脸,深度学习已经取得了无数奇迹。
这篇文章是使用 TensorFlow 实现 Eminem 歌词生成器(更正式地说,是无监督文本生成器)的分步指南。先来偷看一下主要内容:
内容:
- 递归神经网络简介
- 使用 RNNs 生成文本
- 实施
- 结论
递归神经网络简介
一个典型的 RNN via Colah 的博客
在本节中,我们将对 RNNs 及其几种风格进行高度概括。
神经网络的基本操作是通过由许多嵌套加权和组成的非常复杂的数学函数将输入(自变量)映射到输出(因变量)。这很好,但对于序列或系列数据,因变量在序列或系列中任何给定点的值(通常称为时间步长)不仅是定义它的自变量的函数,也是该序列中因变量的先前值。这可以用任何特定的文本片段的例子来最好地解释,其中任何给定的单词都取决于它添加到句子以及上下文中之前的单词的含义,这给文本添加了语法意义(是的,正是我们要如何生成文本)。
这种建模是通过递归神经网络实现的。这里,输出与输入的加权和一起,还有一个来自先前输出的额外存储单元。前面提到的图表清楚地描绘了 RNN 的架构。在任何给定的时间步长,输入都经过隐藏层,其输出是通过将先前的隐藏层值与当前输出相结合来计算的。该计算值也被保存以在下一个时间步中被组合,因此是“存储元素”。
rnn 工作正常,但是有一些问题,
- 消失梯度问题
- 长期依赖
对这些问题的解释超出了本文的范围,但简而言之,消失梯度意味着在计算权重校正的梯度时,当我们返回时间步长时,梯度往往会减小,最终“消失”。而长期依赖性是普通(普通)RNNs 不能记住更长序列的限制。这些问题由 RNNs 的“后代”——长短期记忆网络或 LSTMs 解决。这些基本上有一个更复杂的单元,具有一个门控结构,确保保存更长的序列,并解决消失梯度问题。LSTM 的一个众所周知的特色是门控循环单元(GRU ),它是 LSTM 的一个增强,用于更快的计算。
LSTM 通过科拉的博客
Colah 的博客是迄今为止对 RNNs 最好的解释之一(在我看来)。如果您有兴趣了解 RNNs 背后的数学原理,可以考虑阅读一下。
使用 RNNs 的文本生成
在这一节中,我们将讨论如何在我们的文本生成问题中使用 RNNs,并对实际工作原理有一个粗浅的了解。
模型的训练输入是给定歌词中的单词,目标是歌词中的下一个单词。以下是相同的图示描述:
文本生成英尺。说唱上帝
所以,这到底是怎么回事?
众所周知,神经网络可以学习输入和输出之间的一般模式。我的意思是,当我训练一个神经网络来预测“look”的“I”和“go”的“easy”+许多不同歌曲的 RNNs 的记忆元素时,神经网络倾向于学习这些歌词中相似的模式。这种模式正是阿姆写作的方式,也就是他写歌的风格,如果你仔细观察的话,这是独一无二的(我们也有自己独特的风格)。现在,经过训练后,如果我们让模型自己预测单词,它将根据他的歌词生成由单词组成的文本,并遵循他的写歌风格(还不如放弃自己的阿姆热门歌曲谁知道呢!).这概括了模型的主要思想。
履行
我们终于开始真正的交易了。密码!
我已经手动收集了阿姆的 15 首歌曲到文本文件中,我们认为每首歌曲都是一个单独的训练样本
首先,我们读取文件并清理数据,使其能够被模型处理:
歌词的最小预处理
正如上一节所解释的,我们训练模型来预测歌词中的下一个词,给定前一个词和最近的一些上下文。有两种方法可以实现这一点:
- 取歌曲的第一个词,获得输出,计算预测的损失,使用该预测来预测下一个词,等等。在这种方法中,如果第一个预测的字不正确,那么丢失将导致大量丢失值的级联,因为输入远远不正确。
- 取歌曲的第一个词,获得输出,计算预测的损失,在下一个时间步,我们不使用预测的词作为输入,而是使用实际的词作为输入。这确保了没有级联损失,并且每个时间步长的损失是相互独立的。这种方法被称为‘老师强迫’。
为了更好更快的收敛,我们将继续进行教师强制。为此,我们将原始歌词作为输入,并将原始歌词的偏移量 1 作为目标。这意味着,如果输入是“看,我要去”,那么目标将是“我要去容易”(偏移 1)。
分离输入和输出
接下来,我们将执行一般的 NLP 步骤,以确保我们对模型的输入是数字。我们将单词标记为整数,然后将序列填充或截断为固定长度,因为模型的所有输入都应该是固定长度的。
标记化和填充
作为一个可行性工具,我们有一个词汇表,可以在令牌和相应的单词之间互换。此外,我们定义了一些常量,可以根据型号和硬件资源的可用性进行调整(我已经考虑了最小值)。
词汇和其他超参数
现在,我们将最终建立模型
训练文本生成器的最简模式
我已经训练了一个极简模型来展示这个概念,它可能会根据要求进行调整。模型调优再次超出了本文的范围。然而,我将描述基本组件。
- 稀疏分类交叉熵损失 可以与具有一个热编码输出序列的分类交叉熵互换使用(在这种情况下,这将是一个巨大的数组,因此计算量大)。
关于 tensorflow 预定义损失的更多信息:https://www.tensorflow.org/api_docs/python/tf/keras/losses。
关于损失函数的更多信息:https://towardsdatascience . com/common-loss-functions-in-machine-learning-46 af 0 ffc 4d 23
- 亚当 优化器 增加收敛速度。
更多关于 tensorflow 优化器:https://www . tensor flow . org/API _ docs/python/TF/keras/optimizer。
关于亚当优化器的更多信息:【https://arxiv.org/abs/1412.6980。
更多关于优化者的一般信息:【https://ruder.io/optimizing-gradient-descent/
以下是模型培训中的一些冗长内容:
Epoch 45/50
1/1 [==============================] - 0s 2ms/step - loss: 0.0386
Epoch 46/50
1/1 [==============================] - 0s 1ms/step - loss: 0.0381
Epoch 47/50
1/1 [==============================] - 0s 1ms/step - loss: 0.0375
Epoch 48/50
1/1 [==============================] - 0s 2ms/step - loss: 0.0369
Epoch 49/50
1/1 [==============================] - 0s 6ms/step - loss: 0.0364
Epoch 50/50
1/1 [==============================] - 0s 2ms/step - loss: 0.0359
对于极简主义者来说相当不错!现在让我们测试结果。
由于我们已经使用教师强制进行训练,对于未知输出的预测,我们需要使用第一种方法,因为我们不知道“实际”输出,我们所拥有的只是下一个预测的单词。我们称之为“推理步骤”。
推理步骤,“无需”教师强迫
将“slim”作为输入传递给生成器,我得到了以下输出:
shady is alright because i am frozen would know sometimes things have tried ideas gonna stand upi am frozen would know sometimes things have tried ideas gonna stand upi am frozen would know sometimes things have tried ideas gonna stand up
结论
正如我前面提到的,这个模型是用最少的参数和 15 首歌来训练的。一个模型很有可能用如此少量的数据过度拟合,并且它显示;一些短语被重复多次。这可以通过适当的调优和更大的数据语料库来解决。
这篇文章的主要内容是文本生成的概念。该模型是通用的,并且可以在任何类型的语料库(比如一本书)上被训练,以生成期望流派或模式的文本。
这里的是指向 github 代码库的链接。随意分叉它,微调模型,训练更多的数据,提出拉请求(如果可以的话)。
参考
- https://colah.github.io/posts/2015-08-Understanding-LSTMs/
- https://www.tensorflow.org/api_docs/python/tf/keras
- https://ruder.io/optimizing-gradient-descent/
- https://arxiv.org/abs/1412.6980
我为数据科学制作了 1000 多份假的约会资料
我如何使用 Python Web Scraping 创建约会档案
D ata 是世界上最新、最珍贵的资源之一。公司收集的大部分数据都是私有的,很少与公众分享。这些数据可能包括一个人的浏览习惯、财务信息或密码。对于 Tinder 或 Hinge 等专注于约会的公司来说,这些数据包含用户的个人信息,他们自愿在约会档案中披露这些信息。因为这个简单的事实,这些信息是保密的,公众无法访问。
但是,如果我们想创建一个使用这些特定数据的项目,该怎么办呢?如果我们想创建一个使用机器学习和人工智能的新约会应用程序,我们需要大量属于这些公司的数据。但是可以理解的是,这些公司将用户数据保密,远离公众。那么我们如何完成这样的任务呢?
嗯,基于交友档案中缺乏用户信息,我们需要为交友档案生成虚假的用户信息。我们需要这些伪造的数据,以便尝试将机器学习用于我们的约会应用。这个应用程序的想法起源可以在以前的文章中读到:
medium.com](https://medium.com/@marcosan93/applying-machine-learning-to-find-love-3558dafcc4a1)
前一篇文章讨论了我们潜在的约会应用程序的布局或格式。我们将使用一种叫做 K-Means 聚类 的机器学习算法,根据他们对几个类别的回答或选择,对每个约会档案进行聚类。此外,我们会考虑他们在简历中提到的另一个因素,这也是对个人资料进行聚类的一个因素。这种形式背后的理论是,一般来说,人们更容易与拥有相同信仰(政治、宗教)和兴趣(体育、电影等)的人相处。)。
有了约会应用的想法,我们可以开始收集或伪造我们的假资料数据,以输入我们的机器学习算法。如果以前已经创建了类似的东西,那么至少我们会学到一些关于自然语言处理( NLP )和 K-Means 聚类中的无监督学习的知识。
伪造假档案
我们需要做的第一件事是找到一种方法来为每个用户配置文件创建一个假的个人资料。没有可行的方法可以在合理的时间内编写成千上万的假 bios。为了构建这些假的 bios,我们将需要依靠第三方网站来为我们生成假的 bios。有很多网站会为我们生成虚假的个人资料。然而,我们不会显示我们选择的网站,因为我们将实施网络抓取技术。
使用 BeautifulSoup
我们将使用 BeautifulSoup 来浏览假的生物发生器网站,以便抓取生成的多个不同的 bios,并将它们存储到熊猫数据帧中。这将使我们能够多次刷新页面,以便为我们的约会档案生成必要数量的假 bios。
我们做的第一件事是导入所有必要的库来运行我们的 web-scraper。我们将解释 BeautifulSoup 正常运行的特殊库包,例如:
- 允许我们访问我们需要抓取的网页。
- 为了在网页刷新之间等待,需要使用
time
。 tqdm
只是为了我们的缘故才需要作为装货港。bs4
需要使用 BeautifulSoup。
抓取网页
代码的下一部分涉及为用户 bios 抓取网页。我们首先创建一个从 0.8 到 1.8 的数字列表。这些数字表示我们在两次请求之间等待刷新页面的秒数。我们创建的下一个东西是一个空列表,用于存储我们将从页面中抓取的所有 bios。
接下来,我们创建一个将刷新页面 1000 次的循环,以生成我们想要的 bios 数量(大约 5000 个不同的 bios)。这个循环被tqdm
所环绕,以创建一个加载或进度条来显示我们还剩多少时间来完成这个站点的抓取。
在循环中,我们使用requests
来访问网页并检索其内容。使用try
语句是因为有时用requests
刷新网页不会返回任何结果,并且会导致代码失败。在那些情况下,我们将简单地pass
到下一个循环。try 语句中是我们实际获取 bios 并将它们添加到我们之前实例化的空列表中的地方。在当前页面中收集了 bios 之后,我们使用time.sleep(random.choice(seq))
来决定要等多久才能开始下一个循环。这样做是为了使我们的刷新基于从我们的数字列表中随机选择的时间间隔而被随机化。
一旦我们从网站上获得了所有需要的 bios,我们将把 bios 列表转换成熊猫数据框架。
为其他类别生成数据
为了完成我们的假约会档案,我们需要填写宗教、政治、电影、电视节目等其他类别。下一部分非常简单,因为它不需要我们从网上抓取任何东西。本质上,我们将生成一个随机数列表,应用于每个类别。
我们要做的第一件事是为我们的约会档案建立分类。这些类别然后被存储到一个列表中,然后被转换成另一个熊猫数据帧。接下来,我们将遍历我们创建的每个新列,并使用numpy
为每行生成一个从 0 到 9 的随机数。行数由我们在之前的数据帧中能够检索到的 bios 数量决定。
包含每个假约会简介的个人资料和类别的数据框架
一旦我们有了每个类别的随机数,我们就可以将生物数据框架和类别数据框架连接在一起,以完成我们的假约会资料的数据。最后,我们可以将最终的数据帧导出为一个.pkl
文件,以备后用。
走向
现在我们已经有了我们的假约会档案的所有数据,我们可以开始探索我们刚刚创建的数据集。使用 NLP ( 自然语言处理),我们将能够深入了解每个约会档案的 bios。在对数据进行了一些探索之后,我们实际上可以开始使用 K-Mean 聚类进行建模,以将每个配置文件相互匹配。请关注下一篇文章,它将讨论如何使用 NLP 来探索 bios 以及 K-Means 集群。
medium.com](https://medium.com/swlh/using-nlp-machine-learning-on-dating-profiles-1d9328484e85)
资源
github.com](https://github.com/marcosan93/AI-Matchmaker) [## 可以用机器学习来寻找爱情吗?
medium.com](https://medium.com/@marcosan93/applying-machine-learning-to-find-love-3558dafcc4a1)
用混合器和动画节点生成分形
这是一个使用 Blender 生成分形视觉效果的教程、例子和资源的集合。我将涵盖递归、n-flakes、L-系统、Mandelbrot/Julia 集和导子等主题。
为什么:如果你对分形图案着迷,对 Blender 中的过程化生成感兴趣,想对动画-节点和着色器节点有更好的理解和动手体验。
谁:此处所有内容均基于 Blender 2.8.2 和动画-节点 v2.1 。我还依赖了一些简短的代码片段(Python ≥ 3.6)。
分形
“美丽复杂,却又如此简单”
分形是具有分形维数的形状。这源于我们在它们扩展时测量它们的方式。理论分形在不同的尺度上是无限自相似的。
对于我们的设置,我们不关心纯理论分形,因为除非在非常特殊的情况下,否则在 Blender 中是无法实现的。我们最关心的是精细结构(不同尺度的细节)和自相似的自然外观(由明显更小的自身副本组成)。
了解分形更多信息的其他建议资源:
- 【YouTube】分形上的 3 blue 1 brown
- 【本书】benot b . Mandelbrot 著《自然的分形几何》
- 【书】分形:肯尼斯·法尔科内的简短介绍
- przemyslaw Prusinkiewicz 的《植物的算法之美》
n-薄片
n-flake(或 polyflake) 是一种分形,通过用自身的多个缩放副本替换初始形状来构建,通常放置在对应于顶点和中心的位置。然后对每个新形状递归重复该过程。
递归
这是我们探索分形的一个很好的起点,因为它允许我们通过一个简单的动画节点设置来涵盖递归和其他重要概念。
这就引出了我们的主要问题:动画节点(目前)不支持纯递归。我们需要一些变通办法。一种选择是始终依赖纯 Python 脚本,正如我在上一篇文章中解释的那样,但我希望这篇文章的重点更多地放在动画节点上,所以我们可以做的是通过迭代和循环队列来近似递归。
这个想法是依靠循环输入 重新分配选项来保存一个结果队列,我们可以在下一次迭代中处理这些结果。
让我们考虑正多边形的 n 片情况。给定若干线段n
(多边形的边数)、半径r
和中心c
,我们计算以c
为中心、半径r
的n
-多边形的点。对于每个新计算的点p
,我们重复这个过程(即,找到以p
为中心的多边形),但是通过一些预定义的因子调整半径r
。
n 多边形动画-实际的节点设置
所有主要的逻辑都在下面的循环中,它负责计算多边形点和新的半径,并重新分配它,以便下一次迭代可以处理它们。理解这一部分是很重要的:在第一次迭代中,循环处理给予Invoke Subprogram
节点的任何东西,而对于所有后续的迭代,循环将处理它在前一次迭代中更新的值。每次这些都被清理,这就是为什么我们维护两个队列(centers_queue
和all_centers
),前者是我们还没有处理的中心,后者是到目前为止计算的所有中心的集合,它将被用作输出来创建我们的样条列表。
带有缩放和重新分配逻辑的主循环
这两个子程序都是 Python 脚本。这些例子让我觉得编码更简洁,不需要翻译成纯粹的节点。
计算正多边形的点:
points = []
# compute regular polygon points for each given center
for center in centers:
angle = 2*pi/segments # angle in radians
for i in range(segments):
x = center[0] + radius*cos(angle*i)
y = center[1] + radius*sin(angle*i)
z = center[2] # constant z values
points.append((x, y, z))
#points.append(points[-segments]) # close the loop
获取比例因子:
from math import cos, pi# compute scale factor for any n-flake
# [https://en.wikipedia.org/wiki/N-flake](https://en.wikipedia.org/wiki/N-flake)
cumulative = 0
for i in range(0, segments//4):
cumulative += cos(2*pi*(i+1) / segments)
scale_factor = 1 / (2*(1 + cumulative))
额外的循环设置是分割点,以便将每个多边形转化为单独的样条线。
转向 3D 规则多面体
我们可以很容易地调整这种设置来处理常规的固体。这一次,我们不是自己计算形状顶点,而是依赖于场景中已经存在的对象,获取它的顶点并递归地变换它们。我们利用矩阵属性和Compose Matrix
节点来最小化所需的工作量或节点。这是设置
Out matrix_queue
用恒等式变换矩阵(无平移、无旋转和单位比例)初始化,而我们的transformation
列表是我们的目标/输入对象顶点位置,由任意比例因子组成,当用于变换另一个矩阵时,这相当于说“在那里移动一切,并按给定因子缩放”。
这里是执行转换循环和重新分配操作的改编递归部分。
柏拉图立体的示例结果(介于四次和五次迭代之间)。
我讨厌二十面体
然后,可以更新原始平移矩阵,使得新实体不精确地放置在前身顶点上,而是放置在穿过前身中心和新顶点的线的任何点上,如侧面动画中所示。
虽然之前的转换矩阵仅仅是顶点的位置,但是我们现在做了一些矢量数学来将这些点从原始位置转移。如果我们从它们中减去物体中心,我们就得到感兴趣的方向向量。然后,我们可以将它除以任意值,以相应地放置新的递归实体。
如果我们只实例化最后一次迭代的对象,我们会得到正确的柏拉图立体分形。众所周知的例子是 Sierpinski 四面体和 Menger 海绵。
Eevee 中渲染的示例,增加了布尔运算符以增加趣味性
要尝试的事情
- 在递归步骤中任意/随机更改 polyflake 类型
- 使用置换贴图来模拟额外的递归步骤,而无需创建更精细的额外几何体
- 体积和纯着色器节点设置
l 系统
一个 L-系统(或林登迈耶系统)是一个文法;指定如何通过一组规则、字母表和初始公理生成文本字符串的东西。它伴随着将这种字符串转换成几何图形的转换机制。
我们对 L 系统感兴趣,因为它们可以用来生成自相似分形,也因为我们可以通过LSystem
节点免费获得它们。这里是一个将结果实例化为 object 的示例设置。
边上的是由公理X
(初始状态)和两条规则X=F[+X][-X]FX
和F=FF
定义的分形图形。节点负责使系统进化到指定的代数,这可以是很好的分数,允许平滑过渡。它还将文本结果转换为网格。由于这个原因,你必须遵循公理和字母用法的特定惯例。
另一个需要考虑的重要参数是高级节点设置中的Symbol Limit
,它指定了生成的字符串的最大长度。如果超过这个限制,系统将抛出一个错误。
例子
龙曲线,分形植物,sierpinski 三角形
dragon curve - axiom: FX, rules: (X=X+YF, Y=-FX-Y), angle: 90fractal plant - axiom: F, rules: (F=FF-[-F+F+F]+[+F-F-F]), angle: 22.5sierpinski triangle - axiom: F-F1-F1, rules: (X=X+YF, Y=-FX-Y), angle: 120
要尝试的事情
- 3D L 系统(例如方位角+倾斜度)和随机 L 系统。(见 Python 代码)
- 步长规则,与增长成比例,这样视图可以在增加代数时保持不变
曼德尔布罗特
作为分形之父,最常见和最频繁展示的分形以他的名字命名也就不足为奇了。Mandelbrot 集合与 Julia 集合一起为一种现象提供了理论基础,这种现象也是由简单的规则定义的,但能够产生一幅美丽和复杂的无限画面。
我们可以把这些集合看作是“用坐标形式表示的函数的迭代”,其中我们处理的是复数的平面。Jeremy Behreandt 的这篇文章提供了关于复数、Mandelbrot 集合和更多内容的令人印象深刻的详细介绍,所有这些都包含在 Blender 的 Python API 和开放着色语言(OSL)中。
我们完全可以在 Blender 节点中实现类似的结果(没有脚本),但是像往常一样,我们受到设置中缺少的迭代/递归功能的限制。基本思想是围绕复数平面移动,如公式所示
根据每个点收敛到无穷大的速度给每个点着色。这里z
是我们的起点,c
可以是任意选择的复数。该公式可以用纯笛卡尔平面坐标形式改写为
这里我们现在用(x,y)
作为我们的点,用a
和b
作为我们的控制值(之前c
的实部和虚部)。
这里是着色器节点设置中公式的翻译
坐标形式函数的节点设置
iterations_count 部分用于跟踪有效的迭代,那些点没有分叉的迭代。出于实用目的,这可以通过检查向量幅度是否小于 2 来近似计算。
Mandelbrot 集合的设置
然后我们可以插入我们的纹理坐标,通过分离x
和y
如图所示。为a
和b
重用它们给了我们 Mandebrot 集合,或者我们可以传递自定义值来探索其他集合和结果。现在是丑陋的部分:我们必须通过非常不优雅的复制粘贴混乱来近似函数迭代,对我来说最终看起来像这样。100 次迭代是提供良好结果的合理估计,你添加的越多,你得到的细节就越多,但是设置会很快变得很慢。
是..
但至少我们现在有东西可以玩了
曼德尔布尔
Mandelbulb 是 Mandelbrot 集合的 3D 投影。另外已经有一个很棒的教程由 Jonas Dichelle 编写,解释了如何在 Blender nodes 和 Eevee 中模拟 Mandelbulb。鉴于这种方法依赖于体积学,也值得参考一下 Gleb Alexandrov 在 Blender 会议上关于 3D 星云的演讲。
玩弄公式和颜色
结论
我总是被那些从简单规则中展现出复杂性的现象所吸引,而将它们可视化的能力让这一切变得更加有趣。Blender 只是让它变得如此简单,而 animation-nodes 只是已经存在的一组令人惊叹的工具/实用程序的又一个补充,特别是从过程的角度来看。
我承认远离纯粹的编码对我来说有点可怕,就像一种发自内心的感觉,我没有做正确的事情,但是从迭代和实验的角度来看,毫无疑问我可以通过使用节点来实现效率。我仍然相信一些部分更适合作为单独的脚本,并且人们应该总是首先用代码构建/理解基础,因为它为复杂性分解和组织以及解决问题的一般化提供了更好的框架。
我计划探索更多类似的主题,完善理论理解和实践能力,以令人信服的方式再现它们。
具体来说,这些是当前最热门的条目:
- 超越三维
- 元胞自动机
- 形态发生
- 反应扩散
- 奇怪的吸引子
- 棋盘形布置
- 本轮
我欢迎任何建议、批评和反馈。
你可以在 Instagram 上看到更多我“润色”的图形结果,在 Twitter 上阅读更多技术/故障解释,在 Github 上试用我的代码。
使用 LSTMs 和混合密度网络生成手写序列
由于每个人都在年初提出了一个解决方案,所以我会尽量少在博客上发帖😀。已经过去一个多月了,我想在这里分享一些话😀。在这篇博客中,我们将讨论一篇由 Alex Graves(DeepMind)提出的关于用递归神经网络生成序列的有趣论文。我还将使用 Tensorflow 和 Keras 来实现这篇论文。
问题定义
这里的数据集是手写笔画的数学表示形式。所以笔画序列中的一个点是一个长度=3 的向量。第一个值是一个二进制数字,表示笔是否在该点上升起。第二个值是 x 坐标相对于序列中前一个 x 值的偏移量,同样,第三个值是 y 坐标的偏移量。问题是,给定数据集,我们需要一个可以无条件生成手写笔画的模型(通过给定一个种子值随机生成,就像 GANs 一样)。
混合密度网络
在进入实现部分之前,我想讨论一种特殊的网络,称为 MDNs 或混合密度网络。监督问题的目标是对要素和标注的生成器或联合概率分布进行建模。但是在大多数情况下,这些分布被认为是时不变的,例如在图像或文本分类的情况下。但是,当概率分布本身不稳定时,比如手写笔画或汽车的路径坐标,正常的神经网络在这种情况下表现很差。混合密度网络假设数据集是各种分布的混合物,并倾向于学习这些分布的统计参数,从而获得更好的性能。迷茫?
来源:https://media1.giphy.com/media/3o7btPCcdNniyf0ArS/giphy.gif
好吧,好吧,我们会进入更多的细节。
让我们举一个下图所示的例子:
来源:https://cedar . buffalo . edu/~ Sri Hari/CSE 574/chap 5/chap 5.6-mixdensitynetworks . pdf
在第一张图中,给定θ1 和θ2,当被问及预测机械臂的位置时,我们有一个独特的解决方案。现在看第二个图,问题反过来了。给定 x1 和 x2,当被问及预测θ参数时,我们得到两个解。在大多数情况下,我们会遇到像第一张图这样的问题,其中数据分布可以被假设为来自单个高斯分布。而对于像第二幅图这样的情况,如果我们使用传统的神经网络,它的性能不会下降。我很快会告诉你为什么。
这并不是说具有均方损失或交叉熵损失的神经网络不考虑(混合分布)的事情。它考虑了这一点,但给出了一个结果分布,它是所有这些混合物的平均值,这篇论文建议我们不需要平均值,而是最可能的混合物成分来模拟统计参数。因此 MDN 进入了画面。
它看起来怎么样?
因此,MDN 只不过是一个预测统计参数的神经网络,而不是预测回归问题的类或值的概率。让我们假设数据是 M 正态分布的混合物,因此最终预测将是混合物的 M 概率权重、 M 平均值和 M 方差。取样时,我们取最可能的混合物成分的参数。我们将在后面详细讨论其结构和实现。
长短期记忆网络
来源:https://i.stack.imgur.com/SjiQE.png
虽然这不是什么新鲜事,但我可能会遇到一些新人。因此,我将在这里给出一个关于 LSTMs 的基本概念。因此,LSTMs 只不过是一种特殊的神经网络,具有三个门,分别是输入门、遗忘门和输出门。LSTM 细胞中最重要的东西是它的记忆——如上图所示。
遗忘门:一个 LSTM 细胞会输出隐藏状态和本细胞的记忆,反馈给它旁边的细胞。一个遗忘门决定了从前一个存储器传入当前单元的信息量。它基本上采用以前的输出和当前的输入,并输出一个遗忘概率。
输入门:它接受与遗忘门相同的输入,并输出一个概率,该概率决定了有助于记忆的单元状态的信息量。
输出门:它再次接受与上述相同的输入,并给出一个概率,该概率预测决定要传递给下一个单元的信息量的概率。
LSTMs 非常擅长处理顺序输入,如前面讨论的文本、时间序列数据集或笔画数据集,并为样本数据序列提供健壮的特征向量。Alex Graves 因此建议将它们用于手写笔画,以找到笔画序列的特征向量,然后将该特征向量馈送到 MDN 网络,以找到统计参数,如下图所示:
我希望我们现在可以进入实施阶段了。我将使用 Keras 来创建网络,并在其后端使用 Tensorflow 来定义损失函数。
假设 x 和 y 点偏移遵循二元正态分布的混合,笔抬起遵循伯努利分布(很明显,对吗?).我将使用两个二元正态分布的混合来演示解决方案,伯努利分布可以用一个概率值来表示,对吗?
哦!我忘了告诉你损失函数。它由下面给出的等式定义:
其中,π是混合物成分的概率,e 是确定伯努利分布参数的冲程终点的概率。
现在让我们完成我们被雇来的任务😮….🤫….😃代码代码代码:
输入将是 3 个长度向量的序列,每个点的输出是笔画序列中的下一个点,如下图所示:
车型(LSTM + MDN)
import numpy **as** np
import numpy
import tensorflow **as** tf
import tensorflow.keras **as** keras
import tensorflow.keras.backend **as** K
import keras.layers.Input **as** Input
import keras.layers.LSTM **as** LSTM
from tensorflow.keras.models import Model
*# nout = 1 eos + 2 mixture weights + 2*2 means \
# + 2*2 variances + 2 correlations for bivariates*
**def** **build_model**(ninp**=**3, nmix**=**2):
inp **=** Input(shape**=**(None, ninp), dtype**=**'float32')
l,h,c **=** LSTM(400, return_sequences**=**True, \
return_state**=**True)(inp)
l1 ,_,_**=** LSTM(400, return_sequences**=**True, \
return_state**=**True)(l, initial_state**=**[h,c])
output **=** keras**.**layers**.**Dense(nmix*****6 **+** 1)(l1)
model **=** Model(inp,output)
**return** model
损失函数
**def** **seqloss**():
**def** **pdf**(x1, x2, mu1, mu2, s1, s2,rho):
norm1 **=** tf**.**subtract(x1, mu1)
norm2 **=** tf**.**subtract(x2, mu2)
s1s2 **=** tf**.**multiply(s1, s2)
z **=** tf**.**square(tf**.**div(norm1, s1)) **+** \
tf**.**square(tf**.**div(norm2, s2)) **-** \
2 ***** tf**.**div(tf**.**multiply(rho, tf**.**multiply(norm1, norm2)), s1s2)
negRho **=** 1 **-** tf**.**square(rho)
result **=** tf**.**exp(tf**.**div(**-**z, 2 ***** negRho))
denom **=** 2 ***** np**.**pi ***** tf**.**multiply(s1s2, tf**.**sqrt(negRho))
result **=** tf**.**div(result, denom)
**return** result
**def** **loss**(y_true, pred):
prob **=** K**.**sigmoid(pred[0][:,0]); pi **=** K**.**softmax(pred[0][:,1:3])
x **=** y_true[0][:,1]; y **=** y_true[0][:,2]; penlifts **=** y_true[0][:,0]
m11 **=** K**.**sigmoid(pred[0][:,3]); m12 **=** K**.**sigmoid(pred[0][:,4])
s11**=** K**.**exp(pred[0][:,5]); s12 **=** K**.**exp(pred[0][:,6])
rho1 **=** K**.**tanh(pred[0][:,7])
pdf1 **=** tf**.**maximum(tf**.**multiply(pdf(x, y, m11, m12, s11, s12, rho1),pi[:,0]), K**.**epsilon())
**for** i **in** range(1,2):
m11 **=** K**.**sigmoid(pred[0][:,3**+**5*****i]); m12 **=** K**.**sigmoid(pred[0][:,4**+**5*****i])
s11 **=** K**.**exp(pred[0][:,5**+**5*****i]); s12 **=** K**.**exp(pred[0][:,6**+**5*****i])
rho1 **=** K**.**tanh(pred[0][:,7**+**5*****i])
pdf1 **+=** tf**.**maximum(tf**.**multiply(pdf(x, y, m11, m12, s11, s12, rho1),pi[:,i]), K**.**epsilon())
loss1 **=** tf**.**math**.**reduce_sum(**-**tf**.**log(pdf1))
pos **=** tf**.**multiply(prob, penlifts)
neg **=** tf**.**multiply(1**-**prob, 1**-**penlifts)
loss2 **=** tf**.**math**.**reduce_mean(**-**tf**.**log(pos**+**neg))
final_loss **=** loss1**+**loss2
**return** final_loss
**return** loss
我在一个小的笔画数据集上随机训练了 2 个时期的模型,因为笔画的长度是变化的。我得到的结果如下所示:
我认为,如果你增加混合、数据集和时期的数量,你会得到更好的结果。我希望我的博客是一个愉快的阅读,如果你有任何疑问或建议,请联系我。
参考
- 【https://arxiv.org/abs/1308.0850
- https://cedar . buffalo . edu/~ Sri Hari/CSE 574/chap 5/chap 5.6-mixdensitynetworks . pdf
- https://github.com/Grzego/handwriting-generation
生成图像分割蒙版——简单的方法
…不到 5 分钟
米歇尔·胡贝尔在 Unsplash 上拍摄的图片。作者编辑
如果你正在读这篇文章,那么你可能知道你在寻找什么🤷。所以我就开门见山了,假设你熟悉图像分割是什么意思,语义分割和实例分割的区别,以及 U-Net、Mask R-CNN 等不同的分割模型。如果没有,我强烈推荐阅读这篇关于分析的优秀文章 Vidhya,全面介绍这个主题,最后用一个例子完成使用 Mask R-CNN。
大多数在线图像分割教程使用预处理和标记的数据集,并生成地面真实图像和掩膜。在实际项目中,当您想要处理类似的任务时,几乎不会出现这种情况。我也面临过同样的问题,在一个实例分割项目中,我花了无数个小时试图找到一个足够简单和完整的例子。我不能,因此决定写自己的:)
下面是我们将在本文中做的事情的一个简单的可视化:)
作者图片
VGG 图像注释器(VIA)
VIA 是一个非常轻量级的注释器,支持图像和视频。可以通过项目首页了解更多。使用 VIA 时,您有两种选择:V2 或 V3。我会试着解释下面的区别:
- V2 要老得多,但足以完成基本任务,而且界面简单
- 与 V2 不同,V3 支持视频和音频注释器
- 如果您的目标是使用 JSON 和 CSV 等多种导出选项进行图像分割,那么 V2 是更好的选择
- V2 项目与 V3 项目不兼容
在这篇文章中,我将使用 V2。你可以在这里下载需要的文件。或者,如果您想通过在线试用,您可以在这里进行。
操作方法
我将使用 ka gglenucleus 数据集并注释其中一幅测试图像,以生成分割蒙版。充分披露,我是而不是认证的医学专家,我做的注释只是为了这篇文章。您也可以快速调整该过程以适应其他类型的对象。
根文件夹树如下所示。via.html
是我们将用来注释图像的文件。它位于上面提供的 VIA V2 ZIP 下载链接中。将所有要添加注释的图片放在images
文件夹中。maskGen.py
是一个将注释转换成遮罩的脚本。
├── images
│ └── test_image.png
├── maskGen.py
└── via.html
图一。作者图片
1.打开 via.html
:会在你默认的浏览器中打开。在区域形状下,选择折线工具(最后一个选项)并给你的项目命名。然后点击添加文件并选择您想要添加注释的所有图像。此时,您的屏幕应该如图 1 所示。
2.开始标注:点击一个对象的边框,在对象周围画一个多边形。你可以通过按回车来完成多边形,或者如果你犯了一个错误,按退格键。对所有对象重复此操作。完成后,您的屏幕应该如图 2 所示。
图二。作者图片
3.导出注释:完成后,点击顶部的注释选项卡,选择导出注释(作为 JSON)。一个 JSON 文件将被保存到您的设备中。按照上面给出的树,找到这个文件并将其转移到根文件夹。
4.生成遮罩:现在,你的根文件夹应该看起来像这样。
├── images
│ └── test_image.png
├── maskGen_json.json
├── maskGen.py
└── via.html
maskGen.py
给出了下面的大意。它读取 JSON 文件,记住每个遮罩对象的多边形坐标,生成遮罩,并将其保存在中。png 格式。对于images
文件夹中的每个图像,脚本会以该图像的名称创建一个新文件夹,该文件夹包含原始图像和生成的遮罩文件的子文件夹。确保将 json_path 变量更新为您的 json 文件名,并设置遮罩高度和宽度。几个生成的掩码显示在要点之后。
如果你做的一切都是正确的,你的根文件夹树应该看起来像这样。每个mask
文件夹中的文件数量对应于你在地面真实图像中标注的对象数量。
├── images
│ └── test_image
│ ├── images
│ │ └── test_image.png
│ └── masks
│ ├── test_image_10.png
│ ├── test_image_11.png
│ ├── test_image_12.png
│ ├── test_image_13.png
│ ├── test_image_14.png
│ ├── test_image_15.png
│ ├── test_image_1.png
│ ├── test_image_2.png
│ ├── test_image_3.png
│ ├── test_image_4.png
│ ├── test_image_5.png
│ ├── test_image_6.png
│ ├── test_image_7.png
│ ├── test_image_8.png
│ └── test_image_9.png
├── maskGen_json.json
├── maskGen.py
└── via.html
一些最终的分割蒙版。作者图片
结论
我希望这篇文章对你的项目有所帮助。请联系我们寻求任何建议/澄清:)
你可以通过以下方式联系我:邮箱、 LinkedIn 、 GitHub
谷歌语音识别 API 的最佳开源替代方案——现在是第二大英语国家……
towardsdatascience.com](/automatic-speech-recognition-for-the-indian-accent-91bb011ad169) [## 使用 Mozilla DeepSpeech 自动生成字幕
对于那些嘴里薯条的噪音让你无法看电影的时候:)
towardsdatascience.com](/generating-subtitles-automatically-using-mozilla-deepspeech-562c633936a7)
用可变自动编码器生成新的人脸
一个关于使用可变自动编码器生成新面的综合教程。
照片由 Vidushi Rajput 在 Unsplash 拍摄
介绍
深度生成模型在行业和学术研究中都获得了极大的欢迎。计算机程序生成新的人类面孔或新的动物的想法是非常令人兴奋的。与我们很快将讨论的监督学习相比,深度生成模型采用了一种稍微不同的方法。
本教程涵盖了使用可变自动编码器的生成式深度学习的基础知识。我假设你相当熟悉卷积神经网络和表示学习的概念。如果没有,我会推荐观看安德烈·卡帕西的 CS231n 演讲视频,因为在我看来,它们是在互联网上学习 CNN 的最佳资源。你也可以在这里找到课程的课堂讲稿。
这个例子演示了使用 Keras 生成新脸来构建和训练 VAE 的过程。我们将使用来自 Kaggle 的名人面孔属性(CelebA)数据集和 Google Colab 来训练 VAE 模型。
生成模型
如果你开始探索生成性深度学习领域,一个变化的自动编码器(VAE)是开启你旅程的理想选择。VAE 的建筑很直观,也很容易理解。与诸如 CNN 分类器的判别模型相反,生成模型试图学习数据的基本分布,而不是将数据分类到许多类别中的一个。一个训练有素的 CNN 分类器在区分汽车和房子的图像时会非常准确。然而,这并没有实现我们生成汽车和房屋图像的目标。
判别模型学习从数据中捕捉有用的信息,并利用该信息将新数据点分类为两个或更多类别中的一个。从概率的角度来看,判别模型估计概率𝑝(𝑦|𝑥,其中𝑦是类别或级别,𝑥是数据点。它估计数据点𝑥属于𝑦.类别的概率例如,图像是汽车或房子的概率。
生成模型学习解释数据如何生成的数据的底层分布。本质上,它模拟了底层分布,并允许我们从中取样以生成新数据。它可以被定义为估计概率𝑝(𝑥),其中𝑥是数据点。它估计在分布中观察到数据点𝑥的概率。
简单自动编码器
在深入研究变型自动编码器之前,分析一个简单的自动编码器是至关重要的。
一个简单或普通的自动编码器由两个神经网络组成——一个编码器和一个解码器。编码器负责将图像转换成紧凑的低维向量(或潜在向量)。这个潜在向量是图像的压缩表示。因此,编码器将输入从高维输入空间映射到低维潜在空间。这类似于 CNN 分类器。在 CNN 分类器中,这个潜在向量将随后被送入 softmax 层,以计算各个类别的概率。然而,在自动编码器中,这个潜在向量被送入解码器。解码器是一个不同的神经网络,它试图重建图像,从而从较低维度的潜在空间映射到较高维度的输出空间。编码器和解码器执行完全相反的映射,如图 img-1 所示。
img-1(资料来源:en.wikipedia.org/wiki/Autoencoder)
考虑下面的类比来更好地理解这一点。想象你正在和你的朋友通过电话玩游戏。游戏规则很简单。你会看到许多不同的圆柱体。你的任务是向你的朋友描述这些圆柱体,然后他将尝试用粘土模型重新制作它们。禁止您发送图片。你将如何传达这一信息?
因为任何圆柱体都可以用两个参数来构造——高度和直径,所以最有效的策略是估计这两个尺寸,并把它们传达给你的朋友。你的朋友收到这个信息后,就可以重建圆筒。在这个例子中,很明显,你通过将视觉信息压缩成两个量来执行编码器的功能。相反,你的朋友正在执行一个解码器的功能,利用这个浓缩的信息来重建圆筒。
家政
下载数据集
数据集可以使用如下所示的 Kaggle API 直接下载到您的 Google Colab 环境中。更多细节可以参考这个在 Medium 上的帖子。
上传从您注册的 Kaggle 帐户下载的 Kaggle.json。
!pip install -U -q kaggle
!mkdir -p ~/.kaggle
从 Kaggle 下载数据集。
!cp kaggle.json ~/.kaggle/
!kaggle datasets download -d jessicali9530/celeba-dataset
定义项目结构。
注意(针对 Colab 用户) :不要试图使用左边的查看器浏览目录,因为数据集太大,页面会变得没有响应。
进口
数据
由于数据集非常大,我们将创建一个 ImageDataGenerator 对象,并使用其成员函数 flow_from_directory 来定义直接来自磁盘的数据流,而不是将整个数据集加载到内存中。ImageDataGenerator 还可用于动态应用各种图像增强转换,这在小数据集的情况下特别有用。
我强烈建议你参考文档来理解数据流函数的各种参数。
模型架构
构建编码器
如下所示,编码器的架构由一堆卷积层组成,后面是一个密集(全连接)层,输出大小为 200 的矢量。
注意:padding = 'same '和 stride = 2 的组合将产生一个在高度和宽度上都是输入张量一半大小的输出张量。深度/通道不受影响,因为它们在数字上等于过滤器的数量。
构建解码器
回想一下,解码器的功能是从潜在向量中重建图像。因此,有必要定义解码器,以便通过网络逐渐增加激活的大小。这可以通过上采样 2D 层或 Conv2DTransponse 层来实现。
这里采用了 Conv2DTranspose 层。这一层产生的输出张量在高度和宽度上都是输入张量的两倍。
注意:在这个例子中,解码器被定义为编码器的镜像,这不是强制性的。
将解码器连接到编码器
汇编和培训
使用的损失函数是简单的均方根误差(RMSE)。真正的输出是在输入层提供给模型的同一批图像。Adam 优化器正在优化 RMSE 误差,用于将一批图像编码到它们各自的潜在向量中,并随后解码它们以重建图像。
ModelCheckpoint Keras 回调保存模型权重以供重用。它在每个时期后用一组新的权重覆盖文件。
注意:如果你正在使用 Google Colab,要么下载重量到光盘,要么安装你的 Google Drive。
提示:这是我在 Reddit(arvind 1096)上找到的一个非常有用的提示——为了防止 Google Colab 由于超时问题而断开连接,请在 Google Chrome 控制台中执行以下 JS 函数。
function ClickConnect(){console.log(“Working”);document.querySelector(“colab-toolbar-button#connect”).click()}setInterval(ClickConnect,60000)
重建
第一步是使用顶部“数据”部分中定义的 ImageDataGenerator 生成一批新图像。图像以数组的形式返回,图像的数量等于 BATCH_SIZE 。
第一行显示直接来自数据集的图像,第二行显示已经通过自动编码器的图像。显然,该模型已经很好地学会了编码和解码(重建)。
注意:图像缺乏清晰度的一个原因是 RMSE 损失,因为它平均了单个像素值之间的差异。
缺点
将从标准正态分布采样的噪声向量添加到图像编码
可以观察到,随着编码中加入一点噪声,图像开始失真。一个可能的原因是模型没有确保编码值周围的空间(潜在空间)是连续的。
尝试从从标准正态分布采样的潜在向量生成图像
显然,从标准正态分布采样的潜在向量不能用于生成新的人脸。这表明由模型生成的潜在向量不是以原点为中心/对称的。这也加强了我们的推论,即潜空间不是连续的。
因为我们没有一个明确的分布来采样潜在向量,所以我们不清楚如何生成新的面孔。我们观察到在潜在向量中加入一点杂讯并不会产生新的面孔。我们可以编码和解码图像,但这并不符合我们的目标。
基于这种想法,如果我们可以从标准正态分布的潜在向量中生成新的面孔,那不是很好吗?这基本上就是变分自动编码器能够做到的。
变分自动编码器
变型自动编码器解决了上面讨论的大多数问题。他们被训练来从从标准正态分布采样的潜在向量中生成新的面孔。简单的自动编码器学习将每个图像映射到潜在空间中的固定点,而变分自动编码器(VAE)的编码器将每个图像映射到 z 维标准正态分布。
如何修改简单的自动编码器,使编码器映射到 z 维标准正态分布?
任何 z 维正态分布都可以用平均矢量𝜇和协方差矩阵σ来表示。
因此,在 VAE 的情况下,编码器应该输出均值向量( 𝜇 )和协方差矩阵(σ)来映射到正态分布,对吧?
是的,但是,需要对协方差矩阵σ进行细微的修改。
修改 1) 假设潜在向量的元素之间没有相关性。因此,代表协方差的非对角线元素都是零。因此,协方差矩阵是一个对角矩阵。
因为这个矩阵的对角元素代表方差,所以它被简单地表示为𝜎2,一个 z 维方差向量。
修改 2) 回想一下方差只能取非负值。为了保证编码器的输出无界,编码器实际上映射到均值向量和方差向量的对数。对数确保输出现在可以取范围内的任何值,(∞,∞)。这使得训练更容易,因为神经网络的输出自然是无界的。
现在,如何确保编码器映射到标准正态分布(即平均值为 0,标准偏差为 1)?
输入 KL 散度。
KL 散度提供了一个概率分布与另一个概率分布不同程度的度量。具有平均𝜇和标准偏差𝜎的分布与标准正态分布之间的 KL 散度采用以下形式:
通过稍微修改损失函数以包括除 RMSE 损失之外的 KL 发散损失,VAE 被迫确保编码非常类似于多元标准正态分布。因为多元标准正态分布的平均值为零,所以它以原点为中心。将每个图像映射到与固定点相对的标准正态分布确保了潜在空间是连续的,并且潜在向量以原点为中心。
看一下图像 img-2 以便更好地理解。
img-2(来源:blog . Bayes labs . co/2019/06/04/All-you-need-to-know-about-Vae)
如果编码器映射到𝜇和𝜎,而不是 z 维的潜在向量,那么在训练期间给解码器的输入是什么?
解码器的输入,如 img-2 所示,是从编码器输出——𝜇和𝜎.——表示的正态分布中采样的向量这种采样可以按如下方式进行:
𝑍=𝜇+𝜎𝜀
其中𝜀是从多元标准正态分布中取样的。
我们如何生成新面孔?
由于 KL 散度确保编码器映射尽可能接近标准正态分布,因此我们可以从 z 维标准正态分布进行采样,并将其提供给解码器以生成新图像。
解码器需要修改吗?
不,解码器保持不变。它等同于简单的自动编码器。
VAE 建筑模型
编码器架构有微小的变化。它现在有两个输出:mu(均值)和 log_var(方差的对数),如下所示。
构建解码器
由于解码器保持不变,简单自动编码器的解码器架构被重用。
将解码器连接到编码器
汇编和培训
损失函数是 RMSE 和 KL 散度之和。RMSE 损耗被赋予一个权重,称为损耗因子。损耗因子乘以 RMSE 损耗。如果我们使用高损耗因子,简单的自动编码器的缺点就开始出现了。然而,如果我们使用的损耗因子太低,重建图像的质量将会很差。因此,损耗因子是一个需要调整的超参数。
重建
重建过程与简单自动编码器的过程相同。
重建结果与简单的自动编码器非常相似。
从标准正态分布采样的随机向量生成新面孔。
VAE 显然有足够的能力从标准正态分布的样本向量中产生新的面孔。神经网络能够从随机噪声中生成新面孔的事实表明,它在执行极其复杂的映射方面是多么强大!
由于不可能可视化 200 维向量,潜在向量的一些元素被单独可视化,以查看它们是否接近标准正态分布。
观察到 Z 维向量的前 30 个元素非常类似于标准正态分布。因此,增加 KL 散度项是合理的。
结论
正如我们所观察到的,变分自动编码器实现了我们创建生成模型的目标。VAE 已经学会模拟数据的基本分布,并在多元标准正态分布和表面数据之间执行非常复杂的映射。换句话说,我们可以从多元标准正态分布中采样一个潜在向量,我们的 VAE 模型将该向量转换成一个新的面孔。
本教程的笔记本可以从我的GitHub 资源库 访问。
为了进一步阅读,我推荐卡尔·多施的关于变分自动编码器 的 教程。
感谢阅读!
在没有数据集的情况下生成新内容
重写 GAN 中的规则:上下文相关的复制和粘贴特性
编辑样式为马添加头盔【来源】
介绍
GAN 架构一直是通过人工智能生成内容的标准,但它真的能在训练数据集中可用的内容之外发明新的内容吗?还是只是模仿训练数据,用新的方式混合特征?
在本文中,我将讨论“重写深度生成模型”论文,该论文支持直接编辑 GAN 模型以给出我们想要的输出,即使它与现有数据集不匹配。上面的图像是一个编辑的例子,其中您复制头盔特征并将其粘贴到马的上下文中。我相信这种可能性将在数字行业中开辟许多新的有趣的应用程序,例如为动画或游戏生成虚拟内容,这些内容可能不存在现有的数据集。您可以通过下面的链接观看演示视频。
[## 重写深度生成模型(ECCV 2020)——cross minds . ai
重写深度生成网络,发表于 ECCV 2020(口头)。在本文中,我们展示了如何深生成…
crossminds.ai](https://crossminds.ai/video/5f3b29e596cfcc9d075e35f0/?utm_campaign=5edc3d3a07df2164&utm_medium=share)
【甘】
生成对抗网络(GAN)是一种生成模型,这意味着它可以生成与训练数据相似的真实输出。例如,在人脸上训练的 GAN 将能够生成看起来相似的真实人脸。GAN 能够通过学习训练数据的分布来做到这一点,并生成遵循相同分布的新内容。
甘建筑[图片由作者提供]
甘通过一个试图区分真实和虚假图像的鉴别器和一个创建虚假数据来欺骗鉴别器的生成器来“间接”学习分布。这两个网络将不断竞争和相互学习,直到它们都能够分别生成和区分真实的图像。
GAN 限制
尽管 GAN 能够学习一般的数据分布并生成数据集的不同图像。它仍然局限于训练数据中存在的内容。举个例子,我们来看一个在人脸上训练的 GAN 模型。虽然它可以生成数据集中不存在的新面孔,但它不能发明一个具有新颖特征的全新面孔。你只能期望它以新的方式结合模型已经知道的东西。
因此,如果我们只想生成正常的脸,这是没有问题的。但是如果我们想要长着浓密眉毛或者第三只眼睛的脸呢?GAN 模型无法生成这一点,因为在训练数据中没有眉毛浓密或第三只眼睛的样本。快速的解决方法是用照片编辑工具简单地编辑生成的人脸,但是如果我们想要生成大量这样的图像,这是不可行的。因此,GAN 模型更适合这个问题,但是在没有现有数据集的情况下,我们如何让 GAN 生成我们想要的图像呢?
改写甘规则
2020 年 1 月,MIT 和 Adobe Research 发表了一篇名为“重写深度生成模型”的有趣论文,使我们能够直接编辑 GAN 模型,生成新颖的内容。模型重写是什么意思?我们没有让模型根据训练数据或标签来优化自己,而是直接设置我们希望保留的规则(参数),以获得所需的结果。想要马背上的头盔吗?没问题。我们可以复制头盔的特征,并将其放在马头特征上。然而,这需要了解内部参数及其如何影响输出,这在过去是一个相当大的挑战。虽然,论文已经证明是可行的。
通过重写模型,根据上下文复制和粘贴特征[图片由作者提供]
训练和重写之间的区别类似于自然选择和基因工程之间的区别。虽然培训可以有效地优化整体目标,但它不允许直接指定内部机制。相比之下,重写允许一个人直接选择他们希望包含的内部规则,即使这些选择碰巧不匹配现有的数据集或优化全局目标。
-大卫·鲍尔(论文主要作者)
正如大卫·鲍尔所说,重写一个模型就像基因工程。这就像把发光水母的 DNA 植入猫体内,制造出一只在黑暗中发光的猫。
发光的猫【图片由阿纳希德·巴拉瓦尼亚】
它是如何工作的
你实际上如何重写一个生成模型?本文提出了将生成器的权值视为最优线性联想记忆(OLAM)的思想。OLAM 的作用是存储一个键值对关联。我们将选择某一层 L,它表示值 V ,该值表示图像的输出特征,例如微笑表情。然后,层 L 之前的前一层将表示键 K ,其表示有意义的上下文,例如嘴的位置。这里,层 L 和层 L-1 之间的权重 W 充当存储 K 和 v 之间的关联的线性关联存储器
权重作为联想记忆[图片由作者提供]
我们可以认为 K🠖V 协会是模型中的一个规则。例如,想象一下,如果我们有一个在马身上训练的 StyleGAN 模型,我们想重写这个模型,给马戴上头盔。我们将我们想要的特征头盔标记为 V* ,将我们想要粘贴特征的上下文马头标记为 K* 。因此,为了得到我们想要的特征,我们想把原来的规则 K 🠖 V 改为我们想要的规则 K* 🠖 V* 。为此,我们以这样的方式更新权重,以将规则更改为目标 K* 🠖 V* 。
模型重写[图片由作者提供]
数学细节
我们如何更新 W 来得到目标 K* 🠖 V* ?我们想要设定新规则 K* 🠖 V* 同时最小化旧 k🠖v.的变化因此,
上面的表达式是一个约束最小二乘问题,可以用
这可以被简化
其中 C = K*KT,λ和 c^−1 k∫都是简单矢量。
因此,我们的更新将有两个分量,幅度λ和更新方向 c^−1 k。我们将更新方向 c^−1 k∫表示为 d 。更新方向 d 仅受密钥 k 影响,且仅λ取决于值 v* 。为了简化,更新方向确保只有影响所选上下文 k的权重将被更新,以最小化与其他规则的干扰,同时λ确保我们实现期望的 **v。关于数学的更多细节,我推荐阅读论文本身。**
总之,获得更新的权重 W1 的步骤是
结果
本研究通过重写预先训练好的 StyleGAN 和 ProGAN 模型来验证其能力。一些演示是给马戴上头盔,把圆顶变成树顶,把皱眉变成微笑,去掉耳环,加上浓密的眉毛,加上眼镜。我推荐观看 David Bau 的演示视频,他展示了如何使用研究制作的接口工具重写模型。
[## 重写深度生成模型(ECCV 2020)——cross minds . ai
重写深度生成网络,发表于 ECCV 2020(口头)。在本文中,我们展示了如何深生成…
crossminds.ai](https://crossminds.ai/video/5f3b29e596cfcc9d075e35f0/?utm_campaign=5edc3d3a07df2164&utm_medium=share)
给马戴帽【来源】
添加浓眉【来源】
把教堂顶改成树顶【来源】
变皱眉为微笑【来源】
取下耳环【来源】
添加眼镜【来源】
该界面工具易于使用,并且可以在 Jupyter 笔记本上运行。您可以使用这个 Colab 笔记本或从源代码中使用该工具。
接口工具重写模型【来源】
感谢您的阅读,如果您喜欢我的文章,请随时查看我的其他文章!
理解变分自动编码器背后的直觉(VAE)
medium.com](https://medium.com/vitrox-publication/generative-modeling-with-variational-auto-encoder-vae-fc449be9890e) [## 使用 StyleGAN2 生成动画角色
了解如何生成这个很酷的动画人脸插值
towardsdatascience.com](/generating-anime-characters-with-stylegan2-6f8ae59e237b)
参考
[1] Bau,d .,Liu,s .,Wang,t .,Zhu,J. Y .,& Torralba,A. (2020)。重写深度生成模型。arXiv 预印本 arXiv:2007.15646 。
https://rewriting.csail.mit.edu/
在 Python 中生成帕累托分布
概率统计/帕累托分布
让我们更好地理解帕累托分布
照片由 iambipin 拍摄
1.帕累托分布
P areto 分布是一种幂律概率分布,以意大利土木工程师、经济学家、社会学家维尔弗雷多·帕累托的名字命名,用于描述社会、科学、地球物理、精算以及其他各种类型的可观测现象。帕累托分布有时被称为帕累托原则或“80-20”规则,因为该规则表明,80%的社会财富掌握在 20%的人口手中。帕累托分布不是自然规律,而是一种观察。它在许多现实世界的问题中是有用的。这是一个偏斜的重尾分布。
看完定义,你一定在疑惑什么是幂律?幂律是两个量之间的函数关系,使得一个量的变化触发另一个量的成比例变化,而不管两个量的初始大小。
照片由 iambipin 拍摄
80-20 法则在很多情况下都适用。例如,维尔弗雷多·帕累托发现意大利 80%的土地被 20%的人口占有。他还发现,从他的菜园里采购的 80%的豌豆来自 20%的豌豆植株。全球 82.7%的收入被 20%的人口控制。微软 2002 年的一份报告表明,Windows 和 MS Office 80%的错误和崩溃是由 20%的检测到的错误造成的。80%的销售额来自 20%的产品。80%的客户只使用 20%的软件功能。这种 80-20 分布经常出现。
2.在 Python 中生成帕累托分布
可以使用 Scipy.stats 模块或 NumPy 在 Python 中复制 Pareto 分布。Scipy.stats 模块包含各种概率分布和不断增长的统计函数库。Scipy 是用于科学计算和技术计算的 Python 库。NumPy 是一个用于科学计算的 Python 库,除了科学用途之外,它还可以用作通用数据的多维容器。
2.1 使用 Scipy.stats
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import pareto
x_m = 1 #scale
alpha = [1, 2, 3] #list of values of shape parameters
samples = np.linspace(start=0, stop=5, num=1000)for a in alpha:
output = np.array([pareto.pdf(x=samples, b=a, loc=0, scale=x_m)])
plt.plot(samples, output.T, label='alpha {0}' .format(a))plt.xlabel('samples', fontsize=15)
plt.ylabel('PDF', fontsize=15)
plt.title('Probability Density function', fontsize=15)
plt.grid(b=True, color='grey', alpha=0.3, linestyle='-.', linewidth=2)
plt.rcParams["figure.figsize"] = [5, 5]
plt.legend(loc='best')
plt.show()
照片由 iambipin 拍摄
x_m 和α是帕累托分布的两个参数。x_m 是标度参数,代表帕累托分布随机变量可以取的最小值。α是形状参数,等于 n/SUM{ln(x_i/x_m)}。
np.linspace() 返回特定间隔[开始,停止]内间隔均匀的样本(样本数等于 num)。在上面的代码中,linspace()方法返回范围[0,5]内 1000 个均匀分布的样本。
shape values -alpha 列表被迭代以绘制每个值的线条。 np.array() 创建一个数组。 scipy.stats.pareto() 方法返回 pareto 连续随机变量。 pareto.pdf() 创建概率密度函数(pdf)。参数 x、b、loc 和 scale 分别是类数组分位数、类数组形状参数、类数组可选位置参数(默认值=0)和类数组可选比例参数(默认值=1)。
plt.plot() 绘制均匀间隔的样本和 PDF 值数组。该图是为每个α值绘制的。这里,输出。T 正在变换输出。输出是具有 3 行的 Pareto 分布值的数组,每个形状参数一行。在转置时,输出被转换成 1000 行的数组。
剩下的代码行几乎是不言自明的。 plt.xlabel() 和 plt.ylabel() 用于标注 x 轴和 y 轴。 plt.title() 给图形指定标题。 plt.grid() 配置网格线。
plt.rcParams['figure.figsize'] = [width, height]
plt.rcParams[] 设置当前 rc 参数。Matplotlib 使用 matplotlibrc 配置文件自定义各种属性,称为‘RC 设置’或‘RC 参数’。Matplotlib 中几乎每个属性的默认值都可以控制:图形大小和 DPI、线条宽度、颜色和样式、轴、轴和网格属性、文本和字体属性等等。
plt.legend() 显示图例,plt.show()显示所有图形。想进一步了解如何让你的情节更有意义,请访问这里。
2.2 使用 Numpy
import numpy as np
import matplotlib.pyplot as plt
x_m, alpha = 1, 3.
#drawing samples from distribution
samples = (np.random.pareto(alpha, 1000) + 1) * x_m
count, bins, _ = plt.hist(samples, 100, normed=True)
fit = alpha*x_m**alpha / bins**(alpha+1)
plt.plot(bins, max(count)*fit/max(fit), linewidth=2, color='r')
plt.xlabel('bins', fontsize=15)
plt.ylabel('probability density', fontsize=15)
plt.title('Probability Density Function', fontsize=15)
plt.grid(b=True, color='grey', alpha=0.3, linestyle='-.', linewidth=2)
plt.rcParams['figure.figsize'] = [8, 8]
plt.show()
照片由 iambipin 拍摄
np.random.pareto() 从指定形状的 Pareto II 或 Lomax 分布中抽取随机样本。帕累托 II 分布是一种转移的帕累托分布。通过加 1 并乘以标度参数 x_m,可以从 Lomax 分布得到经典的 Pareto 分布。
samples = (np.random.pareto(alpha, 1000) + 1) * x_m
帕累托 II 分布的最小值是零,而经典帕累托分布的最小值是μ,其中标准帕累托分布的位置μ= 1。
plt.hist() 绘制直方图。当参数 density 或 normed 设置为 True 时,返回的元组将把第一个元素作为计数规范化以形成概率密度。因此直方图下的面积将是 1。这是通过将计数除以观察数量乘以箱宽度而不是除以观察总数来实现的。因此,y 轴将具有样本密度。
count,bin,_ 中的 '_' 表示返回的元组的最后一个值不重要( plt.hist() 将返回一个具有三个值的元组)。
我们将在分箱的数据上绘制曲线,
我们将通过计算 Pareto 分布在由具有参数 x_m 和 alpha 的条柱定义的 x 值处的概率密度,使 Pareto 分布符合我们的随机采样数据,并在我们的数据之上绘制该分布。
fit = alpha*x_m**alpha / bins**(alpha+1)
3.验证帕累托分布
Q-Q 图(分位数-分位数图)用于判断连续随机变量是否遵循帕累托分布。
import numpy as np
import matplotlib.pyplot as plt
import scipy.stats as statsx_m = 10
alpha = 15
size = 100000 #the size of the sample(no. of random samples)
samples = (np.random.pareto(alpha, size) + 1) * x_m
stats.probplot(samples, dist='pareto', sparams=(15, 10), plot=pylab)
plt.show()
照片由 iambipin 拍摄
stats . proplot根据指定理论分布(帕累托分布)的分位数,生成从分布(样本数据)中抽取的随机样本的概率图。由于绝大多数蓝点(样本数据)几乎与红线(理论分布)对齐,我们可以得出分布遵循帕累托分布的结论。
在得出结论之前,必须了解帕累托分布在现实世界中的应用。
4.帕累托分布的应用
- 人类住区的规模(更少的城市和更多的村庄)。
- 沙粒的大小。
- 遵循 TCP 协议通过互联网分发的数据的文件大小。
- 油田(几个大油田和许多小油田)的石油储量值
- 在 Tinder 中,80%的女性争夺 20%最有魅力的男性。
- 用户在 Steam 中玩各种游戏所花费的时间(很少有游戏玩得很多,比大多数很少玩的游戏更频繁)。
帕累托分布及其概念非常简单而强大。它总是有助于收集重要的线索,以了解广泛的人类行为,科学和社会现象。我希望你能更好地理解帕累托分布,以及如何从中抽取样本并使用 Pyplot、Numpy、Scipy 和 Python 绘图。编码快乐!!!
参考
[## scipy.stats.pareto — SciPy v1.4.1 参考指南
scipy.stats. pareto( *args,**kwds) = [source]一个 pareto 连续随机变量。作为 rv_continuous 的一个实例…
docs.scipy.org](https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.pareto.html) [## NumPy . random . Pareto—NumPy 1.10 版手册
numpy.random. pareto ( a,size=None)从指定形状的 Pareto II 或 Lomax 分布中抽取样本。的…
docs.scipy.org](https://docs.scipy.org/doc/numpy-1.10.1/reference/generated/numpy.random.pareto.html)
https://en.wikipedia.org/wiki/Pareto_distribution
https://docs . scipy . org/doc/numpy-1 . 10 . 1/reference/generated/numpy . random . Pareto . html
用扩张卷积神经网络生成钢琴音乐
如何建立完全卷积的神经网络,能够以惊人的成功模拟复杂的钢琴音乐结构
介绍
由扩展 1D 卷积组成的完全卷积神经网络易于构建、易于训练,并且可以生成逼真的钢琴音乐,例如:
通过 100 小时古典音乐训练的完全卷积网络产生的示例性能。
动机
大量的研究致力于训练可以创作钢琴音乐的深度神经网络。例如,OpenAI 开发的 Musenet 已经训练出能够创作长度为许多分钟的逼真钢琴曲的大规模变形金刚模型。Musenet 使用的模型采用了许多技术,比如最初为 NLP 任务开发的注意力层。参见上一篇 TDS 文章,了解更多关于将注意力模型应用于音乐生成的细节。
虽然基于 NLP 的方法非常适合基于机器的音乐生成(毕竟,音乐就像一种语言),但 transformer 模型架构有些复杂,并且适当的数据准备和训练可能需要极大的关注和经验。这种陡峭的学习曲线促使我探索更简单的方法来训练可以创作钢琴音乐的深度神经网络。特别是,我将重点关注基于扩展卷积的全卷积神经网络,它只需要几行代码来定义,需要最少的数据准备,并且易于训练。
历史关联
2016 年, DeepMind 研究人员推出了 WaveNet 模型架构,在语音合成方面产生了最先进的性能。他们的研究表明,具有指数增长膨胀率的堆叠 1D 卷积层可以极其有效地处理原始音频波形序列,从而产生可以合成来自各种来源的令人信服的音频的生成模型,包括钢琴音乐。
在这篇文章中,我以 DeepMind 的研究为基础,明确专注于创作钢琴音乐。我没有从录制的音乐中向模型输入原始音频,而是显式地向模型输入在乐器数字接口(MIDI)文件中编码的钢琴音符序列。这有助于数据收集,大大减少计算量,并允许模型完全集中于数据的音乐方面。这种高效的数据编码和数据收集的便利性使得快速探索完全卷积网络能够多好地理解钢琴音乐成为可能。
这些模特“弹钢琴”的水平如何?
为了让大家了解这些模型听起来有多逼真,我们来玩一个模仿游戏。以下哪一段是由人类创作的,哪一段是由模特创作的:
钢琴作曲 A:人还是模特?
钢琴作曲 B:人还是模特?
也许你预料到了这个诡计,但是这两个作品都是由这篇文章中描述的模型产生的。生成上述两个片段的模型仅用了四天时间在一台 NVIDIA Tesla T4 上进行训练,训练集中有 100 小时的古典钢琴音乐。
我希望这两个表演的质量为你提供动力,继续阅读并探索如何构建你自己的钢琴音乐创作模型。这个项目中描述的代码可以在 piano et 的 Github 中找到,更多示例作品可以在 piano et 的 SoundCloud 中找到。
现在,让我们深入了解如何训练一个模型像上面的例子一样产生钢琴曲的细节。
方法
当开始任何机器学习项目时,很好的做法是明确定义我们试图完成的任务,我们的模型将从中学习的体验,以及我们将用来确定我们的模型是否在任务中改进的性能度量。
工作
我们的首要目标是生成一个模型,有效地逼近数据生成分布 P(X)。这个分布是一个函数,它将钢琴音符的任意序列 X 映射到一个范围在 0 和 1 之间的实数。本质上,P(X)赋予更可能由熟练的人类作曲家创作的序列更大的值。举个例子,如果 X 是由 200 个小时随机选取的音符组成的曲子,X 是莫扎特的奏鸣曲,那么 P(X ) < P(X)。此外,P(X)将非常接近零。
实际上,分布 P(X)永远无法精确确定,因为这需要将所有可能存在的人类作曲家聚集在一个房间里,让他们永远创作钢琴曲。然而,对于更简单的数据生成分布,也存在同样的不完整性。例如,精确确定人类身高的分布需要所有可能的人类存在并被测量,但这并不阻止我们定义和近似这样的分布。在这个意义上,上面定义的 P(X)是一个有用的数学抽象,它包含了决定钢琴音乐如何产生的所有可能的因素。
如果我们用一个模型很好地估计了 P(X ),我们就可以用这个模型随机抽样出新的、现实的、从未听过的作品。这个定义仍然有点抽象,所以让我们应用一些合理的近似,使 P(X)的估计更容易处理。
数据编码:我们需要用计算机可以理解的方式对钢琴音乐进行编码。为此,我们将把一首钢琴作品表示为一个可变长度的二进制状态时间序列,每个状态跟踪在一个时间步长内手指是否按下了键盘上的给定音符:
图 1:数据编码过程,展示了如何将钢琴音乐表示为二进制按键状态的 1D 序列,其中一个状态表示在特定的时间步长内按下了一个按键。
数据处理不等式告诉我们,只有当我们处理信息时,信息才会丢失,我们选择的钢琴音乐编码方法也不例外。在这种情况下,信息丢失有两种方式。首先,必须通过使时间步长有限来限制时间方向上的分辨率。幸运的是,0.02 秒的相当大的步长仍然会导致音乐质量的可忽略不计的降低。第二,我们不能代表按键的速度,因此,音乐的力度就丧失了。
尽管有重要的近似,这种编码方法以简洁和机器友好的形式捕获了大量的底层信息。这是因为钢琴实际上是一个大型的有限状态机。有效地编码其他更微妙的乐器的音乐,如吉他或长笛,可能会困难得多。
既然我们有了编码方案,我们可以用更具体的形式来表示数据生成分布:
等式 1:在 88 键钢琴上演奏的钢琴作品的联合概率分布。有 T 个时间步长,或 88 个按键状态的集合,每个 x 变量可以是 1(按键)或 0。
作为一个例子,在第一时间步,如果钢琴上的第一个音符被按下,在 t =1, x ₁ = 1。
分解分布:另一种简化涉及使用概率链规则分解(1)中的联合概率分布:
等式 2:将编码数据生成分布因式分解成条件概率。
类似于 n -gram 语言模型,我们可以做出马尔可夫假设,即在过去发生超过 N 个时间步的音符对在时间 t=n 的音符是否被按下没有影响。这允许我们最多使用最后一个 N 音符来重写(2)中的每个因子:
等式 3 😦 2)中的每一项可以使用该表达式来近似,该表达式假设只有键状态的最后 N 个时间步长影响下一个键被按下的概率。
请注意,在一首歌曲的开头,音符历史可以用多达 N 个零来填充,以便总是有至少 N 个音符的历史。此外,请注意 N 必须在数百个时间步长(许多秒的历史)内,这种近似才能很好地适用于钢琴音乐。我们将在后面看到 N 是由我们模型的感受野决定的,并且是表演质量的关键决定因素。
最后,我们已经涵盖了足够的数学背景,以严格定义这个项目的任务:使用真实钢琴音乐的编码序列数据集,训练一个估计器 p̂ ,给出最后 N 个音符状态下下一个音符被按下的概率。然后,我们可以使用 p̂ 重复采样下一个音符,在每次采样后更新音符历史,自动回归创建一个全新的乐曲。
在这个项目中,我们的估计器 p̂ 将是一个完全卷积的深度神经网络。但是,在我们谈论模型架构或培训之前,我们需要收集一些数据。
经验
我们的模型在训练期间看到的数据将在很大程度上决定其生成的音乐的质量和风格。此外,我们试图估计的联合概率分布是非常高维的,并且容易出现数据稀疏的问题。这可以通过使用足够大量的训练数据和适当的模型选择来克服。关于前者,钢琴音乐很容易收集和预处理,原因有二。
首先,互联网上有大量 MIDI 格式的钢琴音乐。这些文件本质上是钢琴键状态的序列,并且需要最少的预处理来获得我们期望的训练数据的编码。第二,我们试图完成的任务是自监督学习,其中我们的目标标签可以从数据中自动生成,无需人工标注:
图 2:输入和目标音符序列由相同的数据组成,目标相对于输入向前移动一个音符。该任务的自我监督性质排除了手动贴标的需要。
图 2 显示了如何收集这个任务的训练实例。首先,从一首歌曲中选择一个二元密钥状态序列。然后,这些状态被分成两个子序列;一个输入序列,然后是一个大小相等的目标序列,但索引向前移动一个音符。总而言之,构建我们的数据集就像从互联网上下载高质量的钢琴 midi 文件并操作数组一样简单。
值得注意的是,我们在训练集中包含的音乐风格将在很大程度上决定我们最终估计的数据生成分布 P(X)的子空间。如果我们喂巴赫和莫札特的模式序列,它显然不会学习产生塞隆尼斯·蒙克的爵士乐。出于数据收集的考虑,我将重点放在最著名的古典作曲家的输入数据上,包括巴赫、肖邦、莫扎特和贝多芬。然而,将这项工作扩展到不同风格的钢琴音乐只需要用这些附加风格的代表性例子来扩充数据集。
绩效指标
PianoNet 本质上是一个自我监督的二进制分类器,它被反复调用来预测下一个键状态是向上还是向下。对于任何概率二进制分类器,我们可以通过最小化训练数据上的交叉熵损失来最大化模型对数据集的预测可能性:
等式 4:交叉熵损失,针对具有参数θ的概率模型 p̂,在一组 m 个实例的训练集上计算。如果第 I 个训练实例的实际目标音符被按下,yᵢ = 1,否则为 0。
在训练过程中,我们的模型被输入长序列的音符状态输入和从人类创作的钢琴音乐中采样的目标。在每个训练步骤,该模型使用有限的音符历史来预测下一个钢琴键状态被按下的概率。如果当真键状态被按下时预测概率接近于零,或者当真键状态未被按下时预测概率接近于 1,则模型将根据(4)受到强烈惩罚。
这个损失函数有一个潜在的问题。我们的最终目标不是创建一个基于人类创作的作品中的过去音符来预测单个下一个音符的模型,而是基于模型本身生成的音符历史来创建扩展的音符序列。在这个意义上,我们做了一个非常强有力的假设:当给定一个人为生成的音符状态的历史时,随着模型在预测下一个钢琴音符的状态方面的改进,它通常也会在使用其自己的输出作为音符历史自动回归地生成扩展的演奏方面有所改进。
事实证明,这个假设并不 而不是总是成立的,因为验证损失相对较低的模型在生成表演时听起来仍然很不悦耳。下面,我表明这个假设不适用于浅层网络,但对于足够深的网络,它在实践中适用。
模型架构
我们现在知道如何定义任务,并可以收集大量正确编码的数据,但我们应该根据数据训练什么模型才能更好地完成钢琴作曲的任务?作为一个好的起点,我们的模型应该有几个属性,这些属性应该能够提高它对看不见的数据进行归纳的能力:
- 该模型对于调的移调应该是不变的,也就是说,将按下的音符向上或向下移动固定数量的键:为了更好地近似,以 c 大调和弦开始的莫札特奏鸣曲应该具有与以 G 大调和弦开始的奏鸣曲相同的相对音符分布。调式并不完全对称,但是音调的相对频率在很大程度上决定了作品的音乐语义。这种现象就是为什么一个乐队可以用主唱更舒服的音调来覆盖汤姆·佩蒂的自由落体,而观众不会注意到。
- 该模型对于节奏的微小变化应该是不变的:如果一首歌曲播放快了或慢了 2%,这应该不会对下一个音符的分布产生太大影响。事实证明,我们不能直接强加这种不变性,但我们可以通过使用数据增强来教授模型速度不变性。
- 当数据流经网络时,模型不应降低输入分辨率:未来音符的分布可能对音乐中的高分辨率细微差别非常敏感,尤其是时间方向上的节奏变化,因此我们不应破坏这些信息。在这方面,假定像池化这样的技术会损害模型性能。
- 模型参数的数量应与感受野的大小成比例:总感受野是模型输入的大小。该值越大,可用于影响未来票据概率的票据历史就越多。在钢琴曲中,这必须至少有几秒钟长。对于几秒钟的感受野,模型参数的数量应该保持在几百万或更少。
由堆叠的扩展卷积层组成的全卷积神经网络是满足上述特性的简单而有效的选择。除了在内核输入之间可能有一个或多个长度的间隙之外,扩展卷积类似于传统卷积。见这篇 TDS 博客文章了解膨胀卷积的更详细的概述,虽然我们在这种情况下使用 1D 卷积,而不是 2D。
与 WaveNet 论文中使用的方法类似,我们将在每个“块”中使用指数增长的膨胀率来构建我们的模型,并堆叠多个块来创建完整的网络。下面是构建两个块模型的 Tensorflow 代码示例,每个块有七层。整个网络变得越来越宽,因此第二个块的每一层比第一个块有更多的滤波器。最后,该模型以单个滤波器结束,其输出通过 sigmoid 激活运行,并且该输出将是下一个音符状态为 1 的预测概率。
from tensorflow.keras.layers import Input, Conv1D, Activation
from tensorflow.keras.models import Model
filter_counts = [
[4, 8, 12, 16, 20, 24, 28], *# block one's filter counts* [32, 36, 38, 42, 46, 50, 54], *# block two's filter counts* ]
inputs = Input(shape=(None, 1)) *# None allows variable input lengths* conv = inputs
for block_idx in range(0, len(filter_counts)):
block_filter_counts = filter_counts[block_idx]
for i in range(0, len(block_filter_counts)):
filter_count = block_filter_counts[i]
dilation_rate = 2**i *# exponentially growing receptive field*conv = Conv1D(filters=filter_count, kernel_size=2,
strides=1, dilation_rate=dilation_rate,
padding=**'valid'**)(conv)
conv = Activation(**'elu'**)(conv)
outputs = Conv1D(filters=1, kernel_size=1, strides=1,
dilation_rate=1, padding=**'valid'**)(conv)
outputs = Activation(**'sigmoid'**)(outputs)
model = Model(inputs=inputs, outputs=outputs)
线dilation_rate = 2**i
导致每个块中的膨胀率从 1 开始(类似于核大小为 2 的传统卷积),然后随着块中的每个后续层呈指数增加。一旦第二个区块开始,这个膨胀率重置为大小 1,并开始像以前一样以指数方式增加。
我们可以让扩张率继续呈指数增长,而无需重置,但这将导致感受野在每个模型深度增长过快。我们也不必为每个新层增加过滤器的数量。我们可以让每一层的过滤器计数总是等于某个大的数字,比如 64。然而,在实验过程中,我发现从少量的过滤器开始,慢慢地增加数量会产生一个统计效率更高的模型。图 3 显示了一个包含两个模块的示意模型:
图 3:模型架构,包括堆叠的扩张 1D 卷积层,每增加一层,扩张率增加两倍。当第二个块开始时,膨胀率重置为 1。每个节点代表一个向量,该向量包含每个后续层的越来越多的元素。
需要对每个Conv1D
层实例化的padding='valid'
参数做最后的说明。我们希望我们的模型输入只看到在当前预测音符之前发生的音符——否则我们将允许在推理时不存在的未来信息泄漏到我们的预测中。我们也不想在输入的音符序列中填充零(静音)。输入序列可以在歌曲中间开始,用无声填充会产生一种人为的输入状态,其中无声突然被内部歌曲片段打断。“有效”设置以满足上述两个要求的方式填充序列。
训练的技巧和诀窍
尽管获取大型数据集很容易,并且我们讨论的模型架构构建起来也相当简单,但训练一个产生真实音乐的模型仍然是一门艺术。这里有一些技巧可以帮助你克服我面临的许多挑战。
深度的重要性
当我开始训练模型时,我从单块浅层网络架构开始,大约有 15 层,每层有大量的权重。虽然验证损失表明这些模型正在从数据中学习,而不是过度拟合,但听到如下表现时,我感到沮丧:
来自太浅(13 个卷积层)的网络的示例性能。
浅层网络的表现开始有点音乐,但随着它离人类产生的种子越来越远,它最终陷入混乱,最终让位于大部分沉默。
令人惊讶的是,当我通过添加更多块来增加网络深度,但保持参数数量不变时,即使验证损失没有减少,性能质量也显著提高:
深层网络(36 层)的性能示例。这种表演在音乐上与种子相去甚远。
产生上述表演的浅层和深层网络在验证集上的损失几乎相同,然而深层网络能够形成连贯的音乐短语,而浅层网络是……前卫。请注意,上述两个网络相对较小,具有 2.6 秒的狭窄感受野和 10-40 层。本文开头显示的更高级的模型有接近 100 层和几乎 10 秒的感受域。同样,这些结果也不是随机的。我已经听了很多分钟的音乐,这些音乐来自浅层和深层模型,使用了许多不同的种子,模式保持一致。
为什么验证损失不能说明全部情况?请记住,我们使用的损失衡量的是模型预测下一个音符的能力,因为人类生成了大量音符。这将涉及,但不完全捕获,一个模型的能力,使用它的 自己的生成的音符历史自动回归一个干净的性能,在很长一段时间内,没有下降到 P(X)内的一个空间,它没有被给予代表性的训练数据。(4)中给出的交叉熵损失是便于训练的内在度量,但是表演的主观质量才是真正的外在度量。
我支持为什么低验证损失并不意味着良好的音响性能,但为什么增加我们的网络深度往往会提高相同损失的音乐性?很可能统计效率是这里的决定性因素。深而薄的卷积神经网络很可能代表了我们试图比浅而宽的卷积神经网络更有效地估计的函数家族。作曲的过程,像许多人类的任务一样,似乎更好地表现为一系列简单功能的组合,而不是一些相对复杂的功能。很可能浅层网络正在“记忆”笔记历史中的常见模式,而深层网络正在将这些模式编码成更有意义的表示,这些表示随着输入的小变化而变化。
我发现了最后一个关于深度的启发,它可以提高表现,减少训练时间。每个附加块的感受野应该相对于最后一个块缩小两倍。这需要每个后续块比上一个少一层。这大大减少了卷积运算的次数,因为最宽的层(靠近输出层的层)具有最小的感受野。同时,如果第一层具有相当大的感受野,模型的整体感受野不会受到很大影响。
应该训练多久?更久。
添加到网络中的每一个额外的块都增加了显著的深度——每个块大约十层。因此,让这些深层网络接受训练可能会很快变得棘手。为了对抗训练期间的爆炸梯度,学习率需要相当小,通常在 0.0001 左右,这意味着模型在收敛到好的解决方案之前必须训练更多的步骤。
然而,也有一线希望。因为我们可以访问如此多的数据(数十亿个注释状态),所以对于一个具有大约一百万个参数的中等规模的模型来说,很难适应数据集。这实际上意味着,你让你的模型训练的时间越长,它听起来就越好。随着训练的进行,验证损失将越来越少,但是模型在训练后期继续学习音乐的极其重要的元素。
为了证明让模型训练足够长的时间是多么重要,这里有一组迷你表演,展示了给定模型在训练过程中是如何学习的。每场迷你演出都有相同的人类作曲种子,由贝多芬的 Für Elise 的片段组成。W 随着每一次重复,模型在被要求完成 seed 之前被给予越来越多的训练时间:
随着模型训练时间的延长,它可以更令人信服地完成音乐短语。
我们看到,直到最后 20%的训练时间,模型才产生音乐上合理的乐句。
为了加快训练速度,我经常从一个批量开始,慢慢增加到 32 个。这鼓励了跨参数空间的探索,并限制了所需的计算资源量。我们可以不考虑初始批量大小为 1 的情况,因为每个单独的训练样本可以包含歌曲片段中的数百个预测(参见图 2 中的输入和目标序列)。
战略偏差初始化
大多数时候,钢琴键没有被按下。只有最具实验性的艺术家才会在任何时候按下几个以上的键。这使得在输出层中正确设置偏置非常重要:
output_bias_init = tensorflow.keras.initializers.Constant(-3.2)outputs = Conv1D(filters=1, kernel_size=1, strides=1,
dilation_rate=1, padding=**'valid'**,
bias_initializer=output_bias_init)(conv)outputs = Activation(**'sigmoid'**)(outputs)
通过将偏差设置为-3.2,我们的模型从一开始就预测了平均被按压的音符的正确基本比率(大约 0.04)。该模型不再需要花费许多训练步骤来弄清楚钢琴键只是没有被经常按下。这消除了曲棍球杆的学习曲线,并加快了训练速度。
数据扩充
同样,旋转和缩放图像可以帮助模型在计算机视觉任务中更好地概括,我们可以将钢琴音符的 1D 序列拉伸和压缩几个百分点,以进一步增加训练数据。其思想是,给定的钢琴作品可以演奏得快一点或慢一点,而不改变其从数据生成分布中被采样的机会。这在对抗数据稀疏性方面有很大帮助,并且它还帮助模型学会对节奏的微小变化保持不变。我通常为每首输入歌曲创建五个克隆,随机拉长 15%到 15%的速度。
安全创建验证数据集
在构建训练集和验证集时,我们不应该对所有歌曲的随机片段进行采样。钢琴曲经常重复整个部分,所以如果我们使用随机抽样的方法,我们可能会无意中在验证集上结束训练。
为了避免这种情况,我们必须确保在将训练集和验证集分成样本片段之前,为它们分配完全不同的歌曲。只要从每个作曲家那里采样的歌曲数量是数百首,我们仍然会得到模型对未知音乐的泛化误差的合理估计。
作曲家和熵
一般来说,不同的作曲家会有更高或更低的音乐熵。也就是说,作曲家的作品或多或少是不可预测的,因为在前一个音符之后可能会有或多或少的不确定性。一般来说,早期作曲家,如巴赫或海顿,在其数据生成分布中具有较低的不确定性,而后期作曲家,如普罗科菲耶夫或拉威尔,在给定音符历史的情况下,将具有更多可能的音符。
由于这种不同程度的熵,如果您的模型需要更多的模型容量、训练时间和/或训练数据来重现更不可预测的风格,请不要感到惊讶。根据我的经验,这篇文章中描述的模型通常可以轻松地用一百首歌曲复制巴赫的作品,而让一个模型产生引人注目的肖邦音乐似乎需要更多的输入数据。
使用训练模型生成性能
朴素模型推理
一旦你有了一个训练好的模型,你会想用它来生成新的作品。为了生成一个性能,我们从一个长度等于模型感受域的输入种子开始。这颗种子可以是人类写的一首歌的一部分,或者只是沉默。然后我们可以调用这个输入种子上的model.predict
来生成下一个时间步长中第一个键的状态被按下的概率。如果这个预测是 0.01,那么我们将以 1%的概率对下一个被按下的键进行采样。
如果我们从输入种子中删除第一个音符,并将模型的最后一个采样音符状态添加到末尾,那么我们再次得到长度等于模型感受域的输入向量。我们可以使用新的输入来生成另一个键状态,这次使用模型的最后一个输出作为输入的一部分。最后,我们可以无限地重复这个过程,在某一点上,输入完全由我们的模型生成。这是自回归背后的核心思想。
快速波网生成算法
虽然上述方法可以很好地工作,但制作一分钟的音乐可能需要几个小时。这是因为每个model.predict(input)
调用需要非常大量的连续卷积运算来计算最终输出,并且计算时间与模型深度的比例很差。
事实证明,如果我们将过去卷积运算的一些信息存储在一个队列中,我们可以大大加快这些计算的速度。这就是快速波网生成算法背后的思想。这个算法如何工作超出了本文的范围,但是请放心,我已经在下面更详细描述的 PianoNet 包中实现了一个纯 Python 版本。如果你使用这个软件包,即使有一个包含数百万参数的非常大的网络,生成一分钟的钢琴音乐也需要五分钟的时间,而不是几个小时。
边缘厌恶
关于性能生成的最后一个细节包括限制模型漂移。因为我们的架构相对于按键变化是不变的,所以它不编码按键状态处于哪个八度音阶。这意味着该模型将键盘上的所有八度音程视为同等可能性,并且可以向钢琴键状态的边缘漂移。我们不希望这种情况发生,因为作曲家倾向于将按下的键放在更靠近钢琴中间的位置。
为了用最简单的方法来对抗这种漂移,我使用了我称之为边缘厌恶的方法。这种方法是在表演过程中使模型偏离边缘的一种方式,而不会过多地改变模型的输出分布。本质上,边缘厌恶强制钢琴键盘上非常高和非常低的音符仅在模型预测到非常高的概率时被演奏。例如,如果模型预测钢琴上最高的键将以 0.05 的概率被按下,则该键仍然不会被随机采样为被按下。
使用 PianoNet Python 包
此时,您可能会兴奋地开始训练自己的模型来生成钢琴曲。为了使这个过程尽可能简单,我创建了 PianoNet ,这是一个 Python 包,用于轻松复制我上面描述的工作。
有关如何使用该软件包的完整端到端教程,请参见链接报告中包含的自述文件。在下面的小节中,我将简要地讨论用于控制数据收集、模型训练和性能生成的抽象。任何工作流程的基本步骤都是:
- 使用 piano net/scripts/master _ note _ array _ creation . py 脚本创建培训和验证 MasterNoteArray 文件
- 创建一个目录来保存您的计算并包含一个 run_description.json 文件,该文件指定了用于训练的超参数以及到输入数据文件的链接
- 使用 pianonet/scripts/runner.py 脚本在同一目录中启动运行
- 一旦训练完成,使用 piano net/model _ inspection/performance _ tools . py 中的
get_performance
方法生成表演
收集和预处理数据
第一步是找到一些 midi 文件,其中包含您希望模型学习的风格的钢琴演奏。互联网是找到各种作曲家的免费钢琴 midi 文件的好地方。一旦你收集了这些文件,把它们都移到你选择的目录下。在本例中,我假设它们位于/path/to/midi/
。
为了使用 PianoNet 包训练模型,我们需要将所有的 midi 文件提取到保存到磁盘的训练和验证 MasterNoteArray 实例中。MasterNoteArray 对象实质上是所有输入歌曲的 1D 数组,这些歌曲以一种易于训练的方式连接在一起。我们可以创建一个数据描述 json 文件来生成 MasterNoteArray,如下所示:
{
"file_name_prefix": "dataset_master_note_array",
"min_key_index": 31,
"num_keys": 72,
"resolution": 1.0,
"end_padding_range_in_seconds": [
4.0,
6.0
],
"num_augmentations_per_midi_file": 5,
"stretch_range": [
0.85,
1.15
],
"time_steps_crop_range": [],
"midi_locator": {
"paths_to_directories_of_midi_files": ["/path/to/midi/"],
"whitelisted_midi_file_names": []
},
"validation_fraction": 0.2
}
确保将上面的/path/to/midi/
路径更改为存储 midi 文件的位置。对于更多的增强(每首歌曲的拉伸),将num_augmentations_per_midi_file
从 5 增加到一个更大的数字。
最后,我们可以使用以下命令生成输入数据集:
*python pianonet/scripts/master_note_array_creation.py /path/to/data_description.json /path/to/output/directory*
这个脚本将生成两个.mna_jl
文件,一个包含训练数据,一个包含验证数据。
培训模型
一旦我们有了 MasterNoteArray 数据集,我们就可以开始模型训练课程或运行。每个运行都位于自己的目录中,并且有一个 run_description.json 文件来描述运行应该如何执行:
{
"data_description": {
"training_master_note_array_path": "./dataset_train.mna_jl",
"validation_master_note_array_path": "./dataset_val.mna_jl"
},
"model_description": {
"model_path": "",
"model_initializer": {
"path": "pianonet/scripts/model_generators/basic_convolutional_with_blocks.py",
"params": {
"filter_increments": [
[ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4],
[ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4],
[ 6, 6, 6, 6, 6, 6, 6, 6, 8, 8],
],
"default_activation": "elu",
"use_calibrated_output_bias": true
}
}
},
"training_description": {
"batch_size": 8,
"num_predicted_time_steps_in_sample": 24,
"epochs": 10000,
"checkpoint_frequency_in_steps": 250,
"fraction_data_each_epoch": 0.1,
"loss": "binary_crossentropy",
"optimizer_description": {
"type": "Adam",
"kwargs": {
"lr": 0.0001,
"beta_1": 0.9,
"beta_2": 0.999,
"epsilon": 1e-07,
"decay": 0.0
}
}
},
"validation_description": {
"batch_size": 1,
"num_predicted_time_steps_in_sample": 512,
"fraction_data_each_epoch": 1.0
}
}
重要的是training_master_note_array_path
和validation_master_note_array_path
都指向。在数据集创建步骤中创建的 mna_jl 文件。
model_description
参数描述了应该如何生成模型。filter_increments
中的每个列表控制一个块,每个数字代表当前层的过滤器数量相对于前一层的过滤器数量的增加。添加更多的列表将通过增加深度(更多的块)来增加模型容量,而增加过滤器增量幅度将增加模型的宽度。其余的描述符字段都是不言自明的,它们允许您控制超参数,比如批量大小、每个输入样本序列包含多少个预测音符以及优化器的参数。以上数字都代表了理智的默认。
一旦创建了 run_description.json 文件并将其放在运行目录中,就可以使用以下命令开始培训:
*python pianonet/scripts/runner.py /path/to/run/directory*
这将在您的终端中启动一个培训会话。要查看运行的输出日志,在运行目录中启动另一个终端,并使用tail -f training_output.txt
在训练过程中实时查看日志。
模型将定期保存在运行路径内的 Models 目录中,文件名类似于0_trained_model
。如果训练停止,可以使用上述命令重新开始,并从最后保存的模型开始。
产生性能
最后一步,一旦你对模型进行了充分的训练,就是听听它的声音。这包括输入您的训练模型的文件路径和性能工具方法的种子。我已经创建了一个 jupyter 笔记本,概述了如何一步一步地做到这一点,可以在pianonet/examples/pianonet_mini/get_performances.ipynb
找到。
结论
基于扩张卷积的全卷积神经网络在正确训练时可以成功地生成令人信服的钢琴音乐。这些模型可以输入原始的音符序列,易于构建,并且在一些技巧的帮助下易于训练。最棒的是,如果使用 Fast-WaveNet 算法,他们几乎可以实时生成音乐。 PianoNet 是一个 python 包,可以轻松重现所有这些特性。
虽然这些全卷积网络的性能听起来相当不错,但与简介中提到的基于变压器的架构相比,它们在某些方面有所欠缺。因为全卷积网络被有效地限制为具有短于 20 秒的感受域,所以它们不能学习超过这个时间尺度的歌曲模式,例如奏鸣曲式的重复结构。卷积模型也很难学习旋律,而基于 NLP 的 transformer 模型似乎做得很好。
尽管有这些缺点,本帖中描述的全卷积架构相对简单,易于使用,并且是探索机器生成艺术可能性的一种快速方式。
参考
[1]波网论文:https://arxiv.org/abs/1609.03499
https://en.wikipedia.org/wiki/Data_processing_inequality
[3]快波网论文:https://arxiv.org/abs/1611.09482
用闪亮的 Web 应用生成泊松分布
使用闪亮的网络应用程序进行交互式概率分析
泊松分布使我们能够想象在给定的时间间隔内一个事件发生的概率。这些事件必须相互独立。
这方面的一些例子可能是:
- 一个人每周收到的电子邮件数量(假设电子邮件相互独立到达)
- 给定时间间隔内极端天气事件的数量,例如一个国家每 10 年发生一次异常寒潮的概率
- 一家公司在一周内的产品销售数量
例子
让我们以酒店取消为例(Antonio、Almeida 和 Nunes 的数据和研究,可从下面的参考资料部分获得)。
假设在给定的一周内,酒店会有一定数量的预订取消。根据研究中的第一个数据集(H1),酒店预计每周平均有 115 次预订取消。
来源:闪亮的网络应用
从上面我们可以看到,酒店预计每周最少有 90 次取消预订,最多有 150 次取消预订。
然而,一个闪亮的 Web 应用程序的优势在于,它允许我们动态地分析一系列不同的场景,由此可以操纵 l (我们的λ值)。
例如,研究中的另一家酒店(H2)平均每周有 287 次取消预订。让我们更改 lambda 值,看看这会如何影响分析。
来源:闪亮的网络应用
在这种情况下,我们看到每周大约有 230–330 次取消。这将允许酒店经理对每周可预期的酒店取消的大致数量进行概率评估。
也就是说,上述泊松分布可用于分析任何问题——只需知道λ值,并在必要时调整 x 轴,以说明特定数据集的整体值范围。
你为什么不自己试试这个模拟?
创建您自己的闪亮网络应用程序
要生成闪亮的 Web 应用程序,请执行以下操作:
- 打开 RStudio,点击文件- >新文件- >闪亮 Web App 。
- 选择多文件并为您的应用程序指定一个名称。
- 在 ui 中。R ,粘贴以下内容:
library(shiny)# Define UI for application that draws a Poisson probability plot
shinyUI(fluidPage(
# Application title
titlePanel("Poisson Distribution"),
# Sidebar with a slider input for value of lambda
sidebarLayout(
sidebarPanel(
sliderInput("l",
"l:",
min = 1,
max = 400,
value = 1)
),
# Show a plot of the generated probability plot
mainPanel(
plotOutput("ProbPlot")
)
)
))
4.在服务器中。R ,粘贴以下内容:
library(shiny)
library(ggplot2)
library(scales)# Shiny Application
shinyServer(function(input, output) {
# Reactive expressions
output$ProbPlot <- renderPlot({
# generate lambda based on input$lambda from ui.R
l = input$l
x <- 0:400
# generate trials based on lambda value
muCalculation <- function(x, lambda) {dpois(x, l=lambda)}
probability_at_lambda <- sapply(input$x, muCalculation, seq(1, 400, 0.01))
# draw the probability
plot(x, dpois(x, l), type='h',lwd=3)
title(main="Poisson Distribution")
})
})
5.完成后,点击控制台右上角的运行应用:
资料来源:RStudio
现在,您将看到一个动态泊松分布,其中 l (lambda) 可以动态调整:)
来源:闪亮的网络应用
现在,假设你想调整音阶。如果您正在处理一个小得多的问题,例如,您平均每天收到 10 封电子邮件,并且想知道这个时间间隔的最小值和最大值,该怎么办?
在 ui 中。r,我们把最大 lambda 值设为 20。
# Sidebar with a slider input for value of lambda
sidebarLayout(
sidebarPanel(
sliderInput("l",
"l:",
min = 1,
max = 20,
value = 1)
在服务器中。r, x 的值和顺序可以调整。让我们将最大范围设置为 20。
x <- 0:20
probability_at_lambda <- sapply(input$x, muCalculation, seq(1, 20, 0.01))
假设 l = 10 ,下面是现在的分布图:
来源:闪亮的网络应用
结论
泊松分布在分析给定时间间隔内一定数量的事件发生的概率时非常有用——当我们可以使用闪亮的 Web 应用程序进行动态分析时甚至更有用!
非常感谢您阅读本文,感谢您的任何问题或反馈。
您还可以找到我的 GitHub(MGCodesandStats/shiny-we b-apps)的链接,其中包含下面的代码脚本,以及其他有用的参考资料。
免责声明:本文是在“原样”的基础上编写的,没有担保。本文旨在提供数据科学概念的概述,不应以任何方式解释为专业建议。
参考
- 安东尼奥、阿尔梅达、努内斯,2016。使用数据科学预测酒店预订取消
- GitHub:MGCodesandStats/hotel-modeling
- GitHub:MGCodesandStats/shiny-web-apps
- R 文档:泊松分布
使用 Python 在数据库中生成随机数据
使用 Pandas 用虚拟数据填充 MySQL 数据库
在本文中,我们将演示如何为 MySQL 数据库生成虚拟数据和输入。大多数情况下,我们需要创建一个数据库来测试我们构建的一些软件。为此,我们可以使用的结构化的 Q 查询 L 语言(SQL)来创建一个模拟数据库系统。但是,如果我们要测试数百甚至数千条记录,可能会出现问题。我们绝对不可能在我们的 SQL 脚本中编写这么多的 insert 语句来满足产品部署的测试截止日期,那将是非常乏味和耗时的。
为了帮助解决这个问题,我们值得信赖的朋友 Python 来帮忙了!
巨蟒来救援了!
您可能知道也可能不知道,python 是一种脚本语言,以其丰富的库而闻名。Python 还以其使用非常简单的语法自动执行任务的能力而闻名。
必备知识:
- 使用 pip 安装 python 包。
- 对什么是数据库以及它们如何使用 SQL 有基本的了解。
即使您不具备上述要求,本文也将确保您理解其内容,以帮助您进一步发展您的 Python 事业。
所以让我们开始吧!
我们将在这个项目中使用的 python 库是:
- Faker — 这是一个可以为你生成伪数据的包。我们将用它来生成我们的虚拟数据。
- 熊猫 —这是一个数据分析工具。这将用于封装我们的虚拟数据,并将其转换为数据库系统中的表格。
- SQLAlchemy —这是一个用于 SQL 的对象关系映射器(ORM)。ORM 将被用作我们的数据库引擎,因为它将为我们的数据库提供连接。
我们将使用的其他工具包括:
- XAMPP —这是一个包含 MariaDB、PHP 和 Perl 数据库管理系统的发行版。我们将使用 MariaDB,这是我们的 SQL 数据库。
注意:然而,可以使用不同类型的数据库,因为本文将关注 MySQL 连接。所以,对于任何其他数据库如 PostgreSQL、微软 SQL Server、Oracle 等。将需要替代配置。
入门指南
我们开始吧!
首先,我们需要前面提到的那些 python 库。Pip—PIPIinstallsPpackages(简称 Pip)。PIP 是 python 的包管理器,也是我们用来安装 python 包的工具。
注:康达也可以用于那些森蚺发行爱好者。
*pip install **pandas Faker sqlalchemy mysqlclient***
我们现在已经使用上面的代码安装了所有相关的包。
导入库
首先,我们需要导入相关的库。
*import pandas as pd
from faker import Faker
from collections import defaultdict
from sqlalchemy import create_engine*
defaultdict
(默认字典)来自 collections 包。这将作为我们的字典,因为它将提供比普通字典更多的功能,我们将在本文后面看到。
生成虚拟数据
为了生成我们的虚拟数据,我们将首先初始化我们将用来获取虚拟数据的Faker
实例。
*fake = Faker()*
我们将使用fake_data
来创建我们的字典。defaultdict(list)
将创建一个字典,该字典在被访问时将创建当前没有存储在字典中的键值对。本质上,您不需要在字典中定义任何键。
*fake_data = defaultdict(list)*
现在,让我们决定从Faker
实例中获取哪种数据,并存储在fake
变量中。这种数据的一个例子包括:
- 西方人名的第一个字
- 姓
- 职业
- 出生日期
- 国家
我们可以在伪数据实例中通过不同的方法访问这些数据。
我们将要访问的方法是first_name()
、last_name()
、 job()
、date_of_birth()
和country()
。
我们将循环一千次来访问这些方法,然后将它们添加到我们的字典中。
*for _ in range(1000):
fake_data["first_name"].append( fake.first_name() )
fake_data["last_name"].append( fake.last_name() )
fake_data["occupation"].append( fake.job() )
fake_data["dob"].append( fake.date_of_birth() )
fake_data["country"].append( fake.country() )*
注意:因为使用了 **defaultdict(list)**
,所以每当我们访问一个不是我们创建的键时,就会自动添加一个空 list []作为它的值。
现在,因为我们的字典fake_data
中有所有的随机数据。我们需要将这些数据打包到我们的熊猫数据框架中。
*df_fake_data = pd.DataFrame(fake_data)*
pandas 数据框架提供了许多分析和操作数据的功能。为了这个项目的目的,我们将操纵这个数据帧作为一个数据库条目。
向数据库添加虚拟数据
首先,让我们建立数据库连接!我们将使用前面提到的 XAMPP 来启动我们的 SQL 实例。
我们已经成功启动了我们的数据库。现在,我们将创建一个数据库,其中包含由 python 脚本使用下面的代码生成的表。
*mysql -u root
create database testdb;*
看啊!我们启动了我们的 MySQL 数据库(这里是 MariaDB ),并创建了我们的testdb
数据库。
注意:Root 是我的用户名。没有为我的帐户配置(创建)密码。但是,如果您创建了密码,在 **mysql -u root**
后可能会有提示,要求您输入密码。
如你所见,我使用的是命令行版本的 MySQL。GUI 版本也是一个备选选项。
现在,我们的下一步是将我们创建的数据库连接到我们的 python 代码。为此,我们将使用来自sqlalchemy
库的引擎。
*engine = create_engine('mysql://root:[@localhost/](http://twitter.com/localhost/foo)testdb', echo=False)*
create_engine
中的琴弦遵循driver://username:password**@host**:port/database
的结构。如你所见,我没有密码,所以它是空白的。可以指定端口,但是如果您使用 MySQL 的默认配置,就没有必要了。
现在我们已经创建了我们的连接,我们现在可以将熊猫数据帧直接保存到我们的数据库testdb
。
*df_fake_data.to_sql('user', con=engine,index=False)*
当执行上面的代码时,将在我们的数据库中创建一个用户表,并向其中添加一千条记录。
作者截图
作者截图
通过查看数据和屏幕截图左下角的行号,您可以看到,我们刚刚生成了 1000 条记录!
按作者显示表中行数的屏幕截图。
获取代码
该项目的完整代码位于以下要点中:
奖金!
让我们生成十万(100,000)条记录!
一个可以完成的任务是通过增加循环中的迭代次数来增加生成的假记录的数量。
注意。我们首先需要使用查询 *drop table user;*
删除之前由我们的脚本创建的用户表
*for _ in range(**100000**):
fake_data["first_name"].append( fake.first_name() )
fake_data["last_name"].append( fake.last_name() )
fake_data["occupation"].append( fake.job() )
fake_data["dob"].append( fake.date_of_birth() )
fake_data["country"].append( fake.country() )*
由于我的终端输出的记录太多,我将只说明行数:
下面的时间戳显示了在我的机器上生成十万条记录所用的时间:
不错的成绩对吧!
我们在短短 31 秒内就在数据库中创建了 100,000 条记录。31 秒!你能相信吗,疯狂的权利!
摘要
我们能够创建一个由普通用户数据填充的数据库,如他们的名字、姓氏、出生日期、职业和国家。我们不仅创造了它,而且在很短的时间内创造了它。这个解决方案的一个好处是,它比我遇到的许多其他解决方案都要快。请随意使用 faker 库。它还可以用来生成许多其他类型的数据。
加入我们的邮件列表
如果您喜欢这篇文章,请注册,以便在我们发布新文章时收到电子邮件。使用此链接输入您的电子邮件,完全免费!
用 LSTM 的生成简短的星球大战文本
“会说话并不会让你变得聪明”——绝地大师魁刚·金。
毫无疑问,我是那些被称为“星球大战迷”的人中的一员。我记得小时候在电视上看《幽灵的威胁》时被它震撼了(我知道,这不是一部伟大的电影)。后来,我看了《T2》中的《克隆人的进攻》和《T4》中的《西斯的复仇》(这是影院上映的最后一部电影),接着又看了原版三部曲的 DVD,那时这些电影还在上映。作为一个成年人,我在右臂上纹了一个 Tie 战士的纹身,这让我达到了高潮。
对《星球大战》的热情很好地回答了“我应该做什么样的 NLP 项目?”;我想开发一个完整的自然语言处理项目,边做边练习一些技能。
这是一个关于 LSTM 氏症的项目实验的简要文档,以及如何训练一个语言模型来在字符级别上生成特定域内的文本。给定一个种子标题,它通过一个 API 编写一个简短的描述。该模型是使用 Tensorflow 从零开始构建的,没有迁移学习。它使用了来自 Wookiepedia.com 的文本,Fandom.com 的团队(负责管理网站)非常友好地允许我使用网络抓取器收集数据并发表这篇文章。
最终产品
下面可以看到工作模型的演示。
作者图片
要求
为了复制这个模型,你需要从这里下载代码。然后,安装所有需要的依赖项(强烈建议创建一个新的虚拟环境,并在其上安装所有软件包),如果您使用的是 Linux,则:
pip install -r requirements-linux.txt
但是,如果您使用的是 Windows,只需使用:
pip install -r requirements-windows.txt
在安装了所有依赖项之后,您需要经过训练的模型能够生成任何文本。由于 GitHub 上的大小限制,模型必须从这里下载。只需下载整个模型文件夹,并将其放在部署文件夹下。最后,激活安装了所有依赖项的环境,并在下载项目的文件夹中打开终端,键入:
python deploy/deploy.py
该项目
下面是所有使用的重要库的列表:
- 美丽的声音
- 要求
- Lxml
- 熊猫
- Numpy
- MatplotLib
- 张量流
- 瓶
- 超文本标记语言
- 半铸钢ˌ钢性铸铁(Cast Semi-Steel)
我们现在将检查项目的所有主要部分。
数据处理
用于训练模型的文本是使用网络报废从伍基人百科网站(一种星球大战维基百科)中挖掘出来的。用于该任务的所有代码都在 wookiescraper.py 文件中,并且创建了一个类Article
来构造文本,每篇文章都包含一个标题、主题描述(这将是文章页面上的第一个简要描述)以及它们所属的类别。提取数据的主要库是 beautifulsoup、requests 和 Pandas(在数据帧中存储文本)。
为了列出所有可能的文章,调用了函数create_complete_database
。它创建了一个所有佳能文章的 URL 列表(在《星球大战》宇宙中,所有在书籍、漫画和视频游戏等替代媒体中产生的故事都被重新启动;旧的故事被贴上“传说的标签,而仍然是官方的故事和新的故事被认为是经典。然后,它通过使用 Article 类自己的函数下载并创建列表中的每篇文章。然后创建一个包含所有下载文章的数据框架,并保存在一个完整的 Database.csv 文件中。
为了给模型提供信息,我们还必须处理获得的数据;文件 data_processor.py 包含该任务使用的所有代码。函数clean_data
获取文本的数据帧,并通过删除不需要的字符和缩短文本对其进行格式化(这是必须要做的,因为我们正在创建一个在字符级别上工作的模型,该模型将很难从较长的句子中学习模式和上下文)。该文件还包含将给定的文本语料库转换成单热点向量的函数,反之亦然,以及数据集生成器函数;函数build_datasets
将构建一个 training_dataset 和一个 validation_dataset ,而不是在训练期间将所有数据加载到内存中,每一个都是 Tensorflow Dataset 对象,它们在训练期间处理数据并将数据作为块馈送到模型中。
模型和培训
LSTM 是一种递归神经网络细胞,在长数据序列中具有更高的信息保持能力。因为这个特性,它在处理 NLP 问题时非常有用。对于这个项目,使用了一个序列到序列架构来生成输出句子。在这种方法中,输入字符串(文章的标题)被提供给编码器,编码器按顺序逐个字符地处理数据,并传递包含输入信息的编码向量。然后,解码器将使用此信息再次按顺序逐个字符地生成新的嵌入向量,该向量将进入 softmax 层以生成概率向量,每个可能的字符一个值。每个输出字符在前一个字符之后生成,总是使用包含编码器生成的输入信息的向量。创建和训练模型的代码在文件 run.py 中。
在 GPU 初始化和词汇定义之后,创建了一个config
字典,以使超参数调整更容易:
# enable memory growth to be able to work with GPU
GPU = tf.config.experimental.get_visible_devices('GPU')[0]
tf.config.experimental.set_memory_growth(GPU, enable=True)# set tensorflow to work with float64
tf.keras.backend.set_floatx('float64')# the new line character (\n) is the 'end of sentence', therefore there is no need to add a '[STOP]' character
vocab = 'c-y5i8"j\'fk,theqm:/.wnlrdg0u1 v\n4b97)o36z2axs(p'
vocab = list(vocab) + ['[START]']config = { # dictionary that contains the training set up. Will be saved as a JSON file
'DIM_VOCAB': len(vocab),
'MAX_LEN_TITLE': MAX_LEN_TITLE,
'MAX_LEN_TEXT': MAX_LEN_TEXT,
'DIM_LSTM_LAYER': 512,
'ENCODER_DEPTH': 2,
'DECODER_DEPTH': 2,
'LEARNING_RATE': 0.0005,
'BATCH_SIZE': 16,
'EPOCHS': 100,
'SEED': 1,
# 'GRAD_VAL_CLIP': 0.5,
# 'GRAD_NORM_CLIP': 1,
'DECAY_AT_10_EPOCHS': 0.9,
'DROPOUT': 0.2,
}
为了使结果具有可重复性,还将使用种子:
tf.random.set_seed(config['SEED'])
之后,最后,采集的数据被加载到数据帧中,进行清理,现在可以设置新的配置选项,如训练/验证分割:
data = pd.read_csv('Complete Database.csv', index_col=0)data = clean_data(data)config['STEPS_PER_EPOCH'] = int((data.shape[0] - 1500) / config['BATCH_SIZE'])
config['VALIDATION_SAMPLES'] = int(
data.shape[0]) - (config['STEPS_PER_EPOCH'] * config['BATCH_SIZE'])
config['VALIDATION_STEPS'] = int(
np.floor(config['VALIDATION_SAMPLES'] / config['BATCH_SIZE']))
然后定义学习率。在这个项目中,一个指数衰减的学习率被证明可以给出最好的结果:
# configures the learning rate to be decayed by the value specified at config['DECAY_AT_10_EPOCHS'] at each 10 epochs, but to that gradually at each epoch
learning_rate = ExponentialDecay(initial_learning_rate=config['LEARNING_RATE'],
decay_steps=config['STEPS_PER_EPOCH'],
decay_rate=np.power(
config['DECAY_AT_10_EPOCHS'], 1/10),
staircase=True)
加载数据后,现在可以构建训练数据集和验证数据集:
training_dataset, validation_dataset = build_datasets(
data, seed=config['SEED'], validation_samples=config['VALIDATION_SAMPLES'], batch=config['BATCH_SIZE'], vocab=vocab)
以训练后保存模型为目标,将选择一条路径。该文件夹将以通用名称命名,然后更改为模型完成训练的具体时间。此外,词汇和模型配置都将保存为json
文件。
folder_path = 'Training Logs/Training' # creates folder to save traning logs
if not os.path.exists(folder_path):
os.makedirs(folder_path)# saves the training configuration as a JSON file
with open(folder_path + '/config.json', 'w') as json_file:
json.dump(config, json_file, indent=4)
# saves the vocab used as a JSON file
with open(folder_path + '/vocab.json', 'w') as json_file:
json.dump(vocab, json_file, indent=4)
为了监控模型,使用了一些回调函数(在训练期间以指定的时间间隔调用的函数)。这些函数包含在 callbacks.py 文件中。创建了一个自定义类CallbackPlot
,以便在整个训练过程中绘制训练误差。Tensorflow 回调类ModelCheckpoint
和CSVLogger
的对象也被实例化,以便分别保存训练时的模型和训练日志:
loss_plot_settings = {'variables': {'loss': 'Training loss',
'val_loss': 'Validation loss'},
'title': 'Losses',
'ylabel': 'Epoch Loss'}last_5_plot_settings = {'variables': {'loss': 'Training loss',
'val_loss': 'Validation loss'},
'title': 'Losses',
'ylabel': 'Epoch Loss',
'last_epochs': 5}plot_callback = CallbackPlot(folder_path=folder_path,
plots_settings=[
loss_plot_settings, last_5_plot_settings],
title='Losses', share_x=False)model_checkpoint_callback = ModelCheckpoint(
filepath=folder_path + '/trained_model.h5')csv_logger = CSVLogger(filename=folder_path +
'/Training logs.csv', separator=',', append=False)
最后,可以构建、编译和训练模型:
###### BUILDS MODEL FROM SCRATCH WITH MULTI LAYER LSTM ###########
tf.keras.backend.clear_session() # destroys the current graphencoder_inputs = Input(shape=(None, config['DIM_VOCAB']), name='encoder_input')
enc_internal_tensor = encoder_inputsfor i in range(config['ENCODER_DEPTH']):
encoder_LSTM = LSTM(units=config['DIM_LSTM_LAYER'],
batch_input_shape=(
config['BATCH_SIZE'], MAX_LEN_TITLE, enc_internal_tensor.shape[-1]),
return_sequences=True, return_state=True,
name='encoder_LSTM_' + str(i), dropout=config['DROPOUT'])
enc_internal_tensor, enc_memory_state, enc_carry_state = encoder_LSTM(
enc_internal_tensor) # only the last states are of interestdecoder_inputs = Input(shape=(None, config['DIM_VOCAB']), name='decoder_input')
dec_internal_tensor = decoder_inputsfor i in range(config['DECODER_DEPTH']):
decoder_LSTM = LSTM(units=config['DIM_LSTM_LAYER'],
batch_input_shape=(
config['BATCH_SIZE'], MAX_LEN_TEXT, dec_internal_tensor.shape[-1]),
return_sequences=True, return_state=True,
name='decoder_LSTM_' + str(i), dropout=config['DROPOUT']) # return_state must be set in order to retrieve the internal states in inference model later
# every LSTM layer in the decoder model have their states initialized with states from last time step from last LSTM layer in the encoder
dec_internal_tensor, _, _ = decoder_LSTM(dec_internal_tensor, initial_state=[
enc_memory_state, enc_carry_state])decoder_output = dec_internal_tensordense = Dense(units=config['DIM_VOCAB'], activation='softmax', name='output')
dense_output = dense(decoder_output)model = Model(inputs=[encoder_inputs, decoder_inputs], outputs=dense_output)model.compile(optimizer=optimizer, loss='categorical_crossentropy')history = model.fit(x=training_dataset,
epochs=config['EPOCHS'],
steps_per_epoch=config['STEPS_PER_EPOCH'],
callbacks=[plot_callback, csv_logger,
model_checkpoint_callback],
validation_data=validation_dataset,
validation_steps=config['VALIDATION_STEPS'])
训练后,文件夹将包含与本次训练相关的所有数据,如损失函数图、不同时间步长的误差以及模型本身。
model.save(folder_path + '/trained_model.h5', save_format='h5')
model.save_weights(folder_path + '/trained_model_weights.h5')
plot_model(model, to_file=folder_path + '/model_layout.png', show_shapes=True, show_layer_names=True, rankdir='LR') timestamp_end = datetime.now().strftime('%d-%b-%y -- %H:%M:%S')# renames the training folder with the end-of-training timestamp
root, _ = os.path.split(folder_path)timestamp_end = timestamp_end.replace(':', '-')
os.rename(folder_path, root + '/' + 'Training Session - ' + timestamp_end)print("Training Successfully finished.")
部署
为了服务于该模型,使用 Flask 包构建了一个简单的接口,其代码可以在 deploy 文件夹下找到。
结论
经过训练后,该模型能够在给定种子字符串的情况下生成句子。下面是生成句子和用于生成句子的种子字符串的一些示例(我将使用我的猫的名字作为示例,但是因为它们已经被称为 Luke、Han 和 Leia,所以它们已经存在于训练数据集中):
- 佩德罗:佩德罗是一名男性人类,在瑞博·l·阿拉内特·奥尼号上担任指挥官。
- 佩德罗·恩里克:佩德罗·欧索农是一名人类女性,在银河共和国的加巴尔形态中担任指挥官。
- 奇科是一名塔姆泰德人,作为银河帝国的指挥官为银河帝国服务。
- 莎拉:莎拉是一名人类男性,在银河内战期间,他在共和军中担任指挥官。
从上面可以看出,该模型学习了如何合理地形成一些单词,如何确定这些单词的大小,如何正确地结束一个句子,以及如何形成某种上下文。然而,它似乎严重偏向于总是描述在《星球大战》宇宙中的一个派系下服务的人类。这可以用这样一个事实来解释,即模型的架构不是使用单词嵌入构建的(这将允许更复杂的上下文学习),因为有几个单词是《星球大战》独有的。我对未来项目的一个好主意将是为星球大战宇宙生成一个特定的单词嵌入,然后使用它来生成新的文本。