GraphQL 最佳实践
在使用 GraphQL 6 个月之后,我分享了我对创建 graph QL 服务器的良好实践的想法
首先
GraphQL 是一种 API 查询语言,也是一种用现有数据完成这些查询的运行时语言。它为 API 中的数据提供了一个完整的、可理解的描述,并且让客户能够准确地要求他们所需要的,仅此而已。
它是由脸书开发的,作为他们移动应用的内部解决方案,后来向社区开源。
最佳实践
如果你看一下官方的 GraphQL 最佳实践页面,你会注意到它只分享了一些更常见的最佳实践的简要描述,在我看来,这只是一些指南,并没有支持实现的细节。我将尝试分享一些使用 GraphQL 的后端应用程序的具体实现。
这绝对不是一个完整的指南;这只是关于如何避免经常出现的最大陷阱的最佳实践和说明的列表。
将精益数据模型用于简单的查询和变异
当您开始创建模式时,设计数据模型可能会很棘手。有多种方法可以做到这一点,尽管大多数实现都可以正常工作,但是只有当您尝试扩展实现时,问题才会浮出水面。
一般来说,看看已经在使用 GraphQL 的平台总是好的。Github API 是理解输入和输出对象如何建模以及查询和变异如何暴露的好地方。
作为一般的经验法则。让你的突变尽可能小。保持输入精简,并精心命名。
突变addComment
简单、简洁、清晰。它接受一个输入AddCommentInput
并返回一个Comment
。我们正在应用一个非空的修饰符(!)以确保输入有效负载不能为空。这里需要注意的一点是我们发送回创建/更新的对象。这允许客户端更新状态,以便用户知道是否有更新。
相当整洁!
如有任何问题或讨论,请随时联系我。我在推特上有空。
使用嵌套对象减少网络调用
上例中的类型Comment
是一个只有一个字段的简单类型。假设我们想将userId
作为Comment
的一部分发送回去。一种可能的方法是这样的。
虽然这看起来是一种快速简单的方法,但是有一个小问题。对于前端来说,userId
本身并不是很有用,它最终必须调用userId
来获取用户。这确实很不方便,也没有最大限度地发挥 GraphQL 的威力。在 GraphQL 中,嵌套输出类型要好得多。这样,我们可以用一个请求调用所有的东西,还可以用数据加载器执行缓存和批处理。
方法将是在Comment
中发送User
。它看起来会像这样。
然后查询看起来像这样。
启用正确的错误处理
这是几乎每个人在开始时都会忘记的事情之一,然后当应用程序变得庞大时,覆盖错误情况就变得非常麻烦。当涉及到 GraphQl 错误处理时可能会非常棘手,因为响应总是有一个 HTTP 状态200 OK
。如果请求失败,JSON 有效负载响应将包含一个名为errors
的根字段,其中包含失败的详细信息。
如果没有适当的错误处理,响应将如下所示:
这一点帮助都没有——回复并没有告诉你到底哪里出了问题。这就是为什么大多数应用程序失败了,却没有真正给用户提供正确的消息或后备选项。处理错误本身就是一个很大的话题,我为此写了一篇单独的博客,里面有一个完整的运行示例。可以在这里找到。
[## 用 GraphQL、Spring Boot 和科特林处理错误
使用 Spring Boot 和 Kotlin 对 GraphQL 错误和异常建模
medium.com](https://medium.com/better-programming/error-handling-with-graphql-spring-boot-and-kotlin-ed55f9da4221)
简而言之,实现后的输出如下所示:
使用接口和联合进行抽象
接口作为父对象,其他对象可以继承。
接口类型扩展用于表示从某个原始接口扩展而来的接口。例如,这可能用于表示多种类型的常见本地数据,或者由 GraphQL 服务(它本身是另一个 GraphQL 服务的扩展)来表示。
并且联合是一种表示许多对象的对象类型。
GraphQL 联合表示可能是 GraphQL 对象类型列表中的一个对象,但是在这些类型之间不提供有保证的字段。它们与接口的不同之处还在于,对象类型声明它们实现什么接口,但不知道包含它们的联合是什么。
考虑下面的例子。这个概要文件是一个由User
和Company
实现的接口。很少有字段是必填的。可以根据不同的类型和要求添加附加字段。
当您的查询需要返回两种类型时,可以使用类似的联合。您的search
查询可以返回User
或Company
。
关于更详细的解释和实现,你可以阅读这个。
[## 通过 Spring Boot 使用 GraphQL:接口和联合
GraphQL 的接口和联合提供了一种在查询中处理多种字段类型的好方法
medium.com](https://medium.com/better-programming/using-graphql-with-spring-boot-interfaces-and-unions-a76f62d62867)
使用片段重用已定义的类型
片段是 GraphQL 中可重用的东西。你可以假设像编程语言中的函数一样的片段。
使用扩展运算符(...
)消耗片段。这样会减少很多冗余代码。
感谢阅读。我希望它能帮助您实现一个健壮的 GraphQL 服务器。这些方法屡试不爽。如果你有除此之外的建议,请随时回复。如有任何问题或讨论,请随时联系我。我在推特上有空。
如果你想知道 GraphQL 是否适合你,你可能会有兴趣阅读这篇文章。
在后端使用 GraphQL 做了 6 个月的项目后,我衡量了这项技术是否适合…
levelup.gitconnected.com](https://levelup.gitconnected.com/6-months-of-using-graphql-faa0fb68b4af)
参考资料:
带 Python Flask 的 GraphQL
GraphQL、石墨烯、Flask 和 Mysql
照片由 Michael Dziedzic 在 Unsplash 拍摄
在我们开始之前,让我们先谈谈我们的目标。
我们正在尝试开发一个在 Flask 上运行的初学者工具包项目,它具有以下功能:
- GraphQL 支持
- 数据库集成
先决条件
对于本教程,我们需要:
- Python
- Pip
- Virtualenv
- Docker
安装烧瓶
Flask 是一个基于 Werkzeug、Jinja 2 和 good intentions 的 Python 微框架。—烧瓶文件
建立一个准系统 Flask 项目非常简单。然而,由于我们希望内置相当多的功能,下面的步骤将比通常花费稍长的时间。
首先创建一个虚拟环境。
$ virtualenv venv
$ source venv/bin/activate
接下来,我们可以安装所需的软件包。为了不使每个人感到困惑,我将只分别包括每个步骤所需的包。
$ pip install flask flask_script
$ mkdir -p app/__init__.py
$ touch manage.py
MySQL 与 Flask 的集成
我们首先必须安装所需的依赖项
$ pip install flask-sqlalchemy pymysql
我们现在可以开始设置我们的 Flask 应用程序来连接我们的 MySQL 服务。为此,我们必须添加一个config.py
文件,并更新我们的app/__init__.py
文件。
使用一个配置文件,我们能够分离我们的环境变量,以及根据需要存储我们的常量变量。
这样,当我们运行docker-compose up
时,应用程序将开始运行,并与 MySQL 服务连接。
设置 GraphQL
GraphQL 是一种针对 API 和运行时的查询语言,用于使用现有数据完成这些查询。GraphQL 为 API 中的数据提供了一个完整且易于理解的描述,让客户能够准确地了解他们的需求,使 API 更容易随时间发展,并支持强大的开发工具。
$ pip install graphene Flask-GraphQL
让我们分析一下我们刚刚安装的软件包。
烧瓶图
Flask-GraphQL 是一个包装层,为 Flask 应用程序增加了 GraphQL 支持。
我们必须为客户端添加一个/graphql
路由来访问 API。为此,我们可以简单地更新我们的app/__init__.py
文件。
在这种情况下,我们从Flask-GraphQL
模块导入了GraphQLView
,并使用它来实现我们的/graphql
路线。
石墨烯
接下来,我们将使用石墨烯-Python 包。Graphene 是一个开源库,允许开发人员使用 Python 中的 GraphQL 构建简单但可扩展的 API。
如果你已经从我们最新的app/__init__.py
文件中注意到,我们实际上缺少一个app/schema.py
文件来让我们的 Flask 应用程序工作。GraphQL 模式是任何 GraphQL 服务器实现的核心,它描述了连接到它的客户机可用的功能。
在这种情况下,我们将创建一个简单的模式,这样我们的应用程序将能够运行,没有任何错误。
这样,尝试运行您的应用程序。当你访问http://localhost:5000/graph QL时,你应该能看到这个页面。如果你熟悉 GraphQL,这个页面你应该很熟悉。这是一个允许你输入 GraphQL 查询的界面。
结论
我希望本文能让您更好地理解和概述如何使用 GraphQL 构建 Python 应用程序。这也可以通过 Django 框架来实现。
我还在这里添加了一个 Github 知识库供参考。这是 GraphQL 最佳实践的另一个故事,您可能会感兴趣。
在使用 GraphQL 6 个月之后,我分享了我对创建 graph QL 服务器的良好实践的想法
towardsdatascience.com](/graphql-best-practices-3fda586538c4)
图表、渐变和分组依据
图片作者。
我的第一个主要数据科学项目教会了我什么。
当我完成熨斗学校数据科学项目第一阶段的第一个项目时,我对我现在知道如何做的大量事情感到惊讶,这些事情在短短两个月前我甚至不知道存在。退一步说,这个项目的节奏很快。我发现自己不断地在母亲生活的诸多责任中周旋,同时尽可能地挤出时间投入到学习中。从本质上说,这意味着我两岁的孩子经常生我的气……而且我一直没有睡觉。(所以如果有人想给我买十月礼物,我是大号 XXXL 咖啡,谢谢。)
但是,这个项目教会了我很多东西。在我进入代码的本质之前,我需要提到一个事实,这个项目的工作教会了我如何在未来做得更好。我一直纠结的问题是,在学术方面,我天生是个完美主义者。认识我的人听到这个消息有时会感到惊讶,因为我家的标准脏乱程度并不会让你联想到完美。我写这篇文章时,正盯着一间堆满玩具的房间和一张用昨天的工艺用品装饰的桌子。但是当涉及到我正在学习或被评估的任何事情时,我都以很高的标准要求自己。这给我带来了麻烦。
问题?我们有两周的时间作为一个团队来完成一个主要项目。我自己做的,很早就开始了,给自己三周左右的时间完成。但是我做了这个项目的三个不同版本,因为我一直试图转换方向,让它变得更好。我一直告诉自己,我还有时间,所以我会用这些时间去探索其他的道路。我没有保持在正确的轨道上,专注于完成必要的任务,而是不断放弃我的数据,以支持新的东西或看起来比我现有的更好的东西。我导入,我抓取网页,我调用 API,我清理和准备数据——然后我用不同的东西重新开始。最后,尽管我很早就开始了,但我最终还是匆忙完成了这个项目,因为我转换了很多次方向。这给了我,或者说提醒了我一个非常重要的教训,那就是保持在正确的轨道上,不要对自己太苛刻,在开始阶段就把事情做得尽善尽美。我明白了,我已经太老了,不能再熬通宵,在午夜吃麦片,数着离孩子们醒来还有几个小时!
另一个问题是,又是这个完美主义的东西,我不断为我的分析创造越来越多的可视化。我们被要求有 4 个有意义的可视化,在我写这篇文章的时候,我有 27 个。问题?我本可以专注于制作更令人惊奇的更少的可视化效果,而不是大量不同程度的可视化效果。我对这种过度绘图和绘制的唯一解释是,我是在完成一个极其彻底的分析的心态。我一直在想,“现在,如果我把它画成这个,那么我的图表就会按照我想要的方式显示数据。”老实说,我喜欢绘图,每次发现显示数据的新方法时都很兴奋。(简短的题外话:我真的对我制作的一个特定的 seaborn 小提琴情节感到兴奋,直到一个匿名的消息来源告诉我,这个特定的情节看起来像彩虹一样的阴道。所以我不得不再次转换方向,只是因为我不能无视它,可悲的是阴道与我的项目无关。)同样,如果你没有严格的时间限制,这不是问题,但不幸的是,这正是我的时间限制。
现在,我已经有了一些时间来反思我的过程,并获得了一些对自己未来非常重要的见解,我可以让自己为自己迄今为止所学到的知识和取得的成就感到自豪:
- 网络搜集。当我们第一次在课程材料中了解到网络搜集时,我对这个话题并不感兴趣。我不喜欢它的伦理灰色地带;与其他数据采集方法相比,它感觉很不雅。但我想我缺乏最初的兴趣只是因为缺乏必要。一旦我发现自己在做一个需要特定数据的项目,很快我就明白了网络抓取是导入信息的一种快速而有用的方式。能够在网站上生成一份财务报告,然后编写一个快速的 python 函数来获取该报告,并将其导入为熊猫数据框架,这是我直到最近才知道的事情。所以当我意识到它的速度和简单性时,我感觉非常棒。每当我能够像这样成功地执行功能时,我可能会也可能不会从我的桌子上跳起来跳舞。
def number_scraper(year):
"""
Scrapes 100 top-grossing movies from The-Numbers.com.
Adds year input to url and scrapes resulting table.
Parameters:
year (int): user input 4-digit year for movie gross to be scraped.
Returns:
numbers_df (Pandas DataFrame): A dataframe populated with movie gross table values.
"""
# url for the full customized report of top 100 movies for release years in range listed
url = f"[https://www.the-numbers.com/movies/report/All/All/All/All/All/All/All/All/All/None/None/{year}/{year}/None/None/None/None/None/None?show-release-date=On&view-order-by=domestic-box-office&show-release-year=On&view-order-direction=desc&show-production-budget=On&show-domestic-box-office=On&show-international-box-office=On&show-worldwide-box-office=On&show-genre=On&show-production-method=On&show-creative-type=On](https://www.the-numbers.com/movies/report/All/All/All/All/All/All/All/All/All/None/None/{year}/{year}/None/None/None/None/None/None?show-release-date=On&view-order-by=domestic-box-office&show-release-year=On&view-order-direction=desc&show-production-budget=On&show-domestic-box-office=On&show-international-box-office=On&show-worldwide-box-office=On&show-genre=On&show-production-method=On&show-creative-type=On)"
response = requests.get(url)
# creating soup
soup = BeautifulSoup(response.text, 'lxml')
# finding table
table = soup.find('table')
# converting html of table into string
table_string = f"""{table}"""
# reading html string into pandas
table_read = pd.read_html(table_string)
# converting into DataFrame
numbers_df = table_read[0]
return numbers_df
- **进行 API 调用。**我非常喜欢关于 JSON 和 API 调用的课程材料。所以毫不奇怪,我必须将这一点纳入我的项目。能够编写一个函数来获取一部电影的标题并返回你可能想知道的关于这部电影的任何内容,这是多么令人惊奇啊!当我编写一个函数,将电影的标题作为一个参数,检索它的上映日期、导演和作曲家,然后将它添加到我的 DataFrame 中时,我感到无比自豪。我还喜欢能够将我的 API 密匙保存在一个秘密文件夹中,并将其设置为函数中使用的变量。
def get_keys(path):
"""Retrieves API key from files as api_key."""
with open(path) as f:
return json.load(f)
keys = get_keys("/Users/dtunnicliffe/.secret/TMDb_api.json")
api_key = keys['api_key']def get_director(title):
"""
Updates director information for movie in dataframe.
Queries TMDB for a given movie title.
Retrieves TMDB movie_id for title.
Retrieves director information based on movie_id.
Adds director information to a list.
Converts director information from list to string.
Adds new director value as string to movie's row in dataframe.
Parameters:
title (str): user input movie title.
Returns:
Updated cells in Pandas DataFrame.
"""
title_r = title.replace(' ', '+')
url = f"[https://api.themoviedb.org/3/search/movie?api_key={api_key}&query={title_r](https://api.themoviedb.org/3/search/movie?api_key={api_key}&query={title_r)}"
response = requests.get(url)
if len(response.json()['results']) > 0:
movie_id = response.json()['results'][0]['id']
url2 = f"[https://api.themoviedb.org/3/movie/{movie_id}/credits?api_key={api_key](https://api.themoviedb.org/3/movie/{movie_id}/credits?api_key={api_key)}"
response2 = requests.get(url2)
crew = response2.json()['crew']
directors = []
for member in crew:
if member['job'] == 'Director':
directors.append(member['name'])
d = str(directors)
d = d.replace('[', '').replace(']', '').replace("'","")
merged_df.loc[merged_df['title']==title, 'director'] = d
else:
pass
# creating a list of all the super hero movie titles
superhero_titles = [title for title in superhero['title']]
# getting director info for movies in list and updating data accordingly
for title in superhero_titles:
get_director(title)
- **带兵!**是的,我们已经谈到了我对情节和图表的痴迷。但这真的是一个扩展我绘图知识的绝佳机会。对于这个项目,我利用 matplotlib 和 seaborn 来绘制我的图,我真的很喜欢这样做。作为一名视觉学习者,我可以直接看到这些图如何讲述一个故事,以及它们对于一名数据科学家向另一个人或组织传达他们的发现有多重要。一件既令人难以置信的乏味又令人难以置信的有益的事情是在我的一些地块上标注或标记点。例如,我选择标注代表我的项目所关注的子类别的票房最高的电影的点,超级英雄电影和动画儿童音乐电影。将电影片名放在正确的位置上非常耗时,有时甚至令人沮丧。但是看到最终的结果让我觉得一切都值了。
# plotting world gross by budget for animated movies
sns.set_style('darkgrid')
sns.lmplot(x='budget_in_mil', y='world_gross_mil', data=merged_df.loc[merged_df['prod_method']=='Digital Animation'], aspect=3)
plt.title('World Gross by Budget for Animated Movies', fontsize=20)
plt.xlabel('Budget in Millions', fontsize=15)
plt.ylabel('World Gross in Millions', fontsize=15)
plt.xlim(0, None)
plt.axhline(y=1000, ls='--', c='green')
for v, w, x, y, z in zip(merged_df['genre'], merged_df['prod_method'], merged_df['budget_in_mil'], merged_df['world_gross_mil'], merged_df['title']):
if (z=='Frozen'):
plt.text(x=x-15, y=y-15, s=z.upper(), fontsize=12, color='black')
elif z=='Toy Story 3':
plt.text(x=x-23, y=y-10, s=z.upper(), fontsize=12, color='black')
elif z=='Zootopia':
plt.text(x=x+2, y=y, s=z.upper(), fontsize=12, color='black')
elif (z=='Toy Story 4'):
plt.text(x = x+2, y = y, s = z.upper(), fontsize=12, color='black')
elif (w=='Digital Animation') and y>1000:
plt.text(x = x+2, y = y-15, s = z.upper(), fontsize=12, color='black');
#saved in images as fig22
#plt.tight_layout()
#plt.savefig('./images/fig22.png')
# plotting world gross by budget and release month for super hero movies
sns.set_style('darkgrid')
g = sns.relplot(x='budget_in_mil', y='world_gross_mil', data=merged_df.loc[merged_df['creative_type']=='Super Hero'], hue='release_month', hue_order=['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'], s=130, aspect=3, palette='Paired')
plt.title('World Gross by Budget and Release Month for Super Hero Movies', fontsize=20)
plt.xlabel('Budget in Millions', fontsize=18)
plt.ylabel('World Gross in Millions', fontsize=18)
plt.xticks(fontsize=15)
plt.yticks(fontsize=15)
plt.xlim(0, None)
g.fig.set_size_inches(16,10)
for w, x, y, z in zip(merged_df['animated_or_super'], merged_df['budget_in_mil'], merged_df['world_gross_mil'], merged_df['title']):
if (z=='Avengers: Endgame'):
plt.text(x=x-60, y=y-15, s=z.upper(), fontsize=12, color='black')
elif (z=='Avengers: Infinity War'):
plt.text(x=x+2, y=y-15, s=z.upper(), fontsize=12, color='black')
elif (z=='Avengers: Age of Ultron'):
plt.text(x=x-76, y=y-15, s=z.upper(), fontsize=12, color='black')
elif (z=='The Avengers'):
plt.text(x=x+2, y=y-15, s=z.upper(), fontsize=12, color='black')
elif (z=='Spider-Man: Far From Home'):
plt.text(x=x-80, y=y-15, s=z.upper(), fontsize=12, color='black')
elif (z=='Aquaman'):
plt.text(x=x, y=y+10, s=z.upper(), fontsize=12, color='black')
elif (z=='Captain Marvel'):
plt.text(x=x+2, y=y-35, s=z.upper(), fontsize=12, color='black')
elif (w == 'super') & (y>1000):
plt.text(x = x+2, y = y-15, s = z.upper(), fontsize=12, color='black')
plt.axhline(y=1000, ls='--', c='green');
#saved in images as fig25
#plt.tight_layout()
#plt.savefig('./images/fig25.png')
图片作者。
- **色调和调色板。**这是上一点的翻版,但是在 seaborn 中使用色调和调色板既有用又有趣。我选择将我的大部分绘图设置为相同的配色方案,以便它们与我的演示幻灯片相匹配。但是我喜欢在如此多美观的组合中设置不同的渐变和调色板的能力。我也一直在我的 seaborn 图中使用色调参数来区分不同的值,比如电影上映月份或电影类型。
图片作者。
图片作者。
- 数学/统计技术。当然,任何数据科学项目的主要组成部分都是数学。我很高兴着手这个项目,因为我已经掌握了我需要的大多数统计工具的知识。简单的
.describe
方法在获取重要信息方面走了很长的路,比如我的数值列的平均值、中间值和百分点,比如国内、国际和全球票房总额或制作预算。计算四分位差(IQR)和我碰巧需要的任何其他描述性统计数据也很容易。 - Groupby。在这个项目开始之前,我对使用
pandas.groupby
有些不放心。我理解它,但只是抽象地理解。将它与我获得的真实电影数据一起使用后,我现在对如何在未来使用它有了更坚定的把握。在这个项目中,我经常使用它来查找像发行月份或电影类型这样的类别的平均值和中间值。
# getting mean and median world gross amounts by genre
genre_stats = merged_df.groupby('genre')['world_gross_mil'].agg(['median', 'mean'])
genre_stats.sort_values(by='mean', ascending=False)
图片作者。
- **数据清洗。**我把它放在这个列表的底部(尽管从重要性的角度来说它应该在列表的顶部),因为项目的这一部分,对于我开始的项目的所有三个版本来说,是最令人畏惧和最不愉快的。到了我的第三个也是最后一个项目版本,我已经将我所有的清理动作添加到一个单一的函数中,以帮助加速清理并使其更容易。但是,删除重复项,找出用什么替换每个空值(然后实际替换所述空值),修改列名和实际列,以及找出何时真正需要删除数据……就没那么有趣了。更不用说处理字符串格式而不是整数格式的数据,或者处理浮点数格式而不是日期时间格式的年份了…
def clean(df):
"""
Cleans and modifies a given dataframe according to criteria set for this particular project.
Drops column called 'Unnamed:0'.
Converts column names to lowercase.
Renames certain columns to make them shorter/more comprehensive.
Removes dollar signs and commas from dollar amounts.
Converts dollar amounts from strings into integers.
Parameters:
df (Pandas DataFrame): user input dataframe based on previously scraped table values from The-Numbers.com.
Returns:
df (Pandas DataFrame): A dataframe cleaned and adjusted as per criteria listed above.
"""
# drop 'Unnamed' column
df = df.drop(columns='Unnamed: 0')
# make column names lowercase
df.columns = [x.lower() for x in df.columns]
# rename certain columns
df = df.rename(columns = {'released':'release_date', 'released.1':'release_year', 'productionmethod':'prod_method',
'domesticbox office':'dom_gross', 'internationalbox office':'int_gross', 'worldwidebox office':'world_gross',
'creativetype':'creative_type', 'productionbudget': 'budget'})
# removing dollar signs and commas from dollar amounts
# converting dollar amounts from strings into integers
df['dom_gross'] = df['dom_gross'].str.replace(',', '').str.replace('$', '').astype(int)
df['int_gross'] = df['int_gross'].str.replace(',', '').str.replace('$', '').astype(int)
df['world_gross'] = df['world_gross'].str.replace(',', '').str.replace('$', '').astype(int)
df['budget'] = df['budget'].str.replace(',', '').str.replace('$', '').astype(int)
return df
- **日期时间。**我从对日期时间一无所知,到至少知道足够将我的一些列转换成日期时间值,并基于所述值使用特性工程函数(即,列创建)。所以这很酷。关于这个主题,我还有很多要学习的,因此,在多次尝试使用 datetime 按月份顺序显示我的图表后,我不得不手动做一些事情,如按名称设置我的发布月份顺序。但令我高兴的是,我学到了足够多的知识,可以将我的一些数据转换为日期时间值,并根据日期时间值过滤我的数据帧。
# converting release_date column to datetime object
merged_df['release_date'] = pd.to_datetime(merged_df['release_date'])
# generating release_month value based on datetime release_date
merged_df['release_month'] = merged_df['release_date'].dt.strftime('%B')
- **名正言顺。**早在我开始数据科学之路之前,这就是我一直在努力解决的问题。当我在高中的时候,“二年级演讲”是我们十年级的一个标志,我仍然记得站在全班近 500 名学生面前发表关于素食主义的演讲时的焦虑,因为我(不幸地)进入了最后一轮。这稍微容易一点,因为我正在通过 Zoom 向一位教师展示我的非技术性发现,但焦虑仍然非常真实。我知道这是一项我需要提高的技能,这样我才能在作为数据科学家的职业生涯中自如地交流我的发现。(对我的同学 Fen 大声喊出来,感谢她抽出时间来帮助我练习这个,让我对演讲这个想法更加适应!像这样的联系让旅程变得更加美好。)
现在我可以松口气了,我们进入了第二阶段,学习概率、统计分布、中心极限定理、假设检验和方差分析。因此,在这一点上,我只是希望统计学知识就像骑自行车一样,我从本科统计学中学到的一切开始在我的大脑中重新展现。如果您已经做到了这一步,请随时联系我们,提供任何反馈!感谢您的阅读。
对于我的整个一期项目,包括完整的 Jupyter 笔记本和我的幻灯片演示的 pdf,请访问我的项目 1 GitHub Repo 这里 。
用多元宇宙理论解决事件概率
不要射杀建模者——至少在这个世界里
吉列尔莫·费拉在 Unsplash 上拍摄的照片
人类有很多认知能力,但我们大多数人都在努力的一个领域是估计、解释和准备不太可能发生的事件。这个主题支撑了纳西姆·塔勒布的两部主要作品:被随机性愚弄和黑天鹅:极不可能事件的影响。在后一部作品中,塔勒布将黑天鹅事件定义为具有三个特征:事件是一个惊喜(对观察者而言),它有重大影响,以及人们事后错误地试图合理化(强调我的)。
Taleb 关注世界舞台上的黑天鹅事件,如互联网的创建、第一次世界大战、苏联解体等。我相信人类的行为是相似的,而且对他们有害,统计模型预测的可能性较小的事件,只要它们是重要的。我们最近在 2016 年总统选举结果中看到了这一点。《赫芬顿邮报》、《纽约时报》和《纽约时报》发布的模型都认为希拉里·克林顿有 71-98%的胜算。在她竞选失败后,一个普遍的反应是[我们是否可以再次信任民意调查和依赖于他们数据的模型】,我很惊讶地看到,即使是从有统计学头脑的朋友那里。为什么我们的本能反应是仔细检查那个说特朗普有 29%胜算的模型?我们不会质疑一枚硬币出现两次反面(25%的可能性)或掷骰子出现 1(17%的可能性)的公平性;这两个数字都低于特朗普赢得 2016 年大选的赔率。
我假设这种反应是一种事后合理化的形式,是由于缺乏一种心智框架来应对现实世界的事件概率,而没有这种框架会导致非理性的决策。为了说明这一点,让我们从选举建模的世界转移到商业世界中遇到的更常见的情况。
最黑暗的时间线
假设你是一名数据科学家,在一家 B2B 科技公司模拟与潜在客户达成销售的概率。你的公司正在争取一个特别大的客户,Megacorp,所以首席执行官每天检查你的模型,以跟踪完成销售的可能性。
周四 : 98%的成交
难以置信的消息!无论你的队友在做什么,看起来都很有效。
周五 : 99%的成交
甚至更好!首席执行官开始计划在预期的意外之财后,他需要对你的企业做出的改变。雇佣更多员工?收购竞争对手?买超级碗广告?
周一:成交 0%
发生了什么事?!您了解到潜在客户在最后一刻选择了与竞争对手合作。这对每个人的士气都是一个巨大的打击,首席执行官在他认为几乎可以保证的销售后,花在梦想扩张上的时间和精力都浪费了。
对这种惨败的普遍反应是什么?“型号肯定是错的!”首席执行官在事后总结会上宣布,他会打电话给你。“我们应该知道这不像你说的那样可能。在它修好之前,我不想听到你的消息。”
制造问题
有可能 CEO 是对的。你的模型可以预测到你的公司最终没有达成交易的可能性较低。不过,也有可能模型没问题。你的公司有很大的机会达成交易,但是不可思议的事情发生了,你没有成功。
你现在处于一个不舒服的位置。“改进”该模型的唯一方法可能是,以精确模拟未来销售为代价,制作一个估计完成 Megacorp 销售的可能性较低的模型*。这叫做过拟合。为了讨首席执行官的欢心而顺从他的要求,扭曲了对未来销售的预测,从而损害了你的模式和公司。*
回归基础
在我们开始讨论我提出的可以帮助人们更好地理解复杂的现实世界事件概率的心理框架之前,让我们从一个简单的例子开始。想象你有一个公平的六面骰子。你一遍又一遍地滚动骰子,开始记录结果。在多次掷骰子之后,你会观察到你所期望的:大约相同数量的掷骰子产生了每个数字,从 1 到 6。
在几百次掷骰子之后,你会得到一个不太可能的、不幸的(在大多数游戏中)连胜。一个 1,接着是另一个 1,接着是另一个 1!出现这种情况的几率是 1/216 = ~0.5%。这是一个不太可能发生的事件。你惊讶吗?你怀疑死亡的公平性吗?你有没有告诉骰子,在你再次掷骰子之前,它最好先调整好自己?
大多数人,甚至那些没有受过任何统计训练的人,都会说不。发生的事情不太可能,但骰子的表现却如预期的那样。此外,如果我们认真思考,我们可以解释骰子滚动随机性背后的机制。一旦骰子离开你的手,它就服从物理定律,并且是完全可预测的。掷骰子是随机的原因是释放后速度和方向的微小变化会导致不同的结果。没有人有在掷骰子时作弊所需的精细动作技能,这将允许他们让一些数字比其他数字出现得更频繁。
我们可以使用统计总体的概念,统计总体是“对某个问题或实验感兴趣的一组相似的项目或事件”(维基百科)。在这种情况下,感兴趣的项目是骰子点数。由于速度和方向不同,我们得到不同的结果。
当我们掷骰子时,我们所做的是从这个群体中取样。所有可能的投掷(甚至是骰子从你的手中掉落或被扔出敞开的窗户的投掷)的群体包含每种结果的相等份额。如果我们从面积的角度来考虑这个问题,如上图所示,我们可以想象,从 1 到 6,每个结果的人口面积相等。
图片由作者提供
在我们增加一些复杂性之前,我想强调一个观点,即虽然只有六种同样可能的掷骰结果,但速度和方向有无限多种组合,当遵守不变的物理定律时,就会产生这些结果。凭借对掷骰子的深刻理解,我们可以想象做两种不同类型的工作:
- 我们可以利用我们对同等权重的人口结果的理解,在不同的领域,如游戏、赌博和商业中提出最优策略。
- 我们可以尝试使用我们对驱动等概率的机制的理解,即释放后骰子的速度和方向可以完美地预测其结果,以尝试让某些结果更频繁地发生。人类做不到这一点,但也许我们可以创造一个机器人,它可以扔出每次都是 6 的骰子。
增加复杂性
在我们的销售示例中,统计总体是什么?大多数人会回答说,这都是可能的销售客户。我总是发现这个定义有一些令人沮丧的地方:我们不能用它来解释为什么我们的模型有 99%的机会获得我们没有获得的销售,除非承认它一定是模型错误。如果是这样的话,我们的首席执行官——他不是概率和统计方面的专家——觉得他的要求是合理的。
图表由作者提供
解决这一问题的一种方法是将预测按准确度分组(例如 0–10%、10–20%、…90–100%),并共同绘制准确度图表。一个表现良好的模型将会显示这些预测围绕着观察到的总体情况进行。
这仍然不能满足我理解预测的 99%的需要。想象一下,总的来说,我们预测在 90–100%的预测时段中,96%的销售将被关闭,但只有 95%被关闭。人们仍然可以争辩说,通过修正模型,并降低我们预测的 99%,我们仍然可以提高总的准确性。
图片由作者提供
与掷骰子的例子不同,我们不能轻易地从总体中重新取样。我们无法重温历史,以 100 种略有不同的方式向 Megacorp 推销 100 次,100 次中有 99 次都能卖出。但是我们可以用多元宇宙理论想象这样做!在多元宇宙理论中,特别是第三层:多世界解释,概率事件的所有可能结果都发生在某个宇宙中,并且与它们的概率成比例。我想将多元宇宙人口定义为所有可能宇宙中的统计人口(在本例中:所有潜在客户)*。*扩展上面的统计人口示例,我们可以将代表每个潜在客户的每个点变成一个圆圈,代表该潜在客户在所有领域的结果。赢得该客户业务的可能性越高,赢得其业务的宇宙的比例就越大,因此客户圈的面积就越大。请注意,如果我在整个多元宇宙群体中添加阴影,它将类似于我之前分享的掷骰子图。
图表由作者提供
拥有多元宇宙精神框架的好处
敏锐的读者已经注意到,我们不能用这种多元宇宙的概率方法来证明我们的模型是正确的。我们不能从一个领域跳到另一个领域,列出我们赢得 Megacorp 销售的比例,向我们的首席执行官展示我们的模式正在按预期运行。事实上,任何模型都不可能完美地解释这个世界。我们能做的最好的事情就是知道一个模型合理地预测了我们感兴趣的人群中的样本,通常是通过与其他不太准确的模型进行对比。即便如此,我们仍然依赖于假设来这样做,其中最重要的一点是,系统不会经历意料之外的结构变化。如果发生这样的变化,我们所有的预测都将落空。在我们的例子中,一个强有力的竞争者进入或离开市场将代表这种变化。
根据我的经验,许多数据科学家忽视甚至摒弃非经验主义的想法,比如这种用多元宇宙理论来解决概率问题的方法,但他们这样做是在自担风险。使用该理论的第一个好处是,这是一种向那些没有很强背景的人解释概率的简单方法,比如我们例子中的 CEO。用多元宇宙理论解释你的模型输出有可能帮助你的模型消费者更容易理解如何解释它们。它可以单独完成,或者添加到关于过度拟合的简短解释中,以说服我们假设的首席执行官放弃他(潜在的)非理性需求来调整你的模型,直到它降低了 Megacorp 的销售概率。
第二个好处是,多元宇宙理论解释概率的方法为我们提供了一个有用的假设分析结构。还记得我们掷骰子例子中的随机机制吗?我分享了一个公平的骰子以相同的比例出现每个结果的原因,因为小的变化——比我们用精细的运动技能所能控制的更小——会导致不同的结果。我们可以证明,用多一点的力或者不同的方向,会把一个会导致 1 的投掷变成一个会导致 2 的投掷。不幸的是,像经典力学那样一致的系统无法完美地预测业务成果。然而,我们可以试着想象我们可以做些什么来增加我们完成 Megacorp 销售的机会,并利用它来帮助未来的销售。当人们做假设分析时,他们总是这样做:可以想象首席执行官在想“如果我亲自回答了 Megacorp 关于我们服务的问题”或“我想知道如果我们给他们折扣会发生什么,因为他们会是一个令人印象深刻的客户。”我们需要明白,这些行为都不能保证销售,但它们可以增加销售的几率,应用多元宇宙理论,我们可以想象将销售失败的宇宙翻转到销售成功的宇宙。在下图中,我展示了 Normalcorp 客户的情况,该客户最初有 50%的销售机会,因为从 50%开始增加更容易看到。
图表由作者提供
我希望使用多元宇宙理论查看事件概率有助于缩小理解统计课程中常用的简单概率(例如,掷硬币、掷骰子或从一副牌中抽出卡片)和现实世界中复杂概率(例如,完成销售的概率、将用户转化为付费订阅计划的可能性)之间的差距。对于通过多元宇宙思考概率的一些有趣探索,我会推荐社区的补救混沌理论集(以及后续引用它的集),其中一个骰子滚动创建了多个时间线,以及所有的瑞克和莫蒂。自己尝试一下,与他人分享和讨论这个想法——包括我,我会尽最大努力回应这里或推特上的评论,看看它是否有助于理解复杂的现实世界现象,并使决策更加理性。
应对商业和数据科学中的不确定性
如何将数据科学原理应用于更好的商业决策
Javier Allegue Barros 在 Unsplash 上拍摄的照片
数据和数据科学在商业中的作用
在我的职业生涯中,我一直着迷于决策以及如何帮助组织做出更好的决策。这个问题中存在人的因素,这是心理学家、组织行为学家和领导力专家研究的难题之一。另一个难题是使用数据为决策提供信息的实践。
“数据驱动的决策”是我经常听到的一个短语,用来描述一个组织的文化或个人偏好的领导风格。对我来说,说一个组织是“数据驱动的”的缺陷是,数据被认为是真理的来源,可以使决策清晰明了。我不同意这个观点。其实我相信数据是善变的;数据可以很容易地被操纵以适应任何故事,所以更多的数据往往会混淆决策。那么,数据有什么帮助呢?
我相信对数据的深思熟虑的使用是我们接近真相的最好方法。数据可以帮助我们将广阔的可能性世界缩小为可能的真理,并对不同真理的可能性进行细致入微的观察。数据帮助通知 我们的决策;它不能替我们做决定。
为什么我们关心不确定性?
每项业务都是建立在一系列决策之上的:我应该在哪里开店?我应该销售这个产品吗?我应该如何给这个产品定价?我应该何时订购更多库存?我应该雇用这个候选人吗?这些决定是重要的,不仅关系到企业的成功,也关系到企业员工的福祉。做出这些决策的关键是理解决策对我们感兴趣的结果的影响,是更高的利润、更少的产品浪费还是更健康的团队。挑战在于,真相永远不会像预测的那样。以我的经验来看,这通常会导致对预测模型和数据的不信任。
照片由 Riho Kroll 在 Unsplash 拍摄
经过深思熟虑的解释和分析,数据可以帮助我们理解不确定性。仅仅因为一个预测不完全正确,我们不应该抛弃整个过程。当解释和分析得好的时候,我们可以使用数据以微妙的方式来思考未来的结果,这不仅仅是给我们一个预测,而是一个范围或多个预测。我们可以确定什么样的结果是可能的 以及这些结果有多合理 。举个例子,如果我考虑买一辆车,我不会相信这种说法,即这辆车会在 5 年后抛锚。我认为,在最初的 3 年内,有 30%的失败几率,在第 4 年和第 5 年有 20%的失败几率。将这些概率与我正在考虑的另一款车型的概率进行比较,可以为我提供有用且可信的信息,帮助我做出决定。
试图理解一系列可能结果的概念对我们大多数人来说并不陌生。我们中的许多人很自然地这样做,寻找定性和轶事信息来帮助我们做决定。当考虑买车时,我们会在网上搜索评论,并与朋友和家人谈论他们的经历。当涉及到组织环境中的决策制定时,我们需要形式化不确定性的度量。我们可以通过熟练地将定量技术与我们的直觉相结合来做出重要的决定。
揭开不确定性的神秘面纱——后续系列文章
在接下来的一系列博客文章中,我将概述帮助我们更好地理解不确定性的分析技术。技术范围从建模场景的业务分析师工具包到利用统计技术估计不确定性的数据科学家工具包。
许多组织已经有了在不同的场景下测试他们的决策以做出重要决策的经验,尽管有时是零星的。改善决策的下一步是系统地使用统计技术来估计不确定性。做得好的组织利用他们对不确定性的更好理解,熟练地权衡风险,以便做出更明智的决策,并为一系列可能的结果做好准备。它们还使组织的所有级别能够使用数据及其细微差别为自己做出决策,从而支持共同的使命。
作为一个在接触数据科学中更严格的分析技术之前就开始了管理咨询职业生涯的人,我理解抛弃统计技术的本能。我以前分享过这些信念,统计学太死板,无法应用于现实世界,太复杂,无法向决策者解释,当数据有限时,它不起作用。在过去一年的学习和工作中,我发现这些信念是没有根据的,尤其是基于模拟的技术,在今天的计算能力下已经成为可能。在接下来的几个星期里,我希望能让你相信这一点。
从社交媒体分析角度看 QAnon 阴谋论
由德克萨斯州奥斯汀西湖高中的阿迪特·巴鲁阿 & 约什·巴鲁阿撰写。
**卡农是一种草根现象,还是主要由少数有影响力的人推动?**要被认为是一个有影响力的人,用户必须从网络中的其他人那里获得大量关注,这可以在 Twitterverse 中以用户收到的转发、回复和提及的份额来衡量。通过来自 1 万名 QAnon 支持者的推文,我们使用社交网络分析来表明 QAnon 在很大程度上是基于影响者的;相对来说,一小部分用户吸引了大部分注意力,大多数 QAnon 支持者重复影响者所说的话。尽管特朗普总统不使用受欢迎的 QAnon 标签发推文,但@realDonaldTrump 的提及率在 QAnon 网络中名列前五。因此,他可以被描述为一个被动的影响者。在 QAnon 网络中,被不同标签提及的用户中,@realDonaldTrump 遥遥领先排名第一。当我们结合这两种方法来衡量关注度时,特朗普总统仍然是 QAnon 网络中的头号(被动)影响者。
一个现象是由草根还是由有影响力的人驱动有关系吗?草根运动是指大多数参与者的重要性虽小,但几乎同等重要的运动。相比之下,在影响者驱动的运动中,少数参与者非常重要,他们推动了大部分行动,而其他人基本上无足轻重。由于不依赖于少数主要参与者,草根运动通常比基于影响者的运动更有弹性或持久力。相比之下,如果高层影响者因为这样或那样的原因离开,基于影响者的现象可能会失败。当然,运动的性质会随着时间而改变。例如,有人认为#MeToo 最初是由影响者驱动的,但随着时间的推移变得更加草根。相比之下,我们对 BLM 数据的初步分析(未在此展示)表明,这更像是一场草根推动的运动。现在判断 QAnon 是否会最终成为一种草根现象还为时过早。
卡农阴谋论
如果你正在阅读我们的文章,你可能已经知道 QAnon 是什么了。以防你太忙而无法关注这一相对较新的发展,这是维基百科对这一现象的定义:
QAnon 是一个极右翼的阴谋论,声称一个所谓的“深层国家”针对唐纳德·特朗普总统及其支持者的秘密阴谋。….这个理论始于 2017 年 10 月“Q”在匿名 imageboard 4chan 上的一篇帖子,他可能是一个美国人,但很可能成为一群人。q 声称能够接触到涉及特朗普政府及其在美国的对手的机密信息。”
《纽约时报》报道称,QAnon 最近变得更加主流,出现在许多社交网络的显著位置,发布了关于疫情、BLM 和疫苗的帖子。多家媒体和事实核查人员报告称,QAnon 散布了关于这些和其他话题的错误信息。一些共和党国会候选人在推特上支持卡农,特朗普总统也转发了这些推特。
通过社交网络分析了解卡侬
为了使用社会网络分析来检查 QAnon,我们使用以下步骤:
1.收集带有 QAnon 标签的推文。
2.执行情绪分析,丢弃对 QAnon 有负面情绪的推文(因为我们想要分析 QAnon 支持者的样本网络)。
3.从推文中创建 QAnon 支持者的定向关注网络。
4.计算节点(用户)的网络中心性指标。
5.可视化网络并显示突出的节点。
6.删除 10%和 15%的顶级节点,并检查没有它们时网络的表现。
我们在我们的一个 GitHub 存储库中提供了用于创建注意力网络、中心性度量计算以及可视化的 python 脚本:https://GitHub . com/adib 2002/Social-Network-Analysis-Python-Scripts
我们不提供通过 Twitter API 访问 tweets 的脚本。已经有多篇文章在 Medium 上发表了关于这个主题的代码,这里有一篇很优秀的发表在 TDS 上:https://towardsdatascience . com/Twitter-data-collection-tutorial-using-python-3267d 7 CFA 93 e
我们收集了使用一个或多个热门 QAnon 标签的推文:#qanon、#q、#savethechildren、#thegreatawakening、#wwg、#qarmy 等。通过反复使用 Twitter API,我们获得了超过 1 万名使用这些 QAnon 标签的 Twitter 用户的推文。为了确保我们分析来自 QAnon 支持者的推文,我们对每条推文进行了 VADER 情绪分析,并丢弃了对 QAnon 有负面情绪的推文。
接下来,我们通过以下方式创建了 QAnon 支持者的定向网络。如果一个用户@abc 转发、回复或提及另一个用户@xyz,那么这两个用户由一个从@abc 到@xyz 的箭头连接。箭头表示@xyz 正受到@abc 的关注。用户的原始推文被表示为自循环,由带箭头的圆圈示出。这是一些来自我们数据的真实推文。出于隐私原因,发送推文的人和转发、提及或回复推文的人的用户 id 已被替换为虚构的 id。
表 1:推文、转发、提及和回复
从 tweets 中,我们得到了两列,如表 2 所示。
表 2:创建注意力网络的数据
根据表 2 中的数据,我们创建了如图 1 所示的网络。
图 1:注意力网络
虽然有人建议用关注者的数量来衡量影响力,但拥有大量关注者并不能保证你的推文获得大量转发,或者你会在其他人的推文中获得大量提及。事实上,在我们的数据中,关注者数量和提及次数之间的相关性是 0.51,关注者数量和转发次数之间的相关性是 0.52,这当然不是很高。通过使用转发、提及和回复,我们捕捉到了每个节点从其他节点获得的真正关注。
虽然我们已经在https://github . com/adib 2002/Social-Network-Analysis-Python-Scripts分享了我们用于此分析的 Python 脚本,但我们发现,对于可视化部分,像 node XL(Excel 中的模板)和 Gephi 这样的免费网络分析工具比 Python 中的 networkx 提供了更多的灵活性和更好的性能。所以我们最终使用 NodeXL 来实现下面的可视化。如果您想尝试我们的分析,我们建议您使用我们的 Python 代码来生成网络并计算中心性度量,然后使用 NodeXL 或 Gephi 进行可视化。
谁是关系网中的重要人物?
在网络中,并非每个人都生来平等!为了理解一个节点有多重要,我们可以使用多种测量方法,这些方法被称为网络中心性度量。在我们的分析中,我们使用其中的两个,入度和中间性,尽管还有其他的,如出度、接近度和特征向量中心性。由于我们正在分析一个注意力网络,我们的重点是找到那些获得大部分注意力的个体。
节点(我们上下文中的用户)的入度(In-degree)是进入节点的箭头数量,其中箭头可以表示转发、回复或提及。具有高入度的节点意味着它正得到网络中其他节点的大量关注。在图 1 中,@xyz 在四个用户中得到最高的关注。可以区分转发、回复和提及,因为转发可以被认为是三种类型中最重要的。然而,为了简单起见,我们认为它们同等重要。
节点的介数是一个分数,用来衡量它在连接网络最远部分时的重要性。考虑节点介数的另一种方式是,如果没有这个节点,整个网络中的节点相互连接的难度有多大。在我们的注意力网络中,一个具有高介数分数的用户意味着该用户不仅得到了大量的关注,而且这种关注来自于整个网络。
入度和介数衡量的是同一个东西吗?不完全是。高的入度分数可能是由于来自网络的一部分的关注,而中间性考虑了整个网络。因此,这两个指标并不完全相关,我们将在下面的分析中看到这一点。
QA non 网络中的影响者
关于 QAnon 受影响者驱动的第一个线索来自用户的介数分数的分布,如图 2 所示。
图 QAnon 网络中介数分数的分布
图 2 中的图表告诉我们的是,QAnon 网络中的一小部分节点得到了整个网络的大部分关注,而绝大多数节点几乎没有得到任何关注。特朗普总统的中间性得分最高,比第二高的得分大 2 倍以上;尽管我们的数据中没有他自己的推文,但@realDonaldTrump 正被整个网络的 QAnon 支持者用各种各样的标签提到。从图 3 中 QAnon 网络的可视化可以得出相同的结论。
图 3: QAnon 网络(10k 用户),更重要的节点以红色和更大的尺寸显示
在图 3 中,根据介数中心性,节点颜色从红色变为绿色。我们不得不使用介数分数的对数来将节点颜色从红色逐渐变为绿色;介数分数下降如此之快,以至于使用原始分数会显示特朗普总统是网络中唯一的红色节点!实心黑色背景代表用户之间的定向链接或箭头。节点的大小随介数中心性而变化。我们还使用了一个名为节点不透明度的特性,它使一些节点比其他节点更明显。我们选择入度作为标准,入度越高的节点显示得越明显。
虽然图 3 讲述了与图 2 相同的故事,但是如果我们使用入度而不是介数,我们的结论会改变吗?在 QAnon 网络中,入度和介数之间存在 0.69 的相关性。高,但不是超高!让我们看看图 4 中的 in-degree 分数分布。
图 QAnon 网络中的学位分数分布
入度分布与介数分布几乎相同,这也反映在下面图 5 的网络图中;只有少数节点的入度得分高于 0。特朗普总统的学位分数将他排在第五位,这解释了为什么他在图 5 中显示为橙色,而不是红色。
图 5: QAnon 网络突出显示了具有最高学位分数的用户
如果我们去掉最受关注的人,会发生什么?
我们很好奇,如果我们移除 10%或 15%具有最高介数分数的用户,QAnon 网络会发生什么变化。
图 6:删除了 10%和 15%顶部节点的示例 QAnon 网络
当我们将图 6 所示的网络与图 3 所示的完整网络进行比较时,我们会发现一些主要差异。图 6 中的红色节点(吸引注意力的节点)不见了,图 3 中黑色背景代表的箭头也不见了。虽然图 3 中 90%和 85%的用户仍然分别在图 6a 和 6b 的两个网络中,但是如果没有前 10–15%的高介数用户,网络上几乎没有任何活动。基本上,卡侬的注意力网络已经崩溃了!
如果我们使用不同的注意力测量方法呢?
即使我们结合入度和介数来衡量注意力,我们也会得到相同的结果。首先,我们为这些度量创建了标准化分数,其中节点的标准化分数是网络中的入度或介数分数/最高入度或介数分数。接下来,我们尝试了一个注意力测量=标准化的程度内得分+标准化的中间得分。结果是一样的,因为这种综合的注意力测量与入度有 95%的相关性,与中间值有 88%的相关性。特朗普总统仍然是最重要的关注焦点,网络中前 25 名甚至 50 名用户的名单几乎没有变化。
外卖
一小部分人在 QAnon 网络中获得了最大的关注,这表明大多数 QAnon 支持者只是重复这些顶级用户在推特上发布的内容,同时用各种标签吸引特朗普总统的注意。随着时间的推移,这种现象是否会演变成一场更加草根化的运动,还有待观察。我们进行的分析可以在不同的时间点重复进行,以了解 QAnon 网络的演变。
重力和电荷:吸引和排斥
为什么引力总是相互吸引,而电荷却相互排斥
许多读过《时间简史》一书的人可能记得有一章叫做“基本粒子和自然力”,霍金在其中写道,“在量子力学中,相互作用力都被认为是由粒子携带的……”。
在这篇文章中,我将试图解释他这句话大概是什么意思。我将描述相互作用与粒子交换关联背后的推理,粒子交换是量子场论(QFT)的一个普遍特征。例如,粒子间的电磁力在 QFT 被解释为所谓的“虚光子”交换的结果
图 1:该图显示了电子之间通过虚拟光子的相互作用。波浪线代表虚拟光子的传播(源)。
什么是量子场论?
量子场论 (QFT)是结合经典场论(包括电磁学和引力等领域)、狭义相对论、量子力学的框架。QFT 主要用于粒子物理学,但物理学的许多其他分支使用基于它的技术。在 QFT,粒子是它们相应的量子场的激发或量子。
图 2:迈克尔·法拉第(来源)和他的一个演示感应的实验(来源)。
大概第一次认真考虑磁场是在英国科学家 T4 用电和磁做了几个实验的时候。由于法拉第的直觉,即电和磁的定律可以用贯穿空间和时间的场来表述,场的概念从那时起统治了物理学,并取代了基于力的牛顿程序。
图 3:正负电荷周围电场示意图(来源)。
大概我们可以考虑的最简单的场是经典实标量场,由函数 φ ( x , t 描述,其中 x 是空间坐标, t 是时间。该场满足所谓的克莱因-戈登方程 (KG):
方程 1:大质量标量场φ( x ,t)的 Klein-Gordon 方程。
KG 方程解是唯一确定的,只要
等式 2:标量场φ( x ,t)的 Klein-Gordon 等式的初始条件。
初始(或边界)条件是已知的。
图 4:标量场 φ ( x , t )追踪一个时空曲面(源)。
在没有外力的情况下,量子场论中的基本对象是下面的路径积分称为真空到真空跃迁振幅
等式 3:QFT 中的泛函积分或真空到真空的转换振幅。
从 Z 的被积函数的指数中的拉格朗日密度使用
等式 4:使用 V(φ)和δS/δφ=0 的选择,得到 KG 等式。
图 5:展示最速下降法的动画(来源)。
并应用动作原理,在这种情况下变成:
方程式 5:作用原理适用于自由重公斤场的情况。
给出等式 1 中给出的 KG 方程。标量粒子的一个例子是希格斯玻色子。
图 6:模拟粒子碰撞产生希格斯玻色子,希格斯玻色子是 QFT 著名的预言(来源)。
我们注意到 QFT 的真空远非平静,而是充满了量子涨落(见图 5)。能量-时间不确定性原理指出人们无法确定只存在短暂时间的量子态的能量。在数学上,这可以表示为
事实上,真空能量剧烈波动,允许产生成对的(粒子-反粒子)虚粒子。
图 7:真空波动是一个空间区域中能量数量的短暂而强烈的变化。它们允许创建粒子-反粒子虚拟对粒子(来源)。
来源
使用来源语言的 QFT 公式是由美国理论物理学家和诺贝尔奖获得者 T2·朱利安·施温格发明的。
图 8:美国理论物理学家、诺奖得主朱利安·施温格,20 世纪最伟大的物理学家之一,以量子电动力学方面的工作而闻名(来源)。
考虑一个粒子,例如,在碰撞后产生,然后被探测器摧毁或湮灭。人们可以把创造和毁灭描述为源和汇。源出现在乘以理论的量子场的行动中。他们描述了真空是如何被扰乱的。由于我们目前正在研究量子标量场,所以动作中出现了术语J(x)φ(x)。存在辐射源时,真空到真空的转换 Z ( J )振幅由下式给出:
等式 6:在存在源 J(φ(x))的情况下,QFT 中的泛函积分(或真空-真空跃迁振幅)。
图 9:存在源时真空-真空转换振幅 Z(J)的表示(基于来自源的图表)。
顺着莱德、齐、这个环节,让我们设置两个源,一个用于创建粒子(源),一个用于湮灭粒子(汇)。方程式中的积分。3 和情商。因为它们是高斯型的。它们只是简单的高斯积分的复杂版本:
****
图 10:高斯分布图及其下方的区域(来源)。
结果可以写成:
方程式 7:方程式的积分。1.
对于自由标量理论,
等式 8:等式 8 给出的泛函 Z(J)中的指数 W(J)。2.
其中指数中的 D ( x ,y)=D(x-y)函数为:
方程 9:自由传播子写成动量空间传播子的傅立叶变换。
传播子 D ( x,y )等于场中一个扰动从 y 传播到 x 的振幅。将两个源场写成傅立叶变换,并代入等式。8 给出(详见 Zee 或此链接):
等式 10:根据 J(x)的傅立叶变换写出的指数 W(J)。
在 Zee 或这个环节之后,我们可以为我们现在的目的选择一个方便的 J ( x )。在这种情况下,设 J ( x )为和:
等式 11:选择 J(x)作为源和汇的和。
集中在两个时空区域 1 和 2,如图 7 所示。傅里叶变换两者并代入等式。我们将得到四个术语。由于我们对相互作用的研究感兴趣,我们将忽略自我相互作用的术语,如
等式 12:这些项将被忽略,因为它们代表自相互作用。
并保留其中两项,即:
等式 13:等式中的项。10 涉及不同来源之间的相互作用。
现在,如果我们研究 Eq。11 我们看到, W ( J )变大的唯一方式是两件事同时发生:
- 等式中的两个术语。11 强重叠:如果其中一个为零,而另一个很大,则 JJ- 贡献很小。
- 来自 Eq。11、有重叠处的 k 的值必须接近 m (回想一下ε → 0)。等价地,重叠一定发生在 k - m 几乎消失的时候。导致尖峰。具体来说,在 k = m ,这意味着粒子的能量动量得到满足,即所谓的“质量壳层”条件,我们有一个大尖峰,我们的理论中有一个大质量粒子。积分涵盖了动量 k,的所有可能值,而不仅仅是质量壳动量。这些其他的ks就是虚粒子。
让我们再一次遵循 Zee T1 的理论,将我们目前的理论解释如下:区域 1 扰乱了磁场,将这个扰动(比方说,一个质量为 T2 m T3 的粒子)送到时空中的区域 2。为了便于计算,需要源有明确的数学形式。更简单的可能性是选择三个空间维度中的 J s 为狄拉克δ函数:
等式 14:源被选择为狄拉克δ函数。Js 需要一个明确定义的数学形式来执行计算。
图 Dirac delta 函数作为零中心正态分布序列的极限(来源)。
这些源与时间无关,但它们会重叠。代入等式。12 进 W ( J )我们得到(经过一些简单的代数步骤):
方程式 15:使用方程式的结果。并执行一些例行的积分。时间 T 是相互作用的时间。相互作用的能量由 e 给出。
**这里 T 是源相互作用的间隔, E 是相互作用的源之间的相互作用能量。注意能量是负的。这是极其重要的,因为它暗示着源由于耦合到场 φ而相互吸引。用复分析求解积分我们发现
等式 16:在 k 上积分后,源相互作用的相互作用能量。
图 13:耦合费米子场和介子场的汤川相互作用。
我们看到,在距离 1/ m 处,源之间的吸引力以指数速度衰减到零。这是一个基本的结果,因为它意味着力的范围取决于由场φ描述的粒子的质量。
我们刚刚发现了一个深刻的基本结果:力的范围取决于交换粒子的质量。
**还要注意,质量越接近,能量越低。于是德 / 博士0。这种势能是由日本理论物理学家和诺贝尔奖获得者汤川秀树在 1934 年提出的,用来解释原子核中核子之间由于耦合到类似 φ的磁场而产生的吸引力。后者现在被称为π介子或π介子,于 1947 年通过实验发现。
图 14:几个 m 值的汤川电位比较( 来源 )。**
这个结果(以及后面的结果)的重要性怎么强调都不为过。引用 Zee :
“粒子的交换可以产生一种力,这是物理学中最深刻的概念性进展之一。”—阿·齐
为什么库仑力对同性电荷是排斥的?
QFT 的另一个深刻的结果是力是吸引的还是排斥的,与在相应的相互作用中交换的粒子的自旋之间的关系。如果一个场的自旋为 1,它会在旋转组下转换为一个向量。在这种情况下,该字段最简单的可能性是一个四向量(上一节中的标量字段将“获得一个索引”):
等式 17:新的场一个自旋为 1 的调解电磁力。
然而,由于自旋为 1 的粒子只有三个自由度,对应于其静止坐标系中的三个偏振方向
方程 18:静止坐标系中自旋为 1 的大质量粒子的极化矢量。
图 15:大质量自旋 1 光子的三个偏振向量。
必须存在一个条件来限制的独立组件的数量。一个简单的条件是洛伦兹协变是:
等式 19:四向量 A 上的条件将 A 的独立分量的数量从四个限制为三个。
出于超出本文范围的原因(参见此链接、齐或科尔曼了解更多细节),光子将被赋予一个小质量 m、,在计算结束时可以被取为零。
同样,因为静止坐标系中的动量向量
方程 20:静止坐标系中的动量矢量。
将遵守以下条件:
等式 21:由 k 矢量和极化矢量 ε 满足的条件。
根据洛伦兹协变性,这个条件在所有的框架中都必须成立(换句话说,它对移动的大质量光子也必须成立)。
现在,在标量场的情况下,要在对应于J(x)φ(x)的 A 的拉格朗日函数中包含源项,源也必须是一个四维向量**
等式 22:为了在拉格朗日中包括源项,源也必须是四个向量。
图 16:虚质量光子的源(产生虚光子的地方)和汇(吸收虚光子的地方)(基于来自源的图表)。
拉格朗日中的项是:
方程 23:大规模电磁场的拉格朗日中的源项。
在这种情况下,源称为电流(或四电流)。
动量为k且极化为a的粒子被产生(在源处)并被吸收(在汇处)的概率正比于:**
等式 24:动量为 k 且极化为 a 的粒子在源和汇被产生和吸收的概率。
对( a )指数求和(对三个偏振),我们获得从源到接收器的传播的总概率。从标量情况下的等式。对于大质量自旋为 0 的场,我们首先注意到极点的剩余是粒子的一种属性。新的传播子现在将有两个洛伦兹指数 μ 和 ν ,它将变成:
方程 25:大质量光子的传播子。与标量场情况相比,新的传播子获得了两个指数μ和ν。
下一步是将偏振乘积的和作为具有两个指数的对象写入,这两个指数将由(减) G 表示。如 Zee 所述,洛伦兹协方差将限制为以下项的组合(根据 Zee 的讲座):**
等式 26:出现在传播算子中的张量
现在,把两边的动量相乘,我们得到:
而是来自 Eqs。在图 20 和 25 中,左手边是零,这意味着:
转到休息帧,设置 μ = ν =1 我们发现整体符号是-1。人们于是有了:
等式 27:电磁情况下的 W(J)。
如果我们记得电流是守恒的,我们必须:
方程式 28:电磁学中的电流守恒。
如果看情商。26,分子中有以下第一项:
等式 29:电流守恒消除了分子中的一项。结果是 W(J ),其符号取决于电荷的符号。
你可以看到,与汤川势相反,两个符号相等的电荷 J ⁰( x )会相互排斥(因为光子没有质量,所以质量下降了)。
我们刚刚发现了一个深刻的结果:两个符号相等的电荷会互相排斥。
图 17:称为太阳等离子体磁重联的电磁现象的一个例子。这种现象导致了太阳耀斑(来源)。****
大众为什么吸引?
重力由一个巨大的自旋为 2 的粒子调节,5 度的偏振由无迹张量表示:
方程 30:大质量自旋 2 粒子的极化。
遵循赋予我们情商的逻辑。26 对于大质量自旋为 2 的粒子,我们得到以下传播子:
方程式 31:引力场的传播子。
这给出了:
等式 32:重力的 W(J)。
将 T 的指数设置为 00,我们获得能量密度。相应的 W ( T )则为:
等式 33:W(T)的负号表示引力吸引。
请注意,现在总的迹象是负。这意味着质量相互吸引,这与我们在上一节看到的同性电荷相反。
我们在这个分析中看到的确实很吸引人。根据在相互作用中交换的粒子的自旋,人们可以确定力是排斥的还是吸引的。这些结果有许多含义,例如在(来自 Zee 的例子):
- 宇宙中结构的形成:没有等式中的负号。33、宇宙中的结构不会形成!
- 相互吸引的核力点燃了恒星,当它们支配质子间的排斥电力(由虚光子介导)时,它们形成了我们在自然界中看到的各种原子核
- 同性相斥的电荷允许原子的形成。
我的 Github 和个人网站 www.marcotavora.me 有一些其他有趣的材料,既有关于物理的,也有关于数学、数据科学和金融等其他主题的。看看他们!
伟大的数据科学和机器学习播客
教育、职业发展和研究
汤米·洛佩兹在的照片
美国劳工统计局估计,到 2026 年,将新增 1150 万个数据科学工作岗位。这一预测表明,随着行业对这些职位的高需求,数据科学领域将继续快速增长。对于那些刚刚开始涉足该领域或考虑从另一个行业转型的人来说,播客对于发展对工业和研究中的数据科学和机器学习的理解非常有用。在这篇文章中,我将讨论我最喜欢的三个播客,它们讨论了研究和行业中的数据科学领域。这些播客也为那些刚刚起步的人提供了学习资源。
我们开始吧!
超级数据科学
超级数据科学播客由数据科学教练兼企业家基里尔·叶列缅科主持。SuperDataScience 介绍了许多领先的数据科学家和数据分析师,他们为如何在数据科学领域建立成功的职业生涯提供了见解。对于任何想更好地了解这个行业并继续在这个领域进行自我教育的人来说,这个播客是必不可少的。我最喜欢的一个是SDS 391:John Elder 的数据科学营火故事。在这一集里,Kirill 和 John Elder 讨论了微积分、统计和重采样等数学概念。他们还讨论了领域知识的重要性、关于神经网络的想法、关于数据科学未来的想法等等。我也很喜欢 SDS 373: TensorFlow 和面向开发者的 AI 学习。在这里,Kirill 与 Laurence Moroney 坐下来讨论 TensorFlow 以及开发人员如何使用它来推进他们在数据科学领域的职业生涯。我还推荐 SDS 379:漩涡、混乱和伤害:指引你的数据科学职业道路。在这里,Kirill 与 Christopher Bishop 坐在一起,他概述了一个框架,以帮助那些开始从事数据科学的人确定他们在该领域的热情。
莱克斯·弗里德曼播客
在这段播客中,麻省理工学院人工智能研究员 Lex Fridman 谈论了人工智能、科学、技术等话题。具体来说,大部分内容都围绕着深度学习、人工智能机器人、计算机视觉、人工通用智能、计算机科学和神经科学。与 SuperDataScience 播客相反,该播客更侧重于广泛的概念,如智力和意识。对于那些对机器学习方法在人工智能系统中的广泛应用感兴趣的人以及那些对大脑如何工作感兴趣的人来说,这是一个很好的播客。我强烈推荐*# 106——Matt Botvinick:神经科学,以及 DeepMind 的 AI*,在那里弗里德曼与 Matt Botvinick 坐下来讨论大脑的神经元机制,因为它与学习有关。我还推荐*# 101——约沙·巴赫:人工意识和现实的本质*,弗里德曼和巴赫在这里讨论了人类大脑的运作、自主机器人、存在的不连续性以及许多更有趣的哲学思考。最后推荐 #81 — Anca Dragan:人机交互与奖励工程。在本期播客中,Fridman 和 Anca 讨论了世界上作为代理的机器人如何在导航物体和人的同时执行任务。
数据怀疑论者
Kyle Polich 主持的数据怀疑论者播客主要围绕数据科学、统计学、机器学习和人工智能进行采访和讨论。这个播客更侧重于研究,其中许多集涉及对机器学习领域最近论文的讨论。我推荐医疗保健中的可解释人工智能,Polich 与 Jayaraman Thiagarajan 坐下来讨论他的论文校准医疗保健人工智能:走向可靠和可解释的深度预测模型。在本播客中,Polich 和 Thiagarajan 讨论了向医生解释医疗保健中的模型预测的形式化过程。我还喜欢自动驾驶汽车和行人,Polich 和 Arash Kalatian 坐下来讨论如何使用虚拟现实和可解释的深度学习解码行人和自动车辆的交互。最后,我推荐对抗性解释,Polich 与 Walt Woods 坐下来讨论他的论文理解图像分类决策和改进神经网络鲁棒性的对抗性解释。在这里,Polich 和 Woods 谈到了对抗性解释的概念,它涉及到神经网络中间层的热图。
结论
总之,在这篇文章中,我们讨论了三个讨论机器学习和数据科学的伟大播客。首先,我们讨论了超级数据科学(SuperDataScience)公司(T1),该公司专注于为初露头角的数据科学家提供职业建议。接下来我们谈论了的莱克斯·弗里德曼播客,它解决了许多关于智力和意识的有趣问题。最后,我们讨论了数据怀疑论者,它围绕机器学习领域的前沿研究展开了许多讨论。我希望你觉得这篇文章有趣/有用。感谢您的阅读!
用于访问公共数据的优秀 Python 库
使用 Python 访问公共数据
有许多优秀的 API 允许用户访问公共数据。这些数据可用于为金融模型生成信号,分析公众对特定主题的情绪,并发现公众行为的趋势。在这篇文章中,我将简要讨论两个允许你访问公共数据的 python 库。具体来说,我将讨论 Tweepy 和 Pytrends。
使用 Tweepy 获取公共推文
Tweepy 是一个 python 库,允许您通过 Twitter API 访问公共 tweets。创建 twitter 开发人员帐户和应用程序后,您可以使用 Tweepy 来获取包含您指定的关键字的 tweets。例如,如果你有兴趣了解公众对当前选举状况的看法,你可以调出最近所有关键词为“选举”的推文。Tweepy 对象返回 tweet 文本,可用于创建情感评分。除了政治之外,这还可以用于各种垂直行业,包括医疗保健、金融、零售、娱乐等。我之前写的一篇文章, 患者对药品的情绪来自推特 ,用推特分析了大众对热门药品的公众情绪。在另一篇文章Python中关于小丑(2019 电影)的推文分析中,我分析了 2019 电影*小丑的公众情绪。*我鼓励你去看看这些教程,自己进行一些数据整理和分析。申请 Twitter 开发者账户和创建 Twitter 应用程序的步骤在这里列出。tweepy 的文档可以在这里找到。
使用 Pytrends 提取趋势主题数据
Pytrends 是一个 python 库,允许您访问表示某个关键字或主题在 Google 上被搜索的次数的数据。与 Tweepy 类似,您可以提供关键字和位置信息,Pytrends 对象将返回一个表示规范化 Google 搜索值的时间序列索引。这在零售和金融领域也有应用,但查看不同地区的关键词趋势也很有趣。关于 Pytrends 的友好介绍,请查看 Python 中的 冠状病毒 Google Trends和 Python 中的 使用 Google Trends API 选择万圣节服装 。Pytrends 的文档可以在这里找到。
结论
总之,在这篇文章中,我们讨论了两个可以用来提取公共数据的 python 库。Tweepy 库非常适合从 Twitter 中提取推文,进行情感分析和趋势分析。Pytrends 库非常适合分析全球特定时间段和地区的 Google 趋势主题。我希望你觉得这篇文章很有趣。感谢您的阅读!
学习机器学习和深度学习的优质免费课程
Bermix 工作室在 Unsplash 拍摄的照片
顶级大学高质量免费课程的链接
这是一个时髦的话题。机器学习是当今发展最快的领域。就业市场正在飞速发展。越来越多的大学开设新课程。这么多免费资源乱飞。越来越多的人对学习机器学习感兴趣。
它同时是好的和坏的。对于新的学习者来说几乎是势不可挡的。很难理解从哪里开始,学什么,如何获得所需的技能。
我想先警告一件事。不要把钱浪费在任何训练营上,那些训练营告诉你他们会在六个月内教会你编程、统计学、数据分析、机器学习、数据库查询。我甚至知道他们承诺学生在 12 周内完成或准备好的新兵训练营。千万不要在他们身上浪费钱。因为如果你不是超人或女超人,这是不可能的。这里有一篇关于它的详细文章:
花钱前再想一想
towardsdatascience.com](/want-to-become-a-data-scientist-in-12-weeks-3926d8eacee2)
如果你有工程、数学、物理、统计或任何其他技术领域的本科学位,你就有优势。但是现在有很多图书馆。你不需要理解背后所有的数学。你仍然可以使用那些算法,执行机器学习和深度学习任务。
我将分享这两种课程的资源。有些会教你流行的库,有些课程是从头开始学习算法。
有许多数据科学家或工程师作为首席数据科学家工作,但不知道如何从头开发机器学习算法。他们使用图书馆。因此,学习库也是一项很好的时间投资。
另一方面,即使你没有数学或工程背景,如果你真的有兴趣从头开始学习开发机器学习,你仍然可以补充一些数学和统计课程。
机器学习和深度学习是数据科学非常有趣的一个方面。
因为数据科学家需要学习机器学习。还有机器学习工程师。其实这些角色都很混乱。在我的理解中,当数据科学家从事机器学习时,他们会与其他人交流,并帮助他们根据自己的工作做出决定。
但是机器学习工程师做机器学习项目,他们的目标是与机器交流。他们需要了解数据库查询、Rest APIs,并构建一个其他人可以使用的接口。
如果你打算成为一名数据科学家或机器学习工程师,核心的机器学习概念是相同的。
以下大部分课程来自 Coursera,你可以免费学习以下所有课程。您必须找到审计选项。如果你以前没有旁听过课程,这里有一个视频展示了如何在 Coursera 中旁听课程:
您可以根据自己的需要对以下每门课程进行多次审核。如果你不能在指定的时间内完成,你可以再次审计!
是不是很酷!
这里有一些免费的资源
我是 python 用户。所以我只能给出 Python 中机器学习的思路。如果你是一个完全的初学者,并且对 python 不是很了解,那么先练习一下 python 吧。这里是 Python 的一个专门化。通过大量的练习,它将教会你所有的 python 语法和结构:
由密歇根大学提供。这个专业教授 Python 3 的编程基础。我们会…
www.coursera.org](https://www.coursera.org/specializations/python-3-programming)
在那之后练习 python 变得更好。有几个很棒的平台给我们提供练习题。我用 l eetcode 和 checkio 来练习编程。在这些平台里,你可以看到别人的解决方案变得更好。还有很多其他平台可以练习编程:code wars、 CodeChef 是我经常听说的另外两个平台。
在学好编程之后,学习一些 python 的计算、数据操作、可视化库是个不错的主意。在你深入机器学习之前,它们是学习的基础。
Python 有强大的库,如 Numpy、Pandas、Matplotlib、Seaborn、Scipy 等,用于计算、数据操作、可视化和统计分析。Coursera 中有一个专业化系列,其中有两门课程是关于 Numpy、Pandas、Matplotlib、Seaborn、Scipy 的,第三门课程是关于应用机器学习的:
本专业的应用机器学习课程不会教你从零开始开发算法。但是它会教你一些概念以及如何使用 python 中 scikit-learn 库中的这些算法。对于初学者来说,这是一个很好的开始。密歇根大学提供这一专业。该专业包含的五门课程是:
这门课有一些好的专题,会增加你的作品集。此外,每周都会给你一个笔记本,可以作为你未来工作的备忘单。他们在这门课程中提供的材料非常好。
这是另一种专业化。它有四道菜。
这些课程的伟大之处在于,这些课程将采用基于项目的方法,每周的作业都是不同的项目。结束时,你将有一个完整的投资组合来炫耀。华盛顿大学开设了这门课程。
CS50 的课程通常质量很高。该课程由哈佛大学提供。你们知道,你们对哈佛的期望不会更低。顾名思义,这是一门入门课程。这门课会给你更多前两门课没有的机器学习的概念。如果你学了前面的课程,你将会学到更多的模型和概念,并且在你的投资组合中包含更多的项目。
本课程将涵盖图形搜索算法、对抗性搜索、知识表示、逻辑推理、概率论、贝叶斯网络、马尔可夫模型、约束满足、机器学习、强化学习、神经网络和自然语言处理。
吴恩达教授因其分解机器学习概念的巨大能力而闻名。本课程由斯坦福大学提供。本课程与前三门课程不同。以上三门课教你如何使用 python 的库中内置的机器学习算法。
但是吴恩达教授会教你如何从头开始开发机器学习算法。所以,这比以前的课程要难多了。
但是如果你能完成它,它会给你很大的力量。这是一个长达 11 周的课程。但是你可以免费旁听这个课程,次数不限。本课程将教你从零开始开发线性回归、逻辑回归、神经网络、支持向量机、k 均值聚类、主成分分析、异常检测、推荐系统开发。
这门课可能有点不同的一点是,作业指令在 Matlab 中。但是如果你擅长 python,你可以把这些概念用 python 来实现。你可以在本页找到大部分用 python 完成的作业的链接:
[## 使用 Python 从零开始的多类分类算法:分步指南
本文介绍两种方法:梯度下降法和优化函数法
towardsdatascience.com](/multiclass-classification-algorithm-from-scratch-with-a-project-in-python-step-by-step-guide-485a83c79992)
我还在用 python 写剩余作业的教程,很快就会完成。
看起来很多啊!对吗?
但是学习机器学习库会更容易。在你学会使用一些算法后,你会更容易掌握。但是在吴恩达的课程中从头开始学习算法将会花费很多时间。
这些都是我想分享的关于机器学习的课程。这里有一些深度学习的课程。
这也是一种专业化。现在他们把它升级了,做成 Tensorflow 上的专业认证课程。本系列将教你如何在项目中使用 TensorFlow。这门课程没那么难。因为它没有教你如何从零开始开发深度学习算法。它将教你如何使用 TensorFlow 库。
Tensorflow 是深度学习的一个非常强大的工具。它将处理幕后所有的数学难题。你只需要安装它,调用库,并使用它。
这个专业将教你使用 TensorFlow 进行数值预测,自然语言处理,图像分类和时间序列预测。
这是该专业的四门课程:
面向人工智能、机器学习和深度学习的 TensorFlow 简介
这些课程都采用基于项目的方法。所以,学习是有趣的!
这是吴恩达教授的另一系列课程。如果你在尝试学习机器学习和深度学习,很难避开 Ng 教授。他是先驱者之一!
他把概念教得很清楚,教你详细开发算法。这门课会有点难,因为它是关于从零开始开发算法,并从其核心了解它。但如果你能完成它,这将是值得的。它包括以下课程:
结论
如果你能把时间奉献给这些课程,你就是机器学习和深度学习的行家。还有很多其他的库和主题。因为机器学习是一个广阔的领域,而且每天都在增长。但是如果你有一个坚实的基础,你将很快学会任何其他新的库。
你必须对此保持开放的态度。这是一个学习永无止境的领域。不管你学了多少,明天都会有新的东西出现。
最后一个建议是,不要急于学习任何对你来说新的东西。先掌握几个库和算法。这会培养你的判断力。你会明白哪个对你重要,你的兴趣是什么。
欢迎在推特上关注我,喜欢我的脸书页面。
阅读推荐:
日常工作中需要的所有数字方法
towardsdatascience.com](/a-complete-guide-to-numpy-fb9235fb3e9d) [## 学习使用 Python 的 Scikit_learn 库通过项目开发 KNN 分类器
适合机器学习新手
towardsdatascience.com](/clear-understanding-of-a-knn-classifier-with-a-project-for-the-beginners-865f56aaf58f) [## 数据科学家假设检验完全指南,Python 示例
用样本研究问题、解决步骤和完整代码清楚地解释
towardsdatascience.com](/a-complete-guide-to-hypothesis-testing-in-python-6c34c855af5c) [## Python 中的线性回归算法:一步一步
学习线性回归的概念,并使用 python 从头开始开发一个完整的线性回归算法
towardsdatascience.com](/basic-linear-regression-algorithm-in-python-for-beginners-c519a808b5f8) [## 使用 Python 中的简单代码构建一个推荐系统
如何用 Python 构建电影推荐系统
medium.com](https://medium.com/towards-artificial-intelligence/build-a-simple-recommendation-system-in-python-7747be06a2f2)
更高的准确性并不意味着更好的机器学习模型性能
我们可以定量衡量机器学习模型的性能,但不仅仅是准确性,还有很多。
介绍
如果我们想要基于数据预测一些结果,我们将使用机器学习模型从数据中进行监督学习。如果我们想确保模型正确工作,我们必须定量地知道模型的性能如何。对于那些刚接触机器学习的人来说,他们只是依赖准确性。
准确性是指模型正确预测所有标签的程度。他们认为更高的精度意味着更好的性能。嗯,确实是这样,但是如果只是用准确性的话,就要小心了。精确度计算所有真实的预测值,但不针对每个存在的标注。
如果你的目标想要正确地预测一个特定的标签,例如一个正标签,这是一个严重的问题。更高的准确性并不意味着我们在预测特定标签上有很好的表现。为了解决这个问题,有许多度量标准,在分类问题中,有精确度、召回率、F1 值等。
让我来解释一下这些指标:
精确
精度描述了模型正确预测标签的程度。
回忆
Recall 描述了模型如何正确检索所有标签。
F1 分数
F1 分数是精度和召回率的乘积除以精度和召回率的和,然后加权 2 的比例。该指标结合了精确度和召回率。如果值变大,则模型越好。
建议使用此指标,因为它们仅指定了标签的一个值。
例子
基于这些指标,我们如何衡量它?好吧,我们可以先根据真实和预测标签做一个混淆矩阵。我使用 sklearn.metrics 库中的混淆矩阵函数创建了混淆矩阵,这是我预测明天是否下雨的项目。
如果你对我的项目感兴趣,你可以查看下面我的 GitHub,
permalink dissolve GitHub 是超过 5000 万开发人员的家园,他们一起工作来托管和审查代码,管理…
github.com](https://github.com/khalidmeister/my-projects/blob/master/DS1_Rain%20In%20Australia/code.ipynb)
矩阵是这样显示的,
注意:左上是真正的否定,右下是真正的肯定。
在左上方,它代表真阴性的计数,在右上方代表假阳性(简单地说是错误阳性,实际上是阴性),左下方代表假阴性(错误阴性,实际上是阳性),最后,右下方代表真阴性的计数。有了这些值,我们就可以计算每个指标。在这种情况下,我们计算正面标签或明天下雨的度量。通过使用 sklearn.metrics 库中的 classification_report 函数,我们可以从这样的计算中获得概述。
可以看到,模型的准确率是 79%。如果观察真正值,模型的准确率为 52%,召回率为 53%。这不是一个好结果,因为模型不能正确预测“1”标签,而目标是预测该标签。
如果我们只使用准确性来衡量模型的性能,那么我们将无法实现预测标签的目标。这就是为什么我们应该使用另一个指标来支持准确性结果。
结论
总之,在我们制作模型时,我们应该检查现有的度量标准,而不仅仅是依赖于准确性。如果你只关注准确性,我们不确定该模型是否能很好地预测现有的某种标签。如果我们已经知道度量值是多少,我们就可以通过进行超参数调整、特征选择、特征工程,甚至对数据进行采样来制作一个平衡级数据集,从而改进模型。
感谢您阅读我的文章,您也可以在下面查看我以前的文章:
为什么你应该在它上面投资更多,为什么这使你的分析变得容易得多
towardsdatascience.com](/tidy-data-with-r-f3d078853fc6) [## 如果我有自己的时间,我会这样学习数据科学
感觉不知所措和筋疲力尽让我在释放数据科学技能方面表现不佳。这个…
towardsdatascience.com](/this-is-how-i-will-learn-data-science-if-i-got-back-my-own-time-e9148c909ce9)
绿色是新的黑色:用人工智能拯救亚马逊雨林!
新成果 /拯救环境利用 AI /哈佛 AI 为社会造福 /在 IEEESMC 2020
基于多模态噪声分割的亚马逊雨林碎片烧伤疤痕识别
亚马逊地区巴西北部帕拉 BR-163 高速公路附近的焚烧概述。经古斯塔沃·巴索许可使用,2019
(已收) 萨蒂扬·莫赫拉 、西德哈斯·莫赫拉、阿努帕姆·古哈、比普拉·班纳吉;IEEE 国际系统会议,Man,&控制论(IEEE SMC’20)
对于各种灾害管理和生态学研究来说,探测由难以接近的雨林中的野火引起的烧伤痕迹是重要的。耕地景观的碎片化和不同的种植模式经常阻碍烧伤疤痕的精确测绘。遥感方面的最新进展和多式数据的提供为这一制图问题提供了一个可行的解决办法。然而,分割烧伤痕迹的任务是困难的,因为它与相似外观的土地模式难以区分,烧伤痕迹的严重碎片性质和部分标记的噪声数据集。在这项工作中,我们提出了 AmazonNET 一个基于卷积的网络,允许从多模态遥感图像中提取燃烧模式。该网络包括 UNet-一种众所周知的具有跳跃连接的编码器解码器类型的架构。提出的框架利用堆叠的 RGB-NIR 通道,通过对来自亚马逊的新的弱标记噪声数据集进行训练,来分割来自牧场的烧伤疤痕。我们的模型通过正确识别部分标记的烧伤疤痕并拒绝错误标记的样本,展示了优异的性能,证明了我们的方法是第一个在多模式烧伤疤痕识别中有效利用基于深度学习的分割模型的方法。
在亚马逊河流域,火与几种土地耕作方式联系在一起。刀耕火种是巴西农业中最常用的做法之一(作为称为“queimada”的季节循环的一部分)。无论是对于开辟和清理农业区,还是更新牧场,其在农业链条中的重要性是不可否认的。不幸的是,这通常是森林野火的原因。
亚马逊雨林是植物和数十亿吨碳的主要储存库,碳的释放会导致气温大幅上升。因此,最近亚马逊森林大火的消息引起了轩然大波和关注。
无法控制的火灾,尤其是在旱季,会对当地和区域产生重大影响,导致自然生物群落的破坏、动植物物种的灭绝、污染、侵蚀和碳循环的失衡。这种干扰也影响到农业生产。因此,许多环境研究和资源管理活动需要准确识别燃烧区域,以便在空间和时间上监测受影响区域(所谓的燃烧疤痕),从而了解和评估这些区域的脆弱性,并促进可持续发展。
由于在区域和全球范围内火灾的地理范围很大,而且受火灾影响的地区的可及性有限,在过去几年里,遥感方法已成为具有成本效益的替代方法,能够以足够的空间和时间分辨率收集燃烧地区的信息。遥感技术可以为火灾管理、估计和探测、燃料测绘、野火后监测,包括燃烧面积和严重程度估计,提供有用的数据。
问题陈述
当前的非深度学习方法严重依赖领域知识和用户的手动输入,并且不能从数据中提取抽象表示。深度学习试图解决这些问题,然而,由于普遍缺乏任何标记数据,它们在烧伤疤痕预测中仍然被忽视。
在这项工作中,我们利用传感领域的最新进展,在遥感领域实现了无处不在的多模态数据和计算机视觉,利用有噪声的弱标记数据,通过 UNet 识别碎片化的烧伤疤痕,使我们的方法成为首批在多模态烧伤疤痕识别中利用基于深度学习的分割模型的方法之一。
结果(更多信息见下文)
相关著作
语义分割
语义分割是计算机视觉中的一个重要问题。如果不同的像素属于同一个对象类,它就将这些像素聚集在一起。由于它们能够捕捉精确定位的语义上下文,它们已被用于自动驾驶、人机交互、诊断、遥感等各种应用中。
在 DNNs 出现之前,各种各样的特征被用于语义分割,如 K-means、梯度方向直方图、尺度不变特征变换等。今天,已经提出了许多编码器-解码器网络及其变体,如 SegNet。专业应用带来了新的改进,如用于医学图像分割的 UNet,用于精细分割的基于 CRFs 的网络。
遥感中的多模态数据
遥感中的多模态分割涉及利用各种策略来有效地组合包含在多模态中的信息,以生成有利于精确土地利用分类的单一、丰富、融合的表示。常用方法包括在输入阶段连接通道,连接从类似 CNN 的单峰网络提取的特征,以生成土地制图分割。最近的工作涉及更复杂的想法,如“交叉注意”,以融合多种形式,产生专注的光谱和空间表示。
烧伤疤痕鉴定
多模式数据的同时获得导致了最近在定位火灾和量化燃烧面积方面的进步。每种模式都提供了关于同一地理区域的辨别信息,有助于在不利条件下绘制地图,如光谱混淆(如由于云遮蔽)和烧伤疤痕的可变性,使区分植被变得困难。在这个领域中所做的大部分工作涉及诸如自相关、自组织映射、线性光谱混合模型、SVM、随机森林等方法。然而,最近的作品似乎都没有利用当前的深度学习方法,如 CNN 或 SegNet 或 UNet 等编码器-解码器模型,这可能是因为缺乏标记数据。
提议的方法
这项工作的目的是执行语义分割和识别烧伤疤痕利用空间光谱信息构成的可见光和近红外。
体系结构
在遥感中,由于缺乏良好的标记数据集,基于计算机视觉的方法很难应用,因为所需的数据处理和标记只能由现场专家完成,这使得标记数据很少或不可用。类似的问题也出现在医学图像分割中,因此遥感中的常用方法有时是从医学分割领域中得到启发的。对于烧伤疤痕分割任务,我们将我们的网络基于 UNet 架构,其中来自编码器的特征激活被存储并被传输到相应的解码器层用于连接。
**编码器:**编码器网络由 3×3 卷积层以及批量标准化层、ReLU 非线性激活层和 2×2 最大池层组成。
**解码器:**解码器网络由名为 UpPool 块的上采样层组成,它执行 3x3 Conv2DTranspose、3×3 卷积以及批归一化层和 Dropout2D 层,Dropout 2d 层的 drop out 值为 0.1。对输出结果进行阈值处理,以获得表示烧伤疤痕的二进制输出图。
数据集
该数据集由亚马逊雨林的 LANDSAT8 的可见光和近红外卫星图像组成。数据集是 2017 年和 2018 年从超过 4 个州(即托坎廷斯州、马兰豪州、马托格罗索州、帕拉州)获取的,覆盖了超过 4 个陆地巴西生物群落,即塞拉多、亚马逊、卡廷加和潘塔纳尔。它由 299 个样本 VISNIR 图像对组成,大小为 1024x1024,具有地面实况,是二进制图像,其中 1 表示森林中的烧伤疤痕,0 表示未受火灾影响的区域。从图中可以看出,地面真实是有噪声的,也是部分标记的,有时是错误标记的。该数据集由国家空间研究所(INPE)管理,是 Quiemadas 项目的一部分。
结果
结果:该网络正确地分割烧伤疤痕,拒绝错误标记的斑点,并正确地识别部分标记或未标记的样本。(a)可见通道(b)近红外通道(假彩色 hsv) ©可用标签(包括部分/不正确的标签)(d)预测的烧伤疤痕(e)二元烧伤疤痕。(a)和(b)中黑色轮廓表示标记数据©的轮廓,以便于观察。黄色方框表示正确标记或未标记的烧伤疤痕。白色方框表示贴错标签/识别错误的烧伤疤痕
该模型的训练准确率为 69.51%,验证准确率为 63.33%。结果验证了利用基于 U-net 的分割在烧伤疤痕识别中的有效性。
可以看出,网络正确地识别未标记的碎片烧伤疤痕(表示为黄色虚线框),并取消选择输出二进制图中错误标记的区域(表示为白色虚线框)(正确标记的输出突出显示为黄色,取消选择的标记为白色)。
分割烧伤疤痕中的小缺陷:网络错误地将河流、曲流、牛轭湖和云分割为烧伤疤痕。(a)和(b)中黑色轮廓表示标记数据©的轮廓,以便于观察。黄色方框表示正确标记或未标记的烧伤疤痕。白色方框表示贴错标签/识别错误的烧伤疤痕。缺陷归因于没有可用的分割标签和数据集中这些特征的可忽略的实例。
然而,我们的网络无法将河流和云的图案与烧伤疤痕区分开来,如下图所示。当我们的网络段(a)河流(b)蜿蜒曲折,牛轭湖和云作为烧伤疤痕模式时,缺陷出现。有趣的是,在图中的样本 2 和 3 中,网络如何准确地分割小的碎片状烧伤疤痕,但绝对不能拒绝这些疤痕。这可主要归因于(I)缺乏任何标记样本,以及(ii)数据集中包含上述地理特征的样本可忽略不计。
结论和未来工作
我们利用代表亚马逊雨林烧伤模式的部分/错误标记数据集,提出了基于 U-net 的分割网络,以正确识别烧伤疤痕并拒绝不正确的标签,证明了人工智能在碎片烧伤疤痕识别中的有效性。我们提出了不足之处,并考虑在未来的工作中解决这些问题。
基于多模态噪声分割的亚马逊雨林碎片烧伤疤痕识别
(已接受) 萨蒂扬·莫赫拉 、西德哈特·莫赫拉、阿努帕姆·古哈、比普拉·班纳吉;IEEE 国际系统会议,Man,&控制论(IEEE SMC 2020)
(赠送) 萨蒂扬·莫赫拉 、西德哈斯·莫赫拉、阿努帕姆·古哈;
哈佛 CRCS 人工智能公益工作坊 2020
温室气体(CO2)排放和炼油
炼油厂在排放二氧化碳中扮演了多大的角色,哪些州是主要排放者?
Marcin Jozwiak 在 Unsplash 上拍摄的照片
我之前的帖子已经使用 Python 可视化来探索美国各地的炼油能力。此外,我们分析了多年来原油和燃料价格的行为。
在这篇文章中,我将探讨一些其他的东西:炼油过程中的二氧化碳(CO2)排放。以下所有工作的来源数据来自美国环保署的温室气体报告项目,其数据在此公开。该数据由 2011 年至 2018 年 1210 个行业的 5441 家公司以公吨为单位的自我报告的二氧化碳当量排放量组成。
教育要点:二氧化碳当量是一种测量任何污染物排放量的方法。例如,1 吨二氧化碳当量意味着实际释放的气体量将使地球变暖,与释放 1 吨二氧化碳的量相同。
TL/DR:发电产生的二氧化碳占排放到大气中的二氧化碳总量的 62%多一点,炼油和化学制品占第二位,为 9.6%。德克萨斯州、路易斯安那州和加利福尼亚州是主要的排放源,伊利诺伊州也是某些行业的主要排放源。
炼油给人的印象是喷出烟雾和蒸汽的巨大结构。但是——与美国其他主要行业相比,炼油厂到底排放了多少二氧化碳?这些数字在各州之间是如何变化的?就当前排放量而言,哪个行业提供的减排目标最大?
首先,我要说明一个众所周知的观点,即理解二氧化碳排放非常重要,因为大气中的二氧化碳吸收辐射,并将热量截留在地球大气中。二氧化碳含量越高,储存在大气中的热量就越多,气温和一般天气干扰的增加就越大——也被称为全球变暖或气候变化。
首先,让我们使用下面的代码来生成一个条形图,以显示二氧化碳当量最高的行业。
fig = px.bar(ByIndustry, x='Latest Reported Industry Type (sectors)', y='%')
fig.update_xaxes(tickangle=45, tickfont=dict(family='Rockwell', size=14))
fig.update_layout(width=1000, height=500, title='% of emissions from various Industries')fig.add_annotation(x=31, y=62.38, font=dict(family="Courier New, monospace", size=18, color="black",),
text="62.38% of emissions from Power Plants")fig.show()
图 1:趋势显示 2011-2018 年总排放量的 62%来自发电厂。炼油厂和化工厂远远低于
有趣的是,从 2011 年到 2018 年,电厂的排放量占总排放量的 62%多一点。相比之下,炼油和化学制品共占 9.1%的排放量,比这少了 85%。
图 2:显示最高排放行业的代码和表格
观察不同行业在排放二氧化碳当量中扮演的角色是很有趣的。这在各州之间有何不同?请看下面的动态图表,该图表分析了 2018 年各州对以上列表中指定行业的排放贡献:
图 3:显示美国各行业 2018 年排放数据的动态曲线图
美国 2018 年排放数据的关键要点:
- 得克萨斯州 T2 发电厂 T3 排放的二氧化碳当量达到峰值,为 1 . 85 亿吨。佛罗里达州和印第安纳州分别以 8400 万吨和 8100 万吨位居第二。
- 炼油厂和化工怎么样?德克萨斯州以 3800 万吨二氧化碳当量再次领先,路易斯安那州以 2800 万吨二氧化碳当量排名第二,这与 choropleth 在我的炼油厂产能文章中显示的 2019 年 1 月炼油厂产能的信息相符。
- 工业气体和矿物行业提供了思考的食粮。就背景而言,矿物是采矿业务的一部分,而工业气体则包括为其他业务制造常用气体,如氮气、二氧化碳、氩气、氢气和氦气。****
肯塔基州每年排放大约 100 万吨工业气体,而路易斯安那州和印第安纳州紧随其后,每年排放略多于 70 万吨。与此同时,德克萨斯州、密苏里州和伊利诺伊州是采矿业的主要排放者
这里的底线是,许多国家和许多行业在向大气排放二氧化碳方面发挥了作用,而发电是减排潜力最大的行业。
这一结论在美国环保署收集温室气体数据的 2011-2018 年间保持稳定。如下图 4 所示,这一时间范围内没有任何特定年份改变上述叙述。事实上,在这 7 年里,任何行业的排放情况都没有显著变化。
图 4:从 2011 年到 2018 年,发电厂提供了二氧化碳当量排放量的最大份额。这在 2011 年和 2018 年之间不会改变。
让我们开始改变思路,考虑炼油过程中的减排。
我之前讨论过将原油转化为汽油、喷气燃料、柴油等的炼油经济。炼油厂是如何做到这一点的?它通过使用各种精炼工艺单元来实现,其中主要有:加氢裂化器、流化催化裂化器(FCCs)、重整器和延迟焦化器。
这是许多新术语,我们将在接下来的几篇文章中深入探讨机器学习算法如何帮助优化这些不同单位的盈利能力和排放之间的平衡。
一如既往,我欢迎对这篇文章或其他文章的任何反馈或问题。支持本文分析的回购是此处。
Python 中大数据探索性数据分析
理解大数据
探索和收集 1200 万份美国健康保险市场数据的见解
在我之前的文章中,我们探索了 Python 中执行探索性数据分析(EDA)的图形化方法。会上讨论了折线图、回归线和奇特的运动图,讨论了如何利用它们来收集关于教育数据中的人口、收入和性别平等的见解。然而,数据相对较小,大约 200 行。在本帖中,我们将探索大约 1200 万条记录的更大数据,并研究在 Python 中执行 EDA 的其他方法。
资料组
我们将使用的数据集包含通过美国健康保险市场向个人和小型企业提供的健康和牙科计划的数据。它最初是由医疗保险中心&医疗补助服务(CMS) 准备和发布的,随后发表在 Kaggle 上。我们被告知数据包含 7 列:
- 商业年度 —计划为注册人提供保险的年份
- 州代码 —两个字符的州缩写,表示提供计划的州
- IssuerId —在健康保险监督系统(HIOS)中标识发行机构的五位数字代码
- 计划 id—14 个字符的字母数字代码,用于标识 HIOS 境内的保险计划
- 年龄 —订户年龄是否用于确定保险计划费率资格的分类指标
- 个别费率 —适用于某一费率区内保险计划的非烟草用户,或无烟草偏好的普通订户的每月保险费成本的美元值
- individual tobacco correct——适用于烟草用户在评级区域的保险计划的每月保险费成本的美元值
让我们将数据加载到 Python 中,并进行一些简单的初步探索。
import pandas as pd
insurance= pd.read_csv('./InsuranceRates.csv')# How many rows and columns are there?
print("The number of rows and columns are " + str(insurance.shape))
作者提供的图片— Jupyter 笔记本输出的数据形状
正如前面提到的,这个数据集非常大,它包含 12694445 行和前面讨论的 7 列。让我们看看一些数据列的唯一值。
# How many years does the data cover?
print("The number of unique years are " + str(insurance.BusinessYear.nunique()) + '\n')# What are the possible values for 'Age'?
print("The possible values for 'Age' are " + str(insurance.Age.unique()) + '\n')# How many states are there?
print("The number of states are " + str(insurance.StateCode.nunique()) + '\n')# How many insurance providers are there?
print("The number of insurance providers are " + str(insurance.IssuerId.nunique()) + '\n')
按作者分类的图像— Jupyter 笔记本在数据列上的输出
我们知道这些数据涵盖了 3 年的数据,美国 39 个州和总共 910 家保险提供商。年龄组从 21 岁到 64 岁不等,另外三个类别为“0-20 岁”、“家庭选项”和“65 岁及以上”。让我们看看个人每月支付的保险费的平均金额,以及最高和最低值。
# What are the average, maximum and minimum values for the monthly insurance premium cost for an individual?
print("The average value for the monthly insurance premium cost for an individual is " + str(insurance.IndividualRate.mean()))
print("The max value for the monthly insurance premium cost for an individual is " + str(insurance.IndividualRate.max()))
print("The min value for the monthly insurance premium cost for an individual is " + str(insurance.IndividualRate.min()))
作者提供的图片— Jupyter 笔记本输出的保险费平均值/最大值/最小值
个人每月保险费成本的平均值为 4098.03 美元,最大值为 999999 美元,最小值为 0 美元。这些值似乎不合理,因为最大值太高,接近每月一百万美元,而最小值不可能是 0 美元。显然,这似乎表明该数据集中可能存在一些数据错误。
调查个人保险成本
由于我们发现个人保险费成本似乎不合理,让我们通过首先绘制一个直方图(使用 Python matplotlib
)来显示分布来更详细地探究它。
# plotting the histogram over the whole range
plt.hist(insurance.IndividualRate,bins=20)
plt.xlabel('Individual Rate')
plt.ylabel('Frequency')
plt.title('Frequency of Individual Rate')
plt.show()# plotting the histogram from 800000 to 1000000
plt.hist(insurance.IndividualRate,bins=20,range=(800000,1000000))
plt.xlabel('Individual Rate')
plt.ylabel('Frequency')
plt.title('Frequency of Individual Rate')
plt.show()
按作者分类的图像—保险费直方图上的 Jupyter 笔记本输出
如前一节所述,我们发现大约有 48000 人每月支付接近 100 万澳元的保险费。
由于 1200 万中的 48000 只占数据的 0.4%,所以我们把它们去掉,专注于剩下的数据(即保险费少于 2000 美元)。
# filter data to those premium below $2000
insurance=insurance[(insurance.IndividualRate>0) & (insurance.IndividualRate<=2000)]# Generate a new histogram with a larger number of bins (say 200).
plt.hist(insurance.IndividualRate,bins=20)
plt.xlabel('Individual Rate')
plt.ylabel('Frequency')
plt.title('Frequency of Individual Rate')
plt.show()
按作者分类的图片— Jupyter 笔记本在保险费直方图上的输出(≤2000 美元)
我们看到直方图现在显示了更清晰的分布,有 15 个不同的组,即 0–100、100–200、200–300、300–400、400–500、500–600、600–700、700–800、800–900、900–1000、1000–1100、1100–1200、1200 接近 4000000 英镑的大多数人支付 0-100 英镑,少数人支付 1300-1500 英镑。
各州保险费的差异
将我们的数据过滤到更准确、更合理的保险费范围后,现在让我们使用箱线图来探索美国 39 个不同州的保险费成本是如何变化的。
# rotate the axis and set a bigger figsize to ease reading
insurance.boxplot(column = 'IndividualRate', by = 'StateCode',rot=90,fontsize=9,figsize=(10,10))
作者图片 Jupyter 笔记本输出的各州保险费箱线图
从箱线图中,我们知道 MO 的保险费率中值最低,而 AK 的保险费率最高。
让我们看看各州的保险公司数量是否也有所不同。请注意,我们需要进行一些数据聚合(即分组),然后才能在条形图中绘制数据。
# aggregate the data to find the number of insurance companies by state
groupbystate = insurance.groupby('StateCode').agg({'IssuerId':{'companies_count':'nunique'}})
groupbystate=groupbystate.reset_index()
groupbystate.columns=groupbystate.columns.droplevel(0)
groupbystate.rename(columns = {'':'StateCode'},inplace = True)plt.bar(groupbystate.StateCode,groupbystate.companies_count)
plt.title('No. of insurance issuers in each state')
plt.xlabel('State Code')
plt.ylabel('No.of insurance issuers')
plt.xticks(rotation=90,fontsize=7)
plt.show()
按作者分类的图片— Jupyter 笔记本输出在各州保险发行商的条形图上
从柱状图中,我们知道各州的保险发行人数量差异很大。HI 最低为 4,TX 最高为 48。
从这两个图表中,一个合乎逻辑的问题是,竞争会影响每个州的保险费吗?拥有更多的保险提供商可能会导致较低的保险费率,而在该州拥有较少的保险提供商可能会导致较高的保险费率。让我们使用散点图来探索这种可能的关系。我们首先需要汇总数据(即使用 groupby)以获得每个州的保险费成本中值。
# Use a scatterplot to plot the number of insurance issuers against the median insurance cost for each state.
# Aggregate the data to find number of insurance issuers and median cost for each state
groupbystate2 = insurance.groupby('StateCode').agg({'IssuerId':{'companies_count':'nunique'},'IndividualRate':{'median_cost':'median'}})
groupbystate2=groupbystate2.reset_index()
groupbystate2.columns=groupbystate2.columns.droplevel(0)
groupbystate2.rename(columns = {'':'StateCode'},inplace = True)plt.scatter(groupbystate2.StateCode,groupbystate2.companies_count,s=groupbystate2.median_cost)
plt.title('No. of Insurance Issuers VS Median Insurance Cost for each state')
plt.xlabel('State Code')
plt.ylabel('No. of Insurance Issuers')
plt.xticks(rotation=90,fontsize=7)
plt.show()
按作者分类的图片 Jupyter 笔记本输出各州保费中位数散点图
从散点图来看,保险中值成本和保险公司数量之间通常没有明确的关系。保险发行人数量较少的州仍会导致较低的中值利率(用圆圈大小表示)。同样,拥有大量保险发行人的州仍然会导致中值利率较高。
保险费随时间的变化
请记住,我们的数据包含三年的数据,让我们使用箱线图来探索保险费是否会随着时间的推移而变得更贵。
insurance.boxplot(column='IndividualRate',by='BusinessYear')
plt.ylim(0, 1000)
按作者分类的图片— Jupyter 笔记本电脑在一段时间内的保费箱线图输出
如箱线图所示,中位数(用绿线表示)在这些年中略有增加,这意味着这些年来保险单变得稍微贵了一些。
保险费随年龄的变化
在现实世界中,我们知道一个人年龄越大,他/她就越容易发生事故或健康疾病,这意味着保险公司需要收取更高的保险费率。最后,让我们用箱线图来研究保险人的年龄是否对保险费成本有影响。请注意,我们必须过滤掉“家庭选项”,因为它无法根据年龄进行衡量。
# Filter out 'Family Option' in order to plot boxplot
temp_insurance=insurance[(insurance.Age!='Family Option')]
temp_insurance.boxplot(column='IndividualRate',by='Age',rot=90,fontsize=9,figsize=(10,10))
按作者分类的图片— Jupyter 笔记本输出的各年龄段保费箱线图
箱线图与我们之前在现实世界中讨论的一致。很明显,保险费成本随着被保险人的年龄而增加,因为我们可以看到中间值在整个时间段内不断增加。因此,将老年人(65 岁及以上)与年轻人(0-20 岁)进行比较,老年人支付的保险费用中位数比年轻人高,老年人每月的差额约为 700-150 = 550 英镑。
摘要
继上一篇文章之后,这篇文章进一步讨论了如何使用箱线图、散点图和条形图从我们的数据中发现洞见。还要注意,这个数据比前一个大得多,但是肯定的是,这不会影响在 Python 上构建这些可视化的处理时间。希望你们已经用这种新的图形化方法在 Python 中做探索性数据分析时找到了一些替代方法。感谢阅读!
超参数调谐的网格搜索
各位机器学习爱好者好!由于实习的原因,我已经有一段时间没有在 Medium 上写东西了。当我在做我的项目时,我面临着一种情况,我需要用不同的超参数来尝试不同的分类器。我发现每次都很难手动更改超参数并使它们适合我的训练数据。原因如下:
- 这很费时间
- 很难跟踪我们尝试过的超参数,我们仍然必须尝试
因此,我很快询问谷歌是否有解决我的问题的方法,谷歌向我展示了来自 Sklearn 的名为的东西。让我用一个简单的例子来分享我是如何利用这个 GridSearchCV 来解决我的问题的。
什么是 GridSearchCV?
GridSearchCV 是一个库函数,是 sklearn 的 model_selection 包的成员。它有助于遍历预定义的超参数,并使您的估计器(模型)适合您的训练集。因此,最后,您可以从列出的超参数中选择最佳参数。
除此之外,您可以为每组超参数指定交叉验证的次数。
带解释的示例
from sklearn.model_selection import GridSearchCV
from sklearn.neighbors import KNeighborsClassifierkn = KNeighborsClassifier()params = {
'n_neighbors' : [5, 25],
'weights': ['uniform', 'distance'],
'algorithm': ['auto', 'ball_tree', 'kd_tree', 'brute']
}grid_kn = GridSearchCV(estimator = kn,
param_grid = params,
scoring = 'accuracy',
cv = 5,
verbose = 1,
n_jobs = -1)grid_kn.fit(X_train, y_train)
我们来分解一下上面的代码块。像往常一样,您需要从 sklearn 库中导入 GridSearchCV 和估计器/模型(在我的例子中是 KNClassifier)。
下一步是定义您想要尝试的超参数。这取决于你选择的评估者。您所需要做的就是创建一个字典(我的代码中的变量 params ),它将超参数作为键,并有一个 iterable 来保存您需要尝试的选项。
然后你要做的就是创建一个 GridSearchCV 的对象。这里基本上需要定义几个命名参数:
- 估计器:您创建的估计器对象
- params_grid :保存您想要尝试的超参数的字典对象
- 评分:您想要使用的评估指标,您可以简单地传递一个有效的评估指标字符串/对象
- cv :您必须为每组选定的超参数尝试交叉验证的次数
- verbose :您可以将它设置为 1,以便在将数据放入 GridSearchCV 时得到详细的打印结果
- n_jobs :如果 it -1 将使用所有可用的处理器,您希望为该任务并行运行的进程数。
这些都是你需要定义的。然后你要像平时一样拟合你的训练数据。您将得到如下所示的第一行:
Fitting 5 folds for each of 16 candidates, totalling 80 fits
...
...
...
你不明白这是什么意思吗?简单!因为我们必须为 n_neighbors 尝试两个选项,两个用于权重,四个用于算法,所以总共有 16 种不同的组合我们应该尝试。对于每个组合,我们有 5 个 CV 拟合,因此我们的 GridSearcCV 对象将测试 80 个不同的拟合。
这种适合的时间取决于你正在尝试的超参数的数量。一旦一切完成,您将得到如下输出:
[Parallel(n_jobs=1)]: Done 80 out of 80 | elapsed: 74.1min finished]
然后要知道什么是最好的参数,你可以简单地打印出来
# extract best estimator
print(grid_kn.best_estimator_)Output:
KNeighborsClassifier(algorithm='auto',
leaf_size=30, metric='minkowski',metric_params=None, n_jobs=-1, n_neighbors=25, p=2, weights='distance')# to test the bestfit
print(grid_kn.score(X_test, y_test))Output:
0.9524753
很酷不是吗?现在你所要做的就是改变估计量,定义一个你必须尝试的超参数字典。希望这对你有帮助。要了解更多关于 GridSearchCV 的内容,请查看 sklearn 的官方文档。
参数调整的网格搜索
学习这个简单易行的技术来调整你的机器学习模型
简介
一旦你建立了一个机器学习模型,你会想要调整它的参数以获得最佳性能。每个数据集的最佳参数是不同的,因此它们需要调整,以便算法可以获得其最大潜力。
我见过很多初学数据的科学家用手做参数调优。这意味着运行模型,然后在笔记本中更改一个或多个参数,等待模型运行,收集结果,然后一次又一次地重复这个过程。通常,人们会在途中忘记哪些参数是最好的,他们需要重新做一次。
一般来说,上述策略并不是最高效的。幸运的是,由于 sci-kit learn library 的作者添加了 GridSeachCV,这个过程可以很容易地自动化。
什么是 GridSearchCV?
图片来自 Pixabay 的 Nicolás Damián Visceglio
GridSearchCV 是我上面描述的简单方法的替代方法。不用手动调整参数并多次重新运行算法,您可以列出您希望算法尝试的所有参数值,并将其传递给 GridSeachCV。
GridSearchCV 将尝试这些参数的所有组合,使用交叉验证和您提供的评分标准来评估结果。最终它会吐槽出最适合你数据集的参数。
GridSearchCV 可以与 sci-kit learn 库中的任何监督学习机器学习算法一起使用。如果你提供一个合适的度量标准,它将同时适用于回归和分类。
让我们用一个真实的例子来看看它是如何工作的。
GridSearchCV 代码示例
为了说明,让我们加载虹膜数据集。这个数据集有三种不同鸢尾物种的 150 个例子。数据集没有缺失值,因此不需要清理数据。
from sklearn.datasets import load_iris
import pandas as pd
%matplotlib inlinedata = load_iris()
df = pd.DataFrame(data['data'], columns=data['feature_names'])
df['species'] = data['target']
df.head()
现在让我们将数据集划分为训练和测试。
from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(df.drop('species', axis=1), df.species ,test_size = 0.2, random_state=13)
一旦我们划分了数据集,我们就可以用我们选择的算法建立网格搜索。在我们的例子中,我们将使用它来调整随机森林分类器。
from sklearn.model_selection import GridSearchCV
from sklearn.ensemble import RandomForestClassifierrfc = RandomForestClassifier()grid_values = {'n_estimators': [10, 30, 50, 100],
'max_features': ['sqrt', 0.25, 0.5, 0.75, 1.0],
'max_depth' : [4,5,6,7,8],
}grid_search_rfc = GridSearchCV(rfc, param_grid = grid_values, scoring = 'accuracy')
grid_search_rfc.fit(x_train, y_train)
在上面的代码中,我们首先通过使用不带参数的构造函数来设置随机森林分类器。然后,我们定义参数以及 grid_values 变量中每个参数的值。“grid_values”变量随后被传递给 GridSearchCV,同时传递的还有随机森林对象(我们之前已经创建过)和评分函数的名称(在我们的例子中是“accuracy”)。最后,我们通过调用网格搜索对象上的 fit 函数来拟合它。
现在为了找到最佳参数,可以使用 best_params_ attribute:
grid_search_rfc.best_params_
使用 max_features 参数的 75 %的特征并使用 10 个估计器,我们得到了具有六层深度的树的最高准确度。
这比手动尝试所有参数要容易得多。
现在,您可以使用网格搜索对象,使用最佳参数进行新的预测。
grid_search_rfc = grid_clf_acc.predict(x_test)
并在测试集上运行分类报告,以查看模型在新数据上的表现。
from sklearn.metrics import classification_report
print(classification_report(y_test, predictions))
您可以看到所有类别的准确度、召回率、精确度和 f 分数的详细结果。
请注意,我们使用精确度来调整模型。这可能不是最好的选择。我们实际上可以使用其他指标,如精确度、召回率和 f 值。让我们开始吧。
from sklearn.metrics import make_scorer, accuracy_score, precision_score, recall_score, f1_scorescoring = {'accuracy': make_scorer(accuracy_score),
'precision': make_scorer(precision_score, average = 'macro'),
'recall': make_scorer(recall_score, average = 'macro'),
'f1': make_scorer(f1_score, average = 'macro')}grid_search_rfc = GridSearchCV(rfc, param_grid = grid_values, scoring = scoring, refit='f1')
grid_search_rfc.fit(x_train, y_train)
在上面的代码中,我们设置了四个评分标准:准确度、精确度、召回率和 f-score,我们将它们存储在列表中,稍后作为评分参数传递给 grid search。我们还将 refit 参数设置为等于评分函数之一。这是 f-score 是我们的情况。
一旦我们运行它,我们可以获得 f-score 的最佳参数:
grid_search_rfc.best_params_
此外,我们可以使用 cv_results_ attribute 来了解有关 grid_search 设置的更多信息。
grid_search_rfc.cv_results_
如果您想查看其他指标的结果,可以使用 cv _ results[’ mean _ test _【T0]']。因此,为了获得我们之前设置为评分函数之一的召回结果,您可以使用:
grid_search_rfc.cv_results_['mean_test_recall']
上面我们可以看到网格搜索参数组合的所有召回值。
GridSearchCV 缺点
你有没有注意到所有召回结果的清单很长?它实际上有 100 个元素。这意味着 grid 已经尝试了 100 种不同的参数组合。这非常多,而且非常耗时,尤其是在大型数据集上。
在我们的例子中,grid search 对 100 个不同的随机森林设置进行了五重交叉验证。想象一下,如果我们有更多的参数要调整!
GridSearchCV 有一个替代方案叫做 RandomizedSearchCV。它不是尝试所有参数,而是只从给定分布中采样参数的子集,因此可能更快更有效。
总结
在本文中,您了解了如何使用网格搜索来优化参数调整。是时候在不同的数据集上使用不同的模型而不是随机森林来尝试您新获得的技能了。编码快乐!
最初发布于 aboutdatablog.com: 学习如何使用网格搜索进行参数整定,2020 年 10 月 8 日。
PS:我正在 Medium 和aboutdatablog.com上撰写以简单易懂的方式解释基本数据科学概念的文章。你可以订阅我的 邮件列表 在我每次写新文章的时候得到通知。如果你还不是中等会员,你可以在这里加入。
下面还有一些你可能喜欢的帖子
* [## 关于 jupyter 笔记本你不知道的 9 件事
用这些建议提高你的工作效率
towardsdatascience.com](/9-things-you-did-not-know-about-jupyter-notebook-d0d995a8efb3) [## 最佳数据科学书籍
帮助您学习数据科学的前三本书
towardsdatascience.com](/best-data-science-books-be1ab472876d) [## 作为一名有抱负的数据科学家,你应该关注的中型作家
我最喜欢的 10 个数据科学博主,让你的学习之旅更轻松。
towardsdatascience.com](/medium-writers-you-should-follow-as-an-aspiring-data-scientist-13d5a7e6c5dc)*
SARIMAX 参数的网格搜索
实践教程
为 statsmodels SARIMAX 模型找到最佳参数的简单方法
作者图片
如果您已经在这里登陆,那么您很可能正在实现一个 statsmodels SARIMAX 时间序列模型,并且您正在寻找一种简单的方法来识别所有最佳参数。我有一些好消息要告诉你…你来对地方了!
在本教程中,您将学习如何运行简单的网格搜索来为 statsmodel SARIMAX 时间序列模型查找最佳参数。或者您可以直接复制并粘贴代码——甚至更简单!
对于我们的 SARIMAX 模型,总共有七个子参数,单独计算它们并不容易。然而,通过几行简单的代码,我们可以创建一个定制的网格搜索,给出一个最优参数列表,按照用户选择的选择标准(AIC 或 BIC)排序。
选择标准
让我们从选择标准开始。这里的两个选择是 AIC 和 BIC。它们分别代表阿凯克信息准则和贝叶斯信息准则。他们选择用最少的独立变量解释最大变化的模型。[1]他们计算的方法是使用最大似然估计(MLE ),他们都惩罚一个增加变量数量以防止过度拟合的模型。
围绕哪一个最好用,有很多讨论。BIC 会因为一个模型有越来越多的变量而加大对它的惩罚力度。如果在未来的数据集或模型将应用的多个数据集中有更多的变化,那么我推荐使用 BIC。就像我在这里一样。然而,并没有很大的区别,AIC 也很普通,所以真正的选择权在你。
SARIMAX 参数
SARIMAX 是时间序列模型中的佼佼者。在进行时间序列建模时,它基本上考虑了所有可以考虑的因素。
我们将在这里对参数进行简单直观的解释。
**S——代表季节性。**这意味着数据表现出季节性。这样的例子可能是一年中的季节,因此给定位置的温度波动将根据季节而变化,通常夏天更暖和,冬天更凉爽。如果我们有月平均值的数据,那么“S”就是 12。在我们的模型中用“s”来表示。
AR —代表自回归。这是数据在任何给定的时间间隔或之前的时期与之前的数据的相似程度。简单来说,它代表数据中的重复模式。自回归是指数据在之前数据的某个滞后期内回归,并找到回归最强的地方,代表数据中的模式。在我们的模型中表示为“p”。
I—代表集成的。“I”表示数据值已被替换为其值与先前值之间的差值。[2] 在我们的模型中用“d”来表示。
**MA——代表移动平均线。**此术语计算给定周期数的移动平均值。它用于减少模型中的噪波或使其平滑。移动平均周期越长,噪声就越平滑。在我们的模型中表示为“q”。
**X —代表外源性。**这考虑了一个已知的外部因素。在我们的模型中,这是一个可选的参数,而不是一个参数。是我们可以添加到模型中的一系列外生回归变量。这部分是可选的,在计算我们的最佳模型参数时不是必需的。
在我们的模型中,我们的参数如下所示:
SARIMAX (p,D,q) x (P,D,Q,s)
statsmodel SARIMAX 模型考虑了常规 ARIMA 模型(P,D,Q)以及季节性 ARIMA 模型(P,D,Q,s)的参数。这几组参数是我们的模型中的参数,分别称为订单和季节订单。
现在是有趣的部分——让我们编码吧!
以下是运行网格搜索以找到 statsmodel SARIMAX 模型的最佳参数所需的所有代码。
这是输出的样子!
作者图片
对于我的模型,当我将预测值与已知值进行比较时,二阶组合的 BIC 略高,但 RMSE(均方根误差)较低。为此,我选择用列出的二阶组合来构建我的模型。
下面是我们如何从上面的参数网格搜索中获取结果,并构建我们的模型!在此代码中,我们将:
- 建立我们的模型
- 打印摘要
- 绘制诊断图
这是我们的汇总输出的样子!
作者图片
我们可以看到这个模型的所有参数都很重要。情况并非总是如此,这些数据没有太多的可变性。
这是我们的输出图诊断的样子!
作者图片
我们找到了。
我希望这个教程对你有用。一如既往,欢迎在评论中问我任何问题。
参考
- [1]https://www . scribbr . com/statistics/a kaike-information-criterion/
- [2]https://en . Wikipedia . org/wiki/auto regressive _ integrated _ moving _ average
- [3]https://www . stats models . org/dev/generated/stats models . TSA . statespace . sarimax . sarimax . html
使用的软件
- 计算机编程语言
- Jupyter 笔记本
Python 中的网格搜索—超参数调整
图片由 JPuckett Design 在 Instagram 上提供。
超参数调整对于机器学习(ML)模型的正确运行至关重要。网格搜索法是超参数优化的基本工具。
网格搜索方法考虑几个超参数组合,并选择返回较低错误分数的一个。当只有几个超参数需要优化时,这种方法特别有用,尽管当 ML 模型变得复杂时,这种方法优于其他加权随机搜索方法。
本文介绍了用网格搜索进行超参数调整的思想。您将学习网格搜索如何工作,以及如何实现它来优化您的机器学习方法的性能。
提供代码片段有助于理解实现。完整的代码可以在这个 GitHub 库中找到。
网格搜索提供了一种调整 ML 模型超参数的直观方法,对于低维空间非常有效。
网格搜索概述
1-准备数据库。
2-确定要优化的模型超参数,然后选择要测试的超参数值。
3-评估超参数网格中每个组合的误差分数。
4-选择具有最佳误差度量的超参数组合。
生成数据
一个数据库由一系列特征x:{xx₂,…,xₙ*}和一个或多个目标属性y:{f₁( f₂(x₁、x₂,…、xₙ)、…、fₙ(x₁、***
在我们的例子中,我们将使用一个简单的数据库,有两个描述符x**:{x₁,x₂}和一个目标属性y:{f(x₁, x**
*作为一个例子,我们将使用遵循二维函数*f(x₁、x₂)=sin(x₁)+cos(x₂】的数据,加上区间(-0)中的小的随机变化因此,我们的数据将遵循以下表达式:
f(x₁,x₂)= sin(x₁)+cos(x₂)+rnn
我们在区间x₁:10,10 和x**₂:10,10 中生成一个 21×21 的网格。下图显示了数据库中 441 个点的可视化表示。**
作者图片
用来生成这个的函数( x ₁、x₂、、f(x₁、x【₂))
机器学习模型
在这种情况下,我们将使用一个核岭回归 (KRR)模型,带有一个径向基函数核。通过调整两个超参数来评估模型的准确性:正则化常数(α)和核方差(γ)。如果你想知道更多关于 KRR 如何运作的细节,我建议看看我最近写的关于这个话题的文章。
在上一节中,我们已经看到了如何生成一个( x ₁, x ₂ ,f(x【₁】,x₂))数据集。为简单起见,我们将其转换为一个(x,y)数据集,其中 X 是一个 2D NumPy 数组,包含(x【₁】, x
最后,我们可以构建一个函数KRR _ 函数。该函数读取(X, , y ) ,并使用 10 重交叉验证来计算模型的准确性。作为误差度量,我们在这里使用均方根误差(RMSE),它测量实际的*f(x₁、x【₂】值和预测值之间的平均误差。如果你不熟悉交叉验证,我最近写了一篇文章,你可能会感兴趣。*
探索超参数空间
我们需要决定一组我们想要研究的超参数值,然后我们使用我们的 ML 模型来计算相应的 RMSE。最后,我们可以选择最优(α,γ)组合作为最小化 RMSE 的组合。
下图显示了相应的 RMSE 值。我们使用了可变的标记大小来更清楚地说明具有最佳 RMSE 的值。
作者图片
使用下面的代码,我们探索这个可能的超参数值的 10×10 网格,并且我们获得最小值RMSE = 0.3014at(α=10⁻·, γ=2.0 )。
因此,使用这些函数并获得最佳超参数的最终代码应该是:
结论
在这个简短的教程中,我们看到了如何实现和使用网格搜索来调整 ML 模型的超参数。我们已经生成了一个简单的 2D 数据集,并看到了如何优化具有两个超参数的 ML 模型的误差度量。
当我们有大量超参数时,网格搜索对于调整 ML 模型并不有效,然后应该转向其他方法。举个例子,我最近写了一篇关于如何使用遗传算法调优 ML 模型的文章。
我希望这篇教程是有用的,并且记住你可以在这个资源库中访问这篇教程中使用的完整代码和图形。
寻找好酒的格子
在大多数 ML 项目中,微调一个人的模型是至关重要的一步。即使有很好的数据清理和特征工程,选择不当的超参数也会阻止用户从其模型中获得最佳性能。在过去的项目中,我发现自己循环通过参数并打印出训练错误来挑选最好的,但我最近读了安德烈亚斯·c·müller&萨拉·圭多的用 Python 介绍机器学习。在本书中,作者解释并演示了 sklearn 自动搜索 ML 模型的给定超参数列表,然后挑选最佳组合的能力。我发现这个功能非常有用,所以我写了这篇文章,作为传递 Müller 和圭多的教导的一个快速演示。
我将使用来自 Kaggle 的红酒质量数据集,它的可用性得分为 8.8 分,不算太大(1599 行)。观察最上面的几行可以看出,这个数据集包含了红酒的各个方面,如 pH 值、残糖克数、酒精含量等…以及从 1 到 10 的葡萄酒总体质量分数。
由于我肯定不是品酒师,可能无法区分 7 分的葡萄酒和 8 分的葡萄酒,我将训练一个支持向量机来确定葡萄酒是否好喝。对我来说,“好”是指质量得分为 7 或更高的任何东西,所以我必须在数据中添加该特性。添加之后,我可以将数据拆分为独立变量和因变量,并创建我的培训/测试拆分。
既然数据的形式正确,我们就转向网格搜索。我做的第一件事是创建一个超参数列表,供网格搜索迭代。因为我使用的是支持向量分类器,所以我测试了 C 和 gamma 的不同值,它们分别是正则化参数和核系数。我输入模型、我的参数网格和交叉验证折叠 5 来创建 GridSearchCV()对象,然后像 sklearn 中的任何其他模型一样用。fit()方法。
现在模型已经拟合好了,是时候查看结果了。我可以使用。网格搜索对象的 cv_results_ attribute,然后将结果透视到热图中。这清楚地表明,该数据的最佳组合是归一化参数的高值和核系数的低值。
网格搜索对象还将最佳拟合参数存储在。最佳参数属性。顺便提一下,Müller 和圭多解释说,sklearn 总是将来自训练数据的项目存储在以下划线结尾的属性中(例如:“。best_params_ ","cv_results_ ")。我们可以使用 kwargs 将这些参数直接插入到我们希望拟合测试数据的模型中,如下文使用**SVC( * grid _ search . best _ params _)***所演示的。使用网格搜索中找到的最佳参数,该模型在测试集上获得 100%的分类准确率。
希望这是 sklearn 网格搜索能力的一个有用的演示。要了解更多关于这方面的信息和大量其他的 ML 主题,我推荐阅读*Python 机器学习入门。*感谢阅读,完整的脚本/数据可在这里找到。要获得所有中型文章的完整访问权限,请点击此处!
GridSearch:终极机器学习工具
什么是 GridSearch,为什么它如此重要?
GridSearch:终极机器学习工具。克里斯·利维拉尼在 Unsplash 上的照片
简而言之,机器学习
监督机器学习的目标是基于历史数据建立预测功能。该数据有独立(解释)变量和目标变量(您要预测的变量)。
一旦建立了预测模型,我们就在单独的测试数据集上测量它的误差。我们使用 KPI 来量化模型的误差,例如,回归环境中的均方误差(定量目标变量)或分类环境中的准确性(分类目标变量)。
误差最小的模型通常被选为最佳模型。然后,我们使用该模型通过输入解释变量来预测目标变量的值。
在本文中,我将深入研究 GridSearch。
机器学习的两种优化
GridSearch 是一个用于超参数调整的工具。如前所述,机器学习在实践中归结为将不同的模型相互比较,并试图找到最佳的工作模型。
除了选择正确的数据集,优化预测模型通常有两个方面:
- 优化最佳模型的选择
- 使用超参数调整优化模型拟合
现在让我们来研究一下这些,以解释网格搜索的必要性。
第一部分。优化最佳模型的选择
在一些数据集中,可能存在简单的线性关系,可以从解释变量预测目标变量。在其他数据集中,这些关系可能更复杂或高度非线性。
与此同时,许多模型存在。这包括从线性回归这样的简单模型,到深度神经网络这样非常复杂的模型。
关键是使用适合我们数据的模型。
例如,如果我们在一个非常复杂的任务上使用线性回归,那么这个模型将是无效的。但是如果我们在一个非常简单的任务上使用深度神经网络,这也将是不可执行的!
为了找到合适的机器学习模型,解决方案是将数据分成训练和测试数据,然后在训练数据上拟合许多模型,并在测试数据上测试每个模型。测试数据误差最小的模型将被保留。
来自 Scikit Learn 的监督模型列表的截图显示,有大量模型可供试用!
第二部分。使用超参数调整优化模型拟合
在选择了一个(或几个)性能良好的模型之后,第二个要优化的是模型的超参数。超参数就像模型训练阶段的配置。它们影响模型能学什么或不能学什么。
因此,调整超参数可以进一步降低测试数据集的误差。
每个模型的估计方法都不同,因此每个模型都有自己的超参数需要优化。
Scikit Learn 的 RandomForestClassifier 的文档摘录显示了许多参数,这些参数都会影响模型的最终精度。
彻底搜索最佳超参数的一种方法是使用名为 GridSearch 的工具。
什么是 GridSearch?
GridSearch 是我们在优化超参数时使用的一个优化工具。我们定义想要搜索的参数网格,并为我们的数据选择最佳的参数组合。
网格搜索中的“搜索”
假设存在不同超参数值的特定组合,这将最小化我们的预测模型的误差。我们使用 GridSearch 的目标是找到这种特定的参数组合。
Grid search 中的“网格”
GridSearch 寻找最佳参数组合的想法很简单:只需测试每个可能的参数组合并选择最佳的一个!
虽然不是每个组合都是可能的,因为对于一个连续的规模来说,会有无限多的组合要测试。解决方案是定义一个网格。该网格为每个超参数定义了应该测试的值。
两个超参数α和β的 GridSearch 示意图(图片由作者提供)
在一个例子中,调整了两个超参数α和β:我们可以给它们两个值[0.1,0.01,0.001,0.0001],得到下面的“网格”值。在每个交叉点,我们的 GridSearch 将拟合模型,以查看该点的误差。
在检查了所有的网格点后,我们知道哪个参数组合最适合我们的预测。
网格搜索中的“交叉验证”
此时,只剩下一件事需要添加:交叉验证错误。
当使用每个超参数组合测试模型的性能时,可能存在过度拟合的风险。这意味着仅仅出于偶然,只有训练数据集很好地符合这个特定的超参数组合!在新的真实数据上的表现可能更差!
为了更可靠地估计超参数组合的性能,我们采用交叉验证误差。
交叉验证的示意图(作者提供的图片)
在交叉验证中,数据被分成多个部分。例如 5 份。然后模型被拟合 5 次,同时遗漏五分之一的数据。这五分之一被遗漏的数据用于测量性能。
对于超参数值的一个组合,5 个误差的平均值构成交叉验证误差。这使得最终组合的选择更加可靠。
是什么让 GridSearch 如此重要?
GridSearch 允许我们非常容易地找到给定数据集的最佳模型。通过自动搜索,它实际上使机器学习成为数据科学家角色的一部分变得更加容易。
在机器学习方面,仍然需要做的一些事情是决定测量误差的正确方法,决定尝试哪些模型和测试哪些超参数。最重要的部分,数据准备工作,也留给了数据科学家。
由于网格搜索方法,数据科学家可以专注于数据争论工作,同时自动化模型比较的重复任务。这使得工作更有趣,并允许数据科学家在最需要的地方增加价值:处理数据。
GridSearch 有许多替代方法,包括随机搜索、贝叶斯优化、遗传算法等等。我很快会写一篇关于这些的文章,所以不要犹豫,敬请期待。感谢阅读!
Pandas 和 SQL 中的分组依据
聚合函数的比较
法国艺术家 Paulo Grangeon 的 1600 只熊猫+世界巡演。保罗·格兰金的作品|由 ARR 策划。参考资料中的艺术品网站链接
数据分析的一个重要组成部分是通过计算总和、最大值、最小值、平均值、中值等聚合来生成摘要。这样,我们可以对数据的总体情况有所了解。这对于大型数据集尤其重要,因为查看原始数据可能不会产生任何有意义的见解。
在这篇博文中,我将首先描述一下Group By
的下划线机制,然后比较一下Group By
概念在 Python 的pandas
库和 SQL 中的实现。
什么是分组依据?
正如pandas
开发团队在他们的文档中对 GroupBy object ,Group By
包含三个步骤:
- 步骤 1: 根据一些标准将数据分成组
- 步骤 2: 独立地对每个组应用一个函数
- 步骤 3: 将结果组合成一个数据结构
在分析数据框的上下文中,步骤 1 相当于找到一列,并使用该列的唯一值将数据框分成多个组。第 2 步是选择一个函数,如聚合、转换或过滤。所选功能将在每个单独的组上运行。步骤 2 的结果将被合并,并在步骤 3 中显示为新的数据结构。
以上所有听起来可能仍然相当抽象;让我们使用来自泰坦尼克号追逐赛的真实数据集来尝试一下。(注意,有三个数据集可用,我将在这篇博文中使用“train.csv”。)
在熊猫中分组
Python pandas
库有一个名为groupby
的高效操作来执行 Group By 任务。
在泰坦尼克号的数据集中,有一个名为“登船”的列,为每个乘客提供登船港口的信息。有三个不同的值:C、Q 和 S (C =瑟堡,Q =皇后镇,S =南安普顿)。如果我们想检查每个港口的乘客数量,我们可以使用以下命令:
train.groupby('Embarked')['Embarked'].count()
它给出以下输出:
让我们使用分割-应用-合并步骤对此进行一些分解。
第 1 步选择列“apollowed ”,使用它的唯一值——即 C、Q、S——将数据集分成三类。
请注意,在此阶段,没有对组执行任何操作。命令train.groupby('Embarked')
仅仅输出一个GroupBy
对象:
步骤 2 是选择count()
方法作为我们的函数,它产生每个类别的总数。
第三步是组合并显示结果。pandas
GroupBy 对象支持列索引,我们可以指定希望在聚合结果中看到哪些列。在我们的例子中,我们只关心“已装船”这一列。如果没有此规范,pandas
将返回所有数字列的汇总结果,如下所示:
SQL 中的分组依据
Group By 概念是 SQL 语法的重要组成部分。尽管存在一些语法差异,SQL 的GOURP BY
语句的工作方式与pandas
相似。
上述pandas
操作可以用 SQL 实现如下:
SELECT Embarked, COUNT(Embarked)
FROM titanic.train
GROUP BY Embarked;
在GROUP BY
语句中,选择列“apollowed”来分割数据集。在SELECT
语句的第二部分,选择COUNT()
作为聚合函数。SELECT
语句还包含我们希望在输出中显示哪些列的信息。第一次出现“已装船”相当于pandas
列索引[Embarked]
。另一个微小的区别是,SQL 使用FROM
语句来指定我们正在处理的数据集,即来自“titanic”模式的“train”表;而在pandas
中,我们将数据帧的名称放在groupby
命令的开头。
同样值得注意的是,SQL 在使用GROUP BY
时会显示缺失值。从 SQL 输出中,我们可以看到两个乘客错过了登机港口。
高级示例
示例 1
如果我们想查看幸存与死亡乘客的平均票价,我们可以在pandas
中使用以下命令:
train.groupby('Survived')['Fare'].mean()
它给出了以下输出:
类似地,我们可以在 SQL 中通过以下方式实现这一点:
SELECT Survived, AVG(Fare)
FROM titanic.train
GROUP BY Survived;
正如我们所看到的,幸存的人比死去的人平均支付了更高的费用。
示例 2
泰坦尼克号上有三种乘客等级,1 级、2 级和 3 级。假设我们想要检查每一个职业的幸存人数与死亡人数的百分比,我们可以结合使用下面的groupby
命令和pandas
中的.apply()
方法:
pclass = train.groupby(['Pclass','Survived']).agg({'Pclass':'count'}) pclass_pcts = pclass.groupby(level=0).apply(
lambda x: 100 * x / float(x.sum()))
注意,在.apply()
方法中,我们可以传递一个lambda
函数来计算百分比。
这也可以在 SQL 中使用窗口函数来实现:
WITH t1 AS
(SELECT Pclass, Survived, Count(*) AS n
FROM titanic.train
GROUP BY Pclass, Survived)
SELECT Pclass,
Survived,
100 * n / (SUM(n) OVER (PARTITION BY Pclass)) AS percent
FROM t1;
我们可以得出结论,存活率随着乘客等级的降低而降低。
我的博文到此结束。Group By 在pandas
和 SQL 中的实现非常通用,可以和很多其他函数结合使用,值得进一步探索。
感谢您的阅读。一如既往,如果您有任何意见或反馈,我很乐意收到您的来信。
参考
- 杰克·范德普拉斯的《Python 数据科学手册》
- SQL 窗口函数
- 按对象分组的熊猫文档
- 保罗·格兰金的 1600 只熊猫+世界巡演
使用高斯混合模型(EM 算法)对相似图像进行分组
为图像聚类问题从头实现 GMM
聚类最流行的无监督机器学习问题之一。我们已经熟悉了 k-means 聚类算法,但是这里有一个问题:
- 高度依赖于质心的初始值:如果我们改变它的初始化,然后改变集群的位置,那么最终的集群很可能会改变它的位置。
- 聚类具有不同的大小和密度:具有不同形状(分布的数据点)和大小(数据点的数量)的聚类不能通过这种简单的技术来处理。
- 离群点:可能出现一个数据点离任何一个聚类都很远,它可以作为一个单独的聚类。为了避免这样的问题,我们通常会删除这些点。
如果你不想理解这个令人讨厌的数学,那么请向下滚动到 GMM 的实现部分,在那里我将讨论 python 中的实现。
为了理解高斯混合模型,你需要理解一些术语
- 密度估计:概率密度估计是基于不可观察的潜在概率密度函数的观察数据的近似构造。这一步包括选择概率分布函数和描述观测数据的最佳联合概率分布的函数参数。
- 最大似然估计(MLE):是一种通过最大化对数似然函数来估计概率分布参数的方法。
- 潜在变量:与观察变量相反,即不是直接观察到的,而是从数学模型中推断出来的。我们以后再谈。
现在最大似然估计在潜在变量存在的情况下不太适用。期望最大化算法是一种在潜在变量存在的情况下可以找到合适的模型参数的方法。
EM 是一种寻找参数的最大似然或最大后验估计的迭代方法,其中模型依赖于未观察到的潜在变量。
让我们稍微简化一下。
给定 N 个观察值
和概率模型 p(X |θ)
我们的目标是找到使 p(X |θ)最大化的θ值,这被称为最大似然估计。
如果模型是简单的高斯分布,那么参数就像
在哪里
如果是这种情况,在 ML 中解决这个问题的最常见方法是梯度下降,通过最小化作为损失函数的对数似然函数-log(p(X |θ))(记住我们实际上最大化参数的概率函数
是数值稳定性的一种简单方法,因为它将乘积变为和,并且该函数的负值的最小化与该函数的最大化是相同的)。
但是梯度下降是一种解决非线性问题的方法,典型的是针对一些多维分布的最小值。计算高斯混合的均值(或其他矩)不是非线性问题,因此不需要为非线性问题设计的方法。那么有没有更好的方法来解决这个问题。
EM 算法来了。让我们看看 EM 算法是如何在高斯混合模型中使用的。
通过引入潜在变量,极大似然估计可以得到简化。潜在变量模型假设观测 xi 是由一些潜在的潜在变量引起的。嗯……还是不清楚,好吧,考虑一下这张图片
这种类型的概率模型被称为高斯混合模型(GMM),在本例中 C 个高斯分量的加权和为 C = 3
πc 和σc 分别是混合分量 Cc 的权重、均值向量和协方差矩阵。权重是非负的,总和为 1,即
参数向量
表示所有模型参数的集合。如果我们引入一个离散的潜在变量‘t ’,它决定了对混合物成分的观察值的分配,我们可以用条件分布 p(x| t,θ)和先验分布 p( t | θ)来定义观察变量和潜在变量 p(x,t| θ)的联合分布
在哪里
t 的值是一位热编码的。例如,t2=1 指的是第二个混合成分,这意味着如果总共有 C=3 个成分,则 t = (0,1,0)。边际分布 p (x | θ)是通过对 t 的所有可能状态求和得到的。
对于每个观察 xi,我们有一个潜在变量 ti,也称为责任。
考虑到 X 是所有观测值的集合,所有潜在变量 T 的集合,那么我们可以很容易地最大化完全数据的对数似然 p(X,T | θ)。因为找到对数似然后,我们就知道点的聚类分配,这就是为什么我们会找到边际对数似然或不完全数据对数似然 p (X | θ)。所以从数学上来说,
GMM 的实施
让我们一步一步地看看如何使用高斯混合模型来聚类我们的图像。我在这里使用 python 来实现 GMM 模型:
需要外部 Python 库:
- imageio:用于从图像中获取 RGB 特征
- 熊猫:用于处理数据集
- numpy:用于数学运算
第一步:
让我们从我们的数据集开始,我们已经给出了所有图像驻留的文件夹路径。我们将创建一个方法,从中我们可以提取每个图像的 R,G,B 向量的平均值。对于单个图像,函数应该是:
######## loading external package dependency ####################Import pandas as pdImport numpy as npfrom scipy.stats import multivariate_normalImport imageioFrom functools import reducedef get_image_feature(path): Im = imageio.imread(os.path.join(path), pilmode=’RGB’) temp = Im/255\. # divide by 255 to get in fraction mn = temp.sum(axis=0).sum(axis=0)/(temp.shape[0]*temp.shape[1]) return mn/np.linalg.norm(mn, ord=None) # taking 2nd norm to scale vector
假设提供的图像路径是’(some_path)/Image_SunSet.jpg '这个函数返回[红、绿、蓝]图像像素的强度像[0.9144867979,0.3184891297,0.2495567485]我们可以清楚地看到红色具有更大的强度。
类似地,我们可以对所有图像执行此操作,并创建一个包含图像细节的熊猫数据帧:
- 文件名:文件的名称
- 路径:文件的路径
- 扩展:图像扩展。jpg,。巴布亚新几内亚经济贸易委员会
- ImageBase64:图像的 Base64 编码
- 红色:红色平均强度
- 绿色:绿色平均强度
- 蓝色:蓝色平均强度
现在,我们的数据集包含特征(即数据集的[红、绿、蓝]列,而不是仅用于查看的所有其余数据)。如果你想得到更高 k 值(聚类数),你可以从图像中提取自己的特征,但要代表更广泛的颜色代码,不仅仅是 r、g 和 b。
第二步:
模型生成(实现高斯混合模型的 EM 算法):
因为在 GMM,我们假设所有的数据点都来自 k 高斯分布,其中 k 只不过是一些集群。嗯……有点棘手,让我们简化一下。
考虑这个 1D 概率分布。如果我们假设所有数据点都来自 2 高斯分布,这意味着我们假设 k=2(聚类数),那么如果我们想知道哪些数据来自哪个高斯分布,我们需要做的就是计算两个高斯分布的均值和方差。但是等等,我们还不知道。但有一秒钟,如果我们假设我们知道这些参数(即 k 高斯分布的均值和方差),那么我们就可以知道每个数据点来自哪个高斯的可能性(这个可能性指的是责任)。哇,但是我们也不知道这个。所以我们的结论是
如果我们知道参数(即高斯的均值和方差),我们可以计算责任,如果我们知道责任,那么我们可以计算参数。
这就是 EM 算法的由来,它首先将“k”个高斯随机放入图像中(为高斯生成随机平均值和方差),然后在 E 和 M 步之间迭代,直到收敛,其中:
紧急步骤:根据当前参数分配集群责任
M 步:更新参数,给定当前集群职责
下面更详细的说一下它的实现。
让我们记住为什么 GMM 被用来。与 k- mean 不同的是,它不会对聚类进行硬分配,例如图像
如果我们使用 k=4,即 4 个集群模型,即日落、森林、云和天空,那么在 k 均值算法中,这张图片只属于云,这称为硬分配,但在 GMM,它属于 97%的云和 3%的森林,这称为软分配。
E 步骤:
在这一步中,我们计算集群责任。让 rik 代表聚类 k 对于数据点 I 的责任。您可以将责任假定为数据点‘I’属于‘k’聚类的概率。因为这种可能性意味着
为了知道一个聚类对一个给定的数据点有多大影响,我们计算该数据点在特定的聚类分配下的可能性,乘以该聚类的权重。对于数据点 I 和聚类 k,数学上:
其中 N (xi | μk,σk)是聚类 k 的高斯分布(具有均值μk 和协方差σk)。
我们用∝,是因为量 N (xi | μk,σk)还不是我们想要的责任。为了确保每个数据点上的所有责任加起来等于 1,我们在分母中添加了归一化常数:
但是我们的数据是三维向量,即[红、绿、蓝]。Scipy 提供了计算多元正态分布的便捷函数。检查这个
也就是 multivariate _ normal . pdf([数据点],mean =[均值向量],cov =[协方差矩阵])
我们后面会讲到如何计算 M 步的均值向量和协方差矩阵。
# data (numpy array) : array of observations
# weights (numpy array) : numpy array of weight of each clusters of size (1, n_clusters)
#means (numpy array) : numpy array of means of each clusters of size (n_cluster, dimension)
#covariances(numpy array) : numpy array of covariance metrix of size (n_clusters, dimension, dimension)
def get_responsibilities( data, weights, means, covariances):
n_data = len(data)
n_clusters = len(means)
resp = np.zeros((n_data, n_clusters))
for i in range(n_data):
for k in range(n_clusters):
resp[i, k] = weights[k]* multivariate_normal.pdf(data[i],means[k],covariances[k],allow_singular=True)
# Add up responsibilities over each data point and normalize
row_sums = resp.sum(axis=1)[:, np.newaxis]
resp = resp / row_sums
return resp
记住这个责任是我们潜在的变量。
M 步:
现在计算了聚类责任,我们必须更新与每个聚类相关的聚类参数,即(权重(πk)、均值(μk)和协方差(σk))。
更新权重:
聚类权重给出了每个聚类代表所有数据点的多少。数学上它被定义为:
python 代码
# resp(numpy array) : responsibility numpy array size (n_sample, n_clusters)
def get_soft_counts(resp):
return np.sum(resp, axis=0)# counts (numpy array) : count list of sum of soft counts for all clusters of size (n_cluster)
def get_weights(counts):
n_clusters = len(counts)
sum_count = np.sum(counts)
weights = np.array(list(map(lambda k : counts[k]/sum_count, range(n_clusters))))
return weights
更新的意思是:
每个分类的平均值等于所有数据点的加权平均值,由分类责任进行加权。数学上,对于每个数据点,第 k 个集群的 xi 和责任 rik 是第 k 个集群的平均值,可以定义为:
python 代码
# data (numpy array): array of observation points
# resp (numpy array) : responsibility numpy array size (n_sample, n_clusters)
# counts (numpy array) : count list of sum of soft counts for all clusters of size (n_cluster)
def _get_means( data, resp, counts):
n_clusters = len(counts)
n_data = len(data)
means = np.zeros((n_clusters, len(data[0])))
for k in range(n_clusters):
weighted_sum = reduce(lambda x,i : x + resp[i,k]*data[i], range(n_data), 0.0)
means[k] = weighted_sum/counts[k]
return means
更新协方差:
每个集群的协方差等于由集群责任加权的所有外部产品的加权平均值。数学上它被定义为:
在哪里
是外积。让我们举一个简单的例子,看看外部产品是什么样子的。
让
是两个向量,那么 a 和 b 的外积定义为:
转置只是为了把列向量变成行向量。因为按照惯例,向量在机器学习中表示为列向量。
python 代码
# data (numpy array) : array of observation points
# resp(numpy array) : responsibility numpy array size (n_sample, n_clusters)
# counts (numpy array) : count list of sum of soft counts for all clusters of size (n_cluster)
# means (numpy array) : numpy array of means of each clusters of size (n_cluster, dimension)
def _get_covariances( data, resp, counts, means):
n_clusters = len(counts)
dimension = len(data[0]) # to get dimention of data
n_data = len(data)
covariances = np.zeros((n_clusters, dimension, dimension))
for k in range(n_clusters):
weighted_sum = reduce (lambda x, i :x + resp[i,k] * np.outer((data[i]-means[k]), (data[i]- means[k]).T), range(n_data), np.zeros((dimension, dimension)))
covariances[k] = weighted_sum /counts[k] # normalize by total sum of counts
return covariances
而是我们将如何度量我们的混合高斯模型。为此,我们将计算高斯混合的对数似然。它量化了在特定参数(即均值、协方差和权重)下观察到一组给定数据的概率。我们将继续使用不同的参数集迭代 EM 算法,并检查其收敛性,即对数似然不会改变太多,或者直到固定的迭代次数。
Python 代码
# Z (numpy array) : numpy array of size (1, n_clusters)
def _ln_sum_exp( Z):
return np.log(np.sum(np.exp(Z)))# data (numpy array) : array of observation points
# weights (numpy array) : numpy array of weight of each clusters ofsize (1, n_clusters)
# means (numpy array) : numpy array of means of each clusters of size (n_cluster, dimension)
# covs (numpy array) : numpy array of covariance metrix of size (n_clusters, dimension, dimension)
def get_log_likelihood( data, weights, means, covs):
n_clusters = len(means)
dimension = len(data[0])
sum_ln_exp = 0
for d in data:
Z = np.zeros(n_clusters)
for k in range(n_clusters):
# compute exponential term in multivariate_normal
delta = np.array(d) — means[k]
inv = np.linalg.inv(covs[k])
exponent_term = np.dot (delta.T, np.dot (inv, delta))
# Compute loglikelihood contribution for this data point and this cluster
Z[k] += np.log (weights[k])
det = np.linalg.det(covs[k])
Z[k] -= 1/2\. * (dimension * np.log (2*np.pi) + np.log (det) + exponent_term)
# Increment loglikelihood contribution of this data point across all clusters
sum_ln_exp += _ln_sum_exp(Z)
return sum_ln_exp
现在是 EM 算法的实际主要实现方法的时候了
Python 代码
# data (numpy array) : array of observation points
# init_weights (numpy array) : numpy array of initial weight of each clusters ofsize (1, n_clusters)
# init_means (numpy array) : numpy array of initial means of each clusters of size (n_cluster, dimension)
# init_covariances (numpy array) : numpy array of initial covariance metrix of size (n_clusters, dimension, dimension)
# maxiter (int) : maximum iteration to rum EM (optional)
# threshold (float) : maximum threshold to stop EM (optional)
def em_from_parameter(data, init_means, init_covariances, init_weights, maxiter=1000, thresh=1e-4):
# Make copies of initial parameters
means = init_means[:]
covariances = init_covariances[:]
weights = init_weights[:]
# Infer length of dataset
n_data = len(data)
# Infer number of cluster
n_clusters = len(means)
# Initialize some useful variables
resp = np.zeros((n_data, n_clusters), dtype=np.float64)
l1 = get_log_likelihood(data, weights, means, covariances)
l1_list = [l1]
for it in range(maxiter):
# E-step: calculate responsibilities
resp = get_responsibilities(data, weights, means, covariances)
# M-step calculate cluster parameter
counts = get_soft_counts(resp)
weights = _get_weights(counts)
means = _get_means(data, resp, counts)
covariances = _get_covariances(data, resp, counts, means)
l1_new = get_log_likelihood(data, weights, means, covariances)
l1_list.append(l1_new)
if abs(l1_new – l1) < thresh :
break
l1 = l1_new
param = {'weights': weights, 'means': means, 'covariances': covariances, 'loglikelihood': l1_list, 'responsibility': resp, 'Iterations': it}
return param
现在都准备好了,但是我们的初始参数是什么。我们有许多方法可以采用。
一种方法是相等地初始化所有聚类的权重,即每个聚类的(1/k ),其中 k 是聚类的总数。
1 个聚类的协方差矩阵的初始化是维数等于数据维数*数据维数的对角方阵(在这种情况下,数据维数是 3,即[r,g,b])
其中,σxy =是 x 和 y 之间的协方差
对于均值初始化来说,有点棘手。还记得 k 均值算法吗?我们使用这些算法的输出来初始化我们的 EM 均值参数。因为通过使用 k-均值算法,我们推动均值坐标到达最近的收敛点。因此,如果我们从这一点出发,EM 算法将花费更少的时间来收敛。但是有一个问题,我们将如何初始化 k 均值的初始质心。
嗯。对于这个问题,我们将采用 k mean++初始化策略,它选择 k 个彼此相距最远的质心。现在我们已经有了所有的三参数。
注意:完整的解决方案请看我的 git-hub 链接 PYTHONAPI 你会发现这个集群问题端到端的完整 Flask 代码,可以被任何客户端技术使用。我选择 Angular 8 作为客户端,其代码可在 ML_algorithmUI 上获得。
对于那些只想检查 EM 相关代码的人来说,链接是这个,它包含两个文件夹 Expectation _ Maximization 和 K_Mean,这两个文件夹分别包含相关代码。请浏览代码,如果您面临任何问题,请随时问我。
现在回到代码,这个程序的驱动代码
Python 代码
# k: number of clusters
# data: observations coming from distribution
# return final weights, means, covariances, list of loglikelihood till convergence and final responsibility of each observation and number of Iterations it takes till convergencedef em(k, data) :
dimension = len(data[0])
# mean co-ordinate of all clusters not recommended at all since it is a very bad method to initialized the mean points alternatively use the k-mean initializing method as discussed early
means = np.random.rand(k, dimension)
#### alternate better method to initialize mean points by kmean ####
## from KM import KM ## self created KM class available in my github repos
## check K_Mean folder in listed github repos path
km = KM()
means, dont_require = km.kmeans(data, k) # return centroids and cluster assignments
cov = np.diag(np.var(data, axis=0)) # get covariance of data initialize by diagonal metrix of covariance of data
covariances = np.array([cov] * k) # initiate covariance metrix with diagonal element is covariance of data
weights = np.array([1/ k] * k) # initialize equal weight to all clusters
return em_from_parameter(data, means, covariances, weights)
images_path = ['…/image2.jpg ‘,’…/image2.jpg ',… ] #图像路径列表……………………………………………………………………………………(1)
########### Data generation ################
data = []
For path in images_path:data.append(get_image_feature(path))data = np.array(data)
现在我们的数据已经准备好了,k 的值,即集群的数量,都是个人的选择。为简单起见,我取 k = 4 意味着我们假设数据的分布来自 4 高斯分布。既然我们的数据和 k 值已经准备好了,那么我们就准备创建一个模型。
####### create model #######parameters = em(k, data) # store returned parameters of model along with responsibilities
. . . . . . ………………………………………………………………………….(2)
这需要时间,别担心,但最终,你会得到你想要的。现在,如果您有新的观察数据,您可以找出软分配,因为我们已经获得了正确的集群参数。
Python 代码
# data (numpy array) : array of observation points
# weights (numpy array) : numpy array of weight of each clusters of size (1, n_clusters)
# means (numpy array) : numpy array of means of each clusters of size (n_cluster, dimension)
# covs (numpy array) : numpy array of covariance metrix of size (n_clusters, dimension, dimension)
def get_responsibilities(self, data, weights, means, covariances):
n_data = len(data)
n_clusters = len(means)
resp = np.zeros((n_data, n_clusters))for i in range(n_data):for k in range(n_clusters):resp[i, k] = weights[k]* multivariate_normal.pdf(data[i],means[k],covariances[k])# Add up responsibilities over each data point and normalizerow_sums = resp.sum(axis=1)[:, np.newaxis]resp = resp / row_sumsreturn resp
创建观察值的 numpy 数组,并使用 em 返回的参数调用该方法,如(2)所示,并将其传递给该函数,您将获得每个集群的软分配。例如,让你有一个带有路径的图像”…/…/test_example.jpg "
首先获取图像的[r,g,b ]向量,如(1)所示,您将得到类似于[0.9144867979,0.3184891297,0.2495567485 ]的内容,将这些数据以及返回的集群参数传递给 get _ responsibilities 函数。您将在参数变量中获得该值,即
**注意:**记住这个函数采用一个观察值数组,在这种情况下,我们只有一个图像,即一个观察值,所以首先将这个观察值转换成一个数组,然后调用这个函数。
data = np.array([ np.array([0.9144867979, 0.3184891297, 0.2495567485 ])])mean = parameters[‘means’]cov = parameters[‘covariances’]weights = parameters[‘weights’]resp = get_responsibilities(data, weights, mean, cov)
万岁,你得到了你的集群任务。我对一些截图做了一个基于网络的可视化结果。
我们来谈谈它是如何消除 k 均值的所有缺点的:
- 更好的初始化策略:因为我们使用 K mean++初始化策略,所以我们不太可能得到错误的集群结构。
- 变化的聚类密度:与 k 均值不同,它具有变化的聚类密度,这为我们提供了对具有各自聚类分配的观测值的概率估计。
- 离群点:离群点被考虑,因为它包含非常少的对其最近的集群的责任。
**结论:**最后我们可以说,任何具有原色集合的图像都可以用这种技术进行聚类。我们可以使用这种方法对任意数量的图像进行聚类。在这里,我只是举了一个简单的例子,主要颜色,但根据需要,特征生成可能会改变多种颜色的阴影。GMM 是最适合的情况下,你想要的概率图像分配到一个特定的集群。
现在是时候结束我的博客了,如果你真的对这段代码感兴趣,请访问我的 git-hub 链接,并随时贡献和提出改进建议。快乐学习…
链接和参考:
马丁·克拉瑟潜变量模型 Part-1 高斯混合模型与 EM 算法(2019 年 11 月 21 日):https://克拉瑟姆. github . io/2019/11/21/Latent-variable-models-Part-1/
ethen 8181:http://ethen 8181 . github . io/machine-learning/clustering/GMM/GMM . html
维基参考:https://en . Wikipedia . org/wiki/Expectation–maximization _ algorithm
谷歌开发者资源:https://Developers . Google . com/machine-learning/clustering/algorithm/advantage-visits