建立基于 LSTM 的太阳能预测模型
处理 LSTM 的一些设计问题
图片来源:https://unsplash.com/photos/mG8sgwkMhCY
太阳能是清洁和可再生能源替代来源的最重要组成部分之一。太阳能发电的预测对于下游应用和与传统电网的整合至关重要。不是测量太阳能电池的光电输出,而是经常估计从太阳接收的辐射作为太阳能发电的替代。用于测量的量称为总水平辐照度(GHI ),它包括直接辐射和散射辐射。
预测的方法可以大致分为以下几种
图 1:预测模型的类型 a)基于方法 b)基于预测窗口 c)基于变量数量。(图片来源:作者)
不同的模型是物理模型(NWP)、统计模型(ARIMA、GARCH)、机器学习模型(随机森林、Boosting)和基于深度学习的模型(RNN、LSTM、GRU)。基于预测窗口,如果少于 30 分钟,则是非常短期的预测,如果多于 30 分钟,则是短期的预测。短期预测现在更适合印度的情况。如果你想开始了解 LSTM,你应该浏览阅读量最大的博客。
而我们希望基于理解复杂和非线性模式的能力来使用 LSTM。我们浏览了很多博客和论文,有一些事情我们不确定。我们计划系统地检查它们。这些数据来自 2016 年,分别与 Chennai、Howrah 和 Ajmer 的三个太阳能发电站有关。对于每个站点,我们从两个季节获取数据,即雨季(由于云层覆盖,最不稳定)和冬季(最不稳定)。还需要指出的是,我们分别讨论了“湿热”和“干热”气候带。对于每个问题,都进行了实验并分析了结果。如下图所示,LSTM 类型用三维形状对数据进行建模。
图 2a:LSTM 型模型的输入数据形状。(图片来源:作者)
**数字输入特性:**这简单地告诉我们,有多少变量用于预测。除了使用过去的 GHI 值,我们还可以使用其他气象变量,如温度、湿度等。
**时间步数:**这通常被称为输入窗口大小,即我们用于预测的序列的过去值的数量。例如,如果我们打算预测上午 10:00 的值,我们可以考虑上午 09:55、09:50、09:45、09:40 以及 09: 35 和 09: 30 的值。在这种情况下,窗口大小为 6。
**批量:**我们知道,对于所有的 ML/DL 模型,我们都试图使用梯度下降法找到模型的最佳参数。我们还知道,与其在整个训练数据上寻找梯度,不如在较小的批次上寻找梯度通常更有效。批处理大小参数负责处理这个问题。
还有一个输出窗口大小的概念,它基于我们是只对下一个观察感兴趣还是对几个后续观察感兴趣。这显示在下图中。如果只有一个观察,我们将有一个单一的输出节点,否则超过一个输出节点。
图 2b:具有输出窗口的 LSTM 网络
D 设计问题 1:LSTM 的好处之一似乎是不需要时间序列相关的预处理,如去除趋势和季节性,然而研究界似乎正在应用预处理。所以我们想研究是否需要预处理。
实验装置很简单。我们通过应用 a)无预处理和 b)有预处理(去除季节性,数据中没有趋势)来比较性能。结果如下图所示。
图 3:用原始数据和预处理数据比较模型(图片来源:作者)
很明显,LSTM 在原始时间序列上的训练给出了更好的结果。性能是根据标准化 RMSE 和解释方差得分来衡量的,它们是时间序列预测任务的标准度量。
设计问题 2:许多从业者似乎也在非时间序列设置中使用 LSTM,也就是说,他们使用感兴趣变量的先前值,但将它们视为不同的自变量。有意义吗?
实验装置简单而琐碎。在设置 1 中,数据形状为(72,1,30),30 个特征,窗口大小为 1。在设置 2 中,数据形状取为(72,30,1),一个特征,窗口大小为 30。
图 4:基于非时间序列和时间序列设置的模型比较(图片来源:作者)
看到相当多的博客和研究论文将时间步长作为独立的特征,我们真的很困惑,幸运的是,结果显示时间序列设置的结果要好得多。
D 设计问题 3:一个普遍的直觉是,当我们增加节点或层时,性能应该总是增加的。但是随之而来的是训练时间以及对训练数据的需求的增加。问题是这种直觉有多真实,在哪里停止?
这里,我们使用了一个非常简单的想法。如果给他们更难的问题,任何学习者都需要学得更多。根据同样的类比,如果数据更难处理,任何网络都将需要更大的容量(在层、节点等方面)。一个明显的后续问题是,我们如何衡量这一点?我们认为输入数据显示的可变性越大,预测就越困难。因此,我们首先测量每个站点的 GHI 的可变性,然后针对不同数量的节点进行训练和评估。结果如下所示。
图 5:我们应该继续增加网络的复杂性吗
对于变化率在 50 左右或小于 50 的站点-季节组合,我们需要尽可能多的节点,当变化率超过这个范围时,我们需要大约 100 个节点来达到最佳预测。下图是上表的复制。
图 6:节点数量和性能
结论:
LSTM 可以是一个很好的太阳预测模型,建议使用原始时间序列,它们应该被视为时间序列数据,而不是将每个时间步长视为一个单独的属性。更多的节点不一定意味着最好的性能,更复杂的场景将需要更复杂的结构,并且对于简单和复杂的场景,在一定数量的节点之后,学习饱和(可能过度适应)
致谢:
这是 Sourav Malakar 作为加尔各答大学数据科学实验室(由 A . k . choudh URY IT 学院和统计系设立)的博士学者的工作成果。与 Amlan Chakrabarti 教授和 Bhaswati Ganguli 教授的多轮讨论帮助很大。我们也感谢国家风能研究所(NIWE)的领域知识,最后但并非最不重要的是 LISA(https://sites.google.com/site/lisa2020network/about跨学科统计分析实验室)项目,该项目实际上使团队更具凝聚力。rested 的读者可以在这里阅读发表在 Springer Nature Applied Science上的完整文章。
使用 KNIME 中的树集成学习器构建用于领导决策的机器学习模型
为 KNIME 平台中的销售线索决策构建基于分类的预测模型的实用指南
作者图片
有机会在线索生成领域利用机器学习算法,我遇到了设计和实现各种线索评分和线索决策预测模型的可能性。之前,在讨论将基于 Python 的 Scikit-learn 模型集成到微软。NET 生态系统,我还介绍了这种线索评分解决方案的概念、潜力和好处。在参考文章的“进一步应用”一节中,我还提到了使用基于分类的算法开发销售线索决策集成模块。假设集成方法遵循已经描述的步骤,在本文中,我将更多地关注由 KNIME 平台支持的 lead decision 模型设计**。因此,我将强调构建系统的方法,包括导入、标准化、准备和应用数据以训练、测试、评估和优化决策模型的核心节点。**
*注意:本文中介绍的解决方案设计仅用于演示目的——简化到强调核心基础和构建模块的程度,但它也完全适用于在真实测试或生产部署系统中构建和导出基于预测决策的模型。
**注意:我不打算详细介绍树集成机器学习算法的本质或工作方式,以及与其配置参数相关的实际意义和各种可能性。深入了解每个配置属性的过程和详细说明是不同的综合性主题,需要特别注意和描述。更多信息可以在文章中提到的参考文献和文档中找到,也可以在官方图书馆的文档中找到。
什么是领导决策?
Lead Decision 遵循我在上一篇文章中定义的几乎相同的定义结构。唯一的区别只是向公司的销售线索数据库分配具体决策的技术,以指导营销和销售团队向最终客户转化。这种差异直接源于处理模型输出的技术方法,直接分配决策(不包括相关概率的计算)。虽然这看起来像是一个更一般化的方案,但它被认为是一个全面而有益的调整战略和提高营销和销售团队绩效和效率的过程。
导入数据
正如在构建线索评分原型的参考文章中提到的,我还使用了 Kaggle 上公开的相同的数据集。考虑到初始数据概述、探索性数据分析(EDA)的过程和数据预处理超出了本文的范围,我将直接介绍经过处理的数据结构概述,并将其导入到 KNIME 平台。在这方面,我使用了Jupyter Notebook environment和 NumPy 和 pandas 等数据分析和辩论库的优势,其次是 Matplotlib 和 seaborn 等统计数据可视化库的支持。
生成的数据集维度、属性及其在本地系统路径上的存储过程如下所示(数据文件以逗号分隔值格式保存)。
作者图片
在这个场景中,我不会考虑应用额外的特性工程和维度缩减,而是将所有特性(作为数据分析和处理的结果而检索到的)直接导入到 KNIME 平台中。
作者图片
数据过滤和标准化
考虑到数据最初是在 Jupyter Notebook workspace 中探索和处理的,我将继续解释过滤和标准化数据模块的设计,如下图所示。
作者图片
我使用用于手动配置的列过滤器设置数据过滤,它提供了删除一个额外的自动生成的存储记录的增量 id 值的列的功能。该节点还可以用于数据预处理过程中更高级的过滤场景,支持通配符和类型选择过滤模式。
作者图片
在数据过滤过程之后,我添加了标准的混洗节点操作器,用于实现随机记录顺序(应用默认配置)。随后结合规格化器节点,用于基于高斯(0,1)分布使用 Z 分数线性规格化规格化数字特征(在本例中为**‘总访问量’,‘网站总花费时间’,‘每次访问的页面浏览量’**)。
作者图片
更进一步,我还将标准化数据的输出连接到 Cronbach Alpha 节点中,用于比较各个列的方差和所有列之和的方差。一般来说,它代表一个可靠性系数,分别作为内部一致性和特征相关性的度量。系数的解释超出了本文的范围。
我将使用字符串节点的编号来结束此模块,以便将目标属性**‘已转换’**类型转换为字符串(以下模块需要该格式来准备数据和构建机器学习模型)。
作者图片
数据划分
该模块代表实际建模程序之前的阶段。因此,我使用分割节点和两对表格视图节点来将数据分割成训练和测试子集——这是继续使用监督机器学习方法的先决条件。
作者图片
我想提一下,我决定相对分割数据集,将 70%的数据分配给作为训练数据,30%作为测试数据,并配置它使用明确指定的随机种子随机抽取。这个过程有助于在多个不同的流执行中保持执行结果的一致性。在这种情况下,它可以确保数据集总是被完全相同地划分。插入表视图只是为了将数据传输到后面的模块,并改进流结构和可读性。
作者图片
模型建立和优化
通常,我决定使用树集成学习器和树集成预测器节点来实现监督机器学习模型。根据简介中引用的文章,构建线索决策系统意味着实施机器学习算法进行分类,而不是基于线索评分和逻辑回归的解决方案。由于我也提到了随机森林分类器,我想强调的是,系综树学习器代表了一个决策树系综(就像随机森林变量的情况一样)。KNIME 中还有随机森林学习器和随机森林预测器节点。尽管如此,我还是选择了系综树实现,因为结果解释过程中需要丰富的配置可能性和简化的属性统计——结果解释(特性重要性)超出了本文的范围。树集合节点被提供超参数优化/调整的参数优化循环开始和参数优化循环结束节点所包围——执行过程如下图所示。
作者图片
参数优化循环开始节点基于先前提供的配置开始参数优化循环。在这方面,它利用了 KNIME 中的流变量的概念,确保每个参数值将作为特定的流变量传递。我使用标准设置方法将**‘模型数量’、、【最大级别】和、【最小节点大小】**配置为树集成算法参数。利用值区间和步长,该循环提供了不同分类模型的构建和评估。
作者图片
同时,参数优化循环结束节点从流变量收集目标函数值,并将控制转移回相应的循环周期。它还提供了最佳参数输出端口,我从这里检索最佳参数组合,以获得最大化的配置精度。
作者图片
应通过使用计分器节点来支持这种设计方法,以在先前定义的测试数据集上提供循环结果模型预测。因此,为了实现这个流程,我将系综树预测器节点(默认配置)与评分机制连接起来,并将其包裹在优化循环周围。
不过,值得一提的是树集合学习者节点配置。在属性选择区中,需要配置的一切都是目标列属性,即“已转换”的数据集特性。
作者图片
更进一步,在树选项区域中,我将信息增益比率配置为算法分割标准,用于根据属性拥有的熵来标准化属性的信息增益。
作者图片
在配置窗口的底部可以看到,明确提到**【max levels】【minNodeSize】【NR models】由变量控制。这些是作为循环过程结果的输入流变量,我之前解释过了。因此,在这种情况下,每个创建的模型都将经历不同的配置参数——参数调整。考虑到需要显式的流变量匹配,我在流变量屏幕**中映射了具体的名称。
作者图片
另一方面,我通过查看集合配置将数据采样设置为随机模式。我配置了 sample(平方根)属性采样,并使用了每个树节点的不同属性集。如前所述,我还分配了一个特定的静态随机种子值。
作者图片
参数优化回路末端节点提供两个输出端口,最佳参数和所有参数。我使用所有参数概述来分析基于不同参数组合的销售线索决策分类的性能。这被认为是初步理解模型如何工作的良好实践,以及如何进一步优化它的指标,这些技术超出了本文的范围。
作者图片
在本模块的最后,我使用了表写入器节点来保存本地系统路径上存储的表中的最佳参数记录。
作者图片
作者图片
领导决策模型提取
最后的模块表示使用单独的树集成学习器和预测器节点的优化的机器学习分类器提取。我在这里使用的技术是前一个模块的后续,在那里我存储了从算法优化过程中检索到的最佳参数。通常,我使用表读取器节点从先前定义的系统位置读取表,并利用表行到变量节点的优势将表记录转换为变量格式。因此,变量转换配置,过滤精度值参数,如下图所示。
作者图片
作者图片
按照流变量输入的相同方法,我在树集成学习器上将变量参数配置为树集成算法参数。我必须在这里强调的是在提取最佳性能领导决策模型时,来自构建和优化的训练数据集应该是相同的。这同样适用于测试数据。这是使用在前一模块中解释的特定随机种子值实现的。此外,在树集成学习器的集成配置中配置的静态随机种子也应该在建立和优化以及提取最终模型的模块中被识别。
最后,我插入了一个计分器节点(JavaScript) ,用于分析提取的线索决策模型的性能。在这种情况下,我选择了支持 Java 脚本视图的 scorer 来生成更具洞察力和交互性的摘要。测试数据预测,包括混淆矩阵、类别和总体统计在下面的截图中显示。
作者图片
因此,类 0 代表**【未转换】** 导联,而类 1 代表**【已转换】导联**。该列也可以在数据处理过程中作为分类列对齐,这样就可以进行分类描述,而不是处理整数(转换成字符串)。
*注:根据对提取的销售线索决策模型的评估检索结果摘要。额外优化、结果解释和生成特征重要性的过程不属于本主题。但是,考虑到所需的场景目标和达到的 80%以上的总体准确性,这种模型构建结果是令人满意和可接受的。
最后的话
在本文中,我介绍了使用 KNIME 平台开发完整的销售线索决策系统的逐步设计过程。在这种情况下,我讨论了在建立工作基础设施方面实现的核心节点的使用,以便为先前分析和处理的数据提供主要决策。这种方法是最广泛的系统化方法的一部分处理和解释营销和销售数据,以建立更具洞察力和实用性的销售线索挖掘流程**。**
这个解决方案完全是使用 KNIME 设计和开发的,KNIME 是一个开源的数据分析、报告和集成平台,为机器学习提供了一组不同的组件。这是一个令人兴奋的环境,能够建立机器学习模型,同时以非常直观的视觉方式呈现它。除此之外,我认为它是一个快速建模的优秀平台,特别是在有尝试和分析多种不同机器学习算法的特定需求的情况下。同时,没有编码背景的研究人员或专业人员可以非常容易地使用它。它有一个非常简单的界面和集成的文档,用于构建不同的机器学习管道、流程和模型。
— — — — — — — — — — — —
感谢您花时间阅读本文。我相信它很有见地,给了你灵感。
欢迎分享你的想法,我会很感激你的建议和观点。
最初发表于【https://www.linkedin.com】。
使用雪花和 Dask 构建机器学习管道
弗雷德里克·菲利克斯在 Unsplash 上的照片
理解大数据
介绍
最近,作为一名数据科学家,我一直在努力寻找更好的方法来改善我的工作流程。我倾向于在工作中花费大量时间建模和构建 ETL。这意味着我越来越需要依赖工具来可靠、高效地处理大型数据集。我很快意识到,用熊猫来操纵这些数据集并不总是一个好方法,这促使我寻找其他替代方法。
在这篇文章中,我想分享我最近探索的一些工具,并向你展示我如何使用它们,以及它们如何帮助我提高工作流程的效率。我将特别谈到的两个是雪花和 Dask。两个非常不同的工具,但是可以很好地互补,尤其是作为 ML 生命周期的一部分。我希望在读完这篇文章后,你能很好地理解什么是雪花和 Dask,如何有效地使用它们,并且能够使用你自己的用例。
更具体地说,我想向您展示如何使用雪花和 Python 构建 ETL 管道,为机器学习任务生成训练数据。然后,我想介绍 Dask 和 Saturn Cloud ,并向您展示如何利用云中的并行处理来真正加快 ML 培训过程,从而提高您作为数据科学家的工作效率。
用雪花和 Python 构建 ETL
在我们开始编码之前,我最好简单解释一下雪花是什么。这是我最近在我的团队决定开始使用它时问的问题。在高层次上,它是云中的数据仓库。玩了一会儿后,我意识到它有多强大。我认为对我来说,最有用的功能之一是你可以使用的虚拟仓库。虚拟仓库让您可以访问相同的数据,但完全独立于其他虚拟仓库,因此计算资源不会在团队之间共享。事实证明这非常有用,因为它消除了其他用户全天执行查询所导致的任何潜在性能问题。这减少了等待查询运行时的挫败感和时间浪费。
由于我们将使用雪花,我将简要概述如何设置它,并开始自己试验。我们需要做到以下几点:
- 获得雪花账户设置
- 把我们的数据变成雪花
- 使用 SQL 和雪花 UI 编写并测试我们的查询
- 编写一个 Python 类,它可以执行我们的查询来生成我们的最终数据集用于建模
建立一个账户就像在他们的网站上注册免费试用一样简单。一旦你完成了,你可以在这里下载 snowsql CLI。这将使向雪花添加数据变得简单。按照这些步骤,我们可以尝试使用我们的凭证和命令行连接到雪花。
snowsql -a <account_name> -u <user_name>
登录雪花 UI 可以在网址中找到自己的账户名。它看起来应该是这样的:xxxxx.europe-west2.gcp。好了,让我们进入下一步,将我们的数据放入雪花中。这里我们需要遵循几个步骤,即:
- 创建我们的虚拟仓库
- 创建数据库
- 定义并创建我们的表格
- 为我们的 CSV 文件创建暂存表
- 将数据复制到我们的表格中
幸运的是,这并不太难,我们完全可以使用 snowsql CLI 来完成。对于这个项目,我将使用一个比我想要的更小的数据集,但不幸的是,我不能使用我公司的任何数据,而且很难在网上找到合适的大型数据集。不过,我确实从 Dunnhumby 找到了一些交易数据,这些数据可以在 Kaggle 上免费获得。只是为了好玩,我使用这些数据创建了一个更大的合成数据集来测试 Dask 与 sklearn 相比处理挑战的能力。
首先,我们需要在雪花 UI 中使用以下命令建立一个虚拟仓库和一个数据库。
创建 或 用
warehouse _ size = " X-SMALL "
auto _ suspend = 180
auto _ resume = true
initially _ suspended = true;
创建 或 替换 数据库dunhumby;
我们的数据由 6 个 CSV 组成,我们将把它们转换成 6 个表。我不会花太多时间浏览数据集,因为这篇文章更多的是关于使用雪花和 Dask,而不是解释数据。
下面是我们可以用来创建表格的命令。您需要预先知道的是您将使用什么列和数据类型。
**create** **or** **replace** **table** campaign_desc (
description **string**,
campaign number,
start_day number,
end_day number );**create** **or** **replace** **table** campaign_table (
description **string**,
Household_key number,
campaign number );**create** **or** **replace** **table** coupon (
COUPON_UPC number,
product_id number,
campaign number );**create** **or** **replace** **table** coupon_redempt (
household_key number,
**day** number,
coupon_upc number,
campaign number );**create** **or** **replace** **table** transactions (
household_key number,
BASKET_ID number,
**day** number,
product_id number,
quantity number,
sales_value number,
store_id number,
retail_disc decimal,
trans_time number,
week_no number,
coupon_disc decimal,
coupon_match_disc decimal );**create** **or** **replace** **table** demographic_data (
age_dec **string**,
marital_status_code **string**,
income_desc **string**,
homeowner_desc **string**,
hh_comp_desc **string**,
household_size_desc string,
kid_category_desc **string**,
Household_key number);
现在我们已经创建了表,我们可以开始考虑如何将数据放入其中。为此,我们需要存放 CSV 文件。这基本上只是一个中间步骤,因此雪花可以直接将文件从我们的阶段加载到我们的表中。我们可以使用 PUT 命令将本地文件放到我们的 stage 中,然后使用 COPY INTO 命令来指示雪花将这些数据放在哪里。
use database dunnhumby;**create** **or** **replace** stage dunnhumby_stage;PUT file://campaigns_table.csv @dunnhumby.public.dunnhumby_stage;PUT file://campaigns_desc.csv @dunnhumby.public.dunnhumby_stage;PUT file://coupon.csv @dunnhumby.public.dunnhumby_stage;PUT file://coupon_d=redempt.csv @dunnhumby.public.dunnhumby_stage;
PUT file://transaction_data.csv @dunnhumby.public.dunnhumby_stage;
PUT file://demographics.csv @dunnhumby.public.dunnhumby_stage;
作为快速检查,您可以运行此命令来检查临时区域中有什么。
ls @dunnhumby.public.dunnhumby_stage;
现在我们只需要使用下面的查询将数据复制到我们的表中。您可以在登录 Snowflake 后,在 Snowflake UI 或命令行中执行这些操作。
copy into campaign_table
from @dunnhumby.public.dunnhumby_stage/campaigns_table.csv.gz
file_format = ( type = csv
skip_header=1
error_on_column_count_mismatch = false
field_optionally_enclosed_by=’”’);copy into campaign_desc
from @dunnhumby.public.dunnhumby_stage/campaign_desc.csv.gz
file_format = ( type = csv
skip_header=1
error_on_column_count_mismatch = false
field_optionally_enclosed_by=’”’);copy into coupon
from @dunnhumby.public.dunnhumby_stage/coupon.csv.gz
file_format = ( type = csv
skip_header=1
error_on_column_count_mismatch = false
field_optionally_enclosed_by=’”’);copy into coupon_redempt
from @dunnhumby.public.dunnhumby_stage/coupon_redempt.csv.gz
file_format = ( type = csv
skip_header=1
error_on_column_count_mismatch = false
field_optionally_enclosed_by=’”’);copy into transactions
from @dunnhumby.public.dunnhumby_stage/transaction_data.csv.gz
file_format = ( type = csv
skip_header=1
error_on_column_count_mismatch = false
field_optionally_enclosed_by=’”’);copy into demographic_data
from @dunnhumby.public.dunnhumby_stage/demographics.csv.gz
file_format = ( type = csv skip_header=1
error_on_column_count_mismatch = false
field_optionally_enclosed_by=’”’);
好极了,如果运气好的话,我们第一次尝试就有数据了。哦,要是有那么简单就好了,整个过程我试了几次才弄对(小心拼写错误)。希望你能跟上这一步,做好准备。我们离有趣的东西越来越近了,但是上面的步骤是这个过程中至关重要的一部分,所以要确保你理解了每一个步骤。
用 SQL 编写我们的管道
在下一步中,我们将编写查询来生成我们的目标和功能,然后最终生成一个训练数据集。创建用于建模的数据集的一种方法是将这些数据读入内存,然后使用 pandas 创建新要素并将所有数据框连接在一起。这是你在 Kaggle 和其他在线教程中看到的典型方法。这样做的问题是效率不是很高,尤其是当您处理任何合理大小的数据集时。出于这个原因,将繁重的工作外包给 Snowflake 是一个更好的主意,它可以非常好地处理大量数据集,并可能为您节省大量时间。在这里,我不会花太多时间深入研究我们数据集的细节,因为它对我要展示的内容并不重要。不过,一般来说,在开始建模之前,您会希望花大量的时间来探索和理解您的数据。这些查询的目标是对数据进行预处理,并创建一些简单的特征,我们稍后可以在我们的模型中使用它们。
目标定义
显然,监督机器学习的一个重要组成部分是定义一个合适的预测目标。对于我们的用例,我们将通过计算用户是否在截止周之后的两周内再次访问来预测流失。2 周的选择是相当随意的,将取决于我们试图解决的具体问题,但让我们假设它对这个项目来说是好的。一般来说,你需要仔细分析你的客户,以了解访问间隔的分布情况,从而得出一个合适的客户流失定义。
这里的主要思想是,对于每个表,我们希望每个 household_key 有一行包含我们的每个特性的值。
活动特征
交易特征
下面我们根据总体统计数据创建一些简单的指标,如平均值、最大值和标准差。
人口特征
这个数据集有很多缺失数据,所以我决定在这里使用插补。有大量的技术可以处理缺失数据,从丢弃缺失数据到先进的插补方法。在这里,我让自己的生活变得简单,并用模式替换了缺失的值。我一般不推荐采用这种方法,因为理解为什么数据丢失对于决定如何处理它非常重要,但是对于这个例子来说,我将继续采用简单的方法。我们首先计算每个特征的模式,然后使用 coalesce 在数据丢失时用模式替换每一行。
培训用数据
最后,我们通过将我们的主表连接在一起为我们的训练数据构建一个查询,最终得到一个包含我们的目标、我们的活动、交易和人口统计特征的表,我们可以用它来构建一个模型。
作为一个简短的旁白,对于那些有兴趣了解更多关于雪花的特征和细微差别的人,我推荐以下这本书: 雪花食谱 。我开始读这本书,它充满了关于如何使用雪花的真正有用的信息,并且比我在这里做的更详细。
用于 ETL 的 Python 代码
我们需要这个 ETL 的最后一部分是编写一个脚本来执行它。现在,只有当您计划定期运行这样的 ETL 时,才真正需要这样做,但这是一个很好的实践,并且在需要时运行 ETL 会容易得多。
让我们简单讨论一下 EtlTraining 类的主要组件。我们班有一个输入,就是截止周。这是由于我们的数据集中定义数据的方式,但通常情况下,这将是一种日期格式,对应于我们希望选择的生成训练数据的截止日期。
我们初始化了一个查询列表,这样我们就可以轻松地遍历并执行它们。我们还创建了一个字典,其中包含我们传递给雪花连接的参数。这里我们使用我们在土星云中设置的环境变量。这里的是如何做到这一点的指南。连接到雪花并不太难,我们需要做的就是使用雪花连接器并传入我们的凭证字典。我们在雪花连接方法中实现了这一点,并将该连接作为属性返回。
为了使这些查询更容易运行,我将每个查询作为 python 字符串变量保存在 ml_query_pipeline.py 文件中。execute_etl 方法完全按照它在 tin 上所说的去做。我们遍历每个查询,格式化它,执行它,最后关闭雪花连接。
要运行这个 ETL,我们只需在终端中输入下面的命令。(其中 ml_pipeline 是上面脚本的名称。)
python -m ml_pipeline -w 102 -j ‘train’
简单地说,您可能希望定期运行这样的 ETL。例如,如果您想要进行每日预测,那么您需要每天生成一个这样的数据集,并传递到您的模型中,这样您就可以确定哪些客户可能会流失。我不会在这里详细讨论这个,但是在我的工作中,我们使用气流来编排我们的 ETL,所以如果你感兴趣,我建议你去看看。事实上,我最近买了一本名为《使用 Apache Airflow 的数据管道的书,我认为这本书很棒,它给出了一些关于如何使用 Airflow 的可靠例子和建议。
Dask 和建模
现在我们已经建立了数据管道,我们可以开始考虑建模了。我写这篇文章的另一个主要目的是强调在 ML 开发过程中使用 Dask 的优势,并向你们展示它的易用性。
对于项目的这一部分,我还使用了 Saturn Cloud ,这是我最近遇到的一个非常好的工具,它允许我们在云中的计算机集群上利用 Dask 的能力。对我来说,使用 Saturn 的主要优势是,分享您的工作非常容易,在您需要时扩展您的计算非常简单,并且它有一个免费的层选项。一般来说,模型开发对于 Dask 来说是一个非常好的用例,因为我们通常想要训练一堆不同的模型,看看什么最有效。我们做得越快越好,因为我们有更多的时间关注模型开发的其他重要方面。与雪花类似,你只需要在这里注册,你就可以非常快速地建立一个 Jupyter 实验室的实例,并开始自己进行实验。
现在,我意识到在这一点上我提到过几次 Dask,但从未真正解释过它是什么。所以让我花一点时间给你一个非常高层次的概述 Dask 和为什么我认为它是可怕的。非常简单,Dask 是一个 python 库,它利用并行计算来允许您在非常大的数据集上处理和执行操作。最好的部分是,如果你已经熟悉 Python,那么 Dask 应该非常简单,因为语法非常相似。
下图突出显示了 Dask 的主要组件。
来源: Dask 文件
集合允许我们创建一个可以在多台计算机上执行的任务图。其中一些数据结构可能听起来很熟悉,比如数组和数据框,它们与 python 中的数据结构相似,但有一些重要的区别。例如,您可以将 Dask 数据帧想象为一组 pandas 数据帧,它们的构建方式允许我们并行执行操作。
从集合开始,我们有了调度器。一旦我们创建了任务图,调度程序就会为我们处理剩下的工作。它管理工作流,并将这些任务发送到单台机器或分布到整个集群。希望这能让你对 Dask 的工作原理有一个简单的了解。要了解更多信息,我建议查看一下文档或者这本书。两者都是深入探讨这个主题的很好的资源。
用于建模的 Python 代码
建模时,我倾向于先尝试少量的算法。这通常会给我一个好主意,什么可能适合我的具体问题。这些模型是逻辑回归、随机森林和梯度推进。根据我的经验,当处理表格数据时,这些算法通常会给你很好的结果。下面我们用这三个模型建立一个 sklearn 模型管道。我们在这里使用的确切模型并不重要,因为管道应该适用于任何 sklearn 分类模型,这只是我的偏好。
事不宜迟,让我们深入研究代码。幸运的是,我们将大部分预处理外包给了雪花,因此我们不必在这里过多地摆弄我们的训练数据,但我们将使用 sklearn 管道添加一些额外的步骤。
下面的第一段代码展示了使用 sklearn 时的管道。请注意,我们的数据集是一个普通的旧 pandas 数据帧,我们的预处理步骤都是使用 sklearn 方法执行的。这里没有什么特别不寻常的事情。我们从雪花 ETL 生成的表中读入数据,并将其传递给 sklearn 管道。通常的建模步骤在这里适用。我们将数据集分为训练和测试,并做一些预处理,即使用中位数估算缺失值,缩放数据并一次性编码我们的分类数据。我是 sklearn 管道的忠实粉丝,现在每当我开发模型时,基本上都使用它们,它们确实有助于代码简洁明了。
这个管道在大约有 200 万行的数据集上表现如何?在没有任何超参数调整的情况下运行这个模型大约需要 34 分钟。哎哟,有点慢。你可以想象,如果我们想要进行任何类型的超参数调整,这将需要多长时间。好吧,不太理想,但让我们看看 Dask 是如何应对挑战的。
Dask ML Python 代码
我们的目标是看看我们是否能打败上面的 sklearn 管道,剧透一下,我们肯定能。Dask 很酷的一点是,当你已经熟悉 python 时,入门的门槛相当低。我们可以在 Dask 中建立并运行这条管道,只需做一些更改。
你可能会注意到的第一个变化是我们有了一些不同的进口。这条管道与前一条管道的主要区别之一是,我们将使用 Dask 数据框架而不是 pandas 数据框架来训练我们的模型。你可以把 Dask 数据帧想象成一堆熊猫数据帧,我们可以同时对每个数据帧进行计算。这是 Dask 并行性的核心,也是减少该管道培训时间的原因。
注意我们使用 @dask.delayed 作为我们的load _ training _ data函数的装饰器。这指示 Dask 为我们并行这个功能。
我们还将从 Dask 导入一些预处理和管道方法,最重要的是,我们将需要导入 SaturnCluster,这将允许我们创建一个用于训练模型的集群。这段代码的另一个关键区别是,在我们的训练测试分割之后,我们使用了 dask.persist 。在这之前,由于 Dask 的懒惰计算,我们的函数实际上没有被计算过。一旦我们使用了 persist 方法,我们就告诉 Dask 将我们的数据发送给 workers,并执行到目前为止我们已经创建的任务,并将这些对象留在集群上。
最后,我们使用延迟方法训练我们的模型。同样,这使我们能够以一种懒惰的方式创建我们的管道。在我们到达以下代码之前,管道不会被执行:
fit_pipelines = dask.compute(*pipelines_)
这一次,我们只花了大约 10 分钟就在完全相同的数据集上运行了这条管道。这是 3.4 倍的加速,不算太差。现在,如果我们想的话,我们可以通过在土星上按一下按钮来扩大我们的计算资源,从而进一步加快速度。
部署我们的管道
我之前提到过,你可能希望使用类似气流这样的东西,定期运行这样的管道。碰巧的是,如果你不想为气流设置一切的最初麻烦,土星云提供了一个简单的替代工作。作业允许我们打包代码,并定期或根据需要运行它。您只需转到一个现有项目,然后单击 create a job。一旦我们这样做了,它应该看起来如下:
来源:土星
从这里开始,我们需要做的就是确保我们上面的 python 文件在图像中的目录中,并且我们可以输入上面的 python 命令
python -m ml_pipeline -w 102 -j 'train'
如果愿意,我们还可以使用 cron 语法设置一个时间表,每天运行 ETL。对于那些感兴趣的人来说,这里有一个教程,可以深入到所有的细节。
结论和要点
好了,我们现在已经到了项目的尾声。显然,我已经忽略了 ML 开发周期中的一些关键部分,例如超参数调整和部署我们的模型,但也许我会留待以后再说。我觉得你应该试试达斯克吗?我绝不是专家,但就我目前所见,它确实非常有用,作为一名数据科学家,我非常兴奋能够对它进行更多的实验,并找到更多的机会将其融入我的日常工作中。希望您发现这很有用,并且您也可以看到雪花和 Dask 的一些优点,并且您将开始自己尝试它们。
资源
我的其他一些帖子你可能会感兴趣
注意:这篇文章中的一些链接是附属链接。
从数据质量开发平台构建机器学习工作流
直接从 YData 开发平台集成 AzureML Studio 的指南
照片由记者拍摄于 Freepik
数据在开发机器学习管道中具有很高的风险,采用一种获得高质量数据的方法至关重要。有意义的数据不仅稀缺且嘈杂,而且获取成本高昂。
像吴恩达这样的专家认为,机器学习目前达到的门槛只能通过提高数据的质量和数量来突破。
随着机器学习变得越来越普遍,人工智能公司显然面临新的挑战,对此,以模型为中心的方法可能没有所有的解决方案。我们 YData 提倡以数据为中心的方法——因为一切都始于数据,也止于数据。
YData 是首个提高数据质量的数据开发平台。它提供的工具不仅可以理解数据质量及其对 ML 模型的影响,还可以提供更高质量的数据准备。我们相信互操作性是优化业务体验的关键——正是出于这一目的,我们将 YData 设计为与许多其他 MLOps 平台高度集成。
微软 Azure 是一个广泛用于各种部署的服务平台,包括在 MLOps 中——它具有成本效益,易于使用,并为任何新手开始部署提供了出色的文档。
我们很高兴分享 YData 和 Azure 之间的新集成!
作者创造的形象
本文将深入探讨我们如何集成这两个强大的机器学习平台,以提高用于训练 ML 模型的数据质量,并使它们能够在部署后提供有价值的见解。
为什么要集成 YData 和 Azure?
我们理解构建最佳性能模型的重要性。我们也意识到高质量的数据对构建这一平台至关重要。许多人忘记的是,在商业环境中,构建解决方案所花费的时间也同样重要。
虽然公司关注数据质量和训练有素的模型,但为数据科学团队提供易于使用的开发平台也至关重要。结合 YData 和 Azure 将提高数据科学团队的生产力,改善 ML 模型提供的价值,并为在生产环境中轻松部署可扩展的解决方案铺平道路。
以下是使用 YData 作为开发平台的一些优势:
- YData Labs 不是你本地机器上的 Jupyter 笔记本。YData Labs 运行在 Kubernetes 集群之上,可以在您需要扩展时进行扩展,无需您的干预,如果不需要计算资源,还可以缩小规模。
- 能够以分布式方式连接来自 AWS、Azure、GCP 等数据源的数据源,确保高吞吐量。
- 合成数据生成器和能够复制真实世界数据的模型,通过设计确保相似的统计属性和隐私。
- 数据质量评估报告确保您的数据符合您设定的高标准。
- 集成到流行的云提供商和 MLOps 平台
简而言之,YData 允许与 Azure(和其他流行的云平台)无缝集成,使公司能够快速、可扩展和良好监控地将其数据优化训练模型部署到生产中。
既然我们已经了解了为什么要进行集成的背景,我们将把重点放在您可以遵循的集成步骤上(剧透:这比您想象的要简单得多。)我们将使用电信客户流失数据集,其中我们的目标是根据历史模式预测客户是否会在短期内流失。
我们鼓励您从我们的 GitHub 资源库中找到这个相关的 Jupyter 笔记本,并继续关注。
设置 AzureML
AzureML 允许为您的部署提供“点击创建”工作区,这将在一个地方存储您的模型、服务端点和用于服务它们的计算实例。
在“类别”中选择人工智能和机器学习,然后单击“创建”获得“机器学习”
AzureML 工作区供应(所有剩余截图由作者提供)
我们使用您的 AzureML 工作区中的 config.json 文件从 YData 连接和操作它。
从 YData 的实验室连接到您的 AzureML 工作区
azureml python SDK 创建了一个 API,它调用函数来远程创建、操作和删除云工作区上的资源。
创建模型
我们选择实现逻辑回归,因为它没有对特征空间中类的分布做任何假设。它可以很容易地扩展到多个类别,如软搅棒、硬搅棒,具有类别预测的自然概率视图。
但是,您可以尝试其他算法,如 XGBoost 和 Random Forests,并检查模型性能。
部署为 Web 服务
AzureML 部署遵循任何容器化部署的标准流程:
- 定义环境
- 配置依赖关系
- 插入代码并部署
评分脚本将模型加载到容器中,也作为要预测的数据样本的加载点。
在 ACI (Azure 容器实例)内部执行预测的代码
部署环境配置是在容器中安装所需的 python 包。
容器——预装了 python 包和要运行的“score.py”代码——现在将在 Azure 上部署和运行。
Azure 容器实例很容易在控制台上部署和监控。
微软 Azure 机器学习工作室端点。
一旦端点被部署并运行,我们就使用测试数据对其进行测试。
根据测试数据进行预测
这个想法是将测试数据集输入到模型中以获得预测。
这里,我们将数据作为 pandas dataframe 发送到 ACI 中托管的 web 服务。然后,我们使用 SDK 的“运行”API 来调用服务。您还可以使用任何 HTTP 工具(如 curl)进行原始调用。
通过 HTTP 请求进行预测
您还可以发送原始 HTTP 请求来测试 web 服务。一个样本代码,其中随机选择一个客户,并预测他们是否会流失。
因此,在部署时,您的模型可以通过发送 HTTP 请求来发送预测。当您的模型集成到需要实时预测的用户友好的 web/移动应用程序中时,这是一个常见的应用程序。
可能性是无限的
数据科学和人工智能超越了任何特定的工具或技术。作为数据科学家,我们应该学会如何利用最好的工具来实现各种目的。学习适应更新的工具并组合它们以充分利用它们是建立高效和多产的数据科学团队的必备技能。
因此,本文展示了如何利用 YData 平台和 Microsoft Azure。
我们从创建 AzureML studio 开始,使用 YData 的数据源连接器加载数据,构建模型,直接从 YData 的实验室设置 azure 环境,部署和服务于实时预测——我们已经完成了所有工作。
虽然这看起来很难理解——但这是再简单不过的了。请参考我们资料库中的本 Jupyter 笔记本了解更多技术细节和代码。通过这种集成,您可以同时拥有 Azure 和 YData 平台的优势。
你知道我们有一个友好的 slack 社区吗?我们的开发人员在那里帮助你从你的人工智能项目中获得最大的利益,并获得你的宝贵反馈。加入我们吧!
构建内存高效的元混合推荐引擎:从后到前(第 1 部分)
如何从头开始构建一个推荐器,在保持简单的同时提高其准确性
你会推荐什么?
2020 年至 2021 年的环境使得越来越多的企业主考虑将与客户的主要通信转移到网上。你可能已经注意到,预测、引导和购买的在线活动(甚至是线下活动)的数量最近发生了多么大的变化?似乎任何互联网企业都尽最大努力与客户保持无休止的对话。在这样的对话中,客户期望从卖家那里接收至少相关的个人报价,以便更快地做出选择。
面向客户的个人报价是由所谓的推荐系统(recsys)产生的。Recsys 是机器学习算法的一个子类,用于获得用户可能偏好的物品的排序列表。
整个 recsys 的种类可以分为几类(由 Rocca,2019 ):
图片作者:Baptiste Rocca
在这一系列出版物中,我们将浏览:
- 基于记忆的推荐系统的机制
- 构建你自己的基于用户的协同过滤推荐器
- 应用来自流行 python 模块的“开箱即用”推荐器
- 评价推荐者效率和准确性的技巧
沿着这条路,我们将分享我们在设计所谓的元混合推荐引擎以解决实际业务问题方面的经验。
推荐引擎是如何工作的?
数据介绍
在该出版物中,我们将使用数据集。
除了用户的评级,数据集还包含关于电影本身的描述性信息(如上映年份、类型、内容标签),我们将在第二部分使用这些信息来提高推荐者预测的准确性。
为了理解推荐器是如何工作的,让我们通过几个步骤来创建一个微数据集:
- 从主数据集中随机选择几个用户
- 编辑一个电影列表,上面列表中的每部电影都有 3 个或更多的用户看过
- 从前面的列表中随机选择几部电影
- 从随机选择的用户和电影的交集上的所有用户-电影对构建评级矩阵。
设计定制推荐引擎
由此产生的评级矩阵代表了 recsys 表现的典型背景:一些用户可以从我们的微库中收到他们以前没有看过的电影的推荐;推荐者必须对个人推荐进行排名,以保持用户对未看过电影的喜爱。
解决这些任务所需的最少信息在评级的微矩阵本身中,即用户与所看电影的交互历史(即除了我们的协同过滤推荐器的“记忆”之外的所有信息)。
基于记忆的推荐器简单地基于由另一组{A…X}用户分配给那个 Z 电影的评级来预测 Y 用户对一部未看过的 Z 电影的评级,所述另一组用户的电影偏好与 Y 用户的相似。
在最简单的情况下,Y-Z 对的评级预测被计算为 Z-movie 的一组可用评级的加权平均值,取决于 Y-user 与每个{A…X}用户的“相似度”。
“相似度”是两个用户的评级向量之间的距离,通过减去平均值来标准化(缩放)。因此,分级评级向量的平均值为零,这允许我们用零安全地填充所有缺失的值(即特定用户对未看过的电影的评级)。
用户评级的标准化向量之间的余弦距离矩阵。
正如所料,矩阵主对角线的所有元素都等于零(用户到自己的距离)。
去掉主对角线后,我们将得到每个用户到系统中所有其他用户的距离向量。此外,在推荐引擎中,这些距离将被转换为“逆”权重,以加权平均值的形式计算评级。
换句话说,我们的定制推荐器迭代地预测每个用户电影对(包括用户未看过的电影!成对)作为特定电影的可用评级的向量和与从特定用户到其评级被考虑的其他用户的距离成反比的权重向量的乘积。
尽管很简单,但我们的定制推荐器可以处理缺少实际用户评级的情况(即评级矩阵中缺少单元)以及用户“相反”电影偏好的情况(即双向分级评级向量)。
我们的客户推荐器的平均绝对百分比误差(MAPE)大约是 15.5%
生产就绪型推荐器
我们已经建立了一个推荐器,它可以预测评分,并可以产生一个排名推荐列表——这太棒了,哇!
然而,我们的“原样”推荐器有相当多的缺点:
- 它不允许从基于用户的协同过滤切换到基于项目的协同过滤
- 它不允许在计算向量乘积时微调要考虑的用户数量
- 它不适用于具有高级预测算法的实验
- 最后,它需要优化来处理大型数据集
所有这些缺点,对于用 惊喜 这样的 python 模块搭建的推荐器来说,都是无关紧要的。
例如,将来自 surprise 家族(即 [KNNWithMeans](https://surprise.readthedocs.io/en/stable/knn_inspired.html# surprise.prediction_algorithms.knns.KNNWithMeans) )的稍微更高级(但大体上接近于上面描述的那个)的预测算法应用于相同的“非常评级”微矩阵“开箱即用”推荐器,您会立即将 MAPE 从 15.5%降低到 11.4%:
两个演示推荐器的预测结果显示在可视化界面上:
有价值的推荐:小心对推荐人的评价
我们通常不使用 MAPE /梅/ RMSE 来比较推荐者的准确性,而是使用高于指定阈值的估计评分的项目列表的准确性和召回率。
请注意,如果我们决定将我们的电影微库中推荐的电影的最低评级设置为 3.5 或 4.0,对相同用户的推荐将如何变化?
在本出版物的下一部分,我们将展示如何使用适当的指标并提高“开箱即用” SVD 预测算法(来自惊喜模块)的效率,该算法被称为网飞奖获得者。
使用 TensorFlow 从头构建 MobileNet
在 TensorFlow 中从头开始创建 MobileNet 架构
图一。(来源:安德里亚·德·森蒂斯峰在 Unsplash 上拍摄的照片)
之前我已经讨论了 MobileNet 的架构和它最重要的层“ 深度方向可分离卷积 ”,在故事中— 理解深度方向可分离卷积和 MobileNet 的效率。
接下来,我们将看到如何使用 TensorFlow 从头开始实现这个架构。
实施:
图二。MobileNet 架构(来源:图片来自原论文)
图 2 显示了我们将用代码实现的 MobileNet 架构。网络从 Vonv、BatchNorm、ReLU 块开始,然后是多个 MobileNet 块。它最终以一个平均池和一个完全连接的层结束,具有一个 Softmax 激活。
我们看到这个架构有这样的模式——conv dw/S1,然后是 Conv/s1,等等。这里的 dw 是深度层的步数,后面是 Conv 层的步数。这两行是 MobileNet 块。
“过滤器形状”列给出了内核大小和要使用的过滤器数量的详细信息。该列的最后一个数字给出了过滤器的数量。我们看到过滤器数量逐渐从 32 增加到 64,从 64 增加到 128,从 128 增加到 256,等等。
最后一列显示了随着我们深入网络,图像的大小是如何变化的。输入尺寸选择为 224*224 像素,具有 3 个通道,输出层分类 1000 个类。
图 3。普通 CNN 架构(左)与 MobileNet 架构(右)之间的差异(来源:图片来自原始论文)
构建网络时需要记住的几件事:
- 所有层之后是批量归一化和 ReLU 非线性。
- 与具有 Conv2D 层的普通 CNN 模型不同,MobileNet 具有深度方向的 Conv 层,如图 3 所示。要更好地理解这一层,请参考— 深度方向卷积块。
工作流程:
- 从 TensorFlow 库中导入所有必需的图层
- 为 MobileNet 块编写一个助手函数
- 构建模型的主干
- 使用辅助函数来构建模型的主要部分
导入图层
**import tensorflow as tf**#import all necessary layers**from tensorflow.keras.layers import Input, DepthwiseConv2D
from tensorflow.keras.layers import Conv2D, BatchNormalization
from tensorflow.keras.layers import ReLU, AvgPool2D, Flatten, Dense****from tensorflow.keras import Model**
Keras 已经内置了一个 DepthwiseConv 层,所以我们不需要从头开始创建它。
MobileNet 模块
图 4。MobileNet 块的表示(来源:图片来自原始论文)
为了创建 MobileNet 块的函数,我们需要以下步骤:
- 函数的输入:
a .一个张量(x)
b .卷积层的滤波器数量(滤波器)
c .深度方向卷积层的步距(步距)
2.运行(图 3 —右侧图像):
a .应用具有步长的 3×3 去 hwise 卷积层,之后是批量归一化层和 ReLU 激活
b .应用带有滤波器的 1x1 卷积层,之后是批量归一化层和 ReLU 激活
3.返回张量(输出)
这 3 个步骤在下面的代码块中实现。
# MobileNet block**def mobilnet_block (x, filters, strides):
x = DepthwiseConv2D(kernel_size = 3, strides = strides, padding = 'same')(x)
x = BatchNormalization()(x)
x = ReLU()(x)
x = Conv2D(filters = filters, kernel_size = 1, strides = 1)(x)
x = BatchNormalization()(x)
x = ReLU()(x)
return x**
建立模型的主干
如图 2 所示,第一层是 Conv/s2,过滤器形状为 3x3x3x32。
图 5。模型的主干(来源:图片来自原论文)
#stem of the model**input = Input(shape = (224,224,3))****x = Conv2D(filters = 32, kernel_size = 3, strides = 2, padding = 'same')(input)
x = BatchNormalization()(x)
x = ReLU()(x)**
模型的主要部分
图 6。模型的主体部分(来源:图片来自原论文)
# main part of the model**x = mobilnet_block(x, filters = 64, strides = 1)
x = mobilnet_block(x, filters = 128, strides = 2)
x = mobilnet_block(x, filters = 128, strides = 1)
x = mobilnet_block(x, filters = 256, strides = 2)
x = mobilnet_block(x, filters = 256, strides = 1)
x = mobilnet_block(x, filters = 512, strides = 2)****for _ in range (5):
x = mobilnet_block(x, filters = 512, strides = 1)****x = mobilnet_block(x, filters = 1024, strides = 2)
x = mobilnet_block(x, filters = 1024, strides = 1)****x = AvgPool2D (pool_size = 7, strides = 1, data_format='channels_first')(x)
output = Dense (units = 1000, activation = 'softmax')(x)****model = Model(inputs=input, outputs=output)
model.summary()**
图 7。模型摘要的片段
绘制模型
#plot the model**tf.keras.utils.plot_model(model, to_file='model.png', show_shapes=True, show_dtype=False,show_layer_names=True, rankdir='TB', expand_nested=False, dpi=96)**
图 8:模型图的一个片段
使用 TensorFlow 的整个 MobileNet 模型实现:
**import tensorflow as tf**#import all necessary layers**from tensorflow.keras.layers import Input, DepthwiseConv2D
from tensorflow.keras.layers import Conv2D, BatchNormalization
from tensorflow.keras.layers import ReLU, AvgPool2D, Flatten, Dense****from tensorflow.keras import Model**# MobileNet block**def mobilnet_block (x, filters, strides):
x = DepthwiseConv2D(kernel_size = 3, strides = strides, padding = 'same')(x)
x = BatchNormalization()(x)
x = ReLU()(x)
x = Conv2D(filters = filters, kernel_size = 1, strides = 1)(x)
x = BatchNormalization()(x)
x = ReLU()(x)
return x**#stem of the model**input = Input(shape = (224,224,3))****x = Conv2D(filters = 32, kernel_size = 3, strides = 2, padding = 'same')(input)
x = BatchNormalization()(x)
x = ReLU()(x)**# main part of the model**x = mobilnet_block(x, filters = 64, strides = 1)
x = mobilnet_block(x, filters = 128, strides = 2)
x = mobilnet_block(x, filters = 128, strides = 1)
x = mobilnet_block(x, filters = 256, strides = 2)
x = mobilnet_block(x, filters = 256, strides = 1)
x = mobilnet_block(x, filters = 512, strides = 2)****for _ in range (5):
x = mobilnet_block(x, filters = 512, strides = 1)****x = mobilnet_block(x, filters = 1024, strides = 2)
x = mobilnet_block(x, filters = 1024, strides = 1)****x = AvgPool2D (pool_size = 7, strides = 1, data_format='channels_first')(x)
output = Dense (units = 1000, activation = 'softmax')(x)****model = Model(inputs=input, outputs=output)
model.summary()**#plot the model**tf.keras.utils.plot_model(model, to_file='model.png', show_shapes=True, show_dtype=False,show_layer_names=True, rankdir='TB', expand_nested=False, dpi=96)**
结论
MobileNet 是最小的深度神经网络之一,快速高效,可以在没有高端 GPU 的设备上运行。当使用 Keras(tensor flow 上)这样的框架时,这些网络的实现非常简单。
相关文章
要了解如何使用 TensorFlow 实现其他著名的 CNN 架构,请访问以下链接-
参考资料:
Howard,A.G .,Zhu,m .,Chen,b .,Kalenichenko,d .,Wang,w .,Weyand,t .,Andreetto,m .,& Adam,H. (2017)。MobileNets:用于移动视觉应用的高效卷积神经网络。 ArXiv,abs/1704.04861 。
用 HTML 构建现代轻量级 NLP Python 接口
为什么是鳗鱼?继续读!(戴维·克洛德博士讲解 Unsplash
如何将 Python 和 Web 设计结合起来,立即创建漂亮而强大的数据科学图形用户界面
为什么?
众所周知, Python 是当今实现自然语言处理管道的流行编程语言。虽然用 Python 编写一个简单的 GUI 框架并不是一件很困难的任务,但是构建一个现代而漂亮的界面却是一个真正的噩梦,涉及到外部库、繁重的设计工具和陡峭的学习曲线。
例如,像 Tkinter 这样的框架,唯一一个内置到 Python 标准库中的框架,提供了创建 GUI 应用程序的快速而简单的方法,但涉及大量的样板代码,并且根本不是为了呈现好看的现代界面而设计的。
真的吗?这是 1995 年吗?(图片由作者提供)
也就是说,Tkinter 可以用于管理开发的早期阶段,并创建简单的仪表板来监控或调整我们的模型参数。对于其他一切,人们可能应该去别处看看。
然而,在这种情况下寻找其他地方意味着登陆软件,这些软件通常是专有的,并且总是难以消化。最流行的 Tkinter 替代品之一是 Qt ,这是一个用于创建生产质量图形界面和跨平台应用程序的小部件工具包。使用这个框架可以开启 无限的机会,但也有代价:你需要安装一个全新的环境,学习如何使用 Python 绑定,掌握复杂的设计接口等等。结果会很棒,但是……值得吗? 如果你对这个问题的回答是否,那么这篇文章就是给你的。
是的,我们需要找到一种方法来提供漂亮的图形用户界面,但是整个 3 GB 的设计套件看起来有点大材小用。(图片作者提供)
这篇博文旨在提供一个简单且易于管理的环境,通过现代且设计良好的用户界面在本地展示数据科学模型。通过将 Python 与 HTML、CSS 绑定,将 Javascript 与 Eel (一个用于制作简单电子类应用程序的小 Python 库),熟悉 web 设计的人可以轻松扩展这个环境**,在几个小时内**构建出令人惊叹的 web 应用程序,几乎不需要额外的努力。
带着这个目标,我决定实现一个问答(QA)机器人,名为“问我任何事情(AMA)机器人”,基于 Deepset 的 RoBERTa 实现,托管在 Huggingface 上,并在 SQuAD 2.0 上接受训练以进行提取 QA。使用 NLP,我设计了这个项目来托管和显示 Huggingface 的模型,但是完全相同的环境可以扩展到任何 Python 管道来满足您的需求。
怎么会?
最酷的部分来了。用 Eel 构建一个基于 web 的 python GUI 是很容易的事情。Eel 是一个轻量级 Python 库,用于制作简单的离线 web 应用程序,可以完全访问 Python 功能和库。它托管本地 web 服务器,然后让您用 Python 注释函数,以便可以从 Javascript 调用它们,反之亦然。
换句话说,如果你知道 Python 并且熟悉前端 web 开发,你就可以开始了。
Eel 提供了 Python 和 Javascript 之间的双向绑定。(图片由作者提供)
构建应用
除了安装 Python 之外,构建这些应用程序没有特别的先决条件。它们只依赖于 Eel 模块,它可以像任何其他 Python 模块一样通过 pip -install 加载到您的虚拟环境中。当然,同一个环境应该包括项目所需的所有其他依赖项。在我的例子中,我也需要 Transformer 库。
一旦你的环境准备好了,像往常一样编写你的 Python 代码,用 @eel.expose 修饰你想要公开给 Javascript 端的函数。这里有一个 AMA 机器人 python 代码的例子:
从 Javascript 方面来说,现在可以通过 Eel 对象访问 Python 功能。
对于 web 应用程序的外观,规则与任何其他 web 应用程序相同:创建 HTML 文件,用 CSS 样式化它们,使之与 Javascript 交互。为了使它更容易,你可以使用 Bootstrap 或者任何你喜欢的框架。
以下是最终结果:
来自 AMA 机器人网络应用的屏幕截图。(图片由作者提供)
如果您需要关于如何实现 Eel 应用程序的更多细节,请查看作者资源库中的文档。
结论
Eel 是数据科学家和机器学习研究人员的游戏规则改变者,他们需要立即用干净和现代的仪表板显示他们的结果。这个轻量级的绑定器填补了玩具级图形用户界面和产品级图形用户界面之间的空白,提供了一种巧妙的方式来获得兼顾 Python 和 HTML/CSS/JS 的中级图形用户界面。
作为一个在医学信息学领域工作的人,每当我需要向临床医生和同事展示 ML 演示时,我发现这特别有用。在不损失太多时间的情况下,我们可以交付真实且引人注目的应用程序,帮助利益相关者理解我们工作的潜力。
在这里你可以找到这个项目的 GitHub 库。请随意使用 eel 来创建令人惊奇的 NLP 应用程序和更多!
https://github.com/detsutut/ama-bot
我希望这篇文章对你有用!如果你喜欢我的工作,现在你也可以给我买一瓶啤酒!🍺
https://www.linkedin.com/in/tbuonocore
使用金属着色器在 Swift 中构建神经网络
使用金属性能着色器框架构建神经网络
克里希纳·潘迪在 Unsplash 上拍摄的照片
介绍
在上一篇文章中,我从零开始实现了神经网络框架。它支持 CPU 多线程,但不支持 GPU 计算。在这篇文章中,我将实现类似的框架,但使用金属性能着色器。 WWDC19 session 614 启发我写这篇文章。
Metal 性能着色器框架包含一组高度优化的计算和图形着色器,旨在轻松高效地集成到您的 Metal 应用程序中。这些数据并行原语经过专门调整,以利用每个 GPU 系列的独特硬件特征来确保最佳性能。
金属性能着色器提供了高度灵活的神经网络编程。
神经网络数据模型
数据模型。图片由 Yauheni Stsefankou 提供。
我们的神经网络模型由模型的层、图和参数组成。
神经网络的参数。图片由 Yauheni Stsefankou 提供。
参数是批量大小和每次训练的时期数。
神经网络图。图片由 Yauheni Stsefankou 提供。
在我们的模型中有两个图:训练图和推理图。两者重量相同。
我们的神经网络框架将有很多层,如密集,下降,平坦,卷积 2D,汇集最大值和平均值,Sigmoid 和 ReLU 激活。
为了方便起见,你可以在我的 GitHub 库中找到所有代码。
不可训练层
这些层的实现非常简单,因为它们没有权重。他们在不改变策略的情况下不断转换输入。
基本图层类为空,因为所有图层没有共同点。
池层有填充和池模式、过滤器大小和步幅。
漏失层非常简单,它只有一个保持输入值的概率。
展平图层也很简单。我们必须提供展平宽度(输入尺寸)。
激活层不需要存储任何数据,所以它们会像基本层类一样为空。
只剩下两个未实现的层:密集和卷积 2D。对于它们,我们需要实现数据源类。它包括神经元的权重、偏差和层参数。
卷积和密集图层的数据源
我们必须在特定的金属向量中存储重量、偏差及其速度和动量。此外,我们在这些向量和优化器的参数上保留指针。
MPSCNNConvolutionDataSource 协议正在强制我们实现一些功能。
第一个函数返回权重的数据类型。我们将保持权重值为 32 位浮点数。
第二个函数返回卷积描述符,它是在类初始化时用给定的参数创建的。
这些函数返回指向带有层权重和偏差的向量的指针。
该方法加载权重和偏差。
这两个函数是插头,因为它们对于我们的任务是不必要的。
而这是ConvDataSource
类中的最后一个函数。它返回数据源的标签。
卷积和密集层的权重使用公共卷积参数初始化,例如内核大小、输入通道和滤波器的数量、步幅和学习速率。
这里最基本的东西是优化器和卷积描述符。它们存储我们的层的主要参数,并在用它们训练时更新权重。
在初始化权重和偏差向量之前,我们必须计算它们的长度并为它们创建描述符。
现在,当我们有了描述符,我们可以初始化重量,速度和动量的向量。
然后,我们对偏差向量做同样的处理。
我们使用内核的 glorot 初始化。这意味着我们在-limit 到 limit 的范围内随机生成内核权重,其中 limit 是 sqrt(6/(inputFeatureChannels+outputFeatureChannels))
内核生成后,我们初始化权重和偏差状态和命令缓冲区,所有的初始化都将在这里执行。
然后,我们运行命令完成,并等待它完成。
卷积和密集层
密集层就大不一样了,不需要填充。
图形生成
为了从层中创建图形,我们需要实现函数。
图形是在将节点修补到其他节点时创建的。我们需要存储最后一个节点的结果图像作为新节点的源。
在 switch-case 语句中,我们将为层创建节点。每个节点在初始化时都需要源。
卷积节点需要权重的数据源和填充策略。
激活函数不需要任何参数。
展平图层需要平面输入大小(您可以通过运行 getOutputSize 方法而无需展平和下一个图层来获得)。该层将输入转换为 1x1 矩阵,其通道数等于平面输入大小。
脱落节点仅包含在训练图中。它需要保值的概率。
密集层结构类似于卷积层,但不需要填充策略。
最后一种情况是默认的,所以如果当前图层不是图层类的子类,那么我们就不会在图中包含任何节点。
我们的神经网络框架将仅支持神经网络模型末端的 softmax 激活函数。我们将 softmax 的损失节点添加到训练图中,并将 softmax 节点添加到推理图中。
神经网络模型初始化
在我们实现了为我们的模型创建节点的方法之后,我们可以实现我们的 NN 模型的初始化。
那么,我们需要什么来初始化模型呢?首先,我们需要MTLDevice
。
MTLDevice
协议定义了 GPU 的接口。你可以向MTLDevice
查询它为你的金属应用提供的独特功能,并使用MTLDevice
发布你所有的金属命令。
我们需要的第二样东西是来自我们MTLDevice
的MTLCommandQueue
。
一个
MTLCommandQueue
对象将命令缓冲区的有序列表排队等待[MTLDevice](https://developer.apple.com/documentation/metal/mtldevice)
执行。命令队列是线程安全的,允许多个未完成的命令缓冲区同时编码。
第三个是层数组,我们将在createNodes()
函数中使用它来构建我们的图表。
最后两个参数是基本的模型参数:时期数和批量大小。
首先,我们将初始化的参数复制到模型的本地存储中。
为了得到最终节点,我们运行createNodes()
方法。但是在方法运行之后,我们得到了没有丢失节点的节点(除了 softmax 丢失节点)。
为了创建训练图,我们需要添加损失节点并获得损失出口,因此我们运行trainingGraph
方法。它返回图形的亏损退出点。在我们的例子中,它必须只包含一个止损点。
在得到损失退出点后,我们可以用它来创建训练图。
推理图初始化类似于训练图。这种初始化更容易,因为我们不必创建丢失节点。
创建推理图与创建训练图非常相似。
数据集实现
我们的数据集支持两种类型的数据:图像和字节。
数据集包括样本数组、图像大小参数(如果数据类型是图像)、类别标签(我们的神经网络模型应该是分类器)。
为了方便使用,数据示例复制了数据类型。此外,它将标签索引存储在数据集中的classLabels
数组中。样本的主体取决于数据类型。如果数据类型是图像,那么样本存储CGImage
及其MTLTexture
。
在数据样本初始化之前,我们需要创建从 CGImage 到 MTLTexture 的 convert 方法。
我将 convert 方法实现为一个可计算的变量。
正因为如此,我不得不再次得到 default MTLDevice
。金属编码中最常见的事情就是使用描述符。我们需要创建纹理描述符。我们纹理的像素格式是r8Unorm
。这意味着这里只有红色通道(灰度),值表示为无符号的 8 位规范化整数。此外,我们将 CGImage 的宽度和高度复制到描述符中。我们的纹理区域的原点为零,大小等于 CGImage 的大小。
我们颜色空间使用每个分量 8 位,每个像素一个分量(红色通道)。另外,我们的颜色空间不使用 alpha 通道。
我们使用加速框架使图像符合我们的纹理格式。之后,我们使用图像来获取可以写入纹理的数据。
这种方法有助于我们更容易地创建数据样本。数据样本的初始化是定义类型,从实参中复制参数并获取纹理(如果类型是图像)。
但是我们不能用我们的数据样本来构建模型。所以,我们必须实现向MPSImage
的转换。
MPSImage
可以用纹理轻松初始化。当数据类型是字节时,我们用描述符创建平面MPSImage
,并向其中写入字节。
好的。我们实现了数据样本类。现在我们必须实现数据集初始化。
用字节初始化数据集非常简单。我们所要做的就是为每个样本提供带有字节数组和标签的初始化器。
此外,我们将从包含图像的子文件夹的文件夹中编写初始化程序。要处理文件夹和文件,我们必须使用默认的FileManager
。
然后,我们实现我们的文件结构。首先,我们在给定的文件夹中寻找子文件夹。每个子文件夹代表一个唯一的类。然后,我们在每个子文件夹中寻找文件。
之后,我们尝试从每个文件中读取图像。如果我们成功读取图像,那么我们可以使用CGContext
将图像调整到格式。最后一件事是从图像中创建DataSample
并将其添加到 samples 数组中。
对于这种情况,当我们只有一个数据集,并希望将其分为训练集和测试集时,我们必须编写一个适当的方法。它复制所有的公共参数,如类标签数组、图像大小和数据类型。
之后,它使用Dictionary
按类划分样本。
然后,它使用给定的测试集百分比划分样本的所有类别。
培养
我们的训练过程分为几个时期。在开始新的纪元之前,我们必须等到前一个纪元完成。我们在训练之前和每个时期之后评估我们的模型。
在每一个时期,我们都把所有的样本分批送去训练。
但是在实现trainIteration
方法之前,我们必须实现从数据集获取训练批次。
获取训练批次并不难,因为我们实现了从样本中获取MPSImage
。Batch 是这些图像的数组。我们还必须用标签改变损失状态批次。
丢失数据是 float 类型的数组,其中除了正确的变量之外,所有变量都等于 0。
得到训练批次后,我们必须将其编码为MTLCommandBuffer
。
在训练迭代中,我们得到训练批次及其期望值。使用双缓冲信号量导致永久的 100% GPU 负载。
我们必须将完整的处理程序添加到命令缓冲区中。训练迭代结束后,统计训练损失并输出到终端。我们使用fflush
来替换先前的迭代状态。
为了得到训练损失,我们将一个批次中的所有损失相加,然后除以批次大小。
最后,我们用命令缓冲区同步批处理,启动缓冲区中的命令并返回它。
估价
在评估时,我们计算正确答案并除以测试集中的样本数。然后,我们从数据源重新加载推理图,因为在训练过程中权重只在训练图中改变,而不在推理图中改变。
评估也将有双缓冲信号量。
批量备考甚至比在培训中还要容易。我们只需要一组数据样本。
然后我们将使用predict
方法,因为我们需要实现它。它返回一个标签预测数组。我们将预期与预测进行比较,并评估模型。
为了预测,我们必须准备数据样本。我们将它们转换成 MPSImages 并编码到命令缓冲区。
将推理批次编码到命令缓冲区非常类似于将训练批次编码。唯一的区别是,我们对推理图进行批量编码,而不是训练。
当 GPU 上的操作完成后,我们可以检查批处理中每个项目的输出。
然后,我们读取批处理中每一项的输出,并通过最大值的索引追加一组答案。这就是分类器的工作方式。
例子
我尝试了使用 ETL 数据集的平假名分类和使用 MNIST 的数字分类。经过 10 分钟的训练,平假名分类器的准确率达到 75%,数字分类器的准确率达到 98%。
结论
这个神经网络框架比我们之前做的版本快多了。然而,我们的实现只支持分类器。如果你真的对用金属性能着色器构建其他神经网络模型感兴趣,我们的框架很灵活,你也可以修改它。保存模型是一件很平常的事情,这就是为什么我在这篇文章中没有考虑它。你可以在我的 GitHub 库中找到保存有模型的源代码。
感谢阅读。如果你有任何建议或者你在这篇文章中发现了错误,请在评论中留下你的反馈。我已经准备好改善我的读者的体验。
使用 Gradio 和拥抱面部变形金刚构建 NLP Web 应用程序
向同事和客户展示你的 NLP/ML 解决方案时,web 应用是必不可少的。但是构建和部署一个有时会是一场噩梦。输入 Gradio。
蔡钦汉的情绪分析网络应用的 Gif。
Web 应用程序设计和部署可以说是数据科学家和分析师最容易忽视的技能之一。然而,如果你需要向同事或客户展示你的 NLP 或机器学习解决方案,这些技能是必不可少的。
虽然一个好的演示会有很长的路要走,但没有什么比拥有一个原型更好的了,在这个原型中,非技术用户可以自己测试提议的解决方案。如果你最近几年尝试过部署一个 ML web 应用,你就会知道大多数托管服务对于那些前端开发经验有限的人来说并不容易。
相比之下,Gradio 是一个相对较新的库,它使 ML 和 NLP web 应用程序的创建和共享变得轻而易举。只需几行代码,您就可以将您的模型放在许多标准模板中,这样就不必创建单独的 HTML 模板,也不必考虑 UI、按钮的颜色和大小等问题。
可以肯定的是,Gradio 模板非常简约。但是坦率地说,在测试和开发的早期阶段,这就是你所需要的全部,那时的目标仅仅是快速测试和快速迭代。Gradio 的与抱抱脸的 变形金刚库和模型中枢的紧密集成使其成为一个更加强大的工具。
当然,Gradio 不是唯一可以用来快速开发 web 应用程序的库。 Streamlit 和 Plotly 是这一领域的另外两个著名名字,各有所长。但是我要说 Gradio 是迄今为止最用户友好的,并且是早期 NLP/ML 解决方案开发的游戏改变者。
通过几个 Jupyter 笔记本,我将分享我如何使用 Gradio 构建独立的和“链式链接”的 NLP 应用程序的例子,这些应用程序结合了不同的功能和 transformer 模型。
回购、文件和项目范围
我为这个项目准备的 repo 包含了运行本文中的例子所需的所有文件: 5 个 Jupyter 笔记本、 2 个音频文件和一个腌制的逻辑回归模型(演示 Gradio 在 transformer 模型之外的用法)。
这里的演示更适合新手,以及那些处于 NLP/ML 项目早期探索阶段的人。Gradio 提供了托管和私有部署的选项,但是这已经超出了本文的范围。
除了定义 NLP/ML 模型的输入和输出之外,只需几行代码就可以启动并运行 Gradio 应用程序。如果你使用拥抱脸的公共推理 API ,这个过程就更简单了。但我不会在我的例子中采用这种方法,因为我发现 Hugging Face 的公共/免费推理 API 相对较慢,如果你试图一次加载太多 transformer 模型,你的应用程序可能会崩溃。
因此,我编写了在本地机器上运行的演示程序。要公开分享 app,只需要在 Gradio 界面更改一个参数即可。
1.独立的情绪分析应用
让我们从一个最简单的例子开始——使用 Hugging Face 的管道 API 构建一个用于情感分析的 web 应用。情感分析管道中的默认蒸馏模型返回两个值—一个标签(正或负)和一个分数(浮点)。
Gradio 消除了从零开始设计 web 应用程序的痛苦,并解决了如何正确标记两个输出的问题。下面的屏幕截图展示了如何通过在 Gradio 中更改一些参数来轻松调整应用程序的外观:
屏幕截图:蔡振汉
由于 Gradio 和 transformers 库之间的紧密集成,只需几分钟就可以调整 notebook1.0 中的代码,将情感分析 web 应用程序转变为一个用于翻译、摘要或零镜头分类的应用程序。
2.独立的 TROLL TWEET 探测器应用程序
通过 Scikit-learn 等标准机器学习库,Gradio 可以很好地处理酸洗模型。在笔记本 1 中。 1,我加载了我在之前的项目中为巨魔推文构建的逻辑回归分类器,Gradio 应用程序很快就启动并运行了:
屏幕截图:蔡振汉
Gradio 模板是准系统,毫无疑问。但这就是我在早期阶段所需要的,以查看模型是否如预期的那样工作,以及解决方案是否对非技术观众来说是清楚的。
3.“平行”中的梯度—比较两种汇总模型
最近,几乎不可能跟上各种 NLP 任务可用的新 transformer 模型的数量。例如,如果你正在做一个文本摘要项目,你如何展示拥抱脸的模型中枢上可用的 248 个模型中哪一个更适合你的用例?或者,您如何证明您自己的微调模型比其他模型表现得更好?
Gradio 提供了一个简洁的解决方案,允许在同一个应用程序中“并行”加载 transformer 模型。这样,您可以直接比较一个输入的不同结果:
屏幕截图:蔡振汉
在 notebook2.0 中,我开发了一个快速网络应用程序来比较两种不同模型的摘要能力:FB 的 Bart 和 Google 的 Pegasus。这是一个直接比较多个模型的结果的好方法,而不必复制不同应用程序的结果,或者在两个模型之间来回切换屏幕。
这也是比较文本生成或翻译模型性能的一个很好的方法,在这种情况下,不同模型的结果会有很大的不同。Gradio 没有说明一次可以并行加载的模型的最大数量,所以根据需要应用一些常识性的限制。
4.“系列”中的 GRADIO 结合两种变压器模型进行翻译和总结
另一种利用大量变形金刚模型的方法是将它们“串联”起来,即在一个 Gradio 应用程序下连接不同功能的模型。
Notebook3.0 演示了如何构建一个翻译摘要器,它接收中文文本并生成英文翻译摘要:
屏幕截图:蔡振汉
正如上面的截屏图所示,最终结果并不令人印象深刻。这很好地提醒了约束在这些 NLP/ML 项目中的重要性——仅仅因为一些东西在技术上可行并不意味着结果会有任何好处。
虽然链接不同功能的多个 transformer 模型的能力是一个非常受欢迎的能力,但要找到一个有效的组合并交付良好的结果还需要付出相当多的努力。
5.具有音频输入和文本输出的语音到文本“混合媒体”应用程序
我的最后一个例子,在 notebook4.0 中,演示了一种使用humping Face 对脸书的 Wav2Vec2 模型的实现来构建简单的语音到文本网络应用的快速方法。
使用最新版本的 Gradio,您可以轻松配置混合媒体应用程序,这些应用程序采用一种特定的输入格式,例如音频或视频,并以另一种格式输出,例如文本或数字。为了简化演示,我选择了一款语音转文本应用,它可以接收音频剪辑并返回文本文本:
屏幕截图:蔡振汉
一个新人使用 Flask 和 HTML 模板创建一个类似的语音到文本的应用程序可能需要几个小时,如果不是几天的话,更不用说在正确部署到托管提供商的过程中还要经历额外的考验。
对于 Gradio,这个特殊示例的挑战主要在于定义一个函数来处理较长的音频文件,而不会出现会导致本地计算机崩溃的内存不足问题:
创建应用程序界面实际上只需要一行代码:
我已经在 repo 中包含了两个音频文件,你可以尝试一下语音转文本应用程序——美国总统约翰·肯尼迪在 1961 年的著名就职演说,以及青年诗人阿曼达·戈尔曼在 2021 年美国总统乔·拜登就职典礼上的诗。
结束注释
坦率地说,数据专业人员高效工作所需的技能越来越多,这是非常荒谬的。很少有人(如果有的话)会有时间学习与数据相关的编码和分析技能,以及前端开发。
像 Gradio 这样的库有助于将所需技能“压缩”成更易于管理的东西,让数据科学家和分析师不必为了一个简单的演示而花费数小时摆弄 Flask 和 HTML。
从长远来看,我认为这有助于促进机器学习的更大透明度和问责制,因为数据专业人员不再有任何借口说他们不能快速构建应用程序来让其他人尝试提议的解决方案。
随着越来越多的人开始在早期阶段测试此类应用,希望深埋在特定解决方案的训练数据和设计中的偏见和道德问题将更容易被发现。
和往常一样,如果你在这篇文章或我之前的文章中发现了错误,请联系我:
- 推特:蔡锦鸿
- 领英:【www.linkedin.com/in/chuachinhon
用 Apache Kafka 构建 Python 微服务:一分耕耘一分收获
工程师在日常工作中经常使用阿帕奇卡夫卡。Kafka 执行的主要任务是:阅读消息、处理消息、将消息写到一个主题,或者在一定时间内聚合消息。窗口可以是固定的(例如每小时聚合)或滑动的(例如从某个时间点开始的每小时聚合)。
鉴于 Kafka 作品的复杂性,我不建议您尝试从头构建自己的解决方案。微调、测试和支持将非常困难。幸运的是,有现成的实现,比如 Kafka Streams for Java 和 Kafka Streams for Python——由 Robinhood 编写的 Python Faust 。该团队还有一个社区支持的分支— 浮士德流媒体。
本文分享了我在 Provectus 使用 Apache Kafka 构建异步 Python 微服务进行“通信”的经验。 Faust ,一个将 Kafka 流的思想移植到 Python 的流处理库,被用作微服务基础。Faust 还为间隔任务和调度任务提供了一个 HTTP 服务器和一个调度程序。在一个测试实现中,我将使用 FastAPI、Grafana 和 Prometheus 等工具和库。
浮士德
浮士德是卡夫卡流在 Python 上的实现。它最初由 Robinhood 开发,现在仍被用作高性能分布式系统和实时数据管道的默认库。
该库适用于 Python 3.6+和 Kafka 0.10.1+,并支持各种数据存储、数据收集和加速模块。Faust 使您能够利用常见的 Python 库,如 NumPy、SciPy、TensorFlow、SQLAlchemy 等。
项目架构
我相信要真正学会如何做一件事,你应该一步一步地去做。下面是我们将要构建的一个简单测试项目的架构图。
作者图片
测试数据将在演示服务器微服务中编译,而数据请求者微服务将每秒请求数据。数据请求者是我们 Faust 系统中的第一个微服务。一旦数据被请求,data _ requestor会将回复推送给 Kafka。然后,这个消息被 data_processor 读取、处理并推回给 Kafka。随后,它被 data_aggregator 和 db_loader 读取,其中第一个微服务计算平均值,第二个微服务将它们加载到数据库中。 db_loader 还加载由 data_aggregator 生成的消息。同时,当用户请求时, api_gateway 可以从数据库中提取数据。使用 Prometheus 监控所有活动,并可在 Grafana 中可视化。该项目和附加服务在 docker-compose 推出。
第一步。基础设施设置
因为我们将使用 docker-compose 加速项目,所以描述所有第三方服务是有意义的。我们将需要 Apache Kafka,一个数据库,一个管理仪表板来管理它,以及几个监控服务 (Prometheus 和 Grafana)。让我们使用 Postgres 作为我们的数据库,使用 PgAdmin 作为它的管理和监控组件。
在这个阶段,我们的 docker-compose.yml 看起来是这样的:
*docker-compose,*与基础设施、配置和附加脚本一起,存储在存储库的单独文件夹中。
微服务的常见组件
尽管每个服务都有自己的任务,并使用两个不同的库(FastAPI 和 Faust)编写,但它们有几个共同的组件。
首先是基于配置加载器库的配置加载器。它使您能够使用 yaml 文件加载配置,并通过可变的环境重新定义它们的值。这在需要在 docker 容器中重新定义特定值的情况下非常有用。
其次是普罗米修斯出口商。您可以使用它来导出数据和指标,并在 Grafana 中显示它们。请记住,它在 FastAPI 和 Faust 中的实现略有不同。
第二步。数据仿真微服务
为了测试和演示该系统,我们将使用一个简单的服务,以 JSON 格式返回货币对的值。为此,我们也将使用 FastAPI 库。这项服务和 Flask 一样容易使用,但是它自带了 async 和 Swagger UI 文档。可以在这里找到:http://127 . 0 . 0 . 1:8002/docs
我们将只为服务指定一个请求:
从上面的代码中可以看出,每个请求都返回一个 JSON,其中包含两对随机生成的值。回复可能是这样的:
{
“USDRUB”: 85.33,
“EURRUB”: 65.03
}
要监控这些值,请将 Prometheus 添加到系统中。它应该作为中间件添加,并有单独的指定路径。
您可以在浏览器中查看您的值的指标:http://127 . 0 . 0 . 1:8002/metrics
第三步。数据请求者微服务
在 Faust 的帮助下,API _ requestor微服务请求来自演示服务器的数据。因为 Faust 是异步的,我们可以简单地使用 aiohttp 客户端来请求测试数据。
首先,让我们创建一个 Faust 应用程序。
创建应用程序时,请确保指定了它的名称,这是一个必需的参数。如果我们启动几个同名的服务版本,Kafka 将在它们之间分配分区,使我们能够横向扩展我们的系统。接下来,我们需要在 value_serializer 中指定消息序列化的方法。在这个例子中,我们可以照原样读取原始的消息,以便随后序列化接收到的消息。我们还应该定义访问 Faust 托管的 HTTP 服务器的地址和端口。
接下来,我们需要定义将要接收来自 demo_server 的回复的主题。
第一个参数是主题名;这是强制性的。然后,您可以指定分区的数量(可选)。请记住,分区的数量必须与 Kafka 中主题分区的数量相似(在创建主题时指定)。
请注意,data _ requestor服务并不从主题中读取消息,而是不断地将请求推送到数据模拟器,然后处理回复。要实现这一点,您需要编写一个实时时钟函数,该函数每秒触发一次,或者在指定的时间间隔触发。
我们可以看到该功能是由 @app.timer decorator 周期性执行的。它接收每秒间隔作为参数。然后,该函数为 DataProvider 生成一个类实例,负责提供对请求的回复。在每一个随后的请求之后,普罗米修斯计时器的值增加。如果数据是按请求接收的,它将被发送到一个主题。因为 Kafka 使用特定的键和字节内消息,所以我们需要在将数据推送到主题之前对其进行序列化。
在启动应用程序时,您还需要初始化 Prometheus。为此,您可以简单地使用启动应用程序时调用的函数。
请记住,函数是由 @app.task decorator 分配和定义的。Prometheus 使用自己的端口作为独立的服务器启动,它与 Faust 的 HTTP 服务器并行工作。
给你!每秒向 Kafka 发出请求和写入数据并不困难。添加监控组件也可以很快完成。
第四步。数据处理微服务
我们的下一个微服务——数据处理器——处理由 api 请求者微服务接收的数据对。应用程序初始化和数据监控的代码与我们用来管理data _ requestor的代码相同。然而,在 data_processor 的情况下,服务从主题接收消息来处理它们。可以用函数来完成。
该函数基于*@ app . agent(src _ data _ topic)装饰器,该装饰器告诉该函数处理 src_data_topic 中的消息。从 stream.items() 的 msg_key,msg_value 的异步读取消息。*
然后,我们需要序列化接收到的消息,并提取货币对及其值。每一对都应该分别写一个相应的主题。
第五步。数据聚合微服务
请求和处理数据的微服务以流的形式在 Kafka 中读写数据。每个新消息都独立于以前的消息进行处理。但是,偶尔您可能需要同时处理一定数量的消息。假设您需要找到最后十对值的平均值。为此,你的系统应该能够在某个地方存储这 10 对。您不能在本地存储数据:如果您触发了聚合服务的多个版本,每个版本都将只在本地存储其值,这将导致平均值计算不正确。这里是你可以利用浮士德表的地方。
这些表将值存储在 changelog 主题和本地——rocks db 中。这允许服务的所有版本同步工作。重启后,它从本地数据库恢复状态,当读取可用的变更日志时,它继续工作。
表的主题名是这样分配的: - -changelog。在我们的系统中,名称是:data-aggregator-average-changelog。
在表中处理和存储新消息非常简单:
从上面的代码可以看出,您需要定义函数如何处理主题中的消息。收到的每个新值都应存储在表中,以计算平均值。您可以以类似于标准 Python 字典的方式使用该表。
第六步。数据库加载器微服务
db_loader 微服务一次读取两个主题——data-aggregator-average-changelog和processed _ data——两个。它将消息从 data_aggregator 写入第一个主题,从 data_processor 写入第二个主题。这就是为什么我们需要描述消息处理的两个函数。
与其他服务类似,您需要:读取消息,将其存储在数据库中,更新指标。为了管理数据库,我们将使用 ORM SQLAlchemy。请注意,您应该将其设置为异步工作。为此,定义所需的依赖关系。
asyncpg==0.23.0
SQLAlchemy==1.4.0
在配置中指定 DB URI 。
DB_URI: “postgresql+asyncpg://postgres:postgres@127.0.0.1:5432/currencies”
在数据库管理代码中使用异步会话。
第七步。结果请求者微服务
最后,让我们使用 FastAPI 创建一个简单的服务,从数据库中请求结果。它将从数据库中读取结果,并将它们推送到 JSON。要管理数据库,可以使用 ORM(就像我们之前做的那样)。至于 FastAPI,说明类似于步骤 2。数据仿真微服务。
结论
在 Python 上用 Apache Kafka 构建微服务相当简单。你可以把处理和管理卡夫卡的几乎所有工作都交给浮士德。然后,只需描述和定义函数,就能看到它们实时处理您的消息。
我希望这篇教程能帮助你更好地理解如何将微服务、Apache Kafka 和 Python 结合起来。如果您对 Apache Kafka 中的集群监控和管理感兴趣,我建议您也查看一下本文。
有什么问题吗?请在评论区联系我进行讨论。
构建可复制的机器学习管道
如何安全地部署机器学习模型
可重复性是企业进一步理解和信任机器学习在我们日常生活中的应用所需的责任。
随着机器学习变得更加生产化,许多企业和研究人员可能会感到不得不匆忙采取行动,这不利于充分理解实现某些方法所涉及的复杂性,或者在没有正确程序的情况下匆忙完成过程会牺牲什么,所有这些都是为了更快地获得结果。
随着信息唾手可得,机器学习失败成为头条新闻,就像微软的 Twitter 聊天机器人的情况一样,公众的怀疑态度增加了,因此强调了任何方法中可重复性的必要性和重要性。
注:本文中的内容摘自我在 Udemy 上的机器学习模型部署课程的笔记。
什么是再现性?
在机器学习环境中,再现性是指精确复制模型的能力,使得当模型被传递相同的输入数据时,复制的模型将返回相同的输出。
未能事先考虑再现性可能会在开发过程中产生一些后果。一个典型的例子是从金融的角度。如果一个人在研究环境中对模型开发投入了大量资源,但在生产中却无法重现模型,那么模型及其预测基本上是无用的。这浪费了时间、精力和资金,全都付诸东流。
此外,再现性在允许我们对照以前的解决方案检查我们的机器学习模型方面起着关键作用。如果不能再现以前的结果,我们就无法准确区分新模型是否超过了以前模型的性能。
再现性的挑战
在创建和构建机器学习模型时,从业者将在整个过程中在多个环境中工作——一个环境描述了软件(或其他产品)被开发或投入运行的计算机的状态。开发机器学习模型通常涉及三种环境:
- 研究环境 —在研究环境中,数据科学家将执行数据分析以了解数据,构建模型,并对其进行评估,以更好地了解模型正在做出什么决策,以及它是否符合项目预期结果的标准。
- 开发环境 —在开发环境中,机器学习工程师寻求重现在研究环境中开发的机器学习流水线。该环境还结合了软件工程最佳实践,以确保管道可以作为一个适当的软件系统运行。
- 生产环境— 在生产环境中,模型能够服务于其他软件系统和/或客户。
研究环境之外的每个环境都试图复制管道开发的研究。但是,每个环境达到所需状态的步骤是不同的。从数据采集到从模型获得预测,这几乎在流水线的每一个单独步骤中都给再现性带来了许多潜在的障碍和挑战。
数据收集的再现性
永远记住数据先于科学;没有数据,机器学习模型是没有意义的。数据采集是再现性主题中最重要也是最难解决的问题之一。
“一个模型永远不会完全一样,除非用完全相同的数据和过程来训练它。”——索勒达德·加利博士。机器学习模型部署(Udemy)讲师
当在研究环境中工作时,数据科学家可能可以访问一个版本的训练数据,然而,当机器学习工程师必须在生产中再现流水线时,数据可能已经改变。这取决于一些数据库的工作方式。他们可能会不断地用更新的版本覆盖旧版本的数据,从而随着时间的推移改变训练数据。此外,如果数据科学家使用 SQL 查询数据,则会引入随机性,因为 SQL 会随机地将数据加载到内存中。
虽然没有一种方法在所有情况下都是绝对可靠的,但是有很多方法可以解决这些问题。例如,一个解决方案可能是确保数据科学家保存用于建立模型的训练数据的快照。当数据非常大时,如果它妨碍团队遵守数据法规,这种方法就会失败。
另一种方法,也可能是更理想的方法,是向数据源添加准确的时间戳,这可以在以后用于识别哪些数据用于训练模型。但是,请务必注意,如果在进行更新时数据不断被替换,这种方法不能解决问题。进一步补充一下,如果数据库不是为跟踪时间戳而设计的,那么可能需要很大的努力来集成这一功能。
特征工程中的再现性
如果我们在数据采集阶段的再现性方面出错,那么在试图再现管道的特征工程阶段时,可能会出现问题。
“如果两种环境中的数据不相同,从数据中得出的参数将会改变”——Solledad Galli 博士。机器学习模型部署(Udemy)讲师
假设数据中有缺失值,而数据科学家选择通过输入要素的平均值来填充这些值。如果研究和生产环境中的训练数据之间存在差异,那么特定功能的平均值也可能存在差异。当然,如果来自研究环境的训练数据可以被复制,那么这个问题就消失了。
在特征工程阶段,可能阻碍再现性的其他因素包括:
- 复杂的提取方法(问题与研究环境中数据缺乏可重复性有关)
- 不保持超参数不变(它们必须在不同环境之间保持不变)
- 依赖于随机采样的特征生成方法(设置种子参数可确保生成相同的样本)
利用版本控制的力量来跟踪所有的差异可以解决与再现特征工程阶段相关的问题——如果它们不是源于不能再现数据获取阶段的话。
建模中的再现性
一些机器学习模型在训练过程中引入了随机性,这给再现性带来了挑战。这种现象的一个主要例子是树集合(随机森林,梯度增强等。)和神经网络。
例如,随机森林模型在构建每一棵树时使用装袋和特征随机性来创建不相关的森林。并且,神经网络在权重初始化期间引入了随机性。这种随机性会导致模型之间的不一致,无论它们是否基于相同的训练数据。
与特征工程类似,应对这种再现性威胁的简单解决方案是在需要时设置种子和跟踪超参数。
部署中的再现性
在机器学习工作流的部署阶段,再现性的挑战围绕着将模型集成到其他系统的失败。
部署中再现性失败的一个主要例子是,用于训练模型的群体不能代表真实环境中的群体。这可能是由于在实际或研究环境中使用了在建模时未被检测到的过滤器,或者构建模型的数据科学家没有完全理解模型在生产中的使用方式。
另一种情况发生在编程语言在不同环境之间变化的时候。机器学习通常用 Python 或 R 来完成,而应用程序通常使用 C++或 Java。这可能会激励团队用另一种语言再现研究环境,从而显著增加 1)人为错误和 2)部署错误的可能性。使用一种编程语言是克服这一挑战的最佳方式。
最后,软件版本的改变可能妨碍在部署中再现机器学习流水线的能力,因为每个改变可能导致流水线内的差异。这个问题不太常见,然而,它可能是最难解决的,因此确保所有软件环境在整个过程中是相同的是至关重要的。
包裹
数据科学家和机器学习工程师在试图确保机器学习工作流的可重复性时,往往会面临几个挑战。虽然有些问题需要重大变革才能克服,但其他问题可能相当简单(例如,埋下种子)。要点是,必须解决所有潜在的再现性障碍,以获得真正的再现性。
感谢您的阅读!
如果你喜欢这篇文章,请通过订阅我的**每周简讯与我联系。不要错过我写的关于人工智能、数据科学和自由职业的帖子。**
相关文章
**https://medium.datadriveninvestor.com/machine-learning-model-deployment-b1eaf7ca96cd [## 机器学习工作流程
towardsdatascience.com](/the-machine-learning-workflow-1d168cf93dea) https://medium.datadriveninvestor.com/machine-learning-engineers-must-read-these-5-books-583e81922b84 **
使用 PyTorch 为时间序列构建 RNN、LSTM 和 GRU
照片由恩库鲁列科乔纳斯拍摄。
用新的工具包重新审视十年之久的问题
从历史上看,时间序列预测一直由线性和集成方法主导,因为在特征工程的支持下,它们在各种问题上得到很好的理解和高度有效。部分由于这个原因,深度学习在某种程度上被忽视了;换句话说,与图像识别、语音识别和 NLP 等其他领域相比,它对时间序列预测的影响较小。
随着 80 年代递归神经网络(RNN) 的出现,随后是更复杂的 RNN 结构,即 1997 年的长短期记忆(LSTM) ,以及最近 2014 年的门控递归单元(GRU) ,深度学习技术使得能够通过有限的特征工程来学习顺序输入和输出之间的复杂关系。简而言之,这些 RNN 技术和类似的技术在以以前不实用的方式分析大规模时间序列方面具有巨大的潜力。
在这篇文章中,我想给你介绍一些 RNN 结构,如 RNN、LSTM 和 GRU,并帮助你开始建立时间序列预测的深度学习模型。虽然这不是本文的重点,但我将提供一些广泛应用于时间序列预测的特征工程技术,比如一次性编码、滞后和周期性时间特征。我将使用 Scikit-learn 、 Pandas 和 PyTorch ,这是一个主要由脸书人工智能研究实验室开发的开源机器学习库。虽然前两者长期以来一直是数据科学家和机器学习实践者的最爱,但 PyTorch 相对较新,但越来越受欢迎。然而,由于它的新近性,我从一开始就很难找到相关的信息和代码样本,对于已经存在一段时间的框架来说,比如说 TensorFlow ,这通常要容易一些。所以,我决定把我想早点知道的事情整理一下。少说多做:我们从哪里开始?
数据在哪里?
嗯,我想我们需要一些时间序列数据作为开始。无论是支付交易还是股票交易数据,时间序列数据无处不在。一个这样的公共数据集是 PJM 的每小时能源消耗数据,这是一个从美国不同地区收集的 10 多年每小时观测数据的单变量时间序列数据集。我将使用 PJM 东部地区的数据,该数据最初包含 2001 年至 2018 年的每小时能耗数据,但链接中提供的任何数据集都应该可用。
鉴于有大量关于数据可视化的博客,我将把探索性数据分析(EDA)部分写得很短。对于感兴趣的人,我可以推荐使用 Plotly 来创建交互式图形。以下方法将绘制一个简单的交互式图形,该图形允许您交互式地处理数据集。
2012 年至 2018 年 PJME 地区的估计能耗(MW)
预计 2017 年 PJME 地区的能源消耗(MW)
2017 年 7 月至 2017 年 9 月 PJME 地区的估计能耗(MW)
下一步是生成要素列,将单变量数据集转换为多变量数据集。如果你愿意,我们将把这个时间序列转换成一个监督学习问题。在某些数据集中,每小时的温度、湿度或降雨量等要素都很容易获得。然而,在我们的数据集中,没有额外的信息可以帮助我们预测给定的能耗。因此,创建这样的预测器,即特征列,就落到了我们的肩上。
我将向您展示两种生成要素的流行方法:将滞后观测值作为要素传递,以及根据日期时间索引创建日期时间要素。这两种方法各有优缺点,根据手头的任务,每种方法可能更有用。
使用滞后观测值作为特征
让我们从使用时间步长作为特征开始。换句话说,我们试图从先前的 n 个观察值 *Xt,X+1,…,*和 X(t+n-1)中预测下一个值 X(t+n),。然后,我们需要做的只是用前面的观察创建 n 列。幸运的是,Pandas 提供了方法 shift() 来移动列中的值。因此,我们可以编写一个 for 循环,通过将一列中的值移动 n 次并删除前 n 列来创建这样的滞后观察值。滞后是一个简单但很好的起点,尤其是如果您在开始时没有很多功能可以使用的话。
在将输入特征(即滞后观测值)的数量设置为 100 后,我们得到以下具有 101 列的数据帧,一列用于实际值,其余用于每行的前 100 个观测值。
从时间戳生成要素
尽管它的名字,特征工程通常是艺术多于科学。尽管如此,一些经验法则可以指导数据科学家之类的人。在这一节中,我的目标不是在这里介绍所有这些实践,而是演示其中的一些实践,让您自己进行试验。实际上,特征工程在很大程度上依赖于您正在工作的领域,可能需要为手头的任务创建一组不同的特征。
有了单变量时间序列数据集,生成日期和时间特征似乎是合乎逻辑的。由于我们已经将其索引转换为 Pandas 的 DatetimeIndex 类型,即一系列 DateTime 对象,因此我们可以轻松地从索引值创建新的特性,如一天中的小时、一个月中的天、一个月、一周中的天以及一年中的星期,如下所示。
尽管将日期和时间功能无任何接触地传递给模型在实践中可能行得通,但是模型很难了解这些功能之间的相互依赖关系。对我们人类来说,很容易看到小时、天、周和月遵循某种循环模式。虽然说 12 月之后是 1 月对我们来说是微不足道的,但理解一年中第一个月的算法出现在 12 月之后可能并不明显。就此而言,人们可以很容易地举出更多的例子。这使得良好的特征工程对于构建深度学习模型至关重要,对于传统的机器学习模型更是如此。
一键编码
对日期时间特性进行编码的一种方法是将它们视为分类变量,并为每个唯一值添加一个新的二进制变量,这就是众所周知的一次性编码。假设您对 month 列应用了 one-hot 编码,范围从 1 到 12。在这种情况下,创建了 12 个新的月份列,比如[Jan,Feb,… Dec],其中只有一个列的值为 1,而其余的都为零。例如,二月的一些日期时间值应该将这些编码列的第二列设为 1,如 [0,1,… 0] 。
使用 Pandas 的 get_dummies 方法,我们可以从给定的数据集中快速创建一个热编码列。
或者,您可能希望使用 Scikit-learn 的 OneHotEncoder 通过 ColumnTransformer 类对数据帧中的列进行编码。与 Pandas 方式不同,ColumnTranformer 在被调用以适应数据帧时输出一个 Numpy 数组。
尽管对分类特征编码非常有用,但一键编码并不能完全捕获日期时间特征中的循环模式。如果您愿意,它只是创建分类桶,并让模型从这些看似独立的特征中学习。例如,类似地对星期几进行编码,会丢失星期一比星期三更接近星期二的信息。
对于一些用例来说,这可能并不太重要。事实上,通过足够的数据、训练时间和模型复杂性,模型可以独立地学习这些特征之间的关系。但是还有另一种方法。
生成循环时间特征
与我们迄今为止处理的所有数据一样,一些数据具有内在的周期性。无论是几小时、几天、几周还是几个月,它们都遵循着周期性的循环。同样,这对我们来说是微不足道的,但对机器学习模型来说就不那么重要了。我们如何告诉算法 23 小时和 0 小时像 1 小时和 2 小时一样接近?
要点是创建两个新的循环特性,计算给定日期时间特性(比如一天中的某个小时)的正弦和余弦变换。模型不再使用小时的原始值,而是使用正弦变换,保持其周期性。要了解它是如何工作的以及为什么工作,请随意参考皮埃尔-路易或大卫的博客文章,这篇文章更详细地解释了这个概念。
一个很好的思考练习可能是思考如何在基于决策树的模型(随机森林、梯度增强树和 XGBoost)中将一个时间要素分成两个时间要素。这些特征一次根据一个特征形成它们的分裂,这意味着它将不能同时使用两次特征,例如正弦和余弦变换。通常,这些模型足够健壮来处理这样的分裂,但是它确实值得思考。
其他功能呢?
考虑到我们现在正在处理能源消耗数据,有人可能会问,一年中的假期是否会影响能源消耗模式。的确,很有可能。对于这样的二进制变量,即 0 或 1,我们可以生成具有二进制值的额外列来表示给定日期是否实际上是假日。至少可以说,记住所有的假日或者手动定义它们是一项单调乏味的任务。幸运的是,一个名为假期的套餐实现了它的承诺。
在特征工程中,可能性似乎是无限的,当然也有实验和创造的空间。好消息是已经有相当多的软件包为我们完成了这项工作,比如用于历史天气数据的 meteostat 或者用于股票市场数据的 yfinance 。不好的一点是,在没有实际尝试的情况下,对于哪些附加特性可以提高模型性能,通常没有明确的答案。
仅仅是一个想法,人们也可以尝试包括天气数据,例如温度、湿度、降水、风、雨、雪等等,以了解天气如何影响给定小时、天、周和月的能量消耗。
将数据分为训练集、验证集和测试集
在创建了特征列(无论是延时观测还是日期/时间特征)之后,我们将数据集分成三个不同的数据集:训练集、验证集和测试集。因为我们处理的是与时间相关的数据,所以保持时间序列的完整性是至关重要的,如果你愿意的话,也可以说是不混乱的。通过将参数 shuffle 设置为 *false,*避免在分成组时进行洗牌,您可以很容易地做到这一点。
对于神经网络,缩放数据集中的值是一种强烈推荐的做法,对于其他机器学习技术也是如此。它通过使模型更容易更新权重来加速学习。您可以通过使用 Scikit-learn 的 Scaler、MinMaxScaler、RobustScaler、Standard Scaler 等工具轻松实现这一点。关于每个缩放器效果的更多信息,请参考官方文档。
如果您正在寻找一种快速切换缩放器的方法,这里有一个很酷的技巧。让自己适应切换器功能;我们以后可能还会用到它。
将数据集加载到数据加载器中
将数据标准化后,通常就万事大吉了。这次没那么快。在花了相当多的时间使用 PyTorch 并浏览了互联网上其他人的代码后,我注意到大多数人最终都在为小批量训练做矩阵运算,即使用 NumPy 将数据分割成更小的批量。你可能认为 NumPy 就是干这个的;我明白了。但是还有一种更优雅的 PyTorch 方式,在我看来,这种方式得到的关注肯定要少得多。
PyTorch 的 DataLoader 类,一个可在 Dataset 上迭代的 Python,加载数据并将它们分成批,供您进行小批训练。DataLoader 构造函数最重要的参数是 dataset,它指示要从中加载数据的 Dataset 对象。主要有两种类型的数据集:地图样式的数据集和可迭代样式的数据集。
我将在本教程中使用后者,但也可以在官方文档中查看它们。也可以根据自己的需求编写自己的数据集或数据加载器类,但这肯定超出了本文的范围,因为内置的构造函数已经足够了。但是这里有一个链接指向关于这个话题的官方教程。
现在,我将使用名为 TensorDataset 的类,一个包装张量的数据集类。由于 Scikit-learn 的 scalers 输出 NumPy 数组,所以我需要将它们转换成 Torch 张量,以便加载到 TensorDatasets 中。在为每个数据集创建张量数据集之后,我将使用它们来创建我的数据加载器。
您可能会注意到一个批量为 1 的额外数据加载器,并想知道我们到底为什么需要它。简而言之,它不是必须拥有的,而是必须拥有的。像小批量训练一样,您也可以进行小批量测试来评估模型的性能,由于张量运算,这可能会快得多。但是,这样做将会丢弃无法组成批处理的最后时间步骤,从而导致丢失这些数据点。这不太可能导致误差指标的显著变化,除非您的批量很大。
从美学的角度来看,如果有人对预测未来感兴趣,丢弃最后的时间步骤可能会导致从测试集到预测值的不连续。至于训练和验证数据加载器,这种影响是可以容忍的,因为批处理在训练中提供了显著的性能改进,并且这种减少的时间步骤不太明显。
建立循环神经网络(RNN)
如果我试图在这里用几句话解释他们如何工作的本质,我不认为我能公正地对待 rnn。幸运的是,对于那些正在寻找起点的人来说,有几篇关于这些网络的写得很好的文章,安德烈·卡帕西的循环神经网络的不合理的有效性,克里斯·奥拉的理解 LSTM 网络,以及迈克尔·皮的《LSTM 和 GRU 的图解指南:一步一步的解释是我想到的几篇。
如果你看过电影《记忆碎片》——顺便说一句,这绝对是一部很棒的电影——你可能已经知道在记忆丧失的情况下做预测有多难了。当你的记忆每隔几分钟就被重置时,你很容易就无法说出发生了什么,你要去哪里,或者为什么——更不用说追踪杀害你妻子的凶手了。这同样适用于处理序列数据,无论是单词还是零售数据。一般来说,拥有前面的数据点有助于你理解模式,建立一个完整的画面,并做出更好的预测。
然而,传统的神经网络做不到这一点,每次给它们一个任务,它们就从零开始,很像伦纳德,你看。RNN 解决了这个缺点。为了使总体简单化,他们通过将信息从网络的一个步骤循环到下一个步骤,允许信息在网络中持续存在。这使得它们成为解决涉及序列数据的各种问题的强有力候选,例如语音识别、语言翻译或时间序列预测,我们稍后将会看到。
香草 RNN
通过扩展 PyTorch 的 nn。模块,所有神经网络模块的基类,我们如下定义我们的 RNN 模块。我们的 RNN 模块将有一个或多个由全连接层连接的 RNN 层,以将 RNN 输出转换为所需的输出形状。我们还需要将正向传播函数定义为一个类方法,称为 forward() 。这个方法按顺序执行,传递输入和零初始化的隐藏状态。尽管如此,PyTorch 会自动创建并计算反向传播函数 backward() 。
不过,香草 RNN 有一个缺点。简单的 RNNs 可以将以前的信息连接到当前的信息,其中相关的过去信息和当前信息之间的时间间隔很小。随着这一差距的扩大,rnn 学习长期依赖性的能力越来越弱。这就是 LSTM 寻求帮助的地方。
长短期记忆(LSTM)
长短期记忆,简称 LSTM,是一种特殊类型的循环网络,能够学习长期依赖性,在各种各样的任务中往往比标准版本好得多。可以说是服用类固醇的 RNNs。
标准版本的主要区别在于,除了隐藏状态之外,LSTMs 还有单元状态,它就像一条传送带,将相关信息从前面的步骤传送到后面的步骤。在这个过程中,新信息通过输入和遗忘门被添加到细胞状态或从细胞状态中删除,这两个神经网络确定哪些信息是相关的。
从实现的角度来看,您真的不必为这些细节费心。您只需要在您的 forward() 方法中添加一个单元格状态。
门控循环单元(GRU)
门控循环单元(GRU)是一个稍微精简的变体,它提供了相当的性能和相当快的计算速度。与 LSTMs 一样,它们也能捕捉长期依赖关系,但它们是通过使用没有任何单元状态的复位和更新门来实现的。
更新门决定需要保留多少过去的信息,而重置门决定要忘记多少过去的信息。gru 比 LSTMs 执行更少的张量运算,通常速度更快,需要的内存更少。正如你在下面看到的,它的模型级几乎和 RNN 的一模一样。
类似于我们对定标器使用的技巧,我们也可以轻松地在我们刚刚创建的这些模型之间切换。
现在,似乎我们已经做好了训练 RNN 模特的一切准备。但是我们从哪里开始呢?
训练模型
让我们从创建用于训练模型的主要框架开始。可能有很多方法可以做到这一点,其中之一是使用一个帮助器,或者包装器,保存训练、验证和评估方法的类。首先,我们需要一个模型类、一个计算损失的损失函数和一个更新网络权重的优化器。
如果你熟悉神经网络,你已经知道训练它们是一个相当重复的过程,在向前推进和向后推进之间来回循环。我发现用一个抽象层次,一个训练步骤函数或包装器来组合这些重复的步骤是很有用的。
在定义了一个合适的训练步骤后,我们现在可以开始编写训练循环,在每个时期都会调用这个步骤函数。在训练的每个时期,有两个阶段:训练和验证。在每一个训练步骤之后,网络的权值被调整一点以最小化损失函数。然后,验证步骤将评估模型的当前状态,以查看在最近一次更新之后是否有任何改进。
我将使用小批量训练,这是一种每次只使用一部分数据的训练技术。给定足够大的批量,模型可以通过仅学习数据样本来更有效地学习和更新其权重。这通常需要将每个批次张量重新整形为正确的输入维度,以便网络可以将其用作输入。为了获得张量运算的计算优势,我之前定义了我们的 RNN 模型来处理 3D 输入张量,除非你还没有注意到。因此,您可以将每一批看作数据包,就像仓库中的盒子,具有批大小、序列长度和 input_dim 的维度。
每个阶段也有两个 for 循环,其中模型被逐批训练和验证。重要的是在训练期间激活*训练()模式,在验证期间激活评估()*模式。虽然 train() 模式允许更新网络的权重,但是 eval() 模式向模型发出信号,表示不需要计算梯度。因此,权重会根据操作进行更新或保持不变。
现在,我们终于可以训练我们的模型了。然而,如果不使用单独的测试集(即保留集)来评估这些模型,就不可能知道该模型与我们正在构建的其他模型相比表现如何。与 train() 方法中的验证循环非常相似,我们将定义一个测试方法来评估我们的模型,如下所示。
在训练期间,损失函数输出通常是模型是学习、过拟合还是欠拟合的良好指标。为此,我们将使用以下方法绘制简单的损失数字。
培养
到目前为止,我们已经准备好了数据集,定义了模型类和包装类。我们需要把它们放在一起。您可以在下面的代码片段中找到一些定义的超参数,我鼓励您随意使用它们。下面的代码将使用我们之前定义的模块构建一个 LSTM 模型。您还可以通过将函数 get_model 的输入从lstm快速更改为您选择的模型来构建 RNN 或 GRU 模型。事不宜迟,让我们开始训练我们的模型。
您可能还记得,我们用标准化的输入来训练我们的网络;因此,模型的所有预测也会被缩放。此外,在我们的评估方法中使用批处理后,我们所有的预测现在都是批处理的。为了计算误差指标并绘制这些预测,我们需要首先将这些多维张量简化为一维向量,即展平,然后应用 inverse_transform() 获得预测的真实值。
在拉平和缩小数值后,我们现在可以计算误差指标,如平均绝对误差(MAE)、均方误差(MSE)和均方根误差(RMSE)。
0.64 的 R2 分数……不算好,也不算糟糕。正如您在下一节中看到的,还有改进的空间,您可以通过设计更好的功能和尝试不同的超参数来实现。我把这个挑战留给你。
生成基线预测
拥有某种基线模型有助于我们比较我们的模型在预测中的实际表现。对于这个任务,我选择了好的老式线性回归,它足以生成一个合理的基线,但也足够简单,可以快速完成。
可视化预测
最后但并非最不重要的一点是,可视化结果有助于您更好地了解模型的性能,并添加可能会改进模型的功能。我将再次使用 Plotly,但请随意使用您更喜欢的软件包。
最后的话
我想说这就是全部,但肯定还会有更多。深度学习是机器学习中最有成果的研究领域之一。对顺序深度学习模型的研究正在增长,并且在未来可能会保持增长。你可以把这篇文章看作是探索这些技术为时间序列预测提供什么的第一步。对于那些喜欢这篇文章并对使用这些算法预测未来价值感兴趣的人,我最近发表了另一篇关于这个主题的文章。
这里有一个链接到这篇文章的 Google Colab 笔记本,如果你想看看完整的笔记本并玩玩它。如果有不合理的地方或者你不同意,请联系我或者在评论中告诉我。这也适用于你可能有的各种反馈。
我还想写几个主题,比如使用时滞和日期时间特征预测未来的时间步长,正则化技术,其中一些我们已经在本文中使用过,以及更高级的时间序列深度学习架构。这样的例子不胜枚举。让我们希望我的动机不辜负这样的雄心壮志。
但是,现在,这是一个总结。
机器学习沙堡
思考为什么我们需要一个主导的设计,而不是另一个建立机器学习平台的创业公司
在大多数组织机器学习(ML)代表了任务和工具的不同组合,数据工程师负责数据管道,数据科学家负责数据分析、模型训练、验证和测试,硬件工程师负责计算配置,软件工程师负责部署。任务和工具如此分离导致了机器学习项目的高总体失败率,并限制了 ML 项目吞吐量的质量和数量。
这导致了 ML 平台的发展。当我提到 ML 平台时,我指的是用于数据和模型开发、跨多台机器扩展实验、跟踪和版本控制模型、部署模型以及监控性能的一体化产品。
这些一体化平台的前景仍然不发达,虽然这可能听起来对那些有工程和创业愿望的人有吸引力,但需要谨慎。
从数据科学的角度看流程
数据挖掘的跨行业标准流程 CRISP-DM 是开放标准流程模型的一个例子,它描述了许多常见的数据科学任务,这些任务如今通常构成 ML 生命周期的一部分。
CRISP-DM 参考模型的一般任务(粗体)和输出(斜体)。来源
现在,拥有 ML 能力的组织让一个人拥有整个生命周期变得越来越普遍。除了其他事情之外,这个人监督人员方面,建立和管理一个有凝聚力的团队,以及过程方面,确保整个生命周期中任务的集成和执行。
不同的音调象征着个人或团队可能执行的不同任务。a .将不同类型的任务分开,专家可以处理比一个人更多的情况;数字相加。显示的是两个专家可以做一个专家两倍的工作。例如,如果每个人可以做 10,000 个任务,他们一起可以做 20,000 个。b .团队可以处理更多不同的情况,因为数量会成倍增加。对于两人团队来说,这将是 10,000 x 10,000 = 100,000,000。来源
备注: ML 工程最佳实践
把 ML 当成工程问题是个问题
虽然将 ML 作为一个工程问题来对待可能很诱人,但是那些确实忽视了对过程至关重要并且不是工程师的广大用户。例如,主题专家经常提供从数据标记和特征工程到模型评估的各种输入。主题专家有必要有效地参与到这个过程中,同时记住许多人并不精通编程。对于这些用户来说,设想一个低代码环境是合理的。为了支持这些低代码用户,需要有抽象层,每一层都定义良好。对于大多数主题专家来说,最高级别的抽象——用户界面(UI)驱动的 ML——还没有实现,但是暂时想象一下,我们在市场上出现了许多为这些用户提供 UI 驱动能力的 ML 平台。毫无疑问,每个平台的运作方式都不同。几乎每个人都知道如何操作微软 Office,因为它是主导设计——在市场上占主导地位的设计,远远超过类似的产品。以微软 Office 为例,无论你是在家、在办公室还是在学术机构,都是同样的设计。没有主导的设计,就没有跨组织、领域和工具的一致和简化的用户体验。
快速迭代,其中 ML 代码只是一小部分
像通用软件系统这样的 ML 平台需要是可靠的、可扩展的和可维护的,此外还要具有适应性,因为它们是从数据中学习的系统——这些数据可能会频繁地改变,从而需要快速的开发和部署周期。此外,目前 ML 通常是一个固有的经验过程,其成功部分与实验产量成比例,需要快速迭代。然而,与传统系统不同,在传统系统中编程是最难的部分,在 ML 中编程只是全部工作的一小部分,并且处理从所有不同组件引入的技术债务是对吞吐量的重大限制。
机器学习系统中隐藏的技术债务。来源
备注:
深度学习系统中真实故障的分类
框架和 API
部署在野外的 ML 可以类似于模型和算法的九头蛇。部署可以包括主成分分析、梯度增强和神经网络的集合。就 ML 平台而言,支持框架和库的决定受到深度学习的严重影响,尽管仅支持深度学习是完全不够的。
支持主要的深度学习框架和库,如 PyTorch 和 TensorFlow ,将 API 的带入画面。考虑到没有总体标准或主导设计的恶劣环境,有太多开发和维护成本高昂的 API。API 需要开源,以确保架构保持开放,组织不会被迫使用专有技术栈。此外,需要开发和访问作为单一接口的数据编排规则和 API,以支持跨分布式环境的部署 ML(参见 ModelOps)。这表明需要一个更加通用的平台,减少多样化。
重新思考建筑
我们需要重新思考支持 ML 的生态系统的设计,特别是在数据方面——这就是现在所说的“以数据为中心”的 ML 方法。
是追求面向数据的架构(DOA)还是微服务架构是需要在这方面进行的重要讨论。DOA 方法本质上是一种基于流的体系结构,它使业务逻辑元素之间的数据流动更加明确和可访问,从而简化了数据发现、收集和标记等任务。然而,微服务架构由于其可扩展性而非常普遍。
备注: 现代面向数据编程米兰:面向数据编程的一次进化面向数据架构
开源作为一种标准
虽然没有针对 ML 平台的主流设计,但是开源已经成为标准方法。这有很多原因,在初创公司的情况下,组织经常需要这样做,以便在初创公司失败的情况下,他们可以访问源代码。追求开源也意味着初创公司现在要与现有的开源工具竞争,必须确定一个可行的商业模式,其中包括专有和开源功能的某种混合。对于一家初创公司来说,这绝非易事。例如,如果你是一个像谷歌这样的大型组织,你可以从业务的盈利部分引导资金来支持 TensorFlow 上的开源工作,以创建一个具有网络效应良性循环的生态系统——使用该框架的人越多,了解它的人越多,反过来又会带来更多的用户——从而形成一个大型的可持续生态系统,其中有机会将谷歌云平台等专有工具和服务货币化。据传,谷歌的 TensorFlow 团队有近 1000 人。
就 ML 平台而言,我还没有看到很多机会 1)围绕特定工具建立一个大型开源用户社区,以及 2)从其他来源获得收入的机会,这些机会可以为建立这样一个社区提供长期资金。
关注一个狭窄的用例
许多初创公司现在正试图开发服务于特定领域的 ML 平台,这将初创公司局限于较小的市场和用户群。我的论点是,任何 ML 平台必须对用例不可知,因为核心底层技术不是特定于领域的。由于基础技术的应用将不会局限于单个领域,人们将会发现,建立一个持久的护城河来防止其他人(因为他们服务于更大的市场而拥有更大的基础)侵犯狭窄的用例的能力是不够的。
注释: 告别“发现药物的沃森”
关注工作流程中的一小步
一个 ML 工具(我称之为“工具”,因为它不是一个一体化的产品)只支持 ML 生命周期的一部分,比如模型训练和评估,不可避免地需要一个组织将多个工具缝合在一起。由于 ML 堆栈中的产品在不断发展,并且没有通用的接口行业标准,开发和维护跨 ML 工作流的必要集成的成本是不小的。除了所有令人头疼的集成问题之外,用户必须熟悉使用多种工具和用户界面所带来的问题也阻碍了它的采用。
对于一个大型技术组织来说,ML 是产品和服务的核心组成部分,在缺乏主导设计的情况下,将工具缝合在一起以创建 ML 平台是当前的方法。这些组织(与大多数不同)拥有必要的技能、专业知识、经验和资源来投入这项工作。这样的组织通常将重点放在互操作性上,以构建跨越整个工作流的集成解决方案,对于他们来说,平台只处理组织所支持的产品和服务的用例就足够了。
备注: 遇见米开朗基罗:优步的机器学习平台用 Twitter 上的工作流制作 MLTFX:一个基于 TensorFlow 的量产级机器学习平台引入 FBLearner Flow:脸书的 AI 骨干
紧密耦合的组件
ML 开发和部署环境在组织间是异构的,与上游和下游软件组件耦合过紧的 ML 平台将限制其可移植性。类似地,如果 ML 平台与特定的硬件加速器过于紧密地耦合,那么它本身就会受限于对该硬件的采用。在这种情况下,初创公司需要小心谨慎,以确保它不是在为其市场的未来押注于采用特定的硬件或软件。一个 ML 平台需要在尽可能多的环境中工作,并且在尽可能多的硬件配置上工作,这给我们带来了模型操作和领域特定的架构。
进入 ModelOps
为了避免与生产中隐藏的技术债务相关的问题的出现,模型操作( ModelOps )关注用于在现实世界生产中测试、部署、管理和监控 ML 模型的最佳实践和工具。ModelOps 尤其适用于管理跨组织运行的底层异构软件和基础设施堆栈环境中的模型演变和数据变更。
随着组织转向云,主要的云提供商现在正在寻求将 ModelOps 与组织基础设施的其余部分相集成,这推动了对跨各种 ML 工具和服务的开放集成的重新关注。然而,主要云提供商的这种方法说起来容易做起来难,因为大多数人已经花了几年时间围绕他们的产品和服务建立专有墙。在这些平台真正开放之前,这些产品是否会成为 ML 平台的主流设计是有疑问的。
输入特定领域的架构
特定领域架构(DSA)通常称为加速器,是一类为特定领域定制的处理器。这种以硬件为中心的方法是由性能和效率增益驱动的,因为它们是根据应用的需求定制的。DSA 的例子包括张量处理单元 (TPU 的),以及图形处理单元 (GPU 的)。DSA 使用领域特定语言(DSL)来利用内存访问、并行性并改善应用程序到领域特定处理器的映射。DSL 是一个挑战,虽然是为特定的架构设计的,但是软件需要可移植到不同的环境。
DSA 的硬件/软件协同设计的垂直集成也支持开放式体系结构,因为这增加了用户数量并提高了安全性。
像 Python 这样的编程语言在 ML 环境下会发生什么,这个问题仍然悬而未决。构建一门语言是一项堆积如山的工作,以 ML 的发展速度来看,这需要的时间太长了。看看现有的,最好的选择似乎是朱莉娅或斯威夫特。目前,Swift 在 ML 生态系统中的存在不多,主要用于 iOS 应用程序,但近年来,苹果和谷歌都在朝着类似的方向发展,谷歌方面有 S4TF —用于 TensorFlow 的 Swift。
注意: Graphcore 杨树大脑机器学习系统都陷入了墨守成规手电筒:快速灵活的机器学习在 C++中
不了解要求
ML 平台缺乏主导设计导致许多人对最终需求理解不足——ML 平台的构建者经常不知道他们不知道什么。这导致 1)组织严重低估了任务的范围和复杂性,以及 2)决策是在可疑的基础上做出的(并且是合理的)。初创公司认为,只要有几个工程师,他们就能让一些东西运转起来。行业中的非软件技术公司认为这可以归结为是构建还是购买。在外部资助机构的帮助和支持下,学术机构自欺欺人地认为开发一个人工智能平台是他们研究工作的一部分,是对资源的一种很好的利用。
除了无知、政治和封地建设,建造偏见的一个主要因素来自人们对自己作品的喜爱——尽管它可能很糟糕,但建造它的人赋予它更多的价值。现实是,大多数公司都严重缺乏资源来实现卓越的执行力,以对抗像 Palantir 、 C3.ai 、 Databricks 等已经有了几年的跑道并为此投入了大量资源的公司,这些公司可能无法成功成为盈利的独立企业。
自动化。来源
对于大多数组织来说,ML 的价值在于应用,而不在于构建和维护 ML 平台,这似乎是显而易见的,然而缺乏主导设计和对需求的理解导致许多人做出糟糕的购买决策。最终,这为创业公司提供了一个围绕特定组织需求提供专业服务的机会,然而,与那些构建工具和平台相比,这一领域的创业活动和牵引力要少得多。
结论
最终,市场将解决关于 ML 平台的任何主导设计的争论。这一过程的一部分将是金融状况的演变。处于 4000 年最低点的利率误导了资本,促进了投机,并延续了不可持续企业的非自然生命。当资本最终被重新定价时,ML 市场中许多大小厂商的消亡,以及行业最终成熟的顶点将导致整合的机会,并鼓励某种主导设计的发展。
在此期间,ML 平台的提供商和用户应该更战略性地考虑所提出的问题,以实现持久的价值创造解决方案。
附加资源: 即将到来的 ML 系统浪潮斯坦福 MLSys 研讨会系列全栈深度学习芯片虎眼
使用 spaCy 3.0 转换器构建情感分类器
使用 spaCy 3.0 构建文本分类模型
我使用 spaCy 已经有一段时间了,因为它在生产中易于使用,而且 API 简洁、用户友好。该库由 Matthew Honnibal 和 Ines Montani 开发,他们是 Explosion.ai 公司的创始人。他们在 2021 年 2 月 1 日发布了 spaCy 3.0 版本,并添加了最先进的基于变压器的管道。此外,3.0 版还提供了新的配置系统和培训工作流程。在本文中,我展示了使用 spaCy version 3.0 和 Transformer 模型,用很少的几行代码构建一个情感分类器是多么简单。
我从 Kaggle 获取了财经新闻数据集,构建了一个情感分类器,并使用了 Google Colab。
装置
*# Installing Spacy library*
!pip install spacy==3.1.1
!pip install spacy-transformers
接下来,下载预先训练好的 transformer 模型。这里,模型是使用 BERT-base 架构的 Roberta-base。更多详情,请查看此处。
*# Downloading the spaCy Transformer model "en_core_web_trf"*
!python -m spacy download en_core_web_trf
导入必要的库
*# Importing libraries*
import pandas as pd
from datetime import datetime
import spacy
import spacy_transformers
*# Storing docs in binary format*
from spacy.tokens import DocBin
读取数据集
*# Reading the dataset*
df = pd.read_csv("Data1.csv", encoding='latin-1')
df.head()
这个数据集是关于金融新闻及其相应的情绪。数据有三个标签,正面,负面,中性。让我们读一些随机文本
df[‘Text’][2]
上述文字被归类为“负面”。让我们读一篇正面的课文
df['Text'][3]
以上文字归类为“正面”。
检查数据集的形状
df.shape
该数据有 4846 个观察值。由于训练时间的原因,我在本文中选择了一个小数据集。
分割数据集
将数据集拆分为 80:20 比例的训练和测试
*#Splitting the dataset into train and test*train = df.sample(frac = 0.8, random_state = 25)
test = df.drop(train.index)
检查形状
*# Checking the shape*
print(train.shape, test.shape)
输出
将变压器模型加载到空间管道
**import** **spacy**
nlp=spacy.load("en_core_web_trf")
训练数据集
在 3.0 版中,我们需要创建二进制格式的训练数据集来训练模型。第一步是创建元组,元组是带有情感的文本对。为训练和测试数据集创建元组。
#Creating tuplestrain['tuples'] = train.apply(**lambda** row (row['Text'],row['Sentiment']), axis=1)train = train['tuples'].tolist()test['tuples'] = test.apply(**lambda** row: (row['Text'],row['Sentiment']), axis=1)
test = test['tuples'].tolist()train[0]
第二步是在 transformer 模型(en_core_web_trf)
的帮助下,使用名为nlp
的空间管道为训练和测试数据集中的每个元组创建一个空间文档。每个元组只不过是文本和它的情感。
# User function for converting the train and test dataset into spaCy document**def** document(data):
#Creating empty list called "text" text = []
**for** doc, label **in** nlp.pipe(data, as_tuples = **True**):
**if** (label=='positive'):
doc.cats['positive'] = 1
doc.cats['negative'] = 0
doc.cats['neutral'] = 0
**elif** (label=='negative'):
doc.cats['positive'] = 0
doc.cats['negative'] = 1
doc.cats['neutral'] = 0
**else**:
doc.cats['positive'] = 0
doc.cats['negative'] = 0
doc.cats['neutral'] = 1
#Adding the doc into the list 'text'
text.append(doc)
**return**(text)
创建了上面这个名为document
的用户自定义函数。在这里,处理文本并添加到空间文档,并为每个情感添加类别。如果文本具有正面情感,那么它被分配为‘1’,并且对于中性和负面都是相同的。然后每个doc
被添加到名为text
的列表中。最后的text
是文本的内部空间表示。
将训练和测试数据集转换为二进制格式
将train
数据集传递给document
函数,并保存为train_docs
对象。然后使用DocBin
spaCy 函数将文本转换成二进制对象,并将训练数据集二进制对象保存为train.spacy
*# Calculate the time for converting into binary document for train dataset*
start_time = datetime.now()
*#passing the train dataset into function 'document'*
train_docs = document(train)
*#Creating binary document using DocBin function in spaCy*
doc_bin = DocBin(docs = train_docs)
*#Saving the binary document as train.spacy*
doc_bin.to_disk("train.spacy")
end_time = datetime.now()
*#Printing the time duration for train dataset*
print('Duration: **{}**'.format(end_time - start_time))
在 Google Colab 中将训练数据集转换为二进制对象花费了将近 7 分钟。
我们必须对测试数据集做同样的事情。将test
数据集传递给document
函数,并将其保存为test_docs
对象。然后使用DocBin
spaCy 函数将文本转换成二进制对象,并将测试数据集二进制对象保存为test.spacy
*# Calculate the time for converting into binary document for test dataset*
start_time = datetime.now()
*#passing the test dataset into function 'document'*
test_docs = document(test)
doc_bin = DocBin(docs = test_docs)
doc_bin.to_disk("valid.spacy")
end_time = datetime.now()
*#Printing the time duration for test dataset*
print('Duration: **{}**'.format(end_time - start_time))
在 Google Colab 中将测试数据集转换成二进制对象需要 1.40 分钟。现在,我们已经按照所需的空间格式将数据输入到二进制对象中。
训练模型
下一步,训练模型。在 3.0 版本中,spaCy 提供了一个命令行界面来执行培训。为此,我们需要从这个站点下载基本配置文件。在下载之前,我们需要选择组件下的textcat
,因为这是一个分类问题。我选择了硬件GPU
,就像我使用 google colab 和选择accuracy
一样。
作者图片
我们可以在记事本中打开基本配置文件,并需要为配置文件的train = “train.spacy"
和dev = "test.spacy".
截图指定路径,如下所示
作者图片
下一步是使用基本配置,并使用下面的代码将其转换为我们分类的完整配置。以下代码可从 spaCy 网站获得。完整的配置文件将作为config.cfg
保存在您的路径文件夹中
*#Converting base configuration into full config file*
!python -m spacy init fill-config ./base_config.cfg ./config.cfg
配置文件具有用于训练分类的所有模型参数。我在本文中使用了默认参数并训练了分类。现在,我们需要使用下面的代码来训练我们的模型。同样,代码也可以在 spaCy 网站上找到。我通过添加--gpu-id 0
启用了 GPU,模型输出保存在一个名为output_updated.
的文件夹中
#Calculating the time for training the model
start_time = datetime.now()# To train the model. Enabled GPU and storing the model output in folder called output_updated
!python -m spacy train config.cfg --verbose --gpu-id 0 --output ./output_updated
end_time = datetime.now()#Printing the time taken for training the model
print('Duration: **{}**'.format(end_time - start_time))
输出
在 GPU 下的 Google Colab 中训练模型花了 37 分钟。上面的输出显示了每个训练步骤的损失和准确性。我可以看到这个模型的精确度是 0.85。训练后,模型被保存在output_updated/model-last.
文件夹中
测试模型
现在我们的模型准备好了。让我们用一些随机文本进行测试
text = “Australia’s largest airline temporarily lays off 2,500 employees”*# Loading the best model from output_updated folder*
nlp = spacy.load("output_updated/model-best")
demo = nlp(text)
print(demo.cats)
输出
负分很高,它被正确地归类为阴性。让我们试着用一些正面的文字
text1 = “Apple earnings: Huge iPhone 12 sales beat analyst expectations”
demo = nlp(text1)
print(demo.cats)
输出
是的,文本的情绪是积极的,因为积极的分数很高。型号分类正确。
本文的目的是演示如何使用 spaCy transformers 3.0 进行文本分类。我们可以更改配置文件中的参数来提高模型性能。
你可以在我的 GitHub repo 中找到完整的代码和数据。
你可能也喜欢我以前的文章spaCy 自然语言处理——步骤和例子
感谢阅读。
构建小型服务,部署在 Kubernetes 上,并与 API Gateway 集成
用 Python 和 Redis 抽象后端 API 认证
亚历杭德罗·埃斯卡米拉在 Unsplash 上的照片
最近,我正致力于后端系统与 API 网关的集成。后端系统有自己的 API,但没有身份验证。或者我应该说,它有认证,但只适用于单个用户。也就是说,所有客户端将使用相同的用户名和密码来获取后端系统提供的令牌,并使用该令牌向后端发送 API 请求。这当然不理想,所有的 API 用户都使用同一个帐户。应该考虑到后端系统根本没有认证。
为了应对这种情况,我们希望将后端与 API 网关集成在一起,并将身份验证卸载到 API 网关。特别是,所有 API 用户都将拥有由 API 网关提供的自己的帐户和 API 密钥。当他们向 API 网关发送 API 请求时,API 网关用这个帐户向后端系统发送请求。因此认证功能是在 API 网关上执行的。
我们需要解决两个主要问题。
第一个是**如何对 API 用户隐藏后端系统的令牌获取过程。**我们不希望 API 用户知道任何关于后端系统认证的事情。所有的认证(使用 API 密钥或 OAuth 或任何其他认证方法)都应该由 API 网关来处理。API 用户仅使用 API 网关进行身份验证。
第二个是我们如何存储后端返回的令牌,并让 API 网关在令牌过期之前为将来的请求使用相同的令牌。我们不希望 API 网关每次收到新请求时都从后端系统生成新令牌,因为这可能会增加服务器负载。
API 网关为所有客户端提供单一入口点。它有一个名为 Mashups 的功能,可以组织和编排多个 API,并将它们作为单个 API 公开。当客户端发送一个 API 请求时,API 网关首先从我们新构建的令牌服务中获取一个令牌,捕获返回的令牌,然后使用该令牌调用后端 API。下图说明了流程。
新 API 请求的基本流程(图片由作者提供)
服务工作流程
我们新建的令牌服务工作流程如下:
- 当一个新的请求到来时,API gateway 调用令牌服务
- 令牌服务检查 Redis 是否包含有效令牌
- 如果 Redis 有令牌值,它返回
- 如果没有,令牌服务将向后端系统发送令牌请求
- 后端系统返回一个新生成的带有到期日期的令牌
- 令牌服务设置一个密钥来保存具有相关超时值的令牌值
- 令牌服务返回令牌(从 Redis 或后端系统)
- API 网关将带有收到的令牌的原始请求发送到原始后端 API 端点
我们新建的令牌服务工作流程(图片由作者提供)
设置 Redis 和 RedisInsight
为了继续上面的工作流程,我们在 Kubernetes 上安装并设置了一个 Redis 数据库。为了监控 Redis,我们使用了 RedisInsight。在 Kubernetes 上设置 Redis 最简单的方法是通过 Bitnami 的图表:
helm repo add bitnami [https://charts.bitnami.com/bitnami](https://charts.bitnami.com/bitnami)
helm install my-release bitnami/redis
所有 pod 运行后,您可以通过集群中以下 DNS 名称上的端口 6379 访问 Redis:
my-release-redis-master.default.svc.cluster.local for read/write operations
my-release-redis-slave.default.svc.cluster.local for read-only operations
要访问 Kubernetes 集群外部的 Redis,需要将 helm chart 值中的主服务器的服务类型设置为 LoadBalancer:
master:
service:
type: LoadBalancer
通过kubectl get svc -n redis
,可以获得 Redis 的外部访问 IP。回应会是这样的:
# kubectl get svc -n redisNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
redis-headless ClusterIP None <none> 6379/TCP 187d
redis-master LoadBalancer 10.233.50.103 172.16.100.223 6379:31808/TCP 187d
redis-slave ClusterIP 10.233.23.109 <none> 6379/TCP 187d
https://redislabs.com/redis-enterprise/redis-insight/
要使用 GUI 监控和管理 Redis,可以在 macOS、Linux 或 Windows 上安装 RedisInsight。安装后可以通过 http://localhost:8001/ 访问 UI。连接到 Redis 实例后,您应该会看到以下页面:
RedisInsight 的 GUI 视图(图片由作者提供)
其中,您可以监控您的 Redis 状态,通过 CLI 通信 Redis,最重要的是,您可以在浏览器中检查您的令牌。
将 Python 客户端连接到 Redis
有一个很好的 Python 包叫做 redis-py。它提供了从 python 代码到 Redis 实例的接口。以下代码片段将我们的 Python 应用程序连接到 Redis:
import redis
import sys
import osdef redis_connect() -> redis.client.Redis:
REDIS_HOST = os.getenv("REDIS_HOST")
REDIS_PW = os.getenv("REDIS_PW") try:
client = redis.Redis(
host=REDIS_HOST,
port=6379,
password=REDIS_PW,
db=0,
socket_timeout=5,
)
ping = client.ping()
if ping is True:
return client
except redis.AuthenticationError:
print(“AuthenticationError”)
sys.exit(1)
注意,我通过 OS 环境提供了 Redis 的主机和密码,稍后当我们的服务部署在 Kubernetes 上时,将由 ConfigMap 提供。我把上面的代码片段放在了redis_client.py
中,这样我就可以在其他文件中导入redis_connect
函数。
从后端 API 获取令牌
下面的函数从后端系统获取令牌。
import requestsdef get_token_from_api() -> str:
"""Token from backend API.""" with requests.Session() as session:
base_url = os.getenv("BASE_URL")
api_key = os.getenv("API_KEY") url = f"{base_url}/token session.headers.update({"backend-APIKey": api_key}) response = session.get(url)
token = response.json()["token"] return token
base_url
是后端 API 的基本 URL,url
添加了获取令牌的端点。我们还需要在头中提供后端系统用来认证的 API 密钥。后端系统将返回一个有效的令牌。
如果你没有这样的后台系统,但你仍然想跟进,我建议你把网址改为http://www.httpbin.org/uuid。它将返回一个 UUID,你可以把它当作一个令牌。而且你还需要把对应的行改成response.json()[“uuid”]
才能得到结果。
从 Redis 数据库中检索和设置令牌
通过下面简单的逻辑函数,我们可以检查令牌是否存在,如果存在,就从 Redis 中检索令牌。如果它不存在,我们从前面的函数中获取令牌,并将令牌设置为 Redis,超时值略小于原始到期日期。超时后,令牌将在 Redis 中自动删除,这将触发我们的服务直接从后端系统获取令牌。
def return_token():
if client.exists("token"):
token = client.get("token")
else:
token = get_token_from_api()
result = client.setex(
"token",
timedelta(minutes=30),
value=token
)
print(result) return { "token": token }
作为 API 公开
我们的令牌服务的所有构件都已经准备好了,下一步是将它们公开为一个 API。我用 FastAPI 来构建 API。顾名思义,就是**快!就性能而言,还有快!就编码时间而言,和一样快!**在学习时间方面。
from fastapi import FastAPIapp = FastAPI()@app.get("/token")
def return_token():
…
正如我们在上面看到的,你只需要用 app.get (HTTP 方法)包装你的函数,你就已经有了一个可用的 API 端点。
把所有的放在一起
import requests
from datetime import timedelta
from fastapi import FastAPI
from redis_client import redis_connectclient = redis_connect()app = FastAPI()def get_token_from_api() -> str:
"""Token from backend API.""" with requests.Session() as session:
base_url = os.getenv("BASE_URL")
api_key = os.getenv("API_KEY") url = f"{base_url}/token session.headers.update({"backend-APIKey": api_key}) response = session.get(url)
token = response.json()["token"] return token @app.get("/token")
def return_token():
if client.exists("token"):
token = client.get("token")
else:
token = get_token_from_api()
result = client.setex(
"token",
timedelta(minutes=30),
value=token
)
print(result)return { "token": token }
我们完了!让我们在 localhost 中试试我们的应用程序吧!
FastAPI 的安装很容易,用pip install fastapi
安装 FastAPI 本身,然后用pip install uvicorn
安装 ASGI 服务器。之后,使用uvicorn main:app –reload
运行服务器
INFO: Uvicorn running on [http://127.0.0.1:8000](http://127.0.0.1:8000) (Press CTRL+C to quit)
INFO: Started reloader process [28720]
INFO: Started server process [28722]
INFO: Waiting for application startup.
INFO: Application startup complete.
我们去参观的时候 127.0.0.1:8000/token。嘿,我们拿到代币了!
服务传回令牌(作者图片)
转到 RedisInsight,在浏览器页面中,我们还应该看到您的令牌在那里。
令牌存储在 Redis(作者图片)中
好吧!我们的应用程序完成了。我们接下来要做的是在 Kubernetes 上部署服务。首先,我们需要建立自己的码头工人形象。在您的 docker 文件中,使用基本映像和要求
FROM tiangolo/uvicorn-gunicorn-fastapi:python3.7COPY ./app /appRUN pip install -r requirements.txt
目录结构应该如下所示:
.
├── Dockerfile
├── app
│ ├── main.py
│ ├── redis_client.py
│ └── requirements.txt
使用docker build –t tokenservice ./
建立自己的形象,然后推送到 Docker Hub 或者自己的私人注册处。如果您将您的图像推送到 Docker Hub,您应该使用docker login
登录您的 Docker Hub 帐户,然后使用docker tag tokenservice <yourdockerhubaccount>/<yourrepository>:tokenservice
标记您的图像。最后你用docker push <yourdockerhubaccount>/<yourrepository>:tokenservice
推送你的图片。
在 Kubernetes 部署
接下来,我们需要准备我们的 Kubernetes YAML 文件,以便将我们的服务部署到 Kubernetes。我们将为这个服务的环境变量部署一个部署、一个服务和一个配置映射。
配置图
将所有环境变量(Redis 主机和密码、后端 URL 和 API-Key)放在。env 文件,并使用以下命令创建一个配置映射:kubectl create configmap tokenservice-env-file --from-env-file=.env
部署
之后,我们使用以下 YAML 文件配置 pod 以使用配置映射:
apiVersion: apps/v1
kind: Deployment
metadata:
name: tokenservice
labels:
app: tokenservice
spec:
replicas: 3
selector:
matchLabels:
app: tokenservice
template:
metadata:
labels:
app: tokenservice
spec:
containers:
- name: tokenservice
image: <yourdockerhubaccount>/<yourrepository>:tokenservice
ports:
- containerPort: 80
envFrom:
- configMapRef:
name: tokenservice-env
您可以根据需要设置任意数量的副本。点击此链接了解更多信息:
https://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-configmap/
过一会儿,您会看到所有的 pod(在本例中是 3 个)都在运行。
令牌服务的 pod 正在运行!(图片由作者提供)
服务
下一步是使用 Kubernetes 服务公开我们的应用程序。我们将使用 LoadBalancer 公开我们的应用程序。记住将您的选择器与您在部署中指定的标签相匹配。
apiVersion: v1
kind: Service
metadata:
name: tokenservice
spec:
selector:
app: tokenservice
ports:
- protocol: TCP
port: 80
type: LoadBalancer
与 API 网关集成
根据您使用的 API 网关的不同,API 网关上的 API 创建可能会有所不同,但一般过程几乎是相同的。我们可以使用 swagger 文件导入我们的 API,或者从头开始创建它。
好消息是,FastAPI 已经为我们生成了 OpenAPI 文件。去http://127 . 0 . 0 . 1:8000/docs可以找到 openapi.json 链接。
你可以在 127.0.0.1:8000/docs 中查看你的 API 规范(图片由作者提供)
FastAPI 已经生成了 OpenAPI 规范。(图片由作者提供)
虽然它只是一个简单的 OpenAPI 文件,没有太多关于 API 的描述和信息,但它足以让我们将它导入 API 网关并公开我们的服务。在 webMethods API Gateway 中,我们可以通过导入 OpenAPI 文件来轻松创建我们的 API。
从文件导入 API(图片由作者提供)
我们只需要将端点 URI 更改为上一步中获得的负载平衡器 IP。
更改为将端点 URI 路由到我们服务的负载平衡器 IP(图片由作者提供)
然后,我们可以使用 Mashups 创建一个新的 API 来实现我们在开始时看到的流程。也就是先从令牌服务获取令牌,然后调用原来的 API。在下图中, getToken 流程从我们刚刚创建的 tokenservice API 获取令牌,然后将响应有效负载传递给下一步 callFlow ,这是原始的后端服务。
我们新 API 的混搭流(图片由作者提供)
正如您在上面的图片中看到的,我们将得到的令牌添加到第二个调用的头部,并透明地传递来自最终用户的所有有效负载。
最后,我们用 Python 和 Redis 实现了抽象后端 API 认证的服务。
结论
在本文中,我演示了构建一个与后端系统一起工作的小服务、将其部署在 Kubernetes 上,以及将该服务与 API 网关集成以便其他人可以轻松地向后端系统发送 HTTP 请求的设计流程。这个项目涉及广泛的技术堆栈,我真的很喜欢这个开发过程。希望我能与你分享更多的开发经验。
如果你想知道更多关于我们在 Kubernetes 上能做什么,请查看这篇文章。在那篇文章中,我展示了如何在本地 Kubernetes 集群上部署 Apache Airflow。