对数据帧使用 Pandas Append 函数
数据科学家的 python 教程,用于向已建立的数据帧追加新行
Theodor Lundqvist 在Unsplash【1】上拍摄的照片。
目录
- 资料组
- 熊猫
- 附加
- 教程代码
- 摘要
- 参考
资料组
本分析和教程中用于熊猫追加函数的数据集是一个虚拟数据集,创建该数据集是为了模拟具有文本和数字特征的数据帧。请随意使用您自己的 csv 文件,包含文本和/或数字列,以遵循下面的教程。
熊猫
Pandas【2】是数据科学家和机器学习工程师最常用的库之一。它主要用于构建模型的探索性数据分析步骤,以及模型结果的即席分析。它还包含几个函数,包括 append 函数。
附加
虽然我之前写了一篇关于 pandas 查询函数的文章[3 ],其中提到了添加多行,但我认为强调 append 函数本身的好处是有用的。append 函数通过向现有数据帧添加新行来工作。这些行是利用 Series 函数创建的,该函数存储的值将被追加到 index 参数中列出的列的顺序中。
附加功能的一些好处包括:
- 处理熊猫数据帧
- 与熊猫系列功能配合使用
- 向现有数据添加新数据的快速方法
教程代码
在下面的代码片段中,虚拟数据缺少一些信息。为了解决这个问题,我求助于 append 方法。使用 append 函数,我可以轻松地添加新行,而不会丢失原始数据帧的数据和完整性。首先,我导入了 pandas 库并创建了别名‘PD*’——这个别名有助于以简写格式使用 pandas 库。接下来,我从现有的 csv 文件中读取数据帧,引用它在本地驱动器上的位置和路径。我打印出数据帧,看看前五行是什么样子。下一步是设置 append 函数的代码。使用 Series 函数按顺序将值(在本例中为文本或数字)作为列表放置,然后在各自的列上对其进行索引,新行被写出并保存为‘rows’*。
实际的 append 函数本身相当简单;将这些行作为第一个参数传递,然后将’ ignore_index’ 设置为’True【T3]',这样就不会使用索引标签。最后,我使用 tail 函数检查我的行是否正确,该函数从 append 代码返回最后三行的数据帧。
下面是我使用的所有代码的截图:
追加 3 个新行的示例。作者截图[4]。
这里是代码【5】便于参考。请随意导入您自己的数据和引用您自己的文件路径,以及更改系列中的数据值,以查看在您的 python 环境或 jupyter 笔记本中追加是如何工作的。
# All the python code below for use:
# import library
import pandas as pd# read in your dataframe
df = pd.read_csv('/Users/data.csv')# print out your first five rows
df.head()# write out new rows
rows = [pd.Series([200, 200, 30,'Yellow','Label_1'], index=df.columns),
pd.Series([200, 120, 40,'Red','Label_2'], index=df.columns),
pd.Series([120, 40, 200,'Red','Label_3'], index=df.columns)]# append the multiple rows
new_df = df.append(rows, ignore_index=True)# check the newest 3 rows you made
new_df.tail(3)
要查看 python 格式的代码,而不是截图或写出来的代码,下面是嵌入的要点 [5]:
GitHub 要点来自作者【5】。
摘要
append 函数是向数据帧添加新数据的一种简单快捷的方法。还引用了 Series 函数,以便保存以后要追加的行。在最终创建数据科学和机器学习模型之前,我希望本教程对您创建数据和在数据框架上执行探索性数据分析有所帮助。感谢您的阅读!如上所述,请随意阅读我的类似的文章,它讨论了熊猫的查询功能。
参考
[1] T.Lundqvist , Unsplash (2017)
[2]熊猫,熊猫 (2020)
[3] M.Przybyla,条 (2020 年)
[4] M.Przybyla,截图(2020 年)
[5] M.Przybyla,要点 (2020)
在日常生活中使用熊猫数据框架
在这篇文章中,你将知道如何在日常生活中使用熊猫数据框。
B 在开始之前,让我们快速复习一下,Pandas 是一个 python 库,它为 python 编程语言的数据分析工具提供了优秀的、简单易用的信息结构。下面的文章将帮助你提高对熊猫图书馆的理解水平。别忘了看一看。事实上,这是官方文件。值得一读。
pandas 是一个快速、强大、灵活且易于使用的开源数据分析和操作工具,构建于…
pandas.pydata.org](https://pandas.pydata.org/)
帮助您入门的必要设置
有两个重要的必要步骤,你需要注意,以执行顺利运作:
- IDE 或者 Jupyter 笔记本:不管你用的是像 PyCharm 、Visual Studio Code、 IntelliJ IDEA 还是 Jupyter 笔记本 这样的 IDE 环境,其实都不重要。我个人用的是Google Colabwith isJypter 笔记本环境 因为它良好的文档化特性。我们可以更清晰、更准确地解释事物。
- 如果这是你第一次安装 Python,不要担心,我会支持你的。遵循下面给出的文章,并遵循有效安装 Python 的方法。
[## Python 3 安装和设置指南-真正的 Python
开始使用 Python 的第一步是在您的机器上安装它。在本教程中,您将学习如何…
realpython.com](https://realpython.com/installing-python/)
注意 → 如果你正在使用Google Colab或者任何Jupyter 笔记本 环境,那么你可以跳过 Python 安装这一步。
方案
让我们在这里理解一个场景,并使用 Pandas DataFrame 提出一个解决方案。
每个人都喜欢购物。不是所有时候你都在同一家商店购物,你是基于折扣和优惠购物的(我就是这样)。现在,很明显,记录购物细节是一个很好的做法,比如商店名称、位置、数量、日期等。”
为了存储和操作日期,我知道你会使用“”Microsoft Excel”但是这里的要点是我们将使用“ Pandas DataFrame ”,它使用起来更容易也更有趣。别担心,我会告诉你怎么做。
别误会, 微软 Excel “就是它在的地方。但是对于所有的 Python 爱好者来说,这是理解熊猫并使其更上一层楼的最好例子。
解决办法
让我们使用 Pandas DataFrame 来解决上面的场景。为了做到这一点,你需要遵循一定的步骤来避免犯错误。我会提供一个使用熊猫的系统方法,这样你就可以在你即将到来的项目中机械地使用它。
导入 Pandas 数据帧库
我们用走样的概念,用熊猫当**pd**
。所以在后面的步骤中,与其每次都使用**pandas**
,我们可以直接告诉**pd**
。
import **pandas** as **pd**
感谢引入混叠概念的人。
第一次将购物细节存储在列表中
对于那些不了解 Python 列表的人来说。请在这里查找。因为我现在住在加拿大萨斯喀彻温省的里贾纳。所以大多数杂货店和你的相比,可能相似,也可能不同。请记住这一点。这真的没关系,你可以根据自己的选择输入数据。
**date** = ['1-9-20', '3-9-20', '3-9-20', '6-9-20', '9-9-20']**storeName** = ['Walmart', 'Real Canadian Superstore', 'Co-op Food Store', 'Sobeys', 'M&M Food Market']**storeLocation** = ['Gordon Road', 'Albert Street', 'Albert Street', 'Quance Street', 'Gordon Street']**amount** = [55.65, 21.62, 7.10, 15.56, 5.85]
现在,我说的“第一次是什么意思,就是以后说假设你以后继续去买菜,而不是手动把值存储在列表里。这个列表将会增长到一列火车那么长。另外,不建议这样做。所以我在下面的部分写了一个处理这种情况的方法。
创建一个 Pandas 数据框架来存储所有的列表值
在这里,因为我们将所有的值都存储在一个列表中,所以让我们将它们放在一个数据帧中。我们可以使用**pd.DataFrame()**
并传递值,这种情况下是所有的列表。
df = pd.DataFrame({'Date': date,
'Store Name': storeName,
'Store Location': storeLocation,
'Amount Purchased': amount})df
执行此操作后,您将得到一个漂亮的数据帧,如下所示
塔努·南达·帕布拍摄的照片
将购物细节作为用户的输入——用于将来的访问
正如我所说的,将所有值存储在一个列表中并不是一个很好的做法。这样,我们自动使列表增长。处理这种情况有一个简洁而可爱的方法。首先,让我们从输入中提取输入,并将它们存储在临时变量中,如下所示:
**dateNew** = input("Enter the date in dd-mm-yy format ---> ")
**storeNameNew** = input("Enter the name of the store ---> ")
**storeLocationNew** = input("Enter the location of the store ---> ")
**amountNew** = float(input("Enter the total amount purchased ---> "))
所以第二天,我的意思是**10–9–20**
我和戈登·拉姆齐一起去杂货店买了些香料,开玩笑我是一个人去的。以下是新店的详细情况。
**Enter the date in dd-mm-yy format** ---> 10-9-20
**Enter the name of the store** ---> India Market
**Enter the location of the store** ---> Albert Street
**Enter the total amount purchased** ---> 24.68
将新数据追加到现有列表中
这是一个明显的步骤,因为我们需要将新数据(购物细节)追加到现有的数据帧中。我们可以在**append()**
Python 的帮助下做到这一点。
date.**append**(dateNew)
storeName.**append**(storeNameNew)
storeLocation.**append**(storeLocationNew)
amount.**append**(amountNew)
通过数据帧显示更新的结果
这一步很简单,因为我们所做的只是将上述步骤中所有更新的结果显示为一个数据帧。类似于上面所示的“创建熊猫数据帧以存储所有列表值”步骤。
df = pd.DataFrame({'Date': date,
'Store Name': storeName,
'Store Location': storeLocation,
'Amount': amount})df
执行这段代码后,将提示您上一步中添加的新杂货店的更新结果
塔努·南达·帕布拍摄的照片
这就对了,你已经成功地解决了这个问题。您可以继续添加更多的数据(购物详情)并保持每月或每年明智的。
奖金
您可以在现有数据帧上执行的漂亮技巧
下面是一些你可以在空闲时间执行或做的提示和技巧。因为它们很容易理解。让我来解释一下。
绘制条形图
绘制数据图以获得更好的可读性始终是一个好习惯。现在为了绘图,让我们使用**Amount**
栏,这样我们就可以在绘图的帮助下知道到目前为止我们已经花了多少钱。这可以通过使用如下所示的**df.plot.bar()**
来完成
注意:切记在熊猫 中只能绘制数字数据
df.plot.bar()
在编写这一行代码时,您可以看到如下所示的美丽情节:
塔努·南达·帕布拍摄的照片
删除整行/整列
假设我们需要删除最后一行,因为条目中有一个错误,所以可以按如下方式执行。现在要删除一整行,我们需要在熊猫中使用**drop()**
,如下所示:
df = df.**drop**([5])
df
这里的值**5**
是最后一行的索引值。通过执行这个,我们得到
塔努·南达·帕布拍摄的照片
删除列就像删除行一样,你所要做的就是传递要删除的列名,这种情况下就像**df.drop([column_name, axis = 1])**``**axis = 1**
在这里很重要。假设您需要删除数据帧中的列**Amount**
,那么您只需要说:
df = df.**drop**(['Amount'], axis = 1)
df
通过执行该操作,您现在可以看到列**Amount**
已经被删除。
塔努·南达·帕布拍摄的照片
修改特定条目
假设我们需要更新数据帧中的特定条目,在这种情况下,存储名称为**M&M Food Market**
的第 4 个索引在其存储位置有一个错误(错误条目)**Gordon Street**
,我们需要将其更正为**Gordon Road**
。为此,只需使用:
df['Store Location'][4] = "Gordon Road"
df
我们需要知道特定条目的索引来更新条目,所以在执行上述操作之后,我们得到了更新的结果
塔努·南达·帕布拍摄的照片
结论
好了,祝贺你们,你们已经成功地阅读/实现了这篇美丽的文章“在日常生活中使用熊猫数据框架”。现在,这还不是结束,我们可以使用 DataFrame 的许多其他方法或函数,并使其更上一层楼。我在这里只涉及了基础知识。如果你们发现了一些新的或有创意的东西,请在下面评论。我希望你们今天学到了新东西。敬请关注更多更新,下次再见。再见,祝你愉快,注意安全!
使用 R 中的预测能力评分
最近一个关于预测能力得分的帖子吸引了许多数据科学家的注意。让我们看看它是什么以及如何在 R 中使用它
最近几个月 Florian Wetschoreck 在《走向数据科学》的媒体频道上发表了一篇文章,由于其极具煽动性的标题:“RIP correlation”,这篇文章在 LinkedIn 上吸引了许多数据科学家的注意。介绍预测能力评分"。让我们看看它是什么,以及如何在 r 中使用它。
预测能力得分的定义
预测能力得分 (PPS)是一个标准化指数(范围从 0 到 1),它告诉我们有多少变量 x (无论是数字还是分类)可用于预测变量 y (数字还是分类)。PPS 指数越高,变量 x 在预测变量 y 时越具有决定性。
PPS 与相关系数在概念上的相似性是显而易见的,尽管存在以下差异:
- PPS 还检测 x 和y之间的非线性关系
- PPS 不是对称指数。这就意味着 PPS( x 、 y ) ≠ PPS( y 、 x )。换句话说,并不是说如果 x 预测 y ,那么 y 也预测 x 。
- PPS 允许数字和分类变量。
基本上,PPS 是一个非对称的非线性指数,适用于所有类型的变量进行预测。
在幕后,它实现决策树作为学习算法,因为它们对异常值和糟糕的数据预处理具有鲁棒性。
分数是在 sci kit-learncross _ val _ score函数给出的默认 4 重交叉验证的测试集上计算的,并根据目标变量定义的问题类型(回归或分类)由不同的度量给出:
- 回归:给定 y 的“原始”值基线,将平均绝对误差(MAE)归一化为目标变量的中值。
- 分类:给定作为目标变量最常见值或随机值计算的 y 的“原始”值基线(有时,随机值比最常见值具有更高的 F1),归一化到[0,1]区间的加权 F1。
你可以深入了解 Python 代码的细节,这要归功于预测能力评分项目在 Github 上作为开源发布的事实。
皮尔逊相关与 PPS
这两个指数来自不同的领域,必须进行基本区分:
- 皮尔逊相关性由两个数值变量之间的归一化协方差给出。协方差取决于两个变量相对于各自均值的偏差,因此它是一种统计度量。给定两个数值变量,Pearson correlation 是一个描述性指标在数学上得到很好的定义,它给出了描述变量之间关系的最佳线性函数的拟合优度。
- PPS 试图通过应用决策树估计来解决相关性分析中仅测量线性相关性和仅测量数字变量的问题。它来源于该评估的性能指标。在撰写本文时(版本 1.1.0),默认情况下,它从输入数据集中获得一个 5000 行的随机样本(如果需要,可以分层),以加快计算速度(样本的大小可以使用适当的参数进行修改)。没有进行任何调整来获得决策树的最佳模型参数。因此,存在过度配合或配合不足的可能性。此外,由于算法固有的随机性(随机种子可用于保证结果的可重复性),在同一数据集上每次运行时,PPS 结果可能不同。所以,PPS 可能不准确。但是它的目的不是给出一个精确的分数,而是给出依赖的一般概念和一个快速的结果。
这两个索引都应该在勘探数据分析阶段使用。正如同一位作者所说:
对于在数据中寻找预测模式而言,PPS 明显优于相关性。然而,一旦发现了模式,相关性仍然是传达发现的线性关系的一个很好的方式。
PPS 使用案例
作者在他的文章中列出了几个 PPS 可能增加价值的用例:
- 查找关联找到的每一个关系,甚至更多
- 特征选择
- 检测信息泄漏
- 通过将 PPS 矩阵解释为有向图,找到数据中的实体结构。
如何在自己的研发项目中使用 PPS
【https://github.com/paulvanderlaken/ppsr】更新:2020 年 12 月下旬研发出 R 专用包:
如前所述,项目 ppscore 是开源的,它是用 Python 开发的。R 中目前没有 ppscore 的移植项目,那么如何在 R 脚本中使用 ppscore 函数呢?由于为 Python 和 R 之间的互操作性而创建的一些库,可以将 R 的强大功能与 Python 的编程能力结合起来使用,反之亦然。在其他库中,有两个库使用最广泛:
- rpy2: 嵌入在 Python 进程中的 R running 接口
- reticulate: 由于在 R sessions 中嵌入了 Python 会话,这是一套用于 Python 和 R 之间互操作性的综合工具
图 1——得益于 rpy2 和 reticulate,Python 和 R 之间的互操作性
在我们的场景中, reticulate 将允许从 R 脚本中调用 ppscore 函数。
为 ppscore 准备单独的 Python 环境
首先,如果你没有在你的机器上安装 Python,我建议安装Miniconda(Anaconda 的一个小的引导版本,只包括 conda、Python 和少数其他有用的包),如果是 Windows 机器,让安装程序将 Python 安装路径添加到你的 path 环境变量中。
Python 开发者通常使用虚拟环境。虚拟环境是一种工具,通过创建包含特定 Python 版本的 Python 安装以及许多附加包的自包含目录树,有助于将不同项目所需的依赖关系分开。Conda 是一个帮助管理环境和包的工具。
在这种情况下,将创建一个新的 Python 环境,以便在其上安装 ppscore 所需的包。您可以使用下面的 R 脚本为 ppscore 库准备一个新的 Python 环境:
# Install reticulate
if( !require(reticulate) ) {
install.packages("reticulate")
}# Load reticulate
library(reticulate)# List current python environments
conda_list()# Create a new environemnt called 'test_ppscore'
conda_create(envname = "test_ppscore")# Install the ppscore package and its dependencies using pip into the test_ppscore environment.
# Requirements are listed here: [https://github.com/8080labs/ppscore/blob/master/requirements.txt](https://github.com/8080labs/ppscore/blob/master/requirements.txt)
conda_install(envname = "test_ppscore", packages = "pandas", pip = TRUE)
conda_install(envname = "test_ppscore", packages = "scikit-learn", pip = TRUE)
conda_install(envname = "test_ppscore", packages = "ppscore", pip = TRUE)# Check if the new environment is now listed
conda_list()# Make sure to use the new environment
use_condaenv("test_ppscore")# Import the ppscore Python module in your R session
pps <- import(module = "ppscore")
Python 模块和类中的函数和其他数据可以通过$
操作符访问(以及与 R 列表交互)。导入的 Python 模块支持代码完成和内联帮助。例如, pps 模块中的matrix
功能可通过以下方式访问:
图 2 — pps 模块的矩阵功能
如果你想了解更多关于网纹的信息,你可以通过它的主页。
《泰坦尼克号》预测因子和可变重要性的 PPScore 热图
从作者包含在资源库中的示例开始,可以使用matrix
函数复制 Titanic PPScore 热图:
图 3 — PPScore 热图
给定目标“幸存”的变量重要性可通过适当过滤由matrix
函数给出的输出(预测器之间的完全相互作用)获得。但是也可以直接使用predictor
函数直接得到相同的结果,注意在两个函数中使用相同的random_seed
参数,以避免得到不同的结果。
给定目标变量“存活”的变量重要性图如下:
图 4 —使用 PPS 的可变重要性图
用于获得上述图的 R 代码如下:
library(reticulate)
library(readr)
library(dplyr)
library(ggplot2)heatmap <- function(df, x, y, value,
main_title = "Heatmap", legend_title = "Value",
x_title = "feature", y_title = "target") {
x_quo <- enquo(x)
y_quo <- enquo(y)
value_quo <- enquo(value)
res <- ggplot( df, aes(x = !!x_quo, y = !!y_quo, fill = !!value_quo) ) +
geom_tile(color = "white") +
scale_fill_gradient2(low = "white", high = "steelblue",
limit = c(0,1), space = "Lab",
name="PPScore") +
theme_minimal()+ # minimal theme
# theme(axis.text.x = element_text(angle = 45, vjust = 1,
# size = 12, hjust = 1)) +
coord_fixed() +
geom_text(aes(x, y, label = round(!!value_quo, 2)), color = "black", size = 4) +
theme(
axis.text.x = element_text(angle = 45, vjust = 1,
size = 12, hjust = 1),
axis.text.y = element_text(size = 12),
panel.grid.major = element_blank(),
panel.border = element_blank(),
panel.background = element_blank(),
axis.ticks = element_blank()
) +
xlab(x_title) +
ylab(y_title) +
labs(fill = legend_title) +
guides(fill = guide_colorbar(barwidth = 1, barheight = 10,
title.position = "top", title.hjust = 1)) +
ggtitle(main_title)
return(res)
}lollipop <- function(df, x, y,
main_title = "Variable Importance",
x_title = "PPScore", y_title = "Predictors",
caption_title = "Data from Titanic dataset") {
x_quo <- enquo(x)
y_quo <- enquo(y)
res <- ggplot(df, aes(x=!!x_quo, y=forcats::fct_reorder(!!y_quo, !!x_quo, .desc=FALSE))) +
geom_segment( aes(x = 0,
y=forcats::fct_reorder(!!y_quo, !!x_quo, .desc=FALSE),
xend = !!x_quo,
yend = forcats::fct_reorder(!!y_quo, !!x_quo, .desc=FALSE)),
color = "gray50") +
geom_point( color = "darkorange" ) +
labs(x = x_title, y = y_title,
title = main_title,
#subtitle = "subtitle",
caption = caption_title) +
theme_minimal() +
geom_text(aes(label=round(!!x_quo, 2)), hjust=-.5, size = 3.5
) +
theme(panel.border = element_blank(),
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
axis.line = element_blank(),
axis.text.x = element_blank())
return(res)
}df <- read_csv("[https://raw.githubusercontent.com/8080labs/ppscore/master/examples/titanic.csv](https://raw.githubusercontent.com/8080labs/ppscore/master/examples/titanic.csv)")df <- df %>%
mutate( Survived = as.factor(Survived) ) %>%
mutate( across(where(is.character), as.factor) ) %>%
select(
Survived,
Class = Pclass,
Sex,
Age,
TicketID = Ticket,
TicketPrice = Fare,
Port = Embarked
)use_condaenv("test_ppscore")pps <- import(module = "ppscore")# PPScore heatmap
score <- pps$matrix(df = df, random_seed = 1234L)score %>% heatmap(x = x, y = y, value = ppscore,
main_title = "PPScore for Titanic's predictors", legend_title = "PPScore")# Variable importance
vi <- pps$predictors( df = df, y = "Survived", random_seed = 1234L)vi %>%
mutate( x = as.factor(x) ) %>%
lollipop( ppscore, x,
main_title = "Variable Importance for target = 'Survived'",
x_title = "PPScore", y_title = "Predictors",
caption_title = "Data from Titanic dataset")
PPS 和与各种分布的相关性
Denis Boigelot 在 Wikimedia Commons 上发布了一张关于 Pearson 关联示例的非常著名的图片,并分享了用于获得这张图片的 R 代码:
图 Boigelot 分布的皮尔逊相关性
从这段代码开始,可以计算相同分布的 PPScore。这里是 X→Y 的结果:
图 Boigelot 分布中“x 预测 y”的 PPScore
这里是 Y→X 的结果:
图 Boigelot 分布中“y 预测 x”的 PPScore
很明显,在写这篇文章的时候
PPScore 很难在原点线存在最小离散点的情况下确定线性关系(如前面图片中黄色和红色突出显示的)。
这些案例表明
为了避免得出错误的结论,使用相关指数来检查线性是多么重要。
我与作者分享了这些结果,他证实他们正在研究解决线性关系问题的解决方案。他还在这个环节分享了为什么 PPScore 1.1.0 不能很好地处理线性关系的原因。
相反,PPScore 在 Pearson 相关性不足的一些分布上提供了良好的结果(如前面图片中绿色突出显示的)。
用于获得上述 Boigelot 发行版的 R 代码如下:
# Install packages
pkgs <- c("dplyr","ggplot2","ggpubr","mvtnorm")for (pkg in pkgs) {
if (! (pkg %in% rownames(installed.packages()))) { install.packages(pkg) }
}# Load packages
library(mvtnorm)
library(dplyr)
library(ggplot2)
library(ggpubr)
library(reticulate)# Functions
MyPlot <- function(xy, xlim = c(-4, 4), ylim = c(-4, 4), eps = 1e-15,
metric = c("cor", "ppsxy", "ppsyx")) {
metric <- metric[1]
df <- as.data.frame(xy)
names(df) <- c("x", "y")
if (metric == "cor") {
value <- round(cor(xy[,1], xy[,2]), 1)
if (sd(xy[,2]) < eps) {
#title <- bquote("corr = " * "undef") # corr. coeff. is undefined
title <- paste0("corr = NA") # corr. coeff. is undefined
} else {
#title <- bquote("corr = " * .(value))
title <- paste0("corr = ", value)
}
subtitle <- NULL
} else if (metric == "ppsxy") {
pps_df <- pps$matrix(df = df, random_seed = 1111L)
value <- pps_df %>%
filter( x == "x" & y == "y" ) %>%
mutate( ppscore = round(ppscore, 1) ) %>%
pull(ppscore)
title <- bquote("pps"[X%->%Y] * " = " * .(value))
subtitle <- NULL
} else if (metric == "ppsyx") {
pps_df <- pps$matrix(df = df, random_seed = 1111L)
value <- pps_df %>%
filter( x == "y" & y == "x" ) %>%
mutate( ppscore = round(ppscore, 1) ) %>%
pull(ppscore)
title <- bquote("pps"[Y%->%X] * " = " * .(value))
subtitle <- NULL
}
ggplot(df, aes(x, y)) +
geom_point( color = "darkblue", size = 0.2 ) +
xlim(xlim) +
ylim(ylim) +
labs(title = title,
subtitle = subtitle) +
theme_void() +
theme( plot.title = element_text(size = 10, hjust = .5) )
}MvNormal <- function(n = 1000, cor = 0.8, metric = c("cor", "ppsxy", "ppsyx")) {
metric <- metric[1]
res <- list()
j <- 0
for (i in cor) {
sd <- matrix(c(1, i, i, 1), ncol = 2)
x <- rmvnorm(n, c(0, 0), sd)
j <- j + 1
name <- paste0("p", j)
res[[name]] <- MyPlot(x, metric = metric)
}
return(res)
}rotation <- function(t, X) return(X %*% matrix(c(cos(t), sin(t), -sin(t), cos(t)), ncol = 2))RotNormal <- function(n = 1000, t = pi/2, metric = c("cor", "ppsxy", "ppsyx")) {
metric <- metric[1]
sd <- matrix(c(1, 1, 1, 1), ncol = 2)
x <- rmvnorm(n, c(0, 0), sd)
res <- list()
j <- 0
for (i in t) {
j <- j + 1
name <- paste0("p", j)
res[[name]] <- MyPlot(rotation(i, x), metric = metric)
}
return(res)}Others <- function(n = 1000, metric = c("cor", "ppsxy", "ppsyx")) {
metric <- metric[1]
res <- list()
x <- runif(n, -1, 1)
y <- 4 * (x^2 - 1/2)^2 + runif(n, -1, 1)/3
res[["p1"]] <- MyPlot(cbind(x,y), xlim = c(-1, 1), ylim = c(-1/3, 1+1/3), metric = metric)
y <- runif(n, -1, 1)
xy <- rotation(-pi/8, cbind(x,y))
lim <- sqrt(2+sqrt(2)) / sqrt(2)
res[["p2"]] <- MyPlot(xy, xlim = c(-lim, lim), ylim = c(-lim, lim), metric = metric)
xy <- rotation(-pi/8, xy)
res[["p3"]] <- MyPlot(xy, xlim = c(-sqrt(2), sqrt(2)), ylim = c(-sqrt(2), sqrt(2)), metric = metric)
y <- 2*x^2 + runif(n, -1, 1)
res[["p4"]] <- MyPlot(cbind(x,y), xlim = c(-1, 1), ylim = c(-1, 3), metric = metric)
y <- (x^2 + runif(n, 0, 1/2)) * sample(seq(-1, 1, 2), n, replace = TRUE)
res[["p5"]] <- MyPlot(cbind(x,y), xlim = c(-1.5, 1.5), ylim = c(-1.5, 1.5), metric = metric)
y <- cos(x*pi) + rnorm(n, 0, 1/8)
x <- sin(x*pi) + rnorm(n, 0, 1/8)
res[["p6"]] <- MyPlot(cbind(x,y), xlim = c(-1.5, 1.5), ylim = c(-1.5, 1.5), metric = metric)
xy1 <- rmvnorm(n/4, c( 3, 3))
xy2 <- rmvnorm(n/4, c(-3, 3))
xy3 <- rmvnorm(n/4, c(-3, -3))
xy4 <- rmvnorm(n/4, c( 3, -3))
res[["p7"]] <- MyPlot(rbind(xy1, xy2, xy3, xy4), xlim = c(-3-4, 3+4), ylim = c(-3-4, 3+4), metric = metric)
return(res)
}output <- function( metric = c("cor", "ppsxy", "ppsyx") ) {
metric <- metric[1]
plots1 <- MvNormal( n = 800, cor = c(1.0, 0.8, 0.4, 0.0, -0.4, -0.8, -1.0), metric = metric );
plots2 <- RotNormal(200, c(0, pi/12, pi/6, pi/4, pi/2-pi/6, pi/2-pi/12, pi/2), metric = metric);
plots3 <- Others(800, metric = metric)
ggarrange(
plots1$p1, plots1$p2, plots1$p3, plots1$p4, plots1$p5, plots1$p6, plots1$p7,
plots2$p1, plots2$p2, plots2$p3, plots2$p4, plots2$p5, plots2$p6, plots2$p7,
plots3$p1, plots3$p2, plots3$p3, plots3$p4, plots3$p5, plots3$p6, plots3$p7,
ncol = 7, nrow = 3
)
}#-- Main -------------------------------------
use_condaenv("test_ppscore")pps <- import(module = "ppscore")output( metric = "cor" )
output( metric = "ppsxy" )
output( metric = "ppsyx" )
结论
Florian Wetschoreck 提出的预测能力得分(PPS)指数试图帮助数据科学家给出提示,在项目的 EDA 阶段找到两个变量之间的任何类型的关系,无论它们是数字还是类别。PPS 可能不准确,但它的目的不是给出一个精确的分数,而是给出两个变量之间依赖关系的一般概念和一个快速的结果。
使用 Boigelot 分布表明,PPS(当前版本为 1.1.0)能够识别相关性指数无法识别的非线性关系。相反,PPS 似乎很难识别线性关系,即使这些点与原点线的离散度很小。
虽然 PPScore 是用 Python 开发的,但由于有了 reticulate ,它的函数也可以用在 R 脚本中。
更新。一个 R 专用的包已经在 2020 年 12 月下旬开发出来,你可以在这里找到:https://github.com/paulvanderlaken/ppsr
所有的代码都可以在这个 Github 库上找到。
为你的神经网络使用正确的维度
当建立你的人工神经网络时,一个令人沮丧的错误是把向量和矩阵的形状弄错了。这是理解底层数学运算的简要指南,并阐明了要传入的维度。
用什么形状?(作者图片)
更新— 8 月 8 日 该系列已简化为三集系列。
目标
如果你刚刚开始机器学习,你应该已经遇到了基本的模型,如顺序网络。这些全连接神经网络(FCNN)是在转向更复杂的架构之前理解基本深度学习架构的完美练习。
初学者会发现,通过 Keras 和 TensorFlow 等高级库很容易开始这一旅程,技术细节和数学运算都是从你这里抽象出来的。好处是能够快速启动学习之旅,而不会被数学所困,但如果你不知道自己在做什么,它会很快带来问题。
如果你像我一样,你可能遇到过代码不能按你期望的方式工作的情况,通过反复试验,你似乎能够让它工作。还是做到了?
为了建立基础,我决定参加 Coursera 的在线培训(神经网络和深度学习由 deeplearning.ai 提供),并希望分享我的学习。主要目标是强迫自己写作,在这个过程中,确保我已经真正理解了这些概念。
结构
这是 3 部分系列的第一部分,我们将逐步建立对以下领域的基本理解:
- 为你的神经网络使用正确的维度
- 神经网络是如何学习的?
- 为二元分类构建您自己的神经网络
在本系列结束时,您将理解序列神经网络背后的基本数学,并编写自己的前向和后向传播例程来执行二进制分类。
让我们从第一个主题开始,理解并使用向量和矩阵的正确维数。
为什么选择正确的尺寸很重要
像 TensorFlow 和 Keras 这样的高级库从用户那里抽象出复杂的数学运算。这使得用户更容易开始深度学习的旅程。另一方面,如果没有正确的基础,深度学习有时会感觉像“黑魔法”。
它只是工作。我想。
凭直觉,你可能知道为什么,但不明白到底发生了什么,也无法验证答案。这可能是一个危险的开始,因为如果你没有掌握好基础知识,你可能会发现自己在更复杂的架构中碰壁了。
当我发现自己使用试错法调整代码只是为了让它工作时,我决定我必须从基础开始。
我选择这个主题作为起点,因为我总是对向量和矩阵维数感到困惑。在一些文献中,我看到变量的不同排序。有时,我看到一个换位操作,有时我看不到。
让我们定义一些术语和一致的表示方式,首先使用简单的单个神经元,这将是监督学习 FCNN 架构的构建块。
具有 3 个输入的单神经元(图片由作者提供)
在上图中,我们有 3 个 输入 ,每个输入代表一个独立的特征,我们用它来训练和预测输出。单个神经元的每个输入都有一个与其相关的 权重 ,它形成了正在被训练的参数。有多少个输入信号就有多少个权重进入一个神经元,并且每个神经元有单个 偏差 (实数)。我们使用下面的 线性函数 来计算一个值 z ,然后将其传递给一个 非线性激活函数 g(z) 。
在定义了基本术语后,我们现在将其扩展到一个简化的 FCNN,其中一个隐藏层包含 4 个神经元和 1 个输出节点。
简单的顺序架构,带有一个 4 节点隐藏层(图片由作者提供)
Python 中向量和矩阵的实现
在 Python 实现中,计算通常使用矢量化算法进行,如 numpy 和 pandas,这消除了低效的for
循环。
由于这些基础计算,正确设置向量和矩阵的维数至关重要。如果做得不正确,你可能会陷入两种情况中的一种。
第一,如果行和列不匹配,代码会拒绝运行,导致运行错误。第二种更危险的结果是,如果形状是正确的,但方向是错误的,则会导致不正确的输出(例如,如果在 4 元素层中有 4 个输入,则尺寸可能会被交换,从而导致错误的输出)。
在上面提到的 Coursera 模块中,我从吴恩达那里学到的一个教训是,首先要草拟出你的神经网络的架构。拥有一个标准的约定和对基础数学运算的理解有助于你轻松地决定是否需要移调和/或整形。它还帮助您更好地理解输出矩阵如何映射到您的神经网络架构。
让我们从通过 FCNN 发送的单个训练示例开始,上面有单个 4 神经元隐藏层。稍后我们将把它扩展到 m 训练组。
单一训练示例
我个人发现,记住以下几条经验法则会更容易记住结构:
- 使用建筑布局的结构作为输入向量的形状。如果我们看下面的图表,将单个训练示例的形状记为一个 单列 (就像您将如何绘制它一样),在本例中是(3,1)。
- 请记住,权重和偏差仅取决于层中单元的数量。它与训练样本的数量无关。换句话说, m (训练集的数量)不改变权重和偏差矩阵。
- 权重的形状在垂直方向上与该层中神经元的数量相同。在水平方向上,它将基于输入的数量。
使用建筑的形状来模拟你的矢量形状(图片由作者提供)
了解矩阵乘法的排序对于确认形状和排序是否正确很有用。
(a, b) . (b, c) will result in (a, c)
下面的代码片段有助于理解约定,您可以自己尝试。
尝试形状和点操作(来源:作者)
矩阵乘法如下图所示。我们现在包括了偏置向量,它与输出的形状相同,只需要是一列(或者一行,如果你愿意的话)。如果提供 m 训练集,它将被广播到正确的形状。
计算输出 Z(来源:作者)
把它堆在一起
我了解到,一旦你有了正确的基础,就很容易把训练的例子堆在一起。我遵循了吴恩达在他的课程中建议的惯例,用 m 来表示训练样本的数量。
记住,权重和偏差不受训练集数量的影响。它们的结构是由体系结构决定的,这就是为什么我先花时间解释单个集合。
如果您遵循了我的约定,我们现在只需要将输入向量扩展成一个有 m 列的矩阵。我发现训练集的水平方向很容易记住,因为不同的输入被发送到网络中。
因此,我们现在有:
m 训练集的矩阵乘法
如您所见,权重和偏差矩阵和向量保持不变。通过扩展输入向量以覆盖 m 个训练集,我们简单地相应扩展输出矩阵。
结论
我希望这篇介绍性的文章有助于揭开矩阵形状的一些令人沮丧的秘密,也为你提供验证和检查你的结果的基础。
在我将在接下来的几周内撰写的后续文章中,我们将逐步构建简单的 FCNN 来预测 MNIST 人物。
这篇文章是基于我自己的学习,以及我试图阐明我的理解而写的。如果我的理解有错误,请随时纠正我,这样我们可以在这个过程中一起学习。
在 Scala 中使用 Spark 聚合器类
**注意:**如果你已经理解了Aggregator
类,并且只是想看看它是如何在 Scala 中实现的例子,请随意跳到“使用我们的聚合器”部分。
马库斯·温克勒在 Unsplash 上拍摄的照片
类型安全聚合
它们是什么?
你在机场的行李认领处等你的行李。你内心的混乱正在加剧,因为大多数其他乘客现在已经收到了他们的行李,你肯定不想丢失你的衣服、化妆品或你托运的那瓶豪华精酿啤酒。就在你开始绝望的时候,你看到了它:一个大而亮的橙色行李箱,显然是你的。你兴高采烈地把它捡起来,微笑开始掠过你的脸庞——直到你注意到从拉链上滴下来的水滴。然后又是一个。另一个。虽然袋子本身和你托运时一样,但是里面的东西都湿透了。你不禁好奇,这是什么时候的事?是飞机里的低压吗?它是从手推车上掉下来的吗?我应该用气泡包装吗?不幸的是,你真的没有任何办法知道。虽然你应该用气泡包装。
当数据科学家在 Spark 中对数据集执行聚合时,也会有类似的苦恼,只是输出数据集的内容以某种方式显示为无效;也许输入和输出都是完整的数据集,但输出的内容类型有些不正确。聚合的内部机制以及哪里出了问题通常只能在大量的检测工作之后才能得到诊断。即使这样,也不能保证输入数据的未来实例不会给聚合带来另一个问题。
然而,就像添加更多的气泡包装以防止瓶子在您的手提箱中破碎一样,我们可以使用 Spark 的Aggregator
类创建强类型化的自定义聚合。这个类允许数据科学家准确地指定 worker 节点执行什么转换,如何跨节点组合数据,以及每个步骤在预期的输入和输出类型方面应该包含什么。结果是对聚合转换是如何执行的有了深入的了解,并且有了一种自动的方法来诊断可能出现的任何类型问题。换句话说,这是一个万无一失的方法,可以让你下次坐飞机时省下一瓶啤酒。
何时使用类型安全聚合
决定是否对 Spark 数据集执行类型安全聚合可能是一个困难的决定。一方面,很容易就豁出去了,把输入输出类型声明为Dataset
,就到此为止。根据我的经验,这适用于数据科学家可能遇到的许多探索和分析实例,因为数据和类型验证可以在笔记本或脚本环境中动态执行。
另一方面,在有些情况下(例如生产中的自动化 ML 管道,为其他数据科学家创建 API ),您需要确保转换不会在没有警告的情况下产生意外的和潜在的错误结果。这些是您想要在 Spark 中使用聚合器类的情况。该类允许数据科学家在执行某种类型的自定义聚合时识别输入、中间和输出类型。
第一次遇到的时候发现 Spark 的Aggregator
类有些混乱。有几个步骤,看似不用的参数,网上例子有限。我见过的大多数例子都倾向于描述简单的用例,比如对一组整数求和或者计算平均值。虽然这些对于同义反复来说肯定是有用的,但是它们并没有提供对可能需要执行的其他常见聚合的更多了解。因此,我在这里的目的是演示Aggregator
类的两种用法,人们可能会在编写数据预处理应用程序时实际使用它们,并逐行分解它们。
安`T3例
让我们假设您有以下家装商店的零售数据
我们的五金店数据集。
并希望以类型安全的方式执行以下操作:
- 统计每个
retailCategory
中有多少独立客户 - 收集唯一的
productID
数组,按retailCategory
分组 - 将上述数据转换成包含前两步的
Dataset
让我们假设我们想要使用下面的通用结构来实现我们的聚合器:
这将导致数据科学家在使用我们的库时调用以下 API:
val myValue = DoSomething.transformData(…)
请注意,我们正在创建一个对象,其中包含我们希望用来执行聚合的方法,以及我们希望在该方法中使用的两个聚合器。这与我在网上看到的许多其他例子不同,原因有二。首先,我们试图创建一个 API 供数据科学家在数据预处理阶段使用,而不是创建一个工作流来调用某个mainApp
对象。其次,我们正在构建两个以上的由同一个转换使用的聚合器。
我们的待办事项列表上的第一项是导入Aggregator
类、ExpressionEncoder
类,为上面显示的输入数据模式创建一个case class
,并为我们的输出模式创建另一个case class
:
import org.apache.spark.sql.expressions.Aggregator
import org.apache.spark.sql.catalyst.encoders.ExpressionEncoder// input schema
case class RetailByCustomer(customerID: String,
productID: String,
retailCategory: String)// output schema
case class AggRetailByCategory(retailCategory: String,
customerCount:Int,
productSet: Set[String])
注意:我们需要导入ExpressionEncoder
类来为Set[String]
类型定义我们自己的编码器,因为这不是 Scala 中的默认编码器。
现在我们开始写我们的聚合器。我现在将完整地展示这两种方法,然后逐行分解。
我们的两个聚合器。
正如您可能从值名中推断的那样,我们的第一个聚合器提供了不同客户的计数。这是通过将每个客户添加到一个Set
来完成的,因为集合只包含任何给定值的一个实例。在这种情况下使用不同的计数也很重要,以避免在传入数据可能包含重复值或没有映射到任何retailCategory
的productID
值的情况下出现过度计数。现在,让我们看看第一行:
val distinctCustomerCountAggregator:
TypedColumn[RetailByCustomer, Int] = new
Aggregator[RetailByCustomer, Set[String], Int]
这里我们声明一个值,该值将返回一个具有预期输入(RetailByCustomer
—一个表达式)和预期输出(Int
—一个表达式编码器)的 TypedColumn 。我们将这个值设置为一个新的Aggregator
,并定义输入类型(RetailByCustomer
)、中间类型(Set[String]
)和输出类型(Int
)。也就是说,这个聚合器将接受在我们的RetailByCustomer
case 类中定义的某种类型,执行某种类型的转换,这将产生一个Set[String]
类型,并在聚合后返回类型Int
的某个值。
现在来看函数的实质。但首先,这里有一只小狗来打破单调,让你的灵魂稍微清醒一下。
如果您正在阅读本文,您可能知道 Spark 是一个以分布式方式执行数据转换的框架,因此您可能知道有一个主节点向工作节点执行器发送任务来执行数据转换。但是,在对整个数据集执行聚合时,您会向工作节点发送哪些任务来协同工作呢?
我喜欢从高层次考虑这个问题从单个工人节点开始。Aggregator
类将任务发送给单个工作节点(以及该作业的所有其他活动工作节点)上的执行器,说明如何开始聚合:
override def zero: Set[String] = Set[String]()
也就是说,在我们的例子中,每个 worker 节点应该用一个类型为String
的空集开始聚合。
接下来,Aggregator
类告诉每个 worker 节点如何处理它在内存中的数据:
override def reduce(es: Set[String], rbc: RetailByCustomer):
Set[String] = es + rbc.customerID
我们的reduce
定义中的指令告诉每个 worker 节点期待来自上一步的“空集”es
和上面定义的RetailByCustomer
case 类rbc
的输入。此外,每个 worker 节点上的这个转换的输出将会产生一个期望的类型Set[String]
。最后,定义了在每个 worker 节点上发生的实际转换。每个存储为类型RetailByCustomer.customerID
(我们定义为String
)的内存值被添加到现有的空集,从而产生一个包含不同客户 id 的Set[String]
。
为了在实践中看到这一点,假设我们的数据已经由retailCategory
进行了分区,这样关于“照明”和“园艺”的信息存储在一个 worker 节点上,“空调”和“清洁”存储在第二个节点上,而“油漆”存储在第三个节点上。每个节点上的结果集应该如下所示:
既然已经在每个工作者节点上执行了寻找唯一的customerID
的转换,那么Aggregator
类必须指示工作者节点如何相互交互,以便在节点上寻找唯一的customerID
s 。这让我们想到了merge
的定义:
override def merge(wx: Set[String], wy: Set[String]):
Set[String] = wx.union(wy)
这里,我们将两个预期输入定义为类型Set[String]
;一个来自“工人 X”(wx
),一个来自“工人 Y”(wy
)。同样,我们将预期的输出类型定义为Set[String]
。最后,Aggregator
类必须给出每个 worker 节点在与其他节点交互时应该做什么的指令。在这种情况下,我们告诉每个工人将来自reduce
步骤的输出与另一个工人的reduce
输出合并(即返回两个集合之间的所有元素)。
虽然信息在工作节点之间传递的方式有很多细微差别,但是为了理解Aggregator
类,我们可以将merge
步骤的输出可视化如下:
假设如果我们的 workers 节点没有联合结果,它们将返回什么。
这将导致类型Set[String]
的输出“集合(“001”、“005”、“003”、“004”、“002”)”。
下一步finish
,为驾驶员提供执行动作的指令:
override def finish(reduction: Set[String]): Int = reduction.size
这里,我们将期望的输入类型定义为我们的union
ed Set[String]
,期望的输出类型定义为Int
。我们还通过调用size
方法定义了对Set[String]
输入采取的动作,该方法返回一个表示集合中项目数量的整数。在我们的例子中,这会返回5
。
最后一步是为我们的缓冲(或中间)步骤和输出步骤定义Encoder
:
override def bufferEncoder: Encoder[Set[String]] =
implicitly(ExpressionEncoder[Set[String]])override def outputEncoder: Encoder[Int] =
implicitly(Encoders.scalaInt)
distinctCustomerCountAggregator
的输出是一个Int
,它在 Scala 中有一个预定义的编码器。因此,我们可以在定义我们的outputEncoder
时简单地调用Encoders.scalaInt
,并隐式地将它传递给其他定义。然而,尽管Set
和String
在 Scala 中都有预定义的编码器,但是Set[String]
没有。因此,我们必须使用第一步中导入的ExpressionEncoder
类定义我们自己的Set[String]
编码器。
productIDAggregator
实际上与distinctCustomerCountAggregator
非常相似,除了一个关键的区别:我们期望的是Set[String]
类型的输出,而不是Int
。这将改变几个步骤。
练习 : 如果你正在全文阅读这篇文章,我鼓励你在继续下一步之前,思考哪些步骤需要改变以促进这一点(提示:有 3 个)。
第一个区别是将TypedColumn
和预期Aggregator
输出类型都改为Set[String]
:
val productIDAggregator:
TypedColumn[RetailByCustomer, Set[String]] =
new Aggregator[RetailByCustomer, Set[String], Set[String]]
接下来的三个步骤对于两个聚合器来说实际上是相同的:我们指示工作节点创建空集,向这些集合添加productID
s,并联合它们自己得到的集合。
然而,第二个区别出现在finish
定义中。我们希望将预期的输出类型从Int
改为Set[String]
,并删除size
方法,因为我们只想返回实际的一组productID
s:
override def finish(reduction: Set[String]): Set[String] = reduction
最后,我们需要改变outputEncoder
。由于在这种情况下我们没有真正在类型之间转换,我们的bufferEncoder
和outputEncoder
将是相同的:
override def bufferEncoder: Encoder[Set[String]] =
implicitly(ExpressionEncoder[Set[String]])override def outputEncoder: Encoder[Set[String]] =
implicitly(ExpressionEncoder[Set[String]])
就是这样!我们已经创建了两个执行两种不同功能的聚合器!
使用我们的聚合器
为了在实践中使用这些,让我们看一下完整的代码:
编译并发布上述代码后,数据科学家可以在构建数据转换管道时调用以下代码(或者您可以在文件底部调用它):
import com.wherever.code.is.PreprocessDataval retailData = Seq(
("001", "zk67", "Lighting"),
("001", "gg89", "Paint"),
("002", "gg97", "Paint"),
("003", "gd01", "Gardening"),
("003", "af83", "A.C."),
("003", "af84", "A.C."),
("004", "gd77", "Gardening"),
("004", "gd73", "Gardening"),
("005", "cl55", "Cleaning"),
("005", "zk67", "Lighting"),
).toDF("customerID", "productID", "retailCategory")val transformedRetailData = PreprocessData.createArrayAndCount(retailData)
这里显示的val transformedRetailData
的输出应该类似于
结论
- 类型安全聚合允许数据科学家指定如何进行转换,如何在节点内和节点间组合数据,以及每个步骤在预期的输入和输出类型方面应该包含什么。
- 使用 Spark 的
Aggregator
类来执行类型安全转换。典型的用例是在生产级环境中,编写 API,或者计划重复使用聚合。 - 理解 Spark 中发生的事情对于理解
Aggregator
类如何工作是很重要的。
伸手
感谢阅读这篇文章!如有任何意见/问题,请随时联系我们或通过 Twitter 。你也可以访问我的个人网站:【mfjackson.github.com。我一直在学习并寻求提高我在数据科学方面的知识,所以如果我做了任何错误的陈述,或者你知道比本文更好的方法,请告诉我!!
利用拓扑文本分析迎接新冠肺炎公开研究挑战
我对新冠肺炎·卡格尔挑战科学白皮书的分析。这项研究是帮助病毒学、药学和微生物学专家找到问题答案的第一步。
使用 DataRefiner 平台的拓扑图可视化
编者按: 走向数据科学 是一份以数据科学和机器学习研究为主的中型刊物。我们不是健康专家或流行病学家,本文的观点不应被解释为专业建议。想了解更多关于疫情冠状病毒的信息,可以点击 这里 。
3 月 12 日,白宫和一个领先的研究团体联盟准备了新冠肺炎开放研究数据集,以应对冠状病毒疫情。该数据集由超过 44,000 篇学术文章组成,包括超过 29,000 篇关于新冠肺炎、新型冠状病毒和相关冠状病毒的全文。这个免费提供的数据集提供给全球研究界,以应用自然语言处理和其他人工智能技术的最新进展,产生新的见解,支持正在进行的抗击这种传染病的斗争。这个挑战的 Kaggle 页面可以在这里找到:https://www . ka ggle . com/Allen-institute-for-ai/CORD-19-research-challenge
召集所有主题专家
如果您是病毒学、药学或微生物学方面的专家,我们很乐意让您免费访问 DataRefiner 平台上的以下分析,以帮助加快科学研究和应对病毒。请在 ed@datarefiner.com 与我们联系。
该数据集是由艾伦人工智能研究所与陈·扎克伯格倡议,乔治敦大学安全和新兴技术中心,微软研究院和国家医学图书馆-国家卫生研究院合作创建的,并与白宫科技政策办公室合作
挑战
挑战赛为机构群体提出了许多问题:
关于传播、潜伏期和环境稳定性,我们知道些什么?我们对该病毒的自然史、传播和诊断了解多少?关于感染预防和控制,我们学到了什么?
我们对新冠肺炎风险因素了解多少?
我们从流行病学研究中学到了什么?
我们对病毒的遗传、起源和进化了解多少? 我们对人与动物界面的病毒起源和管理措施了解多少?
我们对非药物干预了解多少? 关于非药物干预的公平性和合规性障碍,大家都知道些什么?
我们对疫苗和疗法了解多少?
关于疫苗和疗法的研发和评估工作,已经发表了什么?
我们对诊断和监控了解多少?
数据预处理
我们在分析中使用了文件的摘要和全文。该数据集中的文档通常只有摘要或全文。将它们融合在一起有助于展现全貌。
来自 kaggle 页面的挑战数据质量仪表板
科技论文的摘要通常相当复杂,分析它们的一种方法是将它们分解成句子。在这种情况下,每个句子都是一小段信息,这对于自动分析来说应该足够了。
这是其中一个摘要的例子:
传染因子的基本繁殖数是在一个未被感染的人群中,一个病例在传染期内可以产生的平均传染数。众所周知,由于一些方法问题,包括不同的假设和参数选择、使用的模型、使用的数据集和估计期,对这一数字的估计可能会有所不同。随着新型冠状病毒(2019-nCoV)感染的传播,发现繁殖数量有所变化,反映了冠状病毒爆发的传播动态以及病例报告率。“由于控制策略的显著变化(随着时间的推移一直在变化),以及由于检测技术的引入,这些技术得到了快速改进,能够缩短从感染/症状发作到诊断的时间,从而更快地确认新冠状病毒病例,我们之前对 2019-nCoV 传播风险的估计需要修订。”
为了我们的分析,我们把它分成句子:
- 感染因子的基本繁殖数是在一个未受感染的原始人群中,一个病例在感染期内可能产生的平均感染数
- 众所周知,这一数字的估算可能因若干方法问题而有所不同,包括不同的假设和参数选择、所用模型、所用数据集和估算期
- 随着新型冠状病毒(2019-nCoV)感染的传播,发现繁殖数量发生变化,反映了冠状病毒爆发的传播动态以及病例报告率
- 由于随着时间的推移,控制策略发生了显著变化,并且由于检测技术的引入得到了快速改进,从而能够缩短从感染/症状发作到诊断的时间,从而更快地确认新冠状病毒病例,因此我们之前对 2019-nCoV 传播风险的估计需要修订
正如你所看到的,每一句话实际上足以表达研究人员至少部分回答一个问题所需的一条信息。我们总共有 291281 个句子——这对分析来说足够了。
句子分析
所有的句子都进行了分析,并显示为下面的拓扑图,图上的每个点都是一个或多个句子,相似的句子彼此靠近放置。聚类是自动形成的,代表文本中不同的主题。
数据集中所有句子的拓扑图
在我们训练模型时,除了自动聚类,我们还配置了它,以提取包含“起源”、“稳定性”、“进化”、“风险”和“传播”等关键词的句子。我们使用半监督方法在地图上手动分离这些集群。
让我们来看看“起源”集群,我们可以从中提取什么知识。
“起源”分类的热门关键词
这些热门关键词是从句子中自动提取的,我们在这里显示前 20 个,其余的将用于查找以最佳方式描述集群的热门摘要句子。
为源聚类自动选择的前几个句子
描述聚类“起源”的顶级句子也被自动识别,并且相当准确。
让我们来看看另一个集群——这一次是完全自动形成的——“冠状病毒 sars / sars 冠状病毒/冠状病毒 mers”。
“冠状病毒 sars / sars 冠状病毒/冠状病毒 mers”聚类的热门关键词
用户调查自动发现的集群“冠状病毒 sars / sars 冠状病毒/冠状病毒 mers”的顶级摘要句子
分析与“传输”关键字相关的句子作为单独的地图
除了对一整套句子进行分析之外,该系统还能够处理与特定关键字相关的句子,例如“transmission”。下面是与关键词“传输”相关的 8198 个句子的图:
“传输”关键字的映射
该数据集不仅包括冠状病毒白皮书,还包括大量关于其他近期病毒的论文,如“SARS”(严重急性呼吸综合征相关冠状病毒)、“MARS”(中东呼吸综合征相关冠状病毒)、“TGEV”传染性胃肠炎病毒和其他一些病毒。我们分别审查了不同的组群,以了解关于这种疾病和类似疾病传播的所有关键信息。
“传染病/方法使用/使用数学”群集的描述
“传染性胃肠炎/细胞/病毒”群集的描述
“疾病爆发/控制工作/传染病”组的描述
“估计概率/模型/速率/传输速率”群集的描述
“控制措施/减少/人/ sars”组的描述
“传染病/数字接触”集群的描述
该系统自动识别关键词簇,并在其中分割数据集。聚类越接近,相似的术语和主题就越多。这有助于理解高级分组并在数据中找到元聚类。
结论
文本的拓扑分割有助于研究人员将大量文本信息中的知识系统化。在执行分段和分析每个数据段时,整个数据量被分成簇,这简化了数据的分析和理解。识别出的关键词和总结句子有助于找到最有代表性的信息,并加快寻找答案的速度。
本文的分析是使用 DataRefiner 平台执行的。我们已经在许多公司使用这项技术,不仅用于文本分析,还用于用户活动和物联网细分。如果您想了解更多信息或预订演示,请联系我们。
【https://datarefiner.com/feed/covid-challenge】原文:https://datarefiner.com/feed/covid-challenge
DataRefiner.com是一家总部位于英国的公司,专门从事用户活动、传感器或文本等复杂数据的分析和细分。DataRefiner 平台是本文讨论的多年提炼方法的结果,但它被广泛应用于各种行业,包括航空、社交网络、欺诈检测等。如需了解更多关于您所在行业的信息,请通过 ed@datarefiner.com 联系我们
- 我们对诊断和监控了解多少?
关于系统的、整体的诊断方法(从公共卫生监督的角度到能够预测临床结果)已经发表了什么?
通过 Keras 在 Google Colab 上使用 TPU
罗曼·维涅斯在 Unsplash 上的照片
今天,我在温习一门关于情感分类的旧 NLP 课程,突然感觉到在一些大型数据集上尝试一下的冲动。我玩得很开心,也看到了 Colab 上的免费 TPU 到底有多快。
速度快了 10 倍,但过程并不简单。包括代码变更(大部分是样板文件),因此这篇文章。
作为第一步,我只是想建立一个简单的模型来编译和提供一些预测。我选择了 Kaggle 上的sensition 140 数据集。该数据集有 160 万条带有积极和消极情绪标签的推文。它的大小是 288 MB,这对于我的目的来说是很好的。我为训练模型编写的代码非常简单,因为我正在做非常基本的预处理。
**训练时间——几乎每个时代 1 小时 30 分!**这对于最初的模型来说是行不通的。是时候测试一下 Colab 上提供的免费 TPU 了。
我最初以为这只是一个简单的设置变化。所以我进入编辑菜单中的笔记本设置,要求一个 TPU 硬件加速器。训练仍然需要一个多小时,所以很明显没有提供 TPU。在浏览 TPU 文档时(这里:使用 TPU ),很明显我们必须明确设置什么是计算分布策略并在它下面构建我们的模型。以下文本中的解释,以及相关的样板文件:
- 首先,我们必须明确要求在代码中使用 TPU。Colab 和真实的 GCP 云 TPU 是不同的,所以必须小心。
*import tensorflow as tf
#Get a handle to the attached TPU. On GCP it will be the CloudTPU itself
resolver = tf.distribute.cluster_resolver.TPUClusterResolver(tpu=’grpc://’ + os.environ[‘COLAB_TPU_ADDR’])**#Connect to the TPU handle and initialise it
tf.config.experimental_connect_to_cluster(resolver)
tf.tpu.experimental.initialize_tpu_system(resolver)*
接下来,我们设定分销策略
*strategy = tf.distribute.experimental.TPUStrategy(resolver)*
- 之后,我们创建模型
*with strategy.scope():
model = create_model()#Build your model
model.compile(optimizer=…)#Set your parameters*
- 然后用通常的方式训练它
*model.fit(…)*
这现在起作用了。过去需要 90 分钟的训练,现在只需 9.5 分钟就能完成。毫无疑问非常有效和高效,尽管主题相当神秘。
问题是:它所需要的只是一些样板代码,那么它为什么没有隐藏在一些 Keras 抽象下呢?也许在未来的版本中会有。
使用基于转换器的语言模型进行情感分析
来源: shutterstock
如何轻松击败最先进的情感模型
情感分析对许多企业来说是有用的,有助于评估客户对公司或特定产品的情绪。它的任务是根据文本的极性对文本进行分类,即识别作者对主题的感觉是积极的、消极的还是中性的。
为了能够大规模地自动进行情感分析,我们需要在带注释的数据集上训练模型。本文将展示如何通过使用开源框架 FARM 以快速简单的方式将最先进的 transformer 模型应用于情感分析,从而大幅超越当前的基准测试(提高约 5 个百分点)。
数据集
如前所述,我们需要带注释的数据来监督训练一个模型。为此,我使用了两个共享任务的数据集: SemEval 2017 和 Germeval 2017 。
seme val 数据集由英语推文组成,而 GermEval 数据集包含来自不同社交媒体和网络来源的关于 Deutsche Bahn (德国铁路公司)的德语文本。两个数据集合的文本根据其极性(即正、负或中性)进行标记。
用 FARM 微调变压器模型
当涉及不同的 NLP 任务时,如文本分类、命名实体识别和问答,像 BERT 这样的 Transformer 模型是当前最先进的。有多种不同的预训练模型。对于 Germeval 2017 数据集,使用了 deepset 的 GermanBERT ,因为它在一系列任务中表现出了强大的性能。
然而,对于 SemEval-2017 数据集,由于训练数据由英语文本组成,但结果模型的目的是评估德国客户对文本的情感,因此尝试了零镜头学习方法,因此需要多语言模型。为此,使用了 XLM-罗伯塔-拉奇,它受过 100 种不同语言的训练。如果你想更多地了解 XLM-罗伯塔,我强烈推荐这篇博客文章。
由于预训练模型仅被训练来捕捉对语言的一般理解,而不是特定下游 NLP 任务的细微差别,所以我们需要使这些模型适应我们的特定目的。为了实现这一点,使用了用于调整表示模型(FARM) 的框架。FARM 允许轻松调整变压器模型以适应不同的 NLP 任务。为了实现构建一个可靠的情感分类器的目标,我遵循了 FARM 的 doc_classification 示例。下面的代码片段展示了我如何通过几个步骤使 GermanBERT 适应情感分析的任务:
数据处理
为了能够加载训练实例,数据集需要在 csv 文件中,其中每一行都由训练示例及其标签组成。TextClassificationProcessor
加载并转换数据,以便建模组件可以使用它。为此,我们需要指定可能的标签集、包含训练集和测试集的数据目录,以及包含每个训练实例的标签的列的名称。这里,我们还必须指出我们的模型中使用的最大序列长度。
转换后的数据随后被传递到DataSilo
。其目的是存储数据,并逐批提供给模型。因此,必须在这一步指定批量大小。
建模
下一步是定义模型架构。首先,我们需要决定我们想要微调的语言模型。在这种情况下,我们使用bert-base-german-cased
。然后,我们必须为我们的特定任务选择正确的预测头。因为我们想使用离散值根据文本的情感对文本进行分类,所以我们需要选择一个TextClassificationHead
。最后一步是在语言模型上堆叠预测头,这是由AdaptiveModel
完成的。
培养
既然已经加载了数据并且定义了模型架构,我们就可以开始训练模型了。首先,我们必须初始化一个优化器。在这里,我们设置学习率和我们想要训练模型的时期数。不仅初始化一个优化器,而且初始化一个学习率调度器。默认情况下,所有训练步骤的前 10%是学习率的线性热身。
最后,我们可以将所有组件输入到Trainer
,开始训练并保存生成的模型以备后用。在 Germeval-17 数据集上微调 GermanBERT 在 Tesla V100 16GB GPU 上耗时不到 16 分钟;根据 SemEval-17 的数据调整 XLM-罗伯塔需要 28 分多一点的时间。
结果
对于 Germeval 2017 共享任务,使用微观平均 F1 分数评估提交模型的性能。提供了两个不同的测试集:一个包含与训练数据集相同时期的 tweet(同步测试集),另一个测试集包含稍后时期的 tweet(历时测试集)。
最佳提交(Naderalvojoud et al. 2017)在共时测试集上取得了 74.9%的微观平均 F1 分,在历时测试集上取得了 73.6%的微观平均 F1 分。使用 GermanBERT 和 FARM 训练的模型比这些分数高出 5%以上,在同步测试集上实现了 80.1%的微观平均 F1 分数,在历时测试集上实现了 80.2%的微观平均 F1 分数。
Germeval-2017 数据集的评估结果
然而,对 SemEval 2017 共享任务的提交的性能是通过宏观平均召回来评估的。在那里,最佳提交(placend 2017,Baziotis 等人 2017)实现了 68.1%的宏观平均召回率。同样,使用 XLM-罗伯塔-大型和农场训练的模型比这些提交的模型高出 5%以上,实现了 73.6%的宏观平均召回率。
SemEval-2017 数据集的评估结果
因为使用 XLM-罗伯塔而不是单语模型的原因是将该模型应用于德国数据,所以也在 Germeval-17 测试集上评估了 XLM-罗伯塔情感模型。这里,我们在同步测试集和历时测试集上分别获得了 59.1%和 57.5%的微观平均 F1 值。这一表现比分数分别为 65.6%和 67.2%的基本多数类基线差。出现这种情况的一个原因可能是两个数据集的类别分布差异很大。虽然 Germeval 数据集中的大多数实例被标记为中性,几乎没有包含正面情绪的案例,但 SemEval 数据集中的大多数类别是正面。
另一个问题可能是两个数据集包含主题不同的文本。Germeval 数据集就其主题而言非常有限,主要包含来自不同社交媒体和网络来源的关于德国铁路公司的文本。相反,SemEval 数据集不受主题限制。
Germeval-2017 和 SemEval-2017 数据集的类别分布
所有上述结果都是使用以下超参数获得的:
用于训练模型的超参数
结论
这篇博客文章展示了我们如何使用 transformer 模型来训练我们自己的情感模型。我们看到,在框架农场的帮助下,可以轻松地对预训练模型进行微调,我们甚至能够击败不同共享任务的排行榜。如果你有兴趣亲自尝试一下 GermanBERT 情绪模型,你可以通过 huggingface 的模型中心访问它。
使用无监督学习生成艺术家推荐
Scikit 的最近邻 Python 学习者
在本文中,我将探索使用无监督的机器学习来使用 Spotify 的数据生成艺术家推荐。虽然有许多算法可以用于此目的,但这里考虑的是最近邻学习器,使用 Python 中的 Scikit Learn 实现。
让我们开始吧!
1。数据
我们将使用来自 Spotify Web API 的数据(可以在这里找到)。该数据集包含了从 1921 年到 2020 年的数据,涵盖了大约 27000 多位艺术家!
对于每个艺术家来说,有几个领域可以描述他们的音乐:响度、听觉、舞蹈性、流行度等等。(关于这些意味着什么的细节可以在这里找到)。我们将只使用这些特征(没有其他信息,如艺术家属于哪个流派)来训练我们的模型,然后要求它为类似于我们指定的艺术家生成推荐。
2。算法
虽然许多无监督学习算法可以用于这个问题,但我们将考虑一个最简单的算法——最近邻学习器。该算法本质上扫描 n 维空间(n 是我们给模型的“特征”或特性的数量)来寻找最接近我们指定的点。例如,如果我们只给模型响度和流行度,并要求它推荐 5 位与 Eminem 相似的艺术家,它将浏览所有艺术家并找到响度和流行度值最相似的 5 位艺术家。
形象化的一个有用方法是想象我们生成了一个散点图,响度在 x 轴上,流行度在 y 轴上。每个艺术家的 x-y 坐标可以被认为是他们的“地址”,我们的问题归结为找到“最近的邻居”(即在这个二维空间中离阿姆“距离”最小的艺术家)。这里我们将在 11 维空间中工作,但同样的原理也适用。
幸运的是,我们不需要费心去弄清楚如何编写代码来实际实现这个算法,它已经为我们完成了,我们可以直接使用 Python 中的 SciKit Learn 包。
作者在二维平面图像中寻找“最近的邻居”
3。流程
首先,我们将数据加载到 Python 中。可以从 Kaggle 下载 csv 格式的文件。
二。然后,我们将决定应该将哪些“特性”或特征传递给模型。
三。这些特征中的一些可能具有偏斜分布。例如,大多数歌曲的“器乐性”值很低,这意味着我们有许多艺术家的器乐性得分接近 0,但相比之下,少数艺术家的得分要高得多(见下图)。机器学习算法通常不能很好地处理这种分布,因为异常值会在数据中引入大量“噪声”。为了解决这个问题,我们将使用 SciKit Learn 的 StandardScaler 函数来缩放我们的数据,这将减少这些异常值对我们数据的影响。更多关于定标器的细节可以在这里找到。
工具性显示了一个偏斜的分布:我们需要通过作者来修正这个图像
四。在我们的数据被缩放后,我们准备好训练我们的模型。我们将数据传递给最近的邻居学习者(文档)。虽然我们有机会调整该学员考虑的许多技术参数,但对于这个问题,我们将只让每个参数取默认值。我们将告诉我们的模型的唯一事情是我们想要多少推荐。
动词 (verb 的缩写)就是这样!我们现在将要求我们的模型扫描所有 27,000+位艺术家,并推荐与我们指定的艺术家相似的艺术家。
让我们开始吧:
import pandas as pd
import numpy as npdata_artist = pd.read_csv('./Spotify/data_by_artist.csv') *#This is a datafram with 27621 rows (each row represents one artist) and 15 columns (each column has a unique feature)**## Let's see the 10 most popular artists according to Spotify's algorithm*data_artist.sort_values(by='popularity',ascending=False).head(10)['artists']
Spotify 上最受欢迎的十位艺术家
我们现在将编写一个函数来准备数据、缩放数据、训练模型并生成建议:
features= list(data_artist.columns[0:12]) # We select only the features we want to pass to the model
df = data_artist[features]def getArtist_recommendations(data,artist,numArtists):
X = data.iloc[:,1:] #This contains all our features
Y = data.iloc[:,0] #This contains the name of the artists
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X) #We have now scaled our data to reduce the impact of outliers
from sklearn.neighbors import NearestNeighbors
recommender = NearestNeighbors(n_neighbors=numArtists+1).fit(X_scaled) #Training the model took a single line of code :)
distances, indices = recommender.kneighbors(X_scaled)
output = pd.DataFrame(indices)
output['Artist'] = Y
recommendations = output.loc[output.Artist==artist,output.columns!='Artist']
Y[list(recommendations.values[0])][1:]
return(Y[list(recommendations.values[0])][1:])
就是这样!让我们测试一下模型的表现。我们将要求它推荐 10 位与阿姆、肯德里克·拉马尔和阿肯相似的艺术家。
getArtist_recommendations(df,'Eminem',10)
getArtist_recommendations(df,'Kendrick Lamar',10)
getArtist_recommendations(df,'Akon',10)
推荐与阿姆相似的艺术家-作者图片
推荐与 Kendrick Lamar 相似的艺术家-作者图片
推荐与阿肯相似的艺术家-作者图片
快乐聆听:)
利用加权 K-均值聚类确定配送中心位置
Marcin Jozwiak 在 Unsplash 上拍摄的照片
另一个你可能不知道的 K-means 算法的修改版本的用例
背景
我们所有人都一定熟悉快餐连锁公司,如肯德基或麦当劳,因为它们在我们周围有大量的分店。这些名字以快速提供即食餐而闻名,这要归功于提前散装烹饪,有时还包括预包装。作为其商业模式的一个暗示,快餐连锁店需要快速的供应链执行,特别是对于配料。这是为了确保它们总是新鲜的提供给顾客。
关于这一点,你有没有想过他们如何能及时提供这些易腐的原料?这并不是一个听起来简单的问题,因为涉及的出口数量巨大。在这方面,仓库所描绘的配送中心起着至关重要的作用。
快餐连锁店的仓库是供应商卸下货物的地方,这些货物随后被包装,最后被分发到商店。通常,一个仓库负责周围几个城市的所有经销店。因此,快餐连锁店会有几个仓库分布在他们的经营区域。
在本文中,我们考虑一家全球快餐公司,该公司最近计划进入印度尼西亚市场。管理层预计分两个阶段进行扩张:第一阶段和第二阶段。第一阶段将只覆盖爪哇岛,而其余地区将在第二阶段处理。为此,我们将帮助确定其仓库的位置,特别是在第一阶段
我们将帮助一家快餐店确定遍布爪哇岛的仓库位置。
回忆(标准)K 均值聚类
K 均值聚类是一种将数据划分为 K 个不同聚类的算法。关于算法如何工作的高级视图如下。给定 K 个簇(通常是随机的)的初始(这意味着来自 K 个质心),该算法在以下两个步骤之间迭代:
- 计算所有数据点和现有 K 个质心之间的距离,并相应地将每个数据点重新分配给其最近的质心。
- 基于这个新的聚类分配,通过取数据点的平均值来计算新的 K 个质心。
执行上述迭代,直到质心不随迭代而改变(算法收敛)或者满足特定的停止标准(例如,触发最大迭代次数)
读者可能已经知道,该算法中的数字 K 是一个超参数(即预定义的数字)。在实践中,我们可以通过使用例如肘方法来选择最佳 K,我们将在本文后面使用并解释该方法。
此外,在选择距离度量时也有一定的自由度,而不是在原始算法中使用的标准欧几里德距离。如果有必要或者看起来更合适,我们可以使用其他距离度量,如哈弗森距离(我们将在文章中使用的距离)或曼哈顿距离。
使用加权 K 均值聚类解决问题
让我们回到我们的问题!确定仓库的位置可以看作是寻找相应服务分支的聚类的质心。因此,这是 K-均值聚类,特别是加权 K-均值聚类的一个很好的用例。我们所说的“加权”一会儿就明白了。
我们首先已经知道,这家快餐公司将在爪哇开设分店。因此,我们可以通过首先确定分支机构的位置来解决这个问题,我们可以用 Java 中所有城市/地区的经度-纬度(相当于(x,y))来合理地近似这个位置。使用这些城市中心的数据点,我们实际上可以运行标准的 K-means 聚类来解决这个问题。然而,这种方法没有考虑到这样一个事实,即一些城市确实比其他城市更大,这意味着对供应的原料有更高的需求量。
标准的 K-均值方法没有考虑到这样一个事实,即一些城市确实比其他城市大,这意味着对供应的原料有更高的需求量。
考虑到这一点,我们还包括人口数据,作为配料需求的代理。这些人口数据将成为相应城市(数据点)的权重,瞧!我们已经有了运行加权 K 均值聚类算法的设置。
为了解释权重对算法的影响,假设我们现在正在处理不同大小的数据点(由它们的权重表示)。我们可以进一步看到大小与数据点所拥有的重力成比例。因此,权重越大,数据点将质心拉得越近。总之,与标准算法的根本区别在于质心计算,现在使用加权平均值,而不是标准平均值。
计算质心的标准(左)与加权(右)平均值的图示
数据来源
我们感谢 benangmerah GitHub 帐户的这个存储库提供了所考虑的城市的经纬度坐标数据。原来,爪哇岛上有 119 个城市,从西部的奇勒贡到靠近巴厘岛的左部的班尤万吉。此外,我们还参考了印度尼西亚统计局(BPS)2018 年爪哇省各城市的人口数据。
哈弗森距离
请注意,我们将使用经纬度,它代表地球的球面——与(x,y)中的标准平面 2D 坐标系相反。因此,欧几里德距离不是这里使用的最佳距离度量。相反,我们将使用哈弗线距离,这是球面上一个合适的距离度量。
下面的等式中,φ是纬度,λ是经度,R 是地球半径(平均半径= 6371 公里),这就是我们如何使用哈弗辛方法计算距离(记为 d )。
A = sin((φB—φA)/2)+cosφA * cosφB * sin((λB—λA)/2)
c = 2 * atan2( √a,√( 1a))
⋅ c
下面给出了这种距离度量的另一种实现方式。
#haversine distance functionhaversine_dist = function(point1, point2) { #each argument is a numeric vector with two elements (lon, lat)
lon1 = point1[1]
lat1 = point1[2]
lon2 = point2[1]
lat2 = point2[2]
R = 6371000 #earth radius in meters
phi1 = lat1 * pi / 180 #convert to radian
phi2 = lat2 * pi / 180 #convert to radian
delta_phi = (lat2 - lat1) * pi / 180
delta_lambda = (lon2 - lon1) * pi / 180
a = (sin(delta_phi/2))^2 + cos(phi1) * cos(phi2) * ((sin(delta_lambda/2))^2)
c = 2 * atan2(sqrt(a), sqrt(1-a))
distance = R * c #haversine distance between point1 and point 2 in meters
return(round(distance, 2))
}
逐步解决方案和 R 中的代码
好了,让我们动手做代码吧!在本文中,我们将使用 R 来运行算法。
- 数据准备
我们导入一个包含所需列的数据帧:城市名称、经度、纬度和人口。
df_city = read.csv('/Users/parara/Documents/Project/Weighted K-Means/city_data.csv')head(df_city)
负责人(df_city)
- 初始化
我们固定集群的数量 K,比如说 5 个。接下来,我们随机选择五个城市作为初始质心。之后,使用这些质心,我们将每个城市分配到其最近的质心(初始聚类)。回想一下,我们使用哈弗线距离作为距离度量。
#number of clusters
K = 5#initial centroids by random
init_centroids_index = sample(nrow(df_city),K)#initiate containers
distance_matrix = matrix(data = NA, nrow = nrow(df_city), ncol = K)
cluster = vector()
centroid_long = vector()
centroid_lat = vector()#compute distance between cities and initial centroids
for (k in c(1:K)) {
for (i in c(1:nrow(df_city))) {
city_i = as.numeric(df_city[i,2:3])
centroid_k = as.numeric(df_city[init_centroids_index[k],2:3])
distance_matrix[i,k] = haversine_dist(city_i,centroid_k)
}
}#initial cluster assignment for each city
for (i in c(1:nrow(df_city))) {
cluster[i] = which.min(distance_matrix[i,])
}
- 循环
有了初始集群,我们就可以开始循环了。我们迭代地更新质心,并将城市重新分配给聚类,直到聚类分配在迭代之间保持不变。
#iteration baseline
old_cluster = vector(length = length(cluster))
new_cluster = cluster#iterations
while (!all(old_cluster == new_cluster)) {
#update old cluster assignment
old_cluster = new_cluster
#calculate centroids using weighted average
for (k in c(1:K)) {
cluster_k = which(old_cluster == k) #city index of cluster k
centroid_long[k] = weighted.mean(df_city$longitude[cluster_k], df_city$population[cluster_k])
centroid_lat[k] = weighted.mean(df_city$latitude[cluster_k], df_city$population[cluster_k])
}
df_centroid = as.data.frame(cbind(centroid_long, centroid_lat))
#compute distance between cities and centroids
for (k in c(1:K)) {
for (i in c(1:nrow(df_city))) {
city_i = as.numeric(df_city[i,2:3])
centroid_k = as.numeric(df_centroid[k,])
distance_matrix[i,k] = haversine_dist(city_i,centroid_k)
}
}
#update cluster assignment for each city
for (i in c(1:nrow(df_city))) {
cluster[i] = which.min(distance_matrix[i,])
}
#update new_cluster
new_cluster = cluster
}
- 选择最佳 K
事实是,上面选择的 K = 5 的值是追溯性的。我们事先根据下面的肘法挑选了号码。但是,理解这种技术需要我们首先理解什么是所谓的平方和误差(SSE)。在 K-means 算法中,SSE 中的术语“误差”是指数据点到其质心之间的偏差。因此,我们通过对所有数据点的所有此类平方误差求和来获得 SSE 的值。
我们注意到,任何集群分配都对应于一个 SSE 值。因此,我们可以绘制不同 K(聚类数)下的收敛算法的 SSE 值。使用该图,我们查看 K,在 K 处,值之后的下降斜率不再显著。这是我们正在寻找的最佳 K。通常,这个 K 值会在整个图上形成一个“肘形”,因此得名。
对于我们来说,情节如下。请注意,在 K = 5 之后,上证指数的下降不再显著,因此我们选择这个数字作为最佳 K。
上证指数在不同 k 线下跌
- 结果
最后,算法的结果如下。
加权 K 均值结果
基于上述结果,我们对五个仓库位置的建议如下:
- 西爪哇省德博克附近
- 西爪哇 Majalengka 附近
- 靠近爪哇中部的 Temanggung
- 东爪哇马迪恩附近
- 东爪哇 Probolinggo 附近
结论和备注
在本文中,我们演示了加权 K 均值聚类算法在确定快餐店仓库位置中的应用。总之,K-means 的这个修改版本与原始版本的不同之处在于它计算聚类质心的方式,它使用加权平均值而不是常规平均值。
关于我们的实现还有最后一句话(代码在这篇文章中给出)。 K-means 算法只产生局部最优,而不是全局最优。也就是说,获得的最终簇分配取决于初始分配,这是随机的。因此, 在实际应用中,我们需要多次复制整个算法,并从产生最小 SSE 的最佳复制中挑选结果。
对于感兴趣的读者,我把这个“真实实现”的代码放在我的 GitHub 库这里。在那里,您还可以找到 SSE 函数的代码,生成本文中显示的图,当然,还有本文中使用的数据集。
总之,感谢您的阅读,让我们在 https://www.linkedin.com/in/pararawendy-indarjo-5b87bab7的 LinkedIn 上与我联系。
使用词语嵌入进行新闻研究
为了分析政治演讲中词语的语境,我不得不跨界到计算语言学。
一个词向量从 1949 年到 2019 年随时间变化的可视化。
像大多数其他领域一样,新闻业正在走向数字化。我是德国《南德意志报》数据和数字调查团队的一员,在那里,我们试图利用现代技术手段开展新闻研究。
在我最新的项目中,我结合了政策分析和机器学习。我想知道:随着时间的推移,政治语言和某些词汇的使用发生了怎样的变化?
自 1949 年 5 月德意志联邦共和国成立以来,70 年已经过去了。在这段时间里,被称为 联邦议院 的联邦议会 4200 多次会议的每一次都被一丝不苟地记录下来:每一次演讲、每一次评论、每一次鼓掌都被记录下来。一个保存德国近代史、深远发展和重大政治冲突的数据宝库。
当然,计算机理解语言的方式与人类不同。相反,计算语言学的数学系统从上下文中揭示单词的含义。语言学家约翰·鲁帕特·弗斯在 1957 年说过一句著名的话:“你应该从一个单词所交的朋友那里知道这个单词”,这远在计算机被用来寻找内容的上下文之前。然而,这个原则在 21 世纪仍然适用,那时算法可以接管这项任务。
我选择的自动评估文本的方法叫做单词嵌入。我们无时无刻不在与这项技术互动,例如在进行简单的谷歌搜索或使用翻译软件时。我将计算语言学科学领域的这些方法应用于政治修辞学。
在我读到的每一篇关于单词嵌入的论文中,都有这样的短语:“有前途的新领域”或“大量研究”。是的,两者都被证明是真的…
算法输入:输入数据
像世界上其他国家一样,德国议会记录了议员们的一言一行。得到的文档是我分析的语料库。
我对随着时间的推移而发生的变化感兴趣,所以我不会在这些年里创建一个大型语料库,但是每个立法术语都有自己的语料库。所以总共有 19 个不同的语料库。
从表中可以看出,单个语料库并不是很大。这导致了如何将单词嵌入方法应用于 19 个相当小的议会记录语料库的第一个重大决定。
大多数研究论文引用了更多数据的项目。这是发人深省的,因为这些方法不能简单地应用于我的情况。但是我在 Antoniak et 里发现了一张桌子。艾尔。(2018) 那真让我振奋。它区分了两种不同的方法:以下游为中心的方法,一种是针对大型语料库的通用方法,另一种是针对特定领域(如政治)的以语料库为中心的方法。
以下游为中心和以语料库为中心的词汇嵌入方法比较。截图:安东尼亚克等人。艾尔。(2018)
这种区别决定了分析的方法,正如安东尼亚克和明诺所写的:
“与以下游为中心的方法不同,以语料库为中心的方法基于对嵌入向量的最近邻居的直接人类分析,训练语料库不仅仅是现成的便利,而是研究的中心对象。”
人类和计算机之间的翻译:单词嵌入
计算语言学使用的一种从上下文中提取单词含义的方法叫做单词嵌入。语料库中的每个词通过上下文和语义嵌入到模型中。关于德国议会的记录,这意味着:当计算单词嵌入的模型时,议会成员的语言的上下文和语义被嵌入到模型中。
每一个独特的单词都由一系列数字(也称为维度)来表示,这些数字是在学习上下文的过程中计数和权衡的结果。这个过程叫做“训练模型”。
结果看起来像这样:
环境*(um welt)(0.08567299 0.11279329 0.2180736-0.3106728…)* 自然保护*(Naturschutz)(-0.012405696 0.07818038-0.045643393-0.0051204716…)* 气候灾害
学习机的能力隐藏在这些乍看起来不起眼的单词和数字序列中。
这组向量携带以下信息:
- 每个向量描述了单词的上下文
- 相对于所有其他向量,每个向量对单词的含义进行编码
理论上,维度越多,一个向量可以包含的意义就越多。但实际上,它受到你输入算法的文本量的限制。寻找最佳长度可能是一个反复试验的评估过程。在这里,我使用 300 个维度。这是许多研究人员使用的默认设置,我坚持他们的经验。
单词嵌入的用户可以自由决定应该计算多少维度。这样的设置选项在机器学习中被称为超参数。它们的调整会严重影响结果,并且用户在实现机器学习模型时会反复面临做出这样的决定。
请记住,嵌入模型中的含义只适用于特定的领域。在这种情况下:在过去的 70 年里,德国议会的演讲。
因此,一种算法将文本语料库作为输入,并返回计算出的单词向量。但是这是怎么发生的呢?
单词如何变成数字:word2vec 算法
有几种可能的算法可以解决计算单词嵌入的任务。对于这个项目,我选择了有据可查的 word2vec 算法。它于 2013 年在谷歌推出(米科洛夫等人,2013 年 a )。许多科学论文测试了单词嵌入算法,并得出结论:word2vec 是最佳选择。我使用了 Python 的实现 gensim ( Rehurek 和 Sojka,2010 )。
首先,构建词汇表:从输入文本中提取并存储每个唯一的单词。Word2vec 通过检查哪些单词在特定上下文中一起出现来从文本中学习。上下文被定义为在被检查的单词周围的窗口。窗口的大小是要设置的另一个超参数。
我们来看这句话:
截图:jaye sh Bapu Ahire 的《词向量入门》
现在我们要用五个单词的窗口大小来训练狗的向量:用黄色标记的单词是从词汇表中挑选出来的,并在窗口内的每一对可能的单词上进行训练: The 、 fluffy 、汪汪汪、 as 。紧邻的单词被赋予更高的权重。例如,蓬松的 + 剥皮的被赋予比更大的权重,把 + 称为。
接下来,我们再深入一个词,看看吠。窗口向前移动一个单词,并且相邻单词被加权。
通过这种方式,该窗口迭代了自 1949 年以来的所有议会演讲。
所有出现的单词对都是一维神经网络训练的输入。Word2vec 旨在计算一个模型,该模型可以预测一个单词与另一个单词出现的可能性。为了解决这个任务,有一个隐藏层,其神经元的数量与向量的维数一样多。
“单词 vector 是模型试图学习单词的良好数字表示,以最小化其预测的损失(误差),Jayesh Bapu Ahire写道。
最后,当训练过程结束时,输出层被剥离。单词向量是经过训练的神经元的结果。在机器学习领域,这被称为假任务:你可以在一个任务上训练一个模型,你有足够的数据,然后扔掉最后一层。
word 2 vec 的内部运作方式。截图:Jayesh Bapu Ahire对词向量的介绍
如果你想更深入地研究词向量和神经网络,我推荐这些博客帖子(按书呆子气升序排列):
- Jayesh Bapu Ahire 对单词向量的非常好的介绍。你会认出上面的图形
- 在 Word2Vec 教程—跳格模型中有更多关于神经元网络和跳格实现的细节
- 如果你还需要更多关于神经元网络内部发生了什么的信息: Word2vec 使之变得容易
用语言计算:文字作为数字的魔力
在训练过程中,该模型不仅识别单词的上下文,还从概念上学习语法类别,如数、格或性别。这些关系被编码在模型的维度中。
一个概念性的例子:想象向量“女总理”( Bundeskanzlerin )、“男总理”( Bundeskanzler )、“女人”( Frau )和“男人”( Mann )有四个维度和虚构的数字。
考虑从左边开始的第二维:向量“女总理”( Bundeskanzlerin )和“女人”( Frau )具有高值,而“男总理”( Bundeskanzler )和“男人”( Mann )具有低值。这个维度可能被贴上女性化的标签。从左边算起的第三维度则是相反的方向,暗示着男子气概。
德国联邦议院模型中有 300 个维度,而不是 4 个维度——当然,没有标签。不仅仅是四个词,仅最近的选举任期就有超过 61,000 个词。这导致了 1830 万个数据点——对我们的大脑来说太复杂了,难以想象。尽管这是不可想象的,但是用向量来计算是可能的,就像回到高中一样。
描述 Word2Vec 的这一特性时,一个通俗的例子是:“国王—男人+女人=女王”。应用来自德国议会的数据,数学运算可以用“女总理——女人+男人=男总理”来进行。
一个很好的计算不同语料库的应用是北欧语言处理实验室的语义计算器。
用向量计算很有趣。但是探索广阔的向量空间可能会超出我们读者的耐心。此外,它无法回答主要问题:政治辩论正在发生怎样的变化?
解决不稳定的单词嵌入:不是一个,而是 30 个模型
发现政治演讲转变的主要想法是比较一届立法任期与前一届有多大不同。因此,下面的测量结果是有用的:余弦相似性。它计算两个向量之间角度的余弦值。结果是一个介于 0 和 1 之间的数字,其中 0 表示没有相似性,1 表示绝对相似。余弦相似度越接近 1,一个词就越能被解释为同义词。
自 71 年前德意志联邦共和国成立以来,共有 19 届选举。对于每个术语,我计算了一个模型,然后查询模型,以提取与 180 个特别感兴趣的词最相似的词,例如:“气候变化”( Klimawandel ),“交通”( Verkehr ),“难民”( Flüchtlinge )或“种族主义”(rassi mus)。
我们用于研究 word 嵌入模型的内部仪表盘截图。
我们在《南德意志报》的团队定义了一组 180 个高频相关术语,以便进行更深入的研究。我们没有事先选择这个查询词列表。花了一些时间在档案和数据中探索和研究,以识别有故事可讲的单词。
例如:下表显示了每个立法术语中与德语 Umwelt 中的“环境”一词最相似的词:
很明显,在最初的 20 年里,这个最相似的词每一个词都在变化,而且有着与今天不同的含义。那时候,“环境”( Umwelt )经常被用来描述周围的世界。这种情况在 20 世纪 80 年代发生了变化,当时绿党被选入议会,其对环境问题的关注极大地影响了“环境”一词的使用。同义词首先变成了“生计”(lebengrundlage),然后进一步变成了“自然保护”( Naturschutz )。近来最相似的词是“生物多样性”。
这真的很强大:单词嵌入可以重现政治辩论。但是它们有一个巨大的缺点:它们不稳定,特别是对于像我这样的相对小的语料库[1,2]。每次您使用相同的数据和相同的设置编写新模型时,针对特定术语嵌入的查询结果都会因模型而异。安东尼亚克等人。艾尔。[1]在他们的论文中研究了这一现象:
“我们发现嵌入中存在相当大的可变性,这些可变性对于这些方法的用户来说可能并不明显。大多数相似单词的排名是不可靠的,并且这种列表中的排序和成员资格容易发生显著变化。一些不确定性是预料之中的,对于‘可接受的’方差水平没有明确的标准,但我们认为我们观察到的变化量足以对整个方法提出质疑。”
下表列出了从 1983 年到 1987 年与“环境”最相似的 15 个单词。这六列代表不同的型号。这些值是“环境”和行中相应单词之间的余弦相似度。空单元格意味着该模型没有将该单词识别为十大相似单词之一。
因为单词嵌入模型是不稳定的,所以它们不会在不同的模型上提供相同的结果。例如,第二个模型不承认德语leben straum中的“生活空间”是“环境”的同义词,但其他五个模型承认。
当模型产生不同的结果时,我应该相信哪一个?没有。单一模型如此不稳定的结果是新闻研究不可接受的基础。我通过训练 30 个模型来解决这个问题,而不是每个选举任期只训练一个。总共有 570 个模型用于 19 个选举任期。我测试了一下:我删除了所有的模型,重新计算。我的查询对新模型产生了相同的结果。所以分析是可重复的。
当查找政治相关术语的相似词时,我从一个选举术语的所有 30 个模型中提取了 10 个最相似的词。下一步是计数过程:一个词在每个术语中作为同义词出现的频率是多少?
这种查询多个模型并对出现次数进行计数的过程确保了我们只检查那些在模型中反复出现并且可以被视为稳定结果的单词和伴随的嵌入。
为了可视化结果,我们使用热图。下表显示了“环境”最常见的同义词。
左边的十二个字是按照 1949 年以来的整体面貌排序的。在这里,我们也绘制低于 30 的值,因为它们表示“环境”话语中某个单词的上升或下降。
热图是一个很好的工具,可以检查整个时间跨度并查看随时间的变化。但它们不足以详细评估一个选举任期。为此,我们使用了由 30 个模型的平均余弦相似性排序的相似单词列表,并用更强的字体权重标记稳定的单词。
我在 Github 上发表了 2017–2019 最新选举任期的 word embedding models。在德国政治领域,你可以随意使用它来完成你自己的项目。如果你这样做,请写信给我,我对你的工作很感兴趣。
意犹未尽:对历时词汇语义变化的探索
到目前为止,我们已经研究了单词的上下文是如何变化的。另一种方法是关注单词本身,看单词向量的变化。这就是所谓的历时词汇语义变化。
金等人。艾尔。[3]提供了这样做的方法。他们与谷歌图书 ngram 语料库合作,计算了从 1900 年到 2009 年每年的一个模型。有两个词特别值得注意:“细胞”和“同性恋”。在这 100 年里,它们的意义发生了很大变化。" gay “的邻近词从"欢快的”、“愉快的”、“辉煌的"变成了"女同性恋”、“双性恋"和"女同性恋”,而" cell “从"壁橱”、“地牢”、“帐篷"变成了"电话”、“无绳”、“蜂窝”。
为德国政治争取这些词汇不是很好吗?
不幸的是,这篇论文经常被引用来批评其寻找这些单词的方法,例如,因为他们正在处理不稳定的单词嵌入。
幸运的是,我联系到了张秀坤·施勒特韦格,他是一名博士生,与萨宾·舒尔特教授一起在瓦尔代研究自然语言处理研究所(斯图加特大学)语义变化的分布式模型。他耐心地建议我将该方法应用于我的数据。
Schlechtweg 等人。艾尔。 [4]测试了几种测量这些变化的方法:“总体表现最好的模型是具有正交排列和余弦距离的 Skip-Gram。”
该方法可以应用如下:
- 来自一个时间段的每个模型必须与下一个时间段的模型对齐:时间段 1 到时间段 2,时间段 2 到时间段 3 等等。结果是更多的模型:每次新的选举期开始,我都会得到一对新的模型。
- 对于每个单词,计算从模型到模型的余弦距离。
- 棘手的部分:为了消除频率[5]的统计影响,应该额外提取在相应术语上具有相同频率变化的可比单词。
等等,这里有个例子:
视觉推理的首次尝试:
你看到的蓝线是向量“ Umwelt ”(环境)在 15 个模型中的平均变化。浅蓝色的一个点表示来自一个模型的单个余弦距离值(余弦距离= 1-余弦相似性)。
蓝线和灰线的对比很重要,灰线显示了“Umwelt”的频率。如你所见:如果灰线改变,蓝线也会改变,因为余弦相似度直接取决于频率。可用单词越多,相似度越低。
有了这个背景你就明白了:没什么可看的。单词向量(蓝线)的变化是由于频率变化(灰线)。为了消除这种统计效应,你必须找到频率变化相同的单词,并绘制出它们之间的距离。
下一次尝试以引用词结束,如下图所示:
它看起来更好,因为有一个项,第 10 项,蓝色点位于浅蓝色点之上。这表明不管频率如何,词向量都有真实的变化。
蓝点代表向量“Umwelt”(这 15 个模型)的余弦距离。浅蓝色的点现在表示与在模型对中的最近术语中与我感兴趣的单词共享相同频率的可比单词的余弦距离。参考字从模型对变化到最佳地表示该时间点的频率。
你注意到这个缺陷了吗?
当我比较和计算来自两个不同选举术语的模型对的相似性时,我还需要比较来自两个选举术语的参考词的频率变化。词向量在一段时间内的距离,并不是与另一段时间内的课程进行比较,而是一个特定的时间点。这就是缺陷。
我试图通过搜索那些与“Umwelt”有相同频率变化的单词来修复它。但是没有相同的频率变化,即使我让频率有小幅度的波动。语料库实在太小了。
那么,让我们进入下一个迭代。而不是绝对变化,也许相对频率变化可以有所帮助?
看上面的图表:当频率折线图改变时,余弦距离折线图也随之改变。
如果选择相对频率变化相同但绝对频率更高的参考词,余弦相似性可能会更小,因为频率和余弦相似性之间存在反比关系。
因此,根据感兴趣的单词,你会仅仅因为基于频率差异的统计效应,而对单词向量的视觉变化进行推理。
这个问题有一个理论上的解决方案:寻找绝对数变化相同但频率更高的词,然后回到最初的语料库。现在的关键是通过随机屏蔽对参考单词进行采样,直到频率与感兴趣的单词相同。然后计算新模型,将它们对齐,并查询图表的相似度。那是我停下来的地方。
当我们讨论这个未解决的任务时,张秀坤写道:“我还想说,自动意义变化检测领域还没有准备好为你的应用问题提供标准方法。两年后,情况可能会有所不同。”
寻找故事:新闻的结果
基于这项研究发表了三个故事。
- 德国联邦议院如何搞糟气候变化:自 20 世纪 80 年代海尔马特·斯克米特总理时代起,联邦议院就一直在讨论气候灾难。但是一项数据研究表明:一代又一代的议员几乎不屑于对他们采取行动。
- 匆忙的议会:所谓的难民危机是一个转折点,德国变了。一项数据研究显示:关于难民和移民的话语已经向右转移——同样是由 AfD(德语中的)提出的。
- 《在语言的引擎室》:一个关于方法论的大故事。目标群体是《南德意志报》的普通受众。其野心是解释机器学习的话题,而不是求助于一个“这是一个黑箱”(德语中的)。
交流不确定性:技术沙文主义的危险
我对计算机如何捕捉自然语言很着迷。但是这种迷恋有技术沙文主义的危险。技术沙文主义是梅雷迪思·布鲁萨德提出的一个术语——“相信技术总是解决问题的方法”。
我们如此热衷于给机器分配我们的大脑无法解决的任务。所以我们很容易忽略这样一个事实,结果也必须被质疑。我们陷入了计算机总是客观地产生完美的结果的想法。这就是为什么我认为沟通像这样的项目的不确定性是很重要的。如果我改变算法的超参数,数据中的其他关系可能会被更强烈地强调,潜在的文章可能会产生不同的结果。这就像就一个具体问题询问多位证人。我如何表达我的问题也会影响我得到的答案。
你有答案或意见吗?我感谢任何反馈。
特别感谢卡塔琳娜·布鲁纳和卡门·海格的评论。
DIY:Github 上的 30 个单词嵌入模型
我在 Github 上发布了当前选举任期(2017–2019)的 30 个单词嵌入模型。随意使用它们并引用我。
阅读更多:相关的科学工作
[1]安东尼亚克,玛丽亚;米姆诺,大卫。基于嵌入的词语相似度的稳定性评估。《计算语言学协会汇刊》,第 107–119 页,2018 年 2 月。
[2]约翰内斯·赫尔里奇和乌多·哈恩。2016.坏公司——神经嵌入空间中的邻居被认为有害。《2016 年计算语言学国际会议论文集》,2785-2796 页,日本大阪。
[3] Yoon Kim、Yi-I Chiu、健太郎·哈纳基、达尔山·黑德和斯拉夫·彼得罗夫。2014.通过神经语言模型对语言进行时间分析。ACL 语言技术和计算社会科学研讨会论文集,第 61-65 页。
[4]施莱希特韦格,张秀坤等,“变化之风:检测和评估跨时间和领域的词汇语义变化。”计算语言学协会第 57 届年会论文集(2019)
[5] Haim Dubossarsky、Daphna Weinshall 和 Eitan Grossman。2017.失去控制:语义变化的规律和单词表征模型中的固有偏差。《2017 年自然语言处理经验方法会议论文集》,1147-1156 页,丹麦哥本哈根。
使用单词向量以数学方式找到意思相似的单词
入门
学习使用词向量以编程方式计算文本的语义相似度
卡尼·查拉科娃摄
简而言之,单词向量只不过是代表自然语言单词含义的一系列实数。这项技术是有用的 NLP 能力的重要使能器,使机器能够“理解”人类语言。本文讨论了如何使用单词向量以编程方式计算文本的语义相似性,例如,如果您需要根据文本所涵盖的主题对其进行分类,这将非常有用。它从一个概念视图和示例开始,然后说明如何使用 spaCy(一个领先的用于 NLP 的 Python 库)来确定文本的语义相似性。
词向量的概念
所以,让我们先从概念上看一下单词向量,这样你就可以基本理解如何用数学方法计算以向量形式表示的单词之间的语义相似度。然后,您将查看 spaCy 的 similarity()方法,该方法比较容器对象(Doc、Span、Token)的单词向量,以确定它们含义的接近程度。
在统计建模中,您将单词映射到反映单词语义相似性的实数向量。你可以把一个单词向量空间想象成一朵云,其中有相似意思的单词向量位于附近。例如,表示单词“apple”的向量应该更接近单词“pear”的向量,而不是单词“car”的向量。因为前两个指的是可食用的水果,而后者指的是四轮公路车辆。为了生成这些向量,你需要对这些单词的意思进行编码。事实上,有几种方法可以对意义进行编码。
用坐标定义意义
生成有意义的单词向量的一种方式是将现实世界中的对象或类别分配给单词向量的每个坐标。例如,假设您正在为以下单词生成单词向量:罗马、意大利、雅典和希腊。向量这个词应该在数学上反映出罗马是意大利的首都,并且与意大利有着雅典所没有的联系。同时,它们应该反映出雅典和罗马是首都城市,而希腊和意大利是国家。
下图以矩阵的形式展示了这个向量空间的样子。
在这里,你在一个四维空间的坐标之间分配每个单词的含义,代表“国家”、“首都”、“希腊”和“意大利”等类别。在这个简化的例子中,坐标值可以是 1 或 0,指示相应的单词是否属于该类别。
一旦你有了一个向量空间,其中的数字向量捕获了相应单词的含义,你就可以在这个向量空间上使用向量算术来洞察一个单词的含义。要找出雅典是哪个国家的首都,您可以使用以下等式,其中每个令牌代表其对应的向量,X 是未知向量:
意大利—罗马= X —雅典
这个等式表达了一个类比,其中 X 代表单词 vector,它与雅典的关系就像意大利与罗马的关系一样。为了解出 X,你可以把等式改写成这样:
X =意大利—罗马+雅典
首先,通过减去相应的向量元素,从向量意大利中减去向量罗马。然后你把结果向量和雅典向量相加。下图总结了这一计算。
通过从代表意大利的单词 vector 中减去代表罗马的单词 vector,然后加上代表雅典的单词 vector,可以得到一个等于希腊的矢量。
用维度来表达意义
虽然您刚刚创建的向量空间只有四个类别,但是现实世界的向量空间可能需要成千上万个这样的类别。这种大小的向量空间对于大多数应用来说是不切实际的,因为它需要一个巨大的字嵌入矩阵。例如,如果您有 10,000 个类别和 1,000,000 个实体要编码,您将需要一个 10,000 × 1,000,000 的嵌入矩阵。
减少嵌入矩阵大小的明显方法是减少向量空间中类别的数量。单词向量空间的实际实现不是使用坐标来表示所有类别,而是使用向量之间的距离来量化和分类语义相似性。各个维度通常没有固有的含义。相反,它们表示向量空间中的位置,向量之间的距离表示相应单词含义的相似性。要看一个真实向量空间的例子,你可以在https://fasttext.cc/docs/en/english-vectors.html下载 fastText 单词向量库,它将单词的意思分布在一个 300 维的单词向量空间中。
空间相似性()方法
在 spaCy 中,每种类型的容器对象都有一个相似性方法,允许您通过比较两个任意类型的容器对象的词向量来计算它们之间的语义相似性估计。为了计算跨度和文档的相似性(它们没有自己的词向量),spaCy 对它们包含的标记的词向量进行平均。
即使两个容器对象不同,也可以计算这两个对象的语义相似度。例如,您可以将令牌对象与 Span 对象进行比较,将 Span 对象与 Doc 对象进行比较,等等。
以下示例计算 Span 对象与 Doc 对象的相似程度:
doc=nlp(‘我想要一个青苹果。’)
这段代码计算句子“我想要一个青苹果”之间的语义相似度。短语“一个青苹果”也来源于这个句子。如您所见,计算出的相似度足够高,可以认为两个对象的内容是相似的(相似度的范围从 0 到 1)。毫不奇怪,当您将一个对象与其自身进行比较时,similarity()方法返回 1:
doc . similarity(doc)
1.0
doc[2:5]。相似性(doc[2:5])
1.0
注:文中使用的例子摘自我最近由 No Starch 出版社(https://nostarch.com/)出版的《用 Python 和 spaCy(https://nostarch.com/NLPPython)进行自然语言处理》一书。
使用 YOLOv3 实时检测个人防护设备和火灾
工业安全中的人工智能:这篇文章解释了我们如何使用 YOLOv3:一种用于实时检测个人防护设备(PPE)和火灾的对象检测算法
“安全不是一个小玩意,而是一种心态!”
世界摩天大楼的不断攀升是一个不断演变的挑战的故事。我们每天生活在摩天大楼、购物中心和仓库周围。大多数时候,我们对建造这些建筑需要什么一无所知。建筑业负责创造如此宏伟的杰作。截至 2020 年 3 月,建筑业拥有 1.3 万亿美元的年支出(约占 GDP 的 6.3%)[1]和 760 万名员工(约占总劳动力的 5%)[2],是美国经济中最大的行业之一。然而,由于建筑工地的高死亡率,它也被认为是最危险的行业之一。
与建筑相关的伤亡的主要原因是跌倒、人员被设备卡住、触电和碰撞。如果工人穿戴适当的个人防护装备,如安全帽、安全背心、手套、护目镜和钢头靴,大多数伤害都是可以避免的。为了确保工作场所的安全,美国职业安全与健康管理局(OSHA)要求承包商强制执行并监控个人防护设备(PPE)的正确使用。
目前,PPE 检测技术可以分为两种类型:基于传感器的和基于视觉的。基于传感器的方法使用 RFID(射频识别 RFID)标签,需要在购买、安装和维护复杂的传感器网络方面进行大量投资。相反,基于视觉的方法使用摄像机记录施工现场的图像或视频,然后对其进行分析以验证 PPE 合规性。这种方法提供了关于场景的更丰富的信息,可用于更迅速、精确和全面地理解复杂的建筑工地。
本文解释了如何使用基于 YOLOv3(你只看一次)架构的深度学习模型来满足各种安全要求,如建筑工地工人的 PPE 合规性和使用最少硬件(即监控摄像头)的火灾探测。深度学习算法通过单个卷积神经网络(CNN)框架同时检测单个工人并验证 PPE 合规性。在当今世界,卷积神经网络(CNN)最广泛地用于图像分类和对象检测,因为它能够从大量带注释的训练数据中进行自我学习。
各种实时物体检测技术
计算机视觉中的任何对象检测问题都可以定义为识别图像中的对象(也称为分类),然后精确估计其在图像中的位置(也称为定位)。如今,物体检测几乎在任何地方都被使用。用例是无穷无尽的,无论是跟踪对象、视频监控、行人检测、异常检测、自动驾驶汽车还是人脸检测。
目前在工业中使用的主要对象检测算法有三种:
R-CNN:基于区域的卷积网络
R-CNN 首先识别几个感兴趣的区域,然后使用 CNN 对这些区域进行分类,以检测其中的对象。由于原始的 R-CNN 很慢,所以已经提出了它的更快的变体,例如快速 R-CNN、更快 R-CNN 和屏蔽 R-CNN。在 R-CNN 中,图像首先被分成大约 2000 个区域推荐(区域比例),然后 CNN (ConvNet)被分别应用于每个区域。
确定区域的大小,并将正确的区域插入到人工神经网络中。这种方法最大的问题是时间。由于图中的每个区域分别应用 CNN,训练时间约为 84 小时,预测时间约为 47 秒。
YOLO:你只看一次
大多数目标检测算法使用区域来定位图像中的目标。网络不会看到完整的图像。取而代之的是,他们观察图像中包含物体的概率较高的部分。YOLO 或你只看一次是一个对象检测算法,与上面看到的基于区域的算法有很大不同。在 YOLO,单个卷积网络预测边界框和这些框的类别概率。
YOLO 天体探测器管道的简化图( 来源 )
YOLO 使用的策略与早期的探测系统完全不同。单个神经网络被应用于整个图像。该网络将图片分成多个区域,并计算每个区域的边界框和概率。这些边界框由预测概率加权。
SSD:单次多盒探测器
SSD 是物体检测中比较流行的算法。一般比更快的 RCNN 快。SSD 将边界框的输出空间离散化为一组默认框,每个要素地图位置具有不同的纵横比和比例。在预测时,网络为每个默认框中每个对象类别的存在生成分数,并对框进行调整以更好地匹配对象形状。此外,该网络结合了来自不同分辨率的多个特征地图的预测,以自然地处理各种尺寸的物体。[4]
截图来自解释固态硬盘架构的文章
现在,我们将看到我们如何为这个项目和 YOLOv3 的实现收集数据。
数据集采集
在这个项目中,我们首先将正在探测的物体分为四类:头盔(戴头盔的人)、人(不戴头盔的人)、消防和安全背心。
作者照片
YOLO 格式的图像标注
训练数据集是作为样本收集的图像,并被注释用于训练深度神经网络。我们从多个来源为我们的数据集收集图像,如可以在 github 上找到的安全头盔数据集,来自 google 和 Flickr 的图像,并使用 labelIMg 对它们进行注释。此外,为了获得这些对象的定制数据,我们使用 Flickr API &下载了使用 python 代码的图像。为了给图像加标签,我们使用了标签工具。
标签工具
LabelImg 是一个图形化的图像注释工具。它是用 Python 编写的,图形界面使用 Qt。对于对象检测,有许多格式可用于准备和注释训练数据集。图像注释以 PASCAL VOC 格式保存为 XML 文件,这是 ImageNet 使用的格式。它也支持 YOLO 格式,我们使用了这种格式,注释保存在下面的文本文件中。
文本文件中的格式
/ 对应于边界框对应轴的中心
/ 对应边界框的宽度和高度
;范围从[0;1]
举例:
2 0.513125 0.209167 0.173750 0.358333
作者照片
你可以参考这篇文章https://medium . com/deepquestai/object-detection-training-preparing-your-custom-dataset-6248679 f0d 1d来全面了解如何使用这个工具来注释你的定制数据集。
贴标签时的重要注意事项
为了避免收集火灾图像数据时的错误预测,我们确保我们包括每个对象的各种方向、背景和角度,以更好地训练我们的模型。例如,我们已经包含了落日的图像,将注释置为空,以避免它被误认为是火。
这里有一个链接用于我们的数据集,其中包含一个戴头盔的人、一个不戴头盔的人、消防和安全背心的图像。
为什么使用 YOLO?
与其他检测系统相比,使用 YOLO 的最大优势之一是,它的预测是由测试时整个图像的全球背景提供的。与 R-CNN 等基于分类器的系统不同,它还通过单一网络评估进行预测。YOLO 比 R-CNN 快 1000 多倍,比快速 R-CNN 快 100 多倍。[10]
- 速度(每秒 45 帧,比实时速度快)
- 网络理解一般化的对象表示(这允许他们在真实世界的图像上训练网络,并且对艺术品的预测仍然相当准确)
- 更快的版本(架构更小)—每秒 155 帧,但精度较低
- 开源:https://pjreddie.com/darknet/yolo/
不同版本的 YOLO
我们在项目中使用的版本是 YOLOv3。YOLO 目前有 4 个版本:v1、v2、v3 和 v4。每个版本都有不断的改进。我们使用的 v3 中最显著的改进是在类别预测和特征提取方面。对于类预测,YOLOv3 不使用 softmax,因为它假设每个“盒子”只能有一个类,这通常是不正确的。相反,研究人员使用独立的逻辑分类器。对于特征提取,他们使用了一个有 53 个卷积层的网络——称为 Darknet-53。[11]
地图上 5 IOU 度量的速度/精度权衡参考网站
YOLO 的不同实现
- 暗网(https://pjreddie.com/darknet/)
- AlexeyAB/darknet(https://github.com/AlexeyAB/darknet)
- 暗流(【https://github.com/thtrieu/darkflow/】T2
我们使用 YOLO 的暗网实现
yolov 3 是如何工作的?
请参考关于“现场安全深度学习:个人防护装备实时检测的论文,以更好地了解 YOLOv3 的工作情况。
- 在 YOLO-v3 中,我们将 416 × 416 RGB 图像作为输入,它包含三个输出层,每个层将输入图像分别划分为 13 × 13 网格(输出-1)、26 × 26 网格(输出-2)和 52 × 52 网格(输出-3),如下图所示。[12]
YOLOv3 算法,如“现场安全深度学习:个人防护设备的实时检测”中所述
2.三个锚盒连接到三个输出层中的每一层,总共有九个锚盒。[12]
3.输出-1 层指的是三个最大的锚定框(用于检测大的对象),输出-2 层连接到下三个最大的锚定框(用于检测中等大小的对象),输出-3 层与三个最小的锚定框相关联(用于检测小的对象)。[12]
4.在训练过程中,输出层中的每个网格单元采用适当的锚定框,并学习如何移动和/或缩放这些锚定框,以使改变后的框(也称为边界框)完美地适合感兴趣的对象,如图所示…[12]
5.每个预测的边界框与一个(N +5)维向量相关联。[12]
在上面的向量 tx 中,ty、tw 和 th 分别表示中心的 x 和 y 坐标,以及盒子的宽度和高度。
6.值 p0(也称为客体分数)是主体/客体在边界框内的概率。其余的值是 N 个条件概率,P(Ci|object),每个表示给定一个存在于盒子内的对象,它属于 Ci 类的概率是多少,其中 i = 1,…,N[12]
7.总的来说,YOLO-v3 对于单个图像产生总共 10,647 个盒子,因为(13×13×3)+(26×26×3)+(52×52×3)= 10,647。然而,直觉上,输入图片中的大多数输出框要么是假阳性,要么代表相同的东西。[12]
8.为了避免推理部分的不一致和重复,YOLO 使用非最大抑制(NMS)技术来消除重叠百分比较高但置信度较低的框,从而确保给定项目的单个边界框的保留。[12]
结果的定量分析
衡量对象检测算法性能的一个常见度量是交集/并集(又称 IoU)。如下图所示,IoU 表示两个框(如地面实况框(G)和预测框(P ))之间的重叠百分比,并使用下面给出的公式进行计算:[12]
计算欠条
在下图中,我们可以看到白色的边界框代表真实情况,红色的边界框代表预测。地面真实值和预测值之间的差异用于获得我们模型的平均精度。
作者图片
作者图片
作者图片
AP(平均精度)是测量物体检测器(如更快的 R-CNN、SSD、YOLOv3 等)精度时使用最广泛的指标。平均精度计算 0 到 1 之间的召回值的平均精度值。
下图显示了我们对整个数据集的预测结果。我们获得了 77.58%的平均精度
作者图片
我们在一个视频上测试了我们训练过的模型,视频中有人戴着头盔,穿着安全背心,还有一个有火的场景。
我们训练好的模型对视频输入的结果:由我的队友上传到他的 YouTube 频道
如果你对在我们的自定义数据集上实现 YOLOv3 的代码感兴趣,请使用下面的链接访问它。
* [## rroy1212/PPE_Detection_YOLOv3
permalink dissolve GitHub 是超过 5000 万开发人员的家园,他们一起工作来托管和审查代码,管理…
github.com](https://github.com/rroy1212/PPE_Detection_YOLOv3/blob/master/PPE_and_Fire_Detection.ipynb)
我们项目的视频讲解
视频讲解个人防护设备和使用 Yolov3 进行火灾探测
贡献者
由 Aastha Goyal、Harshit Nainwani、Rishi Dhaka、Rupali Roy 和 Shweta Shambhavi 为 UT Austin 的数据科学实验室课程 EE 460J 设计的项目。特别感谢 Alex Dimakis 教授!
参考
- 美国人口普查局,建筑支出,https://www.census.gov/建筑/c30/prpdf.html ,访问日期:2020 年 5 月 11 日。
- 劳工统计局,《行业一览:建筑业》,https://www . bls . gov/iag/TGS/iag 23 . htm,访问日期:2020 年 5 月 11 日。
- https://towardsdatascience . com/r-CNN-fast-r-CNN-faster-r-CNN-yolo-object-detection-algorithms-36d 53571365 e
- https://arxiv.org/abs/1512.02325
- https://www . science direct . com/science/article/ABS/pii/s 0926580519308325?通过%3Dihub
- https://medium . com/@ Jonathan _ hui/map-mean-average-precision-for-object-detection-45c 121 a 31173
- T.-林毅夫、戈亚尔、吉希克、贺铿和杜大伟。密集物体探测的聚焦损失。arXiv 预印本:1708.02002,2017
- https://arxiv.org/abs/1506.02640
- https://medium . com/analytics-vid hya/初学者-目标探测指南-算法-6620fb31c375
- https://towards data science . com/yolo-you-only-look-once-real-time-object-detection-explained-492 DC 9230006
- j .雷德蒙,a .法尔哈迪。YOLOv3:增量改进,2018。arXiv
- Nath、Nipun D .、Amir H. Behzadan 和 Stephanie G. Paal 关于“现场安全的深度学习:个人防护设备的实时检测”建筑自动化 112 (2020): 103085*
用你的声音交易股票
嘿,谷歌,购买 10 只 MSFT 股票。了解如何将您的谷歌助手连接到 IFTTT,并使用羊驼 API 进行股票交易。
如果炒股可以像告诉谷歌开灯一样简单,那不是很好吗?有了羊驼毛 API 和 IFTTT,就是!羊驼最近宣布账号用户可以在 IFTTT 上构建小程序。这项新功能使用户能够用声音进行股票交易。
首先,你需要拥有羊驼、谷歌和 IFTTT 的账户。一旦你为每项服务创建了账户。登录 IFTTT。你需要将羊驼服务和谷歌助理服务链接到你的 IFTTT 账户。你可以点击上面的链接或者搜索这些服务。它会询问您的登录信息(每个帐户的用户名和密码。)
成功连接两个服务后,您可以通过单击“设置”来检查它们是否已连接。您将看到这样一个屏幕,表明状态是活动的。
下一步是构建 applet。您将通过导航到 IFTTT 的“创建”区域来完成此操作。创建平台如下所示。
IFTTT 使用的逻辑是 IF This(触发动作)Then That(服务动作)。我们这个语音交易服务的逻辑是这样的。如果(谷歌助手听到某个短语)那么(羊驼会帮我们一个忙)。
当您为(此)触发器服务选择 Google Assistant 时,系统会提示您选择触发器类型。您将选择允许数字和文本成分的选项。
然后,它会提示您添加触发短语和触发操作的详细信息。
我把羊驼放在短语的开头,这样更独特,也更容易让谷歌助手触发 IFTTT。我用“购买”而不是“购买”,因为我的谷歌助手一直听到“按”而不是“购买”,它不会触发 IFTTT。创建触发器后,它会提示您选择服务。您将选择/搜索羊驼。
这将向您展示羊驼服务的所有当前/可用选项。对于此子视图,我们将选择“下达市场订单”。
这是您将使用“配料”的地方
如果您选择“添加配料”,它会列出所有可能的选项。* *注:这就是我一直使用谷歌助手的原因。目前,亚马逊 Alexa 触发选项不包括文本和数字成分。
对于该子视图,我们将选择“购买”。添加完所有正确的逻辑后,单击 Create action。
最后一步是保存操作,并选择是否希望在 applet 运行时收到通知。
最后,在测试之前,IFTTT 将显示一个连接的屏幕——这允许您打开和关闭这个小程序。
考验的时候到了!
它非常有效!
最后一步是在 IFTTT 中创建另一个 applet(参考前面的步骤)来销售股票。一旦你完成了这个小程序,你就可以用你的声音来交易股票了。
结论
使用 API 进行股票交易已经从各个方面改变了股票交易。利用 IFTTT 和 Google Assistant 就是一个很好的例子。我希望你喜欢这篇关于如何为自己设置的教程。我也希望这能引发其他人思考如何使用羊驼和 IFTTT。
干杯。
利用新型数据流对抗新冠肺炎病毒
美国墨西哥湾;照片由美国宇航局拍摄。
随着社交距离措施的到位,大量关于新冠肺炎的讨论现在发生在 Twitter 等社交媒体平台上。这些平台包含丰富的信息,可以帮助我们回答诸如*今天有多少人表现出冠状病毒症状?*然而,并非所有信息都是平等的——这些平台也包含大量错误信息,可能会对公众造成伤害。
我们开发了一个系统来跟踪和分析提到新冠肺炎症状的推文。这个系统“听”提到新冠肺炎症状的推文。一旦被识别,推文就会通过机器学习分类器进行分类,识别它是否与用户的个人症状、其他人的症状有关,或者推文是否包含错误信息。
我们还可以使用地理定位数据来计算在给定国家的每个地区(用户允许地理定位的地方)发布症状的用户数量。根据这些数据,可以确定在给定国家的不同地区之间旅行的用户数量。这些信息可能有助于识别一个国家内的新爆发群集,并提供对公众如何应对封锁措施的深入了解。
显示一段时间内与新冠肺炎症状相关的推文数量的仪表板
为了方便获取这些信息,我们开发了一个 【症状观察】 仪表板,报告每天提到症状的推文数量。目前,美国各州和英国各级(地方和上层机构、NHS 地区和国家)都提供这些计数。这项功能将在不久的将来扩展到其他国家。
我们还与 Evergreen Life 合作,分析他们健康应用程序中的数据。作为对新冠肺炎的回应,常青人寿一直在向应用程序用户提问,以深入了解疫情。例如,用户被要求报告他们是否被隔离,或者他们或他们家中的某人是否有症状。收集的数据的深度和广度令人印象深刻,可以回答无数的问题。
该团队已经开发出解决这些问题的方法,例如,一个人经历新冠肺炎症状的平均持续时间。Evergreen Life 应用程序的用户报告是零星的,因此我们看不到一个人出现症状的完整时间表。为了处理用户报告的零星性质,我们在“Stan”编程语言中定义并拟合了一个贝叶斯模型,使我们能够确定用户最有可能在 3.06 天内经历症状。
常青人寿仪表板数据显示
当用户报告一名家庭成员出现症状时,我们可以通过确定两名家庭成员患病之间的时间来了解新冠肺炎在家庭内的相互作用。我们还知道用户是否正在隔离并随后出现症状。从这些报告中,我们可以量化隔离是否会降低您患冠状病毒的几率。我们分析了今年 3 月至 6 月期间收集的数据,并确定未隔离的个人在报告未隔离的 7 天内报告症状的可能性增加了 35%。
到目前为止,我们所做的工作展示了如何利用新的数据流来加深对新冠肺炎疫情的理解。当与更传统的数据流结合时,这些新的数据流可以帮助政府做出更明智的决策来抗击病毒。
Matthew Carter 是一名博士生,他是 EPSRC 分布式算法 CDT 的成员。这篇博客最初发布在利物浦大学新冠肺炎中心。
利用这段时间学习新的数据科学技能
哪些鲜为人知的技能在数据科学家中变得越来越普遍
这段时光一去不复返,好好利用吧。不做任何有建设性的事情就让时间、日子、星期飞逝太容易了。把这段额外的时间作为一份礼物,让自己变得积极主动。
戴维·特拉维斯在 Unsplash 上拍摄的照片
这些是最常见的技能,在数据科学家中变得非常流行。
码头工人
它不需要介绍,因为容器化你的应用程序是很常见的。但是让我们用一个例子来理解,为什么它如此有帮助。
假设您在 tensor flow lite for embedded devices 的帮助下构建了一个用于对象检测的应用程序。它必须部署在 20 个树莓设备上。现在,问题是您必须将源代码复制到所有的 raspberry pie 设备上,并安装要求,还需要设置一个进程控制监视器,以确保当设备重启或启动时,应用程序立即启动。您了解这种设置的复杂性,并且您必须在 20 台设备上执行类似的设置。你很有可能会犯一些错误,并且会花相当多的时间去改正。此外,如果有任何更新,你理解的复杂性。
现在,让我们看看 Docker 对类似设置的实现。当设备启动时,您不需要任何过程控制监视器来启动应用程序,因为有一个参数 restart,只需将其设置为 always。我们将使用应用程序的 Docker 图像。这将确保每个覆盆子馅饼得到相同的代码,这种方式也可以很容易地安装。
使用 Docker 的机器学习模型部署中的动手学习,并提供各种示例。
towardsdatascience.com](/docker-made-easy-for-data-scientists-b32efbc23165)
瓶
Flask 是一个为机器学习模型或数据库创建 API 的微框架。学起来用起来超级简单。
这是使用 flask 的示例代码片段
大多数情况下,你会使用类似的脚本,你的同事,比如前端开发人员或后端开发人员会问你 API 和有效负载。
生产机器学习模型
medium.com](https://medium.com/zykrrtech/what-i-wish-i-knew-earlier-in-ml-1212f2eed73a)
PyTest(写作测试)
戴维·特拉维斯在 Unsplash 上拍摄的照片
这被认为是最无聊的事情之一,但是相信我,你必须开始写测试了。原因是当你的应用程序开始增长,很多人都在工作时,很难跟踪变化的东西,我说的变化不是指 Git。想象一下,由于某人更新了一段代码,应用程序的某个特性突然停止了工作。现在,如果有测试,在部署之前就可以很容易地检测出来。
您可能会问,如果有人在部署之前忘记运行测试用例,该怎么办。这就是 CI/CD 派上用场的地方。您可以创建一个管道来运行代码测试,然后部署它。如果测试失败,那么您的应用程序将不会被部署。
哨兵
Sentry 用于您必须在数据科学应用程序上设置的警报。我认为这是监控机器学习模型的一部分。不同之处在于,我们用它来监控错误,而不是性能。当您已经部署了您的模型,并且它仍然处于早期阶段时,监控错误是一件非常重要的事情。
您还可以使用 Telegram 或 Slack API 将所有错误或警告发送给一个组,所有团队成员都可以看到。
许多人认为这些事情是 DevOps 的工作,但让我诚实地告诉你,它增加了你的技能,给你的简历增加了更多的价值,而且如果你在一家初创公司工作,你有机会自己做类似的事情。
绝对要结帐的附加文章
让我们看看生产级代码的一些行业标准。
towardsdatascience.com](/best-python-practices-for-data-scientists-11056edda8c7)
谢谢你…!
利用您的数据科学项目(第 2 部分)
使用 PyQt5 为 LendingClub 仪表板创建 GUI
我的 LendingClub 仪表板应用程序的图形用户界面
这是两部分系列的第二部分。查看第一篇文章,点击 此处 。
在过去的几年里,数据科学就业市场对软件工程技能和将机器学习模型投入生产的经验的重视程度显著提高。在这个故事的第 1 部分,我经历了在最终产品(理论上是面向用户的)的环境中编写利用机器学习模型的程序的一些考虑。这个过程揭示了许多启示,我相信这些启示启发了我对数据科学产品过程的了解。
在这一部分,我将重点介绍一个对许多数据科学家或学生来说可能不熟悉的过程;创建用户界面。如果你想提高数据科学就业市场的技能,学习如何为消费者包装机器学习产品,或者创建一个程序,使你的项目更容易用于个人用途,请继续阅读!
选择 PyQt5 框架
PyQt5 是一组 Python 绑定(一种从 Python 调用 C 函数的方式),允许我们创建 GUI(图形用户界面),在 C++中实现 Qt 库,只使用 Python,允许开发人员相当容易地从头创建应用程序。我也和 Kivy 和 Tkinter 一起玩过,但是 PyQt5 似乎最适合我的需要。Tkinter 是一个相当标准的 GUI 库,但它在 MAC 上有点问题,Kivy 对于移动应用程序开发来说很棒,但对于桌面应用程序来说不太有效。最终,我选择了 PyQt5,因为它非常直观和灵活。只要你熟悉创建和定义类,以及面向对象编程的一般原则,这是很容易掌握的。
我大量使用了来自编程频道“Tech With Tim”的 YouTube 教程来学习这个库和框架的基础知识。你可以在这里找到他的系列视频。
用 Qt Designer 设计界面
Qt 框架的一个我最喜欢的特性是能够用一个叫做 Qt Designer 的工具手动布局窗口。Designer 允许您拖放不同的元素(如按钮、标签、滑块等)。)拖到一个示例窗口上,这样您就可以设计一个视觉上吸引人的程序,而不用在代码中摆弄 x/y 坐标或维度。
在 Qt Desginer 中设置我的仪表板
一旦有了喜欢的布局,就可以将其保存为. ui 文件。从那里,我们可以在命令行中使用 pyuic5 命令从我们的。ui 文件。在确保您位于正确的工作目录后,只需在您选择的命令行界面中键入以下内容:
pyuic5 -x saved_file.ui -o new_file.py
该命令的“-x”部分添加了一段 Python 代码,当我们运行生成的 Python 脚本时,该代码将执行并显示程序。“-o”命令指定输出文件的名称。
一旦你完成了这些,你就可以在你喜欢的文本编辑器中打开这个文件并创建一个工作应用程序!
你可以在这里下载 Qt Designer for Mac 或 Windows 。在 Windows 上,您还可以使用:
pip install pyqt5-tools
在命令行中安装一套与这个库相关的工具。
向界面添加功能
一旦你为你的用户界面创建了一个 Python 文件,你可以试着运行它。它应该像正常程序一样显示在你的屏幕上,但是按钮或小部件都没有做任何事情。我们仍然需要用 Python 编写代码来指导我们的程序做我们想要做的事情。
这一部分显然会根据您创建的应用程序而有所不同,但是我将概述我创建 Lending Club 程序的主要步骤。
填充仪表板
在第一部分中,我概述了从 Lending Club 下载实时数据,并使用该模型预测还款可能性的步骤。完成后,我们希望在仪表板中显示贷款信息以及我们的预测。
我们自动生成的代码创建了一个类(我的名为 Ui_Form),然后是一个名为 setupUi 的方法。我们的主窗口将是这个类的一个实例,正如您可能猜到的,setupUi()将在脚本运行时创建并设置这个实例。生成我们的表或 tableWidget 的代码是自动为我们编写的,设置列维度也是如此。使用以下代码设置行数(display 是我们想要显示的数据框):
self.tableWidget.setRowCount(display.shape[0])
更多自动生成的代码创建表的列,然后用以下循环填充表:
for r in range(display.shape[0]):
for c in range(7):
self.tableWidget.setItem(
r,c,
QtWidgets.QTableWidgetItem(
str(display.iloc[r,c]))
)
很可能有一种更有效的方法来做到这一点。然而,我们的数据框可能不会超过 60 或 70 行。代码在速度方面没有留下任何需要改进的地方,所以我没有进一步优化。
添加过滤器
我希望用户能够缩小他们正在查看的贷款选项,以符合他们的个人偏好。首先,我添加了各种贷款类别的复选框(Lending Club 将所有贷款从 A 到 G 进行评级)。当程序启动时,我的程序自动检查每个等级的复选框,然后连接到一个名为“redraw”的类方法,每当单击它时,它将在接下来被定义。
self.a_box.setCheckState(2) # A state of 2 means the box is checked
self.a_box.clicked.connect(self.redraw)
的功能。connect()将一个事件(在本例中是 self.a_box.clicked,或者是改变状态的 A 框)连接到一个方法。这是我们赋予界面生命的主要方法。除了“全部”框之外,前面的代码对每个复选框都重复。这个框连接到一个单独的函数,该函数检查或取消检查所有的框(取决于状态),然后调用 redraw 方法。
接下来,我为用户添加了滑块,以便按利率或期望值/平均结果过滤显示的贷款。这个过程是相似的,除了我们必须设置最小和最大值,并且可能在初始化时为滑块设置一个值。
self.min_int.setMinimum(min(display_raw.intRate)-1)
self.min_int.setMaximum(max(display_raw.intRate)+1)
self.min_int.sliderMoved.connect(self.redraw)self.max_int.setMinimum(min(display_raw.intRate)-1)
self.max_int.setMaximum(max(display_raw.intRate)+1)
self.max_int.setValue(max(display_raw.intRate)+1)
self.max_int.sliderMoved.connect(self.redraw)
同样,无论何时移动滑块,动作都连接到 redraw 函数。
重画功能
每当更改过滤器时,都会调用 redraw 函数。它实际上做的和它听起来的一样。首先,创建一个过滤后的贷款等级列表。每当复选框被选中时,方法 checkState()为 2,因此 disp_rows 是用户希望返回的贷款等级列表。接下来,创建一个对象 newdf,它是反映过滤器状态的贷款数据框架的子集。最后,表会像最初一样被填充。
def redraw(self):disp_rows = [(self.a_box.checkState() == 2) * 'A',
(self.b_box.checkState() == 2) * 'B',
(self.c_box.checkState() == 2) * 'C',
(self.d_box.checkState() == 2) * 'D',
(self.e_box.checkState() == 2) * 'E',
(self.f_box.checkState() == 2) * 'F',
(self.g_box.checkState() == 2) * 'G',
]newdf = display[[x in disp_rows for x in display.grade] &
(display_raw['intRate']>=self.min_int.value()) &
(display_raw['intRate']<=self.max_int.value()) &
(display_raw['EV'] >= self.min_ev.value()) &
(display_raw['EV'] <= self.max_ev.value())
]
self.tableWidget.setRowCount(newdf.shape[0])for r in range(newdf.shape[0]):
for c in range(7):
self.tableWidget.setItem(
r,c,
QtWidgets.QTableWidgetItem(
str(newdf.iloc[r,c]))
)
登录屏幕
最后需要做的事情是合并一个登录屏幕。除了让它感觉更像一个“真实的”应用程序,登录屏幕还为用户提供了一个输入登录信息的简单方法(在本例中,是一个连接到 Lending Club API 的 API 键)。
为了创建一个单独的窗口,定义了一个新的类(我再次使用了 Qt Designer)。这个新的登录窗口将是程序启动时初始化的窗口,主窗口稍后创建。
一旦用户输入他们的 API 键,我们必须将一个按钮点击事件连接到一个函数。
self.ok_button.clicked.connect(self.logging_in)
当调用这个函数时,程序将用户的输入存储为一个名为 apikey 的全局变量,当我们设置主窗口时会再次调用这个变量。之后,主窗口被初始化,我们所知道的程序开始了。
def logging_in(self):
global apikey
apikey = ui.keybox.text()
Form = QtWidgets.QDialog()
uimain = Ui_Form()
uimain.setupUi(Form)
Form.show()
Form.exec_()
结论
经历这个过程教会了我很多关于将机器学习模型投入生产的生命周期、面向对象编程以及一般的应用程序开发。对于使用像 PyQt5 这样的框架可以完成的事情,我仅仅触及了皮毛。如果你有兴趣了解更多,请查看之前提到的 Tim(不是我)的 Youtube 教程、 PyQt5 文档,或者尝试自己创建一个应用或项目!
如果你想了解更多关于我的应用程序的后端,请在这里阅读第一部分*。在我的 Github* 上随意查看完整代码,这里 。
利用二元交叉熵的内生变量
如何使用内生变量做出更明智和准确的决策,而不破坏模型的基本数学和逻辑。
内生性通常是由于因果关系。时间的方向可以告诉我们变量在模型中应该如何相互关联。(burst.shopify.com)
最近,我的任务是使用具有有趣的内生变量的数据构建一个分类器——这对于告诉我的模型如何训练非常有价值。在本文中,我将详细介绍我所采取的步骤,您自己也可以采用类似的策略。最终,我们将有办法使用内生变量来训练一个模型,而不用担心它对模型假设的影响,也不用走任何逻辑捷径。很明显,使用一个不能包含在模型中的单独的数据,我们可以通过告诉它是否做了一个好的预测来改进模型。
开始之前,您应该对分类算法的工作原理和成本函数的作用有一个基本的了解。此外,对微分学有所了解会对最后一节有所帮助。
Sigmoid 函数——二元分类的关键数学公式。(维基共享资源)
什么是二元交叉熵?
二元交叉熵,也称为对数损失或逻辑损失,是逻辑回归的成本函数。Log-loss 也是一种流行的损失函数,用于深度学习模型的分类,以及像 XGBoost 这样的树集成模型。“二元交叉熵”恰好是这篇文章中探索的方法的一个特别合适的名字,因为我们将充分利用这个方程的可分性。
对数损失作为分段函数
上面是写为分段函数的二元交叉熵方程。
- T5【J】T6是损失函数
- y 才是真正的标签
- p 是模型的连续输出,预测概率 y =1
逻辑损失是并排绘制的两个方程。
我们的预测离实际值越远,成本函数值就越高。
它也可以合并成一个等式:
对数损失作为一个等式
请注意,该等式简化为 y=1 和 y=0 的第一个函数片段,这是 y 的唯一可能值。
快速旁注…
损失函数、成本函数、和目标函数经常互换使用。“损失函数”指的是与单个观测相关联的损失,而“成本”指的是整个数据集。“目标函数”只是一个函数被最小化或最大化的更一般的术语。
渐变和粗麻布
在分类模型中,目标是找到优化 p 的模型参数,最小化所有观察的该函数。为了做到这一点,我们需要使用函数的梯度,有时也是 hessian。这些是通过分别对模型参数取一阶和二阶导数而得到的。
对数损失的梯度和 hessian 如下所示。在讨论了原始成本函数的修改版本后,我们稍后将回到这个问题,并更深入地研究数学。
什么是内生变量?
内生变量是依赖于模型输出的变量。通常,人们可能将模型的目标变量称为内生变量,但在某些情况下,可能不止一个。
例如,如果一个模型预测某人是否会买一只宠物狗,他们是否会给这只狗取名为“矮胖”将取决于他们是否买过这只狗。如果我们试图在“这个人会买狗吗”模型中使用被命名为 Chunky 的狗作为变量,就会出现两个问题。
- ***因果关系:*买狗先于命名。未来发生的事情不能决定过去的事情。
- ***训练偏差:*只有买过狗的人才会有关于他们是否给自己的狗起名为矮胖的有用信息。有些人差点就买了一只狗,并给它取名为矮胖,但他们没有买。如果我们在分类模型中使用“矮胖”变量,我们只能对一组结果这样做。我们无法得到一只不存在的狗的名字。
想一想第二点……一个人愿意给他们的狗起名叫矮胖,这可能首先就说明了这个人和他们对狗的喜爱。我们稍后将回到这个概念。
分类模型的其他内生变量示例:
- 预测是否会下雨的模型中的降雨量
- 就职演说的长度预测某人是否会赢得政治选举的模型
- 在一个预测学生是否能通过课程的模型中,最终成绩达到了 T21
一个雨量计,本意是测量 后 下雨。(维基共享)
到目前为止,你会注意到四个例子,有时变量是可量化的,有时是不可量化的。例如,你可以测量零英寸的降雨量,但是你不能得到一个没有发生的演讲的长度。有不同的方法来估算缺失值,但那是采用了不必要的假设,给我们的预测添加了不必要的警告。在下一节中,我们将讨论一个通用的解决方案。
我们如何修改二元交叉熵,为什么我们要这样做?
让我们再看一个降雨的例子。一个预测“无雨”的模型实际上只下了半英寸的雨,从数量上来说,这比一个预测“无雨”的模型实际上只下了六英寸的雨要少得多。使用这些信息来改进模型可能是个好主意。
有时,这种关系并不十分清晰,但是如果内生变量和目标变量之间的关系可以被合理化并以数学方式公式化,,那么它就可以被纳入成本函数。
这将“惩罚”一个模型,而不是预测一个错误更多的结果,而不是一个错误更少的结果,从而相应地为成本函数产生更高或更低的输出。
修改成本函数有什么作用?
修改成本函数将改变模型参数的误差表面的最小值和斜率。这意味着收敛可能发生在模型参数被更好地概括的地方,因为模型现在包含了额外的信息。
为什么不用回归模型来代替呢?
在某些情况下,预测正在讨论的内生变量,然后建立一个决策边界来预测原始目标变量可能是有效的。然而,可能会出现一些问题:
- 带有决策边界的回归可能无法很好地回答最初的问题。回到降雨量的例子,累积十分之一英寸的降雨量仍然是雨,在二进制尺度上,这仍然是 1。
- 连续内生变量的分布可能不适用于大多数参数建模技术。(例如,参见下面的 Tweedie 分布)。
- 对于缺失的内生变量来说,一个合理的值可能并不存在。在某些情况下,估算值可能是合理的,但在我的特定项目中却不是这样。
特威迪分布
另一个想法可能是使用内生变量为 y 设定一些修改值,但是**您必须*考虑这对成本函数有什么影响。*如果 p > 1,将使用原始成本函数来定义值。
一种改进的分类代价函数。
由于二进制交叉熵可以像分段函数一样处理,我们可以只修改方程的一部分。我们将添加一个比例因子, s ,它是我们内生变量的函数,对于 y=1 的情况。该比例因子是所讨论的内生变量的函数,在相当中性的情况下取值 1,当成本应该更高时取值高,当成本应该更低时取值低。
这最终会根据内生变量的值,或多或少地惩罚模型。现在不仅仅是估计是否错误的问题,还有另一个因素在某些情况下发挥作用。你可以原谅这样的情况,你可以说类似于“这个估计在技术上是错误的,但是我不在乎这个其他的因素,所以我不希望这个模型有太大的改变。”本质上,这就是改变成本函数的作用。
现在我们可以看到,对于我们缺失的内生变量的值,我们估算什么并不重要,因为对于 y=0 的情况,它在数学上是不相关的。当将原始函数转换为非分段函数时,这仍然成立。
微积分!
一些机器学习库,以及任何定制的梯度下降实现,将需要计算成本函数的梯度。有时,也需要粗麻布。
现在我们将计算这个二进制交叉熵的修改版本的梯度和 hessian。
梯度
首先,求 J 对 p 的偏导数。
请记住,p 是模型输出的 sigmoid 变换,我们称之为“x”
上面显示了 s 形曲线的导数。
代入上式,求梯度。
修改后的成本函数的梯度,使用我们上面计算的两个偏导数。
对于 s =1 和 y =0 的情况,这简化为原始梯度。我们现在已经找到了修改后的成本函数的一阶导数,这是基本梯度下降算法所需要的。
打包麻布
对于更复杂的算法,我们也需要二阶导数。请记住,我们已经将 x 定义为预转换模型输出。
定义黑森人。
替代。
重构。
我们可以用 p 来继续区分。
换人后。
求导之后。
将 p 替换回。
简化。
这简化为原始的 hessian 对于 s =1,以及 y =0 的情况。
摘要
仅仅因为一个变量是内生的,并不意味着它不能用来改进一个模型。在这里,我提出了一个调整逻辑损失的建议,以纳入通常不会在模型中使用的数据,这可能会改善结果。对如何使用这些变量的决策进行合理化仍然很重要,但有足够的空间进行实验。
后续步骤
这是我自己想出来的方法,以前没有遇到过类似的解决问题的方法。如果你已经找到了关于这个主题的其他资源,请在评论中分享!
矮胖的