用整数线性规划解决数独难题
如果你更擅长线性编程,而不是用传统的方法解决数独难题,那么这篇博文就是为你准备的!
我刚刚结束了研究生院的第三个学期,我想写一篇关于我在算法课程中学到的一些东西的短文。我最喜欢的话题是线性编程,事实证明同样的原理也适用于解决数独难题。
如果你可以用一个线性目标函数和线性不等式约束,线性规划 (LP)来表述一个问题,那么它就是寻找其最优解的有力工具。换句话说,如果你能把一个问题写成:
Primal (left) and dual (right) formulations of linear programming problems — maximize or minimize an objective function, subject to inequality constraints where b, c, x and y are vectors and A is a matrix.
你可以找到最优参数 x 或者 y. (比如 LP 可以用来分配工作,最小化运输成本,或者创造健康饮食。)LP 的一种特殊类型是整数线性规划 (ILP),由此我们将参数 x 或 y 限制为整数*。*
在这篇文章中,我想研究 ILP 的一个特殊应用:解决数独。(感谢西南航空机组提供我的拼图和零食!)
Courtesy of the December 2017 issue of Southwest Magazine. I will show how to solve “Hard 1” using LP.
目标函数
与典型的 LP 或 ILP 问题不同,数独没有比另一个更好的解决方案。在我们的例子中,我们希望找到任何可行的解决方案——一个满足我们的约束的方案。因此,我们可以指定一个任意的目标函数。
线性约束
一个难题包括填充一个 9×9 的网格,这样每一行、每一列和每一个 3×3 的子网格只包含一个整数值。换句话说,你不能在左下方的第一行、第一列或第一个子格子中输入另一个数字。
因为每个框可以填充九个值,并且我们被约束在九行九列中的每一行,所以我们有 9 = 729 个参数。下面我们将用 v (值) r (行) c (列)来索引 x 中的这些值。
更具体地,我们可以将我们的目标函数和线性约束公式化如下:
Primal formulation for solving a generic sudoku problem. Note the arbitrary objective function mentioned earlier. Most of the heavy lifting occurs within the four constraints above. Adapted from http://profs.sci.univr.it/~rrizzi/classes/PLS2015/sudoku/doc/497_Olszowy_Wiktor_Sudoku.pdf.
第一个约束要求每个单元格(由其行和列表示)包含一个值。第二个和第三个约束保持列和行中分别只包含一个值。最后一个约束确定了在每个子网格中只能找到一个值。
当您试图解决任何特定的数独时,甚至需要指定更多的约束,即每个非空单元格的初始状态。
解开谜题
我使用名为 PuLP 的用于解决 LP 问题的 Python 包来解决上面的“困难 1”数独。纸浆有一些很好的现有文档关于如何使用它的软件来解决这个问题。这是用 LP 解决数独难题的又一个详解,附带补充码。
我改编的纸浆的数独例子可以在这里找到。请注意,我的编辑约束只是满足我的特定谜题的起始状态。
我希望你喜欢我解决数独难题的乐趣。说到捣乱,请注意:以下是我的谜题解答。
相关阅读
- 达斯古普塔、帕帕迪米特里奥和瓦齐拉尼的算法,第 7 章
- CLRS ,第 29 章
使用 SSH 在远程 Docker 容器上运行 Jupyter
Own source
设置您的环境,使之与远程实例中运行的 Docker 和 Jupyter 一起工作
在我的研究中,我使用机器学习/深度学习算法,我主要使用 Python 开发这些算法。因此,我发现测试不同的框架很有用(比如 Keras、PyTorch、Tensorflow… )。这导致我使用 Docker ,特别是 ufoym/deepo 图像,因为它提供了一个非常完整的环境。
我使用一台装有 GPU 的远程机器,在那里我安装了 Docker。在编程和测试一些代码片段的时候,我发现使用 Jupyter 真的很有帮助。我以前在本地使用过 Jupyter…但是这里的问题是我如何远程使用运行在另一台机器上的 Jupyter。除此之外,如果我在我的远程机器上使用 Docker 容器会怎么样呢?
在本教程中,我将尝试分享我是如何解决这个难题并使本地使用 Jupyter web 应用程序在远程机器上运行代码成为可能的经验。
我的工作环境
让我首先说明我的工作环境设置。
Own source, drawn using Google Draw
- 我用我的普通笔记本电脑作为主机。
- 我通过 ssh 连接到我部门的服务器(远程)。
- 然后,我连接到服务器中一台特定的机器,这台机器有 GPU。
- 最后,我启动了那台机器中的码头工人集装箱。
在我的例子中,我使用的机器是上图中标为 GPU 的机器,它恰好只能从服务器中的另一台机器访问。因此有了双 ssh……但是在你的情况下,你可以从外部世界直接连接到你的 GPU 机器。
这听起来很乱,但请耐心听我说,你只需要一劳永逸地组织起来。
先决条件
码头工人
确保在你的远程机器上安装了docker。如果你不熟悉 Docker,请查看一些可用的资源(我发现这个和这个很有帮助)。如果您的目标是 GPU 支持环境,请查看下一步。
GPU 支持
如果您的远程机器有 GPU,您可能想利用这一事实。确保你已经安装了 NVIDIA 驱动 。还需要安装 nvidia-docker 。请注意,通过安装 nvidia-docker,我们会自动安装最后一个稳定版本的 docker-ce ,因此您之前不需要显式安装 docker。
设置连接
主机—远程
Jupyter 笔记本运行在机器的某个端口上。因此,基本的想法是使该端口可以从您的主机到达。幸运的是, ssh 提供了-L 选项来指定端口转发。
$ ssh -L <host port>:localhost:<remote port> user@remote
在我的例子中,我在两端使用端口 9999,即 <主机端口> = <远程端口> = 9999。
远程— GPU
这里的方法和以前完全一样。
$ ssh -L <remote port>:localhost:<GPU port> user@GPU
再次,我用 <主机端口> = <远程端口> = 9999 。
GPU — docker
这一步略有不同。首先,您需要使用您喜欢的 docker 映像创建一个 docker 容器。在我的例子中,如上所述,我使用的是 ufoym/deepo 。因为我想要 GPU 支持,所以我将使用nvidia-docker
来创建容器。
$ nvidia-docker run -it \
-p <GPU port>:<container port> \
--name <container name> \
ufoym/deepo bash
注意选项-p
,它告诉 docker 容器进行端口转发。通过这种方式,我们可以从外部世界访问在某个端口上运行的应用程序。这里我也用 <主机端口> = <远程端口> = 9999 。
顺便说一下,创建容器时一个非常有用的选项是-v
,它允许您从 docker 容器中访问机器文件。
运行 Jupyter
一旦所有的隧道都建立好了,我们就可以启动我们的 jupyter 应用程序了。我们将使用0.0.0.0
ip 和创建 docker 容器实例时使用的相同端口。同样,在我的例子中,我使用了选项--allow-root
,因为我在我的容器中是 root,除非我使用这个选项,否则 Jupyter 不会运行。
$ jupyter notebook --ip 0.0.0.0 --port <container port> --allow-root
哦,如果你喜欢新的很酷的 jupyter lab ,就用下面的命令代替。
$ jupyter lab --ip 0.0.0.0 --port <container port> --allow-root
现在,在我的主机上,我只需简单地进入 localhost:9999 就可以了。
旁注
启用更多端口
如果您有一个类似的工作环境,我建议启用更多的远程访问端口。您可以使用-L <local port>:localhost:<remote port>
将它们添加到您的 ssh 命令中。这样,如果您碰巧在 docker 机器的某些端口上运行其他服务,您可以轻松地远程访问它们。
自动 ssh 登录
当您向 ssh 命令添加更多选项时,它会增加大小。一个选项是在~/.ssh/config
文件下定义一个主机。在我的例子中,我添加了一个用户<name>
:
Host <name>
Hostname <remote IP>
Port <remote port for SSH (typically 22)>
IdentityFile <path to rsa key>
LocalForward 9999 127.0.0.1:9999
有误差吗?
测试隧道是否工作的一个好方法是使用 python 中的http.server
。这样,您可以检查每个阶段端口是否被正确转发。使用以下命令在特定端口上运行简单的 http 服务器。
$ python -m http.server <remote port>
注意,这适用于 python3,对于 python2 版本,使用python -m SimpleHTTPServer <remote port>
代替。
如果你有任何其他困难,请在评论中留下,我很乐意帮助你!
使用 LDA 构建缺失的 Yelp 特征
如果你曾经在旧金山的金融区外出喝酒,你可能会偶然发现 Mikkeller 酒吧。这家丹麦啤酒馆是下班后和朋友一起去的好地方,这里的气氛非常活跃,足以进行精彩的交谈。有着丰富的名单和选择,这是我想经常去喝酒的地方。
现在,如果我在 Yelp 上搜索 Mikkeller,我不仅会得到酒吧本身,还会得到该地区类似简介的酒吧,如果我想尝试一个有类似氛围的地方,这很好。
但是,当您在不同的位置搜索时,这种类型的搜索不会转换。例如,如果我试图搜索芝加哥的 Mikkeller 酒吧:
虽然通过搜索特定的关键字(如 beer hall 或腹肌)可能会得到类似的结果,但我觉得应该实现更具体的搜索方法。为什么 Yelp 没有一个功能可以让用户在他们喜欢的栏上输入内容,然后在用户选择的位置上扩展搜索?这种搜索方法也能够捕捉到不太明显的方面,如环境。因此,我决定在我的第四个 Metis 项目中,我将使用无监督学习和 NLP 技术来构建这个功能。
数据
数据来源:
为了这个项目,我最终从 Yelp 挑战数据集中获得了我的数据。Yelp 提供了一个公开的数据集,这是一个无价的资源,因为 Yelp 的 TOS 是非常反网络抓取的。该数据集本身非常庞大,因为有超过 600 万条针对 800,000 多家不同企业的评论,分布在美国和加拿大的 10 多个不同的大都市地区。如此多的地理多样性是我的项目的额外收获,因为我希望它是位置不可知的。Yelp 的数据存储在。json 格式,所以我使用本地 MongoDB 实例查询和过滤数据。回想起来。json 文件非常大(评论文本 JSON 为 4.72 GB),如果我将所有内容都转移到一个大内存 AWS 实例中,那么这个项目的数据分析会更加高效。我的 Macbook Air 的 8 GB 内存不足以在不微观管理计算机资源的情况下对数据进行分析。
数据过滤:
将我的数据加载到 MongoDB 后,我进行数据处理的第一步是只过滤酒吧的企业列表。我的第一个方法(使用 pymongo )是执行一个正则表达式,匹配其类别类型中包含“bar”的任何业务。“酒吧”还必须有至少 50 个评论,以便有足够的文本数据进行推断:
# Create subset of business that contain bars in question
db.business.aggregate([
{"$match": {'categories': {'$regex': 'Bars'}}},
{"$match": {"review_count": {"$gte": 50}}},
{"$out": "bars"}]);
这种方法最终将搜索的企业数量从 180,000+减少到 5000+。然而,我很快意识到的一个怪癖是,regex 方法不仅捕获酒吧或体育酒吧等酒吧类型,还捕获寿司店。我最终变得更加严格,编写了两个函数来返回一个只包含特定类别(在本例中为“酒吧”)的企业:
def check_for_cat(categories, cat_type):
"""
Returns whether business is a given type, returns a boolean
"""
# splits input category list from json file
categories_list = categories.split(',')
# loop through each category to see if it is equal to cat_type
for category in categories_list:
# lowercase the category & strip any extra spaces
mod_cat = category.strip().lower()
if mod_cat == cat_type:
return True
return False
def filter_yelplist(locations, cat_type):
"""
Function that returns list of filtered yelp locations
"""
return [loc for loc in locations
if check_for_cat(loc['categories'], cat_type) == True]
这种额外的过滤给了我大约 4000+条。
接下来,我必须将每个酒吧的评论加入酒吧列表。通常在 SQL 数据库中,这可以通过一个简单 join 命令来完成。然而,MongoDB 有一个 NoSQL 数据库结构,所以我不能这样做(我用$lookup 命令做了实验)。我最终编写了以下查询来获取相关数据:
# Create subset of reviews that contain the bar ids
locations = list(db.bars.find({}, {'_id': 0, 'business_id': 1, 'categories': 1}))
filtered_bars = filter_yelplist(locations, 'bars')
allbar_ids = [i['business_id'] for i in filtered_bars]db.reviews.aggregate([
{'$match': {'business_id': {'$in': allbar_ids}}},
{"$out": "bar_reviews"}]);
在所有的数据处理之后,我最终得到了一个包含 4000 多个棒线的数据子集,每个棒线至少有 50 条评论。总共有 800,000 多条评论可供我分析。
文本处理
在过滤了数据之后,我仍然需要处理评论文本本身,然后将它传递给分词器。我做了三件事:
- 使用正则表达式删除数字和标点符号
- 将所有大小写类型转换为小写
- 将所有单词词条化
词汇化很重要,因为我不想让模型包含同一个单词的不同形式。词汇化完成这个任务是因为它返回单词的词汇或“词典形式”,所以给定单词的复数和分词形式被替换为基本形式。例如,“行走”的引理会是“行走”,而“更好”的引理会是“好”。
特征工程
我觉得酒吧的氛围非常重要,因此我在用户评论中使用了 NLP 来捕捉这种氛围。在尝试了不同的令牌矢量化工具和降维技术之后,我最终使用了计数矢量化和潜在狄利克雷分配(LDA)的组合来给我最大的可解释性。
…向量化…
在执行矢量化的过程中,我需要从评论文本中生成一个相关的标记列表。虽然 NLTK 提供了要排除的英语停用词的基本集合,但是在主题生成中还需要排除一些额外的标记。最终出现在排除列表中的令牌类型分为两类:与位置相关的和与食物相关的。为了使模型不可知位置,排除了与位置相关的标记,同时排除了与食物相关的标记,以便主题集中在饮料和环境上。就矢量器的类型而言,CountVectorizer 模型最终比 TFIDF 的性能更好。我相信 CountVectorizer 比 TFIDF 表现得更好,因为描述一个酒吧的重要词(如啤酒、音乐、饮料)会重复出现,CountVectorizer 最终会促进重复,而 TFIDF 则会惩罚它。
主题建模
通过使用 LDA,我能够执行主题建模并生成 9 个主题,我认为这些主题代表了我的数据集中不同类型的条形:
其中两个酒吧原型最终成为最具描述性的(户外和早午餐),因为大多数酒吧最终都被这两个主题所描述。这可以通过这两个主题如何映射到 TSNE 图来证明:
其余的 7 个主题最终变得更加具体,因为它们通常描述了数据集中的几个条形。举例来说,街机和视频游戏酒吧最终被映射到我的 TSNE 情节如下:
分类特征
除了 NLP 生成的特征之外,模型中还添加了一些有价值的分类特征。Yelp 数据集实际上包含许多分类特征:例如,一个地方是否适合跳舞,或者他们是否有电视可用。然而,我决定不利用这些特性,因为我想通过主题建模来确定这些特性的代理。因此,我最终直接从数据集中提取的唯一分类特征是企业的价格范围。
虽然主题建模让我了解了酒吧的氛围,但我也希望我的模型关注酒吧的主要酒精类型。为了做到这一点,我决定在评论中放大某些关键词的存在。例如,对于每个小节,我检测到标记“威士忌”被提及的次数。将该计数除以总评论数,我得到了一个比例,它表明了一个给定的酒吧有多少是威士忌酒吧。我最终在关键词上创建了这些存在类型分类特征:威士忌、伏特加、啤酒、葡萄酒、龙舌兰酒、朗姆酒、烈酒、杜松子酒、白兰地、烧酒、苹果酒和清酒。
推荐引擎
给出建议
随着要素的建立,我创建了一个管道来为我的数据集中的每个条生成要素。通过将输入条形图的余弦相似度与数据集中的所有其他条形图进行比较,给出了一个建议。如果有多个条形图作为输入,则使用输入条形图平均矢量的余弦相似度。这种相似性比较形成了我的推荐引擎的基础。基于数据集,以下是一些可能的建议:
Some recommendations if you are the TGIF’s type of person
If you are interested in Irish pubs there appears to be plenty of options
该推荐引擎随后通过 dashboard 应用程序和交互式 Tableau 仪表板部署在 Heroku 上,如下所示:
如果您想自己尝试一些建议,您可以在这里访问应用程序。
未来的工作
如果给我更多的时间来扩展这个项目,我会做一些改进:
- 更多相关数据:当前的数据集不包括旧金山或纽约州等主要都会区的酒吧位置,我认为这将有助于创建更相关的建议
- 链接交互图形:如果这个功能真的是通过 Yelp 或者类似的服务部署的,我觉得交互图形会是一个很大的卖点。因此,我希望链接 Tableau 仪表盘和 dashboard 界面,以便对最终用户进行更大程度的控制。
如果你想了解更多关于这个项目的信息,你可以在我的 Github repo 找到代码和数据。
用线性回归法模拟大学篮球比赛中的得分差距
显而易见,预测谁将赢得大学篮球赛的最简单的方法就是预测谁将获得更多的分数。维加斯簿记员预测的点差可以高度预测获胜概率,但有时点差不可用,例如当你为锦标赛整理数据并试图预测第二轮、第三轮和第四轮比赛的结果时。对于这些情况,这篇文章描述了三种预测分数分布的尝试:
- 试探法:一种线性模型,使用分析师根据直觉或探索性分析的结果选择的预测值。
- 双向逐步模型:一种线性模型,使用由算法选择的预测因子,该算法根据赤池信息标准(AIC)设计用于优化模型拟合。
- 最小角度回归:一种线性模型,使用数据集中的所有预测值,但对不同的预测值赋予不同的权重。这是一个与双向逐步相似的过程,但它不是从模型中删除预测值,而是降低其系数的权重。
这些模型是使用从 sports-reference.com 的下载的 351 支甲级队的游戏日志数据构建和测试的。训练数据由 2014–2017 年的游戏日志组成,模型在 2017–18 年的游戏日志上进行测试。
模型拟合
启发式模型
尝试的第一个模型是线性模型,使用从领域专业知识和探索性分析的组合中选择的预测器。这里考虑的所有数据都是差距——一个队与另一个队在某些事件(如盖帽)上的差异。说tov.spread = 5
意味着一个队比他们的对手多失误五次。
一个方便的探索工具是GGally::ggpairs()
绘图工具。它生成一个图形矩阵,显示数据集中每对变量之间的关系以及它们的相关性和分布。最下面一行显示了不同变量与点扩散的相互作用。
关注图的底部行,其显示了各种表现指标和分差之间的线性关系,投篮命中率、三分命中率、助攻、防守篮板和失误似乎与分差有很强的关系。
使用这些变量的线性模型是一个好的开始!调整后的 R 平方为 0.901 意味着我们已经解释了数据中 90%的方差。残差——单个预测值与其实际值之间的差异——似乎以零为中心,这很好。但当点差最大时,错误率会更高,这表明可能还有改进的空间。
也许更多的算法方法会产生更好的模型!
双向逐步
一种这样的算法方法是使用双向逐步变量选择的线性建模-一种迭代测试预测变量组合并选择最小化 AIC 的模型的方法。在每一次迭代中,新的变量被添加到模型中,模型被测试,如果变量不能提高拟合度,就被删除( ISLR )。
用这种方法选出的最佳模型包括 10 个变量。这次我们包括价差,但也包括三分球和罚球的数量以及进攻篮板的价差。
这个模型更好!它解释了点差变化的 95.5%。投篮命中率仍然是模型中最强的预测因素,但加入其他变量显然是有帮助的。
我们的残差仍然具有重尾分布,换句话说,当利差非常高或非常低时,我们的预测仍然不太准确。
到目前为止,我们尝试的每一个模型都是“贪婪的”,在某种意义上,预测变量被包括或不被包括在没有灰色区域的范围内。最终尝试的模型采用了不同的方法。
最小角度回归
最小角度回归(LAR)也是一种迭代预测值选择方法,但它不是包含或排除变量的二元决策,而是包含有助于提高模型拟合度的变量。
使用与目标具有最高相关性的变量来启动模型。然后调整预测变量的系数,直到另一个变量具有比它更高的相关性,此时第一个系数被锁定到下一个系数的运动中。重复该过程,直到找到最佳模型。如果变量的系数缩小到零,变量可以从模型中排除,但这个过程允许在决策中有更多的细微差别( ESLR )。
LAR 产生的变量和系数看起来比双向逐步方法更类似于启发式,更侧重于分布变量(A 队比 B 队多 5 次失误),而不是计数变量(A 队有 15 次失误,B 队有 10 次失误)。
至于模型拟合,LAR 正好介于其他两种方法之间。该模型解释了数据中 92%的差异。
模型评估
测试数据由 2017–2018 游戏日志组成,涵盖 3316 款游戏。我们的三个模型是根据误差项的分布以及平均绝对误差(MAE)来评估的。除了上述三个模型拟合,还测试了另外两个集合预测:
- 总体平均值—三次初始预测的平均值
- 总体中值-三个模型的预测中值
没有一个模型完美地反映了实际分数的分布,特别是在极端情况下。但是,这就是说,双向逐步看起来相当不错!在零附近有一个适当的倾斜,重尾描绘了实际点差分布的大致轮廓。
双向逐步回归也使 MAE 最小化,然后是总体平均值和中位数模型。
结论
双向逐步模型证明了样本外的准确性,并解释了数据中的最大差异。这显然是预测点扩散的最佳模型。
接下来,我计划使用点数分布作为分类模型的输入,来预测团队获胜的可能性。
测试集中的游戏日志于 2018 年 1 月 27 日下载
代码和数据可以在这里找到: github 。
使用 LSTMs 进行股市预测(Tensorflow)
在本教程中,你将看到如何使用一个被称为长短期记忆的时间序列模型。从设计上来说,LSTM 模型非常强大,尤其是在保留长期记忆方面,你将在后面看到。在本教程中,您将处理以下主题:
- 理解为什么你需要能够预测股票价格的变动;
- 下载数据——您将使用从 Alphavantage/Kaggle 收集的股市数据;
- 拆分训练测试数据,并执行一些数据标准化;
- 激发并简要讨论 LSTM 模型,因为它允许提前一步以上进行预测;
- 用当前数据预测和可视化未来股票市场
**注意:**但在我们开始之前,我并不主张 LSTMs 是一个高度可靠的模型,可以完美地利用股票数据中的模式,或者可以在没有任何人在回路中的情况下盲目使用。我这样做是作为一个实验,纯粹出于机器学习的兴趣。在我看来,该模型已经观察到了数据中的某些模式,从而使其能够在大多数时候正确预测股票走势。但是这个模型能不能用于实际用途,就看你自己了。
为什么需要时间序列模型?
您希望正确地模拟股票价格,因此作为股票购买者,您可以合理地决定何时买入股票,何时卖出股票以获取利润。这就是时间序列建模的用武之地。你需要好的机器学习模型,它可以查看数据序列的历史,并正确预测该序列的未来元素将会是什么。
警告:股票市场的价格是高度不可预测和不稳定的。这意味着数据中没有一致的模式可以让你近乎完美地模拟一段时间内的股票价格。不要相信我,相信普林斯顿大学的经济学家伯顿·马尔基尔,他在他 1973 年的书《漫步华尔街》中认为如果市场是真正有效的,股票价格一公布就立即反映了所有的因素,那么一只蒙着眼睛的猴子向报纸上的股票上市投掷飞镖应该和任何投资专业人士做得一样好。
然而,我们不要完全相信这只是一个随机或随机的过程,机器学习没有希望。让我们看看您是否至少可以对数据进行建模,以便您做出的预测与数据的实际行为相关联。换句话说,你不需要未来的确切股票价值,而是股票价格的变动(也就是说,它在不久的将来是上涨还是下跌)。
下载数据
您将使用来自以下来源的数据:
- 阿尔法优势。然而,在开始之前,你首先需要一个 API 密匙,你可以在这里免费获得。之后,您可以将该键分配给
api_key
变量。 - 使用本页中的数据。您需要将 zip 文件中的股票文件夹复制到您的项目主文件夹中。
股票价格有几种不同的风格。他们是,
- 开盘:当天的开盘价
- 收盘:当天的收盘价
- 高:数据的最高股票价格
- 低:当天最低的股票价格
从 Alphavantage 获取数据
您将首先从 Alpha Vantage 加载数据。因为您将利用美国航空公司的股票市场价格来进行预测,所以您将自动收报机设置为"AAL"
。此外,您还定义了一个url_string
,它将返回一个 JSON 文件,其中包含美国航空公司在过去 20 年中的所有股票市场数据,以及一个file_to_save
,它将是您保存数据的文件。您将使用预先定义的ticker
变量来帮助命名这个文件。
接下来,您将指定一个条件:如果您还没有保存数据,您将继续从您在url_string
中设置的 URL 获取数据;你将把日期、盘低、盘高、成交量、收盘价、开盘价存储到 pandas 数据框架df
中,然后保存到file_to_save
。但是,如果数据已经存在,您只需从 CSV 加载它。
从 Kaggle 获取数据
Kaggle 上的数据是 csv 文件的集合,您不必做任何预处理,因此您可以直接将数据加载到 Pandas DataFrame 中。确保将数据下载到项目主目录中。因此股票文件夹应该在项目主目录中。
数据探索
在这里,您将把收集的数据打印到数据框中。您还应该确保数据按日期排序,因为数据的顺序在时间序列建模中至关重要。
数据可视化
现在让我们看看你有什么样的数据。您需要随时间推移出现各种模式的数据。
这张图表已经说明了很多事情。我选择这家公司而不是其他公司的具体原因是,随着时间的推移,这张图表充满了不同的股价行为。这将使学习更加稳健,也给你一个机会来测试对各种情况的预测有多好。
另一个需要注意的是,接近 2017 年的数值要比接近 20 世纪 70 年代的数值高得多,波动也更大。因此,您需要确保数据在整个时间段内表现为相似的值范围。您将在数据标准化阶段处理这个问题。
将数据分成训练集和测试集
您将使用中间价,该中间价通过取一天中最高和最低记录价格的平均值来计算。
现在,您可以拆分训练数据和测试数据。训练数据将是时间序列的前 11,000 个数据点,其余的将是测试数据。
现在您需要定义一个 scaler 来规范化数据。MinMaxScalar
将所有数据缩放到 0 和 1 的范围内。您还可以将训练和测试数据整形为形状[data_size, num_features]
。
根据您之前的观察,即不同时间段的数据具有不同的值范围,您可以通过将整个系列拆分为多个窗口来规范化数据。如果不这样做,早期的数据将接近于 0,不会给学习过程增加太多价值。这里你选择一个大小为 2500 的窗口。
提示:当选择窗口大小时,确保它不要太小,因为当你执行窗口归一化时,它会在每个窗口的最后引入一个断点,因为每个窗口都是独立归一化的。
在本例中,4 个数据点将受此影响。但是假设你有 11,000 个数据点,4 点不会引起任何问题
将数据重塑回[data_size]
的形状
现在,您可以使用指数移动平均来平滑数据。这有助于你摆脱股票价格中数据固有的混乱,并产生一个更平滑的曲线。
**注意:**我们仅使用训练数据训练最小最大缩放器,通过将最小最大缩放器拟合到测试数据来标准化测试数据是错误的
注意你应该只平滑训练数据。
评估结果
我们将使用均方差来计算我们的模型有多好。均方误差(MSE)可以通过计算前一步的真实值和预测值之间的平方误差,并对所有预测进行平均来计算。
平均作为股票价格建模技术
在原始教程中,我谈到了对于这种类型的问题,欺骗和糟糕的平均是如何的。结论是,
平均法可以很好地预测一个时间点(这对于股市预测来说不是很有用),但对于未来却没有多少时间点。更多详情可以在原创教程中找到。
LSTMs 简介:预测遥远未来的股票走势
长短期记忆模型是非常强大的时间序列模型。他们可以预测未来的任意步数。一个 LSTM 模块(或细胞)有 5 个基本组成部分,使其能够模拟长期和短期数据。
- 细胞状态(c _ t)——这代表细胞的内部记忆,它既存储短期记忆又存储长期记忆
- 隐藏状态(h_t) —这是根据当前输入、先前隐藏状态和当前单元格输入计算的输出状态信息,您最终使用这些信息来预测未来的股票市场价格。此外,隐藏状态可以决定仅检索存储在单元状态中的短期或长期或两种类型的记忆来进行下一次预测。
- 输入门(i_t) —决定有多少信息从电流输入流向单元状态
- 遗忘门(f_t) —决定有多少来自当前输入和前一单元状态的信息流入当前单元状态
- 输出门(o_t) —决定有多少信息从当前单元状态流入隐藏状态,这样,如果需要,LSTM 只能选择长期记忆或短期记忆和长期记忆
下图是一个单元格。
并且用于计算这些实体中的每一个的等式如下。
- I*_ t =σ(W*{ IX } * x*_ t+W*{ ih } * h _ { t-1 }+b _ I)
- \ tilde { c }_ t =σ(W{ CX } * x*_ t+W*{ ch } * h _ { t-1 }+b _ c)
- f*_ t =σ(W*{ FX } * xt+W{ FH } * h _ { t-1 }+b _ f)
- c _ t = f*_ t * c*{ t-1 }+I _ t * \波浪号{c}_t
- o*_ t =σ(W*{ ox } * xt+W{ oh } * h _ { t-1 }+b _ o)
- h_t = o_t * tanh(c_t)
为了更好地(更技术性地)理解 LSTMs,你可以参考这篇文章。
数据生成程序
下面,您演示了如何可视化地创建一批数据。基本思想是,我们将数据序列分成 N/b 个段,使每个段的大小为 b,然后我们定义游标,每个段 1 个。然后对单批数据进行采样,我们得到一个输入(当前段游标索引)和一个真预测(在[当前段游标+ 1,当前段游标+ 5]之间随机采样的一个)。请注意,我们并不总是得到输入旁边的值,就像它的预测一样。这是减少过度拟合的一个步骤。在每次采样结束时,我们将光标增加 1。你可以在原教程中找到更多关于数据生成的信息。
定义超参数
在本节中,您将定义几个超参数。D
是输入的维数。这很简单,因为你把前一个股票价格作为输入,预测下一个,应该是1
。
然后你有num_unrollings
,表示你为一个优化步骤考虑多少个连续的时间步骤。越大越好。
然后你就有了batch_size
。批量大小是指您在一个时间步长中考虑的数据样本数量。越大越好,因为在给定的时间内,数据的可见性更高。
接下来定义num_nodes
,它代表每个细胞中隐藏神经元的数量。您可以看到,本例中有三层 LSTMs。
定义输入和输出
接下来,为训练输入和标签定义占位符。这非常简单,因为您有一个输入占位符列表,其中每个占位符包含一批数据。列表中有num_unrollings
占位符,将在单个优化步骤中立即使用。
定义 LSTM 和回归图层的参数
你将有一个三层的 LSTMs 和一个线性回归层,用w
和b
表示,它获取最后一个长短期记忆单元的输出,并输出下一个时间步的预测。您可以使用 TensorFlow 中的MultiRNNCell
来封装您创建的三个LSTMCell
对象。此外,您可以让 dropout 实现 LSTM 单元,因为它们可以提高性能并减少过拟合。
计算 LSTM 输出并将其馈送到回归层以获得最终预测
在本节中,首先创建 TensorFlow 变量(c
和h
),这些变量将保存长短期记忆单元的单元状态和隐藏状态。然后你把train_inputs
的列表转换成[num_unrollings, batch_size, D]
的形状,这是用tf.nn.dynamic_rnn
函数计算输出所需要的。然后用tf.nn.dynamic_rnn
函数计算 LSTM 输出,并将输出拆分回一个num_unrolling
张量列表。预测和真实股价之间的差距。
损失计算和优化程序
现在,你将计算损失。但是,您应该注意,在计算损失时有一个独特的特征。对于每批预测和真实输出,计算均方误差。你将所有这些均方损失相加(不是平均)。最后,您定义将用于优化神经网络的优化器。在这种情况下,您可以使用 Adam,这是一个非常新的、性能良好的优化器。
在此定义与预测相关的张量流运算。首先,为输入定义一个占位符(sample_inputs
),然后类似于训练阶段,为预测定义状态变量(sample_c
和sample_h
)。最后用tf.nn.dynamic_rnn
函数计算预测,然后通过回归层(w
和b
)发送输出。您还应该定义reset_sample_state
操作,它重置单元格状态和隐藏状态。每次进行一系列预测时,都应该在开始时执行这个操作。
运行 LSTM
在这里,您将训练和预测几个时期的股票价格运动,并查看预测随着时间的推移是变好还是变坏。您遵循以下程序。我不分享代码,因为我分享的是完整 Jupyter 笔记本的链接。
*在时间序列上定义一组测试起点( *test_points_seq*
),以评估上的模型
*对于每个时期
** 训练数据全序列长度
** * 展开一套
*num_unrollings*
批次** * 用展开的批次训练神经网络
** 计算平均培训损失
** 对于测试集中的每个起始点
** * 通过迭代在测试点之前找到的先前
*num_unrollings*
数据点来更新 LSTM 状态** * 连续对
*n_predict_once*
步骤进行预测,使用之前的预测作为当前输入** * 计算预测的
*n_predict_once*
点与该时间点的真实股价之间的 MSE 损失
可视化预测
你可以看到 MSE 损失是如何随着训练量的增加而下降的。这是一个好迹象,表明模型正在学习一些有用的东西。为了量化您的发现,您可以将网络的 MSE 损失与进行标准平均(0.004)时获得的 MSE 损失进行比较。你可以看到 LSTM 的表现比标准平均值要好。你知道标准平均法(虽然不完美)合理地跟随真实的股票价格运动。
虽然不完美,LSTMs 似乎能够在大多数时候正确预测股票价格行为。请注意,您做出的预测大致在 0 到 1.0 的范围内(也就是说,不是真实的股票价格)。这没关系,因为你预测的是股票价格的变动,而不是价格本身。
结论
我希望这篇教程对你有用。我应该提到这对我来说是一次有益的经历。在本教程中,我了解到设计一个能够正确预测股价走势的模型有多困难。你从为什么需要对股票价格建模的动机开始。接下来是解释和下载数据的代码。然后你看了两种平均技术,它们可以让你对未来进行预测。接下来你会看到,当你需要预测未来不止一步的时候,这些方法是没有用的。此后,您讨论了如何使用 LSTMs 对未来进行预测。最后,您将结果可视化,并发现您的模型(尽管并不完美)在正确预测股票价格变动方面非常出色。
在这里,我陈述几个本教程的要点。
- 股票价格/运动预测是一项极其困难的任务。就我个人而言,我不认为任何股票预测模型是理所当然的,也不应该盲目依赖它们。然而,模型可能在大多数情况下能够正确预测股票价格的变动,但并不总是如此。
- 不要被那些显示预测曲线与真实股价完美重叠的文章所迷惑。这可以用一个简单的平均技术来复制,但实际上这是没有用的。更明智的做法是预测股价走势。
- 模型的超参数对您获得的结果极其敏感。因此,一个非常好的做法是对超参数运行一些超参数优化技术(例如,网格搜索/随机搜索)。这里我列出了一些最关键的超参数;优化器的学习速率,层数和每层的隐藏单元数,优化器(我发现亚当表现最好),模型的类型(GRU/LSTM/带窥视孔的 LSTM)
- 在本教程中,你做了一些错误的事情(由于数据量小)!那就是你利用考试损失来降低学习速度。这间接地将关于测试集的信息泄露到训练过程中。一个更好的处理方法是拥有一个单独的验证集(与测试集分开)和关于验证集性能的衰减学习率。
这里
参考
我参考了这个库来了解如何使用 LSTMs 进行股票预测。但是细节可能与参考资料中的实现有很大不同。
想在深度网络和 TensorFlow 上做得更好?
检查我在这个课题上的工作。
[1] (书)张量流 2 在行动——曼宁
[2] (视频教程)Python 中的机器翻译 — DataCamp
[3] (书)TensorFlow 中的自然语言处理 1 — Packt
利用 LSTMs 预测时间序列
Photo by Nick Chong on Unsplash
有几种时间序列预测技术,如自回归(AR)模型、移动平均(MA)模型、霍尔特-温特斯、ARIMA 等。,不一而足。那么,还有什么必要用另一个像 LSTM-RNN 这样的模型来预测时间序列呢?这是一个非常合理的问题,以下是我能想到的理由(如果你知道更多,我很想知道,请在下面回答)—
- RNN(LSTM)非常擅长在输入特征空间中提取模式,其中输入数据跨越长序列。鉴于 LSTM 的门控结构具有操纵其记忆状态的能力,它们是解决这类问题的理想选择。
- LSTMs 几乎可以无缝地模拟具有多个输入变量的问题。我们需要的只是一个 3D 输入向量,它需要被输入到 LSTM 的输入形状中。只要我们找到一种方法,将所有的输入变量转换成三维矢量形式,我们就可以很好地使用 LSTM。这在时间序列预测中增加了很大的好处,在这种情况下,经典的线性方法可能很难适应多变量或多输入预测问题(此处对多变量预测的补充说明—请记住,当我们使用多变量数据进行预测时,我们还需要“未来多变量”数据来预测未来的结果!)
- 总的来说,在使用 LSTM 的方法时,我发现它们在建模问题时提供了很大的灵活性——这意味着我们可以很好地控制时间序列的几个参数。特别是我们可以—
- 灵活使用 seq2seq LSTM 模型的多种组合来预测时间序列——多对一模型(当我们希望在给定所有先前输入的情况下预测当前时间步长时有用),多对多模型(当我们希望在给定所有先前输入的情况下一次预测多个未来时间步长时有用)以及这些模型的其他几种变体。我们可以定制一些东西,例如,在当前步骤进行预测的回顾窗口的大小,我们希望预测未来的时间步骤的数量,将当前预测反馈到窗口中以在下一个时间步骤进行预测(这种技术也称为向前移动窗口)等等。
另一方面,在使用 LSTM(或任何 DNN 架构)时,有一些常见的缺点需要小心——需要大量数据,需要调整多个超参数等。,我也看到一些文章提到 LSTM 并不擅长自回归类型的序列。所以不要全信这个。
使用一个简单的正弦波作为模型数据集来模拟时间序列预测。你可以在我的 github 简介中找到我自己对这个例子的实现。这个例子的核心思想和数据取自的博客,但为了便于理解,我做了自己的修改。
那么我们给定的数据是什么样的呢?下面是整个正弦波数据集的曲线图。
fig 1: Plot of entire sine wave data
在我们深入研究细节之前,先简要介绍一下整体方法—
- 使用大小为 50 的向前移动窗口,这意味着我们将使用前 50 个数据点作为输出输入 X 来预测 y1-第 51 个数据点。接下来,我们将使用 1 到 51 个数据点之间的窗口作为输入 X 来预测 y2,即第 52 个数据点,依此类推…这是前 50 个数据点的图—
2.使用两层 LSTM 架构和密集输出层进行预测。
3.我们将查看预测输出的几种方法,a .)逐步预测测试数据集, b.) 通过向前移动一步,将先前的预测反馈到输入窗口,然后在当前时间步进行预测。
现在让我们深入细节—
数据准备—
- 使用 minmax scaler 标准化数据(参见下面的代码片段)
2.将移动窗口大小固定为 50。为此,我们使用 pandas shift 函数,按照我们指定的数字移动整个列。在下面的代码片段中,我们将列 向上 移动了 1(因此使用了-1。如果我们想将 向下 移动 1,我们必须使用+1),然后将其连接到原始数据。
我试图在下面的一个玩具数据集上说明这一点,上面的循环 的 是如何为 3 的窗口大小工作的。**
注意——在上面的代码片段中,我们删除了所有包含 Nan 值的行。
如果仔细观察玩具数据集,您会发现它以我们希望输入到 LSTM 中的方式模拟了输入数据。上表中的最后一列成为目标 y ,前三列成为我们的输入 x1、x2 和 x3 特征。如果你熟悉在自然语言处理中使用 LSTM,那么你可以把它看作是一个长度为 3 的句子的固定序列,每个序列包含 3 个单词,我们的任务是预测第 4 个单词。
3.为 LSTM 准备 3D 输入矢量。记住,LSTM 的输入向量是 3D 数组:(样本数,时间步数,特征数)。在这种情况下,我们有时间步数= 50* 和数量 _ 特征= 1 (扩展我们在上一点中看到的相同类比,我发现这在理解为什么输入形状必须是这样非常有用——比方说,我们在一个句子中有 50 个单词,每个单词由一个单词向量表示。因此,我们需要 50 个时间步长来遍历句子中的每个单词向量,作为每个时间步长的 LSTM 的输入。每个观察有一个句子,因此 num_features = 1 。像这样,我们需要迭代训练数据中的所有句子,以提取所有句子中单词之间的模式。这也正是我们在时间序列预测中想要的—我们想要识别窗口中每个先前值之间存在的所有模式,以预测当前时间步长!)*
模型架构—
下面是所使用的模型架构,这一点不言而喻—(它是一个双层堆叠的 LSTM 层,第一个 LSTM 在每个时间步的输出被馈送到第二个 LSTM)
Model architecture
做预测—
- 逐步预测测试数据(参考下面的代码片段)。这很简单。给定从训练数据中学习到的所有参数,我们使用它们来一次一个地预测所有的测试序列。
**预测值与实际值的曲线几乎相互重叠,以至于我们无法区分下图中的蓝色曲线和红色曲线。
然而,以上通常不是进行预测的现实方式,因为我们不会有所有可用的未来窗口序列。
2.因此,如果我们想要预测未来的多个时间步长,那么更现实的方法是一次预测未来的一个时间步长,并将该预测反馈到后面的输入窗口,同时在窗口开始时弹出第一个观察值(以便窗口大小保持不变)。参考下面做这部分的代码片段—(如果你浏览我上面提到的 github 链接中的代码,代码中的注释是不言自明的)—
使用这个预测模型,结果绘制如下—
可以看出,可以理解的是,我们试图预测的时间越远,在每个时间步上建立在先前预测误差上的误差就越大。然而,该函数的行为仍然像一个阻尼正弦波!正如我前面说过的,这是任何时间序列问题的更现实的模型,因为我们没有所有的未来序列。
这个代码可以很好地扩展到预测任何时间序列。请注意,您可能需要注意数据准备的其他方面,如在将数据提供给 LSTM 进行预测之前,对序列进行去趋势化、差分以使数据稳定等。
就是这样!希望这篇文章对使用 LSTM 预测时间序列有一个很好的理解。如果这篇文章中有一些摘录,请鼓掌表示感谢:)
使用机器学习模型推荐航空公司——第一部分
Photo by Gary Lopater on Unsplash
机器学习是任何数据科学家都必须拥有的一项技能,但这并不是一项容易获得的技能。我是一名训练有素的物理学家,在欧洲核子研究中心和 T2 BNL 等实验室做过物理研究,数据分析对我来说很自然。但是,我不得不承认,当我想为机器学习解渴的时候,我纠结了!在这篇博客中,我的目的是通过一个真实的例子来展示机器学习的力量,从而帮助任何数据科学爱好者。
问题形式化
目标是根据数据集中提供的信息预测航班是否会被取消。该机构只能出售三家航空公司(AA、UA 和 DL)的机票,并且希望能够告知其客户哪家航空公司的取消最少。
当你在旅行中寻找一个可以休息的地方时,酒店评论在预订住宿时相当重要。为什么,因为你至少会在一个特定的地方住一晚,舒适和预算是非常重要的。然而,在预订航空公司时,人们的观点大相径庭。我个人想预订最便宜的航班,而不去关注评论。原因很简单,如果我在国内旅行,我只会花几个小时。这在假期会变得特别棘手,因为大多数人都会去见家人。至少在美国,当你带着孩子旅行并且你的航班延误时,感恩节和圣诞节会是一个非常紧张的时刻。机器学习算法如何在这样的场景中派上用场?我的目的是证明这一点。
我学了很多课程来学习机器学习。我将在这里总结一些最有用的材料。吴恩达在 coursera 上的机器学习;在 udemy 上进行深度学习的 tensorflow;和这本书是我学习这套技能的旅程中最有用的垫脚石。
我在这个博客中的大部分工作都是受威尔·科尔森的 GitHub 资源库的启发。请参考我的 GitHub 资源库,完整了解这篇博客中定义的问题,以及这里使用的数据集。
关于数据集的信息
Figure I: Data set information
首要任务是确定我们正在处理什么类型的问题?基于上面的问题定义和数据集信息,这可以被归类为监督分类类型的问题。
这是一个监督问题,因为数据集包含预期结果的例子,也称为标签,这是一个分类类型问题,因为结果是二元的。代理商希望根据对未来取消率的预测来推荐某家航空公司。
好了,现在我们已经定义了我们正在处理的问题的类型,在我们深入数据分析部分之前,让我们首先建立一个机器学习清单。清单帮助我对手头的问题有更全面的了解,并保持专注。
机器学习项目清单
- 定义问题,看大局。
- 执行探索性数据分析(EDA)以获得洞察力。
- 清理和准备数据,以更好地揭示其中的潜在模式。
- 探索许多不同的机器学习模型,并选择最佳模型。
- 执行模型交叉验证,以确保分析是稳健的。
既然我们已经定义了更大的图片,让我们从将数据加载到 pandas 数据框中开始,并查看前五条记录。
Using pandas library for data manipulation
数据类型和缺失值
dataframe.info()方法是一种快速评估数据的方法,它显示每一列的数据类型和非缺失值的数量。查看下面的输出,这是一个相对容易分析的数据集,因为这里没有重大的数据质量问题。在给定的数据集中有 6000 个实例,只有四列缺少一些数据值。在这种情况下,我们将直接进入数据的探索性分析。
探索性数据分析
EDA 是一种数据分析的图形化方法,允许我们最大限度地洞察我们的数据集;发现潜在模式;启用特征提取;发现异常值和异常情况;并测试潜在的假设。
单变量分析
让我们首先分析“距离”变量,以获得关于我们拥有什么类型的航空公司数据的信息,国内的还是国际的。
Python method for plotting a variable using matplotlib library.
Figure 2: Distribution of distance as observed in the data set.
上面的图 2 显示了一个偏离正态分布的分布。它是一种概率分布,描述了变量的值是如何填充的。这种分布是对称的,其中大部分观察值聚集在中心峰值周围,并且距离平均值较远的值的概率在两个方向上逐渐变小。大自然的大部分现象都倾向于遵循正态分布。例如,身高、血压测量误差、智商得分等…
距离不遵循正态分布是不足为奇的,然而,图 2 显示了大于大约 3000 英里的距离的非常稀疏的值。还建议将对数标度图可视化,以便能够评估距离中的异常值。假设这些数据仅对应于美国境内的国内航班,那么谨慎地移除可能导致对航班取消的有偏见和/或不正确预测的数据点是很重要的。很难解释飞行距离约为 10000 英里的国内航班。上面的“距离”图显示了一个带有长尾的朗道(向右倾斜的分布)型分布。同样,假设这些航班仅是国内航班,下一步是想出一个策略来删除这些数据点。我再怎么强调也不为过,仅仅因为数据点看起来可疑,就在删除任何数据点时保持谨慎是极其重要的。它们可能是需要进一步调查的实际现象的结果。
移除异常值
对于这个项目,我使用了一种标准的物理方法来消除异常值,在这种情况下,这是平均值周围的三个标准偏差。
Figure 4: Distribution of flight distance after dropping values greater than three standard deviations.
查看数据点是否确实被删除。
当去除大于三个标准偏差的距离观察值时,大约 1%的数据点被丢弃。
由于有限的统计数据导致的偏差
我想提醒我的读者,我们正在处理的数据集只有非常有限的统计数据,只有 6000 个实例。请记住,从下面的分析中推断出的结论可能会有偏差。由偏差引起的误差被定义为我们的模型的预期预测和我们试图预测的正确值之间的差异。最小化这种偏差的最好方法是多次重复整个模型建立过程;在每次迭代中,您运行新的分析,在新收集的数据集上创建新的模型。由于基础数据集中的随机性,生成的模型将具有一系列预测。偏差衡量的是这些模型的预测与正确值的差距。
最常用的航空公司
Figure 5: Most used airline carrier.
上面的图显示,在给定的数据集中,美国航空公司是使用最多的航空公司,但它是最可靠的航空公司吗?
最可靠的航空公司
现在,我们已经很好地理解了手头的数据集,让我们进行分析,以确定最可靠的航空公司。
Figure 6: Flight cancellation rate for individual carriers.
下面我简单地绘制了图 6 中获得的取消率,显示在图 7 中。
Figure 7: Usage and reliability for each airline carrier.
- 根据数据集,上述数字不足为奇,AA 航空公司是使用率最高的航空公司,使用率为 41%,相比之下,UA 为 30%,DL 为 28%。
- AA 航空公司未被取消的可靠度为 80%,而 UA 的可靠度为 81 %, DL 航空公司的可靠度约为 90%。
**所以达美航空是这个数据集中最靠谱的航空公司。**借助 EDA,我们能够证明达美航空是最可靠的航空公司。我们现在可以使用机器学习模型来预测这家航空公司未来的取消率。
双变量分析
图 8(下图)显示了该数据集中不同变量之间的相关矩阵。也称为热图,是一个强大的工具,可以直观地显示我们目标标签背后引人注目的特征。
Figure 8: Correlation matrix between different variables present in this data set.
图 8 中的外卖信息:
- 目标标签“已取消”与“月”、“SchedElapsedTime”、“ArrDelay”、“DepDelay”和“距离”呈负相关。
- 取消列不依赖于“出发时间”。
- 到达延误(ArrDelay)发生在有出发延误(DepDelay)的时候。这两个变量是高度相关的,这是理所应当的!
- 鉴于给定数据集中的信息,航班取消可能会因技术故障而发生,但我们没有可用的信息。所以我们将使用“月”列。
下图 9 展示了每个航空公司每月的使用情况。除了 12 月份,美国航空公司仍然是最常用的承运人。
Figure 9: Month-wise usage for each airline carrier.
类似地,可以绘制每个航空公司每月的取消百分比。重要的是要看相对百分比值,而不是看绝对数字。有趣的是,我们发现 AA 在四月份的使用率最高。也许他们在那个月提供最便宜的票价?从 1 月到 4 月,AA 的取消趋势上升,而其他两家航空公司的趋势相反。尽管如此,除了 6 月、7 月、9 月和 11 月,达美航班取消的可能性最低。
Figure 10: month-wise cancellation percentage for each airline carrier.
有了上述所有准备,我们现在将更深入地模拟未来航班取消的预测。请参考我博客的第二部分。
使用机器学习模型推荐航空公司——第二部分
Photo by Andrew Palmer on Unsplash
在使用机器学习模型解决问题时,没有什么灵丹妙药。我还没有见过一种模式适合所有人。因此,强烈建议对您可能正在处理的任何问题使用多个模型,并评估为什么一个特定的模型比其他模型更好。请参考我博客的第一部分,了解所用数据集的详细信息。
在这篇博文中,我的目的是展示如何使用几个机器学习模型找到一个解决方案,为最可靠的航空公司推荐未来的取消率,并讨论每个模型的利弊。
创建训练和测试数据集
Figure 1: Creating test, train data sets using train_test_split method in sci-kit learn.
对于手头的分类问题,我将使用以下模型:
- 逻辑回归
- 支持向量机
- 决策图表
- 随机森林
L **逻辑回归:**与其名称相反,逻辑回归用于因变量为二分变量(二元/分类)的分类类型问题。计算感兴趣特征出现的概率。
logit§ = ln(特性存在的概率/特性不存在的概率)
逻辑回归中的估计不是选择最小化线性回归中误差平方和的参数,而是选择最大化观察样本值可能性的参数。
Figure 2: Logistic regression. Source
Figure 3: Logistic regression. Source
图 4 展示了如何实例化逻辑回归类,以及如何在自变量(X_train)和因变量(y_train)上使用“fit”方法。我们得到了大约 83%的准确率。这是一个相当低的准确度分数,让我们试着理解为什么这个模型的性能如此之差。
Figure 4: Using logistic regression model training data set.
Figure 5: Applying logistic regression model on test data set.
在上面的图 5 中,我们在测试数据集上获得了大约 84%的准确率。下面的相关矩阵图(图 6)显示,逻辑回归模型在预测取消时没有考虑“月”列。这也许解释了为什么这个模型的性能如此之差。
Figure 6: Driving factor behind poor performance of logistic regression model.
利弊:
- 逻辑回归是一个广义的线性模型,所以我们不能用这个模型解决非线性问题——不管是赞成还是反对,这取决于你试图解决的问题的类型。
- 由于参数和输入向量的线性组合,计算非常简单。
- 输出可以解释为概率;所以你可以用它来排名而不是分类。
- 树木通常很难计算出精确的概率。
- 遭受多重共线性。
支持向量机:SVM 是一个非常强大和通用的最大似然模型,能够解决线性以及非线性分类,回归甚至异常检测类型的问题。是 ML 中最受欢迎的型号之一。缺点是 SVM 只适用于中小型数据集。
Figure 7: Support vector machines aka large margin classification.
SVM 分类器试图在不同类别之间拟合最宽的可能街道(由图 7 中的蓝色矩形表示)。这就是所谓的大幅度分类。图 7 左侧的黑线很好地将两个类别分开,但是没有最大的余量(也称为街道/超平面),如在右手侧。请注意,在“街道之外”添加更多实例根本不会影响决策边界。决策边界完全由街道边缘的实例决定。这些实例被称为支持向量,因此命名为支持向量机。
Figure 8: Instantiating the SVC class.
由于我们使用的是一个相当小的数据集(只有 6000 个实例),计算相对来说并不费力。SVM 模型的精确度约为 99.9%。
与逻辑回归不同,SVM 分类器不会输出每个类别的概率。
缺点:
- 训练这种模型可能会非常低效。不建议用于大型数据集。
- 支持向量机对特征缩放很敏感,与数据集中的其余特征相比,具有小得多的缩放比例的特征将在大幅度确定中被忽略。
D 决策树:与 SVM 相似,决策树是非常通用的 ML 算法,可以执行分类和回归任务。这是一个流程图类型的算法,其中每个决策节点后面都有一个结果。
Figure 9: Using decision tree model.
Figure 10: Simple decision tree.
利弊:
- 决策树的众多特性之一是它们需要很少的数据准备。它们根本不需要特征缩放或居中。
- 该模型易于解读、解释、易于使用、功能多样且功能强大。
- 决策树还可以估计某个实例属于某个特定类 k 的概率:首先,它遍历树来查找该实例的叶节点,然后返回该节点中类 k 的训练实例的比率。
- 决策树最大的缺点是它们对训练数据的微小变化非常敏感。他们倾向于过度拟合数据。
我有一种不可思议的感觉,100%准确的决策树过度拟合了这里的数据。
andom Forest:是一种集成学习算法。一组预测器称为集合;这项技术被称为集成学习。这里训练一组决策树分类器,每个分类器基于训练集的不同随机子集。为了进行预测,我们可以获得所有单个树的预测,然后预测获得最多投票的类。这样的决策树集合被称为随机森林。
随机森林模型的成功可以用大数定律来解释。最简单的解释是:拿一个装有 100 枚金币的盒子。在任何给定的时间点,期望值大约是 50 头和 50 尾。但是,我摇一次箱子,可能得不到预期的值。但是诀窍是通过摇动盒子来进行合理的大量观察,使得头和尾的数量的平均值接近期望值。
随机森林不需要纯粹基于决策树作为唯一的预测器。可以有一组不同的分类器,并预测得到最多投票的类别,如下图 11 所示。
Figure 11: Diverse set of classifiers. Source.
图 12 显示了随机森林分类器是如何实例化的。
Figure 12: Random forest classifier.
利弊:
- 与决策树相比,随机森林不是一个容易直观解释的模型。
- 易于在可能具有相关特征的数据集上使用。
摘要
在这篇博文中,我展示了各种 ML 模型的使用,以及它们的优缺点。大家 ML 快乐!
使用机器学习为 Google Home 构建对话式放射学助手
介绍
一段时间以来,我一直对对话代理感到兴奋,之前我构建了一个 iOS 聊天机器人,模拟由沃森驱动的人类放射科医生。
作为一个令人愉快的周末项目,我和我的光荣的柯基犬坐下来,喝了很多咖啡,为谷歌主页制作了一个放射学助手。它旨在以快速、对话、免提的方式帮助医疗保健提供者满足其放射学需求。一些视频演示:
智能扬声器是一项革命性的技术,像 Google Home 和 Amazon Echo 这样的工具有潜力为医疗保健提供巨大的价值。这项技术强大而有趣,原因如下:
- 方便查看和联系我们的患者。患者讨厌医生持续盯着电脑或平板电脑,这种行为有损医患关系。智能扬声器有助于绘制数据和获取信息,同时继续与患者进行眼神交流。
- 无菌的需要。在无菌手术室中访问信息很麻烦,外科医生基本上无法使用标准的桌面/智能手机技术。智能音箱很好地解决了这个问题。
- 独有的低摩擦信息检索。医疗保健提供商面临着不堪重负的工作负载,无缝访问即时信息非常重要。当然,我可以花 3 分钟查找 Fleischner 标准,但是如果我可以简单地“询问房间”并在 5 秒钟内得到答案,那就好得多了。此外,新的 Google Home 应用程序可以立即使用,不需要单独下载和管理 100 个分散的应用程序。
- 交谈的力量。我们天生就被编程为通过对话与世界互动,精心制作的对话软件非常直观且易于使用。这对于不喜欢传统图形用户界面的老年患者/医生尤其有用。
您现在已经理解了为什么这个平台很酷,但是我们的放射科助理到底能做什么呢?当前功能的示例包括:
- 偶发病变随访建议(例如肺结节 Fleischner 标准
- 对比过敏预处理方案
- 大小阈值信息(例如肥厚性幽门狭窄的阈值)
- 临床评分计算器(例如 MELD 评分)
在本文中,我将向您详细描述这个 Google Home 工具是如何构建的。
构建对话式放射学助理
步骤 1:使用 DialogFlow 理解语言
首先,我向您提交一个令人难以置信的图表,它提供了应用程序中信息流的概述:
When a user queries the Google Home device, natural language processing is performed by DialogFlow to understand the user input and parse it into useful variables of interest. These variables are passed to a webhook for further processing. Following webhook processing, information is returned to DialogFlow and used to create the Google Home output message.
要构建这种工具,首先需要强大的自然语言处理来理解用户输入,并将其分解成有意义的块。这可以通过使用 Google 提供的服务 DialogFlow 来实现(通过收购流行的 API)。AI)。
在 DialogFlow 中,功能模块被称为意图。还记得应用程序如何为肺结节提供 Fleischner 随访建议吗?这就是肺结节的意图。同样,也有计算 MELD 评分、对比过敏预处理和幽门正常尺寸的意图。看看所有这些美妙的意图:
Intents!
但是程序如何获取给定的用户输入并将其与适当的意图配对呢?它能做到这一点,是因为训练数据让它变得聪明。也就是说,我们提供一堆用户可能会说的事情的例子来触发特定的意图——我们提供的越多,人工智能就越聪明——程序“学习”正确识别感兴趣的意图。下面是一个用于训练“MELD 分数”意图的数据类型的示例,这只是用户可能询问 MELD 分数的一系列方式:
所以,我们现在明白了程序是如何确定用户到底在问什么。DialogFlow 执行的下一个重要的 NLP 任务是将用户输入解析成可以理解和处理的有意义的块。
以肺结节评估为例。为了提供 Fleischner 建议,我们需要关于结节的四位信息:(1)结节特征(实性、部分实性、毛玻璃),(2)结节大小,(3)患者风险水平(低或高),以及(4)结节数量(单个或多个)。在 DialogFlow 中,像这样的数据位称为实体,我们为这四个变量中的每一个变量创建一个单独的实体(从技术上讲,我们使用通用的“单位长度”实体来表示结节大小,但在本讨论中可以忽略)。它最终看起来有点像这样:
DialogFlow 很酷的一点是,它提供了智能的自然语言处理,可以在给定适当的训练数据的情况下,自动从任意句子中提取感兴趣的实体。这意味着,当用户说“我在一个高风险患者中有一个 6 毫米的固体肺结节”时,DialogFlow 会自然地解析数据,并将结节大小(6 毫米)、结节特征(固体)和患者风险(高)存储为可用变量。在这种情况下,它认识到它仍然需要知道“结节数”,因此将使用上面列出的“提示”询问该变量。如果用户查询包含所有 4 个必要的信息,它会立即继续并给出推荐。相反,如果用户简单地说“肺结节”,它会询问所有四个参数。
DialogFlow 是如何聪明到可以知道句子的哪些部分代表哪个实体的?当然是训练数据!你只需将句子的相关部分标记为特定实体,它就会学习在新句子中识别这些实体。看起来是这样的:
Yellow label = nodule number; orange label = nodule size; pink label = nodule character; purple label = risk level. Note that you can specify that “smoker” is equivalent to saying “high risk.” Pretty cool!
步骤 2:使用 webhook 处理理解的语言
到目前为止,我们已经使用了奇特的自然语言处理来获取用户输入,理解用户想要什么(意图),并将其分解为感兴趣的有意义的参数(实体)。
现在我们有了这些参数,我们使用一个“webhook”来处理它们,并向用户提供他们想要的信息(MELD 评分、Fleischner 推荐等。).出于我们的目的,您可以将 webhook 想象成一段代码,它接受来自 DialogFlow 的数据,对其进行处理,并返回感兴趣的最终值。
在肺结节的情况下,webhook 采用上述四个结节特征,并执行一系列简单的逻辑语句来识别和返回相关的 Fleischner 建议。也就是说,它采用了这里描述的基本逻辑:
变成了这样:
在这个 webhook 处理之后,一个适当的答案被返回给 DialogFlow,它将这个答案输入到 Google Home,然后,瞧,你就有了一个智能放射学助理。
结论
希望你觉得这个教程有用!此外,我希望你花些时间思考这个迷人的新平台如何为医生和他们的病人提供价值。如果你有任何问题,请告诉我,如果你想聊天,请随时联系 twitter 或 LinkedIn 。感谢阅读!
使用机器学习来模拟世界杯比赛
世界杯正在进入一个新的阶段,很少有人能够预测小组赛的结果。现在是时候进入一个更加激动人心的阶段了,世界上最伟大的人将会面对面。本文的目标是,通过 Python 使用数据科学的力量,尝试揭示这些游戏将呈现的一些统计数据。
在本帖中,我们将:
- 创建一个爬虫,从网上获取团队统计数据
- 预处理、探索和可视化数据
- 下载具有匹配结果的另一种数据
- 组合这两个数据集并构建一个模型来预测匹配结果
- 创建一个简单的蒙特卡洛模拟,并获得 2018 年世界杯淘汰赛阶段的赢家赔率
我们将要使用的一些库:
- 熊猫
- Numpy
- Sklearn
- Plotly
这里的想法是制作一个机器学习算法来预测单场比赛的获胜者,并从那里建立一个蒙特卡洛模拟,可以推断出每个淘汰赛获胜者的概率,以及随后世界冠军的概率。
这篇文章将提供一些图表和代码,但是如果你愿意的话,可以随意跳过它,我会尽量让它更直观。
我们的策略
大多数游戏模拟器倾向于使用一个代表团队表现的总数。在这里,我们正在尝试一种不同的方法,以一种更复杂的方式,不仅与整体,而且与其他三个价值观(进攻、防守、中路)一起工作,以避免简单地将所有特征集中到一个决定球队实力的单一因素上。
该模型将建立在 Sklearn 库的基础上,使用 Pandas dataframes 来操作表中的数据,并 Plotly 来可视化一些有趣的功能。
获取数据
因此,获取数据的第一步是制作一个小爬虫,从国际足联索引中获取信息,这是从 2004 年开始收集国际队数据的一个很好的来源。以下是表格在网站上的排列方式:
用这种方式显示表格,很容易就能得到这样一个网站。为此,我使用了漂亮的 Soup 库来访问 HTML 代码,并使用 Pandas read_html 函数将其转换成可读的数据帧。
我必须承认这个爬虫有点懒惰,它可能会在我们的数据集上复制一些东西。不过,不用担心,因为我们可以稍后删除那些关于熊猫的重复数据(在本文中,我还将提供一些原始数据集的链接)。
我不会进入这个 scrapper 是如何建立的细节,但代码将留在下面,如果你想检查一下。如果你有其他兴趣,请随时联系我。
web_address = '[https://www.fifaindex.com/teams/fifa'](https://www.fifaindex.com/teams/fifa%27)df = pd.DataFrame()for day in range(1,260,1):
for pag in range(1,30):
source_address = web_address + '05_' + str(day) + '/' + str(pag) + '/' + '?type=1'
print('Day:', str(day))
print(pag)try:
soup = get_soup(source_address)
result_list = soup.find('div', {'id': 'no-more-tables'})
except:
print('Page not found.')
breakdate = str(soup.find('ol', {'class': 'breadcrumb'}))if df.empty:
df = pd.read_html(str(result_list))[0]
df['date'] = date
else:
temp_df = pd.read_html(str(result_list))[0]
temp_df['date'] = date
df = df.append(temp_df)
弄乱原始数据集后,我保存了一个更干净的版本(使用 Pickle ),我们将使用这个数据集作为起点。
df = read_pickle('team_stats.pickle')
df.head()
探索和可视化
该表包含了 2004 年至 2018 年国际足联对多支国际球队的评分。原始数据也包括月和日,但是为了简单起见,我将每个团队的表现按年进行了平均,因此我们需要处理的数据点很少。让我们使用 Plotly 在散点图中查看各队每年的总得分:
Plotly 拥有令人惊叹的交互式图表,当你悬停在它上面时,它实际上可以显示信息。不过,这份报告信息量不大。让我们试着在条形图中检查每个日期表现最好的团队,看看它们在这些年中是如何变化的。下图显示了按年度排名的最佳团队以及所有团队的平均表现(如白线所示)。
这是一个很好的图表!西班牙长期以来一直处于领先地位。现在让我们打开包含国际队比赛结果信息的 CSV 文件:
results = pd.read_csv('match_results.csv')
results.head()
结果数据集来自 github ,由 1872 年的足球比赛结果组成,包括球队、比分和其他一些信息。让我们清理一下,只保留对我们有用的特性。
results = results.drop(['city', 'tournament', 'country'], axis=1)
results.home_team = results.home_team.apply(text_norm) #lower_case
results.away_team = results.away_team.apply(text_norm) #lower_case
results.index = pd.DatetimeIndex(results.date).year
results = results.drop('date', 1)
results.head()
三个步骤对我们利用这些数据非常重要:
- 首先,由于我们只有 2004 年的统计数据,我们应该去掉其他年份的数据(不幸的是)。
- 其次,我们不应该与不在我们主要数据框架内的团队合作,因为我们没有他们的评分。
- 最后,我们必须解决这种主客场球队的情况,因为对于世界杯的预测,我们将考虑所有体育场是中立的。
results = results.loc[2004:2017]df_teams = list(df.name.unique())
results = results.reset_index()for index, row in results.iterrows():
if row.home_team not in df_teams:
results.loc[index, 'home_team'] = None
if row.away_team not in df_teams:
results.loc[index, 'away_team'] = None
results = results.dropna()
现在让我们将团队的统计数据输入到结果数据框中。为此,我们将创建 8 个新列,代表每个团队的 4 项技能(赢家和输家)。这是我们现在得出的结论:
现在我们需要从 away_score 中减去 home_score ,这样我们就只有一个因素代表哪支球队赢了(负的或正的进球数)。请注意,我使用术语“主场”和“客场”只是因为这是原始数据集的来源,但这不会对我们的分析产生任何影响,因为我们将只研究世界杯比赛中的中立比赛。从现在开始,我将只称他们为团队 1 和团队 2 (从左到右,团队 1 总是第一个出现在我们的数据集中)。
因此,让我们将每个团队的目标数量压缩到一个单独的特性中,并创建一个 winner 列,这将是要预测的目标。如果 winner > 0,表示队 1 胜,< 0 表示队 2 胜。我们也将丢弃有平局的数据点,因为淘汰赛总是有赢家。
results['score'] = results.home_score - results.away_score
results = results.drop(['home_score', 'away_score', 'home_team', 'away_team'], 1)results['winner'] = None
results['winner'][results.score > 0] = 1
results['winner'][results.score < 0] = -1
results['winner'][results.score == 0] = 0results = results[results.winner != 0]
为了进一步简化问题,我将“帮助”模型从数据中提取信息,提供技能之间的差异,而不是处理性能本身。例如,的攻击 (att) 将会是队的 1 次攻击减去队的 2 次攻击。
results['att'] = results['att1'] - results['att2']
results['def'] = results['def1'] - results['def2']
results['mid'] = results['mid1'] - results['mid2']
results['ovr'] = results['ovr1'] - results['ovr2']to_drop = results[results.winner == 1].sample(247)
results = results.drop(labels=to_drop.index, axis=0)
这就是我们目前的数据:
让我们检查一下团队 1 的总体和得分之间的相关性:
这对我们有好处。您可以看到这些特征之间的正相关,这表明我们的数据似乎是有意义的(团队的整体得分越高,我们应该期望的目标数量就越多)。
创建模型
为了应用机器学习,我构建了一个小函数来准备数据,删除不必要的属性,使用 Numpy 将其转换为数组格式,并将其分成训练和测试集,以便我们可以评估我们模型的精度。
在这个例子中,我将上下文视为一个分类问题,我们的目标是:
- 1: 第一队获胜
- -1: 第二队获胜
使用的分类器算法是逻辑回归、随机森林分类器和线性支持向量分类器:
lr = LogisticRegression()
lr.fit(x_train, y_train)rf = RandomForestClassifier()
rf.fit(x_train, y_train)svc = SVC(kernel='linear')
svc.fit(x_train, y_train)
以下是这些模型的准确度得分:
LR → 68.4%
射频→ 67.9%
SVC → 70.1%
在这里,SVC 似乎比其他产品的性能更好,让我们分别检查一下每个类的性能:
我们可以在 Team 1 上观察到多一点的准确性。这可能与这样一个事实有关,即在原始数据集中,这支球队是主队,也许它有更多的胜利,这可能以某种方式影响了模型的行为。但除此之外,这也可能是随机的,所以我继续。
好的。我们有一个预测器,可以以 70%的准确率猜测哪个队会赢。足够建立一个模拟。我们走吧。
模拟比赛
我们首先需要的是通过小组赛的世界杯球队的表现数据。我们将建立一个类似于我们前些年建立的刮刀,但现在使用的是国际足联指数中的 2018 年世界杯数据。
不幸的是,这一数据似乎与 2018 年(非世界杯)的数据没有太大差异,因为德国仍然占据排名第二的位置,而实际上它已经出局了。无论如何,我们将坚持这个来源来收集我们的数据。
wc = read_pickle('world_cup_teams.pickle')
wc.head()
太好了。我们现在离拥有模拟器只有几步之遥。我们将使用我们的机器学习模型作为蒙特卡洛模拟的规则。如果你很少或没有接触过蒙特卡洛,我推荐你从麻省理工学院开放课程软件的这个课程开始。
让我们构建一个比较团队能力的函数,用我们的 SVC 模型评估获胜者(分别为团队 1 或**团队 2、**的 1 或-1),并返回获胜者姓名。
def match(wc, team1, team2, model):
match = pd.DataFrame(columns=['att1','def1','mid1','ovr1','att2','def2','mid2','ovr2'], index=[0])
match['att1'] = wc[wc.name == team1]['att'].iloc[0]
match['def1'] = wc[wc.name == team1]['def'].iloc[0]
match['mid1'] = wc[wc.name == team1]['mid'].iloc[0]
match['ovr1'] = wc[wc.name == team1]['ovr'].iloc[0]match['att2'] = wc[wc.name == team2]['att'].iloc[0]
match['def2'] = wc[wc.name == team2]['def'].iloc[0]
match['mid2'] = wc[wc.name == team2]['mid'].iloc[0]
match['ovr2'] = wc[wc.name == team2]['ovr'].iloc[0]
match['att'] = match['att1'] - match['att2']
match['def'] = match['def1'] - match['def2']
match['mid'] = match['mid1'] - match['mid2']
match['ovr'] = match['ovr1'] - match['ovr2']
match = match[['att', 'def', 'mid', 'ovr']]
match_array = match.values
prediction = model.predict(match_array)
winner = None
if prediction == 1:
winner = team1
elif prediction == -1:
winner = team2
return winner
嗯……巴西对西班牙会有什么表现?
match(wc, 'brazil', 'spain', svc)>>> 'spain'
哦不。没想到我们会有如此悲惨的结局!但是这里有一个主要的问题,球队的表现有很大的不同,本届世界杯的第一阶段就证明了这一点。因此,让我们添加一些随机性,以避免每次运行模拟时结果都相同。
def match(wc, team1, team2, model, random_scale=5):
match = pd.DataFrame(columns=['att1','def1','mid1','ovr1','att2','def2','mid2','ovr2'], index=[0])
att1 = wc[wc.name == team1]['att'].iloc[0]
def1 = wc[wc.name == team1]['def'].iloc[0]
mid1 = wc[wc.name == team1]['mid'].iloc[0]
ovr1 = wc[wc.name == team1]['ovr'].iloc[0]att2 = wc[wc.name == team2]['att'].iloc[0]
def2 = wc[wc.name == team2]['def'].iloc[0]
mid2 = wc[wc.name == team2]['mid'].iloc[0]
ovr2 = wc[wc.name == team2]['ovr'].iloc[0]
match['att1'] = np.random.normal(att1, scale=random_scale)
match['def1'] = np.random.normal(def1, scale=random_scale)
match['mid1'] = np.random.normal(mid1, scale=random_scale)
match['ovr1'] = np.random.normal(ovr1, scale=random_scale)match['att2'] = np.random.normal(att2, scale=random_scale)
match['def2'] = np.random.normal(def2, scale=random_scale)
match['mid2'] = np.random.normal(mid2, scale=random_scale)
match['ovr2'] = np.random.normal(ovr2, scale=random_scale)
match['att'] = match['att1'] - match['att2']
match['def'] = match['def1'] - match['def2']
match['mid'] = match['mid1'] - match['mid2']
match['ovr'] = match['ovr1'] - match['ovr2']
match = match[['att', 'def', 'mid', 'ovr']]
match_array = match.values
prediction = model.predict(match_array)
winner = None
if prediction == 1:
winner = team1
elif prediction == -1:
winner = team2
return winner
在这里, random_scale 将是决定我们想要对一个团队的表现应用多少随机性的因素。
下一步也是最后一步是创建一个函数,多次运行 match 函数,并计算每个团队的胜利概率。
def simulate_matches(team1, team2, n_matches=10000):
match_results = []
for i in range(n_matches):
match_results.append(match(wc, team1, team2, svc, random_scale=5))
team1_proba = match_results.count(team1)/len(match_results)*100
team2_proba = match_results.count(team2)/len(match_results)*100
print(team1, str(round(team1_proba, 2)) + '%')
print(team2, str(round(team2_proba,2)) + '%')
print('-------------------------')
print()
if team1_proba > team2_proba:
overall_winner = team1
else:
overall_winner = team2
return {'team1': team1,
'team2': team2,
'team1_proba': team1_proba,
'team2_proba': team2_proba,
'overall_winner': overall_winner,
'match_results': match_results}
让我们看看下周日克罗地亚击败丹麦有多难:
simulation_test = simulate_matches('croatia', 'denmark', n_matches=10000)
克罗地亚:40.62%
丹麦:59.38%
好的,这里你看到模型估计丹麦击败克罗地亚的概率更高,那可能是因为 Fifa 指数数据集很可能没有考虑克罗地亚在世界杯开始后的表现。
随着我们进行越来越多的模拟,让我们来衡量两个团队概率之间的差异:
p_list = []
for i in range(len(simulation_test['match_results'])):
denmark = simulation_test['match_results'][:i].count('denmark') / (i+1) * 100
croatia = simulation_test['match_results'][:i].count('croatia') / (i+1) * 100
p_list.append(denmark - croatia)
我们可以看到,赢家概率稳定在大约 8,000 场比赛模拟中,这就是我们要使用的值。让我们建立冠军树:
仅此而已。2018 世界杯全模拟——淘汰赛!
考虑到我们的模型有 30%的误差,让我们计算一下西班牙击败所有球队并实际成为冠军的几率:
spain_proba = 0.817 * 0.862 * 0.718 * 0.593 * 100 * (0.7 ** 4)print('Chance of Spain winning:', str(round(spain_proba,2)) + '%')
西班牙获胜的几率:7.2%
嗯,我觉得这很公平。只是仍然希望巴西证明它是错的!
这个帖子到此为止。本文旨在展示如何使用机器学习来计算模拟中的概率,并不试图实际获得正确的结果,因为使用的数据还不够(或者事件本身根本不可预测)。请把这当成一个教程,用世界杯比赛只是因为这是一个很酷的和最新的主题。应该使用更深入的方法来提高结果的清晰度,使它们在任何级别都有意义!
可以增加一些步骤来改进模型,包括:
- 尝试更复杂的机器学习算法和微调超参数
- 收集更多的数据,不仅仅是国际数据,还有国家队的训练数据
- 更进一步,基于玩家统计数据建立一个模型,这篇文章对这种方法有很好的介绍
这是我的第一篇文章,所以谢谢你一直读到最后。我会试着每个月发布一些与数据科学和机器学习相关的好材料。欢迎发表任何评论或关注,如果你喜欢,别忘了为**鼓掌!**谢谢,下次发帖再见。
用马尔可夫链蒙特卡罗方法进行项目估算
用张量流概率进行哈密顿取样
Free photo from https://pixabay.com
我收到的对之前项目评估工作的一种批评是对数正态分布有短尾巴。这是真的,尽管对数正态分布有很多好处。原因很简单:当将数据拟合到分布形状时,我们选择最可能的参数μμ和σσ。这种方法,无论多么简单,总是导致短尾巴,特别是对于我们拥有的少量数据。事实上,对数正态分布的参数可能不同于我们基于五个数据点得到的最可能的参数。合适的方法是获得预测和参数的联合分布,然后根据参数进行边缘化。在正态分布的情况下,我们将得到具有漂亮长尾的学生 t 分布。对于对数正态分布,我们会得到一个更复杂的分布,但也有长尾。
为了提醒您我们正在处理的问题,我们的任务是根据历史数据来估计一个敏捷迭代/sprint 中可以容纳的故事点的数量。特别是,我们感兴趣的是找到我们可以在一次迭代中完成的故事点的数量,并且有 95%的置信度。
我们将从定义大小为 1 的样本的似然函数开始:
让我们选择μ和σ的先验。
对于σ,我们选择非信息先验:
对于μ,我们选择共轭先验,它是具有 L2 正则化超参数λ的正态分布:
这种情况下的后验概率正比于:
因此,让我们计算百分位数的联合概率分布是:
因此,我们可以通过忽略参数μ和σ来计算所有百分点。答案可以通过分析得出,但在我们的情况下,我想用 MCMC 哈密顿取样法数值求解。
这种方法的思想是得到一个联合分布 p(x,μ,σ2|x(i))的样本。之后,我们可以计算 x 的百分位数。这相当于通过后验分布的参数边缘化。
正如我前面说过的,答案可以通过分析得出,但是我们将使用一种可以用于分布的方法,对于这种分布,很难或者不可能获得样本。这种方法叫做马尔可夫链蒙特卡罗。该方法的思想是在变量空间中进行随机行走,但是尝试更频繁地访问更可能的区域,以便在结果样本中直方图遵循概率分布。在这种情况下,一些不太可能的值必须被拒绝。
MCMC 的一个特殊风格是哈密尔顿采样方法。它有点类似于梯度下降算法,除了 MCMC 中的步长足够大,而不是试图收敛到成本函数的最小值,因此它也探索变量空间中可能性较小的区域,但是倾向于更频繁地访问高可能性区域。
为此,我们需要取联合分布函数的对数(忽略常数):
有很多库做哈密顿取样。甚至还有为此优化的概率编程语言,比如 Stan 。但是这里我们将使用 TensorFlow Probability,这是一个由 Google 创建的概率库
**import** **numpy** **as** **np**
**import** **pandas** **as** **pd**
**import** **tensorflow** **as** **tf**
**import** **tensorflow_probability** **as** **tfp**lamb = 2e-1
**def** log_likelihood(x, mu, sigma2):
'The (negative) log likelihood function for one sample'
**return** (tf.log(x)-mu)**2/2.0/sigma2**def** get_unnormalized_log_probability(data):
**def** joined_probability(x, mu, sigma2):
result = -(2.0+len(data))/2*tf.log(sigma2) - lamb * mu**2/2.0 -log_likelihood(x, mu, sigma2) *#sigma2*
**for** datum **in** data:
result -= log_likelihood(float(datum), mu, sigma2)
**return** result
**return** joined_probability
我们希望确保我们的功能正常工作,最好的方法是编写一个单元测试
**import** **unittest**
**import** **math****class** **TestUnnormalizedLogProbability**(unittest.TestCase): **def** test_get(self):
tf.reset_default_graph()
data=np.array([1.0,1.0])
x = tf.constant(value=1.0)
mu = tf.constant(value=0.0)
sigma2 = tf.exp(1.0)
probability_function = get_unnormalized_log_probability(data)
probability = probability_function(x, mu, sigma2)
init = tf.global_variables_initializer() **with** tf.Session() **as** sess:
sess.run(init)
self.assertTrue(abs(-2-probability.eval())<1e-5)
unittest.main(argv=[''], verbosity=0, exit=**False**);*----------------------------------------------------------------------
Ran 1 test in 0.088s**OK*
我们将使用与之前相同的数据
data=np.array([14, 12, 7, 14, 13])
*# Create state to hold updated `step_size`.*
step_size = tf.get_variable(
name='step_size',
initializer=1e-1,
use_resource=**True**, *# For TFE compatibility.*
trainable=**False**)*# Initialize the HMC transition kernel.*
hmc = tfp.mcmc.HamiltonianMonteCarlo(
target_log_prob_fn=get_unnormalized_log_probability(data),
num_leapfrog_steps=3,
step_size=step_size,
step_size_update_fn=tfp.mcmc.make_simple_step_size_update_policy(),
seed=1398)*# Run the chain (with burn-in).*
samples, kernel_results = tfp.mcmc.sample_chain(
num_results=int(1e5),
*#num_burnin_steps=int(1e1),*
current_state=[10.0,2.0,0.2],
kernel=hmc)*# Initialize all constructed variables.*
init_op = tf.global_variables_initializer()**with** tf.Session() **as** sess:
init_op.run()
samples_, kernel_results_ = sess.run([samples, kernel_results])all_samples = np.array(samples_)
all_samples.shape*(3, 100000)*
我们的结果有三行,分别是 x,μ和σ。我们可以利用这三个变量
all_samples.mean(axis=1)*array([14.040146 , 2.4572225 , 0.21323058], dtype=float32)*
出乎意料的是,x
的均值比我们想象的要高(12)。让我们绘制直方图来查看概率分布:
%**matplotlib** inline
**import** **matplotlib.pyplot** **as** **plt**
**import** **seaborn** **as** **sns**
fig, ax = plt.subplots(figsize=(11.7, 8.27))
sns.distplot(all_samples[0,:], hist=**False**);
我们看到最大值约为 12,因为我们将使用最大后验分布。
该曲线具有不规则的形状,但对于多变量 MCMC 结果来说,这在某种程度上是意料之中的。还要注意,对数正态分布的尾部更长。这是忽略后验分布参数的结果。让我们看看我们 95%的信心会落在哪里:
np.percentile(all_samples[0,:], q=5)*7.160015678405762*
95%的置信度也非常接近我们用更简单的方法得到的结果。然而,用 MCMC I 进行的实验发现,该方法不稳定,并且强烈依赖于播种、初始值和超参数的选择。随着维度的增加,这个问题变得更加严重。
结论
在这个故事中,我们试图在敏捷项目评估中使用贝叶斯方法,而不是分析性地计算百分位数,我们展示了如何使用马尔可夫链蒙特卡罗和哈密尔顿抽样来实现这个结果。我们还演示了如何在 TensorFlow 概率包中实现它。
利用最大似然法自动检测汽车牌照
概观
我们讨论底层技术,它在不同领域的应用,如收费站,犯罪预防等。然后,我们给出了一个如何使用张量流实现这一点的演练。
然后我们讨论代码和算法,浏览最终的输出和精度数字。
简介:
LPR(license plate recognition)是一种技术,主要是软件,它使计算机系统能够从数字图片中自动读取车辆的注册号码(牌照号码)。
自动读取车牌号意味着将数字图像的像素转换成车牌的 ASCII 文本。
由于车辆的日益增多,车牌识别(LPR)在这个繁忙的世界中扮演着重要的角色。偷窃车辆、违反交通规则、进入受限空间也呈线性增长,因此想要阻止这种行为。
车牌识别是车辆自动识别的一种有效方法。在诸如车牌检测、字符分割和每个字符的识别的基本处理步骤中,分割起着重要的作用,因为识别的准确性基于分割的完美程度。这项工作特别处理印度车牌的识别,也可以扩展到识别。
ANPR 是如何工作的?
当车辆到达指定的入口时,摄像头会自动检测并读取车牌。随后生成的数据被立即存储(在停车场的情况下),或者可以基于生成的数据和预先确定的设置来准许或拒绝访问。该数据被存储以供将来分析(在 IT 园区或门禁社区的情况下)。通过在两端安装不同的摄像机,可以并行管理入口和出口系统。每台摄像机都有一个 IP 地址,整个系统以高度的连通性连接在一起。
该系统利用互联网协议进行控制和通信,但该结构通过加密数据流保持高度安全。可以在安装时设置访问权限。
技术要求:
从 LPR/ANPR 的角度来看,图像质量始终是关键因素。捕捉快速运动的车辆需要特殊的技术来避免运动模糊,运动模糊会大大降低识别精度。为了确保正确的图像质量,短快门时间需要与高功率照明结合使用。
只有专用的 ANPR 相机才能满足这些要求,如 AHR 的 ANPR 相机,它提供灵活的快门控制,内置红外闪光灯,能够捕捉高达 250 公里/小时的车辆,适用于各种车牌读取应用。
使用案例:
**执法:**全国和世界各地的执法机构越来越多地采用自动车牌识别(ALPR)系统来增强他们的执法和调查能力,扩大他们的相关数据收集,并加快将车辆牌照与被盗、被通缉和其他相关车辆的列表进行比较的冗长耗时的过程。德国、瑞典、丹麦、加拿大等多个国家已经部署了 ALPR 用于执法目的。
**交通控制:**软件可以分析视频数据,并可以计算交通流量参数,从而计算出以下标准交通特征:
- 平均速度[公里/小时];
- 交通量(每小时车辆数)[车辆/小时];
- 交通密度[车辆/公里]。
任何违反标准限制的车辆都可能被黑启动,并根据国家法律采取进一步的行动。
**收费公路:**借助这个 ANPR,我们可以减少整个收费过程中的人为交互,提高系统的安全水平。这项技术将用于全国各地的不同收费亭,在那里可以很容易地控制交通和有效地管理时间。
**犯罪威慑:**自动车牌识别(ANPR)技术用于在地方、部队、地区和国家层面帮助检测、威慑和破坏犯罪活动,包括打击流窜犯、有组织犯罪集团和恐怖分子。ANPR 为犯罪调查提供线索和证据,被英格兰、威尔士、苏格兰和北爱尔兰的执法机构广泛使用。
印度车牌的自动车牌检测:
印度车牌的数据集不可用,因此需要从头开始创建。为此,从谷歌下载了大约 700 张图片,并对数据进行了清理。清理后的数据集有 250 幅图像,这些图像被上传到 Dataturks 注释工具(矩形边界框)并进行手动注释。
The Dataset on Dataturks
该工具自动解析图像,允许我们创建感兴趣的实体的注释,并生成 JSON 格式的训练数据,每行包含图像链接以及注释细节。
数据快照如下所示:
上面的数据集由 230 张带注释的图像组成,可以在这里找到。我们用 200 份简历数据训练模型,在 30 张图片上测试。
由 Dataturks 注释工具生成的 JSON 格式的数据示例如下:
为运行 Tensorflow 模型准备数据集:
带注释的数据被下载(到一个包含图像和 JSON 文档的文件夹中)并被分成训练和测试数据,如上所述。然后,训练和测试文件夹作为 python 脚本的输入,用于将数据转换为 Pascal VOC(这里是演示)。
由代码生成的 pascal voc 的 XML 格式数据的示例如下:
从 json 到 Pascal VOC 脚本获得的 xml 文档和图像然后被转换成一组 csv 记录和图像。最后,这些数据被转换成张量记录用于训练和测试。生成的张量记录示例如下:
github 上提供了将数据转换为不同格式的代码:
培训:
模型的训练是在 Ubuntu 16.04 LTS GPU 箱上完成的,该 GPU 箱具有 NVIDIA TESLA K80 和 7GB RAM、12GB 显卡以及 30 GB 硬盘。训练进行了 10,000 步,耗时约 2.5 -3 小时。
可以在 Dataturks 博客帖子中找到详细的培训教程,标题为使用 Tensorflow 和 GCP 在电子商务中自动标记服装。
因为我使用了 MobileNet 模型,所以我对相同的配置文件进行了更改。训练好的模型可以在这里下载。
配置文件中的更改:
后面部分:
number-plate-detection . Pb txt:
运行培训作业
可以使用以下命令运行本地培训作业:
其中${PATH_TO_YOUR_PIPELINE_CONFIG}
指向流水线配置,而${PATH_TO_TRAIN_DIR}
指向训练检查点和事件将被写入的目录。默认情况下,训练作业将无限期运行,直到用户终止它或者它完成了提到的步骤数。
运行评估作业
评估作为单独的作业运行。eval 作业将定期轮询 train 目录中的新检查点,并在测试数据集上对其进行评估。可以使用以下命令运行该作业:
其中${PATH_TO_YOUR_PIPELINE_CONFIG}
指向流水线配置,${PATH_TO_TRAIN_DIR}
指向保存训练检查点的目录(与训练作业相同),而${PATH_TO_EVAL_DIR}
指向保存评估事件的目录。与培训作业一样,默认情况下,eval 作业会一直运行,直到终止。
测试:
模型准备变量单元格:
在被检测的第一个单元中:
这将完成您的模型的测试。
对测试图像进行测试:
对真实视频帧的测试:
请不要说我没有在 CCTV 视频上测试过,而是在 CCTV 视频帧上测试过。为此,需要将 opencv 视频捕获附加到该代码上,并对每一帧运行它。这可能是一个有趣的扩展,并且将向实时部署迈进一步。
注意:关于培训、测试、tensorboard 和运行评估工作的详细教程可以在 Dataturks 博客帖子中找到,标题为使用 Tensorflow 和 GCP 在电子商务中自动标记服装。
结论:
结果令人印象深刻,在 30 幅测试图像中,有 27 幅得到了正确的分类。分类的置信度在 85%到 99%之间。
移动互联网似乎在实时检测方面发挥了惊人的作用。许多类似的应用程序也已经使用 inception net 开发出来,但是移动网络的结果已经被证明比 inception net 更适合实时应用。
为什么选择 MobileNets?
研究表明,inception net 是 CPU 密集型的,全速运行时使用 25-30%。有道理,因为我们会以和汽车经过一样快的速度行驶。我们希望降低到 5%以下,我们将通过降低它运行的频率来做到这一点,因为我们的用例不需要连续运行推理来实现我们上面讨论的隐私目标。将它改为每 18 帧分类一次(大约每秒一次)会将平均使用率降低到大约 5.5%。所以 MobileNet 模型是 Inception 的 1/30 大。每帧运行速度提高了 3 倍以上。而且它使用的 CPU 资源要少得多。
应用:
这些用于图像分类的定制模型可以部署在停车场、科技园或任何其他门禁社区的现场汽车监控中。
我希望听到任何建议或疑问。请在 devika.mishra@dataturks.com 给我写信
资源:
所有代码:车牌检测代码
数据集:车辆号牌检测数据集
使用蒙特卡洛树搜索你的梦幻足球选秀
本帖中的代码也可作为 Jupyter 笔记本 。
离下一个美式足球赛季开始还有两个月,这意味着世界各地的梦幻足球运动员正在为他们即将到来的联赛选秀做准备。在这篇文章中,我们将使用蒙特卡罗树搜索算法来优化我们在一个典型的蛇草案中的下一个选择。如果你不熟悉梦幻足球或者蛇选秀,你可以在 ESPN 的梦幻足球 101 找到很好的介绍。我们将看到一个由十支球队组成的标准 PPR 得分联盟,但是这些设置应该很容易改变以适应你自己的联盟。我们将主要关注草案逻辑的 Python 3 实现,将算法的细节留给文本中的参考,同时仍然提供代码。
Photo by Karri Terra on Pixy
草案
选秀是你的联赛赛季的开始。每个竞争者挑选真实世界的足球运动员来组成他的初始名单。糟糕的选秀真的会毁了你的赛季,所以很多研究都花在为你的球队挑选合适的球员上。在大多数联赛中,选择是按照蛇的顺序依次进行的(1 比 10,然后 10 比 1,等等)。).这意味着在正确的时间填补正确的位置是你战略的重要组成部分。如果你的大部分竞争对手都是从挑选外接球手开始的,你应该从众还是逆势而为?这正是我们想用算法回答的问题。
但首先是逻辑草案。我们需要一个对象来描述我们草稿的确切状态。在每个回合中,我们需要知道到目前为止每个花名册选择了哪些足球运动员,哪些足球运动员仍然可用(自由球员),接下来回合的顺序是什么,以及哪个竞争者最后移动(因此算法可以从该竞争者的角度评估最后移动)。
class DraftState:
def __init__(self, rosters, turns, freeagents, playerjm=None):
self.rosters = rosters
self.freeagents = freeagents
self.turns = turns
self.playerJustMoved = playerjm
让我们看看如何准确地评估一个草案状态。我们的名单包括一名四分卫(QB),两名跑卫(RB),两名外接员(WR),一名紧逼端(TE),一名灵活端(RB,WR 或 TE),一名防守端(D),一名踢球者(K)和七名替补。首先,让我们假设我们可以根据每个足球运动员的赛季总幻想点数来评估他的价值。然后,我们可以根据一些权衡来评估阵容中的每个位置。重量是至关重要的,因为我们不能在赛季的每一周都使用相同的球员阵容,因为赛季期间的周、伤病或战略决策。
根据一些实验,下面的重量看起来还可以。
由于草案的逻辑都是关于将足球运动员转移到联盟的名单上,所以包含一个NflPlayer
类也是有意义的。
class NflPlayer:
def __init__(self, name, team, position, points):
self.name = name
self.team = team
self.position = position
self.points = points
现在可以通过将每个球员(从第一个选秀权开始向下移动)映射到其位置的最高可用权重来评估名册。具体来说,如果出现平局,我们将降低灵活头寸的优先级。如果最后的任何权重没有映射到名册上的球员,则相应位置的前三名自由球员的平均值将映射到该权重——有点像对该位置进行分流。代码如下所示。
import numpy as npdef GetResult(self, playerjm):
""" Get the game result from the viewpoint of playerjm.
"""
if playerjm is None: return 0
pos_wgts = {
("QB"): [.6, .4],
("WR"): [.7, .7, .4, .2],
("RB"): [.7, .7, .4, .2],
("TE"): [.6, .4],
("RB", "WR", "TE"): [.6, .4],
("D"): [.6, .3, .1],
("K"): [.5, .2, .2, .1]
} result = 0
# map the drafted players to the weights
for p in self.rosters[playerjm]:
max_wgt, _, max_pos, old_wgts = max(
((wgts[0], -len(lineup_pos), lineup_pos, wgts) for lineup_pos, wgts in pos_wgts.items()
if p.position in lineup_pos),
default=(0, 0, (), []))
if max_wgt > 0:
result += max_wgt * p.points
old_wgts.pop(0)
if not old_wgts:
pos_wgts.pop(max_pos)
# map the remaining weights to the top three free agents
for pos, wgts in pos_wgts.items():
result += np.mean([p.points for p in self.freeagents if p.position in pos][:3]) * sum(wgts) return resultDraftState.GetResult = GetResult
注意,这个方法包含了很多假设。最值得注意的是,它纯粹基于季节预测,忽略了诸如(共)方差或(强度)时间表之类的东西。此外,它只是孤立地看待每个花名册的价值,而不是相互比较。这些假设应该在以后研究。
让我们继续下一个选秀逻辑——在给定选秀状态的情况下,找出当前竞争对手的可用移动。理论上,你可以选择任何可用的自由球员,但幸运的是,我们可以大大简化事情。因为我们的估价只是基于每个球员的赛季预测,所以选择一个预测比另一个相同位置的自由球员低的自由球员是没有意义的。这将可用的移动限制为选择一个位置(然后选择该位置上最有价值的自由球员)。此外,由于估值中使用的权重,例如起草三个四分卫是没有意义的(第三个四分卫不会增加任何价值)。实际上,以下每个位置的限制似乎是合理的。
这导致了下面的代码。
def GetMoves(self):
""" Get all possible moves from this state.
"""
pos_max = {"QB": 2, "WR": 6, "RB": 6, "TE": 2, "D": 2, "K": 1} if len(self.turns) == 0: return [] roster_positions = np.array([p.position for p in self.rosters[self.turns[0]]], dtype=str)
moves = [pos for pos, max_ in pos_max.items() if np.sum(roster_positions == pos) < max_]
return movesDraftState.GetMoves = GetMoves
最后一部分逻辑涉及到在每次选择后更新草稿状态。代码很简单。
def DoMove(self, move):
""" Update a state by carrying out the given move.
Must update playerJustMoved.
"""
player = next(p for p in self.freeagents if p.position == move)
self.freeagents.remove(player)
rosterId = self.turns.pop(0)
self.rosters[rosterId].append(player)
self.playerJustMoved = rosterId
DraftState.DoMove = DoMove
最后,我们的算法需要一个Clone
方法,这样多个模拟运行就不会相互干扰。
def Clone(self):
""" Create a deep clone of this game state.
"""
rosters = list(map(lambda r: r[:], self.rosters))
st = DraftState(rosters, self.turns[:], self.freeagents[:],
self.playerJustMoved)
return stDraftState.Clone = Clone
该算法
蒙特卡洛树搜索是一种启发式搜索算法,用于在基于回合的游戏中寻找最佳的下一步棋,在这种游戏中,几乎不可能考虑所有可能的棋步(及其最终结果)。它被成功地用于玩游戏,如国际象棋和扑克,最近是谷歌战胜世界冠军围棋手的一个非常重要的部分。因为我们的蛇形选秀也是回合制的,每回合都有很多可能的走法,所以 MCTS 看起来是击败我们竞争对手的绝佳选择。该算法背后的基本思想是模拟大量的游戏——首先通过选择随机移动,然后通过专注于最有希望的移动来收敛到最佳移动。它平衡了探索(寻找更有前途的步骤)和开发(关注最有前途的步骤)之间的权衡。
我们下面使用的实现来自 MCTS 研究中心,在那里你也可以找到对算法的很好的介绍。另一个精彩的介绍请看一下杰夫·布拉德伯里的博客文章。
# This is a very simple implementation of the UCT Monte Carlo Tree Search algorithm in Python 2.7.
# The function UCT(rootstate, itermax, verbose = False) is towards the bottom of the code.
# It aims to have the clearest and simplest possible code, and for the sake of clarity, the code
# is orders of magnitude less efficient than it could be made, particularly by using a
# state.GetRandomMove() or state.DoRandomRollout() function.
#
# Written by Peter Cowling, Ed Powley, Daniel Whitehouse (University of York, UK) September 2012.
#
# Licence is granted to freely use and distribute for any sensible/legal purpose so long as this comment
# remains in any distributed code.
#
# For more information about Monte Carlo Tree Search check out our web site at [www.mcts.ai](http://www.mcts.ai)from math import *
import randomclass Node:
""" A node in the game tree. Note wins is always from the viewpoint of playerJustMoved.
Crashes if state not specified.
"""
def __init__(self, move = None, parent = None, state = None):
self.move = move # the move that got us to this node - "None" for the root node
self.parentNode = parent # "None" for the root node
self.childNodes = []
self.wins = 0
self.visits = 0
self.untriedMoves = state.GetMoves() # future child nodes
self.playerJustMoved = state.playerJustMoved # the only part of the state that the Node needs later
def UCTSelectChild(self):
""" Use the UCB1 formula to select a child node. Often a constant UCTK is applied so we have
lambda c: c.wins/c.visits + UCTK * sqrt(2*log(self.visits)/c.visits to vary the amount of
exploration versus exploitation.
"""
UCTK = 200
s = sorted(self.childNodes, key = lambda c: c.wins/c.visits + UCTK * sqrt(2*log(self.visits)/c.visits))[-1]
return s
def AddChild(self, m, s):
""" Remove m from untriedMoves and add a new child node for this move.
Return the added child node
"""
n = Node(move = m, parent = self, state = s)
self.untriedMoves.remove(m)
self.childNodes.append(n)
return n
def Update(self, result):
""" Update this node - one additional visit and result additional wins. result must be from the viewpoint of playerJustmoved.
"""
self.visits += 1
self.wins += resultdef UCT(rootstate, itermax, verbose = False):
""" Conduct a UCT search for itermax iterations starting from rootstate.
Return the best move from the rootstate.
""" rootnode = Node(state = rootstate) for i in range(itermax):
node = rootnode
state = rootstate.Clone() # Select
while node.untriedMoves == [] and node.childNodes != []: # node is fully expanded and non-terminal
node = node.UCTSelectChild()
state.DoMove(node.move) # Expand
if node.untriedMoves != []: # if we can expand (i.e. state/node is non-terminal)
m = random.choice(node.untriedMoves)
state.DoMove(m)
node = node.AddChild(m,state) # add child and descend tree # Rollout - this can often be made orders of magnitude quicker using a state.GetRandomMove() function
while state.GetMoves() != []: # while state is non-terminal
state.DoMove(random.choice(state.GetMoves())) # Backpropagate
while node != None: # backpropagate from the expanded node and work back to the root node
node.Update(state.GetResult(node.playerJustMoved)) # state is terminal. Update node with result from POV of node.playerJustMoved
node = node.parentNode return sorted(rootnode.childNodes, key = lambda c: c.visits)[-1].move # return the move that was most visited
从字面上看,这段代码唯一的变化是在UCTSelectChild
中,我们应用了UCTK
来更好地权衡勘探和开发。这是必要的,因为原始代码是基于 0(输)和 1(赢)之间的比赛结果,而我们的结果是一个团队的总预测分数,这是完全不同的尺度。值 200 是根据经验设置的。
运行代码
我们现在应该有了运行草案的所有组件。唯一缺少的是季节预测。你可以找到许多网站提供这些预测,但我解决了 ESPN 的预测,因为这是我的联盟主办。我创建了一个 CSV ,对 2018 年的预测如下。
我们可以运行一个完整的草案,其中每个竞争者草案,他的团队使用蒙特卡罗树搜索算法。让我们试一试。
import pandas as pdnfl_players = pd.read_csv("nfl_players.csv", index_col=0)
freeagents = [NflPlayer(*p) for p in nfl_players.itertuples(index=False, name=None)]num_competitors = 10
rosters = [[] for _ in range(num_competitors)] # empty rosters to start withnum_rounds = 16
turns = []
# generate turns by snake order
for i in range(num_rounds):
turns += reversed(range(num_competitors)) if i % 2 else range(num_competitors)
state = DraftState(rosters, turns, freeagents)
iterations = 1000
while state.GetMoves() != []:
move = UCT(state, iterations)
print(move, end=".")
state.DoMove(move)
到目前为止,一切顺利。虽然代码相当慢,但一切似乎都运行良好。速度还不是我们关注的焦点,需要对实现做进一步的研究。但是现在我认为结果看起来很有希望,我们可以对算法有一个很好的总体想法。需要注意的一点是,上面的代码每轮使用 1000 次迭代,目前看来还可以,但这也需要进一步研究。
各参赛队的最终结果如下所示。
后续步骤
现在我们有了一个应用于梦幻足球选秀的蒙特卡洛搜索树算法的工作版本,我们应该花一些时间来分析性能。我会在我的下一篇文章中尝试一下,所以请关注我的博客…
感谢阅读!请提供反馈。
使用 R 中的 networkD3 创建简单明了的 Sankey 图
我发现桑基图对于说明人员流动或偏好非常有用。R 中的networkD3
包提供了一种直接生成这些图的方法,不需要知道实际 D3 代码的来龙去脉。
为了说明我的意思,我生成了一个桑基图,以显示英国的 12 个地区如何对 2016 年英国退出欧盟公投的整体结果做出贡献,在公投中,选民以 17,410,742 票对 16,141,241 票选择离开欧盟。
如果你想看到完全交互式的 Sankey 图,你可以在 RPubs 这里通过 RMarkdown 文档查看代码。不幸的是,媒体上只能显示静态图像。
使数据成形
关于英国退出欧盟公投的非常详细的数据可以从英国选举委员会的网站上获得。第一步是加载我们的库,并将数据放入 r 中。由于数据非常详细,直到最本地化的投票中心,我们需要汇总所有的离开和剩余投票,以获得每个地区的总数。
## load librarieslibrary(dplyr)
library(networkD3)
library(tidyr)# read in EU referendum results datasetrefresults <- read.csv("EU-referendum-result-data.csv")# aggregate by regionresults <- refresults %>%
dplyr::group_by(Region) %>%
dplyr::summarise(Remain = sum(Remain), Leave = sum(Leave))
然后我们需要创建两个数据帧供networkD3
在其sankeyNetwork()
函数中使用:
- 一个
nodes
数据帧,对源节点(即 12 个英国区域)和目的节点(即离开和留下)进行编号,从零开始。 - 一个
links
数据框架,使用source
、target
和value
列逐项列出每个流程。例如,西米德兰兹地区为休假投了 1,755,687 票,因此在这种情况下,source
将是西米德兰兹的节点,target
将是休假的节点,value
将是 1,755,687 票。
以下是以这种方式构建数据的一些简单代码:
# format in prep for sankey diagramresults <- tidyr::gather(results, result, vote, -Region)# create nodes dataframeregions <- unique(as.character(results$Region))
nodes <- data.frame(node = c(0:13),
name = c(regions, "Leave", "Remain"))#create links dataframeresults <- merge(results, nodes, by.x = "Region", by.y = "name")
results <- merge(results, nodes, by.x = "result", by.y = "name")
links <- results[ , c("node.x", "node.y", "vote")]
colnames(links) <- c("source", "target", "value")
现在我们已经以正确的方式构建了数据,我们可以简单地使用networkD3::sankeyNetwork()
函数来创建图表。这产生了一个简单有效的图表,滚动交互显示每个投票流的细节。这里展示的是静态版本。
# draw sankey networknetworkD3::sankeyNetwork(Links = links, Nodes = nodes,
Source = 'source',
Target = 'target',
Value = 'value',
NodeID = 'name',
units = 'votes')
Brexit Referendum 2016 vote flows by region
最初我是一名纯粹的数学家,后来我成为了一名心理计量学家和数据科学家。我热衷于将所有这些学科的严谨性应用到复杂的人的问题上。我也是一个编码极客和日本 RPG 的超级粉丝。在 LinkedIn 或Twitter上找我。
用自然语言处理检测阿尔茨海默病患者的语言线索
这篇论文旨在从阿尔茨海默病(AD)患者生成的语音转录中检测语言特征和语法模式。这被认为是自然语言处理和深度学习技术在计算健康方面的一个重要应用。
简介
作者提出了几种神经模型,如 CNN 和 LSTM-RNNs——以及它们的组合——来增强广告分类任务。训练好的神经模型通过 激活聚类 和 一阶导数显著性 技术用于解释 AD 患者的语言特征(包括性别差异)。
动机
语言变异可以作为监控患者认知功能如何受到影响(例如,单词查找和推理障碍)的代理。这可以使机器具备诊断能力,这对于治疗 AD 特别有效,因为它既不可治愈也不可逆转。
挑战与局限
检测 AD 阳性患者的挑战在于它需要不同的语言和世界知识。考虑下面的例子:
"嗯…有个妈妈站在那里嗯嗯地洗碗,水槽满溢…满溢"有几个语言提示,比如“溢出……溢出,表示混乱和记忆丧失的迹象,这在 AD 阳性患者中非常常见。因此,作者提出了一种从数据中自动学习这些语言线索的神经模型,而不是依赖于手工制作的特征。从以前的文献中观察到的其他重要问题如下:
- 手工制作的特征可能不适合分析 AD 患者数据,这些数据传达了语言模式的渐进变化。
- 随着语言和文化的发展,手工制作的功能很快就会过时。
- 仅仅依靠神经网络并不能提供太多的可解释性。
数据
这项工作使用了痴呆银行数据集,它由 AD(和对照组)患者的抄本和音频记录组成。这些记录是通过对几项任务的采访收集的,如“回忆测试”和“波士顿饼干盗窃”。抄本被分割成带有词性(POS)标签的单独话语。
车型
提出了三种类型的神经方法:CNN(嵌入+卷积层+最大池层)、LSTM-RNN(嵌入+ LSTM 层)和 CNN-LSTM(基本上是在 CNN 之上放置一个 LSTM——架构如下图所示)。(详见论文。)
结果
表现最好的模型(POS tags + CNN-LSTM)达到了 91.1%的准确率,这为广告分类任务设定了新的基准。参见下面的其他结果。
作者观察到,几乎所有的 AD 阳性结果都被正确分类,而对非 AD 样本的分类有更多的错误。这可能是因为数据集包含了具有与 AD 相关的不同程度症状的患者。(更多结果见论文。)
分析
男性和女性 AD 患者的语言模式没有显著差异。
此外,使用两种可视化技术对由神经模型捕获的语言线索进行解释:
- 激活聚类 —提供对句子级模式的洞察
- 一阶导数显著性 —提供对单词重要性的洞察
通过激活聚类,在 AD 患者中发现的三种常见语言模式从聚类中出现:简短回答和突发言语(例如,“和”和“哦!”),反复要求澄清(例如,“我说的是事实吗?”),并以感叹词(“所以”和“嗯”)开头。此外,对于 Cookie 和 Recall 等几个任务,广告聚类最常用的 POS 标签是不同的。
通过显著性热图,可以看出对照组和 AD 患者之间单词重要性的差异。如下图(左)所示,单词“uh”和“um”是对 AD 患者进行分类的重要且可区分的言语特征。右图显示,控制组没有大量使用这类填充词。
未来工作与结论
- 结合可视化技术的神经模型可以为 AD 患者的语言线索和变化提供更多的见解。也就是说,这种模型可以推广到研究其他神经疾病。
- 上下文感知模型和会话上下文可以帮助提高模型的预测性能,并且还提供更多的可解释性。
参考文献
通过解释神经模型检测阿尔茨海默氏痴呆的语言特征——(Sweta Karlekar,Tong Niu,和 Mohit Bansal
使用 NLP 识别控制多个帐户的 Redditors
Photo by Daniel Monteiro on Unsplash
介绍
我建立了一个模型,可以确定两个 Reddit 帐户是否由同一个用户控制,仅仅基于他们的写作风格。代码是用 Python 和 Spark 写的,可以在我的 Github 上找到。如果您有任何问题或改进建议,可以通过 Linkedin 联系我。
语境
几个世纪以来,作者可以匿名写作,认为他们的真实身份永远不会被揭露。然而,在过去的几年里,这一切都改变了,因为机器学习方法提高了笔形测量的效率。文体学是对写作风格的研究,涉及通过揭示单词选择、句子结构、标点符号等方面的独特模式来识别作者的写作倾向。
一些最著名的文体分析的例子包括使用它来确定詹姆斯·麦迪逊和亚历山大·汉密尔顿是匿名撰写的《联邦党人文集》的作者,成功地将 J.K .罗琳与《布谷鸟的呼唤》一书的匿名作者联系起来,并确定莎士比亚实际上写了哪些莎士比亚戏剧,哪些是他合写的,哪些是由完全不同的人写的。
但这些都是分析专业人士撰写的大量文本的例子。文体学在文本样本较少的非正式场合(如社交媒体)是否仍然有效?在我们开始区分一个作者的写作风格和另一个作者的写作风格之前,我们需要多少单词?在我们开始看到两个写作风格非常相似的用户之前,我们可以比较多少个作者的匿名文本?
履行
对于这个项目,我想看看能否通过分析 Reddit 上的用户来回答这些问题。网站上的所有用户都以给定的用户名匿名提交帖子和评论,每个人的评论都是公开的。创建 Reddit 帐户所需的唯一信息是一封电子邮件,因此一些用户创建了多个帐户。当一个用户的账户因为不文明的讨论而被禁止进入子编辑时,这就产生了一个问题。他们可以简单地创建另一个帐户来重新获得子编辑访问权。也许,如果我幸运的话,我可以识别两个或更多属于同一个用户的账户,以此帮助 subreddit 版主在他们的社区中保持更健康的讨论。
因为这是一个无监督的学习问题,我需要一些方法来验证我的模型的准确性。为了做到这一点,我获取了一个用户的全部评论历史,并随机抽取了一半的评论,用这些评论创建了一个新的伪用户。然后,我测量了我的模型是否成功地将这个伪用户与包含另外 50%评论的原始用户正确匹配。
基线
文体学中最古老的技术之一可以追溯到 19 世纪,当时通过使用不同长度单词的频率来比较作者。一些人倾向于更多地使用两三个字母的单词,而另一些人倾向于使用更大的词汇量。使用这些单词长度的频率的技术被称为门登霍尔的组成特征曲线(MCCC)。虽然这种技术相当粗糙,但仍然可以用来准确地识别作者。我们可以通过确定具有最低平均 RMSE 的曲线来确定谁的合成曲线与匿名文本的曲线最相似。在 7 个用户的评论历史被分成子集 1 中的伪用户和子集 2 中的原始用户之后,MCCC 首先在 7 个随机选择的 Redditors 的小组上进行测试。
通过遍历子集 1 中的每个伪用户,并将其与子集 2 中的每个用户进行比较,MCCC 实际上能够正确识别 7 个用户中的 5 个。虽然很有希望,但这种技术过于简单,无法大规模使用。
模拟陋居的三角洲
许多自然语言处理应用,如主题建模,通常使用词频-逆文档频率(TF-IDF)来识别可用于帮助描述文本的罕见关键词。在文体学中,最重要的词实际上是虚词,常见的词如“大约”、“因为”、“之上”、“而”。作者通常会写一个广泛的主题,因此他们使用的词汇变化很大。然而,虚词出现在每篇文章中,并且对于给定的作者,它们在不同的文档中的使用频率往往保持相当一致。
在我的分析中,150 个最常用的虚词被用来通过 Delta 方法识别用户的写作风格。记录每个功能词的频率,然后通过减去平均值并除以标准偏差进行标准化,给出每个特征值的 z 得分表示。结果是一个 150 维的向量,在作者比普通用户更频繁地使用一个词的特征维度上是正的,而在作者比普通用户更少地使用一个词的特征维度上是负的。然后,通过余弦相似性的测量,可以最精确地将伪用户的向量与每个用户的向量进行比较。
这种方法大大改善了结果。之前分析的 7 个用户现在以 100%的准确度匹配回他们的正确用户。从一个随机的 40 人小组中识别用户(过滤掉那些评论少于 200 条的用户,这些用户的历史太小,无法识别写作倾向)返回了 95%的准确率。
Cosines of pseudo-users to their original matching accounts vs non-matches
除了词法分析,我们还可以用句法来区分独特的写作风格。使用 nltk 的词性(POS)标记和跳格,我找到了 100 个最常用的词性跳格序列,并对每个用户使用它们的频率进行了矢量化,就像对虚词进行矢量化一样。对于同样的 40 个用户,该模型返回了 90%的准确率。然而,通过将这两种技术结合在一起,我达到了 100%的准确率。
按比例放大
通过继续添加随机用户,该模型继续完美地预测多达 100 个用户的作者身份。超过这一点,当从 3000 个用户中识别出一个用户时,准确率开始稍微下降,一直下降到 92.2%。
准确度下降的原因主要是由于缺乏数据。所有超过 1000 条评论的用户仍然被 100%准确地预测到。评论历史较少的人不容易被识别出来。我还发现某些用户的写作风格不是很独特;它们的绝大多数特征值都在平均值附近徘徊。下图所示的用户 KhasminFFBE 就是这种情况。此外,更多用户的引入增加了一些用户将具有高度相似的写作风格的机会。如果用户倾向于“代码转换”,即在不同的上下文中改变他们的写作风格,他们可能会被意外地识别为具有非常相似的写作倾向的另一个用户。
comparing the feature vectors of two users
为了进一步改进模型,我在特征向量中加入了标点符号的使用和 Reddit 上常用的某些 markdown 格式化方法(比如“ ”用于显示超链接)。我还添加了一些社交媒体上常见的俚语词,这些词类似于之前的一些虚词。这些包括像“耶”、“将要”和“哈哈”这样的词。这使得我的模型在一组 3000 个用户中正确匹配用户的准确率提高到了 93.8%。
Probability distribution of the final model
Alpha for non-matching accounts
从分布中,我能够确定临界值,在该临界值上,我可以拒绝用户不匹配的无效假设。因为错误地禁止用户为假阳性比确定假阴性更有害,所以α应该尽可能小,但是也应该相对于用户总数来选择。
最终应用
使用分层聚类模型来确定任何随机选择的账户实际上是否是由共同用户创作的。下面的树形图显示了 40 个用户的作者聚类分析。选择 0.1%的α水平是考虑到被比较的用户相当少,并且发现误报的可能性极小。对所有 3000 名随机选择的用户也进行了聚类,毫不奇怪,他们都不匹配。
解释结果
当应用于一小群用户或者一个很长的匿名文本(超过 1000 条评论)时,所创建的模型非常准确。较小的子编辑的版主肯定可以使用这种方法来查看他们的用户,看看他们中是否有属于被禁止用户的备用帐户。对于具有较大社区的子街道,这将更加困难,除非在进行风格比较之前可以将可能的嫌疑人缩小到较小的群体。
来源
数据是从谷歌大查询获得的
引用的研究:
也非常感谢 Shlomo Argamon ,他在 2008 年首次引入余弦相似度作为用于作者身份识别的最佳距离度量(并在上述资料中引用)。
使用物体检测实现更智能的零售结账体验
用数据做酷事!
我一直在玩 Tensorflow 对象检测 API ,对这些模型的强大感到惊讶。我想分享一些实际用例的 API 的性能。
第一个用例是更智能的零售结账体验。在亚马逊 Go 商店宣布后,这是一个热门领域。
商店可以设计成智能货架,跟踪顾客从里面挑选的商品。我通过构建两个对象检测模型来做到这一点——一个跟踪手并捕捉手拾取的东西。第二个独立的模型监控货架空间。见下图 GIF。通过使用两个模型,你可以最小化单一方法的误差。
Hand Tracking and Inventory Monitoring
计算机视觉在零售结账中的另一个应用是,在结账系统中,不是一件一件地扫描物品,而是将所有东西放在一起,摄像头能够检测和记录所有东西。也许我们甚至不需要收银台。购物车可以安装摄像头,你可以推着购物车走出来,当你走出商店时,购物车会给你开账单。这不是很酷吗!我用 API 设计了一个有 3 个随机物品的“迷你”模型,该模型可以很容易地检测出放置了什么和数量。见下图 GIF。通过各种实验,我发现该 API 甚至在仅部分可见的项目上表现得非常好。
Detection of items with high accuracy
那么我们如何建造它呢?
- 收集数据
可以通过查看在线公开的数据集或创建自己的数据来收集图像。每种方法都有其优点和缺点。我通常混合使用两种。例如,可以通过使用公开可用的数据集,如 Ego Hand 数据集,来构建手部检测器。该数据集在手形、颜色和姿势方面有很多可变性,当该模型应用于现实世界时,这将是有用的。另一方面,对于货架上或购物车中的商品,最好收集您自己的数据,因为我们不希望有太多的可变性,也希望确保从各个方面收集数据。在你建立你的模型之前,通过使用像 PIL 和 OpenCV 这样的图像处理库来增加你的数据来创建额外的图像总是一个好主意,这些图像在亮度、缩放、旋转等方面有随机的变化。这个过程可以创建大量的额外样本,使模型稳健。
对于对象检测模型,我们需要注释——感兴趣对象周围的边界框。为此,我使用了标签。用 Python 写的,接口用 Qt。这是一个非常方便的工具,注释是以 Pascal VOC 格式创建的,这使得使用 Tensorflow Github — [create_pascal_tf_record](https://github.com/tensorflow/models/blob/master/research/object_detection/create_pascal_tf_record.py).py
和[create_pet_tf_record.py](https://github.com/tensorflow/models/blob/master/research/object_detection/create_pet_tf_record.py).
中共享的脚本创建 TFRecord 文件变得很容易
2。建立模型
我写过一个非常详细的教程,关于在你的自定义数据集上训练 Tensorflow 物体检测 API——用 Tensorflow 物体检测 API 构建一个玩具检测器。以及关联的 Github 。请用这个开始。
在构建模型时,您必须做出的一个重大决定是将哪个对象检测模型用作微调检查点。已在 COCO 数据集上接受培训的可用型号的最新列表如下:
Tensorflow COCO Trained Models
黑白速度和准确性之间存在直接的权衡。对于实时手部检测,最好使用 SSD 模型或更快的 RCNN 初始,这是我个人更喜欢的。对于货架或购物车上的物品检测,我更喜欢速度较慢但精度较高的模型,如更快的 RCNN Resnet 或更快的 RCNN Inception Resnet。
3。测试和改进模型
我个人认为真正的工作是在你建立了模型的第一个版本之后才开始的!由于没有一个模型是完美的,当你开始使用它时,你会注意到它的性能差距。然后,您将需要使用您的直觉来决定是否可以填补这些缺口并改进模型,或者是否需要另一个模型或非模型来达到您期望的准确性。如果幸运的话,您只需要添加额外的数据来提高性能。
如果你想了解更多关于物体检测和 Tensorflow 物体检测 API 的知识,请查看我的文章—Google tensor flow 物体检测 API 是实现图像识别最简单的方法吗?
给我一个❤️,如果你喜欢这个职位:)希望你拉代码,并尝试自己。如果你对这个话题有其他想法,请在这篇文章上发表评论,或者给我发邮件,地址是 priya.toronto3@gmail.com
其他著述:【http://deeplearninganalytics.org/blog】T4
PS:我有自己的深度学习咨询公司,喜欢研究有趣的问题。我已经帮助几家初创公司部署了基于人工智能的创新解决方案。请到 http://deeplearninganalytics.org/的来看看我们吧。
如果你有一个我们可以合作的项目,请通过我的网站或 priya.toronto3@gmail.com 联系我
参考文献:
在复杂图像分类场景中使用对象检测第 1 部分:
人工智能计算机视觉革命
TLDR;本系列基于在下面的现实生活代码故事中检测复杂策略的工作。该系列的代码可以在这里找到。
第 1 部分:人工智能计算机视觉革命
计算机视觉的最新发展已经改变了计算机视觉的前景。很多曾经被认为只有科幻才有可能的场景,最近变得和消费一个 API 一样容易。
例如,仅在四年前,以下漫画出现在 XKCD 上。
XKCD September 24th 2014
早在 2014 年,对普通开发人员来说,执行基本图像分类的能力被认为接近科幻小说,仅仅三年后,我们不仅可以确定图片是否是鸟类,我们还可以告诉你鸟类的种类,鸟在图片中的位置,并实时跟踪鸟类的迁徙模式。
Microsoft Developer Blog Bird Detection with Azure ML and Active Learning for Object Detection in Partnership with Conservation Metrics November 6, 2018
实际上,并不是每一个计算机视觉问题都与鸟、花、猫和狗有关。
在本系列中,我们将回顾一个来自零售业的真实世界计算机视觉用例,并比较和对比一些可用于解决该问题的不同方法和技术。
一年多前,微软与中欧和东欧的一家大型糖果产品制造商合作,建立了一个机器学习模型,用于验证经销商是否正确库存巧克力。我们合作的公司在超过 14 个国家拥有庞大的连锁超市分销网络。这些经销商中的每一个都被要求根据标准化的政策来安排他们摊位上的巧克力。每个政策都描述了给定的巧克力应该放在哪个货架上,以及应该以什么样的顺序储存。
执行这些政策的“例行”审计活动会产生巨大的成本。我们的合作伙伴希望开发一个系统,让审计员或商店经理可以拍一张照片,并立即被告知货架的库存是否正确,如上图所示。
即使采用最先进的方法,这也具有挑战性,原因有很多,例如:
- 图像质量
- 图像角度
- 微妙的违反政策行为
It’s easy for state of the art models to tell that there are no chocolates in this
尽管一些错误存储货架的图像(如上图右侧所示)是公然违反政策的行为,很容易分类,但其他图像(如下面的“违反一次”图像)则更加微妙,更难正确分类。
It is very difficult to for image recognition models to know that this image is off by one chocolate.
系列挑战
在本系列中,我们将构建一个复杂的策略分类器,类似于上述场景中的分类器。对于要有效存储的图像,必须按以下顺序存储。
- 顶层搁板(苏打水)
- 中间搁板(果汁)
- 底部搁板(水)
On the left we have a Valid Image on the Right we have an Invalid Image
这个问题的潜在技术解决方案
微软提供了一系列人工智能服务,可以用来解决像这样的计算机视觉任务,每个解决方案都可以在 Azure 上运行。
从 Azure Cognitive Services 到 Azure DSVM 和 Azure Machine Learning 每种技术和方法都有不同的优势和权衡,符合计算机视觉用例的范围。
认知服务
Azure 认知服务是一组开箱即用的 API 和高级服务,帮助您在不需要任何领域经验的情况下开始人工智能任务。
Azure DSVM & ML 服务
数据科学虚拟机是 Azure 上的一个虚拟机(VM)映像。它已经安装和配置了许多流行的数据科学和深度学习工具。这些图片附带了流行的数据科学和机器学习工具,包括 Microsoft R Server Developer Edition、Microsoft R Open、Anaconda Python、Julia、Jupyter notebooks、Visual Studio Code、RStudio、xgboost 等等。您可以配置一个 DSVM,而不是自己部署一个类似的工作区,从而在安装、配置和软件包管理过程中节省大量时间。一旦部署了 DSVM,您就可以立即开始您的数据科学项目。
有关 DSVM 的更多信息,请参见在 Azure 上使用 Linux 数据科学虚拟机的数据科学
Azure Machine Learning Service
Azure ML 服务 让你可以更快地建立和训练机器学习模型,并通过 Azure 机器学习服务轻松部署到云或边缘。使用最新的开源技术,如 TensorFlow、PyTorch 或 Jupyter。在本地进行试验,然后通过云中支持 GPU 的大型集群快速向上或向外扩展。通过自动化机器学习和超参数调整加快数据科学的发展。使用集成的 CI/CD 工具跟踪您的实验、管理模型并轻松部署。
深度学习框架和 Keras
通常,深度学习工程师不会完全手动实现矩阵代数运算。相反,他们使用 PyTorch 或 TensorFlow 等框架。Keras 是用 Python 编写的开源神经网络库。它能够在 TensorFlow、微软认知工具包或 Theano 上运行。旨在实现深度神经网络的快速实验,它专注于用户友好、模块化和可扩展。维基百科(一个基于 wiki 技术的多语言的百科全书协作计划ˌ也是一部用不同语言写成的网络百科全书ˌ 其目标及宗旨是为全人类提供自由的百科全书)ˌ开放性的百科全书
[## 在数据科学虚拟机上使用 Jupyter、Docker 和 PyTorch 进行交互式深度学习…
学习使用 Jupyter、PyTorch 和数据科学虚拟机来训练深度学习模型。
docs.microsoft.com](https://docs.microsoft.com/learn/modules/interactive-deep-learning/?WT.mc_id=medium-blog-abornst)
在接下来的系列文章中,我们将探索其中的一些技术和架构,以解决我们自己复杂的策略分类场景。
系列目标
在本系列结束时,您应该更好地
- 了解人工智能计算机视觉领域
- 能够构建和部署定制的愿景服务模型
- 用 Keras 和迁移学习建立 CNN 图像识别模型
- 使用 VoTT 和 FasterRCNN 来注释和训练用于复杂分类场景的对象检测模型
- 使用 Azure ML 服务和 ACI 在云上训练和部署定制的计算机视觉模型
资源
[## Pythic Coder 推荐的 Azure 机器学习入门内容
Tldr 由于 DevOps 资源上的帖子很受欢迎,而且很难找到文档,所以我…
medium.com](https://medium.com/microsoftazure/the-pythic-coders-recommended-content-for-getting-started-with-machine-learning-on-azure-fcd1c5a8dbb4) [## aribornstein -概述
@ pythiccoder。aribornstein 有 68 个存储库。在 GitHub 上关注他们的代码。
github.com](https://github.com/aribornstein) [## 认知服务|微软 Azure
微软 Azure Stack 是 Azure 的扩展——将云计算的灵活性和创新性带到您的…
azure.microsoft.com](https://azure.microsoft.com/en-us/services/cognitive-services/?v=18.44a)
下一个帖子
本系列的下一篇文章将讨论如何使用定制视觉服务完成这项任务。后续文章将讨论以下内容:
- 【Keras CNNs、MobileNet 和迁移学习的政策识别
- 使用 Keras RetinaNet 进行策略检测
- 使用 Azure ML 服务在云上训练和计算机视觉模型
- 使用 Azure 机器学习在远程集群上训练计算机视觉模型
如果您有任何问题、评论或希望我讨论的话题,请随时在 Twitter 上关注我。如果您认为我错过了某个里程碑,请告诉我。
关于作者
亚伦(阿里)博恩施泰因 是一个狂热的人工智能爱好者,对历史充满热情,致力于新技术和计算医学。作为微软云开发倡导团队的开源工程师,他与以色列高科技社区合作,用改变游戏规则的技术解决现实世界的问题,然后将这些技术记录在案、开源并与世界其他地方共享。
将对象检测用于复杂的图像分类场景第 2 部分:
定制视觉服务
TLDR;本系列基于在下面的现实生活代码故事中检测复杂策略的工作。该系列的代码可以在这里找到。
第二部分:定制视觉服务
在系列文章的最后一篇文章中,我们概述了复杂图像分类任务的挑战。在这篇文章中,我们将介绍并评估 Azure Custom Vision 服务作为解决我们挑战的技术。
定制视觉服务为您的应用带来了机器学习的力量
自定义视觉服务是一个用于构建自定义图像分类器的工具。它使得构建、部署和改进图像分类器变得简单而快速。我们提供了一个 REST API 和一个 web 界面来上传您的图像和训练。
定制视觉服务能做好什么?
定制视觉服务是一个工具,用于构建定制图像分类器,并随着时间的推移使它们变得更好。例如,如果你想要一个可以识别“雏菊”、“水仙花”和“大丽花”图像的工具,你可以训练一个分类器来做这件事。为此,您可以为每个想要识别的标签提供定制视觉服务。
当您尝试分类的项目在您的图像中很突出时,定制视觉服务效果最佳。自定义视觉服务进行“图像分类”,但不进行“对象检测”这意味着自定义视觉服务识别图像是否属于特定对象,但不识别该对象在图像中的位置。
创建一个分类器只需要很少的图像— 每个类 30 张图像就足够启动你的原型。Custom Vision Service 使用的方法对差异具有很强的鲁棒性,这使您可以用很少的数据开始原型制作。但是,这意味着定制视觉服务不太适合您想要检测非常细微差异的情况(例如,质量保证情况下的微小裂缝或凹痕)。)
定制视觉服务旨在简化分类器的构建,并帮助您随着时间的推移提高分类器的质量。
开发者还可以导出他们的模型,在边缘设备上运行,比如 iOS、Android 或 RaspberryPi
下面的步骤将指导您使用 python 和自定义视觉服务为我们的挑战训练一个模型。
以下步骤基于文档快速入门。
准备密钥:
通过登录自定义视觉服务并导航至如下所示的帐户设置,获取您的培训和预测密钥。
创建自定义视觉服务项目
接下来,我们需要使用您在上面获得的密钥,有计划地创建一个新的定制视觉服务项目。
**from** **azure.cognitiveservices.vision.customvision.training** **import** CustomVisionTrainingClient
**from** **azure.cognitiveservices.vision.customvision.training.models** **import** ImageUrlCreateEntryENDPOINT = "https://southcentralus.api.cognitive.microsoft.com"*# Replace with a valid key*
training_key = '' *#<your training key>*
prediction_key = '' *#<your prediction key>*trainer = CustomVisionTrainingClient(training_key, endpoint=ENDPOINT)*# Create a new project*
print("Creating Beverage Policy Classifier...")
project = trainer.create_project("Beverage Policy Classifier")
Creating Beverage Policy Classifier...
将标签添加到项目中
运行以下代码,为我们的有效和无效示例创建标签
*# Make two tags in the new project*
valid_tag = trainer.create_tag(project.id, "Valid")
invalid_tag = trainer.create_tag(project.id, "Invalid")
将图像上传到项目
要将图像添加到项目中,请在标记创建后插入以下代码。这将上传带有相应标签的图像。
print ("Adding images...")**import** **os**
**from** **multiprocessing.dummy** **import** Pool **as** ThreadPool**def** upload(filepath):
**with** open(filepath, mode="rb") **as** img_data:
**if** "Invalid" **in** filepath:
trainer.create_images_from_data(project.id, img_data.read(), [ invalid_tag.id ])
**else**:
trainer.create_images_from_data(project.id, img_data.read(), [ valid_tag.id ])
print('.', end='')
**def** upload_parallel(filepaths, threads=5):
pool = ThreadPool(threads)
results = pool.map(upload, filepaths)
pool.close()
pool.join()valid_dir = "dataset/Beverages/Train/Valid/"
invalid_dir = "dataset/Beverages/Train/Invalid/"
valid_paths = [valid_dir + os.fsdecode(image) **for** image **in** os.listdir(os.fsencode(valid_dir))]
invalid_paths = [invalid_dir + os.fsdecode(image) **for** image **in** os.listdir(os.fsencode(invalid_dir))]
img_paths = valid_paths + invalid_paths
upload_parallel(img_paths)print("Added **{}** images, **{}** valid, **{}** invalid.".format(len(img_paths), len(valid_paths), len(invalid_paths)))
Adding images...
....................................................................................................................................................................................Added 180 images, 90 valid, 90 invalid.
培训项目
既然我们已经向项目添加了标签和图像,我们就可以训练它了。这将创建项目中的第一个迭代。然后我们可以将这个迭代标记为默认迭代。
**import** **time**print ("Training...")
iteration = trainer.train_project(project.id)
**while** (iteration.status == "Training"):
iteration = trainer.get_iteration(project.id, iteration.id)
print ("Training status: " + iteration.status)
time.sleep(1)
Training...
Training status: Training
Training status: Training
Training status: Training
Training status: Training
Training status: Training
Training status: Training
Training status: Training
Training status: Training
Training status: Training
Training status: Completed
评估模型
现在让我们在服务从未见过的本地测试数据集上评估模型。
从azure . cognitive services . vision . custom vision . prediction导入customvisionpedictionclient
*# Now there is a trained endpoint, it can be used to make a prediction*predictor = CustomVisionPredictionClient(prediction_key, endpoint=ENDPOINT)project_id = project.id
**def** predict(filepath):
**with** open(filepath, mode="rb") **as** test_data:
results = predictor.predict_image(project_id, test_data.read(), iteration.id)
pred = max(results.predictions, key=**lambda** x:x.probability).tag_name
true ='Invalid' **if** "Invalid" **in** filepath **else** 'Valid'
print('.', pred , end='')
**return** (true, pred)
**def** predict_parallel(filepaths, threads=2):
pool = ThreadPool(threads)
results = pool.map(predict, filepaths)
pool.close()
pool.join()
**return** zip(*results)test_valid_dir = "dataset/Beverages/Test/Valid/"
test_invalid_dir = "dataset/Beverages/Test//Invalid/"
test_valid_paths = [test_valid_dir + os.fsdecode(image) **for** image **in** os.listdir(os.fsencode(test_valid_dir))]
test_invalid_paths = [test_invalid_dir + os.fsdecode(image) **for** image **in** os.listdir(os.fsencode(test_invalid_dir))]
test_img_paths = test_valid_paths + test_invalid_pathsy_true, y_pred = predict_parallel(test_img_paths)
. Valid. Valid. Valid. Valid. Valid. Valid. Valid. Valid. Valid. Valid. Valid. Valid. Valid. Valid. Valid. Valid. Valid. Valid. Valid. Valid. Valid. Valid. Valid. Valid. Valid. Valid. Valid. Valid. Invalid. Valid. Invalid. Valid. Invalid. Invalid. Invalid. Invalid. Invalid. Invalid. Invalid. Invalid. Invalid. Invalid. Invalid. Invalid. Invalid. Invalid. Invalid. Invalid. Invalid. Invalid. Invalid. Invalid. Invalid. Invalid. Invalid. Invalid. Invalid. Invalid. Invalid. Invalid
第七步:基准测试
**from** **utils** **import** classification_report
classification_report(y_true, y_pred)
precision recall f1-score support Invalid 1.00 1.00 1.00 30
Valid 1.00 1.00 1.00 30 micro avg 1.00 1.00 1.00 60
macro avg 1.00 1.00 1.00 60
weighted avg 1.00 1.00 1.00 60Confusion matrix, without normalization
[[30 0]
[ 0 30]]
Normalized confusion matrix
[[1\. 0.]
[0\. 1.]]
结论
正如您所看到的,custom vision 服务是一个很好的工具,可以用相对较少的数据和较少的工作生成强大的模型。在大多数用例中,它是一个 go too 工具,也是一个很好的起点。当您尝试分类的项目在您的图像中很突出时,定制视觉服务效果最佳。
该服务是任何分类任务的起点。但是,在为生产进行部署时,有几个事项需要考虑。
- 服务失败的情况有哪些(可能是 2%的错误情况在生产中出现 30%,或者是您的用例中要考虑的最重要的情况。
- 您正在对多少个类别/策略进行分类(2-10 个策略的模型效果更好)
- 你的任务是什么领域?
资源
[## Pythic Coder 推荐的 Azure 机器学习入门内容
Tldr 由于 DevOps 资源上的帖子很受欢迎,而且很难找到文档,所以我…
medium.com](https://medium.com/microsoftazure/the-pythic-coders-recommended-content-for-getting-started-with-machine-learning-on-azure-fcd1c5a8dbb4) [## aribornstein —概述
@ pythiccoder。aribornstein 有 68 个存储库。在 GitHub 上关注他们的代码。
github.com](https://github.com/aribornstein) [## 认知服务|微软 Azure
微软 Azure Stack 是 Azure 的扩展——将云计算的灵活性和创新性带到您的…
azure.microsoft.com](https://azure.microsoft.com/services/cognitive-services/?v=18.44a&v=18.44a&v=18.44a&WT.mc_id=medium-blog-abornst)
下一篇文章
本系列的下一篇文章将回顾如何通过 Keras CNNs、MobileNet 和迁移学习的政策认可来完成这项任务后续文章将讨论以下内容:
- 使用 Keras RetinaNet 进行策略检测
- 使用 Azure ML 服务在云上训练和计算机视觉模型
- 使用 Azure 机器学习在远程集群上训练计算机视觉模型
如果您有任何问题、评论或希望我讨论的话题,请随时关注我的 Twitter 如果您认为我错过了某个里程碑,请告诉我。
关于作者
亚伦(阿里)博恩施泰因 是一个狂热的人工智能爱好者,对历史充满热情,致力于新技术和计算医学。作为微软云开发倡导团队的开源工程师,他与以色列高科技社区合作,用改变游戏规则的技术解决现实世界的问题,然后将这些技术记录在案、开源并与世界其他地方共享。