TowardsDataScience-博客中文翻译-2021-三十四-
TowardsDataScience 博客中文翻译 2021(三十四)
通过简单的升级,显著提高数据库插入速度
创建速度惊人的 Python 数据库连接的 4 个级别
你用 fast_executemany 升级后的 python 脚本(图片由 NASA 在 Unsplash 上提供)
使用 Python 将数据上传到数据库很容易。将您的数据加载到 Pandas 数据框架中,并使用 dataframe.to_sql()方法。但是,您有没有注意到,在处理大型表时,插入会花费很多时间?我们没有时间无所事事,等待我们的查询完成!
只要稍加调整,你就可以更快地制作插页。比较棕色条(默认为 _sql()方法)和绿色条(我们的目标)的写时间。还要注意垂直轴在对数刻度上!在本文结束时,您将能够执行闪电般的数据库操作。准备好了吗?我们走吧!
目标和步骤
我们将分两步研究超快速插入方法。第一部分集中在如何正确连接到我们的数据库,第二部分将探索 4 种以升序快速插入数据的方法。
1.连接到我们的数据库
为了与任何数据库进行通信,您首先需要创建一个数据库引擎。这个引擎把你的 python 对象(比如熊猫数据帧)翻译成可以插入数据库的东西。为此,它需要知道如何访问您的数据库。这就是连接字符串的用途。我们将首先创建一个连接字符串,然后用它来创建我们的数据库引擎。
连接字符串可以看作是使用我们数据库的关键(图片由pix abayonPexels提供)
1.1 创建连接字符串
连接字符串包含有关数据库类型、odbc 驱动程序和我们访问数据库所需的数据库凭证的信息。
constring = "mssql+pyodbc://USERNAME:PASSWORD@DATABASESERVER_IP/DATABASENAME?driver=SQL+Server+Native+Client+11.0"
在上面的例子中,我们创建了一个连接到 Microsoft SQL Server 数据库的字符串。如您所见,我们指定了数据库凭证(用户名、密码、数据库服务器的 IP 和数据库服务器的名称),以及我们使用的驱动程序。连接字符串的格式因数据库而异,请查看connectionstrings.com以了解您的连接字符串应该是什么样子。
关于驱动程序:驱动程序需要与你正在使用的数据库版本相匹配。在上面的示例中,我们使用 MS SQL Server 2011,因此我们需要一个 SQL Server Native Client 11 驱动程序。这个驱动程序需要安装在运行 Python 脚本的机器上。通过搜索“ODBC 数据源”(在“驱动程序”选项卡下),检查安装了哪些驱动程序。如果您需要的驱动程序没有安装,您可以轻松下载(例如 https://www.microsoft.com/en-us/download/details.aspx?id=36434 。
1.2 使用我们的连接字符串创建数据库引擎
一旦连接字符串有效,就很容易创建数据库引擎。我通常创建我的如下。
import sqlalchemy
dbEngine = sqlalchemy.create_engine(constring, connect_args={'connect_timeout': 10}, echo=False)
将 echo 设置为 True 允许您查看执行的所有查询。如果您将 echo 设置为字符串“debug ”,结果行也会被打印出来。
1.3 测试我们的数据库引擎
使用下面的小脚本来测试您的连接。如果一切顺利,它应该打印引擎是有效的。如果出了问题,它会打印错误。
try:
with dbEngine.connect() as con:
con.execute("SELECT 1")
print('engine is valid')
except Exception as e:
print(f'Engine invalid: {str(e)}')
2.快速插入方法的四个级别
既然我们能够连接到我们的数据库,我们可以开始插入数据到数据库中。我们将探索 4 种插入数据的方式,以最快的方式结束。在本文的最后,您会发现 pandas 的 to_sql 方法的详细总结。
让我们比赛这些方法,看看哪一个赢了(图片由绝对魅力在像素上)
2.1 普通的 to_sql 方法
您可以在数据帧上调用此方法,并将其传递给数据库引擎。这是一个相当简单的方法,我们可以调整,以获得速度的每一滴。在下面的例子中,我们创建了一个数据帧并上传它。
import pandas as pd# 1\. Create a dataframe
df = pd.DataFrame({'numbers': [1, 2, 3], 'colors': ['red', 'white', 'blue']})
print(df.head())# dataframe looks like:
numbers colors
0 1 red
1 2 white
2 3 blue# 2\. Upload this dataframe
df.to_sql(con=dbEngine, schema="dbo", name="colortable", if_exists="replace", index=False)
如您所见,我们只需指定我们的连接(我们之前创建的数据库引擎),我们希望将新表和新表名放在哪个模式中。此外,如果指定的 schema.tablename 已经存在(在我们的例子中是 replace ),我们可以决定做什么,以及我们是否要在表上放一个索引(进一步查看完整的参数列表)。
上面的例子是插入数据最简单的方法,但也是最慢的。问题是我们一次写入整个数据帧,为每个记录创建一个 insert 语句。在小桌子上,比如我们的彩色桌子,这不是一个大问题,但在大桌子上,这肯定是一个大问题。
2.2 分块
如果我们增加一个 chunksize,我们的代码会运行得更好。这将按指定的块大小批量写入数据,从而节省大量内存。
df_large.to_sql(con=dbEngine, schema="dbo", name="largetable", if_exists="replace", index=False, chunksize=1000)
这个方法仍然为我们的表中的每个记录创建一个 insert 语句,这仍然非常慢。
2.3 多重插入
添加“multi”方法将大大提高插入速度。我们现在可以在一条语句中发送多行,而不是为每条记录编写一条 insert 语句。databast 可以在一个操作中处理多个记录,而不是每个记录一个操作。
重要说明:对于 Microsoft SQL Server 数据库,此方法不起作用,也不是必需的。请参见方法 4。
df_target.to_sql(con=dbEngine, schema="dbo", name="targettable", if_exists="replace", index=False, chunksize=1000, method='multi')
理想的块大小取决于表的尺寸。有很多列的表比只有 3 列的表需要更小的块大小。
对于许多数据库来说,这是写入数据库的最快方式。然而,对于微软服务器,仍然有一个更快的选择。
2.4 SQL Server 快速执行许多
SQLAlchemy 1.3 在为 SQL server 创建 dbEngine 时为我们提供了 fast_executemany 选项。此方法是将数据帧写入 SQL Server 数据库的最快方法。
dbEngine = sqlalchemy.create_engine(constring, fast_executemany=True, connect_args={'connect_timeout': 10}, echo=False) df_target.to_sql(con=dbEngine, schema="dbo", name="targettable", if_exists="replace", index=False, chunksize=1000)
在上面的代码中,你可以看到我们必须对数据库引擎进行一些调整;我们必须添加 fast_executemany 选项。就是这样。然后,当我们将数据帧写入数据库时,我们必须记住的唯一一件事就是我们不要指定我们的方法(或者设置方法=None)。这是因为默认情况下 fast_executemany 是多插入的。
fast_executemany 就像赛马中的 F1 赛车!(图片由上的和下的拍摄)
结论
通过这些简单的升级,您现在拥有了改进 python 到数据库连接的工具。我在下面包含了一个带有示例代码和步骤的总结。编码快乐!
迈克
页(page 的缩写)学生:比如我正在做的事情?跟我来!
快速插入一百万行到我们的数据库中
概述:to_sql 参数
下面是对 to_sql 函数所有设置的概述和解释。
- con (sqlalchemy 引擎)
数据库连接(sqlalchemy 引擎) - name (str) :必需的
要写入的表格的名称 - schema(str):默认值:默认数据库模式(dbo)
要写入的模式的名称。 - if _ exists(str):默认:' fail'
如果指定的表已经存在怎么办?- 'fail' :抛出错误
--【append’:将数据追加到指定的表格
-' replace’:将表格全部替换(警告:这很危险)
- 'fail' :抛出错误
- index(bool):默认值:True
如果 Index 设置为 True,它会创建一个名为“id”的额外列,该列被编入索引 - index_label (str 或 sequence): 默认:无
列标签为索引列。默认无。 - 【chunksize(int):默认:无
批量写入大小为【chunk size】的行。如果您有大量记录需要上传,这是一个好主意。这样可以节省内存。如果没有:一次写入所有行 - dtype (dict 或 scalar) :默认无
指定数据类型
如果指定了 scalar:在写入数据库之前,将此数据类型应用于 dataframe 中的所有列。要指定每列的数据类型,请提供一个字典,其中数据帧列名是键。这些值是 sqlalchemy 类型(例如,sqlalchemy。浮动等) - method(str):默认 None
控制 SQL 插入子句- None:使用标准的 SQL 插入子句(每行一个)
--“multi”:将多个值传递给单个插入子句。
- None:使用标准的 SQL 插入子句(每行一个)
显著改善您的探索性数据分析(EDA)
PYTHON。数据可视化。分析。数据科学
Python 探索性数据分析速成班
威廉·艾文在 Unsplash 上的照片
探索性数据分析(EDA)是探索数据并研究其结构以发现模式并从所述模式中发现异常的过程。
然后,EDA 将涉及使用统计和可视化方法来总结数据,以发现非数字模式。
理想情况下,EDA 应该从无法通过正式建模和假设测试获得的数据中获得洞察力和实现。
如果处理得当,EDA 可以极大地简化或推进您的数据科学问题,甚至可能解决它!
EDA 过程的目标
一个合适的 EDA 希望实现几个目标:
- 质疑数据并确定数据集中是否存在固有问题;
- 确定手头的数据是否足以回答特定的研究问题,或者是否需要额外的特征工程;
- 建立一个回答研究问题的框架;
- 并根据你对数据的了解提炼问题和/或研究问题。
可以看出,EDA 本身就是一个迭代过程。
与大多数人认为的不同,EDA 不是一个“正式过程”。事实上,显而易见,任何带有“探索性”一词的东西都不受严格规则的约束,因此是非正规的。
相反,EDA 是自由形式的。正是在这个阶段,你开始追逐你可能有的想法,并看看哪些会有所进展。其中一些想法可能不一定行得通,因此你可以开始追求或从其他可行的想法中分支。
因此,EDA 让你批判性地思考你的问题有多“可回答”,以及它是否符合你的期望。
EDA 备忘单
探索性数据分析一般分为两种方式:图形化或非图形化和单变量或多变量。
一些参考文献认为,在使用图形方法之前,应该先使用非图形方法。论点是,使用非图形方法可以确保您熟悉数据集,这使得可视化更容易,例如哪些变量是定量和定性的。
EDA 之前
虽然有些资料来源认为这是 EDA 过程的一部分,但有些资料来源认为这是数据预处理,是有效 EDA 的一个要求。我们用后者吧。
在实施这四(4)个类别之前,数据科学家应该完成以下工作:
- 确定数据集维度 —相对于特征(列)的数量,有多少个观测值。当特征的数量超过观察的数量时,一些算法不能产生期望的输出,如在优化问题中。
- 识别数据类型 —特别是对于 Python,EDA 方法,尤其是图形方法,最适合某些数据类型。虽然数据的形式可能看起来相似,例如,对于数字列,如果数据类型被分类为“object”与“float”或“int ”,一些方法将不起作用。
- 识别目标或输出变量——在一些图形方法中,识别目标或输出变量以针对单个变量进行可视化是很重要的。
实现这一点的一个好方法是使用。info() 熊猫的方法:
它同样为您提供“非空”信息,这是单变量非图形方法的一部分。
单变量非图解法
对于单变量非图形化 EDA,我们想知道的取值范围和每个值的频率。
对于定量变量,我们要关注位置(均值,中位数),扩散( IQR,标准差。偏差、范围、模态(模式)、形状(偏斜度、峰度)和异常值。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns#Aesthetics
sns.set_style("darkgrid")%matplotlib inline# Loading built-in Datasets:
tips = sns.load_dataset('tips')#Quantitative
tips.total_bill.describe()
Pandas 有一系列方法来提供我们想要知道的其他信息,如模式、和峰度。
对于 IQR,我们需要做一些计算:
#IQR
#calculate interquartile range
q3, q1 = np.percentile(tips.total_bill, [75 ,25])
iqr = q3 - q1#display interquartile range
iqr
pandas 的 describe 方法最常用于数字列,但是通过添加参数“include=['O']”,我们可以将它用于分类值。
tips.sex.describe(include=['O'])
单变量图解法
我最喜欢使用的图表之一是直方图,因为它为我们提供了数据分布的概念。
在这方面,数据分布的*似值要求它具有定量的性质。
sns.histplot(data=iris, x="sepal_length")
密度图是直方图的平滑版本:
sns.kdeplot(data=iris, x="sepal_length")
对于分类变量,条形图非常有用:
sns.barplot(x="day", y='tip', data=tips)
请注意,对于条形图,惯例是在 y 轴上表示目标/输出/因变量。
对于 seaborn,y 轴代表“平均”值。一些来源建议我们表示“计数”或观察值的数量,但这与单独的直方图没有任何不同,在直方图中,您用想要计数的分类变量标记 x 轴:
sns.histplot(data=tips, x="day")
显示分类变量“日”计数的直方图代码。从技术上讲,这是“日”类别的单变量条形图。
对于数字数据来说,最有用的可视化方法之一是箱线图。
sns.boxplot(y=tips["total_bill"])
可以通过填充“x”参数而不是“y”参数来水平表示箱线图。
箱形图(又名箱形图和须状图)在表示关于中心趋势、对称性、偏斜和异常值的信息方面非常出色。他们唯一可能缺少的是关于多功能的信息。
垂直箱线图的详细信息
下须和上须被拉出到最极端的数据点,这些数据点超出相应铰链不到 1.5 IQRs。所以胡须代表数据 的最大值和最小值,不包括异常值*。*胡须后的圆点代表这些异常值。
对称性可以通过中线的位置来评估。如果它以晶须长度相等的方式切割盒子,那么数据分布是对称的。在倾斜数据集的情况下,中值被推向较短的须。更长的须意味着高(或极端)值出现在这个方向。较长的上须意味着数据集正偏(右偏),而较长的下须意味着数据集负偏(左偏)。
对于峰度,许多异常值的存在可能表明一两件事情:
- 出现厚尾(或正峰度)或
- 数据输入错误。
如果数据集很大,短须的存在可能表明负峰度。
最后,但同样重要的是,正态分位数图可用于检测左右偏斜、正负峰度和双峰。
为此, statmodels 软件包可以帮助我们:
import statsmodels.api as smsm.qqplot(iris.sepal_length)
正态分位数图绘制两种分布的概率,特别是它们的分位数。如果两个分布相似,这些点将在 y=x 线上返回一条完美的直线。在上面的例子中,虹膜的概率分布与正态分布进行了比较。
对于其他可视化,我们可以将它们视为“多变量”情况的扩展。
多元非图形
对于一个分类变量和一个定量变量,理想情况下,我们希望针对分类变量“ ”的每个级别,分别呈现定量变量的“ 标准单变量非图形统计。下面是一个例子:
#Separate the categories into a list and the quantitative variables in another list:
category = ['sex', 'smoker', 'day', 'time']
quantitative = ['total_bill', 'tip', 'size']for i in category:
for j in quantitative:
print(f'--------------{i} vs {j} ---------------')
display(tips.groupby(i)[j].describe().reset_index())
对于定量变量,以下方法可用于生成相关性、协方差和描述性统计数据:
tips.cov()
tips.corr()
tips.describe()
对于缺失数据检测,我们可以使用。info() 方法如前所述。
多元图形
对于两个分类变量,最常用的 EDA 图是分组条形图。与常规条形图一样,目标/输出/独立变量必须用 y 轴表示:
sns.catplot(
data=tips, kind="bar",
x="day", y="tip", hue="sex",
ci="sd", palette="dark", alpha=.6, height=6
)
如果箱线图是单变量分析中最有用的 EDA 图之一,那么它的多变量对应物也是如此。并排箱线图用于分析一个定量和分类变量:
sns.boxplot(x="day", y="total_bill", data=tips)
对于两个定量变量,散点图将是理想的。在某些情况下,它可以很容易地揭示一些模式,如图中两个定量变量之间是否存在线性、多项式(偶数指数)或其他关系。
sns.scatterplot(data=tips, x="total_bill", y="tip")
seaborn 的 pairplot 方法提供了任意两个定量变量排列之间散点图的可能组合。作为奖励,它在单个定量变量的主对角线上添加了直方图:
sns.pairplot(tips)
您可能想更进一步,在 pairplot 中添加类别:
sns.pairplot(tips, hue='sex')
对于缺失数据,可能很难完全掌握多个变量的缺失信息。幸运的是,Python 中的一个包可以帮助我们轻松地将这一点可视化。
pip install missingnoimport missingno as msno #A simple library to view completeness of data
试用我从菲律宾家庭收入和支出调查中获得的数据集:
msno.matrix(df, labels=True) #labels=True is important if the number of columns exceed 50
使用 missingno,您可以很容易地看到每个功能有多完整,并且如果该功能对您的研究设计至关重要,您可以评估如何进行。
相关性虽然主要用于非图形方法,但同样可以被可视化。当我们有许多难以注意到多重相关性的特征时,这很有帮助。
对于这些情况,我们可以利用热图可视化:
fig = plt.figure(figsize=(18,14))corr = tips.corr() #.corr gets the pairwise correlation
c = plt.pcolor(corr)fig.colorbar(c) #displays the range used in the colorbar
结束语
虽然我们试图制作我们的工具和方法,但我们拥有的列表并不完整。
例如,地理空间和时间特征需要不同的 EDA 原理和过程,并且应该在必要时进行探索。
新的 EDA 技术正在有规律地发展。应该鼓励数据科学家学*它们,并了解从这些新技术中获得的见解是否无法被我们已经介绍过的一系列技术所涵盖。
无论如何,数据科学家不应该害怕添加和探索更多内容,因为 EDA 的目的是熟悉您的数据并推进手头的研究问题。
代码可以在我的 GitHub 页面找到。
参考
https://r4ds.had.co.nz/exploratory-data-analysis.html
https://www.stat.cmu.edu/~hseltman/309/Book/chapter4.pdf
Ozdemir,s .,2016,《数据科学原理》,伯明翰:Packt 出版社。
给我画一幅画——卑微的线条图
快速介绍如何构建信息丰富且(相对)美观的线图。
背景
线图是你在实践中遇到的最简单的图表之一,但这并不意味着它不是可视化信息的强大工具!
我在过去遇到过一些资源(包括一个关于专业沟通的精算考试,听起来很有趣),它们试图展示线形图的最佳实践。不幸的是,就像许多理论和现实的交叉点一样,这些指导方针并不总是适用的。
现在,我喜欢线条图,我甚至可以说这是我最喜欢创造的一种图。书呆子,我知道。
我已经学会了一些技巧,我将在下面分享。
什么时候用?
线形图最适用于以下情况:
- 当绘制的数量(即显示在 y 轴上的数量)为数字时。
- 当图表的目标是传达某个特征(如时间)的变化或趋势时。
- 当用于区分目标量的特征(即显示在 x 轴上的内容)不太粒度化,或者可以转换为不太粒度化时。
- 当用于区分目标数量的特征具有某种排序或进展时(理想而非强制!).
让我们开始吧!
这是足够的理论,现在是采取行动的时候了。
我们将从一个真实的情节示例开始,讨论我们不喜欢它的地方,并构建我们的代码,直到我们有一个很好的可视化。
免责声明:所有剧情均在 matplotlib
Python 库中创建。是的,同样的图可以使用 seaborn
的简单界面创建。不,这并不意味着你不需要了解 matplotlib
的基础知识。
免责声明(2):我是精算师,不是平面设计师……当你看到我的颜色选择时,请记住这一点!
免责声明(3):虽然我会分享我的代码,但我不会涉及太多的技术细节。如果你对 matplotlib
、中所有可用的图表潜力感到好奇,看看官方文档:Matplotlib . py plot . plot—Matplotlib 3 . 4 . 3 文档
基地地块
让我们绘制一个简单的股价线图和一个平滑版本的股价图。
这是一段简单的代码:
它产生了一个简单的线图:
作者图片
这相当简单——两行代码为您提供了一个基本的情节。如果你不介意显示对象的内存地址,你甚至可以在一行中完成!
好吧,但是这张图表看起来不太顺眼,也没有真正地讲述一个故事。
- 它很小——我们应该让它容易看到。
- 很难说这是在测量什么——我们应该让它容易理解。
- 很难从图表中读出数值,也很难理解它所代表的东西——我们应该把它弄清楚。
- 我不喜欢这些颜色——我们应该把它弄得好看些。
- 它不一定传达我们想要传达的确切信息——我们应该让它讲述一个故事。
让我们解决这个问题,一步一步来!
使其易于查看:更改图表大小
有许多方法可以做到这一点。
我更喜欢像许多其他 Python 用户一样使用fig, ax = plt.subplots()
方法。这给了我们很多灵活性,尤其是当我们使用seaborn
的时候。
plt.subplots()
接受一个figsize
参数,它允许我们设置图表的大小。我喜欢我的图表是长方形的,宽于高,发现这样更容易在屏幕上看到。
第一条建议:试试这个,看看有没有让你喜欢的尺码范围。
结果是:
作者图片
好多了!
使其易于理解:添加标签和标题
我总是惊讶地看到,当一个图表有适当的轴标签和标题时,它能传达多少更多的信息。
使用set_xlabel()
、set_ylabel()
、set_title()
方法很容易做到这些。
让我们来看看:
作者图片
相对较小的变化,但我们现在可以很容易地看到:
- 我们试图传达的内容(即“每日收盘股价(美元):2010–2013”)。
- 我们在 y 轴上测量的单位(即“收盘股价(美元)”)。
- 我们在 x 轴上区分的内容(即“日期”)。
我们的图表开始看起来相当不错,但是仍然很难从图表中读出数值,更不用说很难理解这些线条的确切含义了!让我们解决这个问题。
明确说明:添加网格线和图例
对我来说,在图表上不包括网格线是一种罪过。强烈的言辞,但是它让我不必在我的电脑屏幕上使用物理标尺(是的,我实际上已经这样做了)。
同样地,任何有一个以上数量被可视化的图表绝对需要一个图例。了解绘制的是什么以及每个量是如何表示的是至关重要的。
使用grid()
和legend()
方法添加网格线和图例非常容易——让我们看看它们的实际应用。
提示 2:网格线不应该分散观察者对实际绘制内容的注意力。使用 alpha
参数淡化网格线。
提示 3:图例可以放在图表内部和外部的不同位置。查看 loc
和 bbox_to_anchor
参数。
作者图片
现在我们有进展了!让我们做一些美学上的改变——我不是基础主题的粉丝。
让它看起来更好:应用一些样式
虽然我们的图表开始看起来相当不错,但它可以说是相当平淡无奇,并没有真正将自己与周围浮动的许多其他图表区分开来。
在你的图表上打上你个人或者公司的印记是个好主意。我们现在就开始吧。
许多公司都有一个风格指南,规定了字体风格和图形调色板。利用这一点来创造更突出的视觉效果。
让我们想象一下,我工作的公司使用深绿色主题。我们怎样才能把这一点融入情节中呢?
- 我将用深绿色标出公司的股价——这是一个很好的链接。
- 平滑后的股价目前用橙色标出。我不是深绿色和橙色组合的粉丝,所以我会把这个改成在绿色的映衬下显得很突出的;红色通常会起作用。
- 我不喜欢用实线显示推断值或预测值。我觉得这传达了这些是具体的事实,但在大多数情况下并不是真的如此。为此,我将使用虚线绘制平滑后的股价。
作者图片
不算太寒酸!需要注意一些事情:
- 我使用了
linewidth
参数来控制每条线的粗细。这是一个很好的主意,这取决于你到底想强调什么。 linestyle
参数控制绘制的线条类型。有多个选项可供选择——查看文档了解更多信息。
到目前为止,一切顺利。但是我们仍然有一个问题,这张图表没有真正传达我们想要传达的信息。让我们纠正这一点。
让它讲述一个故事:使用图表标题、附加图形和图表文本
我们会做到这一点——准备好一大段代码:
这给了我们:
作者图片
我们做了什么?
- 我们使用图表标题来明确传达我们想要传达的信息。
- 彩色方框将观众的注意力吸引到关注的特定区域(用
axvspan()
来做)。 - 适当颜色(和位置)的文本信息为阴影区域提供了更好的背景(用
text()
做这件事)。
包扎
…还有点漫无边际。
折线图是显示数字趋势或跨一些显著特征的关系的一个很好的工具。在matplotlib
制作它们很简单,有大量的定制选项。
我们已经看到了一个坏图表以及如何纠正错误的好例子。特别是,我们改变了:
- 图表大小
- 标签和标题
- 传说
- 颜色和主题。
我们简单地谈到了如何明确地向观众传达信息。我们通过有效地使用图表标题以及向图表添加额外的图形和文本信息来做到这一点。我强烈推荐这一点,特别是如果你的观众不熟悉手边的主题,或者如果趋势或关系没有立即显现出来。
正如我提到的,我喜欢好的折线图。创建好的线图需要练*,我鼓励你尝试一些不同的东西,并一直问“这张图表传达了信息吗?”在内心深处。
如何利用谷歌趋势来洞察人们的兴趣
数据、数据分析、兴趣和谷歌
通过 Google Trends,Google 开发了一个数据分析包来了解全球和本地人们的兴趣。这个故事使用了赛车和流行运动的例子。
赛车。蒂姆·凯里在 Unsplash 上的照片
“信息是 21 世纪的石油,分析是内燃机。”彼得·桑德加德
G oogle 为数十亿网页编制索引。反过来,广受欢迎的谷歌搜索允许人们根据关键词和操作符找到许多这样的页面。此外,谷歌存储搜索数据,并通过其谷歌趋势网站提供汇总数据。这些数据可以被视为可视化。用户还可以下载数据,以便在 Tableau、Microsoft Power BI 或其他工具中进一步使用和分析。
这个故事描述了几个使用谷歌趋势数据的例子。也许你会为谷歌趋势找到一个商业或个人目的。
Google Trends 的使用示例
印第安纳波利斯 500 汽车赛
数据审核
印第安纳波利斯 500 是一场 500 英里的汽车比赛,自 1911 年以来一直在印第安纳波利斯的印第安纳波利斯赛道举行。早些年,比赛在装饰日举行,这个节日现在美国人称为阵亡将士纪念日。现在仍定在每年 5 月下旬的阵亡将士纪念日周末。因此,我对在谷歌趋势中搜索这场比赛可能会把我引向何方很感兴趣。在全球范围内搜索“印第安纳波利斯 500 强”主题,会发现一些不足为奇的数据点:
- 搜索兴趣在 2021 年 5 月 30 日至 6 月 5 日达到峰值。实际上,比赛在 2021 年 5 月 30 日举行。
- 在美国,搜索兴趣很高,比赛在印第安纳州的印第安纳波利斯举行。
- 美国的朋友和邻居加拿大对搜索的兴趣也很高。
印第安纳波利斯 500 全球兴趣。作者在谷歌趋势中的截屏。
其他数据点也很突出:
- 荷兰、西班牙和新西兰的搜索兴趣很高。
- 2020 年 8 月 23 日至 8 月 29 日搜索兴趣高。
这些数据点意味着什么?我并不关注赛车或 Indy 500,但我猜荷兰、西班牙和新西兰的人对这场比赛感兴趣。也许比赛中的车手来自那些国家。以下是我在 Indy Motor Speedway 网站上找到的关于这些国家车手的信息:
- 荷兰 —埃德·卡彭特赛车队的里努斯·韦凯
- 西班牙——奇普·加纳西赛车队的亚历克斯·帕劳
- 纽西兰——奇普·加纳西赛车队的斯科特·狄克逊
- 新西兰 —潘世奇队的斯科特·麦克劳克林
但是现场还包括两位来自加拿大的车手:
- AJ Foyt 赛车队的道尔顿·凯利特
- 安德雷蒂汽车运动公司的詹姆斯·欣奇克利夫
其他国家的车手也参加了比赛。然而,“印第安纳波利斯 500”并没有在他们的搜索中占据很高的位置。
但是是什么引发了 2020 年 8 月 23 日到 8 月 29 日的搜索高峰呢?我不知道,但我猜想在那段时间的某个时候,在印第安纳波利斯赛车场会举行另一场比赛。一项小小的研究表明,2020 年的印第安纳波利斯 500 因为新冠肺炎疫情而从 5 月的阵亡将士纪念日周末推迟到 8 月 23 日。
我没有解决一个商业问题或支持决策来搜索 Indy 500 汽车比赛。但是我学到了一些关于比赛和赛车的新事实或琐事。车手从世界各地被吸引来参加比赛,这已经成为一个国际事件。
潜在商业用途
以下是如何在商业中使用 Indy 500 搜索数据的想法:
- 从其他国家招募车队和车手,以扩大赛事对更多车迷的影响。
- 计划设计、生产、分配和销售与种族相关的商品,如 t 恤衫和纪念品。
- 促进和准备车迷从地区到比赛地点的旅行,可能包括机票、酒店、汽车租赁、豪华轿车服务等。
"我从不猜测,这是一个令人震惊的*惯——对逻辑能力有破坏性."[人名]夏洛克·福尔摩斯(英国侦探小说家亚瑟·柯南·道尔所塑造的小说人物)
比较印第安纳波利斯 500 和世界范围内以及美国的其他比赛
数据审核
让我们在谷歌趋势搜索中添加另外三个著名的汽车比赛:代托纳 500、摩纳哥大奖赛和勒芒 24 小时耐力赛。我们可以立即看到,在比赛周期间,人们对每项赛事的兴趣都有所增加:
- 印第安纳波利斯 500—2020 年 8 月 23 日至 29 日,比赛将于 8 月 23 日举行
- 代托纳 500—2021 年 2 月 14 日至 20 日,比赛于 2 月 15 日举行
- 摩纳哥大奖赛—2021 年 5 月 23 日至 29 日,比赛于 5 月 23 日举行
- 勒芒 24 小时耐力赛—2020 年 9 月 13 日至 19 日,比赛将在 9 月 19 日和 20 日举行
从按地区(或者,在本例中,按国家)显示兴趣水平的图表中,我们看到美国和加拿大对 Daytona 500 和 Indianapolis 500 表现出较高的兴趣水平。相比之下,大多数人对比赛的兴趣很低。事实上,在美国,这两个美国种族共有 41%的兴趣水平。同样,美国和加拿大以外的人对 Daytona 500 的兴趣也在增加,而对 Indy 500 的兴趣仍然很高。摩纳哥大奖赛和勒芒 24 小时耐力赛在许多领域表现出更高的兴趣。这种兴趣水平可能是由比赛的地点(例如,勒芒在法国)和车手的祖国等因素造成的。但这里就不赘述了。
世界著名的汽车比赛。作者在谷歌趋势中的截屏。
潜在的商业用途
企业可以以类似于上面印第安纳波利斯 500 强中描述的方式使用数据。以下是其他想法:
- 开发一项服务,为高收入车迷提供赛车旅行套餐(全包交通、住宿、餐饮、门票等)。
- 为汽车性能产品做广告,在比赛前可能会有以赛车手为主角的广告。
季节性运动装备广告
数据回顾
人们感兴趣的运动在世界各地各不相同。在本练*中,我们来回顾一下这些运动的搜索趋势:
- 棒球
- 篮球
- 冰球
- 垒球
- 网球
足球一词被排除在外,因为它在不同的国家意味着不同的运动。
全世界流行的体育兴趣。作者在谷歌趋势中的截屏。
从过去一年的全球数据中可以得出什么结论?以下是一些例子:
- 篮球在全球范围内引起了更高的兴趣。紧随其后的是棒球和网球,两者几乎不相上下。
- 如以下加拿大的细分数据所示,冰球是人们感兴趣的主要运动。
- 对网球的兴趣在南半球和西欧占主导地位,除了篮球占主导地位的西班牙。在葡萄牙,人们对篮球和网球的兴趣相当。
- 线形图显示了一些季节性,尤其是篮球。但是南北半球的季节性可能不同。
加拿大的大众体育兴趣。作者在谷歌趋势中的截屏。
潜在商业用途
组织和公司可以将体育兴趣数据用于各种目的。以下是一些例子:
- 通过向选定的世界地区进行营销,试图增加这些地区对体育参与或特定体育赛事的兴趣(例如,特定体育项目的温布尔登网球锦标赛或世界锦标赛)。
- 执行更详细的搜索和分析,以确定生产、分销和销售与特定运动相关的商品的最佳时间和地点。
摘要
谷歌趋势是一个强大的免费工具,你可以用它来了解和洞察世界各地人们的兴趣。谷歌趋势易于使用,并可能为您的组织或个人生活提供有用的信息。
请关注即将发布的关于从 Google Trends 下载数据以扩展 Tableau 功能的报道。
绘制高斯分类器的后验概率面
机器学*中的必备知识
高斯分类器,探索协方差和先验对后验概率面的影响
作者图片
简介
正态分布的数据在自然界中随处可见。例如,在人类中,我们的身高、智商和出生体重形成了美丽的钟形曲线。如果已知精确的分布参数,高斯分类器可以完美地估计类成员的概率。这使得高斯分类器成为一个非常强大的工具。
在这篇文章中,我会多次提到后验概率。它指示分类问题的解决方案,并且是包含空间中的数据点将属于一个类别的概率的表面。更多信息可以在我的贝叶斯定理文章中找到。
高斯分布数据
多元高斯分布由两个参数参数化。在一维高斯分布中,这些是标准偏差和平均值。在多元高斯模型中,这些是协方差矩阵(𝛴)和先验矩阵(μ)。在本文中,我分析了当改变这些参数时,后验概率会发生什么变化。
来自贝叶斯定理
后验概率由贝叶斯定理给出,是似然概率和先验概率的函数。如果这些是正态分布,我们除以分子,我们发现后验概率面实际上是 s 形的。
许多人会从神经网络中使用的激活函数中识别出 sigmoid 函数。这里它仅仅代表后表面的形状。
我们可以使用双变量高斯公式扩展上述等式中的“a”项。
所得方程是 x 上的二次方程,但是,我们注意到,当协方差矩阵相同时(𝛴1 和𝛴2),高阶项相互抵消。结果是 x 上的线性方程。
相同协方差
从上面,我们可以得出结论如果协方差矩阵相同,后验概率轮廓是线性的。这是一个极其重要的结果,因为它意味着这个问题的最优解是一个线性分类器(我在另一篇文章中证明了这等同于正则化最小二乘分类器)。
让我们想象一下这是什么意思。
作者图片
上面是后验概率曲线。正如你所看到的,从侧面看,它有一个 S 形的形状(就像一个 S 形)。底部是两个二元高斯分布的数据聚类。每个数据点属于每个类别的概率由后验曲面给出。
作者图片
如果我们从顶部看,画出后表面的轮廓,我们可以看到这些是线性的。
不同协方差
如果协方差矩阵不同,sigmoid 函数中的高阶项不会抵消,因此后验概率轮廓将不是线性的。
作者图片
先验对后验的影响
我们已经看到了协方差对后验概率的影响。让我们看看当先验改变时会发生什么。
当查看后验的 sigmoid 中的指数项时,我们可以看到两个分布的先验充当指数项的标量。当缩放 sigmoid 函数中的指数时,表面仅向最高的前一类移动。
作者图片
结论
当处理多元高斯分类问题时,协方差和先验可以改变。如果分布具有相同的协方差,后验概率轮廓是线性的,因此最优决策边界将是线性的。当先验改变时,后验的形状不变,只是在空间上向具有更高先验的类移动。
支持我👏
希望这对你有所帮助,如果你喜欢它,你可以跟随我!
您也可以成为 中级会员 使用我的推荐链接,访问我的所有文章以及更多:https://diegounzuetaruedas.medium.com/membership
你可能喜欢的其他文章
机器学*中的漂移
为什么很难,该怎么做
合著者 Shayak Sen
照片由格雷格·波尔、米恰斯·维奇日斯基在 Unsplash 拍摄
新冠肺炎疫情引发了人们对机器学*中数据漂移的浓厚兴趣。漂移是一个关键问题,因为机器学*通常依赖于一个关键假设:过去==未来。在现实世界中,这种情况很少发生。因此,无论是在部署模型之前还是在部署期间,了解数据的变化将如何影响模型的行为都是至关重要的。例如,在新冠肺炎疫情期间,由于数据和环境的巨大变化,许多行业的模型都在未知领域运行。
作者图片
在疫情期间,数据科学家看到了如上图所示的模式:模型输出在特定方向上发生了重大转折。并试图回答一些问题:
- 数据中是什么导致了这种转变?
- 这对我的模型质量有什么影响?
- 我需要以任何方式改变我的模型吗?
在地面真实数据不是即时可用的情况下,这个问题变得更加严重,使得在为时已晚之前很难测量模型的准确性。在欺诈、信用风险、营销等情况下尤其如此,在这些情况下,预测的结果只能以一定的滞后量来衡量。
虽然再培训被视为灵丹妙药,但在实践中,如果对根本原因和后果没有足够的了解,再培训可能会导致业绩不佳。在本文中,我们将探讨漂移影响的评估和再培训的细微差别,以帮助数据科学家做好准备,在充满挑战的环境中解决漂移问题。我们首先概述标准漂移术语和方法,以及检测漂移及其后果的困难。然后,我们讨论了理解漂移的本质和根本原因如何在实践中导致有效的缓解。
漂移概述
保罗·梅尔基在 Unsplash 上拍摄的照片
漂移为什么会发生?
模型应用的世界和环境可能会不断变化。应用和训练模型的数据可能存在显著差异的一些方式示例如下:
- 外部世界已经发生了变化:这些可能是外部事件,如疫情和利率变动,也可能是更多的内部事件,如导致数据质量问题的模型管道故障
- 该模型被应用于新的上下文:例如,在维基百科数据上训练的语言模型然后被应用于新闻文章。
- 训练数据首先来自不同的集合:这可能是由于样本选择偏差。有时这些偏见是不可避免的。例如,对于贷款申请,ground truth 仅适用于获得贷款的个人。
漂移有哪些不同的种类?
当谈到漂移时,可能会出现许多不同的密切相关的术语:协变量漂移、概念漂移、数据漂移、模型漂移、模型衰减、数据集漂移、分布漂移。这些术语指的是关于变化的不同假设。
- 协变量移位:输入特征的分布发生变化。
- 概念漂移:目标变量和输入特征之间的关系发生变化。
- 模型衰减:漂移导致模型性能下降。
- 数据漂移:任何分布变化。这有时被用来具体指代协变量移位。
- 其他术语(分布偏移、数据集偏移)通常可以指上述任何概念或它们的组合。
识别漂移
确定偏差的标准方法是测量模型性能(准确性或其他方面)或检查或测量以下培训和部署分布的差异:
- 预测产量,
- 地面真相,
- 单个输入特征,或
- 联合输入特征。
这些方法都提供了有价值的漂移信号,应该尽可能使用。然而,在实践中,它们面临着严峻的挑战,使得测量漂移成为一个难题。
维数灾难— 随着数据中维数特征数量的增加,区分来自两种分布的样本变得越来越困难。这使得测量多个特征之间的联合漂移特别困难,并且可能影响具有多个输出的多类预测或模型中的漂移测量。诅咒也是为什么直接检查概念漂移(所有输入和输出特征的联合分布的属性)可能不切实际。然而,由于下一个挑战,这可能没有实际意义。
缺乏地面实况— 部署中的模型性能测试可能足以确定漂移是否足够大,以保证干预。不幸的是,部署中的实际情况可能不会立即可用。例如,在信贷决策中,一笔贷款是否会导致违约,可能要到贷款期过去几个月或几年后才能知道。在这种情况下,概念漂移不能与其他形式的数据漂移分开独立确定。
无关紧要的漂移 —通常很难识别模型中漂移问题的影响。通常,特征的较大变化可能无关紧要,因为它以不影响模型行为的方式改变。再加上总会有某种程度的漂移这一事实,很难判断漂移问题是否值得解决。
这些挑战意味着,无论在实践中采用何种检测漂移的手段,它都可能是实际漂移和实际后果的不精确指标。因此,重要的是要注意解决漂移问题的方法的成本。
对于漂移我们能做些什么?
照片由 Unsplash 上的zdenk Macháek拍摄
在测量了特征、预测和/或基本事实之间的各种形式的漂移后,我们可以考虑对此做些什么。人们必须问的第一个问题是漂移的后果或根本原因是什么。也就是说,发现漂移的后果是否足以证明潜在的代价高昂的行动?如果是这样,推动漂移的关键因素是什么。当退化可以直接测量时,这当然更简单,但正如我们所指出的,在具有挑战性的情况下,漂移对性能的影响无法确定或只能估计。
预测漂移— 发现共同重要的特征也是一个维数灾难问题,并且在几个重要特征之间共同测量漂移也是如此。然而,关注预测输出分布中的漂移似乎是合理的,假设这是一个显著降低维度的概念。重要输入之间的漂移,无论是联合的还是独立的,都应该反映在输出的漂移中。
根本原因分析——一旦检测到漂移,这种模型得分偏移,或者准确度下降。重要的是要理解根本原因是什么:哪种特性导致了漂移。测量特征重要性的技术在测量根本原因时是非常宝贵的;重要特性的漂移比不太重要的特性的漂移更有可能导致性能下降。然而,标准特征重要性度量需要针对漂移进行调整,而不仅仅是预测重要性。导致漂移的特征可能与最重要的模型特征不同。
估计准确性— 测量漂移的一个圣杯功能是能够将数据中的变化与模型性能联系起来。这是 TruEra 平台基于我们 ML 团队的独特研究提供的一项功能。关键的想法是汇总相应特征的变化,以了解它可能如何影响模型性能。
缓解措施
米切尔·奥尔在 Unsplash 上拍摄的照片
使用额外标签数据进行再训练通常被吹捧为解决数据漂移的标准答案,但根据成本或我们对漂移负面后果的确定性,这可能并不合适。根本原因驱动的理解漂移的方法可以导致更精确的缓解策略。修复数据质量问题
漂移的根本原因通常是部分数据管道被破坏。进行根本原因分析的能力可以快速查明有问题的特性。这些问题可能从意外的丢失值到数据类型不匹配,在大型堆栈中很难找到这些问题。
移除特征,保留模型— 处理作为漂移根本原因的特征的一个选项是简单地将它们从模型的考虑中移除,即,用训练均值或模式替换它们。这不需要收集额外的标记数据或重新训练,但可能会导致一些性能损失。然而,如果这种损失低于因漂移而导致的预期退化,则移除可能是最具成本效益的选择。
移除特征,重新训练— 在某些情况下,可能会移除漂移特征,并希望重新训练使用替代特征来实现类似的预测能力。但是,请注意,如果备选特征是因果相关的,这将无济于事,备选特征也可能漂移。
向新数据添加通用功能,重新训练—
随着时间的推移,美国零售支出在两个方面呈现出周期性和独特的趋势。(百万美元;左轴为红色,右轴为蓝色)。数据来源:census.gov。图片作者。
漂移可能是可预测的,也可能是周期性的(例如上图中的酒类零售支出)。将您的训练数据收集周期跨越更长的周期和出现周期性循环的周期,并且包括指示时间或周期(即,季节、星期几等)的特征,可以产生能够对时间本身的变化做出响应的模型。然而,由于过度延伸的维数灾难,这是有代价的:假设额外的周期特征是必然的,那么模型学*其目标概念与原始特征的关系以及额外的周期特征所需的数据量就会增加。此外,需要更加注意确保数据代表相关的时间段。
添加罕见事件的缓解者,重新训练—
隐藏的背景:疫情和突发事件通过失业率对目标贷款违约产生影响。虽然在训练数据中列举所有罕见且有影响的事件是不切实际的,但它们的影响可以通过更广泛可用的变量来减轻。作者图片
虽然一些漂移指标可能很清楚,并不难纳入培训管道,但“未知的未知”必然会不时出现。例如,全局疫情的指示符可能看起来像是许多建模任务的相关特征,包含这样的指示符很可能是一个失败的原因。大撞击事件是独特且不可预测的。这里,特征工程可以受益于基本任务的因果分析,因为可以获得更广泛可用的漂移指标。例如,虽然全球疫情的存在可能是某些类型的贷款偿还减少的根本原因,但中间原因如(失业)就业率也可能起到同样的作用,并且可以作为中介覆盖其他难以计划的意外事件。因此,在训练样本时纳入本地或全球经济指标,可以帮助信贷决策模型对其获得的概念漂移做出响应。
失业率作为疫情和贷款决策之间的缓和因素。图片由美国劳工统计局提供。
向现有数据添加特征,重新训练— 另一个成本较低的选择是将额外的特征纳入训练数据集中。如果可以对现有的训练数据集做到这一点,就不必支付标签成本。如上所述,添加指示漂移的特征将要求现有数据跨越足够的时间段。
不管是否需要新的标记数据,漂移指示特征的使用有效地将概念漂移的问题转化为协变量移位的问题。
重新加权数据,重新训练— 理论上,输入和标签之间的分布可能代表一个概念,该概念不会因训练时间和部署时间而变化。在实践中,这并不意味着在特定环境或月相下的特定训练算法将从其可用的数据样本中学*它。这种差异的一个原因是训练数据在某些区域没有足够的代表性,因此训练不能提供足够的模型容量。如果部署数据在该区域变得密集(即漂移),性能可能会受到影响。假设在该区域中至少有一些训练样本,增加它们的权重可以用于帮助随后的再训练在重要的区域中投入更多的模型容量。
总结
漂移是数据科学家在部署模型时面临的一个挑战性问题。在这篇博客中,我们概述了使理解和调试漂移具有挑战性的核心原因,以及采取有指导的观点来减轻漂移的方法。
- 地面事实的缺乏使得识别撞击漂移的问题变得更加复杂。
- 识别漂移的各种方法在不同程度上不确定漂移及其对模型性能的影响。
- 漂移根本原因分析工具可以引导数据科学家找到解决漂移的正确解决方案。解决方案可能涉及也可能不涉及标注更多数据或重新训练,可能涉及修复数据管道或调整模型中使用的功能。
漂移指标:如何选择正确的指标来分析漂移
在我们的上一篇文章中,我们总结了机器学*部署中的漂移问题(“机器学*中的漂移:为什么很难以及该怎么办”《走向数据科学》)。这篇文章的一个要点是:处理漂移的方法必须确定漂移是否以及如何对模型性能产生后果。在这方面,数据科学家必须做出的一个关键选择是,即漂移指标以用于他们的特定情况。在本文中,我们进一步研究如何选择使用哪些漂移指标。
问题是
形式上,假设我们有两个经验分布(即数据集):模型输入 X₁与相应的标签 y₁;还有 X₂,还没有标签。我们可以生成模型输出ŷ₁和ŷ₂.我们现在必须决定,₁与₂的差异是否足以让我们担心数据漂移,我们在 y₁x₁训练的模型是否很适合在 X₂.表现良好
数据肯定已经发生了变化,对于至少有一些数字或连续特性的分布,很可能在训练和部署输入之间没有一个共同的实例。我们试图通过一次查看单个特征或一小组特征来避免维数灾难。
疫情前后要素的密度可视化。改变了多少?图片作者。
漂移度量从两个数据集获取特征值,并为我们提供一个差异度量,一个实数通常表示值越大差异越大。在决定正确的指标时,我们需要问的问题包括:
- 它适用于数据类型吗?
- 它是否带有有意义的单位或者有其他方便的解释?
- 它是否对数据分布做了任何假设?
- 它是否捕捉到小概率事件/值的差异?
- 有没有现成的实现可以用在我的项目中?
在本文的剩余部分,我们将考虑这些问题和几个相关的度量标准:它们适用于什么类型的特征:分类的还是数字的,大多数度量标准的解释或基础理论来自信息论或统计学,罕见事件的处理,假设和可调参数。虽然我们详细阐述了这些方面,但重要的是要认识到,任何指标,作为将数据集之间的复杂差异汇总到单个数字中的方法,只能提供可能的数据转移类型的狭隘观点。
功能类型的度量
数字特征
正态分布的 200 个样本的样本、直方图和累积密度图,是测量数字特征之间漂移的基础。图片作者。
测量数字特征之间差异的漂移度量(通常)将数据集视为样本集、分箱密度或累积密度。其中最简单的只是比较常见的聚合统计数据,如数据集间的平均值:
当基础数据以这种集合很好地概括的方式分布时,这种度量可能是有用的(即,如果 X₁和 X₂是正态分布的,均值和标准差之间的差异提供了它们之间差异的完整说明),但数据很少很好地分布,这些度量可能会遗漏重要的差异。
均值差异有意义(左)和无用(右)的情况。比较 2000 个正态分布样本的直方图。图片作者。
在数学意义上,总体统计数据中的差异(如平均值中的差异)不是度量标准。每当 X₁ = X₂时,度量标准通常要求 M(X₁,X₂) = 0,这也是测量漂移的理想特征,并且是均值差不考虑的特征。一个度量标准是推土机距离或瓦瑟斯坦距离。这个定义不是很有见地,但“推土机”的同义词是:该度量标准测量需要移动多少土(概率密度)以及将一种分布转换成另一种分布需要多远(以 X₁'s 域为单位)。在 scipy(wasser stein _ distance)中有一个实现。
分类特征
分类特征的两种分布,是测量分类特征漂移的基础。图片作者。
适用于分类特征的漂移度量(通常)将经验数据视为离散分布。这些是根据与分布中感兴趣的给定特征的每个分类值相关联的概率来定义的。从规范中导出的度量就是这种度量的例子:
基数 p 通常是 1、2 或无穷大。我们将在下面的可解释性部分进一步讨论这些距离。规范在包括 numpy ( linalg.norm )的数值库中实现。
适用于分类特征的另一组度量是信息理论度量的离散版本,我们将在下一节中讨论。
度量起源和可解释性
一些距离测量比其他的更容易解释。度量的起源与我们可以应用的解释或直觉密切相关。我们在这里讨论共同的起源和相关的解释。
上述差异是指或任何可解释的总体统计数据的差异,其优点是可根据各自的总体(即平均值、偏差等)进行解释。).
运土机距离有一个解释,正如本文前面提到的,可以给出可解释的单位。如果特征 X 以单位 u(美元、英里等)来测量,那么推土机距离也以 u 来测量(技术上它包括一个无单位概率的因素),并捕获分布必须移动多远(以 u 为单位)以相互匹配的预期值。这有一些好处,也有一些弊端。解释“5000 美元”的推土机距离可能需要对潜在特征有一些了解,因为这可能是一个小的变化,也可能是一个大的变化,取决于该特征代表什么。如果分析师确实有一些关于该特征的线索,并且能够将这样的数量与他们的经验联系起来,这也是一种优势(例如,如果该特征是“年薪”,那么“5000 美元”的距离应该能够被典型的数据科学家直观地解释)。
统计数字
“这些分布是相同的吗”这个问题经常在统计领域被发现和回答。一些所谓的“测试”因此找到了测量漂移的方法。例子包括 Kolmogorov-Smirnov 检验和相关统计,以及卡方检验。基于统计的方法的一个好处是它们可以用置信度或 p 值来解释;不仅表明分布有多大差异,而且表明对观察结果的信心。这两种测试都可以在 scipy 中获得( kstest 、 chisquare )。
信息论
另一类度量来自信息论。最合适的是詹森-香农距离。虽然该领域的特点是众多的分布差异的措施,他们的范围往往是无限的,或者他们是不对称的。JS 距离是相对熵(或 Kullback-Leibler 散度)的对称有界变量,范围为 0-1。这些度量的另一个重要方面是,由特性获得的值并不重要,重要的是它们的概率。例如,对于分类特征, Jensen-Shannon distance 不会在意您是否改变了特征值,只要概率集保持固定。
从信息论中导出的度量可能与“比特”或“尼特”的单位相关联(取决于所使用的对数基数,分别是 2 或 e),但是我们在此注意到的相对度量不如熵本身那样容易解释,熵本身与编码长度相关联,因此可以说定义了单位“比特”。
scipy ( 熵, jensenshannon )中提供了这些度量的实现。它们应用于分布(即概率列表),因此只对分类特征进行操作。在假设或参数 部分,我们将讨论它们在数字特征中的应用。
其他解释来源
从技术上讲,有些度量可以用无单位概率来解释。这可能不像运土机中的特征单位那样直观,但它可能提供一些洞察力。将经验分布解释为实际概率分布的几个度量对分类特征进行操作,产生可解释为概率的值。这包括上面提到的 1 范数和无穷范数距离以及总变差距离(等于 L₁范数距离的一半)。例如,L₁告诉我们所有类别概率的总差异。另一方面,L∞,告诉我们最不同类别的概率差异。
演示了各种基础的标准距离。均匀分布特征的质量在值 A/B 和值 C/D 之间以一半的速率移动,而值 F 的质量不变。图片作者。
p-范数距离具有空间解释,但它们适用于概率值。如果分布是坐标(每个概率是一个维度),那么 1 范数距离对应于曼哈顿距离,而 2 范数距离是欧几里德距离。虽然空间解释可能不太直观,但这种规范的这一特性更有用:
- 在较大的基数中,p-范数距离强调最不同维度/概率的差异。在极限∞-范数下,距离变成:
- 在较小的基底中,p-范数距离强调差异中涉及的维数。在 0 范数的极端情况下,距离变成了概率变化的计数,而不管变化了多少。不过,我们警告说,0 范数对于测量漂移是没有用的,因为它对微小的变化都很敏感。
除了单位或概率,一些指标被限制在一个固定的范围内。本文中提到的所有指标的最小值为 0,表示最小可能差异(或相同分布),除了差异指标和推土机,以上所有指标的界限为 1、sqrt(2)或其他一些最大值,表示最大可能差异。
假设或参数
使用上述差异法中的聚合统计来区分分布带有假设。具体地说,平均值足以描述两种分布中分布和差异。即使正态分布也需要额外的变量来完全描述,这在一般情况下限制了这种度量的信息量。然而,简单的总体差异仍然是有用的,因为它们易于解释(下面讨论)。当描述一个数字数据集时,统计平均值可能是我们首先想到的,而且我们经常不需要考虑更多。
另一类假设来自使用离散化为连续分布定义的度量的*似值。相对熵和 Jensen-Shannon 距离等信息论度量对于分类特征来说计算起来很简单,但对于连续特征来说是用积分来正式定义的。此处的一个快速解决方案是将连续值要素离散化到条柱中,并对结果应用离散分布版本的度量。然而,这现在引入了可以影响结果的参数(例如,箱的数量)。虽然各种经验法则建议宁滨策略(见箱和宽度),但它们往往会导致高估漂移。
宁滨经验 1000 样本对两个正态分布之间相对熵的估计,作为箱数的函数。突出显示了几个箱子数量的“经验法则”。还显示了解析导出的相对熵的真实值。图片作者。
当容器数量设置过高时,指标会失效。产生的漂移结果倾向于仅仅是箱数的函数,而不是所涉及的实际经验分布。
另一种不易受宁滨影响的方法是通过(核)密度估计:用函数拟合观察到的密度,并评估这些估计密度之间的距离,而不是面元频率。虽然计算估计值之间的距离也需要宁滨,但是该设置变得不太重要,并且在某些情况下可以设置为任意高(至少对于正常距离)。
使用密度估计也依赖于参数:使用什么样的函数来估计密度?一种常见的方法是通过带宽参数化的高斯混合。下图展示了这种方法的一个示例,左边是样本的真实密度和估计密度,右边是结果距离。设置带宽的经验法则也是可用的(参见内核密度估计带宽的重要性)。
1000 个大小的法线样本的混合高斯估计和作为带宽函数在估计之间计算的 NormDistance₂。使用 100 个箱来完成距离计算。图片作者。
numpy 和 sklearn 通过直方图和内核密度以及本文前面提到的统计或信息理论方法提供了实现简单宁滨方法和内核密度估计方法所需的大多数底层工具。
稀有值
我们在本文中讨论的漂移指标的最后一个方面是罕见事件(罕见值)变化的影响。在大多数指标中,分类值的任何变化(很少开始)的影响是最小的,与其在被比较的数据集中的概率成比例。如下面的示例动画所示,在训练过程中,只有 10%的实例值为“rareA”的分类特征通常对漂移测量只有 10%的影响,即使在部署数据中,该值的实例数减少到 0%。另一方面,在一些度量中,概率变化被认为是相对于彼此的(倍增的),并且从 10%到 0%的变化是显著的。
相对熵定义中的加法和乘法差项。
NormDistances 只有加法差项。
在示例度量定义的引擎盖下,相对性和正态距离,我们看到两个元素:对训练分割值的期望(A 部分)或训练/测试概率之间的加性差异(如在正态距离中),以及在某些情况下,比较训练值与测试值的概率的相对分量(B 部分)。第一项负责限制稀有值的影响,而后者可以在稀有值的相对概率显著变化时突出稀有值的差异。大多数分类指标都以 A 部分为特征,但并不是所有的都以 b 部分的相关部分为特征。
L₂范数距离(无乘性差项)和相对熵(有乘性差项)对偏离 X₁的 X₂和 X₃两个分布的漂移测度的比较:X₂在普通值之间转移权重,而 X₃在稀有值之间转移权重。稀有值的偏移相对较大。注意x₁→x₂的 NormDistance 较大,而 X₁ → X₃.的相对 neutropy 较大图片作者。
以 B 为特征的度量包括相对熵、Jensen Shannon 散度和种群稳定性指数。对于一些指标,在数据集之间事件从> 0%到 0%表示的情况下测量的漂移在技术上可能是不受限制的(由于在定义中除以 0)。人们可以通过在所有概率中加入小的附加常数或者至少在分母位置加入一个常数来避免这些。
总结
- 各种漂移指标可以帮助您检测和量化数据漂移,这些指标具有不同的应用、解释、来源和其他可能对您的应用非常重要的方面。
- 对指标的解释各不相同,对指标来源领域的一些了解可能有助于理解这些指标。
- 度量标准通常是*似值,尤其是数字或连续特征的度量标准,并且可能做出一些便于计算的假设,并且可能需要设置参数。
- 虽然一些指标没有捕捉到罕见类别中相对较大的漂移,但其他漂移指标能够突出此类变化。
以下列表总结了本文中提到的漂移指标和我们讨论过的显著特征,供您参考:
正常距离
- 原点:几何体
- 特征类型:分类,*带假设的数字
- 释义:距离;p=0(不推荐);p=1(曼哈顿距离);p=2(欧氏距离);p=∞(最大概率移位)
- 相对:没有
- 工具:numpy linalg.norm
相对熵/库尔贝克-利布勒散度
- 起源:信息论
- 特征类型:分类,*带假设的数字
- 解释:比特/单位,排列不变
- 亲戚:是
- 工具:科学熵
詹森-香农距离
- 起源:信息论
- 特征类型:分类,*带假设的数字
- 解释:比特/单位,排列不变
- 相对:是*(技术上是但实际上不是:JS 距离使用一个输入分布和两个输入分布中点之间的相对熵;结果是,即使一个消失的值也将具有有限的影响,因为它的相对项被 1 所限制
- 工具:scipy jensenshannon
总变化距离
- 起源:概率论
- 特征类型:分类,*带假设的数字
- 解释:与基数为 1 的正常距离相同
- 相对:没有
- 工具:numpy linalg.norm
差异含义
- 来源:统计
- 特征类型:数字
- 解释:与特征值相同的单位
- 相对:没有
- 工具:numpy 的意思是
推土机距离/瓦瑟斯坦距离
- 特征类型:数字
- 解释:与特征值相同的单位
- 相对:没有
- 工具:scipywasser stein _ distance
科尔莫戈罗夫-斯米尔诺夫试验
- 来源:统计
- 特征类型:数字
- 解释:可用的统计置信度
- 相对:没有
- 工具:scipy kstest
卡方检验
- 来源:统计
- 特征类型:数字
- 解释:可用的统计置信度
- 相对:没有
- 工具:剪刀卡尺
*数字特征的分类度量
在你“先放下”之前,读读这个
如果您关心模型的可解释性,请考虑从一个热编码的列中删除哪个类别
概观
作为一名数据科学的学生,我最*学*了如何使用普通最小二乘(OLS)线性回归对变量交互进行建模。让我感到奇怪的是,在分析分类变量时,避免虚拟变量陷阱的常见建议是简单地删除基于字母数字类别标签的第列。
我的直觉是,我们选择删除哪一列在一定程度上很重要。如果真的有关系,因为标签排在第一位而删除一列似乎非常武断,也不是特别科学。
我发现,虽然有大量的 web 资源描述了为什么在这个场景中我们要删除一个列,但是很少有人试图解决选择哪个列的问题。因此,为了满足我的好奇心并加强我对 OLS 线性回归的理解,我创建了一些实验数据并测试了删除不同列的结果。我的目标是确定:
- 我们放弃哪个是否重要(我的假设是肯定的);和
- 如果有关系,数据科学家在选择合适的色谱柱时应该考虑哪些因素。
对于那些想直奔主题的人,我的分析揭示了:
虽然删除的列似乎不会影响 OLS 线性回归模型的性能,但它会对模型系数的可解释性产生重大影响。
准备普通最小二乘多元线性回归模型的数据科学家应仔细考虑从每个类别中删除哪些列,如果他们的分析目标包括:
- 对标准化系数进行排序,以推断预测因素对目标变量影响的相对大小或重要性(例如,“平方英尺或卧室数量是否会增加房屋销售价格?”)
- 准确理解预测变量的单位变化如何影响目标(即“每增加一平方英尺居住空间,房屋销售价格的平均涨幅是多少?”)
- 向非技术风险承担者解释上述结果。
任意删除第一列而不考虑它所代表的内容,不仅会使解释模型系数变得困难,而且还会使非技术涉众难以理解。
数据科学家不应依赖于删除第一列的惯例,应考虑删除代表逻辑基线参考点的列,模型将假设所有包含的预测变量偏离这些参考点。在某些情况下,最小值可能是合适的,在其他情况下,平均值可能是合适的,而在其他情况下,合适的参考类别可能完全取决于分析的目标。
下面,我首先提供一些背景,然后概述我得出这些结论的步骤。我的方法和分析的全部细节可以在我的 github repo 中找到。
背景
什么是一键编码?
为线性回归模型准备数据时,有必要将分类变量虚拟化或一键编码(OHE)到单独的列中,以将非数值表示为数字。
例如,表示性别的分类变量将从一个单独的列转换为多个一键编码的列,这样命名是因为值 1 表示该类别中的成员资格,其余的类别列用零填充。
示例一-热编码(OHE)转换
为什么要删除任何列呢?
当学*线性回归时,数据科学的学生被教导从每个分类变量组中删除一列作为参考类别,并避免“虚拟变量陷阱”:预测值之间的完美多重共线性。
在我们的示例中,我们可以删除女性、男性或非二进制,并在模型中保留其他两列。
正如这篇文章说明的如果我们不删除一列,用线性代数根本不可能解决 OLS 问题,因为我们会有一个奇异矩阵。尽管 scikit-learn 和 statsmodels 库使用不同的算法来求解 OLS,所以从技术上来说,如果我们不从每个类别中删除一列的话,可以生成模型,但这样做仍然被认为是最佳实践。在我的实验中,我包括了没有删除列的模型来说明奇怪的结果。
如果数据集包含多个分类变量,则应从每组生成的 OHE 列中删除一列。在本文中,我将讨论删除“一列”,这应该理解为每个分类变量组中的一列。
请注意,从 OLS 线性回归模型中删除一列是必要的,但对于其他模型类型可能没有必要(甚至是不明智的)。这里得出的结论适用于 OLS 线性回归模型。
任意丢弃“第一”
Pandas 和 sckikit-learn 等 Python 库在它们的 one-hot-encoding 方法中内置了参数,允许我们从每个分类组中删除一列。一种常见的方法是先删除,即删除集合中字母数字顺序第一个出现的代表类别值名称的列。在我们的性别示例中,第一列是女性,因为 F 在 M 和 n 之前。
事实上,先放弃似乎是一个如此普遍的约定,以至于熊猫和 scikit-learn 都有方便的常数可以用来这样做,所以你不必弄清楚第一个类别是什么。
但这真的是在所有情况下选择一个变量丢弃的最佳方式吗?在接下来的小节中,我将介绍如何创建数据来测试这一点,并得出结论,删除第一列并不总是最佳选择。
创建测试数据集
我根据房屋特征和销售情况创建了一个 n=20,000 的测试数据集,其中我的 OLS 线性回归模型将尝试预测房屋的最终销售价格。
首先,我生成了独立(预测)变量:
- 居住面积平方英尺:服从正态分布的随机生成的连续变量,平均值约为 2800。为了生成价格,我应用了每平方英尺 100 美元的固定系数。
平方英尺与价格的直方图、箱线图和散点图
- 邮政编码:具有大量类别的分类变量(70)。我用正态分布的随机数创建了乘数来控制每个邮编对价格的影响程度,并随机将它们分配给相当均匀的几组房屋。“第一个”邮政编码类别 30000 被设置为从平均价格中扣除最多,因此它代表最小贡献或最大扣除,这取决于您的观点。平均邮政编码类别(30066)对价格的贡献很小,最高的增加最多。
每个邮政编码类别的平均价格,从低到高排列
- 条件:变量个数较少的分类变量(5):差、低于平均、一般、高于平均、优秀。我设计了乘数,这样平均条件不会给价格增加任何东西;贫穷和低于平均水平减损;高于平均水平和优秀增加。不是将房子分成 5 个相当均匀的组并分配条件(就像我用邮政编码做的那样),而是 70%的房子被分配到平均条件类别,10%的房子被分配到平均水平以下和以上,5%的房子被分配到差和优秀。
每种条件的平均价格
- 一周中的某一天(DOTW) :一个变化无常的变量,它对价格的贡献没有任何特定的模式(我只是为每一天编了系数)。我测试了从我的模型中排除这一点,以减少模型应该能够解释的目标的可变性,使其更加现实。
每 DOTW 平均价格
随着我的平方英尺值的随机生成,房屋被分配到上述类别,我应用乘数来生成价格。我使用 100,000 美元的起始基线(这成为预期的 y 截距),并将分类乘数应用于 50,000 美元的常数。
我现在有了一个目标变量 Price,它是基于每个预测变量的已知系数生成的。
目标可变价格的直方图和箱线图,由预测系数生成
我记录了每个预测值类别的原始单位的预期系数,以及连续变量平方英尺的标准化单位(标准差)的系数。这使我能够将每个模型的结果与预期的系数进行比较。
左边是标准化的预期系数,右边是原始单位。它们对于分类变量是相同的,只有平方英尺不同,因为分类变量不需要进一步标准化。
建模
我的主要问题是从模型中删除类别列(即用作模型的参考点)是否会影响模型的结果。我想测试删除第一列,因为这是常见的约定,作为替代,我想删除代表平均类别的一列:平均房价最接*人口平均值的类别。
我对三种不同类型的结果感兴趣:
- 性能—训练和测试的 R 平方和 RMSE
- 可解释性——标准化系数的准确排名
- 可解释性——准确直观的原始单位系数
所以我至少需要:
- 两种不同的模型来比较删除第一列与平均值
- 比较标准化系数和原始单位系数的另外两个模型
我决定也运行不删除分类列的模型,看看会发生什么。最后,我改变了是否在模型中包含 DOTW 变量,其中包含它应该允许模型预测 100%的目标可变性,而排除它会引入一些误差,应该稍微更现实一些。
最终,我用这些不同的参数遍历了 12 个模型,然后查看了结果。
模型解释和结论
为了评估性能,我使用了训练-测试分割,并生成了训练和测试的 R 平方、残差平方和(RSS)和均方根误差(RMSE)。
为了评估可解释性,我将测试模型系数与预期进行了比较,回顾了测试模型之间的差异,并考虑了我的自然结论的准确性。
表演
能够在给定独立变量的情况下预测目标是线性回归模型成功的关键衡量标准之一。
每个模型的性能统计 R 平方和 RMSE。我们可以看到,当混杂变量 DOTW 被包括在内时,所有版本的模型都是完美的。当我们不包括 DOTW 时,我们会看到一些如我们所料的误差,但改变类别或数据是否标准化并不影响结果。
在前 6 个模型中,我包括了“星期几”变量,在后 6 个模型中,我没有包括它,所以它将作为一个混杂变量。
尽管数据处理方式的其他方面各不相同,如数据是否标准化以及删除了哪个类别,但这些因素对 R 平方、均方根误差或残差平方和没有明显影响(为简洁起见,上面未显示)。换句话说,前 6 款看起来一样,后 6 款看起来也一样。
在没有“星期几”变量的情况下运行的模型并不完美,这具有直观的意义。但是在我排除 DOTW 的情况下,这些模型之间似乎没有任何区别。它们有相同的 R 平方和 RMSE。
我的结论是,改变从模型中删除的类别列不会影响模型的性能。
可解释性——准确排列标准化系数
我们可能希望能够使用我们的模型的另一种方式是对标准化系数进行排序,以比较它们对目标的影响大小,或者它们的重要性。目标是确定哪些变量在更大程度上增加或减少目标。对连续变量进行标准化很重要,这样它们的系数就可以用标准偏差单位进行比较。
这些见解与领域知识相结合,可能有助于业务利益相关者决定适当的行动计划。如果他们的目标是调整预测因子来影响目标,知道哪些预测因子具有最大的影响是关键。
为了衡量我们模型的分级系数的准确性,请考虑下面的热图。虽然每个模型的热图都使用自己的颜色渐变比例,但是我们希望测试模型的渐变顺序与预期模型的渐变顺序相匹配。
将测试模型的标准化系数与预期系数进行比较。不包括 y 轴截距。
在这两个模型中,邮政编码看起来很好地遵循了预期的顺序。但是在 Dropped First 模式下,条件类别和平方英尺看起来不太对;它们的颜色从渐变中突出,因为它们的标准化系数比我们预期的要低得多。
在平均下降模型中,所有的预测变量看起来都非常接*。
请注意,两个测试模型的 y 截距与预期有很大不同。事实上,下降平均模型的 y 截距非常不同。但是我们不太可能关心 y 轴截距的准确排序;我们可能最关心的是实际预测变量系数的排名。
让我们去掉邮政编码类别,放大其他类别。
如果我们把它们从邮政编码的上下文中取出来,它看起来确实像两个模型预期的条件类别匹配的顺序。换句话说,优秀的条件比平均水平高,等等。
然而,平方英尺应该比良好的条件多增加大约 55000 美元的价格。我们看到这种差异在下降平均模型中相当准确,但在下降第一个模型中,优秀条件的排名高于平方英尺。
如果我使用标准化下降平均模型来估计哪些变量对价格上涨更重要,我会相当准确。
但是,如果我使用标准化的 Dropped First 模型来估计这一点,我会假设邮政编码大体上是最重要的,其次是条件良好,然后是平方英尺。这不会很准确。
准确直观地解释原始单位系数
我们也可能对使用原始单位的系数感兴趣,这些系数来自我们没有标准化数据的模型,以了解每个预测变量的每个单位的目标变量如何变化。
例如,我们希望能够使用与平方英尺相关的系数来表示“每增加一平方英尺,价格就会上涨大约 100 美元。”
我创建了类似的热图来检查原始单位系数的准确性。请注意,在这个可视化中,两个测试模型的色阶都被强制匹配预期的模型,因此我们可以通过直接比较阴影来比较这些值。这里,我们关心的是系数的准确性,而不是标准化系数,标准化系数更关心排名顺序。
要比较系数值,请直接比较颜色深浅
我们可以看到,下降平均模型的系数非常接*预期值。它的颜色和预期的非常相似,而且顺序正确。
然而,在丢弃的第一个模型中,y 截距非常低,这导致大多数其他系数过高,无法弥补。
我们可以看到平方英尺系数非常接*,但邮政编码值相当高,很难确定条件属于哪里。让我们去掉邮政编码来放大其他变量。
我们将花一些时间来解释我们在这里看到的差异,因为第一个模型的单位系数与预期不完全匹配的事实并不一定意味着它们本质上是错误的或无用的。
首先,我们注意到两个测试模型都有非常精确的平方英尺单位系数,这是我们唯一的连续变量。
我们可以清楚地看到,丢弃的平均模型的条件变量的单位系数非常接*预期,而丢弃的第一个模型的系数则不是。实际上,这意味着什么?
将这些单位系数放在每个模型的基线或参考点的上下文中是很重要的。系数必须相对于该基线进行解释。
对于我们的连续变量,平方英尺,参考点是 0,这将是我们包括的任何连续变量的情况。
但是对于我们的分类变量,基线中的参考点成为我们从模型中删除的任何分类列。所以我们可以说:
两个测试模型之间单位系数的差异是由于模型的分类值具有不同的参考点。
现在,为什么两个模型的平方英尺系数相同,但分类系数不同就更有意义了:平方英尺在两个模型中都假定基线为 0。
下一个合乎逻辑的问题是:一个基线天生就比另一个好吗?因为我们看到模型性能没有改变,所以现在还不清楚。为了回答这个问题,让我们考虑一下,假设我们不知道预期的是什么,我们将如何解释丢弃的第一个模型的条件单元系数。
在删除的第一个模型中,我们删除了“条件差”列。所以我们的基线假设了一个参考点,即可能的最坏情况。我们将这些系数解释为:
- 低于平均水平的条件会使房屋的基准价格增加 5 万美元
- 处于平均水平的情况下会在房屋的基准价格上增加66000 美元
- 条件高于平均水平的会在房屋的基准价格上增加71,000 美元
- 拥有良好的条件会使房屋的基准价格增加 81,000 美元
在删除的第一个模型中,我们将每个条件级别解释为添加到基线,甚至低于平均水平。而在平均下降模型中,基线假设条件为平均,因此我们将系数解释为:
- 一套房子的基准价格中减去了 66000 美元
- 低于平均水平的条件会从房屋的基准价格中减去 16000 美元
- 拥有高于平均水平的条件会使房子的基准价格增加 5000 美元
- 拥有良好的条件会在房子的基准价格上增加 15000 美元
在两个模型中,系数值之间的步长是相同的,但是正/负方面是不同的。
客观上,一个并不比另一个好,但是:
如果分析的目标是向非技术风险承担者提供实际的系数值,那么去掉条件的平均类别会产生最直观的系数,这些系数更有可能对风险承担者有意义。
想象一下,虽然我们对低于平均条件的影响有一个正数,但这是在实际上有负价格的基线房子的背景下解释的困难…这真的没有多大意义。这本身并没有错,但如果没有正确的框架,人们可能会得出误导性的结论。
摘要
虽然您选择删除的类别不会影响模型的性能,但它会对模型的可解释性产生重大影响。
如果您计划使用模型中的系数对以下内容进行精确推断:
- 每个预测单位对目标的影响,
- 或者每个预测器相对于其他预测器的影响程度,
然后,您应该仔细考虑要从模型中删除哪些分类值。
线性回归模型的系数是在基线模型的上下文中解释的。对于连续变量,基线使用参考点 0。但是对于分类变量,无论删除哪一列,都会成为参考点,这对于如何解释系数有重大影响。
对于数据科学家来说,重要的是考虑哪些列代表每个类别的最直观的参考点,并删除这些列。简单地删除第一列是任意的:第一列不一定代表最小值,最小值也不一定总是一个好的参考点。
深思熟虑地选择删除哪一列将产生更容易准确解释的系数。
您应该如何选择要删除的列?
不要假设第一个或最小类别值是最合适的,而是考虑哪个类别代表了利益相关者最直观的参考点,或者有助于回答分析的根本问题。
例如,如果您正在对工资的预测值进行建模,那么您可能有一个教育水平的分类预测值,其值如下:
- 不到高中
- 高中或 GED
- 副学士学位
- 学士学位
- 硕士学位
- 博士学位
如果你依赖于字母数字的第一列,那么的副学士学位将是你的参考点。如果你的利益相关者对大专以下学历的人与大专以上学历的人相比收入多少感兴趣,这可能是有意义的。
但是,如果你觉得 T4 高中或 GED T5 能更准确地代表人们的平均教育程度,那么把它们作为参照点也是合理的。你甚至可以计算出这些类别中哪个类别的平均或中值工资最接*人口的平均或中值,从而有统计学上的理由选择特定类别作为参考点。
在这个实验中,我放弃了代表平均值的类别。这是有意义的,因为我的类别代表了什么。要为给定场景选择合适的列,数据科学家需要考虑哪一列代表其利益相关者的直观参考点,在这种情况下,正系数和负系数将具有相关的意义。
当然,如果你的主要目标是预测,你就不必担心你会降低哪个类别的值!
我希望其他人会发现这是有用的和有益的。由于我仍然是数据科学的学生,我欢迎任何关于我的测试方法或我的结论的适用性的想法。
利用卷积神经网络预防疲劳驾驶事故
使用 Keras、人脸识别、OpenCV 和 PIL 进行全面演练
作者图片
疲劳驾驶:一个严重的问题
国家公路交通安全管理局估计,每年有 91,000 起涉及昏昏欲睡的司机的撞车事故,导致估计 50,000 人受伤,* 800 人死亡。此外,每 24 名成年司机中就有 1 人报告在过去的 30 天里开车时睡着了。研究甚至发现,超过 20 小时不睡觉相当于的血液酒精浓度为 0.08%——美国法定上限。
由于这个严重的问题,我和一群其他数据科学家着手开发一个可以检测眼睛是否闭合的神经网络,当与计算机视觉结合使用时,检测一个活生生的人是否闭眼超过一秒钟。这种技术对任何对提高驾驶安全性感兴趣的人都很有用,包括商业司机、汽车公司和汽车保险公司。
目录:
数据收集
我们使用了许多来源的完整面部数据,即来自马萨诸塞大学的睁眼面部数据和来自南大的闭眼面部数据。
然后,我们使用一个简单的 python 函数从这个数据集中裁剪出眼睛,给我们留下了 30,000 多张裁剪后的眼睛图像。我们给每个图像添加了一个缓冲区,这样不仅可以得到眼睛,还可以得到眼睛周围的区域。这个裁剪功能将在稍后的网络摄像头部分重新使用。
一个警告:手动擦洗这个数据集,除非你想要人们眨眼或者 200 张比尔·克林顿的照片来训练你的模型。以下是我们用来训练模型的数据示例:
作者图片
创建卷积神经网络
决定一个指标
因为对我们来说,预测正面类别(一个睡着的司机)比预测负面类别(一个醒着的司机)更重要,所以我们最重要的衡量标准将是回忆(灵敏度)。召回率越高,模型错误预测的睡着的司机醒着的数量就越少(假阴性)。
这里唯一的问题是,我们的积极阶层在数量上远远超过了我们的消极阶层。正因为如此,最好使用 F1 得分或精确回忆 AUC 得分,因为它们也考虑了我们猜测司机睡着但实际上醒着的次数(精确)。否则,我们的模型将总是预测我们睡着了,无法使用。另一个我们没有用来处理不平衡图像数据的方法是使用图像增强。我在这里没有利用这一点,但是 Jason Brownlee 做了很好的工作来解释你如何能在这里。
准备图像数据
下一步是导入图像并为模型进行预处理。
此部分所需的导入:
**import cv2
import** **tensorflow** **as** **tf**
**from** **tensorflow** **import** keras
**from** **sklearn.model_selection** **import** train_test_split
**from** **tensorflow.keras.wrappers.scikit_learn** **import** KerasClassifier
**from** **keras.models** **import** Sequential
**from** **keras.layers** **import** Dense,Flatten,Conv2D,MaxPooling2D,Dropout
导入我们之前创建的图像,调整图像大小,使它们完全匹配。对于这个项目,我们调整到 80x80 像素。这是一个使用操作系统库的简单导入函数:
设置变量,自变量 X 为图像,因变量 y 为相应的标签(1 表示闭眼,0 表示睁眼):
X = []
y = []
**for** features, label **in** eyes:
X.append(features)
y.append(label)
将图像转换为数组,以便它可以进入模型。此外,通过除以 255 来缩放数据。
X = np.array(X).reshape(-1, 80, 80, 3)
y = np.array(y)
X = X/255.0
使用 scikit learn 的train_test_split
将数据分为训练集和验证集。重要:使确定对 y 进行分层,因为我们有不平衡的类。
X_train, X_test, y_train, y_test = train_test_split(X, y, stratify = y)
创建模型架构
作者图片
卷积层:
这一层创建像素的子集而不是完整的图像,并允许更快的模型。根据您设置的过滤器数量,这可能比原始图像更密集或更不密集,但它们将使模型能够使用更少的资源来学*更复杂的关系。我用了 32 个滤镜。至少使用一个卷积层,通常需要两个或更多。对我来说,最佳设置是两个 3x3 的放在一起,然后是三个 3x3 的放在一起。CNN 的总体趋势是使用更小的过滤器。事实上,双 3x3 层与 5x5 层本质上是一样的,但是更快并且常常会得到更好的分数,正如 Arnault Chazareix 在这篇精彩的文章中所解释的那样。之后汇集并不总是必要的或更好的。如果可能的话,试试你的模型。
展平
确保展平图像阵列,以便它可以进入密集层。
密集层
层数越多,模型的训练时间就越长。随着这些层中神经元数量的增加,网络所学*的关系的复杂性将增加。一般来说,卷积层的想法是避免不得不制定过深的密集层方案。在我们的模型中,我们使用了三层神经元(256,128,64)以递减的速率激活。我们还在每一层之后使用了 30% 的压差。
输出层
最后,因为这是一个二进制分类问题,请确保为外层使用 sigmoid 激活。
编译模型
在model.compile()
中,您将需要将指标设置为 PR AUC(tensor flow 中的tf.keras.metrics.AUC (curve = 'PR')
)或 recall(tensor flow 中的tf.keras.metrics.recall
)。设置损失等于binary_crossentropy
,因为这是一个二元分类模型,一个好的优化器通常是adam
。
拟合模型
将您的批量设置得尽可能高,但不要在此过程中炸掉您的机器!我在 Google Colab 的 32 GB TPU 上运行了一个 gridsearch,它轻松地运行了 1000 多个批次。当有疑问时,尝试 32 批,如果不超负荷你的记忆,增加。就时代而言,20 个时代后收益递减,所以我不会比 CNN 的收益高太多。
以下是 Tensorflow Keras 的完整设置:
最终精确-召回曲线下面积分数:
0.981033
让我知道你是否能做得更好!
创建网络摄像头应用
一旦你有了满意的模型,使用model.save('yourmodelname.h5')
保存它。确保在保存时运行没有验证数据的生产模型。这将导致在导入时出现问题。
安装和进口:
这些都是 Mac 优化的,虽然也可以在 Windows 上使用相同的脚本。查看此链接此处用于诊断 windows dlib。
# installations needed for webcam application
# pip install opencv-python #
# if you want to play a sound for the alert:
# pip install -U PyObjC
# pip install playsound# imports for webcam application
import cv2
from playsound import playsound
# import model saved above
eye_model = keras.models.load_model(‘best_model.h5’)
使用 OpenCV 访问网络摄像头
使用cv2.VideoCapture(0)
开始网络摄像头捕捉。如果你想基于相对帧尺寸而不是绝对坐标来确定文本位置,请确保使用cap.get(cv2.CAP_PROP_FRAME_WIDTH)
保存网络摄像头的宽度和高度。您还可以查看每秒帧数。OpenCV 捕获属性的完整列表可以在这里找到。
cap = cv2.VideoCapture(0)
w = cap.get(cv2.CAP_PROP_FRAME_WIDTH)
h = cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
print(cap.get(cv2.CAP_PROP_FPS))if not cap.isOpened():
raise IOError(‘Cannot open webcam’)
用 OpenCV 捕捉帧并裁剪它们
如果你打算按帧计算闭上的眼睛,一定要设置一个计数器。一个while True:
循环会让摄像机一直开着,直到你完成剧本。在 while 循环中,使用ret, frame = cap.read()
格式捕获网络摄像机视频帧。最后,调用框架上的函数。它应该从帧中返回一个裁剪过的眼睛,如果它在帧中找不到眼睛,该函数将返回不能被 255 整除的None
,并将跳到下一帧。
counter = 0# create a while loop that runs while webcam is in use
while True: # capture frames being outputted by webcam ret, frame = cap.read() # function called on the frame
image_for_prediction = eye_cropper(frame)
try:
image_for_prediction = image_for_prediction/255.0
except:
continue
通过模型运行框架
然后我们可以通过模型运行图像,并得到一个预测。如果预测更接* 0,那么我们在屏幕上显示“打开”。否则(即更接* 1),我们显示“关闭”。注意,如果模型检测到睁开的眼睛,计数器重置为 0,如果眼睛闭上,计数器增加 1。我们可以使用cv2.putText()
显示一些基本文本来指示眼睛是闭着还是睁着。
prediction = eye_model.predict(image_for_prediction)if prediction < 0.5:
counter = 0
status = ‘Open’ cv2.putText(frame, status, (round(w/2)-80,70),
cv2.FONT_HERSHEY_SIMPLEX, 2, (0,255,0), 2, cv2.LINE_4) else:
counter = counter + 1
status = ‘Closed’ cv2.putText(frame, status, (round(w/2)-104,70), cv2.FONT_HERSHEY_SIMPLEX, 2, (0,0,255), 2, cv2.LINE_4)
我们还希望显示一个警告,如果连续有 6 帧闭着眼睛(“睡觉”)。这可以使用一个简单的 if 语句来完成:
if counter > 5:
cv2.putText(frame, ‘DRIVER SLEEPING’, (round(w/2)-136,round(h) — 146), cv2.FONT_HERSHEY_SIMPLEX, 1, (0,0,255), 2, cv2.LINE_4)
counter = 5
作者图片
最后,我们需要显示框架,并为 while 循环提供一个退出键。cv2.waitKey(1)
决定画面将显示多长时间。括号中的数字是该帧将显示的毫秒数,除非按下“k”键,在本例中为 27,或者按下 escape 键:
cv2.imshow(‘Drowsiness Detection’, frame)
k = cv2.waitKey(1)
if k == 27:
break
在循环之外,释放网络摄像头并关闭应用程序:
cap.release()
cv2.destroyAllWindows()
最终产品
加上一些风格上的补充,这就是最终产品。你也可以包括声音,我已经在下面的完整脚本中包括了。我用的是 a Down 的《杂碎》(如果你知道,你就知道)的 System 中的《醒来》歌词。
作者图片
如您所见,该模型非常有效,尽管训练时间很长,但在几毫秒内就能返回预测结果。随着一些进一步的改进和输出到外部机器,这个程序可以很容易地应用于实际情况,也许可以挽救生命。
感谢阅读。如果您有任何问题或改进,请随时通过 LinkedIn 联系我。
药物剂量反应数据分析
关于如何在 R 中分析药物剂量反应数据并导出药物疗效关键指标的教程:IC50,EC50,Einf,E0。额外收获是,真正理解著名的“曲线拟合”表达式的含义
药物剂量反应研究是在细胞系中测试药物功效的实验。它们被广泛用于癌症研究,以确定在患者身上进行测试的药物的优先顺序。在癌症药物剂量反应研究中,给定癌症的细胞培养物用增加浓度的特定药物处理,并测量细胞生长的抑制百分比。对细胞生长的抑制越强,测试药物对特定癌细胞系的效果越好。
这些研究的原始数据输出如下,其中 x 轴代表药物浓度(微摩尔),而 y 轴代表细胞生长抑制(%)。
通常从这些数据中提取几个指标来衡量药物疗效:
- E0 :无药物时观察到的反应
- Einf :药物引起的最大反应
- EC50 :诱导 50%最大反应的药物浓度。EC50 通常以摩尔浓度来测量,并用作激动剂药物效力的量度——EC50 值越低,引发 50%最大反应所需的药物浓度越低,药物效力越大。
- IC50 :抑制 50%细胞生长所需的药物浓度。它通常以摩尔浓度来测量,并被用作拮抗剂药物效力的量度 IC50 值越低,药物效力越强。
有了实验的原始数据,我们对药物反应只有一个离散的看法。然而,我们需要药物-反应关系的连续表示,因此我们可以获得药物在任何给定浓度下引起的反应的完整视图。为了获得这种连续视图,我们需要使用“曲线拟合”算法。曲线拟合算法优化了曲线或函数的参数,使其尽可能“调整”或“拟合”我们的数据。有了调整后的曲线,我们将能够内插和外推响应值,因此我们将知道任何给定药物浓度下的响应。
一种广泛使用的方法是拟合 4 参数逻辑曲线,由以下等式定义:
曲线拟合算法试图调整参数 b 、 c 、 d 和 e ,以使方程定义的曲线符合剂量反应数据。将曲线拟合到数据后,曲线看起来会像这样:
现在,让我们看看曲线中的参数 b 、 c 、 d 和 e 是什么意思:
分析曲线方程,我们看到:
- c 是曲线的最低点——对应于在没有药物的情况下观察到的反应( E0
- d 是曲线的最高点(曲线收敛的值)——对应于药物的最大反应( Einf )
- b 决定曲线的斜率。 -b 被称为“希尔项”( H ),测量药物浓度增加时反应如何变化。高 H 值表明当增加药物浓度时响应显著增加,反之亦然
- 如果我们在 y 轴 ( (d-c) / 2 )的中点画一条水平线,则该线与曲线的交点所对应的药物浓度就是 e 参数,即诱导 50%最大反应的药物浓度( EC50
- 如果中点( (d-c) / 2 )等于 50%,那么 e 也对应药物的 IC50 。否则,可以用 f(x) 代替 50 并在曲线方程中找到 x 来计算 IC50:
现在我们知道了拟合曲线的含义以及如何获得药物疗效的指标,让我们一步一步地进行 R 中的数据分析
逐步数据分析
首先,我们加载将要使用的库,并创建合成药物剂量反应数据:
我们绘制原始剂量反应数据:
然后我们使用 drc 包中的 drm 函数拟合曲线,并绘制拟合曲线:
现在,我们已经准备好获取药物疗效指标:
我们的分析完成了!
简而言之,药物剂量反应研究用于测量药物对给定细胞培养物的功效。需要分析这些研究中产生的原始数据,并需要“拟合”曲线,以获得药物剂量-反应关系的连续表示。一旦完成,就有可能获得药物疗效指标,如 E0、Einf、EC50 和 IC50。
妇女健康数据分析
探索瑞典和丹麦妇女使用镇静剂、抗抑郁药、抗精神病药和抗焦虑药的国家趋势
凯文·安吉尔斯在 Unsplash 上拍摄的照片
在这一部分,我将调查丹麦妇女使用镇静剂、抗抑郁药、抗精神病药和抗焦虑药的国家趋势。然后我会将丹麦的国家趋势与我在第一部分中发现的瑞典国家趋势进行比较。
我在这篇文章中使用的所有代码都可以在这里找到。这段代码为我节省了最多的时间,它是一个简洁的“ggplot2”包装器,用于绘制药物利用情况。
在 20 至 54 岁的丹麦女性中,抗抑郁药的使用率稳步下降,而瑞典女性的使用率却在上升。在丹麦,每 1000 名女性中有 56 名 20-24 岁的女性和 72 名 35-45 岁的女性接受抗抑郁药处方。
答 在丹麦和瑞典,抗精神病药物的使用逐年增加。与同年龄组的瑞典妇女相比,丹麦妇女使用抗精神病药物的比率更高。
瑞典女性使用避孕药的比率更高。在丹麦,从 1999 年到 2019 年,20 至 54 岁所有年龄组的妇女使用抗焦虑药的情况都在减少。
与同年龄组的丹麦妇女相比,瑞典妇女使用镇静剂的比率更高。在所有被调查的年龄组中,丹麦妇女使用镇静剂的比例低于每 1000 名同年龄组妇女中 50 名妇女。在瑞典妇女中,只有 20 至 24 岁的妇女使用镇静剂的比例低于每 1000 名妇女 50 人。
带回家的信息
- 在许多方面相似的国家可能呈现不同的数据模式
- 丹麦和瑞典女性使用抗抑郁药、抗精神病药、抗焦虑药和镇静剂的情况并不完全相同
感谢收听🤓
药物 NER 使用 Python 中的空间
训练您自己的自定义 NER 组件来检测药品名称
在 Unsplash 上 Myriam Zilles 拍摄的照片
以前,我写过一篇关于使用 Python 中的 spaCy 进行讽刺文本分类的文章。在本文中,您将了解更多关于命名实体识别(NER)组件的内容。
供您参考,NER 是 NLP 任务的一部分,用于定位非结构化文本中的实体并将其分类到不同的类别中。例如,给定以下句子:
John Doe bought 100 shares of Apple in 2020.
NER 模型预计将确定以下实体及其相应的类别:
- 无名氏
- 苹果公司(组织)
- 2020 年(时间)
本教程着重于训练一个自定义 NER 组件来识别药品名称。您可以很容易地修改它,使之适应您自己的用例。设置和训练过程是相同的,唯一的例外是数据集。
让我们继续下一节,开始安装必要的软件包。
设置
在继续之前,强烈建议您设置一个虚拟环境。
宽大的
您可以通过多种方式安装空间。
您可以通过pip install
轻松安装,如下所示:
pip install -U spacy
或者,您可以通过conda
安装:
conda install -c conda-forge spacy
资料组
该数据集基于 Reddit 评论中的药物名称,由 spaCy 开发者创建,作为示例项目的一部分。
创建一个名为assets
的新文件夹,并从下面的存储库中下载相关的数据集:
将两个数据集放置在资产文件夹中,如下所示:
- drugs_eval.jsonl
- drugs_train.jsonl
这两个文件都基于 JSONL 格式,每个数据点都包含以下重要字段:
text
—原弦。tokens
—表示标记化单词的词典列表。spans
—标记实体的字典列表。每个条目包含开始索引、结束索引、开始令牌的 id、结束令牌的 id 和标签注释。
以下是单个数据点的示例:
{
"text": "Idk if that Xanax or ur just an ass hole",
"tokens": [
{ "text": "Idk", "start": 0, "end": 3, "id": 0 },
{ "text": "if", "start": 4, "end": 6, "id": 1 },
{ "text": "that", "start": 7, "end": 11, "id": 2 },
{ "text": "Xanax", "start": 12, "end": 17, "id": 3 },
{ "text": "or", "start": 18, "end": 20, "id": 4 },
{ "text": "ur", "start": 21, "end": 23, "id": 5 },
{ "text": "just", "start": 24, "end": 28, "id": 6 },
{ "text": "an", "start": 29, "end": 31, "id": 7 },
{ "text": "ass", "start": 32, "end": 35, "id": 8 },
{ "text": "hole", "start": 36, "end": 40, "id": 9 }
],
"spans": [
{
"start": 12,
"end": 17,
"token_start": 3,
"token_end": 3,
"label": "DRUG"
}
],
"_input_hash": -2128862848,
"_task_hash": -334208479,
"answer": "accept"
}
在构建自己的数据集时,您可以放心地忽略其他字段。相比之下,与文本分类相比,为 NER 构建数据集要繁琐得多。
预处理脚本
此外,您将需要一个预处理脚本来将 JSONL 数据集转换为 spaCy 二进制格式以进行训练。在工作目录中创建一个名为scripts
的新文件夹。
然后,创建一个名为preprocess.py
的新 Python 文件,并在其中添加以下代码:
配置文件
之后,在您的工作目录中创建另一个新文件夹,并将其命名为configs
。在其中,创建一个名为config.conf
的新配置文件,并在文件中添加以下代码。
它包含项目的所有相关配置。您可以修改的几个关键区域在training
部分:
[training]
...
patience = 1600
max_epochs = 0
max_steps = 20000
eval_frequency = 200
...
patience
—早期停止机制无改善的步数。在这种情况下,如果 1600 步后分数没有提高,它将停止训练。max_epochs
—训练时期的最大数量。max_steps
—最大训练步数。eval_frequency
—每次评估的步骤数。在这种情况下,它将每 200 步评估一次模型。
此外,您可以在training.score_weights
部分更改首选评分标准。
[training.score_weights]
ents_per_type = null
ents_f = 1.0
ents_p = 0.0
ents_r = 0.0
当前设置基于最佳f-score
。如果您更喜欢拥有一辆性能良好的precision
NER 车型,而不考虑召回,您可以对其进行如下调整:
[training.score_weights]
ents_per_type = null
ents_f = 0.0
ents_p = 1.0
ents_r = 0.0
项目文件
拼图的最后一块是项目文件。在同一个工作目录中,创建一个名为project.yml
的新文件,并用以下代码填充它:
项目文件包含以下详细信息:
- 与项目相关的元数据
- 命令
- 工作流程
只需根据自己的需求对它们进行修改。例如,在训练模型时,您可以指定不同的version
。因此,spaCy 将使用给定的version
对输出进行后缀。
vars:
config: "config.conf"
name: "ner_drugs"
version: "0.0.0"
train: "drugs_train.jsonl"
dev: "drugs_eval.jsonl"
如果您打算为配置文件和数据集使用不同的名称,请在vars.train
和vars.dev
对其进行相应的修改。
另一方面,命令是预定义的动作,可以通过以下方式触发:
spacy project run [command]
在这种情况下,项目包含以下命令:
preprocess
—将数据转换为 spaCy 的二进制格式。training
—训练 NER 模型。evaluate
—评估模型并导出相关指标。package
—将训练好的模型打包成 Python 包。
要单独运行preprocess
,只需使用以下命令:
spacy project run preprocess
除此之外,您还可以使用 workflow 将一些命令分组为一个包,如下所示:
workflows:
all:
- preprocess
- train
- evaluate
- package
您可以通过以下命令运行上面的工作流:
spacy project run all
它将按顺序运行所有指定的命令。在这种情况下,它将首先运行preprocess
命令,并以package
命令结束。
一旦你完成了它,就可以进入下一部分进行实现了。
履行
让我们利用您之前创建的命令来训练这个模型。
使用工作流
让我们使用工作流来为您完成这些技巧,而不是一个一个地运行所有的命令。
spacy project run all
它将执行以下操作:
- 将数据转换为空间二进制格式
- 训练模型
- 评估训练最好的模型
- 将训练好的模型打包成 Python 模块
检查文件夹
在实际训练过程之前,它将检查并创建以下文件夹(如果不存在):
- 资产
- 文集
- 配置
- 培养
- 剧本
- 包装
预处理数据集
preprocess
命令将加载您的 JSONL 数据集,并将其转换为corpus
文件夹中空间所需的二进制格式。您应该得到以下文件:
- drug_eval.jsonl.spacy
- drug_train.jsonl.spacy
训练模型
一旦预处理步骤完成,它将初始化所需的管道组件tok2vec
和ner
:
=========================== Initializing pipeline ===========================←[0m
[2021-06-24 16:36:47,013] [INFO] Set up nlp object from config
[2021-06-24 16:36:47,024] [INFO] Pipeline: ['tok2vec', 'ner']
[2021-06-24 16:36:47,029] [INFO] Created vocabulary
[2021-06-24 16:36:47,029] [INFO] Finished initializing nlp object
[2021-06-24 16:36:47,545] [INFO] Initialized pipeline components: ['tok2vec', 'ner']
✔ Initialized pipeline
默认情况下,spaCy 会在定型模型之前将整个数据集加载到内存中。如果您有一个大型数据集,该过程可能需要一段时间。随着训练的进行,您应该会在控制台中看到以下输出:
============================= Training pipeline =============================←[0m
ℹ Pipeline: ['tok2vec', 'ner']
ℹ Initial learn rate: 0.0
E # LOSS TOK2VEC LOSS NER ENTS_F ENTS_P ENTS_R SCORE
--- ------ ------------ -------- ------ ------ ------ ------
0 0 0.00 45.83 0.23 0.20 0.28 0.00
0 200 9.60 15156.42 0.00 0.00 0.00 0.00
0 400 21.03 1491.74 0.00 0.00 0.00 0.00
1 600 13.16 836.43 0.00 0.00 0.00 0.00
1 800 22.12 1227.63 0.00 0.00 0.00 0.00
2 1000 26.70 1117.34 20.34 82.35 11.60 0.20
3 1200 43.24 1095.54 46.64 81.94 32.60 0.47
4 1400 67.99 1048.39 59.73 78.12 48.34 0.60
5 1600 94.88 1039.05 65.81 78.03 56.91 0.66
LOSS TOK2VEC
—tok2vec
组件的损耗值。LOSS NER
—ner
组件的损耗值。ENTS_F
— f 值从 0 到 100。ENTS_P
—精度分数从 0 到 100。ENTS_R
—回忆分数从 0 到 100。SCORE
—0.0-1.0 分,小数点后两位(四舍五入)。它基于您在config.conf
中定义的training.score_weights
。
训练将继续进行,直到满足以下任一条件:
- 达到了定义的
max_epochs
- 达到了定义的
max_steps
- 达到规定的
patience
(分数没有任何提高的步数)
然后,将最佳模型和最后模型保存在training
文件夹中。
模型评估
工作流中的下一个命令是evaluate
命令,它使用最佳模型计算指标。结果将作为metrics.json
导出到training
文件夹中。
{
"token_acc":0.9999332072,
"ents_p":0.7820895522,
"ents_r":0.7237569061,
"ents_f":0.7517934003,
"speed":22459.7479096054,
"ents_per_type":{
"DRUG":{
"p":0.7820895522,
"r":0.7237569061,
"f":0.7517934003
}
}
}
p
—表示精度值r
—代表召回值f
—f1 值
打包模型
最后一步是把最好的模型打包成 Python 包。它会将打包的模型保存在packages
文件夹中,如下所示:
en_ner_drugs-0.0.0
当您打开它时,您会找到以下文件和文件夹:
- 距离
- 能量药物
- en_ner_drugs.egg-info
- 清单. in
- meta.json
- setup.py
将模型部署为 Python 包
通过在setup.py
所在的目录中运行以下命令,您可以很容易地将它作为 Python 包安装在您的虚拟环境中。
python setup.py
不仅如此,你还可以复制出en_ner_drugs
文件夹,把它当作一个普通的 Python 模块。只需将它放在 Python 应用程序的同一个目录中,并按如下方式正常调用它:
import en_ner_drugsnlp = en_ner_drugs.load()
或者,您可以通过传入以下文件路径来使用 spacy 进行加载:
import spacynlp = spacy.load('en_ner_drugs/en_ner_drugs-0.0.0')
模型加载后,您可以在任何文本上运行它,如下所示:
doc = nlp("This is a text")
输出是一个空间的 Doc 对象,它包含代表实体的ents
属性。您可以用下面的代码调用它:
doc.ents
当处理大量文本时,您应该使用pipe
批量运行它:
texts = ["This is a text", "These are lots of texts", "..."]# bad
docs = [nlp(text) for text in texts]# good
docs = list(nlp.pipe(texts))
请看下面的代码片段,它说明了如何在 Python 应用程序中使用该模型来获得药品名称的相关 NER。
运行该脚本时,您应该会在控制台上看到以下输出。
promethazine DRUG
weed DRUG
heroin DRUG
结论
让我们回顾一下你今天所学的内容。
这篇文章首先简要介绍了命名实体识别(NER)。
接下来,通过pip
或conda
安装空间。此外,还提供了训练和评估数据集作为链接。
然后,对整个实现过程进行了说明,包括将 JSONL 格式的数据集预处理为 spaCy 的二进制格式、训练模型、模型评估以及将最佳模型打包为 Python 包。
最后,它提供了一个关于 Python 部署以及如何获取相关实体及其相应标签的示例。
感谢阅读这篇文章。希望在下一个教程中再见到你!
参考
参考
CSML DSML……UCL 大学机器学*理科硕士课程的比较
找出最适合你的课程
尼克·莫里森在 Unsplash 的照片
TDLR
- 如果你有兴趣攻读博士学位,理学硕士是最好的选择
- MSc CSML 是最好的,如果你想要最多样的选择
- 如果你有兴趣进入金融领域或者从其他背景转到数据科学领域,DSML 理学硕士是最好的选择
UCL 大学是一所世界知名的大学,一直在全球排名前 10 位。具体来说,来自 UCLGatsby计算神经科学部门的 DeepMind 的成立使 UCL 成为了顶级的机器学*目的地。
本文将研究 UCL 大学最受欢迎的三门机器学*课程,并对它们进行比较,以让您更好地了解哪门课程适合您。当然,UCL 不是英国唯一一所在机器学*方面表现出色的大学:爱丁堡在自然语言处理方面表现出色,剑桥大学的哲学硕士非常适合研究,帝国理工大学的计算硕士课程让你接触机器人和逻辑学*的利基领域。
UCL 机器学*最出名的是它的行业链接(与许多行业理学硕士项目),计算金融,神经科学和医疗保健人工智能,当然还有 DeepMind。
概观
机器学*理学硕士( ML )
这是 UCL 大学的基本机器学*课程。是必修模块最少的一个(唯一一个是监督学*)。
唯一的其他限制是,学生必须要么参加图形模型要么参加概率和无监督学*(由 Gatsby 单位教授的博士水平课程)。
这个项目的学生喜欢选择涵盖人工智能医疗保健(如生物信息学、生物医学成像计算建模等)和机器人学(如机器人视觉和导航)的选修模块。
然而,由于课程主要关注计算机科学,学生无法选修统计科学系的课程,或计算金融课程(如区块链技术或算法交易)。
模块选择规则
学生有 1 个核心模块(监督学*)。学生必须选择 7 个模块,其中 2 个必须是可选模块,必须包括图形模型或概率和无监督学*。
计算统计学和机器学*理学硕士(CSML)
在三门 ML 课程中,CSML 为学生提供的模块数量最多。这包括 ML 的所有模块,也包括统计科学系教授的模块。
不过这门课的学生有两个必修模块,统计模型与数据分析(统计科学系教授)和监督学*。
一些值得注意的统计学模块提供给 CSML 的学生,但不提供给 ML 的学生,它们是统计计算、金融中的随机方法和调查的统计设计。
区块链技术和算法交易也是 CSML 学生学不到的。
模块选择规则
学生有 2 个核心模块(监督学*和统计模型和数据分析)。学生必须选择 6 个模块,其中 3 个必须是可选模块,这些模块必须包括图形模型或概率和无监督学*。
数据科学和机器学*理学硕士( DSML )
这门课程不同于其他两门 ML 课程。它只允许学生访问 ML 模块的一个子集和统计科学模块的一个子集,但是允许学生访问 DSML 特有的模块。一些著名的例子包括区块链技术、算法交易和复杂网络和 Web。
本课程有两个必修模块:即应用机器学*和统计数据科学导论。
本课程的优势在于它提供的选修模块,允许学生选修一些独特的金融课程(如金融工程和数字金融)以及独特的统计学课程(预测)。
这门课的学生无法使用的主要 ML 模块是机器人模块和盖茨比模块。
模块选择规则
学生有两个核心模块(应用机器学*和统计数据科学导论)。学生必须选择 6 个模块,其中一个必须是监督学*或机器学*导论。
关于模块选择的一个注记
本节将简要介绍在应用这些课程时需要记住的一些模块的要点。
盖茨比模块
盖茨比模块是概率和无监督学*、概率模型中的*似推理和机器学*中的高级课题。这些只提供给麻省理工学院和 CSML 大学的学生。
这些模块虽然被列为计算机科学系教授,但实际上是盖茨比单元教授的。它们是对数学要求很高的课程,一般建议学生不要选修。
机器学*高级课题是一门涵盖机器学*研究当前研究趋势的课程。
概率和无监督学*和概率模型中的*似推理是盖茨比单位博士生的必修课。就内容而言,它们涵盖了与图形模型相同的范围(这就是为什么你不能同时采用图形模型和概率和无监督学*),但它们更深入。
它们实际上是模块,每个模块运行半个学期。所以这个想法是,前半学期你做概率和无监督学*,后半学期你做概率模型中的*似推理。但是,有可能只拿其中一个。
财务模块
如前所述,ML 上没有面向学生的金融模块。CSML 只有两个金融模块,它们是金融中的随机方法第一和第二部分(由统计科学系教授)。
DSML 大学提供了一系列由计算机科学系教授的金融模块。分别是区块链技术、算法交易、金融工程和数字金融。然而,由于某种原因,金融中的随机方法模块在 DSML 上不可用。
其他独特的 DSML 模块
DSML 大学提供三门独特的计算机科学课程,这些课程在麻省理工学院和 CSML 大学都没有。这些是多智能体人工智能、信息检索和数据挖掘和复杂网络和 Web 。
这些课程都是 100%的课程,实用性很强。令人惊讶的是,它们在其他 ML 课程中是不可用的。
模块的完整列表
2021/2022 年可用的所有模块的完整列表如下:
一张显示在 ML,CSML 和 DSML 所有可用课程的表。粉色代表盖茨比单元教授的模块,蓝色代表计算机科学教授的模块,黄色代表统计科学教授的模块(图片由作者提供)
结论
我认为课程的选择取决于你追求什么。我总结了以下我认为最适合的课程:
ML
- 如果你有兴趣攻读博士学位。
- 没有限制意味着你可以集中你的主题选择和专业。
- 如果你对什么都想学一点,但不介意错过 CSML 和 DSML 学生可以学*的模块。
T5【CSML】T6
- 如果您不确定想要学*的模块,但希望有最多的选项。
- 在本课程中,您可以使用盖茨比模块、机器人模块和一些统计科学模块。
DSML
- 如果你对金融感兴趣,或者正在寻找最“行业”的课程。
- 如果你想对每件事都有所了解,但又不想学*盖茨比模块,并且对机器人技术不感兴趣。
我希望这有助于那些你正在寻找做任何这些课程!祝你申请好运,记住不要紧张!
Python 中的双轴绘图
本文讨论了如何向 Python 绘图添加辅助轴
克里斯·利维拉尼在 Unsplash 上的照片
有时,我们可能需要将两个不同比例的变量添加到绘图轴上。例如,我们希望在 y 轴上显示人均 GDP(单位为美元)和年 GDP 增长率%,在 x 轴上显示年份。换句话说,我们需要可视化多年来人均 GDP($)和 GDP 增长率的趋势。20 世纪 80 年代以来,人均国内生产总值(美元)和国内生产总值增长率有不同的比例.使用相同的 y 轴绘制它们会破坏另一个。
作者图片
在上图中,我们可以看到,年增长率的趋势完全被人均国内生产总值(美元)所破坏。在下图中,我们看到在 y 轴上使用对数刻度也没有帮助。
对数 y 轴(图片由作者提供)
这使得年增长率(%)必须有一个辅助 y 轴。为了说明新增的副轴,我们将使用下面显示的数据框(名为“gdp”),其中包含 2000 年至 2020 年的人均 GDP($)和年增长率(%)数据。
gdp 数据框(图片由作者提供)
在上面的代码中,我们使用 twinx()函数创建了一个名为“ax2”的辅助轴。twinx()创建一个共享 x 轴的辅助轴。还有一个名为 twiny()的函数用于创建一个共享 y 轴的次轴。在下图中,我们可以清楚地看到人均 GDP)和年增长率(%)的趋势。
带副轴的绘图(作者提供的图片)
在下一个例子中,我们将绘制 Nifty(印度的一个股票指数)的趋势和成交量。在本例中,我们将对指数值使用折线图,对成交量使用条形图。下面是我们将在本例中使用的数据框(名为“nifty_2021”)的前几条记录。
nifty_2021 数据框(图片由作者提供)
上面的代码类似于我们之前看到的代码。然而,有一些不同之处需要注意。
- 在上面的代码中,我们使用了 pandas plot()来绘制音量条图。这是因为 Matplotlib 的 plt.bar()函数可能无法正常处理不同类型的绘图。因此,我更喜欢 Matplotlib 只用于线图。
- 我们使用了 ax2.plot(ax.get_xticks() 而不是ax2 . plot(nifty _ 2021[' Date ']。这是因为使用 nifty_2021['Date'] 是将图向右移动或者根本不显示线图。
作者图片
本文到此结束。我们已经讨论了不同比例的变量在一起绘图时可能会产生的问题,并看到了添加一个副轴是如何解决问题的。我们还看到了如何使用次轴绘制折线图和条形图。
双重引导已死:验尸
只需一个命令,就可以将任何机器变成开发人员工作站。
图片来自 Pixabay
两年前,我有一个问题:一方面,我工作的公司是一个重度微软产品用户。微软办公软件对我的工作至关重要。同时,我更喜欢在 Linux 机器上进行编程工作。我的 Linux 环境中的一个 Windows 虚拟机运行太慢。
所以,我想出的解决方案很简单。我决定双启动 Ubuntu 和 Windows 10。很长一段时间里,双靴都是答案。然而,一百万次上下文切换之后,Linux 的 Windows 子系统,简称 WSL,发布了。我决定尝试一下,并开始将我的部分工作流程转移到 Windows 和 WSL。
尽管如此,许多东西还是不见了。但是 WSL 2 来了,它似乎是一个游戏改变者。一年后,WSL 2 仍然没有实现它的承诺或潜力。也许有一天会的,但我现在需要一些东西。此外,由于我是远程工作,我需要一些可移植的东西,一个我可以随身携带并在 10 分钟内设置好的开发环境。
这个故事介绍了解决这个问题的两个解决方案,以及如何将它们结合起来以获得最佳效果!到目前为止,这种组合解决方案对我来说非常有效,但是我很想在下面的评论区听到你的建议!
学*率 是为那些对 AI 和 MLOps 的世界感到好奇的人准备的简讯。你会在每周五收到我关于最新人工智能新闻和文章的更新和想法。订阅 此处 !
便携式基站
当我坐下来完成一些工作时,我需要三样东西:
- 我最喜欢的代码编辑器
- 我正在做的项目
- 项目的依赖项
我是机器学*工程师,所以大部分时间都在摆弄 Python 脚本。我最喜欢的代码编辑器是 Visual Studio 代码。当我坐在一个全新的工作站前时,这是一个我可以遵循的算法:
- 从 GitHub 克隆项目
- 创建一个 python 虚拟环境(
python -m venv venv
) - 激活环境(
source venv/bin/activate
) - 安装依赖项(
pip install -r requirements.txt
)
看起来很简单,对吧?一点也不!首先,我是否安装了 Git?如果没有,我必须安装和配置它。我是否安装了正确版本的 Python?如果没有,我得安装pyenv
,安装我需要的 Python 版本,重新开始。我安装了venv
吗?这是一场噩梦。
所以,我们用 Docker 吧。让我们将项目及其依赖项打包到 docker 映像中,很快就可以成为开发容器。但是我是否安装了 Docker 并配置为与注册表对话?这开始让我心烦。
我需要一个便携式底座。一些我可以快速设置的东西。一些已经具备所有核心元素的东西:Git、Docker、Python 甚至是用于测试我的部署的单节点 Kubernetes 集群。
输入流浪者。要获得便携式基础工作站,我必须执行一次以下算法的步骤:
- 从一个基本的 Ubuntu 流浪盒开始
- 安装和配置我需要的一切:Git,Docker,K3s,我需要的其他 Debian 包等等。
- 将我的环境打包到一个新的流浪盒中(
vagrant package --output workstation.box
) - 上传盒子到流浪云
现在,当我坐在一个全新的工作站前,我要做的就是安装好游民和 VirtualBox,从游民云上拉下我的游民盒子。这需要不到 10 分钟的时间,并且以后永远可用。
代码编辑器
下一步是解决代码编辑器的问题。当我在 VirtualBox 中运行 Ubuntu 时,我更喜欢使用不带 GUI 的 Ubuntu。它只是让事情变得更快更容易。终端环境绰绰有余。
但是,我之前说过,我最喜欢的代码编辑器是 VS Code。那么,我现在如何在虚拟机内部使用它呢?我应该直接使用 ViM 吗?
虽然 ViM 真的很棒,而且我每天都在使用它,尤其是当我在 Jupyter 内部工作时,我仍然更喜欢使用 VS 代码。
Visual Studio 代码是一个免费的、轻量级的、跨平台的代码编辑器。它可能不像 IntelliJ Idea 或 T2 py charm 那样是一个成熟的 IDE,但它是一个强大的、不受干扰的工具,拥有专门的粉丝群和蓬勃发展的生态系统。
更重要的是,VS 代码有一个扩展,它将保持您的环境整洁,并且您的项目是隔离的和可维护的。它可以让你在任何地方从事任何项目。一个可以让你与团队中的任何人毫无争议地合作的平台。欢迎来到远程开发的美丽世界。
Visual Studio 代码的远程开发扩展是您需要安装在本地 VS 代码设置中的唯一扩展。使用它,你将能够在你想要的开发环境中工作;需要更多资源吗?一个特定的 Linux 发行版?专门的硬件?没问题。
因此,您可以使用它的远程 SSH 变种来连接到您的 VM。此外,您在 VM 内部安装的任何扩展都安装在那里,因此它不会污染核心 VS 代码安装。要了解更多关于如何设置和使用它的信息,请阅读下面的故事:
摘要
两年前,我有一个问题:一方面,我工作的公司是一个重度微软产品用户。同时,我更喜欢在 Linux 机器上进行编程工作。
从长远来看,双引导对我来说并不奏效,所以我转向了 WSL 2。一年后,WSL 2 仍然没有实现它的承诺或潜力。我需要一些可移植的东西,一个我可以随身携带的开发环境,并在 10 分钟内设置好。
我的解决方案总结为以下五个步骤:
- 安装 VirtualBox
- 安装流浪者
- 安装 VS 代码和远程扩展
- 从流浪云中取出我的“工作站”盒子
- SSH 进入虚拟机并开始工作
这个解决方案是可移植的,你设置一次,它就永远在那里。此外,它的设置需要不到 10 分钟,它很容易维护和扩展。请随意采用它,但最重要的是,请随意建议我可以做些什么来改进它。作为一名 ML 工程师,我总是在寻找改进算法的机会。
关于作者
我叫迪米特里斯·波罗普洛斯,我是一名为阿里克托工作的机器学*工程师。我曾为欧洲委员会、欧盟统计局、国际货币基金组织、欧洲央行、经合组织和宜家等主要客户设计和实施过人工智能和软件解决方案。
如果你有兴趣阅读更多关于机器学*、深度学*、数据科学和数据运算的帖子,请关注我的 Medium 、 LinkedIn 或 Twitter 上的 @james2pl 。
所表达的观点仅代表我个人,并不代表我的雇主的观点或意见。
二元性:通过一个例子解释优化的无名英雄
对二元性在运筹学领域的目标的深刻理解
安托万·道特里在 Unsplash 上拍摄的照片
随着机器学*和人工智能领域的进步,与优化相关的概念变得比以往任何时候都更加相关。优化中使用的概念对于设计旨在从大量数据中进行推理的算法至关重要。对于在运筹学领域工作的人来说,一个很难理解的话题是对偶原理。我们在这篇文章中的目标是理解这个话题以及二元性的目标是什么。
让我们从一个简单的线性规划问题开始,如下所示
问题 P1:
服从于:
(1)、(2)和(3)表示问题的约束。如果没有与变量相关的约束,问题 P1 的最优解将是无穷大(因为这是在那些情况下目标函数的最大可能值)。
但是既然存在约束,让我们开始使用一些策略来机智地猜测最优解应该是什么。
策略 1
将等式(2)两边都乘以 3 得到:
但是因为我们知道两个变量都是正的(≥0),所以它自动地遵循
因此,我们现在有了我们想要最大化的目标函数的新上限(36)。现在,36 代表目标函数最大值的新上限(以前上限是无穷大)。
策略 2
让我们试试另一个练*。将等式(1)乘以 6 得到:
但是,因为我们知道两个变量都是正的(≥0),它自动地再次遵循
同样,我们对目标函数有一个更好的上限(30)。30 代表现在目标函数最大值的新上限(以前是 36,我们从策略 1 中得到)。
请注意,我们是如何通过仔细处理约束来慢慢降低目标函数的上限的。每一次这样的约束(策略)操作都给出了问题 P1 的目标函数可以采用的相应上限。我们的工作是找出目标函数的最低可能上限,它将最终代表它可以取的最大值。这是二元性原理试图内在实现的。
策略的概括
显然,到目前为止,我们一直在每个策略中用一些随机选择的常数乘以约束。让我们概括一下到目前为止我们一直在做的事情。
将等式(1)乘以 a1,将等式(2)乘以 a2,得到(a1 和 a2 是正值变量)
注意,(a1,a2)的值分别在策略 1 &策略 2 中已经是(0,3) & (6,0)。
将等式(4)和(5)相加,我们得到:
因此,P1 的目标函数的上限表示为:
只要满足以下约束,我们希望通过尝试不同的(a1,a2)值来使最小化:
其中等式(6)和(7)的右手侧代表问题 P1 的目标函数的系数。这个以最小化为目标的新问题被称为对偶问题,P1 和 P1 本身被称为原始问题。
对偶问题(P2)因此变成:
服从于:
我们可以用下面的性质直接写出原问题的对偶:
- 如果原始问题是一个最大化问题,对偶问题就变成了一个最小化问题(就像我们在 P1 和 P2 的例子),反之亦然。
- 对偶问题中变量的数量将等于原始问题中约束的数量(每个约束一个变量)。因为原始有 2 个约束(等式(1)和(2)),对偶有 2 个变量(a1 和 a2)。
- 对偶问题中约束的数量将等于原始问题中变量的数量(每个变量一个约束)。因为原始模型有 2 个变量(x1 和 x2),对偶模型有 2 个约束(等式(6)和(7))。
- 原始约束的右侧值成为对偶目标函数的系数(5 & 12 是原始约束的右侧值,因此也成为对偶目标函数的系数),反之亦然。
结论
每一个线性规划问题都可以看作是一个原始问题,可以转化为相应的对偶问题。这意味着解决 P1 问题(原始)等价于解决 P2 问题(P1 的对偶)。
这是因为 P1 的最优解(即目标函数在 P1 的最大值)与 P2 的最优解(即目标函数在 P2 的最小值)相同。
因此,在运筹学领域,对偶的概念被大量用于解决优化问题,其中原始问题可能相对难以解决,或者甚至在使用列生成技术的情况下,基于对偶的可行性状态来评估我们是否已经在原始问题中达到最优解。
这篇文章的目的是让人们直观地理解二元性的基本概念以及它实际上打算做什么,而不仅仅是从数学的角度探索这个概念。
Python 中的 Dunder/Magic 方法
Dunder 方法的名称前面和后面都有双下划线,因此被称为 dunder。它们也被称为魔术方法,可以帮助覆盖自定义类的内置函数的功能。
1.介绍
为类实现 dunder 方法是一种好的多态形式。如果您曾经在 Python 中创建了一个类并使用了 init 函数,那么您已经在使用 dunder 方法了。
2.目录
1.介绍
2.目录
3.先决条件
4.为什么我们需要邓德方法?
5.我们的定制类
6.我们班的邓德方法
7.更多的邓德方法
8.结论
3.先决条件
在我们继续之前,了解以下内容非常重要:
- 对使用 Python 进行面向对象编程的基本理解。
- 在 Python 中使用类的经验。
- 熟悉 len、get、set 等内置函数。
4.为什么我们需要邓德方法?
考虑这样一种情况,我们有以下类:
class point:
x = 4
y = 5p1 = point()
print(p1)
print 语句将打印类似于<__main__.point object at 0x7fb992998d00>
的内容。但是,我们可能希望 print 语句以格式(4,10)
显示一些内容。我们可以通过重写我们类的__str__
方法来实现这一点。
我们也可以覆盖其他方法,比如len, +, []
等。我们将创建一个新的类,并覆盖本文中的许多内置函数。
5.我们的定制类
class softwares:
names = []
versions = {}
这个类将用于保存软件及其版本的列表。names
是一个存储软件名称的列表,versions
是一个字典,其中键是软件名称,值是版本号。默认情况下,所有软件都以版本 1 开始。
6.我们班的邓德方法
在继续之前,请确保您的缩进是正确的。下面将要讨论的方法是属于我们创建的类的方法,必须适当缩进。
6.1.初始化
如果您曾经使用过类,那么您一定已经使用过这种方法。init
方法用于创建类的一个实例。
def __init__(self,names):
if names:
self.names = names.copy()
for name in names:
self.versions[name] = 1
else:
raise Exception("Please Enter the names")
上面定义的init
方法接受一个名称列表作为参数,并将其存储在类的‘names
列表中。此外,它还填充了versions
字典。我们还在names
列表上做了检查。
如果列表为空,则会引发异常。下面是我们如何使用init
方法。
p = softwares(['S1','S2','S3'])
p1 = softwares([])
第一条语句可以正常工作,但是第二行将引发一个异常,因为一个空列表作为参数传入。
6.2.潜艇用热中子反应堆(submarine thermal reactor 的缩写)
当我们想在 print 语句中使用类的实例时,str
方法很有用。如前所述,它通常返回一个内存对象。但是我们可以覆盖str
方法来满足我们的需求。
def __str__(self):
s ="The current softwares and their versions are listed below: \n"
for key,value in self.versions.items():
s+= f"{key} : v{value} \n"
return s
上面的str
方法返回软件及其版本。确保该函数返回一个字符串。下面是我们调用该方法的方式。
print(p)
6.3.设置项目
在字典中赋值时,调用setitem
方法。
d = {}
d['key'] = value
在setitem
方法的帮助下,我们可以给我们类的实例一个相似的特性。
def __setitem__(self,name,version):
if name in self.versions:
self.versions[name] = version
else:
raise Exception("Software Name doesn't exist")
上面的方法是要更新软件的版本号。如果找不到该软件,它将引发一个错误。
在第 3 行中,我们使用了字典的内置setitem
方法。
我们可以通过以下方式调用setitem
方法:
p['S1'] = 2
p['2'] = 2
第一行将软件 S1 的版本更新为 2。但是第二行将引发一个异常,因为软件 2 不存在。
6.4.getitem
getitem
方法类似于setitem
方法,主要区别在于当我们使用字典的[]
操作符时会调用getitem
方法。
d = {'val':key}
print(d['val'])
我们类的实例也可以被赋予类似的特性。
def __getitem__(self,name):
if name in self.versions:
return self.versions[name]
else:
raise Exception("Software Name doesn't exist")
上面的方法本质上是返回软件的版本。如果找不到该软件,它将引发一个异常。为了调用getitem
方法,我们可以编写下面一行代码。
print(p['S1'])
print(p['1'])
第一行将打印 S1 的版本。但是,第二行将引发一个异常,因为 1 不存在。
6.5.交货
delitem
与setitem
和getitem
方法相同。为了避免重复,我们将继续讨论实现和用例。
def __delitem__(self,name):
if name in self.versions:
del self.versions[name]
self.names.remove(name)
else:
raise Exception("Software Name doesn't exist")
delitem
方法从字典和列表中删除软件。
它可以按如下方式使用。
del p['S1']
6.6.低输入联网(low-entry networking 的缩写)
在字典中,len
方法返回列表中元素的数量或字典中键值对的数量。
我们也可以为我们的类定义一个len
方法。
def __len__(self):
return len(self.names)
我们类的len
方法返回软件的数量。您可能已经注意到,我们正在使用 list 的内置len
方法来返回软件的数量。
我们类的len
方法可以以如下方式使用。
print(len(p))
6.7.包含
使用in
操作符时使用contains
方法。返回值必须是布尔值。
def __contains__(self,name):
if name in self.versions:
return True
else:
return False
该方法检查该名称是否在字典中找到。为此,我们将使用字典内置的contains
方法。
if 'S2' in p:
print("Software Exists")
else:
print("Software DOESN'T exist")
上面的代码打印 if 块中的语句,因为软件 S2 存在于versions
字典中。
6.8.完全码
class softwares:
names = []
versions = {}
def __init__(self,names):
if names:
self.names = names.copy()
for name in names:
self.versions[name] = 1
else:
raise Exception("Please Enter the names")
def __str__(self):
s ="The current softwares and their versions are listed below: \n"
for key,value in self.versions.items():
s+= f"{key} : v{value} \n"
return s
def __setitem__(self,name,version):
if name in self.versions:
self.versions[name] = version
else:
raise Exception("Software Name doesn't exist")
def __getitem__(self,name):
if name in self.versions:
return self.versions[name]
else:
raise Exception("Software Name doesn't exist")
def __delitem__(self,name):
if name in self.versions:
del self.versions[name]
self.names.remove(name)
else:
raise Exception("Software Name doesn't exist")
def __len__(self):
return len(self.names)
def __contains__(self,name):
if name in self.versions:
return True
else:
return False
7.更多的邓德方法
在看更多的 dunder 方法之前,让我们创建一个新的类。
class point:
x = None
y = None
def __init__(self, x , y):
self.x = x
self.y = y
def __str__(self):
s = f'({self.x},{self.y})'
return sp1 = point(5,4)
p2 = point(2,3)
我们创建了一个类点,它基本上是一个 2D 点。该类有一个init
方法和一个str
方法。我们还创建了该类的几个实例。
7.1.增加
使用+
操作符时调用add
方法。我们可以为我们的类定义一个定制的add
方法。
p1 + p2
等于p1._add__(p2)
def __add__(self,p2):
x = self.x + p2.x
y = self.y + p2.y
return point(x,y)
上述方法将第一个point
实例和第二个point
实例的 x 和 y 坐标相加。它将创建一个新的point
实例,然后返回它。
p3 = p1 + p2
上面的代码行调用了add
方法。
7.2.iadd
iadd
方法类似于add
方法。使用+=
操作符时调用
def __iadd__(self,p2):
self.x += p2.x
self.y += p2.y
return self
上面的方法只是通过添加p2
的坐标来更新一个实例的坐标。确保你正在返回self
,否则它将返回 None,不能按预期工作。
p1 += p2
print(p1)
上面的方法调用了iadd
方法。
7.3.其他操作员
__sub__(self,p2)
( -)__isub__(self,p2)
( -=)__mul__(self,p2)
( *)__imul__(self,p2)
( *=)__truediv__(self,p2)
( )__itruediv__(self,p2)
( =)__floordiv__(self,p2)
( \)__ifloordiv__(self,p2)
( =)
7.4.呼叫
当调用像func()
这样的函数时,我们调用的是call
方法。
如果我们为我们的类准备了一个call
方法,我们可以做以下事情:
p1()
p2()
下面是一个调用方法示例:
def __call__(self):
print(f"Called Point {self.x},{self.y}")
7.5.完全码
class point:
x = None
y = None
def __init__(self, x , y):
self.x = x
self.y = y
def __str__(self):
s = f'({self.x},{self.y})'
return s
def __add__(self,p2):
print("In add")
x = self.x + p2.x
y = self.y + p2.y
return point(x,y)
def __iadd__(self,p2):
self.x += p2.x
self.y += p2.y
return self
def __isub__(self,p2):
self.x -= p2.x
self.y -= p2.y
return self
def __imul__(self,p2):
self.x *= p2.x
self.y *= p2.y
return self
def __itruediv__(self,p2):
self.x /= p2.x
self.y /= p2.y
return self
def __ifloordiv__(self,p2):
self.x //= p2.x
self.y //= p2.y
return self
def __call__(self):
print(f"Called Point {self.x},{self.y}")
8.结论
邓德方法确实很神奇,可以帮助你提高类的功能。它们可以帮助您定制您的类并重新定义内置方法。虽然我们已经讨论了一些最常见的 dunder 方法,但是还有更多像 hash 这样真正有用的 dunder 方法。 hash 用来给你的类添加一个哈希算法。这允许您的类的对象被用作字典中的键,并且具有 O(1)的查找时间。你可以在这里找到更多关于 hash 和其他 dunder 方法的信息。
快乐学*!😃
在LinkedIn上联系我
我最*开始了一个修改版的#100daysofcode 挑战。我的目标是每天写与 Python、数据科学或编程相关的内容。关注我在 推特 , 中 ,dev . to,hash node,或者 我的 WordPress 博客
原载于 2021 年 3 月 23 日section . io
Dwight Schrute 教授我们离散概率分布
一篇不是关于正态分布的文章
作者使用imgflip.com/memegenerator/Dwight-Schrute制作的模因
介绍
关于正态分布的所有宣传,数据科学的新手可能会错误地认为数据科学家只关心正态分布的。
虽然正态分布可以说是最重要的(也是最被滥用的)概率分布,但它并不总是解决商业问题的最佳工具,也不是最佳假设。
假设你在邓德米夫林纸业公司工作。区域经理助理 Dwight K. Schrute 给你 100 个客户的档案。Schrute 先生想知道以下情况:
- 假设完成销售的概率为 10%,在 100 名客户中,有 5 名客户接受 Dunder Mifflin 的销售的概率是多少?
- 在销售部做出第一笔销售之前,我们能指望有多少客户会忽视邓德米夫林?
- 我们想给从 Dunder Mifflin 购买的 5 个客户一个 Scrantonicity 专辑。第十个客户端拿到最后一张专辑的概率是多少?
多亏了中心极限定理,我们可以用正态分布来回答上述所有问题。然而,我们可能需要比德怀特给我们的更多的信息来回答其中至少一个问题。
一个更好、更简单的解决方案是对离散随机变量使用概率分布。
在下文中,我解释了离散随机变量的三种概率分布:二项式分布、几何分布和负二项式分布。
离散随机变量的概率分布
离散随机变量是取整数值的随机变量。这与连续随机变量相反,其值包括所有实数(某些条件可能适用)。
让我们来看看我们的第一个概率分布。
二项分布
随着样本量的增加,二项分布越来越接*正态分布。
考虑 n 个独立事件的集合
每个事件(或试验)的成功概率 p 是相同的。
设 Y 为 n 个独立事件的总成功数。然后,我们说 Y 是一个遵循二项式分布的随机变量。
在 n 试验中 y 成功的概率是
Y 的期望值为
E[Y] = np
方差是
Var[Y] = np(1-p)
例子
我们可以用二项分布来回答德怀特的第一个问题。
假设完成销售的概率为 10%,那么在 100 名客户中,有 5 名客户接受 Dunder Mifflin 的销售的概率是多少?
我们有
- p = 0.10
- n = 100
- y = 5
因此,我们的概率质量函数变成了
我们的概率大约是 3.39%。
现在,如果我们想知道至少有* 5 个客户与 Dunder Mifflin 达成交易的概率会怎样?*
如果 n 保持不变,则
相当于,
上述计算揭示了五个以上的客户结束与 Dunder Mifflin 的通话的概率是 97.63%。
几何分布
再次假设,我们有一组 n 个独立的事件,它们共享相同的成功概率。
设 Z 为第一次成功之前的失败次数 k 。
然后,Z 具有带有概率质量函数的几何分布
这给了我们在第一次成功之前进行 k 次 T21 试验的可能性。
期望值和方差是
E[Z] = 1/p,并且
Var[z] = (1 — p)/p,
分别是。
例子
我们可以用几何分布来回答德怀特的第二个问题。
在销售部做出第一笔销售之前,我们能指望有多少客户会忽视邓德米夫林?
因为 p = 0.10,
E[Z] = 1/0.10
E[Z] = 10
因此,我们可以预计,在销售部门完成第一笔销售之前,会有 10 个客户忽略 Dunder Mifflin。
负二项分布
设 r 为成功次数。
负二项分布给出了在 k 次失败后 r 次成功的概率。
更具体地说,我们想要找到这样的概率,第一,在k+r-1 试验中有r-1 次成功,第二,在 k + r. 试验中有一次成功
比如说 r = 3,k = 10。然后,我们想找出十二次试验中两次成功的概率,然后在第十三次试验中第三次成功的概率。【注意:我们不是估计三次成功十次失败的概率。我们正在估计第十三次试验第三次成功的概率。**
设 W 为第 r 次成功前的试验次数。负二项分布的概率质量函数为
其中 k 为失败次数 r— 1 为成功次数。因此, k+ r— 1 为试验次数。
期望值和方差是
E[W] = r(1 — p)/p,并且
Var[W] = r(1 — p)/p,
分别是。
例子
我们可以用负二项分布来回答德怀特的第三个问题:
我们想给从 Dunder Mifflin 购买的 5 个客户一个 Scrantonicity 专辑。第十个客户端拿到最后一张专辑的概率是多少?
我们还知道 r = 5,k = 5。
因此,
这给了我们 0.00744,或 0.744%
笔记
负二项分布有不同的定义。在本文中,我将负二项分布定义为
在 k 次失败后,第 r 次成功的概率。
然而,我们也可以将负二项分布定义为
- 在 k 次失败后,第次成功的概率**
- 在 r 成功之后第 k 次失败的概率。
这些定义产生了均值的替代方程。
如果我们使用第一个定义,那么我们的平均值就变成了
如果我们使用第二个定义,那么我们的平均值就变成了
在计算期望值之前,一定要知道你的主管想要什么信息!
结论
在本文中,我研究了三种离散概率分布:
- 二项式分布
- 几何分布
- 负二项分布
显然,还有更多——泊松、超几何、多项式等等。
您不需要了解每个发行版——我当然不需要!—但是你知道的越多,你解决商业问题的工具就越多。
动力双大轴中的动力轴
你曾经尝试过动态地改变你的视觉轴线吗?这比看起来容易,有了 DAX 的小魔法
https://www . pexels . com/photo/eight-electrical-metric-meters-942316/
几个月前,我写了一篇关于 Power BI 中的动态过滤的博文——以及如何根据用户的选择在一个视图中显示不同的度量——而不使用书签!
上周,我的客户向我提出了类似的要求。事实上,这一次的请求正好相反——他们希望看到相同的指标(度量),但是从不同的角度——例如,每个国家和品牌的总销售额,这取决于用户的选择。
同样,这也可以通过使用书签和在不同页面状态之间切换来实现。然而,我希望找到一种不同的、灵活的、更具可扩展性的解决方案,以防将来他们想要扩展可能的“视角”列表,例如,通过在该列表中添加客户性别或地区。
我喜欢称这个解决方案为:DAXis —因为我们用 DAX 实现了一个动态轴:)
搭建舞台
在寻找可能的解决方案时,我看到了 Kasper de Jonge 写的这篇很棒的博客文章,它基本上给了我一个如何处理这个请求的想法。
像往常一样,我将使用 Contoso 示例数据库进行演示。
作者图片
在此图中,您可以看到我的图像显示了每个品牌的总销售额。这个想法是“以某种方式”使用户能够将轴切换到国家,在视觉上保持现有的措施。不允许书签:)
第一步是生成一个新表,它实质上是我们数据模型中品牌和国家列的所有不同值的一个笛卡尔乘积。这个表将在以后用于构建我们的视觉轴。
Brands & Countries = UNION(
CROSSJOIN(VALUES('Product'[BrandName]),ROW("Axis","Brands")),
CROSSJOIN(VALUES(Geography[RegionCountryName]), ROW("Axis","Countries")
)
)
我刚刚将 BrandName 列重命名为“Value”,因为它不仅包括品牌,还包括国家。
作者图片
回到我们的报告,让我们将该表中的轴放入切片器,并将 BrandName 作为我们的视图中的轴:
作者图片
正如您所看到的,我们得到了每个轴类别的总计。这是因为我们的主要度量(销售额)是对来自在线销售表的值求和,而该表与我们新创建的品牌和国家表之间没有关系。
作者图片
因此,我们的主要衡量标准(销售额)需要重写,以显示正确的结果。我们将利用tretasDAX 函数的用法。以最简单的方式, TREATAS 将表表达式的结果作为过滤器应用于不相关的表中的列。这个函数带有的一些限制,但是它应该在我们的特定场景下工作:
Sales Amt TREATAS =
IF(
HASONEVALUE('Brands & Countries'[Axis]),
SWITCH(VALUES('Brands & Countries'[Axis])
,"Countries", CALCULATE(SUM('Online Sales'[SalesAmount])
,TREATAS(VALUES('Brands & Countries'[Value])
,Geography[RegionCountryName]))
,"Brands", CALCULATE(SUM('Online Sales'[SalesAmount])
,TREATAS(VALUES('Brands & Countries'[Value])
,'Product'[BrandName]))
)
)
在这种情况下, TREATAS 将把过滤器从我们新创建的表推到一个“真正的”维度表中!而且,一旦我将这个新的度量拖到一个可视对象上,我就能够根据用户在切片器中的选择动态地更改 y 轴:
作者图片
多酷啊!感谢 Kasper 的绝妙想法:)
结论
正如您可能看到的,我们为一个常见的业务请求找到了一个非常酷和优雅的解决方案。
对于几乎任何问题,不仅仅是与 BI 相关的电源,都有多种有效且合理的解决方案。我们可以使用按钮和书签来处理这个请求,但是从长远来看,我相信这个具有动态轴和独立表的解决方案提供了更多的可伸缩性,并且更易于维护。
感谢阅读!
多元时间序列预测的动态模式分解
使用 Numpy 的 DMD 预测的 Python 实现
动态模式分解(DMD)是由 Peter Schmid 于 2008 年开发的一种数据驱动的降维算法(论文发表于 2010 年,见[1,2]),类似于矩阵分解和主成分分析(PCA)算法。给定一个多元时间序列数据集,DMD 计算一组动态模式,其中每个模式都与一个固定的振荡频率和衰减/增长率相关联。由于每种动态模式下固有的时间行为,DMD 确实不同于通常使用的降维算法,如 PCA。DMD 允许人们用物理上有意义的模式来解释数据的时间行为。一个重要的特征是 DMD 能够执行多元时间序列预测。在现有的工作中,DMD 模型在时空流体流动分析中有广泛的应用(例如见图 1)。
图一。流体流动数据集上 DMD 的示意图。DMD 书[3]中的一个例子。
在本帖中,我们将介绍 DMD 算法的预备知识,并讨论如何用 Numpy 在 Python 中再现 DMD。然后,为了更好地理解 DMD,我们设计了一个玩具示例实验来执行多变量时间序列预测。下面的内容玩得开心!😃
如果你对图 1 所示的流体动力学分析感兴趣,在 https://medium.com/p/d84065fead4d 的也有一篇博文。这篇文章介绍了如何用 Python 中的张量分解算法重建流体动力学。另一篇关于用 DMD 模型从流体流动数据中发现空间模式的博文在https://medium . com/@ Xinyu . Chen/reproducing-dynamic-mode-decomposition-on-fluid-flow-data-in-python-94 b 8d 7 E1 f 203。
DMD 的模型描述
DMD 与向量自回归(VAR)模型有很强的联系。VAR 是一个经典的统计模型,用于捕捉多元时间序列数据的协同进化模式,在经济学和金融学中有着广泛的应用。给定时间序列数据 X 的大小 N -by- T 其中 N 是变量的数量, T 是时间步长的数量,则在任何时间步长 t 中,一阶 VAR 或 VAR(1)的形式为
其中 x t 表示时间 t 中的快照向量,大小为N-1。 A 是系数矩阵,大小为 N -by- N 。
在 VAR(1)模型中,建模目标是找到一个表现良好的系数矩阵,并使用它来表示时间相关性。在标准的 DMD 模型中,我们也采用 VAR(1)的形式。为了计算系数,第一个脉冲是将上面的等式重写如下,
其中我们在等式中定义了以下矩阵:
与 VAR(1)不同,DMD 不显式计算系数矩阵。在 DMD 中,有一个低秩结构来逼* VAR(1)中的系数矩阵,它就是 Koopman 矩阵。
DMD 算法
在 DMD 中,获得 Koopman 矩阵并实现预测任务并不困难。DMD 中最重要的工具是奇异值分解和特征值分解。为了计算库普曼矩阵,只有两个步骤应该遵循:
- 对数据矩阵 X 1 进行奇异值分解:
其中 U 由左奇异向量组成, V 由右奇异向量组成,σ由其对角线上的奇异值组成。
- 使用某个预定义的秩 r 实现截断奇异值分解,并通过下式计算库普曼矩阵
如上所述,DMD 可以通过动态模式探索可解释的时间行为。如果想要计算动态模式,请遵循以下步骤:
- 对库普曼矩阵进行特征值分解:
其中 Q 由特征向量组成,φ由特征值组成。
DMD 模式也称为动态模式,是特征向量。特征值总是以复共轭对的形式出现,可以解释为 DMD 谱。
在上面,我们得到了库普曼矩阵,动态模式,DMD 谱。一个问题是如何进行多元时间序列预测。我们可以采用以下两个等式来回答这个问题:
现在,我们有了 VAR(1)中提到的系数矩阵 A 。因此,可以实现时间序列预测任务。下面是 DMD 算法的 Python 代码。我们通过使用 Numpy 来重现它,这样就很容易理解了。
import numpy as npdef DMD(data, r):
"""Dynamic Mode Decomposition (DMD) algorithm."""
## Build data matrices
X1 = data[:, : -1]
X2 = data[:, 1 :]
## Perform singular value decomposition on X1
u, s, v = np.linalg.svd(X1, full_matrices = False)
## Compute the Koopman matrix
A_tilde = u[:, : r].conj().T @ X2 @ v[: r, :].conj().T * np.reciprocal(s[: r])
## Perform eigenvalue decomposition on A_tilde
Phi, Q = np.linalg.eig(A_tilde)
## Compute the coefficient matrix
Psi = X2 @ v[: r, :].conj().T @ np.diag(np.reciprocal(s[: r])) @ Q
A = Psi @ np.diag(Phi) @ np.linalg.pinv(Psi)
return A_tilde, Phi, A
输出包括库普曼矩阵、特征值和系数矩阵。
DMD 预测的一个玩具示例
让我们考虑下面的玩具例子。
import numpy as npdef DMD4cast(data, r, pred_step):
N, T = data.shape
_, _, A = DMD(data, r)
mat = np.append(data, np.zeros((N, pred_step)), axis = 1)
for s in range(pred_step):
mat[:, T + s] = (A @ mat[:, T + s - 1]).real
return mat[:, - pred_step :]
在此处编写代码:
X = np.zeros((2, 10))
X[0, :] = np.arange(1, 11)
X[1, :] = np.arange(2, 12)
pred_step = 2
r = 2
mat_hat = DMD4cast(X, r, pred_step)
print(mat_hat)
运行它,输出是
[[11\. 12.]
[12\. 13.]]
可以看出,DMD 预测与地面实况数据完全相同。
简短的总结
这是一篇简单的文章,介绍 DMD 算法及其在多变量时间序列预测任务中的应用。从公式上看,DMD 确实是一个降秩的 VAR 模型,可以解决高维时间序列数据中的过参数化问题。DMD 相对于 VAR 还有其他一些优势。例如,DMD 由于其解释而在时空数据分析中受到广泛关注。或者,我们也可以使用时间矩阵分解模型(组件包括矩阵分解和 VAR)来解决过度参数化问题。有一篇博文(见https://medium.com/p/b1c59faf05ea)讨论了用时间矩阵分解进行流体动力学预测。
感谢你阅读这篇文章!如果你有任何问题,请告诉我!
参考
[1]彼得·施密德(2010 年)。数值和实验数据的动态模式分解流体力学杂志,656:5–28。
[2]维基百科上的动态模式分解:【https://en.wikipedia.org/wiki/Dynamic_mode_decomposition
[3] J .内森·库茨,史蒂文·l·布伦顿,宾尼·w·布伦顿,约书亚·l·普罗克特(2016)。复杂系统的数据驱动建模。暹罗。
西雅图高速公路时空交通速度时间序列的动态模式分解
时空交通数据分析是智能交通系统中的一个新兴领域。在过去的几年中,数据驱动的机器学*模型为理解真实世界的数据、构建数据计算范式和支持真实世界的应用提供了新的维度。
在这篇博文中,我们计划:
- 介绍美国西雅图的一个公开可用的交通流量数据,
- 设计一个易于使用的数据集(作为一个玩具例子)用于交通流量分析,
- 对玩具实例进行动态模式分解,并讨论结果的解释。
西雅图高速公路交通速度数据集
这是由部署在美国西雅图高速公路上的感应线圈探测器收集的交通速度数据集。该数据集在 GitHub 上公开发布(见https://github.com/zhiyongc/Seattle-Loop-Data)。如图 1 所示,高速公路包括 I-5,I-405,I-90 和 SR-520。里程标处的速度信息是主车道上同方向的多个环形检测器的平均值。在数据集中,有 323 个环路检测器。速度信息的时间分辨率为 5 分钟,这意味着我们每天有 288 个时间间隔,或者说每个环路检测器每天有 288 个数据点。
图一。西雅图地区高速公路地图及其感应线圈检测器位置。每个蓝色图标表示里程碑处的环路检测器。来源于 GitHub。
我们可以看看如图 2 所示的数据文件。速度数据是矩阵的形式。该行对应于每个特定的时间间隔,时间间隔由时间戳给出,如 2015–01–01 00:00:00 和 2015–01–01 00:05:00。该列指的是每个环路检测器 ID。
图二。数据集中的一些交通速度数据点。来源于 GitHub。
在这篇博文中,我们从这个数据集中设计了一个玩具示例数据,并创建了一个子集作为toy_data.npy
。该子集可从我们的 GitHub 资源库获得:
https://github . com/xinychen/transdim/blob/master/datasets/Seattle-data-set/toy _ data . npy
图 3 。西雅图 I-5 高速公路的交通速度数据热图。时间段为 2015 年 1 月 5 日上午 6:00—下午 12:00(即 72 个时间间隔)。该子集中有 75 个环路检测器,从 ID 166 到 ID 240。
图 3 显示了交通速度数据(子集)的速度热图,其大小为 75×72(即 75 个环路检测器和 72 个时间间隔)。该子集涵盖了 I-5 高速公路在早高峰时段的交通状态。可以看出,从(粗略地)环路检测器 176 到(粗略地)环路检测器 196,它示出了涉及交通拥塞的明显的低交通速度。在其他环形检测器中,交通速度相对较高。
要在准备好子集的情况下绘制该图,可以尝试以下 Python 代码:
import numpy as np
import seaborn as sns
import matplotlib.pyplot as pltmat = np.load('toy_data.npy')plt.style.use('bmh')
plt.rcParams['font.family'] = 'Arial'fig = plt.figure(figsize = (4, 4))
sns.heatmap(mat, cmap = 'YlGnBu', cbar_kws={'label': 'Traffic speed'}, vmin = 0, vmax = 75)
plt.xticks(np.arange(0, 72 + 1, 12), np.arange(4 * 288 + 72, 4 * 288 + 144 + 1, 12), rotation = 0)
plt.yticks(np.arange(0.5, 75.5, 10), np.arange(166, 241, 10), rotation = 0)
plt.show()
分析交通速度时间序列
在图 3 中,我们考虑了 75×72 的交通速度子集。我们也可以画一些交通速度的时间序列曲线。图 4 分别示出了环路检测器 186、196 和 236 的三个交通速度时间序列。我们可以看到,具有相对较低交通速度的环路检测器 186 和 196 都遭受交通拥塞。
图 4 。环路检测器 186、196 和 236 的交通速度时间序列。
要绘制此图,请尝试以下 Python 代码:
import matplotlib.pyplot as pltfig = plt.figure(figsize = (6, 6))
i = 1
for loc in [185, 195, 235]:
ax = fig.add_subplot(3, 1, i)
plt.plot(mat[loc - 166, :], color = "#006ea3", linewidth = 2.5)
plt.xticks(np.arange(0, 72 + 1, 12),
np.arange(4 * 288 + 72, 4 * 288 + 144 + 1, 12))
if i != 3:
plt.setp(ax.get_xticklabels(), visible = False)
plt.grid(axis = 'both', linestyle = "--", linewidth = 0.1, color = 'gray')
plt.ylabel('Speed')
plt.title('Loop detector {}'.format(loc + 1))
ax.tick_params(direction = "in")
ax.set_xlim([0, 72])
ax.set_ylim([0, 70])
i += 1
plt.show()
在子集上再现动态模式分解(DMD)
DMD 是一种数据驱动的时间序列数据降维方法。对于任何多变量时间序列数据,DMD 可以计算一组动态模式,其中每个模式都与时间行为相关联。特别是,DMD 允许人们用物理上有意义的模式来解释数据的时间行为[1]。有关 DMD 的深入讨论,请查看[1]。我们使用[1]中的 Python 代码进行以下分析。
import numpy as npdef DMD(data, r):
"""Dynamic Mode Decomposition (DMD) algorithm."""
## Build data matrices
X1 = data[:, : -1]
X2 = data[:, 1 :]
## Perform singular value decomposition on X1
u, s, v = np.linalg.svd(X1, full_matrices = False)
## Compute the Koopman matrix
A_tilde = u[:, : r].conj().T @ X2 @ v[: r, :].conj().T * np.reciprocal(s[: r])
## Perform eigenvalue decomposition on A_tilde
eigval, eigvec = np.linalg.eig(A_tilde)
## Compute the coefficient matrix
Psi = X2 @ v[: r, :].conj().T @ np.diag(np.reciprocal(s[: r])) @ eigvec
return eigval, eigvec, Psi
在这种情况下,r
是 DMD 的预定义低秩。eigval
和eigvec
对应 DMD 中库普曼矩阵的特征值和特征向量。特别是,eigval
可以解释为 DMD 谱,以复共轭对的形式出现。Psi
指动态模式。
DMD 对结果的解释
特征值和 DMD 谱
这里,我们使用上面提到的 DMD 函数,并评估秩为 5 的 DMD 模型。Python 代码由下式给出
r = 5
eigval, eigvec, Psi = DMD(mat, r)
然后,我们使用以下 Python 代码来绘制 DMD 谱:
import matplotlib.pyplot as pltfig = plt.figure(figsize = (4, 4))
ax = fig.add_subplot(1, 1, 1)
ax.set_aspect('equal', adjustable = 'box')
plt.plot(eigval.real, eigval.imag, 'o', color = 'red', markersize = 6)
circle = plt.Circle((0, 0), 1, color = 'blue', linewidth = 2.5, fill = False)
ax.add_patch(circle)
plt.grid(axis = 'both', linestyle = "--", linewidth = 0.1, color = 'gray')
ax.tick_params(direction = "in")
plt.xlabel('Real axis')
plt.ylabel('Imaginary axis')
plt.show()
图 5 显示了数据的 DMD 频谱。每个特征值解释了其相应动态模式的动态行为。我们可以将特征值解释如下[2],
- 如果特征值具有非零虚部,则在相应的动态模式中存在振荡。
- 如果特征值在单位圆内,那么动态模式是衰减的。
- 如果特征值在单位圆之外,那么动力模态是增长的。
在图 5 中,点靠*单位圆或在单位圆上。有四个特征值在单位圆内,而一个特征值在虚部为 0 的单位圆上。我们还可以看到,两个特征值对在虚轴上是对称的。最动态的模式是振荡和衰减。这一结果与早高峰时间低交通速度的时空模式相一致。
图五。秩为 5 的 75×72 数据的 DMD 特征值。有 5 个红点显示了 5 个特征值的实轴和虚轴。蓝圈是单位圆。
动态模式和时间动态
在 DMD 模型中,Psi
是指动态模式。我们可以将其形象化,如图 6 所示。可以看出,动态模式显示了示例数据的空间模式,并且在一些环路检测器中有显著的变化,例如从 176 到 196。这也与这些环路检测器监测到的交通拥堵(交通速度低)相一致。
图 6 。DMD 中数据动态模式的热图。
绘制该图的 Python 代码如下所示
fig = plt.figure(figsize = (2, 4))
sns.heatmap(Psi.real)
plt.xticks(np.arange(0.5, 5.5), np.arange(1, 6), rotation = 0)
plt.yticks(np.arange(0.5, 75.5, 10), np.arange(166, 241, 10), rotation = 0)
plt.show()
为了理解示例数据的时间演变,我们可以使用动态模式来重建数据,如图 7 所示。这里我们将 DMD 重建定义如下,
def reconstruct_DMD_system(data, r):
T = data.shape[1]
## Build data matrices
X1 = data[:, : -1]
X2 = data[:, 1 :]
## Perform singular value decomposition on X1
u, s, v = np.linalg.svd(X1, full_matrices = False)
## Compute the Koopman matrix
A_tilde = u[:, : r].conj().T @ X2 @ v[: r, :].conj().T * np.reciprocal(s[: r])
## Perform eigenvalue decomposition on A_tilde
eigval, eigvec = np.linalg.eig(A_tilde)
## Compute the coefficient matrix
Psi = X2 @ v[: r, :].conj().T @ np.diag(np.reciprocal(s[: r])) @ eigvec
time_dynamics = np.zeros((r, T), dtype = 'complex')
b = np.linalg.pinv(Psi) @ data[:, 0]
for t in range(T):
time_dynamics[:, t] = np.power(eigval, t + 1) * b
return (Psi @ time_dynamics).real, time_dynamics.real
图 7 。利用动态模式重构交通速度数据。
要绘制图 7,请使用以下 Python 代码:
r = 5
rec_system, time_dynamics = reconstruct_DMD_system(mat, r)
fig = plt.figure(figsize = (4, 4))
sns.heatmap(rec_system, cmap = 'YlGnBu', cbar_kws={'label': 'Traffic speed'}, vmin = 0, vmax = 75)
plt.xticks(np.arange(0, 72 + 1, 12), np.arange(4 * 288 + 72, 4 * 288 + 144 + 1, 12), rotation = 0)
plt.yticks(np.arange(0.5, 75.5, 10), np.arange(166, 241, 10), rotation = 0)
plt.show()
这里,对应于每个动态模式的时间动态由图 8 给出。
图 8 。DMD 重建中五种动态模式的时间动力学。
要绘制图 8,请使用以下 Python 代码:
fig = plt.figure(figsize = (10, 6))
for loc in range(1, 6):
ax = fig.add_subplot(2, 3, loc)
plt.plot(time_dynamics[loc - 1, :], color = "#006ea3", linewidth = 2.5)
plt.xticks(np.arange(0, 72 + 1, 12),
np.arange(4 * 288 + 72, 4 * 288 + 144 + 1, 12))
if loc == 1 or loc == 2:
plt.setp(ax.get_xticklabels(), visible = False)
plt.grid(axis = 'both', linestyle = "--", linewidth = 0.1, color = 'gray')
plt.title('Time dynamics of mode {}'.format(loc))
ax.tick_params(direction = "in")
ax.set_xlim([0, 72])
plt.show()
结论
在这篇博文中,我们将介绍一个 DMD 模型的玩具示例,用于时空交通数据分析。由于其有意义的解释,DMD 在时空数据分析中应该有许多有趣的应用。如果你有任何建议,请告诉我!感谢你阅读这篇文章!
这篇博文的完整 Python 实现可从以下网址获得:
https://github . com/xinychen/transdim/blob/master/datasets/Seattle-data-set/toy-examples . ipynb
参考
[1]陈新宇(2021)。多元时间序列预测的动态模式分解。媒体上的博客帖子。https://towards data science . com/dynamic-mode-decomposition-for-multi variable-time-series-forecasting-415d 30086 b4b
[2]Python 中的动态模式分解。2016.博客帖子。http://www.pyrunner.com/weblog/2016/07/25/dmd-python/
基于强化学*和神经网络的动态定价
一个可以增加电子商务销售和利润的智能系统,被 Correlation One 和软银评为其数据科学计划的三大项目之一。
动态定价。图片由来源。经允许重新发布。
该项目的主要目标是开发一个动态定价系统,通过适应供需水平来增加电子商务利润。
定价系统应该能够以稳健和及时的方式操纵产品的最终价格,以可伸缩的方式对供求波动做出反应。
首先,创建了一个模拟器环境,根据几个变量模拟订单水平的波动。然后,这个模拟环境被用来训练一个深度强化学*代理来选择最佳定价策略,以实现利润最大化。
数据准备和系统架构
动态定价系统架构由三个基本部分组成。托管在亚马逊 RDS 上的 PostgreSQL 数据库,托管在亚马逊 EC2 上的 Flask API 和 Dash dashboard。
Flask API 是一个处理 HTTP 请求的 Python RESTful 框架。它有两个主要用途,应用强化学*算法和提供数据访问。它使用经过训练的 PyTorch 模型处理数据,并将结果保存在数据库中。它还提供了对 JSON 格式数据的 HTTP 访问。
系统架构。图片作者。
在利用电子商务数据进行分析和建模之前,进行了广泛的清理过程。主要目的是将数据集合并成时间序列格式。对于我们收集的任何给定产品:
- 竞争对手的价格(如有)
- 平均价格
- 平均运输价值
- 订单数量
- 产品类型
- 产品的组别
在 ETL 过程中,必须进行一些调整以确保数据质量。例如,在分析竞争对手的价格时,我们注意到数据集包含一些在短时间内售价为 0.00 雷亚尔的产品。因为产品不太可能是免费提供的,所以从数据集中删除了这些记录。
从剩下的值中,去除了由人工错误导致的异常值(例如:一个售价为 199.99 雷亚尔的产品被宣传为 19.99 雷亚尔)。这是通过排除那些超出产品平均价格正负三倍于其价格标准偏差的值来实现的。
对产品进行聚类
为每个产品单独建模有许多障碍。当一个产品第一次被创建时,没有足够的历史数据来将其建模为时间序列。另一个缺点发生在产品供应中断的时候。
另一方面,为所有的投资组合创建一个单一的模型会产生糟糕的模型,因为这种方法混合了具有非常不同行为的产品。
我们通过使用混合方法解决了一种型号适用于所有型号和一种型号对应不同产品的权衡问题。我们根据类型、组和价格的组合对产品进行了分类。
聚类表示。图片由来源。经允许重新发布。
对于每个产品组+类型组合,根据四分位数范围,创建了 4 个可能价格范围的分类(A、B、C 或 D)。
通过采用这种方法,减少了数据的稀疏性,并且具有短时间序列的新产品可以基于类似的项目进行定价。
使用强化学*
使用真实场景培训强化学*解决方案通常需要大量时间,而且,由于代理在流程开始时没有任何经验,因此可能会做出错误决策,最终导致不必要的损失。
为了避免这些问题,我们使用各种模型创建了一个环境模拟器:线性回归、决策树、随机森林、支持向量机、极端梯度推进和脸书的先知。
我们最终选择了线性回归环境模拟器,因为它有更高的可解释性。然后,我们应用了一种叫做深度 Q 学*的强化学*方法。
强化学*循环。图片来自来源。
简而言之,软件代理会根据环境的当前状态采取动作。在采取一个动作后,会给代理一个奖励,对所选择的动作的好坏进行打分。通过不断试验行动和评估回报,代理人被训练做出最恰当的决定。
在电子商务动态定价问题中,我们可以将这些概念映射到:
- 环境: 市场(例如亚马逊)
- State:市场最低价,库存水平,当前日期特征(星期几,当前年月日,节假日等。)、将价值运送到关键位置等等。
- AgentT7:动态定价算法
- 动作:提高或降低价格,或提供免运费
- 奖励: 总利润由代理商决定
使用全连接的神经网络,其具有 4 个隐藏层,每层 30 个节点。输入层接收状态信息(电子商务的价格、日期参数、库存、运输值、竞争对手的价格),而输出层由 10 个可能的操作组成:通过将项目的成本乘以 2.5 个百分点的增量来设置零售价格。
这样,代理商永远不会亏本销售产品,留给代理商的任务是选择需求价格之间的最优平衡以优化利润。
结果
为了比较结果,在模拟器环境中使用了原始电子商务定价策略和经过训练的代理定价策略。
分析财务结果,强化学*代理比基准定价政策高出 3.48% 。这种利润增长可以提高商家对电子商务平台的满意度,从而提高参与度。
还可以改进商家的定价工作流程,因为手动操作价格变化非常耗时。
动态定价结果。图片作者。**
这种方法可以进一步用于许多其他行业,如旅游业、运输业和农业。
希望你喜欢,如果你有任何疑问,请联系我们。
感谢阅读!
合著者:弗朗西斯科·马吉利,恩里克·纳西门托,莱昂纳多·戈麦斯·卡多佐 e 雷纳托·坎迪多。
递归与动态规划—斐波那契(李特代码 509)
动态编程程序情节,图片由作者提供
在这篇博客中,我将使用 Leetcode 509。以斐波那契数为例,用 Python 说明递归与动态编程的编码逻辑和复杂性。
该项目由马树恒建造。要查看使用的完整代码,请找到 GitHub 。
第 1 节:递归和动态编程简介
1.1 背景
让我们从什么是递归开始
递归是一个函数调用自身直到到达基本用例的过程。而且在这个过程中,复杂的情况会被递归追踪,变得越来越简单。整个过程的结构是树状的。递归不存储任何值,直到到达最后一级(基本情况)。
而动态编程相比简单递归主要是一种优化。主要思想是将原始问题分解成可重复的模式,然后将结果存储为许多子答案。因此,我们不必在以后需要时重新计算前一步的答案。在大 O 方面,这种优化方法一般将时间复杂度从指数级降低到多项式级。
1.2 如何编写递归/动态编程脚本
动态编程和递归非常相似
- 递归和动态编程都是从我们初始化开始的基本情况开始的。
2.在我们写完基础案例之后,我们将试图找到问题逻辑流程所遵循的任何模式。一旦找到了,基本就完事了。
3.主要区别在于,对于递归,我们不存储任何中间值,而动态编程却利用了这一点。
让我们更深入地了解一下斐波那契数列。
第 2 部分:示例:Leetcode 509。斐波那契数
2.1 问题提示
通常表示为
F(n)
的斐波那契数列形成了一个序列,称为斐波那契数列,从0
和1
开始,每个数字都是前面两个数字的和。也就是说,F[0] = 0 作为第一个数字
F[1] = 1 作为我们的第二个数字
之后的数字很容易计算:
F[n] = F[n-1] + F[n-2]
给定 n,我们如何找到 F[n]?
2.2 示例
如果这是第一次听说斐波那契数,不要担心,这里有一些简单的例子来理解这个问题:
给定 n = 2,F[2] = F[1] + F[0] = 0 + 1 = 1
给定 n = 3,F[3] = F[2] + F[1] = 1+ 2= 3
第 3 节:两种方法
3.3 递归方法
让我们从递归方法开始。
递归代码
递归代码,作者图片
从上面的代码中,我们可以看到,我们做的第一件事总是寻找基本案例。
在这种情况下,基本情况将是 F[0] = 0 和 F[1] = 1,为了实现这种效果,我们将这两个条件显式地写在 if 下。
在基本情况之后,下一步是考虑 F[n]如何产生的一般模式。幸运的是,问题提示已经给出了模式:
F[n] = F[n-1] + F[n-2]
因此,我们可以简单地返回结果 F(n) = F(n-1) + F(n-2)。
3.3.2 递归程序图
如果 n =4,该脚本将执行类似下面的操作:
递归程序情节,图片由作者提供
我们从最上面开始,这里 F[4] = F[3] + F[2]。然后我们会试着求 F[3]和 F[2]的值。最终,当我们到达 F[0] = 0,F[1] = 1 的基本情况时,我们可以简单地从下往上求和,得到 F[4] = 3。
3.4 动态规划方法
动态编程代码
动态编程代码,图片作者
从上面的代码中,我们可以看到,我们做的第一件事是再次寻找的基本情况。
在这种情况下,基本情况将是 F[0] = 0 和 F[1] = 1,为了实现这种效果,我们将这两个条件显式地写在 if 下。
在我们完成基础案例之后,我们将创建一个空的动态编程数组来存储所有的中间和临时结果,以便更快地进行计算。由于 n 从 0 开始,我们将创建一个长度为 n+1 的列表。
下一步是考虑 F[n]如何产生的一般模式。幸运的是,问题提示已经给出了模式:
F[n] = F[n-1] + F[n-2]
因此,我们可以简单地返回结果 F(n) = F(n-1) + F(n-2)。更具体地说:
dp[i] = dp[i-2] + dp[i-1]
这实际上是动态编程和递归的主要区别。在递归中,我们不存储任何中间结果,而在动态编程中,我们存储所有中间步骤。
为了计算 F[4],我们会先计算 F[2],F[3]和将它们的值存储到我们事先创建的 DP 列表中。
3.4.2 动态编程程序图
动态编程程序情节,图片由作者提供
我们从最左边开始,这里 F[0] =0,F[1] = 1。然后我们将试图找到 F[4]的值,我们将首先找到 F[3]和 F[2]的值,并将它们的值存储到 dp_list 中。最终,当我们到达 F[4] = 3 的右边时,我们可以返回最终结果。
第四节:时间和空间复杂性
4.1 递归的大 O
对于递归,时间复杂度将是 O(2^n,因为每个节点将分成两个子分支。
并且空间复杂度将是 O(N ),因为树的深度将与 N 的大小成比例
以下是两者的 Leetcode 运行时结果:
Leetcode 递归结果,图片由作者提供
4.2 动态编程的大 O
对于动态编程,时间复杂度是 O(n ),因为我们只循环一次。正如你在动态编程程序图中看到的,它是线性的。
并且空间复杂度将是 O(N ),因为我们需要将所有中间值存储到 dp_list 中。所以我们需要的空间和给定的 n 一样。
以下是两者的 Leetcode 运行时结果:
Leetcode 动态编程结果,图片作者
4.2 时间复杂性的可视化
时间复杂度速度比较,图片作者
红线代表递归的时间复杂度,蓝线代表动态编程。x 轴表示 n 的大小,y 轴表示算法计算结果所需的时间。
第五部分:总结与结论
简单回顾一下,下面总结了一些要点:
两种算法的对照表,图片由作者提供
从上面,我们可以观察到,尽管递归和动态编程都可以处理计算斐波那契数的任务,但它们在处理中间结果和时间消耗方面有很大的不同。动态编程使用同样多的空间,但是它比 T4 快得多。
尽管这两种算法都需要几乎相同难度的努力来理解逻辑(我希望我的博客能对你有所帮助),但在你掌握了算法的核心之后,这是值得的,因为大量的数组问题可以通过动态编程优雅而高效地解决。
如果你觉得你完全理解了上面的例子,并且想要更多的挑战,我计划在不久的将来使用动态编程来解决一系列博客中更困难和现实生活中的问题。感谢您的阅读!
基于 BERTopic 的动态主题建模
消化摘要:用主题建模对我的兴趣进行逆向工程(第 3 部分)
这是一个由三部分组成的调查的最后一篇文章,通过一种叫做主题建模的 NLP 技术来更好地理解我的媒体建议阅读。在本文中,我将介绍我认为是当今该领域中最强大的主题建模算法:**BERTopic**
。我还将尝试说明时间感知的交互式可视化如何能够包含大量信息,而不需要在粒度级别上手动检查任何文档或主题。
动态主题建模,或监控每个主题的结构如何随时间演变的能力,是理解大型语料库的强大而复杂的方法。我这篇文章的主要目的是强调 BERTopic 的功能,并与之前讨论的 wordclouds 的痛点进行对比。我希望证明主题建模对于非技术人员来说是可以理解的,并且抵制文字云对 NLP 空间的诱惑是值得的。声明:我很抱歉制造了文字云来检查我正在处理的任何语料库,但我希望发现更多信息和数学基础的替代方法。
为什么是 BERTopic?
BERTopic 是一种主题建模技术,它利用 BERT 嵌入和 c-TF-IDF 来创建密集的集群,允许轻松解释主题,同时保留主题描述中的重要单词。
这本书是由马腾·格罗登赫斯特在 2020 年写的,从那以后一直在稳步发展。BERTopic 的两个最大优势可以说是其直接开箱即用的可用性和其新颖的交互式可视化方法。对模型已经学*的主题有一个总体的了解,允许我们对模型的质量和语料库中封装的最值得注意的主题产生一个内部的感知。
装置
软件包可以通过 pypi 安装:
pip install bertopic
如果您预计您的项目将使用 BERTopic 包中包含的可视化选项,请按如下方式安装它:
pip install bertopic[visualization]
三个主要算法组件
- 嵌入文档:用
Sentence Transformers
提取文档嵌入。由于我们正在处理的数据是文章标题,我们将需要获得句子嵌入,BERTopic 让我们通过使用其默认的句子转换器模型paraphrase-MiniLM-L6-v2
方便地做到这一点。 - 聚类文档:用
UMAP
(减少嵌入的维度)和HDBSCAN
(识别和聚类语义相似的文档)创建相似文档的组 - 创建主题表示:用
c-TF-IDF
(基于类别的词频,逆文档频率)提取和缩减主题。如果您首先不熟悉 TF-IDF,那么为了大致理解这里发生的事情,您只需要知道一件事:它允许通过计算一个单词在给定文档中的频率来比较文档之间单词的重要性,还允许测量该单词在整个语料库中的流行程度。现在,如果我们将单个簇中的所有文档视为单个文档,然后执行 TF-IDF,结果将是簇中单词的重要性分数。在一个聚类中,越重要的单词越能代表该主题。因此,我们可以为每个主题获取基于关键字的描述!当涉及到从任何无监督聚类技术产生的分组中推断含义时,这是非常强大的。
Image made by author using excalidraw | c-TF-IDF formula: the
针对每个类别i
提取每个单词 t 的频率,并除以单词总数w
。每个类别的平均单词数m
除以所有 n 个类别中单词的总频率t
。
有关这三个算法步骤的更多信息和理论支持,请查阅作者综合指南进行深入解释。
构建基本的 BERT 主题模型
要在 Python 中创建一个BERTopic
对象并转移到有趣的东西(动态主题建模)上,我们只需要预处理过的文档列表。用pd.read_csv()
加载数据后,我们可以编写一些 lambda apply 函数来预处理我们的文本数据:
df.text = df.apply(lambda row: re.sub(r"http\S+", "", row.text).lower(), 1)df.text = df.apply(lambda row: " ".join(filter(lambda x:x[0]!="@", row.text.split())), 1)df.text = df.apply(lambda row: " ".join(re.sub("[^a-zA-Z]+", " ", row.text).split()), 1)
…或者,如果已经清理过了,我们可以准备两个变量。最关键的变量显然是我们的文档列表,我们称之为titles
。其次,我们需要一个与每个文档相对应的日期列表,这样我们就可以深入了解我们的主题是如何随着时间的推移而变化的。
titles = df.text.to_list()
dates = df['date'].apply(lambda x: pd.Timestamp(x)).to_list()
接下来,让我们创建我们的主题模型!
from bertopic import BERTopictopic_model = BERTopic(min_topic_size=70, n_gram_range=(1,3), verbose=True)topics, _ = topic_model.fit_transform(titles)
我们可以根据分配给每个主题的主题数量提取最大的十个主题,还可以预览我们主题的基于关键字的“名称”,如前所述:
freq = topic_model.get_topic_info()
freq.head(10)
作者图片
注意,在上述数据帧中,主题-1 表示由异常值文档组成的主题,这些异常值文档通常被忽略,因为术语在整个语料库中具有相对较高的流行度,因此对任何内聚主题或主题的特异性较低。
我们还可以看看构成感兴趣的特定主题的术语:
topic_nr = freq.iloc[6]["Topic"] # select a frequent topic
topic_model.get_topic(topic_nr)
作者图片
太好了!所有我们最不喜欢的单词都属于话题 5。我们可以看到,前三个词是“冠状病毒”、“covid”和“疫情”,但还有一个 n-gram 也进入了这 10 个特定主题的术语:“covid 疫苗”,这是一个具有更多积极内涵的术语,所以看看“疫苗”一词在哪个时间点开始更频繁地出现在文章标题中会很有趣。
交互式可视化
这种技术的最大优点是能够可视化我们的主题模型,告诉我们足够多的数据,而不需要研究原始文本本身。
如果您还记得我以前的主题建模文章,标题为“ NLP 预处理和使用 Gensim 的潜在 Dirichlet 分配(LDA)主题建模”,有一个名为pyLDAvis
的 Python 可视化包,它使用户能够生成交互式子图,描述 2D 平面上主题之间的距离以及主题内前 30 个最相关和最显著的术语。BERTopic 有自己的主题间距离图实现,它包括一个悬停工具提示,显示指定给特定主题的文档数量以及该主题中最常用的前 5 个术语。生成以下可视化效果所需的代码行非常简单:
topic_model.visualize_topics()
上面的主题间距离图描述了 15 个主题中的五个主要主题组。最接* x 轴的集群属于 Python 的一般主题,基于我的中等兴趣和搜索历史,这具有直观的意义。左下方的集群由机器学*、深度学*、数据科学、技术设计等领域兴趣组成。被吸引到左上角的是关于 NLP、情感分析和 Twitter 数据的文章。尽管事实上这些可以被标记为我的另一个兴趣领域,我假设它们占据了 2D 平面的不同区域,因为我在我的高级顶点项目中对这些主题进行了更深入更细致的研究。我认为,靠* y 轴的中间底部区域的集群的特点是技术含量较少,主题行遵循模板“作为软件工程师避免精疲力尽的 10 个技巧”或“我从 100 天内写 100 篇中型文章中学到的东西”。在我看来,这些文章处于生产力技巧和自我发展智慧的交汇点。他们并不总是教我新的东西,但当我每天早上阅读《媒介文摘》时,他们无疑会激励我。最后,最右边的主题簇代表了不属于技术领域的主题的大杂烩,但是对于在媒体上消费全面的文章选择来说仍然是重要的。
现在,最吸引人的是关于时间的动态主题建模!在下面的视频中,我们可以观察到从 2020 年初到现在,我的建议媒体文章(标题)语料库中十个最流行的主题的频率。
我们可以观察到,数据科学(topic 5
)和开发/编码(topic 9
)是我*两年来的一贯兴趣。有趣的是,React apps ( topic 6
)是我在 2020 年 10 月和 11 月广泛阅读的东西,但我的这种兴趣很快就消退了。你可能想知道这背后的故事?嗯,我正在为一个前端开发面试临时抱佛脚,对此我感到毫无准备。有趣的事实:灵媒教会了我几乎所有关于反应的知识。我们可以注意到,NLP 主题(topic 8
)的频率在 2020 年夏天和 2021 年 6 月左右达到峰值,当我反思我在那些时间从事的工作时,这很有意义。新冠肺炎(topic 2
)在 2020 年 3 月到 5 月的频率达到峰值,我认为这说明了一切…继续前进。百花香主题(topic 0
)在 2020 年初出现频率最高,但在当年晚些时候逐渐减少。我想当我被困在家里的时候,我读了更多的非技术内容,而我现在主要是为了技术目的查阅中型文章。
在不进行更多个人反思的情况下,我希望您能够理解动态主题建模的强大之处。在任何时间点将鼠标悬停在我们的主题频率线图上会显示不同的主题关键词,从而可以分析主题如何随着时间的推移而演变。
虽然我简要地展示了如何访问属于特定主题的热门关键词及其重要性分数,但是我们也可以将这些术语和分数可视化为条形图。
我们的伯托皮综合之旅到此结束!我希望您已经了解了一两件关于动态主题建模的事情,并且能够明白为什么我认为这个主题建模 Python 包超越了这个领域中的其他包和技术。再次对 BERTopic 的作者 Maarten Grootendorst 大声疾呼,感谢他实现并开源了这个令人敬畏的代码。
总结和简化应用程序
如果您想尝试一下动态主题建模,我已经编写了一个小的 Streamlit 应用程序,它允许您导入一个包含日期和文档的 CSV 文件,您可以从中构建主题模型。然后,它创建一个 BERTopic 模型,并在应用程序中嵌入一些交互式可视化。我希望你喜欢并可以尽情地玩这个应用程序。我开发这款应用的动机如下:
- 让主题建模更具交互性和趣味性,远离令人生畏的技术性 NLP 子域
- 让不太懂技术的人也能理解这个主题,他们可能也想有机会更好地理解给定文本语料库中的主题
此处可访问该应用:https://share . streamlit . io/sejaldua/digesting-the-digest/main/ber topic _ app . py
和往常一样,我把我的源代码放在一个公共的 GitHub 库中:
https://github.com/sejaldua/digesting-the-digest
参考
https://github.com/MaartenGr/BERTopic https://reposhub.com/python/natural-language-processing/MaartenGr-BERTopic.html
Python 中的动态类型
探索 Python 中对象引用的工作方式
塞缪尔·伊莱亚斯·纳德勒摄于unsplash.com
如果你有 Java、C 或 C++等语言的背景,这些语言是编译的或静态类型的,你可能会发现 Python 的工作方式有点令人困惑。例如,当我们给一个变量赋值时(比如说a = 1
,Python 怎么知道变量a
是一个整数呢?
动态类型模型
在静态类型语言中,变量的类型是在编译时确定的。在大多数支持这种静态类型模型的语言中,程序员必须指定每个变量的类型。例如,如果你想在 Java 中定义一个整数变量,你必须在定义中明确地指定它。
# Java Example
int a = 1;
int b;
b = 0;
另一方面,Python 中的类型是在运行时而不是编译时确定的,因此程序员在代码中使用变量之前不需要声明变量。
从技术上讲,变量(在 Python 中也称为名)是在代码中第一次赋值时创建的。这意味着在代码中引用变量之前,必须先给变量赋值(即引用内存中的对象),否则将会报告错误。如前所述,变量赋值从不带有类型定义,因为类型是与对象一起存储的,而不是与变量/名称一起存储的。每次识别出一个变量,它就会自动被内存中它所引用的对象所替换。
对象、变量和引用之间的关系
总而言之,每次我们给变量赋值的时候,Python 都会进行以下三个步骤:
- 在内存中创建一个保存该值的对象
- 如果变量名在名称空间中不存在,继续创建它
- 将对象的引用(在内存中)赋给变量
一个变量,是一个系统表中的符号名,该系统表保存了到对象的链接(即引用)。换句话说,引用是从变量到对象的指针。但是在 Python 中,变量没有类型。因此,可以将不同类型的对象赋给同一个变量名,如下所示。
a = 1
a = 'Hello World'
a = False
在第一行中,变量a
被赋值为引用值为1
的整数对象。同样,第二行将变量a
的引用更改为字符串类型的不同对象,而最后一行更改了引用,因此a
现在指向一个布尔对象。
当我们提到对象时,我们实际上是指一块能够表示我们希望的值的已分配内存。该值可以是整数、字符串或任何其他类型。除了值之外,对象还带有几个头字段。这些字段包括对象的类型及其引用计数器,垃圾收集器使用该计数器来确定回收未使用对象的内存是否合适。因为 Python 对象能够知道自己的类型,所以变量不需要记住这些信息。
共享引用
在 Python 中,多个变量可能引用同一个对象。这种行为被称为共享引用。例如,考虑下面的代码
a = 1
b = a
最初,Python 创建一个值为1
的 integer 对象,然后创建名为a
的变量,最后将内存中指向 integer 对象的引用赋给变量。
第二行是共享引用发挥作用的地方。Python 现在将创建变量b
,并将保存对对象1
的引用,该对象与分配给变量a
的对象相同。请注意,变量a
和b
彼此完全独立,它们没有任何联系。它们只是共享指向物理内存中同一个整数对象的同一个引用。
现在考虑另一个例子,其中我们有一个额外的操作:
a = 1
b = a
a = 'Hello World'
在本例中,在内存中创建了一个值为'Hello World'
的附加字符串对象,并将其引用赋给变量a
。需要强调的是,在这种情况下,变量b
的值保持不变。即使对于相同的对象类型,也观察到相同的行为
a = 1
b = a
a = a - 1
同样,最后一条语句将触发创建一个值为0
的新整数对象,并最终将其引用赋给变量a
,而变量b
保持不变(即,它一直引用值为1
的对象)。
共享引用和可变与不可变对象
在 Python 中,所有变量都是指向保存对象的特定内存位置的引用(即指针)。然而,创建和修改对象的方式取决于它们的类型是可变的还是不可变的。
正如我们在前面的例子中看到的,最后一次赋值a = a — 1
不会修改对象本身,因为整数对象类型是不可变的。这意味着每次我们想要改变不可变对象类型(比如 integer 或 string)的值时,Python 都会创建一个保存所需值的新对象。对于不可变类型,这是直接的,并且使得变更变量相当安全,因为它不影响现有对象的值,因为就地改变不适用于不可变对象类型。
然而,对于可变类型就不是这样了,程序员必须了解 Python 处理可变对象类型的方式,以避免代码中任何不必要的行为和错误。
可变对象类型支持就地更改,这意味着当它们的值被修改时,会对引用该对象的所有变量产生影响。这种对象类型包括列表、字典和集合。为了说明这一概念,让我们考虑下面的例子,其中我们有两个保存对同一个列表对象的引用的列表:
list_1 = [1, 2, 3]
list_2 = list_1
现在让我们从一个简单的用例开始,我们想给第二个列表分配一个新创建的列表:
list_1 = [1, 2, 3]
list_2 = list_1
list_2 = [2, 3, 4]
现在这两个列表拥有不同的引用,每个引用指向两个不同的对象。因此,如果我们对其中一个列表进行就地更改,这种更改不会影响另一个列表,因为这两个对象是不同的,它们存储在内存中的不同位置。
当我们试图修改一个被不同变量引用的列表对象时,事情变得有点复杂。为了说明这一点,我们再来看一个如下所示的例子:
list_1 = [1, 2, 3]
list_2 = list_1
list_1[0] = 0
现在,如果我们将两个列表都打印出来,我们会注意到,尽管我们只更改了list_1
,但实际上这也影响了list_2
的内容:
print(list_1)
print(list_2)>>> [0, 2, 3]
>>> [0, 2, 3]
在这个例子中,我们没有改变列表list_2
本身,但是list_1
的改变导致了list_2
的改变。正如我们已经解释过的,这是因为两个list_2
都引用了与list_1
相同的对象。因此,对对象的就地更改会影响引用它的所有变量。再次强调,这种行为只在可变对象类型的和中被观察到,这一点很重要,并且您必须了解 Python 中原位更改的行为方式,这样您就不会意外地在代码中引入 bug。
在大多数情况下,这种行为是程序员真正希望在他们的代码中看到的。然而,也有许多不希望出现这种情况的用例,需要考虑替代解决方案,以便一个变量的变化不会影响引用同一对象的其他变量。如果是这样的话,那么复制这样的对象就是前进的方向。
在 Python 中复制对象
Python 附带了一个名为copy
的内置包,它提供了复制对象的功能。这两种拷贝类型是浅和深,它们的区别在于你是否必须处理复合对象,即包含其他对象的对象——例如字典列表或列表列表。
一个 浅拷贝 构造了一个新的复合对象,然后(尽可能地)将引用插入到原始对象中。
例如,考虑这样一个例子,我们需要复制一个列表,然后修改两个变量中的一个。在这种情况下,每个变量现在将指向不同的对象,因此两个对象之一的就地更改不会影响另一个:
import copya = [1, 3, 4, 7]
b = copy.copy(a)
b[0] = -1
print(a)
print(b)>>> [1, 3, 4, 7]
>>> [-1, 3, 4, 7]
然而,当您有一个嵌套可变类型的复合对象时,浅拷贝就不行了——比如一个列表的列表。在下面的例子中,我们可以看到,如果我们对一组列表进行浅层复制,原始列表a
或原始复合对象c
的改变,结果将对复制的列表d
产生影响:
import copy a = [1, 3, 5, 7]
b = [2, 4, 6, 8]
c = [a, b]d = copy.copy(c)
a[0] = -1
c[0][1] = -3
print(d)>>> [[-1, 3, 5, 7], [2, 4, 6, 8]]
这是因为浅层复制不会为嵌套实例创建新对象,而是将它们的引用复制到原始对象。在大多数情况下,我们通常需要为嵌套实例创建一个新对象,这样复制的复合对象就完全独立于旧对象。在 Python 中,这被称为深度拷贝。
一个 深度拷贝 构造一个新的复合对象,然后递归地将在原始对象中找到的对象的拷贝插入其中。
现在,如果对象d
是c
的浅层拷贝,那么对a
、b
或c
的更改不会影响拷贝的列表:
import copya = [1, 3, 5, 7]
b = [2, 4, 6, 8]
c = [a, b]d = copy.deepcopy(c)
a[0] = -1
c[0][1] = -3
print(d)>>> [[1, 3, 5, 7], [2, 4, 6, 8]]
解释了对象相等性
由于 Python 参考模型的工作方式,它还提供了两种可能的方法来执行对象之间的相等性检查。但是它总是归结于你到底想要检查什么。要检查两个对象是否有相同的值,可以使用运算符==
。另一方面,如果你需要检查两个变量是否指向同一个对象,你需要使用操作符is
。
>>> a = [1, 2, 3]
>>> b = [1, 2, 3]
>>> c = a
>>>
>>> a == b # both lists have the same values
True
>>> a == c # both lists have the same values
True
>>> a is c # variables a and c point to the same object
True
>>> a is b # variables a and b point to a different object
False
动态类型语言的最大缺点是
尽管动态类型模型提供了灵活性,但在创建 Python 应用程序时,它也会带来麻烦。静态类型语言(如 Java)的主要优点是检查是在编译时完成的,因此可以在早期识别错误。
另一方面,Python 等动态类型语言提高了开发人员的工作效率,但是用户需要格外小心,因为错误只会在运行时报告。例如,考虑这样一个场景,其中同一个变量在整个代码中指向不同的对象类型(注意,这在大多数静态类型语言中是不允许的)。您必须确保在代码中的某些点上只对该变量/对象执行相关的操作,以避免不适用于该特定对象类型的函数或方法的无效使用。
a = [1, 2, 3] # a is initially a list
a = 1 # Now a is an int
a[0] = 0 # Indexing can only be applied over sequencesTraceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'int' object does not support item assignment
结论
在本文中,我们探讨了 Python 的一个基本特征,即它的动态类型模型。我们讨论了变量、对象和引用之间的关系,以及 Python 的引用模型的细节。我们已经看到了共享引用是如何工作的,以及在处理可变对象类型时如何避免不必要的行为。
动态类型模型是编程语言的一个强大特性,但是程序员在用 Python 等动态类型语言编写应用程序时需要格外小心,因为错误更容易被错误引入。
使用 Python 在 web 地图上动态显示数百万个点
动态生成切片地图可以节省存储空间和时间
2500 万艘船在美国水域。图片作者。
去年,汤姆·怀特展示了一张从数百万地理编码的野生动物观察中绘制的网络地图。地图的行为与您预期的一样,每个缩放级别都显示了更详细的数据。
怀特的解决方案中的数字魔法是 Datashader ,这是一个开源库,可以从数百万个数据点快速创建图像。使用 Datashader,数据可以在不同的缩放级别和位置进行预渲染,并由任何 web 服务器提供。添加适当的客户端软件和浏览器,你就有了一个平铺网络地图或滑动地图。
光滑的地图将地球分成反映不同层次的瓦片。缩放级别为 0 时,世界显示为一张 256 x 256 像素的图像。在缩放级别 1 下,地球由四个 256 平方像素的图块表示。每一个缩放级别都会调用更多细节的更细粒度的图块。平滑渲染时,效果就像从高空跌落到街道上。
当你缩放到 10 级时,你已经把地球分成了超过一百万个方块。在缩放级别 15 下,您正在平移十亿个图块,在缩放级别 20 下平移万亿个图块。
缩放级别为 16 的罗马,左上角是圣彼得广场,右下角是斗兽场。来自 ArcGIS online 切片成像仪的图像。
创建瓦片来填充光滑的 masp 是一个计算上的挑战。如果在一台机器上写一个 300 字节的文件需要 1 毫秒,那么你在半小时内就创建了 10 亿字节的数据。到缩放级别 20 时,您已经购买了三年的 TB 级驱动器。
但是 Datashader 快吗——我想知道是否有可能按需生成数据?这将减少处理和存储大量图像的需求。它还允许数据经常更新——每半个小时将给出一个相当准确的全球船舶交通的快照。
为了测试这一点,我使用了来自美国国家海洋和大气管理局(NOAA)运营的海洋地籍网站(T10)的数据,该网站公开了十年来解析的船只位置数据。从这里,我可以获得在美国水域航行的船只的 600 万到 800 万个位置的每日汇总。
准备数据
海洋地籍数据中的每个位置记录不仅仅包含船只的当前位置。对于我们的目的,我们只需要船只的纬度和经度,所以第一步是去除不必要的数据,并将数据从其本地投影转换到 web 墨卡托。
海洋地籍中的位置数据
通过卷曲和解压,检索数据很容易。 GDAL 的 ogr2ogr 命令行工具将坐标调整为 web 墨卡托。
虽然我真的只想用两列来反映每艘船的纬度和经度,但是 ogr2ogr 应用程序不会将输出仅限于位置数据。另一个 awk 过滤器只提取可视化所需的列。
shell 脚本的一部分,用于收集和准备位置数据。
结果是一个逗号分隔值(CSV)文件,其中包含所需投影中每个船只位置的纬度和经度。
准备在 web 地图上显示的数据。
显示数据
原型的平铺显示部分将前一步中准备的位置数据加载到 pandas 数据帧中。当每个 web 请求到达一个图块时,程序将图块位置转换为坐标的边界框,并使用它来查询数据框。使用 Datashader 将结果渲染到图像中,然后将图像传回 web 客户端,以便在适当的地图客户端上显示。
FastAPI 提供了 URL 到呈现函数的路由。启动时,一个 indexhtml 变量被填充了 White 的原始传单应用程序的修改版本。此页面可用于显示底图上的渲染图块。在快速检查文件是否存在之后,船只数据被加载到一个 PyArrow 对象中,然后被传递给一个 pandas DataFrame。
两个 HTTP 端点处理 web 界面所需的静态 HTML 和 JavaScript。对 web 结构根的请求接收 indexhtml 变量的内容,而 app.mount 确保客户端获得呈现 web 地图所需的页面样式表、图形和 Javascript。
slippy map 的处理程序将基本变量从图块请求中分离出来:缩放、 x 和 y 被转发到图块生成器,结果标记为 PNG 文件。
通过将从请求路径提取的 x 和 y 变量传递给翻译函数,获得每个图块的左上和右下坐标。
从 OpenStreetmap wiki 借用的代码将切片路径转换为经度和纬度。对 Datashader 辅助函数的额外调用将图块坐标转换为 web 墨卡托基于米的格式。
定义图块边界框后,代码将原始父数据帧中的位置数据切割成一个较小的数据集。
画布是 256 像素的正方形,它映射到一个边界框,该边界框由被渲染的图块所覆盖的纬度和经度的最小和最大范围形成。接下来,聚合函数处理查询数据并识别纬度和经度。
最后,从集合中创建一个图像,将其分配给一个字节数变量,并传递回调用函数,以传递回浏览器。
最终产品
动态渲染正在进行。图片作者。
如果你运行我把放在 GitHub 上的代码,然后浏览 http://localhost:8080,你会看到一个风格化的全球地图。单击地图右上角的图标将允许您选择底图并启用/禁用数据图层。
嵌入的动画 gif 显示了应用程序从一个包含 800 万个位置报告的源文件中渲染图块的速度。每个单幅图块在 30 到 40 毫秒内渲染,创建详细的图像需要稍多的时间。在高缩放级别下,稀疏数据可能难以辨认,但由于 Datashader 控制像素大小,因此可以创建一个动态算法,在缩放级别较高且数据计数较低时调整像素大小。
弗吉尼亚州诺福克市外,船只来往穿梭。图片作者。
这种渲染技术开辟了一种将面向数据帧的分析与标准网络地图呈现相结合的方法,Datashader 可以被任何快速图像生成功能所取代。
E2E 深度学*:无服务器图像分类
使用 TensorFlow、Docker、AWS Lambda 和 API Gateway 构建端到端深度学*模型,对真实世界的图像进行分类
本杰明·拉斯科在 Unsplash 上的照片
A.介绍
A.1 .背景和动机
在数据科学生命周期中,部署是我们最终将我们的人工智能模型付诸实践的阶段。因此,在我们构建和评估模型之后,我们需要将它作为一个解决方案来部署,以帮助企业解决现实世界中的问题。只有这样做,我们才能从用户或利益相关者那里获得反馈,以改进模型并评估其性能和影响。换句话说,管理端到端数据科学项目的技能是任何数据科学家都必须具备的。
A.2 目标
现在,假设我们需要处理大量真实世界的服装图像,而你的职责是为一家电子商务公司创建一个自动图像分类器。因此,挑战不仅在于建立一个强大的深度学*模型,还在于将其部署为一个无服务器应用。这种无服务器方法有助于我们只关注业务解决方案,而不是承载应用的繁重基础设施。幸运的是, AWS Lambda 和 API Gateway 的组合可以用于托管无服务器 API。
在这个项目中,我们将一起学*如何:
- 建立深度学*模型使用 TensorFlow 对图像进行分类。
- 使用 TensorFlow Lite 将模型转换成尺寸效率更高的格式。
- 使用 Docker 在我们的机器上本地部署模型。
- 使用 AWS Lambda 和 API Gateway 将模型部署为 REST API 。
A.3 .目录
- 简介 > 背景与动机 > 目标>目录
- 模型训练 > 图像数据集>建立模型>训练模型>评估模型
- 型号转换 >转换型号>使用转换后的型号
- 模型部署 > Lambda 功能>本地部署,Docker >在 AWS 上部署
由于这篇教程文章将会非常广泛,请随意跳转到适合您需求的特定部分。
注意: 为了跟进这个项目,我们希望你对如何使用 TensorFlow、Docker [1]、AWS 术语建立深度学*模型有一个基本的了解,并拥有一个 AWS 帐户来访问其服务。
B.模特培训
B.1 .图像数据集
该数据集包含 3781 幅服装图像,具有前 10 个最流行的类别,分为训练集、测试集和验证集。表 1 显示了数据集摘要,以便更好地理解。我们可以在这里访问数据[2]。
表 1 。用于训练深度学*模型的服装图像数据集综述。
要使用 Python 查看图像,我们可以使用matplotlib.pyplot.gcf()
类来获取当前图形,并将其设置为特定的行数和列数。因此,在每一行和每一列,我们可以把一个图像作为一个支线剧情。
图一。10 个类别中每个类别的图像数据集样本。
B.2 .建立模型
我们将使用迁移学*方法和图像增强来构建深度学*模型,以实现良好的性能并防止过拟合。我们使用的预训练模型是 InceptionV3 ,但是也可以随意尝试另一个模型。TensorFlow Keras 为InceptionV3
内置了一个模型定义。我们将使用(150,150,3)作为所需的输入形状图像,不包括顶部的全连通层,并使用我们可以在此下载https://storage.googleapis.com/mledu-datasets/inception_v3_weights_tf_dim_ordering_tf_kernels_notop.h5****【3】的局部权重。导入该类,并通过指定上述配置实例化它,如下所示:
****图二。定义预训练的 InceptionV3 模型。
****图三。预训练模型的总结。
正如我们所看到的,每一层都有自己的名称,其中最后一层的名称是 mixed10 ,它已经被卷积为 3 乘 3。有趣的是,我们可以决定向上移动最后一层,以使用更多一点的信息。例如,混合 7** ,输出 7 乘 7。因此,可以根据我们的需要选择最后一层进行实验。**
****图四。根据我们的需求选择预训练模型的最后一层。
我们将定义一个新的模型,该模型考虑了 InceptionV3 的 pre_trained_model 来对服装图像进行 10 个不同类别的分类。在这里,我们可以为新模型构建最后几层,如下所示:
****图五。使用迁移学*为我们的目标重新构建预训练模型。
B.3 .训练模型
现在,我们准备训练模型。请注意,我们通过将图像像素值除以 255 来标准化它们,在 ImageDataGenerator 中设置几个参数来增加用于训练的输入图像以防止过度拟合,将 batch_size 设置为 32,并将目标图像大小设置为(150,150)以适应模型输入形状。
图 6 。使用 100 个时期的图像增强来训练模型
****图 7。在训练过程中想象模特的表现。
恭喜你。我们只是利用迁移学*和图像增强建立了一个强大的深度学*模型。我们实现了 90.59%的测试准确率和 0.273%的测试损失,并设法避免过度拟合。其实我们的测试准确率比训练准确率高~5%,很棒!
B.4 .评估模型
让我们通过对看不见的图像进行新的预测来验证这个模型。正如我们所料,该模型在对每张测试图像做出正确预测方面表现得非常好。
****图 8。使用训练好的模型预测新图像
C.模型转换
在我们使用 TensorFlow 构建模型后,我们很快就会注意到文件大小太大,并且没有针对部署进行优化,尤其是在移动或边缘设备上。这就是 tensor flow Lite(TF Lite)发挥作用的地方。TFLite 将帮助我们将模型转换成更有效的格式。 tflite。这将生成一个小的二进制大小的模型,它是轻量级的,低延迟的,对准确性影响很小。
C.1 .转换模型
下面是我们需要做的步骤,以便将我们的最佳训练模型转换为 tflite 文件:
- 在 h5 文件中加载模型,
- 从加载的训练模型中实例化 TFLiteConverter 对象,
- 转换并以 tflite 文件格式保存转换后的模型。
****图九。使用 TensorFlow Lite 转换模型
C.2 .使用转换的模型
一旦我们将模型转换为 tflite 文件格式,我们就可以使用 TFLite 解释器来查看模型在移动或边缘设备上部署之前如何进行预测。
****图 10。使用 TFLite 解释器进行预测
D.模型部署
在最后一步,我们将使用 Docker、AWS Lambda 和 AWS API Gateway 部署模型。首先,我们需要创建一个lambda_function.py
来在 AWS Lambda 或 Docker 上部署模型,因为这两个选项都需要这个文件来运行深度学*模型。
D.1 函数
lambda_function.py
存储运行应用程序所需的所有函数,从定义解释器开始,接收输入图像,预处理图像,并使用保存的模型进行预测。
****图 11。λ_ function . py
D.2 .使用 Docker 在本地部署
我们刚刚创造了lambda_fucntion.py
。接下来,我们希望使用 AWS Lambda 来获取和部署它。为此,我们将使用 Docker。AWS Lambda 支持 docker,所以我们可以使用容器映像来部署我们的功能。
在本节中,您将学*如何在您的机器中使用 Docker 来本地运行模型。
D.2.1 .文档文件
下一步是创建 Dockerfile 文件。 Dockerfile 是一种将运行代码所需的所有依赖项放入包含所有内容的单一映像中的方法。Docker 镜像是一个专用于你的容器的私有文件系统。它提供了容器需要的所有文件和代码,例如:
- 安装 python 包管理系统。
- 安装枕头库处理图像文件。
- 安装 TensorFlow Lite tflite_runtime 解释器。
- 在 tflite 文件中获取我们的模型,并将其复制到 docker 图像中。
- 获取 lambda_function.py 并将其复制到 docker 映像中。
图 12。 Dockerfile
我们现在需要做的是运行并构建这个 docker 映像,并在本地运行它。
D.2.2 .建立 Docker 形象
以下是我们在本地运行应用程序的步骤:
****运行 docker 守护进程。有两种方法可以做到这一点:
- 第一种选择是以管理员的身份打开 cmd ,然后启动以下命令:
"C:\Program Files\Docker\Docker\DockerCli.exe" -SwitchDaemon
- 第二个选项是从开始菜单运行 Docker 桌面并验证 Docker 是否处于运行状态。
从 Dockerfile 文件构建一个图像。
- 下面的命令将从您当前所在的文件夹的内容构建图像,标记名为
tf-lite-lambda
。
$ docker build -t tf-lite-lambda .
运行容器映像
****根据您在上一步中构建的映像启动一个容器。运行容器会使用私有资源启动应用程序,并与机器的其他部分安全隔离。
$ docker run --rm -p 8080:8080 --name clothes-classifier tf-lite-lambda
-p
(代表发布)表示我们想要将容器端口 80 映射到主机端口 80。容器在端口 80 上打开一个 web 服务器,我们可以将计算机上的端口映射到容器公开的端口。--rm
(代表移除)表示当容器存在时,我们希望自动移除容器。--name
给一个新容器命名,tf-lite-lambda
是我们用来创建容器的图像名。
以下是之前命令的结果截图:
****图 13。构建并运行 Docker 容器映像
D.2.3 .测试容器图像
在我们运行模型之后,我们想要测试它。我们需要创建一个特殊的文件,我们可以调用它来查看模型预测的结果。
该文件包含:
- 来自预期输入图像的完整类别。
- 从此链接获得的一张裤子(测试)图片:http://bit.ly/mlbookcamp-pants。我们将发送一个请求,其中有一个键
url
和图像的 URL。 - 一个 URL 地址,表明我们部署在 docker 内部的本地主机上。
- 向目标 URL 地址发送 POST 请求以获得预测结果的过程。
- 解析预测结果并显示给用户。
图 14。 test.py
在您的 CLI 上运行test.py
,亲自查看结果:
****图 15。运行 test.py,通过 Docker 容器进行预测
D.3 .在 AWS 上部署
我们刚刚用 Docker 在本地部署了模型。现在,我们可以使用相同的容器并将其部署在 AWS 上。AWS 拥有您在线部署深度学*模型所需的一切。对于这种情况,我们将使用 AWS CLI、AWS ECR、AWS Lambda 和 AWS API Gateway。
D.3.1 .安装 AWS CLI
我们用 AWS 做的一切都是 API 调用。因此,我们必须有一个工具,允许我们对这些 API 调用进行编程或编写脚本。其中一个工具是 AWS 命令行界面(CLI)。在我们继续之前,请确保您已经在本地机器上安装了 AWS CLI[4]。
D.3.2 .配置您的 AWS 帐户
如果我们想在 AWS 上部署应用程序,显然我们需要在那里设置一个帐户。创建 AWS IAM 用户帐户后,设置访问密钥 ID、秘密访问密钥、默认区域和默认输出格式(通常是 JSON)。一旦我们完成了这些,我们就可以从 AWS CLI 对 AWS 进行编程调用。
$ aws configure
****图 16。通过 AWS CLI 配置 AWS 帐户
D.3.3 .在 AWS ECR(弹性容器注册)中创建回购
AWS ECR 是我们放 Docker 图片的地方。通过运行以下命令,我们将创建一个私有存储库来存储我们之前构建的 Docker 映像。
**$** aws ecr create-repository --repository-name lambda-images
图 17。通过 AWS CLI 在 AWS ECR 中创建新的存储库
D.3.4 .向回购发布图像
现在,我们想发布我们在本地构建的图像。以下是直接引用自 AWS 的步骤(AWS ECR >存储库> lambda-images >查看推送命令):
- 检索一个认证令牌,并向您的注册表认证您的 Docker 客户端。
$ aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin XXXXXXXXX474.dkr.ecr.us-east-1.amazonaws.com
- 使用以下命令构建 Docker 映像。
$ docker build -t lambda-images .
- 标记您的图像,以便您可以将图像推送到此存储库。
$ docker tag tf-lite-lambda XXXXXXXXX474.dkr.ecr.us-east-1.amazonaws.com/lambda-images:tf-lite-lambda
- 运行以下命令,将这个映像推送到您新创建的 AWS 存储库。
$ docker push XXXXXXXXX474.dkr.ecr.us-east-1.amazonaws.com/lambda-images:tf-lite-lambda
检查 AWS ECR 网页上的推送图像。确保复制 URI,因为我们需要它来创建一个 Lambda 函数。
****图 18。复制推送图像的 URI,创建 lambda 函数
D.3.5 .创建λ函数
现在,我们准备创建一个 Lambda 函数。进入 AWS Lambda
并点击Create function
。选择Container Image
。
****图 19。通过 AWS Lambda 创建 Lambda 函数
为您的函数取一个惟一的名称,并用您之前复制的图像 URL 填充容器图像 URL。通过将一切保留为默认,点击Create function
您刚刚为预测任务创建了一个 lambda 函数。然而,目前的配置并没有给我们足够的内存和超时。我们有一个大模型,该功能将需要一些时间来运行,并首次将所有内容加载到内存中。因此,我们需要重新配置它。进入Configuration
> General Configuration
>点击Edit
,将 RAM 和超时分别设置为 512/1024 和 30 秒。省省吧。
****图 20。重新配置 lambda 功能的内存和超时
D.3.6 .测试λ功能
接下来,使用以下 JSON 文件格式创建一个测试:
{
"url": "https://tinyurl.com/clothes-t-shirt"
}
****图 21。测试我们刚刚创建的 Lambda 函数
为新事件命名,保存后点击Test
。然后,您将看到以下结果:
****图 22。进行预测时 lambda 函数的执行结果
你需要知道的一件事是,使用 AWS Lambda,你将根据请求的数量和持续时间,也就是执行我们的代码所花费的时间来收费。请参考此链接了解更多定价信息。
D.3.7. API 网关集成
您刚刚测试了该函数,它在进行预测时似乎工作得很好。剩下的就是从外面(网上)用了。为此,我们需要通过 AWS API Gateway 创建一个 API。
1。创建新的 API
- 访问 AWS API 网关,然后点击
Build
按钮选择 REST API。 - 选择协议:
REST
。为选择新 API,创建新 API** 。然后,填写 API 名称并添加一些描述。**
****
****图 23。通过 AWS API 网关为我们的应用程序构建新的 API
2。创建资源:预测和方法帖子
- 从
Actions
中选择制作资源>填写“预测”。 - 从
Actions
中选择制作方法>选择POST
****图 24。为新的 API 创建新的资源和方法
****3。选择 Lambda 函数,添加一些细节。
- 点击
POST
,然后确保为 Lambda 函数写下正确的名称,并保留默认设置。
****图 25。将 Lambda 函数集成到作为触发器的 API 中
4。测试 API 。
- 从流程图执行中,点击
Test
。
****图 26。客户端请求和 lambda 函数通过 API 的执行流程图
- 要测试它,在请求体中输入代码。点击测试。然后,我们应该在响应体中看到结果。****
****
****图 27。测试模拟客户端和 lambda 函数之间做出的预测
5。部署 API
- 最后,我们需要部署 API 以在外部使用它。从
Actions
点击DEPLOY API
。
****图 28。部署 API
- 从“调用 URL”部分获取 URL。在这种情况下,我们有:https://xw2bv0y8mb.execute-api.us-east-1.amazonaws.com/test
- 打开邮差 App 或者去 reqbin 测试我们刚刚创建的 REST API。注意,因为我们指定
predict
作为POST
的方法,我们需要在 URL 的末尾添加/predict
。因此,进行 API 调用以进行预测的完整 URL 是:https://xw 2 bv 0 y8 MB . execute-API . us-east-1 . amazonaws . com/test/predict - 将链接复制并粘贴到应用程序的 URL 部分。
- 复制 JSON 中的以下对象作为主体来发出这个 POST 请求。点击
Send
。
**{
"url": "https://tinyurl.com/clothes-t-shirt"
}**
- 在发出 API call POST 请求后,您可以看到预测结果作为接收到的内容。
图 29。通过 reqbin 调用 API 进行预测
- 或者,我们可以使用
cURL
(代表客户端 URL)通过终端(即 Git Bash)将 POST 请求中的数据(在本例中是 t 恤图像)发送到我们的服务(在本例中是衣服图像分类器)。
**$ curl -d '{"url": "https://tinyurl.com/clothes-t-shirt"}' -H "Content-Type: application/json" -X POST [https://xw2bv0y8mb.execute-api.us-east-1.amazonaws.com/test/predict](https://xw2bv0y8mb.execute-api.us-east-1.amazonaws.com/test/predict)**
- 运行上面的命令将生成这个预测结果:
图 30。通过 Git Bash 通过 API 调用进行预测
恭喜你。现在,您的深度学*模型完全在线,并准备好帮助世界变得更美好!
E.结论
由于要遵循许多程序,进行端到端深度学*项目可能具有挑战性。然而,由于 TensorFlow 的丰富特性,我们可以轻松地构建和部署模型。Docker 和 AWS 等服务的存在有助于数据科学家快速离线或在线交付应用程序,以解决业务问题。
以无服务器的衣服图像分类器为例,我希望这个项目给你一个基本的想法,如何在未来处理类似的情况。继续激励!
谢谢你,
diardano Raihan
LinkedIn 简介
注 :你看到的一切都记载在 我的 GitHub 资源库 里。 对于对完整代码感到好奇的人,请务必访问👍。
参考
- [1] T. Soam,《在窗口 10 上安装 Docker》, Medium ,https://Medium . com/@ tushar 0618/Installing-Docker-desktop-on-Window-10-501 e 594 fc 5 EB,2021 年 6 月。
- [2] A .格里戈里耶夫,《服装数据集(子集)》, GitHub ,https://github.com/alexeygrigorev/clothing-dataset-small,2021 年 6 月。
- [3] 机器学* Edu ,https://storage . Google APIs . com/mledu-datasets/inception _ v3 _ weights _ TF _ dim _ ordering _ TF _ kernels _ notop . H5,2021 年 6 月。
- [4]“在 Windows 上安装、更新和卸载 AWS CLI 版本 2”,亚马逊 Web 服务(AWS) ,https://docs . AWS . Amazon . com/CLI/latest/user guide/install-CLI v2-Windows . html,2021 年 6 月。**
每个卷积核都是一个分类器!你真的了解卷积神经网络是如何工作的吗?
提示和技巧
卷积和池层背后的信息流直观指南。卷积核不是“神奇的石头”,它学*使用基于梯度的优化来提取“某种”特征。卷积神经网络中数值的真正含义是什么?幕后信息如何在卷积层和池层之间流动?
来源:AdobeStock
《艾是新电》 —吴恩达。可以说深度卷积神经网络(CNN)和计算机视觉也是如此:卷积神经网络是计算机视觉的新电。
CNN 确实是机器学*社区的一颗钻石,尤其是在快速增长的计算机视觉领域。它们是计算机视觉中无数基本任务的支柱,从简单的图像分类(但在 20 多年前被认为是非常具有挑战性或棘手的)到更复杂的任务,包括图像分割、图像超分辨率和图像字幕。这就是为什么在过去的十年里,学术界和工业界对 CNN 越来越感兴趣。尽管许多工程师和学生每天都在练*和使用 CNN,但大多数人缺乏对卷积和池块这两个任何 CNN 最基本的构建块的全面理论观点。
来源:GitHub—gwding/draw _ conv net
对细胞神经网络有一个坚实的理论观点,特别是从信息论的角度来看,对于提出新的想法来显著提高深度学*模型在特定任务上的能力,甚至以无人能及的效率解决问题来说,尤为重要。在本文的其余部分,我将简要介绍信息论的概念,然后从信息论和统计学的角度详细解释 CNN 各层中数值的含义。我希望本文涵盖的内容能够帮助您以新的方式了解 CNN 的工作方式,并提出自己的 CNN 模型来更好地解决您的计算机视觉问题,在 Kaggle 上获得奖牌,甚至最终在 CVPR 和 ICCV 这样的顶级会议上发表论文。
深度神经网络中的信息论
你可能对“特征提取”这个概念很熟悉,这正是卷积核所做的一切(从最简单的意义上来说)。但是,你有没有尝试过深入了解更多关于“特征提取”的知识?具体来说,从数值的角度来看,卷积函数是如何进行特征提取的?到头来,CNN 里的只是数字,数字,数字。你的好奇心是否曾促使你去寻找 CNN 层中那些数字的意义和它们之间的关系?
虽然我在基于 CNN 的论文中多次遇到术语“信息流”,但我一直在努力寻找任何涵盖信息论的论文或书籍,或至少是其直观观点,在卷积神经网络背后的可满足的深度。
首先,我们将简要介绍信息论。
1.自我信息
假设一个事件 X=x 有概率 p 发生。那么我们将事件 x 的信息值 I 定义如下:
价值 I 的意义简单来说就是衡量事件 x 发生的知识有多少/有多少价值。发生 p 的几率越小,信息价值越高。直觉上,当一个罕见的事件发生时,人们认为它更有价值;相反,如果一个事件太普通,我们往往认为它不包含太多的信息价值。
比如你知道你下周一要上班( p = 0.99 ),那么这个你下周一要上班的知识其实是没有用的,因为它并没有改变你现在的任何决定。然而,如果你的老板突然告诉你,你明天必须去欧洲参加一个非常重要的商务活动( p = 0.01 ),那么这个信息的价值就非常高,它会对你当前的计划产生很大的影响。
2.熵
自我信息衡量的是单个离散事件的信息值,而熵则更进一步,捕捉一个随机变量的信息值,无论它是离散的还是连续的。
如果一个随机变量 X 有【p(X)的 p.m.f / p.d.f,那么熵定义如下:
- 如果 X 是连续的:
3.条件熵
同样,我们将随机变量 X 和 Y 的条件熵定义如下:
条件熵度量已知事件在【Y】发生时的期望信息值,假设我们知道事件在【X】已经发生。这一特性特别重要,因为它在低至像素级别的级别上与计算机视觉中的信息论直接相关。
4.卷积核中的信息论
几乎所有 CNN 的专业从业者都必须对卷积核的功能有深入的了解,包括步长和特征提取。如果那些概念你不熟悉,我强烈推荐这篇关于卷积神经网络介绍的很棒的文章:卷积神经网络。
在下一节中,我将介绍卷积核的信息论。
卷积核是分类器(惊喜?)
1.卷积评论
现代 CNN 中最常见的内核大小是 33* 内核。假设我们在原始 RGB 图像上应用单个 33* 内核,在感受野中观察到的像素总数将是 27 (H:3,W:3,C:3)。设卷积核为函数 f ,则 f: R ⁷ → R 。输入是 27 像素的图像部分,输出是标量值。
来源:作者图片
这个输出的标量值是什么?让卷积运算在复杂的视觉任务上工作如此高效的这个像素的真正意义是什么?
2.每个内核都是一个分类器
令人惊讶的是,输出的标量实际上是一个相对概率。换句话说,每个卷积核都是一个分类器!这是特征提取的核心思想!
假设内核 f: R ⁷ → R 负责提取一个特征(边缘、圆形物体、明亮物体等。)我们可以将内核 f 改写为:
直观的描述就是内核 f 观察其感受野的全部 27 个像素,学*分类图像中“被看到”的区域是否包含特征 k 或。假设 k 为“边缘”,请看下图 1 中的实验和评论,以获得直观的解释。
图 1:自然图像到特征空间的转换(来源:作者)
- 注意 :这只是相对概率,意思是输出的标量可以是任意实数,不一定在【0,1】**范围内。对于提取的特征图的可视化,已经应用了 softmax 变换来将值转换成精确的概率。**
综上所述,卷积核的核心思想来自信息论方面:
- 每个卷积核在技术上是一个分类器。它观察感受野中的一组像素,并输出一个标量来度量感受野中的图像区域是否包含核特征的概率。
- 一组输出的概率被组织为像素也使得提取的特征变得人眼可见。
- 输出像素的组织 p(特征 k|像素) 对应于原始图像中的像素位置。这个保持了原始图像中物体的整体结构和位置,这解释了信息如何在卷积层之间流动。
图 2:保留对象位置的信息流(来源:作者)
信息论视角下的池层直观观
与卷积核类似,池化也可以用信息论和信息流来解释。
从高层次理解池层的一些好处:
- 降维以实现更快的训练和更简单的学*曲线
- 增加信息密度,消除稀疏分布
从信息论角度看池层:
如前所述,深度特征图中的每个像素在技术上是原始图像中相应区域包含特征 k 的相对概率。高值表示被观察区域很可能具有特征 k ,反之亦然。**
由于图像本质上具有稀疏的信息分布,并且汇集层负责以最小的信息损失进行密集分布。
下图有助于阐明池层之间的信息流:
图 3:信息密集化(来源:作者)
对于相邻像素的移除(例如,在移除 a1 、 a2 、 a7 的同时保留 a8 )有一种自然的担忧:它是否会显著影响层间的信息流并损害 CNN 的性能?答案是肯定会有信息损失,但这是一个可以接受的损失,以使特征图中的每个像素更有意义。 a1 、 a2 、 a7 、 a8 是相邻像素,倾向于相关。因此,这些像素组合的熵增益并不显著,我们可能只需要 1 个像素来表示特征图中这 4 个像素的信息。汇集层过滤三个较不重要的像素,即 a1 、 a2 、 a7 ,并保留 a8 作为流向更深层的唯一信息。
讨论
下面是一系列后续问题,可以帮助你从信息流和信息论的角度更好地理解 CNN:
- 剩余连接中加法的数值意义是什么?传递什么信息?究竟为什么卷积可能有信息损失?(提示:每个卷积核可以捕获 1 个特定的特征,可以是简单的,也可以是复杂的,有些特征集不重叠)。
- 为什么我们使用最大池,有时使用平均池?最小公摊有什么好处?
- 池层是否被认为是 CNN 的瓶颈,为什么是或者不是?
参考
[1]潜入深度学*一书,18.11 节:《信息论》:
潜入深度学*—潜入深度学* 0.17 文档(d2l.ai)
[2]用信息论理解卷积神经网络:初步探索:1804.06537.pdf(arxiv.org)
[3] 机器学*的信息增益和互信息
[4] 信息熵的温和介绍
[5] 卷积神经网络
用 Python 免费标注图像的最简单方法
图像标记过程的需求量很大
照片由 亚历山大·巴甫洛夫波德瓦尔尼发自 派克斯
当我们谈论自动驾驶汽车、面罩检测、活动跟踪、对象分割、车牌检测时,每个用例都需要将图像标记作为关键步骤。
图像标记是一个手动过程,需要大量时间来完成。在我们开始深入研究图像标记过程之前,让我们先了解什么是图像标记以及我们为什么需要它。
图像标记是给图像中出现的特定对象分配特定标签的过程,以便我们的机器能够区分它们。
图像标记用于计算机视觉领域。每个对象检测系统、图像分割模型、活动检测、运动跟踪器模型都利用图像标记过程作为初始步骤,同时为我们的模型准备数据。
现在,既然你对图像标记和我们为什么使用这个图像标记过程有了一些了解,让我们现在看一下标记我们的图像的一步一步的过程。
要开始这个过程,您必须在计算机上安装 python。这是这项任务的唯一要求。
设置环境
对于我们的图像标记任务,我们将使用名为“labelImg
”的开源 python 包。这个包可以帮助我们标记我们的图像。这个库利用 pyqt5 为我们提供一个 GUI 环境来完成我们的任务。
如果您的机器上安装了 Git 在命令提示符下运行下面的命令来克隆它。
git clone [https://github.com/pranjalAI/labelImg.git](https://github.com/pranjalAI/labelImg.git)
如果你没有安装 Git,那么你可以直接从这里下载。只需点击下载按钮,软件包就会被下载。下载后,您可以将其解压缩到任何文件夹中使用。
安装 d 附件
我们需要安装一个名为“pyqt5”的依赖库来使 labelImg 工作。让我们安装它。
- 打开命令提示符。
- 移动到从源文件下载的名为 labelImg 的文件夹。
- 现在,运行下面的命令
pip install pyqt5
设置预定义的类
您需要移动到“labelImg\data”文件夹;在那里,您会找到 predefined_classes.txt
您可以键入图像中的对象名称列表。每个新对象都应该在新的一行。
作者截图
运行包
因为这是一个 python 包,所以我们可以像运行任何 python 脚本一样运行它。一旦运行 python 脚本(如下所示),将会打开一个窗口,它是 python UI 界面的一部分。
这就对了。现在你会发现一个新的 UI 窗口,里面有一些功能。
作者截图
图像标签在行动
这个 python 库的目标是标记我们的图像。您可以将一些图像收集到一个文件夹中以供使用。一旦工具启动,你可以在左边找到一些选项。
- 点击“打开目录”选项,选择保存您需要标记的图像的文件夹。
- 现在您需要设置一个路径来保存您的对象细节。单击“更改保存目录”并选择一个文件夹来保存您的对象详细信息。
Gif :作者记录
对象细节文件包含图像中对象的坐标和图像的标签。
现在,要标记显示的图像,首先需要设置标签类型。可以在左下方做。在这里,我做的是 YOLO 物体检测,所以我选择了相应的。
然后,你可以点击创建矩形按钮,开始绘制边界框。
Gif :作者记录
在绘制边界框时,您可以选择我们之前定义的类。
标记过程完成后,您可以转到对象详细信息文件夹,找到一个单独的图像文本文件。
作者截图
给你!我们现在有了带有各自标签文件的图像,可以用于我们的对象检测任务。
结论
在这篇文章中,我们已经涵盖了一步一步的过程来标记我们的图像。我们利用了开源 python 包 labelImg。图像标记任务是手动且耗时的。但是,你在这个过程中花费的时间越多,你的模型就越好。
我希望你喜欢这篇文章。在我接下来的文章中,我们将看到如何利用这些图像来训练我们自己的物体检测系统。
敬请期待!
以下是我的一些最佳选择:
https://towards data science . com/7-amazing-python-one-liners-you-must-know-413 AE 021470 f
https://better programming . pub/10-python-tricks-that-wow-you-de 450921d 96 a
如果你想获得更多关于数据科技的精彩知识,有兴趣了解我最喜欢的藏书 点击这里订阅我的免费频道 。我将很快开始为所有 python &数据科学爱好者发布一些很酷的东西。
寻找数据科学& python 咨询 —这里可以联系我。随时订阅我的免费简讯: Pranjal 的简讯。
记忆正则表达式(Regex)的最简单方法
提示和技巧
理解正则表达式的基础,以便更有效地提取模式
正则表达式示例—按作者排序的图像
如果你像我一样,在开始的时候,正则表达式模式看起来就像胡言乱语。但是仔细观察之后,你会发现它们实际上并不像看起来那么令人生畏。
和许多概念一样,从一个例子开始通常是有帮助的,所以让我们在这里使用这个例子:
假设我们正试图从一个电子邮件地址的域名中提取名字,如下所示:example@ gmail 。com 因此,在这种情况下,我们将尝试只提取“gmail”部分。
为了练*,我强烈推荐你使用这个网站:https://regex101.com/。
在我们开始处理我们的例子之前,让我们看看一些你可能会遇到或需要的常见字符。
+
正则表达式中的字符+表示“匹配前面的字符一次或多次”。例如ab+c
匹配“abc”、“abbc”、“abbbc”但不匹配“AC”。正则表达式中使用的加号字符称为克莱尼加号,以数学家斯蒂芬·克莱尼(1909-1994)的名字命名,他引入了这个概念。来源*
这个字符在正则表达式中的意思是“零次或多次匹配前面的字符”。例如ab*c
匹配“abc”、“abbc”、“abbbc”、“AC”。这也被称为克林星。- 问号
**?**
表示前面的元素出现零次或一次。比如colou?r
既匹配“颜色”,又匹配“色彩”。 .
句号匹配任何单个字符。例如a.c
匹配“abc”、“adc”、“aec”等。如果我们想要匹配字母“c”之前的多个字符,我们只需使用上面的星号*
就像这样:a.*c
并且这将匹配“ab defghc”。- 这非常有用,因为它定义了一个可能值的范围。这里它只是简单的指从 a 到 z 的所有小写字母,对于大写字母和所有正数我们也可以这样做,像这个
[A-Z]
&[0-9]
。 ^
: 匹配任意一行的起始位置。[^b]at
匹配.at
匹配的除“蝙蝠”以外的所有字符串。因此,当在方括号内使用时,^
后面的字母被排除在外。^[hc]at
匹配“hat”和“cat”,但仅在字符串或行的开头。
现在我们已经准备好了,让我们再来看看我们的例子。因此,要获得电子邮件地址域的名称部分,我们将使用以下正则表达式:
@([a-zA-Z0-9_.+-]+)
\ .[a-zA-Z0-9_.+-]
正则表达式示例—作者图片
这将返回“gmail”,这正是我们想要的。
下面是我们例子中每个元素的分解。
- @:这表示我们希望我们的搜索从“@”开始。
()
:这个括号允许我们只分离出我们想要返回的搜索模式的一部分。移除这些括号并在正则表达式的末尾添加一个+
,将允许我们匹配“@”😡 Gmail . com 之后的所有内容。[]
:方括号允许我们包含我们想要匹配的所有字符,甚至包含这样的范围:a-z 和 0-9。范围后面还有其他字符,如“_”、“”、"+"、"-"等等,这些都是我们的表达式要寻找的文字字符。如果需要的话,我们还可以包括像德国字母这样的特殊字符。"ü"+
:我们已经看到加号一次或多次匹配前面的字符。因为这在我们的方括号之外,我们基本上多次匹配方括号中的所有内容。酷是吗?- :退格键对句号进行转义,这样我们的正则表达式就不会认为这是上面定义的量词的一部分。我们需要句号成为我们模式的一部分,因为它出现在我们的搜索词“example@gmail.com”中
.
:句号在方括号()
之外,所以我们的正则表达式将查找该点之前的所有字符串以及其后的方括号。
为了确保“@gmail.com”在字符串的末尾,可以包含+$
。$
断言该位置位于行尾。所以我们的成品会是这样的
@([a-zA-Z0-9_.+-]+)
\ .[a-zA-Z0-9_.+-]+$
现在,假设我们只想从电子邮件地址“example@gmail.com”中提取“example”,我们只需在上面使用的模式前面添加另一个模式,就像这样([a-zA-Z0-9_.+-]+)
。注意,方括号()
现在位于正则表达式的第一部分之前,表明这是我们想要提取的唯一部分。
([a-zA-Z0-9_.+-]+)@[a-zA-Z0-9_.+-]+
\ .[a-zA-Z0-9_.+-]
作者图片
额外的
要使用正则表达式提取德语字符,只需将它们包含在您的模式中,如下所示:
([a-zA-Z0-9äüö_.+-]+)@[a-zA-Z0-9_.+-]+
\ .[a-zA-Z0-9_.+-]
这将从“schfer@example.com返回“schfer”
这是一个非常简单的例子,还有许多其他方法可以达到同样的效果。但是我相信这有助于获取更复杂的正则表达式。
我希望你觉得这很有帮助,并在评论中分享其他的技巧和诀窍。
可以 成为中等会员 享受更多这样的故事。
参考文献:
- https://cloud . Google . com/big query/docs/reference/standard-SQL/string _ functions # regexp _ instr
- https://regex101.com/
- https://en.wikipedia.org/wiki/Regular_expression
轻松下载用于 AWS Lambda 图层的 Python 包
两分钟内即可上传 AWS Lambda 图层包。使用 Lambda 为我们的 Lambda 创建图层
图片由 Pixabay 提供
AWS 云服务,如 AWS Lambda,是一种快速部署数据解决方案的灵活方式。但是,在部署复杂的 Lambda 基础设施时,处理包和相关的依赖关系可能会令人沮丧,即使您使用的是 Lambda 层。
AWS 使用他们自己风格的 Linux 操作系统。这意味着我们不能只是在本地 pip 安装 Python 包,然后直接部署到 AWS Lambda。通常,在上传到 Lambda 之前,我们需要使用 Docker 或 EC2 来准备 Python 包层。
事实证明,我们可以使用 AWS Lambda 来自动化 AWS Lambda 层准备..
如何:
1.在 S3 创建一个着陆桶
- 在 S3 创建一个存储桶。我们将所有的 Python 包放入这个新的桶中。注意:我们将创建的 Lambda 将自动按照版本组织我们下载的 Python 包。
2.创造一个新的λ
-
创建一个新的 Lambda,确保添加一个内联策略,以允许该 Lambda 将对象放入和删除到 S3 的目标着陆桶中。
-
一定要调整 Lambda 的最大内存和超时,以适应更大的包/依赖项。(例如熊猫用了 280MB,用了 15.153 秒)
-
将以下代码粘贴到您的 Lambda 中:
-
确保将参数与您想要下载的 Python 包、其版本以及您的目标 S3 桶进行交换。
4.享受
花一便士(或者更少)来编译你的 Lambda 层绝对值得节省时间。
在 Lambda 运行之后,您可以检查您的 S3 桶中是否有 Lambda 准备的 Python 包。
按包装分类的 S3 时段组织示例。图片作者。
您请求的每个包都会自动根据其版本进行组织。所以你可以下载同一个包的不同版本(如果需要的话),在 S3 很容易找到。
Pandas 包版本可以作为 Lambda 层上传。图片作者。
现在,您可以轻松地从 S3 Bucket 下载 zip 文件,并将其作为一个层上传到 AWS Lambda。
感谢阅读~。
只需几个步骤,即可在地形图上轻松绘制地震数据
我们从 CSV 文件中读取地震数据,并使用 PyGMT 将其绘制在地图上
地图上的地震数据(图片作者提供)
在本帖中,我们将首先读取一个包含地震位置(纬度和经度)、震级和深度的 CSV 文件,然后将其叠加在地形图上。使用 PyGMT API 自动下载地形数据。
读取事件数据的 CSV 文件
我有一个包含地震事件数据的 CSV 文件。数据来自 PyGMT 示例数据集。它可以在本地获得和保存:
data = pygmt.datasets.load_japan_quakes()
data.to_csv('my_events_data.csv', index=False)
数据是表格形式的,所以我们可以使用 pandas 的read_csv
方法来读取它。
import pandas as pd# this file is retrieved from the pygmt dataset
data = pd.read_csv('my_events_data.csv')
在地形图上绘制数据
首先,我们根据数据定义区域。
# Set the region
region = [
data.longitude.min() - 1,
data.longitude.max() + 1,
data.latitude.min() - 1,
data.latitude.max() + 1,
]
然后我们初始化 PyGMT“Figure”对象:
fig = pygmt.Figure()
接下来,我们获得高分辨率的地形数据,并使用标准化的etopo1
色图绘制它。我们设定最低海拔为-8000 米,最高海拔为 5000 米。如果在此范围之外有任何地形区域,则用白色标出。我们选择宽度为 4 英寸的“墨卡托”投影进行绘图。
# make color pallets
pygmt.makecpt(
cmap='etopo1',
series='-8000/5000/1000', #min elevation of -8000m and max of 5000m
continuous=True
)# define etopo data file
topo_data = "[@earth_relief_30s](http://twitter.com/earth_relief_30s)"
# plot high res topography
fig.grdimage(
grid=topo_data,
region=region,
projection='M4i',
shading=True,
frame=True
)
接下来,我们用海岸线绘制海岸线,并在地图上添加了一个框架。
fig.coast(shorelines=True, frame=True)
最后,我们绘制数据:
# colorbar colormap
pygmt.makecpt(cmap="jet", series=[
data.depth_km.min(), data.depth_km.max()])
fig.plot(
x=data.longitude,
y=data.latitude,
sizes=0.1*data.magnitude,
color=data.depth_km,
cmap=True,
style="cc",
pen="black",
)
fig.colorbar(frame='af+l"Depth (km)"')
fig.savefig('my_earthquakes.png')
我们用代表地震深度的颜色和代表地震震级的圆圈的大小来绘制数据。我们在底部添加了水平色条。
地图上的地震数据(图片作者提供)
完整脚本
原载于 2021 年 5 月 28 日 http://www.earthinversion.com**的 。
蟒蛇皮复活节彩蛋
探索八个用 Python 包装的复活节彩蛋
在 Unsplash 上由 Urszula Trojanowska 拍照
大多数现代编程语言和软件产品都带有一些隐藏的功能,我们通常称之为“复活节彩蛋”。在这篇文章中,我们将讨论复活节彩蛋的目的以及为什么首先要实现它们。此外,我们将探索 Python 核心语言附带的八种不同的复活节彩蛋。
编程语言中的复活节彩蛋是什么?
一般来说,在编程语言和软件产品中,复活节彩蛋是一个隐藏的功能或不寻常的行为,通常没有记录在案。它主要是为了娱乐的目的而实现的,因为在软件开发的环境中,这样的功能并不真正服务于任何目的。复活节彩蛋可以有多种形式;从印刷文本到图像或视频。
据说第一枚复活节彩蛋是 1979 年由雅达利公司的沃伦·罗比内特引进的。该公司在游戏行业运营,想要删除开发名为冒险游戏的开发者的名字,试图阻止其竞争对手雇佣他们。这一政策的结果是,许多后来建立了 Activision 的开发人员决定离开公司,因为他们的努力没有得到认可。然而,没有离开雅达利的罗比内特决定嵌入一个隐藏的功能,当被触发时,会在屏幕上显示一个文本“由沃伦·罗比内特创建”。
照片由 Senad Palic 在 Unsplash 上拍摄
公司里没有人知道这件事,因为罗比内特设法保守了这个秘密,但后来一个 15 岁的孩子发现了这件事,并通知了雅达利。这名少年甚至明确指示公司如何进入“密室”并拿到复活节彩蛋。
我相信这个秘密房间被独立地发现了好几次,因为有一个关于灰点存在的非常强有力的线索,这是进入我签名的秘密房间所需要的。我知道的第一个人是一个来自盐湖城的 15 岁男孩,他给雅达利写了一封信,解释了如何进入密室,并询问雅达利的意见。——太平日子:沃伦·罗比内特
请注意,在试图将复活节彩蛋包含到软件产品中时,您应该始终小心,因为这可能会对公司的形象产生负面影响。有许多公司不允许实现这样的功能。即使你的合同没有任何关于复活节彩蛋的明确声明,也要确保一旦公司或客户发现,你不会丢掉工作。
既然我们已经在软件产品的上下文中讨论了复活节彩蛋,那么让我们看看用 Python 核心语言打包的 8 个已知的复活节彩蛋!
# 1:所有时代经典:你好世界
一个 Hello World 程序是最简单的程序,通常由刚刚开始编程的人编写。下面显示的复活节彩蛋实现了这个程序,它简单地将字符串“Hello World”输出到标准输出。
# 2:Python 的禅
Python 的禅是一套 19 条格言,最初由 Tim Peters 编写,并在 PEP-20 中介绍。这个集合的目的是围绕 Python 的设计提供指导原则。核心语言附带了一个复活节彩蛋,通过简单地导入模块this
,它将这些格言打印到标准输出中。
Python 的禅最初有 20 句格言。但是导入this
的时候只会打印出来 19 个。现在你可能想知道在哪里可以找到第 20 句格言。
良好的..我们不打算在这篇文章中披露。轮到你挖东西了!
关于this
模块有趣的事实是,它违反了 Python 禅宗定义的大部分格言。打印格言的代码如下所示
这段代码绝对是:
- 又丑又不漂亮
- 隐式和非显式
- 复杂而非简单
- 复杂而不是复杂
- 嵌套而非扁平
- 不可读
- 实现很难解释
如果你想知道代码是如何工作的,那么你可以仔细看看 ROT13(旋转 13 位),这是一种替代密码,发明于古罗马,用于用第 13 个字母替换一个字母。
请注意,Python 之禅是官方记录的唯一复活节彩蛋。
# 3:反重力是真的吗?
这对漫画迷来说是一个很好的复活节彩蛋。一旦您导入名为antigravity
的模块,就会在您的默认浏览器上打开一个显示漫画的页面,它本质上演示了用 Python 编写和使用模块是多么容易。请注意,浏览器上的选项卡在每个会话中只会打开一次。
# 4:地理哈希
在antigravity
模块中添加的另一个有趣的复活节彩蛋是geohash
函数,它使用门罗算法,根据所提供的日期时间上的道琼斯工业平均指数生成随机的 GPS 坐标。
下面给出了 antigravity.py 的源代码。
# 5:Python 中的大括号?那永远不会发生...
虽然大多数现代编程语言都使用大括号来将代码块括在特定的命令下(例如在循环中),但 Python 不会这样做。相反,Python 程序员必须使用缩进来表示代码块。这不仅仅是为了可读性 Python 解释器会相应地解析缩进,如果它们有问题,就会引发异常。
下面的复活节彩蛋取笑了其他编程语言。当试图从模块__future__
导入braces
时,会报告一个相当不寻常的SyntaxError
!
# 6:通向无限和更远..
Brian McGowan 在 Unsplash 上拍摄的照片
散列函数返回一个对象的散列值(即一个整数),如果它有一个对象的话。有趣的是,infinity 的哈希值好像是 314159!
# 7:终身友好的语言叔叔
在 2009 年的 Python 大会上,Barry Warsaw(也被称为 Barry 叔叔)被选为 Guido 的继任者,Guido 在 1989 年编写了 Python 的原始实现。
Barry 叔叔意识到 Python 3 中引入的不等式运算符!=
是一个错误。从 Python 3.1 开始,程序员可以通过简单地从__future__
模块导入barry_as_FLUFL
来恢复到<>
不等式运算符。
# 8:命名变量..希腊方式!
尽管在源代码中命名变量时使用 unicode 字符被认为是一种不好的做法,但是 Python3 引入了一个特性,在命名标识符时支持 unicode 字符。
唯一可能方便的用例是在编写通常使用希腊字母的科学符号时,但是我个人还是不鼓励你在命名变量时使用这样的字符。
结论
在本文中,我们介绍了复活节彩蛋在编程语言和软件产品中的含义。我们讨论了在雅达利冒险游戏中引入的第一个复活节彩蛋的历史。此外,我们还探索了用 Python 核心编程语言打包的八个有趣的复活节彩蛋。
享受你的假期,保持安全和快乐的复活节!
简单,轻松,EDA —我最新功能的更新
简化您的探索性数据分析流程,以便您可以更快地获得洞察力
在我最新的博客文章中,我写了一个我在从事最新项目时创建的函数,以在 EDA 过程中简化代码和节省时间。对于那些不熟悉这篇文章的人来说,这是我创建的函数:
def initial_eda(df):
# List of categorical columns
cat_cols = df.select_dtypes('object').columns
for col in cat_cols:
# Formatting
column_name = col.title().replace('_', ' ')
title= 'Distribution of ' + column_name
# Unique values <= 12 to avoid overcrowding
if len(df[col].value_counts())<=12:
plt.figure(figsize = (8, 6))
sns.countplot(x=df[col],
data=df,
palette="Paired",
order = df[col].value_counts().index)
plt.title(title, fontsize = 18, pad = 12)
plt.xlabel(column_name, fontsize = 15)
plt.xticks(rotation=20)
plt.ylabel("Frequency",fontsize = 15)
plt.show();
else:
print(f'{column_name} has {len(df[col].value_counts())} unique values. Alternative EDA should be considered.')
returnSome examples of the visuals created from this code:
通过该函数运行奥斯汀动物中心数据集后,产生了以下一些视觉效果:
作者提供的图片
正如我在上一篇文章中提到的,虽然这在最初的 EDA 中运行良好,但仍有很大的改进空间。例如,上面的函数只为具有 12 个或更少唯一值的列提供可视化效果。具有更多唯一值的用户提供了以下打印语句:
虽然这确实提供了关于这些列中每一列的一些信息,但是错过了提取关于可能有价值的数据的洞察力的机会。下面是该函数的一个新的更新版本:
def initial_eda(df):
# List of categorical columns
cat_cols = df.select_dtypes(['object','category']).columns
for col in cat_cols:
# Formatting
column_name = col.title().replace('_', ' ')
title = 'Distribution of ' + column_name
unique_values = len(df[col].value_counts())
# unique_values <=12 to avoid overcrowding
if unique_values<=12:
plt.figure(figsize = (10, 6))
sns.countplot(x=df[col],
data=df,
palette="Paired",
order = df[col].value_counts().index)
plt.title(title, fontsize = 18, pad = 12)
plt.xlabel(column_name, fontsize = 15)
plt.xticks(rotation=20)
plt.ylabel("Frequency",fontsize = 15)
plt.show();
else:
print(f'{column_name} has {len(df[col].value_counts())} unique values. Here are the top 10:')
print()
col_count = df[col].value_counts()
col_count = col_count[:10,]
plt.figure(figsize = (12, 6))
sns.barplot(x = col_count.index,
y = col_count.values,
palette="Paired")
plt.title(f'Top 10 {column_name}s', fontsize = 18, pad = 12)
plt.ylabel('Frequency', fontsize=15)
plt.xticks(rotation=20)
plt.xlabel(column_name, fontsize=15)
plt.show()
return
如果列有超过 12 个唯一值,这个函数的更新版本现在将创建一个包含 10 个最常见的唯一值的条形图,而不是返回一个 print 语句。一些例子:
作者提供的图片
这一功能的增加为我们提供了更多的信息,并提出了我们可能选择进一步研究的问题。例如,从这些图片中我们可以看到,编号为 A721033 的动物已经进入庇护所超过三十次。为什么会这样?是什么动物类型?这只动物是流浪狗吗?这种动物以前被领养过吗?这些额外的视觉效果可以提供有价值的见解,并在建模过程之前引导项目的方向。
该功能的最后一个附加功能是确保生成的视觉效果在美学上令人愉悦。虽然函数本身为我们提供了有意义的信息,但“一刀切”的方法可能会创建视觉上不吸引人的条形图。例如:
作者提供的图片
也许这是我在吹毛求疵,但我看着这些图表,知道它们可以改进。在我看来,具有两个唯一值的列不应该与具有十二个唯一值的列具有相同的图形大小。下面是最后一个更新的函数,它根据唯一值的数量来配置图形大小:
def initial_eda(df):
# List of categorical columns
cat_cols = df.select_dtypes(['object','category']).columns
for col in cat_cols:
# Formatting
column_name = col.title().replace('_', ' ')
title = 'Distribution of ' + column_name
unique_values = len(df[col].value_counts())
# If statements for figsize
if 2<=unique_values<3:
plt.figure(figsize = (4, 6))
sns.countplot(x=df[col],
data=df,
palette="Paired",
order = df[col].value_counts().index)
plt.title(title, fontsize = 18, pad = 12)
plt.xlabel(column_name, fontsize = 15)
plt.xticks(rotation=20)
plt.ylabel("Frequency",fontsize = 15)
plt.show();
elif 3<=unique_values<8:
plt.figure(figsize = (8, 6))
sns.countplot(x=df[col],
data=df,
palette="Paired",
order = df[col].value_counts().index)
plt.title(title, fontsize = 18, pad = 12)
plt.xlabel(column_name, fontsize = 15)
plt.xticks(rotation=20)
plt.ylabel("Frequency",fontsize = 15)
plt.show();
elif 8<=unique_values<=12:
plt.figure(figsize = (10, 6))
sns.countplot(x=df[col],
data=df,
palette="Paired",
order = df[col].value_counts().index)
plt.title(title, fontsize = 18, pad = 12)
plt.xlabel(column_name, fontsize = 15)
plt.xticks(rotation=20)
plt.ylabel("Frequency",fontsize = 15)
plt.show();
else:
print(f'{column_name} has {len(df[col].value_counts())} unique values. Here are the top 10:')
print()
col_count = df[col].value_counts()
col_count = col_count[:10,]
plt.figure(figsize = (12, 6))
sns.barplot(x = col_count.index,
y = col_count.values,
palette="Paired")
plt.title(f'Top 10 {column_name}s', fontsize = 18, pad = 12)
plt.ylabel('Frequency', fontsize=15)
plt.xticks(rotation=20)
plt.xlabel(column_name, fontsize=15)
plt.show()
return
现在,这个函数将呈现这些视觉效果:
作者提供的图片
在我看来,这些看起来更有吸引力。
探索性数据分析(EDA)可以塑造项目的方向。这是我在数据科学过程中最喜欢的阶段之一,因为在这一阶段可以充分探索一个人的好奇心。虽然数据科学的“魔力”在建模阶段显现出来,但是确定和优化模型以充分发挥其潜力要从正确的数据探索开始。
我希望我自己和其他人不仅可以利用这个功能节省时间,更重要的是利用节省下来的时间来拓展我们的好奇心。
重要的是不要停止质疑。好奇心有它存在的理由。当一个人思考永恒、生命和现实的奇妙结构的奥秘时,他不禁感到敬畏。如果一个人每天仅仅试图理解这种神秘的一点点,这就足够了。永远不要失去神圣的好奇心。—阿尔伯特·爱因斯坦,《生活》杂志(1955 年 5 月 2 日)
要了解更多关于奥斯汀动物成果项目的信息,你可以在我的个人 GitHub 这里查看。
使用 PyTorch 的树助推器容易自定义损失
当您可以让 PyTorch 为您计算时,为什么要为您的自定义目标计算一阶和二阶导数呢?
诸如 Catboost 、 XGBoost 和 LightGBM 之类的树加速器是强大的工具,尤其是在处理表格数据时。他们支持各种现成的损失,但有时你想使用量身定制的损失,一些有特殊魅力的东西,让你的模型闪闪发光。
幸运的是,所有常见的树提升包都支持自定义目标。不幸的是,为了建立一个模型,你必须提供一个函数来计算你的目标相对于模型输出的一阶和二阶导数,谁愿意这么做?我们已经有现代深度学*包可以为我们做到这一点。只需[pip install treeboost_autograd](https://pypi.org/project/treeboost_autograd/)
然后为 CatBoost、XGBoost 或 LightGBM 定义您的自定义损耗,就像这样简单:
PyTorch 前来救援
让我们用火炬来做这个重担。假设您有一个标量目标值(例如 minibatch MSE )和一个模型预测的一维向量。首先用 pytorch 计算objective
w.r.t preds
的一阶导数:
create_graph=True
告诉 pytorch 为导数构建一个计算图,允许我们在其上计算更高级别的导数。
然后,把一阶导数的每个元素当作一个标量,重新推导出 w.r.t preds
:
retain_graph=True
告诉 pytorch 在计算完导数后不要释放计算图,因为我们需要多次使用它。
这就是它的要点。现在我们只需要将它插入到一个 Catboost 友好的目标类中,并开始使用我们的自定义损失!
定义自定义损耗
我们将使用波士顿房价玩具数据集作为例子。比方说,我们希望我们的模型避免低于房价,而不是高于房价,也就是说,对于低于实际房价的预测,我们希望损失更严重。设 x =(预测目标):
作者图片
作者图片
训练模型
现在我们需要做的就是将我们的自定义损失函数插入到一个CatboostObjective
对象中,并训练一个CatBoostRegressor
。
为了了解我们的自定义损失,我们可以绘制模型预测和目标之间相对差异的直方图。正如你所看到的,我们的定制“不低于”模型确实低于目标价格,比使用默认对称 RMSE 损失训练的模型低得多。自然,我们的模型更容易超过目标价格。
作者图片
使用标准 Pytorch 损耗
同样的原理也适用于标准 pytorch 损耗。如果您想使用默认的 Catboost 超参数进行调优,请选择reduction="sum"
。例如,使用这些自定义损失(几乎)等同于使用 Catboost 的默认损失进行分类和回归:
自己用
图片来自皮克斯拜
好吧好吧。没必要为此慌张。只需[pip install treeboost_autograd](https://pypi.org/project/treeboost_autograd/)
就可以了。
完整的实现可以在 git repo 以及回归和二进制分类的现成例子中找到——针对 CatBoost、XGBoost 和 LightGBM。
https://github.com/TomerRonen34/treeboost_autograd
也可以在 PyTorch 论坛上查看一下迪奥戈·佩恩斯关于如何计算二阶导数的精彩回答。
使用 AirMap 轻松绘制数据
AirMap 是一个由 Airtable 支持的数据映射器
图片由 Pixabay 提供
轻松跟踪数据字典、映射、源、列和验证。 AirMap 是一个由 Airtable 支持的数据映射器。查看 Github repo 了解更多信息。
AirMap 是一个数据 ETL 工具,旨在使用云托管的文档表来帮助管理数据流。这允许我们使用一个集中的“真实来源”来获取我们需要了解的关于数据的任何信息(需求、来源、验证等)。AirMap 利用我们在 Airtable 中创建的文档表来处理数据映射、数据源合并以及这两者之间的一切,因此您不必。无需不断更新数据管道来满足新的数据集要求。只需更新 Airtable 中的主映射,让 AirMap 完成剩下的工作。
AirMap 的优势:
- Airtable 将您需要的所有数据信息集中在一个地方。
- 更新 Airtable 中的主数据映射,AirMap 将自动更新您的管道,以匹配您在云中拥有的内容!
- 无需来回发送静态文档,通过 Airtable 易于使用的 web/桌面 GUI 轻松与其他人协作。
- 快速管理、控制和共享您的数据映射、验证和数据字典。
想自己试试 AirMap 吗?你可以在这里 下载一份 demo 。
将所有数据源放在一个地方
我们可以使用 Airtable 来跟踪我们所有的数据源。提供数据集及其来源的集中文档。
AirMap 使用此信息根据您在 Airtable 中概述的要求来验证管道中的数据。
轻松跟踪和更新数据需求
Airtable 类似数据库的结构允许我们在数据源和它们对应的列之间创建链接。
这意味着我们可以确保使用正确的数据细节、验证和/或需求来操作我们的数据。每条记录都是独一无二的,这使得 AirMap 避免了代价高昂的人为错误。
快速设计和部署数据映射
Airtable 类似 excel 的 GUI 允许用户轻松地更新和创建数据映射。当传入(或传出)数据集需求不断变化时,这尤其有用。
点击下面的链接查看完整的 Airtable 库:
https://airtable.com/embed/shr9Tr2wp5rs2Bm7a?backgroundColor=red
如何使用 AirMap
AirMap 很容易集成到现有的数据管道结构中。像往常一样聚合数据源,然后将数据传递给 AirMap。
你可以尝试使用我在这个 Github repo 中提供的样本数据和 Jupyter 笔记本来测试 AirMap。
AirMap 读取您在 Airtable 中创建的映射结构,以生成预期的数据集输出格式。
从 Airtable 中检索指定的数据映射,并转换输入数据以生成映射的输出
来自 AirMap 的结果数据
您还可以直接在 Python 环境中轻松检查您的数据映射需求。这使我们能够仔细检查我们的数据源,验证需求,并确保我们通过 AirMap 传递正确的数据集。
查询 Airtable 以获取所选数据映射的映射详细信息
“项目 A —客户总结”数据图的结果数据图详情
AirMap 的当前版本是一个概念验证。将添加数据验证和(潜在的)数据清理的附加功能。感谢阅读!
用于命名实体识别的变压器的简单微调
如何轻松地微调任何自然语言处理转换器,以用于任何语言的命名实体识别。
照片由 vinsky2002 在 Pixabay 上拍摄
在本文中,我们将介绍如何针对任何语言中的命名实体识别(=NER),轻松地微调任何预训练的自然语言处理(=NLP)转换器。
你为什么要在乎?NER 是一个强大的 NLP 任务,有很多应用,在《走向数据科学》中已经有详细描述。然而,有效地使用 NER 通常需要基于预训练的转换器对您的 NER 模型进行语言或领域特定的微调,这些转换器在给定的计算预算下是可用的和现实的。
为了向您展示如何做到这一点,我们使用 python 包[NERDA](https://github.com/ebanalyse/NERDA)
来微调 NER 的 BERT 转换器。
NERDA
是一个通用的 NER 系统,可以用最少的代码对任何语言中的任何NER 变压器进行微调。
命名实体识别对于初学者来说
如果你不熟悉 NER,看看维基百科的定义:
命名实体识别(也称为(命名)实体识别、实体分块和实体提取)是信息提取的自然语言处理子任务,其寻求定位非结构化文本中提到的命名实体并将其分类成预定义的类别,例如人名、组织、位置、医疗代码、时间表达式、数量、货币值、百分比等。
我们可以用一个 NER 任务的例子进一步说明这一点。
任务:识别文本中的人名和组织:
吉姆购买了 300 股 Acme 公司的股票
解决方案:人员:' Jim ',组织:' Acme Corp . '
为了介绍本文中提到的其他概念和技术,我们在资源部分列出了一些以前的数据科学故事。
成套工具
现在,让我们转到为 NER 实际微调变压器。
不管您选择的转换器和目标语言是什么,我们向您展示的步骤都是相同的。
我们将利用新的 python 包[NERDA](https://github.com/ebanalyse/NERDA)
来完成这项工作。
PIN @ Ekstra Bladet 的‘NERDA’python 包的官方标志。
NERDA
有一个易于使用的界面,用于微调命名实体识别任务的 NLP 转换器。它基于流行的机器学*框架[PyTorch](https://pytorch.org/)
和拥抱脸 [transformers](https://pypi.org/project/transformers/)
。
NERDA
是开源的,可以在 Python 包索引(PyPI)上获得。它可以与以下设备一起安装:
pip install NERDA
资料组
我们将使用带有 NER 注释的英语数据集来训练和验证我们的模型。
首先,我们下载数据集并加载预定义的训练和验证数据分割。
from NERDA.datasets import get_conll_data, download_conll_data download_conll_data()
training = get_conll_data('train')
validation = get_conll_data('valid')
CoNLL-2003 操作以下类型的命名实体(相当标准的类别):
- 每个儿子
- 组织机构
- 位置
- 杂项杂项
- 外部(非命名实体)
来自 CoNLL-2003 数据集的观察结果由单词标记化的句子组成,每个单词标记都有命名实体标签。
下面您可以看到一个来自 CoNLL 数据集中的随机句子的例子,它的单词标记与它们各自的命名实体标记([tag])结合在一起。
Germany [B-LOC]
's [O]
representative [O]
to [O]
the [O]
European [B-ORG]
Union [I-ORG]
's [O]
veterinary [O]
committee [O]
Werner [B-PER]
Zwingmann [I-PER]
said [O]
on [O]
Wednesday [O]
...
数据集实现了内-外-始 (IOB)标注方案。
IOB 标记方案意味着,命名实体开头的单词用‘B-’标记,命名实体内部的单词用‘I-’标记。
在上面的示例中,“德国”被标识为一个地点,“欧盟”被标识为一个组织,“Werner Zwingmann”被标识为一个人。
建立模型
第一步,我们为任务指定可用的 NER 标签(不包括特殊的“outside”标签)。
tag_scheme = [
'B-PER',
'I-PER',
'B-ORG',
'I-ORG',
'B-LOC',
'I-LOC',
'B-MISC',
'I-MISC'
]
接下来,我们必须决定,从拥抱脸transformers
的哪个变形金刚,我们要微调。我们将坚持使用无外壳多语言 BERT 变换器(一个流行的选择)。
transformer = 'bert-base-multilingual-uncased'
此外,我们可以选择为网络以及模型训练本身提供一组基本超参数。
# hyperparameters for network
dropout = 0.1
# hyperparameters for training
training_hyperparameters = {
'epochs' : 4,
'warmup_steps' : 500, 'train_batch_size': 13, 'learning_rate': 0.0001
}
将碎片拼在一起
现在,使用NERDA
模型接口将这些部分组合成一个完整的模型配置。
from NERDA.models import NERDAmodel = NERDA(
dataset_training = training,
dataset_validation = validation,
tag_scheme = tag_scheme,
tag_outside = 'O',
transformer = transformer,
dropout = dropout,
hyperparameters = training_hyperparameters
)
在引擎盖下NERDA
实现了一个torch
神经网络,它建立在选择的变压器(在这个例子中是 BERT)上。默认情况下,网络的架构将类似于 Hvingelby et al. 2020 中的一个模型(如果你愿意,你也可以提供自己的网络架构)。
为了训练模型,从而微调 BERT 变换器,剩下要做的就是调用train
方法。
model.train()
注意:这将需要一些时间,取决于您机器的尺寸(如果您想跳过培训,您可以使用NERDA
附带的预烹饪型号)。
这就是它的全部。我们现在已经为 NER 调整了我们自己最先进的基于 BERT 的模型。
让我们看看该模型在独立测试集上的表现(通过 F1 分数)。
>>> test = get_conll_data('test')
>>> model.evaluate_performance(test)
Level F1-Score
B-PER 0.963
I-PER 0.987
B-ORG 0.887
I-ORG 0.866
B-LOC 0.922
I-LOC 0.817
B-MISC 0.823
I-MISC 0.680
AVG_MICRO 0.907
‘AVG _ 微’:跨实体标签的微平均 F1 分数。
如你所见,性能看起来很棒。
我们现在可以使用该模型来识别新文本中的命名实体,即
>>> model.predict_text('Cristiano Ronaldo plays for Juventus FC')
([['Cristian', 'Ronaldo', 'plays', 'for', 'Juventus', 'FC']],
[['B-PER', 'I-PER', 'O', 'O', 'B-ORG', 'I-ORG']])
该模型(正确地)将“克里斯蒂亚诺·罗纳尔多”(足球运动员)识别为一个人,将“尤文图斯俱乐部”(足球俱乐部)识别为一个组织。
微调任何变压器
目前,拥抱脸网站上有 5000 多个变形金刚模型。那么应该微调哪一个呢?我们不想让你失望,但答案是:视情况而定。天下没有免费的午餐。变压器模型都有其不同的优点和缺点。此外,您需要选择符合您的计算预算和对气候变化的关注程度的变压器。
如前所述,伯特通常是一个很好的选择。然而,与 BERT 相比,新的 kid on the block, ELECTRA 更加轻量级,计算效率更高,并且在 NER 任务中仍然表现出色。
无论你选择哪种转换器,NERDA
都会支持它。在上面的代码示例中,为了微调 ELECTRA 变压器,您必须改变的是改变transformer
参数,而不是 BERT,即:
model = NERDA(
...,
**transformer = 'google/electra-small-discriminator',**
...
)
针对任何语言进行微调
有了NERDA
,你还可以针对任何语言对转换器进行微调,例如轻松使用你自己的数据集。为了微调丹麦语中 NER 的转换器,我们可以利用由带有 NER 注释的丹麦语句子组成的 DaNE 数据集。
要实现这一点,您只需更改前面的代码示例:
**from NERDA.datasets import get_dane_data**
model = NERDA(
...,
**dataset_training = get_dane_data('train'),
dataset_validation = get_dane_data('dev'),** ...
)
如果您没有任何(或只是没有足够的)带有所需语言的 NER 注释的训练数据,您可以使用像 doccano 这样的工具来注释新文本。
令人惊讶的是,针对特定语言的微调NERDA
并不像您想象的那样需要那么多带注释的数据,因为NERDA
利用了转换器中已经存在的知识。例如,丹麦 NER 数据集 DaNE 包括不超过 5500 个句子,这足以训练具有合理性能的NERDA
模型。
关于“内尔达”
NERDA
是作为 Ekstra Bladet 在新闻平台智能(PIN)活动的一部分开发的。PIN 是一个由丹麦技术大学、哥本哈根大学和哥本哈根商学院合作进行的工业研究项目,由丹麦创新基金资助。该项目从 2020 年到 2023 年运行,开发面向新闻出版的推荐系统和自然语言处理系统,其中一些是开源的,如NERDA
。
资源
https://github.com/ebanalyse/NERDA https://huggingface.co/ https://pytorch.org/ https://github.com/google-research/bert
https://github.com/google-research/electra
Python 中的简易分组条形图
如何创建每个条目有两个、三个或更多条的条形图
作者图片
我经常想创建一个图,但不记得如何创建,这是一个条形图,其中每个条目或数据帧的每一行都有两个或更多的条形。这样做的目的是比较同一行中的两个不同结果,例如在比较模型的输出或查看不同来源的测量结果有何不同时。虽然有一些例子,但我经常发现自己在无休止地寻找一个简单的实现,或者从各种来源拼凑代码,所以现在我已经能够做到这一点,并记住了代码,我想我会分享我的简单实现,它可以从每个条目两个扩展到三个甚至更多!
为此,我们可以创建一个虚构的数据集,记录五名候选人在一个虚构的学生社团中三年来的投票结果。为了简单起见,我们可以假设三个候选人在三年内没有变化,并且有 172 个成员各自为他们最喜欢的候选人投票。我们可以假设受欢迎程度会随着时间的推移而变化,这样我们就可以使用条形图来检测潜在的趋势。
#create the dictionary of voting returns
data_dict = {"Candidates": ["Jessica", "William", "Hussein", "Peter", "Meridith"],
2012: [10, 27, 56, 31, 48],
2013: [30, 19, 48, 12, 63],
2014: [52, 7, 38, 22, 53]}#convert that into a dataframe
data_df = pd.DataFrame(data_dict)#show the dictionary
data_df
然后,我们可以使用这个数据集,例如在一个简单的分组条形图中比较 2012 年和 2013 年这两年的结果。我们可以简单地为每个候选人显示两个条形,这样我们就可以看到他们的受欢迎程度在两年内是如何变化的:
#use fivethirty eights style of plots
plt.style.use("fivethirtyeight")#create the base axis to add the bars to
fig, ax = plt.subplots(1,1, figsize = (8,6))#extract the labels
label = data_df["Candidates"]
#use this to create x ticks to add the data to
x = np.arange(len(label))#set a width for each bar
width = 0.3#create out first bar
#set it so that x will be the centre of the bars
#so that we can add our labels later
#so set the centre of the first to be 1/2 width away
#to the left
rect1 = ax.bar(x - width/2,
data_df[2012],
width = width,
label = 2012,
edgecolor = "black"
)#create the second bar
#with a centre half a width to the right
rect2 = ax.bar(x + width/2,
data_df[2013],
width = width,
label = 2013,
edgecolor = "black")#add the labels to the axis
ax.set_ylabel("Votes",
fontsize = 20,
labelpad = 20)
ax.set_xlabel("Candidates",
fontsize = 20,
labelpad =20)
ax.set_title("Votes per candidate",
fontsize = 30,
pad = 20)#set the ticks
ax.set_xticks(x)
ax.set_xticklabels(label)#add the legend
#using the labels of the bars
ax.legend(title = "Year of election",
fontsize = 16,
title_fontsize = 20)#adjust the tick paramaters
ax.tick_params(axis = "x",
which = "both",
labelrotation = 90)
ax.tick_params(axis = "y",
which = "both",
labelsize = 15)
在这里我们可以看到,从 2012 年到 2013 年,梅里蒂斯和杰西卡的人气上升,而威廉、侯赛因和彼得的人气下降。
实现的关键部分是为要添加的条形创建一个位置数组。这里,它的长度等于我们的数据集中候选值的数量,并使用x = np.arange(len(data_df["Candidates"])
生成,以确保我们有相同数量的 x 值作为候选值。然后,我们将每组数据的位置调整到 x 刻度的任一侧。在这种情况下,每个条形的中心width/2
远离x
值,因此x
值保持在每个组的中心。这意味着我们可以使用数组将标签添加到绘图中,方法是将 xticks 设置为数组,并将标签添加为刻度标签。
当我们希望每个条目有两个以上的条时,这当然会变得有点复杂,但这种变化并不太难处理。这方面的一个例子是,如果我们想了解人气在三年内的变化情况:
#create the base axis
fig, ax = plt.subplots(1,1, figsize = (8,6))#set the labels
#and the x positions
label = data_df["Candidates"]
x = np.arange(len(label))#set the width of the bars
width = 0.2#create the first bar -1 width
rect1 = ax.bar(x - width,
data_df[2012],
width = width,
label = 2012,
edgecolor = "black")#create the second bar using x
rect2 = ax.bar(x,
data_df[2013],
width = width,
label = 2013,
edgecolor = "black")#create the third bar plus 1 width
rects2 = ax.bar(x + width,
data_df[2014],
width = width,
label = 2014,
edgecolor = "black")#add the labels to the axis
ax.set_ylabel("Votes",
fontsize = 25,
labelpad = 20)
ax.set_xlabel("Candidates",
fontsize = 25,
labelpad =20)
ax.set_title("Votes per candidate",
fontsize = 30,
pad = 20)#set the ticks
#using the labels
ax.set_xticks(x)
ax.set_xticklabels(label)#add the legend
ax.legend(title = "Year of election",
fontsize = 16,
title_fontsize = 20,
bbox_to_anchor = (1.02, 0.7))#adjust the tick paramaters
ax.tick_params(axis = "x",
which = "both",
labelrotation = 90,
labelsize = 20)
ax.tick_params(axis = "y",
which = "both",
labelsize = 20)
这里的关键是第一个条的中心距离组的中心有一个width
。这是因为中间的条将具有width = 0.2
,因此第二条的中心需要至少width/2 + width/2
远离x
值。我们可以将宽度增加到0.3
,它仍然是相同的计算,这种方式使得实现起来很简单,没有什么复杂的记忆!
如果您想在条形图中添加更多的条形,例如四个、五个甚至更多,那么您需要做的就是调整条形的宽度和每个结果的位置,以便 x 值在条形组的中心对齐。例如,如果您希望每个条目有四个条,那么您可以将x
作为中心,第一个条远离-1.5 * width
,第二个条远离-0.5 * width
,第三个条远离0.5 * width
,第四个条远离1.5 * width
,只要它们具有相同的宽度。这将意味着标签将集中在x
上,因此它们将位于每个组的中心。
好了,现在你可以用一种简洁的方式创建简单的分组条形图了,当我将来想再次使用它时,我不会忘记我把代码放在哪里了!
如果你喜欢你所读的,并且想在 medium 上看到更多其他作者的作品,请随意使用我下面的参考代码注册:
https://philip-wilkinson.medium.com/membership
或者考虑看看我的其他文章:
[## scikit-learn 决策树分类器简介
towardsdatascience.com](/introduction-to-decision-tree-classifiers-from-scikit-learn-32cd5d23f4d)
通过链接内核笔记本,轻松实现 Kaggle 离线提交
插图照片由 Pexels 的 Lucas Fonseca 拍摄
使用 Kaggle 内核作为数据存储的中间步骤,为限制互联网访问的竞赛提供一个简单的指南。
你是新来卡格尔的吗?你是否发现这是一个对数据科学家或机器学*工程师来说非常迷人的世界,但随后你又很难提交。那么下面这个指南就适合你!我将向你展示如何让你的经历更流畅,可复制,并扩展你的职业生活经历。
快速卡格尔游戏攻略
Kaggle 是一个广为人知的数据科学平台,它将机器学*(ML)爱好者与公司和组织联系起来,这些公司和组织提出了一些具有挑战性的主题,这些主题需要解决,并带有一些成功解决方案的财务动机。作为一名 ML 工程师,你可以从已经准备好的(大部分是高质量的)数据集中获益。此外,Kaggle 还提供讨论和分享&,将他们的工作呈现为 Jupyter 笔记本(称为内核)和定制数据集。
最后,如果你参加了一个比赛,想要在排行榜上排名,你需要提交你的解决方案。有两种类型的比赛,他们要求用户(a)以给定的提交格式上传生成的预测,或者(b)准备一个以提交格式生成预测的内核。第一种情况相对简单且容易做到,所以我们将只关注内核提交。
这些竞赛只包括一个最小的测试数据集,用于用户验证他的预测管道。由于数据泄露和其他安全原因,运行中的内核不允许使用互联网(因为用户可以将所有测试数据上传到他们的存储器,然后覆盖数据集)。另一方面,如果您的内核需要安装额外的依赖项或下载最新的训练模型,这使得提交不容易。
数据集的肮脏方式
直观的第一手解决方案是使用所需的额外包和训练有素的模型来创建存储。为此,您可以使用/创建一个 Kaggle 数据集,并将所有数据上传到那里。我怀疑,即使你能看到它是为了不同的目的捆绑一个工具。
我们在之前的文章中已经描述了这个过程。简而言之,创建这样一个数据集的过程是:( 1)本地下载所有的依赖项,( 2)重命名所有以.tar.gz
结尾的源包,因为 Kaggle 执行所有归档文件到文件夹的递归提取,以及(3)上传包到新的数据集。然后在推理内核上,(4)重新命名源包,(5)安装所有带有来自 Kaggle 数据集的 pip 的包。
你感觉需要多少步骤。即使你完成了这一切,你也可能面临一些兼容性问题,因为为你的本地环境下载的包可能与你后来的 Kaggle 内核环境不兼容。而且还有几个角力案例。
链接内核以构建一个在另一个之上
让我们想一想,一个好的数据科学家工作流程应该是怎样的。三个主要步骤是(1)探索性数据分析,(2)建模和解决问题,以及(3)推理(和预测提交)。在这个工作流程的开始,我们有一个输入数据集,最后,我们希望成为竞争提交。
Kaggle 上的 Datascinetist 工作流程示意图。
我们可以在一个笔记本中包含所有三个步骤(这会使它很重,通常用户不需要运行所有步骤),或者用一个内核覆盖每个步骤。连续笔记本中分离的研究阶段允许我们更快地迭代,并且部分地保存中间结果。例如,您将只进行一次 EDA,然后您将迭代训练模型(扩展管道或执行超参数搜索),当您获得新的最佳模型并运行您的推理进行提交时。
好消息是 Kaggle 内核保留了一些(有限的)运行输出。在我们提出的解决方案中,我们将依赖这一特性。简而言之,我们可以保存我们的模型,并下载训练内核(它有一个互联网连接)中所有需要的包,然后将它们加载到下面的推理内核中,它是从互联网上截取的。
插图由安德里亚·皮亚卡迪奥从派克斯拍摄
让我们在两个 Kaggle 内核上演示一下毒性评级。为了简单起见,我们将琐碎的 EDA 和模型训练合并在一个笔记本中。所以第一个是🙊有毒评论与 Lightning⚡Flash 和第二个是🙊有毒的评论同 Lightning⚡Flash【推论】。
下载所需的包
包下载与使用 PyPI 注册表安装非常相似。此外,我们用dest
参数为保存的 python 轮子指定一个文件夹。
*pip download lightning-flash[text] \
--dest frozen_packages \
--prefer-binary*
高级用户可能需要安装他们的软件包或在他们的标准软件包的分支中做一些修补程序。这样的包没有在 PyPI 上注册,但是它是setuptools
完整的。对于这种情况,您仍然可以使用pip
,并告诉它从给定的源创建一个轮子包。
pip wheel 'https://github.com/PyTorchLightning/lightning-flash/archive/refs/heads/fix/serialize_tokenizer.zip' \
--wheel-dir frozen_packages
如果您仍然需要安装额外的软件包,您可以将它们添加到 URL #egg=lightning-flash[text]
的末尾。此外,您还可以从基础环境中的现有包(如 PyTorch)中删除下载的轮子。
rm frozen_packages/torch-*
链接内核作为数据源
第二部分是添加前一个内核作为下一个内核的数据源。我们打开推理内核右上角的配置,并选择“添加数据”选项。
添加了以前的内核作为数据源的截图。
然后我们需要找到我们的内核。我们可以按名称搜索或浏览我们过去的工作,如下图所示:
浏览过去内核的截图。
我们快完成了!我们通过从“本地”路径安装所需的包,用连接过去的内核输出来初始化环境。我们使用下面的命令禁用索引,因为我们已经离线了,并且将路径设置为包含所有包的文件夹。
pip install lightning-flash[text] \
--find-links /kaggle/input/toxic-comments-with-lightning-flash/frozen_packages \
--no-index
使用附加数据和检查点
我们仍然可能需要一些额外的在线资源。如果数据是公开的,我们可以下载它们,例如,发布的检查点:
mkdir checkpoints
wget [https://github.com/unitaryai/detoxify/releases/download/v0.1-alpha/toxic_original-c1212f89.ckpt](https://github.com/unitaryai/detoxify/releases/download/v0.1-alpha/toxic_original-c1212f89.ckpt) --directory-prefix=checkpoints
由于需要大量的模型训练和有限的免费 Kaggle 计算资源,您可能仍然需要为您提交的模型创建一个特定的数据集。
摘要
我简要介绍了 Kaggle 平台,以及它如何将好奇的研究人员和数据科学(DS)爱好者与呼叫实际问题联系起来。我分享了如何应用最佳的 DS 实践——将 EDA、模型训练和模型推理阶段拆分到特定的笔记本中。此外,我已经展示了这个分区对于 Kaggle 离线提交非常有用。
敬请关注,关注我了解更多!
关于作者
吉尔卡·博罗维奇 拥有 CTU 的计算机视觉博士学位。他已经在几家 IT 创业公司和公司从事机器学*和数据科学工作几年了。他喜欢探索有趣的世界问题,用最先进的技术解决这些问题,并开发开源项目。
具有解析解的简单逻辑回归
Python 中从头开始的二进制分类
关于分类的 logistic 回归,不乏文章、视频、教程。这是机器学*中的一个经典主题,通常是进入更复杂算法之前的垫脚石。本文旨在从另一个角度向您展示逻辑回归,我们可以求解权重的公式化解决方案,并将其传递给返回预测概率的模型。我在文章中提供了代码和解决方案的链接。
逻辑回归旨在解决的问题是找到属于某一类的一组特征的观察值的概率。通过训练关于我们看到的观察实际上属于某些类(这将是标签,或目标变量)的例子,我们的模型将对新观察的类有一个好的想法。确切地说如何训练模型并不总是被讨论,但是我想讨论一个简单的规则可以决定这些概率的例子。
你敢提出贝叶斯定理吗
没有你我们做不到:
记住,一个分类问题可以认为是一个条件概率:给定一组特征,某个类标签的概率,。
作为本文的开始,您只需要知道随机变量的条件概率,在本例中,Y,取给定 X 的某个值,采用以下形式:
分母是“全概率法则”的结果,这是概率论中的一个有用工具
乙状结肠功能
您应该已经知道,返回 x 值向量落入某一类的预测概率的函数称为 sigmoid 函数。该函数的便利之处在于,对于所有的实数值,它的范围都在 0 和 1 之间,所以无论你输入什么(无误差),你都会得到一个预测的概率。
这里有趣的是,在将上面的贝叶斯定理方程除以 P(X|Y=1)*P(Y=1)之后,我们得到了一个类似于 sigmoid 函数的形式,并且我们可以求解出在下面的指数中应该是什么:
更有趣的是,权重和偏差如何等于 Y 为 0 和 1 的概率的组合。所以,就好像如果我们知道如何用我们的数据集来表示这些概率,我们就可以找到权重的解决方案!
等协方差
像许多机器学*问题的基本解决方案一样,对数据进行假设。例如,观察值的独立性和数据的*似正态分布是线性回归中使用的假设。
根据我的经验,现实世界的数据很少满足所有的基本假设,以便使用教科书或开箱即用的解决方案。以至于我经常需要回去查阅基本假设作为提醒。
也就是说,本文中关于解决方案的一个基本假设是,两个二元正态分布具有相同的协方差矩阵。他们的传播是相同的数量,在相同的方向。如果它们不相等,我们提出的解决方案会有一些后果。
逻辑上无法解释的跳跃
有一些代数和矩阵操作来得到这一点,但我在这里包括一个完整的推导链接,任何人都可以看到它。
现在看起来很乱,但是会好起来的。请注意自然对数中的第一项,这是二元正态分布的概率密度函数。我在这里发现有趣的是(x1,x2)集合所属的 a 类,直观上是给定(x1,x2)集合的两个概率密度函数的差。这完全说得通,对吧?
我们仍然在从这些二元正态分布中寻找一些东西,可以告诉我们,对于给定的一对(x1,x2),区分这两个类别的权重应该是多少。所以,我们需要乘以 x 向量的东西。所有步骤见链接,但最后有一个分析解决方案说:从数据和两个类别的分布来看,这种偏差和这些权重在分离类别方面是最好的。
最棒的是,你可以看到一个例子,对于简单的分类问题,经验数据可以用来解决一个问题,而不需要复杂的算法。更好的是,使用 Python 我们可以可视化二进制分类问题的解决方案来说明这个概念。
用 Python 阐明观点
如果我们很好地将我们的数据可视化,我们可以看到这两个类别之间存在某种界限。它没有被很好地定义,但是边界可以用 x1 和 x2 来定义,这是我们正在使用的特征。这意味着,基于 R2 中的 任意 x1 和 x2 的值,我们可以找到该组合应该在哪个类中。
两个变量都是正常的,所以想象一个“峰值”在每个聚类的中心向你走来(在 z 轴上)
通常,我们会使用类似 sci-kit learn 的东西来实例化一个逻辑回归对象,使其适合数据,并使用适合的模型对新的观察结果进行预测。我的意思是,通常数据具有不等的协方差,所以为什么不从处理它的算法中受益呢?在这里,为了说明的目的,我们定义一个简单得多的问题。
下面您可以看到,我们可以使用解析解和我们所知道的每个聚类分布来找到权重。
现在我们有了权重,我们将制作一个通常在拟合方法中使用的设计矩阵,通过将特征和权重的点积传递给 sigmoid 函数来对每个观察值进行预测,并在一个表中返回所有内容(一个 pandas DataFrame)。
在返回设计矩阵中每个(x1,x2)组合的预测概率后,绘制它们向我们显示,对于更接*两类之间边界的组合,概率更接* 0.5。具有接* 0 的预测概率将意味着(x1,x2)肯定在“负”的聚类中,类似地,对于接* 1 的概率和“正”的聚类也是如此。
好吧,我希望你读这个的时候有一些乐趣。如果你有兴趣看完整的代码,可以在这里找到。在那里,你可以看到绘制区别线的公式,以及推导细节的链接。
使用 PyCaret + MLflow 轻松实现 MLOps
一个初学者友好的,一步一步的教程,使用 PyCaret 在你的机器学*实验中集成 MLOps
阿迪·戈尔茨坦在 Unsplash 上的照片
PyCaret
PyCaret 是一个开源的低代码机器学*库和端到端的模型管理工具,内置于 Python 中,用于自动化机器学*工作流。它以其易用性、简单性以及快速高效地构建和部署端到端 ML 原型的能力而闻名。
PyCaret 是一个替代的低代码库,可以用几行代码代替数百行代码。这使得实验周期成倍地快速和有效。
py caret——Python 中的开源、低代码机器学*库
要了解更多关于 PyCaret 的信息,你可以查看他们的 GitHub 。
MLflow
MLflow 是一个管理 ML 生命周期的开源平台,包括实验、可复制性、部署和中央模型注册。MLflow 目前提供四个组件:
MLflow 是一个管理 ML 生命周期的开源平台
要了解更多关于 MLflow 的信息,可以查看 GitHub 。
正在安装 PyCaret
安装 PyCaret 非常容易,只需要几分钟。我们强烈建议使用虚拟环境来避免与其他库的潜在冲突。
PyCaret 的默认安装是 pycaret 的精简版本,只安装这里列出的硬依赖项。
**# install slim version (default)** pip install pycaret**# install the full version**
pip install pycaret[full]
当您安装 pycaret 的完整版本时,这里列出的所有可选依赖项也会被安装。MLflow 是 PyCaret 依赖项的一部分,因此不需要单独安装。
👉我们开始吧
在我谈论 MLOps 之前,让我们从较高的层面来谈一谈机器学*生命周期:
机器学*生命周期-按作者分类的图片(从左至右阅读)
- 业务问题— 这是机器学*工作流程的第一步。这可能需要几天到几周的时间来完成,这取决于用例以及问题的复杂性。正是在这个阶段,数据科学家与主题专家(SME)会面,以了解问题,采访关键利益相关者,收集信息,并设定项目的总体预期。
- 数据来源& ETL — 一旦理解了问题,接下来就要使用访谈中获得的信息从企业数据库中获取数据。
- 探索性数据分析(EDA)——建模还没开始。EDA 是分析原始数据的地方。您的目标是探索数据并评估数据质量、缺失值、特征分布、相关性等。
- 数据准备— 现在该准备数据模型培训了。这包括将数据分成训练集和测试集、输入缺失值、一键编码、目标编码、特征工程、特征选择等。
- 模特培训&选拔— 这是每个人都为之兴奋的一步。这包括训练一系列模型、调整超参数、模型集合、评估性能指标、模型分析(如 AUC、混淆矩阵、残差等),以及最终选择一个最佳模型部署到生产中以供业务使用。
- 部署&监控— 这是最后一步,主要是关于 MLOps。这包括打包您的最终模型、创建 docker 映像、编写评分脚本,然后让它们一起工作,最后将其发布为 API,可用于获得对通过管道传输的新数据的预测。
旧的方法非常麻烦、冗长,并且需要大量的技术知识,我可能无法在一个教程中涵盖它。然而,在本教程中,我将使用 PyCaret 来演示对于一个数据科学家来说,非常高效地完成所有这些工作是多么容易。在我们进入实际部分之前,让我们再多谈一点关于 MLOps 的内容。
👉什么是 MLOps?
MLOps 是一门工程学科,旨在结合机器学*开发,即实验(模型训练、超参数调整、模型集成、模型选择等)。),通常由 ML 工程和运营部门的数据科学家执行,以便标准化和简化生产中机器学*模型的持续交付。
如果你是一个绝对的初学者,你可能不知道我在说什么。别担心。让我给你一个简单的、非技术性的定义:
mlop 是一系列技术工程和运营任务,允许您的机器学*模型被组织中的其他用户和应用程序使用。基本上,这是一种方式,通过这种方式,你的工作,即机器学*模型被在线发布,因此其他人可以使用它们并满足一些商业目标。
这是一个非常低调的定义。实际上,它涉及的工作和利益比这多一点,但如果你是新手,这是一个好的开始。
现在让我们按照上图所示的工作流程做一个实际的演示,确保你已经安装了 pycaret。
👉商业问题
在本教程中,我将使用达顿商学院发表在哈佛商业上的一个非常受欢迎的案例研究。这个案子是关于两个未来将要结婚的人的故事。名叫格雷格的家伙想买一枚戒指向名叫莎拉的女孩求婚。问题是找到莎拉会喜欢的戒指,但在他的密友建议后,格雷格决定买一颗钻石,这样莎拉就可以决定她的选择。然后,格雷格收集了 6000 颗钻石的价格和切割、颜色、形状等属性数据。
👉数据
在本教程中,我将使用一个数据集,该数据集来自达顿商学院的一个非常受欢迎的案例研究,发表在哈佛商业上。本教程的目标是根据钻石的重量、切割、颜色等属性来预测钻石的价格。您可以从 PyCaret 的存储库下载数据集。
**# load the dataset from pycaret** from pycaret.datasets import get_data
data = get_data('diamond')
数据中的样本行
👉探索性数据分析
让我们做一些快速可视化来评估独立特征(重量、切割、颜色、净度等)之间的关系。)与目标变量即Price
**# plot scatter carat_weight and Price**
import plotly.express as px
fig = px.scatter(x=data['Carat Weight'], y=data['Price'],
facet_col = data['Cut'], opacity = 0.25, template = 'plotly_dark', trendline='ols',
trendline_color_override = 'red', title = 'SARAH GETS A DIAMOND - A CASE STUDY')
fig.show()
莎拉得到了一份钻石案例研究
让我们检查目标变量的分布。
**# plot histogram**
fig = px.histogram(data, x=["Price"], template = 'plotly_dark', title = 'Histogram of Price')
fig.show()
请注意,Price
的分布是右偏的,我们可以快速查看对数变换是否可以使Price
接*正态,从而给假设正态的算法一个机会。
import numpy as np**# create a copy of data**
data_copy = data.copy()**# create a new feature Log_Price**
data_copy['Log_Price'] = np.log(data['Price'])**# plot histogram**
fig = px.histogram(data_copy, x=["Log_Price"], title = 'Histgram of Log Price', template = 'plotly_dark')
fig.show()
这证实了我们的假设。这种转换将帮助我们摆脱偏态,使目标变量接*正态。基于此,我们将在训练我们的模型之前转换Price
变量。
👉数据准备
对于 PyCaret 中的所有模块来说,setup
是任何使用 PyCaret 的机器学*实验中的第一步,也是唯一一步。该功能负责训练模型之前所需的所有数据准备。除了执行一些基本的默认处理任务,PyCaret 还提供了一系列预处理功能。要了解 PyCaret 中所有预处理功能的更多信息,您可以查看这个链接。
**# initialize setup**
from pycaret.regression import *
s = setup(data, target = 'Price', transform_target = True, log_experiment = True, experiment_name = 'diamond')
pycaret.regression 模块中的设置函数
当您在 PyCaret 中初始化setup
函数时,它会分析数据集并推断所有输入要素的数据类型。如果所有数据类型都推断正确,您可以按 enter 键继续。
请注意:
- 我已经通过了
log_experiment = True
和experiment_name = 'diamond'
,这将告诉 PyCaret 在建模阶段自动记录所有的度量、超参数和模型工件。由于与 MLflow 的集成,这是可能的。 - 还有,我在
setup
里面用过transform_target = True
。PyCaret 将使用 box-cox 变换在后台转换Price
变量。它以类似于日志转换(技术上不同)的方式影响数据的分布。如果你想了解更多关于 box-cox 变换的知识,你可以参考这个链接。
设置的输出—为显示而截断
👉模型训练和选择
现在数据已经准备好进行建模,让我们使用compare_models
函数开始训练过程。它将训练模型库中所有可用的算法,并使用 k-fold 交叉验证评估多个性能指标。
**# compare all models**
best = compare_models()
compare_models 的输出
**# check the residuals of trained model**
plot_model(best, plot = 'residuals_interactive')
最佳模型的残差和 QQ 图
**# check feature importance**
plot_model(best, plot = 'feature')
完成并保存管道
现在让我们最终确定最佳模型,即在包括测试集在内的整个数据集上训练最佳模型,然后将管道保存为 pickle 文件。
**# finalize the model**
final_best = finalize_model(best)**# save model to disk** save_model(final_best, 'diamond-pipeline')
save_model
函数将把整个管道(包括模型)保存为本地磁盘上的 pickle 文件。默认情况下,它会将文件保存在笔记本或脚本所在的文件夹中,但是如果您愿意,也可以传递完整的路径:
save_model(final_best, 'c:/users/moez/models/diamond-pipeline'
👉部署
记得我们在设置函数中通过了log_experiment = True
和experiment_name = 'diamond'
。让我们看看 PyCaret 在 MLflow 的帮助下在幕后实现了什么。让我们启动 MLflow 服务器来看看这个神奇的过程:
**# within notebook (notice ! sign infront)** !mlflow ui**# on command line in the same folder** mlflow ui
现在打开浏览器,输入“localhost:5000”。它将打开这样一个用户界面:
上表中的每个条目代表一个训练运行,该运行产生一个训练管道和一堆元数据,例如运行的日期时间、性能度量、模型超参数、标签等。让我们点击其中一个模型:
第一部分— CatBoost 回归器
第二部分— CatBoost 回归器(续)
第二部分— CatBoost 回归器(续)
注意,您有一个logged_model
的地址路径。这是带有 Catboost 回归器的训练管道。您可以使用load_model
函数读取这个管道。
**# load model**
from pycaret.regression import load_model
pipeline = load_model('C:/Users/moezs/mlruns/1/b8c10d259b294b28a3e233a9d2c209c0/artifacts/model/model')**# print pipeline** print(pipeline)
打印输出(管道)
现在让我们使用这个管道来生成对新数据的预测
**# create a copy of data and drop Price** data2 = data.copy()
data2.drop('Price', axis=1, inplace=True)**# generate predictions** from pycaret.regression import predict_model
predictions = predict_model(pipeline, data=data2)
predictions.head()
从管道生成的预测
呜哇!我们现在从训练有素的管道中得到推论。恭喜,如果这是你的第一次。请注意,所有转换,如目标转换、一键编码、缺失值插补等。自动在幕后发生。你得到一个实际规模的预测数据框架,这就是你所关心的。
即将推出!
我今天展示的是在 MLflow 的帮助下,您可以在生产中使用 PyCaret 提供的经过培训的管道的许多方法之一。在下一篇教程中,我计划展示如何使用 MLflow 原生服务功能来注册您的模型,对它们进行版本化并作为 API 使用。
使用 Python 中的这个轻量级工作流自动化库,您可以实现的目标是无限的。如果你觉得这很有用,请不要忘记给我们 GitHub 库上的⭐️。
想了解更多关于 PyCaret 的信息,请在 LinkedIn 和 Youtube 上关注我们。
加入我们的休闲频道。此处邀请链接。
您可能还对以下内容感兴趣:
使用 PyCaret 2.0
在 Power BI 中构建您自己的 AutoML 使用 Docker
在 Azure 上部署机器学*管道在 Google Kubernetes 引擎上部署机器学*管道
在 AWS Fargate 上部署机器学*管道
构建和部署您的第一个机器学* web 应用
使用 AWS Fargate 无服务器
构建和部署机器
重要链接
文档
博客
GitHub
stack overflow
安装 PyCaret 笔记本教程 投稿于 PyCaret
想了解某个特定模块?
单击下面的链接查看文档和工作示例。
海泡石时序 ETL
业内笔记
数据工程不需要这么复杂
通过 noaa 插件同步海泡石中的天气数据。(图片由作者提供)
我是 Bennett Meares,海泡石的作者。我构建并开源了海泡石以使数据工程更容易理解,今年我完成了关于在现实世界中使用海泡石的同步策略的硕士论文。
如果海泡石对你的项目有帮助,或者如果你有问题,请在项目讨论版上告诉我!
似乎每天都有新的 ETL / ELT 框架向世界承诺。事实是,数据工程兔子洞跑深。对于数据科学家和 BI 分析师来说,花在粗制滥造的脚本或学*庞大框架上的时间比花在实际分析上的时间要少。通常,真正需要做的只是更新表格、获取pandas
数据帧和构建仪表板。
其他 ETL 系统
有一些 ETL 管理系统,例如 AWS Glue 、 Apache Airflow 和 dbt ,每个系统都有自己对 ETL 过程的观点。例如,Airflow 让数据工程师将他们的脚本安排为有向无环图(Dag),其行为有点像具有更多上下文的crontab
。Dbt 倾向于 ETL 的转换方面,并为管理许多 SQL 模型提供 Jinja 风格的模板。AWS Glue 充当亚马逊各种数据存储选项(RDS、红移、S3 等)之间更全面的数据集成服务。).
这些工具对于大规模工作负载来说很有前途,但是在许多项目中,它们可能有点大材小用。在许多情况下,这些行业工具所涉及的设置和模板会导致笨拙的用户体验。您需要使您的 ETL 工作符合您选择的框架,并且需要大量的背景知识来有效地使用这些工具。
这就是海泡石的用武之地――它是一个轻量级的时间序列 ETL 管理系统,构建时考虑到了生活质量。
你仍然可以在更大的框架中使用海泡石作为组件。海泡石给你工具来建造你喜欢的建筑。
介绍海泡石
对于数据科学家来说,海泡石是一个轻量级的通用 ETL 框架。对于大多数项目来说,它在一个预配置堆栈(包括一个时间序列数据库和 Grafana)中带有合理的默认值,这样你就可以快速开始同步和分析数据。
海泡石项目的主页。(图片由作者提供)
我如何开始使用海泡石?
入门指南更全面地介绍了基础知识,但是 TL;DR 从 PyPI 安装并运行mrsm
命令(或者python -m meerschaum
如果mrsm
不在你的PATH
中)。
pip install --user meerschaum
mrsm stack up -d db grafana
mrsm
这将使你进入mrsm
外壳,在那里你可以开始构建你的管道。help
命令可以洞察可用的操作(例如help show pipes
),自动完成建议将在运行命令时指导您。
等等,什么是管道?
海泡石的核心概念是管道,它基本上是数据库上的一个表,带有一些关于如何更新它的元数据。例如,因为海泡石关注时间序列,所以所有管道都需要主datetime
列的名称。
通过管道,您可以接入数据流进行分析:
>>> from meerschaum import Pipe
>>> pipe = Pipe('plugin:noaa', 'weather')
>>> df = pipe.get_data(
... begin = '2021-10-01',
... end = '2021-11-01',
... params = {'station': ['KATL', 'KCEU']},
... )
>>>
没有时序数据流可以用海泡石吗?
当然,您不需要符合包含的时间序列模型。例如,您可以使用连接器作为创建和组织数据库连接的便捷方式:
数据如何同步到管道中?
ETL 过程发生在sync
命令中。对于sql
管道,同步包括执行一个生成的 SQL 查询来获取、过滤和插入最新的数据(我在我的硕士论文中探索了各种策略)。
使用“同步管道”命令同步管道。(图片由作者提供)
对于plugin
管道,begin
和end
日期时间界限被传递给fetch()
函数,结果数据帧被过滤和插入。这里有更多关于写一个获取插件的信息,但是下面是一个基本的例子:
闲聊够了,让我们让一些数据流动起来!
在我们深入细节之前,让我们同步一些数据。入门指南对事情的解释比我在这里所能解决的更透彻,但是如果你想了解这个过程是如何工作的,运行下面的命令让数据流动起来(如果你在交互式 shell 中,你可以省略mrsm
):
### Start the database.mrsm stack up -d db grafana ### Install the NOAA plugin.mrsm install plugin noaa ### Register the pipe (or run `bootstrap` for a wizard).
### The -c stands for 'connector' and -m for 'metric'.mrsm register pipe -c plugin:noaa -m weather ### The 'noaa' plugin will ask you for a station. Enter 'KCLT'.
### It also handles the datetime column name.mrsm sync pipes ### Demonstrate that we've successfully synced our data.mrsm show data
访问 http://localhost:3000 在 Grafana 中查看您的数据(默认用户名和密码为admin
和admin
)。
Grafana 预先配置在海泡石堆栈中。(图片由作者提供)
mrsm
shell 附带有用的命令,比如show
动作(例如show pipes
、show rowcounts
、show columns
等)。).
海泡石外壳在键入命令时提供建议。(图片由作者提供)
对于数据科学家来说,另一个有用的动作是sql
命令。Meerschaum sql
连接器与来自 dbcli 项目的工具集成在一起,所以你可以直接进入你的数据库,而不需要启动整个 IDE。
海泡石与 dbcli 集成在一起,可以让你直接进入你的数据库。(图片由作者提供)
建筑管道
如果您想从关系数据库中提取数据,用bootstrap connector
添加连接,然后用bootstrap pipe
构建管道。bootstrap
命令启动一个向导来引导你添加连接器和管道。
当使用sql
连接器引导管道时,向导将提示您输入 SQL 定义,就好像管道是一个视图一样(您可以稍后使用edit pipe definition
编辑定义)。当获取新数据时,该定义稍后用于更大的查询中,以绑定到日期时间轴上。
同步管道
如上所述,sync pipes
命令获取、过滤和插入新数据到管道中。各种标志会影响行为或同步过程,例如:
--loop
标志持续同步管道,在两圈之间休眠 1 秒。--min-seconds
标志改变两圈之间的秒数。-d
或--daemon
标志将命令作为后台作业运行,并可应用于任何海泡石命令。- 标准的
-c
、-m
和-l
管道过滤器标志指定哪些管道可以被同步。每个标志接受多个值。
例如,以下命令将持续同步带有连接器键plugin:noaa
的管道,并在批处理之间等待一个小时,所有这些都在后台作业中完成。
sync pipes -c plugin:noaa --loop --min-seconds 3600 -d
使用-d 标志生成一个后台作业。(图片由作者提供)
可以使用“显示日志”查看作业的输出。(图片由作者提供)
在你的项目中使用海泡石
以下是海泡石可以帮助你完成项目的例子。你可以遵循框架的模型,但是海泡石给了你所需的灵活性,让你可以按照自己喜欢的方式来构建你的架构。
插件
你可以通过插件将你的脚本包含在你的海泡石实例中,这些插件是简单的 Python 脚本。插件可以获取数据,定义定制的sync
行为,扩展内置的 FastAPI 应用,以及添加或覆盖动作和标志。
为了分享你的插件,你可以上传到默认的公共海泡石库或者你自己的私有海泡石 API 实例-r
。
register plugin myplugin -r api:myinstance
在我今年早些时候写的一篇文章中,我展示了一个我写的从 Apex 清算 API 获取财务数据的海泡石插件。
Web API
你可以连接到你的海泡石实例,通过运行 Web API 来保护防火墙后面的数据库。
start api -d
在另一台机器上,将您的 API 添加为连接器,并使用它来访问您的实例(在 shell 中使用instance
命令或在命令上使用-i
标志)。web 仪表板可用于通过浏览器控制海泡石。
web dashboard 可用于随时随地控制海泡石。(图片由作者提供)
该 API 允许你与团队协作,托管私有插件并允许独立的 Meerschaum 用户登录。注意,到目前为止,用户共享数据库,所以用户系统是为每个特定实例的较小团队设计的。也就是说,插件归用户所有,管道由团队共享。
一个完整的建筑可能包括许多海泡石实例。
Shell 命令
你可以在 shell 脚本中使用海泡石,或者在 crontab 中使用mrsm
或python -m meerschaum
。请注意,您需要通过-y
或-f
,这样您的脚本就不会被交互式问题卡住。
### Sync pipes once per hour.
0 * * * * python -m meerschaum sync pipes### Start jobs on reboot
@reboot python -m meerschaum start jobs -y
后台作业
对于持续的操作(比如运行 API),海泡石后台作业是一种管理流程的简单方法。添加-d
标志来告诉 Meerschaum 运行您指定为后台作业的命令。除非您指定了--name
标志,否则作业会被随机命名。
mrsm start api -d --name api_job
您可以使用start
、stop
和delete
命令管理您的作业。命令show logs
将显示正在运行的后台作业的输出(如docker-compose
)。
Python 包
Python 包可以很容易地导入到您现有的脚本中。完整的软件包文档更详细,尽管下面是几个有用的例子。
一个Pipe
对象可以让你直接访问你的数据流。sync()
方法允许我们向管道中添加新数据(过滤掉重复的数据),而get_data()
方法允许我们根据特定的参数轻松地检索历史数据。这里是关于Pipe
对象所能做的事情的文档。
新管道很容易注册和同步。
get_pipes()
函数允许您检索一个实例上所有注册的管道。
管道的键结构允许更容易的分组和管理。
结论
这篇文章展示了几种可以开始同步时间序列数据的方法,尽管海泡石还可以提供更多。记住,海泡石的主要焦点是生活质量,这样你就可以回到数据分析。对于没有时间研究数据工程的数据科学家来说:海泡石可能适合你!
那么,你还在等什么?试试看!尝试使用内置的 SQLite 实例sql:local
并同步一些数据。
我创建海泡石是为了让每个人都能接触到数据工程,所以如果你需要帮助、有想法或者想展示你的成果,我可以在 GitHub 上的项目讨论页面找到你!
创建算法链的简单方法——带网格搜索、列转换器、特征选择的管道
使用 python 实现设计编译流程的管道
***Table of Contents*****1\. Introduction
2\. Pipeline
3\. Pipeline with Grid Search
4\. Pipeline with ColumnTransformer, GridSearchCV
5\. Pipeline with Feature Selection**
帕特里克·亨德利在 Unsplash 上的照片
1.介绍
为算法准备数据集、设计模型和调整算法的超参数(由开发人员自行决定)以概化模型并达到最佳精度值在之前的文章中已有提及。正如我们所知,对于模型预处理、数据预处理和调整算法的超参数,开发人员都有可供选择的解决方案。开发人员负责应用最合适的组合,并保持他的项目在准确性和通用性方面的最优化。本文包括使用 sklearn 提供的管道一次性实现所有这些提到的操作以及更多内容。python 实现支持所有的头。
2.管道
在其最基本的形式中,管道是用一行代码将指定的数据预处理操作和模型实现到数据集:
IN[1]
iris=load_iris()
iris_data =iris.data
iris_target=iris.targetIN[2]
x_train,x_test,y_train,y_test = train_test_split(iris_data, iris_target,test_size=0.2, random_state=2021)
pip_iris = Pipeline([("scaler", RobustScaler()),("lr",LogisticRegression())])
pip_iris.fit(x_train,y_train)
iris_score=pip_iris.score(x_test,y_test)
print(iris_score) **OUT[2]
0.9333333333333333**
照常使用train_test_split
分离虹膜数据集,并选择RobustScaler()
作为已知为数值数据集的数据集的定标器方法,选择 LogisticRegression 作为分类器。管道也包含各种属性,如.fit
、.score
,就像网格搜索一样。训练数据集在创建的管道中用.fit
命令拟合,分数用.score
创建。
3.带网格搜索的管道
网格搜索评估算法中的超参数组合或任何具有已定义超参数的操作,通知用户具有各种属性的准确率或最佳超参数组合(更多信息点击此处)。将 GridSearchCV 与管道结合使用是消除工作量和混乱的一种非常有效的方式。现在,让我们用各种超参数组合来测试我们在上面实现的逻辑回归算法:
IN[3]
x_train,x_test,y_train,y_test = train_test_split(iris_data, iris_target,test_size=0.2, random_state=2021)
pip_iris_gs = Pipeline([("scaler", RobustScaler()),("lr",LogisticRegression(solver='saga'))])param_grids={'lr__C':[0.001,0.1,2,10],
'lr__penalty':['l1','l2']}gs=GridSearchCV(pip_iris_gs,param_grids)gs.fit(x_train,y_train)
test_score = gs.score(x_test,y_test)
print("test score:",test_score)
print("best parameters: ",gs.best_params_)
print("best score: ", gs.best_score_)
**OUT[3]
test score: 0.9333333333333333
best parameters: {'lr__C': 2, 'lr__penalty': 'l1'}
best score: 0.9583333333333334**
除此之外,‘C’和‘penalty’值由用户通过创建字典定义为param_grids
。后来,包含算法和缩放的管道作为估计器被添加到 GridSearchCV 中。训练数据集用.fit
命令训练,用.score
评估。此外,还获得了关于借助 GridSearchCV 中的各种属性创建的模型的信息。
4.带 ColumnTransformer 的管道
到目前为止,只使用了只包含数字数据的数据集 iris 数据集。为了使情况更复杂,让我们使用玩具数据集,它包含数字和分类数据,并应用:
- 用
MinMaxScaler()
标准化“收入”栏 - 用
OneHotEncoder()
对分类列进行编码 - 将“年龄”列与宁滨分组。
首先,让我们快速浏览一下数据集:
IN[4]
toy = pd.read_csv('toy_dataset.csv')
toy_final=toy.drop(['Number'],axis=1)IN[5]
toy_final.isna().sum()
**OUT[5]
City 0
Gender 0
Age 0
Income 0
Illness 0
dtype: int64**IN[6]
numeric_cols=toy.select_dtypes(include=np.number).columns
print("numeric_cols:",numeric_cols)
categorical_cols=toy.select_dtypes(exclude=np.number).columns
print("categorical_cols:",categorical_cols)
print("shape:",toy_final.shape)
**OUT[6]
numeric_cols: Index(['Number', 'Age', 'Income'], dtype='object')
categorical_cols: Index(['City', 'Gender', 'Illness'], dtype='object')
shape: (150000, 5)**
现在让我们执行上面提到的操作:
IN[7]
bins = KBinsDiscretizer(n_bins=5, encode='onehot-dense', strategy='uniform')
ct = ColumnTransformer([
('normalization', MinMaxScaler(), ['Income']),
('binning', bins, ['Age']),
('categorical-to-numeric', OneHotEncoder(sparse=False, handle_unknown='ignore'), ['City','Gender'])
], remainder='drop')x_train, x_test, y_train, y_test = train_test_split(toy_final.drop('Illness', axis=1), toy_final.Illness,
test_size=0.2, random_state=0)param_grid_lr=[{'lr__solver':['saga'],'lr__C':[0.1,1,10],'lr__penalty':['elasticnet','l1','l2']},
{'lr__solver':['lbfgs'],'lr__C':[0.1,1,10],'lr__penalty':['l2']}]IN[8]
pipe_lr = Pipeline([
('columntransform', ct),
('lr', LogisticRegression()),
])gs_lr =GridSearchCV(pipe_lr,param_grid_lr,cv=5)
gs_lr.fit(x_train,y_train)
test_score_lr = gs_lr.score(x_test,y_test)
print("test score:",test_score_lr)
print("best parameters: ",gs_lr.best_params_)
print("best score: ", gs_lr.best_score_)
**OUT[8]
test score: 0.9198666666666667
best parameters: {'lr__C': 0.1, 'lr__penalty': 'l1', 'lr__solver': 'saga'}
best score: 0.9188750000000001**
sklearn 库中带有KBinsDiscretizer()
的 bins 方法被设置为 5 组,并由 OneHotEncoder 编码。用ColumnTransformer()
应用的预处理过程集中在一只手里。这些操作是:
-规范化为、列、
-离散化为、【年龄】、
-用OneHotEncoder()
编码为分类列
然后,数据集被分成训练和测试两部分。使用选定的超参数创建字典(param_grids_lr
)以评估参数组合。要应用的数据预处理方法由 ColumnTransformer 一手收集(更多信息请单击此处)并且算法-LogisticRegression-被放置在管道中。和上面的例子一样,通过在 GridSearchCV 中选择交叉验证值 5 来完成模型。
param_grid_lr
字典创建为算法+双下划线+超参数。LogisticRegression()
定义为 lr,我们知道' C '是逻辑回归的超参数,所以使用 lr__C。要查看所有可用的超参数,应用lr.get_params().keys()
。
现在让我们试试我们用 DecisionTreeClassifier()
准备的模型:
IN[9]
pipe_dt = Pipeline([
('columntransform', ct),
('dt', DecisionTreeClassifier()),
])
param_grid_dt={'dt__max_depth':[2,3,4,5,6,7,8]}
gs_dt =GridSearchCV(pipe_dt,param_grid_dt,cv=5)
gs_dt.fit(x_train,y_train)
test_score_dt = gs_dt.score(x_test,y_test)
print("test score:",test_score_dt)
print("best parameters: ",gs_dt.best_params_)
print("best score: ", gs_dt.best_score_)
**OUT[9]
test score: 0.9198333333333333
best parameters: {'dt__max_depth': 2}
best score: 0.9188750000000001**
我们选择的 max_depth 值被逐一拟合,通过网格搜索确定最成功的一个。
5.具有特征选择的管线
正如简介中提到的,使用管道和 GridSearchCV 是评估超参数组合并轻松编译它们的一种非常有效的方法。它不仅对于数据预处理和算法非常有用,而且对于数据清理(SimpleImputer
)、特征处理(SelectKBest
、SelectPercentile
,更多信息点击此处等也非常有用。现在,让我们将以下内容应用于包含 30 个特征的乳腺癌数据集:
—用 StandardScaler()
标准化数值
— PolynomialFeatures()
以数值表示
—带SelectPercentile()
的方差分析
—逻辑回归超参数( C 和罚值)
—调整交叉验证=3
IN[10]
cancer=load_breast_cancer()
cancer_data =cancer.data
cancer_target =cancer.targetIN[11]
anova = SelectPercentile()
poly = PolynomialFeatures()
lr=LogisticRegression(solver='saga')param_grid_cancer=dict(poly__degree=[2,3,4],
anova__percentile=[20, 30, 40, 50],
lr__C=[0.01,0.1,1,10],
lr__penalty=['l1','l2']
)pipe_cancer = Pipeline([
('standardization',StandardScaler()),
('poly',poly),
('anova',anova),
('lr',lr)
])gs_final = GridSearchCV(pipe_cancer,param_grid_cancer,cv=3,n_jobs=-1)x_train, x_test, y_train, y_test = train_test_split(cancer_data, cancer_target,test_size=0.2,random_state=2021)gs_final.fit(x_train,y_train)
test_score_final = gs_final.score(x_test,y_test)
print("test score:",test_score_final)
print("best parameters: ",gs_final.best_params_)
print("best score: ", gs_final.best_score_)
**OUT[11]
test score: 0.9736842105263158
best parameters: {'anova__percentile': 20, 'lr__C': 0.1, 'lr__penalty': 'l1', 'poly__degree': 2}
best score: 0.9626612059951203**
用param_grid_cancer
测试的超参数组合已经定义:
PolynomialFeatures()
的度数=[2,3,4]
SelectPercentile()
的百分位数= [20,30,40,50]
对于LogisticRegression()
,C=[0.01,0.1,1,10]
LogisticRegression()
的惩罚=['l1 ',' l2']
这些都是用StandardScaler()
输送进来的。然后在 GridSearchCV 中将交叉验证值设置为 3。数据集用train_test_split
分割,并一如既往地用.fit
装配。当 SelectPercentile 中的'百分位'设置为 20%,Clogistic regression 中的值设置为 0.1,penaltylogistic regression 中的参数设置为“L1”,多项式 Features 中的' degree '设置为 2 时,精度最高。
在评估从单一来源创建管道模型时需要的许多事情时,管道是有用的。
make_pipeline
可以和管道一样使用。make_pipeline
自动为步骤创建必要的名称,因此只需添加流程即可。
回到指南点击此处。
https://ibrahimkovan.medium.com/machine-learning-guideline-959da5c6f73d
为您的数据创建实时和暂存环境的简单方法
从使用数据表单的 SQL 转换的数据图开始
数据形式依赖图。作者图片
L 假设您正在使用 BigQuery、Snowflake、Redshit 等构建您的数据仓库解决方案。您的任务是创建一些报告,并且您需要为您每天进行的 SQL 转换创建生产和测试环境。
您可能希望将数据转换作为代码运行,将更改推送到 git 存储库。您还在寻找一种简单易用的方法来记录一切,运行 SQL 单元测试,并在数据仓库出现问题时接收通知警报。
一个理想的方法是创建两个独立的数据项目,这两个数据项目不重叠,如果需要的话可以很容易地删除。举个例子,
我想创建一个隔离的 staging 数据项目,然后删除与之相关的所有资源。
这是一个琐碎的任务,可以通过 AWS Cloudformation 或 Terraform (与谷歌云平台更相关)轻松完成,数据工程师经常被分派这项任务。
在这个教程中,我将使用 BigQuery。这是我个人最喜欢的数据仓库解决方案,而且是免费的(如果你处理少量数据,比如在staging
上)。你也可以用类似雪花的东西。30 天免费。设置将非常相似。
环境的一个常见用例是为您的 SQL 运行分阶段发布流程。
在试运行环境中测试代码后,代码被提升到稳定的生产环境中。
概述
步骤 1。创建production
BigQuery 项目,随便你怎么称呼,比如bq-shakhomirov
步骤二。将 BigQuery 项目连接到 Dataform 项目。
步骤三。建立 staging 环境,即在 BigQuery bq-shakhomirov-staging
中创建另一个项目
第四步。创建一个空的(完全空的) Github 仓库,并在那里迁移数据表单项目。
完成后,您将能够在自己的分支中开发 SQL 管道,创建拉请求并合并到staging
分支中。该分支将用于数据表单中的暂存环境,其中所有 SQL 转换都在您的暂存 BigQuery 项目中运行。实时数据将保持不变。当你对你的脚本感到满意后,你可以将它合并到主分支,并在 Datform 的生产环境中使用,即bq-shakhomirov
BigQuery 项目。
第一步。为生产创建 BigQuery 项目
将暂存数据和生产数据分开的一种简单明了的方法是为每个环境使用不同的数据库。我们将使用bq-shakhomirov
(替换为您的项目名称)进行生产,使用bq-shakhomirov-staging
进行测试。
1.转到 BigQuery 页面 并创建您的第一个项目:
作者图片
2.生成数据仓库凭证
为了让 Dataform 连接到您的 BigQuery 仓库,您需要使用应用程序默认凭证或服务帐户和 JSON 密钥。
您需要从您的 Google Cloud 控制台创建一个服务帐户,并为其分配访问 BigQuery 的权限。
转到服务账户页面
-确保选择了您创建的新项目,然后单击“打开”。
-单击“创建服务帐户”并为其命名。授予新帐户 BigQuery Admin 角色。
正在创建服务帐户。作者图片
正在授予 BigQuery 管理员角色。作者图片
完成后,您需要为您的新服务帐户创建一个密钥(JSON 格式):
- 在“服务帐户”页面上,找到要为其创建密钥的服务帐户行,然后单击“操作”按钮。
- 然后点击
**管理密钥**
。 - 点击
**创建**
并选择 JSON 键类型。
创建新的 JSON 键。作者图片
现在您已经创建了一个新的 BigQuery 项目并生成了您的仓库凭证,您已经准备好创建您的数据表单项目了!
步骤二。将您的项目连接到数据表单
去https://app.dataform.co/创建你的数据表单项目。例如,您可以选择使用您的** Github **帐户注册,然后创建您的第一个项目。
- 为您的项目命名,例如“bq-shakhomirov”。
- 您将需要您的 BigQuery 项目 ID 来连接到 Dataform。您可以在您的 BigQuery 控制台中找到它,只需点击您的项目。
- 您将看到如下所示的页面。点击“浏览”并上传您的 JSON 密钥。然后点击“测试连接”,如果一切正常,你就可以开始了。然后点击“保存连接”。
连接到数据表单。作者图片
第三步。设置暂存环境
对于暂存环境,我们将在 BigQuery (数据库)中创建一个单独的项目。因此我们可以安全地删除所有暂存资源,以备不时之需。Google 推荐的最佳实践是创建一个单独的项目来分离生产和暂存环境。
1.您可能想要为暂存创建一个新的 BigQuery 项目。
只需重复从步骤 1 开始的步骤,并将“-staging”后缀添加到您的项目名称中:
创建用于暂存的项目。作者图片
2.授予您的服务帐户对临时项目的访问权限。
还记得我们在步骤 2 中创建了一个服务帐户吗?我们向 dataform 提供了这些凭据。现在,我们希望该服务帐户能够访问我们的临时项目。
转到谷歌控制台中的 IAM ,添加来自步骤 1 的我们的服务帐户电子邮件作为具有 BigQuery Admin 权限的新成员:
现在转到您的数据表单项目的环境,选择 staging 并单击 Run schedule :
测试试运行计划。图片作者。
这次运行将在您的 staging BigQuery 项目中创建新表。它可以是计划每天运行的任何 SQL 转换。在我的例子中,它是一个支付历史表和一个视图:
连接到数据表单的暂存环境
注意,我们选择了一个名为“ staging ”的分支。如果你还没有,你可以在数据表单控制台中创建它。
在数据表单中创建新分支。作者图片
4.将您的数据表单项目迁移到 Github。
您可能希望至少有两个分支:一个用于production
(主节点),一个用于staging
。我称之为分期。您的数据团队成员可以在其个人分支,即**mshakhomirov-dev**
中开发任何变更,然后创建拉请求以合并到staging
分支中,该分支将在您的 BigQuery 暂存项目中运行。
1。首先,您需要创建一个完全空的新 Github 存储库。
该存储库可以是私有的或公共的,或者是组织的一部分。存储库必须是全新的,即没有任何提交。
请确保您已经从 git 提供程序提供了有效的访问令牌。确保您提供了一个凭据,该凭据具有推送至您计划使用的存储库的必要权限。
2。然后转到 Dataform 并连接到 GitHub
- 你可以点击导航菜单- >项目设置中的将项目迁移到 GitHub** 按钮。**
将项目迁移到 GitHub
如果需要的话,创建一个 GitHub 个人访问令牌,但总的来说这个过程相当顺利。
- 输入要用来存储项目的 Git 存储库的全名。
- 确保它是空的并且不包含任何文件。甚至没有。gitignore 和 README.md
连接后,您将看到如下屏幕:
连接到 github repo 的数据表单
3。然后你会想要配置你的数据表单的配置
确保你的数据表单的environments.json
文件有gitRef
到你的分支。例如:
现在,无论您将什么合并到存储库中的**staging**
中,它都将使用自己的时间表在 dataform 的**staging** environment
中运行。
默认情况下, Dataform 从项目的主 Git 分支运行所有项目代码。配置环境允许您控制这种行为,使您能够运行多个不同版本的项目代码。
这是一种清晰而简单的方法来分离暂存数据和生产数据。
因此,默认情况下,我将使用我的暂存 BigQuery 项目:
dataform.json .图片作者
当来自暂存分支的拉请求被批准,我们可以将其合并到master
时,我希望数据表单使用我的 live BigQuery 项目。在这种情况下,我的environments.json
将让默认数据库覆盖:
作者图片
./environments.json:
结论
我想在 BigQuery 中可视化我的 SQL 转换管道。我一直在寻找一个专门用于此的图形工具,甚至考虑为此从头开始构建一些东西,但后来我意识到 Dataform 已经有了。
一个 SQL 转换图抵得上一千个字。
我的数据仓库也很大。我已经在使用 git 管理我的 SQL 脚本了。然而,对于一个成长中的数据团队来说,无论他们做什么样的数据转换,投资于自动化文档可能是值得的。Dataform 让我可以轻松地使用。sqlx 和。js 模板。本质上,它是相同的 SQL,但是您将使用 ref() 函数来引用您使用的表。我使用了大量的数据健康检查,在我的 SQL 脚本中明确地定义了它们。同样,dataform 让我通过创建数据检查视图来做到这一点。就个人而言,我更喜欢看到 SQL 脚本中的数据检查 SQL,而不是被迫在另一个选项卡中打开视图 SQL,但这是一件小事。老实说,当数据校验存储在一个专用数据集中时,对团队的其他成员来说效果更好。更容易找到他们。
Dataform 可以很好地描述数据堆栈的数据转换层。您可以声明您的源表,并提供一个描述,说明实际上是什么在向它们提供数据,即 API、DB extracts 或 Kinesis 流。
推荐阅读:
https://docs.dataform.co/getting-started-tutorial/set-up
[4]https://docs . data form . co/best-practices/start-your-data form-project
Python 3.8 中从零开始的计量经济学和统计学
使用 Wooldridge 数据库了解全球变暖的来源,无需统计库,或者如何在 Python 3.8 中执行线性回归和矩阵计算,作者:Louis Brulé Naudet。
建议
与 Python 3.8 的兼容性测试,在 MacOS 11.3 和 Linux Ubuntu Server 20.04 LTS 环境下执行。
使用的库:Numpy,Pandas,Bokeh,SciPy。
为了使程序在最少人工干预的情况下可执行,并优化其在服务器上的部署,已经实现了升级、数据下载和异常处理的自治,特别是在 server_SMTP 类中。
源代码将首先观察其执行所必需的库的存在,然后在不存在的情况下下载它们,以便自动更新执行环境的软件配置。在同一模式下,将从 louisbrulenaudet.com 网站下载一份数据库副本,以通过 SSL 证书验证的变化,避免与不同机器上的访问路径书写相关的冲突。
变量的描述性分析
为了简化源代码的阅读,我们创建了一个子指令块集中的功能,允许对分散和位置进行各种计算。
第一种可能的解释是,平均值几乎系统地高于每个序列的中值,就像世界二氧化碳排放总量的情况一样,或者解释变量:平均温度(分布大多不对称向右)。第二个观察结果是,水泥生产和燃烧产生二氧化碳排放水平大大低于天然气,石油或煤炭生产产生二氧化碳排放水平。具体来说,后两个因素的二氧化碳排放量是我们数据库中已知的最大值。实际上,为了增强用户体验,通过在 HTML/JavaScript 中提供交互式图形,所有的表示都可以在线访问。因此,通过点击此处(垂直滚动页面),可以突出显示解释变量和被解释变量随时间的变化。图示证实了我们的直觉,我们观察到所有解释变量和被解释变量随时间的总体增长,除了燃烧产生的 CO2 排放和水泥生产产生的 CO2 排放。观察到一次能源消耗呈准线性增长,而二氧化碳总排放量则稍不规则。所解释的变量显示了一个不可否认的长期增长,但在中期(五年尺度)更值得怀疑。
为了限制库的使用,所有的统计分析都是从专有指令块发展而来的。根据定义,统计序列的经验方差是有偏差的,因为:
但是,如果 n>1,根据线性期望值,通过将样本上测量的方差乘以以下各项获得的估计方差:
是 sigma 平方的收敛无偏估计量。因此,源代码中应用的公式分别是:
除了燃烧和水泥生产产生的二氧化碳排放量之外,所有解释变量都观察到相对较大的标准偏差值,相当于每个样本值之间的数量分散,从而证实了不存在随时间的恒定性和变化趋势(通过图形分析获得的增长)。
相关性和简单线性回归
对于我们工作的其余部分,我们试图反驳解释变量和被解释变量之间的独立性假设,以实现仿射调整,最接*平面中的每个点云。
第一个观察结果是,皮尔逊相关(一个无量纲量),一种量化两个变量与其各自期望值的联合偏差的协方差的标准化形式,对于上述所有解释变量都相对较高,相反,对于燃烧产生的 CO2 排放量则较低。然而,由于没有一个变量的相关系数低于 0.1,我们可以估计它们或多或少都是被解释变量的依赖源。二氧化碳总排放量和一次能源消耗是与温度最相关的两个解释变量。我们分析中测试的回归基于普通最小二乘法,即最小化回归云的每个点与其投影点之间的残差平方和。因此,简单回归模型试图通过定义β系数向量的最佳无偏估计量来建立两个变量之间的线性关系。
系数β1 解释为一个额外单位的解释变量对被解释变量的边际效应。对于单变量线性回归,R 平方定义为 SES 回归解释的方差与总 SST 方差之比,并测量数据与拟合回归线的接*程度。因此,接* 1 的决定系数将表明该模型解释了平均值周围响应数据的所有可变性,相反,降低的系数将使回归不解释变量之间的依赖性。在我们的研究中,我们观察到最重要的拟合系数是温度对天然气生产 CO2 排放量的回归。这个结果再次证实了我们的假设。我们可以尝试解释的一个异常现象是,石油生产中二氧化碳排放的相关系数较低。从图形上看,我们观察到分布接*指数函数,这可以解释与线性回归定义的不匹配。对于这个推理,所有的回归都可以通过点击这里得到。另一个观察结果涉及与燃烧产生的 CO2 排放相关的β1 系数。诚然,这一个相对于其他的非常高,然而,非常低的决定系数向我们表明回归缺乏显著性。这种批评在数学上反映为相对较大的标准误差值和 p 值。量化统计显著性,其值接* 0.05 表明,如果验证了零假设,可能会得到相同的结果。与该变量不同,所有其他回归在 p 值方面具有统计学意义。关于 t-统计量,我们发现我们的直觉,即丢弃石油生产的 CO2 排放异常,最显著的回归来自天然气生产、一次能源消耗和 CO2 排放总量。
简单线性回归的主要缺点是存在遗漏变量偏差。因此,除了同时性之外,当一个被省略的变量影响被解释变量和一个(或多个)解释变量时,解释变量和误差项之间的相关性也可能发生。这将允许我们解释我们的异常现象。限制这种偏差的一种方法是在多元线性回归中引入几个解释变量。为此,我们首先对数据进行归一化,以避免对梯度下降算法的性能产生任何负面影响。由于平均温度值可能为负值,我们不采用对数变换,而是采用标准变换,即:
然后,这种缩放允许我们补偿每种类型的发射之间的水平的巨大差异。
相关性和多元线性回归
梯度下降算法旨在实现可微优化。因此,它旨在最小化定义在欧几里得空间上的可微实函数。该算法是迭代的,并且通过连续的改进进行,直到收敛。然后,我们试图最小化梯度,导数的多维推广。学*系数将被定义为一个参数,该参数允许调整校正,并且通过扩展,允许调整收敛速度。在我们的模型框架中,我们发现:
为了优化回归的训练系数α和β系数的确定,将通过对每个值进行迭代来生成可能值的列表,以便只保留多元回归的最高确定系数。
根据经验,我们发现 R 平方为 0.900927,这证实了我们关于省略变量偏差的建议。事实上,我们从未通过简单的线性回归获得如此高的统计结果。
中期和长期预测
根据这一观察,我们可以对中期和长期的平均气温值进行预测,方法是在所有条件不变的情况下,对每个变量进行变分,上升和下降都一样。因此,我们将能够确定实施哪些措施来降低全球气温。在我们的分析中,我们决定根据数据库中每个变量的最新已知值,分别执行 5%、10%、25%、50%、100%和 200%的向上和向下变动。
我们观察到,在短期和中期内,影响是不明确的,然而,在长期内,我们发现石油、天然气和水泥生产的 CO2 排放显著增加,显著影响平均温度。在我们讨论的开始没有提到水泥,但是这个发现与简单线性回归相关的β1 的相对水平及其决定系数产生了特别好的共鸣。从长远来看,如果二氧化碳排放量的增幅非常大,它甚至会成为第二大二氧化碳排放源。这种影响可以用两个因素来解释:水泥厂的熟料生产需要石灰石(自然界中最稳定的钙形式)的脱碳,以及在有二氧化硅的情况下煅烧。
因此,减少全球 CO2 排放的假设之一(其似乎与平均温度的增加高度相关)是促进使用替代的非碳酸燃料来代替化石燃料。天然气必须经过去除腐蚀性元素(如硫)的处理,这些过程都是导致 CO2 排放到大气中的因素。另一种选择是使用木质颗粒作为家庭取暖或热泵。然而,似乎很难找到一种在居住舒适性和减少气体排放之间实现完美平衡的解决方案。事实上,用完全可再生的电力资源,比如风能,只靠电力来取暖似乎是乌托邦。然而,不可否认的是,原子裂变发电似乎是二氧化碳污染最小的解决方案之一。它甚至是光伏发电的更好替代品,光伏发电每千瓦时产生 100 至 200 克二氧化碳,比核电高出三倍。最后,根据西欧国家正在实施的公共指令,减少石油生产引起的排放似乎是降低全球温度的主要改进方向。这就提出了与天然气相同的问题,即如何找到一种像石油一样通用、高效的可用系统,同时减少温室气体的产生?
美国国家海洋和大气管理局在 Unsplash 拍摄的照片
电力的提议作为一个奇迹般的解决方案回来了,除了它的储存,需要使用锂电池,这是一种碱金属,很难以更低的成本回收。在运输部门,氢似乎也是一个令人感兴趣的发展前景,从有机材料生产的生物燃料也是如此。然而,我们的多元回归对一次能源消费的影响以及对煤炭生产的影响仍然模糊不清,尽管其决定系数表明了对全球温度的依赖。让我们以所有其他同等变量系统的倒数来结束我们的分析,这些变量对每个变量都有负作用。从乌托邦的角度来看,我们提出的措施会让我们降低全球气温。
注意:由于原始数据库的规模小,省略变量的可能偏差是我们分析中产生异常的原因。温室气体的其他来源肯定存在,地球变暖与人类活动无关的可能性也不容忽视。
Louis bruénaudet,巴黎大学法学和经济学/管理学双学位。
经济史和通往奇点之路
播客
大卫·罗德曼讲述当人工智能将我们推离地图边缘时会发生什么
要选择章节,请访问 Youtube 视频这里。
编者按:这一集是我们关于数据科学和机器学*新兴问题的播客系列的一部分,由 Jeremie Harris 主持。除了主持播客,Jeremie 还帮助运营一家名为sharpes minds的数据科学导师初创公司。可以听下面的播客:
经济学中有一个小谜团,它可能表明,对人类来说,事情将变得非常非常怪异。
这个谜是这样的:许多经济模型预测,在某个时候,人类的经济产出将变得无限。
现在,无限在现实世界中是不会发生的。但当它们被其他合理的理论预测时,它们往往会指出这些理论的假设在某个根本的方面崩溃了。通常,这是因为像相变这样的事情:当气体冷凝或液体蒸发时,它们的一些热力学参数趋于无穷大——不是因为任何“无穷大”的事情真的在发生,而是因为当这些气体变成液体时,定义气体的方程不再适用,反之亦然。
那么,我们应该如何看待那些告诉我们人类经济产出总有一天会达到无穷大的经济模型呢?将它们解释为预测人类经济的一个阶段转变是否合理——如果合理,这个转变会是什么样的?这些都是很难回答的问题,但是我的嘉宾大卫·罗德曼——开放慈善机构的高级顾问——思考了很多。
大卫将他的调查集中在他认为可能导致潜在经济阶段转变的罪魁祸首上:变革性人工智能技术的崛起。他的工作探索了一种强有力的方式来思考如何,甚至何时,变革性的人工智能可能会从根本上改变经济的运作方式。
以下是我在对话中最喜欢的一些观点:
- 当你绘制过去 10,000 年全世界的经济产出时,你得到的曲线不仅仅是指数型的——它甚至更快。在整个人类历史上,不是以每年 3%左右的“稳定”速度增长,而是增长速度本身一直在加速。因此,许多简单推断我们当前增长轨迹的经济模型往往会在有限的时间内趋于无穷大。理解为什么会发生这种情况的一个方法是把新的发明想象成时间压缩机:当我们发明电话时,曾经需要几周时间的任务可以在几分钟内完成。同样,轮子的发明、火的发现和计算机的发展都大大缩短了完成一系列任务所需的时间,使得进一步的创新发生得更快。经济模型认为人类的生产产出将趋于无穷大,通常只是假设这种动态将继续下去——尽管出于显而易见的原因,它不能继续下去,但很明显,人工智能可能代表着创新的最后阶段,在这个阶段,新想法是根据计算机时钟时间而不是人类时间构思的。
- 大卫对经济史进行了大量不同的模拟,以了解他们预测奇点——一个无限经济产出的点——的一致性。他发现奇点的出现是相当稳健的:在他的(公认简单的)模型中,当人类达到大约一万年前的经济产出水平时,奇点最终会在大约 95%的时间里出现。正如大卫指出的,重要的是不要把这些模拟看得太重,但分析是有启发性的,并为关于人类和我们在深层时间中的位置的有趣和有启发性的对话创造了空间。
- 除了通过人工智能预测经济转型的工作之外,大卫还做了大量复制经济学研究的工作——经常有惊人的结果。特别是,他估计大约 50%的时候,他的复制研究与他们试图重现的原作的结论相矛盾。不幸的是,这与其他人在心理学和经济学中做复制研究时的发现一致,表明了学术过程的严重失败。但大卫指出,这不应该令人惊讶:很少有资源花在复制分析和确保发表的学术工作的稳健性上。考虑到价值数十亿美元的决策通常是在结论的基础上做出的,而这些结论的验证却没有得到价值几千美元的关注,这就更加令人不安了。
播客中引用的链接:
- 大卫的个人网站。
- 我强烈建议去看看开放式慈善机构。他们的使命是找出极具影响力且被低估的慈善事业。在新冠肺炎之前,他们已经投资了数百万美元用于疫情防备,支持刑事司法改革工作,并在人工智能安全和人工智能政策研究方面投入了大量资金。
章节:
- 0:00 介绍
- 大卫的背景
- 3:26 什么值得资助?
- 6:51 理解抽象风险
- 11:21 人工智能安全
- 14:20 经济模式
- 22:29 新的发现
- 28:44 文化与安全
- 39:51 结合随机微积分
- 45:26 人性的故事
- 49:00“尽职调查”概述
- 55:00 上行分析
- 57:24 培养自信
- 1:07:33 总结
请查看下面的文字记录:
杰瑞米·哈里斯(00:00:00):
大家好,我是杰瑞米。欢迎回到迈向数据科学播客。今天我们要讨论两个有趣的问题,这两个问题看起来毫无关系,但实际上是有关系的。第一个问题非常简单:如果你有 100 美元,你想用它在这个世界上创造尽可能多的快乐、繁荣和幸福,你会把它花在什么地方?第二个问题与我们作为一个物种的长期未来有关:我们是否正处于人工智能驱动的经济革命的边缘?如果是这样的话,到目前为止,我们可以从人类经济活动的历史中学到什么来帮助我们在这种转变发生时进行导航呢?
Jeremie Harris (00:00:36):
所以今天我将和我的嘉宾 David Roodman 一起探讨这些问题,他是开放慈善事业的高级顾问。由于他在开放慈善事业中的角色,大卫花时间探索既重大又实用的问题。这些问题包括:严厉的犯罪立法弊大于利吗?或者我们能通过使用像小额信贷这样的策略有意义地减少贫困吗?正如我们将看到的,他也做了很多有趣的思考,预测先进的人工智能系统何时可能在推理能力上超越人类。这次谈话非常有趣,它联系了许多不同的想法,这是我以前从未想到的。我希望它对你也一样。我希望你真的喜欢它。事不宜迟,我会让开,让它开始。
大卫,非常感谢你和我一起参加播客。
大卫·罗德曼(00:01:18):
很高兴来到这里。
Jeremie Harris (00:01:19):
你能来我真的很兴奋。我是通过,而不是通过你写的关于小额信贷的书发现你的,这本书真的很棒,而是通过你写的一篇关于长期主义和从人工智能的角度看长期主义的文章。我真的很想深入其中。我想我也想深入一些你们已经做过的探索,重新检查一些早期的结果,这些结果是人们在慈善领域收集的,关于哪些倡议值得投资,哪些不值得投资。所以我们有很多要谈的。但在我们开始之前,我想先了解一下情况,谈谈你的背景。那么是什么把你带到了利他主义的领域,并开启了慈善事业,也就是你现在工作的地方?
大卫·罗德曼(00:02:02):
哦,好吧,我已经长大了,可能要花一个小时才能告诉你所有这些,但我会尽量避免。我是一个离异家庭的孩子,在某种程度上,我认为我的生活一直在试图融合我父母在我一生中给予我或灌输给我的个性方面。所以在我 12 岁的时候,我爸爸给我介绍了电脑。我在穿孔卡和 IBM 主机上学会了我的第一次编程。这是我生活的一部分,我一直对编码和数学感兴趣。但我母亲也是 20 世纪 70 年代女权运动的一员。她给了我们,我妹妹和我一种强烈的公民责任感。我一直在试图找出如何将我对公共政策和公共福利的兴趣与我用数学编码和思考事情的倾向结合起来。
大卫·罗德曼(00:03:01):
所以现在回想起来,一切似乎都很明显,但我不知道它会如何发展,但我花了很多时间在我居住的 DC 华盛顿州的智囊团工作。这些通常是由慈善基金会资助的,为了分析复杂的问题,它与实际的决策相关。所以这些年来,通过那种工作,我了解了很多不同的主题。
杰瑞米·哈里斯(00:03:26):
有意思。我发现开放慈善事业特别吸引人的一点是,它有点集中或嵌入在有效利他主义的矩阵中,有点像有效利他主义社区。一条主线是批判性地看待我们投资解决的各种问题,并问这真的是我们资金的最佳去处吗?这导致了一些非常有趣的反直觉的决定,关于什么能得到资助?你介意提供一点概述吗,有哪些你认为值得投资但人们可能没有意识到的更令人惊讶的事情?
大卫·罗德曼(00:04:02):
我在旧金山一家名为 Open Philanthropy 的公司工作。它本质上是一个基金会,尽管它实际上并不持有大量资产,无论它是提出建议还是提出理由。我们现在影响的大部分资金来自达斯汀·莫斯科维茨和他的妻子卡里·图纳。达斯汀是脸书的创始人之一,我在那里呆了四五年。你完全正确。它在很大程度上受到被称为有效利他主义的哲学或思维方式的影响,这就像我刚才以不同的方式描述我自己一样,它是关于试图将让世界变得更美好的承诺与对实际可行和最重要的事情的坚定思考联系起来。
David Roodman (00:04:50):
在我不太想邀功的过程中,我非常钦佩,但并没有密切参与,我们已经分离出三四个主要原因领域和一些次要原因,随着时间的推移,我们会不断增加。有趣的是,他们中的一些人在四五年前第一次被选中时,看起来比现在更古怪、更疯狂。所以我们在为疫情做准备,这需要很多解释,对吗?显然,我们的赠款并没有阻止疫情,但希望我们在应对和准备未来的反应方面有所作为。我们也成为美国刑事司法改革的重要资助者,通过努力减少监狱中的人数,因为美国的人均监禁率目前是世界上最高的,可能除了朝鲜,这不是一个令人愉快的比较。
大卫·罗德曼(00:05:48):
所以我们一直在努力工作,自从我们开始工作以来,在过去的几年里已经取得了很大的进展。另一个对我们来说是农场动物福利,这听起来有点滑稽。但是现在有数十亿的农场动物一直在受苦。如果你甚至说一只鸡的生命只相当于人的生命的 1%,你算一下,与人类的痛苦相比,这是很大的痛苦。如果通过运动让企业做出承诺,改变他们的养殖方式,只用几百万美元就能给几十亿动物带来巨大的变化,这是非常划算的。这些是我们正在努力的一些领域。另一个,当然我们会谈到的,也是看起来没有五年前那么疯狂的事情,是人工智能安全,试图尽我们所能使人工智能对社会安全,所以它是利大于弊的来源。
Jeremie Harris (00:06:51):
实际上,我发现这里真正有趣的是,所有这些举措之间似乎有一种微妙的主题相互作用,这种方式可能并不明显,尤其是我个人经常感觉到的,当我与人们谈论人工智能安全时,很像 2020 年 1 月的一个人在谈论冠状病毒时的感觉,如果你有这个指数级过程, 或者,正如你指出的超级指数过程,它正在展开,有可能让我们所有人感到惊讶,你可以看到数学的方向,如果你只是随着时间的推移迭代前进。 然而,我们的日常生活似乎真的没有反映出潜在的现实。这是你擅长表达或争论的东西吗?你如何让人们理解更抽象的风险,并让他们认真对待它们?
大卫·罗德曼(00:07:43):
这是一个很好的问题。这真的是一种交流。不知道是不是特别擅长。我是那种喜欢沉浸在一系列想法中,然后尽我所能解释这些想法的人,有点像老师。但是如果你不是一个有上进心的学生,你可能还是不会买它。所以我不确定我有多少智慧可以说,除了你需要做一些事情,比如让你的论点非常具体,利用隐喻,联系人们的生活经历。我们现在可以更有说服力地谈论为下一个疫情做准备了,这是一个简单的例子。
杰里米·哈里斯(00:08:19):
是的。
大卫·罗德曼(00:08:19):
我应该说,我刚刚列出了一些原因领域,它们看起来确实有点像一个袋子,人工智能安全,农场动物福利。但是它们来自于一个过程,我想你已经举例说明了其中的一些优点。也就是说,我们能够达成优先事项,尽管存在一定程度的想象力缺失,这使得我们很难理解为什么这些是重要的。我们系统地做了大量的工作,问了很多人他们认为什么是重要的,然后进行思考。然后我们有一些过滤器,寻找有潜在巨大影响的重要事物,可以影响数百万或数十亿的动物,寻找被忽视的事物。这也是为什么它有点杂乱无章的部分原因,因为我们在寻找大多数其他基金会没有注意到的东西。所以这并不意味着我们认为这些是唯一重要的事情,只是我们认为我们可以做得最好的地方。
Jeremie Harris (00:09:13):
从人类相关能力的角度来看,这一直是我认为最困难的问题之一,也是采取这种理性立场最困难的方面之一。不幸的是,你必须告诉人们,大都会艺术基金或任何对我们社区教育的投资或类似的事情都是很好的,但这一美元,边际美元,可以更好地用于除虫运动或专注于人工智能安全或其他任何事情。这似乎有风险,对吗?因为如果你不小心的话,我想你可能会让人们对你敬而远之。
大卫·罗德曼(00:09:52):
没错。在某种程度上,我们愿意接受这一点。例如,我们愿意说,我们真的认为有些慈善机构比其他慈善机构做得更好,或者有更好的证据证明他们在做好事。这是一个值得挑起的重要话题。与此同时,我认为重要的是要认识到,我们所有人都在不同的事情上分散我们的钱,从相当自私的,当我们为自己买一杯咖啡时,到非常无私的和给予的,例如,给你孩子的学校捐款,是介于两者之间的,对吗?我们对告诉人们如何分配并不感兴趣。我们只想说,“好吧,对于你想以最无私的方式花费的那部分支出,这是我们的建议或者这是我们正在考虑做的事情。”
Jeremie Harris (00:10:42):
有意思。这很有道理。有趣的是,当你谈到自私和利他之间的光谱时,我开始看到我的许多日常选择,我可能认为利他主义反映在其中。这就像你可能在指导一个人,但你真的会因为看到他们脸上的笑容而得到回报。你才是享受这种好处的人。这不像你花时间投资抽象的东西。现在谈到抽象,所以我想我们谈到的一项技能是讨论这些抽象风险的必要性,这些风险值得投资,但可能不会反映在我们的日常生活中,其中一个真正重要的领域是人工智能安全。
Jeremie Harris (00:11:21):
我真的很喜欢,我读了你的文章,我想从头到尾说一遍,但这是一篇很好的博文,讲述了我们从人类经济史的角度看待的东西,以及它与人工智能的关系。我不想太具体地说从哪里开始这个对话,我的意思是,我很想听听你说出那个论点的基线水平是什么样子,也许我们可以戳戳它,探索它。
大卫·罗德曼(00:11:45):
正如我所说的,我们提供资助是为了让更多的人思考和研究如何让人工智能变得安全。现在,因为我们是一个相当反思的组织,一个重视内部辩论的组织,即使已经做出了决定,管理基金会的人,特别是我的老板霍尔登·卡诺夫斯基,也邀请其他人来挑战这种想法,或者至少让我们对它的思考更加敏锐。因此,很难确定的一个关键问题是,人工智能有多大可能达到等于或超过人类智能的程度,以及这种情况何时会发生?因为如果我们以某种方式得出结论,认为这不会发生,或者 300 年内不会发生,那么这将会质疑我们拨款的价值。因此,本着这种精神,我被要求从一个特定的角度来看待人工智能突破的时机和可能性这一问题。
大卫·鲁德曼(00:12:47):
有不同的角度。人们可以看看我们认为大脑中进行了多少计算,然后问,什么时候让计算机做这么多计算变得负担得起,等等。我被鼓励去观察一个特殊的角度,那就是观察超过 12000 年甚至 200 万年的经济产出的历史,或者人类系统的规模。所以我想我们可以通过两种主要方式来看待人类系统的规模。一个是人口数量,另一个是他们的经济产出,我们现在称之为世界生产总值。当你放眼长远,不是一个世纪而是几千年,一个非常奇怪的事实出现了,那就是人类系统越大,它的增长速度就越快。
大卫·罗德曼(00:13:34):
我甚至不是指指数增长,这是病毒有时会有的情况。我并不是说它每年增长 3%,所以它变得越来越大。我的意思是,它过去每年增长 0.1%,然后每年增长 1%,也许有一天会增长 10%,或者 100%。所以问题是,如果我们看数量,如果我们量化过去,这能告诉我们,给我们任何关于未来可能会是什么样子的洞察力吗?因为这似乎让我们对另一场像工业革命一样的经济爆炸更有信心,这似乎是唯一可能发生的方式,至少是最有可能发生的方式是某种大型人工智能突破。
Jeremie Harris (00:14:20):
一个非常有趣的数学特征,我的意思是,我甚至可以称之为你所展示的令人担忧的数学特征,当你说出来的时候,你很容易忘记,但是随着人类的进步,时间实际上开始有效地压缩。我想你在文章中提到过,你会达到无限和有限的时间。说我们会在某个时候达到一个任意的经济产出水平是一回事。如果你预测未来,我们将在某个时候达到,全球 GDP,或者 GWP,无论有多少万亿,但是说,不,我们将在有限的时间内达到,下周星期二我们将达到这一点是一个更戏剧性的说法。对你来说,那件事意味着什么?在你的世界模型中,有没有一些东西,你认为,那意味着,我不知道那实际上意味着什么。你对此有什么想法吗?
大卫·罗德曼(00:15:25):
是的,我认为你说得很好。我在博客文章开头提到的一点是一个奇怪的事实,如果你绘制世界生产总值的历史数据系列,那么回到公元前 10,000 年,我们非常粗略地估计它是 16 亿美元,而今天,我不知道它是 70 还是 80 万亿,在那段时间里大幅增长。无论如何,如果你用某种方式绘制数据点,使它们看起来有点,哦,我想我应该这样做。我不知道。【相声 00:15:55】。它们看起来有点像一条直线,这意味着两条轴都是对数的,然后向前延伸,它预测你会达到无限的产出,在我的最佳模型拟合中,你会在 2047 年达到无限的产出。
大卫·罗德曼(00:16:10):
我没把这当回事,对吧?我不认为到 2047 年我们会有无限的产出。但我确实觉得那里有值得学*的东西。仅仅说,哦,那是愚蠢的,然后转到下一篇博文,这不是最优的。
大卫·罗德曼(00:16:29):
但我觉得我们必须问一个问题,为什么这个简单的模型最能描述历史预测和可能的未来,我们应该如何看待这个模型?
Jeremie Harris (00:16:39):
我认为这个模型在描述过去两个世纪的经济产出方面非常有用,或者说非常有用,尽管随着时间的推移,道路上出现了一些颠簸,但这些颠簸似乎逐渐消失,只是为了再次适应这个模型。好吧,现在突然到了 2047 年,离现在只有 27 年了。这开始让这件事的含义变得更加尖锐。是否有其他经济模式在过去以这种方式崩溃,我们可以在回顾和研究中思考?
大卫·罗德曼(00:17:18):
当你说崩溃的时候,你的意思是最终是错的还是预测到了这些奇怪的未来?
Jeremie Harris (00:17:23):
是的,我想我在想无限可能会出现在其他领域,因为我认为你的分析是无限会发生,所以那里会发生一些奇怪的事情。我很好奇其他经济模型是否可能预测到过去的无限,以及在那些政权中会发生什么样的事情?
大卫·罗德曼(00:17:39):
这是个好问题。我刚才所做的基本观察对我来说并不陌生。我想很可能,我在《邮报》上谈到过,它第一次出现是在 1960 年,当时只是从零年开始统计人口。同样的观点是,他们的作者预测到 2026 年,11 月 13 日,地球上将会有无限多的人。所以这种推断是新的,在经济建模领域,我应该说是宏观经济建模领域,有点特别,在宏观经济建模领域,你试图理解整个经济的动态,而不是单个工厂。这是一门从 20 世纪 50 年代开始真正兴起的学科。他们经常纠结于这个问题,你可以写下来的非常自然的模型,它似乎适合现在,往往会爆炸。因此,为了避免一些奇怪的暗示,他们有时比其他人更扭曲地工作,也许是因为害怕不被重视或不被发表。
大卫·罗德曼(00:18:56):
我应该说,在过去的两个世纪里,我们一直在谈论的简单模型是否还有效尚不清楚,工业革命前后有一次大的增长起飞。但自那以后,工业国家的增长更接*指数模型,每年的增长率相当稳定,可能是每年 3%,而不是增加。因此,这可能意味着我一直在谈论的增长加速的模式已经过时,成为过去,或者它可能只是我们在长期趋势中的一个随机波动,很难知道。
Jeremie Harris (00:19:44):
你认为这种造型有一种可以理解的原因吗?当我们谈论时间压缩时,我总是把它概念化,当我听到这样的论点时,就好像有一天洞穴里的原始人想到了火的概念。火让我们可以在两周内完成一个月的任务。因此有效地压缩了时间。我们继续向前看,托马斯·爱迪生发明了灯泡,突然之间,我们完成了一年、六个月等等的任务。所以实际上,有一种压缩,但我觉得这种模式,如果有什么不同的话,这种模式应该使过去的 200 年更加符合这种趋势。与过去相比,我们似乎更善于提出新想法并加以传播。所以我想我在看这个逻辑结合点,我不清楚,我的哪些假设在这方面失败了。你对此有什么想法吗?
大卫·罗德曼(00:20:44):
不,说得好。因此,我们一直在谈论的超级指数增长、加速增长模型的有趣之处,不仅仅在于它是一个符合历史的简单数学模式,而是在于有一个你刚刚描述的直观理论,可以帮助我们理解为什么增长会随着时间的推移而加速。这就是技术。只需要一个人发明火,然后这个想法就可以传播开来。而且只需要一个人发明半导体,这个想法就可以传播开来。因此,人越多,他们从事研究的资源就越多,那么社会产生新思想的速度就应该越快,这就意味着更高的生产力,也就意味着产生新思想的能力更强。因此,在历史的进程中,似乎出现了一些重要的动态,即他们的创新步伐加快,从而允许更多的加速。
大卫·罗德曼(00:21:47):
因此,相应的问题是,是的,我们今天比以往任何时候都更有创新能力。一个相反的观点,有很多关于这个观点,证据和理论的严肃的论点,就是也许我们已经没有什么可以发现的了,尽管看起来事情变化的如此之快。在过去的 50 年里,工业化国家的人们的生活在很多方面都没有改变。747 已经服役 50 年了,除了在一些能效方面,我们并没有对它们进行太多的改进。因此,如果你看看创新的速度,例如在物理学中,似乎我们今天没有和 100 年前一样的突破。
大卫·罗德曼(00:22:29):
所以,也许,嗯,那些关于量子力学的基本发现真的会再次转化为有用的技术,所以当我们开始做出基础科学发现时,那可能最终意味着我们会停止做出重要的技术发现。所以这是一个值得思考的非常重要的问题,我没有简单的答案。我当然不否认这样一种观点,即我们已经没有东西可以发明了。最大的问题,最大的例外似乎是人工智能。如果你能让机器来做,基本上如果我们能为大脑做我们已经为身体其他部分做的事情。我们已经有了能够移动的机器,对吗?这是人类事务变化的主要来源。如果我们能对大脑做同样的事情,这似乎会有根本性的影响。
Jeremie Harris (00:23:24):
在我像彼得·蒂尔(Peter Teal)一样深入讨论大停滞和原子与比特的概念的背景下,这非常引人注目。这似乎是人们经常画的二分法。他们会说,自 50 年代以来,我们在比特世界取得了巨大的进步,计算机、模拟、虚拟现实和 iPhones,但在原子世界,医疗似乎更慢,教育更昂贵。当你观察像住房和药品这样的产品与像电子产品这样的产品的通货膨胀率的比较时,你几乎可以看到这些东西被绘制出来,这些产品正在疯狂地紧缩。
Jeremie Harris (00:24:05):
我想你的分析让我想知道的一件事是,从长远来看,这两个世界之间实际上有什么有意义的区别。看起来似乎我们需要在比特的世界中取得一定的进步,因为我们已经尽我们所能与身体接触,用我们有限的认知独自工作。在某种意义上,我们需要将一些计算外包给比特,这是限速步骤。一旦我们打开障碍,也许事情将会彻底改变。这是对论文的公正概括吗?
大卫·罗德曼(00:24:40):
我的论文?
杰瑞米·哈里斯(00:24:42):
是的。增长理论,我想这个想法[听不清 00:24:44]。
大卫·鲁德曼(00:24:44):
是的。我认为这是对增长观点的公正概括。对此我有几点看法?一是,我认为经济和我们生活的某些主要领域可能不会受到双边投资条约中如此多创新的影响。让我们四处移动的机器只能变得如此高效。我们不能有 100 倍的改进。这就是一个例子。我个人对这个问题不太感兴趣,因为我很想知道中国的经济增长率是否会从现在的 3%提高到 40%。对此我不太关心。那么,模型和数学是否在向我们发出信号,预示着某种彻底变革的可能性,只是某种彻底的经济变革。这种变革也许不会改变我们生活中的各个经济领域,但在经济上仍然可能和工业革命一样重要,非常重要,至少在这个层面上是这样。
Jeremie Harris (00:25:48):
说到彻底改变,我的意思是,这听起来让人兴奋,但同时也是一种不祥之兆,而且你还提到了人工智能的安全性。所以我想把这两者绑在一起。那么,首先,对于可能出现的问题,你有什么想法?其次,对于我们目前可以采取哪些措施来缓解这些问题,你有什么想法?
大卫·鲁德曼(00:26:08):
在这个领域,我觉得自己读的东西比大多数人都多,但我并不觉得自己是个专家,对这个领域既没怎么写过,也没怎么说过。所以我觉得自己主要是在引用别人的想法。今天有一些关于人工智能的道德和社会影响的讨论,关注的焦点是我们是否可能在我们的系统中训练偏见之类的东西。我认为我们在 Open 慈善会上也看到了这一点,但我们更关心的是可能发生的事情,以及能够改变大部分经济运行方式的更激进的事情。人们担心会出现更为负面的情况,因为如果你的代理能够像人类一样在物理世界或虚拟世界中高效运行,那么所谓的代理,就是软件和硬件的结合。但它们靠的是硅,而不是蛋白质,而蛋白质正是我们大脑运转的基础。但如果它们能跑得和我们一样快,就没有理由认为它们的速度不可能是我们的 10 倍、一周、一年或 100 倍,它们的繁殖速度也不可能比人类快得多。
大卫·罗德曼(00:27:27):
然后就是巨大的意外后果,对吗?也许这些后果是人工智能想要的,但不是我们想要的。大多数人工智能代理被定义为优化某些目标。所以我认为是埃利泽·尤德考斯基首先传播了被回形针的想法,回形针的想法,我们可以创造一个代理人,它被指示制造尽可能多的回形针,然后它继续把整个地球变成回形针,因为没有人能阻止它。因此,重点不是人工智能会恶意地看待我们,而是它不会关心我们,并且按照人类的标准,它会非常有能力实现编程要实现的任何目标。这就是我们最担心的事情。
大卫·罗德曼(00:28:22):
大概有一系列的可能性,即使极端是不现实的,也很难判断,它可能是不太极端的事情的替代品,我们可以做一些准备,例如,在人工智能研究中创造一种安全的文化,就像我们在药物研究和许多其他领域所做的那样。
Jeremie Harris (00:28:44):
我发现对安全文化的强调非常有趣。这绝对是让我产生共鸣的事情,因为,嗯,不仅仅是因为,让这些长期未来的人工智能系统安全似乎存在一些技术问题,我们今天可能甚至没有意识到。我们实际解决技术问题的能力是有限的,也许在游戏的那个阶段会有所关联。当然,我们今天也可以解决一些技术问题,但看起来这几乎是今天的一种技术挑战,也是一种植入正确价值观的文化,这样当我们到达这种机器即将出现的阶段时,我们至少有了某种一致性,人们意识到了安全,他们意识到了这其中涉及的回形针类型的风险。Open Phil 在是否关注文化和安全上是否有侧重点的划分?两个都有人看还是?
大卫·罗德曼(00:29:43):
哦,我不太了解我们的计划或我们的拨款。我不确定这种区分是否站得住脚,因为在一天结束的时候,我们给予资助,试图支持人们思考某些事情,而不是思考其他事情,写某些事情和谈论某些事情,而不是在他们有限的时间内思考其他事情。在某种程度上,这就是文化的改变。
杰里米·哈里斯(00:30:10):
是的。确实如此。我想今天你会有一群人在研究技术问题,这种技术研究小组最终会变得…这是有意义的。现在就回形针而言,当我与人们谈论这个话题时,我总是觉得有趣的一件事是,回形针似乎是一个非常极端的例子。你会告诉他们,你制造了一个人工智能,你告诉它优化回形针,它意识到地上有铁,你的血液里有铁,到处都有铁,我会把它取出来,重新组织世界,制造一堆这样的东西。这看起来几乎是异想天开,但我想我一直面临的挑战之一是传达这样一个事实,即这实际上适用于更平凡的事情,我想正如你所说,Yudkowsky 非常关注这个问题。
Jeremie Harris (00:30:55):
我想我看到他写的一个例子是,当我们可以让一个任意智能的人工智能将一个草莓放在桌子上时,我们就会知道我们有一个安全的人工智能系统。事情就是这么简单,你怎么定义草莓,你怎么找到桌子?如果你有一个瓶子里的精灵,它是任意强大的,意义上的微小差异可以转化为行动上的巨大差异。我猜,你本身不是人工智能安全研究人员,但对于我们是否能够在未来 20 年,也就是 2047 年之前,有意义地推动这一类风险,你的乐观程度如何?
大卫·罗德曼(00:31:41):
是的,我不知道。开放式慈善机构的理念是,如果我们有很小的机会做出重大改变,那么我们应该尝试一下,除非这种机会太小,以至于在某种程度上看起来毫无意义。这就是我们的哲学。我忘了内部的计算是什么,但我认为这就像我们认为我们有不到 10%的机会做出改变,但如果有 1%或 5%的机会塑造下一场经济革命,那就是慈善资金的伟大用途。所以大部分时间我都在回避你的问题。没有人真正知道。
大卫·罗德曼(00:32:35):
我对在对立例子上所做的工作持乐观态度,因为我认为这有助于我们理解,也有助于尝试进入这些深度学*系统的黑匣子,并找出它们所采用的算法。因为在某种程度上,例子就像是煤矿里的金丝雀,它们展示了看似很小的输入变化是如何导致非常大的输出变化的。突然一张在我们看来像苹果的图片被归类为熊猫,对吗?事实上,我们现在已经在这个基础上,或者说正在努力,我认为这是一个好迹象,我们正在深入了解这些系统实际上是如何思考的。然后,如果你能揭开正在发生的事情的神秘面纱,也许他们会让事情变得更容易管理。
杰里米·哈里斯(00:33:25):
有道理。这当然是一整套策略,非常清晰,我认为克里斯[听不清 00:33:30]以前在开放人工智能,现在我认为他在做自己的事情。但这绝对是一个我也很兴奋的研究项目。现在回到时间线的概念,你是否感到惊讶,首先要记住所有常规的注意事项。2047 是建模的结果,谁知道呢?但就数量级而言,2047 年并不是一辈子的事情。我是说,它就在地平线上。这让你感到惊讶吗?如果是的话,这对你的想法有什么影响?就像我想说的生活优先事项,我的意思是,这个数字意味着很多事情,但我只是想知道你对此的想法?
大卫·罗德曼(00:34:13):
这并没有影响我对生活优先事项的思考。我不期待一个奇点,不管它是什么,26 年。好吧,我要说两件事。第一,我认为它确实帮助我认识到人类系统是不稳定的,我们是人,也许现在很多人在听这个或看这个,你和我我们在生活中经历了相对的稳定。经济和社会方面的情况与几十年前,甚至可以说是 100 年前非常相似。我运行了这个模型的不同版本。在某些情况下,有爆炸式起飞,换句话说,在其他一些情况下,有起飞,然后坠毁。很难让这个系统在我给它制定的规则下保持稳定。所以我认为这是一种重要的见解,我不确定它实际上会导致什么。
大卫·罗德曼(00:35:09):
我会更笼统地说,这是我自己更大努力的一部分,只是为了理解事情。我想你至少和我一样了解人工智能的现状,它是如何工作的,它将走向何方。我想通过这个过程来理解什么是深度学*,例如,了解最*的进展。但是我的感觉是,我现在看到了人类水平的人工智能,无论你如何精确地定义它,比我几年前做的更合理,我的想法已经转向合理。当我成年时,计算机能成为国际象棋大师是很可笑的,对吗?现在反过来会很可笑。所以我不得不调整自己的前科。所以我不认为它像我曾经做的那样疯狂。
Jeremie Harris (00:35:58):
你提到了模型中的崩溃,这也是我没有高兴地注意到的事情。那里发生了什么?我的意思是,当曲线上升,上升,上升,然后突然下降时,你能想到任何解释来解释可能导致这些事件的机制吗?
大卫·罗德曼(00:36:21):
这种观点也可以在 20 世纪 70 年代的著作《增长的极限》中找到。在出现崩溃的变体中,我引入了另一个生产要素,以及劳动力、资本和技术。我增加了自然资源,并加入了这样的假设:经济活动越多,我们消耗的资源就越多。因此,如果经济资源朝着无限的方向爆炸,那么在一微秒内,对不起,[听不清 00:36:54],作为几微秒内经济产出的爆炸,你已经耗尽了所有的自然资源。在模型的假设下,经济将会消亡。没有任何资源根本无法生存。所以它崩溃了。所以这足以表明,至少你可以有一个简单的模型,在整个历史中产生加速增长,但并不能预测未来的正无穷大。
Jeremie Harris (00:37:21):
这让我突然想起了 Nassim Taleb 的《火鸡随时间的快乐》……我想是的。
大卫·罗德曼(00:37:31):
但我要说的是,作为简单数学结构中的预测,有一个假设是,随着世界的变化,人们不会改变他们的行为。相对于经济活动,你只是以同样的速度使用资源。他们不会适应环境,我认为这很不现实。当然,这并不意味着我们不能超调和崩溃,只是真的有可能。
Jeremie Harris (00:37:59):
有意思。所以在你看来,你的直觉显然没有人知道,每个人都在大象的不同部位,但你认为人类的未来是无限的还是崩溃到零,基本上,它是这两者之一,或多或少没有真正的中间地带,这是你的观点吗?
大卫·罗德曼(00:38:23):
不,我知道那篇博文中提到的就是这个意思,因为我一直在研究的那些特别简单的模型就指向这个方向。我的意思是,我会说我会把更多的期望放在中间。我认为我们作为人类会造成很多伤害。我认为我们正在大量调整我们的行为,也许不是为了及时避免实质性的伤害,而是为了及时阻止最坏的结果。
杰里米·哈里斯(00:38:52):
有意思。好吧。我发现我自己,当我看那些曲线的时候,即使我假设曲线本身是一条善的曲线,那也是它自己的问题,对吗?因为我们看着这些数字,我们看着世界生产总值,或 GDP,或其他任何指标,就好像我有一种不安,即使我看到这个数字变得无穷大,这与我认为我自己对恒常性和一致性的原始需求有关,事情不会改变太多。这听起来真的很奇怪,但我发现自己在某种程度上几乎支持崩溃为零的结果,只是因为至少它是有限的,至少我可以想象一个人类不存在的宇宙,而在这个宇宙中,一切都像 AGI 一样疯狂或以任何形式出现。我不知道。我的意思是,在情感层面上,似乎有这种共鸣。
大卫·罗德曼(00:39:51):
不,我同意。我明白你的意思。也许我应该解释一下,我工作中的一件事是,我在这些数学模型中加入了一种叫做随机微积分的东西,这种东西对我来说是新的,常用于股票市场建模。这是一种将随机性引入微积分的方法。因此,不是像你在微积分课上学到的那样有美丽的曲线,而是有整体模式的随机摆动。对我来说,这似乎是一个非常好的模型,一个模拟人类经济史的好方法,因为已经有了一个整体模式,但是有很多起伏,黑死病,实际上还有其他流行病,世界大战等等。
大卫·罗德曼(00:40:38):
这是一件非常复杂的事情,要建立一个随机微积分模型,让它有我想要的整体加速,我必须保持其他一切都非常简单。也许正在观看的人有这样的经历,你正在构建一个复杂的东西,这只是一个成就,让新的想法发挥作用,你不想引入任何其他复杂的东西,直到你让那块工作,对吗?我当时就在那里。在这种程度上,这也是原因之一。博客文章和分析的其他一些方式非常简单,它只预测无限起飞或坠落到零,因为这是我在引入随机微积分的同时可以达到的复杂程度。
杰里米·哈里斯(00:41:18):
是的。我仍然感兴趣的是,它的输出如此一致。我记得当你介绍[听不清 00:41:25]微积分和随机微积分时,你展示了所有这些曲线。它们都有惊人一致的形状,我认为有一个标准,你也看到了,那就是当你达到一定的经济活动水平时,这条曲线一直到无穷大的概率开始快速上升。似乎有一个经济活动的基线水平,我们早就越过了,你就像,现在你真的进入了奇点领域或什么的。你能对这个截止日期说一点吗?你可能已经了解到了什么?
David Roodman (00:42:00):
我开发的模型有一个 it 贬值的概念,这是经济学中的标准概念。这是一种观念,即事物会磨损,人会死亡。所以你要数,两个动力学意图。一件事是当材料以指数速度磨损时,即指数衰减。另一个原因是,随着经济的增长,系统的创新能力增强,从而加速增长。这意味着有一个粗略的界限,如果人类从这个界限以下开始,那么它很可能会灭绝,就像大多数物种一样,对吗?它们永远不会成为人类历史。如果在一个随机模型中,它就在它的上方,它要么可以逃离这个阈值并起飞,要么可以在它周围摇摆很长时间,或者也许你最终会低于它并崩溃,所有的可能性都是可能的。所以这多少透露出了一个事实,那就是,几千年来,在起飞发生之前,人类系统的规模并没有增加多少。
Jeremie Harris (00:43:05):
如果真的发生了,我想,根据你的模型,你有没有一种感觉,大概是哪一年我们跨过了这个门槛,现在几乎不可避免的是,我们将在 2020 年拥有计算机和其他东西?
David Roodman (00:43:21):
我想,在我在博文中关注的图表中,我从公元前 10,000 年,也就是 12,000 年前开始模拟世界历史。据我回忆,大概是 95 at 100 paths 开始于我们在公元前 10,000 年的地方,当时经济规模非常小,它们最终起飞了。
杰瑞米·哈里斯(00:43:42):
哇。
大卫·罗德曼(00:43:43):
即使是当时的模型,最适合的模型。我的意思是,这是一种事后的事情。我们拟合数据的模型,然后模型说你应该得到的数据。它说有 90,根据模型,有 95%的可能性最终起飞。现在,在一些运行中,起飞可能发生在 5000 年后。所以我们只是碰巧在 5000 年后发明了轮子,一切都从那里开始。
Jeremie Harris (00:44:07):
但这似乎暗示着我们的神经硬件在那个阶段就已经存在了。再次,假设模型是正确的,我应该加强你的措辞,这都是建模和低维的东西。但有趣的是,它让你稍微思考一下,我们都是各就各位的成分,以至于当我们偏离进化到黑猩猩的那条线时,甚至可能就在那一刻,事情就发生了。我们总会以 iPhone 告终,这只是时间问题。从这个角度来看,它对我们作为计算的物理意义的描述很有意思。
大卫·罗德曼(00:44:46):
如果你去研究语言的发展,我认为你可以更有说服力地证明这一点,我的意思是,我不是这方面的专家,但据我所知,这似乎可以追溯到 4 万或 5 万年前,这是非常*的事情。我们是大约 500 万年前从黑猩猩中分离出来的。我现在不记得了。因此,原始人,公认的人类物种已经在这个星球上存在了几百万年,仅仅在三四万年前,这种叫做语言的东西才发展起来。从那一点来看,这似乎是不可避免的。
杰瑞米·哈里斯(00:45:26):
我想也有一个缩小的问题,也就是说,我们可以讨论一下,在人类出现的那部分世界中,有多少世界会出现某种奇点,某种长远的发展?但是如果你进一步缩小,我的意思是,我可以想象你会问,这条曲线真的是人类的故事还是猿类的故事?是哺乳动物的故事吗?是生活本身的故事吗?或者更根本的是,它是否告诉了我们一个关于宇宙本身的优化过程的故事,这个过程是由原子、分子、质子和其他物质混合而成的,然后,我的意思是,这显然是完全无法回答的,对我来说是细胞问题,但是宇宙的一部分是什么呢?你会自然地得到一个奇点吗?这是什么奇怪的特征吗?
杰里米·哈里斯(00:46:20):
我的意思是,从阴谋论的角度来说,我在这里听起来可能有点阴谋论,但这是这个宇宙的一个特征,而不是一个缺陷吗?它的设计是为了达到那个目的还是别的什么?只是,这似乎引发了很多有趣的猜测。这也是一个如此简单的模型,这也是我觉得它如此令人兴奋的地方。
大卫·罗德曼(00:46:40):
不,邀请太多。是真的。我倾向于认为,我认为说得好。我倾向于认为生活就是如此。在地球历史上,它开始得如此之快。有一种学术观点认为,实际上智慧生命另一方面并不清楚,也许我们在宇宙中真的是唯一的。这方面的数据点是,地球预计将在大约 8 亿年后变得不适合居住。所以智慧生命在地球上出现了,不管它在哪里,就像它的可居住寿命的 80%。就像我们一直在谈论历史的偶然事件和轮子的发明一样,很容易想象,例如,如果 6500 万年前没有米撞击地球,智慧生命就永远不会在地球上进化。
杰里米·哈里斯(00:47:38):
没错。
大卫·罗德曼(00:47:39):
这是一个不大可能发生的事件,如果一个人勇敢的话,当然可以概括整个宇宙。
Jeremie Harris (00:47:47):
有很多事情似乎必须要发生。这真的很有趣,而且以它自己的方式令人感到谦卑,部分是因为它似乎总是揭示出我们对所有这些前科的完全无知。有这么多,分子是 1,我们不知道分母是什么,类型问题在这里不断出现。现在换个话题,也许是一些更日常的务实的事情,因为我知道还有其他你一直在做的事情,Open Phil 也在做。你在这一领域的一些真正令人兴奋的工作是看一些这样的研究,就像我们在谈话开始时谈到的。从一个怀疑的镜头,我想先从小额信贷说起。
大卫·罗德曼(00:48:25):
哦,好的。
Jeremie Harris (00:48:25):
因为我的嫂子,一个随机的故事,但她来自孟加拉国。她的父亲与穆罕默德·尤努斯一起工作,他是社会信用的发明者,或者说是一个大玩家。所以我结束了对这个的研究,我想,“哦,这太迷人了。”但是后来我读了你的作品,我发现从所有这些不同的角度来看是如此有趣。所以你介意从解释什么是小额信贷开始,然后你的一些分析显示了什么,你正在考虑的一些因素是什么?
大卫·罗德曼(00:49:00):
好的。所以我写了一本名为《尽职调查》的书,我对此颇感自豪。我想人们可能会喜欢它,这是一个对小额信贷的深入调查,小额信贷向穷人提供金融服务,主要是在贫穷国家,并提出这样一个问题,它是否无愧于其作为战胜贫穷的伟大方式的声誉?现代形式的小额信贷,嗯,有小额信贷,这是一种小额信贷,但它实际上主要是从信贷开始的,这是有原因的,商业原因。现代小额信贷出现在 20 世纪 70 年代,通过几个不同的线索,但其中一个非常重要的线索是在孟加拉国,你刚才提到的那个人穆罕默德·尤努斯的工作,他后来因为这项工作获得了诺贝尔奖。他建立了一家名为格拉米银行的银行,规模逐渐扩大,每年向数百万人提供一到两次定期贷款,其中大多数是孟加拉国的贫困妇女。
大卫·罗德曼(00:50:10):
这引起了人们极大的兴奋,因为除了增长速度之外,还有妇女获得贷款、购买奶牛、出售牛奶、从虐待她们的丈夫那里获得经济独立的故事,诸如此类。所以它吸引了很多慈善团体的兴趣。尽管穆罕默德·尤努斯本人希望得到的支持很少。他想成为一家独立的企业。他想要自己的独立,但这变成了一场全球性的运动,并产生了很多兴奋。我认为在某种程度上,这是现在人们对所谓的,有时被称为社会商业的更普遍的兴趣的原因。不同的是社会投资的概念,你可以支持发展中国家的企业像企业一样运作,但帮助需要帮助的人。
大卫·罗德曼(00:50:56):
所以确实出现了一些神话,一些人过度宣传它,超越了证据所显示的效果。所以有这样一种想法,用一句老话来说,小额信贷是对抗贫困的灵丹妙药。
Jeremie Harris (00:51:11):
为了确保我在这里检查我的思维模式,我记得,至少当我听到它向我推销时,我们的想法是这样的,我们倾向于瞄准这些更贫困的社区,特别是妇女,因为他们倾向于更多地考虑社区利益,而不是男性,他们可能平均起来更倾向于冒险,他们的行为不太像社区导向的目标。这公平吗[听不清 00:51:38]?
大卫·罗德曼(00:51:38):
这是一种理论。是的。这是一个关于它的故事,它是否真的是另一个问题,对不对?
杰里米·哈里斯(00:51:42):
是的。
大卫·罗德曼(00:51:42):
是的,你完全正确地反映了围绕它的各种想法。
杰里米·哈里斯(00:51:46):
好的。
大卫·罗德曼(00:51:47):
是的,在孟加拉国和其他许多地方,小额信贷主要是在团体中进行的。所以五个女人会组成一个小组,她们会在同一时间借到相同数量的钱。然后他们每周开一次会,在会上他们必须定期付款,这样做有很多商业原因。这被描述为培养女性之间的团结或利用相互支持,但这也是提高效率的一种方式,因为你只需与你的银行官员开一次会,而不是五次,而且还有公开羞辱的作用。如果你没有带着你的贷款出现,那么其他女人,你不会从会议中被释放。所以你变得尴尬,你增加了还款率。所以现实和表象往往是不同的东西。
大卫·罗德曼(00:52:38):
在同一时间,所以我已经暗示过有一些神话和它的好处被夸大了。我认为这是真的。同时,我认为重要的是要明白,如果你是一个贫穷国家的穷人,这不仅仅意味着你的收入低。这并不像你有一千美元的年薪可以计划。意味着你的收入不稳定,不可预测,对吗?这也意味着你对紧急情况的保险更少,比如你的丈夫摔断了腿,赚不到钱。因此,穷人找到了各种各样的技巧和技术以及非正式的理财方式,在这些非常困难的水域中航行。他们可以互相借贷。他们可能会把自己的存款交给他们信任的人,这样他们就不受他们的控制,钱不在家里,所以丈夫不能花。他们可能会去街角的放债人那里。他们会做各种事情来管理。
大卫·罗德曼(00:53:38):
如果一家不会在一夜之间消失的大型机构走进来,尊敬地对待他们,为他们提供比他们从街角放债人那里得到的更优质的服务,也许这实际上是一件好事。这可能不会改变他们的生活,但我不认为这是一个油嘴滑舌的只是驳回它作为无用的。人们必须谦卑地对待穷人自己为管理他们艰难的生活而做出的决定。因此,这本书得出了一个相当平衡的观点,我认为试图改善穷人可以获得的金融服务并使之制度化的项目是一件好事。任何提供金融服务的尝试都有风险,尤其是在信贷方面。如果你过分热情地分散信贷,你很容易陷入困境。这已经发生在小额信贷领域,就像几年前发生在美国的抵押贷款一样。
大卫·罗德曼(00:54:36):
因此,这场运动越是远离信贷和过度热情的信贷发放,转向小额储蓄和小额保险,我们的境况就越好。我认为,作为慈善家,这可能是一个最好少投入资金的领域,因为当许多急切的投资者想要投入资金时,这将推动系统走向信贷,而这实际上是最危险的地方。
Jeremie Harris (00:55:00):
好的,这真的很有趣,因为即使是我们当前的市场状况也有很多相似之处。当信贷真的很便宜时,人们开始把钱投向风险越来越高的东西,因为这些是唯一能产生任何回报的东西。在某些情况下,你会使整个金融机构向一个方向倾斜,与它应该在的方向正交。你对有利方面的分析是什么?我很好奇,有哪些正面的限制,或者有哪些方面像叙事开始有点崩溃?
大卫·罗德曼(00:55:31):
我的项目的一部分是研究现有的关于小额信贷影响的经济学研究。这让我们回到了数据科学。当我开始这个项目时,还没有关于小额信贷影响的随机研究,这是一种黄金标准。这是我们用来决定一种药物是否安全有效的标准。我们随机地给一些人而不是其他人。小额信贷或其他大多数试图帮助发展中国家穷人的措施都没有做到这一点。但也有一些非研究,你只是去看看有小额信贷的人和没有小额信贷的人,用聪明的方法,试着寻找工具,如果人们知道这些工具是什么,试着在数据中找到自然的实验,并认为你已经在获得小额信贷的人和没有获得小额信贷的人之间做了比较,这实际上给出了对影响的洞察。
David Roodman (00:56:21):
在随机研究出现之前,领先的研究是你之前提到的 Muhammad Yunus 在《科学美国人》中引用的研究,该研究表明小额信贷确实减少了孟加拉国的贫困,特别是当它提供给妇女时。但是数学真的很复杂。我最后花了很多时间写我自己的代码来实现基于数学的方法,最大似然法,然后和乔纳森·莫多克一起工作,重新运行最初的研究,最终发现各种各样的问题,并得出结论,这是最初的发现,不准确,可能更好的影响估计是零。
杰瑞米·哈里斯(00:57:03):
哇。
大卫·罗德曼(00:57:04):【00】随机研究也指出了同样的问题,也就是说,如果看整体贫困水平,它似乎并没有真的发生变化,但有一些证据表明,人们能够管理好自己的经济生活,不那么经常挨饿。例如,他们可以通过使用[听不见的 00:57:21]来稳定局势。
耶雷米·哈里斯(00:57:21):
难以衡量的事物。没错。
大卫·鲁德曼(00:57:23):
没错。
Jeremie Harris (00:57:24):
有意思。我的意思是,这类问题有一个迷人的地方。这是一个你已经探索了这么多不同的方式。你有一大堆不同的项目,你要深入挖掘其中一些比较成熟的,可以称之为共识的想法,然后看看它们是否经得起推敲。在很多情况下,你提到了复杂性。这似乎是问题的一个重要组成部分,因为当你遇到一个非常复杂的问题时,如果有人在听,而且他正在为数据科学做实际的模型构建,人们就会知道,通过优化超参数,你能移动多少针,你能过度拟合多少线。实际情况似乎是,人们最终会对着不同的模型大喊大叫,而不是真的去看数据或模型之间的不确定性。在这种情况下,你如何培养信心?此外,实际上,如果你不能培养信心,在这种不确定性的环境下,你怎么打电话呢?
大卫·鲁德曼(00:58:25):
对。我认为经济学在这方面有很多困难,在某些方面可能比其他领域取得了更多进展,尽管还有很多工作要做。一种解释是,这些未必能真正转化为最基本的数据科学原理。随机化、随机化实验的吸引力之一,不仅在于它们明显引人注目,还在于它们在数学上很简单。你可以控制治疗组,观察治疗组、会议组和对照组的均值,然后做一些基本统计。当然,就像所有事情一样,你可以开始让它变得复杂,而它们确实变得复杂。我不知道这是否能转化为一般的数据科学。
David Roodman (00:59:05):
我们已经采取的另一个重要步骤(也是逐步发展的)是提高研究的透明度。所以我说,当我以这种方式运行结果时,我得到了这个。人们越来越期望我把代码和数据放到网上。
耶雷米·哈里斯(00:59:18):
没错。
大卫·罗德曼(00:59:19):
这让人们可以挑战我。这也可能会改变我的行为,并阻止我过多地调整超参数,因为我知道有人会发现这是关键,或者他们得到的结果可能会让我发表。所以我认为我们需要在其他社会科学中看到更多这样的例子。这是一场真正的心理学危机,可以说是由研究缺乏透明度造成的。机器学*可能会越来越有用武之地。我不确定。
Jeremie Harris (00:59:49):
这一切似乎总是可能适得其反,因为这些超参数、开放的自由度几乎使一切都有可能在某种程度上变得政治化,人们有他们预先存在的观念,他们会说:“好吧,我的意思是,我可以根据数据论证任何事情,所以我会这样做。”
大卫·罗德曼(01:00:08):
对。另一个创新,对不起,我认为,我的意思是,我说经济学已经取得了进步,可能医学领先于经济学。另一个创新是预先注册,在第三方网站上发布你的分析计划,对吗?这给了它可信度,他们将保留完整的历史,有点像 GitHub,完整的分析计划的历史。你可以改变计划。你可以做你没有计划的分析,但是每个人都很清楚什么是预先准备的,什么是临时做的。
Jeremie Harris (01:00:45):
在确认事情和进行我们几乎知道答案的研究(因为我们可以确定它们)之间,似乎有一种微妙的舞蹈,然后在进行研究时,我们似乎学到了比我们应该学到的更多,而不是比我们应该学到的更多,但是我们正在探索一个太未探索的空间,在那里我们学到的任何东西都可能太适合,没有足够的东西可以咀嚼。我只是想起了光谱的另一端,我之前在播客中提到过一次,但当我在物理学院学*时,我们会进行实验,我们会从知道实验结果开始。
Jeremie Harris (01:01:24):
我的意思是,这是默认的,对吗?在物理实验室里,你应该预期违反薛定谔方程或者广义相对论的次数,我的意思是,基本上是零。一个有趣的结果是,你实际上有旋钮,你可以在你的实验装置上移动,你自然会说,“看,我得到一个不符合牛顿和爱因斯坦的结果的先验概率是多少,等等。应该是零。”所以你发现自己在做的事情是,“哦,伙计,我不是在确认我的前科。好吧。我需要调整这些旋钮。”
Jeremie Harris (01:01:57):
你基本上是过度适应了,直到你确认了你一直认为你知道的一切,并且以一种奇怪的方式在一个可能不稳定的信念基础上建立了更多的信任。如果人们敢于接受某种带外实验,我想知道在这个空间中是否也有某种类似的自我延续的动力,我想,问这个问题的一种方式是,你多久会发现自己挑战一项工作,不仅仅是一篇单独的论文,而是一大堆似乎遵循一致叙事的研究,甚至从似乎错误的数据中获得一致的结果。
大卫·罗德曼(01:02:37):
这在经济学中可能是不同的,因为我们没有任何人相信的法律。这是供求规律,而且到目前为止已经有了。我的意思是,我应该解释一下,我职业生涯中的很多事情都是在这方面,而不是计划好的,这是我偶然发现的,是我在小额信贷研究中描述的那种模式,在那里我遇到了一篇文献,它谈到了我决定调查的一个问题,比如让更多的人进监狱会减少犯罪吗,对吗?还是给穷人小额信贷让他们过得更好?我觉得,如果我要成为这些问题的专家,那么我需要真正理解这些研究。当其中存在争议或复杂性时,我经常感到有必要亲自重新进行研究,获取数据或重建数据,运行方法等等。
大卫·罗德曼(01:03:33):
我没有高等学位。人们以为我是经济学家,但我不是。所以我是这个领域的局外人,也许这是我走这条路的部分原因。我只是世界上最讨厌的研究消费者。所以我的模式经常是看着一项研究说,“这看起来很好,但也许我应该这样检查或运行它。”在这项研究中,有一半的时间我最终不相信最初的说法。我不认为这是因为他们只是试图符合一些众所周知的经济负荷往往是,如果你得到一个非零的结果,那就更容易发表,如果你是一个大三学生,你正在寻找终身职位,你会特别被吸引去寻找那些非零的结果。方法论的成熟会有回报,但往往会适得其反,我发现有很多力量在起作用。
Jeremie Harris (01:04:27):
你怎么看,因为这似乎暗示了一些非常令人不安的事情,关于我们认为什么是专业知识,我们认为什么和我们认为谁是专家。我想这才是重点。世界就是这么复杂,对吧?我的意思是,我们需要将这些难以置信的复杂问题的降维工作外包给专业人士。但是,如果我们确定专家的过程是同行评议,这似乎非常有效。我并不指望你有什么解决办法,因为这可能是人类对世界的理解中最大的未决问题。但是从一个有意义的角度来看,你怎么看这件事?你认为我们自然会进化出更好的系统,还是你认为,我不知道,你认为呢?
大卫·罗德曼(01:05:13):
不,是真的。我们有社会各方面的专家,工程学、犯罪学等等。所有的,几乎所有的这些专家都在人类社区中工作,我们知道这创造了各种各样奇怪的激励,因为你担心冒犯你的同行,获得终身职位,出版,以及许多其他事情。而且有一种倾向是遵从传统智慧。但说出别人没说过的话也会有回报。所以我认为这些知识群体会随着时间的推移进行自我修正,但速度还不够快。我的意思是,我刚刚描述了经济学中正在发生的一些变化,我认为这些变化正在慢慢蔓延到其他领域。但是也有一些现实的问题。我想知道学术出版能做的事情是否是有限的。
大卫·罗德曼(01:06:00):
更具体地说,我很震惊,所以我带着这个问题,我刚才向你们描述的分析风格,我把它带到了监禁和犯罪的问题上,作为对拨款进行批判性思考的一部分,我们正在围绕刑事司法改革开展开放的慈善事业。最后,我被震撼了,我能够重新运行大约八个核心研究,其中四个我想我只是拆开了。
Jeremie Harris (01:06:27):
哇。
大卫·罗德曼(01:06:27):
我只是认为他们错了。其中一个是正确的,它是如此的明显正确,它不是一个随机的实验,但是它是如此的接*,以至于你只要看看图表就知道它是正确的,讨论结束。所以我意识到这些研究,至少它们的目标是影响数百万人的生活,数十亿美元的支出的决策。他们接受了同行评审,你可能会认为价值 1000 美元,看看经济学家投入评审的时间价值。从社会的角度来看,这不可能是最佳的,我们花费 1000 美元来审查具有数十亿美元影响的研究。我不认为学术出版商能解决这个问题。我甚至不确定学术界能直接解决多少问题,如果这意味着进行更彻底的审查的话。不确定他们有没有钱或者动机。因此,这可能需要政府和慈善家对与政策相关的研究进行更系统的深入审查。
Jeremie Harris (01:07:33):
这真的很有趣。我认为你的一句话突出了整件事。我的意思是,我们如何花 1000 美元来审查一个 20 亿美元的想法或什么的?哇哦。嗯,你这么想…我真的很感激。我知道我们实际上有点超时了,所以我将放弃这里的统治,但非常感谢你在这方面的时间,大卫。这是非常有趣的对话。我会为任何感兴趣的人做标记,这本书是尽职调查。所以,如果你想了解一下,我们会在播客中加入一个链接,抱歉,会在播客的博文中加入。大卫,非常感谢。你有没有一个网站,人们可以去,跟踪你的工作?
大卫·罗德曼(01:08:10):【davidroodman.com,我确实有一个网站。我没有那么积极地维护它,但如果你想知道我是谁,这是一个基础。
杰里米·哈里斯(01:08:17):
太好了。好吧。我们也会链接到那里,这样人们就可以看到了。谢了。我真的很感谢这次聊天。这很有趣。
大卫·罗德曼(01:08:22):
这真是我的荣幸。
过去 20 年印度尼西亚地震发生的 EDA 和可视化
使用印度尼西亚地区过去 20 年的地震活动数据,了解每年发生地震的频率以及最大的地震。
何塞·安东尼奥·加列戈·巴斯克斯在 Unsplash 上拍摄的照片
简介
印度尼西亚是一个自然灾害频发的国家,如火山爆发、森林火灾、洪水,尤其是地震。每天,我都会收到来自我手机的气象、气候和地球物理机构应用程序的地震通知。不止一个,有时一天会出现两到三个通知,通知我印度尼西亚的一个地区发生了地震。这启发了我去创建一个 EDA(探索性数据分析),来分析在过去的 20 年里印度尼西亚地区发生了多少次地震。
地震被认为是最具破坏性的自然灾害之一,能够在没有任何警告的情况下在瞬间摧毁附*的城镇。构造地震是由地壳运动引起的自然现象之一。构造地震是最常见的地震类型[1]。地震能量可以作为地震震级来记录和测量。震级可以用地震仪记录,输出的地震能量用里氏震级表示[2]。
印度尼西亚群岛位于太平洋、欧亚和印度-澳大利亚板块碰撞的构造带上。因为印尼处于复杂构造带的中心,几乎每天都会发生三次以上的地震[3]。根据美国地质调查局(USGS)网站的数据,90 %的地震,包括最大的地震,都发生在火环地区[4]。令人惊讶的是,印尼位于火环和复杂构造带之上。
EDA 和数据可视化可以用来可视化地震发生的次数。在本文中,我将使用 R 编程语言来执行 EDA 和可视化数据,以获得特定的信息。数据集是从美国地质调查局网站收集的,该网站收集了世界各地发生的地震数据。我们将使用 2000 年到 2020 年的地震数据。
EDA 和可视化
在开始编码部分之前,我们必须导入一个我们稍后将使用的特定库。
#Importing specific library
library(dplyr)
library(tidyr)
library(lubridate)
library(ggplot2)
library(plotly)#Load the dataset
df_eq1 <- read.csv(“~/Rstudio/Projects/EDA Earthquake/Dataset/Raw/EQ 2000–2004.csv”)
df_eq2 <- read.csv(“~/Rstudio/Projects/EDA Earthquake/Dataset/Raw/EQ 2005–2010.csv”)
df_eq3 <- read.csv(“~/Rstudio/Projects/EDA Earthquake/Dataset/Raw/EQ 2011–2015.csv”)
df_eq4 <- read.csv(“~/Rstudio/Projects/EDA Earthquake/Dataset/Raw/EQ 2016–2020.csv”)#Combine the dataset
df_eq <- rbind(df_eq4,df_eq3,df_eq2,df_eq1)
df_eq
数据集中的大部分数据来自东盟国家记录的地震。我们必须指定印度尼西亚领土的坐标,以便数据只能填充发生在印度尼西亚地区的地震。
#Specified earthquake’s coordinate happened in Indonesia territory
eq_INA = df_eq %>%
filter(longitude>=93 & longitude<=141.25,latitude>=-15 & latitude<=9)
eq_INA
在我们指定数据之后,我们必须根据震级对地震数据进行分类。
根据里氏震级,地震分为八类[6]。
- 震级小于 2:微地震
- 2 至 3.9 级:轻微地震
- 4 至 4.9 级:轻微地震
- 5 至 5.9 级:中等地震
- 6 至 6.9 级:强烈地震
- 7 至 7.9 级:大地震
- 8 至 9.9 级:大地震
- 震级超过 10 级:史诗级地震
这一分析只包括震级大于里氏 2 级的地震。
#Classifying earthquake based on magnitude richter scaleeq_INA = eq_INA %>%
mutate(mag_class = factor(ifelse(mag>=2 & mag<=4,”minor”,ifelse(mag>=4 & mag<=5,”light”,ifelse(mag>=5 & mag<=6,”moderate”,ifelse(mag>=6 & mag<=7,”strong”,ifelse(mag>=7 & mag<=8,”major”,”great”)))))))
现在我们有了合适的数据框架,我们开始根据震级和年份来计算地震的数量。
eq_INA %>%
group_by(mag_class) %>%
summarise(number_of_earthquakes = n()) %>%
ggplot(aes(x = mag_class, y = number_of_earthquakes)) +
geom_bar(stat = ‘identity’, fill = “red”) + geom_label(aes(label = number_of_earthquakes)) + labs(title = ‘Earthquake distribution based on magnitude classes’,
subtitle = ‘Huge increased on the number of earthquake occurred within the smaller magnitude classes.’,
caption = “The dataset contains list of recorded earthquake in Indonesia from year 2000–2020.”,
x = ‘magnitude classes’,
y = ‘Number of earthquakes’)
图一。基于震级等级的地震分布。
根据图 1 的条形图。从 2000 年到 2020 年,印度尼西亚境内发生了超过 4245 次小地震、38092 次小地震和 4163 次中等地震。最关注的是,强震 319 次,大震 42 次,大震 4 次。几乎可以肯定的是,强地震以上的震级将对附*的城镇造成重大破坏。为了获得更多关于地震发生时间的具体数据,我们需要收集更多关于每年发生的地震的信息。
eq_INA %>%
group_by(year) %>%
summarise(number_of_earthquakes = n()) %>%
ggplot(aes(x = year, y = number_of_earthquakes)) +
geom_bar(stat = ‘identity’, fill =”blue”) + geom_label(aes(label = number_of_earthquakes)) +
labs(title = ‘Earthquake distribution based on number of earthquakes every year’,
subtitle = ‘Huge number of earthquakes has been spotted within time period of year 2005–2007.’,
caption = “The dataset contains list of recorded earthquake in Indonesia from year 2000–2020.”,
x = ‘Year’,
y = ‘Number of earthquakes’)
图 2 按年份顺序的地震分布。
图 2 的条形图。以上清楚地表明,印度尼西亚每年发生 1000 多次地震。2005 年地震次数也显著增加,仅这一年就发生了 5110 多次地震。2005 年的地震是 2004 年记录的地震数量的两倍。
这很有意思。
为什么会出现这种现象的一个假设是,在大地震的余震之前,小地震的数量增加了。我们将使用散点图根据震级和时间段收集更多关于地震分布的信息。
eq_INA %>%
ggplot(aes(x = date, y = mag)) +
geom_point() + labs(title = ‘Scatter plot of earthquake distribution based on magnitude scale from 2000–2020’,
subtitle = ‘There has been spotted 4 great earthquake within time period of year 2004, 2005, 2007, and 2012.’,
caption = “The dataset contains list of recorded earthquake in Indonesia from year 2000–2020.”,
x = ‘Year’,
y = ‘Magnitude scale’)
图 3 2000-2020 年基于震级的地震分布散点图。
图 3 中的散点图。上面显示了 2004 年至 2007 年间发生的三次大地震。我们将主要关注这一现象。
据媒体消息,2004 年 12 月发生了里氏 9.1 级的苏门答腊-安达曼地震。这次地震发生在一个构造俯冲带上,作为巽他板块的一部分,印度板块俯冲到缅甸微板块之下[7]。
继 2004 年 12 月的大地震之后,下一次大地震发生在 4 个月后的 2005 年 3 月。这次地震发生在 2004 年 12 月 8.6 级大地震之前。
第三次大地震发生在 2007 年 9 月,同一天发生了一次 8.4 级的大地震和一次 7.9 级的大地震[9]。
我们认为这次大地震是地震数量大幅增加的主要原因。此外,我们将把调查的重点放在 2004 年至 2007 年,这几年主要发生了大地震。
eq_INA %>%
filter(year==2004 | year==2005 | year==2006 | year==2007) %>%
group_by(mag_class, year) %>%
summarise(number_of_earthquakes = n()) %>%
ggplot(aes(x = mag_class, y = number_of_earthquakes)) +
geom_bar(stat = ‘identity’, fill =”forest green”) + geom_label(aes(label = number_of_earthquakes)) + facet_wrap(~year, ncol=1, strip.position = “left”) +
labs(title = ‘Earthquake distribution based on magnitude class in time period of 2004–2007’,
subtitle = ‘Huge increased on the number of minor earthquake occured in 2005, doubled the last year.’,
caption = “The dataset contains list of recorded earthquake in Indonesia from year 2004–2007.”,
x = ‘magnitude classes’,
y = ‘Number of earthquakes’)
图 4。2004-2007 年期间基于震级等级的地震分布。
从图 4 的条形图中可以看出。上图中,2004 年 12 月地震前的大地震和 2005 年 3 月的第二次大地震导致 2005 年轻震显著增加。为了获得更多关于地震月份分布的信息,我们需要查看从 2004 年到 2007 年每个月发生的地震数量。
eq_INA %>%
filter(year==2004 | year==2005 | year==2006 | year==2007) %>%
group_by(month, year) %>%
summarise(number_of_earthquakes = n()) %>%
ggplot(aes(x = month, y = number_of_earthquakes)) +
geom_bar(stat = ‘identity’, fill =”forest green”) + geom_label(aes(label = number_of_earthquakes)) + facet_wrap(~year, ncol=1, strip.position = “left”) +
labs(title = ‘Earthquake distribution based on magnitude class each month in 2004–2007’,
subtitle = ‘Huge increase on the number of earthquake occured prior to the great earthquake.’,
caption = “The dataset contains list of recorded earthquake in Indonesia from year 2004–2007.”,
x = ‘Month’,
y = ‘Number of earthquakes’)
图五。2004-2007 年基于震级等级的每月地震分布。
通过查看图 5 中的柱状图,我们可以了解到从 2004 年到 2007 年每个月地震数量的增加和减少。2004 年 12 月至 2005 年 4 月期间,发生了大量地震。这些地震发生在 2004 年 9.1 级大地震之前,那次地震导致了接下来一个月的小地震。
在 2005 年 3 月第二次大地震之前,地震次数也有所增加。我们先前关于大地震导致小地震数量增加的假设似乎是正确的。根据 2004 年 12 月至 2005 年 4 月的地震数据,一个月前发生的大地震导致了接下来一个月地震次数的显著增加。
同样的现象也发生在 2007 年 9 月第三次大地震的时候。然而,在这段时间内,地震的数量并没有像上次大地震那样迅速增加。
相对于之前每年每个月的地震次数,我们还会探究地震的震级和深度之间的数据。下面的散点图将显示发生地震的震级和深度之间的关系。
#Relationship between mag and depth
eq_INA %>%
ggplot(aes(x = mag, y = depth)) +
geom_point() +
geom_smooth(method = ‘lm’, se = FALSE) +
facet_wrap(~year) + labs(title = ‘Scatter plot of earthquake distribution based on magnitude and depth every year’,
subtitle = ‘The blue line indicated that the greater value of magnitude, the smaller value of the depth.’,
caption = “The dataset contains list of recorded earthquake in Indonesia from year 2000–2020.”,
x = ‘Magnitude’,
y = ‘Depth’)
图六。基于每年震级和深度的地震分布散点图。
mag 和深度之间的关系是相反的,如上面图 6 的大部分散点图所示。意思是震级越大,深度越小。这次大地震很可能发生在浅层。这是正确的,因为之前的大地震发生在地球表面以下不到 50 公里的深度。
结论
1.从 2000 年到 2020 年,印度尼西亚境内发生了 4245 次小地震、38092 次小地震和 4163 次中等地震。尽管如此,最显著的地震还是大地震,大地震发生了 52 次,大地震发生了 4 次。
2.每年,印度尼西亚都会发生 1000 多次不同震级的地震,从小地震到大地震不等。
3.2005 年发生的大量地震是由前一年 2004 年 12 月和 2005 年 3 月大地震的余震造成的。由于两次大地震,2005 年的地震数量比前一年增加了一倍,尤其是较小的地震。
4.里氏震级较大的地震发生在较浅的深度。
来源
你可以从这里获得源代码和地震数据。
参考
[1]阿达古诺多等人。,1966-2015(2017)年亚非地区 0 < M < 8 地震数据集评估,数据简介
[2] S. A. Greenhalgh 和 R. T. Parham,南澳大利亚里氏震级 (2007 年),《澳大利亚地球科学杂志》
[3] K .普里巴迪等人。,《从过去的地震灾害中吸取教训:加强印度尼西亚基础设施抗灾能力的知识管理系统需求》 (2021),《国际减少灾害风险杂志》
[4] R. Senduk,Indwiarti 和 F. Nhita,使用 K-Medoids 算法对印度尼西亚地震易发区进行聚类 (2019),印度尼西亚计算杂志
https://earthquake.usgs.gov/earthquakes/search/
[6]https://www . GNS . CRI . NZ/Home/Learning/Science-Topics/地震/监测-地震/其他-地震-问题/里氏震级是多少
[8]https://ReliefWeb . int/report/Indonesia/Indonesia-3 月 28 日-地震-情况-报告-9
[9]https://ReliefWeb . int/map/Indonesia/m85-和-79-南苏门答腊-地震-2007 年 9 月 12 日和-m70-13-200 年 9 月
EDA:提高文本分类性能的简单数据扩充技术
研究论文解释
作者图片
机器学*中的数据扩充是一种流行的技术,即使在数据可用性较低的情况下,也能使稳健并推广 ML 模型。通过添加对现有数据稍加修改的副本或从现有数据中新创建的合成数据,它有助于增加原始数据的数量。当在低质量和大小的数据上训练机器学*模型时,添加各种数据大大有助于减少过度拟合。
人们已经看到它在计算机视觉应用中工作得非常好,如图像分类、对象检测等——在这些应用中,我们现在已经有了一套变换函数,如旋转、剪切、裁剪等,这些函数很容易获得、研究过并已知可以工作(当然,很少需要注意)。来自 Protago Labs Research、Dartmouth College 和 Georgetown University 的这篇论文介绍了自然语言处理中的数据扩充转换功能,特别关注文本分类任务。
有了这个背景和介绍,让我们直接开始研究提出的方法。
提议的转换功能
- 同义词替换(SR) —从句子中随机选择 n 个不是停用词的单词。用随机选择的同义词替换这些单词。
- 随机插入(RI) —在句子中找到一个非停用词的随机词的随机同义词。将同义词插入句子中任意位置。这样做 n 次。
- 随机互换(RS) —随机选择句子中的两个单词,互换它们的位置。这样做 n 次。
- 随机删除(RD) —以概率 p 随机删除句子中的每个单词。
作者选择噪音感应比作为句子长度的函数。假设是短文档不太容易受到处理噪声的影响,并且转换可能会导致类的可变性。其中 as,长句可以吸收更多的噪音,同时保持原来的类标签。他们在一个句子中调整的字数定义为n =αL;其中,L 为单词级别的句子长度,α为参数,表示句子中需要改变的单词的百分比。他们摆弄α值{0.05,0.1,0.2,0.3,0.4,0.5}。下图显示了同样的情况—
不同规模数据集的α-变化。图片来自来源
从图中可以清楚地看出,平均而言,随着要处理的单词百分比的增加,性能增益通常会下降一定的量,而与要增加的数据量无关。
实验和结果
他们选择了五个基准文本分类任务来评估所提出的系统—
- SST-2 : 斯坦福情感 Treebank v2 (SST2) 斯坦福数据集拥有 Moview 评论和相关情感。参考文件
- CR :关于客户评论和相关标签的数据集。参考文件
- subject:主观性/客观性带有相关情感的数据集。参考论文
- TREC : 问题类型分类定义了粗粒度和细粒度类别的数据集。参考文献
- PC : Pro-Con 是来自网络上用户生成内容的情感分析目标数据集。参考文件
作者对随机采样的不同大小的训练数据集进行实验,使用 {500、2,000、5,000,所有可用数据}样本,因为他们假设他们的方法对较小的数据集更有帮助。
他们选择 RNNs 和 CNNs 作为他们选择有无 EDA 的分类模型。
使用和不使用 EDA 的模型在五个文本分类任务中的平均性能(%)—图片来源
从上面的结果表中我们可以看出,与处理完整数据集(0.8%)相比,小数据集(3.0%)的增益相对较高。这是可能的,因为更少的数据意味着有限的模式和信息,这就是为什么增强在这种情况下会有更好的帮助,而不是当您使用大规模数据集进行训练时,其中已经有足够的模式和信息供模型学*。因此,在那个级别执行增强实际上不会提高性能数字。
选择用于增强的数据集的百分比及其对准确性的影响。图片来自来源
我们上面讨论的类似模式也可以在单独的数据集上看到。参考上图。
放大多少?
下一个重要的问题是,每个句子要生成多少个句子(naug)。如下图所示,作者对不同的增大尺寸值进行了试验。
不同数据大小的 naug 变量。图片来自来源
在较小的训练数据的情况下,更多的增加可能会导致过度拟合,从而大大提高性能。其中,对于较大的训练规模,具有较高的 naug 值似乎不像预期的那样工作,这仅仅是因为模型已经可以访问大量数据以进行适当的概括。经过大量实验后,他们推荐以下参数—
EDA 的推荐参数。图片来自来源
我有一个同样的多语种字幕视频漫游,如果你喜欢看视频而不是文字(就像我:D 一样),一定要看看
如果你仍然对某件事感到困惑,一定要看报纸。另外,向作者问好,感谢他们的贡献。
论文标题: EDA:提升文本分类任务性能的简易数据增强技术
论文链接:【https://arxiv.org/abs/1901.11196】T21
代码链接 : [http://github。com/jasonwei 20/EDA _ NLP](http://github. com/jasonwei20/eda_nlp)
希望这本书值得你花时间去读!与你的朋友分享给对这些内容感兴趣的人。
谢谢。
EDA 打造更光明的数据文化
这不仅仅是模型开发的一个步骤,它是常年的文档
探索旧数据,找到光明。约书亚·索蒂诺在 Unsplash 上拍摄的照片
探索性数据分析(EDA)是对数据集的第一次观察:一种理解变量、变量分布及其关系的方法。这应该是彻底的数据科学项目的第一步,也是一本易于理解数据领域的手册。
问题是,EDA 通常是一次性的交易:创建报告并汇编我们的见解,就是这样。我们这样做是为了一个短暂的目的,成为许多笔记本中的一个,最终成为一个丢失的存储库,一个项目管理工具中的冲刺卡。
那么,我们怎样才能从我们的探索中挤出最大的影响呢?答案不在于技术细节,而在于你的抽象的转变:EDA 是的入职和文档。
总线因素对数据的影响更大
对该公司来说,数据科学家最重要的资产不是他们的建模能力或工具包:而是他们的领域知识。他们应该知道他们所管理的所有数据集的复杂性,也是最有资格对这些信息的使用提出见解的人。
通过每天处理许多特定于公司的数据集,数据科学家开始聚集特定于领域的见解,这些见解不是原始代码工件、人工智能建模或架构:他们开始理解变量之间的联系、特征选择输出背后的推理、所有表之间的连接点。
当一名数据科学家离开团队,而该知识没有得到充分传播时,企业的数据文化就会受到削弱。这是一个难以填补的结构性空白,在团队恢复旧的速度之前,引发了重大的重构工作。
这是 IT 所有领域的一个痛苦的现实:关键人员的离开和他们编写的所有东西都变得不可触及的故事可以从后端到前端到处流传。前人留下的微不足道的文件变成了被所有人崇敬的古老的真理卷轴。
https://www.eduflow.com/blog/institutional-knowledge-why-it-matters-and-how-to-tap-into-it https://hrdailyadvisor.blr.com/2018/07/18/knowledge-loss-turnover-means-losing-employees/
但是,虽然可以通过代码文档层和过程描述减轻其他领域的损害,但数据科学中丢失的领域知识不容易恢复。
我知道这个数据集是偏斜的,这需要在那个特定异常值检测之前这个预处理函数…但是我如何将这个诀窍传递下去呢?
分析师(和科学家)的基本文档
通常包含数据本身所有秘密的文档是探索性数据分析(EDA) 的结果。它是对数据集的结构化统计调查,其中数据科学家分析数据的完整性,发现变量之间的关系,并在教学图和图表中总结收集的见解。下面的文章有一个 EDA 报告通常包含什么的实际例子。
周围的大多数文章和书籍都会告诉你,探索数据集是数据科学模型开发周期中的一个重要步骤。这当然很重要,但不应该只是一个步骤!EDA 报告包含的信息对于保持公司的数据文化活力和活力非常宝贵,并让新员工快速了解任何数据集的所有复杂性。它可以用于:
- 解释现有模型如何工作;
- 作为新预测模型创建的基础;
- 作为数据分析师的参考点。
因此,您需要做的就是将数据科学项目中的任何报告重新用作文档来源。虽然从管理的角度来看,这看起来很容易,但是在你的公司内吸收这种做法是非常困难的。
呈现并记录
制作文档既辛苦又无聊。当你的云提供商出现问题或者你的 sprint 目标需要一些补充时,你就要做这项工作。对于任何开发团队的持续速度来说,这也是绝对必要的。
数据科学文档非常难,因为它涉及数学和统计学。将数据洞察放入连贯的单词串是一门艺术,因此在我们的实际编程世界中非常少见。
但是有一件事我们已经*惯了,那就是演讲。数据科学家的工作通常包括向外行人解释技术术语,从销售、管理,直接到客户…这使得演示成为数据科学团队日常生活的一部分。
然后,让我们添加另一个表示:数据集表示。每当您的公司中出现新的数据集或新的数据域时,就将获取该数据集的程序员和数据团队召集在一起,制作一个小型演示文稿,将数据的来源、最终更新背后的架构、对该数据的业务兴趣以及总体统计结构混合在一起。
你为什么把采购和统计研究混在一起?这是我基于经验的个人选择:当收购和分析不说话时,管道图就会变得模糊。如果有一个你不想打破的联系,那就是你分析的根源。
录制演示文稿,并将其传播到组织中每个相关的内部 Wiki 页面。将它设置为入职复选框,这样参与流程的每个人都必须至少看过一次。并且总是将视频推荐给对公司的整个建模部分感兴趣的人!
文化很抽象。在你的团队里很难保持知识和流程的活力,更别说在大公司了。培养*惯的唯一方法是通过纯粹的意志力和重复,我相信使用 EDA 来保持数据洞察力是一种有效的策略,可以帮助您的团队走得更快,更有弹性!
一行代码中的 EDA
使用 Sweetviz 轻松探索数据集
在开始或为机器学*模型准备数据集之前探索数据集非常重要,因为我们应该知道数据在说明什么,以及所有功能和目标列都有什么。通常,我们首先找出不同列之间的关系,创建不同类型的可视化,在数据中寻找模式,等等。
根据一项研究,探索性数据分析消耗大约 40%的项目总时间,如果我说它可以减少到 10%呢?这意味着现在您可以更加专注于构建一个强大且高度准确的机器/深度学*模型,而不必在 EDA 中浪费太多时间。
Sweetviz 是一个开源 python 库,只需一行代码即可创建 EDA 报告。是的,你没看错,只用一行代码,你就可以分析数据,可视化数据集中不同列的模式和关联。它是免费提供的,非常容易使用。在本文中,我将向您展示如何使用 Sweetviz 利用数据创造奇迹。
让我们开始吧…
安装所需的库
像任何其他 python 库一样,我们将使用 pip 安装 sweetviz。下面给出的命令将安装它。
pip install sweetviz
导入所需的库
我们将使用 pandas 加载数据集,使用 sweetviz 创建 EDA 报告。下面的命令将导入这些库。
import pandas as pd
import sweetviz as sv
加载数据集
对于本文,我使用著名的糖尿病数据集,您可以使用 Kaggle 下载该数据集。
df = pd.read_csv('Diabetes.csv')
df
数据集(来源:作者
创建 EDA 报告
这是最后一步,我们将用一行代码创建 EDA 报告。该报告将包含关联、特征分析、不同列中的数据分布等。让魔法开始吧…
#Creating the dataset
advert_report = sv.analyze(df)#display the report
advert_report.show_html('Diabetes_report.html')
报告(来源:作者
在这里,您可以看到报告的主页,其中提供了数据集的基本信息,包括要素的数量及其数据类型。在下面的视频中,您可以直观地看到该报告中的不同部分,并在您的分析中使用它们。
Sweetviz 最棒的地方在于它创建了 HTML 格式的报告,您可以与您的团队分享这些报告。它有助于您的演示,并具有理解数据集基础的所有必要信息。
继续尝试不同的数据集,并让我知道您在回复部分的评论。
这篇文章是与 Piyush Ingale 合作的
在你走之前
感谢 的阅读!如果你想与我取得联系,请随时通过 hmix13@gmail.com 联系我或我的 LinkedIn 个人资料 。可以查看我的Github*简介针对不同的数据科学项目和包教程。还有,随意探索* 我的简介 ,阅读我写过的与数据科学相关的不同文章。
用机器学*——计算摄影——编辑你的老照片
使用最智能的 OpenCV 图像降噪算法之一为您的照片降噪
本文将向您展示如何使用训练有素的机器学*模型来降低照片中的噪声。照片去噪也称为图像降噪。在这个项目中,我们将使用一个智能降噪算法对一个嘈杂的图像。降噪可以用来让我们的老照片看起来更好。
在嘈杂的图像中,我们可以看到像素和不想要的细节。减少噪声会使图像更加平滑。我们都知道几年前手机拍不出好照片。许多手机品牌开始在其软件中使用这种降噪技术,为用户提供更好的效果。这项技术被称为计算摄影。你可能在苹果去年 10 月的活动中听说过这个术语。我们的手机可以做到这一点;所以为什么不建造我们自己的呢?
在这个项目中,我们将看到如何将编程和机器学*用于摄影和艺术。作为一名艺术爱好者,我迫不及待地与您分享这个项目。我们开始工作吧!
目录表:
- 简介
- 步骤 1 —库
- 步骤 2 —读取图像
- 第三步——非局部均值去噪算法
- 最后一步——降噪功能在起作用
介绍
我们将在这个项目中使用三个包。软件包如下:OpenCV、NumPy 和 Matplotlib。OpenCV 是一个非常著名的计算机视觉软件包。我们需要 Numpy 作为 OpenCV 的先决条件。当读取图像时,我们将这些像素转换成数组;这是在幕后发生的。NumPy 在处理多维数组时非常有用。
以下是 OpenCV 包的简短定义:
OpenCV(开源计算机视觉库)是一个开源的计算机视觉和机器学*软件库。OpenCV 旨在为计算机视觉应用提供一个公共基础设施,并加速机器感知在商业产品中的应用。
我们将在这个项目中使用的每个包的官方文档页面。当您想解决程序中的一个错误或向程序中添加更多功能时,它可能是一个有用的参考:
步骤 1 —库
正如在介绍中提到的,我们将在这个项目中需要三个包。为了能够在我们的程序中使用它们,首先我们必须安装它们。我们将使用 PIP,它是一个 python 包管理器工具。它使库安装更快更有效。我们可以在一行中安装多个软件包,让我们这样做:
pip install numpy opencv
让我们继续创建一个新的木星笔记本。请随意使用您喜欢的代码编写环境。为了这个项目,我将使用 Jupyter 笔记本。这是我在笔记本中的第一个块,我们在这里导入刚刚安装的库。
import numpy as np
import cv2 as cv
步骤 2 —读取图像
在这一步中,我们将找到一个图像来测试我们的程序。它可以是有噪声的图像,也可以不是。无论你想和谁一起去。用普通照片测试的时候还是会看出区别的。
我们的程序不够智能,无法检测图像是否有噪声,但可以编写一个程序来解决这个问题。选择图像后,将其复制到您正在使用的文件夹中。这样会让阅读过程更容易。
下面是我选择的图片(改名为“profile_image.jpg”):
这是读取图像的一行。我们使用 OpenCV 的 imread 方法:
img = cv.imread('profile_image.jpg')
每个像素都有价值。OpenCV 的 imread 方法将读取图像并将其转换为二维数组。让我向你展示一幅图像阅读后的样子:
作者图片
额外提示:我们可以通过计算数组大小来找到图像的大小。下面,我们可以看到我导入的图像是 509 到 680 像素。以下是如何通过查找数组大小来计算图像大小:
步骤 3 —非局部均值去噪算法
接下来是项目的信息部分。首先,我将分享我们将使用哪种降噪算法。然后,我会分享它有多少个参数,每个参数代表什么。
关于算法
图像中黄色圆圈区域看起来相似。橙色圆圈区域看起来很相似。该算法选取一个像素,在其周围取一个小窗口,然后在图像中搜索相似的窗口,对所有窗口进行平均,并用计算的结果替换该像素。这就是为什么它被称为非本地的,因为它不只是检查它的周围。它寻找整个图像。
该算法的函数版本如下:
- fastNlMeansDenoising(用于灰度图像)。
- fastNlMeansDenoisingColored(用于彩色图像)。
参数
fastNlMeansDenoisingColored(src,dst,h,hcolor,templateWindowSize,searchWindowSize )
- src :我们要做降噪处理的输入图像。
- dst :我们要导出结果的目的地。
- h :亮度分量。(较大的 h 值会移除更多噪点,但也会移除图像细节)。
- hcolor :颜色分量。(10 是彩色图像文档的推荐值)。
- template windowsize:函数将要平滑的区域的像素大小。它应该是一个奇数。(7 是文档中的推荐值,适用于大多数情况)。
- search window size:函数将查找并引用的区域的像素大小。线性影响性能:更大的 searchWindowsSize 意味着更长的去噪时间。此外,它应该是一个奇数整数。(21 是文档中的推荐值,适用于大多数情况)。
现在,让我们看看我们刚刚在实际行动中学到了什么。
最后一步——降噪功能发挥作用
完美!这个项目有趣的部分来了。我们将看到降噪后的图像是什么样子。我们将使用三个不同的值来运行函数,这样就可以看到每个值是如何影响最终结果的。
运行功能
dst1 = cv.fastNlMeansDenoisingColored(img,None,3,3,7,21) dst2 = cv.fastNlMeansDenoisingColored(img,None,5,5,7,21) dst3 = cv.fastNlMeansDenoisingColored(img,None,10,10,7,21) dst4 = cv.fastNlMeansDenoisingColored(img,None,3,3,5,13)
使用 imwrite 功能将结果保存为图像
cv.imwrite('one_1.png', dst1)
cv.imwrite('one_2.png', dst2)
cv.imwrite('one_3.png', dst3)
cv.imwrite('one_4.png', dst4)
结果
dest1 与 dest4 比较(了解不同的窗口像素大小):
dest1 和 dest4 比较
dest2 与 dest3 比较(了解不同的亮度和颜色分量值):
dest2 和 dest3 比较
结论
我个人最喜欢的是 dest2。现在,让我们做一个照片的前后对比,看看有什么不同。你决定我们的降噪模型的表现。
之前和之后
恭喜你。我们已经建立了一个程序,使用非局部均值去噪算法来降低图像的噪声。这是最大的图像降噪方法之一。还有其他去噪算法,但都不如非局部均值去噪算法强大。其中一些也被称为模糊技术。
正如你从结果中看到的,当我们增加更多的平滑度时,我们可以使用这种技术使我们的照片看起来像油画。所以,这完全取决于你的想象力。
从事像这样的动手编程项目是提高编码技能的最好方式。我希望你今天喜欢并学到了一些新东西。如果您有任何问题或意见,请随时联系我。
有趣的 OpenCV 项目:
不平衡和混合数据集对 ML 模型的影响
基于精确度、召回率和 F1 分数评估 ML 模型
图片来自 unsplash
简介
机器学*需要创建强大的训练数据集,因为训练是后续模型评估的种子。如果训练数据被破坏,模型将表现不佳,因为其准确性将下降。图像分类是机器学*的一个蓬勃发展的领域,其中平衡和正确的训练数据集极其重要。人工智能经理和数据科学家需要确保在管道中加载平衡和干净的数据。
分类指标
评估一个模型有几个指标。对于图像分类,我们可以采用测试目录来应用训练好的模型的预测行为。Scikit-Learn 提供了一些简单的包来评估模型在准确性、精确度、召回率和 F1 分数方面的性能。分类报告是所有这些指标的组合。我将简要介绍一下这些指标。
精度
当我们试图猜测两件事之间的某件事时,我们有 50%的可能出错。这就是我们正确识别某物的精确度。机器也是如此。机器被训练来创建模型,并且基于训练数据,它可能正确地识别所有的测试数据,或者一些可能出错。比方说,我们希望模型能够识别猫和狗之间的图像。我们已经用猫和狗的图像训练了模型。该模型可以正确地识别所有图像,或者一些图像可能被错误地标记。相关术语如下
- 真阳性(TP):被识别为猫的猫
- 假阳性(FP):被识别为猫的狗
- 真阴性(TN):被识别为狗的狗
- 假阴性(FN):被识别为狗的猫
很明显,当一个模型不能正确地标记某些东西时,它会增加假阳性或假阴性的数量。我们希望模型具有最高数量的真阳性和真阴性。根据这些结果来更好地评估一个模型,精确度和召回率是很方便的。简而言之,精度衡量一个模型正确检测元素的精度。当一个猫狗检测模型的精度较高时,表明它能够正确识别出大多数的猫是猫,极少数的狗是猫。该模型也可能错误地将一些猫识别为狗图像,但是该信息不在精度值中。为此我们需要召回。
精度= TP/(TP+FP)
来自维基百科的精确度和召回率
召回
召回是模型评估的另一个度量。它衡量模型检测所有事件的能力。
召回= TP/(TP+FN)
当上述模型的召回值高时,表明它能够正确地将大多数猫的图像识别为猫,而将极少数猫识别为狗。
在这个模型中我们有两个类,即猫和狗。对于一个类,如果召回率高而准确率低,就意味着模型偏向于那个特定的类。例如,如果猫的召回率高但精确度低,则该模型能够正确地将大多数猫图像识别为猫,但是它也错误地将许多狗图像识别为猫。另一方面,如果猫的召回率低但精确度高,则表明该模型能够正确地将大多数猫的图像识别为猫,但同时,它也将许多猫的图像错误地识别为狗。
作者图片
X 射线图像数据集
我写了一篇关于正常型和肺炎型 X 线影像分类的博客。在这里,我演示了识别模型的训练和评估步骤。
在本文中,我将使用相同的数据集,但做了一些修改以反映现实世界中的问题。我已经为训练、验证和测试创建了三个独立的数据集。
- 第一数据集:在训练中,它是具有 500 个正常图像和 500 个肺炎图像的平衡数据集
- 第二数据集:它是在训练中具有 500 个正常图像和 250 个肺炎图像的不平衡数据集
- 第三个数据集:它是一个混合但平衡的数据集。250 个正常图像被有意标记为肺炎,250 个肺炎图像被有意标记为正常。
这个 Github 页面提供了训练和评估模型的代码块。当使用第一个数据集时,我们在 50 个历元后得到以下损失和准确度。
平衡数据集的损失
平衡数据集的准确性
显然,我们用训练数据过度拟合了模型。我们可以用其他技术来改进模型,以减少过度拟合,但这里我们关注的是平衡和不平衡数据报告之间的比较。
图像识别
第二个和第三个数据集的损失和精度如下所示
不平衡数据集的损失
不平衡数据集的准确性
混合数据集的损失
混合数据集的准确性
混合数据集非常脏,最终精度非常低。一旦代码被执行,地面真相连同被识别为正常的概率以及被识别为肺炎的概率被显示在每个图像的顶部。我们需要将预测转换为二进制,以获得分类报告。
通常,当没有提到平均值时,分数作为加权平均值给出,以得到单个指标。当平均值为零时,给出两个班级的分数。对前面提到的所有三个数据集执行相同的代码块。数据集的分类报告摘要如下
作者图片
作者图片
作者图片
讨论
在第一个数据集上,类 0 的精度较高,而召回率较低,表明存在大量的假阴性。总体准确率为 58%。在第二个数据集中,训练数据中有更多的 0 样本,使得类 0 的召回值非常高,但精度下降。本质上,它指的是模型变得偏向那个阶级的事实。第三个数据集只是一个混合数据,以一个非常弱的模型结束。因此,为了更好的模型性能,我们需要更高的精度和召回率。有些人更喜欢通过 F1 分数来评估模型,F1 分数结合了这两个指标。
F1 分数定义
对于平衡和不平衡数据集,混淆矩阵在非对角线位置显示一些高值,但是对于混合数据集,它相当混乱。
混淆矩阵
结论
在本文中,我描述了图像分类的模型评估指标及其在 python 中的实现。三种不同的数据集用于反映现实世界的问题,如不平衡数据集和脏数据集。数据科学家和人工智能经理需要确保训练数据对于每个分类都应该足够干净和平衡。在所有冗长的模型训练之后,混合数据可能以弱模型结束。
有效的人工智能基础设施或为什么功能商店是不够的
现代人工智能基础设施可以加速人工智能的生命周期,并在数据科学家和工程师之间创造一种和平的互动。但是,它们是什么呢?它们与 MLOps 有何不同?
图片经由 iStock.com/cifo tart授权给阿尔莫格巴库。
在过去的几年里,人工智能产业爆发并有了巨大的进步。这种增长导致了许多旨在标准化模型开发的新项目。尽管如此,模型开发仅仅是工作的一半,并且大多数项目无法部署到生产。
将模型部署为产品(也称为 ML 驱动的产品)的功能单元仍然是一门艺术,需要它的实践者在瀑布方法中仔细地将所有的部分一个接一个地制作在一起。如下所述,现代人工智能基础设施有可能改变这一点。
构建 ML 驱动的产品是一个瀑布式的过程,有多个相互依赖的步骤。作者插图
尽管 Scrum 和敏捷方法在技术行业变得流行,但是这些方法在开发 ML 驱动的产品时不能应用,因为步骤之间脆弱的依赖关系:
- 数据发现 —数据团队从内部和外部资源收集信息。
- 数据准备 —数据团队转换原始数据并整理与问题相关的相关数据点(也称为“数据特征”)。
- 模型训练 —数据科学家正在用数据做实验来解决一个目标。
- 模型验证—数据科学家正在跟踪模型实验,寻找性能最佳的模型。
- 生产 代码—工程师正在编写一个微服务,利用模型来服务于生产使用中的预测。
- 部署&测试 — 生产代码应针对规模进行设计,并使用组织的 CI/CD 系统进行部署和测试。
- 持续监控和学*
虽然 ML 驱动的产品开发周期(又名 ML 工作流)是一个非常复杂的过程,但我们可以将其拆分为三大阶段:(1) 准备、 (2) 、 (3) 产品化 。
ML 生命周期中最具挑战性的部分,也是最主要的障碍,无疑是 产品化阶段。
来源:https://martinfowler.com/articles/cd4ml.html
为模型提供具有预测时间准确性的特性(又名按需特性)可以生成最准确和最新的预测,这对于 ML 驱动的产品是强制性的。
然而,由于 DS 和 SW 工程师之间臭名昭著的摩擦,这仍然非常具有挑战性。
软件工程师需要与训练该模型的数据科学家密切合作,以使其为生产做好准备。有时,他们甚至对同行的代码进行逆向工程,或者完全从头开始重写。
为了构建这样的解决方案,通常的做法包括编写新的微服务应用程序,该应用程序:
- 将模型包装在推理代码中。
- 在线转换来自生产数据源的数据,正如我们在准备过程中所做的那样,转换为“按需功能”
- 向模型提供按需功能。
- 为预测服务
当前架构
当前的架构被构建为负责翻译特定于域的请求(即,交易)、转换数据、预测、监控和模型管理的单片特定于域的服务(即,“欺诈检测服务”)。
这些类型的架构需要 ds 和 SW 工程师的密切合作来构建这样的服务。
红色-工程责任;黄色- DS 责任;渐变-两侧。作者插图
由于部署模型的复杂工作流程,公司开始寻找新的方法来标准化 ML 驱动产品的开发和部署,并减少生产项目所需的长时间。
2017 年,优步发表了一篇关于 michaelangello——优步的 ML 平台的文章。文章描述了优步为加快 ML 驱动产品的开发所采取的措施,以及一个独特的数据管理层——“特征库** ”**
要素存储充当数据要素的共享存储以及模型和数据之间的通信层。作者插图
特性存储是一个独特的存储层,允许“数据特性”的可重用性它利用“冷”存储来提供培训功能,利用“热”缓存来传达这些功能以用于生产。
尽管共享缓存层的想法在软件开发中并不是一个新方法,但是分离瀑布式工作流的概念是革命性的。它允许分离过程和处理与模型开发分开的数据处理,并简化了特征工程过程。**
特征工程被认为是工作流程中最反复、最耗时、最耗费资源的阶段。事实上,数据团队大约 80%的时间都在做这件事。因此,it 的简化看起来像是人工智能基础设施发展的必要步骤。
最*,其他 公司 采用新范式,开始将 ML 操作系统从其特色操作系统中分离出来,打破单一流程。
这种新兴的架构使组织能够分离特性和模型关注点,标准化模型和数据之间的契约,并减少 DS 和 SW 工程的摩擦。像“微服务”革命一样,工作流程中单元的标准化允许我们推广新的部件并加快我们的开发。
一个新的地平线正在闪耀。
与当前的架构相比,现代人工智能基础设施将把模型部署的操作工作与功能部署分开,并部分分离 DS 和 SW 工程师之间的界限。
特性平台使团队能够专注于特定领域的问题,而不是运营开销。作者插图
此外,新的架构迅速采用了 ML 行业中的特征存储概念和关注点分离。尽管如此,功能商店是不够的,只能解决一部分问题;因此,现代基础设施必须提供全面的解决方案:
数据科学平台(又名 MLOps 平台)
数据科学平台是负责开发、跟踪实验、管理、部署、监控和管理模型的综合解决方案。他们负责该流程的运营方面,并减轻日常运营的复杂性。
MLOps 平台提供了一个通用推理服务器,该服务器可以在给定一组输入要素的情况下为解决方案提供一个推理层。这些服务器可能还实现了一个“转换器层”,可以连接到功能平台。市面上的一些产品已经提供了这样一层: KFServing 、 TensorFlow serving 、 Seldon 、SageMaker、GCP 等等。
许多人已经写了 MLOps 系统的重要性以及它们减少对 Ops 的依赖的能力。尽管如此,由于它们的作用不同,不要将它们与基础设施解决方案相混淆,这一点很重要。* 一个极好的类比是卡夫卡和詹金斯——卡夫卡是基础设施系统,而詹金斯是 DevOps 系统。***
功能(工程)平台
特色平台是 ML 生态系统中缺失的组成部分。特征平台负责转换、服务和监控模型的特征。
由于其作为生产系统功能部分的角色,现代功能平台必须确保遵守生产 SLA,并提供稳定、可扩展、低延迟和故障转移弹性。
需要强调的是,训练和推理都需要特性,特性应该在和两个阶段进行设计。这个两条腿的过程在训练和推理之间产生了一个偏斜——提供一个机制来确保它们之间的一致性是平台的责任。**
与 MLOps 平台不同,功能平台不负责运营生态系统,而是负责访问生产数据流。**
功能平台负责以下目标:
- 访问和存储特征值和特征集
- 管理和监控功能元数据
- 启用和管理 sugared 工程流程
- 充当运营职能并确保高水平的 SLA
现代功能平台不会只提供商店,而是在移动和转换数据的工程工作和实际的转换(业务)逻辑之间提供一个标准化的通信层。****
功能平台作为一个综合框架,处理功能生命周期的多个部分,而不仅仅是操作组件。插图作者**
特性平台应该包括以下功能来实现其目标:治理层、特性存储、转换框架和操作层。
管理
功能平台应该为功能提供统一的治理,包括:
- ****元数据和特征注册——即特征名称、其逻辑的文本解释、所有权等。
- 功能目录 —允许跨组织的功能协作和可重用性。
- 监控 —支持跟踪特性性能并发现数据漂移。
- 版本控制 —跟踪数据转换的不同实现。
功能存储(存储)
特征存储负责为服务和训练提供特征。它还应该作为关于功能培训和服务的单点事实,并确保在线/离线价值之间的一致性。
该组件还负责启用“时间旅行”功能,这对于跟踪时间序列要素的不同值以及同步数据集中其他要素的值至关重要。
可以实现不同的架构来达到这个目标;然而,典型的体系结构结合了“在线/离线”存储(因此—功能存储),通过延迟要求的过滤器来分割负载。
转换框架
转换框架应该布置工具来与特征存储进行通信,以将原始数据处理、丰富和计算成特征值,并将其保存到特征存储中。
转换框架应该通过减少处理“高级用例”所需的工程代码量,使开发人员能够进行更好的交流,这些“高级用例”包括:回填、按需转换、批量(离线)转换、功能性(即地理距离、派生功能)转换等。
操作层
最后,由于功能平台是生产解决方案的关键部分,平台必须符合产品的 SLA——提供低延迟服务、可伸缩性、高可用性等。
大规模部署可能还会处理将特性部署到不同云环境的挑战。
整体情况
部署一个 ML 驱动的产品,作为产品的一个功能部分,可以准确地服务于在线模型预测,这是一个非常复杂的任务,但它不应该是这样的。现代人工智能基础设施可以有效地减少摩擦,并在数据科学家和工程师之间建立和平的互动。
与传统的软件流程一样,我们预计工作流会有所中断,从而在开发和部署新服务时减少运营和工程开销。
由于数据驱动产品的复杂性,ML 领域将不得不将其单片系统分成多个系统。每个人负责不同的任务,类似于传统的软件领域。
从数据特征系统中分离 ML 系统。作者插图
特征平台将从生产数据源收集原始数据,并管理转换后的特征,模型平台可以使用这些特征进行训练和服务。相比之下,MLOps 平台将帮助数据科学家开发和部署 ML 模型。
最后的想法
就目前市场上的解决方案而言,仍然很难区分架构的不同部分。因此,区分 MLOps 和 AI 基础设施至关重要,因为每个系统的角色非常不同。**
尽管一些新兴架构已经取得了长足的发展,但是当前的解决方案仍然关注于操作和服务特性,而不是关注于创建它们的挑战。**
现代特征平台将不得不专注于特征创建而不是缓存存储,并且可能是简化新的 ML 驱动的产品和服务的开发和部署的关键。
你怎么想呢?请告诉我您采用基础设施的用例以及您面临的挑战(或者您是否需要帮助来设置这样的系统)。非常欢迎你通过电子邮件或 LinkedIn 给我写信。
利用领域驱动和产品思维方法进行有效的数据管理
“分而治之”和“产品思维”的思想可以带来有效的数据管理。数据中的产品思维,即“数据作为产品”是一种思维模式,也不一定需要全面的数据网格
图片来自 Pixabay 许可下的 Pixabay
数据作为资产
数据作为资产的思想已经存在了一段时间。这种想法旨在促进有效的数据管理、利用和可能的数据货币化。它为数据治理提供了基线。数据管理和治理中的一些关键活动是确保数据得到及时维护、正确记录、质量提高和归(企业)所有。
成功的数据管理面临的挑战
但是有多少这样的数据管理程序真正获得了成功呢?不多。
一家公司是基于直接面向客户的价值主张而被认可的。它的成功取决于产品销售和客户参与度。因此,利润思维和专注于在更丰富的产品方面创造差异化始终是首要任务。虽然数据是大多数产品的核心,但通常是应用程序功能和用户体验的外层掩盖了核心。
在组织范围内实施数据管理很少会成功。价值流线索需要关注价值主张。产品负责人需要专注于改善客户体验的方法。虽然数据管理的目标是使这些事情变得更容易,但它最终成为一种开销,降低了它们的速度,并产生了不必要的依赖性。
如何解决这个问题?
解决这个问题需要我们后退一步(或者几步),把信息系统作为一个整体来看待。这就是“领域”概念和“作为产品的数据”思想可以发挥作用的地方。关于这些术语的参考,请参见此处的、此处的和此处的。
作为“领域产品”的数据
传统数据管理和治理的所有方面都是特定于领域的。每个域完全负责为其数据建立数据目录、所有权、管理权、质量检查和主数据管理。
数据作为产品(或领域产品)是数据网的基本原则之一。基于这一原理,数据网格也带来了“数据产品”的概念。数据网格环境中的“数据产品”是一个架构量子,非常类似于应用架构中的微服务,即仍然完全完整并提供特定价值的最小可能单元。但是数据产品可以简单到仅仅是“数据”。数据网格数据产品的其他元素,如代码、API 等。是推动者并提供杠杆作用。
每个域都应该将它所公开的数据视为一种产品。这种数据的“产品思维”使得它像产品一样。因此,数据仍然是一种资产,但它是在一个领域内。外面,是产品。
作为“领域”产品的数据
对于每个领域,产品所有者和产品团队都会形成一种商人心态。这意味着…
- 就像商人维护产品清单一样,域维护可搜索的数据目录。
- 就像商家确保文档和库存数量信息、客户评论等。对于产品是更新的和可见的,领域确保文档(定义、模式、API 端点等。),以及数据的其他元数据对于消费者来说是容易获得的。
- 就像商家为产品提供售后支持一样,域提供对事件日志、API 调用历史、API 日志等的访问。以便下游应用程序可以调试和处理错误。
- 就像商家推出新产品,特别是软件更新,例如,提供向后兼容性和回滚的可能性,API 接口和事件模式应该迎合 API 版本和模式演变。
- 就像有一个产品所有者一样,它应该提供一个数据所有者。
- 就像有一个产品路线图一样,它也应该有一个指示数据、集成、存储和架构变化的数据路线图。
版权所有 Prajwalan Karanjit
这个想法是,就像供应商对产品负责一样,领域也应该有同样的感觉。
经典的产品思维不仅仅是识别最终用户需求,它还理解业务驱动因素、竞争、颠覆者和硬成本。通过将数据视为一种产品,我们将所有这些品质带入数据管理。
那么,什么是域呢?
域是某种有界的上下文。由一个或几个应用程序和数据库组成的特定业务功能可以是一个域。同样,属于不同业务能力的相关应用程序的集合也可以被认为是一个域。
分离有界上下文的方法不止一种。这里的主要思想是分而治之。
它是关于建立一条阻力最小的道路。数据治理的非侵入式模型也适用于整体数据管理。使用领域驱动的方法会更有效。
通过将数据管理责任委派给各个领域,我们已经让他们负起责任,并使他们能够实施适当的适合环境的方法来进行数据管理。适合上下文的方法很重要,因为在一个重要的方面,一些业务领域在另一个方面可能不那么重要。
不要创建专门的角色,如数据管理员,而是将现有的人员识别为这样的角色。在某些情况下,这已经是他们工作的一部分。因此,我们的工作是通过用数据职责来丰富工作描述,使之更加正式和明确。人是领域的一部分,通过这样做,我们让领域对它们如何定义、产生和使用数据负责。
一个简单的分而治之的例子是一个国家。一个国家有几个州或区。当然,有些治理方面适用于各州,但每个州/区也有自己的治理模式。
对比、挑战和缺点
这与用一个数据管理模型来管理所有数据形成了对比。甚至数据湖也可能是特定于域的,其中每个域可能有不同的数据湖技术和不同的部署模型(内部部署、云)。这给了域更多的自主权,当需要协同时,域之间的数据交换可以通过 API 或事件来实现。
这样做的缺点是数据、应用程序和基础架构的重复,这可能会导致成本增加。然而,考虑到整体效益,这种成本和开销应被视为一种投资。当需要跨领域的洞察力时,可能还会有一些技术挑战。
这里的回报是高度可管理的领域,它们可以扩展、进化和成长,甚至可以安全地被划分出来或被淘汰。
结论
有效的数据管理不仅仅是数据。这不是一个孤立的活动,我们应该把问题空间和解决方案空间作为一个整体来考虑。每个领域都对它完全负责,组织也应该同样授权它们。每个领域都应该将它的领域数据作为业务资产来维护和治理,但是要有产品思维。
为大于内存的数据集提供有效的数据故事
图片来自unsplash.com
使用 Streamlit、Dask 和 Coiled 创建直观的交互式 web 应用程序来可视化大数据
TL;博士
将 Streamlit 与 Dask 和 Coiled 集成在一起,可以让您创建直观、交互式的 web 应用程序,毫不费力地处理大量数据。这篇博客文章引导你编写一个集成的 Streamlit-on-Coiled 脚本,在交互式热图可视化中呈现 10gb 以上的数据。然后我们将脚本扩展为包含:
- 一个较重的分组通过计算,
- 交互式小部件,用于放大或缩小盘绕的集群,以及
- 按需关闭集群的选项。
面向大数据的简化 it
Streamlit 使数据科学家能够构建轻量级、直观的 web 应用,而无需编写任何前端代码。您甚至不必离开 Python 的友好范围;就是那么好;)
也就是说,当数据集或计算的规模超过几秒钟内完成的规模时,使用 Streamlit 这样的前端解决方案可能会变得很麻烦。很可能您使用 Streamlit 的初衷是因为您想要创建一个更流畅、更直观的用户体验。我猜让你的用户坐几分钟(或者几小时!)虽然计算运行不符合您对“平滑”的定义…
当然,您可以选择购买和管理昂贵的机器集群(本地或云)。但是,除非你的 Streamlit 应用程序像网飞一样受欢迎,否则你的集群很可能会长期闲置。这意味着浪费时间和金钱。也不好!
在这里,将繁重的计算任务委托给 Dask 集群很值得考虑。 Coiled 允许您在云中按需启动 Dask 集群,而无需担心任何开发工作,如设置节点、安全性、扩展甚至关闭集群。Streamlit 在单个 web 应用程序中加入各种力量,处理应用程序的前端布局和交互性,而 Coiled 整理后端基础架构以进行高要求的计算。
这篇博文将向您展示如何构建一个 Streamlit-on-Coiled 应用程序。我们将从一个基本脚本开始,该脚本将来自纽约市出租车数据集的 10gb 以上的数据加载到一个交互式用户界面中。从那里,我们将真正通过运行更重的工作负载来充分利用 Dask 和 Coiled。最后,我们将调整我们的 Streamlit 界面,允许用户使用简单的滑块来放大和缩小集群,并包括一个关闭集群的按钮,让用户可以更好地控制他们的计算能力,而无需编写任何代码。
你可以从这个 GitHub repo 下载基本的和最终的、扩展的 Python 脚本。要继续编码,你需要一个 Coiled Free Tier 账户,你可以通过 cloud.coiled.io 使用你的 GitHub 凭证来设置这个账户。对 Dask 和 Streamlit 有一些基本的了解是有帮助的,但不是必须的。
免责声明:我在 Coiled 工作,是一名数据科学传道者实*生。 Coiled 由Dask的最初作者 Matthew Rocklin 创立,是一个面向分布式计算的开源 Python 库。
可视化大于内存的数据集
下面的示例脚本使用 Coiled 和 Streamlit 从纽约市出租车数据集中读取超过 1.46 亿条记录(10+ GB ),并可视化出租车上下车的位置。让我们来分析一下脚本中发生了什么:
首先,我们导入运行脚本所需的 Python 库,在本例中是 Coiled、Dask、Streamlit 和 Folium。
import coiled
import dask
import dask.dataframe as dd
import folium
import streamlit as st
from dask.distributed import Client
from folium.plugins import HeatMap
from streamlit_folium import folium_static
在下一节中,我们将使用 Streamlit 创建前端用户界面。我们从一些描述性的标题和文本开始,然后包括两个下拉框,允许用户选择他们想要可视化的数据类型。
# Text in Streamlitst.header("Coiled and Streamlit")st.subheader("Analyzing Large Datasets with Coiled and Streamlit")st.write(
"""
The computations for this Streamlit app are powered by Coiled, which provides on-demand, hosted Dask clusters in the cloud. Change the options below to view different visualizations of transportation pickups/dropoffs, then let Coiled handle all of the infrastructure and compute.
"""
)# Interactive widgets in Streamlittaxi_mode = st.selectbox("Taxi pickup or dropoff?", ("Pickups", "Dropoffs"))num_passengers = st.slider("Number of passengers", 0, 9, (0, 9))
在那里,我们编写了一个函数,它将旋转一个盘绕的簇。在这里,我们指定工作人员的数量、集群的名称,以便我们可以在以后重用它(如果有多人查看您的 Streamlit 应用程序,这一点至关重要),以及要分发给我们的调度程序和工作人员的软件环境。关于如何设置软件环境的更多信息,请参见中的本页。
您可以在Coiled Cloud 页面上查看任何活动的和已关闭的集群,以及您的软件环境和集群配置(前提是您已登录您的帐户)。
# Start and connect to Coiled cluster
cluster_state = st.empty()@st.cache(allow_output_mutation=True)def get_client():
cluster_state.write("Starting or connecting to Coiled cluster...")
cluster = coiled.Cluster(
n_workers=10,
name="coiled-streamlit",
software="coiled-examples/streamlit"
)
client = Client(cluster)
return clientclient = get_client()if client.status == "closed":# In a long-running Streamlit app, the cluster could have shut down from idleness. If so, clear the Streamlit cache to restart it.st.caching.clear_cache()
client = get_client()
cluster_state.write(f"Coiled cluster is up! ({client.dashboard_link})")
接下来,我们从公共的亚马逊 S3 存储桶加载数据作为 Dask 数据帧,指定我们想要包含的列和每个分区的块大小。注意这里对 df.persist() 的调用。这将数据帧保存在集群上,因此无需在每次应用刷新时重新加载。调用后,只要集群在运行,就可以立即访问数据集。
# Load data (runs on Coiled)@st.cache(hash_funcs={dd.DataFrame: dask.base.tokenize})
def load_data():
df = dd.read_csv(
"s3://nyc-tlc/trip data/yellow_tripdata_2015-*.csv",
usecols=[
"passenger_count",
"pickup_longitude",
"pickup_latitude",
"dropoff_longitude",
"dropoff_latitude",
"tip_amount",
"payment_type",
],
storage_options={"anon": True},
blocksize="16 MiB",
) df = df.dropna()
df.persist()
return dfdf = load_data()
最后,我们使用上面的 Streamlit 小部件的输入来创建一个名为 map_data 的数据子集,并将其传递给叶地图,指定我们希望它显示为热图渲染。
# Filter data based on inputs (runs on Coiled)with st.spinner("Calculating map data..."):
map_data = df[
(df["passenger_count"] >= num_passengers[0])
& (df["passenger_count"] <= num_passengers[1])
] if taxi_mode == "Pickups":
map_data = map_data.iloc[:, [2, 1]] elif taxi_mode == "Dropoffs":
map_data = map_data.iloc[:, [4, 3]] map_data.columns = ["lat", "lon"]
map_data = map_data.loc[~(map_data == 0).all(axis=1)]
map_data = map_data.head(500)# Display map in Streamlitst.subheader("Map of selected rides")m = folium.Map([40.76, -73.95], tiles="cartodbpositron", zoom_start=12)HeatMap(map_data).add_to(folium.FeatureGroup(name="Heat Map").add_to(m))folium_static(m)
就是这样!让我们看看这是什么样子。
请注意,这是一个独立的 Python 脚本,您可以使用streamlit run<path/to/file>从您的终端运行,而不是从 Jupyter 笔记本上运行。
所以,继续在你的终端上运行它…几秒钟后,你的浏览器就会呈现给你一个交互式界面,如下图所示。
很神奇吧?尤其是当您考虑到每次刷新地图时,应用程序都会在一瞬间处理超过 1.46 亿行(超过 10GB)的数据!
填充 Dask 仪表板
现在让我们来看看 Coiled 如何处理更重的工作负载。如果您碰巧点击了 Dask 仪表板的 URL,您会看到生成地图的计算只需几个任务就完成了。虽然 Dask 毫不犹豫地处理了这个问题,但它实际上是为分布式计算而设计的——当有大量任务需要它运行时,它真的会露出牙齿。所以让我们给它一个闪耀的机会,好吗?
我们将在脚本中创建一个新的部分,允许用户通过计算建立一个组。我们将为用户提供一个选项,让用户选择要分组的列,以及要计算的汇总统计类型。并且包括触发计算的按钮。
# Performing a groupbyst.subheader(
'Time for some heavier lifting!'
)st.write(
'''
Let's move on to doing some heavier lifting to really see Dask in action. We'll try grouping a column and calculating a summary statistic for the tip amount.\n Select a column to group by below and a summary statistic to calculate:
'''
)# Interactive widgets in Streamlitgroupby_column = st.selectbox(
"Which column do you want to group by?",
('passenger_count', 'payment_type')
)aggregator = st.selectbox(
"Which summary statistic do you want to calculate?",
("Mean", "Sum", "Median")
)st.subheader(
f"The {aggregator} tip_amount by {groupby_column} is:"
)if st.button('Start Computation!'):
with st.spinner("Performing your groupby aggregation..."):
if aggregator == "Sum":
st.write(
df.groupby(groupby_column).tip_amount.sum().compute()
)
elif aggregator == "Mean":
st.write(
df.groupby(groupby_column).tip_amount.mean().compute()
)
elif aggregator == "Median":
st.write(
df.groupby(groupby_column).tip_amount.median().compute()
)
保存 Python 脚本并在您的终端中重新运行streamlit run<path/to/file>将加载 Streamlit 应用程序的更新版本,如下图所示。
显示分组依据计算的 Dask 仪表板
扩展 Streamlit 接口
现在,我们可以使用新的下拉选项通过计算来自定义我们的 groupby。单击 new 按钮会在我们的 Coiled 集群上触发一些繁重的计算,在 45 秒内计算超过 1.46 亿行的汇总统计数据。
缩放和关闭你的盘绕式集群
但是…如果我们挑剔的话,使用整个集群来生成地图有点大材小用了;它只包含少量的任务。另一方面,在一次重要的董事会会议之前,你可能正在向工作过度的首席执行官演示这款应用程序,而你最不想做的事情就是让他们在 groupby 计算运行的 45 秒内盯着一个转动的轮子。
如果有一种方法可以根据我们的计算需求来扩大或缩小我们的集群就好了…
带着对盘绕的呼唤。我们可以指定我们的集群拥有的工人数量。请注意,我们必须指定集群的名称,以便在该调用中进行伸缩。让我们继续在脚本中添加一个新部分,将该调用附加到一个交互式 Streamlit 滑块。这意味着我们的用户现在可以根据需要调整他们的计算能力…就在我们的 web 应用程序中,无需编写任何代码。
# Option to scale cluster up/downst.subheader(
"Scaling your cluster up or down"
)st.write(
'''
By default, your Coiled Cluster spins up with 10 workers. You can scale this number up or down using the slider and button below.
'''
)num_workers = st.slider(
"Number of workers",
5,
20,
(10)
)if st.button("Scale your cluster!"):
coiled.Cluster(name='coiled-streamlit').scale(num_workers)
请注意,虽然缩减是即时的,但扩展集群需要一到两分钟。好消息是,当集群扩展时,您可以继续运行您的计算。您可以使用 Coiled Cloud web 界面来查看您的集群当前有多少工作线程。
coiled Cloud UI Dashboard—Cloud . coiled . io
最后,让我们构建一个按钮,允许用户关闭集群以避免不必要的成本。请注意,这里有一个权衡:如果您正在快速迭代 Streamlit 应用程序,我们建议保持集群运行,这样您就不必在每次重新运行脚本时都等待它开始运行。在这种情况下,命名集群很重要,这样您就可以在后续运行中引用它。但是,如果在可预见的将来您已经完成了所有工作,那么关闭集群是一个很好的做法。
# Option to shutdown clusterst.subheader(
"Cluster Hygiene"
)st.write(
'''
To avoid incurring unnecessary costs, click the button below to shut down your cluster. Note that this means that a new cluster will have to be spun up the next time you run the app.
'''
)if st.button('Shutdown Cluster'):
with st.spinner("Shutting down your cluster..."):
client.shutdown()
我们在这里偷偷插入一个提示:默认情况下,盘绕的集群在 20 分钟不活动后关闭。您可以通过使用 idle_timeout 关键字参数来设置您自己的首选超时窗口。
cluster = coiled.Cluster**(** scheduler_options=**{**"idle_timeout"**:** "2 hours"**}
)**
让我们回顾一下
我们从运行来自 Coiled 文档的 Streamlit-on-Coiled 示例脚本开始。我们看到了如何快速、轻松地创建一个直观、交互式的 web 应用程序,可以处理 1.46 亿行数据。接下来,我们将这一点做了进一步,让我们的 web 应用程序的用户能够在我们的盘绕式集群上计算更繁重的计算。然后,我们通过构建一个选项来根据需要扩大(或缩小)集群,从而增强了我们的计算能力。最后,我们讨论了何时以及如何关闭集群以避免不必要的成本。
我希望这篇博文能帮助您创建有效的数据故事应用程序,以传达您的数据科学工作流的影响。如果您对未来的材料有任何问题或建议,请随时联系这里或Coiled Community Slack channel,我很乐意听到您的意见!
有效的特征选择:使用 R 的递归特征消除
安东尼·马蒂诺在 Unsplash 上的照片
D 在机器学*中开发一个准确而简单(且可解释)的模型可能是一项非常具有挑战性的任务。根据建模方法(例如,神经网络与逻辑回归),模型中有太多的特征(即,预测值)可能会增加模型的复杂性或导致其他问题,如多重共线性和过度拟合。此外,对于高度复杂的模型,获取(或维护)用于未来预测的大量特征可能会更加困难。因此,选择最佳特性非常重要。
有时候,少即是多。——威廉·莎士比亚
递归特征消除,简称 RFE,是一种广泛使用的算法,用于选择与预测模型中预测目标变量最相关的特征,无论是回归还是分类。RFE 应用逆向选择过程来寻找特征的最佳组合。首先,它基于所有特征构建模型,并计算模型中每个特征的重要性。然后,它基于模型评估度量(例如,RMSE、准确度和 Kappa)对特征进行排序,并迭代地移除重要性最低的特征。可以基于模型(例如,随机森林重要性标准)或使用模型独立的度量(例如,ROC 曲线 analysis)⁴.)来计算特征重要性这个过程一直持续到更小的特征子集被保留在模型中(从技术上讲,RFE 仍然可以在最终模型中保留所有(或大部分)特征)。
RFE 可以用几种编程语言来实现,比如 R、Python 和 MATLAB。多亏了著名的脱字符包,从头开始在 R 中实现 RFE 方法非常简单。在本文中,我将演示如何在 r 中使用 RFE 进行特性选择。
- 了解 RFE 在构建预测模型时如何选择重要特征,以及
- 知道如何使用 caret 包在 r 中实现基于 RFE 的特性选择
例如:心脏病数据集
杰西·奥里科在 Unsplash 上拍摄的照片
在这个例子中,我们将使用来自 UCI 机器学*库的最受欢迎的数据集之一——克里夫兰心脏病数据集。研究人员使用克利夫兰数据库预测了 303 名患者样本中的心脏病。尽管最初的 Cleveland 数据库包含 76 个变量(参见完整的变量列表这里,研究人员在建立预测模型时经常选择 14 个变量的子集(13 个特征和一个目标变量)(例如,参见 Shubhankar Rawat 的帖子关于走向数据科学)。因此,我们将使用相同的功能子集:
- 年龄:个人的年龄。
- 性别:个体性别:1 =男性;0 =女性。
- cp :个人经历的胸痛类型:1 =典型心绞痛;2 =不典型心绞痛;3 =非心绞痛性疼痛;4 =渐*。
- trestbps :个人入院时的静息血压值,单位为 mmHg。
- 胆固醇:血清胆固醇,单位为毫克/分升
- fbs :个人空腹血糖水平。如果空腹血糖> 120mg/dl 那么 1;否则 0。
- 静息心电图:静息心电图结果:0 =正常;1 =有 ST-T 波异常;2 =左心室肥大。
- thalach :个人达到的最大心率。
- exang :运动诱发心绞痛:1 =是;0 =否。
- oldpeak :运动相对于休息诱发的 ST 段压低。
- 斜率:运动 ST 段峰值的斜率:1 =上坡;2 =平;3 =下坡。
- ca :透视着色的主要血管数(0-3)。
- 地中海贫血:地中海贫血:3 =正常;6 =修复缺陷;7 =可逆缺陷。
- 目标:心脏病诊断:0 =无心脏病;1 =存在心脏病。
数据准备
在开始分析之前,我们将首先激活本例中需要的 R 包:
- dplyr (用于数据扯皮),
- 仿 (用于模拟新变量——详见下文),
- DataExplorer (用于探索性数据分析),以及
- 脱字符号 和 随机森林 (用于实现特征选择的 RFE)。
接下来,我们将把克利夫兰心脏病数据集导入到 R 中,并预览前六行。您可以在我的 Github 库中找到克利夫兰心脏病数据集的副本以及本文中使用的 R 代码。
克利夫兰心脏病数据集的前六行
为了让示例更有趣,我们将向数据集添加四个伪变量:
- 分类变量:随机分类变量,级别为 A、b 和 c,与目标变量( r = 0)无关
- 变量 1 :一个随机的连续变量,平均值为 10,标准差为 2,与目标变量无关( r = 0)
- 变量 2 :均值为 10、标准差为 2 的连续变量,与目标变量的相关性较低( r = 0.2)
- 连续变量 3 :均值为 10、标准差为 2 的连续变量,与目标变量的相关性适中( r = 0.5)
我们将在特征选择过程中使用这些变量,看看 RFE 是否会选择它们中的任何一个作为包含在模型中的特征。包括伪变量的新数据集在这里可用。
最后,我们将把分类特征和目标变量重新编码为一个因子,并对数据集中的数字特征进行居中(和缩放)处理。
探索性数据分析
使用 DataExplorer 包,我们将检查数据集中的变量。首先,我们将检查变量的类型。接下来,我们将为分类变量创建条形图。最后,我们将创建一个关联图,显示数据集中所有变量之间的相关性。
我们探索性分析的第一幅图证实了数据中的变量类型。数据中没有缺失值。一半的变量是数字的,而另一半是分类的。
数据集中的变量类型
条形图显示了数据集中的分类变量。这些图形对于检测变量的意外值特别有用。在我们的数据集中,所有分类变量的级别看起来都是正确的(基于上面的数据集描述)。对于两个变量( restecg 和 thal ),一些电平似乎具有非常低的频率。现在,我们将保留这些变量的原始级别,并在以后必要时折叠它们。
分类变量的条形图
相关矩阵图显示目标变量的两个级别(参见标记为 target_0 和 target_1 的行或列)似乎与数据集中的几个特征(例如 chol 、 oldpeak 和 thalach )具有中等相关性。根据这些特征之间的相互关系,它们很可能被选为 RFE 分析中的最佳特征。
所有变量的相关矩阵图
特征选择
使用数据集中的特征(即原始数据集中的 13 个特征和我们已经创建的 4 个伪特征),我们的目标是建立预测心脏病诊断的模型(0 =没有心脏病;1 =存在心脏病)。在第一部分中,我们将实施 RFE 来识别原始数据集中的哪些特征(即 13 个特征)可用于预测目标变量。在第二部分中,我们将把这四个伪特征添加到 RFE 模型中,并重复同样的分析。
为了实现 RFE,我们将使用插入符号包中的rfe
函数。该函数需要四个参数:
x
:特征的矩阵或数据帧y
:要预测的目标变量sizes
:特征选择过程中应保留的特征数量rfeControl
:特征选择算法的控制选项列表
所以,在使用rfe
功能之前,我们首先需要选择rfeControl
的控制选项。 caret 包包含了许多 RFE 算法,比如随机森林、朴素贝叶斯、袋装树和线性回归。在这个例子中,我们将使用“随机森林”(名为rfFuncs
),因为它有一个很好的计算特性重要性的内置机制。此外,我们将使用 5 次重复的 10 重交叉验证来提高 RFE 特征选择的性能。
接下来,我们将数据集随机分为训练(80%)和测试(20%)数据集,然后将特征和目标变量保存为单独的数据集(例如,特征的 x_train 和目标变量的 y_train)。
最后,我们将把所有东西放在一起,用 RFE 实现特性选择。在rfe
函数中,我们使用sizes = c(1:13)
,以便该函数尝试所有可能的解决方案(即,只有 1 个特征、2 个特征、……13 个特征)来找到最佳特征。或者,我们可以键入类似于sizes = c(1:5, 10, 13)
的内容,来尝试具有多达 5 个特性、10 个特性和 13 个特性的解决方案。
下图显示了从rfe
功能返回的输出。输出表明,RFE 为该模型推荐了 8 个特性(参见“Selected”列下的小星号)。当模型中保留 8 个特征时,准确率和 Kappa 都达到最大水平。
Recursive feature selectionOuter resampling method: Cross-Validated (10 fold, repeated 5 times)Resampling performance over subset size:Variables Accuracy Kappa AccuracySD KappaSD Selected
1 0.720 0.433 0.0833 0.169
2 0.715 0.418 0.0840 0.171
3 0.815 0.625 0.0851 0.171
4 0.802 0.599 0.0652 0.132
5 0.806 0.606 0.0764 0.154
6 0.813 0.621 0.0748 0.151
7 0.823 0.641 0.0677 0.137
**8 0.836 0.668 0.0672 0.135 ***
9 0.833 0.662 0.0779 0.156
10 0.827 0.650 0.0670 0.136
11 0.835 0.665 0.0659 0.133
12 0.835 0.665 0.0661 0.133
13 0.827 0.650 0.0714 0.144The top 5 variables (out of 8):
ca, cp, thal, oldpeak, thalach
我们还可以在下图中直观地看到相同的结果(蓝点代表最佳解决方案,即 8 个特征)。
基于特征数量的模型精度
基于特征数量的模型的 Kappa
现在,我们知道 RFE 选择了 8 个特征,但是这些特征是什么呢?predictors(result_rfe1)
给出了所选功能的列表。
[1] "ca" "cp" "thal" "oldpeak" "thalach" "exang" "slope" "sex"
接下来,我们将直观地检查所选特征的变量重要性,并查看哪些特征对于预测目标变量更重要。
下面的柱状图表明 ca (荧光透视着色的主要血管数量)是最重要的特征,其次是 cp (个体经历的胸痛类型)和 thal (地中海贫血状态)。
显示所选功能的可变重要性的条形图
我们还可以使用测试数据集来检查模型性能。准确性和 Kappa 值似乎与从训练数据集获得的相似。
Accuracy Kappa
0.8333 0.6564
如果我们在分析中也包括这四个伪特征呢?结果会改变还是保持不变?从逻辑上讲,我们希望 RFE 消除随机变量( catvar 和 contvar1 )以及低相关性变量( contvar2 ),但保留与模型中的目标变量( contvar3 )适度相关的变量。但是,从前面的分析中选择的特征还会被保留吗?让我们找出答案。
新分析返回的输出(有 17 个特性)显示 RFE 选择了一个有 5 个特性的解决方案(也就是说,一个更节省的解决方案!).准确性和 Kappa 值也有所提高——模型的改进并不令人惊讶,因为我们包括的一个伪特征( contvar3 )与目标变量有适度的相关性。因此,我们希望这个特性能够对模型性能产生积极的影响。
Recursive feature selectionOuter resampling method: Cross-Validated (10 fold, repeated 5 times)Resampling performance over subset size:Variables Accuracy Kappa AccuracySD KappaSD Selected
1 0.618 0.228 0.0845 0.171
2 0.750 0.491 0.0829 0.168
3 0.804 0.602 0.0638 0.129
4 0.844 0.684 0.0800 0.161
** 5 0.869 0.735 0.0738 0.149 ***
6 0.854 0.705 0.0782 0.158
7 0.852 0.700 0.0790 0.160
8 0.856 0.708 0.0736 0.149
9 0.857 0.711 0.0709 0.144
10 0.859 0.715 0.0770 0.157
11 0.858 0.713 0.0709 0.145
12 0.858 0.712 0.0700 0.142
13 0.863 0.723 0.0761 0.155
14 0.860 0.716 0.0753 0.153
15 0.866 0.728 0.0683 0.140
16 0.861 0.717 0.0752 0.154
17 0.862 0.719 0.0680 0.13
下面的条形图证实了我们的假设: contvar3 确实是被选中的特征之一。事实上,这似乎是模型中最重要的特征。正如我们所预料的,其他伪特征( catvar 、 contvar1 和 contvar2 )不在所选特征之列,因为它们与目标变量没有足够的关联。在添加了一个与目标变量适度相关的特征后,RFE 消除了一些不太重要的特征(例如 exang 、 slope 和 sex ),产生了一个更简化的解决方案,只有 5 个特征。总之,这个简单的实验证明了 RFE 在发现重要特征和消除不太相关的特征方面的敏感性。
显示所选功能的可变重要性的条形图
结论
总的来说,RFE 是在机器学*中为回归和分类任务找到最优特征集的有效方法。 caret 包提供了一个易于使用的函数来实现 r 中的 RFE。虽然我已经演示了如何使用带有默认选项的rfe
函数,但是也可以使用用户定义的函数来定制控制选项(参见 caret 手册的助手函数一节中的精彩示例)。我希望本文中描述的 R 代码和过程对您未来的项目有所帮助。
RFE 有很多种用法。在我网站上最*的一篇博文中,我展示了如何使用 RFE 来缩短考试时间,方法是在预测学生考试分数时选择最重要的问题(即特征)。
参考
[1]库恩,m .,&约翰逊,K. (2019)。特征工程和选择:预测模型的实用方法。CRC 出版社。
[2] Guyon,I .,Weston,j .,Barnhill,s .,& Vapnik,V. (2002 年)。基于支持向量机的癌症分类基因选择。机器学*,46 (1),389–422。
[3]库恩,M. (2020)。插入符号:分类和回归训练。可从https://CRAN.R-project.org/package=caret获得。
[4]库恩,M. (2019)。脱字包。可从 https://topepo.github.io/caret/index.html获得
你在任何 AWS 建议中都找不到的有效 IaC AWS 成本降低措施
如何在不购买保留实例的情况下大幅降低 EC2 成本。
简介
当开发一个新项目时,在 AWS 上从头开始建立云架构会很快导致成本快速增加。从第一天起,了解服务成本并采取节约成本的措施是至关重要的。
通常,客户希望尽可能地灵活,但由于需求快速变化,为他们的计算或数据库服务利用保留实例定价是不可行的。
在本文中,我将解释如何在不购买保留实例的情况下大幅降低 EC2 成本。
大小合适的实例
在讨论 AWS 服务的不同定价模式之前,有必要看一下通常由两种 AWS 服务提供的适当调整建议:
- 成本管理/合适的规模建议[2]
- 计算优化器[3]
考虑这些服务的建议可以从一开始就防止过度供应。
尽可能对所有非生产环境使用 Spot 实例
通常,在应用程序在生产环境中投入使用之前,会有一些部署阶段(开发、测试、UAT、试运行等)。).对于非生产环境,只要有可能就使用点实例被证明是有效的,尤其是当您使用无状态、容错的工作负载时([1])。
使用 spots 时,你要预料到它们可能会被打断。在选择实例类型之前,可以通过查看 AWS 的“spot Instance Advisor”[2】来评估 Spot 实例被中断的概率,该向导显示了与按需实例相比的节省,以及 Spot 实例被中断的概率,具体取决于地区。
欧盟-中欧-1(法兰克福)现场顾问 2021–08–05
上表显示了中断的可能性,并比较了从 AWS 文档[5]中收集的 3 种实例类型(m5.large、m4.large、c5.xlarge)的不同定价模式(按需、现货、预留 1 年全额预付)的降价情况。
使用/创建 spot 实例与相应的 terraform 资源有各种不同的方式:
- 单点实例/
aws_spot_instance_request
[6] - 现货舰队请求/
aws_spot_fleet_request
【7】 - 使用混合实例策略/
aws_autoscaling_group
[8]自动缩放组
使用自动缩放组并设置descired_capacity
、min_size
和max-size
不仅可以确保 spot 实例在被中断时被重新请求,还可以让我们非常容易地实现另一个节省成本的措施,我们将在下一节中看到如何实现。
通过用on_demand_base_capacity=0
、on_demand_percentage_above_base_capacity=0
配置aws_autoscaling_group
—资源中的instances_distribution
块,可以确保只使用点实例。将spot_allocation_strategy
设置为lowest-price
将进一步从定义的实例池中请求最便宜的实例类型。
instances_distribution {
on_demand_base_capacity = 0
on_demand_percentage_above_base_capacity = 0
spot_allocation_strategy = "lowest-price"
}
在非办公时间关闭所有非生产环境的实例
除了通过使用 spots 实现节约之外,在用户不工作的时间或节假日实施计划停机也是可行的。
"为什么要为根本没有使用的资源支付租金?"
对于 ASG 管理的实例,如使用具有混合实例策略的启动模板的方法所描述的,这可以通过自动缩放组的“预定动作”功能来实现[10]。
通过使用 terra form-resourceaws_autoscaling_schedule
【9】,您可以通过在 cron 表达式定义的特定时间设置所需的 ASG 大小参数来缩放 ASG。
让我们使用所描述的方法,将非办公时间定义为
- 周一至周五:20:00–06:00
- 星期六,星期日:00:00–24:00
一个实例的月成本比较
比较不同的成本节约选项,我们会发现将现场实例和计划关闭相结合明显优于标准保留实例定价(1 年全额预付)。
结论
AWS 文档包含许多优化成本的信息和文档,通常非常受欢迎。可以理解的是,并不是每种方法都被 AWS 正式推荐,但是在实践中证明是非常有效的。
我展示了一种方法,它结合了 spot 实例的使用,并使用 ASG 调度操作在非办公时间关闭它们,与按需定价相比,这大大降低了成本。
如果您在降低 AWS 基础设施成本方面需要更多帮助,请联系我们。
参考文献
[2]https://docs . AWS . Amazon . com/AWS count billing/latest/about v2/ce-right sizing . html
[3]https://aws.amazon.com/de/compute-optimizer/
https://aws.amazon.com/de/ec2/spot/instance-advisor/
[5]https://AWS . Amazon . com/de/ec2/pricing/reserved-instances/pricing/#
[10]https://docs . AWS . Amazon . com/auto scaling/ec2/user guide/schedule _ time . html
机器学*的有效测试(上)
测试 ML 系列
用于开发健壮的 ML 项目的渐进的、逐步的框架
图片作者。
更新:第二部现已出!
这篇博文系列描述了我在过去几年中开发的一个策略,用来有效地测试机器学*项目。考虑到 ML 项目的不确定性,随着项目的成熟,这是一个你可以采用的增量策略;它包括测试示例,为这些测试在实践中的表现提供了清晰的思路,一个用 Ploomber 实现的完整项目在 GitHub 上可用。到本文结束时,您将能够开发更健壮的 ML 管道。
测试 ML 项目时的挑战
测试机器学*项目具有挑战性。训练一个模型是一个长期运行的任务,可能需要几个小时来运行,并且有一个不确定的输出,这与我们测试软件需要的相反:快速和确定的过程。一年前,我发表了一篇关于测试数据密集型项目以使持续集成可行的文章。后来,我把这篇博文变成了一篇演讲,并在 PyData 2020 上发表。但是这些以前的工作只涵盖了测试数据管道的一般方面,而忽略了测试 ML 模型。
澄清一下测试和监控是两回事很重要。测试是一个离线的过程,它允许我们评估我们的代码是否做了它应该做的事情(例如,产生一个高质量的模型)。相比之下,监控包括检查部署的型号,以确保其正常工作。因此,测试发生在部署之前;监控发生在部署之后。
我在整篇文章中使用了术语管道和任务。任务是一个工作单元(通常是一个函数或一个脚本);例如,一个任务可以是下载原始数据的脚本,另一个任务可以清理这些数据。另一方面,管道只是一系列按照预先定义的顺序执行的任务。构建由小任务组成的管道的动机是使我们的代码更易于维护和测试;这符合我们的开源框架的目标,即帮助数据科学家使用 Jupyter 构建更易维护的项目。在接下来的部分中,您将看到一些示例 Python 代码;我们用 pytest ,pandas,还有 Ploomber 。
如果你想知道第二部何时上映,请订阅我们的 简讯 ,关注我们的 推特 或LinkedIn。
机器学*流水线的组成部分
在我们描述测试策略之前,让我们分析一下典型的 ML 管道是什么样子的。通过分别分析每个部分,我们可以清楚地陈述它在项目中的作用,并相应地设计测试策略。标准 ML 管道包含以下组件:
- 特征生成流水线。处理原始数据并将每个数据点映射到特征向量的一系列计算。请注意,我们在培训和服务时间使用这个组件。
- 训练任务。获取训练集并生成模型文件。
- 模型文件。训练任务的输出。这是一个单独的文件,包含一个带有学*参数的模型。此外,它可能包括预处理,如缩放或一键编码。
- 培训管道。封装了训练逻辑:获取原始数据,生成特征,训练模型。
- 服务管道。(也称为推理管道)封装了服务逻辑:获取新的观察,生成特征,通过模型传递特征,返回预测。
图片作者。
会出什么问题?
为了激励我们的测试策略,让我们列举每个部分可能出现的问题:
特征生成管道
- 无法运行管道(例如,设置问题、代码损坏)。
- 无法重现以前生成的训练集。
- Pipeline 产生低质量的训练数据。
训练任务
- 无法训练模型(例如,缺少依赖项、代码损坏)。
- 使用高质量的数据运行训练任务会产生低质量的模型。
模型文件
- 生成的模型比我们当前生产的模型质量低。
- 模型文件没有与服务管道正确集成。
服务管道
- 无法提供预测(例如,缺少依赖项、代码损坏)。
- 训练时的预处理和服务时间不匹配(又名训练-服务偏差)。
- 传递无效原始数据时输出预测。
- 传递有效数据时崩溃。
请注意,这不是一个详尽的列表,但它涵盖了最常见的问题。根据您的用例,您可能有其他潜在的问题,并且列出它们来相应地定制您的测试策略是至关重要的。
测试策略
在开发 ML 模型的时候,我们迭代的越快,成功的几率就越高。不像传统的软件工程项目,我们应该构建什么是很清楚的(例如,一个注册表单),ML 项目有很多不确定性:使用哪些数据集?有哪些功能可以尝试?用什么型号?由于我们事先不知道这些问题的答案,我们必须尝试一些实验,并评估它们是否会产生更好的结果。由于这种不确定性,我们必须平衡迭代速度和测试质量。如果我们迭代太快,我们就有可能写出草率的代码;如果我们花太多时间彻底测试每一行代码,我们就不能足够快地改进我们的模型。
这个框架稳步提高了测试的质量。该战略包括五个层次;当到达最后一层时,您有足够健壮的测试,允许您自信地将新的模型版本推向生产。
测试等级
- 烟雾测试。我们通过在每个
git push
上运行代码来确保代码正常工作。 - 集成测试和单元测试。测试任务的输出和数据转换。
- 分布变化和服务管道。测试数据分布的变化和测试我们可以加载一个模型文件并进行预测。
- 训练——发球歪斜。测试训练和服务逻辑是否一致。
- 模型质量。测试模型质量。
pytest 测试快速入门
如果你以前用过 *pytest*
,你可以跳过这一节。
测试是检查我们的代码是否工作的简短程序。例如:
测试是运行一些代码的函数,并且断言它的输出。比如前面的文件有两个测试:test_add
和test_substract
,组织在一个名为test_math.py
的文件中;通常每个模块都有一个文件(例如,test_math.py
测试一个math.py
模块中的所有功能)。测试文件通常放在tests/
目录下:
像 pytest 这样的测试框架允许你收集所有的测试,执行它们并报告哪些失败了,哪些成功了:
典型的项目结构如下所示:
src/
包含项目的管道任务和其他实用功能。exploratory/
包括探索笔记本,您的测试进入tests/
目录。src/
中的代码必须可以从其他两个目录中导入。最简单的方法就是打包你的项目。否则,你必须摆弄sys.path
或PYTHONPATH
。
如何浏览样本代码
此处提供了示例代码。存储库有五个分支,每个分支都实现了我将在接下来的章节中描述的测试级别。由于这是一个渐进的策略,您可以从第一个分支开始,向上移动到下面的分支,从而看到项目的演进。
该项目使用我们的开源框架 Ploomber 来实现管道。因此,您可以在pipeline.yaml
文件中看到管道规范。要查看我们使用哪些命令来测试管道,打开[.github/workflows/ci.yml](https://github.com/edublancas/ml-testing/blob/1-smoke-testing/.github/workflows/ci.yml)
,这是一个 GitHub actions 配置文件,告诉 GitHub 在每个git push
上运行某些命令。
虽然不是绝对必要的,但你可能想看看我们的 Ploomber 入门教程来理解基本概念。
请注意,这篇博文中显示的代码片段是通用的(它们没有使用任何特定的管道框架),因为我们希望用通用术语解释这个概念;然而,存储库中的示例代码使用 Ploomber。
一级:烟雾测试
冒烟测试是最基本的测试级别,应该在您开始一个项目时就实施。冒烟测试不检查代码的输出,而只是确保它能够运行。虽然这可能看起来过于简单,但这比根本没有测试要好得多。
记录依赖关系
在开始任何软件项目时,列出外部依赖关系是第一步,所以在创建虚拟环境时,确保记录运行项目所需的所有依赖关系。例如,如果使用pip
,您的requirements.txt
文件可能如下所示:
创建虚拟环境后,创建另一个文件(requirements.lock.txt
)来注册所有依赖项的已安装版本。您可以使用pip freeze > requirements.lock.txt
命令(在运行pip install -r requirements.txt
后执行)来实现,它会生成如下内容:
记录特定的依赖项版本可以确保来自这些包的任何更改都不会破坏您的项目。
另一个重要的考虑是保持你的依赖列表尽可能的短。通常在开发时需要一组依赖项,但在生产中不需要。例如,您可能将matplotlib
用于模型评估图,但您不需要它来进行预测。强烈建议拆分开发和部署依赖项。具有大量依赖项的项目增加了版本冲突的风险。
测试特征生成管道
项目中的第一个里程碑之一必须是获得一个端到端的特性生成管道。编写一些代码来获取原始数据,执行一些基本的清理,并生成一些功能。一旦您有了一个端到端的流程,您必须确保它是可重复的:删除原始数据,并检查您是否可以重新运行该流程并获得相同的训练数据。
一旦有了这些,就该实施我们的第一个测试了;使用原始数据的样本(比如 1%)运行管道。目标是让这个测试运行得更快(不超过几分钟)。您的测试将如下所示:
请注意,这是一个基本测试;我们不是在检查管道的输出!然而,这个简单的测试允许我们检查代码是否运行。每当我们执行git push
时,运行这个测试是必要的。如果你正在使用 GitHub,你可以用 GitHub 动作来完成,其他 git 平台也有类似的功能。
测试培训任务
生成特征后,训练模型。训练任务将训练集作为输入,并输出模型文件。测试模型训练过程是具有挑战性的,因为在给定一些输入(训练集)的情况下,我们不能容易地定义预期的输出(模型文件),这主要是因为我们的训练集变化很快(即,添加、移除特征)。因此,在这个阶段,我们的第一个测试只检查任务是否运行。由于我们忽略了输出(目前),我们可以用数据样本训练一个模型;请记住,这个冒烟测试必须在每次按压时执行。因此,让我们扩展之前的示例,以涵盖特征生成和模型训练:
在样本库中,我们使用 Ploomber,所以我们通过调用 [**ploomber build**](https://github.com/edublancas/ml-testing/blob/1-smoke-testing/.github/workflows/ci.yml#L20)
来测试特性管道和训练任务,它执行我们管道中的所有任务。
第二级:集成测试和单元测试
将管道模块化成小任务是必要的,这样我们就可以单独测试输出。在实现了第二个测试级别之后,您将实现两件事情:
- 确保用于训练模型的数据满足最低质量水平。
- 分别测试代码中具有精确定义的行为的部分。
让我们讨论第一个目标。
集成测试
测试数据处理代码是复杂的,因为它的目标是主观的。例如,假设我让你测试一个函数,这个函数获取一个数据帧,然后清理它。你将如何测试它?数据清洗的思想是提高数据质量。但是,这样的概念取决于数据和项目的具体情况。因此,由您来定义干净数据的概念,并将其转化为集成测试,尽管在这种情况下,我们可以使用术语数据质量测试来更加精确。
集成测试的思想适用于管道中的所有阶段:从下载数据到生成特性:由您来定义每个阶段的期望。我们可以在下图中看到集成测试的图形表示:
图片作者。
例如,为了向数据清理函数添加集成测试(让我们称之为clean
),我们在函数体的末尾运行一些检查来验证其输出质量。普通检查不包括空值、预期范围内的数字列或预定义值集中的分类值:
这种形式的测试不同于我们在第一部分中介绍的测试。单元测试存在于 *tests/*
文件夹中,可以独立运行,但是集成测试在您执行培训管道时运行。测试失败意味着您的数据假设不成立,必须重新定义数据假设(这意味着相应地更新您的测试),或者您的清洗程序应该改变以确保您的测试通过。
通过在每个任务的末尾添加 assert 语句,您可以在没有任何额外框架的情况下编写集成测试。然而,一些图书馆可以提供帮助。例如, Ploomber 支持在任务完成时运行一个函数。
单元测试
在管道中的每个任务中(例如在clean
中),您可能会有更小的例程;你的代码的这些部分应该作为单独的函数和单元测试来编写(例如,在tests/
目录中添加测试)。
编写单元测试的一个很好的选择是对列中的单个值应用转换。例如,假设您正在使用心脏病数据集,并创建一个函数来将chest_pain_type
分类列从整数映射到它们相应的人类可读值。您的clean
函数可能是这样的:
与一般的clean
过程不同,transform.chest_pain_type
有一个显式的、客观定义的行为:它应该将整数映射到相应的人类可读的值。我们可以通过指定输入和预期输出,将这转化为单元测试。
单元测试必须是所有即将到来的测试级别的连续工作流。所以,每当你遇到一个有精确目标的逻辑,就把它抽象成一个函数,单独测试。
参考
- 心脏病数据集从加州大学欧文分校机器学*知识库中检索,在 CC BY 4.0 许可下分发。
接下来是
到目前为止,我们已经实现了一个基本的策略,确保我们的特征生成管道生成具有最低质量水平的数据(集成测试或数据质量测试),并验证我们的数据转换的正确性(单元测试)。在本系列的下一部分中,我们将添加更健壮的测试:测试分布变化,确保我们的训练和服务逻辑是一致的,并检查我们的管道产生高质量的模型。
如果你想知道第二部何时上映,请订阅我们的时事通讯,在推特或 LinkedIn 上关注我们。
发现错误?点击这里让我们知道。
最初发表于ploomber . io。
机器学*的有效测试(下)
测试 ML 系列
用于开发健壮的 ML 项目的渐进的、逐步的框架。
图片作者。
介绍
在这个系列的第一部分中,我们从一个简单的冒烟测试策略开始,以确保我们的代码在每个git push
都能运行。然后,我们在它的基础上进行构建,以确保我们的特征生成管道产生最低质量水平的数据(集成测试),并验证我们的数据转换的正确性(单元测试)。
现在,我们将添加更健壮的测试:分布变化,确保我们的训练和服务逻辑是一致的,并检查我们的管道产生高质量的模型。
如果你想知道第三部何时上映,请订阅我们的 简讯 ,关注我们的 推特 或LinkedIn。
第 3 级:分销变化和服务渠道
测试目标变量分布
图片作者。
在前一级,我们引入了集成测试来检查我们对数据的假设。这种测试验证基本的数据属性,如 no NULL
s 或数值范围。然而,如果在监督学*问题上工作,我们必须额外注意目标变量并检查其分布,以确保我们不会在不正确的数据上训练。让我们看一个例子。
在以前的项目中,我需要用新数据更新我的训练集。在这样做并训练了一个新模型之后,评估指标提高了很多。我持怀疑态度,但在最*的代码更改中找不到任何问题。于是我从生产中的模型里抽出评测报告,和我训练过的对比。评估报告包括一个带有培训数据汇总的表格;目标变量的平均值大幅下降;
由于目标变量的平均值降低,回归问题变得更容易。想象一个数字变量的分布:一个预测零的模型将有一个等于分布的绝对平均值的平均误差;现在,假设您添加了最*生成的数据,这些数据进一步增加了目标变量的浓度(即,平均值降低):如果您评估的模型总是预测为零,MAE 将会降低,给人的印象是您的新模型变得更好了!
在与业务涉众会面后,我们发现数据源中最*的变化引入了虚假的观察结果。在这样的事件之后,我创建了一个新的集成测试,将目标变量的平均值和标准偏差与参考值(从当前生产模型使用的训练集中获得)进行比较:
参考值不要想多了;取你之前的值,加上一些公差。然后,如果测试失败,调查是否是由于数据生成过程中的根本变化(在这种情况下,您可能需要更新您的参考值),而不是由于一些数据错误。如果你正在处理一个分类问题,你可以比较每个标签的比例。
我们可以对所有特性应用相同的逻辑,并测试它们的分布:因为它们不应该从一次提交到下一次提交发生剧烈的变化。但是,如果您有数百个要素,手动计算参考范围将花费大量时间,因此至少要测试目标变量的分布。
注意,比较均值和标准差是一个简单(但幼稚)的测试;如果你不希望你的数据从一次迭代到下一次迭代有很大的变化,它会工作得很好,但是一个比较分布的更健壮和统计学原理的方法是 KS 测试。 这里有一个 KS 测试实现的例子。
测试推理管道
部署一个模型比加载一个.pickle
文件和调用model.predict(some_input)
要多几个步骤。在将输入数据传递给模型之前,您很可能必须对其进行预处理。更重要的是,您必须确保这样的预处理步骤总是发生在调用模型之前。为此,将推理逻辑封装在一个调用中:
一旦封装了推理逻辑,就要确保对无效的输入数据抛出错误。引发异常可以防止定义不明确的情况(由您来定义)。例如,如果您期望一个包含价格数量的列,如果它有负值,您可能会引发一个错误。
可能还有其他情况,你不想让你的模型做出预测。例如,当模型的输入在一个子群体中,而您知道该模型不准确时,您可能会提出一个错误。
以下示例代码显示了如何测试pytest
中的异常:
上面的例子是一种新型的单元测试;我们不是检查某个东西是否返回了特定的输出,而是测试它是否引发了错误。在您的ServingPipeline.predict
方法中,可能会有这样的内容:
要了解更多关于用pytest
、测试异常的信息,请点击这里。
这里有一个示例实现 ,它检查我们的管道在传递不正确的输入数据时抛出一个有意义的错误。
级别 4:培训-服务偏差
在这个层次上,我们测试我们的推理管道是正确的。我们必须验证两个方面来评估我们推理管道的正确性:
- 预处理在训练和服务时是一致的。
- 推理管道的特征生成部分正确地与模型文件集成。
训练发球偏斜
培训服务不对称是 ML 项目中最常见的问题之一。当输入数据在服务时间(与训练时间相比)进行不同的预处理时,就会发生这种情况。理想情况下,我们应该在服务和训练时共享相同的预处理代码;然而,即使是这种情况,检查我们的训练和服务代码以相同的方式预处理数据仍然是必要的。
测试如下:从您的原始数据中采样一些观察值,并通过您的数据处理管道传递它们,以获得一组(raw_input, feature_vector)
对。现在,取同样的输入,通过你的推理管道,并确保两个特征向量精确匹配。
这里是一个测试 的示例实现,它来自我们的示例存储库,检查没有训练服务偏斜。
为了简化培训和服务管道的维护,请查看 Ploomber,我们的功能之一是将基于批处理的培训管道转换为内存中的管道,而无需更改代码。
特征和模型文件集成
假设我在一个月前部署了一个使用了十个特性的模型。现在,我正在开发一个新的,所以我将相应的代码添加到培训管道并部署:生产中断。发生了什么事?我忘了更新我的推理管道来包含最*添加的特性。因此,添加另一个测试来确保您的推理代码正确地与训练管道生成的模型文件集成是非常必要的:推理管道应该生成与模型被训练时相同的特性。
在我们的示例项目中,我们首先用数据样本训练一个模型,然后我们确保可以使用训练好的模型来生成预测。 这样的测试 是在 **ci.yml**
文件中实现的,它在每个 **git push**
上执行。
级别 5:模型质量
在部署模型之前,有必要对其进行离线评估,以确保它至少具有与基准模型相同的性能。基准模型通常是生产中的当前模型。这个模型质量测试帮助您快速确保将一些候选模型发布到产品中是可接受的。
例如,如果您正在处理一个回归问题,并使用平均绝对误差(MAE) 作为您的度量标准,您可以计算整个验证集和一些感兴趣的子群体的 MAE。假设您部署了一个模型,并计算了您的 MAE 指标;经过一些工作之后,您添加了一个新的特性来改进模型度量。最后,您将训练一个新模型并生成指标:
你如何比较这些结果?乍一看,您的模型似乎是有效的,因为它降低了前两个指标的 MAE,尽管它增加了第三组的 MAE。这种模式更好吗?
仅凭一组参考值很难判断。因此,与其只有一组参考值,不如通过多次使用相同的参数训练生产模型来创建一个分布。假设我们对基准模型和候选模型重复训练过程三次,这次我们得到三个数据点:
我们现在有了一个经验分布。然后,您可以比较您的候选模型中的指标是否在观察到的范围内:
请注意,我们正在评估两边的current
;这是很重要的,因为我们希望测试在我们的模型性能下降和急剧上升时提醒我们。虽然性能提高是个好消息,但我们应该确保这种提高是由于一些特定的改进(例如,对更多数据进行训练,添加新功能),而不是由于方法错误,如信息泄露。
注意:使用最小和最大度量值来评估模型性能是一种简单的入门方法,但是,在某些时候,您可能想要实现更具统计学原理的方法, 本文 回顾了一些实现方法。
在每次代码变更时测试模型质量是至关重要的。例如,假设您已经有一段时间没有运行测试了;自从上次运行以来,您增加了训练集的大小,添加了更多功能,并优化了一些数据转换的性能。然后,您训练一个模型,并且测试中断,因为管道产生了一个具有更高性能的模型。在这种情况下,您可能需要从以前的提交中训练模型,以了解是什么操作导致模型的性能超出了预期范围。与您测试每个变化的场景相比:如果您的模型在添加更多的训练数据时提高了性能,这是一个好消息!但是,如果您提高了某些数据转换的性能(例如,使用更少的内存),并且您的模型突然变得更好,那么就有理由持怀疑态度。
从统计学的角度来看,要比较的指标越多,您的测试失败的几率就越高。没有明显原因就失败的测试并不好。为了提高这种测试的可靠性,您有两种选择:
- 生成更准确的经验分布(例如,将基准模型训练 50 次,而不是 10 次)。
- 减少要比较的指标数量。
对于关键模型,用一些手工质量评估来补充前面的模型质量测试是一个好主意。手动评估很有帮助,因为有些模型属性更难自动测试,但通过人工视觉检查可以很快显示出来;一旦您检测到任何要跟踪的关键指标,您就可以将它们包含在自动化测试中。
快速生成模型评估报告的一种方法是将您的培训代码作为笔记本运行;例如,假设您的train.py
脚本如下所示:
您可以使用 Ploomber ,而不是编写额外的代码将图表和表格保存在.html
文件中,它会在运行时自动将您的.py
转换为.ipynb
(或.html
)文件,因此您可以在每次训练新模型时轻松获得模型评估报告。也可以使用 sklearn-evaluation ,可以比较多个.ipynb
文件(见本例)。
下一个
在第二部分中,我们增加了测试的健壮性:我们检查目标变量的分布变化,确保我们的训练和服务逻辑是一致的,并检查我们的管道产生高质量的模型,从而结束我们的 5 级框架。
在本系列接下来的(也是最后的)部分,我们将提供额外的建议来建立一个简化的工作流,允许我们重复develop -> test -> improve
循环:我们修改我们的管道,确保它正确工作,并继续改进。
如果你想知道第三部何时上映,请订阅我们的时事通讯,在推特或 LinkedIn 上关注我们。
发现错误?点击这里让我们知道。
最初发表于ploomber . io。
Python 中多维图像数据的有效可视化
获得多维图像数据?Python 可以帮忙!
安德烈·利亚科夫在 Unsplash 上拍摄的照片
一般来说,多维图像数据难以可视化。
在科学成像中(或者说在大多数成像领域),多维图像是非常常见的。额外的维度可以是来自物理第三维度(“Z 轴”)的任何维度,其中 2D 图像是在不同深度拍摄的;时间维度,在不同的时间间隔拍摄 2D 图像;例如原子力显微镜或 RGB 图像中的不同通道。
我们将使用基于matplotlib
的 Python 中的开源图像可视化库seaborn-image
。
它深受流行的
*seaborn*
库的启发,用于统计可视化
装置
pip install -U seaborn-image
你可以在 GitHub 上了解更多关于
*seaborn-image*
项目的信息
加载样本 3D 数据
*import* seaborn_image *as* isnscells = isns.load_image("cells")cells.shape
(256, 256, 60)
设想
我们将使用seaborn_image
中的ImageGrid
来可视化数据。它将在网格上绘制一系列图像。
首先,我们将只使用slices
关键字参数绘制一些选定的切片。
g = isns.ImageGrid(cells, *slices*=[10, 20, 30, 40, 50])
默认情况下,切片是沿着最后一个轴进行的。然而,我们可以使用axis
关键字参数将它们带到另一个维度。
g = isns.ImageGrid(cells, *slices*=[10, 20, 30, 40, 50], *axis*=0)
我们还可以分别使用start
、stop
和step
参数指定不同的开始/停止点以及步长。
在下面的代码中,我们从第 10 个片段开始,以 3 为步长一直到第 40 个片段。
如果没有指定,切片和步骤在最后一个轴上进行。
g = isns.ImageGrid(cells, *start*=10, *stop*=40, *step*=3)
我们也可以绘制所有的图像,而不需要任何索引或切片。
g = isns.ImageGrid(cells, *cbar*=False, *height*=1, *col_wrap*=10)
注意——我们改变了单个图像的高度和图像列的数量。
转换
最后,我们还可以对图像进行变换并将其可视化。这里,我们将使用scikit-image
中的adjust_gamma
功能调整曝光。
我们可以通过将函数对象传递给map_func
参数来实现这一点。函数对象的附加参数可以作为关键字参数传递。
*from* skimage *import* exposureg = isns.ImageGrid(
cells,
*map_func*=exposure.adjust_gamma, *# function to map
gamma*=0.5, *# additional keyword for `adjust_gamma`
cbar*=False,
*height*=1,
*col_wrap*=10
)
ImageGrid
返回一个seaborn_image.ImageGrid
对象,是一个图形级函数,即生成一个新的matplotlib
图形。我们可以分别使用fig
和axes
属性访问图形和所有单独的轴。这意味着对于任何在seaborn-image
中不直接可用的定制(参见文档,我们可以下降到matplotlib
并使用其强大的 API。
总的来说,正如我们在这篇文章中看到的,seaborn-image
通过为快速、有效和吸引人的图像数据可视化提供高级 API,使我们变得更加高效。
你可以在 GitHub 上找到更多关于seaborn-image
项目的信息。
感谢阅读!
原载于 2021 年 2 月 25 日https://sarthakjariwala . github . io。
短时间内学* Python 的有效方法
从 python 编程初学者到专家的循序渐进指南
Python 是 21 世纪最受欢迎的编程语言之一。学* Python 可以让你在软件工程师的职业生涯中占据优势。
但是,作为初学者,我们发现很难决定正确的学*路径,我们通常会浪费大量的时间来决定更好的学*资源。
这在大多数程序员身上都可以观察到——当他们开始学*新东西时,他们通常会在频道或硬盘上堆积不同的课程,这不是一个明智的做法。
在这篇文章中,我将谈论即使作为初学者,如何以最有效的方式学* python。我试图根据你的兴趣领域给出一个学*路径的简要概述,以及一些让学*过程更有趣的技巧和诀窍。
先了解一下语言
在学*语言之前,我们必须先了解语言是如何使用的,在哪里使用。Python 在 web 开发、数据科学、机器学*、网络工程等各个领域都有应用。同时学*所有领域是不可能的,所以我们必须专注于我们需要学*的关键概念,并据此进行。
例如,对于 web 开发,我们需要学*面向对象编程和 Django/ Flask ,而对于数据科学,我们需要学* Numpy、Pandas、Matplotlib 等以及基本的 Python 语法。
学*基础知识
下一步是熟悉基本的 Python 语法、数据类型、条件语句、循环和各种其他 Python 操作。
由于 python 简单且*乎英语的语法,即使是初学者也能在一两周内学会。尽管这些概念中的一些乍看起来非常简单,但是为了获得无缝编程体验,您必须正确理解它们。
这里有一本关于 Python 编程的书,我肯定会推荐给所有初学者。而对于高级学*者看一看这本书。
边学边实施
学*任何东西的最好方法是实现它,Python 也不例外。无论你是从网上课程还是从书上学*,你都应该接触它。
只要打开你的电脑,设置你的编码环境,开始编码。例如,如果你已经学*了条件循环,试着用它来做一个数字猜谜游戏。
您还可以使用 if-else 语句定制它,以获得更好的体验。如果你正在学*一个新的 Python 库,你可以用它做一个小项目。
这将使对这个概念的理解提高十倍。
构建结构良好的项目
这一点只有在你很好地掌握了 python 的基础之后才适用。
构建一个结构良好的项目会让你对复杂的概念有一个很好的了解,比如 OOP、文件处理数据库、并发性和多线程。尝试建立一个与你感兴趣的领域相关的项目。
例如,如果你对 web 开发感兴趣,那么使用 Django 或 Flask 构建一个 web 应用程序。
数据科学和机器学*爱好者可以开发手写识别、未来股票价值预测等项目。
这些 Keystone 项目不仅能帮助你获得该领域的深入知识,还能为你的简历加分。
学*特定领域的概念和库:
当在现实世界中工作时,你不能只理解 python。我们需要学*一些特定于该领域的概念和 Python 库来开发应用程序。
这一步只适用于精通 Python 概念的开发人员。
例如,如果您对数据科学领域感兴趣,那么除了核心 Python 之外,您还需要学*与之相关的概念和库:
● 数据处理和清理:数据科学最重要的部分是组织数据和清理数据(去掉不需要的数据)。为此你应该学*的 Python 库是 Numpy 和 Pandas 。
● 数据可视化:数据科学的另一个重要方面是数据可视化,即以图表、条形图、直方图等形式表示数据。为此你需要学*的 Python 库是 Matplotlib 。
● 分析和 ML: 为了使用机器学*从数据中找到模式,我们需要学* Python 框架,如 Scikit-learn、Tensorflow 等。
这里有一本关于 python 编程数据科学的书,我一定会推荐给所有数据科学爱好者。
结论
尽管 python 是一门简单的语言,即使对初学者来说也是如此,但是要熟悉 Python 的所有基本概念可能需要花费大量的时间。
使用上述方法,您可以轻松地学* python 的核心概念和特定于领域的内容。
建议 参考这本书 或网上课程(YouTube 上有很多)有条理地获取所有内容。
Python 的官方文档也是一个很好的参考资料。主要的焦点应该放在实现你所学*和记住的东西上,学*没有捷径。
注:
本文包含代销商链接。这意味着,如果你点击它,并选择购买我上面链接的资源,你的订阅费的一小部分将归我所有。
然而,推荐的资源是我亲身经历的,并在我的数据科学职业生涯中帮助了我。
在你走之前……
如果你喜欢这篇文章,并且想继续关注更多关于 Python &数据科学的精彩文章——请点击这里https://pranjalai.medium.com/membership考虑成为一名中级会员。
请考虑使用我的推荐链接注册。通过这种方式,会员费的一部分归我,这激励我写更多关于 Python 和数据科学的令人兴奋的东西。
还有,可以随时订阅我的免费简讯: Pranjal 的简讯 。
数据科学在线网络的有效写作
如何让冷冰冰的邮件和信息发挥作用
当今世界是一个网络化的世界。老实说,你认识的人越多,你就越容易找到工作,找到工作,过好自己的生活。但是本地聚会、会议和社交活动的日子已经一去不复返了。感谢伟大的在线平台(LinkedIn、Twitter、Instagram 等。)让我们保持联系,即使世界因为 Covid 而关闭。
在我的职业生涯中,我把我获得的大多数机会或面试归功于在 LinkedIn 上或通过冷冰冰的电子邮件建立关系网或接*人们。
这篇文章是我在网上接触人们时学到的。什么增加和减少了你得到回复的机会。我的经验主要基于电子邮件和 LinkedIn,但我确信这些技巧适用于任何平台和领域。这些将帮助你充分利用你的关系。
接*某人时不要做的事情
这部分非常主观,基于我自己的看法。但是遵循这些确实帮助我得到了回复,并导致了富有成效的讨论。
使用称呼
为了避免在你知道某人喜欢的代词之前不小心冒犯了他,使用中性代词是很重要的。除非对方表明自己是男是女,否则最好称呼对方的名字。
就像在这种情况下,这个人称呼我为先生。这是我们群发邮件时很常见的错误。
不要仅仅发送问候
我不确定如何以及为什么回复没有正文的消息。我在 LinkedIn 上得到这些,这是一个专业的网络平台。不要浪费你的时间发送嗨,你好的信息,并期待一个回应。可操作的项目总是有助于保持直截了当的对话。
撰写冗长的非结构化邮件
我对这条信息的唯一问题是它的结构不良。如果把信息写在一个点上,会更好地表达出来。
嘿,你介意回答以下问题吗-
如何进入数据科学——学*使用库还是专注于基础?
理解每个人的时间都是宝贵的,以更好的方式提出问题会节省时间(对你和你想从中获得价值的人都是如此)。如果把这个问题设计得更好,我本可以更好地回答它。
保持电子邮件/信息简短明了,便于阅读。
询问手机号码
这个问题我已经被问过很多次了,最终,我只是忽略了这样的请求。我明白有时候问号码并不是出于恶意,但仍然尊重某人的隐私应该是非常明显的。大多数时候,这个人会说出联系他们的最佳方式。难道你不能礼貌地问~‘我联系你的最佳方式是什么?’
问抽象的问题
当然,这条信息是以“嗨,Purvanshi”开始的,我不知道如何回复。第二条信息更有针对性,但不具体。
- 稍微说说我的经历 ~专业上?一切都已经在我的 LinkedIn 上了,还是说这是为了申请研究生院?如果是这样的话,这个人到底在寻找什么样的问题?帮助选择大学?还是迎合了签证程序?基本上,缺失的一点是经验与什么有关?
- 我申请 时的个人资料~还是不确定我的 LinkedIn 个人资料上没有哪个部分。也许这个人想让我在申请研究生之前列出我的经历?
这种开放式的问题令人困惑,难以回答。问清楚具体的问题是最好的方法。
如何构建消息/电子邮件
接*一个人背后的心理很重要。因此,始终认为对方的时间是宝贵的,你想用简洁的信息抓住他/她的注意力。构造到点消息 s 是关键。
以磅为单位书写
如果你有问题,以要点的形式提出来。
如果你有很多事情要讨论,就用要点来写。
如果你想写一些不相关的话题,插入段落。
你也可以给你的段落加上粗体标题。要点不仅有助于快速略读,也有助于更好地回复。
这种特殊的信息有针对性的问题,使人们更容易回答。
给出一个共同的参考
有时给出一个共同的参考,比如同一所大学/同一家公司,或者如果你在会议上见过这个人,有助于吸引注意力。一个常见的参考也可以是这个人正在研究的主题/他开源的软件/他写的你也有兴趣了解的论文。
这里有一个例子-
这封邮件有一个很好的开头参考,分为几个段落,这样更容易阅读,最后有可操作的内容“如果你不介意跳过一个电话”。
这个人不会假设我会准备好接电话,而是礼貌地询问。
提高问题的质量
问题越有针对性,越具体,你从谈话中获得的价值就越大。这是一个例子—
前几天有人打电话问我~
你能谈谈微软的文化吗
这个问题没有错,但我回答说这很好,人们非常支持等等。没有提取多少价值。
为了更有针对性,我会问-
- 你平均工作多少小时?
- 截止日期有多难?你有时必须随叫随到吗?
- 在分配给你的工作中,团队在多大程度上考虑了你的个人选择?
这些更有针对性,可能会给你提供更多的信息。
结论
在线社交是一种技能,如果学会了,机会是无穷无尽的。没有人被限制去接*任何人,但是用对你有益的正确方式去做是需要学*的。这些是我从自己的经历中学到的一些技巧。如果有任何其他对你有用的,请随时给我发关于 purvanshi.mehta11@gmail.com 的信息,我会在这篇文章中注明。
过滤的 HNSW 搜索对回忆和潜伏期的影响
一个实验,看看松散和限制性过滤器如何影响矢量搜索速度和质量。
本文包含了一个实验,该实验是关于将过滤器应用于基于 HNSW 的向量搜索如何影响召回率和查询速度的。使用一个简单的技巧,我们可以在任何类型的过滤器上实现高召回率和低响应时间——而不需要预先知道过滤器是什么样子的。
过滤的 HNSW 向量搜索—对召回和查询时间的影响。图片作者。
向量搜索现在是一个热门话题,就召回/延迟权衡而言,HNSW T1 是 ANN 索引模型的 T2 顶级竞争者 T3 之一。如果增量构建和 CRUD 操作很重要——矢量搜索引擎通常就是这种情况——HNSW 通常被认为是最佳选择。在现实生活中,向量搜索经常需要过滤。例如,在一个电子商务应用程序中,用户可能希望找到与当前购物车中的商品相关的商品(向量搜索),但根据可用性和利润率对它们进行过滤(结构化搜索)。
可以调整 HNSW 以允许这种结构化过滤,但这带来了这种过滤对查询速度和召回率的影响的问题。现有的基准只衡量未过滤搜索的这些权衡。在本文中,我们将使用开源矢量搜索引擎 Weaviate 运行一些过滤矢量搜索实验,该引擎支持这些开箱即用的操作。我们将应用不同限制性的过滤器,并将查询速度和召回率与未过滤的向量搜索进行比较。
后置过滤与前置过滤
通常,有两种关于过滤向量搜索的范例;在矢量搜索已经完成之后或之前应用过滤器。如果过滤器的限制不是很严格,即当它包含大部分数据集时,后过滤可能会工作得很好。然而,当过滤器变得非常严格时,过滤后的召回率急剧下降。想象一个场景,其中过滤器仅匹配数据集的 5%。现在执行 k=20 矢量搜索。假设数据是均匀分布的,我们希望结果集中只包含一个匹配,然而用户要求 20 个结果。
当过滤器变得更加严格时,后过滤会导致非常低的召回值。因此,需要预过滤结合有效的向量索引来实现更大的整体召回率。
这表明,如果我们想要解决所有限制级别的过滤器,后置过滤不是可行的选择。相反,我们需要应用预过滤。一些作者认为,术语“逐级过滤”意味着不能再使用人工神经网络模型。然而,这不是本文中使用的定义。我们使用术语预过滤来表示有效的“传统”索引结构,例如倒排表,可以用于确定允许的候选集合。然后这个列表被传递给 ANN 模型——在这个例子中是 HNSW。
测试设置
对于我们的实验,我们选择了以下设置:
- 使用 Weaviate v1.8.0 运行,该版本使用带有扩展过滤和 CRUD 支持的定制 HNSW 实现。
- 100 个过滤器,限制性增量为 1%(0%限制性->100%数据集包含在过滤器中,99%限制性-> 1%数据集包含在过滤器中)
- 带有随机 256d 向量的 250k 对象
- Weaviate 的默认 HNSW 构造设置(
efConstruction=128, maxConnections=64
) - 1 个弱服务器节点上的 1 个类碎片
- 在配有四核英特尔酷睿 i7 CPU 的 2014 款 iMac 工作站上运行
有些选择是随意的,可能会影响结果。例如,使用更现代的 CPU 会导致查询时间略微加快,但不会影响召回。使用更大的数据集会导致查询时间稍长,等等。它们是代表许多用户需求的起点,可以针对其他用例进行调整。
结果
回忆
在详细查看查询时间之前,我们想看一下不同限制的过滤器的召回。下图在左侧显示了一个非常宽松的过滤器(0%限制度),在右侧显示了一个非常严格的过滤器(99%限制度),以及其间的各种步骤。已经针对 k=10、k=15 和 k=20 的大小执行了查询,这将是搜索结果页面上的典型长度。
召回过滤向量搜索与削弱各种水平的限制。x 轴的左端表示宽松的过滤器,其中包含 100%的数据集,中心表示包含 50%,右端表示仅包含 1%的数据集。图片作者。
正如我们所看到的,召回率在天平的两端都没有显著下降。x 轴的左边缘表示匹配 100%数据的筛选器。换句话说,它相当于一个未过滤的矢量搜索。这些回忆值作为我们比较的基线。随着过滤器越来越严格,线条有时会下降到基线以下,有时会上升到基线以上。但平均而言,过滤似乎不会对矢量搜索的回忆产生负面影响。有趣的是,随着过滤器变得更加严格(少于所包含的数据集的 20%),召回是完美的(100%)。请注意,当禁用 HNSW 切断时也是如此,这将在下面解释。
查询速度
知道了召回不会受到很大影响——即使应用非常严格的过滤器——我们现在可以将注意力转移到查询速度上。下面显示了与上面相同的过滤器的查询速度。速度由三个要素组成:
- 倒排索引 这表示为所有候选人建立一个允许列表所需的时间。这发生在列表被传递给 HNSW 实现之前。它使用磁盘上的倒排表。
- HNSW
这表示执行过滤的 HNSW 搜索所需的时间。 - 对象检索
各种过滤向量搜索的查询速度(毫秒)。因为搜索(几乎)是不受限制的(最左边),所以大部分时间都花在用倒排索引构建允许列表上。随着过滤器变得更加严格,HNSW 搜索时间会稍微增加,但是总时间不会超过初始值。图片作者。
上图强调了以下几点:
- 无论使用何种过滤器,搜索时间通常不会超过 30 毫秒,平均值远低于 20 毫秒。注意:有一些随机峰值是由后台任务和/或垃圾收集造成的。注意,实验是在具有各种后台任务的 macOS 工作站上执行的,而不是在专用硬件上。
- 使用倒排索引从磁盘构建允许列表的成本大致呈线性下降,因为过滤器变得更加严格,从而减少了潜在匹配的数量。
- HNSW 索引在搜索未过滤时效率最高(最左侧)。随着过滤器变得更加严格,搜索 HNSW 索引的成本会略微增加,但通常会被构建倒排索引的成本减少所抵消。
- 垂直虚线突出显示了一个可配置的截止值。此时,Weaviate 在内部切换搜索机制。这种截止是完全可配置的,将在下一节中详细解释。在我们的实验中,它被设置为 40,000 个对象或总数据集大小的 15%。
非常严格的过滤器和引入平面搜索截止的影响
在上一节中,我们强调了 Weaviate 会在指定的截止点自动切换搜索机制。为了理解为什么会发生这样的转变,我们需要看看如何使用 HNSW 执行过滤搜索。
HNSW 是一个基于图形的索引模型。这意味着图形中的节点被放置成使得相似的对象位于附*。每个节点都有一组到相邻节点的边。为了避免图形变得过于庞大,HNSW 以两种方式限制边的数量;对于边的数量有一个固定的限制,并且有一个启发式方法来确定最有价值的连接。当由于连接变得太大而需要修剪连接时,HNSW 将删除那些离现有邻居比离节点本身更*的元素。例如,在非常简单的图中,A--B--C
没有必要将 A 连接到 C,因为只需从 A 增加一跳(通过 B)就可以有效到达 C。这被称为小世界现象,它代表 HNSW 中的“SW”。
在搜索时,HNSW 总是从一个特定的入口点开始,并评估它的相邻连接。与指定的查询向量最接*的被添加为候选。在任一点保留的候选项的数量由查询时间ef
参数控制。如果添加新的候选者不再改善候选者集合中的最差距离,则搜索完成。这就是为什么 HNSW 是一个*似的索引,并且比暴力破解要快得多。此外,该图由多个层组成(HNSW 中“H”的“分层”)。这允许在包含较少点并且边相当长的较高层上在图形的正确方向上有效地移动。
无截止的过滤搜索。图片作者。
过滤搜索时,不在允许列表中的候选人不会被添加到结果集中。这意味着,随着过滤器变得非常严格(85%及以上),HNSW 搜索将找到越来越多从距离角度来看可行但被过滤器排除在外的候选。如果我们从上面显示的搜索中移除截止点,我们可以看到当我们移动到右边时,查询时间急剧增加(非常严格的过滤器)。
相比之下,暴力搜索会是什么样子呢?图片作者。
然而,由于 Weaviate 还具有倒排索引的特性,并且在开始向量搜索之前就已经确定了候选项,所以我们现在可以利用这少量的数据点。平面搜索(强力搜索)的成本与数据集的大小成线性关系。侧边的图表显示,平面搜索遵循与 HNSW 搜索相反的模式。随着我们对过滤器的限制越来越多,查询时间会减少。为了证明蛮力的成本是过滤器限制性的函数,我们将 Weaviate 中的截止值设置为 250k,这意味着对于每个过滤的向量搜索,HNSW 索引都被有效地跳过。
这清楚地表明,在大多数情况下,平面搜索查询时间要差得多。然而,在 HNSW 苦苦挣扎的那些案例中,平面搜索大放异彩。因此,一个简单的从一个切换到另一个的截止点允许在任何限制级别上进行非常有效的搜索。
结论
这个实验表明,当使用 Weaviate 的混合方法时,对于大多数过滤的搜索使用 HNSW 索引,但是对于极其有限的搜索切换到平面搜索,我们可以在任何过滤器上实现很大的召回和查询时间。
召回率在整个过滤器集合中大致恒定,并且处于与未过滤的对照搜索相当的水平。实验中的查询时间总是低于 30 毫秒,平均查询时间远低于 20 毫秒,这对于延迟最关键的用例来说已经足够快了。
要了解更多关于 Weaviate 的信息,您可以浏览文档。
基于 Spark 和 Postgres 的 HLL 的有效基数估计
帮助您使用超对数算法进行端到端有效基数估计的完整指南。
问题陈述
基数估计问题是寻找数据块中不同元素的数量的问题,其中相同的元素被重复多次。这个问题非常普遍,在现实世界中有很多应用,比如网站的活跃用户、独特的实时会话等等。
这个问题似乎是小孩子的游戏,但我们必须将所有的值保存在一个集合中,以跟踪我们已经考虑的所有值,当我们谈论数百万或数十亿用户/会话时,这最终将是一个问题。我们当然不能在内存中保存和处理具有如此大基数的集合。
可能的解决方案
我们可以使用以下任何一种方法来进行基数估计:
- 独特计数
- 线性计数
- 对数对数算法
- 超对数算法
不同计数
寻找基数的蛮力方法,我们上面讨论的关于保持所有唯一元素在一个集合中并找出基数的方法。就时间和空间复杂性而言,它是线性的 O(n ),按原样存储所有对象。
线性计数
该技术使用容量为 B 位的哈希表,所有位初始化为 0。当一个条目到达时,我们对它进行哈希运算,该位的值被改为 1。重复数据删除通过对输入进行哈希处理来实现。该算法在时间和空间复杂度方面也是线性的,但是占用的空间少得多,因为我们只保存比特而不是精确的值。现在,让我们比较哈希表的大小和我们可以拥有的唯一元素的大小。假设您的集合中有 N 个唯一的条目:
- 当 N << B, then hash collisions would be less and the count of 1 bit in the hash table would be a good estimate of the cardinality.
- When N ≈ B, then a significant number of hash collisions would surely occur. But we can estimate the number of collisions by checking how full the hash table is. Using this estimation of the number of collisions we can extrapolate to the approximate cardinality of the set.
- When N > > B 时,散列表中的每一位都将被设置为 1,这样就不可能计算出真正的基数。
当我们知道先验的最大可能的独特元素时,这种方法似乎是一个很好的选择。
对数对数算法
这种算法也是基于散列的,它跟踪散列值的二进制表示中出现的零的最大数量。让我们假设出现的零的最大数量是 k,那么估计计数将是 2^k.
它基于这样的想法:一半的值以 1 开始,四分之一以 01 开始,八分之一以 001 开始,依此类推,得到 k 个前导零的概率是 1/2^(k+1).现在,这种估计会非常不稳定,并且会有非常高的方差,因为我们可以让第一个哈希值也有 5 个前导零。
为了使它更稳定,我们采用由多个散列函数给出的估计值的平均值(算术平均值)。把我们带到另一个计算量很大的过程,哈希。现在,为了解决这个问题,LogLog 将从已经散列的值获得的值转换成 2 个值,取前 m 位,它指向一个特定的值桶,然后取剩余的位作为获得最大数量的零的值。
因此,如果我们假设我们的散列函数返回 32 位,并且我们用前 14 位来决定它所指向的桶,那么我们将有 2^14 桶来平均 k 的值。
假设在每个桶中,我们想要存储 N 个元素,那么我们将必须只存储 log(N)数量级的单个数字,并且为了存储,我们只需要 log(log(N))个比特,这就是它的名字。
举个例子,如果我们为每个桶取 5 位寄存器来存储该桶和 2^14 桶的 k 的最大值,这将允许我们为每个桶取从 0 到 31 的值,这意味着我们可能在每个桶中有 2^31 元素(使用上述概率),这意味着我们可以仅通过使用空间(214 * 5) = 81920 位(10 KBs)来*似地将 2^(31+14 的不同元素保持在最大值。
它将估计数量定义如下:
其中,α →估计因子(常数值)
寄存器(i) →第 I 个桶的最大前导零计数的值
超对数算法
这是一个对数对数算法的改进算法。当组合多个桶的结果时,它使用调和平均值代替算术平均值,这迎合了最大前导零计数的异常高的值。此外,它还提供了对两种极端情况的修正,一种是不是所有的桶都被占用,另一种是哈希冲突导致低估。
超对数实现
不同的图书馆都在实现 HLL 算法,我们选择使用聚合知识的 HLL 实现,原因如下:
- 有一个可与 Postgres DB(PostgreSQL-hll)互操作的库,同样被 Swoop 的 Spark-Alchemy 使用。
- 有能力联合两个 hll。
- 易于使用的 API。
履行
要在 Mac 系统上试用 postgresql-hll 扩展,请使用以下步骤:
所以,扩展安装在 Postgres 中。现在,我们需要运行以下查询来激活特定 DB 中的扩展。
为了测试上面的示例,使用以下查询创建一个表并插入一些数据:
然后尝试 Postgres 中的 hll 与 Java APIs 的互操作性。使用以下查询创建了另一个表
然后用一个 Java 程序创建 hll,使用从 0 到 n 的一系列数字,这样我们就知道其中有多少唯一的条目,将它们推送到 Postgres,然后对它们进行聚合和计数。因为我们这样知道了唯一的条目,所以我们可以计算 user_id 和 session_id 的错误百分比。
这里需要注意的几件事是:
- 如果我们想要联合两个 hll 或者合并两个 hll,那么我们应该总是用相同的种子散列它们,否则相同的值也将给出两个不同的散列值,并且将被计为两个。
- hll 的标准误差可由以下公式给出,预计计算值分别在 65%、95%和 99%情况下的实际计数的σ、2σ和 3σ范围内。σ = (1.04)/√(桶数)
Spark UDFs
我们使用 Java APIs 来编写 UDF
聚合知识的存储规范
让我们来看看 AgKn 为提高基数估计的准确性而提出的 HLLs 类型。
类型
- 空一个常数值表示空集。
- Explicit 这样做是为了提高低基数时的准确性,因为我们可以直接将散列输入的显式集合保存为 64 位整数的排序列表。我们还可以调整 expthresh,根据我们的用例保留更多或更少的数字,但是自动模式支持最佳的内存使用。
- 稀疏
它的思想是基于稀疏数组的,在稀疏数组中我们保存只有非零值的索引的值。类似地,它是 HLL 的基于映射的实现,只存储映射中非零寄存器的索引值,直到非零寄存器的数量超过固定基数。因此,它实际上只保存那些寄存器至少有 1 个非 0 位的桶的寄存器值。 - 完全
完全具体化的、基于列表的 HLL 实现。在按寄存器索引排序的列表中显式存储每个寄存器的值。
不是,访问 Schema 布局和数据如何以字节数组格式排列,可以在这里访问。
聚合知识的参数定义和调整
1.Log2m
这个值决定了桶或者寄存器的数量,m 是这里实际的寄存器数量,我们可以用 2(Log2m)来计算寄存器的数量。此外,该值决定了 HLLs 的精度。下面的表达式给出了标准误差估计。此外,需要注意的一点是,对于 65%、95%和 99%的情况,计算值预计分别在实际计数的σ、2σ和 3σ范围内。σ = (1.04)/√(m)
2.RegWidth
这是寄存器/桶的宽度或 HLL 算法中每个寄存器使用的位数。必须至少为 1,最多为 8。该参数与 log2m 结合使用,可以调整其基数可以估计的集合的最大基数。请参考表了解 HLL 大小和最大基数。
3。ExpThresh
根据集的基数,在显式提升为稀疏提升时进行调整。
If the `EXPLICIT` representation is turned off, the `EMPTY` set is promoted directly to `SPARSE`. Must be -1 {Promote at whatever cutoff makes sense for optimal memory usage. ('auto' mode)}, 0 {Skip EXPLICIT representation in hierarchy. (Promotes directly to Sparse.)}, or 1-18 inclusive {Promote at 2^expthresh - 1 cardinality}.
您可以选择显式截断,这样它将比完整的 hll 表示占用更多的内存。这是允许的,因为在某些预设的基数范围内需要完美的精度和准确性,在此之后,基数的估计就足够了。
4。斯巴森
启用或禁用稀疏表示。如果显式和稀疏表示都被禁用,空集将直接提升为完整集。如果启用了稀疏,当内部稀疏表示的内存占用超过完整版本的内存占用时,将从稀疏提升到完整版本。必须为真或假。True 表示启用,false 表示禁用。
结论
超对数算法有助于在一定阈值内解决基数估计问题,而不会使存储空间过载以保留所有元素。
此外,聚合知识为 HLL 算法提供了强大的支持。其他可以尝试的库有 Datasketch , Zetasketch 。
参考
- https://mungingdata . com/Apache-spark/hyperlog log-count-distinct/
- https://github . com/swoop-Inc/Spark-alchemy/wiki/Spark-HyperLogLog-Functions
- https://data bricks . com/session _ eu19/高性能-高级-分析-火花-炼金术
- https://github.com/aggregateknowledge/java-hll
- https://www . moderndesartes . com/essays/hyperlog log/index . html
- https://highly scalable . WordPress . com/2012/05/01/probability-structures-we B- analytics-data-mining/
- https://github . com/citus data/PostgreSQL-hll #参数解释和调整
- https://github . com/aggregate knowledge/hll-storage-spec/blob/master/storage . MD
- https://banner 2 . clean png . com/2019 06 23/uxe/kiss png-logo-Java-development-kit-portable-network-graphic-5d 0f 25d 68733 . jpg
- https://upload . wikimedia . org/Wikipedia/commons/thumb/f/F3/Apache _ Spark _ logo . SVG/1200 px-Apache _ Spark _ logo . SVG . png
- https://CPL . Thales group . com/sites/default/files/content/paragraphs/intro/2020-03/PostgreSQL-logo . png
具有精确 Shapley 值的高效数据评估
思想和理论
用更少的数据产生更好的模型。
这篇文章概述了论文中所做的工作— “最*邻算法的高效特定任务数据评估”。
数据越多,结果越好吗?鉴于机器学*的趋势,你会有同样的期待。鉴于最*越来越多的具有优异性能的机器学*模型的兴起,更多的数据似乎是王道。
这种模式在机器学*的进步中得到了例证,如 OpenAIs GPT-3 模型,它在来自书籍和互联网的超过 45tb 的数据上进行训练,以支持其 1750 亿个参数。但这种模式与谷歌的新开关变压器架构相比实在是相形见绌,该架构拥有 1.6 万亿个参数。因此,似乎越来越大的模型和越来越多的数据是不可避免的。
“信息是 21 世纪的石油,而分析则是内燃机。”——Peter Sondergaard,Gartner 高级副总裁
如果数据如此有价值,我们应该收集更多吗?为什么我们不能拥有更多如此珍贵的东西?
这种方法有一个问题。如果你收集的额外数据与你的问题无关呢?如果纯粹是噪音呢?那么任何额外的数据都会严重影响模型的性能。那么你能做些什么来缓解这个问题呢?
首先,你可以仔细检查你的数据。这种方法可能是最有效的工具之一,但是当您拥有大量数据时,这种方法非常耗时。对于计算机视觉任务,有许多不同的技术可以使用。这篇文章展示了一些令人兴奋的技术。
在许多情况下,适用的数据相对容易识别。然而,对于其他人来说,这充其量是一个不明确的任务。通常问题的一部分是数据和结果之间的关系是我们试图建模的。然后确定什么数据最适用是一项艰巨的任务。
幸运的是,有一种基于 Shapley 值的新方法非常适合这种情况。
SHAP 价值观
SHapleyAadditive exPlanations(SHAP)是一种解释任何机器学*模型输出的博弈论方法。这种方法是众所周知的,但是属性仅仅基于特征。
该方法将来自特征的属性值提供给模型的输出。SHAP 值基于沙普利值,沙普利值决定了如何在游戏中公平地在多个玩家之间分配报酬。对于 SHAP 价值观来说,玩家联盟是基于特征的。
https://github.com/slundberg/shap
SHAP 价值观非常灵活。例如,在计算机视觉任务中,SHAP 值代表不同像素对模型输出的贡献。有许多不同的方法来计算 SHAP 值,包括与模型无关的 KernelSHAP 方法。
对于 SHAP 值的每一个变化,归因总是与模型的特征有关。然而,还有一个选择。将实例用作玩家,并计算每个实例的属性。
数据估值
“最*邻算法的有效的特定任务数据评估”是一篇最*的论文,提供了计算精确 Shapley 值的新算法。在本文的其余部分,我将为每个实例生成的 Shapley 值称为数据 Shapley 值。
数据 Shapley 值是最*的一项创新,它利用 Shapley 值来确定不同数据实例的属性。这项研究的动机是通过优化从数据市场中选择的记录来保护隐私的机器学*。数据市场由许多不同的医疗记录组成。因此,数据购买者选择从数据市场购买记录的子集。由于数据成本,买方的目标是为他们的模型选择最佳的患者子集。
数据市场问题https://arxiv.org/pdf/1908.08619.pdf(图片来自论文)
该问题配置中的 Shapley 值测量归因于每个数据点平均总体可能数据子集的效用的边际改善。
计算 Shapley 值的最重要的问题是高度的复杂性。一般来说,这是在 O(2^N 的水平)进行精确计算。
然而,研究人员开发了一种使用 KNN 实用程序为 K-NN 分类器设计的精确 Shapley 值的新算法。该算法依赖于 KNN 效用满足分段效用差分性质的事实。
我将把精确的数学公式留给论文和好奇的读者。但结果是这样的。该算法以 O(N log N)复杂度执行精确计算。
https://github.com/AI-secure/KNN-PVLDB
实验
实验的结构遵循简单的格式。首先,数据被分成训练集、验证集和测试集。然后,基于训练实例对验证实例的属性来计算精确的 Shapley 值。
然后根据它们的 Shapley 值对训练实例进行排序。这种设置为用户提供了每个实例对验证集的属性。接下来,用户可以只选择那些具有最高 Shapley 值的实例,为用户提供更小的数据子集。
由于该过程选择对验证集的性能贡献最大的数据,因此移除实例将降低 Shapley 值,这通常会移除数据中噪音最大的实例。同时,保留最有代表性的例子。
实验使用了 diamonds 数据集。该数据集包含* 54,000 颗钻石。这些特征包括钻石属性,如钻石的克拉、切割、颜色和其他一些特征。一些特征是分类的,对于这些实验,我将这些特征转换成每个类别的布尔特征。
目标是钻石的价格。
https://www.kaggle.com/shivam2503/diamonds
我已经进行了 5000 次验证和测试。剩余的数据用于训练。
目的是利用较少的数据建立一个预测钻石价格的模型。我用一个决策树回归器评估了性能。基于 Shapley 值对数据进行排序,并且使用 R 分数来测量性能。
import numpy as np
from sklearn.utils import shuffle
from sklearn.tree import DecisionTreeRegressor
from exact_sp import get_true_KNN, compute_single_unweighted_knn_class_shapley
import matplotlib.pyplot as plt
import pandas as pddf = pd.read_csv('diamonds.csv', index_col='index').reset_index(drop=True)
df = pd.get_dummies(df, columns = ['cut', 'color', 'clarity'])
x_trn = df.drop(['price'], axis=1).to_numpy()
y_trn = df['price'].astype(float).to_numpy()
x_trn, y_trn = shuffle(x_trn, y_trn, random_state=10)
x_tst, y_tst = x_trn[:5000], y_trn[:5000]
x_val, y_val = x_trn[5000:10000], y_trn[5000:10000]
x_trn, y_trn = x_trn[10000:], y_trn[10000:]
K = 1
接下来,计算 Shapley 值进行验证。代码块也依次适合较小数据量的模型。该数据是根据递减的 Shapley 值考虑的。
一旦在训练数据的子集上训练了模型,就在测试数据集上评估该模型。
x_val_knn_gt = get_true_KNN(x_trn, x_val)
val_sp_gt = compute_single_unweighted_knn_class_shapley(x_trn, y_trn, x_val_knn_gt, y_val, K)g_values = np.mean(val_sp_gt, axis=0)
count = int(len(x_trn)*4/5)
interval = int(count*0.02)
x = np.arange(0, count, interval)/len(x_trn)
g_r2_scores = []
g_r2_scores_val = []
idxs = np.argsort(g_values)
keep_idxs = idxs.tolist()for j in range(0, count, interval):
if len(keep_idxs) == len(x_trn):
x_train_keep, y_train_keep = x_trn, y_trn
else:
x_train_keep, y_train_keep = x_trn[keep_idxs], y_trn[keep_idxs]
reg = DecisionTreeRegressor()
reg.fit(x_train_keep, y_train_keep)
r2_score = reg.score(x_tst, y_tst)
r2_score_val = reg.score(x_val, y_val)
g_r2_scores.append(r2_score)
g_r2_scores_val.append(r2_score_val)
keep_idxs = keep_idxs[interval:]plt.plot(x, g_r2_scores, '-', label='Test Performance', color='olive')
plt.plot(x, g_r2_scores_val, '-', label='Validation Performance', color='blue')
plt.ylabel('R2 Score')
plt.xlabel('Percent of Data Removed')
plt.legend(loc="lower left")
plt.title('Data Validation with Exact Shapley Values')
plt.show()
实验结果表明,该模型在较少数据的情况下,对测试数据的表现较好。当几乎 50%的训练数据被删除时,测试性能达到峰值。
同样有趣的是,当超过 60%的训练数据被删除时,验证集上的模型性能达到峰值。因为实例是根据验证集的 Shapley 值删除的,所以这种模式有一定的意义。
删除数据后的性能变化(图片由作者提供)
结论
尽管海量数据的可用性不断增加,但数据的质量至关重要。在低质量的数据上构建模型会产生低质量的模型。
在实例上使用 Shapley 值提供了一种替代方法。您可以用更少的实例创建模型,从而提高性能。实验表明,即使数据子集非常小,较少的数据也可以提高模型性能。
数据是新的石油,但是石油的质量很重要。
考虑在下一个机器学*模型中使用数据 Shapley 值。
如果你有兴趣阅读关于新颖的数据科学工具和理解机器学*算法的文章,可以考虑在 medium 上关注我。
如果你对我的写作感兴趣,想直接支持我,请通过以下链接订阅。这个链接确保我会收到你的会员费的一部分。
https://zjwarnes.medium.com/membership
Python 中的有效前沿—详细教程
不用包在 Python 中实现现代投资组合理论
包含在本文中的代码的输出
介绍
Harry Markowitz 在他 1952 年题为 投资组合选择 的论文中介绍了现代投资组合理论。他首先概述了投资组合选择是一个两步过程;首先,投资者必须考虑可用资产的未来表现(就风险和回报而言),随后,可以决定如何构建投资组合(即给每项资产分配多少资金)。
Markowitz 专注于投资组合构建方面,将预测未来表现的更具投机性的任务留给了读者。事实上,在整篇论文中,收益率都被假设为服从简单的高斯(正态)分布。这一假设是整个现代投资组合理论的基础,但也是许多批评的原因,因为股票价格回报已被证明不符合正态分布。
然而,让我们把批评放在一边,写一些 Python 代码来绘制马科维茨子弹,并更好地理解为马科维茨赢得诺贝尔奖的数学理论。
数据
我们的数据由六只股票的每日收益组成,如下所示。回报范围从 2000 年初到 2018 年末。
返回数据
我们可以使用两行代码从这些数据中立即得到预期年化回报、方差和协方差矩阵。我们注意到,通过像这样使用过去的数据,我们假设未来将遵循过去的趋势。在金融市场上,这是一个极具争议的假设,但就目前而言,这是可行的。
获得年度回报和协方差矩阵
我还制作了一个 YouTube 教程,使用了与本文相同的代码,如果视频是你首选的学*媒介的话!
获得投资组合——简单的版本
除了 numpy 之外,不需要使用任何包,我们可以使用下面的代码快速轻松地创建一个均值-方差图。均值-方差图让我们看到每个投资组合的风险和回报之间的权衡。
创造
关于这段代码,需要注意一些事情:
- 我们使用以下公式计算预期收益
更多信息请见此处
- 下面的投资组合方差公式(包括协方差和个体方差)
更多信息请参见此处
- 您可以调整每个投资组合中要考虑的资产数量。
使用我们生成的均值-方差对列表,我们现在可以绘制这些投资组合🎉
在这个图中,每个点代表一个投资组合。这里需要注意的是,这里显示的大多数投资组合都在有效边界上而不是。我们将在这篇文章的下一部分解决这个问题。
我在这里使用图形库 Plotly 如果你还没有尝试过,我强烈建议给它一个机会!在 Jupyter Notebook 中剧情是完全交互的,对于数据探索有很大的帮助。此处见剧情互动版。
更复杂的版本
以前我们随机抽样投资组合权重;让我们努力提高效率(一语双关)。我们可以引入支配的概念,这样我们就不会不必要地抽样“坏”投资组合。
作为一个(有点)理性的投资者,如果有两个回报相等的投资组合,我们会选择风险较低的一个,而给定两个风险相等的投资组合,我们会选择回报较高的一个。什么事?🤞就上面的情节而言,这意味着什么?左上角是最好的位置,任何在左上角有另一个投资组合的投资组合都是受支配的,任何理性的投资者都不会选择它。
因此,让我们确保我们的代码不包括任何由我们已经取样的投资组合支配的投资组合。
在这个版本中,我们还存储了每个投资组合的资产名称和权重。使用 Plotly 的交互功能,我们可以将鼠标悬停在图上的每个点上,它会告诉我们每个点的资产和权重。您可以在下面的截图中静态地看到这一点,但请单击此处的查看互动情节!
使用这种抽样技术,我们可以很快地沿着有效边界对投资组合进行抽样。一些早期的投资组合样本在有效边界以下清晰可见;然而,这可以很容易地通过在某次迭代后增加投资组合来补救(类似于 MCMC 中的“磨合期”的想法)。
https://medium.com/@riandolphin/membership
对于那些走到这一步的人,感谢你的阅读!请随时在下面的评论中提出任何问题。Jupyter 笔记本形式的全部代码可以在这里查看。也可以从我的 Github 下载使用的数据和笔记本。
高效的机器学*——为什么您应该在算法中考虑“客户”
当用户发现您的系统不可用时,只看传统指标会让您陷入困境。考虑使用替代评估方法,以确保您的系统已做好业务准备。
很容易陷入构建一个可爱的新机器学*算法。你看到一种网络设计,然后是另一种,你不断地改变你的方法。你花了几个小时调整超参数,优化你的方法,直到最后,你有一个出色的解决方案。100%准确,你心里想。人们还想要什么?
首先是现实主义。如果你真的相信你的方法是 100%准确的,要么你不需要机器学*,要么你需要重新思考你在做什么!
但是,即使是“真实”的表现,你可能也没有看到正确的衡量标准。仅仅关注准确性很难给你全面的了解。考虑一个能够产生 95%准确率的计算机视觉算法。这足以让它“部署”到智能手机应用程序吗?
不要!还有无数其他因素需要你考虑。在这篇文章中,我将试着写下一些你应该如何看待你的算法的其他方式,以及一些如何确保你的算法为现实世界做好准备的建议。
图片来自 PicPedia (CC BY-SA 3.0)。
考虑你的观众是如何使用这个模型的
谁会使用你的机器学*算法?他们在什么样的“体制”中应用你的模型?模型推理有两种主要设置—离线和在线。
在离线设置中,您的最终用户“偶尔”会运行该算法。他们并不特别在乎(在合理的范围内)需要多长时间,因为他们只是偶尔做一次。但是,请注意,这类用户可能会一次给你的模型提供许多样本!他们期望高效的批量推理,而不是提供连续的数据流。
在测试你的模型时要考虑到这一点。确保将吞吐量视为主要指标。也许并行化设计是合适的。
在在线设置中,您的用户需要结果,而且他们现在就需要。您的算法正在接收数据,它需要尽快给出结果,以避免糟糕的最终用户体验。以谷歌的搜索引擎为例——他们的研究显示,当用户选择他们选择的搜索引擎时,结果的延迟是一个主要因素,结果花费超过 1 秒可能会有问题!
请记住,在这种情况下,你的模型不是唯一的减速,所以你的时间比你想象的还要少。你需要考虑网络延迟,输入延迟,各种各样的问题!目标是使你的模型尽可能快,这样如果有延迟问题,你的系统是没有问题的。参见这篇论文和这篇论文的例子,了解研究人员如何在终端设备上加速 CNN 推理。
考虑你的观众正在使用的硬件
你的模型在哪里运行?您的系统有哪些可用的资源?也许你正在使用无穷无尽的云资源……或者你的模型正在树莓 Pi 上运行?你必须知道答案——深度学*模型可能需要大量资源。
当开发一个打算在智能手表上运行的模型时,要考虑内存要求、功耗,甚至设备的计算能力。让我提前告诉你,你不是在苹果手表上本地运行 GPT 3。
即使硬件充足,你的用户想要分配多少资源给你的模型?微软 word 中的“下一个单词”预测器是而不是用户的优先级。他们不想看到 Word 95%的资源需求来自一个告诉他们接下来可能会使用什么单词的系统!
如果你不为你的模型考虑环境,你的顾客会有一些愤怒的抱怨。
图片来自维基共享资源 (CC BY-SA 3.0)
考虑你的用户的优先权
这个和上一个有关系。你的客户看重什么?他们需要超精确的预测(自动驾驶汽车、金融市场),还是粗略的“猜测”就足够了(下一个单词预测者、书籍推荐者)。如果你把你的产品标榜为“有史以来设计的最精确的算法”,但是你的用户真的不在乎 70%和 90%的精确度之间的差别,你就没有太大的影响力!
也许他们把隐私看得比什么都重要,你“微调”模型的计划需要取消。当你意识到他们正在把你的模型安装到一个 1KB 的设备上时,也许那个把你的模型的大小增加三倍的好主意最终被证明不是那么好。
和你的用户一起理解什么是需要的,并以此为目标来建模你的设计。你可能会意识到你以前重视的并不重要。
结论
机器学*是很容易被你的设计之“美”所吸引的领域之一。确保你能从你的头脑中看到你的模型在现实世界中的位置。说到底,你的模型对其用户的影响比你的设计的理论意义更重要。
许多 ML 开发者认为自己是艺术家。但是如果你想让你的项目被使用,也许首先作为一个工程师来评估它。
有效的网络与嘈杂的学生训练
一种有效的方法来建立强大的 CNN 模型,并进一步提高其精度与嘈杂的学生算法。
如你所知,如果我们需要高计算能力,CNN 是可以扩展的,但是网络越大,就需要越多的资源来训练它。因此,CNN 是根据资源可用性构建的,当更多的资源可用时,它们可以缩放以获得更好的准确性。
在《高效网络》中,我们研究了如何在输入的宽度、深度和分辨率这三个维度上扩展我们的模型。
目录:
1.介绍和以前的方法
2。高效网络—复合扩展
3。架构
4。吵闹的学生训练
5。随机深度
6。迁移学*
介绍
神经网络的缩放过程还没有被很好地理解:有许多方法可以做到这一点,但参数大多是任意选择的。高效网络提供了一种沿三维扩展 CNN 的有效方式。
(a)基线模型(b)通过增加通道数量的宽度缩放通过增加层数的深度缩放(d)通过增加输入图像的分辨率的分辨率缩放(e)通过用复合系数同时增加所有三个维度的复合缩放(源)
深度缩放 —最常见的缩放方式,根据要求增加或减少层数。随着层数的增加,我们得到越来越复杂的特征。从理论上讲,增加层数总是会给我们带来更高的精度,但事实并非如此,因为网络越深,过度拟合、渐变消失的可能性就越大。ResNet-1000 与 ResNet-101 具有相似的精度。
理想情况下,层数的增加也不会降低性能。ResNet-34 的性能优于 ResNet-18。宽度缩放— 增加网络的宽度有助于我们捕捉更精细的特征(图像中更小的细节)。当我们想要限制区域模型尺寸时,会这样做,但是在这种缩放类型中容易发生精度饱和。
分辨率缩放— 增加输入图像的尺寸有助于模型更好地学*特征,因为像素携带的信息会更加准确。像 Yolo 这样的先进型号使用 418*418 作为输入图像尺寸。但是在较大模型的情况下,精度增益很快降低。
沿着一个维度缩放可以提高精度,但在较大模型的情况下,这种增益并不显著。
高效网络—复合扩展
现在我们能不能结合这些缩放方法来提高更大模型的精确度?为什么我们不增加所有三个维度的参数呢?
任意增加它们只会让事情变得更糟,因为有时维度的增加会导致模型的损失或不精确。这将是一项单调乏味的任务,因为我们必须同时在三个维度上进行尝试。
于是引入了一种新的方法, 通过恒定比例缩放来平衡宽度、深度和分辨率的尺寸 。
我们取的常数比叫做复合系数 ɸ.建议的等式是:
复合比例方程式(来源)
ɸ 由用户根据可用资源的数量指定。 Alpha、beta、和 gamma 表示资源将如何分配到深度、宽度和分辨率维度。
现在它是如何工作的:
1 在 (α * β * γ ) ≈ 2 的约束下,我们固定 ɸ (=1) 的值,并通过训练基模型找到α、β和γ的最佳可能值。
对 Efficient-Net-B0 模型进行训练,得到的值为α = 1.2,β = 1.1,γ = 1.15。α、β和γ是由基本网络上的网格搜索确定的常系数。
2 现在有了上面找到的α、β和γ值,我们用不同的 ɸ 值放大基线模型,得到有效网 B1-B7。
高效网络的最高精度图(来源)
通过在大型模型中查找α、β和γ可以获得更好的性能,但是它也有增加搜索成本的缺点。
体系结构
可以使用任何小型且易于扩展的基线架构。研究人员证明,复合缩放技术有助于不断提高 ResNets 和 MobileNets 的模型精度。
研究人员通过使用 AutoML MNAS 框架进行神经架构搜索,开发了一种新的基线网络。
基准模型 B0 的架构简单明了,因此更易于扩展。
简单高效的 Net B0 架构(来源)
吵闹的学生训练
噪声学生训练是一种半监督学*技术,通过使用更大或相等大小的学生模型并在训练时向学生添加噪声来实现自我训练和提炼的思想。
什么!!冷静点。如果我们再深入一点,一切都会水落石出。
算法很简单:
1) 我们在带标签的图像上训练教师模型。
教师模型的损失方程(来源)
现在,我们使用教师模型为未标记的图像生成软或硬伪标签。
3) 我们采用了一个相同或更大尺寸的学生模型,并使用在图像和模型中添加了噪声的组合数据对其进行训练。
学生模型的损失方程(来源)
4) 将学生模型作为教师,重复几次步骤 2 的过程。
嘈杂的学生训练大大提高了所有模型尺寸的准确性。
嘈杂学生培训的前 1 名准确度映射(来源)
我们生成了大量的标签(从* 3 亿张未标记的图像中),这样学生模型比老师学得更好。
我们尝试使用软标签,因为它们会产生更好的结果(在某些情况下)。软标签意味着我们用对象属于那个类别的概率来标记它,而不是标记对象的类别。
顾名思义,我们在训练学生时向数据和模型添加噪声。我们在谈论什么样的噪音?
对于图像数据,我们做增强添加输入噪声,对于模型噪声,我们在训练时添加丢失和随机深度。
我们都知道什么是数据增加和丢失,因为它们是深度学*中的常用术语。但是什么是随机深度噪声呢?
随机深度
随机深度是一个毫不费力的想法,通过跳过连接绕过变换,集中于在训练期间缩小网络的深度。这样,我们得到了一个网络,它的预期深度非常小,而它的最大深度却很高。这通过跳过层的子集来创建类似集合的模型,从而从根本上减少了训练时间。
随机深度训练(图片由作者提供)
测试期间模型深度保持不变,减少了测试损失。测试期间的随机深度需要对网络进行某些改变,因为来自任何层的输出都是用该层参与训练的次数来校准的。
但这些细节是另一篇文章,因为这你应该能够理解随机深度噪声是如何在学生训练期间添加的。
就像丢失禁用信息通过层的节点一样,随机深度禁用整个层或层的子集。
迁移学*
高效网络的迁移学*节省了大量的训练时间和计算能力。您可以使用有效网络的预训练权重,并对其进行微调,以解决您自己的分类问题。只需几行代码,您就可以获得比许多已知模型更高的精度。
用于图像分类的高效网络传输学*代码
让我们来看一看,并比较所有过去的模型和高效网所达到的精度。
模型精度前 1 名和前 5 名及其可训练参数计数(来源)
https://github.com/bharatdhyani13/EfficientNet-Noisy-Student-Training
结论
高效网络是产生高端结果的强大模型。一般而言,与一维缩放相比,复合缩放可将精度提高 2.5%。
嘈杂的学生自我训练是一种有效的方法,可以利用未标记的数据集,并通过在训练时向学生模型添加噪声来提高准确性,从而使其学*超出教师的知识范围。
随机深度是一个简单而巧妙的想法,通过跳过连接绕过变换来给模型添加噪声。这减少了训练时间,同时可以在测试期间激活被绕过的层,以产生更准确的结果。
这是一些简单的想法,结合起来提高图像分类的准确性。这为通过简单而巧妙的数学变换来改进缩放和训练提供了可能性。
参考
高效的熊猫:应用与矢量化操作
时间和效率很重要
Marc-Olivier Jodoin 在 Unsplash 上拍摄的照片
Pandas 是数据科学生态系统中最常用的数据分析和操作库之一。它提供了大量的函数和方法来执行高效的操作。
我最喜欢熊猫的一点是,完成一项既定任务几乎总是有多种方式。然而,当从可用选项中选择一个方法时,我们应该考虑时间和计算复杂性。
仅仅完成给定的任务是不够的。我们应该尽可能提高效率。因此,全面理解函数和方法是如何工作的至关重要。
在本文中,我们将举例比较 pandas 的 apply 和 applymap 功能与矢量化操作。apply 和 applymap 函数可用于许多任务。然而,随着数据量的增加,时间成为一个问题。
让我们创建一个包含 100k 行的示例数据框架。我们首先需要导入所需的库。
import numpy as np
import pandas as pd
import timeit df = pd.DataFrame({
'cola':np.random.randint(1,100, size=100000),
'colb':np.random.randint(100,1000, size=100000),
'colc':np.random.random(100000)
})df.shape
(100000, 3)
我们将使用 timeit 库来测量执行一个操作所需的时间。
apply 函数通过遍历元素来执行按行或按列的操作。applymap 函数以类似的方式工作,但是对数据帧中的所有元素执行给定的任务。
下面的代码将对“cola”列中的每个数字求平方。
%%timeitdf['cola'].apply(lambda x: x**2)best of 3: 54.4 ms per loop
需要 54.4 毫秒。让我们用矢量化运算来完成同样的任务。
%%timeitdf['cola'] ** 2best of 3: 1.15 ms per loop
在 100k 行上大约快 50 倍。
下一个示例包括一个在数据帧的每一行中查找最小值的任务。
%%timeitdf.apply(lambda x: x.min(), axis=1)best of 3: 3.01 s per loop
执行应用功能需要 3 秒钟。
%%timeitdf.min(axis=1)best of 3: 1.58 ms per loop
用矢量化运算还是毫秒级的。差别巨大。差异显著增加的原因是 apply 函数在每行的每列中循环。
让我们稍微增加一下操作的复杂度。我们想找出一行中最高值和最低值之间的差异。
该操作通过应用功能完成,如下所示:
%%timeitdf.apply(lambda x: x.max() - x.min(), axis=1)best of 3: 5.29 s per loop
我们使用 lambda 表达式来计算最高值和最低值之间的差值。轴设置为 1,表示对行的操作已经完成。该操作执行需要 5.29 秒。
同样任务可以通过两个矢量化运算来完成。
%%timeitdf.max(axis=1) - df.min(axis=1)best of 3: 3.86 ms per loop
我们使用 max 和 min 函数作为矢量化运算。axis 参数为 1,表示我们需要一行中的最小值或最大值。如果我们不将轴参数指定为 1,则返回列中的最小值或最大值。
矢量化版本的执行时间为 3.86 毫秒,快了一千多倍。
下一个示例将 applymap 函数与矢量化运算进行了比较。以下代码将 dataframe 中的每个元素加倍。
%%timeitdf.applymap(lambda x: x * 2)best of 3: 93.6 ms per loop
需要 93.6 毫秒。
%%timeitdf * 2best of 3: 1.03 ms per loop
同样的操作大约需要 1 毫秒,比 applymap 函数快 90 倍。
结论
我们已经介绍了一些例子来比较矢量化运算和 apply 和 applymap 函数。
对于小数据,时间差通常可以忽略不计。然而,随着大小的增加,差异开始变得明显。我们可能会处理大量的数据,所以时间应该总是被考虑在内。
apply 和 applymap 函数的成功之处在于它们执行给定任务的方式。给定的操作是通过循环遍历元素来完成的,随着数据变大,执行速度会变慢。
感谢您的阅读。如果您有任何反馈,请告诉我。
使用 Python 的 Pmdarima 库进行高效的时间序列分析
展示了与实现传统 arima 模型相比,pmdarima 的 auto_arima()函数的效率。
阿列克谢·扎伊采夫在 Unsplash 上的照片
什么是时间序列分析?
数据科学中的一个关键概念是时间序列分析,它涉及使用统计模型根据过去的结果预测时间序列的未来值(即金融价格、天气、新冠肺炎阳性病例/死亡人数)的过程。在时间序列分析中可能会看到的一些组成部分是:
- 趋势:显示一段时间内时间序列数据的大致方向,趋势可以是上升(向上)、下降(向下)或水平(静止)。
- 季节性:这一部分表现出一种在时间、数量和方向上重复的趋势,例如在夏季的几个月里冰淇淋销量的增加,或者在较冷的几个月里地铁乘客的增加。
- 周期性成分:在一定时期内没有固定重复的趋势。一个周期可以是一段起伏的时期,主要见于商业周期——周期不表现出季节性趋势。
- 不规则变化:时间序列数据的波动是不稳定的,不可预测的,可能是/也可能不是随机的。
进行时间序列分析时,有单变量时间序列分析或多变量时间序列分析。当相对于时间仅观察到一个变量时,使用单变量,而如果相对于时间观察到两个或更多变量,则使用多变量。
什么是 ARIMA?为什么使用 Pmdarima?
ARIMA 是首字母缩略词,代表自回归综合移动平均,是一种建模时间序列数据进行预测的方法,由三个序参数( p,d,q): 指定
- AR( p ):说明数据的增长/下降模式
- I ( d ):说明了增长/下降的变化率
- 马(问):考虑时间点之间的噪声
有三种类型的 ARIMA 模型,ARIMA 模型、萨里玛模型和萨里玛模型,它们根据季节和/或外部变量的使用而有所不同。
Pmdarima 的 auto_arima 函数在构建 arima 模型时非常有用,因为它可以帮助我们确定最佳的 p,d,q 参数,并返回拟合的 arima 模型。
作为一名数据科学的新手,在进行时间序列分析时,我采取了“长”的方式,然后遇到了 pmdarima 的 auto_arima 函数来构建高性能的时间序列模型。在本文中,我将重点关注单变量时间序列分析,以预测航空乘客的数量(从 Kaggle 到),并通过传统的 ARIMA 实现与更有效的 auto_arima 方法进行讨论。
实施 ARIMA 模型的一般步骤:
- 加载和准备数据
- 检查平稳性(如有必要,使数据平稳)并确定 d 值
- 创建 ACF 和 PACF 图以确定 p 和 q 值
- 拟合 ARIMA 模型
- 预测测试集上的值
- 计算 r
首先,我通过将日期更改为 datetime 对象、使用 set_index 方法将日期设置为索引并检查 null 值来加载和准备数据。
df=pd.read_csv('AirPassengers.csv')
df=df.rename(columns={'#Passengers':'passengers','Month':'date'})
df['date'] = pd.to_datetime(df['date'])
df.set_index(df['date'], inplace=True)
df=df.drop(columns=['date'])
df.head()
作者图片
然后,我初步研究了航空乘客的月平均人数,发现数据并不是稳定的。通过进行 Dickey-Fuller 测试进一步证实了这一点,Dickey-Fuller 测试是针对平稳性的单位根测试,如下图所示:
作者图片
在对我们的数据进行两次差分后,我们的 p 值小于我们的 alpha (0.05),因此我们能够拒绝零假设,并接受数据是稳定的替代假设。然后,我们通过将 d 参数设置为 2 来建模我们的时间序列数据。接下来,我使用差分数据查看了我们的 ACF/PACF 图,以可视化在模拟乘客数量时可能会产生影响的滞后。
作者图片
根据我们的可视化,我确定我们的 p 参数是 0,而 q 参数是 2——对于 ARIMA 模型,我们的 p,d,q 参数将是(0,2,2)。在将数据分成训练组和测试组,并在训练集上拟合 ARIMA 模型以预测测试集之后,我们获得了-1.52 的 r 值——告诉我们该模型根本没有遵循数据的趋势。
作者图片
我很可能错误地计算了 p,d,q 值,导致 r 值为负,但同时让我们尝试使用 PMMA 建立另一个 ARIMA 模型。
将 pmdarima 用于汽车 arima 模型
在之前的方法中,检查平稳性,如有必要使数据平稳,并使用 ACF/PACF 图确定 p 和 q 的值可能会非常耗时且效率较低。通过使用 pmdarima 的 auto_arima()函数,我们可以省去实现 arima 模型的第 2 步和第 3 步,从而简化这项任务。让我们用当前数据集来尝试一下。
在加载并准备好数据之后,我们可以使用 pmdarima 的 ADFTest()函数进行 Dickey-Fuller 测试。
adf_test=ADFTest(alpha=0.05)
adf_test.should_diff(df)# Output
(0.01, False)
这一结果表明数据不是平稳的,因此我们需要使用“整合(I)”概念( d 参数)在建立汽车 ARIMA 模型时使数据平稳。
接下来,我将数据集分为训练集和测试集(80%/20%),以在训练集上构建自动 ARIMA 模型,并使用测试数据集进行预测
train=df[:114]
test=df[-30:]
plt.plot(train)
plt.plot(test)
作者图片
然后,我们使用 pmdarima 的 auto_arima()函数建立自动 ARIMA 模型。使用 auto_arima()函数调用代表非季节性成分的小 p,D,q 值和代表季节性成分的大写 P,D,Q 值。Auto_arima()类似于其他超参数调整方法,并确定使用不同的组合来找到 p,d,q 的最优值。最终的 p、d、q 值是在考虑了较低的 AIC 和 BIC 参数的情况下确定的。
model=auto_arima(train,start_p=0,d=1,start_q=0,
max_p=5,max_d=5,max_q=5, start_P=0,
D=1, start_Q=0, max_P=5,max_D=5,
max_Q=5, m=12, seasonal=True,
error_action='warn',trace=True,
supress_warnings=True,stepwise=True,
random_state=20,n_fits=50)
作者图片
我们可以查看模型摘要:
作者图片
接下来,我们可以使用训练好的模型在测试集上预测航空乘客的数量,并创建可视化效果。
prediction = pd.DataFrame(model.predict(n_periods = 30),index=test.index)
prediction.columns = ['predicted_passengers']plt.figure(figsize=(8,5))
plt.plot(train,label="Training")
plt.plot(test,label="Test")
plt.plot(prediction,label="Predicted")
plt.legend(loc = 'upper left')
plt.savefig('SecondPrection.jpg')
plt.show()
作者图片
自动 ARIMA 模型给了我们 0.65 的 r 值——与我第一次实现 ARIMA 模型相比,这个模型在捕捉数据趋势方面做得更好。
在本文中,我使用 auto_arima()演示了 ARIMA 模型的传统实现,并与自动 ARIMA 模型进行了比较。虽然传统的 ARIMA 实现需要执行差分并绘制 ACF 和 PACF 图,但使用 pmdarima 的 auto_arima()函数的自动 ARIMA 模型在确定最佳 p,d,q 值时更有效。
有关 pmdarima 的 auto_arima()函数的更多信息,请参见以下文档
感谢您的阅读!我的 GitHub :)上有所有代码
高效计算大规模分布式数据集中的方差
理解大数据
针对数据分析中最基本问题之一的可扩展数据汇总技术
分布式计算数据中心。图片来自 Unsplash。
计算数据特征的方差可能是数据分析中最基本的操作之一。无论是为了可视化还是统计测试,通常都需要数据特征的方差,或者说标准差。
通常,我们只是使用一个为我们计算的库。但是,这些库假定可以访问全部数据。想象一下,在一个环境中,我们无法提供对整个数据集的访问。例如,您需要计算用户访问在线购物网站的平均时间的标准差。该网站使用几个服务器来管理来自世界各地的流量,我们无法承担存储和合并全部数据的费用。此外,我们需要考虑问题的延伸,如“一天中客户在网站上花费的时间的标准偏差是多少?”因此,我们需要以某种方式计算每台服务器上的统计数据,然后有效地汇总这些数据。
在这篇文章中,我将介绍有效计算的算法,或者更确切地说,估计大规模和分布式数据集中的方差。
每个坐标一次更新
在下文中,我们考虑实数值 x[i]的大规模数组 X。方差被定义
在上式中,μ是数据 x 的平均值。计算上述值的标准方法是首先计算数据的平均值,然后计算上述总和中的每一项。然而,我们不能存储数据 X。此外,数组 X 在不同的位置生成为值 x _ i 的流,因此我们谈论每个坐标的单次更新,这意味着我们只看到每个 x_i 一次。例如,x_i 是用户在第 I 次浏览会话期间在我们网站上花费的时间。注意,我们不关心用户身份。
让我们重写方差的定义:
图片作者。
上面显示我们只需要存储三个量:例子的数量 n ,例子的总数
第二个时刻
这三个量可以在不显式存储数组 x 的情况下进行计算。因此,在每一端,我们收集这三个量,并在中央服务器中将它们相加。因此,我们只需要恒定的空间和恒定的更新时间。这很简单,对吧?
每个坐标多次更新
现在让我们考虑一个更具挑战性的环境。我们想要计算单个条目 x[i]随时间更新的情况下的方差。输入是一个更新流(I,w_i ),表示我们通过值 w_i 更新坐标 I。例如,我们不考虑每次访问我们网站的平均时间的方差,而是考虑每个用户一周的总时间的方差。这意味着对于我们拥有的单个条目
在上面的例子中, S 是一串更新,比如(用户 i= "简·多伊" s pent w_i=4 分钟浏览网站)。
请注意,上述总和可以在多个地点收集。
我们能再次应用上述方法吗?
我们可以通过简单地将不同的计数器相加来再次计算总和:
然而,在这种设置中,我们不能简单地通过保留一个计数器并将这些计数器相加来计算数据示例的数量。当我们看到像“Jane Doe”这样的元素时,我们需要知道他们在过去的一周是否访问过该站点。我们需要跟踪不同的元素的总数。我们可以使用散列映射,但是这需要存储所有不同的条目,并且这可能是非常昂贵的。
第二个问题是如何计算二阶矩:
我将在数据总结的基础上提出这两个问题的解决方案。
估计不同元素的数量
我提出了[1]中的方法。假设我们有一个函数,它为任意整数输入生成一个区间(0,1)内的随机数。请注意,这是一个函数,因此每当我们看到相同的整数,我们就生成相同的随机数。我们处理输入并存储 k 个最小的随机数,作为用户定义的 k。让 max_k 是 k 个元素中最大的。因此,对于流中的不同值,max_k 是所有随机数中第 k 个最大的随机数。在处理流之后,我们将不同元素的数量估计为 k/max_k。为什么这样做?如果总共有 t 个不同的元素,我们预计其中一半的随机值 t/2 小于 0.5,或(t/2)/t。同样,我们预计 k 个元素小于 k/t。因此,这 k 个最小元素中的最大元素应该具有 k/t 左右的值。这种直观的解释可以是正式的,并且可以表明,对于 k=O(1/eps),我们可以估计乘法误差为(1+eps)的不同元素的数量。例如,通过使用 k = 100,我们可以获得一个在真实值的 90%和 110%之间的估计值。如果我们使用 k = 1,000,那么估计值将在真实数字的 97%和 103%之间。在不同地点收集的草图可以很容易地聚集在一起:我们将它们合并,只保留 k 个最小的元素。
一个非常有效的方法就是超对数算法。
估计二阶矩
现在我将介绍一种估算二阶矩的方法:
AMS 草图以其发明者 Noga Alon、Yossi Mattias 和 Mario Szegedy 的名字命名[2]。输入是更新流(I,w ),其中 I 是索引,w 是相应的权重。AMS 草图非常简单:
对 X 的二阶矩的无偏估计就是
为什么会这样?AMS 草图只是一个单一的数字,对吗?如果我们计算得到的期望值
第二个和是 0,因为 sign(i)*sign(j)是概率为 1/2 的-1 和概率为 1/2 的 1,因此期望值是 0。
当然,估计量的方差是显著的。在[2]中,作者证明了 1/eps 估计量的均值在(1-eps),(1+eps)的乘法误差内产生一个估计。
AMS 草图的一个主要优点是它们可以简单地互相添加。如果我们在几个不同的地点收集数据,那么通过简单地使用相同的符号函数,我们可以计算每个地点的草图并将它们相加。
改进 AMS 草图。
AMS 草图有一个缺点。也就是说,每个输入(I,w_i)必须更新所有 1/eps 草图。研究人员提出了一个想法,允许每次更新的处理时间保持不变。所谓的 CountSketch [3]用包含 1/eps 元素的哈希表 B 代替 AMS sketch。想法是,除了散列函数之外,我们使用散列函数 h 将索引映射到随机选择的箱。
方差的估计值只是哈希表内部的一个向量:
注意,如果没有两个项目共享一个桶,那么我们精确地计算二阶矩。
为了保证估计是大概率正确的,建议取 m 个独立估计量的中值,其中 m 是 7 或 9 这样的小数字。
请注意,在各个站点收集的哈希表可以相互添加。
实现可以在这里找到:【https://github.com/konstantinkutzkov/variance_estimation】T4
[1]齐夫·巴尔-优素福、T. S .贾伊兰、拉维·库马尔、d .西瓦库马尔、卢卡·特雷维桑。对数据流中的不同元素进行计数。兰登 2002
[2] Noga Alon,Yossi Matias,Mario Szegedy:
逼*频率矩的空间复杂度。STOC 1996
[3]摩西·沙里卡尔、凯文·陈、马丁·法拉奇-科尔顿。
在数据流中寻找频繁项。Theor。计算机。Sci。312(1)
有效地筛选分类模型
以更快的方式收敛到正确的模型
现在是晚上 8 点,您仍然在清理数据,执行 EDA,并创建更多的功能。您与业务部门的初步讨论是明天的第一件事,期望讨论正在考虑的潜在“分类”方法,以及所考虑的模型的关键特征。作为一名数据科学家,您希望尝试尽可能多的方法,并为您的业务会议获得最佳的模型预测。
但是,和往常一样,时间紧迫!!
如今,数据科学家(DS)有节约成本/增加收入的目标,工作压力巨大。由于数据清理、争论和特性工程消耗了大部分时间,DS 从来没有足够的时间在数据上尝试所有相关的方法。
尽管业内的一些专家确实知道哪种分类算法适用于特定类型的数据,但是通常很难做出选择。要做出明智的决策,需要考虑太多因素,如数据线性、模型训练时间、数据类型(分类与数值)、可解释性、持续监控的难易程度,当然还有准确性。
确实有必要加快模型的初步筛选,列出入围的前“n”个模型,然后对它们进行微调,以提高准确性和其他关键指标。
要列出一些方法,可以一次运行一个模型,并比较各种输出指标。然而,这是低效和费时的,当然有有效的方法。
此后,我们讨论三个关键有效的方法来执行这一初步入围。每种方法都有其优点和缺点,并且取决于一个人所处的情况——从这些方法中选择一种是值得的。
1。****lazy predict:lazy predict 是一个 python 包,封装了 30 个分类模型和 40 个回归模型。它自动化了训练管道,并返回与模型输出相对应的各种度量。在度量的比较之后,可以微调入围模型的超参数,以改进输出度量。
训练的一些关键分类模型是 SVC、AdaBoost、逻辑回归、树外分类器、随机森林、高斯朴素贝叶斯、XG Boost、岭分类器等。
Python 代码:
假设已经定义了 X_train、X_test、y_train、y_test,并且已经执行了 EDA。
安装 lazypredict
来自 lazypredict。受监督的进口懒汉
cls = lazy classifier(ignore _ warnings = False,custom_metric=None)
模型,预测= cls.fit(X_train,X_test,y_train,y_test)
2。 模型列表迭代:虽然这是一种正统的必不可少的方法,但是,这种方法提供了选择评分方法、创建各种度量(如混淆矩阵等)的灵活性。)并执行交叉验证。这里的想法是创建一个要执行的模型列表,并在创建各种模型度量时遍历模型列表。
Python 代码:
从熊猫导入 read_csv
从 matplotlib 导入 pyplot
从 sklearn.neighbors 导入 KNeighborsClassifier
从 sklearn.tree 导入决策树分类器
从 sklearn.model_selection 导入 KFold
来自 sklearn.linear_model 导入逻辑回归
从 sklearn.discriminant _ analysis 导入线性判别分析
从 sklearn.svm 导入 SVC
从 sklearn.model_selection 导入 cross_val_score
data frame = read _ CSV(' classification data . CSV ')
数组 _a1 = dataframe.values
X = array_a1 [:,0:20]
Y =数组[:,20]
创建模型列表,并追加各种模型进行测试。
models_list = []
models_list.append(('LogitReg ',LogisticRegression()))
models_list.append(('SVM ',SVC()))
models _ list . append((' KNN _ 算法',KNeighborsClassifier()))
models_list.append(('LDA_Algo ',LinearDiscriminantAnalysis()))
models_list.append(('CART_DT ',DecisionTreeClassifier()))
模型结果= []
模型名称= []
这可能是 f1、roc 等。取决于问题陈述
scoring_type = '准确性'
遍历模型列表,使用交叉验证分数找到最佳模型。
对于伪名称,模型列表中的模型名称:
kfold = KFold(n_splits=10,random_state=7)
cross val _ results = cross _ val _ score(model,X,Y,cv=kfold,scoring= scoring_type)
model _ results . append(crossval _ results)
模型名称.追加(假名)
print (name,cv_results.mean(),cv_results.std())
- AutoML 功能:在所有解决方案中,这是最有效、最受欢迎的解决方案。有各种各样的 python 包支持 AutoML。一些关键的是-自动 sklearn,自动 Keras,TPOT 等。请注意,这些软件包不仅关注模型过滤的自动化,还加速了数据处理、特征工程等。
结论: LazyPredict、模型迭代和 AutoML 是在入围和微调模型之前训练模型列表的各种方法。随着数据科学以前所未有的速度发展,我确信有许多其他方法可以更快地获得更好的模型。(如果您遇到任何问题,请在评论中分享)
然而,随时准备好选项也无妨——您永远不知道与业务部门的会议安排在什么时候。
免责声明:本文中表达的观点是作者以个人身份发表的意见,而非其雇主的意见。
使用 NumPy 在 Python 中高效地将图像分割成图块
发展对数组步长和 numpy.reshape()的直觉
图一。问题图解。图片作者。
在很多情况下,您需要将一幅大图像分解成小块(例如,作为 ML 预处理管道的一部分)来进行批处理。本文旨在探索用 Python 实现这一点的更低级的方法。
1.嵌套 for 循环:低效的方法。
我见过的创建图像块堆栈的最常见方法之一是使用嵌套的 for 循环,例如用所需的维度实例化一个新数组,并将其填充到这些循环中。然而,这种方法在 Python 中效率特别低(尤其是),并且根本不适合大型数据集(也不适合较小的切片)。
嵌套的 for-loops 方法类似于下面的代码片段:
# Nested for-loops method.
# Creating a stack of 2 x 2 tiles out of a 4 x 4
# RGB image with channels on the last axis.
import numpy as np
image = np.random.randn(4, 4, 3)
tiles = np.zeros((4, 2, 2, 3))
c = 0
for i in range(0, image.shape[1], 2):
for j in range(0, image.shape[2], 2):
tiles[c] = image[i:i+2, j:j+2, :]
c += 1
随着迭代的增长(图像大小与图块大小的比率),更不用说迭代大量的图像(第三个 for-loop),这可能会占用资源并迅速在您的管道中产生瓶颈。
2.或者:内存视图
图 2.1。阵列存储器布局。图片作者。
如果我们可以对同一块内存使用不同的视图,并将其显示为图块,会怎么样?让我们来谈谈数组。
Numpy 数组和一般意义上的数组在内存中以连续字节块的形式排列。然后可以使用步长以不同的形状查看那块连续的内存。
在数组的内存级别上,跨距表示为了到达数组中包含的下一个元素,需要向前跳转的字节数。这取决于数组中包含的数据类型的字节长度。
例如,16 位整数(2 字节长)的一维数组的跨度为 2 字节。
另外需要注意的是,数组必须包含相同数据类型的元素,以保证相等的字节长度间隔。当然,有多种方法可以在单个数组中表示不同的数据类型,但这超出了本文的范围。
2.1.数组维度
那么,数组中的元素是什么呢?只是按顺序排列的字节。元素的长度可以变化,这取决于表示每个元素所需的字节数。
因此,元素是字节的集合,而字节又是位的集合,特别是 8 位。都生活在同一个物理空间。那么,有没有可能将一个数组进一步聚合成更大的块呢?是的,它是。这正是数组维数的作用。
图 2.2。内存中 C 阶二维数组的图示。图片作者。
为了将数组元素组织成行并创建第二维度,您需要做的就是定义第二个步幅来指向每一行的开头。在那个维度上,各个元素就是行。这也可以看作是将一个数组分割成多个大小相等的子数组。
在 numpy.shape()返回的数组维度大小中,上面描述的示例将呈现为一个二维 shape (3,3) 。返回的维度顺序总是从最高级别的(最左边)开始,到最低级别的(最右边)。这意味着元组中第一个维度 i 的大小 3 表示行的数量,而第二个维度的大小 3 表示每行的单个元素的数量。正如我们将在后面看到的,有一些 numpy 方法可以改变维度层次结构,但是上面的方法通常代表使用 numpy.reshape()形成的数组视图以及 numpy 中新实例化的数组。
注 1:上面的维度顺序是指 numpy.shape()返回的元组中访问的顺序,第一维度在索引位置 0 (shape[0]),第二维度在索引位置 1 (shape[1])。
注 2:有多种布局(行/列优先),但本文仅讨论行优先顺序,因为这是我最熟悉的一种。更多布局信息 点击 。
图 2.3。连续数组维数的图示。图片作者。
出于我们的目的,我们将给出尺寸大小的一般定义如下:
以 shape (m,n,I,j) 的 4 维数组为例,第 1 个(最高&最左边)维包含以 shape(n,I,j) 组织的 m 个元素。第二维保存形状为 (i,j) 的 n 个元素,而第三维保存大小为 ( j ) 的 i 个元素。第四个也是最后一个维度只是一行 j 元素。然后:
尺寸大小:后续(下)尺寸描述的元素个数。
2.2.通过指定自定义步幅,将 2D numpy 图像数组分割成图块
现在,表示为 numpy 数组的 2D 图像将具有形状 (m,n),,其中 m 将指示以像素为单位的图像高度,而 n 将指示以像素为单位的图像宽度。作为一个例子,让我们以一个 6×4、8 位灰度图像阵列为例,通过使用步长创建一个新的内存视图,将它分成 2×2 的小块。记住,元素的长度必须相等,因此,两个数组的维数都必须能被 2 整除。
我们的图像可以被认为是由 6 行 4 个元素组成的。我们示例的可视化如下所示:
图 2.4。说明我们的瓷砖问题。图片作者。
那么,我们怎么去呢?
让我们一步一步来。看到我们总共需要 6 个图块,组织成 3 行 2 列,让我们首先尝试将当前行分成 2 列。我们当前的形状是(6,4),因此,我们需要将最后一个维度减半,并添加一个更高的维度 2,表示我们想要形成的列。这一步的目标三维形状是(2,6,2): 2 列×6 行×每行 2 个数组元素。现在我们知道了我们需要的形状。我们可以继续计算步幅。
我们来观察下图。我们的 2D 形象的步幅是(4,1)。最低维度的步幅 1 必须保持不变,因为对其进行改动会使我们失去像素级别的空间关系(例如,增加它将意味着跳过像素)。行尺寸的跨度 4 也将保持不变,因为所需的行数保持为 6。一般来说,如果你改变这两个步幅中的任何一个,最终都会扭曲图像。然后,我们需要做的就是计算出尺寸为 2 的最高维度的步幅,它代表我们正在定义的新的更高级别的 列。记住,我们将原来的每行 4 个元素分成两半,这样它们可以包含在单独的列中,这给了我们一个提示。
图 2.5。列跨距解决方案的插图。图片作者。
查看图 2.5。我们数组中的所有像素按照它们在内存中的排列顺序进行编号(C 顺序)。以前,每 4 个元素我们必须改变一行,这个规则仍然有效。我们希望包含一个额外的规则:在每一行中, 每隔 2 个元素 改变列。所以我们最高维度的步幅是 2 x element_byte_size 。在我们的 8 位整数的例子中,步距是 2x1 字节= 2。
让我们打开一个终端并用代码测试它:
图 2.6。我们的示例数组的形状和大小。图片作者。
在 numpy 中,您可以使用numpy . lib . stride _ tricks . as _ strided()来操作数组的跨度。我们需要指定我们想要操作的数组、我们想要它的形状以及我们想要每个维度的步距:
图 2.7。将示例图像分成两列。图片作者。
我们得到了包含我们想要的元素的两列!现在,我们可以继续将每列分为 3 组,每组 2 行,这样总共有 6 块瓷砖。
我们当前的形状是(2,6,2),6 是我们在图像中的行数。类似于上一步,我们需要第 3 维元素(2 列)来保存每一行(第 1 维元素)中只有 2 个数组元素的行,现在我们需要顶层层次结构中的第 4 维元素来保存每一行只有 2 个图像。因此,我们需要将“图像行”的维度从 6 减少到 2。我们的目标形状将是(3,2,2,2)。
总之,较低级别的元素仍然是 2,我们的行减少到 2,第 3 维的列仍然是 2,我们现在添加第 4 维来将这些列分成 3 个部分。
图 2.8。瓷砖步幅解决方案插图。图片作者。
我们对这个 4 维数组的定义表明,最高维现在将改变每 2 图像行的元素,而不是 1 。因此,总之,新的步幅必须是图像行的两倍。那么,我们新维度的步距将是 2 x row_stride = 2x4 = 8 字节。
在代码中测试它:
图 2.9。自定义步长解决方案的实施。图片作者。
我们完事了。6 块瓷砖全部成型。现在,我们可以使用 numpy.reshape()合并 2 个最高维度,使我们的数组成为 (number_of_tiles,tile_height,tile_width) 的三维形状,如果我们愿意的话,因为这是图像批处理的基本格式。我们首先需要 4 个维度而不是 3 个维度的原因是因为内存中每个区块的位置不能由一个单独的步幅来表示。我们的形象毕竟是二维的。
我们刚刚实现的使用步长将 8 位整数的 6×4 图像分割成 2×2 块的方法,可以推广到任何 C 排序的 2D 图像,如下所示:
情商。2.1.用于将灰度图像分割成图块的形状和步幅。图片作者。
事实证明,对于特定格式的多通道图像,我们也可以使用类似的公式。具体来说,它们是连续的 C 有序阵列,具有最低维度的通道(具有彼此相邻的相同像素 RGB 值),否则在每种情况下步幅将不同。有些库像那样直接加载图像,有些库不这样,但是重塑它们并不是特别困难。
例如,GDAL 将以通道作为最高(最左侧)维度加载图像。
当我们的多通道数组维度以这种方式格式化时,最低的维度将是每个像素的每通道值,我们可以通过只做一个字节长度的步骤来导航。因为,不是 1 个单个像素值,我们将有 n 个像素值用于 n 个通道,我们需要做的就是将所有其他步幅乘以通道数。总之,一切都保持不变,但我们现在每个像素有多个值要跳转,每个通道一个值:
情商。2.2.用于将多通道图像分割成小块。图片作者。
这不是一种特别干净的方法,但是我们将在下一节中看到有一种更干净的方法。处理跨步是一个非常敏感的过程,需要非常小心。每次您都必须验证邻*性并采取必要的步骤。
幸运的是,numpy 提供了一些更高级的方法来省去我们大部分的麻烦。
2.3.Numpy.reshape()
图 2.10。numpy.reshape()的输出给出了错误的结果。图片作者。
Numpy . shape()是在 numpy 中操作数组形状的标准、最常见的方法。然而,如果我们在之前的 6 x 4 图像示例中简单地声明我们想要一个形状为(3,2,2,2)的数组,它就不会工作。
这是因为默认情况下,numpy.reshape()试图保持后续值的连续性。这意味着在程序中使用的数组的顺序索引将继续存在于相邻的内存块中。不幸的是,为了得到我们的结果,我们需要打破连续性。尽管如此,出于性能原因,我们可以在将它分成瓦片后使其再次连续。
图 2.11。numpy.reshape()输出的步幅。步幅与我们在上一节中期望的解决方案不同。图片作者。
< <步幅与我们之前的方案不同。
它是如何工作的?它基本上接受一个维度(在任何级别),将其分解为相等的相邻部分,并在更高的级别将它们堆叠在一起。在我们的例子中,它的作用如下:
图 2.12。numpy.reshape()输出的图示。图片作者。
相反,我们想要的是这个:
图 2.13。内存中我们想要的输出布局图。图片作者。
那么,我们该怎么解决呢?
作为两个数组步距提示的简单比较,我们需要做的就是在轴 1 和轴 2(第一维和第二维)之间交换步距。具体来说,我们需要的步距是 (8,2,4,1) ,但是 numpy.reshape()返回一个步距为 (8,4,2,1) 的数组。Numpy 数组为此公开了一个方法,称为 swapaxes:
图 2.14。使用 numpy.reshape()和 swapaxes 方法更正解决方案。图片作者。
然后,numpy.reshape()实现可以推广到最后维度中具有通道的任何 2D 或 3D 图像,如下所示:
情商。2.3.用于将多通道图像分割成拼贴的形状尺寸和 swapaxes 实现。对于灰度图像,您可以省略通道尺寸。图片作者。
3.比较和结论
我们来对比一下执行力!
我们将使用这个看起来很酷的家伙的 RGB JPEG:
图 3.2。图像检查。图片作者。
在我们探索了图像的尺寸之后,我们可以看到它们可以被 25 和 12 整除,所以让我们用它们来表示我们的平铺尺寸。
我们还将使用这个装饰器来为每次执行计时:
图 3.3。我们的计时器装饰器和依赖项。它将用于记录每次进场的时间。图片作者。
我们的 for 循环实现如下所示:
图 3.4。For 循环实现函数被修饰为定时。图片作者。
大踏步的实现是:
图 3.5。跨步装饰实现定时功能。图片作者。
numpy.reshape()的实现是:
图 3.6。Numpy.reshape()实现被修饰为计时。图片作者。
我们将加载我们的图像作为一个 numpy 数组,从命令行设置图块尺寸,运行每个函数并检查数组的相等性。
图 3.7。我们的代码包括平等测试。图片作者。
关键时刻:
图 3.8。结果。Numpy.reshape()优于其他方法。图片作者。
numpy.reshape()方法似乎是最干净也是最有效的。此外,很明显,for 循环实现不能真正与内存视图方法相比较。同样值得注意的是,内存视图性能几乎不受必须生成的切片“数量”的影响,而 for 循环的执行时间会随着工作负载的增加而增加。
最后一点:正如大多数框架所期望的那样,还有一个额外的步骤来将我们的瓦片以批处理的形式带来。要在单个索引中合并我们的图块,我们所要做的就是tiled _ arr . shape(-1, tile _ dimensions)并使用numpy . moveaxis()*在适当的地方重新定位通道维度。通常为(n_tiles,n_channels,t_height,t_width)。
感谢您的阅读!
我希望这篇文章很有见地,你可以在自己的计算机视觉工作流程中实现这里演示的内容!
在 LibreOffice 中创建了插图和公式。
代码:您可以在这里 找到测试脚本 。
如有任何建议或修改,请随时联系我,地址:iosif.doundoulakis@outlook.com
你可以在LinkedIn上联系我。
来源:
【3】:Numpy 文档:Numpy . lib . stride _ tricks . as _ strided()
通过 S3 选择有效地传输大型 AWS S3 文件
理解大数据
使用 AWS S3 选择将一个大的 S3 文件流式处理成易于管理的块,而不需要将整个文件下载到本地
AWS S3 是行业领先的对象存储服务。我们倾向于在 S3 上存储大量数据文件,有时需要处理这些文件。如果我们正在处理的文件很小,我们基本上可以采用传统的文件处理流程,从 S3 获取文件,然后逐行处理。但是问题来了,如果文件的大小比。> 1GB
?😓
导入(读取)大文件导致Out of Memory
错误。它还会导致系统崩溃。有图书馆即。熊猫、达斯克等。非常擅长处理大文件,但同样的文件是在本地,即我们将不得不从 S3 进口到我们的本地机器。但是如果我们不想在本地获取和存储整个 S3 文件呢?🤔
📜让我们考虑一些用例:
- 我们希望每天处理一个大的 CSV S3 文件(~2GB)。它必须在一定的时间范围内处理(如 4 小时)
- 我们需要定期处理来自 FTP 服务器的大型 S3 文件。新文件以特定的时间间隔出现,并被顺序处理,即在开始处理新文件之前必须处理旧文件。
这些是一些非常好的场景,其中本地处理可能会影响系统的整体流程。此外,如果我们在容器中运行这些文件处理单元,那么我们可以使用的磁盘空间是有限的。因此,需要一个云流式传输流(它还可以通过在并行线程/进程中流式传输同一文件的不同块来parallelize the processing of multiple chunks
同一文件)。这就是我偶然发现AWS S3 Select
功能的地方。😎
📝这篇文章关注的是将一个大文件分成更小的可管理的块(按顺序)。然后,通过在并发线程/进程中运行,这种方法可用于并行处理。查看我在这个上的下一篇帖子。
S3 精选
使用Amazon S3 Select
,您可以使用简单的结构化查询语言(SQL)语句来过滤亚马逊 S3 对象的内容,并只检索您需要的数据子集。使用Amazon S3 Select
过滤这些数据,您可以减少亚马逊 S3 传输的数据量,降低检索这些数据的成本和延迟。
Amazon S3 Select 适用于以 CSV、JSON 或 Apache Parquet 格式存储的对象。它还可以处理用 GZIP 或 BZIP2 压缩的对象(仅适用于 CSV 和 JSON 对象)和服务器端加密的对象。您可以将结果的格式指定为 CSV 或 JSON,并且可以确定如何分隔结果中的记录。
📝我们将使用 Python boto3
来完成我们的最终目标。
🧱构造 SQL 表达式
为了配合S3 Select
,boto3
提供了select _ object _ content()函数来查询 S3。您在请求中将 SQL 表达式传递给亚马逊 S3。Amazon S3 Select
支持 SQL 的子集。查看此链接,了解关于此的更多信息。
response = s3_client.select_object_content(
Bucket=bucket,
Key=key,
ExpressionType='SQL',
Expression='SELECT * FROM S3Object',
InputSerialization={
'CSV': {
'FileHeaderInfo': 'USE',
'FieldDelimiter': ',',
'RecordDelimiter': '\n'
}
},
OutputSerialization={
'JSON': {
'RecordDelimiter': ','
}
}
)
在上面的请求中,InputSerialization
决定了 S3 文件的类型和相关属性,而OutputSerialization
决定了我们从这个select_object_content()
中得到的response
。
🌫️流块
现在,我们已经对S3 Select
的工作原理有了一些了解,让我们试着完成一个大文件的流块(子集)用例,就像paginated API works
一样。😋
S3 Select
支持ScanRange
参数,该参数通过指定要查询的字节范围来帮助我们流式传输对象的子集。S3 Select
请求一系列不重叠的扫描范围。扫描范围不需要与记录边界对齐。查询将处理在指定扫描范围内开始但超出该扫描范围的记录。这意味着将在扫描范围内提取该行,并且可以扩展到提取整行。如果是doesn't fetch a subset of a row
,要么读取整行,要么跳过整行(在另一个扫描范围内读取)。
让我们尝试用两个简单的步骤来实现这一点:
1.找出 S3 文件的总字节数
下面的代码片段展示了将对我们的 S3 文件执行HEAD
请求并确定文件大小(以字节为单位)的函数。
def get_s3_file_size(bucket: str, key: str) -> int:
"""Gets the file size of S3 object by a HEAD request
Args:
bucket (str): S3 bucket
key (str): S3 object path
Returns:
int: File size in bytes. Defaults to 0 if any error.
"""
aws_profile = current_app.config.get('AWS_PROFILE_NAME')
s3_client = boto3.session.Session(profile_name=aws_profile).client('s3')
file_size = 0
try:
response = s3_client.head_object(Bucket=bucket, Key=key)
if response:
file_size = int(response.get('ResponseMetadata').get('HTTPHeaders').get('content-length'))
except ClientError:
logger.exception(f'Client error reading S3 file {bucket} : {key}')
return file_size
2.创建一个生成器来传输块
现在,逻辑是产生 S3 文件的字节流块,直到我们达到文件大小。请放心,这种连续的扫描范围不会导致响应中的行重叠😉(检查输出图像/ GitHub repo)。很简单,嗯?😝
import ast
import boto3
from botocore.exceptions import ClientError
def stream_s3_file(bucket: str, key: str, file_size: int, chunk_bytes=5000) -> tuple[dict]:
"""Streams a S3 file via a generator.
Args:
bucket (str): S3 bucket
key (str): S3 object path
chunk_bytes (int): Chunk size in bytes. Defaults to 5000
Returns:
tuple[dict]: Returns a tuple of dictionary containing rows of file content
"""
aws_profile = current_app.config.get('AWS_PROFILE_NAME')
s3_client = boto3.session.Session(profile_name=aws_profile).client('s3')
expression = 'SELECT * FROM S3Object'
start_range = 0
end_range = min(chunk_bytes, file_size)
while start_range < file_size:
response = s3_client.select_object_content(
Bucket=bucket,
Key=key,
ExpressionType='SQL',
Expression=expression,
InputSerialization={
'CSV': {
'FileHeaderInfo': 'USE',
'FieldDelimiter': ',',
'RecordDelimiter': '\n'
}
},
OutputSerialization={
'JSON': {
'RecordDelimiter': ','
}
},
ScanRange={
'Start': start_range,
'End': end_range
},
)
"""
select_object_content() response is an event stream that can be looped to concatenate the overall result set
Hence, we are joining the results of the stream in a string before converting it to a tuple of dict
"""
result_stream = []
for event in response['Payload']:
if records := event.get('Records'):
result_stream.append(records['Payload'].decode('utf-8'))
yield ast.literal_eval(''.join(result_stream))
start_range = end_range
end_range = end_range + min(chunk_bytes, file_size - end_range)
def s3_file_processing():
bucket = '<s3-bucket>'
key = '<s3-key>'
file_size = get_s3_file_size(bucket=bucket, key=key)
logger.debug(f'Initiating streaming file of {file_size} bytes')
chunk_size = 524288 # 512KB or 0.5MB
for file_chunk in stream_s3_file(bucket=bucket, key=key,
file_size=file_size, chunk_bytes=chunk_size):
logger.info(f'\n{30 * "*"} New chunk {30 * "*"}')
id_set = set()
for row in file_chunk:
# perform any other processing here
id_set.add(int(row.get('id')))
logger.info(f'{min(id_set)} --> {max(id_set)}')
图片作者
恭喜你!👏我们已经成功地解决了在不使系统崩溃的情况下处理大型 S3 文件的关键挑战之一。🤘
📌您可以查看我的 GitHub 库以获得这种方法的完整工作示例。
🔖实现更多并发性的下一步是并行处理文件。点击查看这篇文章的续集。
使用 S3-Select 的✔️优势
- 减少 IO,从而提高性能
- 由于数据传输费用减少,成本降低
- 使用多线程/进程中的
ScanRange
,可以并行运行多个块来加速文件处理
S3 选择的❗限制
- 输入或结果中记录的最大长度是 1 MB
- 亚马逊 S3 选择只能使用 JSON 输出格式发出嵌套数据
- S3 选择返回一个编码字节流,所以我们必须循环返回的流并解码输出
records['Payload'].decode('utf-8')
- 仅适用于以 CSV、JSON 或 Apache Parquet 格式存储的对象。为了更多的灵活性/功能,您可以选择 AWS Athena
📑资源
原载于 2021 年 4 月 6 日https://dev . to。
借助 Azure 机器学*和 PyTorch 加速,轻松实现 PyTorch 模型的分布式培训
帮助您开始使用 AzureML CLI v2 的全面指南
当训练深度学*模型时,通常会有一个点,当使用单一机器不再适合你时,通常是因为你需要更多的 GPU 来加速训练!即使对于有经验的从业者来说,从在本地机器上训练模型转移到云中的集群也是一个令人望而生畏的前景。通常,这个过程需要更新您的 PyTorch 训练脚本,以包括初始化过程组和用“分布式数据并行”包装模型等步骤,以及提供和管理基础设施;确保环境一致,并设置正确的变量等。令人欣慰的是,PyTorch 加速的工具和 Azure 机器学*工具使得这个过程尽可能的简单,只需要对你的脚本进行最小的修改。
本文的目的是展示使用 AzureML 运行分布式培训作业是多么简单,并帮助您尽快获得培训!例如,我们将探索如何在 Imagenette 数据集上训练图像分类模型;imagenette 是 Imagenet 中 10 个容易分类的类的子集。然而,虽然我们将使用此培训脚本作为示例,但我们的重点主要是整体工作流程,而不是试图最大限度地提高给定任务的性能。任务本身是本文中最不重要的部分,因为它的想法是,您将会很乐意将脚本更改为您自己的脚本!
免责声明:在我为微软工作期间,我没有被要求以任何方式推广 Azure 机器学*,也没有得到任何报酬。在 Data Intelligence &设计团队中,我们引以为豪的是,根据具体情况和我们正在合作的客户,使用我们认为最适合工作的工具。如果我们选择不使用微软产品,我们会向产品团队提供详细的反馈,说明原因,以及我们认为缺少或可以改进的地方;这种反馈循环通常会导致微软产品非常适合我们的需求。在这里,我选择推广 Azure 机器学*的特性,因为 CLI v2 是我个人选择的基于云的培训工具。
在本地进行培训
在我们看 AzureML 之前,让我们了解并验证一下,我们如何在本地运行训练脚本。
下载数据集
首先,让我们为数据创建一个文件夹,并下载 Imagenette 数据集:
定义培训脚本
现在我们已经下载了数据,让我们为培训任务和输出创建文件夹,并编写脚本:
在这里,我们正在训练一个来自优秀的 timm 库的 Resnet-RS50 模型——这是标准 Resnet 架构的改进版本,我建议使用它来代替常规的Resnet 50——带有 AdamW 优化器和单周期学*速率表;我发现这个配置对于大多数图像分类任务来说是一个很好的默认配置。
因此,我们不需要编写训练循环,或者管理向不同设备移动数据,我们使用 P yTorch-accelerated 的训练器来为我们处理这些问题。这样,我们的代码将保持不变,无论我们是在单个 GPU 上训练,还是在分布于不同节点的多个 GPU 上训练。由于建议使用 Accelerate CLI 启动 P yTorch-accelerated 脚本,这也意味着我们使用的命令也将保持一致,不管我们的硬件如何。
如果您对 PyTorch 加速 不熟悉,并且想在开始阅读本文之前了解更多信息,请查看 介绍性博客文章 或 文档;或者,这很简单,缺乏这方面的知识不会影响您对本文所探讨内容的理解!
在本地进行培训
让我们验证一下训练脚本是否在我们的本地机器上运行。首先,让我们创建一个配置文件,使用 accelerate CLI 指定我们的硬件选项。我们可以通过运行以下命令并回答问题来做到这一点:
accelerate config --config_file train_imagenette/accelerate_config.yaml
我们可以生成以下 yaml 文件:
我们现在可以使用 accelerate launch 命令开始我们的训练跑了:
accelerate launch --config_file train_imagenette/accelerate_config.yaml \
train_imagenette/train.py --data_dir data/imagenette2-320 --epochs 1
这将产生以下输出:
为了使用额外的 GPU 进行训练,我们需要修改我们的 accelerate 配置文件。一种方法是使用与之前相同的过程。或者,由于我们想要更改的只是进程的数量,我们可以直接从命令行覆盖该属性,如下所示。为了避免不得不在不同的运行之间运行“accelerate config”命令,我们将在稍后使用 AzureML 时利用这一点。
accelerate launch --config_file train_imagenette/accelerate_config.yaml --num_processes 2 \
train_imagenette/train.py --data_dir data/imagenette2-320 --epochs 1
我们可以从输出中看到,由于使用了两个 GPU,在训练和验证期间需要的步骤减少了一半。
现在我们已经验证了我们可以在本地运行我们的脚本,让我们探索如何使用 AzureML 来扩展我们的训练。
如果您更喜欢使用 vanilla PyTorch 编写脚本,您仍然可以遵循本文中概述的方法,但是您必须手动处理脚本中所有复杂的分布式训练,并确保您为您的硬件使用正确的启动命令!
azure ml 上的分布式培训
AzureML 为训练模型提供了两种不同的方法:
- 使用 CLI v2 (我个人偏好)
- 使用 Python SDK
这里,我们将重点介绍 CLI v2 的使用。
AzureML CLI v2 提供了一种基于命令行的模型训练方法,其中我们的配置是在 yaml 文件中定义的。如果你*惯于在笔记本上使用 SDK,这可能会有点令人生畏,但是一旦你理解了可用的选项是什么,这就很简单了!
在我们开始培训之前,需要注意一些先决条件。要遵循本指南,您需要:
- 一个 Azure 订阅——在这里很容易注册一个订阅,它包括前 30 天的免费积分,然后在特定阈值下免费使用。
- 安装 Azure CLI 和
- 创建一个 Azure 机器学*工作区— 这很简单,可以使用门户或CLI来完成
- 为了安装azure ml CLI v2
在撰写本文时使用的版本如下所示:
注意: 重要的是,CLI v2 的扩展名显示为“ml”,如果您看到的是“azure-cli-ml”,这是 CLI v1,以下步骤将不起作用!
为了获得 Azure ML CLI v2 中可用功能的概述,我们可以运行命令:
一旦一切都安装好了,我们就可以开始了!
将日志记录添加到培训脚本(可选)
尽管我们的脚本已经准备好按原样运行,但是唯一将被捕获的输出是标准输出日志,当在 AzureML 上运行时,它将被写入一个文本文件。为了帮助我们跟踪和比较不同的实验运行,而不必查看日志,我们可以设置一个日志记录器来记录某些指标和工件,以显示在 AzureML 门户上。
使用 azureml-mlflow 包,我们可以使用 MLFlow fluent API 进行日志记录;更多关于使用 MLFlow 和 AzureML 的信息可以在这里找到。
通过 PyTorch 加速,我们可以为此创建一个回调,如下图所示:
正如我们所见,这根本不需要太多代码!这里,我们子类化了一个现有的 PyTorch 加速回调函数,并覆盖了日志记录方法来记录 MLFlow 这只能在所有节点的主流程中完成,以防止相同的指标被记录两次。通常,当使用 MLFlow 时,我们必须显式地开始和结束运行,但 AzureML 将为我们处理这一点;以及确保为我们设置了包含跟踪 URI 的环境。除了记录指标,我们还将记录训练者的跑步配置,其中包含诸如训练周期数和是否使用了混合精度等信息。如果我们需要重新运行相同的实验,获取这些信息将有助于我们在将来复制训练条件。
此外,正如我们前面看到的,PyTorch-accelerated 默认显示一个进度条。让我们删除它,以保持日志尽可能清晰。我们可以通过移除 ProgressBarCallback 来做到这一点。
让我们更新我们的脚本来反映这些变化。
这里我们可以看到,我们已经更新了传递给培训师的回访列表。因为我们正在将模型保存到。outputs '文件夹,这将由 AzureML 为我们存储。
定义我们的培训环境
既然我们的训练代码已经准备好了,我们需要定义代码执行的环境。AzureML 提供了管理环境——旨在按原样使用——以及通过指定 conda 依赖文件来创建环境的选项,conda 依赖文件定义了要安装到默认基本 docker 映像上的指定包。
虽然这些方法可能是一个很好的开始,因为它们是建立在“默认”docker 映像之上的,并且以这种方式创建的环境通常包括许多您没有指定和不需要的包;在这些环境中安装额外的包可能会产生您没有意识到的冲突。
这些方法的替代方法是定义我们自己的 docker 图像。就我个人而言,尤其是在生产环境中,我喜欢完全控制环境的所有方面,并且倾向于发现,在第一次设置实验时,我花在创建 docker 映像上的少量时间可以节省我以后花在调试环境问题上的更多时间!
对于这个实验,我们可以使用 base PyTorch image 来定义一个自定义 Dockerfile,如下所示。由于我们的基本映像包含 PyTorch 和所有必要的 cuda 库,我们需要指定的唯一包是 AzureML 组件、MlFlow 和运行我们的训练脚本所需的依赖项。
下面演示了这一点:
我们也可以在此时将我们的训练脚本复制到 docker 映像中,但是我们会让 AzureML 稍后为我们做这件事!为此,我们将在实验文件夹中使用一个单独的目录来存储 docker 文件,它将被用作 docker 构建上下文;AzureML 只会在该文件夹中的内容发生变化时触发映像构建过程。通过使用 AzureML 将我们的脚本复制到环境中,我们可以避免在每次修改脚本时触发构建和推送映像的过程。
关于创建 Dockerfiles 的更多信息可以在这里找到。
注册数据集
我们必须做的另一件事是将数据集上传到云中。虽然我们有多种方法可以做到这一点(取决于数据的位置),但我们将通过从本地文件夹注册数据集来做到这一点。我们可以通过定义以下 yaml 文件来做到这一点:
现在,我们可以使用 CLI 在工作区中注册该数据集,并使用以下命令上传文件:
az ml data create -f data/register_dataset.yaml \
--resource-group myResourceGroup --workspace-name myWorkspace
虽然在培训工作中有其他选择来访问数据,但注册和版本化数据集是一种推荐的方法,可以实现实验的跟踪和再现性。
创建计算目标
接下来要做的是定义我们希望运行培训脚本的计算目标。我们可以通过访问 Azure 文档或运行以下命令来了解可用的选项:
这里我们用一个 Standard_NV24 ,它有 4 个NVIDIA Tesla M60GPU。
我们可以按如下方式定义和配置我们的计算集群:
az ml compute create -f infra/create_compute_target.yaml \
--resource-group myResourceGroup --workspace-name myWorkspace
由于我们已经定义了一系列实例,AzureML 将根据需求管理集群的伸缩。
定义培训工作配置
现在,是时候为我们的训练运行定义配置了!这是通过使用一个任务来完成的,它指定了以下内容:
- 跑什么
- 在哪里运行
- 如何运行它
让我们定义我们的作业配置文件,然后分解它:
首先,我们已经指定了我们想要运行的内容,这非常类似于我们用来在本地运行培训的 accelerate launch 命令;关键的区别在于我们覆盖了 accelerate 配置文件的一些属性。
这里,我们使用了由 AzureML 自动设置的环境变量来指定分布式训练所需的机器等级、主进程 IP 地址和主进程端口的值。这将确保在每个节点上使用正确的值,这样我们就不需要为每台机器创建单独的配置文件。
我们还定义了一些输入,作为参数传递给我们的脚本。通过定义输入部分,我们可以选择从命令行覆盖这些值,这样我们就不必为不同的超参数配置修改 yaml 脚本。
要访问我们的数据集,因为它是在我们的工作区中注册的,我们可以使用' azureml '前缀来访问它,后跟它注册时的名称和数据集版本标签;AzureML 将把这个数据集挂载到每个计算实例,并将正确的数据路径传递给我们的训练脚本。
接下来,我们定义了想要运行代码的位置:
这里,我们指定环境——包含我们之前创建的 docker 文件的目录——我们代码的本地路径,以及我们提供的计算目标。提交时,AzureML 将构建 docker 映像,然后将此映像推送到容器注册中心;这将被缓存,直到做出更改。对于每次运行,“代码”路径中的所有内容都将被复制到 docker 映像中,并在指定的计算目标上执行。
最后,我们已经定义了希望如何运行作业,在本例中,就是指定我们的分布式培训配置。
因为 accelerate launch 命令将处理为每个 GPU 创建进程的任务,所以我们只需要在每台机器上执行一个进程。在参考资料部分,我们已经指定了我们希望用于培训的实例数量。
要了解配置作业的所有可用选项,您可以查看文档或检查命令作业模式。
启动 AzureML 培训
现在,让我们使用下面的命令启动我们的培训作业。默认情况下,我们的配置指定在单台机器上使用单个 GPU,因此我们覆盖了其中的一些值,以便在两台机器上使用八个 GPU。
az ml job create -f train_imagenette/train_config.yaml --set \
inputs.num_machines=2 \
inputs.num_processes=8 \
resources.instance_count=2 \
--resource-group myResourceGroup --workspace-name myWorkspace
现在,如果我们导航到 AzureML 工作区中的实验部分,我们应该能够看到我们的训练运行!
在这里,我们可以看到一些图表是基于我们在培训期间记录的值绘制的;这个视图可以根据你的喜好定制。由于我们没有在配置文件中指定显示名称,AzureML 已经指定了一个随机名称;如果我们对多次运行使用相同的显示名称,将会使图表上显示的指标与正确的运行相关联变得更加困难。
我们还可以选择我们的跑步来查看其他信息,包括从教练的跑步配置中记录的标签。
浏览选项卡,我们可以看到记录的指标的更细粒度视图,以及所用代码的快照。
在“Outputs + Logs”选项卡中,我们可以看到由我们的脚本生成的日志,以及我们保存到 Outputs 文件夹的模型检查点:
结论
希望这已经展示了使用 Azure 机器学*和 PyTorch 加速的强大组合,在 Azure 上开始 PyTorch 模型的分布式培训是多么容易!
克里斯·休斯上了LinkedIn。
轻松的超宽 GCNs 分布式训练
图 GIST 培训管道的描述。子 GCNs 将 GCN 模型分成多个子 GCNs。每个子 GCN 都是用聚类操作构造的小批量子训练的。通过 subAgg 操作,子 GCN 参数被间歇地聚集到全局模型中。【图由作者创作。]
在这篇文章中,我将概述最*提出的用于大规模图卷积网络(GCNs)的分布式训练框架,称为图独立子网训练(GIST) [1]。GIST 极大地加速了任何架构的 GCN 训练过程,并可用于支持超过单个 GPU 能力的大规模模型的训练。我的目标是在这篇文章中涵盖 GIST 的最关键的方面,包括相关的背景信息,对训练方法的全面描述,以及关于 GIST 的实验验证的细节。一份完整、详细的手稿被用来描述 GIST,可在 Arxiv 上获得。此外,使用 GIST 进行的所有实验的源代码都可以在 GitHub 上公开获得。
介绍
机器学*和深度学*已经通过它们在工业和科学问题上的许多应用而普及(例如,自动驾驶汽车、推荐系统、人员跟踪等)。),但图形上的机器学*(我将简称为 graphML)最*刚刚成为计算机科学和人工智能研究的焦点。虽然 graphML 的普及有许多原因,但一个主要原因是一个简单的事实,即并非所有数据都可以在欧几里得空间中编码。在许多应用中,图是更直观的数据结构,例如社交网络(即,图的节点是人,边代表社交关系)或化学(即,图的节点代表原子,边代表化学键)。同样地,在欧几里德数据上概括现有的学*策略(例如,卷积神经网络、变换器等。)研究图表是一个很有价值的问题。
为了实现这个目标,已经开发了几种针对图的(深度)学*技术,其中最流行的是图卷积网络(GCN) [2]。受光谱图卷积的一阶*似的启发,GCN 实现了图的卷积运算的一般化。尽管 GCN 很受欢迎,并且在执行节点和图级别的分类任务方面取得了广泛的成功,但该模型的效率非常低,并且难以扩展到大型图。这一问题促进了节点划分技术的发展,包括邻域采样(例如,LADIES 和 FastGCN)和图划分(例如,ClusterGCN 和 GraphSAGE),其将大图划分成计算上易处理的部分。尽管如此,graphML 研究中使用的数据仍然保持在相对较小的规模,由于更深层次网络中的过度平滑问题,大多数 GCN 模型的规模有限[3]。graphML 实验中这种较小数据和模型的使用与主流深度学*研究形成鲜明对比,后者的实验规模不断扩大。
为了弥合深度学*和 graphML 之间的规模差距,GIST 旨在利用更大的模型和数据集进行实验。GIST 可用于训练任何 GCN 体系结构,并与现有的节点划分技术兼容,它通过随机划分全局模型内的隐藏特征空间,将全局 GCN 模型分解成几个等深度的窄子 gcn 来操作。然后,这些子 gcn 被分发到单独的 GPU,并在将它们的更新聚合到完整的全局模型中之前,独立且并行地训练几次迭代。然后,创建/分发一组新的子 gcn,重复相同的过程,直到收敛。在非常大的图的情况下,我们采用现有的图划分方法来形成小批量,这允许 GIST 在任意大的图上训练 GCN 模型。
简而言之,GIST 旨在以最少的挂钟训练时间为大规模 GCN 实验提供一个分布式训练框架。此外,因为 GIST 训练子 gcn 而不是直接训练全局模型,所以它可以用于训练具有超过单个 GPU 能力的极大隐藏层的模型(例如,我们使用 GIST 在 Amazon2M 上训练“超宽”32,768 维 GraphSAGE 模型)。需要注意的是,我们选择关注缩放模型的宽度,而不是深度,因为深度 GCN 模型会受到过度平滑的影响[3]。
要点是什么?
这里,我们解释 GIST 使用的一般训练方法。这种培训方法旨在实现快速、大规模的 GCN 实验,适用于任何 GCN 建筑或取样方法。在我们的解释中,我们假设读者对 GCN 建筑有一个大致的了解。为了全面了解 GCN 的建筑,我们推荐这篇文章。图 1 提供了 GIST 培训方法的全局视图,我们将在以下部分进一步解释该方法的每个组成部分。
创建子 gcn
图 2:m = 2 的 GCN 分割。橙色和蓝色代表不同的功能分区。两个隐藏维度(d1 和 d2)都被分区。输出维度(d3)没有分区,对输入维度(d0)的分区是可选的。GIST 不划分输入维度。【图由作者创作。]
用 GIST 训练 GCN 模型的第一步是划分全局模型的隐藏维度,以形成多个等深度的窄子 gcn(即,图 1 中的sub-GCNs
)。在划分之前,必须知道子 gcn 的数量,表示为m
。然后,对于全局模型的每个隐藏层,该层内的神经元的索引被随机划分成大小相等的m
不相交组,对应于不同的子 gcn。一旦构造了这些分区,通过用来自前一层的分区索引来索引全局权重矩阵的行,用当前层的分区索引来索引全局权重矩阵的列,可以构造任意层的子 GCN 权重矩阵。这样,这种划分方法为对应于已经选择的随机特征划分的每个子 GCN 创建更小的权重矩阵。
图 2 中描述的上述方法对全局 GCN 模型的所有层执行,但是输入和输出维度不包括在划分中。输入维度(即,图 2 中的 d0)没有被划分,因为这将导致每个子 GCN 只能访问每个节点的输入向量的一部分,这将导致m
的值越大,性能越下降。类似地,全局模型的输出维度(即,图 2 中的 d3)没有被分割,使得每个子 GCN 产生相同大小的输出向量。因此,不需要对损失函数进行修改,并且可以训练所有子 gcn 来最小化相同的全局损失函数。
一旦构建了子 gcn,它们每个都被发送到单独的 GPU 进行独立和并行的训练。应当注意的是,完整模型从不被传送(即,仅子 gcn 在设备之间被传送),这极大地提高了利用 GIST 的分布式训练的通信效率。子 GCN 分区的过程如图 2 所示,其中不同的子 GCN 分区用橙色和蓝色表示。回想一下,GIST 中没有划分输入和输出维度,如图 2 的(b)所示。
培训子 GCNs
在子 gcn 被构造并被发送到它们各自的设备之后,它们每个都被独立且并行地训练一组迭代次数(即图 1 中的subTrain
),这被称为局部迭代。当子 GCN 已经完成它们的局部迭代时,每个子 GCN 的参数更新被聚集到全局模型中,并且新的子 gcn 组被创建。这个过程重复进行,直到收敛。如前所述,训练子 gcn 以最小化相同的全局损失函数。此外,在相同的数据上训练每个子 GCN(即,不假设跨设备的数据的非 iid 分区)。
为了确保使用 GIST 训练的模型和使用标准单 GPU 方法训练的模型之间的训练总量保持恒定,使用 GIST 训练的 GCN 模型具有跨子 gcn 分割的训练历元总数。例如,如果普通的基线 GCN 模型使用单个 GPU 训练 10 个时期,那么使用两个子 GCN 用 GIST 训练的可比 GCN 模型将为每个子 GCN 进行 5 个时期的训练。因为子 GCN 是并行训练的,所以每个子 GCN 的训练历元数量的这种减少导致了大的训练加速。
如果训练图足够小,子 gcn 可以并行进行整批训练。然而,在训练图对于要执行的整批训练来说太大的情况下,作为预处理步骤,采用图划分方法将训练图分解成更小的、计算上易处理的子图(即,图 1 中的Cluster
)。然后,这些子图在独立的训练迭代期间被用作小批量,这大致反映了 clusterGCN 提出的训练方法[4]。尽管可以使用任何划分方法,但 GIST 使用 METIS 是因为它在大规模图形上证明了效率[5]。
聚集子 gcn
在子 gcn 完成独立训练之后,在新的子 gcn 的另一轮独立训练开始之前,必须将它们的参数聚集到全局模型中(即图 1 中的subAgg
)。这种聚集通过简单地将每个子 GCN 的参数复制到它们在全局模型中的相应位置来执行。由于在 GIST 中创建的特征分区是不连续的,因此在此过程中不会发生冲突。有趣的是,不是所有的参数都在每一轮独立训练中更新。例如,在图 2 的(b)中,实际上只有重叠的橙色和蓝色块被划分为子 gcn,而其他参数被排除在独立训练之外。然而,如果进行了足够多的独立训练回合,则全局模型中的所有参数都应该被更新多次,因为每个训练回合都利用了新的随机特征划分。
GIST 为什么有用?
乍一看,GIST 培训方法似乎有些复杂,让人想知道为什么要使用它。在这一节中,我概述了 GIST 的好处,以及为什么它会带来更有效的、大规模的图形实验。
与架构无关的分布式培训
GIST 是一种分布式培训方法,可用于任何 GCN 架构。特别地,GIST 被用于训练原始手稿中的普通 GCN、GraphSAGE 和图形注意力网络(GAT)架构,但是 GIST 不限于这些模型。因此,它是一个通用框架,可用于加速任何 GCN 模型的训练。
与取样方法的兼容性
GIST 中的特征划分策略与为有效的 GCN 训练而提出的许多节点划分策略是正交的。因此,这些策略中的任何一种都可以很容易地与 GIST 相结合,以提高训练效率。例如,图划分被用于在具有 GIST 的较大图上训练 gcn,GIST 甚至被用于训练 GraphSAGE 模型。这些实验证明了 GIST 与现有的图形和邻域采样方法的兼容性。
超宽 GCN 训练
GIST 通过训练更小的子 GCN 来间接更新全局 GCN 模型,这使得具有极大隐藏维度(即超过单个 GPU 的能力)的模型能够被训练。例如,当使用 8 个子 GCN 训练具有 GIST 的 GCN 模型时,在单个 GPU 的容量限制下,该模型的隐藏维度可以比模型大大约 8 倍。这种性质使得能够用 GIST 训练“超宽”GCN 模型,如在用 GIST 的实验中所证明的。
提高了模型的复杂性
GIST 显著降低了分布式 GCN 训练的通信和计算复杂度,导致挂钟训练时间的急剧加速。这种复杂度的降低是由于 GIST 只传送和训练比全局模型小得多的子 gcn。GIST 提供的复杂性降低的更精确的表达可在原始手稿中获得。
GIST 在实践中表现如何?
在这一节中,我概述了使用 GIST 进行的实验,这些实验验证了它在显著减少挂钟时间的情况下训练 GCN 模型达到高性能的能力。实验在大量数据集上进行,包括 Cora、Citeseer、Pubmed、OGBN-Arxiv、Reddit 和 Amazon2M。然而,在本文中,我将重点放在 Reddit 和 Amazon2M 的实验上,因为这些数据集要大得多,并且与实际的 graphML 应用程序更相关。较小的数据集主要用作 GIST 方法的设计/消融实验,更多细节可在手稿中获得。
Reddit 数据集
图 3:在 Reddit 数据集上使用 GIST 和标准单 GPU 方法训练的 GraphSAGE 和 GAT 模型的 F1 分数和挂钟训练时间。【图由作者创作。]
GIST 在 Reddit 数据集上的实验是用 256 维 GraphSAGE 和 GAT 模型两到四层进行的。使用多个不同数量的子 GCN 用 GIST 训练模型,其中假设每个子 GCN 被分配到单独的 GPU(即,8 个子 GCN 实验总共使用 8 个 GPU)。使用 Adam 优化器执行总训练的 80 个时期,并且没有权重衰减,并且局部迭代的数量被设置为 500。在训练期间,训练图被分成 15,000 个子图。基线模型使用标准的单 GPU 方法进行训练,所有其他实验细节保持不变。如图 3 所示,使用 GIST 训练的所有模型的性能都达到或超过了使用标准单 GPU 方法训练的模型。此外,与标准的单 GPU 训练相比,GIST 的训练时间显著减少。
Amazon2M 数据集
图 GraphSAGE 模型的 F1 分数和挂钟训练时间,这些模型具有不同的隐藏维度和层数,使用 GIST 和标准的单 GPU 方法在 Amazon2M 数据集上进行训练。【图由作者创作。]
在 Amazon2M 数据集上使用 GraphSAGE 模型进行 GIST 实验,该模型具有 400 和 4096 的隐藏维度(即窄模型和宽模型)以及不同的层数。再次,使用多个不同数量的子 gcn 用 GIST 训练模型,并且训练图被分解成 15,000 个分区。基线实验使用标准的单 GPU 训练方法来执行。使用 Adam 优化器进行训练,在总共 400 个时期内没有权重衰减,并且局部迭代的数量被设置为 5,000。如图 4 所示,与使用标准单 GPU 方法训练的基线模型相比,使用 GIST 训练的模型完成训练的速度明显更快。此外,在所有情况下,用 GIST 训练的模型与用标准方法训练的模型表现相当。
超宽 GCNs
图 5:在 Amazon2M 数据集上使用 GIST 和单 GPU 方法训练的 400 到 32,768 个隐藏维度的 GraphSAGE 模型的性能指标。标有“OOM”的事例在训练过程中导致内存不足错误。【图由作者创作。]
如前所述,GIST 可以用于训练非常广泛的 GCN 模型,因为全球模型是通过子 gcn 的独立训练间接更新的。为了展示这种能力,在 Amazon2M 数据集上训练了具有越来越大的隐藏维度的 GraphSAGE 模型。如图 5 所示,GIST 可用于在 Amazon2M 数据集上训练隐藏维度高达 32,768 的 GraphSAGE 模型,从而以相对最少的训练时间获得高性能。在这些情况下,单 GPU 训练方法会出现内存不足的错误(即使是在小得多的 GCN 模型中),从而证明 GIST 可以用于训练远远超过单个 GPU 能力的模型。此外,与使用 GIST 训练的模型相比,仅使用单个 GPU 训练的模型的挂钟训练时间变得非常令人望而却步,从而突出了其加速大规模 GCN 实验的能力。正如这些实验所表明的,GIST 使 GCN 实验能够在以前不可行的规模上进行。
结论
在这篇博文中,我概述了 GIST,一种用于大型 GCN 模型的新型分布式训练方法。GIST 通过将全局 GCN 模型划分为几个狭窄的子 gcn 来运行,这些子 gcn 分布在单独的 GPU 上,并且在将它们的参数聚集到全局模型中之前独立地并行训练。GIST 可以用于训练任何 GCN 体系结构,与现有的采样方法兼容,并且可以在不降低模型性能的情况下显著加快训练时间。此外,GIST 能够将难以置信的宽 GCN 模型训练到最先进的性能,如 Amazon2M 数据集上的 32,768 维 GraphSAGE 模型。
我真的很感谢你对这篇博客感兴趣。如果您有任何意见或问题,请随时联系我或留下您的意见(联系信息可在我的网站上找到)。GIST 是作为独立子网训练(IST)计划的一部分,在莱斯大学我的研究实验室(T3)内开发的。更多相关项目信息请点击这里。
引文
[1]https://arxiv.org/abs/2102.10424
https://arxiv.org/abs/1609.02907
[3]https://arxiv.org/abs/1801.07606
https://arxiv.org/abs/1905.07953
http://citeseerx.ist.psu.edu/viewdoc/summary?[5]doi=10.1.1.106.4101
轻松的探索性数据分析(EDA)
使用 PyCaret 在 1 行代码中生成全面的 EDA 报告。
什么是 EDA,为什么它是必不可少的?
在我们开始建模之前,理解手头的数据是至关重要的。单单分析数据就能给我们提供有价值的见解来解决我们的问题。此外,理解数据对于确定哪些特征将有助于我们的模型、哪些特征可以去除、我们如何处理缺失值和相关性等等非常有用。在考虑了所有这些因素之后建立一个模型将确保它是稳健的,并且可以很好地推广。
PyCaret 是什么?
由于从 EDA 到模型原型的数据科学管道有些结构化(至少对于简单的 ML 任务),我们可以使用 PyCaret 等工具来自动化 EDA、模型构建和调整超参数。它还有许多其他功能,如打包、提升、堆叠模型、从数据生成多项式特征、用高基数转换分类数据等等,所有这些我将在后面的文章中讨论。今天我们的重点是 EDA。值得注意的是 PyCaret 使用 pandas-profiling 来执行 EDA。
让我们在几秒钟内生成一个回归问题的 EDA 报告。
我在这里使用了流行的加州房价数据集(1990) 。让我们试着分析一下什么特征会影响一个街区内的房价。我强烈建议用户在 Google Colab 或 Jupyter 上尝试这段代码,除非你的设备有足够的 RAM。
- 正在安装 PyCaret 并准备好 Colab。
!pip install pycaret
from pycaret.utils import enable_colab
enable_colab()
import pandas as pd
from pycaret.regression import *dataset=pd.read_csv('/content/housing.csv') #Read the Csv File
2.生成 EDA 报告!
exp1 = setup(data = dataset, target = 'median_house_value', session_id=123 , profile=True)
确保配置文件参数设置为 True
让我们看看我们有什么
首先,值得注意的是,该报告仅在 18.99 秒内生成。另外,我们有一个非常酷的导航栏,可以跳转到报告中的各个部分。
快速转到 sample,它基本上向我们展示了数据集的头部。
导航条
样本部分
接下来
概述部分
概述部分告诉我们数据集结构、缺失单元、重复单元的数量、数据集占用的大小以及变量的数量及其类型。
让我们在变量部分详细观察一个数字特征(经度)。
观察数字特征
一旦我们点击变量名下的 toggle details,我们就有了大量的数据、分位数统计数据、标准偏差、列中的不同值。这只是开始!一旦我们切换到直方图选项卡,我们可以看到变量的分布。
切换到直方图选项卡时
这里还有常用值和极值选项卡,向我们显示列&中值的频率以及列中的最小值和最大值。
现在让我们来看一个分类变量(Ocean_Proximity)
在切换到类别选项卡时,我们会看到一些惊人的图表,尤其是非常重要的饼状图。我们还可以通过图表看到我们的列中不同的值。
分类变量
印象深刻吧?还有呢!
交互式双变量分析
在 interactions 选项卡下,我们可以看到两个变量是如何关联的。请随意使用这些特性。作为一个例子,我们可以看到中值收入与中值房价之间的关系。
相互作用
描述相关性的热图。
我们可以看到所有数字特征是如何相关的&还可以切换相关类型。该报告还描述了如何计算特定类型的相关性。
相关
缺少值!
这一节告诉我们列中缺少的值。我最喜欢的特性是缺失值的矩阵描述,因为它向我们展示了包含缺失值的行!在下图中,白色水平线表示缺少的值。
缺少值
结论
这份 EDA 报告非常有用,几乎涵盖了我们完成简单 EDA 任务所需的所有基本要求。它确实错过了一些重要的特性,比如单向 Anova 测试和卡方测试,但是你不可能在 19 秒和一行代码中完成所有的事情!
虽然这非常方便,但我觉得 EDA 并不是一个僵化的过程,对于棘手的问题,目前不属于本库的创造性技术可能非常有见地。
查看我的GitHub的一些其他项目和完整代码。你可以在我的website上联系我。 感谢您的配合!
轻松的自然语言处理使用预先训练的拥抱脸管道
学*如何用 3 行代码进行自然语言处理
最*,BERT 模型在语言处理领域越来越受欢迎,因为它成功地将最先进的性能与有限的计算能力结合起来。在这篇文章中,我将向你展示如何使用拥抱脸变形金刚库,只用 3 行代码就可以使用这个模型!但首先,让我们看看伯特模型是如何工作的。
伯特是什么?
BERT 代表来自变压器的双向编码器表示。它是一种最新的语言模型,能够在广泛的自然语言处理任务中获得前沿的结果。
BERT 的主要优势之一是它是双向的,这意味着该模型可以一次考虑整个单词序列。与从左到右的方法相反,这允许 BERT 使用所有周围的单词(在左侧和右侧)来上下文化每个单词。
伯特模型的两个阶段。图片来自原创研究论文
此外,您可以使用计算能力有限的 BERT 模型,因为它利用了迁移学*:首先在一些一般任务上训练一个模型(预训练),然后将获得的知识‘转移’到相关的 NLP 任务(微调)。让我们更详细地看看这两个步骤。
预培训
首先,该模型是在像维基百科这样的大型纯文本语料库上预先训练的。预培训应该是通用的,以便以后将该模型用于广泛的目标。其次,预训练是在自我监督的情况下完成的,因此不需要对输入进行标记,这反过来意味着我们拥有几乎无限的训练数据。BERT 模型的预训练在两个任务上完成:
- 掩蔽语言建模(MLM): 对语料库中 15%的词进行掩蔽,目标是预测掩蔽作品。例如,一个被屏蔽的句子可能是
Paris is the [MASK] of France
,模型将尝试预测capital
。 - 下一句预测(NSP): 从语料库中随机抽取两个句子进行组合。目标是预测这两个句子是否在原始语料库中相邻出现。例如,这两个句子可以是
the man went to the store
和he bought a gallon of milk
,它们在逻辑上可以相互跟随。然而,句子也可以是the man went to the store
和penguins are flightless
,它们不太可能连续出现。
这些任务的结合使伯特既能理解单词之间的关系,也能理解句子之间的关系。预训练只需要进行一次(节省计算能力),并且预训练的模型在网上广泛可用,用于一种或多种语言,以及有大小写和无大小写的文本。
微调
但是,预先训练好的 BERT 模型还是很一般的。为了能够将其用于情感分析、命名实体识别、摘要、翻译或其他用途,我们需要针对我们的特定用例对模型进行微调。很大的好处是,这种微调相对便宜:大部分繁重的工作已经在预训练阶段完成,只需要做一次。
谢尔盖·斯韦奇尼科夫在 Unsplash 上的照片
如果你没有一个已标记的训练集,已经微调过的模型也可以在网上广泛获得,例如在拥抱脸模型中心。这是我将在本文中使用的方法。
关于伯特的更多信息,我会推荐github.com/google-research/bert,或者为更高级的读者推荐原始研究论文“伯特:语言理解的深度双向转换器的预训练
拥抱面部变形金刚
使用 BERT 模型最简单的方法之一是使用拥抱脸变形金刚:一个基于 PyTorch 和 TensorFlow 的最先进的 NLP 库。通过他们的模型中心,Hugging Face 目前提供超过 7500 个预训练模型,用于各种 NLP 任务和各种语言。这样,您几乎总能找到符合您特定目标的模型。
看看拥抱脸模型库,其中包含超过 7500 个预先训练的模型。(图片由作者提供)
这些模型中的每一个都可以使用拥抱脸变形金刚库提供的简单方法在您自己的数据集上进行微调。然而,更容易的是,模型也可以开箱即用,只需最少的编程,使用拥抱面部变形器为这 11 个任务提供的管道之一:
- 特征抽出
- 情感分析
- 命名实体识别
- 问题回答
- 遮罩填充
- 摘要
- 翻译
- 语言生成
- 文本到文本生成
- 零射击分类
- 多回合对话
更多信息,请看 github.com/huggingface/transformers 的
使用管道(只有 3 行代码!)
确保你首先安装拥抱脸变形库,比如在你的终端上运行pip install transformers
。然后,您可以开始使用只有 3 行代码的拥抱脸模型!例如,请看下面的情感分析代码:
看,那很简单!您所要做的就是导入库,初始化管道,然后您就可以开始使用这个模型了!如前所述,这些功能使用来自拥抱面部模型中枢的预训练模型。默认情况下,情感分析管道使用distil Bert-base-un cased-fine tuned-SST-2-English模型,但是您可以使用模型中心的任何模型。
我们来看两个扩展:从模型中枢挑选不同的模型,解决不同的任务。
使用模型中心的 7500+模型之一
创建管线时,通过设置model
参数,可以很容易地使用不同于缺省模型的模型。例如,假设我们正在进行一个项目,想要预测财务状况。在 model hub 上快速搜索,我们会看到 ProsusAI/finbert 模型,它专门针对金融内容的情感进行训练。这个模型的实现和前面的例子一样简单,只需包含model
参数:
拥抱脸变形金刚会自动为你下载选中的型号!
其他 NLP 任务
管道目前能够处理 11 种不同的任务,从命名实体识别到翻译。创建管线时,可通过将'sentiment-analysis'
更改为其他名称来选择模型。例如,让我们试着翻译“我喜欢狗!”从英语到德语。转到模型中心,过滤任务“翻译”和语言“德”,您将看到 100 多个模型。我将使用t5-小型型号:
就是这样!要获得管道可以完成的所有任务的完整列表,请看一下这个维基页面。
结论
在本文中,您已经了解了 BERT 模型的工作原理及其训练方式。此外,你已经看到了拥抱脸变形金刚管道是多么强大和容易使用。的确,NLP 对每个人都是可行的!
本文是作为荷兰鹿特丹伊拉兹马斯大学 图灵机&深度学* 课程的一部分而撰写的。更多我的内容,可以看看我的中页或者 GitHub 简介:RvMerle。
EFPN:用于小目标检测的扩展特征金字塔网络
思想和理论
当小事情造成大问题时
自最初几天的智能机器系统以来,目标检测一直是计算机视觉应用中的一个突破。尽管被研究了很长时间,但这个话题似乎永远不会过时,已经成为视频理解和计算机视觉中必须知道的问题之一。我希望你已经了解了物体检测的一些背景知识,因为我将忽略关于物体检测的几个基本概念,比如什么是物体检测,物体检测器有多少种。我相信你可以很容易地找到大量关于计算机视觉的定义和解释。
当技术刚刚取得显著成就时,人们喜欢让事情变得更复杂。在对象检测中,尽管已经提出了许多更具创新性的算法,但我们还会有更多的数据。随着现代图像数据变得越来越具有挑战性,例如包括大量小(或非常小)的物体,传统的物体检测器,如 R-CNN、单次检测器(SSD)和 YOLO,正变得过时,因为它们不能处理小物体检测问题。因此,作为上述方法(级联 R-CNN、DSSD、约洛夫 4、约洛夫 4-5D)的继承者的现代模型已被提出来应对挑战。你可以在这里看看我为 yolov 4-5D 评论和解说写的帖子。在这篇文章中,我将回顾用于小物体检测的扩展特征金字塔网络(EFPN),你可能想在这里阅读全文。
香草 FPN 探测器的问题
香草特征金字塔网络(图片来自论文
如论文中所述,特征金字塔网络(FPN)是在不同尺度的特征地图上检测对象的第一个概念,以增强对象检测器的性能,特别是在小对象检测中。fpn 具有与编码器-解码器架构相同的结构,其中从输入图像提取的特征首先被编码到提取的特征图(瓶颈),然后该信息被再次放大并与相应的特征级别组合以形成用于预测的最终特征图。通过这样做,较低层中的位置信息可以与较深层中的丰富语义信息相结合,以提高整体检测性能。
尽管传统的 FPN 目标检测器可以在一定范围内获得良好的检测结果,但是它们仍然存在一个缺点。如下图所示,检测器不能有效地使用特征图来预测合适的物体尺寸。具体而言,在 FPN 的工作机制中,高分辨率特征图(例如下图中的 1 级)应用于小物体检测,低分辨率特征图(例如 4 级)应负责预测大物体。然而,在几乎所有的特征图级别中都产生了大物体建议,在相同的特征级别(级别 1)中提出了中等物体和小物体,这些统计数据证明了普通 FPN 检测器并不以我们字面上预期的方式工作。
传统 FPN 检测器中金字塔等级和提案规模之间的映射(图来自论文
扩展要素金字塔网络(EFPN)
为了解决上述问题,Deng 等人提出了用于小目标检测的扩展特征金字塔网络(全文 r)。本文的主要贡献可以概括为:
- 改进小目标检测性能的扩展特征金字塔网络(EFPN)。
- 一个特征纹理传输(FTT)模块,为更准确的小物体检测结果提供可信的细节。
- 前景-背景平衡损失函数,以减轻前景和背景之间的区域不平衡。
现在,让我们浏览一下每一项。
1.EFPN 的建筑
EFPN 的管道(图来自报
如上图所示,与香草 FPN 的架构相比,主要区别包括:(1) EFPN 多了一个检测层 P2’;(2) EFPN 利用 FTT 模块将 P2 和 P3 的功能转移到 P2。不同于仅使用较低的和相邻的特征图进行放大的先前步骤,FTT 模块将两个特征图 P2 和 P3 作为其过程的输入,并生成 P3‘其随后用于新的检测层 P2’。EFPN 能够在 5 个不同的尺度上做出预测。
2.FTT 模块
FTT 模块的管道(图来自纸
在 FFT 模块中,首先使用内容提取器从 P3 (Main)中提取语义特征,然后应用亚像素卷积层来放大内容提取器的输出。然后,最新信息与特征图 P2(参考)相关联,以形成用于选择小物体的可信纹理的纹理提取器的输入。最后,建立残差连接用于特征融合并产生输出特征图 P3’。通过应用这种方式,P3 从浅层特征图 P2 中导出选择性特征,并从深层 P3 中接收语义。
3.前景背景平衡损失
提高对象检测性能的经典方法是使用高分辨率输入。受这一想法的启发,作者提出了一种新的训练机制,称为交叉解析蒸馏。
为了应用知识提炼,学生模型需要从教师网络的输出中学*。为了应对这种情况,作者使用该模型以 2x 输入进行测试,然后利用前 4 层的输出作为知识提取的训练目标。例如,与原始输入(1x 输入)的 P5 相比,2x 输入的 P5 具有两倍的分辨率,并且与原始输入(1x 输入)的 P4 具有相同的分辨率,如上图所示。因此,2x 输入的 P3 和 P2 将被分别用作训练原始输入(1x 输入)的 P3 和 P2 的目标。使用以下损失函数来训练学生 EFPN:
其中 L_fbb 是建议的前景-背景平衡损失。这个损失函数实际上是 L1 损失,然而,它由两部分组成:全局重建损失 L_glob 和正补片损失 L_pos。建议的前景-背景平衡损失可描述为:
其中 F 表示计算的特征图,而 Ft 表示目标特征图。λ是重量平衡参数。 L_glob 和 L_pos 公式如下:
其中 P_pos 表示地面真实物体的块, (x,y) 表示特征地图坐标, N 表示正像素的数量。更多详情可以找原论文。
结果
与其他现代方法相比,作者在清华-腾讯 100K 小交通标志数据集和 MS COCO 小对象数据集上测试了 EFPN。定量结果如下表所示。与其他最新算法相比,EFPN 在各种实验设置中呈现了最先进的检测结果。
表格来自纸
表来自的论文
视觉检测结果如下图所示,每个图像对显示了香草 FPN(左)和 EFPN(右)结果之间的比较(红色:假阴性,蓝色:假阳性,绿色:真阳性)。很明显,EFPN 在小物体探测方面优于 FPN。
FPN 和 EFPN 的视觉对比(图来自报)
结论
在这篇文章中,我回顾了扩展特征金字塔网络(EFPN),这是一个用于小物体检测的普通特征金字塔网络(FPN)的改进。EFPN 在准确性和计算方面都显示了它的有效性。EFPN 已经在清华-腾讯 100K 小型交通标志数据集和 MS COCO 小型物体数据集上产生了最先进的结果。
欢迎读者访问我的脸书粉丝页面,这是一个分享关于机器学*的东西的页面:深入机器学*。我关于物体检测的其他著名帖子也可以在yolov 4–5D 评论和 Darkeras 找到。
感谢阅读!
主成分分析中的特征值和特征向量
关于我们的数据,他们告诉了我们什么?
(作者 GIF)
介绍
我在大学的一门线性代数课上学过特征值和特征向量。这是非常枯燥和数学,所以我不明白,这是怎么回事。但是我想用一种更直观的方式向你们展示这个话题,我会用很多动画来说明。
首先,我们将看看如何应用一个矩阵到一个矢量旋转和缩放一个矢量。这将向我们展示什么是特征值和特征向量。然后我们将学*主成分,它们是协方差矩阵的特征向量。这些知识将帮助我们理解我们最后的主题,主成分分析。
矩阵乘法
为了理解特征值和特征向量,我们必须先看看矩阵乘法。
让我们考虑下面的矩阵。
(图片由作者提供)
当我们取一个矩阵和一个向量的点积时,得到的向量是原始向量的旋转和缩放版本。
(图片由作者提供)
(作者 GIF)
在数据科学中,我们大多谈论数据点,而不是向量。但它们本质上是相同的,可以互换使用。数据点也可以像矢量一样通过矩阵乘法进行变换。
(作者 GIF)
但即使矩阵乘法旋转缩放,也是一个线性变换。
为什么矩阵乘法是一种线性变换?考虑一组数据点(用红色表示)。想象一下这些点所在的网格。当我们将矩阵应用于我们的数据点并沿着数据点移动网格时,我们看到网格的线保持笔直。如果这些线是曲线,那么转换将是非线性的。
(作者 GIF)
特征向量
我们考虑与上面相同的矩阵。
(图片由作者提供)
当将这个矩阵应用于不同的向量时,它们的行为是不同的。他们中的一些可能会得到旋转和缩放。有些只旋转了,有些只缩放了,有些可能根本没有变化。
特征向量 s 是向量,其中
- 只进行缩放。
- 或者做完全不变。
(作者 GIF)
你可以看到,特征向量保持在同一条线上,其他向量(一般向量)旋转了一定角度。
(作者 GIF)
2x2 矩阵总是有两个特征向量,但并不总是相互正交。
本征值
每个特征向量有一个相应的特征值。当矩阵对特征向量进行变换时,它是特征向量缩放的因子。如上所述,我们考虑相同的矩阵,因此考虑相同的两个特征向量。
(图片由作者提供)
这个矩阵的两个特征向量之一(我称之为特征向量 1,但这是任意的)被缩放 1.4 倍。
(作者 GIF)
特征向量 2 的缩放系数也是 1.4,但它的方向是相反的。因此,特征值 2 为-1.4。
(作者 GIF)
特征向量和特征值在数据科学中的应用
主成分
使用特征值和特征向量,我们可以找到数据的主轴。第一主轴(也称为“第一主成分”)是数据变化最大的轴。第二主轴(也称为“第二主成分”)是变化第二大的轴,以此类推。
让我们考虑一个二维数据集。
(作者 GIF)
为了找到主成分,我们首先计算方差-协方差矩阵 C 。
(图片由作者提供)
我们可以用 numpy 来计算它们。注意,我们的数据( X )必须像熊猫数据框一样排序。每列代表一个不同的变量/特征。
import numpy as np
C = np.cov(X, rowvar = False)
然后我们就可以计算出 C 的特征向量和特征值。
import numpy as np
eigenvalues,eigenvectors = np.linalg.eig(C)
特征向量向我们展示了数据主轴(主成分)的方向。特征值越大,沿该轴的变化越大。因此,具有最大特征值的特征向量对应于具有最大方差的轴。
(作者 GIF)
我们应该记住,矩阵代表线性变换。当我们将协方差矩阵与我们的数据相乘时,我们可以看到数据的中心没有变化。并且数据在具有较大方差/特征值的特征向量的方向上被拉伸,并且沿着具有较小方差的特征向量的轴被压缩。
直接位于特征向量上的数据点不会旋转。
(作者 GIF)
主成分分析
主成分分析使用特征向量和特征值的能力来减少数据中的特征数量,同时保留大部分方差(因此也保留了大部分信息)。在 PCA 中,我们预先指定想要保留的成分数量。
PCA 算法由以下步骤组成。
- 通过减去平均值并除以标准差来标准化数据
- 计算协方差矩阵。
- 计算特征值和特征向量
- 将特征向量合并成矩阵,并将其应用于数据。这将旋转和缩放数据。主要组件现在与我们的特征轴对齐。
- 保留引起最大变化的新特征,丢弃其余的。
让我们看看 PCA 在二维数据集上做了什么。在这个例子中,我们没有减少特征的数量。减少特征的数量对于高维数据是有意义的,因为这样可以减少特征的数量。
(作者 GIF)
让我们加载虹膜数据集。它包含了三种不同种类的鸢尾花的尺寸。这些品种是海滨鸢尾、杂色鸢尾和刚毛鸢尾。
(作者代码)
让我们快速浏览一下数据集。
print(iris_df.head())
(作者 GIF)
我们可以创建一个所谓的“scree plot ”,看看哪些变量对数据的可变性最大。为此,我们执行第一个主成分分析。
(作者代码)
(图片由作者提供)
正如我们所看到的,前两个部分解释了数据中的大部分可变性。因此,我决定只保留前两个组件,放弃其余的。确定要保留的元件数量后,我们可以运行第二个 PCA,减少特征的数量。
我们看一下我们的数据,它现在是一个数组。
print(iris_transformed[:5,:])
(图片由作者提供)
我们可以看到,我们只剩下两列。这些列/变量是我们原始数据的线性组合,并不对应于原始数据集的特征(如萼片宽度、萼片长度等)。
让我们将数据可视化。
(作者代码)
我们会在 x 轴和 y 轴上看到新的组合特征。植物种类由数据点的颜色表示。
(图片由作者提供)
我们可以看到,数据中的许多信息都被保留了下来,我们现在可以训练一个 ML 模型,根据三个物种对数据点进行分类。
结论
我现在将总结最重要的概念。
矩阵乘法
当我们用一个向量乘一个矩阵时,向量是线性变换的。这种线性变换是旋转和缩放矢量的混合。只缩放而不旋转的向量称为特征向量。它们被缩放的因子是相应的特征值。
主成分
主成分是我们的数据显示变化最大的轴。第一主成分解释了观察到的变化的最大部分,第二主成分解释了第二大部分,以此类推。主分量是协方差矩阵的特征向量。第一主分量对应于具有最大特征值的特征向量。
主成分分析
主成分分析是一种减少数据集中特征数量的技术。它由以下处理步骤组成。
- 通过减去平均值并除以标准差来标准化数据
- 计算协方差矩阵。
- 计算特征值和特征向量
- 将特征向量合并成矩阵,并将其应用于数据。这将旋转和缩放数据。主要组件现在与我们的特征轴对齐。
- 保留我们指定的尽可能多的新特性,放弃其余的。我们保留能够解释数据中最大变化的特征。
来源
https://datascienceplus . com/understanding-the-协方差矩阵/
https://en.wikipedia.org/wiki/Iris_flower_data_set
https://scikit-learn . org/stable/auto _ examples/decomposition/plot _ PCA _ iris . html
数据集
彩虹女神
Iris 数据集和许可证可在以下位置找到:
它是在 creative commons 下许可的,这意味着你可以复制、修改、分发和执行作品,即使是出于商业目的,都无需征得许可。
相关文章
作者撰写的其他文章
想要连接吗?
领英
https://www.linkedin.com/in/vincent-m%C3%BCller-6b3542214/
脸书
https://www.facebook.com/profile.php?id=100072095823739
推特
https://twitter.com/Vincent02770108
中等
https://medium.com/@Vincent.Mueller
你可以成为中等会员,同时支持我
https://medium.com/@Vincent.Mueller/membership
数学附录
计算特征值和特征向量
如果你喜欢数学并想深入研究,我总结了这篇博文中用到的一些数学知识。
我们可以很容易地在 python 中计算特征向量和特征值。
import numpy as np
eigenvalues,eigenvectors = np.linalg.eig(M)
如果我们想手工计算它们,那就有点复杂了。
正如我们所见,当我们将矩阵 M 乘以一个特征向量(用 𝑣 表示)时,这与缩放其特征值 𝜆.是一样的
(图片由作者提供)
我们现在重新排列等式。
(图片由作者提供)
其中 I 是单位矩阵,对角线上有 1,其他地方有 0。它的形状和 A 一样。
(图片由作者提供)
(图片由作者提供)
这个等式成立的唯一方法。
(图片由作者提供)
这只在矩阵的行列式(a-𝜆⋅I)变为 0 时成立。
矩阵的行列式是一个因子,在 2×2 矩阵的情况下,矩阵通过该因子缩放面积,在 3×3 矩阵的情况下缩放体积。如果行列式为零,那么矩阵(a-𝜆⋅I)挤压指向原点(原点就是零点)。这是非零向量成为零向量的唯一方法。****
所以我们寻找所有特征值𝜆,使行列式为 0。
在我们找到了特征值之后,我们可以求解这个方程:
(图片由作者提供)
我们找到了特征向量。
协方差矩阵
方差-协方差矩阵可以使用以下公式根据数据进行估计:
(图片由作者提供)
Python 中的八个“无代码”特性
有时候,你不需要写代码来使用 Python。
Python 变得流行的原因之一是我们可以编写相对较少的代码来实现复杂的功能。Python 开发人员社区欢迎使用简单接口封装复杂实现的库。
然而,这还不是最简单的。你能相信我们甚至可以不用任何代码就使用 Python 吗?
在本文中,我将介绍无需编写任何代码就可以利用的 8 个 Python 内置特性。
0.Python CLI "-m "
图片由 wal_172619 发自 Pixabay
一切从 Python CLI(命令行界面)开始吧。尽管我们不必编写代码来使用稍后将介绍的特性,但我们仍然需要让 Python 知道我们想要执行什么。为此,我们可以使用 Python CLI。
只要我们的机器上安装了 Python,我们就可以在 Python CLI 中显示所有支持的参数。
$ python --help
该屏幕截图只显示了部分帮助输出,因为它太长了。我想强调的是-m
选项。它将运行一个库模块作为脚本。因此,如果该模块实现为支持 CLI,我们将能够直接使用它。
现在我们应该开始了:)
1.不使用 Telnet 测试端口号
图片来自 Pixabay 的 FunkyFocus
有时,我们希望测试 IP:端口的出站网络流量。通常,telnet 是一个不错的选择,尤其是在 Windows 平台上。但是,默认情况下通常不会安装它。我们必须在使用前下载并安装它,如果我们只是想要一个简单的测试,然后丢弃它,这可能是一种浪费。
但是,如果你安装了 Python,你就不必下载 telnet,因为 Python 已经有了。我们试试 Google 搜索它的 443 端口的一个 IP。
python -m telnetlib -d 142.250.70.174 443
正如截图所示,流量是可以的,我们甚至收到了谷歌的空字符串。如果我们试图 telnet 一个我们不应该访问的随机端口,将会抛出一个错误。
python -m telnetlib -d 142.250.70.174 999
2.启动 Web 服务器
如果你以前不知道这个,我想这可能会很惊讶。是的,我们可以不用写任何代码就能使用 Python 启动一个 web 服务器。只需运行如下命令。
python -m http.server
在我们运行它之后,它显示服务器正在监听端口 8000 上的本地主机。然后,我们可以尝试从浏览器访问http://localhost:8000/
。
web 服务器将使用我们作为 root 启动服务器的路径来呈现我们的本地文件系统。换句话说,我们将无法访问根目录以上的任何目录。
你是在问这是做什么用的吗?例如,如果我们在目录或任何子目录中有许多文本/PDF/图像文件,我们可以非常容易和快速地访问它们。
如果你想知道更多有趣的用法,请查看这篇文章。
💔-lines-of-python-code-to-write-a-web-server-46a109666dbf>
如果你遵循上面的文章,并把它变成一个“低代码”的解决方案,你也许能给它添加更多的定制功能。
3.验证和美化 JSON
图片来自 Pixabay 的 Felix Wolf
如果您有一个没有格式化的长 JSON 字符串,它可能很难阅读。通常,我更喜欢使用带有 JSON 插件的文本编辑器,如 Sublime 或 VS code,来使字符串更漂亮。然而,如果我们手头没有这些工具,Python 将能够暂时提供帮助。
假设我们有这样一个 JSON 字符串。出于演示目的,我将使用一个短的。
echo '{"name": {"first_name":"Chris", "last_name":"Tao"} "age":33}'
我们的操作系统不识别它,所以字符串将按原样显示。然而,如果我们添加 Python json.tool
作为魔术,它将被很好地格式化。
echo '{"name": {"first_name":"Chris", "last_name":"Tao"} "age":33}' | python -m json.tool
哎呀!JSON 字符串无效,json.tool
帮助我们识别了这个问题。我们在名称对象后面漏掉了一个逗号。让我添加逗号使其有效。
echo '{"name": {"first_name":"Chris", "last_name":"Tao"}, "age":33}' | python -m json.tool
现在,它以完美的缩进输出!现在很容易读懂。
4.创建文本编辑器
图片来自 Pixabay 的 Werner Moser
是的,我们可以使用 Python 来“创建”一个文本编辑器。当然不是很强大,但是如果你没有装更好的会很方便。此外,它不会比 Vim 和 Nanos 更强大,但它完全是基于 UI 的,而不是命令行文本编辑器。这个编辑器是由idlelib
用 Tkinter 创建的,所以它甚至是跨平台的。
假设我们要编写一个简单的 Python 应用程序来显示当前时间。我们想快速编写代码,但不想下载和安装代码编辑器。现在,让我们运行这个命令。
mkdir get_time_app
python -m idlelib get_time_app/print_time.py
idlelib
如果目录不存在就无法创建,所以如果需要的话我们需要创建一个。在我们运行这个命令之后,在我们保存之前,不会创建print_time.py
。
编辑器现在应该会弹出来。我们可以在里面写一些代码。代码甚至可以是语法着色的。
现在,我们可以按 ctrl+s 保存它,然后关闭它。让我们回到命令行并显示文件的内容,代码肯定在那里。
cat get_time_app/print_time.py
5.创建可执行的应用程序
如果我们只想创建一个简单的应用程序,就像上面写的那样,我们不需要任何第三方库,比如 PyInstaller。Python 内置的 Zipapp 可以提供帮助。
假设我们要打包“获取时间”应用程序,我们可以运行下面的命令。
python -m zipapp get_time_app -m "print_time:main"
在命令中,我们只需要让zipapp
知道 app 的名字get_time_app
,将被用作入口点的 Python 文件的名字print_time
和主方法main
。
扩展名为.pyz
的是我们刚刚创建的应用程序。我们可以将它作为一个文件而不是一个文件夹来分发。
要使用打包的 app,只需用 python 调用即可。
python get_time_app.pyz
6.对字符串或文件进行编码和解码
使用 Python CLI,我们可以加密字符串或文件。先说个有意思的。Rot 13 是一种简单地将任何英文字母向右移动 13 个位置的加密方法。例如,一个“a”(位置:1)将变成一个“n”(位置:14)。
我们可以使用encodings.rot_13
来加密一个字符串,如下所示。
echo "I am Chris" | python -m encodings.rot_13
当然,不要把这个用在任何真正机密的东西上。因为英语有 26 个字母,所以我们可以通过再次运行这个算法非常容易地破译字符串:)
echo 'V nz Puevf' | python -m encodings.rot_13
现在,让我们来看看一个更有用的 base64。我们可以用 base64 格式对字符串进行编码,如下所示。
echo "I am Chris" | python -m base64
然后,我们可以添加一个标志-d
来解码它。
echo "SSBhbSBDaHJpcwo=" | python -m base64 -d
如果我们有一个动态编码的图像文件,这将非常有用。我们可以对文件进行如下编码。
python -m base64 get_time_app/print_time.py
非常有趣的是,解码后的 Python 脚本文件可以动态执行。
echo "ZnJvbSBkYXRldGltZSBpbXBvcnQgZGF0ZXRpbWUKCgpkZWYgbWFpbigpOgogICAgY3VycmVudF90aW1lID0gZGF0ZXRpbWUubm93KCkKICAgIHByaW50KGYnQ3VycmVudCB0aW1lIGlzIHtjdXJyZW50X3RpbWV9LicpCgoKaWYgX19uYW1lX18gPT0gJ19fbWFpbl9fJzoKICAgIG1haW4oKQo=" | python -m base64 -d | python
7.获取系统元数据
如果我们想获得当前的系统信息,Python 提供了一种简单的方法。我们可以运行下面的命令。
python -m sysconfig
显示所有系统配置,如路径和环境变量。还有更多的东西显示,截图只显示了一部分。
如果我们只想显示路径和当前工作目录,我们也可以运行这个命令。
python -m site
8.压缩文件
图片来自 Pixabay 的Steve buiss NNE
我们也可以使用 Python 压缩文件,而不需要下载 tar/zip/gzip。例如,如果我们想压缩我们刚刚在第 4 节中编写的应用程序,我们可以运行下面的命令将文件夹添加到一个 zip 文件中。在该命令中,选项-c
代表“创建”。
python -m zipfile -c get_time_app.zip get_time_app
当然,我们也可以提取它。让我们提取文件夹,并把它放入一个新的目录,这样它就可以从原来的目录中分离出来。在下面的命令中,选项-e
代表“提取”。
python -m zipfile -e get_time_app.zip get_time_app_extracted
然后,我们就可以验证了。
ls get_time_app_extracted
cat get_time_app_extracted/get_time_app/print_time.py
我用 zip 做演示,Python 也支持 tar 和 gzip。
摘要
在本文中,我介绍了一种无需编写任何代码就能使用 Python 内置库的方法。如果我们确实记得使用它们,它确实提供了许多方便。希望你喜欢阅读这篇文章,内容会有所帮助!
https://medium.com/@qiuyujx/membership
如果你觉得我的文章有帮助,请考虑加入 Medium 会员来支持我和成千上万的其他作者!(点击上面的链接)
爱因斯坦指数符号
爱因斯坦求和、指数符号和数值
爱因斯坦符号与矩阵
作为一个线性代数迷,向量和矩阵迷,我很长一段时间都不清楚为什么我应该使用爱因斯坦符号。但是当我对反向传播微积分感兴趣的时候,我到了一个点,张量参与其中,然后我意识到用矩阵的方式思考将我的思维限制在二维。在这篇文章中,我仍然会使用许多矩阵和向量的类比,以便这个主题变得更容易理解。
自由指数
自由指数是出现在等式两边的指数。例如:
(图片由作者提供)
𝑣现在可以表示一行或一列向量。
(图片由作者提供)
这并不是索引符号的要点。你把自己从任何矢量的具体表示中解放出来。
我们还可以有两个自由指数:
(图片由作者提供)
我们可以想象这个方程描述了矩阵的行和列 𝐴 。
(图片由作者提供)
然而,如果我们继续增加自由指数的数量,就越来越难以想象一个具体的表示。
如果有 3 个自由指数,它将是一个张量,我们可以把它想象成一个矩阵向量。
(图片由作者提供)
虚拟指数
哑元可以出现在等式的一边,作为指数,它们在每个产品中出现的次数是偶数。
一个例子是:
(图片由作者提供)
这个等式也可以写成一个行向量和一个列向量的内积。
(图片由作者提供)
爱因斯坦求和约定
当我们使用应用这一惯例时,即使没有求和符号,我们也会对虚拟索引求和。这个约定是有用的,因为虚拟指数的求和在线性代数中经常发生。
应用这一约定,最后一个等式可以改写如下:
(图片由作者提供)
上限和下限指数
有些人适用以下约定,有些人不适用。如果我必须在索引符号和矢量化形式之间快速转换,我自己也会使用它。
使用这个惯例,我们写下和上索引。请不要将上面的指数与“的幂”相混淆。
那么只有彼此对角的相同指数被求和。
重复索引的示例,我们对其求和:
(图片由作者提供)
另一个例子:
(图片由作者提供)
重复索引的示例,我们不对其进行求和:
(图片由作者提供)
组合自由和虚拟索引
大多数情况下,我们会在同一个等式中找到空闲和虚拟索引。对于一些读者来说,这可能一开始听起来很可怕,但是看过几次之后,我相信你会欣赏它的抽象性。
让我们看看下面的等式:
(图片由作者提供)
我们将用索引符号重写它:
(图片由作者提供)
这里可以看到,索引 I 是一个自由索引,索引 j 是一个伪索引,get 被求和。
如果你想只使用下标,你可以这样写:
(图片由作者提供)
np.einsum
在 numpy 中,你可以使用爱因斯坦符号来乘数组。这为 np.dot()函数提供了一个替代方法,它是线性代数点积的 numpys 实现。
但是 np.einsum 能做的比 np.dot 多。
np.einsum 可以以任何可能的方式将数组相乘,此外:
- 沿轴求和
- 转置输入和输出数组
以及这些操作以任何顺序的任何可能的组合。
np.einsum 的第一个例子
我们先来看一个例子。我们创建两个 2D 数组: A 和 B 。
(作者代码)
当现在计算 A 与 B 的点积时:
(作者代码)
输出是:
[[ 6 12 18]
[ 9 18 27]
[12 24 36]]
我们现在想对 np.einsum 做同样的事情:
(作者代码)
结果是一样的:
[[ 6 12 18]
[ 9 18 27]
[12 24 36]]
但是这是怎么回事呢?我们必须理解所谓的签名字符串:“ik,kj->ij”。
箭头的左边是:ik,kj。这部分指定了输入数组的索引。
箭头的右边是:ij。这部分指定了结果数组的索引。
整个签名串将意味着:“第一输入数组具有索引 ik,第二输入串具有索引 kj。这些索引被转换成输出数组中的索引 ij”。
(图片由作者提供)
相应的数学等式如下所示:
(图片由作者提供)
NP . einsum 的第二个例子
假设我们有相同的两个数组 A 和 B 。
(作者代码)
我们想在元素方面增加它们。在 numpy 中,我们可以这样做:
结果将是:
[[ 1 4 9 ]
[ 2 6 12]
[ 3 8 15]]
我们对 np.einsum 做同样的事情:
(作者代码)
得到的数组是相同的。
[[ 1 4 9 ]
[ 2 6 12]
[ 3 8 15]]
阵列乘法以如下方式执行:
(图片由作者提供)
NP . einsum 的第三个例子
我们现在想要 A 和 b 的转置的点积。
我们编写了与 A 和 B 的点积相同的代码,但是我们交换了 B 的索引。
阵列乘法以如下方式执行:
(图片由作者提供)
NP . einsum 的第四个例子
好吧,假设我们想要执行相同的元素乘法,但是想要在列轴(轴 1)上求和。
np.einsum 可以做到:
结果如下所示:
[14 20 26]
请注意,numpy 减少了输出结果中的一个维度,得到的向量只是 1D。
数组乘法是这样执行的:
(图片由作者提供)
结论
到目前为止,您应该对爱因斯坦符号和 np.einsum 的工作原理有了基本的了解。如果对你来说这是一个新的话题,那么我强烈建议你用 np.einsum 做实验,改变虚拟索引和自由索引,看看结果如何变化。
相关文章
https://towards data science . com/back propagation-in-neural-networks-6561 e 1268 da 8
https://towards data science . com/back propagation-in-neural-networks-6561 e 1268 da 8
想联系支持我?
LinkedIn
https://www.linkedin.com/in/vincent-m%C3%BCller-6b3542214/
脸书
https://www.facebook.com/profile.php?id=100072095823739
Twitter
https://twitter.com/Vincent02770108
Medium
https://medium.com/@Vincent.Mueller
成为 Medium 会员并支持我(你的部分会费直接归我)
https://medium.com/@Vincent.Mueller/membership
一个被低估的函数
一个功能—多种可能性。如何简单明了地使用线性代数进行深度学*?
Taton moise 在 Unsplash 上拍摄的照片
线性代数在深度学*领域发挥着基础作用。它总是关于形状,移调等。PyTorch、Numpy 和 Tensorflow 等库为此提供了很多函数。但是您可能会忘记其中的一个或另一个,或者将一个函数与另一个库中的一个函数混淆。
尽管阿尔伯特·爱因斯坦肯定没有这个问题,他用所谓的 爱因斯坦符号 帮助我们。这个想法很简单:省略了 sum 字符以改善概述,取而代之的是,对出现两次以上的索引进行求和。
变成了:
谢谢,艾伯特🙏!
有了爱因斯坦符号和 einsum 函数,我们可以只用一个函数就用向量和矩阵进行计算: torch.einsum(方程,操作数)* 。我将在接下来的代码中使用 Pytorch 的 einsum 函数,但你也可以使用 Numpy 的或 Tensorflow 的函数——它们可以互换。我们将看到 einsum 的不同用法,以及本机 PyTorch 函数。
对于这种神奇的理解来说,最重要的是指数的概念。有两种类型:
- 自由索引-在输出中指定
- 总和指数—所有其他
让我们看一个简短的例子:
torch.einsum(‘ik, kj->ij’, X, Y)
也许你已经明白这里发生了什么:这是矩阵乘法。 i 和 j 是所谓的自由指标,,k 是一个求和指标。后者可以定义为发生求和的指标。如果我们将矩阵乘法想象成嵌套循环, i 和 j 将是外部循环,k 循环将是求和循环:
很简单,对吧?所以让我们开始吧!
排列
这可能用于其他事情,但转置一个向量或一个矩阵似乎是最著名的用例。
我们只需切换标识符——瞧。简单,即使 X.T 也是一个精致的解决方案😉。
总和
在这种情况下——简单的求和,我们不返回索引。输出是一个标量。或者准确的说,只有一个值的张量。
行列求和
一个索引产生了差异——按行或列求和。
逐元素乘法
Pytorch 的实现非常简单——只需使用乘法运算符( *** )。用 einsum 看起来怎么样?
这里的索引总是平均排列的。 i , j 乘以 i , j 给出一个形状相同的新矩阵。
点积
可能是比较有名的手术之一。也称为标量积。顾名思义,它返回一个标量。
einsum 函数没有输出索引,这意味着它返回一个标量。
外部产品
两个坐标向量的外积是一个矩阵。
矩阵向量乘法
要将矩阵乘以向量,矩阵的列数必须与向量的行数相同。
这是一个很好的例子,说明了 einsum 函数如何处理两个操作:转置 y 和乘法。
矩阵-矩阵乘法
深度学*中最重要的计算之一就是矩阵乘法。而且在机器学*的其他领域,也经常用到这个函数。
批量矩阵乘法
最后,我们来看看批量矩阵乘法。即使 Pytorch 的实现简洁明了,但用一个函数来完成所有的线性代数计算还是不错的。
我希望这几个例子能让我的理解更加清晰。关于它还有很多(例如,广播)。但是现在,应该就是这样了。然后还有 einops 。一整个图书馆都是张量运算——看看吧。下次见——再见。
弹性搜索 411
Elasticsearch 是一个开源的分布式数据搜索引擎,越来越受欢迎——下面是你应该知道的 411
JOSHUA COLEMAN 在 Unsplash 上拍摄的照片
如今,由于数字化,非结构化数据正以比以往更高的数量和速度生成。组织坐拥大量非结构化数据,但从这些数据中检索有效信息并非易事。Elasticsearch 是一个流行的面向文档的搜索引擎,以其快速高效地搜索和检索非结构化和结构化数据的能力而闻名。
在本文中,我将对 Elasticsearch 做一个简单的介绍,到此结束时,初学者将能够理解它的优势、架构和一些值得注意的用例。
什么是 Elasticsearch?
有很多像 MongoDB、PostgreSQL、Solr 这样的 NoSQL 数据库可以用来存储和查询非结构化数据。然而,这些 NoSQL 数据库执行复杂查询的能力有限,并且它们通常不能很好地执行实时信息检索。这就是 Elasticsearch 填补空白的地方。
Elasticsearch 是一个具有分析能力的开源分布式搜索引擎。它建立在 Lucene 框架之上,用 Java 编写。从 2010 年它的创造者 Shay Banon 首次发布以来,Elasticsearch 就获得了开发者和公司的积极关注。它越来越受欢迎很大程度上是因为它强大的搜索引擎,可以搜索任何类型的数据,包括文本、数字、地理空间、结构化和非结构化数据。
与 NoSQL 数据库不同,Elasticsearch 主要被设计成一个搜索引擎,而不是一个数据库。它有惊人的能力来执行快速,几乎实时,和先进的搜索。通常,它集成在其他数据库之上,但 Elasticsearch 本身也可以用作数据存储。
Elasticsearch 的另一个强大且广受欢迎的特性是,可以使用其强大的 Restful APIs 进行搜索,因此其他系统很容易与之集成。
弹性研究架构
让我们一个接一个地快速看一下 Elasticsearch 架构的不同组件。
文档和类型
一个文档是 Elasticsearch 中数据的基本单位。文档以 JSON 格式保存,可以有多个属性,并且与一个类型相关联。例如,如果我们想要添加关于电子商务产品的数据,那么我们可以创建一个“产品”类型的文档对象,其属性类似于名称、描述、价格、评论。类似地,我们可以创建客户类型的文档,这些文档可能具有像姓名、联系人、地址、订单历史这样的属性。
对于那些来自 SQL 背景的人来说,他们可能将类型关联为 RDBMS 的表,将文档关联为表的行,将属性关联为表的列。
然而应该注意的是,Elasticsearch 已经计划淘汰类型的概念。他们已经在 6.0 和 7.0 中限制了类型的使用
弹性搜索指数
逻辑上相关的文档或那些具有相似特征的文档被组织在称为索引的公共集合下。在我们的例子中,所有的产品文档被绑定到一个索引,所有的客户文档被绑定到另一个索引。
索引类似于 RDBMS 中的数据库。然而,请注意,我们与 RDBMS 的所有类比都是为了便于理解,这并不是说 Elasticsearch 的工作方式与 RDBMS 类似。
倒排索引
一旦文档被编入索引,Elasticsearch 就会创建一个称为倒排索引的数据结构,它主要负责闪电般快速的搜索结果检索。简而言之,倒排索引包含这些术语以及这些术语在所有文档中出现的信息。下图将清楚地说明这一点。例如,如果搜索“blue ”, elastic search 将在倒排索引中进行查找,并快速返回文档 1 和 2。
弹性搜索指数图解(图片由作者提供,来源)
节点和集群
在 Elasticsearch 中,数据分布在多个称为节点的服务器上。当启动查询时,必须从多个节点整合搜索数据的结果。所有这些节点的集合称为一个集群。
分片和复制
Elasticsearch 将一个索引分解成多个分片,每个分片本身就是一个保存在一个节点上的全功能索引。分片有助于跨多个节点水平分布索引所需的资源,并确保将来的可伸缩性。
Elasticsearch 的另一个非常重要的特性是跨集群上的多个节点复制分片,以提高容错能力。如果一个节点由于某种原因出现故障,那么由于碎片的复制,相同的数据在另一个节点上可用。
什么是弹性栈(以前叫 ELK 栈)
不谈到弹性叠加,对弹性研究的介绍是不完整的。Elasticsearch 是一个通用的文档搜索引擎,可以独立工作。然而,大约在 2010 年代早期创建 ES 的同时,乔丹·西塞尔创建了一个名为 Logstash 的数据摄取工具,它能够将日志数据摄取到任何目标系统中,其中之一就是 Elasticsearch。
现场还有一个可视化工具,叫做 Kibana。基巴纳是由拉什德·卡汉开发的,因此它可以用来有效地可视化日志数据。人们发现这三个工具对于日志搜索和日志分析的常见用例非常有用。于是三位产品负责人决定在 2012 年联手,正式组建了 ELK stack,即 Elasticsearch、Logstash、Kibana stack。这个名字后来更名为弹性堆栈。此后,弹性堆栈在数据工程领域广为人知。
弹性栈基巴纳仪表板(信用:【https://www.elastic.co/】T2)
基巴纳的挑战
尽管 Kibana 仍然是可视化组件的一个流行选项,但是最*出现了一些具有更多特性的其他选项。
其原因是,尽管 Kibana 非常适合在 Elasticsearch 中可视化日志数据,但 Elastic 的用例已经远远超出了它们最初的日志数据来源(参见下面关于用例的部分)。
由于范围的扩大,基巴纳
为什么 Elasticsearch 受欢迎
根据 Stackshare 的调查,以下是开发者和公司选择 Elasticsearch 的主要原因:
- 强大的 API
- 伟大的搜索引擎
- 开放源码
- 安静的
- *实时搜索
- 自由的
- 搜索一切
- 易于上手
- 分析学
- 分布的
弹性搜索的用例
Elasticsearch 在日志搜索和分析、应用程序监控、网络搜索、应用程序搜索和商业分析方面有着广泛的应用。有许多知名的公司和企业都在使用 Elasticsearch,让我们来看看其中的一些用例。
● 辉瑞在其名为科学数据云(SDC)的数据湖之上使用 Elasticsearch 进行审计、搜索和*乎实时的仪表板报告。
● 纽约时报正在利用 Elasticsearch 搜索过去 160 年出版历史中的 1500 万篇文章。
● Ebay 正在使用 Elasticsearch 在几秒钟内搜索 8 亿个商品列表。
● Groupon 正在利用 Elasticsearch 为其客户检索最相关的本地交易。
● GoDaddy 正在使用 Elasticsearch 及其内置的机器学*功能来检测他们日志中的异常,以提供更好的客户体验。
弹性机器学*(信用:https://www.elastic.co/))
结论
如果您是初学者,这篇文章会让您对 Elasticsearch 及其特性有一个较高层次的了解。我们还简单介绍了麋鹿生态系统以及一些真实的行业用例。我们希望这将成为你为你的项目需求采用 Elasticsearch 的动力和垫脚石。
肘部和轮廓:Python 中的实际客户细分
用 k-Means 和 Mean Shift 对混合分类和数值数据进行聚类。Scree 图、肘点和轮廓分数。
不是所有的顾客都一样。他们的偏好、购买力、签订长期合同的意愿以及其他特征会有很大差异。
StockSnap 拍摄的站着的女孩购物袋——由 StockSnap 拍摄的免费照片
产品供应、价格价值主张或促销活动可能会引起一部分客户的共鸣,但对其他客户来说却没有效果。会有代沟和性别差异。一些顾客在购买某些产品时会更加吝啬,这可能是出于需要,也可能是出于意愿。一些人会犹豫是否签订长期合同或订阅,而另一些人则喜欢与他们信任的商业伙伴建立长期稳定的关系。他们中的一些人可以享受全面的一揽子商品和服务,即“黄金会员”。其他人喜欢从产品范围中自由挑选几个选项。另一方面,将每一位顾客都视为唯一的买家是荒谬的。这是一个很好的广告口号,向所有顾客保证他们是独一无二的,每个人都是如此。但我们没有。
客户细分——又名聚类——有助于将市场划分为兴趣和属性相似的子群体。市场由当前活跃的客户组成;由于已知或未知的原因在某个时间点停止从该企业购买产品的不活跃客户,以及营销团队希望重新激活的客户;以及公司希望吸引的潜在未来客户,以获得市场份额。客户细分有助于企业定制其产品和服务、价格策略和营销信息,以提高对不同客户群体的影响力。
传统的客户细分方法依赖于公司销售人员的直觉:“我了解我们的客户。我每天和其中的 3 个,有时甚至 5 个聊天。”或许他们是对的。但是在一个有一定规模的公司里,单个的销售经理只能监管整个市场的一小部分,比如某个特定的区域或某条产品线。
现代方法使用数据科学方法来分析所有销售区域和整个产品范围的源数据。它试图客观地识别区分不同客户群的模式。与第一种方法一样,判断要求将随后出现:在分析从原始数据中提炼出可量化的事实之后。
客户细分是一个聚类问题。我们将浏览一个示例,并从不同的角度处理集群任务。
我们将讨论以下步骤:
- 将分类变量转化为数值变量
- k 均值聚类
- —k 均值的概念
- —主要成分
- —肘图和惯性分数
- —剪影分数
- —集群景观:集群间距离图
- —解释:群集分析—仪表板
- 均值漂移聚类
- 结论
电子商务在线销售,由 Mediamodifier 提供 Pixabay 上的免费照片由 Mediamodifier 提供
1.属国
除了我们的核心库——numpy、pandas 和 matplotlib——之外,我们还导入
- Scikit-learn 与聚类、PCA 和预处理相关的类
- Yellowbrick 包——在 sklearn 的许多其他可视化工具中——提供了 k-Means 算法的肘图
- kmodes 包——覆盖我们将应用 k 原型的章节
- kneed 包,将牵制肘点为我们
2.数据加载和争论
我们将使用的数据源是电信数据集,它是 IBM 在一个公开的 GitHub 站点上提供的,并且多年来,它已经成为 TDS 文章和其他地方的客户流失预测演示的全球首选数据集。今天,我们将使用它的摘录作为客户细分的教程。
作者图片
作者图片
对缺失值的检查不会在源数据中发现任何缺口。我们可以进入下一步:为 k-Means 聚类准备非数字值。
3.数字变量的分类
k-Means 和 Mean Shift 算法只能处理数字数据。因此,我们将把非浮点值的源数据转换成整数,使它们在数学上易于理解。
- 我们要对其应用聚合函数(如 sum 或 mean)的列应为类型 float64 。
- 除了对其元素进行计数之外,不进行计算的列应该是类型 object/string ,为即将到来的转换步骤做准备。
我们首先将任期变量从整数转换为浮点数。任期是一个变量,我们可以并希望计算平均值,中位数和其他描述性统计数据。我们还将 SeniorCitizen 列重新定义为字符串/对象变量,它只是一个值为 0 或 1 的二进制变量。
select_dtypes()。nunique() 函数显示了我们在每个类别列中找到的唯一值的数量。这些是将被映射到集群的特征。
具有连续数值的两列是 MonthlyCharges 和 tenure,我们可以对它们应用 pandas 的 describe() 函数来理解它们的内容。
作者图片
作者图片
在对源数据进行更深入的更改之前,我们复制数据帧,以便保留其原始值和结构。
- 包含数千个唯一值的列 customerID 对于聚类没有意义,因此我们将其从复制的数据帧 dfa2 中删除。
- 然后我们选择第 8 行中的所有数值变量;
- 并在第 10 到 13 行对其应用 sklearn 的标准 scaler,以防止聚类算法被列值之间的巨大差异所困。
作者图片
接下来,我们需要将分类变量转换成数值。我们可以在两种可供选择的方法中进行选择:
- one-hot 编码,它为每个分类列中的唯一值创建虚拟变量——这是后续文章将演示的方法;
或者
- pandas 的 cat.codes 构造函数,它自动将字符串值映射到唯一的整数。
这两种方法都不是没有问题的。我们需要意识到,当我们计算平均距离时,它们会产生*似值。如果一个列包含不同的字符串值,例如,四个客户会员等级,如白金、黄金、白银和经济,标签将被转换为 0 到 3 之间的整数。数字的排序顺序不会反映任何固有的值差异。整数不会是序数。1 可以自动分配给白金,2 分配给经济,3 分配给白银,0 分配给黄金,这种排序顺序与现实没有合理的联系。此外,整数值并不表示会员等级经济从任何可以想象的角度来看都比白金价值高 2 倍。
在第 2 行到第 4 行,我们编写了一个助手函数 catcode ,它将 dataframe 的一个 categorial 列作为其参数,并对其应用 pandas 的as type(" category "). cat . codes转换器。然后,它从数据帧中删除原始列。
第 9 行的 list comprehension 调用了 helper 函数。在其右侧,comprehension 选择它在 dataframe 中找到的所有非数字列(过滤掩码: dtypes == np.object )。中间的for-in构造将这些分类列中的每一个、 col、传递给左侧的表达式,该表达式将其传递给 catcode 方法,在该方法中,字符串被转换为唯一的整数值。
第 10 行报告了每一列包含多少唯一值——现在是 integer 或 float 类型。
作者图片
4.k 均值聚类
4.1 均值概念
k-Means 是一种无监督的数据科学方法,它将观察值映射到更小数量的标签,即所谓的聚类。一个星团的中心,即质心,是其观测值的平均值。每个点都必须比其他质心更靠*自己簇的中心。我们希望在一个集群内获得相似的分组,紧紧围绕其中心,但与相邻的集群不同。
该算法最重要的超参数是应该将观察值映射到的聚类数 k。
三种可供选择的方法将有助于我们推断出适当的数字 k:
- 一个视觉线索和一个度量:主成分的数量
- 一个视觉线索,由一个度量证实:在一个碎石图中的肘点;我们还将应用一种直接计算肘点的方法,而无需参考图
- 一个由视觉线索支持的指标:最大轮廓分数
稍后,我们将试用 sklearn 的 Mean Shift 聚类类作为 k-Means 的替代。均值漂移是一种聚类算法,它不需要将聚类数预设为超参数。
拐点试探法要求我们找到图表中曲线的拐点。因为它们是启发性的,它们并不总是提供一个困难的、唯一的答案,而是开放的解释。第三种方法搜索最大的轮廓分数,并返回唯一的答案。
让我们研究一下这些方法。
4.2 聚类数:主成分
主成分分析 PCA 将揭示数据帧中有多少列可以解释总体方差的 80%—90%。
当我们处理高维数据集时,一个有许多列的数据集,我们应该从 PCA 开始提取相关的维数。所谓的维度诅咒会折磨欧几里德距离度量,就像那些在 k-Means 聚类中使用的度量,因为“在高维数据中……所有的对象都显得稀疏和不相似”(维基百科)。PCA 有助于确定表征数据集的特征分组的数量。
主要成分排列在下图所示的碎石图中。图表显示,前五列可以承担大部分的解释责任。超过第十个 PCA 特征的成分的贡献变得可以忽略。我们可以得出结论,可以描述数据的聚类数量不会超过 k = 12,并且可能在 4 到 6 附*,之后 PCA 特征贡献开始减弱。
作者图片
4.3 集群数量:搜索弯头
k-Means 试图最小化惯性。惯性测量一个观察值和它被分配到的簇的中间(质心)之间的距离,是所有观察值的总和。它表达了聚类的内聚性:聚类算法能够将它们紧密地组合在一起。
2.3。集群-sci kit-learn 1 . 0 . 1 文档
肘图是一种启发式方法,使我们能够根据图表中的视觉线索选择适当数量的集群。该图表绘制了一条得分曲线,每一个备选的聚类数对应一个得分。肘部标记了线条显示其最大曲率的点。在我们达到这一点之前,群集数量的增加有助于降低误差平方和 SSE。当每个观察值都有自己的聚类时,SSE 缩小到零,这将违背聚类练*的目的。因此,我们并不寻求将 SSE 最小化到零,而是找到一个合理的折衷方案。在拐点之后,我们会看到收益递减:通过增加更多的集群,上证综指的增量下降会随着我们越出拐点而变得越来越微不足道,并且在越过曲线的拐点(拐点)后会相对更快。
为了绘制肘图,我们可以使用 Yellowbrick visualizer 包。Yellowbrick 包装了 sklearn 的许多类,并提供了一个图表类型目录,其中一个肘图接受 k-Means 算法的一个实例作为其参数。
要 pip 安装或 conda 安装 Yellowbrick,请使用:( Yellowbrick )
- $ pip 安装 yellowbrick
- $ conda install-c district data labs yellow brick
Yellowbrick 为集群任务提供了三种类型的图表:
- 手肘法
- 剪影可视化器
- 聚类间距离图
我们通过编写一个助手函数 elbowplot 来开始搜索肘点,该函数将实例化 Yellowbrick 的 KElbowVisualizer (第 2 到 13 行)。
第 23 行的 list comprehension 调用 helper 函数三次,对于三个可选的度量:
- 扭曲 或观察点与其质心的平方距离
- 剪影分数
- 卡林斯基-哈拉巴斯分数
作者图片
失真测量观察值与它们的聚类中心的平方距离的平均值。
失真度量的 scree 图报告了 k = 6 个聚类处的肘形,这与我们从主成分分析中得出的结论一致。
作者图片
轮廓得分返回最*聚类距离与聚类内距离的平均比率。分数越高,表示聚类质量越好。
然而,局部最大值在位于 k = 3 的第一个肘点处捕获了 Yellowbrick 的轮廓度量。然而,同一图表显示了第二个肘部,即 k = 6 个聚类处的全局最大值,这将使剪影结果与失真度量对齐。
作者图片
Calinski-Harabasz 得分(也称为方差比标准)被定义为聚类之间和聚类内部的离差比,其中离差代表距离平方的和。高系数表示密集且分离良好的聚类结果。
在我们的例子中,CH 分数的曲线在 k = 6 处显示了小的凸起,但是它太弱而不能被识别为肘,并且它还在错误的方向上凸起,这将使它变成“膝”。
主成分、离差和最大轮廓分数强调了第四个结果,即 CH 分数,应该被解释为异常值。总之,我们可以得出结论,我们不应该依赖一个单一的指标,而是应该评估我们到目前为止讨论的所有四种方法。在这个例子中,我们已经收集了足够的证据来得出结论,集群的适当数量是 6。
或者,我们也可以从 sklearn 中推断出集群的数量,而不需要像 Yellowbrick 那样导入额外的库。
下面的 list comprehension,在第 4 行到第 9 行,运行 sklearn 的 k-Means 算法来选择聚类数,并记录它们的结果惯性分数。
- 第 9 行定义了聚类数的范围,在 2 和 nK = 12 之间,我们将进行评估。
- 第 4 到 7 行包含 KMeans 方法及其参数。list comprehension 将为 k 提供一个接一个的可选值,从第 9 行的范围到顶部的 KMeans 方法。
- 然后,第 8 行将接收 KMeans 模型,将其拟合到数据帧 dfa3,并将相应 k 的结果惯性分数传递给结果列表,惯性分数。
- 第 12 行的字典 dict_inertia 将分数与其对应的聚类数 k 相结合。
- 第 14 行的列表理解将逐行打印分数。
- 在第 17 行和第 22 行之间,我们创建了一个 scree 图表,该图表将绘制惯性得分以揭示拐点。
作者图片
惯性得分的曲线非常平滑。当我们眯着眼睛看时,我们可以在 k = 6 个集群处辨别出一个肘点。
不是很有特色的肘关节。我们应该通过计算一个精确的度量来验证这个微弱的视觉线索,这个度量有望为我们提供更多的确定性。“‘肘’不能总是被明确地识别,使得这种方法非常主观和不可靠。”(维基百科)
此外,如果我们仅仅依赖于我们对图表的解释,那么这个任务就变成了一种实际的监督问题。每当有更新时,Python 脚本将不得不暂停并等待用户根据对图表的观察做出决定,尽管聚类被认为是一种无监督的数据科学方法。相比之下,量化的指标将使 Python 脚本能够不受阻碍地继续运行,而无需等待用户输入。
任何曲线的拐点都代表一个微分问题。或者,因为我们在处理离散值,一个差分问题。的论文“在干草堆中寻找‘Kneedle’……”(Satop 等人)概述了 Kneedle 算法, kneed 包已经用 Python 实现了该算法。kneed 包使我们能够自动搜索肘点( kneed )。
要 pip-install 或 conda-install kneed ,使用以下命令:
Kneed 是一个轻量级的包,不会与现有的 Python 环境发生冲突。
- 如果我们将其曲线参数设置为“凹面”,则膝盖定位器会在给定曲线中找到一个“膝盖”——由一系列 x(水平轴)和一系列平行的 y(垂直轴)组成
- 而“凸”曲线将识别相反的形状,即我们想要定位的肘关节,下降曲线从最初的陡峭下降过渡到较平坦的末端。
作者图片
膝盖定位器证实了我们之前得出的结论。惯性分数表明集群的适当数量是 6。
4.4 聚类数:搜索最大剪影
轮廓分数测量观察值如何简洁地映射到单独的聚类,以及它们与相邻聚类的分离程度。它表达了“一个物体与自己的集群相比(相干性)与其他集群(分离性)是多么的相似。”(维基百科)
对于单个观察,分数计算如下
- (b — a) / max(a,b),
在哪里
- 变量 a 测量内聚力:来自同一聚类中所有其他点的观察值的平均距离= 平均聚类内距离;其值越小,观察值与其自身聚类的差异越小或匹配越好;
- 而 b 测量间隔或相异度:一个观测值与该观测值不属于的最*聚类中所有点的平均距离= 平均最*聚类距离
- Sklearn 的函数 silhouette_score 计算所有观察值的平均值。它测量所有观察值围绕各自聚类中心的紧密程度。
- 通过 silhouette_samples 方法可以获得一个填充了个人分数的数组。
轮廓分数的范围在-1 和+1 之间。
- 高分是优选的,它们表明密集且分离良好的聚类。
- 低值或负值表示簇的数量太低或太高,无法生成良好的映射。
- 如果值接*-1,则表示观察值被映射到了错误的聚类,这与相邻聚类更相似。
- 接*零的值表示聚类的观测值非常接*相邻聚类的边界;他们忍受着不完美的分离。
我们通过在候选数 k 中搜索最大轮廓分数来找到最佳的聚类数。
下面的代码片段显示了一个简单的例子,说明了如何对随机选择的几个聚类进行 k 均值分析,k = 4。它在第 6 行获得结果轮廓分数。
作者图片
让我们检查一下是否可以通过在剪影得分中找到全局最大值来改进这个初步结果。
在下面的脚本中,第 2 行到第 6 行中的 list comprehension 对备选数字 k 运行 k-Means 方法。
- 第 6 行定义了要尝试的数字范围,在 2 和 kN = 12 之间。
- list comprehension 将 k 的值一个接一个地输入到顶部的表达式中,如第 2 行到第 4 行所示:我们在上面代码片段的第 6 行看到的 silhouette_score 函数。
- 第 5 行将 k-Means 模型与数据帧中的定标观察值进行拟合。
- 列表理解将每次 k-Means 迭代产生的轮廓分数附加到第 2 行的列表 sil_scores 中。
- 第 8 行收集了一个字典中的分数列表及其对应的集群编号 k,第 10 行中的 list comprehension 将逐行打印出来。
- 第 11 行和第 12 行标识了字典中聚类的最佳数量和相关联的最大轮廓分数。
- 第 15 到 21 行绘制了分数曲线,其峰值在 k = 6 个聚类。
作者图片
到目前为止,肘图和侧影分数都证实了六个聚类充分描述了我们的数据。
我们可以再次使用黄砖图表包来确认计算结果。它提供了轮廓图,帮助我们可视化从分数中获得的数字洞察力。
我们正在寻找产生最大轮廓的聚类数。优选地,所有聚类的形状应该延伸超过平均轮廓分数的垂直线;并且簇应该具有相似的宽度,理想的是均匀的宽度。
然而,在这个特殊的例子中,我们不得不再次眯起眼睛来确认六个图中的哪一个在 x 轴的最大位置显示了垂直的红色虚线。所有的图都展示了不远处右侧 0.2 的轮廓分数。列表分数证实最大值位于 k = 6 个聚类。
作者图片
作者图片
作者图片
4.5 聚类数:聚类间距离图
最后,Yellowbrick 的星团间距离图表使我们能够从另一个角度研究星团。
这个图表显示了集群的相对距离和大小。它仅在二维空间中绘制聚类中心。气泡的大小代表了每个聚类中的观察次数。
作者图片
重叠的圆圈并不意味着集群的分离已经失败。重叠仅仅是它们的大小(一个簇中的记录数量)的结果,结合特征空间的扁平到仅仅 2 维。即使聚类在 2D 空间中看起来重叠,它们在它们的原始特征空间中也不一定彼此靠*。“维度诅咒”将它们之间的距离置于一个比 2D 可见的更高维的空间中。
下面的图表显示了介于 4 和 7 之间的备选集群数量的情况。
作者图片
4.6 训练:k 均值聚类
我们已经完成了对源数据的分析,并且从不同的角度确认了最合适的聚类数是 6。
在下一步中,我们在训练和测试数据集之间拆分观察。
第 6 行到第 7 行对训练数据集运行 k-Means 算法。我们将惯性和轮廓分析的结果(六个集群)输入到该方法中。第 8 行将生成的分类标签作为新列分配给定型数据集中的记录。
作者图片
作者图片
4.7 解释:集群分析和仪表板
我们从聚类标签中获得了什么样的洞察力?
我们的脚本在 dataframe 中生成了一个新的列“Cluster”。我们如何将其转化为可操作的信息?营销团队中的计划者或执行团队中的决策者可以使用哪些信息来定义将增强公司竞争优势的战略?我们能否使用集群标签来设计针对特定客户集群的产品和促销活动?
首先,我们应该研究星团的大小。截屏显示所有六个集群具有相似的量值,在 757 和 1,515 之间。没有明显的异常值或奇异的小星团,也没有让其他星团相形见绌的优势星团。分组过程在这方面没有缺陷。
作者图片
为了获得每个集群的配置文件,我们可以使用许多 pandas 对象,它们从不同的角度提供了洞察。
一个交叉表使我们能够比较多达两个其他变量,以及它们在一个集群或另一个集群中的存在或不存在。
例如,下面的交叉表询问客户流失是否与每月费用相关;如果是,聚类是否反映了这种相关性。该表表明,集群 2 已经失去了一些高价值客户,他们可能发现价格/价值定位对他们来说太贵了。没有续签合同的客户(流失=是)的月费是那些留在公司的客户(流失=否)的两倍多。
作者图片
下一个交叉表对比了这些集群对互联网和流媒体服务的选择。第 1 类客户主要是那些没有通过电视选项预订互联网服务的客户。
作者图片
尽管如此,交叉表还是有点麻烦,因为我们必须为我们想要研究的每一对特征设置它们。
让我们寻找额外的替代工具。
下面的 groupby 方法可以逐个分类地确定每列中最频繁出现的值。
例如,集群 0 的特点是大部分女性客户拥有长期的使用权,她们预订了光纤互联网服务和电视,并签署了最长的合同期限,即两年。我们可以使用这种表来标识每一列中的模式,从而为每个分类开发一个配置文件:“该分类代表哪种类型的客户?”在我们向营销团队提供的技术含量较低的说明或用户指南中,我们可以选择将集群 0 标记为“高价值客户:稳定的客户”
第二类客户主要是男性客户,他们倾向于签订月租合同,只选择电话服务,可以被称为“轻浮先生”或“试探先生”。营销团队需要考虑产品报价,以说服集群 2 的客户签订更长期、更稳定的合同。
作者图片
不过,上面的 groupby 视图并不完美,因为它显示了每一列中的模式:最频繁出现的值也显示在数字列中,这与分类变量相比没有什么意义。
对于数值变量,我们应该显示平均值或中位数,而不是众数。我们还希望创建图表,使营销策略师和决策者更容易识别每个聚类的概况。
下面的截图将显示我们下一步可以对集群数据做什么。
配置文件表在其列中列出了六个集群,并在其行中显示了它们的特征。对于分类变量:模式;对于数值(每月费用和任期):中间值。
下面的饼图有助于理解每个星团的组成。每行饼图代表单个集群的七个核心属性:从左边的合同条款到右边的电视流媒体选项。像这样排列,馅饼为每个集群提供了一个视觉轮廓。
作者图片
作者图片
两个辅助函数生成了上面的表格和饼图。
- 函数 c luster_profile,通过其聚合函数 agg 和特定于列的 lambda 函数,识别中值或众数( value_counts()。index[0]) 用于属于给定聚类的记录。
助手函数 cluster_pies 创建饼图。
一个循环贯穿所有六个集群。在每一列中, value_counts() 函数确定有多少元素已经或尚未映射到相应的分类,并通过饼图回答以下问题:给定分类的该特征(该列)的组成是什么?
5。均值漂移聚类
与 k-Means 不同, Mean Shift 算法不需要我们预先估计聚类数。相反,它采用一个带宽参数来限定搜索空间。带宽决定了它将评估距离的感兴趣区域。带宽可以由用户设置;或者可以通过算法来估计。
均值漂移的最大缺点是计算量大。当它进行多次最*邻搜索时,它的时间复杂度是数据点数的平方:O(n)。因此,均值漂移不容易扩展到大型数据集。
像 k-Means 一样,它不能处理分类数据。因此,我们将把用于 k-Means 的相同的转换后的全数字数据帧 dfa3 传递给它。
我们在单个笔记本单元中结合了我们已经讨论过的 k-Means 的步骤。它的所有行都相当于 k-Means 脚本。
- 在第 5 行,我们建议算法自己估计带宽。如果分位数设置为 0.5,则所有成对距离的中值用于估计。默认值为 0.3。
- 在第 7 行,我们运行算法。参数 bin_seeding 如果设置为 True,将加快算法的速度。如果为 false 或省略,均值漂移搜索将运行更长时间。
- 第 11 行将集群标签作为一个新列映射到数据帧。
- 第 14 行计算轮廓分数。
- 第 20 行调用助手函数,该函数将返回集群概要表和饼状图。
均值漂移与 k-Means 一致,并发现六个聚类充分描述了我们的源数据的结构。
作者图片
作者图片
6.结论
- 我们已经讨论了如何将 k-Means 和 Mean Shift 聚类算法应用于混合(分类和数值)数据类型。
- 我们使用了一系列技术来确定最重要的超参数:最佳聚类数。
- 我们还编写了为计划者和决策者准备仪表板的代码:特定于集群的饼图和显示每个集群特征值的表格。
下一篇文章将演示当我们需要处理分类数据时可以应用的另外两种聚类方法:
- 分类变量的一键编码
和
- k 原型
Jupyter 笔记本可在 GitHub 上下载: h3ik0th/clustering:用 kmeans、meanshift、kprototypes、one-hot encoding、inertia 和 silhouette scores(github.com)对混合变量进行聚类
数据集:Apache-2.0 许可证。【IBM/telco-customer-churn-on-ICP 4d:在 IBM Cloud Pak for Data(github.com)上使用 WML 进行数据分析、模型构建和部署
用 Plotly 进行的选举调查
堆叠棒线还是发散棒线?
2021 年,阿根廷将举行全国立法选举。PASO(Primarias Abiertas simultáneas y Obligatorias,同步强制性公开初选)将于 9 月 12 日举行,而大选将于 11 月 14 日举行。众议院将更新其 257 个席位中的 127 个,参议院将更新其 54 个席位中的 24 个。这被认为是一次超越性的选举,因为执政党距离在众议院获得自己的多数席位只有 7 名代表。由于它已经在参议院占多数,有利的结果将使它获得完全的立法控制权。
竞选活动尚未开始,因为参加不同选举联盟竞选的所有候选人尚未确定。尽管如此,每周还是会发布几项民意调查,试图确立选民的未来行为。其中一些调查关注仅有的两个有机会赢得选举的选举力量的主要政治对象的形象,作为预测选民在投票时行为的代理人。
一位主要的民意调查者询问了现任总统、前两任总统、在任的两位主要州长以及反对党主要政党的主席的形象。当然,显示结果的最佳方式是通过某种数据可视化技术。
在下文中,我指出了 Plotly 允许显示选举调查结果的两种选择。
备选项 1: 100%堆叠水平条
人们认为调查应该是对称的,因为正面或正面的回答与负面或负面的回答数量相等。当然,中立的回应也不能缺席(以免在立场严格中立的情况下,强迫被回应者选择任何一方)。也不应该没有“不知道-不回答”选项,以包括被调查者对所研究项目的无知程度。
显示调查结果最常用的图表之一是 1 00%堆积水平条形图。一个堆积条形图显示了一组条形或矩形,每个条形或矩形代表一个被划分为子类别的主类别。每个子类别的数值由一个接一个堆叠的矩形线段的长度表示。在调查结果的特殊情况下,矩形水平定向。此外,在 100%堆积条形图的情况下,每个区段代表每个子类别的百分比,注意百分比的总和必须等于 100。
以下 100%堆积条形图显示了阿根廷主要政治家的形象水平。该图根据正面图像按降序排序。堆叠的条的顺序如下:正图像;中性形象;负面形象;政客的无知。
图 1:作者用 Plotly 制作的 100%堆积水平条形图。
获得图 1 的代码如下:
首先,我们将 Plotly Express 导入为 px,Plotly . graph _ objects 模块导入为 go ,Pandas 和 Colorlover 库分别导入为 pd 和 cl :
import plotly.express as pximport plotly.graph_objects as goimport pandas as pdimport colorlover as cl
然后,我们创建了包含调查结果的数据框架,并根据正面形象的级别对其进行了分类:
surv = {‘Pol_List’: [‘PB’, ‘HRL’, ‘AF’, ‘CFK’, ‘MM’, ‘AK’], ‘Posit’: [43.9, 39.8, 26.8, 26.7, 24.8, 21.7], ‘Neutr’: [14.3, 25.5, 10.6, 6.0, 26.4, 9.1], ‘No_Op’: [4.8, 4.2, 0.4, 0.5, 0.7, 2.1], ‘Negat’: [37.0, 30.5, 62.2, 66.8, 48.1, 67.1] }df_surv = pd.DataFrame(surv)df_surv = df_surv.sort_values(by=’Posit’, ascending = True)images = [‘Negat’, ‘No_Op’, ‘Neutr’, ‘Posit’]
现在,我们准备创建我们的图表。
plotly 的通常程序是使用方法.add_trace(go.Bar())
生成图形,然后使用方法.update_layout()
和.update_xaxes
对其进行操作。最后用.show()
显示图表,用 .write_image()
导出。
我们需要将.add_trace(go.Bar())
方法放在中,以便循环遍历数据帧,提取政治家的首字母(y = df_surv[‘Pol_List’]
)和调查中记录的量化值(x = df_surv[column]
。用 orientation = ‘h’
我们表示我们的条将水平定向,用 text
和 textposition
我们将在矩形段内包含数值。cl.scales
选择一个色标,以便快速辨别正值和负值。
fig.update.layout
允许我们设置标题文本和字体,以及图例的方向和位置。包含barmode = ‘stack’
非常重要,以确保杆段一个接一个地堆叠。
最后,我们指出,在 x 轴上,我们不想放置标尺或网格,因为它们对我们讲故事来说是不必要的。
fig = go.Figure()for column in df_surv.columns[1:5]: fig.add_trace(go.Bar( x = df_surv[column], y = df_surv[‘Pol_List’], name = column, orientation = ‘h’, text = df_surv[column], textposition = ‘auto’, marker_color=cl.scales[
str(len(images))]’div’]’RdBu’]
images.index(column)] ))fig.update_layout( title = ‘Image of Argentinian Politicians’, title_font= dict(family = ‘Arial’, size = 30), barmode = ‘stack’, legend_orientation =’h’, legend_traceorder=”normal”, legend_x = 0.10, legend_y = 1.1, )fig.update_xaxes( visible = False, showgrid = False)fig.write_image(path + ‘Survey1.png’)fig.show()
备选方案 2:发散堆积条形图
发散堆积条形图是之前描述的堆积条形图的延伸,增加了一条垂直基线。概念上的想法是否定回答放在基线的左边,而肯定回答放在基线的右边。这种特殊的设计便于对各种类别的积极或有利的反应和不利或消极的反应进行比较。
每个条形被分成水平矩形段,一个接一个地堆叠。正如所有条形图的特点一样,每段的长度与要表示的数值成正比。根据要绘制的数据集的特征,该数值可以是绝对值或百分比。
为了将否定回答放在基线的左侧,我们继续将相应的列转换为负值,并根据该列进行排序:
df_surv['Negat'] = df_surv['Negat'] * -1df_surv = df_surv.sort_values(by=['Negat'], ascending = False)
我们继续用相同的方法.add_trace(go.Bar())
绘制图表,使用的代码等同于之前用 100%堆积水平条表示的代码。
fig2 = go.Figure()for column in df_surv.columns[1:5]: fig2.add_trace(go.Bar( x = df_surv[column], y = df_surv['Pol_List'], name = column, orientation = 'h', text = df_surv[column], textposition = 'auto',
marker_color=cl.scales[
str(len(images))]’div’]’RdBu’]
images.index(column)] ))
我们必须在 update.layout 方法中做两处修改:现在我们指定barmode = ‘relative’
和legend_traceorder=”reversed”.
fig2.update_layout( title = 'Image of Argentinian Politicians', title_font= dict(family = 'Arial', size = 30), barmode = 'relative', legend_orientation ='h', legend_traceorder="rerversed", legend_x = 0.10, legend_y = 1.1, )
然后,我们画一条中心垂直线,表示发散的堆叠棒线:
# Draw a central vertical linefig2.add_shape(type='line', x0 = 0.5, y0 = -0.5, x1 = 0.5, y1 = 5.5, line=dict(color = 'yellow', width = 3))
在 x 轴上,我们保留默认选项来显示数字值和网格,这增强了故事的讲述:
fig2.update_xaxes( visible = True, showgrid = True)fig2.write_image( path + 'Divergent1.png')fig2.show()
图 2:作者用 Plotly 做的发散堆积条形图。
发散堆积条形图中的一个常见替代方法是将中性响应放置在正响应和负响应之间,并在代表中性响应的线段中心画一条垂直线。图 3 示出了用具有这些特征的 Matplotlib 制作的图:SD:强烈不同意;d:不同意;n:中性;答:同意;山:非常同意。很明显,这种布局很难对阳性和阴性反应进行直接比较,因为它们不是从同一基线开始的。
图 3:作者用 Matplotlib 制作的发散堆积条形图。
你可以选择讲述你的故事。
如果你对这篇文章感兴趣,请阅读我以前的(https://medium.com/@dar.wtz):
棒棒糖和哑铃图,有曲线、平均值或中值?
斜率图表,为什么和如何,用斜率讲故事
伊莱克特拉是伯特——超级增压
ELECTRA 的性能优于所有其他变压器,体积仅为其 1/4
转换器主宰了自然语言处理(NLP)领域。2017 年的论文“注意力是你所需要的一切”介绍了第一个变压器——此后没有其他架构取代了这些 NLP 发电站。
快进一年到 2018 年,我们发现 BERT (变压器的双向编码器表示)。一个巨大的预先训练好的模型,无需进一步训练就可以应用于广泛的语言任务。
BERT 的方法已经成为 NLP 的标准——在难以想象的大数据集上训练一个大规模的基于 transformer 的架构,在几个标准化的基准上测试它——并向公众发布。
此后推出了几个模型,它们在 BERT 的成功基础上有所改进,如 RoBERTa 和 XLNet,但它们都依赖于更大的网络和更大的数据集。
除了 2020 ELECTRA 模型之外,所有模型都引入了一种新的训练方法,可以产生与最好的变压器性能相当(通常更好)的模型,并且只需要一小部分计算能力。
> How to Train a Transformer
> A Different Pre-Training Approach
> Performance Compared to Other Models
如何训练一名变压器
伯特在预训练中使用了掩蔽语言模型(MLM)。MLM 包括获取输入序列并用掩码标记[MASK]
替换其中一些标记。
这意味着:
["the", "chef", "cooked", "the", "meal"]
变成了:
[**"[MASK]"**, "chef", **"[MASK]"**, "the", "meal"]
在屏蔽了这些输入之后,模型被训练来用正确的单词替换这些[MASK]
记号。
MLM 的方法非常有效,并产生了真正惊人的结果。成千上万的 ML 工程师和数据科学家正在将 BERT 作为解决方案的关键组件应用于许多行业。对于每一个 BERT 用例,我们看到另一个用于 XLNet,另一个用于 GPT-2/3。
这些模式取得了巨大的成功。但是它们都依赖于大量的训练数据。训练这些模型的计算成本是另一个影响巨大的因素——GPT-3 的训练成本估计为 460 万美元[1]。
训练一个模特的 460 万美元是一笔巨款——而这只是一次训练。没有一个研究团队会在第一次尝试中成功地设计和训练一个模型。这些事情需要时间和大量的培训和再培训。
展望未来,这些大型变压器模型的趋势是添加更多的训练数据和更多的参数,从而推动性能越来越高。这自然会导致更长的训练时间和更多的计算。
不同的预培训方法
伊莱克特拉打破了这一趋势。与依赖于 MLM 预训练的前辈不同,ELECTRA 使用了一种更有效的方法,称为“替换令牌检测”。
这种方法不是屏蔽随机选择的输入令牌,而是使用另一种神经网络,试图通过用假令牌替换随机令牌来欺骗我们的模型。
生成对抗网络(GAN)的数据流和生成器-鉴别器结构
该方法与 GANs 采用的方法相似。在训练 GANs 时,我们让两个网络(生成器和鉴别器)相互竞争。生成器经过优化,可以用越来越有说服力(但仍然是假的)的数据“欺骗”鉴别器。
然后,我们的鉴别器可以识别由生成器模型生成的数据哪些是真的,哪些是假的。
ELECTRA 采用了类似的方法。我们的模型是鉴别器,它的任务是识别哪些令牌是真的,哪些是生成器放在那里的赝品。
ELECTRA 论文中使用的类似但不同的发生器-鉴别器架构方法,来源
这不是 GAN 型架构的原因是,发生器没有优化以增加鉴频器的损耗,而是像典型的 MLM 模型一样训练,它必须最好地猜测[MASK]
值。
根据 ELECTRA 的论文,这种预训练方法比 MLM 更有效,因为模型必须考虑它看到的每个样本中的每个标记——而 MLM 只要求模型专注于[MASK]
标记[2]。
因为该模型关注并训练每一个标记,所以该模型产生了对上下文的改进的理解。
边注:使用较小的生成器模型可以改善结果——特别是使用 25–50%的参数作为鉴别器的生成器[2]。
预训练完成后,发电机模型被丢弃,留给我们新的 ELECTRA 变压器模型。然后,我们可以将 ELECTRA 应用于由 transformers 处理的典型语言任务,例如文本生成或问答。
与其他型号相比的性能
总的来说,ELECTRA 看起来是性能最高的变压器型号之一,同时比与其性能相当的所有其他型号都小得多。
在 2020 年的论文中,该模型在 GLUE 和 SQuAD 基准上进行了测试。
GLUE 是一项分类任务,其中语言模型必须准确预测给定句子的正确单词。该基准旨在通过测量不同语言现象的九项任务来深入测量模型在自然语言理解(NLU)方面的能力[3]。
ELECTRA 论文作者用于 GLUE 基准测试的模型架构的高级视图。
对于 GLUE 基准测试,在 ELECTRA 的基础上使用额外的线性分类器对模型进行微调。
SQuAD 是斯坦福大学创建的问答基准,有两个版本,SQuAD 和 SQuAD 2。在这里,伊莱克特拉是以两者为基准的——她的任务是选择回答一个给定问题的文章的跨度。
ELECTRA 论文作者用于团队基准测试的模型架构的高级视图。
对于小队基准,来自 XLNet 的问答模块被添加到 ELECTRA 中。
胶
GLUE 基准测试结果,RoBERTa 和 ELECTRA 分别参考了 RoBERTa-500K 和 ELECTRA-1.75m[2]。
在 GLUE 测试集上,伊莱克特的表现优于所有其他 transformer 模型(当取几个指标的平均分时)——尽管比所有其他模型(除了 Bert)需要更少的计算来训练。
班
小队基准测试结果,RoBERTa 和 ELECTRA 分别参考了 RoBERTa-500K 和 ELECTRA-1.75m[2]。
将伊莱克特与 SQuAD 基准测试中的其他模型进行比较表明,伊莱克特在性能和训练计算要求方面都是明显的赢家,只有 Bert 在计算方面和 XLNet 在 SQuAD 1.1 开发集上的性能例外。
为了解决这个问题——实际上,伯特仍然非常重要——但伊莱克特展示了用一点点创新思维可以做些什么,并且看到未来出现其他迷人的模型将是令人难以置信的。
如果你还没有——我绝对推荐你在你正在做的任何基于 transformer 的 NLP 任务中尝试伊莱克特。
如果这一切对你来说都是新的,但你想尝试一下伊莱克特,看看 HuggingFace 的变形金刚库和他们的关于伊莱克特本身的文档。
我希望你喜欢这篇文章!如果你有任何建议或问题,请随时通过推特或在下面的评论中联系我们!如果你有兴趣看到更多这样的内容,我也会在 YouTube 上发布。
感谢阅读!
参考
[1] L. Chuan, OpenAI 的 GPT-3 语言模型:技术概述 (2020),Lambda Labs
[2]K·克拉克等, ELECTRA:将 Tect 编码器作为鉴别器而不是发生器进行预培训 (2020),ICLR
[3] A. Wang 等, GLUE:自然语言理解多任务基准与分析平台 (2018),EMNLP
有兴趣进一步了解变形金刚吗?我写过很多关于它们的文章——但这很容易成为我最喜欢的开场白:
*
**除另有说明外,所有图片均为作者所有
优雅高效地使用 If-Else 子句
编程;编排
用优雅的 if-else 语句润色你的代码
作者图片
拿一张纸。去掉衔接手段(然而,因此……)。并阅读它。这还有意义吗?不要!同样的事情也适用于没有if-else
语句的代码。
条件分支是一个基本的和强制性的编程概念。它标志着推理的转变。明智地利用条件可以得到简短、可维护和可读的代码。
也就是说,自从我开始写代码,我就把尽可能多的时间花在阅读高级和入门级程序员的代码上。在这里,我列出了我用来揭开高级代码神秘面纱的轨迹。
令人惊讶的是,我读得越多,就越发现经验丰富的程序员和初学者之间的差距有多大。高年级学生写的代码让你大吃一惊。大三写的代码让你想揪头发。
结论:不是语法的问题,是如何使用语法的问题。
在这篇文章中,我将尽我所知解释伟大的程序员如何利用if-else
子句的力量。
请记住:
不是剑的问题,是剑客的问题。
控制流工具:如果
教授:“大家好,今天我们要来看看一个重要的流程控制工具:If-else 语句。所以,请注意这个流程图。”
我:“哦,又来了,流程图。该睡觉了。Zzzz" 。
我必须承认。我不是一个专心的学生。每当教授们的演讲超过 15 分钟,我就开始打哈欠。通过流程图的解释只会加速这个过程。那是一个让我付出巨大代价的错误。
流程图是表示工作流程的最简单的方式。我确信,一旦你理解了if-else
流程图,你的大部分条件分支问题就会迎刃而解。
它看起来是这样的:
作者在diagrams.net创建的 if 语句流程图
从图中可以看出if-else
是如何解读的相当容易。这意味着一旦一个if
表达式被评估为真,它的主体被执行,其余的子句被忽略。
完全理解流程图是至关重要的,因为遗漏一个细节可能会让你失去高效的if
语句。不要担心,我们将在这篇文章的后面详细讨论这个问题。
两组程序员
我已经编码 5 年多了。在这段时间里,我意识到有两种类型的程序员。
- 那些使用冗长松散的
if-else
语句的人。 - 那些使用简洁干净的
if-else
语句。
具有讽刺意味的是,虽然【如何】与不同,但**【为什么】却是一样的。为了证明这一点,我们将使用上述两种观点来解决下面的问题。
问题:
你有一堆信用卡。你想把它们归类为“可能是万事达卡”、“可能是维萨卡”或无效卡。Visa 卡的第一个数字必须是 4,总共有 16 或 13 个数字。万事达卡必须有 51,52 …直到 55 作为前两位,总共 16 位。
第一组样本代码:
ccn = 13
fd = 4
**if** ccn == 16 **and** (fd == 51 **or** fd == 52 **or** fd == 53 **or** fd == 54 **or** fd == 55):
print("Maybe MASTERCARD")
**elif** (ccn == 13 **or** ccn == 16) **and** fd == 4:
print("Maybe VISA")
**else**:
print("INVALID")
输出:
>>> Maybe VISA
第二组样本代码:
ccn = 13
fd = 54
**if** ccn == 16 **and** 51 <= fd <= 55:
print("Maybe MASTERCARD")
**elif** ccn **in** [13, 16] **and** fd == 4:
print("Maybe VISA")
**else**:
print("INVALID")
输出:
>>> Maybe VISA
您可以看到,虽然两个代码执行相同的任务,但每个代码的编写方式都不同。第一个是冗长和不优雅的。二是简洁,有感染力。这是你想写的那种if-else
式的陈述。但是怎么做呢?
- 学*数理逻辑。斯坦福大学的这个班级是一个很好的参考
- 每当有多个值要检查时,就使用数组
- 阅读有经验的程序员代码
我很确定如果你遵守这三条规则。你会写出更好的if-else
陈述。
三元运算符
三元算子字面意思是由三部分组成的算子。这些部分是:
- 中间部分:一个条件
- 左半部分:条件为真时要执行的表达式
- 右半部分:否则要执行的表达式
这转化为下面的流程图:
作者在diagrams.net创建的三元运算符流程图
就像标准的if-else
语句一样,您可以使用三元运算符来编写平面和嵌套表达式。
平
<expression1> **if** <condition> **else** <expression2>
嵌套
<expression1> **if** <condition1> **else** <expression2> **if** <condition2> **else** <expression3> .... **if** <condition(N-1)> **else** <expressionN>
看到一些程序员滥用这个操作符只是因为它比显式的if-else
语句短,这让我很难过。
使用三元运算符的主要原因是为赋值提供一个简洁的媒介。因此,当涉及多个分支时,最好使用标准的控制流工具,而不是使用运算符来编写嵌套表达式。
引用 Python PEP 308 条件表达式提案的作者:
“Python 的一个设计原则是,每当对该走哪条路有疑问时,就倾向于保持现状。”
高效的 if-else 语句
你有没有想过串行if
块和if-else
块有什么区别?例如:
**if** <expression1>:
statement1
**if** <expression2>:
statement2
.
.
.
**if** <expressionN>:
statementN
并且:
**if** <expression1>:
statement1
**elif** <expression2>:
statement2
.
.
.
**elif** <expressionN>:
statementN
第一次向我介绍条件表达式时,我并没有完全理解if-else
流程图。结果,我总是使用连续的if
语句,因为它们总是产生预期的结果。我错了!
在串行if
块中,所有条件都被测试,而在if-else
块中,一旦if
表达式评估为真,剩余的测试被一起跳过。这种方法被称为断路器行为,比串行if
块更有效。
也就是说,当你处理一个只需要满足一个条件的案例时,使用if-else if-else
语句。
逃离嵌套 if-else 语句的噩梦
也许我们已经到了这篇文章最重要的部分。嵌套的if-else
语句。
条件子句的过度使用有时会导致深度嵌套的if-else
语句,这是编程社区所不赞同的,并被认为是一个糟糕的设计。
幸运的是,有一种方法可以战胜嵌套的if-else
语句。那是的决策表。
了解如何充分利用决策表。我们将编写以下代码,比较 200 的数字:
(此示例是来自教程要点的代码的修改版本)
输出:
Value is less than 200
Between 100 and 150
It is 125
哦,天哪,我们有一堆嵌套在 3 层上的if-else
语句。这是一段糟糕的代码,因为它冗长,难以阅读和维护。
让我们试着弄平这些嵌套的if-else
语句。
但是在我们这样做之前,为了最大化学*成果,我们要分析并记下上面代码的注释。
- 第一级
if
将数字分类为大于或小于 200 - 第二级
if
将数字分为小于 100、小于 150、大于或等于 150 - 第三级
if
打印与条件中的数字相等的数字
现在,我们将把 remarque n 2 和 n 3 注入到单独的函数中。print_interval(val)
和print_value(val)
。然后我们将两个嵌套的if-else
块存储在两个字典(决策表)中。第一个按照备注 2 进行分类的人。第二个按照备注 3 打印号码。
一切就绪后,我们得到以下代码:
输出:
Value is less than 200
Between 100 and 150
It is 125
正如你所看到的,我们通过使用函数和决策表成功去除了嵌套的if-else
语句。这使得我们能够编写设计更好的代码。可读性和可维护性。
最终,你可以看到我们混合使用了条件语句、函数、字典而不是滥用if-else
。然而,如何操作这些取决于你和你自己的判断。
外卖食品
为了帮助您将条件分支技能提高到一个新的水平,我们讨论了if-else
语句的陷阱以及如何绕过它们。以上信息是 5 年编程的成果。尽管如此,编程是一个无限的领域。这就是为什么他们说:
“编程是一盘棋。简单易学。很难掌握”。
优雅的 CICD,配有 Databricks 笔记本
如何用 Azure DevOps 发布 Databricks 笔记本神器
卢克·范·德·维尔登
从数据科学探索到生产中的 ETL 和 ML,笔记本是数据块的主要运行时。这种对笔记本的强调要求我们改变对生产质量标准的理解。我们必须消除对凌乱的笔记本的犹豫,并问自己:我们如何将笔记本转移到我们的生产流水线中?我们如何在笔记本上执行单元测试和集成测试?我们可以将笔记本视为 DevOps 管道中的工件吗?
作为一等公民的数据砖笔记本
选择 Databricks 作为计算平台时,您的最佳选择是在您的生产环境中运行笔记本电脑。这个决定是由对笔记本运行时和经典 python 脚本的压倒性支持决定的。我们认为,人们应该完全接受笔记本电脑方法,并选择最佳方法在生产环境中测试和部署笔记本电脑。在这篇博客中,我们使用 Azure DevOps 管道通过瞬态数据块集群和笔记本工件注册进行笔记本(单元、集成)测试。
笔记本:python 包的入口点
笔记本可以独立存在,但是我们更喜欢将它们作为 Git 存储库的一部分,它具有以下结构。它包含一个笔记本目录,用于将数据块笔记本作为源文件签入;一个 Python 包(“my_model”),包含要在笔记本中导入的功能;一个测试目录,包含 Python 包的单元测试;一个 Azure DevOps 管道和一个 cluster-config.json,用于配置我们的瞬态数据块集群。此外,我们使用诗歌进行基于 pyproject.toml 规范的 Python 依赖管理和打包。
notebooks/
- run_model.py # Databricks notebook checked in as .py file
my_model/
- preprocessing.py # Python module imported in notebook
tests/
azure-pipelines.yml
cluster-config.yml
pyproject.toml
...
通过将 Git 存储库链接到 Databricks 工作区中的笔记本,或者通过手动将笔记本导出为源文件,可以将笔记本提交到 Git 存储库中。在这两种情况下,存储库中的笔记本都是带有数据块标记命令的 Python 文件。我们存储库的笔记本入口点如下所示。请注意,它从包含的存储库中安装并导入 Python 包‘my _ model’build。包版本将在后面详细说明。任何笔记本逻辑都在 main 函数中被捕获。执行主函数 dbutils.notebook.exit()后,调用该函数,表示成功完成,并允许将结果值返回给调用者。
# Databricks notebook source
dbutils.widgets.text("package_version", defaultValue='')
package_version = dbutils.widgets.get("package_version")
# COMMAND ----------
devops_pat = dbutils.secrets.get("devops_scope", "devops-artifact-read")
%pip install my_model==$package_version --index=[https://build:$devops_pat@pkgs.dev.azure.com/organization/project/_packaging/feed/pypi/simple/](https://build:$devops_pat@pkgs.dev.azure.com/organization/project/_packaging/feed/pypi/simple/)
# COMMAND ----------
from my_model.preprocessing import do_nothing
# COMMAND ----------
# define the main model function
def main(spark):
do_nothing(spark)
# COMMAND ----------
# run the model
from loguru import logger
with logger.catch(reraise=True):
main(spark)
# COMMAND ----------
dbutils.notebook.exit("OK")
笔记本拉取请求管道
在开发笔记本及其支持的 Python 包时,开发人员提交开发分支,并创建一个 Pull 请求供同事审阅。图 1 显示了支持我们的拉请求的管道步骤。Pull 请求自动触发 Azure DevOps 管道,该管道必须在最*一次提交时成功。首先,我们运行 Python 包的单元测试,成功后构建它并将开发构建包发布到 Azure 工件。这个开发构建包的版本字符串被传递给笔记本输入小部件“package_version ”,以便在我们的登台环境中进行笔记本集成测试。管道验证笔记本是否成功运行(是否调用了 dbutils.notebook.exit ),并提供关于 Pull 请求的反馈。
图 1:用于持续集成的 PR 笔记本管道,图片由作者提供
瞬态集群上的集成测试
目标是在来自 Azure DevOps 管道的数据块上执行这个笔记本。为了灵活性,我们选择数据块池。这些池的优势在于,当许多不同的作业需要在即时集群上运行时,它们可以减少集群的启动和自动扩展时间。对于笔记本的执行(以及对可选数据源的访问),我们使用 Azure 应用程序注册。此 Azure 应用程序注册将拥有管理数据块群集和执行笔记本的权限。流水线的基本步骤包括数据块集群配置和创建、笔记本的执行以及集群的最终删除。我们将详细讨论每个步骤(图 2)。
图 Databricks 笔记本的集成测试管道步骤,作者图片
为了使用 Azure DevOps 管道来测试和部署 Databricks 笔记本,我们使用由 Data heedy Ltd 开发的 Azure DevOps 任务来创建集群,并使用来自微软 DevLabs 的任务来执行笔记本。由于他们的任务集还不支持所有需要的操作,我们也使用他们为数据块开发的 PowerShell 工具。任务和 PowerShell 工具都是围绕 Databricks API 的包装器。
应用程序注册的数据块权限
作为准备,我们创建了一个可用于集成测试的数据块池。我们使用 Azure 应用程序注册作为主体在实例池上执行笔记本。应用程序注册被注册为 Databricks 服务主体,在 Databricks 池上具有“可以附加到”权限以创建群集。
数据块实例池配置,作者图片
准备管道机密
CI/CD 管道的第一步是获取所有需要的机密。为简单起见,我们将应用注册客户端 id、secret、tenant-id 和 Databricks 池 id 存储在一个密钥库中。秘密是使用 AzureKeyVault 任务收集的。
# azure-pipelines.yml excerpt
jobs:
- job: integration_test
displayName: Test on databricks
pool:
vmImage: "windows-latest"
steps:
- task: AzureKeyVault@1
inputs:
azureSubscription: "Azure DevOps Service Connection"
keyVaultName: "keyvault-test-environment"
secretsFilter: "appreg-client-id,appreg-client-secret,tenant-id,databricks-pool-id"
数据块工作空间连接
为了与数据块交互,我们需要从 Azure DevOps 连接到工作区。我们使用 Data heart 中的两个 Azure Devops 任务来生成 Databricks 的访问令牌并连接到工作区。令牌存储在 BearerToken 变量中,并为我们在 Databricks 中授予权限的应用程序注册生成。可以在 Databricks 资源上的 Azure 门户中找到工作区 URL。
# azure-pipelines.yml excerpt
- task: databricksDeployCreateBearer@0
inputs:
applicationId: $(appreg-client-id)
spSecret: $(appreg-client-secret)
resourceGroup: "DatabricksResourceGroup"
workspace: "DatabricksWorkspace"
subscriptionId: "AzureSubscriptionId"
tenantId: $(tenant-id)
region: "westeurope"
- task: configuredatabricks@0
inputs:
url: "[https://adb-000000000000.0.azuredatabricks.net](https://adb-000000000000.0.azuredatabricks.net)"
token: $(BearerToken)
请注意,使用 databricksDeployCreateBearer 任务存在潜在的安全问题,我们已经在实时管道中解决了这个问题。该任务的当前版本创建了没有截止日期的不记名令牌,不幸的是,无法使用该任务设置截止日期。或者,也可以使用 Data hedge 的 Powershell Databricks 工具。通过连续调用 Connect-Databricks 和 New-databrickbearertoken,可以创建具有有限生存期的令牌。
创建瞬态测试集群
在建立到数据块的连接之后,我们在数据块中创建一个专用集群,用于由这个管道执行的集成测试。集群配置只包含一个工作人员,这对于集成测试来说已经足够了。当我们在 ADLS 第二代存储帐户上存储笔记本电脑所需的测试数据时,我们设置了 ADLS 传递,以允许应用程序注册通过存储帐户进行验证。为了达到最佳效果,我们而不是直接在集群配置中插入应用注册客户端密码,因为这将在数据块中可见。相反,我们在集群配置中使用 Databricks Secret 作用域及其模板标记,这在运行时填充。
// cluster-config.json
{
"num_workers": 1,
"cluster_name": "",
"spark_version": "",
"spark_conf": {
"fs.azure.account.auth.type": "OAuth",
"fs.azure.account.oauth.provider.type": "org.apache.hadoop.fs.azurebfs.oauth2.ClientCredsTokenProvider",
"fs.azure.account.oauth2.client.id": "",
"fs.azure.account.oauth2.client.secret": "{{secrets/zpz_scope/appreg-client-secret}}",
"fs.azure.account.oauth2.client.endpoint": "",
"spark.hadoop.fs.permissions.umask-mode": "002"
},
"ssh_public_keys": [],
"custom_tags": {},
"spark_env_vars": {
"PYSPARK_PYTHON": "/databricks/python3/bin/python3",
"NSK_ENV": ""
},
"autotermination_minutes": 10,
"cluster_source": "API",
"init_scripts": [],
"instance_pool_id": ""
}
我们提交集群配置文件的上述模板,并在运行时使用 Linux‘jq’命令从 Azure Key Vault 中填写详细信息,如池 id 和应用注册客户端 id。集群名称基于当前的 devops 构建 ID 和其他参数,cluster-config.json 被呈现并写入磁盘。
# azure-pipelines.yml excerpt
- bash: |
jq -c ".cluster_name = \"${CLUSTER_NAME}\""`
`"| .spark_conf.\"fs.azure.account.oauth2.client.id\" = \"$(ar20zpz001-client-id)\""`
`"| .spark_conf.\"fs.azure.account.oauth2.client.endpoint\" = \"[https://login.microsoftonline.com/$(tenant-id)/oauth2/token\](https://login.microsoftonline.com/$(tenant-id)/oauth2/token\)""`
`"| .spark_version = \"${RUNTIME}\""`
`"| .instance_pool_id = \"${INSTANCE_POOL_ID}\""`
`"| .spark_env_vars.NSK_ENV = \"${NSK_ENV}\"" cluster-config.json > tmp.$$.json
mv tmp.$$.json cluster-config.json
echo "Generated cluster-config.json:"
cat cluster-config.json
displayName: "Generate cluster-config.json"
env:
CLUSTER_NAME: "integration-build-$(Build.BuildId)"
RUNTIME: "7.5.x-scala2.12"
INSTANCE_POOL_ID: $(main-pool-id)
NSK_ENV: "test"
Data heedy 中的 databricksClusterTask 使用呈现的 cluster-config.json 在我们的暂存环境中创建和部署一个集群,其中包含从 Databricks 池中获取的资源。
- task: databricksClusterTask@0
name: createCluster
inputs:
authMethod: "bearer"
bearerToken: $(appreg-access-token)
region: "westeurope"
sourcePath: "cluster-config.json"
执行笔记本
最后,我们可以上传笔记本来测试和执行它。databricksDeployScripts 任务将笔记本上载到 Databricks,该任务使用 Microsoft DevLabs 的 executenotebook 任务执行。笔记本存储在包含 devops 构建 ID 的路径中,以便在以后需要时识别(并删除)它。如果笔记本使用小部件,executionParams 输入用于传递带有输入参数的 JSON 字符串。在我们的例子中,Python 包开发版本字符串作为“package_version”传递,用于受控集成测试。最后,我们等待笔记本的执行完成。如果在笔记本运行期间调用了 Databricks builtin dbutils . notebook . exit(" return value "),则 executenotebook 任务将成功完成。
# azure-pipelines.yml excerpt
- task: databricksDeployScripts@0
inputs:
authMethod: "bearer"
bearerToken: $(appreg-access-token)
region: "westeurope"
localPath: "notebooks/"
databricksPath: "/test/package_name/$(Build.BuildId)"
clean: false
- task: executenotebook@0
inputs:
notebookPath: "/test/package_name/$(Build.BuildId)/$(notebook_name)"
executionParams: '{"package_version":"0.0.0-dev"}'
existingClusterId: $(createCluster.DatabricksClusterId)
- task: waitexecution@0
name: waitForNotebook
删除集群
最后,我们删除集群。不幸的是,不存在来自数据渴求的 Azure DevOps 任务来删除集群,所以我们安装了他们的 Powershell Databricks 工具,并使用 Remove-DatabricksCluster 命令来删除集群。
# azure-pipelines.yml excerpt
- task: PowerShell@2
condition: always()
inputs:
targetType: "inline"
script: |
Install-Module -Name azure.databricks.cicd.tools -force -Scope CurrentUser
- task: PowerShell@2
condition: always()
inputs:
targetType: "inline"
script: |
Remove-DatabricksCluster -BearerToken $(BearerToken) -Region 'westeurope' -ClusterId $(createCluster.DatabricksClusterId)
displayName: "Delete Databricks integration cluster"
笔记本神器发布
测试成功的笔记本准备与主分支合并。合并后,我们希望将笔记本电脑投入生产。我们使用 Azure devops 工件将项目笔记本目录注册为 Azure devops 工件提要中的通用包。我们用发布版本标记主分支,这触发了包括工件注册的管道运行。在注册 notebook 工件之前,发布版本在 notebook input 小部件中用 sed 设置为默认的“package_version ”,见下文(下面的示例为 1.0.0 版)。注意,附带的 Python 包也注册为具有相同名称和版本的工件,但是在不同的工件 devops 工件提要中。这确保了在默认情况下,笔记本将使用测试时使用的 Python 包版本运行。因此,我们的笔记本产品是可复制的,并允许一个受控的发布过程。
# Databricks notebook source
dbutils.widgets.text("package_version", defaultValue='1.0.0')
package_version = dbutils.widgets.get("package_version")
如何生成发布版本由您决定。最初,您可以向主分支添加一个 git 标签来触发一个包含工件注册的构建,如下所示。对于完整 CICD,您可以在合并拉取请求时即时生成一个版本。
# azure-pipelines.yml excerpt
variables:
packageName: 'my_model'
${{ if startsWith(variables['Build.SourceBranch'], 'refs/tags/') }}:
packageVersion: $(Build.SourceBranchName)
${{ if not(startsWith(variables['Build.SourceBranch'], 'refs/tags/')) }}:
packageVersion: 0.0.0-dev.$(Build.BuildId)- job: publish_notebook_artifact
pool:
vmImage: "ubuntu-latest"
dependsOn: [integration_test]
condition: and(succeeded(), or(eq(variables['Build.Reason'], 'Manual'), startsWith(variables['Build.SourceBranch'], 'refs/tags/')))
steps:
- bash: |
set -e
if [ -z "$PACKAGEVERSION" ]
then
echo "Require PACKAGEVERSION parameter"
exit 1
fi
sed -i "s/defaultValue=.*/defaultValue='$PACKAGEVERSION')/" \
notebooks/run_model.py
displayName: Update default value for version
- task: UniversalPackages@0
displayName: Publish notebook artifact $(packageVersion)
inputs:
command: publish
publishDirectory: "notebooks/"
vstsFeedPublish: "DNAKAA/databricks-notebooks"
vstsFeedPackagePublish: "my_model"
packagePublishDescription: "notebooks of my_model"
versionOption: custom
versionPublish: "$(packageVersion)"
结论
我们已经展示了如何通过 Python 包单元测试在瞬态数据块集群上运行笔记本集成测试。这导致可再现的笔记本工件,允许笔记本的受控发布过程。Databricks 笔记本电脑是一等公民,要求工程师将笔记本电脑解放到他们的测试和发布流程中。我们期待着了解更多关于将数据科学家的现实与数据工程师的现实相结合,以提高生产力,定期发布。我们的目标是简化从探索、概念验证到生产的过程。在我们的下一篇博客中,我们将深入探讨如何在生产管道中使用笔记本工件,重点是 Azure DataFactory 管道。
最初发布于https://codebeez . nl。
用 Python 语言用 Emmett 进行优雅的 Web 开发
概述如何使用 Emmett Web 框架,并使用它在 Python 中创建 Web 应用程序
(src =https://pixabay.com/images/id-1209008/
Python web 开发是 Python 编程的一部分,多年来一直由 Flask 和 Django 等流行选项主导。那些解决方案当然很棒,我自己也用过很多。也就是说,我认为任何软件都有缺点,因为必须采取某些方向,并且有些软件在某些方面比其他方面更好。在寻找我写的一篇关于机器学*验证的文章时,我偶然发现了埃米特项目。
谁会想到我会有一个以自己名字命名的 Python web-framework 呢?当然,这只是一个笑话,这个框架实际上是以《回到未来》中的埃米特·布朗博士命名的。框架的标志是他戴着标志性护目镜的脸。事实上,我是那部电影的粉丝,但不是第二部(那是什么?)鉴于这个名字,这个包激发了我的好奇心,我决定尝试一下。如果你想更深入地了解这个包,你可以在 Github 上查看:
https://github.com/emmett-framework/emmett
首先,我应该说,我通常对这类事情不抱太大期望,因为有这么多 web 框架的好例子,有更好的文档和更多的用户。然而,在阅读了特性列表并查看了方法之后,我对这个奇怪的包很感兴趣。虽然我不能说谎,但每当我尝试的时候,对自己的事情感到奇怪和好奇。但是我很高兴展示这个框架和它的能力!除了 Emmett、Django 和 Flask 之外,我过去还写过两篇讨论其他解决方案的文章,您可以在这里阅读:
</5-smooth-python-web-frameworks-for-the-modern-developer-47db692dfd52>
或者这里:
</5-cool-alternatives-to-django-and-flask-for-deploying-endpoints-5c99a066696>
功能概述
任何 web 框架实现的例子都会产生一个问题,为什么选择 Emmett 而不是 Flask 或 Django 等更受欢迎的解决方案。当我们在这两种解决方案之间比较 Emmett 时,我们应该注意到 Emmett 在很多方面是这两者的结合。Django 更常用于 fullstack 应用程序,而 Flask 更常用于更多数据驱动和简单的应用程序。两者都有不同的方法和语法。埃米特在很多方面是他们之间的一个伟大的中间地带。
语法和方法非常接* Flask。就我个人而言,我更喜欢这样。我从来都不喜欢 Django 压倒一切的模板性质。虽然它肯定没有任何问题,但它会立即生成大量目录和文件,而不是让用户自己创建。Emmett 介于这两者之间,但它不依赖于目录和文件,而是使用模块调用,我认为这要优雅得多。如果你想了解更多关于 Flask 和 Django 之间的区别,我有另一篇文章会详细介绍:
首先也是最重要的,埃米特有一套平易*人、易于使用的方法。使用与 Flask 类似的语法,大多数用户会发现自己在使用框架时如鱼得水。然而,这个框架并不像 Flask 那么轻量级,这意味着 Flask 内置了比我们预期的更多的优秀特性。它写起来像 Flask,但工作起来更像 Django。
该模块带有数据库类,使查询事物、任务甚至身份验证变得更加容易。根据我使用该模块的经验,所有这些都比 Django 的实现更容易使用。该项目还声称是生产就绪,这对于那些想在他们的技术堆栈中使用该模块的人来说当然是很好的。
Emmett 的另一个很酷的地方是它是异步的。Websockets 太牛了!埃米特被设计在阿辛西奥的顶部,这是一个为这类事情而建的古老的图书馆。这种方法使 Emmett 成为非阻塞的,并且对许多设计模式更加有用。另一个很酷的事情是这个包自带了自己的查询接口。
埃米特最酷的一点是模板。在大多数 web 框架中,模板通常是一团乱麻。有时他们有自己的模板框架,需要学*一门新的语言,有时你可能需要写一些 HTML 和/或 Javascript 来完成工作。Emmett 有一个非常强大的模板系统,没有这些传统系统的所有问题。模板是用纯 Python 编写的!
利用埃米特
根据我对该软件包的体验,使用 Emmett 非常简单,非常平易*人。文档也很特别。您可以安装软件包
pip3 install emmett
当然,我不是网络开发人员。我在 web 框架中寻找的可能与传统上使用 Python 进行全栈的框架不同。我所缺乏的恰好也是埃米特所拥有的,所以那绝对是非常酷的!Emmett 实际上包含了一个我们可以使用的非常棒且易于使用的查询框架,我认为它确实令人印象深刻。查看文档中的以下示例,我们只用几行代码就创建了一个包含用户、帖子和评论的模式!
from emmett import session, now
from emmett.orm import Model, Field, belongs_to, has_many
from emmett.tools.auth import AuthUserclass User(AuthUser):
# will create "users" table and groups/permissions ones
has_many('posts', 'comments')class Post(Model):
belongs_to('user')
has_many('comments')title = Field()
text = Field.text()
date = Field.datetime()default_values = {
'user': lambda: session.auth.user.id,
'date': lambda: now
}
validation = {
'title': {'presence': True},
'text': {'presence': True}
}
fields_rw = {
'user': False,
'date': False
}class Comment(Model):
belongs_to('user', 'post')text = Field.text()
date = Field.datetime()default_values = {
'user': lambda: session.auth.user.id,
'date': lambda: now
}
validation = {
'text': {'presence': True}
}
fields_rw = {
'user': False,
'post': False,
'date': False
}
我们可以使用@app 语法来路由应用程序。在我看来,这是非常独特和酷的,我们使用@app.command()方法,然后我们可以调用全局定义的函数供我们的应用程序使用!一个非常流行的数据科学和数据工程任务是创建一个简单的端点,下面是使用 Emmett 完成该任务的代码:
from emmett import App
app = App(__name__)
@app.route("/")
async def hello():
return "Here is a simple endpoint!"
正如您可能知道的,这个端点与 Flask 中的端点非常相似!事实上,代码几乎是相同的。然而,很容易看出这个框架不是烧瓶,它带来了一大堆特性。在我看来,它真的很酷,很高级,而且声明性很强,有时似乎太容易使用了。我并不经常发现自己爱上我找到的像这样的随机包,但它确实值得一试!我知道我将来会用到它!如果你对 Flask 更感兴趣,我确实有另一篇真的很老的文章(2019 年的!),您可以阅读以了解有关在 it 中部署端点的更多信息!
结论
虽然有时用一个有我名字的包工作有点令人不安,但这个包肯定值得处理那种恐怖谷的感觉。我真的很喜欢使用这个包,至少可以说,一次又一次地意识到它有多酷是令人震惊的。当然,当我使用这样的包时,我总是将它与我已经使用的包进行比较。在这方面,我最常用的选择可能是 Flask,我真的很喜欢 Flask 既小又大的设计。然而,这个网络框架让我对采用同样的方法并深入其中感到完全无语。如果我必须用 Python 程序员能理解的一句话来描述它,我会说“它就像 Flask,只是有很多令人惊奇和有用的特性。”我当然推荐这个模块,不仅仅是因为我们有相同的名字。感谢您的阅读,我希望您考虑试用这个软件包——它太棒了!
ElegantRL-Helloworld:一个轻量级且稳定的深度强化学*库
实践教程
24 小时学会实现深度强化学*算法。
由、Steven Li 和 Zeng 撰写的这篇文章描述了 ElegantRL 库( Twitter 和 Github )。
ElegantRL 的优势
强化学*(RL)的一句话总结:在 RL 中,智能体通过不断地与环境交互,以试错的方式进行学*,在不确定性下做出顺序决策,并在探索(新领域)和开发(使用从经验中学到的知识)之间实现平衡。
深度强化学*(DRL)在解决对人类具有挑战性的现实世界问题方面具有巨大潜力,例如自动驾驶汽车、游戏、自然语言处理(NLP)和金融交易。从 AlphaGo 的成功开始,各种 DRL 算法和应用正在以颠覆性的方式出现。ElegantRL 库使研究人员和从业人员能够对 DRL 技术的颠覆性“设计、开发和部署”进行流水线作业。
要展示的图书馆在以下几个方面具有“优雅”的特点:
ElegantRL 支持最先进的 DRL 算法,包括离散和连续算法,并在 Jupyter 笔记本中提供用户友好的教程。
ElegantRL 在 Actor-Critic 框架下实现 DRL 算法,其中一个代理(也称为 DRL 算法)由 Actor 网络和 Critic 网络组成。由于代码结构的完整性和简单性,用户能够容易地定制他们自己的代理。
概述:文件结构和功能
图一。 Agent.py 中的代理使用 Net.py 中的网络,并通过与 Env.py 中的环境交互,在 Run.py 中接受训练。【图片由作者提供。】
ElegantRL 的文件结构如图 1 所示:
- Env.py :它包含代理与之交互的环境。
- 一个用于体育馆环境修改的预处理类。
- 以自己创建的股票交易环境为例进行用户定制。
- Net.py :网络有三种类型:
- Q-Net,
- 演员网络,
- 评论家网络,
每个都包括一个用于继承的基础网络和一组用于不同算法的变体。
-
Agent.py :包含不同 DRL 算法的代理。
-
Run.py :为训练和评估过程提供基本功能;
- 参数初始化,
- 训练循环,
- 评估员。
作为高层次的概述,文件之间的关系如下。初始化 Env.py 中的一个环境和 Agent.py 中的一个代理。代理由 Net.py 中的演员和评论家网络构成。在 Run.py 中的每个训练步骤中,代理与环境交互,生成存储在重放缓冲区中的转换。然后,代理从重放缓冲区获取转换来训练其网络。每次更新后,评估器评估代理的性能,如果性能良好,则保存代理。
DRL 算法的实现
本部分分别介绍 DQN 系列算法和 DDPG 系列算法。每个 DRL 算法代理都遵循从其基类开始的层次结构。
图二。DQN 级数算法的继承层次。【图片由作者提供。】
如图 2 所示,DQN 级数算法的继承层次如下:
- 标准的 DQN 代理。
- AgentDoubleDQN :继承自 AgentDQN 的一个双 DQN 代理,具有两个 Q 网,用于减少高估。
- AgentDuelingDQN :具有不同 Q 值计算的 DQN 代理,继承自 AgentDQN。
- agend 3 qn:AgentDoubleDQN 和 AgentDuelingDQN 的组合,继承自 AgentDoubleDQN。
class **AgentBase**:
def **init**(self);
def **select_action**(states); # states = (state, …)
def **explore_env**(env, buffer, target_step, reward_scale, gamma);
def **update_net**(buffer, max_step, batch_size, repeat_times);
def **save_load_model**(cwd, if_save);
def **soft_update**(target_net, current_net);class **AgentDQN**:
def **init**(net_dim, state_dim, action_dim);
def **select_action**(states); # for discrete action space
def **explore_env**(env, buffer, target_step, reward_scale, gamma);
def **update_net**(buffer, max_step, batch_size, repeat_times);
def **save_or_load_model**(cwd, if_save);class **AgentDuelingDQN**(AgentDQN):
def **init**(net_dim, state_dim, action_dim);class **AgentDoubleDQN**(AgentDQN):
def **init**(self, net_dim, state_dim, action_dim);
def **select_action**(states);
def **update_net**(buffer, max_step, batch_size, repeat_times);class **AgentD3QN**(AgentDoubleDQN): # D3QN: Dueling Double DQN
def **init**(net_dim, state_dim, action_dim);
图 3。DDPG 级数算法的继承层次。【图片作者。】
如图 3 所示,DDPG 级数算法的继承层次如下
- AgentBase :所有演员评论家代理的基类。
- AgentDDPG:DDPG 代理,继承自 AgentBase。
class **AgentBase**:
def **init**(self);
def **select_action**(states); # states = (state, …)
def **explore_env**(env, buffer, target_step, reward_scale, gamma);
def **update_net**(buffer, max_step, batch_size, repeat_times);
def **save_load_model**(cwd, if_save);
def **soft_update**(target_net, current_net);class **AgentDDPG**(AgentBase):
def **init**(net_dim, state_dim, action_dim);
def **select_action**(states);
def **update_net**(buffer, max_step, batch_size, repeat_times);
在构建 DRL 代理时应用这样的层次有效地提高了轻量级和有效性。用户可以以类似的流程轻松设计和实现新的代理。
图 4。培训代理的数据流。【图片作者。]
基本上,代理有两个基本功能,数据流如图 4 所示:
- explore_env() :它允许代理与环境交互,并为训练网络生成转换。
- update_net() :先从重放缓冲区取一批变迁,然后用反向传播训练网络。
培训渠道
培训代理的两个主要步骤:
- 初始化:
- 超参数 args 。
- env = PreprocessEnv() :创建一个环境(以 OpenAI gym 格式)。
- agent = AgentXXX() :为 DRL 算法创建代理。
- evaluator = Evaluator() :评估并存储训练好的模型。
- buffer = ReplayBuffer() :存储过渡。
2.然后,训练过程由 while-loop 控制:
- agent.explore_env(…): 代理在目标步骤中探索环境,生成转换,并将它们存储到 ReplayBuffer 中。
- agent.update_net(…) :代理使用 ReplayBuffer 中的批处理更新网络参数。
- evaluator . evaluate _ save(…):评估代理的表现,保留最高分的训练好的模型。
当条件满足时,while 循环将终止,例如,达到目标分数、最大步数或手动中断。
测试示例:两足步行器-v3
两足步行机-v3 是机器人学中的一项经典任务,它执行一项基本技能:移动。目标是让 2D 两足步行机走过崎岖的地形。双足步行器被认为是连续动作空间中的一个困难任务,能够达到目标奖励的 RL 实现只有少数。
步骤 1:安装 ElegantRL
pip install git+https://github.com/AI4Finance-LLC/ElegantRL.git
步骤 2:导入包
- 优雅的
- OpenAI Gym :开发和比较强化学*算法的工具包。
- py bullet Gym:open ai Gym MuJoCo 环境的开源实现。
from **elegantrl.run** import *from **elegantrl.agent** import AgentGaePPOfrom **elegantrl.env** import PreprocessEnvimport gymgym.logger.set_level(40) # Block warning
步骤 3:指定代理和环境
- args.agent :首先选择一个 DRL 算法,用户可以从 agent.py 中的一组代理中选择一个
- args.env :创建并预处理环境,用户可以自定义自己的环境,也可以从 env.py 中的 OpenAI Gym 和 PyBullet Gym 中预处理环境
args = Arguments(if_on_policy=False)args.agent = AgentGaePPO() # AgentSAC(), AgentTD3(), AgentDDPG()args.env = PreprocessEnv(env=gym.make(‘BipedalWalker-v3’))args.reward_scale = 2 ** -1 # RewardRange: -200 < -150 < 300 < 334args.gamma = 0.95args.rollout_num = 2 # the number of rollout workers (larger is not always faster)
步骤 4:培训和评估代理
训练和评估过程在函数train _ and _ evaluate _ _ multi processing(args)内部,参数为 args 。它包括 DRL 的两个基本目标:
- 代理人,
- 环境。
训练的参数是:
- 批量大小,
- 目标 _ 步骤,
- 奖励 _ 尺度,
- 伽马等。
用于评估的参数还有:
- break_step
- 随机种子等。
train_and_evaluate__multiprocessing(args) # the training process will terminate once it reaches the target reward.
第五步:测试结果
达到目标奖励后,我们为每个状态生成帧,并将帧合成为视频结果。从视频来看,步行者能够不断地向前移动。
for i in range(1024):
frame = gym_env.render('rgb_array')
cv2.imwrite(f'{save_dir}/{i:06}.png', frame)
states = torch.as_tensor((state,), dtype=torch.float32, device=device)
actions = agent.act(states)
action = actions.detach().cpu().numpy()[0]
next_state, reward, done, _ = env.step(action) if done:
state = env.reset()
else:
state = next_state
图 5。(左)随机行动的特工。(右)ElegantRL 里的一个 PPO 特工。【图片作者。]
看看这个 BipedalWalker-v3 演示的代码。
ElegantRL:掌握 PPO 算法
思想和理论
*似策略优化算法(PPO)教程
由和 Steven Li 撰写的这篇文章描述了在 ElegantRL 库中实现*似策略优化(PPO)算法( Twitter 和 Github )。PPO 算法是目前广泛使用的深度学*算法,被许多研究机构和学者选为基准。
请查看 ElegantRL 库的介绍性文章。
PPO 算法概述
符合策略和不符合策略的算法有什么区别?
图一。策略外和策略内算法。【图片由作者提供。]
对于基于策略的算法,它们根据当前策略网络生成的转换来更新策略网络。评论家网络将在普通环境下对当前策略网络做出更准确的价值预测。
对于非策略算法,它们允许使用旧策略的转换来更新当前策略网络。因此,可以重新利用旧的转换,如图 1 所示,点分散在由不同策略生成的轨迹上,这提高了采样效率并减少了总的训练步骤。
有没有一种方法可以提高基于策略的算法的样本效率而不损失它们的好处?
答案是是的。作为一种基于策略的算法,PPO 利用替代目标来避免新策略与旧策略相差太远,从而解决了样本效率问题。代理目标是 PPO 的关键特征,因为它既规范了策略更新,又支持训练数据的重用。
标准 PPO 有一个削波目标函数[1]:
PPO-Clip 只是在概率比项上加了一个剪辑区间,剪辑成一个范围[1 — ϶,1 + ϶],其中϶是一个超参数。则该函数取原始比率和限幅比率之间的最小值。
目标函数内部的概率比项是什么?
由于替代目标,PPO 允许使用梯度上升的多个时期,而不会与旧政策相差太远。在这种情况下,我们必须使用重要性抽样估计器来补偿训练数据分布和当前策略状态分布之间的差距,这是概率比项:
伪代码和算法细节
对于策略正则化,标准 PPO 算法使用剪切目标;对于策略参数化,标准 PPO 算法在连续动作空间使用高斯分布,在离散动作空间使用 Softmax 函数。此外,它遵循一个经典的演员-评论家框架,由四个部分组成:
- 初始化:初始化相关属性和网络。
- 探索:通过演员网络与环境的互动来探索过渡。
- 计算:计算相关变量,如比率项、确切报酬、估计优势等。
- 更新:基于损失函数和目标函数更新演员和评论家网络。
接下来,我们解释 Alg。1 以逐步的方式:
Alg。1:PPO-Clip 算法。来自[1]。
- 步骤 1 :初始化演员和评论家网络以及参数϶.
- 第三步:从最新的演员策略中收集一批轨迹。
- 第 4 步:计算每一步中每条轨迹的确切奖励。
- 步骤 5 :根据最新的 Critic 网络计算每个轨迹的估计优势。
- 步骤 6 :通过在 K 个时期内最大化 PPO-CLIP 目标函数,通过随机梯度上升来更新演员的参数。当我们开始运行优化时,旧策略与更新后的策略相同,这意味着比率为 1。随着我们不断更新政策,该比率将开始触及上限。
- 步骤 7 :通过均方误差的梯度下降来更新评论家的参数。
雅致放射中的多酚氧化酶
ElegantRL 中的每个 DRL 代理都遵循其基类的层次结构,以实现轻量级编码。在本节中,我们将讨论标准 PPO 在 agent.py 中的设计和实现。
图二。PPO 算法的继承层次结构。【图片由作者提供。]
代理库:
基本上,大多数经典的 DRL 算法都继承自 AgentBase 类。在 AgentBase 内部,我们初始化公共的变量:
- 学*率
- 环境状态
和功能:
- select_action() :给定环境的状态,获取相应的动作。
- explore_env() :使用 select_action() ,通过演员网络和环境之间的交互来探索过渡。
- update_net() :从 ReplayBuffer 中采样批量数据,更新神经网络。
- save_load_model() :保存模型用于训练,或者加载模型用于推理。
- soft_update() :如果需要,从当前网络更新目标网络。
AgentPPO:
在 AgentPPO 中,我们添加了一些与 PPO 算法相关的新变量,并重新定义了几个函数。
我们遵循 Alg 中的步骤。1、先讨论变量(第 1 行):
- ratio_clip :限幅后的区间范围。
- λ_ 熵:探测参数。
- act = ActorPPO() :演员网。
- cri = CriticAdv() :评论家网。
图 3。AgentPPO 中的函数结构。【图片由作者提供。]
我们在 Alg 中拆分 for 循环(第 3–7 行)。1 分为两个核心功能:
- explore_env (第 3 行):使用 Actor-network 来探索环境,并将结果转换存储到 ReplayBuffer 中。
def **explore_env**(self, env, buffer, target_step, reward_scale, gamma):
"""
:**env:** RL training environment.
:**buffer:** Experience Replay Buffer.
:**int target_step:** explored target_step number of step in env.
:**float reward_scale:** scale reward, 'reward * reward_scale'.
:**float gamma:** discount factor, 'mask = 0.0 if done else gamma'.
:**return** **int target_step:** collected target_step number of step in
env.
"""
**# empty the ReplayBuffer (necessary for on-policy algorithm)**
buffer.empty_buffer_before_explore()
actual_step = 0
while actual_step < target_step:
**# for each trajectory, reset the environment at beginning**
state = env.reset() for _ in range(env.max_step):
**# get the action from Actor network**
action, noise = self.select_action(state)
**# step the environment forward based on the action**
next_state, reward, done, _ = env.step(np.tanh(action))
... other = (reward * reward_scale, 0.0 if done else gamma,
*action, *noise)
**# append transition into ReplayBuffer**
buffer.append_buffer(state, other) ... state = next_state return actual_step
- update_policy (第 4–7 行):从 ReplayBuffer 中抽取一批转换,并计算更新网络的目标。
def **update_net**(self, buffer, _target_step, batch_size, repeat_times=4):
"""
**:buffer:** Experience replay buffer.
:**int target_step:** explore target_step number of step in env.
**:int batch_size:** sample batch_size of data for Stochastic
Gradient Descent
**:float repeat_times:** the times of sample batch.
**:return float obj_a:** the objective value of Actor.
**:return float obj_c:** the objective value of Critic.
""" ... with torch.no_grad():
** # sample data from ReplayBuffer**
buf_reward, buf_mask, buf_action, buf_noise, buf_state =
buffer.sample_all()
**# compute the expected value from critic network**
bs = 2 ** 10
buf_value = torch.cat([self.cri(buf_state[i:i + bs]) for i
in range(0, buf_state.size(0), bs)], dim=0)
**# compute the log of probability, which is the denominator
of the probability ratio in the surrogate objectve**
buf_logprob = -(buf_noise.pow(2).__mul__(0.5) +
self.act.a_std_log +
self.act.sqrt_2pi_log).sum(1)
** # compute the reward and advantage**
buf_r_sum, buf_advantage = self.compute_reward(buf_len,
buf_reward, buf_mask, buf_value) ... for _ in range(int(repeat_times * buf_len / batch_size)):
**# randomly select a transition**
indices = torch.randint(buf_len, size=(batch_size,),
requires_grad=False, device=self.device)
state = buf_state[indices]
action = buf_action[indices]
r_sum = buf_r_sum[indices]
logprob = buf_logprob[indices]
advantage = buf_advantage[indices] **# compute the new log of probability and probability ratio**
new_logprob = self.act.compute_logprob(state, action)
ratio = (new_logprob — logprob).exp()
**# compute the surrogate objective with entropy exploration**
obj_surrogate1 = advantage * ratio
obj_surrogate2 = advantage * ratio.clamp(1 —
self.ratio_clip, 1 + self.ratio_clip)
obj_surrogate = -torch.min(obj_surrogate1,
obj_surrogate2).mean()
obj_entropy = (new_logprob.exp() * new_logprob).mean()
obj_actor = obj_surrogate + obj_entropy *
self.lambda_entropy **# compute the loss of Critic network**
value = self.cri(state).squeeze(1)
obj_critic = self.criterion(value, r_sum)
**# update Actor and Critic networks together**
obj_united = obj_actor + obj_critic / (r_sum.std() + 1e-5)
self.optimizer.zero_grad()
obj_united.backward()
self.optimizer.step() return self.act.a_std_log.mean().item(), obj_critic.item()
培训渠道
培训代理的两个主要步骤:
- 初始化:
- 超参数 args 。
- env = PreprocessEnv() :创建一个环境(以 OpenAI gym 格式)。
- agent = AgentXXX() :为 DRL 算法创建一个代理。
- evaluator = Evaluator() :评估并存储训练好的模型。
- buffer = ReplayBuffer() :存储过渡。
2.然后,训练过程由 while 循环控制:
- agent.explore_env(…): 代理浏览目标步骤中的环境,生成转换,并将它们存储到 ReplayBuffer 中。
- agent.update_net(…) :代理使用 ReplayBuffer 中的批处理更新网络参数。
- evaluator . evaluate _ save(…):评估代理的表现,保留最高分的训练好的模型。
当条件满足时,while 循环将终止,例如,达到目标分数、最大步数或手动中断。
测试示例
欢迎感兴趣的用户测试 PPO 单档。
[1] OpenAI 在深度 RL 中加速旋转,*端策略优化。
[2]政策梯度算法, PPO 。
ElegantRL-Podracer:一个可扩展的弹性库,用于云原生深度强化学*
代理作为 pod 在云上进行赛车比赛。
“当风暴结束时,你可以看到我的赛车。我在造一辆赛车!”
—阿纳金·天行者
我们正在云上构建一个赛车,使用https://github.com/AI4Finance-Foundation/ElegantRL。
如果你想看一个在几分钟内训练一个强大的深度强化学*(DRL)代理的“赛车”,这篇文章就是为你准备的。ElegantRL-Podracer 是一个云解决方案,支持数百万个 GPU 核心在多个级别上进行大规模并行 DRL 训练。让我们开始吧!
Steven Li 和刘晓阳的这篇文章描述了我们最*在neur IPS 2021:Deep RL Workshop上发表的论文 ElegantRL-Podracer:可扩展和弹性的云原生深度强化学*库。
*https://arxiv.org/abs/2112.05923
代码、文档和演示可在 GitHub 上获得。
https://github.com/AI4Finance-Foundation/ElegantRL
最大的挑战是什么?
eep 强化学*平衡了(未知领域的)探索和(当前信息的)利用,在游戏和机器人控制等应用中彻底改变了学*和驱动。
然而,数据收集的成本仍然是在复杂和动态环境的现实世界问题中更广泛采用 DRL 的一个主要挑战。因此,一个引人注目的解决方案是在数百甚至数千个 GPU 上进行大规模并行训练,比如数百万个 GPU 核心。
现有的 DRL 框架/库要么缺乏可伸缩性,要么学*难度很大。
图 1:不同框架/库的比较。【图片由作者提供。]
设计原则
我们开发了一个用户友好的开源库,利用云资源来训练 DRL 特工。该库强调以下设计原则:
- 横向扩展:可伸缩性和弹性。
- 效率:低通信开销,大规模并行模拟,代理的健壮性。
- 可达性:轻量化、定制化。
基于锦标赛的集成方案
ElegantRL-Podracer 采用基于锦标赛的合奏方案,在数百甚至数千个 GPU 上编排训练过程,安排排行榜和具有数百个代理(pod)的训练池之间的交互。
图 2:基于锦标赛的合奏方案。【图片由作者提供。]
与进化策略(es)不同,在进化策略中,一群智能体经过几代进化,我们的基于锦标赛的集成方案并行地异步更新智能体,这将群体进化和单个智能体学*解耦。基于锦标赛的合奏方案的关键是排行榜和训练池之间的互动。
- 一个编排器实例化一个新的代理(pod)并将其放入一个训练池。
- 一个生成器用从排行榜中选择的网络和优化器初始化一个代理(pod)。
- 一个更新器在一个代理被训练一定步数或一定时间后,根据其表现确定是否将代理插入排行榜以及在何处插入。
直觉上,ElegantRL-Podracer 的灵感来自凯文·凯利的书《失控》(失控)(嵌套层次结构的优点)和《从愚蠢的事情中变得聪明》。对于低级别的训练,ElegantRL-Podracer 通过采用面向硬件的优化来实现嵌套层次。对于高级调度,我们从数百个弱代理中获得一个智能代理。
云原生范式
ElegantRL-Podracer 通过实现微服务和容器化的开发原则,遵循了云原生范式。我们将培训过程分解为五个组成部分,并将其作为微服务来实施,例如,编排者、排行榜、工作者、学*者和评估者。我们使用 Kubernetes (K8s)作为资源管理器,并在独立的容器和 pod 中执行每个流程。
ElegantRL-Podracer 的特点
可扩展的并行性:ElegantRL-pod racer 的多级并行性带来了很高的可扩展性。
- 代理并行:训练池中的代理是并行的,因此可以扩展到大量。并行代理的异步训练也可以减少代理对代理的通信频率。
- 学*者并行:一个 agent 采用多个学*者并行训练神经网络,然后融合网络的参数得到一个结果 agent,而不是使用分布式 SGD。这种通过网络参数的模型融合涉及低频通信,因为融合过程发生在一个时期的末尾。
- Worker parallelism :一个代理利用多个部署工作器并行地对转换进行采样。
弹性资源分配:弹性对于云级应用至关重要,因为它有助于用户适应云资源的变化,防止资源过度供应和供应不足。ElegantRL-Podracer 可以通过使用 orchestrator 来监控可用的计算资源和当前的训练状态,从而灵活地分配代理(pod)的数量。
面向云的优化 : ElegantRL-Podracer 在 GPU 上共同定位微服务,以加速数据收集和模型训练的并行计算。对于数据传输和存储,ElegantRL-Podracer 将数据表示为张量以加快通信速度,并在 GPU 的连续内存上分配共享重放缓冲区以提高寻址速度。
实验
金融是 DRL 算法的一个充满希望和挑战的现实世界应用。作为例子,我们将 ElegantRL-podracer 应用于股票交易任务,以展示其在量化金融中的潜力。
我们的目标是训练一个 DRL 代理人来决定在股票市场上在哪里交易,在什么价格交易,在什么数量交易,因此问题的目标是最大化期望收益和最小化风险。我们将股票交易任务建模为马尔可夫决策过程(MDP),如 FinRL 所示。我们遵循一个训练-回溯测试管道,并将数据集分为两组:从 2016 年 1 月 1 日到 2020 年 5 月 25 日的数据用于训练,从 2020 年 5 月 26 日到 2021 年 5 月 26 日的数据用于回溯测试。
这些实验是在一个 DGX SuperPOD 云中使用 NVIDIA DGX-2 服务器执行的,这是一个云原生基础设施。
左图:分钟级别纳斯达克 100 指数成份股的累积回报率(初始资本
$1,000,000,交易成本 0.2%)。右图:使用 ElegantRL-podracer 和 RLlib 的模型快照,达到累积
奖励 1.7 和 1.8 的训练时间(挂钟时间)。
纳斯达克 100 成份股的分钟级数据的股票交易表现。
就累积回报而言,所有 DRL 代理人都可以取得比市场基准更好的表现,证明了算法的有效性。我们观察到 ElegantRL-podracer 累计回报 104.743%,年回报 103.591%,夏普比率 2.20,大幅跑赢 RLlib。然而,ElegantRL-podracer 在回溯测试期间不像 RLlib 那样稳定:它实现了最大 35.357%的年波动率。下降-17.187%,卡尔马尔比率为 6.02。这种不稳定性有两个可能的原因:
- 股票交易环境中的报酬设计主要与累积收益相关,从而导致代理人较少顾及风险;
- ElegantRL-podracer 在 2021–03 年前后持有大量资金,自然导致了更大的滑盘。
我们比较了不同数量的 GPU 上的训练性能,即 8、16、32 和 80。我们测量所需的训练时间,以获得两个分别为 1.7 和 1.8 的累积回报。无论是 ElegantRL-podracer 还是 RLlib,随着 GPU 数量的增加,达到相同的累积回报所需的训练时间更少,这直接证明了云计算资源在 DRL 训练上的优势。对于 80 个 GPU 的 ElegantRL-podracer,需要(1900,2200)才能达到 1.7 和 1.8 的累积回报。拥有 32 个和 16 个 GPU 的 ElegantRL-podracer 需要(2400、2800)和(3400、4000)才能实现相同的累积回报。它展示了 ElegantRL-podracer 的高可扩展性和我们面向云的优化的有效性。对于使用 RLlib 的实验来说,增加 GPU 的数量并不会带来很大的加速。
确认
这项研究使用了由 IDEA 研究院提供的 GPU 云平台的计算资源。
参考
[1]埃里克·梁、理查德·廖、罗伯特·西原、菲利普·莫里茨、罗伊·福克斯、肯·戈德堡、约瑟夫·冈萨雷斯、迈克尔·乔丹和扬·斯托伊察。RLlib:分布式强化学*的抽象。在 ICML ,第 3053–3062 页。PMLR,2018。
[2] J .舒尔曼、f .沃尔斯基、普拉富拉·德里瓦尔、亚历克·拉德福德和奥列格·克里莫夫。*似策略优化算法。 ArXiv ,abs/1707.06347,2017。
[3] Erik Wijmans、Abhishek Kadian、Ari S. Morcos、Stefan Lee、Irfan Essa、Devi Parikh、M. Savva 和 Dhruv Batra。DD-PPO:从 25 亿帧中学**乎完美的 pointgoal 导航仪。在 ICLR ,2020 年。
[4]凯文·凯利。失控:新生物文明的崛起。艾迪森-韦斯利朗曼出版公司,1994 年。*
提升您的数据科学能力:学会随机应变
请加入这个新系列,我们将探索提高您作为数据科学家的效率的软技能
照片由 Unsplash 上的 noe fornells 拍摄
什么是足智多谋?
椰子章鱼可能是世界上最机智的动物之一。章鱼以其智慧而闻名,但椰子章鱼却因一个惊人的特性而脱颖而出:它们经常携带椰子和贝壳作为装甲保护。
因为它们通常有 3 到 6 英寸长,而且无毒,所以必须采取保护措施,以免成为其他海洋动物的午餐。但这还不是全部。这些足智多谋的海底生物能够用两足行走,更广为人知的是行走。说说惊艳吧!
足智多谋就是有能力找到快速聪明的方法来克服困难。那就是用你手头的资源去解决问题。在数据科学领域,我们在完成工作的过程中不缺少需要解决的问题。
我在新西兰长大,我们有一种说法,我们可以用 8 号铁丝网建造或修理任何东西。我们也称之为 kiwi 独创性,这是一种积极进取的态度和横向思考解决问题的能力。
在新西兰,我们为自己是一个足智多谋的民族而自豪,但我并非生来就有这种技能。我必须学会它。这是一种需要刻意练*和关注来发展的技能。但是当你这样做的时候,这是一项会给你和你的雇主带来无数好处的技能。
彼得·阿肖夫在 Unsplash 上拍摄的照片
足智多谋有什么好处?
- 足智多谋的人思想开放:他们乐于接受新的想法、观点和挑战。足智多谋的人是狂热的读者和探索者,总是在学*。
- 足智多谋的人自信:他们对自己知道的和不知道的同样自信。足智多谋的人敢于要求他们需要的东西。
- 足智多谋的人富有创造力:这就是 8 号线和独创性概念发挥最大作用的地方。足智多谋的人乐于接受新的不同的解决方案。他们不相信一个问题必须用一种方式解决,因为它总是这样。
- 足智多谋的人积极主动:他们不会坐以待毙,等待解决方案的到来。足智多谋的人会站出来带头,召集志同道合的同事一起踏上征程。
- 足智多谋的人坚持不懈:他们知道解决问题有很多方法,有时问题不会一次就解决。但是,他们会继续尝试,直到完成。
- 足智多谋的人是问题的解决者:当面对新问题时,足智多谋的人既有能力运用过去的知识,也有能力寻找新的知识来找到最佳的解决方案。
- 足智多谋的人适应性强:他们不会拘泥于一种解决所有问题的方法,他们会乐于寻求同事的建议,找到替代方法。事实上,当他们这样做的时候,他们会有成就感。
我如何变得足智多谋?
它的核心是,足智多谋是关于身体的行动。这里有一些切实可行的方法让你发展和展示你的足智多谋:
- 磨练你的研究技能:在我看来,足智多谋就是知道如何以及在哪里找到你需要的信息。创建一个有用资源的目录,当需要的时候你可以研究它。作为数据科学家,我们有很多资源可以支配,通常是免费的:Google Search、Git Hub、Medium、Stack Overflow 等等。通常情况下,你不需要重新发明轮子,因为解决方案或者至少是有助于你实现目标的小技巧已经存在。
- 利用你的人际网络:拥有一个强大的人际网络是非常重要的。不要害怕向以前的同事、商业伙伴和行业专家寻求帮助。除了获取信息来帮助你解决当前的问题,你还可以学*新的技能来增加你的工具包,以解决未来的问题。
- 不断开发新技能:要想变得足智多谋,没有比增加可用资源更好的方法了。在数据科学中,总是有新的工具、技术和方法需要学*。保持最新。
- 了解自己的优势和劣势:你对这些了解得越多,你就能越快地确定你是否已经拥有解决问题所需的资源和知识,或者你是否需要深入研究或拓展你的人际网络。
- 给自己时间思考和制定策略:停下来确定你解决问题的策略,需要什么资源,在哪里可以获得这些资源将会带来无尽的回报。
- 更好地利用你已经拥有的资源:足智多谋的一个关键是了解你手头已经有哪些资源,以及如何最好地利用它们。使用你已经拥有的资源往往比去别处寻找更快更容易。但是,同样重要的是,当你没有你需要的东西时,要快速识别。
除了是一种技能,足智多谋还是一种心态,一种态度。足智多谋需要有意识地努力采取行动。
结束语
要实现你想要的,你不应该依赖于你拥有或没有的资源。你成为一个足智多谋的人的能力取决于你如何使用你已经拥有的东西,以及你如何获得你没有但需要的东西。
正如托尼·罗宾斯曾经说过的,不是缺乏资源,而是你缺乏足智多谋。
我毫不怀疑我的足智多谋给我的事业和生活带来了成功。事实上,我喜欢解决问题的挑战,我喜欢寻找解决方案。
如果你也成为一个足智多谋的人,你会用更好的思维和更多的创造力影响你周围的人。
也请查看我的系列文章通过示例学* Python 数据分析:
电梯优化问题。
最*,我完成了一个小项目,该项目要求我为优化纽约市一座理论上的高层建筑内的电梯配置提出建议。该建筑的结构如下。进入大楼的人必须首先通过安全系统刷卡,然后他们可以按下电梯呼叫按钮,等待电梯到来。我被要求回答 5 个主要问题,并且只花几个小时完成这项任务。主要问题是:
概述电梯等待时间的当前状态。
算出总的平均等待时间。
确定一天中等待时间较长的时间。
确定电梯等待时间较长的楼层。
就如何优化电梯配置以减少等待时间提供至少 2 条建议。
为了回答这些问题,提供了 2 个数据集。一个名为people.csv
的记录了所有进入大楼安全系统的刷卡记录,另一个名为simulation.csv
的记录了我们拥有数据的几个月中每部电梯的状态和操作。这座 20 层的大楼里总共有 4 部电梯。
people.csv 的数据描述
simulation.csv 的数据描述
初步见解。
我想做的第一件事是把我的头缠在数据上,这样我就可以开始思考如何回答提供的问题。正是在这一阶段,我确定只有 4 部电梯,而且这栋建筑有 20 层。我还了解到,为了正确地分析数据,我需要将每个数据集中的日期时间列从字符串转换为 pandas 日期时间。
simulation['time'] = pd.to_datetime(simulation['time'], infer_datetime_format=True)
people['time'] = pd.to_datetime(people['time'], infer_datetime_format=True)
还有一些额外的列没有在数据描述中列出,但是存在于数据集中,其中包括date
、date_daily
,这两个列似乎都是从time
列派生出来的,因此我决定删除它们。
simulation.drop(['date','date_daily', 'weight'], axis=1, inplace=True)
为了查看谁进入了大楼,我使用了 seaborn countplot 来可视化进入大楼的客人/非客人的总数。
更深入的分析。
我想弄清楚大厅里的人刷卡进入后需要多长时间才能呼叫电梯。为了做到这一点,我需要对一个人去电梯的路径做一个假设。我假设在进入大楼的安全系统后,一个人的下一个动作是点击呼叫按钮。
为了解决这个问题,我使用了 pd.merge_asof ,它类似于一个左连接,但是它不是匹配精确的键,而是匹配最*的键。在我们的例子中,我们希望将距离 0 楼(大厅)最*的呼叫与记录的进入大楼的时间最接*。我们希望进行反向搜索,因为刷卡时间会早于所有人进入大楼的电梯呼叫时间。然后,我们可以从刷卡时间中减去电梯呼叫时间,以确定一个人刷卡后按下呼叫按钮的时间。
现在我们知道,人们刷过安检后,平均需要 28 秒才能按下电梯呼叫按钮。有人按下呼叫按钮的最长时间是 59 秒。在确定这一点之后,我决定将我的精力集中在simulation.csv
文件上,它有每个电梯的日志,可以用来回答主要的问题。
要确定电梯完成每一次行程需要多长时间,搜索系统(呼叫)动作的数据,然后找到下一次电梯门在呼叫按钮被按下的楼层打开的时间。然后,我可以找出哪部电梯到达了,用了多长时间,以及电梯从哪一层到达。这些信息足以回答主要问题。
通过从到达的电梯楼层中减去电梯呼叫楼层,我们可以看到电梯运行了多少层到达呼叫它的人,这可以提供一些关于长于平均等待时间的见解。
现在我们应该有足够的数据来分析和回答主要问题。
一天中电梯等待时间较长的时间?
正如我们在上面看到的,等待时间在工作日的开始(上午 7:30 后不久)和结束(下午 4:00-7/8:00)时达到峰值。午餐时间还有两个较小的峰值,在上午 11:00 到下午 1:00 之间。
在哪个楼层等待时间更长?
上图绘制了第一、第二和第三分位数的等待时间。红线描述了那些等待电梯时间最长的人等待的时间。从这个图表中我们可以看到,在高峰时段,你在大楼里越高,你等电梯的时间就越长。为什么?进一步的分析表明,那些等待时间最长的人是在等待电梯运行超过 10 层才能到达他们那里。这意味着电梯默认是通往大厅的。
下图显示,对于等待电梯到达时间超过 60 秒的电梯用户,到达的电梯通常来自大厅(0 层)。
总体平均等待时间?
wait_times = simulation.loc[simulation['action'] == 'call']overall_average = wait_times.wait_time.mean()
电梯的总平均等待时间为 25.69 秒。
描述性机器学*。
作为一种健全性检查并满足我自己的好奇心,我对数据拟合了一个线性回归模型,然后计算了特征的排列重要性,以确定模型认为对电梯等待时间影响最大的因素是什么。事实证明,total_floors_traveled
对电梯使用者等待的时间影响最大。
建议。
为了减少我们虚拟建筑中电梯的等待时间,我建议首先在用户刷过安检时触发电梯呼叫大厅,这将节省一些乘坐电梯到达目的地楼层的时间。
我的第二个建议是实施一个固定分区公共分区系统的一些变体,这是一种配置,其中每个电梯被给予它自己的优先分区。由于我们总共有 4 部电梯,我们将有 4 个不同的区域,一部指定的电梯将服务于这些区域。
我还建议至少配置一部电梯默认到顶层(19 层),以减少等待电梯到达的时间超过一分钟。这些改变将减少大楼占用者与等待相关的投诉。
下一步。
这是一个值得思考和尝试“解决”的非常有趣的项目。我对此做了很少的研究,欢迎在评论中对我的方法和方式提出批评。在完成这个迭代之后,我发现有大量的工作致力于解决这个问题。我觉得特别有趣的一本书是鲁提菲·谢里夫·吉娜·巴尼的《电梯交通手册理论与实践》。在我读完这本书之后,重访这个项目并应用我所学到的东西/看看我可以在哪里改进我最初的分析会很有趣。
在那之前,编码快乐!
你可以在这里找到这个项目的数据和笔记本。
消除人工智能偏见
公平和偏见
识别人工智能偏差并知道如何防止其在人工智能/人工智能管道中发生
由 Sushil Nash 在 Unsplash 上拍摄
人工智能偏见的证据
人工智能(AI)的主要目的是通过使用机器扫描大量数据的能力来检测潜在的模式和异常,以节省时间和提高效率,从而减少人工劳动。然而,人工智能算法也不能避免偏差。人工智能偏见以几种形式出现,下面重点介绍一些例子:
- 2018 年,亚马逊因对女性有偏见而停止使用其人工智能招聘工具。
- 2018 年 7 月,美国司法系统(如 COMPAS)使用的 AI 工具在用于预测累犯时,因显示种族偏见而受到审查。
- 2019 年 11 月,苹果信用卡因基于性别提供不同的信用额度而固有地对女性比对男性有偏见而受到指控。
- 2020 年,英国使用人工智能算法进行公共 welfare⁴.决策的委员会数量有所增加
- 最*,在 2021 年 9 月,哈佛商学院和埃森哲的一份报告发现,美国约有 2700 万工人被自动化技术从工作申请中过滤掉。这些工作人员包括护理人员和 immigrants⁵.
由于人工智能算法可能会对一个组织的声誉产生长期影响,并对公众产生严重后果,因此确保它们不会偏向人口中的特定子群体非常重要。通俗地说,人工智能算法中的算法偏差发生在结果缺乏公平性或由于具体的类别区分而偏向某个群体时,这些类别包括种族、年龄、性别、资格、残疾和地理位置。
AI 偏差是如何发生的?
当在机器学*过程中对数据集或模型输出做出错误的假设时,就会发生人工智能偏差,这随后会导致不公平的结果。偏差可能发生在项目设计或数据收集过程中,产生的结果不公平地代表了人口。例如,脸书上发布的一项调查询问人们对维多利亚州新冠肺炎封锁的看法,发现 90%的维多利亚人害怕跨州和海外旅行,因为疫情。这种说法是有缺陷的,因为它基于仅访问社交媒体(即脸书)的个人,可能包括不在维多利亚州的用户,并且可能过度代表特定年龄组(即 20-30 岁之间的年龄超过 50 岁及以上的年龄)、种族或性别,这是由他们使用脸书决定的。
为了有效地识别人工智能偏见,我们需要在人工智能生命周期中寻找偏见的存在,如图 1 所示。
图 1:人工智能/人工智能管道的五个阶段和相关的人工智能偏差(图片由作者提供)
人工智能/人工智能管道中人工智能偏差的来源
一个典型的 AI/ML 管道从一个业务问题开始,需要使用数据和分析来理解问题的驱动因素。这个业务问题通常被转换成一个或多个数据假设。然后设计一项研究,以确定需要收集的数据、执行数据收集、注释、准备和转换为可用于模型开发的格式的过程。
让我们来看看不同类型的偏置是如何在流水线的每个阶段引入的。
研究设计和假设制定
抽样偏倚发生在研究设计中只对人群的一个子集进行抽样,或者从人群中选择的样本是非随机的(见图 2)。这种类型的偏差通常在调查和在线民意测验中普遍存在,在这些调查和在线民意测验中,只对人口的一个子集进行抽样,因为他们可以访问民意测验或自愿完成调查或民意测验,从而导致自愿偏差,这是一种抽样偏差。
图 2:抽样偏差的证据,其中只考虑了有权访问纸质调查的个人(图片由作者提供)
数据收集、预处理和探索
一旦研究被设计,数据就被收集、预处理,并以概括数据集的初步图表的形式进行探索。
通常收集的数据是用于代表典型目标人群的样本。然而,由于设计偏差,由于数据集中的不平衡,样本可能无法代表总体。例如,谷歌搜索引擎中使用的机器学*模型很难区分人类图像和 gorillas⁶.图像这是因为数据集没有包括大猩猩、黑猩猩和猴子图像的公平表示。
当由于少数民族数据集的代表性不足而导致数据集中存在不平衡时,该算法在这种情况下对过度代表性的类(如熊猫和贵宾犬)表现良好,但对少数民族类(属于猴子家族的那些)表现不佳。由于预处理阶段使用的标签技术并不完善,这一问题变得更加突出。因此,这导致了数据集的不正确注释,这是一种形式的标签偏差,称为回忆偏差。
测量偏差是一种标签偏差,当训练数据集与真实数据集不同时,由于错误的测量导致数据失真,就会出现这种偏差。一个这样的例子发生在一个项目中,在该项目中,不同品牌的分光计被用于收集训练集和测试集中的植物样本的波长数据。这些光谱仪记录不同波长范围的数据。此外,由于电池电量低,用于训练集的老式光谱仪在捕获植物样品波长时不使用光源。因此,需要调整测试数据集来消除灯光效果。
排除偏差发生在数据清理步骤中,数据科学家可能会删除被认为不相关的特征。例如,超市的数据集由澳大利亚和新西兰的销售数据组成。在数据探索阶段,发现 95%的客户来自澳大利亚。考虑到位置字段是不相关的,因此从数据集中删除了该字段。这意味着该模型可能不会考虑澳大利亚和新西兰客户之间的差异,例如后者在网上购物上花费更多。
模型开发
在开发模型之前,数据集通常分为训练集、验证集和测试集。这可能导致时间间隔偏差,数据科学家有意选择一个特定的时间框架来支持假设。一个例子是形成一个结论,即某一特定的泳装系列是有利可图的,因为只考虑夏季月份的销售。
图 3:时间间隔偏差的证据,在评估泳装系列的利润时,仅考虑澳大利亚的夏季月份
存活偏差是在模型开发阶段出现的另一种偏差,此时数据科学家只包括在选择过程中“存活”下来的数据。一个很好的例子是,二战期间从事海军分析的研究人员被要求找出军方战斗机中最薄弱的地方。为了回答这个问题,研究人员只检查了从战斗任务中返回的飞机,寻找子弹穿透飞机的点(见图 4 )。利用这些信息,他们提出了加强战斗机上这些精确点的建议。这一分析的问题是,样本排除了没有从战斗任务中返回的飞机。这些飞机会比返回的飞机提供更多有用的信息,因为它们遭受了大面积的 damage⁷.
图 4:幸存者偏差的证据,二战期间,只有返回的战斗机被用来分析飞机上最薄弱点的信息(图片由作者提供)
遗漏变量偏差发生在数据科学家从他们的模型中排除一个或多个特征时。这通常发生在预测机器学*模型中。一个例子是开发了一个模型来预测应该给小麦、大麦和油菜作物施用多少氮肥。当开发该模型的训练集时,仅考虑诸如植物类型、植物种植日期、测量植物产量的日期以及植物发射的紫外光波长之类的特征来训练机器学*算法。已知在大多数监督学*问题上表现良好的 xgboost 模型(梯度增强的树回归模型)产生了模型准确性的负 R 平方(拟合优度统计)值。这里的问题是模型只考虑了涉众想要包含的特性。在对哪些其他特征可以使模型变得更好进行了一些头脑风暴之后,降雨量、阳光量、风速、气温和土壤含量等附加特征被添加到了模型中。这导致了模型精度的显著提高。
混杂偏倚的出现是因为模型中存在混杂因素,即与反应变量和预测变量相关的变量。混杂因素会显著影响模型的准确性。例如,在线性回归模型中,混杂因素会改变回归线的斜率和方向。
模型解释和交流
确认偏差或观察者偏差发生在模型解释阶段,在这个阶段,数据科学家解释或寻找与他们的信念一致的信息。例如,在模型中,数据科学家可能会为他们认为是更好的预测者的特征分配更高的权重,而不是将权重建立在模型结果的基础上,或者他们甚至会排除与他们关于某些特征的假设不一致的数据。
当一个模型的结果在某种程度上偏向于支持一个项目的财务发起人时,就会出现资金偏向。是观察者偏向的一种,就是倾向于看到观察者想看到的东西。
当向业务利益相关者传达机器学*模型时,这是非常常见的情况,这些利益相关者有时不愿意接受模型的结果,因为它不符合他们的期望。例如,客户流失模型的结果表明,最*的营销活动未能有效防止客户离开保险公司,因为它瞄准了错误的客户群体。这个分析没有得到市场部主管的好评。由于客户流失模型的结果不支持营销团队的资金计划,数据科学家被要求尝试不同的技术并探索其他功能,直到实现“可接受的结果”。
因果偏差是数据分析中最常见的偏差类型。这就是相关性被误认为因果关系的时候。一个流行的例子是关于 20 世纪 80 年代的一位学者,他研究了纽约市的犯罪率,发现街头小贩出售的冰淇淋数量和犯罪率之间有很强的相关性。如果得出吃冰淇淋会导致犯罪率上升的结论,那就大错特错了。相反,犯罪率在夏季较高更合理,因为这是冰淇淋销售最高的季节。然而,冰淇淋的销售并没有导致 crime⁸.的增长
模型验证、测试和监控
在模型验证和测试步骤中,对模型输出进行分析。当模型中没有足够的特征时,模型欠拟合就会发生,因此在训练集上表现很差。这些模型具有较小的统计方差(即,模型对特定数据集的敏感性)和较高的统计偏差(即,模型学*数据集的真实底层模式的灵活性有限)。回归、朴素贝叶斯、线性和参数算法容易出现欠拟合。
过度拟合的罪魁祸首是基于树的模型、最*邻算法、非线性模型和非参数模型。当模型可以准确预测训练集中的几乎所有值时,就会发生过度拟合。然而,当涉及到对测试集的预测时,模型就失败了。这表明该模型不能一般化,并且随着新的观察值被添加到数据集,它很可能失败,表明它具有高统计方差但低统计偏差。
数据泄漏发生在训练和测试数据集之间共享信息时。通常,当样本数据集被分为训练和测试时,两者之间不应共享任何数据。例如,预测模型使用历史数据(从 2005 年 1 月到 2021 年 7 月)来预测 2021 年 8 月到 10 月的产品销售。然而,如果训练数据集包括来自用于预测的时间段的观察值,则已经发生了时间数据泄漏。这种类型的偏差在模型验证阶段很容易识别,因为模型可能会输出非常高的模型准确度分数。
如何防止 AI 偏见?
那么,对于 AI 偏见能做些什么呢?以下部分详细介绍了如何在 AI/ML 流水线的每个阶段防止 AI 偏差。图 5 总结了这些信息。
图 5:AI/ML 管道每个阶段的 AI 偏差缓解策略(图片由作者提供)
研究设计和假设制定
当决定在您的研究中包括哪些数据和特征时,请关注研究的设计。目的使样本数据集能够代表目标人群。例如,在墨尔本大都市开展调查以衡量 UberEats 的满意度时,确保考虑到不同年龄组、不同性别、不同文化、语言和教育背景的个人,墨尔本大都市的所有邮政编码,以及回复电子邮件、邮件和社交媒体调查的客户。该调查还应在一天的不同时间和一周的不同日子在线进行,以实现包容性。此外,在一年中多次进行调查是一个好主意,因为客户满意度可能会在一年中发生变化。
下一步是从回答者样本中选择一个随机且有代表性的数据集,以确保每个回答者在研究中被选中的机会均等。这种类型的研究设计消除了抽样、自愿和时间间隔偏差。
数据收集、预处理和探索
如果基本的统计假设得到满足,则可以依赖机器学*算法的输出。对于很多算法,中心极限定理假设要求样本和总体是正态分布的。通常可以用足够大的样本量或变量变换来证实这一假设。因此,如果在数据收集步骤之后,发现样本量太小,建议数据科学家尽可能增加样本量。
为了确定样本大小是否足够大,可以进行功效分析,其提供满足预定统计显著性水平所需的最小样本大小。
在数据预处理步骤中,记录所有数据清理和转换步骤非常重要,这将有助于确定偏差的来源,例如排除某些特征(排除偏差)和不正确的数据标记(标记偏差)。
排除偏差也可以通过在丢弃它们之前调查每个特征来防止。这可以通过能够识别冗余特征的主题专家(SME)的帮助或者通过使用机器学*方法来完成,例如输出可变重要性列表的随机森林。
测量偏差可以通过检查异常值来减少,异常值是异常小或异常大的值,与平均值相差很大,并使用库克距离或马哈拉诺比斯距离等方法计算它们对结果变量的影响程度。
在数据探索阶段,数据科学家可能会发现某项调查中的受访者样本不平衡,因为女性受访者多于男性,从而导致设计偏差。可以通过考虑下采样或过采样方法来平衡数据集。自动做到这一点的 python 包的例子是 SMOTE for over-sampling 或 imblearn。
在数据探索步骤中计算模型中包含的所有变量之间的成对相关性是一种很好的做法,这是一种用于识别多重共线性的统计技术。这将有助于识别导致混杂偏倚的混杂变量。可以设置相关性阈值,以确定从模型中排除哪些特征。
当发现混杂因素时,最好与企业的 SME 联系,以确定这些变量是否应该包含在模型中。避免混淆的一个例子是在开发预测模型的过程中,该模型使用 LNG(液化天然气)工厂中的传感器数据来预测 LNG 产量。在最初的几次模型运行中,模型产生了非常高的模型准确度分数。在模型演示过程中查看主要预测变量后,生产工程师发现了一个混杂变量。液化天然气工厂中的这一变量在液化天然气生产后会发生变化,因此是一个代理因变量。通过从模型中移除这一变量,模型能够生成更可靠的结果。
去除标签偏差可能涉及一些深入的数据探索。例如,几个大学生被用来给植物样品贴上标签,要么是小麦,要么是大麦,要么是油菜。预测模型输出生成了非常低的模型准确度分数。对模型中输入的所有特征进行无监督聚类(k-means 聚类),以确定该算法是否会将小麦、大麦和油菜样本作为单独的聚类进行聚类。模型输出准确地对大部分观察值进行了分类,但是有一些观察值出现在错误的聚类中。这归因于模型中输入的一个特征。该特征和属于特定类的概率之间的关系用于重新标注数据集。
模型开发
在开发良好的模型时,要素选择是关键,因为从数据集中过滤掉不相关或冗余的要素非常重要。这可以防止遗漏变量偏差的发生。一些监督算法具有内置的特征选择,例如正则化回归和随机森林。
如果没有内置的特征选择(例如最*邻),数据科学家可以使用方差阈值。值保持不变的特征应该从模型中排除,因为它们没有足够的方差来解释结果变量。例如,95%的值为 1 的连续变量不太可能有用。同样,所有观察值都属于一个类别的分类变量不会为模型提供信息。
当数据集具有大量特征时,可使用主成分分析(PCA) 将特征数量减少到主成分(特征的线性组合)。但是,这会改变输出要素的比例。主成分也很难解释。
另一种选择是遗传算法(GAs ),它是一种搜索算法,使用变异和交叉(生物学和自然选择原理)来有效地遍历大型解决方案空间;从而有助于从非常高维的数据集中选择特征。与 PCA 不同,GAs 保留了输出特征。
模型解释和交流
当解释一个模型时,对于数据科学家来说,查看从一个模型中得到的所有信息并真实地呈现这些信息是很重要的,即使这些信息可能与业务假设不一致。同样,当交流模型输出时,与涉众进行基于事实的对话是很重要的。
为了确保受众能够理解和信任模型输出,而不是将其视为黑盒,数据科学家应该使用能够提高透明度和模型可解释性的技术。
可以使用全局或局部 explainability⁹.来解释模型全局可解释性显示了模型的高级视图,以及数据中的特性如何共同影响结果。一个例子是使用多元线性回归模型的系数来解释特征的大小和方向,以解释结果变量的可变性。局部可解释性用于一次使用一个特征来解释数据集中的每个观察值。
部分相关图(PDP)提供了一个或两个特征如何影响模型预测值的全局可视化表示,同时保持其他特征不变。这些图有助于确定结果变量和所选特征之间的关系是线性的还是复杂的。PDP 与模型无关。
个体条件期望(ICE)图提供了模型特征相对于结果特征的个体效应的局部显示。与 PDP 不同,ICE 图通过提供样本内单个观察值的可视化表示,用于显示结果变量值对特征值的依赖性的单独预测。像 PDP 一样,ICE 图也是与模型无关的。
累积局部效应(ALE) 图是另一种用于局部解释的图。与 PDP 不同,该方法关注每个特性的小范围值,改变该特性的值,同时保持所有其他特性不变。计算该范围开始和结束时的预测差异。ALE 图是 PDP 的一种更快、偏差更小的替代方法,因为它关注的是单个预测,而不是 PDP 中使用的平均预测。
另一种方法是保留一列(LOCO) ,它在排除一列后重新训练一个模型。然后,它计算每个 LOCO 模型预测得分与原始模型预测得分的差异。如果分数变化很大,则表明对模型很重要的一个特性被遗漏了。
最后,LIME(局部可解释模型不可知解释)是另一种局部可解释性技术,它对一个特性的值执行多重扰动,并测量对输出预测的最终影响。
还有其他一些技术有助于模型的可解释性,比如 Shapley 值、Anchors、DeepLift 和 ProfWeight。高模型可解释性有助于模型输出的解释和交流,从而导致模型透明和信任。
当利益相关者接受了常见机器学*技术及其缺陷的教育后,他们更有可能以批判的方式看待模型输出。为了教育利益相关者,需要在组织内进行变革管理,鼓励企业内的所有员工,从研究生分析师到首席执行官,参加在线学*模块和研讨会,帮助他们进行模型输出评估。
模型验证、测试和监控
在验证和测试模型时,可能会观察到过度拟合。它可以通过在训练集上的非常高的拟合优度统计来识别,但是在测试集上的值很低。通过对训练集和测试集的较差拟合度统计,可以发现拟合不足。
防止过度拟合的一个强有力的预防措施是交叉验证,其中初始训练数据用于生成多个训练测试分裂,然后用于调整模型(即 k 倍交叉验证)。增加样本的大小也有助于将模型的预测推广到现实世界。
用于简化模型的技术被归类为规范化。所使用的方法取决于所使用的算法。修剪决策树是降低复杂度和防止过度拟合的一种方法。惩罚参数可以添加到回归中的成本函数,以缩小系数的值,有助于防止过拟合。稀释是在神经网络的训练过程中随机排除单元(隐藏的和可见的)的过程,也是已知的防止过度拟合的过程。提前停止是防止模型过度拟合的另一种方法,其中模型在运行一定次数后停止,因为额外的迭代不会提高模型精度。这些技术通常作为模型开发中的参数,可以进行超调。
集成学*是一种结合多个模型预测的技术。两种最常见的技术是 bagging,它试图通过并行训练许多“强”学*者(高模型精度模型)并组合他们的预测以提供集合模型的最终预测来减少过拟合。Boosting 旨在通过依次训练大量“弱”学*器来提高简单模型(即,在欠拟合是一个问题的情况下)的预测灵活性,其中弱学*器被定义为受约束的模型(即,在基于树的分类器中树的深度是有限的)。每一个连续的模型都从上一个模型的错误中学*。最后一步是将所有弱学*者合并成一个强学*者。
模型建立后,需要持续进行监控,以确保它们对于正在建模的数据集仍然有效。例如,随着时间的推移,企业可能会停止捕获一些要素或在数据集中包含新的要素。此外,特征的分布可能改变,这将阻止模型预测超出预定值范围的结果。所有这些实例都需要构建新的模型来替换现有的模型,现有的模型在处理任何新数据时都会表现不佳。
有哪些防止 AI 偏差的工具?
有几个现有的工具,包括 Python 包,可以帮助人工智能偏见预防⁰.
Google tensor flow 团队开发的假设工具(WIT)是一个可视化的交互式界面,可以显示数据集和模型。用户无需编写代码即可探索模型结果,并可查看 PDP 等图。
FairML 是另一个有用的工具,它通过量化模型对输入特征的相对预测依赖性来审计预测建模中的各种类型的偏差。然后利用模型特征的相对重要性来评估模型的歧视程度或公平性。
IBM 的 AI Fairness 360 包含一套全面的指标、指标解释和算法,以减轻数据集和模型中的偏差。
微软的 Fairlearn 确定了以分配损害和服务质量损害的形式影响人们的模型偏差。当人工智能系统扩展或保留信息、机会或资源时,就会发生先验。一些用例出现在招聘、学校或大学录取以及银行贷款中。后者指的是一个系统是否对所有人都同样有效,即使没有信息、机会或资源被保留或扩展。这个工具可以用来减轻⁴.的偏见和种族歧视
最后,Aequitas 是另一个著名的开源偏见审计工具包,可用于审计机器学*模型的歧视和偏见。
除了工具包,还有各种用于 AI 偏差检测的 Python 库。在检测人工智能偏差时,特定于算法的 Python 库有公平分类⁵、公平回归⁶和可扩展公平聚类⁷.还有一些 python 包,如偏差检测器⁸和性别偏差⁹,用于检测模型中的性别、民族和种族偏差,这些模型可能由于样本不平衡和缺少目标人群的代表性而存在。
结论
这篇博文指出了 AI/ML 管道中可能出现的几种类型的偏差,以及可以用来减少或消除它们的措施。有现成的工具包可以帮助减轻人工智能偏差,而不是重新发明轮子。然而,重要的是,整个企业的个人,从研究生雇员到 C 级高管,都要接受严格评估模型输出的培训,以使他们能够理解 AI/ML 算法,并质疑它们的有效性。
参考
[1] Dastin,J. (2018)," Amazon scraps secret AI recruiting tool that showed-bias-against women ",https://www . Reuters . com/article/us-Amazon-com-jobs-automation-insight/Amazon-scraps-secret-AI-recruiting-tool-that-showed-bias-against-women-iduscn1 MK 08g
[2] Thomas (2019),“我们如何从 AI 算法中消除偏见?笔测试宣言”,https://fastdatascience . com/how-can-we-elite-bias-from-ai-algorithms-the-pen-testing-manifesto/
[3] BBC 新闻(2019),“苹果'性别歧视'信用卡被美国监管机构调查”,【https://www.bbc.com/news/business-50365609
[4]《卫报》(2020),“英国*一半的议会使用算法来帮助做出索赔决定”,https://www . The Guardian . com/society/2020/oct/28/英国*一半的议会使用算法来帮助做出索赔决定
[5] Jones,S (2021),“自动化招聘系统正在对招聘人员‘隐藏’候选人——我们如何才能阻止这种情况?”,https://www . weforum . org/agenda/2021/09/人工智能-工作-招聘-流程/
[6] Simonite,T (2018),“当涉及到大猩猩时,谷歌照片仍然是盲目的”,https://www . wired . com/story/When-It-to-Gorillas-Google-Photos-Remains-Blind/
[7] Agarwal,R (2020),"数据科学中的五种认知偏见(以及如何避免这些偏见)",https://towardsdatascience . com/Five-Cognitive-bias-In-Data-Science-And-how-to-avoid-them-2bf 17459 b041
[8] Agarwal,R (2020),"数据科学中的五种认知偏差以及如何避免它们",https://towards data science . com/Five-cognitive-bias-in-data-science-and-how-to-avoid-them-2bf 17459 b041
[9] Onose (2021),“ML 中的可解释性和可审计性:定义、技术和工具”,https://Neptune . ai/blog/explability-Auditability-ML-Definitions-Techniques-Tools
[10]“每个数据科学家都应该知道的最基本的 Python 公平库”,https://techairesearch . com/Most-Essential-Python-Fairness-Libraries-Every-Data-Scientist-Should-Know/
[11] 假设分析工具,https://pair-code.github.io/what-if-tool/
[12] Adebayo,Julius,“FairML:预测建模偏差诊断工具箱”,https://dspace.mit.edu/handle/1721.1/108212
[13]艾公平 360,
[14]费尔勒恩,https://github.com/fairlearn/fairlearn
[15]公平分类,https://github.com/mbilalzafar/fair-classification
[16]公平回归,https://github.com/jkomiyama/fairregresion
[17]可扩展公平聚类,https://github.com/talwagner/fair_clustering
[18]https://pypi.org/project/bias-detector/偏差检测器
[19]性别偏见,【https://github.com/gender-bias/gender-bias】T4
ELO:新的葡萄酒评级系统
探索星级评定系统的替代方案
无论是优步、亚马逊还是谷歌,如今我们到处都可以看到星级评定。尽管它们很简单,但却存在一系列问题。举几个例子:极端观点在平均评级中的过度表现,不同用户对什么构成好/坏的不同标准,以及现有评级的锚定效应。
Vivino 平台上的葡萄酒评级使用了相同的星级逻辑。在这篇文章中,我们将探索是否可以借鉴国际象棋游戏来为葡萄酒提供一种新的评级方式:一种用于评估国际象棋选手相对实力的 ELO 分数的变体。
你可以在这里找到所有相关代码的详细分析。
葡萄酒的星级评定
为了展示这一新评级系统的潜力,我们将使用由荷兰 2500 名 Vivino 用户的约 100 万条葡萄酒评论组成的数据集。
原始数据集,NL Vivino 用户对大约 100 万种葡萄酒的评级
仔细看看每个评论者的平均星级开始揭示这个系统的一些固有问题:最重要的是,评论者不同地应用评级尺度。
在我们的数据集中,10%最挑剔的评论家给葡萄酒的平均分数低于 3.3/5 分。10%最慷慨的评论者平均给出 4.0/5 分以上。
虽然更挑剔的评论家可能只是喝了更差的酒,但这不太可能解释这里的全部差异。有理由认为,有些人只是简单地给予葡萄酒较低或较高的平均评级。这对葡萄酒的平均评级也有影响。如果一款葡萄酒被慷慨的个人评级,那么它会比被更挑剔的用户评级得到更高的分数。
关于 ELO 的一个简短说明
Arpad Elo 的图片,公共领域
ELO 记分法最初是由匈牙利棋手和物理学家阿帕德·埃洛发明的。几十年来,他开发的评分系统一直被用于模拟零和游戏中的技能水平,最著名的是国际象棋。基础知识:
两个国际象棋选手之间的评分差异可以作为谁更有可能赢得比赛的指标。每场比赛都有三种可能的结果:赢、平或输。比赛结束后,双方球员的分数会根据实际结果和预期结果进行调整。玩家通常会参加由几场比赛组成的比赛。在这里,分数更新通常在锦标赛进行之后进行,而不是在每场单独的比赛之后进行。
ELO 评级系统有多种实施方式,每种方式的参数都略有不同。出于我们这里练*的目的,我们使用美国国际象棋联合会(USCF)使用的最新 (2021)规则,在国际象棋比赛包中实现。
重要的是,未分级的玩家(即,尚未参加任何锦标赛)初始的 ELO 分数为 1300,在 USCF 分级体系中,任何 26 岁以上的成年人都是如此。
从星级到 ELO
我们可以通过将一个评论家在某一天的个人评分框定为一场零和比赛,然后计算每种葡萄酒的 ELO 分数,来规避困扰星级评定系统的许多问题。为此,我们提出以下定义:
- 玩家:来自特定年份的特定葡萄酒
- 匹配:由特定的个人在特定的一天对两种葡萄酒进行面对面的零和比较
- 锦标赛:特定个人在特定日期进行的面对面比赛的集合
上面的结构依赖于一个基本假设,即我们可以相信一个人在给定的一天对葡萄酒进行评级的方式是非常一致的。如果他们在某一天给一种酒 4 星,给另一种酒 5 星,我们可以推断出 5 星的酒比 4 星的好。
一个假设的例子来说明:
这三场面对面的比赛可以组合在一起参加锦标赛。我们将按时间顺序进行这些比赛。尚未参加锦标赛的葡萄酒(玩家)将获得新初始化的 ELO 分数(1300),而过去参加过锦标赛的葡萄酒将以其最*的 ELO 分数进入新的锦标赛。
当我们举办每场锦标赛时,我们会将每场比赛的信息存储在一个查找表中。这将允许我们研究葡萄酒 ELO 分数如何随着时间的推移而演变。
为了更好地管理我们可能在 ELO 分数中看到的一些波动,我们将另外计算移动平均(MA) ELO 分数,具有 5 场比赛的跟踪窗口。这将使我们能够消除个别比赛对任何一种葡萄酒的影响,同时保留葡萄酒分数随时间上升或下降的能力。
结果
让我们来看看结果吧!在将我们的葡萄酒评级建模为赢/平/输并如上所述运行锦标赛后,我们可以根据马 ELO 分数计算排名靠前的葡萄酒。为了减少小样本葡萄酒的影响,我们已经剔除了所有参加少于 10 场比赛或少于 25 场比赛的葡萄酒(根据美国国际象棋联合会关于什么构成“既定”玩家评级的指导方针)。
10 大葡萄酒由马评分
丁丁!我们有一个赢家:2017 年南非斯泰伦博斯的 DeMorgenzon 的陈宁勃朗葡萄酒。这款葡萄酒已经参加了 22 场有效的比赛,并与其他 180 款葡萄酒竞争。它的移动平均 ELO 目前是 2513,远远高于我们数据中的其他葡萄酒。
我们可以仔细看看这个 ELO 分数是如何计算的,以及它是如何随着时间的推移而演变的:
绿色 Chenin Blanc 获胜|黄色=平局|红色=失败
上图中的黑点显示了我们的 DeMorgenzon Chenin Blanc 在 22 场比赛中的 ELO 分数。彩色圆点表示与之竞争的葡萄酒的 ELO 分数,圆点的颜色表示面对面比赛的结果:(绿色=赢,黄色=平,红色=输)。
在锦标赛 1 中,它被初始化为 ELO 1300,这是所有未评级葡萄酒的情况。绿点表示它赢得了那场比赛的所有比赛,并且它的分数随后增加到大约 2100 分。
让我们仔细看看它参加的第二届锦标赛,以便更深入地了解星级和 ELO 之间的动态关系。下面是锦标赛 2 的参与者,以及他们的星级和初始 ELO 分数(他们和 ELO 一起参加锦标赛)。
我们的 DeMorgenzon Chenin Blanc 以 2126 的 ELO 成绩参加了第二届锦标赛。它面对的是 10 种其他的葡萄酒,从 ELO 的 1222 年到 2283 年不等。它获得了 4.5 的星级,高于比赛中其他任何一款葡萄酒。这意味着 10 场全胜,ELO 又一次跃升至 2683 场。
这里的评级跳升(+553)很高,有几个具体原因——首先,我们的获奖葡萄酒进入锦标赛之前只参加了 11 场比赛(锦标赛 1),并赢得了所有这些比赛。由于样本量小,加上连续获胜,根据美国国际象棋联合会的规范,ELO 算法使用“特殊评级”公式来计算评级调整。这种特殊的评级公式会导致更高的潜在跳跃,因为它用于玩家评级存在更多不确定性的情况。跳跃如此之大的第二个原因是因为我们的 Chenin Blanc 击败了 2017 年的 di Lenardo Thanks Bianco,这是一款当时 ELO 得分为 2283 的葡萄酒。击败比自己强大得多的玩家可以在 ELO 获得更大的飞跃。
在我们继续之前,让我们看看负责锦标赛 2 的评审。我们的获奖葡萄酒被评为 4.5 星——但对于这位评论家来说,这有多特别呢?
该输出表明锦标赛 2 中的评论者(总共 1100+评级)通常比我们数据集中的大多数其他评论者更悲观。他们的评分只有 2%是 4.5 星或以上。相比之下,当查看我们的完整数据集中的约 100 万条评论时,我们可以看到 13%的葡萄酒评级为 4.5 星或以上。锦标赛 2 评论者在所有评论中给出的平均评级进一步支持了这一论点:3.4 颗星。回顾我们在本笔记本前面的审核者分布,此人属于 20%最关键的审核者。
我们可以得出结论,星级系统并没有真正公正地评价我们的 Chenin Blanc 在锦标赛 2 中的评价。另一方面,我们的 ELO 分数能够控制审核者的“严格性”,因此,由于这种出色的表现,分数大幅提升(在这种情况下为+553)。
我们的 ELO 评分还控制了个体评审者随时间应用评级的差异(变得更宽松/更严格)。它只要求评论者在同一天与他们的葡萄酒评级保持一致。与葡萄酒整个生命周期的平均星级相比,ELO 能够更好地捕捉葡萄酒质量随时间的变化。随着葡萄酒的陈年和变得更好/更坏,它的 ELO 将会进化来反映这一点。
看来我们的 ELO 评分确实解决了困扰星级评定的一些基本问题。那么应该怎么用呢?让我们后退一步,看看我们数据集中所有葡萄酒的 ELO 和星级之间的差异。
我们可以看到有(不出所料!)平均星级和移动平均 ELO 之间有很强的正相关性。五星评级较高的葡萄酒通常有较高的马 ELO 评分。
我们可以用一条最佳拟合线(绿线)来表示哪些葡萄酒的表现超过了它们的平均星级。这条线右边的葡萄酒的马 ELO 分数比它们的平均 5 分等级要高。这些葡萄酒可能会因简单的平均星级而服务不足,这可能会掩盖其相对于平台上其他葡萄酒的质量。2017 年的 DeMorgenzon Chenin Blanc 就是一个最好的例子:上图中最右边的点,MA ELO 远远高于其星级。
再举几个例子:
- 2018 年来自葡萄牙 Dao 的 Cadiz 的 Dao Reserva Branco:2221 的马 ELO,平均星级 3.9
- 2018 克林根堡 sptburgbunder by Weingut Stadt 克林根堡来自德国弗兰肯:1866 年的马,平均星级 3.4
- 2015 Serego Alighieri Poderi del Bello Ovile Toscana 来自意大利托斯卡纳的马西:1773 年的马 ELO,平均星级 3.5
总结想法
在我们取消星级评定之前,先给你提个醒。
星级系统很容易理解。对于普通用户来说,ELO 会不那么直观。它也有 5 分等级量表所没有的缺点。重要的是,ELO 对 1 星和 5 星的评价与 3 星和 3.5 星的评价没有任何不同。偏好的大小被零和更简单的方案所取代。
尽管如此,我们还是证明了 ELO 提供的价值是普通五星评级所不能提供的。以上是展示这一指标潜力的探索性练*,但只是开始触及皮毛。使用的样本量(荷兰前 2500 名 Vivino 用户的葡萄酒评论)非常小。有效锦标赛的定义(由同一用户在同一天对葡萄酒进行评级)也非常严格,可以放宽,以允许更多葡萄酒之间的直接比较。随着更多的数据和一些潜在假设的微调,ELO 分数将变得更加稳健和可靠。我们可能还想开发一种 ELO 算法,其参数是专门为葡萄酒评级而构建的,而不是使用国际象棋世界中实现的精确系统。
那么现在这样如何:除了平均星级之外,让我们使用 ELO 。并在此过程中发现一些隐藏的宝石,如 2017 年德莫根松储备白诗宁!
电子邮件附件摄取
AWS 中的电子邮件附件自动化工作流
我一直在和另一位媒体作家本·奥尔尼谈论电子邮件附件自动化。处理电子邮件附件并将数据存储在数据库中的操作。我想看看我是否能让它在亚马逊 AWS 上工作。
我在 Medium 上发现了一篇非常棒的文章,描述了如何将电子邮件附件放入亚马逊 S3 网站。这是一篇写得非常好的文章,非常清楚地描述了这个过程。我认为只有几个部分可以更详细地介绍,比如亚马逊 IAM 中的角色,可能还有更多关于安全性的内容。
上面这篇文章详细解释了推理和过程。由于显而易见的原因,我不打算再谈论它了。我将提供一个快速总结和一些改进,然后描述下一部分。
在亚马逊 S3中存储原始附件很棒,但这只是其中的一半。下一部分是如何在亚马逊 RDS 中处理和存储这些附件。这部分有点棘手,尤其是如果你想以一种安全的方式来做这件事。我设法让它工作,我将按照我认为最有意义的顺序告诉你每一步。我将在前进到下一部分之前,包括有用的验证,以确认每个“构建块”的工作。
这是我典型的分阶段设计解决方案的方式。这是一个相当复杂的端到端工作流程,因此确保每个步骤都能够独立工作非常重要,否则您会在结束时发现它不起作用,然后故障排除会变得更加复杂。
步骤 1:亚马逊 AWS IAM 角色
我喜欢在开始时创建服务角色。你在哪个阶段这样做并不重要,但我个人喜欢在需要的时候准备好。
登录 AWS IAM ,从菜单中选择“角色,然后选择“创建角色”。
附加以下内置策略:
- CloudWatchFullAccess
- AmazonRDSFullAccess
- AmazonRDSDataFullAccess
- 亚马逊 3 完全访问
- AWSLambdaVPCAccessExecutionRole
- awslambdabasiceexecutionrole
这是一个非常宽松的角色,允许 Lambda 访问 CloudWatch、RDS、VPC 和 S3。它可以被多个项目使用。如果您在生产环境中部署这个解决方案,您将希望创建一个更具限制性的策略,只允许 Lambda 访问需要的内容。
点击“下一步:标签,然后点击“下一步:回顾”。
对于“角色名”,调用它的RoleForLambdaToAWSServices,然后“创建角色”。
步骤 2:创建我们的 S3 存储桶
正如我上面提到的,我们需要两个 S3 水桶。一个用于传入的原始电子邮件和附件,另一个用于将附件移动到 Lambda 获取的 RDS 中。
你可以给这些桶起任何你喜欢的名字,但是我建议这样:
- my bucket-附件
- my bucket-已处理
“ mybucket ”应该是对你的描述。S3 存储桶需要是全球唯一的。
请登录 AWS S3 并立即创建您的两个桶。
第三步:创建我们的专用邮件服务器
为此,我们将使用 AWS 工作邮件。这样做有很好的成本效益原因,如果你想知道背后的原因,请参考原文。
点击创建组织。
在组织设置下,您可以选择合适的选项。我选择了“免费测试域”(这个可以以后改)。对于“别名”,为您的组织/项目命名。例如“ myorg ”,然后点击“创建组织”。
创建完成后,打开你的新组织邮件服务器,点击“用户”。您将看到一个默认的“管理员”帐户,您无法删除该帐户。您需要添加一个新用户。
也许是这样的:
- 用户名:代理
- 名字:MyOrg
- 姓氏:代理人
- 显示名称:MyOrg 代理
这将创建一个新的电子邮件地址,“agent@myorg.awsapps.com”。这将是您发送带有处理附件的电子邮件的电子邮件地址。出于本教程的目的,它将接受来自任何人的带有任何附件的电子邮件。这带来了安全风险。一旦你做到了这一点,我建议添加过滤功能,只允许“T2”白名单中的“T3”被允许的发件人,也许还可以过滤一些附件的内容。这真的很重要,所以以后不要忽视它。
有几种方法可以做到这一点。在工作邮件服务器上过滤,通过登录“https://mapi.awsapps.com/mail”【agent@myorg.awsapps.com】账户过滤(用你的组织名称替换“ myorg ”),或者在我们不久将讨论的 Lambda 函数中过滤。我会说 Lambda 函数将是我的首选方法,因为它给了我们最多的控制,但也许你会想做一个组合过滤。
亚马逊工作邮件每个用户每月 4.00 美元,包括每个用户 50 GB 的邮箱存储。您可以开始为多达 25 个用户提供 30 天的免费试用。
第四步:亚马逊简单电子邮件服务(SES)
我们现在有了由 AWS WorkMail 提供的邮件服务器。通过部署 AWS 工作邮件, AWS SES 将自动配置为处理域的传入邮件。
我们想在这里做一些有趣的事情,那就是添加一个电子邮件接收“规则集”来发送接收到的电子邮件的副本,并将其存储在我们的 S3 桶中。正如我之前提到的,可能类似于“ mybucket-attachments ”。如果您还没有这样做,请登录 AWS S3 并使用默认选项创建您的两个空桶。
点击 AWS SES 中邮件接收下的规则集。
点击查看活动规则集按钮。
在位置 1 应该有一个现有的规则,这就是处理 AWS 工作邮件的传入电子邮件。我们想通过点击“创建规则来创建一个新的规则。
收件人应该是您的“代理人”电子邮件……例如“agent@myorg.awsapps.com”。点击添加收件人,然后下一步。
****添加动作应为“ S3 ”:
- S3 铲斗:我的铲斗-附件(或者你管它叫什么)
点击下一步。
您希望为您的规则命名,例如“myorg-email-to-s3-bucket ”。其他一切保持默认。
点击“下一步,然后“创建规则”。
如果一切按计划进行,你应该有两条规则。一个用于处理来自 AWS 工作邮件的入站电子邮件,在位置 2 新创建的规则用于处理将原始电子邮件复制到 S3 存储桶。
第五步:确认它工作到这一点!
给你的新邮箱地址“agent@myorg.awsapps.com”发送一封带有文本文件附件的邮件(这也适用于二进制附件,但用简单的 CSV 或文本文件更容易演示)。目前,邮件内容和附件内容并不重要。只是一些小而轻的东西来测试这个。
如果您登录 webmail,“https://myorg.awsapps.com/mail”并确认您在“代理人”邮箱中收到了该邮件。假设你也称你的用户为代理。
步骤 6:使用 Lambda 向 S3 发送工作邮件
第一步是打开 AWS Lambda 控制台,点击创建功能。通过 Scratch 选择Author,给你的函数起一个名字(比如 email-to-s3-bucket ),选择 Python 3.8 运行时。这是撰写本文时可用的最高 Python 版本。
展开“更改默认执行角色,选择“使用现有角色”,选择我们在开始时创建的角色。我把我的命名为“RoleForLambdaToAWSServices”。
点击创建功能。
在“代码源下,编辑“ lambda_function.py ”文件,替换为下面的代码。
我在两个地方修改了原始作者代码。我们没有将处理后的电子邮件保存在“ mybucket-attachments ”中,而是保存在我们的第二个 S3 存储桶“ mybucket-processed ”中。请确保将下面的“ mybucket-processed ”替换为您的目标 S3 铲斗。这一部分很重要,因为这将触发事件,以便稍后将 CSV 的内容摄入 RDS。
s3.upload_file('/tmp/attach.csv', '**mybucket-processed**', 'attach-upload-' + timestamp + '.csv')
我还加入了一个发件人白名单。这很重要,因为你不想处理来自每个人的邮件。
# sender whitelist
if fromAddress not in ['[sender@domain.com](mailto:sender@domain.com)']:
raise PermissionError(f'{fromAddress} not in whitelist!')
只需将“sender@domain.com”替换为允许的发件人电子邮件地址列表。你可以删去这三行,但这是不可取的。事实上,在生产环境中,我也会对附件进行一些检查,以确认它们看起来是合法的。
Sandeep Madamanchi的原始代码
当你完成后,确保你的部署。
步骤 7:配置 AWS S3 来使用我们新的 Lambda 函数
打开 AWS S3 控制台,点击“铲斗,打开我们的“ mybucket-attachments ”铲斗。
点击“属性,向下滚动到“事件通知,点击“创建事件通知”。
- 事件名称:"创建对象"
- 事件类型:"放","贴"
- Lambda 函数:"电子邮件转 s3 桶"
点击保存修改****
第八步:我们来测试一下!
从 Lambda 函数中的白名单地址发送一封电子邮件,附件为“agent@myorg.awsapps.com”。我推荐像基本 CSV 这样的小东西,只是为了验证它的工作。给它一个测试对象和身体,让这个变得有趣。
- 如果你登录,“https://myorg.awsapps.com/mail”你应该会看到你的邮件。
- 打开 AWS CloudWatch ,然后打开日志,然后打开日志组。您应该看到 Lambda 函数的日志文件,“email-to-s3-bucket ”。打开它,确认你看到了你的电子邮件被接收和处理的日志。
- 打开你的 AWS S3 桶。您应该在“ mybucket-attachments 中看到原始电子邮件,在“ mybucket-processed 中看到电子邮件附件。
如果这三项看起来都没问题,那么到目前为止你做得很好。我的已经工作了,我在我的“my bucket-processed”S3 桶中看到“attach-upload-2021–8–23–1–59 . CSV”。如果我打开附件,我可以看到我用电子邮件发送的 CSV 附件的内容。
我还尝试从一个不在白名单上的地址发送附件,我可以在 CloudWatch 日志中看到它被拒绝,因为发件人地址不在白名单上。我还确认了被拒绝的电子邮件没有保存在 S3 的任何一个桶中。
步骤 9:准备 RDS 数据库安全组/访问列表
虽然您可以在创建 RDS 数据库实例时创建安全组,但我建议您提前创建。
打开 AWS VPC 控制台,点击“您的 VPCs** ”。请记下“ IPv4 CIDR ”。在我的例子中是“ 172.31.0.0/16 ”。**
打开 AWS EC2 控制台,点击网络&安全下的安全组。点击创建安全组。
**基本详情:
安全组名称:acl-rds-mysql-in
描述:acl-rds-mysql-in
**入站规则("添加规则 1"): *类型:MySQL/Aurora
来源:MySQL
目的地:MyIP
**入站规则("添加规则 2"): *类型:MySQL/Aurora
*来源:MySQL
*目的地:自定义
- 172.31.0.0/16 ← 添加您的 VPC 子网**
点击创建安全组****
步骤 10:部署 RDS MySQL 实例
打开 AWS RDS 控制台,点击创建数据库。部署数据库时有许多配置选项,所以如果我没有提到什么,请假设您将配置选项保留为默认选项。
*关闭数据库创建方法: 标准创建
**引擎选项:
*引擎类型:MySQL
模板
自由层*
**设置:
*数据库集群标识符:附件
主用户名:管理员
检查:自动生成密码
**DB 实例类:
- db.t2.micro**
**连接:
*虚拟私有云(VPC):默认 VPC
*公共访问:是(MySQL 工作台所需)
- VPC 安全组:选择现有的
—现有的 VPC 安全组:acl-rds-mysql-in(我们在步骤 9 中创建的)
—删除“默认”
可用性区域:无首选项*
*数据库认证:
选择:密码认证
**附加配置:
*初始数据库名称:附件
*取消选中:启用自动备份
取消选中:启用增强监控
取消选中:启用删除保护
点击创建数据库****
创建数据库需要几分钟时间。如果您如上所述选择了“自动生成密码”,创建后会弹出一个对话框,提示“查看连接详情”。你需要注意这一点。如果您愿意,也可以在创建表单中设置自己的密码。
步骤 11:下载并安装 MySQL Workbench
下载并安装 MySQL 工作台。这是一个免费的 MySQL 桌面客户端,可以让你连接到你的数据库。安装后,您需要创建一个新的连接,并从上面的“查看连接详细信息”中输入详细信息。
作者图片
步骤 12:为 Lambda 准备依赖关系
我们将使用 Python 库" pymysql "从 Lambda 访问我们的 RDS 实例。“ pymysql ”不附带 Lambda,需要手动上传。
我用的是 Mac 电脑,但对于其他操作系统来说,这个过程是相似的。
~ % mkdir lambda_tmp
~ % cd lambda_tmp
lambda_tmp % pip install -t . pymysql
lambda_tmp % zip -r lambda.zip .
我们将把 lambda.zip 上传到我们的 lambda 函数中。完成此操作后,您可以删除此本地临时目录。
步骤 13:为 RDS 摄取创建 AWS Lambda 函数
下一步是打开 AWS Lambda 控制台,点击创建功能。选择“ Author by Scratch ,给你的函数起一个名字(例如“ s3-bucket-to-rds ”),选择“ Python 3.8 运行时。这是撰写本文时可用的最高 Python 版本。
展开“更改默认执行角色”,选择“使用现有角色”,选择我们开始创建的角色。我把我的命名为“RoleForLambdaToAWSServices”。
展开“高级设置,在“网络下选择您默认的 VPC。我们这样做的原因是,出于安全原因,我们的 RDS MySQL 安全组只允许来自 VPC 内部的连接。如果我们不这样做,Lambda 函数将无法访问我们的 RDS 实例。在子网下,选择所有与 VPC 相关的子网。对于“安全组,选择默认的 VPC 安全组。它应该允许任何东西进出 Lambda。
点击创建功能。
在“代码源”部分,点击“从上传”并上传上面创建的“ lambda.zip ”。然后在项目根目录下创建一个名为“ lambda_function.py 的新文件。我们稍后会添加一些内容,但它应该是这样的。
作者图片
点击顶部的“ +添加触发器,选择“ S3 ”。
- 存储桶: mybucket-processed (这是 S3 存储桶,附件在被接收和处理后被移动到该存储桶)
- 事件类型:所有对象创建事件****
- 后缀:。csv** (每个附件文件类型添加一个触发器)**
现在它要做的是,每当一个新对象被添加到我们的“my bucket-processed”bucket 时,它将调用这个 Lambda 函数。我们现在要做的是添加一些代码,将附件的内容接收到 RDS MySQL 中。
打开我们创建的“ lambda_function.py ”,添加下面的代码。请确保在第 6 行更新可用区域。我用的是" eu-west-1 "但是你可能用的是别的。第 22–25 行是您的 RDS MySQL 数据库凭证。你需要在这里添加你自己的。
6 号线其实真的很重要。你可能没见过 boto3 客户端是这样写的。之所以用这种扩展的形式来写,是因为我已经用 VPC(“专用网络”)保护了 Lambda、RDS 和 S3 之间的连接。默认情况下,所有这些通信将通过互联网,这是不确定的。事实上,你在网上找到的所有指南,包括这篇文章,都是以“快速而肮脏”的方式完成的。您将您的解决方案暴露于外部互联网的风险很大。通过虚拟专用网络建立所有连接有点复杂,但值得一试。
当你把你的 Lambda 函数添加到 VPC 时,你会注意到你的 Lambda 函数不再能够访问 S3。您需要在 VPC 中添加一个 S3 端点。使用“ boto3 时,S3 网址默认为虚拟。因为它需要互联网接入来解析到区域特定的 URL。这会导致 Lambda 函数挂起,直到超时。
解决这个问题的方法是在 VPC 创建一个 S3 端点。打开 AWS VPC 控制台。打开“虚拟私有云下的“端点”。单击“创建端点”。您需要添加 S3 网关,如下所示。
作者图片
第十四步:都做好了!
这一切都应该工作,但让我们给这个测试和验证的方式。
准备
- 清空你的两个 S3 桶。
- 清空您的两个 CloudWatch 日志组中的日志。
- 为自己创建一个 CSV 文件(我将自己的文件命名为“ input.csv ”)
作者图片
测试
发邮件给你的“代理人”工作邮箱地址,附上“ input.csv ”即可。除非你想,否则不需要添加主题或正文。
以“代理人”的身份登录您的工作邮件网络邮件门户,并确认您看到了该邮件。
打开 CloudWatch 日志组,打开 S3 中与接收邮件相关的日志组并保存。
作者图片
我的已经到了,目前处理的还不错。
前往 S3 并打开您的附件** 铲斗。您配置的接收电子邮件的桶。如果一切按计划进行,您应该会在桶中看到原始电子邮件。**
作者图片
现在,Lambda 函数会将附件复制到您的 已处理的 桶中。如果您打开* processed* bucket,它实际上应该是空的,因为 RDS lambda 函数会在附件被处理后将其删除。在这一点上,您可以做的一个测试是查看桶,确保那里没有未处理的附件。如果你这么做了,那么 Lambda 函数中有东西坏了。**
如果您有任何与 S3 和 Lambda 集成相关的问题,最好看看 Lambda 函数是否与您的存储桶相关联。你这样做的方法是打开你的 S3 桶,转到“属性,向下滚动到“事件通知,你应该看到那里的事件和相关的 Lambda 函数。如果你没有看到一个 Lambda 函数与你的两个 S3 桶相关联,那么你在某个地方犯了一个错误。
打开 CloudWatch 日志组,打开将附件从 S3 存储到 RDS 的相关日志组。
作者图片
太好了,这说明第二个 Lambda 函数已经被触发了。让我们看看日志的内容。
作者图片
这看起来都处理得很好,但是让我们在 MySQL Workbench 中确认一下。
作者图片
太神奇了!
这显示了接收带有附件的电子邮件并将内容存储在数据库中的完整的端到端自动化工作流。很酷的东西。
感谢本·奥尔尼和我一起集思广益。他有一些很棒的文章,建议你去看看。
他最*的一部真的很有趣…
迈克尔·惠特尔
尴尬浅薄的自动编码器(容易)的建议
不带褶边的建议
安妮·斯普拉特在 Unsplash 上的照片
没有荷叶边的推荐
帖子摘要
- 了解一个新的( ish )最先进的推荐引擎 EASE
- 一步一步地查看总共 5 行代码
- 理解其工作原理背后的直觉
- 发现使用它的一些附带好处(速度、多样性等)
- 查看我的 PyTorch repo 中的完整实现
背景
我总是喜欢学*既简单又有效的推荐系统。我经常听到关于复杂引擎的故事,这些引擎有如此多的移动部件,和/或训练起来非常昂贵,然后在生产环境中出现故障。我经常在 PapersWithCode.com 排行榜上看到的两位作者是斯特芬·伦德尔(谷歌)和哈拉尔德·斯特克(网飞)。他们研究的惊人之处是什么?
- 易于理解的简单模型
- 实施很快
- 只需要用户+项目交互作为特征
- 两人都有 SOTA 的表演
当我看到一个新的深度学*架构时,我经常会为跳上复杂性的潮流而感到内疚。存在于你感兴趣的任何领域的“前沿”可能是有趣和令人兴奋的,增加了一层又一层的复杂性。然而,这些真的能产生最好的结果吗?
如果你读过我的基于图表的推荐文章,你可能已经注意到我引用了 Dacrema 等人的一篇伟大论文。(2019).在许多领域/数据集中,最好的算法可能是简单的方法:
- 矩阵分解
- 基于流行度
- 项目或用户相似性
让我们拥抱奥卡姆剃刀,坚持使用那些能产生伟大结果的算法,而不去理会其他所有的废话。这就是轻松发挥作用的地方。它本质上是一个项目-项目相似性模型,但是规定我们强制一个项目不能与其自身相似。这使得模型更加一般化,并且可以产生既有趣又多样的结果。
例子
我是在浏览PapersWithCode.com的时候发现安逸的。在百万首歌曲数据库上,这是迄今为止最好的算法,在许多其他数据集上也是前 5 名。实现是用 Numpy 编写的,虽然它运行得很好,但我想用 PyTorch 加快一点速度。因此,我在尝试了解它是如何工作的同时,对它进行了重构,亲爱的读者,你可以在这个旅程中跟随我。
在我们尝试在真实数据集上运行它之前,让我们制作一个虚拟示例来看看这一切是如何工作的。我们可以制作几个用户+项目(评级可选)对,并将它们存储在一个数据帧中。对于每个用户和项目,我们将使用整数令牌来标识它。这将是我们的 X 矩阵。
现在,我们可以将它存储为一个稠密的 PyTorch 张量,但在实际应用中可能会非常稀疏。相反,我们将把它保持为一个稀疏张量,直到我们需要它:
从这里,我们需要构建我们的 B 矩阵,这是我们的项目到项目权重矩阵,它将是我们预测的来源:
P(user, item) = X[user]*B[item]
步骤
案例文件解释了形成此封闭形式解决方案所需的所有步骤,但以下是基本步骤:
- 构建 Gram 矩阵:G = X.t() * X
- 沿着 G 的对角线添加一个正则化值(λ)
- P = G.inverse()
- b =-P/对角线(P)
- 将 B 的对角线设为 0
我个人认为,当试图理解这些步骤在做什么时,更容易看到这些步骤:
第一步
第二步
第三步
步骤 4、5
这是我们的重量矩阵!现在,我们只需将包含给定用户所有交互的任何向量乘以【B】, ,输出将是预测的评分列表。
这些预测应该有一些直观的意义。高分被分配给用户已经交互过的项目。第 4 项没有被交互,具有下一个最大的分数。应该推荐!我们可以通过将我们的整个 X 矩阵乘以 B ( sparse.to_dense()@B )来获得整个预测集,从而推荐一组用户。
直觉
我认为矩阵乘法的步骤是这样的:
- 作为用户,您已经与一些项目进行了交互
- 对于这些项目中的每一个,在 B 的列中存储有许多类似的项目
- 每个返回的项目推荐都基于它与您已经交互过的其他项目的相似程度
- 可视化这些矩阵乘法的最佳方式可能是使用http://matrixmultiplication.xyz/。
这里我用的术语相似是广义的:黑客帝国和瓦力在体裁、语气等方面可能不太相似。,但是有可能这两个项目通常都被相似的用户观看,这是加权的同现。如果你喜欢 Wall-E,Wall-Ewatchers 观看的其他项目可能也与你相关。可能吧。
现在,我们可以检查我们的预测分数,为每个用户选择 TopK 项,然后返回这些项的名称。简单!我们甚至一次都不用梯度下降或链式法则。
除了获得 SOTA 结果有多简单之外,我们使用 EASE 这样的方法还有什么其他原因呢?这篇论文的作者提出了这样的理由:
- 要调整的单个超参数(λ),这似乎对多样性或性能有微小的影响
- 大多数学术数据集的训练只需几分钟,而不是几小时/几天
- 推荐更多不常见的独特物品
对我来说,我个人喜欢被推荐一些我会喜欢的稀有和有趣的东西,而不是我可能已经知道的流行的东西。发现奇妙事物的新奇和快乐是一种独特的满足感。
对于那些可能会问“为什么这是一个自动编码器?这里哪来的密层?”所以这才是浅 AE!不需要密集层来压缩用户向量。相反,我们为输出层存储 B 的权重。
那这个为什么会尴尬?我认为这里唯一的尴尬是这样一个事实:如此简单的一组变换仍然如此强大,足以成为 SOTA,而复杂的神经网络和集成却落后了。
评级
EASE 还可以处理明确的用户反馈(例如,五星评级)来进行预测。我还没有完全测试那部分代码,但是理论上应该可以!还有更多的工作要做。
指标
通常对于这些类型的系统,我喜欢使用 HitRate@K 作为我选择的度量标准。很简单理解(predicted[《1984》《攻面》《*卫!卫兵!”]与 actual[《1984》]一炮而红。此外,它非常适合大多数业务用例。
作为测试用例,我抓取了 GoodReads 数据集,过滤了英文书籍,并在 500 万本书的评分上训练了 EASE ( lambda = 250.0 )。然后,我对 10 万用户进行了 20 次预测,并对结果进行了比较:20 次预测的命中率为 9.1%!不算太坏。如果我只是预测 GoodReads 数据上最受欢迎的书籍,我只会得到 6%,所以这是一个明显的改善和一些个性化。整个管道运行不到一分钟!
结论
我鼓励你们都来看看我的回购这里,以及最初的 Numpy 版本这里。论文本身就值得一读,因为它短小精悍。Tuan 也刚刚发表了一篇关于其他简单推荐系统的文章,你可能想看看。
使用 Plotly 和 Chartstudio 和 Python 在媒体/网站中嵌入交互式图表
数据科学|数据可视化
使用 Plotly 和 Chartstudio 将您的交互式绘图嵌入媒体、网页和其他平台
没有所谓的信息过载。只有糟糕的设计。—爱德华·塔夫特
我们都知道一张图片胜过千言万语 ,数据可视化是对信息的可视化总结,使其更容易理解/识别模式和趋势,而不是查看电子表格中的数千行。一个好的数据可视化以精确和简洁的方式表达复杂数据集的含义。交互式数据可视化使理解数据和从数据中发现真知灼见变得更加容易。
本文介绍了如何使用 Plotly 创建不同的交互式绘图,以及如何使用 Chartstudio 在媒体和网站中嵌入交互式数据可视化。
我们去找 started✨
与 Chartstudio 连接
Chartstudio 是一个在云中托管交互式绘图/图表的平台。它还为 Python、R、MATLAB、Javascript 和其他计算机编程语言开发了开源图形应用编程接口(API)库。Plotly 和 chartstudio 的重要特点是:
- 它制作交互式图形/图表。
- 这些图形以 JavaScript 对象符号 (JSON)数据格式存储,以便可以使用其他编程语言(如 R、Julia、MATLAB 等)的脚本读取。
- 图形可以以各种光栅和矢量图像格式导出
让我们看看如何连接 chartstudio,
- 使用谷歌、脸书、Github 等凭证注册 chartstudio 平台
- 进入设置> API 密钥,保存用户名和 API 密钥。
以下代码服务于使用 Python 连接 chartstudio 的目的。chartstudio.tools.set_credentials_file
通过username
和api key
与平台连接。
嵌入图表
要在媒体/网站上嵌入交互图,我们需要使用 chartstudio 存储交互图。让我们看看如何使用 Chartstusio Python API 保存交互图。
在本文中,我们将使用gapminder
数据集,该数据集包含一年的大量国家级健康、财富和发展指标。
圆形分格统计图表
下面的代码绘制了 2007 年亚洲每个国家的人口饼状图。使用plotly.express.pie()
方法绘制图表。
创建好情节后,我们必须将其保存到 chartstudio cloud 中。为此,我们使用了chart_studio.plotly.plot()
方法,即图形对象、文件名和其他参数。执行下面一行代码后,会产生一个超链接,可用于嵌入媒体或其他网站。
py.plot(pop, filename = 'Population_Asia', auto_open=True)# Output
[https://plotly.com/~syamkakarla98/41/](https://plotly.com/~syamkakarla98/41/)
将链接粘贴到媒体中以嵌入交互式图表。
不仅是饼图,你还可以嵌入三维图,盒图,分布图,多维图,瓷砖地图,等等
分类图
您还可以使用 Plotly 创建分类图表,并使用 chartstudio 嵌入它们。以下示例显示了如何为来自sklearn.datasets
的合成圆数据集创建简单的 KNN 分类图,结果图如下所示。
结论
本文帮助读者使用 Plotly 和 Chartstudio API 和 Python 创建交互式绘图,并将它们嵌入到媒体或其他网站中。
更多来自作者
参考文献
https://plotly.com/python/ https://chart-studio.plotly.com/feed/#/
嵌入式可观察笔记本
将所有酷的例子用于你自己的数据,同时将数据保存在本地或你的网络中。
过着最佳觅食生活的鸡。图片作者。
如果你没听说过, Observable 是 d3 的创造者 Mike Bostock 带给世界的一个交互编码平台。它体现了 Bret Victor 对反应式编程的大胆设想,坦率地说,我认为它非常酷。一个缺点:我不擅长核心语言 Javascript。我非常擅长这个,不要误解我,但是我一直对人们在 Observable 上做的事情印象深刻。冒着重复 Observable 团队优秀指南的风险,我只分享一个具体的例子。
如果你被我的指南卡住了,权威参考是嵌入简介、高级嵌入和下载 g,以及嵌入故障排除。
现在我们走吧。Mike Bostock 展示了 d3 的两个出色的可视化示例:带工具提示的折线图和日历视图。我给加州大学伯克利分校 MIDS 分校的学生展示了后者,我们很好奇日历视图中显示的数据在折线图中会是什么样子。首先从日历视图下载数据:
下载“数据”变量的 JSON。图片作者。
现在我们有了数据,让我们创建嵌入。从折线图中,单击右上角的三个点,然后选择嵌入:
单击嵌入单元格。图片作者。
现在我们只需要图表,所以只选择图表。确保获得“JavaScript 运行时”,这样我们就可以覆盖数据。
图片作者。
现在您应该有了这段代码:
<div id="observablehq-chart-22ee36d2"></div>
<p>Credit: <a href="[https://observablehq.com/@d3/line-chart-with-tooltip](https://observablehq.com/@d3/line-chart-with-tooltip)">Line Chart with Tooltip by D3</a></p><script type="module">
import {Runtime, Inspector} from "[https://cdn.jsdelivr.net/npm/@observablehq/runtime@4/dist/runtime.js](https://cdn.jsdelivr.net/npm/@observablehq/runtime@4/dist/runtime.js)";
import define from "[https://api.observablehq.com/@d3/line-chart-with-tooltip.js?v=3](https://api.observablehq.com/@d3/line-chart-with-tooltip.js?v=3)";
new Runtime().module(define, name => {
if (name === "chart") return new Inspector(document.querySelector("#observablehq-chart-22ee36d2"));
});
</script>
在您已经下载的data.json
旁边打开一个空的index.html
,我们将把它嵌入到一个 HTML 容器中(在我的 Atom 编辑器中,我只需键入html
,并使用补全来获得“外壳”)。它应该是这样的:
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<div id="observablehq-chart-b01c95fe"></div> <script type="module">
import {Runtime, Inspector} from "[https://cdn.jsdelivr.net/npm/@observablehq/runtime@4/dist/runtime.js](https://cdn.jsdelivr.net/npm/@observablehq/runtime@4/dist/runtime.js)";
import define from "[https://api.observablehq.com/@d3/line-chart-with-tooltip.js?v=3](https://api.observablehq.com/@d3/line-chart-with-tooltip.js?v=3)";
new Runtime().module(define, name => {
if (name === "chart") return new Inspector(document.querySelector("#observablehq-chart-b01c95fe"));
return ["x","y","yAxis","bisect"," xAxis","line"].includes(name);
});
</script>
</body>
</html>
现在我们将加载数据,首先加载 d3 库并使用它来获取 JSON 文件:
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<div id="observablehq-chart-b01c95fe"></div> <script src="[https://d3js.org/d3.v7.min.js](https://d3js.org/d3.v7.min.js)"></script>
<script type="module">
import {Runtime, Inspector} from "[https://cdn.jsdelivr.net/npm/@observablehq/runtime@4/dist/runtime.js](https://cdn.jsdelivr.net/npm/@observablehq/runtime@4/dist/runtime.js)";
import define from "[https://api.observablehq.com/@d3/line-chart-with-tooltip.js?v=3](https://api.observablehq.com/@d3/line-chart-with-tooltip.js?v=3)";
const main = new Runtime().module(define, name => {
if (name === "chart") return new Inspector(document.querySelector("#observablehq-chart-b01c95fe"));
return ["x","y","yAxis","bisect"," xAxis","line"].includes(name);
}); d3.json("data.json").then(function(d) {
[next bit goes here]
});
</script>
</body>
</html>
好的,我不仅仅是加载了 d3 和数据。看还有什么?我还将运行时固定在一个变量中,我用const main = ...
称之为“main”。
最后,一旦数据被加载,我们只需要覆盖运行时上的数据。为了避免再次重复整个代码块,我们将用我们的覆盖替换[next bit goes here]
。让我们开始吧,首先用日期制作日期对象并添加轴标签,然后覆盖数据:
const newData = Object.assign(d.map(({date, close}) => ({date: new Date(date), value: close})), {y: "$ Close"});
main.redefine("data", newData);
而且应该管用!因为我们从本地文件系统请求data.json
,要查看index.html
文件,你需要一个本地服务器(你的浏览器保护你免受恶意 JS 文件的攻击!).出于方便,我通常只是用
python3 -m http.server
当导航到 http://localhost:8000/ 查看成品时:
是的,我需要升级我的浏览器。图片作者。
干杯!
使用 datapane 将您的交互式可视化嵌入到中型帖子中
以及如何以编程方式为风险承担者创建报告
还记得你创造了那个令人敬畏的互动情节,并且很难把它嵌入到你的文章中吗?或者你想让读者看到数据框,但不一定只是粘贴一张df.head()
的截图?这样的奋斗不仅限于写文章。你也可以在准备任何类型的报告时遇到它们,这些报告可以使用漂亮的 viz 来展示你的分析结果,但同时不需要大量的时间和精力来准备。我最*遇到了一个 Python 库,可以帮助你解决这些问题。
关于数据面板
datapane
是一个 Python API/库,对于想要分享分析结果的人来说特别方便。这并不局限于在中型文章(或其他网站)中嵌入可视化,因为您也可以轻松地生成 HTML 报告等。还记得您准备了某种分析(通常是临时的),将笔记本的 HTML 版本发送给涉众(隐藏代码,这样他们就不会不知所措),然后他们回来向您添加更多内容或更改过滤器并重新共享结果的情况吗?然后同样的操作重复多次,导致几十个 HTML 文件被发送过来。绝对不是愉快的经历…
在这种情况下,datapane
的一个很好的特性可以成为救命稻草,那就是你可以创建一份报告,与他人(同事、利益相关者等)分享。)然后万一有东西需要更新或刷新,你可以自己做,只需重新发布。不再重复发送相同的报告。最重要的是,这些报告可以是交互式的,因此利益相关者可以更深入地研究数据,自己做一些探索。
在datapane
中,您以编程的方式创建报告,也就是说,像在 Jupyter 笔记本中一样用 Python 运行您的分析,准备输出(数据帧或图表),然后将所有这些构建模块与一些描述组合在一起,形成一个漂亮的报告。为了使我们的生活更容易,datapane
允许包装日常分析中非常常见的对象,例如pandas
数据帧、来自不同库的图(matplotlib
、plotly
、bokeh
、altair
等)。)、降价文本和乳胶配方。
我认为这对于介绍来说已经足够了,让我们开始编码并展示结果。
运行中的数据面板
我不需要努力寻找datapane
可以派上用场的分析例子。在我之前的文章中,我简要介绍了一个简单的交易策略。与此同时,我下载了网飞的股票价格,并使用plotly
创建了一个蜡烛图。虽然我的笔记本中的情节很好,也很有互动性,但文章中的截图并非如此。
在你说任何事情之前,我知道对于plotly
来说,发布图表并将其作为嵌入对象在媒体上共享也是相对容易的。然而,对于其他流行的绘图库来说,情况可能并非如此。最后我展示的不仅仅是情节。但是我们不要想太多。和往常一样,我们从加载所需的库开始。
由于datapane
是一个平台,你需要在这里创建一个账户。对于简单的情况,比如你将在本文中看到的报告,免费帐户已经足够了。创建帐户后,我们需要连接到他们的 API。我们可以在终端中运行以下命令:
datapane login --server=https://datapane.com/ --token=API_TOKEN
或者在笔记本上运行下一行:
dp.login(API_TOKEN)
其中 API 密钥是您连接到datapane
的个人密钥,您可以在您的设置中找到它。然后,我们将下载与上一篇文章相同的数据集— Netlix 从 2019 年到 2021 年调整后的股票价格。
这次我将跳过粘贴df.head()
的结果和一个简单的线图。相反,让我们看看如何使用plotly
绘制一个蜡烛图。
对于那些感兴趣的人来说,有一个很好的 Python 库cufflinks
(repo here ),这使得创建烛台图表变得更加容易。我在另一篇文章中也提到过。
第一次报告
我们就从最简单的报道开始,也就是刚刚我们在上面创造的情节。为此,我们创建了一个dp.Report
类的实例,并提供构建块作为参数。首先,我们使用dp.Text
来添加标题(使用 Markdown),但是我们也可以使用相同的对象来提供对报告中发生的事情的更多描述。然后,我们将存储在fig
中的情节嵌入到dp.Plot
中,并为其添加一个漂亮的标题。
你可以在下面看到结果。
我们使用的第一种方法(save
)存储报告的 HTML 版本,并在默认浏览器中打开它。第二个— publish
—正如它的名字所表明的那样,即将它发布到 Datapane,从那里我们可以复制链接并粘贴到我们的文章中。我们还必须指定报告是公开的还是保密的。
提示:在datapane,
中有不同类型的报告,每种报告适合显示不同类型的信息。在本文中,我们使用默认的,但是你可以在这里阅读更多关于它们的内容。
如您所见,创建第一个报告非常容易。让我们添加一些其他功能!
第二次报告
这一次,我们在标题下添加了一个额外的描述,并包含了两个表,呈现了数据集的前/后五个观察值。为此,我们使用dp.Table
类,直接捕获我们准备好的数据帧的输出。
第三次报告
最后,我们更进一步,创建了一个两页的报告。为了指定页面,我们使用了dp.Page
类,直接传递给通用的dp.Report
类。第一页将与上面的报告非常相似,我们只是使用dp.Group
助手类将两个表存储在不同的列中,以便更好地呈现。在使用dp.Page
时,我们还将页面上想要使用的所有元素作为列表传递给blocks
参数。
在第二页上,我们使用dp.DataTable
类嵌入整个数据集。让事情变得更好的是,观众可以使用 SandDance 或 Pandas Profiling 自动探索它。只需单击报告的数据集页面上的按钮,所有这一切就都完成了!
如您所见,我们用几行代码在笔记本上准备的结果的基础上准备了一个漂亮的交互式报告。这种形式在与其他人合作时肯定更容易使用,尤其是非技术观众。
外卖食品
datapane
是一个平台,使您能够轻松共享您以编程方式生成的报告。- 报告可以包括文本、情节(也可以是交互式的)和整个数据集,观众可以使用熊猫概况等工具对其进行进一步分析。
- 这些报告可以嵌入到 Medium、Reddit、Confluence、您自己的网站等。
你可以在我的 GitHub 上找到本文使用的代码。此外,欢迎任何建设性的反馈。你可以在推特上或者评论里联系我。
如果您喜欢这篇文章,您可能还会对以下内容感兴趣:
[## 用 Python 创建交互式烛台图表的最简单方法
towardsdatascience.com](/the-simplest-way-to-create-an-interactive-candlestick-chart-in-python-ee9c1cde50d8) </5-types-of-plots-that-will-help-you-with-time-series-analysis-b63747818705>
使用自然语言处理嵌入足球语言
实践教程
使用最先进的 NLP 算法构建体育分析领域未来机器学*解决方案的表示
世界上有 13 亿人说汉语,这使它成为世界上最通用的语言。有趣的是,最受欢迎的运动——足球——拥有 40 亿粉丝,是它的 3 倍多。任何体育游戏都有严格的规则和格式,就像语法一样,还有一套定义好的动作,就像词汇一样。显然,如果足球是一种正式语言,它将是世界上最受欢迎的语言。
受这一见解的启发,在这部作品中,我试图将当前最先进的表达人类语言的方法,也就是自然语言处理,投射到代表足球的全球语言上。
我将主要关注创建这种表示的动机,以及如何创建它,并在某种程度上验证结果。我的下一篇文章将讨论在表示的基础上开发解释器和 UI,并演示它在足球领域的各种用例中的使用。
这篇文章相当长。为了您的方便,我添加了一个目录。如果你熟悉上面提到的 NLP 概念,可以直接跳到action 2 vec——嵌入球上动作。
本作使用的所有代码 都可以在 Github 上的 Football2Vec 库中获得。
目录
- 动机
- 先验知识
- 数据集
- 构建足球语言
- 动作 2Vec —嵌入球上动作
- 玩家 2 vec——用文字描述玩家
- 我们如何使用它?
- 寻找相似的玩家
- 足球类比
- 下一步是什么?
动机
首先,任何涉及数学模型的复杂任务都需要一种表示,也就是向算法提供数据的方式。具体来说,数据表示是机器学*建模的核心。例如,如果我们想确定一个球员是否适合某个特定的俱乐部,我们首先需要定义如何表示足球运动员和足球俱乐部。表示应该尽可能的通用和描述性,以适应各种各样的用例。文本嵌入满足这些要求。
第二,也是最重要的一点,我一直觉得足球很迷人。我很好奇它会把我引向何方。随着数据变得越来越容易获取,我知道这可能非常有趣,这本身就是做任何事情的正当理由,不是吗?
先验知识
这项工作包含了自然语言处理(NLP)中的一些高级概念,如单词嵌入、 Word2Vec 和 Doc2Vec 。对于那些不熟悉单词嵌入的人来说,它们基本上是单词的密集、低维表示,遵循的思想是单词之间的语义相似度越高,它们在空间上应该越接*。为了更深入地了解它,我强烈推荐这篇关于单词嵌入的评论和Zafar Ali的简单 word2vec 介绍。
在实践方面,如果你想知道如何使用 Python 实现 Word2Vec,请按照这个指南使用 Gensim 编码 Word2Vec(当然,也可能适用许多其他包,如 Keras / TF / Pytorch /等。).最后,为了绘制 2D 空间上的嵌入,我们使用 UMAP 进行降维,这被认为是最佳实践。
数据集
这项工作的数据基于 Statsbomb 开放数据集。它包含约 900 场比赛和来自各种锦标赛的约 900 名 4K 选手(男性和女性)。季节范围在 2003 年到 2020 年之间。值得注意的是,很多赛季只有很少的比赛。数据集中的每场比赛都包含球队元数据(例如,国家、阵容等。)、比赛元数据(例如,舞台、体育场等。),最重要的是,手动收集和标记事件数据。
比赛事件数据本质上使用丰富的属性来描述持球动作:动作名称、位置、球员名称、时间,以及作为动作结果的可选参数和用于执行动作的球员身体部位。详细的文档可以在数据集的 Github 库上找到。总体而言,该数据集包含约 300 万条事件记录。想了解更多关于足球数据集的信息,请访问克里斯蒂安·科蒂施克的这篇精彩评论。
构建足球语言
关于我们的代表,我们定义了两个目标:
- 表现必须嵌入关于游戏的知识。
- 表现形式必须尽可能直观。
以下基本原则将指导我们设计语言,只描述重要的东西,同时模仿人类说话的方式,尽可能保持简单和直观。
遵循人类的说话方式
人们倾向于用丰富的专业语言来描述球员。例如,在寻找新球员时,我们可以说:“我们需要一个在禁区内得分的目标人物,双腿都有出色的射门能力和良好的头球。他还需要在压力下成功接到长传球……”。根据这份资料,我们可以想到埃托奥、伊布、苏亚雷斯等球员。
有意思的是,这样复杂的描述,可能会换成一个代表球员的名字。例如,有人可能会说“给我找另一个苏亚雷斯】。为此,我们应该为具有相似人类等级描述的玩家提供相似的表示。那么,在对足球语言进行建模时,我们需要考虑哪些方面呢?
- 球员做什么——因为我们使用事件数据,我们将仅仅处理有球动作,比如传球、运球等。
- 他在球场上做什么——中锋、边路、防守位置、前锋位置等等。
- 他是如何做到的——使用身体的哪个部位,持续多长时间,等等。
我们的结论是,我们需要一个能够捕捉到的实际行动、空间和背景的表现形式。
为什么是单词嵌入?
在我们开始建模之前,我们应该考虑一些可能的方法。三个因素促使我选择单词嵌入:
- 单词嵌入直观地编码语义上下文。
- 单词嵌入是一种强大的数学工具,可以产生最先进的结果。然而,它们的使用效率很高。
- 这些模型很多都是开源的,就这么简单。
可以用 Word2Vec & Doc2Vec 吗?使用 Word2Vec 和 Doc2Vec 需要验证三个主要假设:(1)句子的正常性,(2)在给定上下文的情况下预测缺失单词的身份的能力,以及(3)定义良好的词汇。接下来,我们将解决这些需求。
作为文本数据的事件数据
想想足球在广播里的播报方式,这是现实生活中一种口头的、简洁的消费方式。这对于如何使用事件数据来描述持球动作是一个很好的启发。
事件数据描述了哪些动作发生了,什么时候,在球场上的什么地方,由谁做的。由于这些事件是按时间顺序排列的,我们可以用它们来构建事件的序列,也就是我们语言中的句子。
下一个要解决的问题是——给定一系列真实事件,我们能推断出缺少了什么事件吗?要回答这个问题,请考虑以下一系列操作:
传到侧翼,传到禁区,?,守门员扑救。
空白处可以放什么?禁区内任何类型的射门或头球都可以。虽然你们大多数人觉得这个谜语很简单,但提出了两个重要的见解:
- 知道答案,在某种意义上就是了解足球是如何运作的。
- 符合这个缺失单词的所有动作都可以被认为是语义相似的——拿球并直接试图在禁区内射门的动作。
定义词汇
那么,我们如何用我们的语言将事件编码成单词呢?虽然选择是无穷无尽的,但我决定采用以下方案:
<动作名称> <动作位置><附加参数>
- 动作:传球、运球、射门、守门员动作、拦截、接球、持球、脱手、决斗、拦网、犯规、越位和解围。
- 位置—我将音高从左到右水平分成五个箱,从上到下垂直分成五个箱,如下所示:
图 1:动作位置符号。x 轴和 y 轴被分成五个箱。右下角有角度符号。图片作者。
- 其他参数——身体部位、传球高度、传球方向、脚后跟传球、投篮技术类型、动作结果等。
理解参数选择高度影响结果是很重要的。例如,我没有考虑任何与行动持续时间相关的参数,这意味着语言在描述玩家时不会捕捉到这个因素。
概括一下,我们将使用比赛数据来构建描述足球比赛的令牌。使用这些单词,我们将构造句子,在这些句子上,我们可以训练 Word2Vec 模型来理解足球动作。然后,我们将使用 Doc2Vec 模型来表示足球运动员。
图 2:研究计划。图片作者。
重要的免责声明——这种描述意在描述玩家执行动作的内容、地点和方式,而不是他们执行得有多好。在下一篇文章之前,技能水平是我们表述中缺失的一块。例如,我们的模型可以表明一名球员执行与梅西相同的动作,但这并不意味着他和梅西一样优秀。不幸的是,我不相信事件数据能有效衡量玩家的技能水平。
Action2Vec —嵌入球上动作
有了词汇之后,我们就可以开始造一些关于球上动作的句子了。我们根据球的所有权来分组行动,意思是,每一句话都是一系列的团队行动,直到球被另一个团队丢掉。我们允许连接以下所有物,以防它们短于模型的采样窗口参数。
我们使用具有以下超参数的 Gensim Word2Vec :窗口大小为 3,嵌入大小为 32,以及 10 次出现的数据集中的最小单词数。这个模型被输入了我们创建的单词的整数编码。总的来说,我们的词汇表中有大约 19K 个单词。让我们看看我们语言中的两个单词示例,它们的表示,以及足球空间中最接*的(语义)单词。
示例 1:单词 ID 3274
- 文字描述: (4/5,3/5) <传球>:(^<)|高长|左脚
- 含义:前场位置的长对角线高传球。对角线方向遵循图 2 中解释的符号。
- 单词嵌入= [0.55 -0.57 … -0.13 0.13] (32 x 1 向量)
图 3:字 ID: 3274,字说明: (4/5,3/5) <关>:(^<)|高长|左脚。图片作者。
- 最相似的词是 742: (4/5,3/5) <传球>:(^<)| ground-long | left _ foot(同样传球,但是在地面上)。第二个最相似的单词对应于相同的传球,但是是用另一只(右)脚。不同身体部位或高度的相同传球之间的高度相似性表明,该模型学*了单词 id 背后的语义,捕捉了游戏逻辑和流程。
示例 2:单词 ID 315
- 文字描述: (1/5,1/5) <运球>:不完整。
- 含义:防守左侧运球不成功。
- 表示= [-0.45 -0.82 … 1.1 0.97] (32 x 1 向量)。
图 4:单词 ID 315,单词描述: (1/5,1/5) <运球>:不完整。图片作者。
- 最相似的词: (1/5,1/5) <剥夺> ,意味着模型了解到不成功的运球和剥夺是相似的,因为两者都将自己球队的控球权转移给了对手球队,这是持球者执行的动作的结果。
现在,让我们看看它是如何查找整个词汇表的。为此,我们使用 UMAP 作为一种技术,将维度从 32 维减少到仅仅 2 维。
图 5:完整的 19K 单词 Action2Vec 词汇的 UMAP 投影。图片作者。
我们可以注意到传球的流行,用紫蓝色标出,这是大多数的动作。视觉上,传球的形状像一只巨大的章鱼,到处张开双臂,因为它们连接着所有其他的动作、球员和位置。照片中的文字(粉红色)被十字、收件箱通行证等包围。此外,防守传球距离进攻传球很远,所以在某种意义上,位置也是根据数据推断出来的。话虽如此,重要的是要记住,这仅仅是一个 2D 减少的代表性。
播放器 2Vec
player match 2 vec——用文字描述玩家
如果动作是单词,句子是一系列有序的动作,那么文档通常是后续句子的集合。在我们的例子中,文档是特定时间范围内的后续所有物的列表——一场完整的比赛、一场 10 分钟的比赛等等。然而,文档的这个通用定义同时描述了所有团队成员,而不是单个成员。
有几种可能的方法来构建描述单个玩家的文档。我选择了一个简单的解决方案,通过玩家在定义的范围内的有序“行动包”来定义玩家文档,即一场比赛中的所有玩家行动。因此,我们可能有多个文档描述数据中的一个玩家。
为了为每个玩家产生单个向量,我们可以在粒度级别上,例如在他的所有匹配上,对向量进行平均。然而,这样做我们消除了玩家的空间跨度,这可能也是有意义的。为了代表玩家,我们使用了 Gensim 的 Doc2Vec 模型,其参数与 Action2Vec 相似,只是采样窗口较小。
图 PlayerMatch2vec 的 UMAP 投影。每个点代表特定比赛中的一名球员。球员是按位置着色的。图片作者。
图 6 覆盖了数据集中所有玩家的匹配,用玩家的位置来着色。位置是一个非常合理的“解释者”,因为在相同位置的球员通常被要求在相似的位置做相同的动作。比如门将集群,很好区分,非常同质。有趣的是,攻击者在空间上离守门员最*,因为他们触发了守门员的扑救。
更多的异构集群是右后卫和左后卫集群。虽然这两者看起来不相干,但更深入的探究揭示了两个主要的本土后卫集群,如约尔迪·阿尔巴和本杰明·门迪,以及一个包括安东尼奥·瓦伦西亚和丹尼尔·瓦斯等后卫的“桥梁”区域。所有其他位置都位于这三个集群之间。
我猜你想知道右边的“小臂”是什么,它似乎是一个单色的星团。嗯,这个非凡的造型属于一个非凡的球员——莱昂内尔·梅西。欢迎你自己探索梅西的集群中包括哪些球员,但不要担心——我会在下一篇文章中亲自挖掘。
player 2 vec——用一个向量表示每个玩家
为了给每个玩家生成一个向量,我们对所有匹配的表示进行平均。您可以使用下面的交互式图表来探索这一有趣的结果:
图 7:玩家 2vec 的 Plotly 交互式 UMAP 投影,其中所有玩家的匹配被平均到单个向量。球员是按位置着色的。图片作者。
同样,我们可以花几个小时来研究它,并发现许多奇特的见解(以及一些我无法解释的见解),但我更愿意专注于一个有代表性的例子——对右翼分子的考察:
图 8:数据集中所有右翼分子的 UMAP 投影播放器 2vec。图片作者。
集群#1 是一种异常值,这是有意义的,因为它本质上包含非本地边锋。‘umap _ 1’轴上的最高点是莱昂内尔·梅西。在他的集群中,集群#2,我们可以找到倾向于中间切入的边锋,如穆罕默德·萨拉赫,Xherdan Shakiri 和哈基姆·齐耶什。第三组包括像雷比奇、费古利和华金(皇家贝蒂斯)这样的球员,他们更多地粘在边路,把球传到禁区。
怎么会用呢?
寻找相似的玩家
还记得这篇文章开头的寻找新球员签约的例子吗?对我来说,它匹配像埃托奥,兹拉坦伊布拉希莫维奇和路易斯苏亚雷斯这样的球员。那么,当我们根据我们的表述通过余弦相似度来寻找与苏亚雷斯最相似的球员时,我们能期待什么呢?
- 弗朗西丝卡·科比
- 兹拉坦·伊布拉西莫维奇
- 薇薇安·米德马
- Samuel Eto'o
- 托马斯·穆勒
- 凯蒂·斯坦格尔
- 大卫·比利亚
- 克里斯蒂亚诺·罗纳尔多
- 阿莱克西斯·桑切斯
- 迭戈弗兰
- 尼基塔·帕里斯
有趣的是,前三名中有两位女选手,都是成功的前锋科比和米德玛。当只检查男性玩家时,我们得到了我最初列出的确切玩家。这些结果令人惊讶,因为我在检查结果之前就列出了这个清单(允许怀疑)。想象一下,如果不是稀疏的 4K 顶级球员数据集,我们将拥有一个更大、更完整、最新的所有活跃球员的数据库,会取得什么样的成就!
我们即将完成这篇文章,但是你不能写一篇关于单词嵌入的文章而不展示一些有趣的语义类比。为此,我用 regex 挖掘了我们的词汇,创建了人工文档。例如,运球文档是保存任何成功运球令牌的一个副本的文档的模型表示。
作为一个“免责声明优先”的人,需要注意的是这种人工文档表示是随机的。此外,为了恰当地表示,文档的设计和结构应该类似于原生文档——这绝不是一项简单的任务。我将在下一篇文章中介绍它。最后,我使用*似符号来提醒自己,这些不是等式,而是与查询向量最相似的文档(通过余弦相似度)。
所以,在怀疑和期待之间找到平衡,让乐趣开始吧!
足球类比
与安德烈斯·伊涅斯塔最相似的球员:阿瑟·梅洛(尤文图斯,前巴塞罗那球员)
- 伊涅斯塔+发件箱得分+收件箱得分~凯文·德布劳内
- 伊涅斯塔+发件箱得分——运球~托尼·克罗斯
- 伊涅斯塔+收件箱得分~埃登·阿扎尔
与内马尔最相似的球员:罗纳尔迪尼奥
- 内马尔——运球(所有位置)~蒂埃里·亨利(巴萨)
- 内马尔——边路运球~菲利佩·库蒂尼奥
与格里兹曼最相似的球员:卡洛斯·贝拉
- 格里兹曼+运球(所有位置)~阿扬·罗本
- 格里兹曼+侧翼运球~米克尔奥亚尔扎巴尔
与布斯克茨最相似的球员:亚亚·图雷
- 布斯克茨+运球~蒂亚戈阿尔坎塔拉
总而言之,在这篇文章中,我们创造了动作嵌入——一种足球动作的表现;和球员嵌入——足球运动员持球动作的表现。就直接价值而言,我们可以使用模型来找到具有特定游戏风格的玩家。我们记得(!)这是一个描述性的表示,而不是质量指标。
虽然用它来制作很棒的 gif 可能很酷,但是我们用这一切实际上能达到什么目的呢?有什么好处?所以,如果你读到这里,你肯定有足够的动力,但只是为了让每个人都保持清醒,我们将在这里停下来,并在下一篇文章中继续。
下一步是什么?
在下一篇文章中,我们将解释嵌入和玩家的相似之处。为此,我将为大家分享一些嵌入可交代性和掩盖冰山一角 技能评估 的做法。最后,我们将使用精彩的Stramlit&PlotlyPython 包创建一个令人惊叹的 UI 和交互式可视化。所有代码都可以通过 Github 上的 Football2Vec 库获得。下面是另一个有趣的 GIF 图片:
图 9:先睹为快下一篇博文。图片作者。
最后,我真诚地呼吁所有数据收集公司发布更多的公共数据集。跟踪数据也可以捕捉无球运动,标记,压力,速度,战术,等等。我们刚刚经历了足球数据革命的开端,我们请求你们也让我们享受它。毕竟是团队运动。
嵌入,不仅仅是文字
除了单词之外的空间嵌入的一些应用的好处
帕特里克·托马索在 Unsplash 上的照片
嵌入是机器学*(ML)中一个方便的概念,大多数时候,像向量和单词表示这样的术语经常出现在上下文中。本文描述了向量大小对 ML 模型意味着什么,以及嵌入与模型输入有什么关系。
嵌入只是一个映射函数,可以将离散的值列表映射到连续的向量空间。连续空间是密集的,用多维向量表示。在稀疏数据上训练模型是一项非常艰巨的任务;因此,在这种情况下,嵌入变得非常有影响力。
表现
自然语言处理(NLP)模型使用嵌入来表示单词,主要是在语料库包含许多单词的情况下。机器学*中的单词需要数字表示。嵌入可以帮助建立一个多维空间,通常代表每个单词的语义。
例如,如果我们有 1000 个单词,我们可以用几种技术来表示每个单词:
- 给每个单词分配一个索引:在这种情况下,我们可以为长度为( n )的每个单词构建一个向量。 n: 代表词汇量。
图 1:使用单词包方法编码单词。向量的长度等于词汇量的大小。
- 或者,我们可以将每个单词映射到 n 个嵌入的向量。神经网络学会了如何将语义相似的单词相对地放在一起。这种表示的主要好处是词汇表中的相似单词将被映射到输入空间中的相似区域。认识到这样的区域有助于我们建立多种能力,例如相似性、相关性,这些能力可以在推荐模型中使用。
有更多的编码表示。如果我们在一个巨大的词汇表列表上训练一个模型,我们应该预料到模型正在更新的许多权重,这可能会显著影响模型的性能。
单词嵌入
单词嵌入是一种普遍使用的方法,也是一种非常创新的处理输入词汇域的方法。它们在 NLP 问题中被广泛用于建立语言模型。语言模型可以执行许多具有挑战性的任务,例如分类、翻译、情感分析和文本生成。Word2Vec、Glove Vectors 只是构建神经网络来学*给定语料库中的单词嵌入的两个例子。
在图 1 提供的例子中,如果我们的词汇表是 1000 个单词的长度,那么["Machine "," Learning"]输入数组用两个 1000 维的向量表示。
例如:Word2Vec
Word2Vec 是一个依赖机器学*建立模型的学*表示的例子。该模型的目的是计算单词的含义,而不是对其在句子中的出现进行编码。基于每个向量之间的距离来构建词向量。为了建立这个模型,我们使用神经网络来学*将这些向量与预测它们周围单词的隐含意义相关联。
下面的交互式图形说明了如何构建该模型,其中使用单个隐藏层构建了一个神经网络。这个模型使我们能够通过学*输入文本提供的隐式交互来轻松地比较词汇,如图 2 和图 3 所示。
图 2:word 2 vec 学*过程的可视化
图 3:word 2 vec 学*过程的二维可视化
示例:神经嵌入
构建神经网络时,我们可以创建一个嵌入层,以较小的维度对输入进行分组。为了说明在 NLP 问题中使用嵌入层,我们将看一个简单的例子。假设我们有十个句子,每个句子包含一些 ML 和其他通用计算主题。
sentences = [
'Machine Learning',
'Model training',
'Supervised learning',
'Unsupervised learning',
'Reinforcement learning',
'Computer Hardware Engineering',
'Computer Security',
'Computer architecture',
'Operating System',
'Parallel computing'
]
我们将跳过文本处理细节,我们也简化它。例如,我们没有将字符转换成小写字母。然而,本文末尾的链接中提供了完整的代码。对句子中的每个单词进行编码是我们的下一个目标。
encoded_sentences = []
for sentence in sentences:
encoded_sentences.append(label_encoder.transform(sentence.split(' ')) + 1)
如果我们打印出这个数组的内容,我们应该得到如下结果:
[array([5, 4]),
array([ 6, 17]),
array([11, 16]),
array([13, 16]),
array([ 9, 16]),
array([1, 3, 2]),
array([ 1, 10]),
array([ 1, 14]),
array([ 7, 12]),
array([ 8, 15])]
请注意,第六个句子有三个单词,而所有其他句子都只有两个。因此,我们需要在较短的句子末尾填充零。
from keras.preprocessing.sequence import pad_sequences
# Since the sentences size is different, we need to add padding.
max_length = 3 # No more than three words per sentence
#padding = post since we need to fill the zeros at the end
padded_enc_sentences = pad_sequences(encoded_sentences, maxlen=max_length, padding='post')
让我们再看一眼编码的句子。
array([[ 5, 4, 0],
[ 6, 17, 0],
[11, 16, 0],
[13, 16, 0],
[ 9, 16, 0],
[ 1, 3, 2],
[ 1, 10, 0],
[ 1, 14, 0],
[ 7, 12, 0],
[ 8, 15, 0]], dtype=int32)
构建嵌入层非常简单。我们将使用 Keras [1]来创建这一层并编译模型。注意,有三个参数传递给嵌入层:input_dim、output_dim 和 input_length。Input_dim 表示语料库的大小(词汇量),output_dim 是我们想要构建的嵌入向量的大小,以及输入的向量大小。
model = Sequential()
embedding_layer = Embedding(input_dim=len(all_tokens), output_dim=2, input_length=3)
model.add(embedding_layer)
model.compile('adam', 'mse')
如果我们想要查看句子“机器学*”的嵌入权重,该模型将返回以下权重:
array([[[ 0.02791763, -0.02687442],
[ 0.00129487, 0.02928725],
[ 0.00248948, 0.04000784]]], dtype=float32)
同样值得一提的是,NLP 中的嵌入面临许多挑战。例如,一个句子中的一个单词如果放在另一个上下文中可能有完全不同的意思。有更高级的嵌入方法来补充这种基本方法。
超越单词嵌入
正如我们所看到的,嵌入可以给传递给模型的输入提供有价值的见解。它可以有效地作为一种降维技术,同时学*隐藏的关系。
嵌入建模已经与传统算法(如贝叶斯网络)和神经网络(前馈、卷积神经网络等)一起使用。关键特征是嵌入提供了输入到一些可学*的上下文表示中的组合。可以嵌入特性,提供不同数据类型的语义相似性。通常,它们是非结构化的,可以包含图像。
应用:
除了 NLP 之外,在许多领域中,嵌入都是非常有用的特征工程任务,这样的例子有很多。
- 学*浏览在线商店时的用户行为。
- 学*在一组给定的数据中发现异常。
- 理解推荐系统的项目-用户关系。
- 学*在一系列交易中发现欺诈。
- 学*蛋白质序列。
例如:电影推荐
我们将使用 MovieLens 数据集来学*用户以前对电影的评级,以预测他们接下来有兴趣看什么。数据集可以在 Kaggle 上找到。数据集由几个 CSV 文件组成,我们将使用的是“评级”和“电影”。数据集必须准备好合并这两个 CSV 文件,从而产生两千万条包含六列的记录。最小额定值为 0.5,最大额定值为 5。
MovieLens 数据集的示例。
我们的预测模型将学*两列:userId 和 movieId。两列都包含离散的高基数数值。我们将使用嵌入为每个值构建一个密集向量。输入由一个 userId 和一个 movieId 的组合组成,分为两个输入层,每个输入长度为一。
# userId input:
user_id_input = Input(shape=(1,), dtype='int64', name='user_id')
# movieId input:
movie_id_input = Input(shape=(1,), dtype='int64', name='movie_id')
我们将创建两个嵌入层:一个用于 userId,另一个用于 movieId。嵌入的大小设置为 8,但可以根据模型的性能进行调整。
# Building the embedding layers
user_embedding_size = 8
movie_embedding_size = 8user_embedding = Embedding(len(unq_users), user_embedding_size, input_length=1, embeddings_regularizer=l2(1e-4))(user_id_input)movie_embedding = Embedding(len(unq_movies), movie_embedding_size, input_length=1, embeddings_regularizer=l2(1e-4))(movie_id_input)
现在,在构建模型之前,我们需要设计这两个嵌入层是如何结合在一起的?例如,我们将它们合并为两个独立的输入,或者使用点积。在这个例子中,我们将使用 Keras 点函数。
dot_out = dot([user_embedding, movie_embedding], axes=-1, normalize=False)out = Flatten()(dot_out)model = Model(
inputs = [user_id_input, movie_id_input],
outputs = out,
)model.compile(Adam(0.001), loss='mse')
在上面的编译说明中,我们选择了损失的最小平方误差函数。让我们打印出我们模型的摘要:
__________________________________________________________________________________________________
Layer (type) Output Shape Param # Connected to
==================================================================================================
user_id (InputLayer) (None, 1) 0
__________________________________________________________________________________________________
movie_id (InputLayer) (None, 1) 0
__________________________________________________________________________________________________
embedding_1 (Embedding) (None, 1, 8) 1085000 user_id[0][0]
__________________________________________________________________________________________________
embedding_2 (Embedding) (None, 1, 8) 141872 movie_id[0][0]
__________________________________________________________________________________________________
dot_1 (Dot) (None, 1, 1) 0 embedding_1[0][0]
embedding_2[0][0]
__________________________________________________________________________________________________
flatten_1 (Flatten) (None, 1) 0 dot_1[0][0]
==================================================================================================
Total params: 1,226,872
Trainable params: 1,226,872
Non-trainable params: 0
现在的培训步骤应该很简单:
model.fit(
[df.userId, df.movieId],
df.rating,
batch_size=64,
epochs=1,
validation_split=.05,
)
该模型的实现被简化,因为其目的是向仅使用用户 id 的用户展示嵌入如何捕捉电影之间的相似性。在捕获语义的同时,可以将 userId 和 movieId 列的高维度转换到低维空间。
摘要
在本文中,我们演示了嵌入如何向 ML 模型展示发现数据模式的机会。这是产生更有效的低维空间特征的非常有用的技术。我们已经看到单词袋向量是如何被转化成更小的向量的。我们还讨论了在神经网络中使用嵌入作为一个层。如果你使用的是 ML 工具包,比如 Keras,那么要确保向量的大小是固定的。
你可以在我的 Github 上找到完整的代码示例。如果你仍然渴望了解更多,请查看[2]中的速成课程。谢谢大家!
参考
[1] Keras,在 https://keras.io/api/layers/core_layers/embedding/有售
[2]嵌入:谷歌的速成课程可在https://developers . Google . com/machine-learning/Crash-Course/Embeddings/上获得
使用句子嵌入的简单无监督关键短语抽取
研究论文 E 解说
作者图片
背景
关键词/关键短语提取是提取与底层文档相关的重要单词的任务。通过将文档索引为文档别名,它可以让您更快地搜索文档,甚至有助于对这些中心主题的给定文本进行分类。现在,这些可能是抽象的(来自书面文本之外的相关关键词)或提取的(书面文本中存在的相关关键词)本质上。两者都有各自的好处,但是在这篇博客中,我们将仔细阅读瑞士电信公司和 EPFL 的研究人员以无人监督的方式进行的关键短语提取的这项非常有趣的工作。作者特别选择了无监督的方式,因为它比有监督的方式具有优势和灵活性,如域外概括,不需要带有关键字的大型手工注释语料库等。
大多数当前的关键词提取系统在速度和为文档生成一些不相关和冗余的关键词方面具有局限性。在这里,本文作者用自己提出的 无监督算法 解决了这两个问题。
有了这个背景和介绍,让我们直接开始理解这个算法。
他们提出了一个 3 步管道—
- 基于词性序列,从文本中提取候选短语。更准确地说,它们遵循的模式是提取由零个或多个形容词后跟一个或多个名词组成的短语。它可以被认为是一个正则表达式过滤器JJ * NN+覆盖整个文档的文本。
- 作为第二步的一部分,他们使用句子嵌入(Sent2Vec 和 Doc2Vec)将候选短语和原始文档嵌入到同一个高维向量空间中。
- 最后,他们根据给定短语与原始文档的相关程度对每个候选关键短语进行排序,同时最小化他们选择作为最终集合一部分的冗余关键短语的数量。
Sent2Vec 和 Doc2Vec
虽然方法 Sent2Vec 和 Doc2Vec 都是流行的嵌入技术,允许我们将任意长度的输入嵌入到固定长度的向量表示中,但是它们有不同的训练方法。
Sent2Vec 将句子嵌入定义为句子中出现的上下文单词嵌入的平均值,其中上下文单词嵌入不仅限于单字,还扩展到每个句子中出现的 n 个单字。 这里有一个 python 包,你可以用它来实现 。
在 Doc2Vec 中,通过添加段落 id 来扩展 word2vec,也作为输入中的一个组件。下图显示了同样的情况。
作者图片
这里, D 是文档 id, w 是来自某个窗口的文档的单词。我们连接表示并学*分类窗口的中心词。直观上, w 代表单词概念的单词向量, D —文档向量代表文档的概念。同样你也可以认为 Skipgram 版本是一样的。
作者使用 Sent2Vec 和 Doc2Vec 的预训练模型,其中 Sent2Vec 表示 700 维的词汇单元,而 Doc2vec 表示 300 维的词汇单元。
最大边际关联
在这种情况下, MMR 背后的核心思想是选择与底层文档相关的关键字,并且您选择的每个渐进式关键字与已经选择的关键字集具有最小的相似性。下面的等式显示了相同的数学表达式—
图片来自来源
这里,等式中的第一项计算文档 Q 和不在所选短语中的每个候选短语 D(i)之间的余弦相似度,由参数λ正则化。等式中的第二项计算每个候选短语和所选短语之间的余弦相似度,并选择最大相似度。也可以在这个位置选择 min,avg fxn,而不是 max。Max 确保我们对相似性进行最大限度的惩罚。λ是有界的 b/w [0,1],其中,1 将意味着没有多样性,并且将归结为仅基于余弦相似性的选择(信息量),并且将意味着高精度,而λ= 0 将意味着高多样性。这个正则化参数应该基于用例进行调整。理想情况下,应该选择 0.45-0.65 之间的λ值,以在两者之间保持最佳平衡。
我鼓励你也通读一下这份写得非常好的文件。
从下图中可以看出,没有多样性的结果(MMR)对于形态变化是多余的,而 As,embe bead 的++版本显然有助于减少这些形态相似的关键短语,同时形成最终集。作者选择多样性参数(λ)为 0.5,给予相关性和多样性同等的权重。但是这个参数也可以由用户根据下游应用的要求来调整。
图片来自来源
结果和评价
作者在三个常见的关键词提取数据集上测试了他们的方法—
- Inspec :由来自科学期刊摘要的2000 篇短文组成。为了对其他算法进行标准测试,他们对来自其他方法的 500 个常见文档(测试集)进行了测试。
- DUC :由 TREC-9 的 308 篇中篇报刊文章组成。它们专门提取原始文档的第一个标签中包含的文本(而不是标题和其他元数据中存在的内容)
- NUS :由 211 篇长文件(全科学会议论文)组成,4 到 12 页不等。每个文档都有一组由作者和其他贡献者创建和注释的关键短语。他们根据这些来评估他们的提取物。
修改自来源
上表展示了所有三个数据集的不同方法在精度、召回率和 F1 分数评估指标上的结果。很明显,在大多数情况下,恩贝拉德的表现都优于其他人。
如果你有兴趣了解更多关于关键词提取算法的知识,那么我有一篇文章讨论 NLP 中的 10 种流行的关键词提取算法 。一定要去看看!
我也有一个多语言字幕的视频漫游,如果你喜欢看视频而不是文字(就像我一样:D),一定要看看
如果你仍然对某件事感到困惑,一定要看报纸。另外,向作者问好,感谢他们的贡献。
论文标题: 使用句子嵌入的简单无监督关键短语抽取
希望这本书值得你花时间去读!
谢谢。
拥抱 R 来促进你的蛋白质组分析
一千个数据集的旅程从一个 R 脚本开始
在 Unsplash 上由 averie woodard 拍摄的照片
r 是一种流行的编程语言,特别关注统计计算。它最强的一点是多年来由社区贡献的附带包的宇宙。尽管我自己是一个狂热的 Python 用户,但我不能否认 R 拥有无与伦比的生物信息学( Bioconductor )、统计分析和出版物质量的数据可视化软件包。
我经常使用基于质谱(MS)的蛋白质组数据集,经常使用通用学生 t 检验和方差分析来评估各组样本之间的蛋白质差异丰度。然而,进步并没有停滞不前,科学家们正在开发专门针对生物数据的方法。一个非常流行的用于差异表达分析的 R 包是微阵列数据的线性模型,或 limma [1],它适合基因线性模型,但也借用基因之间的信息来产生更稳健的方差估计和更可靠的统计推断。Limma 已经存在了一段时间,现在它被广泛用于通过各种方法获得的基因表达数据,而不仅仅是通过微阵列。
在 limma 之上构建的是另一个 R/Bioconductor 包,称为DEqMS【2】。它使用特定于质谱的特征,例如每个蛋白质的肽或肽谱匹配(PSM)的数量,以建立依赖于数据的方差先验估计。用我自己的话来说,DEqMS 增强了具有多个 PSM 的蛋白质的 quan 结果的显著性,这是我们确实想看到的,因为多个定量事件增强了观察到的差异表达的可靠性。
DEqMS 简介包含关于如何使用软件包的清晰解释和指导性示例。然而,我认为后退一点,用从蛋白质表到结果的可视化和导出的所有步骤编写一个 R 脚本可能是有用的,这样一个 R 初学者可以有一个模板来开始和建立。
先决条件
完整的脚本可以在 Github repo 中找到,而工作流程的要点将在下面介绍。我在 Ubuntu 20.04 上使用了 RStudio IDE 和 R 3.6 来运行脚本。我们从安装所需的软件包开始,其中大部分可能已经安装在您的系统上:
加载包:
准备和检查数据
示例数据是内部通过分析市售酵母三重敲除(TKO)同量异位标记标准生成的。LC-MS 原始文件在Proteome Discoverer 2.4(PD)中处理,输出文件保存为制表符分隔的文本文件,并上传到同一个 Github repo。
TKO 标准由 11 个样品组成:野生型酵母菌株的 2 个副本和分别敲除 HIS4、MET6 或 URA2 基因的每个菌株的 3 个副本。除了明显缺乏相应的蛋白质,敲除引起蛋白质组的扰动,我们也应该能够检测到。
我们首先读取它,并找到具有量化值的列。
## [1] 1904 51
然后,我们过滤掉污染的蛋白质,添加一个单独的基因名称列,并自动检测和重命名具有定量值的列,根据“丰度”识别它们。比率"字符串中的名称为:
## [1] "BY4741_2" "his4_1" "his4_2" "his4_3" "met6_1" "met6_2" "met6_3" "ura2_1" "ura2_2" "ura2_3"
在过滤、格式化和对数转换之后,我们可以查看每个样本中定量值的分布:
作者图片
数据可能受益于标准化,以补偿由于样品收集和制备中的微小变化而导致的样品范围内的偏移。实现这一点的一种方法是使样本达到相等的中值,如下所示:
作者图片
主成分分析将向我们展示样本的一般分组:
作者图片
这个图是一个好消息,因为对应于每个敲除菌株的良好分离的组正是我们从这个数据集中所期望的。
方差分析和 T 检验
单向方差分析是在我们的情况下以数据集范围的方式分析差异丰度的逻辑方法,因为我们有一个变量(敲除基因)和三组: his4 、 met6 和 ura2。
调整后 p 值的一个常用阈值是 0.05。从实践的角度来看,我们可以进一步优先考虑条件之间具有较大倍数变化(FCs)的蛋白质,因为这些应该更容易通过其他方法来验证。对于像这样的酵母数据集,让我们选择 FC ≥ 30%,(log-FC ≥ log2(1.3)),并查看有多少蛋白质通过这些过滤条件:
## [1] 190 9
一次比较两组水平的经典方法是学生的 t 检验。让我们以 met6 击倒对手和 his4 击倒对手为例进行比较:
有多少蛋白质通过了相同的过滤标准(0.05/30%):
## [1] 116 11
火山图是表示差异丰度数据的常用方法。分类栏将帮助我们突出显示符合设定标准的蛋白质:
作者图片
我们可以直接看到一件好事:我们的 MET6 和 HIS4 是 3 种蛋白质中具有最低调整 p 值的两种,即最有可能实际差异表达的蛋白质。
利马和德克姆斯
DEqMS 是基于 limma 的,所以我们将能够一石二鸟,同时获得两种算法的结果。我们首先选择用于比较的色谱柱(不包括野生型,因为它没有 3 个重复),定义设计(哪根色谱柱属于哪组)并指出我们想要观察的对比(比较):
函数 spectraCounteBayes 是专用于 DEqMS 的部分。我们提取原始数据中每个蛋白质的 PSM 数量信息,然后 spectraCounteBayes 用它来计算 fit4 中的数据相关方差。让我们看看 PSM 数量和方差之间的关系:
作者图片
这个图有助于理解主要信息:DEqMS 期望具有大量 PSM 的蛋白质具有较少的可变性。因此,这种“可靠的”蛋白质的差异表达应该比具有单一 PSM 的蛋白质的相同丰度差异得到更高的权重。
让我们看看 met6 和 his4 组相同对比的表格结果:
## logFC AveExpr t
## P00815 -2.8532509 -0.54962113 -85.67738
## P05694 3.5999485 -0.85859918 77.41234
## P37291 -0.6922910 0.40048261 -28.07419
## P15992 -0.6353142 0.44930312 -27.22447
## P39954 -0.5661252 0.01364542 -22.95737
## Q12443 -1.2662064 0.60896317 -44.48587## P.Value adj.P.Val B gene count sca.t
## P00815 8.428621e-13 1.318236e-09 18.82971 P00815 20 -108.89141
## P05694 1.846004e-12 1.443575e-09 18.37417 P05694 30 96.37396
## P37291 4.585032e-09 1.434198e-06 11.77468 P37291 36 -42.07175
## P15992 5.803328e-09 1.512734e-06 11.53513 P15992 18 -36.62270
## P39954 2.138660e-08 3.344865e-06 10.18583 P39954 22 -31.96714
## Q12443 1.329146e-10 6.929282e-08 15.15264 Q12443 4 -30.06690## sca.P.Value sca.adj.pval
## P00815 8.998668e-18 1.407392e-14
## P05694 3.360470e-17 2.627888e-14
## P37291 2.531791e-13 1.319907e-10
## P15992 1.121588e-12 4.385408e-10
## P39954 4.810014e-12 1.504572e-09
## Q12443 9.260675e-12 2.413949e-09
这里的 P.Value 和 adj.P.Val 来自 limma,而光谱计数调整的 sca。P.Value 和 sca.adj.pval 是 DEqMS 算法的输出。根据 limma 和 DEqMS,对于对比 his4 vs met6 ,有多少蛋白质通过了我们的筛选标准:
## [1] 148 13
## [1] 154 13
与 t-检验相比,这两种方法在通过阈值的蛋白质数量上都有显著增加。这通常是好的,可能是更有趣的蛋白质。
在几个额外的操作之后,我们可以看火山图。通过 DEqMS,MET6 和 HIS4 蛋白得到极高的负 Log-p 值,因为这些蛋白具有多个 PSM,这是由于它们在样品中的高丰度而不是各自的敲除:
作者图片
作者图片
基因集合富集分析
结果的生物学解释将高度依赖于实验系统和研究的目的,这里面有艺术的成分!开始功能解释的一个广泛适用的方法是基因集合富集分析,或GSEA【3】,通过包 fgsea 实现 R。
Fgsea 需要一个排序的基因列表来执行富集分析。为了纳入统计结果和菌株间的倍数变化,我们将计算来自 DEqMS 和 log-FC 的调整后 p 值的乘积,并将其用于排名。我们可以继续使用同一个的 4-met6 对比,看看 10 个负面排名值最低的基因:
## HIS4 RTN2 SIP18 HSP12 GCV2
## -39.522063 -10.911242 -8.174439 -7.404254 -6.980468
## SHM2 HSP26 SAM1 SAH1 GPH1
## -6.839459 -5.945264 -5.255033 -4.994689 -4.869912
fgsea 的另一个必需输入是一个文件,它用相应的基因注释基因集(途径、功能组)。我们可以从 Uniprot 下载广泛使用且全面的酵母基因本体注释【4】;基因本体联盟的数据可以在知识共享许可下获得。我已经把下载的文件转换成了。gmt 格式,这是 GSEA 的典型格式,使用基因符号作为主要标识符,因为它们比 Uniprot 成员更能为生物学家提供信息。
使用注释文件和我们的基因排序,我们使用默认设置运行富集:
结果文件中的途径得到富集分数(ES)和归一化富集分数(NES),这些分数指向差异丰富基因中具有最显著过度表达的途径。让我们来看看在负 ES 区最显著富集的 10 条途径:
丰富的 p 值和调整的 p 值并不重要,这可能取决于数据集的大小、注释的类型以及我们应用的设置。我们可以进一步研究其中一条具有最显著负 ES 的途径:
作者图片
哪些基因属于这一途径:
"ADE1" "ADE12" "ADE13" "ADE16" "ADE17" "ADE2" "ADE3" "ADE4" "ADE5,7" "ADE6" "ADE8" "MIS1" "MTD1"
"PRS1" "PRS2" "PRS3" "PRS4" "PRS5"
GSEA 可以成为生物学解释的一个方便的起点。至于它的终点,只有天空才是极限!
结论
我展示了一个 R 脚本的主要亮点,该脚本使用 DEqMS 算法进行差异丰度分析,对同量异位标记蛋白质组数据进行基本分析和可视化。完整的脚本和示例数据可以在 Github 存储库中找到。
参考
[1] M.E. Ritchie,B. Phipson,D. Wu,Y. Hu,C.W. Law,W. Shi 和 G.K. Smyth, limma 为 RNA 测序和微阵列研究提供差异表达分析 (2015),核酸研究, 43 (7),e47 — 开放获取。
[2] Y. Zhu,L.M. Orre,Y.Z. Tran,G. Mermelekas,H.J. Johansson,A. Malyutina,S. Anders 和 J. Lehtiö,DEqMS: 差异蛋白质表达分析中精确方差估计的方法 (2020),分子细胞蛋白质组学,19(6):1047–1057—开放存取。
[3] A. Subramanian 等 基因集合富集分析:一种基于知识的方法,用于解释全基因组表达谱(2005)PNAS,102 (43),15545–15550。
[4] M. Ashburner 等人。基因本体论:统一生物学的工具(2000)Nat Genet,25(1),25–9。
拥抱模糊
数据素养不仅仅是阅读图表的能力
D 数据对商业成功越来越重要。公司在数据基础设施和数据专业人员方面投入巨大。然而,主要的舆论制造者警告说,这可能还不够。
数据素养,即阅读、处理、分析和交流数据的能力,是大多数组织所缺乏的。因此,这些组织无法充分利用数据这一至关重要的业务资源来获得业务优势。主要的声音现在建议如何评估数据素养和如何提高数据素养。
数据倡导者和狂热分子
有许多倡议旨在培训人们成为数据素养。他们中的大多数都做得很好,解释了:数据的价值;数据和业务之间的链接;以及教授技术技能或制作、阅读和解释图表的能力。这当然是至关重要的,因为它有助于增加数据倡导者的数量。
然而,在备受称赞的“成为数据驱动型”或“做出基于数据的决策”的追求中,人们可能会变得过于热情。他们开始狂热而盲目地相信数据。
我们中的每一个人都在进行一场情绪化的、激烈的讨论,双方都使用相同的数据。但是,解释完全不同。并且,用非黑即白的思维,很容易给对方贴上无能和明目张胆错误的标签。然而,事情就这么简单吗?
虽然数据狂热者通常是出于好意,但他们实际上是在损害数据。
数据是真实世界的表现
在我看来,打破这一僵局的关键在于理解数据只是真实世界的一种表现。有时非常准确,有时不太准确。
让我解释一下。
我们在学校里被教导数学是一门精确的科学——一门证明其结果绝对精确的科学。
但并非所有数据都是如此。
在数学中,我们从现实世界的物体中抽象出来。然而,在商业环境中,我们使用数据来代表真实世界。
一个重要的区别。
也就是说,我们都必须问自己的实际问题是— 数据在多大程度上反映了真实世界?或者更好的是,代表现实世界中与当前问题相关的部分的数据有多好?
需要注意什么?
这不是一个简单的问题。世界的复杂性是巨大的。因此,任何简化总是伴随着一系列的警告!
务实地说,让我提出几个出发点。
问题本身 —有多复杂?例如:测试销售额是否比去年有所增长很简单。检测长期趋势有点复杂。将销售额归因于个人因素可能非常复杂。
此外,许多问题涉及随机性和不确定性。这个问题能被完美地解决吗,或者我们只是不得不接受这样一个事实,即结果在一定程度上总是不可预测的?
例如,在给定航班时刻表、飞机任务等的情况下,有效地给飞机分配机组人员。纯粹是一个确定性问题——不包含任何随机性。然而,预测消费者的购买行为是不确定的。
在大多数情况下,我们不能被问题固有的随机性和不确定性所麻痹。相反,我们必须接受这种模糊性。
“预测是非常困难的,尤其是当涉及到未来的时候。”——尼尔斯·玻尔
数据。在理想的世界中,我们应该拥有所有可用的数据。完美的质量。可靠。并准备好支持决策过程。
但事实从来都不是这样。
我们将永远不得不与现实的简化模型打交道。我们将永远不得不做出假设。我们将永远不得不质疑我们使用的数据的质量。
假设您想预测将有多少游客参观您所有的滑雪胜地。你可以使用滑雪者人数的历史数据。但是,每个度假村可能使用不同的售票系统,因此合并数据可能不容易。你也知道游客的数量很大程度上取决于天气。所以,你可以在你的模型中加入天气预报,但是你能相信它吗?
棘手。但是,这并不意味着我们放弃。
这只是意味着在使用数据时总是有免责声明。我们不能把它藏在众所周知的地毯下。相反,我们需要清楚地阐述和“解释”这些缺点,就像会计师在年度财务报表附注中所做的那样。毕竟,这是导致更深层次的理解和适用性和可复制性。
因此,努力捕捉数据模型中最具影响力的因素,并最大限度地提高数据质量,以增加最终数据解决方案的可靠性。
上下文。真空中什么都不存在。更不用说我们数据科学家面临的商业难题了。总会有更大的图景影响对结果的解读。
想象一下,建立一个倾向模型,用于推广一种产品,却发现它正在蚕食你的其他更有利可图的产品。
现实世界不是一个简单的、静止的地方。新的环境出现了(通常在很久以后才出现),这可以从根本上改变我们看待事物的方式。正如历史经验会影响我们今天看待事物的方式。此外,作为个人和团队,我们会受到多种认知(有意识和无意识)偏见的影响。因此,花尽可能多的时间去理解背景,就像你创造解决方案一样。
飞船。多年来,数据专业人员制定了许多指导方针和最佳实践。为了减少你项目中的错误,我强烈建议你遵循它们。但是它们不是防弹的。通常,由于逻辑原因,他们不能被严格遵守。伟大的数据科学是艺术和科学的炼金术——艺术通常是在许多许多错误和许多许多用例之后才发展起来的。这就是为什么它是一种工艺的原因。
模特们。随着机器学*越来越多地被采用,我们也需要了解这些技术的局限性,尽管它们被认为很“性感”。所有这些都带有理论上的假设,在现实世界中很少遇到。所有人都从他们过去获得的数据中学*。这种训练数据(尽管有其局限性和偏见)对现实情况下的模型性能有着根本性的影响。
在中欧开发的信用评分模型可能在西欧或非洲不适用。就像根据美国数据训练的 NLP 模型在日本行不通一样。不仅仅是因为不同的语言。
未知的未知数。我上面讨论的很多内容都触及了所有数据科学家面临的已知未知。但是我们不要忘记未知的未知的非穷尽列表!有许多因素会影响基于数据的决策在实际情况下的效果。所以,要适应。倾听——倾听你内心的指南针,倾听你的团队和同事。甚至听听狂热分子的观点,因为他们的观点很可能会指出你自己的假设中的优点和缺点。这是我结论的一个简洁的延续。
不要轻易发表意见
到目前为止,前景似乎不太乐观!但这将是一个不公平的结论。
即使有不完美的地方,数据也是一个非常有价值的工具。一种工具,如果使用得当,可以对增加公司利润或降低风险产生重大影响。
它只是带有许多模糊性,不应该害怕,而是应该接受。
因此,在你开始狂热地捍卫一个数据产品(无论是商业智能仪表板还是人工智能决策引擎)的输出之前,确保你已经考虑过,并在必要时传达了一系列警告。数据可以让很多事情自动化。批判性思维不是其中之一。
通过遵循最佳实践过程并强调您在面对模糊性时所做的假设(在您的方法和模型中),您将与您的同事展开更丰富的讨论,从而为手头的业务问题提供更好的分析解决方案。
最终,掌握数据意味着适应与数据相关的模糊性、不确定性、随机性和未知性。
一如既往,我无限感激 切尔西·威尔金森 耐心地将我的想法塑造成可出版的格式。
感谢阅读!
欢迎在评论中分享你的想法或观点。
机器学*和深度学*的出现
数据、计算资源、算法和开源框架
马特·保罗·卡塔拉诺在 Unsplash 上的照片
深度学*(DL)和机器学*(ML)是人工智能(AI)的两个子集。ML 不同于传统的编程,它能够从数据中学*,而不需要提供明确的规则。它可以识别数据背后隐藏的模式。ML 对于分析结构化数据很方便。当我们考虑模型的复杂性时,DL 超越了 ML。它提供了一种人工神经网络(ANN)方法。DL 对于分析非结构化数据(图像、文本、语音等)非常有用。).ML 不太适合这些类型的数据。
在过去的几十年里,ML 和 DL 一直存在于我们身边。这两种技术的出现是由于以下因素:
- 大批量数据
- 计算资源
- 算法
- 开源框架
这篇文章是关于这四个导致 ML 和 DL 出现的因素的。
大量数据
由 Alexander Sinn 在 Unsplash 上拍摄
在这个数字时代,智能设备(手机、笔记本电脑等)的使用。)和 web 服务有了显著的增长。这产生了大量不同格式的数据,如文本、图像、语音、数字等。大多数人使用社交媒体。当你在一个帖子上贴上一个赞,也是一种数据。当您对从在线网站购买的产品进行评论时,它是一种文本数据,可用于获得关于客户满意度的宝贵见解。
数据是 ML 和 DL 中最有价值的资产。最*,ML 和 DL 技术以各种方式用于处理数据:
- 数据提取:数据不到我们手里。我们应该从各种来源提取它们。这些来源包括网页、数据库、图像等。ML 和 DL 技术被广泛用于从这些来源中提取数据。
- 数据预处理:现实世界的数据大多是杂乱无章的。在使用它们之前,我们需要对它们进行预处理。缺失值处理和异常值检测是最基本的数据预处理任务。除了一般方法之外,ML 和 DL 技术也用于此。
- 建模:为了分析数据,我们需要创建模型。大部分都是 ML 和 DL 的型号。这些模型可以从数据中学*,并对新的未知数据进行预测。
计算资源
计算资源的可用性导致了 ML 和 DL 的出现。拥有大量超参数的 ML 模型需要强大的计算能力。层数多的深度学*模型也需要强大的计算能力。图形处理单元(GPU)提供这些任务所需的计算能力。现在一台 GPU 好的电脑,价格也能买得起。
还有一点就是云计算的存在。你不需要在你的本地机器上有强大的硬件来运行消耗能量的算法。借助云软件,你可以随时随地运行它们。大多数服务都是免费的。但是,在使用这些服务时,您需要支付一些额外的费用。AWS 和微软 Azure 是当今众所周知的云计算服务。
算法
马库斯·斯皮斯克在 Unsplash 上拍摄的照片
算法上的重大改进使得 ML 和 DL 技术更加有用。如今,算法可用于任何类型的数据。线性回归、逻辑回归、支持向量机等可用于线性数据。决策树、随机森林、XGBoost(基于树的算法)可用于非线性数据。随机森林和 XGBoost 算法非常强大,因此它们的预测非常准确。
卷积神经网络(CNN)可用于图像数据。递归神经网络(RNNs)可用于文本或序列数据。
为了利用神经网络,你需要有大量的数据。一般最大似然算法的性能不会随着数据量的增加而提高。如果您有大量数据,使用神经网络将提高模型的性能。
开源框架
在 Unsplash 上由 Hitesh Choudhary 拍摄的照片
上述算法和许多其他算法都可以通过开源框架免费获得。Python 有 ML 和 DL 任务的高层框架。
对于一般的 ML,Scikit-learn 是最好的框架。它的语法非常一致。它包括许多机器学*模型,可以使用几行代码轻松实现。Google 开发的 TensorFlow 和 Keras 是最流行的 DL 框架。脸书的 PyTorch 是 DL 框架的另一个很好的选择。
这些框架背后有强大的社区。他们经常给这些框架添加新的东西。高度标准的文档也是可用的,因此任何人都可以参考它们来学*更多关于这些框架的知识。
下次见,祝大家学*愉快!同时,你可以在以下网址阅读我的其他帖子:
【https://rukshanpramoditha.medium.com
鲁克山·普拉莫迪塔
2021–07–10
数据中的表情符号🤓
你需要知道的关于你的数据库或数据集中的表情符号的一切。
由作者创建的标题
让我们面对现实吧,现在是 2021 年,表情符号是不可避免的。从聊天到产品评论,有时甚至是用户名,你都可以看到它。今天我们将回答一些问题:
什么是表情符号
每个人都知道表情符号是什么,但它们到底是什么?它们是图像还是字体,为什么不同的系统会以不同的方式显示同一个笑的表情符号?对于初学者来说,表情符号是一个字形,把它当成一个字体。在每个笑脸的背后,表情符号是一个十六进制代码点。
拿🤓“书呆子脸”表情符号为例,其十六进制码位(用 U+ 表示)为 U+1F913 如表情百科所列。每个代码点都引用一个被普遍理解的叫做 Unicode 的字典上的东西。如果字典里有这个词,你会得到它的定义。
如果你试着在英语词典中查找一个中文单词,你将得不到那个单词的定义。这个概念适用于 Unicode 在您的系统上的工作方式。如果您的系统不包含代码点的标志符号,它将无法显示🤓。就像你对来自剑桥或牛津的同一个英语单词有不同的定义一样,不同的系统也有它们自己的字形设计,而不会偏离原意。
表情符号是如何存储在数据库中的
现在你知道表情符号是如何由计算机系统表示的,我们如何存储或如何存储到数据库中?我们知道表情符号只是十六进制代码点,我们是否需要有一个特殊的 Unicode 列类型,或者我们是否将它存储为字符串并在以后解析为表情符号?
在大多数数据库中,你可以通知引擎这个特殊的字符块应该被存储为 Unicode 而不是字符串,这个过程叫做 \escape 。下面是几个在数据库中转义和存储表情符号的例子。不同的数据库在存储转义字符方面有稍微不同的要求,所以一定要仔细阅读每个关于转义的数据库文档。
-- POSTGRES
SELECT ('\+01F913'), (U&'\+01F913'), (U&'\d83e\dd13')-- POSTGRES RESULT: "\+01F913", "🤓", 🤓" -- BIG QUERY
SELECT ('\U0001F913')-- BIG QUERY RESULT: "🤓"
如 Postgres 示例所示,第一列按字面意义存储为字符串' +01F913 '。通过使用U&作为第二列进行转义,数据库现在明白了这个字符块不是一个常规字符串,而应该理解为一个十六进制序列。
作者图片
注意我们在 1F913 前面加了一个 0,这是因为这里的 Postgres 文档规定 Unicode 转义需要“一个反斜杠,后跟一个加号,再后跟一个六位数的十六进制码位号”。我们的原始代码点只有 5 位数字,因此我们必须在不改变含义的情况下,对代码点进行零填充,使其成为 6 位数字。第三列是代码单元代理对的示例。代码单元的代理项对构成一个代码点。正如在同一 Postgres 文档中提到的,代理对(16 位+16 位)的存在是为了组成大于 U+FFFF (16 位)的码位。
f(十六进制)= 1111(二进制)= 4 位
FFFF = 1111 1111 1111 1111 = 16 位
这使得可用的代码点数量增加到了一百万以上。然而,这是我们最不关心的,因为 Postgres 会在存储它之前将代理对组合成一个代码点。最后一列基本上只是以字节存储十六进制代码。
BigQuery 也是如此,在他们的文档中这里,他们指出他们的 Unicode 转义需要 8 位数字。因此,如上例所示,我们将对 Unicode 进行零填充。
在我的数据集或数据库中保留表情符号有多重要
作者图片
两份非常相似的英文文本,但在表达方式上却大相径庭。随着这种趋势的继续,表情符号越来越成为表达情感的方式,NLP 模型和数据集应该适应存储和处理这种信息。新的表情符号不断增加,随着变体或肤色的引入,复杂性也增加了。适应每一个表情符号可能并不容易,但是值得注意的是unicode.org有一个表情符号频率列表,可以帮助我们了解我们应该注意的少数表情符号。
毫无疑问,在数据库中存储表情符号已经成为一种常见的做法,而且应该如此。谷歌有一份白皮书,可以在这里找到该白皮书指出,启用包括表情符号在内的最大可用字符集是存储用户密码的良好做法之一。大多数在线购物平台上的产品名称和描述也包含表情符号,我们绝对不希望在用户保存表情符号或在你为自己的商业案例进行推理时将它们删除。
下一步是什么🤷
表情符号将会一直存在,这取决于开发者如何管理这些额外的信息,也取决于数据从业者如何理解这些信息。
图片取自作者的电报信息
表情符号已经成为我们日常对话中不可或缺的一部分,在某些情况下,它还是问题的答案。管理这些信息,无论是用相关的英文单词替换表情符号,还是将其编码为另一个符号,都是值得考虑的事情。
以下是一些很酷的工具,可能会帮助您更好地理解 Unicode(排名不分先后):
基于情感的 C-GAN 艺术生成
AI 能有创造力,通过艺术理解情感吗?
基于深度学*的艺术生成:风景+正面情感,作者图像
介绍
随着基于深度学*的图像生成和情感分类解决方案的出现,我在想,我们是否可以将这两个目标结合起来,建立一个模型,以一种简单的情感(积极、消极和中性)作为输入,并生成一件艺术作品,以某种方式整合之前提供的情感。
这个项目带来了计算机科学和艺术中的哲学问题。
当我们看一幅画时,一种情绪是仅仅来自视觉刺激还是有其他无意识的因素影响我们的感觉?如果是这样的话,这些视觉刺激一般会唤起一种共同的感觉吗,不管观看者是谁?
大多数艺术家和情感专家会拒绝这两个建议,他们会回答说,每个人都有自己的欣赏,受自己的生活经历的指导,情感的起源很难破译。
我将把这些哲学问题留给引言,并希望像神经网络这样令人惊讶的系统能够提取出可能导致基本情绪的一般模式。例如,深色可能会引起负面反应,而亮色/彩色可能会引起正面反应。
令人惊讶的是,我第一次想利用数据集中隐藏的偏见,并最终利用它们来创造艺术!
数据集
→ Wiki-Art :视觉艺术百科(https://www.wikiart.org/,公开可用数据集)
Wiki-Art 是一个大型数据集,包含来自 100 多个国家的博物馆、大学、市政厅和其他市政建筑的绘画图像。这些作品大多不公开展出。维基艺术包含了 195 位不同艺术家的作品。该数据集有* 100,000 幅图像。该数据集在 Kaggle 中可用,并提供各种风格类别:'abstract', 'animal-painting', 'cityscape', 'figurative', 'flower-painting', 'genre-painting', 'landscape', 'marina', 'mythological-painting', 'nude-painting-nu', 'portrait', 'religious-painting', 'still-life', 'symbolic-painting'.
→ 维基-艺术情感
由 Art. Saif M. Mohammad 和 Svetlana Kiritchenko 唤起的情感注释数据集。在2018 年 5 月日本宫崎第 11 版语言资源与评价会议论文集(LREC-2018) 。
wikiartemotion数据集的汇总表(Mohammad、Saif 和 Svetlana Kiritchenko)。
Wiki-Art Emotions 由 4105 张用情感标注的艺术图片组成,由 WikiArt 构建而成。每个图像都被标注了至少一个到二十个情感类别。
为了简单起见,我合并了几种情绪,每张图片只有 3 种最终情绪(积极的、消极的和中性的)。例如,我把“后悔”、“恐惧”合并成一个消极的类别。同样,我将“乐观”、“爱”归入积极的类别。最后,对于中性类别,我合并了“中性”、“谦逊”等标签。
我意识到,只有 3 个类别和 4105 幅图像,我定制的维基艺术情感版本不足以训练一个生成性对抗神经网络(该模型将遭受崩溃模式)。
因此,我决定创建一个图像分类器,它将一张图像作为输入,并将其分为 3 种情绪类别(积极、消极和中性),这样我就可以使用这个分类器来标记原始 WikiArt dataset 中的其他数据。我将在下一节讨论我用来构建图像-情感分类器的模型的架构。
总的来说,我最终得到了一个由 95808 张图片组成的维基艺术情感,这 95808 张图片被标记为 3 种基本情感:积极(43792 张图片)、消极(33091 张图片)、和中性(18925 张图片)。
因为我已经有了构建情感到图像生成器的目标,所以我不想通过允许选择生成的假画的风格来增加任务的复杂性。
相反,我决定构建几个情感到图像生成器,分别用于以下风格:抽象(15000 张图像)、花卉绘画(1800 张图像)、风景(15000 张图像)、和肖像(15000 张图像)。
以下是每种所选风格的“情绪”分布:
作者图片
模型
正如您在上一节中可能已经猜到的,我使用了两个模型来实现生成基于情感的艺术的目标。我创建的第一个模型是“图像到情感”分类器,我将其用作数据增强的标记工具,第二个模型(也更有趣)是“情感到图像”生成器。
图像到情感
使用来自原始 wiki-art 情感数据集的 4105 幅图像,我使用 Pytorch 训练了一个预训练的深度卷积神经网络。
我用于迁移学*任务的基础架构是 VGG16:
除了这个预训练的 ConvNet,我还添加了一个具有 3 个输出神经元的多层感知器(针对每种情绪)。我完全冻结了 VGG16,因为我假设最初学*的特性(使用 Imagenet)足够通用,可以提取绘画中常见的视觉模式。
我还利用 Pytorch 变换来使用数据扩充技术(裁剪、翻转等)。
以下是我获得的训练图:
作者图片
很明显,我的模型遭受了过度拟合,因为验证曲线是平坦的,并且不遵循学*曲线。正如我所料,这意味着我的“图像到情绪”分类器未能提取一般特征来分类情绪。
经过几次尝试和实验,我意识到,以这样的数据量和任务的复杂性,我可能不会得到更好的结果。同样很有可能的是,数据本身非常主观(正如艺术专家倾向于解释的那样),对于神经网络来说没有意义。
所以我决定继续前进,希望我的 VGG16 在用大型 Wiki-Art 数据集标记剩余的艺术图像方面不会太差。注意,我在数据集部分描述的统计数据是由这个模型生成的。
情感到图像
使用新标记的绘画图像,我决定训练 4 个情感到图像生成器(每种风格一个:抽象、花卉绘画、风景和肖像)。
对于情绪到图像模型,我决定使用条件生成对抗性神经网络(CGAN),下面是它的架构图:
CGAN 架构(来自 Alptekin Temizel )
它是一个有两个演员(像大多数 GANs 一样)的神经网络训练,一个被称为生成器(G),负责生成与给定的 C 类(这里是我们基于情感的绘画)相关的 64x64 假图像,另一个被称为鉴别器(D),负责检测图像是真是假。
发生器 G 由噪声向量 Z(通常称为潜在空间)与类别 C(这里是情感)的编码向量连接而成。
鉴别器 D 由两个部分提供:由 G 产生的应该属于类别 C 的假图像和实际上属于类别 C 的真实图像。这两个图像,假图像和真实图像,与类别 C 的编码版本连接在一起(使得 D 也具有要区分的类别的信息)。
使用反向传播和梯度下降来训练发生器 G 和鉴别器 D。
关于 GANs(以及 CGANs)有趣的事情是,G 更善于从 D 的反馈中学*,同样,D 通过观察 G 的工作更善于检测伪造的图像。这就像一场战斗,两个战斗者在整个游戏中学会了压制对方。
在训练过程中,我们可以看到发电机越来越好:
风景(左)和花卉(右)G 训练,图片作者
最后,一旦训练完成,我们只保留生成器 G 来生成作为输入提供的给定类 C 的新的假图像。
换句话说,我可以只通过向我训练过的生成器提供一个噪声向量和一个编码的情感类来生成一幅应该具有“正面”(或“负面”,或“中性”)类的绘画(风景、肖像等)。
推理
在这篇文章的最后,我与你分享我在 Github 库中的代码,这样你就可以生成你自己的基于情感的绘画。
git clone [https://github.com/MarvinMartin24/CGAN-Emotion-Art/](https://github.com/MarvinMartin24/CGAN-Emotion-Art/)# python3 generator_64.py STYLE EMOTION NUM_IMG_TO_GENERATEpython3 generator_64.py landscape positive 20
python3 generator_64.py portrait negative 2
结果
鲜花+正片(图片由作者提供)
肖像+负片(图片由作者提供)
风景+正片(作者图片)
抽象+神经(作者图片)
我为你生成了几幅基于情感的画作,它们的伟大之处在于它们在风格上相当现实(抽象、风景、肖像和花卉)。然而,相对于输入中提供的情感,结果的质量相当令人失望。
看起来 CGAN 并没有产生任何特定情感的特征。这并不奇怪,因为训练数据集本身是由 VGG16 标记的,而 vgg 16 也不理解它。我希望颜色或形状会随着情绪的变化而变化,但事实似乎并非如此。再说一遍,这个项目纯粹是为了好玩和探索,我也预料到结果会是那样。
密码
部署在 Heroku 上的 GitHub 存储库和 Flask Web 应用:
https://github.com/MarvinMartin24/CGAN-Emotion-Art
图像到情感 : VGG16(识别情感,不考虑艺术风格)
https://colab.research.google.com/drive/1ZqtZx9DP0FoW0SyzHikjCOHjzPznFf0d?usp=sharing
情感到图像:条件生成对抗性神经网络(基于情感的人像)
https://colab.research.google.com/drive/1dQcHAClX1fjBNvt-hMauWEg7x4m7Kq3V?usp=sharing
类似项目
→谭,万良等 用于对绘画所唤起的情绪进行分类的 CNN 模型 。技术报告,美国斯坦福大学 SVL 实验室,2018 年
→ Alvarez-Melis、David 和 Judith Amores。《情感甘》:以情感促发艺术的生成。2017 neur IPS 面向创意和设计的机器学*工作坊。2017.
结论
我真的很喜欢探索艺术发挥作用的深度学*的这一部分。我很高兴看到生成的图像不断改进。就结果而言,我会再次得出结论,深度学*不是魔法,我们需要定性数据和明确的任务来帮助模型理解任务。在这个项目中,这两个要求都不存在。然而,从纯粹的艺术角度来看,这个实验非常有趣。
重要提示:
Wiki-Art emotion 是根据本文开头讨论的特定研究目标创建的。不建议将数据集用于商业应用,对个人进行推断,以及在数据差异很大的环境中构建机器学*系统。不要使用数据集从人们的面部表情中自动检测他们的情绪。关于数据集的更多问题,请访问http://saifmohammad.com/WebPages/wikiartemotions.html。
请随时在评论区与我分享您的反馈和想法💻🔥💻。
谢谢大家!
使用 Tkinter GUI 增强 Docker 的能力
通过 Docker 的 GUI 对于许多应用程序来说是至关重要的。
边境摄影师在 Unsplash 拍摄的照片。
在几种情况下,创建一个可以在 Docker 中访问的图形用户界面(GUI)会很有用,例如在开发需要可视化图像和视频的机器学*应用程序时。但是,需要几个步骤才能在主机上查看 GUI。在本文中,我们将使用 Tkinter 在 Python 中创建一个简单的 GUI,可以通过 Docker 访问它。
目录
在本文中,我们将涵盖几个步骤。前两步是在 Docker 中运行 Tkinter GUI 应用程序所必需的。其他步骤是可选的,用于获得如何使用 Tkinter 和 Docker 增强开发阶段的额外信息。这些步骤是:
- 使用 Tkinter 创建非常简单的应用程序。
- 创建一个简单的 Docker 映像,并在 Docker 容器中运行 Tkinter 应用程序。
- 使用 shell 脚本简化 Docker 运行命令。(可选)
- 允许 GUI 的持续开发。(可选)
先决条件
本文只在 Linux 上测试过,可能不适用于其他操作系统。你也应该已经安装了 Docker。
目录结构
下面是目录结构的概述。这些文件将用于本文,因此您可以预先创建它们。
app/
├─ tkinter_app.py
Dockerfile
run.sh (optional)
Tkinter
因为本文的目标是演示如何通过 Docker 访问 GUI,所以创建了一个如下所示的简单窗口。这个 GUI 包含文本和一个退出应用程序的按钮。
简单的 Tkinter 应用程序。
为了创建这个简单的应用程序,使用了下面的代码。
创建简单 Tkinter 应用程序的代码。
码头工人
然后,应该创建一个 docker 文件,其中包含以下代码。在这个 Docker 文件中,我们将 Docker 映像的基础设置为 Python 3.8.12 的精简版本。之后,我们更新包信息并安装 Tkinter。最后两行用 Python 运行命令 /app/tkinter_app.py 。
创建 docker 文件的代码。
构建 Docker 映像
在 Docker 文件所在的目录中,应该执行以下命令来构建 Docker 映像:
docker build -t tkinter_in_docker .
上面的命令构建了一个名为 tkinter_in_docker 的 Docker 映像。
运行 Docker 容器
既然已经构建了 Docker 映像,我们可以运行 Docker 映像的 Docker 容器。要做到这一点,需要获得许可,这可能会有一些安全问题。在本文中,我们将采用一种安全的方法。然而,还有其他更安全的方法,在这里有所涉及。在本文中,我们将使用以下命令输入我们自己的用户凭证:
# Read the above before pasting
docker run -u=$(id -u $USER):$(id -g $USER) \
-e DISPLAY=$DISPLAY \
-v /tmp/.X11-unix:/tmp/.X11-unix:rw \
-v $(pwd)/app:/app \
--rm \
tkinter_in_docker
在上面的代码中,- u 用于设置用户名,- e 用于设置显示,第一个 -v 在 X11-unix 中创建了一个卷来提供显示,第二个 -v 创建了另一个卷,Docker 容器可以在其中访问我们的 Tkinter 应用程序,最后我们使用- rm 来在使用后自动删除容器。
使用 Shell 脚本简化 Docker 运行(可选)
上面介绍的复杂的 Docker run 命令可以通过创建一个简单的 bash 文件来简化,如下所示:
简化 Docker 运行命令的 Shell 脚本。
要使 shell 脚本可执行,您可以运行以下命令:
chmod +x ./run.sh
现在,您只需使用以下命令即可运行它:
./run.sh
GUI 的持续开发(可选)
在我们的场景中,我们为应用程序 tkinter_app.py 和准备了一个文件,因此几乎没有理由使用 Python 在 Docker 容器中手动运行它。然而,对于较大的应用程序,可能希望在 Docker 环境中运行单独的脚本,而不是每次都运行同一个脚本。
要允许持续开发,请遵循以下步骤:
- 注释 Docker 文件中的第 11 行和第 12 行,防止 Docker 自动运行 tkinter_app.py 。
- 使用以下 Docker build 命令用新的更改来重建映像:
docker build -t tkinter_in_docker .
- 要运行 Docker 容器并与之交互,请使用以下命令:
# Read "Running Docker Container" before running this command
docker run -u=$(id -u $USER):$(id -g $USER) \
-e DISPLAY=$DISPLAY \
-v /tmp/.X11-unix:/tmp/.X11-unix:rw \
-v $(pwd)/app:/app \
--rm \
-it \
tkinter_in_docker \
/bin/bash
上面的命令基于前面的 Docker run 命令。-添加 it 是为了在 Docker 环境中获得一个交互终端,添加 /bin/bash 是为了使用 bash 运行它。如果需要,您可以用 shell 脚本中的命令替换上面的命令,这在前面已经介绍过了。
- 导航到位于/app/中的应用程序目录。
- 使用命令运行应用程序:
python tkinter_app.py
因为我们已经通过 Docker run 命令设置了一个共享卷,所以我们现在能够在本地使用一个 IDE 并不断地改变我们的应用程序,而不需要重新启动 Docker 容器。
注意 :要分离 docker 容器(退出 Docker 容器),按 Ctrl+d。
结论
在本文中,您使用 Tkinter 开发了一个简单的 GUI 应用程序,您可以通过 Docker 运行它。这是一项需要掌握的强大技能,尤其是在数据科学领域,因为运行交互式隔离 GUI 环境的可能性很大。现在,您应该能够进一步开发自己的应用程序了。
感谢阅读
这是我第一篇关于媒体的文章,因此非常感谢反馈。
利用 RNNPool 运算符和 SeeDot 编译器在微型微控制器上实现精确的计算机视觉
理解大数据
关于使用微软 EdgeML 存储库在 ARM Cortex-M 类设备上训练、量化和部署精确人脸检测模型的教程。
摘要
卷积神经网络在计算需求方面很昂贵,但在工作 RAM 需求方面更是如此。主要原因是它们有很大的中间激活图。这是在低资源设备上部署的主要障碍。所以这给我们留下了两个选择:
- 减少通道数量,根据经验观察,这会导致总体性能不佳。
- 减少行/列的数量,这就引出了一个问题,我们如何通过池化/步长卷积来减少行/列?
当应用标准池操作来快速减少行/列的数量时,会导致准确性的严重下降。 RNNPool 是一个新的池操作符,可以显著减少行/列,而不会显著降低准确性。我们的工作已经在 NeurIPS 的 20 篇论文和 NeurIPS 的聚焦演讲中发表。这篇微软研究博客文章对新运营商及其含义进行了高度概括。
这篇文章讲述了如何使用 RNNPool 来减少 CNN 的内存需求。我们通过走极端来说明它的多功能性,即在一个微型微控制器上实现人脸检测。在资源受限的环境中部署解决方案的另一个关键方面是量化模型(如 Joel Nicholls 在此清晰地解释的那样,以便它可以在没有浮点运算的微控制器上执行,利用 8 位和 16 位整数计算的速度和内存优势。这篇文章还展示了微软印度研究院的 SeeDot (现在的狡猾天狗)编译器的功能,它可以用来量化基于 RNNPool 的 PyTorch 模型,并生成具有最佳内存使用的 C 代码。
最后,这篇文章展示了如何将 RNNPool 和 SeeDot 放在一起,在一个 ST 微控制器上训练和部署人脸检测模型,该微控制器包含一个 ARM Cortex-M3/M4 级微处理器(或更高版本),其 RAM 小于 256 KBs。
RNNPool 运算符:什么和为什么?
池运算符通常用于对激活图进行下采样。典型的池操作符是平均池和最大池。它们在感受野上执行“粗略”操作,因此是不准确的。
RNNPool 使用 4 遍 rnn 聚合大块激活图。(作者供图)
rnn 是有趣的,因为它们需要少量的内存,但在计算方面可能比卷积更昂贵。然而,通过大幅缩小激活图的大小,RNNs 可以将几种标准视觉架构的总计算量减少 2-4 倍。
╔═══════════════╦═════════════════════════╦═════════════════════════╗
║ Model ║ Standard Models ║ RNNPool-based Models ║
╠═══════════════╬═════╦══════╦══════╦═════╬═════╦══════╦══════╦═════╣
║ ║Acc. ║Params║ RAM ║MAdds║Acc. ║Params║ RAM ║MAdds║
║ ║ ║ ║ ║ ║ ║ ║ ║ ║
║MobileNetV2 ║94.20║2.20M ║2.29MB║0.30G║**94.40**║**2.00M** ║**0.24MB**║**0.23G**║
║EfficientNet-B0║96.00║4.03M ║2.29MB║0.39G║**96.40**║**3.90M** ║**0.25MB**║**0.33G**║
║ResNet18 ║**94.80**║11.20M║3.06MB║1.80G║94.40║**10.60M**║**0.38MB**║**0.95G**║
║DenseNet121 ║**95.40**║6.96M ║3.06MB║2.83G║94.80║**5.60M** ║**0.77MB**║**1.04G**║
║GoogLeNet ║**96.00**║9.96M ║3.06MB║1.57G║95.60║**9.35M** ║**0.78MB**║**0.81G**║
╚═══════════════╩═════╩══════╩══════╩═════╩═════╩══════╩══════╩═════╝
cImageNet-10 数据集上有无 RNNPool 层的推理复杂度和准确性比较。
由于 RNNPool 是一个更“细粒度”和更好学*的池操作符,它也可以导致更高的准确性。
╔═════════════════════════════════╦══════════╦════════╦════════╗
║ Method ║ Accuracy ║ MAdds ║ Params ║
╠═════════════════════════════════╬══════════╬════════╬════════╣
║ Base Network ║ 94.20 ║ 0.300G ║ 2.2M ║
║ Last Layer RNNPool ║ 95.00 ║ 0.334G ║ 2.9M ║
║ Average Pooling ║ 90.80 ║ **0.200G** ║ **2.0M** ║
║ Max Pooling ║ 92.80 ║ **0.200G** ║ **2.0M** ║
║ Strided Convolution ║ 93.00 ║ 0.258G ║ 2.1M ║
║ ReNet ║ 92.20 ║ 0.296G ║ 2.3M ║
║ RNNPoolLayer ║ 94.40 ║ 0.226G ║ **2.0M** ║
║ RNNPoolLayer+Last Layer RNNPool ║ **95.60** ║ 0.260G ║ 2.7M ║
╚═════════════════════════════════╩══════════╩════════╩════════╝
在 ImageNet-10 数据集上,各种下采样和池操作符对 MobileNetV2 的准确性、推理复杂性和模型大小的影响。
我们可以用 RNNPool 操作符做什么?
我们将了解如何使用 RNNPool 来构建适合部署在微控制器上的紧凑型人脸检测架构。
我们将从流行的 S3FD 架构开始。 S3FD 使用一个 CNN 为基础的主干网络——vgg 16——进行特征提取。为了提高效率,我们使用 RNNPool 和 MBConv 模块来修改这个主干。检测层附着在该网络的不同深度,以检测不同比例的边界框中的人脸。每个检测图层使用单个卷积图层来预测每个空间位置的 6 个输出值,其中 4 个是边界框坐标,2 个是类。
╔═══════════╦══════════════╦═══════════╦═════════════╦════════╗
║ Input ║ Operator ║ Expansion ║ Out Channel ║ Stride ║
╠═══════════╬══════════════╬═══════════╬═════════════╬════════╣
║ 320x240x1 ║ Conv2D 3x3 ║ 1 ║ 4 ║ 2 ║
║ 160x120x4 ║ RNNPoolLayer ║ 1 ║ 64 ║ 4 ║
║ 40x30x64 ║ Bottleneck ║ 2 ║ 32 ║ 1 ║
║ 40x30x32 ║ Bottleneck ║ 2 ║ 32 ║ 1 ║
║ 40x30x32 ║ Bottleneck ║ 2 ║ 64 ║ 2 ║
║ 20x15x64 ║ Bottleneck ║ 2 ║ 64 ║ 1 ║
╚═══════════╩══════════════╩═══════════╩═════════════╩════════╝
引入卷积后开始使用 RNNPool。
EdgeML + SeeDot:模型训练、量化和代码生成
在这里,我们提供了一个端到端的管道,用于在宽面数据集(用 PyTorch 编写的模型)上训练我们设计的模型,在 SCUT-Head Part B 数据集上对它们进行微调,使用 SeeDot 编译器对它们进行量化,并生成 C 代码,这些代码可以定制为部署在一系列低端微控制器上。我们选择这些数据集专门为会议室人数计算场景训练模型。
我们在这里展示了两个可以直接部署在 M4 级微控制器上的示例模型:
- rnn pool _ Face _ QVGA _ Monochrome:一个具有 14 个 MBConv 层的模型,在验证数据集上提供高的贴图得分(预测和实际边界框之间的重叠)。这种型号非常适合精度至关重要的情况。
- RNNPool _ Face _ M4:预测延迟较低的较小模型,具有 4 个 MBConv 层,并且 rnn pool 层的权重稀疏,从而提供合理的映射得分。这种模式非常适合延迟至关重要的情况。
╔═════════════════════╦═════════════════╦═════════╗
║ Metrics ║ QVGA MONOCHROME ║ FACE M4 ║
╠═════════════════════╬═════════════════╬═════════╣
║ Flash Size (KB) ║ 450 ║ **160** ║
║ Peak RAM Usage (KB) ║ **185** ║ **185** ║
║ Accuracy (mAP) ║ **0.61** ║ 0.58 ║
║ Compute (MACs) ║ 228M ║ **110M** ║
║ Latency (seconds) ║ 20.3 ║ **10.5** ║
╚═════════════════════╩═════════════════╩═════════╝
Jupyter 笔记本可以配置在第一个单元中,在两种不同型号和其他训练环境配置之间切换。每个命令都附有详细说明其用途的注释。更详细的解释,也可以参考自述文件。
用于端到端训练、量化和代码生成的 Jupyter 笔记本。
截至这篇博客发表时,我们还在添加对自动化 PyTorch 到 TFLite 转换器的支持,以允许感兴趣的用户使用 RNNPool + SeeDot 构建和训练定制模型,适合在低资源设备上部署。
由微软印度研究院维护的 EdgeML 存储库中提供了将这个端到端管道组合在一起的所有必需代码。另外, Jupyter 笔记本可以在这里访问。
在第一个单元中设置配置并运行整个笔记本后,生成的代码将被转储到EdgeML/tools/SeeDot/m3 dump/文件夹中。因此,我们现在可以在微控制器上部署这些代码。
设置部署环境
我们在 NUCLEO-F439ZI 电路板上部署了我们的模型,并在相同的环境中对我们的代码运行了基准测试。或者,可以选择满足 192 KBs 基本 RAM 可用性和 450 KBs 闪存可用性(对于rnn pool _ Face _ QVGA _ Monochrome型号)和 170 KBs(对于 RNNPool_Face_M4 型号)的任何其他微控制器板。
要详细了解如何设置与 ARM Cortex-M 类设备相关的开发环境,我们建议读者参考 Carmine Novello 的精彩著作以及他关于该主题的惊人的工程博客。
1)安装 Java (版本 8 或更好)。
2.安装用于 C/c++T3 的 Eclipse IDE(版本2020–09在 Linux 和 Windows 上测试)。
3.安装 CDT 和 GNU ARM 嵌入式 CDT 插件用于 Eclipse(转到帮助- >安装新软件)..且看下图)。
正在安装 CDT 插件。(作者截图)
点击下拉菜单中的 CDT 链接,选择 CDT 主要功能选项。点击下一个> 的,并按照窗口中的剩余安装说明进行操作。**
此外,使用上面提供的链接进行添加..ARM Embedded CDT 插件链接到一组软件安装源,如下图所示,同时安装 Embedded CDT 工具。
挑选稳定的插件安装通道。(作者截图)
将该频道添加到软件安装站点列表中,并将其命名为 GNU MCU Eclipse 插件。(作者截图)
点击下一个> 安装上图所示的所有资源,然后从那里开始。(作者截图)
4.安装 libusb-1.0 (仅适用于基于 Linux 的系统,使用命令):
$ sudo apt-get install libusb-1.0
5.安装 GNU Arm 嵌入式工具链 ,其中包括 Arm 直接提供的基于 GNU GCC 的 Cortex-M 设备交叉编译器(版本 2020-q2 已在 Linux 和 Windows 上测试)。
6.安装 OpenOCD 软件,用于调试选择的板(版本 0.10.0 在 Linux 和 Windows 上测试)。
7.可选:安装 ST-LINK 固件升级软件 并升级板卡固件(仅在第一次使用板卡时)。
8.可选:根据具体要求,安装 STM32CubeMX 软件 为您的微控制器生成初始化代码。
现在,人们可以通过以下两种方式部署代码:
- 使用 STM32CubeMX (取决于具体的功能需求)生成初始化代码,从头开始设置一个新项目,并添加生成的。c 和。h 文件分别保存到 src/ 和 include/ 文件夹。
- 使用我们的一个示例部署片段(此处可用,其中我们使用 STM32CubeMX (此处可用)生成一个最低要求的初始化代码,并将我们的 SeeDot 生成的代码添加到这个基本示例中。这些示例代码片段已经针对速度进行了进一步优化,执行速度可以提高 15%左右。
这里,我们只关注后一种选择。然而,我们鼓励感兴趣的读者也尝试一下前一种选择。
要部署代码,只需将整个文件夹复制粘贴到您的 Eclipse 工作区目录中,如下所示。
1)在 Eclipse 中打开一个新的/现有的工作区。(作者截图)
2)点击导入项目..选项。(作者截图)
3)点击已有项目进入工作区选项,加载下载的文件夹。(作者截图)
4)浏览到包含我们示例代码的下载文件夹,并点击完成。(作者截图)
5)从项目浏览器菜单可以看到,项目导入过程现在已经完成。(作者截图)
使用 Eclipse GUI 设置下载编译器的路径,并使用编译器工具链构建项目二进制文件,如下所示。
1)点击 项目 标签,选择 属性 选项,导航到 手臂工具链路径MCU下拉菜单。(作者截图)
2)浏览到前面提到的 GNU ARM 嵌入式工具链 的安装目录。(作者截图)
3)导航到bin/目录里面,点击 打开。 (作者截图)
4)点击 应用并关闭, 再次导航到 项目 标签页,选择 构建项目 选项生成交叉编译的二进制文件。(作者截图)
最后一步,我们需要设置一个调试配置。我们通过 OpenOCD 来实现这一点,它允许我们通过主机本身来控制一步一步的调试。
1)点击 运行 选项卡,选择 调试配置.. 选项,并双击 GDB OpenOCD 调试 选项即会创建一个新的配置。(作者截图)
2)选择生成的配置并填写 主 页签明细,最后点击 应用 。(作者截图)
3)导航到 调试器 选项卡填写 OpenOCD 安装路径,完成后点击 应用 。(作者截图)
4)导航到 启动 选项卡,如图所示设置配置,点击 应用 。(作者截图)
现在,我们准备在我们选择的微控制器上运行编译好的二进制文件。只需将电路板连接到 PC,然后点击调试按钮。运行 OpenOCD 的控制台应启动,并立即停止。
点击调试选项,您会得到以下提示。选择开关选项,程序将在 main()的第一行执行处停止。(作者截图)
最终部署
将缩放后的图像作为输入(作为 input_image 变量存储在 main.h 文件中),我们在我们选择的模型上执行整个流水线,并根据需要设置输出目录(在 main.c 文件的 run_test() 函数中提到)。使用scale _ image . py文件可以完成任意图像的缩放过程。简单地运行,**
***$ IS_QVGA_MONO=1 python scale_image.py --image_path <path_to_image> --scale <scaleForX value in m3dump/scales.h file> > output.txt***
并从 output.txt 文件中复制粘贴整个文本,替换 main.h 中的 input_image 变量。
该模型为它认为是面部的图像的每个区域生成位置( loc )和置信度( conf )边界框。
1)点击 Resume 选项,继续执行整个流水线。(作者截图)
(左)我们看到连接板上的两个 LED 亮起,一个是通信 LED,在红色和绿色之间闪烁(使用 OpenOCD 表示 PC 设备通信),另一个是绿色电源 LED。(右图)点击 Resume ,我们会看到一个蓝色 LED 灯瞬间亮起,表示微控制器上核心图像处理流水线执行的开始和结束。该 LED 还可用于测量设备处理图像所花费的时间。(图片由作者提供)
2)过了一会儿,我们看到一个时间流逝计数器,和一个半托管退出消息,表明结果已经被写到提到的输出目录,现在执行已经完成。(作者截图)
最后,我们需要将获得的输出叠加到原始图像上,以获得最终所需的输出。为此,可以简单地使用 eval_fromquant.py 脚本:
***$ IS_QVGA_MONO=1 python eval_fromquant.py --save_dir <output image directory> --thresh <threshold value for bounding boxes> --image_dir <input image directory containing original test image> --trace_file <path to output file generated by microcontroller> --scale <scaleForY value in m3dump/scales.h file>***
这给了我们想要的输出图像,它的副本将被存储在这个文件夹中。
微软印度研究院研究小组的样本图像。(图片经作者许可复制)
来自 RNNPool_Face_M4 (最小延迟)模型的相应输出(阈值:0.45)。(图片经作者许可编辑)
来自rnn pool _ Face _ QVGA _ Monochrome(最高精度)模型的相应输出(阈值:0.60)。(图片经作者许可编辑)**
结论
因此,我们在 C 中有一个功能性的人脸检测流水线,它可以在一个微型微控制器上执行。我们可以向微控制器输入一幅图像,执行代码以生成这些 conf 和 loc 输出,并将这些输出发送回主机 PC 以获得最终图像,其中边界框如图所示叠加在一起。
人们可以通过将相机模块集成到微控制器来进一步扩展这一想法,以进行实时图像捕捉和处理。遗憾的是,对这项工作的技术细节的阐述超出了本文的范围。为此,我们在 RNNPool_Face_M4 型号的基础上,试验了一个 200 万像素的 OmniVision OV7670 摄像头模块。同样的代码可以在这里查看(标题 RNNPool_Face_M4 相机)。
一个 NUCLEO-F439 板,附带 OV7670 摄像机。(作者供图)
设备拍摄的样本。(作者供图)
这篇博文是由微软印度研究院的许多优秀的研究人员、研究员和实*生共同努力的结果。感谢 Oindrila Saha 、 Aayan Kumar 、 Harsha Vardhan Simhadri 、 Prateek Jain 和 Rahul Sharma 对本文不同章节的贡献。
在 Jupyter 笔记本中启用单元格完成通知
当长时间运行的单元完成执行时得到通知。
在 Unsplash上由 Manja Vitolic 拍摄的照片
如果你是一个 Jupyter 笔记本用户,一定有过这样的场景,某个特定的单元格花了很多时间来完成执行。这在机器学*、超参数优化中的模型训练期间,甚至在运行冗长的计算等时尤其常见。如果是的话,那么一旦这个过程完成,浏览器就会通知你,这就非常方便了。通过这种方式,您将能够导航到其他选项卡,并且只有在收到完成通知后才能返回到您的机器学*实验。事实证明,有一个 Jupyter 扩展可以做到这一点,它被非常恰当地命名为 Notify 。在本文中,我们将了解如何使用 notify 在 Jupyter 笔记本和 Jupyter 实验室中启用通知。
通知:浏览器通知单元格完成的一个 Jupyter 魔术
Notify 是一个 Jupyter 笔记本扩展,一旦一个长时间运行的单元格通过浏览器通知完成执行,它就会通知用户。Chrome 和 Firefox 支持 Notify。然而,如果你面临任何关于安装的问题,你可以按照这些详细说明来启动和运行。
现在让我们看看如何安装和使用这个包。
装置
安装可以通过 pip 完成
pip install jupyternotify
或通过以下来源:
git clone git@github.com:ShopRunner/jupyter-notify.git
cd jupyter-notify/
virtualenv venv
source venv/bin/activate
pip install -r requirements.txt
jupyter notebook
在 Jupyter 笔记本中启用通知
首先,我们将看看如何在 Jupyter 笔记本中启用通知。在笔记本的第一个单元格中输入以下文本:
%load_ext jupyternotify
当您第一次运行此单元时,您的浏览器会要求您允许在笔记本中发出通知。你应该按“是”现在我们都准备好了。在这里,我将向您展示一小段代码,使用sleep() function.
该函数在给定的秒数内暂停(等待)当前线程的执行。
%%notify
import time
time.sleep(10)
print('Finished!')
在执行上述单元格时,您将在浏览器中得到以下通知:
作者 GIF
当您单击通知的正文时,它会将您直接带到浏览器窗口和包含您的笔记本的选项卡。
在 Jupyter 实验室中启用通知
虽然目前 Jupyter 实验室没有官方支持,但我找到了一个解决方法。显然,一个用户已经提交了一个带有解决方案的拉请求,但是它还没有被合并。然而,该解决方案确实可以无缝地工作。
来源:https://github.com/ShopRunner/jupyter-notify/pull/38
将以下内容粘贴到 Jupyter Lab 单元格中,后跟您希望运行的代码:
!pip uninstall jupyternotify -y
!pip install git+[https://github.com/cphyc/jupyter-notify.git](https://github.com/cphyc/jupyter-notify.git)
%reload_ext jupyternotify
我们现在将使用与上面相同的代码来检查通知是否被启用。
%%notify
import time
time.sleep(5)
作者 GIF
自定义通知消息
如果您希望有一个更好的通知消息,代码可以很容易地调整这样做。
%%notify -m "Congrats.The process has finished"
import time
time.sleep(3)
还有许多其他选项可用,例如,在单元格中间触发通知或在一定时间后自动触发通知。我强烈建议你查看软件包的 GitHub 库以获取详细信息。
结论
像通知这样琐碎的功能可能是有益的,特别是当您经常处理需要大量时间执行的流程时。能够导航到另一个选项卡甚至桌面,并且在运行的进程结束时仍然得到通知,这是非常方便的。有时候,像 jupyternotify 这样的小插件确实可以帮助提高用户的工作效率。
启用 Amazon SageMaker Studio 资源的自助供应
使用 AWS 服务目录工厂管理 SageMaker Studio 的分步指南
在大多数大型企业中,标准化、供应和确保管理 ML 环境是中央 IT 团队的责任。
迪米特里·阿尼金在 Unsplash 上拍摄的照片
我最*出版了一份持续交付定制图像的指南,IT 团队可以在为他们的最终用户建立 SageMaker Studio 时使用。
在本帖中,我们将更进一步,使用 AWS 服务目录来实现经批准的 SageMaker Studio 环境的自助供应。
作者图片
我们将为 SageMaker Studio 创建一个服务目录组合,其中包含批准的域、用户配置文件、模板化 MLOps 项目和自定义映像管道。我们将使用 AWS 服务目录工厂自动部署我们的产品组合。
快速访问亚马逊 SageMaker 工作室、 AWS 服务目录工具研讨会和 AWS 管理&治理博客可能是一个好的开始,如果这些东西对你来说听起来很新的话。
演练概述
我们将通过 3 个步骤实现自助式工作室环境:
- 我们将首先安装服务目录工厂,并为我们的工作室设置创建一个文件夹。
- 然后,我们将把工作室产品的模板添加到文件夹中。
- 最后,我将展示终端用户如何使用 AWS 服务目录自助服务 Studio 环境。
先决条件
要浏览此示例,请确保您具备以下条件:
- 这是建立在 AWS 服务目录工厂之上的。确保您熟悉 AWS 服务目录工具研讨会中显示的概念。
- 从您的环境中使用代码提交的能力。
- 这个 GitHub 库克隆到您的环境中
步骤 1:使用服务目录工厂创建工作室组合
借助服务目录工厂,我们可以在 YAML 定义产品组合。YAML 文件包含我们想要管理的投资组合和产品的定义。对 AWS CodeCommit repo 中的 YAML 文件的更新会触发 AWS 代码管道,并反映服务目录组合中的更改。
通过 CI/CD 渠道管理投资组合是一个非常好的方法。
作者图片:使用服务目录工厂管理项目组合
在您的帐户中安装服务目录工厂
您可以通过按照本车间页面上的说明安装工厂。
图片由作者提供:遵循本页中的安装说明。
确保在 EnabledRegions 字段中输入您的 AWS 区域 ID。在这个例子中,我将 SCMSourceProvider 设置为 CodeCommit ,但是如果需要的话,可以随意放置其他代码源。
工厂堆栈将在 CodeCommit 中创建ServiceCatalogFactoryrepo,并在 CodePipeline 中创建关联的service catalog-factory-pipeline。
Image by author:您可以将创建的 CodeCommit repo 克隆到您的环境中
创建您的工作室产品组合
接下来,您需要将这个文件夹中的内容推入 CodeCommit repo main 分支。它将触发管道并部署我为您准备的示例组合。
在 sagemaker-studio.yaml 文件中,您可以编辑以下行,并向您帐户中的 IAM 组/用户授予投资组合访问权限:
作者图片
进入主分支机构几分钟后,您应该会在服务目录中看到您的产品组合:
作者图片
从现在开始,每次您提交时,管道将自动更新服务目录中的产品组合。
步骤 2:将工作室产品添加到产品组合中
这里,我们在产品组合中创建了 4 个示例产品:
- 域在该区域创建一个工作室域。
- 用户配置文件将一个工作室用户添加到域中。
- 启动项目,根据 MLOps 项目模板创建 SageMaker 项目。
- 自定义图像创建一个工作室自定义图像管道,如我之前的帖子中的所示。
服务目录工厂允许您随着时间的推移轻松地向产品组合添加新产品和版本。该过程在车间的日常使用章节中进行了说明
作者图片:您现在可以在服务目录控制台中看到产品组合
作者图片:服务目录工厂还为每个组合产品创建了一个管道
接下来,您需要为每个投资组合产品创建一个相应的 CodeCommit repo。每个代码管道都将被连接到一个 repo 主分支,并在您提交时更新产品。整个设置如下所示:
作者图片
作者图片:我创建后的回复
现在,您可以将我准备的文件夹的内容推送到它们的 CodeCommit repo main 分支中。每个回购都有一个名为 product.template.yaml 的云信息文件,它定义了产品本身。在这篇文章中,我主要通过原生云形成类型来定义 SageMaker 资源。
图片作者:下面是我推送到主分支后的域代码回购
步骤 3:自助式工作室环境
现在,有权访问投资组合的用户应该能够看到他们可用的产品。
作者图片
通常情况下,您将首先启动域产品,这是一个一次性的设置。然后,您可以将用户配置文件添加到域中,启动 MLOps 项目,等等。
作者图片:从服务目录启动的我的 SageMaker Studio 域和用户配置文件的视图
注意:我已经在域产品中添加了一个 Lambda 函数来使 Studio 用户能够访问 MLOps 项目模板。创建 Studio 用户配置文件时,可以引用 Lambda 函数 ARN,这样用户就可以访问项目。
结论
借助 AWS 服务目录,组织的 IT 团队可以创建和管理在 AWS 上使用的批准资源目录。
在这篇文章中,我展示了如何使用服务目录工厂为您的最终用户实现 SageMaker 资源的自助供应。随着时间的推移,这种设置将允许您轻松地向 ML 平台添加新产品和新版本。
为了更进一步,您还可以访问在 AWS 上构建安全的企业机器学*平台白皮书,该白皮书提供了在 AWS 上构建安全的企业 ML 平台的指导。
增强的表格数据可视化(Pandas)
改进熊猫数据帧表示的简单而有效的技术
来自 Pixabay
在本文中,我们将讨论一些有用的选项和函数,以便在 pandas 中将数据帧有效地可视化为一组表格数据。让我们从为进一步的实验创建一个数据框架开始:
import pandas as pd
import numpy as npdf = pd.DataFrame(np.random.randn(20, 40))# Renaming columns
df.columns = [x for x in 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMN']# Adding some missing values
df.iloc[3,4] = np.nan
df.iloc[2,0] = np.nan
df.iloc[4,5] = np.nan
df.iloc[0,6] = np.nan
df.head()
作者图片
注意:本文中的代码是在 pandas 版本 1.3.2 中运行的。有些函数相当新,在旧版本中会抛出错误。
自定义显示的列数和浮点精度
看着上面的数据框架,我们可能想解决两件事:
- 显示数据框的所有列。现在,从
k
到D
的列是隐藏的。 - 限制浮点值的精度。
让我们检查显示的列数和浮点精度的默认值:
print(pd.options.display.max_columns)
print(pd.options.display.precision)**Output:** 20
6
我们想要显示所有列(len(df.columns)
)并且精度为 2 个小数点,所以我们必须重新分配这两个选项:我们想要显示所有列(len(df.columns)
)并且精度为 2 个小数点,所以我们必须重新分配这两个选项:
pd.options.display.max_columns = len(df.columns)
pd.options.display.precision = 2print(pd.options.display.max_columns)
print(pd.options.display.precision)
df.head()**Output:** 40
2
作者图片
压制科学符号
既然我们可以看到所有列中的值,并且以更容易理解的形式显示,另一个问题出现了:一些浮点数以科学记数法显示(例如,7.85e-1 而不是 0.785)。为了解决这个问题,我们应该显式地为float_format
属性指定必要的格式(在我们的例子中是 2 个小数点):
pd.options.display.float_format = '{:.2f}'.format
df.head()
作者图片
隐藏索引和标题
在接下来的实验中,让我们从主数据帧(df
)中截取一个较小的数据帧(df1
):
df1 = df.iloc[:5,:8]
df1
作者图片
我们可能想要隐藏数据帧中的索引或标题(或两者都隐藏)。为此,我们应该使用DataFrame.style
属性创建一个 Styler 类的实例,并对其应用相应的方法hide_index()
或hide_columns()
。我们将在接下来的所有实验中使用这个属性。
df1.style.hide_index()
作者图片
df1.style.hide_columns()
作者图片
链接这些方法(df1.style.hide_index().hide_columns()
)将隐藏索引和头。我们还可以注意到,当使用DataFrame.style
属性时,NaN
值显示为nan
。
总是显示索引或标题
在其他一些情况下,我们可能希望,正好相反,当滚动数据帧时,索引(或标题)总是可见的。对于大数据帧尤其方便,所以还是回到我们最初的df
。这里用的方法是set_sticky()
。根据我们想要粘贴的内容,我们应该传入axis='index'
或axis='columns'
:
df.style.set_sticky(axis='index')
作者图片
df.style.set_sticky(axis='columns')
作者图片
突出显示特定值:特定范围内的空值、最小值、最大值http://localhost:8888/notebooks/Desktop/Dataquest/Guided Project_ Clean And Analyze Employee Exit Surveys/Untitled.ipynb#Highlighting-particular-values:-null,-minimum,-maximum,-values-from-a-certain-range
为了突出空值,我们可以使用一个内置函数highlight_null()
:
df1.style.highlight_null()
作者图片
默认颜色是红色,但是我们可以通过传入一个可选参数null_color
来改变它。此外,可以只显示一个或几个选定列(或行)的空值。我们使用subset
参数,传入列名(或行索引)或名称列表(索引):
df1.style.highlight_null(null_color='lime', subset=['e', 'g'])
作者图片
为了突出显示数据帧每一列中的最小值和最大值,我们可以应用方法highlight_min()
和highlight_max()
:
df1.style.highlight_min()
作者图片
df1.style.highlight_max()
作者图片
默认颜色可以通过传入一个可选参数color
来改变。同样在这里,我们可以使用subset
参数只选择一列或几列来显示最小值或最大值。当然,我们可以链接这两种方法:
df1.style.highlight_min(color='cyan', subset='d').highlight_max(color='magenta', subset='d')
作者图片
默认情况下,最小值和最大值按列显示。如果我们需要这样的行信息,我们必须指定axis='columns'
:
df1.style.highlight_min(axis='columns')
作者图片
在这种情况下,如果我们只想选择一行或几行而不是整个数据帧,我们应该为subset
传入相应的值:行索引。
最后,可以使用highlight_between()
方法突出显示选定范围的值。除了已经熟悉的参数color
和subset
,我们还必须指定left
和/或right
参数,以及可选的inclusive
,默认为'both'
(其他可能的值为'neither'
、'left'
或'right'
):
df1.style.highlight_between(left=-0.1, right=0.1, inclusive='neither')
作者图片
将数据帧值显示为热图
有两种奇怪的方法可以根据数值的数值范围,以渐变、类似热图的风格突出显示单元格或其中的文本:background_gradient()
和text_gradient()
。两种方法都需要安装 matplotlib(不一定要导入)。
df1.style.background_gradient()
作者图片
df1.style.text_gradient()
作者图片
除了subset
,我们可以调整以下参数:
cmap
—matplotlib 色彩映射表(默认为'PuBu'
),axis
—按列(axis='index'
)、按行(axis='columns'
)或整个数据帧(默认)对值进行着色。low
、high
—基于原始基于数据的范围的相应部分,在低端/高端扩展梯度的范围,vmin
、vmax
—定义一个对应于色彩映射表最小/最大值的数据值(默认为最小/最大数据值)。text_color_threshold
—仅在background_gradient()
中使用,决定文本颜色的亮/暗变化,以增强整个单元格背景颜色的文本可见性(默认为 0.408)。
让我们试着调整一些参数:
df1.style.text_gradient(cmap='cool', subset=3, axis='columns', vmin=-2)
作者图片
结论
在 Python 中,还有许多其他方式可以灵活地定制表格可视化:应用更高级的文本格式、控制数据切片、更改文本字体、修改单元格边界属性、分配悬停效果等。一般来说,我们可以用任何定制逻辑应用我们需要的任何函数,当使用DataFrame.style
属性时,可以使用各种各样的 CSS 样式元素。在本文中,我们考虑了一些最常见的任务,这些任务比其他任务使用得更频繁,导致创建了用于这些目的的内置函数,这些函数具有简单明了的语法和高度可定制的输出。
感谢阅读!
你会发现这些文章也很有趣:
https://levelup.gitconnected.com/when-a-python-gotcha-leads-to-wrong-results-2447f379fdfe https://betterprogramming.pub/read-your-horoscope-in-python-91ca561910e1 https://python.plainenglish.io/the-little-prince-on-a-word-cloud-8c912b9e587e
分类变量编码:一次性编码与虚拟编码
用 Pandas 和 Scikit 实现-学*
(图片由作者提供,用 draw.io 制作)
您经常会在数据集中找到分类变量。分类变量的值有有限数量的类别或标签。例如, 性别 是一个分类变量,可以取“男性”和“女性”为其值。
分类变量的类型
分类变量主要分为两种类型:
- 有序分类变量:这些分类变量的值遵循自然顺序。例如,在变量中,其“小学”、“某学院”和“研究生学历”的值遵循一个顺序,即“研究生学历”是最高的学历,“小学”是最低的。
- 名义分类变量:这些分类变量的值 而非 遵循自然顺序。例如, 性别 是一个名词性分类变量,其“男性”和“女性”的值不遵循顺序。
什么是分类变量编码,我们为什么需要它?
分类变量通常有字符串值。许多机器学*算法不支持输入变量的字符串值。因此,我们需要用数字替换这些字符串值。这个过程被称为分类变量编码。
编码类型
这里,我们将讨论两种不同类型的编码:
- 一键编码
- 虚拟编码
我们将从一次性编码开始。
一键编码
在一次性编码中,我们创建一组新的虚拟(二进制)变量,它等于变量中类别的数量(k)。例如,假设我们有一个分类变量 Color ,它有三个类别,称为“红色”、“绿色”和“蓝色”,我们需要使用三个虚拟变量,使用一键编码对这个变量进行编码。虚拟(二进制)变量只取值 0 或 1 来表示排除或包含某个类别。
一键编码(图片由作者提供)
在独热编码中,
- “红色”被编码为大小为 3 的[1 0 0]向量。
- “绿色”被编码为大小为 3 的[0 1 0]向量。
- “蓝色”被编码为大小为 3 的[0 0 1]矢量。
虚拟编码
虚拟编码也使用虚拟(二进制)变量。哑元编码使用 k-1 个哑元变量,而不是创建与变量中类别数(k)相等的多个哑元变量。要使用哑编码对三个类别的相同 颜色 变量进行编码,我们只需要使用两个哑变量。
虚拟编码(图片由作者提供)
在虚拟编码中,
- “红色”被编码为大小为 2 的[1 0]向量。
- “绿色”被编码为大小为 2 的[0-1]向量。
- “蓝色”被编码为大小为 2 的[0 0]向量。
虚拟编码消除了一次性编码中存在的重复类别。
熊猫的实施
通过使用它的 get_dummies 函数,可以在 Pandas 中实现一位热编码和虚拟编码。
*import pandas as pdpd.**get_dummies**(*data*, *prefix*, *dummy_na*,*columns*, *drop_first*)*
- 数据 —这里我们指定需要编码的数据。它可以是 NumPy 数组、Pandas 系列或 DataFrame。
- 前缀 —如果我们指定一个前缀,它将添加到列名中,这样我们就可以很容易地识别列。前缀可以指定为单个列名的字符串。对于多个列名,它被定义为将列名映射到前缀的字典。有关更多详细信息,请参见下面的示例。
- dummy_na —如果为 False(默认),则在对变量进行编码时忽略缺失值(nan)。如果为真,这将在单独的类别中返回缺失的数据。
- 列 —指定要编码的列名。如果无(默认),将对 数据 参数中的所有分类列进行编码。如果将列名指定为列表,则只对指定的列进行编码。
- drop_first —这是最重要的参数。这需要一个布尔值,真或假。如果为 False(默认值),将执行一键编码。如果为真,这将删除每个分类变量的第一个类别,为每个分类变量创建 k-1 个虚拟变量,并执行虚拟编码。
现在,我们使用 钻石 数据集(参见底部的源代码和许可证信息)来查看这两种类型的编码。
*import pandas as pddf = pd.read_csv("diamonds.csv")
df.head()*
钻石数据集的前几行(图片由作者提供)
*df.shape*
(图片由作者提供)
数据集包含 53,940 个实例和 10 个变量。
让我们看看数据集中是否有丢失的值。
*df.isnull().sum().sum()*
(图片由作者提供)
因为这会返回 0,所以数据集中没有丢失的值。
让我们看看数据集中有多少分类变量。
*df.info()*
钻石数据集变量信息(图片由作者提供)
分类变量有 对象 或 类别 数据类型。因此,数据集中有 3 个分类变量。它们是切割、颜色和净度。
让我们看看切割变量的独特类别或标签。
*df["cut"].unique()*
切割变量的独特类别(图片由作者提供)
在切割变量中有 5 个独特的类别。为了对这个变量进行编码,我们需要在一键编码中创建 5 个伪变量,在伪编码中创建 4 个伪变量。
同样地,颜色和清晰度变量也有独特的类别。
*df["color"].unique()*
颜色变量的独特类别(图片由作者提供)
*df["clarity"].unique()*
透明度变量的独特类别(图片由作者提供)
用 Pandas 实现一键编码
现在,我们单独对颜色变量应用一键编码,看看结果。
*one_hot = pd.get_dummies(df["color"],
prefix="color",
**drop_first=False**)one_hot*
这将返回编码数据的熊猫数据帧。让我们看看它的前几行。
(图片由作者提供)
查看前缀参数中指定的文本是如何与颜色的类别名称组合在一起的。
现在,我们将颜色变量的一键编码添加到数据集中。
*one_hot_df = pd.get_dummies(df, prefix="color",
columns=["color"],
**drop_first=False**)one_hot_df.head()*
(图片由作者提供)
现在,我们对数据集中的所有分类变量应用一次性编码。
*one_hot_df = pd.get_dummies(df, prefix={'color':'color',
'cut':'cut',
'clarity':'clarity'},
**drop_first=False**)*
现在,我们看到了编码数据集的形状。
*one_hot_df.shape*
(图片由作者提供)
编码数据集有 27 个变量。这是因为在对分类变量进行编码时,一键编码增加了 20 个额外的虚拟变量。因此,一键编码扩展了数据集中的特征空间(维度)。
用 Pandas 实现虚拟编码
要对数据实现虚拟编码,您可以遵循在一键编码中执行的相同步骤。唯一不同的是,你应该将 drop_first 参数设置为 True 而不是 False。
*dummy_df = pd.get_dummies(df, prefix={'color':'color',
'cut':'cut',
'clarity':'clarity'},
**drop_first=True**)*
现在,我们看到了编码数据集的形状。
*dummy_df.shape*
(图片由作者提供)
编码数据集有 24 个变量。这是因为哑编码在对分类变量进行编码时增加了 17 个额外的哑变量。因此,虚拟编码也扩展了数据集中的特征空间(维度)。
用 Scikit-learn 实现
通过使用 Scikit-learn 的 OneHotEncoder 函数,可以在 Scikit-learn 中实现单热编码和伪编码。
*from sklearn.preprocessing import OneHotEncoderohe = **OneHotEncoder**(*categories*, *drop*, *sparse*)encoded_data = ohe.fit_transform(original_data)
#Returns a NumPy array of encoded data*
- 类别 —默认为【自动】自动确定每个变量中的类别。
- 删除 —默认为无,执行一键编码。要执行虚拟编码,将该参数设置为‘first’,这将删除每个变量的第一个类别。
- 稀疏 —设置为 False 以 NumPy 数组的形式返回输出。默认值为 True,这将返回一个稀疏矩阵。
用 Scikit-learn 实现一键编码
这里,我们也使用相同的 钻石 数据集。我们对数据集中的所有分类变量应用一次性编码。
*from sklearn.preprocessing import OneHotEncoderohe = OneHotEncoder(categories='auto',
drop=None,sparse=False)ohe_df = pd.DataFrame(ohe.fit_transform(df)*
现在,我们看到了编码数据集的形状。
(图片由作者提供)
哦。什么?编码数据集有 13,687 个变量。这可能发生吗?是的,这是因为 Scikit-learn 的 OneHotEncoder() 函数也为数字变量创建了虚拟变量。为了避免这种情况,我们应该在调用它的 fit_transform 方法时只传递分类数据。
*ohe_df = pd.DataFrame(ohe.fit_transform(df[['cut',
'color', 'clarity']]))*
但是,这仅返回编码的分类数据,而不是包含数值变量的整个数据集。
(图片由作者提供)
用 Scikit-learn 实现虚拟编码
您可以简单地通过将 下降 参数指定为‘第一’来实现。
*ohe = OneHotEncoder(categories='auto',
drop='first',sparse=False)*
何时使用独热编码和虚拟编码
这两种类型的编码都可以用来编码序数和名义分类变量。然而,如果您严格地想要保持有序分类变量的自然顺序,您可以使用标签编码来代替我们上面讨论的两种编码。
假设我们有一个分类变量 质量 ,有三个类别,称为“一般”、“良好”和“优质”。这些类别的自然顺序是:
*Fair < Good < Premium => 0 < 1 < 2*
我们可以这样编码。
标签编码(图片由作者提供)
标签编码的一个优点是,它根本不扩展特征空间,因为我们只是用数字替换类别名称。这里,我们不使用虚拟变量。
标签编码的主要缺点是机器学*算法可能会认为编码类别之间可能存在关系。例如,某个算法可能会将 Premium (2)解释为 Good (1)的两倍。实际上,类别之间没有这种关系。
为了避免这种情况,标签编码应该只应用于目标(y)值,而不是输入(X)值。
可以通过使用 Scikit-learn 的 LabelEncoder 函数来应用标签编码。现在,我们将它应用于钻石数据集中的**切割变量。这仅用于说明目的,因为我们不使用标签编码来编码输入(X)值。
***from sklearn.preprocessing import LabelEncoderdf['cut_enc'] = LabelEncoder().fit_transform(df['cut'])
df.head(10)***
(图片由作者提供)
右边添加了新的编码数据列(cut_enc)。我们现在可以移除切割变量。**
Pandas get_dummies()函数相对于 Scikit-learn onehotencode()函数的优势
- get_dummies() 函数返回带有变量名的编码数据。我们还可以在每个分类变量名称中为虚拟变量添加前缀。
- get_dummies() 函数返回包含数字变量的整个数据集。
伪编码相对于一位热码编码的优势
- 两者都通过添加虚拟变量来扩展数据集中的特征空间(维度)。然而,哑元编码比一位热码编码添加更少的哑元变量。
- 虚拟编码删除了每个分类变量中的重复类别。这避免了虚拟变量陷阱。
今天的帖子到此结束。
如果您有任何反馈,请告诉我。
同时,你可以 注册成为会员 来获得我写的每一个故事,我会收到你的一部分会员费。
非常感谢你一直以来的支持!下一个故事再见。祝大家学*愉快!
钻石数据集来源:
钻石 数据集可在https://www.kaggle.com/shivam2503/diamonds下载
钻石数据集许可证:
钻石 数据集由公共领域许可证授权,如此处所定义。这意味着数据集已经捐赠给公众。
鲁克山普拉莫迪塔
2021–12–16
用转换器编码数据
相关事件
如何使用基于转换器的技术来执行数据编码
数据编码是人工智能领域最新的技术进步之一。通过使用编码器模型,我们可以将分类数据转换为数字数据,这使我们能够进行比较,了解数据之间的关系,提出建议,改进搜索…
在这篇文章中,我将解释如何通过使用安装在 RelevanceAI 库中的一个模型,将一组文章(文本数据)转换成向量(数字数据)。
如果您希望使用 API,可以遵循一个快速入门指南,使用基于向量的技术对数据集执行第一次语义搜索。
什么是编码
编码意味着我们将分类数据转换成数字数据。有非常基本的编码类型,例如,one_hot 编码或基于索引的编码。然而,当我们处理文本数据时,最高级的编码形式可以使用嵌入来完成。
嵌入能够扫描单词集,并将每个单词放入多维空间,本质上是将每个单词转换成一个向量。一旦模型被训练,语料库中的每个单词都被适当地放置到具有相似含义的单词附*的数学空间中。
您可以使用谷歌的嵌入投影仪来体验嵌入的乐趣:
Tensorflow 嵌入式投影仪,检索自:【https://projector.tensorflow.org/
这项技术对现在搜索的工作方式产生了巨大的影响,在搜索引擎、推荐系统和计算机视觉中找到了大多数应用。
有多少编码器?
直到几年前,最流行的文本编码器是 word2vec。由于有几种模型,你可以将每个单词转换成空间中相应的向量。然而,这被称为静态嵌入,这意味着向量永远不会改变:该模型是一个字一个字地编码,而忽略了句子的上下文:我们可以做得更好!
这个问题的答案现在以变形金刚模型的形式出现了。这些编码器使用动态嵌入:根据周围的单词,每个单词可以有不同的向量。
可以想象,这比使用静态嵌入要精确得多:RelevanceAI 致力于使用同样的技术。
用几行代码编码数据
要对文本数据进行编码,您唯一需要做的就是下载 vectorhub 库,该库托管相关的编码器:
#encode on local
from vectorhub.encoders.text.sentence_transformers import SentenceTransformer2Vec
model = SentenceTransformer2Vec("bert-base-uncased")df_json = model.encode_documents(documents=df_json, fields=['raw'])
df_json
编码大数据
因为尝试使用更大的数据集总是有用的,所以您可以通过 relevance API 使用我们的数据集。让我们尝试编码一个数据集,我们将在后面的文章中使用它来上传到您的相关工作空间,并尝试几种方法:
1.安装 relevanceai 和 vectorhub
第一步是在你的笔记本上安装 relevanceai。安装非常简单,因为它使用 pip。
!pip install vectorhub[encoders-text-sentence-transformers]
!pip install -U relevanceaiimport relevanceai
print(relevanceai.__version__)
#restart notebook if you are updating the API rather than just installing it for the first timeOutput:
0.12.17
2.加载数据集
RelevanceAI 允许您下载几个可能的样本数据集。在这种情况下,我将使用约有 20.000 个样本的 flipkart 数据集。要下载它,只需使用以下代码:
from relevanceai import datasetsjson_files = datasets.get_flipkart_dataset()
json_files
3.数据集架构
上传过程结束后,现在让我们检查数据集的模式:我们可以看到它的所有字段。到目前为止,还没有任何字段被编码。
{'_id': 0,
'product_name': "Alisha Solid Women's Cycling Shorts",
'description': "Key Features of Alisha Solid...",
'retail_price': 999.0},
{'_id': 1,
'product_name': 'FabHomeDecor Fabric Double Sofa Bed',
'description': "FabHomeDecor Fabric Double ...",
'retail_price': 32157.0},
{'_id': 2,
'product_name': 'AW Bellies',
'description': 'Key Features of AW Bellies Sandals...',
'retail_price': 999.0},
{'_id': 3,
'product_name': "Alisha Solid Women's Cycling Shorts",
'description': "Key Features of Alisha Solid Women's Cycling...",
'retail_price': 699.0},
4.执行编码
要开始在本地执行文本数据的编码,您可以通过 vectorhub 库轻松访问我们的一些 transformers 模型。执行编码非常简单,您只需要传入指定您希望编码的字段的 json_files 数据:
#encode on local
from vectorhub.encoders.text.sentence_transformers import SentenceTransformer2Vec
model = SentenceTransformer2Vec("bert-base-uncased")df_json = model.encode_documents(documents=json_files[0:1000], fields=['product_name'])
df_json
我将只编码前 1000 个样本,否则,编码器可能会运行一段时间。大约一分钟后,输出如下:如您所见,字典中添加了一个包含向量的新字段。
Output:
[{'_id': 0,
'product_name': "Alisha Solid Women's Cycling Shorts",
'description': "Key Features of Alisha Solid Women's...",
'retail_price': 999.0,
'product_name_sentence_transformers_vector_': [0.29085323214530945,
-0.12144982814788818,
-0.33044129610061646,
0.07810567319393158,
0.3813101351261139,
-0.13027772307395935,
5.为可视化准备数据
因为您还没有将数据集上传到 relevanceAI 中(我们将在下一篇文章中向您展示如何做到这一点),所以您必须手动可视化您的数据。下面是一个示例代码,您可以使用它将输出字典转换成 pandas 数据帧。
import numpy as np
import pandas as pddf = pd.DataFrame(df_json)
df_vectors = pd.DataFrame(np.column_stack(list(zip(*df[['product_name_sentence_transformers_vector_']].values))))
df_vectors.index = df['product_name']
df_vectors
转换成熊猫数据帧的结果,图片由作者提供
6.可视化数据
因为数据由 768 列组成,为了可视化,您需要压缩它。您可以使用主成分分析轻松可视化您的数据。要知道,有很多更先进的技术来获得相同的结果,但这将足以快速浏览数据。
from sklearn.decomposition import PCA
import matplotlib.pyplot as pltpca = PCA(n_components=2, svd_solver='auto')
pca_result = pca.fit_transform(df_vectors.values)
#display(df)fig = plt.figure(figsize=(14, 8))
x = list(pca_result[:,0])
y = list(pca_result[:,1])
# x and y given as array_like objects
import plotly.express as px
fig = px.scatter(df, x=x, y=y, hover_name=df_vectors.index)
fig.update_traces(textfont_size=10)
fig.show()
精彩!所有这 1000 个样本都被放置在太空中,现在我们可以看到它们了。
1000 个产品的数据压缩,图片由作者提供
通过放大数据,我们可以了解每个产品与另一个产品的关系:
放大数据压缩,按作者显示文本和图像
使用 GPG 轻松加密您的文件
学*软件工具
了解一个免费的个人加密开源工具
Y 你有一个文件,你想在发送给你的朋友之前加密它,你会怎么做?有很多工具可以做到。下面是一个广为人知的 IT 友好工具, Gnu 私人卫队(又名 GPG) 。
- 它是开源的:https://github.com/gpg/gnupg
- 可以免费下载:https://gnupg.org/download/
- 它使用非对称加密,即私钥对公钥
不错!使用它也很简单。
生成密钥对
下载后,第一步是生成您的密钥
gpg --generate-key
它将为您提供几个步骤来生成您的密钥,只需您的姓名、电子邮件和 ok,如下面的粗体所示。
Real name: **My Name**
Email address: **myname@mail.com**
You selected this USER-ID:
"My Name <myname@mail.com>"
Change (N)ame, (E)mail, or (O)kay/(Q)uit? **O**
之后,它会问你需要记住的最重要的事情,你的秘密短语。
一旦您输入您的机密短语,它将显示在下面。
pub rsa3072 2021-04-27 [SC] [expires: 2023-04-27]
C0419C440EF637AD9D39A89137BF9320BC0410A4
uid My Name <myname@mail.com>
sub rsa3072 2021-04-27 [E] [expires: 2023-04-27]
恭喜你。您已经生成了密钥对(公钥和私钥)。
为了识别您的密钥,我在这里称之为**<key-id>**
,您可以使用
- 名字
- 电子邮件
- 或者生成的数字的最后 8 位数字,即
BC0410A4
。这个我也叫**<digit-key-id>**
如果您想对密钥生成有更多的控制,请使用
gpg --full-generate-key
验证您的密钥是否已创建
要验证您的密钥是否已创建,只需键入
gpg -k
您将能够看到您的密钥已创建
要删除,只需输入
gpg --delete-key **<key-id>**
。如果您的密钥对既有私钥又有公钥,您首先需要--delete-secret-key
。
将您的公钥发送给您的朋友
有两种方法发送给你的朋友
手动导出
手动方式是导出并发送给你的朋友
// Binary based
gpg --export **<key-id>** > mypublickey.pgp// OR// ASCII based
gpg --export --armor **<key-id>** > mypublickey.asc
然后把文件发给你的朋友。然后他们可以使用导入到他们的 gpg
// Binary based
gpg --import mypublickey.pgp// OR// ASCII based
gpg --import mypublickey.asc
发送到密钥服务器
您可以使用将您的公钥上传到服务器
gpg --keyserver **<key-server>** --send-keys **<digit-key-id>**
密钥服务器的一个例子是hkp://pool.sks-keyservers.net
然后你的朋友可以使用
gpg --keyserver **<key-server>** --recv-keys **<digit-key-id>**
成功后,您的朋友可以看到信息
gpg: key **<8-digi-key-id>**: "**<name>** **<mail>**" not changed
gpg: Total number processed: 1
gpg: unchanged: 1
您的朋友也可以使用gpg -k
命令来检查密钥。
也可以使用下面的命令搜索一个键
gpg --keyserver **<key-server>** --search-keys **<key-id>**
将显示一个键列表,用户可以通过键入用空格分隔的数字来选择要导入的键,例如
1 3
表示选择第一个和第三个键。更多信息点击这里。
用公钥加密你的文件
现在你的朋友在发送文件之前可以先用下面的命令加密它。
gpg -r <key-id> -e file.ext
file.ext
可以是二进制或 ASCII。
输出文件是file.ext.gpg
。这是可以安全发送的加密文件。
提示:你也可以用多个公钥
gpg -r **<key-id-1>** -r **<key-id-2>** -e file.ext
加密一个文件,这样它就可以被发送给多个接收者
用私钥解密文件
一旦您收到了gpg
文件,您可以使用
gpg -d file.ext.gpg > **<filenameyoulike>**
在您解密之前,系统会提示您输入机密短语。
移动您的密钥集(私钥和公钥)
您可能知道,您密钥集存储在您的机器上。有时候,你必须改变你的机器。那么,如何将您的密钥集转移到另一台机器上呢?
您可以使用下面的命令
// Binary based
gpg --export-secret-key **<key-id>** > mysecretkey.pgp// OR// ASCII based
gpg --export-secret-key --armor **<key-id>** > mysecretkey.asc
在成功导出之前,它会提示您输入密码。
一旦完成,现在你可以将你的文件复制到另一台机器上并导入。
要导入密钥,请使用正常的导入步骤。对于导入,您也会被提示输入机密短语。因此,记住你的秘密短语是很重要的。
GPG 的其他用途
除了加密文件,你还可以用 GPG 做很多其他的事情,例如
要获得更多的 gpg 命令,请点击查看。
安全自由地享受加密!
H2O AutoML、MLflow、FastAPI 和 Streamlit 的端到端 AutoML 管道
关于使用一系列强大工具来培训和服务保险交叉销售的 AutoML 渠道的简单易懂的综合指南
传统的机器学*( ML )模型开发耗时、资源密集,需要高度的技术专业知识和多行代码。
随着自动化机器学*( AutoML )的出现,这一模型开发过程已经加速,允许数据科学家高效地生成高性能和可扩展的模型。
然而,除了模型开发,在一个生产就绪的 ML 系统中还有多个组件需要大量的工作。
在本综合指南中,我们将探讨如何使用 H2O AutoML、MLflow、FastAPI 和 Streamlit 的强大功能来建立、培训和服务一个 ML 系统。
ML 代码只是 ML 生产系统的一小部分
内容
(1)商业背景
保险中的交叉销售是推销与客户已有保单互补的产品的做法。
交叉销售创造了双赢局面,客户可以以较低的捆绑价格获得全面的保护。与此同时,保险公司可以通过提高保单转换来增加收入。
该项目旨在通过建立一个 ML 渠道来识别有兴趣购买额外车辆保险的健康保险客户,从而使交叉销售更加高效和有针对性。
(2)工具概述
H2O 汽车公司
在 Apache 许可 2.0 下使用的图像
H2O 是一个开源的、分布式的、可扩展的平台,使用户能够在企业环境中轻松构建和生产 ML 模型。
H2O 的主要特色之一是 H2O 汽车 ,这是一项自动化 ML 工作流程的服务,包括多个模型的自动训练和调整。
这种自动化允许团队关注其他重要的组件,例如数据预处理、特征工程和模型部署。
MLflow
MLflow 是一个管理 ML 生命周期的开源平台,包括实验、部署和中央模型注册中心的创建。
ML flow Tracking组件是一个 API,它记录并加载ML 模型实验的参数、代码版本和工件。
MLflow 还附带了一个mlflow . H2OAPI 模块,该模块集成了 H2O AutoML 运行与 ml flow 跟踪。
FastAPI
根据麻省理工学院许可使用图像
FastAPI 是一个快速高效的 web 框架,用于在 Python 中构建 API。它的设计是用户友好的、直观的和生产就绪的。
将我们的 ML 模型部署为 FastAPI 端点的目的是在将我们的测试数据解析到 API 中之后,能够容易地检索预测结果。
细流
Streamlit 是一个开源的应用框架,可以在几分钟内将数据脚本转化为可共享的 web 应用。它有助于为技术和非技术用户创建用户友好的前端界面。
用户界面允许我们通过 FastAPI 端点将数据上传到后端管道,然后点击鼠标下载预测。
虚拟环境设置
我们首先使用 venv 模块创建一个 Python 虚拟环境(参见下面的设置截图)。
激活虚拟环境后,我们根据 需求,用pip install -r requirements.txt
安装必要的包。
虚拟环境的设置(命名为 env_automl ) |图片作者
(3)分步实施
这个项目的 GitHub repo 可以在 这里 找到,在这里可以找到跟随的代码。
㈠数据获取和勘探
我们将使用健康保险交叉销售预测数据,我们可以通过 Kaggle API 检索这些数据。
要使用 API,请转到“帐户”选项卡(kaggle.com/<用户名>/帐户),然后选择“创建 API 令牌”。这个动作触发下载包含您的 API 凭证的 kaggle.json 文件。
我们使用 opendatasets 包(与pip install opendatasets
一起安装)和凭证来检索原始数据:
数据集包含 12 个特征,描述 38 万+ 健康保险投保人的概况。所有的数据都被匿名化和数字化,以确保隐私和保密性。
目标变量是 响应 ,客户购买额外车辆保险的兴趣*的二元度量(其中 1 表示正兴趣)。*
其他变量如下:
- id: 每个客户的唯一标识符
- 性别:客户性别
- 年龄:客户年龄(岁)
- 驾驶执照:客户是否持有驾驶执照(1)
- Region_Code: 客户所在地区的唯一代码
- 以前投保:客户是否已经投保车辆保险(1)
- 车龄:客户车辆的车龄
- 车辆损坏:客户的车辆在过去(1)是否损坏
- 年度 _ 保费:客户一年支付的保费总额
- 保单销售渠道:保险代理人联系客户的渠道的匿名代码,例如电话、面谈
- 年份:客户在保险公司工作的天数
(二)数据预处理
数据预处理和特征工程是任何数据科学项目的关键步骤。以下是所采取步骤的摘要:
- 标签编码性别变量的*(1 =女性,0 =男性)*
- 分类变量的一键编码
- 为更清晰起见,重命名一键编码变量的
- 数值变量的最小-最大缩放比例
因为重点是展示端到端管道的设置,所以执行的转换没有尽可能广泛。在现实世界中,作为以数据为中心的方法的一部分,这一步值得深入研究。
㈢H2O 和物流设置
在对处理过的数据集开始模型训练之前,我们首先初始化一个 H2O 实例和一个 MLflow 客户端。
H2O 初始化
一个 H2O 实例的初始化可以用h2o.init()
来完成,之后它将在127.0.0.1:54321
连接一个本地 H2O 服务器。
H2O 连接成功后,将显示以下打印输出。
成功连接到 H2O 本地服务器后的输出|图片作者
MLflow 初始化
MLflow 用client = MlflowClient()
初始化,这创建了一个管理我们的实验运行的 MLflow 跟踪服务器客户端。
一个 MLflow 实验可以包含多个 ML 训练运行,所以我们从创建和设置我们的第一个实验(automl-insurance
)开始。默认情况下,日志记录在本地的/mlruns
子文件夹中。
MLflow 实验设置的打印输出|作者提供的图片
MLflow 有一个用户界面用于浏览我们的 ML 实验。我们可以通过在命令提示符下(从项目目录中)运行mlflow ui
,然后访问本地主机 URL 127.0.0.1:5000
来访问该界面。
按作者初始化 MLflow 跟踪 UI |图像的命令
MLflow 实验跟踪的用户界面|作者图片
除了本地文件存储之外,还有各种后端和工件存储配置,用于将运行记录到 SQLAlchemy 兼容数据库或远程跟踪服务器。
(四)H2O 汽车公司的物流跟踪培训
将我们的数据作为 H2O 数据框导入,定义目标和预测特征,并将目标变量设置为分类变量(用于二元分类)后,我们可以开始模型训练设置。
H2O AutoML 提供了一个包装器函数H2OAutoML()
,它自动执行各种 ML 算法的训练。
当我们初始化一个 AutoML 实例(aml = H2OAutoML()
)时,我们使用mlflow.start_run()
将它包装在一个 MLflow 上下文管理器中,以便将模型记录在一个活动的 MLflow 运行中。
以下是对 AutoML 实例参数的解释:
**max_models**
—在自动运行中构建的模型的最大数量**seed**
—再现性随机种子**balance_classes**
—指示是否对少数类进行过采样以平衡类分布。**sort_metric**
—指定用于在 AutoML 运行结束时对排行榜进行排序的指标**verbosity**
—培训期间要打印的后端消息类型**exclude_algos**
—指定建模时要跳过的算法(我排除了* GLM 和DRF以便重点放在GBM*算法)**
然后,我们可以用一行代码启动 AutoML 培训:
**考虑在Google Colab
(五)记录最佳 AutoML 模型
训练完成后,我们会获得一个显示候选型号列表的排行榜。该表根据前面指定的sort_metric
性能指标(日志损失)进行排序。
排行榜中的 10 大算法|作者图片
排行榜显示,基于对数损失值(0.267856),堆叠集合模型' stacked ensemble _ all models _ 4 '具有最佳性能。
H2O 的堆叠集成是一个监督的 ML 模型,它使用 堆叠 找到各种算法的最佳组合。算法的堆叠提供了比任何组成学*算法更好的预测性能。
接下来要做的是记录最佳 AutoML 模型(又名 AutoML leader )。作为日志记录的一部分,我们还导入了mlflow.h2o
模块。我们希望记录 AutoML 模型的三个方面:
- 指标(例如,对数损失、AUC)
- 模型工件
- 排行榜(保存为工件文件夹中的 CSV 文件)
您可以使用train . py脚本执行 AutoML 训练。这可以通过命令提示符下的 python train.py — —target ‘Response'
来完成。
H2O 还提供 模型可解释性 让你向技术和商业利益相关者解释模型的内部运作。
㈥生成和评估预测
在多次运行一个实验(或不同的实验)后,我们希望选择最佳模型进行模型推断。
我们可以使用mlflow.search_runs()
来寻找性能最好的模型,mlflow.h2o.load_model()
来加载 H2O 模型,而.predict()
来生成预测。
AutoML 模型与像XGBoost with randomized search cv这样的基线模型相比表现如何?以下是评估他们的预测后的结果:
两种模型的性能指标比较|图片由作者提供
AutoML 型号略胜基准 。这种更好的性能证明了 AutoML 能够快速、轻松地交付高性能模型。
请务必查看详细描述这两个模型的训练和预测的笔记本:
FastAPI 设置
既然我们已经选择了我们的最佳模型,那么是时候将它部署为 FastAPI 端点了。我们的目标是创建一个后端服务器,在那里我们的模型被加载并通过 HTTP 请求进行实时预测。
在一个新的 Python 脚本main.py
中,我们用app = FastAPI()
创建了一个 FastAPI 实例,然后设置一个路径操作@app.post("/predict")
来生成并返回预测。
我们还包括 H2O 和 MLflow 初始化的代码,以及来自 步骤 vi 的模型推断的代码。
步骤六 中的模型推理代码旨在从所有实验中检索最佳模型,但您可以更改 ID 参数,以便使用特定实验运行的模型。
路径操作符将接收测试数据作为一个字节的文件对象,所以我们使用BytesIO
作为将数据转换成 H2O 帧的一部分。
最后,我们需要一个包含模型预测的 JSON 输出。可以使用来自fastapi.responses
的JSONResponse
将输出作为 JSON 文件返回。
你可以在main . py脚本中找到完整的 FastAPI 代码。
最后一部分是通过运行uvi corn服务器(从存储脚本的backend
子目录中)来激活我们的 FastAPI 端点,方法是使用以下命令:
*uvicorn main:app --host 0.0.0.0 --port 8000*
运行 uvicorn 服务器启动 FastAPI | Image by author 的命令行
FastAPI 端点将在本地机器上提供服务,我们可以在127.0.0.1:8000/docs
(或localhost:8000/docs
)访问 API 接口
浏览器中的 FastAPI 用户界面|作者图片
我们看到我们的/predict
路径操作符出现在 FastAPI 接口的 /docs 页面上,表明 API 已经准备好通过 POST 请求接收数据。
当我们点击 POST 部分时,我们看到 API 已经被正确设置为接收二进制文件,例如 CSV 文件。
FastAPI endpoint |作者图片的帖子部分
此时,我们已经可以通过使用 Jupyter 笔记本中的requests
模块和几行 Python 代码来检索预测了。
然而,让这个系统更上一层楼的一个好方法是建立一个带有图形用户界面的网络应用,用户只需点击几次鼠标就可以上传数据和下载预测。
(八)设置和构建 Streamlit 网络应用程序
Streamlit 提供了一个简单的框架来构建 web 应用,使数据团队能够随时展示他们的 ML 产品。
web 应用程序的目标是让用户上传数据文件,运行模型推理,然后将预测下载为 JSON 文件,所有这些都无需编写任何代码。
我们创建了一个新脚本app.py
来存放以下关键组件的 Streamlit 代码:
- 用于选择 CSV 或 Excel 测试数据集文件的文件上传程序
- 将文件作为 Pandas 数据帧读取,并显示前五行(用于完整性检查)
- 将数据帧转换成一个字节的文件对象
- 显示一个'开始预测'按钮,点击该按钮,将通过
requests.post()
将 BytesIO 对象解析为 FastAPI 端点 - 完成模型推断后,会出现一个“下载”按钮,供用户将预测下载为 JSON 文件。
作为模型推理的一部分,我们还包括端点的定义— [http://localhost:8000/predict](http://localhost:8000/predict.)
。
在 GitHub repo 中,可以在app . py脚本中找到完整的 Streamlit 代码。
㈨初始化 FastAPI 和 Streamlit 服务器
到目前为止,FastAPI 和 Streamlit 函数的所有 Python 脚本都已经准备好了。接下来,我们在命令提示符下初始化两个服务器。
(1)如果您的uvicon服务器(用于 FastAPI)仍然从 步骤 vii 运行,您可以保持原样。否则,打开一个新的命令提示符,转到包含main.py
脚本的/backend
子目录,并输入:
*uvicorn main:app --host 0.0.0.0 --port 8000*
(2)打开另一个单独的命令提示符,用streamlit run app.py
初始化 Streamlit 服务器(从包含脚本的frontend
文件夹)
运行 start Streamlit server | Image by author 的命令行
(x)运行预测并下载结果
随着 FastAPI 和 Streamlit 服务器的启动和运行,是时候通过前往http://localhost:8501
访问我们的 Streamlit web 应用程序了。
下面这张 gif 图说明了 web app 从数据上传到预测下载的简单性。
**注:用于演示预测的文件为data/processed/test . CSV
Streamlit web 应用程序演示|作者图片
以下是 JSON 输出的快照,包含对每个客户的预测(1 或 0):
包含模型预测的 JSON 输出|作者图片
(4)向前迈进
让我们回顾一下我们所做的事情。我们首先进行 H2O AutoML 培训和 MLflow 跟踪,开发各种 ML 模型。
然后,我们检索性能最佳的模型,并使用 FastAPI 将其部署为端点。最后,我们创建了一个 Streamlit web 应用程序来轻松上传测试集数据和下载模型预测。
你可以在这里 找到项目 GitHub repo 。
虽然到目前为止,在成功构建我们的端到端 ML 管道方面已经做了大量的工作,但是我们还需要几个步骤来完成整个画面。这些任务包括:
- 将整个 ML 应用程序和部署放在云上的docking
- Docker 中的管道编排(如气流、Kedro)
下面是这篇文章的续集,我们使用 Docker 来封装整个管道:
* *
在你走之前
欢迎您加入我们的数据科学学*之旅!关注我的 Medium 页面和 GitHub 了解更多精彩的数据科学内容。同时,享受构建端到端 AutoML 解决方案的乐趣!
* https://kennethleungty.medium.com/membership *
使用 Python 进行端到端乳腺癌检测—第 1 部分
第 1 部分:用 YOLOv4 检测乳腺肿块
来自已实现框架的检测。图片作者。
根据cancer.org 的说法,乳腺癌是美国女性最常见的癌症。在美国,女性患乳腺癌的几率是八分之一。在这一系列文章中,我们将展示如何应用深度学*和图像处理来检测恶性乳腺肿块。我们将首先关注检测乳腺肿块(第 1 部分),然后我们将对其进行分割(第 2 部分),最后我们将对肿块进行良性或恶性分类(第 3 部分)。
数据描述
存在两种类型的乳房 x 线照相术:胶片屏和最*的全视场数字乳房 x 线照相术。在本文中,我使用了广播数据集中的。它包含 115 个病例,共 410 张 DICOM 格式的全视场数字乳腺摄影**。数据库中记录了四种不同类型的乳腺疾病,包括肿块、钙化、不对称和扭曲。仅选择肿块病变,得到 106 幅图像。每幅图像都配有由专家以 XML 格式制作的精确轮廓。对于检测部分,由于我们不需要分割质量,轮廓被转换成包围盒。下面您可以找到将病灶的遮罩转换为 YOLO 格式注释的代码:
下面显示了数据集中的一些样本以及相应生成的边界框:
为 INbreast 数据集生成的 bboxes 示例
图像处理
种植
正如我们所看到的,许多像素是背景像素,没有带来任何信息。为了避免质量像素的比例被低估,我们可以裁剪 ROI,即乳房区域。这可以通过 OpenCV 和 Otsu 的阈值技术来实现:
裁剪 ROI 后的结果。
然后,受[1]的启发,我应用了 3 个预处理步骤。这 3 个步骤包括:
- 截断归一化
- 图像增强
- 图像合成
截断归一化
即使在裁剪后,图像仍然由许多黑色像素组成。如果我们按原样对图像进行归一化,这可能会对检测产生负面影响(因为乳房区域会显得不那么强烈)。这里的目标是标准化乳房区域中像素的强度分布。可以参考原文看算法细节[1]。下面我提供了我的 python 实现:
乳房感兴趣区域像素截断和归一化后的结果。
图像增强
为了增强乳房区域以及肿块病变的对比度,应用了对比度受限的自适应直方图均衡化(CLAHE)算法,限幅 1 和 2:
应用 clip_limit=2 的 CLAHE 后的结果
图像合成
应用这三个步骤后,肿块病变变得更加清晰。这将提高 Yolov4 的准确性。在最后一步中,合成 3 通道图像,并由截断和归一化图像、具有限幅 1 的对比度增强图像和具有限幅 2 的对比度增强图像组成[1]。
图像增强管道的最终结果。
培训程序
YOLOv4 已用于对增强图像执行质量检测。与两阶段检测器相比,当需要上下文时,一阶段检测器特别有效。如[1]中所解释的,当背景独立于前景时,两阶段检测器是有用的,因为第一阶段提取 ROI。然而,Cao,H [1]提到,在乳腺肿块检测中,病变并不独立于乳腺区域,因此一级检测器可能更有效。为了正确评估算法的性能,我将数据集分成 3 部分:
- 训练集由 80%的图像组成
- 验证集由 10%的图像组成
- 测试集由 10%的图像组成
由于生成的训练集很小,已经应用了数据扩充(仅在训练集上)。每张图片都被随机旋转、翻转和移动放大了 8 倍。YOLO 负责其他增强,如镶嵌数据增强等;参见[2]。我使用与上述相同的程序进行了双重交叉验证。
结果
为了评估检测器,我使用了召回率和假阴性率(FNR)指标。请注意,在癌症检测中,具有非常低的 FNR 和高召回率非常重要(否则我们会错过潜在的癌症)。如果预测边界框和地面真值之间的并集上的交集(IOU)大于 0.25,则检测被认为是真阳性(TP)。此外,边界框只有在其置信度大于 0.25 时才会被考虑。在验证集和测试集上对结果进行了评估。
Validation set evaluation
+--------+----+----+----+--------+------+
| | TP | FP | FN | Recall | FNR |
+--------+----+----+----+--------+------+
| Fold 1 | 10 | 3 | 1 | 0.91 | 0.07 |
| Fold 2 | 11 | 1 | 1 | 0.92 | 0.08 |
+--------+----+----+----+--------+------+ Test set evaluation
+--------+----+----+----+--------+------+
| | TP | FP | FN | Recall | FNR |
+--------+----+----+----+--------+------+
| Fold 1 | 12 | 1 | 0 | 1 | 0 |
| Fold 2 | 11 | 4 | 2 | 0.85 | 0.13 |
+--------+----+----+----+--------+------+
YOLOv4 在乳腺肿块检测中似乎非常高效。我在验证和测试集上实现了 92% 的平均召回率,这与文献中的最新结果一致(【1】实现了 91.3% 的召回率)。该模型似乎在 fold2 测试集中的小肿块病变上有点困难。我在 8GB 英伟达 RTX 2080 GPU 上训练 Yolov4。这迫使我将图像大小限制在 512x512。拥有更多的资源和数据可以让我们在更强大的模型上训练算法,如最先进的 YOLOv4-P7 和更高分辨率的图像。这将提高小质量区域的精度。以下是两次折叠期间测试集的结果(看不见的图像):
我们的检测器应用于 fold-1 的不可见图像:100%召回。红色代表预测,黄色代表事实
我们的检测器应用于 fold-2 的不可见图像:85%的召回率。红色代表预测,黄色代表事实
结论
我们用 YOLOv4 用 python 训练了一个高精度的乳腺肿块检测器。这部分归功于有效的图像预处理步骤。结果真的很有希望,并且与文献中的结果相似。在接下来的文章中,我们将看到如何分割质量。已经具有能够裁剪质量的检测器将有助于仅在质量区域上训练分割模型。然后我们再对一个肿块是否恶性进行分类。敬请期待;)
注意:我提供了在 我的 github 😃 上为训练 YOLO 创建数据集和我的配置文件的脚本
参考文献
- [1]曹,H. (2020).基于无锚架构的数字乳腺摄影中乳腺肿块检测。 ArXiv,abs/2009.00857 。
- [2]波奇科夫斯基,王,陈,廖,H. (2020).YOLOv4:物体检测的最佳速度和精确度。 ArXiv,abs/2004.10934 。
- [3]安塔里·马,马斯尼·马,崔山,韩,金茨。一个完全集成的计算机辅助诊断系统,用于通过深度学*检测、分割和分类进行数字 X 射线乳房 x 光检查。国际医学信息学杂志。2018 年 9 月;117:44–54.DOI:10.1016/j . ijmedinf . 2018 . 06 . 003
端到端案例研究(分类):Lending Club 数据
Avinash Kumar 在 Unsplash 上拍摄的照片
Lending Club 是一个借贷平台,它以基于信用记录和其他因素的利率将钱借给有需要的人。在这篇博客中,我们将分析这些数据,并根据我们的需要对其进行预处理,并建立一个机器学*模型,该模型可以根据他/她与 Lending Club 的交易历史来识别潜在的违约者。你可以在这里 找到数据 。
该数据集包含 42538 行和 144 列。在这 144 列中,大多数列的值为空。
事实上,总体数据中有 63.15%的值是空值。因此,小心处理这些空值非常重要,因为它们会显著影响我们的结果。
空值可视化绘图:
处理空值:
处理空值是这里的一项重要任务。在下面的代码中,您可以看到 144 列中只有 53 列的空值小于 40%。
表 1
在上表中,每行代表 144 列中空值少于特定百分比的列数。例如,行 1 表示有 52 列,每列中的空值不到 10%。
通过考虑空值少于 40%的列,我们能够将总列数从 144 减少到 53。
了解特征
理解特征/列很重要,因为数据中出现的一些分类列是以数值的形式出现的,反之亦然。我首先尝试检查每一列,但后来明白对所有 53 列执行这些操作会非常麻烦。因此,我决定首先删除那些不会给数据增加价值的列,然后分析每个字段。
检查对象:
丢弃不必要的对象:
检查数字列:
删除不必要的数字列:
在检查数据之后,我们已经删除了这 53 列中总共 18 列没有为我们的数据增加价值的数据。我们能够将列数从 53 列减少到 35 列,我们仍将努力减少列数。
将分类列转换为数字列:
我们已经将分类列转换为数字列,方法是根据它们表示的数据类型执行一键编码或标签编码。例如,对 ['home_ownership ',' verification_status ',' purpose ']【T1]列执行一个热编码,而对“grade”和“sub grade”列执行标签编码,因为它们本质上是序数。
一个热门编码:
标签编码:
用标签编码值更新等级列:
将日期时间列转换为数字列:
列 ['issue_d ',' last_pymnt_d ',' last_credit_pull_d'] 是日期时间列,通过使用 pandas 日期时间模块进一步分为月和年。新列分别命名为'发行日','发行日','发行日','最后一年','最后一年','最后一年','最后一年','最后一年','最后一年','最后一年'。
将对象转换为数字列:
列 int_rate 和 term 被存储为对象。我们已经执行了必要的字符串操作,将它们转换成数字列。
检查相关性:现在我们已经将所有列转换为数字列,我们将检查相关性。
相关热图
有几列具有高相关性,但是在解决我们的问题时没有考虑这些列。例如,在尝试对贷款是否将由客户偿还进行分类时,我们不会考虑任何未来交易,如 total_pymnt 和 total_pymnt_inv 。因此,这里没有删除这些列。
处理空值:
让我们在显著清理列之后检查是否有空值。
空值图
正如我们所看到的,我们仍然可以在数据中找到一些空值。我们将检查这些空值并采取必要的措施。
让我们检查空值百分比最高的列。
空值的百分比
有些列的空值百分比非常小(不到 1%)。在那里,我们可以用各自列的中值替换空值。
对于空值百分比很高的列,我们将在非空值的基础上运行模型,并预测相应列中缺少的值。
由于没有空值,我们将进入下一步,即建立机器学*模型。
分类
我们分类任务的目标是确定客户(请求贷款的人)是否能够偿还贷款以及利息金额。因为我们有一些列包含贷款日期的未来交易信息(如贷款后每月支付贷款分期付款等。),我们将它们从预处理的数据集中删除,以进行分类任务。
我们分类任务的目标是根据客户(请求贷款的客户)在获得贷款后与贷方的历史交易来确定其是否会违约。
让我们删除几个包含冲销贷款信息的列。
分类中删除的列:
['total_pymnt ',' total_pymnt_inv ',' total_rec_prncp ',' total_rec_int ',' total_rec_late_fee ',' recoveries']
“loan_status”列用作目标变量,根据记录对客户进行分类。“loan_status”列有 4 个唯一值,为了便于表示,所有值都用标签编码。该列的标签如下:
使用 VIF 检查要素之间的多重共线性,然后删除值高于阈值的列。
我们已经删除了具有高 VIF 因子(10 或以上)的列。
模型建筑
在对目标变量进行标签编码后,我们将数据以 70:30 的比例拆分为训练和测试数据。
我们使用了 sklearn 的 cross_val_score 和以得分为 f1 分的网格搜索 cv 来考察每个模型在每个折叠中的表现。下图显示了每个型号在 3 个折叠中的 F1 分数。橙色线代表每个模型的平均 F1 分数,而 IQR 代表这些分数的方差。
从上图我们可以说 bagging 分类器是最稳定的模型,加权 F1 得分的均值最高,方差最小。
使用 bagging 分类器建立模型。
装袋分级机的分类报告
该模型的准确率为 0.89,F1 平均得分为 0.75。
最终混淆矩阵:
Bagging 分类器模型的混淆矩阵
由于 bagging 分类器没有特征重要性选项,我们使用决策树来寻找特征重要性。
决策树模型的特征重要性
结论:
在这篇博客中,我们广泛地介绍了这些数据所需的预处理步骤,然后使用网格搜索和 KFolds 找到了最适合的模型。我希望这篇博客已经让你对解决分类问题有了一个总体的了解。更多详细代码,请参考https://github.com/pawanreddy-u/lendingclub9