加密您的数据
使用 Python 进行对称加密
Unlock the Lock
美国泄密者爱德华·斯诺登和前国家安全局(NSA)局长迈克尔·海登说美国有了“牢不可破的端到端加密技术”更加安全斯诺登继续说道,“我可以向你保证……有一件事:如果我在某件事情上与国家安全局局长并肩站在一起,那一定有一个非常好的理由。”
任何与数据打交道的人都有责任确保敏感数据不会面临被盗的风险。已经实施的网络安全政策往往被当作一个最低限度的检查清单,而事实证明,这是不够的。2018 年,T-Mobile、Quora、脸书和 18 家其他大公司都屈服于数据泄露。不幸的是,数据伦理和数据安全经常被忽视。将这篇文章作为加密您自己的数据的快速教程,请进一步学习如何在数据库系统上正确地实现加密。
Insert hologram of padlock
加密的思想很简单:发送方可以用加密密钥对数据进行加密,然后接收方用解密密钥对数据进行解密。对称加密意味着加密密钥和解密密钥是相同的。这是效率较低的方法,因为密钥的传递可能会被数字截获。不对称加密意味着加密密钥和解密密钥不同。不对称加密或公钥加密是通过使用公钥和私钥来实现的。私钥用于解密数据。
好的加密密钥应该不小于 2048 位。由于巨大的计算负担,很少使用大于这个值的键;然而,这种大小的密钥已经足够好了,因为一台普通的计算机需要 140 亿年才能破解一个 2048 位的证书,而没有人有时间做这个。
Sweet Brown
瑟里奥特先生指出,没有任何借口不加密你的数据。它在您的设备上,因此您有责任对您使用的数据进行加密,尤其是当您使用敏感数据时。我现在有一个数据库,里面有我朋友的名字、社保号码和他们最害怕的事情。如果任何人得到这个信息,它可能意味着危险!
让我们来看看如何使用对称密钥系统最好地加密这个熊猫数据帧的步骤。下面的模型简化了加密过程。
Symmetric Encryption
在开始之前,我们需要安装 Python 加密模块。此模块的文档可在这里找到。危险材料层激起了我的兴趣。此外,检查他们的开源项目!
C.R.E.A.M.
我想加密我的非常敏感的数据,所以我打算先将它保存为一个文件—一个 CSV 文件。
导入加密模块并创建新密钥。每次运行这段代码,都会打印一个新的密钥。密钥是一个 Base64 编码器字符串的字节对象。
我们需要保存一个密钥,这样我们就可以用它来加密。为了保存密钥,我们可以将其写入文件。下面的代码会将它保存在您当前工作的文件夹中的一个文件中。
如果您想要检索密钥,我们可以从用 read bytes 保存的文件中检索密钥。
如果您想创建一个新的带密码的加密密钥,您也可以这样做。使用这种方法,将为使用的每个新密码创建一个新的加密密钥。您需要创建一个 salt ,使用 OS 模块可以轻松完成。
我们可以复制并粘贴输出,并在下面的代码中将其指定为 salt。
有了所有这些参数,我们就可以简单地更改提供的密码,我们将被分配一个新的密钥。每个唯一的密码都有一个唯一的密钥。
有了我们的密钥,我们现在可以加密和解密一个文件,甚至是一个字符串!使用下面的代码,你可以加密一个文件。
文件现在看起来像这样:
Encrypted!
要解密,我们可以简单地切换一个文件名和一些如下所示的命令:
现在你应该有三个文件。原始文件、该文件的加密版本和该文件的解密版本。只要您有密钥,您就可以删除原始文件,只保留加密版本。正确的密钥管理对于加密的有效性至关重要。钥匙应始终保存在一个单独且安全的地方。您不应该将加密密钥保存在一个文件或同一个文件夹中。这就像把前门的钥匙放在门垫下面一样。
你甚至可以加密你的加密密钥!这被称为密钥加密密钥(KEK ),应该与它解锁的数据隔离开来。最佳实践包括用主加密密钥和主签名密钥保护您的 KEKs。对数据的不同部分使用不同的键也是一个好的做法。这可能很困难,因为它要求用户知道每次加密或解密数据时要获取哪个密钥。遵循适当的加密协议有可能减少你被黑客攻击的机会,但不幸的是,大多数程序员在数据安全方面做得很少。太多的开发人员遵循 HIPAA 或公司政策等指导原则所要求的清单,但是这些指导原则通常不足以对数据泄露进行适当的防御。这里有一些你应该避免的常见加密错误。
权力越大,责任越大,如果您在计算机上处理敏感数据,保护这些数据就是您的责任。自学加密的细节和最佳实施方法应该可以防止数据泄露发生在您身上。练习加密你的文件,因为你永远不知道谁会在看。
从人脸中检测亲缘关系的端到端解决方案。
这篇博客将一步一步地指导你如何用 Keras 构建一个深度神经网络模型,从无到有,最后用 Flask 将其部署到网络上。这个问题是由 Kaggle 主办的一个比赛,可以在这里找到。
Photo by Priscilla Du Preez on Unsplash
目录:
1-定义我们的目标
2-数据及其描述
3-建筑模型
4-评估
5-生产模型
6-视频
7 项待办任务
8-参考文献
定义我们的目标:
你的鼻子长在你身上了吗?
血亲往往有共同的面部特征。现在,东北大学的研究人员希望改进他们的面部图像分类算法,以弥合研究与 DNA 结果等其他家族标记之间的差距。
由于以下几个原因,这项技术在实践中仍然鲜为人知:
1.用于亲属关系识别任务的现有图像数据库不够大,不足以捕捉和反映世界上家庭的真实数据分布。
2.许多隐藏因素会影响家族面部关系,因此需要一个比最常用于更高级别分类(例如面部识别或对象分类)的计算机视觉算法更具判别力的模型。
因此,我们将建立一个复杂的模型,通过仅仅基于他们的面部图像来确定两个人是否有血缘关系。
数据:
我们将使用 Families In the Wild (FIW) 提供的数据,这是最大、最全面的自动亲属识别图像数据库。
FIW 的数据集是从名人的公开图片中获取的。有关他们贴标过程的更多信息,请访问他们的数据库页面。
文件描述:
文件夹“train”由名为(F0123
)的族的子文件夹组成,这些族文件夹包含个人的子文件夹(MIDx
)。同一个MIDx
文件夹中的图片属于同一个人。同一F0123
文件夹中的图像属于同一家族。
列车文件夹如下所示:
Train Folder
“train”的每个子文件夹如下所示:
每个个人文件夹都包含此人的面孔:
文件夹“test”包含需要与另一个随机图像测试是否有血缘关系的人脸图像。
下面显示的文件“train_relationships.csv”包含训练标签。记住,不是每个家庭成员都有亲属关系。例如,父母和他们的孩子是亲戚,但彼此不是。
train_relationships.csv
设置所需的库以便在需要时使用:
注意:如果您的系统中不存在库“keras_vggface ”,导入时可能会出错。要下载它,您可以参考/运行/使用以下代码:
!pip install git+https://github.com/rcmalli/keras-vggface.git
深入数据文件夹并分析 train_relationship.csv 文件,我发现了一些问题。例如:在 train_relationship.csv 文件中有一个‘f 0039/mid 1’和‘f 0039/mid 3’的关系,但是在 train 文件夹中没有‘f 0039/mid 3’的文件夹。
我能看到一些类似的问题是因为缺少了下面的文件夹
f 0039/mid 4
f 0041/mid 5
f 0041/mid 7
f 0051/mid 5
…等等。
解决上述问题的一个简单方法是忽略这些空目录,只考虑那些对我们可用的目录。
加载数据并将其分成训练和验证集。
“val_images”包含文件夹名称以“F09”开头的系列文件夹,而“train_images”包含所有其他系列文件夹。
现在,这两个文件夹也包含空目录,这是我们上面讨论过的问题。是时候忽略它们了:
我们有“train”和“val ”,它们分别包含用于培训和验证过程的系列文件夹。
在定义了这个问题的目标之后,我们探索并理解了我们所拥有的数据的本质,我们也克服了我们所面临的一个简单的问题。现在是做一些建模的时候了。回想一下我们一开始为自己设定的目标:
“预测,给定两张脸是否有血缘关系.”
所以基本上我们手头有一个分类问题需要解决。
深度学习模型:
与以前的经典方法相比,使用深度学习架构,人脸识别的任务显示出高度提高的准确性。当用巨大的数据集训练时,最先进的模型现在甚至可以胜过人类。
对于我们的问题,我们将使用两种不同的架构,在一系列人脸识别基准数据集上取得了最先进的结果。这两个系统都可以用来从人脸中提取高质量的特征,称为人脸嵌入,然后可以用来比较两张相似或不相似的人脸。
1- Facenet :是谷歌的研究人员在 2015 年开发的人脸识别系统。它以图像为输入,预测 128 维向量或人脸嵌入。所以简单来说,这个矢量/人脸嵌入现在用数字来表示输入的人脸。
2- VGGFace :它是由牛津大学视觉几何组(Visual Geometry Group)的研究人员开发的,该组是图像处理领域最著名的小组之一。它将图像作为输入,并预测 2048 维向量或人脸嵌入。
这两个模型将作为我们的基础模型,也就是说,我们将把我们的输入图像对传递给这两个模型,并获得代表输入人脸的人脸嵌入。一旦我们建立了下面的实际模型,就很清楚了。
输入:
我们有两个人脸图像,图像 1 和图像 2,我们的两个基本模型将采取这些图像中的每一个。Facenet 会把 image_1 和 image_2 作为 input_1 和 input_2。VGGFace 会把 image_1 和 image_2 作为 input_3 和 input_4。
注:不同型号的图像输入尺寸不同。
在将输入图像通过两个基本模型后,我们将得到两个图像的人脸嵌入。我们有
来自 Facenet 模型的图像 1 的 x1-面部嵌入
来自面网模型的图像 2 的 x2 面嵌入
来自 VGGFace 模型的图像 1 的 x3-人脸嵌入
来自 VGGFace 模型的图像 2 的 x4-人脸嵌入
我们可以将这些嵌入直接用于我们的分类任务,方法是将它们通过密集的 FC 层,但不是这样做,而是将这些嵌入组合或合并以获得更好的结果,这将是一个很好的特征工程技巧。
例如:对向量 x1 求平方可以给出关于 image_1 的更多信息。
像这样,我们可以使用许多不同的组合,如加法(x1,x2)乘法(x3,x4)等。这方面的代码如下所示:
我们已经准备好了模型架构。下一步是开始用一些损耗和优化器来训练它。在训练模型之前,我们需要定义一些帮助函数,它们将在训练和推理步骤中帮助我们。
read_img_fn '将采用输入图像的路径,并返回具有预定义大小的相同图像。请记住,我们对不同的基本模型使用不同的图像输入尺寸。与此类似,另一个函数‘read _ img _ vgg’将为 VGGFace 模型做同样的事情。
一个特殊的辅助函数“生成”用于生成具有某个固定 bath_size 的成批图像对。对于每一批,它将返回四个图像的组合(每一对用于两个模型的输入)和标签。
损失函数和模型配置:
正如我们在开始时讨论的,这是一个二元分类问题。我们将使用二元交叉熵或对数损失作为这个问题的损失函数。
为了配置模型,我们将使用准确性作为工具来跟踪性能,而训练和优化器将是学习率= 1e-5 的 Adam。
训练:
最后,我们准备好训练上面定义的模型。使用回调在各个点存储和使用训练好的模型,训练的最终代码看起来是这样的。
您可以看到,我们正在使用“生成”辅助函数来生成批量大小为 16 的图像。在几个时期的训练之后,模型收敛,并且精度没有从那里提高。
最后,用文件名’ facenet_vgg.h5 '保存训练好的模型。
评估:
现在,我们可以使用这个训练好的模型来预测给定两个输入图像的概率。为此,我们将使用 Kaggle 提供的“sample_submission.csv”文件。该文件包含成对的图像,模型需要预测亲属关系的概率。
然后将这个文件“face_vgg.csv”提交给 Kaggle 来检查分数。我们的模型表现良好,在私有 lb 上给出的 AUC 分数为“0.887”,在公共 lb 上给出的 AUC 分数为“0.881”。
由此,我们可以说,我们复杂的深度学习模型在这项任务中表现良好,并准备投入生产。
生产途径:
我们将使用基于 Python 的微型 web 框架 Flask 在本地主机上制作一个简单的 web 服务器,它将充当 API,并帮助最终用户与我们训练过的模型进行通信。所以现在任何人都可以访问我们的模型。
首先,让我们使用 Flask 构建一个简单的 web API 来演示事情是如何工作的。
我们需要制作两个不同的文件。
1- app.py —后端的 Flask 脚本
from flask import Flask, request, render_template
app = Flask(__name__)[@app](http://twitter.com/app).route('/')
def hello_world():
return render_template('index.html')
上面的代码将导入必要的文件并启动 Flask 实例。函数 hello_world()将把我们的主页呈现给用户。
2-index.html-用户将在前端看到的主页 html 文件。
<!DOCTYPE html>
<html>
<head>
<title>Flask</title>
</head>
<body>
<p>Hello world</p>
</body>
</html>
现在我们有了我们需要的两个文件。运行 app.py 文件后,用户将能够看到“Hello world”页面。
在构建之前,让我们首先想象一下事物如何工作的整体结构。用户将看到一个网页,要求他/她上传两个图像文件,他/她需要看看他们是否有血缘关系。在后端,我们的 Flask 脚本将访问这两个图像,并在其上运行所需的操作。它将获取图像,执行预处理,然后将它们传递给我们训练好的模型。最后,该模型将预测概率,并在前端投射给用户。
现在,我们将开始构建我们的亲属预测 API。
这是我们项目布局的样子——
**/Kin-Prediction
├── templates
└── index.html
└── end.html** **├── static
└── Image1
└── Image2
├── venv/
├── app.py
├── facenet_vgg.h5**
项目文件夹包含我们的烧瓶需要的所有文件。
文件夹模板将包含前端的 HTML 文件,Flask 将在需要时使用这些文件进行渲染。
静态文件夹将包含用户将上传用于预测的图像。
“facenet_vgg.h5”是我们训练好的模型,需要保存在目录中,以便 Flask 可以直接使用它。
app.py 文件是将运行并执行所有后端操作的主 Flask 脚本。
app.py 中的以下函数将为用户呈现主页。
@app.route('/')
@app.route('/home')
def upload_image():
return flask.render_template('index.html')
index.html 的文件如下所示:
<html>
<head>
<meta charset="UTF-8">
<title>Kin Relationship</title>
</head>
<body style="background-color:gray;" >
<form action = "http://localhost:5000/upload" method = "POST"
enctype = "multipart/form-data" align="center">
First Image:<label>
<input type = "file" name = "file1" /></label>
Second Image<label>
<input type = "file" name = "file2" /></label>
<label>
<input type = "submit" value="Predict"/> </label>
</form>
</body>
</html>
该页面将如下所示:
选择图像并点击预测按钮后,这两幅图像都将被上传并保存到我们的本地目录文件夹中,该文件夹是“静态”的。Flask 将从那里访问图像并执行所需的操作,最后预测概率。
app.py 文件中的代码如下所示:
最后,flask 将使用图像和预测来呈现模板“end.html”。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Prediction</title>
</head>
<body style="background-color:powderblue;">
<div>
{% for i in image_name %}
<img src=" {{url_for('static', filename=i) }} " style="width:250px;height:300px;" hspace="200">
{% endfor %}
</div>
<h1 style="font-family:verdana;"> Probablity of two persons having kin relation is {{pred}} </h1>
</body>
</html>
就是这样。我们的模型已经准备好并正在本地主机上运行。你可以在这里下载并参考完整代码。
我试着这样做,首先用我和我爸爸的照片,然后用妈妈和爸爸的照片。在这两种情况下,模型都工作得非常好,并分别预测高概率(血缘相关)和低概率(血缘不相关)。
视频:
这是展示上述实验的视频。
待办任务:
1-我们可以将它部署在基于云的 web 服务上,而不仅仅是在本地服务器上运行,比如 Heroku,这样任何地方的任何人都可以访问你的模型。
2-目前,我们的模型只拍摄人脸图像,因为它只在人脸上进行训练。如果给定一个人的完整图像,它将不起作用。我们可以构建一个不同的解决方案来处理这个问题。
3-使用先进的 CSS 和 javascript,我们可以设计我们的前端看起来不错。
谢谢你在这个博客中走了这么远。希望你喜欢。有什么建议可以通过 Linkedin 联系我。
参考资料:
1-https://www . ka ggle . com/c/recogniting-faces-in-the-wild/overview
2-https://blog . keras . io/building-a-simple-keras-deep-learning-rest-API . html
3-https://heart beat . fritz . ai/brilliant-初学者-模型部署指南-133e158f6717
在 Apache PySpark 中引入时间序列数据的端到端插值
Photo by Steve Halama on Unsplash
任何处理数据的人都知道现实世界中的数据通常是不完整的,清理数据会占用你大量的时间(有人知道 80/20 法则吗?).最近从 Pandas 搬到 Pyspark,我已经习惯了 Pandas 提供的便利,而 Pyspark 由于它的分布式特性有时会缺乏这些便利。我一直特别缺少的一个特性是插值(或填充)时间序列数据的直接方式。虽然填充缺失值的问题已经被讨论过几次(例如,这里的[]这里的 ]),但是我找不到一个来源,它详细描述了生成底层时间网格然后填充缺失值的端到端过程。这篇文章试图填补这个空白。从缺失条目的时间序列开始,我将展示我们如何利用 PySpark 首先生成缺失的时间戳,然后使用三种不同的插值方法(向前填充、向后填充和插值)填充缺失的值。使用在一组房屋中收集的传感器读取数据的例子来演示这一点。这篇文章的完整代码可以在我的 github 中的找到。
准备数据并可视化问题
为了演示这个过程,首先,我们生成一些测试数据。数据集包含两所房子的数据,并使用 sin()和 cos()函数为一组日期生成一些传感器读取数据。为了生成缺失值,我们随机丢弃一半的条目。
Creating the sensor data test set
Raw data set
下图显示了缺失值清晰可见的数据。
Read Data with Missing Entries
为了并行化数据集,我们将 Pandas 数据帧转换为 Spark 数据帧。注意,我们需要用 10⁹除日期时间,因为熊猫日期时间和火花的时间单位是不同的。我们还添加了列‘read time _ exist’来跟踪哪些值丢失了。
import pyspark.sql.functions as func
from pyspark.sql.functions import coldf = spark.createDataFrame(df0)
df = df.withColumn("readtime", col('readtime')/1e9)\
.withColumn("readtime_existent", col("readtime"))
现在我们的数据框架看起来像这样:
Read Data prepared for interpolation with PySpark
插入文字
对读取的日期时间重新采样
第一步是对读取时间数据进行重新采样。如果我们和熊猫一起工作,这将是直接的,我们可以使用resample()
方法。然而,Spark 在分布式数据集上工作,因此没有提供等效的方法。在 PySpark 中获得相同的功能需要三个步骤。在第一步中,我们按“房子”对数据进行分组,并为每栋房子生成一个包含等距时间网格的数组。在第二步中,我们使用 spark SQL 函数explode()
为数组的每个元素创建一行。在第三步中,使用所得结构作为基础,使用外部左连接将现有读取值信息连接到该基础。下面的代码展示了如何做到这一点。
注意,这里我们使用的是 spark 用户自定义函数(如果你想了解更多关于如何创建 UDF 的知识,你可以看看这里的。从 Spark 2.3 开始,Spark 提供了一个 pandas udf,它利用 Apache Arrow 的性能来分布计算。如果您使用 Spark 2.3,我建议您研究一下这个,而不是使用(性能很差的)内置 UDF。
结果表的摘录如下所示:
可以看到,readtime _ existent 列中的 null 表示缺少读取值。
使用窗口函数的向前填充和向后填充
当使用向前填充时,我们用最新的已知值填充缺失的数据。相反,当使用向后填充时,我们用下一个已知值填充数据。这可以通过结合使用 SQL 窗口函数和last()
和first()
来实现。为了确保我们不会用另一个缺失值填充缺失值,可以使用ignorenulls=True
参数。我们还需要确保设置了正确的窗口范围。对于向前填充,我们将窗口限制为负无穷大和现在之间的值(我们只查看过去,不查看未来),对于向后填充,我们将窗口限制为现在和无穷大之间的值(我们只查看未来,不查看过去)。下面的代码展示了如何实现这一点。
请注意,如果我们想使用插值而不是向前或向后填充,我们需要知道前一个现有读取值和下一个现有读取值之间的时间差。因此,我们需要保留 readtime _ existent 列。
from pyspark.sql import Window
import syswindow_ff = Window.partitionBy('house')\
.orderBy('readtime')\
.rowsBetween(-sys.maxsize, 0)
window_bf = Window.partitionBy('house')\
.orderBy('readtime')\
.rowsBetween(0, sys.maxsize)
# create series containing the filled values
read_last = func.last(df_all_dates['readvalue'],
ignorenulls=True)\
.over(window_ff)
readtime_last = func.last(df_all_dates['readtime_existent'],
ignorenulls=True)\
.over(window_ff)read_next = func.first(df_all_dates['readvalue'],
ignorenulls=True)\
.over(window_bf)
readtime_next = func.first(df_all_dates['readtime_existent'],
ignorenulls=True)\
.over(window_bf)# add columns to the dataframe
df_filled = df_all_dates.withColumn('readvalue_ff', read_last)\
.withColumn('readtime_ff', readtime_last)\
.withColumn('readvalue_bf', read_next)\
.withColumn('readtime_bf', readtime_next)
插入文字
在最后一步中,我们使用向前填充和向后填充的数据,通过一个简单的样条来插值读取日期时间和读取值。这也可以使用用户定义的函数来完成。
这给我们留下了一个包含所有插值方法的单一数据帧。它的结构是这样的:
Interpolated read data
形象化
最后,我们可以将结果可视化,以观察插值技术之间的差异。不透明点显示插值。
Original data (dark) and interpolated data (light), interpolated using (top) forward filling, (middle) backward filling and (bottom) interpolation.
我们可以清楚地看到,在上面的图中,间隙已被上一个已知值填充,在中间的图中,间隙已被下一个值填充,而在下面的图中,差异已被插值。
总结和结论
在这篇文章中,我们看到了如何使用 PySpark 来执行时间序列数据的端到端插值。我们已经演示了如何使用重采样时间序列数据,以及如何将Window
函数与first()
和last()
函数结合使用来填充生成的缺失值。然后,我们看到了如何使用用户定义的函数来执行简单的样条插值。
希望这篇帖子有助于填补 PySpark 中关于端到端时间序列插值的文献空白。
原发布于https://walken ho . github . io。
Python 中的主题建模:潜在狄利克雷分配(LDA)
http://getwallpapers.com/image/398564
深入分析
如何开始使用 Python 中的 LDA 进行主题建模
**前言:**本文旨在提供潜在主题的综合信息,不应被视为原创作品。这些信息和代码通过一些在线文章、研究论文、书籍和开源代码被重新利用
介绍
简而言之,主题模型是一种统计语言模型,用于揭示文本集合中隐藏的结构。在实际和更直观的情况下,你可以把它想象成一项任务:
降维,不要将文本 T 在其特征空间中表示为{Word_i: count(Word_i,T) for Word_i in Vocabulary},而是在主题空间中表示为{Topic_i: Weight(Topic_i,T) for Topic_i in Topics}
无监督学习,可以比作聚类,在聚类的情况下,主题的数量和聚类的数量一样,是一个输出参数。通过进行主题建模,我们建立了单词簇,而不是文本簇。因此,文本是所有主题的混合,每个主题都有特定的权重
标记,抽象文档集合中出现的最能代表其中信息的“主题”。
有几种现有的算法可以用来执行主题建模。最常见的有潜在语义分析(LSA/LSI)、概率潜在语义分析(pLSA)和潜在狄利克雷分配(LDA)
在本文中,我们将仔细研究 LDA,并使用 python 2.7 中的 sklearn 实现来实现我们的第一个主题模型
理论概述
LDA 是一个生成概率模型,它假设每个主题是一组潜在单词的混合,每个文档是一组主题概率的混合。
http://chdoig.github.io/pytexas2015-topic-modeling/#/3/4
我们可以将 LDA 的生成过程描述为,给定 M 个文档, N 个单词,以及之前 K 个主题,模型训练输出:
psi ,每个题目的字数分布 K
φ,每个文档的主题分布 i
LDA 的参数
Alpha 参数是代表文档-主题密度的狄利克雷先验集中参数——随着更高的 Alpha,文档被假定为由更多的主题组成,并导致每个文档更具体的主题分布。
Beta 参数是表示主题-词密度的相同的先验集中参数— 在高 Beta 的情况下,主题被假定为由大多数词组成,并且导致每个主题的更具体的词分布。
LDA 实施
完整的代码可以在 GitHub 的 Jupyter 笔记本上找到
- 加载数据
- 数据清理
- 探索性分析
- 为 LDA 分析准备数据
- LDA 模型训练
- 分析 LDA 模型结果
加载数据
对于本教程,我们将使用在 NeurIPS (NIPS)会议上发表的论文数据集,该会议是机器学习社区中最负盛名的年度活动之一。CSV 数据文件包含了从 1987 年到 2016 年(29 年!).这些论文讨论了机器学习中的各种主题,从神经网络到优化方法,等等。
让我们从查看文件的内容开始
# Importing modules
import pandas as pd
import osos.chdir('..')# Read data into papers
papers = pd.read_csv('./data/NIPS Papers/papers.csv')# Print head
papers.head()
Sample of raw data
数据清理
因为这个分析的目标是执行主题建模,所以让我们只关注每篇论文的文本数据,忽略其他元数据列。此外,为了演示,我们将只查看 100 篇论文
# Remove the columns
papers = papers.drop(columns=['id', 'event_type', 'pdf_name'], axis=1).sample(100)# Print out the first rows of papers
papers.head()
去掉标点/小写
接下来,让我们对 paper_text 列的内容进行简单的预处理,使它们更易于分析,并得到可靠的结果。为了做到这一点,我们将使用一个正则表达式来删除任何标点符号,然后小写文本
# Load the regular expression library
import re# Remove punctuation
papers['paper_text_processed'] = \
papers['paper_text'].map(lambda x: re.sub('[,\.!?]', '', x))# Convert the titles to lowercase
papers['paper_text_processed'] = \
papers['paper_text_processed'].map(lambda x: x.lower())# Print out the first rows of papers
papers['paper_text_processed'].head()
探索性分析
为了验证预处理是否有效,我们将使用 wordcloud 包制作一个单词云,以获得最常见单词的可视化表示。这是理解数据和确保我们在正确的轨道上的关键,以及在训练模型之前是否需要更多的预处理。
# Import the wordcloud library
from wordcloud import WordCloud# Join the different processed titles together.
long_string = ','.join(list(papers['paper_text_processed'].values))# Create a WordCloud object
wordcloud = WordCloud(background_color="white", max_words=5000, contour_width=3, contour_color='steelblue')# Generate a word cloud
wordcloud.generate(long_string)# Visualize the word cloud
wordcloud.to_image()
为 LDA 分析准备数据
接下来,让我们将文本数据转换成一种格式,作为训练 LDA 模型的输入。我们从标记文本和删除停用词开始。接下来,我们将标记化的对象转换成语料库和词典。
import gensim
from gensim.utils import simple_preprocess
import nltk
nltk.download('stopwords')
from nltk.corpus import stopwordsstop_words = stopwords.words('english')
stop_words.extend(['from', 'subject', 're', 'edu', 'use'])def sent_to_words(sentences):
for sentence in sentences:
# deacc=True removes punctuations
yield(gensim.utils.simple_preprocess(str(sentence), deacc=True))def remove_stopwords(texts):
return [[word for word in simple_preprocess(str(doc))
if word not in stop_words] for doc in texts]data = papers.paper_text_processed.values.tolist()
data_words = list(sent_to_words(data))# remove stop words
data_words = remove_stopwords(data_words)print(data_words[:1][0][:30])
import gensim.corpora as corpora# Create Dictionary
id2word = corpora.Dictionary(data_words)# Create Corpus
texts = data_words# Term Document Frequency
corpus = [id2word.doc2bow(text) for text in texts]# View
print(corpus[:1][0][:30])
LDA 模型训练
为了简单起见,除了输入主题数量之外,我们将保持所有参数的默认值。对于本教程,我们将建立一个有 10 个主题的模型,其中每个主题是关键字的组合,每个关键字对主题有一定的权重。
from pprint import pprint# number of topics
num_topics = 10# Build LDA model
lda_model = gensim.models.LdaMulticore(corpus=corpus,
id2word=id2word,
num_topics=num_topics)# Print the Keyword in the 10 topics
pprint(lda_model.print_topics())
doc_lda = lda_model[corpus]
分析 LDA 模型结果
现在我们已经有了一个训练好的模型,让我们来想象一下主题的可解释性。为此,我们将使用一个流行的可视化软件包, pyLDAvis ,该软件包旨在以交互方式帮助:
- 更好地理解和解释单个主题,以及
- 更好地理解主题之间的关系。
对于(1),您可以使用不同的λ参数值,手动选择每个主题来查看其最常用和/或“相关”的术语。当你试图给每个主题指定一个人类可以理解的名称或“含义”时,这很有帮助。
对于(2),探索主题间距离图可以帮助你了解主题如何相互关联,包括主题组之间潜在的更高层次的结构。
import pyLDAvis.gensim
import pickle
import pyLDAvis# Visualize the topics
pyLDAvis.enable_notebook()LDAvis_data_filepath = os.path.join('./results/ldavis_prepared_'+str(num_topics))# # this is a bit time consuming - make the if statement True
# # if you want to execute visualization prep yourself
if 1 == 1:
LDAvis_prepared = pyLDAvis.gensim.prepare(lda_model, corpus, id2word)
with open(LDAvis_data_filepath, 'wb') as f:
pickle.dump(LDAvis_prepared, f)# load the pre-prepared pyLDAvis data from disk
with open(LDAvis_data_filepath, 'rb') as f:
LDAvis_prepared = pickle.load(f)pyLDAvis.save_html(LDAvis_prepared, './results/ldavis_prepared_'+ str(num_topics) +'.html')LDAvis_prepared
结束语
在过去十年中,机器学习变得越来越受欢迎,计算可用性的最新进展导致人们寻求如何整合新方法来推进自然语言处理领域的方法呈指数增长。
通常,我们将主题模型视为黑盒算法,但是希望这篇文章能够阐明底层的数学、其背后的直觉,以及让您开始处理任何文本数据的高级代码。
在下一篇文章中,我们将更深入地了解如何评估主题模型的性能,调整其超参数以获得更直观和可靠的结果。
参考文献:
[1]主题模型—维基百科。https://en.wikipedia.org/wiki/Topic_model
[2]主题建模的分布式策略。https://www . ideas . Illinois . edu/bitstream/handle/2142/46405/paralleltopicmodels . pdf?sequence=2 & isAllowed=y
[3]主题地图—软件—资源— Amaral 实验室。https://amaral . northwestern . edu/resources/software/topic-mapping
[4]文本挖掘中的主题建模综述。https://thesai . org/Downloads/volume 6 no 1/Paper _ 21-A _ Survey _ of _ Topic _ Modeling _ in _ Text _ mining . pdf
感谢阅读。如果你有任何反馈,欢迎评论这篇文章,在 LinkedIn 上给我发消息,或者给我发邮件(shmkapadia[at]gmail.com)
如果你喜欢这篇文章,请访问我在 NLP 上的其他文章
构建可解释主题模型的分步指南
towardsdatascience.com](/evaluate-topic-model-in-python-latent-dirichlet-allocation-lda-7d57484bb5d0) [## 自然语言处理(NLP)简介
自然语言处理简介
medium.com](https://medium.com/@shashank.kapadia/introduction-to-natural-language-processing-nlp-2a8fae09ed03) [## 构建块:文本预处理
本文是关于自然语言处理的后续文章的第二篇。这一系列…的目的
towardsdatascience.com](/building-blocks-text-pre-processing-641cae8ba3bf) [## 语言模型简介:N-Gram
本文是关于自然语言处理的第三篇文章。这一系列…的目的
towardsdatascience.com](/introduction-to-language-models-n-gram-e323081503d9)
端到端作者识别:离线与在线方法
对两个样本笔迹是否由同一人书写进行分类。
Photo by Estée Janssens on Unsplash
动机:
每个人都有自己独特的笔迹,有自己的特点和小细节,使其与众不同。如果我们随机选取两个由两个人书写的笔迹样本,我们可能会注意到笔画的构成方式、字母之间的间距和其他细节是不同的。在这篇文章中,我们将训练一个模型来尝试区分两个样本笔迹是否是同一个人写的。
数据集:
我们将使用 IAM 在线手写数据库,它给出了每个书写者的多个手写样本,以及手写样本构建过程中记录的笔画顺序。
该数据集使我们有机会使用两种不同的方法来解决书写者识别问题:
离线设置:
Static representation of handwriting
在离线设置中,我们有手写最终结果的静态图像。这给出了笔迹外观的全局表示。这种类型的表示可能最适合用作视觉模型的输入。
在这个设置中,我们将制作两个像上面这样的图像到一个连体视觉模型(在我们的例子中是 Resnet50 ),并尝试预测这两个样本是否是同一个人写的。
Simplified illustration of the off-line model
对图像进行预处理,以去除边缘上多余的空白,并将其大小归一化为(128,1024),同时小心地保持前后相同的纵横比,以免使笔迹变形。
对每幅图像使用相同的编码器,为每幅图像产生固定长度的表示,然后将这两种表示组合起来,并提供给完全连接的层,最终进行预测。
由于我们有每个书写者的一组笔迹,我们将使用该信息通过选择来自同一书写者的两个样本来创建正面样本,并通过选择来自两个不同书写者的样本来创建负面样本。我们还确保相同的作者不能同时出现在训练集和评估集中。
在线设置:
Handwriting with individual strokes details
由于我们也将手写内容表示为一系列手写笔画,每个笔画路径的坐标为(x,y ),因此我们可以将此信息用作分类器的输入。这个坐标序列给出了产生最终手写结果所需的步骤,一次一行。它更详细地描述了这位作家与众不同的特点。
输入这种格式的最自然的方式是每个子笔画的图像序列,然而,每个手写有几十个笔画,每个笔画有多个子笔画线。由于内存问题,这使得它不实用,并且可能不是非常有效。这就是为什么我们必须将原始的线坐标作为一个(x,y)数序列提供给一个像 GRU 这样的顺序模型。
Simplified illustration of the on-line model
这有一个更轻的内存使用,我们将展示它可以很好地工作在一个比图像编码器小得多的模型上。
结果:
- 在线 Resnet 模型验证准确率: 88%
- 离线 GRU 模型验证准确率: 92%
这意味着,当应用于看不见的书写者时,GRU 模型可以以 92%的准确率分辨出两组笔迹是否出自同一个人之手。鉴于数据集的规模较小,这些结果令人鼓舞。
未来可能的改进:
有多种方法可以改进这个项目->
- 参数调整。
- 更好的(x,y)笔画序列预处理和特征工程。
- 寻找更真实的手写数据集(墨水笔在纸上),而不是手写的电子记录。
- 使用笔迹伪造的数据集作为用于训练/评估的硬负样本。
结论:
这是一个非常有趣的机器学习应用,因为它证明了每个人的笔迹都可以被识别。它还表明,在找到最佳方法之前,可以考虑用多种方法来模拟同一个问题。
数据:http://www . fki . INF . unibe . ch/databases/iam-on-line-手写-数据库/download-the-iam-on-line-手写-数据库
代号:https://github.com/CVxTz/handwriting_forensics
内生性介绍
为什么我们应该了解我们的数据
描述不同类型内生性的例子
一个冰淇淋小贩在海滩上卖冰淇淋。他收集了两年的总销售额(Y)和销售价格(X)的数据。他将数据交给数据科学家,请他找出最佳销售价格。数据科学家拟合一个线性回归模型,Y = alpha+beta X+error,通过对利润函数=(X-cost)(alpha+beta X)求微分找到最优。
然而,冰淇淋供应商过去常常随着需求的增加而提高冰淇淋的价格。他忘了向数据科学家提及他的定价策略。线性回归认为,随着销售价格的增加,销售额增加(β> 0)或者斜率没有预期的那么负。因此,该模型得出的最优售价至少是次优的(如果对业务无害的话)。
一家商店给顾客赠送定制的优惠券。有不同的优惠券(有不同的优惠)。商店收集顾客反馈数据,即他们是否使用优惠券(Y)和优惠券详情(X)。他们想知道每张优惠券的有效性,以便将来可以定制并发送给不同的客户。
数据科学家拟合一个模型来预测每个客户从每张优惠券购买(下个月或接下来几个月)的概率。同样,商店有一个策略,他们发送优惠券给谁,他们发送什么优惠券(而不是随机优惠券)。例如,如果他们认为顾客即使没有优惠券也会购买,他们就不会发送优惠券,或者如果他们认为某人可能会购买,他们就会发送更多的优惠券。这一策略没有向数据科学家公开。如果将更多优惠券发送给具有更高购买机会的顾客,也可能给出有偏见的优惠券“有效性”。
富人变得更富,穷人变得更穷(概念上类似于后面解释的示例 2,但此示例旨在表明内生性不仅限于数据科学)。这在社会学上也叫马修效应。努力工作帮助电影艺术家赢得奖项。制片人希望与获奖的艺术家建立联系,因为他们将奖项与表演质量联系在一起,因此他们获得了更好的机会,从而获得了更多的认可和更多的奖项(同样,富人变得更富)。
所有这些例子都违背了实验设计的基本原则之一——随机化。很多时候,一个公司无法承担随机化。在现场实验中,受试者被随机分配到不同的处理水平(或者我们应该随机设置自变量的值并观察因变量)。但是在上面的例子中,这被违反了(因为独立变量依赖于特定的策略——由冰淇淋供应商/商品商店/电影制片人)。
换句话说,独立预测变量(X)应该是外生而不是内生。外生变量不受其他因素驱动(可观察或不可观察)。在第一个例子中,价格由温度决定。在第二个例子中,优惠券是由过去的销售推动的)。内生变量与误差项相关,我们来解构一下。
模型中的响应(Y)应该用独立变量(X)来解释,所有不能用 X 来解释的都包括在误差项中。(我们知道线性回归中多重共线性的问题和不利影响——在多重共线性的情况下,X1 由 X2 解释)。如果有一个因素可以解释 Y 但没有被观察到,比如 Z,它就包含在误差中。但是如果 Z 可以解释任何 X(在例 1 中,Z =温度,X =价格),误差项与 X 相关(因此,价格是内生变量)。因此,当我们有一个与 Y 相关的 Z,但它也与 X 相关,并且不包括在模型中时,内生性问题就出现了。
内生性有多种来源——如果供应商根据时间改变位置会怎样,如果供应商根据队列改变价格会怎样。内生性的来源可以大致分为:
- 省略变量(如冰激凌示例所解释的,价格是内生变量,温度是省略变量)。
- s 同态性(例 2)其中 X 引起 Y,Y 引起 X,这是最常见的,也是最难的一个。在省略变量的情况下,如果结构信息是已知的,那就很简单了(这将在本博客的第 2 部分中讨论)。示例 2 是优惠券导致销售和销售导致给顾客更多优惠券的同时性的示例。在例 1 中,如果卖方基于队列长度改变作为控制的价格(在动态规划或顺序控制理论中研究的*),这是队列导致价格和价格导致队列的同时性。同时也可能是*自选造成的。**
- 选择偏差(例 3)如果取样有偏差,我们无法确定治疗对受试者的影响。例如,如果我们收集周末或高档社区的冰淇淋销售数据。如果我们的样本主要包括获奖者,我们会高估演员的表演质量。赫克曼博士就此课题获得了诺贝尔经济学奖(此处)。
修订一些定义:
- P(X=x,Y=y)是一个联合分布
- P(X=x|Y=y)是给定 Y 时 X 的条件分布
- P(X=x) = sum [ P(X=x|Y=y)P(Y=y)除以所有 y]是 X 的边际
- 如果 P(X=x,Y=y) = P(X=x)P(Y=y),那么 X 独立于 Y
我希望下面的陈述现在更有意义(简单地说就是自变量被战略性地设定或者其中自变量是内生的):
当自变量的边际分布不独立于因变量的条件分布时,内生性就产生了。
如果不考虑内生性呢?
- 这些例子说明了两个原因:首先,模型中自变量的实际影响可能被低估或高估。其次,基于模型推断的决策可能是次优的。
- 当数据科学家在不考虑内生性的情况下拟合模型时,模型拟合更好(增加了对错误事物的信心)。第二部分解释了这项索赔。
内容大量取自这里和这里。此处,我们将在处浏览所讨论的冰淇淋供应商案例。这里将介绍同时性和本文中使用的参考文献。
原载于 2019 年 6 月 2 日 https://medium.com**T21。
处理第一类内生性
为什么我们应该了解我们的数据
用冰淇淋小贩的例子来解释第一类内生性
在这里我们通过例子讨论了内生性的含义,内生性的几种可能来源以及它为什么重要。这一部分集中在冰淇淋供应商的例子,以深入探讨为什么我们应该小心内生性。线性回归模型由下式给出:
Sales _ I = alpha+beta Price _ I+error _ I(m1) error _ I ~ N(0,var_e)
在第一部分之后, Price_i 是一个内生变量,因为它可以用 temperature_i 来解释。从而| *cov(Price_i,error_i)| > 0。*设 Price_i ~N(p,var_p) 。
考虑到 Sales_i 和 Price_i 的分布为二元正态分布,我们可以推导出 S ales_i|Price_i. 的条件分布,这是一个标准推导,其推导可以在这里检查。
如左图所示,如果忽略内生性:
1。我们没有计算真实系数,导致次优,因为我们正在计算 alpha’ 和 beta’ 。
2。与模型的差异( Sales_i|Price_i )低于实际差异。
这可能会给我们错误的信心认为模型表现(适合)得很好。
补救措施
- 使用温度 _i 预测价格’ _i ,并将价格 _i 替换为 m1 中的价格’ _i 。这里,温度 _i 称为工具变量(IV) ,这种方法称为 IV 方法
- 用 Temperature_i 预测 Price’_i 求 E_i = Price_i-Price’_i 并使用:
Sales _ I = alpha+beta Price _ I+gamma E _ I+error _ I(m2)
如果有内生性,gamma 的质量会从 0 偏移。或者干脆,|gamma| > 0 有把握(统计显著)。这种方法被称为控制功能方法。
省略变量的内生性需要问题结构的知识,但如果知道,这是很简单的。然而,找到合适的 IVs 是非常困难的。可以采取预防措施,因为发现薄弱或错误的静脉注射会导致更坏的结果。
下面的陈述是我的意见和主张,可能是错误的:
的高度非线性函数逼近【Sales_i=f(Price_i) 例如,随机森林/隐马尔可夫模型/深度学习可以处理内生性,因为底层非线性隐结构旨在找到 Price_i。在因子分析或 SVD 中,如果该问题被处理为 Sales_i 和 Price_i 的联合 pdf,则隐藏/潜在因子类似于 IVs。
更普遍和困难的内生性问题是同时性的情况。此处介绍了关于同时性的讨论。
原载于 2019 年 6 月 2 日 https://medium.com。**
处理第二类内生性
为什么我们应该了解我们的数据
处理第二类内生性的文献中的例子
通过介绍和处理一种“更简单”形式的内生性(第一类),这部分探讨了更困难的同时内生性问题。当 Y 引起 X,X 引起 Y 时,同时发生(富者愈富)。
这个问题很难回答。添加工具变量(iv)可能对
2 没有帮助。这是最常见的,也很难诊断(或识别)3。X 是 i.i.d. 的标准假设不成立(一月份优惠券数量的影响可能会持续到三月份)。
如果不纠正同时性,错误模型的不良影响和对错误模型的信心仍然对同时性有效(如第二部分省略变量所述)。在大多数向客户发放优惠券的公司中,优惠券是根据某种策略决定的(这可能是之前的数据科学家构建的模型)。
博客不能涵盖同时性。因此,一些关于在哪里同时阅读的例子。
**例 1。**药品明细:制药公司向医生投放广告。他们在处方量较高的医生身上花费更多。数据科学家可以在模型中使用滞后处方,其中当前处方不仅取决于当前的详细程度,还取决于以前的处方。
**例二。**电影/产品/节目在不同的城市相继上映,这取决于在影院上映的竞争电影以及来自电影先前上映城市的电影评论。因此,在哪里和哪个城市上映电影的决定取决于电影的表现。选择合适的工具变量(或使“其他事物等于”)的控制变量,内生性是可以处理的。思路是:驱动前几个城市票房的外生变量会驱动下一个城市的票房。因此,找到正确的 IVs 有助于对抗同时性。在论文中,他们使用竞争电影的指标作为 iv。
**例 3。**一家公司可能会根据 R、F、M 值(非常常用:新近度、频率和货币)向买家发放优惠券。t-1 时的 RFM 可用作控制变量(因为它们与 t 时的误差无关)。也可以考虑控制函数(第二部分)方法,其中 R、F、M 回归为 t-1 时 R、F、M 的函数,R、F、M 的预测值和实际值之间的差异用作销售建模时的独立变量。
**例 4。**在冰淇淋供应商示例中,可以使用价格和 error_i 的联合概率分布,而不是使用温度作为 IV。虽然 IVs 控制方法模型需要数据收集的精确结构,但它并不总是可用的。例 1 中考虑的模型假设了响应模型的知识(X|Y 如何导致 Y ),这也是未知的。Copula 方法是一种使用回归量 X 和 error_i 的联合分布的无模型方法。Copula 是一种将 m 维多元分布耦合到 m 个一维边际的函数。这种方法使用最大似然法来获得联合分布。
我希望这三个部分能让你明白了解数据来源的重要性(数据是如何创建或收集的)。虽然现代机器学习方法能够通过省略变量来处理内生性,但是同时在建立模型之前需要一些头脑风暴。
这些参考资料是:
- 内生性的非技术性指南(博客第一、二部分的基础论文)
- 药品详情:市场研究杂志
- 用户评论对电影表现的影响:市场营销科学
- 用 copulas 处理内生性:营销科学
- 税收优惠券:管理科学
原载于 2019 年 6 月 2 日 https://medium.com**的 。
英国的能源——能源性能证书分析
Photo by Heidi Sandstrom. on Unsplash
自从我发表了关于伦敦智能电表和可能的数据分析的文章后,我定期收到人们的消息,他们有兴趣将智能电表数据和家用监视器的能效联系起来。我在本文中写道没有直接的方法将智能电表数据、acorn 和家庭能效联系起来,但是围绕家庭能源和能效的其他数据集仍然有一些有趣的事情要做。
很久以来,我一直想写一篇关于我在以前的工作中测试过的有趣工具的文章,这就是来自数据库的 DSS,它对于处理数据的人来说非常有趣。在这篇文章中,我将介绍这个工具和其他数据集。
什么是大台库 DSS
Dataiku DSS 是由法国公司 Dataiku 开发的产品,在其网站上被定义为**“数据科学家、数据分析师和工程师团队的协作数据科学软件平台,以更有效地探索、原型化、构建和交付他们自己的数据产品”**。
简而言之,它是一个简化公司中数据/模型处理和共享的工具,我真的邀请你去看看他们的网站,那里描述了许多商业案例和平台的功能。但是需要知道的是平台有两种版本:
- 免费版:我在我的机器上托管的这个项目中使用的(但是有一个版本也可以在 dataiku 服务器上免费托管)
- 企业版:提供更多的数据连接器(Hive 等)和更少的限制,网站上没有公布价格,因为我认为这是基于客户的需求,但我听说它并不便宜。但是有一个 2 周的免费试用来测试这项服务。
免费版的安装非常简单,你可以在所有可能的操作系统上安装它。在展示完数据后,我将深入研究该软件的功能。
描述数据
对于这个项目,我将使用以下数据源:
- EPC:这是英国的多份业绩证书的集合(大约 1500 万)
- Nomis data :该网站收集了不同人口普查期间(最近一次是 2011 年)在英国收集的多种信息
让我们看一下数据集的更详细描述。
经济和计划委员会(Economic and Planning Council)
首先,什么是 EPC?这很简单,它是一个家庭的能源性能评级,下图中有一个例子。
例如,它就像可以在设备上找到的那个,并且它需要在每个新租户或房东处被刷新。住房、社区和地方政府部开放的数据源非常完整(超过 1500 万份证书),并且不仅仅是对这些数据的简单评级(数据字典),还有关于玻璃、能耗和建筑面积等信息。
Nomis 数据
Nomis 网站“是国家统计局提供的一项服务,免费提供来自官方来源的最详细和最新的联合王国劳动力市场统计数据”,该网站上有在不同人口普查期间收集的关于联合王国公民的多种信息。有很多详细的信息,人口普查的数据主要用于创建我在关于智能电表的文章中定义的 ACORN 组。
从这个门户网站上,我提取了英国公民在地区层面上的信息:
- 职业
- 资格
- 国家统计-社会经济分类
- 人口
让我们现在就开始吧。
决策支持系统中的数据处理
为此项目提取的所有数据都是 csv 文件,下面的动画演示了在 DSS 中创建数据集的过程。
在 DSS 中可以很容易地拖放文件,你可以对数据、质量等有一个总体的了解。在免费版本中,也可以连接 SQL 数据库,非常简单。
对于这个项目,想法是连接来自 EPC 的数据和 Nomis 数据,因此有很大一部分证书处理要在地区级汇总并连接到 Nomis 数据。
有一个完整过程的概述
下图显示了准备 EPC 数据集的流程,该数据集可与 Nomis 数据连接。
有一个准备阶段(使用画笔),其中有一个正确的列选择,处理邮政编码以获得区号。
与上一步一样,第二步是准备数据集,以找到家庭的最新 EPC(检查日期的正确格式)。
为了获得数据集中每户家庭的最后检查日期,有一个 group by(过程图像上的正方形三角形圆圈图标)函数,下面的动画中有一个过程的图示。
现在我们已经清理了 EPC,并在另一个表中列出了每个家庭的最后检查日期,这两个数据集之间用连接函数进行了连接(流程中的连接标志),现在有一个连接菜单的显示,您可以在其中选择连接键和所选的列。
最后,根据每个地区的功能、家庭类型、供暖系统类型和 EPC 等级进行分组。
最后一步是使用 python 脚本,通过 pandas 的 pivot 函数(每个等级的 EPC 计数和家庭类型)获得地区级别的汇总信息,但我也可以使用 DSS 的 pivot 函数。
瞧,我们有了汇总到区一级的 EPC 数据,这让我们了解了这一级别的家庭评级和家庭类型。
使用 join 函数可以很容易地将这些数据与 Nomis 的数据连接起来。
我使用了 DSS 的一些内部函数来执行 join,groupby,但是我可以使用:
- 构建数据集的 python 或 R 脚本
- 如果是 SQL 表,则为 SQL 脚本
- “大数据”配置中的 Hive 或 impala
现在数据可用了,让我们做一个分析和一个仪表板来总结一些结果。
决策支持系统中的数据分析
这些分析将会是超高水平的,这只是为了展示 DSS 的特性。
这是我用 DSS 制作的仪表板的动画。
这个仪表板的构建要比 Tableau 容易得多(我的观点),但是他们共享这种拖放方法来构建每个非常有用的图表。
有一种方法可以使用实验室工具直接从数据集进行一些分析,其中可以使用内部函数(例如确定相关性)或使用一些脚本来分析数据,在这种情况下,我选择 python 来使用 seaborn 进行一些绘图。
分析部分真的很酷,我认为它可以满足很多需求,但另一个令人印象深刻的部分是基于处理的数据建立模型的 ML 部分。
决策支持系统中的模型服务
因此,构建模型有多种方法,但首先让我们定义这一部分的目的:
“根据位置、总建筑面积和家庭供暖类型建立一个能量等级估算器”
DSS 为您提供了三个“级别”来构建模型,但老实说,这是相同的界面,只是您在模型配置部分的旅程是从菜单的更高层次开始的。
在构建模型的菜单中,可以:
- 准备培训和测试集
- 选择评估指标
- 拿起功能
- 选择网格搜索的模型和参数
- 在测试部分之后比较模型
有一个简单的动画概述了模型构建器的特性。
有趣的是,您可以使用预构建的函数(我假设 scikit 学习函数),或者编写自己的 python 代码。就过程和结果的可视化而言,测试模型的工具确实令人印象深刻。
很好的一点是可视化的结果,例如一个非常容易理解的决策树(用这个工具分解)。
在你找到合适的模型后,会有一个 API 生成器来嵌入这个模型。在这个空间中,您可以定义一些测试请求来查看运行中的模型。
为了展示这个模型,免费版没有激活这个特性(或者我错过了使用它),但是它看起来非常直观。
现在是总结的时候了。
对项目的反馈
我将强烈推荐在能源领域工作/感兴趣的人去钻研 EPC 数据,因为他们是英国房产市场知识的一个很好的来源。总的来说,对我这个最法国的外国人来说,这么说很痛苦,但英国政府在收集和共享数据方面做得很好,政府平台上有非常有趣的数据集可供数据科学家使用(在法国,我们在这个问题上起步很晚,但事情正在慢慢改变)。
对于 Dataiku DSS 来说,它是一个伟大的工具,无论数据科学家是否试验过,我都能感觉到这个工具是由数据科学家为数据科学家设计的,有太多的功能我没有使用过,比如所有的协作部分,深度学习等等。有多个内部功能可以使数据处理更容易,这真的很酷,但如果 Dataiku 决定放弃这些功能(或使其成为高级功能),如果数据人员不知道如何加入 groupby 等,数据管道传输可能会很困难,但我真的很喜欢 Dataiku 没有让用户使用他们的内部功能,而是让其他方法操作数据的可能性(例如 SQL)成为可能。
在我的日常工作中,DSS 能满足我的需求吗?没有,因为我目前有多种工具可供我使用来完成我的工作,我需要在数据端和开发端的灵活性来试验和部署东西,但这个工具绝对值得尝试,因为它可以满足没有我的需求的数据团队的需求(他们在世界各地有很多)。
最初发表于 的古怪数据家伙 。
比特币挖矿和人工智能的海量计算
走向数据科学
能源(各种形式)就是金钱!另一种方式是智能节能,并在加密活跃的世界和不断增长的人工智能部署中应对能源挑战。
Energy-Smart Bitcoin Mining by Stephane Bilodeau
Energy-Smart Massive Computing for Bitcoin Mining and AI in TowardsDataScience by Stephane Bilodeau
为什么比特币消耗这么大?
这应该被认为是原始的,秘密矿井目前产生大量的热量,然后在许多工业、农业、建筑等领域排放它们。燃烧丰富的化石燃料来产生热量。密码可以非常有效地将电能转化为大量热能。因此,明智地使用,数据挖掘可以更有效,因为取暖费用可以抵消。
区块链还将允许许多数据库操作从昂贵的位置分布到远程位置,不仅提供能源效益。
Photo by Dmitry Moraine on Unsplash
为了创造比特币,数以吨计的服务器,即“采矿机器”,被连接到加密货币网络。他们负责检查兑换比特币的人所做的交易。因此,连接到该系统的计算机的总功率(和消耗)解释了该系统疯狂的碳足迹。
比特币网络确实存在能耗的问题。尽管每年处理不到 1 亿笔金融交易,但它消耗大量能源。相比之下,传统金融业每年处理 5000 亿笔交易。如果我们考虑数据中心使用的能源量,比特币每笔交易消耗的能源比世界上所有银行消耗的总和还要多。
对计算能力的高需求
开采加密货币比开采黄金消耗更多的能源也就不足为奇了。众所周知的研究,尤其是去年年底发表在《自然》杂志上的一篇文章已经证实了这一点。
对于所产生的美元价值,计算机密集型的“开采”数字货币的过程比物理开采黄金、铂或稀土金属消耗更多的能源。《自然》杂志上的这项研究对开采加密货币的能源和碳成本进行了有趣的量化。只有铝矿的每美元消耗量超过了主要加密货币的消耗量。
Credit: Quantification of energy and carbon costs for mining cryptocurrencies by Krause and Tolaymat published in Nature 2018
开采贵金属是一项艰苦的工作,需要大型机器、大型熔炉和苛刻的化学物质来提取像黄金和铜这样的资源。在加密挖掘方面,大量的“虚拟”工作用于挖掘数字货币。许多数字货币——比特币是最著名的例子——是通过让计算机研究越来越难的数学问题而产生的。
随着比特币等数字货币越来越受欢迎,能源需求只会上升——这不仅仅是因为对采矿的兴趣增加。人们担心的不仅仅是消耗的能源数量,还有这种能源所代表的导致气候变暖的碳排放量。
回想一下,需要大的计算能力,既要管理比特币的流动,也要挖矿。这种加密货币的良好运行因此爆炸了功耗。科学杂志 Joule 的另一项研究也估计,用于比特币的电力每年将产生 2200 万吨二氧化碳,这一数字与约旦或斯里兰卡等国家的数字非常接近。加密货币对环境有什么影响?对于这个问题,剑桥大学试图通过提出剑桥比特币电力消耗指数工具来部分回答。
剑桥大学的工具模拟了世界各地比特币矿工的操作。它使用的平均电力成本约为每千瓦时 0.05 英镑,以及比特币网络的能源需求。最后,假设世界上所有的比特币挖矿机都以不同的效率运行。
Cambridge Bitcoin Electricity Consumption Index
后者每三十秒显示一次与比特币相关的全球消费。在撰写这些文章时,star 加密货币将消耗 7.23 千兆瓦,每年总消耗估计为 61.88 太瓦时(太瓦/小时)……超过瑞士、科威特、希腊或阿尔及利亚,但略低于捷克共和国或奥地利。
大规模计算:人工智能也受到关注
去年,OpenAI 发表了一份分析,记录了过去六年计算能力的爆炸,这推动了人工智能的进步。这篇文章发现,自 2012 年以来,最大规模的人工智能训练中使用的计算能力每 3.5 个月翻一倍。
下面的图表显示了用于训练选定结果的总计算量,这些结果相对而言是众所周知的,使用了大量的计算时间,并提供了足够的信息来估计所使用的计算。一个 petaflop/s-day (pfs-day)由一天每秒执行 1015 次神经网络运算组成,或总共约 1020 次运算。这个计算时间参数是一种心理上的便利,类似于能量的 kW-hr。顺便说一下,能耗往往会随着千万亿次浮点运算成比例增加…
Credit: Dario Amodei & Danny Hernandez, openai.com
计算的改进一直是人工智能进步的关键组成部分,所以只要这一趋势继续,就值得为远远超出今天能力的系统的影响做好准备。但人工智能和机器学习的能源供应也需要改善。
海量计算:人工智能也可以成为解决方案的一部分
如果没有很好的组织,人工智能将产生与使用能源的加密货币完全相同的问题,但甚至更大。
AI 使用相同或相似的芯片技术,因此每个设备产生的热量相似。简单地消除产生的热量或者更糟的是通过使用空调来消除热量是一个可怕的原始解决方案。在部署第一代这些技术之后,当前这种去除免费但不需要的能量的方法应该会消失。如果不是这样,与我们将面临的功耗和人工智能、机器学习、预测分析所需的所有连接对象的散热量相比,我们看到的加密活性物将是很小的。
因为,所有的能源都是物有所值的,散热应该作为一种能源而不是纯粹的能源浪费。
绿化数字化运营
因此,让我们将能源挑战转化为机遇,提出解决方案。包括人工智能、可再生能源和混合储能在内的清洁技术的结合提供了很好的选择。利用采矿作业产生的废能回收和热量转换来提供能源。在以前的文章中讨论了许多选项:
加密货币能源范式的必要变革。
medium.com](https://medium.com/@smbilodeau/to-be-green-or-not-to-be-that-is-the-crypto-question-536f302f4498) [## 组合效应对抗蝴蝶效应
人工智能促进储能,为可再生能源提供弹性和效率…
towardsdatascience.com](/the-combo-effect-against-the-butterfly-effect-bf7dd7a507a0)
当政府间气候变化专门委员会( IPCC )不断重复呼吁面对全球变暖要谨慎时,这种耗能技术的使用显然受到质疑。
设想一种基于“空间证明/时间证明”安全性的“绿色”加密货币的解决方案已经存在。因此,质疑公共当局参与开发这些绿色技术是有意义的。
所以,让我们现在就开始改进,趁这个领域还处于起步阶段。反过来,这将绝对有助于利用我们看到的人工智能、机器学习、预测分析等越来越多的使用即将面临的能源挑战。
Photo by Bruna Fiscuk on Unsplash
作者,夏羽·比洛多,ing。是清洁技术领域的企业家,致力于创新,尤其是在储能、可再生能源和人工智能领域。他是 Smart Phases ( Novacab )的创始人兼首席技术官、 Engineers Canada 的研究员以及 Energy Central 和 Medium 的专家撰稿人。
你也可以看看 TowardsDataScience 和 The Startup 中的其他文章,主要讨论能源和人工智能,或者颠覆性融资,包括新的脸书天秤座加密货币和其他区块链技术:
我们如何利用颠覆性技术和前瞻性领导来应对颠覆性气候
medium.com](https://medium.com/swlh/an-eye-for-an-eye-a-disruption-for-a-disruption-141b317ac52b) [## 绿色还是绿色,这是一个秘密问题!
加密货币能源范式的必要变革。
medium.com](https://medium.com/@smbilodeau/to-be-green-or-not-to-be-that-is-the-crypto-question-536f302f4498) [## 人工智能,服务于能源转型?
虽然人工智能在能源领域已经取得了多年的进展,但许多人担心有一天…
www.energycentral.com](https://www.energycentral.com/c/cp/artificial-intelligence-serving-energy-transition) [## 人工智能和储能将从哪些方面改变能源领域?(第二部分)
全球能源行业在生产、销售和分配能源的方式上面临着根本性的变化。还有一些…
www.energycentral.com](https://www.energycentral.com/c/gn/what-ways-will-artificial-intelligence-and-energy-storage-change-energy-sector) [## 没有智能储能,就没有快速(足够)的能量转换!
人工智能和储能 fast 需要强耦合,以便与可再生能源实现高效过渡,从而对抗狂暴的…
towardsdatascience.com](/no-fast-enough-energy-transition-without-intelligent-energy-storage-72cfbd3c096b)
ENet——一种用于实时语义分割的深度神经架构
论文摘要
Fig 1. A conversation between a semantic segmented guy and a toon
这是一篇论文的论文摘要: ENet:一种用于实时语义分割的深度神经网络架构 by亚当·帕兹克 论文:https://arxiv.org/abs/1606.02147
概观
ENet(高效神经网络) 提供实时执行逐像素语义分割的能力。ENet 的速度提高了 18 倍,所需的 FLOPs 减少了 75 倍,参数减少了 79 倍,精度与现有模型相当或更高。在 CamVid、CityScapes 和 SUN 数据集上进行了测试。
Fig 2. Semantic Segmentations using ENet
方法
Fig 3. ENet Architecture
以上是完整的网络架构。
它分为几个阶段,如表中的水平线和每个块名后的第一个数字所示。
报告输入图像分辨率为512 * 512
时的输出尺寸
Fig 4. Each module of ENet in detail
的直观表示:初始块如 (a)
- 所示,瓶颈块如 (b) 所示
每个瓶颈模块由:
- 1x1 投影降维
-一个主卷积层(*conv*
)(要么— 正则 , 扩张 或 满*)(3x 3)*
-1x1 扩展
如果瓶颈是缩减采样,则最大池图层将被添加到主分支。此外,第一个 1x1 投影被替换为跨距=2 的 2x2 卷积。
它们将激活填充为零,以匹配特征映射的数量。
conv
有时是非对称卷积,即一系列5 * 1
和1 * 5
卷积。
对于regularizer
,他们使用空间落差 :
-瓶颈前带p = 0.01
2.0
-瓶颈后带p = 0.1
所以,
- 阶段 1,2,3— 编码器 —由 5 个瓶颈块组成(除了阶段 3 不下采样)。
- 阶段 4,5— 解码器 —阶段 4 包含 3 个瓶颈,阶段 5 包含 2 个瓶颈
- 后跟一个
fullconv
,它输出维度为C * 512 * 512
的最终输出,其中C
是滤波器的数量。
还有几个事实:
-他们没有在任何投影中使用偏差项
-在每个卷积层和激活之间,他们使用批量归一化
-在解码器中,MaxPooling 被替换为 MaxUnpooling
-在解码器中,Padding 被替换为没有偏差的空间卷积
-在最后一个 ( 5.0 )上采样模块
中没有使用池索引-网络的最后一个模块是完全卷积,它单独
-每个分支都有一个空间落差,第一阶段为p = 0.01
,随后阶段为p = 0.1
。
结果
使用 SegNet[2]作为基准,在
- CamVid (道路场景)
- 城市风景(道路场景)
- SUN RGB-D (室内场景)
上对 ENet 的性能进行基准测试,因为它是最快的分割模型之一。使用 cuDNN 后端使用 Torch7 库。
推理速度是使用 NVIDIA Titan X GPU 以及在 NVIDIA TX1 嵌入式系统模块上记录的。输入图像大小为 640x360 时,达到 10fps 以上。
Fig 5. Inference Time Comparison on the two different GPUs with SegNet as the baseline
Fig 6. Hardware requirements for SegNet and ENet
基准
用于亚当 。 ENet 收敛速度非常快,在每个数据集上,使用 4 个 Titan X GPUs 的训练只需要大约 3-6 个小时。
分两个阶段执行:
-首先,他们训练编码器对输入图像的降采样区域进行分类。
-然后追加解码器,训练网络执行上采样和逐像素分类。
学习速率— 5e-4
L2 重量衰减 2e-4
批量大小 10
自定义等级称重方案定义为
Fig 7. The formula for the custom class weighing scheme
其中 c = 1.02
并且类权重被限制在***【1,50】***的区间内
Fig 8. Performance on CityScapes Dataset
Fig 9. Performance on CamVid Dataset
Fig 10. Performance on SUN RGB-D dataset
参考
- A. Paszke,A. Chaurasia,S. Kim 和 E. Culurciello。Enet:用于实时语义分割的深度神经网络架构。arXiv 预印本 arXiv:1606.02147,2016。
- V. Badrinarayanan,A. Kendall,R. Cipolla,“Segnet:用于图像分割的深度卷积编码器-解码器架构”,arXiv 预印本 arXiv:1511.00561,2015 年。
我最近也转载了这篇论文,可以在这里找到:https://github . com/iArunava/ENet-Real-Time-Semantic-Segmentation
感谢阅读!一定要看报纸!
如果我发现其他有趣的见解,我会更新的!
对行走分数算法进行逆向工程
使用机器学习建立可行走性评分
Heatmap of Predicted Walk Scores throughout Seattle, WA
我住在西雅图,最近搬到了另一个社区。根据步行评分的专有算法,我从西雅图最适合步行社区的第 9 名升至第 30 名。我仍然可以很容易地走到当地的咖啡店和理发店,但仅此而已!我可以说,我已经搬到了一个不太适合步行的社区,但不清楚如何量化这个数值,也不清楚步行性得分是多少。
我之前使用过步行评分 API 作为预测电动滑板车位置聚类的数据源。Walk Score 是一个网站,它获取一个地址,并使用专有算法和各种数据流在 0-100 的范围内计算其可步行性。
作为一个喜欢步行到达目的地的人(还有徒步旅行!),我很好奇是什么推动了这些产生可步行性分数的专有算法。我开始提出以下问题:
- 专有的 Walk Score 算法可以被逆向工程吗?
2.在构建可行走性评分时,哪些特征是重要的?
为了回答这些问题,我通过收集一组不同的粒度西雅图市数据和步行分数 API 数据来建立一个自定义数据集,以训练机器学习模型来预测步行分数。我能够训练一个模型,在测试集数据上达到 0.95 的 R。r 平方解释了特征集的方差在多大程度上解释了行走分数的方差。
本质上,我能够逆向工程的步行评分方法,并重新创建专有的算法,他们的步行评分。对于一个位置的步行性来说,最重要的特征是 1000 米范围内的餐馆数量、人口普查区域内的人口密度、1000 米范围内的超市数量以及与最近的商业区的距离(以米为单位)。
数据源和机器学习管道
The Full Data Pipeline for Reverse Engineering the Walk Score Methodology
数据
我从随机生成大西雅图地区的经纬度坐标开始。有了大约 7800 个唯一地理位置的列表后,我利用 Walk Score API 返回每个唯一地理位置的可步行性分数。然后,我开始收集反映一个地点周围区域可步行性的数据。
数据来源:
- OSMnx : Python 包,允许您从 OpenStreetMap 的 API 下载空间几何图形和模型、投影、可视化和分析街道网络。
- 步行评分 API :返回任何地点的步行性评分。
- LocationIQ API :附近兴趣点(PoI) API 返回给定坐标周围指定的 PoI 或地点。
- 西雅图城市分区:分区指定使用类别(例如,独栋住宅、多户住宅、商业、工业等。)
- 西雅图人口普查数据:为人口普查区和地理标识符内的人口普查区提供人口和平方英里面积
- 美国人口普查地理编码器 API :对于给定的地理位置,API 返回人口普查区域和唯一的地理标识符。这对于正确合并分区和普查数据至关重要。
特征工程
由于 LocationIQ API 每日请求的限制,我将数据收集阶段延长了两周。这给我留下了大约 7800 个独特的地理位置,然后我设计了 27 个特征来训练机器学习模型,以预测整个西雅图的步行能力。
Full Feature Set
这些功能分为四类:
- 基于便利设施:指定半径内的公交车站、公园、餐馆、学校和所有便利设施的数量(大多数便利设施使用 1000 米半径)
2)普查得出:分区类别和人口密度
Geolocation Observations Grouped by Zoning Category
3)基于距离:邻近最近的高速公路、最近的主要道路、最近的次要道路、最近的住宅道路、最近的工业分区
4)步行网络结构:交叉口数、平均迂回度、平均街道长度、每个节点平均街道数
A single geolocation plotted on top of the OSMnx library for Walk Network Structure feature generation
模型开发
我训练了三个机器学习模型:随机森林回归、梯度推进回归和极端梯度推进回归。我用收集到的三分之二的数据来训练这些模型,剩下的三分之一用于测试。
Extreme gradient boosting model predictions (R² of 0.95) of Walk Score against the one-third test set.
极端梯度推进回归在预测步行分数方面做得很好,在三分之一测试集(约 2300 个样本)上实现了 0.95 的 R 。该模型具有最佳的测试集精度(RMSE)。
黑盒子里有什么?
对 Walk Score 方法进行逆向工程的目的是为了了解算法中的关键特征。我们想知道是什么让一个地方适合步行,而不仅仅是分数!通过检查极端梯度增强模型的特征重要性,1000 米内的餐馆数量占主导地位,是最重要的特征。其他重要的模型特征包括给定人口普查区域的人口密度、所有便利设施的数量、1000 米范围内超市的数量以及与最近商业区的距离(以米为单位)。
The number of restaurants, supermarkets and total amenities within 1000 meters, population density and proximity to commercial zoning are the most important features for predicting a location’s Walk Score.
结论
步行分数已经是一个有用的决策工具,可以根据步行、骑自行车和交通偏好来决定在哪里居住和开发房地产。现在,了解 Walk Score 方法以及构建算法的功能是很有帮助的。我们现在知道了根据步行分数使一个位置可步行的输入。
当前的模型是在西雅图市范围内训练的,那里的城市特征是相似的。还可以收集其他特征来增强步行评分的预测能力,例如地形测量和最近舒适距离计算。模型预测可以很容易地扩展到其他领域,因为 Walk Score API 和用于生成要素的基础数据源(美国人口普查、OSMnx、城市分区、LocationIQ)可以广泛使用。
密码
该项目的代码可以在我的 GitHub 上找到
评论或提问?请发电子邮件至:perryrjohnson7@gmail.com 或联系LinkedIn
工程遇到数据科学——如何平衡数据科学和敏捷之间的紧张关系
几个月前,我开始管理一个关于机器学习和深度学习的研究小组。
Photo by Austin Neill on Unsplash
我有很多管理开发和工程团队的经验,但这是我第一次接触这些主题、算法和工作方法。
一方面,我有一个研究团队,他们说需要时间来调查问题和阅读更多的材料,无法承诺日期和准确性水平。
另一方面,我让我的初创公司的管理层询问时间表、结果和承诺。
这是我的第一份流程草案,我认为这是正确的,可以满足研究团队的需求和管理层的需求,在某种程度上是中间的:
- 产品定义问题并创建描述需求的用户故事。
- 研究团队阅读相关文章,并寻找其他数据科学团队面临的类似问题。
- 数据团队收集相关数据,需要有人(内部或外部)标注。
- 该团队尝试了一些算法和方法,并返回了关于结果的输入——例如,在我们的案例中,我们展示了一个混淆矩阵,展示了错误发生的位置,这允许我们对混淆的区域采取行动。
- 在这里,我们可以对产品进行一些迭代,有时会对问题的定义进行一点点修改,这样不会损害特性,并提高成功率。
- 一旦我们有了足够好的结果,我们就开发一个 API 和一个特性。我们称之为生产化。
这个过程定义了步骤,但是仍然存在关于承诺时间表和承诺准确性的问题。
我向几个脸书小组介绍了我的过程和我仍然面临的公开问题,还组织了一次圆桌会议,听取工程经理管理一个研究团队和几个研究团队经理的意见。
以下是与会者提出的一些观点:
-
在我工作的地方,有一个额外的 POC 阶段,客户与我们试图解决的问题相关。我们将对他们的数据运行算法,然后根据结果改进模型。
-
在我的案例中,改进模型需要处理数据本身,添加特性并改进现有的特性。当涉及到客户时,我们需要他们的输入,同样,我们会添加我们以前没有想到的功能,或者我们会修改模型。
-
关于时间表。在 POC 结束之前,时间表是按周定义的:一周测试模型,一周测试原型,一周在两个不同的客户机上运行。在我的领域(安全),有时我们需要改变整个问题的定义并重新开始。一旦 POC 成功,也就是说客户认可了它,模型就交给另一个团队进行开发。
-
我们增加了一个回顾阶段,研究人员必须致力于目标,如果他们没有满足自己的期限,他们会进行回顾,以了解什么可行,什么不可行。
-
我们了解到,最重要的是设定非常准确和具体的目标,并在中间设置检查点。
-
部署到生产环境并不是流程的结束。我们需要矩阵来跟踪算法的性能。我们需要警报。额外的迭代来改善结果。添加更多数据。有时候会有死胡同,我们要重新开始。
-
我认为管理数据科学项目最困难的事情之一是时间表的不确定性和任务成功的可能性。信任和开放的沟通渠道对于克服可能的障碍至关重要。有些人可能不同意我的观点,但是我不太相信研究项目的时间估计。当我觉得这是我的经理需要的东西时,我已经这样做了(同时添加了所有需要的免责声明并反映了所有可能的失败点)。在我目前的工作场所,我根本不这么做。我对时间表有自己的预期,但当它需要更长时间时,我总是可以解释原因:我们发现了我们不知道的事情,基线比我们想象的更差,任务更难。这又回到了信任。我可以把工作分成几个冲刺阶段,但这是人为的,对我没有任何好处。现在对我来说重要的是传达项目的所有依赖项、步骤以及不确定性。如果经理是一个因不确定性而感到压力的人,数据科学家可以少交流,更努力地工作,希望它足够好,这正是事情出错的地方。矩阵和 KPI 需要在检查可能性和满足业务需求后与研究团队一起设置。这听起来微不足道,但我不知道有多少次我听到一位同事说——我需要达到 90%的准确率,因为我的经理(不是数据科学家)将它作为一个目标,而没有问一些重要的问题。如果你只达到 85%会发生什么?有没有非算法的方法来弥补缺失的 5%?基线是什么?算法是什么?目前精度如何?90%是合理的目标吗?如果这比达到 85%要困难得多,那么这种努力值得吗?矩阵也应该迭代设置。我们可以有一个最终目标,但是它应该对研究过程中出现的事情有一定的灵活性。
-
我们的一些工作使用了敏捷方法。我们做了很多调整,并正在学习如何做对我们自己。我们每周举行两次全队“站立”活动,并为团队设立了一个特雷罗董事会。我们一起计划并进行回顾。这使我们更加透明,并改善团队中的合作和互助。在一个团队中工作,每个人都知道其他成员在做什么,这对团队中的每个人都是有价值的。在数据科学中,进步不是线性的。但是,设定短期目标仍然很重要,特别是对于该领域的初级人员,这样我们就有了方向感,知道自己在做什么,要去哪里。与客户一起试验也很重要,这样我们就知道一个特性如何满足业务需求,以及它是否为客户解决了正确的问题。
-
以类似于管理程序员的方式管理数据科学家非常重要。数据科学家必须了解业务需求,包括时间表。他们应该能够向他们的经理解释与其工作相关的所有必要的技术资料。这里面没有什么你作为管理者不应该能够理解的。数据科学家应该愿意编码(至少一点点),以减少对开发人员、开发/运营等的依赖。虽然研究任务有许多未知因素,并且很难承诺一个截止日期和具体结果,但设定明确的里程碑并将其分解为短期任务是至关重要的,这些任务有明确的 DoD,可以在 sprints 中管理。
样本任务可以包括导入数据、清理数据源、进行单个实验等。我建议在短时间内监控 KPI,作为 sprint 的一部分,这样你就可以在信息完全透明的情况下做出管理决策,而不是设定一个 3 个月的 KPI 目标并努力实现它。
如果在研究中有几个方向可以选择,我建议为每个选择设定一个时间框架,然后回顾在此期间取得的成果。作为管理者,你应该能够决定停止一个研究方向,转向另一个方向。有时候,为了整个项目的利益,你必须做出艰难的决定,违背研究人员的意愿,开辟一条非生产性的研究道路。和给数据科学家的一些建议:
你能帮助你的经理成功(并让自己得到赏识)的最好办法是:
*学会清楚地解释你的工作,以便你的经理能做出好的商业决策。学习如何处理截止日期和短期里程碑。这会让你更有效率,实际上你会获得更多。
*如果你觉得某条研究路线行不通,尽快告诉你的经理。这并不意味着你失败了——这只是意味着一条研究道路走进了死胡同,这就是研究工作的方式。 -
处理里程碑和短期任务,并全程监控 KPI,这些都非常重要。对于研究人员和数据科学家来说,唯一真正困难的事情是提前计划太多,并给出非常准确的时间估计。
我们仍然应该能够给出时间表,但是经理应该灵活,并且理解计划可能会在项目的整个生命周期中频繁地改变,因为,例如,有时整个建模方法需要改变,或者数据不符合预期。
此外,确保数据科学家在短周期内工作也很重要,不要消失在几个月的研究中,而是在整个过程中与客户/产品进行一些互动。
最好的方法是尽快交付最简单的概念验证,与客户一起测试,然后根据客户的反馈进行调整或构建更复杂的内容 -
我作为数据科学家的两点意见:是的,数据科学团队和数据科学家应该使用时间表。无结构的工作环境对每个人都不利。然而,请记住,数据科学家的时间表比开发人员的时间表更“软”。分类/预测问题的解决方案可以在不同的层次上实现。简单的解决方案可以在更紧的时间表内用更少的数据实现。复杂的问题需要更多的数据,并且具有更大的不确定性。据此规划研究路线图,根据经验,从低挂的果实开始。作为一名经理,确保你理解对你的产品至关重要的任务中模糊性的重要性,以及你能做些什么来帮助研究。
-
数据科学家应该知道如何编码,因为无知可能会削弱他们。然而,作为一名经理,你要确保你在明智地使用你的工具。有时,将技术工作交给熟练的程序员,让数据科学家从事他们擅长的工作会更有好处。
我自己从所有这些中得出的结论是,尽管传统的敏捷并不是 100%适合数据科学领域,但设定目标和时间表仍然很重要。如果数据科学团队和管理层之间的关系良好,并且双方相互信任,时间表和准确性承诺可以发挥作用,但应被视为软承诺,假设一切都可以回顾、解释和借鉴。
从分享的不同观点中,从与如此多不同的经理和数据科学家的会面中,从倾听他们的意见和他们要说的话中,我学到了很多。我希望读完这篇文章后,你也一样。
我要感谢米莉·库利艾尔、希尔·梅厄、莱娜·卡彭、阿伊莱特·萨奇托、米哈尔·古特曼、达纳·阿韦尔布赫、塔马·阿米蒂、雷切尔·吕德默、因巴尔·罗森斯托克和因巴尔·瑙尔对本次讨论的深刻见解和贡献。
用机器学习增强增强现实
在 AR 应用之上分层 ML 扩展了它们的有用性。
随着增强现实(AR)技术的改进,我们开始看到超越营销和简单可视化的用例。这些包括产品可视化、远程协助、强化学习、质量控制和维护。
苹果的 Measure 是我最喜欢的 AR 应用之一。这是一种用智能手机进行物理测量的简单可靠的方法,它证明了 AR 跟踪已经变得多么强大。
Apple’s Measure in action. The results are accurate within ±1 cm!
在其核心,AR 是由先进的计算机视觉算法驱动的。**但是我们可以做得更多。**通过在核心 AR 技术之上分层机器学习系统,可能的用例范围可以大大扩展。在接下来的部分中,我将向您展示如何操作。
AR 就是计算机视觉。
AR 技术经常使用 SLAM ( 同时定位和映射 ):一种计算机视觉算法,它比较相机帧之间的视觉特征,以便映射和跟踪环境。结合来自智能手机陀螺仪和加速度计的传感器数据,可以实现非常可靠的跟踪。
像苹果、谷歌和 6D.ai 这样的公司都在孜孜不倦地改进这些算法。这意味着,作为开发者,我们将能够使用 AR 不断构建更好、更可靠的应用程序。
6D.ai mapping the world in real time.
你可以把 AR 应用想象成一组技术层。在的核心,AR 框架如 ARKit 和 ARCore 实现了计算机视觉算法来做跟踪和映射。如果使用 Unity3D , AR Foundation 提供了跨多个 AR 框架的统一 API。在边缘,有您的特定应用使用的代码和资产。
A hierarchy of AR technologies, going from the core to the edge.
边缘的计算机视觉。
AR 技术堆栈的核心框架通常提供整洁的额外功能,如图像跟踪、姿势估计,以及最近的 ARKit 3,人物遮挡。这些功能中有许多是由机器学习提供动力的。
但是还有更大的潜力!
事实上,用机器学习来丰富 AR 应用程序是合适的,因为:
- 摄像机一直开着,收集图像数据。
- 在 AR 中跟踪的对象可以可靠地在相机图像中定位。
- AR 可视化是向用户展示信息的一种很好的方式。
Wouldn’t it be nice if this app could learn the difference between rugs, tables, lamps and windows?
每当 AR 应用程序处理可以从环境中收集有用信息的情况时,可以添加一层由机器学习驱动的计算机视觉。
你可能想:
- 📖阅读标牌上的文字。
- 👀检测用户正在查看哪种类型的对象。
- 👫判断一个人穿的是哪件衣服。
- ❌检测物体是否有视觉缺陷或异常。
Auto-filling the inspection report for a damaged sticker in Kanda’s ARC.
在 Kanda ,我们正在使用 AR ( 代号 ARC )进行质量控制和维护的申请。在 AR 框架之上使用定制的机器学习层,该应用可以检测相关资产的视觉缺陷以在检查过程中提供一种“自动填充”。这是为了让一线工人的生活更轻松。
该应用程序利用了被检查的资产已经在 AR 中被跟踪的事实。这提供了从相机帧中精确裁剪对象所需的位置信息。然后,我们使用定制的神经网络来检测裁剪图像中的视觉缺陷。
LEGO Hidden Side using object detection to locate the kit.
今年,乐高发布了他们的隐藏面 AR 主题。附带的 AR 应用程序利用对象检测(使用 Vuforia )来定位相机框架中的乐高套件,并在其上渲染 3D 世界。我认为这很棒!
AR demo from Wikitude and Anyline to read & submit utility meter data.
Anyline 提供了一个 SDK 来做文本识别,可以用在 AR 中。在上面的演示中,AR 被用来轻松地定位和选择实用程序箱。然后,使用 OCR(光学字符识别)自动读取其能耗表。
这比手工输入数据要快得多(也更准确),这也是为什么意大利主要能源供应商爱迪生已经在使用 Anyline 的技术。
解决技术挑战。
用机器学习增强 AR 应用时的一个主要挑战是对低延迟的要求。最常见的情况是,定制的机器学习层需要实时提供信息,因此模型必须在设备的硬件上运行,而不是在云中运行。
有时候,你的问题可以用现有的软件产品来解决,比如 Vuforia 和 Anyline 提供的产品。当你需要一些定制的东西时,你必须弄清楚如何在你想要的应用平台上有效地运行机器学习模型。
Kanda’s ARC being developed in Unity3D.
Kanda 的 AR 应用 go-to 开发平台是 Unity3D 。这是一个游戏引擎,允许高效的 3D 编程和计算机图形。虽然在这个平台上集成了几个边缘机器学习系统,但我很高兴地告诉你,Unity3D 不是为机器学习推理而制造的。
然而,【Unity 的一个新项目(代号Barracuda),正在成为 Unity3D 的一个高效(而且相当易用)的机器学习推理机!
Enter The Matrix - Compute Shaders make matrix multiplications real fast!
梭鱼 使用 计算着色器 在设备硬件上高效运行模型。这些程序运行在显卡上,但是(不同于常规着色器 ) 它们可以用于非图形任务。
为了增加交易的甜头,计算着色器可以利用许多平台上的图形硬件:MacOS、iOS、Android、Windows、PS4、Xbox。这意味着用 Unity3D 进行机器学习推理可以快速且跨平台🤩
总之。
由于过去几年取得的技术进步,AR 应用程序在它们能做什么方面超出了预期。
由于相机图像始终可用,这些应用程序还提供了使用视觉数据丰富体验的独特机会:例如,使应用程序能够阅读文本,找到物体或检测异常。这些功能可以由构建在核心技术之上的定制机器学习层来支持。
我们已经到了这篇文章的结尾。我希望你对如何使用机器学习来增强 AR 应用程序有一些想法,以及在开始时需要注意什么🤓
利用深度多时相超分辨率增强卫星图像
DeepSUM 赢得欧空局的 PROBA-V 超分辨率挑战
Modified ESA Copernicus Sentinel data, licenced under CC BY-SA 3.0 IGO.
“你能增强它吗?
我们多少次从电视节目中听到这种比喻,多少次嘲笑计算机怪胎能够从一幅图像中提取出如此荒谬的细节?
虽然电视节目实现的不可能的图像增强理应受到嘲笑,但实际上存在数学和物理表明有可能提高图像分辨率的场景。多幅图像超分辨率(MISR) 就是这种情况,强调多幅图像的使用。
MISR 拍摄了一幅图像的多个低分辨率(LR)版本,并试图将它们组合起来,以恢复场景的高分辨率(HR)版本的细节。诀窍在于 LR 图像彼此略有不同,并且由于 LR 采集而在一个图像中丢失的信息可能仍然存在于另一个图像中。
下面是一个简单的例子来说明这个原理。假设你有一个真正的高分辨率图像,像素呈棋盘状,但你只能观察到四个低分辨率图像,每个图像的高分辨率像素只有四分之一。然而,还假设第一个 LR 图像在两个方向上从 HR 图像采样奇数索引的像素,第二个 LR 图像垂直地采样偶数,水平地采样奇数,第三个采样奇数/偶数,第四个采样偶数/偶数。无需使用任何魔法,只需从 LR 图像中提取像素并以正确的顺序交错,就可以重建精确的 HR 图像!
Toy example of multi-image super-resolution.
卫星图像是 MISR 大放异彩的最好例子。地球观测卫星,如 PROBA-V 或 Sentinel constellation ,获取同一场景的多幅图像,因为它们在轨道运行期间会定期重访该场景。这是监测环境、评估洪水等自然灾害风险或通过检测一段时间内影响一个地区的变化来了解人类活动和气候变化的影响的一项重要工作。因此,有丰富的多时相数据可用于提取更多信息,而不是查看单个图像。通过 MISR 方法提高空间分辨率有助于为科学家提供更高质量的产品。
然而,总的来说,MISR 比上面的玩具例子要困难得多。特别是,在试图合并多时卫星图像时遇到的主要问题是:
- 未注册图像:现有 LR 图像间的位移难以准确获知;
- 绝对亮度变化:虽然场景可能大致相同,但可能会因光照条件而出现不同;
- 云:它们可能会遮挡部分场景,它们的阴影会影响地形亮度;
- 时间可变性:场景内容因自然现象或人类活动而随时间变化。
Example of temporal variability. PROBA-V challenge dataset. Courtesy: ESA ACT.
PROBA-V MISR 挑战和 DeepSUM
2018 年 11 月 1 日,欧洲航天局开始了一项公开挑战,以增强 PROBA-V 卫星获取的图像。PROBA-V 是一颗小型卫星,专门用于研究地球植被,其数据有助于遏制世界各地沙漠的蔓延和毁林活动。
PROBA-V 获取地面分辨率为 300 米的图像,重访时间为 1 天。然而,它也获取更高分辨率的图像(每像素 100 米),尽管频率较低,大约每五天一幅。为这种情况开发 MISR 算法特别有趣,因为它可以通过利用更频繁的低分辨率图像来帮助提高高分辨率图像的时间可用性。
挑战赛的公共数据集正是为此而建立的。一个场景在一个月内获得的多个低分辨率图像是可用的,并且还提供了该场景的地面实况高分辨率版本。
一场激动人心的比赛开始了,许多团队贡献了他们自己的解决方案。在经历了上周的忙碌和令人难忘的通宵之后,2019 年 6 月 1 日,在 DeepSUM,都灵理工大学图像处理和学习实验室提出的方法成功击败了由蒙特利尔学习算法研究所(MILA)和 Element AI 成员组成的团队 rarefin 的激烈竞争,赢得了挑战。
DeepSUM 是一个端到端的可训练卷积神经网络,它处理大量低分辨率输入图像,以在其输出端提供超分辨率图像。
DeepSUM
在高层次上,网络由三个主要阶段组成:
- SISRNet:该部分独立处理输入图像(通过双三次放大进行预处理),以提取有助于描述其内容和提高其分辨率的特征;
- RegNet:它使用 SISRNet 学习的特征来估计将图像的特征图彼此对齐的过滤器;
- FusionNet:它缓慢地组合所有图像的特征,以生成对双三次上采样和注册输入的平均值的残差校正。
总的来说,DeepSUM 的设计和训练是由明确解决影响我们上面讨论的遥感图像 MISR 的问题的思想来指导的。下面是解决这些问题的方法:
- 未注册的图像 : RegNet 使用网络的特征空间而不是像素空间来解决注册问题。这些特征捕获更高层次的结构,并且对于噪声和其他复杂的扰动更加鲁棒,使得由 RegNet 产生的配准滤波器比直接从像素比较(例如,图像互相关)中导出的滤波器更好。损失函数还考虑到超分辨率图像和高分辨率地面实况可能相互偏移;
- 绝对亮度变化:用于训练网络的损失函数包含使其对全局亮度变化不变的项;
- 云 : DeepSUM 支持使用质量掩码,例如由云检测器计算的二进制掩码,来指导其内部工作。特别地,遮罩用于让网络知道一些图像在哪里具有不可靠的像素,以便可以用来自其他图像的地面内容来填充它们,从而使隐藏层更加稳定。损失函数还考虑了这样一个事实,即如果所有图像在同一区域都有一个掩膜,则无法预测高分辨率地形;
- 时间可变性 : FusionNet 使用小的 3D 卷积来学习过滤器,允许以最鲁棒的方式合并多个图像。
Example from validation set (scene: imgset0103). Top row: 3 low-resolution images. Bottom-left: DeepSUM super-resolved. Bottom-right: ground truth.
今后
虽然多图像超分辨率对于遥感界来说是一个非常重要的问题,正如大量科学文献所证明的那样,但很少有作品使用深度学习来解决它。欧空局的挑战提供的新数据集肯定会引起人们的兴趣,新技术将在未来几个月和几年出现。我们将期待激动人心的新发展。
用动画增强静态图
使用 gganimate 增加 ggplot2 的可视化效果
这篇文章旨在向你介绍使用托马斯·林·彼得森的gganimate
包在 r 语言中制作ggplot2
可视化动画。
如果我按照我在这篇帖子中解释的简单模型来预测(或在澳大利亚称为 tip)AFL 的获胜者,这篇帖子将会可视化我将会获得的理论奖金。分析中使用的数据是从 AFL Tables 网站上收集的,是我写的关于 AFL 人群的一个更大系列的一部分。更广泛的项目可以在这里找到。
library(tidyverse)
library(lubridate)
library(scales)
library(gganimate)# set themes
theme_set(theme_minimal() +
theme(axis.title.x = element_text(size = 16, hjust = 1),
axis.title.y = element_text(size = 16),
axis.text = element_text(size = 13),
plot.title = element_text(size = 19)))# create a colour pallette
colours <- c(“#5EB296”, “#4E9EBA”, “#F29239”, “#C2CE46”, “#FF7A7F”, “#4D4D4D”)# — — — Read in data — — -#
afl <- read.csv("[https://raw.githubusercontent.com/JaseZiv/AFL-Crowd-Analytics/master/data/cleaned_data/afl_model_data.csv](https://raw.githubusercontent.com/JaseZiv/AFL-Crowd-Analytics/master/data/cleaned_data/afl_model_data.csv)", stringsAsFactors = F)# Data pre-processing — — — — — — — — — — — — — — — — — — — — — — — — — — -# make all variables character type to make splitting and string manipulation easier
afl <- afl %>%
mutate_if(is.factor, as.character) %>%
mutate(team1_score = as.numeric(team1_score),
team2_score = as.numeric(team2_score)) %>%
mutate(fav_team = ifelse(AwayLineClose < 0, team2, team1)) %>%
mutate(winning_team = ifelse(winner == “Home”, team1, ifelse(winner == “Away”, team2, “Draw”))) %>%
mutate(fav_win = ifelse(fav_team == winning_team, “Yes”, “No”)) %>%
filter(season >= 2014,
!str_detect(round, “F”)) %>%
mutate(tip = ifelse(abs(AwayLineClose) < 3, team1, fav_team))# function to calculate odds
calculate_odds_available <- function(tip, winning_team, team1, team2, HomeOddsClose, AwayOddsClose) {
if(tip == winning_team) {
odds_available <- ifelse(tip == team1, HomeOddsClose, AwayOddsClose)
} else {
odds_available <- 0
}
}# apply function and calculate returns
afl <- afl %>%
mutate(odds_available = mapply(calculate_odds_available, tip, winning_team, team1, team2, HomeOddsClose, AwayOddsClose),
game_return = odds_available * 10,
game_profit = game_return — 10)# create a df that calculates total winnings per round
money_per_round <- afl %>%
group_by(season, round) %>%
summarise(total_profit = sum(game_profit)) %>% ungroup()# add a round 0, where all seasons start at $0
zeros <- data.frame(season = (2014:2019), round = 0, total_profit = 0)# join zeros df on to money_per_round
money_per_round <- money_per_round %>% rbind(zeros)# create a df that sums up winnings cumulatively
total_money <- money_per_round %>%
arrange(season, round) %>%
group_by(season) %>%
mutate(cumulating_winnings = cumsum(total_profit)) %>% ungroup()
我们开始吧
好的,所以当我写第一篇文章的时候,我采取的第一步是创建一个 ggplot2 可视化来绘制我使用我的简单策略会得到的收益(或损失)。
这是结果:
total_money %>%
ggplot(aes(x= round, y= cumulating_winnings,
group = season, colour = as.character(season))) +
geom_line(size = 1) +
geom_point(size = 2, colour = “black”) +
labs(x= “Round”, y= “Cumulative Wins/Losses”, colour = “Season”) +
scale_x_continuous(limits = c(0, 27),
labels = c(0, 3, 6, 9, 12, 15, 18, 21, 24),
breaks = c(0, 3, 6, 9, 12, 15, 18, 21, 24)) +
scale_colour_manual(values = colours) +
ggtitle(“2017 WOULD’VE BEEN A BAD YEAR”) +
theme(legend.position = “bottom”)
The legend is hard to follow here
还不错,但肯定可以改进。在我看来,随着六季的绘制,这个传说很难映射到这条线本身。还有,除了 2017 赛季特别差,其他赛季轮次之间的变化很难看到。
此外,以这种方式绘制数据,如果不花费太多精力关注我在哪里会赚钱,在哪里会赔钱,就很难实现。
标签和注释
呸——你不能简单地把季节加为标签——你什么都看不到!
total_money %>%
ggplot(aes(x= round, y= cumulating_winnings, **group** = season, colour = **as**.character(season))) +
geom_line(size = 1) +
geom_point(size = 2, colour = "black") +
geom_hline(yintercept = 0, linetype = 2, size = 2) + **# added in horizontal line at $0**
geom_text(aes(label = season), hjust = -1, size = 6) + **# season labels added**
scale_colour_manual(values = colours) +
labs(x= "Round", y= "Cumulative Wins/Losses") + scale_x_continuous(limits = c(0, 27), labels = c(1, 3, 6, 9, 12, 15, 18, 21, 24), breaks = c(1, 3, 6, 9, 12, 15, 18, 21, 24)) + scale_y_continuous(labels = dollar) + **# y-axis formatted to dollar format using scales**
annotate("text", x= 26, y= 6, label = "Break Even $", size = 6) + **# added text to break even line**
ggtitle("2017 WOULD'VE BEEN A BAD YEAR") +
theme(legend.position = "none") **# turned legend off**
That didn’t work!
相反,只有一个季节标签适用,并适用于每条线的运行结束。这个看起来好多了。
如我们所见,静态图表中添加了更多元素,包括:
- 添加盈亏平衡线;
- 将 y 轴格式化为美元格式;和
- 添加标签和移除图例
这大大提高了剧情的可读性。
total_money %>%
ggplot(aes(x= round, y= cumulating_winnings, **group** = season, colour = **as**.character(season))) +
geom_line(size = 1) +
geom_point(size = 2, colour = “black”) +
geom_hline(yintercept = 0, linetype = 2, size = 2) + **# added in horizontal line at $0**
geom_text(data = total_money %>% filter(round == max(round)), aes(label = season), hjust = -0.3, size = 6) + **# season labels added, but only one label per season**
scale_colour_manual(values = colours) +
labs(x= “Round”, y= “Cumulative Wins/Losses”) + scale_x_continuous(limits = c(0, 27), labels = c(1, 3, 6, 9, 12, 15, 18, 21, 24), breaks = c(1, 3, 6, 9, 12, 15, 18, 21, 24)) + scale_y_continuous(labels = dollar) + **# y-axis formatted to dollar format using scales**
annotate(“text”, x= 26, y= 6, label = “Break Even $”, size = 6) + **# added text to break even line**
ggtitle(“2017 WOULD’VE BEEN A BAD YEAR”) +
theme(legend.position = “none”) **# turned legend off**
Looking better…
你好动画!
虽然上面的图表看起来好多了,但没有夸张!
输入来自gganimate
的动画!
使用动画情节允许我们删除更多的元素。通过标签和动画的正确组合,y 轴不再是必要的——在每一轮中,我们可以跟踪我们的输赢,而盈亏平衡线为我们提供了一个参考点。
这里做的其他事情包括使用fps
(每秒帧数)减慢帧的速度,并将transition_reveal()
中的range
调整为比它转换的回合长(即调整范围为c(0,25)
)。这允许动画在完成循环后暂停。
最后,为了增加输出的大小,根据您的喜好调整width
和height
参数。
total_money_anim <- total_money %>%
ggplot(aes(x= round, y= cumulating_winnings, **group** = season, colour = **as**.character(season))) +
geom_line(size = 2) +
geom_point(size = 3, colour = "black") +
geom_hline(yintercept = 0, linetype = 2, size = 2) + geom_text(aes(label = paste0(season, ": ", dollar(cumulating_winnings))), hjust = -0.3, size = 6) + scale_colour_manual(values = colours) +
labs(x= "Round", y= "Cumulative Wins/Losses") + scale_x_continuous(limits = c(0, 27), labels = c(1, 3, 6, 9, 12, 15, 18, 21, 24), breaks = c(1, 3, 6, 9, 12, 15, 18, 21, 24)) + scale_y_continuous(labels = dollar) +
annotate("text", x= 26, y= 6, label = "Break Even $", size = 6) + ggtitle("2017 WOULD'VE BEEN A BAD YEAR") +
theme(legend.position = "none", axis.text.y = element_blank(), axis.title.y = element_blank(), panel.grid.major.y = element_blank(), panel.grid.minor.y = element_blank()) + transition_reveal(round, range = c(0, 25)) animate(total_money_anim, width = 900, height = 900, fps = 5)
Animations make things so much better
希望这给了你一些灵感,让你开始制作一些你自己的动画形象。
如果你有任何问题或建议,请在评论中告诉我。
本帖原创,发布在不怪数据博客【https://www.dontblamethedata.com】
增强交叉熵损失的能力用于图像分类
交叉熵损失已经在许多分类任务中取得了最先进的结果。但是,对于其类具有相似特征的数据集,它不会像预期的那样执行。原因是,交叉熵损失无法学习区分度不够的可分离特征。为了解决这个问题,已经提出了许多方法。一个非常有效的方法是用角距离(边距)来分离学习到的特征,这将在本文中解释。
使用 SoftMax 和交叉熵损失进行训练
首先,让我们在 mnist 数据集中训练一个 VGG16 Cnn 模型(Simonyan & Zisserman,2014)(图 1)。
Figure 1: VGG16 model for Mnist
mnist 数据集中的每个图像都有(28x28x1)个维度,但是我们在 VGG16 网络中只能馈送一个以(32x32x1)为下限的图像。因此,在每个图像的边界添加了额外的零填充,以达到最小输入大小。此外,“通道优先”顺序被证明可以加速 GPU 中的训练,因此维度被转换为(1x32x32)。除了标准的 VGG16 架构之外,我们还可以添加全连接的层,然后使用 SoftMax 为每个类生成最终预测。最后一个完全连接的层也可以用于提取特征。这些特征可以使用 2D 或 3D 欧几里德空间中的 PCA 或 T-SNE 等降维技术来绘制,以提供关于训练的见解。
在 VGG16 训练之后,我们可以使用它的权重在看不见的图像(测试集)中进行预测。测试集中最后一个全连接层(特征)的神经元的输出在下面使用 PCA 在 3D 欧几里得空间中被可视化。
很快有人可以理解,仅使用交叉熵损失的训练不能提供足够的类内紧密度和类间差异,以用于具有相似特征(例如人脸)的数千个类的数据集。
注意当使用 PCA 降低维度时,总方差下降,因此特征的拓扑是近似的。例如,在图 2 中,红点(代表一个类别的要素)之间的距离相对较大。如果它们被投射到 PCA1 中,它们的最终距离将相对较小。因此,红色要素的差异很小,该类的可视化结果也不准确。
Figure 2: Dimensionality reduction using PCA
转换交叉熵损失
变换的思想是将所有特征分布在超球面上,并在每类特征之间增加一个角裕度,以增强它们的区分能力(刘等,2017)。
首先让我们回忆一下交叉熵损失函数:
我们可以重写逻辑
作为第一项加上第二项的内积
其中θ是权重和特征之间的角度。此外,如果我们将权重标准化,将偏差归零
请注意,的最终预测值仅取决于角度θ(刘等,2017)。因此,交叉熵损失函数被转换为
我们应该做的另一个修改是归一化特征向量的范数,以消除径向方向上的变化。因此,特征位于半径为“s”的超球面的表面上,并且学习仅依赖于余弦值来开发辨别能力(王等人,2018)。
通过这种修改的交叉熵损失,我们的网络可以学习用角度边界分隔的特征,但这并不意味着它足够强,可以满足我们最初的假设。因此,我们将在权重和特征之间添加附加的角度裕度损失,以同时增强类内紧凑性和类间差异。已经提出了许多角裕度罚值,但是加性罚值比其他罚值具有更好的几何属性,与超球流形上的测地距离精确对应(邓,郭,薛,& Zafeiriou,2018)。添加余量后的最终转换交叉熵损失如下所示:
在图 3 中示出了修改的整个过程
Figure 3: Visualization of the whole Cross-Entropy loss modification
在最后一个全连接层的测试集中计算的特征在下面使用 PCA 在 3D 欧几里得空间中被可视化。
正如我们所观察到的,由于附加的角度裕度“m”(上面的 0.5),每一类的特征都非常紧凑,并且可以与其他类分开。因此,这种方法适用于包含成千上万个类别的数据集,这些类别的特征不具有很高的区分度。
在 tf.keras 中,我们可以简单地修改 SoftMax 层来实现这种所需的行为。以下是使用两个输入(要素,正确 _ 标签)进行训练的修改后的 SoftMax 图层的代码:
完整的代码可以在我的 github 链接中找到。您可以试验不同的“m”值,以查看其在最终可视化中的影响。
进一步分析
移除特征提取器中的 ReLU
上述层的第一个输入是作为最后一个全连接层的输出的特征。现代 CNN 通常在几乎每一层之后使用 ReLU 非线性,使得输出位于范围[0,+∞]内。如果 ReLU 存在于使用上述交叉熵损失备选方案的网络中的最后一个全连接层之后,则可以放置特征的角度是有限的。从该层的顶部移除 ReLU(Liu 等人,2017)可以有利于特征学习,因为特征可以分布在更宽的空间中。
从另一个角度标准化权重
我们看到权重的归一化给出了更好的几何解释。权重的归一化也可以帮助解决训练数据不平衡的问题,因为同一类中的样本数量越大,与该类相关联的权重的范数就越大。因此,对修正的交叉熵损失中的权重范数进行归一化,解决了这个问题(刘等,2017)。
消除偏见
通过将偏差归零,修正的交叉熵损失具有清晰的几何解释,因此变得更容易分析(刘等人,2017)。通过实验,这对特征分布没有直接影响,并且学习的特征同样强。
参考
邓军,郭军,薛,n .,& Zafeiriou,S. (2018,1)。ArcFace:深度人脸识别的附加角裕度损失。 arXiv 电子版,arXiv:1801.07698。
刘文伟,文,于,李,李,宋,李(2017,4)。用于人脸识别的深度超球面嵌入。 arXiv 电子版,arXiv:1704.08063。
Simonyan,k .,& Zisserman,A. (2014,9)。用于大规模图像识别的非常深的卷积网络。 arXiv 电子印花,arXiv:1409.1556。
王,洪,王,周,钟,季,谢,龚,周。。。刘,W. (2018,1)。CosFace:深度人脸识别的大幅度余弦损失。 arXiv 电子印花,arXiv:1801.09414。
开明的数据实验室笔记本
具有专用存储桶、IAM 权限和安全防火墙配置
通往云计算专业知识的道路充满了令人苦恼的漫长下午,以及无数未完成的博客帖子。当您想要快速启动一个虚拟机,并在浏览器中从笔记本开始使用 Python 时,您经常会遇到这些挫折。为什么我不能写我的桶?是什么阻止笔记本在我的浏览器中工作? 为什么 SSH 进入机器看不到任何文件?甚至可能是“Docker 是什么?”
本文旨在帮助其他数据科学家,他们正在冒险进入谷歌计算平台(“GCP”)堆栈,或者可能在开始新的 Kaggle 竞争时重新访问它。关注我在 Twitter 上的 #FullStackDS 帖子,获取类似的帖子。
DataLab 笔记本是 GCP 的产品,用于从私有环境连接到谷歌计算引擎(“GCE”)实例(或 VM)。它使用了 Jupyter Notebook 的基础设施和 Docker,并且可以链接到 R/Julia/Python/Bash 等中的其他内核。——如果你想变得非常奇特。设置 DataLab 笔记本的技巧在于配置防火墙、IAM 权限以及理解一些独特的 Linux 命令。我还没有找到一篇解决这些复杂问题的文章。一旦你克服了这个困难,旋转起来就非常容易了。让我们通过一个完整的可重复设置来引导您。
设置您的项目
在我们开始之前,如果你还没有在 GCP 设立账户,你会想要的。对于首次用户,有 300 美元的信用和 12 个月的免费试用。要不,我们开始吧https://console.cloud.google.com。
从左上角的“汉堡包菜单”(*看起来像汉堡包),我们将从主菜单中选择主页。接下来,让我们使用主页上的“创建项目”(下面突出显示)。创建项目允许用户在共享环境中创建多个虚拟机。它还允许分开计费。
接下来,从New Project
页面,我们将我们的项目命名为“DistributedScraping ”,并单击CREATE.
在主屏幕上,你应该会在下拉列表中看到项目DistributedScraping
。我们还想从页面的右上方打开免费的云外壳,下面用绿色圈起来——它还没有连接到任何 GCE 实例。请注意,黑色命令行云 Shell 本身是一个免费的 Linux 机器,我们将使用它来设置防火墙、IAM 权限,并从中创建 DataLab 笔记本。
为私人浏览器访问设置防火墙
在启动 DataLab GCE 实例之前,我们需要设置对 localhost 网关的访问,该网关在 GCP 项目中受到保护,并通过安全的 SSH 连接进行链接。默认情况下,DataLab 同时使用端口 8081 和 22。源范围设置为“0.0.0.0/0”,这允许在每次重新启动后链接到您的笔记本—每次启动和停止 DataLab Docker 实例时都会创建一个新的外部 IP。在更高级的设置中,我们可以分配一个固定的外部 IP,并为其分配一个源范围。我们将在下面的 on 命令中建立防火墙规则。
在 Google Cloud Shell 中,在一长行中输入 Github Gist 中的代码。上面的代码突出显示了项目名称和防火墙规则名称。
接下来,我们需要从开放云 Shell 配置我们当前的项目:
启动数据实验室笔记本
下表显示了机器类型名称的成本/尺寸。我们将在这里创建一个 8 核低内存机器,每小时 23 美元。我们可以通过使用另一个 linux 标志来获得更低的可抢占价格——我不会在这里深入讨论。您可以通过访问此链接来选择您自己的机器类型。在我的下一篇文章中,我们将探索来自 DataLab 的高级抓取——包括来自 Python 的并行代理和 selenium 抓取,因此我们希望使用比标准双核机器更大的实例。
在云 Shell 中运行下面的代码,创建您的 DataLab GCE 实例,用一个轻量级的 Docker 容器包装;它包括“us-west1-a”地区的一台 8 CPU/核心计算机,名称为“iptest”:
运行上面的 linux 行之后,我们将看到下面的输出。通过在云外壳中单击,访问用绿色圈出的 enable API 链接。这将启动一个新的浏览器选项卡,并根据您的 GCP 受保护环境提供对新创建的虚拟机的访问。
选择 ENABLE 按钮后,在您的云 Shell 中再次输入相同的命令来创建机器。不知道为什么这需要两步…?
输出应该如下所示,这将传播您的 SSH 密钥并设置您的 GCE DataLab 实例。接下来,单击 Web Preview 按钮(下面用绿色圈出),将端口从“8080”(默认)更改为“8081”,然后选择 LAUNCH AND PREVIEW 链接(步骤如下所示)。
您也可以不使用上述方法从浏览器重定向。只需将下面的链接粘贴到您的浏览器中,或者在云外壳文本的输出中找到它。我发现我必须尝试几次,才能让安全重定向生效。保持耐心!会有用的…
探索您的数据实验室笔记本
选择+NOTEBOOK 按钮(下面用绿色圈起来的),启动一个新的“Untitled Notebook.ipynb”。我们将在下面浏览几个 Hello World 示例,在这些示例中,我将向您展示如何设置对您的 DataLab 存储(“存储桶”)的读/写访问权限,该存储桶是自动创建的,自动备份在与您的虚拟机同名的文件夹下。
运行下面的…
从新启动的选项卡中,我们需要选择右上方的下拉内核,从工作簿中选择 Python3。注意,由于某种原因,当前默认设置为 Python2(?).现在,在键盘上使用Shift + Enter
,从print('hello world')
函数之后的第一个单元格(代码块)开始。瞧,成功了!“你好世界!”。
让我们设置对我们新创建的 bucket‘distributed scrpping’的读/写访问,您可以访问 Hamburger > > Storage > > Browser extension。
在这里,我们需要访问我们的“GCE 电子邮件”,作为“服务帐户”进行绑定。在您的云 Shell 中运行下面的gcloud
命令,手动在文本输出中搜索 serviceAccounts:email ,并在接下来的 Linux 命令中使用。
现在,我们将服务帐户绑定到自动创建的 DataLab bucket。
现在让我们升级游戏,尝试一个高级的 helloworld,w 将一个 CSV 和 TXT 写入我们的桶中,并用 Python 将它们读回到 DataLab 笔记本中。注意,那个“!”在行首,是穿插 Python 代码的单行 bash 脚本(非常酷!).
在浏览器中将以下代码输入到您新创建的笔记本中。
输出:
如果你想扩展这种探索,谷歌有一个很棒的 YouTube 视频[ Datalab:云中的笔记本(AI Adventures) ],它涉及到使用谷歌的 BigSQL 分布式关系数据库产品 BigQuery 进行读/写/管理。
注意:每小时/每天/每周自动备份iptest
存储桶。即使您删除了您的 DataLab 虚拟机,这种情况仍会存在。我不确定我们能在哪里控制这个特性;请注意,这种数据存储是有象征性成本的。
检查虚拟机状态并关闭
从主屏幕左上角的汉堡图标,导航至计算引擎>>虚拟机实例。这里您将看到iptes
,我们的 DataLab GCE 实例正在运行(注意绿色的勾号)。即使您关闭了选项卡,GCE 实例也会继续计费。记住选中机器上带有蓝色箭头的框,然后在每次使用完实例后停止机器(从页面的右上角开始)!
另请注意,每次重启时,您都必须重新安装 Linux 软件包和更新,但您将保留备份在distributedscraping/iptest
桶中的所有数据——可以通过存储> >浏览器扩展从汉堡图标访问这些数据。
确保确认您的实例已停止(灰显)。
停机后重新连接。
在云 Shell 中,启动之后,只需运行下面的 linux 命令来重新启动 DataLab。同样,连接到iptest
后,您可以通过 http://localhost:8081/ 访问笔记本电脑。
datalab connect iptest
如果您出于任何原因需要 SSH 到机器,请在本地机器上使用下面的 from SDK 命令,或者在浏览器中使用 Cloud Shell。注意,您在这里找不到任何文件,因为它们都是通过 project bucket 链接的。这也适用于没有安装 DataLab 笔记本 Docker 映像的一般 GCE 实例——并允许在该上下文中进行ls
探索。
gcloud compute ssh 'iptest' --zone=us-west1-a
--ssh-flag='-D' --ssh-flag='10000' --ssh-flag='-N'
你现在自由了!
Photo by Jünior Rodríguez on Unsplash
感谢您花时间完成本次探索。这应该可以让您开始使用 DataLab 笔记本电脑。我让您深入研究连接到远程实例的细节,为您的特定需求设置权限,并以可重复的方式分享您的发现。像往常一样,使用此处的所有内容需要您自担风险。在下一篇文章中,您将很快成为一名网页抓取忍者。\\
集成学习:来自 1994 年美国人口普查数据库的案例研究
Photo by 3dman_eu via Needpix.
在我们开始理解什么是集成学习以及它是如何工作的之前,我们需要知道在这个案例研究中使用的数据集。
使用的数据集是 1994 年美国人口普查数据库的子集,该数据库由 Barry Becker 提供,可在 UCI 机器学习库获得。该库的预测任务是确定一个 人一年的收入是否超过 50K,为此提供了以下属性和值:
- 高收入:目标阶层。
- 年龄:连续。
- 工作类别:私人,自我雇佣非公司,自我雇佣公司,联邦政府,地方政府,州政府,无薪,从未工作。
- fnlwgt :连续。
- 教育:学士、部分大学、11 年级、HS-grad、Prof-school、Assoc-acdm、Assoc-voc、9 年级、7-8 年级、12 年级、硕士、1-4 年级、10 年级、博士、5-6 年级、学前教育。
- 教育编号:连续。
- 婚姻状况:已婚配偶、离婚、未婚、分居、丧偶、无配偶、已婚配偶。
- 职业:技术支持、工艺修理、其他服务、销售、行政管理、专业、搬运工人、清洁工、机器操作员、检查员、行政文员、农业、渔业、运输、私人服务、保安服务、武装部队。
- 关系:妻子、亲生子女、丈夫、非家庭成员、其他亲属、未婚。
- 种族:白人,亚洲太平洋岛民,美洲印第安爱斯基摩人,其他人,黑人。
- 性别:女,男。
- 资本收益:持续。
- 资本损失:持续。
- 每周小时数:连续。
- 本土国家:美国、柬埔寨、英国、波多黎各、加拿大、德国、美国外围地区(关岛-USVI 等)、印度、日本、希腊、韩国、中国、古巴、伊朗、洪都拉斯、菲律宾、意大利、波兰、牙买加、越南、墨西哥、葡萄牙、爱尔兰、法国、多米尼加共和国、老挝、厄瓜多尔、台湾、海地、哥伦比亚、匈牙利、危地马拉、尼加拉瓜、苏格兰、泰国、南斯拉夫、萨尔瓦多、特立尼达岛&多巴哥、秘鲁、香港、荷兰。
什么是集成学习?
Photo via Verde Valley Nursery
集成学习是一种结合其他机器学习模型来优化和创建更好模型的技术。
有一些不同类型的集合方法,如:助推,堆叠,粘贴,装袋和随机森林。在本案例研究中,重点是最后两种方法。
装袋粘贴:是什么?它是如何工作的?
Photo via Giphy
BootstrapAggregating或 Bagging 是一种结合了自举和聚合方法的技术。第一种方法是用替换将数据集分成 n 个子集,第二种方法的思想是创建 n 个模型,每个子集一个模型,然后将它们聚集在一起以产生最终预测。
粘贴方法的工作原理与装袋相似,但不同之处在于引导步骤,在该步骤中,分割是在没有替换的情况下进行的。
那随机森林呢?
Photo via Dribbble
随机森林是一种特殊类型的集成算法,它使用多个决策树来构建其模型。每棵树都用数据集的不同部分来训练。通常,这种分割与 bagging 技术类似,最终模型由几个决策树模型组成,这些模型组合起来产生模型的预测。
嗷,该编码了!
Via Github
对于这个项目,我们将使用 python 作为编程语言,库: pandas 和 scikit-learn 。首先,让我们了解一下我们数据的面:
# load the dataset
income = pd.read_csv("income.csv")
income.head()
在继续之前,我们需要对数据进行预处理,将分类变量转换为数值变量。在这个过程之后,这是一个数据样本:
现在,我们将使用两个伟大的 scikit-learn 特性,称为管道和 GridSearchCV ,它们允许我们自动测试不同模型的几个超参数。
# split-out train/validation and test dataset
X_train, X_test, y_train, y_test = train_test_split(income.drop(labels="high_income",axis=1), income["high_income"],test_size=0.20,random_state=seed,shuffle=True,stratify=income["high_income"])
# The full pipeline as a step in another pipeline with an estimator as the final step
pipe = Pipeline(steps = [("clf",RandomForestClassifier())])# create a dictionary with the hyperparameters
search_space = [{"clf":[DecisionTreeClassifier()],
"clf__criterion": ["gini","entropy"],
"clf__splitter": ["best","random"],
"clf__random_state": [seed],
"fs__score_func":[chi2],
"fs__k":[4,6,8]},
{"clf":[RandomForestClassifier()],
"clf__n_estimators": [200,300],
"clf__criterion": ["gini","entropy"],
"clf__max_leaf_nodes": [32,64,128],
"clf__random_state": [seed],
"fs__score_func":[chi2],
"fs__k":[4,6,8]},
{'clf' [BaggingClassifier(DecisionTreeClassifier(random_state=42))],
"clf__base_estimator__criterion": ['gini','entropy'],
"clf__base_estimator__splitter": ['best','random'],
"clf__oob_score": [True],
"clf__n_estimators": [200,300],
"clf__bootstrap":[True],
"fs__score_func":[chi2],
"fs__k":[4,6,8]},
{'clf': [BaggingClassifier(DecisionTreeClassifier(random_state=42))],
"clf__base_estimator__criterion": ['gini','entropy'],
"clf__base_estimator__splitter": ['best','random'],
"clf__oob_score": [False],
"clf__n_estimators": [200,300],
"clf__bootstrap":[False],
"fs__score_func":[chi2],
"fs__k":[4,6,8]}]# create grid search
kfold = KFold(n_splits=num_folds,random_state=seed)grid = GridSearchCV(estimator=pipe,
param_grid=search_space,
cv=kfold,
scoring=scoring,
return_train_score=True,
n_jobs=-1,
refit="AUC", verbose=10)
# fit grid search
best_model = grid.fit(X_train,y_train)
这种配置允许算法使用所有可用的核心,这将以并行方式测试 960 个不同配置的决策树、随机森林和 bagging 分类器(以决策树作为内部模型)。
在此过程之后,GridSearchCV 生成的最佳模型是一个随机森林模型,具有以下配置:
在我们定义这是否是最佳模型之前,让我们检查模型的准确性以训练和测试数据集。这种比较的目的是验证模型是欠配还是过配。
正如我们在上面看到的,两个集合的精度都很好,更重要的是,训练和测试精度的值非常接近。因此,这个结果表明 GridSearchCV 产生的最佳模型具有很好的通用性。
在随机森林分类器中,我们可以设置一个名为 bootstrap 的超参数,它定义天气样本是否将被替换训练。尽管选择的最佳模型的参数为假,这试图帮助模型最小化过拟合的机会,但当参数设置为**真、**时,其他几个模型也呈现出类似的结果,如下图所示。
因此,对于这个数据集,不管 bootstrap 变量的值如何,我们都取得了很好的结果。然而,由于可能的过度拟合,bootstrap 等于 true 时会出现最坏的结果。
了解特性的重要性
Via Giphy
现在,让我们检查数据集的每个特征对于我们的模型的重要性。对于这个任务,我们使用了两个工具:特征重要性,来自随机森林分类器和库SHAP*(SHapleyA加性 exPlanations)*其中是解释任何机器输出的统一方法
左图由 scikit-learn 的 feature_importances 制作,右图由 SHAP 制作。重要的是要看到两个结果呈现相似的结果,并且 4 个最重要的特征是相同的,仅改变了位置。有了这些结果,就很容易知道对模型影响最大的信息,这极大地有助于理解和解决问题。
集成学习案例研究:模型可解释性
这是一篇由两部分组成的文章的第一部分,在这篇文章中,我们将探索 1994 年人口普查收入数据集 ,它包含诸如年龄、受教育年限、婚姻状况、**种族、**以及许多其他信息。我们将使用该数据集将人们的潜在收入分为两类:年收入低于或等于 5 万美元的人(编码为 0)和年收入高于 5 万美元的人(编码为 1)。
在第一部分中,我们将使用这个数据集来比较简单决策树和集成方法的性能。稍后,我们还将探索一些工具来帮助我们解释为什么模型遵循可解释的人工智能(XAI) 的一些原则来做出决策。
我们需要做的第一件事是看一看选择的数据集,以便更好地了解它。所以,让我们开始吧!
1.准备数据
我们将使用没有空值的数据集的预处理版本。首先,我们加载基本库和数据集本身,并查看数据帧信息:
import as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
plt.style.use('ggplot')# load the dataset
income = pd.read_csv("income.csv")
income.info()
Figure 1: DataFrame information
如我们所见,数据集有 32561 个观察值和 15 列,其中 14 列是特征,一列是目标变量( high_income )。有些特征是分类的(类型对象)有些是数值的(类型 int64 ),所以我们需要为它们执行不同的预处理步骤。
为了实现这一点,我们将创建两个管道:一个对分类特征执行预处理步骤,另一个对数字特征执行预处理步骤。然后,我们将使用 FeatureUnion 将这两条管道连接在一起,形成最终的预处理管道。为此以及本文中的后续步骤,我们需要从 scikit-learn 库中导入必要的模块:
from sklearn.base import BaseEstimator
from sklearn.base import TransformerMixinfrom sklearn.preprocessing import StandardScaler
from sklearn.preprocessing import FunctionTransformerfrom sklearn.pipeline import Pipeline
from sklearn.pipeline import FeatureUnionfrom sklearn.model_selection import train_test_split
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import KFold
from sklearn.model_selection import StratifiedKFold
from sklearn.model_selection import RandomizedSearchCVfrom sklearn.metrics import make_scorer
from sklearn.metrics import accuracy_score
from sklearn.metrics import classification_report
from sklearn.metrics import confusion_matrixfrom sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.ensemble import BaggingClassifier
使用 BaseEstimator 和transformer mixin类,我们可以创建两个定制的转换程序放在我们的管道上:一个将数据分成分类和数字特征,另一个预处理分类特征。这两种变压器如下所示:
# Custom Transformer that extracts columns passed as argument
class FeatureSelector(BaseEstimator, TransformerMixin):
#Class Constructor
def __init__(self, feature_names):
self.feature_names = feature_names #Return self nothing else to do here
def fit(self, X, y = None):
return self #Method that describes what we need this transformer to do
def transform(self, X, y = None):
return X[self.feature_names]# converts certain features to categorical
class CategoricalTransformer( BaseEstimator, TransformerMixin ):
#Class constructor method that takes a boolean as its argument
def __init__(self, new_features=True):
self.new_features = new_features #Return self nothing else to do here
def fit( self, X, y = None ):
return self #Transformer method we wrote for this transformer
def transform(self, X , y = None ):
df = X.copy()
if self.new_features:
# Treat ? workclass as unknown
df['workclass']= df['workclass'].replace('?','Unknown')
# Two many category level, convert just US and Non-US
df.loc[df['native_country']!=' United-States','native_country'] = 'non_usa'
df.loc[df['native_country']==' United-States','native_country'] = 'usa' # convert columns to categorical
for name in df.columns.to_list():
col = pd.Categorical(df[name])
df[name] = col.codes # returns numpy array
return df
有了这些定制的转换器,我们就可以构建预处理管道。我们需要做的第一件事是创建 X 特征矩阵和 y 目标向量:
# Create the X feature matrix and the y target vector
X = income.drop(labels="high_income", axis=1)
y = income["high_income"]# the only step necessary to be done outside of pipeline
# convert the target column to categorical
col = pd.Categorical(y)
y = pd.Series(col.codes)# global variables
seed = 108
之后,我们提取分类和数字特征名称,并在定义了要在每个特征中使用的步骤之后创建 2 个管道。对于分类管道,我们使用 FeatureSelector 只选择分类列,然后使用 CategoricalTransformer 将数据转换成所需的格式;至于数字管道,我们也将使用 **FestureSelector,**这次只选择数字特征,随后使用 as StandardScaler 来标准化数据。代码如下所示:
# get the categorical feature names
categorical_features = X.select_dtypes("object").columns.to_list()
# get the numerical feature names
numerical_features = X.select_dtypes("int64").columns.to_list()# create the steps for the categorical pipeline
categorical_steps = [
('cat_selector', FeatureSelector(categorical_features)),
('cat_transformer', CategoricalTransformer())
]# create the steps for the numerical pipeline
numerical_steps = [
('num_selector', FeatureSelector(numerical_features)),
('std_scaler', StandardScaler()),
]# create the 2 pipelines with the respective steps
categorical_pipeline = Pipeline(categorical_steps)
numerical_pipeline = Pipeline(numerical_steps)
现在我们可以使用 FeatureUnion 类来水平连接这两个管道,这样我们最终只有一个最终预处理管道:
pipeline_list = [
('categorical_pipeline', categorical_pipeline),
('numerical_pipeline', numerical_pipeline)
]# Combining the 2 pieplines horizontally into one full pipeline
preprocessing_pipeline =FeatureUnion(transformer_list=pipeline_list)
就这样,现在你所要做的就是对数据执行所有预处理步骤,调用预处理 _ 管道对象的 fit_transform 方法,将 X 矩阵作为参数传递!简洁明了。
2.训练第一个模型
在训练任何机器学习模型之前,我们必须将数据分成训练和测试组。为此,我们使用训练 _ 测试 _ 分割功能:
# split-out train/validation and test dataset
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.20, random_state=seed, shuffle=True, stratify=y)
我们将使用的第一个模型是一个简单的决策树分类器。为了充分利用**管道的全部功能,**我们可以创建一个完整的管道,第一步通过预处理管道,第二步通过所需的分类模型:
# we pass the preprocessing pipeline as a step to the full pipeline
full_pipeline_steps = [
('preprocessing_pipeline', preprocessing_pipeline),
('model', DecisionTreeClassifier(random_state=seed))
]# create the full pipeline object
full_pipeline = Pipeline(steps=full_pipeline_steps)
通过这种方式,如果我们想要尝试不同的模型(正如我们稍后将要做的),我们所要做的就是更新这个管道的**‘model’**步骤!
下一步是使用 RandomizedSearchCV 执行模型超参数调整。随机搜索不如常规网格搜索彻底,因为它不会测试超参数的每个可能组合。另一方面,它的计算成本更低,使我们能够在低端硬件中实现**“足够好”**的结果,同时调整多个超参数。使用 n_iter 参数,我们将迭代次数限制为 50。
我们还将使用stratified fold来执行交叉验证。与常规的 KFold 不同,它保留了样品在褶皱间的分布,可能会产生更好的结果。
最后一步是用我们需要调优的超参数构建一个 param_grid 字典。完整的代码可以在下面看到。注意我们是如何将 full_pipeline 对象作为 RandomizedSearchCV 估计器传递的,然后我们在结果对象上调用 fit 方法,就像我们调用任何其他 sklearn 模型一样。这样,当我们想要测试其他模型时,我们所要做的就是改变管道上的模型,并创建一个新的参数网格来传递,就这么简单!
# Create the grid search parameter grid and scoring funcitons
param_grid = {
"model": [DecisionTreeClassifier(random_state=seed)],
"model__criterion": ["gini","entropy"],
"model__splitter": ["best","random"],
"model__max_leaf_nodes": [16, 64, 128, 256],
"model__max_depth": np.linspace(1, 32, 32)
}scoring = {
'AUC': 'roc_auc',
'Accuracy': make_scorer(accuracy_score)
}# create the Kfold object
num_folds = 10
kfold = StratifiedKFold(n_splits=num_folds, random_state=seed)# create the grid search object with the full pipeline as estimator
n_iter=50grid = RandomizedSearchCV(
estimator=full_pipeline,
param_distributions=param_grid,
cv=kfold,
scoring=scoring,
n_jobs=-1,
n_iter=n_iter,
refit="AUC"
)# fit grid search
best_model = grid.fit(X_train,y_train)
对于决策树分类器,我们正在调整以下参数:
- **标准:**定义了度量树节点上分割质量的函数;
- **拆分器:**定义在每个树节点选择拆分的策略;
- max_leaf_nodes: 限制树中叶子节点的最大数量;
- max_depth: 限制树可以生长的最大深度;
随机搜索完成且最佳模型符合我们的数据后,我们可以进行预测并衡量模型的性能:
# final Decision Tree model
pred_test = best_model.predict(X_test)
pred_train = best_model.predict(X_train)print('Train Accuracy: ', accuracy_score(y_train, pred_train))
print('Test Accuraccy: ', accuracy_score(y_test, pred_test))print('\nConfusion Matrix:')
print(confusion_matrix(y_test,pred_test))
print('\nClassification Report:')
print(classification_report(y_test,pred_test))
Figure 2: Decision tree performance
3.集成方法
集成方法是将几个基本模型结合起来以产生一个最佳预测模型的模型。RandomForestClassifier是一个典型的例子,因为它结合了几个更简单的决策树来生成输出。这样,就有可能克服决策树模型的几个局限性,比如它的过度拟合倾向。
3.1.装袋分级机
我们要使用的第一个集成方法是 BaggingClassifier 。名称 bagging 来自 bootstrap aggregating ,它的工作原理是将数据分成几个随机的子样本 ,然后用于训练每个独立的基本估计器。该策略可以以两种方式之一执行:替换,这意味着样本可以多次用于同一个估计器;无需更换,意味着每个样品只能使用一次(这种方法称为粘贴)。
装袋通常比粘贴产生更好的结果,它还有一个巧妙的锦囊妙计:袋外 **评估。**由于样本是替换抽取的,并且同一样本可以随机使用多次,因此训练集上的一些样本可能永远不会用于训练任何基础估计量!这意味着我们可以使用这些样本进行进一步的模型评估!
因此,考虑到所有这些,我们将更新我们之前的管道,以使用 BaggingClassifier 和决策树作为基本估计器。为此,我们所要做的就是更改管道定义中的**‘模型’**步骤,并重新定义 RandomizedSearchCV 参数搜索空间,如下面的代码片段所示:
# we pass the preprocessing pipeline as a step to the full pipeline
full_pipeline_steps = [
('preprocessing_pipeline', preprocessing_pipeline),
('model', BaggingClassifier(
DecisionTreeClassifier(max_features="auto", splitter="random", max_leaf_nodes=128, random_state=seed),
random_state=seed
))
]# create the full pipeline object
full_pipeline = Pipeline(steps=full_pipeline_steps)# Create the grid search parameter grid
param_grid = {
"model": [BaggingClassifier(
DecisionTreeClassifier(max_features="auto", splitter="random", max_leaf_nodes=128, random_state=seed),
random_state=seed
)],
"model__n_estimators": np.arange(100, 1000, 100),
"model__max_samples":[0.8, 1.0],
"model__max_features": [0.8, 1.0],
"model__bootstrap": [True],
"model__oob_score": [True],
}scoring = {
'AUC': 'roc_auc',
'Accuracy': make_scorer(accuracy_score)
}# create the Kfold object
num_folds = 10
kfold = StratifiedKFold(n_splits=num_folds, random_state=seed)# create the grid search object with the full pipeline as estimator
n_iter=25grid = RandomizedSearchCV(
estimator=full_pipeline,
param_distributions=param_grid,
cv=kfold,
scoring=scoring,
n_jobs=-1,
n_iter=n_iter,
refit="AUC"
)
# fit grid search
best_bag = grid.fit(X_train,y_train)
对于打包分类器,我们正在调整以下参数:
- n_estimators: 定义要使用的估计器(在这种情况下是决策树)的总数;
- max_samples: 从 X 中抽取的用于训练每个基本估计量的样本的最大百分比;
- max_features: 从 X 中提取的特征的最大百分比,用于训练每个基本估计量;
- bootstrap: 设置为 False 时,抽取样本而不替换(粘贴)。当设置为真时,用替换(装袋)抽取样本。因为我们要使用开箱得分,所以我们必须将该参数设置为 True
- oob_score: 设置为 True 时,返回最佳模型的开箱得分;
随机搜索完成且最佳模型符合我们的数据后,我们可以检查调整后的模型超参数,进行预测并测量模型的性能:
print(f'Best score: {best_bag.best_score_}')
print(f'Best model: {best_bag.best_params_}')
Figure 3: Best Bagging model
pred_test = best_bag.predict(X_test)
pred_train = best_bag.predict(X_train)print('Train Accuracy: ', accuracy_score(y_train, pred_train))
print('Test Accuraccy: ', accuracy_score(y_test, pred_test))
print("Out-of-Bag Accuracy: ", best_bag.best_params_['model'].oob_score_)print('\nConfusion Matrix:')
print(confusion_matrix(y_test,pred_test))
print('\nClassification Report:')
print(classification_report(y_test,pred_test))
Figure 4: Bagging performance
如我们所见,该模型达到的所有 3 个精确度彼此非常接近,表明该模型对新数据具有良好的泛化能力,因此可能不会过度拟合。
3.2.随机森林分类器
我们将训练的最后一个模型是 RandomForestClassifier 。使用它的过程与上面给出的过程相同,可以在下面的代码片段中看到:
# we pass the preprocessing pipeline as a step to the full pipeline
full_pipeline_steps = [
('preprocessing_pipeline', preprocessing_pipeline),
('model', RandomForestClassifier(random_state=seed))
]# create the full pipeline object
full_pipeline = Pipeline(steps=full_pipeline_steps)# Create the grid search parameter grid and scoring funcitons
param_grid = {
"model": [RandomForestClassifier(random_state=seed)],
"model__max_depth": np.linspace(1, 32, 32),
"model__n_estimators": np.arange(100, 1000, 100),
"model__criterion": ["gini","entropy"],
"model__max_leaf_nodes": [16, 64, 128, 256],
"model__oob_score": [True],
}scoring = {
'AUC': 'roc_auc',
'Accuracy': make_scorer(accuracy_score)
}# create the Kfold object
num_folds = 10
kfold = StratifiedKFold(n_splits=num_folds, random_state=seed)# create the grid search object with the full pipeline as estimator
n_iter=50grid = RandomizedSearchCV(
estimator=full_pipeline,
param_distributions=param_grid,
cv=kfold,
scoring=scoring,
n_jobs=-1,
n_iter=n_iter,
refit="AUC"
)# fit grid search
best_rf = grid.fit(X_train,y_train)
对于 RandomForestClassifier ,我们正在调整以下参数:
- **标准:**定义了度量树节点上分割质量的函数;
- max_leaf_nodes: 限制树中的最大叶节点数;
- max_depth: 限制树木可以生长的最大深度;
- n_estimators: 定义森林中使用的树木总数
- oob_score: 设置为 True 时,返回最佳模型的出袋分数;
正如我们之前所做的,在随机搜索完成且最佳模型符合我们的数据后,我们可以进行预测并衡量模型的性能:
print(f'Best score: {best_rf.best_score_}')
print(f'Best model: {best_rf.best_params_}')
Figure 5: Best Random Forest model
pred_test = best_rf.predict(X_test)
pred_train = best_rf.predict(X_train)print('Train Accuracy: ', accuracy_score(y_train, pred_train))
print('Test Accuraccy: ', accuracy_score(y_test, pred_test))
print("Out-of-Bag Accuracy: ", best_rf.best_params_['model'].oob_score_)print('\nConfusion Matrix:')
print(confusion_matrix(y_test,pred_test))
print('\nClassification Report:')
print(classification_report(y_test,pred_test))
Figure 6: Random Forest performance
与 BaggingClassifier 相似,精确度彼此非常接近,表明对看不见的数据具有良好的泛化能力,因此没有过拟合。由于这是我们发现的性能最好的模型,我们将在本文的剩余部分中使用它作为例子。
4.模型可解释性
在某些情况下,从你的模型中获得好的预测可以说和理解为什么给出答案一样重要。在这些情况下,我们可以利用可解释的人工智能(XAI) 的概念,并试图使模型更容易被人类理解。
一种方法是分析模型结果中每个特征的重要性。幸运的是,随机森林有一个内置属性可以告诉我们这一点,它叫做 feature_importances_ 。下面的代码显示了访问和绘制这些信息的步骤:
# lets get the random forest model configuration and feature names
rf_model = best_rf.best_params_['model']
features = np.array(X_train.columns)# Transforming the test data.
new_X_test = preprocessing_pipeline.fit_transform(X_test)
new_X_test = pd.DataFrame(new_X_test, columns=X_test.columns)# get the predicitons from the random forest object
y_pred = rf_model.predict(new_X_test)# get the feature importances
importances = rf_model.feature_importances_# sort the indexes
sorted_index = np.argsort(importances)
sorted_importances = importances[sorted_index]
sorted_features = features[sorted_index]# plot the explained variance using a barplot
fig, ax = plt.subplots()
ax.barh(sorted_features , sorted_importances)
ax.set_xlabel('Importances')
ax.set_ylabel('Features')
Figure 7: Feature importances
这是一个简单而有效的例子,可以让你从数据和模型推理中获得更多的洞察力。有了剧情,我们可以看到最重要的特征是 education_num , capital_loss , fnlwgt , capital_gain ,以及 race 。
为了更进一步,我们将简要探索两个第三方库,它们使我们能够获得不同的可视化: ELI5 和 SHAP。
4.1.使用 ELI5 进行模型解释
ELI5 是一个 Python 包,帮助调试机器学习分类器,解释它们的预测。它为一些机器学习库提供支持,包括 scikit-learn 和 XGBoost。
有了它,我们能够以两种主要方式查看分类模型:第一种是检查模型参数并分析模型如何全局工作(类似于默认的特征重要性属性);第二个是检查单个预测,以弄清楚为什么模型会做出这样的决定。
对于第一个用例,我们使用 show_weights() 函数,如下面的代码片段所示:
import eli5# lets get the random forest model configuration and feature names
rf_model = best_rf.best_params_['model']
features = np.array(X_train.columns)eli5.show_weights(rf_model, feature_names=features)
Figure 8: ELI5 feature weights
正如我们在上面的图像中看到的,结果与我们从树特征重要性中获得的结果非常相似。
至于第二个用例,我们可以使用 explain_prediction() 函数来检查和分析模型的各个预测。为了测试这一点,我们检查了一个真正的负面预测(实际值为 0,预测值也为 0)和一个真正的正面预测(实际值为 1,预测值也为 1):
# predicting a person earns less than 50k/year (true negative)
index = 4
print('Actual Label:', y_test.iloc[index])
print('Predicted Label:', y_pred[index])
eli5.explain_prediction(rf_model, new_X_test.iloc[index], feature_names=features)# predicting a person earns more than 50k/year (true positive)
index = 7
print('Actual Label:', y_test.iloc[index])
print('Predicted Label:', y_pred[index])
eli5.explain_prediction(rf_model, new_X_test.iloc[index], feature_names=features)
Figure 9: ELI5 true negative prediction example
Figure 10: ELI5 true positive prediction example
因此,有助于模型预测这些特定观察结果的最有影响力的特征分别是种族和教育 _ 数量。
4.2.SHAP 的模型解释
**SHAP(SHapley Additive exPlanations)**库是解释任何机器学习模型输出的统一方法。与 ELI5 类似,它也支持几个机器学习库,包括 scikit-learn 和 XGBoost。
为了将它的功能用于我们的随机森林模型,我们首先需要创建一个 TreeExplainer 对象,并为模型获取所谓的 shap_values 。这个过程如下面的代码所示:
import shap
shap.initjs()# Create the explainer object
explainer = shap.TreeExplainer(rf_model)
print('Expected Value:', explainer.expected_value)# get the shap values from the explainer
shap_values = explainer.shap_values(new_X_test)
正如我们对 ELI5 所做的那样,我们也可以使用 SHAP 库来解释单个模型预测,如下所示,这些数据点与我们之前处理的数据点相同:
# predicting a person earns less than 50k/year (true negative)
shap.force_plot(explainer.expected_value[0],
shap_values[0][4], X_test.iloc[4])# predicting a person earns more than 50k/year (true positive)
shap.force_plot(explainer.expected_value[1],
shap_values[1][7], X_test.iloc[7])
Figure 11: SHAP true negative prediction example
Figure 12: SHAP true positive prediction example
此外,还可以一次显示多个预测,如下图所示为数据集的前 1000 个样本:
shap.force_plot(explainer.expected_value[0],
shap_values[0][:1000,:], X_test.iloc[:1000,:])
Figure 12: SHAP prediction explanation for the first 1000 samples
在同一个图中,还可以分析不同特征在最终模型预测中的影响。为了测试这一点,我们对图进行了配置,以显示 education_num 特性对相同样本的重要性:
Figure 13: SHAP education_num effect for the first 1000 samples
最后,我们可以使用 summary_plot 函数来绘制按类划分的特性重要性:
shap.summary_plot(shap_values, X_test)
Figure 14: SHAP feature importances by class
我们可以看到,得到的结果与树内置特性重要性和 ELI5 库得到的结果非常相似。
5.结论
我们几乎没有触及机器学习领域中几个重要主题的表面,如管道、超参数调整、集成方法和模型可解释性。还有更多的内容要介绍!
在这个系列的下一部分,我们将看看新的集成方法的“酷小子”:梯度增强。我们将看看由 XGBoost 库提供的实现,敬请关注!