使用概率模型和 AWS Lambda 函数构建智能(er)应用程序
用 WebPPL 概率程序为端点供电的快速、廉价和无服务器的方法。
哎呀!…我需要再次部署一个模型
“概率是生活的向导.”―西塞罗
当今天的酷孩子忙着画张量和画 PyTorch 时,更酷的孩子越来越喜欢概率编程(PP)——例如参见 Pyro , Figaro , Stan ,Infer.NET(想了解更多关于 PP 的哲学倾向的读者可以从我们自己关于概念学习的观点文章开始
如果你是一名数据科学家/数据工程师/人工智能什么的,并且你正在考虑给你现有的数据管道添加一些概率魔法——而不打扰你的 devOps 团队和花费云资金——你可能会喜欢让微服务以一种快速、廉价和直接的方式公开模型的想法。
在这篇文章中,我们利用无服务器和 AWS lambda 功能在几分钟内部署一个 WebPPL 驱动的端点。正如我们将看到的,新部署的端点,加上其他无服务器代码(例如,一个单页 web 应用程序),成为一个可以在组织内共享的原型,用于快速反馈和快速迭代。
这是“懒惰开发”系列中之前帖子的续篇,该系列以带有 Tensorflow 模型和 Spark 决策树的 AWS Lambda 函数为特色。
先决条件
在深入研究代码之前,请确保:
- 拥有一个安装了 npm 的工作节点环境;
- 拥有一个 AWS 帐户来将代码部署到AWS Lambda;
- 设置无服务器(遵循此处的说明;记得设置你的 AWS 凭证);
- 如果你有一个 WebPPL 模型准备好了,太好了(100 奖励点!);如果没有,不用担心:我们将一起发货!
像往常一样,你可以在我们的 GitHub 页面中找到所有代码:让我们开始工作吧。
(玩具)概率应用程序
“所有的模型都是错的,但有些是有用的。”―G .盒子
如果说 Javascript 中程序的语义是从程序到执行的函数,那么它的 PP 表亲 WebPPL 的语义是从程序到执行的分发的函数。换句话说,当你运行这个程序时:
结果是结果的分布,通过 I)列举随机变量(die1
和die2
)的可能值,ii)以die1 + die2
之和的值(8
)为条件,即拒绝条件为不满足的所有运行:
Frequency distribution for the value of the first die, conditional on the sum of the two being 8.
由于这不是对概率规划的介绍,因此我们仅简单提及 PP 原则已经成功应用于各种各样的现象,例如技能评级、计算机视觉和基于图像的推理(参见此处和此处)、程序合成、不确定性下的决策, 自然语言语义学和语用学,以及显然在认知科学中模拟人类推理的棘手任务——非懒惰读者被强烈鼓励进一步探索理论景观并了解这一工具家族的许多优点(例如,在服务预测时量化模型不确定性)。
正如所料,我们将使用的语言是 WebPPL ,一个纯粹的函数 Javascript 子集,增加了概率原语,如sample
(一个函数,不出所料,从分布中采样值)。为了展示 WebPPL 的概率性质,我们将从高斯过程中采样并提取数据点 ( GP ): GPs 非常酷,非常通用并在数据科学社区中得到越来越多的关注(显然,我们讨论的内容并不特定于 GPs ,因此可以应用于其他类型的 WebPPL 程序,例如,对计算机视觉的推理
Charting priors from GPs with the RBF kernel (mean=0, sigma=1).
预期的工作流程如下:
A lambda-powered web app for prototyping and data viz. with WebPPL and standard JS libraries.
- 用户从公共 URL 请求 web 应用程序;
- 一个λ函数被 API 网关调用;响应是简单的 HTML 代码,包含一个基本表单,要求用户输入一些变量;
- 用户通过一个 HTML 按钮提交她的选择,这个按钮使用 JQuery 向模型端点发送一个 AJAX 请求;
- API 网关调用 Lambda 函数;响应是在用户输入上运行所选择的 WebPPL 程序(下面将详细介绍)的输出;
- 最后,一些 Chart.js 代码将端点响应映射到一个漂亮的数据可视化。
在下一节中,我们将看到如何在几秒钟内从一个存在于 powerpoint 中的想法变成一个有效的 web 应用程序。
空谈不值钱,给我看看代码!
“给一个人一个程序,让他沮丧一天。
教一个人编程,挫他一辈子。”―米·瓦西姆
我们的项目功能仅依赖于项目中的两个文件:
handler.js
包含上述两个功能的代码;serverless.yml
是一个标准的 yml 文件,描述了运行这两个功能的 AWS 基础设施。
serverless.yml
是一个普通的无服务器文件,它通过 API Gateway 将 lambda 函数公开为端点:这里没有什么特别的(例如,万维网上已经充满了关于这个设置的教程,包括我们自己的)。
所有神奇的事情都发生在这里。该结构确实相当简单,因此我们将重点介绍一些关键特性:
- WebPPL 是用顶部的
webppl = require('webppl')
语法导入的; app
是一个简单的函数,返回一个完整的 HTML 页面,包括在末尾添加所需输入字段和基本交互的 JQuery 函数——HTML 代码简单地存储为一个字符串,并在响应中返回特定的headers
;model
函数是实际运行 WebPPL 代码的函数:它从一些简单的参数检查/验证/类型转换开始,然后使用webppl.run(CODE)
执行在const code
变量中指定的代码。请注意,为了向模型传递查询参数,我们使用了书中最简单的技巧,即构建一个包含脚本变量的代码片段(作为字符串),并确保code
使用输入变量的正确名称。一个显而易见的选择是全球商店,但是如果没有特别的顾虑(就像在这个例子中),我们的简单策略就足够了。
要查看项目的运行情况,只需克隆 repo ,打开终端,cd
进入文件夹,下载 WebPPL 依赖关系:
npm install webppl
然后最后键入:
serverless deploy
如果无服务器安装正确,第一个deploy
触发 AWS 上所需资源的创建(未来的deploy
命令会更快)。等待一段时间后,您应该会在终端中看到一条成功消息:
Congratulations, your lambdas are up and running!
如果你将浏览器指向在终端中找到的/app
URL(见上图中的“端点”),一切都应该如预期的那样工作:
Our fantastic web app is up and running (original video here)!
当您对原型满意并准备好清理资源时,只需使用remove
命令来安全地移除堆栈:
serverless remove
后续步骤
“一千个模特的旅程从一个(几乎)老子开始”
在这篇的简短帖子中,我们展示了如何将概率模型部署到 AWS,并通过标准 GET 请求用几行代码按需提供预测:
这种设置快速、可靠且廉价(如果不是免费的,因为前 100 万个请求/mo 完全在杰夫·贝索斯上)。
要使提供的模板适应您自己的原型,只需更改model
函数中的 WebPPL 代码,然后相应地调整 HTML 输入字段、参数解析和数据可视化:在几分钟内,您将能够与任何人分享您非凡的贝叶斯建模的结果,或者生成有洞察力的可视化来帮助您的科学探索。
在可视化方面, WebPPL viz 模块可能会有一些启发来概括现在简单而杂乱的图表:使用 chart.js / d3 围绕 WebPPL 模型的主要用例构建一个可重用的包装器应该不会花费太长时间对于非懒惰读者的练习:对于线性回归案例,例如单一回归来说,什么可能是有趣且引人入胜的可视化].
在建模方面,我们选择了 GP s 来提供一个在浏览器窗口中看起来不错的端到端概率程序,但显然概率人工智能的世界是伟大的,并且——像宇宙一样——在不断扩展:如果你想要一些 PP 建模的想法,这里有一吨很酷的东西。
最后,如果你真的喜欢这个设置,你认为它已经准备好了,一定要添加测试,错误处理和所有其他重要的东西,这个教程太短了,无法包含。
快乐(概率)编码!
再见,太空牛仔
如果您有问题、反馈或评论,请与jacopo . taglia bue @ tooso . ai分享您的故事。
别忘了从 Linkedin 、 Twitter 和 Instagram 上的 Tooso 获取最新消息。
感谢
如果没有 Tooso 的高级工程师和节点向导 Luca,这一切都是不可能的:Luca 建立了我们的第一个 WebPPL-to-lambda 游乐场,并耐心地回答了我所有(愚蠢的)节点问题。
这篇文章中大多数好的想法都是卢卡的,剩下的错误都是我的。
构建正确的自动编码器——使用 PCA 原理进行调整和优化。第一部分
在这里,我们将学习自动编码器的期望属性,这些属性源自它与 PCA 的相似性。由此,我们将在第二部分中为自动编码器构建定制约束,以进行调整和优化。
<了解深度学习,了解更多> >
深度学习 API 的可用性,如 Keras 和 TensorFlow,使模型建立和实验变得极其容易。然而,对基本面缺乏清晰的理解可能会让我们在最佳模式的竞赛中迷失方向。在这样的比赛中达到最佳模式是靠运气的。
在这里,我们将了解自动编码器所需的基本属性。这将为自动编码器调整和优化提供一个很好的指导方法。在第一部分中,我们将重点学习这些属性及其优点。在第二部分,我们将开发自定义层和约束来合并属性。
我们将在这里学习的主要概念是,自动编码器与主成分分析(PCA) 直接相关,这将使我们能够构建一个正确的自动编码器。一个“正确的”自动编码器在数学上意味着一个适定的自动编码器。一个适定的模型更容易调整和优化。
自动编码器相对于PCA 、
- 线性激活的自动编码器近似于 PCA。数学上,最小化 PCA 建模中的重建误差与单层线性自动编码器相同。
- 自动编码器将 PCA 扩展到非线性空间。换句话说,自动编码器是 PCA 的非线性扩展。
因此,自动编码器应该理想地具有 PCA 的特性。这些属性是,
- **绑定权重:**编码器和相应解码器层上的权重相等(在下一节的图 1 中阐明)。
- **正交权重:**每个权重向量都是相互独立的。
- **不相关特征:**编码层的输出不相关。
- **单位定额:**一层上的权重有单位定额。
然而,大多数教程中解释的自动编码器,例如在 Keras【1】中构建自动编码器,没有这些属性。缺少这一点使它们不是最佳的。
因此,对于一个适定的自动编码器来说,结合这些属性是很重要的。通过合并它们,我们还将
- **有正规化。**正交性和单位范数约束作为正则化。此外,正如我们将在后面看到的,绑定权重将网络参数的数量减少了近一半,这是另一种正则化类型。
- **解决爆炸和消失的渐变。**单位范数约束防止权重变大,因此解决了爆炸梯度问题。此外,由于正交性约束,只有重要的/信息性的权重是非零的。因此,在反向传播期间,足够的信息流过这些非零权重,从而避免消失梯度。
- 有更小的网络:没有正交性,编码器有冗余的权重和特征。为了补偿冗余,增加了编码器的尺寸。相反,正交性确保每个编码特征都有一条唯一的信息——独立于其他特征。这消除了冗余,并且我们可以用较小的编码器(层)编码相同数量的信息。
通过一个更小的网络,我们让 Autoencoder 更接近边缘计算。
本文将通过展示
- PCA 和 Autoencoder 之间的架构相似性,以及
- 传统自动编码器的次优性。
本文将在第二部分中继续,详细介绍优化自动编码器的步骤。在第二部分的中,我们发现优化将自动编码器重构误差提高了 50%以上。
本文假设读者对 PCA 有基本的了解。如果不熟悉,请参考了解 PCA【2】。
PCA 和自动编码器之间的架构相似性
Figure 1. Single layer Autoencoder vis-à-vis PCA.
为简单起见,我们将线性单层自动编码器与 PCA 进行比较。PCA 建模有多种算法。其中之一是通过最小化重建误差进行估计(参见[ 3 ])。遵循该算法可以更清楚地理解 PCA 和自动编码器之间的相似之处。
图 1 显示了单层线性自动编码器。如图底部所示,编码过程类似于 PC 变换。PC 变换将原始数据投影到主成分上,以产生正交特征,称为主得分。类似地,解码过程类似于从主要分数重构数据。在自动编码器和 PCA 中,可以通过最小化重建误差来估计模型权重。
在下文中,我们将通过展示关键的自动编码器组件及其在 PCA 中的等效组件来进一步阐述图 1。
假设我们有带有 p 特征的数据。
输入层—数据样本。
- 在自动编码器中,使用尺寸为 p. 的输入层输入数据
- 在 PCA 中,数据作为样本输入。
编码——数据在主成分上的投影。
- 编码层的大小为 k 。在 PCA 中, k 表示选择的主成分(PCs)的数量。
- 在两者中,我们都有 k < p 进行降维。 k ≥ p 导致过代表模型,因此(接近)零重建误差。
- 图 1 中编码层中的彩色单元是计算节点,其具有表示为p的权重,
也就是说,对于 1,…, k 中的每个编码节点,我们都有一个 p 维的权重向量。这相当于 PCA 中的一个特征向量。
- 自动编码器中的编码层输出是,
x 是输入, W 是权重矩阵。功能 g 是一个激活功能。 g ( Wx )是编码层的输出。如果激活是线性的,这相当于 PCA 中的主要分数。
解码——从主要分数中重建数据。
- 自动编码器和 PCA 重建中解码层的大小必须是输入数据的大小
- 在解码器中,数据从编码中被重构为,
类似地,在 PCA 中,它被重构为,
注意,我们在等式中有W’。4 和等式中的 W 。5.这是因为,默认情况下,编码器和解码器的权重不同。如果编码器和解码器权重与绑定,解码器和 PCA 重建将是相同的,即
解码器单元中的多种颜色表示编码器中不同单元中的权重出现在解码器中的相同单元中。
这就把我们带到了自动编码器和 PCA 之间的数学比较。
数学上,线性自动编码器将类似于 PCA if,
- 捆绑权重:在任何通用多层自动编码器中,编码器模块中的层 l 的权重等于解码器中从末端开始的层 l 的权重的转置。
- 正交权重:编码层上的权重是正交的(参见等式。7b)。可以在中间编码器层上实施相同的正交性约束以进行规则化。
- 不相关特征:PCA 的输出,即主分数,是不相关的。因此,编码器的输出应该具有,
- 单位范数:PCA 中的特征向量被约束为具有单位范数。如果没有这个约束,我们将得不到一个合适的解,因为只要向量的范数增加,投影的方差就可能变得任意大。出于同样的原因,编码层上的权重应该是单位范数(参见等式 1)。7d)。这个约束也应该应用于其他中间层以进行正则化。
正则无约束自动编码器的次优性
这里我们将在随机数据集上实现 PCA 和一个典型的无约束自动编码器。我们将展示它们的输出在上述各个方面的不同。这导致了次优的自动编码器。在这个讨论之后,我们将展示如何为正确的估计(第二部分)约束自动编码器。
完整的代码可在这里获得。
加载库
**from** **numpy.random** **import** seed
seed(123)
**from** **tensorflow** **import** set_random_seed
set_random_seed(234)
**import** **sklearn**
**from** **sklearn** **import** datasets
**import** **numpy** **as** **np**
**from** **sklearn.model_selection** **import** train_test_split
**from** **sklearn.preprocessing** **import** StandardScaler, MinMaxScaler
**from** **sklearn** **import** decomposition
**import** **scipy**
**import** **tensorflow** **as** **tf**
**from** **keras.models** **import** Model, load_model
**from** **keras.layers** **import** Input, Dense, Layer, InputSpec
**from** **keras.callbacks** **import** ModelCheckpoint, TensorBoard
**from** **keras** **import** regularizers, activations, initializers, constraints, Sequential
**from** **keras** **import** backend **as** K
**from** **keras.constraints** **import** UnitNorm, Constraint
生成随机数据
我们生成多元相关正态数据。数据生成的步骤在 GitHub 库中有详细说明。
n_dim = 5
cov = sklearn.datasets.make_spd_matrix(n_dim, random_state=None)
mu = np.random.normal(0, 0.1, n_dim)n = 1000X = np.random.multivariate_normal(mu, cov, n)X_train, X_test = train_test_split(X, test_size=0.5, random_state=123)# *Data Preprocessing* scaler = MinMaxScaler()
scaler.fit(X_train)X_train_scaled = scaler.transform(X_train)X_test_scaled = scaler.transform(X_test)
测试数据集将在第二部分中用于比较自动编码器重建精度。
自动编码器和 PCA 型号
我们安装了一个单层线性自动编码器,编码维数为两个。我们还为 PCA 安装了两个组件**。**
# *Fit Autoencoder*
nb_epoch = 100
batch_size = 16
input_dim = X_train_scaled.shape[1] *#num of predictor variables,*
encoding_dim = 2
learning_rate = 1e-3
encoder = Dense(encoding_dim, activation="linear", input_shape=(input_dim,), use_bias = **True**)
decoder = Dense(input_dim, activation="linear", use_bias = **True**)
autoencoder = Sequential()
autoencoder.add(encoder)
autoencoder.add(decoder)
autoencoder.compile(metrics=['accuracy'],
loss='mean_squared_error',
optimizer='sgd')
autoencoder.summary()
autoencoder.fit(X_train_scaled, X_train_scaled,
epochs=nb_epoch,
batch_size=batch_size,
shuffle=**True**,
verbose=0)# *Fit PCA*
pca = decomposition.PCA(n_components=2)pca.fit(X_train_scaled)
Figure 2. Structure of the single-layer Autoencoder.
从视觉上看,这里开发的编码器-解码器结构如下面的图 3 所示。该图有助于理解权重矩阵是如何排列的。
Figure 3. A simple linear Autoencoder to encode a 5-dimensional data into 2-dimensional features.
为了遵循 PCA 属性,图 3 中的自动编码器应该遵循等式中的条件。下面,我们将示出这种传统的自动编码器不满足它们中的任何一个。
1.捆绑重物
正如我们在下面看到的,编码器和解码器的权重是不同的。
w_encoder = np.round(autoencoder.layers[0].get_weights()[0], 2).T *# W in Figure 3.*
w_decoder = np.round(autoencoder.layers[1].get_weights()[0], 2) *# W' in Figure 3.*
print('Encoder weights **\n**', w_encoder)
print('Decoder weights **\n**', w_decoder)
2。权重正交性
如下所示,与 PCA 权重(即特征向量)不同,编码器和解码器上的权重不是正交的。
w_pca = pca.components_
np.round(np.dot(w_pca, w_pca.T), 3)
np.round(np.dot(w_encoder, w_encoder.T), 3)
np.round(np.dot(w_decoder, w_decoder.T), 3)
3。特征关联
在 PCA 中,特征是不相关的。
pca_features = pca.fit_transform(X_train_scaled)
np.round(np.cov(pca_features.T), 5)
但是编码特征是相关的。
encoder_layer = Model(inputs=autoencoder.inputs, outputs=autoencoder.layers[0].output)
encoded_features = np.array(encoder_layer.predict(X_train_scaled))
print('Encoded feature covariance\n', np.cov(encoded_features.T))
权重非正交性和特征相关性是不期望的,因为它带来了包含在编码特征中的信息的冗余。
4。单位定额
五氯苯甲醚重量的单位标准是 1。这是在 PCA 估计中应用的约束,以产生正确的估计*。*
print('PCA weights norm, \n', np.sum(w_pca ** 2, axis = 1))
print('Encoder weights norm, \n', np.sum(w_encoder ** 2, axis = 1))
print('Decoder weights norm, \n', np.sum(w_decoder ** 2, axis = 1))
完整的代码可在这里获得。
了解 PCA 和自动编码器之间的关系-cran 2367/PCA-自动编码器-关系
github.com](https://github.com/cran2367/pca-autoencoder-relationship/blob/master/pca-autoencoder-relationship.ipynb)
结论
因此,自动编码器模型是不适定的。不适定模型没有稳健的估计。这不利地影响了它的测试精度,即新数据的重建误差。
最近的几项研究进展是建立和利用正交条件来提高深度学习模型的性能。一些研究方向参考[ 4 ]和[ 5 ]。
在续集第二部分中,我们将实现自定义约束,将上述从 PCA 得到的属性合并到自动编码器中。我们将会看到,添加约束改善了测试重构误差。
参考
- 在 Keras 中构建自动编码器
- 理解主成分分析
- 主成分分析:利用重构误差的算法(第 15 页)。
- 黄,雷等.正交权重归一化:深度神经网络中多重相关 stiefel 流形上的优化解。第三十二届 AAAI 人工智能大会。2018.
- 使用内省对抗网络的神经照片编辑。arXiv 预印本 arXiv:1609.07093 (2016)。
构建正确的自动编码器——使用 PCA 原理进行调整和优化。第二部分
在第一部分的延续中,我们将在这里定义并实现自定义约束,以构建一个适定的自动编码器。一个适定的自动编码器是一个正则化的模型,它改善了测试重建误差。
<了解深度学习,了解更多> >
在第一部分中,我们了解到 PCA 和自动编码器在架构上有相似之处。但是尽管如此,自动编码器本身并不具有 PCA 属性,例如正交性。我们知道,结合 PCA 属性将为自动编码器带来显著的好处,例如解决消失和爆炸梯度,以及通过正则化进行过拟合。
基于此,我们希望自动编码器继承的属性是:
- 捆绑重物,
- 正交权重,
- 不相关的特征,以及
- 单位定额。
在本文中,我们将
- 实现自定义层和约束来合并它们。
- 演示它们是如何工作的,以及它们带来的重构误差的改善。
这些实现将能够构造一个适定的自动编码器并对其进行优化。在我们的示例中,优化将重建误差提高了 50%以上。
注意:正则化技术,如辍学,是普遍使用的。但是没有一个适定的模型,这些方法需要更长的时间来优化。
下一节详细展示了该实现。读者可以跳至关键要点部分进行简要总结。
一个适配的自动编码器
Figure 1. Apply constraints for a well-posed Autoencoder.
我们将为随机生成的数据集开发一个具有五个特征的自动编码器。我们将数据集分为训练集和测试集。当我们添加约束时,我们将使用测试数据重构误差来评估性能。
本文包含实现细节,以帮助从业者尝试各种选择。完整的代码显示在这里。
导入库
from numpy.random import seed
seed(123)
from tensorflow import set_random_seed
set_random_seed(234)import sklearn
from sklearn import datasets
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler, MinMaxScaler
from sklearn import decomposition
import scipyimport tensorflow as tf
from keras.models import Model, load_model
from keras.layers import Input, Dense, Layer, InputSpec
from keras.callbacks import ModelCheckpoint, TensorBoard
from keras import regularizers, activations, initializers, constraints, Sequential
from keras import backend as K
from keras.constraints import UnitNorm, Constraint
生成和准备数据
n_dim = 5
cov = sklearn.datasets.make_spd_matrix(n_dim, random_state=None)mu = np.random.normal(0, 0.1, n_dim)n = 1000X = np.random.multivariate_normal(mu, cov, n)X_train, X_test = train_test_split(X, test_size=0.5, random_state=123)# *Scale the data between 0 and 1.*
scaler = MinMaxScaler()
scaler.fit(X_train)X_train_scaled = scaler.transform(X_train)X_test_scaled = scaler.transform(X_test)X_train_scaled
估算参数
nb_epoch = 100
batch_size = 16
input_dim = X_train_scaled.shape[1] *#num of predictor variables,*
encoding_dim = 2
learning_rate = 1e-3
基线模型
encoder = Dense(encoding_dim, activation="linear", input_shape=(input_dim,), use_bias = **True**)
decoder = Dense(input_dim, activation="linear", use_bias = **True**)
autoencoder = Sequential()
autoencoder.add(encoder)
autoencoder.add(decoder)
autoencoder.compile(metrics=['accuracy'],
loss='mean_squared_error',
optimizer='sgd')
autoencoder.summary()
autoencoder.fit(X_train_scaled, X_train_scaled,
epochs=nb_epoch,
batch_size=batch_size,
shuffle=**True**,
verbose=0)
Figure 2.1. Baseline Model Parameters.
基线重建误差
train_predictions = autoencoder.predict(X_train_scaled)
print('Train reconstrunction error**\n**', sklearn.metrics.mean_squared_error(X_train_scaled, train_predictions))
test_predictions = autoencoder.predict(X_test_scaled)
print('Test reconstrunction error**\n**', sklearn.metrics.mean_squared_error(X_test_scaled, test_predictions))
Figure 2.2. Baseline Autoencoder Reconstruction Error.
自动编码器优化
Keras 提供了各种层和约束。对于**单位定额,我们有一个可用的约束。**对于其他人,我们将构建自定义层和约束。
- 自定义图层:捆绑砝码。
有了这个自定义层,我们可以使编码器和解码器的权重相等。数学上,解码器权重的转置等于编码器权重(等式)。第一部分 7a)。
**class** **DenseTied**(Layer):
**def** __init__(self, units,
activation=**None**,
use_bias=**True**,
kernel_initializer='glorot_uniform',
bias_initializer='zeros',
kernel_regularizer=**None**,
bias_regularizer=**None**,
activity_regularizer=**None**,
kernel_constraint=**None**,
bias_constraint=**None**,
tied_to=**None**,
**kwargs):
self.tied_to = tied_to
**if** 'input_shape' **not** **in** kwargs **and** 'input_dim' **in** kwargs:
kwargs['input_shape'] = (kwargs.pop('input_dim'),)
super().__init__(**kwargs)
self.units = units
self.activation = activations.get(activation)
self.use_bias = use_bias
self.kernel_initializer = initializers.get(kernel_initializer)
self.bias_initializer = initializers.get(bias_initializer)
self.kernel_regularizer = regularizers.get(kernel_regularizer)
self.bias_regularizer = regularizers.get(bias_regularizer)
self.activity_regularizer = regularizers.get(activity_regularizer)
self.kernel_constraint = constraints.get(kernel_constraint)
self.bias_constraint = constraints.get(bias_constraint)
self.input_spec = InputSpec(min_ndim=2)
self.supports_masking = **True**
**def** build(self, input_shape):
**assert** len(input_shape) >= 2
input_dim = input_shape[-1]
**if** self.tied_to **is** **not** **None**:
self.kernel = K.transpose(self.tied_to.kernel)
self._non_trainable_weights.append(self.kernel)
**else**:
self.kernel = self.add_weight(shape=(input_dim, self.units),
initializer=self.kernel_initializer,
name='kernel',
regularizer=self.kernel_regularizer,
constraint=self.kernel_constraint)
**if** self.use_bias:
self.bias = self.add_weight(shape=(self.units,),
initializer=self.bias_initializer,
name='bias',
regularizer=self.bias_regularizer,
constraint=self.bias_constraint)
**else**:
self.bias = **None**
self.input_spec = InputSpec(min_ndim=2, axes={-1: input_dim})
self.built = **True**
**def** compute_output_shape(self, input_shape):
**assert** input_shape **and** len(input_shape) >= 2
output_shape = list(input_shape)
output_shape[-1] = self.units
**return** tuple(output_shape)
**def** call(self, inputs):
output = K.dot(inputs, self.kernel)
**if** self.use_bias:
output = K.bias_add(output, self.bias, data_format='channels_last')
**if** self.activation **is** **not** **None**:
output = self.activation(output)
**return** output
带绑定解码器的自动编码器。
encoder = Dense(encoding_dim, activation="linear", input_shape=(input_dim,), use_bias = True)
decoder = DenseTied(input_dim, activation="linear", tied_to=encoder, use_bias = True)autoencoder = Sequential()
autoencoder.add(encoder)
autoencoder.add(decoder)autoencoder.compile(metrics=['accuracy'],
loss='mean_squared_error',
optimizer='sgd')
autoencoder.summary()autoencoder.fit(X_train_scaled, X_train_scaled,
epochs=nb_epoch,
batch_size=batch_size,
shuffle=True,
verbose=0)
观察结果
1a。同等重量。
w_encoder = np.round(np.transpose(autoencoder.layers[0].get_weights()[0]), 3)
w_decoder = np.round(autoencoder.layers[1].get_weights()[0], 3)
print('Encoder weights**\n**', w_encoder)
print('Decoder weights**\n**', w_decoder)
1b。偏见是不同的。
b_encoder = np.round(np.transpose(autoencoder.layers[0].get_weights()[1]), 3)
b_decoder = np.round(np.transpose(autoencoder.layers[1].get_weights()[0]), 3)
print('Encoder bias\n', b_encoder)
print('Decoder bias\n', b_decoder)
2。自定义约束:权重正交性。
**class** **WeightsOrthogonalityConstraint** (Constraint):
**def** __init__(self, encoding_dim, weightage = 1.0, axis = 0):
self.encoding_dim = encoding_dim
self.weightage = weightage
self.axis = axis
**def** weights_orthogonality(self, w):
**if**(self.axis==1):
w = K.transpose(w)
**if**(self.encoding_dim > 1):
m = K.dot(K.transpose(w), w) - K.eye(self.encoding_dim)
**return** self.weightage * K.sqrt(K.sum(K.square(m)))
**else**:
m = K.sum(w ** 2) - 1.
**return** m
**def** __call__(self, w):
**return** self.weights_orthogonality(w)
对编码器和解码器权重应用正交性。
encoder = Dense(encoding_dim, activation="linear", input_shape=(input_dim,), use_bias=**True**, kernel_regularizer=WeightsOrthogonalityConstraint(encoding_dim, weightage=1., axis=0))
decoder = Dense(input_dim, activation="linear", use_bias = **True**, kernel_regularizer=WeightsOrthogonalityConstraint(encoding_dim, weightage=1., axis=1))
autoencoder = Sequential()
autoencoder.add(encoder)
autoencoder.add(decoder)
autoencoder.compile(metrics=['accuracy'],
loss='mean_squared_error',
optimizer='sgd')
autoencoder.summary()
autoencoder.fit(X_train_scaled, X_train_scaled,
epochs=nb_epoch,
batch_size=batch_size,
shuffle=**True**,
verbose=0)
观察。
2a。编码器和解码器的权重接近正交。
w_encoder = autoencoder.layers[0].get_weights()[0]
print('Encoder weights dot product**\n**', np.round(np.dot(w_encoder.T, w_encoder), 2))
w_decoder = autoencoder.layers[1].get_weights()[0]
print('Decoder weights dot product**\n**', np.round(np.dot(w_decoder, w_decoder.T), 2))
3。自定义约束:不相关的编码特征。
对于不相关的特征,我们将对编码特征协方差的非对角元素的和施加惩罚。
**class** **UncorrelatedFeaturesConstraint** (Constraint):
**def** __init__(self, encoding_dim, weightage = 1.0):
self.encoding_dim = encoding_dim
self.weightage = weightage
**def** get_covariance(self, x):
x_centered_list = []
**for** i **in** range(self.encoding_dim):
x_centered_list.append(x[:, i] - K.mean(x[:, i]))
x_centered = tf.stack(x_centered_list)
covariance = K.dot(x_centered, K.transpose(x_centered)) / tf.cast(x_centered.get_shape()[0], tf.float32)
**return** covariance
*# Constraint penalty*
**def** uncorrelated_feature(self, x):
**if**(self.encoding_dim <= 1):
**return** 0.0
**else**:
output = K.sum(K.square(
self.covariance - tf.math.multiply(self.covariance, K.eye(self.encoding_dim))))
**return** output
**def** __call__(self, x):
self.covariance = self.get_covariance(x)
**return** self.weightage * self.uncorrelated_feature(x)
在自动编码器中应用约束。
encoder = Dense(encoding_dim, activation="linear", input_shape=(input_dim,), use_bias = **True**, activity_regularizer=UncorrelatedFeaturesConstraint(encoding_dim, weightage = 1.))
decoder = Dense(input_dim, activation="linear", use_bias = **True**)
autoencoder = Sequential()
autoencoder.add(encoder)
autoencoder.add(decoder)
autoencoder.compile(metrics=['accuracy'],
loss='mean_squared_error',
optimizer='sgd')
autoencoder.summary()
autoencoder.fit(X_train_scaled, X_train_scaled,
epochs=nb_epoch,
batch_size=batch_size,
shuffle=**True**,
verbose=0)
观察。
3a。我们有较少相关的编码特征。施加这种惩罚更加困难。可以探索更强的约束函数。
encoder_layer = Model(inputs=autoencoder.inputs, outputs=autoencoder.layers[0].output)
encoded_features = np.array(encoder_layer.predict(X_train_scaled))
print('Encoded feature covariance\n', np.round(np.cov(encoded_features.T), 3))
4。约束:单位定额。
UnitNorm
约束是在 Keras 中预先构建的。我们将在编码器和解码器层应用这种约束。值得注意的是,我们为编码器层保留了axis=0
,为解码器层保留了axis=1
。
encoder = Dense(encoding_dim, activation="linear", input_shape=(input_dim,), use_bias = True, kernel_constraint=UnitNorm(**axis=0**))
decoder = Dense(input_dim, activation="linear", use_bias = True, kernel_constraint=UnitNorm(**axis=1**))autoencoder = Sequential()
autoencoder.add(encoder)
autoencoder.add(decoder)autoencoder.compile(metrics=['accuracy'],
loss='mean_squared_error',
optimizer='sgd')
autoencoder.summary()autoencoder.fit(X_train_scaled, X_train_scaled,
epochs=nb_epoch,
batch_size=batch_size,
shuffle=True,
verbose=0)
观察。
4.a .编码器和解码器沿编码轴的权重范数为,
w_encoder = np.round(autoencoder.layers[0].get_weights()[0], 2).T # W in Figure 3.
w_decoder = np.round(autoencoder.layers[1].get_weights()[0], 2) # W' in Figure 3.print('Encoder weights norm, \n', np.round(np.sum(w_encoder ** 2, axis = 1),3))
print('Decoder weights norm, \n', np.round(np.sum(w_decoder ** 2, axis = 1),3))
如前所述,规范并不完全是 1.0,因为这不是一个硬约束。
把所有东西放在一起
这里我们将把上述属性放在一起。根据问题的不同,这些属性的某种组合会比其他组合更好。
同时应用几个约束有时会损害评估。例如,在此处使用的数据集中,结合联系层、权重正交性和单位范数效果最佳。
encoder = Dense(encoding_dim, activation="linear", input_shape=(input_dim,), use_bias = True, kernel_regularizer=WeightsOrthogonalityConstraint(encoding_dim, weightage=1., axis=0), kernel_constraint=UnitNorm(axis=0))
decoder = DenseTied(input_dim, activation="linear", tied_to=encoder, use_bias = False)autoencoder = Sequential()
autoencoder.add(encoder)
autoencoder.add(decoder)autoencoder.compile(metrics=['accuracy'],
loss='mean_squared_error',
optimizer='sgd')
autoencoder.summary()autoencoder.fit(X_train_scaled, X_train_scaled,
epochs=nb_epoch,
batch_size=batch_size,
shuffle=True,
verbose=0)train_predictions = autoencoder.predict(X_train_scaled)
print('Train reconstrunction error\n', sklearn.metrics.mean_squared_error(X_train_scaled, train_predictions))
test_predictions = autoencoder.predict(X_test_scaled)
print('Test reconstrunction error\n', sklearn.metrics.mean_squared_error(X_test_scaled, test_predictions))
这里的完整地提到了模型调整的步骤和更多细节。
了解 PCA 和自动编码器之间的关系-cran 2367/PCA-自动编码器-关系
github.com](https://github.com/cran2367/pca-autoencoder-relationship/blob/master/pca-autoencoder-relationship.ipynb)
关键要点
重建误差的改善
Table 1. Summary of reconstruction errors.
- 基线模型测试数据的重建误差为 0.027。
- 将每个属性添加到 Autoencoder 减少了测试错误。改进范围从 19%的重量正交性到 67%的束缚重量。
- 不同的数据有不同的改进。
- 在我们的问题中,结合束缚权重、权重正交性和单位范数产生了具有最佳重构误差的最优模型。
- 尽管最佳模型中的误差大于仅具有束缚权重的模型,但这更稳定,因此更可取。
关键实施说明
绑重物
- 在绑定权重层
DenseTied
中,编码器和解码器中的偏差会有所不同。 - 要使所有重量完全相等,设置
use_bias=False
。
权重正交
kernel_regularizer
用于在层的权重上添加约束或正则化。- 正交轴对于编码器应该是按行的,
axis=0
,对于解码器应该是按列的,axis=1
。
不相关的编码特征
activity_regularizer
用于对层的输出特征应用约束。因此,这里使用它来将编码特征的非对角协方差约束为零。- 这个约束不强。也就是说,它不会将非对角协方差元素推到非常接近零的程度。
- 可以探索该约束的另一种定制。
单位定额
UnitNorm
编码器和解码器应该在不同的轴上。- 类似于权重正交性,这适用于编码器的行
axis=0
和解码器的列axis=1
。
通用
- 有两个类,
Regularizer
和Constraints
用于构建定制函数。实际上,对于我们的应用程序来说,两者是一样的。对于权重正交性和不相关特征,我们使用了Constraints
类。 - 所有三个约束——单位范数、不相关编码特征和权重正交性——都是软约束。也就是说,它们可以使模型权重和特征接近所需属性,但并不精确。例如,权重几乎是单位范数,而不是精确的。
在实践中,
- 合并这些属性的性能将因问题而异。
- 在不同的设置下,例如有和没有偏差,以及不同层上正交性和不相关特征约束的不同权重下,分别探索每个属性。
- 除此之外,在自动编码器中包括流行的正则化技术,例如丢弃层。
摘要
- 在前传第一部分中,我们学习了自动编码器应该从 PCA 继承的重要属性。
- 这里,我们实现了自定义层和约束来合并这些属性。
- 在表 1 中,我们展示了这些特性显著改善了测试重构误差。
- 正如在关键要点中提到的,我们需要反复试验来找到最佳设置。然而,这些试验是在一个具有解释意义的方向上进行的。
参考
用 Python 构建神经网络
机器学习和深度学习之旅
使用 NumPy 实现正向传播、反向传播
Figure 1: Neural Network
这个博客的目的是使用 python 中的包 NumPy 来建立一个神经网络。虽然像 Keras 和 Tensorflow 这样的成熟包使建立模型变得容易,但是值得自己编写向前传播、向后传播和梯度下降的代码,这有助于更好地理解该算法。
概述
Figure 2: Overview of forward propagation and backward propagation
上图显示了训练神经网络模型时,信息是如何流动的。输入 Xn 后,权重 W1 和偏差 B1 的线性组合被应用于 Xn 。接下来,应用激活函数进行非线性变换以得到 A1 。然后进入 A1 作为下一个隐藏层的输入。相同的逻辑应用于生成 A2 和 A3 。产生 A1 、 A2 和 A3 的过程称为正向传播。 A3 也作为神经网络的输出,与自变量 y 比较计算成本。然后计算成本函数的导数,得到 dA3 。对 W3 和 B3 求 dA3 的偏导数,得到 dW3 和 dB3 。同样的逻辑适用于得到 dA2 、 dW2 、 dB2 、 dA1 、 dW1 和 dB1 。生成导数列表的过程称为反向传播。最后应用梯度下降并更新参数。然后,新一轮迭代从更新的参数开始。该算法不会停止,直到它收敛。
创建测试数据
创建一小组测试数据来验证所创建的功能。
初始化参数
在参数初始化阶段,权重被初始化为接近零的随机值。如果权重接近于零,则 sigmoid 的操作部分大致是线性的,因此神经网络崩溃为近似线性的模型[1]sigmoid 函数在零点附近的梯度较陡,利用梯度下降可以快速更新参数。不要使用零和大的权重,这会导致糟糕的解决方案。
我在 Excel 中手动计算了一次神经网络的迭代训练,这有助于您验证每一步创建的函数的准确性。下面是参数初始化的输出。
Table 1: Parameters Initialization Testing Result
正向传播
在神经网络中,输入 Xn 被输入,信息通过整个网络向前流动。输入 Xn 提供初始信息,该信息向上传播到每层的隐藏单元,并最终产生预测。这个过程称为正向传播。正向传播包括两个步骤。第一步是权重和来自最后一层的输出(或输入 Xn )的线性组合,以生成 Z 。第二步是应用激活函数进行非线性变换。
Table 2: Matrix Calculation in forward propagation
第一步,你需要注意投入和产出的维度。假设您有一个维度为[2,3]的输入矩阵 X ,矩阵中的一列代表一条记录。隐含层有 5 个隐含单元,所以权重矩阵 W 的维数为[5,2]。偏置 B 的尺寸为【5,1】。通过应用矩阵乘法,我们可以得到维数为[5,3]的输出矩阵 Z。计算的细节可以在上表中看到。
Table 3: How activation is applied in forward propagation
上表显示了激活函数是如何应用于 *Z 的每个分量的。*使用激活函数的原因是为了进行非线性变换。没有激活函数,无论模型有多少隐层,它仍然是一个线性模型。有几种流行且常用的激活函数,包括 ReLU、Leaky ReLU、sigmoid 和 tanh 函数。这些激活函数的公式和图形如下所示。
Figure 3: Activation Function
首先,你需要定义 sigmoid 和 ReLU 函数。然后为单层正向传播创建函数。最后,上一步中创建的函数被嵌套到名为完全正向传播的函数中。为简单起见,ReLU 函数用于前 N-1 个隐藏层,sigmoid 函数用于最后一个隐藏层(或输出层)。注意,在二分类问题的情况下,使用 sigmoid 函数;在多类分类问题的情况下,使用 softmax 函数。将每个隐藏层中计算的 Z 和 A 保存到缓存中,用于反向传播。
这是测试数据的函数输出。
Table 4: Forward Propagation Testing Result
价值函数
正向传播的输出是二元事件的概率。然后将概率与响应变量进行比较,计算成本。在分类问题中使用交叉熵作为代价函数。在回归问题中,均方误差被用作成本函数。交叉熵的公式如下所示。
这是测试数据的函数输出。
Table 5: Cost Function Testing Result
反向传播
在训练期间,前向传播可以继续向前,直到它产生成本。反向传播是计算成本函数的导数,并使用微积分中的链式法则将所有信息返回到每一层。
假设
和
然后
链式法则指出
激活函数的导数如下所示。
类似于正向传播。首先,你需要为 sigmoid 和 ReLU 的导数创建一个函数。然后定义一个单层反向传播的函数,计算 dW 、 dB 、 dA_prev 。 dA_prev 将被用作前一隐藏层反向传播的输入。最后,上一步中创建的函数被嵌套到名为完全反向传播的函数中。为了与正向传播对齐,前 N-1 个隐藏层使用 ReLU 函数,最后一个隐藏层或输出层使用 sigmoid 函数。你可以修改代码并添加更多的激活功能。将 dW 和 dB 保存到另一个缓存中,用于更新参数。
这是测试数据的函数输出。
Table 6: Backward Propagation Testing Result
更新参数
一旦从反向传播计算出梯度,就通过学习速率*梯度来更新当前参数。然后,更新的参数被用于新一轮的正向传播。
这是测试数据的函数输出。
Table 7: Parameter Update Testing Result
梯度下降的解释可以在我的博客里看到。
本博客将涵盖以下问题和主题:
towardsdatascience.com](/an-introduction-to-gradient-descent-c9cca5739307)
将功能堆叠在一起
为了训练神经网络模型,将前面步骤中创建的函数堆叠在一起。下表提供了功能摘要。
Table 8: Functions Summary
运行模式
首先使用 make_moons 函数创建两个交错的半圆数据。下面提供了数据的可视化。
Figure 4: Training Data
然后运行该函数来训练神经网络模型。下图显示了培训过程。成本在 8000 个时期后收敛,并且模型准确率收敛到 0.9。
Figure 5: Cost over Time
Figure 6: Accuracy over Time
下一步
图 5 和图 6 表明存在潜在的过拟合问题。您可以使用包括提前停止、退出和调整在内的方法来解决这个问题。除了 ReLU 和 sigmoid 函数之外,还可以通过添加其他激活函数来玩 model。本博客使用的是批量梯度下降,但也有很多改进的梯度下降算法如 Momentum、RMSprop、Adam 等。
摘要
虽然之前上过网上课程,读过书中的相关章节,但直到我亲自动手写博客,我才完全理解了这种奇特的方法。俗话说,教是最好的学习方法。希望你能从阅读这篇博客中受益。如果你有兴趣,请阅读我的其他博客。
这一系列博客将从理论和实现两个方面对深度学习进行介绍。
medium.com](https://medium.com/@songyangdetang_41589/table-of-contents-689c8af0c731)
参考
[1]特雷弗·哈斯蒂,罗伯特·蒂布拉尼,杰罗姆·弗里德曼,(2008),统计学习的要素
[2]伊恩·古德费勒,约舒阿·本吉奥,亚伦·库维尔,(2017) 深度学习
[3]https://www.coursera.org/specializations/deep-learning?
[4]https://en.wikipedia.org/wiki/Activation_function
[5]https://explained.ai/matrix-calculus/index.html
构建与购买——可扩展的机器学习基础设施
开源 ML 基础设施的路线图。
在这篇博客文章中,我们将看看机器学习平台由哪些部分组成,并将从头构建自己的基础设施与购买一个为您做所有事情的现成服务进行比较。
购买和构建基础架构的最终目标都是让您的数据科学团队尽可能多地花时间了解数据、构建模型并将其投入生产,从而提高他们的效率。相反,这意味着他们将在基础设施和样板代码上花费尽可能少的时间。为了实现这一点,大多数 ML 基础设施公司(如优步的米开朗基罗)都建立在以下原则之上:
- 集成您的数据科学家已经喜欢使用的工具
- 构建快速迭代实验的平台
- 尽可能自动管理输入数据和输出数据
- 通过提供自动审计跟踪,提供回滚实验的方法
- 支持贵公司使用的环境,从不同的云提供商到内部硬件
- 建立在抽象之上,这样你就可以在新技术出现时支持它们
- 从团队的角度和资源的角度来设计可伸缩性,这样你就可以支持数百个项目、人员、数据源和计算单元。
- 与任何地方的任何东西集成,因此平凡的任务都可以自动化。
记住这些原则,让我们看看如何通过从头开始构建来实现这一点。
构建可扩展的机器学习基础设施
有效的机器学习基础设施需要构建它的团队具备各种各样的能力。您将为您的数据科学团队解决特定需求,因此让我们来看看典型的机器学习基础架构由什么组成。
机器编排
机器编排是你的机器学习基础设施的主干。编排引擎在你的机器编排部分的 UI 中实现自己,无论是通过集成在 ide、笔记本、CLI、API 或 UI 中。最先进的引擎优化资源使用,预测需求,处理排队等。您可以将您的引擎基于虚拟机、Kubernetes 之类的平台,或者直接编排您自己的硬件。
记录保存、记录和版本控制
严格来说,版本控制并不是传统 ML 平台的一部分,但是由于 GDPR 等法规,版本控制变得越来越重要。为了让您的数据科学家只需点击一下鼠标就能重现实验,您需要对数据、参数、硬件和软件环境、日志等进行版本控制和记录。一个很好的起点是使用 Git 进行代码版本控制,使用 Docker 进行环境控制。在下面的图片中可以找到版本的起点。
项目和用户管理
您需要将实验纳入项目中,并管理用户和团队的权限。使用 Azure Active Directory 或 Hadoop Ranger 提供的访问控制,或者自己构建一些东西。访问控制还应该定义用户可以使用哪些资源、不同用户如何对作业进行排队、哪些数据对谁可用等等。您将希望消除孤岛并实现透明,以便您的数据科学家可以相互学习。
自动化 ML 管道
机器学习管道是一个概念,其中工作被分成不同的阶段,从探索开始,继续进行批处理、规范化、培训、部署和中间的许多其他步骤。您需要构建对将结果从一个步骤传递到下一个步骤的支持。当数据湖中的数据发生重大变化时,一些高级触发器可能会自动重新运行部分管道。从基于 Git 的低级方法到 Apache 气流和 ML 气流管道,有几种解决方案,您可能想要探索并组合在一起。
整合,整合,整合!
数据科学家应该能够继续使用他们喜欢的工具和框架,无论是他们最喜欢的 IDE、笔记本还是其他任何东西。根据具体情况,还应该有不同的与基础设施交互的方式。最佳实践是在开放 API 的基础上构建一切,然后可以从 CLI 和 web UI 访问,甚至直接从 Jupyter notebook 访问。对于超参数调优,您可能需要考虑外部优化器,比如 SigOpt 。
可视化正在进行的执行
当数据科学家使用模型时,他们需要了解各个步骤是如何进行的,无论是训练精度还是数据准备。为此,您需要构建一种以有意义的方式显示实时训练数据的方法。解决方案可以是定制的可视化库或例如 TensorBoard 或权重&偏差。
推理部署
ML 管道的最后一部分是部署。您可能不想在实际生产环境中进行部署,但要确保您的数据科学团队在为将预测模型集成到您的业务应用程序中的软件团队部署新模型时能够自给自足。由于开销和缓慢的模型开发周期,将模型交付给 IT 部门的旧方法从长远来看是行不通的。Kubernetes clusters 是业内事实上的云推理部署标准。也可以看看 AWS 的弹性推理,谢顿核心或 Azure 推理引擎与 ONNX 的支持。
购买可扩展的机器学习基础设施
不仅自己构建基础设施需要几年的时间(相信我,我们有历史可以证明这一点),而且随着技术的发展和新技术的支持,您还需要不断开发平台。
Valohai 是一个完整的可扩展的机器学习基础设施服务,可以为您的团队扩展,从 1 到 1000 名数据科学家。Valohai 的一切都是围绕项目和团队构建的,它可以从内部安装扩展到混合云和 Microsoft Azure、AWS 和 Google Cloud 中的全云解决方案。Valohai 为您处理机器编排,并自动跟踪您或您公司中的任何人曾经进行过的每个实验——从数据、代码、超参数、日志、软件环境和库、硬件等等。
Valohai 基于开放 API 构建,通过现成的 CLI、直观的 Web UI 和 Jupyter 笔记本集成,集成到您当前的工作流程中。借助自动化管道,您无需手动操作即可开始批量作业并运行培训流程的每一步。元数据的交互式可视化有助于您的团队了解其他人在做什么,并跟踪他们的模型,直到他们准备好将这些模型投入生产。
然而,有一点是肯定的,ML 基础设施是你的团队生产力中最重要的部分。在最好的情况下,它将减少多达 90%的模型开发时间。
最初发表于【blog.valohai.com】。
在大型数据集上构建 XGBoost / LightGBM 模型——可能的解决方案是什么?
XGBoost 和 LightGBM 已经在许多表格数据集上被证明是性能最好的 ML 算法。但是当数据庞大时,我们如何使用它们呢?
XGBoost 和 LightGBM 在最近所有的 kaggle 表格数据竞争中占据了主导地位。只需进入任何竞赛页面(表格数据)并检查内核,你就会看到。在这些比赛中,数据并不“庞大”——好吧,不要告诉我你正在处理的数据是巨大的,如果它可以在你的笔记本电脑上训练的话。对于这些情况,Jupyter notebook 足够用于 XGBoost 和 LightGBM 模型构造。
当数据变得更大,但不是超级大,而你仍然想坚持使用 Jupyter 笔记本,比方说,来构建模型——一种方法是使用一些内存减少技巧(例如,ArjanGroen 的代码:https://www . ka ggle . com/arjanso/reducing-data frame-memory-size-by-65);或者使用云服务,比如在 AWS 上租一台 EC2。例如,r5.24xlarge 实例拥有 768 GiB 内存,成本为 6 美元/小时,我认为它已经可以处理大量数据,您的老板认为它们真的很“大”。
但是如果数据更大呢?
我们需要分布式机器学习工具。据我所知,如果我们想使用可伸缩的 XGBoost 或 LightGBM,我们有以下几种选择:
1 XGBoost 4j on Scala-Spark
2 light GBM on Spark(py Spark/Scala/R)
3 XGBoost with H2O . ai
4 XGBoost on Amazon SageMaker
我想根据我的个人经验指出每种工具的一些问题,如果您想使用它们,我会提供一些资源。如果你也有类似的问题/你是如何解决的,我也很高兴向你学习!请在下面评论。
在我开始使用这些工具之前,有一些事情需要事先了解。
XGBoost 与 LightGBM
XGBoost 是一种非常快速和准确的 ML 算法,但它现在受到了 LightGBM 的挑战,light GBM 运行得更快(对于某些数据集,根据其基准测试,它快了 10 倍),具有相当的模型准确性,并且有更多的超参数供用户调整。速度上的关键差异是因为 XGBoost 一次将树节点拆分一层,而 LightGBM 一次只拆分一个节点。
因此,XGBoost 开发人员后来改进了他们的算法,以赶上 LightGBM,允许用户也在逐叶分割模式下运行 XGBoost(grow _ policy = ’ loss guide ')。现在,XGBoost 在这一改进下速度快了很多,但根据我在几个数据集上的测试,LightGBM 的速度仍然是 XGB 的 1.3-1.5 倍。(欢迎分享你的测试结果!)
读者可以根据自己的喜好选择任何一个选项。这里再补充一点:XGBoost 有一个 LightGBM 没有的特性——“单调约束”。这将牺牲一些模型精度并增加训练时间,但可以提高模型的可解释性。(参考:https://xgboost . readthe docs . io/en/latest/tutorials/monotonic . html和【https://github.com/dotnet/machinelearning/issues/1651】T2)
在渐变提升树中找到“甜蜜点”
对于随机森林算法,建立的树越多,模型的方差越小。但是在某种程度上,你不能通过添加更多的树来进一步改进这个模型。
XGBoost 和 LightGBM 不是这样工作的。当树的数量增加时,模型精度不断提高,但是在某个点之后,性能开始下降——过度拟合的标志;随着树越建越多,性能越差。
为了找到‘甜蜜点’,你可以做交叉验证或者简单的做训练-验证集分裂,然后利用提前停止时间找到它应该停止训练的地方;或者,你可以用不同数量的树(比如 50、100、200)建立几个模型,然后从中选出最好的一个。
如果你不在乎极端的性能,你可以设置一个更高的学习率,只构建 10-50 棵树(比如说)。它可能有点不合适,但你仍然有一个非常准确的模型,这样你可以节省时间找到最佳的树的数量。这种方法的另一个好处是模型更简单(构建的树更少)。
1.Scala-Spark 上的 XGBoost4j
如果读者打算选择这个选项,https://xgboost . readthedocs . io/en/latest/JVM/xgboost 4j _ spark _ tutorial . html是一个很好的起点。我想在这里指出几个问题(在本文发布时):
- XGBoost4j 不支持 Pyspark。
- XGBoost4j 不支持逐叶分割模式,因此速度较慢。https://github.com/dmlc/xgboost/issues/3724
- 因为是在 Spark 上,所以所有缺失值都要进行插补(vector assembler 不允许缺失值)。这可能会降低模型精度。http://docs . H2O . ai/H2O/latest-stable/H2O-docs/data-science/GBM-FAQ/missing _ values . html
- 早期停止可能仍然包含 bug。如果你关注他们在 https://github.com/dmlc/xgboost/releases的最新发布,你会发现他们最近仍在修复这些漏洞。
2.Spark 上的 light GBM(Scala/Python/R)
基于我个人经验的主要问题:
- 缺少文档和好的例子。https://github . com/Azure/mmspark/blob/master/docs/light GBM . MD
- 所有缺失的值都必须进行估算(类似于 XGBoost4j)
- 我在 spark cross validator 的“提前停止”参数上也有问题。(为了测试它是否正常工作,选择一个较小的数据集,选择一个非常大的回合数,提前停止= 10,并查看训练模型需要多长时间。训练完成后,将模型精度与使用 Python 构建的模型进行比较。如果过拟合得很差,很可能早期停止根本不起作用。)
一些示例代码(不包括矢量汇编程序):
from mmlspark import LightGBMClassifier
from pyspark.ml.evaluation import BinaryClassificationEvaluator
from pyspark.ml.tuning import CrossValidator, ParamGridBuilderlgb_estimator = LightGBMClassifier(learningRate=0.1,
numIterations=1000,
earlyStoppingRound=10,
labelCol="label")paramGrid = ParamGridBuilder().addGrid(lgb_estimator.numLeaves, [30, 50]).build()eval = BinaryClassificationEvaluator(labelCol="label",metricName="areaUnderROC")crossval = CrossValidator(estimator=lgb_estimator,
estimatorParamMaps=paramGrid,
evaluator=eval,
numFolds=3) cvModel = crossval.fit(train_df[["features", "label"]])
3.H2O.ai 上的 XGBoost
这是我个人最喜欢的解决方案。该模型可以使用 H2O.ai 构建,集成在 py sparking Water(H2O . ai+py spark)管道中:
https://www . slide share . net/0x data/productionizing-H2O-models-using-sparking-Water-by-jakub-hava
很容易建立一个带有交叉验证的优化轮数的模型
# binary classificationfeatures = ['A', 'B', 'C']
train['label'] = train['label'].asfactor() # train is an H2O framecv_xgb = H2OXGBoostEstimator(
ntrees = 1000,
learn_rate = 0.1,
max_leaves = 50,
stopping_rounds = 10,
stopping_metric = "AUC",
score_tree_interval = 1,
tree_method="hist",
grow_policy="lossguide",
nfolds=5,
seed=0)cv_xgb.train(x = features, y = 'label', training_frame = train)
并且 XGBoost 模型可以用cv_xgb.save_mojo()
保存在 Python 中使用。如果您想以 h2o 格式保存模型,请使用h2o.save_model()
。
我唯一的抱怨是保存的模型(用save.mojo
保存的那个)不能和 SHAP 包一起使用来生成 SHAP 特性重要性(但是 XGBoost 特性重要性,.get_fscore()
,工作正常)。似乎最初的 XGBoost 包有一些问题。
https://github.com/slundberg/shap/issues/464
https://github.com/dmlc/xgboost/issues/4276
(更新:似乎他们刚刚在其最新版本中实现了 SHAP-https://github . com/h2oai/H2O-3/blob/373 ca 6 B1 BC 7d 194 C6 c 70 e 1070 F2 F6 f 416 f 56 B3 d 0/changes . MD)
4.SageMaker 上的 XGBoost
这是 AWS 的一个非常新的解决方案。两个主要特性是使用贝叶斯优化的自动超参数调整,并且该模型可以作为端点部署。在他们的 Github 上可以找到几个例子:https://Github . com/aw slabs/Amazon-sage maker-examples/tree/master/introduction _ to _ applying _ machine _ learning。以下是我对此的一些担忧:
- 与其他解决方案相比,参数调整工具对用户(数据科学家)不太友好:
https://github . com/aw slats/Amazon-sagemaker-examples/blob/master/hyperparameter _ tuning/xgboost _ direct _ marketing/hpo _ xgboost _ direct _ marketing _ sagemaker _ APIs . ipynb和
https://github . com/aw slats/Amazon-sagemaker-examples/blob/master/hyperparameter _ tuning/Analyze _ results/HPO - 贝叶斯优化是否是调优 XGB 参数的最佳选择还是未知数。如果你检查了文件,梯度推进树没有被提及/测试。https://docs . AWS . Amazon . com/sage maker/latest/DG/automatic-model-tuning-how-it-works . html
- 该参数通过单个验证集进行调整,而不是交叉验证。
- 我还没想好如何在 Python XGBoost 中使用其内置的 XGBoost 算法训练出来的模型神器。
但是除了这些问题,我们仍然可以利用它的端点特性。你可以在任何地方训练你的 XGB 模型,从 Amazon ECR(Elastic Container Registry)把它放在 XGBoost 映像中,然后把它部署成一个端点。
XGBoost / LightGBM 是相当新的 ML 工具,它们都有潜力变得更强。开发人员已经做了出色的工作,创造了这些工具,使人们的生活变得更容易。我在这里指出我的一些观察和分享我的经验,希望它们能成为更好、更易用的工具。
建立你的第一个计算机视觉项目——狗的品种分类
在不到 30 分钟的时间内开始构建您的第一个计算机视觉项目。
Photo by Joe Caione on Unsplash
对我们人类来说,区分不同品种的狗是很容易的。如果你说的是 10-20 种受欢迎的狗品种。当我们谈论 100 多种狗时,情况就完全不同了。对于一个人来说,要正确并一致地对大量品种进行分类,我们需要一种不同于单纯记忆的方法。我们需要开始提取与不同品种相对应的“特征”,如皮毛颜色、耳朵形状、面部形状、尾巴长度等。即使这样,我们也需要记住什么品种有什么特征,这不是一件容易或有趣的任务。
Image credit: It is hard to classify a large number of dog breeds
人类难以识别大量物种是计算机比我们更好的一个完美例子。这并不是说天网要来杀我们所有人;我认为我们现在还不应该为此担心。许多人认为,两个种族一起工作的混合方式比任何一个种族单独工作都要好,而不是用机器来代替人类。你可以在 Andrew McAfee 所著的《机器、平台、人群》一书中了解更多相关信息。
在这个项目中,我将带你经历建立和训练卷积神经网络(CNN)的步骤,该网络将对 133 种不同的狗进行分类。这个项目是我的数据科学家 Nanodegree 的一部分,你可以在这里找到更多关于它的信息。这个项目的代码可在我的 GitHub repo 上获得,如果你想跟着做,请确保你安装了所需的包。
你需要知道的是
计算机如何理解图像
由于数百万年的进化完善了我们的眼睛和大脑的视觉能力,我们非常擅长识别事物。计算机将图像“视为”一系列数字。屏幕上的一个像素由从 0 到 255 的三个数字表示。每个数字都表示为红色、绿色和蓝色的强度。一张 1024768 的图片可以表示为一个 1024768*3 的矩阵,相当于一系列 239,616 个数字。
Image credit: Image represented as numbers
传统上,计算机很难识别或分类图像中的对象。随着计算能力和神经网络研究的最新进展,计算机现在可以在许多特定任务上具有人类级别的视觉能力。
什么是神经网络
有许多很好的资源可以解释什么是神经网络。下面是我最喜欢的解释,借用这个帖子:
神经网络由以下组件组成
- 一个输入层、T3、x
- 任意数量的隐藏层
- 一个输出层,, ŷ ,
- 一组权重和在各层之间偏向, W 和 b
- 一个选择激活功能用于每个隐藏层,。在本教程中,我们将使用一个 Sigmoid 激活函数。
下图显示了 2 层神经网络的架构(注意,在计算神经网络的层数时,输入层通常被排除在外
Image credit: A simplified neural network architecture
详细解释神经网络如何工作超出了这篇文章的范围。你可以在 Medium 上找到许多解释神经网络如何工作的优秀文章。对于我们的狗分类项目,我们的输入层将是用数字表示的狗图像。隐藏层将是许多具有权重和偏差的层,这些权重和偏差将在我们训练模型时更新。输出将是我们 133 个犬种的一系列概率。
虽然对于各种机器学习问题有不同的神经网络架构,但是卷积神经网络(CNN)是用于图像分类问题的最流行的一种。
什么是卷积神经网络
CNN 是通常用于图像分类问题的神经网络的架构。典型的架构包括一个输入层、几个卷积层、一个池层、一个全连接层(FC)和一个输出层。你可以在这里和这里阅读 CNN 更全面的解释。
Image credit: Architecture of a CNN
Image credit: Visualization of a convolutional layer
什么是迁移学习
迁移学习利用解决一个问题时获得的知识,并将其应用于另一个不同但相关的问题。
现代卷积网络是在 ImageNet 这样的大型数据集上训练的,需要数周才能完成。所以很多时候,你并不想自己去训练一个全新的 ConvNet。
有很多著名的预训练模型,比如 VGG-16,雷斯网-50,Inception,Xception 等。这些模型已经在大型数据集上进行了训练,您可以利用特征提取步骤,并将它们作为特征提取器应用于您的特定问题。你可以在这里阅读更多关于迁移学习的内容。
Image credit: Visualization of transfer learning architecture
准备数据
获取数据
你可以在这里下载数据集。该数据集包含 8,351 张狗图像,133 个狗品种,分为 6,680 张用于训练的图像,836 张用于测试的图像,以及 835 张用于验证的图像。
注意,如果你有一个 NVIDIA GPU,你应该遵循这个教程。关于 CPU 的培训需要很多时间。或者,你可以在这里免费使用谷歌实验室。
可以按如下方式加载标签和文件路径的键值对字典:
***from** **sklearn.datasets** **import** load_files
**from** **keras.utils** **import** np_utils
**import** **numpy** **as** **np**
**from** **glob** **import** glob*# define function to load train, test, and validation datasets*
**def** load_dataset(path):
data = load_files(path)
dog_files = np.array(data['filenames'])
dog_targets = np_utils.to_categorical(np.array(data['target']), 133)
**return** dog_files, dog_targets*# load train, test, and validation datasets*
train_files, train_targets = load_dataset('dogImages/train')
valid_files, valid_targets = load_dataset('dogImages/valid')
test_files, test_targets = load_dataset('dogImages/test')*
不同的图像可能有不同的大小。以下代码将输入调整为 224224 像素的图像,并将其作为 numpy 系列加载到内存中:*
***from** **keras.preprocessing** **import** image
**from** **tqdm** **import** tqdm**def** path_to_tensor(img_path):
*# loads RGB image as PIL.Image.Image type*
img = image.load_img(img_path, target_size=(224, 224))
*# convert PIL.Image.Image type to 3D tensor with shape (224, 224, 3)*
x = image.img_to_array(img)
*# convert 3D tensor to 4D tensor with shape (1, 224, 224, 3) and return 4D tensor*
**return** np.expand_dims(x, axis=0)**def** paths_to_tensor(img_paths):
list_of_tensors = [path_to_tensor(img_path) **for** img_path **in** tqdm(img_paths)]
**return** np.vstack(list_of_tensors)*
预处理数据
我们需要标准化我们的数据,以消除测量单位。归一化可以帮助我们的模型更好地比较不同尺度的数据。
***from** **PIL** **import** ImageFile
ImageFile.LOAD_TRUNCATED_IMAGES = **True***# pre-process the data for Keras*
train_tensors = paths_to_tensor(train_files).astype('float32')/255
valid_tensors = paths_to_tensor(valid_files).astype('float32')/255
test_tensors = paths_to_tensor(test_files).astype('float32')/255*
培训用数据
**
It’s challenging even for humans to identify a dog breed
让我们来看看我们的一些数据。我们的例子表明,准确识别狗的品种是具有挑战性的,即使对人类来说也是如此。还有许多其他现实世界的因素会影响我们的模型:
- ***灯光条件:*不同的灯光改变颜色的显示方式
- 目标定位:我们的狗可以帮助很多不同的姿势
- 相框:特写人像相框和全身照片非常不同
- ***缺失特征:*照片中并没有显示狗狗的所有特征
因此,传统方法不可能在计算机视觉任务中取得进展。让我们看看 CNN 和转移学习能做些什么。
创建一个你自己的模型
模型架构
用 Keras 创建 CNN 很简单。您可以用下面的代码定义您的模型架构。
***from** **keras.layers** **import** Conv2D, MaxPooling2D, GlobalAveragePooling2D
**from** **keras.layers** **import** Dropout, Flatten, Dense
**from** **keras.models** **import** Sequentialmodel = Sequential()
model.add(Conv2D(filters=16, kernel_size=2, padding='same',activation='relu',input_shape=(224,224,3)))
model.add(MaxPooling2D())
model.add(Conv2D(filters=32, kernel_size=2, padding='same',activation='relu'))
model.add(MaxPooling2D())
model.add(Conv2D(filters=64, kernel_size=2, padding='same',activation='relu'))
model.add(MaxPooling2D())
model.add(GlobalAveragePooling2D())
model.add(Dense(133,activation='softmax'))model.summary()*
我们的原始模型架构有一个形状为 2242243 的输入层。然后,输入馈入两个卷积层,接着是最大池层(用于下采样)。然后,输出被馈送到全局平均池层,以通过减少参数的数量来最小化过拟合。输出层为我们的品种返回 133 个概率。
Image credit: Visualization of pooling layers such as MaxPooling and AveragePooling
Our primitive model summary
韵律学
在这个项目中,我们的目标是正确预测狗的品种。因此,在每次迭代之后,我们使用准确性作为选择的度量来改进我们的模型。准确性就是我们的模型正确预测犬种的次数。
编译和训练模型
*然后需要编译我们的模型。我们使用 rmsprop 作为我们的优化器,*category _ cross entropy,作为我们的损失函数,准确性作为我们的度量。
*model.compile(optimizer='rmsprop', loss='categorical_crossentropy', metrics=['accuracy'])*
然后,我们可以使用以下代码训练模型:
***from** **keras.callbacks** **import** ModelCheckpointepochs = 5checkpointer = ModelCheckpoint(filepath='saved_models/weights.best.from_scratch.hdf5',
verbose=1, save_best_only=**True**)model.fit(train_tensors, train_targets,
validation_data=(valid_tensors, valid_targets),
epochs=epochs, batch_size=20, callbacks=[checkpointer], verbose=1)*
用五个纪元和大约八分钟,我们取得了 3.2297% 的准确率,比随机猜测(1/133 = 0.75%)要好。
虽然我们的模型比随机猜测的表现好得多,但 3.2297%的模型在现实中没有用。训练和调整我们的模型以达到有意义的准确性需要大量的时间、大量的数据集和大量的计算资源。幸运的是,我们可以使用迁移学习来减少训练时间,而不牺牲准确性。
使用迁移学习创建模型
获取瓶颈特征
Keras 提供了以下经过预先培训的最新架构,您可以在几分钟内使用它们:VGG-19、ResNet-50、Inception 和 Xception。在这个项目中,我们将使用 ResNet-50,但是您可以自己尝试其他体系结构。下面的代码下载预先训练好的模型并加载瓶颈特性。
***import** **requests**
url = 'https://s3-us-west-1.amazonaws.com/udacity-aind/dog-project/DogResnet50Data.npz'
r = requests.get(url)**with** open('bottleneck_features/DogResnet50Data.npz', 'wb') **as** f:
f.write(r.content)bottleneck_features = np.load('bottleneck_features/DogResnet50Data.npz')
train_Resnet50 = bottleneck_features['train']
valid_Resnet50 = bottleneck_features['valid']
test_Resnet50 = bottleneck_features['test']*
模型架构
借助预训练的 ResNet-50 模型,我们使用以下代码创建全连接(FC)层。我们还增加了两个下降层,以防止过度拟合。
*Resnet50_model = Sequential()
Resnet50_model.add(GlobalAveragePooling2D(input_shape=train_Resnet50.shape[1:]))
Resnet50_model.add(Dropout(0.3))
Resnet50_model.add(Dense(1024,activation='relu'))
Resnet50_model.add(Dropout(0.4))
Resnet50_model.add(Dense(133, activation='softmax'))
Resnet50_model.summary()*
Our model architecture using transfer learning looks like this
模型性能
使用迁移学习(ResNet-50),在二十个历元和不到两分钟的情况下,我们取得了 80.8612% 的测试准确率。这是对我们原始模型的巨大改进。
使用上面描述的模型,我创建了一个小的命令行应用程序。如果你输入一只狗的照片,这个应用程序会预测它的品种。如果你输入一个人的图像,比如你的一个朋友说,这个应用程序会预测最接近的匹配犬种。如果没有狗或人出现,应用程序将输出一个错误。你可以按照我的 GitHub repo 上的 README.md 文件中的说明进行操作。
结论
在这个项目中,我将带你了解一些构建你的第一个计算机视觉项目所需的核心概念。我解释了计算机如何理解图像,简要回顾了什么是神经网络、卷积神经网络、、和迁移学习。
*我们还从零开始构建并训练了一个 CNN 架构以及应用迁移学习来大幅提高我们模型的准确率**从 3.2%到 81%。*在做这个项目时,我对你如何利用迁移学习来提高准确性,同时显著减少培训时间印象深刻。以下是我们模型的一些预测:
Some predictions of our model
未来的改进
如果我有更多的时间,这里是这个项目的几个未来发展:
- ***扩充数据:*当前的实现只接受标准图像作为输入。因此,该模型在现实环境中可能不够健壮。我可以增加训练数据,使我们的模型不容易过度拟合。我可以通过随机裁剪、翻转、旋转训练数据来创建更多的训练样本。
- ***调优模型:*我还可以调优模型超参数,以达到更好的精度。一些用于调优的潜在超参数包括优化器、损失函数、激活函数、模型架构等。我也可以用 Keras 使用 Sklearn 的 GridSearch。
- ***尝试其他模型:*在这个项目中,我尝试在 VGG-16 和 ResNet-50 上训练模型。下一步可能是尝试使用不同的架构来训练模型,以及改变我们完全连接的层的架构。
- 创建一个 web/移动应用程序:我也可以用这个模型创建一个 web 应用程序或移动应用程序来取乐。
感谢你的阅读,希望你能学到东西:)
建立您的第一个时差数据警报
动手黑客
不仅要注重实践,还要更深入地融入你的工作流程
序
这个提醒极大地提高了我的工作效率,让我远离了多余的询问和工作,所以这篇文章不仅关注实践,也解释了我是如何将它融入到工作中的。
为了更容易使用,本文中的所有内容都已经打包到这个 Github 库中。
data alert 如何提高我的工作效率?
🔕对多余的询问保持沉默
你对利益相关者的提问感到很烦,他们也是。如果数据能够自己出现并解释这种情况,每个人都会很感激。
🏋️♂️立即追踪异常信号
异常信号出现时跟踪它比一个月后复查要容易得多。不要害怕转发信息并给你的同事/老板贴标签!
👨👧👧让您的团队充分参与进来!
一旦您注意到后端工程师和首席执行官正在讨论是什么原因导致警报发出异常信号,并为您标记营销经理…
你离成功很近了。
什么让数据预警清晰?
色彩管理
颜色是表明当前状态的最佳方式。红色代表警戒,黄色代表警告,蓝色代表沮丧…
Which style draws more attention with clarity?
情节很重要
一张图胜过千言万语,尤其对于没有统计学背景的同事来说,还是能体会到形势有多严峻的。
Don’t mess it up!
易接近
人类是最懒的动物,所以你的警报应该整合到原来的工作场所,比如 slack,而不是外部的网站或者仪表盘。
编码前:松弛应用设置
- 登录您的 slack 帐户。
- 在这里创建一个 Slack App 。
- 允许应用程序通过备用渠道上传文件(例如#图片存储)
- 允许在通道通知中发送传入的 webhook(例如#data-notify)
- 上传一个有趣的图标到你的 Slack 应用程序🤪
Make sure you have the same setting.
让我们编码
1。确保您的数据和绘图准备就绪
2.将绘图上传到时差
这是最有趣的(hack)部分。这个想法是:
- 将您的绘图保存为临时文件
- 将你的剧情发布到另一个 slack 频道,获得只能在 slack 上分享的私人网址!
- 重新分享到#general 或您喜欢的任何频道
3.🚥颜色+📊Plot +📝描述=📩完成的
你做到了,检查你的松弛频道!
如果没有,在这里处理您的错误代码。
替代方案:使用我开发的库
您可以通过克隆slacleoneir来完成数据预警,无需额外编码。
另外:用气流来安排你的剧本
Cronjob 简单而有用,但我个人建议用气流作为更好的替代品。
构建您的第一个开源 Python 项目
工作包的逐步指南
每个软件开发人员和数据科学家都应该经历制作软件包的练习。一路上你会学到很多东西。确保你有时间。🕰
制作一个开源 Python 包可能听起来令人生畏,但是你不需要成为一个头发斑白的老手。你也不需要一个精心制作的产品创意。你确实需要坚持和时间。希望这个指南能帮助你减少这两者的需求。😃
Build something beautiful 😃
在本文中,我们将通过每个步骤来制作一个基本的 Python 包。在以后的文章中,我们将建立自动化测试和文档构建。我们还将集成其他有用的应用程序来改进您的包开发。然后,您可以扩展您构建的内容以满足您的需求。
如果你想学习 Python,看看我的令人难忘的 Python 书籍。
本指南适用于使用 Python 3.7 的 macOS。现在一切正常,但事情变化很快,所以不能保证你在 2030 年读到这篇文章。在下面的代码中替换我的 _ 包、*我的 _ 文件、*等。用你们自己的名字。
我们开始吧!🚀
第一步:制定计划
我们最终计划在 Python 程序中使用一个非常简单的库。该软件包将允许用户轻松地将 Jupyter 笔记本转换成 HTML 文件或 Python 脚本。
我们的包的第一次迭代将允许用户调用打印语句的函数。
现在我们知道了我们想要做什么,我们需要决定如何命名这个包。
第二步:命名
给事物命名是很棘手的。名字应该是独一无二的,简短的,易记的。它们也应该全部小写,并且绝对不能有任何破折号或其他标点符号。不鼓励使用下划线。当你在构建你的包时,检查这个名字在 GitHub、Google 和 PyPI 上是否存在。
如果你非常希望你的包裹有一天会有 10,000 颗 GitHub 星,那么你可能需要检查这个名字在社交网络上是否可用。在这个例子中,我将把我的包命名为 notebookc ,因为它是可用的、简短的、半描述性的。😄
步骤 3:配置环境
确保安装并配置了 Python 3.7、GitHub 和 Homebrew。如果你需要这些,这里有详细信息:
计算机编程语言
在这里下载 Python 3.7 并安装。
开源代码库
如果你没有 GitHub 账户,请点击这里注册一个免费账户。点击查看如何安装和配置 Git 。你需要命令行工具。按照链接下载、安装它,设置您的用户名,并设置您的提交电子邮件地址。
公司自产自用
家酿是一个 Mac 专用的软件包管理器。这里的安装说明是。
Venv
从 Python 3.6 开始,推荐使用 venv 来创建你的虚拟环境进行包开发。使用 Python 管理虚拟环境有多种方法,并且这些建议也在不断发展。见此处的讨论,但小心掉进这个兔子洞。🕳
venv 从 Python 3.3 开始随 Python 一起安装。请注意,从 Python 3.4 开始, venv 将 pip 和 setuptools 安装到虚拟环境中。
使用以下命令创建 Python 3.7 虚拟环境:
python3.7 -m venv my_env
用你喜欢的任何名字替换 my_env 。像这样激活您的虚拟环境:
source my_env/bin/activate
现在,您应该会在终端提示符的最左边看到(my_env)
——或者您给虚拟环境起的名字。
当您完成开发后,使用deactivate
停用您的虚拟环境。
现在让我们在 GitHub 上设置一些东西。
步骤 4:在 GitHub 上创建组织
GitHub 是版本控制注册中心的市场领导者。GitLab 和 Bitbucket 是其他流行的选项。我们将在本指南中使用 GitHub。
- 你会经常用到 Git 和 GitHub,所以如果你不熟悉,可以看看我的文章这里。
- 在 Github 上创建一个新组织。按照提示操作。我将我的组织命名为 notebooktoall。你可以在自己的个人账户下创建回购,但部分目标是学习如何为更大的社区建立一个开源项目。
步骤 5:设置 GitHub Repo
创建新的存储库。我把我的回购记录命名为 c。
从下拉列表中添加一个. gitignore 。为您的存储库选择 Python 。你的的内容。gitignore 文件将匹配要从 Git repo 中排除的文件夹和文件类型。你可以改变你的。gitignore 稍后排除其他不必要的或敏感的文件。
我建议你从添加许可证下拉框*中选择一个许可证。*许可证定义了存储库内容的用户可以做什么。有些许可证比其他的更宽松。如果没有选择许可证,则默认版权法适用。点击了解更多关于许可证的信息。
对于这个项目,我选择了 GNU 通用公共许可证 v3.0,因为它是流行的、合法的,并且“保证最终用户自由运行、学习、共享和修改软件”——来源。
步骤 6:克隆和添加目录
选择要在本地克隆存储库的位置,并运行以下命令:
git clone [https://github.com/notebooktoall/notebookc.git](https://github.com/notebooktoall/notebook_convert.git)
替代您的组织和回购。
使用桌面 GUI 或代码编辑器进入项目文件夹。或者使用带有cd my-project
的命令行,然后用ls —a
查看你的文件。您的初始文件夹和文件应该如下所示:
.git
.gitignore
LICENSE
README.rst
为您的主项目文件创建一个子文件夹。我建议您将这个主子文件夹命名为与您的包相同的名称。确保名称中没有任何空格。🙂
创建一个名为 init 的文件。py 在您的主子文件夹中。该文件暂时将保持为空。要导入此文件夹中的文件,此文件是必需的。
用创建另一个与主子文件夹同名的文件。py 追加到它后面。我的文件命名为 notebookc.py 。您可以随意命名这个 Python 文件。您的软件包用户在导入您的模块时将引用该文件的名称。
我的 notebookc 目录内容现在看起来是这样的:
.git
.gitignore
LICENSE
README.rstnotebookc/__init__.py
notebookc/notebookc.py
步骤 7:创建并安装 requirements_dev.txt
在项目目录的顶层,创建一个 requirements_dev.txt 文件。通常这个文件被命名为 requirements.txt 。称之为 requirements_dev.txt 突出了这些包只由项目开发人员安装。
在 requirements_dev.txt 中,指定需要安装 pip 和轮。
pip==19.0.3
wheel==0.33.1
请注意,我们用双等号和完整的 major.minor.micro 版本号来指定这些包的确切版本。如果你将来读到这篇文章,你会希望使用更新的版本。检查https://pypi.org/以查看更新的软件包版本。
Pin your package versions in requirements_dev.txt
派生项目 repo 并使用 pip 安装钉住的 requirements_dev.txt 包的合作者将拥有与您相同的包版本。你知道他们会为他们工作。此外,阅读文档将使用这个文件来安装软件包时,它建立您的文档。
在您激活的虚拟环境中,使用以下命令安装 requirements_dev.txt 中的包:
pip install -r requirements_dev.txt
随着新版本的发布,您将希望保持这些包的更新。现在,你可以通过搜索 PyPI 来安装最新的版本。
我们将在以后的文章中安装一个工具来帮助完成这个过程。跟着我确保你不会错过它。
步骤 8:编码并提交
出于演示目的,让我们创建一个基本函数。您可以稍后创建自己的牛逼函数。👍
在主文件中输入以下内容(对我来说就是notebook c/notebook c/notebook c . py):
这是我们最重要的职能。😃
文档字符串以三个连续的双引号开始和结束。它们将在后面的文章中用于自动创建文档。
让我们提交我们的更改。如果你想复习 Git 工作流,请参阅本文。
第 9 步:创建 setup.py
setup.py 文件是您的包的构建脚本。Setuptools 的 setup 函数将构建您的包,并上传到 PyPI。Setuptools 包括关于您的软件包、您的版本号以及用户需要哪些其他软件包的信息。
以下是我的示例 setup.py 文件:
注意 long_description 被设置为 README.md 文件的内容。
在setup tools . setup . install _ requires中指定的需求列表包含了您的软件包工作所需的所有软件包依赖项。
与 requirements_dev.txt 中开发所需的包列表不同,这个包列表应该尽可能宽松。点击阅读更多关于为什么的信息。
将这个 install_requires 包列表限制为只需要的——你不想让用户安装不必要的包。注意,您只需要列出不属于 Python 标准库的包。如果您的用户将使用您的包,他们将安装 Python。😄
我们的包不需要任何外部依赖,所以您可以排除上面例子中列出的四个包。
派生项目 repo 并使用 pip 安装固定包的合作者将拥有与您所使用的相同的包版本。这意味着事情应该工作。🤞
更改其他 setuptools 信息以匹配您的软件包信息。还有许多其他可选的关键字参数和分类器——参见这里的列表。更深入的 setup.py 指南可以在这里和这里找到。
将代码提交给本地 Git repo。让我们准备构建一个包吧!
步骤 10:构建第一个版本
Twine 是一个实用程序集合,用于在 PyPI 上安全地发布 Python 包。将捆绳包添加到 requirements_dev.txt 的下一个空行,如下所示:
twine==1.13.0
然后通过重新安装 requirements_dev.txt 包将 Twine 安装到您的虚拟环境中。
pip install -r requirements_dev.txt
然后运行以下命令创建您的包文件:
python setup.py sdist bdist_wheel
应该创建多个隐藏文件夹: dist 、 build 和——在我的例子中——notebookc . egg-info。让我们看看区文件夹中的文件。的。whl 文件是车轮文件,即构建的分布。. tar.gz 文件是一个源归档文件。
Wheel
在用户的机器上,只要有可能,pip 就会把包安装成轮子。车轮安装更快。当 pip 不能安装一个轮子时,它就依靠源档案。
让我们准备上传我们的车轮和源档案。
步骤 11:创建 TestPyPI 帐户
PyPI 代表 Python 包索引。它是官方的 Python 包管理器。当库还没有安装在本地时,pip 从 PyPI 中获取库。
PyPI
TestPyPI 是 PyPI 的功能测试版本。在这里创建一个 TestPyPI 账户并确认你的电子邮件地址。请注意,上传到测试网站和官方网站时,您将有单独的密码。
步骤 12:发布到 TestPyPI
Twine
使用 Twine 将您的包安全地发布到 TestPyPI。输入以下命令—无需修改。
twine upload --repository-url [https://test.pypi.org/legacy/](https://test.pypi.org/legacy/) dist/*
系统会提示您输入用户名和密码。记住,TestPyPI 和 PyPI 有不同的密码!
如果需要,修复任何错误,在 setup.py 中创建一个新的版本号,并删除旧的构建工件: build 、 dist 和 egg 文件夹。用python setup.py sdist bdist_wheel
重建并用 Twine 重新上传。TestPyPI 上的版本号没有任何意义,这没什么大不了的——您是唯一会使用这些包版本的人。😄
成功上传软件包后,让我们确保您可以安装和使用它。
步骤 13:验证安装并使用它
在您的终端 shell 中创建另一个选项卡,并创建另一个虚拟环境。
python3.7 -m venv my_env
激活它。
source my_env/bin/activate
如果你已经把你的包上传到官方的 PyPI 网站,你就会pip install your-package
。我们可以从 TestPypPI 中检索这个包,并用修改后的命令安装它。
下面是从 TestPyPI 安装软件包的官方说明:
您可以通过指定— index-url 标志来告诉 pip 从 TestPyPI 而不是 PyPI 下载包
pip install --index-url [https://test.pypi.org/simple/](https://test.pypi.org/simple/) my_package
如果您想让 pip 也从 PyPI 获取其他包,您可以指定——extra-index-URL 来指向 PyPI。当您测试的包有依赖项时,这很有用:
pip install --index-url [https://test.pypi.org/simple/](https://test.pypi.org/simple/) --extra-index-url [https://pypi.org/simple](https://pypi.org/simple) my_package
如果您的包有依赖项,使用第二个命令并替换您的包名。
您应该会看到虚拟环境中安装了最新版本的软件包。
要验证您可以使用您的软件包,请在您的终端中使用以下命令启动 IPython 会话:
python
导入函数并用字符串参数调用函数。我的代码如下所示:
from notebookc.notebookc import convert
convert(“Jeff”)
然后我会看到以下输出:
I’ll convert a notebook for you some day, Jeff.
你当然会。😉
步骤 14:推送至 PyPI
将你的代码推送到真正的 PyPI 站点,这样人们可以在他们pip install my_package
的时候下载它。
这是你上传的代码。
twine upload dist/*
请注意,如果您想将新版本推送到 PyPI,需要在 setup.py 中更新您的版本号。
好吧,让我们把我们的作品上传到 GitHub。
第 15 步:推送至 GitHub
确保您提交了代码。
我的记事本 c 项目文件夹看起来是这样的:
.git
.gitignore
LICENSE
README.md
requirements_dev.txt
setup.pynotebookc/__init__.py
notebookc/notebookc.py
排除任何不想上传的虚拟环境。巨蟒。我们在进行回购时选择的 gitignore 文件应该防止构建工件被索引。您可能需要删除虚拟环境文件夹。
用git push origin my_branch
推你当地的分支到 GitHub。
步骤 16:创建并合并采购申请
从浏览器中,导航到 GitHub。您应该会看到一个发出拉取请求的选项。继续按绿色按钮创建和合并您的 PR,并删除您的远程功能分支。
回到你的终端,用git branch -d my_feature_branch
删除你的本地特征分支。
步骤 17:在 GitHub 上更新发布版本
通过点击资源库主页上的发布,在 GitHub 上创建一个新的发布版本。输入发布信息并保存。
那就暂时好了!🎉
我们将在以后的文章中添加更多的文件和文件夹。
让我们回顾一下我们的步骤。
总结:工作包的 17 个步骤
- 想办法
- 将其命名
- 配置环境
- 在 Github 上创建组织
- 设置 GitHub Repo
- 克隆和添加目录
- 创建并安装 requirements_dev.txt
- 编码并提交
- 创建 setup.py
- 构建第一个版本
- 创建 TestPyPI 帐户
- 推送至 TestPyPI
- 验证安装和使用
- 推送至 PyPI
- 推送至 GitHub
- 创建并合并请购单
- 在 GitHub 上更新发布版本
包装
我希望这篇关于制作和发布你的第一个 Python 包的指南对你有用。如果你有,请分享到你最喜欢的社交媒体渠道,这样其他人也可以找到它。👏
在本系列的下一部分,我们将添加测试、持续集成、代码覆盖等等。请点击这里查看:
如何添加测试、CI、代码覆盖率等等
towardsdatascience.com](/10-steps-to-set-up-your-python-project-for-success-14ff88b5d13)
我撰写关于 Python、Docker、数据科学和其他技术主题的文章。如果你对此感兴趣,请在这里阅读更多和关注我。😄
快乐大厦!
在创建一个好的回归模型时建立你的直觉
你可以通过分类来辨别那是猫还是不是猫。但是,要回答那只猫有多“猫”,回归是唯一的方法。
Photo by Erik-Jan Leusink on Unsplash
分类与回归
两个大问题,两种不同的原因。
热狗还是不是热狗,猫还是不是猫,诈骗还是不是诈骗。这些问题是分类的常见用例。
通过查看示例,您可以很容易地得出结论:分类问题就是将一些数据集划分为几个类或组。从某种意义上说,组的数量是有限的。
正如您所猜测的,另一方面,回归是将这些数据分成无限组。
例如,拥有一组关于房屋面积的数据。通过查看数据,你可以预测每套房子的价格。因为价格几乎是无限的,所以你可以根据这些数据建立一个回归模型。
那么,如何开始创建回归模型呢?
定义问题
用例子做任何事情总是更好,因为理论不会让你走那么远。
我们来发现一些问题。
我发现了这个:
这是一个很好的开始问题,可以训练你建立良好回归模型的直觉。
基本上,你会得到一堆关于在纽约从一个地方到另一个地方需要付多少钱的数据。根据这些数据,你需要预测你需要支付多少打车费用。
他们在问题中陈述的指标是,在不做所有必要的机器学习事情的情况下,你可以得到 5 到 8 美元的 RMSE(均方根误差)。所以,让我们建立一个能给你更少误差的东西。
开始吧!
数据
下载数据并打开 train.csv(是的,它很大),您将看到大约 5500 万行包含实际出租车费用的数据。
每行将由 7 列组成。
- 接送日期,当出租车开始行驶时
- 皮卡 _ 经度,出租车乘坐皮卡的经度
- 皮卡 _ 纬度,乘坐皮卡的纬度
- 落客 _ 经度,落客的经度
- 落客 _ 纬度,落客的纬度
- 乘客计数,船上乘客的数量
- fare_amount ,我们要预测的那个。
我们走吧!
逻辑
对于本教程,我将使用 Python 和几个标准库,如 Numpy、Pandas 和 LGBM
import numpy as np
import pandas as pd
import scipy as scipy
import datetime as dt
from sklearn.model_selection import train_test_split
import lightgbm as lgbm
import os
import gc
我导入了 GC。那是一个用 python 收集垃圾的库。垃圾收集器的 GC。
为什么要导入?
看看数据,对于本教程,我的计算机无法加载文件中的所有行,我最多只能加载 2200 万行。甚至,手动使用垃圾收集器也有所帮助,这样我就可以释放内存来加载和处理所有这 2200 万行。
# Reading Data
train_df = pd.read_csv('train.csv', nrows = 22000000)# Drop rows with null values
train_df = train_df.dropna(how = 'any', axis = 'rows')# Drop invalid rows
train_df = train_df[(train_df.fare_amount > 0) & (train_df.fare_amount <= 500) & ((train_df.pickup_longitude != 0) & (train_df.pickup_latitude != 0) & (train_df.dropoff_longitude != 0) & (train_df.dropoff_latitude != 0))]
读取并删除所有缺少值或值无效的行。
直觉
现在你已经准备好了数据。下一步是什么?
像这样的问题,你需要几个新的特性(数据中只有 6 个可用)。你可以试着用这些数据来训练模型,但是你给的信息对模型来说太少了。
特征工程是解决方案。
它基本上是使用现有的功能创建或修改数据。这样做将增加您对数据的理解,最终在训练阶段帮助您的模型。
比如经纬度,直接看的话可能没有任何意义。但是,什么对你有意义呢?距离!正确。
让我们准备一个函数来计算拾取点和衰减点之间的距离。
# To Compute Haversine distance
def sphere_dist(pickup_lat, pickup_lon, dropoff_lat, dropoff_lon):
"""
Return distance along great radius between pickup
and dropoff coordinates.
"""
#Define earth radius (km)
R_earth = 6371 #Convert degrees to radians
pickup_lat, pickup_lon, dropoff_lat, dropoff_lon =
map(np.radians, [pickup_lat, pickup_lon,dropoff_lat,
dropoff_lon]) #Compute distances along lat, lon dimensions
dlat = dropoff_lat - pickup_lat
dlon = dropoff_lon - pickup_lon
#Compute haversine distance
a = np.sin(dlat/2.0)**2 + np.cos(pickup_lat) *
np.cos(dropoff_lat) * np.sin(dlon/2.0)**2
return 2 * R_earth * np.arcsin(np.sqrt(a))
这就是哈弗辛公式,一个计算两个纬度和经度之间距离的函数。
然而,在导航中,除了距离之外,你经常需要计算方位,也称为方位角或在北方和游乐设备之间移动的角度。
def sphere_dist_bear(pickup_lat, pickup_lon, dropoff_lat, dropoff_lon):
"""
Return distance along great radius between pickup and dropoff
coordinates.
"""
#Convert degrees to radians
pickup_lat, pickup_lon, dropoff_lat, dropoff_lon =
map(np.radians, [pickup_lat, pickup_lon, dropoff_lat,
dropoff_lon]) #Compute distances along lat, lon dimensions
dlat = dropoff_lat - pickup_lat
dlon = pickup_lon - dropoff_lon
#Compute the bearing
a = np.arctan2(np.sin(dlon *
np.cos(dropoff_lat)),np.cos(pickup_lat) *
np.sin(dropoff_lat) - np.sin(pickup_lat) *
np.cos(dropoff_lat) * np.cos(dlon))
return a
让我们添加数据
train_df['distance'] = sphere_dist(train_df['pickup_latitude'],
train_df['pickup_longitude'],
train_df['dropoff_latitude'],
train_df['dropoff_longitude'])
train_df['bearing'] = sphere_dist_bear(train_df['pickup_latitude'],
train_df['pickup_longitude'],
train_df['dropoff_latitude'],
train_df['dropoff_longitude'])
接下来,您可能想要将纬度和经度转换为弧度。因为用弧度计算会给你已经标准化的输入。弧度参数的最大值是 2π弧度。
def radian_conv(degree):
"""
Return radian.
"""
return np.radians(degree)train_df['pickup_latitude'] =
radian_conv(train_df['pickup_latitude'])
train_df['pickup_longitude'] =
radian_conv(train_df['pickup_longitude'])
train_df['dropoff_latitude'] =
radian_conv(train_df['dropoff_latitude'])
train_df['dropoff_longitude'] =
radian_conv(train_df['dropoff_longitude'])
现在,还有什么?
您可能希望从 datetime 特性中提取详细的日期和时间数据。
def add_datetime_info(dataset):
#Convert to datetime format
dataset['pickup_datetime'] = pd.to_datetime(dataset['pickup_datetime'],format="%Y-%m-%d %H:%M:%S UTC")
dataset['hour'] = dataset.pickup_datetime.dt.hour
dataset['day'] = dataset.pickup_datetime.dt.day
dataset['month'] = dataset.pickup_datetime.dt.month
dataset['weekday'] = dataset.pickup_datetime.dt.weekday
dataset['year'] = dataset.pickup_datetime.dt.year
return datasettrain_df = add_datetime_info(train_df)
这些是您可能希望看到的常见功能,因为出租车费用可能是季节性的。让我们将所有这些要素放入数据集中。
开始变得有创造力
从这一点来说,您已经准备好了,因为您可能已经从初始特征中提取了所有可能的特征。
这一次,你可能想在餐桌上增加一些创意。
给自己一个问题。
为什么人们在纽约打车?
- 可能他们刚到机场就去市区了。在这种情况下,如果乘坐距离机场较近,而乘坐距离机场较远,情况可能会有所不同。
- 人们到处都乘出租车。市内,市外,靠近市中心,远离市中心。但是大概票价会因为你离纽约市中心有多远而有所不同。
- 自由女神像?
Photo by AussieActive on Unsplash
让我们计算一下乘车点和这些点之间的距离。
def add_airport_dist(dataset):
"""
Return minumum distance from pickup or dropoff coordinates to
each airport.
JFK: John F. Kennedy International Airport
EWR: Newark Liberty International Airport
LGA: LaGuardia Airport
SOL: Statue of Liberty
NYC: Newyork Central
"""
jfk_coord = (40.639722, -73.778889)
ewr_coord = (40.6925, -74.168611)
lga_coord = (40.77725, -73.872611)
sol_coord = (40.6892,-74.0445) # Statue of Liberty
nyc_coord = (40.7141667,-74.0063889)
pickup_lat = dataset['pickup_latitude']
dropoff_lat = dataset['dropoff_latitude']
pickup_lon = dataset['pickup_longitude']
dropoff_lon = dataset['dropoff_longitude']
pickup_jfk = sphere_dist(pickup_lat, pickup_lon,
jfk_coord[0], jfk_coord[1])
dropoff_jfk = sphere_dist(jfk_coord[0], jfk_coord[1],
dropoff_lat, dropoff_lon)
pickup_ewr = sphere_dist(pickup_lat, pickup_lon,
ewr_coord[0], ewr_coord[1])
dropoff_ewr = sphere_dist(ewr_coord[0], ewr_coord[1],
dropoff_lat, dropoff_lon)
pickup_lga = sphere_dist(pickup_lat, pickup_lon,
lga_coord[0], lga_coord[1])
dropoff_lga = sphere_dist(lga_coord[0], lga_coord[1],
dropoff_lat, dropoff_lon)
pickup_sol = sphere_dist(pickup_lat, pickup_lon,
sol_coord[0], sol_coord[1])
dropoff_sol = sphere_dist(sol_coord[0], sol_coord[1],
dropoff_lat, dropoff_lon)
pickup_nyc = sphere_dist(pickup_lat, pickup_lon,
nyc_coord[0], nyc_coord[1])
dropoff_nyc = sphere_dist(nyc_coord[0], nyc_coord[1],
dropoff_lat, dropoff_lon)
dataset['jfk_dist'] = pickup_jfk + dropoff_jfk
dataset['ewr_dist'] = pickup_ewr + dropoff_ewr
dataset['lga_dist'] = pickup_lga + dropoff_lga
dataset['sol_dist'] = pickup_sol + dropoff_sol
dataset['nyc_dist'] = pickup_nyc + dropoff_nyc
return datasettrain_df = add_airport_dist(train_df)
这取决于你的想象力,你的特征可能不像我创造的。你可能对影响出租车费用的因素有另一种直觉,比如离商业区或住宅区的距离。或者你想象的任何东西。
该训练了
现在,在您准备好数据之后,让我们来训练模型。
train_df.drop(columns=['key', 'pickup_datetime'], inplace=True)
y = train_df['fare_amount']
train_df = train_df.drop(columns=['fare_amount'])x_train,x_test,y_train,y_test = train_test_split(train_df, y, test_size=0.10)del train_df
del y
gc.collect()
将数据以 90:10 的比例分割成训练验证分割。获得训练数组后,不要忘记删除数据帧。它占用了大量资源。
params = {
'boosting_type':'gbdt',
'objective': 'regression',
'nthread': 4,
'num_leaves': 31,
'learning_rate': 0.05,
'max_depth': -1,
'subsample': 0.8,
'bagging_fraction' : 1,
'max_bin' : 5000 ,
'bagging_freq': 20,
'colsample_bytree': 0.6,
'metric': 'rmse',
'min_split_gain': 0.5,
'min_child_weight': 1,
'min_child_samples': 10,
'scale_pos_weight':1,
'zero_as_missing': True,
'seed':0,
'num_rounds':50000
}train_set = lgbm.Dataset(x_train, y_train, silent=False,categorical_feature=['year','month','day','weekday'])valid_set = lgbm.Dataset(x_test, y_test, silent=False,categorical_feature=['year','month','day','weekday'])model = lgbm.train(params, train_set = train_set, num_boost_round=10000,early_stopping_rounds=500,verbose_eval=500, valid_sets=valid_set)
它将永远训练,直到你的模型不能再优化结果。这将使你达到大约 25,000 次迭代,并给你 $3.47966 均方根误差。
这是一个巨大的提升,可以为你节省 1.5 美元到 4.5 美元的出租车费。你可以用它买一份简单的快餐。Lol。
Photo by Jen Theodore on Unsplash
结论
一开始直觉可能很难。但是你知道,通常是常识帮助你度过所有这些。通过了解基层的数据情况,可以让你比以前更直观一点。
别忘了,继续努力!
干杯
为分布式分类帐建立区块链协议
通过一个简单的编码例子揭开区块链的神秘面纱
Background vector created by freepik — www.freepik.com
在这篇文章中,我将使用区块链构建一个简单的分布式分类帐,扩展 CodeAcademy 和其他人的教程,解释区块链的一些关键机制以及它们如何在 Python 中实现。
区块链概览
区块链协议的基本思想是在一个由机器或节点组成的分布式网络中保存一个名为分类账*的数据库。分类账可以用于任何东西,可以保存交易或数字货币,如比特币或以太坊。为了维护保存在网络不同地方的账本的完整性,区块链协议在网络参与者解决计算密集型问题时对他们进行奖励——这个过程被称为挖掘。*正是网络上的参与者进入的这个过程,确保了公共网络上分布式账本的完整性。区块链本质上是网络上的参与者解决的问题的解决方案的证明,这些解决方案在一个链中链接在一起,并且随着每个新的解决方案的发现,一个新的块被创建,并且下一组记录被复制到该块中。
构建区块链类
首先,我们将构建 Blockchain 类构造函数方法,该方法创建一个初始空列表(存储我们的区块链)和另一个存储分类账(交易记录)的列表。区块链类对象将负责管理整个区块链。我们将创建以下方法来帮助我们管理区块链:
[1] register_node() — 该方法将在网络上注册一个新节点
[2] new_block() —此方法将在区块链中创建一个新块,将最近的事务复制到这个新块中,并清除事务集。
[3] valid_proof() —此方法将检查提交以添加到区块链的块是否解决了问题。
[4]proof _ of _ work()-此方法将检查采矿是否产生了在区块链创建下一个区块的正确证据。如果证明是正确的,我们返回证明,否则它会一直调用 valid_proof() 直到我们找到有效的证明。
[5] valid_chain() — 该方法将遍历整个区块链,调用 valid_proof()来检查后续块是否有效。
[6]new _ transaction()—此方法将向事务列表中添加一个新事务。
[7] last_block() —此方法将返回链中的最后一个块。
区块链类看起来是这样的:
解决网络上的冲突
由于区块链协议是在分布式网络上运行的,我们需要一个关于区块链类对象的方法来解决网络上的冲突。我们将简单地检查网络上是否有任何其他比我们更长的区块链,如果有,使其成为我们自己的链,否则我们可以继续挖掘我们正在为下一个块挖掘的知识,我们的努力是值得的。
与区块链互动
最后,为了让我们的区块链变得生动并与之交互,我们可以使用 flask 通过 web 应用程序接口公开许多方法。
在网络上注册节点
我们将允许用户在网络上注册一个节点,这样他们就可以参与到工作证明过程中,并为他们的努力获得回报。我们在区块链类对象上调用 register_node()方法注册一个节点。
我们还想让这些参与者检查他们的区块链是否有效,如果无效,就从网络上获取区块链。
向区块链添加交易
我们希望允许参与者向分类帐添加交易。为此,我们只需在区块链类对象上调用 new_transaction(),并添加交易细节。
采矿
最后一件重要的事情!我们需要创造互动,让网络上的参与者挖掘他们的区块链。
现在,我们可以实例化 flask 应用程序,创建区块链类对象,运行 flask 应用程序,并与我们的区块链进行交互。
要下载完整的代码文件,请参见:【https://github.com/jamesdhope/blockchain】
参考
[1]https://www.wikihow.com/Build-a-Blockchain-App
[2]https://www . codecademy . com/learn/introduction-to-区块链/modules/区块链-in-python
[3]丹尼尔·范·弗莱明,https://hacker noon . com/learn-区块链-by-building-one-117428612 f46。
[4]https://towards data science . com/building-a-minimal-区块链-in-python-4f2e9934101d
[5]奥默·戈德堡,https://hacker noon . com/building-a-区块链-the-grey-paper-5be 456018040
在 15 分钟内构建您自己的基于聚类的推荐引擎!!
推荐引擎是机器学习技术在当前互联网时代最流行的应用之一。这些广泛用于电子商务网站推荐类似产品和电影推荐网站。他们负责为我们生成各种定制的新闻建议。这将推动用户参与更多内容,从而为组织带来更好的用户体验和更多收入。因此,它们在今天的工业中极其重要。
推荐引擎基本上过滤数据并向用户推荐最相关的结果。这些结果是以最大可能感兴趣的方式推荐的。现在,所有的推荐引擎都有用户数据和他们的历史记录,可以用来创建他们的过滤算法。这最终帮助他们为每个独特的用户生成非常准确的推荐。
User-based filtering is based on history of users and similarity b/w them from their purchase histories for example. But, Item-based recommendations are based on content based similarity. Like, “how many times few items are bought together”. Next time, most frequent of these purchases will be recommended together.
在协同过滤的情况下,“用户行为”被用于推荐项目。这些推荐可以利用用户-用户相似性或者基于项目-项目相似性来生成。并且基于该相似性度量,向用户提供建议。但是,让我们考虑一个场景,在这个场景中,我们没有可用的用户数据,但是我们仍然必须向用户推荐商品。
没有用户数据怎么办?我们的推荐引擎现在将如何工作?
生成推荐的问题现在被简单地转化为类似聚类的问题。其中相似性度量基于“在生成推荐时,两个项目有多接近?”。用于生成推荐的度量将基于两个项目的相似性,如这些项目之间的向量距离。我们将针对 Pluralsight 的在线课程文本数据进行讨论。让我们来做一个仅基于我们可用的项目数据的推荐引擎。
在线课程推荐系统
在本文中,我们将从 Pluralsight 的课程数据中构建一个推荐系统,并查看可以对我们基于聚类的解决方案进行的进一步改进。我们将按下述顺序讨论该项目的整个数据分析流程。为了节省时间,你可以直接参考项目库并遵循精心制作的 README.md 文件。此外,可以为提到的每个模块直接运行实用程序脚本。
1.简介:了解你的数据
2.架构设计:构建实用工具
3.预处理步骤
4.问题讨论、模型训练和优化
5.工作推荐系统
6.结论&主题建模的未来改进(特别是 LDA)
简介:了解你的数据
该项目使用的数据是 Pluralsight 网站上的课程列表和描述。要获取课程数据,只需运行下面提到的 ReST API 查询。但是,为了获得用户注册数据,让我们说一个基于协作过滤器的引擎。
首先,获取在文档中提到的 ReST api-token,然后进行 ReST 查询,以获取关于该网站上所有课程和注册该网站的各个用户的数据。如果您想要获取用户相关数据,则需要此键。否则,为了获得简单的课程相关数据,我们可以编写如下 ReST 查询。
# Input
http://api.pluralsight.com/api-v0.9/courses# Output: A *Courses.csv* file for download. It will be having below mentioned structure.CourseId,CourseTitle,DurationInSeconds,ReleaseDate,Description,AssessmentStatus,IsCourseRetiredabts-advanced-topics,BizTalk 2006 Business Process Management,22198,2008-10-25,"This course covers Business Process Management features in BizTalk Server 2006, including web services, BAM, hosting, and BTS 2009 features",Live,no
abts-fundamentals,BizTalk 2006
...
在本文中,我们仅限于发动机制造的课程数据。否则,这种方法将与其他推荐引擎文章非常相似。通过查看这些数据,我们发现以下几点在训练模型时非常重要。您也可以打开 Courses.csv 文件,自己进行如下观察。
- 课程数据文本描述针对课程 Id、课程标题和课程描述列呈现。因此,在构建我们的推荐引擎时,这些列是我们感兴趣的。利用这些列中的文本数据,我们将能够构建词向量,我们的模型将在预测结果时使用这些词向量。此外,大部分信息仅出现在*【描述】*栏中。因此,没有描述的课程将从培训中删除。
- “已退休”栏描述了网站上课程的当前状态,即网站上目前是否有该课程。因此,我们不想推荐我们训练有素的模型的退役课程。但是,我们绝对可以在我们的训练数据中使用它们。
- 并对该数据的预处理进行了讨论。数据中显然存在一些额外的’-'标记、不同的大小写和停用词。我们将相应地预处理我们的文本,只关注名词/名词短语。
在下一节中,我们将讨论正在开发的这个推荐实用程序的基本架构。有了这个架构,最终我们将拥有一个完整的机器学习工具,它将课程数据作为输入,并基于用户查询生成建议。
架构设计:构建实用工具
下图清楚地说明了我们在这个数据科学项目中的渠道。请在以从左到右的方式进一步阅读之前先看一下。
Three main components: 1. Pre-process & Train; 2. Optimizations; 3. Recommendation Utility Tool
这个实用工具主要分为三个组件,我们将在接下来的章节中详细讨论这些组件。主要是对模型进行训练和优化,减少误差。之后,我们将编写实用工具,该工具将基于唯一课程 id 的输入查询生成推荐。
记住上面的工具架构,让我们转到预处理步骤,并开始为我们的模型进行数据摄取步骤。
预处理步骤
按照下面的代码片段,我们将做一些小的文本预处理,如删除所有标点符号。此外,在大量的术语中,“ll”被用在诸如“we’ll”、“you’ll”等情况中。这些也从*‘描述’*文本中删除。我们还将消除停用词,并以适当的方式合并包含描述、课程 id、标题的列。请参考下面的代码片段,以遵循上述步骤。
import pandas as pd# 1\. read data, from source
# "Courses.csv" file has been renamed
course_df = pd.read_csv("data/courses.csv")# 2\. drop rows with NaN values for any column, specifically 'Description'
# Course with no description won't be of much use
course_df = course_df.dropna(how='any')# 3\. Pre-processing step: remove words like we'll, you'll, they'll etc.
course_df['Description'] = course_df['Description'].replace({"'ll": " "}, regex=True)# 4\. Another Pre-preprocessing step: Removal of '-' from the CourseId field
course_df['CourseId'] = course_df['CourseId'].replace({"-": " "}, regex=True)# 5\. Combine three columns namely: CourseId, CourseTitle, Description
comb_frame = course_df.CourseId.str.cat(" "+course_df.CourseTitle.str.cat(" "+course_df.Description))# 6\. Remove all characters except numbers & alphabets
# Numbers are retained as they are related to specific course series also
comb_frame = comb_frame.replace({"[^A-Za-z0-9 ]+": ""}, regex=True)
在对上述数据进行基本的清理步骤后,*‘comb _ frame’*包含了与课程相关的所有必要的文字描述。之后,让我们移动到这个文本的矢量化,并训练我们的模型。
问题讨论、模型训练和优化
现在,我们将所有需要的文本数据呈现在一个数据框中。但是,我们需要将其转换成有意义的表示。因此,它可以正确地输入到我们的机器学习模型中。
为此,我们使用 tf-idf 权重来表示术语在文档中的重要性。它是对文档中单词重要性的统计度量。该权重与单词在语料库中出现的次数相关,但是被语料库中单词的频率所抵消。
Tf 中的 tf-idf 权重衡量文档中的词频。以及 idf 测量给定语料库中给定术语重要性。这可以从下面提到的公式中推断出来。
**TF(**t**)** = (Number of times term *'t'* appears in a document) **/** (Total number of terms in the document)**IDF(**t**)** = **log_e(**Total number of documents **/** Number of documents with term *'t'* in it**)**
我们将使用 scikit learn 将我们的文本数据转换为上面公式中指定的向量矩阵乘积。按照下面的代码片段进行转换。
# Create word vectors from combined frames
# Make sure to make necessary importsfrom sklearn.cluster import KMeans
from sklearn import metrics
from sklearn.feature_extraction.text import TfidfVectorizervectorizer = TfidfVectorizer(stop_words='english')
X = vectorizer.fit_transform(comb_frame)
在此之后,我们可以将这些数据直接输入到我们的 k 均值学习算法中。但是,对于我们的 k-means 算法,我们将需要***【k】***的理想值,对此我们还没有讨论过。首先,我们可以使用 k=8 的值,因为 Pluralsight 有八种不同类型的课程类别,并检查我们的模型相应训练的预测能力。跟随下面提到的代码片段。
# true_k, derived from elbow method and confirmed from pluralsight's website
true_k = 8# Running model with 15 different centroid initializations & maximum iterations are 500
model = KMeans(n_clusters=true_k, init='k-means++', max_iter=500, n_init=15)
model.fit(X)
我们可以观察来自每个聚类的顶词,以查看所形成的聚类在质量上是否良好,或者它们在某种意义上是否需要改进。运行下面提到的片段,观察每个聚类中的热门词。
# Top terms in each clusters.print("Top terms per cluster:")
order_centroids = model.cluster_centers_.argsort()[:, ::-1]
terms = vectorizer.get_feature_names()
for i in range(true_k):
print("Cluster %d:" % i),
for ind in order_centroids[i, :15]:
print(' %s' % terms[ind]),
print
观察这些单词后,您可能会注意到所有形成的集群都不合适,一些课程类别在多个集群中重复出现(请参考 README.md 文件)。那现在还是好的(😉),我们的模型将数量巨大的课程类别细分为其他子类别。因此,给定类别的课程数量的基数问题暴露出来,我们的模型无法解决。
我们可以看到,细分类别平面艺术,电影设计,动画形成了母’创意-专业’类别。由于课程类别之间的数据分布不均,即数据基数的问题,因此形成了这个子类。因此,像“商务-专业”这样课程数量少的课程类别在我们的理想假设中迷失了,因为 k 等于 8。这很容易发生,因为在我们简单的机器学习模型训练中,不经常出现的业务相关术语很容易失去其 tf-idf 权重。
因此,从这种方法得到的聚类仍然可以通过进一步划分成其他聚类来改进,以用更少数量的课程得到这些更小的课程类别。因为,这些进一步的划分可以公式化为误差最小化的优化问题。我们不想因此过度拟合我们的模型,我们将使用 【肘测试】 方法来寻找 k 的理想值。这个想法是,每当给定的***【k’*****的值的误差急剧下降时,该值对于形成聚类来说足够好。这些形成的集群将具有尖锐的误差极小值,并将为我们的模型给出令人满意的解决方案。按照下面提到的代码对我们的数据进行弯头测试。
**# Continuing after vectorization step# data-structure to store Sum-Of-Square-Errors
sse = {}# Looping over multiple values of k from 1 to 30
for k in range(1, 40):
kmeans = KMeans(n_clusters=k, init='k-means++', max_iter=100).fit(X)
comb_frame["clusters"] = kmeans.labels_
sse[k] = kmeans.inertia_# Plotting the curve with 'k'-value vs SSE
plt.plot(list(sse.keys()), list(sse.values()))
plt.xlabel("Number of cluster")
plt.ylabel("SSE")
# Save the Plot in current directory
plt.savefig('elbow_method.png')**
运行上述代码后,我们得到了下图,在此基础上,我们为 k=30 训练了我们的模型。并为我们的推荐引擎工具实现了相对更好的聚类。
Slope is drastically diminishing after the value of k=30. Hence, we’ll opt for this value for our model.
最后,让我们保存我们的模型,继续我们的推荐实用程序脚本设计,并讨论未来的改进方法。所有这些提到的片段都以 model_train.py 脚本的形式提供,您可以参考它来直接执行。但是,在此之前,请提取 courses.csv 数据文件,并仔细阅读 README.md 。
**# Save machine learning model
filename = 'finalized_model.sav'
pickle.dump(model, open(filename, 'wb'))**
工作推荐系统
我们将为这个推荐模块创建几个实用函数。一个 cluster_predict 函数,它将预测输入其中的任何描述的分类。首选输入是我们之前在 model_train.py 文件的 comb_frame 中设计的类似“描述”的输入。
**def cluster_predict(str_input):
Y = vectorizer.transform(list(str_input))
prediction = model.predict(Y)
return prediction**
之后,我们将根据新的 dataframe 列中的描述向量为每个课程分配类别,即‘cluster prediction’*。见下文。*
**# Create new column for storing predicted categories from our trained model.
course_df['ClusterPrediction'] = ""**
我们将为只有实时课程的数据框存储该聚类类别分数,即删除“无”实时条目的课程。之后,我们将在数据框中为每门课程运行预测函数实用程序,并存储聚类类别。这些存储的类别将在将来与输入查询及其预测类别进行匹配,以生成推荐。
**# load the complete data in a dataframe
course_df = pd.read_csv("data/courses.csv")# drop retired course from analysis. But, courses with no descriptions are kept.
course_df = course_df[course_df.IsCourseRetired == 'no']
# create new column in dataframe which is combination of (CourseId, CourseTitle, Description) in existing data-frame
course_df['InputString'] = course_df.CourseId.str.cat(" "+course_df.CourseTitle.str.cat(" "+course_df.Description))# Create new column for storing predicted categories from our trained model.
course_df['ClusterPrediction'] = ""# Cluster category for each live course
course_df['ClusterPrediction']=course_df.apply(lambda x: cluster_predict(course_df['InputString']), axis=0)**
最后,推荐实用函数将预测具有课程 id 的输入查询的课程类别,并且将从上面转换的数据帧‘course _ df’中推荐几个随机课程,该数据帧具有每个课程的预测值。
**def recommend_util(str_input):
# match on the basis course-id and form whole 'Description' entry out of it.
temp_df = course_df.loc[course_df['CourseId'] == str_input]
temp_df['InputString'] = temp_df.CourseId.str.cat(" "+temp_df.CourseTitle.str.cat(" "+temp_df['Description']))
str_input = list(temp_df['InputString'])
# Predict category of input string category
prediction_inp = cluster_predict(str_input)
prediction_inp = int(prediction_inp) # Based on the above prediction 10 random courses are recommended from the whole data-frame
# Recommendation Logic is kept super-simple for current implementation. temp_df = course_df.loc[course_df['ClusterPrediction'] == prediction_inp]
temp_df = temp_df.sample(10)
return list(temp_df['CourseId'])**
用下面给出的查询测试你训练过的推荐引擎。您也可以通过从 courses.csv 获取课程 id 来添加您的查询。
**queries = ['play-by-play-machine-learning-exposed', 'microsoft-cognitive-services-machine-learning', 'python-scikit-learn-building-machine-learning-models', 'pandas-data-wrangling-machine-learning-engineers', 'xgboost-python-scikit-learn-machine-learning']for query in queries:
res = recommend_util(query)
print(res)**
结论和未来改进
当前推荐引擎的实现本质上是非常原始的。用精确的硬步骤阈值来形成集群的方法是粗糙的,但是给出了用集群化算法实现这些引擎的想法。此外,生成的推荐本质上是随机的。可以采用更具体的方法(如基于最高得分的推荐方法)作为改进。目前,course-id 作为唯一的输入,而不是更好的自然语言输入。但是,这些只是基于实施的改进。
基本上,为了将来的改进,用于训练的类别分配机制和模型可以被改变。此外,可以采用来自主题建模的高级和复杂的机制,如潜在狄利克雷分配(LDA)。主题建模是自然语言处理的一个统计分支,它从文档集中提取摘要。我们将使用 LDA,它将一个特定的文档分配给一个特定的主题和一个实数权重分数,该分数将与相应主题的单词相关联。
只需运行 lda_train.py 来详细了解 lda 的实现,注释/控制台输出将解释关于正在执行的步骤的一切。
这些指定的主题及其与单词的关联分数可以作为上述 cluster_prediction 函数的预测逻辑基础。但是,这些预测将比当前由 k-means 聚类算法生成的任何推荐更精确。一个基于 gensim 的 LDA 实现在这里的同一个 github 仓库中可用。它的推荐实用程序脚本目前还没有添加,你可以作为家庭作业来尝试。
霍普,你喜欢读它,并得到了一个数据科学项目的小手。如果有任何改进,请在 github 上做一个公关或公开一个问题。