TowardsDataScience-博客中文翻译-2019-五十八-
TowardsDataScience 博客中文翻译 2019(五十八)
情感分析:无以言表

任何纽约移民都知道,在纽约之外很难找到好吃的披萨。加上意大利的家庭传统,好的披萨是一件大事。因此,当我努力开发一个自然语言处理项目来探索情感分析工具时,还有什么比帮助像我这样的人找到完美的比萨饼店更好的使用这些工具呢!
在这个项目中,我使用了来自 Yelp 挑战数据集的餐馆评论。特别是,我使用了 Yelp 归类为“披萨餐厅”或“餐厅,披萨”类别的餐厅。
我知道食客喜欢和不喜欢餐馆的原因各不相同,但我对用户给某个地方的评分并不感兴趣。相反,我想了解就餐者喜欢和不喜欢的特定方面,这样潜在的就餐者就可以更好地了解这个地方是否符合他们的需求。这对于那些获得 3-4 星评价的餐厅来说尤其重要,这些餐厅可能会因为各种不同的原因获得这一评级。
例如,也许你正在寻找一片地道的纽约式比萨饼。在这种情况下,服务和及时性可能就不那么重要了,只要厨师能送货上门。另一方面,也许你正在为朋友的生日计划一次集体聚餐。在这种情况下,服务和氛围对体验非常重要——如果你有一些挑食或对食物敏感的朋友,那么菜单上提供的食物选择不仅仅是披萨。最后,如果你正在午休,但渴望任何形式的融化奶酪,食物质量和氛围可能没有食物准备的及时性重要。
那么如何为披萨店获得这种基于方面的情感呢?首先,我将每个评论拆分成句子,并使用 spaCy 和 gensim 获得评论者在每个句子中提到的不同主题(即,食品质量、服务、等待时间、氛围和菜单品种)。一旦我有了自己的主题(我将把主题建模留给另一个博客),我需要弄清楚评论者对餐馆的这一方面是积极还是消极。这篇文章比较了两种模拟评论者情绪的方法:VADER 和 StanfordCoreNLP。
与 VADER 的感情得分
首先,我尝试了 VADER 情绪包,并定义了一个函数 perspective _ analyzer _ scores()来返回从-1(非常负面)到 1(非常正面)的整体情绪评级。
from vaderSentiment.vaderSentiment import SentimentIntensityAnalyzer
import re
import stringanalyzer = SentimentIntensityAnalyzer()def sentiment_analyzer_scores(text):
score = analyzer.polarity_scores(text)
print(text)
print(score)
我尝试的第一句话非常简单:“这个地方的食物和氛围都非常棒”。这篇评论显然是正面的,果然,VADER 综合情绪得分为 0.84。到目前为止一切顺利。
text_pos = 'this place was amazing great food and atmosphere'
sentiment_analyzer_scores(text_pos)

VADER 在这个非常简单的负面评价中也表现不错,综合得分为-0.66:
text_neg = 'i didnt like their italian sub though just seemed like lower quality meats on it and american cheese'
sentiment_analyzer_scores(text_neg)

然而,在这个更微妙的例子中,它卡住了。以“所有东西对我来说都像垃圾,但我们不断回来,因为我的妻子喜欢意大利面”为例。这位评论者显然不喜欢这家餐厅,尽管他或她的妻子“喜欢”意大利面(附带说明,这位评论者应该赢得年度配偶奖,因为他继续吃垃圾来取悦妻子!)任何带有垃圾这个词的美食评论都应该立刻遭到否定,但 VADER 给出了非常正面的 0.7 分。
text_amb = "everything tastes like garbage to me but we keep coming back because my wife loves the pasta"
sentiment_analyzer_scores(text_amb)

发生了什么事?下面这个函数返回一个单词列表,VADER 把这些单词分为积极的、中性的和消极的。根据自述文件,“VADER (Valence Aware 字典和情感推理器)是一个词汇和基于规则的情感分析工具,专门针对社交媒体中表达的情感。”因此,它依赖于某些单词的极性来确定整体情绪。
import nltk
nltk.download('punkt')
nltk.download('vader_lexicon')
from nltk.tokenize import word_tokenize, RegexpTokenizerdef get_word_sentiment(text):
tokenized_text = nltk.word_tokenize(text)
pos_word_list=[]
neu_word_list=[]
neg_word_list=[]for word in tokenized_text:
if (analyzer.polarity_scores(word)['compound']) >= 0.1:
pos_word_list.append(word)
elif (analyzer.polarity_scores(word)['compound']) <= -0.1:
neg_word_list.append(word)
else:
neu_word_list.append(word)print('Positive:',pos_word_list)
print('Neutral:',neu_word_list)
print('Negative:',neg_word_list)
正如下面的输出所示,单词“loves”和“like”的正极性必须相当高。此外,如果没有对这个句子的更广泛的句法理解,将这个句子注册为否定的唯一单词是“垃圾”。在这种情况下,“垃圾”被认为是中性的,整体文本被确定为相当积极的。
get_word_sentiment(text_amb)

进入斯坦福核心 NLP
斯坦福的核心 NLP 程序正好有这个问题的解决方案,因为它是在电影评论上训练的,其中评论者可能会在同一个句子中讨论电影的积极和消极方面(例如,“情节缓慢,但表演很棒”)。
据该网站称,该模型“实际上是基于句子结构建立了一个完整句子的表示,而不是看单个单词的情绪。它根据单词如何组成更长短语的意思来计算情感。这样,模型就不像以前的模型那样容易被愚弄了。”
完美!幸运的是,有一个 Python 包装器可以让您调用核心 NLP 服务器(它返回结果的速度惊人地快)。要进行调用,您需要 pip 安装 pycorenlp,并从 pycorenlp 导入 StanfordCoreNLP。然后,在终端中,将 cd 放入 Stanford CoreNLP 文件夹,并使用以下命令启动服务器:
cd stanford-corenlp-full-2018-10-05
java -mx5g -cp "*" edu.stanford.nlp.pipeline.StanfordCoreNLPServer -timeout 10000
太好了——现在让我们看看效果如何。
#!pip install pycorenlp
from pycorenlp import StanfordCoreNLPnlp = StanfordCoreNLP('[http://localhost:9000'](http://localhost:9000'))def get_sentiment(text):
res = nlp.annotate(text,
properties={'annotators': 'sentiment',
'outputFormat': 'json',
'timeout': 1000,
})
print(text)
print('Sentiment:', res['sentences'][0]['sentiment'])
print('Sentiment score:', res['sentences'][0]['sentimentValue'])
print('Sentiment distribution (0-v. negative, 5-v. positive:', res['sentences'][0]['sentimentDistribution'])
在将食物视为垃圾的评论中,该模型将整个句子分类为非常负面(0 是最负面的,4 是最正面的)。情绪分布显示,这句话有一些中性甚至积极的方面,但总体来说这不是一个好的评估。
get_sentiment(text_amb)

还有一个酷直播演示,展示了该模型如何将句子的不同点解析为积极和消极的方面:

http://nlp.stanford.edu:8080/sentiment/rntnDemo.html
为了更好地衡量,我将把上面的肯定句和否定句放进去:
get_sentiment(text_pos)

get_sentiment(text_neg)

这就是你要的,一个细致入微的情感分析包,非常适合评论电影、书籍、消费品和…披萨!
我应该指出,这篇文章绝不是对 VADER 的批评——它有一些很棒的功能,例如它能够识别社交媒体口语(“LOL”,表情符号),并从所有大写字母和标点符号中提取重点。相反,我的目的是强调一种情绪分析工具,它非常适合包含积极和消极方面的客户评论。
我希望这篇文章对你有所帮助,欢迎在评论中提出任何反馈或问题!
酒店评论的情感分析

Photo by Marten Bjork on Unsplash
不管你喜不喜欢,客人评论正成为影响人们预订/购买的一个突出因素。
想想你过去的经历。当你在 Expedia/Booking/猫途鹰寻找一个度假的地方时,你做了什么?我敢打赌,在你知道之前,你会滚动屏幕查看评论。
作为一名企业主或员工,如果你仍然怀疑客人评论对企业的影响有多重要,不妨看看一些统计数据:
换句话说,客人的评论显然会影响人们的预订决定,这意味着,你最好注意人们对你的酒店的评论!
你不仅想要好的评论,还希望它们能帮助你最大程度地了解你的客户。评论可以告诉你是否符合客户的期望,这对于根据客户的角色制定营销策略至关重要。
点评很重要,作为酒店老板,你需要开始利用它。
什么是情感分析
情感分析,也称为观点挖掘,是一种文本挖掘技术,可以提取给定文本的情感——无论是积极的、消极的还是中性的,并返回一个情感分数。这种技巧通常用于评论或社交媒体文本。
在本文中,我将向您展示如何使用 网络抓取工具有效地收集酒店评论,并使用 Python* 进行情感分析。*
使用 Octoparse 抓取评论
我使用的网页抓取工具叫做octoporparse。这是一个为像我一样没有编码背景的人建立的自己动手的网络刮刀。我将向您展示如何使用 Octoparse 来收集纽约市排名第一的酒店——由图书馆酒店收藏的长颈鹿酒店的评论。
以下是该网页的链接:
https://www . tripadvisor . com/Hotel _ Review-g 60763-d 99762-Reviews-Hotel _ 长颈鹿 _ by _ 图书馆 _ 酒店 _ 收藏-New _ York _ City _ New _ York . html #点评
首先,我们将在 Octoparse 中导入我们的目标网址。

注意每页只有 5 条评论,所以如果我们需要浏览所有评论,我们需要让 Octoparse 对所有评论页面进行分页。

怎么会?当我们仔细查看评论时,我们可以看到一些评论上有一个“阅读更多”按钮。在这种情况下,我们的爬虫需要在提取之前点击按钮来加载整个评论。

接下来,我们循环遍历所有评论项,并提取每个评论。
最后但同样重要的是,将新创建的“循环项目”拖出来,放在第一个“循环项目”的下面。这是因为我们想在提取实际评论之前先点击所有的“阅读更多”。

一旦我们成功提取了该酒店的所有评论,我们就可以使用 Python 获得每个评论的情感分数了。

小贴士:如果你想获得更详细的关于搜集访客评论的分步指南,可以看看这篇 帖子 。
用 Python 进行情感分析
首先,我们要导入库。这里我们将使用两个库进行分析。
第一个叫 pandas ,是一个开源库,用 Python 提供了简单易用的数据结构和分析函数。
第二个我们要使用的是 Python 中一个强大的库,叫做【NLTK】。NLTK 代表自然语言工具包,这是一个常用的 NLP 库,有很多语料库、模型和算法。

让我们继续导入刮掉的评论。

下面,我们应用了来自 nltk .情操.维德包的一个名为SentimentIntensityAnalyzer()的函数。情感分析器可以利用 NLTK 算法和特性实现和促进情感分析任务,因此无需复杂编码即可生成情感得分。在使用它之前,我们需要调用它。

现在我们已经调用了函数,应用它来生成极性得分。有四种类型的分数:负面的、中性的、正面的和复合的。通过使用 apply() 和 lambda ,我们可以转换结果并将它们放入“reviews”数据框架中。

然后我们有每个评论的情感分数。

每个评论都有一个负面、中性、正面和复合分数。复合得分是前三项得分的综合评定。这个分数从-1 到 1 不等。通常我们会设置一个复合分数的阈值来识别情感。这里我们可以将阈值设置为 0.2。如果评论的综合得分大于 0.2,则该评论是正面的。如果一个评论的综合得分小于 0.2,则该评论是负面的。如果复合得分在-0.2 到 0.2 之间,那么复习就是神经的。

我们可以看到,97.2%的评价是正面的,只有 1.22%的评价是负面的。根据分析,可以肯定地说,图书馆酒店收藏的长颈鹿酒店是一家广受欢迎的酒店。
当然,我们还可以做更多的事情来进一步分析评论,例如:
- 构建一个词云或话题建模模型,识别人们喜爱这家酒店的关键原因是什么。
- 通过提取其他酒店的点评,按照上述步骤进行分析,与其他酒店进行情感评分对比。
- 提取更多信息,如审查日期、审查者的贡献、审查者的有益投票、审查有益投票、份额数量等,将其可视化并应用业务分析方法。
你现在知道评论对你的事业成功有多重要了。为什么不去 Octoparse 亲自尝试一下呢?Octoparse 是一款易于使用的网络抓取工具,可以帮助你点击一下就将网站转化为结构化数据。更好的是,有针对不同流行网站的现成模板和终身免费版本。如果你的网络抓取相关项目需要任何帮助,请随意评论!
原载于 2019 年 9 月 11 日https://www.octoparse.com。
情感分析很难,但 AI 可能有答案。

Image by Magic Creative from Pixabay
最近,当我在亚马逊上购买一个笔记本电脑包时,我偶然发现了一条非常有趣的顾客评论:
“这是有史以来最好的电脑包。它太好了,用了不到两个月,就配当杂货袋用了。”
评论中固有的讽刺是显而易见的,因为用户对包的质量不满意。然而,由于该句包含“最好”、“好”和“值得”等词,该评论很容易被误认为是积极的。这种幽默但隐晦的评论在社交媒体上传播是一种常见现象。如果这种反应没有被发现并采取行动,可能会损害公司的声誉,特别是如果他们计划举行新的发布会。检测评论中的讽刺是自然语言处理的一个重要用例,我们将看到机器学习如何在这方面有所帮助。
情感分析:从非结构化数据中获得重要见解

Source: 5 ways sentiment analysis can boost your business
在我们进入讽刺检测的本质之前,让我们试着对情感分析有一个整体的概述。
情感分析,也被称为观点挖掘,是自然语言处理的一个子领域,试图从给定的文本中识别和提取观点。
早些时候,公司依靠调查和焦点小组研究等传统方法来获得消费者的反馈。然而,机器学习和人工智能支持的技术使得分析来自各种来源的文本更加准确和精确成为可能。不用说,从文本中提取情感的能力是一个非常有价值的工具,有可能极大地提高许多企业的投资回报率。
情感分析的重要性

Advantages of Sentiment Analysis in driving Business
时空洞察的首席技术官保罗·霍夫曼曾经说过“如果你想理解人们,尤其是你的客户……那么你必须具备强大的文本分析能力”。我们完全同意 Paul 的观点,因为文本分析给企业带来的力量在最近几年已经非常明显。随着社交媒体活动的激增,从商业角度来看,情感被视为有价值的商品。通过仔细衡量人们的意见和情绪,公司可以合理地了解人们对产品的看法,并相应地纳入反馈。
讽刺:用积极的词语表达消极的情绪
情感分析不是一项容易完成的任务。文本数据通常预先加载了大量噪声。讽刺是一种天生存在于社交媒体和产品评论中的噪音,可能会干扰结果。
讽刺性的文章展示了一种独特的行为。与简单的否定不同,讽刺性的句子只使用词语的正面含义来表达负面情绪。这里有几个明显带有讽刺意味的例子。

source: Semi-Supervised Recognition of Sarcastic Sentences in Online Product Reviews
情感分析很容易被这种讽刺性词语的存在误导,因此,讽刺检测是许多 NLP 任务中至关重要的预处理步骤。在为 NLP 应用训练模型之前,识别并去除噪声样本是有用的。
使用无人驾驶人工智能(DAI)的讽刺检测
无人驾驶 AI 是来自 H2O.ai 的自动机器学习产品。它配备了用于文本分类和回归问题的自然语言处理(NLP)方法。该平台支持独立文本和具有其他数值的文本作为预测特征。以下配方和模型已在 DAI 中实施:

Key Capabilities of the Driverless AI NLP Recipe
该平台使用 TFIDF、CNN 和 GRU 等强大的技术自动将文本字符串转换为特征。通过 TensorFlow,无人驾驶 AI 还可以处理更大的文本块,并使用所有可用的数据建立模型来解决商业问题。无人驾驶人工智能具有最先进的情感分析 NLP 能力,我们将利用它来建立一个讽刺检测分类器。
资料组
该数据集由互联网评论网站 Reddit 的 130 万条讽刺评论组成,分为讽刺和非讽刺两类。数据集的来源是一篇论文,题目是:“ 一个大型的自我注释讽刺语料库 ”。数据集的处理版本也可以在 Kaggle 上找到,让我们在运行各种分类算法之前探索一下数据集。
导入数据

数据集包含一百万行,每条记录包含十个属性:

我们主要对以下两列感兴趣:
label:0用于讽刺性评论,1用于非讽刺性评论comment:将用于运行实验的文本栏
探索性数据分析
数据集非常平衡,讽刺和非讽刺的推特数量相等。

讽刺和正常评论的长度分布也几乎相同。


Distribution of Sarcastic vs Non-Sarcastic Comments
由于数据集已被转换为表格格式,它已准备好输入无人驾驶人工智能。请注意,文本特征将在特征工程过程中自动生成和评估
启动实验
我们将分三部分进行实验,以获得尽可能好的结果。
- 内置 TF/IDF NLP 配方
在第一部分中,我们将使用 DAI 的内置 TF/IDF 功能。
如果你想更新一下关于无人驾驶人工智能入门的知识,请随时参加 试驾 。试驾是 H2O 在 AWS 云上的无人驾驶人工智能,你可以探索它的所有功能,而不必下载它。
重新开始戴的生活。接下来,以 70:30 的比例将数据集分成训练集和测试集,并将label指定为目标列。我们还将取消选择所有其他列,只保留数据集中的comment列。最后,选择LogLoss作为评分者,其他参数保持默认,开始实验。屏幕应该如下所示:

Sentiment Analysis with built-in NLP recipes
- 内置 Tensorflow NLP 配方
作为替代,我们将启动相同实验的另一个实例,但是使用 Tensorflow 模型。这是因为 TextCNN 依赖于张量流模型。点击“Expert Settings’选项卡,打开‘TensorFlow Models’。其余的过程保持不变。

Sentiment Analysis with built-in Tensorflow recipes
- 带自定义人气食谱
如果内置的配方还不够,那么就有必要构建我们自己的配方,专注于我们的特定用例。DAI 的最新版本(1.7.0)实现了一个名为 BYOR 的关键功能,代表 【自带菜谱】。该功能旨在使数据科学家能够根据其业务需求定制 DAI。你可以在这里阅读更多关于这个功能的信息。
要上传定制配方,请进入专家设置并上传所需配方。H2O 已经构建并开源了超过 80 个食谱可以作为模板。这些食谱可以从 https://github.com/h2oai/driverlessai-recipes获得。对于这个实验,我们将使用下面的配方:
- 使用来自 TextBlob 的预训练模型从文本中提取情感。
TextBlob 是一个 python 库,提供了一个简单的 API 来访问它的方法和执行基本的 NLP 任务。它可以执行许多 NLP 任务,如情感分析、拼写检查、摘要创建、翻译等。点击专家设置选项卡并导航至driverlessai-recipes > transformers > nlp并选择所需的制作方法。点击save保存设置。

接下来,您还可以选择特定的变压器并取消选择其余的变压器。

实验结果总结
下面的截图显示了不同配方的戴的三个实例之间的比较。自定义配方的引入将 Logloss 组件从 0.54 减少到 0.50,当转换到业务领域时,可以具有巨大的价值。

一旦实验完成,用户可以进行新的预测,并下载评分管道,就像其他任何无人驾驶 AI 实验一样。
结论
情感分析在营销领域可以发挥至关重要的作用。它可以帮助创建有针对性的品牌信息,并帮助公司了解消费者的偏好。对于一家公司来说,这些见解对于扩大其在一系列行业的影响力至关重要。
Python 中 Anthem 游戏发布的情感分析
视频游戏的发布受到戏剧的困扰。从误导性的预购捆绑包,到发布时远未完成的游戏,大型发行商在决定游戏发布的方式和时间时有相当大的风险要管理。我认为这可能是一个有趣的项目,看看一个游戏在推出期间的情绪变化,AAA 冠军国歌是这个小项目的完美游戏。(仅供参考,我在 2 月 22 日游戏正式发布前写这篇文章)
在我们开始之前,Anthem 有一个独特的发布时间表,可能会影响个人对游戏的看法。
- Anthem 从 2 月 1 日星期五到 2 月 3 日星期日有一个“演示周末”
- Anthem 于 2 月 15 日星期五“提前”面向 EA Access 成员推出
- Anthem 将于 2 月 22 日正式面向所有人发布
所以让我们开始吧!
我将尽力描述我使用的所有软件包,但这里有我的重要陈述供参考!
%matplotlib inline
from twitterscraper import query_tweets
from vaderSentiment.vaderSentiment import SentimentIntensityAnalyzer
import datetime as dt
import pandas as pd
from langdetect import detect
import matplotlib.pyplot as plt
import seaborn as snsanalyzer = SentimentIntensityAnalyzer()
抓取 Twitter 数据
从 twitter 获取数据的方法有一百万种,但我选择使用 taspinar 的软件包twitterscraper,它是 beautifulSoup 的一个包装器,使获取 tweets 变得容易。我喜欢这个包,不仅因为你不必向 twitter 请求 API 密钥,还因为 webscraper 没有 Twitter API 的任何限制。
有了from twitterscraper import query_tweets,获取我需要的所有推文变得简单了。
begin_date = dt.datetime.strptime("Jan 15 2019", "%b %d %Y").date()
tweets = query_tweets("#Anthem OR #AnthemGame",begindate=begin_date)
这需要一些时间…所以在你等待的时候去攻克一个要塞吧!完成后,你应该有一个很长的 twitterscraper 对象列表。
print(len(tweets))
102418
102418 条关于 Anthem 的推文!
Twitterscraper 对象有一个易于使用的 dict 方法,它以字典的形式返回我们想要的所有 tweet 数据。
tweets[1].__dict__{'user': '_WECKLESS',
'fullname': 'WECKLESS™',
'id': '1085323189738192897',
'url': '/_WECKLESS/status/1085323189738192897',
'timestamp': datetime.datetime(2019, 1, 15, 23, 49, 47),
'text': 'Here is my GIF #AnthemGame pic.twitter.com/jCNjiWQnmJ',
'replies': 0,
'retweets': 0,
'likes': 2,
'html': '<p class="TweetTextSize js-tweet-text tweet-text" data-aria-label-part="0" lang="en">Here is my GIF <span class="twitter-hashflag-container"><a class="twitter-hashtag pretty-link js-nav" data-query-source="hashtag_click" dir="ltr" href="/hashtag/AnthemGame?src=hash"><s>#</s><b><strong>AnthemGame</strong></b></a><a dir="ltr" href="/hashtag/AnthemGame?src=hash"><img alt="" class="twitter-hashflag" draggable="false" src="https://abs.twimg.com/hashflags/AnthemGame_Anthem/AnthemGame_Anthem.png"/></a></span> <a class="twitter-timeline-link u-hidden" data-pre-embedded="true" dir="ltr" href="https://t.co/jCNjiWQnmJ">pic.twitter.com/jCNjiWQnmJ</a></p>'}
我们可以看到这本字典有用户名,唯一的推文 id,回复数,转发数,推文的点赞数,最重要的是推文的文本!我们将把所有这些推文放入一个熊猫数据框架中,这样它们就容易使用了!此时,我还将我的推文保存到一个文件中,这样,如果我想重新运行我的分析,我就不必再次经历网络搜集过程。)
tweet_list = (t.__dict__ for t in tweets)
df = pd.DataFrame(tweet_list)
这是我们新的数据框架的样子!

Notice how some tweets are not in english. We’ll deal with that in the next step!
语言和情感分析
下一步我们要解决两件事。首先,请注意,我们的推文并非都是英文的。虽然我们可以翻译我们的推文,并试图从它们那里获得一些情感,但我认为删除非英语推文会更容易、更干净。要做到这一点,我们需要给每条推文贴上语言标签。别担心,那里有一个图书馆! Langdetect by Mimino66 的 detect 功能就是我们识别推文语言所需要的一切。我们可以用from langdetect import detect载入必要的功能
我们将通过在文本数据上映射 detect()函数,在数据帧中创建一个新列,然后只保留英语的 tweets。
df['lang'] = df['text'].map(lambda x: detect(x))
df = df[df['lang']=='en']
当这一步完成时,我只剩下 77,740 条推文。
现在,我们可以开始对我们的推文进行一些文本分析了。 VADER 情感分析是一个流行的 python 包,用于获取一段文本的情感,它特别适合社交媒体数据,并且随时可以开箱即用!
我们需要导入它的 SentimentIntensityAnalyzer 并初始化它。
from vaderSentiment.vaderSentiment import SentimentIntensityAnalyzer
analyzer = SentimentIntensityAnalyzer()
VADER 会为你传递的任何文本返回一个 4 分的字典;正分数、中性分数、负分数和复合分数,范围从-1 到 1。我们最感兴趣的是追踪推文整体情绪的复合分数。
从这里,我们制作了一系列新的数据,包含我们的推文文本的情感,并将其连接到我们的原始数据帧。
sentiment = df['text'].apply(lambda x: analyzer.polarity_scores(x))
df = pd.concat([df,sentiment.apply(pd.Series)],1)
这是我们最终的数据帧。我们可以看到我们的推文是英文的,每条推文都有一组相关的情感评分。

现在开始分析!
分析情绪
首先,让我们调用df.describe()并获取数据集的一些基本信息。

我们有 77,740 条推文,平均有 10 个赞,35 个回复和 2 个转发。查看复合得分,我们可以看到平均推文是积极的,平均情绪为 0.21。
绘制这些数据会让我们更好地了解它的样子。在我们绘图之前,为了方便使用,我对我的数据框架做了一些更改,按时间戳对所有值进行排序,使它们有序,将时间戳复制到索引以使绘图更容易,并计算复合情感得分的扩展和滚动平均值。
df.sort_values(by='timestamp', inplace=True)
df.index = pd.to_datetime(df['timestamp'])df['mean'] = df['compound'].expanding().mean()
df['rolling'] = df['compound'].rolling('6h').mean()
现在使用 matplotlib 和import matplotlib.pyplot as plt,我们可以创建一个快速图表,显示我们的推文和他们的情绪随时间的变化。
fig = plt.figure(figsize=(20,5))
ax = fig.add_subplot(111)
ax.scatter(df['timestamp'],df['compound'], label='Tweet Sentiment')
ax.plot(df['timestamp'],df['rolling'], color ='r', label='Rolling Mean')
ax.plot(df['timestamp'],df['mean'], color='y', label='Expanding Mean')
ax.set_xlim([dt.date(2019,1,15),dt.date(2019,2,21)])
ax.set(title='Anthem Tweets over Time', xlabel='Date', ylabel='Sentiment')
ax.legend(loc='best')
fig.tight_layout()
plt.show(

我们可以马上注意到一些有趣的事情。
- 有很多推特的情绪分是 0。
- 我们有很多数据。
- 在我们的数据中,平均值似乎有些稳定,除了第 25 天,那里的负面推文数量有所增加,平均值的扩大受到了严重影响。
- 似乎有更高密度的区域有更多的推文出现。我们将看看我们是否能把这些与游戏发布的相关事件联系起来。
让我们试着一次解决一个问题。首先让我们看看那些情绪为 0 的推文。Seborn 的 distplot 是一种快速查看我们推文中情感分数分布的方法。
fig = plt.figure(figsize=(10,5))
ax = fig.add_subplot(111)
sns.distplot(df['compound'], bins=15, ax=ax)
plt.show()

Just over 30% of our tweets have a sentiment of 0.
我选择暂时将这些推文留在我的数据集中。但值得注意的是,如果不包括这些因素,平均情绪会高得多。
让我们看看,随着时间的推移,我们是否能对自己的情绪有一个更清晰的了解。总的来说,我们的数据是嘈杂的,实在是太多了。从我们的数据中提取一个样本可能会让我们更容易看到趋势的发展。我们将使用 pandas sample()函数来保留 77,740 条推文中的十分之一。
ot = df.sample(frac=.1, random_state=1111)
ot.sort_index(inplace=True)ot['mean'] = ot['compound'].expanding().mean()
ot['rolling'] = ot['compound'].rolling('6h').mean()
我按日期重新排序,并为我们的数据计算新的扩展和滚动平均值,并绘制新数据集的图表。

By sampling the dataset we can get a much better idea of how sentiment is changing over time.
这个图表好得多,让我们可以看到随着时间的推移,情绪的一些下降和趋势。现在剩下要做的就是找出是什么导致了情绪的变化。
我在本文开头提到了 Anthem 发布的一些重要注意事项。让我们在图表中添加一些重要的日期,看看它们是否符合我们数据中的趋势。
- Anthem 从 2 月 1 日到 2 月 3 日有一个“免费演示周末”。
- Anthem 于 2 月 15 日面向 Origin Access 会员上线。
- Anthem 在 2 月 15 日发布后不久就出现了服务器问题,并于上午 7:30 发布到他们的 twitter 帐户上,这些问题得到了解决,EA 于上午 11:06 发布了一篇 twitter 帖子。
- EA 于 19 日发布了第一天补丁,20 日上午 8:13 发布了完整的补丁说明,补丁于当天下午 4:27 上线。
添加一些行。axvline()和。text()我在这里结束了这个图表。

These lines might not line up perfectly, as I’m not sure the ‘official’ time of each release.
我们可以看到,两大群推文与游戏发布时间一致,都是“演示周末”和 Origin Access 发布。
此外,我们可以看到,在演示周末,情绪下降。演示周末期间的平均情绪为 0.138,相比之下,演示周末之前的同一时期的平均情绪为 0.239。
你也可以很快注意到,1 月下旬还有另一组推文未被解释。快速浏览 Twitter,我发现这实际上是一个 VIP 演示周末,也遇到了服务器问题,加载时间长,需要多个补丁修复。这与市场人气的大幅下降不谋而合。我们也将这条线添加到我们的图表中,并创建一些支线剧情,让我们可以看到一些个别事件的细节。
这里是图形的最终代码,后面是图形本身。
fig = plt.figure(figsize=(20,5))
ax=fig.add_subplot(111)ax.scatter(ot['timestamp'],ot['compound'], label='Tweet Sentiment')
ax.plot(ot['timestamp'],ot['rolling'], color ='r', label='Rolling Mean')
ax.plot(ot['timestamp'],ot['mean'], color='y', label='Expanding Mean')
ax.set_xlim([dt.date(2019,1,15),dt.date(2019,2,21)])
ax.set(title='Anthem Tweets over Time', xlabel='Date', ylabel='Sentiment')
ax.legend(loc='best')#free demo weekend
ax.axvline(x=dt.datetime(2019,2,1) ,linewidth=3, color='r')
ax.text(x=dt.datetime(2019,2,1), y=0, s='Demo Weekend Starts', rotation=-90, size=10)ax.axvline(x=dt.datetime(2019,2,4) ,linewidth=3, color='r')
ax.text(x=dt.datetime(2019,2,4), y=0, s='Demo Weekend Ends', rotation=-90, size=10)#origin access launch
ax.axvline(x=dt.datetime(2019,2,15) ,linewidth=3, color='r', linestyle='dashed')
ax.text(x=dt.datetime(2019,2,15), y=0, s='Origin Access Launch', rotation=-90, size=10)#server fix
ax.axvline(x=dt.datetime(2019,2,15,11,6) ,linewidth=3, color='r')
ax.text(x=dt.datetime(2019,2,15,11,6), y=0, s='Server Up', rotation=-90, size=10)#patchnotes announced
ax.axvline(x=dt.datetime(2019,2,19,12) ,linewidth=3, color='r')
ax.text(x=dt.datetime(2019,2,19,12), y=0, s='Patch Notes Announced', rotation=-90, size=10)#patchnotes released
ax.axvline(x=dt.datetime(2019,2,20,8,13) ,linewidth=3, color='r')
ax.text(x=dt.datetime(2019,2,20,8,13), y=0, s='Patch Notes Released', rotation=-90, size=10)#patch realeased
ax.axvline(x=dt.datetime(2019,2,20,16,27) ,linewidth=3, color='r')
ax.text(x=dt.datetime(2019,2,20,16,27), y=0, s='Patch Released', rotation=-90, size=10)#vip weekend
ax.axvline(x=dt.datetime(2019,1,25,9,0) ,linewidth=3, color='r')
ax.text(x=dt.datetime(2019,1,25,9,0), y=0, s='VIP Demo', rotation=-90, size=10)fig.tight_layout()
plt.show()

Again, the lines might not be perfect as I’m unsure of the ‘official’ time of each launch.
这是添加了 VIP 演示的大图。

我们最终的图表向我们展示了一些有趣的事情。首先,VIP 演示对情绪影响最大。很明显,个人对围绕 VIP 演示的所有问题感到不安。
公开演示周末也显示了情绪的显著下降。这两个都是有趣的案例,在这两个案例中,开发者在游戏没有完全测试之前就决定允许公众玩游戏。一方面,开发者和发行商得到了关于游戏、服务器容量和需要修复的错误的有价值的反馈。问题是,这是以游戏周围的情绪为代价的吗?
也许不是!我们可以看到,当 EA Access 发布开始时,人气已经恢复到原来的水平(尽管从未像 VIP 试玩前那样高。)
游戏开发商和发行商需要权衡让公众作为早期游戏发布的 beta 测试者的价值,以及公众对游戏的看法。如果演示周末被宣传为“测试”周末,或许个人情绪会更高..
总而言之,这是一个有趣的项目,同样的分析可以应用于各种事物,政治,电影等等。现在我想我要从统计中休息一会儿,去参加我的标枪飞行。
《神盾局》主角的情感分析

Central Perk — Where this beautiful journey began.
我是这部美国情景喜剧的最大爱好者之一——自从我开始看这部剧以来就是朋友。最近,我开始在网上寻找这个节目的文字记录(https://fangj.github.io/friends/)。我在这些抄本上找不到太多的数据科学,除了每个角色对话数量的简单统计,他们说出的名字的数量和类似的统计。我想如果我能为像我一样的观众做更多的事情,从数据科学的角度更广泛地了解这部剧,那将会很有趣。
作为第一步,我整理了 plain 可以获得的所有 html 脚本(全部 10 季)。csv 文件(也可以复制成文本,不会造成差异)。皈依者。所有赛季的 csv 文件都可以从我的 github 链接获取——(https://github . com/shilpibattacharyya/Friends _ Analysis/tree/master/transcripts _ Friends)。然后,我使用下面的命令将它们合并在一起。
## Command to append multiple files in a directory - 1* represent all files for season1 and so on ...#sed 1d 1*.csv 2*.csv 3*.csv 4*.csv 5*.csv 6*.csv 7*.csv 8*.csv 9*.csv 100*.csv> merged.csv *
然后使用下面的实用程序将每个角色(瑞秋、罗斯、乔伊、菲比、钱德勒、莫妮卡)的对白分开:
def rem_tabs_newline(str_val):
str_val = str_val.strip('\\n')
str_val = str_val.strip('\\t')
str_val = str_val.replace('\\n','')
str_val = str_val.replace('\\t','')
return str_valfriends_chars={}
Rachel=''
Ross=''
Joey=''
Chandler=''
Phoebe=''
Monica=''
with open("transcripts_friends/season_all/merged.csv", "r+") as fp:
for cnt, line in enumerate(fp):
if line.startswith('Rachel:'):
Rachel=Rachel+' '+(line[8:])
elif line.startswith('Ross:'):
Ross=Ross+' '+(line[6:])
elif line.startswith('Monica:'):
Monica=Monica+' '+(line[8:])
elif line.startswith('Chandler:'):
Chandler=Chandler+' '+(line[10:])
if line.startswith('Phoebe:'):
Phoebe=Phoebe+' '+(line[8:])
if line.startswith('Joey:'):
Joey=Joey+' '+(line[6:])friends_chars['RACHEL']=rem_tabs_newline(Rachel)
friends_chars['ROSS']=rem_tabs_newline(Ross)
friends_chars['MONICA']=rem_tabs_newline(Monica)
friends_chars['PHOEBE']=rem_tabs_newline(Phoebe)
friends_chars['CHANDLER']=rem_tabs_newline(Chandler)
friends_chars['JOEY']=rem_tabs_newline(Joey)
下面的代码使用 nltk 库清除数据,删除停用词、制表符和换行符。
from nltk.corpus import stopwords
from nltk.tokenize import word_tokenize
from nltk.tokenize import sent_tokenizestop_words = set(stopwords.words('english'))def clean_data(val):
val = val.strip('\\n')
val = val.strip('\\t')
val = val.replace('\\n','')
val = val.replace('\\t','')
word_tokens = word_tokenize(str(val).lower().strip('[]') )
filtered_sentence = [w for w in word_tokens if not w in stop_words]filtered_sentence = []for w in word_tokens:
if w not in stop_words:
filtered_sentence.append(w)
return list(filtered_sentence)
一旦我清理了数据并收集了单词,我就为每个字符创建一个集合语料库列表,如下所示。
rachel_corpus=list(set(clean_data(str(friends_chars['RACHEL']).strip('[]'))))
ross_corpus=list(set(clean_data(str(friends_chars['ROSS']).strip('[]'))))
mon_corpus=list(set(clean_data(str(friends_chars['MONICA']).strip('[]'))))
joe_corpus=list(set(clean_data(str(friends_chars['JOEY']).strip('[]'))))
phoebs_corpus=list(set(clean_data(str(friends_chars['PHOEBE']).strip('[]'))))
chandler_corpus=list(set(clean_data(str(friends_chars['CHANDLER']).strip('[]'))))
然后,只过滤掉长度大于 3 的单词,这样我就可以去掉常见的单词,如下所示。
words_rach=[w for w in rachel_corpus if len(w)>3]
words_mon=[w for w in mon_corpus if len(w)>3]
words_ross=[w for w in ross_corpus if len(w)>3]
words_phoebs=[w for w in phoebs_corpus if len(w)>3]
words_joe=[w for w in joe_corpus if len(w)>3]
words_chandler=[w for w in chandler_corpus if len(w)>3]
我将进一步使用 nltk lemmatizer 来查找所有单词的词干作为有效单词。请注意,我在避免使用 portstemmer,因为它也会产生一些无效单词。
WNLemma=nltk.WordNetLemmatizer()
stem_freq_words_rach=[WNLemma.lemmatize(t) for t in words_rach]
stem_freq_words_ross=[WNLemma.lemmatize(t) for t in words_ross]
stem_freq_words_chandler=[WNLemma.lemmatize(t) for t in words_chandler]
stem_freq_words_mon=[WNLemma.lemmatize(t) for t in words_mon]
stem_freq_words_phoebs=[WNLemma.lemmatize(t) for t in words_phoebs]
stem_freq_words_joe=[WNLemma.lemmatize(t) for t in words_joe]
接下来,我将添加所有单词,并为每个角色创建一个口语单词字符串,以提供给 IBM Watson NLU 服务进行情感分析。
s_rachel=""
for w in stem_freq_words_rach:
s_rachel=s_rachel+' '+ws_ross=""
for w in stem_freq_words_ross:
s_ross=s_ross+' '+ws_phoebs=""
for w in stem_freq_words_phoebs:
s_phoebs=s_phoebs+' '+ws_joe=""
for w in stem_freq_words_joe:
s_joe=s_joe+' '+ws_chandler=""
for w in stem_freq_words_chandler:
s_chandler=s_chandler+' '+ws_mon=""
for w in stem_freq_words_mon:
s_mon=s_mon+' '+w
然后,我会调用沃森 NLU 服务,并传递每个角色的口语单词进行情感分析,如下所示。
import json
from ibm_watson import NaturalLanguageUnderstandingV1
from ibm_watson.natural_language_understanding_v1 import Features, EmotionOptions
import pandas as pd
import matplotlib.pyplot as pltdicti={}
natural_language_understanding = NaturalLanguageUnderstandingV1(
version='2019-07-12',
iam_apikey='please put your api key here',
url='[https://gateway.watsonplatform.net/natural-language-understanding/api'](https://gateway.watsonplatform.net/natural-language-understanding/api'))#rachelresponse = natural_language_understanding.analyze(
text=s_rachel,
features=Features(emotion=EmotionOptions())).get_result()
print("Rachel")
print("======================================")
dicti["Rachel"]=response["emotion"]["document"]["emotion"]
print(json.dumps(response["emotion"]["document"]["emotion"], indent=2))#ross
response = natural_language_understanding.analyze(
text=s_ross,
features=Features(emotion=EmotionOptions())).get_result()
dicti["Ross"]=response["emotion"]["document"]["emotion"] #monica
response = natural_language_understanding.analyze(
text=s_mon,
features=Features(emotion=EmotionOptions())).get_result()
dicti["Monica"]=response["emotion"]["document"]["emotion"] #phoebe
response = natural_language_understanding.analyze(
text=s_phoebs,
features=Features(emotion=EmotionOptions())).get_result()
dicti["Phoebe"]=response["emotion"]["document"]["emotion"] #chandler
response = natural_language_understanding.analyze(
text=s_chandler,
features=Features(emotion=EmotionOptions())).get_result()
dicti["Chandler"]=response["emotion"]["document"]["emotion"] #joey
response = natural_language_understanding.analyze(
text=s_joe,
features=Features(emotion=EmotionOptions())).get_result()
dicti["Joey"]=response["emotion"]["document"]["emotion"]print(json.dumps(dicti, indent=2))
我将输出转换成 dataframe 并打印如下。
df = pd.DataFrame(dicti)
df

Sentiment density distribution for each of the Characters
为了更好的形象化,我绘制了如下图。
df.transpose().plot(kind='bar')
plt.show()

Bar chart for sentiment density distribution for each of the Characters
从上面的条形图推断
根据情感密度分布,节目中的所有角色都非常相似。这可能是他们相处融洽、形影不离的原因。这些角色充满了欢乐、悲伤和厌恶:)。
然后,我可视化一个饼图,以便更好地理解这些角色,如下所示(代码可以为每个角色复制—这里只为 Rachel 提供)。
colors = ['b', 'g', 'r', 'c', 'm']
labels = ['Anger', 'Disgust', 'Fear', 'Joy', 'Sadness']
explode = (0.2, 0.2, 0.2, 0.2, 0.1)
plt.pie(df.Rachel, colors=colors, labels=labels,
explode=explode, autopct='%1.1f%%',
counterclock=False, shadow=True)
plt.title('Sentiment Density Index for Rachel')
plt.show()






从上面的饼状图推断
最平衡的角色是钱德勒,其次是瑞秋和莫妮卡。菲比和乔伊彼此非常相似,罗斯、瑞秋和莫妮卡也是。
我还引入了一个新的变量“快乐商数”,来计算每个角色的快乐相对于其他情绪的比例。
我们可以用类似的方式来评估所有其他情感的这个商。
happy_quotient_Rach=df.Rachel.joy/df['Rachel'].sum()
happy_quotient_Ross=df.Ross.joy/df['Ross'].sum()
happy_quotient_Joey=df.Joey.joy/df['Joey'].sum()
happy_quotient_Monica=df.Monica.joy/df['Monica'].sum()
happy_quotient_Phoebe=df.Phoebe.joy/df['Phoebe'].sum()
happy_quotient_Chandler=df.Chandler.joy/df['Chandler'].sum()
让我们看一个散点图,找出最幸福的人物。
x = ['Rachel','Ross','Joey','Monica','Phoebe','Chandler']
y = [happy_quotient_Rach,happy_quotient_Ross,happy_quotient_Joey,happy_quotient_Monica,happy_quotient_Phoebe,happy_quotient_Chandler]colors = np.where(df_hq.Happiness_Quotient > 0.25, '#ff748c', '#497087')
plt.scatter(x,y,s=120, c=colors)
plt.show(
情商的曲线图如下。
x_happy = ['Rachel','Ross','Joey','Monica','Phoebe','Chandler']
y_happy = [happy_quotient_Rach,happy_quotient_Ross,happy_quotient_Joey,happy_quotient_Monica,happy_quotient_Phoebe,happy_quotient_Chandler]
colors = ['r','g','b','y','cyan','m']
plt.scatter(x_happy,y_happy,s=120, c=colors)
plt.title('Happiness Quotient for the Characters')
plt.show()





从上面的散点图推断
最幸福的角色依次是:罗斯、乔伊、菲比、瑞秋、钱德勒
最悲伤的角色依次是:菲比、乔伊、罗斯、莫妮卡、瑞秋、钱德勒
最恶心的角色依次是:钱德勒、莫妮卡、罗斯、乔伊、菲比、瑞秋
最害怕的角色依次是:瑞秋、莫妮卡、钱德勒、乔伊、菲比、罗斯
最愤怒的角色依次是:菲比、乔伊、罗斯、瑞秋、莫妮卡、钱德勒
备注
这是一次尝试,通过网上的文字记录来了解角色的情感,看看我是否能把它与我在观看这部剧时对这些角色的感受联系起来。
我会继续研究这个数据集,希望很快能有更多的见解。
有效地预处理文本数据第 2 部分:实现
这篇文章是我之前关于文本预处理的文章的直接延续。这是一些重要的文本预处理步骤的实际实现,这些步骤在输入到机器学习模型之前使用。我没有通过编写脚本来使用传统的预处理和学习方法,而是使用了一个叫做 Knime 的工具。
数据选择和分析
该数据集可在 UCI 数据库上获得,标题为“情感标签句子数据集”。数据集基本上被收集并手工标记为积极或消极情绪。
数据集由三个原始文本文件组成,分别命名为 amazon _ cells _ labelled.txt、imdb _ labelled.txt 和 yelp _ labelled.txt。每个文件包含 1000 个带标签的评论及其对应的标签,这些标签可以是 0,表示负面情绪,1 表示正面情绪。
在大规模了解单个客户的情绪/行为时,情绪分析是一个主要问题。文本是人类用来传递信息的最广泛的一种交流方式。该消息可以是一些信息、反馈、警告或其他人可以理解的任何类型的信息。当遇到大量用户反馈的情况时,几乎不可能手动检查每条记录,然后试图理解数百万条反馈的整体语义。换句话说,人类几乎不可能总结数百万条信息。因此,抽取文本的情感是解决上述问题的关键部分。最简单的情感分析形式是,我们需要判断某段文字对某个主题是正面情感还是负面情感。
在文本分析领域中,这种需要将消息分类为两个或更多不同类别的问题被称为分类问题。
分类是监督学习的一部分,在监督学习中,我们不仅可以获得数据集实例,还可以获得特定实例所属的标签或类。
在这项总体任务中,我们将回答以下基本数据挖掘问题:
1.)对于原始文本形式的可用数据,定义的预处理步骤是什么?
我们将看到预处理和清理文本与普通的数据预处理任务完全不同。存在依赖于语言的算法,这使得很难提出通用的文本预处理任务。在数据分析部分,将详细解释文本预处理步骤。
2.)通过文本提取什么样的特征?
我们有不同的方法来提取数字特征,如 TF-IDF、BoW 等,这些将在下面的文本挖掘部分进行解释。
现在,我们将看到有成千上万个不同的单词,这意味着我们将使用非常巨大的向量,我们的训练数据集矩阵将是一个非常稀疏的矩阵。
让我们看看稀疏矩阵是什么样的。

Figure 1: Bag of Words features with first 15 rows
经过预处理后,我们剩下 3035 个唯一项,这意味着每个向量将有 3035 个 1 和 0(大部分是 0)。
3.)说到情感分析,哪些分类技术是最强的?
这个问题没有简单的答案,但是,考虑到这是一个二元分类,我们应该期待逻辑回归是最好的结果生产者之一。除此之外,SVM 可以给出很好的结果,因为支持向量机在维数较高的地方更好。就神经网络而言,其架构的变化,即层数和神经元数量的变化,几乎会改变一切。简而言之,神经网络既可以是这项任务的最佳选择,也可以是最差的选择。
4.)什么样的聚类技术比较合适?
正如我们上面讨论的,数据非常稀疏,K-Means 聚类不适合这些场景。我们知道只有几个 1 和大多数 0,这意味着在 3035 维向量空间模型中,如果我们应用 K-Means,我们将得到一个没有太多差异的高密度集群。因此,我们将了解分层聚类是如何进行的。考虑到数据量,使用层次聚类将会产生一个非常复杂的树形图。
5.)整个数据中的整体候选子集看起来如何,意思是,数据呈现的核心主题是什么?
我们将在该报告的数据分析部分看到数据中最热门的关键词趋势。考虑到积极情绪和消极情绪的数量相等,可以预计会有很多混淆的关键词来描述积极情绪和消极情绪。
数据分析
由于缺乏结构,文本数据分析是最麻烦的任务之一。但是,我们将执行基本的文本和数据分析,以找到数据集的摘要。数据集是从三个不同公司的三个不同文件中读取的,在对数据进行任何分析之前,我们必须合并所有记录。这种数据合并在第一步中执行,如下图所示:

Figure 2: Reading and Concatenating multiple files
如果我们观察正类与负类的比率,我们看到在 3000 条记录中,1500 条对应于正类,1500 条对应于负类。在机器学习中,类的数量相等是更可取的,因为当类的数量不相等时,模型预计会变得有偏差。对于一个类,该模型将暴露给数据集的更多实例,并且该类的权重将会更高,最终,与其他类相比,该模型更有可能预测该类。

Figure 3: Row counts for each class
另一种重要的文本数据分析技术叫做词云,或者标签云。标签云很重要,因为它们不是一些具有可解释意义的图形或图表,相反,它们只是代表整个文本集合的基于关键字的总结,这样,我们就能够直接推断出讨论最多的趋势。这种分析被公司用来判断某个产品是否存在特定的问题。即使在竞选活动的数据收集期间,这些来自社交媒体账户的数据也是由这些机构收集的,最高发生趋势通过标签云可视化,以了解公众在大规模上真正谈论的是什么。
虽然数据集非常平衡,这对训练分类模型来说是一件好事,但是图 1 中一个值得注意的事情是数据的稀疏性。观点大多很短,在大多数情况下不超过一句话。这将是一项具有挑战性的任务,因为数据实例之间的决策边界将非常模糊,因此很难训练一个具有真正良好准确性的分类器。
考虑一下“书”这个术语。下面是一个饼图,显示了总评论中出现的术语的比率。

Figure 4: Percentage of the term “book” appearing in the reviews
这说明 99.9%的行不包含“书”这个词,只有 0.1%的行/句包含“书”这个词。其他许多术语也有类似的情况。这种数据很难处理。虽然平衡的课程对我们来说是一个机会,但稀疏的数据集对我们来说是一个挑战,因为评论通常不是非常详细。
我们对整个数据集应用了层次聚类来得到一个树状图,这实际上是一个非常紧凑的树。

Figure 5: Dendogram of the entire corpus
虽然很难判断树突图中到底显示了什么,但我们可以看到密集的区域,并知道数据中存在某些“分组”,这是因为评论来自不同的语言领域。
文本挖掘
我们只有文本评论和它们的情感标签,因此如果没有任何文本挖掘方法,这个问题是不可能解决的。如果没有任何实体文本挖掘方法,我们将无法计算数字特征,没有这些数字特征,就不会有训练好的分类器。
该任务的文本预处理有 9 个主要步骤,如下所示:
- 去掉标点符号,因为它们在文本数据中表现为噪音,因为它们没有特定的语义。
- 去掉数字和数字,因为我们想进行定性分析(肯定或否定),而不是任何涉及数字的定量分析。
- 删除所有少于或等于三个字符的单词,因为这些单词可能是停用词,也可能是充当俚语的单词。
- 删除 and、or、of、is、am、had 等停用词。停用词是如此普遍,并且持有如此少的语义信息,以至于它们的移除是有利的,因为它不仅降低了向量空间模型的维度,而且在大多数情况下增加了分类的准确性。
- 将整个文本转换为小写,因为计算机会将“awesome”和“Awesome”解释为两个不同的术语,因为小写和大写“A”和“A”的编码不同。
- 应用波特·斯特梅尔。词干提取是一个重要的阶段,因为它将单词或术语还原到它们的根源。例如,术语“更快”被转换成“快”,“推荐”转换成“推荐”,“吃”转换成“吃”,等等。这有助于保留句子的语义,同时简化重复。
- 按文档对术语进行分组。
- 从分组的术语中提取关键字,因为我们不希望每隔一个单词都被限定为一个特征。我们只考虑数据集中最重要的术语,并且只保留合格的关键字术语。
- 将提取的关键词转换成稀疏向量,或者我称之为“矢量化”。
向量化是一个重要的决定,因为将英语单词转换成向量,使得它们的语义可以通过非常高维的数字向量来表示,这是一个有点棘手的过程。对于我们的具体问题,我们可以选择使用 TF-IDF 特性,它代表 term frequency-InvertedDocumentFrequency。当我们要开发一些信息检索系统时,这些类型的特征是首选的,在这些系统中,不仅存在,而且术语存在的次数以及术语(单词)存在的文档数量也很重要。TF-IDF 特性基本上为稀有词提供较高的值,为常用词提供较低的权重值。我们将计算的特征被称为 BoW(单词包)特征,它们背后的思想是,我们将一个文档(在我们的例子中,是一篇评论)视为一个集合,或者说,一个由不同术语(单词)组成的“包”。一个单词要么存在于文档中,要么不存在。弓的整个概念是基于非常大的二进制向量。每个单词代表一个唯一的维度,因此如果在 D 个文档的整个集合中有 W 个唯一的单词,那么每个文档将被表示为 W 维向量。考虑四个单词的词汇表,比如“阿尔法、贝塔、查理和德尔塔”。现在,每个单词或术语都有一个索引。假设这些索引是按照字母顺序排序的。在这种情况下,术语“alpha”位于索引 0,“beta”位于索引 1,依此类推。现在,如果我们想要表示一个文档,上面写着“alpha beta delta delta”,那么它可以表示为一个四维向量[1,1,0,1]。请注意,由于“charlie”不是文档的一部分,并且术语“charlie”的索引是 2(从零开始的索引),这意味着向量中的第二个索引仍然是 0,而其他索引是 1。另一个例子可以是文档“alpha alpha charlie”,它将被表示为[1,0,1,0]。
上述任务的总体流程如下图所示:

Figure 6: Text preprocessing flow
我们的数据集是关于常见产品和电影的评论。因此,让我们看看我们的数据集中前 250 个术语是什么,然后我们可以讨论用户通过他们的反馈和评论集体传达了什么样的信息。

Figure 7: Tagcloud of the entire dataset
因此,正如我们所见,“电影”、“影片”、“电话”、“食物”、“时间”、“推荐”、“爱情”、“失望”,这些术语主宰了数据集。我们可以很容易地推断出,大多数人都在谈论手机,通过查看标签云,我们可以估计评论数据集中产品的业务流。
预处理非结构化数据是数据科学和机器学习中最困难的任务之一,因为没有特定的统计准则可用。一旦我们完成了所有步骤,我们只需要将数据分为训练和测试,并对数据应用一些机器学习,以查看基于我们所做的预处理的模型有多好。整个 Knime 工作流程的视图(有点混乱)如下所示:

Figure 8: Overall view of the Knime workflow
我们可以看到不同的机器学习模型,如朴素贝叶斯、多层感知器、SVM、逻辑回归和决策树,以及它们相应的学习器、预测器和评分器节点。作为一个小小的提示,我将给出一个简短的表格,按照分类器的准确度进行分类。我们可以使用其他更高级的评估指标,如 precision、recall 和 F1-Score,但是由于这两个类的行数相当相同,因此只根据准确性来评估模型的性能并不是一个坏主意。
对于二进制分类问题,朴素贝叶斯的表现不到 50%,这比扔硬币还糟糕。总的来说,模型性能不是很好,但是对于一些不同的、更高级的文本矢量化工具,如 Word2Vec、GloVe、BERT 等,还有很大的改进空间。
结论
- 如果你不是一个编码爱好者,有一些令人惊讶的工具可以让你开始学习数据科学和机器学习。
- 在处理非结构化数据时,预处理步骤的选择以及这些步骤的顺序可能很重要(请参考上一篇文章,以了解更多关于那篇文章的信息)。
- 在某些情况下,最简单的解决方案可以胜过高度复杂的解决方案,所以永远不要低估简单的解决方案。
- 尝试执行情感分析时,TF-IDF 不是最佳选择。
使用 Twitter 对 Swachh Bharat 进行情感分析
情感分析是提取人们对特定问题、品牌、方案等的看法。,(感悟)来自文字资料。它的应用范围很广,从品牌监控、产品评论分析到政策制定。在本文中,我们对带有 Swachh Bharat 标签的推文进行情感分析。这个项目的完整代码可在这里获得。
本文分为以下几个部分。
- 执行情感分析的不同方法
- 收集数据
- 将有用的数据转换为 CSV
- 删除重复的行和不必要的列
- 清理推文
- 停止单词删除
- 词汇化和词干化
- 数据可视化
- 将推文文本矢量化
- 从 sklearn 实现分类器
- 实施 Gensim 模型
- 摘要
执行情感分析的不同方法
有两种主要的方法来执行情感分析——基于词典的方法或使用机器学习模型。任何基于词典的方法基本上都是使用带有语义分数的单词词典来计算推文的最终极性。例如,考虑句子“我爱这部电影,尽管第二部分有点慢”。现在,像这样的每个单词将被分配一个(总)情感分数。
“I:0, love:0.750, this:0.0, movie:0.0, eventhough:0.0 the:0.0 second :0.0,part:0.0,is :0.0,boring:-0.250”
正的分数表示这个词是正面的,负的分数表示它是负面的。现在,如果我们把这句话的所有分数加起来,将是-0.500,这是一个积极的情绪。所以这条推文会被贴上正面的标签。可用的重要词库资源有- SentiWordNet 、 TextBlob 、 Afinn 、MPQA(多视角问答)主观性词库和模式。我用 SentiWordNet 使用这种词典方法来标记我的推文(稍后解释)。
第二种,机器学习方法,在预处理数据之后,使用分类器将问题作为文本分类任务来处理。我们遵循后一种方法,这将被详细解释。
关于该任务所需的工具
有许多用于文本处理和分类的库——NLTK(综合和艰难的学习曲线)、TextBlob(易于使用,适合初学者)、Spacy(行业工作的理想选择)等等。我选择 NLTK,因为它提供了许多支持库来实现许多文本处理技术
开始吧!
收集数据
现在我需要文字资料来表达人们对 Swachh Bharat 的看法。很少有选择是使用 Reddit API、脸书 API、Twitter API,以及从网页中抓取数据。我决定使用 twitter API 来收集关于 Swachh Bharat 的推文,因为许多人认为 twitter 是一个巨大的心理数据库。这个项目的推文是使用 python 中的 Tweepy 库从 Twitter 数据库收集的。为了访问推特,你必须创建一个推特开发者账户。有不同类型的开发人员帐户:标准搜索(免费),高级(付费)和企业(付费)。API 的功能或可访问性从标准帐户增加到企业帐户。例如,通过标准帐户,你可以访问过去七天的推文,而这是 30 英镑的高级帐户。为了创建账户,你必须提供核心用例的细节、意图、使用的商业目的、关于你计划进行的分析的细节以及方法或技术。我建议在第一次尝试中提供尽可能多的细节(也可以添加与你的项目相似的网页链接或项目的主题,只要你想澄清事情),否则你必须写长邮件一遍又一遍地解释相同的内容,以防他们需要更多的信息。Twitter 团队将彻底审查你的申请。这篇是一篇关于 Twitter 数据挖掘的好文章。

如果你的信息是完整的,通常不到一天就可以收到批准邮件。

之后,您需要在他们的界面中创建一个新的应用程序,并提供其描述(如下),该描述将生成 API 密钥、API 密钥、访问令牌和访问令牌密钥,我们将使用这些信息通过终端登录。

Creating a new application
现在让我们使用 tweepy 来验证我们的详细信息。AppAuthHandler()函数。用您的身份验证详细信息替换“*”。
import tweepy
API_KEY="*********************"
API_SECRET="*************************"
ACCESS_TOKEN="*******************************"
ACCESS_TOKEN_SECRET="******************************"
auth = tweepy.AppAuthHandler(API_KEY, API_SECRET)
auth.set_access_token(ACCESS_TOKEN, ACCESS_TOKEN_SECRET)
api = tweepy.API(auth)
print(api.me().name)
如果验证成功,您应该会看到打印出的帐户名称。
IAmSai
认证之后,我们就可以下载推文了。我们使用 API.search() 方法来获取 tweets。使用 Tweepy 搜索 Tweepy 有两种主要方式——按标签(或关键字)搜索和按 Tweepy 的 id 搜索(每个 Tweepy 的 id 都是唯一的)。第二种方法在你需要收集所有 tweet 的 id 时很有用(比如,你想收集关于给定 id 的去货币化的 tweet。这些 id 将由那些以前收集过关于该主题的数据的人给出)。因为我没有任何 id,所以我使用第一种方法。
search_query="#SwachhBharat"
new_tweets = api.search(q=search_query,count=tweetsPerQuery,lang="en",tweet_mode='extended')
auth = tweepy.AppAuthHandler(API_KEY,API_SECRET)
api = tweepy.API(auth,wait_on_rate_limit=True,wait_on_rate_limit_notify=True)
fName = 'sb_04_08_19.txt' # where i save the tweets
上面的代码片段中没有给出几行重要的代码。详细代码请看本。
我们使用关键字“#SwachhBharat”进行搜索。后来,在清理数据时,我意识到带有标签的推文——“# myclean India”和“#SwachhBharatUrban”也可以用来搜索关于 Swachh Bharat 的推文。我们使用 AppAuthHandler(第三行)而不是最常用的-OAuthHandler 来为授权API。OAuthHandler 允许每个 15 分钟窗口最多 180 个请求(每个请求最多 100 条 tweets)。所以我们只能在 15 分钟内获得 18000 条推文。但是 AppAuthHandler 有其最大请求速率限制,即每个请求 450 条和 100 条 tweets。因此,它有助于在 15 分钟内获得 45,000 条推文。此外,我使用了扩展 tweet 模式(第二行),以确保我们获得完整的 tweet,而不是被截断的 tweet。下载后,我们将把推文保存在一个文本文件中(或者,我们也可以把它们保存在一个. json 文件中)。我又重复了几次以上的过程,以便以后收集更多的 tweet(两次数据收集之间的间隔超过 7 天,否则我们会得到很多重复的 tweet)。

Downloading the tweets
将有用的数据转换为 CSV
这是我最初很难理解的部分,因为我从未使用过 API 和 JSON。这个文本文件很大,大约有 1100 页的信息。在“sb.txt”(文本文件)中,推文是 JSON (JavaScriptObjectNotation)格式的。看一看它。

Data is full of many JSON objects
只需扫一眼,您就可以看到有许多特性(对象)对我们的任务来说并不重要,比如元数据、索引、屏幕名称。所以我浏览了这个页面,找出我的任务可能需要的对象。我决定使用这些对象——full _ text(推文的全文)、retweet_count(推文的)、favorite_count(推文的)、geo(地理位置)、coordinates(地点的纬度和经度坐标)、place(推文的来源)、follower_count(推文用户的)、created_at(推文的时间和日期)、id_str(推文的字符串格式的 id)。我将上述对象保存在我的 CSV 文件中,并丢弃其余的。
inputFile='sb_04_08_19.txt'
tweets = []
for line in open(inputFile, 'r'):
tweets.append(json.loads(line))
for tweet in tweets:
try:
csvWriter.writerow([tweet['full_text'],tweet['retweet_count'],tweet['user']['followers_count'],tweet['favorite_count'],tweet['place'],tweet['coordinates'],tweet['geo'],tweet['created_at'],str(tweet['id_str'])])
count_lines+=1
except Exception as e:
print(e)
上面给出了从文本文件转换为 CSV 文件的几行重要代码。请参见这个以了解更多信息。我们创建一个 tweets 列表,从文件中读取每一行(第一个 for-loop)。然后,我们使用 csvWriter.writerow()将每个 tweet 的必要信息写入 CSV 文件。而且我们只收集上面提到的对象。
让我们尝试在 Jupyter 笔记本中加载 CSV 文件。有必要在 read_csv()函数中将编码参数设置为“unicode_escape ”,因为一些推文也包含表情符号,这会给默认编码带来困难。
import pandas as pd
df = pd.read_csv('somall.csv', encoding = 'unicode_escape')
df.head(5)
看一下输出。

删除重复行
让我们查看并删除重复的行(如果有的话)。有两种类型的重复——所有列都具有相同值的重复(当 tweet-collector 再次收集相同的 tweet 时会发生这种重复)和 tweet 具有相同文本的重复(当两个或更多用户发布相同的 tweet 时会发生这种重复。)
print(len(df.index))#14195
serlis=df.duplicated().tolist()
print(serlis.count(True))#112
serlis=df.duplicated(['full_text']).tolist()
print(serlis.count(True))#8585
我们可以看到,在 full_text 列中有 112 个重复行和 8585 个重复行,行重复是 full_text 重复的一个子集。所以我们删除了所有重复的行。
df=df.drop_duplicates(['full_text'])
起初,我没有注意到重复。因此,当使用文本分类器时,它们给我的准确率高达 99.54%,这并不好。
删除不必要的列
似乎有几个像“地点”、“地理”这样的特征有更多的非数字(NaN)值。让我们检查一些选定的列中有多少空行
print(df['geo'].isna().sum())
print(df['place'].isna().sum())
print(df['coordinates'].isna().sum())

我们的数据集中总共有 14,195 行。我们看到 14191 行地理列是空的。“位置”和“坐标”中的空行也很高(13951 和 14191)。所以我们把这些列和‘id _ str’(tweet 的 id)一起删除了,因为 tweet id 对我们来说用处不大。虽然我们在上下文中没有使用 retweet_count、user_followers_count、favorite_count,但是我们没有删除它们,因为它们可以用于其他任务(比如估计 tweet 的受欢迎程度,这可以用于为 tweet 分配权重)。
df=df.drop([‘place’,’coordinates’,’geo’,’id_str’],axis=1)
df.head(5)

First five rows of the dataset after dropping a few columns
清理推文
在一条推文的文本中,可能会有一些不必要的符号,这些符号对于我们的分析并不重要。所以让我们继续清理我们的推文。
让我们观察任何特定推文的全文。

full text of a tweet at row number 22
正如我们所看到的,一些不必要的文本和符号被删除——用户名 _ 标签(如@_WeMeanToClean),转发符号(RT),标签(如#MyCleanIndia),网址(如 https://t.co/XK3ZReâ\x80),数字和标点符号。一些有意义的标签传达了意思,在单词被分割成有用的部分后可以有一些情绪在里面(比如#ILoveSwachhBharat)。因此,不是删除所有以 hashtag 符号开头的单词,而是只删除' # '符号。我们使用 python 中的 re 模块来执行文本清理。re.sub()函数搜索一个模式并用我们指定的文本进行替换。我们用空白字符替换所有这些符号。
import re
for i in range(len(df)):
txt = df.loc[i]["full_text"]
txt=re.sub(r'@[A-Z0-9a-z_:]+','',txt)#replace username-tags
txt=re.sub(r'^[RT]+','',txt)#replace RT-tags
txt = re.sub('https?://[A-Za-z0-9./]+','',txt)#replace URLs
txt=re.sub("[^a-zA-Z]", " ",txt)#replace hashtags
df.at[i,"full_text"]=txt
现在,我们可以看到我们的推文看起来很干净。

A tweet after cleaning
词性标注和情感标注
我们已经完成了文本数据的基本清理工作。在 ML 算法中,我们将实现 tweet 的“full_text”作为预测变量(如果我们想要预测 tweet 的影响,可以使用的其他变量是 retweet-count、favorite-count,但这不是我们当前任务的一部分)。很明显,我们需要为我们的数据创建目标变量(情感得分)。为此,我们使用 SentiWordNet。 SentiWordNet 是一个增强的词汇资源,专为支持情感分类和观点挖掘应用而设计。它有一个很大的词类标注英语单词及其情感的语料库。

Predicting sentiment of few words using SentiWordNet-An example
和上面的例子一样,SWN 给出了每个单词的 pos_score 和 neg_score。pos_score 越高,这个词越正面。我们使用这些分数的简单线性求和(我们将 tweet 中所有单词的 pos_score 相加以形成 pos_total,以类似的方式,我们获得 neg_total。然后我们把这两个加起来得到 sent_total)并把一个句子标注为正(1)如果它(sent_total)大于 0,负(-1)如果它小于 0,中性(0),否则。你可能已经发现,为了使用 SWN,我们需要找到我们推文的词性标签。nltk 的 pos_tag 特性可以帮助我们标记文本,但是它并没有被简化(除了名词、代词、形容词、动词之外,它还可以用更多的标签来标记文本,比如 adposition、连接词、限定词等等。,).但是 SWN 只接受简化的标签。所以我们尝试使用 nltk 库中的 map_tag 函数来简化标签。
for i in range(len(df_copy.index)):
text = df_copy.loc[i]['full_text']
tokens = nltk.word_tokenize(text)
tagged_sent = pos_tag(tokens)
store_it = [(word, map_tag('en-ptb', 'universal', tag)) for word, tag in tagged_sent]
第二行将 tweet 文本转换成标记,这些标记将在以后用它们各自的词性进行标记。然后,我们将单词和标签存储在 store_it 列表中。该列表随后将被传递给使用 SWN 的情感标签。
当使用 pos_tag()和简化的 POS-tag 进行标记时,推文的标签(“旁遮普省尼赫鲁·尤瓦·肯德拉的成员开展了挨家挨户的垃圾分类宣传活动”)如下所示。
Tagged Parts of Speech: [('a', 'DT'), ('door', 'NN'), ('to', 'TO'), ('door', 'VB'), ('waste', 'NN'), ('segregation', 'NN'), ('awareness', 'NN'), ('drive', 'NN'), ('was', 'VBD'), ('carried', 'VBN'), ('out', 'RP'), ('by', 'IN'), ('members', 'NNS'), ('of', 'IN'), ('nehru', 'JJ'), ('yuva', 'NN'), ('kendra', 'NN'), ('in', 'IN'), ('kapurthala', 'NN'), ('punjab', 'NN')]Tagged Parts of Speech- Simplified: [('a', 'DET'), ('door', 'NOUN'), ('to', 'PRT'), ('door', 'VERB'), ('waste', 'NOUN'), ('segregation', 'NOUN'), ('awareness', 'NOUN'), ('drive', 'NOUN'), ('was', 'VERB'), ('carried', 'VERB'), ('out', 'PRT'), ('by', 'ADP'), ('members', 'NOUN'), ('of', 'ADP'), ('nehru', 'ADJ'), ('yuva', 'NOUN'), ('kendra', 'NOUN'), ('in', 'ADP'), ('kapurthala', 'NOUN'), ('punjab', 'NOUN')]
然后,我们通过上面介绍中提到的方法来查找 tweet 的情感。请查看完整的代码以了解更多关于情感标签的信息。从 SWN 获得意见后,我们在我们的 CSV 中增加了三列——“积极得分”、“消极得分”、“发送得分”。tweet 的“pos_score”是 tweet 中所有单词的净正分,类似的还有“neg_score”。

Dataset after adding sentiment score for tweets
正面推文的值为 1(pos _ score > neg _ score),中性推文的值为 0(pos _ score = neg _ score),负面推文的值为-1(pos _ score<neg_score there="" are="" positive="" tweets="" neutral="" and="" negative="" tweets.="" i="" also="" used="" class="ae kl" href="https://github.com/SivaAndMe/Sentiment-Analysis-on-Swachh-Bharat-using-Twitter/blob/master/textblob_sentiment_labeling.py" rel="noopener ugc nofollow" target="_blank">text blob 和 Afinn 用于推文的情感分配)。结果如下:</neg_score>
TEXTBLOB:
Total tweets with sentiment: 5610
positive tweets: 2171
negative tweets: 591
neutral tweets: 2848AFINN:
Total tweets: 5610
positive tweets: 2551
negative tweets: 572
neutral tweets: 2487
来自每个情感的推文数量在每个分类器中的比例是一致的,这让我们对情感标签更有信心。此外,我们可以推断出大多数推文是积极的。
现在我们已经完成了情感标注,我们将进一步准备我们的文本数据,以馈入 ML 分类器。
停止单词删除
停用词是常用词,如 of、the、for 等。在我们的语境中,它们并没有给情感增加多少分量。此外,它们增加了数据的维度。所以我们使用 nltk 库删除了停用词(在下一部分)。
在将这些文本数据提供给 ML 算法之前,我们将它转换成向量(在本文的后面部分)。我们不希望相似的单词有不同的(向量)值。我们希望所有这些词都有相同的价值——“分发”、“分配”、“已分发”、“分发”,因为它们本质上表达了相同的意思。如果我们使用 nltk 模块执行词汇化和词干化,这(为相似的单词分配相同的值)可以更好地实现。
词汇化和词干化
词干化将单词缩减为它们的词干。词干提取算法主要是基于规则的。例如,他们把上面所有的单词“distribute”简化为“distribute”。一个词条解释器做了和斯特梅尔一样的事情,但是把这个词的语言学保持在上下文中。例如,以上所有的单词都将被浓缩为“distribute”。
from nltk.corpus import stopwords
from nltk.stem import WordNetLemmatizer
from nltk.stem import PorterStemmer
pstem = PorterStemmer()
lem = WordNetLemmatizer()
stop_words = stopwords.words('english')for i in range(len(df.index)):
text = df.loc[i]['full_text']
tokens = nltk.word_tokenize(text)
tokens = [word for word in tokens if word not in stop_words]for j in range(len(tokens)):
tokens[j] = lem.lemmatize(tokens[j])
tokens[j] = pstem.stem(tokens[j])tokens_sent=' '.join(tokens)
df.at[i,"full_text"] = tokens_sent
第三行删除 tokens-list 中的停用词。经过词汇化和词干化,我们最终用新的文本代替旧的文本。
值得注意的是,上述三个清理过程(停用词去除、词干化、词条化)在(情感标注)之前不执行,因为这样做会导致情感检测的不规则性。像“distribut”、“cleanin”这样的词在 SWN 的英语词汇语料库中是找不到的,因此 SWN 没能检测出这些词的正负分值。这使得只有一部分数据集的推文标有情感分数。我们还使用 nltk 库将所有 tweets 转换成小写。
数据可视化
我们使用 WordCloud 来表示每个情感中的大多数单词。为此,我们将所有带有正标签的 tweets 文本相加,形成一个字符串,并将该字符串表示为 pos_string。对于负面和中性文本,我们也遵循同样的方法。
for i in range(len(df_copy.index)):
if(df_copy.loc[i]["sent_score"]==1):
pos_text+=df_copy.loc[i]["full_text"]
elif(df_copy.loc[i]["sent_score"]==-1):
neg_text+=df_copy.loc[i]["full_text"]
else:
neut_text+=df_copy.loc[i]["full_text"]
文本表示使用 python 中的 WordCloud 模块生成,并使用 matplotlib 绘制。
list_text = [pos_text,neg_text,neut_text]
for txt in list_text:
word_cloud = WordCloud(width = 600,height = 600,max_font_size = 200).generate(txt)
plt.figure(figsize=(12,10))# create a new figure
plt.imshow(word_cloud,interpolation="bilinear")
plt.axis("off")
plt.show()
我们应该记住,单词之前已经被词干化和词条化,所以我们可能无法看到少数几个单词的完整单词。

Positive sentiment WordCloud
如你所见,标签由正面文本 Wordcloud 中的重要单词组成。这很直观,因为我们没有从文本中完全删除标签。虽然是一个印地语单词,但“swachh”(一个肯定的词)在这里经常出现。还有其他有用的词,如垃圾分类、垃圾收集、清洁等。,

Neutral sentiment WordCloud
在中性词 Cloud 中,除去少数标签,我们可以看到许多中性词,如-零件、街道、道路、周围环境、实习等。,

Negative sentiment WordCloud
停止乱扔垃圾,排水回来,排水是云的几个负面词汇,我们也可以看到它包含了许多其他情绪的词汇。
拆分数据
我们使用 nltk 库中 model_selection 模块的 train_test_split 函数将数据分为训练集、验证集和测试集。划分如下— 90%的训练数据,5%的验证数据,5%的测试数据。更多的数据用于训练,记住推特的数量更少。
import sklearn
from sklearn.model_selection import train_test_split
SEED=4
x = df.full_text
y = df.sent_score
x_train,x_val_test,y_train,y_val_test = train_test_split(x,y,test_size=0.1,random_state=SEED)
x_val,x_test,y_val,y_test = train_test_split(x_val_test,y_val_test,test_size=0.5,random_state=SEED)
将推文文本矢量化
在我们实现不同的 ML 文本分类器之前,我们需要将文本数据转换成向量。这是至关重要的,因为算法期望数据是某种数学形式,而不是文本形式。Quora 的这个回答解释了为什么转换到向量空间是必要的。这里,我们实现了来自 sklearn- Count 矢量器和 Tfidf 矢量器的两个矢量器。他们都属于词汇袋模式。
计数矢量器计算一个单词在文档中出现的次数(在每个 tweet 中),并使用这个值作为它的权重。为了形象化,向量中的列(矢量化后)由所有 tweet 中的所有不同单词组成,每行包含 tweet。计数器矢量器给我们一个稀疏矩阵(填充了许多零)作为矢量。
from sklearn.feature_extraction.text import CountVectorizer
cv=CountVectorizer(decode_error='ignore',lowercase=False,max_features=11)
x_traincv=cv.fit_transform(x_train.values.astype('U'))
让我们来看看 CountVectorizer 中的热门词(在所有推文中出现频率最高的词)。见这个的代码可视化顶词。

Count Vectorizer-Top words
让我们也实现 Tfidf 矢量器。TF-IDF 代表“术语频率-逆文档频率”。分配给标记的权重是术语(单词)频率和单词的文档频率的倒数的乘积。因此,如果一个词在 tweets 中出现得越频繁,它在这个矢量器中的重要性就越低(IDF-weight)。
from sklearn.feature_extraction.text import TfidfVectorizer
tf=TfidfVectorizer(decode_error='ignore',lowercase=False,max_features=11)
x_traintf=tf.fit_transform(x_train_copy.values.astype('U'))
Tfidf 的热门词汇是:

Top words- Tfidf Vectorizer
对比 CV 和 Tfidf 中的 top 词,我们看到虽然词是一样的,但是顺序和频率不同。此外,Tfidf 有频率的小数值,而 CV 只有整数值。
N-grams
简单来说,n-grams 是我们在文本(tweets)中可以找到的所有相邻单词的组合。句子“我喜欢玩具”的所有可能组合:单字母词-(“我”、“喜欢”、“玩具”)、双字母词-(“我喜欢”、“喜欢玩具”)、三字母词-(“我喜欢玩具”)。“Swachh”这个词后面更常跟“Bharat”。因此,bi-grams 为我们提供了“Swachh Bharat”以及“Swachh”和“Bharat”。CV 和 Tfidf 都给出了一个名为 ngram_range 的参数。ngram_range as (1,1)表示一元模型,(1,2)表示二元模型,(1,3)表示三元模型等等。我们用 CV 和 Tfidf 实现了 uni、bi 和三元模型,并观察它们中的哪一个给出了更高的准确度分数。
从 sklearn 实现分类器
在对我们的推文进行矢量化之后,我们就可以开始实施分类算法了。我们要实现的分类器是-朴素贝叶斯模型(MultinomialNB,BernoulliNB,)线性模型(LogisticRegression,RidgeClassifier,PassiveAggressiveClassifier,感知器),集成模型(RandomForest 分类器,AdaBoostClassifier)和 SVM 模型(LinearSVC)。我们使用精确度分数来衡量模型的性能(精确度分数、召回和混淆矩阵也被计算)。
使用 sklearn 中的 cross_validate 模块,对每个分类器执行 K-fold 交叉验证,以验证模型性能的一致性。折叠数设为 10,记录交叉验证分数的平均值和标准偏差。训练每个分类器所花费的时间也使用 time.time()函数来测量。之后,结果将存储在 CSV 文件中。下面给出了几行重要的(不是全部)代码。完整代码请看本。
classifiers = [MultinomialNB(),BernoulliNB(),LogisticRegression(),LinearSVC(),AdaBoostClassifier(),RidgeClassifier(),PassiveAggressiveClassifier(),Perceptron(),RandomForestClassifier()]
**for clf in classifiers**:
model = make_pipeline(vec,clf)#vec is CV or Tfidf
model.fit(x_train.values.astype('U'),y_train.values.astype('U'))
labels = model.predict(x_val_copy.values.astype('U'))
ac = accuracy_score(y_val_copy.values.astype('U'),labels)
kfold = KFold(n_splits=10,shuffle=False,random_state=None)
results = cross_validate( model,x_train_copy.values.astype('U'), y_train_copy.values.astype('U'),cv=kfold)
data.append([vec_gram,clf_names[i],ac,crossval_train_score_mean,crossval_test_score_mean,crossval_train_score_std,crossval_test_score_std, end-start])
i+=1
d = pd.DataFrame(data,columns=['Vec_Gram','Classifier','Ac','crossval_train_score_mean','crossval_test_score_mean','crossval_train_score_std','crossval_test_score_std','Time.2'])
d.to_csv('all_clfs.csv')
结果如下。

Results of all classifiers using Count Vectorizer, Tfidf Vectorizer, each with uni, bi, and trigrams.
在上图中,“Ac”代表准确度分数,“cv_1”表示带有 unigrams 的 CountVectorizer。PassiveAggressiveClassifier(tfi df-bigrams)和 LinearSVC(Tfidf-trigrams)给出了 83.5714%的最高准确度。但是前者只需要 4.5 秒就可以训练好,而后者需要 18 秒。在交叉验证均值和标准差方面,LinearSVC 似乎更好。值得注意的是,我们的数据集是不平衡的——正面和负面的推文比中性推文更多。因此,在这种情况下,准确性分数可能会导致我们得出错误的结论。为了避免这种情况,我们还使用了具有平衡类权重的随机森林分类器。该分类器的平均准确率为 74.94%,最高准确率为 78.21%。
实施 Gensim 模型
我们已经使用单词袋模型(CV 和 Tfidf)对文本文档进行数字表示。从数学上来说,这是一个非常简单的方法。在这种方法中,单词之间的语义关系没有被保留。说,一句话有四个字:国王,王后,女人,男人。在 BoW 模型中,这些单词中的每一个都被唯一地分配了 id。因此,单词“king”可能更接近于“woman”而不是“queen”。因此,我们需要一个复杂的模型来保存单词之间的关系,从而给出文本的更好的数学表示。doc 2 vec(word 2 vec 的扩展)模型为我们做到了这一点。

It would be better if the words are semantically related as in above figure rather than being assigned some random ids
Doc2Vec 算法来自 python 中的 Gensim 库。Gensim 是一个免费的 Python 库,旨在尽可能高效地从文档中自动提取语义主题。我们实现了 doc 2 vec-DBOW(Distributrb Bag of Words)、DMC(Distributed Memory Concatenated)、DMM(Distributed Memory Mean)三种算法以及这三种算法的几种组合。稍后,我们使用所有这些模型来训练相同的分类器,就像我们在上面使用 sklearn 所做的那样。这些算法中的每一个要完成的步骤如下——连接所有数据集(训练、验证和测试),给所有推文(可以是随机标签)提供索引(标签),训练模型(DBOW、DMC 或 DMM ),从每个模型中获取向量。
x_all = pd.concat([x_train,x_val,x_test])
串联的第一步是可选的(即,我们可以仅使用训练数据集),但是建议串联以获得更好的结果。然后我们用‘d2v _ index’标记文本,其中‘index’是 tweet 索引。
def label_tweets(tweets,label):
result = []
prefix = label
i=0
for i, t in zip(tweets.index, tweets):
t=str(t)
result.append(LabeledSentence(t.split(), [prefix + '_%s' % i]))
return result
x_all_w2v = label_tweets(x_all,'d2v')#x_all is obtained above by conatenating.
然后,我们训练这三个模型。
DBOW
cpu_count = multiprocessing.cpu_count()
model_dbow = Doc2Vec(dm=0,size=100,negative=5,min_count=2,alpha=0.065,workers=cpu_count,min_alpha=0.065)
model_dbow.build_vocab([each for each in tqdm(x_all_w2v)])for epoch in range(30):
model_dbow.train(utils.shuffle([each for each in tqdm(x_all_w2v)]),total_examples=len(x_all_w2v),epochs=1)
model_dbow.alpha-=0.002
model_dbow.min_alpha = model_dbow.alphatrain_vecs_dbow = get_d2v_vectors(model_dbow,x_train,100)
val_vecs_dbow = get_d2v_vectors(model_dbow,x_val,100)
在第一行中,我们获得了并行处理的 CPU 数量。在定义了模型(第二行)之后,我们使用三个带标签的 tweets (x_all_w2v)来构建我们的词汇表。稍后,我们训练模型,在每次迭代中调整我们的学习率。请阅读本文档以了解更多关于 Doc2Vec 函数的参数。
在定义了每个模型的特定参数后,以相同的方式训练其他两个模型。
DMC
cpu_count = multiprocessing.cpu_count()
model_dmc = Doc2Vec(dm=1,dm_concat=1,size=100,negative=5,min_count=2,alpha=0.065,workers=cpu_count,min_alpha=0.065)
model_dmc.build_vocab([each for each in tqdm(x_all_w2v)])for epoch in range(30):
model_dmc.train(utils.shuffle([each for each in tqdm(x_all_w2v)]),total_examples=len(x_all_w2v),epochs=1)
model_dmc.alpha-=0.002
model_dmc.min_alpha = model_dmc.alphatrain_vecs_dmc = get_d2v_vectors(model_dmc,x_train,100)
val_vecs_dmc = get_d2v_vectors(model_dmc,x_val,100)
数字万用表
cpu_count = multiprocessing.cpu_count()
model_dmm = Doc2Vec(dm=1,dm_mean=1,size=100,negative=5,min_count=2,alpha=0.065,workers=cpu_count,min_alpha=0.065)
model_dmm.build_vocab([each for each in tqdm(x_all_w2v)])for epoch in range(30):
model_dmm.train(utils.shuffle([each for each in tqdm(x_all_w2v)]),total_examples=len(x_all_w2v),epochs=1)
model_dmm.alpha-=0.002
model_dmm.min_alpha = model_dmm.alphatrain_vecs_dmm = get_d2v_vectors(model_dmm,x_train,100)
val_vecs_dmm = get_d2v_vectors(model_dmm,x_val,100)
我们保存了所有三个模型,以消除任何未来使用的重新培训。
model_dbow.save('d2v_model_dbow.doc2vec')
model_dmm.save('d2v_model_dmm.doc2vec')
model_dmc.save('d2v_model_dmc.doc2vec')
#AFTER THE FIRST TIME DON'T RETRAIN ALL THE MODELS,JUST LOAD THESE #like mod = model_dmm.load()
训练后,我们还使用 get_d2v_vectors()函数提取每个模型的向量,如下所示(还记得吗?这是我们使用 Gensim 模型的主要目的——获得文本数据的矢量化表示。下面的函数是在训练完模型后调用的(在上面的代码片段中)。
def get_d2v_vectors(model,corpus,size):
vecs = np.zeros((len(corpus),size))
n=0
for i in corpus.index:
prefix='d2v_'+str(i)
vecs[n] = model.docvecs[prefix]
n+=1
return vecs
我们已经准备好了使用 doc2vec 模型的三种矢量表示。(train_vectors_dbow,train_vectors_dmc,train_vectors_dmm)。在将它们用于分类器之前,还有另一个有用的步骤要执行——连接这些向量(例如,DBOW+DMM)。为了获得更好的结果,Doc2vec 的作者建议采取这一步骤。我们使用 numpy.append()函数来连接这些向量。
#FUNCTION TO CONCAT 2-MODELS VECTORS..NO SPECIAL TRAINING IS REQUIRED
def get_concat_vectors(model1,model2,corpus,size):
vecs = np.zeros((len(corpus),size))
n=0
for i in corpus.index:
prefix='d2v_'+str(i)
vecs[n] = np.append(model1.docvecs[prefix],model2.docvecs[prefix])
n+=1
return vecs
我们将 DBOW 与 DMM 以及 DBOW 与 DMC 连接起来。
train_vecs_dbow_dmm = get_concat_vectors(model_dbow,model_dmm,x_train,200)
val_vecs_dbow_dmm = get_concat_vectors(model_dbow,model_dmm,x_val,200)train_vecs_dbow_dmc = get_concat_vectors(model_dbow,model_dmc,x_train,200)
val_vecs_dbow_dmc = get_concat_vectors(model_dbow,model_dmc,x_val,200)
现在,我们使用相同的 sklearn 算法,以类似的方式使用这五个向量进行分类(就像我们对 CV 和 Tfidf 所做的那样)。
vecs = [(train_vecs_dbow,val_vecs_dbow),(train_vecs_dmc,val_vecs_dmc),(train_vecs_dmm,val_vecs_dmm),(train_vecs_dbow_dmm,val_vecs_dbow_dmm),(train_vecs_dbow_dmc,val_vecs_dbow_dmc)]
classifiers = [BernoulliNB(),LogisticRegression(),LinearSVC(),AdaBoostClassifier(),RidgeClassifier(),PassiveAggressiveClassifier(),Perceptron()]
gensim_names = ['DBOW','DMC','DMM','DBOW+DMM','DBOW+DMC']
data=[]
for train_vecs,val_vecs in vecs:
for clf in classifiers:
clf.fit(train_vecs,y_train)
ac = clf.score(val_vecs,y_val)
data.append([gensim_names[j],clf_names[i],ac,end-start])
d = pd.DataFrame(data,columns=['Model','Classifier','Ac','Time'])
d.to_csv('gensim_all_clfs.csv')
上面只给出了几行重要的代码。参见本。
让我们看看结果。

Results using Gensim models
逻辑回归(DBOW)、线性 SVC(DBOW)、逻辑回归(DBOW +DMC)和线性 SVC(DBOW+DMC)给出了 65.35%的最高准确度。考虑到训练时间,逻辑回归(DBOW)是其中最好的一个(0.52 秒)。RidgeClassifier(DBOW)也表现出良好的性能,准确率为 64.2%,训练时间仅为 0.007 秒。DBOW 模型优于其他两个模型,平均准确率(在所有分类器中)为 60.4%。Gensim 模型的性能比 CV 和 Tfidf 模型稍差。
总之,被动主动分类器(Tfidf-bigrams)的性能最好,准确率为 83.57%。所以我们用测试数据来评估一下。
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model import PassiveAggressiveClassifier
from sklearn.pipeline import make_pipeline
from sklearn.metrics import confusion_matrix
from sklearn.metrics import accuracy_score,precision_score,recall_score
import time
b=2
st = time.time()
tfidf = TfidfVectorizer(ngram_range=(1,b))
model = make_pipeline(tfidf,PassiveAggressiveClassifier())
model.fit(x_train.values.astype('U'),y_train.values.astype('U'))##
labels = model.predict(x_test.values.astype('U'))###cm = confusion_matrix(y_test.values.astype('U'),labels)
ac = accuracy_score(y_test.values.astype('U'),labels)
pr = precision_score(y_test.values.astype('U'),labels,average=None)
rc = recall_score(y_test.values.astype('U'),labels,average=None)
en = time.time()
print(b,"-gram",ac,pr.mean(),rc.mean(),en-st)
当在测试数据上评估该模型时,它给出了 80.42%的准确度。
摘要
使用 Tweepy 库收集推文,然后选择对我的任务有用的功能并转换为 CSV。然后,执行数据清理,如删除 URL、转发符号、用户名标签和标签。SentiWordNet 词典用于标注推文的情绪。对文本数据执行诸如停用词移除、词汇化、词干化之类的步骤。后来用 WordCloud 做数据可视化。拆分数据后,Count 矢量器和 Tfidf 矢量器用于文本的数学表示。然后,对先前获得的向量实施九种分类算法。后来,doc2vec 模型(DBOW、DMC、DMM)被训练并使用向量(通过这些模型获得)用于分类目的,因为这些模型保留了单词之间的语义关系。最后,使用测试数据评估最佳模型。
欢迎对这个项目的改进提出任何建议。感谢您的阅读!
参考文献
2https://bhaskarvk . github . io/2015/01/how-to-use-twitters-search-rest-API-most-effectively。/
情感分析:简化
看看自然语言处理的“你好世界”
什么是情感分析?
情感分析是自然语言处理的一个领域,负责从自然语言中提取观点的系统。NLP 的目标是创建能够像我们人类一样理解语言的管道。情感分析是自然语言处理中最基本的问题之一,也是学生在自然语言处理课程中首先面临的问题之一。

为什么要进行情感分析?
从能够从产品评论中挖掘意见到能够通过研究推文来预测股票价格,情绪分析有非常广泛的应用。由于问题的直观性,情感分析构成了我们称之为自然语言理解的几乎所有其他管道的基础。
从讲师的角度来看,情感分析包含了在 NLP 工作的数据科学家应该了解的一切。句子处理和 NLP 中使用的所有通用模型/架构都可以包含在情感分析的范围内。

情感分析的类型
情感分析本质上是一个分类问题。虽然情感分析包含各种各样的问题,但最常见的类型可以大致分为:
- 极性检测:谈论句子的极性,即正、负或中性。有时,分类甚至可以更精细,如非常积极、积极、中性、消极和非常消极。
- 情感检测:从句子中检测说话者的情感,例如,高兴、悲伤、生气等。
- 意图检测:不仅能够检测出句子中的内容,还能检测出句子的意图。
基本管道
让我们首先讨论从原始文本中提取特征。提供给情感分析的输入并不都是有用的。虽然最近的深度学习模型已经促进了将所有特征工程转移到这些模型,但是 NLP 实践者仍然更喜欢在通过任何管道之前清理输入。

在将单词转换成数学特征后,情感分析就变得类似于一个时间序列问题。这是因为在一个句子中使用的单词彼此相关,它们在句子中出现的顺序也很重要。最近,基于 LSTM 的深度学习模型在情感分析方面非常成功。

下一步是什么?
情感分析的最大挑战之一是能够捕捉句子出现的上下文及其语气。讽刺是普通情感分析系统面临的最大问题之一。研究人员目前正在努力提高理解上下文的能力。
这个博客是努力创建机器学习领域简化介绍的一部分。点击此处查看完整系列
在你一头扎进去之前就知道了
towardsdatascience.com](/machine-learning-simplified-1fe22fec0fac)
或者只是阅读系列的下一篇博客
[## 社交网络中有影响力的社区:简化版
为什么要研究社交网络中的社区?社交网络是大规模现实世界网络的主要例子。他们…
towardsdatascience.com](/influential-communities-in-social-network-simplified-fe5050dbe5a4)
参考文献
【1】https://monkey learn . com/情操-分析/#情操-分析-用例-应用 【2】刘、冰。"情感分析和观点挖掘."人类语言技术综合讲座 5.1(2012):1–167。
[3]帕克、亚历山大、帕特里克·帕鲁贝克。" Twitter 作为情感分析和观点挖掘的语料库."LREc。第十卷。№2010.2010.
[4]王、叶泉、黄敏烈、李昭。"面向方面级情感分类的基于注意力的 LSTM . "2016 年自然语言处理经验方法会议论文集。2016.
使用 ALBERT 进行情感分析
使用 Albert 进行情感分析
让我们微调谷歌最新的情感分析任务的自然语言处理模型
每个研究人员或 NLP 从业者都很清楚 2018 年到来的 BERT。从那时起,NLP 行业已经发生了很大的变化。
阿尔伯特是一个 Lite 伯特是在焦点,使它尽可能轻,通过减少参数大小。
深度学习对于情感分析任务的巨大优势是我们预处理数据的步骤减少了。唯一需要的预处理是将它们转换成小写字母。如果我们使用机器学习方法,如 TF-IDF 的逻辑回归,那么你需要将单词词条化,并删除不必要的单词。如果你正在考虑删除停用词,那么看看这篇文章
删除停用词真的能提高模型性能吗?
towardsdatascience.com](/why-you-should-avoid-removing-stopwords-aa7a353d2a52)
如果您想了解最新的文本预处理步骤,请查阅这篇文章。
让我们讨论一些技术的缺点以及如何改进它们
towardsdatascience.com](/updated-text-preprocessing-techniques-for-sentiment-analysis-549af7fe412a) 
微调
- 首先克隆这个 GitHub repo 。
- 准备数据集。制表符分隔的(。tsv)文件是必需的。数据集需要放在同一目录的文件夹中。
- 数据集将有 2 列。一个包含文本,另一个包含标签。
- 写入列车命令
$ python run_glue.py --data_dir data --model_type albert --model_name_or_path albert-base-v2 --output_dir output --do_train --task_type sst-2
命令行参数-
数据目录 -存放 train.tsv 文件的地方
model_type -您想要用于情感分析任务的模型。这里我们用的是阿尔伯特。
model _ name _ or _ path-您要使用的模型的变体。这里我们使用的是 albert-base-v2。
输出-目录- 你想要保存模型的目录。该脚本将自动创建文件夹。
do-train - 因为我们正在进行列车运行。
task_type - 可以执行两个任务——SST-2 和 SST-5。
以下是您可以使用的各种型号的列表

5.在模型被训练之后,所有的模型文件都将在一个文件夹中。
6.替换 api.py 文件中的模型目录。
7.运行 api.py 文件
$ python api.py
8.如果你想调用它的预测方法
$ from api import SentimentAnalyzer
$ classifier = SentimentAnalyszer()
$ classifier.predict('It was a good movie')
参考
- https://github . com/Google-research/Google-research/tree/master/Albert
- https://github.com/huggingface/transformers
使用 LSTM 进行情感分析(循序渐进教程)
使用 PyTorch 框架进行深度学习

Sentiment Analysis, Image by: Monkeylearn
什么是情绪分析:

Sentiment Analysis from Dictionary
我认为谷歌词典的这个结果给出了一个非常简洁的定义。我不用再强调情感分析变得多么重要了。因此,这里我们将在 IMDB 电影数据集上建立一个分类器,使用一种叫做 RNN 的深度学习技术。
我概述了如何使用长短期记忆(LSTM) 架构实现递归神经网络(RNN) 的逐步过程:
- 加载并可视化数据
- 数据处理—转换为小写
- 数据处理—删除标点符号
- 数据处理—创建评论列表
- Tokenize 创建 Vocab 到 Int 映射字典
- 标记化—对单词进行编码
- 标记化—对标签进行编码
- 分析评论长度
- 去除离群值——去除过长或过短的评论
- 填充/截断剩余数据
- 训练、验证、测试数据集分割
- 数据加载器和批处理
- 定义 LSTM 网络架构
- 定义模型类
- 训练网络
- 测试(针对测试数据和用户生成的数据)
1 )载入并可视化数据
我们正在使用 IMDB 电影评论数据集。如果它以 txt 文件的形式存储在你的机器中,那么我们只需把它加载进来
**# read data from text files**
with open(‘data/reviews.txt’, ‘r’) as f:
reviews = f.read()
with open(‘data/labels.txt’, ‘r’) as f:
labels = f.read()print(reviews[:50])
print()
print(labels[:26])**--- Output ---**bromwell high is a cartoon comedy . it ran at the same time as some other programs about school life such as teachers . my yearspositive
negative
positive
2 )数据处理—转换成小写
reviews = reviews.lower()
3 数据处理—去掉标点符号
from string import punctuation
print(punctuation)**--- Output ---**!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~
我们看到了 python 中预定义的所有标点符号。为了去掉所有这些标点符号,我们将简单地使用
all_text = ''.join([c for c in reviews if c not in punctuation])
4)数据处理—创建评论列表
我们把所有的字符串都放在一个大字符串中。现在,我们将分离出单独的评论,并将它们存储为单独的列表元素。像【点评 _1,点评 _2,点评 _3……】。复习 _n]
reviews_split = all_text.split(‘\n’)
print ('Number of reviews :', len(reviews_split))
评论数量:25001
5)标记化—创建 Vocab 到 Int 的映射字典
在大多数 NLP 任务中,您将创建一个索引映射字典,以便为经常出现的单词分配较低的索引。最常见的方法之一是使用Collections库中的Counter方法。
from collections import Counterall_text2 = ' '.join(reviews_split)
# create a list of words
words = all_text2.split()# Count all the words using Counter Method
count_words = Counter(words)
total_words = len(words)
sorted_words = count_words.most_common(total_words)
让我们来看看我们创建的这些对象
print (count_words)**--- Output ---**Counter({'the': 336713, 'and': 164107, 'a': 163009, 'of': 145864
为了创建一个 vocab 到 int 的映射字典,您只需这样做
vocab_to_int = {w:i for i, (w,c) in enumerate(sorted_words)}
这里有一个小技巧,在这个映射中,索引将从 0 开始,即“the”的映射将是 0。但是稍后我们将为较短的评论填充,填充的常规选择是 0。所以我们需要从 1 开始索引
vocab_to_int = {w:i+1 for i, (w,c) in enumerate(sorted_words)}
让我们来看看这个映射字典。我们可以看到“the”的映射现在是 1
print (vocab_to_int)**--- Output ---**{'the': 1, 'and': 2, 'a': 3, 'of': 4,
6)标记化——对单词进行编码
到目前为止,我们已经使用来自所有评论的 vocab 创建了 a)评论列表和 b)索引映射字典。所有这些都是为了创建评论的编码(用整数代替评论中的单词)
reviews_int = []
for review in reviews_split:
r = [vocab_to_int[w] for w in review.split()]
reviews_int.append(r)
print (reviews_int[0:3])**--- Output ---**[[21025, 308, 6, 3, 1050, 207, 8, 2138, 32, 1, 171, 57, 15, 49, 81, 5785, 44, 382, 110, 140, 15, .....], [5194, 60, 154, 9, 1, 4975, 5852, 475, 71, 5, 260, 12, 21025, 308, 13, 1978, 6, 74, 2395, 5, 613, 73, 6, 5194, 1, 24103, 5, ....], [1983, 10166, 1, 5786, 1499, 36, 51, 66, 204, 145, 67, 1199, 5194.....]]
注意:我们现在创建的是一个列表列表。每个评论都是一个整数值列表,所有这些都存储在一个巨大的列表中
7)标记化——对标签进行编码
这很简单,因为我们只有 2 个输出标签。因此,我们将“正”标记为 1,“负”标记为 0
encoded_labels = [1 if label =='positive' else 0 for label in labels_split]
encoded_labels = np.array(encoded_labels)
8)分析评论长度
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inlinereviews_len = [len(x) for x in reviews_int]
pd.Series(reviews_len).hist()
plt.show()pd.Series(reviews_len).describe()

Review Length Analysis
观察结果 : a)平均评论长度= 240 b)部分评论长度为 0。保留此评论对我们的分析没有任何意义 c)大多数评论少于 500 字或更多 d)有相当多的评论非常长,我们可以手动调查它们,以检查我们是否需要将它们包括在我们的分析中或从我们的分析中排除
9)剔除异常值——剔除过长或过短的评论
reviews_int = [ reviews_int[i] for i, l in enumerate(reviews_len) if l>0 ]
encoded_labels = [ encoded_labels[i] for i, l in enumerate(reviews_len) if l> 0 ]
10)填充/截断剩余数据
为了处理短评论和长评论,我们会将所有评论填充或截断到特定长度。我们用序列长度来定义这个长度。该序列长度与 LSTM 层的时间步数相同。
对于比seq_length短的评论,我们会用 0 填充。对于长于seq_length的评论,我们会将其截断为第一个 seq_length 单词。
def pad_features(reviews_int, seq_length):
''' Return features of review_ints, where each review is padded with 0's or truncated to the input seq_length.
'''
features = np.zeros((len(reviews_int), seq_length), dtype = int)
for i, review in enumerate(reviews_int):
review_len = len(review)
if review_len <= seq_length:
zeroes = list(np.zeros(seq_length-review_len))
new = zeroes+review elif review_len > seq_length:
new = review[0:seq_length]
features[i,:] = np.array(new)
return features
注意:我们正在创建/维护一个 2D 数组结构,就像我们为reviews_int创建的一样。输出将如下所示
print (features[:10,:])

11)训练、验证、测试数据集拆分
一旦我们把数据整理好,我们将把它分成训练集、验证集和测试集
训练= 80% |有效= 10% |测试= 10%
split_frac = 0.8
train_x = features[0:int(split_frac*len_feat)]
train_y = encoded_labels[0:int(split_frac*len_feat)]remaining_x = features[int(split_frac*len_feat):]
remaining_y = encoded_labels[int(split_frac*len_feat):]valid_x = remaining_x[0:int(len(remaining_x)*0.5)]
valid_y = remaining_y[0:int(len(remaining_y)*0.5)]test_x = remaining_x[int(len(remaining_x)*0.5):]
test_y = remaining_y[int(len(remaining_y)*0.5):]
12)数据加载器和批处理
在创建我们的训练、测试和验证数据之后。下一步是为这些数据创建数据加载器。我们可以使用生成器函数将我们的数据分批,而不是使用 TensorDataset 。这是 PyTorch 中一个非常有用的实用程序,可以像使用 torchvision 数据集一样轻松地使用数据加载器的数据
import torch
from torch.utils.data import DataLoader, TensorDataset# create Tensor datasets
train_data = TensorDataset(torch.from_numpy(train_x), torch.from_numpy(train_y))
valid_data = TensorDataset(torch.from_numpy(valid_x), torch.from_numpy(valid_y))
test_data = TensorDataset(torch.from_numpy(test_x), torch.from_numpy(test_y))# dataloaders
batch_size = 50# make sure to SHUFFLE your data
train_loader = DataLoader(train_data, shuffle=True, batch_size=batch_size)
valid_loader = DataLoader(valid_data, shuffle=True, batch_size=batch_size)
test_loader = DataLoader(test_data, shuffle=True, batch_size=batch_size)
为了获得一批可视化的训练数据,我们将创建一个数据迭代器
# obtain one batch of training data
dataiter = iter(train_loader)
sample_x, sample_y = dataiter.next()print('Sample input size: ', sample_x.size()) # batch_size, seq_length
print('Sample input: \n', sample_x)
print()
print('Sample label size: ', sample_y.size()) # batch_size
print('Sample label: \n', sample_y)

这里,50 是批量大小,200 是我们定义的序列长度。现在,我们的数据准备步骤已经完成,接下来我们将查看 LSTM 网络架构,以开始构建我们的模型
13 )定义 LSTM 网络架构

LSTM Architecture for Sentiment Analysis. Image by Author
这些层如下所示:
0.令牌化:这不是 LSTM 网络的一个层,而是将我们的单词转换成令牌(整数)的一个强制步骤
- 嵌入层:将单词标记(整数)转换成特定大小的嵌入
- LSTM 层:由隐藏状态变暗和层数定义
- 全连接图层:将 LSTM 图层的输出映射到所需的输出大小
- Sigmoid 激活层:将所有输出值转换为 0 到 1 之间的值
- 输出:最后一个时间步长的 Sigmoid 输出被认为是该网络的最终输出
注:如果你想更多地了解这些 LSTM 层,并获得事物的微观观点。读读这个—
使用 PyTorch 框架进行深度学习
towardsdatascience.com](/reading-between-the-layers-lstm-network-7956ad192e58)
14)定义模型类
15)训练网络
- 实例化网络
# Instantiate the model w/ hyperparams
vocab_size = len(vocab_to_int)+1 # +1 for the 0 padding
output_size = 1
embedding_dim = 400
hidden_dim = 256
n_layers = 2net = SentimentLSTM(vocab_size, output_size, embedding_dim, hidden_dim, n_layers)print(net)SentimentLSTM(
(embedding): Embedding(74073, 400)
(lstm): LSTM(400, 256, num_layers=2, batch_first=True, dropout=0.5)
(dropout): Dropout(p=0.3)
(fc): Linear(in_features=256, out_features=1, bias=True)
(sig): Sigmoid()
)
- 训练循环
training loop 中的大多数代码都是非常标准的深度学习训练代码,您可能会在所有使用 PyTorch 框架的实现中经常看到这些代码。
16)测试
-
关于测试数据
-
关于用户生成的数据
首先,我们将定义一个负责预处理步骤的tokenize函数,然后我们将创建一个predict函数,它将在解析用户提供的评论后给出最终输出。
结果:
test_review = 'This movie had the best acting and the dialogue was so good. I loved it.'seq_length=200 # good to use the length that was trained onpredict(net, test_review_neg, seq_length)
阳性审查检出
感谢阅读!
- 如果你喜欢这个,关注我的 medium 了解更多。
- 你们的掌声对写更多、写得更好是一个巨大的鼓励和帮助。
- 有兴趣合作吗?我们在 Linkedin 上连线吧。
- 请随意写下您的想法/建议/反馈。
更新:另一篇文章给你一个在层内发生的事情的微观视角。
使用 PyTorch 框架进行深度学习
towardsdatascience.com](/reading-between-the-layers-lstm-network-7956ad192e58)
基于深度学习的网飞评论情感分析
深入分析
网飞评论中人类情感的识别和分类
在这篇文章中,我将涉及情感分析的主题,以及如何实现一个深度学习模型,该模型可以识别和分类网飞评论中的人类情感。

如果你喜欢这篇文章,想分享你的想法,问问题或保持联系,请随时通过 LinkedIn 与我联系。
对企业来说,最重要的因素之一是与客户群保持联系。对于这些公司来说,准确地了解消费者或客户对新的和现有的产品或服务、最近的计划和客户服务的想法是至关重要的。
情感分析是完成这一必要任务的一种方式。
情感分析是自然语言处理(NLP)的一个领域,它建立模型,试图识别和分类表达的属性,例如:
- 极性:如果说话人表达了正面或负面的意见,
- 主题:正在谈论的事情,
- 意见持有人:表达意见的个人或实体。
在我们每天产生 2.5 万亿字节数据的世界里,情感分析已经成为理解这些数据的关键工具。这使得公司能够获得关键的洞察力,并自动化各种流程。
情感分析可以帮助将非结构化信息自动转换为公众对产品、服务、品牌、政治或人们可以表达意见的任何其他主题的意见的结构化数据。这些数据对于商业应用非常有用,如市场分析、公共关系、产品评论、网络推广评分、产品反馈、和客户服务。
在下文中,我将向您展示如何实现一个深度学习模型,该模型可以将网飞的评论分为正面或负面。该模型将整个评论作为输入(一个词接一个词),并提供百分比评级,以检查评论是否传达了积极或消极的情绪。
我使用的数据集包含大约 5000 个负面和 5000 个正面评论。这里是来自数据集的 5 个样本评论,在文章的最后将按照模型进行分类:
***"The film is a hoot and is just as good if not better than much of what s on saturday morning tv, especially the pseudo educational stuff we all can’t stand.”
"The things this movie tries to get the audience to buy just won’t fly with most intelligent viewers.”******"Although life or something like it is very much in the mold of feel good movies, the cast and director stephen herek’s polished direction pour delightfully piquant wine from aged bottles.”
"This is the case of a pregnant premise being wasted by a script that takes few chances and manages to insult the intelligence of everyone in the audience.”
"One of the finest most humane and important holocaust movies ever made."***
深度学习模型+所有必要的数据可以在我的 GitHub repo 中找到。
让我们从一些理论开始。
1.递归神经网络
递归神经网络 ( RNNs )是流行的模型,在许多 NLP 任务中显示出巨大的前景。
RNN 的利用文本等顺序信息。在“传统的”前馈神经网络中,我们假设所有的输入都是相互独立的。但是对于许多任务来说,这是一个非常糟糕的想法。例如,一个句子具有清晰的语法结构和顺序,其中每个单词都依赖于前一个单词。如果你想让你的神经网络学习意思(或者在我们的例子中是情感),网络必须知道哪些单词以什么顺序出现。
rnn被称为递归,因为它们对序列的每个元素执行相同的任务,其输出取决于之前的计算。另一种看待 rnn 的方式是,它们有一个“记忆”,这个“记忆”记录了到目前为止已经计算过的信息。这是一个典型的 RNN 的样子:

Fig. 1 Recurrent Neural Network architecture.
【x(t-1)、 x(t) 、 x(t+1) 是相互依赖的顺序输入(如句子中的词)。【y(t _ 1)y(t)y(t+1)都是输出。对 RNN 来说独一无二的是,计算当前隐藏状态【t】的神经元对于输入的【t】依赖于先前的隐藏状态【t-1】对于先前的输入【t-1】。 Wxh 和 Whh 是连接输入【x(t)与隐藏层【t】,以及 h(t) 与【t-1】的权重矩阵这样,我们向神经网络引入了一个递归,该递归可以被认为是对先前输入的记忆。理论上,这种方式“香草” RNNs 可以利用任意长序列中的信息,但在实践中,它们被限制为只能回顾几步。
这就是 LSTMs 派上用场的地方。
1.1 个 LSTMs
长短期记忆网络——通常简称为“lstm”——是一种特殊的 RNN ,能够学习长期依赖关系。lstm 的架构与rnn没有本质上的不同,但是它们集成了额外的组件。

LSTMs 的关键是单元格状态【C(t),贯穿图表顶部的水平线。单元状态是除了仅使用隐藏状态【t】之外的另一种存储内存的方式。然而, C(t) 使得 LSTMs 可以与普通 RNNs 相反地处理更长的序列。
此外,LSTMs 有能力删除或添加信息到细胞状态,由称为门的结构仔细调节。门是选择性地让信息通过的一种方式。一个 LSTM 有三个这样的门,用来保护和控制细胞状态。
- 遗忘门:在得到隐藏状态 h(t-1 ) of 之前的输入 x(t-1) 之后,遗忘门帮助我们决定什么必须从【t-1】状态中删除,从而只保留相关的东西。
- 输入门:在输入门中,我们决定将当前输入的新东西添加到我们当前的单元格状态【C(t)。
- 输出门:输出门顾名思义,决定从当前单元状态【C(t)到下一个【C(t+1)输出什么。对于语言模型示例,由于它刚刚看到了一个主题,它可能希望输出与一个动词相关的信息,以防接下来会出现这个信息。例如,它可能会输出主语是单数还是复数,这样我们就知道动词应该变成什么形式,如果接下来是什么形式的话。
每种状态背后都有独立的神经网络。可以想象,这使得 LSTMs 非常复杂。在这一点上,我不会去更多的关于 LSTMs 的细节。
2.预处理
在我们可以使用评论作为递归神经网络的输入之前,需要对数据进行一些预处理。我们在这里的主要目的是缩小观察空间。
2.1 单词的统一拼写
考虑一下“ 有事 ”和“ 有事 ”这样的词。对于我们人类来说,这些单词有着相同的意思,它们之间唯一的区别是第一个单词是大写的,因为它可能是一个句子中的第一个单词。但是对于神经网络来说,这些单词将会有(至少在开始时)不同的意思,因为它们的拼写不同。只有在训练期间,神经网络可能会也可能不会学会识别这些单词的意思。我们的目标是防止这种误解。
正因为如此,预处理的第一步就是把所有的单词都变成小写的单词。
2.2 删除特殊字符
特殊字符如。, ! ?等。不会影响评论的情绪,因此可以删除。
最终结果
考虑以下未处理的审查样本:
****"Although life or something like it is very much in the mold of feel good movies, the cast and director stephen herek’s polished direction pour delightfully piquant wine from aged bottles.”****
在我们完成前面提到的预处理步骤后,review 示例如下所示:
****"although life or something like it is very much in the mold of feel good movies the cast and director stephen hereks polished direction pour delightfully piquant wine from aged bottles”****
预处理应用于数据集中的每个评论。
2.3 词对索引
另一个主要步骤是创建所谓的单词到索引映射,它为数据集中的每个单词分配一个唯一的整数值。我在这个项目中使用的包含所有正面和负面评论的数据集由 18339 个独特的单词组成。因此,单词-索引映射具有相同数量的条目。这个数字也被称为词汇量。
我获得的单词到索引映射中的第一个和最后一个条目如下所示:

为数据集中的单词分配唯一整数的这一步至关重要,因为我们不能将字符串数据输入到神经网络中。相反,word-to-index 允许我们使用整数来表示整个句子和评论。考虑下面的评论:
****"the things this movie tries to get the audience to buy just wont fly with most intelligent viewers”****
使用单词到索引的映射,评论可以由整数数组表示,其中每个整数根据映射表示一个单词:
****[0, 5094, 147, 81, 1269, 5, 532, 0, 1303, 5, 1835, 652, 236, 1101, 125, 188, 712, 855]****
3.单词嵌入
当然,神经网络既不能接受字符串,也不能接受单个整数值作为输入。相反,我们必须使用单词嵌入。
**单词嵌入是文本的分布式表示,这可能是深度学习方法在挑战 NLP 问题上令人印象深刻的表现的关键突破之一。单词嵌入实际上是一类技术,其中单个单词由一个实值向量表示,通常有几十或几百维。每个单词被映射到一个特定的向量,向量值由神经网络学习。
这与稀疏单词表示所需的数千或数百万维形成对比,例如一键编码。例如,我们可以嵌入单词 “虽然” 和 “生活” 作为 10 维向量:
****although = [0.8 1.0 4.2 7.5 3.6]
life = [8.3 5.7 7.8 4.6 2.5 ]****
代表数据集中一个单词的每个向量都是从一个称为嵌入矩阵的大矩阵中获得的。该矩阵的行数表示单词嵌入的维度,列数表示词汇大小或数据集中唯一单词的数量。因此,该矩阵的每一列代表数据集中唯一单词的嵌入向量。
我们怎么知道哪一列代表哪个单词?这就是我们使用单词到索引映射的地方。考虑你想要得到单词 【虽然】, 的嵌入向量,根据单词到索引的映射,这个单词由数字 2511 表示。在下一步中,需要创建一个大小为 18339 ( 数据集中的字数),的单热编码向量,其中除了值为 1 的第 2511 个条目之外,每个条目都为 0。
通过在嵌入矩阵和一位热编码向量之间做点积,我们获得矩阵的第 2511 列,这是单词 “虽然”的嵌入向量。

这样,我们可以将整个字符串或网飞评论输入到 LSTM 中。我们只需在单词到索引的映射中查找每个单词的整数值,创建适当的一个热编码向量,并用矩阵执行点积。这篇评论然后被一个字一个字地(一个向量接一个向量地)输入 LSTM 的网络。

4.获取评论的情绪
到目前为止,您已经看到了如何预处理数据以及如何在 LSTM 网络中输入评论。现在,让我们讨论如何最终获得给定评论的情感。
对于每个时间步长 t ,LSTM 网络接收输入向量 x(t) ,这产生输出向量 y(t) 。重复该过程,直到 x(n),n 是评论中的字数。就说 n =20 个字吧。直到x(n)LSTM网络产生了 y(n) 输出向量。这 20 个向量中的每一个都代表了一些东西,但不是我们要寻找的情感。更确切地说,向量 y 是评论特征的编码表示,其(根据神经网络)在确定情感方面将是重要的。
y(8) 代表神经网络为评论的前 8 个单词识别的特征。另一方面, y(20) 代表整个评论的特征。尽管在实践中仅使用最后的输出向量 y(20) 就足够了,但是我已经发现,如果我们使用所有的向量 y(0) — y(20) 来确定情感,则会导致更准确的结果。这可以通过计算所有向量的平均值来实现。我们姑且称这个均值向量 y_mean。
最后,在 y_mean 中编码的评论的特征表示可用于将评论分类为正面或负面的类别。为了做到这一点,需要添加一个最终的分类层,它无非是 y_mean 和另一个权重矩阵 W 之间的点积。
我刚才描述的这个情感分析的过程是在我的GitHub repo中的一个深度学习模型中实现的。欢迎大家来看看,亲自尝试一下。在模型被训练之后,可以对尚未看到的评论执行情感分析:**
***Test Samples:****Review**: "the film is a hoot and is just as good if not better than much of whats on saturday morning tv especially the pseudo educational stuff we all cant stand"**pos. sentiment:** 0.96 %
**neg. sentiment:** 0.04 % **Review:** "the things this movie tries to get the audience to buy just wont fly with most intelligent viewers"**pos. sentiment:** 0.11 % **neg. sentiment:** 0.89 % **Review:** "although life or something like it is very much in the mold of feel good movies the cast and director stephen hereks polished direction pour delightfully piquant wine from aged bottles"**pos. sentiment:** 0.97 %
**neg. sentiment:** 0.03 % **Review:** "this is the case of a pregnant premise being wasted by a script that takes few chances and manages to insult the intelligence of everyone in the audience"**pos. sentiment:** 0.02 %
**neg. sentiment:** 0.98 %*
用 Python 进行情感分析(第 2 部分)
改进电影评论情感分类器

Photo by izayah ramos on Unsplash
在这个系列的第一部分,我们建立了一个准系统的电影评论情感分类器。下一篇文章的目标是概述几种可以用来增强 NLP 模型的技术。也就是说,我们不会在任何特定的话题上做太深入的探讨。
如果你还没有,你可以在这里阅读我的第一篇文章:
IMDb 电影评论分类
towardsdatascience.com](/sentiment-analysis-with-python-part-1-5ce197074184)
本系列中使用的所有代码以及补充材料都可以在这个 GitHub 资源库中找到。
文本处理
对于我们的第一次迭代,我们做了非常基本的文本处理,如删除标点符号和 HTML 标签,并使一切都小写。我们可以通过删除停用词和规范文本来进一步清理。
为了进行这些转换,我们将使用自然语言工具包 (NLTK)中的库。这是一个非常流行的 Python 的 NLP 库。
删除停用词
停用词是非常常见的词,如“如果”、“但是”、“我们”、“他”、“她”和“他们”。我们通常可以在不改变文本语义的情况下删除这些单词,并且经常(但不总是)这样做可以提高模型的性能。当我们开始使用更长的单词序列作为模型特征时,删除这些停用词变得更加有用(见下面的 n-grams)。
在之前
"bromwell high is a cartoon comedy it ran at the same time as some other programs about school life such as teachers my years in the teaching profession lead me to believe that bromwell high’s satire is much closer to reality than is teachers the scramble to survive financially the insightful students who can see right through their pathetic teachers’ pomp the pettiness of the whole situation all remind me of the schools i knew and their students when i saw the episode in which a student repeatedly tried to burn down the school i immediately recalled at high a classic line inspector i’m here to sack one of your teachers student welcome to bromwell high i expect that many adults of my age think that bromwell high is far fetched what a pity that it isn’t"
在之后
"bromwell high cartoon comedy ran time programs school life teachers years teaching profession lead believe bromwell high's satire much closer reality teachers scramble survive financially insightful students see right pathetic teachers' pomp pettiness whole situation remind schools knew students saw episode student repeatedly tried burn school immediately recalled high classic line inspector i'm sack one teachers student welcome bromwell high expect many adults age think bromwell high far fetched pity"
注意:在实践中,删除停用词的一个更简单的方法是在 scikit-learn 的任何“矢量器”类中使用stop_words参数。如果你想使用 NLTK 的停用词完整列表,你可以做stop_words='english’。在实践中,我发现使用 NLTK 的列表实际上会降低我的性能,因为它太昂贵了,所以我通常提供我自己的单词列表。例如,stop_words=['in','of','at','a','the']。
正常化
文本预处理的一个常见的下一步是通过将一个给定单词的所有不同形式转换成一个来使你的语料库中的单词规范化。存在的两种方法是词干和词汇化。
词干
词干被认为是更原始/更强力的规范化方法(尽管这不一定意味着它的性能会更差)。有几种算法,但总的来说,它们都使用基本规则来切断单词的结尾。
NLTK 有几个词干算法实现。我们将在这里使用波特词干分析器,但是你可以在这里通过例子探索所有的选项: NLTK 词干分析器
词汇化
词汇化的工作原理是识别给定单词的词性,然后应用更复杂的规则将该单词转换为其真正的词根。
结果
没有归一化
"this is not the typical mel brooks film it was much less slapstick than most of his movies and actually had a plot that was followable leslie ann warren made the movie she is such a fantastic under rated actress there were some moments that could have been fleshed out a bit more and some scenes that could probably have been cut to make the room to do so but all in all this is worth the price to rent and see it the acting was good overall brooks himself did a good job without his characteristic speaking to directly to the audience again warren was the best actor in the movie but fume and sailor both played their parts well"
词干
"thi is not the typic mel brook film it wa much less slapstick than most of hi movi and actual had a plot that wa follow lesli ann warren made the movi she is such a fantast under rate actress there were some moment that could have been flesh out a bit more and some scene that could probabl have been cut to make the room to do so but all in all thi is worth the price to rent and see it the act wa good overal brook himself did a good job without hi characterist speak to directli to the audienc again warren wa the best actor in the movi but fume and sailor both play their part well"
术语化
"this is not the typical mel brook film it wa much le slapstick than most of his movie and actually had a plot that wa followable leslie ann warren made the movie she is such a fantastic under rated actress there were some moment that could have been fleshed out a bit more and some scene that could probably have been cut to make the room to do so but all in all this is worth the price to rent and see it the acting wa good overall brook himself did a good job without his characteristic speaking to directly to the audience again warren wa the best actor in the movie but fume and sailor both played their part well"
n-grams
上次我们在模型中只使用了单个单词的特征,我们称之为 1-grams 或 unigrams。我们还可以通过添加两个或三个单词序列(二元模型或三元模型)来为我们的模型增加更多的预测能力。例如,如果一条评论有三个单词序列“不喜欢电影”,我们只会用单字母模型单独考虑这些单词,可能不会捕捉到这实际上是一种负面的情绪,因为单词“喜欢”本身将与正面评论高度相关。
scikit-learn 库使这变得非常容易。只需对任何“矢量器”类使用ngram_range参数。
接近 90%了!因此,除了单个单词之外,简单地考虑两个单词的序列将我们的准确率提高了 1.6 个百分点以上。
注意:对于你的模型,n 的大小在技术上没有限制,但是有几件事需要考虑。第一,增加克数不一定会给你更好的表现。其次,随着 n 的增加,矩阵的大小呈指数增长,因此,如果您有一个由大型文档组成的大型语料库,您的模型可能需要很长时间来训练。
陈述
在第 1 部分中,我们将每个评论表示为一个二元向量(1 和 0 ),语料库中的每个唯一单词都有一个槽/列,其中 1 表示给定的单词在评论中。
虽然这种简单的方法可以很好地工作,但我们有办法将更多的信息编码到向量中。
字数
我们可以包括给定单词出现的次数,而不是简单地记录一个单词是否出现在评论中。这可以给我们的情感分类器更多的预测能力。例如,如果一个电影评论者在评论中多次说“惊人”或“糟糕”,那么该评论很可能分别是正面的或负面的。
TF-IDF
另一种表示语料库中每个文档的常见方法是对每个单词使用 tf-idf 统计量 ( 词频-逆文档频率 ) ,这是一种加权因子,我们可以使用它来代替二进制或字数表示。
有几种方法可以进行 tf-idf 转换,但简单地说,tf-idf 的目标是表示给定单词在文档(在我们的例子中是电影评论)中出现的次数相对于该单词在语料库中出现的文档数——其中出现在许多文档中的单词的值接近于 0,出现在较少文档中的单词的值接近于 1。
注意:现在我们已经讨论了 n 元语法,当我提到“单词”时,我实际上是指任何 n 元语法(单词序列),如果模型使用大于 1 的 n。
算法
到目前为止,我们选择将每个评论表示为一个非常稀疏的向量(很多零!)中的每个唯一的 n 元语法都有一个槽(减去出现太频繁或不够频繁的 n 元语法)。线性分类器通常在以这种方式表示的数据上比其他算法执行得更好。
支持向量机(SVM)
回想一下,线性分类器往往在非常稀疏的数据集上工作得很好(就像我们拥有的这个)。另一种可以在短训练时间内产生很好结果的算法是具有线性核的支持向量机。
下面是一个 n 元语法范围从 1 到 2 的例子:
关于支持向量机有很多很好的解释,它们比我做得好得多。如果你有兴趣了解更多,这是一个很好的教程:
从例子中学习支持向量机
blog.statsbot.co](https://blog.statsbot.co/support-vector-machines-tutorial-c1618e635e93)
最终模型
这篇文章的目标是给你一个工具箱,当你试图为你的项目找到正确的模型+数据转换时,你可以试着把它们混合在一起。我发现,删除一小组停用词以及从 1 到 3 的 n 元语法范围和线性支持向量分类器给了我最好的结果。
我们突破了 90%大关!
摘要
我们已经讨论了几种可以提高 NLP 模型准确性的文本转换方法。这些技术的哪种组合会产生最好的结果取决于任务、数据表示和您选择的算法。尝试多种不同的组合来看看什么是有效的,这总是一个好主意。
我非常确信,通过这篇文章中概述的东西的不同组合,可以获得更高的准确性。我将把这个留给更有抱负的读者。:)请评论你的结果和方法!
下次
本系列的下一部分将探索构建情感分类器的深度学习方法。
基于词包和词序列的情感分析
对于一般文本,词袋方法在文本分类方面非常有效。对于本文研究的二进制文本分类任务,LSTM 处理单词序列的质量与 SVM 使用 tf-idf 向量的质量相当。但是性能是另一回事…

将文档转换成数字向量的单词包方法忽略了文档中单词的顺序。然后,使用这种向量的分类器将为没有考虑特定单词序列对其含义和隐含目标类别的影响而付出代价。有道理。这是在考虑更新的深度学习方法时的争论,例如可以处理序列的 LSTM(长短期记忆)神经网络。在之前的文章中,我们确实已经表明,当单词序列是决定分类因素的时,使用单词包向量的朴素贝叶斯分类器(具体来说是 tf-idf)在 LSTM 的手中遭受了重创(朴素贝叶斯的 0.23 分/tf-idf 对 LSTM 的 0.91 分)。
但是文本语料库是人工。建造的目的是为了展示 LSTM 等模型中最好的一面,以及忽视上述序列的其他模型中最差的一面。LSTM 方面的这种表现会扩展到现实生活中的文本语料库吗?在现实生活中,单词序列可能不是分类的决定性因素。这就是我们在这里探讨的问题。在这篇文章中,我们从一个简单的二元分类任务开始,在后面的文章中考虑一个多标签分类任务。我们使用具有 tf-idf 向量的支持向量机(SVM)作为单词袋方法的代理,使用 LSTM 作为序列尊重方法的代理。SVM 通过 SciKit 实现,LSTM 通过 Keras 实现。当我们在这里浏览一些代码片段时,可以从 github 下载重现结果的完整代码。
1.将电影评论符号化
来自斯坦福的文本语料库大型电影评论通常用于二元情感分类,即基于评论电影是好还是坏。正面和负面评论被下载到磁盘的不同目录中。下面是“清理”文档并对其进行标记以供分析的代码片段。
- 第 10-11 行。符号化。删除所有标点符号和 NLTK 停用词。确保所有单词/标记都以字母开头。并且只保留长度在 3 到 15 个字符之间的单词。
- 第 15–24 行:遍历每个文件夹中的电影评论文件并标记。
- 第 25 行:记下每个文档中的字数有助于我们稍后为 LSTM 选择合理的序列长度。对 nTokens 的百分位数统计显示,超过 86%的文档中的单词少于 200 个。
Token Summary:
min avg median std 85/86/87/90/95/99 max
3 116 86 88 189/195/203/230/302/457 1388
2.打包行李和序列
LSTM 使用单词序列作为输入,而传统的分类器使用单词包,如 tf-idf 向量。手里有了每个文档作为令牌列表,我们就可以做好准备。
2.1 SVM 的 Tf-Idf 矢量
我们使用 Scikit 的 Tf-Idf 矢量器从令牌构建词汇表和文档矢量。
2.2 的序列
Keras 中的 text 处理器将每个文档转换成一个整数序列/字符串,其中的整数值表示相同处理生成的 {word:index} 字典中的实际单词。我们使用 200 长的序列,因为令牌上的统计数据显示,超过 86%的文档少于 200 个单词。在下面代码的第 8 行中,少于 200 个单词的文档将用嵌入层忽略的索引值 0 进行“post”填充(在第 3 节的嵌入层定义中设置了 mask_zero=True )。
3.模型
LSTM 通过 Keras 实现,而 SVM 通过 SciKit 实现。两者都使用相同的训练/测试分割,因此比较是公平的。整个语料库(即 10,000 个文档)的 20%被留出用于测试,同时对剩余的 40,000 个文档进行训练。
3.1 LSTM
正如在前面的文章中,我们使用最简单的 LSTM 模型,有一个嵌入层,一个 LSTM 层和输出层。

Figure 1. A simple LSTM model for binary classification.
图 1 中的嵌入层将特征的数量从 98089(语料库中唯一单词的数量)减少到 300。LSTM 图层输出一个 150 长的矢量,该矢量将被输入到输出图层进行分类。模型本身的定义非常简单。
- 第 4 行:嵌入层被训练成将 98089 长的 1-hot vetcors 转换成密集的 300 长的向量
- 第 6 行:下降域有助于防止过度拟合
在下面代码的第 6 行中,通过提前停止来完成训练,以防止过度训练。最终输出图层生成一个与标注数量一样长的矢量,该矢量的 argmax 就是预测的分类标注。
3.2 SVM
SVM 的模型要简单得多,因为需要决定的运动部件和参数要少得多。这当然总是一件好事。
4.模拟
混淆矩阵和获得的 F1 分数是我们感兴趣的。有了这两种方法的预测标签,我们就可以使用 SciKit 的 API 来计算它们。
虽然我们以不同的顺序浏览了一些片段,但是运行 lstm 的 lstm_movies.py 和运行 svm 的 svm_movies.py 的完整代码在 github 上。如上一篇文章所述,各种随机种子被初始化以获得可重复性。
4.1 LSTM
运行 LSTM 与:
#!/bin/bash
echo "PYTHONHASHSEED=0 ; pipenv run python ./lstm_movies.py"
PYTHONHASHSEED=0 ; pipenv run python ./lstm_movies.py
由于早期停止,F1 分数在 6 个时期内收敛,产生约 0.87。
Using TensorFlow backend.
Token Summary:min/avg/median/std 85/86/87/88/89/90/95/99/max:
3 116.47778 86.0 88.1847205941687 189.0 195.0 203.0 211.0 220.0 230.0 302.0 457.0 1388
X, labels #classes classes 50000 (50000,) 2 ['neg', 'pos']
Vocab padded_docs 98089 (50000, 200)
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
embedding_1 (Embedding) (None, 200, 300) 29427000
_________________________________________________________________
lstm_1 (LSTM) (None, 150) 270600
_________________________________________________________________
dense_1 (Dense) (None, 2) 302
=================================================================
Total params: 29,697,902
Trainable params: 29,697,902
Non-trainable params: 0
_________________________________________________________________
None
Train on 40000 samples, validate on 10000 samples
Epoch 1/50
- 1197s - loss: 0.3744 - acc: 0.8409 - val_loss: 0.3081 - val_acc: 0.8822
Epoch 2/50
- 1195s - loss: 0.1955 - acc: 0.9254 - val_loss: 0.4053 - val_acc: 0.8337
...
Epoch 6/50
- 1195s - loss: 0.0189 - acc: 0.9938 - val_loss: 0.5673 - val_acc: 0.8707
Epoch 00006: early stopping
[[4506 494]
[ 799 4201]]
precision recall f1-score support
neg 0.8494 0.9012 0.8745 5000
pos 0.8948 0.8402 0.8666 5000
micro avg 0.8707 0.8707 0.8707 10000
macro avg 0.8721 0.8707 0.8706 10000
weighted avg 0.8721 0.8707 0.8706 10000
Time Taken: 7279.333829402924
4.2 SVM
管理 SVM
#!/bin/bash
echo "PYTHONHASHSEED=0 ; pipenv run python ./svm_movies.py"
PYTHONHASHSEED=0 ; pipenv run python ./svm_movies.py
F1 值为 0.90
Token Summary. min/avg/median/std/85/86/87/88/89/90/95/99/max:
3 116.47778 86.0 88.1847205941687 189.0 195.0 203.0 211.0 220.0 230.0 302.0 457.0 1388
X, labels #classes classes 50000 (50000,) 2 ['neg', 'pos']
Vocab sparse-Xencoded 98089 (50000, 98089)
.....*
optimization finished, #iter = 59
Objective value = -6962.923784
nSV = 20647
[LibLinear][[4466 534]
[ 465 4535]]
precision recall f1-score support
neg 0.9057 0.8932 0.8994 5000
pos 0.8947 0.9070 0.9008 5000
micro avg 0.9001 0.9001 0.9001 10000
macro avg 0.9002 0.9001 0.9001 10000
weighted avg 0.9002 0.9001 0.9001 10000
Time Taken: 0.7256226539611816
5.结论
显然,F1 值为 0.90 的 SVM 和 0.87 的 LSTM 在二元分类方面做得非常好。混淆矩阵如预期的那样表现出极好的对角优势。

Figure 2. Both LSTM and SVM have done very well for this binary sentiment classification exercise
虽然他们在质量方面是平等的,但 LSTM 需要更长的时间——2 小时而不是不到一秒。这是一个不容忽视的巨大差异。
我们以此结束这篇文章。在下一篇文章中,我们将回顾多标签分类练习的结果以及外部单词嵌入(如 fasttext)的影响。
…
原载于 2019 年 1 月 28 日xplordat.com。
工会的情绪
使用情感分析和 Python 工具分析总统国情咨文

在《宪法》第二条第三款中,美国总统被指示“向国会提交国情咨文,并建议国会考虑他认为必要和适宜的措施。”
随着特朗普 2019 年国情咨文演讲的新闻,随着时间的推移,看看这些演讲,看看我们是否能注意到自乔治·华盛顿发表第一次演讲以来的任何有趣趋势和变化,可能会很有趣。在本文中,我们将采用 Python 中的数据驱动方法,并利用情绪分析等工具来更好地了解这些演讲随时间的发展。
在一系列的 python 笔记本中,我查看了不同演讲的情感值,执行了主题建模,创建了文字云,最后通过查看每篇演讲的熵值构建了一个“说了多少”的初步衡量标准。
情感分析是自然语言处理中的一种流行工具,它通过理解文本表达的观点来帮助形成对文本的更好理解和分析。因为我们在这里考虑几种不同的方法,所以我们不会深入探讨。首先,我们将考虑用一个数字来表示每个演讲的情绪(“积极”或“消极”),然后看看主题建模是否能帮助我们获得任何进一步的见解。
在开始之前,让我们考虑一下我们可能会从将要分析的地址中得到什么。当华盛顿在 1790 年发表第一份国情咨文时,对他作为第一任总统的审查使他采取了谨慎和顺从的语气,提出了建议,而不是我们在 9/11 后从乔治·W·布什那里看到的行动呼吁。因此,我们可能会认为华盛顿讲话的情绪值相对“中性”(不赋予数值)。
这是足够的猜测。让我们看看我们的结果。我将第一个笔记本产生的情绪值绘制在下面:

你会注意到图表右侧的一个尖峰,像一个疼痛的拇指一样突出——那是吉米·卡特总统发表的 1981 年国情咨文。如果我们忽略这段数据,其余地址的情感值会显得更加密切相关。不管怎样,当我们开始审视对总统个人的情绪变化时,我们会看一看这个特别的演讲。
让我们再往前推一点——因为我们知道每位总统演讲的情绪值,我们可以看看他们从第一次到最后一次国情咨文演讲的情绪变化。也许这将与他们总统任期的成功有一些关联。
运行这些数字,我们有以下观察结果:
- 增幅最大的是卡特,为 106.6 亿美元。这有点令人惊讶,我们将在下面考虑它。
- 排名第二的学校比这落后了不少:麦金利大学,33.0699999999999 美元。虽然他总统任期的开始涉及古巴危机和与西班牙的战争,但麦金利设法实现了与西班牙的和平并获得了一些领土。在他遇刺前,他总统任期的结束是乐观的,因此这一数值似乎与历史事件相符。
- 降幅最大的是杜鲁门,为-35.07 亿。杜鲁门在二战盟军胜利后就职,并在第一任期领导了马歇尔计划和中国不确定性等理论。杜鲁门第二任期内的朝鲜战争对美国来说是一个令人沮丧的僵局,到 1952 年,杜鲁门获得了在任美国总统的最低支持率。同样,数据似乎与历史相符。
- 罗斯福以-31 紧随杜鲁门之后。33300 . 000000000335 我们可能会想起一个矛盾的开端,罗斯福以压倒性优势战胜杜鲁门,但在大萧条期间担任总统,并创下了四届任期的纪录。他有一个多产的开始,带头主要立法和制定新政。有趣的是,1934 年的演讲并没有“向[美国人]展示一幅对世界事务完全乐观的画面。”虽然罗斯福描绘了一幅更有希望和乐观的画面,描绘了第二次世界大战结束的伟大成就和纳粹法西斯在欧洲统治的结束,但他的长篇演讲提到了“相当大的损失”、“敌人的绝望企图”以及相当于德国“分裂宣传”的“邪恶和毫无根据的谣言”。这可能是我们正在考虑的最有趣的结果,因为虽然 1945 年的演讲在许多方面肯定是积极的和充满希望的,但我们的模型可能会采用过多的令人讨厌的和负面的词汇来描述二战期间的敌人。
- 特朗普的负面因素相当高,到他任期的第二年,负面因素的变化为-8.7699999999989。现在画出完整的轨迹并解释一切还为时过早,但关于俄罗斯调查的指控和证据的持续趋势表明,这种负面情绪的趋势可能会持续到 2019 年的国情咨文中,如果真的发生的话。
在滞胀的一年开始他的总统任期时,如果卡特成功地遏制了这种现象,那么他在讲话中表达的情绪将会显著增加,这可能是有道理的。有趣的是,卡特作为总统的最后 15 个月被包括伊朗人质危机和苏联入侵阿富汗在内的危机所破坏,而卡特本人通常被评价为低于平均水平的总统。
同样有趣的是,尽管人们一致认为波尔克总体上是一位成功的总统(尽管经常被忽视),但对他演讲的看法变化大约是 7。与此同时,不难想象,像波尔克这样的总统会在演讲中采用慎重的语气。另一方面,我们的情绪模型可能会从卡特 1981 年的演讲中获得积极和充满希望的陈述,如下面的段落:
“然而,我坚信,由于过去四年在许多国内和国际领域取得的进展,我们的国家比四年前更加强大、更加富有、更加富有同情心和更加自由。我为此感到自豪。我相信国会也应该感到自豪,因为过去四年来所取得的这么多成就都归功于国会的辛勤工作、远见卓识和通力合作。我为国会的努力和成就喝彩。”
语言本身并不反常,但卡特演讲的整体长度,加上明显充满希望和积极的语言,肯定会有助于我们的模型给演讲打分。虽然卡特没有回避提及困难,但他讲话的意图是描绘四年来的进步。很自然,卡特想总结他认为是他的政府的决定性成就,并为未来设定一个积极的基调。这导致了一个比平常更冗长和更有希望的演讲。
在第二个笔记本中,我进行了主题建模,这是一种帮助我们发现出现在文档和文本中的抽象主题的统计建模。我在演讲的子集上使用了两个模型,潜在狄利克雷分配(LDA)和潜在语义索引(LSI),保留了特朗普给出的最后两个地址。
对于那些寻找这两个模型的技术解释的人,你可以在马志威的文章中找到对 LDA 和 LSI 的很好的解释。在这里,我将主要关注他们能告诉我们什么关于我们感兴趣的演讲。LDA 模型为我们提供了以下主题群(LSI 模型的输出可以在笔记本 2 中找到):

Topics produced by the LDA Model
如果我们跳过那些在所有话题中明显无处不在的词(“美国”、“美国”、“国会”、“年份”),那么这些话题能做的就是让我们对演讲中使用的语言类型有所了解。不幸的是,值得注意的是,该模型并没有给我们太多的主题,例如“农业”或“关系”。另一方面,“必须”、“伟大”和“人民”等词可以告诉我们更多关于演讲语气的信息,以及它们在历史上如何被用作更新和呼吁行动,激发美国人对自己国家的强烈自豪感。
在我们探索的下一部分,我们将试图通过单独计算每个地址的熵来构建一个(非常)基本的“总统说了多少”的度量。在信息论中,熵被定义为随机(random)数据源产生信息的平均速率。在这种情况下,熵表达了我们对一个演讲的信息内容的期望,或者它解决了多少不确定性。
就我们的目的而言,我们可以认为熵值是文本的简洁程度。一个演讲的熵值越高,它就越不简洁,因此“说”得越多。我们将把这种想法作为起点,从那里开始。以下是每场演讲的熵图,从华盛顿开始,到特朗普结束:

Entropy of State of the Union Addresses
不要太深入,值得注意的是,平均而言,随着时间的推移,熵值似乎会随着相当数量的下降而增加。然而,这些值下降得足够明显,以至于我们可能会说,从早期到后期,熵显著增加了。高熵演讲的一个特点是更长的长度,但在这个笔记本上的进一步探索表明,这并不能解释全貌。如果你有兴趣看到一些进一步的分析,或许你自己也可以继续下去,请查看题为“熵”的笔记本。
在探索的最后一步,我生成了一些文字云来可视化以下内容:
- 所有 SOTU 地址的汇编
- 比较最早的 10 个地址和最晚的 10 个地址
- 比较一下奥巴马的演讲和特朗普的两个演讲

WordCloud of compilation of SOTU addresses
如果我们考虑所有地址的词云,我们不应该太惊讶最突出的词是最普遍的,广泛适用于美国人民:“美国”,“美国”,“国家”,“新”和“人民”是特色。我们在主题建模中看到的单词,如“must”和“will”也出现了。
与此同时,虽然它们的优先地位较低,但近年来引发大量辩论且有历史根源的话题也出现了——这些话题包括“毒品”、“移民”和“恐怖分子”。
接下来,我们将查看 10 个最老的地址和 10 个最新的地址。

将最老的地址与最新的地址进行比较,会发现一些显著的变化。在最新的演讲词云中,“美国”几乎被完全删除,而“必须”、“伟大”和“国家”的出现比以前多得多。“利益”和“国家”这两个词也消失了,这表明从早期到当代总统的演讲中出现了一些有趣的修辞变化。
如果我们记得总统任期最初几年的不稳定性,那么呼吁采取行动将比当时更成为当今世界的常态是有道理的——此外,当代话语引发了大量利用美国人自豪感的言论,解释了“伟大”一词。“利益”和“国家”等词的消失可能标志着一种转变,即远离建国之父们如此深入思考的关于民族国家功能的更多理论论述。
最后,让我们比较一下我们最近的两位总统:巴拉克·奥巴马和唐纳德·特朗普发表的国情咨文的文字云。

值得注意的是,在奥巴马的词汇云中,“我们”、“制造”、“新”、“帮助”、“工作”和“每一个”出现的次数更多。特朗普的云以“美国”和“国家”为特色,更强调“人民”和“国家”。我们还可以轻松地发现“毒品”这个词,尽管在奥巴马的云中我们不能。虽然一些最重要的词汇是通用的,但两朵云彩之间的差异及其隐含的修辞暗示表明了奥巴马和特朗普的利益和政策的差异。
奥巴马是积极变革的支持者,也是一位经常利用演讲(正如他在竞选中所做的那样)在听众中产生社区感的政治家,这解释了“我们”和“每一个”等词的存在,而他的国内医疗政策和对这一政策的关注可能解释了“帮助”等词的存在。我们在特朗普的云中注意到的词语反映了他演讲中相当大剂量的民族主义。“will”这个词在两朵云彩中占主导地位,暗示着两位总统的承诺。
作为总结,我们分析了自乔治·华盛顿以来历届总统发表的国情咨文,首先使用一种基本的情绪测量方法来观察演讲中的积极和消极情绪,以及每位总统在这些价值观上的转变。然后,我们考虑主题建模,看看我们是否可以对国情咨文中感兴趣的主题进行推断。接下来,我们认为熵是可压缩性的度量,也是语音“内容”的基本度量。最后,我们使用 WordClouds 来研究这些演讲中与所有总统密切相关的话题,以及一些有趣的团体和个人之间的差异。
像 NLP 这样的工具可以帮助我们分析从演讲到书籍的历史文献中的有趣趋势,并开发新的见解。总统的国情咨文很重要,因为它能告诉我们美国历史上的时事状况,并描绘出乐观和悲观的图景。虽然有帮助,但如果我们不花时间自己检查文件,对形势的纯技术分析可能会导致我们得出误导性的结论,就像罗斯福的情况一样。在我们前进的过程中,我们应该继续使用情感分析等工具,因为它们可以提供强大的洞察力,但请记住,在分析文本和演讲时,尤其是在分析文本和演讲时,有必要考虑大量的上下文和修辞因素,这些因素通常无法像人类一样理解。
来源:
[1] G .华盛顿,第一次向国会发表年度演说 (1790)。
[2] J .卡特,国情咨文 (1981)。
[3]f·罗斯福,国情咨文 (1934)。
[4]f·罗斯福,国情咨文 (1945)。
[5] 威廉·麦金利,维基百科。
[6] 哈里·S·杜鲁门,维基百科。
[7] 富兰克林·罗斯福总统任期,维基百科。
[8] E. Ma, 2 潜在的降维和主题建模方法 (2018),走向数据科学。
情感保持词嵌入

单词嵌入是一种使用连续值向量将单词映射到空间中的技术,使得具有相似上下文的单词看起来彼此更接近。通常,这是通过获取大量数据,然后使用 Word2Vec 或 GloVE 或其他算法从中提取单词嵌入来完成的。这些算法有助于捕捉不同单词的语义和句法上下文,但在涉及情感时会受到很大影响。
让我们看一个例子。这里我们来看单词嵌入空间中单词“good”的 10 个最近邻。我们使用在谷歌新闻上训练的 Word2Vec 模型,有 300 个维度。下面是查看前 10 个邻居的代码。
*import* gensim.models.keyedvectors *as* word2vec
wordVectorModel = word2vec.KeyedVectors.load_word2vec_format("GoogleNews-vectors-negative300.bin",
binary=*True*)
neighbors = list(wordVectorModel.similar_by_word("good", topn=10))
print(neighbors)
这为我们提供了以下输出。
[('great', 0.7291510105133057), ('bad', 0.7190051078796387), ('terrific', 0.6889115571975708), ('decent', 0.6837348937988281), ('nice', 0.6836092472076416), ('excellent', 0.644292950630188), ('fantastic', 0.6407778263092041), ('better', 0.6120728254318237), ('solid', 0.5806034803390503), ('lousy', 0.5764201879501343)]
从这个结果可以明显看出,当前的 Word2Vec 模型并没有真正捕获情感信息。第二个最接近“好”的词是
“坏”,它们在情感上是相反的。
解决方案
[1]为这个问题提供了一个非常新颖的解决方案。在这篇博客中,我们将讨论他们的方法,然后展示“好”的最近邻居,看看这种方法是否有效。
这种方法背后的主要思想是更新当前可用的单词嵌入,使得在单词的最近邻居中,情感上相似的单词看起来更接近。这是按照两步程序完成的。
最近邻排序
在这一步骤中,他们使用了一种叫做 E-ANEW 的情感词典,这种词典用于研究英语单词情感规范的扩展版本。它包含大约 13000 个单词,每个单词有 1-9 分,其中 1 代表非常消极,5 代表中性,9 代表非常积极。
首先,他们基于当前可用的单词嵌入选择前 K 个最近邻,并根据它们的余弦相似性对它们进行降序排列。然后根据参考词与该列表中词的情感得分的绝对差值,对该列表进行重新排序。这种重新排序促进情感上相似的单词更接近参考单词。
细化模型
在为情感词典中的所有单词创建排序列表之后,他们更新单词嵌入。本次更新是在牢记以下几点的情况下完成的。
- 更接近情感上相似的邻居
- 远离不同的邻居,和
- 离原始向量不太远。
在更新单词 embeddings 之后,我们得到单词“good”的如下结果。如果您更改训练参数,这些结果可能会有所不同。
[('astonishing', 0.9998810291290283), ('unbelievable', 0.9998570680618286), ('respectable', 0.9998552203178406), ('solid', 0.9998213648796082), ('commendable', 0.9998155236244202), ('decent', 0.9998145699501038), ('excellent', 0.9998089075088501), ('incredible', 0.9997923374176025), ('amazing', 0.9997886419296265), ('exciting', 0.9997859001159668)]
这个方法的代码可以在[2]中找到。值得注意的是,它们的词嵌入也保留了情感,可以在情感分析性能方面有很大帮助。
参考文献
[1]https://www.aclweb.org/anthology/D17-1056
[2]https://github.com/wangjin0818/word_embedding_refine
用深度学习分离音乐曲目
如何使用 Deezer 的 library spleeter 开始创建您的卡拉 ok。

如果你是一名 DJ,一名音乐采样员或类似的人,通常你会花几个小时从原始音乐中创作歌曲,甚至花上几千美元购买软件来帮助你。这个过程中的一个步骤是能够分离人声和音乐,有时是人声、低音、鼓等等。
Deezer 的出色人员刚刚发布了一个包,它将帮助您快速、轻松且免费地做到这一点!!
它是如何工作的

在论文“歌声分离:训练数据研究”中,Deezer 的人员解释了使用深度神经网络分离音轨的过程。这是要点:
在本文中,我们将重点放在深度神经网络来执行分离。我们选择的基准模型是 U-Net[……]U-Net 与卷积自动编码器采用相同的架构,具有额外的跳跃连接,可将编码阶段丢失的详细信息带回解码阶段。它在编码器中有 5 个步长 2D 卷积层,在解码器中有 5 个步长 2D 去卷积层。
所以他们使用一个 U 型网络,看起来像这样:

https://arxiv.org/pdf/1505.04597.pdf
这个架构是由 Ronneberger 和其他人创建的,用于医学领域的图像分类任务,现在我们将它用于音乐和更多。该网络是从完全卷积网络中构思出来的,经过修改和扩展,它可以使用非常少的训练图像进行工作,并产生更精确的分割。
U-Net 架构还依赖于对数据扩充的大量使用,以便更有效地使用可用的带注释的样本。如果你没有那么多信息,这是很棒的。
Deezer 的人做的主要修改是集成立体声处理:
我们使用 3D 张量(通道、时间步长、频率仓)作为网络的输入和输出。
用于训练网络的数据集:MUSBD 和 Bean。MUSDB 是用于源分离的最大和最新的公共数据集,而 Bean 是包含大部分流行/摇滚歌曲的私有数据集,并且包括作为独立录音的声乐和器乐音轨。
然后他们重建了数据集并做了一些数据扩充,具体来说他们做了:
- 频道交换。
- 时间拉伸。
- 音高移位
- 使再混合
- 逆高斯滤波
- 响度标度
以及更多的组合。最后一句话:
预训练模型的性能非常接近已发布的技术水平,并且据作者所知,在即将公开发布的通用 musdb18 基准测试中,这是性能最好的 4 茎分离模型。Spleeter 的速度也非常快,因为它可以使用预训练的 4 茎模型在单个图形处理单元(GPU)上将混合音频文件分成 4 个茎,比实时 1 快 100 倍。
如何使用它

https://deezer.io/releasing-spleeter-deezer-r-d-source-separation-engine-2b88985e797e
为了使用这个包,我创建了一个 MatrixDS repo,这样你就可以试一试了:
[## MatrixDS |数据项目工作台
MatrixDS 是一个构建、共享和管理任何规模的数据项目的地方。
community.platform.matrixds.com](https://community.platform.matrixds.com/community/project/5dc40ffa9550a3c2d251bcb8/files)
要开始使用该库,首先要做的是安装它:
pip install --user spleeter
对我来说,最好使用康达方式:
git clone https://github.com/deezer/spleeter
conda env create -f spleeter/conda/spleeter-cpu.yaml
conda activate spleeter-cpu
您还需要安装 ffmpeg:
sudo apt-get update
sudo apt-get install ffmpeg
如果您正在运行 mac:
brew install ffmpeg
我将用这些歌曲来测试:
免责声明:我不拥有这些歌曲的权利,版权属于官方音乐创作者和唱片公司。
然后对第一首歌这样做:
spleeter separate -i songs/breath.mp3 -o output/
这将下载用于分离人声和音乐的预训练模型。
对于第一首歌,“你的每一次呼吸”,这些是分离人声和音乐的结果:
[## 伴奏. wav
编辑描述
drive.google.com](https://drive.google.com/file/d/1nQX9c_gkr0ulofnbF2aG2GNS0d8fHAm1/view?usp=sharing) [## vocals.wav
编辑描述
drive.google.com](https://drive.google.com/open?id=1tym_4LCTj0Jt6-cQ8Ui7GdyHK3pfk6td)
以下是“懒歌”的搜索结果:
[## 伴奏(1)。声音资源文件
编辑描述
drive.google.com](https://drive.google.com/file/d/1_Wkdnr4dpxndDAXv9e-zzkXnZf_3GRje/view?usp=sharing) [## 人声(1)。声音资源文件
编辑描述
drive.google.com](https://drive.google.com/file/d/1D62XMBDzvsGAdNfI0UuCj_3KTWSttxjZ/view?usp=sharing)
现在让我们用“不要让我失望”这首歌的 4 茎模型来分离人声/低音/鼓/其他:
spleeter separate -i songs/dont.mp3 -o audio_output -p spleeter:4stems
这将下载用于分离所有音乐源的预训练模型。
以下是这首歌的结果:
编辑描述
drive.google.com](https://drive.google.com/drive/folders/1qA3xsUf71qrk23pwdf65roWvGM_yhsm6?usp=sharing)
你会在那里找到足迹。
这个库的强大给我留下了非常深刻的印象,所有这些只需要几秒钟就可以运行,而且输出非常好。我知道可以改进,你也可以!因为代码是开源的,你可以去那里看看哪里可以让声音更清晰。你甚至可以用特定音乐来训练你自己的模型,这样它就能被更好地识别。
所有相关信息都在这里:
Spleeter 是 Deezer 源分离库,具有用 Python 编写的预训练模型,并使用 Tensorflow。
github.com](https://github.com/deezer/spleeter)
还有这里:
Deezer 源分离库,包括预训练模型。- deezer/spleeter
github.com](https://github.com/deezer/spleeter/wiki/2.-Getting-started)
感谢 Deezer 的出色人员发布了这个库。
如果你想更多地了解我并看到其他文章,请点击这里关注我:
Favio Vázquez 的最新推文(@FavioVaz)。数据科学家。物理学家和计算工程师。我有一个…
twitter.com](https://twitter.com/FavioVaz)
用独立分量分析分离混合信号

Image modified from garageband
周围的世界是各种来源信号的动态混合体。就像上图中的颜色相互融合,产生新的色调,我们所感知的一切都是简单成分的融合。大多数时候,我们甚至没有意识到我们周围的世界是独立过程的混乱混合体。只有在不同的刺激,没有很好地混合,竞争我们的注意力的情况下,我们才意识到这种混乱。一个典型的例子是在鸡尾酒会上,一个人在听另一个人的声音,同时过滤掉所有其他客人的声音。根据房间的响度,这可能是一项简单或困难的任务,但不知何故,我们的大脑能够将信号与噪音分开。虽然还不清楚我们的大脑是如何进行这种分离的,但有几种计算技术旨在将信号分成其基本成分。其中一种方法被称为In 独立 C 组件 A 分析( ICA ),在这里我们将仔细看看这个算法是如何工作的,以及如何用 Python 代码写下来。如果你对代码比对解释更感兴趣,你也可以直接查看 Github 上这篇文章的 Jupyter 笔记本。
什么是独立分量分析?
现在让我们继续以鸡尾酒会为例。想象有两个人在说话,你可以听到他们两个,但是其中一个比另一个离你更近。两个声源的声波将混合在一起,作为一个组合信号到达你的耳朵。你的大脑会将两个声音源分开,你会分别听到两个客人的声音,离你近的那个声音更大。现在让我们用一种更抽象和简化的方式来描述它。每个源都是频率恒定的正弦波。这两种来源的混合取决于你的立场。这意味着离你更近的信号源比离你更远的信号源在混合信号中占主导地位。我们可以用向量矩阵符号将它写下来如下:

其中 x 为观察信号, s 为源信号, A 为混合矩阵。换句话说,我们的模型假设信号 x 是通过源信号的线性组合产生的。在 Python 代码中,我们的示例如下所示:
>> import numpy as np>>> # Number of samples
>>> ns = np.linspace(0, 200, 1000)>>> # Sources with (1) sine wave, (2) saw tooth and (3) random noise
>>> S = np.array([np.sin(ns * 1),
signal.sawtooth(ns * 1.9),
np.random.random(len(ns))]).T>>> # Quadratic mixing matrix
>>> A = np.array([[0.5, 1, 0.2],
[1, 0.5, 0.4],
[0.5, 0.8, 1]])>>> # Mixed signal matrix
>>> X = S.dot(A).T
从图 1 的图中可以看出,代码产生一个正弦波信号、一个锯齿信号和一些随机噪声。这三个信号是我们独立的来源。在下图中,我们还可以看到源信号的三种线性组合。此外,我们看到第一混合信号受锯齿分量支配,第二混合信号受正弦波分量影响更大,最后混合信号受噪声分量支配。


Figure 1: Source signals (upper plots) and linear combinations of the source signals (lower plots).
现在,根据我们的模型,我们可以通过将 x 乘以 A 的倒数,再次从混合信号中检索源信号:


这意味着为了找到源信号,我们需要计算 W 。因此,这篇文章剩余部分的任务将是找到 W 并从三个混合信号中检索三个独立的源信号。
ICA 发挥作用的先决条件
现在,在我们继续之前,我们需要多考虑一下源信号需要具备什么样的属性,以便 ICA 能够成功估计出 W 。算法工作的第一个先决条件是混合信号是任意数量的源信号的线性组合。第二个前提条件是源信号独立。那么独立是什么意思呢?如果信号 s1 中的信息没有给出关于信号 s2 的任何信息,则两个信号是独立的。这意味着它们不相关,这意味着它们的协方差是 0。然而,这里必须小心,因为不相关并不自动意味着独立。第三个前提条件是独立分量是非高斯的。这是为什么呢?两个独立的非高斯信号的联合密度分布在正方形上将是均匀的;参见下面图 2 中的左上图。用正交矩阵混合这两个信号将导致两个信号不再独立,而是在平行四边形上均匀分布;参见图 2 中的左下图。这意味着,如果我们处于一个混合信号的最小值或最大值,我们就知道另一个信号的值。因此他们不再独立。对两个高斯信号进行同样的操作将会产生其他结果(参见图 2 中的右图)。源信号的联合分布是完全对称的,混合信号的联合分布也是如此。因此,它不包含任何关于混合矩阵的信息,我们想要计算混合矩阵的逆矩阵。因此,在这种情况下,ICA 算法将会失败。

Figure 2: Gaussian and non-Gaussian sources and their mixtures
因此,总之,为了使 ICA 算法工作,需要满足以下先决条件:我们的源是( 2 )独立的( 3 )非高斯信号的( 1 )线性混合。
所以让我们快速检查一下上面的测试信号是否满足这些前提条件。在下面的左图中,我们看到正弦波信号与锯齿波信号的关系,而每个点的颜色代表噪声成分。如非高斯随机变量所预期的,信号分布在正方形上。同样,混合信号在图 3 的右图中形成平行四边形,这表明混合信号不再是独立的。

Figure 3: Scatter plots of source and mixed signals
预处理步骤
现在,将混合信号直接馈入 ICA 并不是一个好主意。为了获得独立分量的最佳估计,建议对数据进行一些预处理。下面将更详细地解释两种最重要的预处理技术。
定中心
我们在这里讨论的第一个预处理步骤是定心。这是从我们的输入数据中减去平均值的简单方法。结果,居中的混合信号将具有零均值,这意味着我们的源信号 s 也具有零均值。这简化了 ICA 计算,均值可以在以后加回去。Python 中的居中功能如下。
>>> def center(x):
>>> return x - np.mean(x, axis=1, keepdims=True)
白粉
我们需要的第二个预处理步骤是信号 X 的白化。这里的目标是对 X 进行线性变换,从而消除信号之间的潜在相关性,使其方差等于 1。结果,白化信号的协方差矩阵将等于单位矩阵:

其中 I 是单位矩阵。因为我们还需要在白化过程中计算协方差,所以我们将为它编写一个小的 Python 函数。
>>> def covariance(x):
>>> mean = np.mean(x, axis=1, keepdims=True)
>>> n = np.shape(x)[1] - 1
>>> m = x - mean
>>> return (m.dot(m.T))/n
白化步骤的代码如下所示。它基于 X 的协方差矩阵的奇异值分解(SVD)。如果你对这个程序的细节感兴趣,我推荐这篇文章。
>>> def whiten(x):
>>> # Calculate the covariance matrix
>>> coVarM = covariance(X) >>> # Singular value decoposition
>>> U, S, V = np.linalg.svd(coVarM)
>>> # Calculate diagonal matrix of eigenvalues
>>> d = np.diag(1.0 / np.sqrt(S))
>>> # Calculate whitening matrix
>>> whiteM = np.dot(U, np.dot(d, U.T))
>>> # Project onto whitening matrix
>>> Xw = np.dot(whiteM, X)
>>> return Xw, whiteM
FastICA 算法的实现
好了,现在我们已经有了预处理函数,我们终于可以开始实现 ICA 算法了。基于测量独立性的对比度函数,有几种实现 ICA 的方法。在这里,我们将在名为 FastICA 的 ICA 版本中使用 负熵的近似值。
那么它是如何工作的呢?如上所述,ICA 工作的一个先决条件是我们的源信号是非高斯的。关于两个独立的非高斯信号,有趣的是它们的和比任何源信号都更高斯。因此,我们需要优化 W ,使得 Wx 的结果信号尽可能是非高斯的。为了做到这一点,我们需要一种高斯度量。最简单的测量方法是峰度,它是数据的四阶矩,用于测量分布的“尾部”。正态分布的值为 3,像我们在图 2 中使用的均匀分布的峰度为< 3。从下面的代码可以看出,Python 中的实现非常简单,它还计算数据的其他矩。第一个矩是均值,第二个是方差,第三个是偏度,第四个是峰度。这里从四阶矩中减去 3,使得正态分布的峰度为 0。
>>> def kurtosis(x):
>>> n = np.shape(x)[0]
>>> mean = np.sum((x**1)/n) # Calculate the mean
>>> var = np.sum((x-mean)**2)/n # Calculate the variance
>>> skew = np.sum((x-mean)**3)/n # Calculate the skewness
>>> kurt = np.sum((x-mean)**4)/n # Calculate the kurtosis
>>> kurt = kurt/(var**2)-3>>> return kurt, skew, var, mean
然而,对于 ICA 的实现,我们不会使用峰度作为对比函数,但我们可以在以后使用它来检查我们的结果。相反,我们将使用以下对比函数 g(u) 及其一阶导数 g'(u) :

FastICA 算法在定点迭代方案中以如下方式使用上述两个函数:

因此,根据上述内容,我们要做的是随机猜测每个组件的重量。随机权重和混合信号的点积被传递到两个函数 g 和g’。然后我们从 g 中减去g’的结果,并计算平均值。结果是我们新的权重向量。接下来,我们可以将新的权重向量直接除以其范数,并重复上述操作,直到权重不再变化。这没什么不对的。然而,我们在这里面临的问题是,在第二个组件的迭代中,我们可能会识别出与第一次迭代中相同的组件。为了解决这个问题,我们必须从先前识别的权重中去相关新的权重。这是在更新权重和除以它们的范数之间的步骤中发生的事情。在 Python 中,实现如下所示:
>>> def fastIca(signals, alpha = 1, thresh=1e-8, iterations=5000):
>>> m, n = signals.shape>>> # Initialize random weights
>>> W = np.random.rand(m, m)>>> for c in range(m):
>>> w = W[c, :].copy().reshape(m, 1)
>>> w = w/ np.sqrt((w ** 2).sum())>>> i = 0
>>> lim = 100
>>> while ((lim > thresh) & (i < iterations)):>>> # Dot product of weight and signal
>>> ws = np.dot(w.T, signals)>>> # Pass w*s into contrast function g
>>> wg = np.tanh(ws * alpha).T>>> # Pass w*s into g'
>>> wg_ = (1 - np.square(np.tanh(ws))) * alpha>>> # Update weights
wNew = (signals * wg.T).mean(axis=1) -
>>> wg_.mean() * w.squeeze()>>> # Decorrelate weights
>>> wNew = wNew -
np.dot(np.dot(wNew, W[:c].T), W[:c])
>>> wNew = wNew / np.sqrt((wNew ** 2).sum())>>> # Calculate limit condition
>>> lim = np.abs(np.abs((wNew * w).sum()) - 1)
>>> # Update weights
>>> w = wNew
>>> # Update counter
>>> i += 1>>> W[c, :] = w.T
>>> return W
现在我们已经写好了所有的代码,让我们运行整个程序吧!
>>> # Center signals
>>> Xc, meanX = center(X)>>> # Whiten mixed signals
>>> Xw, whiteM = whiten(Xc)>>> # Run the ICA to estimate W
>>> W = fastIca(Xw, alpha=1)>>> #Un-mix signals using W
>>> unMixed = Xw.T.dot(W.T)>>> # Subtract mean from the unmixed signals
>>> unMixed = (unMixed.T - meanX).T
ICA 的结果显示在下面的图 4 中,其中上图表示原始源信号,下图表示 ICA 实现检索到的独立分量。结果看起来非常好。我们把三个来源都拿回来了!


Figure 4: Results of the ICA analysis. Above true sources; below recovered signals.
最后让我们检查最后一件事:信号的峰度。正如我们在图 5 中看到的,我们所有的混合信号的峰度都≤ 1,而所有恢复的独立分量的峰度都为 1.5,这意味着它们的高斯性低于它们的源。这是必须的,因为 ICA 试图最大化非高斯性。它也很好地说明了上面提到的事实,即非高斯信号的混合将比源更高斯。

Figure 5: Kernel Density Estimates of the three mixed and source signals.
总结一下:我们看到了 ICA 是如何工作的,以及如何用 Python 从头开始实现它。当然,有许多 Python 实现可以直接使用。然而,了解该方法的基本原理以知道何时以及如何使用它总是明智的。如果你有兴趣深入研究 ICA 并了解其细节,我向你推荐这篇论文,作者是 Aapo hyvrinen 和 Erkki Oja,2000 年。
否则你可以在这里查看完整的代码,在 Twitter 上关注我或者通过 LinkedIn 联系我。
这个项目的代码可以在 Github 上找到。
快速 ICA 算法的 Python 实现——akcarsten/Independent _ Component _ Analysis
github.com](https://github.com/akcarsten/Independent_Component_Analysis)
九月版:部署机器学习模型

机器学习的应用似乎是无穷无尽的(正如胡安·德·迪奥斯·桑托斯所展示的,构建一个皮卡丘探测应用)。但是,尽管越来越多的数据科学家开始熟悉这项技术,但找到大规模且健壮的成功用例仍然很困难。
这至少在一定程度上是因为,建立一个准确的、无偏见的模型仅仅被认为是成功的一半。其他人需要用!正如 Ian Xiao 在我们 Edition 的另一篇文章中所讨论的,即使你可以将模型发布到世界上,也会有交互、执行和反馈问题等待着破坏你的人工智能抱负……我们本月的选择在这里帮助你选择正确的栈来发布你的模型。
约书亚·弗莱明——编辑助理
部署机器学习模型作为 REST API
由阮 Ngo — 6 分钟读完
作为一名 Python 开发人员和数据科学家,我渴望构建 web 应用程序来展示我的工作。尽管我喜欢设计前端,但同时学习机器学习和应用程序开发变得非常困难。
在 Python 中部署 Keras 深度学习模型作为 Web 应用
通过将 Koehrsen — 7 分钟读取
深度学习、web 应用、Flask、HTML 和 CSS 在一个项目中
部署深度学习模型
艾萨克·戈弗雷德(is AAC Godfried)5 分钟阅读
最近,学术界和行业研究人员在深度学习领域进行了大量令人兴奋和开创性的研究。
使用 TensorFlow 服务和烧瓶部署 Keras 模型
通过 Himanshu Rawlani — 8 分钟阅读
通常需要抽象出机器学习模型的细节,并将其部署或集成到易于使用的 API 端点中。
使用 Tensorflow 对象检测在 Android 上检测皮卡丘
由胡安·德·迪奥斯·桑多斯 — 12 分钟阅读
在 TensorFlow 的众多功能和工具中,有一个名为 TensorFlow 对象检测 API 的组件。
解决在现实世界中部署人工智能系统的最后一英里问题
Ian Xiao — 10 分钟阅读
最后一英里的问题是实现人工智能承诺的价值的最后障碍。
用 Flask 部署 Keras 深度学习模型
通过本·韦伯 — 7 分钟读取
这篇文章演示了如何使用用 Keras 构建的深度学习模型来设置端点以服务于预测。
大规模部署 scikit-learn 模型
由郁风 G — 4 分钟读出
Scikit-learn 非常适合组装一个快速模型来测试数据集。但是,如果您想对传入的实时数据运行它,该怎么办呢?
我们也感谢最近加入我们的所有伟大的新作家,迈克尔·伯乐斯,阿卡什·坦登,伊夫·佩尔斯曼,扬·范·泽格布鲁克,安德鲁·E·布雷顿,维维维安·林登伯格,西蒙·海尔曼·弗拉奇斯,尼古拉·柳比莫夫,塞拉菲姆·巴佐格洛 理查德·廖,贾汉吉尔·马迈多夫,鲍里斯·克尼亚泽夫,肖尔·乔尔,丹尼尔·霍根,亚历克斯·穆尔,安迪·帕特森,SJ·波特等等。 我们邀请你看看他们的简介,看看他们的工作。
用于聚类和分类的序列嵌入

在这里,我们将学习一种方法来获得字符串序列的矢量嵌入。这些嵌入可用于聚类和分类。
序列建模一直是一个挑战。这是因为序列数据固有的非结构化。就像自然语言处理(NLP)中的文本一样,序列是任意的字符串。对于计算机来说,这些字符串没有任何意义。因此,建立数据挖掘模型非常困难。
对于文本,我们已经提出了嵌入,如 word2vec,它将一个单词转换为一个 n 维向量。本质上,把它放在欧几里得空间。在这篇文章中,我们将学习对序列做同样的事情。
在这里,我们将介绍一种为序列创建嵌入的方法,这种方法将序列带入欧几里得空间。通过这些嵌入,我们可以在序列数据集上执行传统的机器学习和深度学习,例如 kmeans、PCA 和多层感知器。我们提供并处理两个数据集——蛋白质序列和博客。
序列数据集在我们身边随处可见。例如,科技行业的点击流、音乐收听历史和博客。在生物信息学中,我们有大型的蛋白质序列数据库。蛋白质序列由 20 种氨基酸的某种组合组成。典型的蛋白质序列如下所示,其中每个字母对应一种氨基酸。

Fig. 1. Example of a protein sequence.
一个蛋白质序列不一定包含所有的 20 个氨基酸,而是它的一个子集。为了清楚起见,我们将定义一些在这篇文章中使用的关键词。
字母表:组成一个序列的离散元素。例如氨基酸。
alphabet-set :将在语料库中构成序列的所有字母表的集合。例如,语料库中的所有蛋白质序列都由一组 20 个氨基酸组成。
序列:离散字母的有序序列。语料库中的序列包含字母表的子集。
序列语料库通常包含数千到数百万个序列。给定我们已标记或未标记的数据,通常需要聚类和分类。然而,由于序列的非结构化——任意长度的任意字符串,这样做并不简单。
为了克服这一点,可以使用序列嵌入。这里我们将使用 SGT 嵌入,将序列中的长期和短期模式嵌入到一个有限维向量中。SGT 嵌入的优点是,我们可以很容易地调整长期/短期模式的数量,而不会增加计算量。
下面的源代码和数据是这里是。在继续之前,我们需要安装sgt包。
$ pip install sgt
使聚集
蛋白质序列聚类
这里使用的数据取自www.uniprot.org。这是一个蛋白质的公共数据库。该数据包含蛋白质序列及其功能。在这一节中,我们将对蛋白质序列进行聚类,在下一节中,我们将使用它们的功能作为构建分类器的标签。
我们首先读取序列数据,并将其转换为一个列表列表。如下所示,每个序列都是一个字母列表。
>>> protein_data = pd.DataFrame.from_csv('../data/protein_classification.csv')
>>> X = protein_data['Sequence']
>>> def split(word):
>>> return [char for char in word]>>> sequences = [split(x) for x in X]
>>> print(sequences[0])
['M', 'E', 'I', 'E', 'K', 'T', 'N', 'R', 'M', 'N', 'A', 'L', 'F', 'E', 'F', 'Y', 'A', 'A', 'L', 'L', 'T', 'D', 'K', 'Q', 'M', 'N', 'Y', 'I', 'E', 'L', 'Y', 'Y', 'A', 'D', 'D', 'Y', 'S', 'L', 'A', 'E', 'I', 'A', 'E', 'E', 'F', 'G', 'V', 'S', 'R', 'Q', 'A', 'V', 'Y', 'D', 'N', 'I', 'K', 'R', 'T', 'E', 'K', 'I', 'L', 'E', 'D', 'Y', 'E', 'M', 'K', 'L', 'H', 'M', 'Y', 'S', 'D', 'Y', 'I', 'V', 'R', 'S', 'Q', 'I', 'F', 'D', 'Q', 'I', 'L', 'E', 'R', 'Y', 'P', 'K', 'D', 'D', 'F', 'L', 'Q', 'E', 'Q', 'I', 'E', 'I', 'L', 'T', 'S', 'I', 'D', 'N', 'R', 'E']
接下来,我们生成序列嵌入。
>>> from sgt import Sgt
>>> sgt = Sgt(kappa = 10, lengthsensitive = False)
>>> embedding = sgt.fit_transform(corpus=sequences)
嵌入是在 400 维空间。我们先对它做 PCA,降维到两个。这也将有助于集群的可视化。
>>> pca = PCA(n_components=2)
>>> pca.fit(embedding)
>>> X = pca.transform(embedding)>>> print(np.sum(pca.explained_variance_ratio_))
0.6019403543806409
前两个电脑解释了大约 60%的差异。我们将把它们分成 3 组。
>>> kmeans = KMeans(n_clusters=3, max_iter =300)
>>> kmeans.fit(df)>>> labels = kmeans.predict(df)
>>> centroids = kmeans.cluster_centers_>>> fig = plt.figure(figsize=(5, 5))
>>> colmap = {1: 'r', 2: 'g', 3: 'b'}
>>> colors = list(map(lambda x: colmap[x+1], labels))
>>> plt.scatter(df['x1'], df['x2'], color=colors, alpha=0.5, edgecolor=colors)

继续构建分类器。
分类
蛋白质序列分类
我们将从在前面使用的相同蛋白质数据集上构建分类器开始。数据集中的蛋白质有两个功能。因此,我们将构建一个二元分类器。
我们将首先将数据中的function [CC]列转换成标签,这些标签可以在keras中内置的 MLP 模型中获取。
>>> y = protein_data['Function [CC]']
>>> encoder = LabelEncoder()
>>> encoder.fit(y)
>>> encoded_y = encoder.transform(y)
在下文中,我们建立了 MLP 分类器,并运行 10 重交叉验证。
>>> kfold = 10
>>> X = pd.DataFrame(embedding)
>>> y = encoded_y>>> random_state = 1>>> test_F1 = np.zeros(kfold)
>>> skf = KFold(n_splits = kfold, shuffle = True, random_state = random_state)
>>> k = 0
>>> epochs = 50
>>> batch_size = 128>>> for train_index, test_index in skf.split(X, y):
>>> X_train, X_test = X.iloc[train_index], X.iloc[test_index]
>>> y_train, y_test = y[train_index], y[test_index]
>>> X_train = X_train.as_matrix(columns = None)
>>> X_test = X_test.as_matrix(columns = None)
>>>
>>> model = Sequential()
>>> model.add(Dense(64, input_shape = (X_train.shape[1],), init = 'uniform'))
>>> model.add(Activation('relu'))
>>> model.add(Dropout(0.5))
>>> model.add(Dense(32, init='uniform'))
>>> model.add(Activation('relu'))
>>> model.add(Dropout(0.5))
>>> model.add(Dense(1, init='uniform'))
>>> model.add(Activation('sigmoid'))
>>> model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
>>>
>>> model.fit(X_train, y_train ,batch_size=batch_size, epochs=epochs, verbose=0)
>>>
>>> y_pred = model.predict_proba(X_test).round().astype(int)
>>> y_train_pred = model.predict_proba(X_train).round().astype(int)>>> test_F1[k] = sklearn.metrics.f1_score(y_test, y_pred)
>>> k+=1
>>> print ('Average test f1-score', np.mean(test_F1))
Average test f1-score 1.0
这个数据对分类器来说太好了。我们还有另一个更具挑战性的数据集。让我们过一遍。
网络日志数据分类
这个数据样本取自这里的。这是一个网络入侵数据,包含审计日志和任何攻击作为正面标签。由于,网络入侵是一个罕见的事件,数据是不平衡的。此外,我们有一个只有 111 条记录的小数据集。这里我们将建立一个序列分类模型来预测网络入侵。
每个序列中包含的数据是一系列的活动,例如,{login, password, …}。输入数据序列中的字母已经被编码成整数。原始序列数据文件出现在这里。
与之前类似,我们将首先为分类器准备数据。
>>> darpa_data = pd.DataFrame.from_csv('../data/darpa_data.csv')
>>> X = darpa_data['seq']
>>> sequences = [x.split('~') for x in X]>>> y = darpa_data['class']
>>> encoder = LabelEncoder()
>>> encoder.fit(y)
>>> y = encoder.transform(y)
在该数据中,序列嵌入应该是长度敏感的。长度在这里很重要,因为模式相似但长度不同的序列可以有不同的标签。考虑两个会话的简单例子:{login, pswd, login, pswd,…}和{login, pswd,…(repeated several times)…, login, pswd}。虽然第一个会话可能是普通用户输错了一次密码,但另一个会话可能是猜测密码的攻击。因此,序列长度和模式一样重要。
>>> sgt_darpa = Sgt(kappa = 5, lengthsensitive = True)
>>> embedding = sgt_darpa.fit_transform(corpus=sequences)
我们在这里发现的embedding很稀少。因此,在训练分类器之前,我们将使用 PCA 进行降维。
>>> from sklearn.decomposition import PCA
>>> pca = PCA(n_components=35)
>>> pca.fit(embedding)
>>> X = pca.transform(embedding)
>>> print(np.sum(pca.explained_variance_ratio_))
0.9862350164327149
所选的前 35 个主成分分析解释了 98%以上的差异。我们现在将继续使用keras构建一个多层感知器。
由于数据量很小,阳性标记点的数量也很少,我们将进行三重验证。
>>> kfold = 3
>>> random_state = 11>>> test_F1 = np.zeros(kfold)
>>> time_k = np.zeros(kfold)
>>> skf = StratifiedKFold(n_splits=kfold, shuffle=True, random_state=random_state)
>>> k = 0
>>> epochs = 300
>>> batch_size = 15>>> class_weight = {0 : 0.12, 1: 0.88,} # The weights can be changed and made inversely proportional to the class size to improve the accuracy.>>> for train_index, test_index in skf.split(X, y):
>>> X_train, X_test = X[train_index], X[test_index]
>>> y_train, y_test = y[train_index], y[test_index]
>>>
>>> model = Sequential()
>>> model.add(Dense(128, input_shape=(X_train.shape[1],), init='uniform'))
>>> model.add(Activation('relu'))
>>> model.add(Dropout(0.5))
>>> model.add(Dense(1, init='uniform'))
>>> model.add(Activation('sigmoid'))
>>> model.summary()
>>> model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
>>>
>>> start_time = time.time()
>>> model.fit(X_train, y_train ,batch_size=batch_size, epochs=epochs, verbose=1, class_weight=class_weight)
>>> end_time = time.time()
>>> time_k[k] = end_time-start_time>>> y_pred = model.predict_proba(X_test).round().astype(int)
>>> y_train_pred = model.predict_proba(X_train).round().astype(int)
>>> test_F1[k] = sklearn.metrics.f1_score(y_test, y_pred)
>>> k += 1
>>> print ('Average Test f1-score', np.mean(test_F1))
Average Test f1-score 0.5236467236467236>>> print ('Average Run time', np.mean(time_k))
Average Run time 9.076935768127441
这是一个很难分类的数据。为了有一个宽松的基准,让我们在相同的数据上建立一个更好的 LSTM 分类器。
>>> X = darpa_data['seq']
>>> encoded_X = np.ndarray(shape=(len(X),), dtype=list)
>>> for i in range(0,len(X)):
>>> encoded_X[i]=X.iloc[i].split("~")
>>> max_seq_length = np.max(darpa_data['seqlen'])
>>> encoded_X = sequence.pad_sequences(encoded_X, maxlen=max_seq_length)>>> kfold = 3
>>> random_state = 11>>> test_F1 = np.zeros(kfold)
>>> time_k = np.zeros(kfold)>>> epochs = 50
>>> batch_size = 15
>>> skf = StratifiedKFold(n_splits=kfold, shuffle=True, random_state=random_state)
>>> k = 0>>> for train_index, test_index in skf.split(encoded_X, y):
>>> X_train, X_test = encoded_X[train_index], encoded_X[test_index]
>>> y_train, y_test = y[train_index], y[test_index]
>>>
>>> embedding_vecor_length = 32
>>> top_words=50
>>> model = Sequential()
>>> model.add(Embedding(top_words, embedding_vecor_length, input_length=max_seq_length))
>>> model.add(LSTM(32))
>>> model.add(Dense(1, init='uniform'))
>>> model.add(Activation('sigmoid'))
>>> model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])>>> start_time = time.time()
>>> model.fit(X_train, y_train, epochs=epochs, batch_size=batch_size, verbose=1)
>>> end_time=time.time()
>>> time_k[k]=end_time-start_time>>> y_pred = model.predict_proba(X_test).round().astype(int)
>>> y_train_pred=model.predict_proba(X_train).round().astype(int)
>>> test_F1[k]=sklearn.metrics.f1_score(y_test, y_pred)
>>> k+=1>>> print ('Average Test f1-score', np.mean(test_F1))
Average Test f1-score 0.0>>> print ('Average Run time', np.mean(time_k))
Average Run time 425.68603706359863
我们发现 LSTM 分类器给出的 F1 值为 0。这可以通过改变模型来改善。然而,我们发现 SGT 嵌入可以在不需要复杂分类器的情况下对少量且不平衡的数据起作用。此外,在运行时,SGT 嵌入式网络明显更快。平均耗时 9.1 秒,而 LSTM 模型耗时 425.6 秒。
结束语
- 我们学习了使用序列嵌入进行序列聚类和分类。
- 这个嵌入是这个论文的一个实现。这篇文章中没有涉及到,但是可以参考这篇文章来看看 SGT 嵌入相对于其他嵌入的准确性比较。
- 由于 SGT 嵌入捕捉长期和短期模式的能力,它比大多数其他序列建模方法工作得更好。
- 建议您使用调谐参数
kappa,查看其对精确度的影响。
学分:
- 萨曼内·易卜拉希米博士,他是 SGT 论文的合著者,对上述代码做出了重大贡献。
- Yassine Khelifi ,为 SGT 编写第一个 Python 版本的数据科学专家。
吴恩达的序列模型——11 个经验教训
我最近在 Coursera 上完成了吴恩达深度学习专业化的第五门也是最后一门课程: 序列模型 。Ng 很好地描述了创建你自己的递归神经网络所涉及的各种建模复杂性。这门课程中我最喜欢的部分是编程练习。特别是,最后的编程练习让您实现一个触发词检测系统。这些系统可以用来预测一个人何时说“Alexa”,或者预测金融触发事件的时间。
在监督学习中,序列模型可以用于解决各种应用,包括金融时间序列预测、语音识别、音乐生成、情感分类、机器翻译和视频活动识别。唯一的约束是输入或输出是一个序列。换句话说,您可以使用序列模型来解决任何类型的监督学习问题,该问题在输入层或输出层包含时间序列。
在这篇文章中,我将讨论我在课程中学到的 11 个关键经验。我还写了一些文章,详细介绍了我从专业领域的前 4 门课程中学到的其他重要经验。对于我的计算机视觉文章,点击这里。其他深度学习课程,点击这里。
第 1 课:为什么不是标准网络?
传统的前馈神经网络不在网络的不同位置共享特征。换句话说,这些模型假设所有的输入(和输出)都是相互独立的。该模型在序列预测中不起作用,因为先前的输入在预测下一个输出中固有地重要。例如,如果您要预测文本流中的下一个单词,您可能希望至少知道目标单词之前的几个单词。

传统的神经网络要求输入和输出序列长度在所有预测中保持不变。正如第 2 课所讨论的,序列模型网络可以直接解决这个问题。
第二课:RNN 建筑有哪些类型?
如引言中所讨论的,序列模型可以处理各种序列预测应用。在本课程中,讲师将讨论各种网络类型,包括一对一、一对多、多对一和多对多网络。

在音乐生成中,输入可以是空集,输出可以是歌曲(一对多)。在高频金融波动性预测中,输入可能是过去 3 分钟的报价和交易流,输出将是波动性预测(多对一)。最有趣的是,多对多架构可以处理输入和输出序列长度不相同的应用,使用上图右下角所示的编码器/解码器设置。在文献中,多对多模型通常被称为序列对序列模型。
第 3 课:语言模型和序列生成是如何工作的?
语言模型通过估计下一个单词出现的概率来做出预测。在你训练了一个语言模型之后,你所估计的条件分布可能被用来对新的序列进行采样。

在家庭作业练习中,你将根据莎士比亚文本训练一个语言模型,并生成新颖的莎士比亚句子。虽然本课程只讨论基于语言的序列生成,但在其他领域还有各种其他应用。例如,在金融领域,您可以使用这种类型的模型来生成样本股票路径。您可以针对单个名称以不同的 3 分钟时间间隔训练网络,然后使用网络生成样本路径。
第四课:用 RNNs 消失渐变
rnn 可能具有以指数速度快速消失的梯度,使得网络难以学习长期相关性。分解渐变问题不大,因为你可以很容易地应用一个简单的渐变裁剪算法。消失的渐变也很难发现,这使得在将系统部署到生产环境中时更加危险。
第五课:捕捉长期依赖
门控递归单元(gru)可用于通过添加两个门(更新和重置门)来解决消失梯度问题,这两个门跟踪与预测最相关的信息。更新门用于确定有多少过去的信息需要传递到下一个时间步。重置门用于确定有多少信息是不相关的,应该被遗忘。LSTM 细胞也可用于解决长期依赖。
第六课:GRU vs LSTM 细胞
LSTM 细胞有一个额外的门,因此更复杂,需要更长的时间来训练。理论上,LSTM 细胞应该能够记住更长的序列,代价是增加训练时间;然而,没有明确的经验证据表明任何一个网络在所有情况下都优于另一个。讲师建议从 gru 开始,因为它们比 LSTM 单元更简单,可扩展性更强。
第 7 课:使用单词嵌入进行迁移学习
单词嵌入可以被认为是给定单词的向量表示。可以使用 Word2Vec、负采样或 Glove 算法来训练它们。单词嵌入模型可以在非常大的文本语料库(比如 100 个单词)上被训练,然后可以在具有较少数量的训练示例(比如 10,000 个单词)的序列预测任务上被使用。例如,情感分类可以使用单词嵌入来大大减少生成准确模型所需的训练示例的数量。在下图中,E 代表单词嵌入矩阵。

第 8 课:设计一个没有不良偏差的算法
预测任务正被用于做出越来越重要的决策。那么,我们如何设计一个不受性别、种族和社会经济偏见影响的算法呢?一些研究人员同意,这个问题在计算机程序中比在人类中更容易解决。使用一种类似于主成分分析的方法,我们可以识别偏差和非偏差子空间。那么对于每一个非定义性的单词,我们可以将这个单词投影为零偏差。最后,我们可以均衡成对的单词来中和剩余的偏差。例如,我们希望祖母和保姆之间以及祖父和保姆之间的距离相等。

第九课:使用搜索算法的机器翻译
给定一个英语句子,搜索算法可以用来生成最可能的法语句子。波束搜索是一种常用于此任务的算法。该算法的贪婪性由波束宽度参数定义。如果你想不那么贪婪,你可以将波束宽度设置为一个更大的正整数。在诊断错误时,可以确定错误是由于波束搜索算法不准确还是您训练的 RNN 模型。
第十课:序列对序列模型中的注意力模型
不严格地说,注意力模型是基于人类的视觉注意力机制。该算法试图根据目前看到的输入序列来学习应该注意什么。注意力模型在诸如神经机器翻译的任务中非常有用。有一个家庭作业,让你自己实现这个模型。
第 11 课:序列对序列模型的语音识别
序列到序列模型允许从业者对语音识别应用采取更简单的端到端方法。从前,语音识别系统是用音素构建的。然而,随着大数据的兴起,我们可以使用端到端的深度学习,并完全去除人工音素和特征工程步骤。
触发词检测系统是本课程中描述的语音识别的最终应用。你会在自己的作业练习中实现这样的算法。我亲自训练了一个网络来开关我的灯。触发检测算法在其他领域也有各种应用,例如金融经济学;也许你可以训练一个算法来检测股票时间序列中的事件/峰值。
结论
总的来说,序列模型的大量应用使本课程非常值得你花时间。家庭作业也让你练习自己实现实际的系统。我上面解释的课程仅仅代表了本课程中材料的一个子集。通过学习这门课程,你将仅仅触及序列模型的表面,但它可能足以启动一个人工智能的机会或职业生涯。我写这篇文章没有得到 deeplearning.ai 的背书。
如果你对序列模型有任何有趣的应用想分享,请在下面的评论中告诉我。我很乐意讨论新项目的潜在合作。
这就是所有人——如果你已经做到了这一步,请在下面评论并在 LinkedIn 上加我。
我的 Github 是这里的。
其他深度学习课程博客
其他有趣的文章
Python 中的序列匹配器
一个人性化的 最长连续&无垃圾 序列比较器
SequenceMatcher 是 python 模块中可用的类,名为“diff lib”。可用于比较成对的输入序列。本文的目的是通过一个示例来解释 SequenceMatcher 算法。由于可用的文档有限,我想通过一步一步的例子来分享这个概念,这可以帮助读者以清晰的方式理解整个过程。
引用原始文件:
基本思想是找到不包含 【垃圾】 元素的最长连续匹配子序列【LCS】。这不会产生最小的编辑序列,但是会产生与人的 【向右看】 匹配的序列。
等一下。上一段描述了这么多专业术语。我们按顺序解码吧。我希望任何阅读这篇文章的人都知道算法 LCS 。基本上,LCS 的目标是:给定两个序列,找出两个序列中最长的子序列的长度。在这里,我们也试图在一对序列之间找到类似于 LCS 的东西,但它们在数学上不是 100%优雅的。换句话说,结果应该让用户更满意,这就是为什么它被称为对人们来说“看起来合适”的匹配。为了避免任何早期的混淆,让我们观察一个我在 堆栈溢出 上找到的例子来理解这两者之间的区别。
输入字符串: my stackoverflow mysteries 和 mystery
对任何人来说,自然的搭配是"myster"如下:
*my stackoverflow* ***myster****ies .................****myster****y..*
事实上,这就是 SequenceMatcher 算法在处理上述字符串时返回的输出。
然而,LCS 将输出"mystery"
***my******st****ackov****er****flow m****y****steries* ***my****.****st****.....****er****......****y****.......*
因为对于人类专家来说,较长的公共子序列可能比较短的公共子序列显得不那么自然。因此,我们认为 SequenceMatcher 试图找出对人类更友好的输出。
继续向前,接下来是 【垃圾】 元素的概念。【垃圾】是你不希望算法匹配的东西:比如普通文本文件中的空行,或者可能是 HTML 文件中的“< P >”行等。
SequenceMatcher 对象初始化为:
init(isjunk=None,a= ' ',b= ' ')
isjunk 参数为您提供了定义您希望算法视为垃圾的任何元素的选项。
>如果是垃圾没有定义:

>如果是垃圾被定义为认为空白是垃圾:

将空白视为垃圾,可以防止“abcd”与第二个序列末尾的“abcd”直接匹配。相反,只有“abcd”可以匹配,并且匹配第二个序列中最左边的“abcd”。
序列匹配器流程图

给定两个输入字符串 a 和 b,
- ratio( ) 返回输入字符串之间的相似性得分(float in [0,1])。它对函数 get_matching_blocks 返回的所有匹配序列的大小求和,并计算比率为:ratio = 2.0 * M/T,其中 M =匹配,T =两个序列中的元素总数**
- get_matching_blocks( ) 返回描述匹配子序列的三元组列表。最后一个三元组是哑元,(len(a),len(b),0)。它通过重复应用 find_longest_match()来工作
- find_longest_match( ) 返回包含 a[aLow:aHigh]和 b[bLow:bHigh]中最长匹配块的三元组

再解释一下
在深入这个例子之前,让我们多了解一下上面两个重要的函数,这肯定会帮助我们完美地理解这些概念。我试图以一种简单的方式只解释重要的部分。关于完整的 python 代码,建议读者访问 官方网站
get_matching_blocks()解释

Code Snippet of function get_matching_blocks( )
基本上,维护一个队列,该队列用包含两个输入字符串的上限和下限的索引的四元组来初始化。直到队列中有一个四元组可用,它被弹出并传递给 find_longest_match( ) 函数,该函数返回描述匹配子序列的三元组。三元组被添加到 matching_blocks 列表中。
每个三元组的形式为(I,j,n),表示 a[i:i+n] == b[j:j+n]
匹配子序列左边和右边的序列片段被进一步添加到队列中。重复该过程,直到队列为空。
然后对 matching_blocks 列表进行排序,并作为输出返回。
find_longest_match()解释

Code Snippet of find_longest_match( )
这将包含字符串的上限和下限索引的三元组作为输入,并返回包含最长匹配块的三元组。
首先,定义一个字典b2j其中对于字符串 b 中的 x,b2j[x]是 x 出现的索引(到 b 中)的列表。
现在在外循环中逐字符扫描第一个字符串,我们使用 b2j 检查该字符在字符串 b 中的出现。如果有匹配,我们更新另一个字典 newj2len,它有助于记录到目前为止匹配的字符串的长度。并且相应地,变量 besti、bestj 和 bestsize 被更新,这考虑了迄今为止获得的最长匹配块数据。
在所有最大匹配块中,该算法返回在 a 中最早开始的块,在所有最大匹配块中,它返回在 b 中最早开始的块。
我想这些理论已经足够了,现在让我们深入一个例子,它将帮助我们详细地理解整个工作。
一个有插图的例子
让我们通过使用一对输入字符串实现算法来描述算法的一步一步的过程。















根据经验,ratio()值超过 0.6 意味着序列非常匹配。这里,通过计算,我们已经获得了 0.8 的相似性得分比率,因此输入的序列对被视为相似的。
为了交叉验证我们的计算结果,让我们用 python 运行 SequenceMatcher。

万岁!!!我们已经达到了预期的产量。
我希望浏览上面的例子能够帮助读者了解序列相似性算法的工作原理。除了相似性,SequenceMatcher 类中还有许多其他功能。建议读者访问 官方代号 。
到那时,干杯!!
参考文献:
https://github . com/python/cpython/blob/master/Lib/difflib . py
无服务器:它能简化数据科学项目吗?

你认为数据科学家在开发和测试模型上花了多少时间?根据谷歌的 Josh Cogan 的这篇文章,只有 10%。他们的大部分时间都花在数据收集、构建基础设施、开发运维以及集成上。当您最终构建一个模型时,将它交付到产品中的过程有多长、多复杂(假设您已经完成了这一步)?当您最终将模型整合到一些有用的商业应用程序中时,您如何再现或解释其结果?你监控它的准确性吗?而持续的应用和模型升级呢?
简化数据科学开发和加快生产时间的一种方法是采用无服务器架构进行数据收集、探索、模型培训和服务。
这篇文章将解释无服务器及其局限性,并提供一个使用无服务器解决数据科学挑战的实际例子。
无服务器概述
“无服务器”一词是几年前由亚马逊创造的,用来描述其λ功能,一种开发者编写一些代码和规范并点击“部署”的服务 Lambda 自动构建容器化的应用程序,将其部署在生产集群上,并提供自动监控、日志记录、扩展和滚动升级。无服务器的其他好处是更低成本的按使用付费模式及其与平台资源和事件的本机集成。
总体而言,无服务器解决了三个主要的数据科学挑战:
1.它减少了开发、部署和监控代码的开销和复杂性。无服务器带来了更快的生产时间,并允许数据科学家和开发人员专注于业务逻辑,而不是工具和基础设施相关的任务。
2.它提供了一个定义明确、版本化的运行时,其中包含了代码、软件包依赖、ML 模型、环境变量、数据源和运行时配置。这可以保证一致和可再现的结果。
3.Serverelss 允许将功能移动、扩展和复制到多个实施点。我们在 Iguazio 看到的一个很好的例子是在云中开发和测试功能,然后在边缘网关或多个区域运行它们,或者只是扩展它们以满足不断增长的流量。
无服务器已经在应用程序和前端开发人员中引起了广泛关注和广泛采用。我们看到它在一些数据科学口袋中被投机性地使用,但它尚未在数据科学家中得到广泛采用。
无服务器可以解决上面概述的许多数据科学挑战,所以我很惊讶“无服务器”的发明者亚马逊没有在亚马逊自己的数据科学平台 SageMaker 中使用 Lambda。你想过为什么吗?
原因很简单!第一波无服务器功能,如 Amazon Lambda,不太适合数据科学,因为:
- 它们是无状态的,数据科学通常是关于大量数据(状态)的。
- 它们速度慢且是单线程的,而数据处理需要并行性。
- 它们限制了函数图像的大小,ML 模型往往很大(大于 Lambda 的最大图像大小限制)。
- 它们不支持 GPU,而 GPU 在 AI/ML 应用中可能相当高效。
- 数据科学工具/框架和无服务器之间没有本机集成。
但是上述所有限制都与云供应商特定的无服务器实现相关,而不是一般的无服务器实现。其他实施确实在满足数据科学需求的同时提供了无服务器优势。
Nuclio,一个针对数据科学优化的无服务器框架
Nuclio 从一开始就被设计为一个开源项目,旨在解决数据科学应用并消除开发和运营的复杂性。Nuclio 提供了极致的性能,支持有状态和数据密集型工作负载,支持并最大限度地提高 GPU 的效率,并且没有任何阻碍它的“包袱”(参见: Nuclio 与 Lambda 比较了解详细信息)。
Nuclio 运行在广泛采用的 Kubernetes 编排层上,并与各种数据科学和分析框架相集成。
Nuclio 使用高性能并发执行引擎(每个进程支持多达 400,000 个事件)。它是目前唯一一个具有 GPU 支持和快速文件访问的无服务器框架,可以加速机器学习代码的性能,并最大限度地利用 CPU 和 GPU(使用资源池模型),以便代码在 Nuclio 上运行更快。
Nuclio 最酷的特性之一是它与 Jupyter 的集成,Jupyter 是最广泛使用的数据科学工作台。这使得开发人员可以自动将数据科学代码和笔记本转变为可扩展的实时功能,这些功能提供数据集成、版本控制、日志记录、监控、安全性和开箱即用的可扩展性,而无需任何额外的编码。
Nuclio 广泛用于各种数据科学任务:
- 数据收集器、ETL、流处理
- 数据准备和分析
- 超参数模型训练
- 实时模型服务
- 特征向量组合(实时数据准备)
以下示例演示了如何从笔记本开发、测试和部署功能。源代码、更详细的文档和各种示例可在 Nuclio-Jupyter git 中找到。
示例:实现自然语言处理功能
完整笔记本见本 Github 链接。
第一步是在 Docker 或 Kubernetes 上部署 Nuclio。参见说明或者直接使用托管版本。
现在,使用以下命令在您的笔记本中安装 python 和 Jupyter SDK:
!pip install nuclio-jupyter
当使用 Nuclio-Jupyter 时,我们在笔记本中编写和测试我们的代码,就像任何其他数据科学代码一样。我们添加了一些“神奇的”命令来描述额外的配置,例如要安装哪些包、代码将如何被触发(http、cron、stream)、环境变量、我们想要捆绑的额外文件(例如 ML 模型、库)、版本控制等。
我们使用 deploy 命令或 API 将笔记本自动转换为代码,然后以容器化的功能进行部署。我们希望从函数中排除的代码单元格标有“# nuclio: ignore”注释。
接下来的部分演示了 Nuclio 库的导入(在一个“被忽略”的单元中),接着是要安装的包的规范、环境变量的设置和构建配置(例如,指示基本 Docker 映像)。

下一部分将实现我们的代码,该代码接受来自触发器的一些文本(例如 HTTP POST 请求),并使用“TextBlob”库来更正、分析情感,并用翻译成另一种语言的文本进行响应(语言由环境变量指定)。

一旦我们实现了我们的代码,我们就使用模拟的上下文和事件对象在笔记本中测试它。请注意,我们的代码使用了日志,这些日志也会在调试时显示。
下一步是将该功能部署到我们的集群上,并服务于真实的用户请求——使用单个“deploy”命令。一旦部署过程完成,它还将为函数提供一个 URL 分配,然后我们可以用它来测试我们的新函数。

该示例演示了如何在熟悉的数据科学环境中创建代码。我们使用一些神奇的装饰来指定我们的生产需求(包依赖关系、环境和配置),并将其部署到生产集群中,而没有考虑底层基础设施。
这是一个简单的例子。更高级的功能示例可以在下面的存储库中找到,包括 Twitter 和股票数据收集和分析、图像识别和预测基础设施监控的实现。
Nuclio 并不局限于 Python 代码。它支持 8 种编码语言,允许我们为工作选择正确和最熟悉的工具。参见这个例子,了解如何通过几个简单的步骤,在不了解 Docker 的情况下,将一个 bash 脚本变成一个交互式的无服务器函数。
摘要
无服务器让我们专注于数据科学逻辑!它使我们的代码生产准备就绪。函数使用结构化日志记录。它们针对安全性进行了强化,并提供自动横向扩展和纵向扩展,以最大限度地提高性能和资源效率。假设功能是容器化的工件,我们可以在一个地方开发和测试它们,然后将它们部署到一个或多个其他位置(例如,在边缘设备或区域集群上)。在另一篇文章中会有更多的介绍。
尝试一下,如果你遇到任何问题,可以去纽克利奥的社区休闲频道。
使用 Dask、Amazon ECS 和 Python 的分布式数据预处理(下)

Source: pixabay.com
使用 Dask 进行 EDA 和超参数优化(HPO)
在本系列的第 1 部分中,我解释了如何在 AWS Fargate 上构建 Dask 调度器和工作器的无服务器集群。上下调整工人数量相当简单。您可以通过运行以下 AWS CLI 命令来实现这一点:
bash~# aws ecs update-service — service Dask-Workers — desired-count 10 — cluster Fargate-Dask-Cluster > /dev/nullbash~# aws ecs update-service — service Dask-Scheduler — desired-count 1 — cluster Fargate-Dask-Cluster > /dev/null
既然我已经扩展了无服务器 Fargate 集群,让我们尝试一些探索性数据分析(EDA)。我使用的是著名的 NY Yellow Taxi 2017 数据集。
首先,让我们将数据帧装入集群内存。为此,只需从 dask 库中导入 dataframe 类,然后使用 read_csv() 函数加载文件:

client . persist()函数的工作原理是将数据异步保存到内存中,然后立即返回。它将任务图提交给集群,并返回一个 Future 对象。然后,我们可以在输出数据帧上运行快速查询。让我们进行一些分析:
1.1 数据框架分析
让我们在数据框架上得到一些描述性的统计数据。代码看起来应该完全一样,只是对于 Dask dataframes,您需要添加一个 compute() 函数,以便在笔记本上立即获得结果。如您所见,在数据帧上进行一些复杂的计算需要大约 7 秒钟,例如:(计算分位数、平均值、计数、最小值、最大值和标准偏差)。

1.2 计算总行程距离,并为每个供应商计算:

完成这条 groupby() 语句大概花了 5.5 秒。如您所见,供应商 2 的行程比供应商 1 多得多,行驶的距离也长得多。
1.3 计算每个特征的缺失值:
数据集中没有缺失值。

1.4 显示特征之间的相关性:
有一些明显的相关性,如票价金额和行程距离、小费金额和票价金额之间的高度正相关。我需要移除高度相关的要素(total_amount)以避免类似于多重共线性问题的问题。

让我们再试一次 1.2 但是这一次,我将把数据保存在内存中,并让它在后台工作,同时我做一些其他的任务。我还将打印出进度条,以查看任务何时完成。

Persist function progress bar
请注意,当使用集群工作线程在后台加载/计算数据时,提示符会立即返回。
当进度条完成后(全部为绿色),可以快速查询数据的输出。请注意,当使用集群工作线程在后台加载/计算数据时,提示符会立即返回。
使用 Dask 进行机器学习:
机器学习中计算量最大的任务之一是超参数优化(HPO)。HPO 是一种用于调整在训练过程中没有学习到的 ML 参数的技术,例如学习速率、优化器、正则化或神经网络中的隐藏层数。它的工作原理是为您要调优的每个超参数探索预定义范围的搜索空间。有许多库和技术可以实现 HPO 过程,但是在本文中,我将重点关注使用网格搜索技术和 Python 中的 Scikit-Learn 库来调优超参数。
HPO 使用网格搜索:
网格搜索技术是一种穷举搜索,通过手动指定学习算法的超参数空间的子集。然后,该算法将遍历每个超参数的组合,旨在(最大化/最小化)客观度量(准确性/损失)。它最终会给出最好的结果,但是优化的超参数越多,优化过程就越复杂。

2-dimensional Grid Search for HPO
HPO 在 Dask 集群上使用网格搜索:
由于 HPO 过程计算量大,我们将在 Dask 集群上运行它,以便利用其规模和弹性。Scikit-learn 使用一个名为 joblib 的非常强大的库来跨多个 CPU 内核并行化进程。
Joblib 也为其他并行系统提供接口,成为执行引擎。我们可以通过使用parallel_backend上下文管理器在集群中运行数千个内核来实现这一点:
首先,我们需要从 sklearn externals 导入 joblib ,然后注册 Dask Distributed 作为 joblib 的并行后端引擎。
from sklearn.externals.joblib import _dask, parallel_backend
from sklearn.utils import register_parallel_backend
from sklearn.externals.joblib import parallel_backendregister_parallel_backend('distributed',_dask.DaskDistributedBackend)
然后,我们需要运行下面一行来开始使用集群作为执行引擎:
with parallel_backend('distributed', scheduler_host='dask-Scheduler.local-dask:8786'):
<Normal sklearn Code>
然后你的 sklearn 代码逻辑保持完全一样,没有变化。
以下是使用 HPO 网格搜索为随机森林分类器查找最佳超参数的完整代码,该分类器将对 MNIST 数据集中的手写数字进行分类:
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.model_selection import GridSearchCV
from sklearn.ensemble import RandomForestClassifierimport numpy as np
from time import timefrom sklearn.externals.joblib import _dask, parallel_backend
from sklearn.utils import register_parallel_backend
register_parallel_backend('distributed', _dask.DaskDistributedBackend)# Loading the Digits dataset
digits = datasets.load_digits()# To apply an classifier on this data, we need to flatten the image, to
# turn the data in a (samples, feature) matrix:
n_samples = len(digits.images)
X = digits.images.reshape((n_samples, -1))
y = digits.target# Split the dataset in two equal parts
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=0)clf = RandomForestClassifier(n_estimators=20)# use a full grid over all parameters
param_grid = {"max_depth": [3,4,5,6, None],
"max_features": [1, 3, 10, None],
"min_samples_split": [2, 3, 10],
"bootstrap": [True, False],
"criterion": ["gini", "entropy"]}# run grid search
grid_search = GridSearchCV(clf, param_grid=param_grid, cv=8, iid=True)start = time()
with parallel_backend('distributed', scheduler_host='dask-Scheduler.local-dask:8786'):
grid_search.fit(X, y)
clf.fit(X, y)print("GridSearchCV took %.2f seconds for %d candidate parameter settings."
% (time() - start, len(grid_search.cv_results_['params'])))results = grid_search.cv_results_
# Return the index of the best validation score
idx = np.flatnonzero(results['rank_test_score'] == 1 )
print("The best score is: " + str(results['mean_test_score'][idx[0]]))
#print the parameters for the best job
print("Parameters: {0}".format(results['params'][idx[0]]))

The output of the above code
在一个 10 节点集群上,找到分类器的最佳超参数组合需要大约 40 秒,而在一台机器上(即使有多个内核/多个 CPU),如果没有并行化特性,将需要许多分钟。
总结:
根据我使用 Dask 的经验,它是一个很好的以分布式方式预处理大型数据集的库。如果你是熊猫和 Numpy 的粉丝,并且很难将你的数据存储到内存中,那么 Dask 绝对是一个不错的选择。对于时间和成本敏感的机器学习任务,如 HPO、数据插补、数据预处理和探索性分析,这绝对是一个很好的解决方案。
无服务器 ML
AWS Lambdas 让您的模型栩栩如生。

©Philip Massie 2019
介绍
日复一日地处理争论不休的数据集,设计有趣的功能,测试和训练大量的模型,会让你感觉有点脱离现实世界的系统。嗯,至少对我来说是这样。最近,我花时间和软件工程师在一起,他们帮助我理解了如何通过利用无服务器架构,以很低的成本或零成本在云中部署 ML 模型。因此,模型或模型集合可以通过简单的 API 调用适应更大的系统。
使用 AWS Lambdas,我们不需要保持昂贵的 EC2 实例全天候运行。兰姆达斯按照要求旋转得非常快。(Google Cloud 也有类似的云功能,但这里我将重点介绍 AWS 产品。)当然,在后台,它们都运行在 EC2 服务器上,但是 Lambdas 是如此抽象,以至于你不需要担心 EC2。有很多很好的理由认为去无服务器,但我不想运行广告。你可以在这里阅读更多内容
Lambda 本质上是一个小型的快速容器,预先配置了许多不同的运行时之一。其中包括:
作为一名数据科学家,这些天我主要从事 Python 的工作,在本教程中,我将带您在 AWS Lambda 中部署一个简单的 ML 模型。这里有一个附带的 github repo 这里包括你需要的所有代码以及模型、数据和训练数据。
型号
这不是在训练一个伟大的模特。作为一个用例,我使用开普敦的历史天气数据来训练一个模型,根据开普敦的历史来预测明天下雨的可能性。你可以在世界上任何一个城市喂它,它会根据开普敦的历史预测那里明天是否会下雨。没关系,这不是重点。
点穴
如果您不熟悉 Lambdas,或者对在 EC2 实例上存放 Docker 容器的成本不抱幻想,希望这将让您了解考虑更大更复杂的模型和代码系统是多么便宜和容易。幸运的话,你的创造力会被激发。
要求
这看起来有点乏味,但是值得一试。大部分你只需要做一次。我将在这里忽略一些东西,比如 AWS 执行角色和密钥管理。此外,本教程将使用 AWS web 控制台和 CLI。如果您不熟悉 CLI,希望这将是一个简单的介绍。
Python 3.6
我们将使用 Python 3.6 Lambda 运行时环境,因为这个运行时环境附带了一个包含 scipy/numpy 的预配置层。因为这些包具有默认情况下 Lambda 环境中不存在的操作系统依赖性,所以这一层对于我们使用任何低级数学函数(如 SKLearn 中的函数)都是至关重要的。我看到 Python 3.7 运行时现在也有了 scipy/numpy 层!
码头工人
为了构建层,我们需要在与 Lambda 的远程环境相匹配的环境中本地安装 Python 包。
解决方案:Docker ( 如何安装)
如果你在使用 Ubuntu,这应该能让你开始:
sudo apt-get remove docker docker-engine docker.io
sudo apt install docker.iosudo
systemctl start dockersudo
systemctl enable docker
Github 回购
本教程有一个 github repo 可用。目录结构很重要,我假设您从项目根目录运行命令。
- project root/
├-layers/
| ├-02_dependencies/
| | └-requirements.txt
| |-03_rain_model/
| | ├-models/
| | | └-rf_rain.pkl
| | └-keys.csv.template
| ├-zips/
| └-model_training/
├-readme.md
└-rain_forecast.py
你需要一个免费账户来查询天气数据。在https://darksky.net/dev报名。注册和登录后,您的主页应该显示您的密钥。将此存储在layers/03_rain_model/keys.csv中。
location IQ
在 https://locationiq.com 的位置 IQ 注册一个免费的地理编码账户。创建一个访问令牌并将其存储在layers/03_rain_model/keys.csv中。
AWS 设置
账号
如果您还没有,请设置您的免费 AWS 帐户。
安装 AWS CLI
AWS CLI 提供对 AWS 的命令行访问。您需要安装它,通常使用 pip,并将其配置为使用您的 AWS 帐户。
TLDR Ubuntu 版本检查其他系统的这些说明和故障排除:
# install
pip3 install awscli --upgrade --user
# check (might need to log in to ubuntu again)
aws --version
配置
我们需要使用控制台来配置 AWS CLI。
登录到 AWS 控制台:
- 右上角,单击您的姓名>我的安全凭证下的下拉菜单
- 选择“访问密钥(访问密钥 ID 和秘密访问密钥)”
- "单击创建新的访问密钥"
- 下载带有您的密钥和秘密密钥的 csv 文件,并保存在安全的地方
回到你的终端(详细说明):
aws configure
> AWS Access Key ID [None]: ABABABABAEXAMPLE
> AWS Secret Access Key [None]:CDCDCDCDCDCDCDCDCDCDCEXAMPLEKEY
> Default region name [None]: us-west-2
> Default output format [None]: json
为项目 创建一个 S3 时段
回到 web 控制台的 S3 部分,为此项目创建一个空存储桶。在所有存储桶中,存储桶名称必须是唯一的,因此请记住这个名称。当然,您也可以使用 CLI 来完成这项工作。
我会把我在美国西俄勒冈的名字叫做severless-ml-tutorial(以匹配我们的配置)。您需要调整下面的代码来匹配这个名称。
我认为这是唯一一个可能会产生成本的地方。S3 存储不是免费的,尽管在这个规模上它是非常便宜的。构建图层后,删除此桶以节省潜在成本。我有几个里面有几兆字节的桶,我还没有发生费用。有点混乱。

还在吗?干得好!好,让我们建立第一个简单的 Lambda。我们稍后将对此进行详述。
创建一个λ
- 在 AWS 控制台中,单击“服务”>“Lambda”。
- 单击“创建函数”创建一个新的 Lambda 函数。
- 称之为
severless-ml-lambda,选择 Python 3.6 运行时。将权限选项设置为默认值。 - 点击底部的“创建函数”。
您的 Lambda 将被创建,其属性屏幕将打开。
添加 API 网关触发器
现在我们将配置一个 http 网关,在这里我们可以与新的 Lambda 进行交互。
- 选择“API 网关”
- API 下拉菜单>“创建新 API”
- 从“选择模板”中选择“Rest API”
- 安全性下拉菜单>打开
- 附加设置—默认
- 点击“添加”
添加一个测试事件
现在我们添加一个示例查询,这样我们就可以测试 Lambda 将如何响应 API 调用。我们将向 Lambda 发送一个城市的名称,因此让我们设置一个测试事件来完成这项工作。
- 在屏幕顶部的“选择测试事件”下拉列表中,选择配置测试事件
- 输入名称,例如“天气测试”
- 输入以下 json,模拟提交“城市”字符串“洛杉矶”的 https 事件。
{
"httpMethod": "GET",
"queryStringParameters": {
"city": "Los Angeles"
}
}
添加一些代码
现在我们终于可以添加一些代码了。这非常简单,它将传入的字符串赋给一个变量,打印出来并返回一个包含该字符串的格式化的 HTML 字符串。
设计师面板
- 突出显示您的 Lambda 名称
向下滚动到功能代码面板
-
将以下代码粘贴到代码块中(我们稍后将需要所有这些库):
-
再次靠近页面顶部,单击 Save,然后单击 Test。
-
幸运的话,你的 Lambda 刚刚执行了你用 WeatherTest json 发送的有效载荷。
-
结果应该显示在页面顶部附近的绿色面板中。
-
您也可以在那里看到 Python 打印函数的结果。

- 在设计器面板中,单击 API 网关块,向下滚动到 API 网关面板 url,然后单击 API 端点旁边的 url。
- 将
?city=Hong Kong添加到 url 的末尾。点击 enter,您应该看到页面内容更新,以反映这个新的 querystring。
现在您已经有了一个简单的 Lambda,可以通过 URL 访问,您可以向它发送一个有效负载,它可以处理并返回有效负载,在这种情况下作为 HTTP 响应。
扩展 Lambda
层
在我们添加更多代码来做更有趣的事情之前,让我们让 Lambda 架构更强大一点。我们可以通过添加层来做到这一点。层只是你包含在 Lambda 容器中的额外资源——有大小限制要处理,所以不要太疯狂。
在这一节之后,我们可以回到代码并扩展 Lambda。如果你有 github repo ,你会看到我有构建嵌套在层目录中的每个层所需的文件。
通常,我们需要整理每一层的内容,将它们上传到 S3,将内容编译成 Lambda 层,然后将这些层附加到我们的 Lambda 上。
01 科学/数字层
这一层由 AWS 预编译,不仅包括 python 库,还包括我们需要使用的系统库。这对于你自己来说是一个非常困难的层次,但是感谢 AWS 为我们做了大量的工作。我们不需要建立这一层。

02 依赖层
这一层将包括默认安装的所有必需的 Python 库。我们将在本地机器上使用一个类似 Lambda 环境的 docker 容器(即 lambci)来整理这一层的内容。在 lambci 容器中,我们可以使用 pip 将库安装到容器外的本地目录中。然后,我们使用这个目录来构建我们的新层。
在终端中,从项目根目录:
docker run --rm -it -v ${ PWD }/layers/02_dependencies:/var/task lambci/lambda:build-python3.6 bash
# From the docker container, install the python packages
pip install -r requirements.txt --target python/lib/python3.6/site-packages/ --no-deps --ignore-installed exit
如果工作正常,你应该会在你的“02_dependencies”图层目录中看到一个“python”目录。
压缩图层内容
我们需要压缩图层内容,为上传到 S3 做准备。
# clear any old versions first
rm layers/zips/02_dependencies.zip
pushd
layers/02_dependencies zip -r ../zips/02_dependencies.zip python popd
上传图层到 S3
使用 AWS CLI 而不是 web 控制台,将您的 zip 目录的内容与 S3 的一个位置同步。保持 web 控制台打开是一个好主意,这样可以看到您正在做的事情是否达到了预期的效果。
aws s3 sync layers/zips s3://severless-ml-tutorial/lambdas/layers --delete
现在我们可以使用上传的 zip 文件来更新或创建一个 Lambda 层,它可以在任何 Python 3.6 Lambda 中使用。记得在 S3Bucket=旁边更改您的存储桶的名称。
aws lambda publish-layer-version --layer-name weather_02_dependencies --description "dependencies layer" --content S3Bucket =severless-ml-tutorial,S3Key=lambdas/layers/02_dependencies.zip --compatible-runtimes python3.6
03 雨水模型层
该层的文件已经存在于layers/03_rain_model/目录中,所以这里不需要 Docker hijinks。你需要把你的 api 密匙放到名为keys.csv.template的文件中,并把它重命名为keys.csv。
不管这个层的名称和主要目的是什么,除了模型对象之外,我还将包括 API 键。注意,这不是管理云中密钥的正确/安全方式。
重要的钥匙不要这样!
压缩图层内容
我们需要压缩图层内容,为上传到 S3 做准备。
# clear any old versions first
rm layers/zips/03_rain_model.zip
pushd layers/03_rain_model/
zip -r ../zips/03_rain_model.zip .
popd
上传图层到 S3
像以前一样,上传图层内容到 S3…
aws s3 sync layers/zips s3://severless-ml-tutorial/lambdas/layers --delete
构建/更新图层 03_rain_model
…并构建图层。请记住在此处更改您的存储桶名称。
aws lambda publish-layer-version --layer-name weather_03_model --description "model layer" --content S3Bucket=severless-ml-tutorial,S3Key=lambdas/layers/03_rain_model.zip --compatible-runtimes python3.6
检查图层是否已成功创建
使用 AWS web 控制台确保图层存在。
S3 部分
- 确认图层 zip 文件存在于 S3。
兰姆达斯区
- 确认已创建新层。
- 服务> Lambda >层>…
- 每次你更新一个现有的层,它会得到一个新的版本。
将新层添加到您的 Lambda 中
我们将把所有需要的层添加到我们的 Lambda 中,然后我们就完成了设置。
在 web 控制台中,打开您的 Lambda
- 服务> Lambda >单击无服务器-ml-lambda
在设计器窗格中,单击“层”。
- 向下滚动到图层窗格,使用“添加图层”按钮,添加以下 3 个图层的最新版本:
- AWSLambda-Python36-SciPy1x 版本 2
Python 3.6 numpy/scipy+OS deps 层) - weather_02_dependencies
我们的 Python 库依赖 weather_02_dependencies - 天气 _ 03 _ 模型
我们的随机森林模型和密钥文件层天气 _ 02 _ 模型
别忘了点击保存!
这就是我们的计划。如果你已经做得很好了!把这个写出来,好像很长。现在剩下的就是构建代码了!
构建代码
下面的每一步都是对上一步代码的扩展。要跟进并查看功能如何增长,请将每个代码块添加到前一个代码块的底部,覆盖前一个代码的返回括号。在每一步更新和保存之后,重新加载 API 端点 URL 并提交一个不同的城市。
或者,在 severless-ml-lambda.py 中找到完整的代码。
添加地理位置
这个模块加载 API 密钥,并使用 LoationIQ 密钥对您提交的城市进行地理编码。这些结果被打印出来并通过 http 返回。
添加天气查询
该代码采用 DarkSky 键和 LocationIQ GPS 坐标,并返回您提交的城市的当前天气情况。
添加模型预测
添加此代码块以加载您在第 3 层中推送的模型,并预测明天是否会下雨。
注意,当你测试这个的时候,你会遇到内存错误。向下滚动到基本设置面板,将 Lambda 的内存增加到 1024 Mb。当你在那里时,把超时时间增加到 10 秒。
向 S3 添加文字
最后一步是将每个查询的结果写到 S3。这只是为了展示与 Python 和 Lambdas 的 AWS 基础设施进行交互是多么简单。
结论
诚然,这是一个相当长的教程。希望它已经阐明了如何通过使用无服务器函数将一个训练好的模型合并到一个更大的代码库中。享受吧。
使用 PySpark 和 GCP 的无服务器推荐系统
我的在线电影推荐系统的幕后以及如何与谷歌云平台交互。

Photo by Noom Peerapong on Unsplash
“网飞如何预测我的品味?”这是我进入数据科学之前想到的问题。让我对这个领域产生了好奇。
最近我觉得是时候回答这个问题了。所以,我决定创建一个向注册用户推荐电影的 web 应用程序。
我的主要目标是让它在线并且完全没有服务器。在这段旅程中,我面临了几个挑战。
首先,我将简要提及这些挑战,然后我们可以深入了解该项目的细节。
挑战:

- 由于我使用了 ALS 矩阵分解协同过滤算法,主要问题是冷启动问题。也就是说,如果没有之前关于用户的数据,算法根本无法向用户推荐产品(这里指的是电影)。因此,为了避免冷启动问题,我应该要求用户在我的应用程序中指定以前看过的电影。

Asking the user to specify 5 movies.
- 由于这是一个有趣的项目,我决定使用谷歌云平台免费层。因此,Apache Spark 集群不允许我使用超过 24 个 vCPUs。我选择了 1 个主节点和 2 个工作节点,主节点有 8 个 vCPUs,内存为 30 GB,工作节点的属性与主节点相同。使用该集群生成建议大约需要 20 分钟。我必须在等待这些推荐的同时向用户提供快速推荐和最佳电影(我将在下面给出更多关于我如何确定最佳电影以及我如何生成快速推荐的信息,请耐心等待)。
- 我选择用 App Engine 的标准环境,比灵活环境便宜。标准环境不允许使用用 Python 以外的编程语言实现的库。所以,当我需要它的功能时,我不能在我的应用程序中使用熊猫。我应该在 python 的标准 CSV 库中复制我的应用程序所需的一些功能。
深入细节。

首先,ALS 矩阵分解是如何工作的?让我们深入了解一下:
自网飞价格挑战以来,这种算法广为人知。这是一种用于稀疏评级数据的最先进的协同过滤算法。简而言之,矩阵分解是将矩阵分解成具有较低维度的矩形因子矩阵的乘积(在这种情况下是用户和电影矩阵的乘积)。在用户矩阵中,列是潜在特征,行是用户。在项目(电影)矩阵中,行代表潜在特征,列是项目(电影)。

Factorization of user-movie sparse matrix. “d” is latent features.
矩阵分解有其假设:
- 每个用户可以用 f 个潜在特征来描述(潜在特征的数量是我们应该确定的该算法的超参数)。例如,第一个潜在特征可能是表示每个用户喜欢恐怖电影多过喜欢惊险电影的比率,或者可能是表示每个用户喜欢浪漫电影多的数字。
- 每部电影都可以用一组与用户矩阵中相似的潜在特征来描述。例如,第一个潜在特征可能是一个数字,表示特定电影的类型与恐怖电影有多接近。
- 我们可以将用户的每个潜在特征乘以电影的相应特征,然后将所有特征加在一起,这应该会给我们一个用户对特定电影的评级的良好近似值(这就是我如何为我的应用程序生成快速推荐)。
我将用户的因子矩阵称为“X”,电影的因子矩阵称为“Y”。对了,我们不知道潜在特征代表什么,也不用知道。—我们只需要计算出潜在特征的值,然后确定每部电影的未知用户评级只是简单的矩阵乘法问题。
为了预测潜在特征的值,我们将使用交替最小二乘法。
最小平方法的基本形式是用某条线来拟合数据,测量所有点到这条线的平方距离的总和,并尝试得到缺失点的最佳拟合。
使用交替最小二乘法,想法是相同的,但是我们在优化 Y 和固定 X 和 v.v .之间交替迭代。使用这种迭代优化过程的主要目标是尝试更接近原始数据的分解表示。
通过这样做,我们首先使用 X 估计 Y (我们优化 Y 的值并保持 X 固定)并使用 Y 估计 X (我们优化 X 的值并保持 Y 固定)。经过足够次数的迭代(这是我们应该确定的模型的超参数),我们的目标是达到一个收敛点,在这里 X 和 Y 矩阵足够接近我们原始数据的因式分解表示,并且这些矩阵的损失函数最小化。
然而,有一个关于数据的小问题。我们的数据很少,这意味着我们没有每部电影的完整用户评级对:)。这就是我们试图预测那些缺失的评分并建立推荐引擎的原因。所以,我们应该惩罚那些没有分级的电影。在我们这样做之后,我们不必处理没有来自用户的评级的电影,并且不围绕推荐中没有评级的电影做出任何假设。因此,为了惩罚未分级电影,我们需要一个权重矩阵,其中未分级电影的权重为 0,分级电影的权重为 1。我将这个权重矩阵称为“W”。
因为我们必须优化 X 和 Y 矩阵。将有两个对应于 X 和 Y. 的成本函数

J(Xu) is a cost function for the user factor matrix and J(Yi) is a cost function for the movie factor matrix.
请注意上述成本函数中的最后一项。这些是正则项,也是这个算法的最后一个超参数。它们在那里是为了避免过度配合。换句话说,他们在优化过程中不让权重有高值。顺便说一下,您应该通过使用交叉验证集(而不是测试集)来调整 lambda(正则项),使算法能够更好地对看不见的数据进行归纳。
X 和 Y 矩阵的优化解决方案如下所示。

Xu is a solution for the X matrix, Yi is a solution for the Y matrix. u is an u-th user in X matrix and i is an i-th movie in Y matrix.
在上述优化方案中, W u 表示用户潜在特征的权重, W i 表示电影潜在特征的权重。 q u 和 q i 将分别等于 r u 和 r i 当 u 个用户已经对 i- 个电影进行了评级时。 r u 和 r i 分别代表第 u 个用户对应的评分值和第 I 部电影对应的评分值。
我们可以用伪代码显示上述算法,如下所示:

我在这个笔记本里用 Numpy 实现了这个算法的一个幼稚版本。
movielens 数据集中用户认为哪些电影最好看?
为了确定这些电影,我使用 Keras 创建了简单的神经网络嵌入,如下面的片段所示。
这段代码主要做的是处理用于嵌入模型的收视率和电影数据帧,创建用户和电影嵌入向量,将它们彼此相乘,并将相应的偏差嵌入向量添加到获得的矩阵中。
通过这样做,我们得到了一个模型,试图通过使用电影和用户嵌入向量来预测每个用户和电影组合的评级。当用 SGD optimizer 训练该模型时,它通过调整嵌入矩阵的相应值来最小化预测和实际评级之间的均方误差。
在几个时代之后,我们将为每部电影训练电影偏见,我们需要它来确定最好的电影。完美!
我们可以得到最好的电影,如下面的片段所示。
在这里,这个片段中的“best_movies”代表了 movielens 数据集中用户认为最好的 48 部电影。我们用最好的电影向用户展示我们的应用程序,以使它不那么无聊。
我们能快速向用户展示一些推荐吗?
的确,我们可以。通过使用 movielens 小数据集。尽管这些推荐可能不如通过使用完整数据集生成的实际推荐准确。
在我项目的 Github 库中有 quick_reco_engine.py 文件。哪个具有与 engine.py 相同的“推荐者”对象
唯一的区别是,quick_reco_engine.py 不将任何东西写入数据库,它从 ALS 模型中提取“产品特征”(电影的潜在因素),并将这些保存到 Google 云存储中。
在 main.py 文件中,我们从 GCS 中获取那些产品特性,并将这些电影的潜在特性与新用户的评分相乘(未评分的电影将 0 作为评分值)。通过这样做,我们得到了用户对电影评价的一个很好的近似值。然后,我们将这些评级按降序排序,并向用户推荐排名前 12 位的高评级电影。检查 main.py 中的 这几行
让我们看看这个在线推荐系统的幕后,以及它是如何与 GCP 互动的。
在 Google Dataproc 中运行的主要推荐引擎是 engine.py
我的应用程序的数据集是 movielens 的完整数据集(显然),其中包括 280,000 名用户对 58,000 部电影的 27,000,000 个评级。
首先,我在 Google CloudSQL 中创建了以下表格。
- 等级
- 电影
- 链接
- 用户
- 推荐
- 快速推荐
然后导入 ratigs.csv 到评分表,movies.csv 到 movies 表,links.csv 到 links 表。
main.py 和 engine.py 将向应用程序写入新注册用户的用户数据、推荐和 QUICK_RECOMMENDATIONS 表。
这个降价文件详细描述了上述推荐系统如何工作,以及它如何与 GCP 交互。markdown 文件中每一个突出显示部分都表示到相应脚本中的一个或多个特定行的永久链接。请查看!
申请在线 这里 。
主要建议如下所示。
注:App 为每个注册用户创建这些推荐,他们可以随时访问推荐的电影。

There are 48 of those movies.
你也可以查一下这个 Jupyter 笔记本 ,我在这里实现了香草 ALS,做了一些 EDA 来回答那些让我无法入睡的问题,直到我回答完这些问题并确定了最佳电影。
这个项目的源代码可以在下面的 Github 库中找到。
[## badalnabizade/movie hunter-推荐-引擎
1 个用户注册 1.1。main.py 为注册用户分配一个新的 id。1.2.py 写用户的 id,名字,邮件和散列…
github.com](https://github.com/badalnabizade/MovieHunter-Recommendation-Engine)
谢谢你的时间。希望你喜欢它!
带 EC2 自动训练的无服务器 TensorFlow 工作流

机器学习培训工作通常是时间和资源密集型的,因此将这一过程纳入实时自动化工作流可能是一项挑战。
在之前的一篇文章中,我展示了一个在 AWS Lambda 上运行无服务器 TensorFlow 训练作业的原型。这种情况的激励用例是当有有限的标记数据,并且新的输入条目需要非常快速地合并到模型中时。
虽然可以在 Lambda 上运行标准的 Python TensorFlow 库,但很可能许多应用程序很快就会遇到部署包大小和/或执行时间的限制,或者需要额外的计算选项。
本文将介绍如何保持数据管理和预测无服务器,但是将培训任务卸载到一个临时 EC2 实例。这种创建实例的模式将建立在为在云中运行经济高效的超参数优化而开发的模式之上。

在 Lambda 中保留预测功能意味着由于加载 TensorFlow,仍然可能存在大小约束。为了减轻这一点,所有的 Lambda 函数都将为 Node.js 编写,这也将允许我们使用 TensorFlow.js 而不是标准的 Python 库。
TensorFlow.js 有浏览器版本和节点版本,后者包含 C++绑定以提高性能。节点版本似乎是显而易见的选择,但是它解压缩到 690MB(!)这使得它立即不适用于 Lambda。考虑到我们不会在 Lambda 函数中进行训练,预测的性能损失是可以接受的,因此我们将使用解压缩到 55MB 的浏览器版本。
对于底层机器学习模型,我们将尝试基于以下输入参数来预测一个人的舒适度:
- 温度(°F)
- 相对湿度(%)
- 衣服隔热(以“克罗”为单位)
- 风速(米/秒)
实际模型将使用使用 TensorFlow 的 Keras API 构建的简单(非优化)神经网络。
对于数据存储,我们将在 DynamoDB 中创建两个表:
data—将为训练保留带标签的输入数据model—存储培训作业的元数据和指标
环境设置
初始化
由于我们的项目将与 Node Lambda 文件和 Python EC2 文件混合在一起,我们将在如下所示的文件夹结构中将它们分开。我们还将利用无服务器框架,它将保留在顶层,而节点和 Python 部分将在各自的文件夹中初始化。
├── LambdaAutoTraining
│ ├── js
│ │ ├── ...
│ ├── py
│ │ ├── ...
首先,安装 Serverless 并使用节点模板初始化一个新项目。应该会出现一个样板处理程序(handler.js)和配置文件(serverless.yml)。
$ npm install -g serverless
$ mkdir -p LambdaAutoTraining/{js,py}
$ cd LambdaAutoTraining
$ serverless create --template aws-nodejs
节点设置
导航到js文件夹,初始化一个新的节点项目,安装 Tensorflow.js(仅限浏览器版本!).
$ cd js
$ npm init
...follow prompts
$ npm install @tensorflow/tfjs
接下来,使用架构图作为指南,创建必要的 JavaScript 文件,这些文件将映射到最终的 Lambda 函数。
$ touch test.js upload.js train.js infer.js s3proxy.js
最后,将handler.js中的样板代码复制到每个文件中,然后删除handler.js。
Python 设置
导航到py文件夹并创建一个新的虚拟环境。为了创建培训方案,我们将使用 Jupyter 笔记本,我们还需要tensorflowjs模块,以便我们可以将保存的模型转换为 TensorFlow.js 可以理解的格式。
$ cd ../py
$ pyenv virtualenv 3.7.3 autotraining
$ pyenv activate autotraining
$ pip install tensorflow tensorflowjs jupyter
$ pip freeze > requirements.txt
在这一部分,我们只需要创建 Jupyter 笔记本文件和 Dockerfile。Python 文件将作为 Docker 构建过程的一部分创建。
$ touch train.ipynb Dockerfile
您的项目结构现在应该如下所示:
├── LambdaAutoTraining
│ ├── serverless.yml
│ ├── js
│ │ ├── test.js
│ │ ├── upload.js
│ │ ├── train.js
│ │ ├── infer.js
│ │ ├── s3proxy.js
│ │ ├── package.json
│ │ ├── package_lock.json
| │ ├── node_modules
| │ │ ├── ...
│ ├── py
│ │ ├── requirements.txt
│ │ ├── train.ipynb
│ │ ├── Dockerfile
无服务器设置
serverless.yml文件是项目的主要配置。首先删除文件中的所有样板文本(如果需要,您可以稍后参考文档中的所有选项),然后开始构建提供者部分。
与大多数 AWS 无服务器示例的一个关键区别是,我们将定义自己的 IAM 角色。通常情况下,role将替换为iamRoleStatements部分,该部分允许无服务器与其自身的整体 IAM 角色合并的定制策略。然而,我们需要将 EC2 作为可信实体,这在iamRoleStatements中是不可用的。稍后将在参考资料一节中进行构建。
环境部分让我们可以访问 Lambda 函数中的部署相关变量。创建 EC2 实例策略需要用到IAM_ROLE,而test.js和infer.js都将使用API_URL来调用我们的 API 网关端点。
接下来,使用图表和创建的文件作为指导来定义每个功能。为了简单起见,每个处理函数名和 API 端点将与文件名相同。
upload、infer和s3proxy将通过 API 网关调用,因此将有http事件。对于s3proxy,我们将使用路径参数来定义所请求的文件,其中key是 S3 桶中的文件夹。
对于train函数,我们将使用 DynamoDB 流触发器,它将包含在参考资料部分。当至少有一个新事件并且满足以下任一限制时,该事件将被触发:
batchSize—创建的最大项目数batchWindow—创建第一个项目后的最长时间
由于train将主要负责启动 EC2 实例,我们还将定义一些额外的特定环境变量。在本例中,我们的 Docker 图像将存储在 AWS Docker 注册表(ECR)中,但是也可以使用其他图像。
AMI_ID—我们将在本例中使用 ami-0f812849f5bc97db5,因为它是为 Docker 预先构建的KEY_NAME—这是 SSH 访问实例所需的 pem 文件的名称;请确保您可以访问私钥!INSTANCE_TYPE—有效值是该图像可用的 EC2 风格SPOT_DURATION—spot 实例被中断前的最短时间(分钟)VALID_HRS—如果没有实现,现货请求将持续的最长时间ECR_ID—应该与您的 AWS 帐户 id 相同ECR_REPO—ECR 储存库和项目的名称
最后,test将仅用于手动触发,因此没有关联事件。
接下来,创建 S3 存储桶和两个 DynamoDB 表(在此阶段提供的吞吐量有限)。请注意,data表还包含用于触发train功能的StreamSpecification。
要创建的最后一个资源是我们的自定义 IAM 角色,它将被所有功能使用,无服务器的文档提供了一个很好的起点模板。为了将角色从 Lambda 转移到 EC2,我们需要两件事情:
- 将
ec2.amazonaws.com添加到AssumeRolePolicyDocument部分 - 在
Policies部分为iam:PassRole添加一个允许动作
对于Policies部分,我们将首先复制默认的无服务器日志策略和 S3 部署桶(通常这些是自动创建的)。接下来,我们将为之前定义的 S3 桶和 DynamoDB 表添加自定义语句。请注意,在创建自定义策略时,不会自动创建 DynamoDB 流策略,因此我们需要显式地定义它。
此外,我们将添加创建 EC2 实例所需的策略:
- EC2 —创建并运行实例。
- CloudWatch —创建、描述和启用警报,以便我们可以在训练完成时自动终止实例。
- ECR —允许提取 Docker 图像(这将仅由 EC2 使用,而不由 Lambda 函数使用)。
- IAM 获取、创建角色并将其添加到实例配置文件中。当从控制台启动 EC2 实例并选择 IAM 角色时,会自动创建这个概要文件,但是我们需要在我们的函数中手动创建。
安全注意事项:在部署到生产环境之前,这些策略应该仅限于所需的资源
因为您已经向每个函数添加了样板代码,所以现在可以部署和测试所有的配置是否正确。
$ serverless deploy --stage dev
...
$ curl -X POST "https://<api_id>.execute-api.<region>.amazonaws.com/dev/upload"Go Serverless v1.0! Your function executed successfully!
现在我们已经准备好构建应用程序了!

Photo by Susan Holt Simpson on Unsplash
λ:upload . js

upload函数将接受一组新标记的数据作为输入,并将其存储在 DynamoDB 表中。该更新将启动一个流触发器来启动train功能。
在upload.js中,首先导入并设置 AWS SDK。由于这个函数是由一个 HTTP 事件触发的,我们将读取body字段,然后构造一个表示各个 DynamoDB 插入项的对象数组。注意,即使字段有不同的类型(例如“N”或“S”分别代表数字和字符串),实际值也需要作为字符串传入。
如果有新的项目要写,我们将构造一个新的对象,然后使用来自 DynamoDB AWS SDK 的batchWriteItem来写新的项目。batchWriteItem比一系列putItem请求更高效,并且也是原子性的。
现在我们已经构建了upload函数,您还可以构建test.js来生成随机数据,以测试工作流并填充数据库。详见 Github 文件。
重新部署到dev阶段并测试端点。此时,开始用数据填充 DynamoDB 是有价值的,这可以通过手动调用test.js函数来完成。
$ severless deploy --stage dev
...
$ curl -X POST "https://<api_id>.execute-api.<region>.amazonaws.com/dev/upload" -d '[{"created": 1570323309012, "temp": 75, "rh": 60, "wind": 0.6, "clo": 1.0, "label": "ok", "score": -1}]'1 records created successfully
尽管train.js函数还没有构建出来,但是一旦达到批处理窗口或批处理大小,您仍然会看到它被调用。
EC2: train.py

上传新数据的功能完成后,我们现在将重点转移到 Python 训练部分。这里转移焦点而不是完成 JavaScript Lambda 函数的动机是,如果 EC2/ECR 集成完成了,验证train函数会容易得多,否则我们将无法验证启动脚本是否工作。
要开始使用 TensorFlow 模型,请打开 Jupyter 笔记本(您的虚拟环境应该已经激活)。
$ cd py
$ jupyter notebook
打开之前创建为空文件的train.ipynb。我们希望将关键字段作为环境参数传递给 Docker 容器,但是为了便于测试,我们将提供这些值。接下来创建代表两个 DynamoDB 表的变量。

对于输入数据,我们将对 DynamoDB 数据表执行一次扫描。如果结果被分页,那么LastEvaluatedKey就会出现,这种情况发生在响应大于 1MB 的时候。
DynamoDB 返回一个 Decimal 数据类型,所以我们将遍历数据集,转换为 floats,并对标签数据进行一次性编码。最后,这个列表被转换成 numpy 数组,以便输入到 TensorFlow 模型中。

为了创建模型,我们将使用 TensorFlow 的 Keras API ,更具体地说,是允许我们构建神经网络层的顺序模型。本文的重点不是超参数优化,因此我们将使用一个非常简单的配置。请务必注意,必须定义输入形状,以便稍后导入 TensorFlow.js。

一旦模型完成,我们将使用来自tfjs模块的转换器直接将其保存为可由 TensorFlow.js 导入的格式。然后,这些文件被上传到 S3 的一个新文件夹中,并以当前纪元为关键字。我们还将维护一个“最新”文件夹,以定义客户应该使用哪个模型进行预测。最后,每个模型拟合的结果将存储在 DynamoDB 的model表中。
由于应该填充了data表,现在您可以在本地运行这个笔记本并验证其功能。

随着模型开发的完成,我们现在将开始准备 Docker 映像,从提供构建映像的指令的 Docker 文件开始。
打开 docker 文件进行编辑,并按如下所示进行更新,目的如下:
- 从标准 Python 3.7 基础映像开始
- 创建新用户
lambdaautotraining - 复制到 Jupyter 笔记本和需求文件中
- 从需求文件安装 Python 库
- 将 Jupyter 笔记本转换为标准 Python 文件,并在图像启动时运行
FROM python:3.7RUN echo $(python3 --version)RUN useradd -ms /bin/bash lambdaautotrainingWORKDIR /home/lambdaautotrainingRUN apt-get update -yCOPY train.ipynb requirements.txt ./RUN pip install -r requirements.txtRUN chown -R lambdaautotraining:lambdaautotraining ./USER lambdaautotrainingRUN jupyter nbconvert --to script train.ipynbCMD ["python3","-u","train.py"]
接下来,在本地构建 Docker 容器,用 ECR URI 标记,登录 ECR,然后推送到存储库。
$ docker build -t lambda-auto-training-dev .
...
$ docker tag \
lambda-auto-training-dev:latest \
<ecr_id>.dkr.ecr.<region>.amazonaws.com/lambda-auto-training/lambda-auto-training-dev:latest$ $(aws ecr get-login --no-include-email --region <region>)
...
$ docker push \
<ecr_id>.dkr.ecr.<region>.amazonaws.com/lambda-auto-training/lambda-auto-training-dev:latest
您可以手动启动 EC2 实例并执行命令来运行这个映像,但是我们将创建触发 Lambda 函数并一起测试它。
Lambda: train.js

train Lambda 函数的主要目的是对新的一批标记数据做出反应,然后启动一个新的 EC2 实例来完全执行训练工作流。与使用回调样式处理程序的upload函数不同,这里我们将使用 async/await 模式。
该函数中定义的第一个变量是初始化脚本,它将被传递给 EC2 实例进行启动。这值得作为一个单独的 shell 脚本进行测试,但是为了简单起见,这里仅以字符串形式显示。该脚本的主要职责如下:
- 下载并安装 AWS CLI
- 登录到 ECR
- 下拉所需的 Docker 图像
- 运行 Docker 映像
注意,run命令有一系列我们通过 replace 语句定义的环境属性。这些将在我们的训练 Python 脚本中使用,以与 DynamoDB 和 S3 进行交互。
最后,该字符串需要按照 EC2 要求进行 base64 编码。
接下来,检索实例配置文件,它定义了 EC2 实例将使用的 IAM 角色。需要阻塞的每个调用都使用带有 await 关键字的 promise 形式。
有了实例概要文件,我们将为 spot 实例定义完整的 EC2 参数集。另一种选择是单独创建一个模板并直接启动它。我们还将在关闭时终止实例,这里的一个额外优化是根据需要停止/启动一个持久性实例。
我们现在准备开始创建 EC2。成功后,我们将创建并启用一个警报,当 CPU 下降到某个阈值以下时,该警报将自动终止实例,我们将该阈值用作训练完成的代理。
我们现在可以用新功能更新开发环境。
$ cd ../js
$ serverless deploy --stage dev
已经验证了train.js的触发工作,我们将使用控制台测试培训工作流程。在 AWS 中,打开 Lambda、DynamoDB、S3 和 EC2 的服务页面,并执行以下操作:
- λ:用空输入触发训练函数
- EC2:验证实例是使用正确的警报创建的
- DynamoDB:验证模型信息是否更新
- S3:确认模型文件已经上传
- EC2:大约 10 分钟后,验证实例是否已终止
Lambda: infer.js

完整的培训工作流完成后,我们现在准备构建预测/推理部分。infer的主要目的是下载模型,加载到 TensorFlow.js 中,然后根据 HTTP 触发器提供给它的一组输入进行预测。该函数期望输入是一个对象数组,其中的键表示所需的模型输入字段。
TensorFlow.js 的浏览器版本使用的fetch在 Node.js 中不是标准的。为了解决这个问题,我们将安装node-fetch,并在全局范围内使用它来代替fetch。
$ npm install node-fetch
接下来,必须下载模型。我们将再次需要解决这样一个事实,即我们使用的是浏览器版本,它不期望访问标准的本地文件系统。我们可以将必要的模块从tfjs-node提取到我们的项目中,但是在这个例子中,我们将利用loadLayersModel中的直接 HTTP 下载选项。
然而,由于我们的 S3 存储桶没有对世界开放,我们需要确定如何允许这种访问。对于对 S3 的 HTTP 访问,使用签名的 url 是一个合理的选择,但是在下载步骤中,TensorFlow 实际上做了两件事:
- 下载 model . JSON——我们可以在这里传入签名的 url
- 使用 url 根目录下载模型拓扑—步骤 1 中签名的 url 将不再有效!
为了解决这个问题,我们将使用一个单独的代理来接收每个请求,并将其重定向到一个适当的签名 url。更新s3proxy以支持此功能,如下所示:
回到infer函数,加载模型后,我们将输入转换为 2D 张量并运行预测。arraySync会将结果转换为标准浮点数,每组输入被转换为跨输出维度的一组预测。通过寻找最大值,这个预测被转换成一个简单的标签映射,然后在一个新的 JSON 对象中返回。
测试整个工作流程
如果您创建了test函数,那么您可以设置一个 cron 作业,在一个定义的时间间隔执行,这将模拟真实的流量。为此,我们需要在我们的serverless.yml配置中添加一个 CloudWatch 事件触发器(默认禁用):
test:
handler: js/test.test
events:
- schedule:
rate: rate(2 minutes)
enabled: false
手动启用触发器可能有点令人困惑,因为 Lambda UI 会显示为“enabled ”,但实际上您需要转到 CloudWatch 来启用底层事件:
问题是在 Lambda UI 中既有
AWS::Events::Rule(可以启用/禁用)也有触发器(可以启用/禁用)。Lambda UI 显示触发器状态,该状态已启用。然而,我们实际上不能用云的形成来触摸它。AWS::Events::Rule设置为 disabled,这是我们用 CloudFormation 设置的。如果触发器或规则被禁用,它将不会触发您的 Lambda。
对于预测方面,我们可以像以前一样手动测试,或者扩展我们的测试功能策略,以包括推理。

准备就绪后,您现在可以部署到“生产”阶段。对于 Docker 映像,我们只需向现有映像添加一个新标签,并将其推入生产存储库。
$ serverless deploy --stage prod
...
$ docker tag \
lambda-auto-training-dev:latest \
<ecr_id>.dkr.ecr.<region>.amazonaws.com/lambda-auto-training/lambda-auto-training-prod:latest
...
$ docker push \
<ecr_id>.dkr.ecr.<region>.amazonaws.com/lambda-auto-training/lambda-auto-training-prod:latest
最后的想法
鉴于这是一个原型,在部署到真实的生产环境之前,应该考虑许多方面:
- 持久 API 端点的域集成(见
serverless-domain-manager插件)。 - HTTP 事件输入应该经过验证并包含错误处理。
- 可以向面向客户端的端点添加预热功能,以限制冷启动时的长调用时间。
- 应该加强 IAM 资源权限。将这个环境封装在 VPC 中是一个不错的选择,它还提供了允许 HTTP 访问 S3 的代理的替代方案。
- DynamoDB 流触发器相对简单,在高容量环境中可能会过于激进。一个更健壮的解决方案可能是将新事件附加到一个文件中,并单独对新事件进行计数,这也可以减少每次训练运行时扫描整个表的工作量。
- 如果 EC2 实例在每次运行后被终止,最终未使用的警报将需要被清除。如果使用停止/启动一个实例的替代模式,警报也可以重复使用。
- 对于生产中的保护,应在培训工作中应用阈值,以便不引入表现不佳的模型进行预测。
感谢阅读!
你可以在 GitHub 上查看所有代码:https://github.com/mikepm35/LambdaAutoTraining
羽毛球比赛中的发球、得分领先和连续得分
羽毛球分为 5 类:男子单打、女子单打、男子双打、女子双打和混合双打。众所周知,羽毛球运动员的战术、战略和强度在这些类别中有很大的不同。一般来说,双打比赛节奏更快,更注重进攻而不是防守,而单打比赛更注重多功能性,更具活力。考虑到游戏风格的差异,本文着眼于 在 的每个类别中,发球、连续得分和得分领先如何不同地影响玩家的心态/表现。这些发现将从统计学以及游戏性的角度进行解释。
总结观察结果,产生了两个问题来引导本文的范围:
- 发球给玩家的优势是多还是少?单打和双打有什么不同?
- 球员的表现会受到连续得分或大比分领先的影响吗?
本文考察的数据是 3 月 6 日至 3 月 11 日在伯明翰举行的 2019 年全英公开赛的结果。许多羽毛球爱好者飞到英国去看顶尖选手,如、史、等男单排名前三的选手。
bwf 网站上的原始数据记录了比赛在单个分数方面的进步(第一分后的分数,第二分,一直到比赛结束)。有了这些信息,我们就能够将每场比赛的分数处理成单点的实例。每个实例将包括以下信息:
- 玩家 _ 领先:玩家在一个点开始时的领先。可以是正的也可以是负的。(负表示分数落后于对手)
- Player_score:玩家在一个点开始时的得分。
- 玩家 _ 发球:如果玩家赢了最后一分,那么玩家发球。
- Point_won:如果当前玩的点是玩家赢的。
- 玩家在一个点开始时所拥有的连续点数。

Diagram 0: A snippet of processed data
这些处理的实例包括我们将检查的关键因素,即玩家领先、玩家连续得分和玩家服务。
描述性数据
为了让领先、连续得分、发球的影响更容易被形象化,我创造了三组情节。

Diagram 1: service descriptive data
第一组图显示了发球和得分结果之间的关系,得分可以是得分,也可以是失分。纵轴是事件发生的频率,横轴是事件发生的分数。绿色/红色线条显示球员发球得分/失分的频率。
在除“ms”之外的所有类别中,发球和失球的发生率似乎比发球和赢球的发生率高(也就是说红线似乎比绿线高)。从发球的描述数据来看,发球似乎有劣势。

Diagram 2: lead descriptive data
第二组图显示了积分领先和积分结果之间的关系。纵轴是事件发生的频率,横轴是事件发生的分数。绿/红线显示了玩家赢得/失去具有不同点数领先的点数的频率。
在类别“ms”、“ws”和“xd”中,有一小部分获胜的频率高于失败。但总的来说,图表的视觉效果似乎无法确定因积分领先而赢得和失去积分之间的任何差异。需要更多的分析才能有进一步的发言权。

Diagram 3: consecutive points descriptive data
第三组图显示了连续点(条纹)和一个点的结果之间的关系。绿/红线显示了玩家赢得/失去具有不同连续点数的点数的频率。
在类别“md”、“wd”、“xd”和“总体”中,连续点数达到 5 似乎会导致玩家输多于赢。从观察连续点的描述性数据来看,拥有连续点似乎有一个弱劣势。
直观地绘制这些因素当然有助于我们发现任何相关性。综上所述,发球似乎是一个劣势,连续得分似乎是一个弱劣势,领先似乎没有提供任何优势/劣势。要做出合理的结论,需要对数据进行进一步的分析。
逻辑回归
Point_won 在处理的数据集中表示为二进制列。1 指点赢,0 指点输。这使得逻辑回归成为一个很好的模型,可以用来可视化发球、领先、连续得分与赢得一分之间的相关性。
不同类别的逻辑回归绘制在同一图上,以便更容易看出差异。

Plot 4: Logistic regressions and coefficients on player serve for multiple categories
根据上面的图表和系数,发球对得分结果的影响在所有游戏类别中都是负面的。从最负到最小负排列系数,我们得到 md,xd,wd,ws,ms 。双打和单打比赛有非常明显的模式和区别。这个服务用最快的反弹伤害了游戏。 md 以快速驱动和强力扣杀闻名,因此在 md 比赛中发球绝对是个坏主意。 xd 排在 wd 之前是因为 xd 里有个男玩家,使得进攻还是很厉害的。ws 和 ms 受到的影响较小,因为每边只有一名球员击球,所以击球组合通常较慢。因此发球不会有太大的负面影响。
为了看看相关性是否真的很重要,我进行了一个排列测试来推断每个类别的 p 值。测试结果如下所示。

由 ms 反映的相关性不显著,而所有其他类别的相关性显著,p 值非常小。尽管男子单打与发球的相关性并不显著,但其余的相关性仍然强化了关于拉力赛速度与发球的相同概念。更新后的回归图如下所示,仅包括显著相关性(移除 ms )。

Plot 5: Significant Logistic regressions on player serve for multiple categories
同样的方法也适用于连续得分对赢得一分机会的影响。

Plot 6: Logistic regressions on consecutive points’ impact on scoring for multiple categories
我们从系数中看到,只有对于 ms ,连续点数与中奖概率之间存在正相关关系。所有其他类别都具有负相关性,按照从最敏感到最不敏感的顺序排列为“md、xd、wd、ws”。从游戏性的角度来解释这一点,在更高的连续点数上更高的失败几率意味着游戏更具竞争力,更少“滑坡”。同样,双打比赛也受到连续得分的负面影响。发生这种情况的原因很可能是由于球员的水平差异和比赛的接近程度之间的事实,在单打比赛中,即使一名球员比另一名球员好一点点,也可能反映出很大的分数差异,但在双打比赛中,即使两个队的水平非常不同,他们仍然可能有一场接近的比赛。因此,在双打比赛中,与单打比赛相比,这场比赛似乎总是“势均力敌”。
再次进行排列测试,结果如下:

有趣的是,排列测试的结果表明,对于 ms 和 ws,相关性是不显著的。只有在双打比赛中才能得出有意义的结论。这验证了单打比赛和双打比赛之间的实际差异,进一步巩固了双打比赛更“激烈”、“快速”和“接近”的结论。

Plot 7: Significant correlations from player consecutive points’ impact on chances of winning
要考虑的最后一个因素是球员的领先优势及其对赢得一分的机会的影响
从描述性数据来看,这并没有产生任何明确的相关性,希望逻辑回归能给我们更多的具体信息。

Plot 8: Logistic regressions and coefficients on player lead for multiple categories
从系数和情节来看,领先有利于赢得除男子双打以外的所有类别的比赛。Md 脱颖而出,可能再次是同样的“竞争”和“密切”的游戏性质。这可以解释为 md 的比赛如此接近,以至于很难取得大的领先优势,因此当领先优势变大时,就更容易输掉比赛。Wd、ms、ws、xd 的顺序是通过点导致租赁最受益。没有非常明确的迹象和见解来解释这一点,让我们在推测可能的解释之前,先看看这些相关性的显著性检验结果。

在对关于玩家领先的逻辑回归进行排列测试后,我们发现除了将所有类别作为一个整体来看,它们都是不重要的,这不会给类别之间的差异增加太多洞察力。这很重要,因为它否定了我们从玩家领先的影响中推断出的潜在关系。这个测试的结论是玩家的领先和获胜的几率没有关系。
结论
本文利用逻辑回归和线性回归来评估发球、球员领先和球员连续得分的影响,总体上的重要发现是发球降低了赢得一分的机会,连续得分可能是风向转变的迹象。积分领先和赢得积分之间没有显著的相关性。
本文仅绘制了 300 多场羽毛球比赛的非常小的数据集,包含更多比赛统计数据的更大的数据集可以帮助巩固当前的相关性,并进一步探索积分领先的潜在影响。
感谢您的阅读!
如果你有任何意见或者你只是喜欢羽毛球,请联系我们!
用烧瓶服务先知模型——预测未来
演示如何使用 Flask 在 Web 上提供 Prophet 模型 API 的解决方案。prophet——由脸书开发的用于预测时间序列数据的开源 Python 库。

Source: Pixabay
几乎对任何企业来说,准确的预测和未来的预测都是至关重要的。这是显而易见的事情,不需要解释。有一个时间序列数据的概念,这个数据是按日期排序的,通常每个日期都被赋予一个或多个特定于该日期的值。机器学习驱动的模型可以基于时间序列数据生成预测。这种预测可能是商业决策的重要信息来源。
我在研究时间序列预测的 LSTM 模型。LSTM 神经网络擅长预测序列数据,时间序列也是序列数据,这就是为什么 LSTM 网络被用来预测时间序列的原因。LSTM 的实施很复杂,很难为 LSTM 的培训准备输入数据。可能所有 LSTM 的例子都是基于当前数据集的数据进行预测,而不是真实的未来数据-这使得在实践中很难应用 LSTM。
当我在 GitHub 上找到并评估了 Prophet Python 库用于未来预测时,我对它的简单性和实用性印象深刻。prophet——由脸书开发的开源预测库。
我将与 Prophet forecast 实现共享一个示例 Python 笔记本,并展示如何通过 Flask API 保存/加载 Prophet 模型并提供服务。
源代码和数据可在我的 GitHub repo 中获得。
首先,您需要导入 Prophet 库:
import pandas as pd
import matplotlib.pyplot as plt
from fbprophet import Prophet
%matplotlib inline
如果您的环境中没有安装 Prophet,您需要安装它。我把它安装在 Docker 容器中运行的 Ubuntu 中。用 pip 安装没有效果。但是我可以用康达安装:
conda install gcc
conda install -c conda-forge fbprophet
通常下一步是加载数据。对于这个例子,我使用铁/钢价格数据集(从这里下载)。数据被加载到带有熊猫库的框架中。熊猫数据框架允许操纵和争论数据。我们正在删除未使用的列,设置索引并重新排序时间序列数据:
df = pd.read_csv('Dow Jones Iron & Steel Historical Data.csv')
df = df[['Date', 'Price']].dropna()df['Date'] = pd.to_datetime(df['Date'])
df = df.set_index('Date')daily_df = df.resample('D').mean()
d_df = daily_df.reset_index().dropna()
Prophet 操作 ds/y 列,我们应该重命名数据框中的列:
d_df.columns = ['ds', 'y']fig = plt.figure(facecolor='w', figsize=(20, 6))
plt.plot(d_df.ds, d_df.y)
我们将用于 Prophet 模型培训的钢铁价格数据:

Iron/steel price data
关键部分来了——先知模型训练:
m = Prophet()
m.fit(d_df)future = m.make_future_dataframe(periods=90)
forecast = m.predict(future)
forecast[['ds', 'yhat', 'yhat_lower', 'yhat_upper']].tail()
通过调用 fit 函数并传递 Pandas 数据帧来执行模型训练。未来预测由 predict 函数执行,并传递描述未来多少天要预测的参数(上例中为 90 天)。Prophet 返回一个包含各种参数的数据框来描述预测。其中最重要的是:
- ds —预测日期
- yhat —给定日期的预测值
- yhat _ lower 给定日期的预测下限
- yhat _ uppet 给定日期的预测上限
调用 Prophet 模型的 plot 函数,显示模型是如何根据训练数据(黑点—训练数据,蓝线—预测值,浅蓝色区域—预测边界)进行训练的:

Prophet model
此图表显示未来 90 天的模型预测。但是很难看出来,图表显示了所有的数据。我们可以放大数据,并使用从训练数据中分离预测的垂直线来绘制图表:
from datetime import datetime, timedeltafig1 = m.plot(forecast)#datenow = datetime.now()
datenow = datetime(2019, 7, 2)
dateend = datenow + timedelta(days=90)
datestart = dateend - timedelta(days=450)plt.xlim([datestart, dateend])
plt.title("Iron/steel forecast", fontsize=20)
plt.xlabel("Day", fontsize=20)
plt.ylabel("Iron/steel price", fontsize=20)
plt.axvline(datenow, color="k", linestyle=":")
plt.show()
这有助于更清楚地看到 90 天的预测。我们可以看到价格预测的下降趋势:

Forecast trend
打印预测值有一种简单的方法,即从预测数据框中访问最近 90 天的预测值:
forecast[['ds', 'yhat', 'yhat_lower', 'yhat_upper']][-90:]

First 6 entries from 90 days forecast
为了评估模型,显示学习到的趋势是有用的:
fig2 = m.plot_components(forecast)

Trends
Prophet 了解到从三月到十月价格通常会下降。
当预测一定天数时,您应该检查预期误差是多少。这被称为模型交叉验证:
from fbprophet.diagnostics import cross_validation, performance_metrics
df_cv = cross_validation(m, horizon='90 days')
df_p = performance_metrics(df_cv)
df_p.head(5)
结果:

Cross-validation results
我们可以绘制这些指标来直观地观察模型的表现。百分比误差(MAPE)更容易理解,我们可以这样绘制:
from fbprophet.plot import plot_cross_validation_metric
fig3 = plot_cross_validation_metric(df_cv, metric='mape')
它表明 10 天的预测导致大约 10%的误差,然后误差增长到大约 18%:

Forecast percentage error
当新数据可用时,应该重新训练该模型。如果数据没有改变,就没有必要重新训练模型。当用户想要调用预测功能时,保存模型并再次使用。将 pickle 功能用于:
import pickle
with open('forecast_model.pckl', 'wb') as fout:
pickle.dump(m, fout)with open('forecast_model.pckl', 'rb') as fin:
m2 = pickle.load(fin)
这将把模型保存到磁盘上的一个物理文件中。在本例中,我展示了如何保存和加载模型,为您提供方便。在实际实施中,load 函数将在不同的位置调用(在负责处理用户预测请求的函数中)。
Flask 完美地通过 REST API 将 Prophet 模型暴露给外部世界。导入烧瓶库:
from flask import Flask, jsonify, request
from flask_cors import CORS, cross_origin
在带有 Flask 注释的函数内调用预测:
app = Flask(__name__)
CORS(app)[@app](http://twitter.com/app).route("/katana-ml/api/v1.0/forecast/ironsteel", methods=['POST'])
def predict():
horizon = int(request.json['horizon'])
future2 = m2.make_future_dataframe(periods=horizon)
forecast2 = m2.predict(future2)
data = forecast2[['ds', 'yhat', 'yhat_lower', 'yhat_upper']][-horizon:]
ret = data.to_json(orient='records', date_format='iso')
return ret# running REST interface, port=3000 for direct test
if __name__ == "__main__":
app.run(debug=False, host='0.0.0.0', port=3000)
通过 Postman 调用 REST API 的例子。预测范围参数被传递给 API,我们得到带有预测数据的 JSON 响应:

Prophet forecast REST API response example
源代码和示例数据可以在我的 GitHub repo 中获得。
在 Kubernetes 上提供 Vowpal Wabbit 模型

Vowpal Wabbit Logo
在这篇文章中,我将展示一个构建 Kubernetes 服务的例子,将使用没有“本地”服务 API 的库训练的机器学习模型投入生产。我选择了 Vowpal Wabbit,因为虽然它不像其他机器学习框架那样受欢迎,但它通常会提供非常好的开箱即用的结果,并且在数据准备、优化器选择和超参数调整方面需要最少的努力。我个人认为,学习 vow pal Wabbit T1(VW)是一个(初级)数据科学家的良好起点,因为初学者可以轻松、快速、迭代地在相当大的数据集上构建原型。另一方面,从机器学习工程师的角度来看,服务于用 VW 训练的模型是一项有趣的任务,因为该库提供的接口很少,并且没有官方的服务 API。
在这篇文章中,我将带你了解这项服务的发展。有几个 GitHub gists,你可以在这里找到完整的库。
推出 VowpalWabbit
VW 是微软研究院赞助的快速核外机器学习系统。第一个公开版本出现在 2007 年,多年来已经增加了许多功能和改进。大众的设计是学术研究和软件开发之间良性循环的一个例子,论文中出现的算法被纳入库中,框架的速度和性能可以催生进一步的学术研究。
为了更具体地说明 VW 如何工作,让我们考虑一个预测任务,其中我们想要建立一个线性模型 y = X𝛃,其中 y 是要预测的变量, X 是特征向量,𝛃是模型参数的向量。
第一个关键想法是大众如何使用所谓的散列技巧来表示特征。为了简单起见,首先考虑分类特征 𝓕 的情况,其可以取许多可能的值,例如在 (LAX,AMS) 中的对(出发机场,到达机场)。大众不需要用户预先指定所有可能的级别,或者让它知道𝓕 可以假设的不同值的数量;用户需要做的是指定特征的总最大数量𝓓,即𝛃.的行数在幕后,大众将使用一个散列函数将所有特征存储到𝓓值中。具体来说,在 𝓕 = (LAX,AMS) 的情况下,VW 会将字符串“ 𝓕 = (LHR,AMS)”哈希成一个介于 0 和𝓓 -1 之间的整数 i ,并设置𝛃( i ) = 1 ( 一热编码)。在数字特征的情况下,比如说 𝓕 = 2.5,整数 i 将通过散列字符串“ 𝓕 ”来计算,然后大众将设置𝛃( i ) = 2.5。
第二个关键思想是大众使用在线梯度下降(一种在线学习的形式)来优化模型权重𝛃;具体来说,不需要将数据保存在存储器中,可以一次处理一个例子;这使得 VW 能够在非常大的数据集上很好地扩展,因为内存需求现在基本上被绑定到为𝛃.分配内存例如,您可以通过在网络上传输数据,使用 Hadoop 集群中存储的万亿字节数据在您的笔记本电脑上训练一个大众模型。此外,小心实施梯度下降以处理不同重要性的特征,并降低算法对学习率(学习率衰减)精确设置的敏感性。
在进入下一步之前,让我们先看一下大众要求的数据格式。我首先以我们将用于服务的 JSON 格式呈现它:
Training data: JSON format
这里我们处理一个分类任务,因此目标可以是 0 或1;标签为示例的可选唯一标识;特性被组织在名称空间中,其中每个名称空间都有自己的哈希函数,减少了哈希冲突的可能性;例如,在名称空间 a 中,我们发现一个分类特征 x1 取值 a 和一个数值特征 x2 取值 0.814 。在 VW 中接收这个示例的方法是将所有内容放在同一个字符串中,使用“|”字符分隔名称空间:
Training data: VW native format
注意分类特征如何使用“=”字符,数字特征如何使用“:字符;进一步的细节可以在这里找到。
分解问题和设计考虑
让我们首先集思广益,为大众服务。我很快想到了两个解决方案:
- 哈希反转 : VW 提供了以人类可读格式存储模型权重𝛃的可能性;通过额外的工作,VW 还可以对哈希进行反转,从而生成训练示例中出现的原始特征的𝛃索引,例如上面 gists 中的 x2 、 x1 、…。通过这种方式,人们可以获得模型权重的文本文件,并使用任何可以对线性模型进行评分的函数(例如,参见此处采用的为大众模型构建 TensorFlow 包装器的方法)。
- Java 原生接口 ( JNI ): 这个项目旨在提供一个 API 从 Java 调用 VW;因此,这可以用来将 VW 集成到任何基于 Java 的服务库中。
稍加思考,我们就可以很容易地发现这两种方法都有重大缺陷,并且本质上都不令人满意:
- 哈希反转是一种开销很大的操作;它需要进一步遍历所有的训练数据集,并且不能很好地扩展大量的特征。此外,它占用了数据科学家的操作工作时间,而这些时间本可以更好地用于创建和改进算法。此外,在提供权重时总是可能存在翻译错误,这可能导致生产中的模型不同于经过训练的模型。
- 在撰写本文时,大众 Java 项目似乎并不活跃,而且落后于大众的最新版本。此外,使用 JNI 可能会带来一些风险,即引入一个可能导致产品中的模型崩溃的错误。
因此,我们的目标是 1)以尽可能接近原生大众环境的方式提供模型,2)易于扩展。
码头工人将允许满足 1);Docker 是一个在操作系统级别执行虚拟化的软件。这意味着我们可以在操作系统上构建应用程序,同时隔离它所需的特定库和依赖项,也就是说,我们不需要在操作系统级别安装它们。该应用程序将在它自己的容器中运行,这将给人一种独立虚拟机的错觉。具体来说,我们将围绕 VW shell 命令构建一个 Python 包装器,它将模型加载到容器的内存中,并在预测请求到来时对其进行评分。请注意,这种方法可以很容易地推广到其他机器学习库加以必要的修改。
Kubernetes 将允许通过扩展集群上的容器来满足 2)并将模型公开为服务。Kubernetes 是容器编排系统的一个例子,它在过去几年中获得了相当大的流行。顺便提一下,我一直认为这个名字意味着类似“立方体”的东西,因为容器通常被描绘成在机器内部运行的小立方体;然而,事实证明它来源于希腊语,意思是船长。
以下是后续步骤的分类:
- 创建 Docker 映像的原型,以在“本地”环境中隔离大众
- 构建一个操场测试模型,以更熟悉大众,并对最终结果进行健全性检查
- 创建一个 Python 包装器来为模型服务
- 开发一个 Flask 应用程序来处理评分请求
- 在 Kubernetes 上将模型部署为服务
这里值得注意的遗漏是:
- 监控培训和生产环境之间的功能对等性。
- 监控需要在生产中加速运行的请求和实例的数量。
理想情况下,最后两个问题的解决方案应该是独立的服务,这些服务可以使用公共 API 与每个单独的模型服务进行对话。
在 Docker 上开发
第一个概念验证是构建一个能够运行大众的 docker 映像。如果我们从一个 Ubuntu 镜像开始,这很容易,因为 VW 已经作为一个包被维护了:
Prototype Docker Image
使用 apt-get 我们安装 Python 3.6 和 VW;然后我们在 requirements.txt 中安装附加包;然后,当我们将在 Kubernetes 上部署时,我们创建了一个用户 vwserver ,由于安全限制,我们可能无法在 Kubernetes 集群上以 root 身份运行容器。最后,我们创建一个挂载点目录 vw_models 来 1)存储生产中的模型,2)允许我们在开发时在笔记本电脑和容器之间交换脚本和代码。
您可以从标签为andrejschioppa/VW _ serving _ flask:tryubuntu的 DockerHub 中提取此图像;如果你更喜欢 CentOS 图像,我也提供andrejschioppa/VW _ serving _ flask:trycentos。我想指出的是,对于 CentOS,我必须从其原始库编译 VW,由于过时的 GCC / g++编译器, Dockerfile 变得更加棘手。
创建操场测试模型
下一步,我们将更加熟悉 VW,并创建一个模型,用于测试我们的服务应用程序的正确性。让我们初始化一个本地卷目录并启动容器;然后我们将 numpy 安装到容器中:
Running the prototype container
我们现在创建一个测试模型;我们有这个文件test _ model . py坐在 localVolume :
Training data generator
这个模型是一个简单的线性模型,有一个数字特征 x2 和三个分类特征 x1,x2,x3;模型是完全确定的:如果分数是 > 0 我们预测正标签 1 ,否则负标签 0 。
我们现在生成训练示例,并将模型训练到完美拟合(毕竟这里的一切都是确定的,因此我们应该期待一个完美的分类器;返回的概率可能不是精确校准的):
Training a VW model
我们看到,正面例子的概率为 73.11% ,负面例子的概率为 50%。在这些 10 的例子中,模型看起来像一个完美的分类器,尽管概率可能没有被校准。我们将保留文件 test_data.json、test_data.vw 和 test_model.model 以备将来测试之用。**
用于提供预测的 Python 模块
我写了一个类 VWModel 来服务 Python 中的 VW 模型。让我们来看看构造函数:
constructor of VWModel
关键思想是启动一个 shell 进程,运行 VW 并通过 Python 控制标准流 : 标准输入,标准输出和标准错误。第一步是获得正确的 vw 命令(第 26 行);注意,我们首先停止训练更新(选项仅测试),即我们不再更新权重𝛃;其次,我们通过选项 initial_regressor 设置用于评分的模型,并将预测重定向到 stdout 。选项链接(第 32 行)允许在广义线性模型(如使用逻辑链接的二元分类器)的情况下指定一个链接函数,如果在训练期间使用了特征交互,选项 sort_features 强制 VW 对特征进行分类。
现在让我们来看看模型实际上是如何启动的:
How VWModel starts a model
这里,我们只是用上面构建的命令启动一个 shell 子进程,并通过子进程控制标准流。管道。关于处理标准流和管道的精彩阅读请看这个精彩的系列。
最后让我们看看实际的评分函数:
How VWModel handles a prediction request
我们使用一个静态方法, parse_example ,将输入 json 转换成 VW 输入格式的字符串。然后,我们将字符串刷新到正在运行的进程的 stdin 中,并收集 stdout 来提取预测。
完整的 Python 模块请看这里的;请注意,在测试目录中,您可以找到一个基于示例和我们上面构建的模型的单元测试;注意这个单元测试意味着在将 vw_model 模块添加到 PYTHONPATH 之后在容器内部运行。
烧瓶应用程序
为了处理模型服务的预测请求,我们将开发一个简单的 Flask 应用程序。Flask 是一个非常流行的用于开发 web 应用程序的 Python 微框架。服务应用程序变得非常简单:
The base Flask application
一个服务请求将通过对 /serve/ 的 POST 请求使用REST API;具体地说,这相当于将 JSON 格式的特性发送到 /serve/ URL。上面的代码所做的只是将预测请求转发给一个名为 FlaskServer 的对象,我们可以在这里检查它:
The interface between Flask application and VWModel
这个 FlaskServer 类只是提供了一个到 VWModel 的接口;有两个关键的环境变量与容器相关联:
- **模型文件,它指向我们想要服务的模型文件
- 模型 _CONF ,它指向一个配置文件,该文件目前支持链接规范和选项来分类特征。
让我们把所有东西放在一起,在一个容器中测试 Flask 应用程序。您可以在这里找到最终的 docker 文件:
- CentOS:tagT25andrejschioppa/VW _ serving _ flask:CentOS _ v1**
- Ubuntu:tagandrejschioppa/VW _ serving _ flask:Ubuntu _ v1
让我们提取映像并将测试模型和数据复制到本地卷:
preparing to run a test
让我们从一个具有相关配置的容器开始:
starting a container for testing
现在,我们在笔记本电脑中打开另一个 shell,检查预测与测试数据是否一致:
the test
一切看起来都很好,我们开始在 Kubernetes 上部署。
在 Kubernetes 部署
最后一步,我们将在 Kubernetes 上部署该模型。这里的基本概念是:
- 持久卷 :这是一个我们存储模型的卷;这可以在服务于相同模型的容器之间共享;这样,我们只需要上传模型一次。在 Kubernetes 的术语中,具有共享网络、存储和如何运行它们的指令的一个或多个容器的组被称为 pod 。在我们的讨论中,pod 与容器是一样的,因为我们只需要一个容器来为模型服务。
- 部署 :这控制给定种类的多少个 pod 正在运行;它确保达到目标吊舱数量,如果某个吊舱崩溃,它会被一个新的吊舱所取代。通过这种方式,可以以可靠的方式扩展服务模型,因为我们可以确保始终有足够的 pod 来处理预测请求。
- 服务 :我们将使用它使我们部署中的 pod 共享相同的 IP 地址;通过这种方式,预测请求只发送到一个 IP 地址,而不必担心负载如何在 pod 之间平衡。
Kubernetes 可以在配置了不同配置的集群上运行。对于这篇文章,我将使用一个本地集群,即我的笔记本电脑上的集群。这可以通过使用 minikube 来实现,这是一个允许在本地运行 Kubernetes 的工具。安装 minikube 并不总是一个简单的过程,请参考文档。在我的例子中,我使用的是 Linux 发行版,所以我能够运行 minikube 而不需要安装管理程序,但是如果你使用的是 MacOS,你就需要安装一个。这里你可以用。我将使用的 yaml* 配置文件。*
让我们启动 Kubernetes 集群并创建一个目录 /mnt/data ,它将作为我们的持久卷。
starting minikube
然后,我们为永久卷和永久卷声明创建配置文件:
creating a persistent volume
claiming a persistent volume
我们现在可以创建体积和体积索赔
persistent volumes on the cluster
为了复制模型和配置文件,我们将使用一个“假部署”,它只是挂载持久卷。
a fake deployment to access the persistent volume
下一步是识别运行假部署的 pod,并复制模型和配置文件;我们终于可以登录到 pod 中检查一切是否正常:
copying the model in the persistent volume
部署只是运行具有所需环境变量的模型服务容器,这些环境变量指定了模型文件、模型配置和服务端口(在本例中为6025);副本的数量指定了要保持运行多少个容器来分配负载:
the application deployment
该服务将 pod 包装在同一个 IP 地址下;这样,我们就不需要担心如何在副本之间平衡请求:
the service
我们最终可以部署豆荚和服务;然后定位服务的 IP,并通过提交预测请求来运行测试:
deploying and testing
结论
通过一点点努力和设计考虑,我们已经将在没有本地服务 API 的库中训练的生产模型。
如果你面临一个类似的任务,我建议不要急于寻找快速的解决方案,不要在纸上探索设计思想及其结果;给予一些思考和反思通常可以避免走进死胡同。在这种情况下,理想的解决方案非常简单:只需使用一个 shell 进程,并通过 Python 控制它。
我非常感谢 Antonio Castelli 让我注意到 JNI 的问题,并测试了这个库的早期原型。
我将非常感谢对 GitHub 代码的改进/扩展的评论、建议和批评以及合并请求。
集合论——基本符号
第二部分——简单看一下操作、符号和文氏图

Originally Published: https://www.setzeus.com/
如前一篇文章所述,学习集合论的一个核心好处不是源于任何特定的理论,而是建立的语言。这就是为什么这篇后续文章的大部分内容广泛涵盖了集合论符号、运算&可视化表示的基础知识。让我们从介绍表示集合&的两个最基本的符号开始。下表包含一个示例集, A ,包含三个元素:

Originally Published: https://www.setzeus.com/
第一行显示了具有三个不同元素的集合A(A = { 1,2,3 });第二行展示了正确的方式来表示某个元素 1、属于集合 A 。到目前为止相当直接——但是一旦我们通过普通操作开始第二次集合&之旅,集合理论就变得有趣多了。
对于下表,我们来介绍两个次级集合 B & C ,它们分别包含以下元素: B = {3,A,B,C,D,E} , C = {1,2} 。尽管我们总共介绍了三个集合(A、B、& C),但下面的示例操作一次只涉及两个集合,因此请密切注意最左侧列中标注的集合。下表包含五个最常用的集合操作数:

Originally Published: https://www.setzeus.com/
这就是集合论中最常见的五种运算。它们在纯数学之外的领域也很受欢迎。事实上,您很可能在过去见过或处理过这些类型的操作,只是没有确切的术语。举个例子,让任何一个小学生描述两个相交组的维恩图&他们会直观地得出正确的结果。
再看看最后一行,相对补语——这不是很奇怪的措辞吗?相对于什么?如果 A — B 的相对补码定义为 A 而不是 B,那么我们如何 表示不是 B 的一切 ?
万能集和空集
事实证明,如果我们想要得到一个有意义的答案,我们首先必须提供一些背景知识。通常在问题开始时明确说明,当一个集合的可容许元素被限制在一些固定的对象类时,存在一个 泛集 即包含该特定问题的所有元素的大集合。例如,如果我们想要处理严格的英文字母集合,那么我们的通用集合由字母表中的 26 个字母组成。**
对于 U 的任意子集 A ,将 A 的补集(用 A 或U—A表示)定义为 A 中非的 U 的所有元素的集合。参考上面提出的问题,B 的补是指泛集内不是 B 的一切,包括 A 。**
在我们继续之前,还有一个概念集合对基本理解非常重要:**空或空 集合。注意这里选择的语法是经过深思熟虑的。这是一次旅行,但只有一个单个的空集,因此它是"空集,"永远不是"空集。"虽然等价性超出了本文的范围,但这里的基本理论是,如果两个集合有相同的元素,它们就是相等的;因此,只能有一个没有元素的集合。因此只有一个空集。****
维恩图&超越
维恩图,在 1880 年由一个叫约翰·维恩的人正式发明,正是你所想象的,尽管学术定义是这样的:
维恩图是显示不同数学集合之间所有可能的逻辑关系的示意图。
下面是六种最常见的维恩图,几乎都显示了我们最近讨论过的操作数:

Originally Published: https://www.setzeus.com/
从集合的最基本的符号&它的元素开始,我们现在已经讨论了基本的操作数,以便产生上面的可视化指南。除了左下方的对称差之外,其他操作都涵盖了。为了不留下任何知识上的空白,对称差,也称为析取并,仅仅是在任一集合&中不相交的元素的集合。****
我们将通过引入基数的概念来总结这一点。由绝对值符号表示,集合的基数就是包含在指定集合中的唯一元素的数量。在上面的例子中,我们三个集合的基数是:|A| = 3,|B| =6,& |C| = 2。在进入下一部分之前,需要思考一些问题——基数&可能子集的数量之间有什么关系?**
集合论——基数和幂集
第三部分——负责分配决策的直觉

Originally Published On https://www.setzeus.com/
在本系列文章第一篇 & 第二篇中,基本的符号和运算已经清楚,我们现在已经建立了对集合论的基本理解。第三篇文章通过对任何给定集合中最重要的属性进行分区,进一步丰富了这一知识:它包含的独特元素的总数。
也称为 基数 ,集合中不同元素的数量为给定集合的进一步、更丰富的分析提供了基础出发点。首先,基数是我们看到的第一个独特的属性,它允许我们客观地比较不同类型的集合——检查从一个集合到另一个集合是否存在双射(带有轻微限定符 ) 的函数的花哨术语。另一种形式的应用,也是本文剩余部分的主题,基数提供了一个窗口,可以查看给定集合中存在的所有可能的子集。从字面上理解就是日常的决策分配问题,比如预算一次购物旅行或者平衡一个投资组合。

Originally Published On https://www.setzeus.com/
左边的例子(上面的 mobile)描述了五个独立的集合,它们各自的基数在右边。正如所见,集合的基数符号类似于绝对值符号——夹在两条垂直线之间的变量。这些例子很清楚,除了最后一行,它强调了这样一个事实:只有集合中的惟一元素对基数有贡献。
还记得上一篇文章中的子集吗?原来,某个集合的基数AA&AA的可能子集的数量有一种令人着迷的关系。如下所述,可以从某个子集构建的子集的数量随着基数的增加而增加,增加量是可预测的:
# C 中的可能子集= |C|
我们将在下面演示一个例子。然而,让我们先花点时间反思一下上面公式的直觉。把基数想象成一个集合所代表的“槽”的总数。当构建某个子集时,对每个可能的“槽”做出一个布尔(是/否)决策这意味着添加到集合中的每个唯一元素(也就是基数增加 1)都会使可能的子集数量增加两倍。作为一名程序员或计算机科学家,一旦你意识到一个给定集合的所有子集都可以用一个纯二进制数的表来计算,你可能会更加欣赏这个逻辑。
发电机组
在我们为上面的示例集 C 导出所有子集之前,我想介绍最后一个术语——幂集 。 用大写字母 S 表示,后跟一个包含原集合 S (C) 的括号,幂集是 C 的所有子集的集合,包括空/空集&集合 C 本身。下表展示了幂集 S(C) ,集合 C 的所有可能子集的不同排列都包含在一个大集合中。

For formatting reasons I excluded commas between sets***
动力装置有什么用处?好吧,你可能已经在不知不觉中多次使用了力量集合背后的直觉。任何时候你从一个更大的集合中选择一个物品子集,你都是在选择一个能力集合中的一个物品。例如,一个小孩拿着 5 美元逛糖果店——她会选择所有糖果中的哪个元素?或者对于更技术性的问题,作为软件工程师,您可能希望查询所有可能的数据库用户,这些用户也具有属性 X & Y — 另一个例子是从所有可能的子集中选择一个子集。
论等价和双射函数
我们现在理解了集合的基数,为什么它很重要,以及它与幂集的关系。所以让我们暂时回顾一下我们在这篇文章开始时忽略的东西:在集合论中到底是如何定义等价的?
可以说,具有相同基数的两个集合共享一些共同的属性,但是相似之处仅限于此——如果其中一个集合的某个元素重复多次会怎么样?如果两个集合共享相同的基数 & 个元素会怎么样?不可否认,它们在某种程度上是“相等”的,但即使在这种情况下,仍然有和区分的空间,因为每个集合可能有不同的元素重复相同的次数。这里的要点是集合论中等价的概念相对于数学的其他分支来说有点陌生。在这个世界中建立等价关系需要它自己的介绍语言。本系列的最后一篇文章介绍了等价的概念,以及它的基本性质,如单射、双射、&满射函数。
原载于
集合论—函数
用双射、满射和内射描述行为

Originally Published On https://www.setzeus.com/
今天我们将在集合论的世界里展开 函数 。与前面介绍的概念相似,集合内标准函数的命名法与其他数学分支略有不同,&因此需要复习。有相当多的术语要介绍,所以让我们直接开始吧!下面的第一个函数术语表反映了标准函数的域、范围、&输出的概念:

Originally Published On https://www.setzeus.com/
集合论世界中的一个函数简单来说就是集合 A 中的一些(或全部)元素到集合 b 中的一些(或全部)元素的映射,在上面的例子中,A 中所有可能元素的集合被称为 域;而 A 中作为输入的元素被专门命名为 arguments 。在右边,所有可能输出的集合(在其他分支中也称为“范围”),称为;而从 A 映射到 B 中的实际输出元素的集合被称为 图像。
到目前为止没什么太复杂的,只是用一种新的方式定义函数的参数。接下来,我们将介绍如何用常见的函数类型来描述这些映射函数的行为。
注射、满射和双射
在集合论中,常用三个术语对集合映射进行分类:内射、满射&双射。不幸的是,这些术语有几个不同的名称,这加剧了混淆——因此,我们将首先回顾每个定义,然后,浏览一些直观的示例。这三个术语都描述了参数&图像的映射方式:
- 一个函数是 内射的 ( 又名“一对一”),如果共域的每个元素至多被映射到域的一个元素。
- 一个功能是一个一个一个 ( 一个)。 k.a "到")如果共域的每个元素被映射到域的至少一个元素。(即函数的像和共域相等。)
- 如果密码子的每个元素被结构域的一个元素精确地映射到上,则函数是 双射 ( 又名“一对一的&到”、“一对一的对应关系”)。
在这里,复杂命名法之上的众所周知的“樱桃”延伸到了“注射的”、“满射的”和“双射的”这些词的可能含义用于描述一个函数(映射)时,前者的含义是正确的;然而,纯粹通过这些特征来识别函数(映射)也是正确的。所以有注射行为的函数叫做注射,有注射行为的函数叫做注射,&最后,有双注射行为的函数叫做双注射。
重新阅读上面的要点。双射只是一个满足和先前要求的函数——也就是说,该函数是和双射&射。&射函数不一定是射的。接下来是一个直观的例子,这三个分类导致了以下四种可能的注射&射特征组合的集合函数,总结如下:

Originally Published On https://www.setzeus.com/
我们走吧!我们现在对集合世界中常见的映射类型有了初步的了解。然而,这绝不是旅程的结束,因为我们一直在回顾高级别介绍——与此相反,这只是开始。
集合论的基础是解开数学高等分支中的理解的关键。为了继续我们在许多分支中的提升,我们接下来将利用我们的集合论知识来消化所有数学史上绝对最具开创性的理论之一:策梅洛-弗兰克尔集合论。
原为发表于
集合论——历史与概述
第一部分——什么是集合论&为什么它与今天相关?

Originally Published: https://www.setzeus.com/
无穷大的概念在意识形态上与普通的数学术语相去甚远——在数学圈之外,没有任何其他主题可以从实用的分析工具转化为神话般的现象。与宗教和哲学等文化话题相结合,无限的概念拥有一种特殊的神性光环。
曾几何时,在所有的学科中,存在一个单一的无限,这是一个基本假设。
然后,在 1874 年,一位相对默默无闻的数学家发布了一组突破性的观察&针对这一世俗的、根深蒂固的信仰的革命性问题。一个叫乔治·康托(Georg Cantor)的人,在他现在已成为传奇的出版物 中,关于所有实代数数 的集合的一个性质,证明了实数的集合比实代数数的集合“更多”。这第一次表明,存在不同大小的无限集合(不要担心,我们将很快详细回顾他的论文以澄清)。

A Set Is Many That Allows Itself To Be Though Of As One — Georg Cantor
在 1874 年和 1897 年之间,康托尔不顾一切地出版了一份又一份出版物——把他的抽象集合理论扩展成一门欣欣向荣的学科。然而,他遇到了持续的抵制和批评;事实上,许多训导者认为他的理论侵犯了哲学家的领域&违反了宗教原则。
然而,一旦分析的应用开始被发现,人们的态度就改变了。他的想法和结果逐渐被接受。到了 1900 年代,他的观察、理论出版物最终导致了对现代集合论(T21)的认可,这是一个全新的、完全不同的数学分支:
集合论 是关于称为集合的明确集合的数学理论,这些集合由称为集合的成员或元素的不同对象组成。
0 和 1 之间有多少个数字?
在简洁的四页半,康托尔的原始出版物设置为紧凑的辉煌展示酒吧。它被分成两种不同的证明,它们一起得出结论,承认至少两种独特类型的无限。
理论的第一部分考察了实数、代数数 &的集合,确定它是一个可数无穷集合。这里不要迷路,“可数”不一定是严格按整数计数;在集合论的上下文中,可数意味着一个集合,甚至是无限元素中的一个,可以用一个可重复的序列来描述,比如一个有序的多项式函数。康托尔把这个可以与一个序列一一对应的无穷多个数的集合的性质称为 一一对应。
简而言之,所有实数、代数数的集合或集合可以使用一些具有不同次数系数的多项式的理论序列&来导出;因此,所有实数、代数数的集合是一个可数无穷集合。
康托尔论文的第二部分分析了实数的作用,复数数字,又称 先验数字 。超越数,最好的例子是 pi & e,有一个奇特的性质,在数学上不可能用多项式函数来推导它们——它们是而不是代数的。无论高度、部分数量、次数或系数如何,没有一个序列会在它的可数无穷集合中计算圆周率。**
康托尔接着指出,在任何实数的闭区间[ a , b ]中,至少存在一个超越数,它永远不会被计数到一个可数无穷集合中。因为存在一个这样的数,所以假设在实数家族中存在无限个超越数。**
因此,第一次证明了一组连续的、流动的不可数数字之间的非常明显的区别&一组可数的、可排序的数字,例如所有的实代数数。
论符号与运算
康托尔的第一份出版物就停留在对至少两种不同的无限的惊人确认上。从这个原始出版物开始,出现了一系列附录,缓慢但稳定地为现代集合论铺平了道路。

Originally Published: https://www.setzeus.com/
这里值得注意的一个有趣的观察是,大多数在实践中使用集合论的人并不看重某个特定的定理,而是更看重它所建立的的广义语言。由于它的抽象性质,集合论的影响存在于许多其他数学分支的幕后。在需要微分&积分的分析中,对极限&函数连续性的理解最终基于集合论。在布尔代数中,“与”、“或”、“非”的逻辑运算与交集、并集、 & 差的集合论运算相对应。最后但同样重要的是,集合论提供了拓扑学的基础,研究几何性质&的空间关系**
现在对集合的历史有了基本的了解&快速预览它的影响深度,是时候熟悉基本的集合论符号了。在下一篇文章中,我们将介绍集合维恩图的常见符号、操作以及许多配置。
来源
在谷歌云平台上设置深度学习服务器
使用 TensorFlow、Keras、Python 和 Jupyter Notebook 配置和设置深度学习服务器的非常简单的方法

Photo by Thuan Nguyen (The Butchart Gardens, Victoria, British Columbia, 10/2019)
诸如机器学习和深度学习的人工智能(AI)领域正在快速发展,并且由于深度学习在试图训练大量数据(即,大数据)的准确性方面的优势,深度学习最近变得更加流行。近年来,人工智能深度学习在包括工程、医学和制造业在内的不同领域取得了令人惊叹的成就。为了促进和加快建立、训练和测试神经网络的任务,研究人员和从业人员可以使用各种人工智能框架,其中由谷歌开发的开源库 TensorFlow 是目前最受欢迎的一个。手动安装包括工具在内的所有必需的软件应用程序,并使它们在深度学习服务器中平稳可靠地工作,并不是一件容易的事情。使用深度学习映像在谷歌云平台(GCP)上建立远程服务器会容易得多,这些映像可以与所有必要的软件应用程序和开发工具一起使用。它甚至更好的信用$300.00,可用于服务器。
1.在 GCP 创建项目
1.1 创建一个 Google 帐户或 Google Gmail 帐户
使用谷歌云平台需要谷歌账号或 Gmail 账号。如果用户没有,他/她需要创建一个
1.2 获得 GCP 价值 300 美元的 12 个月免费试用
访问链接:
https://cloud.google.com/free/
使用上述 Google 帐户/Gmail 获得此信用点数。
2.访问 GCP 控制台
—)打开 Chrome 浏览器
—)输入:谷歌云控制台进入网址搜索框

3.使用 GCP 深度学习图像创建深度学习服务器
在 https://cloud.google.com/deep-learning-vm/访问云深度学习虚拟机镜像链接

点击转到控制台

单击顶部菜单栏上的箭头选择项目,例如,选择名为“EXAMPLE_PROJECT”的项目(备注 : 新的深度学习服务器将在这个项目下创建为 GCP 计算引擎实例。)

单击计算机引擎上的启动

输入实例的名称,例如“tf1-keras-ann”(注释 : 用户可以根据自己的需要命名实例。)
选择区域:“us-east1-c”或此链接上可用的任何其他区域:https://cloud.google.com/compute/docs/regions-zones/
选择机器类型:点击箭头打开下拉菜单并选择机器类型:n1-standard-8 对于一个正常的深度学习项目来说是一个不错的选择。

选择 GPU 的数量

若要降低使用服务的费用,请选择“无”但是,如果用户需要,请选择要部署的 GPU 数量。
选择 AI 框架的版本:选择 TensorFlow 1.xx 或 TensorFlow 2.0。

选择启动盘类型和大小

选择标准永久磁盘或 SSD 永久磁盘
,输入以 GB 为单位的引导磁盘大小:64 GB 或更大
单击部署

…等待深度学习服务器的部署被部署……..
点击产品和服务图标(左上角的三个水平条)打开菜单

选择计算引擎

完成了!
深度学习服务器已经成功搭建在谷歌云平台(GCP)上。
重要提示:
—)用户在不使用虚拟机时必须将其停止,以避免不必要的费用。
用 Playground.jl 在 Julia 中设置虚拟环境

在任何语言的项目中,有超过十亿个理由让你想要使用虚拟环境,这种情绪在 Julia 中也不例外。当与一个没有虚拟环境的团队一起工作时,开发 web 应用可能是灾难性的。幸运的是,Julia 有一个相当于 virtual/pipenv 的包虚拟化工具,叫做 Playground.jl 。
搭建游乐场
在我们可以使用操场之前,我们当然需要设置它。如果您还没有添加软件包,您可以使用:
julia> using Pkg
julia> Pkg.add("Playground")
有趣的是,这对我不起作用,所以我最终切换到 Pkg REPL,只需按下],然后通过 URL 添加它,就像这样:
julia> ]
pkg> add [https://github.com/rofinn/Playground.jl](https://github.com/rofinn/Playground.jl)
Cloning git-repo `[https://github.com/rofinn/Playground.jl`](https://github.com/rofinn/Playground.jl`)
Updating git-repo `[https://github.com/rofinn/Playground.jl`](https://github.com/rofinn/Playground.jl`)
[ Info: Assigning UUID f8d4ef19-13c9-5673-8ace-5f74ae9cf246 to Playground
Resolving package versions...
Installed Syslogs ─ v0.3.0
Installed Memento ─ v0.12.1
Updating `~/.julia/environments/v1.0/Project.toml`
[f8d4ef19] + Playground v0.0.0 #master ([https://github.com/rofinn/Playground.jl](https://github.com/rofinn/Playground.jl))
Updating `~/.julia/environments/v1.0/Manifest.toml`
[f28f55f0] + Memento v0.12.1
[f8d4ef19] + Playground v0.0.0 #master ([https://github.com/rofinn/Playground.jl](https://github.com/rofinn/Playground.jl))
[cea106d9] + Syslogs v0.3.0
Building Playground → `~/.julia/packages/Playground/AhsNg/deps/build.log`
酷!
为我建造操场也通过一个错误,一个 Stacktrace: Pkg 未定义的错误。为了解决这个问题,我必须首先运行:
ENV["PLAYGROUND_INSTALL"] = true
然后我不得不用朱莉娅·REPL 的 Pkg 来建造它…我不确定这是为什么,但我想这与操场依赖有关。
虽然这不是必需的,但我也在我的。bashrc 文件,因为有时需要它:
echo "PATH=~/.playground/bin/:$PATH" >> ~/.bashrc
使用
用法相对简单,但是需要注意的是,您可能需要关闭并重新打开终端,以便 bashrc 文件回显新命令。为了创造我们的环境,我们使用操场创建:
playground create --name example
您还可以根据需求创建环境:
playground create --requirements /path
如果使用声明文件,你应该确保已经安装了DeclarativePackages.jl。我们可以像这样激活我们的操场环境:
playground activate /path/to/your/playground
另外,playground 保存了我们环境的名称,所以我们可以在终端中使用名称 bash 标签。
playground activate --name example
为了移除我们的环境,我们将使用 rm。
playground rm [playground-name|julia-version] --dir /path
此外,我们有操场名单和操场清洁。
playground list
playground clean
就是这样!
这就是概要!这就是全部内容,playground 很容易使用,也不太深入。因此,希望在缺乏操场文档资源的情况下,这是相对有价值的,并且您现在知道如何在 Julia 中创建和管理虚拟环境了!
使用此扩展设置您的 Jupyter 笔记本

一个方便的 Jupyter 笔记本扩展,帮助您创建更有效的笔记本
在精彩的演讲“我不喜欢笔记本”(视频和幻灯片)中,Joel Grus 对 Jupyter 笔记本提出了无数批评,这可能是最受欢迎的数据科学环境。我发现这个演讲很有教育意义——当每个人都认为某件事很棒时,你需要那些愿意批评它的人,这样我们才不会变得自满。然而,我认为问题不在于笔记本本身,而在于它是如何被使用的:像任何其他工具一样,Jupyter 笔记本可能(也确实)经常被滥用。
因此,我想修改 Grus 的标题并声明“我不喜欢凌乱的、没有标题的、没有解释或评论的无序笔记本Jupyter 笔记本是为文字编程而设计的——将代码、文本、结果、图表和解释混合在一个无缝文档中。据我所见,这种观念经常被完全忽视,导致 GitHub 上可怕的笔记本充斥着资源库:

Don’t let notebooks like this get onto GitHub.
问题很明显:
- 没有标题
- 没有解释代码应该做什么或如何工作
- 电池运行不正常
- 单元输出错误
Jupyter 笔记本可以成为学习、教学、探索和交流的非常有用的工具(这里是一个很好的例子)。然而,如上所述的笔记本在所有这些方面都失败了,当这些问题出现时,几乎不可能调试其他人的工作,甚至不可能弄清楚他们在试图做什么。至少,任何人都应该能够为笔记本命名一些有帮助的东西,写一个简短的介绍,解释和结论,按顺序运行单元格,并确保在将笔记本发布到 GitHub 之前没有错误。
解决方案:安装 Jupyter 笔记本扩展
我决定看看 Jupyter 笔记本扩展能做些什么,而不是仅仅抱怨这个问题。结果是在自动打开新笔记本时的扩展:
- 创建一个模板来鼓励文档
- 插入常用的库导入和设置
- 反复提示您将笔记本名称从“无标题”更改为

The extension running when a new notebook is opened
这个扩展的好处是它改变了默认值。默认情况下,Jupyter 笔记本没有降价单元格,没有命名,也没有导入。我们知道人类是出了名的不擅长改变默认设置,那么为什么不让默认设置鼓励更好的实践呢?把安装扩展想象成一个轻推——一个轻轻地推动你写更好的笔记本的东西。
要使用此扩展:
- 安装 Jupyter 笔记本扩展(无论如何你都应该使用)
- G o 到 GitHub,下载
[setup](https://github.com/WillKoehrsen/Data-Analysis/tree/master/setup)文件夹(有 3 个文件) - 运行
pip show jupyter_contrib_nbextensions找到笔记本扩展的安装位置。在我的 Windows 机器上(带有 anaconda ),它们位于
C:\users\willk\anaconda3\lib\site-packages\jupyter_contrib_nbextensions
在我的 mac 上(没有 anaconda ),它们位于:
/usr/local/lib/python3.6/site-packages/jupyter_contrib_nbextensions
4.将setup文件夹放在上述路径下的nbextensions/中:

5.运行jupyter contrib nbextensions install安装新的扩展
6.运行 Jupyter 笔记本并启用nbextensions选项卡上的Setup(如果您没有看到此选项卡,请打开笔记本并转到edit > nbextensions config)

Enable the Setup extension on the nbextensions tab
现在打开一个新的笔记本,你就可以开始了!你可以在main.js中改变默认模板(参见我的关于编写 Jupyter 笔记本扩展的文章,了解更多关于如何编写你自己的模板的细节)。默认模板和导入相对简单,但是您可以根据需要定制它们。

Default template and imports
如果你打开一个旧的笔记本,你不会得到默认的模板,但每次运行一个单元格,你会被提示从Untitled开始更改名称:

The Setup extension will continue prompting until the notebook name is changed from Untitled.
有时候,你需要一点点坚持来改变你的方式。
离别的思绪
从现在开始,让我们努力创造更好的笔记本。这不需要太多额外的努力,而且回报很大,因为其他人(和你未来的自己)将能够从你的笔记本上学习,或者使用结果来做出更好的决定。这里有一些简单的规则来写有效的笔记本:
- 给你的笔记本命名。当你有几十个文件时,简单但有用。
- 添加清晰而简明的解释,说明您的代码做了什么,如何工作,最重要的结果是什么,以及得出了什么结论。我使用笔记本的标准模板来鼓励这个习惯。
- 在共享笔记本之前,请按顺序运行所有单元格,并确保没有错误。
安装扩展不会解决所有与笔记本相关的问题,但希望这些小小的推动会鼓励你养成更好的习惯。建立最佳实践需要一段时间,但是,一旦你把它们记下来,它们往往会坚持下来。通过一点点额外的努力,我们可以确保下一次有人谈论笔记本时会说:“我喜欢高效的 Jupyter 笔记本。”
一如既往,我欢迎反馈和建设性的批评。可以通过 Twitter @koehrsen_will 联系到我。
使用 Windows Subsystem for Linux (WSL)和 Jupyter 建立数据科学环境
您将从本文中学到什么:在 Linux 系统中使用 WSL 在 Windows 中使用完全可定制的 Jupyter 笔记本建立一个完整的 Python 环境!

我们要做的是:
- 设置 WSL (可选 ZSH 和哎呀呀
- 设置蟒蛇。
- 设置可定制的 Jupyter 笔记本环境。
如果你喜欢这篇文章并想看更多,请务必关注我的简介。
设置 WSL

Windows Subsystem for Linux (WSL) is a compatibility layer for running Linux binary executables natively on Windows 10! It also has the added benefit of you getting full admin rights to the subsystem once installed.
首先,我们必须做一些初步的设置来让 WSL 工作:
1 —打开开发者模式
首先进入 Windows 10 设置菜单中的开发者设置:
设置➡更新&安全➡开发者➡开发者模式
或者简单地搜索“开发人员,然后点击“开发人员设置

在这里,您可能需要登录管理员帐户或获得临时访问权限来打开开发人员模式:

一旦启用,我们需要打开 Linux 的 Windows 子系统功能。
2 —为 Linux 打开 Windows 子系统
该菜单可通过以下方式访问:
控制面板➡程序➡打开和关闭 Windows 功能
或者通过搜索“ windows 功能并选择“打开或关闭 windows 功能

接下来,确保勾选了特性“Windows Subsystem for Linux”:

3 —重启电脑
我认为这不需要任何解释。
4 —安装 Ubuntu
前往微软商店,搜索 Ubuntu ,选择应用(唉..)您喜欢并安装它:

I’m personally using the default “Ubuntu” version.
当第一次启动时,你会被提示输入一个 UNIX 用户名和密码,选择你认为合适的。
祝贺您,您现在拥有了一个全功能的 Linux 子系统,并拥有完全的管理员权限!
5 —设置 ZSH 和 Oh-my-zsh(可选)
就我个人而言,我不太喜欢默认终端的外观,所以我建议按照这个安装 ZSH 和我的天啊,如果你喜欢的话:
[## 用 zsh + oh-my-zsh + ConEmu 为 Linux 设置 Windows 子系统
的时代。NET 开发者被限制只能使用 Windows 作为平台的时代已经一去不复返了。(至少对于 ASP 来说是这样。网)。那个…
blog.joaograssi.com](https://blog.joaograssi.com/windows-subsystem-for-linux-with-oh-my-zsh-conemu/)
设置 Anaconda

Anaconda is one of the most widely used solutions for package management for R and Python, it comes with 1.500+ popular packages out of the box which is more than enough for 99% of all Data Science related tasks!
1 —下载 Anaconda 启动新配置的 WSL 并下载 Anaconda:
wget [https://repo.anaconda.com/archive/Anaconda3-2019.03-Linux-x86_64.sh](https://repo.anaconda.com/archive/Anaconda3-2019.03-Linux-x86_64.sh)
2 —安装 Anaconda 接下来,执行下载的文件来安装 Anaconda:
Anaconda3-2019.03-Linux-x86_64.sh
NB :如果使用 ZSH 遇到问题,使用“bash anaconda 3–2019.03-Linux-x86 _ 64 . sh”。
按照屏幕上的说明安装 Anaconda 之后,只需删除安装文件:
rm Anaconda3-2019.03-Linux-x86_64.sh
3 —更新 Anaconda 现在您应该能够启动您的 Anaconda 环境了:
source ~anaconda3/bin/activate
" ~ anaconda 3/bin/activate "是 Anaconda 将自己安装的默认位置,但是如果您选择了其他位置,只需指向该目录。
激活后,启动完全更新:
conda update --all
4 —完成 一旦更新,向后靠,凝视你的创作,看到它是好的…
您已经成功地用 Anaconda 3 为 Linux 设置了一个 Windows 子系统!
设置 Jupyter 笔记本电脑环境

Jupyter is one of the go-to IDE for interactive cell-based code execution with Python and R. Really well suited for data exploration and analysis!
这非常简单,因为 Jupyter 是 Anaconda 自带的,所以如果您对默认的 Jupyter 笔记本界面/环境满意,那么您已经完成了!激活 Anaconda 环境后,只需使用以下命令启动笔记本:
jupyter notebook
WSL 实例应该自动启动一个连接到笔记本的窗口,但如果不是这样,只需在 WSL 终端中找到主机地址,如下所示:" http://localhost:8888/tree?token = somepletylongtokenhere "并将其复制到您选择的浏览器中。
定制 Jupyter 笔记本电脑
我将介绍两种定制 Jupyter 笔记本体验的主要方式,分别是 Nbextensions 和 Themes ,跳转到任何与您相关的部分。
1-设置扩展
值得庆幸的是,这也很简单,只需运行以下命令来设置 nbextensions 包并重启您的笔记本:
pip install jupyter_contrib_nbextensions && jupyter contrib nbextension install
接下来,当您启动名为“Nbextensions”的笔记本时,会出现一个新的选项卡

现在你可以简单地挑选你想要/需要的插件,并根据你的心意定制。该界面还为您提供了每个扩展的截图和描述,因此您可以判断是否要启用它。
我特别推荐以下几点:
- 腹地 —便于自动校正。
- 目录(2) —用于根据您的减价标题和副标题自动生成目录。
- 切换所有行号——便于调试。
- 变量检查器 —对于一个类似 R 的变量概览,真的很有用!
- ExecuteTime —用于为单元执行计时。
- Autopep8 —用于自动将您的代码格式化为 pep8 标准。
- 可折叠标题 —便于隐藏笔记本的各个部分。
就是这样!如果您觉得这还不足以打消您对扩展的兴趣,我可以强烈推荐以下关于同一主题的文章:
如何充分利用笔记本电脑环境
towardsdatascience.com](/jupyter-notebook-extensions-517fa69d2231)
2 —设置主题
我个人使用的是 GitHub 上 dunovank 的这个包:
要安装此软件包,只需运行以下命令:
pip install jupyterthemes
接下来开始你选择的主题;暗或亮** (请参考GitHub链接了解更多定制选项)只需在您的 WSL 终端中运行以下命令之一:**
# DARK
**jt -t onedork -fs 95 -altp -tfs 11 -nfs 115 -cellw 88% -T**# LIGHT
**jt -t grade3 -fs 95 -altp -tfs 11 -nfs 115 -cellw 88% -T**
我更喜欢深色主题,它让你的笔记本看起来像这样:

就是这样!你完了。享受你的新环境!
感谢阅读
我希望它对你有用。如果你觉得我遗漏了一些重要的东西,或者你只是想给我留下一些反馈,请留下你的评论!
如果你想看和了解更多,一定要关注我的 媒体 🔍和 碎碎念 🐦
阅读彼得·尼斯特拉普在媒介上的作品。数据科学、统计和人工智能...推特:@PeterNistrup,LinkedIn…
medium.com](https://medium.com/@peter.nistrup)**
为初学者设置 AWS EC2 实例
如果你正在从事一个大数据机器学习项目,你很可能需要亚马逊网络服务(AWS)、谷歌云服务或微软 Azure 的服务。我第一次接触云计算是在 AWS,所以这是我最熟悉的平台。我希望使用正确的操作系统和软件包设置虚拟环境的过程像单击一个选项一样简单,但是出于安全原因和提供广泛的选项,情况并非如此。
以下是步骤:
- 选择 EC2 AMI 和实例
- 改变你的。pem 密钥权限
- 通过 SSH 访问您的 EC2 实例
- 通过 SCP 或 Git 管理您的数据
**T3 选择一个 EC2 AMI &实例 **
点击这个 链接 。登录您的 AWS 帐户,如果您还没有,请创建一个。您将花费大部分时间管理 AWS 实例的地方是 AWS 管理控制台。

选择 EC2 服务。

点击启动实例

现在,您需要选择您喜欢的 AMI (Amazon 机器映像)。AMI 因其操作系统和预安装的软件包而异。根据我的经验,大多数 AMI 都提供 Linux 或 Ubuntu 的变体。我给初学者推荐这个 Ubuntu 深度学习 AMI 。
接下来,选择您的首选实例。实例因存储容量以及 CPU 和 GPU 能力而异。亚马逊在这里详细介绍了他们的选项。
注意:如果你选择了一个相对强大的实例,那么 AWS 会要求你提交一份使用意图的说明。他们通常会在几天内回复。
改变自己。pem 密钥权限
选择实例后,会要求您创建一个新的或选择一个现有的密钥对,如下所示。

您的密钥将作为下载到您的计算机上。pem* 文件。最佳做法是仔细存放和整理您的钥匙。如果您失去了它们,那么您就完全没有希望再次访问您的 EC2 实例。请把前面的句子读 10 遍。启动您的实例。*
我通常将我的密钥存储在标有的隐藏文件夹中。MacOS 上的 ssh* 。假设您的密钥已下载到“下载”文件夹,请打开一个终端窗口,并使用您合适的密钥名称键入以下命令:*
*cd downloadsmv ~/downloads/yourkey.pem ~/.ssh*
默认情况下,您的密钥带有权限限制,这将阻止您访问实例或在实例上使用其他命令。为避免这一问题,假设您位于密钥所在的目录中,请键入以下内容:
*chmod 700 yourkey.pem*
通过 SSH 访问您的 EC2 实例
EC2 实例,检查;。pem 键,检查。在继续之前,您需要找到以绿色突出显示的公共 DNS。单击您新创建的实例,应该会出现如下所示的描述框。

您使用 ssh(安全 shell)命令来访问您的实例。打开一个终端窗口,键入以下内容,显然是替换您自己的密钥名和公共 DNS。
*ssh -i ~/.ssh/yourkey.pem [ubuntu@ec2-xx-xx-xxx-xxx.us-west-2.compute.amazonaws.com](mailto:ubuntu@ec2-xx-xx-xxx-xxx.us-west-2.compute.amazonaws.com)*
终端窗口应该显示一个提示,欢迎您进入实例的虚拟环境。从这个窗口,你可以传输你的文件和工作,你真棒项目!
注意:根据您的 AMI 的操作系统,您将在上面的 ssh 命令中的“@your_public_DNS”之前键入“ec2-user”(通常用于基本 Linux)、“ubuntu”或您合适的操作系统名称。
重要提示:您的公共 DNS 可能会因您的位置而异。因此,如果您在访问您的实例时遇到问题,那么很可能您的公共 DNS 已经更改,在这种情况下,请再次确认您的公共 DNS 是否正确。
通过 SCP 或 Git 管理您的数据
要在您的计算机和实例之间传输文件,您可以使用 scp(安全协议)命令。打开一个 n ew 终端窗口,其当前目录位于您的计算机上,而不在实例环境中,并键入以下内容,显然是替换您自己的键名和公共 DNS。
从计算机上传文件到实例
*scp ~/.ssh/yourkey.pem ~/path/to/file [ec2-user@ec2-xx-xx-xxx-xxx.compute-1.amazonaws.com](mailto:ec2-user@ec2-xx-xx-xxx-xxx.compute-1.amazonaws.com):path/to/file*
将文件从实例下载到计算机
*scp ~/.ssh/yourkey.pem [ec2-user@ec2-xx-xx-xxx-xxx.compute-1.amazonaws.com](mailto:ec2-user@ec2-xx-xx-xxx-xxx.compute-1.amazonaws.com):path/to/file ~/path/on/local/machine*
令人惊讶的是,在访问 EC2 实例的终端窗口中,您可以使用 Git 命令在实例环境和 Github 之间拉和推文件,就像在本地机器上一样。显然,实例上的所有数据都存储在 AWS 的服务器上。
真正重要的注意:
AWS 根据您选择的实例规范向您收取及时费用。无论你做的是轻活还是重活,如果你的实例开启,AWS 都会向你收费。
我和我的几个同事,由于不理解上述协议的无知和疏忽,犯了这个菜鸟错误,导致我们累积了几百美元的账单。幸运的是,亚马逊感同身受,取消了收费。但是,您不应该让这种情况发生。
因此,每当你完成一个工作会话,1 分钟或 1 小时,总是停止你的实例。然后当你恢复工作时,简单地启动它。终止会删除你的整个实例,所以你必须从头创建一个新的。

就这样结束了!当我开始建立 AWS 的时候,我有一个痛苦的经历,但是希望这个指南可以成为你的小抄。
有时,我们学习的材料并不困难,但我们的学习方法和资源有助于应对挑战。
使用 Python 和 Gmail 帐户为您的刮板设置电子邮件更新

Photo by Jamie Street on Unsplash
通常,当构建 web 抓取器来收集数据时,您会遇到以下情况之一:
- 您想将程序的结果发送给其他人
- 您正在远程服务器上运行该脚本,并且希望获得自动、实时的结果报告(例如,来自在线零售商的价格信息更新,表明竞争公司对其职位空缺网站进行了更改的更新)
一个简单而有效的解决方案是让你的网络抓取脚本自动将结果通过电子邮件发送给你(或者其他感兴趣的人)。
事实证明这在 Python 中非常容易做到。你只需要一个 Gmail 账户,就可以搭载谷歌的简单邮件传输协议(SMTP)服务器。我发现这个技巧真的很有用,特别是对来说,我最近创建了一个项目,通过一个程序给我和我的家人发送每月财务更新,这个程序对我们的 Mint 账户数据进行一些定制计算。
第一步是导入内置的 Python 包,它将为我们完成大部分工作:
import smtplib
from email.mime.text import MIMEText
smtplib是内置的 Python SMTP 协议客户端,允许我们连接到我们的电子邮件帐户并通过 SMTP 发送邮件。
MIMEText类用于定义电子邮件的内容。MIME(多用途互联网邮件扩展)是一种标准,用于格式化要通过互联网发送的文件,以便可以在浏览器或电子邮件应用程序中查看。它已经存在很久了,它基本上允许你通过电子邮件发送除 ASCII 文本之外的东西,比如音频、视频、图像和其他好东西。以下示例用于发送包含 HTML 的电子邮件。
以下是构建 MIME 电子邮件的示例代码:
sender = ‘your_email@email.com’
receivers = [‘recipient1@recipient.com’, ‘recipient2@recipient.com’]
body_of_email = ‘String of html to display in the email’
msg = MIMEText(body_of_email, ‘html’)
msg[‘Subject’] = ‘Subject line goes here’
msg[‘From’] = sender
msg[‘To’] = ‘,’.join(receivers)
MIMEText对象将电子邮件消息作为一个字符串接收,并指定该消息有一个 html“子类型”。参见本网站获取 MIME 媒体类型和相应子类型的有用列表。查看 Python email.mime docs 中可用于发送其他类型 mime 消息的其他类(例如 MIMEAudio、MIMEImage)。
接下来,我们使用主机‘smtp.gmail.com’和端口 465 连接到 Gmail SMTP 服务器,使用您的 Gmail 帐户凭据登录,然后发送:
s = smtplib.SMTP_SSL(host = ‘smtp.gmail.com’, port = 465)
s.login(user = ‘your_username’, password = ‘your_password’)
s.sendmail(sender, receivers, msg.as_string())
s.quit()
注意:注意,电子邮件收件人的列表需要在msg[‘From’]的赋值中表示为一个字符串(每封电子邮件用逗号分隔),在smtplib对象s.sendmail(sender, receivers, msg.as_string()中指定时表示为一个 Python 列表。(在相当长的一段时间里,我都在用头撞墙,试图找出为什么消息只发送给第一个收件人,或者根本没有发送,这就是错误的根源。终于碰到这个 StackExchange 帖子解决了问题。)
作为最后一步,你需要更改你的 Gmail 帐户设置,以允许访问“不太安全的应用程序”,这样你的 Python 脚本就可以访问你的帐户并从它发送电子邮件(参见此处的说明)。在您的计算机或其他机器上运行的 scraper 被认为是“不太安全的”,因为您的应用程序被视为第三方,它会将您的凭据直接发送到 Gmail 以获得访问权限。相反,第三方应用程序应该使用像 OAuth 这样的授权机制来访问你的账户(见这里的讨论)。
当然,您不必担心您自己的应用程序访问您的帐户,因为您知道它不是恶意的。然而,如果其他不受信任的应用程序可以做到这一点,他们可能会在不告诉您或做其他讨厌的事情的情况下存储您的登录凭据。因此,允许不太安全的应用程序访问会使您的 Gmail 帐户不太安全。
如果你不愿意在你的个人 Gmail 帐户上打开对不太安全的应用程序的访问,一个选择是创建第二个 Gmail 帐户,专门用于从你的应用程序发送电子邮件。这样,如果由于开启了不太安全的应用程序访问,该帐户由于某种原因而受到威胁,攻击者将只能看到来自 scraper 的已发送邮件。
最初发表于【www.marknagelberg.com】。你可以在推特上关注我 这里 。要访问我共享的 Anki deck 和 Roam Research notes 知识库,以及关于间隔重复和提高学习效率的技巧和想法的定期更新, 加入“下载马克的大脑”。
用 Docker 和 Golang 设置 GitHub 包注册表
注:此文最初发布于martinheinz . dev
通常,对于任何编程语言,要运行您的应用程序,您都需要创建某种包(npm表示 JavaScript ,NuGet表示 C# ,...)然后存放在某个地方。在 Docker 的情况下,人们通常只是将他们的图像扔进 Docker Hub 中,但是我们现在有了新的选择...
另一个选择是GitHub Package Registry——它已经测试了一段时间,似乎越来越多的人开始使用它,所以感觉是时候探索它的功能了,这里专门针对 Docker 和 Go 项目。
这篇文章是系列文章“你的下一个 Golang 项目所需要的一切”的一部分,如果这听起来很有趣,请点击这里查看上一部分。
注意:本文适用于任何使用 docker 图像的项目,而不仅仅是 Golang。

为什么使用 GitHub 注册表
首先,你为什么要考虑从,比方说, Docker Hub 或任何其他注册表,切换到 GitHub 包注册表:
- 如果你已经在使用 GitHub 作为你的 SCM,那么使用 GitHub Package Registry 是有意义的,因为它允许你把所有东西放在一个地方,而不是把你的包推到别处。
- GitHub — 动作还有另一个闪亮的新( beta )特性,你可以结合 GitHub 包注册表来利用它(在另一个帖子中有更多关于这个的内容……)。
- 即使我认为 Docker 图像优于例如
npm包,如果你更喜欢非 Docker 工件,你也可以将它们推送到 GitHub 包注册表。
我们开始吧!
那么,现在,让我们来看看如何使用它。让我们从构建和标记您的图像开始:
注意:如果你正在使用 Go,那么你可能想在这里查看 我的库 ,这里所有的 GitHub 包注册表功能都已经绑定到 Makefile 目标中。
为了能够将图像推送到 GitHub 包注册表,您需要使用上面显示的格式来命名它——这实际上只是带有您的 GitHub 用户名和存储库名称的注册表的 URL。
接下来,我们如何访问它?
首先,为了能够用 GitHub 包注册表认证我们自己,我们需要创建个人访问令牌。这个访问令牌必须有read:packages和write:packages作用域,此外,如果您有一个私有存储库,您还必须包括repo作用域。
在 GitHub 帮助网站上已经有非常好的关于如何创建个人令牌的指南,所以我不打算在这里复制和粘贴步骤。你可以在这里阅读
现在,我们有了个人令牌,让我们登录:
最后,是时候推广我们的形象了:
很明显,你也可以把图像拉出来:
在 CI/CD 渠道中使用它
对于 GitHub 包注册表,我们最不想做的事情就是将它与 CI/CD 工具集成在一起。我们来看看如何用 Travis 来完成(全 *.travis.yml* 可在我的资源库 这里 ):
正如你在上面看到的,你可以像在你的机器上一样运行build和push,唯一的区别是docker login。这里,我们使用在 Travis UI 中指定的环境变量。用户名通过-u参数传递给登录命令,密码使用echo传递给stdin,这是必需的,这样我们就不会在 Travis 日志中打印出我们个人的 GitHub 令牌。
那么,我们如何设置这些环境变量呢?这些是步骤:
- 导航到存储库的 Travis 作业设置,例如https://Travis-ci . com/Martin Heinz/go-project-blue print/settings
- 向下滚动到环境变量部分
- 将变量名分别设置为
DOCKER_USERNAME和DOCKER_PASSWORD。在密码( GitHub 令牌)情况下,确保构建日志中的显示值设置为假。 - 点击添加并触发构建
如果你没有使用 Travis 并且想要使用 GitHub Webhook 来触发构建,那么你可以使用[RegistryPackageEvent](https://developer.github.com/v3/activity/events/types/#registrypackageevent)。如果您正在使用 Jenkins 、 OpenShift 或 Kubernetes ,并且希望每次在 GitHub Package Registry 中发布或更新您的包时触发部署,这可能会很有用。
告诫的话
使用 GitHub 包注册表时,你要记住的一件事是,你不能删除你推送到注册表的包。这样你就不会破坏依赖于你的包的项目。你可以从 GitHub Support 请求删除包,但是你不应该指望他们真的删除任何东西。
结论
希望看完这个你给 GitHub 包注册表打个预防针。如果你还没有 beta 权限,可以在这里注册。如果你有任何问题,不要犹豫,联系我,或者你也可以看看我的库,在那里你可以找到 GitHub 包注册表用法的例子。
资源
- https://github.com/features/package-registry
- https://help . github . com/en/articles/about-github-package-registry
为 Python 中的数据可视化设置 Jupyter 笔记本
Jupyter 笔记本是在交互式环境中探索数据和创建可视化效果的一种非常好且相当简单的方式。

Here’s a simple example plot from Pandas in a notebook:
Jupyter,Python 和 Anaconda
我们将看到如何使用 Anaconda 设置 Jupyter 笔记本环境,如何创建笔记本,然后绘制第一个图形。
虽然用于创建情节的语言是 Python,但您并不需要真正了解编程,因为只需要非常有限的一套语言。你将需要学习 Pandas 和/或 Seaborn 中绘图命令的特定语法,而不是其他。在上面的例子中,大部分代码都是样板文件,这是完成工作的最后一行。
通过在程序员编辑器中编写 Python 程序并运行该程序,可以使用 Pandas 或 Seaborn。但我认为,在互动环境中制作精美图表的最简单方法是使用 Jupyter 笔记本。
因此,首先我们要设置 Jupyter 笔记本环境。
本指南不是针对经验丰富的程序员的,所以我将假设你的计算机上没有安装 Python,而是敦促你在你的计算机上安装 Anaconda 。
蟒蛇
Anaconda 是一个包,其中包含 Python 的实现以及一整套数据科学库。
下载和安装非常简单,一旦安装,你将从一个图形界面获得大量的数据科学和人工智能工具。这些工具包括 Python、Pandas 和 Jupyter,以及支持它们所需的所有库。
Anaconda 非常大——几千兆字节——但是由于大多数计算机都有几百千兆字节的存储空间,所以这对大多数人来说可能不是什么大事。对于那些空间不足的人(比如当我在 Windows 平板电脑上安装它时),有一个更小的替代方案,Miniconda,但这需要更多的工作——我将单独介绍这一点,但现在我们将考虑完整版本。
Anaconda 可用于 Windows、MacOS 和 Linux,有 32 或 64 位版本。它还有包含 Python 2.7 Python 3.x 的版本,我建议您选择适合您机器的最新 Python 3 版本。
首先,进入 Anaconda 下载页面,大约在中间你会看到你的操作系统,Windows,MacOS 或者 Linux 的链接。点击正确的选项,下载安装程序。

下载安装程序后,您可以在相应的安装页面上找到如何安装的详细说明。为了方便起见,我在这里列出了它们。
- 在 Windows 上安装 Anaconda
- 在 MacOS 上安装 Anaconda
- 在 Linux 上安装 Anaconda
然而,安装很简单。对于 Windows 和 Mac 用户,只需从网站下载合适的版本,然后像安装其他程序一样安装即可;不需要管理员权限。
对于 Linux 用户来说,正如你所料,你需要而不是使用 apt-get 来安装。下载安装程序,打开一个终端窗口,导航到下载位置,然后运行命令:
bash ./whatever-your-file-is-called.sh
在安装过程中,如果有选择的话,最好接受默认选项。一旦它开始运行,安装就可以留给它自己的设备了——这可能需要一段时间,取决于你的互联网连接和你的电脑的速度。
安装快结束时,会询问你是否要安装微软的 Visual Studio 代码。这是一个非常好的程序员编辑器,但是如果你决定不在这个阶段安装它,你可以在以后安装它。
一旦安装在 Windows 电脑或 Mac 电脑上,你应该会在开始菜单中发现各种新项目。其中一个将是 Anaconda Navigator(如上所示)。您还可以找到 Jupyter 笔记本和 Anaconda 提示符的条目。
在 Linux 上,您可能没有将这些条目添加到您的菜单中。如果是这种情况,只需打开一个终端窗口并键入命令:
anaconda-navigator
通过这种方式,您将获得 Anaconda GUI,从中可以启动 Jupyter 笔记本。
仅此而已。安装了 Anaconda 之后,您就可以开始使用 Jupyter 来制作出色的数据可视化了。
下一篇文章将是关于实际使用一个笔记本,并用熊猫制作第一个可视化。当它准备好了,我会在这里放一个链接。
巨蟒领航员
我将从 Anaconda Navigator GUI 开始,因为无论您使用的是 Windows、MacOS 还是 Linux,从这里启动笔记本都是一样的。
这是导航器屏幕:

如你所见,主窗口左上角的面板是为 Jupyter 笔记本设计的,只需点击启动按钮即可。(您的布局可能与我的不同,但 Jupyter 笔记本面板会在某个地方,如果安装了的话。)
但是在你这么做之前,你应该知道 Jupyter 运行在你的浏览器中。启动 Jupyter 将在您的默认浏览器中创建一个新的选项卡,其页面如下所示:

当然,由于这是我的 Windows 平板电脑上的主文件夹,你的屏幕看起来不会完全一样。但它可能会包含一些文件夹,你不希望与笔记本散落在一起。因此,您可能要做的第一件事就是为您的工作创建一个新目录。
所以,点击新建下拉菜单,选择文件夹。

你现在将有一个名为的新文件夹,无标题文件夹。向下滚动找到它,点击它旁边的复选框使它被选中,然后从屏幕顶部选择重命名并给它一个新名字。
这里有一个窍门。每次打开 Jupyter 时,您可能都想使用这个目录。如果像我一样,你有一大堆文件夹要向下滚动,你可能想选择一个名字,把它放在最上面。我把我的文件夹命名为“_ 笔记本”。下划线将它带到列表中的第二项,这样很容易看到和点击。
现在双击你的新文件夹,你会看到如下内容:

现在我们准备创建我们的第一个笔记本。再次点击新建,但这次选择 Python3。这将在您的浏览器中打开一个新选项卡,这是您的新笔记本,看起来像这样:

需要注意的导入点是左边有彩色条的字段。这是一个单元格。在单元格中,您可以编写文本或代码。默认值是 code,我们将在单元格中编写以下代码(我建议您从下面的文本中剪切并粘贴它):
import numpy as np, pandas as pd, matplotlib.pyplot as plt
这一行代码导入了必要的库,这些库将允许我们对数据进行基本的可视化。我们正在导入三个库, numpy ,它为大型多维数组提供数值支持, pandas ,它提供更多的数据结构和数据分析工具, matplotlib ,它提供一个 2D 绘图库,pandas 使用它来生成各种格式的图表。
现在,单击+图标创建另一个单元格,并将以下内容写入其中(同样,我建议您剪切并粘贴该文本):
data = pd.Series([0,1,2,3,4,5,6,7,8,9])
data.plot()
您应该会得到这样的结果:

现在,如果你是一名程序员,你会知道什么是变量,并且会识别出数据是变量。如果你不是程序员,那么你只需要明白一个变量基本上是一个可以容纳一个值的东西。例如,我们可以有一个名为 x 的变量,并赋予它值 5;然后,我们可以在各种操作中使用名称 x ,它将表示值 5。您可以将其视为使用该值的捷径。
在我称之为数据的变量的例子中,它的值比一个单一的数字要复杂得多;它实际上是一系列数字(我们使用 pandas 库来创建包含该系列的数据结构)。使用 pandas,我们可以创建各种数据结构,本质上是各种形式的数据表,在这种情况下,我们的一系列数字就像表中的一行数字。
因此,我们第二个单元格的第一行将值 0,1,2,3,4,5,6,7,8,9 赋予变量数据。
现在我们可以调用熊猫图书馆的魔力了。
该单元格的第二行基于数据中的一系列数字生成一个图。
让我们试一试。
首先选择第一个单元格,然后单击看起来像“播放”按钮的图标。这将执行该单元格中的代码。不会发生什么事情,尽管您应该注意到星号已经变成了一个数字。(星号表示尚未执行的单元格)。然而,执行这个单元已经导入了我们需要的库。
现在选择第二个单元格,并单击播放图标。这一次,更令人兴奋的事情发生了。
您将在单元格中看到此图像。

当然,这是一个简单的数据折线图,正如我们所料,结果是一个直线图。
我们已经看到了如何安装 Anaconda 和 Jupyter 笔记本,以及如何创建第一个数据可视化。当然,还有更多的东西有待发现:如何导入数据集,以及如何创建不同类型的可视化,不仅仅是折线图,还有散点图、条形图和饼状图等。
你可以在这里看到一篇关于用 Python 和 Pandas 绘制各种图形的文章:
如果您是一名初露头角的数据科学家或数据记者,能够可视化您的数据会让您有能力…
towardsdatascience.com](/plotting-with-pandas-an-introduction-to-data-visualization-8bd7c0831650)
和其他文章:
如今,Python 无疑是一门值得学习的语言。如果你是编程新手,想要一个快速指南…
alanjones.pythonanywhere.com](http://alanjones.pythonanywhere.com/)
在基于 Debian 的 Linux 中设置 PostgreSQL

Linux Mint, PostgreSQL, Ubuntu
在尝试在我的 Linux 机器上安装 PostgreSQL 服务器来练习 SQL 命令的过程中,我遇到了一些困难,所以我决定为我未来的自己编写这个教程,以防我需要再次安装。在安装了基本系统之后,我还能够从一个小的测试数据库中获得一个转储,以供练习之用…所以我还包含了我用来加载这个数据库文件的命令。
注:这个过程是在 2019 年 11 月左右在 Linux Mint 19 Tara 和 Ubuntu 18.04 上测试的。
下面的许多步骤都是从这篇不错的 Wixel 帖子中获得的:https://wixelhq . com/blog/how-to-install-PostgreSQL-on-Ubuntu-remote-access
准备和安装
Ubuntu 和 Mint(以及其他)的默认库有 Postgres 包,所以我们可以使用 apt 来安装。
$ sudo apt update$ sudo apt install postgresql postgresql-contrib
角色设置
postgres 的安装会创建一个用户 postgres,该用户可以访问默认的 Postgres 数据库。通过首先切换到 postgres 用户并运行psql来访问它。
$ sudo -i -u postgrespostgres@server:~$ psql
您现在可以直接访问默认的 postgres 数据库。注意“\q”允许您退出 Postgres 提示符。
postgres=# \q
现在您已经确认了 Postgres 的功能,是时候设置角色了。仍然在 postgres 帐户中,执行以下命令。
postgres@server:~$ createuser --interactive
这将引导您完成设置新用户的过程。关于其他标志的更多细节可以在createuser手册页中找到。
注意:首先创建一个与您的用户帐户同名的角色作为超级用户可能会很有用。
现在运行psql(作为与您刚刚创建的角色同名的用户名)将导致一个错误。虽然您已经创建了一个角色,但是每个角色的默认数据库都是与该角色同名的数据库。因此,有必要提供您希望连接的数据库的名称,或者创建一个相应的数据库,这将在下面的部分中讨论。
访问特定数据库
假设您的角色(您的用户名)已经被授权访问一个数据库{dbname},您可以简单地从您的命令提示符下运行以下命令。
$ psql {dbname}
您也可以指定一个角色{role}而不是您的用户名,如下所示。
$ psql -U {role} {dbname}
创建新数据库
作为 postgres 用户,执行以下命令。注意:将{dbname}替换为所需数据库的名称,通常是上面创建的角色的名称。
postgres@server:~$ createdb {dbname}
加载数据库
如果您得到了一个数据库的转储,这里用{filename}.sql表示,下面将在 PostgreSQL 服务器中创建数据库,并将文件数据加载到数据库中。注意:将{username}替换为 PostgreSQL 服务器中现有的角色,将{dbname}替换为 PostgreSQL 服务器中现有数据库的名称,将{filename}替换为。sql 文件。
$ createdb -U {username} {dbname}$ psql {dbname} < {filename}.sql
现在你已经准备好SELECT something FROM your_tables了。
PSQLing 快乐!
为机器学习项目设置 Python 平台
关于如何开始使用 python 进行机器学习,有很多深入的教程。这些教程主要关注深度学习框架的使用(比如 TensorFlow,PyTorch,Keras 等。)比如如何设置基本的监督学习问题,或者如何创建一个简单的神经网络并训练它等等。但是,即使在开始尝试这些教程之前,主机上也必须有一个可以进行实践的 python 平台。
几年前,当我开始使用 ML 时,我找不到一个关于如何在我的机器上建立一个工作平台的好教程。有很多选项/配置可供选择,我无法决定选择哪一个。在我决定一个配置之前,我必须浏览很多网页,花了我大约 2 周的时间,最终有了一个工作平台。几个月前,我的一位同事面临着同样的问题,所以我帮助她搭建了平台,为她节省了不少时间。因此我决定写一篇关于它的详细文章。
我将这篇文章分为两部分,将分别关注它们
- 第 1 部分:选择并安装 python 发行版和 IDE
- 第 2 部分:创建新项目所需的步骤
第 1 部分— Python 发行版和 IDE
在本文中,我将使用下面的 Python 发行版和 IDE。
- 蟒蛇分布:蟒蛇
- Python IDE: PyCharm
W 什么是蟒蛇?Anaconda 是 Python 和 R 语言发行版,具有简化的包管理和部署。使用 anaconda,可以更容易地拥有不同配置的多个 python 环境,并在它们之间切换。anaconda 包管理器使得解决不同包所需的包的多个版本之间的冲突变得更加容易。关于使用 Anaconda 的利弊的详细解释可以在这里找到
下载&安装: Anaconda 发行版可以从这里下载。安装说明非常简单。
W 帽子是皮查姆?PyCharm 是 python 可用的众多 ide 之一。我更喜欢 PyCharm,因为与其他 ide 相比,它更加用户友好、功能强大且可配置。它提供了与 git 的集成,拥有自己的终端和 python 控制台,提供了对各种便利插件的支持,以及大量有用的键盘快捷键。
下载&安装:要下载 PyCharm,请点击此链接并下载最新的社区(免费)版本,然后按照安装说明进行操作
第 2 部分—创建新项目所需的步骤
您将要处理的不同项目将需要不同的资源和具有不同版本需求的包。因此,总是建议您为每个项目使用单独的虚拟 python 环境。这也确保了您不会意外地用其他版本覆盖某些包的任何现有工作版本,从而使它对您当前的项目无用。
以下是创建新项目时应该采取的步骤。
步骤 1:创建一个新的 Anaconda 虚拟环境:
打开 Anaconda 提示符命令并键入
conda create -n myenv python==3.5
这将创建一个名为 myenv 的新虚拟环境,它将随 python 版本 3.5 一起安装和加载
创建环境后,您可以通过使用以下命令激活环境来验证它
conda activate myenv #For Windows
source activate myenv #For MAC OS
您可以通过键入以下命令找到创建的 env myenv 的位置
which python
# The output will look something like this
# /Users/aqeelanwar/anaconda/envs/myenv/bin/python
在步骤 2 中,我们将使用它来定位我们的环境
步骤 2:安装必要的软件包:
激活环境后,您可以使用
*#General Format:*
conda install package_name*#Example:*
conda install numpy #To install numpy
步骤 3:有用的 Conda 命令:
以下是有用的 conda 命令,在管理 conda 环境时会很方便
# Activate an environment
conda activate env_name# Deactivate an environment
deactivate #Windows
source deactivate #Linux and macOS# Listing all the created environments
conda env list# Listing packages install in an environment
conda list# Installing a package
conda install package_name# Cloning a conda environment
conda create --clone name_env_to_be_cloned --name name_cloned_env
步骤 4:在 PyCharm 上创建一个新项目
- 打开 PyCharm 并选择 Create New Project。

- 选择项目的位置和名称(在本例中为 medium_tutorial)。
- 展开项目解释器选项并选择现有的解释器
- 通过单击现有解释器下最右边的三个点来定位您的环境

- 此时,我们将使用步骤 1 中的 which python 命令显示的位置来定位我们的环境 myenv(如果它还没有出现在项目解释器列表中:稍后将详细介绍)。此外,我们希望这个环境可以用于我们将来创建的其他项目,因此我们将选择“对所有项目可用”复选框

- 点击确定,然后创建。

- 该项目现在将使用环境 myenv。您可以使用 PyCharm 的内置终端将软件包安装到这个环境中。
- 创建新的。py 文件(比如 main.py)并使用
run >> run main.py

步骤 5:在 conda 环境之间切换(可选)
如果将来你想在同一个项目的不同 conda 环境之间切换,你可以按照下面的步骤来做
- PyCharm 只能选择已经包含在其项目解释器列表中的环境
- 向项目解释器列表中添加一个新创建的(比如命名为 PyCaffe)环境
settings >> Project:project_name >> Project Interpreter

- 点击右上方的齿轮图标,并选择添加

- 挑选
Conda Environment > Existing environment > <three dots>
找到新创建的环境并点击 OK

- 现在环境已经被添加到项目解释器列表中,并且可以在下拉菜单中看到

- 此列表显示了现有的环境以及您选择的将用于项目的任何环境
- 注意: PyCharm 终端不会自动激活当前选择的环境。如果您已经从项目解释器列表中选择了 PyCaffe,env,现在想要在其中安装一个新的包,您必须首先在终端中激活环境,然后您可以使用 conda install package_name。否则,该软件包将安装在先前激活的 conda 环境中

现在您已经设置好了平台。此时,您可以安装所需的 ML 框架(TensorFlow、Keras、PyTorch)并开始尝试 ML 教程。
摘要
在本教程中,我们讨论了从事 ML 项目(或任何 python 项目)的先决条件。我们看到了如何使用 Anaconda 和 PyCharm 创建多个 python 环境并在它们之间切换。
如果你在学习本教程的过程中有任何问题,请在下面评论,我会给出解决方案。
如果这篇文章对你有帮助,欢迎鼓掌、分享和回复。如果想了解更多关于机器学习和数据科学的知识,请关注我@Aqeel an war或者在LinkedIn上与我联系。
为计算机视觉设置 Raspberry Pi(安装 OpenCV)

Photo by Harrison Broadbent on Unsplash
设置 Rasbian Noobs
Noobs 是 Raspbian 的一个易于安装的版本。 Raspbian 是基金会官方支持的操作系统。Raspbian 预装了大量用于教育、编程和一般用途的软件。它有 Python,Scratch,Sonic Pi,Java 等等。
注意:Rasbian 是一个基于 Linux 的操作系统。
注:这个过程可能看起来漫长而无聊,但这是一次性的事情。您也可以复制带引号的命令。
- 从https://www.raspberrypi.org/downloads/noobs/下载 zip 文件
- 确保解压缩文件夹,并将所有内容复制到 SD 卡(建议大于 4GB)。
- 通过将一个名为“SSH”的文件(不带任何扩展名)放入 SD 卡的引导分区(引导文件夹内),启用 SSH 。
- 使用手机充电线(非 C 型)给 raspberry pi 通电,并通过以太网将其连接到笔记本电脑。
第一次,我建议使用外部显示器、键盘和鼠标进行初始设置。通过ifconfig记下 pi 的 IP,然后继续通过 wifi/以太网 essentials。
然而,如果你没有初始外设,请浏览此视频:如何在没有显示器或键盘的情况下设置树莓派
获取连接到以太网的 Raspberry Pi 的 IP(Ubuntu)
- 启动 Raspberry Pi,并将以太网连接到 Pi 和笔记本电脑。
- 在你的笔记本上。转到编辑连接设置。
- 导航到 ipv4 选项。选择方法:“共享给其他计算机”。
- 连接到有线连接。然后打开命令提示符并键入命令
nano/var/lib/misc/dnsmasq . leases "。
你将会从那得到覆盆子 pi Ip。
5.然后打开命令提示符并键入:ssh pi@
在 Pi 上设置 OpenCV 3
宋承宪不会? 如果您在网络上看到您的 Pi,但无法对其进行 ssh,您可能需要启用 SSH。这可以通过 Raspberry Pi 桌面首选项菜单(你需要一根 HDMI 线和一个键盘/鼠标)或从 Pi 命令行运行 sudo 服务 ssh start 来轻松完成。
第一步是将文件系统扩展到包括 micro-SD 卡上的所有可用空间:
步骤 1: sudo raspi-config
步骤 2:接着选择“扩展文件系统”:
第三步:一旦出现提示,你要选择第一个选项,“A1。展开文件系统 , 回车 然后 结束
步骤 4:到终端键入“sudo reboot”
(可选)第二种方法是删除所有不必要的文件以清理空间
sudo apt-get 净化钨-发动机
sudo apt-get 清除图书馆*
sudo apt-get clean
sudo apt-get 自动删除
现在,在步骤 3 中,我们将安装所有需要的依赖项
sudo apt-get 更新&& sudo apt-get 升级
sudo apt-get 安装内部版本-基本 cmake pkg-config
sudo apt-get install libjpeg-dev libtiff 5-dev lib jasper-dev libpng 12-dev
sudo apt-get install libavcodec-dev libavformat-dev libswscale-dev libv4l-dev
sudo apt-get 安装程序
sudo apt-get 安装 libgtk2.0-dev libgtk-3-dev
sudo apt-get install lib atlas-base-dev gfortran
为 python 3 和 python 2 安装 OpenCV
安装 python2.7-dev python3-dev
现在,对于第四步,我们将下载 opencv 源代码,然后为 pi 构建它。
CD ~ & & wget-O opencv.ziphttps://github.com/Itseez/opencv/archive/3.3.0.zip&&解压 opencv . zip
(可选)安装 opencv-contrib 库
wget-O opencv_contrib.ziphttps://github.com/Itseez/opencv_contrib/archive/3.3.0.zip&&解压 opencv _ contrib . zip
现在,在第 5 步中,我们将为 python 和
wgethttps://bootstrap.pypa.io/get-pip.py&&须藤 python get-pip.py & &须藤 python3 get-pip.py
大多数情况下,你通过这个博客在单板计算机上设置你的深度学习模块,如 Raspberry Pi。因此,我们将公开 opencv 安装,以便在所有虚拟环境中访问。如果您想使用 envwrapper 或类似工具构建自己的虚拟环境,请激活并继续。
安装 numpy
pip 安装数量
现在,我们将编译并安装 opencv。这个过程需要时间,取决于 pi 模型。在我的 B+ plus 模型中,大约花了 1.5 小时。所以去喝杯咖啡吧!!
cd ~/opencv-3.3.0/ && mkdir 内部版本&& cd 内部版本
CMAKE-D CMAKE _ BUILD _ TYPE = RELEASE -D CMAKE _ INSTALL _ PREFIX =/usr/local -D INSTALL _ PYTHON _ EXAMPLES = ON -D OPENCV _ EXTRA _ MODULES _ PATH = ~/OPENCV _ contrib-3 . 3 . 0/MODULES -D BUILD _ EXAMPLES = ON..
如果您使用的是 python3,请确保您的 python 解释器和库设置在 python 3 或 python 3.5 文件夹中(或 python2 的 python2.7 文件夹中)
在开始编译过程之前,您应该增加您的交换空间大小。这使得 OpenCV 能够用树莓 PI 的所有四个内核进行编译,而不会因为内存问题而导致编译挂起。键入终端
sudo nano /etc/dphys-swapfile
然后将 CONF _ 交换大小编辑为
CONF _ 交换大小=1024
现在,我们将重新启动交换空间。
sudo/etc/init . d/d phys-交换文件停止
sudo/etc/init . d/d phys-swap file start
现在,我们跳到安装 opencv 的最后阶段。我们现在将编译 opencv。
品牌-j4
现在,你需要做的就是在你的 Raspberry Pi 3 上安装 OpenCV 3
sudo make 安装
sudo ldconfig
Bug 修复:老实说不知道为什么,也许是 CMake 脚本中的 Bug,但是在为 Python 3+编译 OpenCV 3 绑定时,输出。所以文件被命名为 cv2 . cpython-35m-arm-Linux-gnueabihf . so(或其变体),而不是简单的 cv2.so(就像在 Python 2.7 绑定中一样)。同样,我不确定为什么会发生这种情况,但这很容易解决。我们需要做的就是重命名文件:
对于 python3
CD/usr/local/lib/python 3.5/site-packages/
sudo mv cv2 . cpython-35m-arm-Linux-gnueabihf . so cv2 . so
cd ~/。virtualenvs/cv/lib/python 3.5/site-packages/
ln-s/usr/local/lib/python 3.5/site-packages/cv2 . so cv2 . so
通过打开 python bash 和来测试安装
导入 cv2
现在,我们将通过删除安装文件来释放空间,并将 pi 恢复到它原来的交换空间。
RM-RF opencv-3 . 3 . 0 opencv _ contrib-3 . 3 . 0
sudo nano /etc/dphys-swapfile
然后将 CONF _ 交换大小编辑为
CONF _ 交换大小=1024
现在,我们将重新启动交换空间。
sudo/etc/init . d/d phys-交换文件停止
sudo/etc/init . d/d phys-swap file start
恭喜你,你现在完成了。享受计算机视觉!!
为 Golang API 设置 Swagger 文档
注:此文最初发布于martinheinz . dev
在之前的文章中——在 Golang 中构建 RESTful API——我们在 Golang 中创建了 RESTful API。现在,我们已经准备好使用我们的项目,我们应该向我们的用户展示如何这样做,否则——如果他们不能测试它和查看它的特性——他们甚至不会碰它。
如果没有我们的 API 的有意义的文档以及测试其端点的能力,用户甚至不会尝试使用它。解决方法是写文档。然而,编写它可能要花很多时间,而这些时间本来可以用来为我们的应用程序开发更酷的功能…那么,我们该怎么办呢?—我们生成大摇大摆的单据!

图书馆
让我们从创建 Swagger 文档所需的库开始。我说的是库,但实际上只需要一个— swag —它是将代码注释(注释)转换为SwaggerDocumentation 2.0 的 Golang 库。除了 swag 之外,你还需要一个中间件/包装器库用于你的 web 框架。在 swag 文档中有到支持框架的库的链接,其中既包括很多人喜欢使用的最简单的选项net/http,也包括我使用的 GIN ,我将在这里展示它。即使您可能使用不同的 web 框架,注释也是一样的,所以无论如何您都可以在这里学到一些东西。
还值得一提的是,还有一个替代的 Golang Swagger 库— go-swagger似乎更受欢迎,也更强大一些。然而,我个人更喜欢swaggo/swag,因为它简单。如果您需要对生成的内容有更多的控制,您可能希望切换到go-swagger。
文档字符串
现在,对于注释/注释/文档字符串或任何你想叫它的东西。它实际上只是特定 API 函数之前的一堆注释,用于生成 Swagger 文档。
在我们开始描述单个 API 端点之前,我们需要首先为整个项目写一个总体描述。这部分注释位于您的main包中,就在main函数之前:
注意:下面所有的例子都来自我的仓库 这里 ,在这里你可以找到包含 Swagger UI/Docs 的 runnable 应用。
上面你可以看到通用 API 信息的例子,包括名称、版本、许可证、基本 URL 等。您可以包括的字段很少,它们在这里列出和一些示例。
除了注释之外,我们还需要导入必要的库,包括我们必须生成的docs包的空白导入(稍后会详细介绍)。我们需要做的另一件事是在某个端点实际安装 Swagger UI ,这里我们使用"/swagger/*any。
这是 UI 的一部分,上面的注释会产生:

现在是重要的部分 API 函数的注释。这些注释在main中连接的每个函数之前,以服务于某个端点,所以当我们服务于像v1.GET("/users/:id", apis.GetUser)这样的端点时,我们需要这样注释它:
大多数注释都是不言自明的,这是您应该包含的最小注释集。我想强调的一点是成功时返回的models.User——这是一个存在于models包中的数据库表模型。通过这样引用它,我们使它出现在模型部分的 Swagger UI 中:

这是我们为我们的终点得到的部分:

生成!
最后,是生成文档的时候了!你所需要的只是一个命令— swag init,这个命令需要从main所在的目录运行,所以对于我做的蓝图库,应该是.../cmd/blueprint/。这个命令将创建名为docs的包,其中包含了我们文档的 JSON 和 YAML 版本。
尽管这个包已经生成,但我更喜欢将它存储在 GitHub 中,因为它是在main包中导入的,因此它是应用程序运行所必需的。如果您想避免将生成的代码推送到 GitHub 中,您可以编写一个 Makefile 目标,在应用程序构建和运行之前,动态地重新生成 Swagger 文档。然而,如果你决定推它 GitHub ,你可能想要通过go fmt运行文档,因为它不一定被格式化“正如它应该的那样”。
证明
在这一点上,我们可以运行应用程序,看看我们漂亮的新 Swagger UI ,然后就到此为止。但是,缺少的一点是 API 的认证。如果您让 Swagger UI 未经认证,那么任何人都可以访问他们想要的任何端点,这可能是非常不可取的,例如,如果您的数据可能被用户损坏。更糟糕的是,您可能会将数据库中的敏感信息暴露给整个互联网。我认为这些是为我们的 API 设置一些简单认证的足够的理由,因此,也是 Swagger UI ,那么我们如何做呢?
首先,我们需要实际实现身份验证。这里是 GIN 的情况,我们创建了一个非常简单的认证中间件,我们将它附加到路由器组:
通过将中间件附加到特定的组,我们可以控制什么是认证的,什么是不认证的,这很重要,因为我们不希望 Swagger UI 本身被认证。
注意:我省略了一些代码,以使例子可读性更好,更容易理解。完整代码,请参见资源库中的 *rest-api* 分支 此处 。
在main模块中,我们需要改变的另一件事是注释——更具体地说,我们需要添加securityDefinitions注释:
这个注释——您已经猜到了——通过Authorization头向 Swagger UI 添加了 API 密钥认证。除了 API 密钥认证之外,您还可以选择使用用户名和密码的基本认证(securitydefinitions.basic)或某个版本的 OAuth2 ( securitydefinitions.oauth2),所有选项都显示在文档这里。我个人喜欢使用 API 密匙,因为在我看来这是简单和最方便的选择。
为了让 Swagger 识别某个端点已经过身份验证,我们还需要向所述 API 函数添加安全注释:
这是最后一步,现在(在重新生成 Swagger docs 之后)我们终于可以运行我们的应用程序了:
您应该在 GIN 日志中看到类似这样的内容:
我们现在可以在http://localhost:1234/Swagger/index . html打开 Swagger UI 并测试我们的文档!
注意:如果上面的命令对你来说没有多大意义,请考虑在这里 通读之前的 。
因此,简单重述一下——main包中的安全定义为我们提供了以下模型:

然后,如果我们输入不正确的 API 密钥("wrong_api_key"),我们会得到 401 响应代码:

如果我们使用正确的 API 密钥,我们将返回 200 所请求的数据:

同样重要的是要提到,像我们在这里所做的那样以明文形式发送授权头是不安全的,并且违背了认证 API 的全部目的,所以在实际应用中,你应该明确地使用 HTTPS。
结论
读完这篇文章后,我希望你现在知道,如何着手为你的 API 建立 Swagger 文档,我也希望你能为你的下一个项目写一些文档,因为它非常简单,而且好的 API 文档对你(手动测试)和应用程序的用户都有很大的价值。您可以在我的存储库的rest-api分支这里检查全部代码,如果您有任何问题或改进,请随时联系我或在存储库中创建问题/请求。🙂
创业时制定数据策略
如何通过数据实现你的愿景
你可能在任何地方都听说过你需要一个数据策略。大数据、数字化转型和人工智能等流行词汇已经渗透到你的公司,似乎除了你之外,你的所有竞争对手都知道他们需要做什么。作为一个在这些术语和行话的另一边工作过的人,我想分享一下对这个话题的不同看法,以及我在一个初创环境中建立数据策略的经验。
像生活中的许多其他事情一样,一小撮离群值会扭曲我们对某些话题的看法。例如,我们钦佩成功的独角兽,但我们错过了太多失败的初创企业。数据分析也没有什么不同,只是因为我们看到了像亚马逊的推荐引擎和谷歌的搜索算法这样的英雄例子,并不意味着每个数据分析用例都会价值数十亿。不要误解我,我毫不怀疑数据是并将继续是任何公司的重要驱动力,尤其是初创公司,但我认为我们需要重新设定我们的预期,并以不同的方式处理数据和分析。

Photo by Johannes Plenio on Unsplash
定义数据策略
一项战略的价值来自于它执行的好坏。战略如果不能执行,只是一个梦想。当您定义数据分析策略时尤其如此,因为这是一个相对较新且不断变化的领域,存在许多模糊性,使得执行很困难。因此,让我们来看看一项战略的不同组成部分,看看我们应该注意什么。
您试图解决的问题
你的策略的出发点应该是你试图解决的问题。数据是一种资源,它使您能够找到解决方案,但它不是解决方案本身。把它想象成金钱,金钱是你到达目的地所需要的资源,但它不是目的地本身。如果你发现自己在根据数据改造一个商业案例,那么你的做法是错误的,你应该后退一步,先想想你的问题。
你如何衡量它?
一旦问题被定义,你需要想出如何测量它。如果你能直接量化和衡量你的目标,这很好,但关键是要有一个可衡量的目标,作为整个战略实施过程中的路标。虽然它可能只是一个指标,但这个指标对日常决策有很大的影响。例如,如果目标是注册人数,那么为了提高你衡量的关键指标,很容易忽略客户参与度或获取成本,所以明智地选择你的指标。
长期与短期交付
一个全面的战略需要长期和短期的里程碑来保持团队的参与。将你的计划分成更小的独立组件要简单得多,每个组件解决更大问题的一部分。这使你能够更快地将你的产品提供给你的客户,甚至可能收到引导你前进的重要反馈。有时在基础层上投资是不可避免的,但是要确保认可这些努力,即使最终客户可能看不到它们。人们如何管理通往长期目标的旅程是区分成功人士和失败人士的关键。
人员和资源
您的人员和资源是您战略中最重要的部分,因为它们将是您成功的关键驱动因素。组建数据团队没有现成的方法,因为这取决于你正在开发的产品、你试图解决的问题以及你已经拥有的现有能力。但是作为一个总的指导方针,你需要在具有分析和工程背景的人之间取得平衡。以多面手为目标,当你开始时,他们可以站在围栏的两边,这样你可以保持你的团队规模小,但仍然有足够的覆盖面来填补所有需要的角色。虽然快速扩大团队规模很诱人,但在你开始展示价值之前,你应该保持小规模,这将降低你的风险,给你一个更好的成功机会。
数据是最重要的资源,在这个数据丰富的世界里,它可能是一件好事,也可能是一件坏事。大量的数据意味着你可以更容易地获得数据,特别是如果你聪明的话。但是,更多并不一定意味着更好,更多的数据有时只是意味着当你试图在其中找到针时,你的干草堆变得更大了。重要的是要有一个数据获取计划,详细说明你将需要什么数据,它可以带来的价值,以及你如何获得它。
行动计划
一旦你想通了上面所有的组成部分,你需要把它们放在一起,并为你的第一个或两个可交付成果制定一个计划。值得注意的是,你的计划会出现偏差,你需要在这个过程中进行调整,所以不要过于珍惜建立一个完美的计划。价值在于规划过程,因为它将帮助您规划出需要采取的关键步骤,并就如何将愿景转化为行动与团队保持一致。
创业公司呢?
虽然建立数据战略的高级流程对任何组织都是相似的,但在实施数据战略时,初创公司有一些独特的优势和劣势。
就缺点而言,
- 创业公司通常不会从大量数据开始,因为客户群很小(但有望增长)。这在一定程度上限制了您可以对数据进行的分析类型,因为人口规模较小且数据集中的可变性较高。
- 从新客户那里收集数据并不像听起来那么简单。随着关于数据被滥用的负面新闻越来越多,客户对如何处理他们的数据更加谨慎(理应如此)。但作为一家与客户没有任何关系的初创企业,你需要付出额外的努力来建立这种信任。以道德和透明的方式运营将有助于建立信任关系,您的客户会很乐意与您分享他们的数据。
另一方面,在优势方面
- 缺乏遗留数据和系统是初创企业可以弥补损失的地方。数据量在广度和深度上都呈指数级增长,旧技术很难跟上。初创企业有更好的机会建立灵活的系统,更有效地利用数据。
- 当谈到快速移动时,小是很棒的。鉴于数据发展的速度,能够快速部署是成功的关键,在实施见解和比大型企业更快上市方面,初创企业具有优势。快速运输的文化是初创企业的真正资产。
我的个人经历
建立数据策略对我来说是一次有趣而又富有挑战性的经历,我相信那些做过同样事情的人也会分享类似的故事。以下是我从迄今为止的经历中学到的一些关键的东西,我希望它们能给你一个好的开始。
从上到下对齐
为了拥有一个成功的数据战略,您需要让所有相关的利益相关者参与进来,从最高层的决策者到最基层的员工。没有太多的一致性,所以确保让每个人都参与进来,并让他们参与进来。建立数据策略是一个旅程,在这个过程中你需要团队的支持。确保你在正确的细节层次上交流,以消除任何歧义。写文件,画模型,如果需要的话,开研讨会,关键是把你的抽象计划变成具体的成果。
从简单开始展示价值
将数据转化为价值是困难的,尽管使用新的闪亮的分析工具包很酷,但快速展示价值也很重要。了解您在分析成熟度模型上的现状并解决正确的项目是获得快速成功的最佳方式。我发现深入研究关键指标是一个很好的起点,因为它开启了与不同利益相关者的对话,并有助于确定什么是重要的。这也是一个很好的方法,通过客观地看数字来识别和挑战人们一直以来所做的任何假设。
设定正确的期望
数据科学本质上是迭代和实验性的,当涉及到测量产出时,这并不能很好地实现。作为一个倡导在你的企业中建立数据战略和团队的人,你需要预先设定期望。通过保持对目标的正确关注,失败的实验不是浪费时间和资源,而是离找到正确的解决方案和良好的学习体验更近了一步。
投资于您的员工
数据科学与工程是一个竞争激烈的市场,找到完美的团队并不容易,但没有找到完美的团队这样的事情,因为他们是建立起来的。所以,要专注于雇佣那些有合适的潜力、态度和文化契合度,并且相信你的目标的人才。有了合适的环境,你就可以组建一个团队,将你的战略付诸实施。
定义数据分析策略并实施它只是初创企业面临的众多挑战之一。成功的关键在于专注于你的目标,并找到利用你的数据达到目标的最佳方式。会有很多意想不到的失败和教训,但只要你坚持下去,做出明智的决定,你就能从你的数据中获得价值。
如果您喜欢我的内容,请在 https://jchoi.solutions/subscribe注册更多内容
使用 Visual Studio 代码和 Anaconda (Python)建立自己的数据科学工作空间

如果你刚开始涉足数据科学领域,创建一个个人工作空间将有助于你组织好你的项目。有很多不同的工具可以使用,如果你和我一样,刚开始在这个领域工作,你可能会发现一头扎进去会让人畏缩。在这篇文章中,我将向你展示如何用业内最常用的工具在 macOS 上建立自己的工作空间。虽然我在本指南中使用的是 macOS,但对于 Windows 平台,步骤几乎是相同的,我希望您在 Windows 上使用本指南应该没有问题。完成本指南后,您将能够:
- 使用 Anaconda 设置 Python 环境。
- 创建 Visual Studio 代码工作空间并运行 Python 脚本。
- 安装包和管理不同的 Anaconda 环境。
让我们开始吧!
使用 Anaconda 设置 Python 环境
Anaconda 是 Python 和 R 的免费发行版,主要用于数据科学、机器学习、数据处理、分析等应用。它允许您管理您自己的环境和包,您将在您的项目中使用它们。我们将使用 Anaconda 进行包管理和部署。为了构建我们的工作空间,我们需要安装和配置 Anaconda。我们将遵循以下步骤:
- 安装 Anaconda。
- 使用最新版本的 python 创建一个新环境。
- 将软件包安装到您的环境中。
下载并安装 Anaconda
访问 Anaconda 网站,并为您的平台下载 Anaconda 的最新版本。您将能够下载 Python 3.7 和 Python 2.7 版本的 Anaconda。尽管我个人更喜欢获得最新版本的 Python,但有时 Python 包需要特定版本的 Python——因此我将向您展示如何设置多个 Python 环境。为了保险起见,我们现在将使用 Python 3.7。安装 Anaconda 后,启动 Anaconda Navigator。
在 Anaconda Navigator 中管理不同的环境
打开 Anaconda Navigator 后,您会看到 Anaconda 已经设置了一些工具和包,如 Jupyter、Spyder。注意这里有一个安装 VS 代码的选项。
点击安装。

The Anaconda Navigator installation comes with default packages preinstalled.
同时,导航到左侧菜单中的环境。在这里,您将看到 Anaconda 已经为您配置了一个现成的基本(根)环境。

The Anaconda Navigator interface showing you the packages installed on the base (root) environment.
在这里,您可以选择想要运行的环境。您也可以从终端直接运行 bash 命令。

Directly open Terminal with our without Python via Anaconda Navigator.
已经为你安装了很多包,但是如果你想安装新的包呢?或者你想为不同版本的 Python 安装不同的包?这就是为什么你想和环境打交道。注意,在上面的截图中,我已经建立了一个像这样的环境,名为 python37。
- 点击创建并配置一个新的 Python 环境。
- 选择 Python 的最新版本,并再次点击创建进行确认。

Anaconda serves R purposes too, but we’ll use Python in this case.
配置环境需要几秒钟的时间。几分钟后,您会注意到一个新的环境已经安装了一些默认的包。一旦设置好 Python 环境,您将主要使用终端在运行中安装软件包(稍后我将向您展示这一点),并且您可能很少使用 Anaconda Navigator。
太好了!从本质上讲,这是在您的机器上开始使用 Python 和 Anaconda 所需要的。现在我们只需要一个工作空间来实际使用我们的 Python 环境,这样我们就可以用我们的包运行我们的脚本了。你可以使用 Anaconda 附带的 Jupyter Notebook,但是我喜欢使用 VS 代码,我会很快解释为什么。
用 VS 代码创建您的数据科学工作空间
Visual Studio Code 是一个免费的代码编辑器,您可以根据自己的需要进行定制。使用像 VS 代码的 Python 扩展、GitHub 和其他有用的包,它是一个轻量级的 IDE,为在您自己的定制工作空间中运行 Python 提供了极好的支持。在前一章中,我们已经设置了 Anaconda 并安装了 VS 代码。
打开VS 代码。

The welcome screen on your first start of Visual Studio Code.
Visual Studio 代码是一个强大的轻量级代码编辑器,允许您为每个项目配置自己的工作区。出于测试目的,我创建了一个名为 DataScienceProject 的虚拟文件夹。
- 点击打开文件夹并选择文件夹。
- 进入菜单,选择文件>将工作空间另存为
- 将您的工作区文件保存在文件夹中
现在,您已经在 VS 代码中设置了一个定制工作区。工作区的伟大之处在于,您可以为每个单独的工作区定制设置。
- 现在,在您的工作区中创建一个名为 helloworld.py 的新文件。
- 打开 helloworld.py。
- 将下面的代码复制到您的文件中并保存。
#%%
# This is the first cell of our Python code
print('Hello world!')#%%
# This is another cell
print('Another cell for the world to see!')
在这个时候,当你打开你的文件时,你可能会得到各种各样的消息,比如“Pylint package not installed”。这是因为 VS 代码会自动识别你正在编辑一个 Python 文件。我们一会儿将进入包,但是首先让我们看看我们是否能运行我们的 Python 文件。您可以直接在终端或交互式 Python 窗口中运行它。交互式 Python 窗口非常有用,因为它为您调试代码提供了更多的反馈,而且还允许您在 Python 脚本中运行称为单元的不同代码。
要运行您的脚本,请按下 shift-enter。您也可以右键单击该文件,然后选择“在终端中运行 Python 文件”或“在交互式 Python 窗口中运行 Python 文件”。
运行第一个脚本后,您应该会在代码右侧看到交互式 Python 窗口,并返回如下内容。
[1] # This is the first cell of our Python code...Hello world![2] # This is another cell...Another cell for the world to see!
恭喜你!您刚刚在 Visual Studio 代码中设置了一个工作区来运行 Python 项目!现在让我们再深入一点,看看我们能否在我们的环境中安装新的软件包。
从您的终端管理包
现在我们已经运行了第一个脚本,您可能想要添加一个新的包。假设您的项目要求您连接到一个 Google API。Google 为我们提供了一个包来完成这项工作,但是这些并没有安装在您的默认环境中。幸运的是,我们有很多选择。Anaconda 有自己的包存储库,还有更多的存储库供我们查找我们的包。我们在示例中寻找的包是 Google API Python 客户端。继续并遵循这些步骤。
打开终端。确保您在基本环境中工作。终端应该通过显示如下内容来告诉我们这一点:
(base) myMac:DataScienceProject myUser$
通过在终端中输入以下命令,检查软件包是否已经安装:
conda list
这将返回当前安装在您的基本(根)环境中的软件包列表。现在,通过在终端中运行以下命令来安装软件包:
conda install -c conda-forge google-api-python-client
该软件包现在将安装在您的基础环境中。如果一切顺利,您应该在终端中看到以下消息。我没有复制所有的信息,但这应该给你一个想法。
Collecting package metadata (current_repodata.json): done
Solving environment: done## Package Plan ##environment location: /Users/myUser/anaconda3added / updated specs:
- google-api-python-clientThe following packages will be downloaded...
...
...Proceed ([y]/n)? y...Preparing transaction: done
Verifying transaction: done
Executing transaction: done
厉害!我们已经成功地在我们的环境中安装了一个新的软件包。这将允许您导入包库,并从脚本中使用 Google API Python 客户端。
但是,如果您已经有一个包在基本环境中运行,并且您不想冒险弄乱您当前的环境设置,该怎么办呢?您可以使用新的环境,并为该环境安装不同的软件包。我们现在知道了如何安装一个包,但是让我向您展示如何从 VS 代码中改变您的 Python 环境。
在工作空间中管理 Python 环境
除了在您自己的定制工作区中工作之外,您还可以在编辑器中管理您的 Anaconda 环境。这样,您就不必一遍又一遍地运行 Anaconda Navigator,而只需直接在编辑器外运行 Python 环境,这样您就可以继续编码了。
你注意到编辑器底部的蓝色条了吗?这个条给你关于你正在工作的代码的信息。在工具栏的最左边,您可以看到您当前正在使用的解释器。在我的例子中,它使用:
Python 3.7.3 64-bit ('base':conda)
正如您所看到的,我正在 Anaconda 的基本(根)环境中运行 Python 3.7.3。它还显示您的代码中是否有任何问题,有多少行、列、空格,您当前选择了哪种编码,以及您是用哪种语言编程的。
通过点击口译员,您可以选择其他口译员。例如,我们之前在 Anaconda 中创建的 Python 环境。
点击你的解释器,然后选择我们之前创建的解释器。

How to select a different Python interpreter.
现在,当你从你的基本解释器切换到一个新的解释器时,有时 Jupyter 服务器启动有困难。Jupyter-server 运行在一个内核上,这个内核在某种程度上是 Python 环境的引擎。Jupyter-kernel 对于在 VS 代码中运行代码至关重要,尤其是在交互式 Python 窗口中运行代码。如果您碰巧遇到这些错误,请在终端中尝试以下操作:
对于 macOS:
source activate <environmentnamehere>
pip install ipykernel
python -m ipykernel install --user
对于 Windows:
activate <environmentnamehere>
pip install ipykernel
python -m ipykernel install --user
这将在您的环境中专门安装一个内核。重启 VS 代码编辑器,尝试在新选择的解释器(python37:conda)中运行代码。
如果一切顺利,那么恭喜你!您已经成功地在 Visual Studio 代码中设置了自己的工作空间,现在您可以将它用于您的 Python 项目了!
结束语
管理您的 Python 环境可能是一件痛苦的事情。了解如何管理您的环境和软件包会给您带来很大的灵活性,并在您的一个环境突然停止工作时避免很多压力。这就是为什么我想向您展示如何切换环境和安装包,因为这些是您容易出现的错误类型。
当然,我还没有向您展示使用 Visual Studio 代码或 Anaconda 可以做的所有事情,这就是为什么我建议您也阅读以下文章:
我希望这本指南对你有所帮助。编码快乐!
在家里设置自己的小服务器

从树莓派开始
就在两年前,我的公司送给我们一个树莓派 3。
那时,我不明白那是什么。 感觉就像一个小板子里有很多复杂的电路和芯片。
所以它在我的橱柜里放了一年。
然后我面临一个特殊的问题。问题是我必须每两个小时运行一次 python 脚本。
一个解决办法是让我自己的 MAC 24 小时开着。 看起来不太合适的事情。
另一个方法是使用 AWS 服务器,但我很吝啬。这个特别的项目是一个家庭项目,所以我不想要任何昂贵的东西。
这时,我想起了橱柜里积满灰尘的覆盆子酱。人们以前说过它是一台小型电脑。
所以我想尝试一下。
这篇文章是关于建立你自己的小型个人服务器,只要 50 美元。
什么是树莓派?
我很晚才发现它,但我的树莓 Pi 3 是一台 1.2GHz 四核迷你电脑,内存为 1GB。
人们可以把它看作是一个基本而廉价的计算机主板,旨在为每个人提供计算能力。
根据 Pi 网站:
我们的使命是将计算和数字制作的力量传递到世界各地的人们手中。我们这样做是为了让更多的人能够利用计算和数字技术的力量来工作,解决他们关心的问题,并创造性地表达自己。
你需要什么配件?
现在,在开始用 Raspberry Pi 创建一个小型服务器之前,我们需要一些东西。你可以选择借用其中的大部分,因为安装时只需要一次。
- 显示器 —任何支持 HDMI 的显示器。这是 第一次只需要 你就要设置你的 Pi 了。我现在正在用我的普通 4K 显示器来做这个,但是它在这里可能有点过头了。
- 外设如 键盘和鼠标 :同样只在第一次你要设置 Pi 的时候。如果你身边没有,你可以向朋友借。
- 一个电源适配器: 一个简单的微型 USB 手机充电器就可以了。为此,我使用了以前的手机充电器。
- micro SD 卡: 你的 Pi 没有自带存储。你必须得到一个微型 SD 卡作为额外的附件,这是我们最终成本的一部分。这里是我得到的一个,它也带了一个卡适配器。
- 当然还有一份树莓酱。

Anatomy of a raspberry Pi: Source
和拉斯比安一起准备
现在我们有了第一次设置所需的所有附件。
下一步是安装操作系统。Pi 最流行的操作系统是 Raspbian。所以我们需要把它安装在我们的 SD 卡上。
这只是您可以遵循的一系列步骤:
- 参观 www.raspberrypi.org/downloads/

- 点击 NOOBS。NOOBS 只是一种在 SD 卡上安装 Raspbian Pi 的方法。
- 你会被带到一个下载选项。只需下载压缩文件。
- 解压缩 zip 文件
- 将 SD 卡插入您的 MAC/Windows 笔记本电脑。我的 Mac 已经有一个 SD 卡插槽。
- 将解压后的 zip 文件的内容复制到 micro SD 卡上。记住的不是文件夹,而是内容。

我们结束了。
现在我们终于可以用我们的树莓派了。
乐高小游戏
现在有趣的部分来了。只是把这一切联系起来。
这是连接所有东西并打开电源后的样子。

All the lego blocks in place
在这里,我已经将 HDMI 电缆连接到显示器、微型 SD 卡、键盘、鼠标,最后是电源适配器。
一旦你启动了你的树莓派。您需要完成一些提示。您将从安装 Raspbian 开始,如下所示,安装将需要几分钟时间。

您可以设置 wifi,也可以为您的 Pi 设置密码。
下面是一个更大的设置视图,你可以最终看到 Rapbian 的行动。它看起来有点像 Linux。

My Home Setup
所以我们手里终于有了一台新电脑。
但是我们仍然想归还我们借来的键盘和额外的外设。对吗?
我们想要一台小型计算机,在后台运行我们的预定任务,占用最小的空间。
最后,我们希望能够移除显示器和外围设备。唯一保持连接的将是 Rasberry Pi、SD 卡和电源适配器。我们称之为无头模式。
我们需要设置 SSH 来实现这一点。一旦我们完成了 SSH 设置,我们将能够使用我们的 Mac SSH 到 Pi。
设置 SSH
为了实现这一点,我们只需要在树莓 GUI 上做几个步骤。
- 打开首选项>树莓 Pi 配置

- 选择接口并启用 SSH。

- 我们还需要找出 Raspberry Pi 的 IP 地址,稍后我们将使用它来 SSH 到它。我们只需使用 Pi Start > Accesories > Terminal 打开终端,并键入命令
ifconfig。我们将在wlan0中看到 IP 地址。我 pi 的 IP 地址是 192.168.0.115。

我们准备好了。
安装后你需要什么?
一旦我们设置好 SSH,您只需在关闭电源后移除显示器、键盘和鼠标,然后再次打开电源即可。

My Headless Pi
我们的 Pi 开始运行了。
要从家庭网络上的 Mac SSH 到 Pi,您只需使用:
ssh pi@192.168.0.115
当它要求时,只需输入您的 Pi 密码。

Pi 预装了很多东西。例如,我们有 Python2 和 Python3。
我们可以使用 pip3 来安装所需的 Python 包。我们也可以使用sudo apt-get来安装依赖项和程序。
结论

恭喜你。
你有自己的服务器。只要 50 美元。
在电源中断的情况下,它会自动重启。我自己的已经连续跑了两天了,一点热度都没有。
你想用它做什么真的取决于你。您可以使用crontab -e命令使用它来运行计划的 cronjobs。您可以定期浏览网站,可以使用 selenium 来自动完成一些日常任务。您可以定期向某人发送电子邮件/消息。
当你想到 的时候,可能性真的是无穷无尽。
在我的下一篇文章中,我将谈论在我的 Pi 上设置一个小应用程序。敬请关注。
如果你想了解更多关于命令行的知识,我想你在使用 Pi 时会经常用到,Coursera 上有一个 UNIX 工作台课程,你可以试试。
将来我也会写更多初学者友好的帖子。在 媒体 关注我或者订阅我的 博客 了解他们。一如既往,我欢迎反馈和建设性的批评,可以通过 Twitter @mlwhiz 联系
在生产中设置 MLflow
使用 Postgres DB 存储元数据和 systemd 单元保持 MLflow 运行的分步指南。

Source: https://mlflow.org
这是我的 MLflow 教程系列的第一篇文章:
- 在生产中设置 ml flow(你在这里!)
- MLflow:基本测井功能
- 张量流的 MLflow 测井
- MLflow 项目
- 使用 Python API 为 MLflow 检索最佳模型
- 使用 MLflow 服务模型
MLflow 是一个用于机器学习生命周期管理的开源平台。最近,我在产品中设置了 MLflow,使用 Postgres 数据库作为跟踪服务器,使用 SFTP 通过网络传输工件。我花了大约 2 周的时间才把所有的组件都弄好,但是这篇文章可以帮助你在 10 分钟内把 MLflow 安装到生产环境中。
要求
跟踪服务器设置(远程服务器)
跟踪服务器存储您在 MLflow 用户界面中看到的元数据。首先,让我们创建一个新的 Conda 环境:
conda create -n mlflow_env
conda activate mlflow_env
安装 MLflow 和 PySFTP 库:
conda install python
pip install mlflow
pip install pysftp
我们的跟踪服务器使用 Postgres 数据库作为存储元数据的后端。所以让我们安装 PostgreSQL:
apt-get install postgresql postgresql-contrib postgresql-server-dev-all
接下来,我们将为跟踪服务器创建管理员用户和数据库
sudo -u postgres psql
在 psql 控制台中:
CREATE DATABASE mlflow_db;
CREATE USER mlflow_user WITH ENCRYPTED PASSWORD 'mlflow';
GRANT ALL PRIVILEGES ON DATABASE mlflow_db TO mlflow_user;
因为我们需要与 Python 中的 Postgres 进行交互,所以需要安装 psycopg2 库。但是,为了确保成功安装,我们需要在以下操作之前安装 GCC Linux 软件包:
sudo apt install gcc
pip install psycopg2-binary
如果您希望远程连接到 PostgreSQL 服务器,或者希望将其访问权授予用户。你可以
cd /var/lib/pgsql/data
然后在 postgresql.conf 文件的末尾添加下面一行。
listen_addresses = '*'
然后,您可以通过在 pg_hba.conf 文件的末尾添加以下行来指定您希望允许连接到 PostgreSQL 服务器的远程 IP
host all all 10.10.10.187/32 trust
其中 10.10.10.187/32 为远程 IP。要允许来自任何 IP 的连接,请使用 0.0.0.0/0 来代替。然后重新启动 PostgreSQL 服务器以应用更改。
service postgresql restart
下一步是为我们的跟踪服务器创建一个目录,以记录机器学习模型和其他工件。请记住,Postgres 数据库仅用于存储关于这些模型的元数据。这个目录叫做神器 URI。
mkdir ~/mlflow/mlruns
创建日志目录。
mkdir ~/mlflow/mllogs
您可以使用以下命令运行跟踪服务器。但是一旦你按 Ctrl-C 或者退出终端,服务器就会停止。
mlflow server --backend-store-uri postgresql://mlflow_user:mlflow@localhost/mlflow_db --default-artifact-root sftp://mlflow_user@**<hostname_of_server>**:~/mlflow/mlruns -h 0.0.0.0 -p 8000
如果您希望跟踪服务器在重新启动后启动并运行,并且对故障具有弹性,那么将它作为 systemd 服务运行是非常有用的。
您需要进入 /etc/systemd/system 目录,创建一个名为 mlflow-tracking.service 的新文件,内容如下:
[Unit]
Description=MLflow Tracking Server
After=network.target[Service]
Restart=on-failure
RestartSec=30
StandardOutput=file:/path_to_your_logging_folder/stdout.log
StandardError=file:/path_to_your_logging_folder/stderr.log
User=root
ExecStart=/bin/bash -c 'PATH=/path_to_your_conda_installation/envs/mlflow_env/bin/:$PATH exec mlflow server --backend-store-uri postgresql://mlflow_user:mlflow@localhost/mlflow_db --default-artifact-root sftp://mlflow_user@**<hostname_of_server>**:~/mlflow/mlruns -h 0.0.0.0 -p 8000'[Install]
WantedBy=multi-user.target
使用以下命令激活并启用上述服务:
sudo systemctl daemon-reload
sudo systemctl enable mlflow-tracking
sudo systemctl start mlflow-tracking
使用以下命令检查是否一切正常:
sudo systemctl status mlflow-tracking
您应该会看到类似如下的输出:

Systemd unit running
为名为 mlflow_user 的服务器创建用户,并将 mlflow 目录作为该用户的工作目录。然后在中创建一个 ssh-key 对。ml flow _ user(/ml flow/)的 ssh** 目录。在我们的例子中是 ssh** )。将公钥放在 authorized_keys 文件中,与用户共享私钥。
此外,为了让 MLflow UI 能够读取工件,将私钥复制到 /root/。宋承宪/ 也是。
接下来,我们需要使用以下命令为服务器手动创建主机密钥:
cd /root/.ssh
ssh-keyscan -H **<hostname_of_server>** >> known_hosts
现在,您可以重新启动计算机,MLflow Tracking Server 将在重新启动后启动并运行。
在客户端机器上(本地)
为了开始跟踪生产跟踪服务器下的一切,有必要在您的中设置以下环境变量。bashrc 。
export MLFLOW_TRACKING_URI='http://**<hostname_of_server>**:8000'
不要忘记为您的寻找来源。bashrc 文件!
. ~/.bashrc
确保在您的环境中为 mlflow 和 pysftp 安装 pip 包(需要 pysftp 来促进工件到生产服务器的传输)。
pip install mlflow
pip install pysftp
为了能够认证 pysftp 传输,将生产服务器上生成的私钥放在中。本地机器的 ssh 目录。那就做吧
ssh **<hostname_of_server>**
当提示将
您可以在http://

The Mlflow UI
从互联网上运行一个样本机器学习模型,以检查 MLflow 是否可以跟踪运行。
mlflow run git@github.com:databricks/mlflow-example.git -P alpha=0.5
在下一篇文章中,我将讲述基本的 MLflow 日志记录功能
参考资料:
[1]物流,安装物流 (2019),物流文件
构建数据科学模型的七个主要步骤
如何构建有效的数据科学模型
数据无处不在,但从这些数据中获取价值是最大的挑战。但是,随着数据科学的引入,情况有了好转。每个人都在谈论它,每个人都想融入这项更新的技术,但并不是每个人都知道如何去做。因此,这里有一个全面的指南,列出了构建成功的数据科学模型的重要步骤。让我们开始吧。

什么是数据科学?
数据科学是一门从数据中分析和提取知识信息的艺术。这种提取得到了领域专业知识、编程和一些数学和统计学的支持,它们共同工作以得出有意义的结论。数据科学是一个奇妙的发现,它引导企业获得难以置信的利润。你不相信吗?
下面的例子说明了同样的情况:
你不为数据科学传播的魔力而惊叹吗?是的,西南航空和联合包裹在这方面做了大量的工作。但是,这不是一日之功。有些事情他们做得不同,你也可以这样做。
改变的主要步骤是建立一个数据科学模型。如果你对如何进行这个过程感到幼稚,这里有一些必要的步骤。
1。数据提取
首先,你需要对手头的问题有一个概念,然后是收集数据。不是任何数据,而是收集的非结构化数据块应该与您将要解决的业务问题相关。你会惊讶地知道万维网是如何被证明是数据发现的福音。
你可以参考流行的在线数据仓库:
- Kaggle —数据科学项目的地方
- UCI ML 资源库 -机器学习档案库
- 数据集搜索引擎 -基于谷歌的数据集搜索
- NCBI——生物技术学术研究平台
注:并非所有数据都是相关和更新的。为了从收集的数据中找到意义,使用网络搜集。这是一个从网站提取相关数据的简化和自动化过程。
2。继续进行数据清理
你听说过不应该把事情留到明天吗?当您需要在收集数据时清理数据时,这一点非常重要。你越早裁员越好!
以下是一些常见的数据错误来源:
- 从许多数据库中收集的重复条目
- 输入数据在准确性方面的误差
- 数据条目被更改/更新/删除
- 跨数据库的变量中缺少值
消除常见错误来源的技巧:
- 通过参考公共 id 过滤掉重复的
- 根据数据的更新日期对数据进行分类,即优先选择最近的数据条目
- 用平均值填写缺失的数据条目
3。深入研究数据
现在每个数据源都准备好了,您可以开始分析所涉及的基本模式。部署有趣的工具,如 Tableau 或微策略可以帮助很多。您所要做的就是构建一个交互式仪表板,并查看您的数据如何成为重要见解的一面镜子。
画面会很清晰,现在你会知道是什么在驱动你的业务的变化特征。例如,如果是定价属性,您将知道价格波动的时间和原因。
5。识别关键特征
当试图掌握商业中的关键模式时,可以部署特征工程。这一步不能被忽略,因为它是最终确定合适的机器学习算法的先决条件。简而言之,如果特征很强,机器学习算法将产生令人敬畏的结果。
有两类功能需要注意:
- 不太可能改变的恒定特征
- 值随时间波动的可变特征
6。探索机器学习的世界
这是最重要的步骤之一,因为机器学习算法有助于建立一个可行的数据模型。有许多算法可供选择,但不要担心,因为数据科学家会让你轻而易举。
用数据科学家的话来说,机器学习就是部署机器来理解一个系统或底层流程,并为其改进做出改变的过程。并且,算法可以被称为计算机系统驱动特定任务的一组指令。
以下是你需要了解的三种机器学习方法:

Types Of Machine Learning
- 监督学习:基于过去类似过程的结果。监督学习有助于根据历史模式预测结果。
举例:通过回顾过去点击量最高的在线广告,你可以预测是什么特别的特点让用户立即点击。
用于监督学习的屡试不爽的算法:
线性回归
-随机森林
-支持向量机
- 无监督学习:这种学习方法仍然缺乏现有的结果或模式。相反,它侧重于分析数据元素之间的连接和关系。
举例:当你看到脸书上的“建议好友”专题时。社交媒体平台计算两个用户共有的朋友数量。共同的朋友越多,关系越密切。
无监督学习的试验和测试算法:
-k 均值
-Apriori 算法
- 强化学习:这是一种有趣的机器学习方法,依赖于与现实世界交互的动态数据集。简单来说,就是系统从错误中学习,一天比一天好的方法。
举例: 深蓝是 IBM 作为下棋专家创造的系统。随着每一场比赛的进行,这个系统从初级水平发展到了专业水平。
强化学习的屡试不爽的算法:
Q-学习
-国家-行动-奖励-国家-行动
-深度 Q 网
7。评估&部署模型
一旦你选择了正确的机器学习算法,接下来就是对它的评估。您需要验证该算法,以检查它是否为您的业务产生了预期的结果。
交叉验证甚至 ROC(受试者操作特征)曲线等技术可以很好地概括新数据的模型输出。如果模型产生了令人满意的结果,那么就可以开始了!实施该模型,并看到您的企业以前所未有的方式发挥作用。
- 工程师有权将模型部署到相应的生产阶段。在这里,专家将模型翻译成生产栈语言,以促进良好的实现。
- 第二,建立基础设施,进一步使数据科学家足够独立,可以独立部署数据模型。随着 API 的快速发展,这是可能的。这些 API 致力于消除数据科学和项目团队之间的滞后。
关键外卖:
无论你的企业在哪个领域运营,数据科学都应该是你的下一个大实验。构建数据科学模型是一个收集各种数据集并赋予其意义的美丽旅程。
如果你真的希望在竞争中领先,这是开始数据科学项目的最佳时机。按照列出的步骤,马上开始吧!毕竟,我们的目标是将数据转化为信息,将信息转化为见解。
作者简介:
Paige Griffin 在洛杉矶 Net Solutions 工作了 7 年,是一名经验丰富的内容作家,擅长撰写博客,为直接回应市场撰写创意和技术文案,并为 B2B 和 B2C 行业制作促销广告。Paige 在纽约出生和长大,拥有英国文学学士学位。她曾在 IT、产品工程、生活方式等行业工作过,并撰写了一些关于 python web 开发、php web 开发、 iOS 应用程序开发等技术的深刻见解。除了技术背景之外,她内心还是一个诗人,喜欢通过一定量的创造力和想象力与人们联系在一起。
机器学习的七个步骤

这是机器学习入门系列的第二个视频。如果你是 ML 新手,我鼓励你先看一下之前的视频。
在之前的视频中,您学习了什么是 ML,为什么它很重要,以及如何从数据集训练机器学习模型。然而,ML 不仅仅是训练模型。在这个视频中,我涵盖了构建机器学习系统所需的七个步骤。
我还透露了我在 面试 有抱负的数据科学家时问的第一个问题 是什么,并提供了一些我之前与客户接触时的行业实例。**
以下是视频中的步骤和内容:
****1。资料收集:《数据湖妄想》。
****2。数据管理:数据模式、语义类型、缺失值处理、数据聚合。
****3。数据探索:趋势/分散的测量,可视化。
****4。特征工程:特征选择,降维,主题专家 vs .数据科学家协同。
****5。建模:如何选择一个机器学习算法?监督与非监督学习,推荐系统,开源工具。
****6。测评:如何挑选最好的型号?
****7。部署:从模型到运行在云上的服务。
关于作者
scar D. Lara Yejas 是高级数据科学家,也是 IBM 机器学习中心的创始成员之一。他与世界上一些最大的企业密切合作,将 ML 应用于他们的特定用例,包括医疗保健、金融、制造、政府和零售。他还为 IBM 大数据产品组合做出了贡献,特别是在大规模机器学习领域,是 Apache Spark 和 Apache SystemML 的贡献者。
scar 拥有南佛罗里达大学的计算机科学和工程博士学位。他是《人类活动识别:使用可穿戴传感器和智能手机》一书的作者,并发表了大量关于大数据、机器学习、以人为中心的传感和组合优化的研究/技术论文。
为客户设定目标和选择衡量标准的七个步骤
当你想为你的客户提高价值时,规划清晰的目标和一个数据驱动的方法来实现它们是值得的。问题是,这是一个棘手的过程,隐藏着陷阱,即使你正在经营你的业务,这些陷阱也会让你感觉良好。在这篇文章中,我分享了我带领团队和决策者将想法转化为清晰可衡量的客户目标的七个步骤。

Seven-steps to set goals and pick metrics for customers. Photo by Samuel Chenard on Unsplash
动机
我们都希望我们的企业为客户提供价值,但我们如何知道我们正在实现它?嗯,有一些与利润和损失相关的财务指标,您可以通过监控这些指标来维持运营。然而,这些很少给你可操作的客户洞察力。
要发展,你的目标不能仅仅是增加利润。你需要考虑如何改善用户体验,增加满意度,减少困惑,或者增加忠诚度。如果做得正确,设定这些目标,并选择衡量其成就的标准,将有助于您更快地向客户交付更多价值。
大错误:将目标定义为度量标准
一个容易犯的错误是将目标定义为度量标准。一些例子:
- 提高转化率
- 减少客户服务查询量
- 提高客户回头率
这些有直观意义。你为什么不想提高转化率或客户回头率?但是你对这些目标有什么看法:
- 让支付变得更容易,转化率的增加就是证明
- 减少混乱,客户询问的减少就是证明
- 通过更高的退货率来提高满意度
相同的指标,巨大的差异。这些目标确切地表达了你想如何让顾客满意。这里的要点是,度量永远不应该定义目标。衡量标准只提供你正在实现目标的证据。
如果这还不行,下面是我不使用度量标准作为目标的主要原因。
不可弯曲的
将一个目标定义为一个度量标准会将你的成就限制在你可以衡量这个度量标准的范围内。好的目标是度量不可知的,并且足够灵活,可以在任何场景下获取成就。
不可宽恕的
当你选择一个不可靠的、无效的、不可移动的或者通常不好的度量标准时,度量标准定义的目标是不可原谅的,你几乎肯定会这样。好的目标会原谅不好的指标选择,并让你在需要时使用其他指标。
无常的
有了像“增加转化率”这样的目标,当事情不按你的方式进行时,你最终将不得不面对不确定性的浪潮。好的目标阐明了无论事情朝哪个方向发展,如何解释证据。
危险的
从任何单一指标得出强有力的结论都是不安全的,因为指标的变化有很多原因。好的目标可以让你用多个指标来衡量你的进步,并让你对自己的决定充满信心。
“指标永远不应该定义目标。衡量标准只提供你正在实现目标的证据。”
设定目标和选择衡量标准的七个步骤
面对这些隐藏的挑战很多次了,我把设定目标和选择标准的过程分成了七个步骤。起初这需要一些额外的时间,但很快就会成为习惯,并让你获得比其他方式更多的成功。
第一步。起草你的目标
你想达到什么目的?不要想多了。把想法写在纸上。为了证明这一点,想象一下想要改进一个电子商务网站的支付流程。我的第一个想法是:

第二步。抓住问题的核心
“为什么?为什么?为什么?”一遍又一遍地问自己。为什么这是我的目标?我真正想解决的问题是什么?我如何为我的客户提高价值?戳和探索,直到你到达重要的美味中心。
为什么我要减少点击?真的吗?也许越多越好。不断询问和挑战,你可能会得出结论,我真的想:

不要低估这一步的难度或价值。如果这里还有一个度量标准,那么你还没有完成。每次我带人们走过这一步,我都必须不断追问“为什么”,直到我们找到具体而有意义的东西。它几乎总是从我们出现偏见和不确定或冲突的目标开始,但以更深刻的见解和更清晰的目标结束。
第三步。将你的目标设定为一个有方向的维度
下一步涉及一些前瞻性的思考。在一天结束时,您将查看您的指标在一个方向或另一个方向上是上升还是下降。
要计划与指标的联系,将你的目标重新表述为一个维度(你有兴趣改变的东西)和方向(你希望它如何改变)。我会这样重新表述我们的支付目标:

一定要用两个明确的极点来定义自己的方向:更好/更差,满意/不满意,更多/更少,等等。“容易”的反义词是“难”。但是“优化”的反义词是什么呢?如果你不能定义两极,如果你的度量与你期望的方向相反,你就不能解释发生了什么。
第四步。画出你的目标
最简单的步骤:在一个指向你想要的方向的箭头上画出你的尺寸。

这有助于为后续步骤保持一切有序。
第五步。想象一下你期望在两端观察到什么
对于你箭头的每一端,问问你自己,“在一个[维度]就是[方向]的世界里,我期望观察到什么?”
在一个顾客很容易付款的世界里,你会观察到什么?难的地方怎么样?发挥创造力,我建议手边要有便利贴。以下是我想到的一些事情:

试着把你的注意力放在客户的想法、感受或行为上,暂时不要包括指标。
第六步。提炼和可视化期望
对于每个预期的观察,完成步骤 2 到 4,将它们转化为与你的目标一致的维度和方向。

事情在这里真正开始成形,对我来说,这是视觉帮助很大的地方。
第七步。选择您的衡量标准
最后,为每个预期的观察(蓝框)列出一个或多个指标(你可以测量的事物)以及它们发生的方向。像这样把它们覆盖在你的视觉上:

你有它!你的目标的视觉表现,如果你正在实现(或没有实现)你期望看到的事情,以及你可以实际测量并记录它们的结果模式。
总结和实用技巧
在流程结束时,您可以将您的目标与如下指标联系起来:

如果你的所有指标都朝着你期望的方向发展,那么你就找到了实现目标的证据。
如果你尝试这样做的话,一个建议是把你自己限制在 3 到 5 个最相关的指标上。这应该给你足够的信息来衡量你的成就,而不会让你的仪表盘上充斥着可能有问题的表盘。
最后,这里有一张小抄,供你下次尝试时参考:

停止广播
感谢阅读,请在下面分享你的想法和评论,我希望这对你有用。
想多读点?你可以在 Medium、 Twitter 或 LinkedIn 上以@drsimonj 的身份关注我的最新帖子。
二手车门户网站抓取和探索性数据分析项目
编辑:文章已修改,保持汽车门户网站的身份匿名。
简介
本项目的目的是开展一个小型项目,涵盖大部分数据科学流程,从数据收集、数据清理到探索性数据分析阶段。
数据来源是东南亚的二手车门户。为了保护公司的身份,省略了门户网站的名称。
考虑到拥有汽车的高昂成本,二手车市场的数据分析可能会为汽车门户网站、消费者和汽车经销商带来节约和收益。汽车门户网站拥有超过 10 万辆汽车的数据库,似乎没有分析或数据科学团队来分析这些数据。因此,这个项目的目的是进行探索性的数据分析,以获得对汽车门户、消费者和经销商有意义的见解。
使用的 Python 版本是 3.7.0。
第 1 部分:数据收集
数据是用 BeautifulSoup 4 v0.01 从汽车门户网站上刮下来的。
首先,我们导入模块:
二手车被列在单独的网页上,它们的 URL 包含唯一的 id,格式便于迭代。此外,所有有用的数据都包含在 html 的main_left部分中。
定义 process_new()函数
接下来,我们定义一个函数process_new(),该函数首次提取单个车辆的数据:
定义 update()函数
然后我们定义一个函数update(),该函数为之前使用process_new()函数抓取的列表重新抓取数据。该函数的目的是捕捉售出的车辆,以及捕捉价格等列表信息的变化。
update()功能与process_new()功能相似,但有一些不同之处,这些不同之处将在下面进行解释:
定义了process_new()和update()函数后,就可以通过现有的清单 id 运行这些函数了。我将每次迭代的休眠时间设置为 0.1 到 0.2 秒之间的随机浮动,以防止服务器过载。
第一次运行 process_new()时
接下来,我们通过运行下面的代码开始构建本地数据库(DB ),第一次一个一个地抓取清单:
SQLite3 用于与数据库接口,而 DB Browser for SQLite 用作 DBMS。一旦列表被抓取,我们就可以开始用update()函数每天更新它们。
更新步骤 1:生成要更新的 id 列表
在运行update()函数之前,我们生成一个应该更新的列表,即状态仍然为“可用”而不是“已售出”或“过期”的列表:
更新步骤 2:批量运行 update()
现在已经创建了 id 列表,我们可以开始在 DB 中更新它们对应的行。
一般来说,对数据库中现有的条目运行update()的日常过程需要大约 2.5 小时,总共大约 12,000 个行/ID,每个行/ID 花费大约 0.7 秒。
每天为新列出的车辆运行 process_new()
process_new()功能也每天运行,以捕捉新列出的车辆。要抓取的 id 范围(从range_start开始,到range_end结束,但不包括 T5)必须每天重新定义。由于数据收集过程是在 Jupyter 笔记本上完成的,特定一天的process_new()执行的range_start就是前一天保存在笔记本上的range_end。通过在汽车门户网站上进行快速手动搜索,找到了range_end。
第 2 部分:数据清理
经过一段时间的数据收集,数据库拥有 23,645 行数据。需要很大程度的清洁。

Snapshot of the Database in DB Browser for SQLite (id and other rows censored)
出于这种探索性数据分析的目的,我们只查看那些成功销售的列表。仍可出售的列表从数据库中删除。我们也不包括非私人机动车辆。因此,货车/卡车、公共汽车和货车等商用车辆不在分析范围内:
汽车门户网站上的一些列表没有年度折旧数字。因此,我们必须根据以下公式手动计算年折旧:
年折旧=(价格—退税)/剩余天数 365*
EC 是权利证书,一种给予一个人拥有和操作一辆汽车的权利的许可证。
退税是指车主只有在第一个 EC 的 10 年期满后注销车辆时才能获得的退税。这些车辆可能被报废或出口。对于该数据库中的车辆,退税为注册费(RF)的 50%。幸运的是,RF 数字包含在数据收集步骤中。
数据库中的depre列没有被修改,以防需要原始值。创建了一个新列final_depre来存储最终的折旧数字。然后运行以下查询来填充final_depre列:
cat栏在筛选符合退税条件的车辆时非常有用。符合退税条件的车辆在卡特彼勒一栏下有一个特定的标签。
随后,删除了不需要的列,包括avail和depre,并且为了更加清晰起见,重新排列了这些列。
移除了price为 0 的车辆。此外,非高峰时段的车辆被删除,因为它们运行在不同的经济(较低的折旧,由于其独特的,有限的使用计划)。
接下来,我们计算每辆车售出的天数:
挑战:细分车辆
从基本的奇瑞 QQ 到豪华的宾利欧陆 GT,汽车的声望各不相同。从这个维度上分割车辆可以得到进一步的见解。浏览数据,我们可以通过几种潜在的方法对车辆进行细分:
- 车型 (
veh_type):车型分为 12 类,分别是中型轿车、豪华轿车、掀背车、MPV、SUV、跑车、旅行车、全商用车、厢式车、货车、客车/小型客车、其他。每辆车只能分配一种车型。这种分类是模糊的,因为高档和大众市场品牌在所有这些细分市场生产汽车。此外,根据汽车门户网站的定义,丰田凯美瑞、宝马 520i 和劳斯莱斯 Ghost 都属于豪华轿车。车辆类型不是分割车辆的好方法。 - 类别 (
cat):类别有 13 类,包括车主直接出售、拍卖车辆、稀有&异国车等。除了仅适用于一小部分汽车的“稀有外国汽车”之外,其他类别并不能告诉我们汽车的声望水平。这在分割车辆时也没有用。 - 品牌和型号:一辆车的品牌和型号很能说明它的声望。仅仅是品牌本身就能告诉我们很多关于对一辆车的期望——例如,当我们得知有人开宝马时,外行人的第一个想法可能是“这是一个有声望的品牌”。让我们根据汽车的品牌来分类。
需要在(1)使分析有意义和(2)避免过度分割车辆之间取得平衡。根据我对我国汽车品牌的了解,并通过谷歌搜索,6 个拟议的细分市场是异国情调、超豪华、豪华、中级、经济和预算。
提议的分段及其各自的构成如下:
- 异域 (4):阿斯顿马丁、法拉利、兰博基尼、迈凯轮、
- 超豪华 (5):宾利、路虎、玛莎拉蒂、保时捷、劳斯莱斯
- 豪华型 (9):奥迪、宝马、捷豹、吉普、雷克萨斯、莲花、奔驰、三雄、沃尔沃
- 中级 (7):阿尔法罗密欧、克莱斯勒、英菲尼迪、迷你、欧宝、萨博、大众
- 经济 (17):雪佛兰、雪铁龙、菲亚特、福特、本田、现代、起亚、马自达、三菱、日产、标致、雷诺、斯柯达、双龙、斯巴鲁、铃木、丰田
- 预算 (5):奇瑞、大发、吉利、Perodua、宝腾
在 DB 中创建了一个新列make。运行以下查询从make_model列提取品牌,并填充make 列:
然而,我们遇到了有空格的品牌的问题,如“阿尔法罗密欧”结果是“阿尔法”。然后,我们收集了一系列独特的品牌:
输出包含 47 行,其中 3 个品牌(Alfa Romeo、Aston Martin 和 Land Rover)需要使用以下查询进行修复:
接下来,我们在表格中创建一个新列segment,并将其应用于品牌各自的细分市场:
标签溢价和直接所有者销售广告
接下来,在表格中添加一个premium_ad列,将广告标记为高级(1)或非高级(0)。对于由直接所有者而非经销商出售或寄售/拍卖的车辆也是如此:
清理带有描述性文本的列
此外,features(特性)、acc(配件)和description(描述)栏似乎有几行是空的。当运行 tf-idf 分析时,这将成问题。因此,我们用“Nil”替换空行或包含“-”的行:
瞧啊。数据库现在可以运行了。然后将其导出为. csv 文件。开始分析。
第 3 部分:探索性数据分析
项目的分析部分是在 Spyder IDE v3.3.2 上完成的。数据的可视化是在 Matplotlib v3.0.0 和 Seaborn v0.9.0 的帮助下完成的。
首先,我们导入模块:
接下来。csv 文件被作为 Pandas 数据帧读取,列标题被更新:
去除异常值
现在我们用散点图快速看一下数据,用Segment给数据点着色,绘制Days to Sell对Depreciation:
输出如下所示:

Scatterplot of Days to Sell against Depreciation; Coloured by Vehicle Segment (Incl. Outliers)
快速浏览散点图可以发现几个异常值。为简单起见,我们排除了年折旧超过 75,000 新元的车辆数据。我们使用折旧而不是价格,因为在我的国家,车辆的使用寿命是有限的,这是由剩余的 EC 期限决定的,因此摊销费用的相关性更大。此外,我们将主数据帧df分解成多个数据帧,代表不同的段:
接下来,为了直观检查分段的准确性,按顺序绘制各个分段:
输出图表已被处理成. gif 格式,如下所示:

Scatterplot of Days to Sell against Depreciation; Coloured by Vehicle Segment (Excl. Outliers)
分割看起来不错。各段大致占据Depreciation轴上的正确区域。
问题 1:向直接所有者购买还是向经销商购买?
购车者通常会在经销商和直接车主之间进退两难。每一种都有其优点和缺点。经销商通常提供内部融资选项并提供短期保修,但众所周知,一些经销商隐瞒缺陷和磨损迹象,例如篡改车辆里程表以显示较低的里程读数。直接所有者不太可能篡改车辆,更愿意接受价格谈判,但时间表更紧,不提供私人融资选项等辅助服务。
汽车门户网站将直接所有者销售的广告标记为“直接所有者销售”。我们将“非直接车主”群体概括为汽车经销商,因为其他非直接车主广告,如寄售和拍卖广告相对不常见。让我们将数据集分成两种不同类型的卖家,并查看一下描述性统计数据:

Table 1: Direct Owner Sales Descriptive Statistics (Incl. 0 Mileage)

Table 2: Dealer Sales Descriptive Statistics (Incl. 0 Mileage)
从上面两张表中可以看出,经销商销售的平均和中间价格和折旧比低。对于经销商来说,平均和中间里程数也比平均里程数低。这与里程数少的车价格高的想法不一致!或许经销商销售的里程数据被大量里程数字为“0”的列表扭曲了?让我们通过更新directowner_df和dealer_df来过滤这些列表,重新运行描述性统计,并绘制里程直方图:

Table 3: Direct Owner Sales Descriptive Statistics (Excl. 0 Mileage)

Table 4: Dealer Sales Descriptive Statistics (Excl. 0 Mileage)
直接和非直接所有者销售的中间价格和平均价格以及折旧的差距已经缩小。平均里程和中值里程也是如此。然而,平均/中值折旧与里程数之间的正相关关系告诉我们,一些经销商可能在进行一些不正当的活动,即篡改待售车辆的里程表。让我们看看里程直方图:

Mileage Histograms for Direct Owner Sales vs Dealer Sales
查看内核密度估计(KDE)线图,经销商销售的车辆具有较平的右尾部,并且更偏向具有较高峰值的左侧。经销商的 KDE 曲线接近双峰,第二个峰值在 35,000 公里大关附近。经销商销售的折旧分布和 KDE 图会是什么样子?(注意两个 KDE 图中对于里程范围< 0 的微小误差。KDE 图是拟合分布的估计值,并不代表实际分布。)

Depreciation Histograms for Direct Owner Sales vs Dealer Sales
经销商销售的 KDE 折旧图在峰值左侧有一个拐点。然而,里程和折旧有一个相反的关系——我们本以为在最高峰的右边会有第二个更低的峰,但 KDE 图显示的却不是这样。因此,我们可以合理地假设经销商更倾向于篡改车辆的里程数。
现在回到是从直接车主还是经销商处购买的问题上来:与其做出明确的选择,对于买家来说,更实际的做法是格外小心一辆里程数低但折旧也低的经销商车辆。类似地,买方应始终与经销商协商更低的折旧给某车型,该车型也可从类似情况下的直接所有者处获得。
问题 2:汽车门户网站是否放弃了来自低端市场的收入?
从表 3 和表 4 中观察到的另一个有趣现象是,直接所有者销售的车辆通常具有更高的发动机容量,平均值为 2,066.5cc,中位数为 1,984.0cc,而经销商销售的车辆的平均值为 1,863.2cc,中位数为 1,598.0cc。发动机容量与折旧有正相关,原因有两个——(1)高档车折旧高,通常配备大型发动机。(2)与发动机容量较低的相同型号相比,车辆的折旧较高,例如,2500 cc 的丰田凯美瑞与 2,000cc 的丰田凯美瑞。
这表明经销商销售较低细分市场车辆的比例较高,而直接所有者销售较高细分市场车辆的比例较高。为了调查这一说法,我们对各个细分市场的直接所有者与经销商销售进行了统计:

Table 5: Count of Vehicle Sales by Direct Owner vs Dealers, for the Various Segments
如表 5 所示,从超豪华到经济型,经销商直接向车主销售的比例大幅上升。之前的说法确实没错。高档车的车主更有可能以自己的身份进行销售。这可能是因为经销商向这些车主提供了很低的价格,以保护他们自己(经销商)免受高额维修费用的影响,这是高端大陆品牌普遍存在的问题。高档车的车主也可能更加精明,因此能够通过在汽车门户网站上进行销售。
另一方面,经济型轿车中较低级别车型的车主更倾向于向经销商交易或出售车辆,而不是自行销售。
从汽车门户网站的角度来看,如果车主决定在汽车门户网站上出售汽车或直接向经销商出售汽车,这很重要。交易有三种可能的方式:
- 车主直接销售给经销商而不在汽车门户上挂牌->经销商在汽车门户上挂牌->汽车销售给私人买家(汽车挂牌一次)
- 车主在汽车门户网站上列出汽车->经销商购买汽车->经销商在汽车门户网站上列出汽车->汽车出售给私人买家(汽车列出两次)
- 车主在汽车门户网站上列出汽车->卖给私人买家的汽车(汽车列出一次)
根据表 5,有太多的经济舱车主按照上述选项 1 进行交易。经销商销售的经济型轿车数量也超过了所有其他车型的总和。如果更多的经济型轿车车主选择自己上市,汽车门户网站的收入会显著增加,因为如果同一辆车被卖给经销商,它可能会第二次上市。截至目前,它对标准广告收取统一费用,直到汽车售出。

Comparison of Direct Owner Revenue and Dealer Markup for 2 Sales Sequences (estimated, not to scale)
上图显示了直接所有者销售之前和之前的经销商销售的估计收入和加价明细。通过鼓励车主自己挂牌,这些汽车中的一部分将不可避免地出售给私人买家。然而,经销商也很有可能是买家,因为他们积极寻找并达成汽车交易,这些汽车可以转手获利。如果第二类经销商销售增加,第一类经销商销售减少,汽车门户网站将获得更高的广告收入,同时减少经销商获得的加价。直接所有者也可能收到更高的价格,因为他将收到更多经销商的报价。
在没有车主信息的情况下,我们只能假设车主直接向经销商销售而不是自己销售的三个原因:
- 快速销售,急需现金——由于快速销售车辆的压力而难以捕捉
- 方便,不需要与挑剔的买家打交道——也很难捕获,因为这需要汽车门户网站提供托运服务
- 不精通技术,即无法自己列出汽车或之前从未听说过汽车门户网站— 目标群体
汽车门户网站可以通过提供帮助这些车主创建他们的列表的服务来捕获第三类车主。一旦车辆被张贴出来,车主只需要管理潜在买家的电话和短信。截至目前,汽车门户网站似乎没有这样的服务,所有的上市过程都是由车主在线完成的。
汽车门户网站已经提供了一项服务,帮助买家和卖家解决与交易相关的文书工作。通过帮助面临技术挑战的车主发布他们的清单,汽车门户网站可以提供真正的端到端服务,从而吸引更广泛的客户。
问题 3:优质广告——有用吗?
据一位在汽车门户网站工作的朋友说,优质广告会有更多的图片和更高的搜索优先级。只有经销商可以选择使用高级广告,并且经销商可以使用的高级广告数量有限。汽车门户网站上的优质广告在许多方面都很突出,例如,当它出现在搜索结果中时会有一个绿色标签。
现在,让我们创建一个新的数据框架,不包括Mileage = 0 的车辆,然后将其分为两个数据框架,分别用于高级和非高级广告。然后我们运行描述性统计:

Table 6: Premium Ad Descriptive Statistics (Excl. 0 Mileage)

Table 7: Non-Premium Ad Descriptive Statistics (Excl. 0 Mileage)
根据上面的表 6 和表 7,溢价广告通常会导致更快的销售,平均需要 11.2 天才能销售,中位数为 7.0 天,而非溢价广告平均需要 12.8 天才能销售,中位数为 9.0 天。溢价广告的平均销售时间比非溢价广告少 12.5%。
人们可能会认为高价广告会以较低的价格快速销售,但描述性统计数据却表明并非如此。溢价广告的平均价格更高,折旧也更高。此外,平均而言,处于中值的优质广告里程数较低,剩余 EC 期较长,且制作时间较晚。换句话说,高价商品清单中包含的是更新、状况更好的车。
简而言之,溢价广告是有效的,因为它们导致更快的平均销售。
当分成不同的部分时,优质广告会显示出有趣的模式吗?

Table 8: Count of Vehicle Sales by Premium vs Non-Premium Ads, for the Various Segments
在细分市场和广告是否优质之间没有有意义的关系。在 6 个细分市场中,有 4 个市场的非溢价广告与溢价广告的比率在 2%以上。
问题 4:我应该使用什么关键词来描述车辆?
汽车门户网站上的每个列表都有 3 个描述性文本字段,即功能、附件和描述。目标是通过拟合几种类型的回归模型,即支持向量回归(SVR)、决策树回归、XGBoost 回归和线性回归,找出文本是否与销售速度(销售车辆所需的天数)有任何关系。我们做的第一件事是在主数据框df中创建一个新列,合并所有三个字段的内容:
然后我们导入相关的模块并清理文本:
接下来,单词袋模型被创建。TfidfVectorizer()的ngram_range被设置为(1,2)以捕获可能在语义上重要的单个单词以及单词对:
我们将探索一个回归模型,自变量为 tf-idf 矩阵,因变量为售出一辆车所需的天数。然后,我们将数据集分为训练集(85%)和测试集(15%):
然后,我们开始在 SVR 上执行网格搜索以获得最佳参数,以均方误差(MSE)作为评分标准:
多边形内核被排除,因为它无法收敛到一个解决方案。出于同样的原因,对于误差罚项 C = 10、100 和 1000 的线性核,也进行了同样的处理。接下来,生成结果:

Table 9: Grid Search Results on the SVR Model
SVR 的最佳参数是kernel = ‘rbf’和C = 10,最小均方误差为 152.55。这意味着 RMSE 为 12.35,考虑到Days to Sell的平均值为 12.48,中位数为 8.00,这是一个非常差的拟合。接下来,我们将 SVR、决策树、XGBoost 和线性回归模型拟合到 tf-idf 矢量器,计算 10 次迭代的平均 RMSE:

Table 10: Mean RMSE for the 4 Regression Models
所有 4 个模型都有大量的 RMSE 数据,在预测汽车售出的天数时没有用。考虑到高 RMSE 数字,进一步调整参数没有什么好处,特别是对于 XGBoost。(在最近了解到 XGBoost 出色的性能和速度后,我很乐意为它这么做)。就我个人而言,我只会调整模型的参数,如果它的 RMSE 数低于 4 的话。因此,我们就到此为止。让我们研究单词/单词对的线性回归系数,以获得任何见解。
线性回归模型重新运行 10 次,以生成特征及其相应的系数:

Tf-idf Features and Their Coefficients for 10 Linear Regression Iterations
接下来,我们对系数进行快速粗略平均,并对平均值进行排序,以查看哪些系数可能是重要的:
系数值范围从-2.33 到 3.61。让我们看看系数最低的 10 个特征和系数最高的 10 个特征:

Table 11: Top Linear Regression Features Based on Coefficient Magnitude
一些特征名看起来很奇怪,因为在数据库中的Text列被矢量化之前,它们被PorterStemmer简化为词干。表 11 包括它们的翻译和它们适用的上下文。具有高绝对系数的特征表示在系数的正端和负端处。这些功能被忽略。让我们看看 5 个不重复的特征(上面橙色的)是否会影响卖出一辆车的天数。
我们在主数据框df中为 5 个特征创建 5 个新列,在Text列中搜索匹配项,如果特征出现则插入1,如果没有出现则插入0。幸运的是,汽车门户网站在其文本字段中将所有单词的首字母大写。因此,必须进行的单词置换的数量保持较低。
接下来,我们绘制直方图,以便快速直观地检查Days to Sell的分布是否受到 5 个特征的影响:

Key Tf-idf Features and Their Impact on the Number of Days Taken to Sell a Vehicle
每个特征的两个直方图看起来非常相似。请注意,“sta vicom”功能提供的比较很差,因为包含该功能的样本数量很少。直方图没有提供任何信息。让我们看看熊猫综合数据框架中的描述性统计数据:

Table 12: Descriptive Statistics on 5 Key Tf-idf Features
对于“雾”和“评估”特性,销售天数的平均值和一般分布几乎没有区别。包含“非强制性”特征的样本具有更高的平均值和,但是该特征在线性回归模型中具有负系数。这可能是由于 1000 个文本特征的多重共线性,因为多个特征可以频繁地一起使用。单看一个单词或一对单词不太可能产生任何有意义的见解。
最后,包含“驱离”特征的样本比不包含“驱离”特征的样本具有更高的均值和中值。这与其正系数一致,但考虑到多重共线性问题,可能只是巧合。
简而言之,多重共线性问题导致 td-idf +回归分析产生有限的洞察力。我们无法判断哪些词会导致更快的销售。文本和卖出一辆车的天数之间可能没有关系。
tf-idf 分析在分类问题中可能比在回归问题中更有用。
探索性数据分析的结论
让我们总结一下各方的要点:
- 汽车门户网站:帮助不了解汽车的车主创建他们的列表,以便从没有在汽车门户网站上列表的大量经济型车主中获取一部分
- 车主销售:鉴于模糊的 tf-idf 分析结果,没有真正的外卖
- 购车者:当心经销商的廉价低里程汽车;始终与经销商协商低于所列的价格
- 经销商:明智地选择优质广告,因为它们的销售速度比非优质广告平均快 12.5%;如果你在篡改里程表,请三思!
第 4 部分:结束语
这个项目产生了比我最初预期的更多的见解。虽然我很想有机会与该公司合作,并获得更多的功能,我对这个项目的结果感到满意,因为它的时间表相当紧张,只有 3 个月。也许更长的数据收集期会有助于更好的探索性分析。
然而,我期待着任何关于改进领域的讨论或反馈。
欢迎在 LinkedIn上给我发消息或者在下面留言。
我们要马上建立透明模型吗?
可解释的人工智能(xAI)是新的酷小子,xAI 方法(建立一个黑盒,然后解释它)现在是机器学习从业者最珍视的工作方式。这真的是最好的路线吗?为什么我们不马上建立一个可解释的模型呢?

Rashomon (羅生門 Rashōmon) is a 1950 Jidaigeki film directed by Akira Kurosawa. The film is known for a plot device that involves various characters providing subjective, alternative, self-serving, and contradictory versions of the same incident. (wikipedia)
可解释与可解释的人工智能
可解释性和可解释性是两个不同的概念,尽管在不同的来源中,这两个概念似乎被错误地互换使用。在这篇博文中,我将根据以下定义[7]进行推理,至少从我的角度来看,这些定义似乎被最广泛地采用:
- 可解释的 ML:使用黑盒并在事后解释它
- 可解释的 ML:使用透明的模型,即不是黑盒
换句话说,基于这些定义,可解释性是一个模型属性,而可解释的 ML 指的是旨在解释黑盒模型的工具和方法论。

xkcd: curve fitting [0]
我们应该首先建立一个黑盒子吗?
辛西娅·鲁丁[1]的采访很好听,让人耳目一新,她的文章也是[2]。在众多观点中,辛西娅提出了两个有趣的观点:
1.可解释的 ML 方法提供了不可靠的解释
当前主流的可解释 AI / Ml 的解释没有多大意义:可解释 Ml 方法提供的解释并不忠实于原始模型计算的内容。根据定义,这是正确的,即使对于本地代理人也是如此;最重要的是,目前可用的局部替代方法是不稳定的,即不健壮[3]
2.准确性和可解释性之间没有权衡
福布斯写道"更复杂,但也可能更强大的算法,如神经网络,包括随机森林在内的集成方法,以及其他类似的算法牺牲了透明性和可解释性【可解释性,根据以上定义】以获得功率、性能和准确性【4】。按照同样的思路,DARPA [6]。真的吗?

Learning Performance vs Explainability (Interpretability according to the definitions above) [6]
不。准确性和可解释性之间没有权衡。罗生门集提供了一个证明:认为数据允许一个相当准确的预测模型的大集合存在。因为这组精确的模型很大,所以它通常包含至少一个可解释的模型。这个模型既可解释又准确。【2】
为什么我们不马上建立一个可解释的模型呢?
回到我们对可解释性的定义
在这篇博文的开始,我们介绍了可解释 ML 的概念。根据我们的定义,可解释的是那些不是黑箱的算法。这到底是什么意思?
如果我们看看罗生门集[2,10]:

Rashomon sets: existence of a simple but accurate model [10]

Rashomon sets: classes of functions F2 that can be approximated with functions from classes F1 within δ using a specified norm [10]
换句话说,可解释性是数学家标记为透明(黑盒的反义词)[9]的函数类的一个属性。
例如,线性模型类比深度神经网络更容易解释的事实似乎完全没有争议。然而,从实践的角度来看,线性模型并不比深度神经网络、更具可解释性,特别是对于高维度模型或存在大量工程特征的情况。线性模型的权重可能看起来很直观,但它们在特征选择和预处理方面可能很脆弱【9】。
换句话说,从业者对可解释性的定义中没有任何内容表明,属于可解释模型类别的模型将被关键决策者(例如:法官)或关键利益相关者(例如:申请假释的囚犯)理解。在我们对可解释性的定义中,没有任何东西暗示一个模型在可解释性方面最终会满足它的需求。
“但是在地下的某个地方,将会有各种各样的计算不可约性,我们永远也不能真正把它们带入人类理解的领域”——史蒂夫·沃尔夫勒姆[8]
可解释性的许多方面
从利益相关者的角度来看,使用一个可解释的模型类别,其中可解释的意思是透明的(与黑箱相反),并没有带来太多的东西。那么可解释性的定义应该是什么呢?不幸的是,可解释性有很多方面。
不同的任务可能需要不同的解释需求。[12]提供了一个非详尽的假设列表,说明是什么使任务在解释需求上相似:
- 全局与局部:全局可解释性意味着知道总体上存在什么模式(比如控制星系形成的关键特征),而局部可解释性意味着知道特定决策的原因(比如为什么某个贷款申请被拒绝)。前者在以科学理解或偏见检测为目标时可能很重要;后者是当一个人需要一个具体决定的理由时。
- 面积,不完整的严重程度:问题公式化的哪一部分不完整,有多不完整?一方面,人们可能会对自动驾驶汽车如何做出决策感到好奇。另一方面,可能希望检查特定的场景列表(例如,导致汽车偏离道路 10 厘米的传感器输入集)。在这两者之间,人们可能想要检查一个一般属性—安全的城市驾驶—而不需要场景和安全标准的详尽列表。不完整的严重程度也可能影响解释需求。
- 时间限制。用户能花多长时间来理解解释?
- 用户专业知识的性质。用户在任务中的经验如何?
简而言之,我看不到我们有任何机会在短期内就可解释性的一个独特的、银弹定义达成一致。
另一方面,我们有奥卡姆剃刀和泛化理论方法,旨在将算法的泛化属性表达为模型复杂性定义的函数[13]。一般化理论有一个严格的、公认的问题定义;是的,复杂性很可能与可解释性负相关(不管定义是什么)。为什么我们不把奥卡姆剃刀作为我们的指导原则呢?
结果
- 当前对可解释性(可解释的模型不是黑盒)的定义并没有给模型的涉众带来任何价值
- 奥卡姆剃刀原则应该仍然是指导原则
- 可解释的 ML 方法论(例如:LIME [11])提供了不可靠的解释。然而,在数据科学家手中,它们仍然是模型理解和调试的有价值的工具
参考
[0] xkcd,曲线拟合,【https://xkcd.com/2048/
[1]https://twimlai . com/twiml-talk-290-the-problem-with-black-box-with-Cynthia-rudin/
[2]辛西娅·鲁丁。"请停止解释高风险决策的黑箱模型." arXiv 预印本 arXiv:1811.10154 (2018)。
[3] Alvarez-Melis、David 和 Tommi S. Jaakkola。"论可解释性方法的稳健性." arXiv 预印本 arXiv:1806.08049 (2018)。
[4]理解可解释的 AI,福布斯,2019 ( 此处)
[5]奥卡姆的威廉
[6] XAI 计划更新,DARPA,(链接)
[7] Keith O'Rourke,可解释的 ML 与可解释的 ML
[8]史蒂夫·沃尔夫勒姆,逻辑,可解释性和理解的未来,(链接)
[9]利普顿,扎卡里·c .〈模型可解释性的神话〉。arXiv 预印本 arXiv:1606.03490 (2016)。(此处)
[10] Semenova、Lesia 和 Cynthia Rudin。"罗生门曲线和体积的研究:机器学习中一般化和模型简单性的新视角." arXiv 预印本 arXiv:1908.01755 (2019)。
[11]里贝罗、马尔科·图利奥、萨梅尔·辛格和卡洛斯·盖斯特林。“我为什么要相信你?:解释任何分类器的预测。第 22 届 ACM SIGKDD 知识发现和数据挖掘国际会议论文集。ACM,2016。(此处)
[12]多希-维勒兹,大结局,和被金。"迈向可解释机器学习的严谨科学." arXiv 预印本 arXiv:1702.08608 (2017)。(此处)
[13] Mattia Ferrini,一般化界限:依靠你的深度学习模型(此处)
浅层人类和深层人工智能
抱怨或对人工智能征税不会有任何帮助。放下你的智能手机,努力工作。

“How can I prevent AI to take my job”… not by spending your day on Instagram.
这是波士顿四月的一个寒冷多雨的星期二晚上,这里的春天通常比日历晚一周,更典型的是几个月。波士顿通常在六月之前不会收到天气应该变暖的通知。
除了作为我的许多教授朋友的客座讲师,在波士顿庞大的学术圈里给许多学生授课,还能做什么?
因此,我今年第 n 次讲述这个故事,我和我的两个联合创始人,如何从我们在波士顿大学艰苦而漫长的计算神经科学研究中开创了我们的人工智能公司。NSF,DARPA,NASA,然后是商业斗争,直到我们找到了在 3000 多万台设备上部署我们的人工智能的天堂。
虽然通常我的观众都是非常投入的学生,但这次他们中有一小部分人比平时更茫然地盯着我(雨?),特别是有一个人,在一个小时的课上,花了 59 分钟盯着她的 iPhone 翻东西(Instagram?谁知道)偶尔的微笑和咯咯的笑声。
“还好…”,我想..“……。人工智能和计算神经科学并不适合所有人。
但是一个醒着的学生问了我一个问题,我脑海中渗透的智力拼图开始在我没有意识控制的情况下拼凑起来。
Max,谢谢你的演讲,我有一个关于人工智能和工作的问题:随着人工智能变得越来越聪明,我们如何确保人类仍然有工作可做
我的大脑自动合成了答案,幸运的是在出来之前被堵在了嘴里:
“不是靠在 Instagram 上玩 1 小时课 59 分钟”。
我们深刻的思想变得有多肤浅?
我经常被问到这个问题。随着人工智能变得越来越聪明,我们如何才能生存下来并仍然获得工作?
是的。你猜对了。不是通过做我们正在做的事情。
我们都知道社会在变化。甚至从我父亲那一代到我这一代,我们在学校的学习方式也发生了巨大的变化。我父亲曾经告诉我,在意大利南部的学校里,他是如何学习但丁的《神曲》的——全部都是记忆中的!——并要求背诵其中任意一段来证明。(我不能……)
虽然现代自称的教育专家可能会说“背诵不是学习”,但我认为这是至高无上的废话。也不是标准化考试,我们的孩子在美国学校被洗脑。
问题是:用你的大脑做一些困难的事情是目标。
无论是记忆还是求解量子场方程系统,找出亚原子粒子的最终位置,都无关紧要。用脑努力才是关键。
这与今天的学生(以及其他非学生)的想法完全相反。
书是给=_$^=#+的!,随便看了一条推文。心态是在一堆话题上花最少的时间,然后继续前进。浅 vs 深。
有趣的是,将从浅薄的人类手中夺走工作的人工智能被称为深度学习。
失败者做什么?提出税收。
《NYT》最近的一篇文章题为“不要和机器人战斗。向他们征税“有天才般的洞察力,提出我们应该向他们征税,而不是与人工智能和机器人作战。
诺奖就在这里,现场。

Great idea: let’s tax the crap out of (robots) humans!
我的办公室在波士顿港口前,当新生的美国向其他想对他们征税的英国诺贝尔奖获得者伸出手指时,茶被扔进了水里。众所周知,英国的结局很好。
这种态度将最终导致我们在人工智能方面的灭亡。相反,努力工作来提高自己,一个人只是向另一个更好的人征税,希望像寄生虫一样生活在那些每天流汗创新并将人类推向下一个前沿的人的工作中。从基因组学到材料科学等不同领域的成千上万的创新者也是如此……对他们的想法征税吗?
不,NYT 作家,去工作吧,拿出点有价值的东西来,我不会为你的食物和假期买单。
解决方法:好好学习,更加努力
那么,我们该何去何从呢?随着人类越来越肤浅,而 AI 越来越深入,人们害怕自己的工作被 AI 抢走。
我的问题是:是什么让我们值得继续登上领奖台的第一步?我们是否在尽最大努力利用大自然赋予我们的思考和推理的奇妙天赋?
如果答案是肯定的,你(和我们作为一个物种)就在正确的轨道上。
答案很可能是否定的
在对艾指指点点之前,我鼓励每个人做一些反省,并仔细检查我们的生活和思考是否充分发挥了我们的潜力。这绝不是要引起像“我们只使用了 2%的脑力”这样愚蠢观念的共鸣(你试过用 100%的肌肉走路吗?试试看,你会看起来像一只癫痫醉酒章鱼)。
它告诉我们:我们变得如此懒惰,以至于我们不应该指责人工智能,而是应该把我们的屁股从椅子上拿开,把我们的大脑从我们正在使用的任何令人麻木的社交媒体应用上拿开,并打开大脑。
最终,我(也没有人……)无法告诉你该做什么、该学什么、该把你的全部创造潜力奉献给什么。**

Winning is possible when one works hard.
但肯定有正确的方法去做:带着激情、创造力和信念去做。人工智能没有,人类有。
把该死的社交媒体电话放下,这样你就有时间做了。
那么,AI 将会有一个强硬的,也许是不可战胜的对手。
浅层神经网络

当我们听到神经网络这个名字时,我们觉得它由许多许多隐藏层组成,但有一种神经网络只有少数几个隐藏层。浅层神经网络仅由 1 或 2 个隐藏层组成。理解一个浅层神经网络能让我们深入了解深层神经网络内部到底发生了什么。在这篇文章中,让我们看看什么是浅层神经网络及其在数学环境中的工作。下图显示了一个具有 1 个隐藏层、1 个输入层和 1 个输出层的浅层神经网络。

神经元
神经元是神经网络的原子单位。给定一个输入,它提供输出并将该输出作为输入传递给后续层。一个神经元可以被认为是两部分的组合:

- 第一部分使用输入和权重计算输出 、Z 。
- 第二部分对 Z 进行激活,给出神经元的最终输出 A 。
隐藏层
隐藏层由各种神经元组成,每个神经元执行上述两种计算。存在于我们的浅层神经网络的隐藏层中的 4 个神经元计算如下:

在上面的等式中,
- 上标数字【I】表示层数,下标数字 j 表示特定层中的神经元数。
- X 是由 3 个特征组成的输入向量。
- W[i]j 是与层 i 中存在的神经元 j 相关联的权重。
- b【I】j是与层 i 中存在的神经元 j 相关联的偏置。
- Z【I】j是与层 i 中存在的神经元 j 相关联的中间输出。
- A【I】j是与层 i 中存在的神经元 j 相关联的最终输出。
- 适马 是乙状结肠的激活功能。数学上它被定义为:

我们可以看到,上面的 4 个方程似乎是多余的。因此,我们将它们矢量化为:

- 第一个等式在单个矩阵乘法中计算所有中间输出 Z 。
- 第二个等式计算单个矩阵乘法中的所有激活 A 。
浅层神经网络
使用各种隐藏层来构建神经网络。现在我们知道了在特定层中发生的计算,让我们理解对于给定的输入 X ,整个神经网络如何计算输出。这些也可以称为 正向传播 方程。

- 第一个等式计算第一个隐藏层的中间输出Z【1】。
- 第二个等式计算第一个隐藏层的最终输出A【1】。
- 第三个等式计算输出层的中间输出Z【2】。
- 第四个等式计算输出层的最终输出A【2】,这也是整个神经网络的最终输出。
激活功能
我们知道神经网络基本上是一组数学方程和权重。为了使网络健壮,以便它在不同的场景中表现良好,我们利用激活功能。这些激活函数在神经网络中引入了非线性特性。让我们借助我们的浅层神经网络来尝试理解为什么激活函数对于任何神经网络都是至关重要的。
如果没有激活函数,我们的浅层神经网络可以表示为:

如果我们将等式 1 中 Z[1]的值代入等式 2,则我们得到以下等式:

如您所见,输出将变成新的权重矩阵 W 、输入 X 和新的偏差 b 的线性组合,这意味着隐藏层中存在的神经元和隐藏层中存在的权重没有任何意义。因此,为了在网络中引入非线性,我们使用激活函数。
有许多激活功能可以使用。其中包括TanhReLU漏液 ReLU 等等很多。对所有层使用特定的激活功能不是强制性的。您可以为特定层选择激活功能,并为另一层选择不同的激活,依此类推。你可以在这篇文章中读到更多关于这些激活功能的内容。
重量初始化
我们知道一个神经网络的权重矩阵 W 是随机初始化的。有人可能想知道,为什么不能用 0 或任何特定的值初始化 W 。让我们借助我们浅薄的神经网络来理解这一点。
设第一层的权重矩阵 W1 和第二层的权重矩阵 W2 初始化为 0 或其他值。现在,如果权重矩阵是相同的,隐藏层中神经元的激活将是相同的。此外,激活的导数将是相同的。因此,该隐藏层中的神经元将以类似的方式修改权重,即在特定隐藏层中具有多于 1 个神经元没有意义。但是,我们不想这样。相反,我们希望隐藏层中的每个神经元都是唯一的,具有不同的权重,并作为唯一的功能工作。因此,我们随机初始化权重。
最好的初始化方法是 Xavier 的初始化 。数学上它被定义为:

它陈述了特定层【l】的权重矩阵W从正态分布中随机选取,其中 均值μ= 0方差σ=层 L1中神经元数量的乘积倒数。所有层的偏置 b 初始化为 0。
梯度下降
我们知道神经网络的权重是随机初始化的。为了使用神经网络进行正确的预测,我们需要更新这些权重。我们更新这些权重的方法被称为梯度下降。让我们用一个计算图来理解这一点。

在上图中,正向传播(用黑线表示)用于计算给定输入 X 的输出。反向传播(用红线表示)用于更新权重矩阵【W[1】】、W[2]* 和偏差【b[1】、b[2】。这个是通过计算图中每一步输入的导数来完成的。我们知道损耗 L 在数学上定义为 :*

使用上述损耗等式 L 并使用 sigmoid 函数作为隐藏和输出层的激活函数,借助导数的链式法则,我们计算如下:

上面的等式可能会令人困惑,但是它们非常适合梯度下降。在 的等式中,dZ【1】, ***** 表示乘积明智乘法,【σ’**表示σ的导数。**
我强烈建议读者,如果他们懂微积分的话,自己算出上面的方程,以便更好地理解梯度下降是如何工作的。
因此,在这个故事中,我们研究了浅层神经网络如何在数学环境中工作。尽管如此,我已经尽可能详细地解释了一切,如果你觉得缺少了什么,请查看我以前的帖子,或者在下面的评论区提出你的疑问。
参考
感谢读者阅读了这个故事。如果你有任何问题或疑问,请在下面的评论区提问。我将非常乐意回答这些问题并帮助你。如果你喜欢这个故事,请关注我,以便在我发布新故事时获得定期更新。我欢迎任何能改进我的故事的建议。
SHAP:分析模型可解释性的可靠方法

by Steven Wright on Unsplash
我开始了这个关于可解释人工智能的系列博客,首先理解了准确性与可解释性之间的平衡,然后继续解释了一些基本的模型不可知技术,以理解模型预测背后的原因,最后解释了石灰的诀窍。以下是这些博客的链接,供您快速参考:
在这篇博客中,我将谈论一种最流行的模型不可知论技术,它被用来解释预测。 SHAP 代表SHapleyAadditive exPlanations。
通过结合来自 合作博弈论 和 局部解释 的概念获得匀称的值。给定一组玩家,合作博弈理论定义了如何在所有合作的支付者之间公平合理地分配收益。这里的类比是:玩家相当于独立的特征,收益是实例的平均预测减去所有实例的平均预测之间的差。
每个特征的 SHAP 值表示在调节该特征时预期模型预测的变化。对于每个特征,SHAP 值解释了解释平均模型预测和实例的实际预测之间的差异的贡献。从以前的帖子中选取相同的示例,我们想要预测实例拖欠信用卡付款的概率,给定年龄、性别和限额余额作为独立特征。
计算独立特征 SHAP 值的步骤:年龄。
- 对于每个实例,性别和极限平衡保持不变
- 年龄的值被从分布中随机选择的值替换
- 该模型预测了这一特定模拟实例的违约概率
- 所有实例的平均预测与该特定实例之间的差异归因于随机采样的年龄独立特征
- 对性别和极限平衡的所有组合重复步骤 1 至 4
- 年龄的 SHAP 值是所有可能组合的所有边际贡献的加权平均值
请参考我之前在 LIME 上发表的帖子,了解我挑选来解释的用例的细节。让我们探索 Python 中的 SHAP 包提供的一些基本视图。每个特征的 SHAP 值的全局视图:
1.每个特征的 SHAP 值的全局视图:

这是一个非常有用的视图,它在全局级别上显示了与平均模型预测相比,每个特征的贡献方向。右侧的 y 轴表示相应的特征值是低还是高。每个点代表数据中的一个实例,这就是为什么您可以看到一簇点,表明数据中有许多具有该特定 SHAP 值的实例。
- PAY_0 表示上月的账单还款情况。这告诉我们, PAY_0 特性的值越高(由红色表示),该实例拖欠信用卡付款的可能性就越大。
- LIMIT_BAL 表示信用余额。这告诉我们降低 LIMIT_BAL 特性的值(用蓝色表示)
2.单个实例视图
再次,我个人发现分析正确预测的实例和错误预测的实例。这只是给出了导致错误预测的特征的感觉。这里提到了基本(平均)值,并显示了每个特征对预测的贡献。指示红色的特征有助于预测高于基数,即趋向违约,而指示蓝色的特征有助于预测低于基数,即趋向非违约
从我之前的帖子中拿起同样的例子。不正确预测为非默认(实际:默认)

此实例被归类为非违约的主要原因似乎是 PAY_0 = 0。这表明,上个月的付款及时完成无疑增加了这种情况被预测为非违约的可能性。您可以进一步分析导致预测非默认的其他连续要素的行为。您可以首先检查他们的总体分布统计数据和每个默认和非默认标签的分布统计数据。
为什么 SHAP 是更受欢迎和可靠的技术?
**1\. Local Accuracy:**
解释模型应该与原始模型相匹配
**2\. Missingness:**
如果简化输入表示要素存在,则缺失要求原始输入中缺失的要素没有影响
**3\. Consistency:**
一致性表明,如果一个模型发生变化,使得一些简化输入的贡献增加或保持不变,而不管其他输入,该输入的属性不应减少
**4\. Efficiency:**
由于 SHAP 值的基础是基于计算博弈论,这是唯一的方法,可以分配功能的增益失败。
**5\. Global comparison:**
SHAP 值提供了一种在全局级别比较要素重要性的方法。您还可以将数据集从全局数据集更改为感兴趣的子集数据集。
缺点:
**1\. Computation time:**
随着特征数量的增加,特征的可能组合的数量呈指数增加。这反过来增加了计算 SHAP 值的周转时间,近似是唯一的解决办法。
**2\. Order of feature selection in all possible combinations:**
典型地,在解决真实世界的问题时,目标与独立特征非线性相关,并且在独立特征之间也存在一些相关性。在这种情况下,在组合中选择特征的顺序很重要,并且会影响 SHAP 值。
**3\. Simulation of scenarios:**
SHAP 不返回模型,像石灰。因此,如果你想模拟某个特定功能的增加会对输出产生多大影响,那么用 SHAP 是不可能的。
**4\. Correlation amongst independent features:**
同样,在大多数现实世界的问题中,独立的特征是相互关联的。在这种情况下,当我们从特征的边缘分布中取样时,可能会产生在现实世界中不可能产生的实例。
朱庇特笔记本的副本
如果您想要一份上面有使用案例的 Jupyter 笔记本,请在下面的评论部分或通过“联系我们”部分告诉我们您的电子邮件 ID。
下一步是什么?
在下一篇博客中,我计划解释 Python 中的 SHAP 包必须提供的一些其他可视化功能。
这个可解释的人工智能领域正在迅速发展,在工具和框架方面有很多新的发展。请在评论区写下你对博客的反馈,以及你在这个领域使用的最新工具。此外,如果你想让我写任何特定的主题,请发表评论。
这些内容最初发表在我的个人博客网站:http://datascienceninja.com/。点击此处查看并订阅即时接收最新博客更新。
借助土星云,您可以在任何地方共享、协作和运行

现在创建自己的服务器很容易
分享见解是每个数据科学家工作的一部分。
昨天,我发布了我的一个 Google Colab 笔记本。这是一个很棒的免费平台,但它不能解决我的主要用例。
我希望我的受众(主要是业务用户)看到我的分析被正确地呈现出来。
如果他们喜欢,可以自己运行它(在 Google Colab 上,任何新用户都必须使用他/她自己的 Google API 密钥,这对一些人来说是一个障碍)
另外,我真的不想像大多数免费软件那样,把自己局限在有限的计算上。
这就是我了解土星云的地方。现在它是由 Anaconda 的联合创始人之一,的 Hugo Shi 制作的产品。
是的,就是您用来在系统中创建数据科学环境的那条蟒蛇。
所以我必须调查一下。
Anaconda 自己解决了一个特殊的用例。下载和管理 Python 环境。
土星云解决在云端管理 Python 环境 。
怎么会?
请继续关注,因为这正是本文的内容。
什么是土星云?
所以让我首先说明一下土星云是什么。
我敢打赌,我们大多数人都尝试过在我们的机器上安装 Python。在安装 Jupyter 笔记本/实验室时遇到了麻烦。
只是在做了大量的摆弄之后,我们才开始真正的工作,即数据科学。
或者,当你需要大型计算时,你可能会遇到困难,于是你去找 AWS(亚马逊网络服务)/GCP(谷歌云平台)。这些系统的创建方式对我们很多人来说似乎很神秘。
是的,你并不孤单。 我自己也尝试过用 GCP 。实际上,我没能做到。
现在我们数据科学家热爱自动化。 我们为什么不摆脱这种设置数据科学工具的卑微过程?
这就是土星云的意义。
它为我们这些对设置不太感兴趣但对数据科学非常感兴趣的数据科学家提供了一种简单的入门方法。
它还帮助我们:
- 为我们的项目设置自定义计算
- 在网络上共享内容
开始并设置自定义计算

所以让我们开始吧。您可以从免费层开始。
你首先需要在 SaturnCloud 注册,然后进入仪表盘。

现在,为一个项目创建一个笔记本/实验室就像 一样简单,只需选择磁盘空间和机器大小,然后点击创建按钮 。谈到 配置 ,SaturnCloud 提供了不同的选项。

这些配置实际上可以满足许多实际数据科学项目的需求。
使用最后一个配置选项,设置带有 GPU 的机器是轻而易举的事情。所以你可以和 AWS/GCP 收购 GPU 的漫长流程说拜拜了。
您也可以为您的项目定义一个 自动关闭持续时间 。这是我特别喜欢 SaturnCloud 的一个功能。我已经被蛰过很多次了,我的 Google Colab 笔记本服务器在一个小时不活动后就关闭了。
在这里,我可以设置我的笔记本关机持续时间为 1 小时,6 小时或从不关机。 从来都不是我喜欢的。
您还可以选择使用高级选项 使用 apt-get、pip 和 conda 选择默认配置。但主要是我一开始并不担心,因为它预装了科学的 python 堆栈。
并且 一旦我创建了笔记本/实验室 ,我就可以使用终端本身下载任何我需要的新包。
因此,一旦您创建了 Jupyter 笔记本/实验室,您应该会在您的仪表板上看到以下内容:

在这里你可以点击去 Jupyter 实验室。瞧,你已经开始运行了。

在网络上共享内容

这是 SaturnCloud 提供的另一个对我非常有价值的功能。
我是一名数据科学家,我需要与我的业务合作伙伴共享报告。此外,我是一个技术博客,我需要与我的读者分享代码。
现在,我分享的报告需要在网络上正确呈现。
我与读者分享的代码需要是可执行的。
土星云为我解决了这两个用例。
怎么会?
我先举个例子。我渴望与我的读者分享对足球数据的分析。我将从我最喜欢的博客文章之一中摘录代码— 每个数据集的 3 个令人敬畏的可视化技术
首先,我用 Jupyter Lab 正常编码。我在那里为我的项目创建了一个名为visproject的文件夹。我把数据上传到那里,然后在我的 jupyter 笔记本的那个文件夹里创建并编写代码
现在,为了分享我的工作,我需要做的就是在 Jupyter 实验室中打开一个终端并运行命令:
publish visproject

现在去出版部门。您应该看到:

现在我有不同的选择。我可以将我的笔记本保密(私人)/公开,我也可以点击“我将为其他人支付执行此笔记本”。
一旦您点击“我将为他人支付执行费用”,您应该会看到类似以下内容:

我已经公开了这个笔记本,并且可以使用上面分配的资源执行。对设置满意后,您可以单击保存按钮。
随后,点击预览链接。

这就是它的样子。结果是:

我喜欢代码块和所有可视化的渲染方式,没有任何混乱,就像我们在 Google Colab 笔记本上看到的那样。它看起来就像是一份预期的报告。
我也喜欢所提供的“土星运行”选项,用户可以直接点击运行这段代码,而不需要任何明确的登录。
一定要试着自己运行它这里。我相信你会喜欢的。
费用:
我喜欢土星云的一点是它的相关成本。
这并不是我对许多付费产品的说法。
我可以毫不费力地获得土星云的所有功能,对于我目前使用的基本计划,我只需要支付云提供商的费用加上一点点开销。
现在这是个人喜好的问题,但我愿意为所提供的服务质量和避免的麻烦支付这笔非常少的间接费用。
事实上,我收回了大部分费用,因为虽然在 AWS/GCP 上停止服务器有点麻烦,但对土星云来说却是小菜一碟。
这是您自己要查看的费用列表。

结论
土星云让数据科学家的生活变得更容易,因为他几乎不用花费任何费用就可以处理大量多余的工作。
我并不是说我永远不会创建定制服务器,但是只要我能使用土星云,我就会避免这样做。
即使当我确实需要创建一个定制服务器时,如果我的公司允许,我也可能会查看土星云提供的企业帐户。
正如他们在网站上所说:
在花时间从事数据科学以及构建数据科学工具之后,我们都非常熟悉数据科学、技术和开发人员操作交汇处的摩擦和痛点。
我们不能为您做模特——但我们可以减轻一些痛苦。
那你还在等什么?只需使用您自己的服务器开始数据科学。它肯定有它的好处。
您可以尝试自己运行已发布的土星云笔记本。
我以后也会写更多这样的帖子。在关注我或者订阅我的 博客 了解他们。一如既往,我欢迎反馈和建设性的批评,可以通过 Twitter @mlwhiz 联系。
分享π:尊重被忽视的数学常数
酷数学的有趣回顾
圆周率得到了一切——一年中有两天(当然是 3 月 14 日和 7 月 22 日)享受甜蜜的放纵;一种人类对记忆和计算更多位数的执念(注意 39 位数计算可观测宇宙的周长精确到一个原子);流行文化中的大量引用。

Source: https://en.wikipedia.org/wiki/Pi#/media/File:Record_pi_approximations.svg
当然,圆周率很酷,但不是因为这些原因。圆周率在数学中无处不在——这是一个非常深刻的现实。欧拉恒等式使用圆周率将 5 个最重要的数字联系在一起。海森堡测不准原理包含圆周率,说明物体的位置和速度不能同时精确测量。Pi 是许多公式中的归一化常数,包括高斯/正态分布。在 2 处评估的 Reimann 函数收敛到π的因子。

但是这篇文章将向那些不太受重视、有时被遗忘、被忽视的数学常数致敬。深度不亚于圆周率,魅力不亚于圆周率,让我们开始吧!
欧拉数:e
T4 有几个特征。它是唯一的基数 a ,通过它,y = a ^x 是它自己的导数,在 x = 0 时有一个 1。也是无限指数增长的极限。它满足下面蓝色突出显示区域所示的积分范围。

The five colored regions are of equal area, and define units of hyperbolic angle along the hyperbola. Source: https://en.wikipedia.org/wiki/E_(mathematical_constant)#/media/File:Part38figure02.gif
e 可以写成连分数,下面是其中的两个。


这一现实产生了一个有趣的程序,叫做 spigot 算法,可以用来计算许多数字 e (还有圆周率……)。一个为 e 编写的 spigot 算法的 Python 实现是这里的,我在下面做了一些修改。
import numpy as npdef continued_fraction(a, b, n, base=10):
(p0, q0), (p1, q1) = (a(0), 1), (a(1) * a(0) + b(1), a(1))
k = 1
cnt = 0
while cnt <= n:
(d0, r0), (d1, r1) = divmod(p0, q0), divmod(p1, q1)
if d0 == d1:
cnt += len(str(d1))
yield d1
p0, p1 = base * r0, base * r1
else:
k = k + 1
x, y = a(k), b(k)
#print(x)
#print(y)
(p0, q0), (p1, q1) = (p1, q1), (x * p1 + y * p0, x * q1 + y * q0)#Generate
e = ''
for digit in continued_fraction(lambda k: 1 if k <= 1 else 4 * k - 2,
lambda k: 2 if k <= 1 else 1,
500000):
e += str(digit)
其工作原理是使用连分式在两个分数估计值(p0/q0 和 p1/q1)之间迭代。当观察到的那些分数的小数收敛时,你可以去掉一致的数字(例如:23/27 = 0.315068 和 34/108 = 0.314815 会让你去掉 0.31)。更有帮助的是,我们可以在去掉那些数字并保持分数的分子/分母小的情况下,对分数进行“重组”。
函数中的λ函数表示连分数的分子/分母。我们将数据存储为一个字符串,以简化成千上万位数字的存储。我通过另一个列表验证了 100k 位的算法。
2004 年,一个著名的谷歌广告牌上写着 e.

现代的 CPU、编程语言和 Google 本身使得解决这个问题变得容易得多,但它仍然超级有趣!在生成足够多的数字 e 开始工作后,我们需要生成 10 位数的素数——素数将是我们的下一个“常数”。
质数
好吧…质数并不是真正的常数,但它们仍然非常酷。最基本的是,质数是一个大于只能被 1 整除的数。这几乎是初等的,但是回想一下,任何本身不是质数的数都可以写成质数的唯一乘积。也就是说,质数是所有其他数的积木!从几个方面来说都是如此:
高达 4*10 ⁸的哥德巴赫猜想证明,每个大于 2 的整数都可以写成两个素数之和(例如 12 = 7 + 5)。一个令人信服的启发指出,随着数字的增长,有越来越多的机会来构建两个这样的数字:例如,100 有 6 对(3+97,11+89,17+83,29+71,41+59,47+53)。下图通过散点图说明了这一点。

Scatter Plot of Available pairs that satisfy Goldbach’s conjecture.
拉格朗日证明了每一个正整数都是四个平方的和(即 310 = 17 + 4 + 2 + 1。假设这个公式是这样的:在公式 x + 3y + 5z 中,3 个数产生一个正方形(例如 2+ 14 + 42 = 25,也就是 5)。
另一个超级酷的事实是:蝉一生中的大部分时间都是在地下度过的,但在 7、13 或 17 年后会在地面上飞行、繁殖,并在几周内死亡。生物学家假设,质数繁殖周期长度是一种进化策略,以防止捕食者与它们的繁殖周期同步。
素数有很多种,其中一种会出现在下面计算的常数中。“孪生素数”是指比另一个素数多 2 或少 2 的素数(例如 13 是孪生素数,因为它比 11 多 2)。斐波纳契素数是指同时也是斐波纳契数的素数。梅森素数有助于生成非常大的素数,它遵循 2^n — 1 的形式。
已知最大的素数有超过 2490 万位数,但没有生成素数的“公式”。许多算法是已知的,其中最容易理解的被称为厄拉多塞筛。该算法首先列出从 2 到 n 的所有整数,或者从 3 到 n 的所有奇数,以便进行一些初始性能调整。接下来(使用第一个公式),计算 2 的所有因子(即 21 = 2,22 = 4,2*3 = 6,等等。),并从您的列表中删除这些项目。移动到你清单中剩下的下一个项目(3),去掉所有的因素。当你移动到下一个项目时,4 不再在列表中,所以你移动到 5。随着你的前进,你逐渐从列表中剔除非素数。
下面的算法使用这种基本逻辑,效率很低。关于模数计算的一些编程注意事项:
//是楼师。7//2 返回 3(并丢弃 0.5 余数)。
%是模数运算符。7%2 返回 1(因为 7/2 = 3 + 1 /2)
|是位二进制 or 运算符。13|14(在二进制 1101 和 1110 中)对整数逐个取或,所以结果是二进制的 1111,也就是 15。
def primes(n):
""" Input n>=6, Returns a array of primes, 2 <= p < n """
sieve = np.ones(n//3 + (n%6==2), dtype=np.bool)
for i in range(1,int(n**0.5)//3+1):
if sieve[i]:
k=3*i+1|1
sieve[k*k//3 : : 2*k] = False
sieve[k*(k-2*(i&1)+4)//3 : : 2*k] = False
return np.r_[2,3,((3*np.nonzero(sieve)[0][1:]+1)|1)]
厄拉多塞的正常筛子大约在多项式时间内运行,这意味着随着 n(你的最大可能素数)的增长,时间增长 n(大约…)。直观地说,这里的问题是你在多次消去同一个数:5M 的 n 将分别消去 4,849,845 次(3,5,7,11,13,17,19),但只有 1 次是必需的。上面的算法通过使用两个不同且更复杂的公式来计算非素数列表,减少了这种重复。
回到我们的谷歌广告牌。我们将 e_list 分割成 10 位数,然后使用素数列表检查它们是否是素数。我们只需要检查 100,000,因为 100,000 不是 11 位数字。
x10 = primes(int(1e5))
print(np.max(x10))e_list = [int(e[i : i+10]) for i in range(0, len(e))]for i in range(len(e_list)):
print(i)
n = 0
for j in range(len(x10)):
n += 1 if e_list[i] % x10[j] == 0 else 0
if n == 0:
print(e_list[i])
break
结果是第 99 位数字给我们 7,427,466,391,这是一个 10 位数的质数!后续事件相当普遍。
布伦和梅塞尔-默滕斯常数
质数出现在我们接下来要讨论的两个迷人的常数中。
布伦常数的形式如下——所有素数对的倒数之和。

数字收敛速度极慢,这本身就很迷人。这些分数的分母迅速减小——问题是,即使对于非常大的数,素数对也相对常见。事实上,布伦常数是未知的。使用所有孪生素数直到 10 ⁶(即 10 万亿亿)时,它被估计为~1.902160583104,但仅被证明小于 2.347。
回到谷歌:在破产的北电公司的专利拍卖中,该公司以出价 1902160540 美元收购布伦常量而闻名。随后的出价为 2,614,972,128 美元,反映了梅塞尔-默滕斯常数(约为 0.26149……)。
Meissel-Mertens 常数也称为质数倒数常数,其定义如下:


这个数字也不是确切知道,虽然收敛速度比布伦常数快得多。
黄金比例和斐波那契数
从欧几里得时代开始研究,黄金分割率在几何中起着重要的作用,并且有许多几何表示。




Left: Let A and B be midpoints of the sides EF and ED of an equilateral triangle DEF. Extend AB to meet the circumcircle of DEF at C. The ratio of AB/BC = AC/AB = The Golden Ratio. Right: A pentagram colored to distinguish its line segments of different lengths. The four lengths are in golden ratio to one another.


Left: The golden ratio in a regular pentagon can be computed using Ptolemy’s theorem. Right: A Fibonacci spiral which approximates the golden spiral, using Fibonacci sequence square sizes up to 34. The spiral is drawn starting from the inner 1×1 square and continues outwards to successively larger squares.
最后一个螺旋图像被称为斐波那契螺旋,代表了黄金分割率的一个迷人的特性:取连续斐波那契数的比率收敛到黄金分割率,我们将在下面讨论。
斐波那契数的形式是 F(n) = F(n-1) + F(n-2)。序列的开始是 1,1,2,3,5,8,13,21,34,55,89,144。
斐波那契数的一些有趣的性质:每个正整数都可以写成斐波那契数的和,其中任何一个数只使用一次。当你考虑到序列前面部分相对于后面部分的密度时,这可能是直观的。斐波纳契数是帕斯卡三角形对角线的总和(由上面两个数的总和构成的每一连续层构成)。

The Fibonacci numbers are the sums of the “shallow” diagonals (shown in red) of Pascal’s triangle.
如上所述,当你取连续的斐波那契数的比率时,黄金分割率收敛。比如 89/55 = 1.61818,接近真实值 1.61803。黄金分割率可以用无理数根 5 来精确定义。

黄金分割率也可以表示为一个连分式,所以我们可以使用上面的 or e- 生成算法来生成黄金分割率的数字。具体来说:

phi = ''
for digit in continued_fraction(lambda k: 1,
lambda k: 1,
500000):
phi += str(digit)
你会注意到,与 e 代码相比,运行上面的代码要花一些时间。500,000 要求算法返回 500,000 位,而不考虑连分数需要迭代多少次。黄金分割连分数收敛很慢,这有一定的道理:分母中的数字越大,重复出现的加法分数就越小。
结论
我们可以讨论更多的常量,但是这篇文章已经有点漫无边际了!我们没有得到第一个大二学生的梦想常数(由 0 和 1 之间的曲线 x^x 界定的区域,因不正确但如此方便的恒等式而得名(x+y)^n = x^n + y^n,这将使这样的积分更容易解决));统计数据中出现的 Gamma 和 Reimann zeta 函数的值;、或在随机随机行走和其他分析应用中出现的任何其他常数。
这篇文章的主要观点:数学超级酷!而且谷歌很有幽默感!
分享就是关心算法

Photo by @maur1ts
开源机器学习&共享代码或数据
重要的是要考虑可以共享的公开许可的技术和内容。我们应该设法更快地在世界不同地区分享有益的解决方案,这显然符合我们的利益。虽然一种解决方案不一定能从一个地方移植到另一个地方,但重要的是要考虑到当地环境的相关方面,并相应地进行调整。因此,我决定探索当前流行的开源框架以及其他不太知名的框架。首先,我将从几个不同的与机器学习相关的开源解决方案开始,并继续讨论发布代码的平台。
“开源软件(OSS)是一种计算机软件,其源代码是根据许可证发布的,在许可证中,版权所有者授予用户研究、更改和向任何人出于任何目的分发软件的权利。”
机器学习(ML) 是计算机系统用来执行特定任务的算法和统计模型的科学研究,不使用明确的指令,而是依靠模式和推理。它被视为人工智能的子集。”
“Apache 许可证 是由 Apache 软件基金会编写的许可自由软件许可证。它允许用户出于任何目的使用软件、分发软件、修改软件以及根据许可证条款分发软件的修改版本,而无需考虑版税。”
这并不意味着整个应用程序或平台都可以改变。然而,在某些情况下,它可能是合作开发的,尽管情况并非总是如此。大多数大型开源项目都与一家或几家大型技术公司有关联,有时归谷歌、微软、亚马逊等公司所有。2017 年,谷歌推出了对人工智能的明确关注,声称它将成为一家“人工智能优先”的公司。因此,利用机器学习技术可用的开源平台开始探索与人工智能相关的解决方案并不是不合理的。
开源机器学习
有各种各样的参与者在为开源市场制作产品。当然,相当多的人似乎受到了构建软件的可能性的激励,这些软件可以促进对其云平台的热情使用——因此拥有最容易访问和最广泛的解决方案似乎是其中许多人的目标。然而,它可以被广泛使用,可以成为行业标准,并且是许多开发人员熟悉的框架。如果你在这个领域工作,那么熟悉一些不同的概念可能会有好处。
张量流
TensorFlow 是一个相对容易使用的开源机器学习(ML)框架。众所周知,它可以跨多种平台部署。因此,它是最广泛使用的机器学习框架之一,也可能是维护得最好的。TensorFlow 由谷歌大脑团队开发,供谷歌内部使用。于 2015 年 11 月 9 日在 Apache License 2.0 下发布。截至 10 月 8 日,这个由谷歌主导的项目已经在 GitHub 上有 69179 个提交和 2223 个贡献者,在其模型库上有 58095 颗星。
克拉斯
Keras 是一个开源软件库,旨在简化深度学习模型的创建。它是用 Python 编写的,可以部署在 TensorFlow、微软认知工具包和 Theano 等其他人工智能技术之上。Franç ois Chollet 是 Keras 的创始人,目前在谷歌担任人工智能研究员,他也是 Keras 开发的核心。根据 Keras 的说法,它认为自己是:“…一个为人类而不是机器设计的 API。Keras 遵循减少认知负荷的最佳实践:它提供一致的&简单 API,最大限度地减少常见用例所需的用户操作数量,并在用户出错时提供清晰可行的反馈。”首次发布是在 2015 年 3 月 27 日,首次稳定发布是在 2019 年 8 月 22 日。Keras 有 5,331 个提交,821 个贡献者和 44,571 颗星。
sci kit-学习
scikit-learn 最初发布于 2007 年,是一个为机器学习开发的开源库。这个传统的框架是用 Python 编写的,具有几个机器学习模型,包括分类、回归、聚类和降维。Scikit-learn 最初是由 David Cournapeau 在 2007 年作为 Google 代码之夏项目开发的。Scikit-learn 有 24,582 个提交,1,439 个贡献者和 37,343 个星级。
微软认知工具包
微软认知工具包是一个人工智能解决方案,它可以让你将你的机器学习项目提升到一个新的水平。微软表示,开源框架能够“训练深度学习算法像人脑一样工作。”最初发布于 2016 年。在 GitHub 上,它有 16,110 个提交,198 个贡献者和 16,455 颗星。
Theano
最初发布于 2007 年, Theano 是一个开源的 Python 库,允许你轻松地构建各种机器学习模型。由于它是最古老的图书馆之一,它被视为激励深度学习发展的行业标准。在 GitHub 上,Theano 有 28,090 次提交,331 个贡献者和 8,933 颗星。
咖啡
Caffe (用于快速特征嵌入的卷积架构)最初于 2017 年发布,是一个专注于表现力、速度和模块化的机器学习框架。开源框架是用 C++写的,附带一个 Python 接口。CAFFE 是一个深度学习框架,最初由加州大学伯克利分校开发。它是在 BSD 许可下的开源软件。BSD 许可证是一系列许可性自由软件许可证,对涵盖软件的使用和分发施加了最低限度的限制。在 GitHub 上,Caffe 有 4154 个提交,265 个贡献者和 29191 颗星。
共享代码的平台
重要的是要考虑共享数据的可能性。如果我们能够负责任地共享代码,这将是一个明显的优势。必须考虑与实现 2030 年议程的相关性。该议程承诺到 2030 年在全世界消除贫困和实现可持续发展,确保没有人被落下。代码或存储库本身当然不会以任何方式、形状或形式默认实现这一点。然而,如果我们希望在地球上实现更加公平和持续的存在,合作可能是我们社会的一个重要拼图或方面。按受欢迎程度排列,最大的平台如下:

Adjusted spreadsheet with comparison of source-code-hosting facilities ranked by Alexa rank
大多数共享代码的平台使用分布式版本控制系统来跟踪软件开发过程中源代码的变化。它被称为 git,由 Linus Torvalds (说瑞典语的芬兰裔美国开发人员)在 2005 年创建,用于开发 Linux 内核,其他内核开发人员为其最初的开发做出了贡献。它是为协调程序员之间的工作而设计的,但也可以用来跟踪任何一组文件中的变化。
开源代码库
美国公司 GitHub 使用 Git 为软件开发版本控制提供托管服务。该公司是微软的子公司,2018 年以 75 亿美元被收购。截至 2019 年 5 月,GitHub 报告拥有超过 3700 万用户和超过 1 亿(包括至少 2800 万公共存储库)。2014 年据估计,它是世界上最大的源代码主机。源代码是程序员使用计算机编程语言编写的一组指令和语句。该服务将由 Nat Friedman 领导,向微软云和人工智能执行副总裁 Scott Guthrie 报告。不得不说,GitHub 有机会注册非营利账号。然而,这些必须是非政府的,非学术的,非商业性的,非政治性的,没有宗教信仰。
一套合作式软件开发管理系统
由 VA Software 于 1999 年创立的 SourceForge 是第一家为免费和开源软件开发者提供集中位置来控制和管理软件开发并免费提供这项服务的提供商。SourceForge 是一项基于网络的服务,它为软件开发者提供了一个集中的在线位置来控制和管理免费和开源软件项目。SourceForge 是首批向开源项目免费提供这项服务的公司之一。自 2012 年以来,该网站一直在 Apache Allura 软件(根据上述 Apache 许可证)上运行。SourceForge 为自由/开源软件的开发者提供免费的主机和工具。然而,2015 年发生了一次权力滥用,导致许多用户离开平台或感到不满。
谷歌知识库
如果我们把目光从开源转向大型科技公司,谷歌的整体软件库(全球 95%的软件开发人员都在使用它)符合超大规模 4 系统的定义,证明了单一资源库模式可以成功扩展。谷歌有自己的 GitHub 替代品:云资源仓库。
联合国全球脉搏
从用户的角度来看,联合国全球脉动是联合国的一项倡议,旨在“为发展和援助项目带来实时监测和预测。”作为数据共享的一个例子,在 one one 项目页面的底部有一个链接,该链接指向一封电子邮件,这意味着你必须向联合国全球脉动办公室之一发送电子邮件,以请求访问代码。然而,在这些页面上共享代码并不总是这样。
数据共享计划
数据是另一个可以共享的点。最近在联合国全球脉搏的博客上有一篇文章,名为 数据和 AI 促进联合国大会 期间的进展。本文重点介绍了几个值得一提的项目。新任命的负责战略协调的助理秘书长(ASG)福尔克·蒂尔克会见了几个组织,讨论从发展角度使用数据的问题。
数据为现在倡议
数据为现在倡议是一项新的全球努力,旨在弥合数据促进发展方面的差距,副秘书长阿米娜·穆罕默德指出:“……我们可以通过共同努力解锁数据、保护人们的隐私和争取包容,开辟一条成功的道路。”在 Data for Now 活动期间,谷歌和可持续发展数据全球伙伴关系(GPSDD)宣布了一项协议,重点是为可持续发展目标开展跨平台地球观测合作。
饥饿地图平台
世界粮食计划署(WFP)和阿里巴巴周四展示了他们新的饥饿地图平台,该平台结合了不同的信息流——如天气、人口数量、冲突、危险、粮食安全、营养和市场信息——以近乎实时地监测和预测全球粮食安全形势。
全球数据共享空间
全球数据共享区(Global Data Commons)——目前由大约 70 个政府、组织和公司组成的非正式合作伙伴关系,致力于为可持续发展目标制定允许快速创新和安全使用人工智能的路线图。会议结束时制定了一项计划,旨在开发一个通用参考架构和治理框架,然后实施一个基于具体用例的工作模型。
这是#500daysofAI 的第 126 天。如果你喜欢这篇文章,请给我一个答复,因为我确实想提高我的写作或发现新的研究,公司和项目。
分享:上不上“AI for Everyone”课程?deeplearning.ai 的课程
参加吴恩达教授的“AI for Everyone”课程后的分享

https://www.coursera.org/learn/ai-for-everyone/
最近刚上了上图所示的课程,一门 AI 非技术入门课程,教授吴恩达, deeplearning.ai ,通过 Coursera 授课的AI for everybody。我不是在做广告,还是详细说说课程内容吧。相反,我想谈谈我的分享。下面是链接:https://www.coursera.org/learn/ai-for-everyone/。( Sik-Ho Tsang @中)
概述
- 包括哪些?
- 真的适合所有人吗?
- 懂 AI 的人需要吗?
- 已经学过吴恩达教授深度学习专业的人还需要吗?
1.包括哪些内容?
课程中有四个主要主题:
- 什么是 AI?
- 打造 AI 项目
- 在你的公司建立人工智能
- AI 与社会
1.1.AI 是什么?
我们可以看到,在第一节,它以一种大家都能理解的非常笼统的方式讲了什么是 AI 和机器学习(ML)。
1.2.打造 AI 项目
在这一节中,它谈到了如果我们需要应用 AI,ML 或数据科学,我们应该做些什么来将它们应用到我们的工作中?以及我们如何应用人工智能并与其他团队合作,以便公司可以降低成本或获得更多利润。
在本节中,也有一些简单的用例来说明。
1.3.在贵公司建立人工智能
在这一部分,它在中提到了更多关于如何在公司中建立人工智能的细节。还描述了更详细的用例。当启动一个人工智能项目时,我们应该避免哪些事情。当我们想在一个公司内部开始一个项目时,我们应该做些什么。
1.4. AI 与社会
最后,还提到了人工智能的伦理和偏见。我们如何减少 AI 的偏见等等。以及人工智能在未来如何影响我们的工作、社会和经济。
2.真的适合所有人吗?
是的,它是为每个人特别是那些以前不知道人工智能的人准备的。这里不涉及数学。每个人都可以很容易地理解材料。
对于那些正在经营企业的人,或者一些管理级别的人,如董事和经理,值得阅读一下它,以了解他们可以应用人工智能来改变业务的任何机会。
另外,了解人工智能的优缺点也很重要。
如果有现有的 AI 团队,了解 AI 的一些概念肯定有助于和 AI 团队的沟通。因为了解了 AI,就能知道 AI 能做什么,不能做什么。这也是对每个需要和 AI 团队一起工作的员工有用的地方。
3.懂 AI 的人需要吗?
是也不是。
对于这个问题,我觉得对于懂 AI,懂业务的人来说是不需要的。比如说,如果你已经在一个 AI 团队工作,并且和其他非 AI 团队合作得很好,我觉得没必要上这门课。
对于我来说,我只知道一些机器学习或深度学习,特别是在计算机视觉领域。我是深度学习专业化第四期课程(CNN)的导师(也是吴恩达教授讲授的深度学习系列课程。).对我来说,我也从这门课中学到了很多。
上了这门课,我可以了解更多计算机视觉(或者你的领域)之外的用例,比如制造业,临床诊断,招聘,市场营销,甚至农业等。(当然,有很多方法可以了解 AI 的用例比如在网上看文章或视频,或者参加 AI 研讨会讲座。如果你只是想了解更多的用例,我认为不值得参加这个课程。)
我学会了需要做什么才能让人工智能和业务团队相互交流。而且我还了解到了AI 团队内部的工作岗位。至少,当我需要在大学之外找工作时,它是有用的。
4。已经学过吴恩达教授深度学习专业的人还需要吗?
是也不是。
还是那句话,懂 AI 的,懂业务的,不需要。比如说,如果你已经在一个 AI 团队工作,并且和其他非 AI 团队合作得很好,我觉得没必要上这门课。
但如果你只上过深度学习专精,那么也值得上这门课。如前所述,它更多地谈论了 AI 团队如何在公司内部进行交互,以及如何与业务团队进行交互,这些都不包括在深度学习专业化中。
如果你不懂 AI,又想有一份 AI 的工作,我建议把“深度学习专精”和“ AI for Everyone ”都上。两者兼而有之,才是进入 AI 领域的第一步。之后更有可能对人工智能进行更多的研究。
如果只是想学习深度学习的核心,不如去上深度学习专精学技术的东西。当然,我只能分享我在课程中学到的东西。也许对你来说不是这样的。但希望这个故事能提供一些帮助。😃
嘘…建造伟大人工智能的秘密

我为这个有点点击诱饵的标题道歉,但在看到埃隆·马斯克的推文后,我不得不创造了上面的迷因:
我认为那很好笑…不管怎样,回到你的常规编程。
当你完成机器学习的博士学位时,他们会把你带到一个特别的房间,向你解释伟大的数据比你刚刚学到的所有花哨的数学和算法更重要
至少,我是这样想象的。我没有博士学位,所以我不能肯定地说,但似乎有可能。
数据>>算法
这就是构建伟大的人工智能系统的秘密——拥有大量的数据。我可以在这里结束这篇文章,但是让我们看看一些支持的事实:
F 这是论文结论的一部分深度学习的扩展是可预测的,来自百度一个小组的经验。这是他们研究结果的图表之一:

上述图表根据经验显示,“随着我们为最先进的(SOTA)模型架构增加训练集,深度学习模型的准确性以幂律形式提高。”基本上,您可以使用幂定律来预测在训练数据增加的情况下,您的模型的性能会提高多少。现在,停下来想一想。改善深度学习模型的一个几乎肯定的方法是收集更多数据。对于无休止的模型调整和发明新架构的尝试,很难说同样的话。谷歌在他们 2009 年的论文数据的不合理有效性中也看到了这一点。
其次,GitHub 充满了开源的最先进的算法,但公开的数据却少得多。想要代码运行最新的深度学习模型吗?你可以在 GitHub 上找到它的一些版本。想要一些关于谷歌搜索结果表现或网飞收视率的数据吗?祝好运。公司意识到数据优势几乎总是优于算法优势。例如,Deep Mind 被谷歌以大约 5 亿多美元的价格收购。这是对惊人的天赋和算法的购买。两年前,脸书以 10 亿美元收购了 Instagram。为什么照片分享应用的价值是 Deep Mind 的两倍?在我看来,一个最大的原因就是数据。正如这篇福布斯文章所引用的
脸书的数据库需要这些信息来优化它将带给你的媒体。这些数据非常有价值!想象一下,你是一个滑雪胜地,想要接触滑雪爱好者,Instagram 将为他们提供一种新的方式,同时比脸书更有针对性。
如果你坐下来创建一份“伟大”人工智能公司的名单,你几乎肯定会发现,它们都是伟大的数据公司。如果你能从大量数据入手,构建近乎神奇的人工智能会容易得多。
L 这是报纸上的一张图表:

Source: Revisiting Unreasonable Effectiveness of Data in Deep Learning Era
这表明性能随着训练数据量的增加而呈对数增长。论文结尾还有一段惊人的引言:
而大量的时间花在工程和参数扫描上;很少甚至没有时间花在数据上。我们的论文试图将焦点放回到数据上。
将焦点放回到数据上
今天有多少公司在谈论建立一个数据科学团队?我明白了——雇佣那个极其聪明的麻省理工博士生很性感。我并不是说这是一件坏事。酪这些公司中有多少在谈论投资建立更大更好的数据集?我经常看到公司将数据视为固定资产,或者他们对数据投资非常怀疑。来吧,要求 10 万美元来创建您自己的专有数据集,这将使您的数据规模增加 10-1000 倍。让我知道这次谈话的进展(不过,这位高管可能会对雇佣另一位数据科学家更感兴趣)。
我希望我已经说服你更战略性地思考数据及其在人工智能工作中的价值。我向你发出挑战今天坐下来,思考你如何为你试图解决的任何问题获得更多更好的数据。这可能就像为你刚起步的电子商务商店打开谷歌分析一样简单。你未来雇佣的数据科学独角兽会感谢你的。
这篇文章也可以在这里找到。
我读了很多关于数据科学和领导力的书。我认为这可能有助于其他人每周发送一封电子邮件,其中包含我本周最喜欢的 3 篇文章。如果你有兴趣,就在下面报名吧!
为什么以消费者为中心的战略需要正确的数据集
将产品和管理团队转变为以消费者体验为导向的思维模式。
作者:夏娜·斯图尔特——产品经理, YML

如果你已经成功地将你的产品和高管团队转变为消费者体验驱动的思维模式——在产品体验中优先考虑对消费者的同情,你应该受到表扬。
然而,在培训团队和建立新流程的所有工作之后,你的产品和分析团队可能会经历一点摩擦。
成长的烦恼之所以会出现,是因为随着问题从以业务为中心发展到以消费者为中心,数据集并没有发展:
- 分析团队只能在业务分析和营销分析范式中运作。
- 因此,他们很难进入以产品和消费者为中心的分析范式。
专注于客户满意度是一件好事——并且是整个品牌业内公认的游戏规则改变者。
通常,转向消费者体验思维模式的团队会意外地将业务或营销数据误认为是产品数据,所以你需要停止关注量化的行为数据。
以下是方法。
以消费者为中心对数据结构来说很难
这主要与数据结构的构建方式有关:

数据结构是你所提问题的函数。
从历史上看,业务数据集是最古老的。他们被设计来回答这样的问题,“我赚了多少钱,我有多少付费客户?
引入营销数据集是为了回答围绕营销活动的执行、范围和影响的问题。
除了这两种,大多数公司都停止构建数据结构。现在,各种品牌都在努力通过一个过时的系统来回答营销和业务开发团队的关键问题。
你可能想知道:
在数据量以秒为单位呈指数增长的时代,我们怎么可能在一天中缺少数据集?
又一个很棒的问题!🧐
现实情况是,数据需要特定的结构来回答特定的问题。通常,数据是以非结构化的方式捕获的,然后需要重新结构化,以回答关键的产品和以消费者为中心的问题。
发展您的数据集
构建这些数据集是一项跨职能的团队运动。这是一项运动,因为它需要指导和练习,并且可以在团队之间产生一些竞争,以创建一个伟大的数据集。

第一步
👉有一个清晰简洁的以消费者为中心的战略。
以消费者为中心的策略需要有一个消费者之旅,当用户在消费者之旅中移动时,消费者的反馈和基于消费者需求的状态会通知这个消费者之旅。一旦完成,确保每个人都知道并同意这个策略。

Source: The consumer decision journey, McKinsey
如果人们开始动摇他们对战略的认同程度,以消费者为中心的分析就会失败,因为分析旨在提供关于战略执行情况的反馈。
如果人们开始不同意该战略或遵循不同的战略,那么以消费者为中心的分析框架将不会提供关于该战略执行情况的信息。
第二步
👉从头开始构建 KPI 结构,首先从以消费者为中心的 KPI 开始。
您的以消费者为中心的 KPI 应该描述您在消费者旅程研究中确定为战略行动的基于消费者需求的状态。他们还应该预测你的业务和营销关键绩效指标。

第三步
👉确定业务、营销、产品和以消费者为中心的问题之间的差异。
我们建议分析团队成员对他们经常被问到的问题进行分类。更进一步,开始对哪些团队提出了什么问题进行分类。
这将有助于稍后建立您的数据民主化策略。不是每个人都有兴趣得到所有问题的答案。为了重温不同类型的分析,看看这篇文章。
第四步
👉选择正确的工具和/或更新您的实施,以确保回答所有问题。
以消费者为中心的问题总是最难回答的,因为它们需要最复杂的数据能力来回答。因此,您的需求应该由以消费者为中心的分析需求引导,然后向后工作,以确保您的工具能够回答更简单的三个问题。

最后
要真正拥有以消费者为中心的数据集,每个人都必须具备一些关键要求:
- 访问围绕用户总结的数据,而不是围绕访问量或页面。
- 跨平台链接用户数据的策略,即身份解析系统
- 建立用户群组并基于这些群组开发定制营销和产品体验的计划和承诺
—
Shayna 是一名产品经理,热衷于以消费者为中心的产品战略和设计,并倡导以消费者为导向的数据战略。

最初发表于 YML — 为什么以消费者为中心的战略需要正确的数据集 。
展示人工智能解决方案
以酷而直观的方式交付数据科学解决方案

Photo by Filip Varga on Unsplash
对于任何数据科学家来说,最重要的技能之一就是能够清晰地向普通受众传达和部署结果。这些人需要理解这些见解,以便采取进一步的行动。不幸的是,太多的数据科学项目陷入了普通读者难以理解的数学和计算中。这就是为什么像 Shiny 这样的工具正迅速成为每个数据科学家最好的朋友。
发光的
Shiny 是一个用于开发交互式 web 应用程序的 R 包。它的一些好处:
- 多样性。您可以通过交互式图表、可视化、文本或表格来交流结果。
- 易用型。如果你已经知道 R,你可以快速开发一个很酷的闪亮的应用程序。查看这篇优秀教程,快速学习核心概念。
- 分享能力。内置功能让您可以轻松地与同事和朋友分享您的工作。
- 设计。甚至闪亮应用的默认显示也是优雅而直观的。
为了说服你,让我给你看两个例子。要自己运行这些示例,请查看这些说明。
基于天气的车祸预测
为了预测纽约市的汽车事故,我和我的团队建立了一个模型,我们根据汽车事故的历史数据以及 IBM 的天气数据进行训练。我们使用每个邮政编码的天气条件作为特征来训练具有 Spark 的逻辑回归,输出特定区域、日期和时间的车祸概率。这是一个闪亮的应用程序的屏幕截图,它在一个交互式地图中显示结果:

Probability of Accident by Zip Code Given Date and Time
计划你的下一个假期
我决定继续用旅行数据做实验,但是从汽车跳到了飞机。具体来说,我决定开发一个闪亮的应用程序,让用户探索美国每个机场的平均航班到达延误时间(以分钟计)。互动功能让用户探索预测的月份和年份,并让用户查看额外的数据,如机场名称,代码,州,城市和平均延误。在下面的截图中,气泡的大小与每个机场的航班量相关。负的平均延迟表示航班通常会提前到达。

Flight Arrival Delays
你想建立自己的闪亮的应用程序吗?
这只是 Shiny 帮助我们实施的两个项目。我们还将推出更多产品,我将很快与大家分享。同时,我鼓励你加入进来。同样,按照这些指令运行闪亮的例子。
推特:@ castan LinkedIn:@jorgecasta
shinymeta——再现性的革命
郑中在 2019 年 7 月向用户展示了 shinymeta,实现了 shiny 的可再现性。这是一个使用 shinymeta 的简单应用程序。你会看到反应性和再现性是如何不互相排斥的。我真的很感谢郑中实现了木卫三项目。

Reproducible R Code created from reactive shiny outputs
介绍
在 2018 年的 R/Pharma 大会上,我第一次听说了使用报价的概念。引用让你闪亮的应用程序代码可复制。这意味着您可以在 shiny 中进行试验,然后获取代码,生成与 R 代码完全相同的输出。制药行业需要这一特性。为什么会这样呢?制药行业需要向监管机构报告数据和分析。我已经在的几篇文章中谈到了这一点。给监管部门提供一个闪亮的应用有多好?太好了。如果能提供一个闪亮的应用程序,让他们能够重现每一个图形或表格,那该有多好?甚至更好。
阿德里安·沃德尔和道格·凯尔科夫是我的两个大学,他们为这项任务提出了解决方案。Doug 构建了 scriptgloss 包,它从闪亮的应用程序中重建静态代码。Adrian 在 R/Pharma 2018 上展示了一个模块化的基于 shiny 的探索性框架。该框架提供动态编码、基于变量的过滤和 R 代码生成。在这种背景下,我在当前的开发项目中开始构思一些概念。如何让闪亮 app 内部的代码具有可复制性?与此同时,道格、郑中和卡森·西沃特开发了一款名为 shinymeta 的迷人工具,并于 7 月 11 日在 userR 大会上发布。
这个工具非常迷人,因为它为我谈到的任务创建了处理程序。它允许将一个简单的闪亮的应用程序变成一个可复制的闪亮的应用程序,只需要一些调整。由于制药行业的闪亮应用对这一功能有着强烈的需求,我是制药行业的闪亮开发者,我想知道:它是如何工作的?有多好?
让我们创建一个与制药相关的闪亮应用程序
作为制药行业中一个闪亮应用的简单例子,我将使用一个线性回归应用。该应用程序将检测有用的线性模型是否可以显示患者属性和存活率之间的相关性。患者的属性是年龄或性别。存活率包括患者将存活多长时间(OS =总生存期)、无进展生存期(PFS =无进展生存期)或无任何事件发生生存期(EFS)。每个病人都可能经历了全部三个生存阶段。让我们用随机数据为这个用例创建数据集:
library(tibble)
library(dplyr)# Patient listingpat_data <- list(
SUBJID = 1:200,
STUDYID = c(rep(1, 40), rep(2, 100), rep(3, 60)),
AGE = sample(20:88, 200, replace = T) %>% as.numeric(),
SEX = c(sample(c("M", "F"), 180, replace = T), rep("U", 20)) %>% as.factor()
) %>% as_tibble()# Days where Overall Survival (OS), Event free survival (EFS) and Progression Free Survival (PFS) happenedevent_data <- list(
SUBJID = rep(1:200, 3),
STUDYID = rep(c(rep(1, 40), rep(2, 100), rep(3, 60)), 3),
PARAMCD = c(rep("OS", 200), rep("EFS", 200), rep("PFS", 200)),
AVAL = c(rexp(200, 1 / 100), rexp(200, 1 / 80), rexp(200, 1 / 60)) %>% as.numeric(),
AVALU = rep("DAYS", 600) %>% as.factor()
) %>% as_tibble()
可以看到患者年龄和性别(性别)是随机分布的。存活天数将呈指数下降。通过这些分布,我们不期望在数据中看到任何东西,但是对于这个例子来说这很好。

Simple app showing a linear regression of patient data
在截图中,您可以看到应用于这些数据的应用程序。该应用程序包含回归图和用lm创建的线性模型的摘要。它基本上有一个输入通过PARAMCD.过滤event_data,第二个输入从pat_data中选择列。这个应用程序有趣的部分是服务器功能。在服务器函数内部,只有两个输出和一个反应值。反应执行多个步骤。它生成线性模型的公式,过滤event_data,选择pat_data,合并数据集,并通过lm计算线性模型。这两个输出从线性模型生成一个图和一个摘要文本。
server function to create a plot and a summary output from a linear model
当然,你以为这个 app 可以被一个聪明的程序员轻松复制。现在想象你只看到用户界面和输出。少了什么?缺少两样东西:
- 如何创建数据?
- 用于创建线性模型的公式是什么?
让我们把 app 做的可复制!
通过 shinymeta 和元编程的方法,我们将使整个应用程序可复制。即使 shinymeta 仍然是实验性的,你会看到,现在它工作得很好。
但是我们需要一步一步来。元编程背后最重要的想法来自艾德里安·沃德尔。你不用在你的应用程序中添加代码,而是用引号将代码括起来。(第一步,也是最重要的一步)。
创建数据
我们可以将此用于添加到应用程序的数据:
我们不运行代码,而是将它包装到quote中。这将返回一个call,我们可以通过eval对其进行评估。它实现了再现性。我们用来生成数据集的代码存储在data_code中。我们可以在以后重用这个变量。这个变量将允许我们显示数据集是如何构建的。
过滤和选择数据
为了实现可重复的过滤和选择,我们将使用 shinymeta 函数。因此,我们将创建一个返回合并数据集的metaReactive。metaReactive 的行为类似于reactive,不同之处在于,您可以在之后获取内部使用的代码。这类似于引用的原理。但是对于metaReactive你不需要使用eval函数,你基本上可以像以前一样坚持使用()评估。
metaReactive内部一个重要的新操作符是!! (bang,bang)操作符。它允许插入标准电抗值。它的行为有点像在rlang包中。您可以使用它来内联标准无功值的值。或者您可以使用它将metaReactive对象作为代码内联。总之,操作器!!有两个功能:
- 取消对反应对象的引用—获取它们的值
- 通过将对象作为代码内联到彼此来链接
metaReactive对象
为了更好地了解!!操作者,请查看 shinymeta 简介:https://github.com/rstudio/shinymeta/tree/master/vignettes
此代码将用于过滤、选择和合并数据:
在代码内部,您可以看到!!操作符与作为值的反应值input$select_regressor和input$filter_param进行交互。这意味着我们取消了对无功值的引用,用它的静态值替换它。这种反应的结果是合并的数据集。当然,这段代码不会运行,直到我们在服务器函数中的任何地方调用data_set_reactive()。
创建模型公式
线性模型的公式将像以前一样创建:
有必要检查选择回归变量值,因为没有选择就不能导出模型
创建线性模型
无需元编程即可生成线性模型的代码如下:
lm(formula = formula_value, data = anl)
我们需要替换formula_value和anl。另外用一个metaReactive代替无功。因此,我们使用函数metaReactive2,它允许在元编程代码之前运行标准的闪亮代码。在这个metaReactive2里面,有必要检查数据和公式:
validate(need(is.data.frame(data_set_reactive()), "Data Set could not be created"))validate(need(is.language(formula_reactive()), "Formula could not be created from column selections"))
metaReactive data_set_reactive可以像任何反应对象一样被调用。产生模型的代码应该是元编程的,因为用户想看到它。函数metaExpr允许这样做。为了获得良好的可复制代码,调用需要如下所示:
metaExpr(bindToReturn = TRUE, {
model_data <- !!data_set_reactive()
lm(formula = !!formula_reactive(), data = model_data)
})
如果你不想在lm调用中看到整个数据集,我们需要将它存储在一个变量中。
为了使代码可跟踪,您需要将!!放在被动调用的前面。在data_set_reactive之前,这允许回溯data_set_reactive的代码,而不仅仅是输出值。
其次,我们可以通过!!操作符取消对formula_reactive的引用。这将直接将创建的公式插入到lm调用中。
第三,bindToReturn会强迫 shinymeta 写:
var1 <- merge(...)
model_data <- var_1
model_reactive <- lm(formula = AVAL ~ AGE, data = model_data)
代替
data_set_reactive <- merge(...)
{
model_data <- data_set_reactive
lm(AVAL ~ AGE, data = model_data
}
如果你想了解更多关于bindToReturn特性的信息,github 上有一个关于 [bindToReturn](https://github.com/rstudio/shinymeta/issues/53) 参数的问题。最终的model_reactive是这样的:
渲染输出
最后但同样重要的是,我们需要以可复制的方式输出图形和文本。代替标准的renderPlot和renderPrint函数,有必要将它们包装在metaRender中。metaRender支持输出元编程反应对象,代码可重复。为了获得模型的值和代码,再次使用了!!操作符。
使用metaRender也将使输出成为元编程对象。这允许以后检索代码,并使其可重复。
检索用户界面内的代码
重要!
很抱歉在这里使用大写字母,但这部分是真正的部分,这使得应用程序可复制。通过插入一个“ Show R Code ”按钮,该应用程序的每个用户都可以看到产生输出的代码。因此 shinymeta 提供了函数
expandChain。下一节将展示如何使用它。
如果用户点击一个按钮,就像这个例子中的input$show_r_code代码的模态应该弹出。在这个模态中,expandChain函数可以处理(1)引用代码和(2) metaRender对象。这种类型的每个对象都可以用在expandChain的…参数中。它将返回一个元表达式。从这个元表达式中,可以提取出 app 中使用的 R 代码。简单地使用formatCode()和paste()会让漂亮的代码出现在模态中。
请不要忘记metaRender对象后的()。
最终服务器功能和 app

All code can be found at https://github.com/zappingseb/shinymetaTest
在完成所有步骤后,您可以看到使用 shinymeta 的代码与标准的 shiny 代码没有太大的不同。主要是metaReactive、metaReactive2、metaExpr、metaRender、!!、expandChain是新学的函数。即使这个包仍然是实验性的,它在使一些反应性的东西也是可复制的方面做得非常好。我最喜欢的功能是混合使用reactive和metaReactive。通过在元代码中使用反应对象,开发人员可以决定哪些代码进入“ Show R Code ”窗口,哪些代码在幕后运行。您可以通过查看本教程的代码来检查自己。当然,这个特性是危险的,因为您可能会忘记将代码放在“显示 R 代码”窗口中,并且不是所有的代码都可以重新运行,或者您的可复制代码变得难看。
教程的全部代码发布在 github 上:https://github.com/zappingseb/shinymetaTest。
该应用程序在https://sebastianwolf.shinyapps.io/shinymetaTest运行。

App running at: https://sebastianwolf.shinyapps.io/shinymetaTest/
结束语
这是我第一次尝试将自己的作品打包成一个全新的包。这个例子中创建的应用程序是在我以前的日常工作中创建的。新的实验包 shinymeta 允许在大约 1 小时内从我的代码切换到元编程。我不仅切换了我的实现,而且由于这个包,我的实现也变得更好了。
Shinymeta 将在制药发光应用中发挥巨大作用。在郑中演讲一周后,我仍然对元编程的概念印象深刻。以及元编程是如何变得闪亮的。包装使闪亮真正可复制。它将为如何在监管领域使用 shiny 提供指导。此外,它将允许更多的用户用 R 编写代码,因为他们可以看到特定输出所需的代码。点击会让他们学习。
相关文章:
- 为什么我们需要对编程语言进行人类可读的测试?
- 让自定义输入变得简单的 7 个步骤
- 教程:R shiny 可视化活检数据的应用程序——在一家制药公司
购物者行为与购物车推荐系统探究

Photo by NeONBRAND on Unsplash
到更好地了解顾客并创造独特的购物体验是每一个商家为了给顾客创造价值而必须做的事情。我认为你可以通过使用机器学习分析你的客户购物体验/习惯来实现这一点。在这个项目中,我想通过分析客户以前的购买来探索他们的购物行为,并建立一个购物车推荐器,为他们提供量身定制的购物体验,以提高参与度。
我认为这对于企业来说是有价值的,原因有很多,例如:
- 有针对性的营销:分析你的客户在你的网站/应用程序上的虚拟足迹可以将你的有针对性的营销带到另一个层次!例如,您可以根据他们的购买历史,在个人层面推出他们最喜欢的商品的特价。
- 在一天中的特定时间,有策略地向每位客户推送不同的营销材料,因为这段时间是您的客户最喜欢购物或最喜欢登录您的应用程序的时间。
- 计算何时重新进货,提高库存周期的效率。
- 推荐他们可能想要添加到购物车中的商品,并通过个性化建议吸引您的客户。
以上只是你可以从顾客的购买历史中获得定量洞察的几件事。
我想解决的问题
对于这个项目,我想探讨以下几个问题:
- 通过分析购物者的购买历史,执行购物者行为分析,看看我是否能找到一些模式,比如顾客在一天/一周的不同时间喜欢购买什么样的商品,人们多长时间再订购一次他们以前购买过的相同产品等等。
- 购物车推荐器:利用顾客重新订购的商品和余弦相似度建立一个推荐系统。
项目设计
- 执行数据清理,将不同的表合并在一起,并进行探索性数据分析(EDA)。
- 分析客户行为,产生见解,并为讲述故事构建数据可视化。
- 定义模型评估的指标。
- 最后,创建一个购物车推荐系统,根据顾客的购买历史向他们推荐商品。
我使用的数据和技术
出于这个项目的目的,我能够在 Instacart 上找到一个客户购买历史数据集,这是 Instacart 在 2017 年发布的一个 Kaggle 挑战赛。



数据集有六种不同的。csv 文件,您需要使用主键和外键来连接它们,以获得完整的图片并执行分析。数据字典可以在这里找到。
我在这个项目中使用的工具是 numpy,pandas,matplotlib,seaborn,以及 sklearn 的 StandardScaler,OneHotEncoder 和 cosine_similarity。
清理&探索性数据分析(EDA)

该数据集拥有 20 多万客户的 300 多万份订单。它具有诸如星期几购买,一天中的时间购买,之前或之前重新排序等功能。
总共有 6 个不同的表:通道、部门、订单 _ 产品 _ 先验、订单 _ 产品 _ 系列、产品和订单。
经过一些检查,我发现数据集非常干净,只需要一些小的清理和争论,因为我在进一步分析之前将表合并在一起。
购物者行为分析
当我们分析数据集时,有些问题需要您回答。例如:
- 数据集有多少个订单?
- 我们有多少独特客户?
- 顾客多久购买一次?
- 他们喜欢什么时候购买?
- 他们每份订单购买多少件商品?
- 还有更多问题。
在本节中,我将探索数据集,以提取见解来回答不同的问题。
数据集有多少个订单?
在浏览 orders 表时,您可以使用下面的代码来找出数据集中有多少订单以及每个标签的订单数。

snapshot of orders table
print(orders.shape)
print('there are', len(orders[orders.eval_set == 'prior']), 'entries for prior')
print('there are', len(orders[orders.eval_set == 'train']), 'entries for train')
print('there are', len(orders[orders.eval_set == 'test']), 'entries for test')
- 一共 3421083 单。
- 有 3214874 个订单是优先订单。
- 训练集中有 131209 个订单。
- 测试集中有 75000 个订单。
数据集有多少不同的用户?
您可以使用下面的代码来找出有多少不同的用户。
print('there are', len(orders[orders.eval_set == 'prior'].user_id.unique()), 'unique customers in total')
print('there are', len(orders[orders.eval_set == 'train'].user_id.unique()), 'unique customers in train set')
print('there are', len(orders[orders.eval_set == 'test'].user_id.unique()), 'unique customers in test set')
- 共有 206209 个独立客户。
- 列车组中有 131209 个客户。
- 测试集中有 75000 个客户。
订单量如何分配?
你可以使用下面的代码来生成一个订单量分布图。
orders_amount_for_customer = orders.groupby('user_id')['order_number'].count().value_counts()
plt.figure(figsize=(20,8))
sns.barplot(x=orders_amount_for_customer.index, y=orders_amount_for_customer.values, color='mediumseagreen')
plt.title('Amount of Orders Distribution', fontsize=16)
plt.ylabel('Number of Customers', fontsize=16)
plt.xlabel('Amount of Orders', fontsize=16)
plt.xticks(rotation='vertical');

amount of orders distribution
- 每个客户的订单数量在 4 到 100 之间。大多数人购买了 4 到 10 次。
他们最喜欢在一周的哪一天购物?
您可以使用下面的代码将订单按一周中的不同日期分组。
plt.figure(figsize=(12,8))
sns.countplot(x=orders.order_dow, color='mediumseagreen')
*#plt.title("Order Amounts by Days", fontsize=16)*
plt.xlabel('', fontsize=16)
plt.xticks(fontsize=15)
plt.ylabel('Order Counts', fontsize=16)
plt.yticks(fontsize=15)
;

order amounts by day
- 0(周日)和 1(周一)一周订单最多,4(周四)订单最少。
人们在一天中的什么时候购物?
您可以使用下面的代码按一天中的小时将订单分组。
plt.figure(figsize=(12,8))
sns.countplot(x=orders.order_hour_of_day, color='mediumseagreen')
*#plt.title("Order Amounts by Hours", fontsize=16)*
plt.xlabel('Hour of Day', fontsize=16)
plt.xticks(fontsize=15)
plt.ylabel('Order Counts', fontsize=16)
plt.yticks(fontsize=15)
;

order amounts by hour
- 看起来人们喜欢在早上 8 点到下午 6 点之间点菜。
人们多久购买一次?
您可以使用下面的代码将订单按照最后一次购买的日期进行分组。
plt.figure(figsize=(12,8))
sns.countplot(x=orders.days_since_prior_order, color= 'mediumseagreen')
*#plt.title("Number of Orders per Days Since Last Purchase", fontsize=16)*
plt.xlabel('Days Since Last Purchase', fontsize=16)
plt.xticks(np.arange(31), [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30], fontsize=15)
plt.xticks(rotation='vertical')
plt.ylabel('Order Counts', fontsize=16)
plt.yticks(fontsize=15)
;

days since last purchase
- 看起来大多数人一周点一次,在 0 点到 7 点之间。
- 而且还有 30 多天订一次的。
人们在一份订单中会购买多少件商品?
为了回答这个问题,我们将镜头转向另一组表:order_product_prior 和 order_product_train。


snapshot of order_product_prior and order_product_train
为了这个项目的目的,它不是为了 Kaggle 挑战提交,所以我把两个表连接在一起,以获得更好的图片。
# Concatenation of both tables.
order_products_total = pd.concat([order_products_prior, order_products_train]) # Create amounts of item per order.
frequency_per_number_of_order = order_products_total.groupby('order_id')['product_id'].count().value_counts()
plt.figure(figsize=(20,8))
sns.barplot(x=frequency_per_number_of_order.index, y=frequency_per_number_of_order.values, color='mediumseagreen')
plt.title('Amount of Items Per Order', fontsize=16)
plt.ylabel('Order Counts', fontsize=16)
plt.xlabel('Number of Items', fontsize=16)
plt.xticks(rotation='vertical');

amount of items per order
- 人们通常每份订单购买 4 件商品。
- 大多数人喜欢每份订单购买 3 到 8 件商品。
这个数据集中有多少交易和独特的产品?
我们可以用下面的代码来回答这个问题。
print('there are', order_products_total.shape[0], 'grocery products ordered')
print('there are', len(order_products_total.order_id.unique()), 'order transactions')
print('there are', len(order_products_total.product_id.unique()), 'unique products')
- 订购了 33819106 种杂货产品。
- 订单交易有 3346083 笔。
- 有 49685 个独特的产品。
在所有订单中,有多少是之前重新订购的,有多少是没有重新订购的?比例是多少?
下面的代码将回答这个问题。
# Find out how many products have been reordered before.
print(len(order_products_total[order_products_total.reordered == 1]), 'products have reordered before')
print(len(order_products_total[order_products_total.reordered == 0]), 'products haven**\'**t reordered before')# Find out the ratio.
print(len(order_products_total[order_products_total.reordered == 1])/order_products_total.shape[0], 'have reordered before')
print(len(order_products_total[order_products_total.reordered == 0])/order_products_total.shape[0], 'haven**\'**t reordered before')
- 19955360 产品之前有过再订购。
- 13863746 产品之前没有再订购过。
- 0.59 之前有重新排序。
- 0.41 之前没有重新排序。
每个产品类别中有多少个产品,拥有最多产品的前十个产品类别是什么?
为了回答这个问题,我们将研究过道、部门和产品表。



snapshot of aisle, department and products tables
# Merging tables together.
products_departments = products.merge(departments, left_on='department_id', right_on='department_id', how='left')
products_departments_aisles = products_departments.merge(aisles, left_on='aisle_id', right_on='aisle_id', how='left')
products_departments_aisles.head()# Counting how many items is in each product category.
products_departments_aisles.groupby('department')['product_id'].count().reset_index().sort_values(by='product_id', ascending=**False**).head(10)

top 10 product categories that have most items.
人们购买最多的物品是什么?
为了探究这个问题,我们需要将 products_departments_aisles 和 order_products_total 表合并在一起。
# Merging products_departments_aisles and order_products_total.
df = order_products_total.merge(products_departments_aisles, left_on='product_id', right_on='product_id', how='left')
df.head()# Find out the top 15 items people purchased the most.
top_15_products = df.product_name.value_counts(ascending=**False**).reset_index().head(15)
top_15_products.columns = ['product_name', 'count']
top_15_products

top 15 items people purchased the most.
- 人们购买最多的前 15 种商品在上面。
- 其中大部分是有机水果/蔬菜。所有的都是水果/蔬菜。
使用相同的逻辑,我们可以找出人们购买最多的前 15 个通道和前 15 个部门。
# Finding top 15 aisles.
top_15_aisles = df.aisle.value_counts(ascending=**False**).reset_index().head(15)
top_15_aisles.columns = ['aisle_name', 'count']
top_15_aisles# Finding top 15 departments.
top_15_department = df.department.value_counts(ascending=**False**).reset_index().head(15)
top_15_department.columns = ['department_name', 'count']
top_15_department


top 15 aisles and top 15 departments that has the most purchases in.
每个部门和通道的再订购率是多少?
# Find out reorder ratio per department.
reorder_ratio_per_dep = df.groupby('department')['reordered'].mean().reset_index()
reorder_ratio_per_dep.columns = ['department', 'reorder_ratio']
reorder_ratio_per_dep.sort_values(by='reorder_ratio', ascending=**False**)# Find out reorder ration per aisle.
reorder_ratio_per_aisle = df.groupby('aisle')['reordered'].mean().reset_index()
reorder_ratio_per_aisle.columns = ['aisle', 'reorder_ratio']
reorder_ratio_per_aisle.sort_values(by='reorder_ratio', ascending=**False**)
- 每个部门的乳制品鸡蛋的再订购率最高。
- 个人护理部门的再订购率最低。
- 牛奶通道的再订购率最高。
- 香料调味料的每条通道的再订购率最低。
定义指标
我想为我的购物车推荐者定义一个指标。人们喜欢使用不同的通用指标进行评估。例如:

precision vs. recall
对于我的项目,我决定使用回忆作为我的评估标准。也就是说,顾客购买的商品中有多少是来自推荐者的?
例如,如果我向顾客推荐 5 件商品,他/她买了 4 件,这意味着召回率= 0.80。换句话说,我推荐的商品有 80%在顾客的购物车里,20%是我推荐给顾客的新商品。
购物车推荐器
构建推荐系统有不同的方式,例如,基于内容的过滤,协同(基于项目或基于用户)过滤,以及两者的混合。在这个项目中,我将探索如何使用余弦相似度构建购物车推荐器,这是协同过滤下的一种方法。

推荐器的设计是先找出之前重新订购过的客户,以及之前重新订购过的商品。然后计算所有这些用户和产品之间的余弦相似度。然后生成一个包含 5 条建议的列表。
下面是如何找到以前重新排序的客户并计算余弦相似度的代码。
*# get the list of orders that have been reordered before*
reorders = order_products_total[order_products_total['reordered'] == 1]orders2 = orders[['order_id', 'user_id']]*# merge to get user_id and product_id*
user_orders = reorders.merge(orders2, on='order_id')*# filtering out the high volumn products that user reordered more than once*
user_orders['high_volume'] = (user_orders['product_id'].value_counts().sort_values(ascending=**False**)>1)
high_volume = user_orders[user_orders['high_volume'] == **True**]*# get a matrix of different high volume items that particular user purchased*
high_volume_users = high_volume.groupby(['user_id', 'product_name']).size().sort_values(ascending=**False**).unstack().fillna(0)*# calculate similarity between each user*
cosine_dists = pd.DataFrame(cosine_similarity(high_volume_users),index=high_volume_users.index, columns=high_volume_users.index)cosine_dists.head()

snapshot of how the matrix looks like.
这是为推荐系统定义一个功能。并尝试根据与我们的目标客户有相似购买历史的相似资料推荐 5 件商品。
**def** Recommender_System(user_id):
*'''*
*enter user_id and return a list of 5 recommendations.*
*'''*
u = high_volume.groupby(['user_id','product_name']).size().sort_values(ascending=**False**).unstack().fillna(0)
u_sim = pd.DataFrame(cosine_similarity(u), index=u.index, columns=u.index)
p = high_volume.groupby(['product_name','user_id']).size().sort_values(ascending=**False**).unstack().fillna(0)
recommendations = pd.Series(np.dot(p.values,cosine_dists[user_id]), index=p.index)
**return** recommendations.sort_values(ascending=**False**).head()# recommendation for customer id 175965.
Recommender_System(175965)

5 recommendations generated for customer id 175965
估价
为了更好地说明,我以自己为例:

purchase history and recommendation for user Ka.
在左边,是用户 Ka 之前购买的前 20 个项目的列表。右边是我的推荐者生成的 5 个推荐列表。如你所见,我推荐的 5 件商品中有 4 件与 Ka 的前 20 件购买记录相符。这意味着我推荐的商品有 80%的机会出现在用户的前 20 个商品列表中。
使用与评估标准相同的逻辑。为了计算推荐器的召回分数,我定义了一个函数来完成这项工作,下面是代码。数据帧太大,无法处理,所以我将数据帧分成 7 份进行度量计算。
# filter 1000 users for calculation
# because the dataframe is too large
users = high_volume.user_id.unique().tolist()# calculate recall for the :1000 users
**def** how_match():
res = []
**for** user **in** sorted(users)[:1000]:
recommendations = Recommender_System(user)
top_20_itmes = _[_.user_id == user].product_name.value_counts().head(20)
recommendations_list = recommendations.index.tolist()
top_20_items_list = top_20_itmes.index.tolist()
res.append((len(set(recommendations_list) & set(top_20_items_list)))/5)
**return** np.mean(res)*# get metric for the :1000 users*
how_match()*# calculate the mean of all the metric from all the Recommender notebooks.*
print('The Final Score for Metric is', (0.531 + 0.522 + 0.519 + 0.530 + 0.523 + 0.519 + 0.526)/7)
最终的召回分数是 0.524,这意味着我推荐的商品有大约 52%的机会出现在用户的前 20 个商品列表中,这比仅仅从 50k 多个产品中随机猜测要好得多。大约一半的商品是我向顾客推荐的新产品。
为了更好地了解我的推荐器有多有效/强大,我认为最好的方法是运行 A/B 测试实验。
经验教训
- 关于如何建立一个推荐器,有许多研究、类型和不同的方法。你真的需要花很多时间去做你的调查研究。没有捷径可走,你需要尝试不同的方法,使用你的数据来建立比较推荐。
- 预计你的数据框架将是巨大的后,你为每个客户和项目创建虚拟。所以需要探索方法,例如:矩阵,使你的输入更可读或更省时,让你的机器来做计算。
在将来
- 创建一个 Flask 应用程序,并将其部署到网上,让人们体验推荐者。
- 使用这个推荐者做一个可以练习/探索 A/B 测试的项目。
- 尝试使用数据集来构建不同类型的推荐器。
- 探索 Turi Create ,苹果现在拥有的开源 ML 工具,用于构建推荐器并与不同类型的推荐器进行比较。
非常感谢您的阅读,如果您有兴趣探索我使用的代码和资源,这个项目在我的 github repo 上。
用你的相机购物:视觉搜索如何改变电子商务

视觉搜索正在席卷零售业。
当客户进行视觉搜索时,他们使用图像而不是关键字来寻找产品。购物者可以给他们想买的东西拍照(比如路人穿的一双运动鞋),然后上传到他们选择的视觉搜索引擎——比如谷歌图片或 Pinterest Lens。视觉搜索引擎将有助于在网上找到它,并建议接近的匹配。
在数字时代注意力持续时间迅速缩短的情况下,视觉搜索能力在抓住顾客想象力方面发挥着不可或缺的作用。现在,顾客比以往任何时候都更容易在网上找到他们想要购买的商品或发现他们想要购买的商品。
Pinterest 首席执行官本·希伯尔曼说:“搜索的未来很大程度上将取决于图片,而不是关键词。
到 2021 年,重新设计网站以支持视觉和语音搜索的早期采用者品牌将使数字商务收入增加 30% 。
交付真正无缝的客户体验

人们每月在 Pinterest 上进行超过 6 亿次视觉搜索,而且这个数字还在增长。62%的千禧一代渴望视觉搜索超过任何其他新的数字购物技术。
有了视觉搜索工具,人们可以拍一张照片来发现多个网站和零售商的类似产品。例如,Google Lens 从 Google Shopping 中提取类似的产品示例。
其他零售商在他们自己的品牌中使用视觉搜索来缩小看到和购买之间的差距。例如,ASOS 的移动应用程序有一个名为 Style Match 的搜索工具,让购物者拍摄或上传照片,然后从他们的目录中发现与之匹配的产品。通过提供可以匹配 850 多个品牌和 85,000 多种产品的工具,ASOS 鼓励用户向他们购买。他们通过向用户展示他们不会像去其他地方那样花费太多来吸引用户。用户可以上传一张设计师产品的图片,找到更实惠的款式与之搭配。
在提供真正无缝的体验方面,人工智能支持的视觉搜索消除了观看和购买之间的摩擦。
当用户返回网站时,无摩擦的体验继续。下一次访问提供了更个性化、更强大的目标搜索,用户可以从上次离开的地方重新开始,更快地找到相关产品。
把“缺货”变成机会
一个访问者访问一个网站,想要购买一件特定的衣服。一旦她进入产品页面,她会看到一个模特穿着那条裙子,戴着项链、手表、帽子和一双高跟鞋——所有这些都可以在网站上找到。
视觉搜索人工智能将让游客知道他们可以轻松地购买整个外观,而不是一个项目,并将向他们展示类似的选项。这增加了顾客在完成购买前将额外物品放入购物车的可能性。
各大品牌也在利用视觉搜索,在顾客被诱惑离开电子商务网站时,给他们一个温和的提示,让他们继续购物。
当客户访问网站并发现他们想要购买的产品缺货时,他们会感到失望。通常,他们会关闭标签页,或者转到另一个有类似功能的网站。
视觉搜索技术有助于让购物者留在网站上,即使他们最初想要的商品已经售完,也可以通过向他们展示库存中的类似产品来实现。
领先的在线零售商 Boohoo 的电子商务网站在实施视觉搜索后出现了巨大的增长。在最近的一项案例研究中,Boohoo 报告称当访问者在产品展示页面上使用“查看相似”时,转化率会高出 100%以上。每个会话的页面增加了 135%以上。
模糊线下和线上的界限

Lonely Planet 最近收购了 TRILL Travel,这是一个人工智能驱动的视觉搜索平台,可以将社交媒体图像转化为即时可预订的体验。TRILL 利用人工智能技术分析图片,并自动创建预订网站的链接。
通过强大的视觉搜索机制简化预订流程有助于缩小旅游癖和现实之间的差距。
用户可以很容易地从浏览 Instagram 上的旅行照片到获得参观这些确切目的地的门票。只需触摸图片,他们就可以预订酒店、体验或在餐厅用餐。
线上和线下世界前所未有地融合在一起。有了视觉搜索,公司可以更接近许多消费者渴望的即时满足感。
视觉搜索的美妙之处在于,它可以点燃想象力,然后激发并捕捉由此产生的购买意图。视觉搜索将不可避免地在未来几年严重扰乱电子商务行业。
短篇小说:快速分析集
以下六个场景涉及到各种需要回答问题的情况。每个数据集只涉及几个变量,以帮助缩小分析的重点。当我们在每个小故事中寻找答案时,请跟着读。
下面介绍的练习册都可以在我的 Tableau 公共档案和 GitHub 上找到。
客户满意度
场景:一家公司收集一段时间内的客户满意度,记录有多少客户得到了服务,以及客户是否满意。2018 年 3 月,该公司失去了三名员工,然后在 2018 年 11 月,他们增加了两名新员工。客户满意度是如何受到影响的?

Customer satisfaction over time
在第一张图表中,我们可以看到客户满意度是如何随时间变化的。该公司平均每月约 90%,直到 2018 年 6 月,届时该百分比将下降。这可能是因为 3 月份损失了 3 名员工;该公司股价立即下跌,随后在 5 月份小幅回升,随后大幅下跌。满意度连续几个月保持低位,8 月份低至 68.4%。2018 年 11 月,该公司雇用了 2 名新员工,以帮助取代 3 月份失去的 3 名员工。这似乎对客户满意度有很大的影响,因为百分比立即增加到几乎 95%。
我们还可以研究客户满意度的变化是否与接受服务的客户数量有任何关系。在本研究的时间段内,总访问量从 2018 年 5 月的 13,000 次到 2018 年 1 月的 24,000 次不等。2018 年 3 月 3 名员工离职后,客户量似乎确实有所下降。虽然没有同期客户满意度下降那么显著,但似乎有更持久的影响。虽然在 11 月雇佣 2 名员工后客户满意度有所恢复,但随着时间的推移,客户数量似乎稳定在较低的水平。我们可以在客户数量的负斜率趋势线中看到这一点。
进入市场
场景:一家公司有兴趣进入城市市场。他们已经对市场进行了一段时间的监测,并试图预测他们自己的潜在销售额。数据库中的所有产品都是竞争对手(都是‘ville’产品)。他们相信他们能够在市场上实现中等或平均的销售额。他们实现目标的可能性有多大?

Market analysis for the ‘ville genre
这家公司有一个雄心勃勃的目标,即在“城市游戏”市场上达到平均或中等销售额。正如我们在面积图上看到的,市场平均每年的销售额约为 920 亿美元,而中位数略低,为 905 亿美元。将这些价值与每家公司的平均年销售额相比,前景并不乐观。最畅销的产品 Township 和 Harvest Moon 的平均总销售额不到 600 亿美元,这只是该公司设定的目标的一小部分。总体而言,公司每年的销售额约为 170 亿美元。更糟糕的是,2017 年市场总销售额下降。它在 2018 年开始复苏,但在市场本身勉强价值 1000 亿美元的情况下,将很难达到 900 亿美元的崇高目标。
对一家新公司来说,一个积极的方面是最近城市市场的多样化。尽管 Harvest Moon 和 Township 占据了几年的主导地位,但几个竞争对手的引入降低了它们的市场份额。热图显示了从 2012 年到 2015 年对市场的强大控制,但在 2016 年,像农场天堂这样的产品几乎与他们的销售持平。接下来的几年里,Farm Paradise 的销售额下降,但其他进入市场的产品帮助更多的公司扩大了市场价值。虽然新公司可能需要重新调整他们的目标,但很大一部分市场可能会按照他们的方式发展。
项目状态
场景:一个公司一直在收集它的项目状态(错过、满足、超过)。它关心的是它的项目状态如何随着时间的推移而变化。它想知道它的项目状态的百分比和绝对分数。随着时间的推移,我们可以看到项目状态的哪些模式?

Project status tracking over time
查看数据,我们可以看到该公司遇到了一个问题,即错过项目状态的频率越来越高。从 2016 年到 2018 年,项目总数有所增加,但目标也有明显差异。从 2016 年到 2017 年中期,该公司稳步达到或超过了项目目标。然而,2017 年 Q2 奥运会后,超额完成目标的数量从 88 个开始下降。在 2018 年 Q2 奥运会上,该公司的超额目标数量最低,同期只有 18 个。积极的一面是,met 目标在 2017 年保持相对稳定,然后在 2018 年初增加。
更令人担忧的是,未达到目标的比率开始上升,同时超过目标的比率开始下降。2018 年的失球率异常高,第三季度达到约 31.3%。2016 年为 2.6%,仅两年时间就发生了显著变化。虽然每个季度实现的目标保持在 9-13%左右,但从超额目标到未实现目标的变化值得关注。
功能满意度
场景:一家公司一直在收集客户对其功能的满意度信息,排名从完全不满意到完全满意。它想知道哪些功能是流行的,哪些是无用的。虽然调查使用了李克特量表的 5 个选项,但它认为完全满意和非常满意彼此非常相似。它还认为完全不满意和不太满意彼此非常相似。哪些功能提供最高和最低的满意度?哪些功能用得最少?

Feature ratings from high to low
平均而言,65%的客户对他们使用的功能评价很高。大约 24%的人认为功能仅有些满意,超过 10%的人认为他们使用的功能满意度较低。评分最高的功能包括安全性、性能和范围,而一些评分最低的功能包括加热方向盘和 LED 照明。性能和安全对于车辆的正常运行至关重要,这可以解释为什么很少有客户表示他们不使用这些功能。
一些功能也很少被用户使用。音频系统是最少使用的功能,超过一半的客户表示他们没有使用过。在使用过音响系统的人中,只有 55.1%的人表示非常满意,远低于所有功能 65%的平均水平。其他一些不太受欢迎的功能是交付时间和保修,可能是因为被客户视为车辆不太必要的功能。
信息视频
场景:一家公司对市场进行了初步调查,以确定潜在客户是否认为他们的产品会让他们满意。然后向顾客展示了一段详细介绍产品特性的短片。公司再次调查了潜在客户,以确定他们的产品是否会令他们满意。视频播出后,态度有变化吗?

Pre- and post-video survey results
我们可以看到,观看视频后,犹豫不决的客户数量明显下降,而意见则相应增加。总的来说,数据表明,视频有助于以一种大多数顾客感到满意的方式描述产品。在更广泛的层面上,视频似乎有助于解释或解决客户可能有的问题,并帮助引导他们做出明智的决定。
40%的顾客在观看视频前犹豫不决,但这一数字下降到 14%,因为他们能够做出决定。虽然这四种观点都有所增加,但增加最多的是那些强烈认为产品会让他们满意的人。在观看视频之前,只有 19%的顾客强烈认为产品令人满意,但之后这一比例翻了一番,达到了 38%。25%的人在看到视频前表示同意。这一比例略微上升至 30%,在视频放映后,这一比例位居第二。甚至负面意见也增加了,尽管不同意或强烈不同意的人各只增加了 1%。
音乐欣赏
场景:每年,一个非盈利机构要求潜在的捐赠者帮助推进它的音乐事业。它要求捐赠者支持不同的音乐流派(古典、乡村、爵士、歌剧和摇滚)。每年它都瞄准一批潜在的捐助者来帮助支持它的事业。筹款有多成功?什么是进步,什么不是?

Music donation trends over time
在这 5 个领域中,古典音乐和爵士乐似乎每年都有最大的增长。这两个领域都比其他三个领域有明显更陡的斜率,尽管值得注意的是,直到最近,爵士乐每年的资助人数都是最低的。2018 年,它超过了 Country,其趋势线可用于预测未来几年的持续增长。摇滚和乡村音乐似乎每年都略有增长,而歌剧几乎没有增长。2016 年,除了爵士乐,几乎所有领域都出现了下滑。
有理由假设爵士乐是 2016 年的重点,因为它是唯一一个正增长的领域。歌剧和摇滚每年分享相似的资金数字,表明它们经常同时被推向市场。国家明确定义了每年交替的“好”和“坏”年份,暗示该地区何时成为目标。Classical 每年的稳定增长可以表明两种理论:要么该区域几乎每年都有目标,要么不管该区域是哪一年的焦点,它都继续增长。该机构可能会受益最大,继续增长爵士乐,同时也培养国家,试图促进其年度增长。
短文本主题建模
直觉和代码来理解和实现短文本的主题建模,比如社交媒体(Tweets,Reddit 的帖子…)

Photo by Hello I’m Nik 🇬🇧 on Unsplash
主题建模的目的是在一个文本语料库(如邮件或新闻文章)中找到主题(或聚类),而不需要先知道那些主题。这就是主题建模的真正力量所在,你不需要任何带标签或带注释的数据,只需要原始文本,从这种混乱的主题建模算法中会找到你的文本所涉及的主题!
在这篇文章中,我们将描述最流行的主题建模方法 LDA 背后的直觉和逻辑,并了解它对短文本的限制。鉴于这篇文章是关于短文本主题建模** ( STTM )我们将不深究 LDA 的细节。想要加深对 LDA 的了解的读者可以在这里和这里找到关于 LDA 的优秀文章和有用资源。
然后,在第二部分中,我们将介绍一种新的 STTM 方法,最后在第三部分中了解如何在玩具数据集上轻松应用它(拟合/预测✌️)并评估其性能。**
已经熟悉 LDA 和主题建模的读者可能想跳过第一部分,直接进入第二和第三部分,这两部分介绍了一种新的短文本主题建模及其 Python 编码的方法🐍。
一.潜在的狄利克雷分配
最流行的话题建模算法是 LDA,潜在狄利克雷分配。让我们首先解开这个令人印象深刻的名字,对它的作用有一个直觉。
- 潜伏因为题目很“隐蔽”。我们有一堆文本,我们希望算法将它们分成对我们有意义的簇。例如,如果我们的文本数据来自新闻内容,通常发现的聚类可能是关于中东政治、计算机、空间…但我们还不知道它。
- 狄利克雷代表狄利克雷分布,该模型将其用作生成文档-主题和词-主题分布的先验。
- 因为我们想给我们的文章分配主题。
下面的图 1 描述了 LDA 步骤如何在文档语料库中清晰地找到主题。
“通过对这些主题的混合物进行采样,然后从该混合物中对单词进行采样,生成一个文档”(吴恩达、大卫·布莱和迈克尔·乔丹,来自 LDA 最初的 论文 )。

Figure 1: LDA documents generation process using Dirichlet distribution.
NB :在上面的图 1 中,为了便于说明,我们在词汇表中设置了 K=3 个主题和 N=8 个单词。为了便于说明,我们还将这些主题命名为计算机、空间和中东政治(而不是称它们为主题 1、主题 2 和主题 3)。事实上,我们的任务是理解 3 个发现的主题是关于计算机、空间和中东政治的内容(我们将在第三部分 STTM 管道的主题归属中更深入地了解这一部分)。
简而言之,LDA 通过使用狄利克雷分布作为先验知识来生成由主题组成的文档,然后更新它们,直到它们匹配基本事实。
二。短文本主题建模(STTM)
尽管 LDA 在中型或大型文本(> 50 个单词)上取得了很好的效果,但通常邮件和新闻文章都在这个大小范围内, LDA 在短文本上表现不佳,如 Tweets、Reddit 帖子或 StackOverflow titles 的问题。

Figure 2: Example of short texts and the topic they discuss.
查看上面图 2 中的短文本示例,很明显,假设文本是主题的混合物(记住图 1 中的第一步)不再正确。我们现在假设一篇短文仅由的一个主题组成。
Gibbs 抽样 Dirichlet 混合模型 ( GSDMM )是一种“改变的”LDA 算法,在 STTM 任务上表现出很好的结果,这使得最初的假设: 1 主题↔️1 文档。文档中的单词是使用相同的唯一主题生成的,而不是像在原始 LDA 中那样来自主题的混合。
在深入代码和实践方面之前,让我们先了解一下 GSDMM 和一个称为电影组过程的等效过程,它将帮助我们了解 STTM 的不同步骤和过程,以及如何有效地调优其超参数(我们记得 LDA 部分的 alpha 和 beta)。
想象一下,一群学生在餐馆里,随机坐在 K 张桌子旁。他们都被要求在纸上写下他们最喜欢的电影(但必须是一个简短的名单)。目标是以这样一种方式将他们聚集在一起,使得同一组中的学生分享相同的电影兴趣。为此,学生必须根据以下两条规则逐一选择新的桌子:
- 规则一:选择学生多的桌子。这条规则提高了完整性,所有对同一部电影感兴趣的学生都被分配到同一个桌子上。
- 规则 2:选择一张桌子,让学生分享相似电影的兴趣。这个规则旨在增加的同质性,我们只希望成员们在一张桌子上分享同一部电影的兴趣。
重复这个过程后,我们预计一些表格会消失,另一些会变得更大,最终会有一群学生符合他们对电影的兴趣。这就是 GSDMM 算法所做的事情!
三。STTM 实施
在这一部分中,我们将使用 Scikit-learn 中用于文本主题建模的 20 个新闻组数据集,从一个具体示例中构建完整的 STTM 管道。
首先,我们需要从 Github 下载 STTM 脚本到我们的项目文件夹中。
cd sttm_project
git clone [https://github.com/rwalk/gsdmm.git](https://github.com/rwalk/gsdmm.git)
现在,我们可以开始实现 STTM 管道(这是我使用的笔记本的静态版本)。
# Useful libs
from sklearn.datasets import fetch_20newsgroups
import pickle
import pandas as pd
import numpy as np# STTM lib from Github
from gsdmm import MovieGroupProcess# Custom python scripts for preprocessing, prediction and
# visualization that I will define more in depth later
from preprocessing import tokenize
from topic_allocation import top_words, topic_attribution
from visualisation import plot_topic_notebook# Load the 20NewsGroups dataset from sklearn
cats = ['talk.politics.mideast', 'comp.windows.x', 'sci.space']
newsgroups_train = fetch_20newsgroups(subset=’train’, categories=cats)
但是在这个练习中,我们不会用新闻的全部内容来从中推断一个话题,而是只考虑新闻的主题和第一句话(见下图 3)。事实上,我们需要短文本来进行短文本主题建模…很明显🙏
此外,为了便于说明,我们将只查看 3 个主题(均匀分布在数据集中)。这些主题如下:
- 中东政治🌍
- 空间👾
- Windows X 🖥

Figure 3: Example of a news about the topic: Windows X. We concatenate these 2 highlighted sentences together to have a document.
# Preprocessing and tokenization
tokenized_data = tokenize(df, form_reduction='stemming', predict=False)
以下是我在这项任务中遵循的预处理配方:
- 使用 spaCy 记号化器进行记号化。
- 删除停用词和 1 个字符的词。
- 使用 nltk 库的词干分析器进行词干分析(根据我的经验,我观察到词干分析比词干分析在短文本上给出了更好的聚类)。
- 删除空文档和超过 30 个令牌的文档。
- 移除唯一令牌(术语频率= 1)。

Figure 4: We do match the Short Text statistics regarding the number of token in our documents, referring to the STTM survey paper p.10.
然而,必须记住预处理是数据相关的,如果使用不同的数据集,应考虑采用其他预处理方法。
既然我们的数据已经被清理并处理成适当的输入格式,我们就可以训练模型了🚀
# Train STTM model# Init of the Gibbs Sampling Dirichlet Mixture Model algorithm
# K = number of potential topic (which we don't know a priori)
# alpha =
# beta =
# n_iters = number of iterations to
mgp = MovieGroupProcess(K=10, alpha=0.1, beta=0.1, n_iters=30)vocab = set(x for doc in docs for x in doc)
n_terms = len(vocab)y = mgp.fit(docs, n_terms)# Save model
with open(‘dumps/trained_models/v1.model’, “wb”) as f:
pickle.dump(mgp, f)
f.close()
让我们深入了解 gsdmm:模型的超参数机制
- K = 10。在实际情况下,我们不知道主题的确切数量,所以我们希望选择一个更高的值。理论上,GSDMM 应该清空无用的簇,并最终找到簇的确切数目。这里不会出现这种情况,但没什么可担心的,我们稍后会更深入地解释这种情况。
- 阿尔法= 0.1贝塔 = 0.1。在这里,我们保留了默认参数(对于几个数据集来说,这些参数工作得很好)。然而,人们可能希望对它们进行调优,以改善关于集群的完整性和同质性的主题分配。不要犹豫参考原文📖为了理解这两个参数之间的平衡。
- n_iters = 30。根据原始论文,GSDMM 在几个数据集上收敛得相当快(大约 5 次迭代),并且保持非常稳定。因此,对于任何类型的数据集,30 次迭代都是一个很好的默认值。
一旦模型被训练,我们想要探索发现的主题,并检查它们在内容上是否一致🔎
假设我们的模型已经将文档收集到 10 个主题中,我们必须给它们一个对其内容有意义的名称。因此,让我们深入研究我们的模型所发现的主题。
doc_count = np.array(mgp.cluster_doc_count)
print('Number of documents per topic :', doc_count)
print('*'*20)# Topics sorted by the number of document they are allocated to
top_index = doc_count.argsort()[-10:][::-1]
print('Most important clusters (by number of docs inside):', top_index)
print('*'*20)# Show the top 5 words in term frequency for each cluster
top_words(mgp.cluster_word_distribution, top_index, 5)
上面的代码显示了下面的统计数据,这些数据让我们了解我们的集群是由什么组成的。

Figure 5: 1) Number of documents by cluster (or topic) index. 2) The sorted list of clusters regarding the number of documents they contain. 3) The top 5 words regarding their frequency inside a cluster.
理想情况下,GSDMM 算法应该找到正确的主题数,这里是 3,而不是 10。我想到三种解释:
- 寻找其他超参数以清空较小的集群(参考原始论文以更深入地理解α和β参数)。
- 我们既有小数据集又有词汇表(大约 1700 个文档和 2100 个单词),这对于模型来说可能难以推断和区分主题之间的显著差异。像往常一样,数据越多越好。
- 该算法可能会在主题中找到主题。让我解释一下。众所周知,其中一个话题是关于中东的新闻。然而,该算法将该主题分成 3 个子主题:以色列和真主党之间的紧张局势(聚类 7)、土耳其政府和亚美尼亚之间的紧张局势(聚类 5)或以色列的犹太复国主义(聚类 0)。
然而,即使找到了 3 个以上的集群,我们也很清楚如何将它们分配到各自的主题中。
# Must be hand made so the topic names match the above clusters
# (Figure 5) regarding their contenttopic_dict = {}
topic_names = ['x',
'mideast',
'x',
'space',
'space',
'mideast',
'space',
'space',
'mideast',
'space']for i, topic_num in enumerate(top_index):
topic_dict[topic_num]=topic_names[i] df_pred = topic_attribution(tokenized_data, mgp, topic_dict, threshold=0.4)
有人可能会问,topic _ attribute函数的阈值输入参数是什么。实际上,主题被分配给给定概率的文本,并且topic _ attribute是一个定制函数,它允许选择考虑哪个阈值(置信度)以便属于一个主题。例如,查看主题到文本的最高概率分配,如果该概率低于 0.4,则该文本将被分配到“其他”主题中。
NB : 这个定制的topic _ attribute函数建立在 GSDMM 包中的原始函数之上: choose_best_label,输出最有可能属于某个文档的主题。
现在是时候将找到的主题分配给文档,并将它们与地面真相进行比较(✅对❌)
df_pred[['content', 'topic_name', 'topic_true_name']].head(20)

天真地比较预测的主题和真实的主题,我们会有 82%的准确率!🎯
仅用一个 9 字平均的文档,一个 1705 个文档的小语料库和很少的超参数调优!

🙌
拥有一个高效的模型当然很好,但如果我们能够简单地展示其结果并与之互动,那就更好了。为此, pyLDAvis 是一个非常强大的主题建模可视化工具,允许在二维空间维度中动态显示集群及其内容。

Screen-shot of pyLDAvis ability. Check out this video to see its full power.
现在轮到你用自己的数据(社交媒体评论、在线聊天的回答……)来尝试了💪
瞧啊!👌
感谢拉贾·埃尔·海姆达尼对我的评价和反馈。
参考资料和其他有用的资源 ——GSD mm
的原文——实现 STTM 的好看的 python 包。pyLDAvis 库漂亮地可视化了一堆文本中的主题(或者任何单词包一样的数据)。
-最近对 STTM 的一项对比调查看其他策略。
PS :对于那些愿意深入 STTM 的人来说,有一种有趣的更进一步的方法(我现在还没有亲自探索过)叫做 GPU-DMM ,它显示了 SOTA 在短文本主题建模任务上的结果。简而言之,GPU-DMM 正在使用预先训练的单词嵌入作为外部知识来源来影响单词的采样,以生成主题和文档。
最短路径相似性:基于项目的推荐的一股清新之风
在本文中,我描述了最短路径相似性——一种替代的基于项目的协同过滤推荐算法。

Image by bertvthul from Pixabay
任何推荐系统的目标都是吸引消费者。新闻网站希望读者阅读更多的新闻,视频主机希望观众观看更多的视频,在线商店希望买家购买更多的东西,等等。在这方面,常见的方法是找到与消费者刚刚阅读/观看/购买的内容相似的内容,并建议他们作为下一个要考虑的内容。这种类型的推荐通常被称为基于项目的,这就是我要解决的问题。
在过去的十年里,推荐系统有一个相当稳定的配方。简而言之,其内容如下:
我的建议是在列表中设置第 1a 段。也就是说,除了传统的协同过滤,还有另一种选择:最短路径相似性算法。
首先,它不存在冷启动问题。也就是说,在有一部新电影上映的情况下,我们知道十个人对后者进行了评级,我们也知道这十个人对二十部其他电影进行了评级:我们的余弦相似性算法有二十部电影可供选择,以推荐给新电影。不多,真的,特别是考虑到收视率不一定相等,也就是说,这二十部电影不太可能相似。同时,《最短路径》将遍历这个图,达到你想要的深度,并且可能会有更多的电影放在桌面上,这些最近的二十部电影中的大多数可能会排在一些更远但更相关的电影之后。
其次,在应用程序中,当相似性是必要的,但对于相关性来说是不充分的,最短路径产生更多的逻辑推荐。我们生活中做的很多事情都是有因果关系的。当一个人不打算喝咖啡的时候,他是不会给自己冲咖啡的,对吗?我们看了《真探》第一集后就不会看第三集了,但不会看第二集。余弦相似度算法在检查了用户的评分后对这三集了解多少?他们彼此几乎一样相似。猜顺序没线索。同时,最短路径确实说明了事情发生的顺序。无论何时定义用户的分级权利(例如,设置:观看整集:正面,中途停止观看:负面,加上忽略明星、喜欢和类似的其他人),最短路径毫不费力地预测最可能的序列。
但是,我并不是说余弦相似度算法本身有问题。当您将一个文档与已学习的特征矩阵进行匹配以找到相似的文档时(例如,针对 TFIDF 矩阵的搜索查询),它非常有用。它的常见问题是误用,余弦相似度不太适合用作独立的推荐算法,例如,在视频托管和新闻网站上,文章的相似度不一定转化为高点击概率。
请查看脚本,它使您能够通过余弦相似性和最短路径相似性并排比较推荐。该脚本是用 Python 编写的,包含对实现方面的详细解释。推荐基于 MovieLens 最新的小型数据集。示例输出如下所示:
让我试着解释一下它是如何工作的。考虑以下五部电影的图表,这五部电影由五个用户进行评估:

其中每个用户给出一个如表中所示的等级:

让我们对给定的输入应用余弦相似度。实际上,我们只需要第二个数字。设θ是矢量 M1 和 M2 之间的角度。为了求出该角度的余弦值,我们使用以下公式:

从欧几里得点积的公式中导出。请注意,在这种情况下,n = 3,我们无法考虑 U2 和 U3 给出的评级,因为他们对 M1 的评级是未知的。我们得到的是:

哇,看起来他们非常相似,虽然,一切都是相对的。让我们看看所有的相似之处:

Cosine similarity item-to-item recommendations
看,M5 碰巧更相似。请注意,在实际应用中,标准化输入值总是更好(例如,按比例缩放它们,使它们落在 0 和 1 之间)。在这种特殊情况下,规范化不会对排名产生任何影响,但是当相似性值之间的差异越来越接近浮点数的精度限制时,规范化就派上了用场。
好了,现在我们有了一些结果。但并不是所有的 5 部电影都得到了 4 个推荐,即使输入矩阵的稀疏度没有那么高。这就是通常所说的冷启动问题,也称为稀疏性问题。余弦相似性不仅需要更多的数据来产生更好的推荐,而且需要更多的数据来产生推荐。
还有另一个问题,没有前者那么明显。相似之处是镜像的。这意味着,例如,sim(M1-M2) = sim(M2-M1)。嗯,这听起来合乎逻辑,因为这是的相似性。但我们正试图在第一时间提出建议。想象一下,M1 是《回到未来》,M2 是《回到未来 2》。虽然它们在两个方面都很相似,但作为推荐,它们是否同样相关?你可能会说,当然,马蒂在第二集的开头就回到了 1955 年。但是让我请你向上滚动一点,看第一个图,沿着 M1 和 M2 之间的所有箭头。是的,两个用户选择了同一个方向,没有人选择相反的方向。如果这是相关性不平等的有效指示呢?用深度学习来整理会是一个很好的答案。但是有一个更简单的方法。
让我们看看最短路径相似性算法在这种情况下是否有什么好处。首先,为了清楚起见,让我们将原始图形展平成一个用户路径表:

Path of each user
很好,现在,既然我们有了一个图,我们可以给它的边分配不同的长度。在这种特殊情况下,最符合逻辑的是从电影分级中导出长度。我想出了下面的公式:

其中 v 和 v’是连接的顶点,r_iv 和 r _ iv’是由第 I 个用户给出的评级,w_r_i 是第 I 个用户意见的权重,w _ v’是给定推荐候选的全局权重。但是,当您想要尝试其他数据的最短路径相似性时,我鼓励您仔细考虑计算长度的最佳方式。我提出的公式对于最短路径相似性算法来说并不重要,也并不完美。顺便说一下,余弦距离可能是一个很好的选择。
在现实世界的应用中,用户意见和推荐候选的权重可以从学习数据集中的出现频率中导出。例如,当一个用户对一部电影的评价高于平均水平时,降低后者的权重是有意义的。如果一部电影太受欢迎,我们可以减轻它的重量。为了简单起见,在这种情况下,有 5 部电影和 5 个用户,我假设所有用户的权重相等,所有电影的权重相等。因此,我以下面的邻接表结束:

请注意,这还不是推荐列表,尽管每个最近的顶点将不可避免地成为最相关的推荐。现在,该说的都说了,该做的都做了,是时候寻找最短的路径了。我建议使用带有优先级队列的 Dijkstra 算法,对于 M1,该算法将如下工作:

从 M1 出发,它将引导我们前往 M2,然后通过 M2 到达 M4,因为没有直达航班,然后通过 M2 到达 M5,最后通过 M2 和 M4 到达 M3。通过每个顶点进入图表后,我们得到了以下建议表:

请注意,现在我们有完整的推荐,我们不能从五部电影中获得更多。虽然,我不会说这是一个伟大的成就,因为我们允许单个用户连接一些顶点,这在实际应用程序中不一定是一个好主意。但更重要的是,现在,尽管 M2 是与 M1 最相关的推荐,反过来,M1 与 M2 最不相关。
所以,就这么简单:跟随用户的路径,记录用户对每对文档的意见,找出对每对文档的平均意见,以确定它们之间的距离,使用最短路径算法,其中记录每个访问过的文档作为对你已经开始的文档的推荐,就这样。这样做是很自然的,因为推荐系统的目标是预测用户想要一个接一个消费的最可能的序列。在我提出的最短路径相似性算法中没有费马定理的证明。它的所有部分都是肤浅的,在软件开发中被广泛使用。如果您对更多细节感兴趣,或者有一个数据集或应用程序,您想尝试最短路径相似性:请不要犹豫联系我们。请查看 https://recom.live ,这是一个基于最短路径相似度的实时推荐系统。
应该由人工智能还是人类来做最后的决定?
这是我“不太常见”的回答——我的两分钱。

长话短说。
几天前,我参加了一个著名教授的分享会,有一件事引起了我的注意(这已经作为本文的题目提到了)。
有趣的是,分享会上的这个话题变成了参与者和教授之间无休止的(几乎)问答式的对话。
这让我进行了更深入的思考,随后巩固了我的想法,从业务和技术的角度来回答这个问题。
所以让我们开始吧!
首先,问题本身并不完整,无法给出完整的答案…
应该由人工智能还是人类来做最后的决定?
在我看来,由于问题本身的模糊性和概括性,这个问题没有正确的答案。
如果你仔细辨别,这个问题缺少两个要素——什么和什么时候。
好吧,让我解释一下。
首先,这个问题没有任何上下文或要解决的特定业务问题(这是“什么”)导致一个答案,即是人工智能还是人类应该做出最终决定。第二,即使我们知道“什么”然而我们仍然需要知道在哪个特定的情况下 (也就是“什么时候”)被考虑。
因此,重新定义一下这个问题,应该是这样的:
“什么时候”应该由人工智能还是人类来做最终决定
“解决一个商业问题”?
这个重新设计的问题让人们更容易理解并得出一个更有说服力的答案,而不是用另一个答案去乞求另一个问题。
再次声明,这只是我的观点,确实值得讨论。请在下面留下您的评论!
1.为什么问题中需要上下文或业务问题(“什么”)。
我们都知道所有的公司都有业务问题要解决。
然而,人工智能不能为所有的商业问题做出决定,需要人类的直觉来做出最终决定。
换句话说,要知道是人工智能还是人类应该做出最终决定,我们首先必须知道我们试图解决的商业问题到底是什么。
例如,当涉及到使用谷歌地图选择到达目的地的最佳路线时,我们将决策委托给人工智能。如果没有短时间内的密集计算,人类几乎不可能做出准确的判断和选择。在这种情况下,人工智能应该是为我们做出最终决定的人。
相反,我们还必须理解,某些公式化的问题具有约束,并且模型(所谓的 AI)不再能够基于从历史数据的学习而被推广到每一个单独的问题。
以最近的案例为例,自 2014 年以来,亚马逊一直在使用他们的招聘算法来扫描和审查求职者的简历。一段时间后,亚马逊的机器学习团队发现招聘算法对女性有偏见。这是因为向模型输入了主要来自男性的训练数据——这反映了男性在整个科技行业的主导地位。
同样,在某些情况下仍然需要人类的控制,人工智能意味着赋予人类权力,而不是取代他们。
2.为什么问题中需要一个情境(即“何时”)。
既然我们已经知道要解决什么业务问题。下一步是什么?
是的,我们仍然需要考虑一个特定的情况以及它何时发生。
我这么说是什么意思?
让我们回到前面给出的亚马逊的例子。假设现在问题变成了:
人工智能或人类应该在什么时候做出最终决定来决定雇佣哪些候选人?
答案之一可能是:
AI 应该做出最终决定“决定雇佣哪些候选人”AI 系统没有表现出歧视。**
或者:
“决定雇佣哪些候选人”“当”人工智能系统显示出不良偏差时”,人类应该做出最终决定。**
我们刚刚一起回答了基于业务问题和情况的问题。
无论是哪一种,答案都是清晰明了的。零歧义。
最后的想法

感谢您的阅读。
就我个人而言,我发现这个问题发人深省,因此觉得有必要在这篇文章中分享我的想法。
我希望通过从不同的角度回答这个问题,甚至在试图回答一个问题之前,给你另一个视角。这样会让你的答案更清晰的回答 真正的 问题。
一如既往,如果您有任何问题或意见,请随时在下面留下您的反馈,或者您可以随时通过 LinkedIn 联系我。在那之前,下一篇文章再见!😄
关于作者
Admond Lee 现在的使命是让每个人都能接触到数据科学。他正在帮助公司和数字营销机构通过创新的数据驱动方法,利用可操作的见解实现营销投资回报。
凭借其在高级社会分析和机器学习方面的专业知识,Admond 旨在弥合数字营销和数据科学之间的差距。
如果你想了解更多关于 Admond 的故事、数据科学服务以及他如何在营销领域帮助你,请查看他的 网站 。
你可以在 LinkedIn 、 Medium 、 Twitter 、脸书上和他联系。
让每个人都能接触到数据科学。Admond 正在通过先进的社交分析和机器学习,利用可操作的见解帮助公司和数字营销机构实现营销投资回报。
www.admondlee.com](https://www.admondlee.com/)
数据科学家是否应该获得许可?
许可可以增加公共安全,但代价是减缓创新

每天你的生活都受到不同机器学习算法的影响。有些是无伤大雅的,比如关于网飞的电影推荐。其他如贷款审批和保释判决如果发展不当,可能会造成无法减轻的伤害。随着这些模型的影响力越来越大,它提出了一个问题,即数据科学家是否应该像律师和医生一样获得许可。
一个正式的职业将意味着数据科学家将被要求保持一定水平的技术能力,遵守行为准则,并通过由该领域专业人士组成的经批准的委员会进行自我监管。由于数据科学需要特殊的培训,而渎职会导致严重的负面后果,因此有充分的理由成立一个数据科学专业。
然而,相对于其他许可领域,数据科学仍处于起步阶段,预计未来几年将会细分和改变。尽管还处于初级阶段,但一名数据科学家的疏忽所造成的影响将远远超过一名医生的疏忽。因此,需要有一种方法来惩罚不良行为者并维持公众信任。无论是通过一个专业的许可委员会来限制雇用未经许可的数据科学家,还是通过一个证书来确保数据科学家的完整性。最终目标是一样的,保护公众免受专业无能的伤害。
许可的好处
在考虑许可的理由时,最明显的理由是保护公众。去看病,你对服务有一定的期待。你知道他们是有标准的,如果他们行为不端,将会有诸如取消他们行医资格的后果。然而,行为不端的数据科学家可以简单地找到另一份工作,继续他们的职业生涯。
许可的另一个好处是通过标准化对数据科学家的教育和期望。目前,人们对数据科学家的角色有很多困惑,这导致许多不合格的人将自己标榜为数据科学家,以利用薪酬和声望的增长。通过发放许可证来建立标准,无法满足新的准入要求的低素质工人将被淘汰,而更有动力的工人将不得不参加与工作相关的培训,以满足新的期望。例如,在英国,保安和护理人员职业许可证的引入导致了资格水平和工作相关培训的上升。
许可的弊端
然而反对许可的理由同样令人信服。例如,有证据表明过多的许可会阻碍就业机会的创造,尤其是对于受教育程度较低的人来说,这可能会加剧不平等。
决定谁有资格也是一项艰巨的任务,特别是因为数据科学家来自各种背景。一项小型研究 (n=1001)探索了数据科学家在学术研究(20%计算机科学,19%数学,19%经济学)以及他们工作领域(42%技术,37%工业,16%金融,5%医疗保健)等领域的背景差异,展示了背景的鲜明对比。此外,由于这个领域非常新,未来的数据科学家很可能会拥有与今天的数据科学家不同的技能。
另一个可能出现的问题是,通过设置高准入门槛来抑制创新。对于移民人才来说尤其如此,他们可能已经很有能力,但可能需要接受他们在本国已经接受过的重复培训。随着数据科学已经面临短缺,毫无疑问,任何形式的专业许可都会损害就业增长,并显著降低从业者的进入率。这些问题将是以数据科学协会形式产生的垄断的直接结果,这将限制劳动力供应并减少竞争。
将所有这些放在一起
就其核心而言,颁发职业许可证的问题最终是为了保护公众免受不良行为者的影响,并确保质量的一致性。其他好处包括标准化对数据科学家的教育和期望,减少低素质工人的数量。另一方面,由于数据科学是一个如此新的领域,它可能会阻碍就业创造并减缓创新。这可能会导致移民人才寻求进入门槛不高的其他国家就业。
虽然实现许可背后的机制可能很困难,但证书可能是对许可限制较少的选项。专业协会可以管理考试,希望确保数据科学家质量的公司可以选择优先考虑那些有证书的候选人。另一种选择是只许可和监管那些处理敏感数据或大规模模型的数据科学家。十有八九,随着该领域的成熟和数据科学家的角色更加标准化,重新考虑许可的问题,或者至少建立某种制衡机制可能是有意义的。就目前的情况来看,实施任何形式的限制性许可程序最终都可能弊大于利。
数据科学家应该知道如何编写生产代码吗?
好吧,这是我的答案…

欢迎来到 2019 年,这是我在新的一年里第一篇关于媒体的文章。
下面是俗气的部分(如果有必要,可以随意跳过)——2018 年是我人生转折点的一年,在 Medium 上写作被证明是我最好的决定之一,与这么多数据科学爱好者和志同道合的人分享我的经验和学习之旅——比如你。
感谢你在我的骑行旅程中陪伴着我,我希望在 2019 年与你分享更多!
所以……回到今天感兴趣的话题。
事实上,这是我刚开始涉足数据科学领域时的一个难题。有趣的是,这也是数据科学家最常争论的话题之一。
所以现在的问题是:数据科学家应该知道如何编写生产代码吗?
以下是我的回答:是的。
生产代码是一个经过充分测试的稳定代码,它考虑到了现实生活中的场景,并且必须功能强大。
作为一家公司的数据科学家,拥有编写生产级代码的能力是非常受欢迎的技能之一。
如果您是一名软件工程师转数据科学家,这是一个好消息,因为您可能已经通过构建各种生产代码来部署之前的角色,从而发展了这一技能。
如果你像我一样,既不是以前的软件工程师,也没有任何计算机科学背景,我知道你的感受,这就是为什么这篇文章是给你的。
我们开始吧!
为什么是生产代码?
为什么?
作为一名数据科学家,我们为什么应该首先关注生产代码的编写?
因为这是我们的分析和模型真正为最终用户增加价值的地方。在痛苦的几个月(甚至几年)的模型开发之后,如果没有模型的部署,如果它们不能给客户或最终用户带来任何好处,那么模型将永远是模型。
所有这些长时间的数据收集和清理、模型构建和优化以及演示都是为了表明您的模型能够生成结果和见解,以达到业务目标。
一旦您成功地说服了利益相关者(假设您的模型是健壮的,分析从业务角度来看是有意义的,并且结果能够实现业务目标),部署阶段就不会太远了,这就是您需要通过交付生产级代码将模型投入生产的时候。
坦率地说,你的老板并不在乎你用的是什么型号。他/她关心的只是结果。
所以交付结果,你就可以开始了。
还是那句话,看情况。
我想说,要成为一名数据科学家,没有必要知道如何编写产品质量代码。
在一天结束时,这取决于…
根据我的经验,我合作过的一些公司和客户需要数据分析和模型构建,主要是为了他们的内部分析和使用。
这可能意味着您的利益相关者只是想知道基于历史数据的业务指标的性能,或者您的模型如何使业务运营更高效和更具成本效益。在这些情况下,考虑到业务目标,您可能不需要编写生产代码。
对于其他一些公司,在部署给最终用户之前,必须将生产就绪代码集成到他们现有的系统中。
这就是为什么一些数据科学家的工作描述不包括生产代码技能作为要求之一,而是更倾向于一种更可取的技能,因为他们可能有一个软件工程师或 IT 人员团队来帮助生产模型。
最后的想法

感谢您的阅读。
我不能说我是编写产品代码的专家,因为学习之旅绝对不容易。但是我现在仍然每天都在学习,一步一步地提高。
作为一名数据科学家,我希望这篇文章能够帮助您理解编写生产代码的重要性,并掌握这项在工作描述中没有明确说明的重要技能。
在一天结束的时候,你为公司增加了价值,为公司实现商业目标做出了贡献。
一如既往,如果您有任何问题或意见,请随时在下面留下您的反馈,或者您可以随时通过 LinkedIn 联系我。在那之前,下一篇文章再见!😄
关于作者
阿德蒙德·李 目前是东南亚排名第一的商业银行 API 平台 Staq — 的联合创始人/首席技术官。
想要获得免费的每周数据科学和创业见解吗?
你可以在 LinkedIn 、 Medium 、 Twitter 、脸书上和他联系。
让每个人都能接触到数据科学。Admond 正在通过先进的社交分析和机器学习,利用可操作的见解帮助公司和数字营销机构实现营销投资回报。
www.admondlee.com](https://www.admondlee.com/)
医学中的数据科学——精确与回忆还是特异性与敏感性?
理解精确性、回忆性和特异性、敏感性之间的区别

在数据科学社区中,通常会通过精确度和召回率来评估您构建的模型。在医学领域,通常通过特异性和敏感性来评估医学测试。这些概念非常相似,但又不同。当这两个世界相遇时,当医学测试是机器学习模型时,这种差异可能会导致医学世界和数据科学人之间的许多误解。
那么,让我们来看看定义:


Precision — Out of all the examples that predicted as positive, how many are really positive?

Recall — Out of all the positive examples, how many are predicted as positive?

Specificity — Out of all the people that do not have the disease, how many got negative results?

Sensitivity — Out of all the people that have the disease, how many got positive test results?
如果我们将一个正面的例子定义为“患有疾病的人”,我们可以看到回忆和敏感性是相同的,但是精确性和特异性是不同的。精度也叫 PPV(阳性预测值)。从现在起,我们将把敏感性称为回忆。
这些定义非常简单,然而,当我试图理解它们的组合对我的算法意味着什么时,我发现自己很困惑。精确度、召回率和灵敏度的每一种组合都是可能的吗?在哪些情况下我做错了什么?
如果有帮助的话,你可以称特异性为当阳性标签被定义为阴性,而阴性标签被定义为阳性时对同一问题的回忆。
为了更好地理解它,我创建了 8 个不同的分类问题和分类器。每个分类器试图以最大化或最小化每个测量值的方式将 10 个样本分类到阳性和阴性“篮子”中。
示例 1 —低精度、高召回率和高特异性

如果分类器预测是否定的,你可以相信它,例子是否定的。但是,注意,如果例子是否定的,你不能确定它会预测为否定的(特异性= 78% )。
如果分类器预测是肯定的,你不能相信它(精度= 33% )。然而,如果这个例子是正面的,你可以信任这个分类器(回忆= 100% )。
示例 2 —高精度、高召回率和低特异性

将一切都预测为积极显然不是一个好主意。但是,由于人口不平衡,精度相对较高,召回率为 100% ,因为所有的阳性样本都被预测为阳性。但是特异性是 0% 因为没有阴性的例子被预测为阴性。
示例 3 —高精度、低召回率和高特异性

这可能是一个有用的分类器——如果它预测一个例子是正面的,你可以相信它——它是正面的。然而,如果它预测它是负的,你不能相信它,很可能它仍然是正的。
示例 4 —低精度、低召回率和高特异性

这个分类器真的很糟糕——它几乎把所有预测为负面,当预测为正面时——它是错误的。实际上,使用与这个分类器预测相反的东西会更好。
示例 5 —高精度、低召回率和低特异性

在这里,与这个分类器预测的相反应该会工作得更好。
示例 6 —低精度、高召回率和低特异性

这个分类器可能没用——它预测一切都是肯定的。所以,它当然可以完美地检测出所有的正面例子(高召回率),但是使用它你得不到任何信息。
示例 7 —高精度、高召回率和高特异性

这是“圣杯”——分类器将所有正面的例子检测为正面的,将负面的例子检测为负面的。所有措施都处于 100% 。
示例 8 —低精度、低召回率和低特异性

这看起来像一个糟糕的分类器,所有的正例被预测为负,所有的负例被预测为正。所有措施都是 0%。然而,你可以简单地做与它预测相反的事情,然后它就完美了👌。
总之,所有的度量(精确度、召回率和特异性)都给了我们关于分类模型好坏的重要信息。重要的是要把它们都看清楚。例如,在不考虑特异性的情况下,您可以创建一个非常精确的模型,并回忆简单地预测所有事情都是真实的,并且没有实际价值(如上面的示例 2)。
数据人员-不要忘记特殊性!尤其是当你在和医学界打交道的时候。
我应该开源我的模型吗?
决定是否开源机器学习模型的最佳实践
我已经研究开源机器学习与敏感性的问题很长时间了,特别是在灾难应对的背景下:什么时候公开发布数据或模型是对的/错的?本文列出了一些常见问题、当今最佳实践的答案,以及我在哪里遇到过这些问题的一些例子。

What are the risks of sharing your Machine Learning model?
OpenAI 本周引发了一场风暴,当时他们发布了新的研究,显示了许多人工智能任务的最新成果,但他们决定不发布他们的机器学习模型,这违背了研究界的普遍做法:
“由于我们担心该技术的恶意应用,我们不会发布经过训练的模型。”
对 OpenAI 决定的批评包括它如何限制了研究社区复制结果的能力,以及这一行动本身如何加剧了媒体对 AI 的恐惧,这种恐惧现在被夸大了。
正是这条推文首先引起了我的注意。Anima Anandkumar 在弥合机器学习的研究和实际应用之间的差距方面有很多经验。我们是 AWS 的同事,最近一起讨论了机器学习从博士到产品的问题(https://vimeo.com/272703924)。
Stephen Merity 很好地总结了社交媒体的反应,他感叹说,机器学习社区在这方面几乎没有经验:
我在机器学习和灾难响应方面的经验很少。在来美国完成斯坦福大学自然语言处理博士学位之前,我在塞拉利昂和利比里亚从事冲突后发展工作,从那以后,我一直在工业和灾难应对领域工作。公平地说,与我一起工作的大多数人以前从未考虑过数据和模型的敏感性,因此本文希望能够弥合这一差距。
本文旨在作为一个独立的资源,供提出这些问题的人参考。如果你想看引发它的文章,这里有 OpenAI 的帖子,其中包含了他们的担忧,如生成类似假新闻的内容和在网上假冒他人的能力:
我们已经训练了一个大规模的无监督语言模型,它可以生成连贯的文本段落,实现…
blog.openai.com](https://blog.openai.com/better-language-models/)
下面是引起如此多讨论的推文——你可以阅读在我写这篇文章的时候仍然收到的回复:
OpenAI 保留他们的模型,而只提前给记者,这是对还是错?我让你来决定。对我来说(正如我下面要讨论的), OpenAI 在两个本可以缓解问题的领域失败了:调查虚假内容是否能被检测出来;并发布多种语言的模型来对抗对英语的偏见。
以下是一些问题,您可以在评估自己是否发布模型或数据集的决策时使用:
应该质疑是否开源我的模型?
是的。如果你的模型是建立在私有数据上的,它可以被逆向工程提取私有数据。
我的模型 100%来自公开数据,那么我需要质疑是否开源我的模型吗?
是的。如果在新的上下文中重新发布,发布的数据可能会变得敏感,聚合数据(包括机器学习模型)可能会变得比单个数据点更敏感。
在阿拉伯之春期间,我看到许多人在推特上谈论他们当地的情况:道路封锁、难民等。虽然它们是“公开”的推文,但这些推文显然是为少数几个追随者写的,他们没有意识到报道道路封闭也有助于描绘部队移动的画面。作为不做什么的一个例子,其中一些推文被复制到联合国控制的网站并重新发布,没有任何机制让原作者将它们从联合国网站上删除。中东和北非的许多参与者将联合国视为负面的外国影响(或入侵者),因此发推文的人被视为合作者——他们不在乎这些人是否只想与少数追随者分享信息。
所以,你需要问自己:将数据或模型重新文本化,使其现在由我自己或我的组织发布,会有什么效果?
当单个数据点不敏感时,聚合数据被视为敏感也是很常见的。这是许多军事组织的标准做法:当他们从一组来源汇总数据时,他们会重新评估汇总信息的敏感程度。聚合通常是统计或无监督机器学习的结果,但基于该数据构建的监督模型同样适用。关于聚合如何改变军队中数据敏感性的公开示例,请参见最近的一个案例,其中军队中使用 Strava 的跑步者意外泄露了基地的位置,当时它显示了军队中跑步最多的人的热图:
现代版的二战时期的警告“信口开河会沉船”可能是“FFS 不要分享你的 Fitbit…
www.wired.com](https://www.wired.com/story/strava-heat-map-military-bases-fitness-trackers-privacy/)
许多组织选择采用类似的政策。Medium 就是其中之一:在写这篇文章时,根据 Medium 的策展指导方针,我不能“暴露某人的身份,包括暴露个人信息或聚集公共信息”。你也应该这样做。
所以,你应该经常问自己:在我的模型中,数据的集合比单个数据点更敏感吗?
我如何评估风险?
使用与安全性相同的模型:权衡误用的成本与它为不良行为者提供的价值。
在安全方面,你把每一个策略都视为“易破的”。目标是使破坏某些安全措施的成本高于您所保护的数据的价值。所以,不值得坏演员花时间去破它。
对于那些出于负面原因想要使用你的研究论文中的模型的人来说,复制这些模型的成本值得吗?你应该明确这一点。这是你决定是否开源的一个因素。
在 OpenAI 的案例中,他们可能已经决定发布该决定的风险简介,大致如下:我们认为不开源该模型的决定足以阻止互联网上大多数孤独的巨魔重新创建该模型。但我们承认,一大群科学家(可能是国家资助的)以我们的研究论文为指导来重建模型是不够的。
我应该相信记者对风险做出决定吗?
不。或者至少,不是没有问题。记者必须出售内容,而更耸人听闻的内容往往会卖得更多。有意识或无意识地,一个记者可能倾向于开源,因为这样他们更容易写它。另一方面,不开源数据的决定可能会导致耸人听闻的关于该决定所包含的危险的报道(就像 OpenAI 的案例一样)。
我见过很多灾难中糟糕的新闻报道导致的可预防的死亡案例。在 2014 年西非埃博拉疫情中,我预测:
每有一个人感染埃博拉病毒,就会有十个人死于其他可预防的疾病。
medium.com](https://medium.com/@robert.munro/the-silent-victims-of-ebola-e1a8f83185a9)
我是应对疫情的大多数主要政府和援助组织的顾问,因为我之前曾在该地区生活过,并分别在东非从事埃博拉疫情追踪工作。我警告过几乎每一家新闻机构,无论是本地的还是国际的,关于围绕报道的炒作和它可能导致的死亡。
一小群记者听了,但大多数没有。疫情结束后,当塞拉利昂卫生部副部长在旧金山的一次会议上发言时,她报告了这些完全相同的令人悲伤的数字:他们估计,每有一个人直接死于埃博拉,就有 10 人因为不去诊所而死于其他疾病。
所以,在和记者谈论你的研究时要非常小心。
我如何确保记者负责任地谈论我的机器学习研究?
接受媒体培训!许多组织都有媒体培训,即使只有几个小时也会有所帮助。我不能在这篇文章中总结你需要知道的所有事情,但是这里是我发现的通常有效的最重要的事情:
问记者他们的故事是关于什么的。
如果他们写的是机器学习研究的进展,那么你可能没问题。如果他们写的是“人工智能的危险”,或“假新闻”,或“干涉选举”,那么你应该越来越小心你的采访可能会被歪曲以适应他们的叙述。
问一个故事是关于什么的策略对我来说很有效,但我记得只有一个例外:一名 BBC 记者正在写一篇关于英语如何主宰互联网的文章。我在接受采访时说,不,英语在互联网上的份额一直在稳步下降:人们更喜欢他们的主要语言,英语正在成为“互联网的第二语言”。但是他们报道我说“英语正在成为互联网的语言”。如果发生这种情况,你也无能为力 BBC 的影响力比我否认我说过这话的推特更大。你可以要求媒体机构修改文章,或者至少公开声明你被错误引用了。
我应该相信政府会做出风险决策吗?
不。很明显,你不应该违反法律。但不能因为合法就说可以。政府是一群像其他人一样的人,试图让他们的头脑面对机器学习的真实和不那么真实的威胁。
政府也容易受到媒体的影响。为应对埃博拉疫情,利比里亚政府关闭了边境。在边境工作过之后,我知道这完全是一场闹剧。边界是一系列相连的村庄、河流、小溪和森林小径,早在今天的官方边界之前就存在了。“关闭”边境和共享边境感染数据让政府看起来很果断,但最终几乎没有人能免受埃博拉病毒的影响,而是让他们远离诊所去治疗其他可治疗的疾病,让情况变得更加糟糕。
像记者一样,将政府视为重要的合作伙伴,但要认识到你们都有不同的议程,其中许多议程将是一致的,但不是全部。
我应该从我的模型中调查负面用例的解决方案吗?
是啊!这是 OpenAI 的失败之一。如果他们认为他们模型的输出可以用来制造假新闻,那么这是可以检验的:
你能创建一个文本分类任务来区分人类书写的内容和 OpenAI 模型的输出吗?
这是一个 OpenAI 可以在几天内运行的实验,它将让我们更好地了解这到底是一个多大的问题。
最近,我与脸书进行了长时间的会谈,讨论加入他们的角色,负责发现假新闻。从一个正在解决这个问题的人的角度来看这个问题,这是我想知道的第一件事:我能通过编程来检测这种模型输出,以便解决它吗?我最终决定担任目前的职位,原因与这个角色本身无关——我认为打击脸书的假新闻是任何人现在都可以做的最重要的事情之一——open ai 的这项额外研究将会有所帮助。更好的是,如果您可以创建一个可以识别生成内容的模型池,那么创建击败所有模型并通过自动检测系统的生成内容将变得更加困难。
如果您可以定量地证明,数据的负面用例更容易或更难应对,那么这将是您决策过程中的一个因素。
这是机器学习的新问题吗?
不,而且你可以从过去的经验中学到很多。
2014—2015 年,沙特阿拉伯政府曾三次找我帮忙监控社交媒体上的异见人士。当时我是 Idibon 的首席执行官,这是一家大约 40 人的人工智能公司,拥有大量语言中最准确的自然语言处理技术,因此我们自然被视为最适合他们用例的技术。沙特阿拉伯的一个部门首先直接接触了我们,然后是间接接触,一次是通过一家精品咨询公司,一次是通过世界上五大咨询公司之一。在每一个案例中,公开的目标都是帮助那些抱怨政府的人。经过与沙特阿拉伯和机器学习专家的仔细磋商,我们决定使用一个识别投诉的系统来识别持不同政见者。由于沙特阿拉伯是一个不经审判就迫害持不同政见者的国家,而且经常是暴力迫害,我们拒绝提供帮助。
如果你正面临类似的困境,请寻找有知识深度的人来谈论最受影响的社区(理想情况下是该社区内的人)以及过去面临类似机器学习问题的人。
假新闻是新问题吗?
不。宣传可能和语言本身一样古老。
2007 年,当我护送记者报道塞拉利昂选举时,我们不断听到暴力报道。我们将跟踪这些报道,以发现没有实际的暴力行为。原来是一家盗版电台在播放假新闻,其中一些是由合法电台接收的,假新闻的意图是将一个或多个政党的支持者描绘成暴力分子,并可能吓得人们根本不去投票。
在塞拉利昂最近的选举中,我看到社交媒体上流传着类似的关于暴力和选举舞弊的假新闻。大型社交媒体公司负责假新闻的人都悄悄地向我承认,他们无法识别塞拉利昂和其他许多国家的大多数语言中的假新闻。
所以,宣传已经在这里很长一段时间了,它使用了一切可用的技术来扩大信息的传播。最大的差距是在对抗宣传的方式上,这意味着在大多数情况下英语之外的更好的人工智能。
我应该专注于平衡机器学习的坏用例与更明显好的用例吗?
是的。通过发布主要具有积极应用领域的模型,很容易对世界产生积极的影响。限制一个有很多负面应用领域的模型发布,很难对世界产生正面影响。
这是 OpenAI 的另一个失败,他们缺乏多样性。与其他任何研究小组相比,OpenAI 发布了仅适用于英语和(很少)少数其他高特权语言的模型和研究。英语只占世界日常对话的 5%。在句子中的词序需要有多严格,在标准化拼写中,以及在“单词”作为机器学习功能的原子单位如何有用方面,英语都是一个异数。OpenAI 的研究依赖于这三个方面:词序、词作为特征、拼写一致。它能适用于世界上大多数的语言吗?我们不知道,因为他们没有测试过。OpenAI 的研究告诉我,我们需要担心英语的这种内容生成,但它没有告诉我今天假新闻流通的 100 多种其他语言的风险。
坦率地说,OpenAI 的多样性问题根深蒂固。当我和几十个人一起注意到一次人工智能会议有 30 多位全是男性的演讲者,并且 OpenAI 的首席科学家是第一位演讲者时,OpenAI 忽视了这些抱怨。
尽管不同的人公开或私下发了一些信息,但我不知道 OpenAI 采取了什么行动来解决这个多样性表现的问题。
我个人拒绝所有我认为会议阵容在机器学习社区中延续偏见的演讲邀请,我知道许多人也这样做。很可能 OpenAI 对多样性的更宽松的态度导致了不多样化的研究。在实践中,我一般不相信世界上 95%的语言只有英语的结果。OpenAI 有很多很好的基础研究,比如如何让任何模型更轻量级,从而在更多的上下文中可用,但他们的英语语言重点限制了积极的用例。
如果你不想踏入假新闻这类应用的灰色地带,那么就选择一个天生更有影响力的研究领域,比如低资源语言中健康相关文本的语言模型。
我需要多深入地考虑用例的敏感性?
一直到各个领域。当我为 AWS 的命名实体解析服务运行产品时,我们必须考虑是否要将街道级地址标识为一个显式字段,并潜在地将坐标映射到该地址。我们认为这是固有的敏感信息,不应该在通用解决方案中产品化。
在任何研究项目中考虑这一点:您是否在您的模型中明确或隐含地识别敏感信息?
我是否应该开源我的模型,仅仅因为其他人都这样做?
不。你应该总是质疑你自己的影响。
无论你是否同意 OpenAI 的决定,他们在做出明智的决定而不是盲目跟风发布完整模型方面是正确的。
我还应该担心什么?
可能有很多东西我今天没有讲到!我写这篇文章是作为对 OpenAI 昨天宣布的快速回应。如果有需求,我会花更多时间分享最佳实践!
机器学习算法应该指导抗生素处方吗?

Photo by freestocks.org on Unsplash
机器学习可以解决抗生素耐药性危机。算法在现实世界中的表现会像在实验室中一样好吗?
答抗生素耐药性正成为一个严重的问题——目前,每年有超过 700,000 人死于抗生素耐药性感染。到 2050 年,预计将有 1000 万人死于耐药性感染。
帮助阻止抗生素耐药性传播的方法
许多感染是“凭经验”治疗的,这意味着如果医生知道你感染了什么细菌,他们会开出标准的抗生素来治疗。这意味着,如果你患有对标准抗生素有耐药性的感染,在你服用抗生素的过程中,细菌仍然在你体内存活和复制。随着时间的推移,这可能导致抗生素耐药性细菌的流行率上升。
人们对在病人开始治疗前测试抗生素耐药性越来越感兴趣。下图有点复杂,但它显示了首次开出正确抗生素的潜在长期影响。

Image: Tuite et al. 2017
图 A 显示了给每个人开两种抗生素组合处方的效果。在几十年的时间里,对这种疗法产生抗药性的感染数量会增加。
B-D展示了如果在开处方前将患者对其中一种药物的耐药性检测从 10%增加到 50%会发生什么。对三种可用抗生素的耐药性实际上上升了,这是一种更糟糕的情况。
但是面板显示了所有三种可用抗生素在 10-50%人口中测试的影响。随着检测覆盖率的增加,对抗生素的耐药性停止增加,大多数感染对所有抗生素都敏感。
将此付诸实践
在开处方前测试抗生素耐药性似乎是一个很好的方法。然而,有一些实际的限制妨碍了这种日常工作。一个主要的问题是,当人们去看医生或去医院接受治疗时,他们希望立刻得到抗生素来治疗他们的感染。实验室耐药性测试可能需要 24 小时(如耐甲氧西林金黄色葡萄球菌)到数月(如肺结核)的时间。

随着时间的推移,全基因组测序变得越来越便宜,使其成为检测抗生素耐药性的更实用的方法。理论上,测序可以在几个小时内给出结果,而不是几天。驱动这些细菌耐药性的机制被编码在它们的 DNA 中,这意味着一个简单的测试就可以告诉我们对一组抗生素的耐药性,还可以给我们其他有用的信息,比如你感染的菌株是否在上升,或者与当时在医院或社区传播的菌株有关。
我们知道我们需要找到的信息在这些细菌的 DNA 中,但我们并不总是知道如何找到它。这就是机器学习的用武之地。
在过去的几年中,有前途的机器学习研究不断涌现,都声称在预测耐药性方面具有很高的准确性。但是,在科学研究的受控条件下比在现实世界中更容易实现高准确性,因此这些算法在面对临床数据时的表现仍然存在不确定性。
检验 ML 模型的稳健性
这是我和我的同事们非常感兴趣的问题,在本月发表的一篇新论文中,我们开始探讨这个话题。这项研究着眼于三种机器学习方法:
- 集合覆盖机
- 随机森林分类
- 随机森林回归
我们正在调查来自淋病奈瑟菌的七组不同的基因组序列数据,这是一种引起淋病的细菌,每一组都是在不同的地点用不同的取样策略收集的。这项研究着眼于环丙沙星(其耐药机制相当简单)和阿奇霉素(其耐药机制更复杂且了解更少)的耐药性。为了将基因组序列数据处理成模型可以使用的东西,基因组序列被分解成 31 聚体,即 31 个字母长的 DNA 片段。使用 5 重交叉验证调整超参数。
虽然 ML 方法对导致环丙沙星耐药性的简单耐药机制表现良好,但它们对更复杂的阿奇霉素耐药性表现不佳。这是一个遗憾,因为我们可以通过寻找单个突变来非常准确地预测环丙沙星耐药性(> 98%的灵敏度和> 99%的特异性)。我们希望 ML 能揭示更多关于阿奇霉素耐药性的信息。我还在全基因组关联研究中发现了类似的事情,这是一种更为明确地设计用于找出基因组中驱动耐药性的因素的更古老的技术——这些方法在告诉我们已知的东西方面很好,但在揭示我们不知道的东西方面不太好。换句话说,如果历史上人类很难理解某个特征的遗传,那么这些算法也很难理解。

Performance of the different methods on predicting ciprofloxacin resistance (left) and azithromycin resistance (right) using two different clinical definitions of resistance (EUCAST and CLSI). Figure from this paper.
就模型性能而言,我们看到抗生素、物种和方法之间存在许多可变性,从这个项目中得到的主要收获是,一种方法并不适合所有问题,即使是在这个狭窄的问题空间内。这意味着,要设计一个我们可以发送所有临床数据的管道并不容易——需要有一个流程来为每个问题选择正确的模型。
我们正在研究的一个主要因素是采样偏差。这可以以仅从特定患者群、特定时间段或特定地理区域取样的形式出现。采样偏差是任何预测建模中的一个主要挑战。在耐药性预测中,从特定患者人口统计数据或在有限的时间段内进行采样会影响耐药性样本的数量以及您捕获的耐药机制的多样性。

Distribution of MIC values (a measure of the concentration of antibiotic bacteria can tolerate before it impairs their growth) for the seven different collections — each collection is shown in a different colour. Figure from this paper.
在不同的淋病数据集中,有些是在当地收集的,有些来自全球收集,有些来自一年,有些跨越 20 多年。我们发现,即使您有一个包含来自世界各地的样本的大型数据集,在此集合上训练的模型在来自新位置的数据上仍然比在本地训练的模型表现更差。有时,在全局集合上训练的模型在来自先前采样位置的新数据上甚至比仅使用来自该位置的数据训练的模型表现更差,这表明存在影响准确性的数据集特定信号(可能是偏差或技术假象)。
这项研究还观察了环丙沙星耐药性预测在肺炎克雷伯氏菌和鲍氏不动杆菌中的表现,这两种细菌比 T4 淋球菌携带的基因范围更广,因此产生更多的 k-mers。对于这些情况,SCM 比 RF 分类器更适合训练数据,但是 RF 分类器总体上不太准确。与淋病一样,寻找单一已知突变比 ML 模型给出了更好或相等的平衡准确性。
这项研究的一个重要收获是不要相信在这个领域建立的只报告其准确性的模型。我们真正关心的耐药谱是那些目前很少对抗生素产生耐药性的谱,所以我们仍然使用抗生素。一个模型可以通过预测所有事物都不具有抗性来实现对这些罕见抗性的高精度。明确了解模型遗漏的耐药分离株的比例和数量非常重要,因为这将导致临床治疗失败和耐药选择。
阅读全文
作者摘要基于机器学习的细菌基因组序列抗生素耐药性预测代表了一个新的研究方向
journals.plos.org](https://journals.plos.org/ploscompbiol/article?id=10.1371/journal.pcbi.1007349)
我们应该为未来技术改造基础设施吗?

Photo by Randy Fath on Unsplash
改变我们现在的世界来适应未来的机器人。
当新技术在公共领域变得流行时,基础设施被修改以更好地适应该技术。在早期殖民时期的美国,港口城市通常采用鹅卵石街道。虽然今天,这种路径被浪漫化了,但它们最初是为了改善马蹄与地面的牵引力而铺设的。作为当时的负重兽,马帮助搬运大量货物,鹅卵石街道给了蹄子更大的推进力。
最近,大城市已经适应了无处不在的智能手机技术。纽约市用充电站和 Wi-Fi 热点取代了电话亭。许多现代交通系统,如飞机和地铁,也可以接入 Wi-Fi。特拉维夫市最近实施了楼层交通灯,让手机粘行人知道十字路口的状态。
这两个例子都是反动的基础设施变化。一种技术(或马)被广泛使用,与之相适应的公共基础设施也随之建立起来。然而,一个几乎相反的问题存在于许多现代自动化技术中。随着各公司试图将无人驾驶汽车、飞行出租车、货运无人机和送货机器人引入公共领域,它们不断努力适应我们已经创造的不一致的世界。所以问题来了,我们应该修改我们自己的世界以更好地适应未来的自主技术吗?
目前的自动驾驶汽车依赖于多种类型的传感器来检测它们的周围环境。除了 GPS,自动驾驶汽车还集成了激光雷达和雷达传感器,以帮助导航和解释他们的环境。特斯拉汽车使用彩色视觉摄像头进行导航。虽然所有这些传感器都有其物理限制,但它们的好坏取决于解释它们的算法。当出现弯道情况时,许多车辆依靠学习算法来更好地改善驾驶。这种方法表明,首先需要犯错误。像 Waymo、这样的公司让他们的车辆经历古怪的情况来提高性能。也许有尚未开发的技术可以提高这些机器人的鲁棒性。
特别是随着 5G 网络技术的发展,已经有了利用网络通信辅助导航的考虑。构成现代道路的许多特征——交通灯、停车标志、交通锥标、道路标线——都是为了方便司机而设计的,这些司机需要对如何在道路上行驶进行视觉排队。可以通过无线方式通信的机器不一定需要这样的队列。知道了自己在城市中的位置,汽车就有可能在附近利用无线技术监听交通灯和停车标志信号。
这种实现在十字路口或需要进行重要操作时肯定会很有用。微操作汽车也有可能应用。照亮现代道路的路灯已经是一种能源。给一排路灯配备射频通信系统可以用来定位路上的汽车并确定其速度(抱歉,超速行驶会更困难)。这将让汽车知道它是否偏离了车道中心或以不适当的速度行驶。更实际的是,如果前方有意想不到的路障,这些系统可以迅速通知汽车。
彩色视觉系统不应该被完全抛弃。随着家用机器人技术的出现,视觉系统可能是给机器人快速引导的便利方法。类似于人类阅读文字以确定指令的能力,给世界贴上二维码可能是告知自动驾驶汽车、制造机器人和无人机如何行动的一种简单方式。二维码有被标准化的好处。机器人不需要理解特定国家的书面语言,这已经是一项艰巨的任务。这种系统也是节能的。与其重新分配电力以适应更智能的道路,不如简单地改变交通标志的内容。
Boston Dynamics’ new ostriches use QR codes!
尽管这些对未来的乐观看法令人愉快,但暗示它们会很便宜是愚蠢的。这样的努力将和目前的自动驾驶汽车本身一样雄心勃勃。需要建立一个能够与现代道路规则相结合的电子标准。手动驾驶和自动驾驶世界之间的过渡必须平稳,并且向后兼容。
随着美国开始承认基础设施大修的必要性,对未来技术的考虑是一个值得讨论的问题。实施智能道路不一定要立竿见影。已经采用自主技术的城市,如旧金山、凤凰城和匹兹堡,可以优先考虑逐步过渡。也可以采取相反的方法。严重依赖汽车的美国被忽视的郊区可以成为这类项目的一个有用的试验台。无论如何,基础设施官员与主要技术公司和专家的互动可以对未来几十年社会和自主技术的继续发展产生迷人的影响。
我们应该用英语编码吗
使用语言来减少进入科技行业的障碍
在进入数据科学领域之前,我的背景是一名语言学家。我研究了从马来语到祖鲁语的一切,所以我知道人类语言中存在的丰富多样的语法和句法。
然而,尽管语言流利是我们与生俱来的能力,但对于有抱负的数据科学家来说,最大的障碍之一是获得与计算机对话所需的语言——代码语言。请考虑以下情况:
var languages = ["Amharic", "Catalan", "Finnish",
"German", "Swazi", "Uzbek"];
var text = "";
var i;
for (i = 0; i < languages.length; i++) {
text += languages[i] + "<br>";
}
说英语但没有接受过计算机培训的人不会立即凭直觉知道这段代码在做什么。
语言学家感兴趣的是编程语言如何在一个范围内,从极度僵化和不透明到接近对话。JavaScript(见上图)是形式语言中明显非自然的一端;纯 Python 更偏向另一边:
languages = ["Amharic", "Catalan", "Finnish",
"German", "Swazi", "Uzbek"]
text = ""
for language in languages:
text += language + "\n"
即使是没有什么编程经验的人,至少也可以对这里发生的事情进行有根据的猜测,而不会完全错误。
两个 for 循环产生相同的输出。两种语言都是图灵完备的。但是从纯语言学的角度来看,一个比另一个更容易被初学者理解。
降低计算机编程的门槛

在一个由数据和计算机主宰的世界里,操纵这两者的能力是一项日益重要的技能。有能力的人和没有能力的人之间的知识差距越来越大,因此收入差距也越来越大。
因此,允许非程序员使用计算机科学工具的编程语言很难被高估。以 R 为例。作为一种编程语言,它是个怪人。你可以使用许多不同的策略来达到相同的目的(违反了 Python 的禅,即“应该有一种——最好只有一种——显而易见的方法来做这件事。”).
但这为统计学家、研究科学家和其他需要即时分析数据的人提供了一个巨大的优势:“它允许统计学家在不了解计算系统的血液和内脏的情况下进行非常复杂的分析。”
它也是免费和开源的,这意味着任何有电脑和一些莫邪的人都可以下载它,学习它,并与其可观的用户群联系。对于传统上在技术领域代表性不足的人群来说,这是一个好消息,尤其是女性,对她们来说,R 社区已经证明是性别多样性的 T2 堡垒。这也意味着除了基本的计算机知识之外,你不需要花钱或接受任何专门的教育来开始学习它。

人类大脑可以做计算机做不到的事情——比如识别母羊、母羊、母狮、母鸡和母猪都是雌性动物。我们的认知意味着我们的语言可以少一些字面上的精确,多一些隐喻。(比如说,计算机需要显式的 XML 标签和属性来实现相同的分类。)那么,在某种程度上,每个严肃的程序员都需要学习像正则表达式和正式语法约定这样的东西。
但是为什么以那些开始?进入科技行业已经够难了,尤其是如果你不是富裕的白人男性。谷歌 2019 年多元化报告显示,女性和少数族裔在领导岗位上的比例有所下降,女性从已经很低的 29.4%下降到 25.7%。2018 年新招聘的科技人员中,只有 25.7%不是男性。83%的科技高管是白人。获得计算机科学教育是一个严重的问题,即使对于相对特权的人来说也是如此——由于高需求,大学正在限制计算机科学专业,进一步扩大了不平等差距。
构建抽象的基础

一旦有人掌握了作为计算机编程基础的算法思维模式,他们就能够将它们应用于任何编程语言。那么,为什么不从一门让学习这些概念变得简单自然而不是令人沮丧的语言开始呢?
熨斗学校的创始人强调了这一点,指出重要的不是你学习的第一语言,而是学习如何学习,获得普遍的想法,并学习将它们应用于各种不同的背景。语言几乎是不重要的;甚至可以用积木和现实世界的物体来教编码。
事实上,所有编程语言都是抽象的,通过提供一个或多或少类似于自然人类语言的接口,允许人们间接编写句法和语义上无法理解的机器代码。认为机器学习可能让我们直接用英文编码并不牵强。对于一个初学编程的人来说,编程语言越接近母语越好。任何人最终都会学到难的东西,但为那些刚起步的人创造公平的竞争环境至关重要。通过混合语言提供一座从普通英语到计算机语言的桥梁,这种混合语言共享两者的特征,这似乎是编码作为一种必需的工作技能日益普遍的自然结果。
最初发布于https://www . espritdecorpus . com/posts/should-we-code-in-English/
该不该读数据科学博士?
如何在学术界和商界之间做出选择
如果你正在考虑攻读数据科学的博士学位,你将不得不决定是想利用你的博士学位获得一份数据科学家的工作,还是更深入地参与研究并试图解决真正复杂的理论问题。这可能是一个具有挑战性的选择。

Should you do a PhD in Data Science
PhD 适合你吗?
另一件事是,博士学位是否适合你。我已经在我的另一篇文章中提到过,但是在这里总结一下:
- 确保数据科学是你的激情所在,并且你愿意在它上面花费大量的时间
- 确保你能够资助你的研究
- 保持好奇心,渴望学习新事物
- 坚持不懈,不要轻易放弃
如果你已经决定这是适合你的,但你仍然不知道到底要不要去做,这里有几个问题值得考虑:
- 你想以后留在学术界做研究吗?
- 你想去企业还是初创企业?
- 你想成为一名自由职业者、顾问还是自己创业?
取决于你选择哪一个,你将以不同的心态毕业。
在第一种情况下,你应该尽可能地专注于研究。在第二种情况下,除了研究之外,你应该考虑在学术界之外建立关系网,并开始以普通观众可以接受的形式传播你所做的事情。在第三种情况下,你需要提高你的软技能——沟通、策略、计划,以便有效地利用你的时间。
什么是数据科学博士?
现在谈谈博士——攻读数据科学博士学位可能意味着几件事:
- 经济学院统计学博士
- 应用数学系数学博士
- 在计算机科学系获得计算机科学或机器学习博士学位
选择哪一个答案将取决于你最终想要投入多少编码时间,以及你想要得到多少实用性。
- 选项是给那些想要实用但不怎么编码的人
- 选项是为那些不想一定实用的人准备的
- option 是为那些想要实用并同时编写大量代码的人准备的
这里没有更好或更坏的选择,这完全取决于你博士毕业后想做什么。你是否想象自己每天编码 8-10 个小时?你想认识人并可视化数据吗?你想在商业决策中使用数据科学?回答这些问题让你决定。
这里的关键是选择那些与你最相关的。
读博后有生活
当你完成研究后,你可以期待一个全职和兼职工作的未来,或者为咨询公司做咨询,为客户做数据分析,或者在一家涉及数据分析的公司工作。
对你来说,最好的决定可能是找到一份适合你优势的工作,并用你的博士学位来帮助你实现目标,无论是获得行业内最好的数据科学工作,还是获得更多的研究经验。
祝你好运!
Should you do a PhD in Data Science — watch this video to know more.
如果你想了解更多,请阅读我关于成为数据科学家的其他文章:
让我们保持联系,继续学习数据科学。](https://creative-producer-9423.ck.page/c3b56f080d) ![]()
Data Science Job
最后,如果你想了解成为一名数据科学家意味着什么,那么看看我的书数据科学工作:如何成为一名数据科学家,它将指导你完成这个过程。
你应该用 SHAP 还是 IG 来解释你的预测?
今天一些最准确的预测模型是黑盒模型,这意味着很难真正理解它们是如何工作的。为了解决这个问题,出现了理解特征重要性的技术:对于一个给定的预测,每个输入特征值对该预测有多重要?两个众所周知的技术是沙普利加法解释(SHAP) 和积分梯度(IG) 。事实上,它们各自代表了一种不同的类型的解释算法:基于 Shapley 值的算法(SHAP)和基于梯度的算法(IG)。
这两种算法类型之间有一个基本的区别。这篇文章描述了这种差异。首先,我们需要一些背景知识。下面,我们回顾 Shapley 值、基于 Shapley 值的方法(包括 SHAP)和基于梯度的方法(包括 IG)。最后,我们回到我们的中心问题:什么时候应该使用基于 Shapley 值的算法(比如 SHAP)还是基于梯度的解释算法(比如 IG)?
什么是沙普利价值观?
Shapley 值(Lloyd Shapley 于 1953 年提出)是一种经典的方法,将合作游戏的总收益分配给合作玩家联盟。可以证明,这是唯一一个具有某些理想属性的发行版(在维基百科上有完整的列表)。
在我们的例子中,我们为每个实例的预测制定了一个游戏。我们认为“总收益”是该实例的预测值,而“玩家”是该实例的模型特征。合作博弈是所有模型特征合作形成一个预测值。Shapley 值效率属性表示特征属性的总和应该等于预测值。归因可以是负的或正的,因为特征可以降低或提高预测值。
有一种叫做 Aumann-Shapley 值的变体,将 Shapley 值的定义扩展到有许多(或无限多)玩家的游戏,其中每个玩家只扮演一个次要角色,如果价值函数(包括玩家联盟的收益)是可微分的。
什么是基于 Shapley 值的解释方法?
基于 Shapley 值的解释方法试图通过检查在其他特征存在或不存在的所有可能组合下移除特征的影响来近似给定预测的 Shapley 值。换句话说,该方法查看像 F(x1, <不存在> ,x3,x4,…, <不存在>, …)这样的特征子集上的函数值。如何评价一个缺少一个或多个特征的函数 F 是很微妙的。
例如,(SHapely Additive exPlanations)通过对从训练集中提取的那些特征的样本进行平均,来估计模型在缺少某些特征的输入上的行为。换句话说,F(x1,<缺项>,x3,…,xn)是从数据集中对缺失特征 x2 进行采样时,通过期望预测来估计的。
具体如何选择样本是很重要的(例如背景数据的边际分布对条件分布对聚类中心),但我将在这里跳过细节。
一旦我们为特征的所有子集定义了模型函数(F ),我们就可以应用 Shapley 值算法来计算特征属性。每个要素的 Shapley 值是该要素对其他要素的所有可能子集的贡献。
SHAP 论文中的“核 SHAP”方法通过定义加权最小二乘回归同时计算所有要素的 Shapley 值,该回归的解是所有要素的 Shapley 值。
最重要的一点是,所有这些方法都依赖于特征子集。这使得理论版本在运行时呈指数增长:对于 n 个特征,存在存在和不存在的 2^N 组合。这对大多数 N 来说太昂贵了,所以这些方法是近似的。即使有近似值,内核 SHAP 也可能很慢。此外,我们不知道任何系统的研究有多好的近似。
有专门针对不同模型架构的 SHAP 版本,以提高速度。例如,树 SHAP 通过巧妙地跟踪所有可能子集流入树的每一片叶子的比例来计算所有子集。但是,如果您的模型架构没有类似这样的专门算法,您就不得不依靠内核 SHAP,或者另一种简单的(未优化的)基于 Shapley 值的方法。
基于 Shapley 值的方法是有吸引力的,因为它只需要对模型的黑盒访问(即从输入计算输出),并且模型架构的版本是不可知的。例如,模型函数是离散的还是连续的并不重要。缺点是精确计算子集在特征数量上是指数级的。
什么是基于梯度的解释方法?
基于梯度的解释方法试图通过使用输出相对于输入要素的梯度(即变化)来解释给定的预测。一些方法,如集成梯度(IG),GradCAM 和 SmoothGrad,实际上应用梯度算子。其他方法如深度提升和 LRP 应用“离散梯度”

Figure 1 from the IG paper, showing three paths between a baseline (r1 , r2) and an input (s1, s2). Path P2, used by Integrated Gradients, simultaneously moves all features from off to on. Path P1 moves along the edges, turning features on in sequence. Other paths like P1 along different edges correspond to different sequences. SHAP computes the expected attribution over all such edge paths like P1.
让我描述一下 IG ,它的优点是试图逼近 Aumann-Shapley 值,这是公理化的。IG 通过在特征空间中考虑从手边的输入(例如,来自训练集的图像)到某个基线输入(例如,黑色图像)的直线路径,并且沿着该路径对关于输入特征(例如,图像像素)的预测的梯度进行积分来操作。
本文对 IG 算法的直觉解释如下。当输入沿着基线和当前输入之间的直线路径变化时,预测沿着从不确定性到确定性(最终预测概率)的轨迹移动。在该轨迹上的每个点,可以使用相对于输入要素的梯度将预测概率的变化归因于输入要素。IG 使用路径积分沿轨迹聚集这些梯度。
IG(粗略地)要求预测是输入特征的连续和分段可微函数。(更准确地说,它要求函数处处连续,并且沿每个输入维的偏导数满足勒贝格可积条件,即不连续点集的测度为零。)

请注意,为 IG 选择一个好的基线以进行合理的特征归因是很重要的。例如,如果选择一幅黑色图像作为基线,IG 不会将重要性赋予实际图像中的全黑像素。基线值应该既具有接近零的预测,又忠实地表示完全没有信号。
IG 具有吸引力,因为它广泛适用于所有可微分模型,易于在大多数机器学习框架(例如 TensorFlow、PyTorch、Caffe)中实现,并且在计算上可扩展到具有数百万神经元的大规模深度网络,如 Inception 和 ResNet。
什么时候应该使用基于 Shapley 值的解释方法,而不是基于梯度的解释方法?
最后,回报!我们的建议:如果模型函数是分段可微的,并且你可以访问模型梯度,那么使用 IG。否则,使用基于 Shapley 值的方法。
使用梯度下降训练的任何模型都是可微分的。例如:神经网络、逻辑回归、支持向量机。这些可以用 IG。不可微模型的主要类别是树:提升树、随机森林。它们在叶子上编码离散值。这需要一个基于沙普利值的方法,比如 SHAP 树。
IG 算法比基于原始 Shapley 值的方法(如核 SHAP)更快,因为它只需要计算几个不同输入(通常为 50)的模型输出梯度。相比之下,基于 Shapley 值的方法需要对从特征值的所有可能组合的指数巨子空间中采样的大量输入计算模型输出。计算可微分模型的梯度是有效的,并且在大多数机器学习框架中得到很好的支持。然而,可微模型是 IG 的先决条件。相比之下,基于 Shapley 值的方法没有这样的假设。
看起来离散的几种类型的输入特征(因此可能需要基于 Shapley 值的方法)实际上可以映射到可微分的模型类型(这让我们使用 IG)。让我们看一个例子:文本情感。假设我们希望将情感预测归因于某些输入文本中的单词。起初,这种模型似乎是不可微的,因为输入是离散的(单词的集合)。然而,像深度神经网络这样的可微分模型可以通过首先使用单词嵌入将单词映射到高维连续空间来处理单词。模型的预测是这些嵌入的可微分函数。这使得它易于 IG。具体来说,我们将预测得分归因于嵌入向量。由于属性是相加的,我们沿着每个嵌入向量的域对属性求和(保留符号),并将其映射到嵌入所对应的特定输入单词。
IG 的一个关键问题是:基线预测是什么?对于这个文本示例,一种选择是使用对应于空文本的嵌入向量。一些模型通过用特殊的“无单词”标记填充短句来接受固定长度的输入。在这种情况下,我们可以把基线看作是只有“无字”标记的句子的嵌入。
结论
在许多情况下(具有梯度的可微分模型),您可以使用集成梯度(IG)来获得对预测的特征重要性的更确定且可能更快的解释。但是,其他(不可微)模型类型需要基于 Shapley 值的方法。
在 Fiddler,我们支持 SHAP 和 IG。(充分披露:Ankur Taly,IG 的合著者,在 Fiddler 工作,是本帖的合著者。)欢迎发送电子邮件 info@fiddler.ai 了解更多信息,或者只是打个招呼!
原载于 2019 年 8 月 13 日https://blog . fiddler . ai。
你应该坐飞机还是开车?

Photo by Ross Parmly on Unsplash
一想到飞机失事我就毛骨悚然,因为我需要定期飞回家看望家人。最近,一家埃塞俄比亚航空公司发生了一起悲惨的事故,所有乘客遇难。如果你对飞机失事的细节感兴趣,你可以在这里找到:
在一次事故现场发现新证据后,波音公司停飞了其全球 737 Max 飞机机队
www.bbc.com](https://www.bbc.com/news/world-africa-47553174)
如果发生了这样的事故,显然很难有人能幸免于难。我想提前警告你,这篇文章可能包含一些关于飞行的惊人数据,所以如果你容易焦虑,我建议不要阅读整篇文章。但是,也有一些关于飞行的积极的事情可以缓解紧张。
所以,最近,我从克罗地亚飞往德国。出发时天气平静晴朗,当然也伴有一些讨厌的风。在登机前,我已经开始思考埃塞俄比亚航空公司的坠机事件,这真的不是登上国际航班时的心态。我具体思考的是关于飞机死亡率的贝叶斯定理,换句话说,你死于登机还是开车的概率是多少?

Photo by Paweł Puszkarski on Unsplash
我想到这一点的原因是一个经典的假设,即从统计数据来看,坐飞机比开车更安全。让我告诉你,它肯定不喜欢它。你从飞机的窗户向外看,你会看到那些翅膀基本上在拍打,看起来一点也不结实——当然,一些机械工程师考虑到翅膀上的力,这是经过深思熟虑的,它们的灵活性使它们更结实,更不容易折断。因此,看起来灵活而不结实的翅膀是个好东西。我不需要陈述显而易见的事实,但我还是要陈述,我不是机械工程师。但我具备了使用谷歌的能力,而且幸运的是,我在大学里学过一些物理学。但是,尽管如此,我还是无法摆脱大脑中的偏见——健壮的东西不应该那样移动!
乘车相对来说比较平稳,大部分时间都没什么事情。另一方面,乘坐飞机有一种叫做湍流的乐趣。如果你经常坐飞机,你可能在生活中的某个时候经历过颠簸,那么你就会知道这不是一种真正的好感觉。
拯救统计数据

我仍然安全地躺在地上,想着贝叶斯统计中癌症测试的经典例子。我不会在这里详细解释贝叶斯统计,但简而言之,它处理的是条件概率,即在其他事情已经发生的情况下,某件事情发生的概率是多少。癌症测试示例之所以如此有趣,是因为基于测试的置信度,您患癌症的概率会显著下降,您可以在此处找到关于癌症示例的贝叶斯统计的更详细解释:
贝叶斯定理是一篇详细文章的主题。这篇文章很好,但是超过 15,000 字——这是…
betterexplained.com](https://betterexplained.com/articles/an-intuitive-and-short-explanation-of-bayes-theorem/)
我具体感兴趣的是我死于坐飞机的概率和死于开车的概率(请原谅我听起来有点可怕):

幸运的是,当我经历人生中最大的动荡之一时,我正在思考这个问题,感谢达尔马提亚的 Bora(亚得里亚海上的强劲北风)。为了解决这个问题,我查阅了一些关于飞机和汽车碰撞和死亡的统计数据,我不允许使用图表,但我会通过链接分享相关的统计数据。
为了让它变得简单一点,因为这是一个快速的思维练习,而不是对飞行和驾驶的完整风险评估,我决定只看 2018 年的统计数据。
所以,首先是 2018 年发生的全球飞机死亡人数,统计数据如下:
该图描绘了从 2006 年到 2018 年的全球空中交通死亡人数。2018 年,561 人死于空难…
www.statista.com](https://www.statista.com/statistics/263443/worldwide-air-traffic-fatalities/)
由于我们着眼于 2018 年,死亡人数为 561 人。从积极的方面来说,我希望你记住以下几点:
死亡率,即死亡人数除以飞行次数,正在下降
我怎么知道这个?这是每年飞行次数的统计数据:
该统计数据给出了从 2004 年到 2019 年航空业在全球范围内执行的航班数量。乘客…
www.statista.com](https://www.statista.com/statistics/564769/airline-industry-number-of-flights/)
请注意,一年中的航班数量在稳步增长。与之前的死亡人数图表相比,我们不能说每年的死亡人数在稳步下降。但是,由于每年的飞行次数在增加,死亡率实际上在下降。死亡人数的波动性是可以理解的,因为飞机坠毁是一个罕见的事件。
更积极的一点是:
并非所有的飞机失事都会导致死亡
2018 年,我们发现航班数量约为 3980 万次。我认为一个安全的假设是平均航班载有 300 名乘客(这当然不准确,但使事情更容易计算,可能与事实相差不远)。所有这些航班的乘客人数如下:

那么,你乘坐 1 次航班成为伤亡人员的概率有多大?嗯,粗略估计是这样的:

很小不是吗?不要抱太大希望。让我们根据以下来源来看一下乘车次数:
[## 道路安全事实-安全国际道路旅行协会
查看世卫组织关于道路安全事实的信息图表。平均每年有近 125 万人死于车祸…
www.asirt.org](https://www.asirt.org/safe-travel/road-safety-facts/)
每年大约有 125 万人死于车祸,这是一笔不小的数目……另一项统计显示,当今世界上有 12 亿辆汽车行驶在路上……12 亿辆!另一方面,想象一下由此造成的污染。这本身就值得进行统计分析。同样,为了简化数学,让我们假设每节车厢平均有 2 名乘客。此外,让我们假设平均来说,一辆汽车每天使用两次,因为大多数人都用它上下班。这是一个很强的假设,可能是错误的,我会假设平均值低于 2,因为许多人只有在长途旅行时才开车。不管怎样,我们要找的数字是一年内参加汽车旅行的人数:

现在我们可以推导出你在一次汽车事故中伤亡的概率:

这与航班相比如何?嗯,你可以注意到它有点低,你死于车祸的可能性大约是死于飞机失事的 16 倍。当你只听到这个数字而没有其他任何东西时,16 次听起来相当可怕。根据乘客的正确期望值,该比率可能会有很大变化。如果预期的乘客数量高于 2,那么该比率也变得更低。具体而言,如果预期乘客数量为 4,则该比率变为 8。但是不管这个比例,你应该看到的是和的概率都很低,没有理由害怕飞机或汽车。
当然,你使用汽车越多,发生悲惨事故的可能性就越大。不用说,这种可能性还取决于其他因素,如你开车的地点、你的驾驶经验、旅程的长度和汽车本身的质量。然而,让我们陈述另一个有趣的问题。根据概率,你需要坐多少次汽车或飞机才能使你死亡的概率收敛到 1?我们可以把这看作是幸存的概率收敛到 0。这种情况只会在无数次尝试后才会发生,但是让我们考虑一下这种情况,根据统计数据,你存活的概率是 1%,这是非常渺茫的存活机会。

所以,你需要做 960 万次汽车旅行才能让你的生存几率降到 1%。对于飞机来说,这种计算看起来如何?重复同样的过程,你会得出这样的结论:

你需要乘飞机旅行超过 9300 万次,才能让你的存活率下降到 1%。事实上,存活率是 N 的函数(基于上面的统计):

存活概率下降需要相当多的尝试,事实上,对于 N=1000 000,存活概率仍然超过 95%!这是一种安慰。你知道你什么时候会做一百万次飞机旅行吗?从不。如果您每天乘坐 1 次航班,您将会:

所以,如果你活到了 100 岁以上,你将需要大约 27 辈子的时间才能使你有 95%的生存概率。这很让人放心,我现在对飞机的感觉好多了…
只是不要太早乐观,我总是提醒自己墨菲定律:
任何可能发生的事情都会发生
显然,飞机和汽车相撞有时会发生,但我们可以肯定它们是极不可能的。如果你想知道更多关于极不可能事件的影响,我建议你读一读纳西姆·塔勒布的《黑天鹅》,这是一本非常有趣的书:
[## 《黑天鹅》:第二版:《极不可能的事情的影响》;增加了一个新的章节:“关于…
黑天鹅是一个事件,积极的或消极的,被认为是不可能的,但造成巨大的后果。在这个…
amzn.to](https://amzn.to/2YatE9c)
总而言之,基于我在这篇文章中写下的这些非常规定的统计数据,你应该对使用汽车或飞机旅行感到安全。但别忘了,这全是随机和运气。
使用 Kaggle 内核展示您的数据科学技能

数据科学是每个大学毕业生都想进入的少数领域之一。这主要是由这个领域过去受到的大肆宣传推动的——从 HBR 的“数据科学家:21 世纪最性感的工作”文章到最近的开发人员调查,这些调查将数据科学家列为薪酬最高的角色之一。借助这种宣传,培训机构、教育机构和训练营开始引入商业分析/机器学习/数据科学/人工智能课程,许多在职专业人士和应届毕业生都参加了这些课程,希望这些课程能让他们在数据科学领域找到工作。但是几乎没有!
投资组合
因为,任何一个理智的招聘人员几乎都不会在意自己所学课程的名称,实际上重点是“作品集”——你所做/制作的展示你对数据/数字热情的东西。它可以是任何东西,从公共数据的可视化到你的项目的 Github 代码和一个阅读良好的文档。
“如果你不生产,你就不会繁荣——不管你有多熟练或多有才华。”—加州新港
框架
在我们深入探讨之前,我想先为一个引人注目的投资组合设定一个框架。您想要建立的投资组合应该能够为您做以下事情:
- 可在谷歌上找到更多信息(搜索)
- 创建品牌
- 允许建立粉丝群/观众群
- 这是你可以向潜在雇主提供的评估你专业技能的东西。
- 最终,在你希望进一步发展的领域获得良好声誉
重点是——ka ggle 内核
在这篇文章中,我试图说明一个人如何用 Kaggle 内核展示他们的数据科学技能——在那里你可以建立你的投资组合——这可以是带有讲故事功能的可视化或最先进的神经网络实现。
Kaggle 神话-竞赛赛道
名字 Kaggle 在数据科学社区引起了高度共鸣,是计算机科学社区中一个有竞争力的机器学习平台——很像 topcoder / hackerrank。正因为如此,许多初学者害怕进入 Kaggle 的世界,天真地认为 Kaggle 是专业人士的天地。他们中的大多数人(在某个时候包括我)忘记的是,那些专业人士(在 Kaggle 排名中是大师或大师)在加入 Kaggle 时也曾是初学者。此外,竞赛不仅仅是 Kaggle 的全部。竞赛只是 Kaggle 上可用的曲目之一。内核是 Kaggle 上另一个非常强大的轨道。Kaggle 内核是非常好的学习资源,也是非常好的分享资源——因此,让它成为你的数据科学投资组合。

Kaggle 内核的优势——作为您的投资组合
- Kaggle 内核由谷歌索引,这意味着,如果有人在这个星球上寻找与你的内核相关的东西,它将作为谷歌搜索结果显示出来

- Kaggle 内核——投票/奖牌贡献内核级数系统和排名。也就是说,只要专注于 Kaggle 内核,你就可以完全远离竞赛,仍然是专家/大师/特级大师

- 奖项/名声/金钱 —无论何时,只要你在拥有全球观众的平台上赢得了什么,你就获得了全球知名度。对 Kaggle 来说也是如此。Kaggle 经常举办基于内核的竞赛,并提供现金奖励或 Kaggle 奖品。最新的是本月的内核作者,并有现金奖励(高达 2000 美元)!

- 观众/粉丝群 — Kaggle 个人资料带有关注者/跟随选项。这意味着,任何对你的工作感兴趣的 Kaggler 也可以在 Kaggle 上关注你。它还可以让你添加其他专业资料,比如 Linkedin、Github,人们可以在 Kaggle 之外进一步关注你。

- Kaggle 知名 — Kaggle 在熟悉数据科学/分析/机器学习的理智的招聘人员中非常知名。这意味着,在你的简历中添加一个足够体面的 Kaggle 个人资料链接将会给你带来很多额外的优势,并且你正在利用 Kaggle 在数据科学市场中为其建立的声誉。
结论
虽然拥有多样化的投资组合是明智的——比如博客帖子、Github 代码、幻灯片演示。对于那些无法管理多元化投资组合的人来说,Kaggle Kernels 平台是一个强大的替代选择。虽然符合我们上面讨论的投资组合框架,Kaggle 内核也可以在数据科学社区中留下您的名字。
因此,你可以简单地用 Kaggle 内核展示你的数据科学技能,这最终会帮助你找到工作。
如果你同意/不同意我的观点,请在评论中告诉我你的想法!
参考
- 卡格尔内核—【https://www.kaggle.com/kernels
- 本月 Kaggle 内核作者——【https://www.kaggle.com/page/kernel-author-of-the-month
- https://www.kaggle.com/sudalairajkumar/ SRK 简介—
- https://towards data science . com/announcing-fast-ai-part-1-now-available-as-ka ggle-kernels-8 ef 4c a3 b 9 ce 6
暹罗梦
用连体神经网络跟踪的初学者指南
你坐在你的沙发上,狗在你的脚边,一只手完全沉浸在一袋白切达奶酪智能食品中。网飞在电视上,你刚刚通过 IMDB 评分 6.4/10 的第 48 分钟,惊悚监视。你看着主角试图躲避警察的监视。他们仔细研究了无休止的闭路电视监控录像,跟踪了对手的每一个动作,却没有触动任何一个按钮。你不禁会想。他们不会真的这么做吧?这个问题的答案是肯定的,他们可以。这个例子描述了在计算机视觉的流行科学学科的研究中的许多可能的应用之一。这是一个不断发展的研究领域,已经出现了大量的用例以及有前途的算法。这种算法包括暹罗神经网络,可以实现这些在电视上看到的结果。

现在,让这成为一个警告,这篇文章不会让你一夜成名,你可能不会像这个网站上的其他文章一样,拿出一个功能齐全的原型。你将会了解这些网络是什么,以及它们如何达到这样的结果。
假定是卷积神经网络及其背后的数学的基本知识。如果您没有,我建议您保存此选项卡,观看此视频,然后再回来。

暹罗概述
让我们考虑我们的最终目标是拍摄一段视频,选择一个目标,并在整个过程中跟踪它。现在让我们从兔子洞往下看,把它抽象出来。首先让我们想一想视频到底是什么。从根本上说,它只是一系列快速连续显示的照片或图像。因此,我们所要做的就是在视频的每一帧中找到一个目标,并在它周围画一个方框。这听起来和每个人最喜欢的儿童读物“沃尔多在哪里”出奇的相似。

用我们的眼睛,也许用我们的手指,从左到右扫描页面,看每一张脸,每一件衬衫或每一个特征,看看它是否与我们的老朋友沃尔多相匹配。甚至可能在他周围画一个圈,只是为了破坏其他可能想尝试的人的游戏。我们想训练一种算法来做到这一点。找到我们的目标,并在它周围画一个方框。

不幸的是,我们知道我们的网络记性不好。我们必须不断提醒它目标图像(瓦尔多)是什么样子的。为此,我们需要两个网络。一个处理我们的沃尔多图像,另一个处理我们要找的他的整个图像。这就是暹罗的用武之地。就像连体双胞胎一样。我们构建两个相同的全卷积网络(我们稍后将讨论全卷积网络)。twins 的工作是处理我们的两个图像(目标和搜索图像)并在两个独立的特征地图中表示不同的特征。它们是相互关联的,因为它们在完全相同的时间执行相同的工作,但是它们的输入是不同的。

它们需要相同的原因是为了以完全相同的方式表示两幅图像共有的特征。这对于比较我们是否拥有真正的瓦尔多至关重要。如果我们比较像他的衬衫这样的特征。我们必须确保我们有共同的属性(大小,颜色,图案)。保持这种状态的唯一方法是通过一个相同的网络来移动它们。
基本的想法是,如果我们可以将目标图像(Waldo)的表示放在搜索图像(场景)的上面。我们可以找到共享最多的特征在哪里,这将是我们的目标图像(Waldo)在图像中的位置。我们在它周围画一个方框,并对所有图像重复,直到视频结束!拍拍自己的背,你刚刚学会了这是如何工作的绝对基础。如果你为了考试或者为了给你的朋友留下深刻印象而学习这个,这可能是一个停止阅读的好地方。如果你正在寻找本质的细节,请继续关注。
为什么这是一种实时跟踪视频的有效方法的关键是我们需要理解的三个概念。这些是迁移学习、完全卷积网络和相关滤波器。我们已经知道什么是卷积神经网络,所以让我们看看什么使一个完全卷积。
全卷积网络
全卷积表示神经网络由卷积层组成,在网络末端没有任何全连接层。

在完全卷积实现中,网络在任何地方都在学习过滤器(特征图)。包括最终决策层。在全连接层中,每个神经元都连接到前一层中的每个神经元。每个连接都有它自己的重量。在卷积层中,每个神经元仅连接到前一层中的几个附近的局部神经元,并且对每个神经元使用相同的一组权重。
我们为什么要这么做?
输入图像大小: 如果你的网络中没有任何完全连通的层,你可以将网络应用于几乎任何大小的图像。因为只有完全连接的层需要特定大小的输入。这就是为什么在像 AlexNet 这样的架构中,你必须提供一定大小的输入图像。由于大多数视频和图像都有不同的大小,这使得我们可以轻松地进行调整,以满足弹性处理所需的规范。在这种情况下,使用全卷积网络会使我们的实现更加鲁棒。
计算成本和速度 :卷积层和全连接层在计算和存储方面也有区别。由于完全连接的性质,它会消耗更多的计算。另一方面,如上所述,全卷积网络具有较少的集中在“本地”的连接。这对于查看图像来说是完美的,其中根据需要,特征是局部的(例如,“Waldo 的红白衬衫”由一组附近的像素组成,而不是遍布整个图像)。连接数越少,全卷积网络的速度越快,由于我们是实时操作,这对于效率至关重要。
迁移学习
当网络不是从零开始学习(调整权重和连接)时,通常使用迁移学习。相反,它使用预先训练的知识,并建立在此基础上。我能想到的一个例子是一名机械师,他可能在大众汽车经销店修理汽车工作了多年。也许他找到了一份修理校车的新工作。他已经知道如何修理汽车,也知道修理汽车的复杂性。虽然他需要专门化他的技能并学习更多的知识来将这些知识转移到一种新型的车辆上。迁移学习在同样的前提下起作用。在我们的例子中,第一对层是经过预训练的,并且承载着一个非常成功的大型网络的重量。这将给出如上所述的图像的一般表示。剩余的层在大型数据集上进行训练,例如具有预定义边界框和目标的戈特-10K 。由网络误差表示的功能损失(预测位置与地面真实值的比较)
Why do we want to do this? -It speeds up network and training!
相关过滤器
好了,我们现在真的开始把这些碎片拼在一起了。让我们回想一下沃尔多在哪里的类比。我们目前有一个我们在 Waldo 寻找的产品展示(眼镜、衬衫、帽子等。).我们所要做的就是将它与我们的搜索图像特征进行比较,看看我们是否能够将它们匹配起来!如果你做了功课,你应该记得卷积神经网络创建特征图的操作。它如何接受滤波器并执行矩阵数学运算,像滑动窗口一样在整个图像上移动。这产生了具有突出特征的新图像。在寻找我们的目标时,我们几乎要做同样的事情。我们将使用搜索图像的特征图,而不是使用一般的学习过滤器。将它逐块传递到图像上,我们对图像进行卷积。我们接收到的输出代表了逐块的相似性,匹配程度越高的区域得分越高。我们搜索图像中相似度最高的区域就是瓦尔多所在的地方。我们现在要做的就是在他周围画一个方框,然后重复。我知道你在想什么,这听起来像是实时发生的许多事情。谢天谢地,数学拯救了我们。我们能够实时进行这些操作的原因是因为它发生在更广阔的领域。我们真的不需要现在就弄清楚为什么会这样。只要知道它允许你的机器同时计算所有这些滑动窗口的操作,通过你的 GPU 的多个核心并行。这使得我们几乎可以在瞬间观察到结果。

未来
类似于我们的 Waldo 在哪里的例子,我们当前实现的问题是我们背景的相似性和遮挡导致了一个问题。我认为在这个领域继续研究是值得的。应用程序是丰富和非常重要的。自动驾驶汽车、机器人、智能跟踪,甚至基于视频跟踪的习得行为,都是特定研究的潜在领域。我强烈推荐 Luca Bertinetto 等人的论文 siamFC:提供了对该领域的一个很好的监督以及一个预训练演示。它是在 Tensorflow 中实现的,允许你在自己的视频上尝试。Github 回购的链接就在这里!如需进一步阅读和更复杂的方法,请查看 siamRPN 和 siamMask 。
最初发表于【http://github.com】。
SiATL 是最新最简单的迁移学习方法

Photo by Allure Graphic Design
这份研究总结只是人工智能学者时事通讯上每周发布的许多研究总结之一。要开始接收每周简讯,请在此注册。
许多传统的迁移学习方法利用已经变得非常流行的预训练语言模型(LMs ),并且具有翻译上下文信息的能力,高级建模语法和语义语言特征在许多任务中产生高端结果,例如对象识别、机器翻译、文本分类等等。
然而,现有的 LMs 面临着缺点,包括高计算成本和对特定任务架构的需求。此外,大多数需要预先培训和微调手头的任务。现在情况不同了,因为研究人员最近发布了一种新的单步迁移学习方法,不需要预先训练或微调。此外,新方法在所有任务中都优于包括 ULMFiT 在内的最先进的迁移学习方法。
单步辅助损失转移学习(SiATL)
SiATL 是一种简单而有效的迁移学习方法,可以解决灾难性遗忘的问题。SiATL 将特定于任务的函数与在训练过程中调整的辅助 LM 损失相结合,并且基于预先训练 LM 并将其权重重新分配给分类器。这使它能够保持语言模型捕捉到的语言规则,同时促进解决任务的充分适应。
正如在简介中提到的,SiATL 不需要预先培训或微调,这使得它的使用非常简单。新模型已经在各种具有挑战性的文本分类任务上进行了测试,并产生了具有竞争力的结果,表明其优于传统的迁移学习方法。

潜在用途和效果
你已经知道了,模特训练不一定要从零开始,也不一定要野。SiATL 可用于驾驭为一项任务训练的模型,并以直接的方式将其应用于另一个领域。在数据不足的情况下,它也能派上用场。
对于数据科学家和开发人员来说,SiATL 提供了一种简单、廉价和实用的方法,通过迁移学习功能来加速模型训练,以增强各种应用的性能,如语音识别、智能视频分析、问答系统、医疗成像等。
感谢阅读。请评论,分享,别忘了订阅!另外,在推特和 T2【LinkedIn】上关注我。干杯!
强化学习中的从零到 SOTA
旁注:为初学者和专业人士发布 4 小时强化学习课程
除了我们的连载博客,****&杯到觉悟’之外,我们还会偶尔发布一些&材料作为单机版或者短期系列。说到题外话,与我们的其他出版物相比,这些材料的性质有些模糊,我们选择称之为“旁注”。
****旁注就这样。与 MTank & AI 相关的附属作品,我们认为我们的读者可能会喜欢。格式会有所不同,但可能包含所有内容。把它想成是对额外项目、会谈&想法的总括。
也就是说,在过去一年左右的时间里,我们花了相当多的时间阅读、回归和提炼我们最喜欢的领域:强化学习(RL)** 。对于那些对作为人工智能分支的 RL 感兴趣的人来说,我们正在开源我们去年建立的 RL 课程,作为对工程师&研究人员的介绍。**
什么是强化学习?
强化学习是近年来最有前途的技术之一;使视频和棋盘游戏取得巨大进步,并提供解决一般顺序决策问题的框架,为更智能的机器铺平道路。我们已经对这个领域着迷了一段时间,并慢慢开始研究网上的核心材料和论文。
虽然我们觉得有很多可用的资料,但我个人推荐理查德·萨顿的书(链接)和大卫·西尔弗的 UCL RL 课程(链接),一般来说,这些资料都是零散的,或者过于入门,或者过于高深。最终,尽管看了看,我们没有找到适合对这个领域感兴趣的人的好的 10 小时以下的入门读物。所以我们造了一个。
我们做的东西
我们创建了两部分的强化学习课程:强化学习入门和深度强化学习入门。第一个处理 RL 的一些理论基础――政策、奖励、等式,所有这些好东西。后者迅速将读者带入一些最先进的(SOTA)方法,这些方法在人工智能公司用他们的智能 RL 代理摧毁人类游戏玩家时不断出现在媒体上。

Tile-based view of the first 20 slides out of 125. There are too many GIFs inside so initial loading of the slides can take some time. This is a price worth paying for awesome GIFs.
课程在哪里出现/我们为谁制作
该课程最初是为 Fernando 于 2018 年在博世内部举办的 4 小时 RL 研讨会组装的。我们也计划在今年夏天发布一个更新版本。此外,他还在 2 月份的 StuttgartAI meetup 上展示了该课程的节略版。
Ben 在开放数据科学大会(ODSC)上做了一个小时的演讲 [ 链接到站点】涵盖了第一部分和第二部分的一些内容。演讲的视频可以从他们的网站获得:https://learnai . odsc . com/courses/introduction-to-reinforcement-learning及以下网址:
这个课程是为完全初学 RL 的人、和想“赶上”RL 和子领域的一些最新技术的人而设计的。我们之所以这样做,是因为我们认为在 RL 上缺乏全面、简洁的材料资源,这些资源优先考虑真实世界的研究实例以及现代方法。
坦率地说,我们是为自己建造的。但是在这样做的时候,我们创造了一些对其他人也有价值的东西。这些幻灯片尚未定稿,将随着时间的推移和技术的变化不断更新。但希望其他人也能从这项工作中获得价值。
故障一目了然

****第一部分涵盖了 RL 的基础知识、核心概念和基本原理。RL 背后的主要应用、灵感和愿景。冰山的其他部分包括“信用分配”问题,勘探与开发。在算法方面,我们涵盖了:蒙特卡罗与时间差异,加上动态规划(策略和值迭代)。
一旦读者理解了第一部分,第二部分在概念上应该相当简单,因为我们只是建立在第一部分的主要概念上。正如下面可以看到的,我们在 RL 中添加了大量使用深度学习的最新方法。这些方法可以大致分为政策梯度方法和基于深度 Q 学习的方法。学习愉快!

收尾
我们希望我们的一些读者,或者未来的读者,会发现这个关于强化学习的闪电战课程很有价值,点击链接。如果你知道,或者知道谁知道,那就把它传下去。此外,我们计划不断改变,完善和更新这些材料。因此,不要羞于发表评论,或者给info@themtank.com发电子邮件,帮助我们做得更好!
如果您有任何问题,需要参考,或希望我们澄清。您知道在哪里可以找到我们,随时乐意为您提供帮助!

We promised many GIFs
尺度不变特征变换
在本文中,我将详细解释 SIFT 算法及其数学原理。
我希望你读完之后会明白这些🤔:
- 筛选的主要步骤
- 为什么我们需要将对数乘以σ来获得比例不变性
- 使用 DoG 接近日志
- 为什么我们使用 Hessian 来拒绝一些位于边缘的特征。
SIFT 是由大卫·g·洛在他的 T2 论文中提出的。(这篇论文很好理解,推荐大家看一看)。
一般来说,SIFT 算法可以分解为四个步骤:
- 特征点(也称为关键点)检测
- 特征点定位
- 方向分配
- 特征描述符生成。
而本文也将遵循这些步骤。
特征点检测
顾名思义,SIFT 具有 尺度不变性 的性质,这使得它比 Harris 更好。Harris 不是比例不变的,如果比例改变,一个 角 可能会变成一个 边 ,如下图所示。

Harris is not scale-invariant
那么什么是尺度,尺度不变性是什么意思?托尼·林德伯格的论文给出了一个很好的解释:
世界上物体的一个固有属性是,它们只在一定的尺度范围内作为有意义的实体存在。一个简单的例子是树枝的概念,它只有在几厘米到几米的范围内才有意义。在纳米或千米级别讨论树的概念是没有意义的。在这些尺度上,谈论形成树叶的分子,或者树木生长的森林,更有意义。同样,只有在一定范围的粗尺度上谈论一朵云才有意义。在更精细的尺度上,更适合考虑单个的液滴,而液滴又由水分子组成,水分子由原子组成,原子由质子和电子等组成。
图像标志的比例是其在图像中的(粗略)直径。它用σ表示,σ以像素为单位,你可以认为比例不变性是指我们可以检测相似的地标,即使它们的比例不同。

那么 SIFT 是如何实现尺度不变性的呢?
你还记得 金字塔 吗?

我们可以找到不同图像尺寸下的特征。
除此之外,我们还可以使用不同σ的高斯拉普拉斯(LoG)来实现这一点。
让我们先来看看日志。
正如李因的文章所指出的,日志操作是这样的。你拿一张图片,稍微模糊一下(使用高斯核)。然后,你计算它的二阶导数的和(或者,拉普拉斯算子)。这将定位图像的边缘和拐角。这些边缘和拐角有利于找到关键点(注意,我们想要一个关键点的检测器,这意味着我们将做一些额外的操作来抑制边缘)。LoG 常用于 blob 检测 (后面我会解释)。
记住卷积和微分的关系。

我们可以只将图像与 高斯的二阶导数进行卷积,并对它们进行求和 (或者只使用 LoG 进行卷积)。

举一个一维的例子, f 是图像的一条扫描线(即图像的一行的像素阵列)。


可以看到,如果我们使用 LoG 来检测边沿,我们需要找到过零的 LoG 响应。如上所述,我们可以使用 LoG 来检测 斑点 ,而不是找到零交叉。

啊哈!我们不再需要找到零交叉,我们可以找到极值(最大值和最小值)。
然而,测井曲线并不是真正的尺度不变,可以发现:拉普拉斯响应随尺度增大而衰减:

那么为什么会出现这种情况呢?
我会给你两种方法来解释:

- 高斯滤波器的导数对理想阶跃边沿的响应随着σ的增加而降低。
- 为了保持响应不变(比例不变),必须将高斯导数乘以σ。
- 拉普拉斯是二阶高斯导数,所以必须乘以σ2。
如果你对上面的解释很满意,也厌倦了纠结一些数学概念,可以随意跳过下面的解释。
另一个解释来自本教程 pdf 。
考虑一个无噪声的图像边缘 I(x)= u(x x0),u 是 单位阶跃函数 。为了先前检测边缘,我们将它们与高斯导数卷积,然后在响应中寻找峰值。假设我们通过将 I(x)与一族高斯滤波器的一阶导数进行卷积来定义尺度空间,其中 一族 意味着我们有一族σ。

在边 x = x0 的位置,我们有

这取决于σ。
**
如果我们有一个 2D 图像,那么我们以同样的方式定义归一化导数滤波器,即

对于 y 也是类似的。使用与上面完全相同的参数来表明水平或垂直边缘处的
值将独立于σ。使用这些滤波器,可以以明显的方式定义归一化梯度尺度空间,并且可以发现任意方向的边缘处的梯度将与σ无关。
接下来是二阶导数
我们知道,如果我们对一个(无噪声)边缘进行滤波,I(x)= u(x x0),利用高斯一阶导数,我们可以在边缘位置获得峰值响应。紧接着,如果我们用高斯的二阶导数来过滤边缘

那么在边缘的位置响应将为零。
高斯滤波器的二阶导数及其 2D 等价物在计算机视觉以及人类视觉建模中非常重要,并且是有影响力的早期边缘检测理论的基础。该滤波器对边缘图像的响应为:

请注意,当 x = x0 时,响应确实为 0,正如我们所预期的那样。峰值出现在哪里?取我们得到的导数

并将其设置为 0。因此,峰值出现在以下情况

即 x=x0 σ。代入,我们看到峰值的值为

正如我们对一阶导数滤波器所做的那样,我们可以通过乘以σ2 来归一化二阶导数滤波器,这样就定义了归一化二阶导数滤波器。

这消除了峰值高度的σ^(−2 依赖性。因此,我们看到,如果用高斯函数的归一化二阶导数(如上所述)对边缘进行滤波,那么在边缘位置会有一个过零事件,在距离边缘σ处会有峰值(正的和负的),但峰值的高度不取决于σ。
综上所述,我们需要 将 LoG 乘以σ2 才能得到真正的尺度不变性。以下是结果:

现在,我们可以在适当的比例下检测地标,这非常重要,因为我们需要在后面的步骤中描述该区域,并且我们需要基于该比例选择其周围区域,如下所示:

实际上,拉普拉斯算子是使用高斯差分(DoG)来近似的。

DoG 和 σ2LoG 之间的关系可以从热扩散方程中理解(用σ参数化,而不是更常见的 t = σ2,更多细节可以参见第 11 页:

由此我们可以看出,利用 kσ和σ处附近尺度的差异,LoG 可以从∂G/∂σ的有限差分近似计算出来:

因此:

这表明当 DoG 函数具有相差一个常数因子的尺度时,它已经结合了尺度不变拉普拉斯算子所需的σ2 尺度归一化。等式中的系数(k1)在所有范围内都是常数,因此不会影响极值位置。
首先,SIFT 结合了金字塔和不同的σ空间来检测不同尺度下的斑点。
**
注意,极值是三维(即 x,y,σ)周围的最大值或最小值。
特征点定位(亚像素定位)
这部分主要来自 N 坎贝尔的文章。
在步骤 1 之后,我们检测一些关键点,这些关键点被粗略地定位,最好定位到最近的像素,这取决于特征在尺度空间中的位置。由于σ在尺度空间中被量化为相对较少的步骤,因此它们在尺度上的局部性也很差。SIFT 算法的第二阶段将这些特征点的位置精确到亚像素精度,同时去除任何不良特征。亚像素定位通过拟合泰勒展开来将 3D 二次表面(在 x、y 和σ上)拟合到局部区域以内插最大值或最小值来进行。忽略二次项以上的项,DoG 的展开式如下所示,其中导数在建议点 z0=[x0,y0,σ0]T 处计算,z=[δx,δy,δσ]T 是从该点的偏移。

然后,通过将 z 的导数设置为零来确定极值 z’的位置:

可以使用来自狗的相邻样本点的标准差分近似值来估计参数,从而得到可以有效求解的 3 × 3 线性系统。该过程可能需要迭代地执行,因为如果任何计算的偏移值移动超过半个像素,则有必要重新评估 z^,因为近似的适当邻域将会改变。不能快速收敛的点被认为不稳定而被丢弃。
可以对局部极值处的值进行插值,

并且任何具有低于某个阈值的值的点被拒绝作为低对比度点。
执行最终测试以移除位于图像边缘的任何特征,因为如果用于匹配目的,这些特征将遭受模糊。位于狗的脊上的峰(对应于图像中的边缘)将具有穿过脊的大的主曲率和沿着脊的低主曲率,而 轮廓分明的峰(斑点)将在两个方向上具有大的主曲率 。黑森中的 H 和 y 中的

对于特征点,再次使用局部差分近似,以及对应于主曲率的特征值λ1 和λ2 的比率,与阈值比率 r 进行比较,如

并且拒绝高比率点。
这个有点类似于 Harris,关心的是所选点周围区域的导数分布。(你可以从这个 pdf 中获得更多信息)

然而,海森 H 的特征值对应的是主曲率。那怎么做呢?
这个 pdf 给出了很好的解释。
让我们假设我们在 R 中有一个曲面 M,它由光滑函数 z = f(x,y)的图形给出。假设 M 穿过原点 p,并且它在那里的切面是{z = 0}平面(对于检测到的斑点几乎是正确的,想想向日葵)。设 N = (0,0,1),在 p 处垂直于 M 的单位。

设 v 是 TpM 中的单位向量,说 v = (v1,v2,0)。设 c 是通过在 v 和 N 所跨越的平面上切割 M 而得到的参数化曲线:

那就是:

现在我们可以计算 c 沿 v = (v1,v2,0)方向的曲率。κv 是 p 处密切圆半径到 c 的倒数。

κv = c ′′( t)| t = 0
所以,
**
曲面在 p 处的主曲率将是κv 的最大和最小可能值λ1、λ2(因为 v 在可能的单位切向量上的范围)。如果你不明白这个论点,可以看看关于 PCA 的优秀博客。
方向分配
当然,我们将使用梯度方向直方图(HOG)

从关键点周围区域内的采样点的梯度方向形成方向直方图。方向直方图有 36 个仓,覆盖 360 度方向范围。添加到直方图的每个样本通过其梯度幅度和高斯加权圆形窗口进行加权,该圆形窗口的σ是关键点尺度的 1.5 倍。
特征描述符生成
SIFT 算法的最后阶段是生成由归一化的 128 维向量组成的描述符。在算法的这个阶段,我们得到了一个特征点列表,这些特征点是按照位置、比例和方向来描述的。这允许我们围绕特征点构建局部坐标系,该坐标系在同一特征的不同视图中应该是相似的。
描述符本身是由灰度图像的梯度形成的直方图。使用梯度角度直方图的 4×4 空间网格。网格的尺寸取决于特征点的比例,并且网格以特征点为中心并旋转到为关键点确定的方向。每个空间仓包含分成 8 个部分的角度直方图。(128=4×4×8).图像梯度幅度和角度再次从尺度空间产生。



然后,将每个像素处的梯度角度添加到网格的适当空间仓中的相应角度仓。每个像素的权重由梯度的大小以及以特征点为中心的比例相关高斯(σ等于描述符窗口宽度的一半)给出,如蓝色圆圈所示。该高斯窗口的目的是避免窗口位置发生微小变化时描述符的突然变化,并减少对远离描述符中心的梯度的强调,因为这些梯度受重合失调误差的影响最大。
在直方图形成期间,使用三线性插值来添加每个值,即 x、y 和θ的插值。这包括基于到面元中心的距离对相邻空间面元上的像素权重进行插值,以及对相邻角度面元进行插值。下图展示了插值的效果。

它用于避免当样本从一个直方图平滑地移动到另一个直方图或从一个方向移动到另一个方向时描述符突然改变的所有边界效应。因此,使用三线性插值将每个梯度样本的值分布到相邻的直方图仓中。换句话说,每个面元的每个条目都乘以 1d 的权重,其中 d 是样本与面元中心值的距离,以直方图面元间距为单位进行测量。
最后,修改特征向量以减少光照变化的影响。首先,向量被标准化为单位长度。图像对比度的变化(其中每个像素值乘以一个常数)将梯度乘以相同的常数,因此这种对比度变化将被向量归一化抵消。向每个图像像素添加常数的亮度变化不会影响梯度值,因为它们是根据像素差异计算的。因此,该描述符对于光照的仿射变化是不变的。然而,由于相机饱和或者由于以不同的量影响具有不同方向的 3D 表面的照明变化,也可能发生非线性照明变化。这些效应会导致某些梯度的相对幅度发生较大变化,但不太可能影响梯度方向。因此,我们通过将单位特征向量中的值阈值化为每个不大于 0.2,然后再归一化为单位长度,来减少大梯度幅度的影响。这意味着匹配大梯度的幅度不再那么重要,方向的分布更加重要。0.2 的值是使用包含相同 3D 对象的不同照明的图像通过实验确定的。
就这些,我真的希望这篇文章能帮助你理解 SIFT 算法。因为我还是一个计算机视觉的学习者,如果你有什么问题或者建议,随时联系我。如果你能指出我的错误,我会非常高兴。🤪
Sigmoid 激活和二元交叉熵——不太完美的匹配?
调查对数值不精确的关注

Really cross, and full of entropy…
在以二进制分类、最后(输出)层的 sigmoid 激活和作为损失函数的二进制交叉熵(BCE) 为任务的神经元网络中,这是标准配置。然而,偶尔有人会无意中发现这样的说法,即最后一层激活和损失的这种特定组合可能会导致数值不精确甚至不稳定 。我想确保我得到了论点,精确到数字,特别是在我目前使用的框架中。听起来很有趣?意识到这可能也与一些图像分割任务相关,或者多类、多标记问题,而不仅仅是猫和狗的问题?那么,请跟我来。如果你喜欢提前复习一下 BCE,我想指出丹尼尔·戈多伊对 BCE 的精彩而深入的解释。
1.问题的定义
让我们从剖析 Keras 对 BCE 的实现开始:
因此,输入参数output首先被裁剪,然后被转换成 logits,然后被送入 TensorFlow 函数tf.nn.sigmoid_cross_entropy_with_logits。好吧…什么是逻辑来着?在数学中,logit 函数是 sigmoid 函数的逆,所以理论上 logit(sigmoid(x)) = x。

Figure 1: Curves you’ve likely seen before
在深度学习中,logits 通常和不幸的是意味着分类网络最后一层的‘原始’输出,即在之前的层的输出,它被传递到激活/标准化函数,例如 sigmoid。原始输出可以取任何值。这是 Keras 的binary_crossentropy的核心sigmoid_cross_entropy_with_logits所期待的。相比之下,在 Keras 中,期望变量output中的值代表概率,因此受限于[0 1]——这就是为什么from_logits默认设置为 False。因此,在输入sigmoid_cross_entropy_with_logits之前,它们需要被转换回原始值。重复一遍,我们首先通过一个函数(sigmoid)运行数字,然后使用反函数(logit)** 将它们转换回来。这似乎有些迂回。然而,真正的潜在问题是这种来回可能导致的数值不稳定,在极端情况下会导致溢出。看看当 x 的类型是 float32 时 y = logit(sigmoid(x))的输出,这是 Keras 中的默认值,据我所知,在大多数其他框架中也是如此:**

Figure 2: Numerical imprecisions with float32
从大约 x=14.6 开始,误差达到 1%的范围,在大约 x=16.6 以上,由于被零除,游戏结束。当 sigmoid 中的分母恰好等于 1,而 logit 中的分母等于 0 时,就会被零除。我们可以通过np.log(np.finfo(np.float32).eps)估计 x 的极限,但是我发现数字的曲折变化很有趣,值得展示。不管怎样,这就是为什么 Keras 函数中的输入变量output的值需要并且实际上被剪裁了。所以,从一开始就打消一个想法:
Keras 的 binary_crossentropy,当输入由 sigmoid 激活产生时,不会产生数字上溢或下溢。
然而,剪切的结果是损失函数在边界处变平。为了形象化,让我们
- 复制设置最小网络的巧妙技巧,手动设置权重,以及
- 通过网络运行单一输入(样品,批量为 1)。
因此,我们能够比较从 Keras 中的 sigmoid 激活计算的 BCE 值和从 TensorFlow 中的原始输出计算的值。这是第一个模型,使用了 sigmoid 激活和 Keras 的标准 BCE:
没有 sigmoid 激活的模型,使用定制的损失函数,将值直接插入sigmoid_cross_entropy_with_logits:
因此,如果我们在标量输入 x 的扫描范围内评估模型,将标签(y)设置为 1,我们可以将模型生成的 BCE 相互比较,还可以与通过高精度浮点计算的 BCE 的简单实现产生的值进行比较。请再次注意,我们在这里是从单个样本计算 BCE,以便提取两种方法之间的差异。

Figure 3: Binary crossentropy computed for single samples in three ways. Note logarithmic y axis in the left plot, and linear y axes in the others.
有意思!使用 TensorFlow 的sigmoid_cross_entropy_with_logits从原始值计算的曲线在测试的 x 值范围内是平滑的,而使用 Keras 的binary_crossentropy从 sigmoid 变换值计算的曲线在两个方向上都变平(如预测的)。在大的正 x 值下,在达到限幅引起的极限之前,sigmoid 导出的曲线显示出阶梯状外观。最值得注意的可能是左边界上发生的情况,这在线性标度上是最好的理解(图 3,中心):原始值越负,由于削波对其 BCE 值的低估越严重。最后,即使使用 float128,如果我们从经过 sigmoid 变换的输入中获得 BCE 值,我们也不会走得太远(最左边的图中的灰色曲线)。
由于损失函数是学习的核心,这意味着采用最后一层 sigmoid + BCE 的模型无法区分预测类别与其标签极度一致或极度不一致的样本。所以,理论上使用 sigmoid + BCE 确实有缺点。但是,在实践中有关系吗?毕竟我们说的是极端的边缘情况。在我们回答这个问题之前,一个更基本的问题是:原始的最后一层输出在实践中达到这样的极值吗?让我们看看。
2.在二元分类网络中检查单个样本的原始输出值
我训练了三个不同的网络来对狗和猫进行分类,使用了 2013 年 Kaggle 竞赛数据集的子集(2000 个训练图像,1000 个用于验证;效仿 F. Chollet ( 深度学习用 Python )。纽约谢尔特岛曼宁出版公司。2017.)).是的,又是猫和狗,为了轻松和专注于手头的问题。
下面的图形和数字来自一个简单的手工制作的 convnet:四对 2D 卷积/最大池层,然后是一个单一的下降和两个密集层,所有这些都具有 relu 激活。最后一个单元素输出层没有激活,作为损失函数,我为 TensorFlow 的sigmoid_cross_entropy_with_logits使用了上面提到的 Keras 包装器。
首先,让我们看看个别图像是否真的会产生输出图层的极端原始值。训练后,我通过网络运行用于训练的相同图像,并从最后一层获得它们的原始输出。此外,我计算了 sigmoid 转换后的输出,以及从两个输出中导出的 BCE 值。这是我在八个纪元的训练后得到的,所以相对来说,我学到的东西很少:

Figure 4: Left and center, distribution of outputs of the last layer after training the network for eight epochs. Input for prediction consisted of the same 2000 images as used for training. Left, raw values; center, sigmoid-transformed values. Right, scatter plot of BCE values computed from sigmoid output vs. those computed from raw output. Batch size = 1.
显然,在训练的初始阶段,我们处于危险区之外;在这个例子中,原始最后层输出值由 ca [-3 8]限制,并且从原始和 sigmoid 输出计算的 BCE 值是相同的。同样令人高兴的是乙状结肠强烈的“挤压”效应(图 4,中间)。
当网络被充分训练(这里定义为在连续 15 个时期没有显示出减少的损失)时,情况是怎样的?

Figure 5: Same depiction as in Fig. 4, but after full training of the network (test accuracy of ca. 0.85). Same conventions as in Fig. 4 apply. Note clipping of BCE values computed from sigmoid outputs (right).
啊哈——正如我们所料,我们看到了更清晰的职业划分。并且少量的图像实际上导致了落入剪裁范围内的极端 logit 值。让我们把重点放在第一类(狗;图中的橙色);另一个类也有类似的论点。没有一个样本产生比 ca 更负的原始输出值。-4,所以这也没问题。然而,一定数量的样本——最小的狗——达到大于大约 16 的原始输出值。因此,通过 sigmoid + Keras 的 binary_crossentropy 计算的相关 BCE 在 ca 处被剪切。10⁻⁷(图 5,右;也参见图 3)。这是一个非常小的值。如果狗和猫的 BCE 值更小(并且个体差异更大),我们会期望学习以一种系统化的不同方式发生吗?特别是如果我们使用合理的批量,具有中等或低原始产值的样品将在损失中占主导地位。图 6 显示了上述相同数据的批次大小为 4 的情况,这仍然是偏低的。

Figure 6: scatter plot of BCE values computed from sigmoid output vs. those computed from raw output of the fully trained network with batch size = 4.
结果与基于 VGG16 和 Inception V3 的网络在 imagenet 数据集上进行预训练(训练时没有对卷积部分进行微调)。
3.结论
首先,让我们重申,由于最后一层中的 sigmoid 激活和作为损失函数的 BCE 的结合,对数字不足或溢出的担心是不合理的——在使用 TensorFlow 后端的 Keras 中。我还不熟悉其他框架,但是如果它们没有类似的预防措施,我会感到非常惊讶。
基于上面对古老的 cats_vs_dogs 数据集的“实验”,似乎 sigmoid +BCE 在精度方面也很好。特别是如果您使用合理的批量和适当的数据,那么 BCE 的计算方式并不重要。然而,这只是一个数据集,很少有模型在上面测试。
所以,我的试探性总结:如果
- 您知道或怀疑最后一层的许多样本的原始输出达到极值,并且
- 您的批量非常小,和/或
- 你想把数字不精确排除在麻烦的可能原因之外,
从原始输出计算 BCE 没有坏处。否则,就坚持乙状结肠+BCE。
其他框架的评论、建议和经验?很高兴听到这个消息。
Sigmoid 神经元——深度神经网络的构建模块
深层神经网络的构建模块被称为乙状结肠神经元。Sigmoid 神经元类似于感知器,但是它们稍有修改,使得 sigmoid 神经元的输出比感知器的阶跃函数输出平滑得多。在这篇文章中,我们将讨论创造乙状结肠神经元的动机和乙状结肠神经元模型的工作原理。
引用注:本文内容和结构基于四分之一实验室的深度学习讲座——pad hai。
这是讨论 sigmoid 神经元的工作及其学习算法的两部分系列的第一部分:
1 | Sigmoid 神经元——深度神经网络的构建模块
为什么是乙状结肠神经元
在我们进入 sigmoid 神经元的工作之前,让我们简单地讨论一下感知器模型及其局限性。
感知器模型接受几个实值输入,并给出一个二进制输出。在感知器模型中,每个输入xi都有与之相关的权重wi。权重表明决策过程中输入的重要性。模型输出由阈值 Wₒ 决定,如果输入的加权和大于阈值 Wₒ 输出将为 1,否则输出将为 0。换句话说,如果加权和大于阈值,模型将被触发。

Perceptron (Left) & Mathematical Representation (Right)
根据数学表示,我们可以说感知器使用的阈值逻辑非常苛刻。让我们用一个例子来看看苛刻的阈值逻辑。考虑一个人的决策过程,他/她是否愿意购买一辆汽车仅基于一个输入X1 —工资,并通过设置阈值 b ( Wₒ ) = -10 和权重 W ₁ = 0.2。感知器模型的输出如下图所示。

Data (Left) & Graphical Representation of Output(Right)
红点表示一个人不会买车,绿点表示这个人想买车。一个 50.1K 的人会买车而一个 49.9K 的人不会买车是不是有点奇怪?感知器输入的微小变化有时会导致输出完全翻转,比如从 0 到 1。这种行为并不是我们选择的具体问题或者我们选择的具体权重和阈值的特征。这是感知器神经元本身的特性,其行为类似于阶跃函数。我们可以通过引入一种叫做s 形神经元的新型人工神经元来克服这个问题。
要了解更多关于感知器的工作,请参考我以前关于感知器模型的帖子
乙状结肠神经元
我们能有一个更平滑(不那么苛刻)的函数吗?
引入 sigmoid 神经元,其输出函数比阶跃函数平滑得多。在乙状结肠神经元中,与阶跃输出相反,输入的小变化仅引起输出的小变化。有许多具有“S”形曲线特征的函数称为 sigmoid 函数。最常用的函数是逻辑函数。

Sigmoid Neuron Representation (logistic function)
我们不再看到临界点 b 的急剧转变。乙状结肠神经元的输出不是 0 或 1。相反,它是一个介于 0-1 之间的真实值,可以解释为一个概率。
数据和任务
回归和分类
与 MP 神经元中的布尔输入不同,sigmoid 神经元的输入可以是实数,输出也是 0-1 之间的实数。在乙状结肠神经元中,我们试图回归 X 和 Y 之间的概率关系。即使输出在 0-1 之间,我们仍然可以通过选择一些阈值来使用 sigmoid 函数进行二进制分类任务。
学习算法
在本节中,我们将讨论通过使用梯度下降算法来学习 sigmoid 神经元模型的参数 w 和 b 的算法。

Minimize the Squared Error Loss
学习算法的目标是确定参数的最佳可能值,使得模型的总损失(平方误差损失)尽可能最小。学习算法是这样的:

Sigmoid Learning Algorithm
我们随机初始化 w 和 b 。然后,我们迭代数据中的所有观察值,对于每个观察值,使用 sigmoid 函数找到相应的预测结果,并计算平方误差损失。基于损失值,我们将更新权重,使得在新参数下模型的总损失将小于模型的当前损失。

Loss Optimization
我们将继续进行更新操作,直到我们满意为止。直到满意可能意味着以下任何一种情况:
- 模型的总损失变为零。
- 模型的总损失变成接近于零的非常小的值。
- 基于计算能力迭代固定次数。
它能处理非线性数据吗?
感知器模型的局限性之一是,学习算法只有在数据是线性可分的情况下才有效。这意味着正的点在边界的一边,负的点在边界的另一边。sigmoid 神经元能处理非线性可分数据吗?。
让我们举一个例子,一个人是否会购买一辆汽车基于两个输入,x₁——年薪 10 万卢比(LPA)和 x₂——家庭规模。我假设在 X 和 Y 之间有一个关系,它是使用 sigmoid 函数近似得到的。

Input Data(Left) & Scatter Plot of Data(Right)
红点表示输出为 0,绿点表示输出为 1。从图中我们可以看到,没有一条线或者一条线性的边界可以有效的将红绿点分开。如果我们在这个数据上训练一个感知器,学习算法将永远不会收敛,因为数据不是线性可分的。我将运行模型一定次数的迭代,以尽可能减少误差,而不是追求收敛。

Perceptron Decision boundary for fixed iterations
从感知器决策边界,我们可以看到,由于苛刻的阈值逻辑,感知器无法区分靠近边界的点和位于内部的点。但是在现实世界的场景中,我们会期望一个站在边界围栏上的人可以选择任何一条路,而不像在决策边界内的人。
让我们看看 sigmoid neuron 将如何处理这些非线性可分离数据。一旦我使用 sigmoid 神经元拟合我们的二维数据,我将能够生成如下所示的 3D 等值线图,以表示所有观察的决策边界。

Sigmoid Neuron Decision Boundary (Left) & Top View of Decision Boundary (Right)
为了进行比较,让我们进行同样的两次观察,看看这些观察的乙状结肠神经元的预测结果是什么。如您所见,在图的最左侧出现的观察的预测值为零(出现在暗红色区域),而另一个观察的预测值约为 0.35,即该人有 35%的机会购买汽车。与感知器的刚性输出不同,现在我们有一个介于 0-1 之间的平滑连续的输出,可以解释为一个概率。
仍然不能完全解决非线性数据的问题。
尽管我们已经引入了非线性 sigmoid 神经元函数,但是它仍然不能够有效地将红点与绿点分开。重要的一点是,从感知器中的刚性决策边界开始,我们已经朝着创建适用于非线性可分离数据的决策边界的方向迈出了第一步。因此,乙状结肠神经元是深层神经网络的构建模块,最终我们不得不使用神经元网络来帮助我们创建一个“完美的决策边界。
继续学习
如果你有兴趣了解更多关于人工神经网络的知识,请查看来自 Starttechacademy 的 Abhishek 和 Pukhraj 的人工神经网络。还有,课程是用最新版本的 Tensorflow 2.0 (Keras 后端)讲授的。他们也有一个非常好的关于 Python 和 R 语言的机器学习(基础+高级)的包。
结论
在这篇文章中,我们看到了导致乙状结肠神经元产生的感知机的局限性。我们还看到了乙状结肠神经元的工作示例,以及它如何克服一些限制。我们已经看到感知器和 sigmoid 神经元模型是如何处理非线性可分离数据的。
在下一篇文章中,我们将利用 math 详细讨论 sigmoid 神经元学习算法,并直观地了解为什么特定的更新规则有效。
推荐阅读:
在本帖中,我们将详细讨论 sigmoid 神经元学习算法背后的数学直觉。
towardsdatascience.com](/sigmoid-neuron-learning-algorithm-explained-with-math-eb9280e53f07) [## 感知器——深度学习基础
麦卡洛克-皮茨神经元的升级。
hackernoon.com](https://hackernoon.com/perceptron-deep-learning-basics-3a938c5f84b6)
联系我
GitHub:https://github.com/Niranjankumar-c LinkedIn:https://www.linkedin.com/in/niranjankumar-c/
免责声明 —这篇文章中可能有一些相关资源的附属链接。你可以以尽可能低的价格购买捆绑包。如果你购买这门课程,我会收到一小笔佣金。**
用数学解释的 Sigmoid 神经元学习算法

Photo by Antoine Dautry on Unsplash
这是讨论 sigmoid 神经元的工作及其学习算法的两部分系列的第二部分:
2 |用数学解释的 Sigmoid 神经元学习算法(当前故事)
在本帖中,我们将详细讨论 sigmoid 神经元学习算法背后的数学直觉。
引用注:本文内容和结构基于四分之一实验室的深度学习讲座——pad hai。
乙状结肠神经元概述
sigmoid 神经元类似于感知器神经元,对于每个输入xi,它都具有与该输入相关联的权重wi。权重表明决策过程中输入的重要性。与感知器模型不同,sigmoid 的输出不是 0 或 1,而是 0-1 之间的真实值,可以解释为概率。最常用的 sigmoid 函数是逻辑函数,它具有“ S 形曲线的特征。

Sigmoid Neuron Representation
学习算法
学习算法的目标是确定参数( w 和 b )的最佳可能值,使得模型的总损失(平方误差损失)尽可能最小。

Learning Algorithm
我们随机初始化 w 和 b 。然后,我们迭代数据中的所有观察值,对于每个观察值,使用 sigmoid 函数找到相应的预测结果,并计算平方误差损失。基于损失值,我们将更新权重,使得模型在新参数下的总损失将比模型的当前损失小。
学习算法:数学部分
我们看到权重是如何根据损失值更新的。在本节中,我们将了解为什么这个特定的更新规则会减少模型的丢失。为了理解为什么更新工作规则有效,我们需要理解它背后的数学原理。
让我们从学习算法的数学部分开始。

在 sigmoid 神经元函数中,我们有两个参数 w 和b。I 将以向量θ的形式表示这些参数,θ是属于 R 的参数的向量。目标是找到使损失函数最小化的θ的最佳值。

Vector Notation of Parameters
我们达到目标的方法是用一些小的随机值来更新θ,即δθδθ,它是 w 的变化和 b 的变化的集合。δθ也是一个属于 R 的向量,我们来看看θ和δθ的几何表示,

Geometric Representation
我们有一个向量θ,我们要给它加上另一个向量δθ。从向量平行四边形定律我们会得到我们的合成向量θnew,它无非是平行四边形的对角线。从几何表示中,我们可以看到,在θ和θnew 之间,θ的值有很大的变化。与其在学习θ时迈大步,不如保守一点,只往同一个方向移动很小的量。

Geometric Representation with Learning Rate
记住δθ是一个向量,为了在同一个方向上迈小步,我需要让δθ有一个较小的幅度。为了得到一个小的量级,我将把δθ乘以学习率(一个很小的值)。现在,合成矢量是红色矢量,表示θnew。新的θ将是向量θ从初始位置的移动。

使用泰勒级数
但是我们如何决定δθ的值以及使用什么样的δθ才是正确的呢?。我们如何以主要方式获得正确的δθ,使得新θ的损耗(是 w 和 b 的函数)应该小于旧θ的损耗。这个问题的答案来自泰勒级数。
泰勒级数告诉我们的是,如果我们有一个函数 f 并且我们知道该函数在特定点 x 的值,那么函数 f 在一个非常接近 x 的新点的值由下面的公式给出:

Taylor Series General Representation
靠近 x 的小步长后的函数值等于 x 处的函数值和其他一些量(出现在红框中)。如果我们观察红框中的数量,它的值完全取决于δx。如果我能够找到 delta x 使得红框中表示的数量为负,那么我将知道新 x 处的新损失小于旧 x 处的损失。
用 sigmoid 神经元参数和损失函数表示泰勒级数。为便于记记,设δθ= u,则我们有,

Replaced x with Parameter theta
将其与一般的泰勒级数方程进行比较,新θ的损耗等于旧θ的损耗和其他一些量(显示在红框中)。盒子中存在的量的值取决于变化向量 uᵀ.所以我们需要找到变化向量 uᵀ,使得红框中的量变成负数。如果为负,那么我们将知道θnew 处的损耗小于θold 处的损耗。
为了找到最佳变化向量 uᵀ,为了简单起见,让我们去掉泰勒级数中的一些项。我们知道学习率η非常小,那么η,η等…将非常接近于零。通过使用此逻辑,泰勒级数可以简化为以下形式:

Truncated Taylor Series
只有当新θ处的损耗小于旧θ时,变化矢量 uᵀ才是最佳的,

Optimal change vector
通过比较上述两个方程(变化向量 uᵀ优化方程和截断泰勒级数方程),

为了解决上面的方程,让我们回到一点线性代数,特别是两个向量之间的余弦角。我们知道 cos(θ)的范围从-1 到 1。

Cosine Angle between Vectors
由于余弦角公式的分母是一个数值,并且它总是正的,我们可以将余弦公式乘以 k (分母)而不影响不等式的符号。我们希望不等式之间的量(标在蓝框中)为负,这只有在 uᵀ和∇之间的余弦角等于-1 时才有可能。如果余弦角等于-1,那么我们知道向量之间的角度等于 180⁰.
如果角度等于 180⁰,这意味着我们选择的变化向量 uᵀ的方向,应该与梯度向量相反。我们可以将梯度下降规则总结如下:

类似地,我们可以如下编写参数更新规则,

计算偏导数
到目前为止,在前面的章节中,我们已经看到了如何使用泰勒级数和向量之间的余弦角来达到参数更新规则。但是我们如何计算对 w 和 b 的偏导数呢?。

为了简单起见,我们假设只有一个点适合于乙状结肠神经元。所以一个点的损失函数可以写成,

Loss function for one point
我们需要计算 w 和 b 对损失函数的偏导数。让我们推导这些导数,

Derivation for partial derivatives of w
首先,我们将偏导数推到括号内,接下来,我们有 y,它是常数,它的偏导数为零。在 f(x)的地方,我们用逻辑函数代替,求逻辑函数 w.r .对 w 的偏导数。上图的右边部分解释了逻辑函数的偏导数。上述最终表达式(用红色标记)仅对一次观察有效,对于两次观察,表达式变为:

类似地,我们可以计算 b 对损失的偏导数,

梯度下降规则
现在我们有了使用梯度下降实现参数更新规则所需的一切。让我们举一个例子,看看我们如何使用我们刚刚导出的参数更新规则来实现梯度下降,以最小化模型的损失。
我已经获取了一些玩具数据,并将参数初始化为 w = 0, b = -8,并将学习率设置为 1.0,然后迭代 1000 个时期的所有观察值,并计算不同值的 w (-2 到 6)和 b (-10 到 2) 的损失。O 一旦我获得了 w 和 b 所有可能组合的损失值,我就能够生成下面的 3D 等高线图来显示损失值的变化。

Loss Value for different w and b
以上代码片段显示了在 2D 玩具数据的情况下,参数 w 和 b 的梯度下降更新规则的实现。我们可以随机初始化参数值,但在这种情况下,我选择这些特定值只是为了更好地说明。
图表中较深的红色阴影表示损失值较高的区域,而山谷中较深的蓝色阴影表示损失的最低点。下面的动画显示了固定迭代的梯度下降规则,底部的点表示 w & b 的不同组合,轮廓上的点表示相应参数值的损失值。

Animation of loss variation
我们已经成功地推导出梯度下降更新规则,该规则保证找到对于 w 和 b 的最佳参数值,其中只要我们在与梯度相反的方向上移动,函数的损失最小。

Photo by JESHOOTS.COM on Unsplash
继续学习
如果你有兴趣了解更多关于人工神经网络的知识,请查看来自 Starttechacademy 的 Abhishek 和 Pukhraj 的人工神经网络。还有,课程是用最新版本的 Tensorflow 2.0 (Keras 后端)讲授的。他们还有一个非常好的包,关于 Python 和 R 语言的机器学习(基础+高级)。
结论
在这篇文章中,我们简要地看了一下乙状结肠神经元。然后,我们看到了学习算法的 sigmoid neuron 数学自由版本,然后我们继续使用泰勒级数、线性代数和偏微分来详细了解学习算法背后的数学直觉。最后,我们证明了我们使用梯度下降导出的更新规则保证通过使用玩具数据找到最佳参数。
这是一个有点冗长&数学很重的帖子,即使你第一次阅读没有完全理解,也要再浏览一遍。如果你有任何问题或建议,请在回复部分发表。
推荐阅读
深层神经网络的构建模块被称为乙状结肠神经元。
towardsdatascience.com](/sigmoid-neuron-deep-neural-networks-a4cd35b629d7)
在以后的文章中,我们将讨论如何用 python 实现 sigmoid 神经元模型,还将讨论其他一些非线性函数,如 RELU、ELU。
Niranjan Kumar 在汇丰银行数据分析部门实习。他对深度学习和人工智能充满热情。他是 Medium 在人工智能的顶尖作家之一。在 LinkedIn 上与我联系,或者在 twitter 上关注我,了解关于深度学习和人工智能的最新文章。
免责声明 —这篇文章中可能有一些相关资源的附属链接。你可以以尽可能低的价格购买捆绑包。如果你购买这门课程,我会收到一小笔佣金。
信号:噪声(毫升)
处理结果的变化

科学很难
最近,我在工作中和 Kaggle 上运行了许多机器学习实验。有一点已经很清楚了:真的很难自信地说 A 型比 B 型好。通常在速度和对结果的信心之间有一个明显的平衡。在像 Kaggle 这样的设置中,您的成功很大程度上取决于您可以评估多少想法,而这反过来又取决于您的测试平台的设计。
例如,您可以训练模型 A 和 B,测量性能,并选择最佳的一个。又快又简单。但是如果你改变随机种子并重复,也许你会得到相反的结果。然后,您可能会决定进行 5 重交叉验证以获得误差线,但这需要大约 5 倍的时间,并且会产生更多关于如何从数据中取样以形成折叠的问题(例如,组 k 折叠、分层 k 折叠等)。然后,您可能决定通过提高学习率或对训练集进行缩减采样来缩短每次运行的持续时间,但这将为您的结果增加额外的方差,使其更难在噪声中找到信号。
那么超参数调整呢?您是对模型 A 和模型 B 分别进行测试,以确保您为该数据/模型组合选择了最佳参数,还是锁定了参数,并接受了它们偏爱一个模型而非另一个模型的可能性?如果您正在为每一个参数进行调优,那么如何评估参数集 1 和参数集 2 呢?这实质上是回到比较两种不同的模型,并提出了所有相同的信号和噪声问题。
答案是:没有简单的答案。最终,时间和计算资源的限制将决定您的比较能力,这将限制您区分越来越小的性能差异的能力。然后,好的工程需要在了解噪声和变化如何与您的设计相互作用的背景下深思熟虑地做出决策。忽视它会导致武断的决定、死胡同和浪费时间。
旁注/咆哮:值得指出的是,这些课程并不是机器学习所独有的。作为半导体行业的工艺工程师,我经历了完全相同的挑战。在这个世界中,目标是减少晶片上的粒子数量。我清楚地记得有一天,我试图通过在 100 个晶片上进行实验来倡导工艺改变,这些晶片被分成实验/控制条件。当我考虑到颗粒数量的变化来计算所需的样本量时,我发现我实际上需要> 100,000 个晶片,这是一个完全荒谬的提议。作为科学家,我们经常面对这些不方便的事实,而且太常见了,反应是忽略它。我认为,这部分解释了许多科学领域文献中的复制危机,特别是在深度学习中,重复进行数百次实验的成本高得不可思议。
秘诀
以下是一个可能帮助你驯服随机性的过程的概要:
1。知道你想要衡量多大的变化。
你是想找到 0.001 AUC 的改进,还是 0.01 AUC 的改进?这个答案将极大地影响你的实验测试床。小心选择你的度量标准,因为有些比其他的更敏感,你想给自己最好的机会去发现你真正关心的事情的变化。
2。理解你随机性的来源。
如果你想对数据进行下采样,这个采样过程会产生变异——样本越小,变异越大。形成验证和测试集的行样本也会引入随机性,因为它们用于早期停止和/或评估。
3。测量基线模型的变化。
您将通过为步骤 2 中概述的每个随机过程随机选择一个值来做到这一点。例如,您将随机抽样一个训练集,引导抽样一个验证集,引导抽样一个测试集。然后,使用采样的验证和测试集构建模型,进行早期停止和性能评估。现在,重复 N 次(10-50 次?)并将结果用于统计分析。如果您的变化是 0.01 AUC,并且您希望观察 0.001 AUC 的变化,那么您要么需要运行每个实验的许多实例,要么需要以减少变化的方式重新定义您的过程,例如增加训练数据大小。
4。计算所需的样本量
一旦您知道了基线的变化和您想要测量的变化量,您就可以使用标准方法来计算在给定的置信水平下所需的样本量。如果这个数字远远大于可行的,你就需要设计一个不同的过程或者调整你的期望。
我是亚马逊网络服务的研究科学家。我有 ML-focused YouTube 频道 。您可以通过 邮件列表 订阅我所有的内容更新。
文章最初发表于https://blog.zakjost.com。
ACF 图和 PACF 图在时间序列分析中的意义

这篇文章是为那些想知道使用 ACF 和 PACF 图确定自回归(AR)和移动平均(MA)序列的顺序背后的直觉的人写的。我们大多数人都知道如何使用 ACF 和 PACF 图来获得 p 和 q 的值,以馈入 AR-I-MA 模型,但我们缺乏背后的直觉,为什么我们使用 PACF 和 ACF 来分别获得 p 和 q,而不是相反。
让我们先了解一下我们所说的 ACF 和 PACF 是什么意思,
ACF 是一个(完全)自相关函数,它给出任何序列与其滞后值的自相关值。我们将这些值与置信带和 tada 一起绘制出来!我们有一个 ACF 图。简而言之,它描述了序列的当前值与其过去值的关联程度。时间序列可以包含趋势、季节性、周期性和残差等成分。ACF 在寻找相关性时考虑了所有这些成分,因此这是一个“完整的自相关图”。
PACF 是一个偏自相关函数。基本上,不是像 ACF 那样寻找当前与滞后的相关性,而是寻找残差(其在移除已经由早期滞后解释的效应之后剩余)与下一个滞后值的相关性,因此是“部分的”而不是“完整的”,因为我们在找到下一个相关性之前移除了已经找到的变化。因此,如果残差中有任何隐藏信息,可以通过下一个滞后进行建模,我们可能会获得良好的相关性,并且我们会在建模时将下一个滞后作为一个特征。请记住,在建模时,我们不希望保留太多相关的要素,因为这会产生多重共线性问题。因此,我们只需要保留相关的特性。
现在让我们看看什么是 AR 和 MA 时间序列过程,
自回归(AR)过程,当时间序列的当前值可以使用同一时间序列的先前值获得时,即当前值是其过去值的加权平均值,则称该时间序列为 AR。股票价格和全球气温上升可以被认为是一个 AR 过程。
订单 p 的 AR 流程可以写成:

ϵt 是白噪声,y't-₁和 y't-₂是滞后。阶 p 是滞后值,在此之后,PACF 图第一次越过置信区间上限。在预测 AR 时间序列时,这些 p 滞后将作为我们的特征。我们不能在这里使用 ACF 图,因为即使对于过去很久的滞后,它也会显示良好的相关性。如果我们考虑这些特征,我们将会有多重共线性问题。这对于 PACF 图来说不是问题,因为它去除了已经由早期滞后解释的成分,所以我们只得到与残差相关的滞后,即没有由早期滞后解释的成分。
在下面的代码中,我定义了一个简单的 AR 过程,并使用 PACF 图找到了它的顺序。我们应该期待我们的 AR 过程在 ACF 图中显示逐渐减少,因为作为 AR 过程,它的现在与过去的滞后有很好的相关性。我们预计 PACF 在接近滞后之后会大幅下跌,因为这些接近当前的滞后可以很好地捕捉变化,以至于我们不需要过去的滞后来预测当前。
现在让我们讨论第二种类型的过程,
移动平均(MA)过程,序列的现值被定义为过去误差的线性组合的过程。我们假设误差独立地服从正态分布。q 阶 MA 过程定义为:

在这里,ϵt 是一个白噪音。为了直观地了解 MA 过程,让我们考虑一下 1 阶 MA 过程,

让我们把 y t 看作原油价格,ϵt 是由于飓风引起的油价变化。假设 c=10(没有飓风时原油价格的平均值)和θ₁=0.5.假设今天有一场飓风,而昨天没有,那么 y 将是 15,假设由于飓风ϵt=5.,石油价格发生了变化明天没有飓风,所以ϵt=0 和ϵt-₁=5 不会有 12.5 度。假设后天没有飓风。在这种情况下,石油价格将是 10 美元,这意味着它在受到飓风的影响后稳定下来。所以在我们的例子中,飓风的影响只停留在一个滞后值上。在这种情况下,飓风是一个独立的现象。
MA 过程的 q 阶从 ACF 图中获得,这是 ACF 第一次越过置信区间上限后的滞后。正如我们所知,PACF 捕捉残差和时间序列滞后的相关性,我们可能会获得最近滞后以及过去滞后的良好相关性。为什么会这样?由于我们的序列是残差的线性组合,没有一个时间序列本身的滞后可以直接解释它的现在(因为它不是 ar),这是 PACF 图的本质,因为它减去了早期滞后已经解释的变化,它的 PACF 在这里失去了它的力量!另一方面,作为一个 MA 过程,它没有季节或趋势成分,因此 ACF 图将只捕捉残差成分的相关性。你也可以把它看作 ACF,它是一个完整的图(捕捉趋势、季节性、循环和残差相关性),作为一个部分图,因为我们没有趋势、季节等。
在下面的代码中,我定义了一个简单的 MA 过程,并使用 ACF 图找到了它的顺序。我们可以预期 ACF 图显示与最近滞后的良好相关性,然后急剧下降,因为它不是与过去滞后具有良好相关性的 AR 过程。此外,我们预计 PACF 图会逐渐减少,因为这是一个 MA 过程,与 AR 过程不同,时间序列的最近滞后值不能真正预测其现值。因此,我们将得到残差与进一步滞后的良好相关性,因此逐渐减少。
总而言之,
在建立机器学习模型时,我们应该避免多重共线特征。这同样适用于时间序列模型。
我们使用 PACF 图找到 AR 过程的最佳特征或顺序,因为它消除了由早期滞后解释的变化,所以我们只得到相关的特征。
我们使用 ACF 图找到 MA 过程的最佳特征或顺序,因为作为 MA 过程,它没有季节和趋势分量,所以我们在 ACF 图中仅获得与时间序列滞后的残差关系。ACF 充当部分情节。
我希望你喜欢这篇文章,我已经尽量让它简单。由于这是我的第一篇关于媒体的文章,我将非常感谢反馈和建议,以便在以后的文章中提高自己。
感谢阅读,快乐学习!再见!
硅谷的大脑干预:科技设备的新领域

哈佛大学分子和细胞生物学教授杰夫·李奇曼在向他的学生介绍人类大脑的研究时曾问道:“如果理解你需要了解的关于大脑的一切是一英里,那么我们已经走了多远?”。他得到的回答是“四分之三英里”、“半英里”和“四分之一英里”。
教授的回应?:我想大概三寸。
上个月,李奇曼的妙语被皇家学会纳入了一份新的报告,该报告调查了神经(或“脑-机”)接口的前景,这是一个热门的研究领域,在过去的几年里,已经有数十亿美元的资金投入其中,这不是没有原因的。预计到 2022 年,神经科技产品的全球市场——定义为“电子和工程在人类神经系统中的应用”— 将达到 133 亿美元。
因此,尽管我们承认缺乏理解,但对于寻求重新发明——或许是不可逆转地影响——我们与世界互动的方式的技术先锋来说,大脑似乎是一个新的重要前沿。
皇家学会的报告推测:
精神健康状况可以通过使用针对大脑相关部分的界面来治疗,从而缓解全球数亿抑郁症患者的痛苦。即使是已经证明对传统疗法有抵抗力的阿尔茨海默病,也可能被阻止或逆转。
医疗用途之外:
人们可以接受“全脑诊断”,以确定他们独特的才能和挑战。今天的“大脑训练”电脑游戏,其影响是有争议的,可能会让位于明显有效的“大脑清洁”或“大脑健身房”课程,以保持思维敏捷和创造力。
神经接口为增强日常生活提供了无数的可能性。我们可以用我们的思维开门、开灯、玩游戏、操作设备或在电脑上打字。
那么就有机会增强或增压大脑本身。植入物、头盔、头带或其他设备可以帮助我们记忆更多,学习更快,更快地做出更好的决定,解决问题,不受偏见的影响…
心情、知识和记忆可以安全保密地备份或上传到数字云中。
我知道,太多了。我还省略了对心灵感应的提及,人类与人工智能的潜在融合,以及将你的神经接口连接到另一种动物(如鸟)的选项。
对于一个科幻迷来说,这一切听起来一定像来自天堂的甘露。对我们其他人来说,这可能有点令人困惑(至少可以这么说)。那么,这是一个真实的命题吗?或者只是一些野心勃勃的硅谷书呆子的(相当令人毛骨悚然的)愿望清单?

事实是,很难说脑机接口的长期轨迹会是怎样的,但在某种程度上,它们已经是了。虽然仍然相当初级,但我们目前有可以只用大脑控制的无人机和假肢,以及提高注意力和记忆力的耳机。这些技术中有些是侵入性的,但许多不是。有些记录大脑活动并对其做出反应,有些刺激大脑活动,有些两者兼而有之。
令人放心的是,非侵入性技术有望走向商业销售。其中大部分是对脑电图(EEG)的重新想象,EEG 是一种监测和记录大脑中电脉冲的系统。商业领域的领导者之一, CTRL-Labs ,特别专注于它所谓的“意图捕捉”。他们的产品是一种基于肌电图的腕带,当电信号在用户手臂肌肉中激活时,它可以做出反应。目前,该公司的演示中有一个玩家只使用这种脉冲检测而没有物理移动来控制一个简单的游戏(看一下)。
如果你怀疑这件事能走多远,你应该知道脸书上个月收购了 CTRL-Labs,就在几周前泄露了马克·扎克伯格内部会议的记录增强了该公司对脑机接口的浓厚兴趣。
在谈及埃隆·马斯克的 Neuralink 项目时,扎克说:
我对非侵入性的脑机接口感到非常兴奋。我们希望能够做到的是能够获得哪怕是一点点。所以你可以做一些事情,比如,你在 AR 里看一些东西,你可以用你的大脑点击。那太令人兴奋了…或者一个对话出现了,你不必用手,你可以只说是或不是。那是一点输入。如果你有两位,你可以开始控制一个菜单,对吧,基本上你可以滚动菜单并点击。你得到更多的比特,你可以开始用你的大脑打字,而不需要用你的手或眼睛或任何类似的东西。我认为这很令人兴奋。所以我认为作为 AR 和 VR 的一部分,我们将最终拥有手动界面,我们将最终拥有语音,我认为我们将拥有一点点直接大脑。
如果一点点“直接大脑”不会困扰你,那就值得期待超越基本系统基本控制的可能性。
例如,我们已经有了能够解读情绪和情感的神经系统。去年,《南华早报》 报道中国公司已经部署了这种技术,希望利用头饰和帽子内置的设备监测员工的愤怒、焦虑或抑郁迹象。也许更令人印象深刻(或令人不安)的是,日本京都大学的研究人员已经能够使用深度神经网络将来自 fMRI 扫描(用于绘制神经活动)的大脑信号转换成图像,该图像包含扫描对象看到的许多形状和颜色特征。
这只是说,一旦这些类型的系统提供了在马克·扎克伯格的 AR hellscape 中点击或滚动的能力,它们就不太可能停止开发。
英国皇家学会的报告肯定会提出一些早期的担忧。大多数理性思考的人不会落后他们太多:如果一家外部公司或政府能够获得我们的情绪,甚至我们的想法,这将意味着什么?如果这些技术变得无处不在,人类的隐私——甚至自主权——将如何得到保护?我们如何确保它们不会被坏人或政府武器化以影响和控制整个人口?(如果他们只是想下意识地哄我们吃得更健康,或者尊重规则,这样可以吗……?)
不难想象,政府将会密切关注这项技术的发展。
尽管在急切敲响警钟之前权衡风险和收益是公平的,但即使在这里也存在模糊之处。这项技术商业化的好处似乎极其有限,至少从表面上看是这样。游戏玩法?健身?增强或虚拟现实环境的免提导航?这些都不像是出售大脑使用权的有力论据。
但是,那些可以提高记忆力或注意力,让我们在生活和工作中变得超级高效的神经接口呢?据推测,人们可以证明这是一笔值得的交易?顺便说一句,在皇家学会的报告发布后,完全独立的研究应该敦促人们对增强这种功能的尝试保持谨慎。
一份新的期刊在科学上发表的发现似乎证实了长期持有的理论,即存在一种活跃的“遗忘机制,它在我们睡觉时启动。研究发现,当研究人员抑制小鼠体内产生自然产生的下丘脑黑色素浓缩激素(MCH)的神经元时,它们的记忆性能实际上有所提高。换句话说,如果没有这种非自然的抑制,这些激素就会非常故意地损害——或“调节”——我们的记忆。
这是生物学上的附加,而不是某种我们必须用技术来补偿的“缺失”。我们可以有把握地假设它服务于某种有价值的进化目的。
事实上,有很好的理由相信,如果我们不忘记,我们将生活在一个永久的混乱状态中,我们的大脑充斥着令人困惑的多余信息。一个有趣的故事讲述了记忆混乱的大脑,这个人被称为主体的;1929 年,一位年轻的驻莫斯科记者(后来被确认为所罗门·舍雷舍夫斯基)带着一个非常特殊的问题找到神经心理学家亚历山大·卢里亚博士:他无法忘记。
根据 Luria 的报告,受试者 s .能够记住外国诗歌、科学公式和一大串单词和数字,而这些都是在他被告知几十年之后。每次卢里亚测验他时,他都把它们背得滚瓜烂熟。
伟大的资产,是吧?在鸡尾酒会上永远不会忘记一个名字,不会错过一个生日,不会在一个你已经知道的事实或公式的测试中失败?用水晶般的清晰来回忆你自己的人生,而不是用模糊的薄雾来冲刷我们最珍贵的记忆?
并非如此。据《纽约时报》报道:
南记忆能力也是日常生活中的一个障碍。他很难理解抽象的概念或比喻性的语言,他在识别面孔方面很糟糕,因为他已经记住了他们在一个确切的时间点,有特定的面部表情和特征。科学家们最终意识到,遗忘的能力和记忆的能力一样重要。
谁知道使用脑机接口来优化进化方面最终会带来什么心理或神经混乱…
但是我们可能还不应该尖叫着跑向山丘。这些系统还处于起步阶段,研究中已经取得了令人难以置信的突破,应该会给精神和身体障碍的人带来巨大的好处。尽管如此,英国皇家学会在伦理和道德困境面前走在前面是正确的,这种伦理和道德困境将伴随着这类技术的商业化。这是一个陌生的领域,允许一个系统干预我们的身体和精神能力是一种前所未有的侵犯,很容易变坏。当然,如果我们从迄今为止科技情报和监控的使用方式来判断的话。
现在,我们应该密切关注这项技术如何发展,以及对其使用的所有建议。有一件事似乎是真的,如果我们认为社会已经达到了它的技术饱和点,那么我们“还没有看到任何东西”。
使用 FastAi 和 airy 的相似图片推荐

基于图像相似性的推荐,如上所示,是我们将要考虑的,给定一个基础图像,推荐视觉上相似的图像。基于图像的推荐在许多情况下非常方便,特别是在涉及用户视觉品味的情况下——服装、珠宝、配饰等。我们将使用 DeepFashion 数据集,其中有 289,222 张服装图片分布在 42 个类别。让我们快速看一下这个项目的工具箱。
FastAi ,【PyTorch 上的一个包装器,它使深度学习任务惊人地简单,并附带了所有最佳实践。
PyTorch ,深度学习库由脸书提供,我们将在我们的项目中使用 PyTorch 的一些功能。
****、近似最近邻由 Spotify 开源实现,能够高效地索引和搜索特征向量。
这个项目的代码可以在这个 jupyter 笔记本中找到。
嵌入或特征向量
嵌入或特征向量可以被认为是对象上的简洁的 n 维向量形式,其目的是捕获包含在对象中的大部分信息。在其 n 维特征空间中,当比较不同的对象时,相似的对象彼此更接近。
对于一幅图像,嵌入可以从卷积神经网络** (CNN)中获得。CNN 能够理解/学习图像中包含的信息,在其每一层的不同细节层次上。初始卷积层理解诸如角、边等低级形状。而末端层理解图像中的复杂结构,人脸,建筑物等。基于任务。CNN 的末端层通常是完全连接的线性层。这些完全连接的层的输出被用作图像嵌入。**

Image Embedding. CNN image source-en.wikipedia.org/wiki/Convolutional_neural_network
使用 FastAI 微调预训练的 Resnet152 模型
我们使用的 CNN 模型是在 Imagenet 数据集上训练的预训练 Resnet152 用于图像分类。虽然这个模型可以直接用于获得嵌入,但使用 FastAi 对模型进行微调以适应深度时尚数据集将有助于模型更好地理解我们数据集的细微差别。这个过程叫做 迁移学习 。
在 DeepFashion 类别预测任务上训练模型,取得了 88.4% 的前 3 名准确率和 93.98%** 的**前 5 名准确率,优于 DeepFashion 白皮书【1】中公布的基准分数。****

Results of training resnet152 for 10 epochs
从这个微调的模型中提取特征向量。
使用 PyTorch 钩子从模型中提取特征向量
根据 resnet152 架构,我们使用倒数第二个全连接层的输出作为嵌入,它们是维数为(512,1)的向量。
为了从 PyTorch 中的模型获得中间层的输出,我们使用了一个名为 Hook 的功能。可以添加钩子来从正向传递或反向传递中提取模型的中间值。
使用 PyTorch 钩子的最佳方法可以改编自 FastAi 库代码本身。
使用 Pytorch 钩子,我们为训练和有效数据集中的所有图像生成特征向量。
使用特征向量推荐相似图像
既然我们有了所有图像的特征向量,我们将不得不在给定一个基础图像的情况下得到相似的图像。最初,尝试一种简单的方法,给定一个基础图像,让计算它与数据集中所有其他图像的相似性得分,并获得前 n 个图像。
这种方法不可扩展,每个查询的复杂度为 O(N ),对于 249,222 张图像,大约需要 10 秒的时间。为了将这种复杂性降低到亚线性时间,我们将使用近似最近邻算法。近似最近邻算法可以看作是精度和性能之间的权衡。《烦人》的作者 Eric Bernhardsson 在的博客上发表了各种人工神经网络算法的基准比较。****
使用骚扰获得相似的图像
****angry(AapproximateNearestNeighborOhYeah)是基于二叉树的 ANN 实现。对其工作原理的最佳解释可以在埃里克的文章中找到。

Annoy — binary tree traversal for a query, source-erikbern.com/2015/10/01/nearest-neighbors-and-vector-models-part-2-how-to-search-in-high-dimensional-spaces.html
精度-性能的权衡由我们构建的二叉树的数量来控制,越多的树意味着越好的精度。我们将使用与数据集中的类数量相同的树。下面的代码用于构建索引。
现在让我们尝试查询恼人的指数。
使用 Annoy 的近似查询时间减少到了 2ms ,比简单的方法少了几个数量级。
相似图片推荐
下面再来几个通过查询骚扰指数得到的推荐。

Bridal Dresses

Hoodies

Girls tees

Men's pants
资源
[1] 刘,罗,邱,王,唐。Deepfashion:通过丰富的注释支持强大的服装识别和检索。2016 年在 CVPR。
希望这篇文章对你有所帮助,请在下面留下你的评论和想法。😊
相似性度量—对文本文章评分
许多现实世界的应用程序利用相似性度量来查看两个对象是如何联系在一起的。例如,我们可以在涉及计算机视觉和自然语言处理的应用程序中使用这些度量来查找和映射相似的文档。对于企业来说,一个重要的用例是将简历与职位描述相匹配,从而为招聘人员节省大量时间。另一个重要的用例是使用 K 均值聚类算法(也使用相似性度量)为营销活动细分不同的客户。
相似性通常是介于 0(无相似性)和 1(完全相似性)之间的正值。我们将具体讨论两个重要的相似性度量,即欧几里德和余弦,以及处理维基百科文章的编码示例。
欧几里得度量
你还记得毕达哥拉斯定理吗??毕达哥拉斯定理用于计算两点之间的距离,如下图所示。

http://rosalind.info/glossary/euclidean-distance/
在图中,我们有两个数据点(x1,y1)和(x2,y2 ),我们感兴趣的是计算这两个点之间的距离或接近程度。为了计算距离,我们需要先从 x1 到 x2 水平移动,然后从 y1 到 y2 垂直向上移动。这就组成了一个直角三角形。我们对计算斜边 d 感兴趣,使用毕达哥拉斯定理可以很容易地计算出斜边 d。

其中 b 是直角三角形的底边,p 是直角三角形的垂线。




这就完成了我们二维空间中两点的欧氏距离公式。

这定义了一维、二维、三维或更高维空间中两点之间的欧几里德距离,其中 n 是维数,x_k 和 y_k 分别是 x 和 y 的分量。
定义欧氏距离的 Python 函数
def euclidean_distance(x, y):
return np.sqrt(np.sum((x - y) ** 2))
这里 x 和 y 是两个向量。
也可以使用 sklearn 库来计算欧氏距离。这个函数在计算上更有效。
from sklearn.metrics.pairwise import euclidean_distances
距离越大,两个对象之间的相似性越低;距离越小,两个对象之间的相似度越高。要将这个距离度量转换为相似性度量,我们可以用最大距离除以对象的距离,然后减去 1,以得到 0 和 1 之间的相似性得分。在讨论了余弦度量之后,我们将看看这个例子。
余弦公制
这是专门为文档寻找相似性的另一个度量。如图所示,此指标用于测量 x 和 y 之间的角度,当矢量的大小无关紧要时使用。

https://en.wikipedia.org/wiki/Cosine_similarity
如果 v 和 w 之间的角度是 0 度,那么余弦相似度=1(完全相似)。
点积余弦公式:

‖⋅‖表示矢量长度
𝐯和𝐰.之间的𝜃:角这里我们感兴趣的是测量 v 和 w 之间的相似性。
如果你想知道如何推导点积的余弦公式,请参考本页:https://proofwiki.org/wiki/Cosine_Formula_for_Dot_Product.这个点积公式是从你们在学校学过的余弦定律推导出来的。
让我们用符号 x 和 y 代替 v 和 w。
示例:






这两个向量具有低相似性,由 0.11 的值来解释。该值接近 0,这意味着 x 和 y 之间的角度接近 90 度。如果该值接近 1,那么这将是角度接近 0 度的非常相似的对象。
将 x 和 y 除以它们的长度,将其长度归一化为 1,这就是所谓的单位向量。这表明余弦相似性不考虑 x 和 y 的大小。当我们需要考虑大小时,欧几里得可能是更好的选择。
如果我们已经有了长度为 1 的向量,余弦相似性可以很容易地使用简单的点积来计算。因此,建议首先归一化矢量,使其具有单位长度,以减少计算时间。
Python 函数定义余弦相似度
def cosine_similarity(x, y):
return np.dot(x, y) / (np.sqrt(np.dot(x, x)) * np.sqrt(np.dot(y, y)))
我应该使用哪个指标?

该图总结了欧几里德度量和余弦度量。
余弦着眼于两个向量之间的角度,忽略大小,而欧几里得着眼于直线距离,考虑向量的大小。
文本挖掘是我们可以利用余弦相似性来映射相似文档的领域之一。我们还可以使用它来根据给定的单词向量对文档进行排序,简历入围就是我们可以利用余弦相似度的用例之一。
让我们看一下我们需要匹配相似文档的问题,我们首先创建文档术语矩阵,它包含术语在文档集合中出现的频率。我们通常有不均匀长度的文档,例如维基百科的文章。假设单词 math 在文档 1 中比在文档 2 中出现得更多,在这种情况下,余弦相似度将是一个完美的选择,因为我们不关心文档的长度,而是它包含的内容。如果我们要考虑长度,那么欧几里得可能是一个完美的选择。
现在我们将使用 Python 编写简短的代码来映射类似的维基百科文章。
import wikipediaarticles=['Data Mining','Machine Learning','Cricket','Swimming','Tennis']
wiki_lst=[]for article in articles:
print(article)
wiki_lst.append(wikipedia.page(article).content)
在这里,我们刚刚从 5 篇维基百科文章中提取了内容。
from sklearn.feature_extraction.text import CountVectorizer
cv = CountVectorizer()
X=cv.fit_transform(wiki_lst)
计数矢量器只是将文档集合转换成字数矩阵。
现在让我们使用欧几里德度量来找出我们的文章是相似的。这里,我们将这些欧几里得距离归一化为 0 到 1,然后减去 1,使其范围在 0 到 1 之间。因此,它越接近 1,相似性越高。
from sklearn.metrics.pairwise import euclidean_distances
print("Data Mining and Machine Learning",1-euclidean_distances(X[0],X[1])/np.max((euclidean_distances(X))))
print("Data Mining and Cricket",1-euclidean_distances(X[0],X[2])/np.max((euclidean_distances(X))))
print("Data Mining and Swimming",1-euclidean_distances(X[0],X[3])/np.max((euclidean_distances(X))))
print("Data Mining and Tennis",1-euclidean_distances(X[0],X[4])/np.max((euclidean_distances(X))))

这两篇文章(数据挖掘游泳)怎么最像?这没有任何意义。现在让我们试试余弦相似度。
from sklearn.metrics.pairwise import cosine_similarity
print("Data Mining and Machine Learning",cosine_similarity(X[0],X[1]))
print("Data Mining and Cricket",cosine_similarity(X[0],X[2]))
print("Data Mining and Swimming",cosine_similarity(X[0],X[3]))
print("Data Mining and Tennis",cosine_similarity(X[0],X[4]))

这对我们来说是有意义的。数据挖掘非常接近机器学习。
结论
欧几里得度量在这里似乎集中于长度而不是内容,而余弦集中于内容而忽略了幅度。
参考文献
【https://proofwiki.org/wiki/Cosine_Formula_for_Dot_Product?】[1]T4source = post _ page-E3 DBD 4 e 58660-
【2】https://cmry.github.io/notes/euclidean-v-cosine?source = post _ page-E3 DBD 4 e 58660-
随机游走文档的相似性
Python 代码和解释,用于计算带有重启的随机行走的图中节点的相似性
考虑具有 5 个节点的无向图,其中节点的连接如下。

Document-topic assignment graph
我们看到有两种类型的节点。一类是文档另一类是题目。在这篇博客文章中,我将解释如何使用重启随机漫步来计算基于主题的文档之间的相似性。您还可以想到一个更大的图,它的节点类型是单词而不是主题,其连接告诉我们哪个单词出现在哪个文档中。为了简单起见,我将选择主题。
随机行走
现在想象我们站在文档 1 的节点上,随机(统一)决定走哪条边到达另一个节点。因为我们只有两条边,所以我们去主题 1 的概率是 0.5,就像我们去主题 2 的概率一样。我们总共有 1 英镑。
如果我们对所有的节点进行这样的想象,我们最终会得到一个矩阵 M,它的起点是列,概率在相应的行中。这个矩阵可以用 python 创建,代码如下:
**import** numpy **as** np
M = np.array([
[0, 0, 0, 1./3., 0.5],
[0, 0, 0, 1./3., 0],
[0, 0, 0, 1./3., 0.5],
[0.5, 1, 0.5, 0, 0],
[0.5, 0, 0.5, 0, 0]
])
通常的随机漫步不会只有一步,而是两步。因此,两步后,您从文档 1 到达节点文档 3 的概率就是这些概率的组合。为了计算一个步行者在两步后到达的概率,下面的脚本会有所帮助。
v = np.array([1., .0, .0, .0, .0]) # starting point
**for** step **in** range(n):
v = np.dot(M, v)
变量 v 告诉我们当前节点的概率。因为我们从文档 1 开始,所以在该节点的概率是 100%。点乘取这个概率,并与下一个可能节点的概率执行向量乘法。因此,我们得到一个向量,它告诉我们,基于我们之前去过的地方的概率,我们可能到达的地方的概率。对于第一步,这只是 m 的第一列。从那里开始,我们可以在主题 1 或主题 2 处,并且必须将这个概率乘以下一个路径的概率。
重新启动
带重启的随机行走算法包括重启的概念。在这里,一个步行者有β的概率继续行走而不需要重新开始,因此 1-β再次传送到它的起点并从那里开始行走。这有助于探索更多样化的道路。
实现相当容易。我们不只是用转移矩阵 M 乘以我们现在可能在哪里的概率,而是包括我们在开始节点结束的概率。
start = np.array([1., .0, .0, .0, .0]) # starting point
v = start
**for** step **in** range(n):
v = beta*np.dot(M, v) + (1-beta)*start
因此,我们将通常的步行乘以β,并加上到达起点的剩余机会(1-β)。
实验
如果我们进行 n 步随机行走,我们得到以下概率:
array([0.34461028, 0.06633499, 0.14461028, 0.24875622, 0.19568823])
它们会告诉我们可能会到达哪个节点。有 34%的概率我们会再次回到起点,7%的概率我们会在文档 2 、处,14%的概率我们会在文档 3* 处,依此类推。因此,出现在文档 3 的概率是文档 2 的两倍,因此我们可以说文档 1 (我们的起点)与文档 3 的相似度是文档 2 的两倍。*
结论
由于事先的题目分配,已经清楚文件 3 与文件 1 相似。无论如何,这个简单的例子可以推广到更复杂的图形和实体之间的关系。结合简单的实现,这是一种计算相似性的好方法。
使用 Python 进行简单和多元线性回归

线性回归是对单个因变量(目标变量)和一个(简单回归)或多个(多元回归)自变量之间的关系进行建模的方法。线性回归模型假设输入和输出变量之间存在线性关系。如果存在这种关系,我们就可以估计模型对新数据进行预测所需的系数。
在本文中,您将学习如何使用多个库,如 Pandas 、 Numpy 、 Scikit-Learn 、、和 Scipy ,从零开始在 Python 中可视化和实现线性回归算法。此外,我们将使用皮尔森 相关系数来衡量两个变量之间线性关系的方向和强度,以及使用均方差等评估指标来衡量线性回归模型的预测精度。
现在!我们开始吧💜
数据集的分析
本文使用的数据集是在 Kaggle 中获取的。 Kaggle 是一个数据科学家和机器学习者的在线社区,在这里可以找到各种各样的数据集。所选的数据集包含 5000 名男性和 5000 名女性的身高和体重,可通过以下链接下载:
[## 你的数据科学之家
编辑描述
www.kaggle.com](https://www.kaggle.com/mustafaali96/weight-height/downloads/weight-height.zip/1)
第一步是使用 熊猫 导入数据集。 Pandas 是一个用于数据科学的 Python 开源库,允许我们轻松处理结构化数据,如 csv 文件、 SQL 表或 Excel 电子表格。导入 csv 文件后,我们可以打印数据集的前五行、每列的数据类型以及空值的数量。


正如我们可以很容易地观察到的那样,数据帧包含三列:性别、身高和体重。性别列包含 object 类型的两个唯一值:男性或女性。高度和重量列中使用了浮点数据类型。由于数据帧不包含空值,并且数据类型是预期的类型,因此没有必要清理数据。
为了更好地理解身高和体重这两个变量的分布,我们可以简单地用直方图来描述这两个变量。直方图是显示数字变量分布的图表,将数据分组到多个条块中。条形的高度代表每个箱的观察数量。


前面的图表显示,身高和体重这两个变量呈现出正态分布。作为我们探索性分析的一部分,在单独的直方图中绘制男性和女性的分布也是很有趣的。


前面的图显示,男性和女性的身高和体重都呈现出正态分布。虽然两种分布的平均值对于男性来说更大,但是两种性别的分布范围是相似的。 Pandas 提供了一个名为 describe 的方法,用于生成数据集的描述性统计数据(集中趋势、分散和形状)。

探索性数据分析包括分析数据集的主要特征,通常采用可视化方法和汇总统计。目标是理解数据,发现模式和异常,并在我们执行进一步评估之前检查假设。经过探索性分析,我们可以得出结论:身高和体重是正态分布。男性分布呈现出较大的平均值,但是与女性分布相比,分布的扩散确实是相似的。
但也许此时你会问自己:身高和体重有关系吗?我可以用一个人的身高来预测他的体重吗?
两个问题的答案都是肯定的!😃 💪让我们继续▶️ ▶️
使用 Matplotlib 的散点图和使用 Numpy 的线性回归
散点图是一种二维数据可视化,显示两个数值变量之间的关系——一个沿 x 轴绘制,另一个沿 y 轴绘制。 Matplotlib 是一个 Python 2D 绘图库,包含一个内置函数来创建散点图Matplotlib . py plot . scatter()函数。
下图显示了男性和女性的身高和体重之间的关系。可视化包含 10000 个观察结果,这就是为什么我们观察到过度绘制。当数据在可视化中重叠时,会出现过度绘制,使得难以可视化单个数据点。在这种情况下,原因是大量的数据点(5000 个男性和 5000 个女性)。另一个原因可能是少量的唯一值;例如,当散点图的一个变量是离散变量时。

在下面的图中,我们随机选取了 500 名女性的身高和体重。该图没有过度绘制,我们可以更好地区分各个数据点。正如我们在前面的图中所观察到的,雄性和雌性的体重随着身高的增加而增加,在两种情况下都显示出线性关系。

简单线性回归是一种对因变量和自变量之间的关系进行建模的线性方法,可获得最符合数据的直线。
y =a+bx
其中 x 为自变量(身高) y 为因变量(体重) b 为斜率, a 为截距。****截距表示 x 为 0 时 y 的值,而斜率表示直线的陡度。目标是获得最符合我们数据的线(最小化误差平方和的线)。误差是实际值 y 和预测值 y_hat 之间的差值,预测值是使用计算的线性方程得到的值。
误差= y(真实)-y(预测)= y(真实)-(a+bx)
使用 Numpy 我们很容易得到这条线。 Numpy 是一个用于科学计算的 python 包,它提供了高性能的多维数组对象。numpy 函数 polyfit numpy.polyfit(x,y,deg) 将一个次数为 deg 的多项式拟合到点 (x,y),返回最小化平方误差的多项式系数。在下面几行代码中,我们获得了预测女性和男性体重的多项式。
下图描述了散点图以及之前的回归线。

带有 seaborn 的散点图和线性回归线
Seaborn 是基于 matplotlib 的 Python 数据可视化库。我们可以使用seaborn . reg plot函数轻松地用 seaborn 创建回归图。与之前的方法相比,所需的线路数量要少得多。

前面的图显示了绘制 10000 个样本时的过度绘制。该图显示了男性和女性的身高和体重之间的正线性关系。为了更直观,下图显示了 300 个随机选择的样本的回归图。

使用 sklearn 拟合简单的线性模型
Scikit-learn 是一个免费的 python 机器学习库。我们可以使用 linear regression 类通过 Scikit-learn 轻松实现线性回归。创建线性回归对象后,我们可以通过调用 fit 方法来获得最适合我们的数据的直线。
使用 Sklearn 线性回归获得的值与之前使用 Numpy polyfit 函数获得的值相匹配,因为两种方法都计算使平方误差最小的直线。如前所述,误差是因变量的实际值与模型预测值之间的差异。最小平方误差通过最小化平方误差的总和 S 找到最佳参数值。
一旦我们拟合了模型,我们就可以使用预测方法进行预测。我们还可以通过使用 polyval 函数,使用 Numpy 中计算的多项式进行预测。使用 Scikit Learn 和 Numpy 获得的预测是相同的,因为这两种方法使用相同的方法来计算拟合线。
皮尔逊相关系数
相关性衡量两个变量相关的程度。皮尔逊相关系数用于衡量两个变量之间线性关系的强度和方向。该系数的计算方法是将变量的 协方差 除以其 标准差的乘积,其值介于+1 和-1 之间,其中 1 表示完全正线性相关,0 表示没有线性相关,1 表示完全负线性相关。
我们可以通过使用 获得一个数据帧的变量的相关系数。corr()法。默认情况下,计算皮尔逊相关系数;然而,也可以计算其他相关系数,例如 Kendall 或 Spearman 相关系数。
****
解释相关系数大小的经验法则如下:
- 1–0.8→非常强
- 0.799–0.6→强
- 0.599–0.4→中等
- 0.399–0.2→弱
- 0.199–0→非常弱
在之前的计算中,我们已经获得了大于 0.8 的皮尔逊** 相关系数,这意味着身高和体重对于男性和女性来说都是高度相关的。**
我们也可以使用 Scipy 的 stats 包计算皮尔逊相关系数。函数scipy . stats . Pearson r(x, y ) 返回两个值 Pearson 相关系数和 p 值。
可以看出,使用熊猫和 Scipy 的相关系数是相同的:
- 雌性相关系数:0.849608
- 男性相关系数:0.8629788
残差图
我们可以使用数值,如皮尔逊相关系数或可视化工具,如散点图来评估线性回归是否适合预测数据。进行这种评估的另一种方法是使用残差图。残差图显示实际值和预测值之间的差异。如果残差图中的点随机分布在水平轴周围,则线性回归模型适用于该数据;否则,非线性模型更合适。
我们可以使用 Seaborn 创建残差图,如下所示:
****
正如我们所见,这些点随机分布在 0 附近,这意味着线性回归是预测我们数据的合适模型。如果残差图呈现曲率,则线性假设是不正确的。在这种情况下,非线性函数将更适合预测数据。
多元线性回归
****简单线性回归使用线性函数来预测目标变量 y 的值,该函数只包含一个自变量 x₁ 。
y =b ₀+b ₁x ₁
在将线性方程拟合到观测数据后,我们可以获得最符合数据的参数 b₀ 和 b₁ 的值,最小化平方误差。
之前,我们已经计算了两个线性模型,一个用于男性,另一个用于女性,以根据人的身高预测体重,获得了以下结果:
- *男性→体重=-224.50+5.96 身高
- *女性→体重=-246.01+5.99 身高
到目前为止,我们已经采用了一个独立变量来预测人的体重 【体重= f(身高) ,创建了两个不同的模型。也许你在想💭❓:我们可以用身高和性别作为独立变量建立一个预测体重的模型吗?答案是肯定的!😄⭐️,这就是多元线性回归发挥作用的地方!
****多元线性回归使用线性函数预测目标变量 y 的值,包含函数 n 自变量x=[x₁,x₂,x₃,…,xₙ】。
y = b ₀+b ₁x ₁+b₂x₂+b₃x₃+…+bₙxₙ****
我们使用与简单线性回归相同的技术(最小平方误差)获得参数 bᵢ 的值。拟合模型后,我们可以用方程预测目标变量 y 的值。在我们的例子中,我们用身高和性别来预测一个人的体重 体重= f(身高,性别)。
多元线性回归中的分类变量
统计学中使用的变量有两种:数值型和分类变量。
- ****数字变量代表可以测量并按升序和降序排序的值,如人的身高。
- ****分类变量是可以分组或分类的值,例如一个人的性别。
****多元线性回归不仅接受数值变量,还接受分类变量。要将分类变量包含在回归模型中,变量必须编码为二进制变量(虚拟变量)。在 Pandas 中,我们可以使用Pandas . get _ dummies函数轻松地将分类变量转换为哑变量。该函数返回一个伪编码数据,其中 1 表示分类变量存在,0 表示不存在。

为了避免多重共线性,我们必须删除其中一个虚拟列。

然后,我们可以使用这个数据框架,使用 Scikit-learn 获得一个多元线性回归模型。
在拟合线性方程后,我们得到以下多元线性回归模型:
- **体重=-244.9235+5.9769 身高+19.3777 性别
如果我们想预测一个男性的体重,性别值是 1,得到下面的等式:
- **男→体重=-244.9235+5.9769 身高+19.3777 * 1 =-225.5458+5.9769 身高
对于女性,性别值为 0。
- **女→体重=-244.9235+5.9769 身高+19.3777 * 0 =-244.9235+5.9769 身高
如果我们比较简单线性模型和多元线性模型,我们可以观察到类似的预测结果。多元线性回归模型的性别变量只改变直线的截距。🙌
关键要点
- ****简单线性回归是对一个因变量和一个自变量之间的关系进行建模的线性方法。
- ****多元线性回归使用线性函数来预测包含函数 n 自变量的因变量的值。
- ****探索性数据分析包括分析数据集的主要特征,通常采用可视化方法和汇总统计。
- ****直方图是显示数值变量分布的图表,将数据分组到箱中。
- Pandas 提供探索性数据分析的方法和函数,如 Dataframe.describe()、Dataframe.info()、Dataframe.dtypes 和 Dataframe.shape.
- ****散点图是二维数据可视化,显示两个数值变量之间的关系——一个沿 x 轴绘制,另一个沿 y 轴绘制。 Matplotlib 和 Seaborn 提供了绘制散点图的内置函数。
- 我们可以使用诸如 Numpy 或 Scikit-learn 之类的库来拟合一个简单线性回归模型。
- ****相关性衡量两个变量相关的程度。皮尔逊相关系数用于衡量强度和方向两个变量之间的线性关系。
- ****残差图可用于分析线性回归模型是否适用于数据。
- ****分类变量必须转换成虚拟变量才能在多元线性回归模型中使用。
感谢阅读🙌 😍 😉 🍀
使用 TensorFlow 2.0 的简单 BERT
在 15 行代码中使用 BERT 和 TensorFlow Hub。最后更新时间:2020 年 11 月 15 日。
这个故事展示了一个使用 TensorFlow 2.0 嵌入 BERT [1]的简单例子。最近发布了 TensorFlow 2.0,该模块旨在使用基于高级 Keras API 的简单易用的模型。伯特之前的用法在的长篇笔记本中有描述,它实现了一个电影评论预测。在这个故事中,我们将看到一个使用 Keras 和最新的 TensorFlow 和 TensorFlow Hub 模块的简单 BERT 嵌入生成器。
Google Colab 上新的更新版本在这里(2020–11–15)。老版本在这里有。
我之前的文章使用了[bert-embedding](https://pypi.org/project/bert-embedding/) 模块,使用预先训练的无案例 BERT 基本模型生成句子级和标记级嵌入。这里,我们将只通过几个步骤来实现这个模块的用法。

更新 2020 年 11 月 15 日:HUB 上的新模型版本 v3
对于 TensorFlow Hub 上的新模型版本 v3,它们包括一个预处理器模型来实现本故事中描述的步骤。Hub 版本还更改为使用字典输入和输出变量,因此如果您想以原始故事中描述的方式实现,请在使用较新的模型版本时考虑到这一点。
有了新版本,我们有 3 个步骤要遵循:1)从 TF、TF-Hub 和 TF-text 导入正确的模块和模型;2)将输入加载到预处理器模型中;3)将预处理的输入加载到 BERT 编码器中。
BERT with TensorFlow HUB — 15 lines of code (from the official HUB model example)
我没有更新 Colab,而是用上面的例子创建了一个新的笔记本。我把原始版本留在这里,因为我相信它有助于理解预处理器模型的步骤。使用不同版本时,请注意轮毂模型导入结束时的版本(/3)。
模块导入
我们将使用最新的 TensorFlow (2.0+)和 TensorFlow Hub (0.7+),因此,它可能需要在系统中进行升级。对于模型创建,我们使用高级 Keras API 模型类(新集成到 tf.keras 中)。
BERT 记号赋予器仍然来自 BERT python 模块(bert-for-tf2)。
Imports of the project
模型
我们将基于 TensorFlow Hub 上的示例实现一个模型。在这里,我们可以看到 bert_layer 可以像其他 Keras 层一样用于更复杂的模型。
该模型的目标是使用预训练的 BERT 来生成嵌入向量。因此,我们只需要 BERT 层所需的输入,并且模型只将 BERT 层作为隐藏层。当然,在 BERT 层内部,还有一个更复杂的架构。
hub.KerasLayer函数将预训练的模型作为 Keras 层导入。
BERT embedding model in Keras
预处理
BERT 层需要 3 个输入序列:
- 标记 id:针对句子中的每个标记。我们从伯特词汇词典中恢复它
- mask ids:for each token,用于屏蔽仅用于序列填充的标记(因此每个序列都具有相同的长度)。
- Segment ids:对于单句序列为 0,如果序列中有两个句子,并且是第二个,则为 1(更多信息请参见原始论文或 GitHub 上 BERT 的相应部分:
[run_classifier.py](https://github.com/google-research/bert/blob/master/run_classifier.py)中的convert_single_example)。
Functions to generate the input based on the tokens and the max sequence length
预言;预测;预告
通过这些步骤,我们可以为我们的句子生成 BERT 上下文化嵌入向量!不要忘记添加[CLS]和[SEP]分隔符标记以保持原始格式!
Bert Embedding Generator in use
作为句子级嵌入的混合嵌入
原始论文建议使用[CLS]分隔符作为整个句子的表示,因为每个句子都有一个[CLS]标记,并且因为它是一个上下文化的嵌入,所以它可以表示整个句子。在我之前的作品中,也是用这个 token 的嵌入作为句子级的表示。来自 TensorFlow Hub 的bert_layer返回一个不同的合并输出,用于表示整个输入序列。
为了比较这两种嵌入,我们使用余弦相似度。汇集嵌入和第一个标记嵌入在例句中的区别“这是一个好句子。”为 0.0276。
BERT 中的偏差
当有人使用预先训练好的模型时,调查它的缺点和优点是很重要的。模型就像数据集一样有偏差,因此,如果使用有偏差的预训练模型,新模型很可能会继承缺陷。如果你使用 BERT,我建议你阅读我关于 BERT 中偏差的帖子。
摘要
这个故事介绍了一个简单的、基于 Keras 的 TensorFlow 2.0 对 BERT 嵌入模型的使用。像阿尔伯特这样的其他模型也可以在 TensorFlow Hub 上找到。
这个故事的所有代码都可以在 Google Colab 上获得。
参考
[1] Devlin,j .,Chang,M. W .,Lee,k .,& Toutanova,K. (2018 年)。 Bert:用于语言理解的深度双向转换器的预训练。 arXiv 预印本 arXiv:1810.04805 。
用伯特的故事学习 NMT
- BLEU-BERT-y:比较句子得分
- 嵌入关系的可视化(word2vec,BERT)
- 机器翻译:简要概述
- 使用 BERT 识别单词的正确含义
- 机器翻译:对比 SOTA
- 使用 TensorFlow 2.0 的简单 BERT
使用 uTensor 在 MCU 上运行卷积神经网络的端到端教程
我试图在 uTensor 上展示 CNN,但在 2018 年的台湾 COSCUP 上没有成功。问题是,我不小心给所有的输入类输入了相同的图像,这和随机猜测一样好。在 CIFAR10 上,这产生了大约 10%准确度。尽管事实上分类器确实在工作!
这是 CNN 关于 uTensor 的背景故事。
在这篇文章中,我将指导你如何用 uTensor 构建一个 CNN。我们将使用utensor-cli将 Tensorflow 中训练的简单 CNN 无缝转换为等效的 uTensor 实现,在 MCU(具体来说是 STM32F767)上编译和运行。
如果你是 uTensor 的新手,在继续之前,你可能想读一读由 uTensor 核心开发者之一, Neil Tan 撰写的这篇 伟大的 MLP 教程 。
好了,我们开始吧!
获取演示代码
首先,克隆回购:
% git clone [https://github.com/uTensor/simple_cnn_tutorial.git](https://github.com/uTensor/simple_cnn_tutorial.git)
% cd simple_cnn_tutorial
接下来,通过运行以下命令来设置环境:
# setup a python virtual environment and activate it
% python2.7 -m virtualenv .venv
% source .venv/bin/activate# install mbed-cli and setup libraries
% pip install mbed-cli
% mbed deploy# install uTensor cli *after* setting up the libraries
% pip install utensor_cgen==0.3.3.dev2
安装完成后,如果没有其他错误,就可以开始了。
在回购中,有几个 python 脚本您可能想研究一下:
model.py是构建 CNN 图表的脚本。train.py是训练脚本。如果你想自己训练一个,你可以运行python train.py --help来查看所有可用的超参数。在本教程中,我们将使用一个预先训练好的 protobuf 文件,cifar10_cnn.pb。cifar10_cnn.pb是我们将在本教程中使用的预训练模型。prepare_test_data.py是一个 python 脚本,它将为您生成img_data.h,这是在 MCU 上运行测试的数据。
如果您想立即看到结果,您可以连接开发板并运行make compile。它应该编译和闪存图像到您的开发板。然后按照 Neil Tan 的教程中“获取输出”部分的说明进行操作。
从 Tensorflow 到 uTensor
用utensor-cli激活虚拟环境。我们将首先识别输出节点,然后从模型中生成 C++文件。
检查图形并找到输出节点:utensor-cli show <model.pb>
该命令显示给定模型文件中的所有操作/节点。例如,如果您运行utensor-cli show cifar10_cnn.pb --oneline,您将在终端上看到以下内容:

如您所见,该命令打印出给定cifar10_cnn.pb中的所有图节点。
当我们想将 protobuf 文件转换成 uTensor C++文件时,需要提供一个带有--output-nodes选项的输出节点列表。与其他工具如tensorboard相比,show命令对于探索模型是有用的和轻量级的。
在cifar10_cnn.pb中,fully_connect_2/logits就是我们要找的。它是这个模型中的输出节点。
然后,使用convert命令将文件转换成 C++文件:
% utensor-cli convert cifar10_cnn.pb --output-nodes \
fully_connect_2/logits
您将在控制台上看到:

此时,您应该在models目录中找到您的模型的 uTensor 实现,它是由utensor-cli生成的。

最后,通过运行以下命令生成一批测试图像:
% python prepare_test_data.py # generate testing image batch

它只需下载 cifar10 数据集,并随机生成一批图像进行测试。您可以在img_data.h中看到像素值(标准化为[0,1])。
看,美国有线电视新闻网在 MCU 上
在main.cpp中,您可以看到如何使用 cli 生成的模型文件。简要总结:
- 创建一个
Context对象 - 在本教程中,使用 cli 生成的助手函数构建神经网络。
- 获取您想要用于推理的张量。
- 评估神经网络。
注意uTensor将在评估后清除参考计数为 0 的张量。如果你在神经网络评估后得到张量,你会得到一个错误或悬空指针问题。
好了,编码的东西够多了。有趣的部分来了,我们准备在 MCU 上编译和运行 CNN。
首先,将开发板连接到计算机上:

My NUCLEO-F767ZI
接下来,运行:
% mbed compile -m auto -t GCC_ARM --profile \
uTensor/build_profile/release.json -f
mbed将自动检测您的开发板型号,带有-m auto标志。- 使用概要文件构建二进制文件,在本教程中为
release.json。 - 并将二进制映像闪存到您的主板,并启用
-f标志。
如果一切顺利,您应该会看到:

可能需要一段时间才能完成,耐心一点就好;)
为了看到我的 NUCLEO-F767ZI 的串行输出,我使用了 CoolTerm(你可以从这里下载)。按照尼尔·谭的教程中所述设置 CoolTerm,并按下重置按钮,您应该会看到:

精确度可能因您使用prepare_test_data.py生成的随机图像批次而异。如果你这一批运气不好,就试试另一批:P
恭喜你。我们的 CNN 在 MCU 上直播。
下一步是什么
助理团队 非常活跃,试图将数据科学带入边缘计算领域。
在本教程中,我们提供了一个展示案例,您可以使用 uTensor 和其他神经网络框架(如 Tensorflow)进行端到端应用。
如果你是 PyTorch 的忠实粉丝,想知道 PyTorch 是否在 uTensor 的路线图上,不要担心。我也是 PyTorch 的粉丝,我们的团队正在努力让 PyTorch 与 Tensor 集成,就像我们与 Tensorflow 集成一样。我们正在取得进展,但欢迎你加入。
你可以在https://github.com/uTensor找到代码,有两个回购你应该先通过:
- 助手:c++运行时
- ❤用 python 写的 uTensor CLI
最后,特别感谢尼尔·谭和 卡扎米给我的有用建议和帮助我调试本教程。
运输中的简单复杂性

Photo by @vidarnm
当人和技术比想象的更容易时
当一列载有 200 多名乘客的火车发生故障时,科技和人类如何合作解决这一问题?一个封闭的问题,因为我今天经历了一系列的延误和故障,以及它是如何处理的第一手资料。
9 月 15 日早些时候,一棵树倒在了挪威克里斯蒂安桑和奥斯陆之间的铁轨上。那是一个星期天,当我登上火车时,列车员看起来相对乐观。一列火车试图通过,但运气不佳,但故障可能会被修复。事实证明并非如此。
火车在克里斯蒂安桑和奥斯陆之间行驶了一半后,在一个偏僻的小站停了下来。在挪威寒冷的秋天,这个车站只有一个 20-30 平方米的房间,外面大约有 200 人。你可以听到人们的窃窃私语,被自动语音信息告知信号故障或延迟时间。
显然,火车公司没有可用的公共汽车,因为他们已将此分包给全国其他较小的供应商。然而,这些分包商没有义务交付货物,运营车站和轨道的公司与拥有列车的公司之间存在混淆。
我试图与工作人员交谈,但他们说这是另一家公司的过错,他们是来照顾车站的,而不是火车或乘客。我决定打电话给服务热线,然而那里的人对的了解甚至比车站宣布的延误或更新还要少。支持公司似乎是一个不同的公司,那些运行列车或负责车站的人。三家公司:Vy 随火车;BaneNor 与车站;和客服人员一起工作。
我与之交谈的支持公司也不接受任何反馈,这是根据我与之交谈的人不负责任的说法,因为这可能会令人困惑。我提到,如果他们在紧急情况下准备好司机会很好,她说她无法记下这一点或通过她的主管。我提到有孩子和老人没有吃东西。我遇到过一个老妇人,她在去医院检查她认为很紧急的东西的路上。她仍然说,她不能做任何事情来帮助或通知任何人,她只是在客户服务中,对现场的情况没有控制权。
我想联系的支持公司等了 40 分钟才联系上。人声 40 分钟。然后在我们的谈话中,支持部门的人让我和她的主管谈几秒钟,随后就挂断了电话,没有给我回电话。我又打了一次电话,不得不排了 30 分钟的队,直到再次与同一个人通话。她没有进一步的消息。
在火车被困在一个不知名的地方四个小时后,我开始想知道这些公司在做什么,他们如何关心或者他们是否关心。晚上晚些时候,一个人来了,他们设法叫了几辆出租车,我很幸运地得到了一辆出租车,当然是在有孩子或生病的家庭被优先考虑之后。
这种情况让我对那些委派责任却无人负责的公司产生了怀疑。隔壁放了一个警卫室,谁都不让进,200 人冷天。监视器上的机器人声音。有人在去医院的路上等着;没有食物的儿童;无处可坐或休息的老年人。没有人走到人们身边寻求安慰或提供信息,一切都是通过扬声器传达的,带有相互矛盾的信息和大量的自动装置。甚至有一条自动消息发给所有的乘客,这条消息是错误的,因为错过了更新或出现了小故障,每个人都跑出去重新进去。
人在回路中
人们已经广泛讨论过,人工智能领域无法在不断变化的环境中完成需要常识的简单任务。强化学习可以适应设定的环境(如游戏或某些房间或模式),如果提供足够的计算能力或数据,或许还可以适应变化的环境。虽然我怀疑火车公司使用强化学习。
在大多数情况下,人工智能无法像人类那样“关心”,只能以这种方式表达关心行为。奇怪的是,在这种情况下建立起来的系统并不允许回路中的人按照规定或内部期望的方式行事。因此,即使我们确实有人类在循环中,它也不需要一种人道的方式来处理给定的情况。
即使消息已经在特定等待时间过去时自动发送到该区域的所有出租车公司(如果等待时间> 3 小时,则发送出租车),他们甚至可以更早地决定修理公共汽车。这看起来确实像是一种节约措施,或者是在这种情况下减少开支的优化措施。人们必须主动申请食物、出租车或任何其他可能不会立即得到支付的费用——这些费用不是免费提供的。有些人还打电话给自己的家人或朋友来接他们。很少有人能幸运地找到人来接他们,或者指望他们买票的公司负责运输。
如果自动响应对公司来说是最低成本的途径,而对试图在全国范围内移动的公民来说是最高成本的途径,那么即使涉及到算法,这也有些问题。如果让员工为这些类型的紧急情况做好准备成本太高,并且打赌一些人会懒得报销费用,这似乎是一种不负责任的赌注,也是危险的,在某些情况下可能是致命的。那里有些人付不起去奥斯陆的出租车费,要等一段时间才有希望收回这笔费用。
在支持中也没有反馈系统,只是在另一端的一个人试图用一些基本信息来安抚你,这些信息甚至可能不够新,没有多大关系。世界上没有任何算法可以帮助错误优化的情况,或者当没有/很少准备处理这种情况时。
它没有针对系统故障的合理响应时间进行优化。我知道在其他领域可能会更难,而且有更大的问题,但考虑我们为哪些其他任务进行优化将是令人着迷的。在某些情况下,过程自动化和预测性维护可能不是最佳的。这取决于那些在这样的条件下定义变量或语句的人。如果是冬天会不会快一点?什么时候应该预料不到的事情?
200 个人在零下的温度下,看不到公共汽车,也没有食物,与这里的情况形成鲜明对比的是,秋天的时候几乎是一样的。我们针对速度或成本进行优化,在英特尔机器中,这可能会导致推测性执行的硬件漏洞。速度比安全更重要,他们希望很少有人会注意到这一点。
在这种情况下,乘坐火车的成本似乎在表面上是一个支配性的因素,以及关于不同行为者的责任或参与的混乱。也许更多的是,一种情况是多么不人性化,更加自动化,信息更少,更加反动。即使是不太像人类的人——疏远或缺乏理解或想要理解在场的人的表现。
积极的一面
更积极的一面是,我坐在等候室的地上,通过游戏编程英雄在手机上学习了更多的 Python 编程。这让我想到了编程在这种情况下能有什么帮助,但显然也有什么是它不能或不能帮助的。当一列火车发生故障时,我想要的不仅仅是一条自动信息和当时不在场的人。在互联网上为公民服务的基础设施出现类似的故障,比如我在火车上经历的那次,这可能不是不可想象的。当基础设施出现故障时,您希望如何处理?
这是第 500 天的第 104 天。我目前第 101-200 天的重点主要是 Python 编程。如果你喜欢这篇文章,请给我一个答复,因为我确实想提高我的写作或发现新的研究,公司和项目。
使用 SQL 进行简单的数据分析

What lies beneath : like the water in this peaceful river, your database can conceal any number of problems. (photo: Author)
在开始之前,了解数据的真实状态。
许多组织将其数据存储在符合 SQL 的数据库中。至少在生命周期的一部分中,这些数据库是有机增长的,这并不是闻所未闻的,所以表可能包含也可能不包含它们的标签会让您认为它们包含的内容,并且质量可能符合也可能不符合您的期望并对您的应用程序有用。
无论如何,创建数据库的最初目的很少是为数据科学家创建一个方便的、结构良好的环境来使用分析基础。因此,尽管您渴望将数据迁移到 R 或 Python 中进行分析,并有望用它来构建模型,但是您需要弄清楚哪些数据是您所需要的,哪些数据满足数据质量的基本标准,从而使其值得使用。
确实,主要的数据库包都有专门用于数据分析的工具。然而,同样值得注意的是,这些工具学习起来很复杂,有时看起来像是敲碎核桃的大锤。真正需要的是一些能够快速完成工作的简单查询。
在本系列的早期文章中,我们研究了 SQL 中的分位数,以及 SQL 中缺失值的探索性统计。这一次,我们将着眼于识别独特的价值,并探索它们出现的频率。
唯一值本身并不难识别— SQL 有一个内置的关键字 DISTINCT,它将返回的行限制为具有唯一值的行。因此,通过创建查询 SELECT DISTINCT col _ name from Table 并查看返回了多少行来发现任何行的值的数量是很容易的。
简单地知道特定列中唯一值的数量有点用处,但可能不如知道变量是分类的情况下每个值出现的次数有用(如果您事先不知道,大多数值的多次出现可能是这是分类值的强烈信号)。
这也相对简单,因为您可以将 DISTINCT 等关键字放入 COUNT 等聚合的括号中。因此
select count(DISTINCT x) from tablegroup by x
更有用的是知道唯一变量的比例。如果字段的标签很差,并且您需要仔细检查哪些列可能是唯一 id,这可能会很有帮助,同时也可以避免将表示简单标识符的字段误认为是包含信息的字段。不要忘记,由于 COUNT 返回一个整数,您需要转换结果以获得一个分数:
select cast(count(DISTINCT x) as decimal)/count(X)
from table
对于分类变量,理解唯一值的数量是理解整个数据集的总体有用性的重要一步。
罗伯特·德格拉夫的书《管理你的数据科学项目》》已经通过出版社出版。
python 中 2D 密度图的简单示例
如何可视化联合分布

这篇文章将向你展示如何:
- 使用高斯核估计两个分布的概率密度函数
- 使用 Matplotlib 用密度图周围带标签的等高线来表示 PDF
- 如何提取等高线
- 如何在 3D 中绘制上面的高斯核
- 如何使用 2D 直方图绘制相同的 PDF
让我们从生成包含 3 个斑点的输入数据集开始:
import numpy as np
import matplotlib.pyplot as plt
import scipy.stats as st
from sklearn.datasets.samples_generator import make_blobsn_components = 3
X, truth = make_blobs(n_samples=300, centers=n_components,
cluster_std = [2, 1.5, 1],
random_state=42)plt.scatter(X[:, 0], X[:, 1], s=50, c = truth)
plt.title(f"Example of a mixture of {n_components} distributions")
plt.xlabel("x")
plt.ylabel("y");

为了拟合高斯核,我们指定一个网格,该网格将在每个轴上使用 100 点插值(例如,mgrid(xmin:xmax:100j)):
# Extract x and y
x = X[:, 0]
y = X[:, 1]# Define the borders
deltaX = (max(x) - min(x))/10
deltaY = (max(y) - min(y))/10xmin = min(x) - deltaX
xmax = max(x) + deltaXymin = min(y) - deltaY
ymax = max(y) + deltaYprint(xmin, xmax, ymin, ymax)# Create meshgrid
xx, yy = np.mgrid[xmin:xmax:100j, ymin:ymax:100j]
我们将使用 scipy 的 gaussian_kde 方法来拟合高斯内核:
positions = np.vstack([xx.ravel(), yy.ravel()])
values = np.vstack([x, y])
kernel = st.gaussian_kde(values)
f = np.reshape(kernel(positions).T, xx.shape)
用带注释的轮廓绘制内核
fig = plt.figure(figsize=(8,8))
ax = fig.gca()
ax.set_xlim(xmin, xmax)
ax.set_ylim(ymin, ymax)
cfset = ax.contourf(xx, yy, f, cmap='coolwarm')
ax.imshow(np.rot90(f), cmap='coolwarm', extent=[xmin, xmax, ymin, ymax])
cset = ax.contour(xx, yy, f, colors='k')
ax.clabel(cset, inline=1, fontsize=10)
ax.set_xlabel('X')
ax.set_ylabel('Y')
plt.title('2D Gaussian Kernel density estimation')

完成整个魔术的 matplotlib 对象被称为 QuadContour set(代码中的 cset )。我们可以通过迭代 allsegs 对象来编程访问轮廓线。计算标签可从标签文本访问。
plt.figure(figsize=(8,8))for j in range(len(cset.allsegs)):
for ii, seg in enumerate(cset.allsegs[j]):
plt.plot(seg[:,0], seg[:,1], '.-', label=f'Cluster{j}, level{ii}')plt.legend()

三维 KDE 图
我们将使用来自 mplot3d 的 matplotlib 的 axes3d。我们可以将密度绘制成一个表面:
fig = plt.figure(figsize=(13, 7))
ax = plt.axes(projection='3d')
surf = ax.plot_surface(xx, yy, f, rstride=1, cstride=1, cmap='coolwarm', edgecolor='none')
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('PDF')
ax.set_title('Surface plot of Gaussian 2D KDE')
fig.colorbar(surf, shrink=0.5, aspect=5) # add color bar indicating the PDF
ax.view_init(60, 35)

或者作为线框:
fig = plt.figure(figsize=(13, 7))
ax = plt.axes(projection='3d')
w = ax.plot_wireframe(xx, yy, f)
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('PDF')
ax.set_title('Wireframe plot of Gaussian 2D KDE');

使用 2D 直方图的表示法
呈现相同信息的另一种方式是使用 2D 直方图。将参数 normed 设置为 False 将返回实际频率,而设置为 True 将返回 PDF。
h =plt.hist2d(x, y)
plt.colorbar(h[3])

完整的代码可以在 Github 上获得。
简单的探索性数据分析:基于西雅图邻里特征的房地产列表(Airbnb 数据)
你好,数据人员!这是我的第一个媒体帖子。在这第一篇文章中,让我们看看如何利用简单的 EDA(探索性数据分析)从数据中提取有用的初步见解。对于数据或分析领域的新手来说,通常首先想到的是机器学习或复杂的统计建模。事实上,根据我过去几年的经验,在我们进行的每次分析中,并不总是强制执行一个花哨的统计模型或机器学习技术。我们可以只使用简单的 EDA 结合良好的领域知识。

Photo by Luca Bravo on Unsplash
首先,了解什么是 EDA 是有好处的。根据维基百科😊、探索性数据分析 ( EDA )是一种对 数据集进行分析以总结其主要特征的方法,往往采用可视化的方法。一个统计模型可以使用也可以不使用,但是 EDA 主要是为了看看数据能告诉我们什么,而不仅仅是正式的建模或假设检验任务。简而言之,EDA 通常在分析的早期阶段进行,以便为我们提供数据的主要思想,有进一步的假设,并可能导致新的数据收集和实验。单独使用 EDA 并呈现我们的发现也是可以的(就像本文中的分析一样!).
数据源和上下文
我们可以从 这个 Kaggle 链接 得到 Airbnb 提供的数据集。数据集由 3 个表组成(列表、日历和评论)。

Data description

‘listings’ table consists of detailed information of each listing (e.g. listing id, url, posted time, listing description, neighborhood description, normal price, location coordinate, etc)

‘calendar’ table. The availability of each listing in particular date and the price within that day
概述:
- Airbnb 是一个提供寄宿家庭或旅游服务的在线市场。它充当东道主的经纪人/聚合人,并从每笔预订中收取佣金。
- 西雅图是美国西海岸的一个海港城市,截至 2018 年有 73 万居民。
在这篇文章中,让我们关注“西雅图每个社区的氛围”。

Photo by Thom Milkovic on Unsplash
调查结果
国会山是 Airbnb 列出的房产可用性最高的社区。它的上市量甚至比上市量第二高的 Ballard 高出 65%。
国会山、贝尔敦和第一山的房源以公寓为主,而巴拉德、米纳尔和沃灵福德的房源大多是房子。安妮女王由一个更加平衡的房子和公寓组合而成。

挂牌价格最高且集中在相对较近区域的三个主要社区是派克市场、贝尔敦和中央商务区。

My Tableau, unfortunately, wasn’t able to connect and overlap the plot to the real map 😦 So let’s just display the snapshot from Gmaps side by side instead 😃
在“列表”表中有“邻居概述”列,在该列中,我们可以获得列表者/物业所有者给出的关于邻居的概述。

neighborhood overview column example
使用邻居概况数据,三个邻居共享或多或少相同的关键词:“中心”、“市场”、“市中心”、“游客”、“博物馆”、“空间”、“博物馆”、“步行”。很明显,派克市场、中央商务区和贝尔敦是西雅图最受欢迎的地区,因为它们位于西雅图市中心、市场/贸易中心,并且靠近旅游景点。因此,这个地区的房租价格较高。

通过计算价格差/距离比,存在一些提供更便宜的挂牌价格但靠近市中心的社区。第一山、国会山和东湖是价格差/距离比最高的社区,因此是西雅图推荐的租房社区。

参观这些街区最繁忙的时间是一年中的五月到八月,而岁末年初是最空闲的时候。

价格不像供货情况那样波动。不过由于最忙的时候是年中,所以最高价也在 6-8 月这个区间。

关键要点
- 国会山的上市数量最多,其次是巴拉德和贝尔敦。国会山、贝尔敦和第一山以公寓为主,而巴拉德、米纳尔和沃灵福德以住宅为主
2.挂牌价格最高且集中在相对较近区域的三个主要社区是派克市场、贝尔敦和中央商务区
3.派克市场、中央商务区和贝尔敦是西雅图最受欢迎的地区,因为它们位于西雅图市中心、市场/贸易中心,并且靠近旅游景点。因此,这个地区的房产租金价格较高
4.第一山、国会山和东湖是最值得推荐的社区,因为它们的挂牌价格更便宜,而且靠近市中心
5.每年 5 月至 8 月是推荐区域租赁物业的最繁忙时间,年末和年初是最空闲的时间
6.价格不像供货情况那样波动。然而,由于最繁忙的时间是在年中,最高价格也在 6 月至 8 月的范围内
附录
工具:
1.用于数据查询和处理的 SQL 和 Excel
2.可视化的 Tableau 和 Thinkcell
3.wordclouds.com 制造单词云
—我很乐意讨论关于分析的进一步想法,请通过https://www.linkedin.com/in/muhammad-lutfi-hernandi/联系我😄 —
—干杯—
时间序列预测的简单指数平滑法
简单指数平滑法是预测时间序列的一种简单而强大的方法。此外,它还被许多其他模型用作构建模块。让我们看看它是如何工作的。
本文摘自我的著作 供应链预测的数据科学 。你可以在这里 阅读我的其他文章 。我也活跃在LinkedIn。

想法
简单的指数平滑法是预测时间序列最简单的方法之一。该模型的基本思想是假设未来将或多或少与(最近的)过去相同。因此,该模型将从需求历史中学习的唯一模式是其水平(你可以在我的博客或我的书中了解更复杂的模型)。
该水平是需求随时间变化的平均值。如下图所示,该水平是需求的平滑版本。

然后,指数平滑模型将预测未来需求,作为其对水平的最后估计。重要的是要明白,没有明确的数学定义的水平。相反,由我们的模型来估计它。
与更简单的预测模型(如简单模型或移动平均模型)相比,指数平滑模型具有一些优势:
- 随着时间的推移,每个观察值的权重呈指数级下降(最近的观察值权重最高)。这通常比移动平均模型更好,移动平均模型对所有相关的历史月份分配相同的权重。
- 离群值和噪声的影响比简单方法小。
模型
指数平滑模型的基本思想是,在每个时期,模型将从最近的需求观察中学习一点,并记住它所做的最后一次预测。神奇的是,模型填充的最后一个预测已经包含了一部分先前的需求观察和一部分先前的预测。诸如此类。这意味着之前的预测包括了模型基于需求历史到目前为止了解到的所有信息。平滑参数(或学习率)α将决定给予最近的需求观察多少重要性。让我们用数学来表示它,

这个公式背后的直觉是什么?
- alpha 是相对于需求历史的重要性,模型将分配给最近观察的重要性的比率(或百分比)。
- alpha d{t-1} 表示之前的需求观察乘以学习率。您可以说,该模型将某个权重(alpha)附加到最后一次需求发生上。
- (1-alpha) f{t-1} 表示模型对其先前预测的记忆程度。注意,这就是递归魔术发生的地方,因为 f{t-1}本身被定义为部分 d{t-2}和 f{t-2}。
这里需要在学习和记忆反应性和稳定性之间做出必要的权衡。如果 alpha 较高,则模型将分配更多的重要性给最近的需求观察(即,模型将快速学习),并且它将对需求水平的变化做出反应。但是它对异常值和噪声也很敏感。另一方面,如果 alpha 较低,模型不会注意到级别的快速变化,但也不会对噪声和异常值反应过度。
未来预测
一旦我们离开了历史时期,我们需要填充未来时期的预测。这很简单:上一次预测(基于最近需求观察的预测)只是对未来的推断。如果我们根据需求历史将 f{t*}定义为我们可以做出的最后一个预测,我们只需

模型初始化
与每个模型一样,问题来自于第一次预测的初始化。不幸的是,这个简单的问题没有简单的答案。在本书中经常会出现这种情况:最简单的问题并不总是有明确和绝对的答案。正如我们将反复讨论的那样,只有实验才能让我们理解哪种技术最适合每个数据集。我们来讨论一些想法。
简单初始化 —我们将第一个预测(期间 0)初始化为第一个需求观察。然后我们有

这是初始化预测的一种公平而直接的方式。
平均值 —我们将预测初始化为第一个 n 个需求发生的平均值。

在这种情况下,我建议测试不同的 n 值。它可以设置为一个固定的小值(3 到 5),也可以设置为学习率的倒数(1/alpha)。如果 n 被设置为学习率的倒数,这允许随着学习率降低而更平滑地估计 f_0。这是有意义的,因为 alpha 的低值意味着我们希望我们的模型对变化做出平滑的反应。
数据泄露
如果您选择的初始化方法包括关于未来多个期间的信息,例如,如果您将初始预测定义为前五个期间的平均值,您将面临数据泄漏。这意味着你为你的模型提供了关于未来的信息。基本上你告诉它:“你能给我提供一个下期的预测吗,知道未来五期的需求平均值是 10?”。这是一个典型的过度拟合的例子:该模型将为您提供初始阶段的良好预测准确性(这很简单,您给了它这些阶段的平均需求!)但未来将无法复制这样的准确性。
当你初始化一个模型的不同参数时,一定要小心,不要给它太多关于未来的信息。
洞察力
阿尔法的影响
下图显示,使用低 alpha 值(此处为 0.1)进行的预测将需要更多时间来对不断变化的需求做出反应,而使用高 alpha 值(此处为 0.8)进行的预测将密切跟踪需求波动。

为什么叫指数平滑?
该模型被称为指数平滑,因为给予每个需求观察的权重是指数减少的。为了说明这一点,我们将从回顾指数平滑模型开始。

如您所见,给予最近需求观察 d{t-1}的权重是α。现在让我们用公式来代替 f{t-1}。

如果我们做一点代数运算,我们会得到下面的公式:

我们看到,给予第二个最近的需求观察值 d{t-2}的权重是α(1-α),低于给予 d{t-1}的重要性。
让我们更进一步,用它的公式代替 f{t-2}。

我们看到给 d{t-3}的权重是 alpha(1-alpha),是给 d{t-2}的权重乘以(1-alpha)。从这里,我们推断出,给予每个进一步的需求观察的权重减少了一个因子(1-α)。这就是为什么我们称这种方法为指数平滑。

限制
这个简单的指数平滑模型比移动平均模型稍微聪明一点,因为它对历史需求观察的加权更聪明。但是它有许多限制:
- 它不预测趋势。我们将通过我们的下一个模型来解决这个问题:趋势指数平滑,也称为双指数平滑。
- 它不承认任何季节性模式。我们将使用三重指数平滑模型来解决这个问题。
- 它不能使用任何外部信息(如定价或营销费用)。
总之,第一个指数平滑模型很可能过于简单,无法获得好的结果,但它是以后创建更复杂模型的良好基础。
霍尔特-温特斯模型简史
指数平滑模型通常被称为“霍尔特-温特斯”,这是基于提出这些模型的研究人员的名字。指数平滑预报的早期形式最初是由布朗于 1956 年提出的。1957 年,来自麻省理工学院和芝加哥大学的美国工程师 Charles C. Holt 在他的论文“通过指数加权平均值预测趋势和季节”中完善了他的方程三年后,彼得·温特斯再次改进了指数平滑模型。他们的两个名字被记住,并被赋予不同的指数平滑技术,我们有时称之为“霍尔特-温特斯”**
Holt & Winters 提出了不同的指数平滑模型(简单、双重和三重),这些模型也可以理解和预测趋势或季节性。这种模型集合对于预测任何时间序列都是相当稳健的。而且,正如霍尔特和温特斯在 1960 年已经解释过的,这些预测只需要适度使用计算能力。
自己做
可以在 Excel ( 此处)或者 Python ( 此处)中做自己简单的指数平滑。
关于作者
[## Nicolas vande put——顾问,创始人——供应链| LinkedIn
查看 Nicolas Vandeput 在世界上最大的职业社区 LinkedIn 上的个人资料。尼古拉斯有 7 份工作列在…
www.linkedin.com](https://www.linkedin.com/in/vandeputnicolas/)
icolas Vandeput 是一名供应链数据科学家,擅长需求预测和库存优化。他在 2016 年创立了自己的咨询公司 SupChains ,并在 2018 年共同创立了 SKU 科学——一个快速、简单、实惠的需求预测平台。尼古拉斯对教育充满热情,他既是一个狂热的学习者,也喜欢在大学教学:自 2014 年以来,他一直在比利时布鲁塞尔为硕士学生教授预测和库存优化。自 2020 年以来,他还在法国巴黎的 CentraleSupelec 教授这两个科目。他于 2018 年出版了 供应链预测的数据科学(2021 年第 2 版)和 2020 年出版了 库存优化:模型与模拟 。

用熊猫探索简单的足球数据集
从 1872 年到 2019 年的国际足球结果

Image Courtesy of Syed Ali via Unsplash
熊猫的简单可视化——一个足球的例子
这篇教程文章详细介绍了如何使用 Python Pandas 库高效地探索数据集。具体来说,这个例子将使用数据集,从 1872 年到 2019 年的国际足球结果,可从 Kaggle 网站获得。首先,本教程将探索简单的可视化,以及如何过滤它们以允许更细粒度的探索。
此外,本教程旨在展示如何使用熊猫来回答数据科学相关的问题。

The Data-set is available by following the link attached, and has records for more than 40 thousand international football results.
画出每年国际比赛的次数?
首先,除了 matplotlib 库允许探索性的数据可视化之外,还需要导入 Pandas 库用于数据分析。我读入保存在本地目录中的 CSV 文件,并将其保存到名为 results 的数据帧中。接下来,我使用 head 方法查看结果数据帧的前 5 条记录。数据帧中的每一行代表一场国际足球比赛。为了帮助自己熟悉数据集,我还查看了列的数据类型。

To familiarize myself with the Dataframe, I check the number of rows and columns using the shape attribute, and check the datatype of the columns.
要计算和可视化每年发生的比赛数量,一个好的起点是查看日期列,因为它有每场比赛的日期。如前所示,日期列有一个对象数据类型。为了方便地从这个日期列中提取年份,只需对结果数据帧中的日期序列使用 pd.to_datetime 方法,并创建一个新列。这里,我创建的新列名为“date_time ”,现在具有 datetime 数据类型。

通过使用“dt”名称空间,可以提取诸如 year 之类的方便的属性,并且可以应用 value_counts 方法。为了绘图,必须对索引(显示在左侧的年份)进行排序。

通过简单的写作。情节,情节现在可以可视化和国际比赛的数量显示。
虽然这个情节当然可以使用一些视觉上的增强,一个简单的熊猫单句就能够传达每年发生的比赛数量!

The default for plot() is line. A line plot type is ideally suited for time-series data.
过滤时间序列以获得更精细的细节
看看更小范围的国际比赛也是很有见地的。时间序列列 date_time 可以使用 Pandas 一行程序进行过滤。在所示示例中,仅显示了 2014 年至 2019 年的国际比赛。
值得注意的是,2019 年的国际比赛似乎有所下降。然而,经过仔细检查,date_time 序列中最后记录的日期是在 2019 年年中,如对 date_time 序列使用 max()方法所示。这可能解释了 2019 年国际比赛突然明显下降的原因。
提示:我们可以很容易地使用多个条件语句过滤日期范围,如注释掉的第一行所示。有趣的是,我们还可以使用 between 方法,通过指定 left=和 right=关键字参数的值来选择和提取年份范围。注意,指定的两个值都包含在内。

Filtering using conditionals or the between method can permit more fined grained details to be visualized.
哪种锦标赛最激动人心?
使用数据集中表现良好的数据!
最激动人心的国际比赛可以用很多方式来定义,但其中一种方式可能是调查进球!为了决定哪一场比赛是最激动人心的,我创建了一个新的列,使用下面显示的代码片段计算每场国际比赛的比赛进球数。为了确认已经创建了该列,我从结果数据帧中抽取了三列,即 home_score、away_score 和 match_goals,并从结果数据帧中随机抽取 5 条记录进行验证。

对于每场锦标赛,我现在可以使用 Pandas groupby 来计算平均比赛进球数,并根据这一指标确定最激动人心的锦标赛。
然而,我必须注意要有合理的样本量,这样结果才有意义。在这个演示中,我将只包括数据帧中有 100 个或更多记录(国际比赛)的锦标赛。
我在结果数据帧的锦标赛系列中使用了 value_counts 方法。该命令返回一个新的系列,其中锦标赛是索引,值是特定锦标赛在数据帧中出现的次数。

我将这个系列保存到一个名为 tournament_count 的变量中,并过滤这个系列,使每个锦标赛只包含 100 个或更多的记录。然后,我选择索引,并使用 isin()方法调用过滤结果数据帧,这将返回一个新的数据帧,该数据帧现在只包括在数据集中出现 100 次或更多次的锦标赛!

现在,我在过滤后的数据框架中对每场锦标赛进行分组,查看每场锦标赛的记录数量(使用计数),并根据平均比赛进球数进行排序。我看着数据帧的尾部,因为排序是按升序进行的。
结果显示,“南太平洋运动会”平均每场比赛有 5.9 个进球,并且在过滤的数据帧中出现了 205 次,表示为 _tour。
进球显然不是决定哪场比赛最激动人心的唯一指标,数据集中缺少像射门次数、点球失误、犯规和所有其他增加比赛价值的因素,这是一个明显的弱点。
提示!要可视化特定的锦标赛,请使用接受列表的 filter 方法。

kind=’barh’ plots a horizontal bar plot, which is useful to differentiate between categories.
使用。过滤方法

过去 5 年的锦标赛次数
这个问题再次展示了熊猫在帮助回答与数据科学相关的问题方面有多么有用。
首先,我过滤结果数据框架,只包括 2015 年以后的数据(过去 5 年)。然后,通过从 date_time 列(具有 datetime 数据类型)中提取 year 属性,对每年进行分组。对于过滤后的数据帧中的每一年,我对锦标赛执行 value_counts,返回一个多系列索引,我使用 unstack 方法将它转换为数据帧。
最后,为了防止输出中出现大量的 nan,我用 0 填充它们。例如,国际足联世界杯每 4 年举行一次,因此每 4 年只有一次有人居住的场地。我将这个输出保存到一个名为“last_5_years”的新数据帧中,并提取我在我的旅程列表中指定的列。
现在我们可以清楚地看到,2018 年有 64 场世界杯比赛,但我们也可以看到一些有趣的关系,例如在 2019 年,非洲国家杯发生了 52 场比赛,这与 2015 年和 2017 年发生的 32 场比赛有所偏差。
用于调查过去 5 年比赛的相关代码包含在 github gist 中,如下图所示。

这与我的最后一个问题有关,自从非洲国家杯(AFCON)成立以来,已经举行了多少场比赛?
绘制非洲杯比赛开始以来的比赛次数
一幅图将有效而清晰地呈现这些数据。我只是通过出现“非洲杯”的锦标赛系列来过滤结果数据帧,并对每一年的锦标赛进行数值计算,然后使用 unstack 返回数据帧。然后,我从 afcon 数据框架中提取“非洲杯”列,并使用柱状图进行绘制。
名词(noun 的缩写)B I 不得不提取“非洲杯”列,以获得一个系列,我可以在那里绘制指数(x 轴上的年数),y 轴上的比赛数量。

现在,每个 Afcon 的游戏数量已经显示出来。快速的在线搜索显示,在最近的 2019 年 Afcon 锦标赛中,比赛数量确实有所增加。
摘要
熊猫可以直观地用来回答数据科学问题。所展示的从 1872 年到 2019 年的国际足球比赛结果提供了一个很好的用例来展示如何实现这一点。
集成学习方法简单指南
什么,为什么,如何和装袋-推进去神秘化,解释,而非传统,读下去:)

在这个帖子之前,我已经发表了一个关于偏差方差权衡、过度拟合和欠拟合的圣杯。如果你是一个新手,或者只是想在全力投入整体建模的海洋之前温习一下偏差和方差的概念,这篇全面的文章可以作为这篇文章的重要前传。观众中的所有其他人可以很容易地从我的笔中了解更多关于整体造型的知识。我将引用一些现实生活中的例子来简化 集合模型的概念,重点是装袋和助推技术 。

场景 1: 你需要一副新耳机。现在,你有可能走进一家商店,购买销售人员向你展示的耳机吗?在这个时代,我确信答案是否定的,因为现在我们在购买任何东西之前都非常依赖我们的研究。你可以浏览一些网络技术门户网站,查看用户评论,然后比较你感兴趣的不同型号,同时检查它们的功能和价格。你也可能会询问你的朋友和同事的意见。简而言之,你不会直接得出结论,而是在彻底研究之后做出明智的决定。
现在,可以看看集成学习方法的技术定义。

什么是系综法?
机器学习中的集成模型将来自多个模型的决策进行组合,以提高整体性能。他们的操作思路与购买耳机时相似。
学习模型中误差的主要原因是由于噪声、偏差和方差。
集合方法有助于最小化这些因素。这些方法旨在提高机器学习算法的稳定性和准确性。
场景二:假设你开发了一款健康健身 app。在将其公之于众之前,您希望收到重要的反馈,以弥补潜在的漏洞(如果有的话)。你可以求助于以下方法之一,阅读并决定哪种方法是最好的:
- 你可以听取你配偶或你最亲密朋友的意见。
- 你可以问一堆你的朋友和办公室同事。
- 你可以发布该应用的测试版,并接收来自 web 开发社区和非偏见用户的反馈。
猜答案没有印象分,😄 是的,我们当然会选择第三个选项。
现在,停下来想想你刚刚做了什么。你从足够多的人那里听取了多种意见,然后根据这些意见做出了明智的决定。这也是集合方法所做的。
机器学习中的集成模型将来自多个模型的决策结合起来,以提高整体性能。
场景三:看看下图;我们可以看到一群被蒙住眼睛的孩子在玩“触摸并告诉”的游戏,同时检查一头他们从未见过的大象。他们中的每一个人都有不同的版本来描述大象的样子,因为他们中的每一个人都接触到了大象的不同部位。现在,如果我们给他们一个任务,让他们提交一份关于大象描述的报告,根据他们的经验,他们的个人报告只能准确地描述一部分,但他们可以结合他们的观察,给出一份关于大象描述的非常准确的报告。
类似地,集成学习方法使用一组模型,与使用单个模型相比,它们的组合结果在预测准确性方面几乎总是更好。
集成是一种用来提高性能的分而治之的方法。

现在,让我们深入一些重要的合奏技巧。

Crack up a little 😄 You are doing great 😃
简单的集成技术
- 取结果的模式
众数:众数是一个统计术语,指在一组数字中出现频率最高的数字。
在这种技术中,使用多个模型来预测每个数据点。每个模型的预测都被视为单独的投票。我们从大多数模型中得到的预测被用作最终预测。
例如:我们可以通过参考上面的场景 2 来理解这一点。我在下面插入了一张图表来展示我们的健康和健身应用程序的测试版从用户社区获得的评级。(将每个人视为不同的模型)
输出=模式=3,因为大多数人都这样投票

2.取结果的平均值
在这种技术中,我们从所有模型中取预测的平均值,并用它来进行最终预测。
平均值=总和(评分*人数)/总人数=(1 * 5)+(2 * 13)+(3 * 45)+(4 * 7)+(5 * 2)/72 = 2.833 =四舍五入到最接近的整数将是 3
3.对结果进行加权平均
这是平均法的延伸。所有模型被赋予不同的权重,定义每个模型对于预测的重要性。例如,如果你的回答者中有 25 人是专业的应用程序开发人员,而其他人之前没有这方面的经验,那么这 25 人的回答会比其他人更重要。
例如:为了子孙后代,我将这个例子的规模缩减到 5 个人
加权平均值=(0.3 * 3)+(0.3 * 2)+(0.3 * 2)+(0.15 * 4)+(0.15 * 3)= 3.15 =四舍五入到最接近的整数得出 3

高级集成技术
我们现在将学习装袋和增压技术。但是,要使用它们,你必须选择一个基本的学习算法。例如,如果我们选择一个分类树,打包和提升将由一个我们想要的大小的树池组成。
1.打包(引导汇总)
自举聚合是一种集成方法。首先,我们用 replacment 创建训练数据集的随机样本(训练数据集的子集)。然后,我们为每个样本建立一个模型(分类器或决策树)。最后,使用平均或多数投票来组合这些多个模型的结果。
由于每个模型都暴露于不同的数据子集,我们在最后使用它们的集体输出,因此我们确保通过不太依赖我们的训练数据集来解决过度拟合的问题。因此,装袋帮助我们减少方差误差。
多个模型的组合降低了方差,尤其是在模型不稳定的情况下,并且可能产生比单一模型更可靠的预测。
随机森林技术实际上使用了这一概念,但它更进一步,通过随机选择每个自举样本的特征子集来进一步减少方差,从而在训练时进行分裂(我的下一篇文章将详细介绍随机森林技术)

2.助推

提升是一种迭代技术,它基于最后一次分类来调整观察的权重。如果一个观察被错误地分类,它会试图增加这个观察的权重,反之亦然。
一般来说,增强会降低偏差误差,并建立强大的预测模型。Boosting 显示出比 bagging 更好的预测准确性,但它也倾向于过度拟合训练数据。因此,参数调整成为 boosting 算法的一个关键部分,以使它们避免过拟合。
Boosting 是一种顺序技术,其中第一个算法在整个数据集上进行训练,后续算法通过拟合第一个算法的残差来构建,从而为先前模型预测不佳的那些观察值赋予更高的权重。

它依赖于创建一系列弱学习者,每个弱学习者可能对整个数据集都不好,但对数据集的某个部分是好的。因此,每个模型实际上都提高了整体的性能。

装袋和增压之间的差异总结

集合方法的优势/好处
几乎所有的 ML 黑客马拉松都使用集成方法来增强模型的预测能力。
让我们来看看使用系综方法的优势:
- 更准确的预测结果——我们可以将集合方法的工作与我们金融投资组合的多样化进行比较。建议保持债券和股票的混合投资组合,以减少可变性,从而最大限度地降低风险。类似地,在大多数情况下,与单个模型相比,模型集合将在测试用例场景(看不见的数据)上提供更好的性能。

2.稳定且更健壮的模型 -多个模型的综合结果总是比单个模型的噪音小。这导致模型的稳定性和鲁棒性。

3.集合模型可用于捕捉数据中的线性和非线性关系。这可以通过使用两种不同的模型并形成两者的集合来实现。
集合方法的缺点
- 降低模型解释能力- 由于复杂性增加,使用集成方法降低了模型解释能力,使得最终很难得出任何关键的商业见解。
- 计算和设计时间很长- 这对实时应用来说并不好。
- 选择模特来创作合奏是一门很难掌握的艺术。
所以,现在我们已经讨论了集合方法的所有基础知识,你可以着手尝试它们来进一步加深你的理解。
感谢您的阅读!😃
神经网络中超参数调整简单指南
关于超参数优化的分步 Jupyter 笔记本演练。

Image courtesy of FT.com.
这是我的全连接(香草)神经网络系列的第四篇文章。在本文中,我们将优化一个神经网络并执行超参数调整,以便在 Beale 函数 上获得一个高性能的模型——这是许多常用于研究各种优化技术有效性的测试函数之一。这种分析可以在任何函数中重复使用,但是我建议您在另一个常见的测试函数中亲自尝试一下,以测试您的技能。就我个人而言,我发现优化一个神经网络会令人难以置信地沮丧(虽然不像 GAN 那么糟糕,如果你熟悉的话)..)除非你有一个清晰明确的程序可以遵循。我希望你喜欢这篇文章,并发现它很有见地。
您可以访问下面的前几篇文章。第一个为那些不熟悉的人提供了神经网络主题的简单介绍。第二篇文章涵盖了更多的中间主题,如激活函数、神经结构和损失函数。
神经网络的详细概述,有大量的例子和简单的图像。
towardsdatascience.com](/simple-introduction-to-neural-networks-ac1d7c3d7a2c) [## 神经网络体系结构综合介绍
神经架构、激活函数、损失函数、输出单元的详细概述。
towardsdatascience.com](/comprehensive-introduction-to-neural-network-architecture-c08c6d8e5d98) [## 神经网络优化
涵盖优化器,动量,自适应学习率,批量标准化,等等。
towardsdatascience.com](/neural-network-optimization-7ca72d4db3e0)
所有相关代码现在都可以在我的 GitHub 存储库中找到:
这个存储库包含与我的全连接神经网络系列相关的 Jupyter 笔记本内容。
github.com](https://github.com/mpstewart1/Neural-Networks)
比厄的功能
神经网络现在在工业和研究中相当普遍,但令人尴尬的是,其中很大一部分人无法很好地与它们合作,以产生能够超越大多数其他算法的高性能网络。
当应用数学家开发一种新的优化算法时,他们喜欢做的一件事是在测试函数上测试它,这有时被称为人工景观。这些人造景观帮助我们找到一种方法,从以下方面比较各种算法的性能:
- 收敛(他们到达答案的速度)
- 精确度(它们与精确答案有多接近)
- 健壮性(它们是对所有功能都表现良好,还是只对一小部分功能表现良好)
- 一般性能(例如计算复杂性)
只要向下滚动维基百科关于优化测试函数的文章,就可以看到一些函数非常糟糕。他们中的许多人被选中,因为他们强调了可能困扰优化算法的具体问题。在本文中,我们将关注一个看起来相对无害的函数,叫做比厄函数。
比厄函数看起来是这样的:

The Beale function.
这个函数看起来并不特别可怕,对吗?这是一个测试函数的原因是,它评估优化算法在具有非常浅的梯度的平坦区域中执行得有多好。在这些情况下,基于梯度的优化程序特别难以达到任何最小值,因为它们不能有效地学习。
本文的其余部分将跟随我的 GitHub 资源库中的 Jupyter 笔记本教程。我们将讨论处理这种人造景观的方法。这种情况类似于神经网络的损耗面。训练神经网络时,目标是通过执行某种形式的优化(通常是随机梯度下降)来找到损失面上的全局最小值。
通过学习如何处理一个困难的优化函数,读者应该更好地准备处理现实生活中实现神经网络的场景。
对于那些不熟悉 Jupyter 笔记本的读者来说,可以在这里阅读更多关于它的内容。
在我们接触任何神经网络之前,我们首先必须定义函数并找到它的最小值(否则,我们怎么知道我们得到了正确的答案?).第一步(在导入任何相关的包之后)是在我们的笔记本中定义 Beale 函数:
然后,我们设置一些函数边界,因为我们对这种情况下的最小值(从我们的图中)以及网格的步长有一个大概的估计。
然后,我们根据这些信息制作一个网格,并准备寻找最小值。
现在我们做一个(可怕的)初步猜测。
然后我们使用scipy.optimize函数,看看会弹出什么答案。
这是结果:

看起来答案是(3,0.5),如果你把这些值代入方程,你会发现这是最小值(维基百科页面上也是这么说的)。
在下一部分,我们将开始我们的神经网络。

神经网络中的优化
神经网络可以被定义为结合输入并试图猜测输出的框架。如果我们足够幸运地有一些结果,称为“地面真相”,来比较网络产生的输出,我们可以计算出误差。因此,网络进行猜测,计算某个误差函数,在尝试最小化该误差的同时再次猜测,并且再次猜测,直到误差不再下降。这就是优化。
在神经网络中,最常用的优化算法是 GD(梯度下降)算法。梯度下降中使用的目标函数就是我们想要最小化的损失函数。
本教程将集中在 Keras 现在,所以我会给一个简短的 Keras 复习。
复习者
Keras是一个用于深度学习的 Python 库,可以运行在 Theano 和 TensorFlow 之上,这两个强大的 Python 库分别由脸书和谷歌创建和发布,用于快速数值计算。
Keras 的开发是为了尽可能快速、简单地开发深度学习模型,用于研究和实际应用。它运行在 Python 2.7 或 3.5 上,可以在 GPU 和 CPU 上无缝执行。
Keras 建立在模型的理念之上。在其核心,我们有一个称为Sequential模型的层序列,它是层的线性堆栈。Keras 还提供了 functional API,这是一种定义复杂模型的方法,例如多输出模型、有向无环图或具有共享层的模型。
我们可以使用顺序模型总结 Keras 中深度学习模型的构建如下:
- 定义你的模型:创建一个
Sequential模型,添加图层。 - 编译你的模型:指定损失函数和优化器,调用
.compile()函数。 - 适合您的模型:通过调用
.fit()函数对模型进行数据训练。 - 进行预测:通过调用
.evaluate()或.predict()等函数,使用模型对新数据进行预测。
您可能会问自己——如何在模型运行时检查它的性能?这是一个很好的问题,答案是通过使用回调。
回调:在我们的模型训练时偷看一眼
您可以通过使用callbacks来查看您的模型的各个阶段发生了什么。回调是在训练过程的给定阶段应用的一组函数。在训练期间,您可以使用回调来获得模型的内部状态和统计数据的视图。您可以将一个回调列表(作为关键字参数回调)传递给顺序类或模型类的.fit()方法。回调的相关方法将在训练的每个阶段被调用。
- 你已经熟悉的一个回调函数是
keras.callbacks.History()。这自动包含在.fit()中。 - 另一个非常有用的是
keras.callbacks.ModelCheckpoint,它保存了模型在训练中某一点的重量。如果您的模型运行了很长一段时间,并且发生了系统故障,那么这可能是有用的。那么,并非一切都失去了。例如,只有当acc观察到改进时,才保存模型权重,这是一个很好的做法。 keras.callbacks.EarlyStopping当监控的数量停止改善时,停止训练。keras.callbacks.LearningRateScheduler会改变训练时的学习率。
我们稍后将应用一些回调。有关callbacks的完整文档,请参见https://keras.io/callbacks/。
我们必须做的第一件事是导入许多不同的功能,让我们的生活更轻松。
如果您希望您的网络使用随机数工作,但为了使结果可重复,您可以做的另一个步骤是使用随机种子。这每次都会产生相同的数字序列,尽管它们仍然是伪随机的(这是比较模型和测试可再现性的好方法)。
步骤 1 —确定网络拓扑(并非真正意义上的优化,但非常重要)
我们将使用 MNIST 数据集,它由手写数字(0–9)的灰度图像组成,尺寸为 28x28 像素。每个像素是 8 位,因此其值的范围是从 0 到 255。
获取数据集非常容易,因为Keras内置了一个函数。
我们的 X 和 Y 数据的输出分别是(60000,28,28)和(60000,1)。最好打印一些数据来检查值(以及数据类型,如果需要的话)。
我们可以通过查看每个数字的一个图像来检查训练数据,以确保它们没有从我们的数据中丢失。

最后一项检查是针对训练集和测试集的维度,这可以相对容易地完成:
我们发现我们有 60,000 个训练图像和 10,000 个测试图像。接下来要做的是预处理数据。
预处理数据
要运行我们的神经网络,我们需要预处理数据(这些步骤可以互换执行):
- 首先,我们必须使 2D 图像数组成为 1D(展平它们)。我们可以通过使用
numpy.reshape()或keras方法的数组整形来实现这一点:一个叫做keras.layers.Flatten的层将图像格式从 2d 数组(28×28 像素)转换为 28 * 28 = 784 像素的 1D 数组。 - 然后,我们需要使用以下变换来归一化像素值(给它们 0 到 1 之间的值):

在我们的例子中,最小值是零,最大值是 255,所以公式变成简单的 𝑥:=𝑥/255.
我们现在想一次性编码我们的数据。
现在我们终于准备好构建我们的模型了!
步骤 2 —调整learning rate
最常见的优化算法之一是随机梯度下降(SGD)。SGD 中可以优化的超参数有learning rate、momentum、decay和nesterov。
Learning rate控制每批结束时的重量,而momentum控制前一次更新对当前重量更新的影响程度。Decay表示每次更新的学习率衰减,而nesterov根据我们是否要应用内斯特罗夫动量取值“真”或“假”。
这些超参数的典型值为 lr=0.01,衰变=1e-6,动量=0.9,nesterov=True。
学习率超参数进入optimizer函数,我们将在下面看到。Keras 在SGD优化器中有一个默认的学习率调度程序,它在随机梯度下降优化算法中降低学习率。学习率根据以下公式降低:
lr = lr×1/(1+衰变∫历元)

Source: http://cs231n.github.io/neural-networks-3
让我们在Keras中实现一个学习率适应计划。我们将从 SGD 和 0.1 的学习率值开始。然后,我们将训练 60 个时期的模型,并将衰减参数设置为 0.0016 (0.1/60)。我们还包括动量值 0.8,因为当使用自适应学习率时,它似乎工作得很好。
接下来,我们构建神经网络的架构:
我们现在可以运行模型,看看它的表现如何。这在我的机器上花了大约 20 分钟,可能快或慢,取决于你的机器。
在它完成运行后,我们可以为训练集和测试集绘制准确度和损失函数作为历元的函数,以查看网络的表现如何。
损失函数图如下所示:

Loss as a function of epochs.
这就是准确性:

我们现在来看看如何应用定制的学习率。
使用LearningRateScheduler应用自定义学习率变化
编写一个函数,执行指数学习率衰减,如下式所示:
𝑙𝑟=𝑙𝑟₀ × 𝑒^(−𝑘𝑡)
这与前面的类似,所以我将在一个代码块中完成,并描述不同之处。
我们在这里看到,这里唯一改变的是我们定义的exp_decay函数的出现及其在LearningRateScheduler函数中的使用。请注意,我们这次还选择在模型中添加了一些回调函数。
我们现在可以绘制学习率和损失函数,作为历元数的函数。学习率曲线非常平滑,因为它遵循我们预先定义的指数衰减函数。

与以前相比,损失函数现在看起来也更平滑了。

这向您展示了开发一个学习率调度器是提高神经网络性能的一个有用的方法。
步骤 3 —选择一个optimizer和一个loss function
当构建一个模型并使用它进行预测时,例如,为图像分配标签分数(“猫”、“飞机”等)。),我们想通过定义一个“损失”函数(或目标函数)来衡量自己的成败。优化的目标是有效地计算最小化该损失函数的参数/权重。keras提供各种类型的损失函数。
有时“损失”函数度量“距离”我们可以用适合问题或数据集的各种方法来定义两个数据点之间的“距离”。使用的距离取决于数据类型和要解决的问题。例如,在自然语言处理(分析文本数据)中,汉明距离更为常见。
距离
- 欧几里得的
- 曼哈顿
- 其他的,比如汉明,测量字符串之间的距离。“carolin”和“cathrin”的汉明距离是 3。
损失函数
- MSE(用于回归)
- 分类交叉熵(用于分类)
- 二元交叉熵(用于分类)
步骤 4 —决定batch size和number of epochs
批量大小定义了通过网络传播的样本数量。
例如,假设您有 1000 个训练样本,您想要设置一个等于 100 的batch_size。该算法从训练数据集中获取前 100 个样本(从第 1 个到第 100 个)并训练网络。接下来,它获取第二个 100 个样本(从第 101 个到第 200 个)并再次训练网络。我们可以继续这样做,直到我们通过网络传播了所有的样本。
使用批量大小的优势< number of all samples:
- It requires less memory. Since you train the network using fewer samples, the overall training procedure requires less memory. That’s especially important if you cannot fit the whole dataset in your machine’s memory.
- Typically networks train faster with mini-batches. That’s because we update the weights after each propagation.
Disadvantages of using a batch size < number of all samples:
- The smaller the batch, the less accurate the estimate of the gradient will be.
The 历元数是一个超参数,它定义了学习算法在整个训练数据集中工作的次数。
一个时期意味着训练数据集中的每个样本都有机会更新内部模型参数。一个时期由一个或多个批次组成。
对于选择批次大小或周期数没有硬性规定,也不能保证增加周期数会比减少周期数得到更好的结果。
步骤 5 —随机重启
这个方法在keras中似乎没有实现。这可以通过改变keras.callbacks.LearningRateScheduler很容易地做到。我将把这个留给读者做练习,但它本质上涉及到在指定数量的时期后,在有限的次数内重置学习率。
使用交叉验证调整超参数
现在,我们将使用 Scikit-Learn 的 GridSearchCV 为我们的超参数尝试几个值并比较结果,而不是手动尝试不同的值。
为了使用keras进行交叉验证,我们将使用 Scikit-Learn API 的包装器。它们提供了一种在 Scikit-Learn 工作流程中使用顺序 Keras 模型(仅单输入)的方法。
有两种包装器可用:
keras.wrappers.scikit_learn.KerasClassifier(build_fn=None, **sk_params),它实现了 Scikit-Learn 分类器接口,
keras.wrappers.scikit_learn.KerasRegressor(build_fn=None, **sk_params),实现 Scikit-Learn 回归器接口。
尝试不同的权重初始化
我们将尝试通过交叉验证优化的第一个超参数是不同权重初始化。
我们网格搜索的结果是:

我们看到,使用 lecun_uniform 初始化或 glorot_uniform 初始化的模型获得了最好的结果,并且我们可以使用我们的网络实现接近 97%的准确度。
将您的神经网络模型保存到 JSON
分层数据格式(HDF5)是一种数据存储格式,用于存储大型数据数组,包括神经网络中的权重值。
可以安装 HDF5 Python 模块:pip install h5py
Keras 允许您使用 JSON 格式描述和保存任何模型。
使用多个超参数进行交叉验证
通常,我们对查看单个参数如何变化不感兴趣,而是查看多个参数变化如何影响我们的结果。我们可以同时对多个参数进行交叉验证,有效地尝试它们的组合。
注意:神经网络中的交叉验证计算量很大。实验前先思考!将您正在验证的要素数量相乘,以查看有多少种组合。使用 k 折叠交叉验证来评估每个组合( k 是我们选择的参数)。
例如,我们可以选择搜索以下各项的不同值:
- 批量
- 时代数
- 初始化模式
选择在字典中指定,并传递给 GridSearchCV。
我们现在将对batch size、number of epochs和initializer的组合进行网格搜索。
结束前的最后一个问题:如果我们必须在 GridSearchCV 中循环的参数和值的数量特别大,我们该怎么办?
这可能是一个特别麻烦的问题——想象一下,有五个参数被选择,而我们为每个参数选择了 10 个潜在值。这种独特组合的数量是 10⁵,这意味着我们必须训练数量多得离谱的网络。这样做是疯狂的,所以通常使用 RandomizedCV 作为替代。
RandomizedCV 允许我们指定所有的潜在参数。然后,对于交叉验证中的每个折叠,它选择一个随机的参数子集用于当前模型。最后,用户可以选择一组最佳参数,并将其用作近似解。
最终意见
感谢您的阅读,希望这篇文章对您有所帮助和启发。我期待着从读者那里听到他们对这个超参数调整指南的应用。本系列的下一篇文章将涵盖全连接神经网络的一些更高级的方面。
延伸阅读
深度学习课程:
- 吴恩达的机器学习课程有一个很好的神经网络介绍部分。
- Geoffrey Hinton 的课程:用于机器学习的 Coursera 神经网络(2012 年秋季)
- 迈克尔·尼尔森的免费书籍神经网络和深度学习
- 约舒阿·本吉奥、伊恩·古德菲勒和亚伦·库维尔写了一本关于深度学习的书
- 雨果·拉罗歇尔在舍布鲁克大学的课程(视频+幻灯片)
- 斯坦福关于无监督特征学习和深度学习的教程(吴恩达等人)
- 牛津大学 2014-2015 年 ML 课程
- 英伟达深度学习课程(2015 年夏季)
- 谷歌在 Udacity 上的深度学习课程(2016 年 1 月)
面向 NLP:
- Stanford CS224d:自然语言处理的深度学习(2015 年春季),作者 Richard Socher
- NAACL HLT 2013 上的教程:自然语言处理的深度学习(无魔法)(视频+幻灯片)
以视觉为导向:
- 用于视觉识别的 CS231n 卷积神经网络作者 Andrej Karpathy(之前的版本,更短更不完善:黑客的神经网络指南)。
重要的神经网络文章:
- 神经网络中的深度学习:概述
- 使用神经网络的持续终身学习:综述——开放存取
- 物理储层计算的最新进展:综述—开放存取
- 脉冲神经网络中的深度学习
- 集成神经网络(ENN):一种无梯度随机方法——开放存取
- 多层前馈网络是通用逼近器
- 深度网络与 ReLU 激活函数和线性样条型方法的比较——开放访问
- 脉冲神经元网络:第三代神经网络模型
- 多层前馈网络的逼近能力
- 关于梯度下降学习算法中的动量项
如何将 R 包上传到 Artifactory
带截图的快速入门指南

Photo by Paul Hanaoka on Unsplash
工厂起重机
假设您想将您的 R 包上传到某个地方,以便其他人可以下载它们。一种简单的方法是使用 Artifactory 托管本地 CRAN 存储库。在下面的指南中,我将向您展示如何以最少的麻烦(主要是通过可视界面)自己完成这项工作,当然,当您想开始更多地自动化这项工作时,一切都是可以脚本化的。
步骤 0:安装 RStudio
这是标准的 IDE,所以您可能已经安装了它。如果没有,就在这里下载:【https://rstudio.com/T2
步骤 1:在 Artifactory 中创建 CRAN 存储库
在 web 浏览器中导航到 Artifactory UI,然后转到“管理”选项卡。

从那里,转到仓库、仓库、本地,然后选择起重机作为包类型。请记住您的回购的名称,因为您稍后将使用它来上传您的文件!如果您没有访问管理页面的权限,您可能需要请求您的 Artifactory 管理员为您创建 CRAN repo。更多信息可以看官方文档。
步骤 1:创建您的 R 包
如果你发现这篇博文,你可能已经创建了 R 包,但是如果你还没有打包好,这里有一篇关于这个的经典博文:https://Hilary Parker . com/2014/04/29/writing-an-R-package-from-scratch/
步骤 2:构建您的包
一旦你有了你的 R 包,你应该用 RStudio 打开它。在这里,您可以打开 Rproj 文件,以便 RStudio 知道您正在构建一个包。

click on this file in the RStudio file browser
从“构建”菜单中选择“构建源代码包”。如果你有很多文件需要针对特定平台进行编译,那么你可以选择“构建二进制包”,但是你上传到 Artifactory 的时候就要输入相关数据了。此操作的结果将是一个压缩文件,然后您可以上传到您的 CRAN on Artifactory。

this is where the Build menu is
步骤 3:将包上传到 Artifactory
首先,使用 RStudio 找到您的包 tarball 是在哪里生成的。它应该位于包含您的软件包源代码的目录旁边。

the build window shows where the tar file is generated. it should be located in the parent directory of your package.
导航到您的 Artifactory web 应用程序。然后,在 Artifactory 上打开工件存储库窗口,并点击 deploy 按钮。

从“目标存储库”菜单中选择您的 CRAN repo。它应该显示为“包装类型:起重机”,如下所示。然后,选择您生成的包文件或将它拖到此窗口中。满意后点击部署。

this is what the deploy menu should look like before you add your package

this is what the deploy menu looks like after you add your package
现在,您应该能够在工件存储库浏览器的树浏览器视图中看到您的包。

you should be able to see your package in artifactory now
步骤 4:检查是否可以从工厂起重机安装
首先,您想通过运行remove.packages("sample.r.package")从 R 库中移除示例包。然后,您可以通过运行install.packages("sample.r.package", repos=c("http://my_artifactory.com/artifactory/my-cran/"))从本地 CRAN 安装它。希望一切都已正确安装,现在您可以与组织的其他成员共享您的软件包了!
恭喜你!您将第一个包上传到 Artifactory!
既然你已经有了自己的计划,世界就是你的了!告诉你公司的每个人你的软件包,他们可以很容易地安装它,而不必使用 devtools 。不错!
旅程的下一步是开始将二进制包上传到 Artifactory。这就是使用像 CRAN 这样的包管理器的真正优势所在,因为这意味着你的包的用户不必安装复杂的工具链来为他们自己的系统重新编译你的包。当你准备好采取下一步行动时,请点击这里的查看文档。
卷积神经网络简介
在本文中,我将使用许多 swan 图片来解释卷积神经网络(CNN)的概念,并将使用 CNN 的常规多层感知器神经网络来处理图像。
图像分析
让我们假设我们想要创建一个能够识别图像中天鹅的神经网络模型。天鹅具有某些特征,可以用来帮助确定天鹅是否存在,例如它的长脖子、白色等等。

A swan has certain characteristic features that can be used for recognition purposes.
对于某些图像,可能更难确定是否存在天鹅,请看下图。

Harder to classify swan image.
这些特征在上面的图像中仍然存在,但我们更难挑出这些特征。让我们考虑一些更极端的情况。

Extreme cases of swan classification.
至少颜色是一致的吧?或者是…

Don’t forget about those black swans!
还能更糟吗?肯定可以。

Worst-case scenarios.
好了,天鹅的照片已经够多了。先说神经网络。我们一直在以一种非常天真的方式谈论检测图像中的特征。研究人员建立了多种计算机视觉技术来处理这些问题:筛选、快速、冲浪、摘要等。然而,类似的问题也出现了:探测器要么太普通,要么太复杂。人类正在设计这些特征检测器,这使得它们要么过于简单,要么难以推广。
- 如果我们学会了检测特征会怎么样?
- 我们需要一个可以进行表征学习(或者特征学习)的系统。
表征学习是一种允许系统自动找到给定任务的相关特征的技术。取代手动特征工程。有几种技术可以做到这一点:
- 无监督的(K 均值、主成分分析等)
- 监督(Sup。字典学习,神经网络!)
传统神经网络的问题
我将假设你已经熟悉传统的神经网络称为多层感知器(MLP)。如果您对这些不熟悉,Medium 上有数百个概述 MLP 如何工作的教程。这些都是在人脑上模拟的,神经元受到连接节点的刺激,只有当达到某个阈值时才会被激活。

A standard multilayer perceptron (traditional neural network).
MLP 有几个缺点,尤其是在图像处理方面。MLP 为每个输入使用一个感知器(例如,图像中的像素,在 RGB 情况下乘以 3)。对于大图像,权重的数量很快变得难以管理。对于具有 3 个颜色通道的 224 x 224 像素图像,大约有 150,000 个权重必须被训练!因此,在训练和过度配合时会出现困难。
另一个常见的问题是 MLP 对输入(图像)及其平移版本的反应不同,它们不是平移不变的。例如,如果一张图片中的图像的左上方出现了一只猫,而另一张图片的右下方出现了一只猫,则 MLP 会尝试自我纠正,并假设图像的这一部分中总会出现一只猫。
显然,MLP 不是图像处理的最佳选择。一个主要问题是当图像被展平为 MLP 时,空间信息丢失。靠近的节点很重要,因为它们有助于定义图像的特征。因此,我们需要一种方法来利用图像特征(像素)的空间相关性,这样无论猫出现在哪里,我们都可以在照片中看到它。在下图中,我们正在学习多余的功能。这种方法并不可靠,因为猫可能会以另一种姿势出现。

A cat detector using an MLP which changes as the position of the cat changes.
进入卷积神经网络
我希望这个案例清楚地说明为什么 MLP 是一个用于图像处理的糟糕想法。现在让我们继续讨论如何使用 CNN 来解决我们的大部分问题。

CNN’s leverage the fact that nearby pixels are more strongly related than distant ones.
我们用一种叫做过滤器的东西来分析附近像素的影响。一个过滤器就是你所想的那样,在我们的情况下,我们采用一个由用户指定大小的过滤器(一个经验法则是 3x3 或 5x5 ),我们将它从左上向右下移动穿过图像。对于图像上的每个点,使用卷积运算基于过滤器计算一个值。
过滤器可以与任何东西相关,对于人类的图片,一个过滤器可以与看到的鼻子相关联,我们的鼻子过滤器会给我们一个指示,即鼻子在我们的图像中出现的强度,以及出现的次数和位置。与 MLP 相比,这减少了神经网络必须学习的权重数量,也意味着当这些特征的位置改变时,它不会扰乱神经网络。

The convolution operation.
如果您想知道网络如何学习不同的特征,以及网络是否有可能学习相同的特征(拥有 10 个鼻子过滤器有点多余),这是非常不可能发生的。在构建网络时,我们随机指定过滤器的值,然后随着网络的训练,过滤器不断更新自身。除非选择的过滤器数量非常大,否则生产出两个相同的过滤器是非常不可能的。
下面是一些过滤器的例子,我们称之为内核。

Examples of kernel filters for CNN’s.
在滤波器通过图像之后,为每个滤波器生成特征图。然后通过激活函数获取这些图像,激活函数决定某个特征是否出现在图像中的给定位置。然后我们可以做很多事情,例如添加更多的过滤层和创建更多的特征地图,随着我们创建更深的 CNN,这些变得越来越抽象。我们还可以使用合并图层来选择要素地图上的最大值,并将其用作后续图层的输入。理论上,任何类型的操作都可以在池化图层中完成,但在实践中,仅使用最大池化,因为我们希望找到异常值-这些是我们的网络看到的要素!

An example CNN with two convolutional layers, two pooling layers, and a fully connected layer which decides the final classification of the image into one of several categories.
只是重申一下我们目前的发现。我们知道 MLP:
- 不适合图像缩放
- 忽略像素位置和与邻居的相关性带来的信息
- 无法处理翻译
CNN 的总体想法是智能地适应图像的属性:
- 像素位置和邻域具有语义含义
- 感兴趣的元素可以出现在图像中的任何地方

Comparison of architecture for MLP and CNN.
CNN 也是由层组成的,但这些层并不完全相连:它们有过滤器,即应用于整个图像的立方体形状的权重集。滤波器的每个 2D 切片被称为内核。这些过滤器引入了平移不变性和参数共享。它们是如何应用的?回旋!

Example of how convolutions are applied to images using kernel filters.
现在的一个好问题是图像的边缘会发生什么?如果我们在普通图像上应用卷积,结果将根据滤波器的大小进行一定量的下采样。如果我们不想让这种情况发生,我们该怎么做?我们可以用衬垫。
填充

Illustration of how full padding and same padding are applied to CNN’s.
填充本质上使得由滤波器核产生的特征图与原始图像具有相同的大小。这对于深度 CNN 非常有用,因为我们不希望输出减少,这样我们在网络的末端只剩下一个 2×2 的区域来预测我们的结果。
我们如何将过滤器连接在一起?
如果我们有许多特征地图,如何在我们的网络中组合这些地图以帮助我们获得最终结果?

为了清楚起见,每个过滤器都与整个 3D 输入立方体进行卷积,但会生成 2D 要素地图。
- 因为我们有多个过滤器,我们最终得到一个 3D 输出:每个过滤器一个 2D 要素地图
- 从一个卷积层到下一个卷积层,特征映射维度可能会发生巨大变化:如果一个层有 128 个滤波器,我们可以以 32x32x16 的输入进入该层,以 32x32x128 的输出退出。
- 用滤波器对图像进行卷积产生特征图,该特征图突出了图像中给定特征的存在。
在卷积层中,我们基本上是在图像上应用多个滤波器来提取不同的特征。但最重要的是,我们正在学习那些过滤器!我们遗漏了一件事:非线性。
介绍 ReLU
CNN 最成功的非线性是整流非线性单元(ReLU ),它解决了 sigmoids 中出现的梯度消失问题。ReLU 更容易计算并产生稀疏性(并不总是有益的)。
不同层的对比
卷积神经网络中有三种类型的层:卷积层、池层和全连接层。这些图层中的每一个都有不同的参数可以优化,并对输入数据执行不同的任务。

Features of a convolutional layer.
卷积层是对原始图像或深层 CNN 中的其他特征地图应用过滤器的层。这是网络中大多数用户指定参数的位置。最重要的参数是颗粒的数量和颗粒的大小。

Features of a pooling layer
池层类似于卷积层,但它们执行特定的功能,如最大池(在某个过滤区域取最大值)或平均池(在某个过滤区域取平均值)。这些通常用于降低网络的维度。

Features of a fully connected layer.
完全连接的图层位于 CNN 的分类输出之前,用于在分类之前展平结果。这类似于 MLP 的输出图层。

The architecture of a standard CNN.
CNN 层学什么?
- CNN 的每一层都学习越来越复杂的过滤器。
- 第一层学习基本的特征检测过滤器:边缘、拐角等
- 中间层学习检测物体部分的过滤器。对于面部,他们可能会学会对眼睛、鼻子等做出反应
- 最后一层有更高的表现:他们学习识别不同形状和位置的完整物体。

Examples of CNN’s trained to recognize specific objects and their generated feature maps.
要看 CNN 实际工作的 3D 例子,请点击下面的链接这里。
时事通讯
关于新博客文章和额外内容的更新,请注册我的时事通讯。
丰富您的学术之旅,加入一个由科学家,研究人员和行业专业人士组成的社区,以获得…
mailchi.mp](https://mailchi.mp/6304809e49e7/matthew-stewart)
神经网络导论
神经网络的详细概述,有大量的例子和简单的图像。
“你的大脑不会制造思想。你的思想塑造了神经网络。” —狄巴克·乔布拉

本文是旨在揭开神经网络背后的理论以及如何设计和实现它们的系列文章中的第一篇。这篇文章旨在对神经网络进行详细而全面的介绍,面向广泛的个人:对神经网络如何工作知之甚少或一无所知的人,以及相对精通其用途但可能不是专家的人。在这篇文章中,我将涵盖神经网络的动机和基础知识。未来的文章将深入探讨关于神经网络和深度学习的设计和优化的更详细的主题。
这些教程主要基于哈佛和斯坦福大学计算机科学和数据科学系的课堂笔记和例子。
我希望你喜欢这篇文章,并且不管你以前对神经网络的理解如何,都能学到一些东西。我们开始吧!
神经网络的动机
未经训练的神经网络模型很像新生婴儿:他们生来对世界一无所知(如果考虑 tabula rasa 认识论的话),只有通过接触世界,即后验知识,他们的无知才会被慢慢修正。算法通过数据体验世界——通过在相关数据集上训练神经网络,我们试图减少它的无知。我们衡量进度的方法是监控网络产生的错误。
在深入研究神经网络世界之前,了解这些网络背后的动机以及它们为什么工作是很重要的。要做到这一点,我们必须谈一谈逻辑回归。
以定量响应变量(如出租车接送数量、自行车租赁数量)的建模和预测为中心的方法被称为回归(以及岭、套索等。).当响应变量为分类时,则该问题不再被称为回归问题,而是被标记为分类问题。
让我们考虑一个二元分类问题。目标是尝试根据一组预测变量将每个观察值分类到由 Y 定义的类别(如类或聚类)中。
假设我们希望根据患者的特征来预测患者是否患有心脏病。这里的响应变量是分类的,有有限的结果,或者更明确地说,是二元的,因为只有两个类别(是/否)。

这里有很多特性—现在,我们将只使用 MaxHR 变量。

为了进行这种预测,我们将使用一种称为逻辑回归的方法。逻辑回归解决了在给定输入值 X 的情况下,估计某人患有心脏病的概率 P(y=1) 的问题。
逻辑回归模型使用一个名为逻辑函数的函数来模拟 P(y=1) :

因此,模型将使用一条 S 形曲线预测 P(y=1) ,这是逻辑函数的一般形状。
β₀ 将曲线向左或向右移动c =β₀/β₁,而 β₁ 控制 S 形曲线的陡度。
请注意,如果 β₁ 为正,则预测的 P(y=1) 从小值 X 的零变为大值 X 的一,如果 β₁ 为负,则具有相反的关联。
这在下面用图表进行了总结。

既然我们已经了解了如何操作我们的逻辑回归曲线,我们可以利用一些变量来得到我们想要的曲线。
我们可以改变 β₀ 值来移动我们的偏移量。

我们可以改变 β₁ 值来扭曲我们的渐变。

手动完成这项工作非常繁琐,而且不太可能收敛到最佳值。为了解决这个问题,我们使用损失函数来量化属于当前参数的误差水平。然后我们找到最小化这个损失函数的系数。对于这种二元分类,我们可以使用二元损失函数来优化我们的逻辑回归。
因此,神经网络的参数与网络产生的误差有关系,当参数改变时,误差也会改变。我们使用一种叫做梯度下降的优化算法来改变参数,这种算法对于寻找函数的最小值很有用。我们正在寻求最小化误差,这也被称为损失函数或目标函数。

那么我们刚才做的有什么意义呢?这和神经网络有什么关系?实际上,我们刚刚做的基本上是由神经网络算法执行的相同程序。
我们在之前的模型中只使用了一个特性。取而代之的是,我们可以采用多种特征,并以网络格式来说明这些特征。我们有每个特征的权重,也有一个偏差项,它们共同构成了我们的回归参数。根据问题是分类问题还是回归问题,表述会略有不同。

当我们谈论神经网络中的权重时,我们讨论的是我们各种输入函数的这些回归参数。然后将其传递给一个激活函数,该函数决定结果是否足够重要,足以“触发”该节点。我将在下一篇文章中更详细地讨论不同的激活函数。
所以现在我们开发了一个非常简单的网络,它由具有四个特征的多重逻辑回归组成。
我们需要从一些任意的值公式开始,以便开始更新和优化参数,这将通过在每次更新后评估损失函数并执行梯度下降来完成。
我们做的第一件事是设置随机选择的权重。最有可能的是,它会表现得很糟糕——在我们的心脏数据中,模型会给我们错误的答案。
然后,我们通过惩罚表现不佳的网络来“训练”它。

然而,仅仅告诉计算机它的性能是好是坏并没有特别的帮助。您需要告诉它如何更改这些权重,以提高模型的性能。
我们已经知道如何告诉计算机它运行良好,我们只需要参考我们的损失函数。现在,程序变得更加复杂,因为我们要处理 5 个砝码。我将只考虑一个重量,但所有重量的程序都是类似的。
理想情况下,我们想知道给出最小 ℒ (w)的 w 的值。

为了找到函数ℒ(w)的最佳点,我们可以对权重进行微分,然后将其设置为零。**

然后,我们需要找到满足该等式的 w 。有时没有明确的解决方案。
比较灵活的方法是从任意一点出发,然后确定往哪个方向走可以减少损失(本例中是向左还是向右)。具体来说,我们可以计算函数在这一点的斜率。如果斜率为负,我们向右移动,如果斜率为正,我们向左移动。然后重复这个过程,直到收敛。

如果步长与斜率成比例,则可以避免超过最小值。
我们如何执行此更新?这是用梯度下降法来完成的,这在前面已经简单提到过了。
梯度下降
梯度下降是一种寻找函数最小值的迭代方法。梯度下降有各种风格,我将在后续文章中详细讨论这些。这篇博文介绍了更新权重的不同方法。现在,我们将坚持传统的梯度下降算法,有时被称为德尔塔法则。
我们知道,我们想要朝着导数的相反方向前进(因为我们试图“远离”误差),我们知道我们想要做出与导数成比例的一步。这一步由称为学习率的参数λ控制。我们的新权重是旧权重和新步长的相加,其中步长是从损失函数和我们的相关参数在影响学习速率中的重要程度(因此是导数)导出的。

一个大的学习率意味着更多的权重放在导数上,这样算法的每次迭代都可以进行大的步骤。较小的学习速率意味着导数的权重较小,因此每次迭代的步长较小。
如果步长太小,算法将需要很长时间才能收敛,如果步长太大,算法将不断错过最佳参数选择。显然,在建立神经网络时,选择学习速率可能是一个重要的参数。

梯度下降有各种考虑因素:
- 我们仍然需要求导。
- 我们需要知道学习率是多少,或者如何设置。
- 我们需要避免局部最小值。
- 最后,完整的损失函数包括所有单个“误差”的总和。这可以是几十万个例子。
现在求导是用自动微分来完成的,所以我们不太关心这个。然而,决定学习速率是一个重要而复杂的问题,我将在后面的系列教程中讨论。
局部最小值对于神经网络来说可能是非常成问题的,因为神经网络的公式不能保证我们将达到全局最小值。

陷入局部最小值意味着我们的参数有一个局部好的优化,但在我们的损失面上有一个更好的优化。神经网络损失曲面可能具有许多这样的局部最优值,这对网络优化是有问题的。例如,参见下图所示的损失面。

Example neural network loss surface. Source

Network getting stuck in local minima.

Network reach global minima.
我们如何解决这个问题?一个建议是使用批量和随机梯度下降。这个想法听起来很复杂,但很简单——使用一批(子集)数据,而不是整个数据集,这样损失面在每次迭代过程中会部分变形。
对于每次迭代 k ,以下损失(可能性)函数可用于导出导数:

这是全损失函数的近似值。我们可以用一个例子来说明这一点。首先,我们从完全损失(可能性)表面开始,我们随机分配的网络权重为我们提供了初始值。

然后,我们选择一批数据,可能是整个数据集的 10%,并构建一个新的损失面。

然后,我们对该批次执行梯度下降,并执行我们的更新。

我们现在在一个新的地方。我们选择完整数据集的一个新的随机子集,并再次构建我们的损失面。

然后,我们对该批次执行梯度下降,并执行我们的更新。

我们用新的子集再次继续这个过程。

执行我们的更新。

这个过程持续多次迭代。

直到网络开始收敛到全局最小值。

我们的工具包中现在有足够的知识来着手建立我们的第一个神经网络。
人工神经网络
既然我们了解了逻辑回归是如何工作的,我们如何评估我们网络的性能,以及我们如何更新网络以提高我们的性能,我们就可以着手建立一个神经网络了。
首先,我想让我们明白神经网络为什么叫神经网络。你可能听说过这是因为它们模仿了神经元的结构,神经元是大脑中存在的细胞。神经元的结构看起来比神经网络复杂得多,但功能是相似的。

实际神经元的工作方式涉及电势的积累,当超过特定值时,会导致突触前神经元穿过轴突放电,并刺激突触后神经元。
人类有数十亿相互连接的神经元,可以产生极其复杂的放电模式。与我们能够做到的相比,人脑的能力是不可思议的,甚至与最先进的神经网络相比也是如此。由于这个原因,我们可能不会很快看到神经网络模仿人脑的功能。
我们可以画一个神经图,把神经网络中的神经元结构和人工神经元进行类比。

鉴于人类大脑的能力,应该很明显,人工神经网络的能力在范围上是相当无限的——特别是一旦我们开始将这些与传感器、致动器以及互联网的财富联系起来——这解释了它们在世界上无处不在,尽管事实上我们正处于它们发展的相对初期阶段。
毕竟,还原论者可能会认为人类只不过是通过神经系统的各个部分连接到传感器和执行器的神经网络的集合。
现在让我们想象我们有多种特征。每个特征都要经过一种叫做仿射变换的东西,这基本上是一种加法(或减法)和/或乘法。这给了我们一个类似回归方程的东西。在多层感知器中,当多个节点汇聚到一个节点时,仿射变换就变得很重要。
然后,我们通过激活函数传递这个结果,这给了我们某种形式的概率。这个概率决定了神经元是否会触发——我们的结果可以插入到我们的损失函数中,以评估算法的性能。

从现在开始,我将把仿射块和激活块抽象成一个块。然而,要清楚的是,仿射变换是来自上游节点的输出的合并,然后求和的输出被传递到激活函数,该函数评估概率以确定它是否是足以使神经元激活的量化值(概率)。
我们现在可以回到第一个例子,看看我们的心脏病数据。我们可以将两个逻辑回归合并在一起。个体逻辑回归看起来像下面的情况:

当我们连接这两个网络时,由于增加了自由度,我们获得了具有增加的灵活性的网络。

这很好地说明了神经网络的力量,我们能够将多个函数串在一起(求和),这样,通过大量的函数——这些函数来自大量的神经元——我们能够产生高度非线性的函数。有了足够大的一组神经元,就可以产生任意复杂的连续函数。
这是一个非常简单的神经网络的例子,然而,我们看到,即使是这样一个简单的网络,我们也已经遇到了问题。我们应该如何更新我们的权重值?

我们需要能够计算损失函数相对于这些权重的导数。为了学习缺失的权重,w₁、w₂和 w₃,我们需要利用反向传播。
反向传播
反向传播是神经网络学习的中心机制。它是告诉网络网络在预测过程中是否犯了错误的信使。反向传播的发现是整个神经网络研究中最重要的里程碑之一。
**传播就是在特定的方向或通过特定的媒介传递某种东西(如光、声)。当我们在神经网络的上下文中讨论反向传播时,我们谈论的是信息的传输,并且该信息与神经网络在对数据进行猜测时产生的误差有关。
在预测过程中,神经网络通过网络节点向前传播信号,直到信号到达做出决策的输出层。然后,网络通过网络反向传播关于该错误的信息,从而可以改变每个参数。

反向传播是我们计算网络中每个参数的导数的方法,这是执行梯度下降所必需的。这是一个重要的区别,因为很容易混淆反向传播和梯度下降。首先执行反向传播,以便获得执行梯度下降所需的信息。
你可能已经注意到了,我们仍然需要计算导数。计算机不能区分,但可以建立一个函数库来做到这一点,而不需要网络设计人员参与,它为我们抽象了这个过程。这就是所谓的自动微分。下面是一个例子。
我们可以像这样手动完成,然后针对每个网络架构和每个节点进行更改。

或者我们可以编写一个函数库,该函数库与体系结构有着内在的联系,这样,当网络体系结构更新时,该过程就会被抽象出来并自动更新。

如果你真的想了解这种抽象的自动微分过程有多有用,请尝试制作一个具有半打节点的多层神经网络,并编写代码来实现反向传播(如果有人有耐心和勇气做到这一点,那么向你致敬)。
更复杂的网络
具有两个节点的网络对于大多数应用来说不是特别有用。通常,我们使用神经网络来逼近传统方法难以描述的复杂函数。
神经网络很特别,因为它们遵循一种叫做 的通用近似定理 。该定理指出,给定神经网络中无限数量的神经元,可以精确地表示任意复杂的连续函数。这是一个相当深刻的声明,因为它意味着,给定足够的计算能力,我们可以近似任何函数。
显然,在实践中,这个想法有几个问题。首先,我们受到现有数据的限制,这限制了我们预测类别或估计值的潜在准确性。其次,我们受到计算能力的限制。设计一个远远超过世界上最强大的超级计算机能力的网络是相当容易的。
诀窍是设计一种网络架构,使我们能够使用相对较少的计算能力和最少的数据实现高精度。
更令人印象深刻的是,一个隐藏层足以表示任意精度的任何函数的近似值。
那么如果一层就够了,为什么人们还要用多层神经网络呢?

A neural architecture with multiple hidden layers.
答案很简单。该网络将需要具有非常宽的神经架构,因为浅网络比深网络需要(指数地)更多的宽度。此外,浅网络对过度拟合具有更高的亲和力。
这就是为什么深度学习领域存在(深度指的是神经网络的多层)并主导机器学习和大多数涉及数据分类和预测的领域的当代研究文献背后的刺激因素。
摘要
本文讨论了神经网络的动机和背景,并概述了如何训练它们。我们讨论了损失函数、误差传播、激活函数和网络架构。下图很好地总结了所有讨论的概念以及它们是如何相互联系的。

Neural networks step-by-step. Source
这篇文章中的知识将为我们提供一个强大的基础,我们可以在未来的文章中讨论如何提高神经网络的性能并将其用于深度学习应用。
时事通讯
关于新博客文章和额外内容的更新,请注册我的时事通讯。
丰富您的学术之旅,加入一个由科学家,研究人员和行业专业人士组成的社区,以获得…
mailchi.mp](https://mailchi.mp/6304809e49e7/matthew-stewart)
参考文献
J.《数值最优化》,施普林格,1999 年
TLDR: J. Bullinaria,“用动量学习,共轭梯度学习”,2015*
Numpy 中从头开始的简单线性回归
线性回归很可能是你已经学会的,或者打算学习的第一个“机器学习”算法。这是一个简单的算法,最初是在统计领域开发的,并作为理解输入和输出变量之间关系的模型进行研究。
顾名思义,它是一个线性模型,因此它假设输入变量()X)和单个(【连续】)输出变量( y )之间是线性关系。更准确地说, y 可以从输入变量的线性组合中计算出来。
在只有一个输入变量的情况下,该方法被称为简单线性回归,这将是本文的主题。你可以说在现实世界中你有不止一个输入变量,这是真的,但是从基础开始总是一个好主意。
线性方程为每个输入值分配一个比例因子,称为系数,通常用希腊字母 Beta ( β )表示。增加了一个系数,给线一个额外的自由度(向上或向下移动线),称为截距或偏差系数。
简单的线性回归可以表示为:

如果你有一个以上的输入变量,回归“线”将被称为一个平面或超平面。此外,不用说,你会有更多的β系数,每一个乘以一定的输入值。如果β系数为零,它告诉你那个位置的变量对模型没有影响。
学习一个线性回归模型意味着用你现有的数据来估计表示中使用的系数的值。
线性回归的假设
准备用于线性回归的数据时,您应该记住以下几点:
- 线性假设 —模型假设变量之间的关系是线性的
- 无噪声 —模型假设输入和输出变量没有噪声——因此,如果可能的话,移除异常值
- 无共线性-当输入变量高度相关时,模型会过度拟合
- 正态分布-如果输入和输出变量呈正态分布,模型将做出更可靠的预测。如果不是这样,试着对你的变量进行一些变换,使它们看起来更正常
- 重定标输入 —使用定标器或规格化器进行更可靠的预测
贝塔系数公式
在简单线性回归中,有两个系数——β0 和β1。它们不一定要‘学习’,你可以通过一个简单的公式计算出来(仅用于简单的线性回归):


可以手工计算,也可以用 Python 计算。我会用 Python。
数据集介绍
我决定不从网上下载任意的数据集,而是自己制作。它由 300 个任意点组成:

快速散点图将揭示变量之间清晰的线性趋势:

现在,您可以将 x 和 y 插入上面的公式中。首先,我将计算β1 系数:

这并不难。β0 或偏差系数的计算将更加简单:

仅此而已。你现在可以做预测了。为此,我决定声明一个函数,calc _ predictions(),它将接受 x 项作为输入。然后,您可以轻松计算预测值:

现在那些 y_preds 可以用来绘制回归线:


那才叫爽。 快也。但是你现在可能想知道,有没有更简单快捷的方法来计算系数?
更简单的方法
你不必使用上面的公式来获得系数,有一个更短的方法。还涉及到公式的用法,不过要短很多。偏差截距的公式保持不变,但β1 的公式发生了变化:

下面是用 Python 实现它的方法:

请注意您必须如何使用数组索引来获得相关系数,您的任务是探索如果不使用它会发生什么。
你可以看到系数值和之前计算的一样,所以一切正常(万岁)。
模型评估
评估一个回归模型的方法有很多,但我会用 均方根误差 。计算方法如下:

其中 Pi 为预测值。
要在 Python 中使用它,有两个选项:
- 从 Scikit 中导入 MSE 学习并求它的平方根
- 从头开始写
我将使用第二个选项,因为计算非常简单:

现在可以对模型进行评估:

最后的话
该说再见了。这是一篇相当短的文章,但是我想说这是线性回归的一个很好的介绍。稍后,我将从头开始发表一篇关于多元线性回归的文章,它在现实世界中有实际应用,因为您的数据集可能有多个输入变量。
在此之前,尝试在您的数据集上使用这些方程,然后尝试将结果与 Scikit-Learn 的线性回归进行比较。
喜欢这篇文章吗?成为 中等会员 继续无限制学习。如果你使用下面的链接,我会收到你的一部分会员费,不需要你额外付费。
[## 通过我的推荐链接加入 Medium-Dario rade ci
作为一个媒体会员,你的会员费的一部分会给你阅读的作家,你可以完全接触到每一个故事…
medium.com](https://medium.com/@radecicdario/membership)
机器学习完全入门指南:简单线性回归四行代码!
一个清晰而全面的蓝图,绝对适合任何想要构建简单机器学习模型的人

甚至你可以建立一个机器学习模型。
说真的!
仅有好的数据并不能说明全部情况。你是想根据一个人多年的经验来计算他的工资应该是多少吗?你需要检查相对于你的年销售额你在广告上花了多少钱吗?线性回归可能正是你所需要的!
GIF via GIPHY
什么是线性回归?
线性回归着眼于您拥有的数据和您想要预测的数据之间的关系。
线性回归是一种基本且常用的预测分析类型。这是所有统计技术中应用最广泛的。它量化了一个或多个预测变量和一个结果变量之间的关系。
线性回归模型用于显示(或预测)两个变量或因素之间的关系。回归分析常用于显示两个变量之间的相关性。
例如,您可以查看棒球队球员的一些信息,并预测他们在该赛季的表现。你可能想研究一家公司的一些变量,并预测他们的股票走势。你甚至可能只想检查人们学习的小时数和他们在考试中的表现,或者你可以查看学生的家庭作业总成绩与他们在考试中表现的关系。这是一个非常有用的技术!

Photo by StockSnap via Pixabay
只要记住:相关不等于因果!两个变量之间存在关系并不意味着一个变量导致了另一个变量!回归分析不用于预测因果关系。它可以观察变量之间的关系。它可以检查变量之间的关联程度。这取决于你来仔细看看那些关系。
几个重要术语:
您的线性回归模型中的方程预测的变量称为因变量。我们称之为 y。用于预测因变量的变量称为自变量。我们称之为 X 。
你可以认为预测( y )依赖于其他变量( X )。这使得 y 成为因变量!
在简单线性回归分析中,每个观察值由两个变量组成。这些是自变量和因变量。多元回归分析着眼于两个或更多的自变量,以及它们如何与自变量相关联。描述 y 如何与 X 相关的等式被称为回归模型!
回归最早是由兴趣广泛的弗朗西斯·高尔顿爵士深入研究的。虽然他是一个非常有问题的角色,有很多值得反对的信仰,但他确实写了一些关于治疗枪伤和让你的马从流沙中脱离的很酷的信息的书。他还在指纹、听力测试方面做了一些有益的工作,甚至设计了第一张天气图。他于 1909 年被封为爵士。
在研究植物和动物中父母和孩子相对大小的数据时,他观察到,比一般人大的父母有比一般人大的孩子,但这些孩子在他们自己的一代中的相对位置会小一些。他称之为回归平庸。用现代术语来说,这将是向平均值的回归。
(然而,我不得不说,“回归平庸”这句话有一定的闪光点,我需要把它融入我的日常生活...)
GIF via GIPHY
不过,要明确的是,我们谈论的是期望(预测)而不是绝对确定性!
回归模型有什么好处?
回归模型用于预测真实值,例如工资或身高。如果你的自变量是时间,那么你就是在预测未来值。否则,您的模型预测的是当前未知的值。回归技术的例子包括:
- 简单回归
- 多次回归
- 多项式回归
- 支持向量回归
假设您正在查看一些数据,其中包括员工的工作年限和工资。你应该看看这两个数字之间的相关性。也许你正在经营一家新企业或小公司,这些数字是随机设定的。
那么你如何找到这两个变量之间的相关性呢?为了搞清楚这一点,我们将创建一个模型,告诉我们什么是这种关系的最佳拟合线。
直觉
这里有一个简单的线性回归公式:

(你可能认为这是高中代数中的斜率或趋势线方程。)
在这个等式中, y 是因变量,这就是你要解释的。在本文的其余部分, y 将是员工在一定工作年限后的工资。
可以看到上面的自变量。这是与你的预测值变化相关的变量。自变量可能引起变化,或者只是与变化有关。记住,线性回归不能证明因果关系!
这个系数就是你如何解释自变量的变化可能不完全等于 y 的变化。
现在我们想看看证据。我们想在我们的数据中画一条最适合我们的数据的线。回归线可以显示正线性关系(该线看起来向上倾斜)、负线性关系(该线向下倾斜)或完全没有关系(一条平线)。



该常数是直线与纵轴相交的点。例如,如果你在下图中看到 0 年的工作经验,你的工资大约是 30,000 美元。所以下图中的常数大约是 30,000 美元。

坡度越陡,你多年的经验得到的钱就越多。例如,也许多一年的经验,你的工资(y)会额外增加 10,000 美元,但如果斜率更大,你可能会得到 15,000 美元。如果斜率为负,随着经验的积累,你实际上会赔钱,但我真的希望你不要在那家公司工作太久...
简单线性回归怎么找到那条线?
当我们看一张图表时,我们可以从这条线到我们的实际观察值画出垂直线。您可以将实际观察值视为点,而线条显示模型观察值(预测值)。

我们画的这条线是员工实际收入和他的模型(预测)收入之间的差异。我们将查看最小平方和以找到最佳直线,这意味着您将获得所有平方差的总和并找到最小值。
那叫普通最小二乘法法!
那么我们该怎么做呢?
首先是进口货!
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
现在让我们预处理我们的数据!如果你不太了解数据清理和预处理,你可能想看看这篇文章。它将引导您完成导入库、准备数据和要素缩放。
如何在几分钟内为机器学习模型成功准备数据
towardsdatascience.com](/the-complete-beginners-guide-to-data-cleaning-and-preprocessing-2070b7d4c6d)
我们将复制并粘贴那篇文章中的代码,并做两处微小的修改。当然,我们需要更改数据集的名称。然后我们来看一下数据。在我们的例子中,假设我们的员工有一列年资和一列薪水,仅此而已。请记住,我们的索引从 0 开始,我们将继续从因变量的数据中分离出最后一列,就像我们已经设置的那样。然而,这一次,我们将获取自变量的第二列,所以我们将做一个小的改变来获取它。
dataset = pd.read_csv('salary.csv')
X = dataset.iloc[:, :-1].values
y = dataset.iloc[:, 1].values
现在 X 是一个特征矩阵(我们的自变量), y 是因变量的向量。完美!
是时候将我们的数据分成训练集和测试集了。通常,我们会对我们的训练和测试数据进行 80/20 分割。然而,在这里,我们使用的是一个只有 30 个观察值的小数据集。也许这一次我们将拆分我们的数据,这样我们就有 20 个训练观察和 10 个测试规模。
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 1/3, random_state = 0)
你有 X_train,X_test,y_train,y_test!你已经准备好了!(永远不要忘记,在这个过程的每一步,都有大约一百万件事情需要学习、改变和改进。你的模型的力量取决于你和你投入的一切!)

Photo by Thomas William on Unsplash
我们将随机状态设置为 0,这样我们可以得到相同的结果。(计算中可能会有随机因素,我想确保我们都在同一页上,这样就没有人会紧张。)
我们将在训练集上训练我们的模型,然后根据我们的信息预测结果。我们的模型将学习训练集上的相关性。然后,我们将通过让它用我们的测试集预测值来测试它学到了什么。我们可以将我们的结果与测试集上的实际结果进行比较,看看我们的模型做得如何!
A 总是把你的数据分成训练集和测试集!如果你用你用来训练它的相同数据来测试你的结果,你可能会有非常好的结果,但是你的模型并不好!它只是记住了你想让它做的事情,而不是学习任何可以用于未知数据的东西。那叫过度拟合,也就是说你没有造好模型!
特征缩放
我们实际上不需要在这里做任何功能缩放!

Photo by Gift Habeshaw on Unsplash
线性回归
现在我们可以让模型适合我们的训练集了!
为此,我们将使用 Scikit-learn learn。首先,我们将导入线性模型库和线性回归类。然后,我们将创建该类的一个对象—回归量。我们将使用一种方法(fit 方法)来使我们创建的回归对象适合训练集。为了创建对象,我们给它命名,然后用括号调用它。我们可以用大约三行代码完成所有这些工作!
让我们从 Scikit-Learn 导入线性回归,以便我们可以继续使用它。在括号之间,我们将指定我们想要使用的数据,以便我们的模型确切地知道我们想要拟合什么。我们希望获得 X_train 和 y_train,因为我们正在处理所有的训练数据。
如果你想了解更多的细节,你可以看看文档。
现在,我们准备创建我们的回归变量,并使其适合我们的训练数据。
from sklearn.linear_model import LinearRegression
regressor = LinearRegression()
regressor.fit(X_train, y_train)
在那里!我们对数据使用简单的线性回归,并准备在测试集上测试我们的预测能力!
这是机器学习!我们创造了一台机器,回归器,我们让它在训练集上学习多年经验和薪水之间的关系。
现在,它可以根据已有的信息预测未来的数据。我们的机器已经准备好根据一个新员工的工作经验来预测他的工资了!
让我们用回归量来预测新的观察结果。我们想通过观察新的观察来了解机器是如何学习的。
我们将创建一个预测值的向量。这是因变量的预测向量,我们称之为 y_pred。为此,我们可以使用我们创建和训练的回归变量,并使用 predict 方法。我们需要指定要进行哪些预测,因此我们希望确保包含测试集。对于 regressor.predict 中的输入参数,我们希望指定新观测值的特征矩阵,因此我们将指定 X_test。
y_pred = regressor.predict(X_test)
说真的。那只需要一行代码!
现在,y_test 是测试集中 10 个观察值的实际工资,y_pred 是我们的模型预测的这 10 个雇员的预测工资。
你做到了!四行代码的线性回归!
GIF via GIPHY
形象化
让我们把结果可视化吧!我们需要看看我们的预测和实际结果之间有什么区别。
我们可以绘制图表来解释结果。首先,我们可以使用 plt.scatter 绘制真实的观察值,以制作散点图。(我们之前导入 matplotlib.pyplot 为 plt)。
我们将首先查看训练集,因此我们将在 X 坐标上绘制 X_train,在 y 坐标上绘制 y_train。那么我们可能想要一些颜色。我们用蓝色表示我们的观察,用红色表示我们的回归线(预测)。对于回归线,我们将再次对 X 坐标使用 X_train,然后使用 X_train 观测值的预测。
让我们用 x 轴和 y 轴的标题和标签来稍微想象一下。
plt.scatter(X_train, y_train, color = 'blue')
plt.plot(X_train, regressor.predict(X_train), color = 'red')
plt.title('Salary vs Experience (Training set)')
plt.xlabel('Years of Experience')
plt.ylabel('Salary')
plt.show()
现在我们可以看到我们的蓝点,它们是我们的真实值和我们沿着红线的预测值!

让我们对测试集做同样的事情!我们将更改测试集的标题,并将代码中的“train”改为“test”。
plt.scatter(X_test, y_test, color = 'blue')
plt.plot(X_train, regressor.predict(X_train), color = 'red')
plt.title('Salary vs Experience (Test set)')
plt.xlabel('Years of Experience')
plt.ylabel('Salary')
plt.show()
请务必注意,我们没有在第二行中将 X_train 更改为 X_test。我们的回归变量已经被训练集训练过了。当我们训练时,我们得到了一个唯一的模型方程。如果我们替换它,我们会得到相同的线,我们可能会建立相同回归线的新点。

这是一个相当不错的模型!
我们的模型很好地预测了这些新员工的工资。一些实际观察和预测是一样的,这很好。在 y 和 X 变量之间没有 100%的相关性,所以一些预测不会完全准确。
你做到了!您导入了库,清理并预处理了数据,构建并训练了一个简单的线性回归器,用它来进行预测,甚至还可视化了结果!
恭喜你!!!

Photo by Free-Photos via Pixabay
想要更多吗?
[## 机器学习完全初学者指南:4 行代码中的多元线性回归!
征服多元线性回归的基础(和向后消除!)并用你的数据预测未来!
towardsdatascience.com](/multiple-linear-regression-in-four-lines-of-code-b8ba26192e84)
继续学习!
机器学习建立在统计学的基础上,如果没有简单的线性回归等概念,你就无法开始理解机器学习。但这并不意味着统计学和机器学习是一回事!除了作为机器学习的基本构件的一部分之外,线性回归器在很大程度上是统计学(和数据科学)的工具。
如果你有兴趣了解更多关于统计学和机器学习之间的差异,我推荐你看看马修·斯图尔特博士研究员写的这篇精彩的文章!他很好地阐明了这些概念,我强烈推荐你花些时间通读他的文章。
不,它们不一样。如果机器学习只是被美化的统计学,那么建筑学只是被美化了…
towardsdatascience.com](/the-actual-difference-between-statistics-and-machine-learning-64b49f07ea3)
和往常一样,如果你用这些信息做了什么很酷的事情,请在下面的回复中让人们知道,或者随时在 LinkedIn 上联系。
你可能也想看看这些文章:
Git 和 GitHub 基础知识,供好奇和完全困惑的人使用(加上最简单的方法来为您的第一次公开…
towardsdatascience.com](/getting-started-with-git-and-github-6fcd0f2d4ac6) [## 如何用 GitHub 毫不费力地免费创建一个网站
GitHub Pages 入门:创建和发布免费作品集的快速简便指南…
towardsdatascience.com](/how-to-create-a-free-github-pages-website-53743d7524e1) [## Google Colab 入门
沮丧和困惑的基本教程
towardsdatascience.com](/getting-started-with-google-colab-f2fff97f594c) [## 深度学习简介
新手、新手和新手的神经网络。
towardsdatascience.com](/intro-to-deep-learning-c025efd92535) [## WTF 是图像分类?
为好奇和困惑征服卷积神经网络
towardsdatascience.com](/wtf-is-image-classification-8e78a8235acb) [## 如何构建准确率高于 97%的图像分类器
清晰完整的成功蓝图
medium.freecodecamp.org](https://medium.freecodecamp.org/how-to-build-the-best-image-classifier-3c72010b3d55) [## 出色的模型部署初学者指南
一个清晰简单的路线图,让你的机器学习模型在互联网上,做一些很酷的事情
heartbeat.fritz.ai](https://heartbeat.fritz.ai/brilliant-beginners-guide-to-model-deployment-133e158f6717)
感谢阅读!
Python 中的简单线性回归
一个带有简化解释的机器学习模型…
Video version of the story, if you are into that sort of thing | Performed by the author
简单线性回归是一种寻找两个连续变量之间关系的统计方法。在现有的两个变量中,一个是自变量,另一个是因变量。统计关系在确定两个变量之间的关系时不准确。比如身高和体重的关系。
这种关系是由著名的线方程定义的,你们在高中应该学过。
y = b0 + b1x*
在哪里,
y 是因变量
x 是自变量
b0 是关系的基值
b1 是解释 y & x. 关系的直线的斜率
例如,y 指的是一个人的工资如何随着他所拥有的经验年限而变化。因此,在这种情况下,工资将是因变量,经验将是自变量,基本价值将是一个没有经验的人的工资。在我们的代码示例中,我们将处理这样的数据集。
为了确定什么是最佳拟合线,让我们看看下面的曲线。

带有红叉的点是实际或真实的数据点,而带有绿叉的点是回归模型的预测值。这里的目标是建立这样一个回归模型,使得误差平方和最小。
I 导入数据集并进行数据预处理
今天,我们进入代码的数据集。我们有一个业务问题,一家公司希望在员工的工资和他们的经验之间建立一种关系。我们将使用简单的线性回归来研究这种相关性。
简单的线性回归会告诉我们这种关系的最佳拟合线是什么。让我们来看看数据集。你可以点击下载数据集。

我们的数据由总共 30 个对象和两个属性组成, 年经历 和 薪水 。属性years experience是独立属性,属性 Salary 是从属属性。
我将使用 Spyder 来编写这个机器学习模型。在进入模型的回归部分之前,我们需要对数据集执行数据预处理。我将直接为其编写代码,但是如果您想了解更多,请访问下面的文章。
概念性数据预处理文章:
有了详细的概念…
towardsdatascience.com](/data-preprocessing-in-data-mining-machine-learning-79a9662e2eb)
编程数据预处理文章:
对于机器学习与工作代码的例子…
towardsdatascience.com](/data-preprocessing-in-python-b52b652e37d5)
# Importing the libraries
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd# Importing the dataset
dataset = pd.read_csv('SimpleLinearRegression.csv')
X = dataset.iloc[:, :-1].values
y = dataset.iloc[:, -1].values# Splitting the dataset into the Training set and Test set
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 1/3, random_state = 0)
执行完这段代码后,我们将训练集和测试集分别与独立属性和从属属性的数组和向量分开。它们应该是这样的:


将数据集拟合到简单的线性回归模型中
在中,为了使数据适合我们的回归模型,我们需要导入一个库,调用回归方法,并使我们的训练数据适合该回归模型。我们的做法如下:
# Fitting Simple Linear Regression to the Training set
from sklearn.linear_model import LinearRegression
regressor = LinearRegression()
regressor.fit(X_train, y_train)
由于我们的机器学习模型已经知道了我们的训练集的相关性,我们现在将预测我们的测试集的值,然后稍后将它们与测试集的实际值进行比较。
为了预测测试集的值,我们需要编写以下代码:
# Predicting the Test set results
y_pred = regressor.predict(X_test)
在执行完代码之后,我们可以手动查看预测的测试集值和实际的测试集值,看看它们有什么不同。

如您所见,这些值互不相同。一些具有非常低的差异,如第 4 个条目,而一些具有稍高的差异,如第 8 个条目。
可视化数据集中的相关性
在中,为了判断数据之间的实际相关性,我们将绘制两条曲线。
- 可视化训练集结果
我们使用以下代码来实现这一点:
# Visualising the Training set results
plt.scatter(X_train, y_train, color = 'red')
plt.plot(X_train, regressor.predict(X_train), color = 'blue')
plt.title('Salary vs Experience (Training set)')
plt.xlabel('Years of Experience')
plt.ylabel('Salary')
plt.show()

可以看出,这里的线并没有那么差,因为只有几个点离它很远,大多数点都在线的周围。
- 可视化测试集结果
我们使用以下代码来实现这一点:
# Visualising the Test set results
plt.scatter(X_test, y_test, color = 'red')
plt.plot(X_train, regressor.predict(X_train), color = 'blue')
plt.title('Salary vs Experience (Test set)')
plt.xlabel('Years of Experience')
plt.ylabel('Salary')
plt.show()
这里,关于为什么在plt.plot(X_train, regressor.predict(X_train), color = 'blue')到X_test中regressor.predict()的参数没有被改变,可能存在混淆。这是因为,如果我们这样做,我们将得到一个新的行,但我们希望将我们的测试集与我们从训练集中得到的行进行比较。曲线看起来会像这样:

这里可以看到,10 个数据点中有 5 个数据点位于直线上。10 分中有 3 分非常接近终点线,10 分中只有 2 分稍微远一点。
这篇关于 Python 中用于机器学习建模的简单线性回归的文章到此结束。
我将免费赠送一本关于一致性的电子书。在这里获得你的免费电子书。
如果你喜欢阅读这样的故事,那么你应该 在你的收件箱 中收到我的帖子,如果你想支持我成为一名作家,考虑注册成为一名媒体成员。每月 5 美元,你可以无限制地阅读媒体上的故事。如果你注册使用我的链接,我会赚一小笔佣金,不需要你额外付费。
作为一个媒体会员,你的会员费的一部分会给你阅读的作家,你可以完全接触到每一个故事…
tarun-gupta.medium.com](https://tarun-gupta.medium.com/membership)
感谢阅读。如果你喜欢这个,可以看看我的其他帖子:
[## 标记故事列表的快速链接—感谢您的访问
我也有一份以快节奏出版为目标的出版物。读书成为作家。
tarun-gupta.medium.com](https://tarun-gupta.medium.com/thank-you-for-visiting-my-profile-9f708062c75e)
评估下一次加密投资的简单方法

Photo by Quantitatives on Unsplash
在本帖中,我们将使用 Python 研究加密货币市场的相关性,以分析和可视化这些市场是如何发展的,并为更深入的分析提供一个基础框架。
不熟悉加密?你可以找到一篇关于区块链技术、加密货币以及它们与中本聪创新的关系的精彩概述。
TLDR:评估各种加密货币之间的相关性和趋势,以了解:
- 哪些硬币可以帮助分散你的投资组合:硬币之间通常有很强的相关性,因此很难分散风险(购买和持有比特币是一种安全的押注)。
- 稳定的硬币实际上是稳定的吗:一般来说,它们似乎与比特币价格波动无关。
- 如果有可能预测下一轮牛市:最多投机。然而,2019 年底看起来与 2015 年、2016 年和 2017 年相似……准备好装满你的袋子吧。
注:所有用于分析的代码都可以在我的 github 页面 上找到,数据来自 coinmarketcap.com,2014 - 2019 年市值排名前 100 的加密货币。
时间序列价格数据的相关性分析?

Image by the author | Price trend for 10 random coins from 2014 to 2019
从上图可以看出,一般来说,每个硬币的趋势似乎是相关的。然而,众所周知,直接在非平稳时间序列数据上计算相关性会给出有偏相关值。因此,每日百分比变化用于评估相关性。以下是市值最高的 20 种硬币价格变化的相关性。

Image by the author | Correlation of the price change of the current top 20 coins by market cap in 2015

Image by the author | Correlation of the price change of the top 20 coins by market cap in 2019
总的来说,市值排名前 20 位的加密货币有三个主要趋势:
- 所有稳定硬币(一种与法定货币或其他商品挂钩的加密货币,如与美元挂钩的系绳)和 Ravencoin 与比特币和其他替代硬币的价格变化百分比的相关性非常低。
- 如下图所示,这种相关性有逐年增加的总体趋势。
- 目前排名前 20 的硬币中只有 45%是 5 年前的。

Image by the author | Density plot of the correlation of the top 20 coins from 2014 to 2019
哪些硬币有助于分散你的投资组合?
值得注意的是,几乎所有的加密货币都变得更加相互关联。实际上,投资股市时,拥有多样化的投资组合可以降低风险。然而,拥有一个前 20 名硬币的多元化投资组合,并不具有与交易股票相同的效果。
足够有趣的是,Ravencoin,这是比特币的一个分支,增加了资产发行的功能,而 transfer 与比特币和前 20 名中的大部分 altcoins 的相关性非常低。我不确定这样做的原因,但它肯定是值得关注的硬币之一。
此外,虽然比特币、以太币和莱特币在过去 5 年中一直排名前 5,但进一步评估其他硬币进入前 20 的驱动因素(技术、营销、创新、用例)将是有趣的。然而,这超出了本文的范围。
你稳定吗兄弟?
稳定的硬币,如系绳,似乎与比特币价格的波动无关。稳定的硬币有助于消除对转换率的怀疑,使加密货币更适合购买商品和服务。这是因为基础资产与比特币不相关。然而,对稳定硬币的主要批评(除了那些集中的),是证明用于支持稳定硬币的商品/法定货币的数量,反映了稳定硬币的流通供给。
月亮什么时候出现(我们能预测下一次牛市吗)?

Image by the author | Bitcoin price grouped by months from 2014 to 2019
这种分析在很大程度上是推测性的,并且是基于上面的箱线图,在过去的 6 年里按月分组。在 2015 年、2016 年和 2017 年,我们看到比特币价格在第 6 个月和第 7 个月左右飙升,之后我们看到年底出现牛市。
同样,2014 年和 2018 年非常相似,第 5 个月和第 6 个月出现增长,但接下来的几个月增长缓慢,预示着市场看跌。
对于 2019 年,如果 8 月份看起来像 7 月份一样强劲,根据 2015 年、2016 年和 2017 年的趋势,我们可能会迎来另一轮牛市,打破以前的纪录。
我们应该在什么时候看到这些高点…下面的直方图显示,比特币高点往往发生在 12 月和 1 月(由这几个月的大量异常值表示)。

Image by the author | Box plot of Bitcoin price grouped by months from 2014 to 2019
接下来呢?
在这一分析的基础上,还可以做更多的事情。我想到的几个例子是:
- 使用来自流行交换的 API 来获得最新的设置。
- 添加市值和交易量等其他指标来丰富分析。
- 添加一些基于 google tends 或 twitter feed 分析的情绪分析来预测未来价格。
- 训练一个预测机器学习模型来预测未来价格。
感谢您的阅读,希望这能激发您对该领域的兴趣。欢迎建设性的反馈,如果你发现代码有问题,你可以在 Github repo 这里提出问题。
来自《走向数据科学》编辑的提示: 虽然我们允许独立作者根据我们的 规则和指导方针 发表文章,但我们不认可每个作者的贡献。你不应该在没有寻求专业建议的情况下依赖一个作者的作品。详见我们的 读者术语 。
用 C 语言实现简单的神经网络

作为深入研究机器学习概念的一部分,我决定在没有任何向量或矩阵库的帮助下,用 C 从头开始编写一个简单的神经网络。
“为什么 C 并没有向量或矩阵库?…"
网上发布的大多数样本神经网络都是用 Python 编写的,并且使用了 numpy 等强大的数学库。虽然这些示例中的代码简洁明了,但是当复杂的矩阵运算被压缩到一条语句中时,可能很难掌握反向传播背后的细节。当你被迫用 C 写出循环时,概念背后的数学变得更加清晰。我强烈建议这样做,即使你最终会使用一个健壮的库,比如 TensorFlow。
本文假设您已经理解了神经网络和反向传播背后的概念。如果你还没有到那里,我建议从这里开始。我将通过讨论下面的关键代码片段来浏览代码。如果你想看看完整的源代码,你可以在这里找到它。
我们开始吧!我们首先定义几个辅助函数,包括激活函数及其相应的导数。第三个函数用于初始化 0.0 和 1.0 之间的权重:
// Activation function and its derivative
**double** sigmoid(**double** x) { **return** 1 / (1 + exp(-x)); }
**double** dSigmoid(**double** x) { **return** x * (1 — x); }// Init all weights and biases between 0.0 and 1.0
**double** init_weight() { **return** ((**double**)rand())/((**double**)RAND_MAX); }
我们的网络将由一个具有 2 个节点的隐藏层和一个输出层节点组成。这是学习 XOR 函数的最低配置:

下面我们定义网络的维度,并为层、偏差和权重分配数组:
**static** **const** **int** numInputs = 2;
**static** **const** **int** numHiddenNodes = 2;
**static** **const** **int** numOutputs = 1;**double** hiddenLayer[numHiddenNodes];
**double** outputLayer[numOutputs];**double** hiddenLayerBias[numHiddenNodes];
**double** outputLayerBias[numOutputs];**double** hiddenWeights[numInputs][numHiddenNodes];
**double** outputWeights[numHiddenNodes][numOutputs];
就反向传播算法而言,我考虑了与如何迭代训练数据相关的两个选项。虽然有许多口味可供选择,但最基本的两种是随机梯度下降(SGD) 和批量梯度下降。在这种情况下,我选择了 SGD ,其中权重基于一对输入/预期输出进行更新。用这种方法更容易将算法概念化。使用这种方法时,为了最大化网络收敛到正确解的机会,随机化训练对的顺序是很重要的。
以下是训练集:
**static** **const** **int** numTrainingSets = 4;**double** training_inputs[numTrainingSets][numInputs] = { {0.0f,0.0f},{1.0f,0.0f},{0.0f,1.0f},{1.0f,1.0f} };**double** training_outputs[numTrainingSets][numOutputs] = { {0.0f},{1.0f},{1.0f},{0.0f} };
主循环首先迭代一定数量的历元(本例中为 10,000 个),对于每个历元,它挑选一对输入和预期输出进行操作。因为 SGD 要求输入/输出对是随机的,所以我在每个时期打乱一个索引数组,以便选择一个随机对,同时确保每个时期使用所有输入:
// Iterate through the entire training for a number of epochs **for** (**int** n=0; n < epochs; n++) { // As per SGD, shuffle the order of the training set
**int** trainingSetOrder[] = {0,1,2,3};
shuffle(trainingSetOrder,numTrainingSets); // Cycle through each of the training set elements
**for** (**int** x=0; x<numTrainingSets; x++) {
**int** i = trainingSetOrder[x];
其余的代码片段在上面的循环中运行,构成了反向传播算法的“核心”。根据 SGD ,我们采用单对训练输入,由变量 i 指定,并执行以下操作:
第一部分是在给定当前权重的情况下计算网络的输出,从根据以下公式计算隐藏层激活开始:

其中 j 是隐藏节点的数量(2),而 k 是输入的数量(2):
// Compute hidden layer activation **for** (**int** j=0; j<numHiddenNodes; j++) {
**double** activation=hiddenLayerBias[j];
**for** (**int** k=0; k<numInputs; k++) {
activation+=training_inputs[i][k]*hiddenWeights[k][j];
}
hiddenLayer[j] = sigmoid(activation);
}
然后,我们通过下式计算输出层激活:

其中 j 是输出节点(1)的数量,而 k 是隐藏节点(2)的数量:
// Compute output layer activation
**for** (**int** j=0; j<numOutputs; j++) {
**double** activation=outputLayerBias[j];
**for** (**int** k=0; k<numHiddenNodes; k++) {
activation+=hiddenLayer[k]*outputWeights[k][j];
}
outputLayer[j] = sigmoid(activation);
}
下一步包括计算网络权重的微小增量变化,这将使网络朝着最小化网络刚刚计算的输出误差的方向移动。这一步从输出节点开始,向后进行。我们通过计算误差的导数并将其乘以该节点输出的导数来计算权重的变化( deltaOutput )。这里我们使用均方误差(MSE) 函数来计算误差。然而,请注意,对于反向传播本身,我们只需计算误差的导数,对于 MSE 而言,该导数就是预期输出和计算输出之间的差值。参见这篇文章以获得这背后更完整的数学解释。对于输出层,我们计算每个输出节点的增量:
// Compute change in output weights **double** deltaOutput[numOutputs];
**for** (**int** j=0; j<numOutputs; j++) {
**double** dError = (training_outputs[i][j]-outputLayer[j]);
deltaOutput[j] = dError*dSigmoid(outputLayer[j]);
}
对于隐藏层,这是一个类似的过程,除了给定隐藏节点的误差计算是所有输出节点的误差之和(对其应用适当的权重):
// Compute change in hidden weights **double** deltaHidden[numHiddenNodes];
**for** (**int** j=0; j<numHiddenNodes; j++) {
**double** dError = 0.0f;
**for**(**int** k=0; k<numOutputs; k++) {
dError+=deltaOutput[k]*outputWeights[j][k];
}
deltaHidden[j] = dError*dSigmoid(hiddenLayer[j]);
}
现在我们已经有了每个输出和隐藏节点的增量,最后一步是将它们应用到各自的权重矩阵和偏差单位:
// Apply change in output weights **for** (**int** j=0; j<numOutputs; j++) {
outputLayerBias[j] += deltaOutput[j]*lr;
**for** (**int** k=0; k<numHiddenNodes; k++) {
outputWeights[k][j]+=hiddenLayer[k]*deltaOutput[j]*lr;
}
}
// Apply change in hidden weights
**for** (**int** j=0; j<numHiddenNodes; j++) {
hiddenLayerBias[j] += deltaHidden[j]*lr;
**for**(**int** k=0; k<numInputs; k++) {
hiddenWeights[k][j]+=training_inputs[i][k]*deltaHidden[j]*lr;
}
}
差不多就是这样!最后一个细节是学习率,在这个应用中设置为 0.1。这是一个值,它必须随着时期的数量进行调整,以最大化网络有效学习问题的机会。对于这个问题,0.1 的学习率和 10,000 的纪元的组合似乎是一个很好的组合。这是一张追踪训练过程中误差的图表。对于该图,我每 10 个时期绘制一个样本:

虽然编写这段代码时没有考虑到可扩展性,但是扩展它的功能,使之包括以下内容会很有趣:可配置的隐藏层数、批量梯度下降以及激活和错误函数的附加选项。请随意摆弄代码,不要犹豫提出问题或反馈!
使用张量流执行的简单神经网络模型

简介
渴望执行是 TensorFlow (TF)中一种从零开始构建深度学习模型的巧妙方法。它允许您构建原型模型,而没有 TF 通常使用的图形化方法带来的麻烦。
例如,使用急切执行,不需要启动图形会话来执行张量计算。这意味着更快的调试,因为您可以即时检查每一行计算,而不需要将计算包装在图形会话中。
然而,作为免责声明,使用急切执行需要一些关于深度学习中使用的矩阵代数概念的知识,特别是关于如何在神经网络中进行前向传递。如果你正在寻找一些更高级的现成的东西,我建议使用 TF 或 PyTorch 中的 Keras API。
本文将通过描述构建、训练和评估一个简单的多层感知器的过程,提供一个如何使用热切执行的例子。
建筑和符号
此示例中构建的神经网络由一个输入层、一个隐藏层和一个输出层组成。输入层包含 3 个节点,隐藏层包含 20 个节点,输出层包含 1 个节点。输出值是连续的(即神经网络执行回归)。
输入层、隐藏层和输出层的值以及层之间的权重可以表示为矩阵。对隐藏层和输出层的偏差可以表示为向量(具有一行或一列的矩阵的特殊情况)。下图显示了每个矩阵和向量的维度。

Notations and dimensions for matrices and vectors
开始急切执行
在导入本例所需的依赖项(主要是 NumPy 和 TF)之后,如果您没有使用 TF 2.0,您将需要启用急切执行。下面的代码片段展示了如何启用急切执行。
为培训和评估准备数据
下一步是通过使用 NumPy 的 random 模块,随机生成一些用于训练和评估的数据(当然是为了说明的目的)。使用这种方法,我创建了两组独立的数据,一组用于训练,另一组用于评估。
每组数据包含一个输入数组和一个输出数组。输入数组的形状为(观测值的数量,要素的数量),而输出数组的形状为(观测值的数量,每个观测值的输出值的数量)。要素数量对应于输入图层中的结点数量,而每个观测值的输出值数量对应于输出图层中的结点数量。
在生成数据之后,我将测试数据分成几批,以便进行更有效的评估。训练数据也将被分成批次,但是在训练过程本身中完成。
下面的片段显示了我如何准备数据。
建立模型
我在这里做的是创建一个 Python 类,它存储负责权重和偏差初始化、正向传递、反向传播以及权重和偏差更新的代码。
权重和偏差通过从标准正态分布中抽取随机值来初始化。权重的随机初始化通常优于用值 0 或 1 初始化权重,以便减少出现诸如消失梯度等问题的机会。
正向传递可由以下等式描述。relu()代表整流线性单位函数,它以非线性方式转换输入和偏差的线性组合。输出 Y 的等式没有变换函数,因为输出应该是连续值。顺便提一下,如果期望输出是分类的,则在第二个等式中将需要诸如 sigmoid 或 softmax 之类的非线性变换函数。

Matrix algebra for the forward pass
损失的反向传播以及权重和偏差的更新只需要几行代码(分别在模型类的 loss()和 backward()方法中)。
下面相当长的片段展示了如何在一个类中实现模型构建过程。额外的 compute_output()方法是前向传递算法的包装器,便于用户选择用于模型训练和评估的硬件设备(CPU 或 GPU)。
您可能已经注意到了类中使用的函数 tf.cast()。原因是,由于前面代码片段中的 from_tensor_slices()方法返回 tf.float64 数据格式的张量,而矩阵运算(例如 tf.matmul())只能处理 tf.float32 数据格式的张量,所以会触发这个奇怪的错误。我没有在 TF 2.0 上尝试过急切执行,所以我不确定这个问题是否已经在这个新版本中解决了。我所知道的是,这个数据格式的问题肯定发生在我在这个例子中使用的 TF 版本(即 1.31.1)中,所以如果在 TF 的旧版本上使用急切执行,这是需要注意的。
训练模特
准备好数据,建立好模型之后,接下来就是训练模型了。模型训练非常简单,只需要几行代码。这里的基本思想是对每个时期的每批数据重复以下步骤:通过模型输入张量以获得预测张量,计算损失,反向传播损失,并更新权重和偏差。在每个时期,训练数据将被随机分成不同的批次,以提高模型训练的计算效率,并帮助模型更好地泛化。下面的代码片段说明了如何通过热切的执行来完成培训。
评估模型
最后一步是使用测试集评估模型。这样做的代码类似于训练的代码,但是没有反向传播以及权重和偏差的更新。
结论
虽然急切执行很容易使用,但我想再次强调,这是一种低级的方法。我建议不要使用渴望执行,除非:1)你正在做的工作需要你从头开始构建深度学习模型(例如,关于深度学习模型的研究/学术工作),2)你正在试图理解深度学习背后正在发生的数学事情,或者 3)你只是喜欢从零开始构建东西。
话虽如此,但我认为急切执行是一种很好的方法,可以帮助你更好地理解我们进行深度学习时实际发生的事情,而不必摆弄复杂的图表或传统 TF 方法带来的其他令人困惑的东西。
我为这个例子创建的 Google Colab 笔记本可以在这里找到。
使用 Keras 的简单神经网络情感分析器

什么是情绪分析器?
想知道产品负责人如何在不阅读数以百万计的文章和评论的情况下,深入了解客户对其产品的反应吗?一个情绪分析器是答案,这些东西可以被快速部署,然后连接到 twitter、评论网站、数据库或所有上述网站,使我们能够了解互联网对我们的产品/服务/业务的总体感受。
这是一种非常懒惰的方式来理解一个产品是如何在很短的时间内被一大群顾客感知的。
Keras 是什么?
维基百科引用:“Keras 是一个用 Python 编写的开源神经网络库。它能够运行在 TensorFlow、微软认知工具包、Theano 或 PlaidML 之上。旨在实现深度神经网络的快速实验,它专注于用户友好、模块化和可扩展
对于那些会用 python 编码,但可能不太熟悉 TensorFlow 或 Theano 的人来说,这是一个很好的选择。
让我们开始吧…导入和格式化数据
对于这段简单的代码,我们将使用通常引用的 IMDB review 数据集,它可以直接从 Keras 中免费获得,它的使用如此频繁,甚至在他们的示例中也是如此。
我们需要做的第一件事是从 Keras 导入 IMDB 数据集,然后将其分成训练和测试数据集。
from keras.datasets import imdb top_words = 10000 (x_train, y_train), (x_test, y_test) = imdb.load_data(num_words=top_words)
我们应该会看到一个下载屏幕和一个警告,上面写着“正在使用 tensorflow backend”,这没什么问题,让它做它自己的事吧。

我们可以运行下面这段代码来看看数据集,它基本上是来自英语词典的单词和相应的 id。我们稍后将使用这些 id 进行翻译,因为神经网络更喜欢处理数字而不是纯文本。
imdb.get_word_index()

接下来的部分可能是唯一复杂的部分,它几乎是可选的——为了测试我们的数据看起来不错,我们需要抓住我们的第一次审查,并把关键内容转化为实际的单词。
word_dict = imdb.get_word_index()
word_dict = { key:(value + 3) for key, value in word_dict.items() } word_dict[''] = 0
# Padding word_dict['&amp;amp;gt;'] = 1
# Start word_dict['?'] = 2
# Unknown word reverse_word_dict = { value:key for key, value in word_dict.items() } print(' '.join(reverse_word_dict[id] for id in x_train[0]))
我说的是可选的,但是在现实世界中,你总是想要做这些检查。

Here is our first review reversed out into text.
接下来需要做一些整理,我们需要确保所有进入神经网络的东西都是相同的形状。也就是说,我们将指定评论的最大字符长度,并将其分配给变量 max_review_length,然后遍历我们的数据集,并用零填充任何评论的结尾,以确保所有内容的长度为 500 个字符。
from keras.preprocessing import sequence
max_review_length = 500
x_train = sequence.pad_sequences(x_train, maxlen=max_review_length) x_test = sequence.pad_sequences(x_test, maxlen=max_review_length)

If we print out our word dictionary, notice we just have a load of numbers? These all correspond to words in our word dictionary.
建立模型

模型如下所示。这是一段可重用的便捷代码。如果你真的想了解这一切是如何联系在一起的,在这个博客这里有大量关于单词嵌入的信息。
您将看到代码被分解以便于理解,并显示在一个完整的块中,以供检查或复制。
首先我们导入必要的包并设置一个可变的“嵌入向量长度”。单词嵌入提供了单词及其相关含义的密集表示。
#Build the Model from keras.models
import Sequential from keras.layers
import Dense from keras.layers.embeddings
import Embedding from keras.layers
import Flattenembedding_vector_length = 32
接下来定义模型:这告诉我们,我们正在使用一个顺序模型——即由许多层堆叠在一起构建的模型,在 Keras 文档中有很多关于这方面的信息。
model = Sequential()
我们添加了一个嵌入层——这是处理文本时使用的最佳实践——它将向量排列成表格,因此更容易处理大量数据。
model.add(Embedding(top_words, embedding_vector_length, input_length=max_review_length))
接下来,我们将输入数据展平,以便下一层使用
model.add(Flatten())
我们添加一些密集层,它们控制模型“如何”学习——前两层有 16 个神经元,我们可以在稍后改变这些神经元来改变模型的准确性。
model.add(Dense(16, activation='relu'))
model.add(Dense(16, activation='relu'))
Relu 是一个激活函数——在文档中有更多,但是现在我们只需要知道 Relu 是我们正在做的事情中最流行的。

The gradient of the ReLU function ensures that we don’t use all the neurons in the network at the same time, therefore its great for high performance deep learning.
最后的密集层是我们的输出层——它被设置为 1 个神经元,因为我们只想要一个整体的输出——情绪的分数!
model.add(Dense(1, activation='sigmoid'))
我们使用 sigmoid 作为我们的输出层激活函数,因为我们提供的输出是 0 到 1 之间的分数,取决于积极或消极的情绪。

接下来,我们需要编译或建模,这是有效地把它们粘在一起。不要进入太多的细节,优化器是我们的网络如何优化它的权重和它如何学习。你可能听说过“梯度下降”这个词,Adam 是它的变体。
model.compile(loss='binary_crossentropy',optimizer='adam', metrics=['accuracy'])
我们还可以改变衡量结果的方式。一般来说,我们使用“准确性”,但在 Keras 中有不同的说法。
我们在执行之前做的最后一件事是打印出模型——这将使我们能够检查我们对配置是否满意。
print(model.summary())

正如所承诺的,这里是完整的代码。
import Sequential from keras.layers
import Dense from keras.layers.embeddings
import Embedding from keras.layers
import Flattenmodel = Sequential()
model.add(Embedding(top_words, embedding_vector_length, input_length=max_review_length))
layer model.add(Flatten())
model.add(Dense(16, activation='relu'))
model.add(Dense(16, activation='relu'))
model.add(Dense(1, activation='sigmoid')) model.compile(loss='binary_crossentropy',optimizer='adam', metrics=['accuracy'])print(model.summary())
拟合模型
拟合模型——你可能在机器学习过程中遇到过这种情况。纪元编号是我们决定进行的迭代次数,意味着我们通过神经网络的次数。
注意,我们在同一行代码中用训练数据拟合,用测试数据验证?
批次大小 128 是随机的,通常较小的批次大小会更准确,但我们仍然需要注意过度拟合—稍后讨论。
hist = model.fit(x_train, y_train, validation_data=(x_test, y_test), epochs=5, batch_size=128)
运行代码,我们应该能够跟踪神经网络,通过你要求它做的任何数量的时期——在我的例子中是 5,所以 5 行结果。

These results are produced to track the progress of a neural network in Keras.
这就是我们需要小心过度拟合的地方。 如果我们看到 acc(训练精度)和 val_acc(测试集精度)值之间的精度相似,那么我们可能会有一个过拟合问题,对此的快速解决方法是记下我们完成的历元数。
模型分数
得分是固有的。我们所需要做的就是观想它!
import seaborn as sns
import matplotlib.pyplot as plt
%matplotlib inline
sns.set() val = hist.history['val_acc'] epochs = range(1, len(acc) + 1) plt.plot(epochs, val, ':', label='Validation accuracy') plt.title('Validation Accuracy') plt.xlabel('Epoch') plt.ylabel('Accuracy') plt.legend(loc='upper left')
plt.plot()

使用我们的代码
现在我们需要做的就是编写一个函数,允许我们提交自己的评论,并根据我们的模型进行分析!下面的代码可以做到这一点。
import string
import numpy as np def analyze(text):
# Prepare the input by removing punctuation characters, converting characters to lower case, and removing words containing numbers translator = str.maketrans('', '', string.punctuation)
text = text.translate(translator)
text = text.lower().split(' ')
text = [word for word in text if word.isalpha()] # Generate an input tensor input = [1] for word in text:
if word in word_dict and word_dict[word] &amp;lt;
top_words: input.append(word_dict[word])
else:
input.append(2)
padded_input = sequence.pad_sequences([input], maxlen=max_review_length) # Invoke the model and return the result result = model.predict(np.array([padded_input][0]))[0][0] return result
测试情感分析器
现在,我们可以使用自己的评论来测试神经网络。我们所要做的就是将我们的评论输入到我们刚刚创建的“分析”函数的参数中。
我们可以从下面我输入的基本评论中看到,非常强烈的评论得分很高,非常负面的评论得分很低,所以看起来我们的情绪分析器神经网络起作用了!

原载于 2019 年 5 月 19 日http://drunkendatascience.com。
python 中的简单绘图(因此您可以专注于更大的事情)

Today it’s all about having the numbers
TLDR :使用的纸笔在建造原型时快速启动你的绘图和洞察力
所以你有了新的实验想法💡。也许是一种新的强化学习算法?新哈希表?我不会再尝试猜测了:)为什么要猜测,当我确定你需要做一件事,无论这个想法是什么,来验证它——你需要绘制一些实验数据。所以你知道如何用 python 实现你的想法,而且你在一个令人惊讶的短时间内就做到了。现在是时候观察这个想法在一些不同的输入上的表现了。并查看性能是否可靠,或者每次运行代码时性能是否会发生变化。
那么,抽象地说,你将采取什么步骤来可视化你正在运行的过程?以下是我在实验中的想法:
- 定义我要测量的量' Y' 。示例:批次损失
- 定义 Y 所依赖的数量' x '(考虑时间步长或输入大小)
- 在我的代码中找到便于记录这些量的值的地方
- 重复几次实验,也许是针对系统的一些不同配置。
- 产生那些美丽而有见地的情节。
这是一个明确的计划。然而,很少有技术挑战逃过我们的注意:如果我们运行几次实验,以某种方式在图中平均结果并显示它们的方差/标准差是有意义的。
..此外,从长远来看,将所有信息存储在一个巨大的全球字典中可能是一件痛苦的事情。
…此外,这些噪声图可以使用一些过滤。
让我们看一个例子,看看我们如何解决它。让我们假设一些添加了噪声的数学函数是您正在开发的新原型:
结果,嗯,不是很好:

介绍pen ' n ' paper—一个简单方便绘图的 python 库;
**import** pennpaper **as** pp
metrics = **[]for** name, func **in** funcs.items***()***:
m = pp.Metric***(***name***)*** m.add_arrays***(***X, func***(***X***))*** metrics.append***(***m***)*** pp.plot_group***(***metrics, smoothen=**True*)***
好了,这给了我们一个有名字的传说,并且去掉了大部分的噪音:

接下来我想做的是,通过对过程进行多次采样,并将有关标准差的信息添加到图中,进一步降低噪声。为了简化代码,让我们从一个函数开始——正弦波。
我们得到了单独策划的罪恶:
**import** pennpaper **as** pp
name = **'sin'** func = funcs***[***name***]*** m1 = pp.Metric***(***name***)*** m1.add_arrays***(***X, func***(***X***))*** pp.plot***(***m1, smoothen=**True*)***

你可以做的是将纸笔指标相加:
m1 = pp.Metric***(***name***)*** m1.add_arrays***(***X, func***(***X***))*** m2 = pp.Metric***(***name***)*** m2.add_arrays***(***X, func***(***X***))*** m3 = pp.Metric***(***name***)*** m3.add_arrays***(***X, func***(***X***))*** pp.plot***(***m1 + m2 + m3, smoothen=**True*)***

似乎有 3 个度量标准有所帮助。让我们有 300?
metrics = ***[]* for** i **in** range***(***300***)***:
m = pp.Metric***(***name***)*** m.add_arrays***(***X, func***(***X***))*** metrics.append***(***m***)*** pp.plot***(***sum***(***metrics***)***, smoothen=**True*)***

大数定律在我们这边,我们得到相当精确的图。另请注意,阴影区域的大小代表标准差,与正弦波相差 2 个单位,这正是我们在噪声公式中输入的值:y += np.random.normal(0,2,size=y.size)。
让我们把我们所学的放在一起,画出所有 3 个函数,画出一个带平滑的图和一个不带平滑的图:
**import** pennpaper **as** pp
**from** collections **import** defaultdict
metrics = defaultdict***(***list***)* for** i **in** range***(***90***)***:
**for** name, f **in** funcs.items***()***:
m = pp.Metric***(***name***)*** m.add_arrays***(***X, f***(***X***))*** metrics***[***m.name***]***.append***(***m***)*** metrics = ***[***sum***(***v***)* for** v **in** metrics.values***()]*** pp.plot_group***(***metrics***)*** pp.plot_group***(***metrics, smoothen=**True*)***

No smoothing, just the effect of averaging 90 tries

Smooth version
总结:我们已经学会了如何使用 pen'n'paper Metric class 轻松组合多个测量值,并使用。plot()和。plot_many()方法。您可以使用以下方式安装笔和纸:
pip install pennpaper
给它一颗星🌟这里:https://github.com/ikamensh/pennpaper
这是一个我为个人使用而写的便利工具,但是我想你可能会发现它的用处!😃
简单强化学习:Q-学习

Typical Exploring Image for RL - Credit @mike.shots
简介
我在参加强化学习课程时学到的最喜欢的算法之一是 q-learning。可能是因为它对我来说最容易理解和编码,但也因为它似乎有意义。在这篇快速的帖子中,我将讨论 q-learning,并提供理解该算法的基本背景。
什么是 q 学习?
Q-learning 是一种非策略强化学习算法,它寻求在给定的当前状态下采取最佳行动。它被认为是不符合策略的,因为 q-learning 函数从当前策略之外的动作中学习,比如采取随机动作,因此不需要策略。更具体地说,q-learning 寻求学习一种使总回报最大化的策略。
什么是‘Q’?
q-learning 中的“q”代表质量。在这种情况下,质量代表了一个给定的行为在获得未来回报方面的有用程度。
创建一个 q 表
当进行 q-learning 时,我们创建一个所谓的 q-table 或遵循[state, action]形状的矩阵,并将我们的值初始化为零。然后,在一集之后,我们更新并存储我们的 q 值。这个 q 表成为我们的代理根据 q 值选择最佳行动的参考表。
import numpy as np# Initialize q-table values to 0Q = np.zeros((state_size, action_size))
Q-学习和更新
下一步只是让代理与环境交互,并更新我们的 q 表Q[state, action]中的状态动作对。
采取行动:探索或利用
代理以两种方式之一与环境交互。第一种是使用 q 表作为参考,并查看给定状态的所有可能动作。然后,代理根据这些操作的最大值选择操作。这就是所谓的 利用 ,因为我们利用现有的信息做出决定。
第二种采取行动的方式是随机行动。这叫做 探索 。我们不是根据最大未来回报来选择行动,而是随机选择行动。随机行动很重要,因为它允许代理探索和发现新的状态,否则在利用过程中可能不会被选择。您可以使用 epsilon ( ε )来平衡探索/利用,并设置您希望探索和利用的频率值。这里有一些粗略的代码,将取决于如何设置状态和动作空间。
import random# Set the percent you want to explore
epsilon = 0.2if random.uniform(0, 1) < epsilon:
"""
Explore: select a random action """
else:
"""
Exploit: select the action with max value (future reward) """
更新 q 表
更新发生在每个步骤或动作之后,并在一集结束时结束。在这种情况下,完成意味着代理到达某个端点。例如,终端状态可以是任何类似于登录到结帐页面、到达某个游戏的结尾、完成某个期望的目标等的状态。在单个情节之后,代理将不会学到太多,但是最终通过足够的探索(步骤和情节),它将收敛并学习最优 q 值或 q 星(Q∗)。
以下是 3 个基本步骤:
- 代理从一个状态(s1)开始,采取一个动作(a1)并收到一个奖励(r1)
- 代理通过随机(epsilon,ε)引用具有最高值(max) 或的 Q 表来选择动作
- 更新 q 值
以下是 q-learning 的基本更新规则:
# Update q valuesQ[state, action] = Q[state, action] + lr * (reward + gamma * np.max(Q[new_state, :]) — Q[state, action])
在上面的更新中,有几个变量我们还没有提到。这里发生的是,我们根据贴现后的新值和旧值之间的差异来调整 q 值。我们使用 gamma 来贴现新值,并使用学习率(lr)来调整步长。以下是一些参考资料。
学习率: lr或学习率,常被称为α或α,可以简单定义为你接受新值相对于旧值的程度。上面我们取新旧之间的差值,然后用这个值乘以学习率。这个值然后被加到我们先前的 q 值上,这实质上使它朝着我们最新更新的方向移动。
γ:gamma或 γ 是贴现因子。它用于平衡当前和未来的奖励。从上面的更新规则中,您可以看到我们将折扣应用于未来的奖励。通常,该值可以在 0.8 到 0.99 的范围内。
奖励: reward是在给定状态下完成某个动作后获得的数值。奖励可以发生在任何给定的时间步或只在终端时间步。
Max: np.max()使用 numpy 库,并获取未来奖励的最大值,并将其应用于当前状态的奖励。这是通过未来可能的回报来影响当前的行为。这就是 q-learning 的妙处。我们将未来奖励分配给当前行动,以帮助代理在任何给定状态下选择最高回报的行动。
结论
就这样,简短而甜蜜(希望如此)。我们讨论过 q-learning 是一种非策略强化学习算法。我们使用一些基本的 python 语法展示了 q-learning 的基本更新规则,并回顾了算法所需的输入。我们了解到,q-learning 使用未来的奖励来影响给定状态下的当前行为,从而帮助代理选择最佳行为,使总奖励最大化。
关于 q-learning 还有很多,但希望这足以让你开始并有兴趣学习更多。我在下面添加了几个资源,它们对我学习 q-learning 很有帮助。尽情享受吧!
资源
- 使用 OpenAI Gym taxi 环境的绝佳 RL 和 q 学习示例
- 强化学习:导论(萨顿的免费书籍)
- Quora Q 学习
- 维基百科 Q-learning
- 大卫·西尔弗关于 RL 的讲座
基于 Fast.ai 随机森林的简单大豆价格回归

image credit: Pexels.com
将前沿机器学习应用于商品价格。
作为一名对农业感兴趣的 fast.ai 机器学习 for Coders MOOC 的学生,想到的 fast.ai 随机森林回归库的第一个应用是根据历史数据预测大豆价格。大豆是一种全球大宗商品,在过去十年里,其每蒲式耳的价格每天都在大幅波动。
个别商品的价格历史可以作为简单的结构化表格数据在网上免费获得,这使得这成为一个直截了当的开头话题。下面是代码——注意我们使用的是 Python 3 和 fast.ai 0.7,所以请遵循安装说明。
首先,我们需要在开始之前导入我们的包:fast.ai、quandl、pandas、sk learn——常见的数据科学库。
%load_ext autoreload
%autoreload 2
%matplotlib inlineimport quandlfrom fastai.imports import *
from fastai.structured import *from pandas_summary import DataFrameSummary
from sklearn.ensemble import RandomForestRegressor, RandomForestClassifier
from IPython.display import displayfrom sklearn import metrics
数据来自免费的 Quandl TF grain/大豆数据集。Quandl 为他们的 API 提供了一个简单的 Python SDK,即入门指南。下拉整个数据集只有一行(很短):
data = quandl.get("TFGRAIN/SOYBEANS", authtoken="<your token")
这将返回一个 Pandas 数据帧(我们称之为“数据”),并且data.info()显示有 4535 行数据被事件日期时间索引。我们可以用data.tail()来表示格式:
<class 'pandas.core.frame.DataFrame'>
DatetimeIndex: 4535 entries, 2000-12-01 to 2019-01-14
Data columns (total 4 columns):
Cash Price 4535 non-null float64
Basis 4535 non-null float64
Fall Price 4516 non-null float64
Fall Basis 4516 non-null float64
dtypes: float64(4)
memory usage: 177.1 KB

data.head()
由于我们有日期时间,这是一个很好的机会来利用 fast.ai 库的日期处理功能工程能力。我将索引重命名为“data ”,并创建了一个新列,其中包含可以由add_datepart()函数处理的相同值:
data.rename_axis('Date')
data['Date'] = data.index
add_datepart(data, 'Date')
data.info()
新列如下。杰瑞米·霍华德在 ML1 第一课中深入解释了这些新列是如何创建的,以及为什么创建。
<class 'pandas.core.frame.DataFrame'>
DatetimeIndex: 4535 entries, 2000-12-01 to 2019-01-14
Data columns (total 18 columns):
id 4535 non-null int64
Cash Price 4535 non-null float64
Basis 4535 non-null float64
Fall Price 4516 non-null float64
Fall Basis 4516 non-null float64
Year 4535 non-null int64
Month 4535 non-null int64
Week 4535 non-null int64
Day 4535 non-null int64
Dayofweek 4535 non-null int64
Dayofyear 4535 non-null int64
Is_month_end 4535 non-null bool
Is_month_start 4535 non-null bool
Is_quarter_end 4535 non-null bool
Is_quarter_start 4535 non-null bool
Is_year_end 4535 non-null bool
Is_year_start 4535 non-null bool
Elapsed 4535 non-null int64
dtypes: bool(6), float64(4), int64(8)
为了简单起见,我在大豆价格回归中第一次删除了一些列。我只保留 col_list 变量中列出的列。
col_list = ['Cash Price', 'Basis', 'Year', 'Month', 'Week', 'Day', 'Dayofweek', 'Dayofyear']dfdata = dfdata[col_list]
为了清理格式,我从 Pandas 数据帧中提取值并重新导入:
df = dfdata.values
df = pd.DataFrame.from_records(df)
df.columns = col_list
现在我们又有了一个 Pandas dataframe,我使用了另一个方便的 fast.ai 函数,它(如文档所解释的那样)将字符串列转换为分类值列,并就地完成。为什么?允许随机森林回归器理解表格数据。同样,这将在本课程中更深入地讨论。
train_cats(df)
另一个 fast.ai 预处理器(如文档所述)获取数据帧,分离响应变量,并将测向转换为全数字数据帧。我们的因变量(‘y’)是这个谷物升降机的大豆每日现金价格,其他一切都是自变量。
df, y, nas = proc_df(df, 'Cash Price')
现在我们已经准备好适应我们的数据了!这很简单,因为:
m = RandomForestRegressor(n_jobs=-1)
m.fit(df, y)
m.score(df,y)Score: 0.9991621993655437
相当不错!Fast.ai 对此的准确率为 99.91%,因此现在我们可以向回归器投掷一个测试数据帧(具有相同的格式),并获得实际的预测。这是我们的单行测试数据框架:
df1 = pd.DataFrame({'Basis':[-.85],
'Year':[2019],
'Month':[1],
'Week':[4],
'Day':[25],
'Dayofweek':[6],
'Dayofyear':[25],})

并获得对 2019 年 1 月 25 日的价格的单一预测,给定 85 美分的基数:
train_cats(df1)
m.predict(df1) Return: array([8.465])
一蒲式耳大豆估计售价为 8.465 美元。
为了好玩,我画了一张图,显示基差的变化如何影响一天(2019 年 1 月 25 日)的回归。代码:
df1.iloc[0:, 0] = -.95
iterpreds = list()
a = list()
for x in range(0,10):
df1.iloc[0, 0] += .1
b = df1.at[0, 'Basis']
iterpreds.append(m.predict(df1).astype(float).item(0))
a.append(b)plt.plot(a, iterpreds)

basis on x axis, price on y
可以做得更多!我们应该扩展我们的模型,以包括所有可用的列,看看是否有第二种商品可以添加到模型中,以探索其他作物价格是否同步移动,并绘制随着时间的推移其他变量的变化的影响。
参考资料:
[1]https://course.fast.ai/ml.html
[2]https://forums . fast . ai/t/fastai-v 0-7-install-issues-thread/24652
[3]https://docs.quandl.com/docs/getting-started
附加代码在我的 github 上:www.github.com/matthewarthur,我的 LinkedIn 是https://www.linkedin.com/in/matt-a-8208aaa/。
运动鞋品牌的简单画面:“Yeezy”和“Off-White”

自本世纪初以来,收集和转售运动鞋一直是我的一个爱好。就个人而言,我喜欢这些运动鞋带来的故事和独特性,但作为一名高中生赚点外快也是一个好处。在我成长的过程中,我一直认为运动鞋市场和收集专属运动鞋的爱好是“城市”的事情,集中在纽约和洛杉矶这样的地区,所以我从来没有想到运动鞋行业和转售市场有一天会成为主流。像 StockX 和 Goat 这样的电子商务网站已经允许新兴市场在曾经无法获得独家发行的城市中发展。
运动鞋行业一直在快速增长,2016 年的估值为 550 亿美元。到 2025 年,该市场预计将达到 950 亿美元。根据 StockX 的数据,运动鞋转售市场目前的估值为 60 亿美元。这种大规模繁荣的一些原因包括社交媒体和运动员/名人代言的力量。社交媒体让品牌获得了更多的曝光率,从而提高了它们的知名度和需求。与运动员和音乐家的合作数量也增加了,这使得新的品牌出现和发展。虽然像耐克和乔丹这样的主要品牌在历史上取得了大部分的成功,但像 Yeezy 和 Off-White 这样的其他品牌可能会有更多的市场控制力,并标志着运动鞋界的一个支点。这些品牌正在蓬勃发展,因为消费者发现它们更有创意,更有个性。
为了更好地理解这两个新兴品牌,我使用了一个名为 Tableau 的数据可视化工具。我研究了一个 StockX 数据集,其中有他们从 2016 年到 2018 年对 Yeezy 和 Off-White 品牌的每笔交易的销售信息。然后,我创建了一些视觉效果,查看收入、销售数量和地区。最后,我将所有的视觉效果整合到一个交互式仪表盘中,提供了进一步的洞察力。这是 Tableau 更具创新性的特点之一。根据你的喜好,视觉效果可以过滤掉,并与其他人互动。
下面是一些仪表盘的截图。我还将展示当你与它们互动时,视觉效果是如何变化的:

以上图片提供了品牌和所有地区的信息。销售额最高的是加利福尼亚和纽约。总的销售价格总计超过 3000 万美元。同样值得注意的是,其他地区也能买到只有在大市场才能买到的产品。虽然主要城市仍然拥有最高的销售数量,但其他地区现在可以购买像 Yeezy 和 Off-White 这样的产品和品牌,这在十年前是闻所未闻的。电子商务网站也可以通过吸引这些地区来发展业务。电子商务使运动鞋和转售市场民主化,这也是这个行业高速发展的原因。

如你所见,当我只选择品牌 Yeezy 时,视觉效果会进行交互,并只向我提供该品牌的信息。我可以清楚地看到,考虑到所有品牌,加州的销售额约为 13,000,仅 Yeezy 品牌的销售额就约为 8800。互动的视觉效果提供了一个清晰和说明性的方式来看到这一点。此外,在比较这两个品牌时,我现在可以看到大部分钱花在了哪里。仅 Yeezy 品牌就花费了近 2000 万美元。

当我只选择纽约地区时,Yeezy 品牌的总销售额大约是 300 万美元,而 Off-White 大约是 230 万美元。此外,根据预测模型,纽约地区的销售价格将保持不变。
Tableau 是对数据集进行探索性分析的好方法。它允许您创建仪表板,以有趣且易于使用的方式提供有价值的见解。
一旦你在 Tableau 中创建了你的视觉效果并将它们添加到一个仪表板,你可以通过使用位于每个视觉效果右上角的过滤方法来使视觉效果相互作用。您只需点击想要相互交流的图表上的过滤图标。
简单文本生成
如何教机器“说话”

Photo by Kaitlin Dowis on Unsplash
如果你想看完整的代码,这里有 Github 链接。
目标
在本文中,我将简要介绍一种使用 Keras 和 Tensorflow 在 Python 中编码和训练文本生成模型的简单方法。我们的目标是训练一个模型来模仿它被训练的文本的说话风格。在这种情况下,我们的数据集是埃德加·爱伦·坡恐怖故事中的 7009 句话。
整理训练数据
训练任何 NLP 模型的第一步是单词的标记化。这里我们使用 Keras Tokenizer,它执行以下操作:
- 删除标点符号
- 将所有文本设置为小写
- 将单词拆分成列表中的单个元素,然后为每个单词分配一个唯一的整数
- 用整数替换该单词的所有实例。
令牌化是为嵌入层准备数据所必需的(参见下面的模型架构部分)
使用滑动窗口设置培训数据。我们正在使用一个相当基本的策略,使用前面的 19 个单词来预测第 20 个单词。这些数字的选择没有任何具体的理由,肯定可以修改以改善。
模型架构
1。嵌入层
嵌入层是任何寻求理解单词的深度学习模型的关键层。从数学的角度来看,嵌入层所做的是将向量从高维空间(数万或更多,我们的 vocab 的原始大小)带到低维空间(我们希望用来表示数据的向量的数量,在 Word2Vec、Fasttext 等模型中通常为 100–300)。
然而,它以这样一种方式做到了这一点,即具有相似含义的单词具有相似的数学值,并且存在于与其含义相对应的空间中。可以对这些向量进行数学运算,例如,‘王’减去‘人’可能等于‘皇族’。
2。两个堆叠的 LSTM 层
我不打算在这里深入 LSTM 的细节,但有大量的好文章来看看他们是如何工作的。一些需要注意的事项:
- 康奈尔大学的这些人表示,当应用于语音识别时,堆叠的 LSTM 可能比单个 LSTM 层中的附加单元增加更多的深度。虽然我们的应用程序不完全相同,但它在使用 LSTM 的程序来尝试识别语言模式方面是相似的,所以我们将尝试这种架构。
- 第一 LSTM 层必须将
return sequences标志设置为真,以便将序列信息传递给第二 LSTM 层,而不仅仅是其结束状态
3。具有 ReLU 激活的密集(回归)层
LSTM 的输出是“隐藏层”。通常在 LSTM 后应用密集图层来进一步捕捉线性关系,但这并不是明确必需的。
4。激活 Softmax 的密集层
这一层对于将上面几层的输出转换成我们整个词汇表中的实际单词概率是必要的。它使用了 softmax 激活函数,这是一个将所有输入单词概率从(-∞,∞)转换为(0,1)的函数。这允许我们选择或生成最可能的单词。
一旦我们建立了我们的模型架构,我们就可以训练它了!
(注意,我们可以使用检查点来保持我们的进度,以防我们的训练被中断。)
部署
训练完成后,我们现在有了一个可以生成文本的模型。然而,我们需要给它一个起点。为此,我们编写了一个函数,它接受一个字符串输入,将其标记化,然后用零填充它,以便它适合我们 19 长的预测窗口。
结果
下面是给定输入字符串后的结果。注意,我们没有用任何标点符号训练我们的模型,所以句子的划分留给读者去解释。
Input String: First of all I dismembered the corpse.
Model 3 :
first of all i dismembered the corpse which is profound since in the first place he appeared at first so suddenly as any matter no answer was impossible to find my sake he now happened to be sure it was he suspected or caution or gods and some voice held forth upon view the conditions
虽然这不是最有意义的,但它实际上似乎是可读的,并且在正确的位置有正确的单词类型。给定一组更大的训练数据,这种方法和模型可以用来生成更容易理解的文本(当然,最有可能的是通过一些参数调整)。
为了量化性能,您可以使用原始数据实现典型的训练测试分割,并根据预测和目标之间的余弦相似性对预测进行评级。
谢谢大家!
今天就到这里,感谢阅读。如果你想了解更多,这里有一些我做过的其他机器学习项目,你可以阅读一下:
简单的变压器——介绍使用伯特、罗伯塔、XLNet 和 XLM 的最简单方法
想要为 NLP 使用 Transformer 模型吗?一页页的代码让你沮丧?不再是了,因为简单的变形金刚正在工作。只需 3 行代码就可以启动、训练和评估变形金刚!

前言
简单的变形金刚库是通过拥抱脸作为优秀的 T2 变形金刚库的包装而构建的。我永远感谢在拥抱脸的人们所做的艰苦工作,使公众能够方便地访问和使用变形金刚模型。没有你们我真不知道该怎么办!
介绍
我相信可以公平地说,Transformer 模型的成功在推进自然语言处理领域取得了惊人的成就。他们不仅在许多他们被设计解决的 NLP 任务上表现出惊人的飞跃,预先训练过的变形金刚在迁移学习上也表现得出奇的好。这意味着任何人都可以利用训练这些模型的长时间和令人难以置信的计算能力来执行无数种 NLP 任务。你不再需要谷歌或脸书的雄厚财力来构建一个最先进的模型来解决你的 NLP 问题了!
或者人们可能希望如此。事实是,让这些模型发挥作用仍然需要大量的技术知识。除非你在深度学习方面有专业知识或至少有经验,否则这似乎是一个令人生畏的挑战。我很高兴地说,我以前关于变形金刚的文章(这里是这里是和这里是)似乎已经帮助了很多人开始使用变形金刚。有趣的是,我注意到不同背景的人(语言学、医学、商业等等)都在尝试使用这些模型来解决他们自己领域的问题。然而,为了使变压器适应特定的任务,需要克服的技术障碍并非微不足道,甚至可能相当令人沮丧。
简单变压器
这个难题是我决定开发一个简单的库来使用 Transformers 执行(二进制和多类)文本分类(我见过的最常见的 NLP 任务)的主要动机。我们的想法是让它尽可能简单,这意味着抽象出许多实现和技术细节。库的实现可以在 Github 上找到。我强烈建议您查看它,以便更好地了解一切是如何工作的,尽管使用该库并不需要了解内部细节。
为此,我们编写了简单的 Transformers 库,只需 3 行代码就可以初始化 Transformer 模型,在给定的数据集上进行训练,并在给定的数据集上进行评估。让我们看看这是怎么做的,好吗?
装置
- 从这里安装 Anaconda 或 Miniconda 包管理器
- 创建新的虚拟环境并安装所需的软件包。
conda create -n transformers python pandas tqdm
conda activate transformers
如果使用 cuda:
conda install pytorch cudatoolkit=10.0 -c pytorch
其他:
conda install pytorch cpuonly -c pytorch
conda install -c anaconda scipy
conda install -c anaconda scikit-learn
pip install transformers
pip install tensorboardx - 安装简单变压器。
使用
快速浏览一下如何在 Yelp 评论数据集上使用这个库。
- 下载 Yelp 评论数据集。
- 提取
train.csv和test.csv并放入目录data/中。
(Bash 用户可以使用 这个脚本 来下载数据集)
这里没有什么特别的,我们只是以正确的形式得到数据。对于任何数据集,这都是您必须做的。
- 为训练和评估部分创建两个熊猫
DataFrame对象。 - 每个
DataFrame应该有两列。第一列包含您想要训练或评估的文本,数据类型为str。第二列有相应的标签,数据类型为int。
更新:现在建议将列命名为*labels*和*text*而不是依赖于列的顺序。
数据整理好了,就该训练和评估模型了。
就是这样!
为了对其他文本进行预测,TransformerModel附带了一个predict(to_predict)方法,它给出了一个文本列表,返回模型预测和原始模型输出。
有关所有可用方法的更多详细信息,请参见 Github repo 。repo 还包含一个使用该库的最小示例。
默认设置以及如何更改它们
下面给出了使用的默认参数。这些都可以通过将包含相应键/值对的 dict 传递给 TransformerModel 的 init 方法来覆盖。(见下面的例子)
self.args = {
'model_type': 'roberta',
'model_name': 'roberta-base',
'output_dir': 'outputs/',
'cache_dir': 'cache/', 'fp16': True,
'fp16_opt_level': 'O1',
'max_seq_length': 128,
'train_batch_size': 8,
'eval_batch_size': 8,
'gradient_accumulation_steps': 1,
'num_train_epochs': 1,
'weight_decay': 0,
'learning_rate': 4e-5,
'adam_epsilon': 1e-8,
'warmup_ratio': 0.06,
'warmup_steps': 0,
'max_grad_norm': 1.0, 'logging_steps': 50,
'evaluate_during_training': False,
'save_steps': 2000,
'eval_all_checkpoints': True,
'use_tensorboard': True, 'overwrite_output_dir': False,
'reprocess_input_data': False,
}
要覆盖其中任何一个,只需向 TransformerModel 传递一个带有适当的键/值对的字典。
关于每个参数的解释,请参考 Github repo 。
更新:当前参数见 文档 。
结论
那都是乡亲们!据我所知,使用变压器模型的最简单方法。
简单变压器——变压器模型的命名实体识别
简单的变形金刚是“它只是工作”的变形金刚库。使用 Transformer 模型进行命名实体识别,只需 3 行代码。是的,真的。

Photo by Brandi Redd on Unsplash
前言
简单变形金刚库是为了使变形金刚模型易于使用而设计的。变形金刚是非常强大的(更不用说巨大的)深度学习模型,在处理各种各样的自然语言处理任务方面取得了巨大的成功。Simple Transformers 使 Transformer 模型的应用程序能够用三行代码完成序列分类任务(最初是二进制分类,但不久后增加了多类分类)。
我很高兴地宣布,Simple Transformers 现在支持命名实体识别,这是另一个常见的 NLP 任务,以及序列分类。
其他功能链接:
简单变形金刚库是在优秀的变形金刚库的基础上,通过抱脸的方式构建的。拥抱脸变形金刚图书馆是为研究人员和其他需要广泛控制事情如何完成的人准备的图书馆。当你需要偏离常规,做不同的事情,或者完全做新的事情时,这也是最好的选择。简单的变形金刚简单多了。
介绍
你想尝试这个绝妙的想法,你想卷起袖子开始工作,但是成千上万行看起来神秘(但很酷)的代码甚至会让一个资深的 NLP 研究员感到害怕。简单变形金刚背后的核心思想是,使用变形金刚并不需要很困难(或令人沮丧)。
简单的转换器抽象出所有复杂的设置代码,同时尽可能保留灵活性和配置空间。Transformer 模型只需要三行代码,一行用于初始化,一行用于训练,一行用于评估。
这篇文章演示了如何使用简单的变压器执行 NER。
所有源代码可在Github Repo上获得。如果您有任何问题或疑问,这是解决它们的地方。请务必检查一下!
装置
- 从这里安装 Anaconda 或 Miniconda 包管理器
- 创建新的虚拟环境并安装所需的软件包。
conda create -n transformers python pandas tqdm
conda activate transformers
如果使用 cuda:
conda install pytorch cudatoolkit=10.0 -c pytorch
其他:
conda install pytorch cpuonly -c pytorch
conda install -c anaconda scipy
conda install -c anaconda scikit-learn
pip install transformers
pip install tensorboardxpip install seqeval - 安装简单变压器
pip install simpletransformers
使用
为了演示命名实体识别,我们将使用 CoNLL 数据集。获得这个数据集可能有点棘手,但我在 Kaggle 上找到了一个版本,它可以满足我们的目的。
数据准备
- 从 Kaggle 下载数据集。
- 将文本文件解压到
data/目录。(它应该包含 3 个文本文件train.txt, valid.txt, test.txt。我们将使用train和test文件。您可以使用valid文件来执行超参数调整,以提高模型性能。
简单变形金刚的 NER 模型既可以使用 *.txt* 文件,也可以使用熊猫 *DataFrames* 。关于 *DataFrames* 的使用示例,请参考 回购 单据中的 NER 最小启动示例。
使用自己的数据集时,输入文本文件应该遵循 CoNLL 格式。文件中的每一行都应该包含一个单词及其相关的标记,每个标记之间用一个空格隔开。简单的转换器假定一行中的第一个“单词”是实际的单词,一行中的最后一个“单词”是它的指定标签。为了表示一个新的句子,在前一个句子的最后一个单词和下一个句子的第一个单词之间添加一个空行。然而,在使用定制数据集时,使用DataFrame方法可能更容易。
神经模型
我们创建了一个NERModel,可用于 NER 任务中的训练、评估和预测。一个NERModel对象的完整参数列表如下。
model_type:型号类型(伯特、罗伯塔)model_name:默认的变压器模型名称或包含变压器模型文件的目录路径(pytorch_nodel.bin)。labels(可选):所有命名实体标签的列表。如果没有给出,将使用["O "," B-MISC "," I-MISC "," B-PER "," I-PER "," B-ORG "," I-ORG "," B-LOC "," I-LOC"]。args(可选):如果未提供此参数,将使用默认参数。如果提供的话,它应该是一个包含应该在默认参数中更改的参数的字典。use_cuda(可选):如果可用,使用 GPU。设置为 False 将强制模型仅使用 CPU。
要加载一个先前保存的模型而不是默认模型,您可以将model_name 更改为包含已保存模型的目录路径。
model = NERModel(‘bert’, ‘path_to_model/’)
一个NERModel包含一个 python 字典args,它有许多属性提供对超参数的控制。在回购文件中提供了每一个的详细描述。默认值如下所示。
当创建一个NERModel或调用它的train_model方法时,只要传入一个包含要更新的键值对的dict就可以修改这些属性。下面给出一个例子。
训练模型
正如承诺的那样,训练可以在一行代码中完成。
train_model方法将在每第 n 步创建一个模型的检查点(保存),其中 n 是self.args['save_steps']。训练完成后,最终模型将保存到self.args['output_dir']。
加载保存的模型如下所示。
评估模型
同样,评估只是一行代码。
这里,三个返回值是:
result:包含评估结果的字典。(eval_loss,precision,recall,f1_score)model_outputs:原始模型输出列表preds_list:预测标签列表
这里给出我得到的评测结果,供参考。
{'eval_loss': 0.10684790916955669, 'precision': 0.9023580786026201, 'recall': 0.9153082919914954, 'f1_score': 0.9087870525112148}
对于使用默认超参数值的单次运行来说,还不算太差!
把所有的放在一起
预测和测试
在实际应用中,我们通常不知道真正的标签应该是什么。要对任意示例执行预测,可以使用predict方法。这个方法与eval_model方法非常相似,除了它接受一个文本列表并返回一个预测列表和一个模型输出列表。
predictions, raw_outputs = model.predict(["Some arbitary sentence"])
包扎
Simple Transformers 提供了一种快速简单的方法来执行命名实体识别(以及其他令牌级分类任务)。借用伯特背后的人的一句台词,简单变形金刚“概念简单,经验丰富”。
用任何分类器训练有序回归的简单技巧
在这篇文章中,我将展示一个简单的方法来处理任何序数回归(序数分类)问题,使用任何现有的二进制分类算法,该方法是由 Eibe Frank 和 Mark Hal 开发的。
原始论文
(Eibe Frank 和 Mark Hal,lECML 2001。第 12 届欧洲会议)https://www . cs . waikato . AC . NZ/~ eibe/pubs/ordinal _ tech _ report . pdf
机器学习的实际应用有时涉及目标值在不同类别之间显示顺序的情况。然而,标准分类算法不能利用这种排序信息,因为它们将每个类属性视为一组无序值[1]。
本文介绍了一个简单的技巧,它能使任何标准分类算法利用类属性中的排序信息。该论文还表明,这个简单的技巧优于天真的分类方法,天真的分类方法将每个类视为一组无序的值。
问题定义
(维基百科)在统计中,有序回归(也称为有序分类)是回归分析的一种,用于预测有序变量,即一个变量的值存在于任意尺度上,其中只有不同值之间的相对顺序是有意义的。
有序回归问题的一些例子是预测人类偏好(强烈不同意到强烈同意),预测温度(热、温和、冷),预测书/电影评级(1 到 5)。
一些可能的方法
解决序数回归问题的一些简单而天真的方法是
- 将其视为回归问题
如果序数值表示间隔或比率,并且我们有原始间隔/比率值,我们可以将其视为回归问题
我们将回归算法拟合到相应的间隔/比率值,该值可以映射到实际的序数值
缺点:如果序数值不表示任何连续的间隔/比率(如书籍/电影评级),或者如果我们没有原始间隔/比率值,我们就不能使用这种方法。 - 将其视为标准分类问题
我们将每个序数值视为一个无序集,并在其上安装一个多类分类算法
缺点:丢失每个类的排序信息,因为标准分类算法将每个类视为一组无序值
提议的方法
只要我们使用能够估计输出类别概率的分类器,所提出的方法将会工作
我们可以通过将一个 k 类有序回归问题转化为一个 k-1 二进制分类问题来利用有序类值,我们将带有有序值 V1、V2、V3、… Vk 的有序属性 A转化为 k-1 个二进制属性,每个属性对应于原始属性的前 k-1 个值。第 I 个二元属性表示测试 A > Vi [1]
例如,如果我们有一个具有从 1 到 5 顺序值电影评级的预测目标,我们可以将其转换成 4 个二进制分类问题,使得
- 如果等级> 1,二元目标为 1,因此分类器将预测 Pr(目标> 1)
- 如果等级> 2,二元目标为 1,因此分类器将预测 Pr(目标> 2)
- 如果等级> 4,二元目标为 1,因此分类器将预测 Pr(目标> 3)
- 如果等级> 4,二元目标为 1,因此分类器将预测 Pr(目标> 4)

Example of the transformation to binary classification problem is done
在我们训练了 4 个二元分类器之后,我们可以使用它来预测序数值的概率,方法是:
Pr(y=1) = 1-Pr(目标> 1)
Pr(y=2) = Pr(目标> 1)-P(目标> 2)
Pr(y=3) = Pr(目标> 2)-P(目标> 3)
Pr(y=4) = Pr(目标> 3)-P(目标> 4)
Pr(y=5) = Pr(目标【T6
或者一般来说,对于第一个序数值,我们可以使用第一个分类器来预测V1**=1—Pr(y>V1) 的概率,我们可以使用最后一个分类器来预测
Vk**= Pr(Target>Vk-1) 的概率,我们可以使用中间分类器来预测
Vi 的概率

这样我们就不会丢失来自类标签的排序信息。
Python 实现
我们通过创建OrdinalClassifier类来实现上述技巧,当调用fit时,该类将训练 k-1 个二元分类器,并且如果调用predict,将返回预测的类。在训练(fit)阶段OrdinalClassifier会将每个训练好的二进制分类器存储到一个 python 字典中。
定义我们的 OrdinalClassifier 的参数是
clf: 任何在其中实现predict_proba方法的 sklearn 分类器
举例来说,使用 max_depth = 3 的决策树创建一个 OrdinalClassifier 应该是:
clf = OrdinalClassifier(DecisionTreeClassifier(max_depth=3))
python 中实现的顺序分类器示例:
- init: 构造函数,简单地定义我们的
OrdinalClassifier并初始化clfs作为一个字典,它将用于存储我们的 k-1 二进制分类器 - 拟合:首先我们存储每个可用的唯一标签,然后对于第一个 k-1 值,对于每次迭代,我们将其标签转换为表示测试 A* > Vi
binary_y = (y > self.unique_class[i].astype(np.uint8))的二进制标签,然后我们在转换后的标签上拟合一个新的分类器(binary_y)最后,将我们的分类器存储到以i为关键字的clfs字典中 - predict_proba :要获得每个类的预测概率:首先我们从存储在
clfs上的所有分类器中获得所有的预测概率,然后简单地枚举所有可能的类标签并将其预测添加到我们的predicted列表中,之后,将其作为 numpy 数组返回 - 预测:要获得预测的类,只需使用我们
predict_proba的argmax
结论
我们可以通过使用一个简单的技巧来解决一个序数分类问题,即把序数分类分成 k-1 个二进制分类
参考
[1]艾贝·弗兰克和马克·霍尔,序数分类法的一种简单方法(2001),2001 年。第 12 届欧洲会议
蒸馏伯特的简单教程
基于 BERT 知识构建特定任务模型

Photo by Iker Urteaga on Unsplash
2019 年 12 月 4 日—更新:这种方法的实验代码可在这里获得。
关于伯特
伯特和变形金刚,总的来说,是 NLP 的一个全新步骤。它是由谷歌在 2018 年推出的,自那以来,它在不同的语言理解任务中表现出了最先进的结果。在https://papers with code . com/paper/Bert-pre-training-of-deep-bidirectional查看他们的论文和结果。
但是有一点美中不足。在生产中很难使用 BERT。 BERT-base 包含 110M 参数。较大的变型 BERT-large 包含 340M 参数。这样大的神经网络在实践中是有问题的。由于大量的参数,在移动设备等资源受限的系统中部署 BERT 非常困难。此外,低推理时间使其在实时系统中无用。这就是为什么找到让它更快的方法如此重要。
当我第一次面对变形金刚时,尝试用它们来完成日常任务是非常诱人的。文本分类就是其中之一。但是如何克服我上面写的局限性呢?在这篇文章中,我想展示一种简单而有效的方法来训练一个特定于任务的分类模型,该模型与基于 BERT 的模型在同一水平上执行。
知识的升华
有几种可能的方法可以加速 BERT。我强烈推荐阅读加速 BERT,其中包含了完整的概述。蒸馏就是其中之一。
里奇·卡鲁阿纳和他的同事描述了知识的升华。想法很简单:训练一个小的“学生”模型,模仿“教师”模型的行为。
样本问题
假设我们有一个常规的二进制文本分类问题。为了简单起见,X是句子列表,y是标签列表(0 或 1)。额外的细节对于描述主旨并不重要。
训练伯特
我就不描述微调 BERT 的全过程了:参考本帖详细了解这个过程。让我们想象一下,我们已经用来自变形金刚库的num_labels=2训练了BertForSequenceClassification。这个模型将是我们想要将其知识转移到“学生”模型的“老师”。
让我们构建“学生”模型
首先,我们将训练一个小型的 BiLSTM 模型作为基线。
输入数据是词汇表中的单词索引。为了构建词汇表,我使用了torchtext包中的内置功能。这些函数将帮助我们将训练数据集中的单词翻译成单词索引。
为了简单起见,我没有使用任何预先训练的单词嵌入。我们的模型将在训练过程中学习投射到内部空间。
模型
我们的模型看起来像:
列车基线
对于这个特定的基线模型,我们设置output_dim=1,因为我们有一个二元分类,因此损失函数是 logloss。PyTorch 有BCEWithLogitsLoss类,它结合了 sigmoid 函数和二元交叉熵:
一个纪元是:
每个时期后的评估:
使用一些辅助函数进行培训的完整代码如下:
蒸馏
这个特别的想法最初来自于论文“ 从 BERT 中提取特定于任务的知识到简单的神经网络 ”。
我们的“学生”模型必须学会模仿 BERT 模型的行为。但什么是“行为”?在我们的例子中,它是训练数据集的输出y_bert。主要思想是使用原始预测,即最终激活函数之前的预测,通常是 softmax 或 sigmoid。假设通过使用原始值,模型可以比使用“硬”预测更好地学习内部表示。Softmax 将这些值归一化为 1,同时保持最大值较高,并将其他值减小到非常接近零的值。零中的信息很少,所以通过使用原始预测,我们也可以从非预测类中学习。
为了学习“行为”,我们通过在传统的交叉熵损失(L_ce)上增加一个额外的项 L _ distill 来修改损失函数:

其中 L _ distill 是学生网络的逻辑值与教师的逻辑值之间的均方误差(MSE)损失:

其中 z(B)和 z(S)分别是教师和学生的逻辑。
我们几乎完全重用了代码,只做了一处修改——损失函数。为了计算交叉熵损失,我使用了CrossEntropyLoss,它结合了 softmax 函数和交叉熵。
为了正确计算损失函数,我们还将输出数量从 1 改为 2。完整的代码应该是:
仅此而已。我们将知识从伯特模型转移到一个更简单的模型。
结果
在我的工作中,我尝试用这种方法来解决二元分类问题。它有高度不平衡的类,因此关键度量是平均精度分数。该模型的比较如下表所示。有趣的是,经过提炼的版本甚至比基于 BERT 的模型具有更好的平均精度分数。在我看来,这个事实是进一步研究的一个很好的切入点。
结论
这是知识升华的一个非常基本的方法。具体的浅层网络的结构和超参数将取决于您的特定任务。在我看来,在现实生活实践中还是挺有用的。它在训练时有些复杂,但是您可以得到一个更好的模型,同时在执行时保持其原始的简单性。
将机器学习模型部署到云的简单方法
使用简单的技术堆栈将您的第一个 ML 模型部署到生产环境中

Photo by Randy Fath on Unsplash
机器学习领域目前看到数据科学家(DS)扮演以下两个突出角色中的一个或两个:
- 其中,DS 接收数据转储,对数据应用某种机器学习算法,并以某种演示或报告的形式报告结果。
- 其中 DS 为利益相关者创建一个可用的软件来使用机器学习模型。
在这篇博文中,我试图展示一个 DS 工作的第二个方面的示例方法,即创建一些可以被利益相关者使用的软件。具体来说,我们将创建一个 web 服务,可以通过查询从机器学习模型中获得预测。这篇文章主要是为那些想超越仅仅开发模型的机器学习实践者准备的。
Tech-stack : Python、 Flask 、 Docker 、 AWS ec2
工作流程可以分为以下几个基本步骤:
- 在本地系统上训练机器学习模型。
- 将推理逻辑包装到 flask 应用程序中。
- 使用 docker 将 flask 应用程序容器化。
- 在 AWS ec2 实例上托管 docker 容器并使用 web 服务。
免责声明:这里展示的系统与商业生产系统的样子相差甚远。这篇博文的关键点应该是开发工作流程,熟悉技术栈,并初步体验构建 ML 生产系统。
让我们从第一步开始。
在本地系统上训练机器学习模型
我们需要一些可以包装在网络服务中的机器学习模型。出于演示的目的,我选择了一个逻辑回归模型来对 iris 数据集进行多类分类(是的,非常简单!# LazinessFTW)。使用 python 3.6 在本地系统上训练该模型。
使用熟悉的 scikit-learn,可以快速训练上述模型。关于模型开发,请参考本博客 github 报告中的笔记本“Model_training.ipynb”。我只想强调模型开发的两个重要方面:
- 训练后生成的模型文件存储为 pickle 文件,这是一种用于存储对象的序列化格式。(在回购中,该文件被命名为“iris_trained_model.pkl”)
- 推理调用(。predict())调用需要 numpy 数组形式的每个测试样本 4 个特征。
将推理逻辑包装到 flask web 服务中
现在我们已经有了训练好的模型文件,我们准备好查询模型来获得一个测试样本的类标签。推断非常简单,只需用测试数据对训练好的模型调用 predict()函数。然而,我们希望将推理构建为一个 web 服务。为此,我们将使用烧瓶。
Flask 是一个强大的 python microwebserver 框架,它允许我们以最少的配置麻烦快速构建基于 REST API 的 web 服务。让我们深入研究代码:
a.首先,让我们定义一个简单的函数来加载训练好的模型文件。
这里,我们定义了一个名为“model”的全局变量,并将其填充到 load_model()函数中。使用全局变量的目的很快就会变得清楚。
b.接下来,我们实例化一个名为“app”的 Flask 对象:
c.现在,我们定义一个 home 端点,当点击它时,返回一个“Hello World!”消息。
注意 app.route decorator 的使用。
d.现在,我们定义一个“预测”端点。端点接受“POST”请求,其中我们希望得到预测的测试数据由端点接收。简单地说,该函数仅在需要预测单个测试样本时才起作用(如果在对端点的单次调用中需要预测多个样本,则该函数不起作用)。
请注意通过“模型”变量对预测函数的直接调用。
e.最后,声明主函数:
在这里,对 load_model()函数的调用确保了变量“model”由经过训练的模型属性填充(因此需要一个全局模型变量)。因此,没有必要在每次调用预测端点时重复加载模型。这使得网络服务变得快速。响应以字符串的形式返回,该字符串是预测的类标签。
完整的烧瓶特定代码如下:
此时,web 服务就可以在本地运行了。我们来测试一下。
从终端执行命令python app.py。进入浏览器,点击网址0.0.0.0:80,显示一条信息Hello World!。这对应于归属端点返回消息。
注意:此时可能会收到权限错误。在这种情况下,在app.py的app.run()命令中将端口号更改为 5000。(端口 80 是一个特权端口,所以将其更改为某个非特权端口,例如:5000)
接下来,让我们在终端上使用下面的 curl post 请求,测试我们是否可以使用这个 web 服务获得预测:
curl -X POST \
0.0.0.0:80/predict \
-H 'Content-Type: application/json' \
-d '[5.9,3.0,5.1,1.8]'
curl 请求向我们的 web 服务器发送一个测试样本[5.9,3.0,5.1,1.8],并返回一个类标签。
使用 docker 将 flask 服务容器化
到目前为止,我们有一个本地运行的 web 服务。我们的最终目的是能够在云虚拟机上运行这段代码。
在软件开发领域,一个开发人员给出了一个著名的理由,他的代码被测试人员破解了:'但是它在我的机器上工作了!’。这里描述的问题通常可以归因于缺乏在不同机器上运行软件的一致环境。理想情况下,我们的代码本身应该独立于运行它的底层机器/操作系统。容器化允许开发人员提供这样的隔离。
它在这里有多重要?
我们的目的是在云虚拟机上运行我们的 web 服务。云虚拟机本身可以运行任何操作系统。我们网络服务器的容器化使我们避免了遇到环境相关问题的麻烦。如果容器化的代码能在一台机器上运行,那么不管这台机器的特性如何,它肯定能在另一台机器上运行。在这一点上,Docker 是最著名的容器化技术,我们将在这里使用同样的技术。关于 docker 的快速教程,请查看这个链接。
让我们深入了解 docker 文件,它包含一组 docker 守护进程构建 docker 映像的指令。
我们从 python dockerhub repo 中提取基本 docker 映像,在其上执行我们的特定构建指令。复制命令只是从当前文件夹中取出特定的文件,并将它们复制到我们试图构建的 docker 映像中名为“deploy”的文件夹中。除了 app.py 和模型文件之外,我们还需要一个需求文件,其中列出了我们用来运行代码的 python 包的特定版本。WORKDIR 命令将工作目录更改为映像中的“deploy/”。然后,我们发出一个运行命令,使用需求文件安装特定的 python 包。EXPOSE 命令使外部世界可以访问端口 80(我们的 flask 服务运行在端口 80 上;我们需要容器内部的这个端口在容器外部是可访问的)。
发出 build 命令,以 docker 映像结束:
docker build -t app-iris .
(不要忘记命令末尾的句号)。
使用命令“docker images”查看创建了名为“app-iris”的 docker 存储库的 docker 映像。(还将看到另一个名为 python 的存储库,因为它是我们构建自定义映像的基础映像。)
现在,映像已经构建好,可以运行了。我们可以使用以下命令来实现这一点:
docker run -p 80:80 app-iris .
上述命令使用-p 标志将本地系统的端口 80 映射到 docker 容器的端口 80,以便将本地 HTTP 端口 80 上的流量重定向到容器的端口 80。(如果您使用本地端口 5000 而不是端口 80,请将命令的端口映射部分更改为 5000:80)。
让我们通过在应该显示“Hello World!”的浏览器上点击 URL: http://0.0.0.0:80 来测试这是否可行这是本地端点输出消息(如果使用端口 5000,请在 url 中将 http 端口修改为 5000)。此外,使用前面提到的 curl 请求来检查是否返回了预测的类标签。
在 AWS ec2 实例上托管 docker 容器
我们已经有了一个在本地系统上运行的容器化应用程序。现在,如果其他人希望使用该服务呢?如果我们需要围绕需要可用、自动化和可伸缩的服务构建一个架构生态系统,会发生什么?显而易见,让 web 服务在本地运行是一个非常糟糕的主意。因此,我们希望在互联网上的某个地方托管 web 服务,以满足我们列出的要求。对于这个博客,我们选择在一个 AWS ec2 实例上托管我们的服务。
作为先决条件,需要有一个 AWS 帐户才能使用 ec2 实例。对于新用户,有几个 AWS 资源可以免费使用 1 年(通常有一定的限制)。在这篇博客中,我将使用一个符合自由层条件的' t2.micro ' ec2 实例类型。对于已经用完 AWS 免费层的用户来说,在写这篇博客的时候,这个实例的费用大约是每小时 1 美分;一笔可以忽略不计的费用。
先说流程。
登录 AWS 管理控制台,在搜索栏中搜索 ec2 以导航到 EC2 仪表板。

Search for ec2 on the aws management console
寻找下面的窗格,选择“密钥对”并创建一个。

Select Key Pairs for looking at existing key pairs and creating new ones
这将下载一个。pem '文件,这是密钥。将此文件安全地保存在某个地方。现在,导航到系统上该文件的位置,发出下面的命令,用您的密钥文件名替换它:
chmod 400 key-file-name.pem
该命令将您的密钥对文件的权限更改为私有。稍后将解释密钥对的使用。
接下来,单击 EC2 仪表板上的“启动实例”:

Launch ec2 instance
从选项列表中选择 Amazon 机器实例(AMI)。AMI 决定了 VM 将要运行的操作系统(以及其他一些我们现在不关心的东西)。对于这篇博客,我选择了默认选择“Amazon Linux 2 AMI”。

Choosing AMI
下一个屏幕允许您选择实例类型。这是可以选择虚拟机硬件部分的地方。如前所述,我们将使用“t2.micro”实例。

Selecting instance type
您可以选择“查看并启动”,进入“第 7 步:查看实例启动”屏幕。在这里,您需要单击“编辑安全组”链接:

Security Groups
现在,您必须修改安全组,以允许外部世界访问您的实例的端口 80 上的 HTTP 流量。这可以通过创建规则来实现。最后,您应该会看到这样一个屏幕:

Adding HTTP rule to security group
如果没有这条规则,您的 web 服务将永远无法到达。有关安全组和配置的更多信息,请参考 AWS 文档。点击“启动”图标会弹出一个窗口,要求确认是否有密钥对。使用之前生成的密钥对的名称,并启动虚拟机。
您将被重定向到启动屏幕:

Launch Status of ec2 instance
使用“查看实例”按钮导航至显示正在启动的 ec2 实例的屏幕。当实例状态变为“正在运行”时,它就可以使用了。
现在,我们将使用命令从本地系统终端 ssh 到 ec2 机器,该命令的 public-dns-name 字段替换为您的 ec2 实例名(格式:【ec2–x–x–x–x.compute-1.amazonaws.com】和您之前保存的密钥对 pem 文件的路径)。
ssh -i /path/my-key-pair.pem [ec2-user@](mailto:ec2-user@ec2-198-51-100-1.compute-1.amazonaws.com)public-dns-name
这将使我们进入实例的提示,我们将首先安装 docker。这是我们的工作流所需要的,因为我们将在 ec2 实例中构建 docker 映像(对于这一步,有更好但稍微复杂的替代方法)。对于我们选择的 AMI,可以使用以下命令:
**sudo amazon-linux-extras install docker
sudo yum install docker
sudo service docker start
sudo usermod -a -G docker ec2-user**
有关命令的解释,请查看文档。
使用“exit”命令注销 ec2 实例,然后使用 ssh 命令再次登录。通过发出“docker info”命令检查 docker 是否工作。再次注销或打开另一个终端窗口。
现在让我们复制在 ec2 实例中构建 docker 映像所需的文件。从本地终端发出命令(不是从 ec2 内部):
scp -i /path/my-key-pair.pem file-to-copy [ec2-user@](mailto:ec2-user@ec2-52-59-206-245.eu-central-1.compute.amazonaws.com)public-dns-name:/home/ec2-user
我们需要复制 requirements.txt、app.py、经过训练的模型文件和 Dockerfile 来构建 docker 映像,就像前面所做的那样。重新登录到 ec2 实例,发出' ls '命令,查看复制的文件是否存在。接下来,使用本地系统中使用的完全相同的命令构建并运行 docker 映像(这次在代码/命令中的所有位置使用端口 80)。
使用公共 dns 名称从您的浏览器点击家庭端点,以查看熟悉的' Hello World!'消息:

Home endpoint works from the browser (I used my ec2 public-dns-name in the address bar)
现在,在用您的名称替换 public-dns-name 之后,用您的测试样本数据从本地终端向您的 web 服务发送一个 curl 请求:
curl -X POST \
public-dns-name:80/predict \
-H 'Content-Type: application/json' \
-d '[5.9,3.0,5.1,1.8]'
这将使您得到与本地相同的预测类标签。
你就大功告成了!现在,您可以将这个 curl 请求分享给任何希望使用您的测试样本的人。
当您不再需要 web 服务时,不要忘记停止或终止 ec2 实例:

Stop or terminate the ec2 instance to avoid getting charged
一些附加的想法
这是一个超级基本的工作流程,为渴望超越模型开发的 ML 从业者而设计。大量的事情需要被改变,以使这个系统成为一个更适合真实生产系统的系统。一些建议(远未完成):
- 在 flask 应用程序中使用 Web 服务器网关接口(WSGI ),如 gunicorn。使用 nginx 反向代理和异步工人的加分。
- 提高 ec2 实例的安全性:该服务目前对全世界开放。建议:限制对一组 IP 的访问。
- 为应用程序编写测试用例:未经测试的软件=朝自己的腿开枪,然后把自己扔进一个装满饥饿狮子的笼子,同时被扔石头。(寓意:在没有彻底测试之前,不要发布产品软件)
上面的列表中还可以添加更多的内容,但也许这是另一篇博文的内容。
Github 回购:【https://github.com/tanujjain/deploy-ml-model
如果能听到您的反馈,那就太好了!
改进 Matplotlib 的简单方法
Matplotlib 的默认属性经常产生不吸引人的图。这里有几个简单的方法来美化你的形象。

Photo by Alice Bartlett. Cropped and resized by author.
Matplotlib 通常是 Python 程序员学习的第一个数据可视化包。虽然它的用户可以用几行代码创建基本的图形,但这些默认的图形在设计美学和交流能力方面往往是不够的。然而,简单的调整可以带来巨大的改进,在这篇文章中,我将分享一些关于如何升级 Matplotlib 图形的技巧。
在接下来的例子中,我将使用在这个关于谷物的 Kaggle 数据集中找到的信息。我通过食用量标准化了三个特征(卡路里、脂肪和糖),以便更好地比较谷物营养和评级。关于这些数据转换的细节和用于生成每个示例图的代码可以在我的 GitHub 上找到。
移除脊椎
默认要更新的第一个 Matplotlib 是每个图周围的黑框,由四个所谓的“脊骨”组成为了调整它们,我们首先通过 pyplot 获得我们的图形的轴,然后根据需要改变每个单独脊椎的可见性。
比方说,我们想要删除顶部和右侧的脊椎。如果我们导入了 Matplotlib 的 pyplot 子模块:
from matplotlib import pyplot as plt
我们只需要在代码中添加以下内容:
plt**.**gca()**.**spines['top']**.**set_visible(False)
plt**.**gca()**.**spines['right']**.**set_visible(False)
顶部和右侧的脊柱将不再出现。移除这些分散注意力的线条可以将更多的注意力放在你的数据上。

Removing distracting spines can help people focus on your data.
探索颜色选项
Matplotlib 的默认颜色刚刚得到升级,但是你仍然可以很容易地改变它们,使你的图更有吸引力,甚至反映你公司的品牌颜色。
十六进制代码
我最喜欢的更新 Matplotlib 颜色的方法之一是直接将十六进制代码传递给颜色参数,因为它允许我非常具体地选择颜色。
plt**.**scatter(**...**, color**=**'#0000CC')
这个方便的工具可以帮助你选择一个合适的十六进制颜色,通过测试它与白色和黑色文本,以及比较几个较亮和较暗的阴影。或者,在选择调色板时,你可以采取更科学的方法,看看布朗可视化研究实验室的康纳·格拉马齐奥的色彩学。Colorgorical 工具允许您通过平衡各种偏好(如人类感知差异和审美愉悦)来构建调色板。
xkcd 颜色
xkcd 颜色库提供了另一种更新 Matplotlib 默认颜色的好方法。这 954 种颜色是由 xkcd 颜色名称调查的几十万参与者特别策划和命名的。您可以在 Matplotlib 中使用它们,方法是在它们的名称前加上“xkcd:”前缀。
plt**.**scatter(**...**, color**=**'xkcd:lightish blue')

Matplotlib’s default colors can easily be updated by passing hex codes or referencing the xkcd library.
图层图形对象
Matplotlib 允许用户将多个图形层层叠加,这在比较结果或设置基线时非常方便。在分层的时候应该利用两个有用的属性:1) alpha用于控制每个组件的不透明度,2) zorder用于将对象移动到前景或背景。
不透明
Matplotlib 中的 alpha 属性调整对象的不透明度。该值的范围从 0 到 1,其中 0 表示完全透明(不可见👀)和一个完全不透明的。减少 alpha 将使您的绘图对象透明,允许同时看到多个层,并允许区分重叠点,比如在散点图中。
plt**.**scatter(**...**, alpha**=**0.5)

Decreasing alpha reduces opacity and can help you visualize overlapping points.
命令
Matplotlib 的 zorder 属性决定了对象与前景的距离。具有较小 zorder 值的对象看起来更靠近背景,而具有较大 z order 值的对象更靠近前面。例如,如果我正在制作一个散点图和一个伴随的线图,我可以通过增加它的 zorder 来使线前移。
plt**.**scatter(**...**, zorder**=**1) *#background*
plt**.**plot(**...**, zorder**=**2) *#foreground*

Plot objects can be brought to the foreground or pushed to the background by adjusting zorder.
For more tips just like these, check out my full Matplotlib Tips playlist on YouTube.
注释要点或例子
许多视觉效果可以从主要观点的注释或具体的、说明性的例子中受益,因为这些直接传达了想法并提高了结果的有效性。要向 Matplotlib 图形添加文本,只需包含指定所需文本及其位置的注释代码。
plt**.**annotate(TEXT, (X_POSITION, Y_POSITION), **...**)
用来制作这个博客视觉效果的谷物数据集包含了几种品牌谷物的营养信息,以及一个标记为“评级”的特征人们可能首先认为“评级”是一个表明消费者偏好的谷物的分数。然而,在上面的 zorder 图中,我建立了一个快速线性回归模型,表明每杯卡路里和评级之间的相关性实际上不存在。卡路里似乎不可能不成为消费者偏好的因素,所以我们可能已经对我们最初关于“评级”的假设产生了怀疑。
当检查极端情况时,这种误解变得更加明显:Cap'n Crunch 是评级最低的谷物,而全麸额外纤维评级最高。用这些有代表性的例子来注释这个图,立刻消除了关于“评级”的错误假设这种评级信息更有可能表明谷物的营养价值。(我也标注过每杯热量最多的麦片;葡萄坚果不太可能被大量食用!😆)

Annotating your visuals with a few examples can improve communication and add legitimacy.
基线和突出显示
给你的视觉效果添加基线有助于设定期望值。一条简单的水平线或垂直线为其他人提供了适当的背景,通常会加速他们对你的结果的理解。同时,突出感兴趣的特定区域可以进一步强调你的结论,也有助于与你的听众交流。Matplotlib 为基线和突出显示提供了几个选项,包括水平线和垂直线、矩形等形状、水平和垂直跨度阴影以及两条线之间的填充。
水平线和垂直线
现在让我们考虑一下谷物数据集中脂肪和糖之间的相互作用。起初,这种关系的基本散点图似乎并不有趣,但在进一步探索后,我们发现每杯谷物的脂肪中位数仅为 1 克,因为许多谷物根本不含脂肪。添加这条基线有助于人们更快地得出这一发现。

A horizontal or vertical baseline can help set the stage for your data.
在其他情况下,您可能希望完全删除 Matplotlib 提供的默认 x 轴和 y 轴,并基于一些数据集合创建自己的轴。这个过程需要三个关键步骤:1)删除所有默认的脊线,2)删除刻度线,3)添加新的轴作为水平线和垂直线。
*#1\. Remove spines*
for spine in plt.gca().spines.values():
spine.set_visible(False)*#2\. Remove ticks*
plt.xticks([])
plt.yticks([])*#3\. Add horizontal and vertical lines*
plt.axhline(Y_POSITION, ...) *#horizontal line*
plt.axvline(X_POSITION, ...) *#vertical line*

You can also create new axes for your data by removing spines and ticks and adding custom lines.
矩形
既然我们已经把谷物的脂肪和糖含量标绘在新的坐标轴上,看来很少有谷物是低糖高脂肪的。也就是说,左上象限几乎是空的。这似乎是合理的,因为谷物通常不好吃。为了使这一点非常清楚,我们可以通过在它周围画一个矩形并加以注释,将注意力引向这个低糖、高脂肪的区域。Matplotlib 通过其补丁模块提供了对几种形状的访问,包括一个矩形甚至一个海豚。首先导入矩形的代码:
from matplotlib.patches import Rectangle
然后,要在图形上创建一个矩形,请抓取当前轴,并添加一个矩形补片及其位置、宽度和高度:
plt**.**gca()**.**add_patch(Rectangle((X_POSITION, Y_POSITION),
WIDTH, HEIGHT, **...**)
这里,x 和 y 位置指的是矩形左下角的位置。

To direct people toward a particular part of your visual, consider adding a rectangle.
明暗法
阴影提供了另一种选择,用于将注意力吸引到图形的特定区域,有几种方法可以使用 Matplotlib 添加阴影。
如果您打算突出显示整个水平或垂直区域,只需在视觉效果中添加一个跨度:
plt**.**axhspan(Y_START, Y_END, **...**) *#horizontal shading*
plt**.**axvspan(X_START, X_END, **...**) *#vertical shading*
之前讨论过的属性像alpha和zorder在这里很关键,因为你可能想要让你的阴影透明和/或把它移到背景。

Shading also provides an effective way to highlight a particular region of your plot.
但是,如果您想要着色的区域遵循更复杂的逻辑,您可以在两条用户定义的线之间使用着色。这种方法需要一组 x 值,第一行和第二行的两组 y 值,以及一个可选的where参数,该参数允许您使用逻辑过滤到您感兴趣的区域。
plt**.**gca()**.**fill_between(X_VALUES, Y_LINE1, Y_LINE2,
WHERE**=**FILTER LOGIC, **...**)
要使之前用矩形突出显示的相同区域变暗,只需为 x 轴定义一个等距糖值数组,在 y 轴上填充中间值和最大脂肪值(高脂肪),并向下过滤小于中间值的糖值(低糖)。
sugars **=** np**.**linspace(df**.**sugars_per_cup**.**min(),
df**.**sugars_per_cup**.**max(),
1000)plt**.**gca()**.**fill_between(sugars,
df**.**fat_per_cup**.**median(),
df**.**fat_per_cup**.**max(),
WHERE**=**sugars **<** df**.**sugars_per_cup**.**median(),
**...**)

More complex shading logic is accomplished by filling between two lines and applying a filter.
结论
Matplotlib 的名声很差,因为它的缺省值很差,而且需要大量代码来产生看起来不错的视觉效果。希望这篇博客中提供的技巧能帮助你解决第一个问题,尽管我承认最后几个示例图需要很多更新和大量代码。如果所需的大量代码困扰着你,那么 Seaborn 可视化库是 Matplotlib 的一个很好的替代品。它提供了更好的默认设置,需要更少的代码行,并且如果需要的话,支持通过传统的 Matplotlib 语法进行定制。
当您可视化数据时,无论您选择哪个包,都要记住的主要事情是您的受众。我在这里提供的建议旨在通过以下方式平滑数据交流过程:1)删除多余的部分,如不必要的线条或刻度线;2)通过设置分层和基线来更快地讲述数据故事;3)通过阴影和注释突出主要结论。由此带来的美感也有所改善,但主要目标是更强大、更无缝的数据通信。
我最近在 ODSC 纽约 的一次数据可视化演讲中分享了类似的内容。你可以在这里访问我的会议资料以及我的 GitHub上的各个幂例图。****
[1] J.D. Hunter,Matplotlib:2D 图形环境 (2007),科学计算&工程。
[2] C .克劳福德, 80 谷物 (2017),卡格尔。
[3] C.C. Gramazio,D.H .莱德劳和 K.B. Schloss, Colorgorical:为信息可视化创建可区分和优选的调色板 (2017),IEEE 可视化和计算机图形学汇刊。
使用 Python 的 Selenium 进行简单的 Web 抓取
沙滩球搜索,从网页到 CSV!

Image Courtesy of Shahadat Rahman via Unsplash
使用 Selenium 模块
夏天对我来说意味着两件事,在沙滩上消磨时间和踢足球。随着天气的好转和 2019 年 FIFA 女足世界杯的进行,我现在需要买一个沙滩足球。这似乎是一个很好的机会来展示我们的网络抓取能力,并编写一个简单的网络抓取器来收集来自沃尔玛的沙滩球数据。
这篇文章旨在介绍如何使用 Selenium 模块进行网页抓取。此外,增加一个额外的挑战,让我们收集数据,不只是从一个网页,而是许多!为了便于数据解释,我将把输出写到一个 CSV 文件中。
我们开始吧!
首先,我们需要安装一个 Webdriver,我们将使用 Selenium 模块通过 Python 来控制它。我们将在本教程中使用的 web 驱动程序是 Chromedriver,可以通过导航到此链接下载。
接下来,我们需要安装 Selenium。在我的 Windows 命令提示符下,我写 pip install selenium。当 Chrome webdriver 和 selenium 模块都安装好后,我就可以导入 webdriver 了。
现在我们可以让 Python 代码使用 Chrome webdriver 来控制浏览器。在 webdriver.chrome()中,需要传递 chromedriver 可执行文件的路径。在这里,chromedriver.exe 在我的本地工作目录中。我把它赋给变量 browser。我们现在可以通过 Python 代码来控制浏览器。
我们可以使用 browser.get()导航到一个网站,方法是将该网站的路径用引号括起来。在这里,我想导航到沃尔玛的网页,其中包括所有关于沙滩球的信息。
我们可以在浏览器上使用许多不同的方法。我们可以使用 CSS select 查询来查找感兴趣的特定元素。CSS 选择器对于网页抓取非常有用。例如,CSS 类定义了多个标签的样式,这是一个可以在网页抓取过程中有效利用的特性!由于我们搜索的元素可能有一个规则的样式,它们很可能有相同的 CSS 类。
在第一个 web 页面上,我希望找到一个包含某个商品所有相关信息的包装器。当我导航到该网页时,右键单击并选择 inspect,我可以看到我感兴趣的包装器有一个 li 标签和一系列类,如下所示:
<李 class = " Grid-col u-size-1–4-l u-size-3–12 米 u-size-6–12 u-size-1–5-XL "

当我在浏览器中时,为了帮助找到所有这些列表项,我可以切换到 Chrome 的开发者工具中的控制台标签。当我在这里时,我可以编写一个测试查询。为此,我写了两个美元符号$$并在括号内输入我的查询,如下所示:
$ $(‘李。grid-col . u-size-1–4-l . u-size-3–12-m . u-size-6–12 . u-size-1–5-XL ')

这将显示一个长度为 40 的列表,如上面的括号所示。我通过滚动列表项并查看浏览器中相应的突出显示,快速验证了每个列表项都对应于 web 页面上相应的沙滩球。我鼓励的一个工作实践是检查列表中的最后一项,并检查它是否是页面上您感兴趣的最后一项。
对我们来说是这样的!
如果不是这样,您可以在控制台开发工具中再次尝试不同的 CSS 查询。这是一个很好的实践,因为我们可以在浏览器中执行初始验证,而不是在 Python 脚本中。它也非常容易看到,因为我们可以同时查看 CSS 查询和浏览器页面!

我们现在可以将这个查询直接传递给。find _ elements _ by _ CSS _ selector方法。我创建了一个名为 beach_balls 的变量,它指向找到的所有沙滩球的列表。我们现在可以迭代这个 beach_balls 列表。
print(type(beach_balls))
<class 'list'>
使用同样的方法,我可以编写一个简单的 for 循环来提取我感兴趣的信息。
这里,我使用find _ element _ by _ CSS _ selector(我使用了 element,而不是 elements )来查找与原始包装器中包含的其他信息相关的标签和类。当我为 desc ect 找到合适的元素时,我使用。text 方法提取文本,lstrip 方法对字符串进行简单清理。
这段代码运行良好,但是 web 抓取真正有用的方面是它提供的自动化。为了演示,我将用沃尔玛的沙滩球抓取 2 页(尽管我们可以很容易地抓取更多页面的数据)。我写了一个 while 循环,它将根据提供的条件迭代两次。
需要注意的是并非每个项目都有所有信息。例如,在一些情况下,运输信息丢失。通常,当您在编写 scraper 时遇到这种情况时,您应该在 for 循环中编写一个条件,这样所有信息都会匹配。然而,沃尔玛的这个页面是这样组织的,当信息丢失时,空白填充空间。这意味着在这种情况下,在 for 循环中不需要条件检查,但是要小心,情况并不总是这样!
多页刮擦
在每次迭代中,我将相关的项目添加到适当的列表中。在 for 循环的第一次迭代结束时,我点击进入下一页。我找到对应于下一页的标签和类,并使用。单击()方法导航到它上面。

如果一切正常,脚本应该在第三页结束。根据我的 while 条件,应该有两次迭代。
该脚本已按预期工作。下面,第三个页面已经加载,如网页底部的绿色图标所示。

最后,我将把输出写到一个 CSV 文件中,通过使用 pandas Dataframe 方法把我的列表压缩在一起并给它们起一个合理的名字。

本教程重点介绍了如何使用 selenium 从一个网站抓取多个页面。如果我们想要,我们可以从不同的网站搜集信息,并开始为我们的沙滩球或任何其他我们可能感兴趣的物品构建一个价格比较模型。
但作为总结,去给自己买个沙滩球,去海滩吧!
对 ClinicalTrials.gov 简单而实用的分析
通过少量的机器学习

临床研究工作教会了我一些重要的事情,所有这些都可以用一句话来表达:
"用心做好事情,让自己免于恐惧."
对于临床试验来说尤其如此,在临床试验中,研究人员要对患者的健康和幸福负责。
开发一种药物的整个过程极其复杂,可能需要几十年时间。一种新药首先在实验室里进行研究,如果这种药看起来有希望,在临床试验过程中就会对其进行仔细的人体研究。
进行临床试验是为了找到预防、诊断和治疗疾病的更好方法。所以从本质上来说,临床试验的目标是确定一种药物是否既安全又有效。
今天,大多数临床试验都要经过彻底的注册程序。FDA ( 美国食品药品监督管理局)要求所有正在进行人体临床试验的药物和设备必须在ClinicalTrials.gov上注册,这是一个基于网络的资源,为公众提供了一个方便获取公共和私人支持的临床研究信息的途径。虽然它无疑是帮助患者找到他们可能参与的研究的一个很好的工具,但它也为研究人员和卫生保健专业人员提供了关于新药开发的最新信息。
这就是我在数据科学之旅中学到的另一件事:没有什么比处理易于访问和结构化的数据更令人满意的了。
一个包含所有 XML 格式研究记录的 zip 文件可供下载。让我们看看我们能从这些数据中获得什么样的见解。
首先,最好能显示这些年来在世界各地进行的临床研究的数量。点击此处查看用 plotly 创建的交互式地图。

由于 ClinicalTrials.gov 提供有关美国临床试验的信息,难怪大多数研究来自美国。除此之外,美国医药市场是世界上最大的国家市场。
现在让我们看看哪个国家倾向于更积极地参与临床试验。为了弄清楚这一点,我将每个国家 2016 年开始的研究数量除以该国人口。你可以在这里查看地图。

出乎意料的是,美国在这里失去了领导地位。丹麦的人均审判次数似乎比其他任何国家都多。很可能,如此大量的研究是丹麦政府将该国确立为临床试验首选国家的多项举措的结果。

尽管美国是领导者,我们可以看到中国是癌症研究的主要地区之一。主要原因可能是中国是世界上最大的烟草消费国和生产国。
T3 机器学习来了
在从事临床研究时,我对制药公司如何决定研究方向特别感兴趣。大多数公司试图专注于特定的利基市场,开发针对特定疾病群体的产品。使用主成分分析(PCA)有可能将排名前 50 位的公司的研究成果可视化。
每个公司由一个向量表示,其中每个值是该公司对特定疾病进行研究的次数。我还将这些值标准化,并使用主成分分析来可视化数据。我鼓励你点击这个链接来查看可视化效果(警告:可能需要一些时间才能打开)。

This way we can see which companies share the same interests in clinical research
这里你可以看到一些公司形成了独立的集群。例如,黄点(赛诺菲巴斯德、MedImmune LLC 和诺华疫苗)是主要从事疫苗研究的公司。底部的紫色公司(波士顿科学公司、圣犹达医疗公司和美敦力心律和心力衰竭公司)因开发心脏病学产品而闻名。不出所料,基因泰克和霍夫曼-罗氏公司也走得很近。
我相信这种分析是一种很好的方式,可以让你对临床试验有一个更全面的了解。毫无疑问,机器学习在临床研究中的潜力很难被高估。
请在下面的评论中分享你对机器学习如何应用于临床研究的想法。
复杂中的简单
向非技术人员解释神经网络

作为一名数据科学家,我听说过对人工智能的两种极端解释。一方面,人工智能就是使用命令。适合“plus”。用提供的工具“预测”,因此我们可以通过简单地导入库来实现人工智能。另一方面,人工智能是一项神奇的任务,其他领域的人很难理解。
虽然对于数据科学家来说,戴上一个看似花哨的头衔,暗示着神奇的工作,这可能很方便,但事实上,这并没有那么花哨。
如果我们能够解释人工智能背后的基本思想,以一种任何人都可以直观理解的方式去神秘化它,那么我们就可以帮助提高组织采用人工智能的速度,就像采用上个世纪改变了我们商业运作方式的任何其他技术一样。
这让我思考,我该如何向非技术观众解释人工智能?经常被问到的一个问题是最热门的算法之一——神经网络,这是一种近年来重新流行的方法,并形成了深度学习的基础。
那么简单来说什么是神经网络呢?让我们把这个术语分解成“神经元和“网络”。
要解读“神经元”,我们可以把它想象成一个把 A 映射到 b 的黑盒,或者更具体地说,它是一个数学函数,取输入 A,给出输出 b。

比方说,我们想预测房价 b。在最简单的情况下,我们假设房价线性依赖于房子的大小 A 乘以常数 C,加上购买该房子所涉及的一些固定法律费用 L。把它放入公式中,我们有一个假设 B = C * A + L,它代表了房价“成分”的基本事实。

这里最本质的任务是确定常数 C 和常数 l,一旦我们在黑盒里有了这样一个公式,给定任何房子的大小,我们就可以预测它的价格。
几乎在所有情况下,我们没有足够的知识来揭示投入和产出之间的真实关系,在这种情况下,房子的大小和房价。那我们怎么去找 C 和 L 呢?
然而,我们确实有一堆数据,每个数据点代表一个特定的房子大小和各自的价格。正是通过这些数据,我们希望了解一个模型,它足够代表我们假设的现实。
换句话说,对于每个问题,都有一个“正确的神经元”可以解决问题。
这是一个最简单的“神经元”的例子,其中输入和输出之间的关系是线性的。事实上,在大多数实践中,这种神经元还包括一个额外的非线性转换,有助于捕捉更复杂的关系。
在许多科学领域,寻找“正确的神经元”的想法确实是解决问题的长期方法。
给定一个问题,我们想要对输入和输出之间的关系进行建模,以试图完全描述一个系统的特征。我们写下一些公式,把这个问题抽象成可解的形式。
许多实际问题往往太复杂,无法用这种方式解决。以房价为例。事实上,价格不仅取决于房子的大小,还取决于许多其他因素,如位置、设施和经济状况。因此,即使是知识最渊博的专家也不可能提供一个公平的公式来考虑所有相关因素,并根据这些因素计算相应的房价。
如果单个神经元不能处理这种复杂性,那么多个神经元能完成这项工作吗?
这就是“网络”概念的由来。

神经网络由多个神经元组成,每个神经元接受一些输入并给出一些输出。一个神经元的输出可以成为其他一些神经元的输入。通过多个神经元,神经元网络可以执行更复杂的计算,同时保持每个神经元相对“简单”。
现在,复杂性正在部分地从神经元内部结构转移到网络结构。有了这样的网络,模型就能够表现更复杂的现象。
本质上,神经网络在复杂中描绘了一幅简单的美丽画面——嵌入在复杂网络中的“简单”神经元。
机器学习中的简单性与复杂性——找到正确的平衡

在统计学和机器学习中,偏差-方差权衡是一组预测模型的属性,其中在参数估计中具有较低偏差的模型在样本间具有较高的参数估计方差,反之亦然。偏差-方差困境或问题是试图同时最小化这两个误差源的冲突,这两个误差源阻止监督学习算法推广到它们的训练集之外:
- 偏差是学习算法中错误假设的误差。偏高(过于简单)会导致算法错过特征和目标输出之间的相关关系(欠拟合)。
- 方差是对训练集中小波动的敏感性误差。高方差(过于复杂)会导致算法对训练数据中的随机噪声建模,而不是对预期输出建模(过拟合)。
简单模型优于复杂模型的 3 个原因
- 防止过度拟合:具有太多特征的高维数据集有时会导致过度拟合(模型捕捉真实和随机效果)。
- 可解释性:具有太多特征的过于复杂的模型可能难以解释,尤其是当特征相互关联时。
- 计算效率:在低维数据集上训练的模型在计算上是高效的(算法的执行需要较少的计算时间)。
当使用高维数据集构建机器学习模型时,从简单的模型开始总是可取的,然后可以根据需要增加复杂性。在模型评估过程中,执行多项测试以确保模型不会捕捉数据集中的随机效应非常重要。为了能够发现随机效应,对你试图解决的问题有充分的了解是很重要的。
在本文中,我们使用 PyLab 来说明偏差-方差问题。通过一个例子,我们讨论了欠拟合(偏差误差,由模型过于简单引起)和过拟合(方差误差,由模型过于复杂引起)的概念。我们展示了如何在简单性和复杂性之间取得平衡。
例子:向上抛向空中的物体的位置
我们考虑一个实验,其中一个物体被抛向空中,其位置作为时间的函数被测量。从这个假设实验中获得的数据如下所示:
**#import necessary libraries**
import pylab
import numpy as np
import matplotlib.pyplot as plt**#create the dataset**
t = np.linspace(0,1,11)
h = np.array([1.67203, 1.79792, 2.37791,2.66408,2.11245, 2.43969,1.88843, 1.59447,1.79634,1.07810,0.21066])

The measured height of a particle at various times.
从我们对力学的研究中,我们知道高度应该随着时间的平方而变化,因此我们应该使用二次曲线来代替数据的线性拟合:

为了说明偏差-方差问题,让我们回到位置数据,并对数据拟合一个线性、一个二次和一个 10 次多项式:
plt.figure(figsize=(15,5))**# fig 1**
plt.subplot(131)
**#perform linear fit using pylab**
degree = 1
model=pylab.polyfit(t,h,degree)
est_h=pylab.polyval(model,t)
#calculating R-squared value
R2 = 1 - ((h-est_h)**2).sum()/((h-h.mean())**2).sum()#plot of observed and modeled data
pylab.scatter(t,h, c='b', label='observed')
pylab.plot(t,est_h, c='r', label='predicted:' + ' R2' '='+ ' ' + str(round(R2,4)))
pylab.xlabel('t(s)')
pylab.ylabel('h(m)')
pylab.title('linear model is not good (underfit)')
pylab.legend()**# fig 2**
plt.subplot(132)
#**perform quadratic fit using pylab**
degree = 2
model=pylab.polyfit(t,h,degree)
est_h=pylab.polyval(model,t)
#calculating R-squared value
R2 = 1 - ((h-est_h)**2).sum()/((h-h.mean())**2).sum()#plot of observed and modeled data
pylab.scatter(t,h, c='b', label='observed')
pylab.plot(t,est_h, c='r', label='predicted:' + ' R2' '='+ ' ' + str(round(R2,4)))
pylab.xlabel('t(s)')
pylab.ylabel('h(m)')
pylab.title('quadratic model is what we need')
pylab.legend()**# fig 3**
plt.subplot(133)
#**perform higher-degree fit using pylab**
degree = 10
model=pylab.polyfit(t,h,degree)
est_h=pylab.polyval(model,t)
#calculating R-squared value
R2 = 1 - ((h-est_h)**2).sum()/((h-h.mean())**2).sum()#plot of observed and modeled data
pylab.scatter(t,h, c='b', label='observed')
pylab.plot(t,est_h, c='r', label='predicted:' + ' R2' '='+ ' ' + str(round(R2,4)))
pylab.xlabel('t(s)')
pylab.ylabel('h(m)')
pylab.title('degree=10 captures random error (overfit)')
pylab.legend()pylab.show()

Illustration of bias error (underfit) and variance error (overfit).
对于线性拟合,R2 (R 平方)参数为 0.3953,非常低。如果我们有一个很好的拟合,我们会期望 R2 值更接近 1.0。对于二次拟合,R2 值为 0.8895。因此,二次拟合比线性拟合有了相当大的改进。使用次数= 10 的多项式,我们发现 R2 值等于 1.0。我们看到高次多项式捕捉到了真实的和随机的效果。在这个问题中,我们的力学知识表明,在二次模型之外进行高阶近似是没有好处的。
总之,我们用一个非常简单的例子讨论了偏差-方差问题。我们已经看到,决定一个好的拟合的主要因素是你要拟合的函数形式的有效性。当然,只要有可能,关于物理问题的理论或分析信息都应该整合到模型中。通常,与过于复杂的模型相比,模型参数较少的简单模型总是更容易解释。
参考
- 维基百科上的偏差-方差权衡。
- 《计算物理学的第一课》,作者:Paul L. DeVries,John Wiley & Sons,1994 年。
理解辍学背后的简化数学
在这里,我们将理解驱动辍学的数学。如何导致正规化?为什么0.5的辍学率导致最正规化?什么是高斯辍学?
<
尽管报告了突破性的结果,但从理论的角度来看,人们对辍学知之甚少。同样,辍学率为 0.5 的重要性以及它应该如何随着层的变化而变化并不明显。此外,我们能把辍学推广到其他方法吗?下面将提供一些解释。
深度学习架构现在变得越来越深入和广泛。有了这些更大的网络,我们能够实现良好的精确度。然而,大约十年前并非如此。事实上,深度学习因过度适应问题而声名狼藉。

Figure 1. A dense neural network.
然后在 2012 年左右,萌生了退学的想法。这个概念彻底改变了深度学习。我们在深度学习方面的成功很大程度上归功于辍学。
快速回顾:什么是辍学?
- Dropout 将学习所有权重的概念改变为在每次训练迭代中学习网络中的一小部分权重。

Figure 2. Illustration of learning a part of the network in each iteration.
- 这个问题解决了大型网络中的过拟合问题。突然之间,更大、更精确的深度学习架构成为可能。
在这篇文章中,我们的目标是理解辍学背后的数学原理。然而,在我们进入数学之前,让我们后退一步,了解辍学改变了什么。这将是接触数学的动力。
退学前一个主要的研究领域是正规化。在神经网络中引入正则化方法,如 L1 和 L2 权重罚值,始于 21 世纪初[1]。然而,这些调整并没有完全解决过拟合问题。
原因是共同适应。
神经网络中的协同适应

Figure 3. Co-adaption of node connections in a Neural Network.
学习大型网络的一个主要问题是共同适应。在这样的网络中,如果所有的权重一起学习,通常一些连接将比其他连接具有更强的预测能力。
在这种情况下,随着网络被反复训练,这些强大的连接被学习得更多,而较弱的连接被忽略。经过多次迭代,只有一小部分节点连接被训练。其余的停止参与。
这种现象被称为共适应。像 L1 和 L2 那样的传统正规化无法避免这一点。原因是它们也基于连接的预测能力进行调整。因此,它们在选择和拒绝权重时变得接近确定性。因此,强者愈强,弱者愈弱。
这样做的一个主要后果是:扩大神经网络的规模无助于。因此,神经网络的规模和准确性变得有限。
然后就辍学了。一种新的正则化方法。它解决了共同适应。现在,我们可以建立更深更广的网络。并利用它的预测能力。
在这样的背景下,让我们深入研究辍学的数学。你可以直接跳到辍学相当于正规化网络部分的推论。
辍学背后的数学
考虑网络中的单层线性单元,如下图 4 所示。详情请参考[ 2 。

Figure 4. A single layer linear unit out of network.
这称为线性激活,因为 f(x) = x 是线性的,如图 4 所示,该层的输出是输入的线性加权和。我们正在考虑这种简化的情况,以获得数学上的解释。这些结果(根据经验)适用于通常的非线性网络。
对于模型估计,我们最小化损失函数。对于这个线性层,我们将考虑普通的最小二乘损耗,

情商。1 显示常规网络和 Eq 的损耗。2 用于辍学网络。在 Eq 中。2、辍学率是𝛿,其中𝛿 ~伯努利( p )。这意味着𝛿等于 1,概率为 p,否则为 0。
网络训练的反向传播使用梯度下降法。因此,我们首先来看看等式(1)中压差网络的梯度。2,然后再来情商里的正规网络。1.

现在,我们将试图找到这个梯度和规则网络的梯度之间的关系。为此,假设我们使等式中的w' =p*w。1.因此,

对等式求导。4、我们发现,

现在,我们有了有趣的部分。如果我们找到压差网络梯度的期望值,我们得到,

如果我们看情商。6、有漏失的梯度的期望,等于正则化正则网络 Eɴ的梯度如果w' =p*w .
等效于正则化网络的丢失
这意味着最小化压差损失(在等式中)。2)等效于最小化正则化网络,如等式所示。7 以下。

也就是说,如果你在方程中对正则化网络进行微分。7,你将得到一个压差网络的(预期)梯度,如等式。6.
这是一种深刻的关系。从这里,我们可以回答:
为什么辍学率, p = 0.5,产生最大正则化?
这是因为等式中的正则化参数 p (1- p )。7,在 p = 0.5 时最大。
不同的层应该选择什么样的 p 值?
在 Keras 中,辍学率参数为(1- p )。对于中间层,对于大型网络,选择(1- p ) = 0.5 是理想的。对于输入层,(1- p )应该保持在大约 0.2 或更低。这是因为丢弃输入数据会对训练产生不利影响。不建议使用 A (1- p ) > 0.5,因为它会剔除更多的连接,而不会促进正则化。
为什么我们在测试或推理过程中用 p 来衡量权重 w?
因为一个失落网络的期望值相当于一个常规网络,其权重与失落率 p 成比例。这种缩放使得从一个断开的网络中得到的推论可以与完整的网络相比较。还有计算上的好处,这在[ 1 ]中用集合建模的观点进行了解释。
在我们走之前,我想谈谈高斯辍学。
什么是高斯辍学?
正如我们之前看到的,在 Dropout 中,我们放弃了与概率(1- p )的联系。用数学方法,用等式。2 我们将连接权重乘以随机变量𝛿,其中𝛿 ~伯努利( p )。
这个退出过程可以看作是在每个连接上放置一个伯努利门。

Figure 5. Dropout seen as a Bernoulli gate on connections.
我们可以用另一个门代替伯努利门。例如高斯门。这给了我们一个高斯漏失。

Figure 6. Dropout generalized to a Gaussian gate (instead of Bernoulli).
高斯辍学生被发现和普通辍学生一样好,有时甚至更好。
在高斯下降的情况下,激活的期望值保持不变(见等式。8).因此,与常规放弃不同,推理过程中不需要权重缩放。

这一特性也为高斯压降提供了计算优势。我们将在接下来的帖子中探讨高斯辍学的表现。在那之前,我要提醒你一句。
虽然漏失门的概念可以推广到除伯努利分布以外的其他分布,但建议理解新的分布将如何影响激活的预期。基于此,应该对激活进行适当的缩放。
结论
在这篇文章中,我们讨论了辍学背后的数学原理。我们在一些简化的条件下做了这道数学题。然而,该结果扩展到深度学习中的一般情况。总之,我们知道,
- 辍学和正常化之间的关系,
- 0.5 的辍学率将导致最大的正规化,以及
- 将辍学推广到高斯-安德洛普。
参考
- n . Srivastava,g . hint on,a . krijevsky,Sutskever,I .,& Salakhutdinov,R. (2014 年)。辍学:防止神经网络过度拟合的简单方法。《机器学习研究杂志》, 15 (1),1929–1958。
- 巴尔迪,p .,&萨多夫斯基,P. J. (2013)。理解辍学。在神经信息处理系统的进展(第 2814-2822 页)。
使用熊猫简化数据集清理

Photo by Markus Spiske on Unsplash
我听到很多分析师/数据科学家说他们花了大部分时间清理数据。你可能已经看过很多清理数据集的教程,但你可能已经知道:它永远不会是 100%干净的,在继续阅读本文之前,你必须理解这一点。所以我实话实说:我不会给你一个神奇的方法来解决你的数据集可能存在的所有数据问题。清理规则取决于您正在处理的域和项目的上下文。本文中的例子来自我自己在现实世界中处理数据的经验。
我已经处理了本文中详述的所有问题/过程。有时问题可能来自数据源本身,您必须清理它,有时只是您的同事或经理要求最终文件中的某些特定字段。欢迎在评论中分享你从经历中看到的主要问题。
开始前你需要什么
计算机编程语言
您需要最新的 Python 版本:3.7+。我推荐使用 Anaconda 发行版来获得 Python、Pandas 和 Jupyter。如果您已经安装了 Anaconda,请忽略以下两个命令。
Seaborn
pip install seaborn
熊猫
pip install pandas
Jupyter 笔记本
pip install jupyter
开始工作
让我们从我在 Kaggle 上找到的一个基本数据集开始。这个数据集是在 Google 上搜索“航班”和“机票”的搜索结果报告。当你在谷歌上搜索一个查询时,“有机的”结果会在搜索结果页面中排名,在这个数据集中每个查询只列出 10 个。正如您所猜测的,我们可能会在某些字段中遇到重复项。
我们将讨论以下主题:
- 删除无用字符
- 从系列中提取相关内容
- 检查 NaN 值
- 更改系列的类型
打开新的 Jupyter 笔记本并导入数据集:
import os
import pandas as pddf = pd.read_csv('flights_tickets_serp2018-12-16.csv')
我们可以使用 3 个神奇的函数快速检查数据集的外观:
- 。info():显示行数和类型
df.info()

- 。describe():显示数据集中每个数字列的主要统计信息
df.describe()

- 。head():显示数据集的前 5 行(可以通过传递一个整数作为参数来更改这个数字)
df.head()

我们甚至可以显示列名,因为我们有太多的列名,它们不适合显示在屏幕上:
df.columns

现在我们对我们正在处理的事情有了更好的认识。从这一点出发,根据你公司的环境和你的目标,你不会寻找同样的东西。此外,如果这个数据集将被用来为机器学习算法提供训练,或者如果你需要为你的经理运行分析,你的输出数据帧看起来不会一样。但这不是这里的主题。我们继续第一个话题。
1.删除无用字符
为什么我们要删除充满数据的数据集中的字符?嗯,数据集并不总是为你的个人用例而构建的,而是为多种用途而构建的。同样,根据你的项目,你的关注点会有所不同。
在你从事 NLP(=自然语言处理)项目的情况下,你需要让你的文本非常干净,去掉那些不改变意思的不常见的字符。如果我们仔细看看“标题”一栏,
df['title']

First 10 values in the “title” column
我们可以看到几个要删除的特殊字符,比如:,。()[ ] + | -
为了安全起见,可以使用特殊字符的完整列表,并使用循环删除它们:
spec_chars = ["!",'"',"#","%","&","'","(",")",
"*","+",",","-",".","/",":",";","<",
"=",">","?","@","[","\\","]","^","_",
"`","{","|","}","~","–"]for char in spec_chars:
df['title'] = df['title'].str.replace(char, ' ')
现在你不应该在你的标题栏中有这些字符。因为我们用加号替换了特殊字符,所以在某些标题中,我们可能会以双空格结束。

String example before removing the special character

String example after removing the special character which creates an extra space
让我们通过使用空格拆分每个标题并使用连接再次重新连接单词来删除它们。
df['title'] = df['title'].str.split().str.join(" ")
我们完成了这个专栏,我们删除了特殊字符。注意,我没有包括货币字符和点“.”在上述特殊字符列表中。原因是一些结果标题包含他们正在销售的机票的价格(例如“514.98 美元”),在下一部分中提取这些信息可能会很有意思。
2.从系列中提取相关内容
使用数据集时,重要的是使数据集更加清晰、易读。你的数据集可能最终被摄入机器学习管道,也就是说,这个想法是从这个数据集中提取大量信息。我将展示一个例子,但是您将提取的信息的粒度取决于您的目标。
假设您想从结果标题(即标题列)中提取所有以美元表示的价格。我们使用一个正则表达式函数来完成这个任务。如果你愿意,你可以在这里用你的正则函数做一些测试。
df['dollar_prices'] = df['title'].str.extract('(\$\.d*\.?\d*)')
我们现在已经在标题列中提取了所有以美元为单位的价格。
我们现在显示结果列的唯一值:
df['dollar_prices'].unique()

正如你所看到的,我们有一些 NaN(=非数字)值(上面数组中的第四个位置)。有些图书没有美元价格,所以 regex 规则找不到它,取而代之的是“nan”。我们将在下一节看到如何处理 NaN 值。
3.检查 NaN 值
大多数情况下,一个大型数据集将包含 NaN 值。如何处置他们?我们应该离开他们吗?绝对不行!你不能离开他们,因为你可能要在数字列上执行计算,或者仅仅是因为你将来要为一个数据科学团队建模。对于那些了解机器学习的人来说,或多或少,你知道你的特征/变量/预测器在训练一个模型之前应该是数值型的。出于所有这些原因,我们希望修复 NaN 值。我不会花太多时间讨论不同的策略,有很多文章解释不同的策略。您可以做什么:
- 移除相应的行:只有在移除行不影响数据集中的分布或者它们不显著时,才可以这样做。如果在您的 1M 数据集中只有 3 行具有一些 NaN 值,那么删除这些行应该是安全的。否则,您可能不得不采用下一个选项。
- 用统计量替换它们(在数值列中):可以用列的平均值替换 NaN 值。小心,你不想歪曲数据。查看另一个列(如分类列)并根据每个类别的平均值替换 NaN 值可能更合适。让我解释一下。例如,在泰坦尼克号的数据集中,每一行代表一名乘客。票价列中的一些值丢失(NaN)。为了用更准确、更接近实际的值来替换这些 NaN:例如,您可以用同一票种的各行票价的平均值来替换它们。你这样做是假设买同一种票的人支付的价格大致相同,这是有道理的。
- 否则:试着了解你的领域,你的背景,这将帮助你知道如何以一种安全的方式替换丢失的变量。如果您不会在机器学习管道中使用该数据集,您可以用另一个值来替换 NaN,如字符串(在字符串类型列的情况下)“None”或“NoData”,任何与您和将使用该数据集的人相关的值。
关于处理 NaN 值的更多例子,我推荐阅读一篇关注这一点的文章。
关于我们的数据集,让我们使用名为 seaborn 的可视化库来看看 NaN 值的比例是多少。您可以直接使用 Pandas 函数,但我认为最好先使用可视化方式,以便了解您的数据:
sns.heatmap(df.isnull(), cmap='viridis')

在这张热图中,您可以看到每列中的 NaN 值为黄色(取决于您使用的 cmap ,但我的是黄色)。这并不意味着没有黄色的列没有任何 NaN,它只是在热图的顶部或底部有一些 NaN,所以它们与热图的图形边框相混淆(这是由于笔记本中打印的图像的分辨率)。无论如何,不要犹豫使用显示原始数据。isnull() 熊猫功能。
在我们的例子中,我们将只保留在标题中有美元价格的行,因此我们将只保留在美元价格列中有值的行:
df = df.dropna(subset=['dollar_prices'])
得到的 DataFrame 只包含 dollar_price 有值的行,其他的都被删除了。
4.更改系列的类型
在这个数据集中,我们可能没有太多的类型变化要做。这取决于你的背景和数据集的质量。如果您对我们在 df.info() 中看到的列类型感到满意,那很好。
输入文件可能来自您的一位同事,他将数据保存到 Excel 文件(.xlsx)。在这种情况下,这是因为 Excel 在这方面很糟糕,您的列的格式可以更改,特别是对于您的 Ids 列。我所说的“Id”是指一个列,它代表您所列出的实体之一的 Id。例如,如果您正在操作一个包含产品库存的数据集,那么其中可能会有一个 ProductId 列。无论如何,Excel 中的数字 id 可以被解释为数字列或文本列,老实说,我不知道 Excel 何时选择其中之一。
对于您的数据操作,这取决于您希望如何处理脚本中的 id,您希望操作文本(字符串)还是数值(整数)?这取决于你,但是在你的脚本中要保持一致,如果你需要连接两个数据帧,例如,基于一个 Id 列,首先以相同的格式转换它们(字符串或整数,但是你必须选择一个)。
让我们回到我们的数据集。在第二部分中,我们创建了一个包含美元价格的新列。在这一点上,我们知道有美元,对不对?因为关于货币的信息在列名中。也就是说,我们可以去掉美元字符:
df['dollar_prices'] **=** df['dollar_prices'].str.replace('$', '')
最后一步是检查这个列的类型。我们应该有一个字符串,因为我们用 regex 规则提取了数据作为字符串:
df.dtypes

这里我们的美元价格是一个对象,实际上意味着一个 s 字符串。我们真的想把我们的价格当成字符串吗?对于价格,我们更喜欢使用 floats ,我们只需要转换列类型。
df['dollar_prices'] = pd.to_numeric(df['dollar_prices'], errors='coerce')
如果您再显示一次 dtypes ,您会注意到 dollar_prices 列不再是一个对象,而是一个 float64 类型。

结论
我希望你能找到一些你可能问过自己的问题的答案。正如我在开始时所说的,没有统一的方法来清理数据集,一些检查必须完成,例如 NaN 值。剩下的真的要看项目需要什么水平的质量了?要提取哪些相关数据?
此外,请记住,您可以从数据集(第 2 节)中提取的所有内容可能对可视化专家非常有帮助,他们将使用您的数据集作为源来构建仪表板。
不要犹豫写评论和问题。
用 Fast.ai 简化深度学习
Fast.ai 程序员实用深度学习课程概述

深度学习是一个因守门而臭名昭著的领域。如果你试图在网上寻找如何进入这个领域的答案,你可能会发现自己被一长串要求淹没了。
这通常会吓跑该领域的潜在贡献者,给我们留下世界上一小部分可以消费和生成深度学习模型的人。
Fast.ai 是一个试图解决这种不平等的组织。它是由杰瑞米·霍华德和雷切尔·托马斯在 2016 年创立的,目标是让深度学习变得更容易实现。它主要以其免费课程和开源库而闻名(名为 fastai ,建立在脸书 PyTorch 库之上)。这是最受欢迎的课程之一,本文的重点是面向程序员的实用深度学习。
“我希望很多人会意识到,即使他们不是斯坦福大学深度学习博士,他们也可以实现深度学习的最先进成果。”— 杰瑞米·霍华德
由于数据和计算资源的可用性增加,面向门外汉的深度学习是一个非常受欢迎的领域。它在图像识别、自然语言处理和语音识别等领域产生了最先进的成果。它是自动驾驶汽车、垃圾邮件过滤器和内容推荐系统的动力。高级别的深度学习是神经网络的扩展,它使用一组输入来训练一组权重,然后产生一个输出(就像将图像分类为热狗)。在深度学习的情况下,有更多的层,这意味着要学习更多的权重。

Fast.ai 引入了一种自上而下的学习方式,这与大多数其他课程不同,这些课程都是从基础开始,然后逐步向上。直觉上这是有意义的,如果你教某人打篮球,你没有教他们这项运动的物理学。相反,你教他们足够多的东西来开始玩,让他们的兴趣引导他们对这项运动有更深的理解。同样,在深度学习课程的前 45 分钟内,Jeremy 从头到尾向您介绍了如何构建一个最先进的图像分类器。第二课让你根据你在谷歌上找到的图像建立和生产你自己的图像分类器。
在本文中,您将对他们的深度学习课程有一个高层次的概述。你还将学习如何从课程中获得最大收益,以及这对深度学习的未来意味着什么。
课程概述
1。图片分类 第一课涵盖了 Jeremy 的背景和资历,参加课程的动机以及完成课程后的可能性。他揭穿了一些关于深度学习的常见神话(例如需要大量数据和 GPU),并展示了一些过去的学生项目和成功故事。
本课程的其余部分涉及使用 fastai 库构建图像分类器,以达到最先进的精度。因为这门课是以自上而下的方式教授的,所以第一课的重点是让一些东西尽快运转起来。
2。生产;继续讨论图像分类器的主题,你现在有机会构建自己的图像分类器。在根据你在谷歌上找到的图片训练出一个模型后,你就可以学习如何将这个模型投入生产。本课最精彩的部分是从一个想法到一个生产应用程序的容易程度(这里有一个在本课中构建的熊图像分类器的实例)。
课程的其余部分包括学习随机梯度下降,从头开始构建。
3。多标签;第三课的第一部分概述了如何处理多标签分类。它还介绍了 fastai 库中使用的一些函数和类。
随着课程的深入,您将进入图像分割的任务。当然,这在自动驾驶汽车和图像标记等应用中得到了广泛应用。你也开始遇到一些与应用程序相匹配的理论,比如学习为什么某些学习速率会产生更好的结果。
这一课也给出了自然语言处理(NLP)的预览。
4。NLP 表格数据;推荐系统
第一个是迁移学习的想法,它允许你使用一个预先训练好的模型来完成一项特定的任务。有了这个预先训练好的模型,你就可以学习如何预测负面或正面的电影评论。基于这个简单的例子,你可以使用标准化的迁移学习方法达到最先进的效果。
然后,您将继续讨论为表格数据(您可能会在电子表格或关系数据库中看到的数据)构建深度学习模型的主题。构建的示例模型预测个人收入是否超过 5 万美元。
本课最后概述了推荐系统(协同过滤)。Jeremy 探讨了冷启动问题(如何向新用户推荐某样东西?)并向您展示一个简单的 Excel 电影推荐示例。
5。反向投影;从零开始的神经网络 顾名思义,这节课更多的是基于理论。在这一点上,你开始学习更多关于微调、解冻/冻结层、正则化、使用区别学习率、嵌入、偏差和其他在前面的课程中从你那里抽象出来的东西。
本课以一个在 excel 中构建的神经网络示例结束,向您展示它是如何工作的。
6。CNN 深潜;伦理学 第六课继续理论的探索,比如辍学的重要性,为什么会有好的结果。本课另一个有趣的部分是数据扩充的主题。这是将一幅图像转换成多幅图像的过程(如下图所示)。

然后你会接触到更多关于卷积神经网络的理论,并深入了解 CNN 被训练时到底发生了什么。
本课的最后一部分专门讨论数据科学中的道德规范。这很大程度上包括了机器学习技术被误用或有能力被误用的所有方式。面部识别系统和翻译服务中的偏见是滥用的两个例子。
7 .。Resnet 优网;GANs 最后一课继续以理论为基础,涵盖了使用 Resnet 和 U-net 来实现最先进的结果。
你也有机会接触到生殖对抗性网络,这个话题最近通过 DeepFakes 等应用程序得到了一些媒体报道。本课中的 GAN 应用于清理映像的环境中,您也有机会了解其内部工作原理。
最后一节回顾了 NLP 并探讨了递归神经网络的主题,在这里您有机会构建一个预测序列中下一个单词的模型。
作为最后的行动号召,Jeremy 建议采取以下后续步骤来巩固和发展从课程中学到的知识

在 7 节课结束时,你将会涵盖一系列广泛的主题,如下图所示。

第二部分将于 2019 年 6 月发布,将涵盖以下主题:

充分利用 Fast.ai
为了从本课程中获得最大收益,我们建议你看一遍讲座,然后看第二遍或第三遍,重点是复制笔记本。一旦你觉得你已经掌握了一些东西,你就可以开始用你自己的数据和项目,甚至是一场 Kaggle 比赛来实现这些技术。把一个项目看完比留下几个未完成的要好得多。
阅读研究论文也是获取你所学知识并在此基础上发展的好方法。拥有博士学位并不是阅读研究论文的必要条件,所以不要感到害怕。Arxiv Sanity Preserver 是一个很好的起点,因为它提供深度学习各种子专业的免费预印论文。如果你不确定如何阅读研究论文,看看这两个指南(指南一,指南二)
简化深度学习
虽然 Fast.ai 并不承诺你在完成他们的课程后会成为深度学习专家,但它确实为你开始实验和修补埋下了最初的种子。许多 Fast.ai 学生继续做许多有趣的工作,无论是在谷歌这样的人工智能驻地,还是在初创公司的深度学习。
简化深度学习背后的整个想法是,它将允许领域专家将他们独特的专业知识与深度学习结合起来。认为一小群机器学习博士将能够解决深度学习可以解决的所有问题是不现实的。尤其是如果他们没有通过严格的学习和实践建立起来的相关领域的专业知识。
Fast.ai 是深度学习领域一个很有前景的发展。如果你对深度学习感兴趣,这是最好的资源之一,可以开始并看看有什么可能。
深度学习完全初学者指南:人工神经网络
深度学习入门!在 15 分钟内攻克人工神经网络的基础知识
本文是 深度学习 系列完整初学者指南的一部分。

Image by Lolame via Pixabay
什么是深度学习?
这是从例子中学习。差不多就是这么回事!
在非常基础的层面上,深度学习是一种机器学习技术。它教会计算机通过层层过滤输入,以学习如何预测和分类信息。观察可以是图像、文本或声音的形式。
深度学习的灵感来自人脑过滤信息的方式。它的目的是模仿人类大脑如何工作来创造一些真正的魔法。
GIF via GIPHY
深度学习试图模仿新大脑皮层中神经元层的活动。
这是一个名副其实的人工神经网络。
在人脑中,大约有 1000 亿个神经元。每个神经元都与大约 10 万个相邻的神经元相连。这就是我们试图创造的,但是在某种程度上是为机器服务的。

Image by geralt on Pixabay
这对神经元、轴突、树突等意味着什么?神经元有一个体,树突和一个轴突。来自一个神经元的信号沿着轴突传递到下一个神经元的树突。信号传递的连接被称为突触。

Image by mohamed_hassan on Pixabay
神经元本身是没用的。但是当你有很多这样的人时,他们会一起创造出一些真正的奇迹。这就是深度学习算法背后的想法!你从观察中获得输入,然后把你的输入放到一个层中。该层创建一个输出,该输出又成为下一层的输入,依此类推。这种情况反复发生,直到你的最终输出信号!
因此神经元(或节点)获得一个或多个信号(输入值),这些信号通过神经元。该神经元传递输出信号。把输入层想象成你的感官:例如,你看到的、闻到的和感觉到的东西。这些是一次观察的独立变量。这些信息被分解成数字和计算机可以使用的二进制数据位。(您需要标准化或规范化这些变量,以便它们在相同的范围内。)
那么突触呢?每个突触都被分配了权重,这对人工神经网络至关重要。重量是人工神经网络学习的方式。通过调整权重,人工神经网络决定信号传递的程度。当你训练你的网络时,你决定如何调整权重。
人工神经网络是如何学习的?
有两种不同的方法可以让程序做你想做的事情。首先,有特别指导和硬编程的方法。在这种方法中,您确切地告诉程序您想要它做什么。然后还有神经网络。在神经网络中,你告诉你的网络输入和你想要的输出,让它自己学习。通过允许网络自己学习,我们可以避免输入所有规则的必要性。对于神经网络,你可以创建架构,然后让它去学习。一旦它训练好了,你可以给它一个新的图像,它将能够区分输出。

Photo by Annie Spratt on Unsplash
有不同种类的神经网络。一般分为前馈和反馈网络。
前馈网络是一个包含输入、输出和隐藏层的网络。信号只能单向传播(向前)。输入数据传递到执行计算的层。每个处理元件基于其输入的加权和进行计算。新值成为新的输入值,用于下一层(前馈)。这将贯穿所有层,并决定输出。前馈网络通常用于例如数据挖掘。
一个反馈网络(例如,一个递归神经网络)有反馈路径。这意味着它们可以使用环路双向传输信号。神经元之间所有可能的连接都是允许的。由于这种类型的网络中存在环路,因此它成为一个非线性动态系统,不断变化直到达到平衡状态。反馈网络通常用于优化问题,其中网络寻找相互关联的因素的最佳安排。
大多数现代深度学习架构都是基于人工神经网络(ann)的。它们使用多层非线性处理单元进行特征提取和转换。每个后续层使用前一层的输出作为其输入。他们所学的形成了概念的层次结构。在这个层次结构中,每一级都学会将其输入数据转换成越来越抽象和复合的表示。

Image by ahmedgad on Pixabay
这意味着,例如,对于一幅图像,输入可能是像素矩阵。第一层可以对边缘进行编码并组成像素。下一层可能构成边缘的排列。下一层可能编码鼻子和眼睛。下一层可能会识别出图像包含人脸,等等。
神经元内部发生了什么?输入节点接收数字形式的信息。信息以激活值的形式呈现,每个节点都有一个编号。数字越高,激活程度越大。
基于连接强度(权重)和传递函数,激活值传递到下一个节点。每个节点对其接收的激活值求和(它计算加权和),并基于其传递函数修改该和。接下来,它应用一个激活函数。激活函数是应用于这个特定神经元的函数。由此,神经元知道是否需要传递信号。激活通过网络运行,直到它到达输出节点。然后输出节点以我们可以理解的方式给我们信息。您的网络将使用成本函数来比较输出和实际预期输出。模型性能由代价函数来评价。它表示为实际值和预测值之间的差值。您可以使用许多不同的成本函数,查看网络中的误差。你在努力减少功能损失。(本质上,损失函数越低,就越接近你想要的输出)。信息返回,神经网络开始学习,目标是通过调整权重来最小化成本函数。这个过程被称为反向传播。
有兴趣了解更多关于成本函数的知识吗?查看 神经网络中使用的成本函数列表,以及堆栈交换上的应用
在前向传播中,信息被输入到输入层,并通过网络向前传播,以获得我们的输出值。我们将这些值与我们的预期结果进行比较。接下来,我们计算误差并反向传播信息。这允许我们训练网络并更新权重。反向传播允许我们同时调整所有的权重。在这个过程中,由于算法的结构,你可以同时调整所有的权重。这使您可以看到神经网络中的每个权重对错误的哪一部分负责。
渴望更多?你可能想读一下 Yann LeCun 等人的 高效反向投影 ,以及 Michael Nielsen 的 神经网络和深度学习 。
当您将重量调整到最佳水平后,您就可以进入测试阶段了!
什么是加权和?
神经元的输入可以是来自训练集的特征,也可以是来自前一层神经元的输出。两个神经元之间的每个连接都有一个独特的突触,并附有独特的权重。如果你想从一个神经元到下一个神经元,你必须沿着突触行进,并支付“通行费”(重量)。然后,神经元将激活函数应用于来自每个传入突触的加权输入的总和。它将结果传递给下一层的所有神经元。当我们谈论更新网络中的权重时,我们谈论的是调整这些突触上的权重。
一个神经元的输入是前一层所有神经元的加权输出之和。每个输入都乘以与将输入连接到当前神经元的突触相关联的权重。如果在前一层中有 3 个输入或神经元,则当前层中的每个神经元将具有 3 个不同的权重:每个突触一个。
那么什么是激活函数呢?
简而言之,一个节点的激活函数定义了该节点的输出。
激活函数(或传递函数)将输入信号转换成输出信号。它在 0 到 1 或-1 到 1 的范围内映射输出值。这是一个抽象概念,代表细胞内动作电位发放的速率。这是一个数字,代表细胞被激发的可能性。最简单的,函数是二元的:是(神经元触发)或否(神经元不触发)。输出可以是 0 或 1(开/关或是/否),也可以是范围内的任何值。例如,如果您使用映射范围在 0 和 1 之间的函数来确定图像是一只猫的可能性,输出 0.9 将显示您的图像实际上是一只猫的概率为 90%。

Photo by minanafotos on Pixabay
我们有什么选择?有许多激活函数,但以下是四个非常常见的函数:
- 阈值函数这是一个阶跃函数。如果输入的合计值达到某个阈值,该函数将传递 0。如果它等于或大于零,那么它将传递 1。这是一个非常严格,简单,是或不是的函数。

Example threshold function
- Sigmoid 函数:该函数用于逻辑回归。与阈值函数不同,它是一个从 0 到 1 的平滑渐进过程。它在输出层非常有用,并且大量用于线性回归。(线性回归是统计学和机器学习中最知名的算法之一)。

Example sigmoid function
- 双曲正切函数这个函数非常类似于 sigmoid 函数。与从 0 到 1 的 sigmoid 函数不同,该值从-1 到 1 变到 0 以下。虽然这不是生物学中发生的事情,但这个函数在训练神经网络时会给出更好的结果。神经网络有时会在使用 sigmoid 函数进行训练时“卡住”。当有很多强烈的负面输入使输出接近于零时,就会发生这种情况,这会扰乱学习过程。

Example hyperbolic tangent function (tanh)
- 整流函数这可能是神经网络宇宙中最流行的激活函数。这是最有效的,也是生物学上最合理的。尽管它有一个扭结,但在 0°扭结后,它是平滑和渐变的。举例来说,这意味着你的输出要么是“否”,要么是“是”的百分比这个函数不需要标准化或其他复杂的计算。

Example rectifier function
想潜得更深吗?查看 Xavier Glorot 等人的 深度稀疏整流器神经网络
比如说,你想要的值是二进制的。你在寻找一个“是”或“否”。你想使用哪个激活功能?从上面的例子中,你可以使用阈值函数,或者你可以使用 sigmoid 激活函数。sigmoid 函数可以给你一个肯定的概率。

那么,权重究竟是如何调整的呢?
你可以使用蛮力方法来调整权重,并测试数千种不同的组合。即使是最简单的只有五个输入值和一个隐藏层的神经网络,你也会得到 10⁷⁵可能的组合。在世界上最快的超级计算机上运行这个程序需要的时间比迄今为止宇宙存在的时间还要长。

Photo by skorchanov on Pixabay
然而,如果您使用梯度下降,您可以查看权重的倾斜角度,并找出它是正还是负,以便继续向下倾斜,在您寻求达到全局最小值的过程中找到最佳权重。
如果你使用梯度下降,你可以查看重物的倾斜角度,并找出它是正还是负。这允许你继续下坡,在你寻求达到全局最小值的过程中找到最佳权重。
****梯度下降是一种求函数最小值的算法。你会一遍又一遍地看到类似的情况,有人被困在山顶,试图下来(找到最小值)。大雾使她看不清路,所以她使用梯度下降法到达山脚。她看了看她所在的山的陡峭程度,然后向最陡的下坡方向走去。你应该假设陡度不会立即变得明显。幸运的是,她有一个工具可以测量陡度。不幸的是,这个工具需要很长时间。她想尽可能少地使用它,以便在天黑前下山。真正的困难是选择她使用工具的频率,这样她就不会偏离轨道。在这个类比中,人就是算法。山的陡度是该点误差面的斜率。她走的方向是该点误差曲面的梯度。她使用的工具是微分(误差表面的斜率可以通过对该点的平方误差函数求导来计算)。在进行另一次测量之前,她行进的速率是算法的学习速率。这不是一个完美的类比,但它让你很好地理解了梯度下降是怎么回事。机器正在学习模型应该采用的梯度或方向,以减少误差。

随机梯度下降
梯度下降要求代价函数是凸的,但如果不是呢?

正常梯度下降将卡在局部最小值而不是全局最小值,从而导致网络不合格。在正常梯度下降中,我们将所有行插入同一个神经网络,看一看权重,然后调整它们。这被称为批量梯度下降。在随机梯度下降中,我们一行接一行,运行神经网络,查看成本函数,调整权重,然后移动到下一行。实际上,您正在调整每一行的权重。
随机梯度下降有更高的波动,这允许你找到全局最小值。它被称为“随机的”,因为样本是随机混合的,而不是作为一个单独的组或当它们出现在训练集中时。它看起来可能会慢一些,但实际上更快,因为它不必将所有数据加载到内存中,并在数据一起运行时等待。批量梯度下降的主要优势在于它是一种确定性算法。这意味着,如果你有相同的起始重量,每次运行网络,你会得到相同的结果。随机梯度下降总是随机工作。(您也可以运行小批量梯度下降,您可以设置行数,一次运行那么多行,然后更新您的权重。)
已经提出并使用了对基本随机梯度下降算法的许多改进,包括隐式更新(ISGD)、动量法、平均随机梯度下降、自适应梯度算法(AdaGrad)、均方根传播(RMSProp)、自适应矩估计(Adam)等等。
喜欢这个吗?你可能想看看安德鲁·特拉斯克 和迈克尔·尼尔森的 神经网络和深度学习
因此,这里有一个用随机梯度下降训练人工神经网络的快速演练:
- 1:将权重随机初始化为接近 0 的小数字
- 2:将数据集的第一个观测值输入到输入图层中,每个要素位于一个输入结点中。
- 3: 前向传播——从左到右,神经元的激活方式是每个神经元的激活受到权重的限制。你传播激活,直到你得到预期的结果。
- 4:比较预测结果和实际结果,测量产生的误差。
- 5: 反向传播 —从右到左,误差反向传播。根据权重对误差的影响程度来更新权重。(学习率决定了我们更新权重的程度。)
- 6: 强化学习(重复步骤 1-5,每次观察后更新权重)或* 批量学习(重复步骤 1-5,但只在一批观察后更新权重)。***
- 7:当整个训练集已经通过 ANN 时,这是一个时期。重复更多的纪元。
就是这样!你现在知道了在人工神经网络中发生的事情背后的基本思想!

Photo by Sam Mathews on Unsplash
还跟我在一起? 过来看第三部 !
和往常一样,如果你对这些信息做了什么酷的事情,请在下面的回复中留下评论,或者随时联系 LinkedIn@ annebonnerdata!
辛普森悖论导论
辛普森悖论是一个迷人的现象,说明了因果关系在推理中的重要性。如果你还没有读过这本书,你应该去看看为什么的书——这是这十年来最重要的书之一。
我认为辛普森悖论很好地介绍了因果推理的价值。在这篇文章中,我将通过几个例子来阐明它是如何产生的,以及我们如何开始形式化我们对它的思考。
击球率
肯·罗斯是辛普森悖论的一个常见例子。他指出,当观察德瑞克·基特和大卫·贾斯蒂斯的击球率时,一个奇怪的矛盾出现了:

David Justice has a better batting average every year, but Derek Jeter has a better batting average overall!
德瑞克·基特每个赛季的平均打击率都更差,但总的来说却有更好的平均打击率!
如果我们盯着这一点看一会儿,随着一些事情的突出,原因就变得很清楚了。第一年,德里克和大卫的分数都很低;第二年他们都得了高分。最重要的是,德里克的低分仅仅来自于几次击球,因此对他的总体平均得分影响不大。另一方面,大卫恰恰相反——他的较低分数占了他击球尝试的绝大部分。即使大卫在没有加权的两年中做得稍好,当我们把蝙蝠的这种不平衡与它们在分数上类似的巨大差异结合起来时,我们得到了这个结果。辛普森悖论告诉我们,总体统计数据的适当加权平均值可能与我们在每个子组中看到的趋势相矛盾。
下面是另一个简单的例子——这次是图形化的。比方说,非常年轻的人(往往比成年人矮)经常打篮球,而且打得很好。比方说,年纪大的人(往往更高)停止练习,通常实际上很不擅长投篮。
现在,假设我们测量他们的投篮表现,希望最终证明高个子更擅长篮球。我们得到的是这样的:

A graphical example of Simpson’s paradox. Taller kids and taller adults are both better at basketball, but in general being tall makes you worse!
又一个难题!对于每个年龄段的人来说,个子高意味着你是一个更好的篮球运动员。总的来说,个子高会让你变得更糟!
我们都知道个子高会让你变得更优秀,解释这个结果的正确方法是用年龄来分隔,而不是用总体人口。但是为什么以及我们是如何知道的呢?我们如何将经验法则推广到更复杂的情况?
因果推理
这里有一个更难的例子,它反映了现实生活中的一项医学研究。假设你生病了,给你开了一种药,对以前的病人有如下效果:

Uh oh. The drug is good for men, good for women, and bad for people.
你没看错。如果你是男性,这种药物会增加你康复的机会;如果你是女性,这也会增加你康复的几率。但如果你是一个人,它更有可能伤害你。你吃这种药吗?
值得庆幸的是,上述说法在逻辑上是不连贯的,是错误的。但是很容易阅读数据并以这种方式看待它。我们如何解决这个问题?
答案
我们知道,这里有三个因素给了我们辛普森悖论。有(1)患者的性别,(2)药物的有效性,以及(3) 患者有多大可能属于治疗或非治疗组。请记住,这是组的不平衡,使整体平均加权可能不同于每个子组的趋势。
那么问题就变成了,因果关系在哪里?可以肯定地说,你的性别不会因为你的治疗或康复而改变。另一方面,性别似乎会影响你选择治疗的可能性——女性在 75%的情况下选择治疗,而男性只有 12%。
鉴于我们以这种方式检查数据,我们清楚地相信性别可能会影响恢复。最后(也很明显),治疗会影响你康复的机会,而不是相反。我们可以画一个漂亮的因果图来总结我们所有的因果知识。

A simple causal diagram of what’s going on. Gender alters the likelihood of accepting treatment. We also expect the likelihood of recovery to be a function of both receiving treatment AND gender.
哪里出了问题?希望很清楚,图表中“受性别影响的治疗选择”部分——将性别与治疗联系起来的箭头——是导致我们悖论的原因。
我们不希望那个信息改变我们的决定。一个人选择的可能性有多大?这种治疗并不能告诉我们这种治疗最终对他们有多有效。为了准确了解这种药物对整个人类的疗效,我们必须消除这种联系。换句话说,我们应该平均每个组的百分比,而不是根据大小来加权。这消除了两性治疗选择不均衡的影响。

Simpson’s paradox is resolved!
它概括了吗?
在上面的例子中,信任子组而不是整体平均值是有意义的。其他情况下,可以反过来。
举一个例子(这个例子来自 Judea Pearl 的书),我们研究了一种用于预防心脏病发作的药物,并评估了它对低血压和高血压患者的影响。我们看到了类似的问题——这种药物减少了低血压和高血压患者的心脏病发作,但总体上增加了心脏病发作!我们该怎么办?在这种情况下,当我们绘制因果图时,它可能看起来像这样:

Causal Diagram for our second example. In this case, the blood pressure is a mediating variable in the path from treatment to heart attack, and thus we should trust the overall statistic.
这看起来非常类似于我们的第一个例子,但它不是。仔细看。我们小组的位置和治疗方法都变了。
在第一个病例中,我们的亚组(性别)影响了接受治疗的。在第二个例子中,我们的亚组(血压)受到接受治疗的的影响。
在这种情况下,心脏病发作的风险和患者的血压都是治疗的因果下游,所以我们应该而不是按血压细分。血压是治疗发挥作用的一种机制——它是从治疗到心脏病发作风险之间因果关系的中介变量。
与之前的案例形成对比,在之前的案例中,治疗和恢复之间的关系受到性别的影响。性别是治疗选择和恢复的上游;为了了解治疗和康复之间的真正关系,我们不得不以此为条件。当处理没有图形双亲时,我们不需要辅助变量。当它发生时(并且该母公司有一个“后门路径”来影响利息),我们会这样做。
另一种方法
思考辛普森悖论的另一种方式是通过纯统计的语言。首先,让我们稍微绕一圈,回顾一下基础知识。
联合概率可以很容易地分解成条件的乘积。这里有一个有趣的例子——你有了一只新狗,你对它的个性很感兴趣!有些狗很年轻,有些很聪明(有些不聪明),有些则很有活力。
下面是代表狗的性格概率分布的方框。我们把正方形的面积设为 1。放下一个别针,它所代表的狗就会有下面的性格特征。
在所有可能的狗中,你可以看到一些是年轻的,一些是聪明的,一些是精力充沛的,还有一些是三者兼而有之。一只随机的狗具有一些特定特征组合的可能性可以很容易地被分解——下面是一个简单的例子来说明当我们寻找年轻、聪明和精力充沛的狗时(Y=1,I=1,E=1)。

Factoring a joint probability into the product of conditionals.
为什么你会想把一个关节放入这样的条件句中呢?在辛普森悖论的背景下,我们将通过重温上面的第一个例子来回答这个问题。
在那个例子中,我们在看(T)治疗、(G)性别和(R)康复的联合概率。我们想询问在给定其他性别和治疗组合的情况下,R=1(成功康复)的可能性。让我们将关节分解,这样我们就可以使用它:

All possible ways to rewrite that joint as a product of conditioned variables.
有六种方法可以分解三个变量的结合。然而,在这种情况下,我们试图用观察关系(在这种情况下,可以说‘假设某人成功康复,他是一个男人的概率’之类的话),而不是用询问他们之间的因果关系(‘如果我们强迫治疗的概率等于 1,一个男人的康复会怎么样?’).T5 这里我们介绍珍珠的 do-operator 。
代替 p(R|T),一个关于治疗和恢复之间相关性的观察,我们实际上关心 p(R |do(T))——给定我们因果干预和强制治疗(通过意志力设定 T=1)的恢复概率。do 运算符限制了我们如何分解关节,因为我们必须将 do(T)保持在关于恢复的条件线的右侧,以回答关于治疗如何影响恢复的因果问题。
所以我们对 p(R|G,do(T)) 感兴趣。这给了我们两个选择:

在第二个选择中,我们发现 p(G|do(T))是一个无意义的关系。我们现在认为是一种因果关系,这个术语回避了一个问题“如果你接受治疗,你的性别改变的可能性有多大?”如果当我们改变(T)时性别不变,我们就不能在因果关系中连贯地使用这样一个术语。
另一方面,第一个等式没有逻辑缺陷。我们注意到 p(do(T)|G)正好是 1——我们在任何理性条件下的 do(T)概率将是 1,因为这是 do 运算符的定义。我们可以去掉这个术语,简化我们的表述。
另一种说法是,基于性别接受治疗的可能性是而不是我们想要的影响我们对治疗成功概率的评估的东西,所以这个术语必须删除。我们知道这个术语是坏消息,因为我们有因果直觉。我们的条件必须遵循我们的因果本能,匹配我们的因果图,使得子节点的概率总是(并且仅仅)取决于它们的父节点。
我们只剩下 p(R | do(T),G) * p(G) 。它向我们展示了康复的可能性与该性别人群的比例有关,而不是与总人口有关。这通过一个更正式的过程给了我们和我们的因果图一样的答案。
现实生活,伙计
辛普森悖论可能出现在现实生活中——就像加州大学伯克利分校众所周知的有争议的性别偏见问题。然而,它真正的美妙之处在于阐释和介绍了统计学中因果推理背后的基本原理。
我们所做的一切在标准统计学的背景下都没有意义,但确实有意义。我希望这篇关于辛普森悖论的介绍让你对如何开始形式化你的因果逻辑有所体会。
长期以来,统计学缺乏讨论因果推理和关系的语言。谢天谢地,统计世界似乎正在积极转变。随之而来的将是科学的一大福音。
辛普森悖论
理解统计错觉
辛普森悖论是一个封闭的术语,指的是在全球层面分析时,群体层面的趋势被隐藏或逆转的情况。这些都是需要思考的 fum 例子,是混合效果模型的主要候选对象,这也是我上一篇文章的主题。
说明
下面是一个来自维基百科文章的例子:蓝色和红色组有明显的积极趋势,然而用一条线回归这两个组会产生消极趋势。

在分析医疗保健索赔趋势时,通常会出现类似的情况(当然也可以扩展到其他行业)。下图显示了 4 种医疗服务——两种住院服务,由于需要多日住院,因此费用更高,还有两种门诊服务。一项常见的健康计划计划旨在将低强度/低成本的住院服务转移到门诊服务中:这样做可以为会员节省大量的自付额/共同保险费用。这产生了一个有趣的趋势,因为相对于其他低强度的门诊服务,低强度的住院服务通常强度更高。
这两个部分分别代表每年每千人的利用率(分析利用率时调整成员增长/衰退的常用指标)和单位成本(通常称为“成本/混合”,因为字段的聚合可以隐藏基础服务利用率的变化,而单位成本不会发生任何变化。
随着图表从第 1 期(P1)移动到第 2 期(P2),50 个单位的廉价住院治疗转移到门诊,在那里它们相对更贵。每项服务的单位成本不变。总的来说,这 50 个单位现在的价格是 9000 美元,而以前的价格是 12000 美元,但是潜在的趋势是骗人的。由于每个时段中高成本服务的比例较高,这两种单位成本趋势都有所增加。住院病人利用趋势下降,被门诊病人抵消。在 IP/OP 级别,该计划看起来像一场灾难!总体水平与现实相符:利用率没有变化,但一些服务现在成本更低了。

技术
如果你发现辛普森悖论的存在,就很容易解决它。在我的混合效应模型文章中,我们通过测试数据的同质性和分析残差图来确定这一点。在上面的维基百科图片中,你可以注意到,随着 x 轴从左向右移动,每组的残差从负值变为正值——残差中的这种相关性是一个好迹象,表明你错过了一些东西。
这强调了数据可视化和解释性数据分析的重要性。我们可以在这篇文章的结尾指出,虽然一张图片胜过千言万语,但有些图片需要千言万语的解释——这就是数据科学家和分析专业的价值。

在 PyBullet 中模拟 ML 的图像——快速简单的方法

当将深度强化学习(RL)应用于机器人时,我们面临一个难题:当深度学习需要数十万甚至数百万个样本时,我们如何训练机器人来完成一项任务?为了在从未见过的物体上实现 96%的抓取成功率,谷歌和伯克利的研究人员通过 580,000 次真实世界的抓取尝试训练了一个机器人代理。这项壮举花费了七个机器人和几个星期来完成。如果没有谷歌资源,对于一般的 ML 从业者来说,这似乎是没有希望的。我们不能指望使用物理机器人轻松地运行数十万次迭代训练,这容易磨损,需要人工监督,两者都不便宜。如果我们能够预先训练这种 RL 算法,以大幅减少现实世界中所需的尝试次数,这将更加可行。
随着深度学习的出现,RL 技术已经成熟,但对数据的需求也越来越大。为了弥合这一差距,许多研究人员正在探索训练数据的合成生成,利用 3D 渲染技术来制作任务环境的模型。虽然这种技术在模拟环境中产生了奇迹,但它并不能很好地推广到现实世界。任何与深度学习密切合作的人都知道,利用训练数据中的细微差别来“欺骗”任务是多么有效。在实践中,对于这些经过模拟训练的模型来说,真实世界是样本外的,因此不出所料,它们会失败。
最近的研究致力于减少“现实差距”,即真实世界和 3D 渲染传真之间的显著差异,以在模拟中预先训练机器人代理,从而大幅减少所需的真实世界训练量。通过积极地随机化模拟的外观和动态,模型学习理论上应该推广到现实世界的特征。根据最近的研究,这甚至可以将训练机器人所需的训练次数减少 99% 。对于产生数十万甚至数百万训练图像不可行的计算机视觉任务,这种方法非常有吸引力。
这个概念
比方说,我们希望生成合成训练数据来预训练一个分类器,该分类器可以区分一组小对象。我们希望选择一个对象,将其放入一个虚拟的“垃圾箱”,并呈现一个屏幕截图。理想情况下,我们希望随机重复这个过程数千次,以创建一个丰富的对象图像数据集。为此(受到 OpenAI 和 T2 谷歌所做工作的启发),我最近开始了为自己的应用程序生成训练数据的旅程。这让我想到了 PyBullet ,这是一个基于子弹物理 SDK 为机器人和机器学习应用程序设计的 Python 模块。
与 Maya 或 Blender 等其他 3D 渲染解决方案不同,PyBullet 专注于机器人技术,并具有关节、动力学模拟、正向和反向运动学等概念的原生实现。此外,它可以很容易地与包管理器一起安装,允许您将其他 Python 包,如 NumPy 和 TensorFlow,无缝集成到您的模拟中。虽然类似于专有的物理模拟软件 MuJoCo ,PyBullet 既免费又易于安装,因此对于希望进行模拟和机器人实验的人来说,它是一个很好的选择。本指南旨在为那些希望生成训练图像的人提供介绍,但是官方 PyBullet 快速入门指南可以在这里找到。请注意,本教程假设您使用的是 Python 版本 3.6 以上(如果您使用的是旧版本,请尝试为本教程创建一个虚拟环境)。
装置
首先,我们将在 Python 环境中安装 PyBullet。
pip install numpy
pip install pybullet
请注意,首先安装 NumPy 是可选的,但在渲染图像时建议这样做,因为在 C/C++和 Python 之间复制图像缓冲区的开销很大。
入门指南
现在您已经成功安装了所有的东西,让我们直接进入 PyBullet 会话。PyBullet 依赖于客户端-服务器模型,其中您的 Python 会话从模拟服务器发送和接收数据。主要有两种类型的服务器:直接和图形用户界面。顾名思义,GUI 允许您在 GUI 中查看物理模拟。“直接”对于无标题渲染非常有用,可用于在没有 GPU 的情况下进行高效渲染。当我们准备渲染成千上万的图像作为训练数据时,直接将是选择的模式,但现在我们将选择 GUI。让我们从初始化 PyBullet 服务器开始。首先启动一个 python 交互式会话,然后输入以下内容:
import pybullet as pb
physicsClient = pb.connect(pb.GUI)
这一步是必要的,因为它实例化了您将使用的世界模拟。physicsClient 变量包含该服务器的唯一 ID。这使得 PyBullet 可以运行多个服务器,甚至跨多台机器,并通过跟踪这些 id 从单个脚本管理它们。PyBullet 中的许多实体都用它们的 id 来指代,包括对象、碰撞器和纹理。
装载飞机
现在,您应该会看到一个模拟窗口弹出。太好了!这意味着你的模拟在起作用。您应该会看到一个主视口,其中包含用于 RGB、深度和分段数据的附加窗口。世界应该是完全空的。让我们首先为我们的模拟创建一个平面。这架飞机带有 pybullet_data 包,可以很容易地加载到任何模拟中。
import pybullet_data
pb.setAdditionalSearchPath(pybullet_data.getDataPath())
planeId = pb.loadURDF(‘plane.urdf’)
这有几个作用。首先,它加载另一个名为 pybullet_data 的模块,其中包含许多有用东西的占位符 URDFs。这被添加到路径中,这样当我们最终调用 loadURDF 时,它知道在哪里可以找到‘plane . urdf’并将其加载到我们的场景中。现在我们应该有一个平面横跨我们的场景。

加载要渲染的对象
PyBullet 中最受支持的文件格式是 URDF、SDF 和 MJCF。这些格式很容易加载和配置,并有自己的专门加载功能。然而,我们在网上找到的许多 3D 模型都是简单而流行的波前 OBJ 文件格式。加载这些是可能的,但是需要知道更多关于 PyBullet 中模型的具体细节,所以我们将咬紧牙关学习用 OBJ 文件加载我们自己的模型。
我们将使用程序生成的对象数据集进行模拟任务。这套包括 1000 个 OBJ 文件,其中包括相应的对撞机,材料和 URDF 版本。在这里下载那些,并把它们解压到你的项目文件夹中的一个名为“random_urdfs”的文件夹中。
物理模拟中的每个典型 3D 对象都由一个描述对象出现方式的视觉网格和一个描述对象物理尺寸以及它将如何与世界交互的碰撞器网格组成,例如它如何与其他物理对象碰撞。碰撞器网格,或简称为碰撞器,通常是我们视觉网格的简化表示,用它来计算碰撞要便宜得多。同时使用这两种类型的网格使我们的物理模拟能够高效运行,同时仍然具有视觉吸引力。
视觉形状
让我们从使用 createVisualShape 函数加载一个对象的可视形状开始。我们可以从程序生成的对象数据集中的一个对象开始。
visualShapeId = pb.createVisualShape(
shapeType=pb.GEOM_MESH,
fileName='random_urdfs/000/000.obj',
rgbaColor=None,
meshScale=[0.1, 0.1, 0.1])
第一个参数 shapeType 告诉 PyBullet 我们正在加载哪种形状。这些包括球体、长方体、圆柱体、平面、胶囊和网格。在我们的例子中,我们希望加载 OBJ 文件中描述的自定义网格。您还可以描述 RGB 颜色(作为描述红色、绿色、蓝色和 alpha 通道的长度为 4 的列表),我们在这里将其设置为 None 。我们稍后将替换对象的纹理。最后,OBJ 文件本质上没有单位,所以我们可能需要指定一个网格比例,以确保我们的对象不会太大或太小。
碰撞形状
现在我们有了一个视觉形状来参考我们的对象,但是没有物理形状。让我们使用 createCollisionShape 函数来加载它。
collisionShapeId = pb.createCollisionShape(
shapeType=pb.GEOM_MESH,
fileName='random_urdfs/000/000_coll.obj',
meshScale=[0.1, 0.1, 0.1])
类似于 createVisualShape 函数,我们需要使用 shapeType 指定我们想要的网格,以及在哪里找到带有文件名的网格数据。我们可以也应该指定网格比例,这样碰撞器就有了和视觉形状一样的形状。
多体
在这一点上,我们有一个视觉形状和一个碰撞器的参考,但这两个没有连接,我们还没有对象。这就是创建多体的用武之地。这个函数将这两者结合起来,并创建一个对象的实例。
multiBodyId = pb.createMultiBody(
baseMass=1.0,
baseCollisionShapeIndex=collisionShapeId,
baseVisualShapeIndex=visualShapeId,
basePosition=[0, 0, 1],
baseOrientation=pb.getQuaternionFromEuler([0, 0, 0]))
我们将碰撞器的 id 和可视形状传递给 createMultiBody 来创建一个新的物体。这个物体需要一个位置和方向来知道它在物理世界中的位置,还需要一个质量来知道它应该如何与其他物体交互。用长度为 3 的 x、y、z 坐标列表设定位置。为了简单起见,我们现在将质量设置为 1.0。
方向有点复杂,需要我们在一个长度为 4 的四元数列表中指定。四元数是三维空间中旋转的一种有用的四维表示,在 Blender、Maya 和 Unity 等计算机图形软件中非常重要,用于避免旋转伪影,如万向节锁。PyBullet 有一个方便的函数,叫做getquaternonfromeuler,可以让我们用绕 x、y、z 轴的旋转来描述旋转,人类更容易可视化和理解。这种旋转的表示被称为欧拉角。

注意:如果收到类似“不支持的纹理图像格式[random_urdfs/000/000.png]”的消息,那么忽略它。无论如何,我们将在下一步覆盖默认纹理。
将纹理应用到对象
成功!我们已经生成了一个物理对象,并将其放置在我们的世界中。回想一下之前我们想要随机化 3D 模型的外观和动态。让我们从外观开始,在这里下载并提取可描述纹理数据集。这包含了 47 个纹理类别的成千上万的图像。将这些内容提取到一个名为“dtd”的文件夹中。让我们随机选择一个纹理,并将其应用到我们的模型中。
import os, glob, randomtexture_paths = glob.glob(os.path.join('dtd', '**', '*.jpg'), recursive=True)
random_texture_path = texture_paths[random.randint(0, len(texture_paths) - 1)]
textureId = pb.loadTexture(random_texture_path)
pb.changeVisualShape(multiBodyId, -1, textureUniqueId=textureId)
此代码片段递归地获取文件夹“dtd”中所有纹理的路径列表,扩展名为“”。jpg”并随机抽取一个样本。然后将它传递给函数 loadTexture 来加载纹理并创建一个 ID。然后,它使用 changeVisualShape 函数将带有 textureId 的纹理应用于带有 multiBodyId 的对象。现在你的物体应该已经应用了纹理。如果愿意,也可以应用相同的方法将纹理附加到平面上。
注意-1 对于必需的参数 jointIndex 是必需的,该参数指定了您想要应用纹理的链接。链接对于创建具有运动部件层次的对象非常有用,例如具有许多关节的机器人。这是 PyBullet 中的一个重要概念,但超出了本教程的范围,所以我们提供了一个值“-1”来告诉它将它应用于基本对象而不是任何链接。
开始我们的模拟
到目前为止,我们已经创建了我们的模拟,但没有什么是移动的。这有几个原因。首先,我们没有对场景应用任何重力,所以我们的对象是漂浮在空间中的。让我们尽快做到这一点。
pb.setGravity(0, 0, -9.8)
这给场景中的物体施加了-9.8 米/秒的向下负加速度,就像我们在地球表面经历的一样。然而,你的物体仍然没有像你预期的那样下落。这是因为模拟没有步进。
有两种方法可以逐步完成你的模拟:手动调用 stepSimulation 进行一个时间步长的模拟,或者调用 setRealTimeSimulation 进行实时渲染。出于演示的目的,我们将使用后者,但是一旦我们开始在一个循环中渲染一批对象,你可能会希望使用 stepSimulation 。
pb.setRealTimeSimulation(1)
设置为“1”可启用,设置为“0”可禁用。现在你的物体应该下落并落地。顺便说一下,默认的时间步长是 1/240 秒,但是如果需要,您可以使用 setTimeStep 函数来覆盖这个默认值。
如何渲染你的图像
我们现在有一个方法来加载一个平面,在我们的平面上放置一个 OBJ 文件,对对象应用纹理,并渲染场景。拼图的最后一块是渲染我们的对象的图像。本质上,我们希望在我们的对象上放置一个虚拟相机,将其向下指向我们的对象,并拍摄照片。
无论我们是要解决分类还是分割任务,PyBullet 都通过 getCameraImage 函数使我们的模拟拍照变得相当简单,该函数同时渲染一个 RGB 图像、一个分割蒙版和一个深度缓冲区。然而,我们需要首先指定相机的外部和内部属性,这个过程比看起来要复杂一些。
相机外在属性本质上描述了我们的相机在世界中的位置,比如它的方向和位置。相机固有属性描述相机本身的属性,例如视野(FOV)和其传感器的纵横比(例如 4:3、16:9 等。).在 PyBullet 中,我们分别用视图矩阵和投影矩阵来描述这些属性。PyBullet 快速入门指南链接到一个很棒的资源,可以了解更多关于相机的外在和内在属性及其对应矩阵的信息。
视图矩阵
摄像机视图矩阵是一个复杂的 4x4 矩阵,但用最简单的术语来说,它描述了摄像机的位置和指向。有一些有用的辅助函数可以通过更直接地指定位置和旋转来创建这个矩阵。函数 computeViewMatrix 可以创建这个矩阵来交换三个向量。
viewMatrix = pb.computeViewMatrix(
cameraEyePosition=[0, 0, 3],
cameraTargetPosition=[0, 0, 0],
cameraUpVector=[0, 1, 0])
顾名思义, cameraEyePosition 描述了摄像机在 x 、 y 、 z 坐标中的物理位置。我们将把它放在平面正上方 3 个单位处,以我们的对象为中心。接下来我们描述我们希望相机面对的点 cameraTargetPosition 。我们想让它直视我们的物体,所以我们让它指向原点。最后,我们需要描述摄像机的方向。我们用camera uvector来做这个,它是一个指向我们相机顶部的向量。我们沿着 y 轴指出这一点。我们的相机现在应该指向正下方,这样它渲染的图像平面就与我们生成的平面完全平行了。
投影矩阵
投影矩阵很像视图矩阵,可以使用一些辅助函数来创建。在这种情况下,computeProjectionMatrixFOV函数以最简单、最贴切的方式描述了我们相机的固有属性。
projectionMatrix = pb.computeProjectionMatrixFOV(
fov=45.0,
aspect=1.0,
nearVal=0.1,
farVal=3.1)
fov 参数以度为单位指定摄像机 fov。它描述了相机的视野有多“宽”,原则上类似于真实相机的焦距。纵横比参数描述了摄像机的纵横比,为了简单起见,我们在这里将其设置为 1.0。 nearVal 和 farVal 参数分别描述了摄像机渲染对象的最小和最大距离。由于我们的相机在平面上方 3 个单位,我们将 farVal 设置为 3.1,以包含该平面。这些参数在 OpenGL 文档中有定义,你可以在这里阅读关于它们的更多信息。
getCameraImage
我们已经描述了相机的属性,现在准备渲染一些图像。让我们用 getCameraImage 来做这件事。
该函数返回三个图像缓冲区:rgbImg、depthImg 和 segImg。 rgbImg 是一个 uint8 图像,带有相机视觉效果的红色、绿色、蓝色和 alpha 通道。depthImg 是一种浮点灰度图像,描述单个渲染像素与相机的距离。它可以用于模拟真实世界深度传感器的视野,如 Microsoft Kinect。最后, segImg 是图像的分割掩模,其中每个像素包含具有对象 id 的唯一整数。这些对于训练机器人智能体的分割算法来说是非常宝贵的,例如需要识别物体以分类到各自的箱子中的机械臂,或者需要识别行人、街道标志和道路的无人驾驶汽车。
width, height, rgbImg, depthImg, segImg = pb.getCameraImage(
width=224,
height=224,
viewMatrix=viewMatrix,
projectionMatrix=projectionMatrix)

一旦调用了这个函数,您应该会在查看器的三个窗口中看到您渲染的图像。如果是这样的话,那么恭喜你!您已经渲染了您的第一个对象。您可能希望将这些图像缓冲区转换为 NumPy 矢量,并用图像库(如 PIL 或 imageio)保存它们。
结论
PyBullet 使我们创建虚拟场景以生成训练图像的任务变得相当简单。利用本教程中讨论的概念,您应该能够生成自己的数据集用于分类或分割。额外的增强可能值得探索,例如向相机和渲染对象的位置和方向添加噪声。人们也可能希望用你希望训练的真实世界环境的模型来代替飞机。希望这篇教程是你开发自己的机器学习物理模拟的坚实起点。
模拟故障
一百万次泵故障为什么不再多一次?如何模拟制造领域的故障?

Photo by Elijah O’Donnell on Unsplash
在制造业领域,我最常被问到的一个问题是,这台机器什么时候会出故障。在化学工程领域,机器常常是泵。许多过程和可靠性工程师根据行业图表和多年的泵工作经验来估算。但是很少有人能告诉我,在我们的工作区域内,泵的正常寿命是多少。因为这些泵不会经常出故障。
这就引出了一个问题,“我怎样才能更好地理解这种泵模式?”
在这种情况下,我经常使用的算法是蒙特卡罗模拟。正如您将看到的,这种风格将帮助我们建立一个基线,让我们更好地了解什么时候可能会发生失败。
我们将要谈论的:
- 如何建立蒙特卡洛模拟?
- 如何模拟泵故障(或任何有明确数据的过程)?
- 如何获得工程团队的信任?
让我们开始吧。下面我们设置了一些虚拟数据来输入我们的算法。
import numpy as np# Generate positive portions of the distribution. positive_num = np.random.randint(low=0, high=100, size = 200)#Generate negative portions of the distribution.negative_num = np.random.randint(low=-100, high=0, size = 300)#Joining the positive and negative datasets together.pump_pressure_changes = np.concatenate([negative_num, positive_num], axis = 0)
在上面的代码中,我们使用 numpy 生成了一些随机数据。这个数据集的负值比正值多。您可以将这些点中的每一点视为泵压的正变化和负变化。事实上,在现实世界的问题中,你应该只取泵压读数的第一个差值,就可以得到与你的问题相似的数据。
现在让我们来构建工作马函数。我们将一步步地了解这段代码的每一部分。
####################################################################
#Monte Carlo Simulation
####################################################################
def Base_Monte_Carlo(current_pressure, final_pressure, max_threshold, iterations, data):
hour_distribution = np.empty(shape=(1,0))
for i in np.arange(iterations):
print("Iteration number: {}".format(i))
hours_til_failure = 0
psi_left = current_pressure
not_done = True
while(not_done):
sample_chunk = np.random.choice(data)
print('Sample drawn: {}'.format(sample_chunk))
hours_til_failure += 1
psi_left += sample_chunk
print('psi left {}'.format(psi_left))
# Check if the predefined cutoff has been hit
if psi_left <= final_pressure:
# This is where the Late-Stopping or hope would be added.
hour_distribution = np.append(hour_distribution, hours_til_failure)
not_done = False
if psi_left > max_threshold:
print("print had a spike reseting back to normal")
psi_left = max_threshold
print(psi_left)
return(hour_distribution)
在此功能中,参数的含义如下
current_pressure =你的泵现在所处的压力,或者你想要模拟的压力。
最终压力=泵出现故障或在产品中产生不良结果的压力。例如,在化学领域,这可能意味着你的产品开始燃烧,因为它在管道中移动变慢了。在真正的解决方案中,这是你需要向熟悉你的流程的人询问的事情。
max_threshold =机械有物理可能的极限。例如,如果你将一杯水倒入水槽,排水管很容易跟上,但如果你将消防水管放入同一个水槽,水就会溢出。我们必须尽可能模拟这些物理障碍,这就是上限的原因。
迭代次数=这是我们想要运行模拟的次数。
现在让我们来看看这个函数在做什么。
hour_distribution = np.empty(shape=(1,0))
这一行代码只是给我们一个地方来存储每次模拟达到“失败”状态所花费的时间。在这个例子中,我们假设样本中的每个数据点都是以一个小时为间隔采集的。
for i in np.arange(iterations):
print("Iteration number: {}".format(i))
hours_til_failure = 0
psi_left = current_pressure
not_done = True
这段代码是模拟开始的地方。它会为您想要的每次迭代生成一个模拟。然后,它让我们知道我们在打印语句的模拟中进行了多远。
hours_til_failure 和 psi_left 是我们在每次迭代开始时重置的两个变量。hours_til_failure 实际上记录了我们的泵在每次迭代中运行了多长时间而没有出现故障。psi_left 是一个变量,我们稍后将根据 final_pressure 对其进行验证,以查看我们是否满足故障标准。
while(not_done):
sample_chunk = np.random.choice(data)
print('Sample drawn: {}'.format(sample_chunk))
hours_til_failure += 1
psi_left += sample_chunk
print('psi left {}'.format(psi_left))
这里,当我们的“not_done”变量为真时,这段代码将从我们的数据集中随机抽取一个样本,给泵已经运行的时间加上一个小时,并从泵的当前压力中加上或减去适当的量。
if psi_left <= final_pressure:
hour_distribution = np.append(hour_distribution, hours_til_failure)
not_done = False
if psi_left > max_threshold:
print("print had a spike resetting back to normal")
psi_left = max_threshold
第一个 if 语句检查我们是否低于我们的 final_pressure。如果是这种情况,那么模拟运行的小时数被附加到 hour_distribution,模拟重新开始。
第二个 if 语句检查我们的压力是否超过了允许的最大值。如果发生这种情况,它会将压力调整回可能的范围。
让我们试着用一些虚构的压力和限制来运行 1000 次。
distribution = Base_Monte_Carlo(current_pressure=1000,
final_pressure=200,
max_threshold=1500,
iterations=1000,
data=pump_pressure_changes)
最后,该函数返回我们的泵在所有迭代中持续的小时分布。我们可以使用 matplotlib.pyplot hist 函数对此进行图形化。
from matplotlib import pyplot as plt
# Plotting the histogram.
plt.hist(x = distribution, bins = 100)
plt.xlabel('Survival time in hours')
plt.ylabel('Number of occurences')

在本例中,我们可以看到泵在运行 90 小时左右最频繁地达到其故障状态。然而,我们也看到,假设它曾经运行了将近 500 个小时。
最后一点要注意的是,一定要从你的数据集中去掉任何异常事件,例如,机器的启动,或者像自然灾害这样的外部事件引起的意外停机。
现在,我发现当试图为过程工程师或可靠性工程师运行这些模型时,通常最好从更广泛的变量开始作为模型的输入。例如,如果他们告诉你,“我们知道在每小时 200 磅的压力下,我们的化学物质开始燃烧”。将您的模型设置为 150 失败,这样,如果您在不同的操作条件下遇到挑战,您可以说您对机器的运行时间能力持乐观态度。一旦建立了信任,您就可以将模型磨练成精确的运行参数。
我们在本帖中讨论了以下内容:
- 如何建立蒙特卡洛模拟?
- 如何模拟泵故障(或任何有明确数据的过程)?
- 如何获得工程团队的信任?
有很多有趣的方法,你可以调整这个模型,你可以真实地模拟任何改变状态的过程。另一个使用它的例子是一个进程的能量消耗。同样,这是一个基线模型,有助于理解该过程。
我希望听到你的反馈和建设性的想法。
用张量网络模拟量子电路
奇点研究
用物理学改进机器学习模型
在本文中,我们将讨论用于模拟量子电路的开源软件 Quimb 。Quimb 如此有趣和有用的原因之一是它能够用张量网络进行计算。如果你不熟悉张量网络,我们会在这里谈一点,但你也可以看看我在 Medium " 黑洞机器学习"上的另一篇文章,在那里我讨论了张量网络在物理学中的历史,以及它们在机器学习和人工智能中的用途。如果你仍然好奇并喜欢钻研一点代码,你也可以看看我的 Quimb 教程的 Github repo。
量子电路

Quantum Circuit using IBM’s Quantum Circuit Composer
量子电路是与音乐家使用的五线谱非常相似的图表。你从左到右读它们,就像音乐一样,五线谱上的每一个方框都代表了在被称为门的量子位上执行的一个矩阵操作。这些线代表量子位在时间中运行,通过这些门。在电路的最右侧,我们通常会看到小的测量符号,类似于汽车中的速度计,或者自行车打气筒上显示气压的仪表。它们表示量子位的测量值,并及时返回该量子位的状态向量。由于量子物理的概率性质,这些测量通常要进行多次。每次测量都将返回不同的值,有可能处于两种状态之一。这是量子计算机与经典计算机真正不同的一点。它们天生具有概率性,量子位不像笔记本电脑或手机中的经典二进制位那样简单地以 0 或 1 的形式存在。在上面由 IBM 的量子计算电路合成器制造的量子电路中,我们看到一个哈达玛门,由一个带有“H”的小盒子表示,它对单个量子位 q[0]进行操作。接下来是一个' CNOT' 门,它对前两个量子位 q[0]和 q[1]进行操作。电路中的第三个是三个量子位 q[1]、q[2]和 q[3]上的算子,由一个 8x8 矩阵表示。最后,对量子位 q[1]进行测量。你可以在 IBM 注册一个免费账户,在云中的实际 IBM 量子计算机上创建和运行你自己的量子电路。
张量网络
假设你有一个可以在经典计算机上有效模拟的量子电路(这并不总是可能的,因为在许多情况下经典计算机很难模拟计算)。有效模拟一些量子电路的一种方法是使用张量网络。张量网络是张量的图形表示,可以认为是多维数组。一个秩为 0 的张量仅仅是一个标量。秩为 1 的张量是一个矢量。一个秩为 2 的张量是一个矩阵,以此类推到秩为 3 后变得难以可视化的更高维数组。
谷歌的 TensorNetwork 库

Google’s TensorNetwork Library Logo
谷歌最近发布了一个以其著名的 TensorFlow 作为后端运行的库。由于 TensorFlow 的流行和广泛使用,在 TensorFlow 上建立一个库是一个巨大的优势,但 TensorNetwork 库本身目前并不是最用户友好的,对新手来说可能有点令人生畏。如果您还不熟悉 TensorFlow,并且还没有扎实的编程背景,那么在尝试执行基本张量网络构造和操作之外的任何操作时,对抛出的一些错误进行故障排除可能会令人头疼。据说这个库在 Github 页面上是稳定的,但根据代码作者的说法,它在很大程度上仍处于 alpha 版本。取决于在这个项目上投入了多少努力,这个库可能还需要一段时间才能像 TensorFlow 一样在研究之外被广泛采用。
一个用户友好的选择…Quimb

Quimb Logo
Quimb 是一个开源软件,用于执行量子电路仿真和构建张量网络,在张量网络上执行操作,使用张量网络开发算法,以及为机器学习训练张量网络。它为那些已经熟悉量子力学和量子计算的人提供了出色的文档,对于那些不熟悉的人,我正在创建一个 Github 教程库,解释代码背后的数学和物理知识,并提供许多额外的示例和解释。第一个例子,查看第一个教程的笔记本。Quimb 更出色的地方在于,它使用了 NetworkX ,一个用于创建、操作和研究复杂网络的结构、动态和功能的 Python 包,来创建漂亮的张量网络图可视化:

Tensor Network using NetworkX in Quimb
张量网络和机器学习
那么,除了量子物理学之外,张量网络还有什么用途呢?它们可以在机器学习中用作深度神经网络中的层。这弥合了经典机器学习和量子机器学习新领域之间的差距,量子机器学习使用量子变分电路作为量子神经网络。在许多情况下,使用张量网络模拟这种量子神经网络可以显著提高机器学习算法的性能。因此,即使你不打算很快钻研量子计算,如果你正在使用神经网络和机器学习,你仍然可以通过将它们集成到你当前的机器学习模型中来受益于量子计算技术的使用。
使用 sklearn 在 Python 中模拟(复制)R 回归图
当谈到数据科学和机器学习工作负载时,R 和 Python 是最流行和最强大的语言。Python 通常被视为具有易于理解的语法的通用语言,而 R 则用于统计分析,有大约 12000 个可用的包。
有几十篇文章从主观角度比较了 Python 和 R。在这里,我不会厚此薄彼。
在本帖中,我们将讨论使用 sklearn 在 Python 中对 R 回归图的复制。
大多数 R 的功能可以很容易地直接在 Python 中转换,但是如果不使用自定义函数,有些功能很难找到对等的功能。例如,R 中用于回归模型的 plot () 函数在 Python 中并不具有所有绘图的直接等价物。
让我们用 r 中可用的忠实数据集来讨论一下,该数据集包含两个变量 喷发 (喷发时间分钟)和(等待下一次喷发的时间)的 272 个观测值。该数据集揭示了美国怀俄明州黄石国家公园老忠实间歇泉的喷发间隔时间和喷发持续时间。
现在,让我们将 R 中的简单线性回归模型拟合到该数据集,以根据喷发时间预测等待时间。

对于验证线性假设,上述每一个图都有其自身的重要性。我们不打算在这里深究。让我们关注 Python 代码来拟合相同的线性回归模型。
导入所有必要的库并加载所需的数据。
现在让我们使用 sklearn.linear_model 在 faithful 数据集上拟合一个线性回归模型。
现在让我们使用 sklearn 逐一关注所有的回归图。
- 剩余剧情
这是由 R 中的 plot() 函数生成的第一个图,有时也被称为残差与拟合图。通过绘制拟合值和残差之间的散点图,它有助于验证线性假设。
如果该图描述了任何特定或规则的模式,则假设目标变量和预测值之间的关系在本质上是非线性的,即存在非线性。并且曲线中没有图案是所选特征和目标变量之间线性的标志。
使用 Seaborn 中的 residplot()函数可以获得 Python 中的相同绘图。这里,第一个和第二个参数分别指向拟合(预测)值和目标变量。lowess=True 确保绘制 lowess(平滑)回归线,并使用 line_kws 参数自定义该线的属性。

2。QQ 剧情
该图描述了残差(误差)是否正态分布。如果这些点靠近法线,那么残差被假设为正态分布。
在 Python 中,使用 seaborn 中可用的 probplot()函数可以实现相同的绘图。这里,残差作为参数传递给函数。

3。比例位置图
通常,它用于猜测残差的同方差。它是标准化残差与拟合值的平方根图。如果它没有描述特定的模式,那么拟合的回归模型支持同质性假设。
使用 Seaborn 中的 regplot()函数可以获得 Python 中的相同绘图。这里,第一个和第二个参数分别指向拟合值和平方根标准化残差。

4。杠杆图
通常,它用于猜测异常值对回归拟合的影响。目前,我不知道如何在 Python 中为基于 sklearn 的合身模型绘制相同的内容。一旦我能够弄清楚,我会更新相同的。使用 statsmodels 很容易使用内置的杠杆图绘制,但我不打算在这里讨论它。
如果你已经知道如何为基于 sklearn 的拟合模型绘制相同的图。在评论里告诉我,我会加进去的!
使用几何布朗运动在 Python 中模拟股票价格
对几何布朗运动的动力学和主要驱动因素的高级描述,并附有示例 Python 代码

The picture was taken from @StockMarketMeme
介绍
如果我们能准确预测股票价格在近期或远期的变化,那就太好了。我们会很富有,但几乎不可能做出准确的预测。股票价格的变动涉及太多难以建模的因素。人类心理学就是其中之一。当然,投资者是根据经验证据和股市指标做出决策的。然而,他们还是人类。不同的人会对数据做出不同的解读,这是因为他们的风险偏好和当时的情绪。当一个国家的突发新闻导致一个投资者买入一只股票时,它会导致另一个投资者卖出同样的股票。因此,预测股票价格是一项困难的工作,但我们仍然有有价值的工具,可以帮助我们了解股票价格在某种程度上的运动。
在本文中,我们将讨论如何使用 Python 构建几何布朗运动(GBM)模拟。在构建脚本时,我们还探索了 GBM 模型背后的直觉。我将不会进入它的推导的理论背景。这超出了本文的范围。我更关心对 GBM 需要什么参数、它的组件是什么以及它如何创建预测的高层次理解。我将尝试一种自下而上的方法,从组件开始构建 GBM 的逻辑。我们在这里开发的仿真模型是一个离散时间模型。因此,这里讨论的所有数学都是连续随机过程的几何布朗运动的离散时间类比。在本文的最后,我们将学习如何使用 GBM 创建模拟,您将拥有完整的代码。
内容
- GBM 做什么
- GBM 模拟需要什么输入参数
- GBM 的组成部分:漂移和扩散
- 建立离散时间 GBM 模型
- 做预测
1。GBM 做什么
在解释相关概念时,我在整篇文章中使用 E.ON 的股票价格作为例子。E.ON 是一家位于德国的电力公司,也是欧洲最大的电力公司之一。我通过 Quandl 的 Python 包从 Xetra Exchange 检索它的股票价格(以欧元计)。这里有一个可以显示股票价格的链接:【Investing.com 链接
为了能够使用 Quandl,你需要注册并从其网站获得授权令牌,还需要安装【Quandl】Python 包。假设您已经完成了这些步骤,您可以使用下面的代码来提取股票价格数据。
几何布朗运动被广泛用于金融中的股票价格建模,人们选择它是有原因的。在下面的线图中,x 轴表示 2019 年 1 月 1 日至 2019 年 7 月 31 日之间的天数,y 轴表示以欧元为单位的股价。我希望你只关注图中主要的、持续时间较长的趋势,忽略小的波动。你会意识到股票价格是波动的。它从 1 月到 4 月增加,然后到 5 月中旬减少,接着是另一个增加系列,直到 6 月中旬,最后到 7 月底减少。现在,我要你关注短期波动。我们来调查一下七月。你可以说大势下行,但股价不只是平稳下跌。在它到七月底的旅程中,它不断上升和下降,这表明在这个运动中没有一个可解释的模式。

E.ON stock prices in the 1st half of 2019
因此,我们已经确定了股票价格运动的两个组成部分。七月,
- E.ON 的股价往往会下跌,而
- 每天都有随机的冲击导致股价下跌,形成不规则的线图。
因此,我们需要一个合适的模型来考虑股票价格的两种类型的运动。这就是几何布朗运动发挥作用的地方。GBM 有两个组件来完成这项工作。一个组件包含长期趋势,而另一个组件应用随机冲击。我们将在后面的章节中讨论这些。
2.GBM 模拟需要什么输入参数
以下是我们的 GBM 模拟模型将采用的输入参数。我们将逐一详细讨论它们。为了这篇文章,我将使用 E . ON 7 月份的股票价格来预测 8 月份的情况。另外,你应该注意到,当我用日期解释事情时,我说的是交易日。我们假设一周只由工作日组成,这意味着周五之后是周一。因此,我希望大家在阅读输入参数时记住这些内容。
我们检索从 开始日期 到 结束日期 之间的历史股票价格。然后使用我们的 GBM 模型,我们将得到我们的模拟直到 pred_end_date 。我们可以玩这些变量,创建不同的设置。
Date Close
0 2019-07-01 9.612
1 2019-07-02 9.761
2 2019-07-03 9.856
3 2019-07-04 9.800
4 2019-07-05 9.664Date Close
18 2019-07-25 9.517
19 2019-07-26 9.460
20 2019-07-29 9.488
21 2019-07-30 9.226
22 2019-07-31 9.054
注意,只检索交易日的股票价格,从上面的数据可以看出这一点。
1。所以
这是初始股价。远期股票价格模拟的根源就是这个初始股票价格值。我们将这个初始值乘以一些表达式,得到八月份每个交易日的预测值。在我们这里, 所以 就是 2019 年 7 月 31 日的收盘价。
9.054
2。dt
这是我们模型中的时间增量。它意味着我们假设的时间单位。请记住,我们有每个交易日股票的收盘价,那么我们模型中的时间增量是 1 天。请注意 dt 遵循历史数据中的时间步长。如果股票价格数据每 7.2 小时发布一次,那么 dt 将是 0.3,因为它对应于 7.2 小时(= 0.3 天)。
1
3。T
T 表示我们预测时间范围的长度。在指定 T 和 dt 的值时,我们应该小心并保持一致。例如,在我们的例子中, T 应该是 22 天,因为我们希望预测 8 月份的 22 个交易日,当给 dt 赋值时,根据我们对 T 的声明,我们应该记住 dt 必须用天数来表示。这两个参数的时间单位必须相同。现在,看一个不同的例子,假设我们的数据中每个交易日有两个股票价格值,我们知道我们将对八月份的 22 个交易日进行预测。在这个设置下,我们的时间增量将是 0.5 天,我们将 0.5 分配给 dt 。因此,在应用 GBM 后,我们将有 44 个预测,8 月的每个交易日有两个预测。
还记得上一部分中的例子吗?我们假设股票价格数据每 7.2 小时发布一次。如果我们希望我们的预测时间范围是 72 小时( T = 72),那么合适的 dt 本身就是 7.2 小时,而不需要将其转换为 0.3 天。所以,最后,我们会有 72 / 7.2 = 10 个预测,每个预测之间间隔 7.2 小时。
如果你在构建和使用 GBM 模型时遵循这一思路,那么在不同环境下对不同股票使用你的模型就会变得容易得多。这让许多人感到困惑,这就是为什么我在这里试图将它标准化:)我们试图达到的最终目的是正确计算(在下一部分解释)。它是在我们的预测时间范围内的时间点的数量,在时间增量大小方面应该与我们的历史数据一致。我知道这是一件简单的事情,但建立一条推理路线总是一个好主意,以防止将来我们代码的不同应用中的潜在混淆。
我们可以使用本节开始时声明的变量【pred _ end _ date】来推断八月份的交易日数。使用下面的代码,我们可以通过计算( end_date + 1 天)和 pred_end_date 之间的工作日,提取我们的模型将预测股票价格的交易日数。在我们的案例中,我们需要的是 2019 年 8 月 1 日至 2019 年 8 月 31 日之间的交易天数。
*22*
4。n
该参数在 dt 和 T 赋值后自动出现。它是预测时间范围内的时间点数量。在我们的例子中,我们的时间增量是 1 天,我们将得到 22 个交易日的预测。这意味着,我们有 22 个不同的时间点(天),最终我们将有 22 个预测。
*22.0*
5。t
这是一个数组,我们在其中显示了模型中的时间进程。它就像一个计时器,我们通过计算经过的时间点来测量时间。建筑数组 t 由 N 计算而来。记住我们有 22 个时间点(天)来做预测。这就是为什么数组 t 从 1 开始,一直到 22。当我们使用数组 t 中的一个元素时,它的意思是,模型中过去了那么多时间点。随着我们的预测距离 的日期越来越远,因此 (起始日),随着时间点的推移,它们会因这个数组 t 而波动得更大。在我们的例子中, t 将是我们模型下面的数组。
*[ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22]*
其他一些例子:
一、 如果 dt 是 0.5 天(每天两个股票价格) T 是 22 天,那么 t :
*[1 2 3 4 5 6 7 8 9 10 ... 44]*
二世。 如果 dt 是 7.2 小时而 T 是 72 小时,那么 t :
*[1 2 3 4 5 6 7 8 9 10]*
我们之所以可以使用数组 t 作为一个正整数序列,直到 N 的原因是,我们在时间尺度上以同样的方式对待历史股价数据和我们的预测。我的意思是,历史数据中的 1 天和我们预测的 1 天是一样的。在网上的一些其他文章中,你可能会遇到这样的情况,数组 t 被缩小,上升到 1。在这些情况下,预测时间范围的 1 天变化的意义和我们的输入参数 mu 、 sigma、 和数组必须被缩放以说明时间-尺度差异。在本文中,我们对历史数据和预测使用相同的时间尺度,以使 GBM 模型更具解释性。
6。穆
这是所选历史日期范围内股票价格的平均回报。在我们的例子中,我们选择 7 月的交易日作为历史数据,我们应该使用 7 月的股票价格计算。在计算 mu 之前,我们要计算每个交易日的收益。计算如下。**

Return calculation for time point k
****[0.015501456512692377, 0.00973260936379476, -0.005681818181818097, -0.013877551020408265, -0.0032077814569537026, 0.00384096335513348, -0.013340227507755901, -0.005554973273241884, -0.008326306913996506, -0.0004251248804337693, -0.0005316321105793732, -0.0013829787234042447, 0.0030893789283050936, 0.0036108751062020944, 0.0010582010582012237, 0.0052854122621563355, 0.002944269190326022, -0.0022017194380374075, -0.0059892823368707165, 0.0029598308668074727, -0.0276138279932545, -0.018642965532191694]****

Mean return of the historical stock prices
在计算了七月份所有日子的收益之后,我们需要使用这些收益计算算术平均值,以获得 mu 。然后我们将使用进行我们的 漂移 分量的计算。它会对股价的长期走势产生影响。此外,正如您在下面看到的, mu 为~ -0.3 %,这表明 7 月份的平均回报率为负,我们应该在计算 8 月份的远期预测时考虑到这一点。**
**-0.0026705996693330543**
需要注意的一点是,我们不需要用一个数字乘以平均回报率来进行调整。我们忠于历史数据的时间增量,并以历史数据存在的方式创建预测序列。我们已经在上面讨论了这个问题,但是为了更详细地说明,假设我们每 7.2 小时检索一次股票价格数据,并且我们有过去 72 小时的数据。此外,我们还想预测接下来的 36 个小时。当我们从历史数据中计算平均回报时,该平均回报占 7.2 小时的相关时间步长(两个数据点之间有 7.2 小时)。在进行预测时,遵循我们对历史和未来时间尺度的一致性规则,我们将 7.2 小时分配给 dt。 这导致 5 个时间点(36/7.2),它们之间有 7.2 小时,我们的数组 t 变成了【1 2 3 4 5】。这意味着我们可以在我们的模型中使用 mean return 作为 mu 而无需对其进行调整。**
7。西格玛
这是 7 月份股票价格收益的标准差。西格玛很重要,因为,它将有助于把随机冲击纳入我们的预测。还记得上面 E .关于股票价格的线图吗,股票价格一天比一天持续上涨和下跌。还有,这个小运动的幅度和方向似乎是随机的。在这里,∑将帮助我们确定运动的幅度。 sigma ,本身并没有给模型增加我们需要的随机性。当选取随机值时,我们将使用标准的正态随机变量。西格玛将通过缩放随机冲击的幅度做出贡献,以便根据股票价格的历史波动性出现小波动。我们不希望任何不相关的随机值来自标准正态分布。下面,你可以看到 sigma 是如何计算的。******

The volatility of the historical stock prices
**0.009362443851856311**
记得我们说过,我们不会将 mu 与任何数字相乘进行调整,因为我们在进行预测时会忠于历史数据中的时间步长。同样的逻辑也适用于此,我们也不需要调整。**
8。b**
这个数组是我们在模型中添加随机性的数组。这里重要的是 scen_size 变量。由于这是一个模拟模型,我们希望在预测时间范围内有许多股票价格的假设路径。将 2 赋给 scen_size 意味着,最终我们会有 2 个不同的股价序列。每个场景的逻辑都是一样的。因此,我将讨论我们是否只创建一个预测系列。还记得在 t 的讨论中,我们声明了一个计算经过时间点的时间推进数组。这里,数组 b ,对于每个相应的预测时间点,存储来自标准正态分布的随机数。这些随机数会给模型增加随机冲击,这一点我们从文章开始就一直在讨论。这就是为什么我们将使用 numpy.random.normal() 从标准正态分布中生成随机值。同样,我们不将这个随机值与任何数字相乘进行调整,遵循与**和相同的推理。****

Creating random shocks
在上面的例子中,我们假设我们的历史股票价格数据是每日的,这就是为什么我们将 1 分配给 dt 。同样,我们想要 5 天的预测,这意味着 T 的值是 5。接下来, N 被计算为 5 个时间点(5 个 1 天周期)。因此,对于每个时间点,我们使用 numpy.random.normal() 方法生成一个来自标准正态分布(mean=0,std=1)的随机数。这些随机值最终组成了我们的数组 b 。
在我们的例子中, T 应该是 22 天。下面的代码中,我们为两种不同的场景创建了两个长度为 22 的随机值数组。
**{'1': array([ 1.37139893, -1.60468185, 0.84629302, 0.84599139, 0.1392499 , -0.23658454, 0.66158754, 1.44276449, 0.25582332, -0.00432083, 1.39636253, 0.21584903, 0.15232219, 1.41902316, 0.15672926, -0.76728718, 0.26303217, 1.61346848, 0.16319213, 1.08515534, -1.43681955, -1.89643601]),'2': array([-1.19967538, -0.66905682, -1.31963702, -0.04374916, 1.36622513, 0.39133558, -0.05713623, -1.16944076, 0.51635002, -0.53486539, 1.864583 , -1.2722681 , 0.06091857, -0.86510476, 0.1338046 , 0.76766205, -0.00538846, 0.92632565, -0.13265282, -0.12242566, 0.4031829 , 0.74874924])}**
9。w
【W】是布朗路径,它决定了股票价格从开始时间点( So )到另一个时间点 t )如何波动。你要区分 b 和 W 。在下一节中,它们之间的区别将会更加清楚,但我还是想在这里简单提一下。 b 是在预测下一个时间点的股价时,被施加到一个时间点股价上的随机冲击。所以,假设,在时间点 3,股价是 S_3。当预测时间点 4 时,b【4】作为随机冲击应用于 S3。 W 则相反,是路径。这意味着它包括了自预测时间范围开始以来所有随机冲击的影响。是随机性并入 所以 (初始股价)直到我们关注的特定时间点的总效应。假设我们预测时间点 4。我们需要将所有随机冲击应用到时间点 4 到 ,因此 。所以我们用索引小于等于 4 的数组 b 元素的累积和【W】【4】代替b【4】。下面是上一部分中我们讨论数组 b 的例子的延续。**

Creating the Brownian paths
**{'1': array([ 1.37139893, -0.23328292, 0.6130101 , 1.45900149, 1.59825139, 1.36166685, 2.02325439, 3.46601888, 3.72184221, 3.71752138, 5.11388391, 5.32973294, 5.48205514, 6.9010783 , 7.05780756, 6.29052038, 6.55355255, 8.16702103, 8.33021316, 9.4153685 , 7.97854894, 6.08211294]),'2': array([-1.19967538, -1.8687322 , -3.18836922, -3.23211838, -1.86589325, -1.47455767, -1.5316939 , -2.70113466, -2.18478464, -2.71965004, -0.85506704, -2.12733514, -2.06641657, -2.93152133, -2.79771673, -2.03005468, -2.03544314, -1.10911749, -1.24177031, -1.36419597, -0.96101307, -0.21226383])}**
以上是我们案例的代码。继上一部分的数组 b 计算之后,我们根据上面的 W (k)表达式取累加和,创建数组 W 。
我们对 GBM 模型输入参数的讨论到此结束。我们通过例子理解了它们背后的原因,在接下来的部分中,我们将从它的组件中构建 GBM 模型。这将使本节中的输入参数对您更有意义。
3.GBM 的组成部分:漂移和扩散
记得在第一节,我们已经确定了几何布朗运动的两个组成部分。一个是股票价格的长期趋势,另一个是短期的随机波动。现在,我们将为它们命名。我们称长期趋势为漂移,称短期波动为扩散。在本节中,我希望你假设我们在一个特定的时间点(k-1),我们将预测时间点(k)。然而,我们在上一节中谈到的相同假设仍然有效。提醒一下:
假设 1:(k-1)和(k)之间的时间段长度,也就是 dt,符合历史数据频率。
假设 2: 我们模拟中的时间是通过计算时间段来进行的。
这些假设使我们忽略了将 mu ,【sigma】, z (k)与一个时间长度值相乘进行调整,我们可以组成我们的数组 t 作为一个时间点计数器【1 2 3…】。****
我们继续吧。我们在一个时间点(k-1)。在这个时间点的股票价格需要服从股票价格的长期趋势,同时当我们使用它来创建时间点(k)的预测时,暴露于随机冲击。因此,我们需要将 GBM 的两个组成部分应用到股票价格中。
让我们用数学术语来看看这些组件是什么:
1。漂移

Drift for time point k
漂移 反映股票价格的长期趋势。大家可以回忆一下上一节,我们已经知道了 mu 和 sigma 。它们是从 7 月份的历史股价数据中计算出来的常量值。因此,我们的 漂移 是一个常数值,如果我们假设不存在随机冲击,我们可以将其应用于时间点(k-1)的股价,如上面的表达式所示。如果我们继续应用 漂移 而没有任何随机冲击,那么如果漂移为正,股价就会平稳上升;如果 漂移 漂移为负,股价就会下跌。你应该注意到股票价格永远不会变成 0,因为 漂移 是通过指数函数应用的。这就是 漂移 的故事。现在,让我们用**扩散移动扩散。**
2。扩散

Diffusion for time point k
扩散反映短期波动。从第 2 节可以回忆起,数组 b 存储了我们需要的随机冲击信息,它从标准正态随机变量 z 中检索随机冲击信息。在计算扩散分量时,我们将随机值 z (k)乘以∑。在这里,您可以注意到随机性是如何融入 GBM 模型的。另外, 扩散 组件使得创建不同的股票价格预测场景成为可能。 漂移 分量不变。我们不能从中创建不同的场景,但 扩散 组件帮助我们创建尽可能多的场景,因为它涉及维纳过程(它创建独立、平稳和正态分布的随机冲击)。****
因此,当考虑到时间点(k-1)的股票价格对时间点(k)进行预测时,我们需要将这两个因素的影响相加。总的组合效应为我们提供了对时间点(k)的预测。如果只有 漂移 成分,我们总是知道股票价格在下一个时间点会变成什么,但是 扩散 成分通过引入随机冲击来破坏这种平滑性。因此,我们没有观察到股票价格的单调上升或下降趋势。下面是总组合效应如何应用于时间点(k-1)的股价。

Adding drift and diffusion for 1 time point ahead prediction
4.建立离散时间 GBM 模型
到目前为止,我们学习了为什么我们需要 GBM,离散时间 GBM 模型的参数是什么,以及如何提前 1 个时间点进行预测。现在,我们建立一般的闭合形式的方程的几何布朗运动调整为离散时间的背景。在上一节中,我们展示了如何通过将 S (k-1)乘以 exp( 漂移 + 扩散 ) 来预测 S (k)。作为此的延续,我们将尝试预测 S (k)使用 So (已知的初始股价)。

为此,我们首先将 S (k-1)替换为
S(k-2)exp(漂移(k-1)+扩散 (k-1) )。****
然后我们将S(k-2)替换为****
S(k-3)exp(漂移 (k-2) + 扩散 (k-2) ) 。****
如果我们继续这样做,最后,我们将有 所以 乘以许多指数项。您可以在下面的步骤结束时看到我们获得的结果。

之后,我们扩展上面的等式,这次分别表示和 扩散 项。**

以上是倒数第二个方程式为 S (k)。这意味着,我们可以从 预测 S (k)所以 通过添加 k 个 漂移 和累积直到 k 的组合效应记住从上一节开始, 漂移 不会从一个时间点变化到另一个时间点。这就是为什么,要预测前方 k 个时间点,我们只需多次添加 漂移 k。这与 扩散 不同。在第 2 节讨论 W 时,我们了解到它是布朗路径。你可以回忆一下那部分。基于该讨论,为了预测 k 个时间点,我们应该考虑在路径上遇到的所有随机冲击的总效应。在建立等式的最后一步,我们通过替换下面的等式来修改上面的等式。**

然后,它给出了方程的最终形式,其中的参数我们都很熟悉,我希望我们在第 2 节中进行的所有讨论现在能帮助您理解我们为什么以这种方式创建这些参数。
我们知道所以我们知道 mu 我们知道 sigma 我们知道数组 t 我们知道数组 W 。我们可以一步轻松地为预测时间范围内的所有时间点创建预测。我们将在下一节中完成这项工作。

Geometric Brownian Motion equation
5.做预测
这是我们文章的最后一部分,也是最有趣的部分。我们现在可以创建预测。首先要做的是计算预测时间范围内所有时间点的 漂移 。你已经记住了**阵列t。我们只需将它与 漂移 相乘,就可以得到一个 漂移 的数组。该 漂移 数组包含预测时间范围内所有时间点的总 漂移 。****
关于 扩散 ,我们将有一个 扩散 数组用于每一个场景。记住,我们使用变量 scen_size 来控制我们想要多少场景。现在,对于这个问题,我们只有两种情况。你想涨多少就涨多少。这就是为什么你会看到 2 个不同的阵列,分别标有‘1’和‘2’*。但是,对他们每个人来说,这是同样的逻辑。我们将布朗路径(数组 W )乘以 【西格玛】 。*****
**drift:
[-0.00271443 -0.00542885 -0.00814328 -0.01085771 -0.01357214 -0.01628656 -0.01900099 -0.02171542 -0.02442985 -0.02714427 -0.0298587 -0.03257313 -0.03528756 -0.03800198 -0.04071641 -0.04343084 -0.04614526 -0.04885969 -0.05157412 -0.05428855 -0.05700297 -0.0597174 ]diffusion:
{'1': array([ 0.01283965, -0.0021841 , 0.00573927, 0.01365982, 0.01496354, 0.01274853, 0.01894261, 0.03245041, 0.03484554, 0.03480509, 0.04787845, 0.04989933, 0.05132543, 0.06461096, 0.06607833, 0.05889464, 0.06135727, 0.07646328, 0.07799115, 0.08815086, 0.07469872, 0.05694344]),'2': array([-0.01123189, -0.0174959 , -0.02985093, -0.03026053, -0.01746932, -0.01380546, -0.0143404 , -0.02528922, -0.02045492, -0.02546257, -0.00800552, -0.01991706, -0.01934671, -0.0274462 , -0.02619347, -0.01900627, -0.01905672, -0.01038405, -0.011626 , -0.01277221, -0.00899743, -0.00198731])}**
以下是预测。因为我们有两个场景,所以作为下面操作的结果,我们将有两个股票价格预测序列。第一行代码就是我们在上一节中推导出的最终 GBM 方程。第二行只是将 So 添加到预测序列中,因为它是起点,我们可能希望在我们的图中看到它。
**[[9.054 9.1461394 8.98533403 9.03226024 9.07940588 9.06660652 9.02202324 9.05347139 9.15171815 9.14879651 9.12362735 9.21862966 9.21223827 9.2003776 9.29815157 9.28656346 9.19509744 9.19278238 9.30740419 9.29636703 9.36583938 9.21564323 9.02891936][9.054 8.92860643 8.84880033 8.71645344 8.6892655 8.77726795 8.78560531 8.75710402 8.63826721 8.65659865 8.59000918 8.71758658 8.59101135 8.57261126 8.48040716 8.46802049 8.50598079 8.48249529 8.53318693 8.49949308 8.46674294 8.47572541 8.51221286]]**
作为最后要做的事情,我们现在创建我们的预测的情节。正如你在下面看到的,我们有两个股票价格预测场景。一个上升,另一个下降。我们问题中的 漂移 是负的,你可以从 漂移 数组中观察到。这意味着股票价格的长期趋势是下降的(记住我们只考虑 7 月份的历史数据。我是在这个基础上说的!).然而,尽管有一个负的 漂移 ,我们有一个向上的预测。这是由于随机冲击。记住,我们使用标准的正态随机变量来创建它们。在价格上涨的情况下,很明显,这些随机冲击超过了 漂移 效应,导致了这样一个系列。

Two different scenarios for August stock prices
结论
在本文中,我们学习了如何在离散时间环境中使用几何布朗运动来构建股票价格的模拟模型。下面是完整的代码。当您在注册后从 Quandl 获取授权令牌并安装所需的 Python 包时,您可以立即使用代码。在每次运行中,你会得到不同的股价情景。是因为我们使用 np.random.normal() 没有设置种子。每次我们运行模型,我们将有一个不同的数组 W ,这将导致不同的预测。
几何布朗运动帮助我们看到股票价格可能遵循的路径,并让我们为即将到来的事情做好准备。当然,预测准确的未来是不可能的,但是这些统计方法给了我们创造可靠的交易和对冲策略的机会。
新浪 _ 剧情娱乐与 ggplot
我最近被要求重新创建一个 ggplot 图表。
knitr::include_graphics("images/image002.jpg")

通过查看它,我能够确定一些虚拟数据,让自己开始…
*# dummy data*
small_group_size <- 20
large_group_size <- 2000
set.seed(123)
small_sample_1 <-
tibble(value1 = rnorm(small_group_size, 70, 20),
value2 = rnorm(small_group_size, 150, 20),
group = "Group A")
small_sample_2 <-
tibble(value1 = rnorm(small_group_size, 30, 20),
value2 = rnorm(small_group_size, 180, 20),
group = "Group B")
control <-
tibble(value1 = rnorm(large_group_size, 50, 20),
value2 = rnorm(large_group_size, 220, 20),
group = "Group C")
*# let's put it all together and add a gender column, as well*
data_collected <- bind_rows(
small_sample_1,
small_sample_2,
control) %>%
mutate(person_id_number = row_number()) %>%
mutate_at(c("value1", "value2"), round, 2) %>%
mutate(gender = sample(c("M", "F"), n(), replace = T)) %>%
mutate(group = factor(group, levels = c("Group A", "Group B", "Group C")))
*# and tidy things up (if you want... object nuking, everying but)*
rm(list = setdiff(ls(), "data_collected"))

我发现在 ggforce 包里也有类似的东西,叫做 sina_plot。相似但不完全…来看看开箱的新浪 _ 剧情吧
data_collected %>%
ggplot() +
aes(x = group, y = value1) +
ggforce::geom_sina()

我看到的图表中有一些有趣的特征,是直线 sina_plot 没有给我的。首先,y 轴值被分类,刻度是某种形式的对数。加上 x 轴抖动是一个不规则的。让我们看看我们能否实现这一点…
given_levels <- data_collected %>%
mutate(value_measured = value1,
level_group = (value1 %/% 5) * 5,
level_group = pmax(level_group, 0)) %>%
group_by(group, level_group) %>%
mutate(count_of_entries = n(),
is_group_count_even = count_of_entries %% 2 == 0)

现在,让我们用颜色来看看。
given_levels %>%
ggplot() +
aes(x = group, y = level_group) +
ggforce::geom_sina()

因此,看起来我必须确定数据准备中的 x 轴和 y 轴位置,并为不同的组使用一个面…并使用 geom_point 图表…稍后再担心颜色。
given_levels_and_width_values <- given_levels %>%
mutate(count_of_entries = n(),
width_of_group = count_of_entries %/% 2,
width_position = row_number() - width_of_group - 1) %>%
mutate(width_position = if_else(is_group_count_even & width_position == 0, width_of_group, width_position)) %>% *# this is tricky... Basically, if there's an even number, we want to relocate that x=0 point somehow...by putting it at the end... this is actually going to cause us problems later :-)*
mutate(width_position = case_when(is_group_count_even & width_position > 0 ~ width_position - 0.5,
is_group_count_even & width_position < 0 ~ width_position + 0.5,
TRUE ~ width_position))
让我们现在画出来
given_levels_and_width_values %>%
ggplot() +
aes(width_position, level_group) +
geom_point() +
facet_grid(. ~ group,
scales = "free_x")

太棒了。稍微整理一下…
distance <- 5
unique_grouping_levels <- unique(given_levels_and_width_values$group)
dummy_data <- tibble(column_to_group = rep(unique_grouping_levels, 2),
width_position = c(rep(-distance, length(unique_grouping_levels)),
rep(distance, length(unique_grouping_levels))),
level_group = rep(NA_integer_, length(unique_grouping_levels)*2))
median_lines <- given_levels_and_width_values %>%
group_by(group) %>%
summarise(level_group = median(value_measured, na.rm = T),
to = max(width_of_group, na.rm = T) + distance/2,
from = -to) %>%
gather(key, width_position, -1, -2) %>%
select(-key)
break_steps <- seq(min(given_levels_and_width_values$level_group, na.rm = T), max(given_levels_and_width_values$level_group, na.rm = T), 25)
nearly_finished_plot <- given_levels_and_width_values %>%
ggplot() +
aes(x = width_position, y = level_group) +
geom_point(shape = 1) +
geom_blank(data = dummy_data) +
labs(x = NULL,
y = NULL) +
scale_y_continuous(breaks = break_steps) +
facet_grid(. ~ group,
scales = "free_x",
**switch** = "both") +
theme(axis.text.x = element_blank(),
axis.ticks.x = element_blank(),
panel.background = element_blank(),
axis.line = element_line(colour = "black"),
strip.background = element_rect(fill = "white"),
strip.placement = "outside",
panel.spacing.x = unit(0, "lines")) +
geom_line(data = median_lines, colour = "red", size = 1)
nearly_finished_plot

我们可能还想看到由另一列着色的点…
only_colour_points <- given_levels_and_width_values %>%
filter(!is.na(gender)) %>%
arrange(gender) %>%
mutate(count_of_entries = n(),
width_of_group = count_of_entries %/% 2,
width_position = row_number() - width_of_group - 1) %>%
mutate(width_position = if_else(is_group_count_even & width_position == 0, width_of_group, width_position)) %>% *# this is tricky... Basically, if there's an even number, we want to relocate that x=0 point somehow...by putting it at the end... this is actually going to cause us problems later :-)*
mutate(width_position = case_when(is_group_count_even & width_position > 0 ~ width_position - 0.5,
is_group_count_even & width_position < 0 ~ width_position + 0.5,
TRUE ~ width_position))
nearly_finished_plot +
geom_point(data = only_colour_points, aes(colour = gender)) *# these colour points are actually being overlayed over the original points, and loaded in from the left... this is beneficial because it allows you to see proportions (roughly) of each group (and even NA values, if there are any)*

好吧,这一切都很好。我们已经制定了数据准备步骤和所有的小 ggplot 添加…从这里,让我们把这些步骤变成函数。(1)分配宽度组(箱)(2)分配宽度位置(3)如果适用,通过颜色变量分配宽度位置(4)绘制!
现在,让我们尽可能的动态化…这样 y 轴和 faceting(分组)变量可以在函数调用中指定。
assign_width_groups <- **function**(data, column_to_group, column_to_bin, min_value_to_plot, bin_size) {
column_to_bin <- rlang::enquo(column_to_bin)
column_to_group <- rlang::enquo(column_to_group)
data %>%
mutate(value_measured = !! column_to_bin,
column_to_group = !! column_to_group,
level_group = ((!! column_to_bin) %/% bin_size) * bin_size,
level_group = pmax(level_group, min_value_to_plot)) %>%
group_by(column_to_group, level_group) %>%
mutate(count_of_entries = n(),
is_group_count_even = count_of_entries %% 2 == 0)
}assign_width_positions <- **function**(data) {
data %>%
mutate(count_of_entries = n(),
width_of_group = count_of_entries %/% 2,
width_position = row_number() - width_of_group - 1) %>%
mutate(width_position = if_else(is_group_count_even & width_position == 0, width_of_group, width_position)) %>%
mutate(width_position = case_when(is_group_count_even & width_position > 0 ~ width_position - 0.5,
is_group_count_even & width_position < 0 ~ width_position + 0.5,
TRUE ~ width_position))
}plotting_function <- **function**(data, distance = 5, colour_selection = NULL) {
colour_selection <- rlang::enquo(colour_selection)
unique_grouping_levels <- unique(data$column_to_group)
dummy_data <- tibble(column_to_group = rep(unique_grouping_levels, 2),
width_position = c(rep(-distance, length(unique_grouping_levels)),
rep(distance, length(unique_grouping_levels))),
level_group = rep(NA_integer_, length(unique_grouping_levels)*2))
median_lines <- data %>%
group_by(column_to_group) %>%
summarise(level_group = median(value_measured, na.rm = T),
to = max(width_of_group, na.rm = T) + distance/2,
from = -to) %>%
gather(key, width_position, -1, -2) %>%
select(-key)
break_steps <- seq(min(data$level_group, na.rm = T), max(data$level_group, na.rm = T), 25)
plot <- data %>%
ggplot() +
aes(x = width_position, y = level_group) +
geom_point(shape = 1) +
geom_blank(data = dummy_data) +
labs(x = NULL,
y = NULL) +
scale_y_continuous(breaks = break_steps) +
facet_grid(. ~ column_to_group,
scales = "free_x",
**switch** = "both") +
theme(axis.text.x = element_blank(),
axis.ticks.x = element_blank(),
panel.background = element_blank(),
axis.line = element_line(colour = "black"),
strip.background = element_rect(fill = "white"),
strip.placement = "outside",
panel.spacing.x = unit(0, "lines")) +
geom_line(data = median_lines, colour = "red", size = 1)
*# we also need to add a final step for when a colour component is selected, as well.*
**if** (!is.null(colour_selection)) {
only_colour_points <- data %>% *# this is the data after both steps of the data-prep...*
mutate(colour_column = !! colour_selection) %>%
filter(!is.na(colour_column)) %>%
arrange(colour_column) %>%
assign_width_positions()
plot <- plot +
geom_point(data = only_colour_points, aes(colour = colour_column))
}
plot
}
现在,只需几行代码就可以将它们组合在一起
data_collected %>%
assign_width_groups(gender, value1, 0, 5) %>%
assign_width_positions() %>%
plotting_function(colour_selection = group)

中线也很棘手。首先,它落在彩色点的后面(这很容易修复),其次,它是从实际值而不是入库值计算出来的(这就是为什么它不完全在某个显示点上或之间)。
还有一个小问题,但我认为它不会被注意到,直到我能费心去解决它(这不是对数刻度…我认为没有它看起来更好)。
不管怎样,所有这些功能都准备好了,我们现在可以把它们放入一个漂亮的小 flexdashboard 中…

结论
天啊,这花了很长时间才解决。与 Tableau 相比,在 ggplot / R 中定制的(和可定制的)图表会花费很长时间…但是现在我知道怎么做了..小心世界:-)实现下一次将是一个 sinch。
新加坡统一价格预测器
用机器学习预测新加坡房价

Photo by Peter Nguyen on Unsplash
新加坡连续第五年被经济学人信息部选为最昂贵的国家之一。作为世界上最昂贵的城市之一,住宿占了很大一部分费用。
为了给新加坡普通民众提供负担得起的住房,新加坡政府一直在新加坡提供一些公共住房选择,更普遍的说法是 HDB 公寓。
对于这个附带项目,我已经采用了 HDB 转售价格数据,并训练了一个机器学习模型来预测新加坡的统一转售价格。
根据智能国家倡议,新加坡政府在网上发布了大量新加坡数据,公众可以访问。此外,有一些很好的 API 可供开发人员集成到他们的应用程序中。
概观
这篇文章被分成几个部分。以下是我所涉及的部分的概述:
- 将数据可视化
- 特征创建
- 训练/测试分割
- 实验
- 创建机器学习模型
- 全体
- 韵律学
- 结果
- 结论
事不宜迟,让我们深入了解一下如何自己创建一个统一价格预测器!
将数据可视化
在开始一个机器学习项目之前,我们执行一些标准的可视化程序来理解我们的数据是很重要的。只有了解我们的数据,我们才能知道清理和预处理数据的正确方法。
以下是该数据中可用的列:
'month', 'town', 'flat_type', 'block', 'street_name', 'storey_range', 'floor_area_sqm', 'flat_model', 'lease_commence_date', 'resale_price'
转售价格将是这个项目的目标标签,其余的列将形成我们的特征矩阵。让我们继续画一些图表。

Distribution Plot for Floor Area against Resale Price
我们可以从图表中推断出,建筑面积越高,公寓的转卖价格就越高。通过绘制分布图,我们可以发现是否有任何异常值,如果它们看起来太不寻常,就将其删除。
除此之外,我们还可以使用箱线图来了解我们的数据分布。下面是一个简单的箱线图,它有助于向我们展示给定特定特征的目标标签的分布。

Image from Flowingdata
在我们的项目中,boxplot 可以帮助我们了解不同公寓类型中的公寓价格分布。

另一个有助于我们理解特征的重要可视化工具是关联矩阵:

Correlation matrix of different flat’s features
那么,从这个矩阵中能得到什么呢?建筑面积和转售价格之间的颜色是其他组合中最浅的。这意味着建筑面积和转售价格高度正相关——更高的建筑面积,更高的转售价格。
相比之下,厕所数量和转售价格的相关性非常低,这意味着厕所数量在预测公寓的转售价格方面可能不是很有用。
特征创建

Photo by Kelly Sikkema on Unsplash
该数据集中可用的特征相当小。但是,没有什么能阻止我们创造更多!
拥有更多功能肯定会帮助机器学习模型学习数据集的模式。
一些我认为有用的功能:
- 方向。这套公寓所在的城镇在这个数据集中给出了。我们如何改进这一功能?我创建了一个新特征,将城镇映射到下面列出方向/区域之一:
"NORTH_EAST","EAST""WEST","NORTH","CENTRAL"
2.房间号&卫生间号。根据给定的公寓类型,我从 HDB 网站上查看,找出房间和厕所的数量,并创建了两个新功能。
3.层高。数据中提供的楼层范围要素是字符串格式:4 到 6,7 到 9,我认为这是次优的,因为如果您的要素是序数,您应该将要素转换为数字。
原因在于,楼层 7 低于楼层 9,将特征建模为数字将有助于模型了解这种关系。如果我们将特征保留为字符串而不进行任何处理,模型将会错过这些重要的信息。
所以我创建了一个随机生成器,它将在数据集中给定的最低楼层和最高楼层之间生成一个数字。请看下面的例子,以便更好地理解:
# Example, :
4 To 6 - The generator output: 4 or 5 or 6
7 TO 9 - The generator output: 7 or 8 or 9
4.销售年度&月。数据集中最初给出的月份是这样的格式:2018–4,因此通过将它们分解为“年”和“月”将允许模型选取序数关系。
新创建的功能在提高模型性能方面的效果如何?我将运行多个实验,并与基本模型进行一些比较。我将在结果的后面部分介绍这一点。
训练/测试分割
在将模型投入生产之前,我们需要将数据分为训练集和测试集,以验证模型的准确性。创建我们的训练测试集时需要注意的一些重要事项:
- 训练和测试集的分布应该类似。训练和测试集的一个不好的例子是,训练集中的单元都来自东部地区,而测试集中的单元都来自西部地区。显然,在这种条件下训练的模型不会非常有效。
- 测试集的分布和现实生活中的数据应该是相似的。测试集是一个安全的评估,让您在投入生产之前了解系统的性能。如果你的测试集没有捕捉到真实生活数据的分布,那么你从测试集中得到的结果不会给你多少信心。您的系统很有可能在生产中表现不佳。
为了产生一个好的训练和测试数据集,我们可以使用分层抽样。在我们进行分层抽样之前,我们需要确定一个重要的关键特征,并确保您的训练和测试集分布基于该特征是相似的。
例如,在我们的项目中,我选择位置作为分层抽样的关键特征。就位置而言,列车和测试设备中的单元具有相似的分布是一个好主意。也就是说,如果您的列车组在中部有 50%的平坦度,在西部有 30%的平坦度,在东部有 20%的平坦度,那么您应该在测试组中保持这个比例。
实验

数据科学的很大一部分是关于进行实验的。创建新功能、更改模型、调整超参数等等。所有这些活动的有效性需要通过运行大量的实验来衡量。
在这一部分,我将介绍一些技巧和工具,它们可以帮助你更有效地进行实验。
MLflow
Mlflow 是一个帮助您简化机器学习开发的平台。MLflow 下有多个子项目,例如:
- MLflow Tracking —帮助您跟踪实验的参数、结果和指标,并通过交互式用户界面进行比较
- MLflow 项目s——帮助您通过 Docker 和 Conda 将代码打包成可重复运行的代码
- MLflow 模型 —帮助您打包模型,以便您可以轻松地与他人共享
在这个项目中,我使用 MLflow 来跟踪我的实验,并在训练后记录模型,以便我可以很容易地与他人分享我的结果。下面的代码片段显示了 MLflow 提供的交互式 UI:

MLflow UI
对于您在实验中所做的任何更改或额外处理,留下一些描述是一个很好的做法。这将有助于您稍后分析结果。
使用较小的样本量进行实验
我从一位了不起的教练那里学到了这个建议——杰瑞米·霍华德。他是 fastai 的创始人,该公司的使命是让人工智能教育对任何人免费开放。
这是一门非常棒的关于机器学习的免费在线课程。如果你有兴趣亲自动手,并开始你的机器学习之旅,一定要去看看他们的课程。
那么这实际上意味着什么呢?在前面的部分中,我们已经解释了训练测试分割过程,这样我们就可以为我们的模型获得一些反馈。现在,我们可以更进一步,从我们的训练集中创建一个更小的样本,用于实验。
我们为什么要这么做?当我们设计模型或添加新功能时,我们希望从我们正在进行的所有活动中获得快速反馈。等待 5-10 分钟去做一件事是不会有成效的。因此,拥有更小的样本量,你可以更快地运行你的实验,获得反馈并做出改变。
样本量应该有多小?经验法则——它应该很小,但足够有代表性,可以帮助你在不到 20 秒的时间内完成实验,并获得对结果的粗略估计。
创建我们的机器学习模型
Sklearn 提供了许多方便的库,帮助我们轻松地创建模型。您可以创建多个模型,并在一个实验中运行所有模型,请参考下面的代码:
elasticnet = make_pipeline(RobustScaler(), ElasticNet(alpha =0.00001, random_state=1))svm = make_pipeline(RobustScaler(),SVR(C=140))lasso = make_pipeline(RobustScaler(), Lasso(alpha =0.0005, random_state=1))GBoost = GradientBoostingRegressor(loss='huber',min_samples_split=10,n_estimators=200)experiment_description = [
'Add in sale year and sales month feature',
'Add more data from 2015'
]run_experiment([elasticnet,svm,lasso,GBoost],experiment_description)
代码可能看起来非常简单,但是它需要大量的知识来理解这些模型背后的理论。例如,你需要知道模型背后的数学理论是什么,超参数是什么,何时何地我们应该使用这个模型。
每一个模型都值得用一整篇文章来彻底解释其背后的理论和机制。在网上查找资源,那里有大量有用的资料。
全体

Photo by Debby Hudson on Unsplash
这是机器学习中非常有用的技术,可以提高你的整体模型的准确性。它所做的是创建一堆不同的分类器(例如:random forest、elasticnet、xgboost),并集成这些分类器的结果以改善结果。
我们可以使用的一种简单而常见的集成技术是简单地对每个分类器的预测进行平均。
你可能想知道它有什么帮助?背后的基本原理是,不同的分类器从不同的角度学习数据集的模式。通过收集和平均结果,我们可以看到一些性能提升。
堆垛
我在这里使用的整体技术是堆叠。与平均模型相比,这是一种稍微先进的技术。然而,它在引擎盖下做的事情非常简单!下面是堆叠的简单架构:

Image from Wikimedia commons
我们有多个分类器对数据进行学习,并输出许多不同的预测。我们没有对预测进行平均,而是将其发送给元学习器,元学习器是另一个机器学习模型,可以给我们最终的预测。
元学习者决定不同分类器所做预测的权重。一些分类器可能比其他分类器表现更好,因此元学习器将优化并找到最佳组合。
虽然整个过程听起来令人生畏,但这实际上只需要很少的代码,因为繁重的工作是由我们出色的 sklearn 库完成的!
韵律学
是时候看看结果了!但是在分析结果之前,我们首先需要了解指标是什么。
我们这里使用的指标是均方根对数误差(RMSLE)。我相信你们大多数人都听过和学习过均方根误差(RMSE)。这两个指标的区别在于,在计算均方根之前,我们对目标值和预测值应用对数。参见下面的等式:

Equation of root mean square logarithmic error
为什么我们用 RMSLE 而不是 RMSE?通过做对数,我们只查看实际值和预测值之间的相对差异/百分比差异。

Comparison between RMSE and RMSLE
如果目标值和预测值之间的差异在数值上很大,RMSE 会给予很大的惩罚。但 RMSLE 正在研究百分比差异,并对两种情况给出相同的损失分数。
在我们的项目中,不同公寓的价格在不同的水平上变化,我们希望所有的误差都以百分比为基础来处理,这是使用 RMSLE 的主要原因。
结果

Comparison of results between the effects of feature creation
损失分数越低,目标值和预测值之间的差异越小,因此意味着更好的结果。通过以上比较,我们可以知道,在所有的特征创建中,创建平面位置到区域的映射是最有帮助的。
通过组合我们创建的所有额外功能,总体改进是非常显著的。您可以比较第一列和最后一列的结果,以观察损失分数的改善情况。
我们用于执行所有这些实验的数据大小只是数据的一个非常小的子集。我们可以运行完整的实验(使用所有的训练数据)并比较“最基本的模型”——模型 A 和“具有所有额外特征的模型”——模型 B ,以验证我们的特征创建实际上是有帮助的。请参见下面的结果:

Comparison between most basic models and models with all extra features
在我们使用的所有型号中,型号 B 的表现要好得多。所以功能创建肯定很有帮助!
我想强调一下这两个模型中性能最好的模型,即堆叠分类器。模型 B 的堆积分类器的损失分数比模型 A 低 15%,这是一个惊人的改进。
结论
简而言之,我已经强调了从零开始构建统一价格预测器的整个过程。你可以从这个 GitHub 库下载源代码。
除了机器学习代码,我还创建了一个 web 应用程序来执行实时预测。
web 应用程序代码在同一个存储库中,如果您觉得对您的用例有用,可以参考它们。
使用 GraphX 跨设备为匿名用户匹配单个用户标识

Public domain image, Pxhere
有可能你正在阅读这篇文章,因为你有一个数字营销的用例。 Gartner 称之为跨设备识别(XDID) 数字营销人员需要关注的新兴技术。此外,当前对 cookie 管理的监管变化进一步增加了概率模型和数据科学在单个用户匹配中的重要性。
但是数字营销并不是唯一可以从单一用户 ID 中获益的用例。其他有价值的用例围绕着推荐系统和个性化,并且与行业无关。
识别用户的不同选项有:
账户登录:一种高度确定性的用户识别方式。这种形式的身份识别的质量是以注册和登录用户数量通常较少为代价的。因此,并非所有返回的用户都必须被识别为单个用户。对于拥有多个帐户的用户也是如此。
Cookie 标识:通常是一种高质量的标识形式,但不能跨设备工作。cookie 许可管理和 cookie 寿命的法规变化也带来了越来越多的问题。
设备指纹:一种识别用户身份的概率方法,但在设备指纹的使用方面受到越来越多的监管和技术限制。此方法也不能跨设备工作。
IP 匹配:识别单个用户的较低质量的概率选项。然而,IP 是跨设备可用的。与 3G/4G 网络或公共 Wifi 一样,动态共享 IP 也存在问题。无论如何,IP 是最可行的选择。(尽管根据 GDPR,知识产权是“个人身份信息”。)

Mozilla Lightbeam, public domain image, Wikimedia
在 Zoopla,消费者可以浏览我们的房源列表并发送线索。但是,我们在这个过程中对用户注册和登录只有相对较弱的激励。因此,许多 Zoopla 用户实际上是匿名的,并且通常使用多种设备。为了创建一个强大的个性化路线图,我们必须找到一种方法来创建单个用户 id,它可以跨设备工作,也可以处理匿名流量。在 Zoopla 每天超过 200 万次会话的规模上,这是一个有趣的数据科学问题,也是一个困难的数据工程挑战。
这篇博文展示了我们如何使用点击流数据创建一个概率性的单用户 ID。值得注意的是,用户在我们的过程中保持匿名。我们的目标不是用户的个人身份,而是更好地理解他们与我们产品的交互。
步骤 1:创建候选图
当匿名消费者使用 Zoopla 的酒店门户时,我们会像其他网站一样生成一个会话 ID。我们的目标是找到一个数据点,我们可以使用它将最有可能来自同一个用户的潜在会话 id 链接在一起。我们的目标是链接会话 IDs)跨设备以及(2)随着时间的推移。
我们用于链接会话 id 的数据是来自点击流的 IP 地址和时间戳:

我们使用时间戳等数据为 ML 模型创建附加功能。例如,我们在模型中使用时间戳的方式:
- 在数据准备中,过滤我们在特定时间窗口内观察到的 IP 和会话,以减少我们的数据总量,同时提高我们模型的质量
- 为了创建额外的功能,我们将图形嵌入与机器学习模型相结合。
第一步是创建我们的图形顶点。每个顶点是一个会话,对应的 IP 特征向量总结了我们在每个会话中看到的相关 IP,包括我们在会话中观察到的时间范围:
val vertices =
preparedClickstreamData
.groupBy("session_id", "ip")
.agg(
count("*").alias("ip_count"),
//Additional ts aggregations etc
)
.withColumn(“ip_count_struct”, struct("ip", "ip_count”))
.groupBy("session_id")
.agg(
//Spark SQL 2.4
map_from_entries(“ip_count_struct”).alias("ip_vector"),
//Additional ts aggregations etc
)

the vertices data model
我们还需要共享至少一个 IP 地址的候选会话图的边:
val udfFunc = udf((r: Seq[String]) => r.combinations(2).toSeq)val edges =
preparedClickstreamData
.groupBy("ip")
.agg(
collect_set("session_id").alias("session_id_set"),
//Additional ts aggregations
)
.withColumn("session_edges", explode(udfFunc($"session_id_set")))
.withColumn("src_session_id", col("session_edges")(0))
.withColumn("dst_session_id", col("session_edges")(1))

the edge data model

这已经为我们提供了一个相当有用的用户图表,其中包含了有见地的数据。为了保持简单并保持在 Spark 环境中,我们将使用 GraphX 作为我们的图形分析引擎。
虽然 Spark SQL 和 Dataframes 非常适合我们的数据准备,但 GraphX 使用 Spark RDDs。以下代码为我们的顶点和边属性创建数据模型,并为 GraphX 创建相应的 rdd:
import org.apache.spark.graphx._
import org.apache.spark.rdd.RDDcase class VertexAttributes(...)
case class EdgeAttributes(...)val vertexIdEncoder = (s: String) => java.util.UUID.fromString(s).getMostSignificantBits()
val udfVertexIdEncoder = udf(vertexIdEncoder)val vertexRdd: RDD[(VertexId, NodeAttributes)] = vertices
// handle collisions in production
.withColumn("vertexid", udfVertexIdEncoder(col("session_id")))
.rdd
.map(row => (row.getAs("vertexid"), VertexAttributes(
session_id=row.getAs("session_id"),
...
)))val edgeRdd: RDD[Edge[EdgeAttributes]] = edges
// handle collisions production
.withColumn("srcVertexId", udfVertexIdEncoder(col("src_session_id")))
.withColumn("dstVertexId", udfVertexIdEncoder(col("dst_session_id")))
.rdd
.map(row => Edge(
row.getAs("srcVertexId"), row.getAs("dstVertexId"),
EdgeAttributes(
ip=row.getAs("ip"),
...
)
))val graph = Graph(vertexRdd, edgeRdd)
graph.triplets.take(1)
我们可以遍历该图,并遍历具有相应顶点和边属性的所有图三元组:

这实现了我们的候选图。
步骤 2:过滤候选图
下一步,我们过滤候选图。根据您的使用情形和数据质量,这可能需要:
- 处理 vertexIdEncoder()函数中由长类型顶点 ID 转换引起的顶点 ID 冲突。
- 根据时间戳或会话/ ip 计数属性过滤顶点和边,例如潜在的公共 wifi 热点,以减少误报等。
- 从机器人和爬虫中移除顶点,即通过查看顶点度数。此外,顶点度数是机器学习模型的一个重要特征。使用 GraphX outerJoinVertices()用度数信息更新顶点属性,如下所示:
val vertexDegrees: VertexRDD[Int] = graph.degrees
val degreeGraph = graph.outerJoinVertices(vertexDegrees) {
(id, oldVertexAttr, vertexDegOpt) =>
vertexDegOpt match {
//Return a new vertex attribute case class with additional degree data
case Some(vertexDegOpt) => newVertexAttr(degree=vertexDegOpt, ...)
//No degree means zero edges
case None => newVertexAttr(degree=0, ...)
}
}
步骤 3:计算概率会话相似性

在最后一步中,我们计算候选图中会话顶点之间的相似性度量。
会话相似性的直接度量是相关会话 IP 向量的余弦相似性。假设 id1 和 id2 是两个给定的会话 id,具有以下观察到的 IP:
id1 -> [ip1, ip1, ip1, ip2, ip2]
id2 -> [ip1, ip1, ip2, ip2, ip2]
考虑到
id1 -> v1 = (3, 2)
id2 -> v2 = (2, 3)
cosine_sim(v1, v2) = cos(ϴ) = v1.v2 / (|v1||v2|)
cosine_sim(v1, v2) = 0.92
捕获顶点度数后,我们可以选择折叠共享多个 IP 的会话中的重复边。这些只会创建具有相同余弦相似性的三元组。这移除了冗余的图形三元组,并加速了顶点相似性计算:
val dedupedGraph = graph.groupEdges(
(edgeAttr1, edgeAttr2) => newEdgeAttr(...)
)
为了通过余弦会话相似性过滤我们的候选图,我们使用 GraphX 子图()来过滤边三元组。首先,我们为组合的 IP 向量空间实现余弦相似度:
object CosineSim {
def dotProduct[A](l: Map[A, Long], r: Map[A, Long]): Long = {
val k = (l.keySet ++ r.keySet).toSeq
val v1 = k.map(l.getOrElse(_, 0L))
val v2 = k.map(r.getOrElse(_, 0L))
(v1 zip v2).map { case (x, y) => x * y }.sum
}
def vectorNorm[A](v : Map[A, Long]): Double = Math.sqrt(dotProduct(v, v))
def cosineSim[A](l: Map[A, Long], r: Map[A, Long]): Double = {
val d = dotProduct(l, r)
val n1 = vectorNorm(l)
val n2 = vectorNorm(r)
if (n1 == 0 || n2 == 0)
0.0
else
d.toDouble / n1 / n2
}
}
并且简单地过滤余弦相似度高于期望阈值的图形三元组:
val cosineFilteredGraph = graph.subgraph(
epred = triplet => CosineSim.cosineSim(triplet.srcAttr.ip_vector, triplet.dstAttr.ip_vector) > threshold
)
通过这些简单的步骤,你就有了第一个版本的概率单用户 ID 图。为了访问单个用户连接的会话,我们从图中提取连接的组件:
val singleUserGraph = cosineFilteredGraph.connectedComponents()
val singleUserComponents = singleUserGraph.vertices
我们可以查找单个会话 ID:
// Lookup a single user ID in the graph for a session
val sessionId = “fa199794-0c60-4088-bccd-858c67a954ed”
val vertexId = vertexIdEncoder(sessionId)
val singleUserId: VertexId = singleUserComponents.filter {
case (id, component) => id == vertexId
}.map(_._2).collect().head
// Get all other sessions connected to this single user ID
singleUserComponents.filter {
case (id, component) => component == singleUserId
}.map(_._1).collect
或者收集整个数据集用于进一步处理:
// Collect the data
singleUserComponents.aggregateByKey(Seq[VertexId]())(
(accum:Seq[VertexId], v:VertexId) => accum :+ v,
(v1:Seq[VertexId], v2:Seq[VertexId]) => v1 ++ v2
)
GraphX 是一个非常高效的图形分析框架,适用于单跳类型的问题,比如计算我们的余弦相似度。但是对于更复杂的查询和图形,例如从图形中提取连接的组件,性能会迅速下降。下面的 DAG 是 Spark 程序,用于创建本文中讨论的单个用户 id:

用新数据逐步更新图表也不是一个简单的问题。但这为我们提供了一个批量解决方案来改进我们的推荐系统,并提供了一个丰富的数据模型来为各种批量 ML 模型创建功能。
信用

Zoopla 的数据科学是一个伟大的团队努力,我站在巨人的肩膀上。特别感谢 Zoopla 的高级数据科学家 Behzad Behzadan ,他对我们的用例进行了最初的研究工作,并且是这个项目的主要个人贡献者。

Jan 是公司数据转型方面的成功思想领袖和顾问,拥有将数据科学大规模应用于商业生产的记录。他最近被 dataIQ 评为英国 100 位最具影响力的数据和分析从业者之一。
在领英上连接:https://www.linkedin.com/in/janteichmann/
阅读其他文章:https://medium.com/@jan.teichmann
Python 中的奇异值分解示例

https://www.pexels.com/photo/woman-writing-on-a-whiteboard-3862130/
奇异值分解(SVD)有着广泛的应用。这些包括降维、图像压缩和数据去噪。本质上,SVD 表明一个矩阵可以表示为其他三个矩阵的乘积。用数学术语来说,SVD 可以写成如下形式:

其中 n 为行数(即样本数) p 代表维数。
假设我们有一个矩阵 一个 。

为了确定关联的矩阵,我们必须先求出矩阵的特征向量乘以矩阵的转置。**

因为

回想一下特征向量和特征值的定义:

后者也可以表示为:

因此,回到我们的例子:

我们得到了一个四次多项式。




求解后,我们用其中一个特征值代替λ。


在将矩阵乘以向量 x 之后,我们得到如下:

在解方程时,我们得到:

我们把特征向量代入的列。****

然后,我们对矩阵乘以矩阵的转置重复同样的过程。**

求解后,我们得到了 V 的表达式。

最后, S 是一个对角矩阵,其值是任一个的特征值的平方根



这就是事情变得有趣的地方。我们知道所有三个矩阵的乘积等价于左边的矩阵。

我们可以排除特征,但仍然保持原始矩阵的近似值。假设矩阵是组成图像的列和行或像素的数据集,我们可以在理论上使用新形成的矩阵来训练模型,并达到相当的(如果不是更好的话)(由于维数灾难)精确度。


密码
让我们看看如何在 Python 中应用奇异值分解。首先,导入以下库。
***import numpy as np
from sklearn.datasets import load_digits
from matplotlib import pyplot as plt
from sklearn.decomposition import TruncatedSVD
float_formatter = lambda x: "%.2f" % x
np.set_printoptions(formatter={'float_kind':float_formatter})
from sklearn.ensemble import RandomForestClassifier***
在接下来的教程中,我们将尝试对手写数字进行分类。幸运的是,scikit-learn库提供了一个包装器函数,用于将数据集导入我们的程序。
***X, y = load_digits(return_X_y=True)***
该数据集包含 1797 幅 8×8 的图像。如果你指定了return_X_y=True,这个函数将把像素作为一维数组返回。
***X.shape***

y 包含每个数字的标签。
**y**

让我们来看看第一个数字。正如我们所看到的,它只是一个长度为 64 的数组,包含像素亮度。
**image = X[0]**

如果我们想使用matplotlib查看图像,我们必须首先重塑数组。
**image = image.reshape((8, 8))plt.matshow(image, cmap = 'gray')**

接下来,我们将使用奇异值分解来看看我们是否能够仅使用每行的两个特征来重建图像。函数返回的 s 矩阵必须使用diag方法转换成对角矩阵。默认情况下,diag将创建一个相对于原始矩阵为 n x n 的矩阵。这导致了一个问题,因为矩阵的大小不再遵循矩阵乘法的规则,其中一个矩阵中的列数必须与另一个矩阵中的行数相匹配。因此,我们创建一个新的 m x n 矩阵,并用对角矩阵填充它的第一个 n x n 部分。
**U, s, V = np.linalg.svd(image)S = np.zeros((image.shape[0], image.shape[1]))S[:image.shape[0], :image.shape[0]] = np.diag(s)n_component = 2S = S[:, :n_component]
VT = VT[:n_component, :]A = U.dot(Sigma.dot(VT))print(A)**

**plt.matshow(A, cmap = 'gray')**

我们可以通过取 U 和 S 矩阵的点积得到缩减的特征空间。
**U.dot(S)**

原始与缩减的特征空间
让我们比较随机森林模型在使用原始手写数字训练时和使用从奇异值分解获得的缩减特征空间训练时的准确性。
我们可以通过查看公开得分来衡量模型的准确性。如果你对 OOB 的概念不熟悉,我鼓励你看看兰登森林的这篇文章。
**rf_original = RandomForestClassifier(oob_score=True)rf_original.fit(X, y)rf_original.oob_score_**

接下来,我们用 2 个组件创建并装配一个TruncatedSVD类的实例。值得一提的是,与前面的例子不同,我们使用的是 2/64 特性。
**svd = TruncatedSVD(n_components=2)X_reduced = svd.fit_transform(X)**
精简数据集中的每个图像(即行)包含 2 个特征。
**X_reduced[0]**

看一看图像,很难区分图像由什么数字组成,它很可能是 5 而不是 0。
**image_reduced = svd.inverse_transform(X_reduced[0].reshape(1,-1))image_reduced = image_reduced.reshape((8,8))plt.matshow(image_reduced, cmap = 'gray')**

在精简的数据集上训练随机森林分类器后,我们获得了 36.7%的微弱准确率
**rf_reduced = RandomForestClassifier(oob_score=True)rf_reduced.fit(X_reduced, y)rf_reduced.oob_score_**

我们可以通过取explained_variance_ratio_属性的和得到总方差解释。我们通常希望达到 80%到 90%的目标。
**svd.explained_variance_ratio_.sum()**

让我们再试一次,只是这一次,我们使用 16 个组件。我们查看包含在 16 个特征中的信息量。
**svd = TruncatedSVD(n_components=16)X_reduced = svd.fit_transform(X)svd.explained_variance_ratio_.sum()**

我们获得了与使用原始图像训练的模型相当的准确度,并且我们使用了 16/64=0.25 的数据量。
**rf_reduced = RandomForestClassifier(oob_score=True)rf_reduced.fit(X_reduced, y)rf_reduced.oob_score_**




浙公网安备 33010602011771号