TowardsDataScience-博客中文翻译-2021-三十三-
TowardsDataScience 博客中文翻译 2021(三十三)
使用 Python 在测井图上显示岩性数据
使用 matplotlib 中的 fill_betweenx()为地质岩性数据添加可变颜色填充和阴影
测井曲线,伽马射线、中子孔隙度和体积密度数据与岩性数据一起绘制。作者创造的形象。
将岩性信息添加到测井曲线可以增强岩石物理或地质解释。它可以用来理解为什么一些日志响应会有这样的行为。该数据可能来源于之前的矿物学解释或泥浆测井。
在我的上一篇文章中:用绘图填充增强测井曲线的可视化,我们看到了:如何在曲线和轨迹边缘之间应用固定颜色填充,如何应用密度-中子交叉填充,以及如何根据绘制的曲线值应用可变填充。
在本文中,我们将介绍如何使用一个可变的填充来应用颜色和阴影。使用这两个选项,我们可以生成岩性填充。
这篇文章是我的 Python &岩石物理学系列的一部分。完整系列可以在这里找到。对于下面的例子,你可以在我的 GitHub 知识库中找到我的 Jupyter 笔记本和数据集,链接如下。
https://github.com/andymcdgeo/Petrophysics-Python-Series
接下来,Jupyter 笔记本可以在上面的链接中找到,这篇文章的数据文件可以在 Python &岩石物理学库的数据子文件夹中找到。
在测井图上设置和显示岩性填充
设置库
第一步是引入我们将在本文中使用的库。我们将只使用两个库: pandas 和 matplotlib 。这将允许我们将数据加载到数据框中,并在图上显示出来。
导入熊猫和 matplotlib。
加载数据
我们将在本文中使用的数据集来自 Xeek 和 FORCE(https://xeek.ai/challenges/force-well-logs/overview)最*举办的岩性预测机器学习竞赛。竞赛的目的是从由 98 口训练井组成的数据集中预测岩性,每口井的测井完整性程度不同。目的是根据测井测量预测岩相。要下载该文件,请导航到上面链接的数据部分。
可以使用pd.read_csv()
加载数据。由于这是一个相当大的数据集,我们将使用其中的一个单独的井来处理,我们还将只取我们需要的曲线(列)。我们可以通过这样的数据子集来实现:
为单井的特定测井曲线过滤 pandas 数据帧。
为了简单起见,我们将把FORCE_2020_LITHOFACIES_LITHOLOGY
列重命名为LITHOLOGY
。这是通过在数据帧上使用 rename 函数并传递旧名称和新名称的字典来完成的。注意,使用inplace=True
参数将替换原始数据帧中的列名。
重命名熊猫数据框架中的列。
当我们使用data.head()
调用我们的数据框架时,我们可以看到我们的数据集很小,我们的岩性列已经被重命名。
我们的熊猫数据框的前五行包含伽马射线(GR)、体积密度(RHOB)、中子孔隙度(NPHI)和岩性。
使用嵌套字典设置岩性
岩性栏中当前列出的岩性包含一系列数字。我们可以将它们映射到一个嵌套字典中,在这个字典中,我们有岩性的全名、简化的数字(如果需要转换的话)、阴影样式和填充的颜色。
这些颜色是基于堪萨斯地质调查局的网站。但是,在 matplotlib 的默认设置中,剖面线符号受到限制。在未来的文章中,我将介绍如何创建自定义阴影和岩性。
包含岩性信息的 python 嵌套字典。
我们可以像这样使用 from_dict 函数,快速地将我们的嵌套字典转换成 pandas 数据帧,使人们更容易阅读。
嵌套字典到熊猫数据框架的转换。
这将返回一个格式良好的表格,我们可以看到每个岩性代码的颜色和阴影。
从嵌套字典生成的熊猫数据帧。
仅仅看字典,很难理解这些单词的样子。为了解决这个问题,我们可以快速创建一个简单的图像,显示每种岩性的不同阴影和颜色。
为此,我们将首先为两个新变量 x 和 y 定义一些坐标。然后,我们将使用子图形设置 matplotlib 图形和轴:
fig, axes = plt.subplots(ncols=4, nrows=3, sharex=True, sharey=True, fig size=(10,5), subplot_kw={'xticks':[], 'yticks':[]})
这允许我们定义绘图的大小(4 列乘 3 行)、图形大小,并关闭两个轴上的刻度线。
然后,我们希望遍历展*的轴和嵌套的字典,将数据和阴影颜色填充添加到每个子情节中。当我们处理一个嵌套的字典时,我们需要多次使用方括号[ ][ ]来进入两个层次。第一层是键,第二层是嵌套字典的键。例如,要检索颜色,我们可以通过键入lithology_numbers[30000]['color']
来访问它
循环遍历嵌套字典中的每一项并将其绘制在子情节上的简短代码片段。
这将生成下面的图例,我们稍后可以参考它。请注意,岩性符号并不 100%匹配堪萨斯地质调查局的图表。我们将在以后的文章中看到如何解决这个问题。
使用 matplotlib 的颜色和阴影的图形表示。作者创建的图像。
用岩性轨迹建立测井曲线
在以前的文章中(用绘图填充增强测井曲线的可视化和用测井曲线数据进行勘探数据分析),我已经动态地设置了绘图并传入了我正在处理的数据帧。在本文中,我将创建一个简单的 makeplot 函数,我们可以将数据帧传递给它。
假设曲线名称相同,这使我们可以轻松地将代码重新用于其他数据帧。这可以进一步完善,使其更加通用。这个新函数接受三个参数,dataframe、top_depth 和 bottom depth。
该代码将生成一个有 3 条轨迹的测井曲线,一条用于伽马射线,一条包含中子孔隙度和体积密度,最后一条包含我们的地质岩性数据。
一个 python 函数,用于绘制包含伽马射线、体积密度和中子孔隙度以及岩性的测井图。
确保 matplotlib 中的岩性fill_betweenx
函数创建可变填充的关键代码如下所示。我们所做的就是遍历字典中的每个键,为字典中的特性分配简单的变量名,然后使用fill_betweenx
函数检查我们的岩性列中的值是否等于字典键。如果是这样,那么它会将相关的颜色和阴影应用到填充中。然后,它将移动到下一个键并重复,直到所有键都循环通过。
循环通过嵌套字典并基于岩性数据应用可变填充。
显示测井曲线和岩性
一旦我们建立了一个函数图。我们可以简单地传递我们的数据框架和我们想要绘制的深度。在本例中,我们将重点关注一小部分数据,以便能够看到绘图填充的变化。
调用 makeplot 函数。
一旦我们这样做了,下面的情节就会出现。
matplotlib 生成的测井曲线,地质岩性数据覆盖 2600 至 3100 米。作者创造的形象。
要更详细地查看岩性填充,我们可以通过更改深度范围值来放大。
matplotlib 生成的测井曲线,地质岩性数据覆盖 2900 至 3100 米。作者创造的形象。
摘要
在本文中,我们介绍了如何使用 matplotlib 的fill_betweenx()
函数设置和显示岩性数据。我们看到了如何使用嵌套字典来存储每个岩性的参数,然后使用 for 循环调用它来填充正确的颜色和阴影。
将这些数据添加到绘图中可以增强地球科学家或岩石物理学家将电测井测量与地质相关联的方式。
感谢阅读!
如果您觉得这篇文章很有用,请随时查看我的其他文章,这些文章介绍了 Python 和测井数据的各个方面。你也可以在GitHub找到我在这篇文章和其他文章中使用的代码。
如果你想联系我,你可以在LinkedIn*或者在我的* 网站 找到我。
有兴趣了解更多关于 python 和测井数据或岩石物理学的知识吗?跟我上 中 。
参考
堪萨斯地质调查局,岩性符号。http://www . kgs . ku . edu/PRS/Ozark/PROFILE/HELP/DATA _ ENTRY/岩性/岩性-符号. html
“岩相数据由 FORCE Machine Learning competition 提供,测井和地震 2020 年”Bormann P .,Aursand P .,Dilib F .,Dischington P .,Manral S. 2020 年。2020 原力机器学习大赛。https://github . com/bolgebrygg/Force-2020-机器学习-竞赛
在 Python 中显示随钻测井(LWD)图像日志
利用 matplotlib 的强大功能显示井筒图像数据
使用 Python 中的 matplotlib 显示随钻测井图像数据。作者创造的形象。
介绍
钻孔图像测井是从不同的测井测量/工具生成的钻孔壁的假彩色伪图像。电缆测井和随钻测井(LWD)获取井眼图像的方式不同。在电缆环境中,测量是通过压在井壁上的衬垫上的按钮进行的,覆盖范围有限,但分辨率很高。相比之下,在 LWD 环境中,测量是由构成钻柱/工具组件一部分的工具中内置的传感器进行的,并使用工具旋转,提供完整的 360 度覆盖范围。LWD 图像数据通常被分成扇区,扇区的数量根据工具技术的不同而不同。随着工具的旋转,数据被收集到相关的扇区中,由此我们可以建立井壁的伪图像。
生成的图像通常以二维形式显示在测井图上,如“展开的钻孔”和上图所示。圆柱形钻孔在垂直井中沿着北方位角切割,或者在斜井/水*井中沿着钻孔的高边切割。作为投影到 2D 表面的结果,任何与钻孔相交的*面要素在图上都表示为正弦曲线形状。通过分析这些正弦曲线的振幅和偏移,地质学家可以了解地下的地质结构。钻孔图像数据也可用于识别和分类不同的地质相/结构、识别薄层、断层和裂缝分析等。
在本文中,我将使用 Python 和 matplotlib 显示方位伽马射线和方位密度测量的随钻测井图像数据。
这篇文章是我的 Python &岩石物理学系列的一部分。详情可在这里找到。对于下面的例子,你可以在我的 GitHub 知识库中找到我的 Jupyter 笔记本和数据集,链接如下。
https://github.com/andymcdgeo/Petrophysics-Python-Series
接下来,本文的数据文件可以在 Python &岩石物理库的数据子文件夹中找到。
加载和显示 LWD 图像数据
设置库和加载数据
在我们开始处理数据之前,我们需要导入一些库来处理。对于本文,我们将使用熊猫、 matplotlib 、 numpy 和 lasio 库。这三个库允许我们加载 las 数据、使用它并创建数据的可视化。
显示 LWD 影像数据所需的 Python 库加载。
导入油井和测量数据
我们使用的数据集来自可公开访问的荷兰 NLOG 数据库。las 文件混合了 LWD 测量值和两幅图像。由于 las 文件是*面的,不支持阵列,LWD 影像数据通常以单个扇区的形式交付。我们将加载的第二个文件是调查,这使我们能够了解井眼的偏差和方位角,并有助于计算准确的地层倾角。在这篇文章中,我们将不讨论倾角调整。
我们将首先使用 lasio 加载 las 文件,并使用.df().
将其转换为数据帧
加载图像 las 文件并将其转换为熊猫数据帧。
一旦数据被加载,我们可以通过使用.describe()
来确认我们所拥有的数据。由于文件中有大量的曲线,我们可以调用df.columns()
查看完整的曲线列表:
检索熊猫数据帧中的列。
它返回:
Index(['APRESM', 'GRAFM', 'RACELM', 'RPCELM', 'RACEHM', 'RPCEHM', 'RACESLM', 'RPCESLM', 'RACESHM', 'RPCESHM', 'RPTHM', 'NPCKLFM', 'DPEFM', 'BDCFM', 'DRHFM', 'TVD', 'BLOCKCOMP', 'INNM', 'ROP_AVG', 'WOB_AVG', 'TCDM', 'ABDCUM', 'ABDCLM', 'ABDCRM', 'ABDCDM', 'ABDC1M', 'ABDC2M', 'ABDC3M', 'ABDC4M', 'ABDC5M', 'ABDC6M', 'ABDC7M', 'ABDC8M', 'ABDC9M', 'ABDC10M', 'ABDC11M', 'ABDC12M', 'ABDC13M', 'ABDC14M', 'ABDC15M', 'ABDC16M','ABDCM', 'GRAS0M', 'GRAS1M', 'GRAS2M', 'GRAS3M', 'GRAS4M', 'GRAS5M', 'GRAS6M', 'GRAS7M', 'GRASM'], dtype='object')
如简介中所述,我们可以看到方位密度图像被分成 16 个单独的扇区,标记为 ABDC1M 至 ABDC16M。方位伽马射线图像被分成 8 个扇区,并标记为 gras 0 到 gras 7。
调查数据包含在 csv 文件中,可以按如下方式加载。
将油井测量数据加载到数据框架中。
当我们从数据帧中读取列时,我们发现有三条曲线:测量深度(深度)、井斜(DEVI)和井眼方位角(AZIM)。我们将使用这些曲线在我们的图上进行额外的可视化。
在我们绘制数据之前,我们将通过将主数据帧分割成两个较小的数据帧来简化数据的处理。我们可以通过调用 dataframe (df)并提供我们想要提取的曲线名称列表来实现这一点。
将主数据帧细分成方位伽马射线和方位密度图像。
绘制图像数据
方位密度图像
绘制图像数据相当简单。我们首先必须使用plt.figure(figsize=(7,15))
创建一个人物对象。figsize 参数允许我们以英寸为单位控制最终绘图的大小。
我们也可以选择为最小和最大深度创建两个新变量。这些用于定义绘图区域的范围。由于我们的数据帧的索引包含我们的深度值,我们可以创建两个新变量miny
和maxy
,它们等于最小和最大索引值。
使用 matplotlib 中的 imshow()绘制方位密度数据,无需插值。
当我们执行代码时,我们生成了一个图像,向我们展示了我们的特征,但同时,我们可以看到它看起来有点块状。如果你看得足够*,你将能够分辨出图像的各个部分。
使用 matplotlib imshow()绘制的方位角密度图像,无插值。
我们可以对图像进行一些插值,使其*滑。在下面的示例中,插值已更改为双线性。
使用带有双线性插值的 matplotlib imshow()绘制的方位密度图像。
您可以在 matplotlib 文档中找到插值选项的完整列表。为了说明不同的方法,我使用了文档中的代码来生成显示不同选项的网格。
在方位密度图像数据上比较不同的 imshow()插值方法。
imshow()和方位密度图像数据的不同类型的插值。
你可以从上面的图像中看到,一些(lanczos 和 sinc)看起来比其他的稍微清晰一些。当处理图像数据时,选择将最终取决于用户偏好。
方位伽马射线图像
我们可以重复上述代码,通过将数据帧从 azidendf 更改为 azigamdf 来生成我们的方位伽马射线图像。
使用 matplotlib imshow()绘制的方位伽马射线图像。
当我们查看我们的图像时,我们注意到与密度图像相比,细节层次明显较少。这与测量类型和记录的较低扇区数有关。
构建最终情节
现在,我们可以使用支线图将我们的图像记录和勘测数据绘制在一起。subplot2grid((1,3),(0,0))
方法允许我们设置绘图的形状,在本例中,我们绘制 1 行 3 列。然后,我们将 ax1、ax2 和 ax3 分配给函数中第二组数字表示的三列。因为我们在与方位角相同的轨道上绘制偏差,我们需要使用ax.twiny()
在现有轴的顶部添加一个新轴。
为了绘制我们的图像数据,我们可以为每个图像使用前面部分中的代码,而不是将其分配给绘图,我们可以将它们分配给子绘图轴。
用于绘制方位密度图像、方位伽马图像以及油井勘测数据的 Python 代码。
方位密度和方位伽马射线数据的 Matplotlib 图,与井斜和方位一起绘制。
现在,我们可以一起看到两幅图像,我们还可以考虑我们的钻孔偏差,这表明我们处于水*段。了解这一点很重要,尤其是在从图像测井计算任何倾角时。
你可能会注意到,2500 点处的河床似乎在两个地块之间发生了偏移,需要进一步调查。这将不在本文中讨论。
摘要
在本文中,我们介绍了如何加载和显示来自 LWD 方位伽马射线和密度测量的井眼成像测井。一旦数据被分成各自的数据帧,很容易将它们传递到 matplotlib 中的imshow()
图。从这些图像数据中,地质学家和岩石物理学家可以更好地了解地下的地质构成。
“所有图片由作者生成”
感谢阅读!
如果您觉得这篇文章很有用,请随时查看我的其他文章,这些文章介绍了 Python 和测井数据的各个方面。你也可以在GitHub找到我在这篇文章和其他文章中使用的代码。
如果你想联系我,你可以在LinkedIn*或者在我的* 网站 找到我。
有兴趣了解更多关于 python 和测井数据或岩石物理学的知识吗?跟我上 中 。
用 NoPdb 剖析 ML 模型
实践教程
编程 Python 调试器 NoPdb 指南,以及可视化视觉转换器(ViT)注意力的应用
图片作者。
调试机器学习模型与调试“传统”代码非常不同。在深度神经网络中,我们必须处理大的特征图和权重矩阵,这些通常看起来没有意义。随着 ML 可解释性的日益重要,已经设计出了几种分析这些内部表示的方法,但是在实践中,获得它们并不总是简单明了的。像 Pdb 这样的经典调试器可能会有所帮助,但是用它来进行可视化和分析至少是不方便的。
虽然 PyTorch 等一些框架通过允许将挂钩附加到网络的层来解决这一问题,但这仅适用于我们感兴趣的功能作为特定层的输入或输出的情况。如果我们想要访问在某个函数中只作为局部变量可用的信息——例如在现在无处不在的变形金刚的许多实现中的注意力权重——我们就不走运了。
还是我们?
认识 NoPdb
NoPdb (声明:我是作者)是一个非交互式 Python 调试器。与标准 Python 调试器 Pdb 不同,NoPdb 没有交互式用户界面,但可以使用方便的上下文管理器对其进行编程———以在给定的代码段运行时执行特定的操作。例如,我们可以使用它轻松地从其他人的代码深处获取一个局部变量,并保存它以供以后分析,甚至可以动态地修改它的值,看看会发生什么。
我们将在这里使用的基本功能由nopdb.capture_call()
和nopdb.capture_calls()
提供(参见文档)。这些上下文管理器允许捕获关于给定函数调用的有用信息,包括参数、局部变量、返回值和堆栈跟踪。一个更强大的上下文管理器是[nopdb.breakpoint()](https://nopdb.readthedocs.io/en/stable/getting-started.html#setting-breakpoints)
,当到达给定的代码行时,它允许执行用户定义的动作(例如计算表达式)。
解剖视觉转换器
为了查看 NoPdb 的运行情况,我们将把它应用到一个 视觉转换器 ( ViT )。ViT 是最*提出的完全基于 Transformer 架构的图像分类模型。如下图所示,其主要思想相当简单:将输入图像分割成小块,让每个小块通过一个线性层,然后对这个“小块嵌入”序列应用一个标准的 Transformer 编码器。为了进行分类,使用一个常见的技巧:我们在输入序列的开头添加一个特殊的[class]
标记,并在编码器输出的相应(第一个)位置附加一个分类头(单层 MLP)。
视觉转换器(ViT)。作者绘图。
虽然我们不太关心这篇文章中架构的细节,但我们确实需要知道模型的每一层都包含一个注意机制,它为的每一对输入位置(即图像块,加上[class]
令牌)计算一个权重(一种相似性得分)。就像我们现在要做的,可视化这些权重可以给我们一个线索,告诉我们图像的哪些部分对模型最重要。
我们将使用来自 pytorch-image-models 库的timm
包中预先训练好的 ViT,并且我们将跟踪这个 Colab 笔记本(笔记本最重要的部分都包含在这里)。
运行 ViT
让我们首先安装timm
计算机视觉包,以及 NoPdb :
pip install timm==0.4.5 nopdb==0.1
加载预训练模型很容易:
现在,我们将加载一个图像并将其提供给模型。让我们试试这张我在瑞士拍的照片:
瑞士阿姆登的奶牛。作者照片。
模型返回的是所有类的逻辑值(前 softmax 值)。在笔记本里,我写了一个小函数predict()
,打印出最有可能的类及其概率。打电话给predict(input)
给:
alp 0.7936609983444214
ox 0.1110275536775589
valley 0.029854662716388702
oxcart 0.008171545341610909
ibex 0.008044715970754623
视觉化注意力
现在让我们看看模型的内部!ViT 由 12 个blocks
组成,每个包含一个attn
层;这是计算注意力权重的地方:
VisionTransformer(
(patch_embed): PatchEmbed(
(proj): Conv2d(3, 768, kernel_size=(16, 16), stride=(16, 16))
)
(pos_drop): Dropout(p=0.0, inplace=False)
(**blocks**): **ModuleList**(
(0): Block(
(norm1): LayerNorm((768,), eps=1e-06, elementwise_affine=True)
(**attn**): **Attention**(
(qkv): Linear(in_features=768, out_features=2304, bias=True)
(attn_drop): Dropout(p=0.0, inplace=False)
(proj): Linear(in_features=768, out_features=768, bias=True)
(proj_drop): Dropout(p=0.0, inplace=False)
)
(drop_path): Identity()
(norm2): LayerNorm((768,), eps=1e-06, elementwise_affine=True)
(mlp): Mlp(
(fc1): Linear(in_features=768, out_features=3072, bias=True)
(act): GELU()
(fc2): Linear(in_features=3072, out_features=768, bias=True)
(drop): Dropout(p=0.0, inplace=False)
)
)
...
(norm): LayerNorm((768,), eps=1e-06, elementwise_affine=True)
(pre_logits): Identity()
(head): Linear(in_features=768, out_features=1000, bias=True)
)
假设我们想将注意力可视化在第 5 个区块,即model.blocks[4]
。查看 [Attention](https://github.com/rwightman/pytorch-image-models/blob/v0.4.5/timm/models/vision_transformer.py#L168-L180)
层的代码,我们可以发现一个名为attn
的变量,它正是我们要寻找的注意力张量:
为了得到它的值,我们将使用nopdb.capture_call()
上下文管理器来捕获对注意力层的forward
方法的调用:
瞧——attn_call
对象现在包含了一堆关于调用的有用信息,包括所有局部变量的值!让我们看看它们是什么:
检查attn_call.locals['attn']
,我们可以看到它是一个形状为[1,12,197,197]的张量,其中 1 是批量大小,12 是注意头的数量,197 是图像块的数量+ 1 用于[class]
令牌(记住,注意机制为每对位置计算一个权重)。
我们可以用不同的方法来分析这些注意力矩阵,但为了简单起见,我选择直观地显示每个补丁*均获得了多少注意力(针对每个注意力头):
(助手功能plot_weights
,只是显示图像,并根据其权重调整每个面片的亮度,可以在笔记本中找到。)
调用plot_attention(input, attn_call.locals[‘attn’][0])
为 12 个注意力头中的每一个产生一个图。以下是其中的一些:
第五个变形金刚模块中的*均注意力权重(对比度增加以便更好地观看)。每个补丁越亮,注意力权重越高。图片作者。
我们可以看到,一些头部倾向于主要关注图像中的特定对象,如奶牛(头部 8)或天空(头部 12),一些头部到处看(头部 2),一些头部主要关注一个看似随机的斑块,如背景中的一座山的一部分(头部 3)。
请记住,这只是一个有限的例子。我们可以通过使用 注意力卷展栏或注意力流 更进一步,这是估计单个输入面片如何对输出做出贡献的更好方法,我们可以以几乎相同的方式利用 NoPdb。
调整重量
NoPdb 可以做的另一件事是将代码插入到函数中。这意味着我们不仅可以捕获变量,还可以修改它们!我选了一个有点傻的例子来说明这一点:我们将在所有层中使用前 softmax 注意力权重,并将它们乘以 3。(这就像应用一个低的 softmax 温度,使分布更加“峰值化”。)我们当然可以通过编辑timm
包的代码来做到这一点,但是我们需要重新加载包和模型,这可能会很繁琐,特别是如果我们需要重复做的话。另一方面,NoPdb 允许我们快速地进行更改,而不需要重新加载。
我们再来看看Attention.forward()
:
我们希望将attn = attn * 3
放在 softmax 之前,即第 174 行。我们将通过在这一行设置一个“断点”并让它执行这条语句来实现这一点。(注意,NoPdb 断点实际上并不停止执行;相反,它只是执行一些我们给它的代码。)我们也要像之前一样捕捉第 5 块的局部变量。
请注意,我们没有指定行号(174),而是指定了该行的实际代码:line='attn = attn.softmax(dim=-1)'
—这只是一个方便的特性,并且line=174
(如同在传统调试器中一样)也可以工作。另外,请注意,由于我们将函数指定为Attention.forward
(而不是model.blocks[4].attn.forward
),断点将在每个关注层的处触发。
让我们看看这是如何改变预测的:
balloon 0.2919192612171173
alp 0.12357209622859955
valley 0.049703165888786316
parachute 0.0346514955163002
airship 0.019190486520528793
我们捕捉到的注意力模式:
和上面一样的情节,但是调整了注意力的权重。图片作者。
关于其他框架的说明
虽然这篇文章关注的是 PyTorch,但是 NoPdb 不依赖于任何特定的框架,可以用于任何 Python 3 代码。
也就是说,一些框架,如 TensorFlow 或 JAX,将模型编译成计算图或直接编译成计算内核,然后在 Python 之外执行,NoPdb 无法访问。幸运的是,在大多数情况下,我们可以出于调试目的禁用该功能:
- 在 TensorFlow 2.x 中,我们可以在执行模型之前调用
[tf.config.run_functions_eagerly(True)](https://www.tensorflow.org/api_docs/python/tf/config/run_functions_eagerly)
。(注意,这不适用于为 TensorFlow 1.x 编写的模型,这些模型被显式编译为图形。) - 在 JAX,我们可以使用
[disable_jit()](https://jax.readthedocs.io/en/latest/jax.html#jax.disable_jit)
上下文管理器。
一些 NoPdb 链接
剖析 Stockfish 第 1 部分:深入了解象棋引擎
Stockfish 是最好的现代国际象棋引擎之一,比 DeepBlue 强几个数量级。
Stockfish 象棋引擎,背景照片由 ᴊᴀᴄʜʏᴍ ᴍɪᴄʜᴀʟ 在 Unsplash 上拍摄
ost 国际象棋引擎有着相同的蓝图:抽象出一个国际象棋的位置,找到所有的候选棋步,遍历候选棋树直到一个给定的深度,并评估这些棋步的相关性以找到最好的一个。但是,它们的质量取决于以下标准:
- 速度:研究候选移动和树的迭代,
- 准确性:评估板。
这篇文章将仔细研究开源象棋引擎 Stockfish 12 的内部工作原理,并解释其成功的关键。本系列的第一部分将关注 Stockfish 如何提取头寸并生成候选人的移动。
比特板
国际象棋棋盘由 64 个方块组成,这意味着一个人可以在一个 64 位变量中存储一个给定棋子的位置。每个位对应一个方块,如果设置为 1 ,则该方块上有一个棋子。例如,下面的位棋盘代表游戏开始时白玩家的棋子。
在 Stockfish 中,bitboards 从 a1 方块开始,按等级进行,位置以小端编码方式存储,如上所述。通过这种方式,棋盘上的操作可以转化为位运算:
- 将棋子向左移动一格:左移 1 位,
- 将棋子向前移动一格:左移 8 位,
- 检索一个玩家的所有棋子:他所有棋盘的逻辑或,
- 验证一个方块是否被占据:棋盘和方块的位置掩码的逻辑与。
- …
作为一个例子,下面是 Stockfish 代码,它可以让方块受到一个 bitboard of 卒 (bitboard.h)的攻击:
使用Bitboard shift
寻找候选人
现在我们有了给定任何位置的棋盘的表示,国际象棋引擎必须为一个玩家找到所有的候选走法,以便建立所有可能走法的树。
作者图片
样板
在模式中移动的棋子(棋子、骑士和国王)的候选移动是在一组固定的基数中。例如,D4 的白衣骑士总是有以下候选人:
在包含所有骑士的位板上的位移操作被用于计算那些候选移动:
可能的移动是所有这些移动的或连接。然而,如果该骑士在一个侧面的行列(Ax,Hx,x1,x8),它不允许移动到棋盘之外。因此,用边列或文件掩蔽位棋盘将确保如果骑士处于这样的位置,将不会生成候选者。
Stockfish 实际上是在生成候选项后执行检查,并使用一个Bitboard safe _ destination(Square s,int step) (bitboard.cpp)方法来确保棋步不会离开棋盘。最后,还有一些额外的验证,如阻塞块或发现的检查。为此,Stockfish 有一个bool Position::legal(Move m)方法(position.cpp)来测试一个伪合法的移动是否真的合法。
滑动件
车、象和王后可以沿着一个方向移动无限数量的方格,直到它们到达棋盘的边缘,或者直到另一个棋子阻碍了它们的移动。使用预先计算的射线即时计算它们的候选移动是可行的。棋子的攻击射线与所有被占据的方格的位板进行“与”运算,使用一个名为bitscan的处理器级指令来寻找计算结果的第一个非*凡位。然而,该操作必须对每个片段的每个攻击射线重复,这导致了实时使用的计算密集型方法。
因此,Stockfish 使用了一种计算效率更高的方法,即在包含滑动棋子候选移动的数组中进行查找。为了生成在该数组中查找的索引,仍然需要找到滑动块的所有阻挡块:
然后,Stockfish 将使用阻挡板作为包含候选移动的预先计算的位板的数组中的索引:
这种方法提出了一个实际问题。由于阻挡器板是一个 64 位变量,包含候选移动的数组将具有 2⁶⁴元素,其大小约为 1 艾字节。由于不可能在内存中创建这么大的数组,Stockfish 将使用一个散列表来存储候选移动。由于存在大约 20,000 个不同的阻断板,hashmap 的大小只有几百千字节。关键的部分是找到一个散列函数,它将在我们的拦截器板空间中快速计算并且没有冲突。
为此,Stockfish 实现了一种叫做魔法位板的方法。目标是找到与特定棋子 p (车或象)和特定方块 s 相关的数字 λ(p,s) ,例如:
与原来的 blockers_board 相比,哈希键将具有更少的位(获得的位】):它可以用于索引我们的数组,使用合理的内存量。给定我们之前在 C7 方块上的车的阻挡器棋盘的例子,散列密钥生成将是:**
这是在相同方块上相同棋子的另一个散列密钥生成,但用于不同的阻断板:
现在,如何通过一个简单的乘法运算找到那些将攻击方块串联成特定位置的幻数 λ(p,s) ?使用一种暴力方法(遍历随机幻数并检查所有阻断板的结果),只需要几秒钟就可以找到合适的幻数。因此,Stockfish 在启动时使用一个种子随机数生成器预先计算所有的魔术数字,以便在最短的时间内选择正确的魔术(bitboard.cpp)。
本文展示了 Stockfish 如何能够抽象出一个棋盘,以及它如何生成候选移动来创建新的位置。Stockfish 选择正确走法的方法还没有出现。
作者图片
本系列的下一部分将关注 Stockfish 评估头寸质量的方法和标准。这种评估方法将有助于选择我们所学的最佳候选动作。
剖析 Stockfish 第 2 部分:深入了解象棋引擎
当神经网络遇到硬编码知识时
Stockfish 象棋引擎,背景照片由 ᴊᴀᴄʜʏᴍ ᴍɪᴄʜᴀʟ 在 Unsplash 上拍摄
这个系列的第一部分展示了 Stockfish 如何抽象出一个国际象棋的位置,以及它如何快速找到每一个可能的走法。现在,引擎必须评估一步棋的质量,以便选择最好的一步。
过去,Stockfish 只依赖一套固定的规则,这些规则是国际象棋概念的算法翻译:速度,材料,空间 …但在 2018 年,神经网络进入了许多国际象棋引擎,Stockfish 的表现优于。为了填补这一空白,Stockfish 12 集成了一个神经网络,当已知经典评估的性能较差时,该网络优先于经典评估,通常处于*衡的闭合位置。
本文将首先关注神经部分的内部工作,然后再研究经典的评估方法。
神经网络体系结构
神经评价函数基于俞那苏的 NNUE 架构(基于高效可更新神经网络的计算机 Shogi 评价函数,俞那苏,2018 )。这个微小的神经网络在不需要图形处理器的情况下评估 CPU 上的游戏位置:Stockfish 仍然可以很容易地集成到许多设备上,并由它的大量贡献者进行调整。
NNUE 的建筑,图片来自罗曼朱可夫,Stockfish NN Release (NNUE),Talkchess
输入编码
NNUE 不理解位板,必须首先对位置进行编码。NNUE 的输入参数表示如下布尔值,对每一个 W,X,Y,Z 迭代:
是正方形 X 上的一个王与正方形 Z 上的一个 W[友|敌]棋子 Y?
总共有 40,960 个参数。用这种二进制编码代替简单的“棋子 X 在正方形 Y 上吗?”编码(768 个参数)有几个好处:
- 在位置改变后更新输入和随后的神经元更快,因为这种编码允许增量计算,这是 NNUE 的优势;
- 改变玩家的回合也更快,因为输入只需要翻转;
- 在编码中提供比所需更多的信息被称为过度专门化,并且允许向网络输入更多的知识以降低其训练成本。
隐藏层和训练
该网络的目标是建立一个评价函数,该函数是由非线性分离的不同层的神经元的组合。该评估函数将根据其神经元的权重进行优化(训练阶段),以便在已知国际象棋游戏的情况下输出最准确的评估。
NNUE 训练的基本思想是建立一个巨大的随机生成位置的输入数据集,这些位置用经典的 Stockfish 在较低的深度进行评估。该网络使用该数据集进行训练,然后可以使用特定的位置和评估进行微调。就其本身而言,网络很简单,因为它由 3 个完全连接的层加上一个输出层组成,输出层可以表示为材料分数。
然而,NNUE 的真正独特之处在于它的输入编码和针对 CPU 评估优化的增量计算。
NNUE:使用 SIMD 内部函数进行大量优化(nnue/layers/clipped_relu.h)
经典评价
除了非常*衡的立场,Stockfish 仍然依赖于经典的评估。
这个想法是建立一个评估函数,作为国际象棋概念的组合,由几个加权和相加的标准组成。然后,该函数可以被缩放,以便用标准卒单位表示物质优势。
Stockfish 中的经典评价,图片来自作者
概念和相关标准如下:
- 材料不*衡:每个玩家的棋子数
- 位置优势:在特定的方格上有特定的棋子
- 材料优势:每件的强度,有一双毕肖普
- 兵的战略优势:双倍兵、孤立兵、连接兵、棋子支援兵、攻击兵
- 其他棋子的战略优势:被阻挡的棋子、在好哨站上的棋子、主教 X 射线攻击、长对角线上的主教、被困住的棋子、暴露的女王、被渗透的女王、在打开的文件上的车、车和女王炮台、被攻击的敌方国王
- 来袭威胁:攻击棋子、悬挂棋子、国王威胁、兵推威胁、发现/滑块攻击威胁、你的棋子可以移动但可以交换的方格、弱保护棋子
- 走卒:被阻挡或未被阻挡的走卒
- 空间:由你所有的棋子控制的方格
- 王者安全:攻击王者,来袭检查,王者在‘棋子避难所’,守军位置
这个巨大的结果评估函数实际上被计算到 2 阶,以基于玩家的已知攻击/防御状态来计算奖励/恶意。
Stockfish GitHub 存储库中的许多 pull 请求包含上述标准权重的微小变化,这导致了 ELO 分数的微小提高。
锥形缩放
不管我们是在开局、中局还是残局,不同标准的权重都是不同的。例如,一个被传递的棋子在游戏后期可能比中期更有价值。因此,Stockfish 对每个标准都有两个权重集:一个用于中期游戏,一个用于末期游戏。Stockfish 然后计算一个范围从 0(游戏结束)到 128(游戏未开始)的相位因子,这有助于在两个不同的权重之间进行插值。
中局和残局之间的锥形评估
然后,评估将再次缩放,以符合五十步规则:当接*达到该规则时,评估将更接**局。
个案研究
为了阐明经典的评估方法,我们将研究卡斯帕罗夫和托帕洛夫之间的一场比赛中的一个已知位置:
卡斯帕罗夫-托帕洛夫,Wijk aan Zee,1999 年。27:白棋
这个例子将集中于位置优势标准:在特定的方格上有特定的棋子。对于棋盘上的每个方格,分数是根据占据该方格的棋子来分配的(例如,在中局中,a5 上暴露的黑王的价值低于 g8,G8 是更安全的方格)。该分数奖励来自两个硬编码的正方形分数表:一个用于棋子,一个用于棋子。拥有两个不同得分表的原因是棋子的得分奖励在垂直轴上是对称的,而棋子(psqt . CPP)则不是。
if (typeof(piece) == PAWN) {
bonus += PBonus[rank][file];
}
else {
file = file < 4 ? file : 8 - file;
bonus += Bonus[piece][rank][file];
}
黑棋和白棋的分数表是一样的,因为只需翻转位板就可以计算黑棋的分数。例如,D4 上的白皇后在中局中值+8,在残局中值+24(如下所示),这与 D5 上的黑皇后是一样的。
棋子方桌奖金—中间游戏
棋子方桌奖金—结束游戏
棋子*方得分是棋盘上所有棋子的总和,通过中间游戏和结束游戏的棋子*方得分表计算得出(见上文)。然后,基于棋盘上非棋子材料的数量来计算渐变评估的相位因子。这里,相位因子等于 83。最后,使用该阶段因子计算在中间游戏和结束游戏棋子*方分数之间插入的最终奖金。
# MG: middle-game score
# EG: end-game score
# P: phase factorFINAL_BONUS = (MG * P + (EG * (P_MAX - P)) / (P_MAX-P_MIN))
最终白棋得分为+247,黑棋得分为+46:从 Stockfish 的角度来看,这个棋盘导致了白棋明显的位置优势。
结论
本系列的第一部分介绍了如何读取一个位置,以及如何根据这个位置生成候选移动。现在,第二部分解释了如何评估任何给定的位置,以便为每个玩家输出一个分数。
本系列的前两部分解释了 Stockfish 概念
然而,生成候选移动的每一个可能的组合并立即评估它们在性能上是不可思议的:随着我们搜索深度的增加,候选移动的数量呈指数增长。Stockfish 需要明智地选择它们,这将是本系列下一部分的主题。
激光传感器测距第一部分
易于使用的传感器,支持自动化系统中的机器学习和人工智能
距离测量是不同任务的最重要的测量之一,主要涉及但不限于机器人或其他自动系统。任务可以包括房间映射、公园传感器、速度测量、运动检测、3D 建模等等。
在这个简短的系列文章中,我将向您展示如何使用激光传感器 Hokuyo UTM-30LX-EW 测量距离,并将其与您的应用程序连接。在本文中,我们将创建一个使用传感器的设置,在下一篇文章中,您将学习如何将该传感器集成到您的定制应用程序中。
介绍
嗯,这是传感器!
激光测距传感器 UTM 北友-30LX-EW(图片来源:作者)
我们可以在正面看到基本部件:
- 电源指示灯—电源的指示(开/关)。
- 运行状态 led——不同状态下的不同行为(例如如果操作没有错误,则始终开启)。
- 光传输和接收*面—传感器在这个塑料盖内。它有两个*面,传输*面和接收*面。
背面呢:
- 以太网电缆——用于与计算机通信。
- 电源线——为传感器供电。它有三条线:棕色(DC 12V),蓝色(GND 0V),绿色(OUT)。传感器正常工作需要大约 1A。
- IP 重置开关—用于重置 IP 配置。
以下是与距离测量和传感器局限性相关的一些重要特征:
- 视野:270°
- 角度分辨率:0.25°
- 测量步长:1080 (270/0.25=1080)
- 最大值距离:30 米
在设置传感器之前,请查阅官方用户手册。
设置
1.电源
传感器通过一根电源线供电,电源线包括三根电线:棕色(DC 12V),蓝色(GND 0V)和绿色(OUT)。该传感器可以汲取 1A 的最大电流。我使用了一个旧的 PC 电源进行传感器测试。
2.沟通
在本文中,我们将展示如何用以太网电缆连接传感器。传感器的初始 IP 设置为 192.168.0.10。为了能够与传感器通信,PC 必须在同一个网络中。在 Windows PC 上,你可以按照这个教程来做。例如,您可以使用 IP 地址 192.168.0.100 和子网掩码 255.255.255.0
衡量
给设备供电并将其连接到 PC 后,我们可以开始测量距离了!
为此,我们将使用 Urg 浏览器应用程序。这是一个简单的应用程序,用于可视化测量和导出结果。使用以太网或串行连接(右上角的按钮)与传感器连接有两种选择。
连接到传感器后,距离测量值显示为表格(左*面)和蓝点(右*面)。在下图中,你可以看到我公寓里的距离测量结果。
距离测量示例(图片来源:作者)
结论
本文展示了使用激光传感器测量距离的用法和基本设置。你可以在许多项目中使用这种传感器,如报警系统、停车传感器或为移动机器人创建地图。
请继续关注本系列的下一篇文章,您将了解如何将该传感器集成到您的定制应用中。
如有任何问题或建议,欢迎评论或联系我!
领英:【https://www.linkedin.com/in/leo-tisljaric-28a56b123/
确认
该传感器由位于萨格勒布大学交通运输科学学院的 T4 智能交通系统实验室提供。
参考
- https://www.hokuyo-aut.jp/search/single.php?serial=170 北越 UTM-30LX-EW 用户手册
- 更改电脑的 IP 地址,https://support . Microsoft . com/en-us/windows/change-TCP-IP-settings-BD 0a 07 af-15 F5-cd6a-363 f-ca 2 b 6 f 391 ace
类伯特模型的提炼:理论
思想和理论
探索蒸馏方法背后的机制
类伯特模型的提取过程。图片作者。
如果你曾经训练过像 BERT 或 RoBERTa 这样的大型 NLP 模型,你就会知道这个过程是极其漫长的。训练这样的模型可能要拖上好几天,因为它们体积庞大。当需要在小型设备上运行它们时,你可能会发现,你正在为今天不断提高的性能付出巨大的内存和时间成本。
幸运的是,有一些方法可以减轻这些痛苦,而对你的模特的表现几乎没有影响,这种技术叫做蒸馏。在本文中,我们将探索 DistilBERT 方法[1]背后的机制,它可用于提取任何类似 BERT 的模型。
首先,我们将讨论蒸馏的一般情况,以及为什么我们选择 DistilBERT 的方法,然后如何初始化该过程,以及蒸馏过程中使用的特殊损耗,最后是一些相关的额外细节,可以单独提及。
摘要
一、DistilBERT
二简介。抄袭老师的架构
三。蒸馏损失
IV。更多详情
五.结论
一、蒸馏器简介
什么是蒸馏? 蒸馏的概念相当直观:它是训练一个小的学生模型尽可能地模仿一个更大的教师模型的过程。如果我们只在用于微调的集群上运行机器学习模型,蒸馏将毫无用处,但遗憾的是,事实并非如此。因此,每当我们想要将一个模型移植到更小的硬件上时,比如有限的笔记本电脑或手机,蒸馏就派上了用场,因为蒸馏模型运行更快,占用的空间更少。
BERT 蒸馏的必要性 正如你可能已经注意到的,基于 BERT 的模型在 NLP 中风靡一时,自从它们在[2]中被首次引入以来。随着性能的提高,出现了许许多多的参数。准确地说,伯特的收入超过了 1.1 亿英镑,我们甚至还没有谈论伯特-拉奇。因此,蒸馏的需要是显而易见的,因为伯特是如此的多才多艺和出色。此外,随后的模型基本上是以相同的方式构建的,类似于 RoBERTa [3],所以通过学习适当地提取 BERT,你可以一举两得。
蒸馏伯特的方法 第一篇关于伯特蒸馏的论文是我们要用来作为灵感的,即[1]。但其他人紧随其后,像[4]或[5],所以很自然地想知道为什么我们把自己局限于蒸馏。答案有三点:第一,它相当简单,所以是很好的蒸馏入门;二是导致好的结果;第三,它还允许对基于 BERT 的模型进行提炼。
DistilBERT 的蒸馏有两个步骤,我们将在下面详述。
二。抄袭老师的建筑
伯特的建筑。图片作者。
伯特的理论主要是基于一系列相互叠加的注意力层。因此,这意味着伯特学到的“隐藏知识”就包含在那些层中。我们不会关心这些是如何工作的,但是对于那些想要更多细节的人来说,除了原始论文[1],我可以推荐这篇 TDS 文章,它做得非常好[6]。现在,我们可以把注意力层当作一个黑盒,这对我们来说并不重要。
从一个 BERT 到另一个 BERT,层数 N 是变化的,但是当然模型的大小与 N 成比例。因此,训练模型所花费的时间和正向传递的持续时间也取决于 N,以及存储模型所花费的内存。因此提取 BERT 的逻辑结论是减少 N 。
DistilBERT 的方法是将的层数减半,并从老师的开始初始化学生的层数。简单而高效:
学生模型初始化。图片作者。
DistilBERT 在一个复制层和一个忽略层之间交替,根据[4]这似乎是最好的启发式,它尝试优先复制顶层或底层。
多亏了 huggingface 的变形金刚模块和对其内部工作原理的一点了解,这可以很容易地实现。我们将在另一篇文章中展示如何做到这一点,因为在这篇文章中我们将仅限于理论。
当然,如果你正在为一个特定的任务使用一个基于 BERT 的模型,比方说序列分类,那么你还需要为学生复制老师的头,但是一般来说,BERT 头的大小与其注意力层的大小相比就相形见绌了。
我们现在有了一个学生模型,可以开始教学了。然而,提炼过程并不是一个经典的拟合程序:我们不是像通常那样教学生学习一种模式,我们的目标也是模仿老师。因此,我们必须调整我们的训练程序,尤其是我们的损失函数。
三。蒸馏损失
本文顶部的图片展示了蒸馏程序。
我们的训练程序将基于损失,如前所述,这寻求实现几个目标:最小化老师训练的经典损失函数和模仿老师本身。更糟糕的是,模仿老师需要混合两种损失函数。因此,我们将从更简单的目标开始:最小化传统损失。
经典损失
关于这一部分没有太多要说的:类似 BERT 的模型都以同样的方式工作,一个核心输出一个嵌入到特定问题的头部。教师被微调的任务有它自己的损失函数。为了计算这个损失,因为这个模型是由注意力层组成的,和老师有着相同的特定问题的头脑,我们只需要插入学生的嵌入和标签。
师生交叉熵损失
交叉熵损失对两个三维矢量的影响。图片作者。
这是第一次损失,旨在减少学生和教师概率分布之间的差距。当类 BERT 模型正向传递输入时,无论是用于屏蔽语言建模、标记分类、序列分类等,它都会输出逻辑,然后通过 softmax 层转换为概率分布。
对于输入 x,教师输出:
学生的输出是:
请记住 softmax 和它附带的符号,我们稍后将回到它。无论如何,如果我们希望 T 和 S 接*,我们可以以 T 为目标对 S 应用交叉熵损失。这就是我们所说的师生交叉熵损失:
师生余弦损失
余弦损失对两个三维向量的影响。图片作者。
帮助学生成为大师的第二个损失是余弦损失。余弦损失是有趣的,因为它不是试图使向量 x 等于目标 y,而是试图将 x 与 y 对齐,而不考虑它们各自的范数或空间原点。我们使用这种损失,以便教师和学生模型中的隐藏向量对齐。使用与之前相同的符号:
实际上,余弦损失有两种版本,一种是对齐向量,一种是将一个向量拉向另一个向量的相反方向。在本文中,我们只对第一个感兴趣。
完全蒸馏损失
完全蒸馏损失是上述三种损失的组合:
四。其他详细信息
softmax 温度对概率分布的影响。图片作者。
蒸馏程序
解释了损失之后,剩下的蒸馏程序就相当简单了。模型训练与其他模型非常相似,唯一不同的是你必须并行运行两个类似 BERT 的模型。感谢您的 GPU 的健康和内存,教师模型不需要梯度,因为反向传播只在学生身上进行。当然,实现损失仍然需要做,就像蒸馏过程一样,但是我们将在以后的文章中讨论它。
温度
如前所述,让我们回到《T2》三中使用的符号。师生交叉熵损失:
DistilBERT 使用[7]中的温度概念,这有助于软化 softmax。温度是一个θ ≥ 1 的变量,当温度上升时会降低 softmax 的“置信度”。正常的 softmax 描述如下:
现在,让我们无用地把它重写为:
每个人都会同意这是正确的。1 实际上对应于温度θ。正常的软最大值是其温度设置为 1 的软最大值,具有常规温度的软最大值的公式为:
随着θ上升,θ上的商变为零,因此整个商变为 1/n,并且 softmax 概率分布变为均匀分布。这可以在上图中观察到。
在 DistilBERT 中,学生和老师的 softmax 在训练时都以相同的温度θ为条件,推理时温度设置为 1。
动词 (verb 的缩写)结论
现在您已经知道了类似 BERT 模型的提取是如何为 DistilBERT 工作的,唯一要做的就是选择一个模型并提取它!
显然,您仍然需要实现蒸馏过程,但是我们将很快介绍如何实现。
参考文献
[1]维克多·桑,弗拉达利出道,朱利安·肖蒙德,托马斯·沃尔夫,蒸馏伯特,伯特的蒸馏版:更小,更快,更便宜,更轻 (2019),拥抱脸
[2]雅各布·德夫林(Jacob Devlin),张明蔚(Ming-Wei Chang),肯顿·李(Kenton Lee),克里斯蒂娜·图坦诺娃(Kristina Toutanova),伯特:语言理解的深度双向转换器的预训练 (2018),谷歌 AI 语言
[3]刘、米莱奥特、纳曼戈亚尔、杜、曼达尔乔希、陈、奥梅尔列维、、卢克塞特勒莫耶、韦塞林斯托扬诺夫、罗伯塔:稳健优化的伯特预训练方法 (2019)、arXiv
[4],焦,尹宜春,,尚,,,,李,,,,, TinyBERT:为自然语言理解提取 BERT(2019),arXiv
[5]孙志清,俞鸿坤,,宋,,刘,,周, MobileBERT:一种面向资源受限设备的紧凑任务不可知 BERT(2020),arXiv
[6]莱米·卡里姆,插图:自我关注 (2019),走向数据科学
[7] Geoffrey Hinton,Oriol Vinyals,Jeff Dean,在神经网络中提取知识 (2015),arXiv
蒸馏变压器:(DeiT)数据高效图像变压器
变形金刚 go brum brum
嗨伙计们!今天我们将实施训练数据高效的图像转换器&通过注意力进行提炼一种新的方法来对视觉转换器进行知识提炼,称为 DeiT。
您很快就会看到这种新方法是多么优雅和简单。
DeiT 可以在我的新电脑视觉库中找到,叫做眼镜
在开始之前我高度推荐先看看视觉变形金刚
介绍
让我们通过了解 DeiT 型号系列的性能来介绍它们
论文作者(Hugo Touvron 等人)提供的图片
将注意力集中在 ViT-B 和 DeiT-S 上。如你所见,他们最小的型号有+4–5%的速度,比更大的 ViT-B 快 100 倍。怎么可能?
知识蒸馏
(这篇论文有一个很好的关于这个主题的总结部分,但是我会讲得很快)
知识蒸馏是一种训练技术,用来教一个学生模型去匹配一个老师模型的预测。这通常是用来,从一个大模型开始作为老师,产生一个新的更小的学生模型,产生比从头训练学生模型更好的性能。
有不同类型的蒸馏技术,在本文中他们使用了所谓的硬标签蒸馏。其思路是同时使用真实目标\(y\)和老师$ y _ t = \ text { arg max } _ cZ _ t(c)$产生的目标。
论文作者(Hugo Touvron 等人)提供的图片
其中\(Z_s\)和\(Z_t\)分别是学生和教师模型的对数,\(\psi\)是 sofmax 函数。
当把真实目标和教师目标错误分类时,损失将惩罚学生。这很重要,因为它们并不总是相同的。教师可能犯了一些错误,或者图片可能被大幅放大,因此目标已经改变。
有趣的是,当他们使用 convnet ( regnet )作为老师,而不是变压器时,最好的结果被存档了。
论文作者(Hugo Touvron 等人)提供的图片
它被称为硬,因为学生依赖于老师的硬标签。在 PyTorch 中,这可以通过
注意力蒸馏
论文作者(Hugo Touvron 等人)提供的图片
ViT 使用类标记进行最终预测。类似地,我们可以添加一个蒸馏令牌,用于进行第二次预测;第二个预测在损失的第二部分。作者报告说,类和提取令牌收敛到一个非常相似的向量,正如预期的那样,因为教师预测与目标相似,但仍然不相同。
我们可以很容易地改变我们的损失:
轻松点。
蒸馏令牌
现在我们必须将dist
标记添加到我们的模型中。DeiT 只是一个普通的 ViT,有了这个额外的令牌,所以我可以从我的 ViT 教程中回收代码。
这个新令牌作为类令牌被添加到嵌入的补丁中。
分类头
变形金刚用类令牌做预测,没啥新意。在我们的例子中,我们还使用蒸馏令牌进行教师流失中使用的第二次预测。
我们还必须在训练时改变头来返回两个预测。在测试时,我们只是将它们*均。
然后,它遵循我在我的上一篇文章中使用的相同 ViT 代码
最后,我们的模型看起来像:
训练的话,可以用大一点的模型(ViT-Huge,RegNetY-16GF……)当老师,小一点的模型(ViT-Small/Base)当学生。训练代码如下所示:
我不是 facebook,所以我没有几百个图形处理器,所以我不能在 ImageNet 上训练这些模型,但你会明白的!这篇论文有大量的实验,如果你好奇的话,我建议你看一下。
结论
在本文中,我们看到了如何使用新技术从 vision transformer 中提取知识。
顺便说一下,我正在开发一个新的计算机视觉库,名为 眼镜 ,如果你喜欢,可以去看看
保重:)
弗朗西斯科
DAX 中的不同值
价值观、截然不同的价值观和所有价值观之间的差异和相似之处
Power Pivot、SQL Server Analysis Services 和 Power BI 中使用的函数式语言 DAX 功能强大。像所有强大的语言一样,理解它们在用法上的细微差别是很重要的。从表面上看,VALUES 和 DISTINCT 这两个函数似乎提供了相同的结果,并且它们似乎与 ALL 函数不相似。但是,给定正确的上下文和用法,VALUES 和 DISTINCT 返回与 ALL 相同的结果。此外,在某些情况下,值的方式和不同的过程数据之间存在微小但有价值的差异。清楚地理解这三个函数之间的相似性和差异,以及每个函数周围的情况,对于在 DAX 代码中正确地实现它们是必要的。
下面是我们将使用的表格以及它们之间的关系。
Sheet1(包含订单编号和其他订单详细信息)
表:Sheet2(包含订单编号和客户编号)
Sheet2 和 Sheet1 之间的一对多关系。这两个表使用 Order Number 列连接。Sheet2 中的每个订单编号只出现一次(一个)。Sheet1 中的每个订单编号都有必要的次数(多次)。
VALUES 函数有什么作用?
当我们创建一个 Power BI 度量时,有时我们希望考虑一列中的不同值。 VALUES(column_name) 从指定的列中返回不同的值,将当前的筛选器上下文考虑在内。例如,让我们使用通过切片器过滤到最*一个季度的销售订单的可视化表格。我们的销售表包含具有相同订单号的多行。我们想统计最*一个季度的订单数量。为此,我们需要一个唯一的订单号列表,不能重复。VALUES(' Sales '[order number])提供了这样一个列表—每个订单号占一行。使用这种技术,我们可以很容易地计算出最*一个季度的订单数量。
用 VALUES 函数创建的计算表。
当 Power BI 在这两个表之间创建关系时,会在表 2(我们的客户订单表)的一侧创建一个空白行,因为表 1(我们的销售订单详细信息表)中有一行的订单编号在表 2 中找不到。这是因为工作表 2 位于一对多关系的一侧。DAX 不喜欢这样,它在关系的一侧创建了一个空白条目,与 Sheet 1 中的额外订单编号相对。此处出现空白是因为值是针对表 2 中的订单编号列执行的,而不是表 1。
独特的功能是做什么的?
好消息!还记得 VALUES 函数吗?DISTINCT 函数做同样的事情。那为什么 DAX 两者都包括?简单。正如我们在上面看到的,有时我们的数据集在一列中有空值。VALUES 将空白视为有效,并将它们包含在其结果集中。DISTINCT 排除了它们。就是这样。
如果我们有一个包含九个实际订单编号的订单编号列和一个包含空白的行,则值将返回所有十行。DISTINCT 将返回十行中的九行。空白行将会丢失。
虽然不建议使用带有空白的数据模型,但是在某些情况下会出现这种情况。要么您正在创建一个更好的数据模型,要么您已经在模型中的两个表之间创建了一个一对多的关系,不知何故,在关系的多面有一个值在关系的一面是缺失的,就像我们的示例表一样。不理想,但现实生活中会发生。
注意,在使用 DISTINCT 时,对应于 8872027 (Sheet1)的空行丢失。
什么时候价值观和独特性像所有人一样发挥作用?
熟悉 ALL 的人会记得它的语法通常是 ALL(table_name) 。该函数的主要用途是返回由 table_name 指定的表格的未过滤版本。然而,ALL 函数还有另一个用途。如果我们提供一个列名而不是表名作为参数,该函数将返回一个包含一列的计算表。该列包含作为参数传递的列中的每个不同值。当然,source 列是未经过滤的,这是我们所期望的。这在我们需要一列不同值的列表时非常有用。
正如我们已经看到的,价值和独特的工作在过滤的上下文中。因此,返回的唯一值仅来自过滤后的子集,当用于度量时。但是,如果在计算列中使用 VALUES 或 DISTINCT 而不是度量值,则筛选的上下文不适用。因此,在这种情况下,它们的工作方式与 ALL(column_name) 完全一样。
ALL 返回与 sheet 2[订单编号]的值完全相同的结果。
按客户编号的汇总表。
请注意,在上面的汇总表中,DISTINCT 和值返回 1,因为它们在每个客户的筛选器上下文中执行,并且值包括有效的空行(表的顶行)。但是当我们通过 ALL 对行进行计数时,ALL 已经丢弃了过滤器,并对表中的所有七行进行计数。
包扎
你应该使用哪种方法?在计算列中使用时,请自行选择。在这种情况下,我推荐其他两个功能。如果需要在计算列的公式中排除空白,请使用 ALLNOBLANKROW。
ALLNOBLANKROW 丢弃类似于 DISTINCT 但不引用任何筛选器的空白。
创建度量时,如果需要列中不同的值列表,而不考虑当前过滤器,请使用 ALL。但是,如果您需要维护过滤后的上下文,请根据您希望如何处理空白,在 VALUES 和 DISTINCT 之间进行选择。我建议您使用值,除非您的特定情况需要排除空值。在这种情况下,使用 DISTINCT。
大兴快乐!
杆蓖麻 帮助企业获得分析权!他帮助国际组织和小型企业改善他们的数据分析、数据科学、技术战略和技术领导力。除了咨询,Rod 还喜欢公开演讲、教学和写作。你可以在 rodcastor.com**和通过他的 邮件列表 了解更多关于 Rod 和他的工作。
Apache Spark 中的 distinct()与 dropDuplicates()
Spark 中 distinct()和 dropDuplicates()有什么区别?
由朱莉安娜在unsplash.com拍摄的照片
Spark 数据帧 API 提供了两个函数,可以用来从给定的数据帧中删除重复数据。这些是distinct()
和dropDuplicates()
。尽管这两种方法做的工作差不多,但它们实际上有一个区别,这在某些用例中非常重要。
在本文中,我们将探讨这两个函数是如何工作的,以及它们的主要区别是什么。此外,我们将讨论何时使用其中一个。
请注意,我们将用来探索这些方法的示例是使用 Python API 构建的。然而,它们相当简单,因此也可以通过 Scala API 使用(尽管提供的一些链接会引用以前的 API)。
distinct()方法
[**distinct()**](https://spark.apache.org/docs/latest/api/python/pyspark.sql.html?highlight=distinct#pyspark.sql.DataFrame.distinct)
返回一个新的
[**DataFrame**](https://spark.apache.org/docs/latest/api/python/pyspark.sql.html?highlight=distinct#pyspark.sql.DataFrame)
,其中包含此[**DataFrame**](https://spark.apache.org/docs/latest/api/python/pyspark.sql.html?highlight=distinct#pyspark.sql.DataFrame)
中的不同行。
distinct()
将返回数据帧的不同行。例如,考虑下面的数据帧
>>> df.show()
+---+------+---+
| id| name|age|
+---+------+---+
| 1|Andrew| 25|
| 1|Andrew| 25|
| 1|Andrew| 26|
| 2| Maria| 30|
+---+------+---+
该方法不采用参数,因此在删除重复项时会考虑所有列:
>>> df.distinct().show()
+---+------+---+
| id| name|age|
+---+------+---+
| 1|Andrew| 26|
| 2| Maria| 30|
| 1|Andrew| 25|
+---+------+---+
现在,如果您在删除重复项时只需要考虑列的子集,那么您首先必须在调用distinct()
之前选择一个列,如下所示。
>>> df.select(['id', 'name']).distinct().show()
+---+------+
| id| name|
+---+------+
| 2| Maria|
| 1|Andrew|
+---+------+
这意味着返回的数据帧将只包含用于消除重复的列的子集。如果是这样的话,那么大概distinct()
就不行了。
dropDuplicates()方法
[**dropDuplicates(subset=None)**](https://spark.apache.org/docs/latest/api/python/pyspark.sql.html?highlight=distinct#pyspark.sql.DataFrame.dropDuplicates)
返回一个删除了重复行的新的
[**DataFrame**](https://spark.apache.org/docs/latest/api/python/pyspark.sql.html?highlight=distinct#pyspark.sql.DataFrame)
,可选地只考虑某些列。对于静态批处理
[**DataFrame**](https://spark.apache.org/docs/latest/api/python/pyspark.sql.html?highlight=distinct#pyspark.sql.DataFrame)
,它只是删除重复的行。对于一个流[**DataFrame**](https://spark.apache.org/docs/latest/api/python/pyspark.sql.html?highlight=distinct#pyspark.sql.DataFrame)
,它将跨触发器保存所有数据作为中间状态,以删除重复的行。您可以使用[**withWatermark()**](https://spark.apache.org/docs/latest/api/python/pyspark.sql.html?highlight=distinct#pyspark.sql.DataFrame.withWatermark)
来限制重复数据的延迟时间,系统将相应地限制状态。此外,将丢弃比水位线更早的数据,以避免任何重复的可能性。
[**drop_duplicates()**](https://spark.apache.org/docs/latest/api/python/pyspark.sql.html?highlight=distinct#pyspark.sql.DataFrame.drop_duplicates)
是[**dropDuplicates()**](https://spark.apache.org/docs/latest/api/python/pyspark.sql.html?highlight=distinct#pyspark.sql.DataFrame.dropDuplicates)
的别名。
现在dropDuplicates()
将删除在一组指定的列(如果提供的话)上检测到的重复项,但是与distinct()
相反,它将返回原始数据帧的所有列。例如,如果您想通过考虑所有列来删除重复项,您可以运行以下命令
>>> df.dropDuplicates().show()+---+------+---+
| id| name|age|
+---+------+---+
| 1|Andrew| 26|
| 2| Maria| 30|
| 1|Andrew| 25|
+---+------+---+
因此,如果您想删除列子集上的重复项,但同时又想保留原始结构的所有列,那么可以使用dropDuplicates()
。
df.dropDuplicates(['id', 'name']).show()+---+------+---+
| id| name|age|
+---+------+---+
| 2| Maria| 30|
| 1|Andrew| 25|
+---+------+---+
结论
在本文中,我们探索了 Spark DataFrame API 的两个有用的函数,即distinct()
和dropDuplicates()
方法。这两种方法都可以用来消除 Spark 数据帧中的重复行,但是,它们的不同之处在于,distinct()
不接受任何参数,而dropDuplicates()
在删除重复记录时可以考虑列的子集。
这意味着当一个人想通过只考虑列的子集来删除重复项,但同时又要返回原始数据帧的所有列时,dropDuplicates()
是一个更合适的选择。
使用 PySpark 的分布式生物医学文本挖掘用于癌症基因突变的分类——第一部分:EDA 和逐点互信息分析
分布式机器学习使用 Apache Spark 对癌症肿瘤基因突变进行大规模分类
在这篇由两部分组成的文章中,我将分享我在分布式计算研究生课程中参与的一个学期研究项目的心得。我使用 PySpark 实现了一个机器学习算法,用于对癌症肿瘤基因突变进行分类,PySpark 是 Apache Spark 的 Python 实现。在这一部分中,我将描述探索性数据分析(EDA)以及逐点互信息(PMI)的自然语言处理(NLP)概念的分布式实现。
我在这篇文章中只分享代码的某些部分,因为包含整个代码会使文章太长。完整代码请查看 GitHub 链接 这里 。我还准备了一个 5 分钟的视频,你可以在这里 找到。如果你想跳过这一步直接进入分布式 ML 部分,这里有第二部分 的链接。
简介
癌症如此难以理解并因此难以治疗的主要原因之一是肿瘤基因突变的巨大复杂性。肿瘤细胞中可能有几千个基因,这些基因中的每一个都可能有几千个已知的突变,以及大量尚未发现的突变。并非所有的突变都是癌性的,其中一些可能是良性的。毕竟,基因不断变异是自然进化的一部分,并不是每个突变都对生物体有害。为了开发有效的抗癌药物,药物研究人员需要了解肿瘤中大多数突变的性质。
然而,对肿瘤基因突变进行分类的过程是手工的、费力的和耗时的。对于在实验室的肿瘤标本中发现的每一个突变,研究人员都必须阅读几页医学文献,才能将发现的突变归类到适当的类别。这种以人为媒介从医学文献中检索信息的过程大大减缓了知识增长的速度。如果一名研究人员花 1 个小时对一个突变进行测序,5 个小时对其进行分类,研究界会产生巨大的机会成本,因为花在分类上的时间本可以更好地用于发现新的突变或研究恶性突变的治疗方法。
生物医学文本挖掘是一套计算技术,用于从大型生物医学研究文献数据集中提取见解和可操作信息。
自从互联网出现以来,尤其是在下一代测序(NGS)等新技术在实验室得到广泛采用之后,生物医学文献一直在快速增长。下图显示了一组选定国家癌症研究出版物数量的年增长率。
全国癌症研究出版物数量的增长(资料来源: Elsevier (第 11 页))
因此,越来越需要计算技术来处理这些数据,并从中获得见解,以帮助医学研究人员加快他们的研究。
现在我将描述我在这项研究中使用的数据集。
数据集
我使用了由纪念斯隆-凯特琳癌症中心(MSKCC)汇编和发布的个性化医疗数据集,用于他们在 2017 年举办的一场 Kaggle 比赛。该数据集包含来自各种医学杂志的关于肿瘤基因突变分类研究的研究论文。训练数据集有 3321 篇这样的论文。每篇论文都是关于一个特定的基因和与之相关的突变。癌症专家研究人员将这些突变归为九类中的一类(1-9)。由于我没有测试数据集的标签,所以我将初始训练数据集拆分为 80:20 的训练和测试拆分,并使用初始训练数据集大小的 80%的新数据集进行训练。
虽然 Kaggle 竞赛中没有说明这些突变类别代表什么,但我从一篇编辑过的文章中发现,它们很可能意味着以下内容:
突变类的含义(图片由作者提供)
下图显示了数据集的结构:
训练数据集的格式(图片由作者提供)
例如,ID 为 1 的论文是关于 CBL 基因及其变异(突变)W802*。这种变异属于 2 类。论文的起始文本显示在标题为“文本”的列中。本文开头的几行如下图所示:
研究论文的开头句子(图片由作者提供)
正如我们所观察到的,这是一篇密集的科学论文,包含几个生物学术语,总共有 5757 个单词。测试数据集与训练数据集具有相同的结构。
挑战
让我们假设一名研究人员的阅读速度为每分钟 300 个单词,她必须阅读 5 篇这样的论文才能对突变进行分类。这意味着,即使不考虑研究论文不能像小说一样阅读的事实,她完成这项任务也需要 2 个小时。如果一个肿瘤基因包含 10 种不同的突变,这对于可能存在的突变数量来说是一个适中的数字,会怎么样呢?
如果我们可以提出一种算法,可以“阅读”大量的生物医学文献,并学习论文中的关键词和论文描述的突变分类之间的关联,我们可以大大减少研究人员手动注释新突变的时间。例如,如果一个算法可以在 80%的时间里在 1 秒钟内注释一个突变(如果我们有足够的计算资源,这是一个相当长的时间,可以大大减少),这样的方法对癌症研究人员来说非常有用。
想法
在这项研究中,我有两个想法:
- 作为探索性步骤,我检查了文献中基因的两个突变的频繁共现是否表明它们属于同一类别的可能性较高,反之亦然,即两个突变的共现低于*均水*是否表明它们属于不同类别的可能性较高。为此,我使用了 PMI,这是一个在 NLP 领域中非常成熟的概念。
- 我使用 Spark MLLib 训练了一个分布式多项式逻辑回归模型,并使用它对测试数据集中的突变进行分类。
我将在这一部分描述第一个想法。在此之前,下面是使用分布式计算的基本原理:
使用分布式计算背后的基本原理
有两类算法挑战——一类是可以分成更小的部分,这样每个部分都可以单独处理,另一类是需要作为一个整体来处理,因此不能分成更小的部分。例如,要将一个大列表中的所有数字相加,我们可以将列表分成几个子列表,分别将这些子列表中的数字相加,然后将这些子列表相加得到最终结果。另一方面,要计算一个数字列表的*均值,我们需要将所有的数字相加,然后将总和除以列表中数字的总数。我们无法通过将列表分成子列表、计算子*均值、然后计算子*均值的*均值来计算正确的*均值。
对于可以分解成几个部分的问题,如果我们将每个部分分配给不同的机器,我们可以加速算法的执行,这就是分布式计算的用武之地。如果我们有 10 台机器,我们可以为每台机器提供 1/10 的较大问题,并在 1/10 的时间内获得结果,而不是在单台机器上处理整个问题,这对于许多涉及大型数据集的问题来说甚至是不可能的。
例如,计算一对基因突变的 PMI 分数需要计算同一篇论文中两个突变的同时出现次数,这一任务非常适合拆分成多个部分并在多台机器上执行。
我现在将讨论一些 EDA 步骤及其结果,以了解数据集的本质:
探索性数据分析
- 查找没有关联发布的条目:导入库并创建 sparkcontext 后,我检查数据集中是否有丢失的值。在训练数据集中的 3321 个条目中,有 5 个条目没有相关的研究论文,如下图所示:
按列检查缺少的值(图片由作者提供)
没有相关研究论文的训练数据行(作者图片)
我用包含基因名称和相关突变的连接文本填充这些 NaN 值。例如,对于 ID 1277,我用“ARID5B 截断突变”替换了 TEXT 列中的 NaN 值,如下图所示:
用基因突变串联替换文本列中的 NaN 值(图片由作者提供)
2.确定训练数据集中输出类的分布:为了检查是否存在显著的类不*衡,我绘制了属于训练数据集中每个类的实例数量,结果如下图所示:
训练和测试数据集中突变类的分布(图片由作者提供)
因此,第 7 类和第 4 类在训练和测试数据集中的比例过高。
3。确定基因及其突变在出版物中出现的分布:
接下来,我绘制了所有 236 个基因中与特定基因相关的发表数量的计数,以检查分布是否相对均匀,或者少数基因是否出现了大量的次数。基因指数相对于其在研究文献中出现频率的图如下所示:
基因直方图(图片由作者提供)
从图中可以清楚地看出,少量基因(~10 个基因)是大部分研究文献的主题,这间接表明了这样一个事实,即这些是突变最多的基因,因为每篇论文对应于一个基因的一个突变。
为了检查是否有相同的突变发生在一个以上的基因中,我绘制了突变指数与突变计数的直方图。
突变直方图(图片由作者提供)
基因之间存在一般的突变,如缺失、融合、截短,但它们的确切性质取决于所讨论的特定基因。正如所料,我们从右边的图中看到,绝大多数突变是独特的(计数为 1),这意味着它们只发生在一个基因中。
现在,在继续讨论代码实现之前,我将简要讨论 PMI 的概念。
点态互信息
逐点互信息(PMI)是信息论和统计学中使用的一种关联度量。事件 x 和 y 的 PMI 由下式给出:
PMI 公式(图片作者提供)
其中,p(x)和 p(y)分别是事件 x 和 y 的边际概率,p(x,y)是事件 x 和 y 的联合概率。正 PMI 表示事件 x 和 y 比它们独立时更有可能同时发生。同样,负的 PMI 表明 x 和 y 不太可能同时出现。
让我们考虑一个例子。假设我们有一个包含 10 篇论文的文档,其中有两个单词 Alpha 和 Beta。α出现在 6 篇论文中,β出现在 4 篇论文中,它们都出现在 3 篇论文中。这种情况下的 PMI 计算如下所示:
PMI 计算示例(图片由作者提供)
如果α和β是基因的两个突变,那么如果它们的 PMI 得分为正,这意味着它们在研究文献中频繁地同时出现,而不是为零或为负。
brca 1 和 CBL 基因突变的 PMI 分布式计算
我考虑了两个基因 BRCA1 和 CBL,并计算了这两个基因所有突变的 PMI 分数。总共有 236 个独特的基因。CBL 的选择是随机的,以避免任何选择偏差,而我选择了 BRCA1 基因,因为它有最高数量的突变。
以下是计算基因突变 PMI 值的步骤顺序:
1.我创建了一个包含所有 BRCA1 基因突变的列表,然后删除了一般的突变,如截短、缺失、扩增等。然后,我生成了该基因所有剩余独特突变的突变对列表,如果组成突变属于同一类,则将一对标记为 1,如果属于不同类,则将一对标记为 0。我嵌入了下面的代码片段,它也显示了输出。突变 R1753T 和 C44Y 属于相同的第 4 类,因此我将这对标记为 1。另一方面,突变 R1753T 和 R1835P 分别属于第 4 类和第 1 类,因此我将这对标记为 0。
根据属于相同或不同类别,将突变对标记为 1 或 0(图片由作者提供)
2.我将所有与 BRACA1 基因相关的文档合并成一个文档,并计算每个突变在这个合并文档中出现的概率。从现在起,分布式计算开始出现。观察 PySpark 如何将计算突变的任务进行划分,方法是将这个大文档分成几组论文,将这些论文发送到不同的机器,然后通过 reduce 操作将结果组合起来,得出组合文本中这些突变出现的概率。S1655F 突变在合并文献中出现频率最高,出现概率为 0.78(这意味着它出现在与 BRCA1 基因相关的 264 篇论文中的 209 篇中)。
计算文献中不同 BRCA1 突变发生的概率(图片由作者提供)
3.接下来,我实现了上面讨论的 PMI 公式的分布式版本,以计算所有 BRCA1 突变对的 PMI 值。例如,突变 G1743R 和 L1657P 各自单独出现 85 次,一起出现 62 次,它们的 PMI 值为 0.358。
PMI 计算代码片段(图片由作者提供)
结果
为了简洁起见,我将讨论 BRCA1 基因的结果,但我观察到的 CBL 基因的趋势是相同的。我绘制了所有突变对的 PMI 与分类相似性,该图如下图所示:
BRCA1 基因突变的 PMI 与突变相似性(图片由作者提供)
从这个图中我们可以得出两个结论:
- 很明显,与人们的预期相反,PMI 得分的值和突变的相似性之间没有明确的关系。存在属于相同突变类别的具有低 PMI 分数的突变对,并且存在属于不同突变类别的具有高 PMI 分数的突变。因此,似乎两个突变之间的高 PMI 分数并不能很好地预测它们的类别相似性。
- 有趣的是,具有最低和最高 PMI 值的突变对属于同一突变类别。为什么几乎不一起发生的突变(即低 PMI 值突变对)属于同一类,这是一个令人感兴趣的研究问题,进一步的研究可以揭示这一意外发现。
应用程序
我们不仅可以在检测基因突变的相似性方面使用分布式 PMI 计算,还可以在我们有一组类别和与类别相关的多个指示词的任何领域使用。例如,我们可以通过计算与我们最相关的一组词对的 PMI,并通过我们的算法运行几篇新闻文章,找到其中几篇在这些词对的 PMI 得分方面排名较高的文章,来确定一篇新闻文章是否与我们相关。我们也可以有两个以上的词,分布式计算也可以很容易地扩展到这种情况。
第一部分到此为止!希望你能从这篇文章中学到一些有用的东西。在 第二部分 中,我将讨论我在 Spark MLlib 中训练分布式多项逻辑回归模型的工作,以对未见过的研究论文中的突变进行分类。
(我感谢 Namratesh 关于这个数据集的帖子,它帮助指导了我的 EDA 的初始部分。)
参考
- 通过探索不同的分类器对基因突变进行自动分类,Badal Soni 等人,使人工智能应用于数据科学,计算智能研究,Springer
基于 PySpark 的分布式生物医学文本挖掘在癌症基因突变分类中的应用——第二部分:多项式逻辑回归
分布式机器学习使用 Apache Spark 对癌症肿瘤基因突变进行大规模分类
在 第一部分 中,我讨论了探索性数据分析和将逐点互信息应用于突变对,以找出 PMI 分数和突变类相似性之间是否存在任何相关性。在这一部分中,我将讨论训练一个分布式多项逻辑回归(MLR)模型,并将其应用于测试数据集以确定突变的类别。
同样,为了简洁起见,我在本文中只分享代码的某些部分。完整代码请查看 GitHub 链接 这里 。我还准备了一个 5 分钟的视频,你可以在这里找到https://www.youtube.com/watch?v=LRp75Ku8Hc4。
在继续在 MLlib 中训练模型之前,需要几个预处理步骤:
数据预处理
- ****处理缺失值:正如我们在第一部分中看到的,训练数据集中有 5 个条目没有相关的研究论文文本。这些在文本列中有 NaN 值。我把这些 NaN 值换成了基因+突变值的组合。
- ****执行基本的 NLP 操作:我在研究论文文本(每行的文本列)上执行了一些基本的 NLP 操作,以标准化跨行的值,并在不丢失有价值信息的情况下减小数据集的大小。这些行动是:
A.用一个空格替换文本中的特殊字符,如*、@等
B.用单个空格替换多个空格
C.将所有字符转换为小写
D.仅存储文本中非停用词的那些词。像 and、the、the 等这样的词称为停用词。它们对于使句子语法正确至关重要,但在大多数情况下并不包含有用的信息。例如,考虑句子“药物阿司匹林已经被用作止痛药很长时间了”。这两个词中哪个是关键词:“阿司匹林”还是“as”?停用词数量众多,虽然移除停用词会造成一些意义的损失,但这种损失通常是最小的,并且所获得的数据压缩是巨大的,因为停用词是任何文档中最频繁出现的词。
下面的代码片段执行这四个操作:
对数据集执行基本的 NLP 预处理操作(图片由作者提供)
如下所示,这些预处理步骤的分布式实现花了 8 秒钟,而我在单台机器上完成时只花了 30 秒钟。
3.矢量化基因、突变(变异)和文本特征:我为基因、突变和训练数据集中的所有文本创建了一个热编码。在训练数据集中的所有文章中有 65946 个唯一单词。
在训练数据集论文中矢量化基因、突变和单词(图片由作者提供)
4.组合所有编码以创建合并的训练数据集:****
对于每一行,我组合了基因、突变和文本 one-hot-encoding 来创建一个表示这些信息的合并向量。
创建合并的编码训练数据集(图片由作者提供)
每行有 69229 个二进制条目,如下图所示:
训练数据集的矢量化表示(图片由作者提供)
5.将数据帧转换为 Spark MLlib 友好格式:****
为了训练分布式 ML 模型,我在上图中显示的训练数据集 panda dataframe 需要转换为特定的格式,这是通过我在 GitHub 文件夹中共享的 csvlibsvm1.py 程序文件来完成的。
训练 MLR 模型
这是我用来训练 MLR 模型的代码片段。我设置了超参数值(正则化常数=0.01,弹性参数=1),在手动调整这些值并观察结果后,产生了良好的结果。更系统的方法是 GridsearchCV,一旦我做到了并得到更好的结果,我会更新这篇文章。
Spark MLLib 中多项式逻辑回归模型的分布式训练(图片由作者提供)
结果
我得到了 64%的总体分类准确率和 1.12 的多项式对数损失,这与 Kaggle 竞赛中最佳表演者报告的值相当。他们得到了 2.03 的多项式对数损失,尽管
他们的测试数据集与我的不同,因为我在分割训练数据集后准备了我的测试数据集。尽管如此,总体而言,MLR 似乎对该数据集工作得很好
。
这是回忆矩阵:
回忆矩阵(作者的图像)
令人鼓舞的是,该模型可以很好地预测实际的班级。2–7、3–7 和 8–7 的标签错误非常严重,需要进一步调查。这可能是由于高偏差导致模型将少数类(2,3,8)分类为多数类(7)。
精度矩阵如下所示:
精确矩阵(作者提供的图像)
该模型总体上具有良好的精度,因为它可以从对角线上呈现的相当高的值得到。3 级和 5 级的精度低于标准,这表明了进一步工作的方向。
混淆矩阵如下所示:
困惑矩阵(图片由作者提供)
再次,该模型在预测突变类别方面具有良好的准确性。有明显的 2–7 和 1–4 错配需要进一步调查。
结论
我发现分布式计算可以显著加速生物医学文本挖掘,这对于大数据尤其重要。我可以想到几个改进领域,包括通过超参数调整进一步提高精度和召回率,使用深度学习(特别是基于 BERT 的模型),通过检查学习到的系数了解模型学习到的内容,提出更好的方法来表示数据,并将此方法应用于生物医学文本挖掘的其他领域以检查其性能。当我得到一些有趣的结果与大家分享时,我会在未来就其中的一些话题发表文章。
希望你觉得这个两部分的系列有用。如果您想了解本研究的任何部分或想与我合作,请随时联系我们。
(再次感谢 Namratesh 关于这个数据集的帖子,它帮助指导了我的数据预处理的最初部分。)
分布式深度学习 101:简介
如何使用 PyTorch 编写分布式深度学习应用程序的指南
Omar Flores 在 Unsplash 上的照片
深层神经网络(dnn)是机器学习最新进展的主要推动力。从图像分类到语言翻译,从内容推荐到药物发现,dnn 已经改变了我们解决各种用例挑战的方式。
如今,向外扩展模型训练以获得更多计算资源的需求比以往任何时候都高。
最*的进展主要归功于我们掌握的大量数据,这些数据是确保 Deep Learning 引擎持续运行的燃料。因此,向外扩展模型训练以获得更多计算资源的需求比以往任何时候都高。
有两种方法可以实现这一点:(I)数据并行化或(ii)模型并行化,前者是最常用的方法。本文解释了什么是数据并行化,并实现了一个教学示例来检查其内部工作。在接下来的故事中,我们将在一个真实的用例中更深入地展开培训过程。
简而言之,数据并行化的概念非常简单;例如,假设我们有一个以上的 GPU 加速器,我们会:
- 在每个图形处理器上复制模型
- 分割数据集,并使模型适合不同的子集
- 在每次迭代中传达梯度,以保持模型同步
虽然这是一个容易理解的想法,但是它的实现却并非微不足道。这就是为什么像 PyTorch 和 TensorFlow 这样的主要框架都带有对分布式培训的本地支持。让我们潜水吧!
学习率是一份针对那些对人工智能和移动机器人世界感兴趣的人的时事通讯。你会听到我每周五与最新的人工智能新闻和文章的更新和想法。在此订阅!
DNN 训练的基础
首先,让我们巩固 DNN 培训的基础。通常,要训练 DNN,我们遵循三步程序:
- 我们通过 DNN 层传递数据来计算损失(即前向传递)
- 我们通过每一层反向传播损耗来计算梯度(即反向通过)
- 我们应用学习规则来更新模型的参数(即优化步骤)
为了构建一些上下文,让我们看看 PyTorch 代码中的这些步骤。以下示例使用一个简单的单层神经网络和一个玩具数据集来演示训练过程。
首先,我们生成两个随机张量;一个用于输入数据(特征),一个用于我们的目标(标签)。然后,我们初始化一个简单的模型(只是一个线性层)、优化器(随机梯度下降— SGD)和损失函数(均方误差损失— MSE)。在第 19 行,我们开始向前传递,将数据输入模型并计算损失。最后,在第 23 行,我们执行向后传递,并在最后一行更新模型的参数。
数据并行性
PyTorch 提供了几个工具来帮助我们扩展上一节中看到的培训过程。但是让我们退一步,运用常识;并行化该培训脚本的最简单方法是什么?
首先,我们需要在每个 GPU 上复制模型。接下来,我们将照常运行正向传递,并在反向传递期间计算梯度。到目前为止,一切顺利,但现在我们需要同步梯度,以保持我们的模型同步。实现这一点的简单方法是收集两个 GPU 上的所有梯度,应用一些缩减操作(例如,对它们求*均),然后更新模型参数。
在这一节中,我们将看到如何将这个简单的想法转换成 PyTorch 代码。下面的脚本是我们将在整篇文章中使用的脚手架。
这个脚本启动了两个进程,初始化了进程组,这样每个进程都能够通过一个主进程进行协调,最后执行给定的train
函数,这个函数此时什么也不做。所以,下一步是填充这个方法。
剧本好像挺熟悉的。我们有正向传递,反向传播步骤,最后,我们更新模型的参数。但是有两个补充:首先,我们需要对数据集进行分区,因此每个进程都处理整个数据集的一个子集。然后,在优化步骤之前,我们有一个名为average_loss
的函数。让我们来实施那些事情吧!
数据集分区
在这个故事中,我们使用流行的 MNIST 数据集。MNIST 数据集有 60,000 张手写数字图像。因此,我们希望将其中的 30.000 个提供给第一个 GPU,其余的提供给第二个 GPU。为了实现这一点,我们实现了一个DatasetPartitioner
助手类。
这和分布式训练没什么关系。它只是获取一个数据集,打乱它的索引,然后把它分成两个子集。然后,它以 PyTorch 数据集的形式返回我们请求的数据集的分区,我们可以将它传递给 DataLoader。在文档中阅读更多关于 PyTorch 数据集和数据加载器的信息。
现在,让我们创建我们在训练函数中使用的partition_dataset
方法。
该方法将 MNIST 数据集加载到内存中,获取可用的 GPU 数量,并相应地对其进行拆分。例如,如果我们使用 2 个 GPU,get_world_size
方法将返回2
。因此,如果我们想给我们的模型提供 128 幅图像,我们只需将 128 分成两组,给我们的两个模型提供 64 幅图像。
最后,我们应该根据运行它的 GPU 来指定每个进程将使用数据的哪个分区;例如,如果我们在第一个 GPU 上,get_rank
方法将返回0
。因此,我们将使用第一个数据分区。
*均梯度
为了将一切联系在一起,我们需要收集梯度,应用归约操作(即*均)并更新模型参数。但在深入研究之前,我们需要一些背景知识。
PyTorch 进程使用集体通信来对数据执行某些操作。例如,对某个张量的reduce
操作将从主节点上的每个进程收集所有这样的张量,并执行指定的操作。
缩小功能-按作者分类的图像
PyTorch 目前共有 6 个集体。更多细节参见文档。但是这里我们需要的不仅仅是收集主节点上的梯度,而是每个节点上的梯度。因此,我们需要all_reduce
集体。
所有缩小功能—作者图片
我们已经准备好实现average_gradients
方法。
首先,我们用get_world_size
方法得到可用的 GPU 数量。然后,对于每个模型参数,我们执行以下操作:
- 使用
dist.all_reduce
收集每个过程的梯度 - 对渐变应用求和运算
- 除以世界尺寸,取*均值
最后,我们可以继续使用*均梯度来更新模型参数!
结论
深度神经网络(DNNs)一直是机器学习领域大多数最新进展背后的主要力量。像这样的突破主要是由于我们可以处理的数据量,这增加了将训练过程扩展到更多计算资源的需求。
在这个故事中,我们研究了 PyTorch 如何帮助我们分发 DNN 的培训,使用一个教学示例来获得更多的洞察力。在随后的故事中,我们将更深入,并在真实的用例中扩展培训过程。
关于作者
我叫迪米特里斯·波罗普洛斯,我是一名为阿里克托工作的机器学习工程师。我曾为欧洲委员会、欧盟统计局、国际货币基金组织、欧洲央行、经合组织和宜家等主要客户设计和实施过人工智能和软件解决方案。
如果你有兴趣阅读更多关于机器学习、深度学习、数据科学和数据运算的帖子,请关注我的 Medium 、 LinkedIn 或 Twitter 上的 @james2pl 。此外,请访问我的网站上的资源页面,这里有很多好书和顶级课程,开始构建您自己的数据科学课程吧!
分布式深度学习—图解
了解如何使用多个 GPU 执行深度学习
照片: pixabay
分布式深度学习可视化!
请继续阅读!(图片由作者提供)
在这篇文章中,我将说明分布式深度学习是如何工作的。我已经创建了动画,应该可以帮助您对分布式深度学习有一个高层次的理解。但是让我们从基础开始。
GPU 如何加速深度学习?
GPU 如何加速执行(图片由作者提供)
图形处理单元(GPU)是能够执行多个同步数学计算的专用内核。深度学习计算可以分解为一系列矩阵乘法,这就是 GPU 优于 CPU 的地方。当代码被执行时,这些数学运算被卸载到 GPU,而代码的其余部分在 CPU 上运行。由于深度学习本质上是对这些数学例程的迭代,我们通过使用 GPU 获得了巨大的加速。
分布式深度学习
当我们想要使用多个 GPU 来加速我们的模型训练过程时,就会使用分布式深度学习。分布式深度学习主要有两种类型——模型并行和数据并行。模型并行用于当您的模型有太多的层而无法放入单个 GPU 时,因此不同的层在不同的 GPU 上训练。数据并行,两者中较为常见的一种,通过将数据分割到不同的分区来实现加速。该模型使用不同的数据分区在多个 GPU 上同时运行。
下面的动画展示了使用多个 GPU 的分布式深度学习。每个阶段调用的函数显示在动画的右侧。这些功能(原语)解释如下。
- 复制:在多个 GPU 上复制一个模型
- 分散:将输入分配给多个 GPU
- 采集:采集所有 GPU 的输出,并发送给主 GPU
- parallel_apply :在已经分布的输入上并行运行模型
输入数据和模型分布
(图片由作者提供)
- 输入数据批被分成与 GPU 数量相同的分区。
- 主 GPU (GPU0)上的模型权重被复制到所有 GPU
输出和损耗计算
(图片由作者提供)
- 该模型同时运行在所有 GPU 各自的数据分区上
- 输出被收集并发送到 GPU0,然后 GPU 0 计算损耗
- 丢失被发送到所有 GPU
梯度计算和模型更新
(图片由作者提供)
- 每个 GPU 在其各自的数据分区上计算梯度
- 梯度被收集并发送到 GPU0
- GPU0 使用*均梯度来计算更新的模型
- 来自 GPU0 的更新模型被发送到所有 GPU,它们将更新模型用于下一次迭代
- 整个过程再次从头开始迭代
我希望现在你已经对分布式深度学习如何在多个 GPU 上工作有了直观的理解。我使用了 PyTorch 深度学习框架,但其基本原则仍然是一样的。使用 PyTorch 框架时的示例代码如下所示。
在多个 GPU 上运行模型的 PyTorch 代码
数据并行模式的缺点
如果你分析上面解释的 PyTorch 中的数据并行工作流,你可以看到设计中存在一定的局限性。GPU0 充当主 GPU,它比其他 GPU 做更多的工作。这意味着当 GPU0 在做额外的工作时,其他 GPU 处于闲置状态。一个更好的设计是确保所有的 GPU 都做同样多的工作,以便它们得到充分利用。
PyTorch DataParallel 模式的另一个限制是,它使用单个进程多线程,这种模式存在著名的 python 全局解释器锁(GIL)问题。数据并行的优点是所需的编码最少。如果您准备编写更多的代码,您应该在 PyTorch 中使用 DistributedDataParallel,它使用多处理。
未来的工作
我计划涵盖其他高级分布式深度学习方法,如分布式数据并行、Ring All-reduce 等。在以后的文章中。理解分布式深度学习是如何工作的,对于构建健壮的大规模机器学习解决方案至关重要。我希望这一系列文章能够帮助您朝着这个方向迈出一步。
基于 BigDL 和 PySpark 的分布式深度学习
我在 BigDL,Horovod 和其他人之间选择的关于 Spark 的深度学习。
正如我所说的,写这篇文章只是为了帮助任何人得到这个有趣的包。当然,这不是一个详尽的搜索。例如,可以执行以下测试:
- 将此示例作为 spark-submit 运行,而不是从 PySpark Shell 中交互运行;
- 使用更复杂的网络,如同一 MNIST 上的 convnets,或者尝试导入预训练的 BERT 模型,并使用 BigDL 对其进行微调(我甚至不知道是否有这种可能性)
等我有时间的时候,一定会去探究上面的两个子弹。
再见,感谢阅读!
使用 PySpark 的分布式拥抱人脸标记器
埃里克·普劳泽特在 Unsplash 上拍摄的照片
如何广播 tokenizer 并将其与 UDF 一起使用
在这篇短文中,我将展示如何使用拥抱脸 记号赋予器。在详细介绍如何使用 PySpark 在 DataFrame 上应用 tokenizer 之前,让我们先看一些简单的 tokenizer 代码。
上面的代码只是加载了一个预先训练好的记号赋予器 roberta-base 。它打印出一本有两个关键字的字典。在本文中,我们将只关注input _ id,它们基本上是与单词“hello”相对应的标记。
在数据帧上使用记号赋予器的第一步是将其转换为 UDF。在下面的代码中,我们创建了一个方法 tokenize,它接受一个字符序列(字符串),我们在输入字符串上使用了前面启动的 tokenizer。我们只输出键 input_ids 的值。由于值是整数列表,我们将模式定义为 ArrayType(IntegerType())。
现在让我们在一个测试数据框架上使用这个 UDF。首先,我们创建一个包含两行的 DataFrame,字段为“value”。然后我们使用 tokenize_udf 来标记每一行中的“value”字段。
广播标记器
在上面的实现中,标记器不是显式传播的,而是隐式传播的。我们可以广播标记化器,并使用广播变量进行标记化。
在上面的代码中,我们广播了 tokenizer,并在 bc_tokenize 方法中使用了广播的变量 bc_tokenizer ,该变量又在 bc_tokenize_udf 中被转换以用于 DataFrame。
在这篇短文中,我解释了如何在 DataFrame 中以分布式方式使用标记化器。虽然由于 Rust 实现,标记化器通常很快,但在开发人员使用 Spark 处理数据的管道中,这比在机器上收集所有数据、处理(标记化)并重新转换为数据帧更方便。尤其是流式实现从基于 UDF 的记号赋予器中获益更多。
使用 Ray-Python 的分布式 Kafka 消费者
如何使用 Ray 用 Python 编写分布式 Kafka 消费者
介绍
通过这个博客,我将尝试给出一个要点—
- 我们将要用到的一些射线成分
- 然后研究如何使用 Ray 为我们的流处理创建分布式 Kafka 消费者。提供至少一个担保。
- 最后,公开 REST APIs 来管理这些消费者。
为什么用 Ray 进行流处理?
许多团队每天都在不同的用例中使用 python。今天,Python 是使用最多的语言之一。
还有其他分布式计算引擎,如 Apache Spark 和 Apache Flink,它们提供 Python 接口,但学习曲线非常陡峭,您需要创建专门的数据集,如 RDDs/DataFrames 等,操作和概念围绕这些结构。
当我第一次使用 Ray 时,它看起来更像是编写分布式系统的 pythonic 方式,使用 Ray 不需要学习太多。您可以轻松地转换现有的 python 函数和类,以在分布式模式下运行,而无需编写新代码或更改现有代码。
雷简介
如果你已经知道 Ray ,请跳到“创造 Kafka 消费者”部分
请通过射线文档了解更多详细信息。
Ray 提供了简单的 API 来在节点集群中运行你的函数和类。
射线术语
- 任务-在分布式设置中运行的 Python 函数
- 参与者-在分布式设置中运行的 Python 类
- 对象引用——这类似于 Javascript 中的未来或承诺。您可以在这些 refs 上调用 get 来从执行的任务中获取数据
在分布式设置中运行功能
要让一个函数以分布式模式运行,你只需要在你的函数上添加一个装饰器@ray.remote()
。
在下面的代码中,我们计算数字 10 到 16 的阶乘。单个数字的计算在 7 个不同的过程中并行进行,这些过程是由 Ray 创建的。
在上面的代码中:
- 我们使用
ray.init().
启动 Ray,它启动调度程序,创建对象存储来保存参与者和任务,并做一系列其他事情。 - 我们得到了一个计算给定数字的阶乘的阶乘函数。我们用
@ray.remote()
修饰了这个函数,它创建了一个新的以分布式模式运行的 Ray 任务。 - 告诉 ray 重新运行任务 3 次,以防任务在完成前终止。当运行任务的节点关闭或死亡时,任务可能会死亡。
num_cpus
告诉 ray 在 1 个 CPU 的 50%运行这个任务。- 在随后的代码中,我们调用 10 到 16 的阶乘,并将未来值保存到一个列表中。我们稍后调用
ray.get
,它允许我们等待所有的factorial()
任务完成。
输出将如下所示:
This cluster consists of
1 nodes in total
8.0 CPU resources in total(factorial pid=1155) calculating factorial of 15
(factorial pid=1155) Factorial of 15 = 1307674368000
(factorial pid=1154) calculating factorial of 16
(factorial pid=1154) Factorial of 16 = 20922789888000
(factorial pid=1148) calculating factorial of 10
(factorial pid=1148) Factorial of 10 = 3628800
(factorial pid=1149) calculating factorial of 14
(factorial pid=1149) Factorial of 14 = 87178291200
(factorial pid=1151) calculating factorial of 11
(factorial pid=1151) Factorial of 11 = 39916800
(factorial pid=1153) calculating factorial of 13
(factorial pid=1153) Factorial of 13 = 6227020800
(factorial pid=1152) calculating factorial of 12
(factorial pid=1152) Factorial of 12 = 479001600Process finished with exit code 0
如您所见,所有的factorial()
任务都已经在它们自己的进程中启动。
上述设置可以转换为基于类的设置(也称为 Actors ),只需用@ray.remote()
注释该类,这将创建一个运行在不同进程上的新 worker 实例,我们可以调用类成员函数来创建这些 worker 上的远程任务。
创建分布式 Kafka 消费者
正如您在上一节中看到的,我们能够在不同的进程中远程运行函数和类成员函数。
我们将使用相同的概念在多个节点的不同流程中运行我们的消费者。
在本节中,我将介绍以下功能-
- 为创建 Kafka 消费者提供配置支持。
- 以分布式模式运行 Kafka 消费者。
- 公开 REST APIs 来管理(启动、停止等)这些消费者。
我们使用 kafka-python 客户端创建消费者,使用 fastapi 为我们的消费者创建 REST APIs
使设置可配置
消费者配置 JSON 文件—该消费者配置 JSON 文件将包含消费者组配置列表。对于列表中的每个配置,我们将使用配置的工作人员/使用者数量来启动一个使用者组。
为了获得最佳性能,保持
number_of_workers
等于主题的分区数量。警告:如果您有 4 个内核,并且每个进程占用 1 个 CPU,那么您最多可以启动 4 个工作进程。
配置详细信息—
配置详细信息
检查 ser_des_util.py 中可用的串行化器和去串行化器。
流转换器
通过扩展抽象类 StreamTransformer 创建你自己的流转换器
下面是一个这样的转换器,它将字符串消息从 Kafka 转换为 JSON,并创建一个 SinkRecordDTO 对象。
注意,我没有在这里的消息中执行任何操作。您可能希望创建自己的 StreamTransformer 来进行一些处理。
最后,在sink_configs
部分下的 consumer_config.json 中添加您新创建的流转换器。
流作者
流写入器是写入目标存储的一组类。例如,要将转换后的事件写入 Elasticsearch,您可以创建自己的流编写器,使用 es 客户端更新索引。
要创建流编写器,请扩展类 StreamWriter 。
例如,我创建了一个流编写器来打印控制台中的键和消息。
最后,我创建了一个接收器任务类,它将作为一个编排器,执行以下任务:
- 转换数据
- 使用提供的流编写器写入数据存储
- 失败时推入死信队列
以分布式模式运行 Kafka 消费者
正如我们在前面一节中看到的,我们可以通过用@ray.remote()
修饰类和函数,在不同的进程中远程运行一个函数或类成员函数
在本节中,我们将创建远程工作人员和管理这些消费者的管理器。
你可以在这里找到完整的代码。
远程消费者工作者
通过这种设置,Ray 将在一个单独的进程中创建一个工人实例ConsumerWorker
,并在其上启动任务,这意味着在其上调用run()
函数。
注意,我们在装饰器中添加了一些额外的参数:
@ray.remote(max_restarts=2, max_task_retries=2, num_cpus=WORKER_NUM_CPUS)
ray_restarts
告诉 ray,如果工人死亡或被杀,最多重启工人实例 2 次。在本例中,ConsumerWorker
是我们的 worker 实例。
max_task_retries
告诉 ray,如果工人死亡或被杀,重新启动任何正在运行的任务/功能最多 2 次。在这种情况下,run()
功能被重新触发。
在上面的工人中,我们有—
- 在构造函数中创建了一个新的 Kafka 消费者
- 创建了一个
run()
函数,该函数执行:
-运行一个无限循环来轮询()消费者记录/消息
-通过调用sink_task.process()
发送记录进行处理-如果消息处理成功,则向代理提交偏移量。
-通过中断循环,按需停止工人 - 创建一个
stop_worker()
函数,设置标志来停止正在运行的工作线程。
消费者工人经理
管理器负责按需启动和停止消费者工作器。
经理课程中需要注意的关键事项
- 它维护了一个包含 ray actors/worker 实例的容器,并对它们调用 run/stop。
- 提供 API 来启动所有的消费者组或特定的消费者组,并启动这些消费者组上的
n
工作器 - 在创建工人时,我们提供了一个选项-
max_concurrancy=2
。这将允许我们在 worker 实例上调用stop()
,即使run()
任务正在运行。
默认情况下,Worker 实例在单线程中顺序运行任务。 - 停止后使用
ray.kill(actor/worker instance)
杀死工作实例。
Rest APIs 管理消费者工作人员
我使用 Fast API 为我们的消费者工作者管理创建端点,并使用基本 auth 来验证请求。
请注意,在服务的@app.on_event("startup")
上,应用程序创建并启动所有的消费群。
在 K8 的远程 Ray 集群上运行消费者
阅读安装指南此处,获取更多详细说明。
要连接到远程光线簇,我们需要在应用程序中进行以下更改—
Instead of ray.init() change to:
----if LOCAL_MODE == 'Y':
ray.init()
else:
ray.init(address=RAY_HEAD_ADDRESS)------
RAY_HEAD_ADDRESS is your remote ray cluster head address ex: ray://10.20.30.40:30001
在 K8 建立射线簇
创建下面的射线 YAML 配置文件:
在下面的配置中,
- 我们正在创建一个头部 pod 和两个工人副本 pod。
- 头节点占用一个内核和 512 MB 内存—根据需要进行更改
- 工作节点占用 0.5 CPU 和 512 MB 内存—根据需要进行更改
- 通过使用节点端口的 K8 外部服务公开仪表板、ray head 节点和 Redis 服务器以供公众访问。
- 将所有代码依赖项作为一个包安装在 head 和 worker 节点中。这样,射线头节点和工作者节点可以找到这些模块。
pip install kafka-connect-dependency==0.1
应用更改:
kubectl apply -f ray-cluster-config.yaml -n ray
运行消费者应用程序
请注意,您的应用程序不一定需要在 K8 集群上运行。您可以在任何服务器上运行,甚至可以在 docker 容器或 K8 pod 中运行。
## to run outside container
RAY_HEAD_ADDRESS=ray://192.168.64.3:30001 uvicorn src.event_consumer_app:app --port 8002
您甚至可以运行 docker 容器。建立 Dockerfile 。然后使用环境变量运行图像,传递光线头节点地址和其他相关的环境变量。
docker run -e RAY_HEAD_ADDRESS=ray://<head node IP>:<port> -e LOCAL_MODE=N -dp 8002:8002 kafka-connect-ray
这将启动应用服务器,并为消费者组some_consumer_group
启动两个消费者,如下所示—
在 Postman 中运行 RestAPI,或者你可以在 path /docs
中查看 Swagger 文档
摘要
在这篇博客中,我展示了一种使用 Ray 创建分布式 Kafka 消费者并使用 Rest APIs 管理这些消费者的生命周期的方法。
如前所述,这种设置是高度可配置的,您只需要创建自己的转换器和流编写器逻辑就可以让它工作。
参考
https://docs.ray.io/ https://fastapi.tiangolo.com/
15 分钟内面向数据工程师的分布式系统概念
了解现代 NoSQL 系统的内部细节
优秀的数据工程师和伟大的数据工程师的区别是什么?了解分布式系统概念有助于做出正确的大数据技术选择,以及编写更好的数据应用和管道。
举例来说,想象一下买一辆车。你通常会有几个不同的汽车模型,然后比较价格/性能,即发动机,变速器,制动等。同样,深入了解大数据系统有助于避开市场营销 FUD ,并有助于了解在正常的日常运营以及磁盘/节点故障、网络分区等故障期间会发生什么。
GIF 由 giphy
分布式系统是一个广泛的话题——有 1000 多本关于几个方面的书,还有 1000 多位博士(我为这个数字贡献了+1)。
那么,如何在 15 分钟内涵盖分布式系统的概念呢?这是我快速理解任何分布式数据系统的秘方
任何现代大数据系统/NoSQL系统如 Cassandra、MongoDB、HDFS、Ceph、Redis 等可以通过看 12 个设计概念来理解。 在这篇博客中,我分享了这些设计概念,并附有一个案例,用这些概念分析了 HDFS 、卡珊德拉、 MongoDB 、 Redis !
1.聚类模型
聚类模型是任何大数据系统最重要和最显著的特征之一。群集模型定义了一种方法,通过这种方法,横向扩展群集中的各个节点可以协调管理资源、处理事件以及支持客户端的存储数据和元数据操作。概括地说,有三种流行的聚类模型:
- 主分类 :在这个分类中,集群中有一个单独的节点(称为主节点),专门负责管理集群状态,并协调活动/任务(稍后描述)。HDFS 是这种分类法的一个例子。
- 无主分类法 :集群内的所有节点共同承担管理集群状态和活动的同等责任。这种分类法随着 Amazon 的 Dynamo paper 而流行,目前用于 OpenStack Swift、Cassandra 等系统。
- :这是单主分类的演变——全局主充当轻量级协调器,在集群内的节点之间划分集群管理任务。这与无主分类法形成对比,在无主分类法中,没有由整个集群维护的单个全局状态。Ceph、GPFS、Bigtable 都是这种分类法的例子。
2.分片
分片指的是在集群内的节点之间划分数据和元数据操作服务职责的能力。一个好的分片算法可以在节点之间*均分配资源使用,包括传入请求和存储空间利用率。概括地说,分片策略可以分为三大类:
- **基于目录的查找:名称空间分布在集群的节点上,有一个集中的目录来跟踪名称空间到节点的映射。
- **基于范围的查找:这是无主系统中的常见模式。密钥空间被预先划分成多个范围,并分配给各个节点。基于该范围计算映射。
- **混合(基于目录+基于范围):使用组合—使用基于范围的分片结合基于确定性策略的功能来分布写入;读取遵循目录查找。
3.可变性模型
可变性模型定义了更新现有数据的策略,并影响系统中其他服务的设计,即缓存、缓冲、一致性、磁盘布局、读/写模型。可变性模型有三大类:
- **就地:更新直接写入现有数据位置被修改。
- 直接更新:数据在其原始位置直接更新。在这个模型中确保原子性需要阻塞同步,以及与缓冲模块的协调。
- 临时错位:更新被聚集在一个新的位置(从其原始位置)。这种持久性是暂时的,更新最终会更新原始数据。
- **不适当的:对名字空间对象的更新被保存在新的资源位置(即,逻辑或物理地址)。与命名空间对象相关联的元数据被更新以反映该变化。
- **版本化:更新作为对象的新版本被持久化。
4.集群管理器服务
集群管理器服务负责跟踪组成员和故障。鉴于分布式系统的性质,节点可以不断地离开和加入集群。Cluster Manager 还负责跟踪副本的状态以及相应的纠正措施。该设计涉及 4 个构建模块:
- 跟踪成员和节点故障
- **完成 IO 操作当一个或多个副本离线时(由于网络分区或节点停机)
- 离线副本的更新
- 跟踪复制品的滞后
5.共识服务
一些分布式操作需要共识——io 和副本更新的排序、资源管理、集群创建、分布式锁定等。,是几个例子。Leader orchestrated 的设计选择有:
- **一致性编排:一致性服务可以使用基于领导者的模型或基于仲裁的模型来实现。
- **领袖选举:决定领袖实际选举方式
- 当选:使用 Paxos、2PC、ABD 等经典算法之一。
- 预选:这通常涉及一种启发,即挑选一个节点作为领导者来协调一致意见。
6.高可用性服务
高可用性(HA)确保数据在集群级故障时仍可访问。该服务需要设计为处理三种常见的集群故障:
- **元数据节点故障:对于主/多主架构,如 HDFS、HBase,其中命名空间元数据与数据分离。
- **数据节点故障:负责命名空间子集的数据节点的处理。同样,对于像 Cassandra 这样数据和元数据共存的无主架构。
- **分区处理集群中的多个节点受到影响(因网络故障而崩溃或断开)
实施故障切换的设计选项取决于副本的状态,即主动-主动、主动-被动(仅在故障期间提供数据)和主动-备份(仅在故障期间重建副本)。
7.命名空间管理器服务
命名空间查找服务将命名空间转换为数据在群集中持久存储的位置。服务的设计有 3 个关键方面:
- **集群范围的元数据表示:元数据是否直接映射到数据位置,而不是映射到对象管理器进行进一步翻译。
- **管理集群范围元数据的新鲜度:它是完全物化的还是动态派生的。
- **对象查找:翻译名称空间请求的架构模型
- **副本查找:副本访问的架构模型
8.一致性服务
一致性服务的目标是确保满足应用程序员的数据模型假设。例如,如果应用程序期望读取是原子的(即,总是最新的数据),则需要由一致性服务来满足需求。该服务使用 4 个构件构建而成:
- 互斥(ME)原语 : RW 和 WW 互斥。
- 排序原语 : RW 和 WW 排序
- **原子性原语😮-1 更新语义的单元
- **事务支持原语:可以使用 ME、原子性和排序来实现不同形式的事务语义
保证的粒度可以是基于每个对象的,也可以是更宽的命名空间粒度,例如同一容器/目录内的对象。
9.耐久性服务
数据持久性确保在发生硬件故障、组件损坏和软件错误时,数据丢失最少。最流行的数据持久性方法是创建多个数据副本。复制会带来空间使用方面的开销(例如,容量的 3 倍),但在部分更新开销和恢复期间需要读取的数据量方面更便宜。相比之下,与复制相比,跨节点的擦除编码具有相反的利弊。随着全闪存环境的采用,擦除编码在最*的研究中备受关注。
建立耐久性服务需要决定以下设计方面:
- **耐久性模型:定义如何实现冗余来处理硬件故障
- **副本字节级保真度:定义副本与内容相似的程度
- **副本一致性:不同层次的一致性语义
- **传播副本更新:更新实际上是如何传播的
- 此外,选择副本协调器、订购副本更新、读取期间的副本选择、副本放置、副本访问、地理冗余协议
10.对象管理器服务
对象管理器服务负责管理与命名空间对象相关联的资源。为了将数据保存在持久介质上,对象管理器将硬件抽象成一个资源单元。设计对象管理器涉及以下 5 个方面:
- **资源单元:从持久介质中读取/写入数据的最小级别。该单元可以是文件、块、存储器地址。
- **名称空间到资源单元:名称空间对象映射到单个还是多个资源单元
- **命名空间对象的元数据:元数据是否和数据一起持久化在同一个资源单元内
- **垃圾收集:如何回收空间
- **删除模式:如何处理对象的删除
11.缓存/缓冲服务
通常,写缓冲和分布式缓存是作为单独的服务实现的。
缓冲最初是为了弥补 CPU 和磁盘速度之间的差距而构建的。想法是将更新组合在一起,将它们写入持久的介质中(以避免每次操作的毫秒级延迟)。这类似于数据库中的组提交。缓存侧重于在内存中维护经常访问的数据,以减少磁盘流量并提高延迟性能。当存在工作负载访问的局部性时有用;也是预取数据的可预测模式。关键的设计方面是:
- 缓冲冲洗模式
- 缓冲读写一致性
- 放置缓存服务
- 缓存策略
- 缓存失效协议
12.资源管理服务
资源管理服务决定数据在物理硬件上的实际布局。涉及三个设计方面:
- **磁盘布局:不同的布局针对不同的访问模式和数据类型进行优化。
- **跨节点和磁盘条带化数据:节点/服务器中有多个磁盘,集群中也有多个服务器。
- **考虑集群异构性:这是一个重要方面,因为硬件是经过多代购买的,具有不同的属性。
好奇这 12 个概念如何在现实世界的系统中发挥作用——这里是分析 HDFS 、卡珊德拉、 MongoDB 、 Redis 的表
希望这篇博客能让你开始探索现代大数据系统的旅程。掌握的最好方法是选择一个开源项目,并尝试填写细节(类似于上面的示例案例研究)。帮你入门,下面是 工作表 。
最后,以我个人的经验来看,了解分布式系统的内部会成为更好的数据工程师。我最喜欢引用莱斯利·兰波特的话:“在分布式系统中,一台你甚至不知道存在的计算机的故障会导致你自己的计算机无法使用。”
渴望进一步磨砺你的数据工程技能:
关于 AWS SageMaker 的分布式培训
了解 AWS SageMaker 上可用于分布式培训的数据并行和模型并行选项。
艾莉娜·格鲁布尼亚克在 Unsplash 上的照片
在当今世界,当我们可以访问海量数据、更深更大的深度学习模型时,在本地机器上的单个 GPU 上进行训练很快就会成为瓶颈。一些模型甚至不适合单个 GPU,即使它们适合,训练也可能非常慢。在这种设置下,即在大量训练数据和模型中,运行单个实验可能需要几周甚至几个月。因此,这可能会妨碍研究和开发,并增加制作概念证明所需的时间。然而,让我们欣慰的是云计算是可用的,它允许人们设置远程机器,并根据项目的要求对它们进行配置。这些都是可扩展的(向上和向下),不需要你来维护。因此,管理费用由为您提供服务的公司承担。一些主要的提供商有:亚马逊 AWS、谷歌云、IBM 云、微软 Azure 等。(按字母顺序排列。)我们将研究使用 AWS SageMaker 的分布式培训来解决伸缩问题。
分布式培训
它有助于解决缩放模型大小和训练数据的挑战[1]。正如前面所讨论的,虽然增加模型大小和复杂性可以提高性能(取决于问题陈述),但单个 GPU 可以容纳的模型大小是有限制的。此外,缩放模型大小会导致更多的计算和训练时间。如果您有非常大的数据集,情况也是如此。
在分布式训练中,训练模型的工作量被分割并在多个小型处理器之间共享,这些处理器被称为工作节点[2]。这些工作节点并行工作以加速模型训练。
基于我们选择如何分割工作,这里有两种主要类型的分布式训练,即数据并行和模型并行。
数据并行性
这是最常见的分布式培训方法。这个想法是,我们有很多训练数据,所以我们批量处理这些数据,并将数据块发送到多个 CPU 或 GPU(节点)以供神经网络处理[1]。数据块/分区的数量等于计算集群中可用节点的数量。模型被复制到这些 worker 节点中的每一个,并且每个 worker 操作它自己的数据子集。我们需要理解,每个节点都必须有足够的计算能力来支持正在训练的模型,即模型必须完全适合每个节点[2]。每个节点都有模型的副本(精确副本),并独立计算其各自数据批次的正向传递、反向传递和梯度。在移动到下一批并最终移动到另一个时期之前,与其他节点共享权重更新以进行同步。
数据并行性:相同的模型在不同的工作节点之间被复制和共享。每个工人都可以访问不同的数据子集进行培训。每个步骤之后的参数更新通过通信在不同的节点上聚集。(图片来源:作者)
SageMaker 分布式数据并行库: AWS SageMaker API 可以让你轻松进行数据并行分布式训练,而无需对你的脚本进行大量修改。它为您处理集群的创建。它还以两种方式解决了这种培训所需的通信开销:
- 该库执行 AllReduce,这是分布式训练期间的一个关键操作,它负责很大一部分通信开销。算法负责使用某种操作将梯度聚合在一起,并将结果转发给所有工人。你可以在这里了解更多信息:
- 分布式深度学习背后的技术:AllReduce |首选网络研究&开发
- 该库通过充分利用 AWS 的网络基础设施和 Amazon EC2 实例拓扑来执行优化的节点到节点通信。
以下是 AWS Sagemaker 和 PyTorch 的数据并行库的基准测试。(虽然我没有亲自验证这些数字。)我们确实观察到了 AWS DDP 库的加速。
来源:SageMaker 分布式数据并行库介绍—亚马逊 SageMaker
模型并行性
在模型并行性(网络并行性)中,模型被分割成不同的部分,这些部分可以在不同的节点上并发运行,并且每个部分都将运行在相同的数据上。其可扩展性取决于算法的任务并行化程度,实现起来比数据并行更复杂。工作者节点只需要同步共享参数,通常对于每个向前和向后传播步骤同步一次。在这种训练中考虑几个因素是很重要的[1]。
模型并行性:根据某种算法,模型被手动或自动分割成不同的分区。每个分区都被赋予相同的数据批次。模型分区的执行基于调度策略。根据计算图形关系,激活/层输出在分区之间传递。(图片来源:作者)
- 如何跨设备分割模型:模型的计算图(PyTorch 和 TensorFlow 中的计算图)、模型参数和激活的大小以及资源约束(例如内存和时间)决定了最佳的分区策略。为了减少有效分割模型所需的时间和精力,您可以使用 Amazon SageMaker 的分布式模型并行库提供的自动化模型分割特性。
- 实现并行化:在深度学习中,模型训练是高度有序的,因为我们运行正向计算和反向传播来获得梯度。每个操作必须等待另一个操作计算其输入。因此,深度学习训练的正向和反向传递阶段不容易并行化,天真地将一个模型拆分到多个 GPU 上可能会导致设备利用率低下。例如, GPU i+1 上的层必须等待来自 GPU i 上的飞片的输出,因此, GPU i+1 在此等待期间保持空闲。模型并行库可以通过构建高效的计算调度来实现流水线执行,从而实现真正的并行化,其中不同的设备可以同时对不同的数据样本进行前向和后向传递。
要进一步了解这些策略,我建议您阅读 Jordi TORRES 的关于并行和分布式基础设施的可扩展深度学习。艾。
AWS 的 SageMaker 的分布式模型并行[5]: 它通过提供自动化的模型拆分和复杂的流水线执行调度,使模型并行更容易实现。模型分割算法可以优化速度或内存消耗。该库还支持手动分区。该功能内置于 PyTorch/TensorFlow 估计器类中。
自动化模型分割:顾名思义,这个库为您处理模型分割。它使用一种分区算法来*衡内存,最大限度地减少设备之间的通信,并优化性能。您可以配置分区算法来优化速度或内存。当第一次调用 smp.step 修饰函数时,自动分区发生在第一个训练步骤中。在调用期间,库首先在 CPU RAM 上构建模型的一个版本,然后分析模型图以做出分区决定。基于这个决定,每个模型分区被加载到一个 GPU 上,并且只执行第一步。
手动模型分割:如果您想要手动指定模型如何在不同的设备上分割,手动分割也是可能的。为此,您可以使用 smp.partition 上下文管理器。
流水线执行时间表:sage maker 的 DMP 库的一个基本特性是流水线执行,它决定了在训练期间跨设备进行计算和处理数据的顺序。这是一种在模型并行中实现真正并行化的技术,通过让 GPU 同时对不同的数据样本进行计算,并克服顺序计算带来的性能损失。流水线操作是基于将一个小批量分成多个小批量,这些小批量被一个接一个地送入训练流水线,并遵循由库运行时定义的执行时间表。有两种类型的管道:
- 交错流水线:在这种流水线中,只要有可能,微批处理的向后执行就会被优先考虑。它允许更快地释放用于激活的内存,更有效地使用内存。
- 简单管道:在这个管道中,每个微批次的前向传递在开始后向传递之前完成。这意味着它仅在自身内部管道化前向传递和后向传递阶段。
结论
我们看到,数据并行和模型并行是可用于深度学习的两种主要类型的分布式训练技术。数据并行比模型并行更容易。AWS SageMaker 对这两者都有选择,并很好地集成到 SageMaker PyTorch/TensorFlow 估计器中。我将在另一篇文章中举例说明如何用代码做到这一点。希望你喜欢这篇文章。关注更多有趣的文章。如果你对分布式培训有什么偏好,请在评论中分享。你可以在 LinkedIn 上和我联系。
参考
[3] PyTorch 分布式概述— PyTorch 教程 1.9.0+cu102 文档
[4] 理解机器学习中的数据并行性
[5]SageMaker 分布式模型并行的核心特性——亚马逊 SageMaker
使用 Pyspark 的分布式情感分析学习
Pyspark 中不同方法在学习速度和准确性方面的权衡
在本文中,我将展示 Pyspark 中不同方法在学习速度和识别积极和消极 twit 任务的准确性方面的权衡。
照片由🇨🇭·克劳迪奥·施瓦茨| @purzlbaum 在 Unsplash
情绪分析在社交媒体监控中非常有用,因为它允许我们获得特定话题背后更广泛的公众意见的概览。这个任务已经用不同的框架和编程语言进行了大量的研究和实现。
在本文中,我将展示我们如何在 Pyspark 中对数据块使用不同的方法,我们可以获得哪些指标,最重要的是我们需要多少时间。众所周知,模型时间性能可能是现实生活中最重要的事情,因为新数据和案例增长非常快,我们几乎每天都需要重新训练我们的模型。第二件事是模型预测响应时间,如果模型具有 99.9%的准确性,这是非常好的,但如果在输入数据流期间模型响应时间太长,我们就会错过机器和深度学习给我们带来的所有好处。
在这项研究中,我使用了来自 Kaggle 的 Twitter 和 Reddit 情感分析数据集。
我的第一种做法是—“HashingTF+IDF+Logistic 回归” 。
让我们开始弄清楚它是什么以及它是如何工作的。词频-逆文档频数(TF-IDF) 是一种广泛应用于文本挖掘的特征矢量化方法,用以反映语料库中某个词对某个文档的重要性。
术语频率和文档频率的定义有几种变体。在 MLlib 中,将 TF 和 IDF 分开,使它们具有灵活性。
TF:HashingTF
和CountVectorizer
都可以用来生成术语频率向量。
HashingTF
是一个Transformer
,它接受多组术语,并将这些集合转换成固定长度的特征向量。在文本处理中,“术语集”可能是一个单词包。HashingTF
利用哈希技巧。通过应用散列函数将原始特征映射到索引(术语)中。这里使用的哈希函数是 MurmurHash 3 。然后基于映射的索引计算术语频率。这种方法避免了计算全局术语-索引映射的需要,这对于大型语料库来说可能是昂贵的,但是它遭受潜在的哈希冲突,其中不同的原始特征在哈希之后可能变成相同的术语。为了减少冲突的机会,我们可以增加目标特征维数,即哈希表的桶的数量。由于使用散列值的简单模来确定向量索引,建议使用 2 的幂作为特征维数,否则,特征将不会均匀地映射到向量索引。默认特征尺寸为218=262,144218=262,144
。可选的二进制切换参数控制术语频率计数。当设置为真时,所有非零频率计数被设置为 1。这对于模拟二进制而非整数计数的离散概率模型尤其有用。
IDF : IDF
是一个Estimator
,它适合数据集并产生一个IDFModel
。IDFModel
获取特征向量(通常由HashingTF
创建)并缩放每个特征。直观上,它降低了语料库中频繁出现的特征的权重。
这是响应文本预处理的管道模式的第一部分。我的第一个模型是逻辑回归,它是机器学习中最简单和快速的模型之一,但如果有良好的数据预处理,它可以给出良好的结果。在统计中,使用逻辑模型(或 logit 模型)对某类或某事件存在的概率进行建模,如正类或负类。逻辑回归使用最大似然技术实现最佳预测。Sigmoid 是一个数学函数,其特征是可以取-∞和+∞之间的任何实数值,并将其映射到 0 到 1 之间的实数值。因此,如果 sigmoid 函数的结果大于 0.5,那么我们将其归类为正类,如果小于 0.5,那么我们可以将其归类为负类。
结果我们得到了:
- 准确度分数:0.8771
- ROC-AUC: 0.8529
- 训练时间 56.79 秒
第二种方法是— “计数向量器+ IDF +逻辑回归” 。这种方法与前一种非常相似,但是对于文本预处理,我想使用 CountVectorizer 方法。那么,它是什么,它是如何工作的?
CountVectorizer
和CountVectorizerModel
旨在帮助将一组文本文档转换成令牌计数的向量。当没有先验词典时,可以使用CountVectorizer
作为Estimator
来提取词汇,并生成一个CountVectorizerModel
。该模型为词汇表中的文档生成稀疏表示,然后可以将其传递给 LDA 等其他算法。
在拟合过程中,CountVectorizer
将选择语料库中按词频排序的前vocabSize
个单词。可选参数minDF
也通过指定一个术语必须出现在词汇表中的最小文档数(如果是< 1.0,则为分数)来影响拟合过程。另一个可选的二进制切换参数控制输出向量。如果设置为真,所有非零计数都被设置为 1。这对于模拟二进制而非整数计数的离散概率模型尤其有用。
结果我们得到了:
- 准确度分数:0.8931
- ROC-AUC: 0.8708
- 训练时间 1.27 分钟
如你所见,准确率更高,但我们需要按训练时间付费。
第三种方法是——N-gram+Chi *方 + Logistic 回归 。
在这个方法中,我使用 N-gram 作为文本预处理的技术,使用卡方作为特征选择的方法。
让我们从 N-gram 开始。n-gram 是某个整数 n 的 n 个标记(通常是单词)的序列。NGram 类可用于将输入要素转换为 n-gram。NGram 将一系列字符串作为输入(例如,分词器的输出)。参数 n 用于确定每个 n 元语法中的项数。输出将由一系列 n-gram 组成,其中每个 n-gram 由一个由 n 个连续单词组成的空格分隔的字符串表示。如果输入序列包含的字符串少于 n 个,则不会产生输出。Spark 不会自动组合来自不同 n-gram 的特征,所以我不得不在管道中使用 VectorAssembler 来组合我从每个 n-gram 获得的特征。
这种方法的下一步是特征选择。要素选择是自动选择数据中对您感兴趣的预测变量或输出贡献最大的那些要素的过程。在对数据建模之前执行要素选择的好处是:
- 避免过度拟合:较少的冗余数据会提高模型的性能,从而减少根据噪声做出决策的机会
- 减少训练时间:更少的数据意味着算法训练更快
**卡方检验用于数据集中的分类特征。我们计算每个特征和目标之间的卡方,并选择具有最佳卡方分数的期望数量的特征。它确定样本的两个分类变量之间的关联是否反映了它们在总体中的真实关联。
机器学习模型不变——逻辑回归。
结果我们得到了:
- 准确度分数:0.9111
- ROC-AUC: 0.8921
- 训练时间 15.25 分钟
让我们做同样的,但没有特征选择。于是,我的第四种方法——N-gram+Logistic 回归*** 。这里我想利用 N-gram 产生的序列分析能力和快速的逻辑回归。*
结果我们得到了:
- 准确度分数:0.9022
- ROC-AUC: 0.8816
- 训练时间 1.54 分钟
正如我们所看到的,在训练时间不可思议地减少的同时,精确度降低了一点点。
好吧,逻辑回归是好的,但是如何使用更复杂的东西呢——梯度增强树。让我们保留相同的数据预处理—N-gram +卡方,并增加梯度增强分类器的功能。所以,我的下一个迭代将是—N 元文法+卡方 + 梯度提升树分类器。
梯度推进树(GBTs)是一种使用决策树集成的流行分类和回归方法。GBTs 迭代地训练决策树以最小化损失函数。在梯度提升中,弱学习器的集合用于提高机器学习模型的性能。弱学习者通常是决策树。结合起来,它们的输出会产生更好的模型。在梯度推进中,弱学习者顺序工作。每个模型都试图改进前一个模型的误差。
结果我们得到了:
- 准确度分数:0.8003
- ROC-AUC: 0.7227
- 训练时间 52.408 分钟
对我来说,它太长了,而且我们有很多风险让 GBT 生产的模型过度拟合。GBT 是一个非常好的模型,但在这种情况下,我们有太多的特征,对我来说卡方检验法可能不太好。
让我们检查相同的管道,但是没有卡方检验,只是为了检查如果我们排除特征选择,我们节省了多少时间。于是,就有了下一种方法——N-gram+梯度提升树分类器。
结果我们得到了:
- 准确度分数:0.7976
- ROC-AUC: 0.7185
- 训练时间 39.564 分钟
我们真的可以减少时间,但正如我之前所说,我们的算法确实需要更少的功能,所以我们可以看到我们在精度上损失了一点。
假设我们不知道或不想在我们的管道中使用任何其他特征选择技术,但仍然希望提高准确性。在这种情况下,我们可以使用深度学习的力量。
**在接下来的步骤中,我还想保留相同的数据预处理管道,但使用深度学习(MLP)模型— **N-gram +卡方+MLP。
多层感知器分类器(MLPC)是基于前馈人工神经网络的分类器。MLPC 由多层节点组成。网络中的每一层都完全连接到下一层。输入层中的节点表示输入数据。所有其他节点通过输入与节点权重w
和偏差b
的线性组合以及应用激活函数,将输入映射到输出。这可以用矩阵形式写为 MLPC 与K+1
层如下:
中间层中的节点使用 sigmoid(逻辑)函数:
输出层中的节点使用 softmax 函数:
输出层中的节点数量N
对应于类的数量。MLPC 采用反向传播来学习模型。我们使用逻辑损失函数优化和 L-BFGS 作为优化程序。
在我的例子中,我将对 64 和 32 个神经元使用 2 个隐藏层。
结果我们得到了:
- 准确度分数:0.9261
- ROC-AUC: 0.9143
- 训练时间 70.2 分钟
与以前的方法相比,这是最好的结果,但是训练时间太长,而且只针对少量数据。
让我们检查相同的管道,但是没有卡方检验,只是为了检查如果我们排除特征选择,我们节省了多少时间。于是,就有了下一种方法——N-gram+MLP。
结果我们得到了:
- 准确度分数:0.9115
- ROC-AUC: 0.8975
- 训练时间 54.108 分钟
我们确实节省了时间,但如果我们将结果与之前的所有测试进行比较,我们会发现使用逻辑回归的方法会更好,这种方法可以产生几乎相同的准确性,但时间更短。
以下是所有结果的表格和图表:
作者的实验结果
结论
结论很简单——建模越快,就越希望使用简单的模型和数据预处理管道。在本文中,我只使用了数据科学的全部方法和模型中的一小部分。
对于这个任务,你也可以使用架构比 MLP 更复杂的 TensorFlow 或者 PyTorch 框架。要使用 Databricks 的强大功能,您还可以使用分布式培训方法。 Horovod 是一个用于 TensorFlow、Keras 和 PyTorch 的分布式培训框架。Databricks 支持使用 HorovodRunner 和horovod.spark
包的分布式深度学习训练。对于使用 Keras 或 PyTorch 的 Spark ML 流水线应用,可以使用horovod.spark
估算器 API 。
此外,您可以使用微调语言模型进行情感分析任务,例如使用 BERT 和所有其他衍生工具。
你能在 Git 仓库中找到的所有代码— 链接。
你也可以在我的文章中阅读如何使用 Keras 进行情感分析——“深度学习方法的假新闻检测器(第二部分)建模”。
分布分析器
实践教程
用 Streamlit 制作的 Web 应用程序
Distribution Analyser 是一个 web 应用程序,允许您以交互方式探索连续分布,并使分布符合您的数据。Distribution Analyser 是使用 Streamlit 构建的,Streamlit 是一个用于制作定制 web 应用的开源 Python 库。
分布分析器
分布分析器的出现
不久前,我在用“世界幸福”数据集做探索性的数据分析,思考我要如何呈现这些数据,我在各种可视化中寻找灵感。很快,我停在了配对剧情上,我只是盯着对角线上的支线剧情,看着各种各样的分布。过了一会儿,我发现自己在scipy . stats页面上对列出的发行版数量感到敬畏,因为它们中的大多数我都不认识。我首先想到的是——他们长得怎么样?****
当然,互联网提供了一些关于这些分布看起来如何的答案,但是我并不满意,所以我开始了创造我的可视化的任务。这个项目的结果是一个包含所有分布、它们的名称和方程的大图,除此之外,将鼠标悬停在图上可以看到一个放大的区域。我把这个图像放在我的网站上,并在社交媒体上分享——这吸引了人们的注意。你可以在我的网站上找到这个高分辨率的可视化效果,并在下面看到一个. gif 示例。
虽然这个项目给了我最初问题的答案,但我想要更多的自由、交互性和有用性——这让我产生了创建一个 web 应用程序的想法。我一点一点地将我最初的想法转换成代码,出现了分布分析器。****
分布分析器概述
在“分布分析器”1.0 版中,主要的应用选项有:
- 探索发行版 — 交互探索 SciPy v1.6.1 的连续发行版函数。如果用户想熟悉某个特定的发行版,这个特性非常有用。通过与分布参数的相互作用,可以很容易地看到分布是如何变化的。
- 拟合分布 — 导入您的数据并拟合选定的连续分布。如果用户希望快速了解如何以及哪些分布适合他们的数据,该功能非常有用,使他们能够缩小分布的选择范围,继续进行数据分析。
探索发行版
有 100 个连续分布可以玩。对于每个选定的分发,用户将获得:
- 方程(概率密度函数);
- 分布图(带可调组件,见图 2。);
- 分布参数—可通过滑块或手动输入进行调整。在选择时,每个参数的名字旁边都会写有它的默认值(如在 SciPy 中);
- 到官方 SciPy 文档的超链接,有更多的细节。
图一。选择发行版的演练,探索发行版如何通过改变其参数(通过滑块和手动输入)来改变,并演示到官方 SciPy 文档的超链接。
调整显示
对于在图上显示什么有一点自由会大有帮助。也许用户只希望看到一个直方图,也许用户希望看到尽可能多的信息,因此,用户可以选择他们希望在图中显示的内容:
- 直方图;
- 概率密度函数;
- CDF——累积密度函数;
- SF —生存函数;
- P(X <=x) — Probability of obtaining a value smaller than x for selected x;
- Empirical quantiles for a data array: Q1, Q2, Q3 respectively 0.25, 0.50, 0.75;
- Shaded: mean± xσ (σ 为标准差)。
- 箱线图。
用户还可以选择在亮或暗主题中显示图形——这很重要,不仅因为他们可以根据自己的屏幕亮度调整图形,而且重新使用所需的图形会更方便。除了图形之外,用户还可以显示关于所选分布及其参数的统计信息——这些信息将出现在图形下方的表格中。通过此功能,用户可以很容易地看到统计信息是如何随着分布参数的变化而变化的(例如*均值)。
图二。调整地物组件(可以打开/关闭每个特性)的演练,演示地物模式:暗/亮并显示带有统计描述的表格。
出口代码
用户将收到预填充的 Python 代码及其选定的参数,以便他们可以重用它。拥有这样的选项可以节省用户的时间和精力来复制他们选择的发行版。我写了一篇关于此功能重要性的文章,您可以在此处阅读该文章:
****
图 3。使用选定的分发版及其参数导出 Python 代码的演练,这些代码可重复使用(在本例中,使用 Spyder 在 Python 中本地绘图)。****
拟合分布
数据导入和检查
上传. csv 文件后,用户可以通过绘制直方图和叠加基本统计信息来检查他们的数据。然后,用户选择分布来适应他们的数据,或者选择任意数量的单个分布,或者选择“所有分布”选项来适应所有可用的分布(这需要 10-15 秒)。
图 4。演示数据上传、检查数据和选择我们希望适合数据的发行版的演练。这里选择了“ All_distributions ”选项,但也可以选择任意数量的发行版。
结果
用户将获得最佳拟合分布、其参数和误差估计,以及到官方 SciPy 文档的链接。拟合结果的形式为:
- 交互式图形:两个图形是使用 Bokeh 制作的,Bokeh 是一个用于创建交互式可视化的 Python 库。在一张图中,pdf 被覆盖,而在另一张图中,CDF 被覆盖。拥有一个互动图形的重要性如下所示——有时拟合可能会脱离图表,使图表无法检查。使用散景互动,用户可以放大图形的特定部分,并通过互动图例选择在图形上显示哪个分布(15 个最佳分布中的一个)。****
- 表:所有分布及其参数的列表,按误差估计排序(从最佳到最差)。
- Python 代码具有最佳拟合分布及其参数。
图 5。检查拟合结果的演练:1)两个交互图形:一个用于检查 pdf,一个用于检查 CDFs2)具有所有分布、它们的拟合参数和误差估计的表格;3)包含可重复使用的最佳分布的 python 代码(在这种情况下,使用 Spyder 在 Python 中本地绘图)。
最后
Distribution Analyser 是一个 web 应用程序,它允许您以交互方式探索连续分布,使分布符合您的数据,并获得比数字更多的信息。
我向所有人推荐这款应用,无论他们是发行版新手还是专家,无论他们是学生还是教师,因为:
- 通过与分布参数的交互,用户可以非常快速和容易地熟悉特定的分布。
- 用户可以快速查看哪些分布适合他们的数据,从而缩小他们需要用于分析的分布的数量。
分布分析器 是通过 Streamlit 共享部署的,你可以在这里访问。我的GitHub上有完整的源代码。新的应用功能正在准备中,敬请关注。
*本文所有图均为作者所作。
深度学习模型的基于分布的损失函数
实践教程
交叉熵及其度量分类损失的变体综述——以高度不*衡数据集为例
赋予数据意义
信息由数据组成。在训练步骤期间,人工神经网络学习将一组输入映射(预测)到来自标记数据集的一组输出。计算最佳权重是一个优化问题,通常通过 随机梯度下降 来解决:使用预测误差的反向传播来更新权重。梯度下降算法更新沿着误差的梯度(或斜率)向下导航的权重,从而可以减少下一次预测的误差。从本质上讲,这就是神经网络的工作方式。
什么是损失函数?
在寻找最佳权重时,学习算法需要一个特殊的对象来评估一组候选权重的适用性:这是目标函数。
照片由 Afif Kusuma 在 Unsplash 上拍摄
根据上下文,目标函数可以最大化或最小化。在处理深度学习模型时,专家更喜欢根据误差进行推理,因此他们的目标是最小化目标函数。因此,目标函数称为损失函数,其值(即误差)简称为 损失 。损失函数对于确保模型响应的适当数学表示至关重要,必须仔细考虑其选择,因为它必须适合模型域及其分类目标。
损失函数的定义和应用始于标准的机器学习方法。当时,这些函数是基于标签的分布,正是由于这个原因,下面的函数被称为基于分布的损失函数。
我们的讨论正是从这里开始的,特别是从交叉熵的概念开始。
注意:所有用于情节和案例研究的代码都可以在我的个人 github 上获得:https://github . com/Andrea-ci/misc-stuff/tree/master/nn-metrics。
引入交叉熵
照片由 Greyson Joralemon 在 Unsplash 上拍摄
根据信息论,给定一个离散随机变量 x ,其熵 H(x) (也被称为香农熵,由其创造者克劳德·香农定义为概率的倒数的对数的期望值:
熵—作者提供的图像
直观地说,熵衡量与随机过程的可能结果相关的不确定性:对观察者来说,它们越“令人惊讶”,熵就越高。
作者的伯努利变量图像的熵
请注意,对数以 2 为底,因为在信息论的背景下,我们感兴趣的是对随机过程携带的信息进行编码所需的比特数。
现在假设我们有两个概率分布, p 和 q ,定义在同一个随机变量 x 上。如果编码方案是针对 q 而不是 p 优化的(这将是真实的分布),我们想要测量从 x 的样本空间中识别一个事件所需的*均比特数。
在可能的结果只有两个的情况下,如在前面的伯努利变量的例子中,我们对二元交叉熵损失函数有如下定义:
二元交叉熵—作者图片
具有二元场景允许简化方程,使得我们只有一个自变量, pt,,其代表由模型分配给真实类别(即样本实际所属的类别)的概率值。
二元交叉熵被广泛用作损失函数,因为它对许多分类任务都很有效。事实上,它是基于分布的损失函数的基本基线。
作者图片
图中, yt 是二元分类任务中样本的类别标签, yp 是模型赋予该类别的概率。该函数严重惩罚预测错误,其中以高置信度做出错误决策(即 yp 接* 0 ),而当预测接* 1 的真值时(即做出正确分类),该函数变为零。
注意:为了简单起见,我们将保留与二元分类任务相关的讨论和示例。然而,到目前为止以及在下文中所做的考虑是有效的,并且自然也适用于多标签分类的情况。
交叉熵的演化
在实践中,我们必须经常处理非常不*衡的数据集:例如,如果我们想要训练一个模型来识别海洋中的船只,那么正像素的数量,即那些属于“船”类的像素,相对于所有像素来说将是非常小的百分比。
有时阳性样本就像四叶草一样罕见——达斯汀·休姆斯在 Unsplash 拍摄
因此,基于它们在数据集中的丰度,或者换句话说,根据它们的先验分类概率,具有能够以不同方式处理不同类别标签的损失函数将是有用的。
为此开发了两种二进制交叉熵:加权二进制交叉熵和*衡交叉熵。
加权二进制交叉熵 ( WBCE )使用一个系数对阳性样本进行加权,当数据表现出明显的偏斜时,它通常是首选:
加权二进制交叉熵—作者图片
*衡二进制交叉熵()类似于加权交叉熵,但是在这种情况下,负样本也受到如下权重系数的影响:****
*衡二进制交叉熵—作者图片
最后,焦点损失(FL) 也可以看作是二元交叉熵的变化。此函数适用于高度不*衡的数据集,因为它对正确分类的样本的贡献进行加权,并允许更好地学习硬样本(即未检测到的阳性样本):
焦点丢失—作者提供的图像
这里 λ 是一个超 - 参数,我们可以用它来校准错误分类样本的重要性。直观地说, λ 通过扩展简单样本接收低值损失的范围来减少简单样本的损失。
作者图片
注意,当 λ=0 时,我们得到标准交叉熵损失。
此外,聚焦损失还可以采用另一个参数,通常称为 α 参数,其允许进一步*衡误差项:
焦点丢失的 Alpha 版本—图片由作者提供
案例研究:信用卡欺诈的检测
马库斯·温克勒在 Unsplash 拍摄的照片
让我们举一个例子,其中选择聚焦损失允许我们解决一个在极度不*衡的数据集上的二进制分类问题。
为此,我们考虑托管在 Kaggle 上的信用卡欺诈检测数据集。正如他们的网页上所说:
“该数据集包含欧洲持卡人在 2013 年 9 月的信用卡交易。
该数据集显示了两天内发生的交易,其中 284,807 笔交易中有 492 笔欺诈。数据集高度不*衡,正类(欺诈)占所有交易的 0.172%。”
不言而喻,这里的目标是检测所有交易中欺诈操作的(非常)小的子集。在下文中,我们给出了以下步骤的简要概述。
首先,我们加载数据集并执行一些清理,以便为模型训练准备数据。
**#
# DATA LOADING/CLEANSING
## Load data from CSV file.
df_raw = pd.read_csv(‘creditcardfraud/creditcard.csv’)
n_samples = len(df_raw)
print(f’Num. of samples: {n_samples}.’)# Check size of samples.
df_pos = df_raw[df_raw[‘Class’] == 1]
n_pos_samples = len(df_pos)
pos_ratio = 100 * n_pos_samples / n_samples
print(f’Num. of positive samples: {n_pos_samples} ({pos_ratio:.2f}% of total).’)# Drop useless data and convert amount to log space.
df_cleaned = df_raw.copy()
df_cleaned.pop(‘Time’)
df_cleaned[‘log-amount’] = np.log(df_cleaned.pop(‘Amount’) + 0.001)# Double train/test split for testing and validation data.
df_train, df_test = train_test_split(df_cleaned, test_size = 0.2, shuffle = True)
df_train, df_valid = train_test_split(df_train, test_size = 0.2, shuffle = True)print(f’Size of training data: {len(df_train)}.’)
print(f’Size of validation data: {len(df_valid)}.’)
print(f’Size of test data: {len(df_test)}.’)# Extract labels and features from data.
labels_train = np.array(df_train.pop(‘Class’))
labels_valid = np.array(df_valid.pop(‘Class’))
labels_test = np.array(df_test.pop(‘Class’))
features_train = np.array(df_train)
features_valid = np.array(df_valid)
features_test = np.array(df_test)# Normalize data.
scaler = StandardScaler()
features_train = scaler.fit_transform(features_train)
features_valid = scaler.transform(features_valid)
features_test = scaler.transform(features_test)# Enforce lower/upper bounds.
features_train = np.clip(features_train, -5, 5)
features_valid = np.clip(features_valid, -5, 5)
features_test = np.clip(features_test, -5, 5)
n_features = features_train.shape[-1]**
我们可以看到数据集实际上是极不*衡的,阳性样本只占预期总数的 0.17% 。
样本大小—图片由作者提供
使用 Keras ,我们建立了一个简单的模型,并使用二进制交叉熵作为损失函数来训练它。这是我们的基线模型。然后我们采用焦损函数来代替,并比较所获得的性能。
Keras logo —来源: keras.io 主页
作为评估指标,我们认为真阳性、假阴性和回忆指标、是因为我们对衡量模型预测欺诈的能力感兴趣。****
事实上,在这种情况下,使用准确性并不是一个正确的选择:一个总是预测假(即没有欺诈)的“模型”将在这个数据集上获得超过 99.8%的准确性。这就是为什么很难在如此不*衡的数据上训练一个模型。**
**#
# MODEL TRAINING
## Model parameters.
opt = Adam(learning_rate = 1e-3)metrics = [
TruePositives(name = 'tp'),
FalseNegatives(name = 'fn'),
Recall(name = 'recall')
]losses = [
BinaryCrossentropy(),
SigmoidFocalCrossEntropy(gamma = 2, alpha = 4)
]loss_names = [
'binary cross-entropy',
'focal loss'
]logs_loss = []
logs_recall = []for loss in losses: # Setup/compile the model.
model = Sequential()
model.add(Dense(16, input_dim = n_features, activation = 'relu',
kernel_initializer = 'he_uniform'))
model.add(Dropout(0.5))
model.add(Dense(1, activation = 'sigmoid'))
model.compile(optimizer = opt, loss = loss, metrics = metrics) # Fit the model.
logs = model.fit(features_train, labels_train, validation_data = (features_valid,
labels_valid), epochs = EPOCHS, verbose = 0) logs_loss.append(logs.history['loss'])
logs_recall.append(logs.history['recall']) # Evaluate the model.
eval_train = model.evaluate(features_train, labels_train, verbose = 0)
eval_test = model.evaluate(features_valid, labels_valid, verbose = 0)table = PrettyTable()
table.field_names = ['Data', 'Loss', 'TruePositives', 'FalseNegatives', 'Recall'] for stage, eval_info in zip(('training', 'test'), (eval_train, eval_test)):
row = [stage]
for ii, lbl in enumerate(model.metrics_names):
row.append(f'{eval_info[ii]:.3f}')
table.add_row(row) print('\n')
print(table)**
在训练和验证了这两种配置之后,我们可以比较所获得的结果。从基线模型中,我们得到:
二值交叉熵分类—按作者分类的图像
相反,使用焦点损失时,分类性能会显著提高,并且所有欺诈案例都会被正确检测到:
焦点损失分类—按作者分类的图像
让我们也比较两个损失函数的行为。
损失之间的比较—按作者分类的图像
正如所料,焦点损失值低于交叉熵值。焦点损失会降低被错误分类的阳性样本(欺诈)的权重,从而“鼓励”模型提高对欺诈案例的敏感度。
参考资料:
[1]宗-林逸、普里亚·戈亚尔等人,密集物体探测的焦损失
[2] Hichame Yessou 等,多标签遥感影像分类的深度学习损失函数比较研究
[4] 不*衡数据的分类
使用 Python 的分布可视化 101
选择你的武器:直方图,密度图,或方块图
数据就像音乐,以错误的方式呈现会变得很糟糕(图片来自 Unsplash
处理数据时,快速理解数据的最佳方式是通过可视化。与其以表格的形式分析它们,不如将它们可视化,以便快速直观地理解。通过可视化数据,您甚至可能会发现令人惊讶的结果。
“我们大多数人需要听音乐才能理解它有多美。但这通常是我们展示统计数据的方式:我们只展示音符,不演奏音乐。”— 汉斯·罗斯林
我们可以从我们的数据中获得许多有趣的见解,这可以通过了解数据的分布来获得。可视化数值数据的分布可以给我们一些见解,比如数据的偏斜度、模式(最高峰)、分布形状等等。
使用密度图的分布可视化示例(图片由作者生成)
有时,我们也有兴趣看到多个数值观测值的分布。例如,我们可能对比较 5 个不同亚洲国家的人的身高感兴趣。为了从统计学上确认组间的差异,我们可以利用 t 检验(2 组)或 ANOVA(2 组以上)。但是,一个直观的方式来看到不同群体之间的差异是可视化。
直观显示差异的一个简单方法是将同一观测值的不同组的分布图叠加起来。下面是一些最流行的方法,我们可以用 Python 可视化不同组的分布差异。
柱状图
直方图是分布可视化的经典。直方图将数据频率可视化。直方图中的条形越高,它在观察数据中出现的频率越高。让我们比较一下三种不同鸢尾的分布。
用于分布比较的直方图示例(图片由作者生成)
为了检查物种之间的差异,我们可以使用 ANOVA 并检查产生的 p 值。然而,单独用方差分析解释结果可能是不够的,特别是当我们向对统计学理解有限的人展示结果时。使用直方图可以帮助我们克服知识障碍,因为直方图非常直观。在没有任何统计分析的情况下,很明显,与上图中的杂色鸢尾和海滨鸢尾相比,刚毛鸢尾的花瓣长度更小。
我最喜欢添加到直方图或任何其他分布图的附加指标之一是*均线。*均线可以从数据的*均值或中值构建,尽管我个人更喜欢使用中值,因为它更能抵抗异常值。添加一条*均线可以快速确定总体的期望值。
密度图
类似于直方图,从下图中我们可以清楚地看到,即使不做任何统计分析,刚毛鸢尾的花瓣长度也与杂色鸢尾和海滨鸢尾不同。但是密度图中的*滑曲线是怎么来的呢?
用于分布比较的密度图示例(图片由作者生成)
类似于直方图,密度图测量数据频率,但用*滑的连续线代替矩形条。类似于直方图,x 轴是来自观察数据的数值。密度图的 y 轴非常特殊,因为它不是频率的绝对计数,而是产生密度曲线的给定数据的概率密度函数(PDF) 的估计值。将绝对频率计数变成*滑曲线的一种常见方法是使用核密度估计(KDE) 。
解读 y 轴可能会有点混乱。例如,鸢尾花瓣长度的峰值怎么会在 2.4 左右?这个数字从何而来?在密度图中,积分时曲线下的总面积为 1。但是曲线的一小部分呢?假设我们想从手工计算中得到 2.4。我们可以通过一个简单的公式来实现:
由作者生成的图像
面积是曲线函数的积分。因为我们的曲线看起来像一个翻转的 U,的*似函数是:
曲线的功能在 Desmos 中可视化
我们选择峰值周围的基值。在这种情况下,我们选择 1.4 到 1.5。那么,这个函数的积分是:
https://www.integral-calculator.com/的积分计算器。24%的观察数据落在这条曲线下。
根据我们非常粗略的计算,我们将 0.24 除以 0.1(碱基之间的差异),得到 2.4,这是我们密度图的峰值。如果所有的数学太复杂,只要记住密度函数最终测量频率。峰值越高,频率越高。
箱形图
直方图和密度都很难将许多组(超过 3 组)的差异可视化。看看这个密度图。
美国 5 个种族每周工作时间分布图(图片由作者生成)
上面的图表解释了美国人每周花在工作上的时间。从图中我们可以得出一个结论,不管什么种族,大多数美国人每周工作大约 40 个小时。然而,数据的堆叠使得密度图不便于查看和分析。一种替代方法是将数据或分成子集,以使用箱线图代替。看看下面的例子。
美国 5 个种族的每周工作时间箱线图(图片由作者生成)
如果我们看看箱线图结果,虽然我们的发现仍然正确,即所有种族的*均工作时间为 40 小时/周,但通过离群值可视化,结果解释变得更有趣。
当我们对异常值感兴趣时,箱线图特别有用。盒状图须外部的黑色圆圈表示组中的异常值。一圈相当于一次观察。盒图中的盒子包含 50%的数据。盒子外面是胡须,胡须外面是离群值。我们可以看到,实际上有许多美国人没有遵守每周*均工作 40 小时的规定。有些人每周工作不到 10 小时,有些人每周工作超过 60 小时。
结论
现在你已经理解了我们可视化分布的几种方法,你可以根据你的需要使用上面的任何一种方法。为了快速直观地了解组间比较,请尝试使用直方图或密度图。但是,如果您对可视化异常值感兴趣,或者如果有太多组,箱线图可能是更好的选择。
那都是乡亲们!这个帖子使用的代码和数据可以在这里找到【https://github.com/ZainulArifin1/distribution_viz 。让我知道你是否有其他有趣和有意义的方法来可视化分布。
图片来自 Pixabay
丢弃 p 值。请改用 Bootstrap 置信区间
行业笔记
p 值并不意味着人们认为他们意味着什么;他们依赖于不太可能实现的隐藏假设;他们偏离了真正的问题。下面是如何在 R 和 Python 中使用引导程序
几年前,我受雇于美国最大的保险公司之一,开始并领导他们的行为科学团队。我拥有世界十大经济系之一的行为经济学博士学位和五年的战略顾问经验,因此我相信我的团队能够通过精心设计的实验和数据分析来推动商业决策。
由马库斯·斯皮斯克在 Unsplash 上拍摄的照片。什么叫我的样本“太小”?
事实上,我与对统计学有着敏锐理解的高技能数据科学家一起工作。但是在设计和分析实验多年后,我对我们向决策者传达结果的方式越来越不满意。我觉得对 p 值的过度依赖导致了次优决策。在与其他公司的同事交谈后,我意识到这是一个更广泛的问题,我着手编写更好的数据分析指南。在本文中,我将介绍本书最重要的建议之一,即放弃 p 值,使用 Bootstrap 置信区间。
沟渠 p 值
您应该放弃 p 值的原因有很多,我将在这里分析其中的三个主要原因:
- 他们的意思并不像人们认为的那样
- 他们依赖于不太可能实现的隐性假设
- 他们偏离了真正的问题
1.他们的意思并不像人们认为的那样
当您运行应用数据分析时,无论是在私营部门、非营利部门还是公共部门,您的目标都是为决策提供信息。我们需要解决的很大一部分问题是不确定性:数据告诉我们数字是 10,但它可能是 5 吗?也许 100?我们能依靠数据分析得出的数字吗?经过多年,有时是几十年,在这个问题上教育商业伙伴,他们通常理解不确定性的风险。不幸的是,他们经常从那里跳到假设 p 值代表一个结果是偶然的概率:p 值为 0.03 意味着有 3%的可能性我们认为是正的数字实际上是空的或负的。事实并非如此。实际上,它代表了假设真实值确实为零,我们看到的结果的概率。
在科学术语中,真实值为零或负值被称为零假设(缩写为 H0),真实值严格大于零被称为备择假设(缩写为 H1)。人们错误地认为 P 值是给定数据 P(H0 |数据)的 H0 的概率,而实际上它是给定数据 H0 的概率,P(数据|H0)。你可能会想:马铃薯 po-tah-to,这是令人毛骨悚然的,非常小的 p 值确实是一个好迹象,表明一个结果不是由于偶然。在许多情况下,你会大致正确,但在某些情况下,你会完全错误。
让我们举一个简单但有启发性的例子:我们想确定罗伯特的公民身份。无效假设:罗伯特·H0 是美国公民。另一种假设:H1,他不是。我们的数据:我们知道罗伯特是美国参议员。3.3 亿美国公民中有 100 名参议员,因此在零假设下,我们的数据(即 p 值)的概率为 100 / 300,000,000 ≈ 0.000000303。根据统计显著性规则,我们可以有把握地得出结论,我们的零假设被驳回,罗伯特不是美国公民。这显然是错误的,那么问题出在哪里呢?如果罗伯特是美国公民,他成为美国参议员的概率确实很低,但如果他不是,概率就更低了(即零!).p 值在这里帮不了我们,即使有更严格的 0.01 甚至 0.001 阈值(关于这个问题的另一个说明,参见 xkcd )。
2.他们依赖隐藏的假设
p 值是在所有计算都必须手工完成的时代发明的,因此它们依赖于简化的统计假设。概括地说,他们假设你所观察到的现象服从某种规则的统计分布,通常是正态分布(或者来自同一个家族的分布)。不幸的是,这很少是真的:
- 除非你在测量一些低水*的心理生理变量,否则你感兴趣的人群通常是由异质群体组成的。假设你是不可能汉堡的营销经理,正在调查肉类替代品的需求。你必须考虑两个群体:一方面,素食者,对他们来说,相关的选择是不同的素食产品;另一方面,肉食者可能会被诱惑,但比起肉本身,他们更关心味道和价格。
- 正态分布是对称的,并向两个方向延伸至无穷大。现实生活中,有不对称,有门槛,有极限。人们从来不买负数,也不买无穷大。他们在 18 岁之前根本不投票,120 岁的市场比 90 岁和 60 岁的市场要窄得多。
- 相反,我们看到“厚尾”分布,其中极值比正态分布的预期更频繁。从百万富翁和亿万富翁的数量来看,亿万富翁的数量比你想象的要多。
这意味着来自标准模型的 p 值通常是错误的。即使你正确地把它们当作 P(数据|H0)而不是 P(H0 |数据),它们也经常会相差很大。
3.他们偏离了真正的问题
比方说,你已经记住了前两个问题,并建立了一个完整的贝叶斯模型,最终允许你正确地计算 P(H0 |数据),即在给定观察数据的情况下,真实值为零或负的概率。你应该把它带给你的决策者吗?我认为你不应该这么做,因为它不能反映经济结果。
假设业务决策者正在考虑两种可能的行动,A 和 b。根据观察到的数据,零收益或负收益的概率为:
- 行动 A 为 0.08
- 行动 B 为 0.001
决策者应该根据这些数字选择行动 B 吗?如果我告诉你相应的 90%置信区间是:
- [-50 万美元;9950 万美元]用于行动 A
- [10 万美元;20 万美元]用于行动 B
行动 B 可能导致零或负结果的概率较低,但它对企业的预期价值要低得多,除非企业极其厌恶风险。在大多数情况下,决策者的“经济重要性”取决于两个问题:
- 我们可能赚多少钱?(又名,期望值)
- 在“最糟糕的情况下”,我们会损失多少钱?(又名,置信区间的下限)
置信区间是一个比单一概率值更好的工具来回答这些问题。
请改用引导程序
让我们举一个具体的例子,改编并简化自我的书。一家公司进行了一项时间研究,根据面包师的经验水*,他们准备定制蛋糕需要多长时间。让一个工业工程师在全国各个商店测量做一个蛋糕需要多长时间,既费钱又费时,所以数据集只有 10 个点,如下图所示。
除了样本规模非常小之外,左上角还有一个异常值:一名新员工花了一天的大部分时间在一个复杂的蛋糕上,为一次公司务虚会做准备。应该如何向业务伙伴报告数据?人们可以抛弃极端的观察。但是,这种观察虽然不同寻常,但本质上并不反常。没有测量误差,这种情况可能会时有发生。另一种选择是只报告总*均持续时间,56 分钟,但这也会产生误导,因为它不会传达数据中的可变性和不确定性。
或者,可以根据传统的统计假设计算正常置信区间(CI)。正态置信区间与 p 值密切相关:当且仅当相应的置信区间不包含零时,估计才具有统计显著性。正如您将在任何统计类中学到的,正常 95% CI 的下限等于*均值减去 1.96 倍标准误差,上限等于*均值加上 1.96 倍标准误差。不幸的是,在目前的情况下,置信区间是[-23;135]而且我们可以想象,商业伙伴不会太喜欢负烘烤持续时间的可能性…
这个问题来自于烘焙时间是正态分布的假设,而事实显然不是这样。人们可以尝试拟合更好的分布,但使用 Bootstrap 置信区间要简单得多。
1.引导程序通过替换绘图来工作
为了建立 Bootstrap 置信区间,你只需要通过从原始样本中抽取替换样本来建立“大量相似样本”。在 R 和 Python 中,替换绘图都非常简单,我们只需在每种情况下将“replace”设置为 true:
## R
boot_dat <- slice_sample(dat, n=nrow(dat), replace = TRUE)## Python
boot_df = data_df.sample(len(data_df), replace = True)
为什么我们要用替换来绘图?为了真正掌握 Bootstrap 发生了什么,值得后退一步并记住统计学的要点:我们有一个无法完全检验的群体,所以我们试图根据有限的样本对这个群体做出推断。为此,我们通过统计假设或 Bootstrap 创建了一个“假想”人口。根据统计假设,我们说,“假设这个样本是从一个正态分布的总体中抽取的”,然后在此基础上做出推论。对于 Bootstrap,我们说,“假设总体与样本具有完全相同的概率分布,”或者等价地,“假设样本是从由该样本的大量(或无限)副本组成的总体中抽取的。”那么从该样本中抽取替换就等同于从该假想总体中抽取不替换。统计学家会说,“自助样本对于样本就像样本对于总体一样。”
我们重复这个过程很多次(比如说 2000 次):
## R
mean_lst <- list()
B <- 2000
N <- nrow(dat)
for(i in 1:B){
boot_dat <- slice_sample(dat, n=N, replace = TRUE)
M <- mean(boot_dat$times)
mean_lst[[i]] <- M}
mean_summ <- tibble(means = unlist(mean_lst))## Python
res_boot_sim = []
B = 2000
N = len(data_df)
for i in range(B):
boot_df = data_df.sample(N, replace = True)
M = np.mean(boot_df.times)
res_boot_sim.append(M)
该过程的结果是一个 Bootstrap 样本均值列表,我们可以用直方图绘制该列表:
如您所见,直方图非常不规则:有一个峰值接*我们原始数据集的*均值,还有一些较小的峰值对应于某些模式。考虑到我们的异常值有多极端,七个峰值中的每一个都对应于它在 Bootstrap 样本中的重复次数,从 0 到 6。换句话说,它根本不会出现在均值位于第一个(最左边)峰的样本中,它只出现在均值位于第二个峰的样本中一次,依此类推。
2.从 Bootstrap 样本计算置信区间
由此,我们可以计算自举置信区间(CI)。置信区间的界限由前面*均值的经验分布决定。我们可以简单地将值从最小到最大排序,然后查看 2.5%分位数和 97.5%分位数以找到双尾 95% CI,而不是尝试拟合统计分布(例如,正态分布)。对于 2,000 个样本,2.5%的分位数等于第 50 个最小*均值的值(因为 2,000 * 0.025 = 50),97.5%的分位数等于从较小到较大的第 1950 个*均值的值,或者第 50 个最大*均值的值(因为两个尾部具有相同数量的值)。幸运的是,我们不必手工计算这些:
## R
LL_b <- as.numeric(quantile(mean_summ$means, c(0.025)))
UL_b <- as.numeric(quantile(mean_summ$means, c(0.975)))## Python
LL_b = np.quantile(mean_lst, 0.025)
UL_b = np.quantile(mean_lst, 0.975)
下图显示了与之前相同的直方图,但添加了均值、正常 CI 界限和 Bootstrap CI 界限。
2,000 个样本的*均值分布,包括*均值(蓝色粗线)、正常 95%置信区间(黑色虚线)和引导置信区间(蓝色虚线)
Bootstrap 95%-CI 为7.50;140.80,这就真实多了。没有正常假设的负值!
3.Bootstrap 更灵活,与业务决策更相关
一旦你开始使用 Bootstrap,你会对它的灵活性感到惊讶。小样本量、不规则分布、业务规则、期望值、集群组的 A/B 测试:Bootstrap 可以完成所有这些!
例如,让我们想象一下,在我们前面的例子中,公司正在考虑制定一个时间承诺——“你的蛋糕在三个小时内或打五折”——并想知道蛋糕目前需要三个多小时才能烤好。我们的估计是样本百分比:它发生在 10 个观察到的案例中的 1 个,或 10%。但是我们不能就此打住,因为这个估计有很大的不确定性,我们需要传达出来。10 次观察中的 10%比 100 次或 1000 次观察中的 10%更不确定。那么,我们如何围绕这 10%的价值构建 CI 呢?当然是用自举啦!
Bootstrap 估计值的直方图也为业务合作伙伴提供了一个很好的可视化展示:“这条垂直线是实际测量的结果,但你也可以看到所有可能的值”。根据现有信息,90%或 95%的下限提供了“合理的最坏情况”。
最后,如果你的老板或商业伙伴对 p 值非常执着,Bootstrap 提供了一个类似的衡量标准,达到的显著性水*(ASL)。ASL 就是等于或小于零的自举值的百分比。这种解释非常接*人们错误地分配给 p 值的解释,所以需要的教育非常有限:“美国手语是 3%,所以有 3%的可能性,真实值是零或更少;ASL 小于 0.05,因此我们可以将该结果视为显著的“”。
结论
总而言之,尽管 p 值在应用数据分析中仍然无处不在,但它们已经不受欢迎了。他们并不意味着人们通常认为他们的意思;即使他们做到了,这也不是决策者想要的答案。即使在学术界,目前也有一股强大的推动力来减少对 p 值的盲目依赖(参见美国大豆协会的声明 below⁴).
使用 Bootstrap 置信区间既简单又有说服力。他们不依赖隐藏的统计假设,只依赖一个简单明了的假设:总体人口看起来和我们的样本一样。它们提供了关于可能结果的信息,这些信息更加丰富,并且与业务决策更加相关。
但是等等,还有呢!
最后不要脸的塞来了。如果你想了解更多关于 Bootstrap 的知识,我的书会告诉你:
- 如何确定抽取的 Bootstrap 样本数;
- 如何将 Bootstrap 应用于回归、A/B 测试和特别统计;
- 如何编写高性能的代码,不会让你的同事对你的 FOR 循环窃笑;
- 以及许多其他关于在商业中分析客户数据的很酷的事情。
参考
[1] F. Buisson, 行为数据分析用 R 和 Python (2021)。显然是我的书!本书的 Github repo 中提供了示例代码。
[2] R. Wilcox, 现代统计方法基础:大幅度提高功效和准确性 (2010)。我们通常假设我们的数据是正态分布的。威尔科克斯表明,这是没有根据的,会严重影响分析。一本关于高级主题的可读性很强的书。
[3]参见我之前的帖子“你的行为数据真的是行为的吗?”。
[4] R. L .沃瑟斯坦和 N. A .耶戈(2016) 《美国统计学会关于 p 值的声明:背景、过程和目的》,《美国统计学家,70:2,129–133,DOI:10.1080/00030303506
抛弃“统计显著性”——但保留统计证据
怎么会?一位统计学家分享一份书写样本。
安娜·涅克拉舍维奇在的照片 https://www . pexels . com/photo/pen-business-eyewear-research-6801648/
(滚动阅读写作范例,了解为什么应该这样做。【2021 年 6 月 25 日,我第一次写下了下面这句格言。)
证据的重要性不等于证据的重要性。
关键想法
- “显著”p 值≦“显著”发现:真 X 的统计证据的显著性(即,真 X 估计值的 p 值的统计显著性)绝对没有说明真 X 的实际/科学显著性。也就是说,证据的显著性不是显著性的证据。增加你的样本量不会增加你的实用/科学假设的实用/科学意义。
- “显著的”p-value = "可辨别的"发现:真 X 的统计证据的显著性是否告诉我们估计值能够多好地辨别真 X。也就是说,证据的显著性 是可辨别的 证据。增加你的样本量会增加你的发现能多好地辨别你的实际/科学假设吗。**
统计证据不是问题。问题在于,将“统计显著性”转化为(科学的)“显著性”的简单炼金术通过省略一个词创造了什么:从一个微小但显著的变化中纺出的科学确定性的幻觉。
书写样本
请随意使用我下面写的文字作为你自己思想的模板。在你自己的手稿、出版物和报告的方法和分析部分包含你自己的版本。但是一定要先修改下面的文字,以确保你理解你所倡导的东西以及它的重要性——并避免抄袭!
将变量报告为“有辨识力”而非“显著性”,或者描述变量的“有辨识力”而非“显著性”。
我们将在两个重要方面偏离通常的统计方法。首先,我们将不使用短语“统计上显著的”,因为它与更广泛使用的一般术语“显著的”容易混淆;后者在很大程度上被理解为“科学上或临床上重要的”。统计意义与科学意义无关,但却被普遍误认为是科学意义。这种证据充分且长期存在的现象被称为“显著性谬误”( Kühberger et al,2015;Silva-aya guer 等人,2010 年; Barnett 和 Mathisen,1997 ),生物医学和心理学复制危机的关键贡献者。
因此,领先的统计权威强烈建议完全放弃“统计显著性”这一短语(安海因等人,2019;麦克沙恩等人,2019;瓦瑟斯坦等人,2019;瓦瑟斯坦和耶戈,2016 ),迫使人们寻找另一个短语来描述研究结果中的统计证据数量。我们将使用更加直观准确的短语来描述科学发现(即未知量的点估计)。这些包括“统计上可辨别的”、“统计上明显的”、“统计上明显的”、“统计上支持的”、“统计上可推断的”、“统计上可靠的”和“统计上稳定的”(有时简称为“统计的”)。 Daza 等人(2020) 和 Matias 等人(2022) 是成功运用这种词汇策略的出版物的例子。
其次,我们将像通常所做的那样报告 p 值,但将避免区分“统计显著”和“统计不显著”的 p 值。这是因为这样做会稀释 p 值中包含的信息,而这些信息对于更具批判性的思考和有意识的决策是有用的。(回想一下,p 值有助于量化估计的不确定性水*和统计证据。)将 p 值二分法为“具有统计显著性”或“不具有统计显著性”的常见科学实践通过有意忽略其大量统计信息传达了一种虚幻的确定感(安海因等人,2019;麦克沙恩等人,2019;瓦瑟斯坦等人,2019;沃瑟斯坦和耶戈,2016 年。
我们将简单地报告 p 值,并用大小(例如,小、低、大、高)来描述它们(当我们这样做时)。然而,我们将根据强度来描述 p 值的相应点估计的统计证据水*。例如,我们可以使用像“强烈可辨别”、“中等可辨别”和“不可辨别”这样的顺序类别。(将 p 值本身描述为“可强烈辨别”在技术上没有意义。但是具有小 p 值的点估计是“强烈可辨别的”。)
我们还会将一个变量报告为“可识别”而不是“显著”,或者描述一个变量的“可识别性”而不是“显著性”。也就是说,我们将描述它是否/如何有助于从统计学上辨别结果的差异——这可能有也可能没有实际/科学/临床意义。例如,假设一个分析发现一个连续的结果 Y 要用 X 和 Z 来解释或预测,因为 X 和 Z 具有统计上显著的 p 值。不要说“X 和 Z 是 Y 的显著预测因子”,我们会说“X 和 Z 是 Y 的识别预测因子”,不要说“X 和 Y 是识别变量”,而要说“X 和 Y 是显著变量”。
尽管如此,我们仍将继续报告阈值和统计显著性水*,因为这些是构建置信区间所必需的。后者可能是有用的,只要它们被类似地报告和解释为软边界而不是硬边界,传达它们对应的点估计的统计可辨性。
2022 年 1 月 2 日添加的其他示例文本:
我们不会使用 p 值(以及科学数量的其他统计证据衡量标准)来确定一个数量是否具有实际意义(例如,在临床上、科学上)。我们将仅使用它们来帮助评估或决定一个量在多大程度上可以用于统计辨别其预期的真实值(即,它是否具有统计显著性),其实际显著性将由具有领域专业知识的研究调查者来确定(例如,具有临床意义的差异)。
我欢迎你在评论中提出建设性的反馈。
真实的例子
以下出版物成功使用了上述文本类型。
- 睡眠剥夺对非糖尿病患者血糖、食物渴望和情感的影响:一项 N-of-1 随机试点研究。医疗保健 2020 年 3 月(第 8 卷,第 1 页,第 6 页)。多学科数字出版研究所。(mdpi.com/2227–9032/8/1/6/htm
- 什么可能影响夜间心率?从 N-of-1 观测数据得出的结论。数字健康。2022 年 8 月;8:20552076221120725.(【journals.sagepub.com/doi/10.1177/20552076221120725】T4)
什么?为什么?!
与其说是技术问题。这是一个语言和行为问题——科学交流的失败。也就是对科学不利。
我们可以一起改进科学!但改变根深蒂固的坏习惯,尤其是在制度和社会层面,需要时间。行为改变很难——所以让我们现在就开始。我们走吧。
关键概念
- 我们必须继续评估我们研究证据的统计质量(使用 p 值、置信区间、可信区间等)。)来奖励我们的科学发现。这对科学有好处。
- 我们必须停止混淆我们研究证据的统计质量对于我们的科学发现【统计显著性】)与我们研究发现的科学质量【显著性】。每当我们把“统计显著”缩短为“显著”时,我们都这样做。这对科学不利。
概观
统计证据不是问题。问题在于,将“统计显著性”转化为(科学的)“显著性”的简单炼金术通过省略一个词创造了什么:从一个微小但显著的变化中纺出的科学确定性的幻觉。
- 短语“统计显著性”描述了 p 值,一种用于量化统计证据的度量。
- 短语“统计显著性”并不描述我们收集所述统计证据的科学发现本身(即,科学假设但未知*均值的点估计)。充其量,将“统计显著性”缩写为“显著性”是对“发现证据的统计显著性”这一短语的草率简化。例如,“0.3 的(统计)显著相关性”是“0.3 相关性的统计显著证据”的简称。
但是,如果“统计显著性”仍然是描述 p 值的合理方式,我们不能继续使用它来描述 p 值吗?没有。
重要并不意味着重要。
一次又一次,短语“统计显著性”被缩短为“显著性”。后者是科学发现的一种品质——,充其量与“统计显著性”没有明确的关系。也就是说,一个“统计上显著的”p 值被错误地用作完全不同的东西——发现本身——具有(科学上)“显著性”的证据。
每当我们将“统计显著性”(一种统计质量)缩短为“显著性”(一种科学质量)时,这就是我们犯的认知错误。因此,我们需要为找到一个更好的短语来描述我们的发现的证据的统计质量,而不会与发现本身的科学质量相混淆。
那么我们应该如何描述 p 值呢?我目前的观点和方法是,只要有可能,我们应该仅用大小(例如,小、低、大、高)来描述 p 值。为什么?
我担心用大小以外的术语来描述 p 值同样会将其意义与科学重要性混为一谈。例如,“统计学上显著的”p 值将被错误地用作科学发现“显著”的证据。用“值得注意的”、“有意义的”、“重要的”以及其他类似的词语来表达一种并非纯粹统计的重要直觉似乎并不能解决我们认知混乱的问题。也就是说,显著性并不意味着重要性。**
也就是说,对于如何描述 p 值,我很乐意提供建议。
1.继续评估统计证据
我是受过训练的统计学家。我 绝不 要求你抛弃统计证据!
统计证据是关于包含在人口样本中的人口的科学假设的证据。它是根据统计特性(即,标准误差、置信区间、p 值、可信区间等)报告的。)的特定研究结果(即,估计*均值、估计比例等。).**
评估统计证据对于以一种有原则的、结构化的和严谨的方式用嘈杂的数据“做好科学”是至关重要的。
- 进行适当的科学推断(从我们的样本到我们感兴趣的人群)包括报告和解释我们试图重现的(验证性研究)或新假设的(探索性研究)。
- 它还包括报告和解释统计证据的性质和质量,以便我们重现类似的发现(经检验的假设),或者在随后的研究中重现我们的新想法(产生的假设)。
但我的一位统计学家同事很好地描述了定义“统计证据”的挑战:
没有像温度这样的证据
Twitter @healthstatsdude 写道:没有像温度这样的证据
传统上,我们使用 p 值(以及其他常用指标)来量化统计证据。离完美还差得很远。
即便如此,正确描述它也很重要:在最好的情况下,p 值可以量化我们对科学假设的猜测证据的统计质量(样本发现证据的“统计显著性”)。但它可以 从不 量化我们关于一个科学假设的猜测的科学质量(从样本中发现的“重要性”)。**
p 值本身就是一种确定性的错觉:这个单一的数字充其量只是一个一次性的*似值,一个随研究而变化的变化无常的数字——使得“统计显著性水*”(例如,0.10,0.05,0.01)成为一个同样变化无常的决策工具( Gelman and Stern,2006 )。对于任何给定的研究(不包括系统综述或荟萃分析),p 值只应被用作提示或指导,而不是它已经成为的替代“黄金标准”可操作性指标。
2.停止使用“统计显著性”这个短语
我们统计学家可以成为科学的积极捍卫者。我们尤其反对将“统计显著性”与(科学)“显著性”混为一谈的智力不诚实。在统计和科学交流的几十年中,这种广泛的做法得到了专业的认可和制度化。它被称为“显著性谬误”( Kühberger et al,2015;Silva-aya guer 等人,2010 年; Barnett 和 Mathisen,1997 ,生物医学和心理学中复制危机的关键促成者。
因此,主要的统计权威大声而有力地反对继续使用“统计意义”这个词。他们一丝不苟地解释了为什么我们所有人都应该停止使用这个短语,因为它从未打算(也永远不可能)描述一项发现的科学质量(安海因等人,2019;麦克沙恩等人,2019;瓦瑟斯坦等人,2019;瓦瑟斯坦和耶戈,2016 。这是一种描述 p 值的合理方式,但作为描述一项发现的科学价值的方式是没有意义的(数学上,至少)。
我试图用我自己的方式来引起人们对这个重要科学话题的关注。我写了许多推文和 LinkedIn 咆哮、恳求、隐喻和迷你课程。我还写了两篇关于数据科学的文章,可能会帮助你更好地理解科学交流长期危机(实际上是慢性病)的严重性:
如何摆脱统计证据报告中的“统计意义”
我喜欢用更直观准确的短语来描述科学发现。其中包括:
- “统计上可辨别的”
- “统计上明显的”
- “统计上明显”
- "有统计数据支持"
- “可统计推断”
- “统计上可靠”
- “统计稳定”
必要时,如果有意义的话,我会简单地用“统计学”。我的一份出版物也使用了类似的策略:达扎等人(2020)
感谢你让科学变得更好!🙂
参考
- 安海因、瓦伦丁、桑德·格林兰和布雷克·麦克肖恩。"科学家反对统计学意义。"大自然。第 567 卷,第 305-307 页。2019 doi:https://doi.org/10.1038/d41586-019-00857-9(nature.com/articles/d41586-019-00857-9)
- p 值的暴政:统计学意义和常识之间的冲突。牙科研究杂志。1997;76(1):534–536.DOI:10.1177/00220345970760010201。(journals.sagepub.com/doi/abs/10.1177/00220345970760010201)
- 睡眠剥夺对非糖尿病患者血糖、食物渴望和情感的影响:一项 N-of-1 随机试点研究。医疗保健 2020 年 3 月(第 8 卷,第 1 页,第 6 页)。多学科数字出版研究所。(mdpi.com/2227–9032/8/1/6/htm)
- “显著”和“不显著”之间的差异本身并不具有统计学意义。美国统计学家。2006 年 11 月 1 日;60(4):328–31.tandfonline.com/doi/abs/10.1198/000313006x152649
- 推论统计学中的显著性谬误。BMC 研究笔记。2015 年 12 月;8(1):1–9.doi:10.1186/S13 104–015–1020–4。PMID:25888971;PMCID: PMC4377068。(pubmed.ncbi.nlm.nih.gov/25888971/
- 什么可能影响夜间心率?从 N-of-1 观测数据得出的结论。数字健康。2022 年 8 月;8:20552076221120725.(【https://journals.sagepub.com/doi/10.1177/20552076221120725】T2
- 麦克沙恩 BB,加尔 D,赫尔曼 A,罗伯特 C,塔克特 JL。放弃统计学意义。美国统计学家。2019 年 3 月 29 日;73(sup 1):235–45。DOI:10.1080/00031305.2527253。(amstat . tandfonline . com/doi/full/10.1080/00031305 . 2018 . 1527253)
- Silva-aya guer LC,Suárez-Gil P,Fernández-Somoano A .卫生科学研究中的零假设显著性检验(1995–2006):统计分析和解释。BMC 医学研究方法论。2010 年 12 月;10(1):1–9.DOI:10.1186/1471–2288–10–44。(bmcmedresmethodol . biomed central . com/articles/10.1186/1471–2288–10–44)
- 沃瑟斯坦 RL,耶戈纳。美国儿科学会关于 p 值的声明:背景、过程和目的。美国统计学家。2016 年 03 月 07 日;70:2,129–133,DOI:10.1080/00031305.2154108。(tandfonline.com/doi/full/10.1080/00031305.2016.1154108)
- 沃瑟斯坦 RL,席尔姆铝,耶戈纳。走向一个超越 tandfonline.com/doi/full/10.1080/00031305.2019.1583913 的世界
关于作者
Eric J. Daza 是数字健康领域的数据科学统计学家。他获得了生物统计学博士学位,在临床试验、公共卫生和行为改变研究方面拥有 18 年以上的工作经验。Daza 博士为用于个性化健康建议的个人内(即,n-of-1,单病例,独特的)数字健康研究开发因果推断方法。| ericjdaza.com🇺🇸🇵🇭@埃里克森 linkedin.com/in/ericjdaza|statsof1.org@ stats of@ fsbiostats
招聘数据团队中的不同技能和角色
相同但不同(来源:图片来源于 Nikhil Goyal 授权的 Shutterstock)
Psst:你的“数据人”不是东西
建立一个数据团队可能是一件棘手的事情。有几十个松散使用的标题来表示需要在组织中执行的一组数据相关的任务(已识别的和通常未识别的)。数据科学家和业务分析师可能是市场上最知名的数据角色,数据工程师在过去的 12-24 个月里变得炙手可热。许多雇佣了大量数据科学家来寻找数据天堂的公司最终都失望了,并且不明白为什么分析没有带来承诺的影响。Gartner(我以前的公司)的研究显示,超过一半的营销领导者对分析投资的影响或缺乏感到失望( Gartner 对高级营销领导者的调查)。
如果要我指出数据科学泡沫破裂的原因,那就是四象限数据科学家信息图。
我不认可这张信息图。(来源: 我营销酒厂法师,http://www . marketingdistillery . com/2014/08/30/data-science-skill-set-explained/)
将数据科学家描述为拥有这 4 个方面的技能本身并没有错,但这至少给了一些非技术业务领导者(尤其是在营销领域)一种错误的感觉,即通过雇佣独角兽数据科学家来建立数据团队所需的流程、基础设施和技能的完整性和可替代性。这也让人们对招聘抱有不切实际的期望,最终让买家后悔。
在某种程度上,聪明人纠正了这一点,并开始雇用数据工程师,如果你还没有听说,现在雇用一名经验丰富的数据工程师比试图在魁地奇比赛中找到金色飞贼要糟糕得多(数据工程工作在 2020 年同比增长 50%)。当我在 LeafLink 面试时被问到,2020 年我会雇佣多少数据科学家时,我的回答是“大概 0”。相反,我试图解释的是下面的图表:
构建数据仓库和标准化关键指标报告等基础工作减少了临时报告任务,从而让数据团队有时间解决更高层次的业务问题。(图片来源:Nikhil Goyal)
最*,作为项目的一部分,Fishtown Analytics 对这个主题进行了启发性的思考。通过认识到分析师需要借鉴软件开发剧本,并仔细考虑数据分析师遇到的用例及问题(与后端工程师非常不同),dbt 定义了市场中一个全新的数据角色——分析工程师的角色(什么是分析工程师?)。它非常出色,因为它系统化了大多数组织在构建数据堆栈的早期发现他们需要的角色,但不知道该如何称呼。在我看来,分析工程师是最终的数据通才,是 BI 分析师角色的演变。有人可以理解业务逻辑,在维度模型中表示它,编写 SQL 来计算指标,并利用 dbt 的强大功能来抽象,表达,模板和版本管理所有这些。
即使有了所有这些角色定义,数据工作通常也不会具体化为行动,因为执行分析的人和采取行动的人之间存在沟通差距。理论上,数据科学家应该拥有出色的沟通技能,但市场供求动态导致低于最佳预期的均衡(阅读:难以捉摸的独角兽数据科学家)。麦肯锡建议,为了弥合这一差距,数据团队需要一名分析翻译(分析翻译——新的必备角色)。如果我听说过一个小众角色,那就是这个——可以去给别人的作品代言的人。坦率地说,这是数据分析师最讨厌的角色。冒着点燃许多人激情的风险,我要补充一点,我宁愿培训分析师撰写建议和提供故事,也不愿雇佣分析翻译来翻译数据科学家或 BI 分析师的工作。
回到我们开始的地方,在 BI 分析师、数据科学家、数据工程师、分析工程师、分析翻译和无数其他人(数据库管理员、ML 工程师、BI 开发人员、数据可视化人员)之间,应该从哪里开始招聘呢?所有这些角色和细微差别给数据专业的招聘经理带来了更多问题,特别是在向非技术利益相关方(如财务、人力资源和其他赞助或支持数据角色招聘的业务合作伙伴)解释时。像这样的问题:
- BI 分析师、数据科学家、数据工程师和分析工程师之间有什么区别?我在他们所有的简历上看到了如此多的技能重叠——他们都知道 SQL、Python、R 和 Tableau!
- 我们多久才能意识到所有这些角色的影响?
事实是,随着数据生态系统随着业务的发展而发展,您将需要它们。没有认识到数据运营领域和技能集的广度会导致利用数据力量的工作不协调和不完整(请原谅这种陈词滥调)以及糟糕的招聘决策。更令人困惑的是,数据专业人员通常有着丰富的经验,涉及数据操作的各个方面,让每个人看起来都像是万事通。因此,数据团队中的每个人都是一个…。“数据人”。在这里,与工程界的简单类比是“全栈工程师”。在理想的情况下,每个雇员都应该是所有方面的专家。这种类比的一个有趣而荒谬的简化是只雇佣全栈员工——一个可以管理产品、销售、运行客户体验、执行收款、记账等的角色。还有…雇佣和解雇他们自己。想象一下一个满是堆垛机的公司。我在故意搞笑。但是为了便于讨论,让我们假设某人是多种技能的专家,他们有时间扮演所有这些角色吗?
考虑您需要哪种数据角色的一个有用的方法是回到经常重复的问题——“您需要雇佣什么样的人”。问一个问题——哪些技能对该角色最重要,该角色最需要在数据价值链的哪个部分工作。
为了我自己和我的招聘伙伴的利益,我制作了一个简单的技能矩阵来理解和解释不同的角色:
列 A:广泛的技能领域,列 B:用于执行工作的工具或技术,列 C-G:映射到重点领域和专业水*的关键数据角色。(图片来源:Nikhil Goyal)
需要澄清的是,我并不是说数据科学家不能拥有数据工程师的技能,或者分析工程师不能成为专业的 BI 开发人员。数据领域的技能非常清晰,因此没有什么可以阻止数据工程师成为数据分析和可视化方面的专家,或者数据科学家拥有专家级的数据工作流编排技能集。在所有广泛的数据操作领域获得最低的熟练程度几乎是这个行业的标准。一些聪明的人学会成为多个领域的专家。是的,独角兽是存在的。我知道几个。但是寻找独角兽通常会导致不适当的延迟,有时会导致巨大的失望。
对于有抱负的数据专业人士来说,把自己的关注领域和专业领域想象成一片雨林。技能集的树冠是什么样子的?而深根是什么样子的呢?
这是一种看法。我很乐意听取和学习该领域专业人士的观点和经验,并发展我的思维。
附注: LeafLink 正在招聘数据工程师、分析工程师、数据科学家和 BI 开发人员。查看我们的职业页面,并在我们的网站上申请,或者直接给我留言!
多样性带来更好的解决方案。原因如下。
多样性优势背后的数学原理
使用数据推动创新和业务成果的优势经常被讨论,大量证据支持其功效。然而,数据本身没有价值。正是人剖析数据(数字的或模拟的)以提取解决棘手问题所需的信息。因此,讨论我们系统中的人组件如何影响我们解决方案的质量是有意义的。
在导致非凡表现的人为因素中,团队多元化一直是我们所掌握的最不为人所知、最未被充分利用的杠杆之一。
我写这篇文章并不是为了让你相信最大化多元化会提高企业绩效。有一个 压倒性 数量证据 证明 多样 团队 产生 更 有利 结果(对,那些都是商业和科学领域的链接)。
我想探究现象背后的机制。多元化如何带来令人满意的结果?我们可以使用从进化生物学和统计学中推导出的正式模型来得出导致这一有趣结果的基本力量。
如果你选择的媒体是视频,请完全跳过这篇文章的其余部分,只看我用动画挥手展示这些概念。
如果你更喜欢阅读材料,博客的其余部分以文本形式涵盖了完全相同的材料。
问题解决形式化
如果我们想讨论多样性如何影响我们解决方案的质量,我们必须首先正式定义解决问题意味着什么。
我们有些问题 → P
我们提出一些解决方案 → S
我们的解决方案对我们有一些价值 → V(S)
我们希望在有限的时间内实现价值最大化
如果我们将问题的所有潜在解决方案的价值可视化,它可能看起来像这样:
解决方案 1–7 的假设值|作者图片
观点
你可以从不同的角度看待一个特定的问题。观点并不新鲜。例如,要表示*面上的特定点,您可以使用笛卡尔透视,将该点表示为 X 和 Y 坐标,或者使用极坐标透视,其中该点由角度和长度定义。
空间中某点的替代视角|作者图片
你视角的选择将极大地影响你表达问题解决方案的能力。如果您想要表示一条直线,使用笛卡尔透视图更有意义,而要表示圆弧,极坐标透视图可能是更合适的编码。
视角的选择影响解决方案的简单性|作者图片
这是另一个例子。假设你正在找房子。你选择使用的视角会对你找到的公寓的满意度产生很大的影响。
- 奢华→ *方英尺
- 便利→ 靠*地铁线
- 教育→ 附*学校的质量
当你从一个特定的角度看待一个问题时,你最终会得到一个由这个角度定义的潜在解决方案的连续景观。
连续解决方案风景|作者图片
例如,假设你是一个巧克力棒制造商,想要最大化新产品将销售的巧克力棒数量的价值函数 V(S) 。
你可以从新巧克力棒的卡路里或咀嚼度的角度来看问题。视角的选择会影响你的风景。
视角的选择影响风景的形状|作者图片
对于卡路里,左边是对卡路里敏感的零食爱好者的峰值,另一边是糖爱好者的峰值,但是一旦每根棒的卡路里含量高得离谱,就没人会买了。
咀嚼度会产生更多的峰值,因为销量和巧克力的咀嚼度之间没有明确或有用的关系。这些峰值被称为局部最优值,更好的视角往往有更少的峰值,使解决方案更容易导航。
热量景观的峰值比咀嚼景观的峰值少|图片由作者提供
更少的峰值是可取的,因为当你提出一个解决方案,所有相邻的可能解决方案都有较低的值,找到一个更好的解决方案是棘手的,除非你跳出框框思考。峰值越少,你被卡住的机会就越少。
完美的视角
有一个完美的视角,产生一个只有 1 个最优的景观。这种景观被称为富士山景观。
从铲大小的角度看富士山的积雪量|图片由作者提供
富士山风景的一个例子是铲雪时的大小。当铲的尺寸很小时,可以移动的雪很少,当铲的尺寸太大时,雪变得太重而无法移动。
富士山风景不仅仅适用于简单的问题。考虑下面的双人策略游戏。我们有 1 到 9 张牌,每个玩家轮流选择一张牌,试图以 3 张牌结束游戏,总共 15 张牌。
Sum-to-15 策略游戏|作者图片
你可以想象要阻止你的对手拿到正确的牌需要多少策略。
排列成魔方的卡片|作者图片
用正确的视角解决并不一定很难。富士山的视角将卡片排列成一个魔方。魔方是所有行、列和对角线的总和为 15 的数字网格。
突然间,我们的高难度策略游戏变成了我们熟悉的井字游戏,只需要一个简单的最优决策树。
有趣的是,我们知道富士山的风景可以解决所有的问题。
学者存在定理
对于任何问题,都存在许多创造富士山景观的视角。
怎么会?
一个人只需要以这样的方式排列潜在的解决方案,就能产生这样的景观?如果顺序存在,视角也存在。
问题是,这种完美的视角在大多数情况下是极其难以找到的。
考虑一下这个。某个问题只有 15 种可能的解决方案,但实际上有 15 种!订购解决方案的方式。那是 1.3 万亿个视角。
我们需要一些方法来更有效地导航解决方案空间。
这就是我们来到启发式的地方
探索法
试探法是一种不完美但实用的解决问题的方法,众所周知,这种方法在大多数情况下都能达到*似最优的解决方案。
有许多你可能熟悉的启发法。
爬山启发式|作者图片
最常见的启发式是“爬山”或梯度上升。使用这种启发式方法,您可以找到与当前解决方案最接*的可能解决方案,从而产生显著的改进。大多数增量改进都属于这一类。
如您所见,这可以很好地工作,但是有陷入局部最优的风险。这对于依赖梯度下降来寻找最小化预测误差的函数的参数的机器学习实践者来说是众所周知的。
另一个启发可能是“反其道而行之”你可能通过扮演“魔鬼代言人”来实践这种形式。
这方面的一个例子可以在市场营销领域看到。大多数营销人员将他们最有价值的资产放在电子邮件捕获表单后面来收集线索。
一些营销人员做的正好相反。他们不是在遇到障碍后给出价值,而是提前传递价值,用所提供信息的质量吸引观众,引导他们愿意给出电子邮件以了解更多信息。这种解决方案可以有更好的参与度,甚至可以收集更多的电子邮件。
“反其道而行之”启发式电子邮件捕获|作者图片
管理领域的另一个启发性例子是“先有大石头再有石头”。如果你今年有一大堆目标,而你所有的努力都花在了小目标上,那么清单上的大目标就不会得到足够的关注。
你可能会问自己这个百万美元的问题:
“对于所有问题,有没有比其他方法更好的启发式方法?”
不幸的是,答案是不这已经被没有免费的午餐定理所证明,该定理指出,除非你对正在解决的问题有所了解,否则在搜索所有解决方案时,没有任何搜索算法会比任何其他算法表现得更好。
团队多样性的影响
现在我们有了词汇,我们可以把它们汇集在一起。到目前为止,我一直含糊地说“多样化”会带来更好的结果。什么样的多样性?
为了想出更好的解决方案,我们需要不同的启发法以及不同的观点。当然,这些与性别和种族多样性高度相关。
对角线 vs 行列试探法|作者图片
因为在所有的解决方案中,没有一个试探法比其他的更好,我们需要不同的试探法来在我们的环境中找到更多的最优解。例如,如果我们的解空间是一个网格,我向上、向下、向左、向右看寻找最佳解,如果你也斜着看,我们可能会更快地收敛到最优解。
不同的视角对整个团队的解决方案前景有着非常有趣的影响。假设我们有两个团队成员 Hans 和 Hanna,每个人都有独特的视角。
汉斯和汉娜视角下的风景|作者图片
请注意,Hans 环境中的解决方案 A 和 B 代表 Hanna 环境中完全相同的解决方案 A 和 B。换句话说,不同的视角在两种情况下都会产生相同的局部最优解。
谁的视角更好?汉娜的峰值更少(意味着卡住的地方更少),所以她的视角可能更有用。我们也可以从每个人的局部最优的*均值的“角度”来看这个问题。每个人的最优值的*均值称为个人能力。
查看能力,我们可以看到 Hanna 的局部最优解也比 Hans 的有更高的*均值。
汉斯和汉娜的个人能力|作者图片
在团队成员中拥有不同的局部最优解意味着,如果一个团队成员陷入最优解,他们可以检查其他团队成员是否有更好的解决方案,并简单地从那里继续搜索。
总的来说,我们可以作出如下主张:
认领
一个团队只能卡在一个由 每个成员 共享的局部最优上。
团队能力往往高于任何个人能力,因为团队的局部最优值越少,意味着侵蚀*均值的局部峰值越少。
团队能力往往高于个人能力|作者图片
随着团队成员的增加,这种效果会成倍增加。然而,只有在个人观点多样化的情况下,对团队能力的有益影响才会实现。如果所有的团队成员都有相同的观点,你不会减少局部最优的重叠,放弃任何可能提供的收益。
细则
上述模型忽略了一些影响结果多样性有效性的因素。
不完善的沟通
我们不是完美的沟通者。我不可能把我的想法灌输给你,让你明白我的解决方案的价值。一种减少错误的方法是跳过口头交流,制作一个代表解决方案的人工制品,这样你就可以直接观察到它。
缺乏包容性
如果我邀请你。但是没有以一种有意义的方式把你包括进来,你独特的观点、启发和解决方案将会被浪费。
甲骨文的价值
到目前为止,我一直认为任何特定解决方案的价值对整个团队来说都是显而易见的。事实上,我们没有一些“价值先知”来告诉我们潜在解决方案的确切价值。
在这种情况下,我们必须依靠专家预测的汇总来准确估计每个解决方案的价值。这就把我们带到了下一个话题,“群体的智慧”
群体的智慧
虽然个人做出的预测往往不准确,但当你开始组合多个个人的预测时,你往往会得到一个非常接*标准的预测。
例如,假设我们有 3 个人对实际值为 18 的结果进行预测。
个人预测对比*均预测|作者图片
就个人而言,他们的预测做得不好,但他们的*均预测只差 1。
我们可以将每个单独预测的误差定义为预测值和实际值之间的差值。在计算*均个体误差时,为了防止实际值的正差异抵消负差异,我们可以对个体误差求*方以去除负值。
误差=(预测-实际) |图片作者
类似地,我们可以通过测量每个预测与*均预测的差异来计算预测的多样性。我们再次*方这些项来处理负值。在统计学中,这个差异项也称为样本的方差。
多样性=(预测-*均预测)|作者图片
已经定义了误差和差异,我们现在可以解开差异预测定理。
多样性预测定理 群体误差=*均个体误差—多样性
虽然这看起来像伪数学,但我给你的定义是扩展这一项,证明等价有效所必需的。
形式上,这被定义为以下公式
多样性预测定理|作者图片
c =预测的人群
=实际值
s i =预测的第 i 个个体
n =个体数量
这个简单的公式继续告诉我们同一个故事:没有多样性,人群中就没有智慧。****
为什么?
个体误差小的时候,人群的误差也低,多样性起不到作用,因为我们的问题容易。
当我们的问题很难解决时,个人的预测将会不准确,导致我们群体的误差上升。减少群体误差的唯一方法是增加多样性。
信号独立性
有时,即使你引入了多样性,你也可能因为没有从独特的视角和启发中提取价值的过程而错过所有的好处。
例如,在头脑风暴会议期间,如果房间里的每个人都在参与同一个讨论,我们往往会陷入集体思考的陷阱,失去来自我们多元化团队的个人天赋。让每个人并行构思,然后共同努力,以实现最高价值的解决方案,这样会好得多。
这一观点得到了研究的充分支持,研究表明,即使团队成员的预测之间存在轻微的相关性,你也只能获得从独立角度获得的信号的一小部分。
独立专家的等效数量给出不同专家之间的相关性|图来自 原文
令人震惊的是,即使相关性为 0.2,9 个专家添加的信号并不比 3 个有独立观点的专家添加的信号多。如果相关性更高,达到 0.4,你将很难得到比两个独立专家更高的信息。
这种相关性是如何产生的?它可能来自于专家们一起讨论问题,甚至是和他们共同认识的人一起讨论。它也可能产生于社会文化因素,如共享相似的文化或教育背景。
进一步阅读
这只是对多样性主题及其如何影响我们组织的浅显探讨。我们已经讨论了一些为什么,但是让多元化成为我们工作文化的一部分仍然是一个值得研究的挑战。
本文的大部分材料都是基于 Scott E. Page 所做的工作。具体来说,将进化生物学(适应度景观)和计算机科学(启发式)的思想应用到创新领域源于他的书《模型思考者》。
Scott 还在他的书《差异》和《T2》中详细阐述了这些观点。
如果您想探索围绕多元化的有形业务优势所做的工作,您可以使用以下资源:
- HBR — 当团队的认知更加多样化时,他们解决问题的速度会更快
- 麦肯锡— 多元化取胜:包容性有多重要(或详细报告)
- TED — 多元化如何让团队更具创新性
- 世界经济论坛— 工作场所多元化的商业案例现在势不可挡
- 《自然》——这些实验室非常多样化——这就是它们在科学领域获奖的原因。
- HBR — 其他多元化红利
如果你对实施让多元化成为头等大事所需的系统有什么见解,请在评论中或在你选择的社交网络上参与讨论。
想进一步讨论这个吗?在 Linkedin 上留言或联系我。
📕这篇文章最初发表在向我展示数据博客上,在那里我讨论了更多关于数据驱动业务的话题。
将 MLOPs 分成五个操作组
MLOps = devo PS+data ops+MLLabOps+MLProdOps+DSOps
MLOps 很复杂。在 Unsplash 上由 Neven Krcmarek 拍摄的照片
我被教导通过将问题分成更小的子问题,将复杂的问题转化为更简单的问题。
我通过将机器学习操作( MLOps )分成五个不同但重叠的过程组或操作( Ops )组,使其过程范式更简单。
MLOps 对我意味着什么?
MLOps 正在实现机器学习产品生命周期的自动化。
MLOps 为什么存在?
1990 年,我们有 80%以上的 IT 项目从未推出。失败率下降是因为标准化的开发工具、可重复的过程迭代、瀑布方法的死亡、敏捷方法的兴起和单元测试——这里列举了一些代码开发的进步。
在 2000 年,我们有 80%以上的 Web 项目从未推出。失败率随着 DevOps: 开发过程自动化而下降。使用没有内存管理的面向对象语言(指针、malloc 等。)可能增加了首次展示的成功率。
2020 年,Gartner、VentureBeat 和其他民调告诉我们,80%以上的机器学习项目永远无法走出实验室。
MLOps 承诺降低机器学习应用推广的失败率。但与 DevOps 的成熟做法不同, MLOps 还在不断进化。
新代码触发 DevOps 操作。
DevOps 可以处理数据格式或类型的变化,因为它们并不常见。因此,所需的代码更改和随后的生产部署并不常见。例如,避免在财务表中添加新列。
注意:有些人会认为代码变更的成本抑制了数据格式、类型和形状的变化。我参加过“数据标准委员会”的许多会议,会上他们考虑了在共享的 rmdb(ed)的表中添加、删除或重命名列的“环境影响”。关系数据库管理系统)。
我不认为 T21 需要更换。推出代码变更的过程自动化工作“好 够”更重要的是,我认为 DevOps 很好地服务于 Jenkins、Ansible、Cafe、Chef、Puppet、Github Actions、Gitlab 等工具。
DevOps 很棒。这里没有争论。
然而…
如果说几十年来有一个经验教训的模式,那么我们有新的流程在 DevOps 的范围之外。
我们为什么需要 MLOps?
我们需要我们的计算机在互联网、我们的手机、我们的身体、蛋白质测序以及所有其他现有和未来技术每秒钟创造的数据量爆炸式增长的过程中动态地学习和适应。
机器学习解决方案的数据经常变化。
我们需要传销,因为数据的变化可以改变传销的优化或改变我们对传销的选择。
如果数据的形状改变,机器学习应用程序的生产版本也会改变。
注:微软、谷歌、斯坦福等都有过机器学习应用推出失败的情况。在训练、测试和验证中工作良好的机器学习应用在真实世界的数据上失败了。
定义 MLOps 的五个操作组= devo PS+data Ops+MLLabOps+MLProdOps+DSOps
我将五个 Ops 组称为“重叠”,因为我们正处于 MLOps 的早期采用阶段。另外、****MLOps范式正在快速发展。在 MLOps 中的流程组件可以被添加、删除、复制、重命名,并且可以被分成不同的子流程组件。
我将 MLOps 的五个子 Ops 组定义为:
- 开发运营或 DevOps 是工具和子流程的成熟市场。
- 分布式系统操作或dsop不要与更通用的术语云操作或 CloudOps 相混淆。我称之为 DSOps 是因为我想包含 CloudOps 术语中不一定包含的东西。一个例子是边缘设备或者更好地称为物联网。目前,我将 CloudOps 归类为 DSOps 的子集,专注于在供应商或本地 IT 云中执行组件的架构和编排。
图一。显示了部署物联网边缘设备时发生的dsop的附加流程。
图一。DSOps 涵盖了边缘设备到物联网部署的底层流程(深蓝色)。图片:去飞溅
注:我可能是唯一一个用 DSOps 这个词的人。如果 DSOps 这个术语演变成 CloudOps ,我不会感到惊讶。称它为 CloudOps 比称它为 DSOps 要酷得多。
3.数据操作是一个成熟但仍在发展的工具和子流程市场。
4.机器学习实验室操作或 MLLabOps 是决定然后训练机器学习模型(MLm)的过程。此外,在 MLLabOps 组中是特定于 Mlm 的过程,如数据编码、特征重要性、特征工程、数据异常值去除和超参数优化。还可能有进一步的 MLm 微调、验证和多个 MLm 的集合。注意到 MLLabOps 的一些动作也可能在 DataOps 中。
5.机器学习生产操作或 MLProdOps 是将机器学习应用程序投入生产所需的所有流程。 MLProdOps 中的进程是经过训练的机器学习模型、日志记录、监控、可解释性,以及一些将新数据发送回数据操作进程的机制。日志记录、监控和可解释过程也可能出现在 MLLabOps 中。
MLOps = devo PS+data ops+MLLabOps+MLProdOps+DSOps
结论
这篇博客是一长系列 MLOps 博客的第四部分。前三个博客是:
</10-pitfalls-and-11-best-practices-for-the-design-phase-of-a-machine-learning-application-project-66c3d599f87> https://medium.com/swlh/stop-calling-machine-learning-operations-execution-flow-a-pipeline-94e55dd45366
MLOps 系列的下一个计划博客是 :
- m lops 生态系统的 22 个工具;
- m lops 治理的四十条准则。
我通过将复杂的主题 MLOps 分成五个操作( Ops )组来简化它——MLOps = devo PS+data Ops+MLLabOps+MLProdOps+DSOp。
我相信这些 xOps 的名字会随着 MLOps 的成熟而改变或被吸收。与此同时,它帮助我将工作分成不同的小组。希望这对读者你有帮助。
深入研究计算机视觉、变形金刚和自然语言处理
你已经有一段时间没有机会深入探讨一个话题了吗?你来对地方了。最*,我们很多人的生活都很忙碌,这意味着花时间真正学习、吸收和反思可能是一个挑战。我们在这里帮助选择最*的帖子,这些帖子涵盖了广泛而多样的主题,但仍然有一个共同点:对复杂的主题采取耐心、仔细和引人入胜的方法。我们开始吧!
- 赶上变形金刚 的高级微调技术。在之前的一篇文章中向我们介绍了微调的基础知识之后, Peggy Chang 将读者带到了下一个层次,解释了如何处理更棘手的情况(如大型模型和较小的数据集),同时使用包括分层学习率衰减和随机权重*均(SWA)在内的方法来提高性能。
- 建立一个图像分类系统,做出类似人类的决策 。大多数与计算机视觉相关的教程在粗略地做了一些介绍性笔记后就直接进入了实现阶段。Raveena Jayadev 的帖子与众不同之处在于,我们确实得到了一个全面的、实际操作的指导,来构建一个能够识别数字的机器学习系统,但这是在我们首先了解该系统的“心智模型”如何运作之前。
- 在线(机器)学习中打好基础 。作为一个流行的深度学习主题,在线学习在模型“必须从不断可用的新数据中学习”的场景中有许多行业应用,卡梅隆·沃尔夫在他对该主题的基本(全面)介绍中解释道。卡梅伦的帖子继续涵盖了在线学习方法,如正规化、提炼和再*衡(仅举几例)。
- 从设计阶段 就确保你的 ML 模型的公*性。 Divya Gopinath 从实用主义的角度探讨了机器学习中的公*问题:仅仅想要避免偏见是不够的;这种意图需要转化为可衡量和可持续的行动。她的最新帖子涵盖了公* ML 模型设计的基本原则。
- 学习如何用自然语言处理方法推断因果关系 。Haaya Naushan 的最新文章建立在她之前关于计量经济学的工作的基础上,并研究了 NLP 技术在因果推理中的一些有前途的应用,特别是在社会科学的背景下。
- 反思 AI 系统的灵活性 。经过短暂的夏季休息后,TDS 播客带着新一季和令人兴奋的新嘉宾阵容回归主持人杰瑞米·哈里斯。这一集的主角是 IBM 人工智能伦理全球负责人 Francesca Rossi,他讨论了人类认知和人工智能之间的联系,人工通用智能的潜在出现,以及可解释的人工智能(XAI)研究的前景。
谢谢你花时间和我们一起度过这一周,也谢谢你用各种方式支持我们的出版物和我们的作者。
直到下一个变量,
TDS 编辑器
我们策划主题的最新内容:
入门
实践教程
- 多层感知器用现实生活中的例子和 Python 代码解释:情感分析作者卡罗莱纳便当
- Julia 上带跳转的混合整数规划综合研究(第三部分)ouguenouni Mohamed
- 如何通过 Tirthajyoti Sarkar 在几分钟内从文档字符串生成专业 API 文档
深潜
思想和理论
- 由 Aadhithya Sankar 编写的关于阿特鲁(扩张)卷积和深度方向可分卷积的初级读本
- 反馈校准方法作者阿尔伯特·希门尼斯
- 利用时态词嵌入测量语义变化作者燕妮君
深入了解不同的 GAN 架构
英格玛在 Unsplash 上的照片
简介
GAN 是机器学习中有趣且令人兴奋的创新之一。生成对抗网络 (GAN)是两个神经网络以零和博弈的形式相互竞争的一类。给定一个训练集,这种技术学习生成具有与训练集相同的统计数据的新数据。
这是机器学习中的一项无监督学习任务,涉及自动发现和学习输入数据中的模式,以使模型可用于生成或输出可从原始数据集中提取的新示例。
在无监督学习中,只给出输入,目标是在数据中找到感兴趣的模式。
图:无监督学习示例 Img Ref: 机器学习掌握度
gan 是生成模型,即它们从训练数据创建新的数据实例。
应用
GAN 在时尚、艺术、科学、视频游戏等领域的应用迅速增加。例如,甘人从罗马皇帝的雕像中创造出看起来像人的形象。
建筑
生成式对抗网络由生成器和鉴别器两部分组成。生成器学习生成看似来自训练数据的可能数据,另一方面,鉴别器试图将生成器的假数据与真实数据区分开。鉴别器因不可信的结果而惩罚发生器。
生成器试图欺骗鉴别器,使其认为生成的图像是真实的,鉴别器试图区分真实和虚假的图像。随机噪声被输入发生器,发生器将随机噪声转换成“假图像”。鉴别器从训练集图像和来自生成器的伪图像中得到反馈,并且它必须将它们区分开来。
图:GAN Img 的架构 Ref: Google 开发者
GAN 背后的主要思想是交替训练发生器和鉴别器,以使它们在产生和鉴别图像方面尽最大努力。目的是通过改进其中一个网络,以这种博弈论的方式,另一个网络必须做得更好才能赢得比赛,这反过来又提高了性能,这个循环继续下去。生成性对抗网络在无监督任务中表现出令人印象深刻的性能。
一些最受欢迎的 GAN 架构包括:
a. CycleGAN
b.斯泰勒根
c.伊斯甘
d. DiscoGAN
照片由 Michal Bar Haim 在 Unsplash 上拍摄
a. CycleGAN
它是一种涉及图像到图像翻译模型的自动训练的技术,而不需要成对的例子。图像到图像翻译的例子是将马翻译成斑马,反之亦然。CycleGAN 使用周期一致性损失来实现不需要成对数据的训练。它可以从一个域转换到另一个域,而不需要源域和目标域之间的一对一映射。图像到图像转换问题的目标是使用对准图像对的训练集来学习输入图像和输出图像之间的映射。
对于两个域 X 和 Y ,CycleGAN 学习一个映射 G:X - > Y 和 F:Y - > X 。
CycleGAN 有三个部分:编码器、变压器和解码器。
当需要进行颜色转换时,CycleGAN 会很有帮助。
b. StyleGAN
由 Nvidia 研究人员推出的 StyleGAN 是一种新型的生成式对抗网络。StyleGAN 是 GAN 架构的扩展,它对生成器模型提出了较大的更改,包括使用映射网络将潜在空间中的点映射到中间潜在空间,使用中间潜在空间来控制生成器模型中每个点的样式,以及引入噪声作为生成器模型中每个点的变化源。
生成的模型不仅能够生成令人印象深刻的照片级高质量人脸照片,还能通过改变样式向量和噪声来控制不同细节层次的生成图像的样式。
c. LSGAN(最小二乘敌对网络)
在正常的 GAN 中,鉴别器使用交叉熵损失函数,这有时会导致消失梯度问题。相反,LSGAN 对鉴频器使用最小*方损失函数。它希望向生成器提供关于远离鉴别器模型的判定边界的假样本的信号,以便将它们分类为真或假。它可以通过对鉴别器层的输出层进行微小的改变并采用最小二乘法或损失函数来实现。
d. DiscoGAN
给定域 A 中的图像,它生成域 B 中产品的图像。它将元素从一幅图像转移到另一幅图像。DiscoGAN 有两个发电机。第一生成器将输入图像从域 A 映射到域 B 。第二生成器将图像从域 B 重建到域 a。
CycleGAN 和 DiscoGAN 有一点相似。在 CycleGAN 中,它有一个附加的超参数来调整重建/周期一致性损失在总损失函数中的贡献。
DiscoGAN 可用于将装饰从一款时尚单品(如包袋)转移至另一款时尚单品(如一双鞋)。
结论
有数千篇关于 GAN 的论文和数百个命名的 GAN,即具有定义名称的模型,通常包括“ GAN ”。使用 GAN 架构创建新架构可能会有一些困难。众所周知,训练 GAN 网络非常不稳定。超参数优化很难在学习率之间找到正确的*衡来有效地训练网络。在所有架构中,生成器最常用于训练数据。
参考资料:
1.https://en . Wikipedia . org/wiki/Generative _ adversarial _ network
2.https://machine learning mastery . com/what-are-generative-adversarial-networks-gans/
3.https://developers . Google . com/machine-learning/gan/gan _ structure
4.https://www . ka ggle . com/Roy data science/introduction-to-generative-adversarial-networks
5.https://www . geeks forgeeks . org/cycle-generative-adversarial-network-cycle gan-2/
6.https://neptune.ai/blog/6-gan-architectures
跳进我的火绒里
用 NLP 探索三年约会 app 消息
介绍
情人节就要到了,我们很多人都有浪漫的想法。为了公共健康的利益,我最*避免了约会应用程序,但当我在思考下一个数据集时,我突然想到 Tinder 可以帮我找到(双关语)我过去多年的个人数据。如果你好奇,你也可以通过 Tinder 的下载我的数据工具来请求你的。
在提交我的请求后不久,我收到了一封允许访问 zip 文件的电子邮件,内容如下:
个人 Tinder 数据 zip 文件内容
“data.json”文件包含购买和订阅数据、应用程序打开日期、我的个人资料内容、我发送的消息等等。我最感兴趣的是应用自然语言处理工具来分析我的消息数据,这将是本文的重点。
数据的结构
JSON 文件有许多嵌套的字典和列表,从中检索数据可能会很棘手。我用json.load()
将数据读入字典,并将消息分配给‘message _ data’,这是对应于唯一匹配的字典列表。每个字典都包含一个匿名的匹配 ID 和一个发送给匹配的所有消息的列表。在列表中,每条消息都采用另一个字典形式,带有“收件人”、“发件人”、“消息”和“发送日期”关键字。
以下是发送给单个匹配项的消息列表示例。虽然我很想分享这次交流的有趣细节,但我必须承认,我不记得我想说什么,为什么我想用法语说,或者“Match 194”指的是谁:
邮件列表示例
因为我对分析消息本身的数据感兴趣,所以我用下面的代码创建了一个消息字符串列表:
第一个块创建长度大于零的所有消息列表的列表(即,与至少发送一次消息的匹配相关联的数据)。第二块索引来自每个列表的每个消息,并将其附加到最终的“消息”列表。留给我的是 1013 个消息字符串的列表。
清理时间
为了清理文本,我首先使用自然语言工具包(NLTK)的停用词语料库创建了一个停用词列表——常用的和不感兴趣的词,如“the”和“in”。您会注意到,在上面的消息示例中,数据包含特定类型标点符号的 HTML 代码,如撇号和冒号。为了避免将这段代码解释为文本中的单词,我将它与“gif”和“http”等文本一起附加到停用词列表中。我将所有停用字词转换成小写,并使用以下函数将消息列表转换成字词列表:
第一个块将消息连接在一起,然后用一个空格代替所有非字母字符。第二个模块将单词简化为它们的“词条”(字典形式),并通过将文本转换为单词列表来“标记”文本。第三个块遍历列表,如果单词没有出现在停用词列表中,则将它们追加到“clean_words_list”中。
词云
我用下面的代码创建了一个单词云,以直观地了解我的消息语料库中最常见的单词:
第一块设置字体、背景、遮罩和轮廓美感。第二个块生成云,第三个块调整图形的大小和设置。这是渲染出来的单词云:
作者的 Wordcloud。经由 Vecteezy 的云罩。
云端显示了我住过的一些地方——布达佩斯、马德里和华盛顿特区——以及大量与安排约会相关的词汇,比如“自由”、“周末”、“明天”和“见面”还记得我们可以随意旅行,和刚在网上认识的人一起吃饭的日子吗?是啊,我也是…
你还会注意到云中散布的几个西班牙语单词。住在西班牙时,我尽力适应当地的语言,滑稽的笨拙对话总是以“no hablo mucho espaol”开头。
二元柱状图
NLTK 的搭配模块允许您查找并评估双词或在文本中一起出现的词对的频率。以下函数接收文本字符串数据,并返回前 40 个最常见二元模型及其频率分数的列表:
我对清理后的消息数据调用函数,并在 Plotly Express 条形图中绘制二元语法-频率配对:
由作者策划。
在这里,你会再次看到很多与安排会议和/或将对话从 Tinder 上移开有关的语言。在疫情出现之前的日子里,我更喜欢在约会应用程序上保持最少的来回,因为面对面的交谈通常会带来更好的化学反应。
对我来说,bigram ('bring ',' dog ')进入前 40 名并不奇怪。老实说,狗狗陪伴的承诺是我正在进行的 Tinder 活动的一个主要卖点。
信息情感
最后,我用Vader impression计算了每条消息的情感得分,它识别四种情感类别:负面、正面、中性和复合(衡量整体情感价值的一种方法)。下面的代码遍历消息列表,计算它们的极性分数,并将每个情感类别的分数附加到单独的列表中。
为了直观显示邮件中情感的总体分布,我计算了每个情感类别的得分总和,并绘制了它们:
由作者策划。
柱状图表明,“中性”是这些信息的主导情绪。应该注意的是,取情感分数的总和是一种相对简单的方法,它不处理单个消息的细微差别。例如,一些“中性”分数极高的信息很可能导致了该类的主导地位。
尽管如此,中立在这里比积极或消极更重要是有道理的:在与某人交谈的早期阶段,我试图显得礼貌,而不是用特别强烈、积极的语言超越自己。制定计划的语言——时间、地点等——很大程度上是中性的,似乎在我的信息语料库中广泛存在。
结论
如果你发现自己今年情人节没有计划,你可以花它来探索你自己的 Tinder 数据!你可能会发现有趣的趋势,不仅是在你发送的信息中,也在你对应用程序的使用中。
要查看该分析的完整代码,请访问其 GitHub 库。
XGBoost 解释:用不到 200 行 python DIY XGBoost 库
更新:发现我关于渐变增强的新书,实用渐变增强。这是用 python 中的许多例子对渐变增强的深入探究。
https://amzn.to/3OMgEkR 所示。但是有多少人真正理解它的基本原理呢?
你可能会认为,一个性能和 XGBoost 一样好的机器学习算法可能会使用非常复杂和高级的数学。你可能会认为这是软件工程的杰作。
你说对了一部分。XGBoost 库非常复杂,但是如果只考虑应用于决策树的梯度推进的数学公式,它并不复杂。
您将在下面看到如何使用不到 200 行代码的梯度推进方法为回归训练决策树的详细信息。
决策图表
在进入数学细节之前,让我们回顾一下关于决策树的记忆。原理相当简单:将一个值与遍历二叉树的一组给定特征相关联。二叉树的每个节点都附加一个条件;树叶是有价值的。
如果条件为真,我们使用左边的节点继续遍历,否则,我们使用右边的节点。一旦到达一片叶子,我们就有了我们的预测。
通常,一张图片胜过千言万语:
三层决策树。图片由作者提供。
附加在节点上的条件可以看作是一个决策,因此得名决策树。
这种结构在计算机科学的历史上非常古老,已经成功地使用了几十年。下面几行给出了一个基本实现:
堆叠多棵树
尽管决策树已经在一些应用中取得了一些成功,如专家系统(在人工智能冬天之前),但它仍然是一个非常基本的模型,无法处理现实世界数据中通常遇到的复杂性。
我们通常将这类估计量称为 弱模型 。
为了克服这一限制,九十年代出现了将多个弱模型组合起来创建一个强模型: 集成学习 的想法。
这种方法可以应用于任何类型的模型,但由于决策树是简单、快速、通用和易于解释的模型,所以它们被广泛使用。
可以部署各种策略来组合模型。例如,我们可以使用每个模型预测的加权和。或者更好的是,使用贝叶斯方法基于学习将它们结合起来。
XGBoost 和所有的 boosting 方法使用另一种方法:每个新模型都试图补偿前一个模型的错误。
优化决策树
正如我们在上面看到的,使用决策树进行预测非常简单。当使用集成学习时,这项工作并没有变得更加复杂:我们所要做的就是将每个模型的贡献相加。
真正复杂的是建造树本身!我们如何找到在训练数据集的每个节点应用的最佳条件?这就是数学帮助我们的地方。完整的推导可以在 XGBoost 文档中找到。这里,我们将只关注本文感兴趣的公式。
与机器学习一样,我们希望设置我们的模型参数,以便我们的模型对训练集的预测最小化给定的目标:
客观公式。作者的公式。
请注意,该目标由两个术语组成:
- 一个是测量预测产生的误差。这就是著名的损失函数 l(y,y_hat) 。
- 另一个,ω,控制模型复杂度。
正如 XGBoost 文档中所述,复杂性是目标的一个非常重要的部分,它允许我们调整偏差/方差权衡。许多不同的函数可以用来定义这个正则项。XGBoost 使用:
正则项。作者的公式。
这里 T 是叶子的总数,而 w_j 是附在每片叶子上的权重。这意味着大的重量和大量的叶子会被扣分。
由于误差通常是复杂的非线性函数,我们使用二阶泰勒展开将其线性化:
损失的二阶展开。作者的公式。
其中:
高斯和海森公式。作者的公式。
线性化是相对于预测项计算的,因为我们想要估计当预测改变时误差如何改变。线性化是必不可少的,因为它将使误差最小化。
我们想要用梯度增强实现的是找到将最小化损失函数的最优【δ_ y _ I】,即,我们想要找到如何修改现有的树,使得修改改进预测。
在处理树模型时,有两种参数需要考虑:
- 定义树本身的:每个节点的条件,树的深度
- 附在每片树叶上的值。这些值就是预测本身。
探索每棵树的配置将会太复杂,因此梯度树提升方法仅考虑将一个节点分成两片叶子。这意味着我们必须优化三个参数:
- 分割值:在什么条件下我们分割数据
- 附加到左叶的值
- 附加到右叶的值
在 XGBoost 文档中,树叶 j 相对于目标的最佳值由下式给出:
相对于目标的最佳叶值。作者的公式。
其中 G_j 是附着在节点 j 上的训练点的梯度之和, H_j 是附着在节点j上的训练点的 hessian 之和
利用该最佳参数获得的目标函数的减少量为:
使用最佳权重时的客观改进。作者的公式。
使用 brut force 选择正确的分割值:我们计算每个分割值的改进,并保留最佳值。
现在,我们已经获得了所有必要的数学信息,可以通过添加新叶来提高初始树的性能。
在具体做之前,我们先花点时间了解一下这些公式是什么意思。
爬行梯度推进
让我们试着了解一下权重是如何计算的,以及 G_j 和 H_i 等于什么。因为它们分别是损失函数相对于预测的梯度和 hessian,我们必须选择一个损失函数。
我们将关注*方误差,这是常用的,也是 XGBoost 的默认目标:
*方误差损失函数。作者的公式
这是一个非常简单的公式,它的梯度是:
损失函数的梯度。作者的公式。
和黑森:
损失函数的海森。作者的公式。
因此,如果我们记住最大限度减少误差的最佳权重公式:
相对于目标的最佳叶值。作者的公式。
我们认识到最优权重,即我们添加到先前预测的值是先前预测和真实值之间的*均误差的相反值(当正则化被禁用时,即λ= 0)。使用*方损失来训练具有梯度增强的决策树归结起来就是用每个新节点中的*均误差来更新预测。
我们还看到λ具有预期的效果,即确保权重不会太大,因为权重与λ成反比。
训练决策树
现在是容易的部分。假设我们有一个现有的决策树,它可以确保预测有给定的误差。我们希望通过分割一个节点来减少误差并改进附加目标。
这样做的算法非常简单:
- 选择感兴趣的特征
- 使用所选要素的值对附加到当前节点的数据点进行排序
- 选择一个可能的分割值
- 将此拆分值下方的数据点放在右节点中,将上方的数据点放在左节点中
- 计算父节点、右节点和左节点的目标约简
- 如果左右节点的目标约简之和大于父节点的目标约简之和,则保持分割值为最佳值
- 迭代每个分割值
- 使用最佳分割值(如果有),并添加两个新节点
- 如果没有拆分改进了目标,就不要添加子节点。
产生的代码创建一个决策树类,该类由一个目标、多个估计器(即树的数量)和一个最大深度来配置。
正如承诺的那样,这段代码不到 200 行:
训练的核心编码在函数 _find_best_split 中。它基本上遵循上面详述的步骤。
注意,为了支持任何种类的目标,没有手动计算梯度和 hessian 的痛苦,我们使用自动微分和 jax 库来自动计算。
最初,我们从只有一个节点的树开始,这个节点的叶值 leaf 是零。因为我们模仿 XGBoost,所以我们也使用一个基本分数,我们将它设置为要预测的值的*均值。
另外,请注意,在第 126 行,如果我们达到了初始化树时定义的最大深度,我们将停止树的构建。我们可以使用其他条件,如每片叶子的最小样本数或新权重的最小值。
另一个非常重要的点是用于分割的特征的选择。这里,为了简单起见,特性是随机选择的,但是我们可以使用更聪明的策略,比如使用方差最大的特性。
结论
在本文中,我们看到了梯度推进是如何训练决策树的。为了进一步提高我们的理解,我们编写了训练决策树集合并使用它们进行预测所需的最小行集。
深入理解我们用于机器学习的算法绝对至关重要。它不仅帮助我们构建更好的模型,更重要的是允许我们根据自己的需要修改这些模型。
例如,在梯度增强的情况下,使用损失函数是提高预测精度的一个很好的方法。
DMOT:ETL 的设计模式——数据模型、编排器、转换器
一个为生产 ETL 项目(MLOps)开发的 OOP 框架,受类似于 MVC 的框架的职责分离原则的启发。
图 1: DMOT 框架交互。作者图片(draw.io)
TLDR;编写 ETL 数据管道代码(MLOps)的建议设计模式。使用这个框架来减少调试时间,提高可测试性和多环境产品。
数据模型与数据源和存储进行通信。变压器操纵数据。编排器指导 ETL 作业,连接数据模型&转换器类,并且很可能链接到一个适用的编排服务。
这在写作时是新的(根据作者的知识),任何人都可以改进。
动机
为生产机器学习项目编写管道没有它们可能集成到的软件应用程序那么丰富的经验。然而,我们可以借鉴他们发展过程中的一些经验。
在 iOS 开发中,基于宏观层面的职责分离代码与 MVC 合作得很好。给 iOS 开发者的补充说明:如果你认为有人可以编写一个巨大的视图控制器,那就等着看 jupyter 笔记本有多大吧。数据项目已经存在,因此代码库的构建需要考虑测试&验证、开发人员可移植性和易维护性。
DMOT 概况
DMOT 根据 ETL 工作的职责将代码库分为三个不同的类别。该框架认为 ETL 的核心职责是提取和加载数据(有时是两个职责),转换数据,以及编排运行时文件/数据流。
一个 ETL 作业或管道可以由逻辑序列中的几个任务/作业组成。一个很好的软件管道类比是 onboarding flow,每个视图都是一个任务。每个任务都有自己的编排器、转换器和数据模型文件。实用程序类的继承(例如 SQL db 连接器)对于任务文件将是常见的。下面是如何工作的代码示例。
指挥者
orchestrator 是任务的实际运行时间。这是一个获取运行时环境、数据库机密和操纵数据的虚构示例。大多数编制者应遵循以下流程:
- 初始化要求
- 使用数据模型读入数据
- 使用转换器复制数据
- 使用数据模型保存复制的数据
数据模型
数据模型是一个使用自定义 SQL 类的类。这个 SQLclass 代表任何自定义数据库类。正如您所看到的,这个类有两个责任来提取和加载数据。数据模型应该避免使用像 pandas 或 numpy 这样的数据操作库。
在某些情况下,人们可以争论将阅读和写作分开。然而,如果数据源和目的地是同一个数据存储(例如:数据仓库或湖边小屋中的表),作者发现拥有一个类实际上更方便。
变压器
这个转换器例子很简单。它将数组转换成 pandas df,然后利用内置的。dropna()函数删除空值。任何时候数据发生变化,都应该在转换器中执行。
机器学习训练或推理工作将在变压器中进行。通常会看到自定义的 sklearn 或 keras utilty 类被导入到这些转换器中。
数据模型
职责
- 所有数据的提取和加载
- 与数据源/存储直接通信
例子
- 数据库查询
- DDL 语句
- 原始文件副本
-
- *模型加载和保存
验证方法
- 图式一致性
- 数据验证规则
- 数据源/存储测试连接
变压器
责任
- 数据处理和生成
- 从/向 orchestrator 接受和返回数据
例子
- 清理、格式化、日期时间转换
- 特征工程
- 模型训练和推理
验证方法
- 单元测试
管弦乐演奏家
责任
- 按顺序在数据模型和转换器之间传递数据
- 与数据模型、转换器和编排服务直接通信
例子
- 实例化数据模型和转换器类
- 以正确的顺序调用数据模型或转换器类中的公共函数
- 获取环境变量
验证方法
- 管道做它应该做的事情
- 与业务流程服务器的连接
为什么 DMOT 在实践中起作用
- OOP 允许项目间高度可重用的代码。
- 数据模型与转换的分离允许正确的测试发生。测试与数据库的连接不同于单元测试。
- 处理多环境项目,因为 Orchestrator 将环境层分离出来。数据模型和转换器可以在需要时使用它作为输入。
- 高度可移植的代码,因为开发人员将知道在哪里寻找问题。管道运行不正确,请检查业务流程。数字看起来不对,试试变压器。完全没有数据,检查数据模型。
算法有政治吗?
公*和偏见
神器有政治吗?这个问题已经是社会科学讨论了几十年的主题,直到今天仍然适用。
神器有政治吗?这个问题本身就是兰登·温纳的技术政治理论(温纳,1980 年)的标题,已经引起了几十年的讨论,今天仍然是相关的。在他有影响力的文章中,温纳提出了技术作为一种物质人工制品,可以体现政治理念的理论(温纳,1980)。这在当时是一个相当激进的概念——把一个物质艺术品想象成一个政治角色需要一个新的权力关系表达的概念。
事实上,这一理论一直备受争议,对温纳论文的回应一直反对赋予技术而非其他社会行为者的能动性(例如,Joerges,1999;伍尔加和库珀,1999 年)。然而,他的理论强调了关注技术设计的特殊功能和特征的价值。Winner 声称,技术可以体现特定形式的权力和权威,而不是被它们所处的环境政治化(Winner,1980)。他认为这很重要,因为它使我们能够理解技术设计选择的社会政治含义,而不是通过社会力量的镜头来分析一切。
尽管温纳在 40 多年前就发展了他的理论,他描述的概念今天仍然适用。事实上,最*提出的欧盟人工智能法规强调了对人工智能偏见等技术政治问题的认识(欧盟委员会,2021)。诸如此类的法律反映了人们对歧视性算法越来越多的担忧,这在公共和私营部门都可以看到。例如,亚马逊 2014 年的招聘算法被发现不成比例地拒绝了女性的简历。他们通过惩罚包含“女子”一词的简历(例如女子象棋俱乐部)和带有全女子学院名称的简历(路透社,2018 )来做到这一点。
亚马逊的算法不仅通过性别歧视的影响来支持 Winner 的理论,还通过性别歧视思想被编码到技术中的方式。首先,机器学习算法在过去(主要是男性)候选人的简历上进行训练,这使得该算法能够重现女性被排除在 IT 部门之外的历史。其次,使用性别作为相关变量允许算法在此基础上选择候选人。最后,决定设计一种算法,从一系列简历中选择最优秀的候选人,这赋予了该算法决定个人职业机会的能力。尽管这些都是由一系列人类参与者(他们应该对自己的产品负责)做出的设计决策,但该算法的材料特性和特征使其能够成为一个政治参与者,而不管亚马逊团队的意图如何。
“它让我们能够理解技术设计选择的社会政治含义,而不是通过社会力量的镜头来分析一切。”
因此,将 Winner 的技术政治理论应用到亚马逊的招聘算法有助于我们理解为什么技术的社会研究在今天仍然相关。亚马逊的算法不仅因为其所处的社会政治环境而具有性别歧视的影响,还因为政治理念被镌刻在技术中的方式。这些技术特征——用于训练机器学习算法的数据库、被认为与之相关的变量,以及该算法被转化为雇佣/不雇佣决策的方式——具有非常现实和非常政治的意义。
通过理解技术如何体现政治理念,我们可以将技术的概念重新定义为“中性工具”,并开始重新思考如何更好地设计技术。在亚马逊的案例中,我们可能会考虑如何使用更多样化的数据集来训练招聘算法,如何让它们接受审计机制的检查,以标记偏见的例子,或者如何阻止它们在决定我们未来的环境中使用。正如兰登·温纳所说:我们不仅可以考虑“技术是如何被构建的”,还可以考虑[…]我们以技术为中心的世界可能被重建的方式。
参考书目:
j .达斯廷,2021。亚马逊废弃了对女性有偏见的秘密人工智能招聘工具。[在线]路透社。可从以下网址获取:<https://www . Reuters . com/article/us-Amazon-com-jobs-automation-insight-iduskcn 1 MK 08g>【2021 年 10 月 1 日获取】。
欧盟委员会,2021 年。欧洲议会和理事会制定人工智能协调规则(人工智能法案)并修订某些欧盟法案的提案。[在线]欧盟委员会。可在:<https://eur-lex.europa.eu/legal-content/EN/ALL/?购买 uri = CELEX:52021 PC 0206>【2021 年 10 月 3 日访问】。
Joerges,b .,1999 年。政治有人工制品吗?。科学的社会研究,29 卷 3 期,第 411–431 页。
冠军,洛杉矶,1980 年。神器有政治吗?。,【在线】第 109 卷第 1 期,第 121–136 页。
伍尔加和库珀,1999 年。人工制品有矛盾心理吗?科学的社会研究,第 29 卷第 3 期,第 433-449 页。
所有俄罗斯人都热爱自己的工作吗?🧐工作的乐趣如何改变你对时间的重视程度。
数据分析、统计和可视化 13k+对时间计算器值的响应
照片由 Malvestida 杂志在 Unsplash 上拍摄
你一小时的时间值多少钱?可以看看赚多少。但是,如果有人每小时多付你两倍的工资,你会像灰姑娘一样牺牲一个小时的空闲时间来分好的和坏的扁豆吗?
你时间的价值不仅仅在于你得到了多少报酬。你知道这一点,并有意无意地赋予自己的时间一个价值,基于这个价值,你在生活中做出选择。
对于如何支配时间,最好能做出更明智的决定。但是,你怎么能计算出你一小时时间的价值呢?
幸运的是,有一个工具可以帮助你量化这个。而且,根据人们的反馈,我们可以更多地了解工作和时间价值之间的关系。
什么?一个时间价值计算器?😯
时间价值计算器 (VTC)是由研究机构 Clearer Thinking 开发的工具,该机构的“ 使命 是弥合关于人类行为的研究与现实世界中的行动之间的差距。”
VTC 是一个互动的在线测验,它让回答者面对不同的场景和发人深省的问题。完成大约需要 10 分钟,在回答完所有问题后,你会得到一个相当公正的估计,你有多重视他们的时间。(那不是超级酷吗,btw?)
该工具还询问了许多人口统计学问题,可以进一步洞察人们如何与他们的时间相关联。
💜热爱你的工作,热爱你的时间?
我们之前已经研究过就业状态之间的差异,并检查过就业者是否比失业者更珍惜他们的时间。
该工具还询问人们从工作中获得的快乐程度。
现在, 这个 是一个有趣的数据点!我想。
你从工作中获得的快乐程度会改变你对时间的重视程度吗?
总的来说,有 3947 人喜欢他们的工作,3321 人有点喜欢,568 人不喜欢。相当正面的画面。当然,这里可能存在自我选择效应——只有特定类型的人首先对使用职训局感兴趣。
这是每个工作乐趣级别的时间价值分布。
奖励第四个情节,你可以在那里画一只微笑的猫。😸
以下是每个享受组和每个问题的方法:
输出:
好吧,这告诉我们很少,但密谋救援!
输出:
哇,差异似乎真的很小。这张图表也让我想检查一下问题 C 是关于什么的,*均答案是如此之高。😲 [1]
但是!在统计学领域,目测图表是不够的!我们最好在不同的群体之间进行一些 t 检验,用一些实际的数字来告诉我们的直觉!
我们得到了白纸黑字的答案
嗯,现在看看这个!
似乎我们的直觉错了。当比较那些不喜欢自己工作的人和喜欢自己工作的人时,结果表明,他们在回答每个问题和珍惜时间方面的差异是显著的。
也就是说,你越喜欢你的工作,你就越珍惜你的时间。*(但请参见下面的备选解释)
检查其他值对之间的显著性也证明了:
- 那些只喜欢自己工作的人比那些喜欢自己工作的人更不珍惜自己的时间。
- 与那些不喜欢自己工作的人相比,有些喜欢自己工作的人在问题 A 的答案和时间的最终价值中有明显更高的价值。
所以,你对工作的享受程度对你如何评价自己的时间很重要。
这些结果可能有几种不同的解释。
- 享受程度越高,你就越有可能在职业培训中心描述工作场景的问题中给出更有价值的答案。
- 或者,通常喜欢活动(包括工作)的人更重视他们的时间。
- 或者,更珍惜时间的人,会做出更谨慎的工作决定,更有可能从事自己喜欢的工作。
🌏全球工作乐趣!
在数据集中,受访者的收入以下列货币计:美元(USD)、欧元(EUR)、英镑(GBP)、以色列谢克尔(NIS)和印度卢比(INR)。这些值被标准化为国际美元(根据 2019 年来自经合组织数据的转换率)。
好了,这不是整个地球,只是其中的一部分。
让我们看看享受和时间的最终价值之间的关系在不同的货币中是否成立。
传入:*均值!
输出:
为你的视觉享受而策划。
输出:
在类别中,没有俄罗斯受访者,这是否意味着所有俄罗斯人都喜欢自己的工作?
不,我们不要妄下结论。诚然,每个货币组中的受访者人数并不相等,有些人很少(卢布收入者是最小的群体)。因此,就结果而言,我将是对美元、欧元、英镑和 NIS(后者次之)最有信心的。
从上面的图表中可以看出一个令人惊讶的现象。在印度和以色列,那些不喜欢工作的人比喜欢工作的人更看重时间。这与我们之前看到的总体趋势相反,其他所有货币也是如此。
工作乐趣越高,时间价值越高。
🔮工作享受的未来(分析)
作为未来的一步,检查价值(享受和货币的时间价值)与*均年薪的*方是很有趣的。
工作假说认为,更高的年薪会带来更高的享受, 和 会带来更高的时间价值。
如果这是真的,高收入者会非常珍惜他们的时间,即使他们厌恶自己的工作。
我敢肯定你有一个刻板的、最不喜欢的职业,很容易想到这个例子(那个有六位数收入却总是抱怨的朋友?).但是要小心情绪化的可用性偏见!
也许将来我们能够用真实的数字来检验这个假设。
[1]事情是这样的:
假设你有机会在一家你经常去的商店获得一张价值 100 美元的礼券,但是促销活动要求你在商店排队等候领取礼券。假设你有某种形式的最低限度的娱乐,那么你等待的时间既不会特别愉快,也不会特别不愉快。
多长时间 分钟 你愿意等多久?”
如果你喜欢这种思维练习,并且想知道你到底有多珍惜你的时间,那么看看这里的计算器。
人工神经网络真的会学习吗?
或者他们的学习过程只是超铀星的另一个想法?
埃文·卡拉乔戈斯在 Unsplash 上拍摄的照片
两千年前,一些穿着长袍的希腊人问自己,思想从何而来,我们如何学习。这些问题使他们成为重要的历史人物,并引发了一场持续的讨论。回答“我们如何学习?”可能会带我们经历一次世界各地哲学家和思想家的旅程。这与今天关于深度学习的讨论有什么关系?答案少了点哲理,多了点实用……
深度学习前的一些深度思考
在我们生命中的某个时刻,我们一定听说过苏格拉底、柏拉图和亚里士多德。也许你见过他们的大理石半身像,或者你试图在拉斐尔的壁画中找出谁是谁,雅典学院(实际上这并不是一件容易的事情!).他们不断地以不同的方式被绘制和描绘,几个世纪以来,人们一直在研究和分析他们的作品。柏拉图的语录是哲学讲座以及 t 恤和杯子背后的动力。他们和一切都有关系!甚至深度学习。
图一。雅典学派。拉斐尔的壁画(1509-1511)
苏格拉底从未写过任何东西[1]。我们对他的了解都是因为他最著名的学生柏拉图。受到他的老师的学习的启发,柏拉图是第一个思考我们的想法和学习过程的人。简单来说,他认为学习就是记忆。根据柏拉图的说法,我们都来自一个灵魂的世界(他称之为亥伯拉尼翁),在那里我们对所有的思想有着完美的了解。当我们生活在地球上的时候,我们仅仅记得我们从灵魂世界所知道的事情。想法就在那里,我们只需要记住它们!
有时候,当我在等待我的人工神经网络 (ANN)完成它的训练时,我会想到这个。也许人工神经网络也来自这样一个世界,在这个世界里,不管我们有多少个人工神经网络,它们的所有参数都是已知的。也许训练只是记忆?这总是给我希望!
柏拉图的观点受到了批评。柏拉图的一个学生亚里士多德说,不存在我们都来自的思想世界。对亚里士多德来说,人类有能力找到每天都知道的事物的普遍特征。如果我们第一次被带到超市,看到一堆柠檬,我们会知道它们都是柠檬,尽管它们不完全相同。柠檬又圆又黄的想法在所有的柠檬中是普遍的。我们不需要来自一个特殊的灵魂世界才能知道这些!我们的大脑足够聪明来概括这些特征,并提出柠檬的概念。对亚里士多德来说,学习不是记住那些不知何故已经深深埋藏在我们内心的想法。相反,亚里士多德认为我们从现实世界中每天感知和经历的事物中学习。
在亚里斯多德和柏拉图之后一千多年,约翰·洛克提出了类似的想法。他说,当我们出生时,我们的大脑就像一块白板,我们的经历和与世界的互动塑造了我们的思想,进而塑造了我们的人格。这与柏拉图的思想相矛盾,即大脑记得已经存在的事物,并引发了今天仍然活跃的讨论。现代先天 vs 后天的争论包含了两种不同的观点:要么人类的行为是由我们的经历塑造的,要么我们的行为是由我们的基因决定的。
这一切和人工神经网络有什么关系?看起来,所有这些关于哲学家的故事和他们的想法与人工神经网络算法和 Python 库毫无关系。然而,这些事情之间的联系比你想象的更强。
人工神经网络
人工神经网络(ANN)是理解深度学习的关键。在对深度学习过程的每一个描述背后,都有一个人工神经网络,它被设计和训练来完成一个特定的目标。人工神经网络是一组接收和发送信号的连接单元(神经元)。人工神经网络的工作方式应该类似于我们大脑中神经元的交流过程。[2]在现实中,虽然 ann 和真正的神经网络有共同之处,但它们并没有那么相似。为了解释生物神经网络是如何工作的,我们需要谈论电和化学突触以及蛋白质和膜。解释一个简单的人工神经网络如何工作与其他事情有关。
在人工神经网络中,一组输入数据被输入到分布在不同层的单元网络中。输入数据根据每个单元的激活函数以及连接所有单元的权重和偏差进行转换。在网络的末端,最后一层检索输出,并将其与实际观察到的数据进行比较。到目前为止,人工神经网络一直在前向传播模式下工作,这意味着输入数据、激活函数、权重和偏差一起用于确定输出。将该输出与观察到的(真实)数据进行比较会触发人工神经网络中的第二个过程,称为反向传播或简称为反向传播。在此过程中,输出和实际数据之间的差异用于调整连接所有单元的权重和偏差。通过损失函数计算出输出数据与真实数据的差距。[3]这种调整被重复多次,直到我们可以说人工神经网络被“训练”好。最好用一个例子来说明这一点。
苹果、橘子和柠檬
让我们假设我们正在开发一个能够区分柠檬和其他水果的人工神经网络。这类似于我们之前提到的例子,但并不相同(我们稍后将回到这一点)。我们首先需要的是输入数据。在这种情况下,我们应该有一个不同图片的数据库和一个标签,告诉我们这些水果中哪些是柠檬,哪些不是。该数据集的大小和质量是人工神经网络训练过程中最重要的方面之一。该数据集将允许我们将来自人工神经网络的结果与预期结果进行比较,并相应地调整权重和偏差。图 2 显示了一些水果及其标签的例子。这只是为了演示的目的。实际上,我们需要一个大于 8 个例子的数据集。
图二。示例中使用的小型数据集(取自 Microsoft Office 的图片)
像这样的数据集必须以一种算法可以解释的方式进行编码。对于这样一个简单的例子,我们可以将标签转换成一(1)和零(0),并将图像转换成 1D 数组,该数组考虑了图像的分辨率和每个像素的颜色。如果我们假设第一幅图像的分辨率为 30×30 像素,我们可以用一个 30×30×3RGB 数组%20value%20can%20be%20specified.)来表示该图像,该数组可以展*为一个 1D 数组,其中包含每幅图像的多个参数。这意味着具有 30×30 像素分辨率的 8 个图像的输入数据集可以被转换成包含 2700 行和 8 列的 2D 矩阵。请注意,这个过程是解释如何处理图像识别问题的简单方法。更可靠的方法是使用多维张量和使用卷积神经网络【4】
图 3。如何转换数组中的图片(图片取自 Microsoft Office)
前进
输入数据不仅包含编码图像,还包含每个图像的编码标签。因此,对于图 2 的矩阵中的每一列,我们将有另一个矩阵,其标签指示图像是柠檬(1)还是不同的水果(0)。这些矩阵中的第一个通过左侧进入网络,如图 4 所示。这是正向传播过程的开始。每个示例的每个参数根据网络的架构以及权重和偏差的值进行转换。许多书籍和在线资源都包含了不同的例子和解释。[2,5]在这种情况下,可以说每个单元考虑了权重和偏差的值以及从左侧连接的单元的输出。每一条连接单元的黑线都有一个单独的权重( w ),每一个黄色和绿色的单元都包含一个单独的偏差( b )。因此,每个单元内部都有两个主要过程:
- 权重和偏差用于计算该单元的总输入。如果一个单元连接到三个单元,那么我们将需要这些单元的三个输出值、三个权重和一个偏差:
- 一旦我们有了这些值,我们使用每个单元的激活函数将输入转换成每个单元的输出。激活函数是人工神经网络的核心。它们的主要功能是添加建立模式和关联进入网络的所有信息所需的非线性。如果你画出这些激活函数中的一些,你会注意到它们中的大多数是非线性的,这正是我们想要的:在单元和层之间建立非线性关系。
图 4。一个简单的人工神经网络方案(图片由作者制作)
关于人工神经网络的体系结构还有最后一件事要说,这与它的层次有关。图 4 所示的人工神经网络包含一个输入层、一个输出层和三个隐藏层或中间层。我们应该有多少层?每层多少单元?这不是一个简单的问题。层的大小和数量可以极大地改变人工神经网络的性能。更多的层或更多的单元并不一定意味着人工神经网络的性能会更好。人工神经网络的体系结构对于不同类型的问题可能是特定的,并且在决定正确的层数和单元数之前必须进行测试。对于与示例中出现的问题类似的问题,隐藏层的数量通常为 2–4[6],但这可能会根据数据量及其质量而增加。包含两层以上的人工神经网络通常被认为是深度学习的一个例子[7]。
我们已经走了一半了。在这一点上,我们已经完成了第一次正向传播,我们最终得到了一个可以与标签矩阵进行比较的输出矩阵。这种比较将告诉我们我们的人工神经网络有多精确。通常,神经网络的初始权重和偏差是随机选择的。然而,对于人工神经网络的特定应用,情况可能并非如此[8]。在这种随机初始化权重和偏差的情况下,人工神经网络很可能会产生错误的结果。那我们要做什么?我们如何告诉人工神经网络一些苹果被错误地归类为柠檬?更重要的是,柏拉图的思想世界发生了什么变化?为什么这个安不能记住柠檬是什么?
回到超铀星
除了观察到人工神经网络不是真正的神经网络之外,还有一个事实,即人工神经网络的行为依赖于它的权重和偏差。如果这些参数是随机初始化的,那么我们几乎可以保证这个人工神经网络将在它的第一次迭代中错误地分类许多水果。我们需要一个过程来调整权重和偏差,这样我们就可以无误地执行分类。这个过程被称为反向传播。
在反向传播过程中,测量输出和观察数据之间的差异,然后用于在所有层上分布误差,改变权重和偏差。输出和观测数据之间的差异或误差可以用多种形式计算。我们可以使用均方根误差,尽管在分类问题中,比如柠檬例子中的问题,通常使用对数损失函数。这个函数被称为成本函数。
图 5。均方根和对数损失成本函数
计算所有输出和观察数据之间的误差将告诉我们人工神经网络在分类中有多精确。但是,这个数字不足以调整权重和偏差,也不足以改善网络的预测。反向传播背后的主要思想是有一种找到成本函数最小值的方法。请记住,任何成本函数都取决于观察数据( y )和人工神经网络的输出( a )。该输出是每层的权重和偏差以及输入数据的函数( x )。由于输入数据不可修改,调整成本函数最终值的唯一方法是修改权重和偏差。这种修改必须使最终成本尽可能低。因此,反向传播实际上是一个最小化问题。不过,这并不简单。成本函数取决于多个权重和偏差,这些权重和偏差随着添加到网络的每个单元或图层而增加。[3]那么如何解决这个问题呢?
如何反向传播
梯度下降的方法已经广泛应用于这类问题。它允许我们计算一个函数在特定点的梯度,这样我们就可以沿着这个方向,直到找到一个最小值。作为图形示例,请看图 6。它显示了一个依赖于两个变量 x 和 y 的函数。我们想知道 x 和 y 的值是多少,这将导致 z 的最小值。如果我们从 x 和 y 的随机组合开始,这是曲面中的一个随机点,我们可以计算出函数在那个特定点的梯度。这个梯度会告诉我们,如果我们想找到那个曲面的最小值,我们应该沿着哪个方向走。然后我们可以在那个方向上迈出一小步,再次计算梯度。如果我们重复这个过程足够长的时间,根据我们所拥有的表面类型,我们可以找到这个函数的最小值。
图 6。梯度下降(使用 GeoGebra 绘制的 3D 图)
梯度下降法并不完美。不能保证找到的最小值是全局最小值。此外,它高度依赖于正在采取的步骤的大小。尽管如此,梯度下降已在许多不同规模和结构的人工神经网络中实施,并取得了良好的效果。更重要的是,这是理解人工神经网络如何被训练以及反向传播过程中发生了什么的好方法。如果你想尝试不同的优化方法,除了梯度下降,看看这个链接。
注意,如何通过函数对每个变量的偏导数,计算出从初始点到最小值的方向。一旦计算出这些偏导数,就从初始点中减去它们,以找到一个跟随梯度并接*最小值的新点。如果你不完全确定计算这些偏导数的效果是什么,或者你开始因为方程而担心,你可以阅读这篇文章或者做一个快速的在线搜索。这是一个简单的想法。
在图 6 的例子中,计算包括 z 相对于 x 和 y 的偏导数。然而,如果我们回到我们的人工神经网络的例子,你会看到,我们试图最小化的成本函数不仅取决于两个变量,而且取决于多个变量:每层上每个单元的权重和偏差。因此,我们将拥有与我们最初定义的权重和偏差一样多的权重和偏差梯度!最后一件事,还记得我们说过梯度下降法会指出我们应该遵循的方向,以找到最小值,一旦我们有了这个方向,我们就要朝着我们的最终目标迈出一小步。这一步有多小?这就是为什么我们在人工神经网络算法中又多了一个旋钮!学习率[3]将定义该步长的大小。图 7 是一个简单的流程图,包含了人工神经网络工作流程中的所有步骤。请注意,在计算梯度后,我们必须通过从参数中减去梯度来更新参数。这些梯度乘以学习率。小的学习率将需要更多的迭代来找到最小值。一个大的学习率应该需要更少的迭代,但它可能有更多的问题要收敛。
图 7。人工神经网络流程图(图片由作者制作)
那么我们现在到底有什么,我们能用它做什么呢?
前面的例子只是真实的 ANN 实现的一瞥。正如我前面提到的,我们将需要一个更大的数据集和相当多的时间来调整层数和每层的单元数、激活函数、学习速率等。在此之后,我们应该使用不同的数据集(通常是原始数据集的子集)来测试人工神经网络的准确性。人工神经网络的一个常见问题是,它们最终经过足够的训练,可以在训练数据集上正确执行,但在不同的数据集上却给出糟糕的结果。这通常与较差的调优或架构规格有关。一旦我们完成了前面的所有步骤,并且对人工神经网络的性能有了信心,那么我们完成了什么呢?这个安学到了什么?
按照前面的例子,我们将有一个能够区分柠檬和任何其他水果的人工神经网络。我们给这个安一张任何水果的照片,它会告诉我们这是不是柠檬。请记住,每张图片都将作为 1D 数组进入人工神经网络,该数组中的所有值都将与每个单元和每个层的权重和偏差相互作用。在人工神经网络结束时,最后一层将输出一个值,该值将定义进入人工神经网络的图片显示的是柠檬还是任何其他水果。这个过程可以推广到许多其他应用。我们可以有一个人工神经网络,它的输出不是一个单一的值,而是由多个数字组成,这些数字将告诉我们图片是否对应于柠檬、苹果、香蕉或任何其他水果。通过更多的努力,我们可以建立一个人工神经网络来识别照片中是否包含汽车、自行车或交通灯。人工神经网络的应用是多方面的,并不仅仅与图像处理有关。一旦你对人工神经网络的工作原理有了清晰的认识,你就会发现我们日常生活的许多方面都与深度学习有关。不管应用程序的重要性和复杂性如何,人工神经网络背后的学习过程是一样的,事实上人工神经网络需要大量的数据来训练。
还记得我们说过柠檬标识符的例子和某人第一次去超市不完全一样吗?这个人以前对水果一无所知,他会试着辨别哪些水果是柠檬。这个人需要多少“柠檬——不是柠檬”的例子才能确定柠檬是柠檬?对于一个真实的神经网络,我们不需要很多例子。新生儿可能无法立即记住所有水果的名称,但他能够知道所有的柠檬,不管它们的大小、黄色和形状如何,都是柠檬,与香蕉或苹果完全不同。这个新生儿需要多少柠檬来概括柠檬的特征,并了解所有柠檬的共同点?肯定没有人工神经网络需要的多[9]。公*地说,我们的学习过程与人工神经网络的学习过程不同。人工神经网络真的会学习吗?如果他们这样做了,那么这将不是我们同样的学习过程。
柏拉图的形式,亚里士多德的共性,和安的隐藏层
在结束之前,让我们最后一次回到柏拉图、亚里士多德和先天与后天的争论。根据柏拉图的观点,我们在这个世界上看到的不是真实的东西。真正的是这些事物的“观念”或“形式”。在我们当前的世界里,没有完美的圆形或三角形。然而,我们知道完美的三角形或圆形应该是什么样子。这些“形式”代表了事物的本质,不知何故我们的头脑中已经有了这些信息。因此,学习就是意识到这些形式中的哪一种对一个特定的事物来说是独一无二的,从而可以定义它并将其与其他事物区分开来。
另一方面,亚里斯多德说事物之间有的普遍特征。我们可以谈论柠檬的黄色或苹果的红色。然而,对于亚里士多德来说,这些普遍特征并不是自然而然地嵌入我们的头脑中的。它们是我们对现实世界体验的产物。在亚里士多德的观点中,我们可以看到一堆柠檬,并且知道它们都具有相同的黄色特征,尽管不是所有的柠檬都具有相同的形状,甚至不是相同类型的黄色。在这种情况下,学习是我们与现实世界互动的产物。我们的大脑能够定义事物的普遍特征,并知道哪些水果是柠檬,哪些是苹果。
人工神经网络与这些理论有什么关系?首先,很重要的一点是,使用人工神经网络的经典方法是随机初始化权重和偏差。这种随机初始化不同于柏拉图的一系列预先加载的想法或形式的想法。先验地知道正确的权重和偏见是什么,就像有一套预先加载的想法让我们学习一样。这在大多数情况下是不正确的。然而,柏拉图的理论可以解释为什么使用具有相同的初始权重和偏差集以及预先建立的架构的人工神经网络来解决类似的问题。尽管如此,没有一种权重和偏好的完美组合可以解决所有问题。人工神经网络可以在高度非线性的问题上有很好的表现,但对于看不见的数据,它永远不会达到 100%的准确性。
如果我们用亚里士多德的理论分析人工神经网络,那么我们会说,如果我们选择正确的架构,人工神经网络必须总是收敛到一个完美的结果。经过多次迭代并使用几乎无限的输入数据集后,人工神经网络将最终获得权重和偏差的正确值,结果将 100%准确。这对于简单的问题可能是正确的,但对于更复杂的问题,不幸的是,不能保证人工神经网络会收敛到一个完美的结果。的确,增加更多的隐藏层和使用不同的激活函数将有助于识别数据中的非线性关系。然而,这并不意味着人工神经网络将总是收敛到一个可接受的结果。
在现实中,人类和动物的学习是如何工作的可以解释,尽管有一部分信息在出生时就已经存在于大脑中,但学习还包括经验和与世界的互动。研究表明,大脑中神经元的连接方式在出生时就已经预先建立了[9]。这解释了为什么多种动物生来就有本能或指令来帮助它们完成基本的生存任务,如喂食或交配。这种知识已经存在于大脑中,它与包含在基因组中的信息有关,而基因组又是多年进化的产物。大脑中预装的信息补充了通过对世界的体验获得的知识。我们的学习过程是我们大脑结构(先天)和与世界互动(后天)的结果。
结论
虽然“学习”一词经常与人工神经网络联系在一起使用,但很明显,人类学习和人工神经网络学习是两个不同的过程。大脑结构的复杂性和复杂的人工神经网络结构之间有重要的区别。我们可以通过从以前的人工神经网络实现中继承权重、偏差和架构来模仿我们学习过程的一部分是天生的这一事实(这被称为元学习,基本上包括发生在两个级别的学习过程,每个级别都与不同的时间尺度相关联[10–12])。然而,当正在使用的训练算法和我们大脑中发生的事情之间没有明确的*行关系时,我们仍然必须解决学习的“培育”部分。除此之外,记住人工神经网络的最终输出是通过多个层和单元评估的多个非线性函数的结果是很重要的。这个结果与使用的激活函数一样好。例如,仅具有线性激活函数的神经网络将不能识别非线性关系,不管其架构如何。
尽管它们之间存在明显的差异,但人工神经网络的设计遵循了生物神经网络的本质。这是人工神经网络重要改进的灵感来源,比如卷积神经网络。真实的神经网络经常被用作人工神经网络应该是什么的参考。然而,我们应该谨慎对待我们可以从人工神经网络中期待什么,以及它的学习过程与生物学习有何不同。也许一个真正的神经网络只是一个想法或一种形式的人工神经网络应该是什么样子。在我们的内心深处,我们知道真正的学习是什么样子,我们知道可能永远不会有完美的人工神经网络。然而,我们与真实神经网络的互动和经验肯定会帮助我们提高对人工神经网络的理解,并使它们成为我们智力和推理的有力补充。
参考
- 谁是苏格拉底?哲学最大的奥秘解读。大思考。
- 主教,C. (2006 年)。模式识别和机器学习。斯普林格。美国纽约。
- Kantanforoosh,k .,Kunin,d .,Ma,J. (2019) 神经网络中的参数优化。DeepLearning.ai。
- 莫等(2019) 卷积神经网络结合集成学习算法的图像识别。物理学杂志。爵士。1237 022026
- 叶,倪(2014)。数据挖掘。理论、算法和实例。CRC 出版社。美国佛罗里达州。
- 深度神经网络赋值。神经网络和深度学习(来自深度学习专业的课程 1)。Coursera
- 隐藏层数。希顿研究。
- Rudd-Orthner,R.N.M .,Mihaylova,l .在深度学习的智能城市应用中使用非随机权重初始化的可重复确定性。j 可靠的智能环境 6,31–49(2020)。https://doi.org/10.1007/s40860-019-00097-8
- Zador,A.M. 对纯学习的批判以及人工神经网络可以从动物大脑中学习到什么。国家共同体 10,3770 (2019)。https://doi.org/10.1038/s41467-019-11786-6
- 桑托罗等人(2016 年)。记忆增强神经网络元学习。第 33 届机器学习国际会议论文集。美国纽约。
- 元学习的观点和调查。人工智能评论,18(2):77–95,2002。
- 翁,L. (2018)。元学习:学会快速学习。
微积分和连续分布
思想和理论
连续分布对于 Do 演算来说没有概念上的困难,但是因果关系可能是容易的部分
当因果假设被编码在有向无环图(DAG)中时,可以应用微积分来寻找介入分布。然而,这些介入分布通常是难以估计的泛函。“尽管这些方法获得了所有的能力,但仍然没有系统的方法来估计在计算和统计上都有吸引力的任意可识别泛函,”【Jung、Tian 和 Bareinboim 最*在 2020 年 6 月写道。通常,介入性分布用总和表示,至少暗示了离散变量(我还不理解算法)。
Do-Calculus 的演示总是考虑离散分布,大概是因为这种区别对于推理的因果部分在概念上并不重要,也因为它允许使用二元变量的简单示例。即使对于高维的情况,从非参数估计的角度来看,比复杂的多元密度更容易想象知道一个大概率表是一场灾难。
本文探讨了微积分在连续变量中的应用。这是天真的,使用第一原则,并没有逃脱估计“灾难”我将介绍一个以非线性、非正态性为特征的人工数据生成过程,并匹配一个已知的 do-calculus 表达式,我希望这将有助于测试新方法。我会离散化为*似积分,但必须遵循一个规则:如果一个分布是连续的,用单变量核密度估计进行估计,数值积分得到下游量。不到 200 行的代码位于 Github Gist 中。
我们将会看到,虽然连续分布对于 Do 演算来说没有概念上的困难,但是维数灾难却赫然耸现。获得正确的 DAG 可能最终成为更容易的问题。
积分和微积分
本文从 Hünermund 和 Bareinboim 的计量经济学中的因果推理和数据融合中的选择偏差示例开始(第 A.2.2 节,2021 年 3 月 10 日修订)。从图中可以看出:
Hünermund 和 Bareinboim 的选择偏差示例的因果图(第 A.2.2 节)。图片作者。
出现了函数
Pr(y | do(x)) = \sum_{z}P(y|w,z,x,s = 1)P(z|w,s = 1)。
为了说明和与积分在 Do-Calculus 表达式中是可以互换的,请考虑下面这个例子的论点概要,其中强调了 Do-Calculus 的三个规则的应用:
导致介入分配的 Do-Calculus 论证的手写草图。图片作者。
代替典型求和的积分完全来自概率论,除了首先需要扩展表达式之外,与微积分无关。
非线性连续数据生成过程
模拟数据很无聊,但我可以给它一个虚构。这是一个叠加在上面图 G 上的教室场景。这种观点认为,个人享有的“时间资源”既与上课有关(即选择机制),也是学习材料中发现的“乐趣”和个人能够学习的时间的直接原因。不足为奇的是,享受学习的过程会比时间资源本身带来更多的学习。等级和享受之间的无向因果箭头被“主体亲和力”所取代,“主体亲和力”是一种潜在的自然能力,它决定了物质获得的难易程度,我们假设精神分析师无法获得这种能力。成绩本身只是投入的时间、天生的能力和一些噪音的结果。没有曲线;一个学生的成绩独立于所有其他学生。
W:为考试学习的时间资源,以小时为单位。
W~γ(3,4)。
U:受试者亲和力——在示例 7.3 中未提及,但将用作造成双向弧的未观察到的混杂因素。分析师未观察到的。
U ~ Uniform(1,20)。
S: 班级招生二元指标。
Pr(S = 1 | w) 是一个阶跃函数,在特定的时间资源水*上升:
- .1 如果 w < =5,
- 2 如果 w > 5,w < = 20,
- .3 如果 w > 20。
Z:在研究材料中发现的乐趣(分析师可能通过调查等获得)。).
Z | w 保持为 1,直到时间资源超过下限,然后与主题相似度成*方增加,与时间资源成对数增加:
- 1 如果时间资源≤ 1.3,
- subject _ affinity ^ 2 * log(1+time _ resources)),否则。
X:为考试而花在学习上的时间。
X| w,z 是一个截尾正态,其均值随快乐指数的分数次方增加,但在左边受限于 0,在右边受限于时间资源
- a *均值为-1.5 + 1.5 * z ^ .35,
- 一个****三个的标准差
- 并且是不是左边截成 0 小时,右边截成时间资源(学习时数不能比自己多)。
****Y:测试等级。
Y| z,x 正态分布,*均值随受试者亲和力线性增加,随研究时间对数增加
- 40 + u * log(1 + x)的*均值,****
- 一个标准偏差为 4。****
在移除流入 X 的所有箭头的“毁损”之后的介入 DAG 更简单:
来自 Hünermund 和 Bareinboim 的选择偏差示例的“残缺”图(第 A.2.2 节)。图片作者。
记住这个例子用单个混杂因素 u 代替了双向箭头,我们看到 y 和 s 之间的唯一路径 y z s 在条件集{x}下被碰撞器 z 阻塞了。因此,当从该机制进行模拟时,u 可以从 1 到 20 自由地均匀变化。
虽然观察数据生成过程相当复杂,但干预过程非常简单,导致等级分布基本均匀,具有轻微的正态尾部。即
Y = 40 + u * log(1 + do(小时))+ε,
其中 u 均匀(1,20),ε正常(0,4)。对于 do(hours =6) ,我们预计 99%以上的所有等级的分布在 42 到 79 之间,并且正态尾部将范围扩展到 30 到 81。
我们生成的人口有 100 万人,我们非常大的 MOOC 有* 20 万名学生(198,111 人使用代码中提供的种子)。
估计连续介入分布
第一个要问的问题是什么时候我们用时间资源(w)做什么?幸运的是,给定 x,y 和 w 是 d 分隔的,因为能够以 w 为条件解决了我们的选择偏差问题。但是 w 在表达式中两个密度条件的右边,从字面上理解,它告诉我们把它设置为一个值,然后离开它。您选择的哪个值会对观察到的花费时间(x)的条件分布产生影响:
给定时间资源(w ),花费时间(x)的条件分布。图片作者。
花费小时数(x)的数据生成过程将时间资源(w)作为最大可用小时数,因此这并不奇怪。虽然 do-calculus 没有说时间资源(w)的选择,但如果我们想插入 do(hours) = 6,我们将很难估计时间资源(w)设置为* 1 小时的条件分数分布,因为没有数据。这还是在以享受为条件之前(z)。
让我们将时间资源(w)设置为接* 20 个小时,我们在样本中观察一系列学习时间(x)。然而,当我们用快乐(z)来*似积分时,这个时候问题就出现了。对于 z 的几乎一半的可能范围,甚至没有两个点可用于估计密度 f(y|w,z,x,s = 1)。
没有足够的数据来估计每个 z-bucket 的 f(y|w,z,x,s = 1)。图片作者。
如果我们忽略这些洞并重新调整估计的密度,f(y | do(x))的估计就很差。
由于 z. Image 中的缺口,作者对 f(y | do(x))的估计不准确。
同样,我们的数据生成过程讲述了这个故事。随着乐趣(z)的增加,*均分数(y)也增加,这是通过学习更多小时(x)和通过选择拥有更高的*均学科亲和力(u)的影响。但是通过条件反射,学习时间(x)被固定在接* 6 小时,很少看到快乐时间(z)在这个范围的高端。忽略这部分分布会撕裂干预分布右侧的相当大的一部分,使得 6 小时学习的*均成绩看起来比它应该的要差。
我在时间资源上过滤的时候扔掉了很多数据(w);可用的样本大小从 200k 到 15k。后来,当过滤得到接* 6 的小时数(x)时,样本量下降到只有 640。由于观察时间(x)的机制是它们不能超过时间资源(w),例如,如果我们将时间资源固定为接* 6.6,由于天花板效应,我们将有更多的数据。虽然在过滤时间资源(w)时,最初的 200k 样本现在减少到只有 12k,但过滤接* 6 的小时数(x)仍然留下超过 2900 个观察值。这足以估计我们的 18 个享受(z)桶中的每一个的条件等级密度。(请注意,随着乐趣的增加,它们如何向右移动,表明*均分数更高。)
有足够的数据来估计每个 z 桶的 f(y|w,z,x,s = 1)。图片作者。
得到的介入分布的估计更好,尽管不是完美的:
f(y | do(x))的较好估计,仍然包含一些偏差。图片作者。
如果将样本量增加 200 倍(!)和 3 倍的 z 桶数量,您会得到一个非常接*的*似值。然而,这一事件仍不完美。
本文对 f(y | do(x))的最佳估计,尽管样本量很大,但仍不完美。图片作者。
关于宁滨偏见的说明
对于这种第一原则方法,我称我的介入分布估计“足够好”然而,我必须诚实地说,即使拥有无限增加样本量的能力,我也无法消除所有的偏差(我尝试了一段时间)。
当我研究具有主体亲和力(u)的数据片段时(一个假设的分析师无法获得的变量),即使对于相对较小的快乐值(z),接*其下限 1 的 u 值也是罕见的。由于 z 与 u 成二次方增加,因此它相对于 u 快速增加。变量 z 和 u 在最小的 z 桶中紧密协变,在所有实际目的中,存在 1–1 关系(回想一下,z 的结构方程没有附加噪声)。然而,当 z 在其最小的桶中仅覆盖其范围的 3%时,u 覆盖其范围的全部 25%。尽管增加了样本大小和 z 桶的数量,我不认为我曾经完全克服了这一点。或许在 z 方程中加入噪声会有所帮助。
寻找偏差的其他地方是学习时间(x)和时间资源(z)桶的容差,但由于这些变量必须经过对数变换才能产生影响,因此小的偏差影响较小。
最后的想法
我很想看看 Jung,Tian 和 Bareinboim 的加权分布方法是如何处理这个数据生成过程的。我将数据分类的方法是幼稚的,并且丢弃了宝贵的信息。另一方面,如果数据在一个地区不可用,它们就不存在,支持天真的一点是,这是(痛苦地)明显的。
当我第一次开始写这篇文章时,我打算使用 RFCDE 包的基于树的条件密度估计器。我转向分桶只是为了调试我自己的实现错误,但是后来我欣赏了分桶和单变量内核密度估计的清晰性,并继续使用它。我想回去用 RFCDE 进行实验,但我担心更复杂的条件密度估计器会让我不知道数据对于手头的任务来说是不够的,并且会得到一个非常有偏差的介入分布估计,而不知道有什么地方出错了。
也许有一种非参数贝叶斯方法,允许研究人员以有原则的方式“填充”分布差距。
我们的一个变量在等式的调节端,但没有被边缘化,这导致了一个不寻常的情况。因为这个变量可以固定在任何值,所以将这些估计值汇总起来是有意义的。然而,我们看到,当这个条件变量被设置为特定值时,核密度估计是没有希望的。也许一个更复杂的算法将只估计它能可靠估计的那部分介入分布,并在最后组合那些部分估计。
我原本计划在这个例子中引入评分曲线,但意识到这会使情况变得非常复杂,因为现在学生中存在博弈论干扰。我不知道如何扩展图表以适应评分曲线。
尽管我对微积分很着迷,但很难知道如何在实践中接*它。我可以预见未来,它将构成推理的框架,而各种分布假设将提供实体,这样你就不需要任何可以想象的超立方体中的数据。也许只是等待合适的软件,或者“杀手级应用”,一切都有意义。在那之前,孩子们,继续学习准备考试,不要听我的。
不同的神经网络学习的东西是一样的吗?
在 TensorFlow 中实现论文并对其进行注释
你有没有过一个数据集,问:这个模型学习的东西和那个模型不一样?这是阮等人的疑问。艾尔。在他们的论文“广泛的和深度的网络学习同样的东西吗?”[1].
概观
在这篇文章的其余部分,我引用报纸上的话
深度神经网络体系结构通常通过调整其宽度和/或深度来适应可用的计算资源。值得注意的是,这种简单的模型缩放方法可以为高资源和低资源体系带来最先进的网络(Tan & Le,2019)。
并在下面写下我的解释。
我将重点放在这篇 24 页论文的核心部分:建立网络和数据集,计算 CKA,并创建图表。这也是这篇文章的大纲:我们从一个简短的介绍开始,然后建立网络。然后我解释 CKA 分数背后的方程式。最后,我们编写代码,从纸上创建可视化。
介绍
我们应用 CKA (centered kernel alignment)来测量不同神经网络架构的隐藏表示的相似性,发现宽或深模型中的表示呈现出一种特征结构,我们称之为块结构。我们研究了块结构如何在不同的训练运行中变化,并揭示了块结构和模型过度参数化之间的联系——块结构主要出现在过度参数化的模型中。
基于后面显示的代码,我们可以通过比较两个模型之间的激活来可视化这样的块结构:
两个 ResNet50s 的激活比较。颜色越亮表示相似度越高。由作者创作,基于[1]。
实验装置
[……]我们的实验装置由一个 ResNets 家族组成(何等,2016;Zagoruyko & Komodakis,2016)在标准影像分类数据集 CIFAR-10、CIFAR-100 和 ImageNet 上进行训练。
让我们把它写成代码。为了保持计算的可行性,我关注三个 resnet[2]:50、101 和 152;和 CIFAR-10 数据集[3]。
ResNet50
最小的 ResNet 可以很容易地实例化:我们从 Keras 下载模型,并加载在 ImageNet 上预先训练的权重[7]。我们将输入形状设置为(32,32,3),这是 CIFAR-10 数据集中一幅图像的形状。我们不需要完全连接的输出层,所以我们将 include_top 设置为 False 。最后,我们想要汇集输出层,这是一个 4D 张量,从我们的基本模型得到一个 2D 输出。由于我们的数据集中有 10 个类,所以我们包含了自己的密集输出层,其中有 10 个神经元和 softmax 激活。
最后,我们通过使用基础模型的输入作为输入,使用外部密集层作为输出来创建模型实例:
ResNet101
对于较大的 ResNet101,过程类似:
ResNet152
ResNet152 也类似:
CIFAR-10 数据集
获取 CIFAR-10 数据集非常方便,因为它很容易从 TensorFlow 获得。我们下载训练和测试数据集,并将像素值从0–255重新调整到0–1.0,使它们成为浮点:
测量相似性
我们使用线性中心核对齐(Kornblith 等人,2019;Cortes et al .,2012)来度量神经网络隐藏表示之间的相似性。
如何衡量两个神经网络之间的相似性?一种方法是计算(隐藏)层的输出之间的相似性。
从概念上讲,这很简单:你获取一个数据批次,而不是仅仅捕获最终的输出,你还可以捕获任何隐藏层的输出。对两个网络都这样做,现在有两组激活:一组来自网络 A,一组来自网络 B
然后,对 A 的激活和 b 的激活进行两两比较,基本上就这样了。
但是,对于这样的相似性度量有一些要求:首先,它的范围必须在 0 和 1 之间,0 表示两个完全不同的激活(不相似),1 表示两个相同的激活(相似)。此外,它必须处理不同形状的激活。
这个度量是(线性)居中的内核对齐[4][5]:
为了减少内存消耗,我们将 CKA 计算为对 k 个迷你批次计算的*均 HSIC 分数的函数:
其中,xᵢ∈r^(n×p₁)和 yᵢ∈r^(n×p₂)是包含两层激活的矩阵,一层具有 p₁神经元,另一层具有 p₂神经元,这两层激活是对相同的 n 个样本的小批量无替换采样。
在我解释这个等式之前,我们需要知道 HSIC 是什么:
我们使用的无偏估计量(宋等人,2012 年),因此的值与批量无关:
其中~K 和~L 是通过将 K 和 L 的对角线项设置为零而获得的。
好了,让我们详细讨论这些等式,从 CKA 分数所基于的 HSIC₁开始:
HSIC
HSIC 是希尔伯特-施密德独立性标准[6]的缩写,它衡量两个分布之间的统计独立性。在我们的例子中,这些分布就是激活。
让我们再看一遍[6]中的等式,但这次要突出重要的部分:
第一个框标记了 tr()操作符,它是矩阵的轨迹的缩写。矩阵的迹是主对角线上元素的和。我们计算轨迹的矩阵是修正的 K 和 L 之间的点积。
现在,为什么矩阵用粗体书写?来表明它们是矩阵;这是一个标准惯例。这也导致了第二个标记块,即 1 :这是一个长度为 n 的向量,填充了 1(也称为单位向量)。
为什么我们知道它是向量还是矩阵?看最后标记的部分,1^t. t 表示我们调换了它左边的东西。现在,如果我们有一个正方形矩阵(n,n),只填充 1,转置操作将没有任何意义(因为转置的正方形单位矩阵等于原始矩阵)。
最后,如何知道 1 向量的长度?这是由矩阵 K 和 L 的维数决定的,它们是方阵。它们是方形的,因为我们称 HSIC 为
在这里,我们计算原始矩阵与其转置后的第一个和第二个参数之间的点积。矩阵级的点积等于这两个矩阵的乘积。(在阅读了[4]之后,我决定坚持使用点积,他们在第 3 节中使用了这个命名约定)。由于我们将形状(m,k)的矩阵与其形状(k,m)的转置形式相乘,我们得到形状(m,m)的方阵。
我们使用通过在测试数据集上迭代 10 次获得的大小为 n = 256 的小批,在每个时期内采样而不替换。
这表明我们的矩阵以后将具有的维度:我们首先有一个形状(256,a* b * c *……)的激活,然后它变成(256,256)——一个正方形矩阵。
在代码级别,这会产生以下结果:
我们现在已经涵盖了 HSIC,我们可以继续与 CKA:
CKA
CKA 是居中内核对齐[5]的缩写,它将 HSIC 归一化:
我们分数的命名者是一组激活的单个 HSICs 的总和。这是σ,它涵盖了所有单独的激活。这是
我们将 CKA 计算为在 k 个迷你批次上计算的*均 HSIC 分数的函数
简单地说,我们取几个批次(大小为 256),分别捕获模型 A 和模型 B 的激活。对于每个批次,我们现在都有来自模型 A 和模型 B 的激活,我们用它们来计算 CKA 分数。
分母是归一化因子。
尽管 CKA 中的 C 代表居中,但我没有发现[1]中提到的居中过程。抬头一看,我发现
这类似于在计算 HSIC 分数的最后部分中使用的居中过程。
当在 python 中实现这一点时,对来自 CIFAR-10 数据集的一批 256 幅图像的计算需要一个小时。因此,我通过仅使用一个批次来减少计算量,去掉σ:
结果
我们通过调查模型架构的深度和宽度如何影响其内部表示结构来开始我们的研究。在不同的架构中,表示是如何通过隐藏层发展的?不同的隐藏图层表示彼此之间有多相似?为了回答这些问题,我们使用 3.1 节中概述的 CKA 表示相似性度量。
我们发现,随着网络变得更宽和/或更深,它们的表示显示出一种特征性的块结构:许多(几乎)连续的隐藏层具有高度相似的表示。
让我们将它写入代码并验证:
我们首先在 CIFAR-10 数据集上训练我们的结果。为了加快计算速度,我们使用 TPU 和 256:
上面使用的策略对象来自 TensorFlow,管理 TPU 培训。一个历元需要大约 8 秒钟,由于本文/博客的重点不是高精度分数,我们将历元的数量设置为 10。
我们同样训练其他人:
对于较大的模型来说,这些时期大约需要 15 到 20 秒。
现在我们需要一些代码来计算两个激活之间的相似性。
我们首先展*除第一个通道之外的所有通道,这将形状(n,a,b,c)的矩阵转换为形状(n,abc)的矩阵。在此过程中不会丢失任何信息。在重塑激活之后,我们可以计算它们的 CKA 分数。为了节省内存,我们随后删除它们,只返回分数:
这段代码需要两次激活,但是我们如何首先激活它们呢?为此,我们可以使用 Keras 后端:
我们编写了一个短函数,它接受一个模型,并返回一个函数,给定一个数据批次,该函数返回每个中间层的输出:
我们可以将结果可视化为热图,x 轴和 y 轴代表网络的图层,从输入层到输出层。
以下函数采用两个模型和一个数据批次。我们使用前面的方法来获取数据批次的所有隐藏激活。然后,我们为热图创建一个占位符:
热图开始时显示类似棋盘的表示相似性结构,这是因为残差连接后的表示比 ResNet 块内的表示更类似于其他残差后的表示。随着模型变得更宽或更深,我们看到一个独特的块结构的出现——相当大范围的隐藏层具有非常高的表示相似性(在热图上看起来像一个黄色的正方形)。这种块结构大多出现在网络的较后层(最后两级)。
基于前面的函数,我们可以通过调用
然后使用 pyplot 来可视化这个矩阵:
将 ResNet50 的激活与 ResNet101 的激活进行比较需要大约一个小时:
将 ResNet50 的激活与 ResNet101 的激活进行比较。
检查这个图像,我们看到开始提到的块结构,模型的前几层似乎学习非常相似的内部表示。随着层的增加,这变得更加多样化:垂直线(来自 ResNet101 的层的激活)与来自较小的 ResNet 的激活完全不同。
当将较小的 ResNet 与 ResNet152(在 Colab 上耗时 2 小时)进行比较时,这种空白变得更加明显:
比较 ResNet50 和 ResNet 152。
我们可以再次看到早期地层中的小块状构造。值得注意的是,大 ResNet152 的前 100 至 150 层似乎与小得多的模型的前 20 层非常相似。这可能表示过度参数化:
即使我们将参数数量从最小的 ResNet 的 25 636 712 增加到 ResNet152 的 60 419 944,但这些附加层似乎不会导致不同的表示。
[请记住,我的论证有几个缺点:由于资源有限,我只能计算一个批次的激活量,并且只能计算几个网络的激活量。鼓励读者使用我的代码,并用更多的批处理运行它]
视觉上明显的是下层的棋盘状图案。
就是这样。该代码可作为 Colab 笔记本在这里获得。
摘要
我们讨论了论文的核心部分广泛的网络和深度的网络能学到同样的东西吗?。我们从设置三个 ResNets 开始,并准备了 CIFAR-10 数据集。然后,我们研究了本文背后的等式,并用 python 实现了它们。在最后一部分,我们编写了几个简短的函数来比较两个网络的激活,使我们能够创建类似于论文中的图形。
从这里去哪里?
有几个开放点:
- 实施激活的 PCA(第 5.1 节,共[1])
- 随着更多计算机训练更多的网络
- 使用更多批次来*均相似性
参考
[1]阮,等,广网和深网学的东西一样吗?揭示神经网络表示如何随宽度和深度变化 (2020),arXiv
[2] K .何, et。艾尔。, 用于图像识别的深度残差学习 (2015),IEEE 计算机视觉与模式识别会议论文集
[3] A .克里热夫斯基, et。艾尔。,从微小图像中学习多层特征 (2009),技术报告
[4] S. Kornblith 等。艾尔。,再谈神经网络表示的相似性 (2019),机器学习国际会议
[5] C .科尔特斯,等。艾尔。,基于中心对齐的学习核算法 (2012),机器学习研究杂志
[6] L .宋, et。艾尔。,依赖最大化的特征选择 (2012),机器学习研究杂志
[7] J. Deng 等,Imagenet: 一个大规模的层次化图像数据库 (2009),IEEE 计算机视觉与模式识别会议
如果你想要更大的价值,你的框架需要复杂性吗?
简单性、复杂性和有效性的权衡
名称又能代表什么呢
什么是“框架”?按照韦氏词典的定义,它是“(关于思想的)一个基本概念结构”。基本的和概念性的。没有比这更模糊的了。然而,如果你从事数据或技术工作,你无疑会使用或参考数十或数百个框架。通常被发明来简化复杂的概念或问题领域,框架也提供指导方针或边界,并影响在其中开发和迭代的意识形态和模式。
框架是导航决策、教学和创建结构的有用工具。它们可能非常复杂或者非常明显,但是在这些框架的复杂性和通过实现或者意识到一个给定的框架可以实现的价值之间有直接的关联吗?
数据科学
在编程的上下文中,框架是一种抽象,其中提供通用功能的软件可以被附加的用户编写的代码选择性地改变,从而提供专用软件。它提供了一种构建和部署应用程序的标准方法,是一种通用的、可重用的软件环境,提供特定的功能作为更大软件*台的一部分,以促进软件应用程序、产品和解决方案的开发。”
作为一名日常工作中的数据科学家,您可能至少使用过其中一种软件框架,如 Scikit-Learn、TensorFlow、Pandas 或 PyTorch。从完整的端到端*台到 Python 等语言的抽象或扩展,所有这些都是为了使某些东西更容易访问、扩展或重复。
根据不同的用例,经验较少的数据科学家可能无法想象没有这些框架的工作。
复杂但有价值。
数据治理
如果数据治理属于您的权限范围,那么您要么继承了前任的框架,要么更有可能负责创建一个。您将考虑组织内的角色、流程或规则、数据观察或完整性检查,以及法规遵从性需求,作为该框架关注的许多领域中的一部分。
尽管这并不确定,但开发一个复杂而详细的数据治理框架通常是所有这些努力的结果。
毕竟,您的数据治理框架在确保与推动组织前进的数据相关联的质量、完整性、安全性和读写能力方面负有重大责任。
复杂但有价值。
决策和领导
自 1999 年由前 IBM'er Dave Snowden 创建以来,Cynefin 框架已经被应用于多个领域,包括高管培训、项目管理和软件开发等。如果你不熟悉,Cynefin 框架被描述为一个有意义的框架,其中给定的情况或挑战可以被分为五个领域之一。
简单或显而易见的领域包括没有辩论或提问余地的情况。有些人可能会将这些情况归类为已知、熟悉、可重复和可预测的“常识”情况。例如,产品 A 的销售下降,因此整体销售下降。
复杂领域虽然也是可预测的,但比简单领域的一对一因果关系更复杂。这些是已知但不熟悉的情况,专家可能会进行分析,然后确定因果关系。这里的例子是产品 A 的销售下降了。在额外分析前后看到这种模式后,数据团队确定了推动 80%负产品 A delta 的三个关键市场。
该框架的另外两个象限包括不可预测的情况。复杂的情况是不熟悉的,只有在事后才能感知。没有历史先例,因果关系也完全未知。例如,产品 A 的销售下降,数据团队在数据中看不到趋势。回顾我们的例子,我们可以确定网站上的产品页面有一个 bug,因此禁止了产品 A 的销售。第四象限,混乱的情况,需要在有限或没有范围的情况下立即采取行动。一些用户的网站关闭了。我们不知道为什么,针对什么用户,或者对销售会有什么影响。当务之急是让网站重新上线,把这种情况推回到复杂的领域。
Cynefin 框架象限都围绕着第五个领域,无序,在这一领域,情况要么在被分类之前,要么在四个象限之间移动。
此时,您可能已经对 Cynefin 有了清晰的理解,或者,您可能已经重读了几个段落,因为您想到了真实世界的应用程序。我们已经做了一个简单的分解,从根本上说,是一个模糊而简单的框架。然而,正是这种模糊性导致了普遍的应用,这也是 Cynefin 框架的实用性和复杂性的最终来源。
简单的,复杂的,复杂的,混乱的,有价值的。
只是简单有价值?
无论是在数据科学、数据治理还是领导力领域,正如这里所示,我们每天使用的大多数框架要么非常复杂,要么简单到足以变得复杂。但是,一个框架是否需要成为我们从中获取价值的工具?
例如,在“每个新分析师应该关注的 4 个可怕的简单领域”中,我描述了一个我创建的框架,以帮助分析师(或任何其他人)在加入新组织时快速获得他们的数据方位。它并不详细,但提供了如何通过“我如何理解数据?”进行思考的清晰理解
你发现哪些简单的框架最有价值?这里有一条走钢丝,将一个构造简化到标准化的程度,即“框架”,但不要创建过于精炼以至于在应用中变得复杂的东西。也许在我们开发下一套伟大的标准、流程或框架时,我们应该记住这一点?
简单又有价值。
参考
—框架—https://www.merriam-webster.com/dictionary/framework
—软件框架—https://en.wikipedia.org/wiki/Software_framework
"我必须拿到硕士学位才能进入数据科学领域吗?"
办公时间
进入数据科学领域时,直面常见问题
快速背景
我毕业的时候,数据科学还不像今天这样流行。我的许多同龄人要么去了咨询、银行、医药或其他一些传统的行业工作。我没有那么多去大科技的朋友。
我的许多同事都从事过分析性很强的工作:比如商业分析师、运营、营销、非营利等。他们很多人都玩过 Excel,有的甚至自学了 SQL 和 Python。因此,他们的技能自然会转化为数据科学角色。
现在,在行业中工作了几年后,他们中的许多人希望进入初创公司或大型科技公司的“数据科学”岗位。
我不怪他们。
技术领域的数据科学职位比他们以前的职位工资高得多。他们的工资至少可以增加 50-100%,如果不是更多的话(我知道工资是禁忌,但特别是如果你在美国试图进入旧金山的一个角色,我们不要欺骗自己)。
事实上,我自己也经历了这个过程,从四大会计师事务所的顾问到 FAANG 的数据科学家,这一切都发生在 COVID 发生的时候(这是另一个故事)。当我开始阅读来自博客、文章等的建议时。关于如何突破,我被吓到了。
“我需要硕士学位”
“我需要重新开始我的职业生涯”
“在我的 GitHub 投资组合中,我需要一个充满绿色方框的配置文件”
这是一个善意的建议,但对我的具体情况来说,完全无关紧要,如果有的话,阻止了我采取行动。
谢天谢地,我克服了这些恐惧,最终找到了现在的工作,据我所知,这份工作给我带来了 510%的加薪。让我与你分享我在整个旅程中学到的东西。
这是给谁的
如果你想进入数据科学的专业领域(例如,计算机视觉、自然语言处理、数据工程、研究科学),这不适合你。
这是专门为那些希望以数据科学的角色进入技术领域(大型技术公司、初创公司或任何介于两者之间的公司)的分析师而写的,基本上,这些人就像我大学时代的同龄人一样。
当我申请数据科学领域的职位时,这些是我最常见的担忧。我也问过我的朋友,他们也有同样的担忧。
出于隐私考虑,我匿名引用了以下内容。
1.我需要硕士或博士学位才能在数据科学领域找到工作
路易斯·基根在 Unsplash 上的照片
“至于提高(我的)候选资格,我上次看到的大多数角色都需要更多经验。你认为分析硕士会有帮助吗?”—尼尚特
“我还得再考一个硕士吗?!"—景瑞
反应
你需要知道你申请的是什么类型的数据科学职位,以及你来自什么类型的职位。除非是与研究相关的职位,否则几乎不需要硕士学位。如果你已经在某种程度上担任了使用分析的角色,那么除非你想做研究,否则你只需要温习一下技术技能就可以进入了。
你可能会看到一些要求或倾向于硕士或博士的非研究数据科学职位,但这不是一个严格的要求。见鬼,我得到了一个数据科学家的职位,上面明确写着“硕士或以上”,但我只有学士学位(见问题 2)。
否则,如果你目前的角色与分析无关,那么硕士可以是许多选择之一。
然而,我的建议是找一个与分析相关的职位,这个职位可能是也可能不是你的理想职位。它不仅比硕士学位便宜(在写这篇文章的时候,硕士学位一年要花费 4 万到 7 万美元),你还会获得宝贵的经验,这比你的学术背景更受雇主的重视。
2.我没有这个经验
蒂姆·范德奎普在 Unsplash 上拍摄的照片
“有时感觉就像一个第 22 条军规,似乎很多公司都想要有经验的候选人,但如果没有先获得一个好职位,就很难获得经验。”— Abi
“公司对试图进入数据科学领域的候选人期望过高。此外,入门级数据科学职位的招聘信息非常少。”—萨尚克
反应
如果你已经在行业中工作了一段时间,找出你已经掌握的可以转移到数据科学或分析职位的技能。请记住,职位描述更像是愿望清单,而不是硬性要求。
例如,我想从一家大型科技公司的项目经理转为数据科学家。以下是该职位的一些要求:
- 应用统计方法,开发 A/B 测试,并自动化数据管道以开发指标
- 与工程团队合作改进数据收集
- 计算机科学、统计学、数学或其他相关分析学科的硕士或以上学位。
这是我简历上的内容:
- 在 6 个客户群中设计和 A/B 测试显示广告,将营销转换率从 1.5%提高到 3.2%,降低营销成本 49.2%
- 构建了一个自动化的 Python 管道,根据封面周数和销售周数等指标来提醒哪些零售店需要更多库存
- 数学学士学位
尽管我没有达到他们要求的所有“严格”要求(我只有有学士学位),但我能够申请这样的职位,获得最后一轮面试,并得到这个特定的职位,使我的收入增加了 510%(这不是打印错误,我的收入增加了 5.1 倍)!
3.我需要来自一所好学校
反应
不,你不知道。不像咨询或金融行业,好的学校很受重视,尤其是对大型科技公司来说,他们更关心你的技能和经验,而不是你毕业于哪所学校。
关于体验,见问题 2。
4.不知道 Python,SQL,R,Spark,Hadoop,Tensorflow,或者【此处插入技术】
卢克·切瑟在 Unsplash 上拍摄的照片
反应
专注于你最了解的一项技能,并在面试中使用它。如果你知道不止一种语言(比如 SQL 和 Python),确保你在面试中一直使用同一种语言。
如果你不具备工作描述中的任何技能,那就投身其中,从那里开始。
我刚开始的时候,一点编程语言都不懂。然而,作为一名项目经理,我在业余时间自学了 Python(想想 Datacamp、Dataquest、Udacity),甚至让我的老板参与我自己的几个 Python 项目。
我把自己放在那里,为自己创造了一个精通 Python 的机会。现在,我可以更容易地申请工作。你也可以做同样的事情!
开始寻找让你在当前工作中提高技能的方法。如果你找不到这些机会,寻找另一个能让你找到的角色。
5.我没有收到公司的回复
照片由JESHOOTS.COM在 Unsplash 上拍摄
“有太多的通信延迟。他们至少要花两天时间才能给我回复……这个过程很慢,真的很漫长。老实说,如果他们只是提前告诉我时间表是什么样子,(如果他们告诉我)我被拒绝了,我就可以把注意力转移到其他地方。”—范
反应
在你开始发送简历之前,确保你已经做了功课。与你感兴趣的职位和/或公司的数据科学家交谈。理解他们为什么要招聘这样的角色。这样,当你开始和招聘人员交谈时,他们会觉得你知道自己在说什么。
还有,作为这里的 PSA,请对他们好一点。许多招聘人员甚至不是全职员工,每周工作 50-60 小时,周末也工作。想象一下,收到这位“执着”(更像是令人讨厌)的求职者发来的 10 封跟进邮件。可能不会很顺利。
这两种方式都会增加你得到回复的机会,但是并不是每个人都会回复你。说到底,还是一个数字的游戏。也许你给 10 个招聘人员发邮件,可能只有一两个会回复你。
也许你和招聘人员之间没有很好的化学反应,或者也许这个职位已经有人了,但是他们没有通知你。记住,他们很忙!不要把所有的希望和梦想都放在一家公司。向前看。
6.我需要认识公司内部的人才能得到面试机会
“我用 LinkedIn 邀请他们喝咖啡聊天,但仍然有隔阂,因为(这是)陌生人之间的事。通过你的校友或过去的工作建立关系网真的很有帮助,(尤其是当[他们的]团队或组织有职位空缺时)。”—翡翠
反应
也许吧。这里的简短回答是,你应该利用你拥有的任何关系,但它们不会向你保证任何事情。无论你是通过冷漠的电子邮件还是热情的联系得到它,你仍然需要向面试官证明你自己。
事实上,我参加了最后几轮面试,却不认识那个团队或部门的任何人。
当我申请一个业务开发团队的数据策略师职位时,已经是面试过程的最后阶段了。我联系了这个职位的招聘人员,当我打电话给他时,我们真的很合得来。
他对我的技能印象深刻,以至于他特意为我简化了流程,跳过了技术筛选,并说服招聘经理应该就这个角色与我进行最后一轮面试。
我最终没有得到这个职位,但这里的教训是,不一定是你认识谁,而是你如何对待他们。
请记住,我本人并不认识招聘人员,也没有秘密的后门关系。我与招聘人员建立了融洽的关系,向他展示了为什么我是一个好的候选人,并礼貌地跟进了他。
7.我太老了,不能换了
“我觉得我不再像大学时那样有优势了。我周围有那么多高成就的人,但现在我觉得自己在慢慢堕落。”费城
“我知道我需要去(更好的地方)。但由于我必须支持我的丈夫,我觉得现在换工作有风险。”—拉基莎
反应
做出改变真的很难,即使你内心深处知道这是你想要走的职业道路。当然,如果你对你的伴侣和/或家庭有承诺,那么这些也需要得到照顾。
话虽如此,开始新的东西永远不会太晚。特别是在一个有如此多新发展的领域,总会有一个地方让你在职业生涯中成长,并且仍然能够支持那些依赖你的人。
为了使这种转变更容易,我建议与最*转入数据科学并处于与你相似位置的人交谈,并与他们联系。举例来说,如果你是一个正在寻找转变的新婚夫妇,找到其他新婚夫妇不仅有助于你更好地与他们联系,还能在与新婚有关的更多个人问题上给你实用的建议。
这就对了。根据我自己的求职经验,希望我已经能够解决您在过渡到数据科学时的一些主要问题。
如果你想了解更多关于我的背景故事,以及我是如何在数据科学领域获得梦想角色的,请点击这里订阅我的时事通讯。
编辑:修正了一些格式问题和错别字。
我需要一个学位才能在数据科学领域找到工作吗?
探索数据科学求职时学位的必要性
这个问题的简单答案是否定的!有许多从事数据科学工作的人没有学术证书来证明他们的角色——我就是其中之一。然而,随着时间的推移,我反对学士学位、硕士学位和博士学位计划的严格立场颇有意思地软化了。
嗯,算是吧。我现在并不觉得它们完全没有用……我仍然相信,在某种程度上,关注某人的实际工作经验胜过他们的教育,即使我们看看科技领域一些最伟大的成就者,他们中的许多人都是辍学生——马克·扎克伯格、比尔·盖茨和史蒂夫·乔布斯,仅举几例。
但我并不是建议所有希望在数据科学领域谋得一席之地的大学生都应该退学。获得学位有许多好处,例如,众所周知,大学毕业生一生中比未毕业的人挣得更多,此外,你还可以获得纪律等基本好处,如果你选择了技术学位(科学、技术、工程、数学),你将在数据科学的一个关键领域拥有深厚的基础知识。
“相反,把你在教育中的时间看作是建立一个有吸引力的网络的机会,这个网络将在你离开后为你做好适当的准备。”
本质上,成为一名数据科学家需要很好的编程、数学、统计和概率知识,以及对业务部门的理解。如果你能够有效地展示你有这些能力,那么它很可能会战胜你可以吹嘘的大多数证书。因此,要积极寻找作为数据科学家的第一份工作,请考虑发展以下领域:
编程技巧
数据科学最常用的编程语言是 Python 和 r。为了开始探索数据,您必须至少学习其中一种工具。
我的 LinkedIn Feed 上一篇有争议的帖子(来源 : 亚历山大·弗雷伯格帖子)
数据科学家在应该选择哪种编程语言的问题上意见不一——有些人对另一种语言的厌恶并不那么圆滑。我尝试了这两种语言,发现 Python 更容易学习,效率也更高。它还比数据科学有更多的用途,如果你打算在其他领域培养技能,数据科学会有所帮助。
数学、统计和概率
简单地说“图像有可能是一只猫”不如“图像有 80%的可能是一只猫”有说服力。这是一个相当枯燥的例子,但是你应该使用统计学和概率来分析和解释你得到的任何数据。
此外,许多用于数据建模的算法需要很好地理解数学中的不同主题,如线性代数和微积分(以及统计和概率)。它有助于理解在您的算法下发生了什么,以用于调试目的,以及在审计事件中或如果客户想知道他们为什么被拒绝贷款时的可解释性。
项目组合
数据被高度视为“新的石油”,地球上的每个人都在产生大量数据,这一观点在考虑数据科学时非常重要。数据科学家必须从生成的大量复杂数据中提取数据,并突出对业务有益的点,因此需要领域专业知识(这可以是数据科学家或团队中的某个人)。
无论如何,你必须能够证明你能够完成你所说的所有任务,所以有一个令人信服的投资组合来检查各种问题和这些问题的解决方案是有帮助的。
建立工作关系网
古老的格言“你的网络就是你的净值”在数据科学中比以往任何时候都更加突出。说到底,这个领域是新的,没有很多标准来区分优秀的从业者,除非你雇佣他们并发现——这意味着投资的巨大风险。因此,在获得第一个数据科学职位时,有人可以为你担保是非常有用的。
此外,将自己融入数据科学文化是开发和学习数据科学新知识的好地方,也能让你体验到找到第一份工作的感觉。
包裹
我个人认为,当你决定寻找数据科学家的角色时,你是否有学位并不重要。最终,招聘公司认为他们有一个包括数据在内的问题,他们需要一个精通数据的人来帮助他们更深入地研究这个问题,以便公司能够发展——这并没有说需要学位。然而,为了有效地完成数据科学家的工作,你必须具备一些必要的技能,但互联网已经使在线学习许多(如果不是全部)这些技能成为可能,而不必负债。
感谢阅读!
通过 LinkedIn 和 Twitter 与我联系,了解最新动态。
其他最*帖子:
我需要博士学位才能在量子机器学习领域找到工作吗?
我关于量子计算下一个挑战的两个量子
量子机器学习要不要入门?看看 动手量子机器学习用 Python 。
作者图片
上周,我和量子机器学习的同学们进行了两次有趣的对话。有趣的是,他们都有一个主要的担忧:“ 我在学习量子机器学习方面的努力会被认可吗?我需要一个正式的证书甚至是博士学位才能在量子机器学习领域找到工作吗? ”
当然,如果说我知道在量子机器学习领域找到一份工作需要什么样的正式资格,那就太冒昧了。毕竟我不是这方面的招聘人员。
所以,我能给的只有我的两个量子。
“我的两美分”是“我的两便士价值”的简写这是一种表达个人观点的方式,并表示它只值两便士。虽然我喜欢这种仅用两美分来评估观点的谦逊方式,但它在量子计算的背景下是不合适的。因为一个量子(复数量子)是任何物理属性的最小可能离散单位,我相信“我的两个量子”是对我的观点的更好的度量。
巧合的是,我最*读了一篇文章,揭示了量子计算公司难以找到合适的候选人,以及他们为什么要找博士。了解雇主真正想要的是什么是你在找工作时的优势。
注:引文格式的段落直接或间接取自那篇文章。
“雇主需要拥有跨学科技能的量子员工:除了量子物理学背景之外,还必须具备数据分析、工程、建模或编程等方面的经验。这种专业化水*并不常见;事实上,它大多存在于博士阶段。”
如果我们仔细阅读这段引文,它澄清了一个普遍的误解。(好的)雇主不会为了一个正式的资格证书而去寻找它。他们需要一个能胜任这项工作的人。正式资格是一种手段,因为他们发现这与优秀的候选人相关。他们真正需要的是精通一套特定跨学科技能的候选人。
学士和硕士课程通常专注于某一学科,如物理、数学或计算机科学。但是我们还没有那些教授所需混合的项目。但是作为一个博士,你通常会定义自己的具体范围。雇主寻找的是你为自己设定的范围。它不是一张写有你名字的纸。
因此,重要的问题不是你是否有博士学位,而是你是否能在正确的技能组合中展示这种专长。
“找到拥有合适技能组合的人是最大的挑战,”剑桥量子公司量子软件主管罗斯·邓肯告诉 ZDNet 。“在我们雇佣的人中,只有极少数人在进门时就准备好开始工作了。”
作为一个对进入这个领域感兴趣的人,你能做的最好的事情就是发展这些技能。所以,第一步是弄清楚你需要的技能。然后,做一个自我评估。哪些技能你已经具备,哪些技能你还欠缺?最后,你需要填补空白。
这在理论上听起来很简单,但在实践中却非常棘手。而且,我甚至没有提到实际的学习。真正棘手的部分是如何证明你填补了空缺。这又回到了正规教育。现在不仅大学提供文凭。几乎所有在线课程都提供一些认证。
但是如果你更喜欢读一本好的旧书呢?也许,你甚至读了一大堆书。你可能在学习上投入了很多时间。但是你不会拿到毕业证或者证书。那么,这是否意味着你所有的努力对于找到一份工作来说毫无价值?
我完全能理解你渴望一个正式的证书来证明你的能力。但是如果我们仔细想想,我真的怀疑正式证书能证明什么。相信在没有真正掌握各自领域的情况下,你也能拿到很多证书。
所以,我们再从招聘者的角度来看一下。招聘人员正在寻找一个在跨学科领域具有非凡能力的人。她知道自己需求的特殊性。她要找的东西没有蓝图。因此,她必须单独评估每个候选人。当然,她可能会马上拒绝那些完全没有受过教育的候选人。但是如果你有大学的计算机科学学位,我认为你有资格被进一步考虑。
不要误解我。进一步考虑,我的意思不是你已经得到了那份工作。我都不说她会邀请你面试。但是,我想说她会检查你的简历,看看你能提供什么。
现在,你需要一些突出的东西。这是你展示自己专业技能的机会。但我怀疑一份证书清单能解决问题。
相反,我会遵循一个简单的规则:
秀,不要说。
最好,你能证明你不仅点击了在线课程,而且你能够应用你的知识。
拥有一份其他学科的作品集是很好的实践,比如软件开发或数据科学。它展示了你做过的项目。理想的是,这些都是真实的案例。但是量子机器学习是一个新的领域。所以,你应该可以展示你是如何在开源数据集和典型问题上应用你的知识的。
作品集是你可信地展示你如何填补了正规教育中的空白的机会。此外,除了正式的证书,它还提供了与招聘人员建立信任的途径。
请记住,量子机器学习是一个新兴领域。还没有典型的资质。这不仅是应聘者的问题,也是招聘人员的问题。作为唯一资格的替代,招聘人员要求候选人具备下一个最好的条件——博士
所以,我的建议是不要被过于具体的正式要求吓倒。还有,也不要沉迷于证书。相反,确保你掌握了要求的技能。以任何适合你个人学习方式的方式来做这件事。但是,一定要展示你所学到的东西。
据估计,到 2030 年,量子计算产业将成为一个 650 亿美元的市场
如果这一预测接*事实,对技能的需求将超过对正式头衔的需求。
随着企业在不久的将来开始争夺大量合格的求职者,现在可能是开始学习的好时机。
量子机器学习要不要入门?看看 动手量子机器学习用 Python 。
在这里免费获得前三章。
不要让大量的数据科学家让你觉得聚会迟到了
有并将会有足够的空间。
布鲁克·拉克在 Unsplash 拍摄的照片
数据科学是在短时间内经历了巨大普及的事物之一。这是炒作的一个显著特征。有些人甚至认为数据科学是一场等待可怕结局的炒作。
我想先陈述我的观点,这样你就知道接下来的文章会发生什么了。数据科学绝对不是炒作。它已经存在了很长时间,但换了一种不同的服装。
人们,或者至少是科学家,总是知道数据的重要性。弗洛伦斯·南丁格尔的图表就是一个杰出的例子,她被描述为统计学图表化的先驱。她创建了一个数据可视化,展示了从 1854 年到 1856 年英国军队的死亡原因。
我们对数据科学或更广义的数据的了解比 10 年前多得多。主要原因是我们比以往任何时候都更有能力管理数据。更准确地说,收集、处理、传输和分析大量数据变得极其容易。
我的数据科学之旅始于 2019 年初。随着我越来越多地参与数据科学社区,我觉得我开始学习数据科学已经晚了。起初,我认为我没有足够的时间来缩小与我的数据科学家同事的差距并找到一份工作。
周围有大量的数据科学家、分析师和机器学习工程师。我也在积极关注空缺职位,这些职业的需求量很大。
如果你在大学里上过经济学入门课,你就会知道供需曲线。如果不是,你仍然可以理解它,因为它是科学中最符合逻辑和最直观的概念之一。
对于商品和服务来说,如果供大于求,价格就会下降。在我们这里,供给是数据科学家的数量,价格是一个数据科学家的价值。当然,需求是由需要它们的企业产生的。
我听说对数据科学家的需求已经接*饱和点。在“若干”年后,我们将不再需要那么多数据科学家。
尽管有这样的担心,我还是坚持走下去。我成为数据科学家的动机和热情帮助我克服了我的担忧。此外,随着我了解更多,我的旅程变成了一条更令人兴奋的道路。
我没有让大量的数据科学家让我觉得聚会迟到了。
我很高兴我没有让这种事情发生。我踏入这个领域已经快两年半了。数据科学生态系统对职业的需求一直在不断增加。
如果你是这个领域的新手,或者打算开始学习数据科学,你可能会有同样的担忧。你可能会看到许多“数据人”,既有经验丰富的,也有新来的。关于未来对数据科学家需求减少的传言可能会让你感到更加担忧。
在我看来,对数据科学家的需求还会继续增加,至少在可预见的未来是如此。
数据科学并不专注于某一特定领域。事实上,它可以应用于我们可以收集数据的任何行业的任何业务。由于收集和处理数据的能力变得极其容易,数据科学的范围可能会涵盖更广泛的应用。
数据科学生态系统中使用的软件工具和软件包的数量也在增加。这些工具加快并简化了操作工作。例如,您可以在几行代码中应用 10 种不同的算法和各种不同的设置。
我认为这些工具有可能导致误解。是的,它们使操作工作更容易。因此,一些人认为我们不需要像现在这样多的数据科学家。然而,事实绝对不是这样。
首先,一个数据科学家的角色不仅限于应用机器学习算法。如果我们把一个数据科学产品看成一个大饼,机器学习部分只是一个切片。此外,数据科学解决的一些问题不需要机器学习算法。
剩下的部分和机器学习部分一样重要。例如,数据科学家需要首先确定问题的框架。然后,定义数据要求。我们不能仅仅使用任何数据来解决问题。数据科学家的工作是决定哪种数据或特征将提供更多信息或更有价值。
原始数据并不总是最合适的格式。数据科学家处理原始数据,使其更有用。它们衍生出新的特性,有可能提供更多的洞察力。
这篇文章的重点不是数据科学家做什么。我只想强调,软件工具和软件包不能取代数据科学家。它们通过简化操作工作来支持“数据人员”。
最后的想法
如果你打算成为一名数据科学家,你应该做好努力学习的准备。找到第一份工作并不容易。这需要奉献、时间和努力。
让你第一份工作难找的不是你身边的数据科学家数量多。将它们视为数据科学普及的标志。不要让他们打击你。
我觉得难的是领域的特点。数据科学是一个跨学科领域,涵盖一系列技能。但是,只要有足够的努力和动力,成为一名数据科学家是可以实现的目标。
感谢您的阅读。如果您有任何反馈,请告诉我。
不要试图说服你的老板批准你的数据科学项目
办公时间
我给那些想动手的初级分析师的建议是:不要请求许可。不要试图说服。去做吧。
当我们有一个想在工作场所推行的想法时,不要试图说服你的上司批准它。
我们是分析师,研究员。我们的意图不是说服,而是通过数据来展示清晰。
通过清晰,我们允许我们的同伴看到我们所看到的,甚至更多。这是我们的分析思维在我们的编码和项目管理活动之外显现的地方。
作为数据人员,我们有机会影响我们的伙伴,无论是我们一起工作的公司还是我们的队友,不仅通过我们的努力工作,而且特别是通过在日常生活中成为批判性的思考者。
1.不要推销——演示
不要让人们相信任何事情。给他们看看。
收集支持你假设的数据和证据,并围绕这些数据和证据进行叙述。你的努力应该足以获得认可,而不需要向要求任何东西。
证明你的想法能够并且会产生影响,如果允许它成长开花的话。清楚地表明而不是实施你的想法将是一个错误。会让他们赔钱的错误。确保你的叙述至少涵盖以下几点。
-详细描述
如果你不能在第一时间传达你想做的事情,你收集证据的努力可能是无效的。要彻底。慢慢来。仔细想想歌词和节奏。这是你把事实摆在你的观众面前并描述你的证据的地方。
-预期结果
让你的数据通过你的统计分析说话。使用测试,比如 t-test 或任何你能想到的方法,为你的假设提供坚实的基础。如果可能,用模拟记录预期结果。
-成本
这是你老板/投资者注意力最集中的地方。对数字要超级清楚。
-人们
清楚你计划参与的人,以及他们的期望承诺。你不希望从团队 A 中引入没有有效替代他们专业技能的人。这往往是拒绝的理由。
-时间
用几张幻灯片展示路线图和截止日期。放下一张江恩图,不要害怕提出你自己的时间表:它在任何情况下都不会被遵循。
2.制作演示
这是许多人的不足之处。你必须给那些有权批准或拒绝你的提议的人留下深刻印象。
你通过成为一个伟大的讲故事者来做到这一点。带玩具来玩。有了可用的演示,我相信产生影响的几率会更高。
作为一名在顶级数字营销公司工作的数据科学家,我尝试了各种方法,并通过以下方法取得了良好的结果:
模型+ FastAPI /烧瓶+ Streamlit
训练您的模型并使用 FastAPI 创建您的端点。FastAPI 的超酷之处在于它会自动为您创建最先进的文档页面。另外,它非常容易配置。而且非常快。你可以在这里阅读更多关于 FastAPI 的信息。
Streamlit 允许数据科学家和分析师立即用 Python 创建漂亮的界面。有许多组件可供选择——极限是你的创造力。
有了 FastAPI 和 Streamlit,如果你讲的故事切中要害,你就能真正打动你的听众。
模型+ Excel
Excel 是一个非常被低估和讨厌的工具。出于很好的理由,我可以补充一点(在某些情况下非常慢,实际上无法使用)。
也就是说,设置一个小的 VBA 脚本来直接从工作表访问端点是相当容易的。设置您的函数,使特定的单元格包含输入并完成。您的模型现在提供了一个 Excel 表,非编码人员也可以使用。
我经常通过允许非技术用户使用他们经常使用的工具(如 Excel)来获得更好的结果,从而创造出一种“哇”的效果。
型号+ PowerBI
业务分析师每天都在利用 PowerBI、Tableau 和 Qlik 等工具。我个人有使用 PowerBI 的经验,根据您的利益相关者和其他条件,它绝对是模型部署的一个选择。
3.展现你的个性
布雷特·乔丹在 Unsplash 上的照片
我相信你 80%的表现不是通过数据表现出来的,而是通过你的性格和个性。
展示你的分析时,不要害怕大胆和自信。如果你做好了功课,所有将要出现的问题都会很容易回答。如果不是这样,那就诚实地说你现在不能回复,你会通过电子邮件或在稍后的对话中跟进。
一旦你掌握了在数据中找到确凿证据的诀窍,你就会更容易及时关注自己和自己的态度,而不是结果本身。他们会用自己的声音说话,给你空间来提高你的沟通技巧。
关于这一点,我想推荐一本书,它教会了我很多关于向我的读者传达数据概念的知识——用数据讲故事Cole Nuss baumer Knaflic 著。这是一本任何分析师和沟通者都应该读的不可思议的书。
4.交朋友
认为你的想法很酷的人会更酷。利用他们在公司的职位、角色和专业知识来增加你被认可的机会。这没什么不对的,因为这样做你不仅对自己有利,也可能对此刻支持你的所有人有利。
让他们在演示过程中提出具体问题。这不是欺骗:你可以使用这些干预措施在叙述中从一个点转移到另一个点,并可以引发围绕热点话题的讨论。理解这些主题是很重要的,所以确保你在做这些之前已经做了适当的准备。
您的解决方案是为他们量身打造的,对吗?记住,整个公司都应该从批准你的想法中受益。
5.利用您已经拥有的数据
你的研究不应该完全是外来的(在这种情况下,是在你的办公室之外)。利用来自你的公司、客户或同事的数据,在网上做研究之前使用这些数据。
例如,我经常通过使用谷歌表格收集内部对某个想法的看法。我只是问我的同事几个问题,帮助我了解我是否在正确的轨道上。
如果是的话,我可以在我的演讲中使用一些内部验证。不然我就支点一下,从另一个角度看我的想法能不能有价值。如果不是这样,我将干脆放弃这个想法。这就引出了下一点,也是最后一点。
6.不要执着于你的想法
照片由 Trym Nilsen 在 Unsplash 上拍摄
是的。我知道你认为你的想法是你的小孩,你应该喂养它,培养它。
然而,大自然是无情的。而这只会发生在你的脑海里。我自己已经放弃了最初让我晚上几个小时保持清醒的几个想法。
放弃那些被拒绝的想法,,但仅仅是因为记录在案的原因。如果怀疑你的想法是坏的,在你的空闲时间继续努力,并在另一个环境中探索假设。你对一个想法培养得越多,你就越有机会让它在正确的环境下开花结果。然而,这不应该让我们对现实视而不见。
如果这个想法因为好的理由被拒绝,那么就让它去吧。不要依赖他们,因为在你的职业生涯中,你会失去他们中的许多人。你越早接受你的大多数想法都是坏的,你就能越早开始围绕那个罕见的伟大想法创造伟大的东西。
不要再在 Python 中使用 Print 进行调试了
Python 中用于调试的改进的“打印”函数
Python 中最常用的函数是什么?嗯,可能在大多数编程语言中,它必须是print()
函数。我相信大多数像我一样的开发人员会在开发过程中多次使用它将消息打印到控制台中。
当然,没有替代方案可以完全取代print()
功能。然而,当我们出于调试目的想要输出一些东西时,肯定有更好的方法。在本文中,我将介绍一个非常有趣的 Python 第三方库,名为“Ice Cream”。它可以为快速简单的调试提供很多便利。
一个坏例子
图片来自pix abayKrzysztof Pluta
先说一个相对不好的例子。假设我们已经定义了一个函数,我们想看看它是否像预期的那样工作。
def square_of(num):
return num*num
这个函数只是返回作为参数传入的数字的*方。我们可能需要多次测试,如下所示。
print(square_of(2))
print(square_of(3))
print(square_of(4))
现在这样还可以。然而,在实践中我们会有更多的代码行。此外,可能有许多print()
函数将不同的东西打印到输出区域。在这种情况下,有时我们可能会搞不清楚哪个输出是由哪个print()
函数生成的。
因此,给print()
函数的内容添加一些简短的描述来提醒我们它是关于什么的是一种很好的方式。
print('square of 2:', square_of(2))
print('square of 3:', square_of(3))
print('square of 4:', square_of(4))
现在好多了,但是每次都这样太累了。此外,当我们完成开发时,很可能不得不删除大多数调试打印。
基本用法—检查变量
让我们看看冰淇淋图书馆。它如何解决上面提到的问题?
首先,我们需要使用pip
从 PyPI 存储库中安装它。
pip install icecream
然后,我们按如下方式导入库。
from icecream import ic
现在,我们可以用它来打印任何我们想打印的调试信息。
调用函数
我们可以直接使用冰淇淋打印功能,就像我们之前使用print()
功能所做的一样。
ic(square_of(2))
ic(square_of(3))
ic(square_of(4))
太好了!我们从不在ic()
函数中指定任何东西,但是它会自动输出函数名和参数以及结果。因此,我们不必再手动添加“简要描述”了。
查阅字典
不仅仅是调用一个函数,Ice Cream 还可以输出所有便于调试的详细内容,比如访问字典的键值对。
my_dict = {
'name': 'Chris',
'age': 33
}ic(my_dict['name'])
在这个例子中,我定义了一个字典,并试图从它的键中访问其中的一个值。冰淇淋输出了字典的变量名和我正在访问的键。
访问对象的属性
再举一个例子,让我们定义一个类并从中实例化一个对象。
class Dog():
num_legs = 4
tail = Truedog = Dog()
现在,让我们用冰淇淋来输出它的一个属性。
ic(dog.tail)
在 If 条件下调试
图片来自 Pixabay 的 silviarita
冰激凌库不仅在检查变量时有用,在控制语句(如 if 条件)中也很有用。比如,我们写一个简单的 if-else 条件如下。
input = 'Chris'if input == 'Chris':
ic()
else:
ic()
我们只是把冰激凌函数放在 if 和 else 块中,看看会发生什么。
虽然 if-else 语句此刻什么都不做,但是ic()
函数仍然告诉我们它在何时何地被调用过,以及行号。
顺便说一句,我用 Python 笔记本做这个演示。如果这是运行在一个”。py”文件,它还会告诉我们调用它的文件名。
让我们考虑一个更实际的用法如下。
def check_user(username):
if username == 'Chris':
# do something
ic()
else:
# do something else
ic()check_user('Chris')
check_user('Jade')
该功能将为不同的用户做一些事情。出于调试目的,我们总是想知道哪个是当前用户。然后,ic()
函数会一直告诉我们。
插入到现有代码中
在我看来,冰淇淋图书馆的这一酷功能需要得到强调。也就是说,ic()
函数不仅会输出详细信息,还会传递值,以便它可以是任何内容的包装。换句话说,我们可以把ic()
函数放到代码中的任何地方,而不会影响它。
让我们继续使用我们在上一节中定义的sqaure_of()
函数。
num = 2square_of_num = square_of(ic(num))
在这个例子中,假设我们有一个变量num
,我们想计算它的*方。我把ic()
函数放在变量num
之外,而不是square_of(num)
。因此,变量num
的值被打印,分配给square_of_num
的结果不会受到影响。
我们可以如下测试结果。
if ic(square_of_num) == pow(num, 2):
ic('Correct!')
因此,square_of_num
等于变量num
的*方。还有,在这个 if-condition 中,我也在不影响用途的情况下使用了ic()
函数,但是打印变量square_of_num
是为了调试!
禁用冰淇淋
使用print()
函数进行调试的最大问题之一是它们太多了。当我们完成开发时,到处都有它们是很常见的。那么,如果我们想清理代码来删除它们,那将是一场灾难。
如果我们正在使用冰淇淋库进行调试,我们需要做的就是简单地禁用它。
ic.disable()
之后,所有的ic()
功能将停止输出任何东西。例如,下面的代码将不输出任何内容。
你可能会问,变量square_of_num
呢?如果我们禁用了冰淇淋功能,它还会通过吗?别担心,禁用功能只会禁用输出,我们不需要担心任何其他功能。
if ic(square_of_num) == pow(num, 2):
print('Correct!')
如果我们把输出变回print()
函数,它仍然可以输出。这意味着ic(square_of_num)
仍然相当于square_of_num
。
当然,如果我们想回到调试模式,冰淇淋可以重新启用。
ic.enable()
定制冰淇淋输出
冰淇淋也可以根据产量进行定制。最常用的定制是改变前缀。您可能已经注意到,默认输出总是带有前缀ic |
。是的,我们可以定制。
例如,我们可以将其更改为Debug |
,这对其调试目的更有意义。
ic.configureOutput(prefix='Debug | ')
ic('test')
事实上,前缀也可以设置为函数,而不是静态字符串。例如,让我们定义一个以格式化字符串返回当前时间戳的函数。
from datetime import datetimedef now():
return f'[{datetime.now()}] '
然后,我们可以将该函数设置为冰淇淋前缀。
ic.configureOutput(prefix=now)
ic('test')
摘要
图片来自 Pixabay 的 Seksak Kerdkanno
在本文中,我介绍了一个很棒的 Python 第三方库,叫做“冰激凌”。它用详细输出增强了 Python 的常规print()
功能。因此,它使得调试非常方便。
冰激凌库永远不会取代print()
函数,因为它是为调试目的而设计的。此外,这也不意味着要替换日志记录系统。在我看来,它介于这两者之间。快来看看,试试吧!
https://medium.com/@qiuyujx/membership
如果你觉得我的文章有帮助,请考虑加入 Medium 会员来支持我和成千上万的其他作者!(点击上面的链接)
不要将 Python 列表用于任何目的
图片来自 Pixabay
用 itertools 实现 iterables 的 5 个 Python 技巧
列表类型无疑是 Python 中最流行的容器对象类型。在某种程度上,它甚至可以被认为是 Python 的“灵魂”,因为它有许多 Python 的标志性语法,比如切片和列表理解。
但是,它太方便了,很多开发者都习惯了。通常,除了 List,Python 中还有许多类型的 iterables 和容器类型。
在本文中,我将介绍 Python 内置的“Itertools”模块中的 5 个技巧。它使我们能够以 5 种奇特的方式操纵任何 Python 可迭代对象。
本文中的所有函数都来自 itertools 模块。因此,所有示例代码都假设函数已经从模块中导入,如下所示。
from itertools import *
1.链-组合任何容器
假设你有两个列表,你想一个接一个的循环。最直观的解决方案可能是将它们组合成一个列表,并将该列表放入一个 for 循环中。
这里有两个带有名称的示例列表。假设我们想打印这些列表中的所有名字。
my_list_1 = ['Alice', 'Bob', 'Chris']
my_list_2 = ['David', 'Emily','Frank']for name in my_list_1 + my_list_2:
print('Name: ', name)
当然,这样做是没有问题的。它又快又简单。更重要的是,可读性很强。但是,如果我们在不同类型的容器中有这些名称,那该怎么办呢?Python 中典型的可重复项可能包括
- 列表
my_list = [‘Alice’, ‘Bob’]
- 元组
my_tuple = (‘Chris’, ‘David’)
- 字典
my_dictionary = {‘name’: ‘Emily’}
- 设定
my_set = {‘Frank’, ‘Grace’}
- 发电机
my_generator = (name for name in [‘Helen’, ‘Isaac’])
是不是应该把所有的“非列表”对象都转换成列表,然后再组合起来?答案当然是没有,没有必要。我们可以简单地使用 itertools 模块中的chain()
函数。
my_chain = chain(
my_list,
my_tuple,
my_dictionary.values(),
my_set,
my_generator
)for name in my_chain:
print('Name: ', name)
2.星图——对每个元素应用函数
你有没有想过将一个函数应用到一个容器中的所有对象,并在一个容器中得到结果?starmap()
函数可以帮助我们实现这一点。
为了简化演示,让我们使用一个现有的函数pow
,它只是计算一个数的幂。我们将函数名作为第一个参数,将 iterable 作为第二个参数。
result = starmap(pow, [(2,2), (3,2), (4,2), (5,2)])
如果我们想使用定制功能呢?只需定义它并将函数名作为第一个参数传递。如果函数足够简单,您还可以定义一个匿名函数,如下所示。
result = starmap(
lambda x, y: pow(x, y),
[(2,2), (3,2), (4,2), (5,2)]
)
3.压缩—使用遮罩过滤可重复项
图片由 Klittika Suwanjaruen 从 Pixabay 拍摄
如果您曾经使用 Pandas 来转换一些数据帧,您一定知道我们可以传递一个具有真或假值的“系列”作为“掩码”来过滤另一个系列或数据帧。
事实上,我们可以使用compress()
函数在任何 iterables 上复制这个特性。请参见下面的示例。
compress(
['Alice', 'Bob', 'Chris', 'David'],
[True, False, True, False]
)
第一个列表包含 4 个名称,第二个列表包含 4 个布尔值。第二个列表将用于过滤第一个列表,以便只保留具有“真”值的位置。
我们可以一起使用多个 itertools 函数来实现一些更好的东西。假设我们有一些名字和句子。
names = ['Alice', 'Chris']
sentences = ['I am Chris', 'Alice and Chris', 'I am Alice']
现在,我们要找出句子包含名字的所有“名字,句子”对。
我们可以首先使用product()
函数来生成所有的可能性。
product(names, sentences)
然后,我们可以为compress()
函数生成“掩码”。我们想定义一个函数来测试每一对。所以,我们可以在这里使用starmap()
函数。
matched_mask = starmap(
lambda name, sentence: (name in sentence),
product(names, sentences)
)
最后,我们可以使用compress()
函数来过滤由product()
函数生成的列表。
compress(product(names, sentences), matched_mask)
4.Dropwhile、Takewhile 和 Filterfalse —过滤具有特定行为的可重复项
图片来自 Pixabay
基于某些条件进行筛选相对容易。但是,有时候条件并不是全部因素。例如,还需要考虑 iterable 中元素的位置和顺序。
假设我每天每 3 个小时检查一次自己的当前状态。
checkpoints = {
'00:00': 'sleeping',
'03:00': 'sleeping',
'06:00': 'wake up',
'09:00': 'working',
'12:00': 'sleeping',
'15:00': 'working',
'18:00': 'eating',
'21:00': 'entertaining',
'23:00': 'sleeping'
}
现在,我想在这一天生成一个日历。包括我醒来之前的状态没有意义。所以,排除睡眠状态会更好。然而,我在午餐时间睡了一会儿,我不想排除它。
通常,要实现这一点需要多个条件。但是使用dropwhile()
函数,我们可以简单地做以下事情。
my_calendar = dropwhile(
lambda item: item[1] == 'sleeping',
checkpoints.items()
)
我们只需要一个条件,即测试状态是否为“睡眠”。如果是,就“放弃”它。函数dropwhile()
的特点是,一旦它遇到第一个不满足条件的元素,它将停止在 iterable 中丢弃任何东西。换句话说,即使更多的元素满足条件,它们也不会被丢弃。
如果我们想放弃所有的“睡眠”状态呢?我们可以使用filterfalse()
功能。
my_calendar = filterfalse(
lambda item: item[1] == 'sleeping',
checkpoints.items()
)
与dropwhile()
函数相反,takewhile()
函数将保留满足谓词的任何元素。然而,由于第一个元素不满足谓词,所有其余的都将被删除。
my_calendar = takewhile(
lambda item: item[1] == 'sleeping',
checkpoints.items()
)
takewhile()
功能可视为dropwhile()
功能的反义词。
5.is lice——用步骤对可重复项切片
Python 列表可以很容易地切片。这是最 Pythonic 化的语法之一。假设我们有一个如下的列表。
my_list = ['a', 'b', 'c', 'd', 'e', 'f']
如果我们可以得到从第三个开始并在第五个停止的元素,我们可以做以下事情。
my_list[2:5]
使用 Itertools 中的islice()
函数,我们可以再实现一个特性,这就是步骤。
islice()
函数有 4 个参数。第一个是 iterable,其余的是开始索引、停止索引和步骤。
如果我们想得到前两个元素:
islice(my_list, 2) # equivalent to my_list[0:2]
如果我们想得到 2 号到 5 号的商品:
islice(my_list, 2, 5) # equivalent to my_list[2:5]
到目前为止,没有意外。但是,islice()
功能也支持“步骤”。
islice(my_list, 0, None, 2)
从头到尾每第二个项目得到。
摘要
在本文中,我介绍了 Python 中 Itertools 模块的 5 个技巧。事实上,所有的 iterables 都非常方便,并且有它们有趣的行为。我们可能不需要一直使用 List。
itertools 帮助我们以许多奇特的方式操纵 Python 可迭代对象。本文不能包含 Itertools 模块的所有函数。
https://medium.com/@qiuyujx/membership
如果你觉得我的文章有帮助,请考虑加入灵媒会员来支持我和成千上万的其他作家!(点击上面的链接)
除非您知道所有这些要点,否则不要使用 Python Pickle
泡菜电子监管的利与弊,以及我们应该何时使用它
与大多数其他流行的编程语言相比,Python 可能拥有最灵活的对象序列化。在 Python 中,一切都是对象,所以我们可以说几乎一切都可以序列化。是的,我说的模块是泡菜。
然而,与 JSON 等其他“常规”序列化方法相比,Pickle 有更多方面需要我们在使用时小心谨慎。这就是标题所说的,除非你知道这些事实,否则不要用泡菜。
在这篇文章中,我将整理一些关于 Pickle 的重要笔记,希望能有所帮助。
1.基本用法
通过使用 Python Pickle 模块,我们可以轻松地将几乎所有类型的对象序列化到一个文件中。在我们使用它之前,它需要被导入。
import pickle
让我们以字典为例。
my_dict = {
'name': 'Chris',
'age': 33
}
我们可以使用方法pickle.dump()
将字典序列化并写入文件。
with open('my_dict.pickle', 'wb') as f:
pickle.dump(my_dict, f)
然后,我们可以读取该文件并将其加载回一个变量。在那之后,我们就有了准确的字典。它们在内容上 100%相同。
with open('my_dict.pickle', 'rb') as f:
my_dict_unpickled = pickle.load(f)my_dict == my_dict_unpickled
2.为什么是泡菜?有哪些利弊?
图片来自 Pixabay 的 Christine Sponchia
事实上,如果我们在上面的例子中使用 JSON 序列化 Python 字典,会有更多的好处。泡菜电子监管通常有三个主要缺点。
缺点 1:泡菜不安全
不像 JSON,它只是一段字符串,有可能构造恶意 pickle 数据,这些数据将在拆包期间执行任意代码。
因此,我们应该永远不要解开可能来自不可信来源的数据,或者可能已经被篡改的数据。
缺点 2:泡菜不可读
将 Python 字典序列化为 JSON 字符串最重要的一点是结果是人类可读的。然而,对于 Pickle 文件来说,这是不正确的。这是我们刚刚腌制的字典的腌制文件。如果我们试图打开它作为一个文本文件,这就是我们将得到的。
缺点-3: Pickle 在 Python 中是有限的
pickle 对象只能使用 Python 加载。其他语言也可以这样做,但需要第三方库的参与,可能仍不完全支持。
相比之下,JSON 字符串在编程领域非常常用,并且受到大多数编程语言的良好支持。
泡菜的优点
Pickle 通过调用任意函数来构造任意 Python 对象,这就是它不安全的原因。然而,这使得它能够序列化 JSON 和其他序列化方法不能序列化的几乎所有 Python 对象。
解包一个对象通常不需要“样板”。因此,它非常适合快速简便的电子监管。例如,您可以将所有变量转储到 pickle 文件中并终止您的程序。稍后,您可以启动另一个 Python 会话,从序列化文件中恢复所有内容。因此,这使我们能够以更灵活的方式运行程序的一部分。
另一个例子是多线程。当我们使用多进程模块在多线程中运行程序时,我们可以轻松地将任意 Python 对象发送到其他进程或计算节点。
在这些场景中,安全性问题通常不适用,人类也不必读取这些对象。我们只需要快速、简单和兼容性。在这种情况下,泡菜可以被完美地利用。
3.还有什么可以腌制的?
嗯,我一直在说几乎所有能被泡菜连载的东西。现在,让我给你看一些例子。
取消一项功能
第一个例子是一个函数。是的,我们可以在 Python 中序列化一个函数,因为函数在 Python 中也是一个对象。
def my_func(num):
print(f'my function will add 1 to the number {num}')
return num + 1
只需定义一个简单的函数用于演示。现在,让我们把它放到一个新的变量中。
with open('my_func.pickle', 'wb') as f:
pickle.dump(my_func, f)with open('my_func.pickle', 'rb') as f:
my_func_unpickled = pickle.load(f)my_func_unpickled(10)
新变量可以用作函数,函数将与原始变量相同。
泡菜熊猫数据框
另一个例子是熊猫数据框。让我们定义一个熊猫数据框。
import pandas as pdmy_df = pd.DataFrame({
'name': ['Alice', 'Bob', 'Chris'],
'age': [25, 29, 33]
})
现在,我们可以把它放到一个新的变量中。新的数据帧将是相同的。
with open('my_df.pickle', 'wb') as f:
pickle.dump(my_df, f)with open('my_df.pickle', 'rb') as f:
my_df_unpickled = pickle.load(f)
请注意,Pandas 有内置的方法可以处理和取消处理数据帧。他们会做和上面一样的工作,但是代码会更干净。性能也是一样的。
那么,可能会有一个问题,为什么我们应该使用 Pickle 作为数据帧而不是 CSV?
第一个回答是速度。CSV 是人类可读的,但它几乎是存储熊猫数据帧最慢的方式。
这篇 SO 帖子对熊猫数据帧不同序列化方式的性能进行了基准测试。
酸洗 Pandas 数据帧的第二个好处是数据类型。当我们将数据框写入 CSV 文件时,所有内容都必须转换为文本。有时候,这样会给我们装回去带来一些不便或者麻烦。例如,如果我们将 datetime 列写入 CSV,我们可能需要在加载它时指定格式字符串。
然而,这个问题对于 pickle 对象来说并不存在。你腌制的东西,你保证装回去的时候会有完全一样的东西。不需要做其他任何事情。
4.Pickle 协议版本
像我在前面的例子中所做的那样使用 Pickle 是很常见的。他们没有错,但是如果能指定 Pickle 的协议版本(通常是最高的)就太好了。简单来说,泡菜系列有不同的版本。随着 Python 版本的迭代,Pickle 模块也在进化。
如果您对现有版本和改进之处感兴趣,这里有一个来自官方文档的列表。
协议版本 0 是原始的“人类可读”协议,向后兼容早期版本的 Python。
协议版本 1 是一种旧的二进制格式,也兼容早期版本的 Python。
协议版本 2 在 Python 2.3 中推出。它提供了一个更有效的新型类的酸洗。
协议版本 3 在 Python 3.0 中新增。它对字节对象有明确的支持,并且不能被 Python 2.x 取消拾取。这是 Python 3.0–3.7 中的默认协议。
协议版本 4 在 Python 3.4 中新增。它增加了对非常大的对象的支持,支持更多种类的对象,以及一些数据格式优化。从 Python 3.8 开始,这是默认协议。
协议版本 5 新增于 Python 3.8。它增加了对带外数据的支持和对带内数据的加速。
一般来说,在以下方面,高版本总是比低版本好
- 腌制物品的大小
- 拆线的性能
如果我们使用不同版本的 Pandas 数据帧,我们可以看到大小的差异。
with open('my_df_p4.pickle', 'wb') as f:
pickle.dump(my_df, f, protocol=4)with open('my_df_p3.pickle', 'wb') as f:
pickle.dump(my_df, f, protocol=3)with open('my_df_p2.pickle', 'wb') as f:
pickle.dump(my_df, f, protocol=2)with open('my_df_p1.pickle', 'wb') as f:
pickle.dump(my_df, f, protocol=1) import osprint('P4:', os.path.getsize('my_df_p4.pickle'))print('P3:', os.path.getsize('my_df_p3.pickle'))print('P2:', os.path.getsize('my_df_p2.pickle'))print('P1:', os.path.getsize('my_df_p1.pickle'))
为什么 Python 仍然保留旧版本而新版本总是更好?这是因为协议并不总是向后兼容的。这意味着,如果我们想要更好的兼容性,我们必须选择更低的版本。
然而,如果我们使用 pickle 对象而不需要向后兼容,我们可以使用枚举来保证我们的程序使用最新的(最好的)。例子如下。
pickle.dump(my_df, f, protocol=pickle.**HIGHEST_PROTOCOL**)
5.Pickle 自定义类
图片来自 Pixabay
尽管 pickle 支持 Python 中几乎所有的对象,但是当我们 Pickle 一个从自定义类实例化的对象时,我们仍然需要小心。简而言之,当我们加载回 pickled 对象时,该类需要已经存在。
例如,让我们定义一个简单的类“Person ”,它有两个属性和一个方法。
class Person:
def __init__(self, name, age):
self.name = name
self.age = agedef self_introduce(self):
print(f'My name is {self.name} and my age is {self.age}')p = Person('Chris', 33)
p.self_introduce()
现在,让我们用 Pickle 序列化对象“p”。
with open('person.pickle', 'wb') as f:
pickle.dump(p, f)
如果该类不存在,就会出现问题。如果我们试图在一个新的会话中加载 pickled 对象,并且没有定义该类,就会发生这种情况。我们可以通过删除类定义来模拟这个场景。
del Person
然后,如果我们试图加载回腌对象,将有一个异常。
with open('person.pickle', 'rb') as f:
p_unpickled = pickle.load(f)
因此,当我们加载回对象时,我们需要确保该类是存在的。然而,如果类的定义略有不同,可能不会导致问题,但是对象的行为可能会根据新的类定义而改变。
class Person:
def __init__(self, name, age):
self.name = name
self.age = agedef self_introduce(self):
print(f'(Modified) My name is {self.name} and my age is {self.age}')
在新的类定义中,我修改了自我介绍方法的打印消息。
然后,如果我们把腌好的对象加载回去,不会有任何错误,但是自我介绍的方法会和原来不一样。
with open('person.pickle', 'rb') as f:
p_unpickled = pickle.load(f)p_unpickled.self_introduce()
6.不是所有的物品都可以腌制
在这最后一节,我必须回到我最初的陈述“几乎所有的 Python 对象都可以被酸洗”。我使用“几乎所有”是因为还有一些类型的对象不能被 Pickle 序列化。
不能被酸洗的典型类型是活动连接对象,如网络或数据库连接。这是有道理的,因为 Pickle 关闭后将无法建立连接。这些对象只能使用正确的凭据和其他资源重新创建。
另一种需要提到的类型是模块。一个重要的模块也不能被酸洗。请参见下面的示例。
import datetimewith open('datetime.pickle', 'wb') as f:
pickle.dump(datetime, f)
了解这一点很重要,因为这意味着我们不能在global()
中处理所有的东西,因为导入的模块会在那里。
摘要
在本文中,我介绍了 Python 中的内置序列化方法——Pickle。它可用于快速简便的电子监管。它支持几乎所有类型的 Python 对象,例如函数,甚至熊猫数据框。当对不同版本的 Python 使用 Pickle 时,我们还需要记住,Pickle 的版本也可能不同。
https://medium.com/@qiuyujx/membership
如果你觉得我的文章有帮助,请考虑加入 Medium 会员来支持我和其他成千上万的作者!(点击上面的链接)
真正的人工智能挑战未来
不要担心,但要担心
左起:罗莎丽亚·西里波、保罗·塔马格尼尼和迭戈·阿里纳斯。(图片来自采访者和被采访者)
不久前,我的一位同事在一次重要会议上做了主题演讲后发表声明说,如今,关于人工智能(AI)[以及未来的相关挑战]的每一场演讲都需要至少是启示性的。是真的。到目前为止,大多数纪录片、节目、TED 演讲、主题演讲和类似的演示都明显使用了一种非常恐惧技术的语气。这有多少是真的?
我们真的注定要失败吗?我们应该向人工智能投降吗?
AI 会接管人类吗?
AI 是黑魔法吗?
一旦你将一个人工智能应用投入生产,你就不能再控制它了,这是真的吗?
有多少数据科学家能够控制人工智能应用?
我们已经向来自 KNIME 的人工智能专家 Rosaria Silipo 和来自圣安德鲁斯大学的 Diego Arenas 提出了这些问题。
Rosaria Silipo ,博士,现任 KNIME 首席数据科学家,在西门子、Viseca、Nuance Communications 和私人咨询公司从事应用人工智能、预测分析和机器学习工作超过 25 年。Rosaria 分享了她在广泛的行业和部署中的实践经验,包括物联网、客户智能、金融服务、社交媒体和网络安全,她撰写了 50 多份技术出版物,包括她的新书:“智能数据科学指南”(编辑。Springer)和“用 KNIME 进行无代码深度学习”(ed。Packt)。
Diego 是一名从事数据科学和数据工程项目的研究工程师,正在完成他的计算机科学博士学位。Diego 拥有 15 年以上的数据相关项目工作经验;他拥有数据科学硕士学位,曾在银行、金融、电信、零售、人力资源、教育、交通、制造等不同行业担任顾问和机器学习专家。Diego 是一个开源贡献者,也是一个数据爱好者。
【保罗】让我们从一个流行的问题开始。AI 像黑魔法吗?
[Rosaria] AI 是由一套算法和数据处理技术组成的。最终都是数学、统计和数据处理。所以,不。这不是黑魔法,这只是数学。它并不比我汽车的引擎更神奇,它使我的汽车从 A 地移动到 B 地,即使我不了解它的机械的所有细节。
像这样的比较对人工智能产生了一种非常恐惧技术的感觉,促使人们拒绝所有这一切。带着这种恐慌的态度,唯一的解决办法就是告别一切,从你的社交媒体到所有的 messenger 应用,从你的吸尘器到你的汽车,从微波炉到你的牙刷,放弃现代技术的所有好处。
对门外汉来说,看起来的确像是黑魔法,但正如罗莎丽亚所说,它不是。当你看到魔术时,你可能会认为有什么超自然的事情在发生。然而,一旦你仔细观察,你会发现对你迅速贴上标签的魔法有一个合乎逻辑的解释。
这同样适用于人工智能,人们可能会承认人工智能所不具备的能力,当你更仔细地看它时,最终可以用数学运算来解释,在 consort 中工作给人一种难以理解的印象。
人工智能应用能够从数据中学习。那么,一旦你将一个人工智能应用程序投入生产,你就不能再控制它了,这是真的吗?
[Diego]我们需要将人工智能应用视为助手。一个助手,可以做我们以前做的同样的工作,但更准确或更有效。这改变了执行任务的人。但是,仍然应该监视助手,以检查它是否偏离了结果。
你可以,也应该,控制部署,如果人工智能系统没有按预期运行,你可以拔掉插头。人工智能系统的设计和实现应该考虑到这一点。人工智能系统不应该执行伦理上有问题的任务,或者人类出于伦理原因不会执行的任务。
一旦你将一个人工智能应用程序投入生产,你会想看看它的表现如何。通常的情况是,你可以用更新的版本(用更*的数据训练)更新或替换你的 AI 系统,或者只是因为结果是错误的或不如预期的好而关闭 AI 系统。
另一件要考虑的事情是,一旦你开始处理你的人工智能应用程序给出的结果,你可能正在改变原始问题的性质。因此,有必要监控其性能,以测量任何重大偏差。
【Rosaria】很多 AI 应用都是基于机器学习和深度学习算法的。在这两种情况下,一个模型被训练来执行一个特定的任务:给出推荐、产生流失率、提供有针对性的广告、预测机械故障等等
是的,这种算法从数据中学习,它们的知识会随着新输入的数据定期或持续更新,甚至在它们的生产生命周期中也是如此。然而,从那里说你不能控制他们了,这一步有点太大了。他们受到控制:他们不断受到监控,以确保他们保持良好的表现。它们可以永远停止使用,也可以随时进行临时维护。
有多少数据科学家可以控制人工智能应用?
【Rosaria】数据科学家的职业资格是一个宽泛的规范:从负责整个数据架构、收集、存储和准备的数据工程师,到机器学习/深度学习工程师构建和培训模型;从业务分析师和领域专家,定义每个项目的规格并使用最终解决方案,到 IT 生产专家,实际上负责在真实世界的数据上执行和维护真实世界中的 AI 应用程序。所有这些人至少控制一个人工智能应用程序的一部分,如果不是全部的话。
根据一篇关于 KDNuggets 、的文章,2017 年 6 月,Kaggle 社区* http://blog.kaggle.com/2017/06/06/weve-passed-1-million-members/突破 100 万会员,ka ggle 2018 年 9 月 19 日的邮件称,他们在 2018 年 8 月突破 200 万会员。“考虑到主要是机器学习工程师属于 Kaggle 社区,并且这只是世界上所有机器学习工程师的一小部分,并且考虑到构建 AI 解决方案所涉及的大量其他资格,那么能够控制 AI 端解决方案的专业人士的数量肯定远远高于仅仅几个人。*
[Diego]有许多人在日常工作中专门研究特定类型的问题。这可能是某种数据科学或机器学习技术或算法。一个人工智能应用程序意味着几个不同的领域,所以这里的最佳策略是模块化开发和部署,并围绕专业人员团队工作。在我看来,能在 AI 中成功部署一个端到端解决方案的人,在公司里是稀缺的。通常是分析师自己学习新的技术和工具。
针对公司的工作量类型,拥有一个*衡的团队是关键。在角色和经验方面保持*衡。组建协作和有凝聚力的团队是公司分析团队生存的关键。
在人工智能领域工作的人和不熟悉该技术的人对人工智能的理解有区别吗?
[迭戈]是的,这是不同的。该领域的人员将知道针对特定问题使用什么技术。他们会明白,他们只是使用程序和算法来获得问题的*似解决方案。人工智能系统通常是不同机器学习模型的混合。
有时你可以看到不在这个领域但想用人工智能解决一切的人过于兴奋。这令人担忧。在提出任何解决方案之前,有责任首先了解技术并评估使用它的后果。关于人工智能的使用,我们应该在未来几年从专家和非专家那里看到更多的问责。
是的,当然。从事 AI 工作的人都知道,算法只是算法。不熟悉人工智能的人必须依靠泄露的纪录片可能说的话。你知道这种技术恐惧的启示方法无助于为如何解决真正问题的理性讨论做准备。
【Paolo】所以,你是说 AI 不会接管人类?
我真的不认为会这样。它可能会改变我们的生活方式,或好或坏,但我不认为任何算法接*发展某种意识或能够自行行动。我认为,在当今最先进的技术水*下,机器人和算法不会逃脱我们的控制。他们可能会取代乔布斯,但那是另一回事了。
[Diego]想象一下,我们有一个能够使用其他人工智能系统的人工智能系统,让我们称之为全局人工智能。给定一个任务,它能够选择正确的人工智能来解决它。这个全局人工智能能够使用和控制其他人工智能系统来执行不同的任务。现在想象一下,控制系统可以通过重新训练和/或重新适应新任务来改善其他人工智能系统和它自己。在某些时候,人工智能系统的能力可能会超过人类的*均智能。此后不久,它就能够在没有人类帮助或干预的情况下进行自我训练。接下来会发生什么?这是我们担心的情况。在关于这个话题的对话中,通常有两方,一方说这个场景不可能实现。另一方面,有人认为这是一个潜在的威胁,因此我们应该努力减轻这种情况下的任何意外后果。
如果你选择在这场对话中选择关心的一方,我们必须说,我们远远没有达到这种独立的水*。人工智能系统不太可能接管世界,但更紧迫的问题是强大人工智能系统的控制者。我们要求使用我们的数据并影响我们决策的算法和系统更加透明。
你谈到人类的工作被机器人取代。未来 AI 还有哪些真正的挑战?
[Rosaria]未来人工智能面临着许多挑战!
许多将与数据安全和隐私保护有关。所有这些数据都可用,所有我的数据都可用,谁可以访问它们?谁能用它们做什么?法律允许公司如何处理我的数据?法律允许其他公民查看我的数据吗?我的隐私得到足够的保护了吗?
出现数据泄露怎么办?有黑客攻击怎么办?我的数据是否受到足够的保护?我的敏感数据是否受到足够的保护?谁有我的数据,还能控制我?我怎样才能避免那种力量控制我?这些问题不仅仅是技术上的答案,还需要法律和政治上的答案。
人工智能面临的另一种挑战是责任。如果一个算法做了决策,谁来为错误的决策负责?从自动驾驶汽车到贷款转让,从医疗诊断到面部识别,谁将为错误负责?谁来为假新闻的传播负责?还是为了社交媒体上的“言论自由”?自由言论在哪里结束,攻击性言论在哪里开始?同样,在技术答案之前,这里需要政治和法律答案。
算法是基于数据的,数据是有偏见的,就像它来自的世界一样。我们应该努力纠正算法对某些种族群体的偏见,还是仅仅依靠数据?如果我们要做出努力,我们不是在干涉当前的世界体系吗?
人工智能将取代一些工作。不可否认,人工智能将对未来世界产生经济影响。我们需要让人们为过渡做好准备,最重要的是,我们需要让下一代做好更多与人工智能兼容的工作的准备。
[迭戈]我同意罗莎丽亚。
对我来说,一些巨大的挑战围绕着公*、问责、透明和道德。像任何工具一样,人工智能可以用来增加或减少世界上的不*等。使用模型来提出建议或影响人们生活中的决策,给使用人工智能系统进行自动决策的公司带来了巨大的责任,我们没有看到许多公司致力于对他们的机器学习模型或人工智能系统进行道德评估。
我们可以期待更多的人工智能系统和项目将透明度作为开发过程的一部分。包括向外行用户解释人工智能系统所做决定的系统组件。
数据代理是另一个挑战,用户拥有多少自己的数据。他们能够在多大程度上分享和了解他们自己的数据所产生的见解。
如果人工智能将接管一些重复性工作是真的,那么政府应该努力重新培训劳动力,以改善人们的生活。希望技术和人工智能系统的受控和透明使用能够对世界产生积极影响。
所以,你在说“不要担心,但要担心”。
[罗萨莉亚][大笑]是的,这正是我所说的。
人工智能对未来提出了挑战。我们现在需要解决这些问题,以便在进入一个更加自动化和人工智能驱动的世界时尽可能少出现问题。处理这些问题需要更多的法律和政治决策,而不是技术发展。
与此同时,我们需要保持理性,公*地评估技术的利弊,而不是用世界末日的术语来描述人工智能,因为它比实际情况更复杂,更不可控。
[Diego]我们需要解决许多令人担忧的问题,例如,改善人工智能系统的立法,规范企业问责制,以及总体上建立一个更加公*和透明的系统。这是人工智能应用令人兴奋的时代,我认为在梦想人工智能控制的世界之前,我们应该专注于解决我们更直接的问题。
所以,是的。不要担心,但要担心。
幸福长寿:
国会中更多的女性就是答案吗?
克里斯蒂安·纽曼在 Unsplash 上的照片
简介
在国会中有更多女性的国家,人们会活得更长久、更幸福吗?几乎在所有存在某种形式政府的国家中,政治职位或议会或国会中的职位历来都是由男性主导的。然而,这些年来,一些国家的议会中女性人数稳步上升。
这是我对数据进行统计分析的结果报告,旨在测试议会中有更多女性的国家是否有更高的预期寿命和更高的幸福指数。那么到底什么是幸福指数呢?
“请想象一个梯子,梯级从底部的 0 到顶部的 10。梯子的顶端代表你可能过的最好的生活,梯子的底端代表你可能过的最坏的生活。你个人认为你现在站在梯子的哪一级?”这一指标也被称为 Cantril life ladder,是如何收集被调查国家每位受访者的幸福得分的。
分析了由每个国家每年*均预期寿命组成的数据集。这项研究中使用的数据收集时间跨度从 1800 年的预期寿命数据开始,到 2005 年的幸福得分,范围一直到 2019/2020 年。我从 Gapminder 获取所有数据。
方法论
在这项研究中,我总共包括了全球 160 个国家,每个国家都有每年收集的女性在议会中的比例、预期寿命和幸福指数的*均值。
为了便于分析,数据按国家分组,然后在收集数据的所有年份计算所有三个值的*均值、预期寿命、女性在议会中所占的百分比和幸福指数,这样每个国家就有了三个*均值。
创建了两个附加特征来帮助分析;这些新特征被用来将幸福指数和议会中女性的比例分为高或低幸福指数,或高或低议会中女性的比例。
最后,根据女议员比例高的国家和女议员比例低的国家,将数据分成两部分。然后计算预期寿命和幸福得分的*均值,并在双样本 t 检验中进行比较,以查看*均值的差异是否在 0.05 的α显著性水*上具有统计学显著性
议会中的妇女和预期寿命
结果显示,女议员比例较高的国家的*均预期寿命约为73.1 岁,而女议员比例较低的国家的*均预期寿命为69.7 岁。我运行了一个双样本 t 检验来看看这个差异是否具有统计显著性——不等于零(替代假设),或者它是否不具有统计显著性——等于零(零假设)。
图(1) 点击此处查看上图 的互动版。上面的小提琴图在左边显示了妇女在议会中高水*的国家,在右边显示了妇女在议会中低水*的国家。内部方框图显示,议会中妇女比例较高的国家一般*均预期寿命较高。
双样本 t 检验结果(*均寿命)
t 检验的 p 值为 0.0097 ,因此我拒绝零假设,并得出结论,议会中女性比例高的国家与议会中女性比例低的国家之间的预期寿命均值差异不等于零,在 0.05 的阿尔法显著性水*上具有统计显著性。因此,人们可以得出结论,最有可能的情况是,议会中女性比例较高的国家*均寿命也更长。
图(2) 点击此处查看互动版上图 。该条形图显示了议会中女性比例(以百分比表示)最高的前 20 个国家。令人惊讶的是,卢旺达以* 55%的比例高居榜首,但该国也是排名靠前的国家中的异数,其*均预期寿命约为 65 岁,低于排名靠前的其他国家的*均水*。
议会中的女性和幸福指数
议会中女性比例较高的国家的*均幸福指数约为 45.1%(T1),而女性比例较低的国家的*均幸福指数约为 36.6%(T3)(T2)。我像前面一样,通过运行双样本 t 检验,重复了验证均值差异是否具有统计显著性的过程。
图(3) 点击此处查看上图互动版 。上面的方框图左边显示的是议会中女性比例高的国家,右边显示的是女性比例低的国家。从图表中,我们看到左边的国家比右边的国家*均有更高的幸福分数,或者说幸福百分比。
双样本 t 检验结果(幸福指数*均值)
p 值为 0.0009 ,因此我拒绝零假设,并得出结论,议会中女性比例较高的国家和比例较低的国家之间的幸福得分均值差异不等于零,在 0.05α水*上具有统计学意义。这就导致了一个结论,那就是议会中女性比例高的国家*均来说也更幸福。
图(4) 点击此处查看互动版上图 。该条形图显示了幸福指数最高的前 20 个国家。丹麦以大约 72%的*均幸福分数高居榜首。
幸福指数和预期寿命
为了总结这项研究,我进行了回归分析,以测试幸福指数与预期寿命之间的关系。
回归分析/回归图
图(5) 点击此处查看互动版上图 。回归图显示拟合预期寿命和幸福指数的 OLS 回归图。这条线的强度不算中等,但很积极。
回归分析结果(预期寿命和幸福指数)
OLS 结果显示 p 值小于 0.005,R *方值为 0.33。因此,我们拒绝零假设,即适合该模型的两个变量不相关,并得出结论在 0.05 alpha 水*上,幸福百分比和预期寿命之间存在统计上的显著关系。回归线显示了幸福百分比和预期寿命之间的正相关,但是中等偏弱;大约 33%的预期寿命差异可以用幸福百分比的变化来解释。
图(6) 点击此处查看互动版上图 。显示幸福得分和预期寿命*均值的气泡图。颜色代表议会中女性比例的高低,而气泡的大小代表每个国家议会中女性的实际比例。
预期寿命 V 幸福分数动画泡泡剧情
图(7) 点击此处,动画显示上图 中的气泡。气泡图显示了 2005 年至 2019 年间议会中的女性比例、幸福指数和预期寿命的变化。请注意气泡是如何向上和略微向右流动的,这表明在气泡所代表的国家中,女性在议会中的比例正在增加。
结论、局限性和展望
关于女议员比例较高的国家是否比女议员比例较低的国家预期寿命更长的问题,我的结论是,当一个国家的女议员比例较高时,这个国家的预期寿命也很可能更长。
此外,与议会中女性比例较低的国家相比,议会中女性比例较高的国家*均幸福指数也较高。
对于回归分析,我发现幸福指数和预期寿命之间存在一种正相关关系,尽管这两个变量之间的相关强度不太大。
为了清理数据,为了便于分析,我用零替换了原始数据集中缺失的值,由于三个数据文件的年份范围不同,最终的数据框架共有 160 个国家。因此,如果更多的国家加入到数据跨越更长时间的列表中,t 检验和回归分析的结果可能会有所不同。
这个项目的前景可能包括收集更多关于更多国家议会中妇女比例的数据,最终目标是测试这里的结果是否仍然成立。
第二个目标是调查其他社会因素和议会中女性比例的提高是如何影响预期寿命和幸福感的。
自定义 Scipy ODE 解算器
融入时尚
猴子给 Scipy 打补丁,在每个集成步骤做事情
除非你在过去的几年里生活在岩石下,否则你肯定知道 Python 语言通常是科学计算任务的首选武器,因为它是免费的、可扩展的、易于掌握的,并且具有与最好的商业软件相当的性能。科学计算 Python 栈几乎总是由著名的包 Numpy 和 Scipy 、数值类型的高质量实现、许多不同的数学运算和常见的计算原语组成。
在研究和工程中一个很常见的任务就是整合https://en.wikipedia.org/wiki/Ordinary_differential_equation的系统,特别是初值问题。在 Scipy 中,实现这一点的首选方式是通过函数[solve_ivp](https://docs.scipy.org/doc/scipy/reference/generated/scipy.integrate.solve_ivp.html)
。你提供要积分的函数,你想要使用的积分算法/解算器的种类( RK25,RK45 等)。),通常的集成参数,您就可以开始了。让我们试着整合一个著名的自治微分系统,洛伦兹方程:
洛伦兹系统的代码
一个人可以解决洛伦兹系统,而不显示解决方案的图形吗?那鸿(图片由作者提供)
在使用了一点点 **solve_ivp**
之后,我很快意识到 当前版本的函数没有办法指定在集成的每一步之后要执行的回调或任务。
在执行这类任务的软件中,这通常是可能的。虽然最终的解决方案是在最后交付的,但是在每一步之后,您可能想要做的事情是,例如,在解决方案生成时绘制解决方案,以便在早期发现 bug 和错误,或者简单地显示某种信息或指标。当解决计算昂贵和/或必须长时间集成的系统时,特别需要这些特性。那么……怎么做呢?
最幼稚的做法是编辑solve_ivp
的源代码,但这是一个大禁忌:该功能将不再像所有其他使用它的软件预期的那样工作,这是一个灾难。我们可以推出自己版本的solve_ivp
或我们正在使用的求解器,但这涉及到大量的复制粘贴,这将意味着有效地派生当前版本的函数,并且如果我们想跟上可能推出的更新,还需要进行艰苦的维护。最正统、最精简、最优雅的解决方案,或者说最不会让计算机科学家皱眉的解决方案,是 猴子补丁 。
****Monkey patching 是一种编程技术,涉及在运行时动态修改对象,以完成一些期望的行为,而不干预它们的源代码。你可以把它想象成在你的代码开始运行的时候临时定制一个对象来满足你的需求。在本文中,我将展示一个非常简单的用例:添加一个进度条,它将告诉我们在什么点上进行集成,并给我们关于计算速度和进展的反馈。为此,我们将使用轻量级的可扩展包[tqdm](https://github.com/tqdm/tqdm)
。
猴子补丁代码
这是怎么回事?嗯,通过查看 solve_ivp 源代码,我体会到两件事:
- 所有可用的解算器,如
RK23
、RK45
、BDF
等。,继承自一个名为OdeSolver
的母类。 solve_ivp
初始化OdeSolver
并调用方法step
推进整合。
因此,这个想法是将我们自己的进度条形码添加到__init__
和step
方法中。由于我们还想继续做这两个方法在原始代码中做的老事情(咄),我们将这两个方法“保存”为old_init
和old_step
,并用与方法完全相同的签名编写我们自己的包装函数。我们的版本,除了调用旧代码,还创建、更新和关闭一个 tqdm 进度条。最后,我们只是用我们自己的增强版本修补了这两个方法;也就是说,我们简单地在加载的活类上动态替换那些属性——这就是动态编程语言的本质。瞧啊。现在,每当solve_ivp
使用OdeSolver
时,就会出现进度条。
**IVP: 100%|##########| 40.0/40.0 [00:00<00:00, 232.66ut/s]**
您还可以继续做其他有趣的事情:绘制解决方案、显示指标、发出警告、进行健全性检查……就像您可以提供自己的回调函数一样!
科技职业会在 30 岁结束吗?
一位 50 岁数据科学家的反思
一切都在 30 岁结束吗?
今年我遇到了一件奇怪的事。我已经 50 岁了!我是 x 世代,以前看 MTV,80 后的孩子。是的,我是“老了”。但是,你猜怎么着?超过 35 就可以有科技职业。对我来说,至少超过了 15 年。我是一名数据科学家,虽然我的观察主要与数据科学相关,但也适用于其他技术领域。
在这篇文章中,我将谈谈我在地球上生活的五十年中,一些对我有用的(和一些没用的)方法。我还将讨论我在指导学生和与非常有才华的同事一起工作时观察到的情况。许多道路在很大程度上取决于你在生活中的处境;也许我一路上学到的一些东西会有所帮助。
我的道路不完全是传统的。我 13 岁开始在 commodore 64 上编程。从高中毕业后,我主修计算机科学,但在我获得学位三年后辍学。我从来没有实习过。我确实继续接受了教育,并最终获得了博士学位,这使我能够进行一篇与机器学习相关的论文。写自动化特征工程对我作为数据科学家的职业生涯和我在 Kaggle 的排名都有好处。
我一直是技术型的;即使作为一个大公司的副总裁,我仍然编码。我 35 岁左右就结婚了,但是我们没有孩子。我只在职业生涯早期为创业公司工作,从未走上自己创业的道路。
基础:你的早年
我在 1984 年开始编程,当时我 13 岁,在 Commodore 64 上。尽早开始的重要性在于让你自己或者你的孩子接触科技,看看激情在哪里。在这个年纪,尝试很多事情是很重要的。你会学到你热爱什么,也知道你擅长做什么。激情和技巧并不总是相同的。对我来说,我很快就学会了计算机编程,我真的很喜欢它。我很幸运很早就找到了激情。
Commodore 64 8 位计算机( Adobe Stock Image )
我在 20 世纪 80 年代末遵循的特定路径与 21 世纪 20 年代不相关,所以我不会用 8 位计算世界的故事来烦你。在 2020 年代,你当然可以在大学之前学习编码。有几个令人印象深刻但可以实现的壮举,你可能会考虑。总要有个项目。我不在乎这是一个简单的视频游戏,还是一个追踪你最喜欢的 YouTubers 或 Kaggle 笔记本的东西。找点好玩的。我的第一个高中项目是一个 Commodore 64 BBS (本质上是一个消息论坛),它不需要 RAM 扩展。我当时支持了几家运行我的软件的本地 BBS。
- GitHub——高中或更低学历的人,光是在 GitHub 上拥有一个账户就足以让人印象深刻。如果你不想赚钱,GitHub 是一个发布你的项目的好地方。你应该清理你的代码,把它放在 GitHub 上,而不是放在某个笔记本电脑上。
- 应用——我见过几个高中生因为在 IOS/Android 应用商店发布应用而获得奖学金和其他荣誉。即使你不是在寻求商业上的成功,你也可以将这段代码发布到 GitHub 上。
我钦佩的一个在青少年时期就取得巨大成就的年轻人是 Pratham Prasoon。17 岁时,Pratham 在 GitHub 上很活跃,已经有超过 10 万的 Twitter 粉丝了!我无法想象他 25 岁左右会干什么。Pratham 主要专注于机器学习。
然而,不要让社交媒体上真正的模范人物劝阻你。不要陷入冒名顶替综合症。在某个特定领域总会有更好的人。相反,以杰出人物为榜样,努力模仿对你有意义的品质。相信我!这就是我的工作。
获得教育和经验:20 多岁
在你十八九岁和二十出头的时候,你正在训练和积累经验。通往技术理解的道路有很多。
传统的方法是大学制度。虽然我为一所大学教书,并拥有博士学位,但我是第一个承认大学不是展开科技生涯的唯一手段。我开始了这个方法;然而,我没有奖学金支持。为了收支*衡,当我还是一名全日制学生时,我从事了技术咨询工作。我不想要学生贷款,即使是在学生贷款流行的时候。到了第三年,我对赚钱的兴趣远远超过了做学生。我的*均绩点下降了,我退学了。虽然我不提倡这种方法,但请记住,你可以随时改变轨迹。
选择你的道路:大学,MOOCS,自学,或混合( Adobe Stock Image
如果你要进入大学系统,我建议你读一个 stem 学位。除非你打算进入学术界,否则随着你积累经验,你实际上的大学会变得越来越不重要。说你去了哈佛、麻省理工或斯坦福总是好的。然而,除了第一份工作,人们更多地是根据你毕业后的表现来评判你。在我并不出色的大学初期经历中,我从未实习过。但是,如果你走的是大学路线,我强烈推荐实习。一旦你进入就业市场,缺乏经验会成为你最大的劣势。实习让你获得一些经验,看看你在这个领域的职业生涯是为了什么。
如果你不走大学路线,那么你要对你的教育负责。有新兵训练营,但是要小心。训练营有各种各样的品质,有些没有任何评价。一方面,是的!没有测试。
另一方面,我的狗可以和我一起参加课程并获得同样的证书。我自己也走了这条路。那时训练营还不存在,我只是寻找我需要的和学到的东西。我读了很多书。现在有许多指导自学。
在你职业生涯的这个阶段,尽你所能获取经验。当你去找第一份工作时,你马上就陷入了“陷阱”第二十二条军规是大多数初级工作都要求两年的工作经验。任何你声称能弥补这个缺口的东西都是有用的。带薪实习很棒。如果有必要,自愿贡献你的时间。我早期的一些经历来自于靠小本经营融资的小型创业公司。去体验一下吧。
同时,利用这段时间建立你的社交媒体形象。 GitHub , LinkedIn , Medium , YouTube ,某种程度上 Twitter 都是有益的。这方面的一个典型例子是 Khuyen Tran。Khuyen 主要专注于数据科学。我已经关注她好几年了,因为她完成了本科学业,并通过发布适用于数据科学家和经验丰富的 Python 程序员的 Python 编程内容,建立了一个令人惊叹的社交媒体。甚至在开始第一份工作或实习之前,她就已经在打造自己的专家品牌了。
责任和专注:30 多岁
得到第一份工作是很棒的,得到第一次晋升或第二份薪水更高、责任更大的工作就更棒了。理想情况下,第一次晋升和超过入门级别的变动发生在你 20 多岁的时候。然而,30 多岁将是承担额外责任的时候,专注于让你与众不同的事情。你想做什么?你擅长做什么?对我来说,这一直是永恒的*衡。枢轴是可以接受的。我从信息技术(IT)开始,然后转向数据科学。
责任和领导( Adobe Stock Image )
我总是发现,我的晋升与我承担的任务量直接相关,也与我从经理的直接关注中摆脱出来的任务量直接相关。随着你事业的发展,你需要的直接监督应该会减少。理解并帮助塑造经理的愿景和政策,您必须最终帮助执行这一达成一致的愿景。这会在你和你的上司之间建立信任,并带来更多的责任。
如果你保持高度技术性,确保你的公司有一条面向个人贡献者的道路。个人贡献者主题专家能达到的最高水*是什么?高度技术性的个人贡献者总是有上限的——显然,CEO 不可能是个人贡献者。但是,如果你的路径是高度专业化的个人贡献者,而公司对这样的人才的天花板很低,你的职业生涯就会在那个公司碰壁。
管理变化并保持相关性:你的 40 多岁
我知道 6510 汇编语言,我已经用静态 HTML 在 CGI-BIN 中实现了网站,我已经在 80x25 字符终端上创建了高级用户界面,我可以用 VT100 创建远程应用程序,我擅长从 640K 的内存中挤出每个字节。这些都是我花了几个月时间来完善的技能,它们让我从公司获得了合理的报酬。这些都是在 2021 年完全不值钱的技能。
我不得不学习的技能…现在一文不值…(作者图片)
如果你是一名科技工作者,世界将会从你的脚下发生改变。在你的职业生涯中,这种进步不会发生一次,而是多次。如果你不适应,你就不会长久地成为一名技术工人。这种趋势也许是为什么你会看到许多关于技术职业在 35 岁左右崩溃的故事。我经历过很多技术。哦,已经有这么多了。如果有一天你开始质疑为什么你必须学习新的东西,因为同样的任务,你所热爱的技术表现得几乎一样好,这可能是你技术生涯结束的开始——特别是作为一个个人贡献者。学习哪些技能会持续下去,哪些是时尚,以及如何轻松地改变。没有人能预测未来,所以你永远不会做出完全正确的决定。
超越职业生涯思考:你的 50 多岁(及以后)
如果你继续保持技术和相关性,那将是最好的。作为一名数据科学家,这尤其具有挑战性,因为新的技巧和技术会不断引入。我仍然几乎每天都写代码;然而,这可能不是所有人的路。如果你完全进入管理层,试着跟踪你的团队正在实施的技术。
希望到 50 岁的时候,你正处于赚钱的黄金时期。即使你打算永远工作下去,你的健康状况最终也会阻碍你。希望你现在已经存了一些钱。至此,学习个人理财的基础知识是至关重要的。知道何时推迟和消除未来的税收支付可能是至关重要的。我家属于邻家百万富翁类型。那本书在我年轻的时候对我有很大的影响。我的家庭建立了一个不错的净资产,我们知道如何投资。我的房子和汽车很舒适,但并不奢华,但银行并不拥有它们的任何部分。
确保在这个过程中节省一些钱。你不可能永远工作/挣钱。(土坯股票图片)
我确实同意那句老话,“金钱买不到幸福”,然而,缺钱很有可能买到悲伤。
我还上传了一段 YouTube 视频:
科技职业会在 30 岁结束吗?一位 50 岁数据科学家的反思
这样做可以让你的网站在几分钟内变得互动
利用 Chart.js 数据可视化的力量提升您的项目
数据讲述有意义的故事,有了可视化的补充,我们可以收集有价值的见解,以有效地发现、支持和分享想法。但是静态图像通常不能实现数据可视化的全部功能。
如果我们在网站上增加用户与数据的交互,我们能解锁什么?
无论是在项目、个人网站还是博客帖子中,允许用户自定义和探索实践都可以带来更有用和更难忘的体验。
有了 Chart.js ,你可以非常容易地插入令人惊叹的表格和图表,成为一个更有影响力的故事讲述者。这篇文章将指导您使用这个工具,包括在页面上设置和创建几种类型的图形。
那么,Chart.js 到底是什么?
Chart.js 是一个免费的开源库,用于在 JavaScript 文件和 HTML canvas
元素中制作动画。它包括八种不同类型的图形:折线图、条形图、圆环图、饼图、散点图、气泡图、雷达图和极区图,但也可以在同一页面上混合搭配多个图形。
我们打算在这个指南中做什么?
我们将介绍如何设置 Chart.js,创建关于世界人口统计的初始图表,并设置它们的样式。
数据源
以下是本教程中使用的所有数据源的链接。
步骤 1:包括图书馆
我已经整理了一个 GitHub 库包含了我们将在本教程中讨论的所有内容。如果您打开index.html
文件,您应该看到浏览器中弹出一页图表。
现在,在文本编辑器中打开index.html
获取代码。为了方便地导入最新版本的 Chart.js,我们将使用 use this CDN link (以“Chart.min.js”结尾)。然后,将链接放在一个script
标签中,如上所示。
为了格式化我们的图表,包括 CDN 链接到 Bootstrap CSS 和 Bootstrap JS。如果你认为关于 Bootstrap 的更多信息会有所帮助,请点击这里查看。
步骤 2:准备 HTML
在开始可视化我们的数据之前,我们需要准备的一件主要事情是在我们的 HTML 中定义一个我们想要绘制图表的区域。我们必须为我们网页上的每个图形添加canvas
元素。这是如何工作的。
在上面的代码片段中,我们给我们的图形(在本例中是一个线图)一个id
,也就是line
,它指向canvas
元素,我们稍后可以用它来引用 JavaScript 中指定的图形元素。您可以随意命名该元素;唯一重要的是,当在所有文件中引用图表时,使用相同的id
。请记住,每个id
必须是唯一的。
步骤 3:用 JavaScript 创建大纲
为了让我的数据易于组织,我选择将每个图形的代码放在一个单独的 JavaScript 文件中。请随意遵循不同的格式,但这是我发现最有效的。
创建一个 JavaScript 文件(并将其放入一个名为。js 如果你喜欢的话)。
让我们来分解一下line.js
发生了什么。首先,我们定位之前添加到我们的index.html
文件中的canvas
元素(注意id
line
)。然后,使用那个canvas
元素,我们创建一个折线图(type: 'line'
),并传入我们的数据。
步骤 4:插入数据
编辑图表所需的所有数据。对于这个样本线图,我们将使用世界银行的人口增长统计数据。下载数据(如果愿意,也可以手动输入)。
然后,将数据复制并粘贴到如下所示的数组格式中。这是 JavaScript 文件整体结构不可或缺的一部分。
我们也可以给数据集一个标签。如果您保存并刷新 HTML 页面,您将看到一个新的折线图。但是,我们可以采取措施自定义视觉效果,包括轴刻度线和标签、图表标题、点和线的颜色、悬停效果等等!
步骤 5:格式和样式
我们还可以将多个数据集添加到一个图表中,这有助于描绘更多趋势。这在下面的代码中被注释掉了。
对于我们想要创建的每一行,我们向datasets
数组添加另一个对象。每个对象都可以有一个调整范围;我们不仅可以传递新数据来画线,还可以更改线的名称、行为和外观。这些特征可以按照完成的line.js
文件中的描述进行编辑。在 Chart.js 文档中阅读更多关于您可以选择的定制的详细描述!
成品线图
左:一个数据集,右:两个数据集
到目前为止,我们已经了解了如何在网页上创建、个性化和查看折线图。让我们看看 Chart.js 提供的一些其他可视化工具。这些都包含在本教程开头链接的存储库中。
其他类型的可视化
Chart.js 提供了几种其他类型的图表,比如表格、条形图、饼图、散点图等等。下面是更多示例的 JavaScript 代码。
条形图
左:三个数据集,右:两个数据集
圆形分格统计图表
左:饼图,右:圆环图
散点图
桌子
这段代码包含在index.html
中,而不是 JavaScript 中。
要记住的关键事情
在审查代码时,请记住这些提示。
- 确保每个图表的
canvas
id
在整个 HTML 和 JavaScript 文件中保持一致。 - 记得用
script
标签链接 HTML 中的 JavaScript 文件。 - 确保每次添加新要素或数据集时都放置一个逗号。否则,图形将无法正确渲染(或根本无法渲染)。
- 在进行重大更改后刷新 HTML 页面,以跟踪您的进度。
- 玩得开心!请在评论中分享你的想法或问题。
视觉变形器看起来像卷积神经网络吗?(论文解释)
思想和理论
视觉变形金刚(ViT)*年来势头越来越猛。本文将解释论文“视觉变形金刚看起来像卷积神经网络吗?” ( Raghu 等人,2021 )由 Google Research 和 Google Brain 发布,并探讨常规使用的 CNN 和 Vision Transformer 的区别。
本文摘要和博客内容
本文有六个中心摘要:ResNet ( He et al .,2016) as a representative of CNN-based networks. Residual_Learning_CVPR_2016_paper.html) )和 ViT 为代表的基于 CNN 的网络。
- 与 CNN相比,ViT 在浅层和深层获得的表征之间具有更大的相似性
- 与 CNN 不同,ViT 从浅层获得全局表示,但从浅层获得的局部表示也很重要。
- ViT 中的 Skip 连接甚至比 CNN(ResNet)中的影响更大,并对表示的性能和相似性产生实质性影响。
- ViT 比 ResNet 保留更多的空间信息
- ViT 可以学习大量数据的高质量中间表征
- MLP 混频器的表示更接* ViT 而不是 ResNet
在这篇博客中,我将首先简要回顾 ResNet 和 ViT 的结构,它们是基于 CNN 的模型的代表性示例,然后进一步研究本文中描述的所获得的表示的差异。
ResNet 基础知识
ResNet 是计算机视觉(CV)任务的流行模型。如下面的图 2 所示,ResNet 的加权传播端使用跳过一层权重的 skip 连接进行求和。具有跳跃连接的求和过程减轻了诸如梯度消失的问题,并且允许比先前网络更深的层。
ResNet 架构来自何等,2016) as a representative of CNN-based networks. Residual_Learning_CVPR_2016_paper.html)
视觉转换器(ViT)基础知识
首先,让我们来看看视觉变压器(ViT)中使用的变压器编码器。
变压器
变压器是在论文《注意力是你所需要的全部》( Vaswani et al .,2017 )中提出的模型。它是一个使用称为自我关注的机制的模型,这种机制既不是 CNN,也不是 LSTM,并建立了 Transformer 模型,以显著优于现有的方法。结果比现有的方法好得多。
注意下图中标有多头注意的部分是 Transformer 的核心部分,但它也像 ResNet 一样使用了 skip-joining。
变压器架构。来自瓦斯瓦尼等人,2017
变压器中使用的注意机制使用了三个变量: Q (查询)、 K (键)、和 V (值)。简单地说,它计算一个查询标记(标记:类似于一个单词)和一个键标记的注意力权重,并乘以与每个键相关联的值。简而言之,它计算查询令牌和密钥令牌之间的关联(注意力权重),并乘以与每个密钥相关联的值。
(单头)自我关注
将 Q,K,V 计算定义为单头,多头注意机制定义如下。上图中的(单头)注意机制原样使用了 Q 和 K 。尽管如此,在多头注意力机制中,每个头都有其投影矩阵【w_i^v,并且它们使用使用这些矩阵投影的特征值来计算注意力权重。
多头注意力
如果这个注意机制中使用的 Q,K,V 都是从同一个输入计算出来的,则专门称为自我注意。另一方面,Transformer 解码器的上部不是一个“自我”注意力机制,因为它使用来自编码器的 Q 和来自解码器的 K 和 V 来计算注意力。
实际应用的图像如下图所示。该图示出了使用单词“making”作为查询为每个关键令牌计算的注意力权重的可视化。转换器使用多头自关注机制传播到后面的层,每个头学习不同的依赖关系。下图中的关键词是彩色的,代表每个头部的注意力权重。
注意力权重可视化。从 Vaswani 等人,2017 引用的图像,我已经注释过了。
###视觉转换器(ViT)
视觉转换器(ViT)是一种将转换器应用于图像分类任务的模型,于 2020 年 10 月提出( Dosovitskiy 等人 2020 )。模型架构与原始的 Transformer 几乎相同,但有一点不同,它允许将图像作为输入处理,就像自然语言处理一样。
视觉转换器架构。图片引自 Dosovitskiy et al. 2020 ,我已经做了注释。
首先,ViT 将图像分成 N 个“小块”,例如 16x16。由于面片本身是 3D 数据(高 x 宽 x 通道数),它们不能由处理语言(2D)的转换器直接处理,因此它将它们展*并进行线性投影,以将其转换为 2D 数据。因此,每个补丁都可以被视为一个令牌,可以输入到转换器中。
此外,ViT 采用先预训练后微调的策略。ViT 使用包含 3 亿张图像的数据集 JFT-300M 进行预训练,然后在 ImageNet 等下游任务上进行微调。ViT 是第一个在 ImageNet 上实现 SotA 性能的纯变形金刚模型,这导致了对变形金刚应用于计算机视觉任务的研究大幅增加。
但是,训练 ViT 需要大量的数据。变压器在数据较少的情况下不太准确,但在数据较多的情况下变得更加准确,并且在 JFT-300M 上进行预训练时表现优于 CNN。更多详情请参考原论文。
视觉转换结果。( Dosovitskiy 等人 2020
根据我们得到的表达式比较 ResNet 和 ViT
到目前为止,我们已经看到了 ResNet 和 ViT 的概述,两者在图像识别任务中都可以有不错的表现,但是 它们之间有什么区别呢? 在论文《视觉变形金刚看起来像卷积神经网络吗?作者对其进行了研究。
让我们仔细看看介绍中提到的以下六点。
- 与 CNN相比,ViT 在浅层和深层获得的表征之间具有更大的相似性
- 与 CNN 不同,ViT 从浅层获得全局表示,但从浅层获得的局部表示也很重要。
- ViT 中的 Skip 连接甚至比 CNN(ResNet)中的更有影响,并且实质上影响表示的性能和相似性。
- ViT 比 ResNet 保留更多的空间信息
- ViT 可以学习大量数据的高质量中间表征
- MLP 混频器的表示更接* ViT 而不是 ResNet
1。与 CNN相比,ViT 在浅层和深层获得的表示之间有更多的相似性
ViT 和 ResNet 的主要区别之一是初始层的大视野。
相对于输入图像的感受野大小。
CNN(ResNet)只有固定大小的内核视场(大小 3 或 7)。具体来说,CNN 是通过一层一层地重复“卷积”内核周围的信息来逐渐扩大视野的。相比之下,ViT 使用自我关注机制,允许模型即使在最低层也具有整个视野。这样,根据网络的结构,视野是不同的。
下图是 ViT 的实际视野(自我注意机制的有效距离)。在浅层,有一些像 CNN 这样局部视野的部分,但也有很多头部具有全局视野。
注意距离。该数据引自 Dosovitskiy 等人 2020 年
那么,ResNet 和 ViT 在各层深度获得的表象,结构上有什么区别呢?为了找到答案,作者在下图(图 1)中绘制了每一层获得的表示的相似性。
在上图中,他们使用一种称为 CKA 相似性的测量方法绘制了每个图层所获得的表示的相似性(我不会深入 CKA 相似性的技术细节,因此如果您想了解更多信息,请参考原始论文。)图形的对角线分量因为与自身相似,自然高,但是我们再来看看图形的其他部分。
首先,在 ViT(左边两个)中,我们可以看到整体的着色表明,无论层的深度如何,都在获得相似的表征。另一方面,在 CNN(右边两个)中,我们注意到在浅层和深层获得的表征之间没有相似性。这可能是因为在 ViT 中,我们从一开始就获得全局表示,而在 CNN 中,我们需要传播层来获得全局表示。
ViT 和 ResNet 之间的相似性绘制在下图中(图 2)。
我们可以看到 ViT 的 1 到 40 层和 ResNet 的 1 到 70 层的相似度很高。所以 ResNet 需要 70 层来获得 ViT 需要 40 层来获得的表示。这意味着,在浅层获得一种表示的方法是非常不同的。此外,ViT 深层与 ResNet 深层的相似度较低。所以图像的抽象表示在 ViT 和 ResNet 之间有很大的不同。
顺便提一下,一些研究的动机是,由于自我注意图的相似性,ViT 不会受益于加深(周等,2021 )。该研究关注头部之间的高度多样性,并提出了一种称为重新注意的机制,该机制引入了一个学习参数来混合不同头部之间的特征。他们使用它取得了良好的效果(DeepViT)。
自我关注和再关注
使用再注意的深度得益于加深(周等,2021 )
2。与 CNN 不同,ViT 从浅层获得全局表示,但从浅层获得的局部表示也很重要
下图(图 3)是用 JFT-300M (3 亿张图片)预训练,用 ImageNet(130 万张图片)微调后,自我注意机制的有效距离(5000 个数据的自我注意距离的*均值)。
在浅层(encoder_block0,1)中,我们可以看到模型同时获得了局部和全局表示,而在深层(encoder_block22,23,30,31),所有表示都具有全局视图。
正如我们在视觉转换器的描述中看到的,训练 ViT 需要大量的数据(如 JFT-300M),如果数据不足,精度就会下降。下图(图 4)显示了这种情况下的相似之处。
如果我们比较图 3 和图 4,我们可以看到,当数据集很小时,我们无法在浅层获得局部表示。从这个结果以及“ViT 在数据量很小时达不到精度”的事实,我们可以看出,用足够数据训练的 ViT 所获得的“局部表示”对精度有显著的影响。
但是数据量和所需的表示之间有什么关系呢?下图(图 12)说明了这一点。
对于约 10%数据的浅层表示,与使用所有数据获得的表示的相似性在某种程度上增加了。然而,对于深层表示,即使有 30%的数据,相似度也低于 0.2。由此,我们可以说,有助于准确性的深层表示法只能通过大量数据来学习。前面提到局部表示很重要,但似乎在深层可以获得的全局表示也很重要。
虽然这里没有具体说明,但是实验很可能是用 JFT-300M 进行的,所以即使我们说总数据的 3%,仍然有 10M 左右的数据量(大概是 ImageNet 的 10 倍)。在我看来,30%的数据(100M)就足以获得浅层应该获得的局部表示,如果数据更多,就有可能获得全局表示中重要的东西。
3。ViT 中的跳过连接甚至比 CNN(ResNet)中的更有影响力,并且实质上影响表现的性能和相似性
接下来,让我们看看跳过连接和习得表达相似性之间的关系。如下图所示(图 8)。
在该图所示的实验中,我们计算当消除层 i 的跳过连接时所获得的表示的相似度。将该图与图 1 的左侧(ViT)相比较,可以看到在消除了跳过连接的层 i 之后,所获得的表示的类似趋势发生了剧烈变化。换句话说,跳过连接对制图表达传播有重大影响,当它被消除时,图层的相似性会发生显著变化。顺便说一句,当在中间层消除跳跃连接时,精度下降约 4%。
接下来,我们来看看跳过连接在信息传播中是如何发挥作用的。请看下图(图 7)。
在图 7 的左面板中,|| | z _ I |/| f(z _ I)| ||,比值为$ z_i ,输入信息到\(i\)层中的自我注意,而 f(z_i) ,经自我注意和多层网络变换后的特征值【z _ I,被标绘为)比率越大,通过跳跃连接传播的信息越多;左边的图 7 显示了类标记通过初始层中的跳跃连接进行传播,而图像通过自我注意和多层网络进行传播。在更深的层面上,趋势是相反的。
右图显示了与 ResNet 的对比。绿线为 ResNet,而 ViT 值较大,说明通过跳跃结的信息传播起主要作用。
虽然论文中没有具体提到,但跳过连接在信息传播中起着重要作用,这一事实可能导致在图 8 中消除中间层的跳过连接时精度显著下降。
4。ViT 比 ResNet 保留更多的空间信息
接下来,我们来比较一下 ViT 和 ResNet 保留了多少位置信息。看下图。
在本实验中,作者通过绘制输入图像的补片与特定位置的最终图层特征图之间的 CKA 相似性,测试了哪些 ViT 和 ResNet 保留了位置信息。如果它保留位置信息,那么在某个位置与输入图像的片的相似度应该仅在对应于特征图中该位置的位置处高。
首先,我们来看看 ViT(上,中)。不出所料,在最后一层,对应位置的相似度较高。这意味着 ViT 传播表示,同时保留位置信息。接下来,我们来看看 ResNet(底排)。在这种情况下,不相关位置的相似性更高,表明它没有保留位置信息。
这种趋势上的差异很可能是因为网络结构的不同。看下图(图取自[王等 2021](http://Wang et al., 2021) )。
[王等,2021](http://Wang et al., 2021)
ResNet 和其他基于 CNN 的图像分类网络以降低的分辨率传播表示。比如 ResNet 有五个阶段,每个阶段的分辨率减半,这样最终的特征图大小为 1/32 * 1/32(上图左图)。另一方面,ViT 首先标记为 16x16 大小,这会降低该区域的分辨率,但它会以该分辨率传播到最终层。因此,ViT 比 ResNet 更有可能保留位置信息。然而,首先,图像分类任务不需要位置信息来进行分类决策,因此不能说 ViT 比 ResNet 有优势,因为位置信息被保留了。
此外,在最*的研究中,像 ResNet 这样逐渐降低分辨率的策略经常被用于视觉变压器相关的研究中。例如,右上角显示的金字塔视觉转换器。Transformer 系统使用自我关注,占用的内存大小与图像大小的四次方成比例增加。这使得难以处理大分辨率,但是通过使用逐渐降低分辨率的策略,如在 CNN 系统中,可以在第一层中处理高分辨率信息,同时节省存储器。
5。ViT 可以学习大量数据的高质量中间表征
接下来,让我们看看中间层表示的质量。下图(图 13)展示了这个实验。
在这个实验中,作者试图看看他们是否可以使用中间层的表示来用线性模型进行分类。简单模型(如线性模型)的精度越高,图层的表现就越好。
首先,我们来看看数据集的大小和得到的表示之间的关系(左图)。这里,我们比较了包含 130 万张图片的 ImageNet(虚线)和包含 3 亿张图片的 JFT-300M(实线)的实验结果。正如你所看到的,在 JFT-300M 上训练的表现更好,这是一个巨大的数据集。接下来是包括 ResNet 在内的车型对比。可以看出,较大的模型产生更好的表示。
作为旁注,在右图中,基于 ResNet 的模型的精度在接*最终层时突然增加。这是为什么呢?
Frosst 及其同事的一项研究提供了一个提示( Frosst et al .,2019 )。他们在 ResNet 的中间层引入了带有温度项的软最*邻损失,并研究了它的行为。软最*邻损失通过类别指示特征的纠缠状态。较大的软最*邻损失值表示按类划分的要素相互交织,而较小的值表示按类划分的要素相互分离。
软最*邻损失
值越小,分类越好。(弗罗斯特等人,2019 )
[软最*邻损失]
下图显示了 ResNet 的每个块中的软最*邻损失的值。众所周知,这是一个高性能的图像分类网络,但除了最后一层之外,它没有分离每一类的特征。在我看来,ResNet 的这一特性可能是最后一层附*精度快速提升的原因,如图 13 所示。
在最后一层,软最*邻损失的值很小,表明要素是按类别分开的。(弗罗斯特等人,2019 年)
6。MLP-混频器的表示更接* ViT 而不是 ResNet
最*,代替变压器,使用多层感知器(MLPs),即具有密集层的网络,已经提出了高度精确的图像分类模型。一个典型的例子是称为 MLP 混频器的网络( Tolstikhin 等人,2021 )。这个网络的结构如下图所示。
MLP 混合器是一种系统,它使用 MLP1 在小块之间混合信息,然后使用 MLP2 在小块内混合信息,然后通过堆积组合两者的块来传播。这种 MLP 混频器可以达到与 ViT 相同或更高的精度。下图以与前面相同的方式比较了 MLP 混频器的表示。将该图与图 1 和图 2 进行比较,作者认为总的趋势与 ViT 相似。
MLP 混合器通过将图像划分为类似 ViT 的面片来传播图像,因此它在结构上比 ResNet 更接* ViT。这种结构可能是造成这种结果的原因。
Raghu 等人,2021 年
结论
在这篇文章中,我详细研究了 ViT 和 CNN 之间的差异。概括来说,这里有两者之间的一些差异。变形金刚将继续在计算机视觉领域发挥重要影响。希望这篇文章能帮助你了解变形金刚。
- 与 CNN相比,ViT 在浅层和深层获得的表征之间具有更大的相似性
- 与 CNN 不同,ViT 从浅层获得全局表征,但从浅层获得的局部表征也很重要。
- ViT 中的 Skip 连接甚至比 CNN(ResNet)中的影响更大,并对表示的性能和相似性产生实质性影响。
- ViT 比 ResNet 保留更多的空间信息
- ViT 可以学习大量数据的高质量中间表征
- MLP-混频器的表征更接* ViT 而不是 ResNet
— — — — — — — — — — — — — — — — — — –
🌟我每周发布时事通讯!请订阅!🌟
**https://www.getrevue.co/profile/akiratosei
— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —
其他博客
https://medium.com/analytics-vidhya/reach-and-limits-of-the-supermassive-model-gpt-3-5012a6ddff00 **
我们需要数据框架吗?
意见
数据框架对数据科学有多重要,更重要的是;我们真的需要使用它们吗?
(图片由作者提供)
介绍
如果您在数据科学领域工作过一段时间,您可能听说过一种称为数据框的类型。数据框是以表格形式存储数据的有用方式,它保留了要素的一维形状,同时还创建了多维矩阵。无论使用 Pandas、DataFrames.jl 还是 R DataFrames,这一概念在数据科学中常用的所有生态系统中都保持相对一致。虽然每个包都有不同的处理数据的方法和用法,但它们通常遵循相同的基于轴的准则,观察值可由特征集和矩阵位置调用。
我最*用一种新的方法和途径创建了一个用 Julia 语言处理数据的包。这样做部分是因为我对 DataFrames.jl 的某些方面感到失望,但也只是为了实验。然而,我认为这个项目也教会了我更多关于我们可以用来处理数据的不同策略,这些策略的优点和缺点,以及为什么某些策略可以用于不同的应用程序。我所指的包是 OddFrames.jl,它是 Julia 编程语言中 dictionary 数据类型的面向对象包装器。如果您想了解关于这个包的更多信息,我写了一整篇文章来介绍它,您可以在这里阅读:
这遵循了一个非常规的想法
"数据框只是美化了的字典."
虽然 DataFrames.jl 包在某种程度上是正确的,除了一些扩展的功能和更好的输出,但对于科学计算的所有生态系统中的每一个数据框实例来说,肯定不是这样。这方面的一个很好的例子是 Pandas 库,它在一个层次结构中使用 Pandas 系列等专有类型来完成基本相同的目标,只是稍微改变了方法,这通常会使事情变得更加方便。
真实数据
这些方法的问题是,有时简单也可能是聪明的。通常,当我们在数据科学中处理数据时,我们的数据可以像文件名一样抽象。此外,我们使用的大多数特征要么是一维的,要么被分解成一维特征集。考虑到这一点,我们没有理由一定要像使用矩阵一样使用数据框。也就是说,虽然数据框确实提供了这种灵活性,但与传统数据类型相比,它们也是一种依赖性,很难使用。
在我看来,Pandas 的 Python 库就不那么真实了,因为我认为 Pandas 已经很好地处理了数据帧的问题。尽管如此,我仍然认为,如果我们主要在一维中使用数据,那么像 JSON 数据的包装器或字典数据类型这样的东西对于处理一维特性可能更有价值。
答案
鉴于所有这些证据,您是否应该选择一个模块来处理数据框,或者使用传统的数据类型来处理数据是否是一个更好的主意。我认为大多数情况下正确的答案是*衡的方法。我们需要考虑到,在处理密钥对组合时,高维数据将会变得更加难以处理。也就是说,一维特征可能需要单独的方法。
虽然大多数数据框模块可以提供许多用于重塑数据的简洁功能,但在许多情况下,它们并不需要,而且以纯 Pythonic、Julian 或 R 风格的数据类型处理数据可能会更容易。这样做的一个很大的好处是,我们仍然可以访问可能利用这种类型的传统方法,给我们提供了更多的功能,在像 Julia 语言这样的语言中,您自己或其他程序员也可以扩展这些功能,以便非常容易地添加到您的项目中。
结论
我认为,在计算机内部处理数据方面,实际上有很多东西需要考虑。我还认为这是一个很少被讨论的话题,因为程序员可以用很多完全不同的方法来解决这些问题。当然,每种解决方案都有各自的优点和缺点。数据有很多东西,尤其是在高维的情况下,我认为数据框架可以增加这方面的体验,但也会带走更多传统数据可能拥有的其他能力。也就是说,如果你是数据科学的新手,拿起一个数据框包肯定是必要的,通常你会利用这些包来处理数据,此外;你的同事也会。如果你们都使用相同的技术,合作起来会容易得多,所以这肯定是一个值得考虑的方面。感谢您的阅读,我希望这篇文章激发了您的灵感,并启发您使用数组、数据类型和字典,比您传统上可能使用的要多得多!这不仅有时会让你处于优势,还会教你越来越多的关于语言中的数据类型的知识,这肯定会使用该语言处理数据变得更容易!
我们真的需要数据分析管道中的特征选择吗?
定义特征选择问题并识别其优点和缺点显示了它的价值
照片来自 Shutterstock
在典型的数据分析或监督机器学习任务中,目标是使用一组预测器(也称为特征、变量或属性)为感兴趣的变量(也称为目标或结果)构建预测模型。这种预测模型构建的流水线通常由几个步骤组成,例如特征提取/构建(例如,使用降维技术)、预处理(例如,缺失值插补、标准化)、特征选择和模型训练。这些步骤中的任何一个,除了模型训练,当然都可以省略。这篇文章关注的是为什么我们在数据分析管道中需要一个特性选择步骤。要回答这个问题,我们首先需要定义什么是特征选择。接下来,我们需要检查它的优点和缺点,以便得出最后的结论。
什么是特征选择?
在机器学习和统计学中,特征选择,也称为变量选择、属性选择或变量子集选择,是选择相关特征的子集(也称为特征、变量或属性)以用于模型构建的过程,有人在维基百科中读到。这是一个相对模糊的定义,没有明确特征选择的目标。更清晰的定义如下:特征选择可定义为选择变量的最小尺寸子集的问题,这些变量集合(多变量)包含为感兴趣的目标变量(结果)产生最佳预测模型所需的所有预测信息。
因此,特征选择任务的目标是在给定已经选择的变量的情况下,过滤掉不相关的或冗余的变量,并且仅选择那些为感兴趣的结果提供集体唯一信息的变量,从而产生在性能方面最优的预测模型。
作者照片:特征选择任务的目标是过滤掉不相关或冗余的变量,只保留那些为感兴趣的结果提供集体唯一信息的变量。
使用特征选择的优势
通过只选择几个特征(最重要的特征)并且不考虑其余的特征,通常可以学习到更好的模型,尤其是在高维设置中。这是违反直觉的,因为理想的机器学习建模算法应该至少能够在不应用特征选择的情况下执行,因为由所选特征提供的信息已经包含在所提供的数据中。事实上,渐*地(即,当样本大小趋于无穷大)并且给定一个完美/理想学习算法(实际上,没有这样的算法),没有理由为预测任务执行特征选择。
然而,在实践中,解决特征选择问题有几个优点。通过移除不相关和冗余的特征,建模算法的“工作”变得越来越容易,因此通常会产生更好的最终模型。使学习算法的任务变得更容易的另一个优点是,它的训练时间变得更短,并且使用更少的资源(例如,存储器)。这是因为良好的特征选择有助于建模,特别是对于易受维数灾难影响的算法。
特征选择是监督机器学习管道中的一个常见组件,当分析的目标是知识发现时,特征选择是必不可少的。知识发现在分子生物学和生命科学等领域非常重要,在这些领域,研究人员主要对理解问题的潜在机制感兴趣。选择一个特征子集可以简化模型,使它们更容易被研究人员/用户解释。最后,特征选择的一个重要方面是用户通过使用具有较少特征的模型可以实现的成本优化。如果测量某些特征非常昂贵,并且每个特征都与成本相关联,那么这一点尤其重要。
特征选择的缺点
特征选择问题是 NP 难的。对于线性模型,有几种方法可以精确地解决这个问题(也称为最佳子集选择问题)。虽然结果很有希望,但精确的方法最多只能处理几百或几千个变量(因此,它们不适用于高维数据)。
为了有效地解决特征选择问题,大多数方法依赖于某种*似。大多数*似方法可以大致分为逐步方法如 FBED ,基于稀疏性的方法如 LASSO ,信息论方法,以及基于因果的方法。尽管它们是*似值,并不能解决确切的问题,但对于一大类分布或在某些条件下,它们仍然是最佳的。
我之前提到的一个优点是,只选择一个特征子集可以在更短的时间内训练出简化的模型。不幸的是,通常的情况是,特征选择的任务本身非常慢,这减少了更快的模型训练的好处。但是,这可以通过缓存所选要素并因此仅应用一次要素选择来进行优化。
绝大多数特征选择方法的另一个缺点是,它们任意地寻求识别问题的唯一解决方案。然而,在实践中,经常会有多个预测甚至信息等价解存在。尤其是在潜在问题中存在固有冗余的领域中。分子生物学就是这样一个例子,通常存在多种解决方案。
虽然单个解决方案对于构建最佳预测模型是可接受的,但是当特征选择被应用于知识发现时,这是不够的。事实上,它甚至可能是误导。例如,如果一项医学研究中的几组风险因素对一个事件的总体预测能力相同,那么只返回其中一组并声称其余的都是多余的就是误导。
作者照片: 对 JADBio 进行的用于马铃薯品质预测的代谢生物标志物的分析产生了多种解决方案(Steinfath 等. Plant Biotechnol J. 2010 Oct8(8):900–11).在第五个特征中区分了 4 个等价的解决方案。
定论
简而言之,我建议是的,我们需要在我们的数据分析管道中进行特征选择。即使本文中列出的优点和缺点并不详尽,我认为使用特性选择的好处已经很明显了。特别是在任务包含高维数据的情况下,知识发现和/或不同特征与不同成本相关联是优先的。事实上,我们能做的最好的(这是我们在 JADBio 中的选择;www.jadbio.com))用于测试有无功能选择的配置。当然,这伴随着我们需要考虑的任何资源限制,但是在任何情况下,我们都可以尝试各种技巧。最后,不要忘记“赢家的诅咒”无时无刻不在困扰着你,你创造了无数的模型(你能信任 AutoML 吗?)。
你在训练一个 AI 的时候会考虑公*的成分吗?
以下是我对为什么公*在人工智能/人工智能领域很重要的看法
普里西拉·杜·普里兹在 Unsplash 上的照片
我想你花了无数个小时在一份工作申请上,却发现申请被拒绝了,不是因为缺乏技能,而是因为申请人是女性。
是的,这是不公*的。
这实际上发生在亚马逊,当时他们正在测试一个自动化系统,通过使用人工智能的帮助,从一群申请人中选择最佳候选人。人工智能是根据 10 年来收集的简历进行训练的,训练数据中的大多数简历都属于男性。这影响了人工智能在选择过程中偏向男性。此外,它描绘了科技行业男性主导的可悲现实。你可以在这里阅读这篇文章的更多内容。
随着人工智能发展的增长,许多复杂而耗时的任务正在通过预测模型实现自动化。每年都有一套新的复杂的人工智能架构被建立起来,这种架构的决策过程的可解释性正在下降。这增加了认可机构有偏见或“不公*”的原因。
敏感特征
当 AI/ ML 模型在包含诸如性别、种族、民族等敏感特征的数据上被训练时,将不会被证明是公*的模型。他们会捕捉数据中存在的差异,并在他们的预测中表现出来。因此,我们通过在训练过程中使用敏感特征,将偏见(性别偏见、种族偏见等)添加到模型中。
可能存在这样的情况,当您不使用任何显式敏感特征来训练模型时,模型中仍然会有一些偏差。这可能取决于其他因素,如数据不*衡。如果模型的可解释性很低,这个问题就很难解决。我们需要了解这些数据是如何被用来做最终预测的。
偏见
克里斯蒂安·卢在 Unsplash 上的照片
一般来说,偏见是一种潜在的假设,我们利用它来做决定。人类天生就有偏见,这种偏见使我们能够更快地做出决定。当人工智能从数据中学习时,我们从以前的经验中学习,并做出一些心理构建来做出决定。这些偏见中的一些实际上帮助我们做出好的决定,但有时没那么大帮助。
对于一个人工智能模型,我们希望消除不想要的偏见,并使模型在做出判断/决定时公*。
这些有偏见的人工智能的更多例子可以在下面的文章中看到:
- 研究揭露脸书招聘广告中的性别偏见脸书的人工智能向特定性别展示了某些广告,如向男性观众展示多米诺的送货工作等。
- 数百万黑人受医疗算法中的种族偏见影响一种算法被开发出来给病人分配风险分数,其中倾向于给黑人分配比同样患病的白人低的风险分数。
- 一种流行的算法在预测犯罪方面并不比随机选择的人更好COMPAS(替代制裁的矫正罪犯管理剖析)给一个人分配一个分数,表明犯罪的风险。
- 抱怨推特图像裁剪算法存在偏见用户质疑推特算法在裁剪图像时存在性别和种族偏见。
正如你所看到的,有很多例子表明有偏见的人工智能加剧了社会不*等。从道德和伦理的角度来看,这是一个问题。
偏差来源
马库斯·斯皮斯克在 Unsplash 上的照片
从我们人类的角度来看,我们说人工智能算法是有缺陷的,无法解决有偏见的预测问题。
相反,人工智能会认为已经建立的模型是基于实际现实的。就像,人工智能会从我们提供给它的数据中学习东西。如果偏差固有地存在于数据中,那么预测模型也会有偏差。
因此,预测偏差的一个来源来自数据本身。例如,考虑一下亚马逊的自动简历选择器。如果输入数据由男性候选人的许多简历示例组成,则最终的预测/决策将倾向于接*男性候选人(因为该算法是在男性候选人数据多于女性候选人数据的基础上训练的)。
也许,我们可以通过向人工智能模型提供由相同数量的两性简历组成的数据来调整这些有偏见的预测。在接下来的章节中,我将讨论一些技术来消除有偏预测模型。
示例数据集
我参加了一个关于 NLP 多类分类任务的 Kaggle 竞赛,我们得到了各种工作的描述,我们的目标是将它们与 28 种不同的工作进行匹配,如牙医、教师、律师等。
注:本次 Kaggle 比赛数据采集自 CommonCrawl ,免费使用。
在这里,我们有一个明确的性别特征分配给每个职位描述。在工作描述的文本中存在一些隐含的性别信息,如代词(他/她),性别词(母亲,父亲,儿子,女儿等)。
数据集(来源:作者)
从数据集的截图中,我们可以看到职位描述中的性别代词,也是一个性别特征。
让我们检查数据集中各种工作之间的性别差异。
女性较多的十大工作示例:
女性占主导地位的工作实例(来源:作者)
我对每个职位的数据集中出现的例子进行了比例分析(男性:女性)。从图中,你可以看到像 营养师、护士、教师 这样的工作似乎更以女性为主。
像 诗人 和 记者 这样的工作,男女比例接* 1,我们看到男女比例几乎相等。
男性较多的 10 大工作:
男性主导的工作实例(来源:作者)
如果我们访问光谱的另一端,我们看到像 说唱歌手外科医生DJ这样的工作大多是男性主导的。
这显示了我们将输入到人工智能模型中的差异。该模型将根据这些差异和性别敏感特征进行训练,以成为有偏差的预测模型。
尽管我们会提出一个在测试集上表现相当好的精确模型,但它不会是一个公*的模型。
在这种情况下,偏差可能会从以下方面蔓延到模型中:
- 性别特征
- 描述中隐含的性别信息
- 高度不*衡的工作类别,如营养师,说唱歌手。
有什么解决办法吗?
约翰·施诺布里奇在 Unsplash 上拍摄的照片
有一些方法可以解决这个问题,但我们不能完全消除这个问题。
我们(我的团队)在比赛中研究的方法:
- 删除性别特征,不要在培训过程中考虑它。
- 删除所有与性别有关的词汇,如代词、名词、形容词等。
- 在训练模型之前,去除单词嵌入的偏差。
- 语言反向翻译。
总之,我们的目标是通过执行文本预处理,在稍后阶段,在我们训练模型之前,消除单词嵌入的偏见,从源头上消除性别偏见。
我们用于去偏置的一些 python 库:
- Debiaswe:尽量减少单词嵌入的性别歧视
- 双硬 Debias:为性别偏见缓解定制单词嵌入
我们能够完全消除偏见吗?
不幸的是,没有,没有完全,但我们设法在某种程度上减少了偏见。
公*性度量
对于这个特定的数据集,我们使用宏观不同影响作为性能指标来计算模型的公*性。对于每项工作,我们采用最大性别数与最小性别数的比率。
公*指标(来源:作者)
例如,要计算 会计 工作的不同影响,我们只需用最大性别数(1992)除以最小性别数(1129)。所有工作都是如此。
要计算宏观不同影响,只需取所有单独不同影响分数的*均值。
鸣谢:在本次比赛的 78 支参赛队伍中,我的队伍在 Kaggle 私人排行榜排名中名列第八。
结论
使用人工智能自动化复杂的任务令人印象深刻,但我们应该首先解决由此产生的问题,即有偏见的预测模型。人工智能中的公*性最*得到了很多关注,更多的研究正在进行中,以使模型尽可能公*。可解释的人工智能(XAI)也是一个重要的主题,它可以指出模型中偏差的来源。
有一些研究希望提高人工智能情况下的公*性:
- Fairness 360:IBM 开发的一个 python 库,用来减轻偏见,增加模型的公*性。
- 公*流:脸书为了减少人工智能中的偏见而使用的工具。
- ML-Fairness Gym:Google 开发的一个工具包,用于研究人工智能中的公*性。
最后,人工智能中的公*已经引起了业内大玩家的关注,更多的研究将被投入到这个主题中。所以,如果你还没有看过的话,绝对值得你花时间去看看。
如果你看到了这篇文章的这一部分,感谢你的阅读和关注。我希望你觉得这篇文章内容丰富,并且可以通过 LinkedIn 、 Twitter 或 GitHub 联系到我。
你讨厌 Python 有多慢吗?这就是你如何让它跑得更快!
将具有挑战性的 任务委托给另一名玩家
Python 是一种出色的编程语言,也是作者的首选语言。在我看来,当你慢慢进入计算机科学和编程世界时,Python 是最好的学习语言。
然而,每种编程语言都有它的优点和缺点;这就是为什么我们有这么多这样的人。不同的用例需要不同的设计和实现。正如我们所说,没有放之四海而皆准的解决方案。
Python 是一种用户友好、易于学习、免费使用、可移植且易于扩展的编程语言。它可以适应您的编程风格和需求,涵盖各种风格,从面向对象(OOP)到函数式编程方法。
另一方面,Python 是一种解释型语言,它可以设置代码执行速度的上限。它不太适合移动开发,它有内存问题,而且,因为它是一种动态类型的语言,您会在运行时发现大多数错误。
但是并不是所有的希望都破灭了。我认为最关键的问题是速度。但是为什么更快的运行时间如此重要呢?考虑机器学习的用例;你需要快速试验和迭代。当您处理大数据时,让您的功能在几秒钟而不是几分钟内返回至关重要。如果你刚刚发明的新的闪亮的神经网络架构不那么好,你应该能够迅速转向你的下一个想法!
为了解决这个问题,我们可以用另一种语言(例如,C 或 C++)编写要求苛刻的函数,并利用特定的绑定从 Python 调用这些函数。这是许多数字库(例如 NumPy、SciPy 等)都有的。)或深度学习框架(如 TensorFlow、PyTorch 等。)用 Python 做。因此,如果你是一名数据科学家或机器学习工程师,想要调用 CUDA 函数,这个故事适合你。开始吧!
Learning Rate 是为那些对 AI 和 MLOps 世界好奇的人准备的时事通讯。你会在每周五收到我关于最新人工智能新闻和文章的更新和想法。在这里订阅!
编组的
在这个旅程中,我们必须采取的第一步是理解编组是什么以及它是如何工作的。来自维基百科:
编组是将对象的内存表示形式转换为适合存储或传输的数据格式的过程。
为什么这对我们的主题很重要?为了将数据从 Python 移动到 C 或 C++,Python 绑定必须将数据转换成适合传输的形式。
在 Python 中,一切都是对象。整数使用多少字节的内存取决于您安装的 Python 版本和操作系统,以及其他因素。另一方面,C 中的一个uint8_t
整数总是使用总内存的 8 位。因此,我们必须以某种方式调和这两种类型。
编组是 Python 绑定为我们处理的事情,但是在某些情况下我们可能需要干预。这个故事不会出现这种情况,但我们会在后面的文章中遇到。
管理内存
c 和 Python 管理内存的方式不同。在 Python 中,当你声明一个对象时,Python 会自动为它分配内存。当您不需要该对象时,Python 有一个垃圾收集器,可以销毁未使用或未引用的对象,将内存释放回系统。
在 C 语言中,情况完全不同。是你,程序员,必须分配内存空间来创建一个对象,然后又是你,必须将内存释放回系统。
我们应该考虑到这一点,在语言障碍的同一侧释放任何我们不再需要的内存。
简单的例子
我们现在已经到了准备下水的时候了。当您完成这一部分时,您将准备好开始使用 Python 绑定和 c。这绝对是一个初学者教程,但我们将在后面的故事中更深入地研究更复杂的示例。
你需要什么
对于这个故事,我们需要两样东西:
- Python 3.6 或更高版本
- Python 开发工具(例如,
python3-dev
包)
C 源代码
为了简单起见,我们将创建并构建一个将两个数相加的 C 库。复制下面的源代码:
接下来,我们需要编译源代码并构建一个共享库。为此,执行下面的命令:
gcc -shared -Wl,-soname,libcadd -o libcadd.so -fPIC cadd.c
这个命令应该会在您的工作目录中产生一个libcadd.so
文件。现在,您已经准备好进入下一步。
用ctypes
窥探一个 C 库
ctypes
是 Python 标准库中创建 Python 绑定的工具。作为 Python 标准库的一部分,它非常适合我们的初学者教程,因为您不需要安装任何东西。
要从 Python 脚本执行 C cadd
函数,请复制下面的源代码:
在第 7 行,我们创建了一个 C 共享库的句柄。在第 12 行,我们声明了 C cadd
函数的返回类型。这是至关重要的;我们需要让ctypes
知道如何封送对象来传递它们,以及什么类型可以正确地解封它们。
第 14 行的y
变量也是这种情况。我们需要声明这是类型float
。最后,我们可以让x
保持原样,因为默认情况下,ctypes
认为一切都是整数。
我们可以像执行任何其他 Python 脚本一样执行这个脚本:
python3 padd.py
结果是一种神奇!
In cadd: int 6 float 2.3 returning 8.3
In Python: int: 6 float 2.3 return val 8.3
恭喜你!你从 Python 中调用了一个 C 库的函数!
结论
Python 是一种用户友好、易于学习、免费使用、可移植且易于扩展的编程语言。
然而,它也有弱点,其中最突出的是速度。为了解决这个问题,我们可以用另一种语言(例如,C 或 C++)编写要求苛刻的函数,并利用特定的绑定从 Python 调用这些函数。
在本文中,我们使用了ctypes
,这是一个 Python 库。我们能够从 Python 代码中调用 C 库并返回结果。我知道这个功能没有做任何要求,但这只是一个演示。看看后面的文章能不能做点更有挑战性的!
关于作者
我的名字是 Dimitris Poulopoulos ,我是一名为 Arrikto 工作的机器学习工程师。我曾为欧洲委员会、欧盟统计局、国际货币基金组织、欧洲央行、经合组织和宜家等主要客户设计和实施过人工智能和软件解决方案。
如果你有兴趣阅读更多关于机器学习、深度学习、数据科学和数据运算的帖子,请在 Twitter 上关注我的 Medium 、 LinkedIn 或 @james2pl 。
所表达的观点仅代表我个人,并不代表我的雇主的观点或意见。
你有数据科学超能力吗?
在你的数据科学之旅的某个地方,你可能已经找到了某样东西——一项技能、一个利基、一个复杂的工作流程——这让你感到特别满意和有收获,并成为了你的东西。也许你根本不相信超能力,认为真正的优秀在于你多年耐心积累的一整套技能和经验。不管你的立场如何,我们都同意学习和探索有助于我们发现让我们成功的事物。因此,我们希望通过阅读本周的推荐文章,你能学到一些新的东西(关于数据科学和人工智能——或者你自己)。
- 赶在艾身边最新最紧急的对话 。到 2021 年,我们已经走过了将*三分之二的路程,现在是评估人工智能在哪里,以及它的未来面临的最大问题是什么的最佳时机。这正是 TDS 播客主持人 Jeremie Harris 和 Let's Talk AI 播客团队在最*一期节目中所做的,涵盖了模型偏见、所有权和多模态学习等主题。
迈克尔·克罗尔在 Unsplash 上拍摄的照片
- 学习如何根据实验数据给出令人信服的见解 。如果你有时发现自己盯着屏幕,不知道如何将你的发现塑造成任何像样的东西,玛蒂娜·波基亚里的指南是必读的— 它提供了一个通用的协议,在分析你的测试和实验结果时可以遵循(以及让你开始的相关代码)。
- 探索 Python 中时间序列分析的新框架 。正如 Khuyen Tran 在她的文章开始时指出的,时间序列是数据科学中一个至关重要的子领域,允许你理解关键的统计数据和异常,检测回归,并预测未来趋势。Kats 是 Python 中时间序列分析的一站式商店,Khuyen 展示了它如何能够显著地简化过程。
- 发现你需要知道的关于偏最小二乘 的一切。 Joos Korstanje 对偏最小二乘回归魔力的深入探究是一篇包含大量信息(既有高层次的又有实际操作的)又不会让读者不知所措的帖子的有力例子。慢慢来,你会了解什么用例特别适合偏最小二乘法,并且有一堆代码需要修改以满足你未来的需求。
- 寻找向跨学科受众传达你的工作价值的新途径。作为一名数据科学家, Kristina Georgieva 提醒我们,“你会发现自己不得不与不同类型的受众交谈,其中一些人的知识和背景与其他人不同。”让你的工作流程、沟通方式和语言适应共享的专业空间是至关重要的,Kristina 的指南将帮助你*稳有效地走出“数据科学洞穴”。
- 拿数据玩玩 。你听说过 SLICED 吗,这是最*推出的数据科学竞赛-游戏-展示-黑客马拉松?TDS 编辑 Elliot Gunn 与创作者 Nick Wan 和 Meg Risdal 谈论了这一概念的起源,他们在这一过程中对他们社区的了解,以及他们如何为各种各样的数据科学家建立一个热情、包容的空间。
- 阅读人工智能的下一个前沿 。Gadi Singer 在 TDS 上的最后几篇文章描述了日益强大的机器智能的必要架构。在他最*的贡献中,Gadi 介绍了 Thrill-K,这是一个人工智能系统的拟议架构蓝图,它使用了他以前写过的三个知识级别(3LK)——即时、待机和检索外部知识。
在充满奥运激情和疫情不确定性的一周里,感谢您花时间讲述我们的故事,感谢您的支持使我们的工作成为可能。
直到下一个变量,
TDS 编辑器
我们策划主题的最新内容:
入门
- 与研究科学家一起创建一致的产品团队作者伯拉克·阿克布卢特
- Sejal Dua用 SQL 为 2021 年东京奥运会做准备
- Pydash:一个丢失 Python 工具的厨房水槽作者 Khuyen Tran
实践教程
- 如何用相扑模拟城市网络上的交通作者塞犍陀·维维克
- Eirik Berge用漂亮的类型提示使你罪恶的 Python 代码现代化
- 单元 5)进化编程作者布兰登·摩根
深潜
思想和理论
- 由罗霍拉·赞迪用变形金刚生成的热门语言
- 为什么你应该使用 PHATE 进行降维
- 功能数据分析(FDA)简介作者 Florian Heinrichs
通过吸引国际人才加速瑞典应用人工智能的发展
为了加强瑞典在人工智能领域的吸引力,人工智能瑞典公司与三个合作伙伴组织一起推出了人工智能人才计划之眼。syl wia Majchrowska分享她是如何在 AI 人才计划中脱颖而出的。
我过着有序稳定的生活,在一个与计算机视觉领域相关的研究项目中有一份专业工作(然而,合同将于 10 月底到期),还在攻读物理学博士学位(正在最后冲刺,目前主要专注于完成一篇论文)。然而,在 9 月初,整个稳定并不重要,因为我收到了一封电子邮件,开头是一句话:
不到一个月,我整理好未了的事情,收拾好行李箱,动身前往瑞典。不过这个故事还是从头说起吧。
你对人工智能有眼光吗?
人工智能之眼项目是一个为期 18 个月的独家项目,由瑞典人工智能协会与阿斯利康大学、Sahlgrenska 大学医院和 Zenseact 共同创建,得到了人工智能妇女协会的支持。我正在参加人工智能之眼的第一个试点项目,这是一个面向人工智能未来领导者的研究生项目,于 2021 年 10 月开始。总共有 4 名候选人被选中参加这个项目——我来自波兰,另外 3 名优秀的候选人来自世界各地:西班牙、伊朗和厄立特里亚。在这个项目中,我与来自西班牙的女孩桑德拉搭档,我将与她轮流工作——在每家公司工作 6 个月。
第一次和我来自人工智能项目的新朋友见面。图片作者。
人工智能瑞典的第一步
该计划的前两周是位于哥德堡的 AI 瑞典组织的入职流程(第一周,我主要在网上准备,而我在波兰还有一些未完成的任务)和斯德哥尔摩(第二周的前 3 天)。在我踏上这个斯堪的纳维亚国家的第一步时,项目主管陪着我。作为该项目的参与者,我获得了有竞争力的薪酬、住房解决方案和积极融入瑞典社区。我现在的老板负责把我从机场带到我的新公寓(也是他帮我找到的)。人工智能项目经理的眼睛在招聘过程的每一步都做了非常好的组织,并在一开始就照顾我们瑞典!他们准备了一个精彩的介绍日项目,让我们有机会见到来自瑞典 AI 公司和合作伙伴公司的了不起的人。
斯德哥尔摩三日游计划。图片作者。
来认识新面孔。我有机会见到了管理该组织的人员、其目标和令人感兴趣的使命,即
“为了我们的社会、我们的竞争力以及生活在瑞典的每个人的利益,加速人工智能的使用”。
为了实现这一使命,该组织开展了许多活动,培训人工智能领域的未来领导者和人才。AI Sweden 创建了数据工厂和 Edge Lab,通过提供积累资源(硬件、软件和数据)的空间,在一个共同的操场上连接其合作伙伴!
但是技术和资源还不足以实现人工智能的转变。公司正在培训来自超级岛的天才学生参加人工智能改变代理程序。人工智能变革代理人这一新角色背后的主要思想是,通过了解人工智能的可能性和挑战,学习如何进行组织变革,以找到新的工作方式,因为每个组织都是不同的——有不同的目标、使命,并处于不同的水*。理解如何在这种环境下采取行动,通过使用人工智能使员工的工作变得更容易,这种能力非常重要。
与超级岛学生的团队建设活动。图片作者。
加速人工智能的另一个关键限制因素是数据隐私限制,特别是在医疗保健领域,我的项目专业部分始于 Sahlgrenska 大学医院。Sahlgrenska 大学医院旨在通过建立由 Magnus Kjellberg 领导的人工智能能力中心来关注人工智能。我参与的第一个项目与合成数据生成和联合学习有关,在我看来,这在开发该领域的人工智能方面起着至关重要的作用。此外,我们计划指导一些与任务相关的硕士论文项目。我相信这是一个很好的机会去探索更多,也发展我自己的软技能。
融入瑞典社会
但是人活着并不仅仅是为了工作。我到达瑞典的时间有点晚(我需要额外的一周时间来准备移居到这个国家),我的项目同事证明是不可替代的,他们在两天内就带我参观了哥德堡。他们以当地人的身份带我参观了这座城市。我们参观了 Haga,这是市中心最古老的街区之一。在这里,我第一次参加了一个瑞典 fika(一个喝咖啡的休息时间,是的,有一个特别的词来形容那个时间!)在瑞典。哈加街上满是糖果和咖啡馆。我尝了一个盘子大小的肉桂面包,味道好极了!
瑞典 fika!图片作者。
在入职过程中,我们有很多机会加深相互了解。其中之一是在 Zensacts 成立一周年之际,在哥德堡市中心的游乐园 Liseberg 玩了一整天。实际上,Liseberg 的整个景色是为即将到来的万圣节准备的——充满了南瓜和可怕的装饰。我们玩游戏机很开心,还可以尝试过山车——螺旋。我们也有机会聆听了首席执行官的激励性演讲,并在音乐的伴奏下与 Zensact 员工共进晚餐。
我和朋友在利斯堡的正式餐厅。图片作者。
甚至我现在居住地的周边都有利于与人融合。这座建筑位于两个城市的中心——哥德堡和蒙达尔。它位于靠* Sahlgrenska 大学医院和阿斯利康 R&D 中心。此外,靠*自然保护区让我有机会在工作之余呼吸新鲜空气。湖边专门准备了烧烤的地方,让我们可以在温暖的日子里和邻居一起吃饭。
自然保护区里山与海交汇的地方。图片作者。
了解了瑞典语,融入瑞典社区将变得更加容易——到目前为止,每次去商店都与一次难忘的冒险和对食品杂货的猜测有关。为了学习瑞典语,我们每周五都组织课程。我相信整个项目将会是一次奇妙的经历,并永远改变我的生活。
原载于 2021 年 11 月 8 日https://majsylw . netlify . app。
你知道 Python 有内置数组吗?
大家都用“List”,但是 Python 确实有“Array”
当我们在 Python 中说“数组”时,很可能我们指的是“列表”。确实如此。Python List 是 Python 中一个非常强大灵活的容器类型。虽然有点令人困惑,但我们总是在 Python 中使用 List,就像在大多数其他编程语言中使用等价的概念“数组”一样。
或者,你可能会说 Python 确实有“数组”,但它来自 NumPy 库。那也是真的。当我们在处理数学问题时,通常是多维数组或矩阵,NumPy 数组是一个必备的工具。
然而,以上两者今天都不会在本文中介绍。我要介绍一下 Python 中的“数组”。没错,既不是“List”,也不是 NumPy“Array”,而是 Python 内置的“Array”。你知道有这种东西吗?
Python 数组基础
照片由pineapple 22 在 Pixabay 上制作
该数组是内置的 Python,这仅仅意味着您不需要下载和安装它,因为它肯定附带了本机 Python。但是,就像“datetime”等其他内置库一样,您仍然需要导入它。
import array
在我们使用数组之前,重要的是要理解它仍然与大多数其他编程语言中的典型数组概念有很大的不同。
最特殊的特征是它必须用类型代码来定义。我们可以列出所有可用的类型代码如下。
array.typecodes
每个字符对应于数组将包含的特定数据类型。类型代码和相应的类型可在以下官方文件中找到。
Python 官方文档中的表格:[array](https://docs.python.org/3.9/library/array.html#module-array)
数组的类型代码必须在数组实例化时定义。这也意味着
Python 数组只能包含某种类型的元素。
这和列表很不一样,对吧?对于 Python 列表,我们可以灵活地将任何对象放在一个列表中,而不用担心它们的类型。
现在,让我们定义一个数组。我想再次导入这个库,如下所示,因为我不想让array.array()
出现在我的代码中,就像datetime.datetime.now()
有点难看一样。
from array import array
我们可以如下定义一个字符数组。
arr = array('u', 'abcdefg')
参考上表,类型代码“u”表示该数组包含 Unicode 字符。所以,这些字母将被分开处理,现在这个数组中有 7 个元素。
数组的功能
好的。这个内置数组好像挺独特的。因此,它也有一些相当独特的特点如下。
1.获取类型代码
因为类型代码对于一个数组来说非常重要,它决定了数组可以有什么样的元素。我们可以通过访问它的属性typecode
来获取类型代码。
2.获取数组项的大小
如上所述,与 Python List 不同,数组要严格得多。也就是说,数组包含的项必须是相同的类型。因此,项目的大小也将相同。我们可以检查每个项目的大小。
arr.itemsize
不要混淆这不是数组的长度,而是数组中单个元素的大小。
3.计算出现的次数
像 Python List 一样,这个数组也有count()
函数,它将计算数组中某个项目的出现次数。可能你都不知道 Python 列表有这个?试试吧:)
arr.count('b')
4.追加和扩展
如果我们想在数组中添加新的元素,我们可以使用append()
或者extend()
函数。前者将添加单个元素,而后者可以一次添加多个项目。
arr.append('h')
arr.extend('ijk')
5.操纵索引
作为一个数组,我们必须能够使用索引对它做一些事情。是的,我们可以得到某个项目的索引。请注意,如果项目在数组中重复,我们将只有第一次出现的索引。
arr.index('c')
此外,我们可以在某个索引处插入一个条目。
arr.insert(2, 'x')
如果我们想删除某个索引处的某个项目,我们可以使用pop()
功能。
arr.pop(2)
将该项弹出数组后,原始数组将被就地更新。
我们也可以如下反转数组。
arr.reverse()
6.要列出的数组
当然,我们不必局限在 Array 的范围内,因为它是相当受限制的。如果我们想把它转换成一个普通的 Python 列表,这是很容易做到的。
my_list = arr.tolist()
什么时候用 Python 数组?
MustangJoe 在 Pixabay 上的照片
我想这是最重要的问题。如果 Python 数组比 Python 列表有更多的限制,为什么我们需要它呢?换句话说,我们应该在什么时候使用它?
简短的回答是:您可能永远不会使用它,因为列表和 NumPy 数组通常是更好的选择。这也是很少有人知道 Python 有内置数组类型的原因。
但是,当一个东西被限制的时候,它一定会带来一些好处。那就是所谓的“取舍”。在这种情况下,Python 数组的大小将小于 Python 列表。
我们来举个例子。创建了一个包含 100,000 个整数的 Python 列表。然后,将其转换为 Python 数组。
list_large = list(range(0, 100000))
arr_large = array('I', list_large)
让我们看看它们的大小。
arr_large.__sizeof__()
list_large.__sizeof__()
可以看出,数组的大小只有链表大小的一半左右,甚至小于一半。这是因为数组的类型码是固定的,每一项的字节数也是固定的。它失去了一些灵活性,但开销更少。
Python 数组使用的内存比 Python 列表少。
摘要
在本文中,我介绍了 Python 中内置的 Python 数组。它不是我们非常熟悉的 Python 列表。在数学计算和建模方面,NumPy 数组也不是非常强大。它内置于 Python 中,有很大的局限性,因为它只能包含相同类型的元素。
https://medium.com/@qiuyujx/membership
如果你觉得我的文章有帮助,请考虑加入灵媒会员来支持我和成千上万的其他作家!(点击上面的链接)
需要特色店吗?
特征商店使生产更精确的 ML 模型变得更容易和更便宜。
来源: alexdndz /Adobe Stock
一个机器学习模型只会和它输入的数据一样好。更准确地说,一个模型的好坏取决于它被赋予的特性。
特征是从原始数据点或几个原始数据点的集合中提取的有用的度量或属性。模型中使用的特定特征将取决于模型试图做出的预测。例如,如果模型试图预测欺诈性交易,相关特征可能包括交易是否发生在国外,购买量是否大于*时,或者购买量是否与给定客户的典型支出不符。这些特征可以从诸如购买的位置、购买的价值、*均购买的价值以及进行购买的特定用户的合计消费模式等数据点来计算。
虽然训练 ML 模型的数据是最重要的,但准备好好的数据是数据科学家最具挑战性的任务之一。事实上,数据科学家*均 80% 的时间都花在了数据准备上。这包括收集数据,清理和组织数据,并将其工程化为特征。这项工作是手工的、单调的、乏味的: 76% 的数据科学家认为数据准备是他们工作中最不愉快的部分。也许最重要的是,这项工作可能是不必要的——整个公司的许多数据科学家最终都在埋头研究数据,以计算公司另一位数据科学家已经发现的相同特征。此外,数据科学家每次想要部署模型时,都要花费大量精力来复制相同的特征工程管道。
如果这看起来效率低下,那是因为它确实如此。小企业和领先的人工智能公司正在转向功能商店来解决这个问题。
什么是特色店?
来源:作者
一个 特征库 是一个专门用来自动输入、跟踪和管理机器学习模型中的数据的系统。功能存储计算和存储功能,使它们能够在整个公司范围内被注册、发现、使用和共享。要素存储可确保预测要素始终保持最新,并以一致的方式维护每个要素值的历史记录,以便可以对模型进行训练和重新训练。具体而言,特征存储包括:
- 自动化数据转换
- 一致特征注册
- 模特培训和再培训
- 实时特征服务
- 模式监控。
自动化数据转换
要素存储管理将原始数据转换为要素值的数据管道。这些可以是一次聚合数 Pb 数据的预定管道(如计算大型零售商的每个客户 30 天、60 天和 90 天的*均消费金额),也可以是由事件触发并即时更新特征值的实时管道(如在特定客户每次刷信用卡时更新其今天的消费总额)。
一致特征注册表
功能注册表是一个中央界面,用于对组织内的功能定义进行编目。要素注册表包含标准化的要素定义和关联的元数据,作为组织的单一信息源。
功能存储使搜索可用功能和功能定义变得简单明了。它向数据科学家公开 API 和 ui,以查看当前可用的功能、管道和正在生产模型中使用或正在开发中的训练数据集。然后,数据科学家可以挑选用例所需的特性,并将它们合并到模型中,而无需任何额外的代码。
模特培训和再培训
特征存储将较旧的特征组织到时间序列数据库中,以便在训练模型时,所有示例都具有同时对齐的特征。由于所有历史要素值都与其最新值一起存储,因此要素存储可为要素生成完整的训练数据集,并将其与训练标注正确对齐。随着这些特征的更新,特征库可以以完全相同的方式为模型再训练生成更新的训练数据集。
实时特写服务
特征存储为机器学习模型提供由最新特征值组成的单一特征向量。例如,如果应用程序想要向用户推荐特定产品,模型可能需要知道用户在特定支出类别中花费的*均金额,以及在过去 48 小时内花费在购物上的总时间。特性存储将拥有这些度量标准的最新值,可立即用于模型,而不必运行数据管道来计算它们。
模式监控
假设模型的所有先前预测都与当时模型的输入一起存储,则将这些要素(从要素存储中收集)与更新的标注(当它们可用时)与模型预测进行比较就变成了一个简单的 API 调用。这允许用户监控模型的性能,并跟踪任何特征漂移、模型预测漂移和模型精度(当标签可用时)。因为特征存储以时间一致的方式保存所有最新的特征值和所有历史值,所以使用特征存储监控模型很容易。
特色店如何提高生产力和业绩?
来源: alexdndz /Adobe Stock
通过实现以下功能,特征存储提高了数据科学家的工作效率,并改善了企业中 ML 模型的性能:
- 特征重用
在典型的数据科学工作流中,新项目需要收集数据,将其转换为可用的功能,培训,然后部署模型。因为特性不容易共享,多个团队在各自的筒仓中经常多次重复相同的特性工程工作。
有了要素库,数据科学家可以通过探索现有的要素,立即着手解决新问题。在许多情况下,用于过去模型的特征,或者由其他数据科学家构建的特征,可以在你的下一个机器学习项目中重用。
如果还没有所需的功能,数据科学家可以随时添加新功能,从而在将来为自己和他人加强功能存储。随着这一迭代过程的发展,它的价值通过加速数据科学和简化模型部署而增加。
2。特征一致性
缺乏一致的方法来计算特征会导致不同数据仓库之间的模型差异很大。例如,在一家零售公司中,一个团队可能通过从销售额中减去退货来计算“总客户收入”,而另一个团队只使用销售额来计算。两者都是有效的指标,但如果它们都被称为“总客户收入”,则会导致不同数据管道中计算的指标不一致。
功能存储的单一功能注册表为功能提供了一个中心位置,每个功能都以单一方式计算,因此不会再有混淆。
3。时间点正确性
用于训练的特征值组必须是在模型被训练的事件发生时已知的值。这确保了当模型用于预测时,它使用的输入特征值与其训练一致。特征存储通过产生训练数据集来解决这个问题,该训练数据集具有在建模事件的时间点从每个特征集的历史中获取的时间一致的特征值。
4。模型可解释性和治理
有了特性库,您可以轻松地识别模型训练的数据,并将其与部署的模型实际输入的数据进行比较。这使得迭代、训练和调试模型变得更加容易,因为您能够确切地看到您在什么时候使用了什么数据。此外,端到端的沿袭确保您可以回答为什么您的模型在过去的任何时间点做出某些预测的问题。
特色店的好处
来源: alexdndz /Adobe Stock
数据科学家少之又少,而且价格不菲。通过消除重复和不必要的工作来提高数据科学生产力,意味着您可以用现有的员工在更短的时间内制作更多的模型。
通过将数据新鲜度提升到一个全新的水*,特征存储可以实现更精确的模型。通过将数据管道从 ML 模型中分离出来,可以在需要时立即检索可能需要数小时计算的基于聚合的大型特征。这使得实时模型能够访问它们原本无法访问的特征值。通过访问实时数据,模型可以根据现实世界中正在发生的事情进行更准确的预测,而不是停留在昨天的数据上。
功能商店允许企业人工智能以前所未有的方式扩展机器学习。特征商店不仅使你的模型尽可能的精确,它们还为你的 ML 团队提供了一个组织结构,使他们的工作更加容易和愉快。通过特色商店让您的公司在竞争中领先。
如果你想看一个特色商店的运作,看看这个 5 分钟的视频。
需要数据科学硕士学位吗?你问错问题了。
是的,有大量免费和低成本的资源。然而,当你考虑数据科学时,你应该确定你问的是正确的问题。
蒂姆·高在 Unsplash 上拍摄的照片
在众多的*台上,有大量的文章解释为什么“进入数据科学不需要硕士学位!”接下来通常是对可用资源的某种回顾,也许是作者关于他们是如何做到的故事,以及许多尝试的鼓励。有时,大学里会有不那么隐晦的讽刺。这对未来的数据科学家来说是一种解放!
让我们作为数据科学家来探讨这个话题。
如果“不!不需要学历!”是答案,是什么问题?这里的问题如下:“没有硕士学位的人有可能通过自学和社交来获得数据科学领域的工作吗?”
这是我们在失败的数据科学项目中看到的第一个问题的一个很好的例子:对错误的问题给出一个全面而详细的答案。我在课堂上经常谈到这一点,在我的职业生涯中也亲身经历过。
正在回答的问题是一个存在的问题。我再重申一遍:在整个数据科学领域,有没有人是数据科学家,却没有获得硕士学位?希望现在你看到了问题所在。当然有。可能有人连本科学位都没拿到。可能有人从未亲自或在线参加过正式的编程课程。
相反,让我们想一想,当有人点击这样的文章标题时,他们在问什么真正的问题。大概是这样的:
具有我这种背景、气质和自律精神的人通过自学、在线证书和职业关系网成为成功的数据科学家的概率有多大?
所有这些文章都可以说,假设你的背景、气质、自律都和作者一样,就是“概率不为零”这可能让人感觉不那么自由。
事实上,一个充满希望的数据科学家可能会问一个更精确的问题:
鉴于我目前的就业状况,学历背景,个人家庭情况,气质,自律,想在数据科学领域有一个成功的职业生涯,应该选择什么路径?
这是一个非常非常难回答的问题,但它非常接*真正的问题。这里有很多资源,我自己也写了一些。这里有一个:
然而,硕士学位并不适合所有人,这是事实。如果不适合你,以下是我对思考的看法:
不过,当你阅读时,请考虑我最*发现的来自 Gartner Research 的统计数据:当前 71%的数据科学家拥有硕士学位或博士学位。
不过,这个数据点也不能回答你的问题。例如,它并没有说,如果你没有高等学位,你只有 29%的机会成为数据科学家。我们知道,许多早期的数据科学家都拥有一系列定量领域的高级学位。这并不意味着新进入该领域的人也会如此,鉴于对数据科学家的需求,我预计未来几年这一比例将下降 71%,可能会大幅下降。
好消息是你不需要对这些概率进行精确的估计。然而,71%的统计数据会影响你对职业道路下一步的选择。有硕士学位的人比没有硕士学位的人成为数据科学家的机会更大,这种假设是合理的。
在大型、成熟的公司中,有大量的数据科学项目没有进展,因为团队花费了数百甚至数千小时来回答错误的问题。当你开始你的职业生涯时,确保你非常精确地思考你要问和回答什么问题,并确保两者一致。甚至词语的细微变化也很重要:“有可能吗……”与“有可能吗……”有很大不同
如果你决定申请研究生院,看看这个,它会为你(和我)节省一些时间和精力:
祝你好运!
成为数据科学家需要硕士学位吗?
不要让守门人阻止你获得梦想中的工作。
我通过在线课程、书籍和 YouTube 视频自学了数据科学。经过*一年的自学,我现在是一名数据科学家。
在这段旅程的某个地方,我经常发现自己迷失在自我怀疑中。
我阅读了无数的文章,强调进入数据科学的唯一方法是掌握统计学、数学、线性代数和预测建模。
虽然这在某种程度上是正确的,但它导致了一种假设,即只有数据科学硕士毕业生才能成为数据科学家。
数据科学家需要多少数学知识?
当我自学数据科学时,我经常碰壁,发现自己不知道该如何继续。我觉得我的数学背景不够强。
为了过渡到数据科学,我决定参加微积分、统计学和线性代数的在线课程。
我已经有了数学背景,因为我在 A-levels 期间学过数学和进一步的数学。
我花了大约两个月的时间练习微分、积分和矩阵运算。然后,我上了多门统计和概率的课。
我花了大约 6 个月的时间温习了所有推荐的数据科学家必备的数学知识。
这对我过渡到数据科学有帮助吗?
是也不是。
我学到的大多数微积分和线性代数概念都不能直接应用到模型构建过程中。
我的日常工作不需要我知道如何在没有计算器的情况下计算泰勒级数。
然而,我不后悔花时间在这些课程上。当我参加这些数学课程并每天做作业时,我解决问题的能力真的提高了。
在我学的所有数学课程中,我发现统计学对我的工作有最大的直接影响。
我了解了不同的采样技术、不同类型的分布以及它们如何应用于大型数据集的标准化、假设检验和特征选择。
对算法如何工作有一个坚实的理解真的提高了我的模型建立过程。
然而,要学习一些统计学课程,你需要熟悉一些数学符号。这就是对微积分有一个基本(大学预科,或者可能是高中水*)理解的地方。
你需要知道一些基本的概念,比如求和与微分,以便学习统计学课程,但是不需要太多的深度。
好消息是你可以忽略看门人。
你不需要有统计学硕士学位或博士学位就能在数据科学领域找到工作。
你需要知道的一切都可以自学。
事实上,我的许多同事(包括我的数据科学团队的负责人)都没有数学背景。他们中的许多人都有商业学位,并设法自学了数据科学。
超越数学和统计学
然而,比数学要求更重要的是实现和扩展你构建的算法的能力。
例如
作为一名数据科学家,我的大部分工作都在 Jupyter 笔记本上完成。然而,几天前我必须处理的一个数据集有大约 3000 万行。这意味着我不能在本地构建模型。
我花了大部分时间在 AWS 上建立一个环境来构建和训练我的模型。
我不得不把我写的代码从 Python 改成 Pyspark。Pyspark 是用 Python 编写的 API,支持 Apache Spark,允许并行执行代码。这意味着它可以快速处理大量数据,并允许您处理大数据。
所有这些都必须快速完成,我必须在一天之内将我所有的代码换成不同的语言。
作为一名数据科学家,您需要掌握足够的编程和 SQL 知识,以便能够调整和扩展您的模型。
这是一项来自实践的技能,所以我强烈建议每天至少花 3-4 个小时来编码和解决问题。
管理端到端工作流
比模型构建过程更重要的是处理端到端过程的能力。
这从首先理解业务需求开始。
然后,您需要了解解决业务问题所需的不同数据点。
为了收集这些数据,你需要很强的技术和编程能力。你需要知道如何使用 API,以及如何构建 web 抓取器。
在收集数据之后,在开始建模过程之前,您需要清理数据、预处理数据并做一些初步的分析。
还有,并不是所有的问题都需要机器学习来解决。
如果你被要求建立一个模型来解决一个商业问题,而你意识到机器学习在这种情况下是不必要的,那么就提出不同的建议。
模型构建部分可能是整个数据科学项目中耗时最少的部分,而且您的工作还没有结束。
一旦您构建了模型,您将需要以一种全面的方式向非技术背景的人展示它。
您将需要创建用例及场景,用易于理解的可视化方式解释您所构建的内容。
出于所有这些原因,一个看似简单的数据科学项目可能需要比预期长得多的时间才能完成。业务需求是不断变化的,所以要做好被召回来对模型进行修改或者加入新特性的准备。
数学和统计学在学习数据科学中很重要,但它们在行业中的作用被夸大了。
胜任的编程技能和用数据解决商业问题的能力将会让你在这个行业走得更远,而不仅仅是擅长数学。
成为数据科学家需要硕士学位吗?
乔恩·泰森在 Unsplash 上的照片
我作为波士顿大学数据分析专业硕士学生的经历和体会
鉴于对数据科学的大肆宣传,这是一个非常有效的问题,你需要硕士学位吗?这种炒作是否真实也是另一个大问题。但本文将关注硕士学位是否有必要。还有很多其他更短、更容易、更便宜的选择。事实上,Coursera、edx 和学习网站上有很多免费课程。还有必要经历一个大的学术过程吗?作业、测验、考试、学期项目、报告?
本文将涵盖:
- 一点点关于我自己的旅程
- 目前做硕士生的感受。
- 我还相信没有学位也能成为成功的数据科学家吗?
- 我自学时遇到的一些障碍。
- 克服自学问题的几点建议
简单介绍一下我开始攻读硕士学位之前的经历
在我开始攻读硕士学位之前,我自己也有过一年左右的这种争论。我的学士学位是土木工程,硕士学位是环境工程。在大学的这些年里,我没有写过一行代码。我在硕士毕业后开始学习编程,当时我是一名教师。我通过 LaunchCode 获得了六个月的免费训练营。
像大多数新手训练营一样,它教会了我基础知识。我从 Coursera、Udacity 和 Udemy 上了很多关于数据科学、统计学、python 和机器学习的课程。在本文中,我详细描述了这些课程以及我是如何转向数据科学的:
幸运的是,我能够做两份实习和一些自由职业项目。我意识到我有很多东西要学。但是我真的不知道去哪里找合适的材料。接下来学什么?
当我参加 Coursera 的统计课程时,它慢慢变得对我来说太难了。这是一个三门专业课。前两门课还不错,但是第三门课真的很难。我想我需要更多的帮助。此外,在我选择这个专业之前,我还选择了另一个专业。很快我意识到那不适合我。在那里浪费时间。
我也学了一些机器学习课程。我也学了一段时间,然后意识到这个专业不适合我。然后我找到了教授 Andrew Ngs 机器学习课程。他是我发现的能够很好地将机器学习概念分解成碎片的最佳人选。顺便说一句,我免费学了那些课程。Coursera 中有一个审计选项。你可以免费旁听一门课程,次数不限。我没有在那里花任何钱。
还用 Tensorflow 上了一些 SQL 课程和深度学习课程。这让我非常自信,我学到了很多东西。但是,当我看到招聘启事中的要求时,我几乎找不到与我的知识相匹配的东西。有时我会强迫自己学习几个月的新技能,因为我在招聘广告中看到了它们。在某个时刻,我意识到我永远不会满足所有招聘信息中的所有要求。我需要选择一些技能。现在的挑战是找出这些技能是什么?
与此同时,我每天整天独自在家自学。当我陷入困境时,没有人可以倾诉。有时很累。
虽然我不想去读硕士,因为我已经有硕士学位了。但是我意识到也许有必要去读一个组织有序的硕士学位。这可能会让我对自己的知识充满信心,并得到他人的认可和认可。虽然我知道许多人没有任何数据科学甚至计算机科学的学位,但在这个领域却很成功。但是我想我可能没那么聪明或者没那么幸运!
我需要补充一下,你可能会问为什么一个硕士学位,有很多看起来很好很有前途的新兵训练营!我只是找不到一个我真正信任的人。我调查了很多。他们中的很多人都是六个月长,他们的课程是如此之大!对我来说它们看起来不现实。我觉得他们可能会重复我已经学过的基础知识。对于一个完全的初学者来说,它们可能是一个很好的起点。
再说一次,我找到了一个承诺能帮你找到工作的。每个主题教三个星期。像编程三周,统计学三周,数据挖掘三周,机器学习三周,大数据三周。但是在经历了所有这些专业和课程之后,我至少知道这个话题没有一个能在三周内涵盖。对于编程、机器学习、统计学来说,即使 12 周也太少了。我花了将* 6 个月的时间完成了 Andre Ngs 的机器学习课程。那是初级水*。所以我想这些新兵训练营会教给我已经掌握的基础知识。我可能应该去读硕士,那会教我一些比基础知识更多的东西。但是对于一个完全的初学者来说,如果钱不是一个限制的话,训练营是有帮助的。
所以,我应该说,自学帮助了我做决定。
在波士顿大学读硕士课程的感受如何?
说实话,太棒了!我真诚地相信这对我来说是一个非常好的决定。这是细目表。我在新兵训练营学了编程基础和一些初级算法。但他们真的是首发。后来 Coursera 教了我一些算法。这些也很好,但在这个硕士项目中,我学到了一些高级搜索和排序算法,动态编程,记忆,等等。我也知道我需要自学多少东西。因为几周或 2/3 个月的编程课程永远不足以让你成为算法专家,除非你是超人或女超人。要变得真正优秀需要大量的练习!但是,如果你来自一个完全没有计算机科学背景的地方,至少你需要一个清晰的概念,知道该学什么。
然后是两个学期的数据分析课程,主要是 R 中的统计学。我一直在想学习 R,现在因为这门课我不得不学。正如我之前提到的,我专攻统计学,这很难,而且我认为很先进。在总共 12 周的统计学课程中,我发现了很多我以前已经涉及过的主题,但错过了很多虽小但非常重要的内容。我没有意识到这一点,只是因为我自学时没有人指导我。与此同时,我学到了很多以前不知道的高级课题。此外,我自己不可能在 12 周内学会这么多材料。
有一些像数据挖掘和大数据的课程。我想我已经从头开始学习了用于计算和数据操作的大型库,如 Numpy 和 Pandas,数据可视化库,如 Matplotlib,Seaborn,机器学习和使用 python 的深度学习,还使用了内置库,如 scikit-learn 和 Tensorflow,我学习了 SQL。我刚做完准备开始摇滚。我根本不需要数据挖掘和大数据。但是现在我也在学习这些作为我硕士课程的一部分。
所以我真的很高兴我做了这个决定。但是不要误会我的意思。就算读完硕士也会有很多自学。在此之后,你将会有一个强有力的指导方针。
我仍然相信没有硕士学位也能成为成功的数据科学家吗?
是的。因为我别无选择。我知道很多没有数据科学硕士学位的人做得很好。他们来自完全不同的领域。
自学从来没有这么容易过。因为有太多的材料可供你利用。这里我列了一个可以用来成为该领域专家的高质量免费资源清单:
如果你对深度学习和机器感兴趣,并且学得很好,这里有很多免费资源:
事实上,我之前提到的算法、统计、数据挖掘、大数据都在那里。高质量的免费课程,书籍,如此多的博客,youtube 视频可供学习。许多知名公司甚至像 Instagram 和谷歌雇佣有知识的人,即使你没有学位。
那为什么还要花钱,还要为一个硕士学位、考试、作业和所有这些事情而烦恼呢?
我自学时犯的错误:
是的,一切都在那里。以一种既好又坏的方式。
- 因为你有太多的选择,这就造成了混乱。如果你去问一些有经验的人,你会得到那么多让你更加迷茫的建议和意见。因为不同的人可能会给你展示不同的路线。从哪里开始,学习的顺序应该是什么,还要学什么,练多少,问题太多。但是没有人回答他们。
2.当我参加 Coursera 或 edx 的免费课程时,我试图快速完成所有事情,以便我可以丰富我的简历,并可以快速开始工作。现在我意识到我需要花更多的时间,需要让自己好好消化这些材料。
3.选择正确的课程成为一项挑战。很多时候,我开始参加一个课程,一两周之后,我意识到它不适合我。所以这是浪费时间。自学者总是会遇到这种情况。你必须接受这一点。此外,在开始做你听说的好事情之前,你需要花些时间好好检查一下。
4.很难找到好的练习资料。所以,我学会了这些想法,但可能练习得不够。
5.大部分时间我专注于编码部分的学习。但是数据科学不仅仅是编码。你需要看到问题,提出问题,然后编写解决方案。为此,必要的东西是分析能力,一些理论知识,看到数据差距的能力,以及对主题的理解,这样你才能提出问题。
6.最后,很多人会同意我的观点。你可能开始时很有热情,但过一段时间后,大多数人很难找到动力。因为不仅仅是 2/3 个月。这是一个长期的承诺。尽管有很多训练营承诺让你在 3/4 个月内成为一名数据科学家。我有一篇关于这个的文章:
在你把钱花在某个地方之前,请阅读它。但是如果你是一个完全的编码初学者,训练营可能会给你一些指导。
克服自学问题的一些建议
- 如果能找到一个和你有相同目标的伙伴或者一个团体就更好了。那对你来说是最好的。那将很难找到。但是如果你足够幸运,找到一些人一起学习,当你遇到困难时讨论问题,那事情会变得容易得多。
- 不要匆忙或尝试捷径。那从来没有帮助。留出足够的时间学习编程,统计,每个概念或库非常好。在学好一门语言之前学习一门新的编程语言,或者迁移到新的库,这些都是很有诱惑力的。但那只会浪费你的时间。当你学完一个东西,你需要问自己,你有信心在一个项目中自己用好它吗?
- 对抗分心是一项巨大的工作。在那种情况下,写作很有帮助。我独自在家工作。很容易分心。Youtube,谷歌,脸书,推特,Instagram 这么多分散注意力的东西。尤其是我有点沉迷于 YouTube。我想我现在可以很好地控制它了。尽管如此,要完全摆脱这种瘾还需要做大量的工作。我发现一件事很有帮助。一整天都在提醒自己今天到现在我都做了些什么。
- 对抗分心和保持动力的一个方法是大声说出来。如果你有一个学习小组,对他们做出承诺,或者大声说出你未来三天或明天的目标。也可以对家人做。但是过几天他们可能会觉得无聊。如果没有人大声说话,记日记会有帮助。
- 不断制定短期目标。制定一天或两天的计划。大多数时候,当我们制定长期目标时,我们会成为同谋。大多数学生在考试的最后一刻学习,尽管他们有一周的时间学习。
- 去参加聚会、会议、研讨会,去见人,去交谈。试着去了解他们的工作,他们的背景。那会给你很多视角,打开你的眼界,让你看到很多新事物。慢慢地,你会成为一个数据专业人士的社区。这也是保持动力的好方法,即使成功看起来很遥远,也要继续努力。这实际上对任何阶段的人都有帮助。
- 仔细观看其他人的演示。他们处理问题的方式。如果你是初学者,Kaggle 可能是一个很好的资源。它有一些很棒的免费课程以及许多范例项目。对于免费的数据集,它有这些数据集的示例项目和锻炼。如果你只是打算做一个作品集,可以从那些项目中汲取很多灵感和想法。
- 社交媒体非常有用。我知道有很多关于社交媒体的负面观点。但是我觉得很有帮助。在 Twitter 和 Instagram 上关注合适的人,在 LinkedIn 上联系专业人士,加入不同的好脸书团体。有脸书 r 集团非常专业的专业人士以及学习者。找到适合自己的。这是寻找信息、社区和动力的好方法。
- 如果你足够幸运地找到一位导师,那将在短时间内带你走很长的路。这将使你免于损失大量时间。我经常听人说。但是我从来都不够幸运。我从来没有导师。
结论
你可能会觉得有点困惑。实际上,这完全取决于你的个性。有人可以自己好好工作学习。有些人需要经历一个学术过程。但是,如果你是一个完全的初学者,成为一名数据科学家是一个漫长的旅程。即使你读了硕士,也会有很多自学。我希望这篇文章对你决定攻读硕士学位或继续前进有所帮助。我想分享我的经历。
更多学习:
</10-popular-coding-interview-questions-on-recursion-2ddd8aa86039>
数据科学需要 2021 款 MacBook Pro 吗?
一个每天同时使用 M1 和英特尔 Mac 电脑的人的见解
你好,朋友们!10 月 18 日,苹果终于发布了他们焕然一新的 MacBook Pro 系列,哇那些设备看起来真华丽!众所周知,我是苹果的超级粉丝,虽然我希望能够拥有他们所有的设备,但我和你一样,必须认真选择自己拥有的设备以及升级的频率。苹果在今天的主题演讲中吹捧的一件大事是新的 M1 Pro 和 M1 Max 芯片组的惊人性能,你可能会问,我值得为我的数据科学工作升级到这种新硬件吗?
显然,我自己没有使用过这些设备,但我可以从一个每天都使用 M1 Mac 和英特尔 Mac 的人的角度提供一些见解。更具体地说,我使用 M1 Mac mini 和 M1 iPad Pro 进行个人数据科学工作,而作为机器学习工程师的日常工作,我的雇主为我提供了最新的 16 英寸英特尔 MacBook Pro。因此,尽管我没有亲身体验过 2021 年 MacBook Pros 中的这些新芯片组,但我想说,我对这些旧芯片组的日常体验可能为大多数人提供了足够清晰的结论。
苹果正在吹捧这些新芯片组的一些出色性能,虽然我没有理由怀疑性能是伟大的,但这是你需要的吗?在我们进入这个问题之前,我认为房间里有一只更大的大象要谈:苹果的定制硅。
新定制苹果芯片
十多年来,苹果一直用英特尔提供的芯片驱动 MacBook 系列。与其优秀的——如果说是封闭的——操作系统 macOS 相结合,英特尔 MAC 电脑在性能方面总是受到好评。从 2010 年开始,苹果开始为 iPhone 和 iPad 系列制造自己的硅芯片,A 系列产品至今仍然存在。每年 A 系列都取得了巨大的进步,这给许多人留下了一个问题:苹果会尝试为 Mac 系列制造自己的硅芯片吗?
到了 2020 年,苹果以最新的【M1】****芯片的形式做到了这一点。这款最新的芯片预示着英特尔一个时代的结束(英特尔对并不太高兴),虽然今天看到了为更大的 MacBook Pros 推出的 M1 Pro / Max 芯片,但 M1 芯片一般来说已经存在了大约一年。
问题是,芯片组的设计考虑到了它们自己的架构需求。英特尔芯片使用一种叫做 x86 的架构,大多数软件程序和编码语言都是为配合这种 x86 架构而设计的。苹果已经选择做苹果选择做的事情:创建他们自己的定制芯片组架构,被社区简单地称为苹果芯片 架构。现在,苹果已经通过一项名为 Rosetta 2 的兼容性措施承诺,任何设计用于 x86 的软件都可以在他们的苹果芯片架构上正常工作。在很大程度上,我可以说那是真的。
但是当然……这并不完全正确。至少现在还没有。当这个帖子发布的时候,我在这里遇到了一些问题,但是作为一个机器学习工程师,没有什么问题是无法解决的,这让我有点头疼。(谢天谢地,我的英特尔 Mac 没有这些问题!)以下是我在 M1 Mac 电脑上看到的一些问题:
- Docker 桌面上的熊猫:默认情况下,苹果在幕后使用 Rosetta 2,以便它在终端运行时继续让 x86 兼容的命令工作。这是今天在一台机器上安装 Pandas(和其他类似的库)所必需的。直接在你的 M1 终端上运行一个
pip install pandas
应该没问题,但是使用 Docker 桌面就不是这样了。如果你试图运行一个docker build
命令来构建一个试图安装熊猫的镜像,我相信它试图直接使用苹果硅架构而失败。我不是 100%认为这是根本原因,但我花了一天时间寻找一个变通办法,在我的 Docker 图像上安装熊猫。 - Anaconda : Anaconda 目前并不支持苹果芯片架构。老实说,现在这并不是什么大事,因为 Conda-Forge 团队背后的好人已经创建了一个变通办法,但是谁知道是否有新的 M1 用户能够在第一天使用 Anaconda 呢?我个人在发布几个月后得到了我的 M1 Mac mini,但我想第一天的用户在这里运气不好。
- eGPU 支持:目前的 M1 MAC 不支持 eGPU,我猜较新的 M1 Pro 和 Max 芯片可能也不支持。这对我个人来说并不是一个交易破坏者,我将在下一节解释为什么。
我可以继续下去,但戴上我的乐观帽子,我认为随着时间的推移,这些问题会自行解决。M1 芯片出现才一年,而且只在 Mac 市场的有限部分中出现,所以我认为很多这种东西会有转机。我敢打赌,Docker Desktop 将会得到修补,以适当地构建我的 ML 映像,即使 Anaconda 从未发布官方的本机更新,Conda-Forge 的解决方案也非常棒。
但是你可能会想,那个 eGPU 呢?这些 M1 Pro 和 Max 芯片带来的所有额外功能的承诺呢?好问题,朋友们。让我们在下一节中解决这个问题。
额外计算能力呢?
对于我在 M1 Mac mini 上做的数据科学工作,我确实注意到了速度的提高。具体来说,你们可能都知道,执行网格搜索来寻找最佳模型超参数可能需要相当长的时间。这是因为该算法基本上是通过提供给计算机 CPU 的超参数的每一种可能的组合来完成的。在我之前的 2019 年初的 13 英寸 MacBook Pro 上运行网格搜索作业时,在一个参数适中的大型数据集上,大约需要一分钟才能完成。当在我的 M1 Mac mini 上运行完全相同的网格搜索作业时,它在大约 8 秒内完成。
令人震惊,对吧?
是的,除了考虑到我在我自己的个人机器上用一个公共的 Kaggle 数据集工作,这个数据集对任何人都是免费的。我可以把这个数据集保存在我的 Mac mini 上,没有任何隐私或安全问题。
大多数公司的数据科学并不是这样工作的。
数据安全和隐私一直是一个热点问题,尤其是在过去几年里,没有什么东西会尖叫“违反!”不仅仅是在个人的笔记本电脑上保存公司数据。大多数公司都通过创造性的方法找到了解决这个问题的方法。例如,您可以在 Kubernetes 集群上拥有一个受管理的 JupyterHub 版本,该版本通过适当的凭证访问进行保护,这允许数据科学家在不将任何内容下载到他们的笔记本电脑上的情况下完成他们的工作。此外,许多公司正在转向云,并使用 AWS SageMaker 等原生服务来帮助人们进行数据科学工作。
在这两种情况下,本地工作站的计算能力都没有得到利用。换句话说, 大多数数据科学家甚至都不会接*使用 M1 芯片 的性能潜力。计算发生在另一个更靠*数据所在位置的环境中。在这一点上,M1 Mac 基本上是一扇通向“真正的工作”正在发生的地方的奇特的窗户。因此,虽然我承认 M1 Pro 和 Max 芯片可能确实提供了惊人的性能,但你工作的公司对数据安全的担忧可能会使你利用这种性能的任何机会都化为乌有。
(补充说明:我已经在 Windows 机器上工作了足够长的时间,知道我仍然会向数据科学家推荐 MacBook,因为它植根于 Linux,但如果你想通过给员工提供 MacBook Airs 而不是 MacBook Pros 来节省成本,我不会责怪你。)
最后的想法
回到开始时提出的问题,你需要 2021 年的 MacBook Pro 用于数据科学吗?号码
但是“需要”是一个如此强烈的词,我们在这篇文章中谈论的唯一的事情非常狭隘地局限于数据科学。你必须考虑更多的因素,包括…
- 我已经有一段时间没有升级我的 Mac 了吗?
- 我是否还能做视频编辑之类的事情,从而很好地利用 M1 Pro 和 Max 芯片?
- 我是不是只是觉得很好看超级酷?
如果你对这些问题中的任何一个回答是肯定的,那么无论如何,努力去做吧!虽然我个人对我的 M1 Mac Mini / M1 iPad Pro 组合很满意,但这款最新一代的 MacBook Pros 看起来真的很酷,我很兴奋你们所有人都会买一台!
数据科学需要懂数学吗?
这是我过去两年在学生和经验丰富的专业人士中工作的经验。
阻止人们进入数据科学领域的最大障碍之一是他们可能必须学习数学的观念,正如你可能猜到的,大多数人不想这样做。不幸的是,大多数人在高中时期都对数学感到厌恶。我个人认为,这在很大程度上是因为当我们能够将学习与现实世界的概念联系起来时,我们学得最好,我还没有看到很多高中水*的数学课为现实世界的数学概念打下良好的基础。
(在不进一步离题的情况下,我实际上想知道我们是否应该在高中开始教授数据科学的数学,这样学生就可以看到数学有多棒!)
如果你浏览数据科学家职位的招聘信息,你可能会发现需要某种形式的数学经验,通常是某种学士或硕士学位。当然,这进一步加强了对数学的需求,但有一点要记住,不仅仅是数据科学职位,工作职位通常写得不好,因为它们通常是由对该职位没有直接经验的招聘人员写的。同样,这不是数据科学特有的问题。这是几乎任何技术职位都会发生的事情。
说了这么多,我想我会分享我在这个领域从业两年多的想法。我不仅有机会在自己的公司工作,还有机会指导来自数据科学训练营的学生,指导参加传统四年制大学数据科学专业的大学生,等等。坦率地说,就我自己的经历而言,我没有任何正式的数学学位。事实上,我唯一的正式学位是在领导领域。但是我已经对数学和所有我们能用它做的很酷的事情产生了喜爱的欣赏。从 3Blue1Brown 制作的优秀视频到 Steven Strogatz 和 Ben Orlin 这样的人写的精彩书籍,我认为我是世界上少数热爱数学但没有数学学位的人之一。😂
为了解决这篇文章直接提出的大问题,答案并不是简单的“是或否”。实际上,我会很乐意在这里:是的,你确实需要知道数学,但你需要知道某些数学概念到什么程度将取决于你的具体兴趣。为了帮助弄清楚这到底意味着什么,我将把这篇文章的剩余部分分成两个主要部分。在第一个主要部分,我将把所需的数学知识分成三个不同的层次:第一层是所需的最低层次的数学知识,第三层是所需的最高层次的数学知识。然后,在第二个主要部分,我将给出一些例子,在这些例子中,我们将数据科学应用于所有行业,并分享我对该工作所关联的数学层的想法。
好的,让我们跳到这三个不同的层次!
数学教育的三个层次
在进入这些层次之前,我只想重申,这些只是由我自己和我自己的个人经历定义的。虽然我想我有资格对此给出一个体面的意见,但我完全认识到,互联网上一个人的意见本身并不意味着什么。这里的目的是给你一些初步的想法,告诉你如何规划你自己的数学学习之旅。我不一定要告诉你如何玩转我个人定义的这些层。
第 1 层:对数学的概念性理解
正如我在介绍中提到的,我不认为没有数据科学领域的数学教育是不可避免的。同时,我认为重要的是要承认现在大多数数学都是由某种形式的计算机计算的。无论是打开手机上的计算器应用程序为你的咖啡师计算适当的小费,还是使用电子表格汇总一列数字的总和,我们自己大多不再做数学计算了。
同时,即使是上面提到的那些比较琐碎的任务,还是有一定程度的数学知识的。我能在我的小费计算器中输入数字仅仅是因为我了解小数的基本乘法是如何工作的。数据科学在很大程度上也是如此。我们有许多像 Numpy 和 Scikit-Learn 这样的优秀代码库,它们会为我们做所有这些计算,而不需要我们自己去计算。事实上,深度学习现在成为一个东西的原因是,由于计算机在执行深度学习算法时进行的大量计算,人类几乎不可能创建像神经网络这样的东西。
因此,我会说对任何数据科学家的最低要求是从概念上理解我们如何使用数学来支持各种数据科学活动。尽管这听起来(也确实)比获得全面的数学教育容易,但我还是要说,你不能错过像中学数学教育这样的东西。在不深入探究我可能放入第 1 级桶的所有内容的情况下,您肯定需要知道的一些概念示例是统计概念(例如,*均值、标准差、统计显著性)、基本线性代数概念(例如,矩阵、线性变换)和基本微积分概念(例如,导数、积分、梯度下降)。
但是现代教育已经走了很长的路来教授这些概念,而学生不必学习基础的数学计算。例如,前面提到的 3 blue 1 brown在 YouTube 上有一个关于线性代数的惊人系列,它用动画教学生这些概念,并在很大程度上避免在计算本身上花费太多时间。因此,即使我知道什么是随机梯度下降(SGD ),以及它如何应用于深度学习神经网络,老实说,如果你让我写下数学公式,我也写不出来。
在我们进入下一层之前,我想指出的是,这一小节最长,因为我个人认为大多数数据科学家不需要超过这一层。在下一节中,我会用一些例子来解释得更清楚一些,但是我希望这能让你有所放松。在我自己的数据科学学习之旅开始时,我最初担心,如果没有获得正式的数学学位,我将无法进入这个领域。我很高兴地说,事实并非如此,即使这意味着你没有完全摆脱困境!
第 2 层:数学计算的中等知识
大多数人可能只具备第 1 层知识,但您最好的数据科学家需要更深入地了解底层计算,以获得最佳结果来解决业务问题。这些人仍然可能使用数据科学库来帮助增强他们的工作,但他们将能够以比第 1 层人员更优化的方式应用他们的知识。
如果没有一个具体的例子,很难描述这一层,所以我将用我自己作为例子。当我试图学习我们如何使用数学来比较两个信息矩阵之间的相似性时,老实说,我很挣扎,因为我对线性代数没有更深的理解。我向一个数学背景更强的朋友求助,他很快就能告诉我该做什么。
几年过去了,我一直在努力阐明那个问题是什么,但我还没有找到一种更干净的方法来完成那种工作,而不需要更深入地理解那些线性代数概念。换句话说,我仍然没有找到一个神奇的软件库,将所有这些计算抽象出来。同样,我确信如果我能更好地理解这些数学概念,有些事情我可能会做得更好。第二层次的最佳候选人可能拥有某个数学领域的学士学位,如果不是至少辅修某个数学领域的话。
第三层:数学专业教育
尽管我们在几乎每个行业都发现了数据科学的应用,但我认为仍有一些领域不可避免地需要全面的数学教育。我考虑的这些领域,往往甚至需要专门的执照才能成为一名积极的从业者。这将在我们下一节的例子中变得更加清楚。
这些层如何应用于不同空间的示例
好了,现在我们已经定义了我们的层,我想快速列举一些不同的例子会有所帮助。在下面的小节中,我将介绍一些不同的示例,说明我如何将该角色或活动与其中一个层对应起来,并简要解释我为什么要这样分类。让我们跳进这些例子!
机器学习工程师:第 1 层
这是我白天的工作。因为机器学习工程师(MLE)的职位定义非常模糊,所以我对它的定义是,MLE 更专注于在软件环境中实现预测模型,而不是自己创建这些预测模型。模型将由一个独立的数据科学家角色创建,因此 MLEs 更接*于一般的软件工程师,而不是数据科学家。但是对于 MLE 来说,理解基本的数学概念仍然是很重要的,因为他们经常需要整合一些有助于解决模型漂移等问题的方法。
初级数据科学家:第 1 层
正如我在上面提到的,使用像 Numpy 和 Scikit 这样的 Python 库有很长的路要走——在它们抽象出底层数学概念的时候学习。虽然我在日常工作中是一个 MLE,但我喜欢在工作之外创建自己的预测模型来取乐,我完全可以通过一级知识水*。同样,我认为即使是初级的数据科学家也无法通过中学水*的数学知识,所以在我看来,最低水*永远是第一级。
深度学习实践者:第 1 层
这可能会让一些人感到惊讶,但正如我上面提到的,深度学习几乎不可能用手来计算,因为计算机会进行数十亿次小计算来执行深度学习算法。无论你对自然语言处理(NLP)还是图像分类感兴趣,都无法回避这样一个事实,即你需要计算机的帮助来执行这些复杂的算法。当然,一般的深度学习实践者仍然需要理解像激活函数这样的东西如何在神经网络的各层之间使用,但我想说的是,理解实际计算如何工作并不那么重要。(当然,如果你想为一个主要的深度学习图书馆做贡献,那你就需要更像第三级知识的东西。)
首席数据科学家:第 2 层
虽然初级数据科学家可以在第 1 层知识上走得很远,但最好的数据科学家将能够在第 2 层知识上更进一步。正如我上面提到的,我的朋友能够比我更好地理解线性代数,能够在几乎没有任何时间内解决我试图解决的问题。我最终学会了如何做他在那个小问题上做的事情,但是如果没有第二层的知识,一个初级的数据科学家可能会在更复杂的问题上挣扎。当然,如果一个复杂的问题可以用更复杂的知识水*来解决,这些人应该得到适当的认可,这就是为什么首席数据科学家的角色几乎出现在每个利用数据科学的公司中。
精算分析师:第三级
当我写第三层的描述时,这正是我所想的,主要是因为我自己在保险行业工作。我完全认为精算科学是数据科学的一种形式,但我不可能委托一个具有 1 级或 2 级知识水*的人来构建精算模型。这些人需要三级知识,如果我没记错,我认为你必须通过一些精算考试,才能成为一名执业精算分析师。
硬件工程师:第 3 级
这可能是显而易见的,但我还是把它放在这里。如果你想创建类似用于深度学习的 GPU 的东西,你需要非常专业的数学背景,这是无法回避的事实。如今,硬件的游戏名称是“优化”,鉴于硬件工程师直接与这些特殊计算计算机芯片下面的芯片一起工作,这些人必须灵活运用他们的每一点数学技能来最大限度地利用芯片!
这篇文章到此结束!我希望这对刚刚开始进入数据科学领域的人们有所帮助。显然,最优秀的数据科学家将会是那些拥有最高知识水*的人,但是我希望这是令人鼓舞的,你不必拥有最强大的数学教育就可以进入数据科学领域。感谢阅读,下一篇文章再见!
你需要管理一个数据科学家团队吗?—理解控制理论可能会改变一切
我将如何使用它来管理初级、中级和高级数据科学家
数据科学家是商界非常抢手的职位。有很多人想进入这个领域,也有一些人想尽可能长时间地呆在这个领域。如果你不知道如何正确管理数据科学家,这可能会很困难,特别是当你在世界上晋升时,所以我们将根据控制理论讨论管理初级、中级和高级数据科学家的不同方法。
1.管理数据科学家的问题是
管理数据科学家的问题在于没有固定的方法。每个企业对其数据科学团队都有不同的需求和要求,因此管理他们的流程因公司而异。这不仅仅是管理人员,还包括管理项目和项目结果。您可能希望以一种方式管理初级数据科学家,而以另一种方式管理高级数据科学家。显然,您会将更具挑战性和更大规模的项目分配给高级数据科学家,这就是为什么以不同的方式管理它们是有意义的。然而,如果不了解原因和方式,管理初级数据科学家和中级数据科学家可能对任何一组人都没有效率或效果。更别说事先说清楚了。
例如,管理一个既有初级又有中级水*的团队,但只将更具挑战性的项目分配给高级人员,可能会导致低级人员感到沮丧,他们可能会觉得自己在职业生涯中受到阻碍或没有发展。你不会想这么做的,对吧?
考虑到所有这些,管理数据科学团队是一件棘手的事情,需要作为数据科学经理/领导者/老板的你进行大量的思考和规划…无论你给自己取什么头衔:)
2.什么是控制理论,它在工作场所是如何工作的
好的。你听说过控制理论吗?这个理论在管理领域已经存在了一段时间。基本上,控制理论认为,根据可能结果的不确定性水*和你可以采取的路径,你必须控制过程的不同方面。
上面的插图描述了我的意思。假设有一个明确定义的结果,并且你确切地知道你必须做什么来实现这个结果。该理论认为,你的管理重点应该放在员工的行为上。对于那些您知道可能的结果,但由于环境的变化,有多种方法可以达到结果的项目,那么建议您关注结果,并让您的员工灵活地想出如何达到结果。对于那些复杂的和/或“以前从未做过的”类型的项目,你应该把你的注意力集中在雇佣最有能力的员工来管理这个项目上。
3.初级、中级和高级数据科学家如何使用控制理论
通过定义明确的参数,你可以使用控制理论来管理初级数据科学家。例如,如果你是一名经理,负责向小型企业销售小工具,你雇用了三名经验水*不同的数据科学家,然后给每个人一个他们将负责的要素,如确保有足够的供应来满足需求,或管理活动成本或管理需求。
-如果你雇佣了一名初级数据科学家,那么定义上一个项目的成功之处,并设定清晰的参数来遵循该模式。对于这个角色,当你招聘这个角色时,你应该强调他们的技术能力和服从指示的意愿。他/她更可能做探索性的数据分析和文档。
-中层数据科学家在了解目标后,可以找出在这些界限内获得预期结果的方法。他们更有可能尝试新事物,因为他们对以前的项目有足够的经验。他们可能会测试不同的机器学习算法以获得定义的结果。在招聘这个职位时,你的面试问题应该设计成捕捉他们与其他团队成员合作解决问题的能力,并关注推动他们努力实现目标。
-高级数据科学家将更好地了解您的公司如何运作,以及设置哪些参数很重要,以便他们能够找到以最佳方式实现这一目标的方法。你可能想从一个你以前没有真正做过的想法或创新方式开始。你应该让高级数据科学家更自由地探索和挑战假设。因此,寻找他们以前管理项目的经验至关重要,从理想化到生产应该是你的雇佣标准。
4.为什么你应该尝试这种新的管理系统
我们知道这是典型的医院员工管理方式。当你走进一家医院时,你很可能会得到一名护士,她会例行检查你的体温和血压。假设你需要一名神经外科医生,你知道他/她不受预先设定的程序约束,也不受医院主管的密切监控/管理。
很容易根据一些任务的难度来分配任务和管理重点(例如,将简单的任务交给初级数据科学家)。然而,我建议你应该根据你所能控制的来转移你的管理重点。
-初级数据科学家需要得到更有经验的数据科学家的指导;如果听之任之,他们可能会觉得自己的能力和技能不够。
-中层员工将受益于对项目的控制,这使他们能够为自己(或与他们的初级数据科学家)做出决策。
-高级工作人员将能够在管理初级和中级工作人员的同时,汇集他们的经验和知识。
数据科学是一个需要理解统计学、机器学习和数据工程的领域。一次性学会所有这些技能并将其应用到实际项目中并不容易。建立你的技能需要时间和经验。随着你的职业向项目领导、经理或高管的方向发展,你需要开始考虑发展你的管理技能(即软技能)。当你成为经理、领导者或老板时,也许你可以利用控制理论来重新思考如何在管理多个项目和利益相关者的同时管理你的数据科学家。通常,将简单的任务分配给初级员工,将困难的任务分配给高级员工是一个好主意,但是使用这种方法,你可能会错过这两类员工提高技能的机会。
学数据科学需要付费吗?
意见
钱是学习数据科学的必备条件吗?分析最好的免费资源和有用的链接让你开始!
莎伦·麦卡琴在 Unsplash 上的照片
“做一个终生的学生。学的越多,赚的越多,也会越有自信。”布莱恩·特雷西。
在当代,数据是一种宝贵的资源,新兴趋势表明数据科学学科的受欢迎程度急剧上升。
因此,这可能是您投入宝贵时间和精力来提高您在数据科学领域的技能的最佳时机。发展成为未来成功的数据科学家!
但是,如果你是该学科的初学者爱好者,甚至是中级学习者,那么有效和高效地学习数据科学的最佳行动方案是什么?
初学数据科学的爱好者经常问的一个问题是,学习数据科学并通过掌握该领域的所有本质方面成为专家,你实际上需要支付多少钱。
在这篇文章中,我将试图用一些背景知识来回答这些问题,同时也澄清一些误解。
然后,我们将查看一些最好的免费资源和有用的链接,以便精通数据科学。
最后,我们将有动力学习更多知识,并始终专注于自我完善,因为数据科学是一个不断发展的领域,我们也必须同步发展。
简单的旅程
三年前,我对数据科学几乎没有任何经验。
我的背景是电子和通信,主要从事机器人项目。我喜欢规划、建造和构造独特的机器人。
然而,我在一个时间点上意识到,如果我可以将人工智能集成到我的机器人项目中,使它们更具创新性,那将是超级棒的。
我希望我的机器人除了其他各种独特的实体和特征之外,还具有人脸识别、物体检测和语音翻译等功能。
就在这时,人工智能极大地激发了我的兴趣,我开始了走向数据科学的旅程。
通过谷歌搜索,我发现开始学习数据科学的最佳资源是用 Python 编程和处理数学概念。
对我来说,数学从来不是主要问题,因为我总是很喜欢它。我还注意到,由于我的背景,我也满足了数学的大部分基本数据科学要求,包括统计、概率和微积分的概念。
不要担心,在接下来的部分中,我会提到一些学习数学的最好的免费资源!
然而,编程对我来说并没有太多的经验,我不得不经常练习。我在高中时学过很多 Java,所以我知道一些面向对象编程的要点。
我的主要目标是学习 Python 和数据科学的基础知识,这样我就可以用我获得的大量知识创建创新和独特的现实世界应用程序。
长话短说,我在不到一个月的时间里学会了 Python 编程的大部分基本要求,并在大约三个月的时间里变得相当熟练。
这大约是一个完全没有经验但有很大兴趣的初学者学习和掌握大多数必要主题所需的*均时间。
数据科学是一个不断发展和进步的领域。我研究了这个课题,并在接下来的几年里参与了许多项目。
我从这段时间利用的免费课程和资源中获得了大部分知识。我最终参加了一个额外的付费课程,获得了一个证书。
然而,大部分时间都花在了免费资源上,由于它们提供了大量有价值的信息,这绝对是不可思议的。
在下一节中,让我们分析和探索一些学习数据科学的最佳免费宝贵资源!
最佳免费资源
有大量耸人听闻的研究论文、大量免费文档、视频教程、简明指南等等,可以提高您对数据科学主题的知识和理解。
在本节中,让我们分别讨论每个基本概念(即数学、Python 和数据科学基础)的最佳选项。
注: 星号(*)表示我个人使用的资源。否则,这是根据研究或其他有知识的人推荐的。
数学:
- 可汗学院(*): 开始学习线性代数、微积分、概率统计等基本数学概念的最佳场所。
- 3Blue1Brown (*): 最好的 YouTube 频道之一,可以查看一些精彩的深入解释,并更好地直观理解概念。
- 书籍:如果你更喜欢阅读书籍,那就在谷歌上快速搜索一下,找出一些优秀的学习数学的免费电子书资源。为了更好地练习,你应该为每个特定的主题选择单独的书。
Python:
- Free Code Camps(*):Free Code camp 是一个非营利性组织,由一个交互式学习网络*台和其他旨在让任何人都可以学习编程的强大功能组成。
- YouTube 视频(*): Sentdex,Tech With Tim 和科里·斯查费是我关注的一些受欢迎的 YouTube 视频,我强烈推荐他们。也有其他精彩的选择,你可以选择学习。
- HackerRank (*): 学习编程和参加比赛的梦幻*台。
- 免费课程(*): 如果你有时间去上热门*台的免费课程,那么强烈推荐。
数据科学(包括机器学习和深度学习):
- 斯坦福大学的机器学习(Coursera) (*): 学习机器学习基本概念的绝对最佳资源。旁听本课程,因为大部分作业都在 Matlab 中,并充分利用这一资源。
- YouTube 视频(*): 我强烈推荐查看机器学习和深度学习内容创作者的各种 YouTube 频道。
- GitHub(*): 分享您的代码和知识,以及有效学习和理解他人代码的最佳*台。
- Kaggle(*): Kaggle 是谷歌公司的子公司,是一个由数据科学家和机器学习实践者组成的在线社区。
- TDS(*): “走向数据科学”聚集了一些最优秀的数据科学家和爱好者,他们创造出令人难以置信的项目,并向广泛的受众展示。其他文章网站也强烈建议检查,以获得更多的知识。
- 研究论文:理解和阅读研究论文将为数据科学、机器学习和深度学习的复杂问题的复杂性提供巨大的洞察力。
- 书籍:我强烈推荐并鼓励你们所有人尽可能地在谷歌上搜索并找到学习数据科学的免费电子书。
我将撰写另一篇文章,更广泛地介绍有用的资源。请务必让我知道你们是否对此感兴趣!
澄清
文章的这一部分更像是一个免责声明,澄清围绕我们今天讨论的主题的任何误解。
有没有可能免费学习数据科学?
—简而言之答案是肯定的。但是…
免费学习数据科学是完全可能的,您会注意到,在本文的前几节中,我已经提供了关于这个主题的简明细节。
本文的主要目标是通过免费资源从头开始学习数据科学背后的学习过程,而不是利用一些付费的在线课程(其中一些也可能是骗局)。
这并不是告诉你不要获得该学科的硕士或博士学位。
如果你有机会在知名大学获得这些学位,我向你保证,这将对你获得更多关于数据科学的知识和更好的理解非常有帮助。
这些是一些付费学位,你应该考虑从事,以了解更多。(如果你同样获得了奖学金,那就是双赢了!)
然而,如果你负担不起,或者你还没有获得在你喜欢的大学居住的机会,所有的希望都不会失去,因为你可以在家里通过多种免费资源学习数据科学。
世界现状也为你拿起数据科学并掌握它提供了一个理想的情境!
本文最后结论部分提供的第一个链接是一个简明的 25 分钟指南,介绍如何在 2021 年的 12 个月内掌握数据科学概念!
额外动机
图片由 Xan Griffin 在 Unsplash 上拍摄
“数据科学家是能够获取、筛选、探索、建模和解释数据的人,融合了黑客技术、统计学和机器学习。数据科学家不仅擅长处理数据,而且将数据本身视为一流产品。”— 希拉里·梅森,快进实验室创始人。
在我看来,只要你喜欢你正在做的事情,并且乐在其中,那么什么都不重要。
确保你做你喜欢和享受的事情。
利用数据科学,你可以创建大量令人惊叹的项目。我将在我的下一篇文章中详细介绍这一点!
数据科学领域非常壮观,有如此多的新想法和创新将在未来被发现。我很高兴我们所有的数据科学爱好者能够以多种方式为这个不断进步和快速发展的领域做出巨大贡献。
但是,如果你发现数据科学不是你喜欢的东西,那么不要因为其他人想这么做而简单地跟风。
世界上还有很多其他美好的选择。了解自己的兴趣,追随自己的梦想!查看下一节的最后一个链接,了解关于这个主题的更多信息。
我们被人工智能和数据科学包围着,我发现这个领域的快速发展非常令人着迷。我对未来更新的技术和数据科学的最终崛起感到兴奋。
数据科学产生的大量宣传,以及创造的各种工作机会,使数据科学成为每个人都必须考虑探索和学习的一步!
这是我们所有数据科学爱好者和数据科学家探索世界上各种选项和机会的最佳时机。
有效利用你的时间将有助于创造一个更好的半球,一个神话般的数据科学项目。
让我们通过构建令人惊叹的模型和项目来解决复杂的任务并找到复杂问题的解决方案,从而为数据科学的发展做出贡献!
结论:
从这篇文章陈述的观点中,我们可以得出,为了掌握数据科学的精髓,付费并不是一个强制性的要求。有大量的免费资源可供你利用和受益。
然而,如果你想学习编程、数学或任何在线数据科学课程,我强烈建议你对该特定课程进行深入研究。确保你阅读了所有的评论,并考虑了参加过该课程的其他人的意见。
随着数据科学的日益普及,互联网上出现了大量的骗局和诈骗网站。所以,安全总比后悔好!
我强烈建议完全初学者先看看免费课程。你要不要遵循这个建议,最终是你自己的选择。
如果你能够找到一门有价值的课程或资源,并能以合理的价格从中受益,那么甚至建议你参加这些课程,以获得更高的技能上限和更多的知识。
祝大家在数据科学之旅中好运!
如果你们喜欢这篇文章,请随时查看我的其他一些故事,你们可能也会喜欢阅读!
</12-steps-for-beginner-to-pro-in-data-science-in-12-months-c6f6ba01f96e> </7-tips-to-increase-your-productivity-in-python-bc1835622aa5> </10-wrong-reasons-to-become-a-data-scientist-319531bb874e>
谢谢你们坚持到最后。我希望你们喜欢阅读这篇文章。我希望你们都有美好的一天!
你看到我看到的了吗?
无监督学习在文档布局中的应用
感知是第一个无监督的算法。
以视觉为例。你有没有注意到我们的大脑是如何自动将视觉空间中接*的事物分组的?请注意,在下图中,我们倾向于看到两组独立的 6 个点,而不仅仅是总共 12 个点的图像:
作者图片
这种自然的群体倾向并不是视觉所独有的。事实上,莫尔斯电码之所以有效,是因为听觉是围绕类似的原理组织的。
在数据科学中,无监督学习是数学模型试图发现数据中自然出现的模式。一类无监督学习是聚类分析。聚类算法像我们的感知系统一样工作,因为距离有助于确定数据中的自然分组。
但是不同的聚类算法以不同的方式定义和使用距离来决定哪些数据属于哪个组。因此,对于相同的聚类任务,每种算法的性能可能会有所不同。
简而言之,仅仅因为我们的眼睛可能会看到一个清晰的集群解决方案,并不意味着我们的算法会得出相同的结论。
我们的眼睛可能会看到聚类算法可能会与之斗争的模式的一个领域是文档。在这个简短演示的剩余部分,我用两种不同的聚类算法(KMeans & DBSCAN)进行实验,并测试它们识别图像上文字的柱状结构的能力。这里我问这些聚类算法;你“看到”我看到的了吗?让我们找出答案。
文档理解
当我们看文件时,很容易感受到我们的感知系统在起作用。简历、表格、法律文件和学期论文都有一个页面布局的组织。在某些情况下,这些形式可能包括使用线条和网格对不同部分进行物理划界。在其他情况下,仅仅是单词的组织就足以让我们的眼睛知道哪个单词与哪个部分相匹配。
然而,对于机器来说,理解这些文档的布局并不容易。事实上,研究人员和公司已经在复杂的深度学习方法上花费了数百万美元,以自动识别文档中的不同部分。
不幸的是,这些深度学习方法有一些限制,包括它们的计算成本、手动标记数据的需要以及样本大小。简而言之,重点是使用深度神经网络架构的监督学习来更好地理解文档布局。
尽管我们几乎不用费力就能看到非常清晰的部分,但所有这一切都发生了。基于这种认识,我决定测试无监督聚类算法检测基本文档组织的能力。
实验
为此,我选择了两个示例文档,一个具有非常清晰的列组织,另一个具有稍微复杂一些的列组织。第一份是简历示例,第二份是信贷申请表示例:
作者图片
在这两个示例中,您可以在每个文档中看到不同的列组织。两个集群算法用于测试它们准确检测这个列结构的能力,KMeans 和 DBSCAN。
KMeans 算法通过在 n 维空间中随机初始化一个中心点来工作。中心点的数量由执行聚类分析的人员在运行模型之前确定。然后,根据每个点到最*质心的距离,将每个点分配到一个组中。一旦所有点被分组,就计算该组的*均值来更新中心。该过程重复进行,直到质心不再显著变化。
DBSCAN (带噪声的应用程序的基于密度的空间聚类)另一方面,通过从 n 维空间中的一个点开始工作。然后,该算法将该起始点一定距离内的所有点进行分组,以形成聚类。距离度和最小点数是 DBSCAN 所需的唯一两个超参数。因此,DBSCAN 不具有与 KMeans 相同的限制,即先验地知道可能存在多少可能的集群。也就是说,距离,或称之为ε,通常需要调整。
为了实现测试,我使用了以下环境:
操作系统:Windows 10
Python 3.7.3
sci kit-学习 1.0.1
OpenCV
熊猫
Matplotlib 3.3.2
立方体立方体 5.0.0
密码
首先,我首先提供图像的路径,并使用内置的 Python 库 glob 列出所有图像。jpg 图片。然后,我使用 OpenCV 读入每个图像,将图像传递给 Pytesseract 的“image_to_data”方法,该方法返回一个制表符分隔的字符串。通过使用 Python StringIO 库,Pandas 的“read_csv”方法可以将制表符分隔的字符串正确地解释为数据帧。
一旦创建了 dataframe,我就删除空值并添加“right”和“bottom”列来标识找到的每个单词的右边和底部像素位置。聚类前的最后一步是规范化将在聚类中使用的“左/右”列,并将它们放入一个数组中。所有这些都在我命名为 data 的元组列表中捕获。
数据列表包括原始图像的名称、图像数组、来自 tesseract 的数据框以及标准化的左/右列数组。
一旦捕获了所需的数据,我就开始测试 DBSCAN 和 KMeans。在下面的代码中,我们通过为变量“an”输入一个值来手动输入我们想要生成输出的图像,然后运行下面的代码来生成输出。这里有一个例子:
为了进一步解释上面的代码,我们从数据列表中的每个元组提取以下信息:
用于聚类的左/右数组(X)
具有镶嵌输出(df)的数据帧
然后,我们将聚类数组(X)传递给 DBSCAN(第 13–15 行)或 KMeans 模型(第 18–23 行),生成并保存结果的散点图(第 26–64 行),将数据帧连接到模型提供的聚类标签(第 66 行),找到聚类的边界框(第 68–71 行),并在保存之前将它们添加到图像中。请注意,运行模型和生成图像的代码很大程度上是从这里的分叉出来的。
结果呢
在这两种情况下,我们看到每种算法的表现都相似,只是结果有一些微小但重要的变化。对于 3 列图像(信贷应用程序),两种解决方案都找到了相似的聚类,但分布略有不同。
作者图片
然而,最大的不同是两栏图像(简历),KMeans 似乎将第二栏中的一些数据与第一栏混为一谈。
作者图片
在评估每个解决方案的边界框时,我们看到 KMeans 在 3 列图像中表现稍好,而 DBSCAN 在 2 列图像中表现好得多。
作者图片
结论
KMeans 的最大限制是该模型有几个超参数,我们可以进一步试验以调整解决方案,而 DBSCAN 只有 2 个(ε和聚类中的最小点数)。因此,DBSCAN 更容易调整,可以更有效地找到更适合 3 列图像的结果。
在这两种情况下,我们看到如何利用无监督聚类算法来帮助识别简单的文档列结构。如果试图对齐 OCR 引擎的输出,以便数据与其列标签正确对齐,这样的分析会非常有用。
简而言之,我们的无监督文档布局方法显示了聚类算法如何能够“看到”我们人眼所看到的,但也可能需要一些调整才能正确。
比如参与学习数据科学、职业发展或糟糕的商业决策?加入我。
熊猫用 Apply 吗?有一种速度快 600 倍的方法
通过利用矢量化和数据类型,您可以大大加快 Pandas 中复杂计算的速度
我最*读了另一篇文章,向你展示了如何加速熊猫的应用功能。这些文章通常会告诉您并行化 apply 函数,使其速度提高 2 到 4 倍。
在我向您展示如何将速度提高 600 倍之前,让我们用普通的 apply()来说明一个用例。
熊猫申请
让我们假设您有一个熊猫数据帧 df,并想对它执行一些操作。
我将使用具有 1m 行和五列的数据帧(整数范围从 0 到 10;我正在使用类似于这篇文章的设置
df = pd.DataFrame(np.random.randint(0, 11, size=(1000000, 5)), columns=('a','b','c','d','e'))
我想应用一个基于“e”的逻辑,它将基于其他四列生成一个结果。
def func(a,b,c,d,e):
if e == 10:
return c*d
elif (e < 10) and (e>=5):
return c+d
elif e < 5:
return a+b
让我们用熊猫申请这个功能。
df['new'] = df.apply(lambda x: func(x['a'], x['b'], x['c'], x['d'], x['e']), axis=1)
我们得到大约 11.8 秒的运行时间(超过 10 次运行,最小运行时间为 11.7 秒)。
*行熊猫申请更快
您可以通过使用 swifter 轻松地并行化这个过程。
由于 swifter 在默认情况下没有和 anaconda 一起安装,所以您必须先安装它。
conda install -c conda-forge swifter
我们现在可以通过在应用之前调用更快的来使用并行化应用
import swifter
df['new'] = df.**swifter**.apply(lambda x : func(x['a'],x['b'],x['c'],x['d'],x['e']),axis=1)
在我的 MacBook Air(使用 M1 CPU)上,我的*均运行时间为 6.71 秒(超过 10 次运行,最小运行时间为 6.45 秒)。这几乎是我们最初的 apply 实现的两倍。
Python 中的并行化并不是灵丹妙药:您只能期待轻微的改进(如果有的话)。
熊猫矢量化
使用 Pandas 和 Numpy 的最快方法是向量化你的函数。另一方面,使用 for 循环、list comprehension 或 apply()沿着数组或序列逐个元素地运行函数是一种不好的做法。
让我们为前面的函数创建一个向量实现。如你所见,我用两个面具来识别相关案例。loc 来更新这些值。此外,默认情况是在不使用任何掩码的情况下分配的。
df['new'] = df['c'] * df['d'] #default case e = =10
mask = df['e'] < 10
df.loc[mask,'new'] = df['c'] + df['d']
mask = df['e'] < 5
df.loc[mask,'new'] = df['a'] + df['b']
现在运行时间为 0.035 秒(最小运行时间为 0.027 秒)。与 swifter 相比,这几乎提高了 200 倍!
矢量化将为您提供闪电般的执行速度
在这里下载我的书的摘录
较轻的熊猫数据帧
你可以通过使用另一个技巧来加快执行速度:通过使用更有效的数据类型来减轻熊猫的数据帧。
因为我们知道 df 只包含 1 到 10 的整数,所以我们可以将数据类型从 64 位减少到 16 位。
for col in ('a','b','c','d'):
df[col] = df[col].astype(np.int16)
看看我们是如何将数据帧的大小从 38MB 减少到 9.5MB 的。很明显,您的计算机在处理将* 4 倍小的对象时会更轻松。
我们函数的运行时间现在减少到了大约 0.019 秒,这几乎是我们使用初始数据帧(使用 np.int64)的两倍。
在现实生活中,你可能没有足够的运气拥有一个只有小整数的数据集。尽管如此,你可以通过使用 np.float32 而不是通常的 np.float64 或者通过使用熊猫类别来尝试加速你的过程。
通过利用数据类型减少数据帧的大小
NumPy 矢量化
上面的代码依赖于 pandas 系列来执行检查和计算。Pandas 系列由 NumPy 数组(用于存储数据)和一些开销信息(如系列索引和名称)组成。
我们可以使用直接访问系列“后面”的 NumPy 数组。值来使我们的矢量化稍微快一点。这通常非常有效,除非您需要使用掩码和特定的列——就像我们的例子一样。
为了向您展示 numpy 矢量化与 pandas 矢量化的强大功能,让我们创建另一个用例。
您需要计算 a、b、c 和 d 列的总和,然后乘以 e。我们还可以将数据帧的大小增加到 100M 行(而不是最初的 1M)。
df = pd.DataFrame(np.random.randint(0, 11, size=(100000000, 5), dtype=np.int16), columns=('a','b','c','d','e'))
我们新的数据帧大约需要 900 兆字节。
df['new'] = df[['a','b','c','d']].sum(axis=1) * df['e']
使用这种 100% pandas 执行,*均运行时间(超过 10 次试验)是 2.92 秒(最少 2.87 秒)
df[‘new’] = df[[‘a’,’b’,’c’,’d’]].values.sum(axis=1) * df[‘e’].values
使用。值,运行时间减少到 2.65 秒(最少 2.62 秒),减少了 10%。
NumPy 数组可以进一步加快大规模数据集的执行时间
结论
我们展示了通过将 pandas 矢量化与高效数据类型结合使用,我们可以将 apply 函数的运行时间减少 600(除了 pandas 之外不使用任何东西)。
- 申请:11.8 秒
- 应用+更快:6.71 秒
- 熊猫矢量化:0.035 秒
- 熊猫矢量化+数据类型:0.019 秒
你用 XGBoost 吗?有一种快 200 倍的方法
提示和技巧
在本文中,我将向您展示四种训练 XGBoost 的方法。与 XGBoost 默认设置相比,我们将实现 200 倍的速度提升。
作为数据科学家,我们喜欢进行许多耗时的实验。降低我们模型的训练速度意味着我们可以在相同的时间内进行更多的实验。此外,我们还可以通过创建更大的模型集合来利用这种速度,最终获得更高的准确性。
常规 XGBoost
陈和 Guestrin(来自华盛顿大学)发布 2016 年 XGBoost 日期。与常规梯度增强相比,它们实现了显著的加速和更高的预测能力(参见我的书进行比较,参见 scikit-learn 进行常规梯度增强)。这个新模型很快成为 Kaggle 上数据科学家的最爱。
让我们在一个 10M 行 30 列的虚拟数据集上运行 XGBoost‘vanilla’版本。
times_cpu = []
for trial in range(trials):
start = time.time()
XGB = XGBRegressor()
XGB = XGB.fit(X,Y)
print(time.time() — start)
times_cpu.append(time.time() — start)
运行时间(在我的英特尔 i7–7700k CPU @ 4.20 GHz 计算机上)大约为 2000 秒。我只尝试了一次这个设置,因为它需要大约半个小时的运行时间。
使用 XGBoost 提升直方图
LGBM 由微软团队于 2017 年发布。他们让训练更快的突破性想法是将连续特征(读取训练数据)存储在离散箱(直方图)中。
基于直方图的增强现在被认为是增强树的最佳实践。现在由 XGBoost、 CatBoost 以及 scikit-learn 来实现。
在训练 XGBoost 时,可以通过设置 tree_method = 'hist' 来使用这种新的基于直方图的方法。
times_cpu_hist = []
for trial in range(trials):
start = time.time()
XGB = XGBRegressor(tree_method = “hist”)
XGB = XGB.fit(X,Y)
print(time.time() — start)
times_cpu.append(time.time() — start)
现在*均跑步时间是 57.7 秒。我们刚刚实现了 35 倍的速度提升!
GPU 上的 XGBoost
XGBoost 允许你使用你的 GPU 来训练模型。这通常比常规 CPU 运行得更快,并且可以很容易地激活。
要使用您的 GPU,请将 tree_method 更改为‘GPU _ hist’
times_gpu = []
for trial in range(trials):
start = time.time()
XGB = XGBRegressor(tree_method = “gpu_hist”)
XGB = XGB.fit(X,Y)
print(time.time() — start)
times_gpu.append(time.time() — start)
运行时间现在约为 13.1 秒(使用 Nvidia GeForce GTX 1080)。比 CPU 快 4.4 倍。
以下是如何使用 GPU 在 windows 机器上运行 XGBoost 的方法。
如果已经可以在 GPU 上运行 XGBoost,可以跳过这一部分。
步骤 1:安装正确版本的 XGBoost
如果你通过 conda/anaconda 安装了 XGBoost,你将无法使用你的 GPU 。(如果您不确定如何在您的机器上获得 XGBoost,95%的可能性是通过 anaconda/conda 获得的)。
相反,我们将使用 pip install 来安装它。
打开控制台,键入以下两个提示
首先卸载 XGBoost。
pip uninstall xgboost
然后,重新安装。
pip install xgboost
步骤 2:安装 CUDA
Cuda 需要使用 Nvidia GPU 进行机器学习和深度学习。
在这里下载:https://developer.nvidia.com/cuda-downloads。
步骤 1:下载 CUDA
您需要使用标准过程(使用快速设置)来安装它—这里没有什么特别要做的。
步骤 2:启动安装程序。
第三步:使用快速推荐的设置(原谅我的法语!)
就是这样!
XGBoost 采用单精度 GPU
您需要将'single _ precision _ histogram'设置为 True。
times_gpu_single = []
for trial in range(trials):
start = time.time()
XGB = XGBRegressor(tree_method = “gpu_hist”, single_precision_histogram=True)
XGB = XGB.fit(X,Y)
print(time.time() — start)
times_gpu.append(time.time() — start)
跑步时间现在是 8.7 秒。与标准的 GPU 方法相比,这减少了大约 32%。
⚠️注意到单精度模式并不总是有效的。如果数据超出了单精度浮点数的限制,就会遇到问题。在跟踪预测模型的*方误差时,我遇到了这种情况:*方误差超过了单精度浮点数可以处理的最大限制。
结论
使用我们的 GPU(而不是 CPU)、直方图提升和单精度浮点数,我们为 XGBoost 实现了 228 倍的训练时间加速。
- CPU: 2000 秒
- CPU 和直方图: 57.7 秒
- 图形处理器和直方图: 13.1 秒
- GPU、直方图和单精度: 8.7 秒
🚀您可以通过在 GPU 和 CPU 上并行运行实验来进一步加快实验过程。
⚠️🔬不要忘记本文使用的是虚拟数据集。数据集和机器上的结果可能不同。在选择您最喜欢的方法之前,您应该尝试一下 GPU、直方图和单精度。
您可能也会对本文感兴趣:
关于作者
Nicolas Vandeput 是供应链数据科学家,专门研究需求预测和库存优化。他在 2016 年创立了自己的咨询公司 SupChains ,并在 2018 年共同创立了 SKU 科学——一个快速、简单、实惠的需求预测*台。尼古拉斯对教育充满热情,他既是一个狂热的学习者,也喜欢在大学教学:自 2014 年以来,他一直在比利时布鲁塞尔为硕士学生教授预测和库存优化。自 2020 年以来,他还在法国巴黎的 CentraleSupelec 教授这两门课程。他于 2018 年出版了 供应链预测的数据科学(2021 年第 2 版),2020 年出版了 库存优化:模型与模拟 。
你想成为一名数据科学家吗?
概述你可以追求的不同角色,以及如何在这个竞争激烈的领域表现出色
在过去的几年里,对数据科学家的需求一直在增长。许多公司都在寻找有经验的人,他们可以为目前可用的大量数据赋予价值。对于许多学生来说,尤其是那些完成自然科学博士学位的学生,成为一名数据科学家是一个很好的机会,可以在开始行业职业生涯的同时应用在大学学到的许多技能。大约 5 年前,我是这些学生中的一员。在完成天文学博士学位的前一年,我决定离开学术界;但我不知道该做什么,也不知道如何开始在学术界之外工作。我非常困惑,直到一位密友给了我一本书,解释什么是数据科学以及如何进入这个奇妙的世界。这本书的结尾是宣传一门课程,声称可以帮助年轻学生成为优秀的数据科学家。大学毕业后,我参加了那门课程,这门课程给了我一些基本技能,帮助我在几个月后找到了第一份工作。
作为一名数据科学家工作了 4 年后,我回顾过去,我希望我能在这个行业的具体方面得到一些建议。这篇博客就是给你这样的建议;数据科学课程中没有解释的关键信息。比如,进入这个行业需要哪些知识?数据科学家在不同公司的职责相同吗?如何在这个竞争激烈的领域出类拔萃?我将根据我的经验回答这些问题,给你一些选择让你完全满意的工作的见解。
我假设你要么正在完成学业(博士或硕士),要么已经博士后快结束了;也许你想改变你的职业。你想知道你是否想成为一名数据科学家。你可能会问:什么是数据科学?嗯,这可能是一个天真的问题;但实际上,这很重要!
根据维基百科的定义,数据科学是“一个跨学科领域,它使用科学方法、流程和算法从许多结构化和非结构化数据中提取知识和见解。要成为一名优秀的数据科学家,你需要具备分析思维,同时还要具备统计、数学和编程方面的良好知识。Josh Wills 对数据科学家做了一个很好的描述,他说“数据科学家是一个比任何软件工程师更擅长统计,比任何统计学家更擅长软件工程的人因此,如果你对这些领域有所了解,或者对其中任何一个领域感兴趣,你就会喜欢这个令人兴奋的领域。但是需要了解哪些编程、统计、数学方面的知识呢?首先,确保熟悉 Python 或 R 和 SQL,因为这些是数据科学家目前使用的编程语言。关于统计,完善你对概率、分布、统计参数和假设检验的理解。数学方面,线性代数和微分学会是你的朋友,让你了解机器学习和深度学习算法背后的逻辑。
这些话题听起来是否对你很有吸引力,以至于你想成为一名数据科学家?如果是的话,恭喜你,欢迎加入俱乐部!现在,接下来是什么?根据我的经验,学习数据科学课程是进入这个充满挑战的领域的最佳方式。一个原因是,这些课程从头到尾都在教你开展数据科学项目的基本技能。数据科学课程让你初次体验如何从数据中收集有用的见解;此外,你有机会开发一个可以在面试中展示的项目;这是在这个行业找工作时非常重要的一点。
一个数据科学项目是通过一系列步骤来实施的,我们称之为数据科学生命周期。这些步骤是数据收集、数据清理、数据探索、特征工程和建模。在这里我简单的解释一下。
数据科学生命周期。作者制作的图像。
****数据收集:这一部分非常重要,因为这一步收集的数据质量决定了你的模型是否成功。数据收集也是项目中需要花费大量时间和精力的步骤之一。有时收集数据仅限于在您公司的数据仓库中搜索;在其他情况下,您需要浏览网站,并与您的领域知识同事密切合作,调查获得所需数据的最佳位置。
****数据清理:一旦你收集了数据,往往需要清理。清理数据的方式取决于您拥有的信息类型;但是,大多数情况下,您会估算缺失值,进行分类编码或者以正确的格式设置变量。在数据清理过程中,拥有领域知识或与拥有此类专业知识的同事合作非常重要,以确保信息可靠且有意义。
****数据探索:在这里你通过各种可视化的方式对数据进行第一次探索:散点图遇到变量之间的相关性;直方图或密度图来推断特征的分布。你甚至可以使用一些聚类技术来查看观察结果之间的相似之处,并找出数据中隐藏的关系。在数据探索过程中,您可能会意识到数据的质量并不好。如果是这种情况,你必须修改清理数据的过程,或者你甚至需要回去收集更多或更好的数据。
****特性工程:在我看来,这是数据科学周期中最激动人心的阶段之一。根据项目的目标,你需要提出在确定预测量时很重要的变量。也许您的数据中有许多相关变量,因此您必须减少要素的数量。为了确保正确的特性工程,您需要与在业务领域拥有专业知识的同事密切合作。
****建模:在这里,您应用最适合您的问题和您拥有的数据的机器学习或深度学习模型。最佳算法通常是基于贵公司定义的关键绩效指标(KPI)或基于贵团队定义的指标(即)进行优化的。,RMSE;f1-得分。这个过程本身是迭代的,这意味着您可能希望先尝试不同的算法,然后优化模型的超参数。在数据建模期间,您还需要解释模型的结果;例如:模型选择什么特征,为什么?(即特征重要性);关于他们的预测指标,目标是如何变化的?(即部分依赖图)。
****项目完成:大多数时候,数据科学家的工作都集中在开发需要上述步骤的数据驱动解决方案上。然而,根据你的具体工作,你可能会也可能不会参与项目的最终确定。例如,假设您是一名顾问,您的项目旨在探索开发一种代码的可能性,该代码可以在给定卫星数据的情况下预测城市的增长率。你同意你的客户有一个原型;也就是说,预处理数据和训练模型的代码以及总结您的分析的文档。在这种情况下,您既参与了代码的开发,也参与了项目的最终确定。现在,让我们假设原型是成功的,也就是说,你的模型是相当准确的,你的客户希望更经常地使用它。在这种情况下,项目包括将模型投入生产的管道的开发。如果您有模型部署的经验,您可能会参与这样的开发。
在前面的示例中,我想向您展示您在数据科学领域可以扮演的不同角色。这完全取决于你的抱负和你想掌握的技能。在这里,我将解释你可以追求的不同角色。
顾问数据科学家:在有限的时间内研究各种主题
顾问数据科学家在不同行业的各种项目中持续工作;因此,您需要广泛的软技能,如分析思维、有效沟通、团队合作,以及快速学习所参与项目领域知识的意愿。由于你通常同时从事不同的项目,你也需要非常好的时间管理技能。
数据科学家顾问的工作有两种类型。第一个也是最常见的是在交付项目中工作。这意味着利用数据科学生命周期为不同的客户设计解决方案。大多数活动包括设计仪表板和向利益相关者传达见解。根据他们的技能,顾问数据科学家也可能参与模型产品化。
顾问数据科学家的另一种工作是开展业务开发和销售活动,这与数据分析师的工作密切相关。在这里,你要参与撰写建议书和向潜在客户进行技术演示。
这份工作的优势:
- 你有机会参与各种项目,这些项目需要处理不同类型的数据和不同的算法。因此,你可以从不同的行业获得很多知识。
- 你与你的利益相关者密切合作;这意味着你的关系网扩大了。
- 工资往往很高。
缺点:
- 短期项目时间有限;这意味着最终交付后,没有改进的空间。
- 大部分职位都是由大公司而不是科技公司发布的;这意味着工作文化可能具有挑战性。
- 工作量通常很大。
如果你有兴趣更多地了解咨询数据科学家的世界,你可以阅读以下博客:
科技公司的数据科学家:学习机器学习工程技能的机会
如果你是一个热爱编程的人,你可能想尝试在一家科技公司建立自己的事业。科技公司的数据科学家投入时间开发机器学习模型的原型,但他们也需要学习适当的工具来将这些原型转化为产品。
通常,数据科学家在 Jupyter 笔记本上构建原型(您将在任何数据科学入门课程中学习到这个工具——如果还没有——的话)。Jupyter 笔记本允许您通过在单个脚本中显示表格和图表来进行快速探索、分析和数据可视化。还可以训练、验证、测试机器学习模型。尽管 Jupyter 笔记本易于使用,但它们并没有提供合适的界面来开发用于生产的模型,原因很简单,因为 Jupyter 笔记本仅用于实验目的。
要将原型转换为生产使用的模型,有必要创建使数据科学生命周期自动化的管道。这包括扩展模型的培训和测试,并将其部署到前端使用。用于执行这些任务的一些工具是 Docker 和 Kubernetes。云计算知识(亚马逊网络服务(AWS));谷歌云(GCP)或 Azure)也非常方便。
*年来,模型产品化已经成为进入数据科学世界的一条不同的职业道路,称为机器学习工程。如果你想了解这个令人兴奋的话题,你可以阅读这些博客:
根据我自己和关系更密切的同事的经验,以下是科技公司数据科学的一些利弊:
优点:
- 科技公司的数据科学家有机会获得许多技能,丰富他们的投资组合。
- 项目期间开发的代码是可重用的,并且可以不断改进。
- 工作环境非常友好。
缺点:
- 根据公司的文化和规模,晋升的可能性可能是有限的。
- 与顾问数据科学家不同,了解不同行业的机会有限。与外部客户的联系也可能受到限制。
- 工资可以低于顾问数据科学家。
如何成为一名优秀的数据科学家,并且不会因为尝试而丧命
尽管对数据科学家的需求每年都在增加,但这一职业竞争相当激烈,尤其是在中高级水*。这就是为什么从一开始就有好的实践对于脱颖而出和专业成长至关重要。以下是我给你的建议,让你成为一名优秀的数据科学家,而不是死于尝试:
Jupyter 笔记本不错,但是…不适合生产代码!
我第一次使用 Jupyter 笔记本电脑是在数据科学课程中。从第一次开始,我就因为它们的易用性而喜欢上了它们。我能够在一个脚本中可视化表格并分析数据;这是完成我课程最后一个项目的完美工具。
我在一家科技公司担任数据科学家的第一份工作是继续使用 Jupyter 笔记本电脑;然而,我很快意识到,在这个工具中开发复杂的函数或类是很困难的,因为笔记本电脑没有允许纠正代码的功能。因此,我花费了大量的时间和精力进行调试。这非常有压力,因为我通常比预期晚交付结果;我开始觉得没有效率。我认为我是一个糟糕的数据科学家。
我决定开始用我的分析中使用的所有函数构建一个 python 库,首先是为了不重复代码,其次是为了更好地与我的数据科学家同事合作。我开始用纯 Python 代码编写这个库。后来,我的同事开始开发更多的功能,使这个库变得更好。代码版本化也允许我们改进我们的团队合作。随着图书馆的发展,我的工作,总的来说,更有成效;然而,创建预测模型的主要渠道仍然在 Jupyter 笔记本上。在我作为数据科学顾问的第二份工作中,我决定使用 Python 代码编辑器来更有效地执行我的项目。这种转变使我能够非常容易地调试代码,并拥有可以快速部署到生产中的原型。
Jupyter 笔记本很棒,但不是用于原型或代码版本控制,也不是用于生产代码。我建议你使用它们来探索你的数据,但是一旦你开始做一个项目,就使用代码编辑器;这会节省你大量的时间!有一些在数据科学社区广泛使用的代码编辑器,比如 Spyder 、 PyCharm 、 Visual Studio 、 Rstudio (针对 R 爱好者)。由于您将进行大量编程,请确保也记录您的代码。对于您创建的每个函数或类,描述其属性、方法、输入和输出参数。这是必不可少的,尤其是在与其他数据科学家和机器学习工程师合作时。拥有良好的编程习惯是成为伟大的数据科学家的步骤之一。
永远记录你的工作!
我很幸运地从始至终执行项目;然而,有时我不得不接管其他同事的工作。在其中的一些机会中,我真的很高兴收到项目的适当文档,包括一个干净的和评论良好的代码;在其他情况下,我想自杀。黄金法则:记录你的工作,就像你要把它交给另一个同事一样。这种做法带来很多好处:你更容易记住你在一个项目中做了什么。此外,它允许其他数据科学家在你换工作的情况下接管你的开发。如何做好一个项目的文档?以下是一些建议:
- 不要使用被动语态。
- 描述缩写:当新同事阅读你的报告时,这特别有用。
- 从项目的目标开始:目标是什么?你想学什么?你的假设是什么?
- 保持心流,即不要跳入不同的想法;详细解释每一部分。
最后,将您的报告和代码一起保存在 Git 中。
不要停止学习
数据科学行业不断发展,很难跟上时代的步伐;然而,了解最新趋势的最佳方式是阅读博客、收听数据科学播客和订阅 YouTube 推广频道。跟踪某个特定主题的在线课程对于在这个领域的成长也是至关重要的。不要停止学习,因为这是保持竞争力的关键。
迎接新挑战
当我完成博士学位时,我害怕去那些我对其领域知识不感兴趣的公司工作。我必须说我大错特错了!所有的项目都非常有趣和具有挑战性。因此,我给你的最后一个建议是,害怕是正常的,但如果你乐于接受新的挑战,你会发现你的工作总是有回报和有吸引力的。
这篇文章旨在让你一瞥数据科学的世界;你对这个行业有什么期待,以及根据你的兴趣可以发展的不同工作。这个博客,当然,是基于我的个人经历。我希望这篇文章能帮助您更好地了解令人兴奋的数据科学世界。另一方面,如果你是一名资深的数据科学家,请在下面留下你的评论,谈谈你在这个领域的经历以及其他哪些技能让你成为了一名伟大的数据科学家。
感谢阅读!**
是否要对未标记的文本数据进行聚类?尝试主题建模
你将需要 5 分钟的时间来理解和实现 Python 中的 LDA
由 Unsplash 上的Cup 先生/杨奇煜·巴拉拍摄的照片
介绍
我们生活在一个被大量文本信息包围的时代,如调查回复、社交媒体评论、推文等。找到满足个人需求的合适信息是一项挑战,尤其是在处理大量不同的数据时。
感谢主题建模,一个自然语言处理的时代,用于通过将大量 未标记文本 数据分组/聚类成主题来有效地分析它们。我们将尝试用 python 来介绍一种叫做 LDA 的主题建模技术。
LDA —它是如何工作的
LDA 代表潜在的狄利克雷分配。该技术试图通过使用基于以下假设的概率分布来从文档语料库中发现主题:
- 假设 1: 主题相似的文档使用相似的词组,意味着所有的文档都是潜在主题上的概率分布。
- 假设 2 :潜在主题可以通过搜索跨语料库的文档中通常一起出现的词组来发现,意味着主题是词上的概率分布。
LDA 的实施
主轴承轴瓦
- 选择固定数量的 K 个主题进行探索。
- 对于语料库中的每个文档,随机将文档中的每个单词分配给其中一个主题。这一步给出了所有文档的主题表示和所有主题的单词分布。
- 从上一步开始,遍历每个文档中的单词来改进这些主题
- 在这个过程的最后,每个文档将被分配一个主题
关于数据
- 这是澳大利亚广播公司八年来发布的新闻,可在 Kaggle 上获得。该数据有两个主要列:
- 发布日期 :文章以 yyyyMMdd 格式发布的日期。
- headline _ text:标题文字,英文
- 数据集具有
数据加载和子集选择
第 6 行显示(1226258,2)意味着 1226258 行和 2 列。由于我们有很多行,为了加快处理速度,我决定选择 20 000 行的子集。这个操作在第 9 行和第 10 行完成。如果愿意,您可以更改观察的数量。
第 13 行显示了所选样本中的 5 个随机行,如下所示
来自 sample_df 数据集的 5 个随机行(图片由作者提供)
LDA数据准备
建立文档 X 术语矩阵
我们对 publish_data 列不感兴趣,因为我们将只使用 headline_text 数据。
如下定义计数矢量器对象,我们忽略:
- 在我们的文档语料库中出现超过 95%次的所有术语。因为
max_df
threshold (0.95)意味着出现次数超过该值的项不显著。这种具体情况下,大部分会是stopwords
。 - 在整个语料库中出现少于三次(
min_df = 3
)的所有术语。
第 9 行显示了下面的输出,这意味着我们有 20.000 个文档和 6425 个不同的单词。
<20000x6425 sparse matrix of type '<class 'numpy.int64'>'
with 89645 stored elements in Compressed Sparse Row format>
让我们看看从文档中提取的一些单词/特征。
第 5 行显示以下单词:
['zimbabwe', 'zimbabwean', 'zone', 'zones', 'zoo']
建立 LDA 模型
从我们的 DTM 矩阵,我们现在可以建立 LDA 模型,从下划线文本中提取主题。要提取的主题数量是一个超参数,我们将使用 7 个主题。
由于 LDA 是一种迭代算法,在我们的例子中,我们将有 30 次迭代,但默认值是 10。随机状态值可以是任何数字,其目的是再现相同的结果。
显示每个主题的热门词汇
从 LDA 模型中,我们现在可以从七个主题中的每一个中生成热门单词。
前面的代码生成以下输出。
THE TOP 10 WORDS FOR TOPIC #0
['weather', 'election', 'labor', 'urged', 'qld', 'act', 'council', 'nsw', 'new', 'govt']
THE TOP 10 WORDS FOR TOPIC #1
['report', 'indigenous', 'country', 'rural', 'charged', 'accused', 'new', 'health', 'calls', 'says']
THE TOP 10 WORDS FOR TOPIC #2
['queensland', 'charges', 'hospital', 'case', 'guilty', 'child', 'sex', 'murder', 'man', 'court']
THE TOP 10 WORDS FOR TOPIC #3
['face', 'england', 'years', 'win', 'australian', 'talks', 'wins', 'final', 'cup', 'world']
THE TOP 10 WORDS FOR TOPIC #4
['probe', 'dead', 'woman', 'killed', 'dies', 'car', 'crash', 'man', 'interview', 'police']
THE TOP 10 WORDS FOR TOPIC #5
['live', 'return', 'care', 'residents', 'test', 'australia', 'new', 'change', 'workers', 'day']
THE TOP 10 WORDS FOR TOPIC #6
['news', 'search', 'west', 'market', 'coronavirus', 'national', 'gold', 'farmers', 'sydney', 'coast']
观察:看起来像
- 主题 1 更加面向政府
- 主题 3 似乎更多的是关于体育而不是其他事情
将发现的主题标签附加到原始文档上
代码的 第 5 行 显示:(20000,7),意味着 final_topics 包含对于我们的 20,000 个文档中的每一个,文档属于 7 个主题中的每一个的可能性的概率分数。
第 11 行显示了前 5 个文档及其相应的主题
文档及其相关主题(图片由作者提供)
根据我们的 LDA 模型:
- 第一和第二个文档属于第 4 个主题。
- 第三和第四个文档属于第四个主题。
- 第五个文档属于第 6 个主题。
- 等等。
有些可视化
从下面的可视化中,我们可以动态地得到每个主题相关的单词集。
这个面板在左边显示由圆圈代表的主题,在右边显示它们的单词集。在这个图表中,我们可以看到主题 3 由单词法庭、男人、谋杀、性、孩子、有罪等表示。从那些话中,我们可以推断出这个题目讲的是 戏剧性 的事情。
结论
你做到了!从无标签数据到创建最终模型,以图形方式将每个文档与一个主题相关联。使用主题建模,我们事先并不知道正确的主题,因此拥有能够帮助解释模型生成的单词的业务知识可能会更好。我希望你喜欢这篇文章,你可以从下面的资源中探索更多。
额外资源
Doc2Map:像在谷歌地图上散步一样浏览文档
为主题建模构建漂亮的交互式可视化
作为一名数据科学家,你应该知道让你的成果大放异彩有多难。
在本文中,您将了解我在第一份工作中的经历,我想出的想法,我遇到的尝试和错误,最终成功地建立了一种直观的方式来有效地可视化数千个文档并与之交互。最后,你会看到你现在可以多么容易地重现这些形象,并使它们成为你自己的形象。
在最后的结果 取一个偷峰
目录
–第一份工作的旅程
–读取异构文档
–预处理
–嵌入的力量
–添加粒度
–新视野
–选择好的聚类算法
–精度 VS 信息困境
–
这是一个关于 Doc2Map 的创建和工作的故事——如果你只是想使用 Doc2Map 的话,这里有一个快速教程https://medium.com/@louisgeisler3/doc2map-the-best-topic-visualization-library-tutorial-82b603d1d357
第一份工作的旅程
作为一名数据科学学生,在没有任何经验的情况下找到第一份实习工作可能会非常复杂。但有时运气会有所帮助,这正是我的简历引起法国最高行政法院注意的原因。
行政法院要求我研究和开发一种工具,用于可视化和探索它收到的所有法律投诉(每年大约 250,0 00 起),以便找到可以一起处理的类似投诉,从而提高该机构的效率。
作者图片
因为这些文件是用法语写的,而且是保密的——有些案件甚至涉及到谷歌——它们将被一个来自 Kaggle 的玩具维基百科数据集所取代。
阅读 Heteroclite 文档
第一个问题是文档不是统一类型的 PDF 文件,(一些是放在 PDF 文件中的纯文本,而另一些只是纸质文档的图像扫描),因此它们应该按照下图分别处理:
作者图片
如您所见,决定 PDF 文档是纯文本还是扫描图像的关键标准是一个文档每页*均行数的阈值。
根据我的测试,从 PDF 中提取文本和图像的最佳选择是 Xpdf 命令行工具 (Pdf2Txt 和 PDF images)——没有 python 的包装器是多么悲哀啊…
对于 OCR 图像, Tesseract 是最佳选择。
注意: Apache Tika 对于更广泛的文档格式来说可能是一个不错的选择,但是由于它是一个 java 应用程序,所以速度相当慢。因此,对于特定的使用情形来说,它不是最好的。然而,Doc2Map 的公共版本将使用 Tika 来处理任何类型的文档。
预处理
奇怪的是,似乎有时候经典的 NLP 库并不总是简单任务的最佳选择。要删除停用词,您可能更喜欢使用停用词-iso 库,而对于词汇化,只需使用词汇化-列表。
为什么不用 spaCy?因为默认情况下,它加载了很多无用的工具,会减慢进程。另一个原因是,它的 lemmatizer 似乎不能很好地将名词转换为单数词根。
嵌入的力量
当问及如何可视化一个大的文档语料库时,你首先想到的一个想法可能是使用一种文档嵌入方法( Doc2Vec ) 将单词转换成 300 维向量。然后,对向量应用任何降维技术(如【UMAP】)将它们投影到 2D 空间。
作者图片
通过一个好的库和一些调整,您可以显示一个漂亮的交互式可视化,甚至可以绑定事件,以便在单击某个点时打开文件或 URL:
全屏/移动版 |用创建 Plotly 和简单的维基百科数据集
不幸的是,它似乎没有多大帮助。是的,你可以有一个全局视图,但是你缺乏粒度:你缺乏中间信息来理解文档的整体组织。
您可能会想象自己正在阅读一张未知世界的地图,在这张地图上,国家、州和城市的名称已经被删除,您只能看到不同的房屋(在我们的案例文档中)。
因此,我们现在有两个问题要解决:
- 如何增加地图的粒度?
- 如何找到有趣的名字作为中级信息添加?
增加粒度
经过多次搜索,似乎最好的工具是传单 javascript 库的 MarkerCluster 插件。它有许多优点。首先,它类似于谷歌地图,是一个直观的工具。其次,它可能是最漂亮、最高效的图书馆:
全屏/手机版 |传单插件示例: MarkerCluster
新的愿景
发明国家、州和城市的名称并不是一件容易的事情——我们如何创造国家、州和城市的名称呢?我们如何通过集群来重组我们的文档呢?
简答: Top2Vec ,2020 年发布的全新话题建模方法。Top2Vec 根据这个方案巧妙的结合了扎实的方法:**word2vec⁴+doc2vecumaphdbscan⁵**。
让我们看看它是如何工作的:
- Word2Vec+Doc2Vec 项目文档和单词进入同一个 300 维的语义空间。
- UMAP 将这些向量投影到二维空间中。
作者图片
3.HDBSCAN 将通过查找密集区域并计算其质心来创建聚类。
4.这些质心将定义主题的位置,最*邻将定义该主题的主题。
作者图片
总而言之,Top2Vec 将在地图上找到文档簇的位置,并将它们与地图上最*的单词链接起来。
作者图片
注意:有一个小技巧,为了更简单的说明,我保守了秘密。事实上,一旦文档簇被创建,我们回到 300 维空间,只有在那里找到质心和最*的单词。维度越多,信息量越大。
选择好的聚类算法
HDBSCAN 是一个很棒的聚类算法,它做得很好。然而,它有两个紧密相连的问题:
- HDBSCAN 将不够密集的区域定义为噪声,并完全忽略它们:
https://hdb scan . readthedocs . io/en/latest/comparising _ clustering _ algorithms . html
- HDBSCAN 使用单一链接树方法,其结果是创建一个不*衡的树。HDBSCAN 的核心思想与用集群和子集群划分地图的思想不相容,正如文档中所解释的:
例如,HDBSCAN 会生成这样的树:
图片来自维基百科
但是对于我们的地图,我们更喜欢像这样的信息量更大的树,有更多的子集群:
图片来自维基百科
精确与信息的困境
在精确度和信息之间很难找到*衡。精确意味着拟合最佳的密集区,而信息意味着增加更多的聚类。
我们必须创建一个特殊的树(称为树状图),在 y 轴上为用户提供不同的缩放级别。使用下面的公式,我们可以根据它们的面积将缩放级别分配给我们找到的每个聚类:
连续与离散变焦级别
你可能已经猜到了这个问题:我们需要一个离散的整数缩放级别,而这个公式给了我们一个实数。更重要的是,两个或多个缩放级别之间可能没有聚类:
作者图片
此外,为了具有良好的可视化,如果一个集群的面积大于屏幕上可见的面积,则不应该显示该集群——只向用户显示一个集群是没有意义的。因此,如果我们用缩放级别指标替换面积指标,这意味着您不应该以高于公式计算的缩放级别来显示集群。(直观上是说在图上,集群只能往上走。)
为了解决这个具有许多约束的复杂问题,可能需要设计一种全新的聚类算法来解决寻找最佳折衷的问题,但这将是一个漫长而困难的任务。
病房联动
简单来说,似乎最符合我们标准的算法是沃德链接层次聚类,它产生一个很好的*衡聚类,如下图所示。
让我们看看将这种方法应用到我们的维基百科玩具数据集会是什么样子,红点代表节点,蓝点代表树叶(可点击):
全屏/手机版 |用创建 Plotly 和简单的维基百科数据集
正如你所看到的,这棵树非常*衡,但是它不在乎缩放比例。因此,我们将不得不强制集群仅定位在整数缩放级别。同样,有许多选择保留或删除集群的可能性。该树是通过实验测试不同的可能性得到的,所以我将跳过细节,直接看结果:
全屏/移动版 |用 Plotly 和简单维基数据集创建(蓝点可点击)
最后,我们得到一个根据缩放级别排序的*衡的树。因此,我们最终可以构建一个图,其中聚类根据用户选择的缩放级别而变化:
全屏/移动版 |用创建 Plotly 和简单的维基百科数据集(点可点击,用底部光标缩放可选)
在下一步中,我们只需将聚类的缩放级别链接到地图的实际缩放级别,以创建我们的最终可视化。
文档世界的地图
有了这个修剪过的层次树,您可以获得一个漂亮的可视化效果来轻松总结您的文档集,瞧:
全屏/手机版 |用传单、 MarkerCluster 、 MarkerPreCluster(自己创建)和简单维基百科数据 t
为了创建这个版本,我必须创建自己的 javascript 插件,以显示预先聚集的数据:https://github.com/louisgeisler/Leaflet.MarkerPreCluster
结论
Doc2Map 将使您的工作在人们的眼中熠熠生辉,这要归功于一种新的可视化主题建模方式,这种方式增加了传统技术所缺乏的粒度,并允许您显示美丽的交互式视图,让人们使用并理解它的工作方式。
它使用了可靠且经过认可的技术,如 Doc2Vec 、 UMAP、HDBSCAN 。
库 Doc2Map 可以在 GitHub 上免费获得:https://github.com/louisgeisler/Doc2Map
而关于如何使用 Doc2Map 的教程正好在这里:https://medium . com/@ louisgeisler 3/doc 2 map-the-best-topic-visualization-library-tutorial-82b 603d 1d 357
远景
今天,机器学习似乎在网络上无处不在,但它还没有出现在我们的机器中。这就是为什么,我坚信在不久的将来,操作系统将直接包含 NLP 和机器视觉工具,作为显示和浏览文件和文件夹的一种方式。
全屏/手机版 |很快,在下一个 Ubuntu 版本;-)
Doc2Map 也是一种全新的浏览网站的方式——你可以想象有一天所有的网站,如维基百科或 Medium,都会有一个 DocMap 来帮助人们获得内容的全局视图。
Doc2Map 还可以很容易地转换成“Image2Map”来组织和显示图像。事实上,您可以用图像自动编码器来替换 Word2Vec 和 Doc2Vec,而不是使用最接*质心的单词来描述聚类,您可以直接使用质心位置作为自动编码器解码器部分的输入,以生成概括一个聚类内容的图像。
参考文献
[1]米科洛夫·托马斯,向量空间中单词表征的有效估计* (2013),https://arxiv.org/abs/1301.3781.pdf*
[2]麦金尼斯,L,希利,J, UMAP:一致流形逼*与降维投影 (2018) ,https://arxiv.org/pdf/1802.03426.pdf
[3]https://arxiv.org/abs/2008.09470.pdf 莫迪安杰洛夫, Top2Vec:主题的分布式表示(2020)
[4]郭克勒,托马斯·米科洛夫,分发陈述的句子和文件* (2014),https://arxiv.org/pdf/1405.4053.pdf*
[5] Ricardo J.G.B. Campello,Davoud Moulavi 和 Joerg Sander,基于分层密度估计的基于密度的聚类(2013)* ,http://pdf.xuebalib.com:1262/2ac1mJln8ATx.pdf*
你听说过我们的救世主 Docker 吗?
使用 Docker 和创建自己的开发环境简介
介绍
在快速变化的环境中工作,越来越多的工具被开源,我的笔记本电脑变得一团糟。安装大量的应用程序,却忘记清理它们。类似地,我用 python 做了一些事情——没有虚拟环境,所以基本上,许多库可以在我的笔记本电脑上自由漫游。
最初的解决方案是从我的笔记本电脑中移除 Python 和所有其他杂乱的东西。但是后来我发现,如果我为一篇博客文章或与工作相关的事情进行 PoC,我会在同一个地方结束(虽然如果我转移到虚拟环境,就不会使用 python)。然后我找到了 Docker。我花了一段时间才明白那里发生了什么,以及我如何利用它来获得优势。
准备
如果你想继续下去,你应该:
- Docker 来自官方页面
- 一些 IDE,或者你可以使用终端或 Jupyter 笔记本
了解什么是 docker
那么这个 docker 是什么?这位魔术师大师是如何解决我的问题的?
Docker 是一个*台。您可以创建一个特定的配置包,并在您自己的隔离环境中运行它,我们称之为容器。在某种程度上,容器实际上类似于虚拟机。你可以很容易地创建一个轻量级的容器,只需要一些相关的库和应用程序,并在上面运行你的代码。如果您决定与他人共享您的代码,您唯一需要共享的就是 docker 映像。
好,我提到的这个形象是什么?一堆指令来设置一个包含所有配置的容器,相关文件在你的工作目录中。
因为它是一个*台,我们有一个服务器-客户端的关系。
- 客户端——最有可能是与 docker-engine 交互的 CLI
- 服务器——在你的 docker-engine 中,是什么协调和映射了不同的东西
- 注册表 Docker 图像存储在其中,以后其他用户可以提取这些图像
好了,理论够了,我们去实践吧。
入门指南
好了,让我们从检查 docker 版本开始:
作者图片
如果我们看到我们有 docker(任何输出的版本),我们就可以开始了,至少现在是这样!
如果您至少了解终端的一些基础知识,您可能会熟悉
ps
该命令列出了所有正在运行的进程及其 PID。幸运的是,我们为 docker 准备了类似的东西:
作者图片
我们可以看到,我有 0 个 docker 进程正在运行。我们如何让 docker 流程运行起来?
你说跑?这就是答案!
作者图片
让我们打破僵局:
docker —我们正在 docker 上执行
运行 —创建/拉动并运行容器
hello-world —加载到容器中的图像
Postgres 数据库
因此,假设我们需要在我们的机器上运行 Postgres DB 来进行一些 POC。为此,我们需要:
- 在 docker hub 中找到 docker 图像(或者如果你在那里有一些私人回购)
- 决定我们要用的版本
- 码头工人运行它
第一步
如果我们在谷歌上搜索 docker Postgres,我们会看到 docker hub 链接到顶部的 Postgres 图片。在那里,我们可以看到大量关于图像及其用法的信息。
第二步
作者图片
好,这里我们看到 13.2,13,最新的,13-阿尔卑斯山。这是什么意思?
即 13.2 数字表示特定的 Postgres 版本。最新版——新版本,如果有新版本的话,会一直更新到新版本。最后但同样重要的是——阿尔卑斯山。Alpine 是应用程序运行的最基本的框架。Alpine 版本尽可能的轻便,只是因为它只有必需品,所以与其他版本相比,它的尺寸很小。使用 alpine 的好处——获得最少的东西,并且只添加相关的东西。你的 docker 容器将尽可能的小。易于移动和部署。
好吧,那我们选 13.2。
第三步
让我们做……
docker run postgres:13.2
作者图片
好的,它没有运行,但是我们看到了问题所在——我们需要为它传递一个密码,用-e. -e 表示环境变量
作者图片
我们看到终端处于运行进程模式,为了在同一个会话中做一些事情,我们需要终止 Postgres 进程。有一种方法可以让 Postgress docker 进程运行,并在同一个终端会话上做一些事情。我们可以使用 -d 来运行分离的流程。
作者图片
现在我们有了一些长绳子。这个字符串是我们的 docker 容器 ID。如果我们运行 docker ps ,我们会看到 docker 容器已经启动并正在运行:
作者图片
我们可以看到,docker ps 中的容器 id 与我们从 docker run 中获得的不一样,也不一样长。如果我们仔细观察,我们可以看到 docker ps one 是较长的一个子串。docker 很酷的一点是,它匹配你的容器 id 的开始,你不需要粘贴它的完整版本!
但是等等,我不是说过 docker 是一个隔离的环境吗?是的。我们不会对数据库做任何事情,因为我们没有暴露任何与它交互的端口。为此,我们需要使用 -p
docker run -e POSTGRES_PASSWORD=myplaintextpassword **-p HOST_PORT:DOCKER_CONTAINER** -d postgres:13.2
Dockerfile 文件
好了,现在你可能会问自己,为什么这个人要向我解释,如何键入长命令来运行一些孤立的东西。重复,无聊。我该怎么分享?将命令发送给下一个人?效率不高…
没错,吊儿郎当是有道理的,但是营造环境,就不是了。这就是我们有 Dockerfile 的原因。
Dockerfile 基本上就是所有这些用环境变量映射出来的指令。所以对于我们的 Postgres,我们可以创建这样的:
作者图片
要构建它,请在 Dockerfile 的文件夹中运行:
docker build .
我们应该看到这样的东西
作者图片
bd9416c1457a —是新建的 docker 图像 id。我们现在可以像这样运行我们的容器:
作者图片
需要提到的一点是,当 docker 在每个步骤中构建您的 Dockerfile 时,它会创建一个新的 Docker 映像,并将其传递给下一个映像。所有这些变量都将放在缓存中,如果有地方可以重用的话,也就是说,在末尾添加一个新的环境变量:
作者图片
它的好处是,我们可以通过在底部添加更改来更快地构建映像。我们只需要从零开始构建新添加的部分!尽管如果我们交换密码和用户名的位置,我们将不得不再次构建映像:
作者图片
与容器交互
如果您决定优雅地关闭 docker 进程,您可以使用
docker stop CONTAINER_ID
或者你想杀了它
docker kill CONTAINER_ID
稍后,您可以使用以下命令检查停止的进程
作者图片
并恢复您的容器
作者图片
运行几个 docker 进程。
好吧,所以所有的都很容易与一个 docker 过程;我们创建一个 Dockerfile 文件并运行它。如果我需要一个额外的,我可以创建并运行它。但是在某些时候,它会很快失控,因为每次 docker 运行都需要设置端口和其他信息。
有一个解决方案— docker-compose。
Docker 撰写
这是一个 YAML 文件,包含了更多关于构建什么以及不同 docker 文件如何一起播放的信息。
作者图片
让我们讨论一些事情:
- 版本—合成文件格式的版本。通过查看 docker 文档来检查你的 docker 引擎是否与之兼容
- app —应用程序/服务将使用一些预定义的 docker 映像(即气流), 8080 端口映射到我们的本地机器 8080
- db—一个数据库应用程序,我们将使用 Dockerfile 对其进行容器化,该文件位于数据库目录中
好了,当我们准备好文件后,我们如何构建它呢?
如果您在目录中,您有文件:
docker-compose up -d --build
此外,您可以指定文件的完整路径:
docker-compose -f "PATH/YOU/HAVE/PUT/DOCKERCOMPOSE/FILE/docker-compose.yml" up -d --build
摘要
总而言之——Docker FTW。
这使得开发变得更加容易。创建一个图像,在其上运行你的应用程序,看看它是否有效。
我个人的原因是:
- 需要一个隔离的 ENV 用于基准测试/测试等。(使用 GitHub actions 和 Travis,您可以在 docker 映像中测试您的应用程序)
- 如果有问题,测试版本升级。
- 如果你弄乱了你的本地环境,而你又懒得去清理它
顺便说一下,代码以类似的方式保存在我的 GitHub 的一个 Jupyter 笔记本中,在那里你可以交互地运行它并看到结果。基本上是一个后续的机会,看看它在实践中如何工作。
基于 Docker 的 RStudio 和 PostgreSQL
这是与 Docker、PostgreSQL 数据库和异常数据集相关的两篇文章的第一部分。
背景
在最*的 LinkedIn 帖子(原创和 Rami 的转帖和推文)中,我向互联网询问他们最喜欢的异常检测问题数据集,特别是在时间序列领域。我收到了很多回复,现在有了大量的数据可以处理,谢谢回复的人们。
为了处理这些数据,我需要一个比将数据保存为 CSV 文件、qs
对象或 R 包更好的解决方案。我想要一个数据库来存储原始输入数据、我处理过的数据和算法结果。虽然我过去以传统方式设置数据库,但这次我希望整个代码库是可移植的&可复制的。
结果,我用 Docker 设置了所有的东西,它工作起来很流畅。如果你想了解如何做到这一点,请关注我的下一篇文章:
- 第一部分—(这篇文章)将教你如何为个人 PostgreSQL 数据库+ RStudio 代码开发建立一个简单的可重复的基于 Docker 的工作流
- 第二部分—(下一篇文章)将是异常数据的 ETL 管道
你为什么要读这个?
在本教程结束时,您将能够快速设置一个基于 Docker 的个人 PostgreSQL 数据库。您将学习如何使用docker-compose
快速部署 PostgreSQL & RStudio。您将能够访问 R 中的数据库,并立即开始开发。最重要的是,整个过程将是完全可复制的,因为您继承了安装脚本和 Docker 的优点。
本教程假设您熟悉 Docker 环境中的 Docker 和 RStudio。如果你不是,我推荐先阅读 R 中的 可复制作品。
概观
您将启动两个 Docker 映像:
- PostgreSQL 图像。我选择
[postgres:13.3](https://hub.docker.com/_/postgres/)
- RStudio 图像。我选择
[hatmatrix/blog:base](https://hub.docker.com/r/hatmatrix/blog/tags?page=1&ordering=last_updated)
为了在容器寿命结束后永久存储数据,您将装入两个卷,每个容器一个卷。我选择了:
- 对于 PostgreSQL:
$HOME/docker/volumes/postgres
- 对于 R 项目:
$HOME/github
由作者创建的设置概述。从 flaticon.com 获得的图标。
除了容器内 PostgreSQL 之外,这些路径都不是特殊的;你可以根据自己的喜好定制其他的。默认情况下,postgres:13.3
希望数据库位于/var/lib/postgresql/data
。如果您选择另一个数据库,请相应地修改它。
我使用docker-compose
同时启动 PostgreSQL 和 RStudio 服务。这很方便,同时也确保了 PostgreSQL 服务首先运行,然后是 RStudio。只需几个命令就可以轻松启动或停止所有服务。
首次设置
第一次设置 PostgreSQL 数据库时,需要运行这些步骤。我已经将这些步骤存储在[00-postgres-init.sh](https://github.com/rsangole/postgres/blob/master/00-postgres-init.sh)
中。
1-目录设置
您需要一个本地目录来存储 PostgreSQL 数据库。第 3-10 行为您处理这个问题。
2-PostgreSQL 设置
现在是设置数据库的时候了。您至少需要两步才能开始:
- 一个新的“角色”(类似于登录),有权创建新的数据库。
- 至少要在一个数据库中工作。在我的脚本中,我做了两个:
work
和anomaly
。
要操作数据库,需要运行 PostgreSQL 服务器来处理psql
命令。您将使用docker run
启动一个。您需要使用-v
安装正确的卷。接下来,我们通过将psql
命令传送到docker exec
来创建角色和数据库。然后,我们停止集装箱
总之,现在我有了一个 PostgresSQL 数据库:
- 存储在
$HOME/docker/volumes/postgres
- 有了新的角色
rahul
和密码pass
- 有两个数据库:
work
和anomaly
日常工作流程
tldr:你是如何开始的?
- 将
[docker-compose.yml](https://github.com/rsangole/postgres/blob/master/docker-compose.yml)
存储在本地目录中 - 如果您更改了我选择的图像/目录,请对其进行修改
- 在 shell 中,运行
docker-compose up -d
protip :要启动一个浏览器(firefox for me)直接进入 RStudio,在你有docker-compose.yml
的目录下运行这个命令:
docker-compose up -d; firefox localhost:8787
pro-protip :保存一个别名,通用化命令。-f
参数指示docker-compose
你想要使用哪个文件。现在可以在系统的任何地方运行。
崩溃
[docker-compose.yml](https://github.com/rsangole/postgres/blob/master/docker-compose.yml)
里有什么?我们正在创建两个服务,一个叫做db
,另一个叫做rstudio
。⁴
先来看db
。如果你熟悉docker run
args,大多数参数看起来都很熟悉。这里的新特性是restart: unless-stopped
arg,它告诉 Docker 只在 PostgreSQL 当前停止时才启动它。
第二个服务是rstudio
。除了典型的参数之外,这里有趣的参数是depends_on
,它告诉 Docker 只在数据库启动并运行之后运行这个映像。太棒了。
通过 R 连接
用于测试您的连接。运行你的DBI::
命令,除了有一个关键的不同。
建立连接时,确保host
的名称是您在docker-compose.yml
中选择的数据库服务的名称。(在 docker 之外,您通常会使用localhost
来连接本地 PostgreSQL 服务器)。
就是这样!你现在要去比赛了。像*常一样使用数据库
停止服务
您有两种选择:
docker-compose stop
将停止服务,您可以使用docker-compose start
重新启动服务。docker-compose down
将容器也取出。再次运行docker-compose up
开始运行。
8 月 8 日编辑:我刚刚得知,我的原始博客的脚注没有转到媒体上。他们在这里:
- 我喋喋不休地谈论“个人”,因为我没有在工作环境中设置适当的角色、授权等。但是,它对我个人使用来说足够好了。
- 这是我自己根据
[rocker/rstudio](https://hub.docker.com/r/rocker/rstudio)
拍摄的照片 - 属性: DB 图标由像素完善
文件夹图标由图标极客 26
RAM 图标由 Freepik
存储图标由 Smashicons - 这些只是标签,你可以随便叫它们
原载于https://rsangole . netlify . app。
Docker Compose 面向绝对初学者——它是如何工作的以及如何使用它(+示例)
定义和运行多容器 Docker 应用程序
将你的容器演奏成一首美妙的交响乐(图片由加布里埃尔·桑托斯在像素上提供)
在第 1 部分:Docker 的基础知识中,我们着重于建立 Docker 形象。从该映像创建容器非常简单,只需执行一条命令。Docker-compose 进一步自动化了这一点。在本文中,我们将创建一个包含多个容器的运行配置的文件。然后,我们可以用一个命令构建所有的图像并运行所有的容器!
我们将借助一个多容器应用程序的详细示例来探索 compose。我们将旋转多个相互连接的容器。您可以将本演练用作自己项目的指南。在本文结束时,您将能够:
- 了解 Docker Compose 是什么以及它是如何工作的
- 了解 Docker Compose 为 Docker 增加了哪些优势,让您的生活更加轻松
- 了解何时使用 Docker 撰写
- 能够使用 Compose 启动和管理多个容器
- 能够吹嘘您的许多基础架构实现了自动化
在 这篇文章 中,我们探讨了 Docker 的基础知识;如果对 Docker 不熟悉,建议先看一下。它详细介绍了如何创建 Docker 图像以及如何和何时使用这些图像。
首先,我们要看看 Compose 的优点;我们为什么需要这个工具,它能为我们提供什么?然后我们将有一个真实的代码示例,向您展示如何使用 Compose。
1.为什么使用 docker compose?
在这一部分,我们将讨论使用 Compose 的主要原因。
一个文件中的所有配置
Compose 围绕着一个名为docker-compose.yml
的配置文件。在其中,我们定义了我们所有的服务。将服务视为应用程序的一部分;例如数据库或 API。我们所有的服务都依赖于我们用来创建容器的图像。旋转容器可以有多种选择;这些选项的配置方式将存储在 yml 文件中。
这些运行选项的一个例子是我们在第 1 部分中定义的端口映射。我们不得不在我们的终端打电话给docker run —publish 5000:5000 python-docker
。Compose 允许我们在一个文件中定义这些选项。稍后我们会看到一个这样的例子。
Compose 在容器中组织我们所有的服务(图片由 Tom Fisk 提供)
将我们所有的服务及其相应的构建选项放在一个文件中的另一个好处是,我们可以通过调用docker-compose up
一次构建并运行我们所有的服务!
使用环境变量
使用环境变量可以更灵活地运行我们的容器。我们可以为docker-compose up
命令提供一个包含一些环境变量的文件。这样,我们可以安全地提供密码,而不必将它们硬编码到配置文件中。
一个例子:我们将创建一个名为.env
的文件,它包含DBPASSWORD=secretpass
。然后我们可以使用 DBPASSWORD 作为docker-compose.yml
中的变量。
为此,我们在调用 docker-compose: docker-compose --env-file .env up
时指定我们的 env 文件。这使得我们的密码不在我们的库和 docker-compose.yml 中,而且它提供了更多的灵活性和更整洁的项目,因为我们的密码不再是硬编码的了。
共享集装箱网络
当我们使用 Compose 构建容器时,它定义了一个所有服务共享的网络。这意味着所有服务都可以在内部通信。
让我们用一个例子来说明这一点。假设我们将应用程序托管在 beersnob.com 上。当我们的 API 需要与我们的数据库通信时,它不需要通过 beersnob.com:5432 连接,而是可以在内部调用数据库。这对于安全性来说非常重要,因为这意味着只有我们的服务可以访问我们的数据库,客户端无法从应用程序外部访问服务。
可移植性和版本控制
因为我们把所有的配置都放在一个文件中,所以我们可以通过 Git 这样的版本控制系统轻松地共享这个文件。用户只需拉出 docker-compose.yml 和源代码,他们就可以运行所有的容器了!一个额外的好处是,我们保留了对我们的配置所做的所有更改的完整历史,以便我们可以随时恢复以前的版本。这也使得为这个应用程序建立 CI/CD 管道变得更加容易,我们将在以后的部分中讨论这一点。
灵活性
因为我们所有的服务都是彼此完全隔离的,所以我们可以很容易地添加新的服务。也许将来我们的应用程序需要一些缓存→只需旋转一个 Redis 容器!像我们的 API 这样的其他服务可以很容易地通过内部网络连接到新的 Redis 服务。
2.Docker 如何工作:创建我们的第一个容器
好了,说够了;让我们看看一些代码!为了真正理解 Compose 如何工作以及如何使用它,我们将构建一个容器化的应用程序。
2.1.目标
我们正在创建一个名为 BeerSnob 的应用程序;一个专门为喝优质啤酒的人建立的网站。它允许用户分享关于他们在特定场所喝的啤酒的评论;在提供价格和口味信息的同时,对场地和啤酒进行评级。
这个应用程序需要一个网站,一个 API 和一个数据库。在 这篇文章 中,我们创建了一个具有迁移功能的数据库模型,在 这篇文章 中,我们为该应用创建了一个 API。现在让我们继续创建运行这些服务的基础设施。
2.2 概述
首先,我们将看看这张我们希望 BeerSnob 的建筑看起来是什么样的美丽图表:
我们的目标概述:我们在 Docker 中的应用(图片由作者提供)
那么这一切意味着什么呢?首先要注意的是,我们现在有多个互连的服务运行在我们的单个服务器上。这本身就令人印象深刻,但它会变得更好。让我们先走过所有的街区:
我们将从包含所有其他内容的大灰色块开始。这是一个网络,在这个网络中,我们的所有服务(其他块)都位于我们的服务器上。请注意,只有两种方法可以进入我们的网络;通过端口 80 (http)、端口 443 (https)和端口 54321 从外部访问数据库。我们稍后会进入访问
橙色块:这是我们的 web 服务器和反向代理。
网络服务器保存我们的网站文件,并通过 http 和 https 向全世界发布;默认端口。
反向代理负责传递请求。例如,我们捕捉beersnob.com/api
并将每个请求传递给蓝色块中的 API 服务,其他请求则传递给 web 服务器。
蓝块:我们的 API 负责我们的 web 服务器和数据库之间的通信。注意,我们的 web 服务器可以在内部执行请求;它不一定要调用beersnob.com/api/users
比如用apicontainer/users
直接调用 API 容器就可以了。这提供了灵活性和安全性。
红色块:我们的数据库,保存着我们所有的数据。注意两件事:我们的 web 服务器和我们的数据库之间没有连接;这都是通过我们的 API 来处理的。第二件事是我们可以从网络外部访问我们的数据库。通过这种方式,我们可以连接到我们的数据库管理系统(例如 PgAdmin)中的数据库,并处理我们数据库中的数据,导出数据,导入数据或制作存储过程等。
查看本文 中关于如何容器化 Postgres 数据库的实用示例。
这个人非常想写一篇啤酒评论(图片由 Cottonbro 在 Pexels 上提供)
3.创建 docker-compose.yml
我们将在 docker-compose.yml 中定义我们所有的服务和连接。
在这区区 50 多行代码中,Docker 编排了我们整个应用程序的所有容器。不过,我可以想象,如果你不熟悉作曲,可能会有点困惑,所以让我们从头到尾看一遍。您会看到定义了三个服务;一个数据库,API 和 web 服务器。让我们一个一个地过一遍。
3.1 数据库
查看我们的数据库容器的配置。
服务 1:我们应用程序的数据库
container_name :我们给容器起的名字。否则将生成一个随机名称
主机名:我们的容器可以通过这个主机名在内部网络上访问。请将此视为一种域名(http://beersnob_database)
图片:将要安装到容器中的软件。在这种情况下,它是默认的 Postgres 图像。
卷:卷是保存数据的一种方式。当我们运行这个映像时,Postgres 被构建在容器中。然后我们可以在里面放一些数据。如果我们移除容器,我们的数据也会消失。卷允许我们将容器中的数据复制到主机。卷也可以由多个容器共享。我们还可以将源代码放在卷中,这样当我们在主机上编辑代码时,更改就会反映在容器中。
环境:这些是容器的设置。这里我们提供了两个数据库名,一个用户名和一个密码,这样 Postgres 可以设置我们的第一个数据库和第一个用户
端口:默认情况下,我们无法访问容器中的数据库。通过这个端口映射,我们可以接入数据库。如果我们转到 localhost:54321,那么主机(Docker 运行的地方)将容器内的网络连接到端口 5432;我们的数据库就在那里运行。
重启:如果我们的容器崩溃了会怎么样?我们选择了“除非-停止”,但我们也可以“总是”重启、不重启(“否”)和“失败时”重启。
3.2 API
我们现在将检查 API 的所有配置,跳过我们已经在数据库中介绍过的部分。
构建:在数据库中,我们可以只传递一个图像,但是当涉及到我们的 API 时,我们必须做一些额外的工作。我们用包含 dockerfile 的上下文来引用名为“beersnob_api”的文件夹。在这个 docker 文件中,我们提取一个节点映像,安装我们的依赖项,并将我们的源代码复制到容器中。
卷:你可以看到我们将源代码(在./beersnob_api/src
中)镜像到容器中。如果我们在宿主中修改了源代码,那么一旦我们重新运行它,这个修改就会反映在容器中。
环境:我们在 node 中的源代码需要用一个环境变量来调用(或者是“开发”或者是“生产”)。这是通过 env 文件完成的。在本文的后面部分会有更多的介绍。
依赖于:一旦数据库启动并运行,就启动这个 API 容器。
3.3 网络服务器
网络服务器将保存我们网站的源代码(通过卷)。还要注意,我们在这个容器中映射了 2 个端口:80 和 443 (http 和 https)。
3.4 内部集装箱网络
请注意,我们的容器化应用程序现在有了自己的内部网络,我们的服务可以用它来相互通信。想象一下,我们把应用程序放在 beersnob.com。我们的网站不必调用http://beersnob.com/api/users
来从数据库中请求用户信息,相反,它可以只使用 API 的内部主机名(http://beersnob_api/api/users
)。
我们的容器就像互相堆叠的积木(图片由苏西·黑兹尔伍德在 Pexels 上拍摄)
4.环境文件
还记得本文前面的环境文件吗?查看 docker-compose.yml 中的第 33 行;上面写着NODE_ENV=${BEERSNOB_ENVIRONMENT}
。这意味着我们必须为 BEERSNOB_ENVIRONMENT 变量提供一个 env 文件。
5.旋转我们的容器
让我们旋转我们的容器!不用担心复制合成文件或没有所有数据;查看 这个链接 ,在这里您可以克隆存储库并在您的机器上旋转容器。
导航到包含 docker-compose.yml 的文件夹并调用docker-compose --env-file ./config/.env_dev up
。就是这样!这个命令提供了环境变量,复制了我们的源代码,安装了我们的依赖项,创建了所有的映像,最后启动了我们的网络和容器。让我们来测试一下!
- 去
[localhost](http://localhost:54322/api/test):54322/api/test
测试 API。 - 转到本地主机或
localhost:80
来测试 web 服务器 - 使用数据库管理系统(如 PgAdmin)通过 docker-compose.yml(第 12–14 行)中的凭证连接到我们在
localhost:54321
上的数据库
我们的容器像时钟中的齿轮一样一起工作(图片由 Felix Mittermeier 在 Pexels 上拍摄)
结论
正如我们所见,Compose 增加了许多自动化、灵活性和标准化。就我个人而言,我认为以这样一种自动化的方式来处理基础设施是令人惊奇的。我们保持我们的服务解耦,我们的源代码隔离,我们的基础设施版本受控。希望这篇文章对您的应用程序容器化有所帮助。
Docker 提供了更多的功能。请继续关注下一部分,我们将在其中介绍在容器化应用程序中实现 CI/CD、实现 Docker Swarm 以及 Docker 提供的更多特性。关注我保持关注!
我希望这篇文章是清楚的,但如果你有建议/澄清,请评论,以便我可以做出改进。同时,请查看我的 关于各种编程相关主题的其他文章 ,例如:
- Docker 适合绝对初学者
- 把你的代码变成一个真正的程序:使用 Docker 打包、运行和分发脚本
- Python 为什么慢,如何加速
- Python 中的高级多任务处理:应用线程池和进程池并进行基准测试
- 编写自己的 C 扩展来加速 Python x100
- 【Cython 入门:如何在 Python 中执行>每秒 17 亿次计算
- 用 FastAPI 用 5 行代码创建一个快速自动归档、可维护且易于使用的 Python API
编码快乐!—迈克
又及:喜欢我正在做的事吗?跟我来!
https://mikehuls.medium.com/membership
面向绝对初学者的 Docker——什么是 Docker 以及如何使用它(+示例)
像管理应用程序一样管理您的基础架构
Docker 是一个神奇的工具,它为我们提供了代码的标准化、生产率、效率、可维护性和兼容性,使我们的生活变得更加容易。它允许我们持续快速地部署和测试我们的代码,并且它是独立于*台的。
如果你不确定 Docker 是什么,用它做什么或者怎么用;这是给你的文章!对于 Docker 的新手,我会尽量给出一个尽可能清晰的解释。在本文结束时,您将:
- 使用 Docker 有很多好处
- 了解 Docker 是什么以及它是如何工作的
- 了解为什么使用 Docker:它能为您提供什么,以及它如何让您的生活更轻松
- 了解何时使用 Docker
- 能够使用 Docker 拉一个图像并旋转你的第一个容器
如果你觉得这篇文章的任何部分需要更多的解释,如果你有问题,或者如果你认为这篇文章可以在任何其他方面得到改善:请评论,让我知道!
我们先从 Docker 带给你的优势说起;为什么你应该首先使用它。我把它分成了几个部分,其中一些可能会有一点重叠。这是因为我试图尽可能简单地解释,以便让尽可能多的人可以使用 Docker。
在第一部分之后,你会完全相信你的生活中需要 Docker 现在我们在泥地里觅食。对于一个简单的例子——应用程序,我们将执行所有的基本步骤来启动并运行 Docker 容器。我们走吧!
1.为什么要用 Docker?
下面列出了使用 Docker 的主要原因。我会尽可能清楚地解释每一点。
虚拟化
数据中心到处都是服务器。这些都是功能强大的计算机,你可以通过互联网访问,可以用于各种事情。数据中心为客户提供租用部分服务器的选项;这叫做虚拟机;不是整个计算机,而是它的一部分,使像一个完整的机器一样运行。这被称为虚拟化,因为你的主机(主机)就像是 3 个独立的机器(访客)一样,如下图所示。**
使用虚拟机托管我们的应用
您将看到服务器托管了 3 台虚拟机;一个用于我们的 API,一个用于 web 服务器,一个用于数据库。此外,它还拥有一些基础架构和一些服务来控制所有虚拟机。这里最重要的一点是,每个虚拟机都有自己的客户操作系统。这完全是多余的,占用了大量内存。
当你使用 Docker 时,你不需要虚拟机。您将应用程序打包在一个运行在机器上的容器中。这可以是服务器,也可以是您自己的笔记本电脑:
将我们的应用归档
请注意,我们节省了大量内存;我们的应用程序共享一个操作系统(至少是内核),这使得它更加轻量级。查看下面的文章,这是一个关于如何将 Postgres 数据库容器化的很好的实用示例。
**
轻便
docker 文件不仅允许我们发布应用程序代码,还允许我们发布环境。我们不仅可以将应用程序的源代码推送到 git,还可以包含 docker 文件。当其他人提取我们的存储库时,他们可以在 Docker 容器中构建源代码,我们可以从 Docker 文件中创建。
版本控制和 CI/CD
就像在可移植性中描述的那样,我们可以跟踪 Docker 文件中的变化。通过这种方式,我们可以试验新版本的软件。例如,我们可以创建一个分支,在这个分支中我们可以试验 Python 的最新版本。
隔离
当您的代码在容器中运行时,它不会影响其他代码。它是完全孤立的。如果在更新全局安装的库之后,您的某个脚本出现了意外错误,您可能会意识到这个问题。
撰写容器
应用程序很少由一部分组成:大多数情况下,多个容器必须协同工作来创建所有的功能。一个例子:一个网站,API 和数据库必须连接在一起。这就是 Docker Compose 允许我们做的事情。我们可以创建一个定义容器如何相互连接的文件。我们可以使用这个文件一次性实例化所有容器的所有 docker 文件!**
2.Docker 如何工作:创建我们的第一个容器
让我们动手做点什么,然后编码吧!你可以把 Docker 看作是一种把你的代码打包到一个漂亮的小容器中的方法,这个容器包含了运行它所需要的一切;它将你的代码容器化。好处是多方面的:容器是可伸缩的,有成本效益的,并且是相互隔离的。本部分重点介绍 docker 元素:
- Dockerfile: 应该如何构建映像的规范
- ****图片:像一张 CD:它包含了所有的代码,但还不能做任何事情。
- ****容器:一个运行的图像。把它想象成你刚刚放入 CD 播放器的 CD。它正在执行图像。
**
所有这些都将在下面解释并给出例子。
是时候开始打包这些容器了(图片由 Kaique Rocha 在像素上拍摄)
2.1 文档文件
我们将创建一组指令,告诉我们的机器如何建立我们的形象。在我们的例子中,我们想在 Flask 中创建一个简单的网站;一个 Python web 框架。查看下面的文档:
让我们一行一行地过一遍。
- 一号线。这告诉 Docker 安装一个安装了 Python 3.8 的 OS (Debian Slim Buster)
- 第三行。在 docker 容器中创建一个名为“app”的文件夹。我们所有的代码都存放在这里
- 第五行。将我们机器上的 requirements.txt 文件复制到 docker 容器上的工作目录中
- 第六行。这将下载并安装我们的应用程序所需的所有 Python 依赖项。在我们的情况下,这将安装烧瓶
- 第八行。将当前目录中的所有内容复制到工作目录中。这将移动我们所有的源代码
- 第 10 行:这通过调用安装的 Flask 模块并在本地主机上运行我们的应用程序来启动我们的应用程序。**
2.2 码头工人形象
我们的 Dockerfile 被定义了,让我们用它来创建一个图像。奔跑
**docker build --tag python-docker**
该命令将获取 docker 文件并将其构建成一个映像。我们还会给它一个名为 python-docker 的标签。当映像建立后,我们可以执行docker images
来找到它。
我们刚刚做了一个图像。把这个想象成一个游戏的光盘;它包含所有的资产,图形和代码,使其工作。我们现在可以在容器中旋转图像。
2.3 码头集装箱
容器是我们图像的一个运行实例。如果图像像一个光盘,现在我们把图像放入我们的电脑,运行我们的游戏。在我们的类比中,跑步游戏是我们的容器。同样,我们可以使用以下命令运行我们的映像:
**docker run --publish 5000:5000 python-docker**
在这个命令中,我们告诉 docker 运行一个名为 python-docker 的映像。这就是我们在上一部分中标记图像的内容。我们还指定了--publish 5000:5000
。这详细说明了我们希望如何在我们的笔记本电脑(主机)和 docker 容器之间连接端口。因为 Flask 默认运行在端口 5000 上,所以这个标志的第二部分需要是 5000。我们已经选择在我们的主机上的端口 5000 上访问容器。要看到这在行动中导航到localhost:5000
,看看我们的 Flask 网站工作!
试着运行docker run --publish 4331:5000 python-docker
,你会发现你必须导航到localhost:4331
。
结论
学会了如何提取图像和旋转容器,为自动化、部署和测试你的软件打开了许多大门。尽管如此,当谈到 Docker 提供的所有好处时,我们只是触及了皮毛。
在 的下一部分 中,我们将深入了解 Docker Compose 如何编排多个容器协同工作,并通过一个配置文件自动组装容器。稍后,我们将研究如何将 Docker 整合到 CI/CD 流程中,以及如何在 Docker 群中管理 Docker 引擎集群。感兴趣吗?关注我保持关注。
我希望这篇文章是清楚的,但如果你有建议/澄清,请评论,以便我可以做出改进。与此同时,请查看我的 其他关于各种编程相关主题的文章 ,例如:
- Docker 为绝对初学者编写
- 把你的代码变成一个真正的程序:使用 Docker 打包、运行和分发脚本
- Python 为什么慢,如何加速
- Python 中的高级多任务处理:应用线程池和进程池并进行基准测试
- 编写自己的 C 扩展来加速 Python x100
- cyt hon 入门:如何用 Python 执行>每秒 17 亿次计算
- 用 FastAPI 用 5 行代码创建一个快速自动归档、可维护且易于使用的 Python API
编码快乐!—迈克
又及:喜欢我正在做的事吗?跟我来!
**https://mikehuls.medium.com/membership **
零碎的码头工人
了解 docker 的基本概念、工作原理,以及如何使用 docker 构建机器学习 Rest APIs。
目录
3 用 Docker 构建机器学习 API
什么是 Docker,为什么它有用
Docker 是一个工具,用于将软件应用程序代码及其依赖项组合成一个单独的包,可以在任何计算机环境中独立运行。Docker 是一个非常有用的 DevOps 工具,用于构建和打包软件应用程序,可以在软件工程中跨多个*台使用。开发的软件应用程序可能依赖于依赖性,并且由于诸如操作系统或不良环境设置的编码环境的差异,软件的依赖性可能无法安装。如果我们能够以这样一种方式隔离软件,使其独立于计算机的环境,那么依赖失败来使用软件的挫折感将会大大减少。这就是 docker 作为一个便捷工具的用武之地。docker 的目标是将软件代码及其依赖项组织成一个单独的包,可以在独立的环境中安装和使用。
我上面解释的一个真实的例子是这样的
python 中内置了一个机器学习软件应用程序,用于对图像和视频中的对象进行分类。工程师的目标是让每个人都可以使用这个软件。实际上,使用该软件需要安装深度学习库,如 tensorflow 或 pytorch,其他依赖项,如 opencv、numpy 和其他软件包。工程师可以使用 docker 轻松地将该软件的代码和依赖项打包成一个包。任何人都可以下载机器学习软件应用程序作为 dockerized 应用程序,并使用它,而不用担心安装其依赖项。
在本文中,我将详细解释 docker 的基本概念以及如何构建一个 docker 化的应用程序。
码头设备
Linux 操作系统
按照这篇教程学习如何在 Linux 上安装 docker。
https://docs.docker.com/engine/install/ubuntu/
Windows 操作系统
请按照本教程学习如何在 Windows 上安装 docker
https://docs.docker.com/desktop/windows/install/
mac 操作系统
请按照本教程学习如何在 Mac OS 上安装 docker。
https://docs.docker.com/desktop/mac/install/
Docker 概念
首先,我们需要理解 docker 的一些基本概念,这些概念将使我们对用它构建软件的过程有一种直觉。一些最重要的概念包括:
- docker 主机:这是我们安装 Docker 并运行 Docker 引擎的物理或虚拟机。它可以是您的笔记本电脑,也可以是云计算*台(如 Azure、AWS)上提供的虚拟机。
- docker 守护进程:这是 Docker 引擎的一部分,控制 Docker 化应用的构建和运行。
- docker 客户端:这是一个接口,用作与 Docker 引擎中的 Docker 守护进程通信的媒介,例如命令行接口。我们可以使用命令行界面输入命令来开始或结束 docker 守护进程中的操作。
- Docker 镜像:这是在构建 Docker 应用程序期间由软件代码组件产生的一组只读层。它是通过打包软件代码及其依赖项而产生的包。它充当生成 docker 容器的模板。
- Docker 容器:它是在运行映像时创建的。它是映像的运行状态。Docker 容器执行打包在 docker 映像中的代码。
- Docker 容器化:在 Docker 容器中运行软应用代码的状态。
- 容器注册处:这是一个集中存放 docker 图片的地方。Docker hub 是 Docker 图片最受欢迎的注册表。像 Azure、AWS 和 Google cloud 这样的云计算*台都有自己的容器注册中心。Docker 图片从 dockerhub 上传和下载。
Docker 映像构建
在这一部分中,我们将讨论将软件代码及其依赖项打包成 docker 应用程序或 docker 映像的过程。我将给出一个建立 docker 形象的简单指南。在每一个 docker 镜像构建过程中,都有一个非常重要的文件叫做 Dockerfile 控制着 docker 镜像构建的所有步骤。
码头形象建设规范
Python 代码
这是我们想要打包到 docker 映像中的 python 代码。
requirements.txt 文件
这是包含我们想要构建的映像所需的依赖项的文件,对于我们简单的 docker 映像,唯一需要安装的是 flask。
flask
Dockerfile
这个文件包含一组指令或命令,告诉 docker 守护进程如何构建 docker 映像。它有许多决定图像构建的命令。
Dockerfile 命令
中的:这是一个从容器注册表中提取或下载 docker 镜像的命令。该映像将作为构建自定义 docker 映像的基础映像。在上面这个例子中,使用的基础图像是python:3.8-slim-buster。
ENV: 这是用于定义 docker 容器中使用的环境变量的命令。
WORKDIR: 该命令指定 docker 映像将被构建的目录,如果该目录不存在,它将被自动创建。
运行:该命令用于执行其他命令,如安装 docker 镜像的依赖项。
COPY: 该命令将构建 docker 映像所需的所有代码文件复制到工作目录中。
EXPOSE: 这是一个命令,它将为从 docker 映像创建的容器分配一个端口。
CMD: 这是执行从映像创建的容器的运行过程的命令。在这个示例中,我们只想运行 python 文件 app.py. 中的代码
图像构建
这是构建映像的 docker 命令。
docker build -t sampleimage .
- 这代表了给我们想要构建的图像的标签。默认标签是 latest,可以给它一个更合理的标签,如 v1,来表示映像构建版本。build 命令末尾的点表示映像的构建上下文。在这种情况下,映像是在当前工作目录中构建的。
Docker 映像构建过程
开始构建映像时,您会在 CLI 界面中看到如下日志:
Sending build context to Docker daemon 2.048kBStep 1/8 : FROM python:3.8-slim-buster3.8-slim-buster: Pulling from library/pythonb380bbd43752: Pull complete81c44d634db0: Pull complete9667f949f66d: Pull complete3e9f5c1d871e: Pull completeb181e9f84c74: Pull completeDigest: sha256:9e3036f6b032794efb662f3c579c4c35d0b678bc793590e3e2e217cb5bf1e11bStatus: Downloaded newer image for python:3.8-slim-buster---> 52c287c5a9a3
Step 2/8 : ENV language Python---> 3daefef935e9Step 3/8 : ENV type Object Oriented---> Running in 374baff80d4aRemoving intermediate container 374baff80d4a---> 5d093fcdd6baStep 4/8 : WORKDIR /app---> Running in 5925b551d682Removing intermediate container 5925b551d682---> 94fa4e3b33ddStep 5/8 : EXPOSE 5000---> Running in 3c3d1bdf6cbcRemoving intermediate container 3c3d1bdf6cbc---> 515acc697171Step 6/8 : COPY . /app---> 67f96e5cdf0bStep 7/8 : RUN pip3 install -r requirements.txt---> Running in eeee3ef25fc7Collecting flaskStep 8/8 : CMD ["python3", "app.py"]---> Running in e7df1dd83cceRemoving intermediate container e7df1dd83cce---> 7a9115cda945Successfully built 7a9115cda945Successfully tagged imagesample:latestdocker build -t imagesample .Sending build context to Docker daemon 2.048kB
向 Docker 守护进程发送构建上下文:当命令Docker Build-t image sample。执行 ,显示的第一个输出是发送构建上下文到 Docker 守护进程, 处理 Docker 映像的构建。命令行界面充当 Docker 客户端,它将输入的命令发送给 Docker 守护进程以构建映像。
Step 1/8 : FROM python:3.8-slim-buster3.8-slim-buster: Pulling from library/pythonb380bbd43752: Pull complete81c44d634db0: Pull complete9667f949f66d: Pull complete3e9f5c1d871e: Pull completeb181e9f84c74: Pull completeDigest: sha256:9e3036f6b032794efb662f3c579c4c35d0b678bc793590e3e2e217cb5bf1e11bStatus: Downloaded newer image for python:3.8-slim-buster---> 52c287c5a9a3
第 1/8 步:是命令的执行拉动基础镜像也就是python:3.8-slim-buster。
注意: Docker Pull 是从容器注册表下载图像的过程。
Step 2/8 : ENV language Python---> 3daefef935e9
Step 3/8 : ENV type Object Oriented---> Running in 374baff80d4a
步骤 2/8 和 3/8: 这些步骤为要在容器中使用的 docker 图像设置环境变量。
Step 4/8 : WORKDIR /app---> Running in 5925b551d682Removing intermediate container 5925b551d682---> 94fa4e3b33ddStep 5/8 : EXPOSE 5000
步骤 4/8 和 5/8:步骤 4 为 docker 镜像设置工作目录,而步骤 5 将运行 docker 镜像的端口 5000 公开为容器。
Step 6/8 : COPY . /app---> 67f96e5cdf0bStep 7/8 : RUN pip3 install -r requirements.txt---> Running in eeee3ef25fc7Collecting flaskStep 8/8 : CMD ["python3", "app.py"]
步骤 6/8,7/8,8/8:步骤 6 从工作目录中复制所有需要的文件。 step8 安装需求文件中提供的包。第 8 步构建运行 docker 镜像的容器层。
成功建立形象
Successfully built 7a9115cda945Successfully tagged imagesample:latest
这是最后一个日志输出,显示映像已成功构建,并且映像标记为 imagesample:latest 。
注:这个 docker 镜像构建过程有八个步骤,对应 dockerfile 中定义的八条指令。如果在 dockerfile 中定义了 20 个阶段,那么将有 20 个步骤来构建映像。
Docker 镜像运行
我们已经构建了映像,下一步是运行它。运行映像时,会自动从映像创建一个容器。容器将执行我们用来构建图像的代码。使用以下命令运行 docker 映像:
docker run -p 80:5000 imagesample
-p 80:5000: 这是用于运行通过运行映像创建的容器的端口分配。我们在【docker file】中暴露了端口 5000 ,并将端口 80 映射到端口 5000 。
运行 docker 的输出将是:
Serving Flask app ‘app’ (lazy loading)
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: off
* Running on all addresses.
WARNING: This is a development server. Do not use it in a production deployment.
* Running on [http://172.17.0.2:5000/](http://172.17.0.2:5000/) (Press CTRL+C to quit)
在您的浏览器中访问 localhost:80,这将是加载的页面!
作者图片
容器加载了一个简单的 web 页面,其中包含我们在图像构建中使用的 python 代码中的打印消息。
@app.route("/")
def home():
return f"{os.getenv('language')} is an {os.getenv('type')} programming language!!!!"
注意:这个简单的 docker 映像用于解释 docker 映像构建背后的基本概念。在本文的后半部分,我们将讨论 docker 在构建 Rest API 中更实际的应用。
Docker 图像上传到容器注册表
我们已经成功地建立了一个 docker 映像,但我们需要一个 docker hub 帐户来让每个人都可以下载和使用它。
注册 DockerHub
在 Dockerhub 上拥有帐户后,您可以使用您的帐户上传 docker 图片。
Docker Push:It是将 Docker 映像上传到容器注册中心的过程。它遵循简单的程序:
通过 CLI 界面登录您的 docker hub,
docker login
用你的 dockerhub 用户名标记你的图片,
docker tag yourimage yourdockerhubusername/yourimage
推送 docker 图片。
docker push yourdockerhubusername/yourimage
样本 docker 图像推送。
docker tag imagesample ayoolaolafenwa/imagesample
docker push ayoolaolafenwa/imagesample
作者图片
用 Docker 构建机器学习 Rest API
docker 最重要的贡献之一就是用它来构建 Rest APIs。Rest API 只是一个使用 HTTP 请求来访问和使用数据的 API。Rest API 是网站的支柱之一,它使我们能够轻松地在线使用和访问数据。你有没有想过在网站中如何使用人工智能模型,购物网站如何根据你以前的购买情况,在后台使用人工智能模型来进行产品推荐?我们能够通过 Rest APIs 在后台使用 AI 模型,这使得模型有可能接受用户的数据来做出正确的预测。在本教程的这一部分,我将展示 docker 在构建机器学习 REST API 中的实际使用。为此,我实现了一个图像分割 API,使用我的 python 库 PixelLib 进行图像分割。
API 目标:我使用 docker 将用于图像分割的代码及其依赖关系封装成一个单独的包来创建 Rest API。
为分段 API 构建 Docker 映像的代码
图像分割 API 的 docker file
我们定义了一个更复杂的 dockerfile ,它带有一种为了建立我们的映像而要安装的软件包的风格。此 docker 文件中安装的所有包都是 PixelLib 图像分割 API 所需的依赖项。这些包包含了很多用于 PixelLib 的LinuxOpenCV 和 pytorch deeplearning 库的包。
requirements.txt
pixellibflaskpycocotools
requirements.txt 文件包含了我们分割 API 的附加依赖项,特别是用于执行图像分割的 PixelLib、库。
分段 API 代码
Web API 的 Html 代码
注意:由于构建映像时要安装大量的依赖项,构建此映像分段 API 将会耗费大量时间。我已经为图像分割 API 构建了这个 docker 图像,它可以在 Dockerhub 上获得,您可以使用以下命令轻松地从那里提取图像:
docker pull ayoolaolafenwa/pixellibapi
如果您想要构建镜像,请克隆代码库:
git clone ayoolaolafenwa/pixellibapi
下载point rende 泡菜分割模型,放在ImageSegmentationAPI目录下。
cd ImageSegmentationAPI
docker build -t yourimagename
运行分段 API
docker run -p 80:5000 ayoolaolafenwa/pixellibapi
注意:我在本文中运行的图像示例使用了我推送到 dockerhub 的示例 docker 图像。如果您构建了自己的图像,请用您的图像名称替换它。
当我们运行图像时,我们可以使用 API 来检测任何图像中的对象。
用于测试我们的 API 的示例图像
运行 API 的代码
results = requests.post("http://localhost:80/segmentapi", files = {"image": open("sample.jpg", "rb")}).json()
print(results["outputs"])
我们使用 python requests 函数来接受我们正在运行的图像分割 API 的 url,即http://localhost:80/segment API。我们传递图像的路径,最后打印出分割 API 的输出。
该示例代码的输出是:
{'boxes': [[372, 158, 528, 504], [530, 163, 605, 374], [1, 219, 81, 299], [374, 309, 542, 542], [227, 204, 420, 332], [477, 151, 596, 239], [589, 195, 703, 257], [742, 213, 774, 259], [376, 181, 429, 218], [172, 167, 264, 206], [279, 190, 294, 200], [299, 185, 334, 205]], 'class_ids': [0, 0, 2, 1, 2, 5, 2, 2, 2, 5, 2, 2], 'class_names': ['person', 'person', 'car', 'bicycle', 'car', 'bus', 'car', 'car', 'car', 'bus', 'car', 'car'], 'mask_shape': [581, 774, 12], 'object_counts': {'bicycle': 1, 'bus': 2, 'car': 7, 'person': 2}, 'scores': [99, 99, 99, 99, 99, 98, 98, 97, 86, 81, 57, 54]}
输出包括关于图像中检测到的对象的许多细节,包括框坐标值、类 id、类名、对象计数、分割掩模形状和预测分数。我们已经成功地运行了一个图像分割 API。任何人都可以提取 docker 图像,将其作为一个容器运行,并执行图像分割,而不必担心有什么要求或依赖关系。这是一个机器学习后端 API 的基本例子。
图像分割前端 API: I 提供了一个简单的 web API,可以直接对图像进行测试,并将结果可视化。访问正在运行 API 的端口,例如 localhost:80 ,执行简单的 web 图像分割。
作者图片
您可以选择任意一幅图像并按下按钮,它将在几秒钟内显示一幅输出的分割图像。
作者图片
Docker 命令
列出 Docker 图片
这是 docker 命令,用于列出计算机中可用的 docker 图像。
*docker image ls*
这个 docker 命令将在您的 CLI 中列出关于 docker 映像的所有信息,包括其标签映像 id、大小 和 创建日期 。下面是一个日志示例:**
作者图片
检查码头集装箱状态
这是 docker 命令,用于检查计算机中正在运行的 docker 容器的状态。
**docker ps**
它将打印出 容器 id , 图像 , 命令 用于运行容器, 日期 容器被创建, 容器状态 ,容器名称和 运行端口 下面是一个示例日志;
作者图片
你可以在运行 docker ps 命令时看到特定图像的 容器名 。 这使得可以与正在运行的容器进行交互,上面的示例日志显示了默认的容器名 sad_goldwasser。
注意:当你运行一个图像时,docker 会自动给从它创建的容器一个默认名称。您可以给它起自己的名字,使用这个修改过的命令来运行 docker 映像。
*docker run -p 80:5000 --name testimage ayoolaolafenwa/pixellibapi*
在上面的命令中,我们将正在运行的 docker 容器命名为 testimage 。
新日志将是:
作者图片
注意:容器名是 testimage, 是我们运行 docker 镜像时给出的名字。
停止码头集装箱
使用此命令以 docker 容器的名称停止正在运行的 docker 容器。
*docker stop container name*
当 docker 容器名为 testimage 时,它将是:
*docker stop testimage*
或者如果没有给出名字,docker 停止使用 docker 给出的默认名字。
*docker stop defaultname*
移除 Docker 图像
使用此命令删除 docker 图像。
*docker image rm yourimagename*
访问 PixelLibAPI 存储库:
*https://github.com/ayoolaolafenwa/PixelLibAPI *
将你的 Dash 应用归档
让 Dash 为简单、现代的部署做好准备
照片由塞巴斯蒂安·佩纳·兰巴里在 Unsplash 上拍摄
本文将研究如何对我们已经构建的 Dash 应用程序进行 Dockerize。理解应用程序的结构很重要,所以请在这里查看存储库。如果你想了解更多的细节和解释,可以看看我们在这里详细介绍这个结构的文章:
现在你已经根据我们的第一篇文章构建了你的 Dash 应用程序,你已经为你的应用程序做好了准备。
Docker 是什么?你为什么要用它?
Docker 是一个提供容器的*台。像虚拟机(VM)一样,Docker 容器是一个打包的计算环境,它将各种软件组件与系统的其余部分隔离开来。两者的主要区别在于规模和可移植性。容器比虚拟机更轻量级,容器映像以兆字节而不是吉字节来度量。
Docker 容器解决了软件几十年来的一个痛点。跨不同*台管理软件非常耗时,因为每个*台都需要应用程序依赖、解释器、二进制文件等。Docker 提供了跨*台的一致性,简化了软件维护和部署。
难怪像 Google、Azure、AWS 和其他*台使用 Docker 来简化所有用户及其不同操作系统的应用部署。在这里,您将了解如何利用一项革命性的部署技术!
安装 Docker
要对你的应用进行 Docker 化,你首先需要安装 Docker。安装 Docker 对 Mac 用户来说很简单,但对 Windows 用户来说稍微复杂一点。如果不安装 Windows System for Linux (WSL2)并从该服务中运行 Docker,Windows 计算机将无法运行 Docker。我们在这里为那些希望使用这个选项的人链接了一个指南。
一旦安装完毕,你需要在你的机器上初始化 Docker。除非服务在后台运行,否则 Docker 命令将不起作用。
编写 Dockerfile 文件
Dockerfile 是一组命令,告诉 Docker 如何构建你的容器。它包括关于 Docker 容器应该扩展什么样的基本映像的信息,需要的任何额外的安装或配置步骤,以及关于运行应用程序的说明。
我们在下面提供了我们的 docker 文件,因此您可以使用正确的规范轻松创建您的 docker 文件:
FROM python:3.9-slim
COPY requirements.txt ./requirements.txt
RUN pip install -r requirements.txt
COPY . ./
CMD gunicorn -b 0.0.0.0:80 app.app:server
关于这个 Dockerfile,有一点可能看起来令人困惑,那就是它分两个阶段复制文件。我们这样做是因为 Docker 分层构建容器,粗略地说,每一行对应一层。构建第一次运行时,它会缓存每个层。如果一个层不需要更改,我们可以使用 Docker 的缓存层来加快构建速度。
让我们仔细看看它的行为。在第一个COPY
命令中,我们只将requirements.txt
文件复制到容器中。
现在为RUN
线。在一个构建中,如果requirements.txt
没有被改变,那么下一层,即安装,可以被跳过。如果缓存了,Docker 就不会重新运行这个层,所以不需要重新安装 Python 依赖项。
在第二个COPY
行中,我们将应用程序文件的剩余部分复制到容器中。自上次构建以来,一些应用程序代码可能已经更改,这意味着用于之后的层的缓存已经没有数据了。尽管如此,由于先前的层已经被构建或从缓存中获取,这一行不会触发其他依赖项重新安装。
如果我们一次复制所有的代码和依赖项,那么更改代码会使缓存无效,即使需求没有改变,依赖项也会被重新安装。
要深入了解优化容器,请查看 Sciforce 的这篇文章。
https://medium.com/sciforce/strategies-of-docker-images-optimization-2ca9cc5719b6
构建 Docker 容器
要启动 Docker 容器构建,请运行:
docker build -t tiny_home .
t 标志“标记”我们的图像,并允许我们命名容器。标签是有帮助的,因为它允许我们用一个更容易理解的名字而不是机器语言来引用我们的容器。在这里,我们将容器命名为 tiny_home 。
这个命令使用我们的 Dockerfile 来构建一个新的容器映像。当 Docker 处理我们的 Docker 文件时,它从包含 Docker Python 映像的基本 Python 容器开始。
Docker 图像
Docker 基础映像有多种类型。我们使用的是 Python 镜像的精简版本,当您希望安装运行 Python 所需的最小软件包时,这是最佳选择。后缀-slim
表示容器应该只装必需品。对于许多基本映像,有精简版和完整版。slim 比完整版包含的少,所以图像更小,是处理空间限制的理想选择。对于大多数语言,也有一个维护的完整版本的图像。如果您不确定在应用程序中使用哪种类型,完整图像通常是最佳选择。如果你想阅读更多关于这个话题的内容,看看朱莉·紫苏·加西亚的这篇文章吧!
图像通常包括许多层。层可能包括系统库、工具、依赖项和其他文件。在我们的例子中,Python slim 映像从从 DockerHub 访问现有的 Python slim 容器开始。映像下载到本地后,就安装了 Debian/Linux OS 发行版和 Python 的最新稳定版本。最后,Docker 编译所有应用程序文件并安装应用程序需求。
在我们的设置中,我们通过在预提交钩子中运行pipenv-to-requirements
,从我们的Pipfile
生成requirements.txt
和requirements-dev.txt
。由于我们只引用requirements.txt
Docker 永远看不到发展。仅使用在我们的容器中运行应用程序所必需的依赖项可以加快构建过程,减小容器的大小,并可能提高安全性。如果你还没有这样做,请参考我们的第一篇文章,运行这些钩子。这将使这个过程变得更加容易!
一旦构建了 Docker 容器,就可以通过运行以下命令来查看它:
docker image ls
然后您会看到 tiny_home 图像填充在这里:
由于我们已经成功构建了 docker 容器,该应用程序将通过端口 80 上的 gunicorn 运行。要访问我们的应用程序运行的地址,我们需要将它在 Docker 主机上运行的端口(端口 80)映射到本地主机上的一个端口。这里,我们将把我们的应用程序映射到端口 8080。要进行端口映射并同时运行我们的应用程序,请运行:
docker run -p 8080:80 tiny_home
当您访问 http://localhost:8080/ (或 0.0.0.0:8080,取决于您的操作系统)时,您应该会看到您的应用程序在 Docker 容器中运行!对于那些不知道的,localhost 是 127.0.0.1 的别名,而 0.0.0.0 引用所有本地 IP 地址。
如果您得到一个错误,指示“绑定 0.0.0.0:8080 失败:端口已被分配”,您可以按照这个堆栈溢出对话和提供的解决方案来终止任何正在使用端口 8080 的进程。
您的应用程序现在已被归档!
敬请关注我们下一篇关于将您的应用部署到 GCP 的云运行服务的文章!
我们希望你喜欢这篇文章!要获得更多关于数据科学、机器学习和开发的内容,请查看 Edward 的 YouTube 频道,并订阅我下面的邮件列表,成为第一个听到新文章的人!
https://edkruegerdata.com/subscribe [## 每当爱德华·克鲁格发表文章时,就收到一封电子邮件。
edkruegerdata.com](https://edkruegerdata.com/subscribe)
在谷歌云上对接和部署闪亮的仪表板
作者图片
将 Shiny 引入云计算的分步指南
闪亮的应用程序和仪表板是高度通用的工具,允许最终用户以各种方式与数据进行交互,而应用程序功能由后台运行的 R 代码支持。然而,为了让其他人访问您的工作并从中受益,您需要考虑将您的应用程序部署到哪里。在某些情况下,无论是免费还是付费计划,直接从 RStudio 内部在 shinyapps.io 上部署您的工作可能会很方便。然而,这种选择有一些限制。例如,一些应用程序可能需要 shinyapps.io 不支持的各种系统依赖关系。在这种情况下,您需要使用 Docker 将您的应用程序容器化,并在其他地方寻找主机。
Docker 是一个功能强大且广泛使用的工具,它允许开发人员将代码、设置和依赖项捆绑和隔离在一起,以便应用程序能够在任何计算环境中以相同的方式运行。在本指南中,我们将在 Google Cloud Run 上整理和部署一个闪亮的仪表板。为了遵循本教程中的步骤,你需要在你的机器上安装 R,RStudio 和 Docker 以及一个谷歌账户。
第 1 部分:如何将闪亮的应用程序或仪表板分类
因为我们将重点关注 dockerization 和 deployment,所以我们将简单地构建一个空白的闪亮仪表板作为临时仪表板。按照这种方法,我们将需要以下文件和文件夹结构。
/myshinydashboardfolder
├── Dockerfile
├── app
│ └── app.R
├── shiny-server.conf
├── shiny-server.sh
app。稀有
我们可以使用下面几行 R 代码创建一个无内容的应用程序。
library(shinydashboard)ui <- dashboardPage(
dashboardHeader(title=”Hello world”),
dashboardSidebar(),
dashboardBody()
)server <- function(input, output) { }shinyApp(ui, server)
如果您运行该应用程序,它应该看起来像下图。
作者图片
Dockerfile 文件
Dockerfile 指定在构建 Docker 映像时要安装哪些依赖项和 R 包。在这种情况下,我们将使用摇滚/闪亮的基础形象。
# get shiny server plus tidyverse packages image
FROM rocker/shiny-verse:latest# system libraries of general use
RUN apt-get update && apt-get install -y \
sudo \
pandoc \
pandoc-citeproc \
libcurl4-gnutls-dev \
libcairo2-dev \
libxt-dev \
libssl-dev \
libssh2-1-dev# install R packages required
# (change it depending on the packages you need)
RUN R -e "install.packages('shinydashboard', repos='[http://cran.rstudio.com/'](http://cran.rstudio.com/'))"# Copy configuration files into the Docker image
COPY shiny-server.conf /etc/shiny-server/shiny-server.conf
COPY /app /srv/shiny-server/
RUN rm /srv/shiny-server/index.html# Make the ShinyApp available at port 80
EXPOSE 80# Copy further configuration files into the Docker image
COPY shiny-server.sh /usr/bin/shiny-server.shRUN ["chmod", "+x", "/usr/bin/shiny-server.sh"]CMD ["/usr/bin/shiny-server.sh"]
与 docker 文件不同,我不需要对 shiny-server.conf 和 shiny-server.sh 文件进行任何修改。
闪亮服务器. conf
# Define the user we should use when spawning R Shiny processes
run_as shiny;# Define a top-level server which will listen on a port
server {
# Instruct this server to listen on port 80.
listen 80;# Define the location available at the base URL
location / {# Run this location in 'site_dir' mode, which hosts the entire directory
# tree at '/srv/shiny-server'
site_dir /srv/shiny-server;
# Define where we should put the log files for this location
log_dir /var/log/shiny-server;
# Should we list the contents of a (non-Shiny-App) directory when the user
# visits the corresponding URL?
directory_index on;
}
}
闪亮服务器. sh
#!/bin/sh# Make sure the directory for individual app logs exists
mkdir -p /var/log/shiny-server
chown shiny.shiny /var/log/shiny-serverexec shiny-server >> /var/log/shiny-server.log 2>&1
现在我们已经把文件整理好了,是时候构建 Docker 映像了!步骤如下:
- 在 RStudio 中打开一个 shell。这将自动从项目文件夹(“myshinydashboardfolder”)开始。
- 键入以下命令来构建映像。请注意,
mydashboard
将是使用-t
选项标记图像的应用程序的名称。
docker build -t mydashboard .
3.使用以下命令,在分离模式下使用端口 80,验证应用程序是否正常运行,或者什么也不做。
docker run -d -p 80:80 mydashboard
第 2 部分:如何在 Google Cloud 上设置项目和容器注册中心
假设我们已经设置了一个帐户,我们现在需要完成以下步骤来创建应用服务:
- 在谷歌云*台上创建一个新项目,并选择一个合适的名称,例如“shinyapps”。记下项目 ID。
- 确保您位于项目目录中,打开云 shell 并通过输入以下命令启用容器注册表。
gcloud services enable containerregistry.googleapis.com
然后会要求您授权此步骤。瞧啊!现在,您可以从 Docker Hub 中提取图像,并将图像推送到您的容器注册表中。在我们的例子中,我们将使用 gcloud CLI 工具,直接从本地机器推送映像。
第 3 部分:如何将 Docker 映像推送到容器注册中心
正如 Google Cloud 文档中所解释的,将 Docker 映像放入容器注册表有不同的方法。按照建议的方法,我们需要执行以下操作:
- 在您的本地机器上安装云 SDK ,并按照说明进行操作。
- 首先运行命令,使用您的用户凭据登录,然后按照步骤操作。
gcloud auth login
3.通过运行以下命令,将 Docker 配置为使用 gcloud CLI 进行身份验证。
gcloud auth configure-docker
4.用注册表名称标记您的图像。这里的gcr.io
是主机名称,您可能希望在名称前加上您的首选位置,例如eu.gcr.io
将在欧盟托管映像。
docker tag mydashboard gcr.io/YOUR-PROJECT-ID/mydashboard
5.现在,您可以使用以下命令最终推送映像:
docker push gcr.io/YOUR-PROJECT-ID/mydashboard
之后,您应该能够在容器注册表中检查您的映像。您甚至可以从这里直接启动部署。
第 4 部分:如何使用 Google Cloud Run 部署您的 dockerized 应用程序
快到了。现在,我们只需要导航到 Google Cloud Run,创建一个名为“myfirstdashboard”的新服务,并指定一个所需的区域。
作者图片
在下一页,我们只需单击“选择”来选择我们之前已推送到容器注册表中的图像。
作者图片
打开高级设置并选择容器端口 80 很重要,这是我们在 docker 文件中指定的。
作者图片
在最后一步,我们做出以下选择:
作者图片
过一会儿,你应该可以看到我们闪亮的新仪表板部署!该链接将显示在我们新创建的服务的仪表板中。
后续步骤
我们的应用程序显然是非常无用的,但我希望这个指南将帮助你根据你的使用情况来整理和部署更高级的闪亮应用程序和仪表板。尽管我们在这里没有涉及到这一点,但是您可能也希望为自动化构建和持续部署建立管道。
感谢 Ivana Hybenová帮助我开始使用 Dockering 闪亮的应用程序。
Doctor.ai,一个人工智能虚拟语音助手,用于医疗保健
用 AWS Lex 和 Neo4j 构建一个聊天机器人
由黄思兴、德里克·丁、埃米尔·帕斯特、伊尔万·布塔尔、闪亮的朱组成。由 Neo4j 的 Maruthi Prithivirajan、Joshua Yu 和 Daniel Ng 提供支持。
我认为未来医疗保健的顶峰将是建立虚拟医疗教练来促进健康人的自我驾驶。我承认有很多障碍,但我仍然相信有一天它会建成并得到充分的临床验证。
上面这个未来派的说法是埃里克·托普在他的书《深度医学》中写的。根据上下文,托普所说的虚拟医疗教练实际上是一个语音人工智能助手。这位助理管理大量数据并从中学习,包括个人医疗记录、健康状况和科学文献。一方面,它可以提出健康建议,解释医学概念,并为患者创建警报。另一方面,它可以帮助医生做出更好的决定。
新冠肺炎全球疫情清楚地表明,我们需要让更多的人获得医疗保健。在这方面,语音助手提供了一些优于智能手机或电脑应用的优势。首先,它是免提的。手术室里的医生不会敲电话或敲键盘。其次,全球相当大一部分人口既不会写也不会写代码。我们不要忘记,许多人都有视力障碍。第三,语音输入比打字快。对于汉语等语言,语音可以比打字快一倍。最后但并非最不重要的一点是,当患者独自一人且无行为能力时,智能语音助手可以触发紧急警报。这最后一点对单身老人尤其重要。
在我看来,由于自然语言理解和元学习仍处于初级阶段,一个真正具有人工一般智能的虚拟语音代理将是遥远的未来。但这并不意味着我们今天不能建立一个具有许多实用功能的语音代理。事实上,许多软件构建模块已经可用。我们只需要把这些碎片拼在一起。例如,通过 AWS Lex,我们可以快速构建一个理解自然语言的聊天机器人。
图一。健康医疗网的 Doctor.ai 概念。图片作者。
2021 年 12 月 3 日至 5 日,在 Neo4j 的支持下,我和四名 Neo4j 同事参加了 2021 年新加坡医疗保健 AI 数据大会和博览会。我们已经建立了一个名为 Doctor.ai 的虚拟语音助手。Doctor.ai 建立在 eICU 数据集的基础上。我们在 AWS 上运行 Neo4j 作为我们的后端数据库。Lex 充当我们的语音代理,它通过 Lambda 连接到 Neo4j。最后,我们基于 Lucas Bassetti 的简单聊天机器人组装了一个前端。
图二。Doctor.ai 图片作者截图。
Doctor.ai 可以在英语对话中为病人和医生服务。一方面,患者可以查询自己的病历,但不能查询别人的。另一方面,医生可以询问患者的病史,如过去的 ICU 就诊、诊断和接受的治疗。此外,Doctor.ai 还可以通过 Neo4j Graph 数据科学库为某些患者做初步的治疗建议。当与 AWS Kendra 结合使用时,Doctor.ai 甚至可以解释医学术语,并从医学文献中获取答案。
在本文中,我将带您完成 Doctor.ai 的设置,并解释它的一些功能,以便您也可以拥有自己的 Doctor.ai 克隆。不过,在这个演示中,我确实去掉了一些高级功能,如用户验证。代码存放在我的 Github 存储库中:
https://github.com/dgg32/doctorai_walkthrough
1.eICU 数据
Doctor.ai 建立在 eICU 数据集之上。根据官方文档,该数据集“由来自整个美国大陆的许多重症监护病房的组合数据填充。协作数据库中的数据涵盖了 2014 年和 2015 年入住重症监护室的患者”。它包含超过 139,000 名患者的 200,000 次 ICU 访问的实验室结果、人口统计信息、诊断、治疗和其他相关信息。我们使用完整的数据集作为我们的替代病历来开发 Doctor.ai。您还可以预览一下演示数据集。
如果你想使用完整的数据集,你应该首先申请对它的授权访问(按照这里的指示)。你需要完成 CITI 的“数据或标本只研究”课程,并获得完成报告。然后,在生理网上填写表格,他们会在几天内审批你的申请。最后,您将能够从 Google 云*台请求访问存储在 Big Query 中的数据。
对于这个项目,我们只需要从eicu_crd
和eicu_crd_derived
数据库下载六个表:
eicu_crd_derived diagnosis_categories icustay_detail pivoted_labeicu_crd diagnosis microlab treatment
图 3。如何从 eICU 下载用于 dr . ai 的表格。
下载需要通过谷歌云存储(GCS)。选择每张桌子,点击EXPORT
和Export to GCS
,使用gz
格式并选择其中一个桶作为目的地。然后,您可以将数据从您的 bucket 下载到您的本地机器。
2.架构、SAM 和手动配置
Doctor.ai 由 EC2 实例上的后端 Neo4j 数据库、自然语言理解引擎 Lex 和 Amplify 托管的前端 web 应用组成(图 4)。在 datathon 中,我们使用了 Neo4j Enterprise,因为它允许我们通过基于角色的访问控制(RBAC)特性来管理医生/患者的权限。我们也将 Kendra 作为我们的 FAQ 引擎。
图 4。艾医生的建筑形象作者。
2.1 SAM
在 datathon 之后,我已经将大部分基础设施编入了一个 AWS 无服务器应用程序模型(SAM)项目。从上面我的 Github 链接克隆项目。为您的 EC2 创建一个名为cloudformation.pem
的密钥对,chmod 400
并将其放入项目文件夹中。使用 SAM CLI,现在您只需要以下三个命令来操作基础架构:
选择一个区域,如us-east-1
,其中 Lex 可用。部署会很快。它将输出 Lex 的 ID、我们的 Neo4j 的 IP 地址和域名,以供后续步骤使用。
不幸的是,AWS SAM 到处都有一些 bug。例如,它不能设置 Lex 别名(此处读)。当在 SAM 中定义时,Amplify 不能自动构建,它也有环境变量的问题。当使用 Kendra FAQ 时,导入的 bot 会在 KendraSearchIntent 中出错。所以我们需要手动配置 Neo4j,Lex 和 Amplify,然后 Doctor.ai 才能上线。
2.2 EC2
首先,使用您的密钥对以用户“ubuntu”的身份登录您的 EC2:
ssh -i "cloudformation.pem" ubuntu@[your neo4j EC2 public domain name]
您需要将这六个表导入到 Neo4j 中。尽管可能存在其他选项,但我建议您首先将这六个文件转移到 EC2 中的/var/lib/neo4j/import
文件夹中。然后通过以下 URL 在您的浏览器中登录 Neo4j:
[http://[your Neo4j EC IP address]:7474/browser/](http://44.201.23.14:7474/browser/)
输入初始用户名“neo4j
”和密码“s00pers3cret
”,迎接你的将是熟悉的 Neo4j 浏览器界面。从我的存储库中运行neo4j_command.txt
中的命令来导入数据(如果需要,调整文件名)。
2.3 Lex
导入后,我们可以继续进行 Lex。首先,确保你在 Lex V2 控制台中(只要Return to the V1 console
在左侧面板中可见)。
图 5。Lex 的配置。图片作者。
点击LexForDoctorai
➡️ Aliases
➡️ TestBotAlias
➡️ English (US)
进入Lambda function
页面。选择LambdaForLex
和$LATEST
并点击Save
按钮。
最后,让我们构建LexForDoctorai
来测试 bot 是否功能正常。点击Intents
并点击Build
按钮。
图 6。如何构建 Lex?图片作者。
构建完成后,您可以使用测试控制台测试LexForDoctorai
。在这里,你可以看到医生。人工智能已经可以进行很好的对话。
图 7。在测试控制台中测试 Lex。图片作者。
2.4 前端
Lex 的测试控制台非常好,功能也非常强大。它既可以听用户说话,也可以向用户回话。然而,我们需要一个前端,以便我们可以将 Doctor.ai 部署为 web 或智能手机应用程序。我们将 Lucas Bassetti 的 React 前端放在一起,并在 Amplify 上托管。我试图在 SAM 中部署前端,但是遇到了错误。所以让我们手动部署放大器。
首先,将这个库分支到您的 Github 帐户,因为 Amplify 只能从您自己的帐户中检索代码。
https://github.com/dgg32/doctorai-ui
一旦完成,前往 AWS 放大页面,点击New app
➡️ Host web app
。然后选择Github
, Amplify 将获取你账户下的所有存储库。在您的帐户下选择doctorai-ui
。点击Next
进入Configure build settings
页面,点击打开Advanced settings
,添加五个密钥对环境变量:REACT_APP_AWS_ACCESS_KEY
、REACT_APP_AWS_SECRET
、REACT_APP_AWS_USERID
、REACT_APP_LEX_botId
和REACT_APP_AWS_REGION
。它们是您的 AWS 访问密钥、AWS 秘密访问密钥、您的 AWS 用户 id、BotID(您可以从sam deploy
输出中获得该值)和您的 AWS 区域。
图 8。放大器的配置。图片作者。
然后点击Next
和Save and deploy
。
3.Doctor.ai 是如何工作的
在我们继续测试 Doctor.ai 之前,让我解释一下 Doctor.ai 是如何工作的。本质上,Doctor.ai 是一个信息检索系统。尽管它能掌握自然语言,能理解上下文,但它并不是一个真正的健谈者。它只理解一组预定义的查询。所以,我们需要有目的的和 dr . ai 说话,比如我们可以问一个病人去了多少次 ICU,是否曾经感染过金黄色葡萄球菌,接受过什么样的治疗。在莱克斯的行话中,这些“目的”被称为“意图”。
目前,Doctor.ai 可以实现以下意图:它检查这是否是第一次 ICU 就诊;它计算病人入院的次数;它显示过去的诊断、实验室结果和分离的微生物;它甚至可以推荐治疗方法。通过增加一些礼貌、命令和测试意图,Doctor.ai 可以像人类接待员一样进行简短的对话。由于上下文理解,Doctor.ai 还可以理解代词。我们用每个意图的样本话语训练 Lex,以便它能够理解生产中的类似话语。因此,当与 Doctor.ai 对话时,它会尝试将用户输入分类为十二种意图之一。如果分类失败,Doctor.ai 将退回到 FallbackIntent。
知道 AWS Kendra 和 Lex 的区别很有意思。Kendra 可以从其摘要文本语料库中以文本摘录的形式给出答案。本质上,它很像一个内部私有数据的搜索引擎。但是它不能聚合数字数据。例如,我们不能问病人去过 ICU 多少次,他的*均血糖水*是多少,或者某个病人的最后两次诊断是什么。相反,Lex 可以在 Lambda 函数的帮助下完成这些查询。这些函数通过 Neo4j 驱动程序查询后端 Neo4j 数据库。我们使用了图形数据库 Neo4j,因为它可以直观、轻松地对许多复杂的 eICU 维度进行建模。它使 Lex 能够汇总患者健康史许多方面的数据。莱克斯甚至可以建议在 GDS 的帮助下进行治疗。
Doctor.ai 中的治疗建议是基于用户相似度的。原则上,它的工作方式与电子商务网站中的产品推荐相同。具体来说,Doctor.ai 计算所有患者之间的成对余弦相似度。相同性别、年龄差异小于 10 且相似性得分高于 0.9 的患者被视为相似。我们设置这些严格的标准是因为我们想避免误报。当患者需要治疗建议时,Doctor.ai 首先返回他的类似患者已经接受的治疗,并考虑建议的治疗是否与患者的当前诊断兼容。如果治疗满足约束条件,Doctor.ai 会将它们推荐给医生。但是由于严格的标准和诊断及治疗数据的缺乏,目前只有少量的患者会得到治疗建议。
为了保护隐私,我们可以通过用户认证和授权来控制患者和医生可以看到谁的记录。借助 Neo4j Enterprise,我们甚至可以使用基于角色的访问控制(RBAC)来对某些维度进行保密。例如,我们可以让医生无法访问“种族”维度,但患者自己可以访问。
4.测试 Doctor.ai
理论了半天,辛苦了半天,还是来测试一下 Doctor.ai 吧,用 Chrome 打开 Amplify 里的Production branch URL
。因为 eICU 数据是匿名的,所以我们使用pid
作为患者的姓名。
4.1 诊断
我们可以一个一个的阅读或者键入下面的询问,看看 Doctor.ai 是怎么回复的。
Are you online?This is patient 002-43934How many times did he visit the ICU?What was the diagnosis?
图 9。作者在.图像中的诊断检索。
正如你从上面的截图或你自己的测试中所看到的,Doctor.ai 能够告诉我们,患者 002–43934 因为心脏骤停已经两次进入 ICU。
4.2 实验室结果
假设患者 002–33870 在我们面前,我们想知道他的葡萄糖和血红蛋白水*:
Are you onlineThis is patient 002–33870What was his glucose level?What was his Hemoglobin level?
图 10。作者在 dr . ai 图像中检索实验室结果。
艾医生很快检索了他最*两次重症监护室就诊时的血糖和血红蛋白读数。
4.3 治疗建议
最后,让我们来看看 Doctor.ai 会为 003–2482 号患者推荐哪些治疗方法。
This is patient 003–2482What was the diagnosis?treatment recommendation
图 11。作者在 Doctor.ai 图片中的治疗建议。
有趣的是,艾医生建议对这位在最后一次重症监护室就诊时服药过量的患者进行会诊。这个建议乍一看很奇怪。但是药物过量可能会损害大脑功能,所以为了他的完全康复,神经科会诊可能是必要的。
结论
在这个项目中,我们将 Neo4j、AWS 和 eICU 数据集放在一起,构建了一个小型虚拟语音助手。尽管 Doctor.ai 目前只能完成有限的一组查询,但不难看出它在医疗保健方面的巨大潜力:我们可以在 ICU、精神病诊所和牙医中使用它。通过改变底层数据,我们甚至可以使其成为其他行业的通用问答聊天机器人。
Doctor.ai 还需要更多的打磨才能成为一款成熟的产品。首先,它的语音识别是由 Chrome 浏览器支持的,并不总是很精确。其次,在交谈中经常会混淆。这部分是由于它的上下文记忆仅持续五分钟。但更有可能的是,它的一些配置需要优化。第三,虽然 eICU 是一个大数据集,但许多患者的记录不完整。这使得信息检索和机器学习变得困难。我们还可以训练它理解更多的意图,提高它的情境意识。此外,您可以将 Kendra 添加到组合中。最后,虽然 Neo4j 社区版功能非常强大,可以有效处理这个 demo,但是并不是针对生产的。所以你应该考虑用企业版或者 AuraDB 来代替。
所以请试试 Doctor.ai 并给我们你的反馈。
更新:
关于 Doctor.ai 的第二篇文章 已经发表在 Neo4j 的官方博客上。它深入到 Lambda 和 Lex 的实现。
第三篇 讲的是把三个知识图谱转移到 Doctor.ai 中,他们把 Doctor.ai 变成了一个更有知识的聊天机器人。
第四篇 是根据第三篇的知识图而来。由于知识图表的数据,Doctor.ai 现在可以根据症状或突变的基因进行简单的诊断。
第五篇 是将图分发给 P2P 网络的尝试。
第六篇 使用 GPT-3 作为 NLU,提高性能,减少开发时间,收缩代码。
The seventh article Can Doctor.ai understand German, Chinese and Japanese? GPT-3 Answers: Ja, 一点点 and できます!shows that Doctor.ai can understand German, Chinese and Japanese thanks to GPT-3.
第八篇用 Alan AI 提高 Doctor.ai 的语音识别。
第九篇使用 Synthea 作为新的替身数据。
第十篇使用 GPT-3 从原始文本中提取主谓宾。
第十一部使用 GPT-3 来 ELI5 复杂的医学概念,使它们更容易理解。
第 12 期对比《ai 医生》中 GPT-J 和 GPT-3
第 13 个演示了一个贝叶斯知识图。
第 14 篇文章基于 Doctor.ai + GPT-3 + Kendra 构建了一个集合聊天机器人。
https://dgg32.medium.com/membership
文档 101: 5 帮助你创建更好文档的工具
消除繁重的负担,专注于内容
编写文档可能是程序员最害怕的任务之一。我的意思是,我们是程序员,不是作家。众所周知,程序员是伟大的,他们写代码,但不擅长解释它的思维过程。
我相信这就是我们如此讨厌写文档的原因,因为那时,我们需要用文字来解释我们的思维过程,以便他人理解。这从来都不是一个有趣的任务。
不管怎样,所有程序员都知道写得好的文档的重要性,以及它对任何编码项目的成功是多么重要,无论它是打算用于开源还是作为团队中的一个提供项目。
当需要添加/删除新特性时,记录编写代码的步骤和一些设计决策的合理性对使用代码的人和编写代码的人都是有益的。
在本文中,我们将介绍 5 种工具,它们可以帮助您生成文档的大纲,让您不必担心小细节,而不必专注于向他人解释您的代码及其用法。
№1:阅读文档
谈到文档工具,我们不能不提到阅读文档。我喜欢称之为阅读文档,在一个文档仙境中。这个工具在开源社区中非常有名的原因是 read docs 不仅是一个帮助你编写文档的工具,而且它还可以帮助你保持所有内容与版本同步,最重要的是,它还可以托管你的文档以便于访问。
图片为作者截图。
阅读文档的另一个好处是,它是一个记录良好的文档工具——看到我在那里做了什么吗?。它的入门指南是你开始写文档所需要知道的,而不需要考虑托管服务器或文档设计。
对于新程序员来说,阅读文档是学习和磨练文档写作技能的绝佳选择。
№2: Github 页面
无论您是开源程序员、在公司工作,还是刚刚开始您的学习之旅,您都有机会在 Github 上看到一些项目,并看到一些开发人员如何使用项目的存储库来提供简单的代码文档。
图片是作者拍摄的 Numpy repo 自述文件截图。
如果你没有创建一个独立的文档站点,并且希望所有的东西都在同一个位置,你可以使用Githubreadme文件来编写你的代码的文档。你需要熟悉降价的格式,并接受简单的单页文档。
如果您想要扩展您的文档并拥有多页文档,那么您可以使用 Github pages 来记录您的代码并将其连接到您项目的 Git 资源库。
图片为作者截图
№3:泰特拉
到目前为止,我们遇到的两个选项允许您编写和托管您的公共使用文档。但是,如果您想编写仅供内部使用的文档,该怎么办呢?这意味着只有公司或团队内部的人才能访问这些文档。那你的选择是什么?
如果您希望托管仅供内部使用的文档,那么 Tettra 可以成为您的文档*台选择。
图片为作者截图。
Tettra 最初是用来建立公司的 wikis,以便新加入的人可以找到他们适应新工作环境所需的所有信息。然而,它可以用来记录任何东西,并在局域网内使用。
№4:收纳盒纸
另一个可以用来创建内部文档的工具是 Dropbox Paper 。Dropbox Paper 是创建公司 wiki 和在局域网内共享信息的另一个好选择。
图片为作者截图。
它允许您在文档工具中添加代码块、图像和所有您可能需要的信息。它还允许文档的主要作者批准或拒绝协作者对文档内容的编辑。
№5:工艺街
如果你是软件开发的新手,或者想把所有项目的所有文档都放在同一个地方,那么 Process Street 就是你要走的路。
图片为作者截图。
您可以使用此工具创建一个清单,列出您需要添加到文档中的所有必要的详细信息(部分),并对您拥有的所有文档进行保存和排序,以便您的团队或公司中的所有人都可以公开并轻松访问这些文档。
最后的想法
记录您的代码是可能使您的项目成功或失败的一个方面。尽管大多数程序员和开发人员编写文档并解释使他们的代码完美完成任务的魔力,但没有人能否认拥有好的文档是多么重要。
如果您是编写供他人使用的软件领域的新手,或者是开发团队的一员,文档可能——起初——看起来是必要的。但是,正如我们所有人逐渐意识到的那样,有时记录代码可能非常关键。
想象一下,有一个包含数千或数百万行代码的代码库,要求您添加新功能或删除现有功能。如果代码没有被很好地记录,你会怎么做呢?
您可能需要阅读大部分代码,试图找出在不破坏代码的情况下完成任务所需的所有连接。
听起来很糟糕,不是吗?
这是当你写任何一段代码时,不管它有多复杂,你都应该记住文档的主要原因之一。
在本文中,我们介绍了 5 个工具,您可以使用它们来简化创建和编写文档的过程。请记住,拥有写得好、容易获取的文档可以改变你的项目,也许还能让你得到梦想中的工作。
记录您的 Python 代码
正确记录 python 代码需要练习的 3 件事
照片由 Unsplash 上的 Azharul Islam 拍摄
我相信你们都已经了解了编写代码文档的重要性。作为一名数据科学家,我通常在开发过程中使用 Jupiter 笔记本,那里的笔记对我来说已经足够了。
但是,老实说,当我几周后回到 it 部门,将代码转移到产品中时,我不得不绞尽脑汁很多次,尽管我不愿意承认。此外,当你在一个团队中工作或把它移交给另一个团队时,这可能是一个挑战。😅
正是那些你忘了记在笔记本上的小事,会引起最大的头痛。
在这篇文章中,我将涵盖你需要知道/实践的 3 件事,以尽可能地完善你的文档。
1.评论
尽可能在代码中添加相关的注释。注意不要在任何事情上添加评论。最好的注释是那些你不必写的注释,因为代码非常清楚。
一般来说,评论分单行和多行两种。在 Python 中,注释通常是在注释前使用标签符号(#)来完成的。
但是,对于多行注释,您也可以使用多行字符串文字(''' ''')方法。由于字符串没有被赋给任何变量,Python 解释器会忽略它;因此充当评论。
多行注释应该使用 hashtag 样式还是 string literal 样式?选一个风格坚持就好了。就我个人而言,我喜欢标签,因为我是一个容易出错的人😅。
如果您不小心使用字符串文字注释样式,您可能会不小心将注释变成文档字符串,这只会使文档变得混乱。
Python 中注释的各种方式。(来源:作者)
你如何决定什么时候需要添加一些评论?我用这个简单的引用来自杰夫的话作为北极星。
代码只能告诉你程序是如何工作的;评论可以告诉你为什么有效。——杰夫·阿特伍德
当决定添加注释时,问问自己代码是否解释了其背后的原因。如果“为什么”含糊不清或者很难一目了然,那么它可能是一些不错的旧时尚评论的好选择。👍
2.显式打字
尽可能明确你的代码。这将大大有助于消除任何歧义。我相信你们所有人对变量的声明都很清楚明确,但是没有几个人对函数的定义很明确。
下图是定义函数时显式键入的一个例子。
显式定义函数的参数类型、默认值和返回类型。(来源:作者)
从上面函数定义的签名中,由于显式类型化,我们可以知道很多关于函数输入和输出的信息。例如:
The function has 3 inputs:
1\. A variable called **Dimensions** of type **List**.
2\. A variable called **shape** of type **string**.3\. A variable called **unit_type** of type **string** with default value called '**metric**'.The function also has a return value of type **float**.
看到这有多有用了吗?😄只要看一下那个签名,我们就能知道很多关于这个功能的信息。
3.文档字符串
在文档方面,Docstrings 是您最好的朋友。它们提供了一种标准化的方法来定义整体效用、参数、异常等等。
如果您使用的是 Visual Studio 代码,请安装Python Docstrings Generator扩展。这将使记录变得更加容易!
VS 代码 Python 文档字符串生成器的运行。(来源: VS 代码扩展市场)
你所要做的就是在一个函数下键入"""
,然后按下 Shift 键。然后,它将生成一个模板 docstring,并使用输入信息自动填充 docstring 的各个部分。
Pro-tip: 完成函数后,执行文档字符串。这样它会尽可能自动填充,包括返回类型、异常等。
作为一个例子,让我们将 docstrings 应用于我们在上一节中使用的函数定义。
在 VS 代码中使用 docstrings 扩展。(来源:作者)
看看 VS 代码中的 docstrings 扩展如何使用来自函数签名的信息自动生成一些文档。它还突出显示了您的文档需要检查的部分。
一旦你为这个项目完成了 docstrings,你就可以使用 mkdocs 把它变成一个简单优雅的静态网站。改天再说吧。
PS:忽略返回值卷下的波浪线。它的出现是因为我没有在函数中声明一个可变体积。T13😅
最后的想法
记录你的可以走很长的路。它不仅帮助其他人理解代码做什么,而且通过让你重新思考你的代码,迫使你在所有文件中应用一些标准化,让你成为一个更好的沟通者,让你成为一个更好的开发者。
起初,记录可能感觉像一些额外的挑战。但是,如果您实践了本文中提到的要点,并在 IDE 中使用了一些扩展,过一段时间后,它就会变得简单并成为您的第二天性。😃
我希望这篇文章对你有用。如果你有任何问题或者你认为我能帮忙,请联系我。
随时和我联系 推特 也一样。😄
你可能也会喜欢我的这些文章:
https://python.plainenglish.io/the-only-data-science-machine-learning-book-i-recommend-4fc23b947dfe https://medium.com/codex/3-simple-side-hustles-to-make-extra-income-per-month-as-a-data-scientist-7470030fbf43
避开激光——来自谷歌招聘挑战的奇妙问题
算法解释
谷歌有一个名为 Foobar 的秘密招聘挑战。我喜欢这些问题,因为它们让我再次使用数学。
图片由来自 Pixabay 的 Javier Rodriguez 拍摄
序幕
我最*写了一篇文章,讲述了我在谷歌名为 Foobar 的秘密招聘挑战中的经历。这个挑战包括五个关卡,设定在一艘虚构的宇宙飞船上,由一个叫 Lambda 指挥官的恶棍操纵。每个级别包含一到三个相当复杂的问题,通常需要一些大学水*的数学知识才能解决。挑战将在第三级后询问成功的参与者是否愿意与谷歌招聘人员分享他们的详细信息。然后,招聘人员通常会联系开发人员。
你可以在这里找到关于 Foobar 的所有细节:
https://betterprogramming.pub/google-has-a-secret-hiring-challenge-called-foobar-14625bfcea7a
躲避激光
写完我使用 Foobar 的经历后,我意识到我仍然可以要求新的挑战。于是我照做了。即使我已经解决了所有五个级别,它给了我一个新的问题和 22 天来解决它。
问题是
*Dodge the Lasers!
=================* Oh no! You've managed to escape Commander Lambda's collapsing space station in an escape pod with the rescued bunny workers - but Commander Lambda isnt about to let you get away that easily. Lambda sent an elite fighter pilot squadron after you -- and they've opened fire!
Fortunately, you know something important about the ships trying to shoot you down. Back when you were still Lambda's assistant, the Commander asked you to help program the aiming mechanisms for the starfighters. They undergo rigorous testing procedures, but you were still able to slip in a subtle bug. The software works as a time step simulation: if it is tracking a target that is accelerating away at 45 degrees, the software will consider the targets acceleration to be equal to the square root of 2, adding the calculated result to the targets end velocity at each timestep. However, thanks to your bug, instead of storing the result with proper precision, it will be truncated to an integer before adding the new velocity to your current position. This means that instead of having your correct position, the targeting software will erringly report your position as sum(i=1..n, floor(i*sqrt(2))) - not far enough off to fail Commander Lambdas testing, but enough that it might just save your life.
If you can quickly calculate the target of the starfighters' laser beams to know how far off they'll be, you can trick them into shooting an asteroid, releasing dust, and concealing the rest of your escape. Write a function solution(str_n) which, given the string representation of an integer n, returns the sum of (floor(1*sqrt(2)) + floor(2*sqrt(2)) + ... + floor(n*sqrt(2))) as a string. That is, for every number i in the range 1 to n, it adds up all of the integer portions of i*sqrt(2).
For example, if str_n was "5", the solution would be calculated as
floor(1*sqrt(2)) +
floor(2*sqrt(2)) +
floor(3*sqrt(2)) +
floor(4*sqrt(2)) +
floor(5*sqrt(2))
= 1+2+4+5+7 = 19
so the function would return "19".
str_n will be a positive integer between 1 and 10^100, inclusive. Since n can be very large (up to 101 digits!), using just sqrt(2) and a loop won't work. Sometimes, it's easier to take a step back and concentrate not on what you have in front of you, but on what you don't.
好吧,让我们仔细分析一下。
目标
我们应该计算 2 的*方根的整倍数的和
初步意见:
- 输入可以很大(最多 101 位)
- 我们需要一个半解析的解决方案,不能简单地强行实施
- 精确度很重要
- “有时候,后退一步,不要把注意力放在你面前的东西上,而是放在你没有的东西上,会更容易。”
解决方案
看完问题,我只是让它沉几天,思考一下我们解决问题会需要的相关概念。
2 的*方根的*似值
我最初认为我可能需要一个 2 的*方根的好的*似值来简化表达式。原来有一个*似。但这并没有让我有所收获。
地板的属性
我的第一个想法是检查地板的属性。也许有办法简化这个等式。然而,这被证明是一个死胡同。我找到了一些属性,但是没有一个可以用来简化表达式。
发现重复模式
所以我想也许我们可以对这个系列本身做些什么来简化表达。我最初认为,有可能存在一种模式来计算该系列的块,并证明另一个块将是第一个块的倍数。我尝试了一些方法,但都不太有希望。
数学解决方案
我想起了那个提示:
有时候,后退一步,不要把注意力放在你面前的东西上,而是放在你没有的东西上,会更容易
因此,也许这意味着有一个表达式,是由我们的原始系列隐含的有用的属性。这是我偶然发现 比蒂序列 的时候。我们手头的序列是一个 Beatty 序列,这意味着存在一个具有有用性质的互补序列(根据 Rayleigh 定理)。
关于 Beatty 序列及其互补序列的有用之处在于,它们划分了自然数(瑞利定理)。这意味着我们可以将问题的解决方案表达为一个递归语句:
浮点运算和精度
快好了。我们找到了问题的递归解决方案。然而,我们还没有处理这些巨大的数字。这些数字几乎肯定会导致浮点运算方面的问题(当乘以 2 的*方根时)。你可能在做浮点数的基本运算时见过这种现象。
作为十进制浮点*似的结果,与 0 的微小差异
很明显,这些差异开始累积,扭曲了正确的结果,尤其是与一百位数相乘时。Python 实现浮点使用 C double 类型,实现了 IEEE 754 双精度二进制浮点。这意味着默认情况下,十进制表示将精确到 15 到 17 位有效数字。
幸运的是,Python 有一个名为decimal
的标准库,它可以确保数学按照我们期望的那样在任意精度水*上运行。我们所要做的就是将精度设置到期望的水*(即 101),计算我们的r
、s
,以及该精度范围内的辅助变量。
最终解决
综合以上所有因素,得出:
当我开始解决这个问题时,我不会想到最终的解决方案会如此简洁明了。但通常情况下,当你真正思考一个问题时,它可以被简化很多。我就是喜欢数学。❤️
今天到此为止。您学习了 Python 中的 Beatty 序列、浮点运算和高精度计算。如果你想聊天,可以随时在 LinkedIn 上联系。我可能会等几天再请求下一次挑战。但请放心,还会有更多。
从学术界向工业界过渡时避开陷阱
办公时间
如何成功地完成这一步,并向面试官证明你会是一名优秀的员工
2016 年,我完成了神经科学博士学位,转到了科技领域。从那以后,我在几家公司做过数据科学家和产品经理。我还花了很多时间筛选和面试数据科学职位的候选人。
我经常收到关于如何成功完成这一转变的问题。这篇文章试图总结我的建议。我从许多也经历过这种转变的人那里得到了帮助。特别是,我认为教师奖学金推动了我在工业界的职业生涯,如果你在英国,我强烈建议你去看看。
跳下去的好理由
过渡还有很多其他原因,但我认为这两个是好的原因。
你喜欢和与你截然不同的人一起工作
实验室各不相同,但学术科学通常是一种相对孤独的体验。
即使是合作,你也倾向于和与你有相似观点和技能的人合作。当然,也许你对前扣带皮层知道的稍微多一点,而他们更像是基底神经节的家伙,但是在事物的大格局中,你们基本上是相同的。
如果你喜欢和观点完全不同的人一起工作,工业界可能是个不错的选择。图片作者。
工业界成功的数据科学家与许多与自己截然不同的人互动,最优秀的人喜欢这样。如果你喜欢和技能和观点与你完全不同的人(设计师、销售人员、软件架构师)在一起,那么你可能会喜欢在工业界工作。
相反,如果你发现没有博士学位的人很无聊,很烦人,那你最好还是呆在一个每个人都有博士学位的世界里。
你的注意力持续时间不到一年
我有一个非常多产的博士学位(大量的论文,大量的引用),但是我没有做很多我真的真的很自豪的科学。这是因为我的注意力持续时间比进行深度原创、彻底的神经科学研究的时间要短。
有些人可以多年保持对项目的兴趣。如果你不是他们中的一员,你可能更适合工业或创业环境。图片作者。
我认为人们有不同的智力波长。我擅长执行耗时 1 到 6 个月的项目。我真正钦佩的科学家擅长执行耗时 1 至 6 年的项目。
快速移动和打破东西的能力在工业界受到高度重视,但在学术界会引起与实验室经理的争论。如果你认为自己有“行动偏好”,或许还伴有“短暂的注意力持续时间”,那么你可能更适合在行业中任职。
阻碍学者转向数据科学的事情
拥有博士学位的人通常都很聪明,所以我认为你不应该把重点放在向面试官证明这一点上。相反,我认为避免一些共同的问题更重要。
当你经历的时候,试着想清楚你如何向我证明我不应该担心这些事情。一旦你认识到了这种需要,就应该不难发现如何提高这些技能。毕竟你有博士学位。
完全披露:我遭受了许多这样的缺陷,可能现在仍然如此。
1.难以理解的交流
在公司工作比学术界更需要协作,如果不能交流就很难协作。图片作者。
许多学科并不鼓励清晰的交流。相反,由于其他人从使用复杂的词汇和复杂的语法中得出积极的推论,学者们经常被鼓励沉溺于冗长和模糊的交流中(见我在那里做的)。
如果没人能理解你在说什么,你几乎不可能被聘用到数据科学岗位。
2.没完没了的项目,经常被放弃
如上所述,学术波长通常比工业波长长很多。学术项目可以在投资多年后失败,这没什么。此外,学者们有时会嘲笑这种想法,即尝试规划项目、估计时间或量化不确定性可能是值得的(“这是科学,笨蛋,它天生就是不确定的”)。
一个完全未经证实的项目不完全模型。根据我的经验,在一个短暂的启动窗口之后,事情没有完成的时间越长,就越有可能永远拖下去。图片作者。
当评估来自不同领域的候选人时,很难说一个项目是否被迅速执行。但是你仍然可以从某人是否完成了某件事情中学到一些东西。
即使是科学上的失败也应该产生一些有用的成果(这通常涉及到交流,见上文)。如果有人做了一堆没有成功的实验,但他们拔掉了插头,写了一些东西,然后继续前进,那很好。
如果他们花了 4 年的时间才意识到有些事情是行不通的,我担心在激烈的行业竞争中,他们将很难有效地分配时间,并产生对组织其他部分很重要的产出。
3.对简单问题寻找复杂解决方案的倾向
弯弯曲曲可能很有趣,但如果你是那种总是用弯弯曲曲的方法来解决直线问题的人,你最终会激怒你的同事,浪费他们的时间。图片作者。
我博士的第一批主要论文之一依赖于“不确定环境下学习的分层贝叶斯模型”。如今,我倾向于避免“贝叶斯”和“分层”这两个词,我非常喜欢“线性”和“回归”这两个词。
你可以通过用复杂的方法解决一个简单的问题来发表论文。你不能通过这样做来赚钱。拥有花哨的博士学位的人有时会忽略简单的解决方案,可能是出于认知失调(“我真的很聪明,如果我正在研究这个问题,它肯定是一个困难的问题,因此有一个复杂的解决方案”)。
在工业中,你和你的组织要为不必要的复杂性付出数倍的代价。这里有几种方法:
- 做这项工作要花你更多的时间
- 验证工作是否正确更加困难
- 向其他人传达这项工作更加困难
- 人们更难处理您的输出(例如 10GB 神经网络与 100MB 随机森林)
- 你消耗更多的计算资源
诸如此类。
4.受好奇心或美学的驱使,而不是结果
强烈的审美欲望加上强烈的好奇心是优秀科学家的典型特征。但是在一个行业环境中,过分纵容他们往往会导致非常长的项目(第 2 点)充满了不必要的复杂解决方案(第 3 点)。
这通常表现为不愿意做让项目上线所需的脏活(“恐怕我不会清理 10,000 行数据,但我会很乐意将这个自动编码器转换成一个变化的自动编码器”)。这可能会激怒并危及项目,但对那些最终做了象牙塔里的人拒绝做的所有繁重工作的同事来说也是不公*的。
文森特在 koaning.io 有许多其他好内容
5.糟糕的编码和协作实践
众所周知,学者们倾向于写糟糕的代码,这也不是他们的错,因为靠自己的力量真的很难写出好的代码。高度可复制的开放科学的激励机制(还)没有安排好(但是许多优秀的人正在为之努力)。
许多学生将学习编程作为他们博士学位的一部分,从博士后和教授那里收集旧的代码片段和不可靠的实践,他们自己从来没有真正花时间学习任何软件工程。
在工业界,有时你如何工作和你做什么一样重要。图片作者。
这可能会在工业环境中产生问题。现代软件实践依赖于将工作很好地分割成小块,这些小块可以分布在整个团队中,合理地快速完成,然后拼凑回一起。
这既需要一些软件工程方面的技能,也需要愿意将你的工作提交给其他人审查(通常比你希望的要早)。将雄心勃勃的事情分解成可管理的步骤是很难培养的。
6.不能容忍不断变化的需求
学术界的目标多年来都是一样的:发表论文,获得终身职位。尽管学术科学有很多不确定性,但关于什么是好的结果,很少有模糊不清的地方。自然报纸就是自然报纸,而且非常棒。
在工业环境中(尤其是在初创企业中),目标经常发生变化。图片作者。
不幸的是,在工业领域,情况并非如此。最终目标(“赚钱”)是稳定的,但它离你的日常生活太远,无法成为一个忠实的指路明星。组织对如何实现这个目标的看法——以及你应该花时间做什么——经常会发生变化。
这可能是乐趣的一部分,但是如果你习惯了非常长时间的项目,这可能会令人难以置信地紧张和沮丧。如果你不能容忍这些不断变化的需求,你会经常对你的上司感到不安和愤怒。作为一个没有太多商业经验的技术人员,很难理解为什么昨天的热门项目变成了今天的堆肥。这会导致愤世嫉俗和对组织其他部分失去信心。
如何证明你很适合这个行业
我认为副业是证明你胜任工业职位的最佳方式。对我来说,拥有几个优秀项目的优秀 GitHub 档案是你在商业组织中成功的最强有力的标志。
一个好的开源项目可以展示:
- 沟通和协作能力
- 完成事情的能力
- 可靠的编码实践
- 愿意做自己不喜欢做的事情(例如开发工作)
- 发现问题并制定解决方案的能力
奖励积分:
- 写得很好
- 获得一些你不认识的用户或贡献者
- 得到任何形式的宣传
根据你的性格,你可能不会发现开始自己的副业是最合适的。为现有的开源软件做贡献也是一个很大的好处,尽管它没有表现出像执行自己的软件那样多的主动性和“完成本能”。
与具有互补技能的人合作——一个需要有数据能力的人的设计师或软件工程师——也是你在行业中取得成功所需要的跨学科合作的极好证明。
非常具体地说,我鼓励你从事以下项目:
- 是用 Python 或 Javascript 编写的(不仅仅是在笔记本上)
- 包括部署一些东西,例如 Heroku 或 AWS
- 依靠格式良好的提交,拉请求和代码审查
- 都经过了很好的测试,测试是自动运行的
- 依靠数据库
包扎
不同的人在进行这种转变时会有不同的挣扎。很难规定采取什么步骤来增加你得到你喜欢的角色的机会。希望试着从面试官的角度审视自己能帮助你理解你需要打磨哪些粗糙的边缘来最大化你的成功机会。
如果你觉得这篇文章发人深省,你可能也会喜欢不要自称程序员,这是一篇精彩的“职业自述文件”,鼓励工程师更全面地思考他们的角色。
如果您有具体的问题,或者想要关于您的简历、申请材料或 Github 个人资料的反馈,请随时联系我 @archydeb 。
如果你觉得这很有趣,你可能也会喜欢这些:
【http://deberker.com】原载于 2021 年 2 月 20 日http://deberker.com/archy/dodging-pitfalls-when-transitioning-from-academia-to-industry/。
好的短镜头预示着好的 Lungo 浓缩咖啡吗?
咖啡数据科学
利用周围的一些数据
我喜欢一杯又短又浓的意大利浓缩咖啡。然而,当我看到其他人拉更长的镜头时,我想知道就提取率而言,我的镜头与他们的相比如何(EY)。对我来说,EY 与有很好的相关性,通常,EY 被用来量化浓缩咖啡的质量。我想知道,如果 EY 从镜头的前半部分很好地预测了最终的 EY 会是什么。幸运的是,我有一些数据。
我收集总溶解固体(TDS ),用于计算 EY,有一段时间,我收集我喜欢的镜头和其余镜头的测量值。这后半个镜头是我的杠杆机器没有三通阀的结果,所以即使你拿走杯子,还是会有更多的咖啡流出来。这让我有两个数据点来比较技术,并从我投出的球的后半部分获得一些信息。
通常,我会在 1:1 的输出与输入比和 1:5 之间进行选择。最*,我已经确定了大约 1.3:1 的比例,所以对于 19 克的咖啡渣,我可以得到大约 25 克的液体咖啡。我无法控制后半部分,当与前半部分结合时,等于 2:1 到 4:1 之间的比率。
这个数据的警告是,这些镜头中的大多数是断奏或断奏篡改镜头。所以它们已经比典型的照片具有更高的 ey。我想假设任何学到的东西都可以应用到非分层的镜头,但事实可能并非如此。然而,我要用我所拥有的去工作。
数据
让我们先来看看 EY 与比率,我拟合了一条 2 次多项式曲线。显示了 R^2(表示曲线与数据的拟合程度,越高越好),但它似乎并没有显示出很好的拟合。然而,这些照片也有其他变量,这可能是为什么所有的数据有更强的拟合。
我还看了 TDS 和 EY 的控制图。1:1 拍摄的 TDS 趋势稳定,但 3:1 拍摄的 TDS 趋势不稳定。我怀疑部分原因是这些镜头的后半部分没有压力脉动。我只是在拉了第一个 1:1 后让水跑了。
现在我们应该直接对比一下。它们似乎对 ey 遵循良好的线性和多项式拟合,但对 TDS 则不然。
这些镜头中有许多并不完全是 1:1 或 3:1,但是我们可以检查 EY 与输出重量的比率。这有助于将差异标准化。在这种情况下,EY 没有很好的拟合,但 TDS 比率有。
这个数据强烈表明,如果你拍摄的前半部分有很高的提取率,你的总提取率也应该更高。然后,通过线性或多项式外推法,就 EY 而言,可以将较短的浓缩咖啡与较长的浓缩咖啡进行比较。
如果你愿意,可以在 Twitter 和 YouTube 上关注我,我会在那里发布不同机器上的浓缩咖啡视频和浓缩咖啡相关的东西。你也可以在 LinkedIn 上找到我。也可以在中和 Patreon 上关注我。
我的进一步阅读:
AI 有政治吗?
人工智能校准和安全
兰登·温纳 1980 年对的分析《人工智能有政治》反思当前的人工智能创新
兰登·温纳 1980 年论文文物有政治吗? (Winner n.d. 1980)是一篇开创性的技术伦理文章,作者在其中提出,技术人工制品可能对某些政治结构有偏见,即威权主义和民主。作者触及了两个主要话题,“作为秩序形式的技术安排”和“固有的政治技术”。本文将探讨这些话题以及它们与当前人工智能创新的关系。
“作为订单形式的技术安排”
Winner 以对服务于一个目的的技术对象的讨论开始,但也可能有政治倾向。作者举了几个存在这些政治偏见的例子,包括开发商罗伯特·摩斯在长岛建造的立交桥,其设计方式使公共汽车无法从下面通过,使经常使用这种交通工具的贫困阶层无法进入他的公园。另一个例子是 McCormick 气动成型机,用于几个制造过程的自动化。这些设备取代了 McCormick reaper 制造厂的许多工作,用一些非熟练工人代替了熟练的模具工人来操作机器。引进这种机器的理由是为了提高生产率。然而,真正的原因是为了解散默尔德斯钢铁全国联盟,该组织与麦考密克有纠纷。
作者在这里提出了一个有趣的问题,即某些技术是否“可以被用来增强一些人对另一些人的权力、权威和特权”。他描述了罗伯特·摩斯的桥和麦考密克的机器是如何被创造出来帮助完成某项任务的(分别是公路运输和成型),然而两者“包含的目的远远超出了它们最初的用途”(1980 年第 125 页)
许多人工智能系统有可能落入类似的类别。
克里斯·利维拉尼在 Unsplash 上拍摄的照片
尽管大多数人工智能技术是为了进步和改善我们的生活而开发的,但当我们考虑如何使用这些技术时,风险就出现了。以从可穿戴设备收集用户健康数据的为例。如果这些数据被用于让佩戴者获得他们健康的准确模型,这将对一个人的健康产生深远的积极影响。然而,如果数据超出了其预期的原始用途,并被出售给第三方以制作个人性质的定向广告,则可能会出现严重的隐私问题,甚至是用户心理健康问题。因此,人工智能和数据收集的良性用例必须保持这种方式——围绕数据用例引入法律,特别是针对基于敏感用户数据的广告,可以有助于加强这一点。
“固有政治技术”
温纳描述人工制品如何具有政治属性的第二种方式是“固有的政治技术,似乎需要或强烈兼容特定类型的政治关系的人造系统。”(Winner n.d. 1980,p123)在这里,作者给出了核能的例子,指的是这样一种危险的技术需要一个严格的军国主义政府来管理和执行严格的监管。另一个例子是原子弹,如果没有一个科学先进、有军方支持的政府的存在和支持,这项技术甚至不会存在。
我们在哪些方面可以与现代人工智能系统的发展相提并论?
由 Enrique Alarcon 在 Unsplash 上拍摄的照片
存在天生专制的人工智能系统吗?一个明显的例子是中国在全国范围内大规模采用和部署面部识别系统。这种大规模监控系统显然是为了维护威权国家的秩序,但这有可能永远是这种特定技术的最终用例吗?面部识别系统于 1993 年由 DARPA 首次用于生产环境,作为 FERET 计划的一部分(Rauss 等人,1997 年)。由于人脸识别领域的许多技术进步都是由美国军事工业开创的,因此这种系统的使用可能永远是为了执行命令。这种系统可能本来就是为专制国家设计的,对其中一些国家来说,使用面部识别进行种族定性并非偶然,而是受到积极鼓励的。(‘记者论中国对待维吾尔族穆斯林:“这是绝对的奥威尔式的监视’”2021。)
结论
令人惊讶的是,获奖者的论文在今天仍然如此切合实际,这说明它在 1980 年是多么精心制作。在最后一段中,在将人工智能和数据收集融入当今社会的背景下,我们发现了一个非常准确的观察结果:
“在我们这个时代,人们常常愿意对他们的生活方式做出剧烈的改变,以适应技术创新,同时他们会抵制基于政治理由的类似的改变”(Winner n.d. 1980,p135)。
毫无疑问,随着更新的人工智能创新在未来不可避免地出现,保持道德警惕将是重要的。
参考文献
Rauss,P.J .,Phillips,j .,Hamilton,M.K .,DePersia,a . t .(1997)“FERET(人脸识别技术)程序”,第 25 届 AIPR 研讨会:计算机视觉的新兴应用,2962,253–263。
中国对待维吾尔穆斯林的记者:“这是绝对的奥威尔式监控”(在线)(2021 年)可用:https://www . CBS news . com/news/China-puts-维吾尔人-uyghyrsmuslim-儿童-监狱-再教育-拘留-营地-vice-news/[2021 年 6 月 14 日访问]。
“艺术品有政治吗?”(1980), 17.
这篇文章是我在爱尔兰利默里克大学(University of Limerick)攻读人工智能非全日制硕士学位期间,撰写的人工智能风险、伦理和治理课程的一部分。
如果你喜欢这个故事,请考虑在媒体上关注我。你可以在 https://mark-garvey.com/找到更多信息
人工智能必须是可理解的才是道德的吗?
播客
Margot Gerritsen 在 TDS 播客
编者按:这一集是我们关于数据科学和机器学习新兴问题的播客系列的一部分,由 Jeremie Harris 主持。除了主持播客,Jeremie 还帮助运营一家名为sharpes minds的数据科学导师初创公司。可以听下面的播客:
随着人工智能系统变得越来越普遍,人们开始更多地关注它们的伦理含义。这些潜在的影响是巨大的:谷歌的搜索算法和 Twitter 的推荐系统都有能力有意义地左右公众对任何问题的看法。因此,谷歌和推特的选择有着巨大的影响——不仅对他们的直接用户群,而且对整个社会。
这种权力伴随着被有意滥用的风险(例如,Twitter 可能会选择提升表达与他们偏好的政策一致的观点的推文)。尽管故意误用是一个重要问题,但同样具有挑战性的是避免人工智能系统无意中产生不良输出的问题。
非故意的坏人工智能可能会导致各种偏见,使算法对一些人来说比其他人表现得更好,或者更普遍地说,使系统对我们实际上长期不想要的东西进行优化。比如,Twitter 和 YouTube 等*台在美国(以及全世界)用户群日益两极分化的过程中发挥了重要作用。当然,他们从来没有打算这样做,但他们对社会凝聚力的影响可以说是基于狭隘的度量优化的内部文化的结果:当你为了短期参与而优化时,你往往会牺牲长期的用户福祉。
几乎根据定义,人工智能系统的意外后果很难预测。但它们的潜在影响使它们非常值得思考和讨论——这就是为什么我在本期播客中采访了斯坦福大学教授、数据科学中的女性(WiDS)倡议的联合主任和 WiDS 播客的主持人 Margot Gerritsen。
以下是我最喜欢的外卖:
- 为了确保人工智能系统合乎道德地运行,我们需要了解它们是如何得出结论的。这就要求这些系统是可解释的。如果我们不仅关心人工智能将预测什么,而且关心它为什么做出预测,那么可解释性就成为建立伦理系统的先决条件。
- 科学理论是人类可以解释的,但人工智能——其中一些被设计来做出与这些理论相同的预测——不是。探究这是为什么很有趣。我们的结论是,答案与表达科学理论的语言有关,例如,像物理学理论。它们往往指的是我们知道或能够概念化的物体,如电子或光粒子。人工智能系统完全没有这种限制:原则上,它们可以用完全抽象的术语“思考”世界,这些术语不能映射到人类可以理解的概念上。这就是为什么可解释性研究的一些分支探索我们如何约束人工智能用来进行预测的抽象概念,使它们成为人类可以理解的。
- 任何时候,一项重要的技术——比如这次的人工智能——迅速起飞,它的演变和行为都会反映出它发展时所处的文化泡沫。世界上大多数先进的人工智能都是由硅谷的科技公司开发的。这些公司有相当强烈的典型人口统计和政治偏见,这就是为什么它们必须做出特别努力,纳入在硅谷技术领域代表性不足的观点,例如某些少数族裔的观点(非亚洲人和非白人相对于总人口而言,在技术领域代表性不足)、女性(在技术员工中,占 20% )和政治保守派(在过去 5 次大选中,三藩市和山景城有 80%至 85%的民主党人)。
你可以点击这里在 Twitter 上关注玛戈特,或者点击这里在 Twitter 上关注我
播客中引用的链接:
- 斯坦福网站上玛戈特的页面在这里。
章节:
- 0:00 介绍
- 今天使用人工智能的 1:00 个问题
- 4:12 解释和结果之间的自然分离
- 15:55 相信数据
- 19:54 应用于数据的工具
- 26:36 预测结果
- 35:38 结合数学
- 45:27 冲向底部
- 56:18 硅谷文化与多元化问题
- 1:04:43 文化差异
- 1:06:56 AI 研究的责任
- 1:09:22 公*标准
- 1:11:06 计算公*性
- 1:18:00 这些系统的复杂性
- 1:19:43 数据科学领域的女性
- 1:21:48 总结
BERT 需要干净的数据吗?第二部分:分类。
火山是非常疯狂的灾难。我想知道是否有人曾经将一首说唱歌曲描述为“火山”……照片由 Unsplash 上的 Yosh Ginsu 拍摄。
现在是有趣的事情。轻清洗,重清洗,还是根本没有语言模型?为 BERT 寻找清理数据的最佳方法。
到本文结束时,你应该能够在 NLP 灾难推文 Kaggle 竞赛中获得前 50 分(84%的准确率)!
我在排行榜上是 63。然而,前 20 名提交的答案被作弊的人占据了,他们只是提交了比赛的答案。图片作者。
请记住,我们正在尝试对一条推文是否指明了一场灾难(如飓风或森林火灾)进行分类。这项任务的难度在于某些单词的上下文含义不同(例如,将鞋子描述为“火”)。
正如在第 1 部分中提到的,一旦完成标准文本清理,我们需要决定我们想要使用什么机器学习模型,以及输入数据应该是什么样子。本文的目标是比较使用神经网络的元特征学习、BERT 之前强度较低(较轻)的文本预处理和 BERT 之前强度较高(较重)的文本预处理。
除此之外,我还将概述 BERT,它是如何工作的,以及为什么它是目前领先的语言模型之一。
在开始之前,我们只需要阅读第 1 部分的泡菜!
# download the pickles saved from part 1
train_df = pd.read_pickle("../data/pickles/clean_train_data.pkl")
test_df = pd.read_pickle("../data/pickles/clean_test_data.pkl")
现在,让我们从预测灾难的第一种方法开始:使用元特征和深度神经网络。
元特征学习
在这里,我们只使用元特征数据来预测灾难推文。这是一种正在测试的替代方法。我的假设是,它不会像 BERT 模型中的一个那样好,但是,观察它的表现仍然很有趣。此外,也许为了在未来改进 BERT,可以包括这种类型的数据,所以我们应该测试我们的元功能如何帮助区分灾难和非灾难推文。
标准化
为了将我们的元特征整合到我们的建模中,我们需要规范化我们的列。规范化用于将数据集中的整数列更改为使用通用比例,而不会扭曲值范围的差异或丢失信息。我们通过使用MinMaxScaler()
进行归一化,这允许我们将数据保持在[0,1]的范围内。
MinMaxScaler 之后我们的数据是什么样的。图片作者。
列车测试分离
在运行我们的深度神经网络之前,我们需要一种分离数据的方法,以便我们可以在之前没有在训练数据中看到的测试数据集上评估模型。为此,我们使用 sci-kit learn 的train_test_split()
函数。
# train test split
X_train, X_test, y_train, y_test = train_test_split(final_train_df, train_labels, test_size=0.3, random_state=42)
现在,我们有了一个训练数据集、一个带标签的测试数据集和一个未带标签的提交测试数据集,我们可以生成我们的模型,根据训练数据拟合它,并根据我们的测试数据测试结果,然后在将提交上传到 Kaggle 之前,对提交数据运行最终模型。
使用深度神经网络生成分类概率
我通过使用序列函数生成深度神经网络,允许我逐层构建 DNN。这里的“深”意味着 NN 连接得很深,如同密集层一样,这些层接收来自前一层的所有神经元的输入。我添加了具有不同密度的 5 个层,使用了 3 个 ReLu 激活函数(修正的线性激活-该激活函数在神经网络中工作良好)和最终的 sigmoid 函数,该函数允许我输出输入行是灾难或非灾难的相关概率。
# create NN
model = Sequential()
model.add(Dense(90, input_dim=9, activation='relu'))
model.add(Dense(120, activation='relu'))
model.add(Dense(100))
model.add(Dense(20, activation='relu'))
model.add(Dense(1, activation='sigmoid'))
# adam optimizer
optimizer = tf.keras.optimizers.Adam(lr=1e-4)
# compile and summarise
model.compile(loss='binary_crossentropy', optimizer=optimizer, metrics=['accuracy'])
model.summary()
# first fit is history1
history = model.fit(X_train, y_train, epochs=100, batch_size=35, validation_data=(X_test, y_test), verbose=1)
在继续进行模型评估之前,可以显示其他模型的构造,模型评估在模型之间具有相同的方法。然后,可以在模型评估部分中进行跨模型的比较。
轻型和重型数据清理
为了优化 BERT 的性能,一些文本清理可能是必要的,但是我们的 Tweets 的功能的删除量是有争议的。因为 BERT 是使用单词的上下文的模型,该上下文提供了单词在句子中相对于所有其他单词的位置,所以通常会建议保存这样的信息。然而,在某些情况下,如果数据被修剪到更高的程度,BERT 可能会优于。在此部分中,完成了轻重文本清理。然后,我们可以在模型评估部分测试这两种方法的性能。
正则表达式文本清理
下面是一些文字清理。代码分为重码和轻码两部分。在重清洗中,进行所有替换。在灯光清洗中,只制作灯光(根据需要编辑该功能)。
# function taken and modified
# from https://towardsdatascience.com/cleaning-text-data-with-python-b69b47b97b76
stopwords = set(STOPWORDS)
stopwords.update(["nan"])
def text_clean(x):
### Light
x = x.lower() # lowercase everything
x = x.encode('ascii', 'ignore').decode() # remove unicode characters
x = re.sub(r'https*\S+', ' ', x) # remove links
x = re.sub(r'http*\S+', ' ', x)
# cleaning up text
x = re.sub(r'\'\w+', '', x)
x = re.sub(r'\w*\d+\w*', '', x)
x = re.sub(r'\s{2,}', ' ', x)
x = re.sub(r'\s[^\w\s]\s', '', x)
### Heavy
x = ' '.join([word for word in x.split(' ') if word not in stopwords])
x = re.sub(r'@\S', '', x)
x = re.sub(r'#\S+', ' ', x)
x = re.sub('[%s]' % re.escape(string.punctuation), ' ', x)
# remove single letters and numbers surrounded by space
x = re.sub(r'\s[a-z]\s|\s[0-9]\s', ' ', x)
return x
我们以下列方式应用该函数:
train_df['cleaned_text'] = train_df.text.apply(text_clean)
test_df['cleaned_text'] = test_df.text.apply(text_clean)
Lemmatisation(用于重度清洁)
仅仅为了大量的清理,我们增加了一个步骤:lemmatisation。在这里,一个词库被用来去除单词的屈折词尾,使它们回到基本形式,这就是所谓的引理。例如,我们将只得到“运行”,而不是“运行”或“运行”。
这有时可以帮助模型更好地解释句子,所以我们在这里测试它。
train_list = []
for word in train_text:
tokens = word_tokenize(word)
lemmatizer = WordNetLemmatizer()
lemmatized = [lemmatizer.lemmatize(word) for word in tokens]
train_list.append(' '.join(lemmatized))
test_list = []
for word in test_text:
tokens = word_tokenize(word)
lemmatizer = WordNetLemmatizer()
lemmatized = [lemmatizer.lemmatize(word) for word in tokens]
test_list.append(' '.join(lemmatized))
标记化和嵌入
对于轻清洗和重清洗,我们最终需要标记我们的文本数据,并创建适当的嵌入,作为将文本数据包括到机器学习模型中的一种方式。
记号化将一段文本分成记号。这些记号是单词。嵌入标记化的句子可以将文本数据转化为机器学习模型可以阅读的数字。我们用拥抱脸变形金刚库来做这个。
# we use a pre-trained bert model to tokenize the text
PRE_TRAINED_MODEL_NAME = 'bert-base-uncased'
tokenizer = BertTokenizer.from_pretrained(PRE_TRAINED_MODEL_NAME)
因为 BERT 使用固定长度的序列,所以我们需要选择序列的最大长度来最好地表示模型。通过存储每条推文的长度,我们可以做到这一点,并评估覆盖范围。
token_lens = []
for txt in list(train_list):
tokens = tokenizer.encode(txt, max_length=512, truncation=True)
token_lens.append(len(tokens))sns.displot(token_lens)
plt.xlim([0, 100])
plt.xlabel('Token count')
plt.show()
输出条形图。从这个图中,我们看到密度在 40 以上下降。因此,我们将 max_length 设置为 40,并重新运行标记器。图片作者。
但是 BertTokenizer 到底在做什么呢?
对于正常的单词嵌入,我们给每个单词分配一些数值。嵌入是分配给每个索引的 d 维向量。更多信息见此处。
基本单词嵌入:每个单词都有一个唯一的索引和一个嵌入向量。图片由 P. Prakash 拍摄(2021)。
作为 BERT 构建基础的 transformer 的新颖之处在于使用正弦位置编码作为单词嵌入的位置索引。通过将正弦和余弦波用于标记化句子中的偶数和奇数索引,相同的单词可以在不同长度的句子中具有相似的嵌入。这为我们的嵌入提供了没有词序的概念,并消除了重复的嵌入值。
使用正弦和余弦的位置编码公式。
对于 BERT 嵌入,这个想法与仅仅一个转换器略有不同。相反,该模型在训练阶段学习位置嵌入,并利用单词片段标记器,其中一些单词被分成子单词。因此,不只是将词汇表外(OOV)的单词标记为包含所有标记,而是将未知的单词分解为模型为其生成嵌入的子单词和字符标记。这保留了原始单词的一些上下文含义。
您可以看到[CLS]标记,它总是出现在文本的开头,特定于分类任务。还有,[SEP]是一种分句的方式。最后,' ##ing '是一个子词嵌入。图片由 P. Prakash 拍摄(2021)。
def bert_tokenizer(text):
encoding = tokenizer.encode_plus(
text,
max_length=40,
truncation=True,
add_special_tokens=True, # Add '[CLS]' and '[SEP]'
return_token_type_ids=False,
pad_to_max_length=True,
padding='max_length',
return_attention_mask=True,
return_tensors='pt', # Return PyTorch tensors
)
return encoding['input_ids'][0], encoding['attention_mask'][0]
我们在训练和测试数据集上使用该函数:
# train data tokenization
train_tokenized_list = []
train_attn_mask_list = []
for text in list(train_list):
tokenized_text, attn_mask = bert_tokenizer(text)
train_tokenized_list.append(tokenized_text.numpy())
train_attn_mask_list.append(attn_mask.numpy())
# test data tokenization
test_tokenized_list = []
test_attn_mask_list = []
for text in list(test_list):
tokenized_text, attn_mask = bert_tokenizer(text)
test_tokenized_list.append(tokenized_text.numpy())
test_attn_mask_list.append(attn_mask.numpy())
然后可以将它们保存为数据帧,以显示我们现在所处的位置。
最后,我们对清理后的数据进行另一次训练测试分割(无论是重度清理的数据还是轻度清理的数据)。
# train test split
X_train, X_test, y_train, y_test, train_mask, val_mask = train_test_split(train_tokenised_text_df, train_labels, train_attn_mask_list, test_size=0.3, random_state=42)
伯特建模
那么,到底什么是 BERT 模型,为什么我们如此大惊小怪地以不同的方式正确清理我们的数据?
虽然这篇文章中有许多技术细节我可以深入探讨,但我还是要让你参考一下谷歌的文章。BERT 做了首字母缩略词描述的事情:它以双向方式训练变压器。通过使用屏蔽语言模型,在简单的意义上是“留一个”任务或完形填空删除,从句子中删除一个单词,该模型尽最大努力预测该单词会是什么。通过这样做,并在左右上下文的条件下,BERT 模型学习文本的特征,从而能够区分我们推文的共同特征,而不仅仅是 TF-IDF。它还使用了单词的上下文和位置。
“BERT 聪明的语言建模任务屏蔽了输入中 15%的单词,并要求模型预测缺失的单词。”图片和说明来自 https://jalammar.github.io/illustrated-bert/。
这里我们使用一个预训练的 BERT 模型。这样做是一个好主意,因为这个模型的嵌入已经在大量的文本数据上进行了训练,超出了我们可以用这个小数据集完成的任务。因此,它更加高效和准确。
num_classes = len(train_labels.unique()) # this is just 2
bert_model = TFBertForSequenceClassification.from_pretrained('bert-base-uncased', num_labels=num_classes)
checkpoint_path = "../models/light_tf_bert.ckpt"
checkpoint_dir = os.path.dirname(checkpoint_path)
model_callback = tf.keras.callbacks.ModelCheckpoint(filepath=checkpoint_path,
save_weights_only=True,
verbose=1)
print('\nBert Model', bert_model.summary())
loss = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)
metric = tf.keras.metrics.SparseCategoricalAccuracy('accuracy')
optimizer = tf.keras.optimizers.Adam(learning_rate=2e-5,epsilon=1e-08)
bert_model.compile(loss=loss,optimizer=optimizer,metrics=[metric])
然后我们拟合模型。在 10 个时期(这花费了很长时间)上测试该模型之后,显示出在第二个时期之后过度拟合。因此,作为早期停止的度量,历元的数量被限制为 2。有多种方法可以做到这一点,但是对于这个预先训练好的模型来说,约束历元的数量是最方便的方法。
history=bert_model.fit(X_train,
y_train,
batch_size=32,
epochs=2, # in heavy cleaning we use 3 epochs
validation_data=(X_test, y_test),
callbacks=[model_callback])
模型评估
为了对模型进行相互评估,有许多技术可以使用。与任何机器学习模型一样,最佳做法是检查准确性、损失、val 准确性和 val 损失。我们可以通过下面的代码做到这一点:
### Accuracy
plt.figure(figsize=(16, 10))
plt.plot(history.history['accuracy'])
plt.plot(history.history['val_accuracy'])
plt.title('model accuracy')
plt.ylabel('accuracy')
plt.xlabel('epoch')
plt.legend(['train', 'val'], loc='upper left')
plt.show()
### Loss
plt.figure(figsize=(16, 10))
plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.title('model loss')
plt.ylabel('loss')
plt.xlabel('epoch')
plt.legend(['train', 'val'], loc='upper left')
plt.show()
精确度图表的输出示例。图片作者。
这些图表有助于理解模型是过拟合、欠拟合还是良好拟合。假设我们使用预训练的 BERT 模型,我们需要很少的历元来训练该模型(参见朱,J )。
损失图输出示例。图片作者。
在用重清洁和轻清洁测试了我的模型之后,很明显,较少的时期对于防止过度拟合更好。随着精度的不断提高,这一点是显而易见的,但是随着更多时期的运行,val 精度下降,val 损失增加。
最后,如果我们对我们的模型满意,我们可以使用我们的测试数据集生成我们的预测,并将准确性与我们的测试数据集标签进行比较。通过这样做,我们找到最强的模型,然后使用该模型对 Kaggle 测试数据进行预测,并提交给 Kaggle。
预言
我们可以通过运行以下代码来预测任何模型:
# predictions
test_pred = model.predict(X_test)
print(test_pred)
如果预测的输出是概率,我们可以使用这段代码将输出概率转换为预测的二进制值 0 和 1。我们把概率分成 0.5。任何高于 0.5 的预测都被标记为灾难,低于 0.5 的预测为非灾难。
# this checks if the probability of
# disaster is above 0.5\. If so, we label 1.
test_pred_bool = test_pred.copy().astype(int)
for index in range(len(test_pred)):
if test_pred[index]>0.5:
test_pred_bool[index]=1
else:
test_pred_bool[index]=0
final_predictions = test_pred_bool.flatten()
然后对于我们的任何模型,我们可以使用一个函数来评估它们的准确性,通过输入我们对测试标签的预测。
# model test function
def eval_model(predictions):
print(accuracy_score(y_test, predictions))
# Compute fpr, tpr, thresholds and roc auc
fpr, tpr, thresholds = roc_curve(np.array(y_test), np.array(predictions))
roc_auc = auc(fpr, tpr)
# Plot ROC curve
plt.plot(fpr, tpr, label='ROC curve (area = %0.3f)' % roc_auc)
plt.plot([0, 1], [0, 1], 'k--') # random predictions curve
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.0])
plt.xlabel('False Positive Rate or (1 - Specifity)')
plt.ylabel('True Positive Rate or (Sensitivity)')
plt.title('Receiver Operating Characteristic')
plt.legend(loc="lower right")
plt.show()
print(classification_report(y_test, np.array(predictions), target_names=["not disaster", "disaster"]))
eval_model(final_predictions)
DNN 元特征结果。图片作者。
轻数据清理 BERT 模型结果。图片作者。
大量数据清理 BERT 模型结果。图片作者。
顶部打印的数字是以浮点数表示的精度。然后是 ROC 曲线,最后是分类报告,给出了各种指标的细节,比如精度、召回率和 f-1 分数。
我们可以从上面的图表和输出中看到,light BERT 模型在各种指标中表现最佳。ROC 曲线最向左上方,表明假阳性率低,真阳性率高。这反映了更高的精确度和召回分数,意味着该模型更经常地,在它预测为阳性的那些中,得到真正的阳性,并且在那些阳性中,它得到大部分预测基本正确(分别是精确度和召回)。
f1 分数是精确度和召回率的调和*均值。这是一个很好的方法来校准我们的分类水*。然而,它在公式中*衡了精确度和召回率。在我们实际检测灾害发生的情况下,我们更害怕假阴性(第二类错误),因为我们宁愿标记更多的灾害,而不是更少的灾害,尤其是当人的生命受到威胁时。
如何计算 f1 分数?图片来自https://en.wikipedia.org/wiki/F-score。
Light Data Cleaning BERT 模型的总体测试集准确率为 84%,这是一个强有力的指标,表明它在未知的 Kaggle competition 测试数据集上的表现如何。
Kaggle 提交
为了在 Kaggle Competition 数据集上测试模型,我们预测了未提供标签的干净测试数据的标签。
# actual test predictions
real_pred = bert_model.predict(test_tokenised_text_df)
# this is output as a tensor of logits, so we use a softmax function
real_tensor_predictions = tf.math.softmax(real_pred.logits, axis=1)
# this outputs the related probabilities of being disaster vs. non-disaster
# so we then use an argmax function to label
real_predictions = [list(bert_model.config.id2label.keys())[i] for i in tf.math.argmax(real_tensor_predictions, axis=1).numpy()]
终于可以提交预测了!找到从 Kaggle 下载的提交文件,我们可以用我们自己的预测覆盖它:
# use utils function to get submission file in folder
utils.kaggle_submit(real_predictions, 'submission-light.csv')
为了提交,我们使用 Kaggle API 并键入以下内容:
kaggle competitions submit -c nlp-getting-started -f submission-light.csv -m "YOUR OWN MESSAGE"
结论
从 Part 1 到这一部分,我们经历了一个清洗文本数据,从中提取特征,使用典型的预处理方法,最后测试不同的机器学习方法对灾害和非灾害进行分类的过程。结果证明了预训练的 BERT 模型在使用包含在文本数据中的上下文信息方面的能力。具体来说,我们发现,当输入到 BERT 模型中时,大量清理文本数据实际上效果更差,因为这些上下文信息丢失了。最重要的是,通过遵循本文中的步骤,任何人都可以朝着理解如何在 Kaggle 竞争中取得竞争结果迈出第一步。
为了进一步提高准确性,应该尝试进一步调整预训练的 BERT 模型,也许使用大的 BERT,并且可以考虑将元特征数据实现为模型中的附加层,以提供训练数据的另一个维度。
如果你喜欢这篇文章,请在推特上关注我!我每天都在建造。
感谢阅读!
文献学
A.什么是标记化?,(2020),分析维迪亚
G.Evitan, NLP 与灾难推特:EDA,cleaning 和 BERT ,(2019),Kaggle 笔记本
G.Giacaglia,变形金刚如何工作,(2019),走向数据科学
I A. Khalid,用 Python 清理文本数据,(2020),走向数据科学
J.Devlin 和 M-W. Chang,谷歌人工智能博客:开源 BERT ,(2018),谷歌博客
J.朱,班底模型对比,(未注明),斯坦福大学
Kaggle 团队,自然语言处理与灾难推文 (2021),Kaggle 竞赛
页(page 的缩写)Prakash,BERT token izer 的解释性指南,(2021),分析 Vidhya
南蒂勒,使用预训练手套向量的基础知识,(2019),分析 Vidhya
维基百科, F 分数,(未注明),维基百科
糠会影响浓缩咖啡的味道吗?
咖啡数据科学
短暂的研磨实验
我想探索谷壳是如何影响味道的,因为我经常磨得很细,以至于我都没有注意到里面有多少,但是当我磨得更粗时,我肯定会注意到。为了探索这种可能性,我双研磨去除糠,这改善了味道。
首先,我使用研磨设置 50 对小生境零是非常粗糙的。这使得谷壳大部分被分离。然后,我去除了干扰,并在设置 13 重新研磨一切。对于对照组,我做了同样的过程,但没有去除干扰物。我去掉了 0.3 克谷壳。
在研磨设置 50 处去除谷壳之前和之后。所有图片由作者提供。
当谷壳被磨碎并混合在一起时,这就是地面通常的样子。很难看到它,因为它也被磨得很细。
绩效指标
我使用两个指标来评估技术之间的差异:最终得分和咖啡萃取。
最终得分 是评分卡上 7 个指标(辛辣、浓郁、糖浆、甜味、酸味、苦味和回味)的*均值。当然,这些分数是主观的,但它们符合我的口味,帮助我提高了我的拍摄水*。分数有一些变化。我的目标是保持每个指标的一致性,但有时粒度很难确定。
设备/技术
浓缩咖啡机:金特快
咖啡研磨机:利基零
咖啡:中杯家庭烘焙咖啡(第一口+ 1 分钟)
镜头准备:断奏夯实
输液:压力脉动
过滤篮:20g VST
其他设备:Atago TDS 测量仪,Acaia Pyxis 秤
表演
谷壳影响味道和提取。味觉的影响是显而易见的,但只有在比较时间相*的照片时。我怀疑是否有人能从一次射击中看出有很多干扰。对提取率也有轻微的影响。
就拍摄时间而言,他们有相似的时间。覆盖过滤器(TCF)的时间基本不受影响,拍摄本身相对较快。
去除渣滓可以在常规的基础上提高浓缩咖啡的质量,但这有点费力,因为双重研磨并不是设置的一部分。然而,对我来说有趣的是,如此小的东西,21g 的子弹 0.3g(重量的 0.14%)会有如此大的影响。
一个警告是,这个测试是一个快速的测试,我甚至不知道我是否去除了所有的干扰。我从咖啡渣杯边上刮下来的。很有可能我可以用粗磨和筛子去除更多。
在树场附*有一种叫做 Kirimai 的研磨机即将问世,它使用双研磨机来清除地面上的谷壳,所以这应该很有趣。
如果你愿意,可以在推特、 YouTube 和 Instagram 上关注我,我会在那里发布不同机器上的浓缩咖啡照片和浓缩咖啡相关的视频。你也可以在 LinkedIn 上找到我。也可以在媒和订阅关注我。
我的进一步阅读:
工作和学校故事集
数据总是说真话吗?
我们每周精选的必读编辑精选和原创特写
*年来,我们可以收集的数据量呈指数级增长,我们可以用来分析数据的计算能力也是如此。然而,数据科学家仍然不得不每天围绕使用这一新发现的权力做出艰难的决定。在商业、科技和医药领域,以及或多或少的其他人类活动领域,情况都是如此。
例如,大公司和小公司都在投入资金和资源,以确保他们的业务战略与底层数据保持一致。然而,正如斯科特·伦德伯格(Scott Lundberg)所展示的那样,我们仍然必须非常小心,不要落入令人困惑的相关性和因果性的陷阱。微调一个模型来预测哪些客户会流失是一回事;确定哪些特征对客户行为有直接的因果影响需要一系列不同的问题。Scott 关于这个复杂现实世界问题的帖子清晰易懂,你绝对应该读一下。
照片由 Stefano Marsella 在 Unsplash 上拍摄
有时候,看似微不足道的事实比我们想象的要复杂得多。在我们的日常生活中,如果有人问我们“你多大了?”即使我们中那些认为这个问题不符合外交辞令的人也能马上给出明确的答案。另一方面,我们的 DNA 拥有自己的真理。在他们最*的项目中,埃莉诺拉·尚西拉和她的合著者研究了实足年龄和生物学年龄之间令人惊讶的差距,专注于 DNA 甲基化作为前者的预测因子。 Valerie Carey 从不同的方向处理与年龄相关的数据,观察年龄如何影响预测模型中的公*措施。她的结论是,“如果不考虑年龄效应,试图纠正或调整模型以均衡指标可能会产生意想不到的后果。”
仔细的数据分析可能是一种强大且必要的缓解工具,可以用来对付试图利用算法驱动*台弱点的恶意行为者。安娜·雅各布森和她的团队检查了来自疑似俄罗斯巨魔工厂的* 900 万条推文,试图防止未来美国选举诚信的风险。与此同时,在 TDS 播客上,杰瑞米·哈里斯与罗西·坎贝尔就自由分享尖端人工智能研究的潜在危险以及该领域更好的出版规范和实践的必要性进行了交谈。
如果你已经做到了这一步,你可能已经得出了合理的结论,即数据可能是——而且经常是——杂乱的,从数据中提取有价值的事实可能会更加混乱。然而,这并不是绝望的原因——我们经常在*处发现困惑和灵感。本周我们的一些最佳指南和教程的作者也是如此,所以如果你正在寻找具体的实践知识来添加到你的工具包中,你就来对地方了:
- 从茱莉亚·尼库尔斯基(Julia Nikulski)对基于变形金刚的 NLP 模型的介绍(想想伯特或 GPT-2)开始,它向读者展示了他们如何将它们应用于下游任务。
- 马塞尔·海德曼和他的队友查看了一个巨大的房地产数据集创建了一个预测市场需求的模型。
- 受全球推动尽可能快速高效地分发新冠肺炎疫苗的启示, Lila Mullany 探索了几种利用计算机视觉简化现场疫苗接种流程的方法。
- 最后,杨深入探讨了不*衡回归这个“非常实用但很少研究的问题”,而则带领读者通过一些强有力的策略来避免这个问题。
感谢您阅读、分享和互动我们发布的作品。我们希望本周的选择能激励你在你目前正在处理的任何数据集中找到真理,并在时机成熟时采取行动。如果你喜欢这些帖子,考虑成为一个媒体成员来支持我们的作者和我们的社区。
直到下一个变量,
TDS 编辑器
我们策划主题的最新内容:
入门指南
- 追踪你的机器学习进度的清单作者帕斯卡·詹尼茨基
- 关于 Vicky Yu 进行的 A/B 测试,产品分析师应该知道什么
- 如何让您的公司为数据科学做好准备作者Marvin lü作者
实践教程
- 构建可扩展的高性能大数据系统作者米歇尔·丽娃
- Parul Pandey利用这些技巧更有效地使用 Colab】
- 由张秀坤·波尔泽撰写的关于异常检测的综合初学者指南
深潜
思想和理论
- 由马克斯·埃利希在 ImageNet 数据集中进行压缩
- 图表示学习——网络嵌入(上)朱塞佩·富蒂亚
- Wouter van hees wijk 博士的强化学习的四个政策类
美元成本*均法有用吗?
我调查了从 2010 年到 2020 年 10 年间新加坡海峡时报指数(STI)和标准普尔 500 的美元成本*均回报率。
科技日报在 Unsplash 上拍摄的
免责声明:我在业余时间进行这个小小的回溯测试研究,完全出于个人兴趣,这不应该作为任何形式的财务建议!
当我第一次开始研究投资时,我遇到的一个推荐给初学者的投资策略是*均成本法。这个概念听起来相当简单——每个月留出一笔固定金额购买交易所交易基金(ETF),这意味着如果价格高,你就购买较少的单位,而当价格低时,你就购买更多的单位。这种策略的一个巨大优势是不需要把握市场时机,如果你选择一次性投资,这是你需要做的事情(非常容易产生压力)。
用一次性投资把握市场时机这一令人焦虑的现实。
也就是说,如果有些事情听起来好得不像是真的,我们可能应该更加谨慎地对待它——在我们的情况下,*均成本真的有效吗?带着满满一卡车的热情(或者至少是在#mondayblues 即将来临的周日晚上所能聚集的热情)和 10 年的数据,我试图回溯测试这个问题的答案。顺便说一下,这项研究中使用的代码可以在我的 GitHub 上找到这里。
设置背景
由于我住在新加坡,我想到的第一个 ETF 是海峡时报指数。海峡时报指数是一个市值加权指数,由在新加坡交易所上市的前 30 家公司组成。有两个 ETF 可供选择,SPDR 提供的 ES3 和日光 AM 提供的 G3B。这两只 ETF 非常相似,因此为了简单起见,我们将查看 ES3 从 2010 年 1 月 1 日到 2020 年 7 月 26 日期间的历史价格和股息。我们假设每月留出 1300 美元用于 ES3 的*均成本,我们使用的经纪人是 FSMone,除了通常的清算和交易费用之外,它还收取 0.08%的佣金(最低佣金为 1 美元)。
作为美元成本*均策略的一部分,一个常见的考虑因素是是否将股息再投资于 ETF。简单地说,如果你在本月底收到 100 美元的股息,你可以保留这些股息(或许可以好好犒劳一下自己),或者在下个月用这些股息购买更多的 ETF 单位。通常,前者受到鼓励,因为它有助于增加你的回报。为了完整起见,我们将回测有和没有股息再投资的美元*均成本。
在不进行股息再投资的情况下,将美元成本*均计入 STI
要评估多年期投资策略的绩效,我们可以考虑以下几点:
- 总回报——投资组合价值(市场价格乘以拥有的单位数量)加上收到的总股息
- 内部收益率——这给出了当涉及多个时间段的多个现金流(在我们的例子中是投资金额和收到的股息)时的年化收益率
总回报
如上图所示,在没有股息再投资的情况下,*均投入 STI 的美元成本的总回报往往会随着时间的推移在正值和负值之间波动,但似乎*均在 10%左右,这不是一个很好的迹象,因为这些回报不是按年计算的。
投资组合的总价值包括在每个时间点收到的累积股息
为了正确看待总回报,我绘制了每个时间点的投资价值与累计支付金额的关系图。乍一看,回报看起来不太大,但让我们保留我们的意见,直到我们计算出年化回报,好吗?
内部收益率
内部回报率为 3.66%,如果我们假设每年的通胀率为 2%左右,那么实际年化回报率约为 1.66%。
通过股息再投资将美元成本*均计入 STI
如果我们将收到的股息进行再投资,回报会更高吗?剧透:不尽然。
总回报
无论你是否将股息进行再投资,总回报似乎都非常相似。事实上,看起来你的红利再投资确实会导致稍微低一点的回报,尤其是在 2015 年以后。
内部收益率
内部收益率为 2.9%,这比你选择不将股息进行再投资时的内部收益率要低得多。
那么,将美元成本*均化到 STI 中的回报被认为是好的吗?
虽然*均投入 STI 的美元成本的回报似乎不太令人兴奋,但为了进行公*的评估,我们需要对 STI 的回报和投资其他替代资产的回报进行粗略的比较。
与 10 年期新加坡政府债券相比
与 STI 相比,新加坡政府债券被认为是风险低得多的投资。作为替代(尽管假设一次性投资于政府债券会使比较有点不公*),如果我们投资于 2011 年发行、2021 年到期的 10 年期债券,该债券的票面利率为 2.25%,低于 STI 的年化回报率。
与标准普尔 500 的*均美元成本进行比较
如果我们的美元成本被*均纳入美国市场指数呢?由于我的工作地点在新加坡,我需要缴纳 30%的股息预扣税。我将做一个简化的假设(部分是因为这是一个周日晚上,我开始有点饿了),我将只根据资本收益来计算我的回报。换句话说,我假设没有支付股利。
我们将基于 iShares 提供的 IVV 进行回溯测试,因为这将是我们为 S&P500 选择的 ETF。为了公*比较,我们将使用相同的时间段(2010 年 1 月 1 日至 2020 年 7 月 26 日),并假设每月投资 1000 美元(大约 1300 新加坡元)。收取的佣金费用仍为 0.08%,最低佣金为 1 美元。
总回报
这张总回报图看起来确实与 STI 图有很大不同——有明显的上升趋势,即使在新冠肺炎疫情引发的抛售期间,总回报也没有跌入负区。
内部收益率
内部收益率为 11.23%,这似乎表明 S&P500 的表现远远超过了 STI!
但是为什么回报如此悬殊呢?
如果我们看一下这两个市场指数过去 10 年的历史价格,我们会发现非常不同的趋势。
STI 的价格(左边的图表)几乎一直在横向移动,而 S&P500 的价格在过去十年中一直在快速增长。因此,对于一种价格呈横向趋势的资产,无论你是选择美元成本*均法还是设法把握好市场时机进行一次性投资,回报都不会太大。
这项研究的注意事项
然而,对这项研究的发现有一些警告。首先,在评估*均计入标准普尔 500 的美元成本回报时,没有考虑汇率波动——所有计算都是在美元基础上进行的。其次,虽然标准普尔 500 的回报一直高于 STI,但我们也应该注意到,标准普尔 500 往往比 STI 更不稳定,因此我们需要能够承受与该市场指数相关的更快速的价格波动。最后,过去的表现并不代表未来的表现。我们不知道未来十年可能会发生什么(就像我们从未预料到新冠肺炎带来的股市崩盘一样),因为我们所知道的是,我们可能会看到投资者情绪从美国市场转移,甚至会遇到 STI 成分的调整。
注来自《走向数据科学》的编辑: 虽然我们允许独立作者根据我们的 规则和指导方针 发表文章,但我们不认可每个作者的贡献。你不应该在没有寻求专业建议的情况下依赖一个作者的作品。详见我们的 读者术语 。
移情在数据驱动中起作用吗?
在硬数字的世界里,一项重要的软技能
It 差不多十年前就被证明了,数据驱动的决策优于 HiPPO 。从那时起,数据驱动已经成为许多公司和个人的雄心。
“在使用数据驱动的决策方面,行业中排名前三分之一的公司比其竞争对手的生产率和利润*均高出 5%和 6%。”— 安德鲁·迈克菲和埃里克·布林约尔松
为了能够做出数据驱动的决策,人们需要一定程度的数据素养,尽管我更喜欢这个术语:数据流畅性。人们需要能够在数据中思考。
但是不要误认为这是一个纯粹的技术领域!
当努力成为数据驱动时,人们有时会过度兴奋,开始使用数据作为他们观点的教条式证明——“只要看看数据!”
面对相同的数据点,人们会得出完全不同的结论,这种情况并不罕见。事实上,我们经常看到这种情况——以新冠肺炎和关于措施、限制或疫苗接种等的辩论为例。
我敢肯定,即使在你的职业生涯中,你也曾在聪明、有经验、受人尊敬的同事之间进行过激烈的讨论,他们对同样的证据做出了截然不同的判断。
这是怎么回事?数据不会说谎!对吧?
让我在这里发表我的意见。我认为有四个主要原因:
- 我们的大脑不断被各种各样的认知偏差所欺骗,比如(仅举几个例子)锚定、确认偏差、现状偏差、赌徒谬误、生存偏差、宜家效应、集群偏差,以及——更糟糕的——盲点偏差。期望人们保持理性公*吗?
- 数据是一个现实的反映。我们复杂的世界永远无法用有限维的数据完美地表示出来。无论我们如何努力,总会有重要的因素被遗漏,许多复杂的问题被简化,可用数据的质量永远不能被认为是理所当然的。我们不像柏拉图洞穴里的囚犯吗?**
- 往往会有很多竞争目标。我们在解决什么问题?我们追求的是短期利益还是长期利益?我们想推动公司收入或特定产品的销售吗?哪种统计误差更糟糕:I 型还是 II 型?目标不一致会导致巨大的误解。**
- 微观经济学告诉我们,个人做出的选择受到他们偏好的影响。因为我们每个人的喜好不同,所以我们做出不同的选择和决定。我们可能如此害怕某些结果,以至于我们想不惜一切代价去阻止它们,不管它们有多不可能。我们中的一些人可能认为 10,000 美元的公路自行车是一笔大交易,其他人不会支付 100 美元。
从这四点来看,两个人——都是基于数据做出决策——肯定不会得出相同的结论。
当他们不这样做时,并不意味着他们中的一个不够数据驱动!一个人可能会有偏见,误解数据,为一个错误的目标优化,或者只是有不同的偏好。但是两人都对他们的结论深信不疑。
在这些情况下,什么会有所帮助?感同身受,心胸开阔!
- 提问。我们正在解决的问题是什么?我们遗漏了什么重要的数据吗?数据会有什么问题?这些问题不一定要用提问的方式来表达——比如‘这里似乎缺少了什么’,或者‘让我试着总结一下我对目标的理解’——这样可能会有用。
- 真诚地对对方的驱动力感兴趣。他们的目标是什么?他们的体验如何?他们的偏好是什么?感觉你有不同的看法。你能告诉我怎么做吗?
- 承认你有偏见。很有可能你有一个盲点。或者你忽略了一些明显的东西。别人不能帮你识别盲点吗?“你能试着在我的分析中找出漏洞吗?”
- 支持不同意见的人。有时,房间里有更多的人,少数意见可能没有足够的空间来表达他们的意见。为他们创造一个空间。‘她说得有道理!让我们讨论一下。
而且还有很多!我们经常生活在自己的泡泡里——周围都是有相似经历、观点和偏好的人。学习、练习和掌握同理心的最好方法是进行许多艰难的对话,寻求与不同意见的人交谈。阅读。很多。
你的经历越多,你看到的就越多,你尝试的方法就越多样,你就越不容易受到狭隘视野的影响,越不容易被困在自己的(小)世界里。
我认为,思想开放和感同身受有助于澄清差异,找到一致之处,并最终做出最好的决定。真诚地对让别人得出不同结论的事情感兴趣是非常有用的技能。
你甚至可能在自己的推理中发现的一个缺陷。**
感谢阅读!
欢迎在评论中分享你的想法或观点。
面部识别对狗有效吗
OpenCV 简化和解释
作者图片
毫无疑问,面部识别无疑已经成为数据科学领域的一个热门话题,但鉴于隐私问题,它也带来了相当大的争议。然而,这项技术正变得越来越普遍,无论是用于用面部解锁手机,还是出于安全原因(如安全摄像头)检测人和/或图像。在这篇文章中,我想把这个概念更进一步,那就是你是否真的可以训练技术来识别狗的脸。
适用性非常有限,但我的动机更多的是为了科学利益和理解,而不是现实世界的应用。我试图解决的难题是,我能否训练一个计算机算法来识别我的狗的脸,如果可以,就开灯,否则就关灯。下图展示了事件的顺序和所需的设备,最重要的是我可爱的狗。我使用了 Python 和 cv2 包以及专门针对狗的 cascade XML 文件。
作者图片
现在,在我们进入测试的实际结果之前,让我们后退一步,了解计算机视觉是如何工作的。以下面我的狗的图片为例。当计算机显示一幅图像时,它实际上并不知道一幅图像中是否包含一只狗、一个罐子或其他设备。它只看到一堆用来渲染图像的数据。那么计算机是如何识别一只狗的呢?好的..让我们开始吧
作者图片
首先,图像本身被转换成灰度图像,只是因为它比使用彩色图像更快。这在很大程度上是因为灰度图像相对于彩色图像的像素维数(详见下文)。灰度像素范围从 0(黑色)到 255(白色),而彩色图像是多维的,因此有术语 RGB。r、G、B 有它们自己的像素比例,混合其中任何一种来呈现特定的颜色。为了这个项目的目的,我不太关心背景的颜色或他的皮毛,而是组成一只狗的特征,也可以用灰度来表现。现在我们有了灰度图像,图像被分解成更小的方块,计算机开始分析和处理。
作者图片
现在,这些方块中的每一个都有称为像素的东西,每个像素中有一个独特的数据组合,代表一种特定的颜色(在这种情况下是黑色和白色之间的阴影)。每个像素的数据在与其相邻像素的上下文中被存储和记忆。这就是计算机如何确定重要事物的方法,比如一张脸的边缘或者一只狗的鼻子。现在,为了利用机器学习,你需要向算法输入成千上万张狗和其他动物的图像,以便它可以学习狗和猫的区别,甚至更具体地学习狗的鼻子和猫的鼻子的区别。一旦你给它足够多的带有标签数据的图像,它就能够从它所学习的一切中引用回来,并提供算法认为图片包含什么的概率,如下图所示。
作者图片
在上面的例子中,算法认为图像有 75%的可能性包含一只狗,20%可能是一只猫,5%可能是一只狐狸。可以肯定地说,在 75%的情况下,算法可能是正确的。现在,您可以随时在它对图像进行分类之前设置您的最小阈值。例如,我可以告诉算法拒绝图像,不给它任何分类,除非它 85%确定它是一个或另一个。
那么我的经历是怎样的呢?嗯,一开始很难让我的狗保持不动,上下看看。我不得不用一些奶酪贿赂他来引起他的注意。我也意识到我没有在足够多的图片上训练它的脸,所以我进入我的小狗图片海洋,找到了大约 100 张最*的图片。一旦我把它加载到训练集中,预测就好了很多。如果你想看代码演练,但更重要的是想看我的狗用他的脸开灯和关灯,你可以在这里看看。
[https://youtu.be/ofh27VwCoLE](https://youtu.be/ofh27VwCoLE)
我在这个项目上真的度过了一段快乐的时光,也展示了通过一点点尝试和错误,你可以做一些非常棒的事情。最后,我会说这个项目对我的研究来说是成功的,尽管我不期望它已经为主流做好了准备。还有更复杂的方法来执行面部识别,例如使用 YOLO、面部识别甚至 pytorch 中的功能。我可能会对这些库中的一些进行基准测试,以确定分类器能够标记正确图像的程度。希望你喜欢这篇文章。
没有球迷,主场优势还重要吗?
实践教程
用数字来证明粉丝确实有所作为
来源:https://unsplash.com/photos/GB9XKDZWwp0
在大多数国家,人们不再被允许进入体育场,至少不能以正常身份进入。
任何体育运动的粉丝都会告诉你,没有粉丝看比赛是不一样的。有一个缺失的元素。
虽然景象可能不一样,但这种不太可能的情况让我们看到了一件事。球迷不在,主场优势还重要吗?
通过多年的观察,我们知道主场优势在任何运动中都是真实存在的。看看任何一个联赛,你会发现绝大多数时候,球队在主场比客场有更好的数据。
但这是为什么呢?是因为旅游吗?对体育场/更衣室/条件的熟悉程度?粉丝支持?还是纯粹精神上的?回答这个问题实际上一直是不可能的。但是,今年,我们可以看到球迷对主场优势的影响。
NFL 中主队的胜率
整个 2020 年 NFL 赛季都是闭门比赛,或者球迷人数非常有限。这对本赛季主队的表现有什么影响?一张图表胜过千言万语。
在 2003 年至 2019 年期间,主队*均赢得了 55.9%的比赛,标准差为 2.84%。本赛季,这个千年来第一次,主队赢了不到一半的比赛(准确的说是 49.09%)。
在赛季初,假设正态分布,如此小比例的主场获胜的概率估计如下。
来源:自制图片
那个灰色的小部分?就是这样。它稳定在 0.82%。这意味着什么?嗯,简而言之,如果所有事情都保持不变,主队如此小的胜率真的不太可能发生。当然,事情并没有保持*等,因为球迷不在那里。
NFL 中每场比赛的客场积分
让我们看看另一个数据。作为一个长期的球迷,我已经听过几百次了,远离家乡的进攻是困难的。玩家之间无法轻松交流,这让每个人的生活都很困难。一些像西雅图和堪萨斯城这样的体育场以特别吵而闻名,那些球迷基地为此感到自豪!
如果移除风扇会发生什么?
本赛季,客场球队每场比赛得到 24.69 分,比以往任何时候都多。2003 年至 2019 年的*均场均得分为 20.8 分。如果一切保持不变,赛季前发生这种情况的可能性估计为 0.27%。
这些统计数据清楚地表明,与*几年相比,NFL 的主场优势已经不一样了。
NBA 主场胜率
在北美四大联赛中,NBA 以拥有最大的主场优势而闻名。球队通常会赢得 57%到 60%的比赛,这个胜率会上升到 60 年代中期的季后赛。
这一季呢?再说一遍,一张图片胜过千言万语。
在 NBA,赛季还很年轻,所以事情仍然可以改变。然而,到目前为止,自从我可以回顾以来,第一次,主场球队赢了不到 50%的比赛(49.5%)。这意味着他们赢得的比赛比 2019 年至 2006 年间少了约 8%。
在足球或曲棍球中可以观察到非常相似的数字。在许多联赛和体育项目中,客场球队从来没有做得更好。套用伊恩·弗莱明(詹姆斯·邦德小说的作者)的话:
一次是机会。两次是巧合。三次是一种模式。
所有这些都是为了说明,在某些时候,我们可以从观察到的东西中得出结论。显然,“主场优势”的优势部分,粉丝的作用很大。当球迷们最终可以回到体育场时,看看事情会发生怎样的变化将会很有趣。在那之前,让我们尽情享受游戏吧。
感谢阅读!
机器学习知道我什么时候害怕吗?可穿戴健康数据中的异常检测
利用真实的 Fitbit 测量在 Keras 中构建自动编码器的机会。
作者图片
像 Fitbit 这样的可耳听健康设备提供了大量关于我们健身和日常活动的信息。这提供了一个处理大型数据集的绝佳机会,其中个人信息可用于更深入地了解实际现实世界的机器学习应用,而不仅仅是像人类活动识别数据集这样的例子。
本文中使用的数据集来自我的个人 Fitbit 设备,其中的卡路里和心率数据被记录为*三周的时间序列。目标是使用 Keras 建立和使用自动编码器模型来识别基于心率和卡路里随时间变化的异常。
工作总结:
我们将开始加载数据集,并查看心率和卡路里在我们的样本期间如何变化。
我们还将检查包含异常的数据集,以帮助我们了解潜在的异常可能是什么样子。一旦我们开始评估模型的性能,这将变得非常有用。
数据将被清理、缩放和整形,以与我们的 autoencoder 模型兼容。
我们的模型将被建立和评估,以确定预测误差,这将在以后被用来确定是否存在异常。
最后,我们将研究检测到的异常现象,试图解开它们可能发生的原因。
加载和可视化:
数据集是从我的个人 Fitbit 账户下载的,文件以 JSON 格式导出。关于下载自己数据的信息可以在这里找到。在导入所有相关模块后,我们可以使用 Pandas 加载这些文件来生成心率和卡路里的数据帧,它们都是时间序列数据:
#Importing the relevant modules
import glob
import numpy
import os
import pandas as pd
import numpy as np
import seaborn as sns; sns.set()
import matplotlib.pyplot as plt
from tensorflow import keras
from sklearn.preprocessing import MinMaxScaler
sns.set_palette(“hls”, 2)#Loading in the heart rate and calories data
heart = pd.DataFrame()
calories = pd.DataFrame()
datasets = [‘heart_rate-2021–03-*.json’, ‘calories-2021–02-*.json’]
for datatype in datasets:
file_list=[]
path = ‘/Physical Activity/’
os.chdir(path)
for file in glob.glob(datatype):
file_list.append(file)
dfs = []
for file in file_list:
data = pd.read_json(path + file)
print(‘Reading: ‘ + str(file))
dfs.append(data)
concatenated = pd.concat(dfs, ignore_index=True)
concatenated[“value”] = concatenated[“value”].apply(str)
if concatenated[‘value’].str.contains(“bpm”).any():
heart = pd.concat([heart, concatenated], axis = 1)
else:
calories = pd.concat([calories, concatenated], axis = 1)
让我们看一下数据,看看我们可能需要做什么样的清洁:
print(heart.head(5))
print(calories.head(5))dateTime value
0 2021–03–08 21:00:07 {‘bpm’: 57, ‘confidence’: 3}
1 2021–03–08 21:00:17 {‘bpm’: 56, ‘confidence’: 3}
2 2021–03–08 21:00:32 {‘bpm’: 56, ‘confidence’: 3}
3 2021–03–08 21:00:42 {‘bpm’: 57, ‘confidence’: 3}
4 2021–03–08 21:00:57 {‘bpm’: 58, ‘confidence’: 3}dateTime value
0 2021–02–18 00:00:00 1.19
1 2021–02–18 00:01:00 1.19
2 2021–02–18 00:02:00 1.19
3 2021–02–18 00:03:00 1.19
4 2021–02–18 00:04:00 1.19
心率以每分钟心跳数(BPM)来衡量,并以 10-15 秒的不规则间隔进行采样。卡路里数据测量使用的卡路里量,这是使用一个人的基础代谢率(BMR)和活动率的组合来计算的。我们可以改变心率数据的采样频率,以便两个数据集都记录每分钟的观察值。值列也应该删除文本,以便作为浮点数读取:
#Cleaning the training data
heart = heart.sort_values(by="dateTime")
heart = heart.set_index('dateTime')
heart["value"] = heart["value"].apply(str)
heart["value"] = heart["value"].str.split("{'bpm':").str[1]
heart["value"] = heart["value"].str.split(",").str[0]
heart["value"] = heart["value"].astype(int)
heart = heart.resample('1Min').mean()
heart['value'] = heart['value'].round(0)
calories = calories.sort_values(by="dateTime")
calories["value"] = calories["value"].astype(float)
calories.columns = ['time', 'value']
calories = calories.set_index('time')
现在,我们可以将心率和卡路里合并在一起,创建一个涵盖样本期的时间序列数据的单一数据框架。创建一个包含覆盖我们的采样周期的 DateTimeIndex 的空 DataFrame 会使事情变得更加方便:
#Merge the datasets together
empty = pd.DataFrame(pd.date_range(start='2021-03-01', end='2021-03-20', freq='1min'))
empty.columns = ['datetimeindex']
empty = empty.set_index('datetimeindex')
from functools import reduce
df = reduce(lambda left,right: pd.merge(left,right,left_index=True, right_index=True), [heart, calories, empty])
df.columns = ['Heart Rate (BPM)', 'Calories Used']
df = df.dropna()
我们的训练数据将来自连续的样本期。测试数据大约来自我们样本中间的一天,因此我们可以假设影响心率和卡路里数据的条件有一定的连续性:
#Splitting the test day
df_test = df.loc[(df.index > '2021-03-12 00:00:00') & (df.index <= '2021-03-12 23:59:59')]
df = df.loc[(df.index < '2021-03-12 00:00:00') | (df.index > '2021-03-12 23:59:59')]
我们现在处于最终可视化 Fitbit 数据的阶段,将从训练数据开始。训练数据中的差距代表我们之前提取的测试日:
#Visualising training and testing data
datasets = [df, df_test]
for dfs in datasets:
fig, axs = plt.subplots(nrows=2, ncols=1, figsize=(12,6))
axs[0].plot(dfs['Heart Rate (BPM)'])
axs[1].plot(dfs['Calories Used'])
plt.setp(axs[0].get_xticklabels(), visible=False)
axs[0].tick_params(axis='x', rotation=70)
axs[1].tick_params(axis='x', rotation=70)
axs[0].set(ylabel='Heart Rate (BPM)')
axs[1].set(ylabel= 'Calories Used', xlabel="Date")
plt.tight_layout()
plt.show
最后来点图!
一段时间内心率和消耗卡路里的训练数据集
数据集显示了心率和卡路里循环趋势;两个数据集都在早上增加,在白天保持相当稳定,然后在晚上逐渐减少。这是一个预期的趋势,因为它与起床、白天工作、晚上睡觉前放松相关。
转到将用于验证是否检测到异常的测试数据集。这些数据涵盖了一天的时间,我们可以根据我们在培训数据中看到的内容来猜测潜在的异常情况:
一段时间内心率和消耗卡路里的测试数据集
有趣的是,这一天的测试数据有两个峰值,心率在下午显著增加,这与卡路里的增加无关。这表明心率的增加可能与锻炼的变化无关。处理个人数据的额外好处是我知道这一天发生了什么;去医院一趟。作为一个不太喜欢去医院的人,我的 Fitbit 清楚地捕捉到了我的紧张情绪,这使它成为我们稍后将试图识别的异常现象的一个极好的例子。
我们可以在心率和卡路里之间做一个简单的相关矩阵,看看它们之间的关系:
#Make a correlation coefficient
print(df.corr()) Heart Rate (BPM) Calories Used
Heart Rate (BPM) 1.000000 0.102251
Calories Used 0.102251 1.000000
心率和消耗的卡路里之间有一个略微正相关的关系。根据 Fitbit 的文档关于卡路里的计算方式,心率影响卡路里的计算方式,因此我们观察到的正相关是可以预期的。
预处理:
回到代码,我们现在需要调整我们的数据,以便我们可以减少训练数据中任何离群值的影响,这些离群值可能会妨碍模型检测异常的能力。我们将使用 MinMaxScaler,因为这将使心率和卡路里数据在 0 和 1 之间变化,这在我们稍后计算预测误差时非常重要。
#Scale the data
scaler = MinMaxScaler()
scaler = scaler.fit(df)
scale_train = pd.DataFrame(scaler.transform(df))
scale_test = pd.DataFrame(scaler.transform(df_test))
现在数据已经被缩放,我们需要重塑以兼容我们的自动编码器模型。我们模型的输入将是一个三维数组,其形状取决于样本的数量、序列的长度和特征的数量。我们将使用 30 个时间步长(也称为半小时时间间隔)的序列,因为我们假设异常是短暂的。
# reshape to [samples, time_steps, n_features]
def create_dataset(X, y, time_steps=1):
Xs, ys = [], []
for i in range(len(X) - time_steps):
v = X.iloc[i:(i + time_steps)].values
Xs.append(v)
ys.append(y.iloc[i + time_steps])
return np.array(Xs), np.array(ys)time_steps = 30
X_train, y_train = create_dataset(scale_train, scale_train, time_steps)
X_test, y_test = create_dataset(scale_test, scale_test, time_steps)
print(X_train.shape, y_train.shape)
这将生成一个形状为(23333,30,2) (23333,2)的训练和测试集。
模型构建:
我们的数据已经成功整形,现在可以构建我们的 autoencoder 了。本文不会深入讨论自动编码器和 LSTMs 的细节,但是我强烈建议查看以下信息:
自动编码器将由分成编码器和解码器的 LSTM 层组成。首先,模型会将数据的输入序列编码成其关键特征的简化表示,然后解码器会学习如何将该简化表示转换回其原始输入状态。使用自动编码器方法允许我们在数据集内创建“正常”特征的表示。因此,当输入异常序列时,模型将不能重建其简化表示,这将导致较高的模型误差。
由于我们的输入使用两个特征,来自组合预测的误差(即来自心率和卡路里序列重建的误差)将被用作我们检测异常的方法。
我们的模型架构在编码器和解码器中使用多个 LSTM 层,由 RepeatVector 层分隔,repeat vector 层重复由最后一个编码器 LSTM 层给出的简化表示。在解码器末端的最终时间分配将输入重建回其原始形状。
#model building
model = keras.Sequential()
model.add(keras.layers.LSTM(256, input_shape=(X_train.shape[1], X_train.shape[2]), return_sequences=True, name='encoder_1'))
model.add(keras.layers.Dropout(rate=0.2))
model.add(keras.layers.LSTM(128, return_sequences=True, name='encoder_2'))
model.add(keras.layers.Dropout(rate=0.2))
model.add(keras.layers.LSTM(64, return_sequences=False, name='encoder_3'))
model.add(keras.layers.RepeatVector(n=X_train.shape[1], name='encoder_decoder'))
model.add(keras.layers.LSTM(64, return_sequences=True, name='decoder_1'))
model.add(keras.layers.Dropout(rate=0.2))
model.add(keras.layers.LSTM(128, return_sequences=True, name='decoder_2'))
model.add(keras.layers.Dropout(rate=0.2))
model.add(keras.layers.LSTM(256, return_sequences=True, name='decoder_3'))
model.add(keras.layers.TimeDistributed(keras.layers.Dense(units=X_train.shape[2])))
model.compile(loss='mae', optimizer='adam')
model.summary()
模型摘要
该模型将使用 Adam 优化器,并测量*均误差损失。我们将使模型适合我们的训练数据;注意,在拟合我们的模型时,训练数据同时用于输入和标签。256 的批量将用于 25 个时期的训练持续时间。
#fitting on training data
history = model.fit(X_train, X_train, epochs=25, batch_size=256,
validation_split=0.1,verbose=1,shuffle=False)
#plotting loss
fig=plt.figure()
plt.plot(history.history['loss'], label='Training loss')
plt.plot(history.history['val_loss'], label='Validation loss')
plt.ylabel('Loss')
plt.xlabel('No. Epochs')
plt.legend()
plt.show()
培训期间的培训和验证损失
我们现在将使用训练数据和预测之间的 MAE 来显示损失的分布,这将作为我们定义异常的方式。MAE 是根据心率和卡路里数据的组合按时间步长计算的。
#predicting on test data
X_pred = model.predict(X_train)
X_pred_2d = pd.DataFrame(X_pred[:,0,:]).astype(float)
X_pred_2d.columns = ['HR Pred', 'Calories Pred']
X_train_2d = pd.DataFrame(X_train[:,0,:]).astype(float)
X_train_2d.columns = ['HR Test', 'Calories Test']#Plot the test data together
fig, axs = plt.subplots(4, figsize=(12,6))
axs[0].plot(X_pred_2d['HR Pred'])
axs[1].plot(X_train_2d['HR Test'])
axs[2].plot(X_pred_2d['Calories Pred'])
axs[3].plot(X_train_2d['Calories Test'])
plt.setp(axs[0].get_xticklabels(), visible=False)
plt.setp(axs[1].get_xticklabels(), visible=False)
plt.setp(axs[2].get_xticklabels(), visible=False)
axs[0].tick_params(axis='x', rotation=70)
axs[1].tick_params(axis='x', rotation=70)
axs[0].set(ylabel= 'HR Prediction')
axs[1].set(ylabel= 'HR Training')
axs[2].set(ylabel= 'Calories Prediction')
axs[3].set(ylabel= 'Calories Training', xlabel= 'Time Step (per minute)')
plt.tight_layout()#calculate error
predictions = pd.concat([X_pred_2d['HR Pred'], X_pred_2d['Calories Pred']], axis = 1)
train_inputs = pd.concat([X_train_2d['HR Test'], X_train_2d['Calories Test']], axis = 1)anomaly = pd.DataFrame(np.abs(predictions.values - train_inputs.values))
anomaly = anomaly.mean(axis=1)ax = sns.distplot(anomaly, bins=50, kde = True)
ax.set_title('Training Data Loss Distribution')
ax.set_xlabel('Loss')
ax.set_ylabel('Frequency')
fig = ax.get_figure()
训练数据丢失分布
在查看损失分布后,我们可以看到存在具有较高损失值的长尾,这表明模型难以重建输入序列的时间。为了定义用于判断预测是否异常的损失阈值,我们将在第 99 百分位的找到训练数据中的损失。
thres = round(numpy.quantile(anomaly, 0.99),3)
print('99th percentile loss value from training: ' + str(thres))99th percentile loss value from training: 0.177
预测:
现在我们已经建立了模型,并且定义了 0.177 的损失阈值,我们可以继续测试我们的模型,看看它是否能够检测到我们之前在测试数据中发现的异常。我们将重新使用我们的模型和先前重新整形的测试数据,并重复相同的过程来计算预测损失。
#Predicting
X_pred = model.predict(X_test)
X_pred = pd.DataFrame(X_pred[:,0,:]).astype(float)
X_pred.columns = ['HR Pred', 'Calories Pred']
X_test_data = pd.DataFrame(X_test[:,0,:]).astype(float)
X_test_data.columns = ['HR Test', 'Calories Test']
我们使用先前定义的损失阈值作为将数据点定义为异常的分界点。为了更好地可视化我们的异常点,我们将异常列与原始测试数据集相结合,以帮助将事情放入上下文中。
difference = pd.DataFrame(np.abs(X_pred.values - X_test_data.values))
difference['mae loss'] = difference.mean(axis=1)
difference['threshold'] = thres
difference['anomaly'] = difference['mae loss'] > difference['threshold']
difference['index'] = difference.indexX_pred['index'] = X_pred.index
X_test_data['index'] = X_test_data.index
X_test_data = X_test_data.join(difference['anomaly'])
使用之前制作的定标器可以将心率和卡路里数据恢复到原始刻度,这将有助于我们更好地理解我们的结果:
X_test_data_original = pd.DataFrame(scaler.inverse_transform(X_test_data[['HR Test','Calories Test']]))
X_test_data = pd.concat([X_test_data, X_test_data_original], axis = 1)
X_test_data.columns = ['HR Test', 'Calories Test', 'Index', 'Anomaly', 'Heart Rate (BPM)', 'Calories Used']
我们最后的步骤将集中在可视化的预测结果。我们将从绘制每个数据点的*均损失开始,根据是否发现异常进行颜色编码:
plt = sns.lmplot(x='index', y='mae loss', data=difference,
fit_reg=False, hue='anomaly', scatter_kws={"s": 10}, legend=True, legend_out=False, height=5, aspect=2)
plt.set(xlabel='Time Steps (per minute)', ylabel='MAE Loss')
一段时间内的预测*均绝对误差
如我们所见,我们之前定义的异常截止点已用于检测异常数据点。
在我们的测试数据中查看模型损失在一天中的分布,很明显,在清晨发生异常的可能性极小,因为数据点在 500 分钟以下(也就是上午 08:30)的损失一直很低。这是有道理的,因为我睡着了,显然不会在半夜去健身房。
然而,在一天的其余时间里,情况略有不同,损失分布变得更大,一些数据点被标记为异常。下午(也就是 720-1140 分钟)损失的更大分布可能归因于我下午时间表的变化。
异常点分布在多个时间间隔中。为了理解这些异常事件发生的原因,我们可以看看心率和卡路里数据集:
plt = sns.lmplot(x ='Index', y='Heart Rate (BPM)', scatter_kws={"s": 10}, data=X_test_data,
fit_reg=False, hue='Anomaly', legend=True, legend_out=False, height=5, aspect=2)
plt.set(xlabel='Time Steps (per minute)', ylabel='Heart Rate (BPM)')
由检测到的异常着色的心率测试数据集
心率数据已通过在之前的损失图中检测到的异常进行了颜色编码。我们可以看到,明显的异常是当我的心率异常高时,达到每分钟 140 次以上,在 800-1000 分钟之间形成两个峰值。这与我之前提到的去医院的时间非常吻合。
有趣的是,我们可以看到异常也发生在正常心率期间(基于相邻的心率值)。我们的模型使用了心率和卡路里,这表明这些异常可能与消耗的卡路里有关。我们需要研究卡路里数据以获得更多信息:
plt = sns.lmplot(x ='Index', y='Calories Used', scatter_kws={"s": 10}, data=X_test_data,
fit_reg=False, hue='Anomaly', legend=True, legend_out=False, height=5, aspect=2)
plt.set(xlabel='Time Steps (per minute)', ylabel='Calories Used')
使用的卡路里测试数据集通过检测到的异常进行着色
我们的卡路里数据显示,在峰值期间检测到异常,通常超过每分钟 6 卡路里,这可能是在我走路的时候。当我们观察与高卡路里相关的异常时,我们看到它们发生在心率被认为正常的时候。这种关系,高卡路里但*均心率,可以被认为是异常的,因为通常大多数卡路里将在心率较高时被使用(心率和卡路里之间的正相关性也支持这一点)。
当心率高而卡路里数据低时,观察到相反的关系,表明心率高是由于与身体活动无关的原因。由于这是我自己的数据,我知道下午我去了医院,由于害怕打针,我的心率非常高。这是异常现象的一个很好的例子,也是我选择这一天作为测试数据集的原因。
结论:
我们已经学会了如何使用 LSTM 网络开发一个自动编码器模型来检测健康时间序列数据中的异常。我们使用我们的模型来学习什么被认为是正常行为,并使用它来检测潜在异常。该模型在一天的异常数据上进行了测试,并正确识别了心率和卡路里之间的一些有趣趋势,这些趋势与自发散步和去医院的可怕旅程有关!
这个项目有望突出处理个人数据的乐趣,让我们将模型结果解释到比一些经典的现成数据集更高的水*。感谢阅读!
邻居陷阱有效吗?
你怎么知道?
澳大利亚瓶刷中的新西兰 tui。作者照片。
许多新西兰人每周至少一次,甚至更频繁地在他们的社区诱捕线工作。这包括参观一系列为啮齿动物、刺猬、鼬鼠或负鼠设置的陷阱,看看是否有新的猎物。他们这样做是作为一种社区服务,帮助新西兰摆脱外来的破坏本地动植物的哺乳动物掠食者。他们的努力现在包括在一项由政府资助的名为Predator Free New Zealand 2050的特别倡议中(参见“改善鸟类的世界”及其参考文献)。
一个重要的问题是这些邻里诱捕努力的效果如何?尽管许多社区组织向数据库如陷阱提供公民科学数据。NZ 和 CatchIT ,似乎对公民科学数据集的科学分析相对较少。在这篇文章中,我研究了新西兰奥克兰西部 Waitakere Ranges 区域公园所包围的 Huia 社区诱捕组的数据。威塔克里有许多诱捕的努力(见无捕食者威塔克里牧场联盟)。我们的社区努力,Huia 诱捕组开始于四年前,在撰写本文时提供了超过 1100 个捕食者的数据。
邻域诱捕努力
在这篇文章中,我首先从陷阱部署的数量和陷阱设置的类型方面研究了我们的邻居陷阱是如何演变的。在过去的四年里,Huia 的邻居诱捕工作大大增加了(图 1)。努力从 2018 年任何给定季度部署的 2 到 5 个陷阱增加到 2021 年部署的 30 多个陷阱。随着时间的推移,陷阱的多样性也在增加。我们最常用的捕鼠器是传统的 DOC200 和自动 A24 捕鼠器,目标是老鼠和刺猬。今年我们增加了捕鼠夹和捕鼠夹。我们还有针对负鼠的 Timms、Warrior 和 Trapinator 陷阱。
图 1:过去四年在怀塔克里山脉的 Huia 的诱捕努力。图例中显示了所用的陷阱类型。A24、DOC200 D-Rat、Victor 和 Rat 捕鼠器捕捉大白鼠和小白鼠,捕鼠器和 nooski 捕鼠器捕捉小白鼠,而 Timms、Warrior 和 Trapinator 捕鼠器则捕杀刷尾负鼠。在标记日期结束的 3 个月期间内,对部署的至少捕获一次的陷阱进行计数。在一个季度内没有捕获任何东西的陷阱不包括在内,这是陷阱的一个限制。新西兰公民科学记录。(作者配图)
邻域陷印的有效性
随着努力的增加,将部署的陷阱数量纳入任何捕获率分析中是很重要的。我们还需要根据陷阱类型、目标动物和陷阱位置对数据进行分层。这是因为陷阱类型、动物行为和不同邻居的栖息地之间的差异可能会使分析结果产生偏差。我们缺乏关于有多少次陷阱没有捕捉到猎物,有多少次陷阱被检查后发现是空的,或者有多少次捕食者接*但没有与陷阱接触的数据。
这些限制使得不可能从公民科学数据中计算陷阱效率或遭遇概率。虽然其他对照研究解决了这些问题,但一些公民科学陷阱数据的优势在于它们跨越了更长的时间段。
我按陷阱类型检查了渔获量的时间趋势,以确定渔获量是否随时间而变化。在这个分析中,我假设捕获率的变化反映了目标动物当地密度的变化。如果捕获率最初很高,然后急剧下降,我们可以推断诱捕正在减少附*捕食者的数量。
通过计算捕获率,我们可以确定我们的邻居诱捕努力是否有效果。两种最常见的捕鼠器(DOC200s 和 A24s)的捕鼠率在捕鼠活动的第一年达到峰值(图 2)。2018 年后,捕获率下降,并围绕更低的值波动(图 2)。这表明诱捕在第一年降低了大鼠和小鼠的数量(图 2),随后随着我们诱捕努力的增加,数量仍然受到抑制(图 1)。我们还可以看到,A24 捕鼠器对大鼠的初始捕获率几乎比小鼠高一个数量级(图 2A & C)。大鼠的 DOC200 捕获率最初约为小鼠的四倍(图 2B 和 D)。
图 2:*四年来怀塔克里山脉 Huia 的大鼠、小鼠和刷尾负鼠的捕获率,说明在更积极的诱捕的第一年,大鼠和小鼠被击倒。A24 是 Goodnature A24 自动捕集阱,DOC200 是传统的盒装 snap 捕集阱。提姆是装在地上的,战士是装在树上的负鼠陷阱。(作者配图)
对于负鼠,我们没有观察到同样的击倒效果(图 2E 和 F)。负鼠的捕获率低于大白鼠或小白鼠,而且在使用捕鼠器的第一年也没有出现高峰。这可能与负鼠的地盘性有关。雄性比雌性活动范围更广,负鼠可能需要长达 8 周或更长时间才能重新占据先前居住者被困的栖息地。
比较传统陷阱和自动陷阱的捕获率
新技术有可能减少检查陷阱和更换诱饵的劳动。我们最常用的两种捕集阱是传统的 DOC200 盒式捕捉阱和 Goodnature A24 自动捕集阱。在一个一周检查几次陷阱的社区环境中,自动陷阱的捕获率是否高于 DOC200 等传统陷阱?或者,有证据表明自动陷阱不如传统陷阱有效吗?
我使用简单的探索性数据分析比较了 DOC200 和 A24 陷阱的捕获率。图 3 显示了散点图、箱线图和每种诱捕器的捕获率分布图,将大鼠和小鼠的捕获情况分开。数据来自单个相邻属性,以减少空间可变性。当没有发现尸体时,拾到的渔获记录了自动 A24 陷阱的攻击。这通常意味着在检查捕鼠器之前,捕获物被猫或老鼠移走了。
两种陷阱类型的捕获率非常相似(图 3)。这两种陷阱类型的数据是高度倾斜的,因为在某些月份捕获率要高得多。这在老鼠身上比在老鼠身上更明显。两种陷阱类型的中值(方框图中的水*线)非常接*,DOC200 和 A24 陷阱的四分位数范围(方框)重叠。我们没有发现自动 A24 捕集阱比传统 DOC200 捕集阱更有效或更无效的证据。这些发现表明,自动陷阱的好处可能主要来自这样一个事实,即它们不需要经常检查,减少了所涉及的劳动。
图 3:两种不同类型的捕鼠器(A24 为 Goodnature A24 自动捕鼠器,DOC200 为传统的盒装 snap 捕鼠器)对大鼠和小鼠的捕获率的比较。(作者配图)
人们可能不愿意采用新技术,并怀疑新方法行不通。A24 捕集阱的价格也是 DOC200s 的三倍左右。不经常检查陷阱的代价是,猎物更有可能被另一个捕食者吃掉,比如家猫或野猫、白鼬或另一只老鼠。节省劳动力的好处在一定程度上被降低的信心所抵消,因为人们对捕获是真实的,而不仅仅是不准确的捕杀计数器的人工制品。诱捕器的成本也是一个考虑因素,特别是对于邻里诱捕来说,劳动是自愿的,因此不需要成本,而昂贵的诱捕器的成本由邻居承担。
结论
我们可以回答“邻居陷阱有效吗?”,至少在我们的 Huia 附*。第二个问题的答案;“我们怎么会知道?”对像 Trap 这样的数据库中的公民科学数据的分析。NZ 或 CatchIT 可以提供证据。
从这种对单个诱捕组的有限分析中,证据表明我们的邻居诱捕对大鼠和小鼠的捕获率有影响,并由此推断对大鼠和小鼠的局部密度有影响。如果没有将数据输入数据库的捕捉器所提供的数据,这将很难检测到。另一种监测方法是使用鸟鸣监测器(见我的 TDS 文章),但这也需要公民科学家的努力。
邻里诱捕的另一个好处是社区为了一个共同的合作目标而参与进来。我们维护一个邮件列表,用于更新和信息传播给所有诱捕组成员。我们还有一个面向最活跃成员的 WhatsApp 群组,人们可以在这里提问并分享他们的经验。这是获得帮助的好方法,也有助于保持动力。你还可以了解你的邻居,加强合作。
密码
数据从陷阱中导出。作为 ascii 文件的 Huia 补漏白组的 NZ 站点。使用 R tidyverse 包,数据文件被导入,字符日期被转换为类日期,变量被排序、选择和重命名,随后的数据帧被转换为时间表。
## import_trapnz_function.R
# Usage:
# source the function
# trapnz <- import.trapnz()# Note: Data were exported from Trap.NZ using menus: Reports/ Trap reports/ Trap records for the full date rangeimport.trapnz <-
function(data.dir = "/mnt/data/dynamic_data/projects/projects2021/trapping/data/",
exported.file = "TrapNZ_export_29_Sept_2021.csv")
{
# load libraries
library(tidyverse)
library(tibbletime)
library(lubridate)
library(readr)
library(tidyr)
# load exported trap.nz data
dat <- paste(data.dir, exported.file, sep = "")
trapnz <- read_csv(dat)
# convert character date to a date object
trapnz <- mutate (trapnz,
Date.new = as.Date(Date, format = "%d %b %Y - %H:%M"))
# order, select and rename variables
trapnz <- arrange(trapnz, Date.new, "Recorded by", Trap)
trapnz <- trapnz %>%
select(Date.new, "Recorded by", Trap, "Trap type", Strikes, "Species caught")
colnames(trapnz) <-
list("date.new", "line", "trap", "traptype", "strikes", "species")
# convert tibble to tibble time
trapnz <- as_tbl_time(trapnz, index = date.new)
# print some summary info
print("########################")
print(paste("Directory: ", data.dir))
print(paste("Exported Trap.NZ file: ", exported.file))
# print(head(trapnz))
# print(tail(trapnz))
trapnz
}
我用下面的代码准备了数据(prepare _ TrapNZ _ catch _ and _ traptype _ data。r)。加载库之后,使用上面的函数(import_trapnz_function)加载数据。r),在使用 source()将其加载到环境中之后。根据整理数据的原则,从物种特征变量中创建新变量。自动捕鼠器的多次捕获,通常是在一个晚上杀死两只老鼠,是通过将捕获单位类别乘以攻击次数来计算的。在连续的季度(3 个月)期间,我按物种对捕获量求和,并计算每种类型的独特陷阱的数量。
## prepare_TrapNZ_catch_and_traptype_data.R# load libraries #################
library(tidyverse)
library(tibbletime)
library(lubridate)
library(readr)
library(tidyr)# load data ###########
### load, sort, subset trap.nz data
source(
"/mnt/data/dynamic_data/projects/projects2021/trapping/r/import_trapnz_function.R"
)
trapnz <- import.trapnz()# create new variables for catches by species ################
series2 <- trapnz
series2 <- mutate (
series2,
# Date.new = as.Date(Date, format = "%d %b %Y - %H:%M"),
rat_catch = ifelse (series2$species %in% c ("Rat", "Rat - Ship", "Rat - Norway"), 1, NA),
mouse_catch = ifelse (series2$species == "Mouse", 1, 0),
possum_catch = ifelse (series2$species == "Possum", 1, 0),
hedgehog_catch = ifelse (series2$species == "Hedgehog", 1, 0),
stoat_catch = ifelse (series2$species == "Stoat", 1, 0),
scavenged_catch = ifelse (series2$species == "Unspecified", 1, 0)
)
# adjust for multiple catches by A24 traps
series2$rat_catch <- series2$rat_catch * series2$strikes
series2$mouse_catch <- series2$mouse_catch * series2$strikes# quarterly summation of catches by species and number of unique traps
series2 <- arrange(series2, date.new, line, traptype, trap)# group catch data into time bins ########################
quarterly_catches <- collapse_by(series2, "quarter") %>%
dplyr::group_by(date.new, traptype) %>%
dplyr::summarise_if(is.numeric, sum, na.rm = TRUE)
quarterly_catches <- arrange(quarterly_catches, date.new, traptype)# group numbers of individual traps into time bins
quarterly_traptype <- collapse_by(series2, "quarter") %>%
dplyr::group_by(date.new, traptype) %>%
dplyr::summarise(trap_count = length(unique(trap)))
quarterly_traptype <- arrange(quarterly_traptype, date.new)# save the data ####################
Huia_results = list(quarterly_catches, quarterly_traptype)
save(Huia_results, file = "/mnt/data/dynamic_data/projects/projects2021/trapping/data/tidy_catch_traptype_TrapNZ.dat")
使用下面的代码(trapping _ effort _ over _ time _ stacked _ bar plot)将这些数据用于创建图 1。r)。加载库之后,我使用了几个主题选项来改善情节。上面的代码保存的数据(prepare _ TrapNZ _ catch _ and _ traptype _ data。r)已加载。然后,我使用 ggplot 绘制了一个堆积条形图,为分类数据手动指定了调色板,调整显示以显示倾斜的 x 轴标签,并显示每个堆积条形图段中陷阱数量的计数。
### trapping_effort_over_time_stacked_barplot.R# libraries #######
library(tidyverse)
library(tibbletime)
library(lubridate)
library(gridExtra)
library(ggpubr)
library(RColorBrewer)
library(colorspace)
# custom theme settings ##########
theme_smcc <- function() {
# theme_bw() +
theme(
text = element_text(family = "Helvetica Light"),
axis.text = element_text(size = 14),
axis.title = element_text(size = 14),
axis.line.x = element_line(color = "black"),
axis.line.y = element_line(color = "black"),
panel.border = element_blank(),
panel.grid.major.x = element_blank(),
panel.grid.minor.x = element_blank(),
panel.grid.minor.y = element_line(colour = "grey80"),
plot.margin = unit(c(1, 1, 1, 1), units = , "cm"),
plot.title = element_text(
size = 18,
vjust = 1,
hjust = 0.5
),
legend.text = element_text(size = 12),
legend.position = c(0.2, 0.6),
legend.key = element_blank(),
legend.background = element_rect(
color = "black",
fill = "transparent",
size = 2,
linetype = "blank"
)
)
}theme_angle_xaxis <- function() {
theme(axis.text.x = element_text(
angle = 45,
hjust = 1,
vjust = 0.95
))
}# load data ###################
load(file = "/mnt/data/dynamic_data/projects/projects2021/trapping/data/tidy_catch_traptype_TrapNZ.dat")quarterly_trap_type <- as.data.frame(Huia_results[2])
quarterly_trap_type <- arrange(traps, date.new, trap_count)# plot ###############
png(
"/mnt/data/dynamic_data/projects/projects2021/trapping/figures/trapping_effort_over_time_stacked_barplot.png",
width = 580,
height = 580
)quarterly_trap_type$date_char <-
as.character(quarterly_trap_type$date.new) # to allow bar width adjustmentmypalette <- brewer.pal(12, "Paired")
legend_title = "Trap type"trapping_effort_over_time_stacked_barplot <-
ggbarplot(
data = quarterly_trap_type,
x = "date_char",
xlab = "Date by quarters",
y = "trap_count",
ylab = "Trap count",
label = TRUE,
lab.pos = "in",
lab.col = "cyan1",
fill = "traptype",
title = "Huia trapping group effort"
) +
scale_fill_manual(legend_title, values = mypalette) +
theme_smcc() +
# x-axis adjustment
theme_angle_xaxis()# save the plot
print(trapping_effort_over_time_stacked_barplot)
dev.off()
图 2 中按陷阱类型和物种分列的捕获率时间序列如下。上面的代码保存的数据(prepare _ TrapNZ _ catch _ and _ traptype _ data。r)已加载。为了节省空间,我将加载库的代码段和之前显示的自定义主题折叠起来。在按渔获量/诱捕器/天计算渔获量比率之前,按诱捕器类型过滤季度渔获量和诱捕器数量。然后,在将它们组合成面板图之前,创建了六个单独的图。
## explore_multiple_catches.R# load libraries #######
...# custom theme settings #############
...# load data ###################
load(file = "/mnt/data/dynamic_data/projects/projects2021/trapping/data/tidy_catch_traptype_TrapNZ.dat")catches <- as.data.frame(Huia_results[1])
catches <- arrange(catches, date.new, traptype)traps <- as.data.frame(Huia_results[2])
traps <- arrange(traps, date.new, trap_count)# traps ######################
# filter by trap type
# A24
catches_a24 <- filter(catches, traptype == "A24")
traps_a24 <- filter(traps, traptype == "A24")catches_and_traps_a24 <-
select(catches_a24, c(date.new, traptype, rat_catch, mouse_catch))interval <- c(NA, diff(catches_and_traps_a24$date.new))catches_and_traps_a24 <- mutate(
catches_and_traps_a24,
trap_count = traps_a24$trap_count,
.after = traptype,
rat_catch_rate = rat_catch / trap_count / interval,
mouse_catch_rate = mouse_catch / trap_count / interval
)
head(catches_and_traps_a24)# DOC200
catches_doc200 <- filter(catches, traptype == "DOC 200")
traps_doc200 <- filter(traps, traptype == "DOC 200")catches_and_traps_doc200 <-
select(catches_doc200, c(date.new, traptype, rat_catch, mouse_catch))interval <- c(NA, diff(catches_and_traps_doc200$date.new))catches_and_traps_doc200 <- mutate(
catches_and_traps_doc200,
trap_count = traps_doc200$trap_count,
.after = traptype,
rat_catch_rate = rat_catch / trap_count / interval,
mouse_catch_rate = mouse_catch / trap_count / interval
)# Mouse trap
catches_mouse_trap <- filter(catches, traptype == "Mouse trap")
traps_mouse_trap <- filter(traps, traptype == "Mouse trap")catches_and_traps_mouse_trap <-
select(catches_mouse_trap, c(date.new, traptype, mouse_catch))interval <- c(NA, diff(catches_and_traps_mouse_trap$date.new))catches_and_traps_mouse_trap <- mutate(
catches_and_traps_mouse_trap,
trap_count = traps_mouse_trap$trap_count,
.after = traptype,
mouse_catch_rate = mouse_catch / trap_count / interval
)# Timms trap
catches_timms_trap <- filter(catches, traptype == "Timms")
traps_timms_trap <- filter(traps, traptype == "Timms")catches_and_traps_timms_trap <-
select(catches_timms_trap, c(date.new, traptype, possum_catch))interval <- c(NA, diff(catches_and_traps_timms_trap$date.new))catches_and_traps_timms_trap <- mutate(
catches_and_traps_timms_trap,
trap_count = traps_timms_trap$trap_count,
.after = traptype,
possum_catch_rate = possum_catch / trap_count / interval
)# Warrior trap
catches_warrior_trap <- filter(catches, traptype == "Warrior")
traps_warrior_trap <- filter(traps, traptype == "Warrior")catches_and_traps_warrior_trap <-
select(catches_warrior_trap, c(date.new, traptype, possum_catch))interval <- c(NA, diff(catches_and_traps_warrior_trap$date.new))catches_and_traps_warrior_trap <-
mutate(
catches_and_traps_warrior_trap,
trap_count = traps_warrior_trap$trap_count,
.after = traptype,
possum_catch_rate = possum_catch / trap_count / interval
)# plots ############################
png(
"/mnt/data/dynamic_data/projects/projects2021/trapping/figures/explore_multiple_catches.png",
width = 480,
height = 720,
)a24_rats <-
ggline(
catches_and_traps_a24,
x = "date.new",
y = "rat_catch_rate",
title = "A24 rats",
size = 1,
# add = "loess",
conf.int = TRUE,
xlab = "Date",
ylab = "Catch/ trap/ day"
) +
theme_smcc() +
theme(axis.text.x = element_text(
angle = 45,
hjust = 0.5,
vjust = 0.5
))
a24_rats <- ggpar(a24_rats, ylim = c(0, 0.4))doc200_rats <-
ggline(
catches_and_traps_doc200,
x = "date.new",
y = "rat_catch_rate",
title = "DOC200 rats",
size = 1,
# add = "loess",
conf.int = TRUE,
xlab = "Date",
ylab = "Catch/ trap/ day"
) +
theme_smcc() +
theme(axis.text.x = element_text(
angle = 45,
hjust = 0.5,
vjust = 0.5
))
doc200_rats <- ggpar(doc200_rats, ylim = c(0, 0.4))a24_mouse <-
ggline(
catches_and_traps_a24,
x = "date.new",
y = "mouse_catch_rate",
title = "A24 mice",
size = 1,
# add = "loess",
conf.int = TRUE,
xlab = "Date",
ylab = "Catch/ trap/ day"
) +
theme_smcc() +
theme(axis.text.x = element_text(
angle = 45,
hjust = 0.5,
vjust = 0.5
))
a24_mouse <- ggpar(a24_mouse, ylim = c(0, 0.1))doc200_mouse <-
ggline(
catches_and_traps_doc200,
x = "date.new",
y = "mouse_catch_rate",
title = "DOC200 mice",
size = 1,
# add = "loess",
conf.int = TRUE,
xlab = "Date",
ylab = "Catch/ trap/ day"
) +
theme_smcc() +
theme(axis.text.x = element_text(
angle = 45,
hjust = 0.5,
vjust = 0.5
))
doc200_mouse <- ggpar(doc200_mouse, ylim = c(0, 0.1))timms_possum <-
ggline(
catches_and_traps_timms_trap,
x = "date.new",
y = "possum_catch_rate",
title = "Timms possums",
size = 1,
# add = "loess",
conf.int = TRUE,
xlab = "Date",
ylab = "Catch/ trap/ day"
) +
theme_smcc() +
theme(axis.text.x = element_text(
angle = 45,
hjust = 0.5,
vjust = 0.5
))
timms_possum <- ggpar(timms_possum, ylim = c(0, 0.05))warrior_possum <-
ggline(
catches_and_traps_warrior_trap,
x = "date.new",
y = "possum_catch_rate",
title = "Warrior possums",
size = 1,
# add = "loess",
conf.int = TRUE,
xlab = "Date",
ylab = "Catch/ trap/ day"
) +
theme_smcc() +
theme(axis.text.x = element_text(
angle = 45,
hjust = 0.5,
vjust = 0.5
))
warrior_possum <- ggpar(warrior_possum, ylim = c(0, 0.05))# panel plot
explore_multiple_catches <-
ggarrange(
a24_rats,
doc200_rats,
a24_mouse,
doc200_mouse,
timms_possum,
warrior_possum,
labels = c("A", "B", "C", "D", "E", "F"),
ncol = 2,
nrow = 3
)print(explore_multiple_catches)
dev.off()
对于最后一个图,数据准备涉及已经包括的内容,例如加载数据,按陷阱类型子集化以选择 A24 和 DOC200 陷阱,以及在本例中按月而不是按季度对捕获量进行分组。唯一新颖的部分是为雨云情节创建阵列。
# # prepare data format for raincloud plot (packing doc200 arrays to match A24 length)
df_plot <- data_2x2(
array_1 = c(gdata_doc200$rat_catch_eff, NA, NA, NA),
array_2 = gdata_a24$rat_catch_eff,
array_3 = c(gdata_doc200$rats_plus_scavenged_catch_eff, NA, NA, NA),
array_4 = gdata_a24$rats_plus_scavenged_catch_eff,
array_5 = c(gdata_doc200$mouse_catch_eff, NA, NA, NA),
array_6 = gdata_a24$mouse_catch_eff,
labels = c("DOC200", "A24"),
jit_distance = 0.075,
jit_seed = 321,
spread_x_ticks = TRUE
)# save the data for raincloud plot
save(df_plot, file = "/mnt/data/dynamic_data/projects/projects2021/trapping/data/tidy_catch_raincloud.dat")
图 3 中的 raincloud 是使用之前保存的数据通过这段代码创建的。
## compare_catch_rates_A24_DOC200_by_raincloud.R# load libraries
library(tidyverse)
library(tibbletime)
library(lubridate)
library(gridExtra)
library(ggthemes)
library(ggpubr)
library(ggrepel)
library(readr)
library(raincloudplots)# custom theme settings
...# load saved tidy_catch_raincloud data
load(file = "/mnt/data/dynamic_data/projects/projects2021/trapping/data/tidy_catch_raincloud.dat")# raincloud plots
# colours
col1 = "aquamarine"
col2 = "darkseagreen"
col3 = "darkcyan"
col4 = "chartreuse3"
col5 = "chartreuse"plot_catches <- raincloud_2x3_repmes(
data = df_plot,
colors = (c(col1, col2, col1, col3, col4, col5)),
fills = (c(col1, col2, col1, col3, col4, col5)),
size = 2.5,
# data dot size
# line_alpha = 0, # make lines fully transparent
alpha = 0.6 # transparency
) +
scale_x_continuous(
breaks = c(1.25, 1.4, 2.25, 2.4, 3.25, 3.4),
labels = c("D", "A", "D", "A", "D", "A"),
# labels = c("DOCC200", "A24", "DOCC200", "A24", "DOCC200", "A24"),
limits = c(1, 4.2)
) +
# scale_y_continuous() +
scale_y_continuous(limits = c(0, 0.62)) +
xlab("Groups") +
ylab("Catch rates (catches/ trap/ day)") +
labs(title = "Compare catch rates between DOC200 (D) and A24 (A) traps") +annotate(geom = "text",
label = "Rats",
x = 1.3, y = 0.5,
size = 6) +
annotate(geom = "text",
label = "Rats + scavenged",
x = 2.5, y = 0.6,
size = 6) +
annotate(geom = "text",
label = "Mice",
x = 3.5, y = 0.3,
size = 6) +
theme_smcc()plot_catches
下载访问
代码、数据和图表都可以通过这个共享链接下载。
媒体现在只报道 COVID19 疫情了吗?
奥地利在线新闻报道的数据分析
过去三个月的报道/作者提供的图片
由于 COVID19 疫情的广泛报道,很容易让人产生没有其他报道的印象。在这篇文章中,我想在数据的基础上检验这种主观印象是否符合现实。我还将看看不同主题的报道在过去几个月里是如何发展的。还将讨论区域差异和新闻报道的基调,即新闻报道是积极的还是消极的。
数据
为了进行分析,我收集并评估了 2020 年 11 月 4 日至 2021 年 2 月 2 日奥地利媒体在线发表的总共 148,991 篇文章。
考虑了来自以下媒体的文章:
-ORF/http://ORF . at
-泰洛勒日报/【http://tt.com】
-上奥地利新闻/http://nachrichten . at
-下奥地利新闻/http://noen . at
-福拉尔贝格在线/ http://vol.at
-斯特赖希 http://diepresse.com
-Kronen Zeitung/http://krone . at
-Der Standard/http://Der Standard . at
-Heute/http://Heute . at
-Neues Volksblatt/https://Volksblatt . at
-维纳 Zeitung/http://t
这几乎完整地描述了奥地利媒体在日常新闻方面的情况。(不幸的是,我无法获得“Kleine Zeitung”/的数据。
分析
每篇文章的标题、出版文本和出版商都与出版日期一起存储。我的数据集中的文章数量在发布服务器之间的分布如下:
按作者列出的每个出版商/图片的文章数量
文章搜索了与疫情报道相关的特定关键词。例如,术语“covid”、“corona”以及“kurzarbeit”、“lockdown”和“mutation”这些帖子还接受了情绪分析,并注明是正面、中立还是负面的报道。
“corona”或“covid”这两个词在文章中出现的比例是多少?
如果我们观察文本中至少有两个关键词之一的文章的比例,在每种情况下按天进行总结,并绘制在时间轴上,当我们总结所有媒体时,会出现下面的图片。
一段时间内“covid”文章的百分比/按作者分类的图片
因此,在整个期间,大约 30-40%的报告是关于这个主题的。这证实了人们的印象,即“只有”关于疫情的报道,虽然不是字面上的,但已经是非常大的比例了。这在整个 3 个月的时间里几乎是不变的。
关于出版商的分歧呢?
一些出版商的“covid”文章百分比/按作者分类的图片
这里已经存在明显的差异,oe24 .几乎达到 50%,“Volksblatt”甚至远高于此,而“Krone”和“Kurier”仅达到 30%或更低。
“突变”的存在呢?
为了做到这一点,我们查看了文本中带有“突变”一词的文章的绝对数量,并绘制了时间图。
一段时间内带有“突变”一词的文章数量/作者图片
这个话题在圣诞节前突然出现在新闻中,然后在一段时间内又显著下降。它从一月中旬开始回升,并一直伴随着我们。如果我们单独看一下地方报纸上的报道,就会发现一个更加不同的画面。
某些出版商的带有“突变”一词的文章数量/按作者分类的图片
在圣诞节,这个话题已经在西方报纸《泰洛勒日报》和《萨尔茨堡新闻报》上出现。在上奥地利(“nachrichten.at”),要到 1 月中旬。这表明了“东西方的分歧”。
关于封锁的问题呢?
下图显示了出现“锁定”一词的文章的绝对数量,按来源分列。
一段时间内带有“锁定”一词的文章数量/作者图片
可以看出,在第一次封锁的当天,在报告中也有一个明显的“峰值”。圣诞节假期后开始的封锁没有显示出明显的高峰。然而,总的来说,该主题以某一周为周期贯穿整个时期。在每种情况下,“OE24.at”(绿色)对峰有很大贡献。
文章的调性(情调)怎么样?
在疫情的主题领域,人们会期待负面的报道。让我们用数字来看整个事情,并且也根据不同的术语来分解时间过程。
作者对不同术语/图像的文章的情感
该图显示了包含相应术语的文章数量的时间顺序,并按音调进行了分解。关于这些话题的正面文章数量之少令人吃惊,这是可以理解的。特别是,关于“突变”和“锁定”的文章主要带有负面情绪。其他两个术语至少有与负面新闻几乎相似的中性报道比例。
该分析通过具体数字表达了我们在日常新闻消费中的感受,其中很大一部分涉及疫情,而且大多带有负面语气。然而,我们也可以看到一些不同的来源,主题领域和时间段。
我其他一些关于媒体报道的文章:
https://medium.com/datadriveninvestor/topics-in-online-news-before-the-austrian-elections-2019-d981facbd0dd https://medium.com/datadriveninvestor/on-the-media-coverage-of-political-parties-e1b1933f0810 https://medium.com/datadriveninvestor/newstrace-a-method-to-analyze-the-impact-of-news-articles-on-stock-prices-2e866c16a9f
你的 A/B 考试是否遭遇了分配失衡?
来源:https://burst.shopify.com/photos/scales-of-justice?q=scale
最*,我的团队进行了一项实验来测试一种新产品推荐算法的性能。在收集了几周的数据后,看起来我们的新算法获得了巨大的成功;这个实验将我们的主要成功指标(每个用户的订单数)提高了 3%。这相当于我们团队在整个上半场的目标。
这些结果好得令人难以置信。我们正要在每周实验回顾中宣布这些结果的成功,这时我们决定暂停一下,再一次检查实验的基本原理。下面是我们看到的关于用户分配的情况:
- 控制:7,018,463 个用户
- 待遇:7 008 472 名用户
与对照组相比,处理组(新产品推荐算法)分配的用户少了 9,991 个。所以我们开始回答两个问题:
1.这种差异可以用随机抽样变异来解释吗?
2.如果不是,是什么导致了偏差?
观察到的差异可以用随机抽样变异来解释吗?
这个问题相当于检查一个硬币是否公*。考虑以下场景——我们投掷硬币 1000 次,观察以下结果:
- 人头:465
- 反面:535
用一枚公*硬币观察到这个结果(相差-70)的概率是多少?我们可以通过构造一个抽样分布来回答这个问题。在 R 中,我们可以通过模拟创建这个分布:
这会产生以下结果:
并且采样分布简单地是增量值的密度图:
模拟分布
原来,标准误差( SE )由下式给出:
其中:
- n=每个样本的观察总数(1000)
- p =正面概率
代入这些值,我们得到 SE = 31.62。既然我们知道了如何计算 SE ,我们就可以不用模拟来重新创建抽样分布了。我们简单地构造一个均值= 0,标准差= 31.62 的正态分布:
计算分布(红色)与模拟分布(黑色)
回到我们的问题,如果我们掷一枚硬币 1000 次,那么观察到 465 次正面和 535 次反面的概率是多少?
如果我们将观察到的-70 的δ除以 SE ,那么我们可以利用标准正态分布来回答我们的问题,如下所示:
这产生了:
所以如果硬币是公*的,只有 2.69%的机会观察到这个结果。
类似地,回到我们最初的问题,给定 50/50 的实验分割,那么观察到的概率是多少:
- 控制:7,018,463 个用户
- 待遇:7 008 472 名用户
我们以同样的方式回答这个问题:
这产生了:
所以只有 0.76%的机会观察到这个结果,如果事实上实验任务是无偏的。
是什么导致了这种偏见?
仅仅随机抽样的变化不可能解释观察到的用户分配的差异:
- 控制:7,018,463 个用户
- 待遇:7 008 472 名用户
我们将这一发现带给了设置实验的机器学习工程师,并要求他们重新检查分配标准。在深入研究代码之后,他们意识到偏见确实被引入了:
- 控制:所有登录的用户都被分配
- 处理:所有登录并在过去 180 天内至少有 2 个订单的用户都被分配
因此,治疗有一个额外的分配标准,意味着我们分配的用户比对照组少。此外,与对照组相比,被分配到治疗组的用户更有可能参与其中。因此,我们的主要成功指标(每用户订单数)显示,治疗组比对照组好得多。
摘要
最后,我们简单地重新开始了实验,这次对两个变量使用了相同的赋值标准。我们现在也监控所有实验中作业数量的差异。使用本文概述的方法,您也可以做到!😃
你的电脑理解你吗?
思想和理论
用上下文森林模型消除语言歧义
对话中的消歧过程几乎和说话本身一样自然。在许多情况下,人类的语言会令人惊讶地含糊不清,然而我们却可以非常轻松地成功地相互交流,而不用特意去思考消除歧义的词语。在过去的五年里,科学界已经将计算语言学模型的能力推向了一个无人能想象的水*。然而,尽管现代计算机拥有强大的计算能力,但它们明白自己在说什么吗?
这篇文章解释了上下文森林背后的主要思想,这是我在我的学士论文期间开发的无监督语言消歧模型。该模型在 Python 中的实现可在 Github 上获得。
像许多其他 NLP(自然语言处理)模型一样,上下文森林在一个单词(即句子中的其他单词)的上下文可以完全确定其含义的假设下运行。考虑到本质上这是我们人类相互交流时所做的事情,这一假设似乎是合理的。
不幸的是,在许多情况下,即使使用最先进的技术,也很难弄清楚这种情况。这个困难存在于上下文所具有的基于知识的成分中;让我们用一个例子来说明这一点。考虑下面的句子:
“最佳女王歌曲重新定义了摇滚。”
现在让我们把注意力集中在女王和摇滚这两个词上。对于我们人类来说,如果熟悉 70 年代的摇滚,这是一个直截了当和几乎自动的过程,以承认我们在谈论英国摇滚乐队皇后和摇滚是一种音乐流派。相反,对于一个语言模型来说,这可能会呈现出一个复杂得多的情况,因为作为独立的单词, Queen 和 rock 可以指代相当多的不同事物(例如,一个女君主和一个固体矿物集合体)。
那么语言模型是如何处理这个问题的呢?其中一个关键点就是这些词一起出现在同一个句子里。从概率上来说,在例句中,我可能在谈论英国的女王伊丽莎白二世,但这似乎不太可能,因为我也在谈论由单词 rock 代表的一些概念,这些概念在统计上与女王伊丽莎白二世的上下文没有关联。
从这一点来看,很自然会想知道语言模型是如何分配这些概率和关联统计的。根据对这个问题的回答,我们可以将语言模型大致分为两种类型:
- 上下文无关模型,如 Word2Vec 或 GloVe ,是基于在单词和向量之间创建 1:1 的映射(通常称为单词嵌入)。尽管实现中的细节可能不同,但该模型的总体思想在于在大型文本语料库上训练神经网络,以获得捕捉语义属性的表示。比如我们考虑向量 k 、 w 、 m 分别对应“王”、“女”、“男”三个词。那么向量q=(k—m)+w与赋给“皇后”这个词的向量非常接*。这些代数性质在许多 NLP 问题中是期望的,但是用这些模型解决 WSD(词义消歧)问题是复杂的,因为每个单词的嵌入是唯一的并且独立于上下文。
手套嵌入表示(图片由作者提供)
- 像埃尔莫、伯特和罗伯塔这样的动态嵌入模型基于一种叫做“变形金刚”的架构,自从它们在 2018 年左右首次出现以来,它们一直处于 NLP 世界的顶端。这些模型与上下文无关模型的区别在于嵌入关联过程。动态模型将根据上下文中的其他单词分配嵌入,而不是固定的嵌入,这意味着同一个单词可以根据句子有不同的嵌入。他们还需要一个大规模的训练语料库和比上下文无关模型大得多的计算能力,但另一方面,WSD 问题的结果要好得多。
不同上下文中“绿色”的 BERT 嵌入的相似性(图片由作者提供)
尽管动态嵌入模型在 WSD 问题中具有明显更好的性能,但是它们在训练过程中与上下文无关模型共享一个关键方面;他们都需要大量的文本语料库。拥有这个巨大的训练语料库意味着,有了足够的计算能力和一些花哨的架构,人们可以完全基于从训练数据中推断的统计数据建立一个相当不错的模型,而无需理解语言所代表的概念。
那么,当我们面对最先进的模型时,我们在看什么呢?语言理解还是统计推断?答案介于两者之间;很明显,语言模型已经掌握了语言的句法规则,但要理解复杂语义环境中的微妙之处,它们还有很长的路要走(参见和 Kao 的文章)。
在思考人们如何在日常对话中解决 WSD 问题时,我得出结论,消除歧义不可能是一个基于记忆的过程。在前面的例子中,我们不知道 Queen 是英国摇滚乐队,因为一天晚上,当我们在酒吧讨论音乐时,我们听到一个朋友的朋友在同一个句子中使用了 Queen 和 rock 这两个词。我们知道 Queen 是一个英国摇滚乐队,而 rock 是一个音乐流派,因为从所有的可能性来看,这两个词的含义是考虑到句子中其余单词的最一致的选项。具体来说,当我们读到前三个词(最佳皇后)时,直到下一个词,在我们的脑海中,皇后可以意味着许多事情,但当我们读到单词歌曲时,我们知道我们在谈论音乐,所以这个皇后一定是摇滚乐队。这种关联正是上下文森林模型背后的思想。
上下文森林
所以我想通过语义联系建立一个消歧系统,使用一个句子中单词的不同可能上下文。我认为使用上下文来建立联系以找到单词含义之间的共同点听起来很像在图中搜索算法中的跨越节点,所以我决定将这个消歧过程建模为不同的树(一个树代表一种可能的含义)试图彼此建立联系。给定一个初始单词,我需要以类似于图表的结构组织的可能含义的上下文,以使模型工作,所以我决定使用维基百科。遗憾的是,维基百科只提供关于名词(物体、人、事件等)的文章。),所以由于这种僵局和时间限制,上下文森林的实现版本只对消除名词歧义有效。然而,如果找到另一种资源来覆盖更多的单词,这个过程是很容易扩展的。
背景森林理念(作者图片)
它是如何工作的?
首先,在确定一个句子中的名词后,有必要为每个名词找出所有可能的含义。这个要求可能是一个问题,因为许多单词可能有与历史事件或歌曲相关的含义,而这些含义不会出现在标准词典中。幸运的是,维基百科有专门为这项任务设计的页面:
女王(消歧)维基百科页面(截图来自维基百科)
值得一提的是,一个普通的维基百科页面有相对大量的链接,递归扩展会导致计算上不可行的搜索问题。出于这个原因,算法需要的下一件事是一个“相关性函数”,它可以评估扩展哪些链接以快速找到树之间的联系(在计算机科学中,这被称为启发式函数)。这个启发式函数需要表示两篇维基百科文章有多接*。起初,我以为只是找到页面之间的共享链接,但事实证明,两个完全不相关的维基百科页面共享一些链接并不罕见:
维基百科页面“土豆”和“微软”之间的常见链接(作者要点)
这个想法的基础是正确的,但需要一些改进。不考虑所有的链接,人们可以通过只考虑相关链接进行计算来更好地捕捉两个维基百科页面的相似性。
那么我们如何定义一个相关的链接呢?为了弄清楚这一点,我们需要确定哪些词在维基百科页面上是语义相关的。我将相关单词与那些均匀分布在文本中的单词区分开来(注意,相关单词不一定意味着非常频繁的单词)。
单词相关性示例(图片由作者提供)
之后,我将链接相关性定义为组成链接标题的单词的*均相关性。这种方法被证明作为相关性度量是无效的,因为使用非加权*均值可能对离群值非常敏感,导致偏向于将最相关的词作为标题的一部分的链接。为了解决这个问题,我研究了相关性分数的分布,我意识到它可以*似为 Zipf 分布。
符合 Zipf 分布(图片由作者提供)
我没有将链接相关性计算为简单的*均值,而是将其定义为排名位置*均值的逆图像,捕捉得分值的递减因子以纠正偏差。
史蒂夫·乔布斯维基百科页面中“苹果商店”链接的相关性(图片由作者提供)
最后,通过使用树,我们有了所有必要的工具来用这种非监督技术消除上下文的歧义
结论
请注意,该算法远非完美,有时可能无法消除一些单词的歧义。尽管如此,这些结果证明了一种潜在的替代方法,通过挖掘基于图的结构(知识图)中的特定信息并模拟推理过程,而不是用数百万个示例进行训练并从中学习消歧所需的统计信息,来成功地消除上下文歧义。在我看来,如果有一天我们想要建造最终能像人类一样推理的模型,那么这种致力于模型如何学习而不是模型能力有多大的想法是值得考虑的。**
感谢您的阅读,请随时提问!
事半功倍:生物医学命名实体识别案例研究
疾控中心在 unsplash 上拍摄的照片
思想和理论
一个简单的字符串匹配方法能和一个大的监督模型竞争吗?
在任何新的 R&D 项目开始时,我都会寻找最合适的解决方案来解决手头的问题。虽然有一些非常令人兴奋的大型模型可用,但我最大的担忧之一是将解决方案投入生产,而不影响我想要支持的结果。我最*的一个问题是,在生物医学命名实体识别(NER)任务中,一种简单的字符串匹配方法——加上一些简单的优化——是否可以与监督模型竞争。我已经将这两种方法进行了面对面的测试。
在字符串匹配系统方面,我利用了 QuickUMLS 分类器。QuickUMLS【1】—作为字符串匹配系统—将字符串作为输入(例如,包含医学概念的论文或论文摘要),并输出文档中匹配统一医学语言系统(UMLS)概念的所有范围。然后,这些概念可以在其他设置中重用,或者作为其他机器学习系统的输入。出于这个原因,QuickUMLS 可以被看作是一个方便的预处理工具,用于从临床和生物医学文本中获取相关概念。然而,在这篇博文中,我们将重点关注使用 QuickUMLS 作为具有挑战性的 MedMentions 数据集上的分类器。【2】
图一。QuickUMLS 工作原理的示意图。给定一个字符串,一个 UMLS 数据库转换成一个 simstring 数据库,该模型返回最佳匹配,它们的概念 id 和语义类型。作者图。
关于生物医学 NER 需要了解的一些关键事项
在我们深入到我们试图解决的问题之前,描述一下生物医学 NER 的一些特质是有用的。一般来说,NER 的问题是找到命名实体(例如,著名的地点、人、组织等。)在正文里。正如您可能猜测的那样,许多这些实体可以通过上下文找到。例如,在“山姆和德鲁去了罗马圆形大剧场”这样的句子中,我们可以推断罗马圆形大剧场是一个地点,因为你通常会去地点。类似地,我们也可以推测“Sam”是一个专有名词,因为处于“to go”主语位置的非常用词往往是人名。
相比之下,生物医学 NER 是关于从文本中发现和消除感兴趣的生物医学术语,如疾病、药物名称,以及通用术语,如“医院”、“ICU 病房”或“酒精”。这是一个重要的区别,因为很少有上下文信息来决定一个给定的词是否具有医学重要性。举一个有趣的例子,考虑“病人喝了很多酒”这句话中的“酒精”这个词。这一发现的严重程度取决于它是指酒精如啤酒或葡萄酒,还是纯酒精,如外用酒精。要更全面地了解 NER 生物医学的艺术水*,请看我在更苗条的人工智能公司的同事西布伦·詹森的博客文章。
在没有大量训练数据的情况下,很难了解哪些概念具有医学重要性,而训练数据通常不容易获得。因此,许多系统使用统一医学语言系统(UMLS),这是一个包含许多不同概念及其字符串表示和其他信息的大型本体。请注意,概念不同于字符串,因为许多字符串可以引用多个概念。例如,字符串“酒精”可以指外用酒精或酒精饮料。
在 UMLS 中,每个概念都由一个概念唯一标识符(CUI)和一个语义类型(STY)来描述,CUI 是任何给定唯一概念的符号 ID,STY 是将具有相似特征的概念分组的族标识符。UMLS 有用但也很难使用的一个原因是它巨大的体积;UMLS 的 2020AB 版本,也就是我们将在下面使用的版本,有超过 300 万个独特的英语概念。这些概念的相当大一部分不太可能出现,即使是在大型的带注释的数据集中。
利用医疗提及数据集
一个这样的数据集是 MedMentions。由 2016 年的 4392 篇 Pubmed 论文(标题和摘要)组成;用来自 UMLS 的 352K 个概念(CUI IDs)和语义类型进行注释。这些论文有大约 34K 个带注释的独特概念,这仍然只占 UMLS 中概念总数的大约 1%。这表明注释 UMLS 提及是一项具有挑战性的任务,不一定能使用监督机器学习来解决。
在这方面特别感兴趣的是,MedMentions 语料库包括测试集中不在训练中出现的 Cui。然而,总的来说,通过使用 UMLS 概念的语义类型作为标签,该任务仍然被视为受监督的机器学习任务。由于 UMLS 有 127 种语义类型,这仍然导致了很大的标签空间。MedMentions 数据集还有一个较小的版本,即 st21pv 数据集,它由与常规数据集相同的文档组成,但只注释了 21 种最常见的语义类型。
一个半马尔可夫基线在实体层面上得到了大约 45.3 的 F 分。【2】测试了其他方法,包括 blue Bert【3】和 BioBERT【4】,并使用实体级别的精确匹配将分数提高到 56.3。【5】注意,所有这些方法都是受监督的,因此在概念上依赖于训练集和测试集之间的一定量的重叠。如果一个概念或标签在训练中从未出现过,有监督的机器学习方法将很难对其进行正确分类。在下文中,我们将使用 MedMentions 数据集的语义类型作为标签。
QuickUMLS:无监督和基于知识的
与 BERT 相比,QuickUMLS 本质上是一种无监督的方法,这意味着它不依赖于训练数据。更准确地说,QuickUMLS 是一种基于知识的方法。这意味着该模型依赖于外部知识库来预测标签,而不是用参数来告诉它预测什么。这意味着两件事:
- 模型的质量受到知识库质量的限制。该模型无法预测知识库中不包含的内容。
- 该模型可以超越带注释的数据进行归纳。一般来说,在训练期间没有看到特定标签的监督模型不能准确地预测这些事情。这个规则的一个例外是零射击学习方法。
根据这两个事实,我们认为基于知识的方法非常适合 MedMentions 数据集。关于第一点,MedMentions 数据库是使用 UMLS 概念注释的,因此知识库和数据集之间的映射是精确的映射。关于第二点,MedMentions 数据集包含训练集中不存在的测试中的概念。
QuickUMLS 模型架构
作为一个模型,QuickUMLS 很简单。它首先使用解析器 spacy 解析文本。然后,该模型基于词性标签模式和停用词表来选择单词 ngrams,即单词的连续序列。简而言之,这意味着如果某些单词包含不需要的标记和标点符号,模型将丢弃它们。这些规则的细节可以在原始文件中找到。【1】在选择了所有候选单词 ngram 之后,在整个 UMLS 数据库中查询与单词 ngram 部分匹配的概念。因为在如此庞大的数据库上进行精确匹配是低效和困难的,所以作者使用 simstring 执行*似的字符串匹配。【6】当给定一个文本时,QuickUMLS 返回 UMLS 中的概念列表,以及它们与查询字符串的相似性和其他相关信息。例如,文本“患者有出血”使用(默认)字符串相似性阈值 0.7 返回以下候选项:
对于单词 patient:
{‘term’: ‘Inpatient’, ‘cui’: ‘C1548438’, ‘similarity’: 0.71, ‘semtypes’: {‘T078’}, ‘preferred’: 1},
{‘term’: ‘Inpatient’, ‘cui’: ‘C1549404’, ‘similarity’: 0.71, ‘semtypes’: {‘T078’}, ‘preferred’: 1},
{‘term’: ‘Inpatient’, ‘cui’: ‘C1555324’, ‘similarity’: 0.71, ‘semtypes’: {‘T058’}, ‘preferred’: 1},
{‘term’: ‘*^patient’, ‘cui’: ‘C0030705’, ‘similarity’: 0.71, ‘semtypes’: {‘T101’}, ‘preferred’: 1},
{‘term’: ‘patient’, ‘cui’: ‘C0030705’, ‘similarity’: 1.0, ‘semtypes’: {‘T101’}, ‘preferred’: 0},
{‘term’: ‘inpatient’, ‘cui’: ‘C0021562’, ‘similarity’: 0.71, ‘semtypes’: {‘T101’}, ‘preferred’: 0}
对于出血这个词:
{‘term’: ‘No hemorrhage’, ‘cui’: ‘C1861265’, ‘similarity’: 0.72, ‘semtypes’: {‘T033’}, ‘preferred’: 1},
{‘term’: ‘hemorrhagin’, ‘cui’: ‘C0121419’, ‘similarity’: 0.7, ‘semtypes’: {‘T116’, ‘T126’}, ‘preferred’: 1},
{‘term’: ‘hemorrhagic’, ‘cui’: ‘C0333275’, ‘similarity’: 0.7, ‘semtypes’: {‘T080’}, ‘preferred’: 1},
{‘term’: ‘hemorrhage’, ‘cui’: ‘C0019080’, ‘similarity’: 1.0, ‘semtypes’: {‘T046’}, ‘preferred’: 0},
{‘term’: ‘GI hemorrhage’, ‘cui’: ‘C0017181’, ‘similarity’: 0.72, ‘semtypes’: {‘T046’}, ‘preferred’: 0},
{‘term’: ‘Hemorrhages’, ‘cui’: ‘C0019080’, ‘similarity’: 0.7, ‘semtypes’: {‘T046’}, ‘preferred’: 0}
如您所见,单词“patient”有三个匹配的正确语义类型(T101),两个匹配的正确概念(C0030705)。出血一词也有多余的匹配,包括“不出血”的概念。然而,如果我们按照相似性来看,排名最高的候选人在两种情况下都是正确的。
在 QuickUMLS 的默认应用程序中,我们只保留首选项,即 preferred 为 1 的项,然后按相似性排序。然后,我们将排名最高的候选项的语义类型(即 semtype)作为预测——我们称之为基线模型。我们使用了 seqeval 和一个严格的匹配范例,与之前的工作相当。【5】
╔═══╦══════╦═══════╗
║ ║ BERT ║ QUMLS ║
╠═══╬══════╬═══════╣
║ P ║ .53 ║ .27 ║
║ R ║ .58 ║ .36 ║
║ F ║ .56 ║ .31 ║
╚═══╩══════╩═══════╝
Table 1: baseline performance
没什么印象,对吧?不幸的是,基线并没有为特定的任务进行优化。因此,让我们使用简单的试探法来优化基线。
用一些简单的优化改进 QuickUMLS
除了最初的性能之外,还有几种方法可以改进 QuickUMLS。首先,我们注意到 QuickUMLS 使用的标准解析器是默认的 spacy 模型,即 en_core_web_sm 。鉴于我们正在处理生物医学文本,我们最好使用生物医学语言模型。在我们的例子中,我们将模型换成了 scispacy【7】模型, en_core_sci_sm 。这已经稍微提高了性能,而且没有任何成本。
╔═══╦══════╦═══════╦═════════╗
║ ║ BERT ║ QUMLS ║ + Spacy ║
╠═══╬══════╬═══════╬═════════╣
║ P ║ .53 ║ .27 ║ .29 ║
║ R ║ .58 ║ .36 ║ .37 ║
║ F ║ .56 ║ .31 ║ .32 ║
╚═══╩══════╩═══════╩═════════╝
Table 2: adding scispacy
通过使用来自训练语料库的一些信息,可以获得其他改进。虽然这确实将 QuickUMLS 从一个纯无监督的方法变成了一个有监督的方法,但是仍然不依赖于大量的特定注释。换句话说,在特定的语料库上没有明确的“适合”步骤:我们将要进行的改进也可以使用一小组注释或医生的先验知识来估计。
优化 QuickUMLS 阈值
QuickUMLS 的默认设置包括阈值 0.7 和一组指标。该度量决定了如何计算字符串相似性,可以设置为“Jaccard”、“余弦”、“重叠”和“骰子”。我们对指标和不同的阈值进行网格搜索。最好的结果是 0.99 的阈值,这意味着我们只使用 SimString 和“Jaccard”度量来执行精确匹配,这在速度和分数方面优于所有其他选项。如你所见,我们离伯特的表现越来越*了。
╔═══╦══════╦═══════╦═════════╦════════╗
║ ║ BERT ║ QUMLS ║ + Spacy ║ + Grid ║
╠═══╬══════╬═══════╬═════════╬════════╣
║ P ║ .53 ║ .27 ║ .29 ║ .37 ║
║ R ║ .58 ║ .36 ║ .37 ║ .37 ║
║ F ║ .56 ║ .31 ║ .32 ║ .37 ║
╚═══╩══════╩═══════╩═════════╩════════╝
Table 3: Grid searching over settings
增加先验的好处
回想一下上面的内容,我们只是根据它是否是首选字符串以及它们的相似性来选择最佳匹配候选项。然而,在许多情况下,不同的概念将具有相同的字符串表示,例如在前面提到的“酒精”示例中。这使得在没有消歧模型的情况下很难选择最佳候选项,这需要上下文,并且再次将学习问题变成了受监督的问题,或者至少是需要术语出现的上下文示例的问题。解决这个难题的一个简单方法是,在其他条件相同的情况下,一些语义类型更有可能,因此在给定的语料库中更有可能。这也叫做在先。
在我们的例子中,我们通过训练集来估计类别先验,就像你在著名的朴素贝叶斯分类器中所做的那样。然后,对于我们为每个候选集提取的每个语义类型,我们取最大相似度,然后将其与先验相乘。在神经网络术语中,你可以认为这是类级别的最大池。这也意味着我们忽略任何候选人的首选术语状态。
╔═══╦══════╦═══════╦═════════╦════════╦══════════╗
║ ║ BERT ║ QUMLS ║ + Spacy ║ + Grid ║ + Priors ║
╠═══╬══════╬═══════╬═════════╬════════╬══════════╣
║ P ║ .53 ║ .27 ║ .29 ║ .37 ║ .39 ║
║ R ║ .58 ║ .36 ║ .37 ║ .37 ║ .39 ║
║ F ║ .56 ║ .31 ║ .32 ║ .37 ║ .39 ║
╚═══╩══════╩═══════╩═════════╩════════╩══════════╝
Table 4: adding priors
不幸的是,这是我们在 QuickUMLS 中使用简单系统所能达到的极限。假设我们最终使用的阈值是. 99,这意味着我们根本没有使用 QuickUMLS 的*似匹配功能。去除*似匹配也将极大地加速整个系统,因为算法的大部分时间现在都花在 QuickUMLS 中的匹配上。
深入错误分析:我们的解决方案适合这项工作吗?
当我们在进行命名实体识别任务时,我们可能会犯几类错误。首先,我们可以提取正确的跨度,但是错误的类。当我们找到一个正确的术语,但给它一个错误的标签时,就会发生这种情况,例如,当它指的是消毒剂时,我们认为“酒精”指的是饮料。第二,我们也可以提取部分跨度,但仍然匹配正确的标签。在这种情况下,我们可以将匹配视为“部分匹配”。在我们的评分中,我们只将精确的匹配视为正确。这方面的一个例子是,当黄金标准是“麻醉剂”时,提取“轻度麻醉剂”。我们也可能完全遗漏跨度,例如,因为 UMLS 不包含该术语,或者提取不符合黄金标准提及的跨度。下图显示了我们的系统会出现哪些类型的错误:
图来源:作者
这表明 QuickUMLS 产生的错误不属于某一特定类别。它提取了太多的项目,但是,当它提取项目时,它也经常给那些项目分配错误的标签。这表明 QuickUMLS 可以用作预提取系统,之后可以使用消歧系统来分配正确的标签。
结论
从结果中可以看出,现成的术语提取系统无需培训即可用作高效且有效的 NER 系统。获取特定用例的训练数据通常是一个耗时的过程,阻碍了 R&D 的速度。我们构建的 QuickUMLS 分类器表明,我们可以用很少的训练样本走很长的路。通过明智地使用我们的资源,我们在 NER 生物医学的 R&D 进程中节省了大量时间。我们修改过的 QuickUMLS 分类器可以在 github 上试用。这种方法的好处可能意味着解决方案足够健壮,易于开发和测试,并且足够小,易于在产品开发中实现。
参考文献
【1】l . Soldaini 和 N. Goharian。Quickumls:一种快速、无监督的医学概念提取方法,(2016),MedIR 研讨会,SIGIR
【2】s . Mohan,D. Li,Medmentions:用概念标注的大型生物医学语料库,(2019),arXiv 预印本 arXiv:1902.09476
【3】彭,陈,陆,多任务学习在生物医学文本挖掘中的实证研究,(2020),arXiv 预印本 arXiv:2005.02799
【4】j . Lee,W. Yoon,S. Kim,D. Kim,S. Kim,C.H. So,和 J. Kang,BioBERT:用于生物医学文本挖掘的预训练生物医学语言表示模型,(2020),生物信息学,36(4)
【5】k . c . Fraser,I. Nejadgholi,B. De Bruijn,M. Li,A. LaPlante 和 K.Z.E. Abidine,使用通用和特定领域深度学习模型从医学文本中提取 UMLS 概念,(2019),arXiv 预印本 arXiv:1910.01274。
【6】n . Okazaki 和 J.I. Tsujii,《简单有效的*似词典匹配算法》(2010 年 8 月),载于《第 23 届国际计算语言学会议论文集》(2010 年科林)
m . Neumann,D. King,I. Beltagy,W. Ammar,Scispacy:生物医学自然语言处理的快速鲁棒模型,(2019), arXiv 预印本 arXiv:1902.07669 。
进行分析与扩展分析
意见
进行分析在很大程度上是一个数据科学家可以解决的问题,而扩展分析需要的不仅仅是一个数据科学家团队
本文中的所有观点都是个人观点,是 而非 可归因于我现在(或过去)所代表的任何组织。
分析、数据科学和人工智能已经成为每个高级管理人员的主流词汇,并且成为“数据驱动”是大多数组织幻灯片中最常见的术语。然而,要在这些计划中取得成功还需要不断的努力,根据 Gartner 的预测,这些项目中有* 80%将永远无法部署。如果你问自己,“为什么?”—最常引用的原因之一是“文化”(下面引用了 HBR 的文章),这是事实,但还有其他因素也有影响。
https://hbr.org/2019/07/building-the-ai-powered-organization
作为一名数据科学从业者,我认为这些计划失败的原因之一是因为“进行分析与扩展分析”是两码事。如果你询问任何大型组织中“分析团队”的起源,你会听到一个熟悉的故事,即 3-4 名数据科学家/数据工程师在一个孤岛式团队中工作,试图构建一个数据湖,探索数据科学的用例,并开发概念证明(PoCs)以向企业展示数据的价值。虽然这种探索性的工作方式是一个很好的开始,但当涉及到将这些算法嵌入到这些组织的工作流程中时,大多数团队都犹豫了。这是因为进行分析在很大程度上是一个数据科学家可以解决的分析问题,而扩展分析需要的不仅仅是一个数据科学家团队,我将在下面详细阐述。
人工智能/人工智能确实可以用大量数据做非凡的事情,但要进行规模分析,第一个问题是,‘你有‘正确的’数据吗?’
当你开始谈论“正确的”数据时,你会在数据科学家中听到一个常见的缩写词是 GIGO(垃圾输入,垃圾输出)。大多数组织都坐在来自不同来源的大量数据之上,他们通常会吹嘘自己收集了多少字节的数据。然而,数十亿字节的数据并不能转化为数据科学的成功,大多数公司只有在开始调查规模分析后才意识到这一点。当您试图合并不同来源的数据时,比如合并销售数据和营销数据,或者合并供应链数据和销售数据,就会出现问题;这时猫从袋子里出来了。然而,这些用于销售、营销等的 IT 系统。当你试图构建管道将它们结合起来时,孤立地工作就像一种魅力——数据质量/完整性问题会阻碍你进行规模化分析。
当涉及到数据质量问题时,一个经常被建议的解决方法是在组织中建立一个“数据治理”模型。虽然拥有这一点很重要,但你必须再问自己两个问题—
i)你的业务有多“数字化”,你的数据收集过程有多少是自动化的?
如果你拿优步、脸书、亚马逊和 Airbnb 这样的企业来说,它们的整个业务流程都是数字化的,并且在某种程度上是可追踪的。在这样的公司中扩展分析比在更传统的企业中更容易,如制造业、汽车、航空航天等。原因在于,业务流程中仍有某些元素没有完全数字化(或者有一些元素依赖于手动数据输入)。因此,当更多像上面这样的传统公司想要加入使用数据科学的行列时,结果并不好,因为数据收集过程还没有标准化(或)简化。
ii)您从项目一开始就让数据科学家参与进来了吗?
在大多数公司,数据科学家只在开发模型/算法时才会参与进来。理想情况下,数据科学家应该从业务需求/理解阶段开始参与,在这个阶段,您构思问题陈述,并开始查看要解决的数据。如果数据科学家无法参与,那么让具备分析知识的人参与进来非常重要,这样他/她可以确保“正确的数据”可用于构建模型。许多公司在让“技术”人员参与商业讨论时会三思,最终会选择那些从分析角度来看你更有可能失败的项目。麦肯锡已经发现了这一差距,并就“分析翻译”这一中介角色发表了文章。
https://www.mckinsey.com/business-functions/mckinsey-analytics/our-insights/analytics-translator
假设你有高质量的数据来开发突破性的 ML 解决方案,扩展分析的下一个问题是,“你有合适的基础设施吗?”
在功能强大的笔记本电脑上使用 Python/R 进行大量预测对于进行分析来说是非常好的,但是当涉及到扩展时,云是一种方式。虽然云计算已经存在了十多年,并且大多数组织都在接受它,但是关于云的讨论通常仅限于 it 部门。这不被视为一种战略优势,也很少出现在董事会的对话中。IT 只是您业务的推动者的日子已经一去不复返了,在这个后疫情时代,IT/分析/技术几乎就是您的业务。正如高盛首席执行官劳埃德·布兰克费恩在 2017 年初所说,“我们是一家技术公司。我们是一个*台。”这种业务*台思维对于扩展分析至关重要,因为合适的云基础设施可以帮助您快速实现除其他优势之外的三个目标——
i) 跨 IT 系统的数据集成,支持您的业务和分析模型的嵌入
ii) 使用云自动化工具跟踪您的业务工作流程,协调各种任务/模型
iii) 模型管理和维护,这一领域最*出现了 MLOps,可以帮助您随着时间的推移对模型进行微调
希望您在数据和基础架构上打勾,下一个关于扩展分析的问题是,“您是否有一个适合在整个价值链中使用分析的运营模式?”
每个组织都有一个决策框架,他们依赖这个框架来运行他们的业务操作。虽然大多数公司的决策都会用到数据,但如果你想利用在整个价值链中扩展分析的复合优势,你的思考和运营方式必须改变。业内专家称之为 【最后一英里挑战】,这被定义为分析的最后阶段,其中洞察力被转化为推动价值的变化或结果。我个人曾有过这样的经历:我们以极高的准确性/度量标准构建了令人惊叹的模型,但企业还没有做好使用该模型的准备。这又回到了我们之前讨论的“文化”方面,这就是你需要在 3p 中进行根本转变的地方——人、过程和目的。
i) People —分析行业多年来一直在努力应对这一挑战,解决方案非常简单;提升数据科学和分析技能。当我与想使用人工智能的人交谈时,他们发现模型很神秘,可以为你解决任何问题,但忘记了实际上它们是协同工作的数学代理。人们观念的转变不会在一夜之间发生,因此组织必须优先考虑数据素养,并确保每个人都了解它,以使用这些模型。
ii) 流程——你作为一家企业的运作方式需要改变,如果这种思维方式的转变在任何组织中都是“自上而下”的,那会更好。领导团队必须在跨地区、市场和所有职能领域的数据&云战略上保持一致。另一个重要的部分是过程是关于你的分析模型的“可解释性”(这是最*的一个大趋势),需要集成到你的业务过程的结构中,这样人们可以更好地信任这些模型。
iii) 目的——这确实触及了“为什么”的哲学层面你甚至应该使用分析。答案是“效率和优化”。当你有一家小公司时,你可能会根据专家的意见/直觉来运作,但仍然保持竞争力。但是,随着业务的增长,做出正确的决策变得更加困难,每一个错误的决策都会造成浪费。因此,在整个公司部署分析的最终目的是更有效地运作,并找到进一步优化的领域。
结论:
总之,如果你想在你的组织中更好地扩展分析,你将需要这些关键要素—
- 不要在数据科学上草率行事。尝试首先在整个 IT 环境中修复您的数据,然后进入科学部分
- 拥抱云,并在您的业务价值链中积极使用它
- 首先考虑您将在哪里使用分析模型来推动您的业务,然后相应地在人员和流程方面做出改变。
参考文献—
1]https://www . tlnt . com/purpose-people-and-process-the-3-PS-of-effective-performance-management/
2]https://digital . HBS . edu/platform-digital/submission/Goldman-Sachs-a-technology-company/
3]https://HBR . org/2021/02/为什么很难成为数据驱动型公司
6]https://www . Gartner . com/en/news room/press-releases/2018-02-13-Gartner-says-*一半的首席信息官正在计划部署人工智能
领域适应
行业笔记
机器学习的性能取决于它接受训练的数据集。数据集是不完美的,所以数据中的问题会影响模型。一种类型的问题是域转移。
这意味着,被训练来学习一个数据集上的任务的模型,可能无法在稍微不同的数据集上执行相同的任务。
比方说,你训练一个模型在公园这样的户外环境中探测狗。它可能在户外场所的狗的测试图像上表现得非常好。然而,当试图检测室内的狗时,该模型可能不会很好地发挥作用,尽管任务本身是相同的。这是一个问题,因为图像的背景并不重要,因为你只是想检测狗。
我们将探讨解决这个问题的四篇不同的研究论文。
词汇
有两个数据集:源数据集和目标数据集。为模型定型的数据集是源数据集。目标数据集是将对其进行测试的数据集。
对于类似问题的领域概化,目标数据集在训练期间不可用。网络在源数据集上进行训练,以避免过度适应特定于域的要素。
在域自适应中,源数据集和目标数据集在训练期间都是可用的,但是目标数据集的标签并不总是可用的。对于无监督的域自适应,在训练期间没有可用于目标数据集的标签。半监督域自适应涉及来自目标数据集的一些标记的例子。使用监督域自适应,来自源数据集和目标数据集的所有数据都有标签。
无监督的领域适应是最普遍研究的问题,因为它有最多的应用。当您有一个已标注的数据集,但它太小而不能直接在其上训练时,有监督的 DA 会很有用。
这些方法可以应用于许多 ML 问题。然而,一个常见的应用是图像分类。我将集中讨论两个常用基准数据集上的图像分类:MNIST 和 SVHN。在手写数字(MNIST)上训练的模型通常在打印的门牌号数字(SVHN)上表现不佳。
对抗方法
领域适应方法的最常见的方法遵循对抗方法。对于一些背景,我建议阅读关于生成性敌对网络(GANs) 。
对抗性领域适应框架(图片由作者提供)
有两个编码器,它们学习产生每个输入的矢量表示。有一个用于对输入进行分类的分类器和一个用于区分数据集的鉴别器。目标是消除编码领域中的差异。这类似于 GAN 目标,因为我们希望编码器通过生成难以区分的编码来欺骗鉴别器。但是,这需要这样做,以便分类器对两个数据集都有效。然后可以将相同的分类器应用于两个数据集。
有许多不同的训练方法、架构和损耗的方法。高层目标一致。我们希望编码器生成包含分类所需的有用信息的编码,但消除域中的转换。
许多算法之间的关键区别在于鉴别器是什么以及它是如何被训练的。在简单的情况下,它只是一个额外的损失项。例如,最大*均差异 (MMD)测量源数据集和目标数据集编码之间的差异。在最小化差异的同时训练网络可以减少域转移。这对于简单的 DA 问题可能是有用的,但是对于较大的差异就不好用了。
阿达
ADDA 的步骤(来源)
对抗性区分域自适应(ADDA)将一种简单的方法应用于区分性 DA。源数据集和目标数据集之间只共享一个编码器。网络分两步训练。
- 编码器和分类器首先被训练以在源数据集上实现高分类准确度。
- 编码器用鉴别器来训练,以失去域的可鉴别性。鉴别器被训练来分类具有对抗性损失的两个域。编码器用这种损失的否定来训练,因为它相对于鉴别器是不利的。这种否定是通过梯度反转完成的,这意味着在反向传播中,梯度在到达编码器之前被否定。
这种方法的一个主要缺点是在自适应步骤中分类性能可能会丢失或被遗忘。这是因为在这个步骤中没有使用标签。
丹恩
DANN ( 来源)
神经网络的领域对抗训练(DANN)非常类似于 ADDA。域鉴别器与分类器一起训练,而不是有一个单独的适应步骤。使用梯度反转层是因为域鉴别器和分类器具有相反的损失函数。这允许分类和区分一起被训练,并且避免网络忘记任务。
图像翻译
解决领域差距的另一种方法是将示例从一个领域转换到另一个领域。这方面的一个例子是将街景数字(SVHN)转换成手写的 MNIST(数字)。在此转换之后,您可以应用 MNIST 训练的图像分类器。这些体系结构更加复杂,因为除了主要任务(图像分类)之外,网络还必须在源域和目标域之间来回转换图像。
将街景图像(SVHN)转换为手写图像(MNIST)的示例(图片由作者提供)
图像到图像的翻译
I2I 网络和损失(来源
像对抗方法一样,图像到图像翻译(I2I)旨在学习图像的域不变编码(Z)。在这个架构中有六个网络:源编码器、源解码器、目标编码器、目标解码器、域鉴别器和任务网络(例如:分类器)。解码器旨在从编码中重建图像。这也包括与域鉴别器的对抗学习。
基于六种不同损失的加权组合来训练网络。本文研究了哪种损失组合能产生最佳性能。
- Qc 是源域上的分类损失。我们无法获得目标域的这一损失,因为没有标签。然而,如果标签存在,损失可以扩展到包括目标域。
- Qid 是对图像进行编码并将其解码回相同域的损失。将图像编码到 Z 中并将其解码回原始域在理想情况下应该返回相同的图像。这种损失可以是原始图像和解码图像之间的差异的 L1 范数。
- Qz 是域鉴别器的损耗。这类似于 ADDA,因为它试图确定编码的域。我们希望这种损失随着编码的改进而增加。
- Qtr 是另一种鉴别损失,其中图像在进入域鉴别器之前被转换到另一个域。
- Qcyc 是循环一致性损失。这个损失和 Qid 差不多。不同之处在于,图像在原始域中被编码和解码之前,在另一个域中被解码。来自源域的图像被编码成 z。这被解码成目标域并被编码回 z。这然后被解码成源域并与原始图像进行比较。源和目标交换时的损耗也适用。这旨在确保不同域中相似图像的编码具有相似的编码。
- Qtrc 类似于 Qcyc,但不是解码回原始域,而是对编码进行分类。与 Qcyc 不同,这是不对称的,因为它涉及标签。来自源域的图像被翻译到目标域,然后被分类。
苏铁
苏铁网络和损失(来源
苏铁类似于 I2I。许多 I2I 损耗和网络在这里都有对应的部分。主要区别在于目标图像没有被转换到源域。此外,GAN 损耗可以应用于图像和编码。
源图像被转换到目标域。它们被转换回源域以应用循环一致性损失(与原始图像的 L1 差异)。
fs 网络在源域中的监督学习任务上被训练。语义一致性损失确保来自该网络的特征在翻译到目标域之前和之后保持接*。这确保图像在翻译后保留语义信息。
然后,将 GAN 损失应用于转换后的图像和目标图像的图像和特征(来自 fT)。这种损失是训练翻译与目标域相似所必需的。存在两个 GAN 损耗,以确保图像和特征是相似的。
最后,将任务损失应用于翻译后的图像。这将任务应用于原始目标图像。
其他领域
图像分类是用来测试领域适应方法的主要问题。然而,域自适应也可以应用于其他计算机视觉问题,如图像分割。它还可以应用于不同的研究领域,如自然语言处理(NLP)。
领域适应的一个特别有趣的应用是自动驾驶汽车和机器人。使用来自模拟环境的数据来为这些应用训练深度神经网络是常见的做法。在模拟环境中收集大量数据比在现实世界中容易得多。然而,为了使根据模拟数据训练的模型在现实环境中发挥作用,通常需要进行领域调整以实现良好的性能。
这个问题也有许多变体,包括少量领域适应、领域一般化和多类领域适应。
结论
领域适应有几种方法,但它们通常有一些共同的特征。领域歧视网络的对抗性学习是常见的。也有很多使用图像到图像翻译的工作,其中有循环一致性损失。将领域适应应用于新问题可能会涉及到这些组件的某种组合。
参考
[1]龙,,等.“用深度适应网络学习可转移特征”机器学习国际会议。PMLR,2015 年。
[2] Eric Tzeng 等,“对抗性判别域适应”。IEEE 计算机视觉和模式识别会议论文集。2017 年,第 7167–7176 页。
[3]雅罗斯拉夫·加宁和维克托·伦皮茨基。“通过反向传播的无监督域适应”。载于:arXiv 预印本 arXiv:1409.7495 (2014 年)。
[4] Zak Murez 等人,“用于领域适应的图像到图像翻译”。IEEE 计算机视觉和模式识别会议论文集。2018,第 4500–4509 页。
[5] Hoffman,Judy,等《苏铁:周期一致的对抗性域适应》。载于:arXiv 预印本 arXiv:1711.03213 (2017)。
域名激增:识别威胁参与者针对的域名主题
如何映射对世界事件的在线响应,以识别新的恶意活动
摘要
威胁行为者可以毫无顾忌地利用世界上任何他们可以利用的危机。无论是飓风、政治动荡,甚至是疫情,威胁行为者都会试图融入世界在线响应,创建欺诈性基础设施,以便赚钱、窃取 PII,甚至引发内乱。规划对不同世界事件的在线反应是能够识别威胁行为者在任何给定时间针对什么主题的第一步。
DomainTools 对我们称之为“域名激增”的现象进行了研究,旨在识别正在注册的域名中的新主题和趋势主题,并强调哪些威胁参与者是其恶意活动的潜在目标。
为了深入分析 2020 年期间发生的一些更值得注意的域名激增,请看一下 《域名工具报告:2021 年春季版 。
背景
早在 2020 年 2 月,当新冠肺炎迅速获得国际知名度时,威胁行为者正在积极利用这一形势,每天注册数百个恶意的新冠肺炎相关域名。DomainTools 安全研究小组每天都在扫描新注册的新冠肺炎相关域名,并运行冠状病毒应用程序。]网站,该网站声称有一款实时冠状病毒追踪器应用可供下载。
安全研究人员拆除了 Android 应用,去镇上将其拆开,发现这实际上是一个勒索软件应用,它会锁定受害者的手机,并要求 100 美元的比特币才能重新访问该设备。
这是一个有价值的发现,也提出了一些问题。首先,安全研究人员在扫描新注册的域名时,需要对每天要查找的单词有良好的直觉。但是那些被威胁者利用却被遗漏的相关词汇呢?第二,新冠肺炎相关领域的快速增长是一个明显的趋势,但下一个不那么明显的趋势呢?我们如何确定威胁参与者瞄准的域名的下一个趋势,以及他们使用的单词?
通过域名注册讲述新冠肺炎的故事
当 DomainTools 第一次开始分析新冠肺炎相关的域名时,早期使用的可视化工具之一是一个特定单词每天注册的域名数量的直方图。下面是 2020 年单词“covid”的直方图,它告诉我们几件事。
首先,在 2020 年 2 月之前,“covid”一词几乎从未用于域名注册。然后在 2 月 11 日,超过 800 个域名突然用“covid”这个词注册了。这一天正好是世界卫生组织(世卫组织)将这种疾病正式命名为“新冠肺炎”的第二天。
到 2 月 14 日,每日“covid”域的数量下降到每天几十个,并在大约一周半的时间里保持稳定。然后在 2 月下旬,域名的数量开始再次增加,并持续到 3 月 12 日,在这一点上,他们暴涨到前所未有的 2500 个域名在一天内注册。这是世卫组织正式宣布新冠肺炎为疫情的第二天。
从 3 月中旬到 4 月初,“covid”域名注册数量每天超过 2000 个,然后开始逐渐下降,最终稳定在每天 200 个左右的新基线,此后一直保持稳定。
您可能会问自己“这些域背后的目的或意图是什么?”一些是由不同的市政当局、卫生和非营利组织创建的新冠肺炎官方信息域。有些是错误信息和虚假信息领域;从网络安全的角度来看,粗略但不构成威胁。许多是域名投机的结果,这似乎不可避免地伴随着任何危机或新闻事件。但是,这些域中有大量是由威胁者出于欺诈、网络钓鱼和个人身份信息(PII)收集目的而创建的。
将微观应用到宏观
网络安全中数据科学的主要目标之一是分析安全研究人员每天使用的策略和技术,并尝试通过算法将它们应用于大规模数据。
根据这种工作方式,我们选择每天监控所有单词,并分析每个单词随时间的频率分布,以确定其基线频率的异常值,而不是试图猜测每天监控哪些单词来识别域名注册中的新主题。
但是词频并不是唯一有用的衡量标准。了解一个单词或术语有多独特可以提供很多有价值的见解。在 2020 年 2 月 11 日之前,“covid”这个词在域名中只出现了几百次;大部分是作为一个更大的单词的一部分或者作为两个单词组合的副产品。当试图发现威胁参与者正在利用的趋势时,识别新的独特趋势词也可能是有用的。
衡量领域中单词的重要性
涵盖词频和唯一性的最常见的统计方法之一是 TF-IDF 。TF-IDF 代表术语频率-逆文档频率,是相对于一组已知的文档(也称为语料库),一个单词对单个文档有多重要的度量。
更正式地说,词频(TF)是单个文档中一个单词的计数。在文档中更频繁使用的单词对文档的语义有更大的影响。在文档中只使用一次或两次的单词对语义的影响较小,尤其是对于较长的文档。
逆文档频率(IDF)是一个词相对于整个文档集提供多少信息的度量,使用以下函数计算:
其中 N 是语料库中的文档总数,DFt 是包含单词 t 的文档的数量。单词在语料库中出现的文档数量越多,其 IDF 得分越低,相反,单词出现在越少的文档中,其 IDF 得分越高。
举个例子来说明这一切,让我们以 Wikipedia.com 为例。今年,维基百科通过了 600 万篇英文文章;所以维基百科的语料库是 6,000,000 个文档。假设单词“the”出现在语料库中的每个文档中是安全的。因此“the”的 IDF 值为:log(6,000,000 / 6,000,000 +1) = 0.30。
让我们比较一下 IDF 对“the”和“dragon”的评分。“龙”出现在 3,340 个维基百科文档中,给它的 IDF 得分为:log(6,000,000 / (3,340 + 1)) = 3.25。
(注意:在分母上加 1 是为了防止某个单词没有出现在任何文档中时出现被零除的错误)
当查看维基百科关于地下城&龙的文章时,可以计算出“the”和“dragon”的 TF-IDF 分数,以显示这两个词对文章的重要性。
单词“The”在文章中的词频为 1705,TF-DIF 得分为:
1705 * 0.30 = 511.5。
单词“dragon”在文章中的词频为 1092,TF-IDF 得分为:
1092 * 3.25 = 3549
因此,当在《龙与地下城》这篇文章的上下文中比较这两个词时,可以看出“龙”比“the”更重要
定义文档
测量一个词的重要性的同样的统计方法可以应用到域名上,但是首先我们必须在域名注册的上下文中定义一个文档是什么。
由于目标是识别随着时间的推移域名注册中的单词趋势,一种方法是将在一天内注册的所有域视为单个文档,并将组成这些域的所有单个单词视为组成该文档的单词集。
比如,以 2020 年 6 月 1 日为例;这一天有 285,835 个域名注册。一旦每个域名被分割成其组成部分(英语)的话,总共有 2,595,402 个单词用于注册该天的域名。这将代表我们语料库中的单个文档。
注意:为了了解每天用于注册域名的单词有多少重复,尽管在 2020 年 6 月 1 日有将* 260 万个单词用于生成域名,但只有 213,244 个独特的单词。
定义语料库
语料库被定义为特定种类或特定主题的所有著作或作品。在 Wikipedia.com 的例子中,语料库是维基百科中的所有英文文章。IDF 分数告诉我们一个单词在所有文档的整个语料库中有多重要。因此,当计算用于注册域名的单词的 IDF 分数时,为了计算有意义的逆文档频率(IDF)分数,定义语料库的范围是很重要的。
语料库可以定义为从某个任意日期开始的每一天,但这有一些问题。由于目标是识别威胁参与者可能针对的新词和趋势词,因此时间回溯太久可能会降低当前有趋势但过去也有趋势的词的 IDF 得分。
以“圣诞节”这个词为例。如果将语料库的开头设置为 2018 年 7 月 1 日,那么每天使用“Christmas”的次数看起来如下。
请注意,从 9 月初开始到 12 月底,每天的域数量会有一个可预测的年度增长。
如果在 2020 年 10 月 1 日,人们正在寻找独特的趋势词,那么圣诞节的 IDF 得分将包括 2018 年和 2019 年趋势峰值的计数。这可能会降低圣诞节的 IDF 分数,使其不再将圣诞节视为一个新的流行词汇。
为了解决这个问题,IDF 分数是根据 180 天的滑动历史窗口计算的。因此,对于每一天,对于每个单词,IDF 计算使用之前的 180 天作为文档集来计算该单词的 IDF 分数。例如,2020 年 10 月 1 日,圣诞节的 IDF 分数是根据 2020 年 4 月 4 日至 2020 年 10 月 1 日期间使用的天数计算的。
这个滑动的 180 天历史窗口使得 TF-IDF 分数能够识别现在唯一有趋势的单词,而不管该单词在之前几年的趋势如何。
识别域名单词
下一个挑战是将域名分解成它们的组成单词。有多种方法可以解决这个问题,但最成功的方法是每天将新注册的域名与包含所有已知单词的字典进行比较。如果在一个域名中找到一个已知单词,它将被输出到包含该域名的所有单词的列表中。
不幸的是,即使在最流行的词典中,也不是每个热门词汇都能找到。例如,单词“covid”直到 2020 年 2 月 11 日才被添加到大多数在线词典中。因此,为了尝试捕获最具代表性的英语单词集,我们对以下来源的进行了分析,并将其合并到一个黄金标准词典中:
- 三种开源在线词典
- 谷歌 n-gram 数据集
- 都市词典
- 从维基百科时事页面解析出的单词(每日更新)
维基百科时事页面是一个有价值的新的重要单词或短语的来源,这些单词或短语在其他单词来源中没有出现。
识别域名中的单词的另一个挑战是从算法上理解注册人在注册域名时针对的是哪些单词。以虚拟域名 sharepoint-chasebank-login 为例。]com。
该域中所有可能的英语单词列表如下:
- 分享
- 野兔
- 要点
- 重新勾嵌
- sharepoint
- 追赶
- 银行
- 原木
- 注册
威胁分析师会立即将注意力集中在“sharepoint”和“login”这两个词上,而不会将它们拆分成更小的词,但从算法上来说,解读注册人的意图要困难得多。
有各种不同的试探法和概率方法来解决这个问题,但是每种方法都有潜在的遗漏重要单词的缺点。取而代之的是,简单地使用域名中的所有单词的方法效果很好。
根据这一数据 TF-IDF 模型,现在有可能调查随着时间的推移,单词在域名注册中的使用情况。
域名激增和峰值
每当值得关注的事件发生时,人们不可避免地会使用词语和主题来注册与事件相关的域名。如果有足够多的与这个主题相关的域名在短时间内被注册,这些域名可以用两种不同的方式来描述;畴尖峰和畴开花。
基线词频
大多数单词都有一个每天在域名注册中使用的基线频率。随着时间的推移,这一基线通常相当稳定,并且通常以 7 天为一个周期,因为人们在周末注册的域名较少。例如,下面是“电话”一词的每日词频。
请注意“phone”这个词每天出现在大约 180 个域中,随着时间的推移相当一致。这种模式就是我们所说的单词的基线频率。
域尖峰
有趣的是,异常值是从基线中识别出来的。例如,下图显示了“电话”一词在 11 月的出现频率。请注意,在 10 月 13 日,为“phone”注册的域名比基线多了 300 个。我们称这样的域名注册事件为峰值。
域名峰值是指一个特定单词相对于其基线每天注册的域名数量急剧增加。峰值通常发生在与该词相关的事件发生后的第二天,通常只持续一到两天。上述峰值对应的是 2020 年 10 月 13 日苹果 iPhone 12 的发布。
另一个与新闻相关的峰值的例子是下面的单词“贝鲁特”,它对应于 2020 年 8 月 4 日在贝鲁特发生的导致 200 多人死亡的化肥储存设施爆炸。
请注意,贝鲁特的峰值直到新闻发布后的第二天才出现,而手机峰值与 iPhone 12 发布在同一天。这两个域尖峰代表了两种不同的行为。
iPhone 12 秒杀事件发生在苹果产品发布会的同一天,挖掘 DomainTools 数据库发现,其中大量产品实际上是由苹果公司注册的;最有可能的是试图将域名撤出市场,这样其他人就不能从中获利或以侵犯苹果商标的方式使用它们。
贝鲁特事件是对一起悲剧事件的回应,看看这个事件中的域名,很多都是以帮助贝鲁特人民为主题的。
helpbeirutlb[.]comhelp4beirut[.]comhope4beirut[.]comhelpforbeirut[.]comhopeforbeirut[.]comdonateforbeirut[.]comdonate-beirut[.]comdonatebeirut[.]comgivetobeirut[.]comfundbeirut[.]comaidbeirut[.]combeirutfund[.]com
不幸的是,当查看今天的域名时,几乎所有的域名都只是停放的域名,很可能是域名投机者注册的,他们试图抢购他们认为一些救援组织可能想要购买的域名。
DGA 域峰值
一类特殊的域尖峰是由基于字典的域生成算法(DGAs)产生的。DGAs 是一种算法,可以自动生成并有时注册恶意软件和僵尸网络用于通信协调的域名。DGA 传统上用随机的字母数字序列生成域名。这些域名很容易识别,因为它们的字符分布与合法域名有很大不同。
另一方面,基于字典的 DGA 试图通过随机组合字典中的单词来创建与合法域名的字符分布非常接*的域名,从而避开这种类型的检测。但是当你知道要寻找什么的时候,这些也很容易被发现。例如,看看每天使用“witness”一词注册的域名数量。
有几个指标表明,几乎每个峰值都是由字典 DGA 产生的。先看一下 5 月前和 10 月后的基准线;有一个一致的基线,每天约 10 个领域与“证人”一词。接下来,如果你观察每个尖峰信号的峰值,每个尖峰信号都是 500 个磁畴的倍数。像这样的精确模式不会在野外发生,并且清楚地指向一些自动生成并以 500 为一批注册域名的脚本。
识别基于字典的 DGA 的另一种方法是视觉扫描构成尖峰的域名。下面是从 5 月 11 日持续到 5 月 13 日的第一次高峰中的 16 个域名的样本,由 1,500 个域名组成。
ultra-outlinetowitnesstoday.infoultraoutline-towitnesstoday.infoultraoutlineto-witnesstoday[.]infoultraoutlinetowitness-today[.]infobest-outlinetowitnesstoday[.]infobestoutline-towitnesstoday[.]infobestoutlineto-witnesstoday[.]infobestoutlinetowitness-today[.]infoboss-outlinetowitnesstoday[.]infobossoutline-towitnesstoday[.]infobossoutlineto-witnesstoday[.]infobossoutlinetowitness-today[.]infocool-outlinetowitnesstoday[.]infocooloutline-towitnesstoday[.]infocooloutlineto-witnesstoday[.]infocooloutlinetowitness-today[.]info
当首先按长度排序域,然后按字母顺序排序时,DDGA 模式通常很好地组合在一起,很容易发现组成算法使用的字典的单词集。最重要的是,这些单词没有一个与注册当天的任何新闻事件相对应。这些只是随机的“单词沙拉”域。
域名泛滥
域名激增开始类似于峰值。一个单词的每日使用频率遵循某个基线频率,然后在某个事件发生后迅速增加到异常水*。但与峰值不同,几天后,每天的域名数量要么继续增加,要么在一段时间内保持稳定。过一段时间后,每天注册的域名数量会逐渐回落到基线水*,或者潜在的保持稳定的新基线水*。
域名绽放的典型例子是本文开头详细描述的新冠肺炎绽放。
水华,尤其是由自然灾害引发的水华,很容易成为威胁者的目标。在新冠肺炎疫情的早期,威胁行为者创建了数百个欺诈性筹款和网络钓鱼域,伪装成帮助疫情危机的非营利组织和市政当局的官方域。
集群领域开花成花束
早期新冠肺炎面临的挑战之一是识别威胁参与者在注册相关恶意域名时利用的其他词。安全研究人员不得不猜测不同的单词,并查看它们的布鲁姆图,以确定它们是否遵循与“covid”相同的趋势,但这起初只是一个猜测游戏,直到可以开发出更具算法性的方法。
一个领域开花实际上只是一个时间序列直方图,或随着时间的推移发生的计数。在数据科学中,这被称为向量,对于任何给定的向量,可以搜索所有已知向量的向量空间,以识别具有非常相似特征的其他向量。理论上,大约在同一时间开始、具有相似形状的峰、然后大约在同一时间结束的两个花开应该在语义上彼此相关。
为了测试这一点,使用了一种称为聚类的特殊类型的机器学习算法,该算法试图将相似的项目分组到一起。为此,为 2020 年期间注册的域名中使用的所有单词生成了布鲁姆直方图向量。然后,这些 bloom 向量通过聚类算法运行,以生成相似 bloom 的组。
一旦生成了 bloom 簇,就识别包含“covid”的 bloom 的簇,并取出 5 个最相似的 bloom 向量。这些单词的缩写如下所示:
请注意,并非每个水华都与“covid”具有相同的数量级,但其大致形状和持续时间非常相似。
在此分析中发现的其他值得注意的集群包括围绕社交距离的集群:
这个是关于黑人的命也是命运动的:
未来的研究
并不是每个重大事件都会像新冠肺炎那样成为威胁者的关注焦点。需要做进一步的研究来更好地对不同类型的 blooms 和 spikes 进行分类,确定它们背后的定义特征,并更好地理解参与注册这些域的人的动机。基于这种学习,我们可以更好地对恶意域进行分类,这些恶意域是在有机会运行之前为响应不同事件而注册的。
要深入分析 2020 年期间发生的一些更值得注意的域名激增和激增,请查看《域名工具报告:2021 年春季版。
捐助者选择:广泛的探索性数据分析(EDA)
一种分析方法,用于解释和揭示对 DonorsChoose *台的见解,以回答业务问题:'什么使项目有趣?
关于项目
DonorsChoose.org是一个在线慈善*台,成千上万的教师可以在这里提出和推广对材料和特定设备的在线项目请求,让学生的教育机会对每个人都是*等的。
该项目基于一个预测 DonorsChoose.org 激动人心的ka ggle 竞赛,旨在确定哪些项目值得关注并具有商业意义。
目标
以下探索性数据分析(EDA)的目的是揭示趋势和见解,从商业角度来看,确定并证明最有吸引力的财务捐赠项目。
主要目标是分析数据,以便回答下面的业务问题:什么使项目令人兴奋?
设置
# Import required librariesimport pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.style as style
import seaborn as sns
%matplotlib inline
import pingouin as pg
color = sns.color_palette()
from pandas.plotting import table
import scipy
from scipy.stats import pearsonr, spearmanr
from wordcloud import WordCloud, STOPWORDS
import datetime as dt
import plotly
import cufflinks as cf
import plotly.offline
cf.go_offline()
cf.set_config_file(offline=False, world_readable=True)# Load datadon = pd.read_csv("donations.csv")
ess = pd.read_csv("essays.csv")
out = pd.read_csv("outcomes.csv")
res = pd.read_csv("resources.csv")
pro = pd.read_csv("projects.csv")
数据集信息
数据——在此下载——被分成 5 个独立的数据集:
1——捐赠,2——论文,3——成果,4——资源,5——项目。
作者图片
664098 个项目有 3097989 个捐赠,619326 个成果。还有 3667217 的资源和 664098 的短文。
功能
让我们创建一个函数,打印数据帧的形状→观察值、特征、重复值、缺失值的数量。
# SHAPEdef shape_df(df):
print(f"Number of observations: {df.shape[0]}")
print(f"Number of variables: {df.shape[1]}")
print(f"Number of duplicates: {df.duplicated().sum()}")
print(f"Are there any missing values {df.isnull().values.any()}")
print("-----")
print(df.dtypes.sort_values(ascending=True))
print("------")
print("Datatypes' proportion:")
print(df.dtypes.value_counts(ascending=True))
用于检测是否有任何缺失值 →如果有,返回缺失值的绝对值和百分比。
# MISSING VALUESdef null_val(df):
detect_null_val = df.isnull().values.any()
if detect_null_val:
null_abs = df.isnull().sum()
null_pc = df.isnull().sum() / df.isnull().shape[0] *100
null_concat = pd.concat([null_abs,null_pc], axis=1).round(2)
null_concat.columns = ['Absolute', 'Percent']
return null_concat.sort_values(by="Absolute", ascending=False)
else:
print("There are no missing values.")
通过热图→皮尔逊方法显示相关性的函数。
# CORRELATIONSdef corrs(x):
mask = np.triu(x.corr(), 1)
plt.figure(figsize=(19, 9))
return sns.heatmap(x.corr(), annot=True, vmax=1, vmin=-1, square=True, cmap='BrBG', mask=mask);
探索性数据分析
找到我们正在处理的变量的类型(以及类型中的数量),检查缺失值、相关性以及数字和分类变量的统计描述。
数据集:项目
基本信息
How many features are available?
How many observations are in the dataset?
Are there any duplicated records?
Are there any missing values?
What type of variables are there?
How many variables of each type?# Observations, features, duplicates, datatypes and proportionsshape_df(pro)>> output:Number of observations: 664098
Number of variables: 35
Number of duplicates: 0
Are there any missing values? True
-----
fulfillment_labor_materials float64
students_reached float64
school_ncesid float64
school_latitude float64
school_longitude float64
total_price_including_optional_support float64
total_price_excluding_optional_support float64
school_zip float64
primary_focus_subject object
primary_focus_area object
secondary_focus_subject object
secondary_focus_area object
projectid object
poverty_level object
grade_level object
teacher_ny_teaching_fellow object
eligible_double_your_impact_match object
resource_type object
teacher_teach_for_america object
school_charter_ready_promise object
eligible_almost_home_match object
school_kipp object
school_nlns object
school_year_round object
school_magnet object
school_charter object
school_county object
school_district object
school_metro object
school_state object
school_city object
schoolid object
teacher_acctid object
teacher_prefix object
date_posted object
dtype: object
------
Datatypes' proportion:
float64 8
object 27
dtype: int64# Describe missing valuesnull_val(pro)
null_val()函数图片作者。
# Stats description (num)
pro.describe().round(2)
数字统计描述。图片作者。
从 664098 条记录来看,‘total _ price _ excluding _ optional _ support’的*均价格是 542,远远高于表示存在异常值的中值。“total _ price _ including _ optional _ support”也是如此。查看第 1 和第 3 个四分位数,除了最大值之外,数据似乎都正常。
# Stats description (cat)
pro.describe(include="object")
分类统计描述(裁剪图像)。图片作者。
加利福尼亚州是提交项目最多的州,但芝加哥是观察次数最多的城市(9007 次)。
有三种大都市类型的学校,城市是最常见的。
在 5 种类型的教师中,“夫人”是最常见的记录,女教师在性别方面领先。
在 7 个学习领域中,识字和语言是最受欢迎的领域,供应品是最受欢迎的资源类型。
在被确定为贫困水*最脆弱的学生中,最常见的是最年轻的“PerK-2 级”。
在 2012 年 9 月的最后一天,1490 个项目请求被发布在网上。
# Checking correlations
corrs(pro)
Corrs()函数。图片作者。
从之前的热图来看,没有深刻的相关性。
1.教师的个人资料是什么?它在要求的项目中是如何分配的?
# Checking feature "teacher_prefix" distribution
df_teacher = pro[["teacher_acctid","teacher_prefix"]].copy()# Replacing np.nan values by ""unknown"
df_teacher.teacher_prefix.replace(np.nan, "Unkown", inplace=True)# Bar plot
ax = df_teacher["teacher_prefix"].value_counts().plot(kind="bar", figsize=(9,6), fontsize=12, color=sns.color_palette("rocket"), table=True)for p in ax.patches:
ax.annotate("%.2f" % p.get_height(), (p.get_x() + p.get_width() / 2., p.get_height()), ha='center', va='center', xytext=(0, 7), textcoords='offset points')plt.xlabel("teacher_prefix", fontsize=10)
plt.xticks(rotation=0, horizontalalignment="center")
plt.ylabel("Absolute values", fontsize=10)
plt.title("Teacher Prefix Distribution", fontsize=10, loc="right")# side table
sidetb = df_teacher.teacher_prefix.value_counts().sort_values(ascending=False).to_frame()
sidetb["percent"] = (sidetb.apply(lambda x: x/x.sum())*100).round(2)
sidetb['cumsum'] = sidetb["percent"].cumsum()
display(sidetb.T)ax.axes.get_xaxis().set_visible(False)
table = ax.tables[0]
table.set_fontsize(10)
table.scale(1,1.7)
图片作者。
从分析中可以清楚地看出,“Mrs”和“Ms .”是最常见的称谓/前缀,它们合起来相当于大约 87%的记录。
时间日期
将 date_posted 转换为“年”、“月”、“日”和“工作日”变量。
# Convert "date_posted" into "year","month","day","weekday" featurespro["date_posted"] = pd.to_datetime(pro["date_posted"], format="%Y-%m-%d")
pro["year_posted"] = pro["date_posted"].dt.year
pro["month_posted"] = pro["date_posted"].dt.month.map({1:"Jan", 2:"Feb", 3:"Mar", 4:"Apr", 5:"May", 6:"Jun", 7:"Jul", 8:"Aug", 9:"Sep", 10:"Oct", 11:"Nov", 12:"Dez"})pro["weekday_posted"] = pro["date_posted"].dt.dayofweek.map({0:"Mon", 1:"Tue", 2:"Wed", 3:"Thu", 4:"Fri", 5:"Sat", 6:"Sun"})pro[["date_posted","year_posted","month_posted","weekday_posted"]]
图片作者。
2.提交项目请求的最常见时期是什么?
2.1.在哪个工作日?
# Checking distribution of submited projects regarding the weekdaysdf_timedate = pro[["teacher_acctid","teacher_prefix","year_posted","month_posted","weekday_posted","date_posted"]].copy()# Bar plot
ax = df_timedate["weekday_posted"].value_counts().plot(kind="bar", figsize=(9,6), fontsize=12, color=sns.color_palette("rocket"), table=False)for p in ax.patches:
ax.annotate("%.2f" % p.get_height(), (p.get_x() + p.get_width() / 2., p.get_height()), ha='center', va='center', xytext=(0, 7), textcoords='offset points')plt.xlabel("Weekdays", fontsize=10)
plt.xticks(rotation=0, horizontalalignment="center")
plt.ylabel("Absolute values", fontsize=10)
plt.title("Submited projects regarding the days of the week", fontsize=10, loc="right")# side table
daysofweek = df_timedate.weekday_posted.value_counts().sort_values(ascending=False).to_frame()
daysofweek["percent"] = (daysofweek.apply(lambda x: x/x.sum())*100).round(2)
daysofweek['cumsum'] = daysofweek["percent"].cumsum()
display(daysofweek.T)
图片作者。
周日显然是一周中老师提交更多项目的日子。趋势是越接*周末,浓度越大。一个可能的解释是,在星期天,老师们有更多的空闲时间来完成这项任务。
2.2.哪几个月?
# Checking distribution of submited projects along the academic year# Bar plot
ax = df_timedate["month_posted"].value_counts().plot(kind="bar", figsize=(9,6), fontsize=12, color=sns.color_palette("rocket"), table=True)plt.xlabel("Months", fontsize=10)
plt.xticks(rotation=0, horizontalalignment="center")
plt.ylabel("Absolute values", fontsize=10)
plt.title("Submited projects along the year", fontsize=10,loc="right")ax.axes.get_xaxis().set_visible(False)
table = ax.tables[0]
table.set_fontsize(10)
table.scale(1,1.7)
图片作者。
分析显示,*台投稿发生率最高的月份是 9 月和 10 月。在学年开始时有更多的提交量是有意义的。第一学期注意力更集中。
2.3。这些年的整体演变是怎样的?
# Excluding 2014 - not complete:df_timedate[df_timedate["year_posted"]==2014].month_posted.unique()>> output: array([‘May’, ‘Apr’, ‘Mar’, ‘Feb’, ‘Jan’], dtype=object)
---# Checking evolution from 2002 to 2013# Bar plot
ax = df_timedate.groupby("year_posted")['teacher_acctid'].count()[:-1].plot(kind="line", figsize=(9,6), fontsize=12, linewidth=2, grid=True, table=True)plt.xlabel("Years", fontsize=10)
plt.xticks(rotation=0, horizontalalignment="center")
plt.ylabel("Absolute values", fontsize=10)
plt.title("Evolution of submited projects", fontsize=10,loc="right")ax.axes.get_xaxis().set_visible(False)
table = ax.tables[0]
table.set_fontsize(10)
table.scale(1,1.7)
图片作者。
对年度项目员额分布的分析揭示了一个明显而强劲的增长趋势。自 2002 年该*台诞生以来,增长率一直呈指数级增长。
3.哪些领域更有责任进行在线提交?
schools_ = pro[["school_metro", "school_state", "school_city", "resource_type", "grade_level", "poverty_level", "year_posted"]].copy()schools_.head()
图片作者。
# Checking feature "school_metro" regarding school located areas# Replacing np.nan values by ""unknown"
schools_.school_metro.replace(np.nan, "Unkown", inplace=True)
---# Bar plot
ax = schools_.school_metro.value_counts().plot(kind="pie", figsize=(9,6), autopct='%1.2f%%', explode= (0.005, 0.005, 0.005, 0.005), startangle=80, fontsize=12, table=True)# Params
plt.xticks(rotation=0, horizontalalignment="center")
plt.ylabel("", fontsize=10)
plt.title("Metropolitan school areas", fontsize=10,loc="right")ax.axes.get_xaxis().set_visible(False)
table = ax.tables[0]
table.set_fontsize(10)
table.scale(1,1.7)
图片作者。
学校属于哪些大都市地区?最高浓度出现在城市地区,其次是郊区和农村。12%未知。
4.提交的项目最集中在哪个州?
# Checking feature "school_state" regarding higher concentration of project openingshigh_states = schools_.school_state.value_counts()# Bar plot
ax = schools_.school_state.value_counts(ascending=False)[:5].plot(kind="bar", figsize=(9,6), fontsize=12, color=sns.color_palette("rocket"), table=True)# Params
plt.xticks(rotation=0, horizontalalignment="center")
plt.ylabel("Absolute values", fontsize=10)
plt.title("States with higher schools' concentration", fontsize=10,loc="right")ax.axes.get_xaxis().set_visible(False)
table = ax.tables[0]
table.set_fontsize(10)
table.scale(1,1.7)
图片作者。
所选的“前 5 名”学校州几乎占所有空缺的 50%。加州领先,其次是纽约和北卡罗来纳,比例接*伊利诺伊州和得克萨斯州。
5.哪个城市的哪个学校影响更大?
# Checking feature "school_city" regarding project openings# Bar plot
ax = schools_.school_city.value_counts(ascending=False)[:10].plot(kind="bar", figsize=(12,8), fontsize=12, color=sns.color_palette("rocket"), table=True)# Params
plt.xticks(rotation=0, horizontalalignment="center")
plt.ylabel("Absolute values", fontsize=10)
plt.title("Cities with higher schools' concentration", fontsize=10,loc="right")ax.axes.get_xaxis().set_visible(False)
table = ax.tables[0]
table.set_fontsize(10)
table.scale(1,1.7)
图片作者。
如果就各州而言,加利福尼亚州之后是纽约州和北卡罗来纳州,比例接*伊利诺伊州和得克萨斯州;就城市而言,芝加哥、洛杉矶、布鲁克林、布朗克斯和纽约最能代表这种分布。
6.哪种类型的资源请求要求最高?
# Checking feature "resource_type" distribution# Bar plot
ax = schools_.resource_type.value_counts().plot(kind="bar", figsize=(9,6), fontsize=12, color=sns.color_palette("rocket"), table=False)for p in ax.patches:
ax.annotate("%.2f" % p.get_height(), (p.get_x() + p.get_width() / 2., p.get_height()), ha='center', va='center', xytext=(0, 7), textcoords='offset points')plt.xlabel("Resources", fontsize=10)
plt.xticks(rotation=0, horizontalalignment="center")
plt.ylabel("Absolute values", fontsize=10)
plt.title("Resource types Distribution", fontsize=10, loc="right")
图片作者。
从所需资源的类型来看,用品和技术是数量要求最高的类型,其次是书籍、其他、旅行和游客。
7.学生的成绩是怎样的?
# Checking feature "grade_level" distribution# Bar plot
ax = schools_.grade_level.value_counts().plot(kind="bar", figsize=(9,6), fontsize=12, color=sns.color_palette("rocket"), table=False)for p in ax.patches:
ax.annotate("%.2f" % p.get_height(), (p.get_x() + p.get_width() / 2., p.get_height()), ha='center', va='center', xytext=(0, 7), textcoords='offset points')plt.xlabel("Grade levels", fontsize=10)
plt.xticks(rotation=0, horizontalalignment="center")
plt.ylabel("Absolute values", fontsize=10)
plt.title("Grade levels distribution", fontsize=10,loc="right")
图片作者。
趋势很明显:学生越年轻,在线请求的数量就越多。
8.职等和所要求的资源类型之间有什么关系吗?
# Understanding how resources are distributed over the grade levelsplt.figure(figsize=(16,8))ax = sns.countplot(x="grade_level", hue="resource_type", data=schools_, dodge=True)plt.legend()
plt.xlabel("Grade levels", fontsize=10)
plt.xticks(rotation=0, horizontalalignment="center")
plt.ylabel("Absolute values", fontsize=10)
plt.title("Resources' distribution by the grade levels", fontsize=10, loc="right");
图片作者。
了解资源在年级水*上的分布:无论年级水*如何,对资源的需求趋势是相同的:供应品、技术、书籍等。
9.贫困程度会影响申请资金的趋势吗?
# Checking feature "poverty_level" distribution# Bar plot
ax = schools_.poverty_level.value_counts(dropna=False).plot(kind="pie", figsize=(12,8), autopct='%1.2f%%', explode= (0.005, 0.005, 0.005, 0.005), startangle=80, fontsize=12, table=True)plt.xticks(rotation=0, horizontalalignment="center")
plt.ylabel("", fontsize=10)
plt.title("Poverty classes distribution regarding requested projects", fontsize=10, loc="right")ax.axes.get_xaxis().set_visible(False)
table = ax.tables[0]
table.set_fontsize(10)
table.scale(1,2)
图片作者。
从对贫困阶层分布的分析中可以明显看出,脆弱性越高,申请资助的项目数量就越大。
10.在大都市安置和贫困水*方面,申请项目的分布是否有趋势?
# Checking "poverty levels" regarding metropolitan school areash_pov = schools_.groupby(["poverty_level","school_metro"]).year_posted.count().iloc[:4]
hpov = pd.DataFrame(h_pov)hst_pov = schools_.groupby(["poverty_level","school_metro"]).year_posted.count().iloc[4:8]
hstpov = pd.DataFrame(hst_pov)l_pov = schools_.groupby(["poverty_level","school_metro"]).year_posted.count().iloc[8:12]
lpov = pd.DataFrame(l_pov)m_pov = schools_.groupby(["poverty_level","school_metro"]).year_posted.count().iloc[12:]
mpov = pd.DataFrame(m_pov) merge_1 = pd.merge(hstpov, hpov, on=["school_metro"])
merge_1.rename(columns={"year_posted_x":"Highest Poverty", "year_posted_y":"High Poverty"}, inplace=True)merge_2 = pd.merge(merge_1,mpov,on=["school_metro"])merge_3 = pd.merge(merge_2,lpov,on=["school_metro"])
merge_3.rename(columns={"year_posted_x":"Moderate Poverty", "year_posted_y":"Low Poverty"},inplace=True)
---# Bar plot
ax = merge_3.sort_values(by=["High Poverty"], ascending=True).plot(kind="bar", figsize=(9,6), fontsize=12, color=sns.color_palette("rocket"), table=True)# Params
plt.xticks(rotation=0, horizontalalignment="center")
plt.ylabel("Absolute values", fontsize=10)
plt.title("Poverty level distribution regarding metropolitan schools' areas", fontsize=10,loc="right")ax.axes.get_xaxis().set_visible(False)
table = ax.tables[0]
table.set_fontsize(10)
table.scale(1,1.7)
图片作者。
根据对大都市学区贫困程度的分析,属于高贫困程度城市地区的学校最常见,这也是因为城市地区的密度更大,这与两个阶层的分布和关系有关系。
11.就研究领域而言,哪些是提交最多的捐赠?
# Checking "primary focus areas" regarding requested projectssubject = pro[["resource_type", "grade_level", "primary_focus_area", "total_price_excluding_optional_support", "total_price_including_optional_support", "poverty_level", "year_posted"]].copy()
---# Bar plot
ax = subject.primary_focus_area.value_counts(ascending=False).plot(kind= "bar", figsize=(9,6), fontsize=12, color=sns.color_palette("rocket"), table=False)for p in ax.patches:
ax.annotate("%.2f" % p.get_height(), (p.get_x() + p.get_width() / 2., p.get_height()), ha='center', va='center', xytext=(0, 7), textcoords='offset points')plt.xlabel("Primary Focus Areas", fontsize=10)
plt.xticks(rotation=30, horizontalalignment="right")
plt.ylabel("Absolute values", fontsize=10)
plt.title("Primary Focus Areas distribution", fontsize=10, loc="right")
图片作者。
关于兴趣/学习领域,与读写和语言相关的科目是提交最多的科目,其次是几乎一半的数学和科学申请。音乐与艺术、应用学习、特殊需求、历史与公民、健康与体育是要求较少的重点领域。
12.成绩水*会影响兴趣/学习领域吗?
# Understanding how primary focus areas are distributed through the grade levelsplt.figure(figsize=(16,8))ax = sns.countplot(x="grade_level", hue="primary_focus_area", data=subject, dodge=True)plt.legend()
plt.xlabel("Grade levels", fontsize=10)
plt.xticks(rotation=0, horizontalalignment="center")
plt.ylabel("Absolute values", fontsize=10)
plt.title("Primary focus areas distribution by grade levels", fontsize=10, loc="right");
图片作者。
不出所料,无论年级高低,研究的重点领域没有明显变化。
数据集:论文
ess.shape
>> (664098, 6)ess.head()
图片作者。
# Describe missing values
null_val(ess)
图片作者。
词云
Wordcloud 是一种显示文本串中最常见和最频繁出现的单词的可视化类型,其中单词的大小与其频率成比例。
13.论文数据集中出现频率最高的单词是什么?
让我们定义一个函数来创建一个单词云,在每一系列文章的数据集中突出并标出最常见的单词。
def plot_wordcloud(wc, otherWords=""):ess[wc].dropna(inplace=True)
text = " ".join(ess[wc][~pd.isnull(ess[wc])])
wordcloud = WordCloud(width=3000, height=2000, random_state=1, background_color="black", colormap="Set2", collocations=False, stopwords=STOPWORDS.update(otherWords)).generate(text)
fig = plt.figure(figsize=(14,14))
plt.imshow(wordcloud)
plt.title("Wordcloud with %s content" % wc, fontsize=12, loc="right")
plt.axis("off")
plt.show()
标题
plot_wordcloud("title", ["need","Help","Classroom"])
WordCloud:标题内容。图片作者。
标题中出现最频繁的词是书,阅读,技术,阅读,科学,数学,图书馆,年级,读写能力,学校,Ipad。
简短描述
plot_wordcloud("short_description", ["will", "class", "allow", "able", "one", "year", "many", "time", "remember", "use", "day", "way"])
WordCloud:简短描述。图片作者。
在简短描述中出现最频繁的词是学生、学校、书籍、阅读、技术、学习、技能、学习、教学、儿童、材料、数学、计算机。
需求陈述
plot_wordcloud("need_statement",["need", "student", "students", "fulfillment", "including", "help", "donorschoose", "org", "shipping,", "set", "new", "will"])
WordCloud:需要声明。图片作者。
需求陈述中出现最频繁的词是书、阅读、数学、成本、技能、项目、中心、材料、HTML、科学、游戏、技能、相机、iPad。
数据集:资源
基本信息
How many features are available?
How many observations are in the dataset?
Are there any duplicated records?
Are there any missing values?
What type of variables are there?
How many variables of each type?# Observations, features, duplicates, datatypes and proportionsshape_df(res)>> output:Number of observations: 3667217
Number of variables: 9
Number of duplicates: 0
Are there any missing values? True
-----
vendorid float64
item_unit_price float64
item_quantity float64
resourceid object
projectid object
vendor_name object
project_resource_type object
item_name object
item_number object
dtype: object
------
Datatypes' proportion:
float64 3
object 6
dtype: int64# Describe missing values
null_val(res)
图片作者。
res.head()
图片作者。
res.describe(include="all").round(1)
图片作者。
超过 150 万本书是最常见的资源类型。
*均项目单价为 180 美元,中位数为 14 美元→这表明存在一些严重的异常值。此外,至少可以说,数据集中出现的一些负值很尴尬。
*均数量为 3.3 个单位,中位数为 1。Q1 和 Q3 表明分布良好,但需要处理那些异常值。
14.每种相应类型的资源总共获得了多少捐赠?
要回答这个问题,我们需要计算每种可用资源的项目数量和项目价格。
项目数量
res["item_quantity"].value_counts(dropna=False)1.0 2304849
2.0 279693
3.0 108075
5.0 81142
4.0 80076
...
315.0 1
10500.0 1
313.0 1
309.0 1
121.0 1
Name: item_quantity, Length: 313, dtype: int64
---res.item_quantity.value_counts(normalize=True,bins=15).round(6)(-993.1089999999999, 66207.2] 0.997734
(926900.8, 993108.0] 0.000000
(860693.6, 926900.8] 0.000000
(794486.4, 860693.6] 0.000000
(728279.2, 794486.4] 0.000000
(662072.0, 728279.2] 0.000000
(595864.8, 662072.0] 0.000000
(529657.6, 595864.8] 0.000000
(463450.4, 529657.6] 0.000000
(397243.2, 463450.4] 0.000000
(331036.0, 397243.2] 0.000000
(264828.8, 331036.0] 0.000000
(198621.6, 264828.8] 0.000000
(132414.4, 198621.6] 0.000000
(66207.2, 132414.4] 0.000000
Name: item_quantity, dtype: float64
通过对 15 个存储桶中的项目进行聚合,我们发现 99%的数据集中在值-993 和 66207 之间。
绘制“项目数量”特征的经验累积分布函数(ECDF) 并检测变量中异常值的存在。它显示了数据中比 x 轴更大和更小的点所占的百分比:确实非常高。
# Plotting ECDFx = np.sort(res["item_quantity"].dropna())
y = np.arange(1, len(x)+1) / len(x)
plt.plot(x,y,marker = ".", linestyle = "none")
plt.title("ECDF Item Quantity",loc="right");
图片作者。
让我们寻找那些遥远的离群值。
value = res.item_quantity.quantile([.9999995])
p = value.iloc[0]
res[res["item_quantity"] > p]
图片作者。
我已经决定放弃“SWATCH BOOK-SCHOOL SMART CONST 09506”(也没有价格)项目,保留所有其他项目。
# Removing outlierres = res[res["item_quantity"] < 993108].copy()
res
图片作者。
res["item_quantity"].describe().round(2)count 3658906.00
mean 3.03
std 11.75
min 0.00
25% 1.00
50% 1.00
75% 2.00
max 10500.00
Name: item_quantity, dtype: float64
---res["item_quantity"].value_counts(dropna=False)1.00 2731320
2.00 317590
3.00 120462
4.00 87816
5.00 85564
...
2.99 1
274.00 1
10500.00 1
268.00 1
290.00 1
Name: item_quantity, Length: 316, dtype: int64
---# Next step is to deal with np.nan, 0.00 and 2.99 valuesres[res["item_quantity"] == 2.99]
图片作者。
# Checking for items with no quantity records
(res[res["item_quantity"] == 0]).head()
图片作者。
解决这两个问题的方法是用中值代替上述所有异常值。
# Replacing anomalous values by np.nanres["item_quantity"] = res["item_quantity"].replace(2.99,3)
res["item_quantity"] = res["item_quantity"].replace(0,np.nan)# Replacing the outliers by the medianres["item_quantity"] = res.item_quantity.fillna(res.item_quantity.median())
---res.item_quantity.describe().round(1)count 3658906.0
mean 3.0
std 11.8
min 1.0
25% 1.0
50% 1.0
75% 2.0
max 10500.0
Name: item_quantity, dtype:float64
项目价格
# Plotting ECDFx = np.sort(res.item_unit_price.dropna())
y = np.arange(1, len(x)+1) / len(x)
plt.plot(x,y,marker = ".", linestyle = "none")
plt.title("ECDF Item Price",loc="right");
图片作者。
re = res.copy() # Make a copy of the dataset# Dealing with the outliersQ1 = re["item_unit_price"].quantile(0.25)
Q3 = re["item_unit_price"].quantile(0.75)
IQR = Q3 - Q1re["item_unit_price_"] = re.item_unit_price[~((re.item_unit_price < (Q1 - 1.5 * IQR)) |(re.item_unit_price > (Q3 + 1.5 * IQR)))]# Displaying data dispersion without the outliersprint(re.item_unit_price.describe().round(1))
print("")
print(int(re.item_unit_price.describe()[0]) - int(re.item_unit_price_.describe()[0]),"outliers were identified with this operation")count 3653295.0
mean 179.8
std 234667.6
min -99.6
25% 6.3
50% 14.0
75% 36.0
max 448421569.0
Name: item_unit_price, dtype: float64>> 488446 outliers were identified with this operationre.item_unit_price_.value_counts(dropna=False)NaN 494057
29.950 56527
6.290 49729
19.950 43492
3.590 42383
...
3.321 1
11.331 1
7.551 1
62.150 1
78.540 1
Name: item_unit_price_, Length: 8199, dtype: int64
---# Imputing outliers by the medianres["item_unit_price"] = re["item_unit_price"].fillna(res.item_unit_price.median())# Removing negative valuesres = re[re["item_unit_price_"] > 0].copy()
res.drop(["item_unit_price_"],axis=1,inplace=True)res.describe().round(2)
图片作者。
# Displaying results without the outliersplt.hist(res.item_unit_price, bins=50)
plt.title('Numeric variable \'Item unit price\'', fontsize=12, loc="right")
plt.ylabel('Absolute Frequency')
plt.show()
图片作者。
项目 _ 资源 _ 类型
r = res[["projectid","project_resource_type","item_unit_price","item_quantity"]].copy()r["amount"] = r.apply(lambda x: x["item_unit_price"] * x["item_quantity"], axis=1)r.head()
图片作者。
# Scatter plot: relationship between Item Unit Price and Item Quantityfig = plt.figure(figsize=(9,6))
ax = fig.add_subplot(111)
ax.scatter(r["item_quantity"],r["item_unit_price"])
ax.set_xlabel("Iten Quantity")
ax.set_ylabel("Item Unit Price")
ax.set_title("Relationship of Item Unit Price vs. Item Quantity", fontsize=10,loc="right")
ax.set_xlim(0,650);
图片作者。
散点图显示,物品越贵,需求的数量越少。
# Bar plotax = r.groupby(["project_resource_type"])["amount"].count().sort_values(ascending=False).plot(kind="bar", figsize=(9,6), fontsize=12, color=sns.color_palette("rocket"), table=False)for p in ax.patches:
ax.annotate("%.2f" % p.get_height(), (p.get_x() + p.get_width() / 2., p.get_height()), ha='center', va='center', xytext=(0, 7), textcoords='offset points')plt.xlabel("project resource type", fontsize=10)
plt.xticks(rotation=30, horizontalalignment="right")
plt.ylabel("Absolute values", fontsize=10)
plt.title("Distribution of project resources by amounts", fontsize=10, loc="right")# side tableamount_ = r.groupby(["project_resource_type"])["amount"].count().sort_values(ascending=False).to_frame()amount_["percent"] = (amount_.apply(lambda x: x/x.sum())*100).round(2)display(amount_.T)
图片作者。
每种资源的总量表明,书籍是最昂贵的资源(累计),其次是补充材料和技术,排在第三位。
数据集:资源+成果+捐赠
合并数据帧
# Merging dataframes Resources and Outcomes, left joindf_m0 = pd.merge(pro,out,how="left",on="projectid")
df_m0.dropna(inplace=True)
---df_m0.drop(columns={"school_ncesid","school_latitude","school_longitude","school_zip","school_county","school_charter","school_magnet","school_year_round","school_nlns","school_kipp","school_charter_ready_promise","teacher_teach_for_america","teacher_ny_teaching_fellow","eligible_double_your_impact_match","eligible_almost_home_match","at_least_1_green_donation","great_chat"}, inplace=True)
---# Merging the above merged dataframe with Donations, left joindf = pd.merge(df_m0,don,how="left",on="projectid")
df.dropna(inplace=True)
---display(df.shape)
>> (467107, 52)
15.*均而言,在哪些州,个人捐赠者的捐赠更集中?
让我们回过头来了解在哪些州提交的项目更集中。所选的“前 5 名”学校州几乎占所有项目开放的 50%。
# Checking feature "school_state" regarding higher concentration of project openings# Bar plotax = schools_.school_state.value_counts(ascending=False)[:5].plot(kind="bar", figsize=(9,6), fontsize=12, color=sns.color_palette("rocket"), table=True)# Paramsplt.xticks(rotation=0, horizontalalignment="center")
plt.ylabel("Absolute values", fontsize=10)
plt.title("States with higher schools' concentration", fontsize=10,loc="right")display(ss.head(5).T)ax.axes.get_xaxis().set_visible(False)
table = ax.tables[0]
table.set_fontsize(10)
table.scale(1,1.7)
图片作者。
检查关于项目捐赠高于*均水*的集中度的特征“学校 _ 州”。
state_schools = df.groupby("school_state")["donation_total"].mean()# Bar plotax = df.groupby("school_state")["donation_total"].mean().sort_values(ascending=False).head().plot(kind="bar", figsize=(9,6), fontsize=12, color=sns.color_palette("rocket"), table=False)# Paramsplt.xticks(rotation=0, horizontalalignment="center")
plt.ylabel("Absolute values", fontsize=10)
plt.xlabel("States with higher schools' concentration", fontsize=10)
plt.title("States with schools receiving donations above avegrage", fontsize=10,loc="right")
plt.axhline(df.groupby("school_state")["donation_total"].mean().mean(), linewidth=2, color ="r")
plt.tight_layout()display(ss.head(5).T)
图片作者。
各州项目捐赠的*均值是多少?
df.groupby("school_state")["donation_total"].mean().mean()
>> 111.75994027601705state_schools.plot.line();
图片作者。
# Describestate_schools = df.groupby("school_state")["donation_total"].mean()
display(state_schools.describe().to_frame()) # Describe# Plot "total_price_excluding_optional_support"f, (ax_box, ax_hist) = plt.subplots(2, sharex=True, gridspec_kw= {"height_ratios": (0.2, 1)})mean=state_schools.mean()
median=state_schools.median()sns.boxplot(state_schools, ax=ax_box)
ax_box.axvline(mean, color='r', linestyle='--')
ax_box.axvline(median, color='g', linestyle='-')sns.distplot(state_schools, ax=ax_hist)
ax_hist.axvline(mean, color='r', linestyle='--')
ax_hist.axvline(median, color='g', linestyle='-')plt.legend({'Mean':mean,'Median':median})
ax_box.set(xlabel='')
plt.show()
16.各州申请的项目数量与各州捐助方数量之间是否存在关联?
我们只需按州获得捐款,并按个人捐款人的数量进行分组,然后按降序对值进行排序。
high_donors = df.groupby("donor_state")["donor_acctid"].count().sort_values(ascending=False).to_frame()high_donors.head()
图片作者。
让我们从前面的计算中获取关于最高提交项目请求的统计数据。
high_states.head().to_frame()
图片作者。
连接两个数据集。
zaq = pd.concat([high_states, high_donors], axis = 1)zaq.rename(columns={"school_state":"Requests", "donor_acctid":"Donations"}, inplace=True)zaq.head()
图片作者。
显示按州提交的项目与按州捐赠的项目之间的相关性。
# Drop nans
zaq.dropna(inplace=True)
---# Correlations
zaq.plot(x="Requests", y="Donations", kind="scatter", figsize=(9,6), fontsize=12)
plt.title("Projects requested by donations made", fontsize=10,loc="right")
plt.xticks(rotation=0,horizontalalignment="center")
plt.ylabel("Donations made",fontsize=10)
plt.xlabel("Requested projects",fontsize=10)
各州申请的项目数量与各州捐助者数量之间似乎存在正相关关系。
# Calculating corr. coef. between Requests and Donations
zaq["Requests"].corr(zaq["Donations"])
>> 0.9689520473560519
17.提交的项目以哪些领域为目标?
# Checking "primary focus areas" regarding requested projects
# Bar plotax = df.primary_focus_area.value_counts(ascending=False).plot(kind="bar", figsize=(9,6), fontsize=12, color=sns.color_palette("rocket"), table=False)for p in ax.patches:
ax.annotate("%.2f" % p.get_height(), (p.get_x() + p.get_width() / 2., p.get_height()), ha='center', va='center', xytext=(0, 7), textcoords='offset points')plt.xlabel("Primary Focus Areas", fontsize=10)
plt.xticks(rotation=30, horizontalalignment="right")
plt.ylabel("Absolute values", fontsize=10)
plt.title("Primary Focus Areas distribution", fontsize=10, loc="right")
文化与语言、数学与科学、音乐与艺术、特殊需求、应用学习、历史与公民、健康与体育。
18.这项研究的哪些领域获得了更多的捐赠?
从商业角度来看,哪些主要关注领域对捐赠者更有吸引力?
prisubject_donor = df.groupby("primary_focus_area")["donation_to_project"].sum().sort_values(ascending=False).to_frame().copy()prisubject_donor.round(2)
prisubject = df.primary_focus_area.value_counts().sort_values(ascending=False).to_frame()prisubject
19.对项目的捐赠和主要关注领域之间有什么关联吗?
让我们连接两个数据集,并显示项目的主要关注领域及其捐赠之间的相关性。
cde = pd.concat([prisubject, prisubject_donor], axis=1) #Concatenatecde.rename(columns={"school_state":"Requests", "donor_acctid":"Donations"}, inplace=True)cde.round(2).head()
cde.dropna(inplace=True) #Drop nans#Correlationscde.plot(x="primary_focus_area", y="donation_to_project", kind="scatter", figsize=(9,6), fontsize=12)plt.title("Primary focus area projects requested and its donations", fontsize=10, loc="right")
plt.xticks(rotation=0, horizontalalignment="center");
有一个非常强的正相关关系(99%) →捐助者更愿意支持扫盲和语言、数学和科学、音乐和艺术等领域,而不是特殊需求或应用学习。
# Correlation
cde.primary_focus_area.corr(cde.donation_to_project)
>> 0.9917272663581518
20.*均而言,一个项目需要多长时间才能获得全额资助?
df[["projectid","date_posted","donation_timestamp"]].head()
图片作者。
# Filtering fully funded projectdft = (df[df["fully_funded"] == "t"]).copy()
---# Converting column (object --> datetime)dft["donation_timestamp"] = pd.to_datetime(dft["donation_timestamp"], format="%Y-%m-%d")
---# displaying date onlydft["donation_timestamp"] = dft["donation_timestamp"].dt.date
dft["donation_timestamp"] = pd.to_datetime(dft["donation_timestamp"], format="%Y-%m-%d")
---# Calcultaing durations (in days) and coverting into int64dft["funding_span"] = dft.donation_timestamp - dft.date_posted
dft["funding_span"] = dft.funding_span.dt.days
---# Counting the days until fully fundedstates_funding_proj = dft.groupby(["school_state","projectid"])["funding_span"].mean()
states_funding_projschool_state projectid
AK 02059528bc746a40214a5fd9cb5e9d6b 66.000000
0213fbd06fb04ca6a797da6ae0040cb1 0.000000
02158d40acc0744af7d7584531092f07 99.066667
0257042b0165752bc3e686cc90665632 13.000000
02ad156dee38b083f364c8164f7f9b2f 0.000000
...
WY fb0296ec6586036457750d0b1b38a49d 101.000000
fc9c54832601b2ed0ba54237b80de254 27.500000
fce5bf5cd0ae40947a1152bd1b834604 41.500000
fd69421691ce38787a90ce9dac747477 24.500000
fe450a764037db85093d0511315ba944 1.000000
Name: funding_span, Length: 173512, dtype: float64
---states_funding_avg = states_funding_proj.groupby(["school_state"]).mean()avg_days = states_funding_avg.mean()
print(f"Fully funding average days:", states_funding_avg.mean())>> Fully funding average days: 40.45127526109651---# Average days until fully funded projects by state
# Bar plotax = states_funding_proj.groupby(["school_state"]).mean().sort_values().plot(kind="barh", figsize=(12,9), fontsize=12, color=sns.color_palette("rocket"), table=False)# Paramsplt.xticks(rotation=0, horizontalalignment="center")
plt.ylabel("States", fontsize=10)
plt.xlabel("Days until fully funded projects", fontsize=10)
plt.title("Average days until fully funded projects by state", fontsize=10, loc="right")
plt.axvline(avg_days, linewidth=2, color ="r")
plt.tight_layout()
图片作者。
41 天是项目获得全额资助的*均时间。
上图区分了比*均天数早得到资助的州和晚得到资助的州。
接下来,低于*均水*的州:
states_funding_avg[states_funding_avg < avg_days].sort_values(ascending=False)school_state
WA 40.141406
ID 40.066648
NJ 39.942280
GA 39.741997
TX 39.658309
NV 39.577678
NY 39.497603
AR 39.418404
AZ 39.395866
NM 39.259313
NH 39.224946
CT 39.036108
IL 39.022429
PA 38.834201
MA 38.724716
MN 38.491491
OR 38.392444
FL 38.354962
MT 37.189795
CO 36.780902
KY 35.623806
WV 35.029220
MO 34.963707
CA 34.799367
TN 32.973356
DC 32.016388
HI 31.547774
DE 31.091836
AK 30.834884
Name: funding_span, dtype: float64
*均天数以上的州:
states_funding_avg[states_funding_avg >= avg_days].sort_values(ascending=False)school_state
MS 53.589771
AL 50.309404
SC 48.911828
RI 48.640019
WY 48.480016
NC 46.838167
MI 46.655316
IA 46.202405
VA 45.981566
LA 44.471631
ME 43.272701
ND 42.832920
VT 42.401578
OH 42.291703
MD 42.054127
NE 41.993862
IN 41.962964
UT 41.597421
WI 41.588098
OK 41.471277
KS 40.946715
SD 40.889714
Name: funding_span, dtype: float64
总结
1-教师简介是什么?女性 87% {Mrs: 47%,Ms: 40%},男性 13%。
2-最常见的提交期限是什么时候?
2.1-在哪些工作日?星期日,星期六,星期二,星期一,,星期三,星期四(已排序)。
2.2-在哪几个月?设定,10 月,1 月,8 月,2 月,12 月,11 月,3 月,4 月,7 月,5 月,6 月
2.3——这些年的整体演变是怎样的?增长呈指数增长。
3-哪些大都市获得了更多提交?城市 53%,郊区 23%,农村 12%,未知 12%。
4-哪个州的项目最集中?接下来的“前 5 名”代表了将* 50%:加利福尼亚、纽约、北卡罗来纳、伊利诺伊和得克萨斯。
5-哪个城市的哪个学校影响更大?有城邦关系:芝加哥、洛杉矶、布鲁克林、布朗克斯、纽约。
6-哪种类型的资源请求要求最高?物资、技术、书籍、其他、旅行、访客(已排序)。
学生的成绩是怎样的?趋势很明显:越年轻的学生,在线请求的数量越多。
8-级别和申请的资源类型之间有什么关系吗?不管等级高低,趋势都是一样的:供应品、技术、书籍、其他、旅行、访客(已分类)。
9-贫困水*会影响申请资金的趋势吗?脆弱性越高,申请资助的项目金额越大。
10-在大都市安置和贫困水*方面,申请项目的分布有趋势吗?最常见的是属于高贫困水*城市地区的学校。
11-就研究领域而言,哪些是提交最多的捐赠?识字&语言、数学&科学、音乐&艺术、应用学习、特殊需求、历史&公民学、健康和体育。
12-成绩水*会影响兴趣/学习领域吗?不出所料,无论年级高低,学习的重点领域没有明显变化。
13-论文数据集中出现频率最高的单词是哪些?
a)标题:书,阅读,科技,阅读,科学,数学,图书馆,年级,识字,学校,Ipad。 b)简短描述:学生、学校、书籍、阅读、技术、学习、技能、材料、数学、计算机。
c)需求陈述:书籍、阅读、数学、成本、技能、项目、中心、素材、HTML、科学、游戏、技能、相机、iPad。
14-每种相应类型的资源共收到多少捐款?书籍(511957),用品(1199005),技术(243412),其他(206560),旅行(520),访客(181)。
15-*均而言,在哪些州个人捐赠者的捐赠更集中?AR,MT,RI,OK,NV。
请求的数量和捐赠者之间有什么联系吗?有正相关。
提交的项目针对哪些领域?识字&语言、数学&科学、音乐&艺术、特殊需求、应用学习、历史&公民学和健康&体育。
研究中的哪些领域获得了更多捐赠?识字&语言、数学&科学、音乐&艺术、应用学习、特殊需求、历史&公民学、健康&体育(已排序)。
19-对项目的捐赠和主要关注领域之间有什么关联吗?有非常强的正相关:99%。
*均而言,一个项目需要多长时间才能获得全额资助? 41 天。
埃达总结道。
- 你可以在这里找到完整的代码。
- 数据集可以在这里 下载 。
- 卡格尔争夺 预测 DonorsChoose.org的兴奋
查看您可能也会喜欢的其他文章:
** </15-business-questions-about-mobile-marketing-campaigns-roas-return-on-ad-spend-ff636a8095b6>
感谢阅读。**
不要问量子计算能为机器学习做什么
问机器学习能为量子计算做什么
作者绘制的图像
量子计算和机器学习是我们这个时代的热门词汇。除了明确的炒作,还有一些真实的依据。随着传统计算的进步,我们能够在基于机器学习和其他领域的图像相关分析中取得令人难以置信的结果。另一方面,量子物理学一直是某种困难的神秘领域,导致数学的惊人进步(以及许多完全不科学的伪科学)。量子计算正在成为克服传统计算的一些限制的新途径,包括小型化晶体管的物理限制。去年,人们已经将量子计算作为神经网络的层或者作为贝叶斯天真分类器对待。
请点击此处了解更多信息:
https://medium.com/illumination-curated/is-quantum-machine-learning-the-next-thing-6328b594f424
2020 年 3 月,谷歌宣布发布tensor flow Quantum——一系列结合了最先进的机器学习和量子计算算法的工具。
总之,这个想法是使用量子计算作为分类系统中的一个步骤,但是我们也可以反过来想。
量子机器学习分类器的典型设置。作者图片。
反过来,Q-CTRL 是一家位于悉尼和洛杉矶之间专注于量子计算的公司的先驱。
铺*的道路是专注于量子计算,并有效地使用机器学习来抑制噪声和量子硬件缺陷的影响。
由于噪音的影响,大多数量子计算机硬件可以在不到一毫秒的时间内执行计算,然后需要重置,目前这还不如一台低成本的笔记本电脑。结果比听起来要糟糕得多,在下一节中会有更好的解释。
脱散
当量子计算机中经典二进制位的量子版本 qu 位暴露于硬件噪声时,其中的信息非常容易退化。这个过程被称为消相干。这是我们仍然处于量子计算初级阶段的原因之一。在下面的截屏中,我记录了一个理想情况下的单个量子位估计,在硬件噪声的影响下也是如此。从一个量子位来看,这似乎并不悲惨,但想象执行一项任务所需的所有量子位,可以让我们知道与 RaspberryPi 或手机相比,结果会有多嘈杂。
1 量子位预期结果来自(作者截屏)
噪声导致 1 个量子位(作者截屏)
我们如何解决退相干?自 90 年代末以来,像安德鲁·斯蒂安和 T2·彼得·肖这样的人已经提出了通过引入某种冗余来补偿它的模型,这在我们当前的量子计算机中是不可行的,如果你想象大量的比特(你将需要重复每个量子比特几次)。
Q-CTRL 的解决方案是基于机器学习创建固件,可以修复退相干,而不需要额外的不可行的硬件。
量子计算硬件基于光-物质相互作用(光学硬件)来执行量子逻辑操作。这些电磁信号的组成实际上是可以由机器学习工具定义/提炼的算法。这个笨重的圆圈应该可以减少退相干。为了真正理解这种方法,需要一个典型的机器学习专家所不具备的量子计算知识。我尽我最大的努力在下面的部分进行总结。
Q-CTRL 解决方案被称为 BOULDER OPAL,它是一个 Python 包,可以通过在终端中键入来轻松安装
pip 安装质量控制
并简单地将其导入为
从质量控制导入质量控制
剩下的如何设置哈密顿量,移相,控制等等……是一个独立的话题(如果你感兴趣,你可以从下面的教程中学习或者阅读文档)。关键的方面是,为了实现控制,从而降低噪声,复杂的基于梯度的优化可以通过使用 TensorFlow 或其他机器学习工具来实现,这将在下面关于强化学习的章节中讨论。
强化学习
在可用于控制噪声的优化中, 强化学习 已经被成功利用。强化学习是机器学习的一个领域,其中智能代理在一个环境中采取行动,以最大化一个累积 奖励 。
量子硬件中强化学习的整体观点。图片改编自 iStock
通过量子计算中的强化学习,学习者通过用量子设备本身做实验来创建优化的脉冲。此外,强化学习可以发现和利用我们不知道的新的物理机制。然而,这样做的缺点是,学习者不能告诉你解决方案是如何找到的,所以我们不知道设备中噪声抑制的物理原理。
对于那些更习惯于机器学习而不是量子计算的人,我将在量子物理学和强化学习中使用的术语之间建立一座桥梁:量子计算机被认为是学习代理的环境。代理的任务是实现执行高保真度门控的目标。一个代理能够对环境做出各种动作(在我们的例子中,是对量子计算机施加脉冲)。
代理通过使用一组可测量的可观察值和基于其与目标接*程度的奖励来学习达到其目标。我们的奖励来源于闸门保真度。学习算法在多次实验后使用这些信息来提高代理的性能。
总结
- 为了理解环境和状态,代理向量子计算机部署一系列脉冲。
- 然后,代理获取该状态,并使用该信息来决定下一步要采取的操作。
在实践中,代理获取状态,并使用神经网络来决定对脉冲的下一段采取什么行动。我们量化脉冲的幅度,以便学习者从有限的选项集中进行选择。
一个完整的门脉冲被称为一个事件,在事件结束时,由国家给代理人一个奖励(在强化学习术语中)。这允许我们将误差信号提升到测量噪声之上。
前述强化学习可以用各种各样的学习器来执行,包括深度策略梯度、深度确定性策略梯度孪生以及软行动者批评。所有这些学习者都有超参数,在用于真正的实验之前必须进行调整
实验可以在 IBM 的量子计算机上运行,结合众所周知的量子计算工具(如 QSkit )和机器学习工具(如 SciKit-learn )。
这种基于机器学习的优化量子计算方法已经证明可以减少硬件错误并提高门保真度:
鸣谢:Q-CTRL/悉尼大学。2017
参考
T. Jaksch,R. Ortner,P. Auer《强化学习的*似最优后悔界限》
如果你愿意,可以和我联系
https://www.linkedin.com/in/alecrimi/
不要认为 NumPy.vectorize 更快
用不同的向量化函数解决生日悖论
照片由 Adi Goldstein 在 Unsplash 上拍摄
循环是您在编程中学习的第一个构造之一,但是对数组中的每个元素执行操作时,循环可能是一种过于冗长的方式。NumPy 提供了一个叫做vectorize
的便捷方法,用更少的代码行对数组执行操作。许多人认为这个函数也能提高性能。在本文中,我将向您展示如何使用vectorize
(这部分很简单),以及它是否能提高性能。我将通过使用两个非常不同的 Python 函数解决一个常见的统计学问题(生日悖论)来实现这一点。
在我进入实现之前,解决争议是很重要的。同样,一些人观察到vectorize
比普通的for
循环要快,但是甚至 NumPy 文档也声明:
“
**vectorize**
功能主要是为了方便,而不是为了性能。该实现本质上是一个 for 循环。
文档是否正确?我将向你介绍我的实验。在本文的最后,我将发布 Jupyter 笔记本,其中包含为本文执行的所有代码和测试,因此您可以复制我的发现。在讨论发现之前,我先解释一下这个例子中用到的问题:生日悖论。
生日悖论解释
在一群人中,两个或两个以上的人同一天生日的概率是多少?当小组由两个人组成时,它是相当小的(大约百分之一的四分之一),如果小组由 366 人组成,它就变成 100%(忽略闰年)。2 到 366 人之间的概率是什么样子的?这就是生日悖论,有时被称为生日问题。
悖论检验了“两个或更多”人同一天生日的概率。这当然意味着三个人可能有同一天生日,如下图所示。或者四个人…
同一天生日的三个人——作者图片
或者两个集合的人可能有相同的生日,而集合本身彼此不同,如下所示。
两组相同的生日——作者图片
两个或更多 的人可能有很多相同的生日,因此更容易计算出的补数,或没有人有相同生日的概率。
我不打算深入讨论得出数学解的细节,因为《走向数据科学》的撰稿人 Cassie Kozyrkov 已经在本文中介绍过了。我将直接跳到解决方案(数学方程和相应的 Python)。
解决方案 1:科学的方法
第一种解决方案使用一种不寻常的符号(下标在 P 的两侧)来表示排列。读作“365 选 k ”
好消息是 Python scipy
库有一个排列函数,可以产生答案!Python 代码远没有上面的等式那么可怕。还有什么比一行函数更简单的呢?
*import scipy.special as sppdef bday_scipy(k):
return 1 - spp.perm(365,k) / 365**k*
解决方案 2:“基本”方法
第二种解决方案是一种基本的暴力方法。它由随着组大小( k )的增加而重复相乘的分数组成。缩小的分数表明,随着群体规模的增加,没有人同一天生日的概率降低(因此两个或更多人同一天生日的机会增加)。
这个解决方案的 Python 表示比第一个解决方案要长,但是很容易理解。
*def bday_basic(k):
p = 1.
for idx in range(1,k):
p *= (365-idx) / 365
return 1-p*
比较两种解决方案
我上面提出的两个方程在数学上是相同的。这些术语被不同地分组以突出不同的模式,但是它们是相同的。如果这是杰拉尔德·兰博教授的课,我会用数学方法证明。相反,我将用图表证明它。
下图显示两个函数产生相同的结果。我还比较了这些图表中的值,发现差异在零和2.22e-16
(本质上是零)之间。换句话说,这两个函数产生 同样的结果。
同一个问题的两种解决方案会产生相同的结果——作者图片
这两种方法在数学上是相同的,但是有不同的 Python 实现。这正是我想要的,以便比较性能。
用 NumPy 向量化生日悖论
同样,我对同一个问题有两种不同的方法:
*import scipy.special as sppdef bday_scipy(k):
return 1 - spp.perm(365,k) / 365**kdef bday_basic(k):
p = 1.
for idx in range(1,k):
p *= (365-idx) / 365
return 1-p*
在使用上述函数的矢量化版本之前,我必须创建矢量化版本。这很简单:
*import numpy as np
scipy_vect = np.vectorize(bday_scipy)
basic_vect = np.vectorize(bday_basic)*
现在我有了两个“普通的”Python 函数和它们的向量等价物。这些向量可以应用于 NumPy 数组而无需循环。下面的代码创建一个表示组大小的整数数组,并计算该组中两个或更多人同一天生日的概率。这里没有for
循环!
*group_size = np.arange(2,60,dtype='uint')# calculate probability for the entire range of group sizes
scipy_probability = scipy_vect(group_size)*
和
*basic_probability = basic_vect(group_size)*
下面是basic_probability
的样子:
*array([0.00273973, 0.00820417, 0.01635591, 0.02713557, 0.04046248,
0.0562357 , 0.07433529, 0.09462383, 0.11694818, 0.14114138,
0.16702479, 0.19441028, 0.22310251, 0.25290132, 0.28360401,
0.31500767, 0.34691142, 0.37911853, 0.41143838, 0.44368834,
0.47569531, 0.50729723, 0.53834426, 0.5686997 , 0.59824082,
0.62685928, 0.65446147, 0.68096854, 0.70631624, 0.73045463,
0.75334753, 0.77497185, 0.79531686, 0.81438324, 0.83218211,
0.84873401, 0.86406782, 0.87821966, 0.89123181, 0.90315161,
0.91403047, 0.92392286, 0.93288537, 0.9409759 , 0.94825284,
0.9547744 , 0.96059797, 0.96577961, 0.97037358, 0.97443199,
0.97800451, 0.98113811, 0.98387696, 0.98626229, 0.98833235,
0.99012246, 0.99166498, 0.99298945])*
从编码的角度来看,NumPy vectorize
很方便,因为它非常简洁。但是它的表现如何呢?矢量化的函数比非矢量化的快吗?或者 NumPy 文档是否正确?
比较向量化和非向量化函数的性能
对于本文前面定义的生日悖论,我有两个解决方案:
bday_scipy
bday_basic
我还创建了每个的 NumPy 矢量化版本:
scipy_vect
basic_vect
他们如何比较?什么更快?在上一节中,我向您展示了如何使用矢量化函数。您已经知道如何将“普通”(非矢量化)Python 函数放入一个for
循环中:
*probability = []
for k in group_size:
probability.append(bday_scipy(k))*
和
*probability = []
for k in group_size:
probability.append(bday_basic(k))*
为了比较性能,我将group_size
定义为一个数组长度为 100,000 的 NumPy 随机整数数组,并使用 Python 的timeit
多次执行四种方法中的每一种,以绘制*均性能图。
请参考下图了解结果。我发现scipy
矢量化函数比非矢量化函数稍微快一点,如下图中橄榄色条所示。重点是轻微的。在下图所示的试验中,矢量化函数的速度提高了 4%。它并不像其他人有时声称的那样快 2 倍或 100 倍。
我发现basic
矢量化的函数比非矢量化的函数稍慢,如上图中紫色条所示。实际上慢了 6%!同样,这些差异是轻微的(不是两倍慢)。
性能结果—按作者分类的图片
我多次执行这些测试,矢量化和非矢量化函数之间的差异并不大,但是测试之间的关系都与上图相似。
请注意,紫色条的高度都是橄榄色条的一半。换句话说,两个版本的“基本”解决方案都比两个版本的“科学”解决方案快一倍!
结论
这个精心设计的实验的结果留给我一个相当沉闷的结论,即 NumPy 文档是正确的。
“
vectorize
功能主要是为了方便,而不是为了性能。该实现本质上是一个 for 循环。
我同意 Chelsea Troy 在这篇非常透彻的博客中的观点,NumPy 本身使用了一些针对 C 语言高度优化的循环结构,但是 NumPy vectorize
函数显然没有而不是使用并行化。**
这两种算法之间的性能差异要比函数的矢量化版本和非矢量化版本之间的性能差异大得多。“基本”解决方案每次完成的时间不到“科学”解决方案的一半。这突出了选择正确的算法可能对性能有最大的影响。
如果您关心性能,请投入时间寻找合适的算法。如果你关心代码的优雅,使用numpy.vectorize
。我个人喜欢两样都做!
正如所承诺的,这里是本文使用的所有代码:
不要成为一个基本的编码者,使用 5 种可能性来避免糟糕的切换情况
您编写真正灵活和可伸缩代码的方法
图片由作者
您想到的第一个问题是:为什么要避免 switch-case 结构?
想象你正在做你梦想中的项目,一切都很好。你不断地实现特性。但是你突然想到:
为什么我会不断地向任何开关盒添加新盒?
为什么我要不断修改以前运行良好的代码?
阅读本文几个月后,您将真正能够构建健壮的、可伸缩的应用程序。代码写一次,重复使用,直到调用树烧毁。在不改变旧代码的情况下添加新功能。甚至一个可理解的 git 历史也得到保证,一个项目/游戏/应用也进化了,而不是突变了。
而这一切都是因为你已经学会了 避免使用 switch——case。
背后的原因
你可能理解开关的情况,因为他们有一个低的学习曲线,并导致相当舒适的使用。它是初级和中级程序员的默认工具集。但是使用它们也有一些缺点。
它们在创建初始用例集的过程中工作得很好,但是一旦你必须扩展它们,它们就会变得不方便并使你的代码膨胀。
此外,当您想要映射复杂的场景时,switch-case 是一团乱麻。
在我多年的 C#开发中,我大量使用了切换用例,并多次遇到切换用例的缺点:
- 不使用变量意味着更少的灵活性和硬编码情况
- 在多种情况下不使用同一个常量意味着运行时灵活性降低
- 没有使用关系表达式(==,!=,<= etc.)
- No usage of float or constants
- Not expandable by redefining the environment, need to redefine switch-case
The Fundamental Problem of Switch-Case
Let’s imagine we want to make a trading card game, and we are about to realize a fundamental part of the game.
在一个回合中从一副牌中抽牌。
因此,我们需要CardDeck
的类,以便以后从中抽牌。
CardDeck
是一个类,并且有一个字段amountOfCards
用于表示剩余要抽的牌的数量。
使用开关盒进行操作
图片来自 Pinterest 上的编码迷因
要从一个初级程序员起步,我们必须首先像一个初级程序员一样思考。
因此,第一种方法旨在使用开关盒。
Draw()
函数将检查该事实,并接受牌组和牌的类型作为参数:
每张抽牌都要告诉我们抽到了什么样的牌。将有 4 个选项:
- 怪物卡
- 魔法卡片
- 陷阱卡
- 土地卡
如果至少还有 1 张牌,我们将随机得到一个进入开关盒的条目cardTypes
。无论画了什么,调用PutCardToHand()
都会打印出类型。
现在Main
函数调用这段代码。我们首先将这个 draw 函数放在一个公共类SwitchCaseUser
中,意思是实例化:
另外,cardTypes
在这里被定义为一个字符串数组。这段代码会导致
虽然这款电源线工作正常,也达到了设计目的,但我们面临着 3 个关键问题:
- switch-case 是硬编码的:定义要在外部绘制的类型将使这个函数与这个外部世界紧密耦合。这个函数哪里知道自己在抽什么牌?外部空间的变化将改变该函数内部的情况。
- 定义可以在该函数中绘制的卡片类型会导致同样的问题,因为现在该函数明确地知道可以绘制什么。但是为什么绘图函数必须知道这些呢?绘制函数应该绘制,不管它在绘制什么。在这种情况下,卡类型的改变也意味着开关外壳的改变。
- 扩展这个行为很笨重,代码也不灵活。因为一旦我们有了一个完整的功能类,它准确地做了它应该做的事情,并且做得很好,它应该永远保持原样。我们应该以有效的代码方式编程,并且应该一直有效。如果外部行为改变,不要再触摸它。这意味着传递所有需要的引用来实现这个结果,并且独立于已经传递的内容。
避免开关盒将有助于消除所有提到的缺点。
避免开关盒— 5 种可能性
同样,我们将卡片组和卡片类型交给我们的Draw()
函数,但是这次我们不使用开关盒。相反,我们使用五种不同的方法来避免切换情况。
对于所有五种方法,函数的每个头和每个第一部分都是相同的:
if 语句中有可互换的部分:
第一个和第二个选项在避免切换的情况下保持原来的字符串数组cardTypes
。所有其他的例子都基于到一个List<string>
的转换。
这只是对开关情况的有意义替换的简短比较。如果您想要对每种技术有更深入的解释,您可以在 Robert C. Martin 的《干净的代码》一书中找到它们。
#1 For 循环/ ForEach 循环
for 循环遍历数组,将cardType
与来自cardTypes
的所有条目进行比较,并将匹配传递给PutCardToHand
函数。
#2 本地功能
局部函数的功能与 for 循环几乎相同,但它使用 forEach 循环将其封装到一个局部函数中。
一些恶意的人可能会说这只是像#1 一样的一种复杂的方式,但是这是一个展示,本地函数在更高级的用例中变得有趣。
#3 索引方法
现在将array
转换为list
。
因为有一个列表可供我们使用,所以我们可以使用一些先验函数来聚集它们。
我们的cardTypesList
现在可以访问一组功能(微软文档中的完整指南)。一个是indexOf
方法,它返回匹配我们输入的条目的索引。
将这个传递给我们的函数PutCardToHand
将会得到想要的行为。
#4 匿名方法
图片来自 Filmstarts.de
匿名的方法就像是 Wile E. Coyote 和他那晦涩的武器去抓走鹃。Meep Meep。
我们必须处理定义开销。
命名函数中的匿名函数作为参数会导致函数定义的双重麻烦。
这是一种相当沉重的编码方式,因此,以后也不容易理解。要注意!
((Func<string (inputParameter1), List<string> (inputParameter2), string (return parameter)>) anonymousFunction(inputParameter1, inputParameter2, returnParameter)
{
Lambda Expression returning cardType}(passingParameter1, passingParameter2) <- Instant Invokation
这个匿名函数的典型用法是当有一个大型数据集传入时,我们只需要在代码的这个特定部分聚合属性的子集,或者另一个典型用法是当您只需要在此时此地对数据做一次事情时。
因此,定义一个命名函数并污染你的类空间是没有必要的。
更容易理解的方法是将匿名函数存储在委托中,然后像这样调用它:
尽管如此,匿名函数应该保持简短。
# 5λ-表达式
在这种情况下,匿名函数既长又过分。我掩护你。因此,对于这类问题,lambda 表达式是简单而正确的。
看看你能多短多快得到结果。
这是一个简单的 oneliner,使用列表的Find()
方法。
关于匿名函数有效的东西在这里也是有效的,因为 Lambda 表达式是一个增强的匿名函数。
Main 中的调用—避免切换案例结尾
因为这是为了避免切换的情况,所以我将所有的选择打包到了SwitchCaseAvoider
类中。
在 main 中,我们把它叫做,就像另一个一样:
会导致
结论
并非所有这些都是处理这种特定情况的最佳方式。
如果您试图在大型数据集中使用它们来获得一个匹配,其中一些(如 for 循环或 forEach 循环)会变得计算密集型。此外,还有内置功能(例如,用于集合)。
他们的共同点是:
它们可以通过重新定义环境来扩展,而不是功能本身。
因此,有一个比处理特定问题的最佳方法更重要的原则要实现。
你的程序不能解决特定的问题;他们总能解决一个或多个复杂的问题。履行总体原则,比如在 Unity 中用我的 SOLID 系列创作游戏更为重要。
感谢您的阅读,如果您想要更多优秀的经过处理的编程内容, 请访问我在 Udemy 上的奇遇 关于 C#和 SQL。
预选套装物品
https://medium.com/geekculture/how-net-5-breaks-your-c-coding-project-with-the-data-type-string-be41ea48f0a2 https://medium.com/nerd-for-tech/the-reasons-not-to-migrate-from-javascript-to-typescript-89b587ed9ac9 https://medium.com/front-end-weekly/how-naming-ruins-your-code-6f3a53a380b9
不要“数据驱动”。以分析为导向。
办公时间
这里有一些你可以采取的实用步骤。
看看这位可怜、困惑的高管,他试图在没有分析支持的情况下做出决策。山脉?钟表?象棋?什么?🤔[图片来自 freepik]
“我们是一家数据驱动的公司。”
我已经听到你在屏幕前呻吟了。让我们来谈谈这个短语现在意味着什么,我们作为数据科学家和分析师真正希望它意味着什么,以及如何弥合这一差距。在您回复下一个特别请求之前,请阅读此内容。
😤如今“数据驱动”是什么意思,为什么它如此危险?
在过去的几年里,自称“数据驱动”的组织一直很性感。这样,组织通常的意思是:我们利用数据做出决策。不要误解我的意思,用数据支持决策过程是一个很好的主意。但这种说法中有一些阴险的东西:它不承认数据科学家/分析师的辛勤工作。重点是数据,而不是分析,也就是说:
我们有如此多的数据,我们只需要看到它并利用它来做决定。分析师和数据科学家只需要为我们获取数据。
数据驱动的组织:分析师获取数据。分析驱动的组织:分析师找到答案。
数据驱动的组织:雇佣越来越多的分析师/d 作为“帮手”。分析驱动的组织:投资基础设施、工具和教育。
数据驱动:“你能把这些数字拉出来吗…?”
分析驱动:“你能帮我想想…?”数据驱动:找到证明管理决策合理的数据
分析驱动:产生洞察,讲述一个令人信服的故事,管理层可以据此采取行动
我认为是时候让组织从数据驱动型转变为分析驱动型了。
🤔对此该怎么办
不幸的是,这里没有灵丹妙药——成功成为“分析驱动型”将取决于您的组织如何看待其分析组织。他们是思想伙伴还是下属?SQL 猴子还是科学家?
最终,利益相关者需要养成询问业务层面的分析问题的习惯,而不是提出数据请求。
[关于这个主题的精彩阅读,请查阅 Pedram Navid 的文章,构建现代数据堆栈。]
也就是说,除了与跨职能领导进行长谈之外,我发现一些程序上的改变有助于支持这种转变:
- 📝记录您的临时工作。
- 🍽记录您的数据。
- 📚设置一个中心位置来查找您的文档。
虽然这些不会直接改变企业文化,但它们会让你过去的工作变得可搜索,而且,恕我直言,是可自我服务的,从而让你减轻临时请求的负担。
📝记录[并拥有]您的临时工作
作为一名数据科学家或分析师,您是最熟悉数据的人,因此与数据相关的请求自然会找上您。我的默认反应一直是尽快放弃对利益相关者的查询/仪表板,但我很快就忘记了。但是下次有人问我的时候,我不得不重复这项工作。此外,这种特别的回应只会强化你与决策者的关系纯粹是交易关系的看法。
给你的特别工作多一点分量,让它和你更强大的分析放在一起。[图片来自 freepik]
相反,写一份文件。从这个问题开始:
“你想回答什么问题?”
然后,熟练地、可重复地完成工作——毕竟,处理数据是一门科学,应该作为一门科学来对待。
这有两个好处:
- 这会减轻你的负担。以后你会有这个问题,以防你再次被问到同样的问题(你会的)。
- 这会让你的工作更有分量。你将被迫更认真地考虑这个问题。这将产生更好的分析,重申你作为思想伙伴的价值,并提高决策的质量。分析驱动我们来了。🚀
🚗记录您的数据[表格+转换]
文件柜。抽屉有点像图式,对吧?🙄[图片来自 freepik]
记录你的工作是很棒的第一步,但是你的分析只有在你使用数据的时候才是好的。如果你想对你的分析工作有信心(如果你也想让别人对你的工作有信心),你应该深入理解并记录你的最终分析中使用的表格和转换。这包括两个部分:
- 记录您的数据。至少,在某个地方保留一个中心文档供您的团队访问可以是一个很好的开始。如果您正在寻找一个轻量级的托管解决方案, Hyperquery 提供了一个查询优先的笔记工具,内置了一个数据目录,因此您可以将 SQL 工作和表格文档放在一个地方。数据编目公司(如 Alation、Collibra 或 data.world)或开源“数据发现”工具(Datahub、Amundsen、metacat、metamapper、Magda)可能适合这里的需求,但它们的设置/维护往往有点繁重。
- 对于您的转换,使用 dbt 或 dataform 之类的工具来记录和版本控制您的转换。
转换应该是可见的,受版本控制的,而不是被强加到视图中的(尽管这可以稳定地工作一段时间)。dbt 是构建、记录和版本控制这类工作的绝佳工具。
📚设置一个中心位置来查找您的文档
[图片来自 freepik]
现在我已经说服你记录事情,还有一个明显的问题:
我们把这些文件放在哪里?
您需要将所有这些文档合并到某个地方。我曾经和一些公司合作过,他们使用 Confluence、concept 甚至 Github 来处理这个问题,这在紧要关头也能发挥作用。在 Airbnb,我们使用知识报告结合数据门户来存储这些作为 git 跟踪的出版物,但这对于临时工作来说总是感觉有点沉重。
Hyperquery 非常适合以这种方式组织工作——Hyperquery 将概念/融合式笔记环境与可执行查询块结合在一起,因此您可以在那里开始您的 SQL 工作,轻松地与您组织的其他人共享它,然后,使用您的工作作为起点来构建一套完整的文档。
但是在一天结束的时候,使用对你有用的工具。重要的是,你的工作需要被团队和公司的其他人看到、访问和搜索。
最后的想法
分析组织不是为了写 SQL 而存在的。我们是来解决问题的。虽然试图迫使利益相关者这样看待它将是一项艰巨的任务,但至少让我们记录我们的数据工作,这样我们工作中解决问题的方面就更明显了。让我们向世界展示为什么分析驱动的组织可以如此强大!🙌
推文@ imrobertyi/@hyperquery来问好。👋
关注我们LinkedIn。🙂 要了解更多关于 Hyperquery 的信息(并注册我们的 all-in-one SQL workspace 的私有测试版),请访问Hyperquery . ai。 (如果你对帮助建立感兴趣,我们正在招聘——查看我们的 空缺职位 )。)
不要被 Python 的天花乱坠所迷惑
仍然是你想要的工具
在 Unsplash 上拍摄的 Verena Yunita Yapi
如果你认为副标题中有错别字,那就想想 JLO(:
众所周知,python 在 DS 从业者中的流行程度在过去几年中飙升,这表明一方面有抱负的 DS 和另一方面的组织都像滚雪球一样更喜欢 python 而不是 R。
展示 python 崛起的一种流行方式是,用标签“pandas”来绘制栈溢出中所提问题的分数,与“dplyr”进行比较:
https://insights.stackoverflow.com/trends?tags=pandas%2Cdplyr
但是这个图表还讲述了另一个故事:所有这些新的 pandas 用户搜索堆栈过度溢出,因为 pandas 真的很难理解。我将在本文后面用几个常见操作的例子来证明这个断言,这些操作在 dplyr 中很简单,但在 pandas 中需要我们大多数人去搜索堆栈溢出。
我非常相信使用正确的工具来完成工作(在过去的 6 个月里,我一直在为我们基于 Java 的生产环境编写 Scala Spark)。对于大多数数据科学家来说,数据争论占据了大部分工作,这已经成为共识。
经 https://scientistcafe.com/2017/03/25/whatisdatascience.html许可拍摄
由此可见,与 pandas 用户相比,dplyr 用户大部分时间都能享受到工作效率的提升。r 在许多其他领域也有优势:可用的 ide、包管理、数据结构和许多其他领域。
这些优势是如此之多,以至于在一篇文章中涵盖所有这些优势是不切实际的。为此,我已经开始在一个专门的 github repo 中汇编 R 相对于 python 的所有优势。我计划从这篇文章开始,在一系列的文章中分批分享它们。
在列举不同的原因时,我尽量避免以下情况:
- 太主观的比较。例如,函数缩进和花括号结束。
- 一段时间后人们可以习惯的问题,如 python 索引(尽管它从 0 开始,或者 object[0:2]只返回前 2 个元素的事实仍然偶尔会让我迷惑)。
你可能会问,我希望通过这个完成什么?我希望:
- 组织意识到使用 R 的价值,并更多地选择使用它来代替 python。
- 结果,python 开发人员社区意识到了可以改进其 DS 产品的方法,并采取了相应的行动。python 已经从 R 中借用了一些很棒的概念(例如,数据帧、因子数据类型、 pydatatable 、 ggplot ) —更重要的是,它这样做了,所以当我们必须使用 python 时,我们可以享受它们。
现在,我不认为在任何可以想象的场景中,R 都比 python 更好。我只是希望意识到 R 提供的优势会鼓励组织考虑更多地使用它。
好了,现在来谈谈举证责任。下面我将给出几个例子来说明为什么在 dplyr 中大多数操作都很简单,而在 pandas 中它们经常需要搜索堆栈溢出。这也是为什么 dplyr 比熊猫更容易相处的原因。关于 R 相对于 python 的其他优势,请参见 repo 。
我们从一个简单的例子开始:计算 iris 数据集中每个物种的*均萼片长度。
在 dplyr 中:
iris %>%
group_by(Species) %>%
summarise(mean_length = mean(Sepal.Length))
在熊猫身上做同样事情的一个常见方法是使用 agg 方法:
(
iris
.groupby('Species')
.agg({'Sepal.Length':'mean'})
.rename({'Sepal.Length':'mean_length'}, axis = 1)
)
我们可以看到 pandas 需要一个额外的重命名调用。
我们可以通过向 agg 传递一个元组来避免额外的重命名:
(
iris
.groupby('Species')
.agg(mean_length = ('Sepal.Length', 'mean'))
)
虽然这看起来更接* dplyr 语法,但它也强调了一个事实,即有多种方法可以使用 agg 方法——与在 R 中有许多方法可以做同样的事情而在 python 中只有一种明显的方法的常识相反。
现在,假设我们想要使用加权*均值(以萼片宽度作为权重)。
在 dplyr 中,我们使用带附加参数的加权均值函数:
iris %>%
group_by(Species) %>%
summarize(weighted_mean_length =
weighted.mean(Sepal.Length, Sepal.Width))
相当直接。事实上,它非常简单,我们可以即时进行实际的加权*均值计算:
iris %>%
group_by(Species) %>%
summarize(weighted_mean_length =
sum(Sepal.Length * Sepal.Width)/ sum(Sepal.Width))
对熊猫来说,这并不简单。人们不能仅仅改变上面的例子。
在搜索堆栈溢出时,我发现了下面的解决方案,在撰写本文时,它获得了 104 张赞成票,并在其中添加了几行以获得我们想要的结果:
def wavg(group):
d = group['Sepal.Length']
w = group['Sepal.Width']
return (d * w).sum() / w.sum()(
iris.groupby('Species')
.apply(wavg)
.to_frame()
.rename({0:'weighted_mean_length'}, axis = 1)
)
更好的解决方案是:
def wavg(group):
d = group['Sepal.Length']
w = group['Sepal.Width']
return pd.Series({'weighted_mean_length':(d * w).sum() / w.sum()})(
iris.groupby('Species')
.apply(wavg)
)
最后,一位评论员指出,有一个更优雅的解决方案,但即使是他的解决方案也必须做一些修改才能达到我们的目的:
(
iris
.groupby('Species')
.apply(lambda x: pd.Series({'weighted_mean_length':
np.average(x['Sepal.Length'],
weights = x['Sepal.Width'])}))
)
总结一下:python 的禅宗说:“应该有一种——最好只有一种——显而易见的方法来做这件事。”。我们可以看到,对于 dplyr 来说,这是非常正确的,而在熊猫中,你有许多许多不同的方式,没有一个是显而易见的,所有都相对繁琐。
窗口功能
窗口上的聚合
假设我们想要计算每个物种的萼片长度的*均值,并将其附加到原始数据集(在 SQL: SUM(Sepal。长度)超过(按物种划分))将是:
(
iris
.assign(mean_sepal = lambda x:
x.groupby('Species')['Sepal.Length']
.transform(np.mean))
)
我们可以看到,这需要一个专用的方法(转换),相比之下,dplyr 只需要添加一个 group_by :
iris %>%
group_by(Species) %>%
mutate(mean_sepal = mean(Sepal.Length))
现在假设我们想对一个有两个参数变量的函数做同样的事情。在 dplyr 中,它非常简单,只是对前面的代码做了一个小而直观的调整:
iris %>%
group_by(Species) %>%
mutate(mean_sepal =
weighted.mean(Sepal.Length, Sepal.Width))
我无法想出一种方法来使用一个具有 1 个以上输入变量的函数,例如熊猫的加权*均值。
扩展窗口
现在假设我们想要计算萼片的展开和。萼片长度增加。每种内的宽度(单位为 SQL: SUM(萼片。长度)超过(按种划分,按萼片排序。宽度))
在 dplyr 中,这非常简单:
iris %>%
arrange(Species, Sepal.Width) %>%
group_by(Species) %>%
mutate(expanding_sepal_sum =
sapply(1:n(),
function(x) sum(Sepal.Length[1:x])))
注意,我们不需要记住任何额外的函数/方法。您使用无处不在的工具(例如 sapply)找到了一个解决方案,只需将其插入到 dplyr 链中。
在 pandas 中,我们将不得不搜索堆栈溢出来提出扩展方法:
(
iris
.sort_values(['Species', 'Sepal.Width'])
.groupby('Species')
.expanding()
.agg({'Sepal.Length': 'sum'})
.rename({'Sepal.Length':'expanding_sepal_sum'}, axis = 1)
)
同样,我们需要使用一个额外的重命名调用。
为了避免额外的重命名,你可能想要像我们上面做的那样将一个元组传递给 agg ,但是出于某种原因,下面的语法不起作用:
(
iris
.sort_values(['Species', 'Sepal.Width'])
.groupby('Species')
.expanding()
.agg(expanding_sepal_sum = ('Sepal.Length', 'sum'))
)
您也可以通过使用以下眼霜来避免额外的重命名:
(
iris.assign(expanding_sepal_sum = lambda x:x.sort_values(['Species', 'Sepal.Width'])
.groupby('Species')
.expanding().agg({'Sepal.Length': 'sum'})
.reset_index()['Sepal.Length'])
)
移动窗口
现在,假设我们想要计算一个移动中心窗口*均值(在 SQL: AVG(Sepal。长度)超过(按种划分,按萼片排序。EN 2 之前和之后 2 行之间行宽)
和往常一样,在 dplyr 中,这非常简单:
iris %>%
arrange(Species, Sepal.Width) %>%
group_by(Species) %>%
mutate(moving_mean_sepal_length = sapply(
1:n(),
function(x) mean(Sepal.Length[max(x - 2, 1):min(x + 2, n())])
))
与其他示例一样,您所要做的就是使用无处不在的工具找到一个解决方案,并将其插入到 dplyr 链中。
在熊猫中,我们必须查找滚动方法,阅读它的文档并得出以下结论:
(
iris
.sort_values(['Species', 'Sepal.Width']).groupby('Species')
.rolling(window = 5, center = True,
min_periods = 1)
.agg({'Sepal.Length': 'mean'})
.rename({'Sepal.Length':'moving_mean_sepal_length'}, axis = 1)
)
这个帖子到此为止。如果你还不是皈依者,请在本系列的下一篇文章中关注更多的例子。您还可以在“R 优于 python”repo中查看更多示例。
在下一次数据科学面试中,不要做这三件事
意见
…作为一名面试官,我应该避免什么
兰迪·雷伯恩在Unsplash【1】上拍摄的照片。
目录
- 介绍
- 假装知道答案
- 沿着长切线前进
- 围绕您的数据科学
- 摘要
- 参考
介绍
从我参加数据科学面试的经历来看,我经历过各种各样的应聘者,他们展现了一些他们应该有的东西,一些他们不应该有的东西。在这篇文章中,我们将讨论在下一次数据科学面试中应该避免什么(其中一些也适用于非数据科学面试)。下面,我将给出一些我个人认为是你在面试中应该避免的事情,以及应该怎么做。
假装知道答案
我以前面试的时候绝对是这个禁忌的罪魁祸首。假装知道答案的一个可能原因是害怕被拒绝。然而,真正令人赞赏的是相反的情况,当你试图回答你确实知道的问题,或者你完全说你只是不知道。这可能会让一些人感到惊讶,但我宁愿有人说他们不知道某事,而不是假装、撒谎或绕着答案打转。
不知道所有事情真的没关系,一些面试官可能只是评估你的知识,而不是测试你,而是知道他们需要教你什么。此外,很高兴知道你在面试中完全诚实和坦率,所以当你得到这份工作时,所有的期望都建立在你的知识基础上。
如果你表现出你现在可能撒谎,或者现在假装,而有人后来发现了,那么这可能会给未来的工作留下不好的印象。
不要做什么:
- 假装,撒谎,或者在答案周围打圈
你应该说什么:
- “我可以告诉你我确实知道这个,但我不知道其余的”
- “在专业环境中,当我不知道答案时,我会这样做”
总的来说,对我个人来说,听到有人说不知道,我觉得更难能可贵。我认为让一个申请人什么都知道是不公*的。当然, IDK 的的数量是有主观限制的,但在数据科学的世界里,数据分析、数据工程、软件工程、机器学习和数据科学本身之间有如此多的重叠,以至于不知道每一个概念、库或技能也没关系。
注意:这可能只是我的偏好,但我认为诚实和透明可以走得很远,我认为我们都一直在努力。
沿着长切线前进
Denys Nevozhai 在Unsplash【3】上拍照。
有时候,人们可能认为回答一个问题的时间越长,就意味着他们听起来有更多的知识。相反的情况很可能是真的,当有一个简洁而自信的答案时,面试官会知道你马上就知道答案了,并且可以继续测试你更多的知识。当一个人滔滔不绝地讲几分钟时,也很难跟上。
不要做什么:
- 为一个数据科学项目提供答案,这个项目花费的时间比几分钟还要长
- 提示:更长的答案并不意味着更令人印象深刻的答案
你应该做什么:
- 通过强调业务问题、约束、利弊以及模型的影响,围绕数据科学项目创建用户故事(模型答案)
总而言之,要简洁,这样面试就更像是一场对话。
围绕您的数据科学
Javier Esteban 在 Unsplash 上的照片。
这个‘不要’听起来可能有点模糊。更明确地说,我指的是当你只谈论一个数据科学项目时,它是如何与你相关的。当然,你的回答的主要部分应该展示你的经验和技能,但是有一个主要部分应该涵盖其他部分。
不要做什么:
- 只谈一个与你有关的数据科学项目
- “我得到了 98%的模型准确率,这对我真的很好”
- “我主要是自己参与这个项目”(如果这是真的,没关系,但是如果有更多的人参与,那么请看下面的)
你应该做什么:
- 提及参与模型创建和最终确定的其他人
- 讨论涉及的利益相关者
- 讨论受益于该模型的不同部门
- “我得到了 98%的模型准确率,这对公司来说非常好,不仅使我们的 KPI 显著提高,还为我们的用户创造了更好的产品”
- “我和许多不同的同事一起研究这个模型,包括我的主要利益相关者,产品经理,以及软件工程师和 UI 开发人员,将完整的模型实现到产品中”
总的来说,你可能认为说你自己做了所有事情听起来更令人印象深刻,但这可能会向面试官展示相反的情况。这可能是因为你很难共事,也没有合作经验。
摘要
和任何观点类文章一样,有保留地接受建议。你可以用不同的方式给面试官留下印象,而不是试图做你认为他们想看到的事情,做你自己,诚实,开放,通过问题说话,简洁,感谢和讨论帮助你达到你现在位置的人。
以下是你在下一次数据科学面试中要避免的三件事:
** Pretend Knowing the Answer* Going on Long Tangents* Centering Data Science Around You*
我希望你觉得我的文章既有趣又有用。如果您同意或不同意在您的数据科学面试中应该避免的这些事情,请随时在下面发表评论。为什么或为什么不?你认为在数据科学面试中还应该避免哪些事情?这些当然可以进一步澄清,但我希望我能够对数据科学面试和一般面试有所启发,感谢您的阅读!
我不属于这些公司中的任何一家。
请随时查看我的个人资料、 Matt Przybyla 、和其他文章,并通过以下链接订阅接收我的博客的电子邮件通知,或通过点击屏幕顶部的订阅图标 点击订阅图标 ,如果您有任何问题或意见,请在 LinkedIn 上联系我。
订阅链接:https://datascience2.medium.com/subscribe
参考
[1]兰迪·雷伯恩在 Unsplash 上拍摄的照片,(2021)
[2]照片由绿色变色龙在 Unsplash 上拍摄,(2015)
[3]照片由 Denys Nevozhai 在Unsplash(2018)拍摄
[4]Javier Esteban 在 Unsplash 上拍摄的照片,(2018)
不要下载!使用 Python 中的 URL 读取数据集
无需以任何格式在本地下载即可阅读 UCI 数据集的教程
旧方法不会打开新的门!Unspalsh 拍摄的照片
对于数据科学专业的学生来说,经常会用到公共数据集。 UCI 是最常见的机器学习知识库,包含成千上万的数据。但是,对于一个赋值,如果您在本地下载数据集,其他人不能在其他机器上运行您的代码,除非下载并替换本地路径。此外,为了在 google collaboratory 等在线*台上训练模型,您需要将数据集保存在 google drive 上,安装 google drive 并使用它。如果没有获得谷歌硬盘的许可,这种功能在其他 collab 笔记本上也无法运行。另一方面,如果从 URL 读取数据集,就不会有这些问题。
在本教程中,我将解释如何读取五种不同类型的数据文件格式,它们是:
.data
文件格式.csv
文件格式.arff
文件格式.zip
文件格式.rar
文件格式
如何从网址读取 **data**
或 csv 文件格式
感谢pandas
,前两种情况最容易。要读取数据集,您只需要输入数据集 URL。例如:
例 1: 输血数据集.data
格式。
读取输血数据集
例二: 宫颈癌数据集带.csv
格式。在本例中,我们已经知道数据集有问号形式的缺失值。因此,为了正确读取数据集,我们包含了na_values=['?']
。
读取宫颈癌数据集
对于其他情况,第一步是使用urllib.request.urlopen()
打开 URL,然后从请求的 URL 读取数据文件。然而,其余的取决于数据文件格式。
如何从 URL 读取 arff 文件格式
要读取作为数据帧的.arff
文件格式,打开 URL 后,读取 URL 并使用scipy.io.arff.loadarff()
加载.arff
文件。在大多数情况下,我们也需要解码它。然后我们可以通过pd.DataFrame()
将其转换为熊猫数据帧。
例 3: 糖尿病视网膜病变数据集.arff
格式。我从链接中获得了列名,以分配给数据帧。
一个常见的问题是,在某些情况下,整数列被读取为object
数据类型,因此我们用b'1'
代替了1
。为了解决这个问题,我们检查了所有的列,如果数据类型是object
,我们再次解码它们(第 18 行)。
读取糖尿病数据集
如何从 URL 读取zip
文件格式
如果数据文件格式是.zip
,它可能包含几个文件,如元数据或数据文件。要读取这样的数据文件,我们需要先解压,剩下的就看数据文件格式在 zip 文件中压缩成什么样了。
例 4: 肥胖水*数据集.zip
格式,其中包含一个.csv
文件。
读取肥胖水*数据集
如何从 URL 读取rar
文件格式
我相信这是最具挑战性的文件格式。有几个 Python 库可以提取和读取 rar 文件,比如rarfile
、patool
、pyunpack
等。我选择rarfile
是因为它使用起来更简单。
例 5: 慢性肾脏疾病数据集.rar
格式,其中包含多个.arff
文件。
阅读慢性肾脏疾病数据集
摘要
好消息是,你可以使用包含函数的 Python 库轻松读取 UCI 数据集。到目前为止,它包含了 36 个数据集,它期待你的贡献来添加更多的数据集。😊
对于上面的例子,加载数据集最简单的方法是安装uci_dataset
pip install uci_dataset
之后,您可以通过从那里加载函数来读取上述所有数据集。
示例 6: 从[uci_dataset](https://github.com/maryami66/uci_dataset)
Python 库中读取数据集。
从 uci_dataset 库中读取 UCI 数据集
https://sciencenotes.medium.com/membership
不要淹没在海洋里
托马斯·维马雷在 Unsplash 上的照片
模型选择可以决定一个 ML 项目的成败
简介
想象你在海洋上扬帆起航,驶向深海,看不见陆地。选择一个 ML 模型可能有点像这样——兴奋和恐惧的奇怪混合。随着大量的机器学习算法在你面前展开(并且逐年增长),第二次猜测选择是正常的,并且可能会在无休止的超参数调整中挣扎。
有一张准确的地图来指导车型选择,可以让你树立信心,相信自己正朝着正确的方向前进,并且会到达目的地。
型号选择阶段
手动
- 我怎么用手做这个?
- 工作流程和步骤
- 固有的挑战
首先把所有的技术放在一边,问问你自己:我怎么用手来做这个?如果我坚持手动实现这个流程,我会从哪里开始,工作流&步骤会是什么样的?考虑到这一点,无论自动化程度如何,都可能会面临挑战。
低技术
- 传统技术
- 启发式、基于规则的解决方案
- 比 ML 更划算
接下来考虑使用传统软件技术解决问题。一个启发式的、基于规则的解决方案会是什么样子?如果符合要求,它们通常更具成本效益。也许 ML 对你的问题来说是矫枉过正了。
高科技简单
- 最简单的 ML 模型
- 装配线上的多个型号
- 为解决方案装配现成的模型
我可以用什么最简单的 ML 模型来解决我的问题?或者是一系列简单的模型——一个接一个,就像一条装配线。有什么办法可以把现成的 ML 模型拿来改装一下,解决我的问题吗?
高科技综合体
- **黑箱进场
- 成熟、全能的模型
- 高度定制的,也许是新颖的算法
如果我有一个黑盒,里面有一个超级复杂的 ML 模型解决了我的问题:那对我来说值多少钱?如果所提供的价值足够高,那么追求高度定制的解决方案可能是值得的,这种解决方案可能建立在新颖的算法之上。
这张图片和作者后来的所有图片
值得吗?
想象一下疯狂的成功:
- 值得吗?
- 你开始的时候投资回报清楚吗?
- 在开始一个项目之前,要知道回报
在你花费更多的大脑周期之前,后退一步,想象你已经安全到达目的地。值得吗?假设你的 ML 实现取得了超出任何人想象的成功——当你开始时,你对投资回报有准确的认识吗?有些项目不管最终的回报如何,都有自己的发展势头。在你出发之前,确保你的旅程结束时有一桶金在等着你。
整体情况
这是我们用来选择 ML 模型的地图。
许多不同类型的机器学习模型可能看起来势不可挡。了解哪些类型的模型在过去的项目中被证明是成功的,这是将正确的工具应用于手头工作的关键。
ML 的三大类型
监督学习
这是 ML 的一个分支,涉及在包含答案的数据集上训练我们的模型,因此当部署时,模型可以智能地响应新的数据点。
无监督学习
ML 的这一分支涉及在数据集上训练我们的模型,这些数据集不包含我们正在寻找的答案——相反,我们通常要求我们的模型在数据中寻找自然模式,或集群。
强化学习
ML 的第三个分支与模型有关,这些模型通过在环境中反复试验来学习,当它们接收到对其行为的反馈时,表现得越来越智能。
监督学习:回归
预测建模是最流行的回归类型之一。假设你想预测一栋房子的价格,基于各种特征——比如:位置、面积、卧室数量、可步行性等。你可以从公共领域收集一些数据(比如 Zillow),根据这些数据训练你的回归模型,然后输入新房子的信息,看看它会做出什么样的价格预测。
监督学习:分类
我们都太熟悉上传到脸书、Instagram 和谷歌图片等服务的图片的自动标记和分类了。这种类型的 ML 模型擅长于基于输入图像上存在的一些属性将某物放置在离散的类别(或类)中。
无监督学习:维数缩减
有时候,我们输入到机器学习模型中的特征并不都那么有用。这就是降维技术的用武之地。使用像主成分分析(PCA)这样的技术,您可以将原始输入特征减少到那些实际上有助于获得所需答案的特征。
无监督学习:聚类
**聚类是一种无监督的学习方法,通过这种方法,机器学习模型被要求在数据中寻找模式。我们不是告诉模型要寻找什么,而是问它是否能在数据中发现任何有趣的东西。这种技术有助于将志同道合的客户聚集在一起,以便为未来提供推荐,就像亚马逊一样。
强化学习
游戏玩家可能很熟悉几年前 DeepMind 凭借 AlphaGo 系统击败顶级职业围棋选手的惊人成功。AlphaGo 的底层是一个复杂的强化学习实现——机器学习模型越玩越擅长玩围棋。
真实世界用例
下面的例子说明了什么类型的 ML 模型在解决什么类型的问题时是有用的:
转移生产线上的不良品
分类(使用卷积神经网络或 CNN 的图像分类)**
为一个游戏树立一个敌人
强化学习
预测汽车销售价格
回归
基于以往采购的针对性营销活动
使聚集
将论坛上的帖子分类
分类(自然语言处理,NLP,可能使用递归神经网络或 RNNs)**
检测信用卡欺诈活动
分类或聚类(两种方法对异常检测都有用)**
通过图表和图形洞察大量数据
降维
结论
- 机器学习是巨大的——但是我们有一张地图
- 选择适当的自动化水*
- 确保风险值得回报
- 使用地图选择模型
- 谷歌避免从空白画布开始
到现在为止,你应该意识到,尽管机器学习的领域确实很广阔,但有一些既定的界限,使得导航它变得简单得多。
从评估适合手头问题的自动化水*开始——确保风险值得回报。
然后确定你的业务问题最适合的 ML 类别,并开始谷歌搜索,看看其他 ML 从业者如何应对类似挑战的例子。有大量的宣传资料可以作为你下一个项目的起点——只需查看 kaggle 上的“笔记本”选项卡就能获得一些灵感!记住:重新发明轮子是永远不会正确的答案。
不要过滤咖啡 TDS 样品
咖啡数据科学
数据挑战惯例
在咖啡的世界里,惯例通常是由专业的咖啡师和几个毫无疑问的专家的多年经验来推动的。几年前,咖啡师开始使用折光仪来更好地了解他们通过多种咖啡技术提取咖啡的效果。折射仪测量总溶解固体量(TDS ),然后通过咖啡渣的流入和咖啡液体的流出来确定提取率(EY)。这种类型的数据被用来以更客观的方式评估咖啡,并挑战一些现有的惯例。
惯例:如果你能负担得起,在使用注射器过滤器测量 TDS 之前过滤所有的样品。
我并没有试图推翻这个惯例,但是我相信我能够进行一个简单的实验来证明过滤样本是没有意义的,或者至少,你没有过滤掉你认为你在过滤的咖啡。
我开始这个实验是为了更好地理解使用糖水的折光仪。然后我决定做一个小小的测试,那就变成了这篇文章。我之前发现注射器过滤器只能线性改变 TDS 读数,所以它们不是很有效。
惯例
注射器过滤器并不便宜。在 VST 的 T2 网站上,50 或 2 美元一个的 T3 大约是 100 美元(含税和运费)。VST 声称,你应该过滤样本,甚至可能两三次,以取得良好的测量结果。他们说,他们的过滤器消除了可能干扰 TDS 测量的细小、未溶解的固体和 CO2。
已经有一些关于折射仪的研究,但是没有一个表明过滤样品是必要的。
糖测试
我开始将 2 克糖加入 30 克水中,因此 TDS 应该是 6.2%,但实际上,它是 5.2%,所以 EY 是 84%。
所有图片由作者提供
然后,我对糖水进行了一些测量,以查看测量的一致性,没有任何咖啡沉淀在样品中的潜在误差。
后来,我想我会把一些废粉末放入样品中。我为一些实验制作了没有任何东西可提取的废粉末,我认为如果样品中的固体真的改变了读数,那么一堆粒子真的会扰乱读数。
我会加入用过的咖啡渣,搅拌它们。我在加入用过的咖啡渣之前和之后读了一遍。
为了进一步增加乐趣,我在这些测试中使用了三级筛选过的废咖啡。在没有添加任何东西的情况下,我没有看到测量值超出样本可变性的界限。
这次测试搞得一团糟。
然而,我在测量中没有发现太大的差异。
最坏情况测试
最好的测试是使用粒度小于 200um 的细磨过的粉末。这次,我从用过的咖啡开始,加入糖水。
同样,这些并没有表现出很大的性能差异。
这个实验使用糖水和废咖啡来显示未经过滤的样品的绝对更差的效果,我没有找到证据来支持未溶解的固体影响 TDS 测量的假设。
根据这些数据,我认为在测量前过滤 TDS 样本是浪费时间和金钱。
考虑到注射器过滤器在咖啡中如此普遍,我建议使用它们的人再看一看。重复这个实验并不困难,我向那些仍然相信注射器过滤器的人提出挑战,以证明过滤器正在去除咖啡样品中可能不公*地改变折射率的东西。
如果你愿意,可以在 Twitter 和 YouTube 上关注我,我会在那里发布不同机器上的浓缩咖啡视频和浓缩咖啡相关的东西。你也可以在 LinkedIn 上找到我。也可以关注我中。
我的进一步阅读:
不要忘记你的数据科学家头衔中的“科学家”
办公时间
使用实验性设计框架如何能够更快地发现错误,并使您成为更有效的数据科学家
构建一个性能良好的神经网络通常需要多次反复调整模型,然后检查结果,然后是更多的调整和更多的检查。像 grid 和 random search 这样的方法可以自动完成一些繁重的工作,但是在选择了超参数之后,很容易受到诱惑而继续进行小的调整。实验设计提供了一个心理框架,在不使结果明显偏离基线结果的情况下,减少了做出改变的障碍。
我在研究生院的一位教授总是督促我们以实验设计的形式提交所有的作业。当时,我觉得这非常乏味,但现在回想起来,我明白了为什么。它让我以一种结构化的方式进行建模,这也让我远离了麻烦。每当我发现自己在试图训练神经网络时陷入困境,我就会问自己:“我是在使用实验设计原则吗?”
问题是
开发精确建模问题的深度学习神经网络并不容易。像 Tensorflow、PyTorch,特别是 Keras 这样的库使得任何人在学习了库的 API 中的几个命令后都可以创建一个神经网络。然而,这些工具可能会使编写构建神经网络的代码变得容易,但它们不会教授如何以一种允许构建高级模型的方式进行思考。
什么是实验设计
实验设计是一种“技术,使科学家和工程师能够有效地评估多种输入或因素对性能测量或响应的影响。”[1]这对数据科学家来说实际上意味着,当构建一个项目时,考虑哪些变量(不一定是代码中的变量)可以被改变以测试不同的结果。换一种方式说,在模型内可以改变什么来测试不同的结果。
这在实践中看起来如何?
构建模型既复杂又繁琐。实验设计为数据科学提供了一个框架来简化这一点。
实验设计有两个重要的关键因素。第一,在做出改变之前,清楚地陈述要测试的假设。无论是把假设写下来,还是把它记在脑子里,都取决于你自己,只要确保它是清晰的,你不会偏离它。
例如,如果模型过于简单,无法准确地学习数据,假设将是,“向我的神经网络添加额外的层将提高模型性能。”同样重要的是,提前决定使用哪些指标来评估模型的变化。
接下来,使用陈述的假设,一次只进行影响模型的一个区域的变更。变更后,模型性能应该有一个清晰的区分,“这是变更前的模型性能,这是变更后的模型性能。”这很重要,因为如果进行了不止一个变更,那么就不清楚是哪个变更导致了整体模型性能的变化。影响模型性能的假设之外的变化称为混杂因素。
扩展之前的例子,假设涉及改变另一层的添加,这非常诱人地说,“如果我除了添加层之外还增加了神经元的数量,然后与我的基线相比,会怎么样呢?”忍住冲动!在没有完全评估因素的不同水*的情况下增加另一个因素会导致你产生一个混杂因素。
我们有办法在实验设计中适应多种因素,但这是另一篇文章的主题。
底线:提前陈述你的因素。不要更改未指定为因素的内容。当你改变一个因素时,不要在没有测量结果的情况下改变另一个因素
在因素中思考
每一个可能导致结果改变的变化都被认为是一个因素。但是每个因素可以有多个层次。这些因素倾向于用变化的级别数来描述。继续我们的示例,如果我们的基线模型中有 3 个层,并且我们想看看如果我们添加另一个层会发生什么,那么我们将有一个 2x1 设计(2 表示因子中的级别数,1 表示没有正在评估的第二个因子)。
作者图片:一个超级简单的因素实验,你只是评估一个额外的层
您可以构建这个例子,在这个例子中,您不仅将基线与单个变更(相同因素的)进行比较,而且与多个变更进行比较。假设我们想看看增加五层会有什么效果。
作者图片:三层设计示例
最后,回到混杂因素的想法,如果不先添加另一个因素,你就无法在实验中添加一种方法来评估神经元数量的变化。为了了解如何做到这一点,我将在这篇文章的后续部分进行介绍。
作者图片:该设计中的混杂因素不符合简单的 2x1 设计。这种设计需要重新配置,以获得可信的结果。
包扎
实验设计是构建数据科学实验的一种很好的方式。使用析因实验[2],您可以评估模型的变化,以便在调整超参数时移动到更优的条件。只是要小心抵制同时进行多个更改的诱惑,并确保避免添加混淆因素。
作为数据科学家,关注标题的数据方面很容易,但使用实验设计等技术将显著提高模型的质量。
有关如何创建避免混杂因素并允许您同时测试多个条件的实验设计的更多信息,请阅读第二部分。
来源
[1]https://www . jmp . com/en _ us/articles/what-is-experimental-design . html
[2]https://en.wikipedia.org/wiki/Factorial_experiment
不要被抓到犯这些新手数据科学错误!
避免这些错误,成为更好的数据科学家!
(src =https://pixabay.com/images/id-709045/
介绍
我以前已经说过很多次了,现在我要再说一遍,数据科学学科是极其多样化的,需要来自几个不同领域的技能组合来协同使用,以获得足够的结果。数据科学不仅需要对软件工程有相当复杂的理解,还需要数学、商业和机器学习领域的知识。不用说,这可能是很难学会的。在数据科学行业起步可能会感觉非常复杂,没有起点也没有终点。在这方面,我也要明确指出,永远不会结束。
从事数据科学需要不断的教育,部分原因是它是几个领域的集合,所有这些领域都有你可以用余生来研究的信息。然而,有一些简单的事情你可以避免,尤其是在开始学习数据科学时,这将使学习更加顺利。这篇文章不涉及任何特定的语言,但是我也有一篇关于 Python 的“新手错误”的文章,您可以在这里查看:
№1:错误代码
作为数据科学家可能犯的第一个严重错误是编写糟糕的代码。虽然这似乎是一个显而易见的观点,但你可能会惊讶有多少数据科学家首先是科学家,其次是程序员。我恰好是后者,所以我当然可以直言不讳地指出,许多数据科学家并不像他们应该的那样擅长编程。
当然,这些技能是可变的。在你的数据科学职业生涯中,你可以在这些技能的基础上成长,你很可能会。当学习数据科学时,有了所有其他要学习的东西,很容易将软件工程部分放在一边,并在数据科学的保护伞下从事其他领域的工作。然而,这可能会产生很多问题,因为最终没有软件和编程,实际进行数据科学会困难得多。当然这方面总是要看你应聘的是什么样的数据科学工作。一些数据科学家甚至专门在 Microsoft Excel 等应用程序中工作,但对于大多数数据科学来说,您可能会使用 Python、R、Scala 或 Julia 等统计编程语言。在我的编程经验中,我发现了一些可以让你的代码变得更好的很棒的技巧,我正好有一篇文章适合你。如果你想通过几个简单的步骤学会如何成为一名更好的程序员,这当然值得一读:
</5-fabulous-refactoring-methods-for-prettier-code-e3bc3447c0b2> [## 更漂亮的代码的 5 个惊人的重构方法
towardsdatascience.com](/5-fabulous-refactoring-methods-for-prettier-code-e3bc3447c0b2)
№2:功能太多
我看到许多数据科学家犯的另一个普遍错误是,对于一个特定的模型,有太多的特征。大量的特性列表会给代码带来一些严重的问题。例如,拥有大量的功能通常会降低机器学习模型的速度。如果您碰巧使用 R 或 Python,这一点可能非常重要,因为这些语言一开始并不一定很快。
我喜欢用两种方法来降低数据的维度。这两种方法中的第一种非常流行,并且相对高效。它是机器学习的主要部分,在机器学习的世界里随处可见。这种技术被称为奇异值分解,简称 SVD。还有其他分解技术,比如随机投影,它基于样本空间投影新值。我正好有关于这两种技术的文章,如果你想了解更多,这里有一些链接:
[## 深入奇异值分解
towardsdatascience.com](/deep-in-singular-value-decomposition-98cfd9532241)
我有很多文章。特别是在这两篇文章中,我想说奇异值分解文章是一篇好文章,因为它详细介绍了 SVD 是如何执行的以及它的用途。
另一种用于降低数据维数的突出技术是特征工程。特征工程可用于将多个特征组合成一个特征。这降低了对统计载荷的要求,降低了过度拟合的风险,也降低了特征对模型的性能影响。
第三:无关紧要的特征
构建模型和神经网络时不要做的一个很好的例子是忽略特征。数据不是为了迎合您的特定测试而创建的。避免寻找相关性和正确探索数据要容易得多,但这样做最终总是会适得其反。
有一些模型为消除与目标不相关的特性提供了有价值的资产。这方面的一个很好的例子是 SkLearn 的随机森林分类器的特征重要性,它允许您测量不同特征在基尼指数中的权重。更多的功能并不意味着你的模型会更好,往往情况恰恰相反。无关紧要的特征会降低你的准确性,所以重要的是建模时不要试图使用它们。
№4:错误的测试
说到统计学,有很多东西要学。你可以花数年时间学习数学和统计学的每一个方面!虽然没有人会知道所有关于统计的知识,但对于数据科学家来说,知道什么样的测试或分布适用于什么样的特定场景是非常重要的。数据总是以不同的方式分布,一些测试和分布甚至不用于执行典型的统计测试。考虑到这一点,由于使用了不适合工作的测试而不得不支持一个假设接受,这将是相当尴尬的。
№5:错误的模式
许多数据科学家犯的另一个简单的错误,尤其是新的错误,是将错误的模型与错误的功能应用程序一起使用。某些模型更擅长预测某些目标,某些模型更擅长在不同类型的维度上使用不同类型的特征进行预测。当然,了解这些模型和网络的细微差别将来自经验。
对于这个例子,我会给你提供一个例子。我有一个同事,我和他一起做一个项目,他整晚没睡,试图让他的神经网络的精确度达到一个可接受的水*。无论他如何操纵数据,调整超参数,或设计功能,他就是不能让它工作。那么问题是什么呢?我的同事一直使用的神经网络结构与他的特征完全不匹配。首先,他使用递归神经网络进行图像分类。虽然我认为这是可行的,但它肯定比卷积等效物更难实现,一个重要的规则是,如果不需要,永远不要过度工作和复杂化您的模型。简单的解决方案需要简单的问题,如果你的问题远比你的解决方案复杂,那么它可能不是正确的解决方案。
№6:懂电脑!
当我去大学攻读应用科学学位时,我的很多同龄人都在学习数据科学。当谈到这些同龄人时,让我感到震惊的是他们缺乏键盘背后的能力。如果你打算用电脑工作,并花一生的时间来编程,知道如何很好地使用电脑可能是一个好主意。我甚至无法开始描述有多少次我被要求帮助解决一个简单的计算机问题,而这个问题应该是具有数据科学资格的人应该知道的。
作为一名数据科学家,计算机是我们的工具。作家用笔当剑,数据科学家用键盘。考虑到这一点,你至少应该对计算机科学的概念有所了解。计算机科学是数据科学世界中一个非常重要的领域。另一个方面是知道如何尽可能高效地使用你的操作系统。从你的编译器/解释器到你的 IDE,甚至到你的操作系统下的内核都是工具。正如一个农民知道如何使用他的拖拉机一样,你也应该尽可能地知道如何使用你的电脑。
结论
数据科学很难,因为它包含许多不同领域的许多不同方面。然而,您可以通过不断学习这些技能来提高您的数据科学技能。利用这个列表,您可以避免一些新数据科学家经常遇到的常见陷阱,并使您的工作比其他情况下更有价值。感谢您的阅读,我希望这个建议是有帮助的。
不要只是做笔记——把它们变成文章并与他人分享
采访《机器学习图书营》一书的作者阿列克谢·格里戈里耶夫
一系列采访,重点介绍数据科学领域作家的不可思议的工作以及他们的写作之路。
照片由阿列克谢·格里戈里耶夫提供
虽然从经验中学习是明智的,但从别人的经验中学习更明智- 华理克
华理克的上述引文强调了向他人学习的重要性。在数据科学的背景下,这甚至更有意义。我们中的许多人(包括我)从其他人的工作中受益匪浅。无论是开源软件、博客文章和论文形式的内容,还是 meetup 活动和会议,我们都在不断学习该领域其他人的工作和经验。
为了将这种引人注目的工作推向前沿,我去年开始了一系列采访。在 第一季 中,我展示了一些来自知名数据科学家和 Kaggle 大师的故事,他们分享了自己的旅程、灵感和成就。第二季,我在采访书籍作者。作为一名作家,我非常尊重写书的人。一篇写得很好的文章需要大量的时间、精力和耐心,而为一本书复制同样的内容绝非易事。因此,本期访谈将揭示数据科学领域一些知名作者的故事。
见见作者:阿列克谢·格里戈里耶夫
如果你对数据科学感兴趣并关注这个领域的社区,我相信你一定听说过数据讲座。俱乐部 —数据爱好者的社区。就数据科学而言,它是目前最活跃的社区之一。顾名思义,该俱乐部组织关于数据、机器学习和工程的讲座,包括每周活动、会议和办公时间。顶端的樱桃——该俱乐部目前还在举办免费的机器学习课程!
这个庞大计划的幕后推手是阿列克谢·格里戈里耶夫,OLX 大学的首席数据科学家。他与妻子和儿子住在柏林。我见证了这个社区的稳步发展,并有幸成为其中一个活动的发言人。阿列克谢也写了几本书,这次采访是为了更多地了解他最*出版的名为机器学习图书营的书。
问:这本书的想法是如何产生的?
Alexey: 这不是我的第一本书。在机器学习图书营之前,我写过几本其他的书。其中一本是我与人合著的( TensorFlow 深度学习项目),是一本基于项目的书——每一章都是一个端到端的项目。我喜欢这种格式,当曼宁伸出手,建议写一本书时,我们决定遵循类似的格式。我们让它基于项目,这就是它的起源。在那之后,我们花了几个月的时间来思考,创建一个大纲,让它得到审查,并对它进行迭代。
问:你能为读者总结一下这本书的要点吗?
阿列克谢: 这本书通常涵盖三个要点:
- 机器学习不是魔术。你需要数据——一个特征矩阵 X 和一个目标变量 y 。然后你把它放入机器学习算法——一堆用编程语言编码的数学——然后得到一个模型。之后,您可以使用模型进行预测。
- 入门 机器学习不需要懂很多数学。有像
Scikit-Learn
这样的库可以用来构建机器学习服务。首先,集中学习如何使用这些库。 - 部署模型是机器学习过程中最关键的 部分之一。如果您不能部署您的模型,其他人就不能从中受益——甚至最准确的模型也变得毫无用处。
问:这本书的目标读者是谁?
阿列克谢: 我写这本书时脑海中的读者是七年前的我。我是一名对机器学习感兴趣的软件工程师。我选了很多课程,总共二十多门。但是这些理论知识在我找到 Kaggle,第一次尝试参赛的时候,都被证明是没有用的。
正是在 Kaggle 上,我学会了机器学习。这对我很有效,因为我必须专注于问题,然后通过解决问题来学习。我认为这是软件工程师进入机器学习的最佳途径。这正是我在机器学习图书营中遵循的方法。
作者图片
尽管我是为软件工程师写的,但许多其他人也发现这本书很有用。数据分析师喜欢模型评估章节,数据科学家喜欢模型部署章节。
问:读者怎样才能充分利用这本书?
阿列克谢: 为了充分利用这本书,我建议如下:
- 不要只是读这本书——打开你的 Jupyter 笔记本,跟着读。
- 不要跳过“探索更多”部分,做建议的项目来巩固你的学习。
- 不要跳过部署章节,它们是必不可少的。一个模型只有在别人能用的时候才是有用的。
问:对于一个刚刚起步的新作家,你有什么建议?
阿列克谢: 当众学习。不要只是做笔记——把它们变成文章,与他人分享。另外,如果你这样做了,你会发现你学得更好。想写书,可以从志愿当书评人开始。在写我的第一本书之前,我复习了一些书。此外,我还采访了尤金·严,他非常擅长写作。以下是他的几个链接:
问:你花了多长时间写完这本书?更重要的是,你是如何在工作之余写了一本书的?
阿列克谢: 从曼宁联系我开始,到我收到印刷本,差不多花了三年时间。在最初的六个月里,我们在制定大纲,并得到了很多反馈。然后我写了前两章。后来,我们将这两章分成五章,作为 MEAP(Manning 的早期访问程序)发布。
随着时间的推移,我不得不放弃很多我最初计划要包含的章节,并专注于解释基础知识。我还重复了现有章节的内容。
作者图片
曼宁在确保质量方面帮了很大的忙。他们组织了三次来自书评人的反馈——第一次在发布 MEAP 之前,第二次在 60%完成之后,最后一次在整本书完成之后。
写一本书同时工作是非常具有挑战性的。所以花了这么多时间。我没有任何秘密。我懈怠了很多,有几次处于放弃的边缘。我没有这么做的主要原因是因为 MEAP 和责任感。许多人已经买了这本书,所以我不能就此停止工作。
问:你有喜欢的书和作者吗(在技术或非技术领域)?
阿列克谢: 我以前很喜欢托尔金,但这几天我不看非技术性的书了。
在技术领域,我最喜欢的是 Martin Kleppmann 的“ 设计数据密集型应用 ”。我无法想象马丁为这本书付出了多少努力。它的结构非常好,即使您对数据工程不感兴趣,我也推荐您阅读它。通过阅读这本书,你会学到很多关于技术写作的知识。
👉阅读本系列其他访谈:
👉期待与 Alexey 连线?在Twitter和LinkedIn上关注他。
不要让机器学习工程师独自工作
与这两个关键角色合作取得成功
图片授权给来自 Shutterstock 的 Mary Alfheim
机器学习工程师需求量很大。如果你在过去几年里试图雇佣一个头衔与此相*的人,你就会痛苦地意识到这一点。这些工程师非常擅长获取数据科学的输出,比如模型,并将其投入生产以发布新的应用程序。雇佣 MLEs 对你的组织来说可能是一笔巨大的投资,你可能会想当然地认为你不需要在其他地方花费就能获得成功。不幸的是,你错了。
假设您负责一项新功能,向您的数字产品用户显示零售产品、歌曲、视频甚至工作列表的推荐。也许你是一名数据科学或 AI/ML 主管,或者是一名数据驱动功能的产品经理。您已经与客户或其他产品经理仔细合作,定义了您想要构建的内容,并扩展了您的数据科学团队,以提供初始推荐模型。让这个模型扩展到实时交付,并在你的产品中向用户展示新的推荐,看起来是下一个大障碍。幸运的是,您团队中的机器学习工程师能够扩展这个解决方案,并构建管道来向正确的用户显示正确的推荐。完成了,对吗?
没有。
虽然一个伟大的数据科学和机器学习团队已经训练和测试了模型,并使用类似混淆矩阵的东西评估了它的精度,但你仍然不知道这个建议在你的实际产品中如何工作。用户对此的反应会如预期吗?有我们没有预料到的错误吗?这真的推动了我们假设的结果吗?我们怎样才能做得更好?
会见可视化工程师
你需要确保你的团队也配备了可视化工程师。这个角色的人将根据观察到的与您的推荐相关的用户行为提供实际的跟踪或报告,或者无论您的面向用户的功能是什么,一旦它出现在面向客户的 UI 中。
该工程师将与您的 MLEs 密切合作,以了解特性和模型意图、其部署日期及其预期结果。然后,他们将使用您的事件数据来构建能够可视化实际用户行为的仪表板。将此用于实际分析——您是否看到任何意外的行为,采用看起来像什么,以及用户正在采取什么样的行动?
不要忘记实验
你还需要把那些观察到的、现实生活中的互动与你的机器学习功能联系起来。理想情况下,您应该向一组随机的用户公开您的新特性,而不让其他组看到它。然后,您可以根据谁可以使用或已经看到新功能,以及谁没有看到新功能,来衡量集团层面的成果,包括收入、留存率、销售额、客户终身价值或其他与您的业务相关的指标。可视化工程师还可以将这种商业智能放入仪表板,并清楚地指出各组之间的行为模式。利用实验及其结果,对你所交付的工作和经验的实际价值进行衡量。利用这一点来帮助通知新的投资,并确保您为客户创造价值。
您还想知道为您的功能提供动力的算法本身是否是最好的。除了仅仅测试特性/无特性组,训练模型的新版本,或者一起尝试新的方法。在 A/B 测试中一次部署两个(或更多)版本,并再次与您的可视化工程师合作,以测量和描述组间行为和结果的差异。确定新的冠军模型,并依靠 MLE 为所有用户部署到生产中。只要您能够创建新的 challenger 模型进行测试,就可以重复这个循环。
将团队整合在一起
机器学习工程师、可视化工程师和实验负责人可以一起工作来完成机器学习产品的生命周期。如果不在产品中观察你的特性,并围绕它的方法进行实验,你将无法改进你当前的工作或确保你为你的用户提供价值。
招募和留住机器学习工程师可能非常困难。如果你能做到这一点,一定要用可视化和实验能力来激励团队,让他们从头到尾看到 AI/ML 项目。
不要让工具和管理方法扼杀你的人工智能创新
与 ModelOp 合作的文章
图片来自 Canva
在企业面临巨大的创新压力之际,它们对人工智能的投资达到前所未有的水*,这绝非巧合。数据科学家开发的人工智能模型为企业提供了新的见解,实现了新的更高效的工作方式,并有助于发现降低成本和推出有利可图的新产品和服务的机会。
人工智能应用的可能性几乎每天都在增长,所以不限制创新很重要。不幸的是,许多组织正是通过将自己束缚于专有工具和解决方案来做到这一点的。随着新的创新变得可用,这可能会束缚数据科学家和 IT,并导致比支持最佳人工智能模型开发和管理的开放环境更高的成本。本文介绍了在企业人工智能中避免专有锁定的指南,以及这样做的重要性。
人工智能进化与分析有相似之处,但区别更重要
人工智能产生于数据分析,后来演变为商业分析和商业智能。早期的分析由少数专有解决方案主导,这些解决方案拥有一个由创新公司开发补充工具和技术来增强供应商*台的有限生态系统。这减缓了分析的采用。
相比之下,人工智能市场是蛮荒的西部。数百家公司,从一些最知名的科技公司到令人难以置信的创新创业公司,都在为人工智能和人工智能模型生命周期的每个阶段提供解决方案。企业不必像竞争对手一样使用同样有限的工具和技术,他们正在利用优势。根据 2021 年 ModelOps 报告的数据,2021 年,81%的金融服务公司正在使用一种以上的人工智能模型开发工具,42%的公司正在使用至少五种。这甚至还没有算上用于人工智能其他阶段和模型生命周期的工具和解决方案。
许多工具都是为特定目的(如欺诈检测、辅助购物)或执行环境(如内部硬件或 AWS 和其他超级云服务)开发模型。为数据科学家提供多种专门构建的最佳模型开发选项,拓宽了可能和有价值的用例的范围,这有助于人工智能在组织内的使用增长。
没有一个工具是适合整个人工智能模型生命周期的
当组织依赖于他们用来创建模型的相同工具在生产中运行和管理它们时,就会发生人工智能锁定。在分析的早期,模型和模型操作更加透明,这可能还可以,但现在模型太复杂了。模型开发的需求和工具与模型操作非常不同。生产中的模型必须与更多的系统交互,包括业务应用程序、风险管理框架、IT 系统、信息安全控制等等。
模型开发工具是为数据科学家设计的,用于简化模型创建工作。模型操作解决方案是为 IT 而设计的,以便在所有企业模型的整个生命周期中高效地管理它们的操作。
也请阅读【ModelOps 如何帮助你执行你的人工智能策略
当一个模型从开发到生产时,过程、任务和用户都会改变,更不用说开发和生产环境和操作之间的检查和*衡了。
有效模型开发的基础是:
- 根据期望的模型结果分析和准备模型使用的数据
- 开发或编码模型并进行实验
- 用各种数据集测试模型并选择使用的模型
有效模型操作的基础是:
- 了解企业的整个模型库存,无论模型类型、用途或运行位置如何
- 在模型的整个生命周期中,为每个模型的持续操作和治理提供流程自动化
- 所有模型的状态和状态及其业务贡献的企业视图
不具备特定于模型开发和模型操作的能力会使您的企业面临更高的 AI 性能问题风险、用于故障排除和再培训的过多模型停机时间,并可能导致不可靠的决策以及法规和合规性问题。
不一定非要那样。模型操作(ModelOps)管理解决方案不受技术限制,因此可以支持您的数据科学团队可能想要使用的所有开发工具和执行环境。你需要的管理工具越少,你的人工智能程序就越容易扩展、协调和管理。
作者精心制作的信息图
灵活性有助于赢得人才大战
众所周知,数据科学家需求量很大。假设你是一名经验丰富的数据科学家,正在仔细考虑招聘人员每周为你提供的一些高薪工作机会。你更有可能回应以下哪个职位?
另请阅读安全可靠人工智能的模型操作
“拥有[特定人工智能编程语言]经验的数据科学家需要创建和维护在[特定 IT 基础设施]中运行的模型。”或者
“使用您喜欢的工具和开发环境,在我们的行业中用人工智能开创新局面。”
你的组织不应该因为你没有让这个人变得有价值的经验,而错过雇佣一个拥有令人垂涎的专业技能的人的机会。
云不会带来更多的清晰度
有一个学派认为,随着人工智能的成熟,其生态系统将变得稀薄,最终大多数人工智能模型将在云中运行。因此,这种想法是,企业可以通过为其喜爱的云环境开发或由其开发的解决方案来满足其人工智能模型运营管理需求。事实上,这不是今天的情况,也可能永远不会。混合、多云环境是人工智能的标准:58%的金融服务公司至少在 AWS 中运行一些人工智能模型,但是,58%的公司也在谷歌云*台(GCP)中运行模型,27%的公司在 Azure 中运行模型,48%的公司在内部执行人工智能模型【1】。这些数字总计超过 100%,这意味着企业正在采用混合基础架构并在不同的地方运行模型,选择最佳环境来执行每个特定的模型。管理模型操作也应该采用这种方法。致力于为一个云*台优化的 AI ModelOps 解决方案会产生经典的供应商锁定问题。这种锁定给业务运营的任何领域带来了问题;对于人工智能来说,它可能会对人工智能为企业所做的事情施加昂贵而不必要的限制。
你的人工智能创新和努力太重要了,不能相信单一的解决方案、执行环境或有限的供应商。你不能用开发 IT 系统或业务应用程序时所用的工具来管理它们——为什么不对人工智能采取同样的方法呢?企业需要开放自己,以利用人工智能成熟和进步带来的所有可能性。开发一个人工智能模型操作*台,该*台可以支持广泛的工具和未来发展,让您的数据科学团队能够自由创新,让您的 IT 团队能够高效和有效地治理和管理整个企业中的所有人工智能模型,而不管它们是如何开发的、用于什么目的或在哪里运行。
【1】Corinium Intelligence 与 ModelOp《2021 年 ModelOps 状态报告》2021 年 4 月 14 日。
关注我的每日技术和创新更新
不要让你的数据让你失望
使用 whylogs 和 Github 操作进行连续数据验证
从 ML 管道的开始到结束,数据是最小的公分母。但是数据的普及也有其不利的一面,因为几乎你的 ML 管道中的每一个问题都以这样或那样的方式源于或影响数据,并且可能以复杂和错综复杂的方式。例如,服务期间出现的坏数据——无论是从外部来源引入的还是在数据转换过程中产生的——不仅会影响当前的预测结果,还会在未来的模型重新训练过程中重新引入循环。
这是许多例子中的一个。底线是,在开发您的 ML 管道时,确保数据质量应该是您的首要任务之一。为了做到这一点,数据验证无疑是一个关键的组成部分。在本文中,我们将展示 whylogs 如何帮助您实现这一目的。我们将首先介绍约束的概念以及如何生成它们。一旦创建了这些约束,就可以通过在 whylogs 日志记录会话中应用它们,将它们直接集成到管道中。在最后一节中,我们将看到另一种验证数据的方法,即在 Github 动作的帮助下,将这些约束集作为持续集成管道的一部分来应用。
本文的代码和文件可以在项目的资源库中找到。您还可以在这个示例笔记本中找到关于生成约束的更多信息,以及在这个示例存储库中找到关于 whylogs 与 Github 动作集成的更多信息。
让我们开始吧。
目录
- why logs 中的约束生成
∘ 值约束 vs 汇总约束
∘ 组装数据集约束
∘ 将约束应用于数据集 - github actions with whylogs
∘概述
∘ 配置工作流
∘ 工作流语法
∘ 约束定义 - 接下来是什么
为什么日志中的约束生成
为了验证您的数据,我们需要一种有效的方式来表达我们的期望。这是在 whylogs 中通过约束完成的——您创建的规则用于断言您的数据位于预期范围内。这些约束反过来会应用于数据集的要素,并且可以通过一种方式进行组织,即一个要素可以有多个约束,一个约束可以应用于多个要素。
值约束与汇总约束
可以针对单个值或完整的数据集配置文件检查约束。对于值约束,将根据数据集中要素的每个值来验证布尔关系。但是,对于 summary 约束,这种关系是根据 whylogs“profile”来验证的,它是 why logs 已经处理的数据集的汇总统计信息的集合。
例如,假设我们要创建一个约束,以确保数据集中每条记录的特征值都小于 3.6。这可以通过一个值约束来实现:
ValueConstraint
接受两个参数:二进制比较运算符的类型(“小于”)和要与传入流进行比较的静态值。
然后,我们简单地将约束从 protobuf 转换成 JSON,这将产生 JSON 格式的输出:
默认情况下,名称是自动生成的,但是也可以通过向ValueConstraint
传递一个名称参数来定制。
类似地,我们可以用SummaryConstraint
生成一个针对统计属性的约束:
上面的代码产生:
组装数据集约束
我们已经了解了如何创建单个约束,但是对于给定的数据集,我们希望将多个约束组合在一起,以便对数据的外观有一个总体的描述,然后将约束列表应用到我们的数据集。
为了演示,让我们使用 Kaggle 中的 LendingClub 数据集。已用子集包含 1000 条通过 LendingClub *台进行的贷款记录。我们将创建一些约束来验证数据集的三个特征:
- loan_amnt —借款人申请的贷款金额;
- fico_range_high — 借款人 fico 在贷款发放时的上限范围属于;
- 年度 _ 公司— 借款人的年收入。
对于 loan_amnt ,我们将设置上限和下限为 548250 和 2500,对于 fico_range_high 最小值为 400 。最后,让我们断言 annual_inc 只有非负值。
这将为我们提供以下信息:
为了保持约束并重用它们,我们可以将它们保存在一个 JSON 文件中。我们将在下一节中需要这个文件,在这里我们将在 CI/CD 管道中集成我们的约束。
将约束应用于数据集
一旦创建了约束,我们就可以最终将它们应用到数据集。为此,我们只需将约束作为参数传递给 log_dataframe(),同时将记录记录到数据集中。
该报告可通过dc.report()
访问,并在一些基本格式化后显示,以使其更具可读性:
Constraint failures by feature -
loan_amnt:
test_name total_run failed
value LT 548250 1000 2
value GT 2500.0 1000 20
fico_range_high:
test_name total_run failed
value GT 4000 1000 1000
annual_inc:
test_name total_run failed
summary min GE 0/None 0 0
在这种情况下,每个规则的值约束被应用了 1000 次,失败列显示了我们的数据有多少次没有达到我们的预期。然而,从total_run
字段可以看出,汇总约束还没有应用。
汇总约束可以应用于现有的概要文件。由于在创建概要文件时已经提供了约束,我们可以不带参数地调用apply_summary_constraints()
:
Constraint failures by feature -annual_inc:test_name total_run failedsummary min GE 0/None 1 0
我们也可以用一个新的汇总约束来覆盖原来的汇总约束,例如:
GitHub 操作和原因日志
到目前为止,我们已经看到了如何将 whylogs 约束直接应用到我们的管道中。或者,我们可以用 Github Actions 来验证我们的数据,作为我们持续集成管道的一部分。
Github 动作通过支持工作流的创建,帮助您自动化软件开发生命周期。工作流是您添加到项目存储库中的自动化过程,它由一个、事件触发,例如每当推送提交或者创建拉取请求时。工作流本身是通过组合一系列构建块来创建的,其中最小的一个被称为动作。使用 Github Actions,您可以以自动化的方式测试、构建和部署您的代码。
概观
有了 whylogs,我们可以将 Github 动作的范围扩展到不仅测试代码,还测试数据。让我们通过想象一个简单的数据管道来演示,在这个管道中,我们从一个数据源获取数据,然后对它应用一个预处理例程。
作者图片
每个验证步骤都有不同的目的:当对源数据应用一组约束时,我们感兴趣的是评估数据本身的质量。外部数据源中的更改可能随时发生,因此,为此,我们将安排作业以固定的时间间隔运行。在用内部代码预处理数据之后,应用第二个验证步骤。在这种情况下,我们的目标是测试数据管道的质量。因为我们希望在每次代码更改时运行这些约束,所以每当有人提交时,我们也将执行作业。出于演示的目的,我们只创建一个由两个不同事件触发的作业。另一种方法是在测试数据管道时,通过修复数据集的一个版本来保持事物的分离。
每次触发工作流时,Github 都会记录信息,因此您可以从项目存储库中的 Actions 选项卡检查输出。此外,每当您的数据不符合您的预期时,它都会向您发出警告。
作者 Gif
配置工作流
要构建工作流,需要通过在.github/workflows
文件夹下创建一个. yml 配置文件来定义它的配置。我们将只定义一个作业——whylogs_constraints
——每当有人向存储库提交一个提交时,就会触发这个作业。
概括地说,工作流程很简单——我们将使用fetch_data.py
从给定的源获取数据,这将生成lending_club_1000.csv
文件。csv 文件根据github-actions/constraints-source.json
中定义的一组约束进行验证。如果数据符合我们的预期,下一步就是对其进行预处理。预处理例程将依次创建一个名为lending_post.csv
的预处理数据集,该数据集将根据github-actions/constraints-processed.json
中定义的一组单独的约束再次进行验证。在本例中,我们将简单地删除带有nan
的行,并将其缩放到 0–1 区间,用于 loan_amnt 列。
值得注意的是,在提交之前,csv 文件不需要存在于我们的存储库中。这些文件将在工作流执行期间在运行器中创建,并且不会保存在我们项目的存储库中。
工作流语法
让我们讨论一下配置文件中的一些行:
on: [push] —指定工作流的触发事件。每个推送事件都会触发工作流。
on: [schedule] —指定工作流的触发事件。它将按计划触发。在本例中,工作流将在每天的 05:30 和 17:30 执行。
runs-on:Ubuntu-latest—指定虚拟机的环境。
用途:actions/checkout@v2 —为了对我们的代码运行操作,我们需要首先将存储库签出到 runner 中,这是通过使用 actions/checkout 社区操作来完成的。
用途:why labs/whylogs-action @ v1—预打包的 why logs 操作,用于将我们的约束应用到所需的数据集。要使用它,我们还需要提供一些参数:
- constraints file:JSON 中要应用的约束集
- 数据文件:包含应用约束的数据的文件。熊猫可以加载的任何格式都可以,但是 CSV 效果很好。
- expect-failure: 即使我们通常编写预期成功的操作,whylogs 也允许您通过设置该标志来创建预期失败的操作。默认为 false。
约束定义
在这个例子中,我们使用了两组约束:constraints-source.json
和constraints-processed.json
。对于第一个问题,我们将使用本文前一节中生成的相同约束:
对于已处理的文件,我们将定义一个summaryConstraint
来验证规范化的 loan_amnt 特性确实在 0–1 范围内:
如前所述,我们可以创建预期失败或成功的操作。为了演示这两种情况,我们预计constraints-source.json
会失败而constraints-processed.json
会成功。
下一步是什么
Whylabs 团队不断扩展 whylogs 以支持更多功能。关于制约因素,正在考虑以下特点:
如果你也喜欢/想要这些功能,请随意喜欢/评论上面相关的 Github 问题!
至于 CI 管道,在数据验证方面可以做得更多。真实的场景肯定会有更复杂的数据管道,因此需要对数据进行更多的验证。为了进一步提高系统的可靠性,我们不仅可以对模型输入施加约束,还可以对输出和实时推理施加约束。结合 whylogs 配置文件,约束使数据科学家和 ML 工程师能够信任他们的 ML 管道!
如果你对探索项目中的 whylogs 感兴趣,考虑加入 Slack 社区来获得支持并分享反馈!
不要让你的模型质量渐行渐远
MLOps
解决生产 ML 系统中的数据漂移和概念漂移
丹尼·斯莱文霍克在 Unsplash 上拍摄的照片
您已经收集和清理了您的数据,试验了各种机器学习模型和数据预处理变体,并微调了您的模型的超参数,最终提出了足以解决您的问题的解决方案。然后,您已经构建了一个健壮的自动化数据管道,为模型编写了一个 API,将它放在一个容器中,并将其部署到生产环境中。您甚至确保检查模型在生产中运行顺畅且正确。终于,你完成了!还是你?差远了。事实上,这只是旅程的开始。
在机器学习系统被部署到生产中后,它可能会出现很多问题!概括地说,我们可以将所有这些潜在的问题分为两类:统计问题和基础设施问题。后者包括像计算资源和内存这样的东西(是否足够?),延迟(模型响应是否足够快?),吞吐量(我们能回答所有进来的请求吗?),等等。但是在本文中,我们将关注前者:统计问题,它有两种主要形式:数据漂移和概念漂移。
数据漂移
数据漂移也称为协变量漂移是指模型输入的分布发生变化的情况。它可能以多种方式出现。例如:
- 当数据由某个传感器收集时,设备可能会发生故障或收到影响测量方式的软件更新。
- 当数据是关于人类时,它会随着时尚或用户统计数据而变化。
因此,模型看到的定型数据并不代表实际到达生产环境的数据。
让我们介绍一些温和的符号。设 P(x) 为输入特征 x 的边际概率,而 P(y|x) 为给定输入特征 x 时目标 y 的条件概率。此外,我们将把概率下标为 Pₜ 来指代训练数据,下标为 Pₛ 来指代服务数据,即生产中的模型要为其提供预测的数据。
在这种表示法中,我们可以将数据漂移定义如下:
pₜ(y|x)= pₛ(y|x)
pₜ(x)≠pₛ(x)
这实际上意味着,在训练数据和服务数据之间,输入特征的分布发生了变化,但是输入和目标之间的关系没有改变。
数据漂移是训练数据和服务数据之间输入要素分布的变化。
数据漂移可能会对模型的性能产生不利影响,尽管不一定需要如此。考虑下面的图片。
数据漂移,作者带给你的。
在训练时,模型已经学习了蓝色类和橙色类之间的合理决策边界。在首次展示后,发生了数据漂移。在顶部的场景中,orange 类示例的分布发生了变化,因此现在只能观察到 y 轴要素的低值。在这种情况下,模型用来对新示例进行分类的决策边界仍然工作正常。
数据漂移可能会也可能不会降低模型的性能。
然而,在底部的场景中,橙色类的分布发生了变化,从而出现了 y 轴要素的高值。在这种情况下,模型的性能可能会恶化。
概念漂移
概念漂移是模型的输入和输出之间的关系已经改变的情况。这可能是由多种原因造成的。毕竟,世界一直在变化。风格和时尚来来去去,企业扩张到(或收缩到)新的地理位置,竞争对手进入或离开市场,宏观经济条件也各不相同。
根据之前定义的符号,概念漂移可以描述如下:
pₜ(y|x)≦pₛ(y|x)
pₜ(x)=**pₛ(x)
它是输入要素和目标之间映射的变化,而输入的分布保持不变。
概念漂移是输入特征和目标之间关系的变化。在大多数情况下,它会降低模型的性能。
概念漂移几乎总是对机器学习系统的性能有害。考虑下图。
概念漂移,作者带给你的。
概念漂移发生后,现实世界中的 x-y 映射发生了变化,但模型没有办法知道。因此,它会根据在训练阶段学到的过时映射对一些示例进行错误分类。
探测漂移
我们已经定义了威胁生产机器学习系统的两个主要统计问题:数据漂移和概念漂移。但是如何去探测它们呢?这一切都归结为监控模型输入、输出以及训练和服务数据中的实际值。
监控训练和服务数据中的模型输入、输出和实际值是漂移检测的关键。
要发现数据漂移,我们需要逐个要素地比较训练数据和服务数据之间的输入要素分布。另一方面,为了检测概念漂移,我们应该查看给定特征的真实和预测目标的条件分布。由于服务数据通常会持续不断地进入系统,因此这种监视和比较分布的行为需要定期进行,并且尽可能地自动化。此时,问题归结为决定两个数据样本是否来自相同的概率分布。如何做到这一点?
使用 TensorFlow 数据验证监控数据统计
判断两个数据样本是否来自同一个分布的最基本方法是对两个样本计算一些简单的统计数据并进行比较。使用 TensorFlow 数据验证可以轻松完成。
TensorFlow 数据验证(TFDV)是一套有用的数据监控工具。它有两个重要的实用功能:
generate_statistics_from_dataframe()
-计算数据帧的统计数据,例如*均值、标准偏差、最小值和最大值、缺失数据的百分比或零值的百分比。visualize_statistics()
—生成交互式图表,允许直观检查和比较数据统计。
让我们在实践中看到它。让我们以 Kaggle housing 数据集为例,将其随机分为训练集和测试集,并使用 TFDV 比较数据统计。
TFDV 关于住房数据集的数据统计比较,由作者编写。
上面你看到的是数字特征的图表,还有一个对应的分类特征的图表。它可以很容易地比较特征分布和现场报警的情况,如许多零在一个特征。
假设检验
虽然直观检查和比较训练数据和服务数据之间的简单统计数据很容易,但涉及主观判断,很难自动化。更严格的方法是依靠统计假设检验。
假设检验提供了一种比较特征分布的严格和自动化的方法。
如果我们希望比较的两个数据样本(例如给定特性的训练数据样本和服务数据样本)是正态分布,我们可以运行 t-test。这是一个统计程序,用来检查两个样本的*均值是否相同。让我们看看在我们的训练和测试数据中,*均价格是否相同。
Ttest_indResult(
statistic=0.0021157257949561813,
pvalue=0.9983126733473535
)
几乎为 1 的大 p 值告诉我们,没有证据拒绝*均价格相等的检验的零假设。如果你需要重温假设检验背后的哲学,以及 p 值、检验统计和显著性水*的意义,请查阅我的假设检验者指南:
我们也可以一次测试多个特性。为了检查一对数字特征的*均值是否相同,我们可以运行 ANOVA 测试。对于分类变量,卡方检验将比较它们的频率。在我关于概率分布的文章中,你可以找到这两个测试的详细描述和 Python 例子。只需向下滚动到方差分析的“f 分布”部分,以及 χ 2 检验的“卡方分布”部分。
如果数据样本不是正态分布,我们仍然可以使用非参数检验来比较它们。 Kruskall-Wallis 检验或 Kolmogorov-Smirnov 都旨在验证两个数据样本是否来自同一个分布。更具体地说,Kruskall-Wallis 检验了两个数据样本的总体中位数相等的零假设。你可以把它想象成 ANOVA 的非参数版本。另一方面,Kolmogorow-Smirnov 通过检查经验分布之间的距离直接测试两个样本是否来自同一分布(它只对连续特征有效)。
KruskalResult(statistic=0.03876427, pvalue=0.8439164)
KstestResult(statistic=0.05504587, pvalue=0.9469318)
对于我们的住房数据中的价格,两种测试都表明,训练和测试数据中的价格分布是相同的。这有道理;毕竟,我们已经随机进行了训练测试。
对抗漂移
一旦我们检测到系统发生漂移,该怎么办?一个快速的回答是:再培训!但真正的问题是如何去做。
这并不像看起来那么容易。图片由作者提供。
最简单的方法是根据最新数据(包括漂移后收集的数据)重新训练模型的参数。然而,如果漂移被早期检测到,则可能不会有大量的漂移后数据。如果是这样的话,这么简单的再培训也帮不了多少。
简单地对所有数据重新训练模型可能不起作用。
另一种方法是给训练样本分配权重,使得模型更加关注漂移后的数据。然而,这些最*的数据可能并不代表正在建模的问题。例如,后漂移期可能会错过之前发生的一些与漂移无关的事件。在这种情况下,将更多的权重放在漂移后的例子上会阻止模型学习有用的模式。
在实践中,正确的解决方案通常依赖于问题和领域,它可能涉及使用新旧数据集成模型,甚至添加全新的数据源。最后,如果漂移很大,可能有必要重新调整模型的超参数,以适应漂移后的新世界。
感谢阅读!
如果你喜欢这篇文章,为什么不在我的新文章上 订阅电子邮件更新 ?通过 成为媒介会员 ,你可以支持我的写作,并无限制地访问其他作者和我自己的所有故事。
需要咨询?你可以问我任何事情,也可以在这里 预定我 1:1 。
也可以试试 我的其他文章 中的一篇。不能选择?从这些中选择一个:
</6-useful-probability-distributions-with-applications-to-data-science-problems-2c0bee7cef28> [## 贝叶斯数据分析最温和的介绍
towardsdatascience.com](/the-gentlest-of-introductions-to-bayesian-data-analysis-74df448da25)
不要丢失数据:通过 CLI 保存 MySQL 数据库备份
如何轻松备份 MySQL 数据库
马库斯·温克勒@马库斯·温克勒在 Unsplash 的照片
本文将快速描述如何通过 CLI 使用 mysqldump 保存数据库的转储文件。在那里,您可以找到以下主题的代码和解释:
- 保存数据库转储文件
- 保存所有数据库或数据库列表
- 保存表格
- 如何在 Windows 上使用 mysqldump CLI
- 错误 1044 拒绝访问-锁定表
如果你曾经使用过数据库,你就会知道备份数据有多重要。数据丢失发生在你没有预料到的时候,你必须做好准备。保存数据库中的转储文件是防止数据丢失的一种方法,甚至更多。
转储文件的主要用途是在数据丢失的情况下备份数据,但除此之外,转储文件还可以用于多种其他用途,如复制数据库并将其用于实验、将数据库的副本发送给某人、作为副本的数据源等等。
保存数据库转储文件
要保存数据库转储文件,首先必须打开终端并键入以下命令。在调用 mysqldump 命令时,必须向数据库的服务器传递一个具有读取权限的用户,数据库的名称,并在“>之后传递转储文件的位置和名称。
mysqldump -h <server> -u <user> -p <database> > file.sql
运行这行代码后,它会询问用户密码,并将您的文件保存在传递的目录中。
保存所有数据库或数据库列表
如果你想一次保存所有的数据库,你可以使用命令“all-databases”和服务器及用户。
mysqldump -h <server> -u <user> --all-databases > file.sql
如果你想一次保存多个数据库,你可以使用命令“databases ”,传递一个你想保存的数据库列表,后面跟服务器和用户。
mysqldump -h <server> -u <user> --databases <db1> <db2> <db3> > file.sql
在运行这些行之后,它将询问用户密码,并将您的文件保存在传递的目录中。
保存表格
如果只想保存一个表,只需在命令中的数据库后面添加表名。
mysqldump -h <server> -u <user> -p <database> <tablename> --single-transaction > file.sql
如何在 Windows 上使用 mysqldump CLI
要在 Windows 上使用它,您只需打开命令行并添加 MySQL 工作台的路径,然后传递命令保存您的数据库。
set path=C:\Program Files\MySQL\MySQL Workbench 6.3 CEmysqldumpmysqldump -h <server> -u <user> -p <database> > file.sql
错误 1044 拒绝访问-锁定表
如果您有一个锁定的表,您会得到“1044 拒绝访问错误”和以下消息:
"获得错误:1044:使用锁表时,用户' user'@'% '对数据库' database '的访问被拒绝"
要解决这个错误,您只需在代码中添加命令“single-transaction”。单个事务命令将强制它在发出 BEGIN 时转储数据库的一致状态,而不会阻塞任何其他应用程序。
set path=C:\Program Files\MySQL\MySQL Workbench 6.3 CEmysqldump -h <server> -u <user> -p <database> --single-transaction > file.sql
如果您想了解更多关于 mysqldump 命令的信息,或者如果您正面临我所介绍的不同场景,请访问以下链接中的 MySQL 文档:
非常感谢您的阅读!我希望这些技巧能帮助你保存 MySQL 数据库的备份,防止项目中的数据丢失。
有任何问题或建议可以通过 LinkedIn 联系我:【https://www.linkedin.com/in/octavio-b-santiago/
更多阅读
[## power bi——击败 Excel 的工具
towardsdatascience.com](/powerbi-the-tool-that-is-beating-excel-8e88d2084213)
不要让闯入数据科学变得比需要的更难
我是如何在没有学位或工作经验的情况下找到第一份数据相关工作的
课程完成,现在我准备好了!
重新学习线性代数-检查。
重新学习统计数据—检查。
重新学习微积分——检查。
学习 Python —检查。
参加 Coursera 上的吴恩达机器学习课程。
完成一个文件夹项目——检查。
终于!
https://medium.com/analytics-vidhya/courses-to-learn-data-science-in-2021-a52e64344e5c
2019 年已经过去了 4 个月,我终于准备好从邮件室进入数据世界。我知道我即将踏上人生中最具挑战性的旅程之一,但我的一位同事在我加入邮件室的最初几个月里说的一句话一直在我脑海中回响——
“任何在收发室工作超过 5 个月的人都会留下来。他们可能会换部门,但不会离开。”—前同事
这是我离开的动力。
今年的变化标志着他连续第 18 年在邮件室,所以我想他显然知道他在说什么。不要误解我的意思,在收发室或公务员队伍工作绝对没有任何问题。只是不适合我。
我在邮件室里呆了 6 个月。根据我同事的说法,从统计学上来说,我完全没有希望。我注定要在接下来的 53 年职业生涯中为伦敦金融城政府邮寄信件。
希望你能想象我有多渴望打破诅咒…
问题是,我知道在找工作时,蛮力策略不是我的强项——我说的蛮力是指申请数百份不同的工作。雪上加霜的是,我之前没有数据科学方面的经验,没有可以依靠的学位,也没有面试工作的经验,因为我挂靴后做过的所有工作都是通过一家机构完成的,而且都是体力劳动。
公*地说,有很多东西值得害怕…我知道如果我要进入数据科学领域,我必须采取不同的策略。
我仍然记得有一天,我的另一位同事向我透露了一个名为 Meetup — 的应用程序,这是一种用于组织在线团体的服务,为有相似兴趣的人举办面对面和虚拟的活动。对于我向数据科学的转变来说,这是革命性的。
下载完 app,花了点时间浏览,发现了几个事件。在接下来的几个月里,我每周二和周四晚上都会参加与数据科学相关的活动。
然后我得到了我的休息…这是一个 IBM 的事件。
我像参加任何活动一样,提前到了那里,并像往常一样,在主持人的眼皮底下选了个座位。没过多久,房间就满了(Covid aye 之前的美好时光)。一个神秘的留着胡子的男人姗姗来迟地走进来,拖着脚走到前面,占了唯一一个空着的座位——就在我旁边。
我没多想,对他笑了笑,继续听着…
到了开始实际工作的时候,有些东西我们必须从 Github 中获取,这样我们才能体验到主持人教给我们的工具。我旁边的神秘人被什么东西卡住了,但是主持人和他的支持团队正忙着帮助其他人。
突然,我感到有人拍了一下我的肩膀—
“嘿伙计,你以前用过这个吗?”
“是啊,你需要帮忙吗?”我回答道:
“如果你不介意的话,请吧!”
我用我的笔记本电脑演示了整个过程,而他跟在后面。这意味着在演示的某些时候,我必须导航到我在 Github 上的存储库…
他的下巴掉了下来—
“哦...你已经做这些东西了?”他说,
"还没有找到工作,但我现在正在积极找工作."
在会议结束时,他问我是否可以让他快速浏览一下我在 Github 上的一些项目。他印象非常深刻。我们交换了电话号码,仅此而已。我对此毫不在意,照常生活。仍然参加我周二和周四晚上的聚会。
有一天,我收到一条短信…
“嘿,库尔提斯!
是*****。几周前我们在 IBM 的会议上见过。我很抱歉没能早点联系到你,因为办公室里太忙了。我想知道你下周是否有空。我把你的情况告诉了首席技术官和首席执行官,他们说他们愿意和你坐下来谈谈,这样你就可以向他们展示你给我看过的项目。除非你准备好了。"
我的心脏几乎停止了跳动!
首先,我一生中从未和首席执行官说过话。其次,我甚至不知道 CTO 是什么意思。我决定在回答之前嚼点口香糖——“嘿*****,没关系。你真是太好了,我很乐意介绍给他们。我会查看我的日程表,然后尽快回复你。显然,我的日历是免费的,我只是不想显得太绝望。
最后,首席执行官被叫去参加最后一分钟的会议,这意味着他不能出席我的演示。我仍然必须向 CTO 展示,这很尴尬,因为这是我第一次亲自向别人展示我的代码。
不管怎样,他在整个过程中都给了我极大的安慰。他从未让我对自己的编码能力感到不安全。他接着问了我一些关于这个项目的问题,然后在这一轮问题的最后,他问我是否有问题,我说“没有”,然后他提出了这个大问题…
你希望的薪水范围是多少?
剩下的就是历史了。
经验教训
把证据放在布丁里
这个类比听起来可能很疯狂,但进入数据世界的大门实际上并没有那么难。我这么说不仅仅是因为我童话般的入口,而是和我一起想一想…
当一家公司雇佣一名数据科学家(或任何人)时,他们希望找到为公司增加价值的人——无论这意味着省钱、赚钱、节省时间等。
通过创建一个投资组合项目,或者“把证据放进布丁”,你证明了你有能力满足至少一个标准,为公司增加价值。进一步补充,你也含蓄地告诉公司,你是一个自我激励者,因此,一旦他们雇用你,他们就不必照看你了——这也是一个很好的方式,他们不必在你的发展上投入太多,但这是另一天的辩论。
我不得不收回我的话。我记得我说过进入数据科学领域最困难的部分是找工作,但是经过一番思考,我认为这可能与事实相差甚远…
进入数据科学真正困难的部分是建立从数据中获得有用见解所需的知识基础。这段旅程需要极度投入,从错误中学习,克服不知道自己是否在正确的道路上的感觉,以及自律。
一旦我们拥有了这种超级力量,这个世界就是我们的了。只要有我们希望用数据解决的问题,或者我们希望用数据回答的问题,我们就有一个项目。此外,由于有了互联网,数据几乎可以在任何地方找到,并且可以通过各种不同的方式访问。
解决问题和解决问题的决心完全取决于我们,我们可以决定在项目中投入多少努力。由我们来决定我们是否要在我们的项目中加入一篇评论——这是我推荐的——以及我们是否要公开分享它,以便其他人可以评论我们所做的事情。
虽然这可能意味着你不会因为使用数据来寻找见解而获得报酬,但它可以作为一个稳定的训练场,让你习惯于当你进入这个领域时生活会是什么样的,因此我相信,任何试图进入这个领域的人都最有兴趣致力于建立一个项目,帮助他们进入这个领域。
利用你的关系网——不是所有的工作都有广告
是我的关系网让我找到了第一份使用技术的全职工作。
是我的网络为我提供了在各种播客上亮相的机会。
是我的关系网给我提供了一个开始自由职业的机会。
这个故事的寓意是你必须建立关系网!
“你知道什么没有你认识谁重要,但你还是应该知道一些事情。”
有时候,当我谈到网络的力量时,我觉得自己疯了,因为我从来没有对自己说“我要开始网络,这样我就可以建立一个网络”。
如果你想知道我建立人际网络的秘密,很简单——对他人感兴趣。
这可能表现在许多方面:
- 参与他人的内容(不是“感谢发帖”——尝试增加价值)
- 分享有助于他人的内容
- [偶尔]不怕麻烦地帮助别人
没有神奇药水。
在我看来,仅仅在 LinkedIn(或任何其他社交媒体)上与某人联系不算社交,或者至少不算良好的社交。我认为良好的人际关系意味着给某人留下持久的积极印象。因此,当机会出现时,你会在他们的脑海中浮现。
我知道这不是一朝一夕的事。当你想给某人留下一个积极持久的印象时,了解他会有所帮助,但是能够以一种不需要的方式交流你的需求也是非常重要的。例如,在我上面分享的故事中,我向后来成为我的直线经理的神秘人传达了我正在找工作,对他没有任何期望。我真诚地告诉他我的现状。
永远努力成为一个给予者。
最后的想法
虽然我知道我是如何迈出这一步的错综复杂是非常独特的,但我仍然觉得我采取的许多行动使得这个解决方案对于任何愿意耐心等待的人来说都是非常可重复的。我知道我们目前正处于疫情,所以参加聚会并不容易,但仍然有可能通过各种社交媒体渠道,如 Twitter、LinkedIn 和 Medium,与人们建立有意义的联系。
以后要不要我做一个网络小指南?
在 LinkedIn 和 T2 Twitter 上与我保持联系,了解我关于数据科学、人工智能和自由职业的最新消息。
相关文章
</4-data-related-books-ill-be-reading-in-april-efd06b367e35>
不要在缩放数据时犯这种错误
MinMaxScaler 可以返回小于 0 且大于 1 的值。
凯利·西克玛在 Unsplash 上的照片
MinMaxScaler 是机器学习中最常用的缩放技术之一(紧随 StandardScaler 之后)。
通过将每个要素缩放到给定范围来变换要素。
该估计器单独缩放和翻译每个特征,使得它在训练集的给定范围内,例如在 0 和 1 之间。
通常,当我们使用 MinMaxScaler 时,我们在 0 和 1 之间缩放值。
您知道 MinMaxScaler 可以返回小于 0 且大于 1 的值吗?我不知道这件事,这让我很惊讶。
如果您感兴趣,Udacity 提供免费访问:
- [Intro to Machine Learning with PyTorch](https://imp.i115008.net/c/2402645/788201/11298)- [Deep Learning Nanodegree and more](https://imp.i115008.net/c/2402645/788202/11298)
问题是
让我们看一个例子。我用两个特性初始化 scaler。
import numpy as np
from sklearn.preprocessing import MinMaxScalerdata = [[-1, 2], [-0.5, 6], [0, 10], [1, 18]]
scaler = MinMaxScaler()
scaler.fit(data)
现在,让我们检查这两个特性的最小值和最大值:
scaler.data_min
# [-1\. 2.]scaler.data_max_
# [ 1\. 18.]
那些估计和预期的一样。
现在,让我们尝试输入大于最大值的值:
scaler.transform(np.array([[2, 20]]))# array([[1.5 , 1.125]])
标量返回一个大于 1 的值。
或更低的最小值:
scaler.transform(np.array([[-2, 1]]))# array([[-0.5 , -0.0625]])
标量返回一个小于 0 的值。
没什么大不了的,对吧?
当我们训练线性分类器时,这个问题可能发生,线性分类器将缩放特征与系数相乘。
分类器还没有看到某个特征的负值,它可以反转系数,这使得分类器工作不正确。
解决方案
我建议您将 MinMaxScaler 的输出限制在 0 到 1 之间。
scaler.transform(np.array([[-2, 1]]))# array([[0., 0.]])
在你走之前
- [Deploy Your 1st Machine Learning Model in the Cloud](https://gumroad.com/l/mjyDQ) [Ebook]- [Free skill tests for Data Scientists & Machine Learning Engineers](https://aigents.co/skills)
上面的一些链接是附属链接,如果你通过它们购买,我会赚取佣金。请记住,我链接课程是因为它们的质量,而不是因为我从你的购买中获得的佣金。
在 Twitter 上关注我,在那里我定期发布关于数据科学和机器学习的消息。
不要错过酒吧/餐馆
对发布/订阅模式的高层次概述,以及为什么您应该将它合并到您的项目中
发布/订阅模式并不是什么新东西,但是随着事件系统的日益复杂以及分布式计算的进步,发布/订阅越来越受欢迎。在本文中,我将解释高层次的发布/订阅模式,并尝试为您提供一些在不同规模的项目中包含该模式的理由。
不带发布/订阅
当构建需要两个或更多不同组件相互通信的复杂事件驱动系统时,传统且最简单的方法是将这些组件直接连接在一起。在某些情况下,这是通过使用 web 服务 API、*面文件交换或通过共享数据存储(如数据库)来完成的。这些方法可行,但也带来了一系列挑战:
- 模块耦合在一起,一个组件的改变可能需要更新与之交互的任何其他组件
- 构建和测试新的集成变得非常耗时
- 随着事件数量和集成点数量的增长,通常缺乏可伸缩性
作者图表
对于只涉及 2 到 3 个组件和很少数据的小型爱好项目,我发现这种典型的直接集成方法是极其有害的,它阻碍了我进行迭代增强。我发现添加一个新模块需要重新访问和重新配置几个组件,这令人望而生畏。
发布/订阅模式
输入发布/订阅模式。通信是通过一组通道完成的,而不是将基础设施的各个部分直接连接在一起。基础结构中的模块可以是发送事件的通道的发布者,也可以是读取事件的订阅者。这带来了一些好处,并有助于解决上述一些缺点。
- 模块变得彼此完全解耦,当涉及到集成时,它们的工作就是简单地正确格式化和发布数据,或者作为订阅者接收有效负载并知道如何处理它们
- 新组件的开发得到了简化,因为开发人员不需要担心各种集成点和网络层
- 通过为主题订阅单元测试脚本或者发布模拟输入,测试可以被隔离和简化
- 如果使用了正确的发布/订阅解决方案,您的系统应该变得无限可伸缩而不需要接触基础设施的任何其他组件
作者图表
结论
发布/订阅模式正在成为构建可伸缩和可维护解决方案的标准。如果您目前没有将这种模式作为基础设施的一部分,我强烈建议至少将它作为一种选择进行评估,因为减少维护和未来开发成本的好处可能会超过将您的解决方案迁移到这种可伸缩模式所需的努力。正如我前面提到的,我强烈推荐这种模式用于更小的爱好项目,以提高解决方案未来的灵活性,并获得使用发布/订阅模式的经验。我使用谷歌的 Pub/Sub 来完成我的大部分个人项目,因为它每月提供 10GB 的免费数据传输,这足以满足我的所有需求。
原载于 2021 年 2 月 15 日【http://www.theappliedarchitect.com】。
不要过度投资:如何有效地支持 ML 能力
组织不应该建立大项目来创建完美的模型,而是应该创建快速原型,并开始更快地从未接触的用例中获取价值。
通常被称为“80 / 20”的帕累托原则表明,大部分价值是在第一次小规模的努力中获得的。将这一概念应用于数据分析项目的战略管理,下面是这一效率原则的两个简单但有用的应用:
收益递减:可以在 20%的时间内发现 80%的模型精度。 在创造一个完美模型的时间里,我们可以创造五个不完美的模型。挖掘以获得更好的结果和手动调整模型最终成为一种“奢侈”的活动,应该应用于这样的情况,即花费时间来增加质量显然是有意义的。但在大多数情况下,即使是一个只有几个变量的基本线性回归方法,也是一个针对特定反应的更全面的方法,而不仅仅是目测或猜测。
正如这里举例说明的,在渐进采样 AutoML 中运行的模型质量迅速收敛(作者在 Einblick 自己的软件中)
快速失败:80%的价值存在于 20%的潜在建模问题中。我们需要创建一个流程来快速发现最有价值的时间投资领域,因为并非所有的分析最终都是有价值的。有些结果是不可预测的——要么是因为我们错过了正确的驱动程序,要么只是因为随机机会发挥了太大的作用。在这两种情况下,重要的是不要过度管理和项目管理分析。有效地获得第一个答案有助于确定讨论的优先顺序。
这个模型是在上面的 30 秒运行中构建的,是您与营销领导进行讨论所需的全部内容。(作者在 Einblick 自己的软件中)
这两个命题其实是趋同的:做出很多不完美的模型。快速原型化让我们过滤并快速击落糟糕的模型,快速部署让我们立即开始实现价值。更多的工作有递减的回报,并且考虑到团队的带宽有限,超过某个点的进一步投资是不明智的,因为它不会有效地产生结果。
当然,一些数据科学项目应该小心谨慎:例如,自动驾驶汽车或受监管的银行风险模型的对象识别。但是对于绝大多数实际的商业应用来说,要清除的障碍不是 100%的准确性,而是直觉的提高。
最终,在大多数组织中,有一长串决策是在有限的数据驱动指导下做出的,即使简单的 ML 也能够轻松实现增量。快速浏览一系列主题,找到“相当好”的答案,并广泛普及 AI/ML 是一个值得立即采用的数据策略。
最初发表于 ein blick:https://ein blick . ai/don-over-invest-efficiency-is-the-name-the-game-when-operationaling-ml/
Einblick 是世界上第一个可视化数据计算*台,创造了与数据最自然的交互。在 https://einblick.ai/try-einblick/报名试听
如果你犯了错误,不要惊慌。我掩护你
交互式 rebase 小指南。此命令允许您更改过去并撤消提交
Artem Maltsev 在 Unsplash 上拍摄的照片以及作者的注释
5 个月前,当我第一次开始为开源做贡献时,有些事情让我一直很紧张。
分支上的每一条提交消息都必须遵循一种格式,否则您的拉请求就不会被合并。
具体来说,应该是:“前缀:该提交内容的摘要”。
如果您已经处理一个拉请求两个月了,但是最后一个提交消息是错误的,会发生什么?
嗯…..
我决定我不想知道。
我习惯屏住呼吸,在用力前检查三遍。我甚至推迟了开分店,因为我不想冒这个险。
静下心来学习 git,并最终学习交互式 rebase,无疑是我这个阶段的 git 用户所能做的最好的投资之一。
好消息是:这不是火箭科学。看一看,然后告诉我你的想法。
先决条件:一些 Git
许多其他作者更好地解释了诸如克隆、添加、提交、推送等 Git 基础知识。我会充分利用您的时间,将它们的链接放在这里:
对于那些认为自己完全是初学者的人,我推荐这本带有动手项目的清晰指南。这是《走向数据科学》的副主编安妮·邦纳带给你的。
Git 如何工作
如果你喜欢一道菜。我强烈推荐这一个。
这门课程比这篇文章先进得多。它深入机制,从根本上解释事情。它也充满了动画和真实的,具体的例子。因为我很喜欢它,所以把它放在这里给有兴趣了解更多的人是很自然的。
专业书籍
这本书也是有帮助的。在最初的几次尝试中,我并没有理解中 Git 如何工作的大部分概念,但是读完这篇文章后,大部分概念都变得清晰了。
https://git-scm.com/book/en/v2
先决条件:打乱提交的方法
我最常犯的两个错误
- 错误的提交消息(例如,混淆了前缀)
- 错误的提交内容(例如,额外的行、额外的空间)
交互式 rebase 比修复这些问题更有能力,但是我主要关注这两个问题,因为我遇到它们的次数最多。
什么是 Rebase?
简单地说,它正在重新定义过去发生的事情的顺序。
所以当我们改变过去的时候,我们实际上在做的是首先让一件新的事情发生。
然后,我们用实际发生提交替换新事件。
之前和之后
然而,交互式 rebase 使事情变得非常简单。即使你不懂这部分,也不妨碍你获取这篇文章的核心外卖。
改变过去
第一步—选择您想要修改的历史部分
执行git log
并复制提交的散列。应该是下面例子中的5a6e6cdc63ecd1ad4c03ed004099d298b776e06a
% git logcommit 5a6e6cdc63ecd1ad4c03ed004099d298b776e06a (**HEAD -> branch-name**, **origin/branch-name**)Author: my-username <myemail@gmail.com>Date: Tue Sep 7 19:14:08 2021
第二步——复制数字,并开始重建基数
命令如下
git rebase -i 5a6e6cdc63ecd1ad4c03ed004099d298b776e06a^
请注意,它包括
- git rebase -i
- 哈希代码
- ^
简单地说,^意味着你想从这一点开始改变过去。它就像一个箭头,指向我们要开始的地方。
如果你愿意,像git rebase -i 5a6e6cdc^
这样的东西也可以工作,因为它们只会寻找以前缀开头的散列。
额外步骤——使用 vim 作为您的编辑器
如果前一步因为编辑器不工作而不起作用,请尝试这一步,然后再次执行第二步:
git config --global core.editor vim
这将允许您在终端窗口中进行交互式重置。我更喜欢这一点,并将在下面提供关于如何使用它的进一步说明。
第三步—选择要修改的提交
这是最复杂的一步。
您应该会看到这样的内容:
% git rebase -i 5a6e6^hint: Waiting for your editor to close the file...pick 5a6e6cdc63 The commit message here. Note by the author # Rebase 71b43c0db0..5a6e6cdc63 onto 71b43c0db0 (1 command)## Commands:# p, pick <commit> = use commit# r, reword <commit> = use commit, but edit the commit message# e, edit <commit> = use commit, but stop for amending# s, squash <commit> = use commit, but meld into previous commit# f, fixup <commit> = like "squash", but discard this commit's log message# x, exec <command> = run command (the rest of the line) using shell# b, break = stop here (continue rebase later with 'git rebase --continue')# d, drop <commit> = remove commit# l, label <label> = label current HEAD with a name# t, reset <label> = reset HEAD to a label# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]# . create a merge commit using the original merge commit's# . message (or the oneline, if no original merge commit was# . specified). Use -c <commit> to reword the commit message.
看看所有的命令!Git rebase -i 有这么多能力。然而现在,我只打算坚持使用唯一一个我经常使用的:edit
。
去把pick
改成edit
,然后保存并关闭文件。
如果你像我一样使用 vim 编辑器,这里有一些关键信息:
i
=开始编辑。向上/向下/向左/向右浏览各行。
backspace
删除“选择”文本。正常打字。
esc
=停止编辑
:wq
=保存w
并关闭q
enter
执行
第四步—编辑内容
您有想要更改的代码。做git status
自己看。就好像您还没有暂存那些文件一样。
立即进行更改。
如果您只想更改提交消息,只需跳过这一部分。
额外提示——如何通过编译来测试代码,然后在 rebase 期间进行更改
残酷的事实是,我们不能在 git rebase 期间编译和测试代码,但好消息是我有几乎一样好的东西。
如果您想要编辑的提交是最新的,应该可以:
在重定基础之前,按照你的意愿进行修改和编译。
然后做git stash
。这是一个有点像“cmd/ctrl + z”或者“cut”的命令。它带走所有的变化并储存起来。
稍后,当你重新设定基础并到达这个阶段时,做git stash pop
。这将把修改“cmd/ctrl + v”或“粘贴”到这里。瞧。你有零钱。
第五步——git 添加、提交、git 重置基础继续
这个提示信息应该出现在终端上—
You can amend the commit now, withgit commit --amendOnce you are satisfied with your changes, rungit rebase --continue
照它说的做,但首先git add
所有的变化。这是继续进行的必要条件。
然后git commit --amend
您的编辑器中应该会显示以下内容:
Commit message here. Note by the author# Please enter the commit message for your changes. Lines starting# with '#' will be ignored, and an empty message aborts the commit.## Date: Tue Sep 7 19:14:08 2021## interactive rebase in progress; onto 71b43c0db9# Last command done (1 command done):# edit 5a6e6cdc62 SCI: Set name of new file to tts, function name to ttsPickLB2NotebookTopic, and remove extra lines# No commands remaining.# You are currently editing a commit while rebasing branch 'sci-tts' on '71b43c0db9'.
编辑提交消息的最佳时间。您应该可以使用我上面提到的 vim 编辑器键。他们以同样的方式工作。
然后用这个包起来。
git rebase --continue
第六步——用力推
是时候展示你的作品了。
不做git push
,做:
git push --force
这是因为我们对历史进行了更改,这与远程(Github 上)存储的内容相冲突。
--force
表示“无论如何都要做”。我确信我有正确的版本”。
在这之后,你应该会在 Github 上看到你想要的改变。
如果出了什么差错…
git rebase --abort
这停止了时间旅行,带你回到正常状态。
我希望这篇文章对你有帮助
我真诚地希望你会好起来,不管你正面临什么样的问题,或者将来可能会面临什么样的问题。
与其希望你永远不会遇到任何问题,我会说…
持久的幸福来自于处理生活中糟糕事情的能力。
我希望你能处理好自己的问题。
中级会员资格使我有可能学到足够的知识,并为数据科学写作。用我的个人链接注册,然后告诉我,我会给你发一份分享我整个旅程的 pdf。
不要从神经网络开始学习数据科学
简要概述为什么不能开始用神经网络学习数据科学
我经常遇到这样的学生,他们以 Keras、Tensorflow 和一般来说的深度学习开始他们的数据科学之旅。他们疯狂地建立了大量的神经网络,但最终他们的模型失败了,因为他们不太了解机器学习,也不能够应用使神经网络工作所需的必要预处理技术。
这就是为什么,如果你的职业生涯是从数据科学家开始的,你不需要从深度学习开始。
不要从模型开始
数据科学是关于数据的,不是关于模型的。因此,专注于深度学习就像专注于模型一样,这是错误的。在使用任何模型之前,您必须关注适当的探索性数据分析和预处理。一个好的可视化可以使我们在使用任何神经网络之前达到好的结果,因为我们的目的是从数据中提取信息。所以,帮你自己一个忙,不要用神经网络或任何其他模型启动机器学习项目。
神经网络很难训练
神经网络有几个超参数,并且由于它们有数百个权重要优化,因此它们需要大型数据集和计算时间来给出好的结果。一般来说,你永远没有足够的数据,也没有那么多时间来训练一个模型,所以从神经网络开始可能是一个错误的想法。好主意是从一个简单的模型开始,如果必要的话增加复杂性,把神经网络留到最后一步。
有比神经网络更多的模型
一切都取决于具体情况,但如果你有表格数据,也许你可以找到比神经网络更强大的模型。例如,一个好的决策树可能是好的。训练可以更快,效果可以更好。有些情况下,神经网络比其他模型工作得更好,但是当我们必须处理表格数据时,我们可以使用一组模型来代替神经网络。从线性模型开始,然后转向 KNN 和贝叶斯模型,然后是决策树和 SVM。在这一点上,你有学习集合模型的知识,只有在最后,你才能专注于神经网络。
神经网络是黑盒
让我们面对现实吧。神经网络是无法解释的,解释我们的模型是机器学习中最复杂的任务之一。我已经写了一篇关于如何使用 SHAP 解释神经网络的文章,但是不使用其他技术,使用一个给我们自己解释特征重要性的模型通常更有用。决策树就是这样一种模型,这就是为什么它们被广泛使用的原因。神经网络本身无法解释清楚,这是一个很大的缺点。所以,如果我们使用神经网络,让我们记住,在我们训练它们之后,我们将不得不以某种困难的方式来解释它们。
神经网络什么时候有用?
当你掌握了数据预处理和机器学习,并且有一个非表格问题要解决时,神经网络就变得有用了。例如,使用 CNN 的图像分类或使用 LSTM 的时间序列分析。另一个用途是,例如,像语言翻译器中使用的序列到序列处理。在某些特殊情况下,如果要素和目标之间的相关性是非线性且非*凡的,前馈神经网络可能会帮助您处理表格数据集。如果其他模型失败,并且您有强有力的证据表明特征和目标之间存在相关性,您可以开始使用神经网络,从单个隐藏层开始,然后依次添加越来越多的层。如果你从神经网络开始,你就失去了拥有一个可以更快训练并给出有用结果的可解释模型的机会。
结论
神经网络本身是一种科学,但它们对预处理和数据集的形状非常敏感,有时它们不会给我们提供更简单模型的预期结果。当我们开始学习数据科学时,有必要从简单的模型开始,然后只有当我们掌握了其他技术,特别是预处理和超参数调整时,才能进入神经网络。在我们获得这些知识后,我们发现神经网络并不像人们说的那样经常需要,而是只在特定的任务中需要。
原载于 2021 年 5 月 24 日 https://www.yourdatateacher.com**的 。
不要对时间序列预测使用 K-fold 验证
如何用 python 中的 sktime 执行时态交叉验证
交叉验证是帮助为机器学习模型选择最佳超参数的有用程序。这对于较小的数据集特别有用,因为没有足够的数据来创建代表性的训练、验证和测试集。简而言之,交叉验证将单个训练数据集分成多个训练和测试数据集子集。
最简单的形式是k-折叠交叉验证,将训练集拆分成 k 个更小的集合,或者说折叠。对于每次分割,使用训练数据的 k-1 倍训练模型。然后根据剩余折叠验证模型。然后,对于每一次拆分,模型都在保留的折叠上进行评分。分数是通过拆分得到的*均值。
作者图片
然而,这种超参数调整方法并不适用于时间序列预测!
下图说明了为什么标准的 k -fold 验证(和其他非时间的数据分割)不适合时间序列机器学习。该图显示了分成五个窗口的单变量序列,并指出序列中的哪些日期被分配给哪个折叠。
作者图片
这些褶皱有三个突出的问题:
- 预测/测试数据出现在训练数据之前。在 0 号窗口中,预测范围(测试数据)出现在训练数据之前!
- 数据泄露。在 windows 2–4 中,一些训练数据出现在测试数据之后。这是有问题的,因为模型能够预见“未来”。
- 串联间隙。在 windows 2–4 中,由于测试数据是从系列的中间获取的,因此训练系列中会有间隙。
(有关交叉验证的更多背景信息,请参见 scikit-learn 文档。)
如何用 sktime 将数据分割成时态折叠
scikit-learn
提供了用类似model_selection.KFold
的类将数据分割成文件夹的方法。sktime
提供了相应的类,“窗口拆分器”,工作方式类似。
窗口拆分器有几个可以配置的参数:
window_length
—每次折叠的训练窗口长度fh
—预测范围;指定在训练窗口之后要包含在测试数据中的值。它可以是整数、整数列表或 sktimeForecastingHorizon
对象。initial_window
—第一个折叠中训练窗口的长度。如果未设置,window_length
用作第一次折叠的长度。step_length
—褶皱之间的步长。默认值为 1 步。
初始化后,窗口拆分器可以像KFold
验证类一样使用,为数据的每次拆分提供训练和测试索引:
滑动窗口拆分器
该拆分器随着时间的推移在滑动窗口上生成折叠。每个折叠的训练系列和测试系列的大小是恒定的。
在这个例子中,window_length=5,意味着训练窗口总是包含 5 个值。预测范围fh
是一个整数列表,指示训练窗口之后的哪些值应该在测试数据中。
来源:sk time/window _ splitters . ipynb
如果您将initial_window
设置为 10,并将step_length
更改为 3,则如下所示:
如下所示,第一个折叠的训练窗口是 10 个时间步长;后续折叠具有长度为 5 的训练窗口。此外,每次折叠都比前一次折叠滑动 3 步。
(代码模板由 sktime 提供,图片由作者提供)
展开 WindowSplitter
与SlidingWindowSplitter
一样,ExpandingWindowSplitter
随着时间的推移会在滑动窗口上生成褶皱。但是,训练系列的长度会随着时间的推移而增长,随后的每个折叠都会保留到该点为止的完整系列历史记录。每个折叠的测试系列长度是恒定的。
来源:sk time/window _ splitters . ipynb
预测模型选择
sktime
提供了两个使用交叉验证来搜索预测模型最佳参数的类:[ForecastingGridSearchCV](https://www.sktime.org/en/latest/api_reference/modules/auto_generated/sktime.forecasting.model_selection.ForecastingGridSearchCV.html#sktime.forecasting.model_selection.ForecastingGridSearchCV)
(评估所有可能的参数组合)和[ForecastingRandomizedSearchCV](https://www.sktime.org/en/latest/api_reference/modules/auto_generated/sktime.forecasting.model_selection.ForecastingRandomizedSearchCV.html#sktime.forecasting.model_selection.ForecastingRandomizedSearchCV)
(随机选择要评估的超参数)。这些类通过反复拟合和评估同一个模型来工作。
这两个类都类似于scikit-learn
中的交叉验证方法,遵循相似的接口。调谐器用以下实例化:
- 要调整的预测者
- 交叉验证构造器(如
SlidingWindowSplitter
) - 参数网格(例如
{'window_length':[1,2,3]}
) - 调谐参数
- 评估指标(可选)
在下面的示例中,跨时间滑动窗口使用了带交叉验证的网格搜索来为指数*滑预测选择最佳模型参数。参数网格指定应该测试模型参数sp
(季节周期数)和seasonal
(季节成分类型)的哪些值。
预测器适合历史数据的 60 个时间步长的初始窗口。后续窗口的长度为 20。预测范围设置为 1,这意味着测试窗口仅包含在训练窗口之后出现的单个值。
那么时间网格搜索交叉验证方法可以被拟合并用于进行预测:
拟合对象包含两个有用的属性:
gscv.best_params_
:调谐参数gscv.best_forecaster_
:具有最优超参数的最佳预测器的实例
有关使用 sktime 进行预测的更多详细信息,包括模型选择和调整,请参见此处的 sktime 预测教程:https://www . sk time . org/en/latest/examples/01 _ forecasting . html
结论
感谢您的阅读!在这篇文章中,我们介绍了交叉验证的基础知识,以及为什么需要对时间序列进行调整,如何将时间序列分割成多个折叠进行时间交叉验证,以及如何进行时间网格搜索和随机搜索交叉验证。
参考资料和资源
- https://sci kit-learn . org/stable/modules/cross _ validation . html
- sk time/window _ splitters . ipynb at main 艾伦图灵研究所/sktime GitHub
- 时间序列嵌套交叉验证| Courtney Cochrane |走向数据科学
当 Pathlib 可以使用 Python OS 库时,不要再使用它
更直观、更方便、更多功能
*年来,Python 被更多的非程序员所了解。这不仅是因为它在机器学习领域很受欢迎,还因为它可以用来自动化许多重复的工作,如批量编辑具有特定模式的文件。
在我以前的一篇文章(如下)中,我介绍了 Python 中的 OS 库,它将处理几乎所有基本的文件系统操作。对于那些刚刚开始使用 Python 来自动化一些重复任务的人,强烈推荐这些操作。
</8-must-know-file-system-operations-in-python-7dc185daeccd>
然而,在我们掌握了操作系统库之后,我还建议升级到另一个库来完成大多数基本的文件系统操作。那就是 Pathlib,也是 Python 内置库。它在语法方面更直观,更容易使用,并且有更多现成的功能。
在本文中,我将首先比较使用 OS 库和 Pathlib 的一些基本操作,以展示它们的区别,并讨论为什么推荐使用 Pathlib。
1.显示当前目录
我想开始的第一个操作是显示当前的工作目录(CWD)。这很重要,因为我们可能大部分时间都想使用相对路径。因此,有时知道我们此刻在哪里是很重要的。
操作系统库
import osos.getcwd()
Pathlib
from pathlib import PathPath.cwd()
差异
如果我们把它们打印出来,结果似乎是一样的。
然而,如果我们检查对象的类型,我们会发现它们是不同的。
OS 库将返回一个字符串,而 Pathlib 将返回一个 PosixPath 对象。返回 PosixPath 的好处是我们可以直接使用返回的对象做更多的操作。这将在后面的章节中演示。
2.检查目录或文件是否存在
就我而言,我使用的是 Google Colab,每次配置新笔记本时,都会自动创建一个名为“sample_data”的文件夹。如果我想检查是否有这样的目录,下面的代码就可以了。
操作系统库
函数os.path.exists()
接受一个字符串类型的参数,它可以是目录名或文件名。
os.path.exists('sample_data/README.md')
Pathlib
当使用 pathlib 时,我们只需将路径作为字符串传递给“Path”类,这将为我们提供一个“PosixPath”对象。要测试“PosixPath”实例是否存在于文件系统中,只需调用它的方法exist()
,比 OS 库更直观。
Path('sample_data/README.md').exists()
差异
在这个操作方面,两个库几乎没有区别。然而,当使用 Pathlib 时,我们有可能以这种风格编写代码。
readme = Path('sample_data/README.md')
readme.exists()
虽然我们使用多一行代码实现了这个操作,但是我们已经获得了对readme.md
文件的引用。换句话说,我们可以稍后将变量readme
用于任何其他操作,而不必再次将完整路径作为字符串传递。
3.创建目录
现在,让我们在工作目录中创建一个名为test_dir
的目录,看看 OS 库和 Pathlib 有什么区别。
操作系统库
在操作系统库中创建一个目录是相当容易的。
os.mkdir('test_dir')
Pathlib
对于 Pathlib,语法非常直观。
Path('test_dir').mkdir()
抑制 FileExistsError 的区别
在操作系统库中,当我们试图创建一个已经存在的目录时,将会抛出一个错误。
在我以前的文章中,有人建议我们应该在创建目录之前检查它是否存在。
if not os.path.exists('test_dir'):
os.mkdir('test_dir')
然而,如果我们使用 Pathlib,处理这个错误就容易多了。
Path('test_dir').mkdir(exist_ok=True)
函数mkdir()
接受一个标志exist_ok
。当它被设置为 true 时,FileExistsError
错误被自动忽略,这相当于我们通过添加一个 if-condition 对 OS 版本实现所做的。
创建多级深度目录的区别
另一个主要区别是在父目录不存在时创建目录。在操作系统库中,我们必须使用不同的函数来实现这一点。我们必须使用makedirs()
,而不是mkdir()
。
os.makedirs(os.path.join('test_dir', 'level_1a', 'level_2a', 'level_3a'))
它确实起作用了。然而,我们必须永远记住使用不同的功能。
如果我们使用 Pathlib,我们只需要将标志parents
设置为 true。
Path(os.path.join('test_dir', 'level_1b', 'level_2b', 'level_3b')).mkdir(parents=True)
不要忘记这些都是同一个函数的标志。换句话说,我们可以同时使用exist_ok
和parents
标志!
4.显示目录内容
操作系统库
当我们想显示一个目录的内容时,在 OS 库中很容易。
os.listdir('sample_data')
Pathlib
在 Pathlib 中显示目录内容的语法并不令人惊讶,如下所示。
Path('sample_data').iterdir()
返回格式的差异
如果我们注意 Pathlib 的返回格式,它实际上提供了一个生成器,而不是一个字符串列表。我们可以通过将这个生成器中的所有对象放入一个列表中来获取所有内容。
list(Path('sample_data').iterdir())
然而,在大多数情况下,我们想要获得一个目录中的所有文件的原因是为了一个接一个地执行一些动作。因此,如果我们只想让它们循环一次,生成器会更有效。
使用 Glob 的区别
操作系统库不提供使用通配符搜索文件的功能。因此,我们必须导入另一个名为 Glob 的库来帮助。
from glob import glob
list(glob(os.path.join('sample_data', '*.csv')))
如果我们使用 Pathlib,非常幸运的是,它带有“glob”特性。
list(Path('sample_data').glob('*.csv'))
5.快速读取/写入文件
这个特性是 Pathlib 独有的。当我们想在文件中读或写一些东西时,通常使用下面的方法。
with open("test.txt", 'rw') as file:
print(file.read())
这确实是 Python 中的标准。然而,有时我们可能只想在一个文件中写入很少的字节。在这种情况下,我们可以使用 Pathlib 非常容易地做到这一点。
f = Path('test_dir/test.txt'))
f.write_text('This is a sentence.')
当然,我们也可以快速地将文件的内容读入一个字符串变量。
f.read_text()
6.文件的元数据
在实践中,我们需要一些关于文件的特定信息是很常见的。现在,让我演示一下 Pathlib 如何为我们轻松地提取关于文件的信息和统计数据。
让我们继续使用我们在上一节中使用过的文件,它是变量f
。
print(f'Path of the file is:\n{f}')
尽管在大多数情况下我们希望使用相对路径,但有时仍然有必要检查文件的绝对路径。我们可以使用 Pathlib 非常容易地做到这一点。
f.resolve()
有时,我们可能只想通过去掉文件扩展名来获得文件名。或者,相反,我们希望提取文件的扩展名。通过访问文件的属性,这两种方法都很容易。
f.stem
f.suffix
更令人吃惊的是,Pathlib 可以从一个 PosixPath 实例轻松地返回统计数据、创建/更新时间等等。这相当于os.stat_result
,但是更容易访问和使用。
f.stat()
例如,如果我们想显示文件的大小,只需如下。
f.stat().st_size
摘要
在本文中,我介绍了另一个 Python 内置库 Pathlib。它被认为比操作系统库更先进、更方便,并提供了更多令人惊叹的功能。
当然,我们仍然需要知道如何使用 OS 库,因为它是 Python 中最强大和最基本的库之一。然而,当我们在典型场景中需要一些文件系统操作时,强烈建议使用 Pathlib。
https://medium.com/@qiuyujx/membership
如果你觉得我的文章有帮助,请考虑加入 Medium 会员来支持我和成千上万的其他作者!(点击上面的链接)
不要浪费时间构建您的数据科学网络
意见
专注于重要的事情
对于经常阅读我文章的人来说,你可能会认为我是这个星球上最矛盾的人。我不怪你。
我最*注意到,在与人的口头或书面交流中,我对与我交流的人做了许多假设。我不会明确地定义非常重要的方面,然后才确切地说出我要说的内容,这取决于上下文,有可能冲淡我希望传达的实际信息。
最*,我写了一篇关于我们如何通过网络简化进入数据科学的初始阶段的文章。作为对这篇文章的回应,我在 LinkedIn 上收到了许多联系请求,声称他们希望与我联系,这样他们就可以建立自己的职业网络。
不要误解我,我对和我联系的人没有意见。事实上,我在每篇文章的结尾都鼓励它。我非常感激我处在一个人们希望与我联系并跟随我踏上数据科学之旅的位置。与大家分享我的经历的目的是,我希望从我的角度表达作为一名数据科学家或自由职业者的现实,因为我们都努力成为不可或缺的人。
坦率地说…
联网是浪费时间!
哎呀,我又做了一次。让我解释一下…我们所知道的,或者我们被告知要去做的联网方式已经完全死亡了。
事实上,我还没能想出一个足够好的名字来描述什么是真正的网络,所以我仍然把称为网络。
建立数据科学网络很可能会浪费时间。这是它的样子。
- 随意与人联系,因为他们的工作描述是“数据科学家”
- 纯粹为了个人利益— 即一份工作
- 为未来的机会而联系— 例如,我正在学习数据科学,但也许将来有一天我们可以一起做一个项目。
在我看来,这种形式的网络不是网络。这完全是一个哑弹。浪费时间。
真正的网络
以下是我对网络的看法…
如果你正在创造一些有趣的东西,那么总会有人想要了解你。
简单明了。
为什么有人想见埃隆·马斯克?如果你说因为他是亿万富翁,你又一次掉进了陷阱。是心态不对。如果你在想他的亿万身家,很可能你在想这对你有什么好处。那是接受者的心态。
人们希望见到埃隆·马斯克,因为他制造人们想要的东西。他是一个解决问题的人,我们需要他!
如果你因为上面列出的三个原因中的任何一个而试图建立一个网络,你绝对是在浪费你的时间,这些时间本可以用来建立人们想要的有趣的东西,从而让人们想要认识你。
如果你看一看 LinkedIn 上讨论数据科学的受欢迎的人,你就会明白我在说什么;
- Vin Vashista —围绕数据科学中应采取的最佳实践建立思维模式。这包括从生产中的机器学习到招聘的一切。
- harp reet sa hota——建立一个社区,让所有级别的数据科学家都能安全地分享经验和提出问题。他还建立了一个播客,在那里他采访了一些数据科学领域最有趣的人,以及与该领域毫无关系的人。
- Kate Strachnyi —建立一个数据故事讲述者的社区。这包括利用数据教育人们如何成为更好的故事讲述者。
这份清单还在继续…
我知道当你阅读我与你分享的名单时你在想什么
这些人都是在外地多年的 。
这是非常正确的…每一个建筑都必须从某个地方开始。
**
合适的人会找到你
本质上,当我提到网络时,我想说的是你应该专注于创造人们想要的东西。做一个创造者。这样,合适的人就会来找你。
从我的观察来看,与我有联系的人最令人难忘的是那些出于以下原因与我有联系的人;
- 他们希望跟上我分享的内容
- 他们对我创作的内容提出了一个经过深入研究的问题
- 他们有一个他们认为对我们双方都有价值的机会
这并不是说我没有人和我联系,问我工作或者其他不在这个清单上的事情,我有。但是,这些人很快就消失了。
在我看来,如果你发现自己试图在网上与这三个原因之外的人联系,那么我建议你在行动中抓住自己。无论你做什么或说什么,都可能让人绝望,并可能在关系开始前就危及它。
如果你真的想建立一个强大的关系网,在你决定开始创造的时候,合适的人会找到你。
要开始创作,你不需要像我一样从博客开始;你有很多不同的方法来创造人们想要的东西。
- 项目
- 摘要
- 个案研究
- vlog/博客
- 分享高排名竞争解决方案
- 将研究论文转换为代码
这些想法不一定要从细微处开始。重要的是,你开始创造并分享你正在创造的东西——最终,合适的人会来找你。
最后的想法
成为一名创作者并在网上分享你正在做的事情起初可能看起来是一项艰巨的任务,尤其是如果你像我一样天生内向的话——克服这个问题要从分享一件事情开始,然后永远不要停止。如果你希望发展一个强大而健康的网络,那么重要的是你要站在创造者的立场上——从那时起,一切都会向你走来。
在 LinkedIn 和 T2 Twitter 上与我保持联系,了解我关于数据科学、人工智能和自由职业的最新消息。
相关文章
https://medium.com/analytics-vidhya/a-strategic-approach-to-becoming-a-data-scientist-2e0f1bf65ebf https://medium.datadriveninvestor.com/5-ways-to-attract-freelance-data-science-clients-to-you-b79fdb76eab **
用于数据分析的环形图
在这个故事中,我们演示了如何使用 python 工具从复杂的 excel 表格中绘制圆环图。
在 Unsplash 上由 Rodion Kutsaev 拍照
像往常一样,我们的工作从数据开始,就像有些人的早餐由甜甜圈组成。食物和数据之间没有关系,除了甜甜圈图有一个甜甜圈形状的图形。
首先我们有一个 excel 文件,记录了一个行业部门从 2018 年到 2020 年的所有销售信息。该部门成立于 2018 年,在中国刚刚经历了新冠肺炎年。幸运的是,它幸存了下来,并庆祝 2021 年新年的开始。
原始数据的解释
现在我们可以回顾一下这个部门在过去三年里发生的事情。excel 由 3 张表组成,包含每日销售额(E 列)和商品总重量(F 列)。收货人(C 列)实际上是付款的公司。该产品有 3 种类型(D 列),取决于其成分,FDY 和 DTY。一般来说,这些原材料有 3 种组合,即 FDY-FDY,DTY-DTY,以及混合 FDY-DTY。FAW(B 列)决定了产品的厚度,因为成品是坯布,是纺织工业的基本产品。
作者图片:excel 文件的截图
有熊猫和 Matplotlib 的圆环图
**import pandas as pd**
df2018=pd.read_excel("outbound_with_company.xlsx",sheet_name='2018',header=0)
df2019=pd.read_excel("outbound_with_company.xlsx",sheet_name='2019',header=0)
df2020=pd.read_excel("outbound_with_company.xlsx",sheet_name='2020',header=0)
我们将每个 excel 表格写入一个数据框。数据框具有与初始数据相同的列。我们可以在头脑中重组数据。如果我们想探究订单和客户之间的关系,换句话说,C 列和 E 列(或 F 列)之间的数字比例,我们可以通过 groupby 操作对数据帧进行重组,该操作使用函数 pandas。data frame . group by。
代码示例:
group_2018_2 = df2018.groupby('company')
print(group_2018_2.size())
作者图片:代码输出
**import** **matplotlib.pyplot as plt**
**from** **matplotlib** **import** **cm**
fig, ax = plt.subplots(figsize=(6, 6), subplot_kw=dict(aspect="equal"))
cs_customer= cm.get_cmap('viridis')(np.linspace(0, 1, 5))
component = group_2018_company.index
data = group_2018_company['weight']
wedges, texts = ax.pie(data, wedgeprops=dict(width=0.3), startangle=90,colors=cs_customer)
plt.legend(wedges, component, loc="center",fontsize=12)
ax.set_title("customers in 2018",fontdict={'fontsize': 16, 'fontweight': 'bold'})
fig.tight_layout()
plt.savefig('donutplot2.png',dpi=100, format='png', bbox_inches='tight')
plt.show()
作者图片:plt.show()的输出,圆环图
从甜甜圈图中,我们可以清楚地看到每个客户对销售额的贡献。ID 为 003 的客户在 2018 年做出了最大贡献。
同样,我们对其他组的数据和计算操作进行分组,如“type”、“FAW”。因此,我们在 2018 年获得了三个环形地块。
作者图片
这份电子表格记录了这个部门 3 年的销售额。这意味着我们可以得到 9 个甜甜圈🍩图表。
在下一个版本中,我们将解释如何用 Matplotlib 通过 DIY 设计来构建一个漂亮的年报。
报告的一页如下所示:
作者图片:用 Matplotlib 构建的年度报告
所有代码和文件(png 和 excel)已经在 Github 提交。
故事评论
到目前为止,我已经基于同一个 excel 文件编写了一系列故事,为此我已经发布了其他相关的故事,这些故事可以通过真实的数据和案例帮助您理解数据和数据分析。
测试新数据科学家候选人的注意事项
JESHOOTS.COM在 Unsplash 上拍照
测试数据科学家职位的候选人可以让招聘组织很好地了解他们在完成工作相关任务和有效管理时间方面的表现。数据科学家成功所需的技能因公司甚至公司内的团队而异,因此测试候选人应该量身定制。不过,总的来说,数据科学是一个包括许多步骤和独立技能的过程,这些步骤和技能的总和大于其各个部分的总和。虽然很难测试,但在本文中,我将分享我认为在测试新的数据科学家候选人时必须遵循的准则。
从技术上来说,测试可以在面试过程的任何一个环节进行,甚至在面试开始之前,但是只有在你想给应聘者一个不为你工作的理由的时候。对我来说,有两个时间可以对数据科学家候选人进行测试:
- 在与人力资源部或正在招聘数据科学家的公司的其他人进行初步筛选面试后,或者
- 在招聘数据科学家的部门的面试过程中
在最初的筛选之后,你可以用简短的重点测试来测试基本技能。如果这份工作需要 Python 和 SQL 技能,那么要求候选人进行基本数据操作或写下他们将如何使用 WHERE 子句查询数据集的测试是合适的。这里的重点是过滤掉那些显然不能在团队中独立工作的人。
当候选人已经与团队中的某人交谈过,并且他们看起来适合该角色时,现在是对他们的数据科学能力进行深入测试的好时机。此时,您对该候选人在团队中的实际表现的期望最高,因此您希望他们向您介绍他们的数据科学方法。活下去。我建议在这一点上为候选人创建一个测试,既测试他们履行职责的能力,又测试他们的创新能力。
请记住,背诵哪些超参数可用于训练模型或回答概率论问题并不总是直接转化为创新的问题解决方案或成为团队成员。确保您测试您的组织对数据科学家的重视程度。
使用预先构建的数据科学技能测试
无论你想测试什么样的数据科学技能,都可能有一个已经设计好的测试。如果你在人力资源部门工作,或者没有时间或经验来设计自己的测试,在网上搜索“数据科学候选人测试”,会看到十几个提供这些测试的公司网站。
我作为候选人参加过这些类型的测试,我觉得这些测试对于筛选大量候选人非常有用。(我曾经做过一个测试,主要是问如何在熊猫数据框架中选择一列)。这是你在第一轮面试前给候选人的,但一定要遵循文章结尾的注意事项。
尽管你可以外包测试的实际管理和结果分析,但在参与之前,还是有一些问题要问你自己:
- 应该考哪些技能?
- 我希望考生在这次考试上花多长时间?
显然,测试的结果应该告诉你谁适合你正在招聘的工作,而不是其他工作。另外,尊重考生的时间,不要期望他们为你的考试放弃一整天。当然,除非他们在很短的候选人名单上;一把左右。
一般来说:测试的长度和参与程度应该与你对他们作为工作候选人的认真程度直接相关。
创建您自己的测试
这条通道是为优秀候选人保留的,因为你没有时间管理或审查所有候选人的测试作为筛选机制,而是依靠简历或基本测试来实现这一目的。我个人认为这种方式更有吸引力,因为你可以发现候选人在你的团队中会有怎样的表现。你可以围绕他们将如何解决角色中的实际问题或一些假设的问题来设计问题。
以下是在创建测试时要问自己的问题:
应该考什么技能?
注意:当我看到“我们”时,我指的是新的数据科学家将工作的团队。即使只有一个人在做测试也是如此。
一个全面的技能列表需要包括基本技能、高级技能和可以扩展团队视野的技能。我建议把这些写在一个专栏里,突出基本技能,因为这些技能可以在面试过程的早期进行测试。这里有一个说明性的例子(添加和删除任何你想要的,不要在评论中太咸):
技能(基本):
- Python:数据帧操作
- python:sci kit-学习模型拟合和预测
- SQL:联接
- 缺失值插补
技能(高级):
- Python: Keras 还是 Tensorflow
- Python:具有统计模型的时间序列
- SQL:存储过程
- 套索拟合解释
技能(最好具备):
- 自然语言处理专家
- 码头工人专家
创建自己的列表应该很容易。我的建议是通过一个半小时的会议来寻求团队的帮助。随便问问。获得最多投票的技能是最重要的,将会议要求框定为定义什么技能是基本的、高级的和对该角色来说是最好的。
我们应该使用什么数据?
作为一名数据科学家,一部分工作是轻松处理数据并从数据中做出推论。在我看来,你的测试必须有数据才是可行的。给你的候选人一些可以接触到的东西(打个比方),用代码或任何你想让他们使用的工具来操作。
对于您提供的数据,以下是一些准则:
这些数据与他们将要从事的行业相关。如果工作是金融科技,给出金融时序数据。如果是高等教育,给他们匿名的人口统计数据。
这些数据不是机密或敏感的。这可能是显而易见的,但不要通过电子邮件向非员工候选人透露敏感数据。当有疑问时,使用公开可用的数据来创建一个测试,或者创建你自己的测试。
把数据弄得有点脏。这非常适合测试候选人如何处理脏数据。测试中的这一步经常被忽视。
测试需要多长时间?
你给自己多长时间来参加这个测试?这就是你的答案。如果你给 20 个任务分配基本前提,那么你必须给更多的时间。根据我进行和构建的测试,以下是一些指导原则:
- 带一些指导性问题的开放式数据集分析(3 小时)
- 数据分析和可视化的业务问题(8 小时)
- 机器学习问题,包括数据清理/转换(24-48 小时)
我们如何分析测试结果?
问问你自己:如果这是一个月的结束,或者一个 sprint 的结束,这个提交的工作会达到、超过或者低于团队的期望吗?在我看来,这是评估结果最自然的方式。基本上,你要检查候选人是否能够处理简单的任务,在高级任务上做得很好,并给出一点点迹象表明他们将在至少一个方向上扩展团队的视野。也许他们在时间序列分析方面比你更好,你是一个优秀的沟通者,实际上是在代码中添加注释或其他突出的东西。
测试新数据科学家候选人时的注意事项
做给反馈。不管怎样。
没有什么比三个月没有后续工作更糟糕的了,然后你收到一封机器人邮件,说这个职位已经有人了。每个阅读这篇文章的人都知道我指的是什么,仅仅因为这个原因,你需要避免这样做。求职者,尤其是数据科学家,明白市场的竞争性,但他们也记得那些把自己的时间视为宝贵的人。
对与工作相关的技能进行测试。
除了职位描述和面试过程,候选人还需要一份真实的工作形象。当你真正关注数据清理和自动化报告时,测试 docker 容器和最新的图像识别神经网络架构将确保人员流动。当期望与现实不同时,数据科学家总是离开公司。不要让你的组织看起来像装腔作势的人。
对你现在的员工进行测试。
说真的,看看你现在的员工在测试中表现如何。这是一个很好的晴雨表,显示了你对候选人的期望。这也是一个很好的现实检验,因为你期望你的新数据科学家将成为所有数据相关领域的专家,而你的团队成员都不是。
不要在与人直接接触之前进行测试。
如果你收到申请后的反应是发一封电子邮件,提醒他们参加测试,那么你不妨只说:“感谢你的兴趣,尽管你对我们来说只是一个数字,但在我们对你没有真正兴趣,也没有任何进一步有意义接触的迹象时,请从你的时间表中抽出时间来做这件事。”你需要提供一些东西,即使是在电子邮件中,显示你对他们作为候选人的兴趣,证明他们花时间参加任何测试是合理的。在我看来,如果他们没有和负责招聘的部门的人谈过,那么他们就不应该参加任何考试。
不要将长时间的测试作为筛选过程的一部分。
当我说“筛选过程”时,我指的是在进入第一或第二阶段面试之前筛选出候选人的过程。如果你想筛选出不能完成基本任务的人,那么一个简短的测试是合适的。我们最多谈 30 分钟。几个小时的测试只是为了在你需要的时候把信息存档,这是一种不好的做法,是对行业集体时间的消耗。
不要进行那些要求你猜测一系列图形中的下一个序列的“智力测试”。(你知道我在说什么)。
说真的,什么&%$这是?我作为一名数据科学家已经工作了多年,除非是在困扰候选人的背景下,否则这个问题永远不会出现。无论如何,你都不会得到测试结果的反馈,而且公司也不需要以此为由不给你打电话面试,所以何必呢?
“谢谢你的兴趣,但你在一系列形状中猜测下一个图像的能力本身就是我们考虑其他候选人的理由。”—没人
结论
测试是确定数据科学家候选人是否适合您的团队的重要工具,但如果您不尊重候选人的时间并设置不必要的障碍,它也会玷污您组织的声誉。通过在其他数据科学家的帮助下写下工作需要什么技能,你可以很容易地确保你管理的测试与你招聘的工作相符。遵循本文中概述的注意事项可以很好地概括您的组织如何对新的数据科学家候选人进行测试。
浓缩咖啡的剂量设置
咖啡数据科学
探索一些剂量设置
浓缩咖啡是迄今为止制作咖啡最丰富的方法,但也是最困难的方法。每一个镜头都有挑战,因为多个变量汇集在一起形成了一个美丽的东西。研磨设置、夯实压力、水温、压力、预注入、剂量、篮子、研磨机和机器都起作用。对于初学者来说,最具挑战性的障碍是能够理解他们应该根据其他镜头的运行方式做出什么调整。
我一直在看我为我的浓缩咖啡拍摄做失败分析的所有方法,我认为看几个变量会很有趣。在开始,我想分别看看这些变量,并展示我所看到的,希望它能帮助其他人更好地看到他们的投篮输出,并理解从那里去。
在这篇文章中,我看看剂量,特别是 18g,19g 和 20g。对于每一个,我把地面放在一个篮子里,用牙签分发,并用自动校*机夯实。我把中心做得不那么密集,以对抗杠杆机器固有的噪音。我也用同样的篮子和同样的豆子拍了三张照片。
18g、19g、20g,所有图片均由作者提供
我为每个镜头都拍了视频。每一行都是 5 秒的时间。这些列是 18 克、19 克和 21 克的剂量。
18g……..……..19 克 20 克
这是流量日志。
它们都有相似的流动剖面,但它们并不完全一致。因此,让我们将 18g 和 19g 向后移动一两秒钟,使流动中的拐点对齐。
从这里开始,18g 真的很快达到峰值,18g 和 19g 的最大流量高于 20g。
我们可以看看圆盘的底部,看起来 18g 的边缘有更多的黑点,但它们看起来相对相似。
18g,19g,20g
绩效指标
我使用两个指标来评估技术之间的差异:最终得分和咖啡萃取。
最终得分 是记分卡 7 个指标(尖锐、浓郁、糖浆、甜味、酸味、苦味和余味)的*均值。当然,这些分数是主观的,但它们符合我的口味,帮助我提高了我的拍摄水*。分数有一些变化。我的目标是保持每个指标的一致性,但有时粒度很难确定。
使用折射仪测量总溶解固体量(TDS),这个数字结合咖啡的输出重量和输入重量用于确定提取到杯中的咖啡的百分比,称为提取率(EY)** 。**
品味和 EY
从口味和提取方面来说,19g 是最好的,EY 也达到了顶峰。
****
正如预期的那样,20g 覆盖过滤器(TCF)的时间最长,但总时间保持不变。
剂量设置的关键是尝试一些设置。耐心会有回报的,因为你已经习惯了你的机器、研磨机和咖啡豆。
如果你愿意,可以在 Twitter 和 YouTube 上关注我,我会在那里发布不同机器上的浓缩咖啡视频和浓缩咖啡相关的东西。你也可以在 LinkedIn 上找到我。也可以关注我中。
我的进一步阅读:
点文件可能是你电脑上最重要的文件。你支持他们吗?
Dotties 是一个 Bash 脚本,它使用 Gist 管理你的点文件
介绍
点文件是类 Unix 环境中隐藏的纯文本配置文件。它们包含用户首选项和应用程序配置。您的编辑器、终端、Python 环境、Git 和其他日常使用的工具很可能会创建点文件。这些文件可能是你电脑上最重要的文件。
由于文件名中有一个点,所以它们被称为点文件。当您需要恢复配置时,保存这些点文件可以让您高枕无忧。您可以在几分钟内使用点文件建立一个新系统。
Dotties 是一个简单的点文件管理脚本和 GitHub CLI Gist 函数的包装器。它从终端管理主目录中的点文件。
要求
Dotties 需要 Github CLI 来管理你的点文件要点。
装置
使用棒极了的软件包管理器,安装 Dotties 很容易。
牛逼的包经理
curl -s [https://raw.githubusercontent.com/shinokada/awesome/main/install](https://raw.githubusercontent.com/shinokada/awesome/main/install) | bash -s install
然后安装 Dotties:
awesome install shinokada/dotties
家酿/Linuxbrew
brew tap shinokada/dotties
brew install dotties
Linux/Debian/Ubuntu
从发布页面下载最新的 deb 文件。
sudo apt install dotties_XXXXXXX.deb
用最新版本替换XXXXXXX
。
如何使用 Dotties
运行dotties
将创建一个新的~/.dotties/dottiesfile
文件。dottiesfile
包含您主目录中的所有点文件。
dotties
如果你还没有设置你的 GitHub CLI 编辑器,它会要求你选择一个。
如果你已经有了一个dottiesfile
,它会询问你是否要创建一个新的dotties
文件或者编辑它。第一次选择n
或new
:
编辑 dotties 文件
再次运行dotties
,但这次使用-e
或--edit
标志。这允许您编辑dotties
文件:
dotties -e
# or
dotties --edit
您的 GitHub 编辑器将打开dotties
文件。
随意编辑文件并保存。
查看 dotties 内容
如果您只想查看dotties
文件的内容,使用-r
或--read
标志来读取:
dotties -r
# or
dotties --read
查看本地点文件
使用-l
或--local
标志查看主目录中的所有你的点文件。
dotties -l
# or
dotties --local
创造一个要点
让我们使用-g
或--gist
标志将您的dotfiles
内容保存到一个要点中。所有与要点相关的标志都用大写字母表示。
dotties -G
# or
dotties --Gist
它将创建一个秘密要点,并打开一个浏览器窗口显示您上传的要点。当您运行dotties -G
时,它将创建~/.dotties/gisturl
来存储 Gist URL。
编辑要点文件
您可以使用-E
或--Edit
标志编辑点文件要点:
dotties -E
# or
dotties --Edit
当您选择一个文件时,您的 GitHub CLI 编辑器将打开该文件。
查看要点文件
您可以使用-V
或--View
标志查看点文件要点的内容:
dotties -V
# or
dotties --View
克隆要点文件
稍后,您可能希望克隆所有的点文件。
使用-C
或--clone
标志从一个要点克隆所有点文件:
dotties -C
# or
dotties --Clone
删除要点文件
您可以使用-D
或--delete
标志删除点文件要点:
dotties -D
# or
dotties --Delete
其他选项
打印帮助。
dotties -h
# or
dotties --help
打印版本。
dotties -v
# or
dotties --version
结论
要了解更多关于点文件的信息,请访问 GitHub repo。互联网上有许多管理点文件的方法。找到最适合自己情况和技能的方式,开始行动。
请让我知道你如何管理你的网络文件。如果你想增加更多的功能,请写在评论区。
通过 成为 会员,获得媒体上所有故事的访问权限。
https://blog.codewithshin.com/subscribe
参考
因果推理的双机器学习
思想与理论, BCN 因果 ALGO
因果推理的双机器学习如何工作,从理论基础到应用实例。
这篇文章是与阿莱克斯·鲁伊斯·德·维拉、耶稣·塞奎兹以及整个因果关系 ALGO·BCN团队共同努力的成果。注意:我们假设读者熟悉因果推理的基本概念。
如果你在因果推理的水域潜水,你可能听说过双机器学习的概念。如果你还没有听说过,我个人打赌你可能会,在不久的将来。或者更好的是,你可能甚至不知道你正在使用它。正如任何伟大的技术一样,用于因果推理的双机器学习有可能变得非常普遍。但是让我们让这位作家的热情*静下来,回到我们的任务上来。这篇文章试图简单而全面地解释什么是双机器学习以及它是如何工作的。为此,我们将从理论基础到因果推理应用的典型例子来讨论这个主题。
介绍
那么什么是双机器学习呢?Chernozukov 等人在一系列论文(本、本和本)中介绍并发展了这一思想,总体上旨在提供以下内容:
- 使用机器学习技术估计因果效应的一般框架
- 这些估计的置信区间
- “根 n 一致”的估计量,即在收敛性和数据效率方面具有良好特性的估计量。
这整个想法从何而来?一方面,显而易见,它来自于使用机器学习进行因果推理的想法。但是,如果我们看得更深入一点,就会发现两个不那么明显的关键想法:
- 从统计学的角度来看,机器学习是非参数或半参数估计方法的集合,
- 有大量关于非参数和半参数估计方法的理论工作(关于界限、效率等)。)
双机器学习将这两点联系起来,从第二点中获取灵感和有用的结果,用于与第一点进行因果推理。
背景
让我们开始吧。我们首先定义数据生成流程的 DAG,我们将在该流程下工作,如下图所示:
数据生成过程的 DAG。作者配图
此外,我们定义了以下控制 DAG 变量之间关系的部分线性模型:
部分线性模型
其中 Y 是结果变量,D 是二元处理,Z 是协变量的向量,U 和 V 是扰动。等式 1.1 是主等式,θ₀是我们想要估计的目标参数,它是 ate 相对于 d 的导数。等式 1.2 跟踪混杂因素,即治疗对协变量的依赖性。这种依赖性是通过 m₀(Z 函数建模的,而结果对协变量的依赖性是通过 g₀(Z).函数建模的我们将在后面看到,双机器学习也适用于完全非线性的模型,但我们开始假设这种部分线性的模型,以使该方法的解释和说明更容易。还要注意的是,我们假设了因果推理中可识别性的所有典型条件,即没有隐藏的混杂因素、阳性和一致性。
然后,回顾并完成引言中给出的双机器学习的定义,该方法的目标是在存在(潜在的高维)有害参数η₀=(g₀、m₀).的情况下,获得感兴趣的(低维)参数θ₀的根 n 一致估计量和置信区间在这种情况下,滋扰意味着我们不直接关心我们的η₀估计的正确性,只要我们得到一个好的(根 n 一致的)θ₀.估计
但是为什么我们首先对使用机器学习来完成这项任务感兴趣呢?主要有三个原因。首先也是最显而易见的是,机器学习方法在建模功能和/或预期方面的能力。第二,因为在处理高维数据时,机器学习模型比传统的统计方法(即线性回归和 OLS)更擅长预测,这在我们的世界中正在成为规范。最后也是最重要的,因为与传统的统计方法相比,机器学习方法没有对 m₀(Z(尤其是 g₀(Z)的函数形式强加如此强的假设,而是从数据中学习那些形式。这是一个很好的防止模型设定错误的措施,在我们的例子中,即使没有不可测量的混杂因素,这也是一个会导致有偏估计的问题。
天真的估计者
那么,为什么不简单地用机器学习来直接估计θ₀呢?例如,我们可以使用交替迭代方法,用随机森林估计 g₀,然后用 OLS 估计θ₀,重复直到收敛(注意,在这种情况下,对θ₀使用 OLS 并不意味着模型设定错误,因为模型在θ₀).是线性的嗯,生活没那么容易。下图显示了这种方法的θ₀-θ分布,并与*均值为 0 的正态分布进行了比较。从两个分布的差异可以看出,估计量是有偏的。请注意,g₀已被设置为具有很少参数的*滑函数,原则上应该由随机森林很好地*似。
θ₀-θ分布,朴素估计量。原文此处。
理解这一现象的关键观察是 g₀(Z)≠𝔼[Y|Z].因此,通常不可能通过对 z 上的 y 进行“回归”来获得对 g₀(Z 的良好估计,这又导致不可能获得对θ₀.的良好估计然而,给定 Z 和 d,完全有可能获得 Y 的非常好的预测。这就是为什么我们通常说机器学习对预测有好处,但对因果推理不利。偏差有两个来源,正则化和过拟合。双机器学习旨在纠正两者:通过正交化的方式纠正正则化偏差,以及通过交叉拟合的方式纠正过拟合偏差。接下来的部分将解释这两种偏差校正策略是如何工作的。关于偏差来源和形状的详细解释,请参见作者的原始工作或本次演讲。
正交化和奈曼正交条件
为了说明正交化是如何工作的,我们首先提到并简要解释弗里希-沃-洛弗尔定理。该定理指出,给定线性模型 Y=β₀+β₁D+β₂Z+U,估计β₁的以下两种方法产生相同的结果:
- 使用 OLS 对 D 和 Z 上的 Y 进行线性回归。
- 三步程序:1)在 Z 上回归 D;2)在 Z 上回归 Y;3)对来自 1 的残差回归来自 2 的残差,以获得β₁(所有回归都使用 OLS)。
以类似的方式,回到我们的部分线性例子,我们可以如下进行:
- 使用机器学习基于 Z 预测 D;
- 使用机器学习基于 Z 预测 Y;
- 来自 2 的残差对来自 1 的残差的线性回归,用于获得θ₀.的估计
这个过程确保了来自步骤 3 的模型是“正交化的”,产生了无偏的、根 n 一致的估计量。这种方法的θ₀-θ分布见下图。
θ₀-θ分布,正交估计量。原文此处。
我们如何将这个过程形式化和一般化?为此,我们首先需要引入得分函数和矩条件的概念(关于广义矩方法的介绍,参见本页)。具体来说,在这种情况下使用的得分函数具有ψ=(d−m₀(z))×(y−g₀(z)−(d−m₀(z))θ形状,其中乘法项是我们的部分线性模型的误差项,尽管其他替代方案是可用的。现在,我们要求这个得分函数等于零,ψ=0,这就构成了我们的矩条件。这在数学上表达了我们希望回归量和误差正交,这类似于我们希望它们不相关。从操作上讲,这意味着一旦我们有了对 g₀和 m₀的估计,我们就能从力矩条件方程中得到θ₀。
我们终于准备好将奈曼正交性 条件表达和定义为
∂η𝔼[ψ(w;θ₀,η₀)][η−η₀] = 0
其中 W 是我们的数据。该方程解释如下:左手边是我们的得分函数相对于我们的干扰参数η在η₀.附*的 gateaux 方向导数我们说我们希望这个导数消失。而且,因为导数是瞬时变化率,所以我们要说的是,我们的得分函数(以及我们对θ₀的估计)对于干扰参数η的“小”扰动应该是稳健的。
总之,将这个内曼正交条件应用于我们的得分函数(从而应用于我们的θ₀和η₀的估计量)使得我们的θ₀的估计量摆脱了两个偏差源之一,正则化偏差。
样本分割和交叉拟合
现在,是时候消除偏置的另一个来源,即过拟合偏置(同样,有关这些偏置形状的详细解释,请参见本演示文稿)。为此,一种可能的策略是所谓的样本分割方法。其运作方式如下:
- 我们将数据随机分成两个子集。
- 我们在第一个子集上为 D 和 Y 拟合我们的机器学习模型。
- 我们使用步骤 2 中获得的模型来估计第二个子集中的θ₀。
这种策略的缺点是它降低了效率和统计能力。但这可以通过交叉拟合的方式解决,这是双机学习方法遵循的策略。内容如下:
- 我们将数据随机分成两个子集。
- 我们在第一个子集上为 D 和 Y 拟合我们的机器学习模型。
- 我们使用步骤 2 中获得的模型估计第二个子集中的θ₀,₁。
- 我们将我们的机器学习模型放在第二个子集中。
- 我们使用步骤 4 中获得的模型来估计第一个子集中的θ₀,₂。
- 我们得到了θ₀作为θ₀,₁和θ₀,₂.*均值的最终估计量
注意,我们可以通过对 K 个折叠重复该过程来获得鲁棒性,其中 K 大于 2。
该算法
我们终于到了这篇文章的结尾,是时候收集所有之前解释过的成分,并将它们放在一个算法中了。
让我们定义一个完全交互的模型,比部分线性的模型更一般。这个模型没有假设 D(处理)是可加性分离的:
通用模型
我们感兴趣的因果参数是 ATE,𝔼[g₀(1;z)-g₀(0;Z)],我们的得分函数是
它是由 Robins 和 Rotnizsky (1995)提供的,并且是 neyman 正交的(并且是双重鲁棒的),满足∂𝔼ψ(w;θ₀, η)= 0.
在这种设置中,双机器学习通过以下步骤提供 ATE 的无偏、根 n 一致估计量及其置信区间(置信水*为α):
因此,我们已经完成了我们的任务。请注意,在 https://github.com/DoubleML的中可以找到实现这个和其他相关算法的 Python 和 R 的可用包。
DouZero:通过强化学习掌握斗地主
解释最受欢迎的中国扑克斗地主的强大人工智能系统
https://douzero.org/。图片作者。
从 AlphaGo 和 AlphaZero 开始,人工智能(AI)*年来取得了令人鼓舞的进步,在围棋、象棋、德州扑克、DOTA、星际争霸等方面击败了人类顶级选手。那么,下一个挑战是什么?
In this article, I would like to introduce an ICML paper that presents a new AI System for a Chinese Poker game DouDizhu (斗地主 in Chinese). DouDizhu is the most popular poker game in China with hundreds of millions of daily active players, with many tournaments held every year. It is easy to learn but difficult to master. Interestingly, DouDizhu remains to be a grand challenge for AI because of competition, collaboration, and a large number of possible moves from turn to turn.
斗地主是什么,为什么有挑战性?
这是一种脱牌游戏,玩家的目标是在其他玩家之前清空自己手中的所有牌。两个农民玩家组队对抗另一个地主玩家。如果任何一个农民玩家第一个没有牌剩下,农民就赢了。每场游戏都有一个叫牌阶段,玩家根据手中牌的强弱为地主叫牌,还有一个出牌阶段,玩家依次出牌。
简而言之,以下特性使得斗地主对 AI 来说非常具有挑战性:
- 缺陷信息:与围棋或象棋不同,斗地主的棋手无法观察其他棋手的手牌。这导致了可能性的爆炸。
- 既竞争又合作:两个农民玩家需要合作对抗地主玩家。大多数以前的扑克人工智能是为竞争环境(如德州扑克)或合作环境(如 Hanabi)设计的。既竞争又合作是一个公开的挑战。
- 大而复杂的动作空间:由于牌的组合,有 27,472 种可能的移动。不同回合的法律行动大相径庭。现有的人工智能往往只支持非常简单和小的行动空间。
一只手及其合法组合的例子。图片由作者提供,来源:https://arxiv.org/abs/2106.06135
用 DouZero 从零开始掌握斗地主
尽管面临这些挑战,我还是很兴奋地向大家介绍最*的 ICML 论文和 DouZero 项目。DouZero 首次实现了一个强大的斗地主 AI,可以通过深度强化学习从零开始训练,达到人类水*的性能。有趣的是,DouZero 背后的魔法出奇的简单却非常有效。作者还基于我们的 RLCard 项目开发了一个在线演示。你可以试试这里的演示。
让我们深入探讨 DouZero 背后的技术。使用的核心算法称为深度蒙特卡罗(DMC),这是经典蒙特卡罗(MC)方法的深度版本,用于强化学习。MC 方法的目标是优化 Q 值 Q(s,a) ,其中 s 代表状态, a 代表动作。MC 方法的关键思想是通过迭代执行以下过程来优化策略:
- 使用当前策略生成剧集。
- 对于每一个出现在剧集中的 s,a ,计算并更新 Q(s,a) ,返回关于 s 、 a 的所有样本的*均值。
- 对于剧集中的每个 s,用导致最大 Q(s,a) 的动作更新策略。
第二步的*均回报通常是通过折现的累计回报得到的。在步骤 1 中,我们可以使用ε-greedy 来*衡勘探和开发。MC 方法是最基本的强化学习算法。它们直接逼*目标值,而无需自举(即,像在 DQN 那样,使用下一个状态的值来估计当前状态的值)。
让我们来看看 DMC,它结合了 MC 和神经网络。在 MC 中, Q(s,a)是用表格表示的。现在,我们用 Q 网络代替 Q 表,它可以是多层神经网络。然后,在步骤 2 中,我们使用均方误差(MSE)损失来更新 Q 网络。这就产生了所谓的 DMC,也就是深度版的 MC。
虽然 MC 方法被批评为不能处理不完整的情节,并且由于高方差而被认为是低效的,但是作者发现 DMC 非常适合斗地主。一方面,斗地主是一个情节任务,因此我们不需要处理不完整的情节。另一方面,DMC 可以很容易地并行化,以有效地每秒生成许多样本,从而缓解高方差问题。DMC 的一个好处是,由于实施和运行非常简单,因此可以通过多个流程轻松高效地加速。DouZero 通过多进程和动作编码进一步增强了 DMC(我将在后面详述),并令人惊讶地实现了非常强大的性能。
与 DQN 和政策梯度的比较
深度 Q 学习(DQN)和策略梯度(PG)是最流行的强化学习算法。为什么选择 DMC 而不是 DQN 或 PG?
在 PG 中,我们经常使用一个类似分类器的函数逼*器,它的输出与动作的数量成线性关系。然而,在斗地主中,有 27,472 个可能的动作,这使得输出非常大并且难以训练。在实际操作中,通常每回合的合法动作并不多,斗地主中的动作可以自然地编码成卡片矩阵。因此,更有希望的方法是将动作作为输入,这样我们就可以避免将 27,472 个动作作为输出。基于值的方法可以自然地处理这种情况,因为 Q 值自然地将状态和动作作为输入。
同样,DQN 也接* Q 值。然而,在斗地主实施 DQN 教是很棘手的。斗地主有一大组可能的行动,其中不同的行动子集在不同的回合中是合法的。DQN 中的自举步骤依赖于最大值运算,这在计算上是昂贵的,并且在可变动作空间上实现起来是棘手的。此外,当行动空间较大时,DQN 可能会遭受高估。
相比之下,DMC 非常容易实现,并且易于通过多个流程加速。虽然 DMC 可能遭受高方差问题,但作者表明它在具有*行参与者的斗地主中工作良好。
在斗地主实施 DMC
动作编码。图片作者。来源:https://arxiv.org/abs/2106.06135
斗地主中的状态包括手牌,历史招式,其他玩家手牌的数量,动作是一套牌。DouZero 将每个卡组合编码成一个 15 x 4 的矩阵。这种表示非常通用,可以用来表示任何卡片。DouZero 将状态和动作编码到这样的卡片矩阵中。
神经架构。图片作者。来源:https://arxiv.org/abs/2106.06135
对于神经网络,DouZero 使用了一个神经网络,该网络通过 LSTM 网络对历史走势进行编码。然后,LSTM 的输出与其他特征连接在一起,一起馈送到六层 MLP 以生成 Q 值。在每一步,我们只需要输入国家和所有的法律行动。网络将预测所有法律行为的 Q 值。我们可以选择最大的一个。
DouZero 为斗地主中的三个位置使用了三种不同的网络,在功能上略有不同。然后,作者在一个 4-GPU 服务器上并行运行 45 个角色进行模拟。经过几天的培训,DMC 可以令人惊讶地学习非常强的政策。官方实现是这里。我们还在 RLCard 项目中支持 DMC。RLCard 是卡牌游戏中强化学习的工具包,目前支持斗地主等 8 种环境,DouZero 等 4 种算法。
摘要
DouZero 的成功表明,经典的蒙特卡罗方法经过一些改进,可以在一个困难的领域——dou dizhu 中产生非常好的结果。Monte-Carlo 方法很容易实现,使用的超参数很少,但是它们非常强大。我希望你喜欢这本书,这种见解可以帮助你开发你的人工智能代理和解决其他问题。我将在后面的帖子中详细介绍 DMC 和 DouZero 的代码库。
批量下载和预处理医学图像
用 Python 实现 DICOM 到 NumPy
胸部 CT 来自维基百科(公共领域)
如果你有兴趣为机器学习项目创建自己的医学图像数据集,这篇文章就是为你准备的。如果您使用公开的医学成像数据集,并且希望进一步了解这些数据集是如何创建的,您可能也会对这篇文章感兴趣。
在这篇文章中,你将学到:
- DICOM 的基础知识,医疗图像在卫生系统中存储的文件格式;
- 如何使用 Python 从卫生系统存储中批量下载数万张医学图像作为 DICOMs
- 如何使用我在准备 RAD-ChestCT 数据集时开发的端到端 Python 管道清理 DICOMs 并将其转换为 NumPy 数组,该数据集包含 36,316 个胸部计算机断层扫描体,是世界上最大的体积医学成像数据集之一。
这篇文章部分基于我最*的一篇论文的附录,“基于机器学习的大规模胸部计算机断层扫描体积的多种异常预测”(发表在杂志医学图像分析上,也可在 arXiv 上获得)。如果你觉得这篇文章对你自己的研究有帮助,请考虑引用我们的论文。这篇帖子的代码在 GitHub 上公开了:rachellea/CT-volume-preprocessing。
有许多不同种类的医学图像,包括投影 x 射线、计算机断层扫描(CT)、磁共振成像(MRI)和超声波。在这篇文章中,我将使用 CT 扫描作为一个运行的例子,尽管这篇文章中讨论的许多概念也适用于其他类型的医学图像。
选择要下载的扫描
构建医学图像数据集的第一步是选择要下载的扫描。选择扫描的一种方法是利用放射学报告的数据库。
每次获取医学图像时,放射科医师都会撰写一份报告,描述医学图像中存在或不存在的异常,以帮助不是医学成像专家的其他医生了解该图像的哪些方面对患者护理最重要。这些书面报告储存在卫生系统数据库中。放射学报告表通常包括报告文本本身,以及三个重要的标识字段:患者的病历号(MRN)、登记号和协议描述。
病历号(MRN)在卫生系统中唯一标识患者。MRN 对于定义训练/验证/测试分割至关重要,在该分割中,没有患者出现在一个以上的集合中。来自同一患者的图像必须全部归入同一组,因为即使扫描相隔多年,它们也是相关的,因为它们描绘的是同一个人。
登录号唯一地标识医学成像事件。它是一个字母数字标识符,例如“AA12345”(不同于 MRN),指定了特定患者成像的特定事件。医学成像事件的登录号用于与该事件相关联的书面报告和图像文件,这使得能够将书面报告与其对应的图像相匹配。
最后,协议描述是一个字符串,它描述了执行了哪种医学成像。协议可以被认为是获得某种医学图像的“处方”。“方案描述”是配方的标题。协议描述通常指定模态(例如,CT 对 MRI 对超声)、被成像的身体部分以及关于如何获取成像的其他细节(例如,是否使用对比)。
以下是 CT 扫描的一些协议描述示例:
- CT 腹部骨盆对比
- CT 胸部 wo 对比 w 3D MIPS 协议
- CT 胸部腹部骨盆增强扫描
- CT 腹部骨盆 W CONT
- CT 腹部骨盆无对比
- 无对比 CT 脑
- CT 胸部 PE 协议,包括 CT 血管造影胸部双对比
- 胸部 CT 增强扫描
- CT 胸部
- CT MIPS
按协议过滤扫描
协议描述的一个关键部分是身体的哪一部分被成像的说明。用于成像的不同解剖位置包括仅胸部、仅腹部、仅骨盆、胸部/腹部/骨盆一起、头部、脊柱、手臂、手等等。
不同解剖区域的 CT 示例如下:
不同解剖区域的 CT 扫描示例。从左到右,图像显示了头部、胸部和腹部。图片来自维基百科(创意通用许可):头部 CT ,胸部/腹部 CT 。
当构建医学成像数据集时,重要的是要考虑是否有必要组合来自不同身体区域的图像,或者仅关注一个身体区域是否最有意义。深度病变数据集包括许多不同的身体区域,因为目标是建立模型来识别“病变”,病变被广泛定义为任何局部异常。相比之下,模拟 CXR 数据集只包括胸部的图像,因为重点是明确的胸部异常。一般来说,您应该有一个很好的理由将来自不同身体部位的图像组合在同一个数据集中,因为不同身体部位的未经过滤的图像将显示相互排斥的病理-例如,头部 CT 扫描永远不会显示肺炎,因为肺炎只针对肺部,而胸部 CT 扫描永远不会显示中风,因为中风只针对大脑。
胸部是医学成像数据集的一个受欢迎的身体区域,因为它包括多个主要器官(最主要的是心脏和肺),并且可以显示各种各样的病理发现,包括肺结节、间质性肺病、胸腔积液、肺癌、感染、炎症和水肿( Purysko 2016 )。关注胸部的医学成像数据集包括模拟 CXR 、 CheXpert 和 RAD-ChestCT 。
协议的第二个重要组成部分是对是否使用造影剂以及如何使用造影剂的描述。造影剂是一种不透射线的液体、糊状物或片剂,由钡、碘或钆制成,通过吞咽、动脉注射、静脉注射或灌肠输送,以突出特定的解剖结构( Lusic 2013 )。不同种类的对比看起来会不一样。在 CT 扫描中,对比度显示为亮白色。造影剂的类型、给药方法以及相对于扫描采集的时间都可以在协议描述中指定。
CT 肺部血管造影照片是一种胸部 CT,其中注射造影剂以填充肺部血管;它通常用于诊断肺栓塞,即肺部血管中的凝块。凝块看起来比周围的白色造影剂更灰,因此放射科医师更容易识别(威特拉姆 2004 )。
可以通过腹部和骨盆的对比 CT 来评估阑尾炎、憩室炎和胰腺炎( Rawson 2013 )。
在其他情况下,造影是不必要的,甚至是禁忌的——例如,如果患者患有肾脏疾病,怀孕,或者之前对造影剂有过敏反应。
RAD-ChestCT 数据集仅包括在没有静脉造影的情况下获得的胸部 CT 扫描。为了选择 RAD-Chestct 数据集的访问号,我过滤了放射学报告表,只选择符合“CT 胸部 wo 对比 3d mips 协议”(第二常见协议)或“CT 胸部无对比 3d mips 协议”的小写协议描述的扫描这产生了 36,861 份 CT 报告,我可以用这些报告的登录号来下载与每份报告相关的实际 CT 图像。
生物医学成像数据交换标准(DICOM)
在开始下载和处理图像的细节之前,有必要介绍一下 DICOM。
生物医学成像数据交换标准(DICOM)是医疗系统中存储医学图像的标准格式。DICOM 格式的图像被保存为带有相关元数据的像素阵列。元数据包括关于患者的信息,包括患者的姓名和生日。元数据还包括关于图像本身的信息,例如用于获取图像的设备名称和某些成像参数。虽然元数据中的几个 DICOM 属性应该总是可用的(例如,患者的姓名),但是其他 DICOM 属性对于特定的扫描仪或机器是唯一的,或者对于特定种类的成像是唯一的,因此可能并不适用于所有的 DICOM 文件。这意味着特定 DICOM 文件中可用的确切属性集不能保证与另一个 DICOM 文件中的属性集相同。
为了创建 RAD-ChestCT 数据集,几个 DICOM 属性特别重要。下表总结了这些属性,其中包括来自 Innolitics 的 DICOM 标准浏览器的摘录:
DICOM 属性有助于清理 CT 体积。图片作者。来源:Innolitics DICOM 浏览器:图像类型、图像方向(患者)、图像位置(患者)、重新缩放截距、重新缩放斜率、像素间距、机架/探测器倾斜。
下载医学图像
可以使用卫生系统中可用的许多软件程序逐个下载医学图像。然而,单独下载对于创建大型医学成像数据集是不实际的。为了收集大量图像,需要自动化机制。
在医疗系统中,医学图像通常保存在供应商中立的档案库或 VNA 中。引用 Wikipedia “供应商中立归档(VNA)是一种医学成像技术,其中图像和文档[…]以标准格式和标准接口存储(归档),以便其他系统可以以供应商中立的方式访问它们。”因此,VNA 基本上是医疗系统中的一个地方,其中存储了一整串 DICOM 格式的医学图像,以便整个医院系统中的各种设备可以轻松访问它们。
为了创建 RAD-ChestCT,我使用为杜克大学供应商神经档案库开发的应用程序接口(API)查询了所有 36,861 个选择的胸部 CT 扫描登录号。代码可在这里;请注意,出于安全原因,所需的密钥已全部取出。
因为我下载了很多 CT 扫描,下载过程需要 49 天。总体而言,下载成功率为 99%,在最初指定的 36,861 卷中获得了 36,316 卷。
基于登录号批量下载 CT 扫描的一个重要考虑因素是如何选择系列。
选择系列
单个 CT 扫描事件由一个唯一的登录号指示,但是该登录号可能对应于一个以上的 CT 体积。例如,如果第一次扫描质量不足,则可以获得第二次扫描。此外,一旦扫描完成,解释扫描的放射科医师可以创建“重新格式化”,这是以不同方式显示患者身体的扫描数据的替代表示。
在 CT 扫描事件期间获得的每个单独的 CT 扫描和之后创建的每个单独的重新格式化被称为单独的“系列”
当下载数以万计的扫描时,每个扫描都有多个系列,假设可以为每个扫描手动选择“最佳和最具代表性的系列”是不切实际的。相反,需要一个自动化的过程来选择系列。
对于 RAD-ChestCT,我定义了以下自动选择系列的过程:
首先,我利用了 ImageType DICOM 属性,只考虑标记为原始的扫描。我排除了标记为衍生、二次或重新格式化的扫描。
接下来,对于包含多个原始系列的登录号,我选择了切片数量最多的系列。
CT 扫描是体积扫描,表示为一叠 2D 图像(“切片”)。原始 CT 系列将由未裁剪的轴向切片组成,例如:
原始 CT 系列预期布局中的 CT 扫描切片。CT 切片显示广泛的肺纤维化。它来自维基百科,在知识共享许可下发布。
以下是非原装 CT 系列的几个例子:
非原创的 CT 系列。在这个非原创的系列中,大部分其他解剖结构都被裁剪出来,集中在心脏上。图片作者。
非原创的 CT 系列。在这个非原创的系列中,病人的两个矢状面视图相互叠加。图片作者。
代号
批量下载原始 CT 卷、选择原始系列以及记录下载进度信息的代码在 GitHub 上公开提供,如下:rachellea/CT-volume-preprocessing/download _ volumes . py。
CT 体积预处理:DICOM 到 NumPy
不幸的是,原始 DICOMs 不能用作任何主要机器学习框架的输入。在对 DICOMs 进行计算分析之前,必须首先对其进行处理。我开发了一个端到端的 Python 管道,可以将对应于一次 CT 扫描的不同切片的独立 DICOM 文件处理成与 PyTorch、Tensorflow 或 Keras 兼容的单个 3D NumPy 数组。
下面是所需处理步骤的快速总结,后续部分将提供更多详细信息:
- 按照正确的顺序叠放 CT 切片;
- 再次检查 CT 扫描是否处于所需的方向;
- 使用 DICOM 属性 RescaleSlope 和 RescaleIntercept 将像素值转换为 Hounsfield 单位(HU );
- 将像素值裁剪为[-1000 HU,+1000 HU],HU 标度的实际下限和上限,分别对应于空气和骨骼的放射密度;
- 使用 SimpleITK 将每个体积重新采样为 0.8 x 0.8 x 0.8 mm,以实现所有患者之间一个像素的一致物理距离含义;
- 将像素值转换为 16 位整数,并使用无损 zip 压缩保存,以降低存储要求。
排序切片
为 CT 体积的每个轴向切片保存单独的 DICOM 文件。相对于站立的人,“轴向切片”代表穿过身体的水*面(由围绕腰部的带子形成的同一*面):
解剖*面。轴向*面显示为绿色。CT 扫描通常保存为轴向*面上的一组 2D 图像,每个 2D 图像有一个单独的 DICOM 文件。这张图片是由 David Richfield 和 Mikael Haggstrom 根据维基百科图片修改而来的,可以在知识共享许可下使用。
单独的轴向切片必须以正确的顺序堆叠,以便重建 3D 体积-这是一个令人惊讶的不简单的过程,但对于获得可用的数据来说是至关重要的。如果切片以错误的顺序放在一起,体积表示就会被破坏。
用于切片排序的最直观的 DICOM 属性称为实例编号。这个属性应该使用整数来指定切片的顺序。不幸的是,该属性并不可靠,并且被一些 CT 扫描仪错误地填充,因此实例编号不应该用于切片排序。
对切片进行排序的最可靠方法是利用 ImageOrientationPatient 和 ImagePositionPatient 属性。
在 CT 体积内有 12 种可能的患者方位:
一个人在一个空间中的 12 种可能的方位。方向很重要,因为人类的内部是不对称的(例如,心脏在左边,胃在左边,肝脏在右边)。图片作者。人体轮廓修改自维基百科人体方案(知识共享许可)。
在 DICOM 文件中,属性ImageOrientationPatient指定了患者的方位,并且需要确定哪个方向是“z 方向”(轴向切片堆叠所沿的头尾方向)。通常情况下,患者在胸部 CT 中呈现“1,0,0,0,1,0”方向,但该方向仍然很重要,因为它有时会发生变化。
给定患者方位,可以确定存储在图像位置患者属性中的哪个值是 z 位置。z 位置反映了切片在空间中头尾(从头到脚)位置的物理距离测量。通过根据切片的 z 位置对切片进行排序,可以获得正确的切片排序并重建完整的 3D 体积。
以下是一次 CT 扫描切片的排序 z 位置的摘录(排序 z 位置的完整列表要长得多,因为有 512 个切片):
[-16.0, -16.6, -17.2, -17.8, -18.4, -19.0, -19.6, -20.2, -20.8, -21.4, -22.0, -22.6, -23.2, -23.8, -24.4, -25.0, -25.6, -26.2, -26.8, -27.4, -28.0, -28.6, -29.2, -29.8, -30.4, -31.0, -31.6, -32.2, -32.8, -33.4, -34.0, -34.6, -35.2, -35.8, -36.4, -37.0, -37.6, -38.2, -38.8, -39.4, -40.0, -40.6, -41.2, -41.8, ..., -318.4, -319.0, -319.6, -320.2, -320.8, -321.4, -322.0, -322.6, -323.2, -323.8, -324.4, -325.0, -325.6, -326.2]
在我的管道中,我利用来自 Innolitics 的 combine_slices.py 来执行基于 DICOM 属性 ImageOrientationPatient 和 ImagePositionPatient 的切片排序步骤。
将像素值重新调整为 Hounsfield 单位
CT 扫描仪的测量值用 Hounsfield 单位表示。
Hounsfield 单位表示材料的放射密度,范围从大约-1000 HU(空气)到+1000 HU(骨),水在中间,为 0 HU。在某些情况下,对于非常致密的骨骼(如耳朵中的耳蜗),HU 可以达到大约+2000,但大多数组织都在-600 到+100 HU 的范围内( Lamba 2014 )。
汉斯菲尔德量表。图片作者。
DICOMs 中的原始像素值经过了 Hounsfield 单位的线性转换,以实现高效的磁盘存储。这个转换必须反向进行,以再次获得 Hounsfield 单位(HU)的像素值。DICOM 标准包括属性 RescaleSlope 和 RescaleIntercept ,它们是公式 y = mx + b 中的 m 和 b ,需要将存储在 DICOM 像素阵列中的原始像素值 x 线性转换为 Hounsfield 单位 y。来自 DICOM 的所有原始像素值必须使用 RescaleSlope 和rescale intercept重新调整为 HU 这确保了像素的特定数值在所有扫描中指示相同的放射密度。注意 RescaleSlope 通常等于 1,而 RescaleIntercept 通常等于-1024,但仔细检查总是一个好主意。**
转换为 HU 后,像素值可被裁剪为[-1000 HU,+1000 HU],这代表 HU 标度的实际下限和上限,分别对应于空气和骨骼的放射密度。
重采样
除了通过转换为 Hounsfield 单位来确保像素值在整个数据集内具有一致的放射密度含义之外,确保每个像素在整个数据集内代表一致的体积也是很重要的。DICOM 标题包括属性像素间距,该属性包括以毫米为单位的 x 测量值和 y 测量值(x 间距和 y 间距)。z-间距(颅尾间距)可以通过减去图像位置患者中报告的相邻切片的 z-位置值来推断。综上所述,(x,y,z)间距表示单个体素所代表的以立方毫米为单位的物理体积(一个体素是一个 3D 像素)。
不幸的是,这些物理距离根据扫描参数而变化,使得每个体素的物理尺寸对于不同的患者是不同的。此外,对于单次扫描,( x,y)间距通常不同于 z 间距:
CT 扫描中(x,y,z)间距变化的一个例子。图片作者。
为了实现数据集中所有像素的一致物理距离含义,RAD-ChestCT 数据集中的所有 CT 都使用 Python 包简单 ITK 重新采样为 0.8 x 0.8 x 0.8 mm。该重采样步骤还具有适度减小每个卷的大小的优点,这降低了对该数据集训练的机器学习模型的磁盘存储需求和内存需求。
使用不规则 Z 间距进行重采样
对于单次 CT 扫描,计算每对相邻轴向切片之间的 z 间距值。因此,对于具有数百个切片的典型 CT 扫描,存在数百个 z 间距不规则的机会。为创建 RAD-ChestCT 数据集而下载的全部扫描中,大约有 4%的扫描在其任何切片之间至少有一个不规则 z 间距的实例。由于缺少切片或扫描采集过程中的其他异常情况,可能会出现不规则的 z 间距。
在执行简单的 ITK 重采样步骤时,小心处理这些扫描非常重要。必须为 SimpleITK 提供输入的(x,y,z)间距和输出的所需(x,y,z)间距。如果有多个 z 间距值可用,则整个扫描的 z 间距模式是 z 间距参数重采样函数的正确输入。使用 z-spacing 模式可防止扭曲,如果改为提供最大或最小 z-spacing 值,则会发生扭曲:
使用 SimpleITK 配置重采样时,选择最大 z 间距值而不是模式导致的扭曲示例。
使用 SimpleITK 配置重采样时,选择最小 z 间距值而不是模式导致的扭曲示例。
健全性检查
对于任何涉及多个步骤的数据准备管道,执行健全性检查总是好的。在 RAD-ChestCT 数据集的创建中执行的一个健全性检查是在轴向、冠状和矢状视图上对多个完整的 CT 体积进行手动检查,以确保所有切片都处于正确的顺序,没有引入重采样伪影,并且灰度像素值对于不同器官来说似乎是合理的。
CT 体积的有效存储
简单的 ITK 重采样步骤将像素值转换为 32 位浮点数。在 RAD-ChestCT 的最终数组中,像素值存储为 16 位整数。DICOMs 中的 Hounsfield 单位都是整数,可以用 12 位表示,但 numpy 中没有 12 位表示,所以选择了 16 位。最终的 3D numpy 数组以无损 zip 压缩保存,以进一步节省存储空间。(我还发现,在训练机器学习模型时,从磁盘上读取压缩文件并解压缩,比从磁盘上读取大得多的未压缩文件更快。)对于 RAD-ChestCT,原始未处理的 DICOMs 需要 9.2 太字节的存储空间。最终压缩清理后的数组需要 2.8。
代号
在 GitHub 的Rachel lea/CT-volume-preprocessing/preprocessing _ volumes . py上可以公开获得用于批量处理 CT 体积的所有前述步骤的 Python 管道。
总结
- 根据登记号,可以批量下载数以千计的医学图像。
- 医学图像需要许多仔细的预处理步骤才能从 DICOM 转换成适合下游机器学习任务的格式。
原载于 2021 年 2 月 16 日 http://glassboxmedicine.com**的 。
将 Kaggle 数据集下载到 Deepnote
我如何使用自己创建的 bash 脚本来实现自动化
来自 Unsplash
如果你像我一样,你通过创建宠物项目来学习。Kaggle.com是一个优秀的 pet 项目数据来源(以及其他)。
我还使用 deep not e 作为工作空间来构建我喜欢的项目。同样,这是因为它拥有我在一个环境中想要的一切,比如文件目录,轻松访问 GitHub,一个终端,无缝删除和创建 python 环境的能力。另外,所有的魔法和实验都发生在我的本地系统之外。
如果你想抱怨一些在 Deepnote 上不工作的事情,他们在每个新的项目环境中都有聊天支持。
Gif 来自 Giphy.com
因为我经常创建宠物项目和使用 Kaggle 数据集,我需要自动化至少 80%的下载过程,以减少摩擦和节省时间。
我创建了一个 bash 脚本来完成这个任务。这是它看起来的样子
来自作者的代码
它是做什么的?
- 第一段代码从当前工作目录切换到直接父目录,在 Deepnote:
work
目录中切换到root
目录 - 设置 Kaggle CLI 包括
pip
安装 Kaggle 库,创建一个名为.kaggle
(命名是固定的,应该位于根目录下)的目录,将你的 KaggleAPI Token
移动到创建的目录下。 - 您将需要 Kaggle 数据集的 API 来下载首选数据集。第行第 14** 行提示请求该 API。第 15 -16 行读取 API 输入并运行。**
- 扩展名为
.zip
的 Kaggle 数据集 API 返回文件。第 20 行解压该文件,并将输出移动到work
目录。 - 剩下的代码集中在清理环境上,即卸载
kaggle
库,将kaggle.json
文件移回原来的存储库文件夹等等。
注意,这个脚本是专门为 Deepnote 项目环境构建的。因此,您应该熟悉 Deepnote 项目环境中的目录,如 *work*
目录。
如何运行脚本
在运行脚本之前,请确保以下事项。
- 您已经将这个存储库克隆或者分支到您的远程存储库。要利用和运行 bash 脚本,脚本文件应该在您的远程 Github repo 中。
- 您拥有一个 Deepnote 帐户,并创建了一个新的项目工作环境。一个新的项目环境看起来像这样。
来自作者的截图
如果你还没有一个 Deepnote 帐户,你可以在这里注册。
- 您已经将 bash 脚本存储库链接到您的 Deepnote 项目环境。这里是https://docs.deepnote.com/integrations/github****文档,用于将存储库链接到您的项目。一旦它被链接,您会注意到存储库文件夹如下所示。
作者截图
- 您有一个 Kaggle API 令牌。你可以在你的 Kaggle 个人资料中找到并下载这个文件,如下图。
来自作者的截图
- API 令牌将被下载到一个名为
kaggle.json
的文件中。上传文件并将其移动到 Deepnote 项目环境中的存储库文件夹中。** 这里的是如何在你的项目环境中上传一个文件。这是它应该看起来像什么的一个例子**
来自作者的截图
只有在将存储库链接到您的项目环境之后,才上传 ka ggle . JSON
- 您已经复制了想要下载的数据集的 API 命令。当您运行脚本时,您将需要它。您可以在选择的 Kaggle 数据集页面的右侧找到该命令。
来自作者的截图
现在你已经设置好了,…
- 前往终端图标,并创建一个新的终端。这里的是一个关于如何的文档。
- 在终端的当前工作目录下,运行下面的命令。
sh Automating_kaggle_dataset_download/download_kaggle_dataset.sh
就这些,真的。说真的。
这是这个过程的视频演示
作者录音
如果返回到文件图标,您会看到数据集文件和存储库文件夹。
您可以通过运行以下命令删除存储库文件夹
rm -r Automating_kaggle_dataset_download/
请注意,Deepnote GitHub 集成一次只链接一个 repo,因此如果您想要将另一个重新定位链接到您的项目工作区,您需要通过删除粘贴的链接来取消前一个重新定位的链接。
使用 Deepnote 时,这种方法将节省我下载 Kaggle 数据集的时间。虽然它不是 100%自动化的,因为每次您想要使用脚本时都需要在运行它之前上传 kaggle.json,但它仍然胜过每次您在 Deepnote 或本地新的虚拟环境中创建新的项目环境时都必须手动执行
**pip install kaggle**
和其他设置程序。
这也是一个简单的脚本创建和简单的任何人都可以适应/改善如果需要。
参考
衡量数据质量投资回报率的正确方法
引入一个更好的指标来计算贵公司坏数据的成本
图片由巴尔·摩西提供。
引用一位朋友的话,“构建您的数据堆栈而不考虑 数据质量 ,就像买了一辆法拉利却放在车库里。”
在这篇文章中,特邀专栏作家 弗朗西斯科·阿尔贝里尼 、产品经理 蒙特卡洛 ,介绍了一种更好的衡量公司不良数据成本的方法。
上周,我与 Lina 进行了一次电话会谈,她是我们一家大客户的数据产品经理,负责监管他们的数据质量项目。
她的团队负责维护 1000 条数据管道,这些管道填充了公司许多最关键的业务表。可靠和值得信赖的数据是产品成功的基础,但 Lina 一直在努力寻找一种清晰的方法来量化她所拥有的管道的重要性和范围。
“我们都知道可靠的数据对业务很重要,”她说。“但我没有一个很好的方法来衡量它的投资回报率,并据此证明对我的团队的投资是合理的。”
她没有获得保持公司管道运行所需的支持(如果管道无法运行),而是发现自己一天 24 小时都在亲自维护管道。
一次又一次,预算和资源将被分配到更华丽、更专注于功能的数据工程工作,当季度规划到来时,她的精干团队陷入困境。
Lina 的经历非常恰当地传达了许多数据团队面临的一场斗争:证明和衡量数据质量计划的 ROI。
与 Lina 和其他许多完全类似的人的对话,引导我们寻找可以帮助团队更好地传达数据可靠性对他们公司的价值的测量框架。
我们发现以下指标(借用自 DevOps world 的)提供了一个良好的开端:检测时间和解决时间。
检测时间(TTD)
检测时间是指您的数据工程团队识别数据质量问题所需的时间,无论是新鲜度异常、模型运行失败,还是导致整个管道陷入混乱的模式更改。
对于许多数据团队来说,TTD 通常以几天到几周,有时甚至几个月来衡量,因为主要的检测手段是等待下游数据消费者传达数据“看起来不合适”
这几周至几个月的数据停机时间对企业来说代价极其高昂,原因有二:
- 经过的时间越长,通过重新处理或回填可能不再可用的源数据来恢复数据就越困难。
- 所有的商业决策、营销活动、产品决策等。使用不正确的数据做出的决策需要重新验证,甚至更糟,需要重新传达给利益相关者。
当然,我们都同意将 TTD 减少到几分钟听起来很棒,但是要达到这一点可能需要大量的工程工作,这抵消了努力的投资回报。
在开始解决问题之前,计算你的基线 TTD 是很重要的。要做到这一点,您可以查看您的团队最*处理的 3-4 起数据事件,并粗略计算检测它们所需的时间。在此基础上,你可以设定一个目标,并以此向领导层传达你需要更多资源的原因。
降低 TTD 的工具包:
- 机器学习驱动的 异常检测 : 在投入生产之前测试您的数据是 P0,但是对于跟踪那些未知的未知,实现自动化异常检测和自定义规则是有帮助的
- 相关事件源和通知。在您的数据*台和 PagerDuty、Slack 或您使用的任何其他事件管理解决方案之间集成一个通信层(可能是一个 API)对于进行根本原因分析、设置 SLA/SLIs以及在数据停机发生时对其进行分类至关重要。
解决时间(TTR)
接下来,数据工程团队应该测量解决时间(TTR),该指标旨在回答以下问题:一旦收到警报,您解决数据事件的速度有多快?
TTR 指标也以小时、分钟或天为单位,可让您了解数据问题的严重性,并跟踪解决问题所需的时间。当转换成美元时(即,由于 TTR 而花费/节省了多少钱),向您的利益相关者传达这些数据的影响会变得容易得多。
降低 TTR 的工具包:
- 统计根本原因分析 :正如我们在上一篇文章中所讨论的,根本原因分析是站点可靠性工程团队在确定应用程序为什么以及如何在生产中中断时的一种常见做法。同样,数据团队可以利用统计根本原因分析和其他关于数据的智能见解来了解这些问题最初为什么会出现。
- 端到端沿袭:在数据生命周期的每个阶段,健壮的沿袭使团队能够跟踪从 A(摄取)到 Z(分析)的数据流,将转换、建模和其他步骤合并到流程中,这对于用统计 RCA 方法补充通常目光狭隘的见解(没有双关语)是至关重要的。用于元数据和谱系收集的 OpenLineage 标准是一个很好的起点。
- 数据发现 了解数据访问模式:虽然许多数据目录都有一个以 UI 为中心的工作流,但数据工程师需要通过数据发现灵活地以编程方式与目录进行交互。这种解决方案也可以用来理解数据、谁使用数据以及如何使用数据之间的关系。
把所有的放在一起
您可以通过了解数据不可用时的成本来衡量数据的财务影响。
等式可能是这样的:
图片由巴尔·摩西提供。
停机时间每小时成本是一个通用指标,代表每停机时间花费的工程时间和数据停机时间对数据消费者和业务决策的影响。
花费的工程时间可以作为停工时间的一个因素来计算。例如,我们可以估计 1 名数据工程师花费 1/4 的停机时间来监控和调查问题,这相当于每个停机时间 25 美元(*均 100 美元/小时的工资+数据工程师的福利)。
数据停机的影响根据停机时间对您业务的潜在影响,成本会有很大差异。例如,如果您依靠数据向华尔街报告收益,导致误报数据的停机时间是灾难性的,可能会造成每小时 1000 美元的停机成本。此外,您还可以增加分析团队的停机成本。例如,如果您有 10 名分析师,他们在停机事件期间闲置的成本是巨大的(*均 75 美元/小时工资* 10 = 750 美元/小时)。假设不是所有的分析师都会受到停机时间的影响,我们可以保守地将其降低 75%至 175 美元/小时。
在这种情况下,我们可以估计一小时的停机时间会给我们的业务造成 500 美元/小时的损失。
假设您每月有 ~100 小时的停机时间,那么您的业务成本很容易超过 $600,000/年(100 小时/月* $ 500/小时* 12 个月)。
同样重要的是要记住,这个等式甚至没有考虑机会成本。查看我们的文章和 Poshmark 的首席数据官 Barkha Saxena 了解更多可能的情况。
当您考虑数据中断的频率时,这可不是一笔小数目。
通过计算基线 TTD 和 TTR,就可以更容易地准确传达您期望对业务产生的影响。如果没有这个基线,就很难从权力那里获得运营上的支持,从而壮大您的团队,提升您的技术堆栈,并扩展您梦想中的数据质量计划。
想象一下能够与您的首席技术官分享这一点:
“增加一名数据工程师并投资一个 数据观察*台 可以帮助我们将*均每月停机时间从 350 小时减少到 14 小时,这相当于 3.4 倍的投资回报,即每年 150 万美元。”
正如他们所说:(下)时间真的是金钱。
有没有更好的方法来衡量不良数据的成本?我们很想了解更多!伸出手去 藩 和 其余的蒙特卡洛团队 。我们洗耳恭听。
请务必在 4 月 28 日星期三报名参加我们的 数据可观测性演示 !
机器医生:它能诊断新冠肺炎吗?
使用深度学习方法根据肺部扫描进行诊断
这篇文章是为 MIS 285N:认知计算而写的。我们的团队由 Ella Akl、Saranya Nagarajan、Amulya Saridey、、Han 和 Deepti Rao 组成,他们一起撰写了这篇文章!
来自《走向数据科学》编辑的提示: 虽然我们允许独立作者根据我们的 规则和指南 发表文章,但我们并不认可每个作者的贡献。你不应该在没有寻求专业建议的情况下依赖一个作者的作品。详见我们的 读者术语 。
简介
在过去的一年里,新冠肺炎肆虐全球,改变了人们的生活方式。几乎每晚都可以在新闻中看到当局告诉人们戴上口罩,保持社交距离,以避免感染病毒,这可能导致人们最终在医院使用呼吸机。这给全球带来了恐惧,这是理所当然的。但是如果一个人被诊断出感染了这种病毒,他的身体会怎么样呢?
新冠肺炎是一种通过空气传播的呼吸道传染病。最常见和最受影响的器官是肺。健康肺部的正常功能是从空气中吸收氧气,并从细胞中释放二氧化碳,而新冠肺炎等感染会中断这一过程,使呼吸困难。
数据集背景
作为数据科学领域的学生,我们认为我们正在学习的技能的一个良好用途是探索目前对我们周围世界最重要的影响之一,并看看机器学习模型是否可以根据肺部扫描识别新冠肺炎。我们在 Kaggle 上找到了一个图像数据集,它由健康肺和肺炎(包括 covid 感染的)肺的数千次肺部扫描组成,我们认为它非常适合这个主题。
这一由 5933 张图像组成的集合包括显示感染了新冠肺炎、ARDS、链球菌和 SARS 的肺部的胸部 x 光片,以及大量用于比较的健康肺部扫描。虽然我们从 kaggle.com 获得了这个数据集,但它最初是来自蒙特利尔大学的研究人员。
这些数据包括肺部扫描和一个附加的 csv,该 CSV 包含扫描的元数据,这些元数据将图像标记为正常或感染,如果感染是已知的,还会标记感染是什么。该数据还包括一个“未知”栏,用于标记未知的扫描。这被合并到测试集中,以查看是否可以基于训练的模型在这些扫描上做出诊断。
很高兴你问了这个问题:新冠肺炎在人体中做什么?
数据集导入&设置
Kaggle 新冠肺炎数据集包括来自健康和患病个体的超过 5300 张训练图像和 600 张肺部扫描测试图像。
作者图片
该数据集还包含两个 CSV 文件,其中包含有关图像的元数据,如标签(正常或肺炎)、病毒类型等。
元数据. csv:
Kaggle 数据集的摘要图像
摘要. csv:
Kaggle 数据集的摘要图像
要开始使用数据集,我们必须首先将文件导入笔记本。我们的团队使用 Google Colab 编写代码,在这种情况下,通过 Google Drive 访问我们的数据集文件非常容易和方便。我们只需将图像和 CSV 文件上传到 Google Drive,然后使用以下几行代码将我们的驱动器安装到 Colab:
from google.colab import drivedrive.mount(‘/content/gdrive’)drive.mount(“/content/gdrive”, force_remount=True)
接下来,我们可以创建指定数据位置的路径。
path_metadata = ‘/content/gdrive/My Drive/Dataset/metadata.csv’path_summary = ‘/content/gdrive/My Drive/Dataset/summary.csv’path_data_train = ‘/content/gdrive/My Drive/Dataset/train’path_data_test = ‘/content/gdrive/My Drive/Dataset/test’
首先,我们使用文件路径将两个 CSV 文件(metadata.csv 和 summary.csv)导入到 Pandas DataFrames 中。
import pandas as pddf_metadata = pd.read_csv(path_metadata)df_summary = pd.read_csv(path_summary)
如果我们看一下我们刚刚创建的 df_metadata 数据帧,我们可以看到图像名称与一个标签相关联,该标签描述图像是正常的肺还是患有肺炎的肺。此外,还有另外两列:dataset_type,表示映像是训练集还是测试集的一部分;以及 virus_type,包括新冠肺炎、SARS 等。
作者图片
在开始导入图像之前,让我们根据 dataset_type 列快速将数据帧分为训练和测试:
train_data = df[df[‘dataset_type’]==’train’]test_data = df[df[‘dataset_type’]==’test’]
由于我们的重点是检测肺部扫描中的新冠肺炎,我们还想使用 virus_type 列过滤掉任何非新冠肺炎图像。
final_train_data = train_data[(train_data[‘label’] == ‘normal’) |((train_data[‘label’] == ‘pneumonia’) &(train_data[‘virus_type’] == ‘covid-19’))]
厉害!现在我们可以开始阅读我们的图像。让我们从定义一个将图像转换成像素值数组的函数开始。
def read_img(filename, size, path):img = image.load_img(os.path.join(path, filename), target_size=size)#convert image to arrayimg = image.img_to_array(img) / 255return img
我们可以在一个循环中调用这个函数来导入我们所有的图像,就这样,我们拥有了开始数据清理和探索所需的所有数据!
探索性数据分析&可视化
现在我们已经有了所有的数据,让我们以可视化的形式进行一些数据探索吧!使用 seaborn,我们可以使用元数据 CSV 文件中的数据创建病毒类型和病毒标签的数值计数图。
首先,进口 seaborn。
import seaborn as sns
接下来,为病毒类型和标签类型创建计数图。这里有两个可视化显示,我们的数据集中有什么类型的病毒。第一个可视化显示了病毒类型,第二个可视化显示了除了 NaN 之外还存在哪些感染。
sns.countplot(x=’virus_type’, data=metadata)
作者图片
作者图片
正如我们从上面的可视化中看到的,与其他疾病相比,我们的数据集具有更多正常肺或肺炎肺的图像,因为我们的数据集中的 NaN 值代表的图像不是链球菌、新冠肺炎、ARDS 或 SARS。这是进一步巩固我们的下一个可视化标签。
sns.countplot(x=’label’, data=metadata)
作者图片
我们可以看到数据集主要是肺炎肺,还有一些标记为正常的。这也指向我们有一个不*衡的数据集。
接下来,让我们处理图像!首先,我们需要从 tensorflow.keras、cv2、imageio 导入 os、matplotlib、image,并从 PIL 导入 Image。
import osimport matplotlib.pyplot as pltfrom tensorflow.keras.preprocessing import imageimport imageioimport cv2from PIL import Image
在这之后,我们需要从我们的数据集中调用图像到 Google Colab 文件中。正如前面所做的,我们将把它们分为两个列表,正常肺部图像的 train_imgs_normal 和肺炎图像的 train_imgs_pneumonia。
train_imgs_normal = train_data[train_data.label == ‘Normal’].image_name.values.tolist()train_imgs_pneumonia = train_data[train_data.label == ‘Pneumonia’].image_name.values.tolist()
现在,让我们显示原始图像并进行比较。为了绘制它,我们定义了一个函数,当调用它时,打印出正常和肺炎的图像。这方面的代码是:
def print_images(images, title):plt.figure(figsize=(10,10))for i in range(9):plt.subplot(3,3,i+1)plt.xticks([])plt.yticks([])plt.grid(False)plt.imshow(images[i], cmap=plt.cm.binary)plt.suptitle(title,fontsize=25)plt.show()plt.tight_layout()
现在,让我们为正常的肺部图像调用这个函数。
print_images(normal_images, ‘Normal Images’)
作者图片
正如你在上面看到的,正常的肺部图像相对清晰,没有阻塞。这通常是正常和健康的肺的情况,因为它们的主要功能是相对容易地吸收氧气。现在让我们看看肺炎的肺。
print_images(pneumonia_images, ‘Pneumonia Images’)
作者图片
我们可以看到肺炎的肺比正常的肺更混浊,当我们试图观察它们时,它们相对来说是阻塞的。这些图像很容易显示感染可能造成的损害。现在让我们试着仔细看看!
当我们试图将肺炎感染的肺与正常健康的肺进行分类时,我们需要注意显示一些结构或生理的 x 射线图像的定性方面,而不是测量感染程度的定量方面。因此,在我们的情况下,图像的像素值本身没有意义,因此,我们可以对图像进行归一化,以清楚地观察健康肺和受感染肺之间的生理差异。
下面是一个计算每张图片*均值的函数。
def find_mean_img(sample_img, title, size = (64, 64)):# calculate the averagemean_img = np.mean(sample_img, axis = 0)# reshape it back to a matrixmean_img = mean_img.reshape(size)plt.imshow(mean_img, vmin=0, vmax=255, cmap=’Greys_r’)plt.title(f’Average {title}’)plt.axis(‘off’)plt.show()return mean_imgnormal_mean = find_mean_img(sample_normal_img, ‘Normal’)pnemonia_mean = find_mean_img(sample_pnemonia_img, ‘Pnemonia’)
从正常肺和肺炎肺的样本*均图像中,我们可以观察到,与健康肺相比,肺炎感染肺在胸部区域周围具有更高的阻塞。
作者图片
我们可以计算*均图像之间的差异,以查看正常肺和肺炎肺之间的任何明显差异
contrast_mean = pnemonia_mean — normal_meanplt.imshow(contrast_mean, cmap=’bwr’)plt.title(f’Difference Between Normal & Pneumonia Average’)plt.axis(‘off’)plt.show()
作者图片
在上面的图中,紫色区域显示了正常肺和感染肺之间的结构差异。
正如您所看到的差异,正常肺(左)有更多的紫色区域,表明阻塞较少,而感染肺(右)的紫色区域较少,表明疾病如何破坏肺结构的完整性。现在让我们在这个数据集上运行一些模型!
型号
具有 3 个卷积层的 CNN:
卷积神经网络是一类深度神经网络。它是图像识别和图像分类最常用的模型之一。它获取输入图像并将其分类到特定类别下。
我们的 CNN 模型从卷积层开始,从输入图像中提取特征。卷积将通过在特定的过滤器尺寸中学习来保持像素之间的关系。这将输出包含所有卷积要素的要素图。同时,我们在模型中引入了 ReLU 作为激活,这是为了保证我们的模型具有非线性。
在我们的卷积层之后,添加了一个 MaxPooling 层来减少参数的数量。我们将从卷积图层生成的输出要素地图中提取最大的元素。
在运行卷积层三次后,我们将添加一个展*层,将数据转换为一维数组,以输入到下一层,即密集层。
密集层基本上是一个完全连接的层,它描述了神经元如何连接到下一层。在每个密集层之后,我们还将添加一个丢弃层来丢弃输出的随机部分。这用于避免相邻层的部分之间的高度依赖性。
作者图片
由于数据的不*衡性质,用于衡量模型的最佳指标是 AUC 分数。
在用 20 个时期训练该 CNN 模型之后,我们得到 0.9214 的最高 auc 分数。
作者图片
CNN 用*衡数据:
现在我们决定试着让数据*衡,并测试这个 CNN 模型。我们基本上把里面有未知列的数据都拿出来了,大概是 96%的数据。
CNN 模型返回了高达 0.6567 的 AUC 分数。
作者图片
*衡 CNN 的结果比不*衡 CNN 差得多,我们可以想到的一个原因是深度学习网络需要大量数据才能表现良好,通过取出 96%的数据,它极大地影响了 CNN 的性能。
RESNET 50:
“深度残差学习在图像识别中的应用”,何,张,任,孙
RESNET 50 是来自 Tensorflow Keras 库的预训练模型,有 50 层。它通常用于计算机视觉中的图像分类。因此,我们决定首先使用 RESNET 50 来拍摄性能快照。
首先,我们需要找到一种*衡数据的方法,因为 96%的数据都是非 Covid 数据。通过以下增强,我们能够读入一幅图像并从中生成 9 幅图像。
作者图片
在我们将增强应用于数据之后,我们需要定义我们的输入形状,并从 Tensorflow Keras 库中获得预训练的模型。
INPUT_SHAPE = (255,255,3)base_model = tf.keras.applications.ResNet50(input_shape= INPUT_SHAPE,include_top=False,weights=’imagenet’)
通过 ResNet50 的一些基本设置,我们能够获得高达 0.9949 的 AUC 分数。
作者图片
最终模型对比
总之,我们首先尝试了一个具有三个卷积层的 CNN 模型,用原始和欠采样数据集进行训练。为了提高性能,我们引入了预训练的 ResNet 50 模型,并适合过采样数据集。在所有三种方法中,具有过采样数据集的 ResNet 50 给我们最高的 AUC 分数,而欠采样 CNN 给我们最低的 AUC。如下图所示,具有过采样数据的 ResNet 50 在所有四个测量中领先。
作者图片
结论
总的来说,这是一个非常有趣的项目,我们可以作为一个团队一起工作,同时学习深度学习中真正不可或缺的概念!我们能够学习和训练不同的模型来诊断新冠肺炎。除此之外,我们还深入了解了如何使用机器学习来帮助医疗工作者更快地诊断这些疾病!这可以节省他们的时间和宝贵的资源,在未来,当像新冠肺炎这样的流行病变得更加规范。总而言之,这个项目向我们展示了机器学习在医疗保健中的有效性,同时给了我们一个真正独特的项目来展示我们的工作!