可视化深度神经网络的损失情况……但是我们能相信他们吗?
我们能相信深度神经网络的损失景观可视化吗?
Landscape from this website
简介
最近开发了一种方法来可视化深度神经网络的损失情况。我个人认为这是一个巨大的突破,然而,我对创建的可视化的有效性感到有点怀疑。今天,我将研究作者的可视化方法,并介绍一些我认为非常酷的其他方法。
方法
创建亏损景观的整个过程非常简单直接。
- 训练网络
- 创建随机方向
- 给固定重量加上不同的扰动量,看看损失值是如何变化的。
唯一需要注意的是这些随机方向是如何产生的。我们来看看作者的方法。
他们的方法被称为“过滤器标准化”,非常容易理解。(这里是链接到作者的代码)。基本上,对于四维张量如(64,3,3,3),我们将匹配关于第一维的范数,因此(64,1,1,1),在权重的范数和随机方向的范数之间。(用一个更简单的术语来说,我们可以将这理解为匹配权重和随机方向之间的比例)。
以上是运行作者代码时的部分结果。现在我们可以利用张量运算来简化整个过程。(我稍后会展示)
网络
绿球 →输入图像(64,64,3)
蓝色矩形 →卷积+ ReLU 激活
红色矩形 →软最大输出
对于这篇文章,我在 CIFAR 10 数据集上训练了三个九层完全卷积神经网络(如上所示)。无任何归一化、批量归一化和局部响应归一化。
并且从上面的图中我们可以看到,批量归一化的网络取得了最高的性能。
从现在起我将把每个网络称为如下的
正常:没有任何归一化层的网络
批量规范:具有批量归一化层的网络
局部规范:具有局部响应归一化层的网络
滤波归一化
上面的代码片段展示了如何使用张量运算进行过滤器标准化。
Normal, Batch Norm, Local Norm
Normal, Batch Norm, Local Norm — Log Scale
当我们使用滤波归一化方法来可视化损失景观时,我们可以看到每个景观看起来并没有太大的不同。只有在我们以对数比例显示景观的情况下,我们才能看到,事实上,局部响应归一化的景观要清晰得多。
当我们将这三幅图按原始比例叠加在一起时,我们可以看到它们看起来是多么相似。
滤波器正交化
上述方法只是作者方法的简单修改,我们从简单的高斯分布生成随机方向,但是通过 QR 分解我们使方向正交化。
Normal, Batch Norm, Local Norm
Normal, Batch Norm, Local Norm — Log Scale
当我们将不同维度的方向正交化时,我们可以立即看到创建的损失景观是如何彼此不同的。与作者的方法相比,我们可以看到三个网络之间的损耗情况有所不同。
正交权重投影
这与滤波器正交化基本相同,唯一的区别在于,对不同维度的收敛权重执行 ZCA 白化,而不是从高斯分布生成。
Normal, Batch Norm, Local Norm
Normal, Batch Norm, Local Norm — Log Scale
类似于滤波器正交化,我们可以看到生成的可视化之间的一些差异。
重量的主要方向
最后一种方法是在不同的维度之间,在它们的第一主方向上扰动权重。
Normal, Batch Norm, Local Norm
Normal, Batch Norm, Local Norm — Log Scale
我们可以清楚地看到所产生的损失情况之间的差异。
讨论
我写这篇文章的唯一原因是为了表明,根据我们使用的方向,创造的损失景观可以发生巨大的变化。因此,我们需要质疑生成的损失景观的有效性,它们是否真正反映了训练网络的特征。
代码
要访问创建可视化效果的代码,请点击此处。
要查看整篇博文的代码,请点击此处。
遗言
我不想做任何大胆的断言,但似乎不同的方向选择会产生不同的视觉效果。问题仍然存在,哪个方向是最‘正确’的?可有正确的,哪一个揭示了真相?此外,我想提一下名为“尖锐极小值可以推广到深度网络的论文,该论文表明已经收敛到尖锐极小值的深度神经网络可以很好地推广,并且该理论不适用于具有 ReLU 激活的网络。就像那篇论文如何证明我们的观察可以根据我们的定义而改变一样,我们应该致力于创造反映真理的定义。
还有更多研究要做,我很期待。如果你希望看到更多这样的帖子,请访问我的网站。
参考
- 李,h,徐,z,泰勒,g,斯图德,c,&戈尔茨坦,T. (2017)。可视化神经网络的损失景观。arXiv.org。2019 年 5 月 3 日检索,来自https://arxiv.org/abs/1712.09913
- tomgoldstein/loss-landscape。(2019).GitHub。检索于 2019 年 5 月 3 日,来自https://github.com/tomgoldstein/loss-landscape
- https://prateekvjoshi . com/2016/04/05/what-is-local-response-normalization-in-convolutionary-neural-networks/
可视化音乐表演
作为一名音乐家和数据科学家,我对可视化音乐表演的想法很感兴趣。在这篇文章中,我概述了如何可视化来自 MAESTRO 数据集的钢琴演奏录音。示例在整个帖子中提供。下面,我使用 Python 的 Mido、Seaborn 和 Pandas 包,一步一步地布置了在 MAESTRO 数据集中打开、清理和可视化钢琴演奏的说明和代码。文章最后展示了弗朗茨·李斯特的匈牙利狂想曲第 2 号和一段音乐录音,这样读者就可以通过视觉观看音乐的展开。还提供了用于创建可视化的完整 Python 脚本的链接。
Joint density plot of Johann Sebastian Bach’s Prelude and Fugue in D Minor, WTC I, BWV 851
数据
MAESTRO 数据集包含来自国际钢琴电子竞赛的超过 200 小时的钢琴演奏。比赛的参与者在雅马哈唱片播放器上表演,这是一种声学钢琴,也可以捕捉和播放乐器数字接口数据。MAESTRO 数据集包含来自参赛者表演的 MIDI 数据以及表演的音频记录。
MIDI 是一种允许数字乐器通过“信息”相互通信的协议。这些信息储存了关于用于回放的乐器类型、要演奏的音符、音符何时开始、音符何时结束等信息。
简介:用 Python 打开 MIDI 文件
要在 Python 中打开 MIDI 文件,请安装 mido 包。从 Mido 包中,导入 MidiFile。下面是使用 MidiFile 在 Python 中打开文件的示例代码。对于 MAESTRO 数据,这个过程将创建一个包含两个音轨的 MidiFile 对象。第一轨道包含关于演奏的元数据(例如,作品的拍号)。第二个轨道包含性能消息。
Joint density plot of Wolfgang Amadeus Mozart’s Sonata in D Major
阐述:管理数据
下面的步骤是我在这篇文章中用来处理数据以创建可视化效果的。
步骤 1: 分离第二个轨迹,然后遍历轨迹提取消息。结果应该是一个消息对象列表。迭代时,省略第一条和最后一条消息。第一个消息是节目消息,最后一个是指示轨道结束的元消息。这两条消息都没有提供关于所演奏的音符的数据。
步骤 2: 遍历消息列表,将消息转换为字符串。可以使用基本 python 中的 str()函数或 mido 包中的 format_as_string()方法完成到字符串的转换。
步骤 3: 使用将消息字符串分割成包含键和值的子字符串。拆分(“”)。
这将创建一个列表列表。单个列表如下所示:
列表的第一个子字符串只包含一个值(消息类型)。为了将数据转换为字典,必须删除该子字符串。
**第四步:**删除第一个子串,存储在一个列表中,将列表转换为 dataframe。
步骤 5: 将列表中的其他子字符串转换成字典,然后转换成 dataframe。=分隔子字符串中的键和值。下面的代码遍历列表和每个子字符串来创建键值对。然后,它将这些键值对存储在一个字典列表中(每个子字符串列表一个字典)。最后,它将字典转换成数据帧。
步骤 6: 将属性数据帧(df2)与包含消息类型的数据帧(df1)连接起来。
步骤 7: 数据帧中的时间变量表示自最后一条消息以来经过的时间。音符变量表示弹奏的钢琴键。为了绘制数据,需要测量经过的时间来绘制随时间变化的数据。可以使用 Python 的创建 time_elapsed 变量。cumsum()方法。音符变量表示弹奏的钢琴键。绘制注释需要将变量从 string 类型转换为 float 类型。
步骤 8: 过滤掉控制消息和 note_off 消息,因为这些消息不会在可视化中使用。控制信息指示延音和弱音踏板何时被按下和释放。控制消息的消息类型等于“控制”。note_off 消息由速度为0 的 note_on 类型消息指示。
步骤 9: 从数据帧中删除不必要的列。
**步骤 10:**munging 过程的最后一步是在数据帧的开头和结尾添加一行。添加这些行将在可视化的边缘和数据点之间提供一些空间。新的第一行被分配一个音符值 0(钢琴较低音域之外的值)和一个等于最大经过时间值的-0.05 倍的经过时间值。(第一个 note_on 消息的 time_elapsed 值大于 0)。新的最后一行被分配了音符值 127(钢琴上限范围之外的值)和时间流逝值,该时间流逝值等于最大时间流逝值的 1.5 倍。添加这些行还可以生成边缘平滑的可视化效果。
Joint density plot of Edvard Grieg’s Lyric Piece in A Minor, “Waltz,” Op. 12 № 2
发展:可视化
为了可视化性能,我使用了 Python 的 Seaborn 包。x 轴代表经过的时间(时间从左到右增加),y 轴代表从低(下)到高(上)弹奏的音符的音高。这些图使用十六进制箱显示了一段时间内音符的联合密度。颜色用于传达十六进制 bin 中包含的音高和时间段范围内的音符频率。在上图中,较高的频率由较暗的绿色阴影表示。
关节密度图显示了十六进制 bin 定义的范围内的音高频率,以及这些频率如何随着演奏的进行而变化。结果是一个曲线图,该曲线图可视化了音高(y 轴)和音高频率(颜色)随时间(y 轴)的变化。该图包含了所有必要的信息,以及在图中可视化的作品的性能(见下面的匈牙利狂想曲示例演示)。
以下步骤分解了这篇文章中的可视化是如何绘制的。
步骤 1: 设置海风风格为“白色”。这一步提供了一个干净的白色背景,在此基础上建立可视化。
**第二步:**定义绘图,x 轴的界限和 y 轴的界限。这就是在 munging 过程的步骤 10 中添加第一行和最后一行有助于可视化的地方。x 轴限制由最短经过时间和最长经过时间定义。这两个数量都由附加行设置。y 轴限制设置为 16 和 113。这样做的原因是为了给图提供平滑的边缘。MIDI 音符值(绘制在 y 轴上)可以取 0 到 127 之间的值。然而,钢琴只有 88 个键,它们的 MIDI 音符值在 21 到 108 之间。第一行和最后一行的音符值被设定在钢琴的可能值范围之外。因此,通过在第一行和最后一行设定的最小和最大音符值内设定 y 轴限制,绘图:
- 显示钢琴的所有可能值,
- 不显示第一行和最后一行的人工音符值,
- 包括每个可视化的边缘与最低和最高音符之间的空间,并且
- 该空间的颜色与地块的背景颜色相同。
关于 plot 函数的另一个注意事项。gridsize 表示 x 方向上十六进制箱的数量。改变 gridsize 会改变 hex bins 的大小,从而改变绘图中 hexgons 的大小。里姆斯基·科萨科夫的《大黄蜂的飞行》的最终情节如下。
Joint density plot of Rimsky-Korsakov’s Flight of the Bumblebee (with labels and marginal plots)
**第三步:**去掉不需要的剧情元素。为了提供联合密度图的艺术再现,我移除了边缘图、轴和刻度标签以及轴。(注意:删除这些元素是提供有意义的可视化的最佳实践的对立面。)下面的代码删除了轴、边缘图、轴标签和刻度标签。
Joint density plot of Nikolai Rimsky-Korsakov’s Flight of the Bumblebee
重述:
这篇文章介绍了从 MAESTRO 数据集打开、清理和可视化 MIDI 演奏数据的步骤。提供了代码片段来演示这些步骤。使用 Seaborn 的 jointplot 方法可视化性能数据。这些图显示了一段时间内音高的密度(或者,弹奏音符的频率)。其结果是在一幅图像中捕捉一段音乐的艺术描绘。
对于那些有兴趣了解可视化如何与音乐作品的表现相结合的读者,下面是一个例子。可视化是弗朗茨·李斯特的*匈牙利狂想曲№ 2。*视频之后是 Adam Gyorgy 的表演视频。对于对创建自己的可视化感兴趣的读者,我的脚本和文档可以在 GitHub 上找到。
Joint density plot of Alban Berg’s Sonata Op. 1
尾声:匈牙利狂想曲№ 2
下面是弗朗茨·李斯特的《匈牙利狂想曲 2》的视频以及亚当·乔吉演奏该作品的视频。当你听视频的时候,通过视觉跟随表演。
Joint density plot of Liszt’s Hungarian Rhapsody № 2
文献学
柯蒂斯·霍桑、安德烈·斯塔舍克、Adam Roberts、伊恩·西蒙、黄承志、桑德·迪勒曼、埃里希·埃尔森、杰西·恩格尔和道格拉斯·埃克。"使用 MAESTRO 数据集实现因式分解的钢琴音乐建模和生成."在 2019 年国际学习代表大会上。
克拉克一世(2016 年 4 月 25 日)。回复:MIDI 规范【在线讨论组】。检索自https://www . midi . org/forum/228-writing-midi-software-send-note-off,-or-zero-velocity-note-on 。
使用 Folium 插件在交互式和动画地图上可视化纽约市自行车数据
Visualizing NYC Bike Data on interactive and animated maps with Folium plugins
当哪里的科学还不够
当分析交通和流动性时,在地图上画图总是很有帮助的。这有助于我们回答问题“事情在哪里发生?”
尽管如此,如果我们不考虑时间,很多时候分析的结果会发生巨大的变化。也许最明显的例子是在分析通勤模式时:如果你分析一整天,看起来去市中心的人和去相反方向的人大致相同。当你按一天中的时间划分时,你可以看到人们早上(去上班)和下午(回家)的移动方式有很大的不同。
在这种情况下,我们想要加深对特定情况的理解,我们需要在分析中添加第二个变量:时间。这将帮助我们在事情发生时回答*。*
在我们想要加深对特定情况的理解的情况下,我们需要在分析中加入第二个变量: 时间 。
友情提醒:请帮我鼓掌(或者很多!)当你读完之后如果觉得这篇文章有帮助。
你会在这篇文章中发现什么
在这篇文章中,我们与纽约自行车共享服务的公开可用数据争论,以介绍一些插件,它们可以帮助我们在 Jupyter 笔记本中用 Python 的叶子库制作动画、交互式地图。
我们不会关注数据分析工作流程或我们从不同的视觉效果中得到的结论。我们将尝试优化这里显示的代码,以获得我们正在寻找的动画地图。
我们会发现一些例子:
导入库
和往常一样,我们做的第一件事是导入我们将使用的 Python 库。
import numpy as np
import pandas as pd
import geopandas as gpd
import folium
import datetime
如果您是 Python 用户,您可能已经熟悉了前三个库。如果你开始使用 Python 并且想了解更多,我强烈推荐你阅读杰克·范德普拉斯的名著《Python 数据科学手册》,以获得完整的介绍。它是免费的,你可以直接从你的电脑上阅读。
第四行是导入leave,这是一个将 Leaflet.js 引入 Python 的库,让我们只用几行代码就能创建交互式地图。
在最后一行,我们导入了 datetime 库,这将使我们能够更轻松地处理时间序列。如果你有兴趣了解更多关于如何有效处理时间序列的知识,你可以阅读本书的这一部分。我发现它很有帮助。
导入和浏览数据
纽约花旗自行车共享服务的数据是公开的,可以在这里下载。在本笔记本中,我们使用了 2019 年 5 月月的数据。
将文件下载到工作目录后,运行以下脚本导入数据:
*# Import the file as a Pandas DataFrame*
fp = 'file.csv'
nyc = pd.read_csv(fp)
nyc.head()
数据看起来很完整。我们对每次旅行都有很多信息:
- **行程持续时间:**行程的持续时间,以秒为单位。
- **开始时间:**行程的开始日期和时间。
- **停止时间:**行程的结束日期和时间。
- **起点站 id、名称、纬度和经度:**识别和定位起点站的所有有用信息。纬度和经度值在 WGS84 投影(GPS 投影)中,这将使我们以后的工作更容易。
- **终点站 id、名称、纬度和经度:**识别和定位旅行终点站的所有有用信息。
- bikeid: 旅途中骑过的自行车的 id。
- **用户类型:**用户是订户(年卡)还是顾客(不同于年卡)。
- 出生年份
- **性别:**表示用户是女性(2)、男性(1)还是未知(0)
对这些变量的完整解释可以在花旗自行车的网页上找到。
转换
我们注意到变量开始时间和停止时间被作为文本(字符串)导入:
为了让 Pandas 充分发挥时间序列的功能,我们将做以下工作:
- 将这些变量的类型更改为日期时间。
- 将start time定义为数据帧的索引。这将使我们能够更容易地按一天中的时间对值进行透视(分组)。
- 创建一个名为**【类型】**的新列来帮助我们进行旋转。
*# Setting the right format for starttime and stoptime*
nyc['starttime'] = nyc['starttime'].str[:-5]
nyc['stoptime'] = nyc['stoptime'].str[:-5]
nyc['starttime'] = pd.to_datetime(nyc['starttime'])
nyc['stoptime'] = pd.to_datetime(nyc['stoptime'])
*# Define the startime as index and create a new column*
nyc = nyc.set_index('starttime')
nyc['type'] = 'station'
nyc.head(1)
这正是我们想要为每个可视化开始特定转换的格式。我们开始吧!
时间戳 GeoJson
这个插件允许我们在交互式叶子地图上绘制数据,并根据时间变量(年、月、日、小时等)制作动画,并且支持任何类型的几何图形(点、线串等)。你唯一要做的就是确保用 geojson 以正确的格式“输入”它。
为此,我们将开始转换数据框。首先,我们计算每个车站每小时的发车次数:
*# Aggregate number of trips for each start station by hour of the day*
start = nyc.pivot_table('tripduration',
index = ['start station id',
'start station latitude',
'start station longitude',
nyc.index.hour],
columns = 'type',
aggfunc='count').reset_index() start.head()start.head()
在上面的代码中,我们使用了 pandaspivot _ table()函数来帮助我们以任何我们想要的方式对数据进行分组。注意,我们使用的最后一个索引是**“NYC . index . hour”**。这将获取数据帧的索引,因为它是 datetime 格式,所以我们可以获得该值的小时,如上所示。我们可以类似地得到日子或月份。
尽管如此,我们得到的计数是整个月的。为了得到日平均值,我们将除以天数。
days = nyc.index.day.max()
start['station'] = start['station']/days
现在,为了方便起见,我们将更改列的名称,并定义我们希望地图上的点具有的颜色。
*# Rename the columns*
start.columns = ['station_id', 'lat', 'lon', 'hour', 'count']
*# Define the color*
start['fillColor'] = '#53c688'
*# The stops where less than one daily trip*
*# will have a different color*
start.loc[start['count']<1, 'fillColor'] = '#586065'
start.head(1)
在将数据框转换为我们需要的格式后,我们必须定义一个函数,从其中获取值,并使用正确的属性创建 geojson 以在插件中使用。
def create_geojson_features(df):
features = []
for _, row in df.iterrows():
feature = {
'type': 'Feature',
'geometry': {
'type':'Point',
'coordinates':[row['lon'],row['lat']]
},
'properties': {
'time': pd.to_datetime(row['hour'], unit='h').__str__(),
'style': {'color' : ''},
'icon': 'circle',
'iconstyle':{
'fillColor': row['fillColor'],
'fillOpacity': 0.8,
'stroke': 'true',
'radius': row['count'] + 5
}
}
}
features.append(feature)
return features
start_geojson[0]
让我们来看看函数:
- 它获取一个数据帧并遍历其行
- 它创建一个要素并将几何定义为一个点,从数据框中获取纬度和经度变量
- 它使用其他变量定义其余的属性:
- 它使用小时变量来创建一个时间属性。这是最重要的数据动画。
- 创建 fillColor 属性需要 fillColor
- 它将点的半径定义为计数变量的函数
一旦定义了函数,我们就可以在数据框中使用它并获得 geojson。
start_geojson = create_geojson_features(start)
start_geojson[0]
有了这个,我们现在可以创建我们的第一个互动和动画地图。
from folium.plugins import TimestampedGeoJson
nyc_map = folium.Map(location = [40.71958611647166, -74.0431174635887],
tiles = "CartoDB Positron",
zoom_start = 14)
TimestampedGeoJson(start_geojson,
period = 'PT1H',
duration = 'PT1M',
transition_time = 1000,
auto_play = True).add_to(nyc_map)
TimestampedGeoJson 插件的参数是:
- 有数据的 geojson
- **周期:**是动画从第一个值开始的时间步长。例如:’ P1M ’ 1/月,’ P1D ’ 1/天,’ PT1H ’ 1/小时,’ PT1M ’ 1/分钟。
- **持续时间:**时间过后,要素将在地图上显示的时间段。如果没有,将显示所有以前的时间。
结果看起来像这样:
双重地图
上面的地图看起来很酷,我们可以清楚地看到,在早高峰,许多行程从市中心周围的地方开始,而在下午,有大量的行程从地图中心的车站开始。
分析一天中不同时间的旅行终点可能是个好主意。更重要的是,并排查看两张地图并尝试识别一种模式可能会很有趣。
DualMap 插件可以帮助我们实现这一点。为了使用它,我们将首先运行一个与上面类似的脚本,以获得一天中每个时间在每个车站结束的车次。
nyc1 = nyc.reset_index().set_index('stoptime')
end = nyc1.pivot_table('tripduration',
index = ['end station id',
'end station latitude',
'end station longitude',
nyc1.index.hour],
columns = 'type',
aggfunc='count').reset_index()
end['station'] = end['station']/days
end.columns = ['station_id', 'lat', 'lon', 'hour', 'count']
end['fillColor'] = '#e64c4e'
end.loc[end['count']<1, 'fillColor'] = '#586065'
end_geojson = create_geojson_features(end)
在这之后,我们只需要创建我们的对偶地图。
from folium.plugins import DualMap
dualmap = DualMap(location = [40.71958611647166, -74.0431174635887],
tiles = 'cartodbpositron',
zoom_start = 14)
TimestampedGeoJson(start_geojson,
period = 'PT1H',
duration = 'PT1M',
transition_time = 1000,
auto_play = True).add_to(dualmap.m1)
TimestampedGeoJson(end_geojson,
period = 'PT1H',
duration = 'PT1M',
transition_time = 1000,
auto_play = True).add_to(dualmap.m2)
dualmap
请注意,我们首先创建对偶地图,然后将 start_geojson 添加到左侧地图(m1),将 end_geojson 添加到右侧地图(m2)。
正如我们所猜测的那样,在早上,大部分行程都在地图中间的车站结束(右边地图上的红色圆圈),而在下午,大部分行程都从那里开始(左边地图上的绿色圆圈),并分布在整个城市。有理由假设那里有一个大的中转站(也许是一个火车站),带着许多人去曼哈顿工作,穿过这座桥。
这是一个很好的例子,说明向空间数据添加时间变量可以帮助我们更好地理解某些情况。
蚂蚁路径
这个插件帮助我们在地图上制作路径动画。在这种情况下,我们没有每次旅行的确切路径,因此我们将创建从起点到目的地的线。
在开始处理我们的数据之前,让我们看看这个插件需要什么设置。从现场演示中我们可以看到设置包括权重(线条粗细)和延迟(动画的速度,延迟越高,动画越慢)。在我们的例子中,利用这些设置来更好地表示两个站点之间的活动水平是很有趣的。今后我们将牢记这一点。
从我们之前看到的地图来看,似乎在地图中间有一个很大的车站,可能值得单独分析。我先从筛选与之互动最多的前 6 个站开始。
df = nyc.pivot_table('tripduration',
index = ["start station name",
"end station name",],
columns = ['type'],
aggfunc='count').reset_index().sort_values(by='station', ascending=False)
df.head()
现在,我们可以将这前 6 个站点作为掩码,在接下来的步骤中过滤信息。
mask1 = df["start station name"].head(10)
mask2 = df["end station name"].head(10)
mask = mask1.append(mask2)
mask = mask.unique()
在用袖扣做了一个关联热图后,我发现最有趣的分析站是“Grove St PATH”(你可以去笔记本找这个的代码,因为这不是本文的目的)。
因此,格罗夫圣路径似乎是最有趣的那里,但我们也将切片我们的数据一点点,以了解时的事情发生。正如我们在盖有时间标记的 geojson 地图上看到的,早上最大的流量是流向Grove St PATH,而下午主要的流量是来自 Grove St PATH 的。在制作数据动画之前,将我们的数据分成这些时间段似乎会很有用。
*# Assign to each row a period of the day*
nyc['time_of_day'] = pd.cut(nyc.index.hour,
[0,6,10,16,20,24],
labels = ['am_valley', 'am_peak', 'mid_valley', 'pm_peak', 'pm_valley'],
right=False)
*# Create a different dataframe for each peak*
am = nyc.loc[nyc['time_of_day'] == 'am_peak', :]
pm = nyc.loc[nyc['time_of_day'] == 'pm_peak', :]
*# Filter the trips that end in Grove St PATH during the morning peak*
*# and the trips that start in Grove St PATH during the afternoon peak*
to_st_path = am.loc[(am['end station name'] == 'Grove St PATH') & (am['start station name'].isin(mask)), :]
from_st_path = pm.loc[(pm['start station name'] == 'Grove St PATH') & (pm['end station name'].isin(mask)), :]
现在我们已经根据需要过滤了信息,我们将转换数据框来创建插件需要的格式。我将在文章中展示将转换为 _st_path 数据帧的过程,但对 from_st_path 也是如此。你可以在这里下载整个笔记本的完整代码。
to_st_path = to_st_path.pivot_table('tripduration',
index = ['start station name',
'start station latitude',
'start station longitude',
'end station name',
'end station latitude',
'end station longitude'],
columns = ['type'],
aggfunc='count').reset_index().sort_values(by='station', ascending=False)
现在我们可以为每一行再创建两个变量:权重和延迟。都将是站的功能。
- 对于体重,我决定在 1 到 10 的范围内。站越高就是越重。
- 对于延迟,我决定在 100 到 800 的范围内进行。较高的工位是较低的工位(它将移动得更快)。
*# Define the weight*
to_st_path['weight'] = to_st_path['station']*10/(to_st_path['station'].max())
*# Get the needed values for interpolating*
a_to_st_path = (800-100)/(to_st_path['station'].min() - to_st_path['station'].max())
b_to_st_path = 100 - to_st_path['station'].max()*a_to_st_path
*# Define the delay interpolating*
to_st_path['delay'] = a_to_st_path*to_st_path['station'] + b_to_st_path
对另一个数据框运行相同的代码后,我们现在可以创建一个双地图,显示上午高峰时朝向 Grove St PATH 的活动和下午高峰时来自 Grove St PATH 的活动**。**
from folium.plugins import DualMap
dualmap = DualMap(location = [to_st_path.loc[0, 'end station latitude'], to_st_path.loc[0, 'end station longitude']],
tiles='cartodbpositron',
zoom_start=15)
to_st_path.apply(lambda row: folium.plugins.AntPath([(row['start station latitude'],
row['start station longitude']),
(row['end station latitude'],
row['end station longitude'])],
color='blue',
weight = row['weight'],
delay = row['delay']).add_to(dualmap.m1),
axis=1)
from_st_path.apply(lambda row: folium.plugins.AntPath([(row['start station latitude'],
row['start station longitude']),
(row['end station latitude'],
row['end station longitude'])],
color='blue',
weight = row['weight'],
delay = row['delay']).add_to(dualmap.m2),
axis=1)
dualmap
热图时间
根据这个插件的文档,它需要的输入是一个列表列表:
“数据([lat,lng]或[lat,lng,weight] 形式的点列表列表)-您想要绘制的点。外部列表按顺序对应不同的时间步长。(权重在(0,1)范围内,如果没有为点指定权重,则默认为 1)”
也就是说,我们需要为一天中的每个小时创建一个点列表,然后将所有这些列表放入一个新列表中。为此,我们创建了一个函数:
*# Create an empty list*
df_hour_list = []
*# Create a series with the different hours of the day*
hours = pd.Series(nyc.index.hour.unique().sort_values())
*# Create a list of points for each hour of the day*
def create_list(hour):
df_hour_list.append(nyc.loc[nyc.index.hour == hour,
['start station latitude',
'start station longitude']].
groupby(['start station latitude',
'start station longitude']).sum().reset_index().values.tolist())
hours.apply(create_list);
之后,我们可以很容易地创建时间热图。
from folium.plugins import HeatMapWithTime
*# Add trip events to the map*
map_time = folium.Map(location=location,
tiles="CartoDB Positron",
zoom_start=12)
HeatMapWithTime(df_hour_list,
auto_play=True,
max_opacity=0.5,
gradient = {0.2: '#FBD973',
0.4: '#fa782f',
0.75: '#F16578',
1: '#782890'}).add_to(map_time)
map_time
目前就这些了,希望你觉得有用。
如果你觉得它有帮助,请留下一些掌声来帮助我继续下去;)
感谢阅读!
参考
How to:follow for maps,heatmaps &时间分析作者 Dave Ian Hickey
用矩阵变换可视化主成分分析
理解特征值、特征向量和主分量的指南
主成分分析(PCA)是一种通过识别特征值和特征向量将数据分解成不相关成分的方法。以下内容旨在帮助您直观地了解这些不同的值代表什么以及它们是如何计算的。首先,我将展示如何使用矩阵来转换数据,然后在 PCA 中如何使用这些矩阵。
矩阵变换
对于以下各项,我将对这个圆和网格应用矩阵变换:
让我们使用这个作为我们的“数据”图像来帮助可视化每个转换发生了什么。图像上的点可以用原点位于圆心的[x,y]坐标来描述,我们可以使用 2D 变换矩阵*来变换这些点。*对于每个示例,我将用蓝色显示转换后的数据图像,用绿色显示原始数据图像。
缩放矩阵
比例矩阵是所有非对角元素为零的对角矩阵。
如果对角线元素小于 1,它会使数据图像在该方向变小。如果它大于 1,它会使数据图像在该方向变大。比如我们设vₓ= 1.2vᵧ= 0.6,就会变宽( vₓ > 1),变短( vᵧ < 1)。
旋转矩阵
旋转矩阵将围绕原点旋转数据一个角度 θ 而不改变其形状,如下所示:
这里我将图像旋转了正 20 度。注意它是逆时针旋转的。
剪切矩阵
剪切矩阵通过具有非零的非对角线元素来倾斜轴。
λ 越大,剪切力越大。这里,x 值被移动以产生剪切。
这里,y 值被移动。
对称矩阵
对称矩阵将基本上以相反的方向旋转 x 轴和 y 轴。
作为对称矩阵只要求每个非对角元素 i,j 与元素 j,i 相同。
非对角线元素的符号决定了歪斜的方向。而且,对角元素是相互独立的,不管它们的值是多少,矩阵仍然是对称的。
矩阵分解
对称矩阵的一个特性是它们可以分解成三个矩阵,其关系如下:
其中 Q 是正交矩阵( Q ⁻ = Qᵀ ),而 D 是对角矩阵。
注意旋转矩阵是正交的(r(θ)⁻=r(-θ)=r(θ)ᵀ),缩放矩阵是对角的。这意味着我们的对称矩阵实际上可以用旋转和缩放矩阵的组合来代替:
因此,数据图像的对称变换与旋转是一回事,沿 x 轴和 y 轴缩放,然后再旋转回来。这里我将使用三种变换(旋转、缩放、反旋转)来进行与上面对称例子相同的最终变换。
另一种思考方式是对称矩阵 A 与缩放矩阵 Sᵥ 相同,但它只是以相对于 x 轴和 y 轴的角度 θ 进行缩放。一个不是一个对角矩阵的唯一原因是它是一个相对于 x 轴和 y 轴缩放的度量。如果我们创建一组旋转了角度 θ 的新轴(如下所示),并制作一个相对于这些轴测量的缩放矩阵,这将是一个对角矩阵。
A 沿其缩放的这些轴是主分量轴。
在相当于 A 的对角缩放矩阵中,对角元素是数据沿主分量轴延伸的量。它们描述了数据的形状,告诉我们数据在不同的方向上是变长了还是变短了。那些对角线元素是特征值*。*
旋转矩阵包含一组给出主分量轴旋转的向量。这些向量就是特征向量。**
单个特征值及其对应的特征向量给出了一个主分量的幅度和方向。**
数据示例
现在让我们找出一组随机数据点的主成分。
让我们制作一些数据,使其以原点为中心(这是矩阵变换正确的必要条件),并从 x 轴倾斜 30°(因此,斜率为δy/δx*=tan30°)。*
一个协方差矩阵显示了数据集中两个矢量元素的协方差。如果两个向量元素一起变化,它们将具有更高的协方差。如果一个元素的变化完全独立于另一个元素,那么它们的协方差会变为零。数据中的斜率意味着 x 和 y 值不是独立的,因此它将具有非零的非对角线值。让我们看看数据的协方差矩阵。
注意这个矩阵是对称的。这是因为 i 和 j 元素的协方差与 j 和 i 元素的协方差相同(它们是相同的两个元素的协方差)。协方差矩阵 C 因此可以分解为:
V 是矩阵,其中每列是不同的特征向量, D 是特征值的对角矩阵。
既然我们知道我们旋转数据的角度,让我们计算我们期望得到的特征向量的值(列 V ):
接下来,我们来计算特征值。特征值将是沿着主分量轴的数据的方差。我们可以通过用特征向量对数据进行反旋转来测量这些值,然后找出 x 和 y 方向上的方差。
这里的数据是反旋转的:
x 和 y 方向的方差:
x 方向的方差最大,因此数据中的大部分信息都在该分量中。这是的第一个* 主成分。进一步的主成分基于它们的方差从最大到最小排序。*
既然我们已经知道会发生什么,让我们使用 scikit-learn 的 PCA 模块并将结果与我们的进行比较。首先,让我们打印出主成分。(该模块以行的形式返回特征向量,所以我将打印出转置,将它们放入如上所示的列中。)
注意这和我们计算的旋转矩阵相似。列是按重要性排序的特征向量,显示第一分量、第二分量等的方向。
接下来,让我们打印出解释的差异:
这些与我们计算的方差一致。同样,这些组件按从大到小的顺序排列。
查看组件
为了更清楚地理解独立的特征向量以及它们如何旋转数据,让我们使用它们来创建图像,以便直观地将主成分与数据进行比较。
一般来说,我们可以将一个特征向量乘以任意数,它会给出该点在主分量轴上的 x 和 y 值。让我们使用噪声的三个标准偏差作为该数字,它可以通过以下关系使用方差来计算
使用 3σ处的点可以很好地指示不同主分量的相对噪声。让我们创建一个值为[-3σ,3σ]的数组,对于我们的第一个主成分,它将是
然后,我们可以将数组乘以第一特征向量,这将给出这两个值沿第一主分量轴的 x 和 y 分量。
类似地,这可以通过改变所使用的指数来对其他主要成分进行。通过在每个主成分的 3σ点之间绘制一条线(通过简单的plt.plot(x_comp,y_comp)
使用 matplotlib),我们可以查看它们的噪声范围。
简而言之,两条红线表示两个主分量的方向。它们的旋转角度由特征向量计算,它们的长度由特征值确定,以显示沿轴的 3σ噪声范围。
降维
为了更好地理解特征值的重要性,让我们在一个降维的例子中使用它们。
如果想知道总方差中有多少是由不同的分量解释的,可以用每个特征值除以特征值的总和。
这意味着 94.6%的解释方差在第一部分。然而,有一种更快的方法可以得到解释方差的比率:
第二主成分占数据中方差信息的 5.4%,可能主要是噪声。如果要进行降维,可以去掉第二个主成分,只保留第一个主成分的信息。
首先,我将展示如何用特征向量来实现这一点,然后如何用 scikit-learn 轻松实现这一点。
如果我们首先从主分量与 x、y 轴对齐的旋转数据(上面的rotated_data
)开始,点的 x 分量实际上是第一主分量。我们可以通过使用第一个特征向量,将主分量旋转回原始方向,就像我们在上面绘制 3σ线一样。
这是first_component
和旋转后的数据一起绘制的。
这是与原始数据一起绘制的first_component_xy
。
数据点现在位于第一主成分轴上。
要使用 scikit-learn 获得第一个主成分,只需将成分数设置为 1 并转换数据。
数组first_component
和first_component_xy
将和上面显示的一样。
总结
与基本矩阵变换相比,主成分分析更容易理解。我们可以将协方差矩阵分解为旋转和缩放矩阵。
旋转矩阵:这些矩阵旋转数据而不改变其形状。类似地,特征向量用于将数据“旋转”到一个新的坐标系中,以便相关的特征与新的轴(主分量轴)对齐。
缩放矩阵:这些对角矩阵沿着不同的坐标轴缩放数据。类似地,特征值的对角矩阵给出了沿着不同主分量轴的数据方差(它们的尺度)的度量。
最后,降维与首先旋转具有要与主分量对齐的特征值的数据,然后仅使用具有最大特征值的分量是相同的。
使用 Seaborn 可视化统计图
这是关于可视化的第二篇文章。你可以阅读第一篇文章,在那里我讨论了使用 matplotlib 进行可视化的基础知识。
概括地说,可视化让我们看到数据是如何分布的,检测异常值,并让我们更有效地传达信息。这就是为什么数据科学家/分析师知道如何可视化以及他们有哪些可用选项非常重要。在本文中,我将介绍如何使用 seaborn 来可视化统计图表。
为此,我们将利用 seaborn 自身的一些数据集。这避免了我们必须下载和导入数据集的麻烦。您可以在这个链接中看到可用数据集的完整列表。
Photo by Luke Chesser on Unsplash
我们将从导入所需的库开始。
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns
接下来,让我们加载“虹膜”数据集,看看它。
iris = sns.load_dataset('iris')
iris.head()
Iris dataset
Iris 数据集包含三种不同物种的花的细节。由于这个数据集主要包含数字列,我们将使用它来探索适合数字数据的可视化。
所以我们能做的第一件事是检查任何一列的分布。
sns.set(style = 'darkgrid')
sns.distplot(iris['sepal_length'])
Fig 2
第一行允许您设置图形的样式,第二行构建分布图。这个“distplot”命令在同一个图形中构建直方图和 KDE 图。
但是我们可以选择定制上面的图表,甚至将它们分开。
sns.distplot(iris['sepal_length'], kde = False, bins = 30)
Fig 3
在这里,我们不仅删除了 KDE 图,而且还能够在图中添加更多的箱,以便更好地了解分布情况。这是分别通过“kde”和“bin”参数实现的。
KDE 图可以在 KDE plot 命令的帮助下单独查看。
sns.kdeplot(iris['sepal_length'])
Fig 4
使该图更好的一种方法是能够显示各种数据点的存在,以便更好地理解数据的分布。类似于直方图,但具有小的柱。这是在地毯图的帮助下完成的。
sns.kdeplot(iris['sepal_length'])
sns.rugplot(iris['sepal_length'])
Fig 5
上面的图称为地毯图,给出了点的分布和密度。
仅仅可视化一个变量就够了,如果你想看看两个变量是如何相对于彼此分布的呢?
假设我们想知道花的萼片和花瓣长度之间的关系。在这种情况下,我们可以使用 jointplot 来可视化它。
sns.jointplot(x = 'sepal_length', y = 'petal_length', data = iris)
Fig 6
上面的图不仅给出了联合分布,也给出了沿轴的个体分布。从上图可以看出,除了在较低端,两者之间几乎是线性关系。
joinplot 的一个很酷的特性是能够拟合数据的回归线。
sns.jointplot(x = 'sepal_length', y = 'petal_length', data = iris, kind = 'reg')
Fig 7
通过将’ reg '传递给 kind 参数,我们可以添加一条回归线,并将 kde 图添加到边上的直方图中。
最后,如果我们想一次性快速了解全部数据,我们可以使用 pairplot 函数。
sns.pairplot(iris)
Fig 8
唯一要传递的参数是数据集名称,它将自动为数据中出现的所有数字列构建上面的图。它将每一个数字列与其他数字列相对照,有助于快速浏览数据。
但是上面的图仍然缺少一列 species,这恰好是一个分类列。我们可以在此包含关于分类列的信息吗?
sns.pairplot(iris, hue = 'species', palette = 'magma')
Fig 9
在每个单独的图中,分类数据以颜色方案的形式表示。参数“色调”可用于传递此信息,而“调色板”控制绘图的颜色方案。
现在让我们来看看包含更多分类列的可视化数据。为此,我们将使用“提示”数据集。让我们把它装上来看一看。
tips = sns.load_dataset('tips')
tips.head()
Tips data
“小费”数据集包含就餐者支付的小费及其信息,如账单金额、性别、时间等。让我们从观察小费金额的分布及其与总账单金额的关系开始。
sns.distplot(tips['tip'], kde = False, bins = 30)
sns.jointplot(x = 'total_bill', y = 'tip', data = tips, kind = 'kde')
Fig 11
Fig 12
现在让我们看看给的小费是否每天都不同。这可以在箱线图的帮助下完成。
sns.boxplot(x = 'day',y = 'tip', data = tips, palette = 'coolwarm')
Fig 13
也许我们想通过用餐者的性别进行比较。像在 pairplot 中一样,我们也可以通过“色调”参数来指定
sns.boxplot(x = 'day',y = 'tip', data = tips, hue = 'sex', palette = 'coolwarm')
Fig 14
这根据性别为每天创建了两个箱线图。
另一种在小提琴的帮助下可视化上述数据的方法。
sns.violinplot(x = 'day',y = 'tip', data = tips, palette = 'rainbow')
Fig 15
小提琴图可以被认为是盒子图和 kde 图的组合。中间的粗线表示四分位数之间的范围,两边都是顶端的 kde。
类似于盒子情节,我们可以用‘性’来创造两个并排的小提琴情节来比较。但在这种情况下我们有更好的选择。
sns.violinplot(x = 'day',y = 'tip', data = tips, hue = 'sex', split = True, palette = 'rainbow')
Fig 16
通过将“split”指示为 true,我们能够在中心线的每一侧创建两个不同的 kde。这让我们可以并排看到差异,而不会排挤剧情。
之前我们看到,我们能够使用配对图来查看整个数据,并包括一个分类列。但是在这种情况下,我们有多个分类列。这些可以通过小平面网格整合到绘图中。
g = sns.FacetGrid(tips, col="time", row="smoker")
Fig 17
上面的命令创建了一个 2 x 2 的图来说明“时间”和“吸烟者”之间的四种可能性。对于用餐时间和用餐者是否吸烟的每种组合,都有一个曲线图。
在这些图中,我们可以添加数字数据,并比较它们在上述“时间”和“吸烟者”两列中的差异。
g = sns.FacetGrid(tips, col="time", row="smoker")
g = g.map(plt.hist, "tip")
Fig 18
我添加了小费值的分布,以观察它们在“时间”和“吸烟者”之间的差异。每个图的上方有一条线,指示它对应的“时间”和“吸烟者”值。
类似地,我们也可以绘制两个数字列,并查看两者之间以及“时间”和“吸烟者”之间的关系。
g = sns.FacetGrid(tips, col="time", row="smoker",hue='sex')
g = g.map(plt.scatter, "total_bill", "tip").add_legend()
Fig 18
上图显示了小费和账单金额之间的关系,以及“时间”和“吸烟者”之间的关系。此外,每个地块的配色方案也显示了性别。通过使用分面网格,我们可以创建包含多个数字和分类列的图。
最后,我想介绍一下热图和聚类图。
在下图中,我使用了热图来显示所有数值之间的相关性。
sns.heatmap(tips.corr(), cmap = 'coolwarm', annot = True)
Fig 19
命令“tips.corr”给出了所有数值变量之间的相关性。通过将“不能”表示为真,我们也可以在图上得到相关值。
为了构建聚类图,我首先透视小费数据集,以获得不同日期和性别的小费金额。
tip_pivot = tips.pivot_table(values='tip',index='day',columns='sex')
Pivoted table
利用这一点,我可以创建一个关于小费价值和日期之间距离的聚类图。
sns.clustermap(tip_pivot, cmap = 'coolwarm', standard_scale = 1)
Fig 21
因为只有两种性别,所以没有什么可看的。但是在星期几的例子中,我们看到星期六和星期四被组合在一起,表明这两天给小费的数量相似。
上面的聚类图非常简单,因为每个轴上没有很多类别。当有多个允许辨别相似性和模式的类别时,一个聚类图会非常出色。
这涵盖了一些可以使用 Seaborn 构建的统计图和可视化。希望你觉得有用。
你也可以通过 LinkedIn 与我联系。
使用 Matplotlib 和 Gym 绘制优雅的股票交易代理
我们将扩展我们在上一个教程中编写的代码,以使用 Matplotlib 呈现环境的可视化效果。如果你没有读过我的第一篇文章 从头开始创造定制的健身房环境 ,,你应该停下来先读一下。
如果你对matplotlib
库不熟悉,不用担心。我们将检查每一行,以便您可以创建自己的gym
环境的自定义可视化。一如既往,本教程的代码将在我的 Github 上提供。
下面是我们将在本文中创建的内容的预览:
如果看起来很复杂,其实也没那么糟糕。只有几个关于每个step
的图表更新,注释了一些关键信息。我们开始吧!
股票交易可视化
在上一个教程中,我们编写了一个简单的render
方法,使用print
语句显示代理的净值和其他重要指标。让我们将这个逻辑转移到一个名为_render_to_file
的新方法中,这样我们就可以在必要时将一个会话的交易指标保存到一个文件中。
***def*** _render_to_file(*self*, *filename*='render.txt'):
profit = self.net_worth - INITIAL_ACCOUNT_BALANCE
file = open(filename, 'a+') file.write(*f*'Step: {self.current_step}\n')
file.write(*f*'Balance: {self.balance}\n')
file.write(*f*'Shares held: {self.shares_held} (Total sold:
{self.total_shares_sold})\n')
file.write(*f*'Avg cost for held shares: {self.cost_basis} (Total
sales value: {self.total_sales_value})\n')
file.write(*f*'Net worth: {self.net_worth} (Max net worth:
{self.max_net_worth})\n')
file.write(*f*'Profit: {profit}\n\n') file.close()
现在,让我们继续创建新的render
方法。它将利用我们新的StockTradingGraph
类,我们还没有写。我们下一步会谈到这个。
***def*** render(*self*, *mode*='live', *title*=None, ***kwargs*):
# Render the environment to the screen **if** mode == 'file':
self._render_to_file(kwargs.get('filename', 'render.txt'))
**elif** mode == 'live':
**if** self.visualization == None:
self.visualization = StockTradingGraph(self.df, title)
**if** self.current_step > LOOKBACK_WINDOW_SIZE:
self.visualization.render(self.current_step, self.net_worth,
self.trades, *window_size*=LOOKBACK_WINDOW_SIZE)
我们在这里使用[kwargs](http://book.pythontips.com/en/latest/args_and_kwargs.html)
将可选的filename
和title
传递给StockTradingGraph
。如果你不熟悉kwargs
,它基本上是一个向函数传递可选关键字参数的字典。
我们还为可视化传递了self.trades
,但是还没有定义它,所以让我们开始吧。回到我们的_take_action
方法,无论何时我们买入或卖出股票,我们现在都要将交易的细节添加到self.trades
对象中,在我们的reset
方法中我们已经将它初始化为[]
。
**def** _take_action(self, action):
... **if** action_type < 1:
**...
**
**if** shares_bought > 0:
self.trades.append({'step': self.current_step,
'shares': shares_bought, 'total': additional_cost,
'type': "buy"}) **elif** action_type < 2:
**...** **if** shares_sold > 0:
self.trades.append({'step': self.current_step,
'shares': shares_sold, 'total': shares_sold * current_price,
'type': "sell"})
现在,我们的StockTradingGraph
已经拥有了呈现股票价格历史和交易量所需的所有信息,以及我们代理的净值和它所做的任何交易。让我们开始渲染我们的可视化。
首先,我们将定义我们的StockTradingGraph
和它的__init__
方法。在这里我们将创建我们的pyplot
人物,并设置每个要渲染的支线剧情。date2num
函数用于将日期重新格式化为时间戳,这在以后的呈现过程中是必要的。
**import** numpy **as** np
**import** matplotlib
**import** matplotlib.pyplot **as** plt
**import** matplotlib.dates **as** mdates***def*** date2num(*date*):
converter = mdates.strpdate2num('%Y-%m-%d')
**return** converter(date)***class*** **StockTradingGraph**:
"""A stock trading visualization using matplotlib made to render
OpenAI gym environments"""***def*** __init__(*self*, *df*, *title*=None):
self.df = df
self.net_worths = np.zeros(len(df['Date'])) # Create a figure on screen and set the title
fig = plt.figure()
fig.suptitle(title) # Create top subplot for net worth axis
self.net_worth_ax = plt.subplot2grid((6, 1), (0, 0), *rowspan*=2,
*colspan*=1)
# Create bottom subplot for shared price/volume axis
self.price_ax = plt.subplot2grid((6, 1), (2, 0), *rowspan*=8,
*colspan*=1, *sharex*=self.net_worth_ax) # Create a new axis for volume which shares its x-axis with
price
self.volume_ax = self.price_ax.twinx() # Add padding to make graph easier to view
plt.subplots_adjust(*left*=0.11, *bottom*=0.24, *right*=0.90,
*top*=0.90, *wspace*=0.2, *hspace*=0) # Show the graph without blocking the rest of the program
plt.show(*block*=False)
我们使用plt.subplot2grid(**...**)
方法首先在我们的数字顶部创建一个子图来呈现我们的净值网格,然后在它下面为我们的价格网格创建另一个子图。subplot2grid
的第一个参数是子情节的大小,第二个参数是在图中的位置。
为了呈现我们的交易量棒线,我们在self.price_ax
上调用twinx()
方法,这允许我们在顶部覆盖另一个共享相同 x 轴的网格。最后,也是最重要的,我们将使用plt.show(block=False)
将我们的图形渲染到屏幕上。如果您忘记通过block=False
,您将只能看到呈现的第一步,之后代理将被阻止继续。
接下来,我们来写我们的render
方法。这将从当前时间步骤中获取所有信息,并在屏幕上实时显示。
***def*** render(*self*, *current_step*, *net_worth*, *trades*, *window_size*=40):
self.net_worths[current_step] = net_worth window_start = max(current_step - window_size, 0)
step_range = range(window_start, current_step + 1) # Format dates as timestamps, necessary for candlestick graph
dates = np.array([date2num(x)
**for** x **in** self.df['Date'].values[step_range]])
self._render_net_worth(current_step, net_worth, window_size,
dates)
self._render_price(current_step, net_worth, dates, step_range)
self._render_volume(current_step, net_worth, dates, step_range)
self._render_trades(current_step, trades, step_range) # Format the date ticks to be more easily read
self.price_ax.set_xticklabels(self.df['Date'].values[step_range],
*rotation*=45, *horizontalalignment*='right') # Hide duplicate net worth date labels
plt.setp(self.net_worth_ax.get_xticklabels(), *visible*=False) # Necessary to view frames before they are unrendered
plt.pause(0.001)
在这里,我们保存net_worth
,然后从上到下渲染每个图形。我们还将使用代理在self.render_trades
方法中进行的交易来注释价格图表。这里调用plt.pause()
很重要,否则在最后一帧真正显示在屏幕上之前,下一次调用render
会清除每一帧。
现在,让我们看看图表的每一种呈现方法,从净值开始。
***def*** _render_net_worth(*self*, *current_step*, *net_worth*, *step_range*,
*dates*):
# Clear the frame rendered last step
self.net_worth_ax.clear() # Plot net worths
self.net_worth_ax.plot_date(dates, self.net_worths[step_range], '-
', *label*='Net Worth') # Show legend, which uses the label we defined for the plot above
self.net_worth_ax.legend()
legend = self.net_worth_ax.legend(*loc*=2, *ncol*=2, *prop*={'size': 8})
legend.get_frame().set_alpha(0.4) last_date = date2num(self.df['Date'].values[current_step])
last_net_worth = self.net_worths[current_step] # Annotate the current net worth on the net worth graph
self.net_worth_ax.annotate('{0*:.2f*}'.format(net_worth),
(last_date, last_net_worth),
*xytext*=(last_date, last_net_worth),
*bbox*=*dict*(*boxstyle*='round', *fc*='w', *ec*='k', *lw*=1),
*color*="black",
*fontsize*="small") # Add space above and below min/max net worth
self.net_worth_ax.set_ylim(
min(self.net_worths[np.nonzero(self.net_worths)]) / 1.25,
max(self.net_worths) * 1.25)
我们只是调用我们的净值支线剧情上的plot_date(**...**)
来绘制一个简单的线图,然后用代理当前的net_worth
进行注释,并添加一个图例。
价格图的呈现有点复杂。为了简单起见,我们将使用不同于音量条的方法来渲染 OHCL 条。首先,如果你还没有这个包,你需要pip install mpl_finance
,因为这个包是我们将要使用的蜡烛图所需要的。然后将这一行添加到文件的顶部。
**from** mpl_finance **import** candlestick_ochl **as** candlestick
很好,让我们清除前一帧,压缩 OHCL 数据,并向self.price_ax
子情节渲染一个蜡烛图。
***def*** _render_price(*self*, *current_step*, *net_worth*, *dates*, *step_range*):
self.price_ax.clear() # Format data for OHCL candlestick graph
candlesticks = zip(dates,
self.df['Open'].values[step_range],
self.df['Close'].values[step_range],
self.df['High'].values[step_range],
self.df['Low'].values[step_range]) # Plot price using candlestick graph from mpl_finance
candlestick(self.price_ax, candlesticks, *width*=1,
*colorup*=UP_COLOR, *colordown*=DOWN_COLOR) last_date = date2num(self.df['Date'].values[current_step])
last_close = self.df['Close'].values[current_step]
last_high = self.df['High'].values[current_step] # Print the current price to the price axis
self.price_ax.annotate('{0*:.2f*}'.format(last_close),
(last_date, last_close),
*xytext*=(last_date, last_high),
*bbox*=*dict*(*boxstyle*='round', *fc*='w', *ec*='k', *lw*=1),
*color*="black",
*fontsize*="small") # Shift price axis up to give volume chart space
ylim = self.price_ax.get_ylim()
self.price_ax.set_ylim(ylim[0] - (ylim[1] - ylim[0])
* VOLUME_CHART_HEIGHT, ylim[1])
我们用股票的当前价格对图表进行了注释,并向上移动图表以防止它与成交量棒线重叠。接下来让我们看看体积渲染方法,这是非常简单的,因为没有注释。
***def*** _render_volume(*self*, *current_step*, *net_worth*, *dates*,
*step_range*):
self.volume_ax.clear() volume = np.array(self.df['Volume'].values[step_range])
pos = self.df['Open'].values[step_range] - \
self.df['Close'].values[step_range] < 0
neg = self.df['Open'].values[step_range] - \
self.df['Close'].values[step_range] > 0 # Color volume bars based on price direction on that date
self.volume_ax.bar(dates[pos], volume[pos], *color*=UP_COLOR,
*alpha*=0.4, *width*=1, *align*='center')
self.volume_ax.bar(dates[neg], volume[neg], *color*=DOWN_COLOR,
*alpha*=0.4, *width*=1, *align*='center') # Cap volume axis height below price chart and hide ticks
self.volume_ax.set_ylim(0, max(volume) / VOLUME_CHART_HEIGHT)
self.volume_ax.yaxis.set_ticks([])
这只是一个简单的条形图,每个条形显示为绿色或红色,这取决于价格在该时间步长内是上升还是下降。
最后,让我们进入有趣的部分:_render_trades
。在这个方法中,我们将在价格图上绘制一个箭头,在这里代理进行了一笔交易,并标注了交易的总金额。
***def*** _render_trades(*self*, *current_step*, *trades*, *step_range*):
**for** trade **in** trades:
**if** trade['step'] **in** step_range:
date = date2num(self.df['Date'].values[trade['step']])
high = self.df['High'].values[trade['step']]
low = self.df['Low'].values[trade['step']] **if** trade['type'] == 'buy':
high_low = low
color = UP_TEXT_COLOR
**else**:
high_low = high
color = DOWN_TEXT_COLOR total = '{0*:.2f*}'.format(trade['total']) # Print the current price to the price axis
self.price_ax.annotate(*f*'${total}', (date, high_low),
*xytext*=(date, high_low),
*color*=color,
*fontsize*=8,
*arrowprops*=(*dict*(*color*=color)))
就是这样!我们现在有了一个在上一篇文章中创建的股票交易环境的漂亮的、实时的可视化效果!太糟糕了,我们仍然没有花太多的时间来教经纪人如何赚钱…我们下次再谈吧!
不算太寒酸!下周,我们将基于本教程的代码创建不会赔钱的比特币交易机器人。
让我们使用深度强化学习来制造有利可图的加密货币交易代理
towardsdatascience.com](/creating-bitcoin-trading-bots-that-dont-lose-money-2e7165fb0b29)
感谢阅读!一如既往,本教程的所有代码都可以在我的 Github 上找到。如果您有任何问题或反馈,请在下面留下评论,我很乐意收到您的来信!我也可以通过@notadamking 的Twitter联系到。
你也可以通过下面的链接在 Github 赞助商 或者Patreon上赞助我。
嗨,我是亚当。我是一名开发人员、作家和企业家,尤其对深度…
github.com](https://github.com/users/notadamking/sponsorship)
Github 赞助商目前正在 1:1 匹配所有捐款,最高可达 5000 美元!
嗨,我是亚当。我是一名开发人员、作家和企业家,尤其对深度…
patreon.com](https://patreon.com/notadamking)
可视化自杀率和世界幸福
两者有关系吗?
一般来说,一个幸福指数较高的国家的自杀率应该较低。然而,一个相当不为人知的事实是,挪威、芬兰等北欧国家的幸福指数更高,自杀率也更高。
通过这个项目,我们想调查这个异常现象,并通过可视化大量关于这个主题的数据来探索一个国家的自杀率和幸福指数之间是否有关系。我们想看看是否能找到这些矛盾存在的原因,并以历史事件或政府干预的形式揭示它们背后的故事。
初始阶段
背景研究
我们进行了一次文献回顾,了解到自杀背后的原因可能很多,而且非常复杂。我们很清楚,这个项目的目的不是讨论一个特定国家自杀率背后的原因,而是试图了解这些年来自杀率的变化,并确定可能导致这种变化的潜在因素。例如,如果它与全球趋势有关,如经济危机、历史事件或政府干预(后面会有更多这方面的例子)。
头脑风暴&素描
在文献回顾的基础上,我们讨论了一些可能的可视化方法,包括线图、线形图和条形图。这给了我们一个很好的起点来构思随之而来的设计元素。一次头脑风暴会议让我们迅速勾画出一些数据可视化的想法,以尽可能最有效的方式呈现我们的数据。
Multi-dimensional World Map & Interactive Timeline with Historical Event Hover
形象化
最终的可视化效果由 2 个仪表盘和它们自己的图形组成——自杀仪表盘和幸福仪表盘。
他们都是从这种想象开始的:
Average of World Happiness & Suicide 2015–2016
自杀仪表盘
从上面的第一张地图中选择一个国家后,接下来的三张图会相应地改变,以获得该国正在发生的事情的详细图片。
- 世界幸福感变化&2015-2016 年自杀率(散点图)
这显示了 2015 年至 2016 年世界幸福和自杀率的变化。深色代表 2016 年,浅色代表 2015 年。用户可以将鼠标悬停在线上,查看是哪个国家,并查看两个指标的值。
用户还可以过滤年份,这样它将只显示所选年份的值。选择两个年份后,用户可以看到两个参数的变化。通过散点图,我们可以很容易地看出没有负相关,这意味着更高的幸福分数并不能保证更低的自杀率,反之亦然。
Scatter plot with Switzerland selected; Scatter plot showing happiness score and suicide rate of the year 2015
2.各国自杀率&年龄(折线图)
为了更深入地了解这些年来自杀率的变化,我们制作了这个折线图。下图显示了从 1985 年到 2016 年的自杀率趋势。用户也可以选择国家,性别和年龄组进行过滤。一个有趣的发现是,从 1985 年到 2016 年,全球女性自杀率通常远低于男性自杀率。
3.各州自杀率(Choropleth)
这张图表显示了美国各州的平均自杀率。从图表中,我们可以看出蒙大拿和阿拉斯加的自杀率较高。用户也可以过滤选择年份。
幸福仪表板
从上面的第一张地图中选择一个国家后,接下来的两张图会相应地改变,以获得该国正在发生的详细情况。
- 2015-2016 年世界幸福指数(堆积条形图)
这个 viz 显示了 2015-2016 年幸福得分的七个属性的分布。根据《世界幸福报告》,衡量幸福得分的属性包括经济(人均 GDP)、家庭、健康(预期寿命)、慷慨、自由、对政府的信任和反乌托邦残余。该图表显示了每个属性的值,以及它们如何在给定的一年中对一个国家的幸福分数做出贡献。用户可以将鼠标悬停在每个条形上,以查看幸福分数的总值及其对每个属性的分解。用户还可以快速了解哪个国家的幸福指数最高。
2.2015-2016 年世界幸福指数(饼状图)
这些饼状图允许用户按年份比较构成某个国家幸福得分的七个属性的总体贡献。用户可以过滤年份并查看不同年份的值。这与前一个不同,因为它不是数值的平均值,而是给出了各年的清晰图像。
有趣的发现
- 没有证据表明幸福指数高的国家自杀率低,反之亦然。
- 自杀背后的原因不容易确定。每个国家都有不同的社会和文化背景,导致不同的自杀原因。
- 几乎每个国家的女性自杀率都比男性低得多。
- 芬兰是一个高幸福指数和高自杀率国家的典型例子。从数据中,我们发现芬兰的自杀率实际上自 1990 年达到顶峰后大幅下降。经过调查,我们发现这种改善的潜在原因可能是由于 20 世纪 80 年代末发起的全国自杀运动。这有助于提高对精神病治疗的认识和对康复的支持。[1]
Line Chart Showing the Trend of Suicide Rate for Denmark, Finland, Greece, Japan, Norway, South Korea, Sweden, and United States
5。希腊当前的经济危机激起了我们的兴趣,看看它是否对其自杀率有任何影响。然而,根据这个图像,与我们感兴趣的其他国家相比,希腊这些年的自杀率相对较低。我们发现的另一篇论文证实了这一发现。[2]
6。在过去的 15 年里,韩国的自杀率急剧上升,仅仅 10 年后,就从 15/10 万上升到了 40/10 万。这可能有几个潜在的原因,如高失业率和网络欺凌的高发生率。我们发现,韩国政府已经采取行动,如建立当地福利和安全支持系统,为那些试图自杀的人提供善后护理,并根据年龄、性别和社会经济地位量身定制医疗保健计划。[3]
7。美国——蒙大拿州的自杀率是美国最高的。从 2016 年的 25.90/100k 上升到 2017 年的 28.90/100k。统计数据显示,蒙大拿州只有不到四分之一的精神卫生保健提供者需要为其居民服务。此外,浓厚的枪支文化、大量饮酒、冬季日照不足和高海拔都导致了高自杀率。[4]2017 年,面临预算短缺,政府削减了该州卫生部门的预算,包括心理健康资金,这进一步加剧了问题。
视频演示
查看我们制作的视频,了解所有的交互和功能!
有待改进的领域
- 更多“相关事实”
由于国家数量众多,我们无法调查所有数据的差异,并且只有少数几个国家的信息。此外,如果能够通过包含某种插图或图标,用更多的视觉设计元素来呈现这些数据,那就更好了。然而,即使我们收集了少量的数据,我们仍然得到了一些很好的见解。
2。更多状态数据
美国是此次发布中唯一一个拥有各州自杀数据的国家。有了更多的信息,我们将有更多的数据来分析,并找出更多的“相关事实”来支持我们的可视化。此外,这将有助于那些没有大的跳跃或变化的国家。在这种情况下,小的起伏可能是由于几个州的个别变化,这可能更容易指出。
3。更多最新数据
我们使用的数据集只到 2016 年。拥有直到 2019 年或至少 2018 年的数据可能会更好,因为这将使我们能够更好地将这些发现与更近期的事件联系起来。
谢谢大家!
参考
[1]欧盟统计局:芬兰自杀率下降接近欧洲平均水平https://yle . fi/uutiset/osasto/news/Eurostat _ Falling _ silicon _ rate _ in _ Finland _ nears _ European _ average/10324113
[2]《经济危机期间希腊克里特岛的自杀率:年龄、性别、失业和心理健康服务提供的影响》https://bmcpsychiatry . biomed central . com/articles/10.1186/s 12888-018-1931-4
[3]韩国瞄准高自杀率https://www . VOA news . com/east-Asia/南朝鲜瞄准高自杀率
[4]蒙大拿州的自杀率是全国最高的。然后预算削减来袭。https://www . NBC news . com/news/us-news/Montana-have-first-rate-country-then-cuts-hit-n 904246
用 Python 实现流水线中的主成分分析和 SVM
管道、网格搜索和等高线图
Decision Boundary (Picture: Author’s Own Work, Saitama, Japan)
在之前的一篇文章中,我已经详细描述了关于主成分分析的和支持向量机(SVM)算法背后的数学。在这里,我将结合 SVM、主成分分析和网格搜索交叉验证来创建一个管道,以找到二元分类的最佳参数,并最终绘制一个决策边界来展示我们的算法表现得有多好。你希望在这篇文章中学到/回顾的东西—
- 通过 Seaborn Library 以有意义的方式联合绘制和表示数据。
- 如果主成分分析中有 2 个以上的成分,如何选择和表示哪 2 个成分比其他成分更相关?
- 使用 PCA 和 SVM 创建管道,通过网格搜索交叉验证找到最佳拟合参数。
- 最后,我们选择 2 个主成分来表示 3d/2d 图中的 SVM 决策边界,使用 Matplotlib 绘制。
1.更好地了解数据集:联合图和 Seaborn
在这里,我使用了 scikit-learn 癌症数据集,这是一个相对简单的数据集,用于研究二元分类,分为恶性和良性两类。让我们来看几行数据帧。
如我们所见,数据集中总共有 569 个样本和 30 个特征,我们的任务是将恶性样本与良性样本进行分类。在确认没有缺失数据后,我们检查特征名称并检查平均特征的相关图。
下面是使用 seaborn 库封装的平均特征的相关图。正如所料,“面积”、“周长”和“半径”高度相关。
Fig. 1: Correlation plot of mean features.
我们可以使用’ seaborn jointplot '来了解各个特征之间的关系。让我们看看下面的两个例子,作为散点图的替代,我选择了 2D 密度图。在右边的面板上,我使用了“十六进制”设置,通过直方图,我们可以了解一个小的六边形区域内的点的数量的集中程度。六边形越暗,落在该区域的点(观察值)越多,这种直觉也可以用绘制在两个特征边界上的直方图来检验。
Fig. 2: Joint-plots can carry more info than simple scatter plots.
在左侧,除了绘制在边界上的单个要素的直方图之外,等值线表示 2D 核密度估计(KDE)。不仅仅是离散的直方图,KDE 直方图通常是有用的,你可以在这里找到一个奇妙的解释。
我们还可以绘制一些配对图,以研究哪些特征与区分良性样本和恶性样本更相关。让我们看下面的一个例子——
Fig. 3: Pair plots of few features in Cancer data-set. Code can be found in my GitHub.
一旦我们玩够了数据集来探索和理解我们手头的东西,那么,让我们转向主要的分类任务。
2.管道、GridSearchCV 和支持向量机:
2.1。主成分:**
现在,我们将按照以下步骤,使用 StandardScaler、PCA 和支持向量机创建一个管道—
- 从分割训练集和测试集中的数据集开始
- 检查主成分分析的效果:PCA 以这样一种方式降低特征空间的维度,使得最终特征彼此正交。因为我们在癌症数据集中有 30 个特征,所以可视化 PCA 实际上做什么是很好的。你可以在我关于 PCA 的另一篇文章中读到更多细节。在应用 PCA 之前,我们通过减去平均值来标准化我们的特征,并使用标准缩放器将其缩放到单位方差。所以,我们从选择 4 个正交分量开始—
让我们绘制这四个主要成分的癌症数据——
Fig. 4: Which principal components are more relevant?
从图中可以看出,前两个主成分与区分恶性和良性样本更相关。方差比怎么样?
正如预期的那样,前两个因素占总方差的 80%。这与在选择 2 个分量来绘制决策边界之前显示相关,因为您可能有一些具有许多特征的数据集,其中选择 2 个主分量在百分比方差比方面是不合理的。在使用 PCA 和一些分类器创建管道之前,最好检查两个主成分的选择。
**2.2。管道&网格搜索交叉验证:T3
一旦我们看到 PCA 对于分类和为分类器绘制决策边界是多么重要,现在,我们用标准缩放器、PCA 和 SVM 创建一个流水线。
你可以查看更多关于管道和网格搜索交叉验证的细节,那是我单独写的。我选择了 2 个主成分,因为我们的目标是在 2D/3D 图中绘制决策边界,并且使用径向基函数核的 SVM 的最佳参数“C”和“Gamma”是通过固定数量的主成分值获得的。让我们检查一下拟合参数—
在这里,我们看到,使用 2 个主成分和 4 个交叉验证,我们的管道与 SVM 分类器获得了 94%的准确性。当然,你可以使用不同的值或者使用不同的内核。关于内核函数背后的数学,你可以查看我的另一篇文章。在进入下一部分之前,我们可以通过绘制混淆矩阵来完成分析,从中可以获得精确度、召回率和 F1 分数。
Fig. 5: Confusion Matrix obtained using our pipeline with SVM classifier and RBF kernel.
3.地块 SVM 决定边界:
我已经按照 Scikit-Learn 的教程绘制了最大间隔分离超平面,但是这里使用的不是教程中使用的线性核,而是径向基函数核。我们将使用决策函数方法,该方法返回样本中每个类的决策函数。直观上,对于二元分类,我们可以把这种方法想象成它告诉我们,我们在分类器生成的超平面的哪一边,有多远。对于 SVM 决策规则的数学公式,如果你有兴趣,可以查看我以前的帖子。让我们看看决策函数的轮廓
Fig. 6: Decision Function contours with Radial Basis Function kernel is shown here along with the support vectors. Best fit-parameters are obtained from 5 fold grid search cross-validation.
让我们来理解我用来绘制上图的代码
- 从选择 2 个主成分进行主成分分析开始。只有 2 个组件,因为它有助于我们在 2D/3D 绘图中可视化边界。将 2 个以上的组件与决策函数轮廓一起可视化是有问题的!
- 设置具有径向基函数核的 SVM 分类器,并且将“C”、“gamma”参数设置为从网格搜索交叉验证中获得的最佳拟合值。
- 定义一个函数来创建由 x、y(最终选择的主成分)和 Z(SVM 的决策函数)组成的等值线。
- 我们通过函数 make-meshgrid 用一个 x 值数组和一个 y 值数组创建一个矩形网格。检查 Numpy Meshgrid 的必要性。
- 最后,我们将决策函数绘制为 2D 等高线图,并将支持向量绘制为分散点。
希望这篇文章能帮助你理解建立支持向量机分类器的策略,并有效地使用主成分分析来可视化决策边界。
让我们来看看它的 3D 动画——
Fig. 7: Decision Function contours as viewed from top. 3D representation for the figure 6.
最后,我强调检查你的理解总是好的,在这里我们可以看到伽马因子如何影响决策函数轮廓。
对于上面的图,我们有γ = 0.5,C = 1。你可以阅读我的关于 SVM 内核的另一篇文章,在那里我已经讨论了增加γ参数如何导致复杂的决策边界。
让我们通过将“C”参数固定为最佳拟合值,使用两个不同的“Gamma”值来检查这一点
- 为γ = 0.01
Fig. 8: Decision function contours plotted with low gamma (γ=0.01) value. Can be compared with figure 6, where γ = 0.5.
- 对于γ = 10.0
Fig. 9: High gamma parameter is causing extremely complicated decision boundaries.
正如你所看到的,增加伽马参数会产生非常复杂的轮廓。最重要的是,几乎所有高伽马值的样本(每个类别的)都充当支持向量。这无疑是对如此简单、容易分类的癌症数据集的过度拟合。
“永远检查你的直觉和理解力!!"—匿名
为了进一步阅读,我建议你查看塞巴斯蒂安·拉什卡(Sebastian Raschka)的《用 Python 进行机器学习》(第 76–88 页,2017 年 9 月第二版)一书中给出的几个精彩演示。
保持坚强和快乐,干杯!
在 Github 中找到这篇文章使用的代码。
在法国可视化特斯拉增压器
从头开始使用 Python 和 Folium 学习可视化
数据可视化不仅仅是科学,它还是一门艺术。根据我们人类大脑的工作方式,以可视化的形式处理信息非常容易。在进入数字地图近 25 年以及许多公司使用机器学习来收集大量数据之后,数据可视化比以往任何时候都更加重要。
在这篇文章中,我们将可视化目前在法国可用的特斯拉超级充电器(充电站)。我们将使用 Python、leav 和 Pandas 来完成这项工作。激动吗?让我们开始吧。
重要的事情先来
数据
首先,我们正在使用由法国政府提供的特斯拉的特斯拉增压器数据。你可以在上面的链接或者我的 GitHub 库中找到这个数据文件。数据有很多列,但我们将只使用纬度、经度和站名。
库
我们将使用 leav 和 Pandas,所以如果您还没有安装它们,请在您的终端中键入以下命令。
pip install foliumpip
pip install pandas
步骤 1-创建底图
好了,所有特斯拉和可视化的狂热分子,让我们开始吧。为了可视化增压器的位置,我们当然需要一张地图。否则,我们怎么找到他们?所以,我们做的第一件事是,我们为一个特定的位置(世界上的任何地方)创建一个基础地图。默认情况下,Folium 从打开的街道地图中获取地图数据。好好看看代码。
首先,我们导入叶子库。接下来,我们在folium.Map()
方法的帮助下创建一个对象——在我们的例子中是地图— 。在该方法中,我们传递地图的坐标和缩放级别。最后,不用说,你保存了地图。我从巴黎开始,你不觉得巴黎的地图像个脑细胞吗?还是只有我?无论如何,让我们进入下一步。
Paris (as if this needed description)
步骤 2-绘制标记
现在我们已经创建了一个基本地图,我们要绘制我们的增压器的位置。我们如何做到这一点?超级简单。我们使用folium.Marker()
方法来定义和定制标记。您可以添加参数,如弹出,改变图标,等等。点击此处了解更多信息。最后,使用.add_to()
方法,您将标记与您的地图关联起来。
我们在 Thiais 增加了特斯拉增压器作为我们的第一个标记,这是巴黎南郊的一个公社,这就是它的样子。
步骤 3 —多个标记
你能画一个标记吗?当然不是。你可能会画出不止一个或者超过 100 个,我们也会这样做。但是在达到 100 之前,让我们试着只做 2。这样我们会对它的工作原理有更多的了解。
如你所见,我们使用了一个for
循环。是的,只是一个for
环。编程并不总是复杂的代码,当你掌握了基本知识,你可以做得非常好。所以我们对两个标记进行循环,剩下的就很容易理解了。不是吗?让我们保存地图,看看我们做得怎么样。
Multiple Markers
步骤 4 —使用数据
我们的确画了两个标记,那么那些 100 呢?你要一个一个加吗?你可以,但是你愿意吗?肯定不是。因此,我们将使用包含纬度和经度的现有数据文件,并在我们创建的for
循环中传递它。
我们将使用 Pandas 作为我们的数据操作库。我猜你对熊猫很熟悉,但是如果不熟悉,看看这个 10 分钟熊猫教程然后回到这里,我等你。
编程并不总是复杂的代码,当你掌握了基本知识,你可以做得非常好。
回来了?完美。在用 read_csv 或 read_excel 加载数据之后(我更喜欢在这个教程中使用 excel,因为 csv 文件没有得到正确的维护),我们提取我们需要的每一列,并将其存储在各自的列表中。在for
循环中,我们提供相同的列表并从头到尾运行它,而不是手动坐标。运行该文件后,您将在地图上看到以下内容。很整洁,是吧?
All Tesla Supercharging stations in France
第 5 步—自定义图标
但是等等,即使它正确地显示了一切,它也没有给人那种“特斯拉”的感觉,你明白我的意思吗?简洁的外观,简约的设计?让我们试着模仿一样的。让我们使用特斯拉图标,而不是这些绿色标记。并尝试使地图背景更清晰或者我应该说最小化?这由你来决定。
要添加自定义图标,首先你要下载一个(透明背景优先),在icon_path
中设置图标的路径。之后,在 that 的CustomIcon()
方法中,提供 icon_path 和图标的大小。你已经准备好了。执行文件并查看更改。
步骤 6-通过聚类美化
看起来比以前更好,但我们可以使用一些东西来使它变得更漂亮——集群。让我们把附近的特斯拉增压器聚集起来,这样你就只能看到你想看到的东西。
用叶的MarkerCluster()
方法,我们可以聚类我们的标记和美化地图。我们只需要做一件不同的事情:在此之前,我们将标记添加到地图,但现在,我们将标记添加到标记聚类,然后将标记聚类添加到地图。
在将所有标记添加到聚类中之后,它看起来比以前更好。
The final visualization/ Live map here
瞧啊。您已经使用 Python 和 have 创建了可视化。没那么难,对吧?
尾注
没有合适的资源学习新东西有时会很难。今天,您学习了如何使用 pandas 操作数据,什么是叶,以及如何使用叶用 Python 可视化事物。既然你已经熟悉了 leav,那么就从不同的存储库或 Google Dataset Search 上找到数据集,并可视化你自己的东西。此外,您可以在我的 GitHub 资源库中找到所有的代码文件、图像和数据。如果你对本教程有任何疑问,我的 Twitter 和 Linkedin DMs 永远开放。或者直接过来打个招呼。
可视化 2017 年野火季节
2017 年是美国野火灾难性的一年,特别是在加利福尼亚州,发生了 9000 多起火灾,燃烧面积超过 130 万英亩。
斯坦福新闻和民主倡议的一个项目“大地方新闻”开发了一个数据库,用来追踪由联邦政府管理的大面积野火(100 多英亩)。根据该数据,2017 年美国发生了超过 23,000 起野火,比前一年增加了 21%,比数据开始的 2014 年增加了近 80%。
虽然这个数据库并没有全面的显示这个国家的野火情况,但是它显示了野火的增加,T2,专家认为这是全球变暖造成的。
Data from Big Local News / Chart by Jared Whalen.
方法论
从大型本地新闻数据集 ( 美国野火的成本(2014–2017)开始,我按唯一标识符统计记录,并按日期分组。没有事故日期的记录被删除。
工具:R (dplyr,lubridate,ggplot),Illustrator
流程
我是通过 Buzzfeed 的 Jeremy Singer-Vine 每周收集的数据集 Data is Plural 简讯偶然发现这个数据集的。在四处探索之后,它看起来像是一个有趣的图表。
谈到野火这个话题,我想选择一种视觉上看起来像火焰的图表类型。这让我想起了 Nadieh Bremer 在她的作品 The Baby Spike 中的美丽的可视化,它使用了径向面积图和生动的颜色着色。深受布雷默文章的启发,我想加入一个使用平均值作为基线的径向面积图设计。
至于数据,我在 r 中完成了所有的争论和分析。我的主要代码只是对大规模数据集进行精简,然后按日期进行汇总。
library(tidyverse)
library(lubridate)# identify unique records
fireData_unique <- fireData %>%
group_by(INC_IDENTIFIER) %>%
filter(n() == 1) %>%
ungroup()
# make field selections and convert dates
fireData_sel <- fireData_unique %>%
select(INCIDENT_NAME,
DISCOVERY_DATE) %>%
mutate(day = yday(ymd_hms(DISCOVERY_DATE)),
week = week(ymd_hms(DISCOVERY_DATE)),
year = year(ymd_hms(DISCOVERY_DATE))
) %>%
# remove records with missing or erroneous dates
filter(
!year %in% c(“2011”, NA)
) %>%
# get count by day
group_by(day, year) %>%
summarise(count = n())# create average df
fireData_avg <- fireData_sel %>%
group_by(day) %>%
summarise(mean = mean(count))
虽然我在 Illustrator 中做了大量的工作,但大部分繁重的工作来自下面使用 ggplot 的代码。
# function to shift baseline by mean
shiftBase = function(x) {
x — mean(fireData_avg$mean)
}# Make the plot
ggplot() +
geom_area(data=filter(fireData_sel, year==2017), aes(x=day, y=count-mean(fireData_avg$mean)), fill=”#FFBF3F”, alpha=1) +
facet_grid(~year) +
geom_area(data=fireData_avg, aes(x=day, y=mean-mean(fireData_avg$mean)), fill=”#547C8E”, alpha=0.2) +
theme_minimal() +
geom_hline(yintercept=0, color=”#FF9843") +
coord_polar(start = 0.1) +
scale_y_continuous(
breaks=shiftBase(c(0,100,200,300)),
labels = function(x){
round(x+mean(fireData_avg$mean),-2)
},
limits=c(-150,max(fireData_sel$count))
)
独立图表
用 Python 可视化中心极限定理
每当您处理数据并想要收集一些关于它们的信息时,您可能要检查的第一件事就是它们是否遵循已知的分布。
为什么了解数据分布如此重要?
这个问题的答案依赖于许多统计类型研究的目标,即更多地了解一个目标,通常称为人口。警告是观察你项目的全部人口通常是不切实际的(甚至是不可能的)。想象一下,也就是说,你想知道年龄在 40 到 50 岁之间的男性的平均体重。你认为你能采访世界上所有具有这些特征的男人吗?
不可能观察到全部人口,这就是我们需要样本的原因。抽样程序包括从全部人口中选择一个子集,这个子集最好能代表初始人口。通过这样做,我们希望从样本中包含的信息出发,对我们的目标总体做出推断。
在前面的例子中(找出男性的平均体重),一种解决方法是从男性子集或样本中收集数据,考虑不同的国籍,这样样本就不会有偏差。因此,我们将获得一个样本平均值,它应该接近总体平均值。
然而,我们怎么能确定这个结果的准确性呢?根据定义,统计学家永远不会确定,这就是为什么我们需要测试我们的结果的稳健性,运行所谓的假设检验。后者允许我们声明我们是否可以拒绝我们的零假设(在我们的例子中,这将是*‘样本均值是真实参数的良好估计’*)。
为了做到这一点,我们需要知道数据的分布。这就是为什么中心极限定理(CLT)如此重要。
CLT 的想法如下:让我们收集大小为 n 的 x 个样本,并让我们计算每个样本的样本均值。然后,(在我们马上要看到的一些假设下)如果我们绘制所有样本均值,它们应该遵循正态分布。此外,所有样本均值的均值应该几乎等于总体的真实参数。
我提到的那些假设是什么?
- 取样程序必须随机执行
- 样本必须相互独立
- 在不替换样本的情况下进行抽样时,样本量不应超过总体的 10%
- 样本量应该足够大(通常,n=30 的样本量被认为是足够大的,即使它确实取决于初始总体)
如果这些假设被认为是正确的,CLT 允许您对初始人口进行推断。此外,样本越大,样本均值分布的钟形就越明显。
为了充分理解这个定理,让我们用 Python 可视化一下。我要做的是创建男性体重的随机样本(想象他们在 60 到 90 公斤之间),每个尺寸 n=50。然后,我将多次运行这个模拟,看看样本均值分布是否类似于正态分布。
from numpy.random import seed
from numpy.random import randint
from numpy import mean
# seed the random number generator, so that the experiment is #replicable
seed(1)
# generate a sample of men's weights
weights = randint(60, 90, 50)
print(weights)
print('The average weight is {} kg'.format(mean(weights)))
现在让我们重复采样模拟 1000 次:
import matplotlib.pyplot as plt
# seed the random number generator, so that the experiment is replicable
seed(1)
# calculate the mean of 50 men's weights 1000 times
means = [mean(randint(60, 90, 50)) for _i in range(1000)]
# plot the distribution of sample means
plt.hist(means)
plt.show()
print('The mean of the sample means is {}'.format(mean(means)))
根据 CLT,样本均值(74.54)应该是真实参数(未知)的良好估计。
为了确定我们的结果,让我们对数据进行正态性检验。出于这个目的,我将使用夏皮罗-维尔克正态性检验(你可以在这里阅读更多关于这个检验的内容),这里的假设是:
H0:数据服从正态分布
H1:数据不符合正态分布
因此,如果我们的样本均值服从正态分布,我们将不拒绝零。
from scipy.stats import shapiro
stat, p = shapiro(means)
print('Statistics={}, p={}'.format(stat, p))
alpha = 0.05
if p > alpha:
print('Sample looks Normal (do not reject H0)')
else:
print('Sample does not look Normal (reject H0)')
由于 p 值远远大于我们的置信水平 alpha (具体来说,它大于任何显著水平的 alpha ),我们不拒绝 H0。
现在让我们看看,如果我们将样本量从 50 分别增加到 80、90 和 100 会发生什么:
正如你所看到的,样本量 n 越大,p 值越高,我们不拒绝正态零假设的置信度就越高。
通过 MTV 想象挑战
项目
仔细看看有史以来收入最高的 28 名球员
Photo by Nathan McBride on Unsplash
《挑战第 34 季:世界之战 2》将于 8 月 28 日开播,随着玩家一步步爬上巅峰,将会有更加艰苦的挑战和巧妙的计划。
事实上,当我们最后一次在第 33 季停下来时,我们的土耳其超人涡轮(Turabi am kran)在一场残酷但竞争激烈的决赛后取得了胜利——最终获得了高达 75 万的收入。同为新人的西奥·坎贝尔以英国联盟最后一名的身份排名第二,而挑战兽医韦斯·博格曼艰难地排在第三。
灵感
33 季的动作片承载了大量的历史,讲述这个故事的最好方式是通过数据直观地展现出来。事实上,只要快速浏览一下任何挑战者的 MediaWiki 页面,就可以全面了解他们的历史,包括总奖金,一直到他们的家乡。
此外,在维基百科上,有一个有史以来收入最高的 28 个挑战者的名单,所以这篇文章基本上是同时对所有 28 个 MediaWiki 页面的全景展示。
如果你在移动设备上,现在是切换到横向视图的好时机,可以充分享受剧情的互动体验。请随意触摸任何绿点。
双击图表进行重置。干杯!
他们来自哪里?
事实上,直到几个赛季前,挑战赛几乎全部由美国演员出演,以至于只有 3 个 MediaWiki 声明的家乡不在美国。非美国人的家乡排在前 28 名的是:
- **turbo—**土耳其梅尔辛
- 英格兰西奥-巴斯
- 加拿大安大略省埃文
不出所料,前 28 名中的其他人确实在美国有自己的家乡,尤其是在东北部地区。这并不是说他们都出生在美国,我们可以以出生在巴西的卡蜜拉为例。随着《世界之战》和《世界之战 2》都包含一半的美国和英国演员,这种以美国为主的 28 强可能会在未来几年有所改变。此外,两场比赛中的后一场是美国队和英国队之间的战斗。
你支持哪一个?
淘汰
在《世界大战》中,每周前 3 名的表演团队(或个人,取决于挑战)组成“法庭”,在那里他们投票选出剩余团队中的一个进入“杀戮舞台”,争夺他们的比赛资格。被选中的队预计将选择任何其他队与之竞争,只要他们不是法庭的一部分。失败的队伍回家了,获胜的队伍继续前进:
为他们的 100 万美元而战。
-TJ·拉文
尽管每一季挑战的形式都会改变,但这通常构成了淘汰赛回合**。**
从这里我们可以看到韦斯可能会在漫长的 11 个赛季中以 14 胜 6 负的成绩脱颖而出,成为前 28 名票房挑战者中的淘汰赛冠军。可以说,也许有一个更好的记录站在乔丹在他的 4 个赛季中惊人的 5 胜 1 负。
在女子组,我们看到卡拉玛利亚 (13 胜 6 负,13 个赛季)卡蜜拉 (9 胜 4 负,10 个赛季)有着非常令人敬畏的记录。
然而,最引人注目的是劳雷尔在短短 4 个赛季中取得了惊人的 9 胜 1 负。
然而,最明显的是,我们亲爱的约翰尼·香蕉(约翰尼·德弗南齐奥)有着最差的淘汰记录,在他的 18 个赛季中,只有 8 场胜利和 12 场失败。
赢得的总金额
这些年来,挑战赛的奖金大幅增加。在早期的比赛中,剩下的最后一名挑战者赢得了超过 10 万英镑的奖金。然而,这些数字一直稳步增长,直至 Turbo 赢得 75 万英镑的大奖。这立即将他推到了有史以来最卖座挑战者名单的第二位。事实上,对于大多数挑战者来说,在花费了 10 个赛季后,通常会获得 10 万到 30 万之间的收入。
这是男女参赛者赢得的总奖金分布的不同情况。这证明了挑战的公平性,在有史以来总收入最高的 28 名竞争者中,男性和女性挑战者之间的金钱成功没有明显的区别。
推特分析
这里我们看到迈克“米兹”米扎宁拥有最多的追随者,超过 3 米。事实上,这显示了世界摔跤娱乐界在发展社交媒体追随者方面的力量。此外,Miz 也有一些广泛的电影记录,主要是直接视频制作。此外,他还在 2017 年主持了慈善季 Champs vs Stars,并在几个不同的季节主持了挑战赛的团聚集。
如前面的图表所示,自从 2006 年首次参加决斗以来,香蕉参加了历史上最多的挑战(18 次)。然而,他的 MTV 处女作是真实世界:基韦斯特。他目前是 NBC 第一套节目的主持人。
毫无疑问,香蕉紧随 Miz-I kid 之后,最近达到了约 30 万粉丝的里程碑。
在 Twitter 上最活跃
这可能是所有图表中最令人震惊的,尽管 2016 年只创建了一个账户,德里克·K 却拥有最多的推文!德里克通常是节目中最安静的人,只是不断说话以加强他对大奖的关注,他打算用这笔钱为家人提供更好的生活。然而,在 twitter 上,他看起来确实很活跃,在他的 3 年里积累了超过 17k 条推文——我印象深刻。
我希望你喜欢这本书,并对观看第 34 季感到兴奋!
我本人是数学金融专业的应届毕业生,非常喜欢与世界分享我的学习曲线。——也就是说,我非常乐意接受任何建议、更正或你在阅读这篇文章时想到的任何一般性反馈。
请随时在 LinkedIn 上与我联系—
[## 约万·梅德福德-加拿大滑铁卢大学| LinkedIn
我是一名四年级数学金融本科生,热衷于使用数学和数据解决问题。我最新的…
www.linkedin.com](https://www.linkedin.com/in/jovan-medford-b86094156/)
或者在 Twitter 上关注我
约万·梅德福德的最新推文(@JovanMedford)。一点特别的东西🤯|数学、金融和科技作家…
twitter.com](https://twitter.com/JovanMedford)
我与 MTV 没有任何关系,这是从 MediaWiki 搜集的非官方数据。
使用基于规则的情感分析可视化电影脚本的情感弧线
我如何使用 Python、D3 和 Flask 来创建这个交互式可视化
大约 72 年前,广受赞誉的美国作家库尔特·冯内古特想出了一种新颖的方法来用描绘故事的情节主线,作为他人类学硕士论文的一部分。
根据冯内古特的说法,尽管他的工作最终被芝加哥大学拒绝,“因为它太简单了,看起来太有趣了”,但在来自佛蒙特大学的一组研究人员决定使用计算方法来测试他的假设后,他被忽视的贡献在过去几年里重新受到了一些关注。
他们想出了近 2000 本英文书籍的计算机生成的故事弧线,归类为冯内古特原始论文中概述的六个主要故事弧线之一。其中包括**【破衣烂衫】【崛起】【破衣烂衫】【堕落】【洞中之人】【堕落然后崛起】【伊卡洛斯】【崛起然后堕落】【灰姑娘】【崛起然后堕落然后崛起】【俄狄浦斯】**(堕落然后崛起然后堕落)。
他们的作品与冯内古特的不同之处在于,他们描绘了故事的情感轨迹,而不仅仅是情节主线。为了做到这一点,他们在每篇文章中滑动 10,000 个单词的窗口,用一个包含 10,000 个独特单词的词汇对故事中数百个点的相对幸福度进行评分,这个词汇按幸福度的 9 分制进行评分,这就产生了用于情感分析的快乐度计工具。
使用唤醒作为行动的代理措施
我使用了一种类似的基于词汇的方法,绘制了一千多个电影剧本的情感弧线,并使用层次聚类法对最相似的剧本进行分组。
然而,由于之前的研究主要关注积极和消极的情绪,而不是上升和下降的行动,以下方法与 UVM 的方法不同,因为它使用了 NRC 价,唤起和支配词汇,它有 20,000 个英语单词的分数,作为故事中行动或冲突的替代措施。具体来说,我用唤醒维度给单词打分,从“平静”或“被动”到“兴奋”或“主动”。
结果是一个交互式可视化,可以用来搜索任何已经在互联网电影数据库上发布的剧本,以可视化其情感故事弧(为了唤起),并找到五个在故事的情感轨迹方面最相似的电影剧本,而不是内容。
例如,在搜索大片《阿凡达》(Avatar)时,你可以看到觉醒在电影接近尾声时达到峰值,约为剧本长度的 90%,这与电影中最紧张和冲突的点相对应(纳威人和人类之间决定地球命运的最终对抗)。
其他具有类似结构的电影,有明确的高潮结尾,包括《美女与野兽》(Beauty and the Beast)、《复仇者联盟》(The Avengers)(2012)和《阿里》(Ali)。
以下是我得出这些结果的过程:
使用 VAD 词典抓取文本和评分
利用《美丽的汤》和杰里米·库恩剧本的修订版,我从互联网电影数据库中搜集了所有的电影,使用以下脚本花了大约 20 分钟:
然后我用 NRC VAD 词典创建了一个新词典:
我调整了简单的 labMT 使用脚本来计算语料库的唤醒分数,而不是快乐,用我自己的 labMT 字典和 labMT 向量来代替:
考虑到电影脚本平均比书籍短,我将固定窗口大小设置为 1000 个单词,并在每个脚本中滑动,以生成 n 唤醒分数,或最终时间序列中的点数。
矩阵分解和层次聚类
在实现了 simple labMT 的修订版之后,我使用了奇异值分解来将故事分解到情感弧的正交基上,使用了线性插值来创建等维的词向量,并使用了 scipy 的层次聚类来根据情感弧的轨迹来查找和分组最相似的电影脚本。
使用沃德的方法和欧几里德距离作为距离度量,我能够最小化电影脚本簇之间的差异,以达到最准确的观察分段。
使用 Flask API 评分,使用 D3 可视化结果
最后,我创建了一个 Flask API 来输出在互联网电影数据库中找到的任何电影的唤醒分数,并返回五个最相似的脚本。
使用一个函数来调用 API 和过滤数据,我使用 D3 内置的 d3.json()方法来加载和解析 json 数据对象,从而可视化结果输出。
你可以在这里玩最后的交互可视化,在这里查看所有的笔记本和代码。下面让我知道你的想法!
可视化卷积神经网络的基础
Photo by Pacto Visual on Unsplash
通过可视化示例理解卷积神经网络背后的主要概念
什么是卷积神经网络?
卷积神经网络(CNN)是人工神经网络(ann)的一个子类型,主要用于图像分类。CNN 遵循能够识别模式的结构复制的生物学原理,以识别不同位置的这些模式。它的灵感来自于诺贝尔奖获得者 Hubel 和 Wiesel 在 1962 年发表的“猫的视觉皮层中的感受野、双目互动和功能结构”中提出的猫的视觉系统模型。运用这一灵感的作品之一是 1980 年福岛的新认知图,尽管当时没有使用卷积这个词。所以,CNN 在图像识别上非常成功,不是巧合。然而,它们在处理时间数据方面也表现出了良好的效果,例如时间序列和语音识别,甚至在应用于图形时也是如此。
CNN 在以大约 10%的优势赢得 2012 年比赛 Imagenet 大规模视觉识别挑战赛后变得非常受欢迎。 Alex Krizhevsky 和 Ilya Sutskever 在 Geoffrey Hinton 的指导下,提交了以“AlexNet”为名而成名的 CNN 架构。当时,杰弗里·辛顿已经在人工神经网络领域做出了重大的科学贡献。他是 1986 年反向传播算法和 1983 年玻尔兹曼机器的贡献者之一。这些就是杰弗里·辛顿被公认为深度学习之父的部分原因。
卷积或互相关
典型的 CNN 由一系列充当特征提取器的卷积层组成,后面是一个分类器,通常是一个多层感知器(MLP) ,也称为全连接层(FC 层),如图 1 所示。
Figure 1 — Architecture of a basic Convolutional Neural Network.
第一层接收用三个颜色通道(RGB 通道)表示的输入图像。然后,第一层用多个核执行输入图像的卷积,产生第一层的一组特征图。每个特征图确定特定特征的强度和位置。由卷积层提取的特征图可以被提交给称为汇集的下采样操作。汇集操作是可选的,因此它可能不会遵循每个卷积层。合并图层的结果是另一组要素地图,地图数量相同,但分辨率降低。下面的卷积层使用来自前一层的特征图来执行更多的卷积并生成新的特征图。来自最后层的特征图是分类器,FC 层的输入。
用星号表示的卷积运算可以描述为:
被𝑥某种类型的输入,如传感器信号,𝑡给定的时间,和𝑘的内核应用。
卷积运算的一个重要性质是它是可交换的,这意味着(𝑥∗ 𝑘)=( 𝑘∗𝑥)如下:
另一方面,由⋆(五角星)表示的互相关运算是不可交换的,可以描述为:
卷积的可交换性来自于内核相对于输入翻转的事实。这种翻转是索引操作的结果。请注意,输入𝑥的索引是𝑎,内核的索引是𝑡−𝑎.尽管可交换性对于编写数学证明是一个有价值的属性,但它与神经网络实现并不相关。事实上,许多机器学习库实现了互相关而不是卷积,并将这两种操作都称为卷积。因此,在训练期间学习的内核与实际实现如等式 1 所述的卷积的库相比将被翻转。在本文中,我们将遵循同样的惯例,称之为互相关卷积。
我们可以将等式 3 用于与 2D 数据(例如灰度图像)的卷积:
是𝑟[𝑖,𝑗]卷积的离散输出,ℎ是核的高度,𝑤是核的宽度,𝑥[𝑎,𝑏]是灰度图像的补片,而𝑘[𝑖+𝑎,𝑗+𝑏]是核。
换句话说,卷积运算从图像中提取多个像素片,然后乘以内核。内核基本上是一个权重矩阵。从图像中提取的像素块通常被称为感受野——在生物学中,感受野是刺激神经元的感觉区域。感受野和内核之间的乘法包括每个像素和内核的相应元素之间的逐元素乘法。在乘法之后,结果被相加以形成特征图的一个元素,由𝑟[𝑖,𝑗].在等式 4 中定义
以下动画显示了 5x5 灰度图像和 3x3 内核之间的卷积运算。感受野用红色方块突出显示。该卷积的输出是 3×3 特征图。
Figure 2 — Step-by-step of the convolution of a 5x5 image with a 3x3 kernel.
动画中使用的实际图像可以在下面的图 3 中看到。内核和特征图中的值被重新缩放以适合 0 到 255 之间的区间,从而被表示为灰度像素。图像中较亮的像素代表卷积的较高值,而较暗的像素代表较低值。
Figure 3 — Convolution of a 5x5 input with a 3x3 kernel.
上面的卷积使用核 3x3,因此,在输入中有九个可能的感受野,每个大小为 3x3。注意,主要由白色像素组成的感受野或主要由暗像素组成的感受野在卷积后会产生非常暗的像素。另一方面,由左边的三个亮像素、中间的中间像素和右边的暗像素组成的感受域在卷积后产生最亮的像素。这是因为这种类型的内核有助于突出显示边缘,特别是从左侧亮区域过渡到右侧暗区域的边缘。
现在,看看当我们将相同的内核应用到一个也包含相反过渡的图像时会发生什么,从左边的暗区域到右边的亮区域。在图 4 中,感受野呈现从暗到亮的过渡,导致最暗的像素。请注意,之前的过渡(从亮到暗)仍然会产生更亮的像素。这意味着该内核不仅检测从亮到暗转变的边缘,还检测相反的从暗到亮的边缘。一种类型的边产生最大的正值,而另一种类型的边产生最大的负值。
Figure 4 — Convolution of an image 17x17 with an edge detector kernel 3x3.
RGB 图像的卷积与灰度图像非常相似。等式 4 可以适用于 RGB 图像,增加另一个循环来迭代 RGB 通道,如下所示:
可变𝑐上的附加循环允许在信道 RBG 上迭代。结果,求和是在三维数据上完成的,而不是在二维数据上,并且仍然产生每个三维感受野和核的单个值。
特征抽出
让我们从一个实际的例子开始这个话题。请看图 5 中下面三个卷积的结果。为了说明卷积的结果,以下示例中的三个核中的每一个都由从图像中提取的小块组成。
Figure 5 — Examples of convolutions.
在第一个例子中,组成内核的面片包括带有白色数字 6 的平面区域。右边的灰度图像本质上是内核和图像之间卷积的结果。最暗的像素代表感受野和内核之间操作的最小结果,另一方面,最亮的像素代表感受野和内核之间操作的最高值。
在第二个例子中,内核由形成飞机轮子的像素片组成。在第三个例子中,内核由从飞机尾部复制的一片黄色像素组成。
请注意,每个结果图像中最亮的像素对应于产生每个内核的位置。在第一个示例中,它对应于数字 6 的位置。在第二个例子中,它对应于轮子的位置。尽管内核是其中一个轮子的副本,另一个轮子非常相似,也产生了明亮的像素。在第三个例子中,最亮的像素对应于平面的所有黄色区域。
进展
步幅是每个感受野之间的距离。到目前为止,我们展示的所有例子都使用了一个步长。采用如此小的步幅导致感受野之间的大重叠。结果,许多信息在相邻的感受野重复出现,如图 6 所示。
Figure 6 –Receptive fields with stride 1.
在 3×3 大小的核的情况下,步长为 2 的采用导致一列或一行与相邻感受野重叠。这种重叠是为了保证步幅不会跳过重要信息。
增加步距将减少卷积的计算成本。如果我们将步距从 1 改为 2,计算成本的减少大约是 4。这是因为步幅影响了二维感受野之间的距离。类似地,如果我们将步幅增加三倍,我们可以预期计算成本降低大约九倍。计算成本降低,因为步幅的增加减少了从输入中提取的感受野的数量,因此,输出的维度也减少了。
图 7 显示了步长为 2、4、8 和 16 的四种卷积结果。卷积中使用的内核大小是 70x70。请注意,将步幅增加两倍,执行时间会减少近四倍。
Figure 7 — Examples of stride 2, 4, 8, and 16. The receptive field dimension is 70x70.
在图 7 中,步长为 16 的卷积结果比步长为 8 的卷积结果少 4 倍的像素。注意,采用 16 的步幅导致 54 行或列的重叠,因为感受野大小是 70×70。步长为 16 时,仍有可能通过平面轮中最亮的像素来识别卷积的最高值。
正向传播
在本节中,我们将研究前向传播在卷积层中的工作原理。为此,我们将了解单个卷积层是如何工作的,然后了解多个卷积层是如何协同工作的。在这项研究中,我们将学习两个新概念:非线性激活和池操作。
在卷积层中
图 8 显示了典型卷积层中的前向传播,它包括三个阶段:卷积、非线性激活和池化。卷积运算已经在第一节中讨论过了。现在,我们将看到其他两个操作。
Figure 8 — The three stages of a convolutional layer.
非线性激活也被称为检测器阶段。在这个阶段,卷积和偏差的结果被提交给非线性激活,例如 ReLU 函数。非线性激活不会改变特征图的尺寸,它只会调整其中的值。
非线性激活
一、什么是线性激活?线性激活是遵循规则 f(x)=ax 的函数,其中 a 是常数,x 是变量。它的图形是一条穿过原点(0,0)的直线。意味着形状 f(x)=ax + b 中的函数,其中 a 和 b 是常数,不是线性的。两者都是仿射函数,但只有一个常数乘以变量的函数是线性函数。
对于一个线性函数,当我们把输入乘以一个常数𝛼时,我们也应该看到输出乘以同一个常数𝛼.这意味着:
另一个要求是,当我们在线性函数中应用两个输入的和时,我们应该得到分别应用于该函数的两个变量的和的输出当量:
那么,我们为什么要使用非线性激活呢?因为当我们在线性函数中应用线性组合(加法或乘法)时,结果也是线性的。尽管许多模型可以用线性模型粗略地近似,但在人工神经网络中使用非线性使得它能够表示线性和非线性模型。换句话说,非线性使人工神经网络成为更强大的函数逼近器。
深度学习中最常用的非线性激活之一是 ReLU 函数,它代表整流线性单元。该函数由下式给出:
该函数在有偏置的情况下,会产生如图 9 所示的图形。
Figure 9 — The graph of ReLU function.
图 10 显示了 ReLU 如何调制卷积的结果。这里,来自图 2 的相同结果被用于应用 ReLU 函数。
Figure 10 — ReLU function applied after the convolution.
上例中使用的 ReLU 函数的等效图像如图 11 所示。请注意,一些中间值被涂黑,突出显示了最亮的三个像素。
Figure 11 — Visual representation of the ReLU function applied after the convolution.
接下来,在图 12 中,您可以看到 ReLU 函数中的一些偏置选项。该函数将平面与包含车轮的图像面片的卷积结果作为输入。请注意,偏差就像一个阈值,决定显示什么和不显示什么。
Figure 12 — Different values of bias applied to the same result of the convolution between the plane and a patch of the image containing the wheel.
图 12 中描绘的阈值行为类似于生物神经元,当它接收到低于某个阈值的刺激时不会触发。如果刺激超过阈值,神经元就开始放电,并且随着刺激的增加,放电频率也会增加。在图 12 中,当偏差为 500 时,它有助于人工神经元的活动。然而,如果我们将偏差定义为-1000,人工神经元只会在最强的刺激下激发。
总之,ReLU 功能就像一个人工神经元。这就是为什么这个阶段被称为检测阶段。ReLU 函数负责检测由内核提取的特征的存在。因此,每个内核都有一个偏差,因为每个特性需要不同的阈值。
联营
最后,但不是最不重要的,池操作。它是对每个特征地图执行的缩减采样操作。它从特征图中提取感受野并用单个值替换它。这个单一值可以通过不同的聚集标准获得,例如根据距感受野中心的距离的最大值、平均值或加权平均值。
除了聚集标准之外,在汇集操作中还有另外两个超参数,感受野的大小和步幅。
与 stride 类似,池化操作导致卷积处理的数据更少。一个区别是,汇集操作不是跳过数据,而是试图将感受域总结成一个值。另一个不同之处在于,跨距是在卷积之前应用的,而池是在卷积的结果上应用的,从而减少了下一层的数据量。此外,汇集操作的感受域是二维的,因为它被单独应用于每个特征图,而卷积的感受域是三维的,包括一层中所有特征图的一部分。
汇集操作的一个期望的副作用是增加了网络对输入的平移的不变性。随着更多卷积层之后是汇集层,这种效应被放大。
图 13 显示了值通过两个池层的传播,池大小为 3x3,跨距为 2。在输入要素地图的蓝色区域中的任何激活都会影响池 1 结果中的蓝色区域。类似地,在汇集 1 的结果中被蓝色覆盖的区域中的激活影响在汇集 2 的结果中被蓝色覆盖的区域。绿色区域之间的关系也是如此。
Figure 13–Propagation of values through pooling layers with pooling size 3x3 and stride of 2. In this example, the convolutional layers were omitted for clarity.
考虑到图 13 中的池是最大池,最高值出现在输入特征图中蓝色区域的哪个位置并不重要,因为它将以同样的方式传播到结果池 2 中的蓝色激活。这就是合并图层增强平移不变性的原因,输入中的小平移不会改变输出中的值。
图 14 显示了卷积步长、ReLU 函数偏差、合并大小和合并步长的不同组合的效果。在左边,有三个步幅的例子:2、9 和 16。对于步幅的每个选项,有三个偏差示例:500、-250 和-1000。对于每种偏差,有三个汇集大小和步幅的示例:汇集大小为 3×3,步幅为 2,汇集大小为 5×5,步幅为 3,汇集大小为 7×7,步幅为 4×4。
Figure 14–Effects of different hyperparameters in convolution, ReLU and max pooling.
卷积中的步幅和最大池中的步幅的效果是累积的。当我们在卷积和最大池中使用步长 2 时,最终结果是特征图的宽度和高度大约减少了四倍。
卷积中的步幅值为 16,最大池中的步幅值为 4,这是不常见的。它们被故意夸大以说明它们对卷积层最终结果的影响。生成的特征图只有 100 个元素(10 x 10),比具有 37636 个元素的特征图(194 x 194)小得多。
将碎片拼在一起
我们将回到 AlexNet,第一个著名的 CNN 架构。这是一个很好的实际例子,可以理解 CNN 的组件是如何一起工作的。AlexNet 的构建模块如图 14 所示。虚线金字塔表示使用来自输入的感受野或来自前一层的特征图执行卷积。大方框代表特征图,特征图内的小方框是感受野。这个 CNN 可以将物体分为 1000 种不同的类别。
Figure 15 — Architecture of AlexNet CNN.
这种架构的一个特点是它使用两个 GPU 进行训练。图 15 顶部的元素分配在一个 GPU 中,而底部的元素分配在另一个 GPU 中。
你可以在一些框架中得到 CNN 的训练,比如 PyTorch。比较好用。您还可以使用迁移学习将此架构应用于其他影像数据集。这需要一些数据处理来标准化和重新缩放图像,以符合 AlexNet 的要求,但值得一试。大概是这样的:
from torchvision import transformspre_transforms = transforms.Compose([
transforms.RandomResizedCrop(224),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
])
您可以在 PyTorch 中导入模型,如下所示:
from torchvision import modelsmodel = models.alexnet(pretrained=True)
您可以禁用卷积层的训练,并创建自己的分类器来覆盖原始的 AlexNet 全连接层:
from torch import nnfor param in model.parameters():
param.requires_grad = False # disable trainingfcLayersDict = OrderedDict([('fc1', nn.Linear(9216, 1024)),
('relu1', nn.ReLU()),
('fc2', nn.Linear(1024, 102)),
('output', nn.LogSoftmax(dim=1))
])
model.fcLayersDict = fcLayersDict
fc = nn.Sequential(fcLayersDict)
model.classifier = fc
除了 AlexNet,PyTorch 中还有其他模型可以用来对您自己的图像数据集进行分类,只要进行必要的定制。真的建议测试这些模型,甚至从零开始建立自己的 CNN,了解 CNN 是如何工作的。
即使您已经熟悉这里介绍的概念,我希望您至少可以从另一个角度来看它们。CNN 还有其他一些概念没有在这里讨论。如果你对 CNN 的某个方面感到好奇或不确定,请告诉我。
可视化神经网络的非线性
在这篇文章中,我将通过一个基本的例子来展示神经网络中非线性激活函数的能力。为此,我创建了一个人工数据集。每个数据点都有两个特征和一个类别标签 0 或 1。所以我们有一个二元分类问题。如果我们把这些特征称为 x₁和 x₂,那么这些数据在(x₂)-spacex₁)的情节如下:
这里,红点对应负类,蓝点对应正类。请注意,数据不是线性可分的,这意味着没有线来分隔蓝点和红点。因此,对于给定的特征表示,线性分类器没有用。现在,我们将训练一个具有两个单元和非线性tanh
激活函数的一个隐藏层的神经网络,并可视化该网络学习的特征。
为了创建模型,我将使用 Tensorflow 2.0 和tf.keras
:
inputs = tf.keras.Input(shape=(2,))
x = tf.keras.layers.Dense(2, activation=tf.nn.tanh)(inputs)
outputs = tf.keras.layers.Dense(1, activation=tf.nn.sigmoid)(x)
model = tf.keras.Model(inputs=inputs, outputs=outputs)model.compile(optimizer='adam',
loss='binary_crossentropy',
metrics=['accuracy'])
让我们将隐藏层的权重和偏差表示如下:
权重和偏差的初始值定义了(x₁,x₂)-space,
训练前的这些初始行以及数据如下所示:
Data and the initial lines defined by the hidden layer in (x₁,x₂)-space
请注意,这两个初始行没有很好地划分给定的两个类。现在让我们训练模型,看看这些线会发生什么。
model.fit(x_train, y_train, batch_size = 16, epochs=100)Epoch 100/100
400/400 [==============================] - 0s 80us/sample - loss: 0.1116 - accuracy: 1.0000
在 100 个时期后,我们的训练准确度是 100%,因此模型正确地分类了训练数据中的所有点。
注意,您可以通过使用model.weights
获得 keras 模型的参数,它返回一个权重和偏差列表:
[<tf.Variable ‘dense/kernel:0’ shape=(2, 2) dtype=float32, numpy= array([[-3.2753847, 3.2302036], [ 3.3264563, -3.2554653]], dtype=float32)>, <tf.Variable ‘dense/bias:0’ shape=(2,) dtype=float32, numpy=array([1.5562934, 1.5492057], dtype=float32)>, <tf.Variable ‘dense_1/kernel:0’ shape=(2, 1) dtype=float32, numpy= array([[2.625529], [2.670275]], dtype=float32)>, <tf.Variable ‘dense_1/bias:0’ shape=(1,) dtype=float32, numpy=array([-2.0918093], dtype=float32)>]
此列表中的前两个元素是隐藏层的权重和偏差,最后两个元素是输出层的权重和偏差。所有这些元素都是张量,您可以使用 numpy()方法获得 numpy 数组形式的值。例如,下面将以 numpy 数组的形式给出隐藏层的权重,
model.weights[0].numpy()array([[-3.2753847, 3.2302036], [ 3.3264563, -3.2554653]], dtype=float32)
第一行的系数是-3.27 和 3.32;第二行的系数是 3.23 和-3.25。你可以通过运行model.weights[1].numpy()
看到他们的偏见
让我们想象由学习到的隐藏层的权重和偏差定义的线,
The data and the lines defined by the hidden layer after training in (x₁,x₂)-space
正如您现在所看到的,这些线以一种将类包含在独立区域中的方式划分了空间。这些线的系数告诉我们每条线的正面。对于蓝线,正方向是上侧,对于橙线,正方向是下侧。所以蓝点在两条线的正侧,红点在一条线的负侧,另一条线的正侧。
现在让我们应用非线性激活函数tanh
并在新的特征空间(a₂).a₁)中可视化数据隐藏层的激活计算如下,
对于每个数据点,tanh
的参数由该数据点相对于上述线的位置决定。我们可以认为 a₁和 a₂是新的特征,a₁,a₂)-space 是新的特征空间。输出层的权重和偏差在这个新的特征空间中定义了一条线,由以下等式给出
这条线和这个新特征空间中的数据一起绘制如下,
The features learned by the model in (a₁, a₂)-space and the line defined by the output layer.
请注意,在这个新的特征空间中,我们的数据变得可线性分离,由输出图层定义的线将两个类分开。蓝点的 a₁和 a₂坐标都是正的,因为(x₁,x₂)空间中的这些点位于由隐藏层参数定义的两条线的正侧,并且在应用tanh
后,两个坐标都是正的。对于红点,a₁和 a₂中的一个是正的,因为在 x₂)-space 的 x₁中,红点仅在由隐藏层参数定义的一条线的正侧,并且根据这条线,它们在新特征空间中只有一个坐标是正的,而另一个是负的。这解释了上图(a₁,a₂)-space)中的数据图。
**结论:**神经网络学习数据的新表示,这使得相对于该新表示进行分类变得容易。
这里有一个链接指向我在这篇文章中使用的 google colab 笔记本。
感谢阅读。
使用 Python 和 IBM Watson 可视化任何电影角色的个性特征
一个简单的数据收集指南,光预处理和任何电影角色的五大性格特征的可视化
Photo by Marko Blažević on Unsplash
伟大的社会心理学家詹姆斯·彭尼贝克曾经说过:“通过更仔细地观察人们用语言表达思想的方式,我们可以开始了解他们的个性、情感以及与他人的联系。”他和许多其他心理学家、语言学家和自然语言处理(NLP)从业者一起,利用双向 LSTMs 和 NLU (自然语言理解)等先进技术,在从书面文本中推断详细(和惊人准确)的个性信息方面取得了巨大进步。
最近,IBM Watson 背后的先锋团队开发了一个名为 Personality Insights 的产品,旨在为业务用例分类个性。在产品主页上有这样一句标语:“通过文字预测个性特征、需求和价值观。从个人层面和整体层面了解客户的习惯和偏好。“虽然商业含义很明显,但我认为利用虚拟人物塑造平台,通过电影主角的口语对话对他们的性格特征进行分类,这将是一件有趣的事情。
数据
为了这个项目,我利用了来自加州大学圣克鲁斯分校的大型电影语料库。语料库按类型细分,包含 960 个电影剧本,其中电影中的对话与场景描述分离。
为了让数据为分析做好准备,需要进行大量的清理和预处理。此外,数据中存在一些结构性挑战。也就是说,需要将每个引用/行与相应的字符进行分解和关联。
出于本文的目的,我不会深入到整个预处理代码中(对于那些感兴趣的人,完整的笔记本可以在这里找到)。然而,我将举例说明我们如何将人物/说话者从他们的对话中分离出来,然后将它们压缩成一个数据帧。
接下来,我们需要将所有与每个角色相关的口语对话组合成一个列表。以下代码的输出将为我们提供由逗号分隔的每个角色的完整对话:
IBM 沃森
Photo by Vasundhara Srinivas on Unsplash
从这里开始,我们的对话就可以输入到我们的 IBM Watson 笔记本中了。请注意,在 IBM Cloud 上注册需要很多步骤。你需要开一个账户,生成一个 API 密匙,还要下载 python SDK 。虽然这些步骤非常简单,但我将为以后的文章保存设置(或者, IBM 网站上的文档相对容易理解)。
一旦我们有了凭证,为列表中的每个字符获取可视化效果的过程就非常简单了。我们是这样设置的:
上面代码的输出将会显示出我们想要分类的角色的非常详细的个性特征。我们将看到输入文本中的字数,五大特征中每一个的百分位数,需求,以及五个主要特征中每一个的详细子特征(称为孩子)。
然而,作为数据科学家,我们不想呈现一个枯燥的读数。相反,我们希望通过一些有吸引力的条形图来形象化这些特征。我将列出这样做的代码,然后输出现代最具代表性的电影角色之一的结果:灰袍甘道夫(和白人!).
想象甘道夫的人格
Photo by Thomas Schweighofer on Unsplash
现在我们已经有了自己的功能,我们可以将甘道夫的全部对话输入到我们的 IBM 笔记本中,并看到他的人格形象。
Big Five profile of Gandalf
The needs of Gandalf
有意思!我们可以看到甘道夫是最开放的,大约在 98%左右。另一方面,他和他们一样内向,对在黑暗的图书馆里研究符文和在莫莉亚矿井下与巴罗格战斗更感兴趣。
至于需求,并没有很大的信号。甚至他的最高需求(好奇与和谐)也没有达到 50%。我想这是有道理的。除了偶尔需要一只鹰从艾辛格塔顶救他们之外,大部分巫师都是自给自足的!(遗憾的是,在 IBMs 服务的这个迭代中没有“eagles”需求列)
除了这一大部分,我们还可以放大甘道夫性格中的儿童特征。让我们看看我们发现了什么:
Openness
Conscientiousness
Extraversion
Agreeableness
Emotional Range
更多有趣的发现!我们可以看到甘道夫在以下特质上处于第 80 到第 100 百分位之间:想象力、智力、艺术兴趣、利他主义、谦虚、同情和忧郁。类似地,我们看到合群和外向的特质的结果很低。
虽然这个程序只能考虑对话而不能考虑动作,但他们确实感觉与指环王电影中甘道夫的角色非常一致。的确,我相信甘道夫本人,如果他能够看到这些结果,会有如下反应:
giphy.com
结论
好了,你知道了。现在,您已经掌握了构建自己的性格分类器的知识,并可以输出一些漂亮、清晰的可视化效果,达到相当高的准确度。
非常令人兴奋的是,我们能够根据个人的书面文本提取出如此详细的信息,虽然这个项目比任何事情都有趣,但这样一个平台的实际应用既重要又多样。例如,我们可以输入一个人的 Twitter 账号,然后输出一个类似的详细而准确的个人资料。虽然这确实令人兴奋,但这也引发了一些关于隐私和我们如何使用这些数据的道德问题。营销人员可以(并且已经)开始从我们的个人资料中提取详细的个性信息,并利用这些信息向我们投放高度个性化的广告和“新闻”文章。
然而,我看到了这种技术更令人兴奋的应用。作为一名体验设计师,我认为未来我们可以使用这种个性分类器来制作定制的交互式叙事内容,并让个人接触到与他们的个性相当独特的特征,以拓宽他们自己的观点。
这是我将在下一个项目中探索的,所以请继续关注。此外,如果你有兴趣深入了解这个项目的步骤和代码,可以看看我的 Github repo。最后,在不久的将来,我将发布一个分步指南,介绍如何使用 python 和 IBM Watson,以及如何使用 NLP 和 LDA、NMF 等降维技术,按类型将相同的电影剧本语料提取为简单的主题。