控制梯度下降
…就像驾驶一辆旧车
拉夫(拉维)凯登在 Unsplash 上的照片
与 Adam 优化算法相比,将要讨论的算法将允许您以大约 10 ⁰的因子常规地达到更低的成本函数值。
继续看下去,看看为什么像“开旧车”!
介绍
在科学和技术领域,有许多计算都需要最小化成本函数(CF)。人们使用 CFs 是因为它们易于构建,并且因为它们的最小值对应于他们寻求了解的内容。它们已经被用于机器学习(ML)、物理学和许多其他领域。事实证明,梯度下降(GD)算法对于寻找 CF 的最小值特别方便。回想一下,GD 通过以下方式对函数 f(θ) 进行运算:将其自变量从 θ_old 更新为 θ_new
其中 α 为学习率,梯度为 ∇f = ∂ f / ∂ θ 。重复这种更新,直到满足停止条件。如图所示,只需要计算梯度,并将 α 设置为一个适当的小数值。
然而,找到 α 的“最佳”值证明是一个问题。通常,人们会使用不同的 α 值进行几次试验优化,看看哪一个看起来会导致最佳行为,然后只选择那个值。然而,即使仅仅是在整个优化运行中使用单个值 α 的想法也是有缺陷的。我认为这相当于试图以单一速度在两个城市之间行驶。例如,在芝加哥和圣路易斯之间开车的最佳速度是每小时 10 英里、40 英里还是 70 英里?当然,对于旅行的不同部分,不同的速度是优选的。这与寻找整个优化运行的最佳 α 的情况类似。
本文的计划是这样的:首先,引入诊断 ρ ,它将洞察 α 是否过大或过小。接下来,将通过一个例子来演示,只是为了建立直觉。之后,给出了一些伪代码来展示如何计算 ρ 而实际上没有额外的成本。最后将展示如何在大家最喜欢的测试题上使用 ρ :对数字进行分类!使用它的算法(NeogradM)优于 Adam,CF 值低了许多数量级。
ρ诊断
我们用下图来解释一下 ρ
作者图片
蓝色 曲线为 CF,两个参数值( θ_old 和 θ_new )标有 红色 圆点。这些值对应的 CF 值为 f_old 和 f_new 。此外,定义 f_est 为基于 GD 算法的估计 CF 值,如下所示
其中 dθ = (θ_new -θ_old) 。注意,这真的只是一个线性方程,就像大家熟悉的“ y = mx + b ”,除了这里的 y = f_est,m = ∇f,x = dθ,b = f_old 。由于 dθ 从 0 到 (θ_new -θ_old) 变化,导致 f_est 的值范围在图中显示为一条 绿色 线。
然而,我们真正感兴趣的是那个图中的 橙色 线。它代表估计值 f_est 和正确值 f_new 之间的差距或“偏差”。在图中,这个偏差表示α是太大还是太小。但是,要让它真正有用,应该是无量纲的。在这里,这是通过将其除以来自 f_est 的垂直落差来实现的。这导致了 ρ 诊断测量:
这里使用绝对值,因为我们只关心大小。
注意,该度量相对于平移和缩放 f 是不变的(同时保持 dθ 不变),这正是所需要的。因此, ρ 作为 α 大小适当性的一种“通用度量”。
如何看待 ρ
你可能在想,我应该用什么值来表示 ρ ?当然,非常小的值将导致低效的更新,而非常大的值将导致相对不稳定和不受控制的更新。
但是,在直接回答 ρ 的一个“最佳值”之前,先考虑一下这个类比。想象一下,有人开着一辆旧车,试图尽可能快地行驶。他甚至不会看速度计;他只注意汽车何时开始高速晃动。这位司机的理由是,“如果车晃动得很小,我可以开得更快,但如果晃动得很大,我就应该减速”。GD 也是这种情况,只不过现在 ρ 是抖音 α 是油门。在这两种情况下,“震动”都是我们希望控制的变量的代理,无论是油门踏板还是学习速率 α 。
这位司机的理由是,“如果车晃动得很小,我可以开得更快,但如果晃动得很大,我就应该减速”。GD 也是这种情况,只不过现在 ρ 是抖音 α 是油门。
总结一下,控制算法变成:如果 ρ 太大,减小 α ,如果 ρ 太小,增大 α 。当然,理想的是将 ρ 保持在一个固定值。在下一节的示例中,将展示如何做到这一点。这被称为“常数 ρ ansatz”,达到这个值的一个 α 被称为理想学习率。
一个例子
证明 ρ 有用的一个很好的例子是一维四次 CF。我们需要计算 ρ 的变量是:
一个简单的计算揭示了
其中 q = αθ 。需要注意的最重要的特征是 ρ 现在取决于 θ 。这意味着单个 α 将根据在 θ=0 处接近最小值而产生不同的效果。更有趣的是,这个等式可以很容易地倒过来有利于 *α。*假设小 q,
虽然相当简单,但这其实相当了不起。我们现在有一个公式,在给定 ρ 的期望值的情况下,通过该公式来设置 α 。随着 GD 算法的每次迭代,随着 θ 的变化,该公式保证保持 ρ 的目标值。这被称为“理想 GD”算法,如下图所示;它显示了 Adam 和理想 GD 的 log f 和 logρ 与迭代。
作者图片
在图中,目标值是 ρ = 0.1。S o,在理想 GD 的每次迭代中,根据上述公式将 α 设置为 0.1/(6θ),。(两种算法的初始 θ 都是 -3 。)该图显示,仅经过 150 次迭代后,用理想 GD 达到的值比来自 Adam 的值小超过 10 倍。显然,运行时间越长,这个因素就变得越大。此外,它还显示,对于 Adam,它开始趋于平稳的阶段与它的 ρ 下降的阶段一致。在图中,这发生在第 50 次迭代附近。这个特征在亚当身上反复出现。由于 ρ 没有用 Adam 控制,所以是自由进化;在这种情况下,它变得非常小,导致低效的更新。本质上,亚当有内置的正则化,因为它的 ρ 自然变得非常小。这类似于提前停止,也是它趋于“停滞”的原因。此外,运行 Adam 也是一个挑战,因为必须进行大量的试运行来为 α 选择一个合适的值。关于理想的 GD 算法,主要问题是它使 CF 变得如此之小,以至于人们不得不担心机器精度误差,因为数字变得那么小。
本质上,Adam 有内置的正则化,因为它的 ρ 自然变得非常小。这类似于提前停止,也是它趋于“停滞”的原因。
高效计算 ρ
在上一节中,我们给出了一个简单成本函数的示例,并确定了 ρ 和 α 之间的精确关系。利用这一点,可以将 α 设置为 ρ 目标值的函数。在实际应用中,CF 变得极其复杂,并且这种关系的计算变得不可能。相反,测量值 ρ 将用于设置 α 。现在看来, ρ 的公式使得每次迭代需要两次 CF 计算。然而,每次迭代只需一次计算就可以轻松测量 ρ 。首先,让我们从 GD 的一些基本伪代码开始:
INIT: θ, num
FOR: i = 1 to num+1
f = f(θ)
g = ∇f(θ)
dθ = -αg
θ = θ + dθ
RETURN: θ
鉴于到目前为止的讨论,它应该是不言自明的。现在,这个版本最重要的是 FOR 循环中以下操作的顺序:(1)求值 f , (2)求值 g , (3)更新 θ 。
这个" f - g -update “序列现在被修改,因此在循环之前有一个初始的” evaluate f “,在循环内部的顺序是” g -update- f "。这种重写允许访问更新前后的值,并且不需要额外的计算。经过这些更改,伪代码显示为
INIT: θ_old, num
f_old = f(θ_old)
FOR: i = 1 to num
g = ∇f(θ_old)
dθ = -αg
θ_new = θ_old + dθ
f_new = f(θ_new)
f_est = f_old + g.dθ
ρ = get_rho( f_old, f_new, f_est )
f_old = f_new
θ_old = θ_new
RETURN: θ_new
其中 get_rho 是我们等式 ρ 的一个实现。最后,如果这种重写对你没有吸引力,还有其他的方法。例如,您可以在诸如“IF i > 1”的条件中评估 ρ ,以确保 f_old 和 f_new 都可用。
近似 α
这样一来,下一步就是理解如何使用原来的 ρ 公式来设置 α 。事实证明,它可以以近似的方式使用,同样没有显著的开销。记住 ρ 的定义,通过定义数量 A 和 B
这样做是为了抽出对 α 的前导顺序依赖,并使其显式化。因此, A 和 B 都是常量,以 α 为前导顺序。但是注意一般情况下 A 保留 α 依赖,而 B 没有 α 依赖。使用这些等式和 ρ 的定义,很容易得出 α 的表达式
注意,在更新之后,这个等式完全成立。但是 ρ 的值很可能会过大或过小。因此,作为第一近似值(参见 Neograd_v0,单位为秒)。6.3 此处)我们将通过替代 ρ 的目标值来获得新的 α 。只要这两个 ρ 值(目标值和当前值)相差不大,这种方法就能很好地工作。此外,它是近似值的原因是因为一般来说, A 有一些 α 依赖性。最后,请注意我们正在做的技巧是将 α 视为相关变量,将 ρ 视为独立变量,而实际情况正好相反。
让我们考虑一个例子来说明这一点。假设第 7 次迭代后 α=0.1 , B=5 , A=10 , ρ=0.2 。注意到这些值完全满足上述 α 的等式,因为它们必须满足(即 0.1=(5/10)0.2 )。现在,我们希望 ρ 值更接近于 0.1 的目标值,因此我们将 0.1 与相同的 A 和 B 一起代入等式,并将 α 的新值计算为 0.05 。这个新值将用于第 8 次迭代。当然, 0.05 的这个值可能并不理想,因为我们忽略了 A 中的 α 依赖性,但是只要 CF 变化不太快,它就能很好地工作。顺便提一下,您可能会注意到,由于这种技术涉及到在随后的迭代中α的近似值,所以“晚了一天,少了一美元”的说法浮现在脑海中。尽管如此,它仍然工作得很好。
由于这项技术涉及到后续迭代中 α 的近似值,所以“晚了一天,少了一美元”的说法浮现在脑海中。尽管如此,它仍然工作得很好。
下一级近似是,不要试图通过代入 ρ 目标值来获得新的 α ,而是使用与原始值相差不大的 ρ 值。这在我的论文里叫做 Neograd_v1。最后,我们可以把这个算法和现有的结合起来。我发现最有效的方法是将这些想法和动力结合起来;它叫 NeogradM。
这里有一个 NeogradM 应用于数字识别问题的例子(数据来自 Scikit-Learn )。CF 是在神经网络的输出和训练标签之间构造的交叉熵惩罚。从图中可以看出
作者图片
使用 NeogradM 得出的 CF 值比使用 Adam 得出的值大约小 10⁸倍。另外,如图 13 中的我的论文【1】所示, ρ 的值主要停留在目标附近;对于 Adam 来说, ρ 的值要低大约 100 倍。同样,Adam 的情况是 ρ 变得很小,CF 曲线有一个平台。
最终意见
本文的目的是向您介绍 Neograd 系列算法的全文中的主要概念。也许最重要的一点是 ρ 诊断指标易于实施,是学习率的有效代表。如果你继续在你自己的梯度下降程序中实现它,那就是成功了一半。然后,您可能会发现您当前的程序可能会运行得更快。在这一点上,也许你会愿意采取下一步,实施 NeogradM 示例代码在我的 Github 上。
除了这里已经回顾的,全文包括许多其他有用的结果,例如其他度量,GD 的另一个推导,等等。
希望你喜欢这篇文章!
参考文献
[1] M.F. Zimmer, Neograd:具有接近理想学习率的梯度下降(2020) ,arXiv 预印本,arXiv:2010:07873。(已提交出版)
Conv1D 和 Conv2D:你知道 Conv1D 是 Conv2D 的子类吗?
比较和评估 Conv1d 和 Conv2D
很可能,阅读本文的大多数人已经实现了一些基于 CNN 的神经网络,并想知道在进行时间序列分析时是使用 Conv1D 还是 Conv2D。在这篇文章中,我将解释和比较这两种卷积类型,并回答以下问题:为什么我说 Conv1D 是 Conv2D 的子类?
如果你不能轻易地回答这个问题,我想这篇文章会让你感兴趣的。一如既往,欢迎任何问题/评论。
几个月前,我被要求为一个基于时间序列的挑战创建一个神经网络。数据大概是这样的:
- 数据数量:500
- 长度:3200
- 频率:40
- 频道:1
基于时间序列的长度,我决定实现短时傅立叶变换进行分析。一个数据看起来类似于图 1 所示的曲线图。此外,输出具有如下所示的形状。
- 输入形状:(500,1,3200)
- 输出 STFT 形状:(500,20,160)
一个数据的 STFT
在与几个人交谈后,我意识到在日常生活中与图像打交道的人建议使用 Conv2D,在 Conv2D 中,数据被视为图像。另一方面,处理时间序列的人表示 Conv1D 是最佳解决方案,类似于多通道时间序列任务,其中每个通道对应一个频率。在 PyTorch 的符号中,神经网络的输入是:
- CNN: ( N =256, Cin =20,林 =160)
- 2D CNN: ( N =256, Cin =1,欣 =20,胜 =160)
因此,我们可以实现两种可能的 2 层神经网络来解决这个任务:
正如所观察到的,1D CNN 的参数数量是 8160,而 2D CNN 是 18816,这两种方法都是时间序列分析的有效方法。
然而,在写报告的时候,我想到了以下问题:有没有可能创建一个类似于 1D CNN 的 2D CNN?如果有,执行情况如何?
虽然这一开始听起来很奇怪,但这是可能的,而且为了理解这种说法,有必要了解卷积层是如何工作的。
使用参数内核大小、填充、步幅和膨胀来定义卷积层:
- 内核大小:指滤镜蒙版的形状。
- 填充:添加到图像中的像素数量。
- 步距:输入矩阵上移动的像素数。
- 膨胀:内核中值之间的间距。
每个卷积层的输出取决于这些参数,并使用以下 PyTorch 公式进行计算。
Conv1D
塑造 1D·conv。参考
Conv2D
塑造 2D·conv。参考
因此,即使首先要获得两种类型卷积的相同结果似乎很困难,但问题的答案最终还是要为参数设置正确的值。
让我们用上面提到的例子来研究这个问题。如果我定义一个具有 32 和 64 个过滤器的两层 1D CNN,参数和形状如下所示。
- 输入:( N =256,= 20,林 =160)
- Conv1d-1: kernel_size=3,padding=1,stride=1,dilation=0
- Conv1d-2: kernel_size=3,padding=1,stride=1,dilation=0
因此,我们的目标是找到 2D CNN 的参数,其输出结果类似于 1D CNN:
- 输出层 1: ( N =256,= 32,林 =160)**
- 输出第二层:( N =256,= 62,林 =160)
在 2D 有线电视新闻网,翻译为获得:
- 输入 2D: ( N =256, Cin =1,欣 =20,赢 =160)
- 输出层 1: ( N =256, Cin =32,欣 =1,赢 =160)
- 输出层 2: ( N =256, Cin =62,欣 =1,赢 =160)
总之,本文提出的问题的答案非常简单:
设置正确的参数,我们可以创建一个具有与 Conv1D 相同功能的 Conv2D。
特别是,要设置的主要参数是内核大小。对于这种特殊情况,Hin 的内核必须设置为 20,类似于 1D CNN 的频率或频道总数。此外,需要将该维度的填充固定为 0,因此我们得到维度 1 作为该层的输出。
实现如下:
Conv2D
总之,我们能肯定 1D CNN 是 2D CNN 的一个子类吗?正如我在这篇文章中试图解释的那样,我们总是可以创建一个 2D CNN,其功能与 1D CNN 相同,并设置正确的内核大小、填充和步幅。作为一个说明性的例子,我们有这个最后的 2D CNN,它的参数数量类似于上面实现的 1D CNN。
然而,这一结论并不意味着我们应该总是使用 2D CNN 而不是 1D CNN,因为后者在处理例如多通道时间序列时更容易实现。我只是试图表达 Conv1D 和 Conv2D 最终是相同的。
如果你喜欢这个帖子,请考虑 订阅 。你将获得我所有的内容+所有其他来自牛逼创作者的文章!
对话式人工智能聊天机器人,带有使用 Pytorch 的预训练变压器
了解如何使用 Huggingface Transformers 构建一个具有 DialoGPT 功能的对话聊天机器人
介绍
会话系统,或者说 对话系统,已经在现代【NLP】社区中获得了巨大的兴趣。看到机器人可以如此接近地模仿我们的思想、逻辑和情感,就像它们的语言所显示的那样,这真是令人兴奋。今天,我们知道在我们的智能手机中,数字助理就在我们的手掌中,如苹果 Siri 、谷歌助理和微软 Cortana 。它们都能够倾听用户的语言并做出反应,尽管并不完美。
在这篇文章中,我们将教你如何利用 DialoGPT 这样的预训练转换器来实现你自己的对话聊天机器人。我们将使用 Huggingface 提供的变形金刚库来构建这个系统。
所以事不宜迟,让我们开始吧!
教程概述
- 步骤 1:安装库
- 步骤 2:导入库
- 步骤 3:构建对话管道
- 步骤 4:添加开始对话
- 步骤 5:添加连续对话
步骤 1:安装库
我们正在使用的库是拥抱脸变形金刚。要安装它,您只需:
pip install transformers
确保有一个工作版本的 Pytorch 或者 Tensorflow ,这样变形金刚就可以使用其中一个作为后端。
步骤 2:导入库
安装了 Transformers 之后,现在是时候用 Python 脚本导入它了。我们没有使用整个 Transformers 库,而是引入了一个[pipeline](https://huggingface.co/transformers/main_classes/pipelines.html)
模块,它基本上提供了一个非常简单的 API 来完成各种 NLP 任务,而不需要理解复杂的代码。我们还导入了[Conversation](https://huggingface.co/transformers/main_classes/pipelines.html#transformers.Conversation)
对象,稍后我们将把它用于对话管道。
要导入它们,您可以:
from transformers import pipeline, Conversation
步骤 3:构建对话管道
导入相关组件后,现在我们可以通过以下方式开始构建对话系统的管道:
conversational_pipeline = pipeline(“conversational”)
这行代码将使用 DialoGPT 作为模型,一个在对话数据集上训练的 GPT2 模型来设置对话管道。
步骤 4:添加开始对话
现在,我们可以开始和机器人说话了!首先,让我们开始与机器人的两次对话,并询问它电影推荐以及它最喜欢的书是什么:
对话 1:我们今晚去看电影吧——有什么推荐吗?
对话 2:你最喜欢的书是什么?
conv1_start = “Let’s watch a movie tonight — any recommendations?”
conv2_start = “What’s your favorite book?”
然后,我们使用定义的变量构建两个对话:
conv1 = Conversation(conv1_start)
conv2 = Conversation(conv2_start)
之后,我们将两个对话都放入列表中,然后将其提供给对话管道:
conversational_pipeline([conv1, conv2])
最后,我们可以看到机器人返回的每个对话的结果:
【对话 id:a9ba e572-cc11–48 c8-b36f-d 88 BCD 49 b 8 c 0
用户> >今晚一起看电影吧——有什么推荐吗?
bot > > The Big Lebowski,
对话 id:e6ee2d 97–5ee 7–404 b-b2e 5–4dd 527 F9 e9 DD
用户> >你最喜欢的书是什么?
bot>T20【饥饿游戏】
步骤 5:添加连续对话
我们可以通过向之前的对话添加新的用户输入来继续与机器人的对话,然后再次处理对话管道。
想象一下,我们想通过在对话 1 中询问电影是关于什么的以及在对话 2 中询问书的类型来跟进机器人。
对话 1:是关于什么的?
对话 2:酷,这本书是什么类型的?
conv1_next = “What is it about?”
conv2_next = “Cool, what is the genre of the book?”
要在之前的对话中添加新的用户输入,我们可以先做:
conv1.add_user_input(conv1_next)
conv2.add_user_input(conv2_next)
之后,我们可以简单地重用之前的代码,用新的对话集更新对话管道:
conversational_pipeline([conv1, conv2])
最后,您应该看到机器人能够返回与主题相关的新响应:
【对话 id:a9ba e572-cc11–48 c8-b36f-d 88 BCD 49 b 8 c 0
用户> >今晚一起看电影吧——有什么推荐吗?
bot > >大李博斯基
用户> >它讲的是什么?这是一部喜剧,讲的是一个人在电影院找到了一份工作,并得到了一堆工作。、
对话 id:e6ee2d 97–5ee 7–404 b-b2e 5–4dd 527 f 9 e 9 DD
用户> >你最喜欢的书是什么?
bot > >饥饿游戏
用户> >爽,书的流派是什么?
bot > >我不确定,但我觉得是幻想。]
结论
这就是本文的全部内容!希望你学到了有用的东西。在这篇文章中,我们讨论了如何使用 Huggingface 提供的预训练模型实现自己的对话机器人。如果你正在寻找它,为了你的方便,我在下面附上了一个 Jupyter 版本的全部代码:
希望你喜欢这篇文章!如果你喜欢我的作品,请订阅电子邮件列表,这样你就可以在我创作新内容时收到更新!如果感兴趣,也可以随意浏览我的其他帖子:
参考
[1] 苹果 Siri 官网,苹果
[2] 谷歌助手官网,谷歌
[3] 微软 Cortana 官网,微软
[4] 变形金刚 Github ,拥抱脸
[5] 变形金刚官方文档,拥抱脸
[6] Pytorch 官网,脸书艾研究
[7] Tensorflow 官网,谷歌大脑
[8]张,,等.“对话概念:大规模生成性会话应答生成的预训练” arXiv 预印本 arXiv:1911.00536 (2019)。
[9]拉德福德、亚历克等“语言模型是无人监督的多任务学习者。” OpenAI 博客 1.8 (2019): 9。
对话式人工智能:2022 年的趋势和预测
在这篇文章中,我提出了 2022 年市场发展的 6 个趋势和预测。
由于当前疫情形势带来的新的个人和职业生活方式,数字化转型正在快速加速。对话助手是这种转变的一部分,它实现了支持和自助服务请求的自动化。
马修·施瓦茨在 Unsplash 上的照片
在健康状况的推动下,对业务流程自动化的需求持续增长,不幸的是,这种情况可能还要持续几个月甚至几年。2020 年是充满疑问的一年,2021 年是前前后后的过渡年。由于远程工作而导致的团队分散,给公司带来了数字化转型,以及随时随地访问其功能的问题。
支持功能被过度订阅,授权团队为自己做事的需求非常重要。根据您是客户还是解决方案提供商,这种方法被称为自助服务或左移,它包括让客户、供应商和员工能够全天候自己获得答案、解决问题或执行某些操作。
在自动化手段的类别中,我们发现由人工智能驱动的对话助手。这些允许自动处理几个用例,例如:回答重复出现或偶然出现的问题,执行或多或少的简单操作,帮助解决一般的计算机问题,在某些操作中帮助人类,等等。
预测 1:需求将继续增长
尽管全球形势严峻,公司(尤其是业务部门)仍需要找到节约资源和提供服务的方法,这推动了对业务流程自动化(BPA)的需求不断增长。一些支持解决方案从语言处理的角度来看是成熟的,但从使其工作所需的工具的角度来看也是成熟的。
这种成熟度增加了这些项目的成功率。如果这些优势不断得到其他自动化解决方案的补充,例如智能文档处理(IDP ),这些解决方案可以自动处理用户发送的文档(发票、订单、身份证、简历等),那么这些优势将更加有趣。),并且能够与用户就所收集的数据进行交互;以及机器人流程自动化(RPA)脚本,无需开发新的连接器即可访问系统。
2022 年,欧洲(不包括英国)的市场增长预计将是 2021 年的 1.5 至 2 倍。
预测 2:扩大规模
一些公司已经试验了对话辅助解决方案,特别是在消费者和专业服务部门(BFSI、电信运营商、某些公共管理部门),在这些部门部署了几个涵盖不同领域的对话实例。在零售/营销领域,通常有一个单独的实例负责回答网站上买家的问题。机器人通常通过基于文本的界面访问,很少通过语音访问。管理许多日常关系的组织当然最容易接受这些技术。
到 2022 年,随着更多具体案例的出现,使用量将会增加,因此,那些已经启动项目并对这些解决方案的优势和劣势有了良好认识的公司的部署将会成倍增加。优先领域:IT 支持(一般请求、问题解决、订购设备等)。)、HR(一般问题、招聘、新进人员等。)、销售(客户服务)、某些特定业务领域(对我的存款账户的操作、投诉等。).
尚未尝试这些技术的公司将不得不考虑解决方案的实施。市场上的大量解决方案不会让他们容易选择,尤其是因为第一个销售人员提出解决方案通常会有溢价,这可能会导致负面和令人失望的体验。
预测三:机器人大师的出现
随着实例的扩散和所涵盖的不同业务领域的增加,对不同机器人的可访问性将成为问题,并将需要在单个界面中对不同的入口点进行分组。这种通用助手,也称为 Master-Bot,将帮助用户解决需要访问不同业务领域的多个问题或任务。
主-Bot 关系和业务领域(作者的图表)
主机器人是各种服务的单一入口点。它是用户和业务领域之间交流的控制塔。该设备允许用户避免在不同的地方寻找所需的专家来回答他的问题。主机器人根据请求联系这些域。
市场上的几种解决方案已经实现了这种机制,并将在 2022 年首次使用。
预测 4:增加语音测试
普通大众对亚马逊 Echo 或谷歌 Home 这样的语音助手设备很熟悉。然而,这些设备不适合在专业环境中使用。尽管如此,语音在专业领域的用途是多种多样且相关的:作为标准客户电话支持(Callbot)的补充,或者在车间、实验室或车辆等场合作为免提助手(Voicebot)。
部署语音助手所需的技术比传统的“Chabot”更复杂,这使得这些解决方案更难掌握,项目也更难成功。
2022 年,公司将继续测试语音,以了解如何使用这一通道和相关技术(电话、语音识别、语音合成、语音对话管理……)。
预测五:互联助手的幻灭
对过去 4 年中在 Google 中(通过 Google Trend)对几个连接的发言者的名字所做的请求的分析显示,在美国的请求总体上减少了。
【2017 年以来美国联网说话人查询趋势(来源:谷歌趋势)
这些扬声器存在几个问题,这些问题减缓了它们的使用,例如对个人数据的使用缺乏信任,不透明或过于笼统的服务(除非你是音乐爱好者或想在亚马逊上订购产品),以及家庭中的家庭自动化设备水平低下。它们被视为圣诞礼物,而不是家中的重要设备。
这种情况预计将在 2022 年持续。
预测六:新人的没落
自动语言处理的进步和统计模型的可用性使得每个人都可以创建助手。然而,助手不仅仅是一个语言处理单元,还是:对话建模工具,引导工具,允许企业自己监督操作,用户和信息系统的连接器,对话历史,高级角色管理,行为分析单元,等等。市面上的解决方案大多背后都有几年的经验和开发,对新人造成了很高的准入门槛。他们需要有其他解决方案还没有的突破性创新,这将变得越来越难找到。
此外,项目的成功不仅基于技术,还基于对项目周期的掌握,这导致市场的高度专业化,需要在整个项目期间及之后提供支持。尤其是因为公司越来越关注这些解决方案的投资回报。公司在开始创建特定的解决方案之前必须三思。特别是因为一些免费的开源解决方案允许以较低的许可成本实现聊天机器人(但具有较高的项目阶段和维护成本)。
唯一的 FAQ 类型的解决方案将不会成功地在这个生态系统中生存,除非提供极低的价格和/或拥有庞大的基础,这将导致长期盈利能力的问题。
因此,到 2022 年,应该很少有新的进入者,最基本的解决方案将会消失。人工智能技术的当前状态不应该允许重大技术突破迅速出现(至少对于工业用途而言)。今年,我们肯定会看到新的整合,尤其是在解决方案提供商层面,以增加他们的服务组合。
在封闭域上使用微调过的多维语言模型还不允许有保证的结果,例如在支持上下文中可以预期的结果。基于知识图的向导可以很好地工作,但是问题在于填充和维护知识图,这限制了它们的部署和使用。
增刊
如果你想了解更多关于这些话题的内容,以下是我的一些文章:
</14-criteria-for-well-choosing-a-chatbots-solution-2e788aace3b8>
转换升力试验已经停止;过渡到地理实验
行业笔记
本文是与 Leo Ubbiali 共同撰写的,他是 Babylon Health 的营销分析师,专门研究计量经济学和因果推理在营销中的应用。
iOS 14.5 的影响
已经有很多文章讨论了 iOS 14.5 和 IDFA 弃用对效果营销的影响。这些文章讲述了苹果的变化如何将归因建模的负担转移到广告平台上,降低了广告定位的有效性,并导致 iOS 和 Android 设备之间的 CPM 定价发生变化。
然而,iOS 14.5 的一个很少受到关注的结果是,这些变化对转换升力测试产生了影响。Lift 测试曾经为脸书广告客户提供了增量测量的黄金标准,但现在它们已经被完全否决,没有替代方案。
让我们看看什么是转换升力试验,为什么他们被脸书否决,以及如何地质实验提供了一个前进的方向。
什么是转换提升测试?
转换提升试验(或简称为提升试验)在市场上相当于随机对照试验。
他们试图了解一个广告活动的影响,方法是将广告随机展示给一组用户,让另一组用户拿着它,并在预定的一段时间内寻找两组用户之间的行为差异。
电梯测试寻找行为的什么不同?这完全取决于进行电梯测试的广告商。一个典型的电子商务品牌可能希望了解他们的脸书广告是否推动了本来不会发生的销售,因此他们希望在提升测试中跟踪购买情况作为 KPI。
对照组和治疗组
如果看过该电子商务品牌广告的用户群(T8 治疗组)继续从该品牌购买的比率高于那些没看过该品牌广告的用户群(T10 对照组),那么这将表明该活动产生了增量销售。
作者图片
如果上述情况不成立,即治疗组不会比对照组更有可能购买该品牌的产品,那么可以说该活动没有增加销售额。在这种情况下,任何归因于该活动的销售(通过点击后或观看后归因)都可能是无论如何都会发生的销售。
多少增量?
仅仅因为一个活动产生了递增的结果并不意味着它是一个值得进行的活动。为了评估这一点,我们需要了解活动的每增量转换成本。
从上面的例子可以看出,电子商务品牌在他们的活动上花费了 10 万美元。他们测量了他们活动的治疗组的 7500 次销售(包括活动期间和活动后的一段固定时间),以及他们活动的对照组的 5000 次销售。
简单地说,这些销售数字之间的差异(2,500)是由活动推动的增量销售的估计数字。如果我们将活动成本除以增量销售的数量,我们将得到每增量购买成本的估计值(CPiP);$40.
作者图片
如果这个 CPiP 对品牌来说是可以忍受的(意味着它低于他们的 LTV,并且他们乐于以这个价格获得客户),那么这个活动就可以被认为是成功的。请注意,如果治疗组和对照组之间的销售差异较小,CPiP 将按比例增加,因此活动成功的可能性降低。
改装升力试验的死亡
能够进行转换提升测试的一个相当重要的部分是能够测量两组人的转换量;你的治疗组(看过你的广告)和你的对照组(没看过你的广告)。
IDFA 折旧,ATT,和 SKAD 网络本身并没有提供任何理由,为什么你仍然不能报告(或至少估计)你的治疗组的转换量。他们对广告互动和转换之间的时间延迟提出了一些限制,并要求您至少有一个小的观察期,在此期间转换回发可以慢慢进入。也就是说,没有根本原因阻止您测量治疗组转换量。
对于测量对照组转换体积,情况更复杂;也就是一群没看过某个品牌广告的用户的购买次数。由于各种原因——尤其是 SKAD Network 回发需要一个活动 ID 来确定转化的归属——不可能从 lift 测试的对照组中测量转化量。
有一个提升测试的处理组的转换体积,但没有它的控制组,是没有多大用处的。正如我们之前看到的,这些数字之间的差异才是真正令人感兴趣的,因此,仅仅拥有治疗组转化量甚至不如常规的互动后归因方法有用(因为它忽略了转化和广告互动之间的时间)。
脸书的回应
尽管脸书在今年早些时候宣布,在 iOS 14.5 推出后,广告商将无法创建新的提升测试,但他们几乎没有宣传这一事实。除了广告管理器中的实验页面中的一个小注释,脸书只对他们的电梯测试文档进行了微小的编辑,以警告广告商即将到来的变化。
脸书建议,广告客户用常规的 A/B 测试(无法衡量增量)或品牌提升测试(旨在衡量一项活动对品牌指标的影响,而不是转化率)来取代衡量策略中的提升测试。这相当于脸书放弃了转换提升测试,并迫使广告商寻找其他方法来衡量他们的广告活动的增量。
提示地质实验
随着转化率提升测试的取消,广告客户又回到了绘图板上,寻找一种新的方法来衡量他们广告活动的有效性。
虽然没有因果推断方法可以被认为是普遍的“下一个最佳选择”,但地理实验特别适合在线广告;它们具有很强的统计严密性,并且易于理解、设计和实现。
什么是地理实验?
地理实验是一种准实验方法,其中将非重叠的地理区域( geos )随机分配给对照组或治疗组。由于地理定位,广告只在治疗组的地理区域提供,而对照组地理区域的用户不会看到广告。
作为一个简单的例子,我们可以在美国进行一个州级地理实验,将每个州随机分配给对照组或治疗组。然后,我们会在组成治疗组的州投放广告,而在组成对照组的州暂停广告。
你如何实施地理实验?
假设你可以将广告定位到相关的位置级别(街区、城市、州等),那么建立地理实验就相当容易。)并在地理水平上测量转化率。所有主要的广告网络/跟踪平台都允许这些功能。
步骤 1:地理标识
第一步是决定运行测试的粒度级别…如果您感兴趣的市场是整个美国,那么将州作为您的地理位置可能是有意义的。如果你的市场是一个州,你可以使用 DMAs 或邮政编码将你感兴趣的区域划分成更小的区域。
为了确保测量的统计稳健性,建议不要选择太小的地理区域(即 z ip 代码),因为人们可能会跨越地理边界,转换量可能太低。
步骤 2:地理随机化和分配
一旦我们定义了我们的市场和它的划分,我们需要将每个地区随机分配给治疗组或对照组。
在理想情况下,所有地理位置在过去具有相似的表现,随机化是基本的,因为它确保了除了广告服务之外的两个组之间的可能差异最小化。这起到了防止偏见的护栏的作用。
想法是在运动开始前(*预试验期)治疗组和对照组尽可能相似,它们在试验期只有一个因素不同;*接触广告。
作者图片
然而,geo 中的差异可能存在,并损害测试的设计和准确性。出于这个原因,通常需要进行初步分析,以便我们可以找到哪些是最好的 geo,以纳入治疗组和对照组。
如何衡量一个地理实验
一旦我们建立了实验,我们可以使用计量经济学方法,如差异差异或综合控制来量化广告增量;如果不投放广告,有多少转化不会发生。
作者图片
这些方法背后的直觉很简单:
- 我们估计了试验期间治疗组的反事实:如果干预没有发生会发生什么(黄色虚线)
- 我们计算实际治疗效果(黄色实线)和反事实之间的差异
尽管这种方法得出的结果不会像转化率提升测试那样准确,但地理实验是一种面向未来的衡量广告效果的方法,因为它们不需要用户跟踪,并且可以应用于不同的渠道。这甚至包括线下渠道。
网上有大量的教程来分析准实验结果,技术演练不在本文的讨论范围之内。有了基本的编码技能,营销人员可以利用非常抽象的软件包,如 CausalImpact (由谷歌开发)或其增强版本 MarketMatching 。
最近脸书发布了 GeoLift,这是一个新的开源包,通过地理实验来测量升力。
展望未来
虽然地理实验需要更多的技术工作来设计、设置和运行,但它们为希望复制转换提升测试的营销人员提供了一条清晰的前进道路。虽然没有广告平台提供开箱即用的地理实验,但脸书过去曾在私人测试版中玩弄过这个想法,他们有可能在未来公开这种工具。
然而,在此之前,有必要熟悉一下作为测量增量工具的地质实验,尤其是在脸书等平台上不再提供的转换提升测试。
最初发表于【https://mackgrenfell.com】。
本文是与 Babylon Health 的营销分析师 Leo Ubbiali 合作撰写的,他专门研究计量经济学和因果推理在营销中的应用。
将 Tensorflow2 模型转换为 OpenVINO
装有无数芯片神经计算棒 2。图片作者。
在任何地方运行您的模型的简要指南
如今,机器学习的应用层出不穷。那里有已经训练好的模型和只需要拟合的数据。
当一个人处理机器学习应用程序时,通常很难决定哪个硬件训练模型并卸载推理。
英特尔推出了 openVINO,帮助将模型(主要与计算机视觉相关)部署到任何英特尔设备,无论它们是 CPU、GPU、FPGAs 还是带有无数芯片的神经计算棒。
https://software.intel.com/content/www/us/en/develop/tools/openvino-toolkit.html
这项技术的一个常见用途是使用 openVINO 进行快速推理(即从网络摄像头预测视频中的实时对象)。一些模型需要一个强大的硬件来训练,但是一个更小更便宜的硬件来执行推理就足够了。例如,你可以使用一个复杂的预训练模型,然后在 轻型硬件上运行它,就像一个 RaspberryPi 。
OpenVINO 是一个强大的工具,但它需要一些段落才能正常工作。我们将经历转换模型的过程,将它加载到推理引擎插件中并执行推理。
定制模型
英特尔框架有一个集合可以使用的预训练和优化模型。但是如果你想训练一个定制模型呢?
我们将考虑使用 Tensorflow 进行模型训练,即使 openVINO 支持许多其他框架。步骤大多相似。
我会用 Google Colab 来描述这一切,它应该很容易被复制。
在 Colab 上下载 OpenVINO
首先,我们需要下载 openVINO repository,安装 Tensorflow 的先决条件。
!git clone https://github.com/openvinotoolkit/openvino!cd openvino/model-optimizer/install_prerequisites/ && ./install_prerequisites.sh tf2
你习惯使用 keras 吗
在这一步你可以做任何你想做的事情。创建一个定制 keras 模型,带有定制层。编译它。在任何数据集上训练它等等。
当你对你的训练感到满意时,你可以准备将模型加载到推理机插件中。那么该模型可以用于任何类型设备上的推理。
从 h5 中保存模型
第一步是将模型保存到一个 .h5 文件中。使用 keras API 很容易做到这一点。
model = tf.keras.models.load_model(“/content/model.h5”)tf.saved_model.save(model,’model’)
转换模型
模型需要被转换成推理机可以加载到设备中进行推理的表示。这个步骤可以使用所谓的模型优化器来完成,如下所示。
模型优化器需要输入一些参数,比如 input_shape。
python3 openvino/model-optimizer/mo_tf.py --saved_model_dir model/ --input_shape=\[1,28,28\]
此时,您应该有两个文件 model.xml 和 model.bin ,它们包含对您的定制模型执行推理所需的所有信息。
例子
以下笔记本中提供了此转换步骤的示例:
https://colab.research.google.com/drive/1wiFilpyXv947kLOqFJh_oqEp2224y8IL?usp=sharing
执行模型
当转换后的模型准备好了,下一步就是使用它进行推理。这可以在任何支持的设备上完成。没有必要使用与您执行培训时相同的设备。
安装 openVINO 进行推理
为了运行推理,有必要安装 openVINO 框架。这可以通过 pip 轻松完成。
!pip install --upgrade pip
!pip install openvino
将模型加载到插件中
我们需要将模型加载到设备上进行推理。
推理引擎核心对象具有读取的能力。xml 和*。将*绑定到网络对象中。
这个网络可以加载到任何设备中,无论是 CPU、GPU 还是 MYRIAD。这是这个 API 的强大之处之一,它可以毫不费力地在任何设备上透明运行。
from openvino.inference_engine import IECore, IENetworkie = IECore()net = ie.read_network(model=model_xml, weights=model_bin)exec_net = ie.load_network(network=net, device_name="MYRIAD")
获取关于拓扑的信息
我们可以检查网络拓扑。这个 API 只支持单个输入,无论如何,如果你在一个视频上运行这个,你需要一次对一个帧进行推理,所以这不是一个大问题。
此外,我们可以检查输入或输出形状,这对于由其他人训练的模型特别有用。我们需要确保数据处于正确的状态。
assert len(net.input_info.keys()) == 1, "Sample supports only single input topologies"assert len(net.outputs) == 1, "Sample supports only single output topologies"input_blob = next(iter(net.input_info.keys())) out_blob = next(iter(net.outputs))net.batch_size = len([0])net.input_info[input_blob].input_data.shape
推理
最后,是进行推理的时候了。给定一个对象 X_test[0] 我们准备在网络上使用 infer 方法来推断它。
res = exec_net.infer(inputs={input_blob: X_test[0]})res[out_blob].reshape(1,10)
结论
我们训练了一个定制模型。
使用 openVINO 模型优化器,我们将转换成将模型加载到推理引擎模块所需的新表示。
当训练好的模型准备好使用推理机来推断输入数据时,它几乎是微不足道的。另外,这可以在任何设备上进行。
使用 Dask 将大型 JSON 转换为拼花地板
使用 Coiled 将 75GB 数据集上的 ETL 管道扩展到云
图片由阿德里安·匡威通过unsplash.com拍摄
TL;博士;医生
这篇文章演示了一个 75GB 数据集的 JSON 到 Parquet 的转换,它无需将数据集下载到本地机器上就可以运行。首先在本地迭代 Dask 来构建和测试你的管道,然后将相同的工作流程转移到云计算服务中,如 Coiled 和最小的代码变更。
免责声明:我在 Coiled 工作,是一名数据科学传道者实习生。 Coiled 由Dask的最初作者 Matthew Rocklin 创立,是一个面向分布式计算的开源 Python 库。
为什么要将 JSON 转换成 Parquet
从 web 上抓取的嵌套 JSON 格式的数据通常需要转换成表格格式,用于探索性数据分析(EDA)和/或机器学习(ML)。Parquet 文件格式是存储表格数据的最佳方法,允许像列修剪和谓词下推过滤这样的操作,这极大地提高了工作流的性能。
本文展示了一个 JSON 到 Parquet 的管道,用于 Github Archive 项目中的 75GB 数据集,使用 Dask 和 Coiled 将数据转换并存储到云对象存储中。该管道不需要将数据集本地存储在您的计算机上。
完成这篇文章后,你将能够:
- 首先在本地构建并测试您的 ETL 工作流,使用一个简单的测试文件,这个文件可以很容易地放入本地机器的内存中。
- 使用 Coiled 将相同的工作流扩展到云中,以处理整个数据集。
剧透——y你将在两种情况下运行完全相同的代码,只是改变了计算运行的位置。
你可以在本笔记本中找到完整的代码示例。要在本地运行笔记本,使用位于笔记本存储库中的 environment.yml 文件构建 conda 环境。
在本地构建您的管道
首先在本地构建管道是一个很好的实践。上面链接的笔记本一步一步地引导你完成这个过程。我们将在这里总结这些步骤。
我们将在 2015 年使用来自 Github 档案项目的数据。这个数据集记录了 Github 上的所有公共活动,在未压缩的情况下占用大约 75GB。
1.
首先从 Github 档案中提取一个文件。这代表 1 小时的数据,占用大约 5MB 的数据。这里不需要使用任何类型的并行或云计算,所以现在可以在本地迭代。
!wget [https://data.gharchive.org/2015-01-01-15.json.gz](https://data.gharchive.org/2015-01-01-15.json.gz)
只有在必要时才扩展到云,以避免不必要的成本和代码复杂性。
2.
太好了,你已经从源头提取了数据。现在,您可以将它转换成表格数据帧格式。数据中有几个不同的模式重叠,这意味着您不能简单地将其转换为 pandas 或 Dask 数据框架。相反,您可以过滤掉一个子集,比如 PushEvents,并使用它。
records.filter(lambda record: record["type"] == "PushEvent").take(1)
您可以应用 process 函数(在笔记本中定义)将嵌套的 JSON 数据展平成表格格式,现在每一行代表一个 Github 提交。
records.filter(lambda record: record["type"] == "PushEvent").take(1)flattened = records.filter(lambda record: record["type"] ==
"PushEvent").map(process).flatten()
然后使用.to_dataframe()
方法将这些数据转换成数据帧。
df = flattened.to_dataframe()
3.
现在,您已经准备好使用 Dask DataFrame .to_parquet()
方法将数据帧作为. parquet 文件写入本地目录。
df.to_parquet( "test.parq", engine="pyarrow", compression="snappy" )
利用卷绕式 Dask 集群进行横向扩展
伟大的工作建立和测试你的工作流程在本地!现在让我们构建一个工作流,该工作流将收集一整年的数据,对其进行处理,并将其保存到云对象存储中。
我们将首先在云中启动一个 Dask 集群,它可以在整个数据集上运行您的管道。要运行本节中的代码,您需要登录 Coiled Cloud 获得一个免费的 Coiled 帐户。你只需要提供你的 Github 凭证来创建一个帐户。
然后,您需要用正确的库创建一个软件环境,以便集群中的工作人员能够执行我们的计算。
import coiled # create Coiled software environment coiled.create_software_environment(
name="github-parquet",
conda=["dask", "pyarrow", "s3fs", "ujson", "requests", "lz4", "fastparquet"]
)
您还可以使用 Docker images、environment.yml (conda)或 requirements.txt (pip)文件创建 Coiled 软件环境。更多信息,请查看 盘绕文档 。
现在,让我们启动您的盘绕式集群,指定集群名称、它运行的软件环境以及 Dask 工作线程的数量。
# spin up a Coiled cluster
cluster = coiled.Cluster(
name="github-parquet",
software="coiled-examples/github-parquet",
n_workers=10
)
最后,让 Dask 在您的盘绕式集群上运行计算。
# connect Dask to your Coiled cluster
from dask.distributed import Client
client = Client(cluster) client
我们期待已久的时刻!您的集群已经启动并运行,这意味着您已经准备好运行您在整个数据集上构建的 JSON to Parquet 管道。
这需要对您的代码进行两处细微的更改:
- 下载Github 的所有存档文件,而不仅仅是一个测试文件
- 将 df.to_parquet()指向 s3 存储桶,而不是本地存储桶
请注意,下面的代码使用了一个文件名列表,其中包含 2015 年的所有文件和上面提到的流程函数。关于这两个物体的定义,请参考笔记本。
%%time
# read in json data
records = db.read_text(filenames).map(ujson.loads) # filter out PushEvents
push = records.filter(lambda record: record["type"] == "PushEvent") # process into tabular format, each row is a single commit
processed = push.map(process) # flatten and cast to dataframe
df = processed.flatten().to_dataframe() # write to parquet
df.to_parquet( 's3://coiled-datasets/etl/test.parq', engine='pyarrow', compression='snappy' )
CPU times: user 15.1 s, sys: 1.74 s, total: 16.8 s
Wall time: 19min 17s
太好了,这很有效。但是让我们看看是否可以加快一点速度…
让我们纵向扩展我们的集群以提升性能。我们将使用cluster.scale()
命令将集群中的工作线程数量增加一倍。我们还将包括一个对client.wait_for_workers()
的调用,它将阻塞活动,直到所有的工人都在线。这样,我们就可以确信我们在计算中已经竭尽全力了。
# double n_workers
cluster.scale(20) # this blocks activity until the specified number of workers have joined the cluster
client.wait_for_workers(20)
现在,让我们在扩展后的集群上重新运行相同的 ETL 管道。
%%time
# re-run etl pipeline
records = db.read_text(filenames).map(ujson.loads)
push = records.filter(lambda record: record["type"] == "PushEvent") processed = push.map(process)
df = processed.flatten().to_dataframe()
df.to_parquet( 's3://coiled-datasets/etl/test.parq', engine='pyarrow', compression='snappy' ) CPU times: user 11.4 s, sys: 1.1 s, total: 12.5 s
Wall time: 9min 53s
我们已经将运行时间减少了一半,干得好!想象一下,如果我们将 n_workers 增加 10 倍或 20 倍,会发生什么!
将大型 JSON 转换为拼花摘要
在这个笔记本中,我们将原始 JSON 数据转换成扁平的数据帧,并以高效的 Parquet 文件格式存储在云对象存储中。我们首先在本地的单个测试文件上执行这个工作流。然后,我们使用 Coiled 上的 Dask 集群将相同的工作流扩展到云上运行,以处理整个 75GB 的数据集。
主要要点:
- Coiled 允许您将常见的 ETL 工作流扩展到大于内存的数据集。
- 仅在需要时扩展到云。云计算带来了自己的一系列挑战和开销。因此,要战略性地决定是否以及何时导入 Coiled 和 spin up 集群。
- 纵向扩展您的集群以提高性能。通过将我们的集群从 10 个工人扩展到 20 个工人,我们将 ETL 功能的运行时间减少了一半。
我希望这篇文章对你有帮助!在 Twitter 上关注我获取每日数据科学内容。
或者来我的博客打招呼:
https://crunchcrunchhuman.com/2021/12/22/kaggle-xgboost-distributed-cloud/
原载于 2021 年 9 月 15 日https://coiled . io。
使用 PyMuPDF 将 PDF 转换为 Python 中的图像
NLP 工具
一个从 pdf 生成 png 的简单实用的工具。
米切尔·伦辛克在 Unsplash 上拍摄的照片
最近,我在玩亚马逊 Textract 的时候遇到了一个小问题。Textract 的同步操作要求输入文档为图像格式。我有一堆(数百个)pdf 文件要处理;我该如何毫不费力地将它们转换成 png 呢?
PyMuPDF 来救援了!
让我们启动一个终端,键入以下内容:
pip install PyMuPDF
然后,让我们启动一个 Jupyter 笔记本,键入以下代码:
不要忘记更改第 8 行的源路径(pdf)和第 15 行的目标路径(pngs)。
就是这样!
谢谢你过来看我的帖子。希望 PyMuPDF 能像帮助我一样帮助你!在我随后的文章中,我会尝试将这些代码移植到 AWS 上…也许吧。
敬请期待!
如果你想了解更多关于我从懒鬼到数据科学家的旅程,请查看下面的文章:
如果你正在考虑改变方向,进入数据科学领域,现在就开始考虑重塑品牌:
参考
https://pymupdf.readthedocs.io/en/latest/faq.html
使用 Python 将照片转换为像素艺术
如何使用 python 从照片中生成像素艺术的分步教程
介绍
随着 NFTs 和加密艺术越来越受欢迎,对能够执行图像处理和生成程序艺术的程序员的需求也在增加。
像素艺术最近也重新流行起来,特别是在数字艺术鉴赏家中间。
在本文中,我将解释如何用 python 执行图像处理,将任何照片转换成像素艺术。
出于演示的目的,我将使用上述图像的裁剪版本(800px X 800px)。
开始编码吧!
所需的库
我们将使用 枕库进行图像操作,使用Matplotlib库进行图像显示和保存。
*#Import Libraries
from PIL import Image
import matplotlib.pyplot as plt*
读取图像
通过提供图像的路径作为枕库的图像模块的打开函数的参数,可以读取图像。因为我们的图像和笔记本在同一个文件夹中,所以只需要图像的名称就足够了。**
一旦图像被读取,就可以使用 matplotlib 的 imshow 函数显示。
*#Read image
img=Image.open('mario.jpg')#show image
plt.imshow(img)
plt.show()*
输入图像(裁剪后的图像 800 像素 X 800px 像素)
转换成小图像
创建像素化图像的第一步是使用 双线性插值 重采样技术将输入图像图像转换成小图像。
您可以访问下面的页面,了解更多关于不同的图像重采样技术。
*https://www.cambridgeincolour.com/tutorials/image-interpolation.htm
这可以通过提供大小和重采样技术作为参数,使用 resize 方法来实现。
我们可以将 800px X 800px 图像转换为 8px X 8px 图像,如下所示:
small_img=img.resize((8,8),Image.BILINEAR)
小图像(8px X 8px)
调整到所需的输出尺寸
然后,再次使用 resize 函数,将小图像调整到所需的输出大小。
我们将使用 最近邻 重采样技术,本质上是放大每个像素。
默认的重采样技术是双三次插值,这将锐化图像。下面的代码将把小图像(8px X 8px)转换成尺寸为 1000 px X 1000px 的图像。
#resize
o_size=(1000,1000) #output size
res=small_img.resize(o_size,Image.NEAREST)#save image
res.save('mario_8x8.png')#display image
plt.imshow(res)
plt.show()
mario_8x8.png,生成的输出图片(图片由作者提供)
请注意,我们已经使用保存功能将图像保存到本地,文件名为 mario_8x8.png.
上面的图像只包含原始图像的 0.01%的数据。因此,如果你不能从这个图像中识别出原始图像也就不足为奇了。
好的像素艺术必须包含足够的来自原始图像的数据,这样才能被识别。然而,在这个过程中,细微的特征将会丢失。
如果原始图形有许多需要保留的细微特征,我们应该使用大得多的尺寸进行初始调整。
合适的小图像尺寸可以通过试错法获得。
创建一个函数来生成像素艺术
下一步是通过创建一个从任何图像生成像素艺术的函数来简化上面讨论的处理。
该功能必须包含以下功能:
i .读取照片
ii。以小图像 尺寸和输出图像尺寸为参数
iii 转换为像素艺术。保存生成的图像
iv。并排显示原始图像和像素图片以进行比较
photo2pixelart 功能(作者要点)
该功能可用于为马里奥图像尝试不同的小图像尺寸,以找到合适的尺寸,从而获得视觉上最愉悦的像素艺术。
函数调用
我们将图像转换为 32px X 32px 大小,以像素化图像。
img.size 可以用来获取原始图像的大小。
通过提供 img.size 作为输出尺寸,我们可以生成与原始图像尺寸相同的像素艺术。
photo2pixelart(image='mario.jpg',i_size=(32,32),
o_size=img.size)
photo2pixelart 函数的输出(图片由作者提供)
我们可以看到,新的像素艺术显示更多的细节相比,最初的。与 8px 版本相比,该图包含的数据量是前者的 16 倍。然而,它只包含来自原始图像的 0.16%的数据。
我们将图像的长度和宽度缩小 10%,即下一张图像为 80px X 80px。这个数字将包含原始图像的 1%的数据。
#Function Call
photo2pixelart(image='mario.jpg',i_size=(80,80),
o_size=img.size)
photo2pixelart 函数的输出(图片由作者提供)
此图像比前一个图像更平滑,图像的特征更容易识别。
资源:
本教程的代码以及所有使用和生成的图像都可以在我的 GitHub Repo 中找到。
成为会员
我希望你喜欢这篇文章,我强烈推荐 注册中级会员 来阅读更多我写的文章或数以千计的其他作者写的各种主题的故事。
你的会员费直接支持我和你看的其他作家。你还可以在 Medium 上看到所有的故事。*
你可能会对作者的其他文章感兴趣
*
封面图片(作者图片)*
将图片转换为 ASCII 图片
关于图像如何工作以及如何将像素表示为 ASCII 字符的简要指南—包括代码示例
由 freestocks 在 Unsplash 发布的原始图片的 ASCII 版本
我相信你们很多人都听说过 ASCII art ,这是一种图形设计技术,使用可打印的 ASCII 字符集来组成图片。这种艺术最简单的形式是表情符号,如:-)
或:-3
。但是,您能用可打印的 ASCII 字符表示更复杂的图像吗?
图像到底是什么
首先,澄清图像在计算机系统中是如何表现的是很重要的。图片通常以.png
或.jpg
等格式存储在磁盘上。所有这些文件类型都有相似的结构:它们大致由一个头和一个数据段组成。前者存储关于图像的有用信息,例如其格式签名%20file%20formats.-,Magic%20number,-%5Bedit%5D),而后者存储实际的像素数据。
你看到的实际图像是由像素组成的,像素是我们都熟悉的光栅图像的最小可寻址元素。它们通常被表示为一组通道,也称为颜色。最常见的颜色值有经典的 RGB(红绿蓝)和 RGBA(红绿蓝 Alpha)。两者的区别在于,后者有一个额外的通道,称为“alpha”,用于指定图像的不透明度。RGBA 是我们将要使用的,因为它也可以用来表现一个空白的背景。
如何将像素转换成 ASCII 码
现在我们已经看到了图像是如何表示的,是时候讨论如何将像素转换成实际的 ASCII 字符了。为了理解这一点,我们必须首先看看像素颜色强度。该值是指所有像素通道的总和,除以通道可以具有的最大值的总和(在本例中为 255)。
从现在开始,我将使用 Python 作为代码示例,因为它简单易读,但是您可以随意使用您喜欢的任何技术。我还将使用
PIL
图像库来保持代码尽可能的简单和抽象。
第一行导入了清晰所需的静态类型。如果你不知道它们是什么,看看我关于类型提示的文章。
在这段代码中,我定义了一个新的Pixel
类型,一个由四个整数组成的Tuple
,每个整数代表一个 RGBA 像素中的一个通道。然后我定义了一个函数来提取给定像素的亮度。它首先将所有通道值相加,然后将结果除以像素通道可以达到的最大值,从而有效地获得强度百分比。
一旦我们计算了像素的亮度,就该把它映射到一个 ASCII 字符了。为此,我们必须定义一个用来表示像素的字符集。
字符集从最轻的空格到最重的 T3 排序。这意味着像素越密集,其对应的 ASCII 字符占用的空间就越大。
该函数将给定的像素亮度映射到集合中的一个字符。因为索引必须是整数,所以intensity * len(CHARACTERS)
的结果被四舍五入。
现在,让我们用一个简单的脚本将这些代码片段粘在一起:
查看 ASCII 图片
一旦我们获得了图像的 ASCII 字符串表示,我们必须找到一种图形化查看它的方法。最简单的方法是将它打印到控制台。由于图像通常是按像素行组织的,所以在打印时,我们也必须相应地使用换行符。
在这里,我编写了一个简单的函数,将 ASCII 图片打印到控制台,并说明了如何从 main 函数中调用它:
让我们看看这个脚本的运行情况!我们将转换一个像这样的简单图像:
假设我们已经调用了图像文件image.png
和脚本converter.py
,该命令将如下所示:
python converter.py image.png
# Or if you are on a Unix-like system and the script is executable
./converter.py image.png
这将向控制台生成以下输出:
Freepik 在https://www.flaticon.com/)发布的原始图片的 ASCII 版本
正如你所看到的,由于线条之间的间隔,图片有点失真。这是许多终端模拟器的局限性,但是有一个简单的解决方案。
使用 HTML 的视觉改进
我们已经看到了如何将图像转换成 ASCII 表示,但是如果能将结果保存到一个实际的文件中会更好。但是为什么是 HTML 呢?
- 所有的网络浏览器都支持它。
- 您可以根据图像的比例轻松放大和缩小。
- 它仍然可以只由 ASCII 字符组成。
让我们编写一个函数来将结果图像保存到 HTML 文件中:
现在我们已经完成了我们的图像转换器,让我们花点时间看看一些很酷的图片和它们的 ASCII 版本:
马修·施瓦茨在 Unsplash 上的照片
由马修·施瓦茨在 Unsplash 拍摄的原始图像的 ASCII 版本
Jacques Bopp 在 Unsplash 上拍摄的照片
由 Jacques Bopp 在 Unsplash 上制作的原始图像的 ASCII 版本
freestocks 在 Unsplash 上拍摄的照片
freestocks 在 Unsplash 上发布的原始图片的 ASCII 版本
这是我这篇文章所基于的项目的 GitHub 库的链接。我还会在下面的要点中直接包含源代码,以防您时间不够:
图像转换的完整脚本
如果有人感兴趣,我还建立了一个简单的免费网站,可以将你的图片转换成 ASCII 码。虽然我否认这不是一个严肃的项目,可能包含错误和漏洞,所以不要想依靠它做任何事情。
结论
在这篇文章中,你简要地学习了图像文件是如何构造的,最重要的是,如何将单个像素转换成相应的 ASCII 字符。
例如,这种 ASCII 艺术可以用作网页中的设计元素。ASCII 图像转换也可能是一些用于社交媒体的酷照片过滤器和视频效果的基础。此外,即使您不能利用这项技术,它仍然是一个很好的编码练习。
我希望您喜欢这篇文章,并在此过程中学到一些新东西,
感谢阅读!
将您的情绪识别笔记本转换为 API,无需额外代码
在 Jupyter 笔记本中训练情感识别,并使用 Cuttle 将其转换为 Flask API 项目,无需额外代码
来源: Unsplash
对于经常使用 Jupyter 笔记本进行开发和测试的人来说,您可能已经习惯了一直复制和粘贴代码。
删除测试代码片段,使用不同的 IDE 来编译和测试您的代码,以及开发 API 只是 ML 开发人员所习惯的一些耗时的任务。
如果我们能够自动化构建一个 API 项目所需的样板代码的苦差事,那不是很好吗?这里我们将在 Jupyter 笔记本中训练一个情绪识别模型,并将其转换为 Flask API 项目,完全不需要额外的代码!
属国
pip install
tensorflow==2.5.0 Keras==2.4.3 numpy==1.19.5 opencv-python==4.4.0.44
我们还需要安装卡特尔来将我们的 ML 笔记本转换成 Flask API 项目。
pip install cuttle
资料组
你可以在情感识别上使用任何数据集,也可以创建自己的数据集。在本文中,我们使用 Kaggle 上的情感识别数据集。你应该在这里找到它。该数据集包含 7 类图像:愤怒、厌恶、恐惧、快乐、中性、悲伤和惊讶。如果您想添加另一个类,只需创建一个新目录并添加属于该类的图像。
在与您下载的数据集相同的目录中创建笔记本,其中包含“train”和“validation”文件夹。
进口
数据集扩充
ImageDataGenerator 允许您在每个训练图像上使用图像增强、变换来拟合模型,还可以传递任何预处理函数(如果可用)。
我们重新缩放图像是因为颜色的范围是从[0–255],重新缩放图像会转换范围为[0–1]的每个像素值。这里,我们将模型的目标大小定义为 48x48,将 batch_size 定义为 64。
模特培训
我们在这里定义一个 CNN,但是你可以选择使用任何预先训练好的模型,比如 VGG16 或者 ResNet,或者也可以使用迁移学习。
完成训练后,您的模型会保存在给定的路径中。
情感识别
我们现在加载模型,给它一个图像并测试它。在这一步,让我们引入 cuttle,并将结果函数转换成任何前端应用程序都可以访问的 API。
让我们使用一个测试图像“test.jpeg”。
来源: Unsplash
我们从模型预测中得到的输出是**‘恐惧’**。继续用更多的图像进行测试,直到你对模型的准确性和结果满意为止。让我们看看如何将它转换成一个 API 项目。
初始化卡特尔
如下用“cuttle init”初始化 cuttle,并输入正在使用的笔记本的名称。
来源—作者
创建卡特尔环境
在这一步中,您命名环境,指定您正在使用的平台,以及您想要使用的转换器。我们想在这个场景中使用烧瓶变压器。您可以选择一个方便的环境名称。我用了“情感记录”作为例子。
来源—作者
此时,您的应该是这样的:
来源—作者
完成配置创建后,就该编辑 Jupyter 笔记本以包含 cuttle 配置了。我们只需要编辑代码的最后一部分来定义 API 路由并设置输出配置。
向单元格添加配置:
我们使用 Cuttle config 来执行两个主要操作,如这里的和所示。
- 禁用训练步骤并从保存的模型文件中加载,这样就不用每次运行脚本时都重新训练。
- 指定每次调用 API 时要执行的单元以及必需的参数
要禁用单元格,请在单元格的开头添加此配置。
#cuttle-environment-disable emotion-rec
现在添加 config 将您的脚本转换成 API,如下所示:
我们正在设置两个配置:切割环境设置配置和切割环境分配配置。它们分别是单元格范围和行范围。单元范围的配置设置了在转换单元期间所需的配置。行范围的配置允许我们配置变量。
在第一行中,我们设置了单元范围的配置,指定了我们选择的环境名称、方法和路由。我们还配置了变量“file ”,允许我们从请求方法和将作为我们响应的“output”变量中获取-config。
改变
我们的最后一步是使用我们一直使用的环境名进行 cuttle-transform。让我们来看看如何:
来源—作者
在这个步骤之后,您应该会看到一个输出目录,它由另一个包含环境名称的子目录组成。转到这个目录,找到您转换成 API 的代码。以下列身份运行该文件:
来源—作者
现在我们的代码默认运行在本地主机端口 5000 上。通过使用 Postman 发送图像来测试这一点,以获得您的响应。
test.jpeg 通过 POST 请求发送到我们的 flask API。来源—作者
您的 API 项目现在可以部署了!Cuttle 还允许您将笔记本转换为脚本或管道。
资源
卡特尔网站: cuttle.it
Github: 情感识别 API 源卡特尔源:卡特尔 CLI 源
你也可以在推特上找到我们@https://twitter.com/cuttlehq
将 conda/pip 环境转换为 Docker 图像
使用 Coiled 作为 Docker 图像管道
图片来自 Unsplash
TL;博士;医生
构建 Docker 映像是大多数数据科学家工作流程中必不可少但极具挑战性的一部分。简化这个过程的一个方法是将 Coiled 连接到您自己的 Docker 注册表(如 DockerHub ),创建一个管道,将您的 conda 或 pip 环境转换成可以在其他地方使用的 Docker 映像。这篇文章向你展示了如何使用coiled.create_software_environment()
命令并通过在你的 Coiled cloud 仪表板的“帐户”设置中设置容器注册后端来实现。
你也可以观看下面的视频,获得一步一步的指导:
免责声明:我在 Coiled 工作,是一名数据科学传道者实习生。 Coiled 由Dask的最初作者 Matthew Rocklin 创立,是一个面向分布式计算的开源 Python 库。
为什么您可能需要 Docker 图像
许多数据科学家需要将 conda 和/或 pip 环境转换为 Docker 映像,以便跨团队协作或将本地工作环境迁移到云上运行。大多数人使用越来越复杂的工作流手动实现这一点(例如,看看这篇中等的文章)。)虽然这在技术上可行,但并不是每个人都能使用。作为一名数据科学家新手,我知道我一开始发现这种体验很复杂,很难掌握。
如何盘绕可以让你更容易
作为为数据科学家设计的基于云的服务, Coiled 需要能够将 conda 和 pip 环境转换成 Docker 映像才能正确运行。[**coiled.create_software_environment()**](https://docs.coiled.io/user_guide/software_environment_creation.html)
命令将一个**environment.yml**
(conda)或 requirements.txt (pip)文件转换成 Docker 映像,然后分发给所有 Dask workers,以确保整个盘绕集群的一致性。
下面是两个代码片段:一个用于 conda,一个用于 pip:
# using conda .yml files
import coiled
coiled.create_software_environment(
name='my-env',
conda='<path/to/environment.yml>'
) # using pip .txt files
import coiledcoiled.create_software_environment(
name='my-env',
pip='<path/to/requirements.txt>'
)
本着社区和开源开发的精神,Coiled 工程师并没有把这个功能藏起来,而是把它变成了一个通用工具。这意味着您可以将 Coiled 连接到您自己的 Docker 注册中心(如 DockerHub)来创建 conda/pip-to-Docker 构建服务。然后,您可以在任何喜欢的地方使用使用 Coiled 创建的 Docker 图像。
如何将 Coiled 连接到 Docker 容器注册表
默认情况下,Coiled 将您创建的软件环境存储在您正在运行的云服务的容器注册表中:AWS、GCP 或 Azure。 您可以在 Coiled Cloud dashboard 的“Account”选项卡中更改此设置,将您的软件环境作为 Docker 映像保存到您的 Docker Hub。
任何支持 Docker 注册表 API V2 的注册表都应该可以工作。在我们文档的“后端”页面阅读更多细节。
请注意:使用 Docker Hub 以外的注册中心是一个正在积极开发中的实验性功能。如果您想在support @ Coiled . io或通过Coiled Community Slack channel讨论您的用例,请联系我们。
我在这里偷偷给你一个额外的提示:创建软件环境实际上并不需要你启动一个集群。这意味着你可以使用你的 conda/pip-to-Docker 管道,而不会烧掉你的任何。
什么都不发明的价值
这就是我喜欢 Python 生态系统的原因。产品不可或缺的功能可以开放给更广泛的公众使用。它遵循了 Matt Rocklin 经常宣称的关于开源库 Dask 的反直觉的最低创造力原则:“什么都不发明。”
这个咒语看起来似乎违反直觉,但实际上包含了一个简单的教训:不要试图重新发明轮子,而是创造与人们已经在使用的工具相集成的产品,从而为已经存在的丰富的生态系统增加额外的一块砖。
我希望这篇文章对你有帮助!在 Twitter 上关注我获取每日数据科学内容。
或者来我的博客打招呼:
https://crunchcrunchhuman.com/2021/12/22/kaggle-xgboost-distributed-cloud/
原载于 2021 年 8 月 23 日https://coiled . io。
在 Pandas 中将数据转换为数字类型的 10 个技巧
熊猫帮助你开始数据分析的提示和技巧
在进行数据分析时,确保正确的数据类型非常重要。否则,您可能会得到意外的结果或错误。在熊猫的例子中,它将在许多情况下正确地推断数据类型,并且您可以继续您的分析,而无需对该主题进行任何进一步的思考。
尽管 pandas 工作得很好,但在数据分析过程中的某个时候,您可能需要显式地将数据从一种类型转换为另一种类型。本文将讨论如何将数据转换为数值类型。更具体地说,您将学习如何使用 Pandas 内置方法astype()
和to_numeric()
来处理以下常见问题:
- 将 string/int 转换为 int/float
- 将 float 转换为 int
- 转换混合数据类型的列
- 处理缺失值
- 将货币列转换为浮动
- 将布尔值转换为 0/1
- 一次转换多个数据列
- 读取 CSV 文件时定义数据类型
- 创建自定义函数来转换数据类型
astype()
对to_numeric()
为了进行演示,我们创建了一个数据集,并用一个函数加载它:
import pandas as pd
import numpy as npdef load_df():
return pd.DataFrame({
'string_col': ['1','2','3','4'],
'int_col': [1,2,3,4],
'float_col': [1.1,1.2,1.3,4.7],
'mix_col': ['a', 2, 3, 4],
'missing_col': [1.0, 2, 3, np.nan],
'money_col': ['£1,000.00','£2,400.00','£2,400.00','£2,400.00'],
'boolean_col': [True, False, True, True],
'custom': ['Y', 'Y', 'N', 'N']
})df = load_df()
作者图片
请查看 Github repo 获取源代码。
检查数据类型
在我们深入研究更改数据类型之前,让我们快速了解一下如何检查数据类型。如果我们想在一个数据帧中看到所有的数据类型,我们可以使用dtypes
属性:
>>> **df.dtypes**string_col object
int_col int64
float_col float64
mix_col object
missing_col float64
money_col object
boolean_col bool
custom object
dtype: object
这个属性在 Series 中也是可用的,我们可以用它来检查特定列上的数据类型。例如,检查 int_col 的数据类型:
>>> df.**int_col.dtypes**dtype('int64')
如果我们想研究数据,那么info()
方法可能更有用,因为它提供了 RangeIndex、总列数、非空计数、dtypes 和内存使用情况。这是大量有价值的信息,可以帮助我们更全面地了解数据。
>>> **df.info()**RangeIndex: 4 entries, 0 to 3
Data columns (total 8 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 string_col 4 non-null object
1 int_col 4 non-null int64
2 float_col 4 non-null float64
3 mix_col 4 non-null object
4 missing_col 3 non-null float64
5 money_col 4 non-null object
6 boolean_col 4 non-null bool
7 custom 4 non-null object
dtypes: bool(1), float64(2), int64(1), object(4)
memory usage: 356.0+ bytes
1.将字符串转换为整型/浮点型
将 Pandas 列转换为不同类型的最简单方法是使用 Series 的方法astype()
。例如,要将字符串转换成整数,我们可以这样调用它:
# string to int
>>> df['string_col'] = df['string_col']**.astype('int')**
>>> df.dtypes**string_col int64**
int_col float64
float_col float64
mix_col object
missing_col float64
money_col object
boolean_col bool
custom object
dtype: object
我们可以看到它默认使用的是 64 位整数。在某些情况下,当处理大型数据集时,使用较短的整数会更节省内存。为此,你可以简单地调用astype('int8')
、astype('int16')
或astype('int32')
同样,如果我们想将数据类型转换为 float,我们可以调用astype('float')
。默认情况下,它使用的是 64 位浮点数。我们可以使用'float128'
来获得更高的精度,或者使用'float16'
来获得更高的内存效率。
# string to float
>>> df['string_col'] = df['string_col']**.astype('float')**
>>> df.dtypes**string_col float64**
int_col int64
float_col float64
mix_col object
missing_col float64
money_col object
boolean_col bool
custom object
dtype: object# For more precision
>>> df['string_col'] = df['string_col']**.astype('float128')**# For more memory efficiency
>>> df['string_col'] = df['string_col']**.astype('float16')** >>> df['string_col'] = df['string_col']**.astype('float32')**
2.将 float 转换为 int
如果我们想将一个浮点列转换成整数,我们可以尝试使用上面使用的astype()
。
df['float_col'] = df['float_col']**.astype('int')**
作者图片
然而,有一个小问题。通过显示 DataFrame,我们可以看到该列被转换为整数,但所有值都被向下舍入。这可能没问题,但在大多数情况下,我会认为这是不对的。如果我们想转换成整数,并按照我们期望的方式四舍五入,我们可以先做round()
。
df['float_col'] = df['float_col']**.round(0)**.astype('int')
作者图片
现在,数字4.7
被四舍五入为5
。
3.转换混合类型的列
让我们继续看一列混合的字符串和数字。当运行astype('int')
时,我们得到一个值错误。
# Getting ValueError
df['mix_col'] = df['mix_col'].astype('int')
作者图片
该错误表明值'a'
有问题,因为它不能转换为整数。为了解决这个问题,我们可以使用带参数errors='coerce'
的 Pandas to_numeric()
函数。
df['mix_col'] = pd.**to_numeric**(df['mix_col'], **errors='coerce'**)
但是当检查dtypes
时,你会发现它被转换为float64
。
>>> df['mix_col'].dtypesdtype('**float64**')
在某些情况下,你不希望输出浮点值,你希望它是整数,例如转换一个 ID 列。我们可以叫astype('Int64')
。注意它有一个大写字母I
并且不同于 Numpy 'int64'
。这样做的目的是将 Numpy 的NaN
改为 Pandas 的NA
,这样它就可以是一个整数。
>>> df['mix_col'] =
pd.to_numeric(df['mix_col'], errors='coerce').**astype('Int64')**>>> df['mix_col'].dtypesInt64Dtype()
或者,我们可以用另一个值替换 Numpy nan
(例如用0
替换NaN
)并调用astype('int')
df['mix_col'] = pd.to_numeric(
df['mix_col'],
errors='coerce'
).**fillna(0)**.**astype('int')**
4.处理缺失值
现在,我们应该完全具备处理缺失值的能力。在 Pandas 中,缺失值被赋予值NaN
,这是“不是一个数字”的缩写。由于技术原因,这些NaN
值总是属于float64
。
df.**missing_col.dtypes**dtype('**float64**')
当将带有缺失值的列转换为整数时,我们还会得到一个 ValueError ,因为NaN
不能转换为整数。
作者图片
为了避免这个错误,我们可以像上面那样调用astype('Int64')
(注意它是大写的I
,和上一节提到的一样)。这样做的是将 Numpy 的NaN
改为 Pandas 的NA
,这允许它是一个整数。
df['missing_col'] = df['missing_col'].**astype('Int64')**
或者,我们可以用另一个值替换 Numpy NaN
(例如用0
替换NaN
)并调用astype('int')
df['mix_col'] = df['missing_col'].**fillna(0)**.**astype('int')**
如果您想了解有关处理缺失值的更多信息,可以查看:
5.将货币列转换为数字
让我们转到钱这一栏。问题是,如果我们使用上面的方法,我们将得到所有的NaN
或NA
值,因为它们都是带有符号£
和,
的字符串,并且它们不能转换成数字。所以我们要做的第一件事就是删除所有无效的符号。
>>> df['money_replace'] =
df['money_col']**.str.replace('£', '').str.replace(',','')**
>>> df['money_replace'] = pd.to_numeric(df['money_replace'])
>>> df['money_replace']0 1000.0
1 2400.0
2 2400.0
3 2400.0
Name: money_replace, dtype: **float64**
我们链接 2 个replace()
调用,一个用于£
,另一个用于,
,用空字符串替换它们。
如果你熟悉正则表达式,我们也可以用正则表达式替换那些符号。
>>> df['money_regex'] =
df['money_col'].str.replace(**'[\£\,]', '', regex=True**)
>>> df['money_regex'] = pd.to_numeric(df['money_replace'])
>>> df['money_regex']
replace('[\£\,]', '', regex=True)
表示我们想用一个空字符串替换£
和,
。参数regex=True
假设传入的模式是一个正则表达式(注意它默认为True
)。
6.将布尔值转换为 0/1
我们有True
/ False
,但是您可以想象一种情况,我们需要这些作为0
和1
,例如,如果您正在构建一个机器学习模型,这是您的输入特征之一,您需要它是数字,您将使用0
和1
来表示False
和True
。这个其实很简单,你可以直接叫astype('int')
:
df['boolean_col'] = df['boolean_col']**.astype('int')**
7.一次转换多个列数据类型
到目前为止,我们一直在一次转换一列的数据类型。例如
# Converting column string_col and int_col one at a time
df['string_col'] = df['string_col'].astype('float16')
df['int_col'] = df['int_col'].astype('float16')
有一个 DataFrame 方法,也称为astype()
,允许我们同时转换多个列数据类型。当您有一堆要更改的列时,这很节省时间。
df = df.astype({
**'string_col': 'float16',
'int_col': 'float16'**
})
8.定义读取 CSV 文件时每列的数据类型
如果您想在读取 CSV 文件时为每一列设置数据类型,可以在加载数据时使用参数dtype``read_csv()
:
df = pd.read_csv(
'dataset.csv',
**dtype={
'string_col': 'float16',
'int_col': 'float16'
}**
)
dtype
参数取一个字典,键代表列,值代表数据类型。此方法与上述方法的区别在于,此方法在读取过程中进行转换,可以节省时间并提高内存效率。
9.创建自定义函数将数据转换为数字
当数据转换有点复杂时,我们可以创建一个自定义函数,并将其应用于每个值,以转换为适当的数据类型。
例如,在 money_col 列中,有一个我们可以使用的简单函数:
>>> **def convert_money(value):
value = value.replace('£','').replace(',', '')
return float(value)**>>> df['money_col']**.apply(convert_money)**0 1000.0
1 2400.0
2 2400.0
3 2400.0
Name: money_col, dtype: **float64**
我们也可以使用λ函数:
df['money_col']
.apply(**lambda v: v.replace('£','').replace(',','')**)
.astype('float')
10.astype()
与to_numeric()
的区别
将数据类型从一种转换到另一种最简单的方法是使用astype()
方法。熊猫数据框架和系列都支持这种方法。如果您已经有了数字数据类型(int8
、int16
、int32
、int64
、float16
、float32
、float64
、float128
和boolean
),您也可以使用astype()
进行以下操作:
- 将其转换为另一种数字数据类型(int 到 float,float 到 int,等等。)
- 使用它向下转换到较小的字节大小,或者向上转换到较大的字节大小
然而,astype()
对混合类型的列不起作用。例如,混合列有a
,而缺失列有NaN
。如果我们尝试使用astype()
,我们将得到一个值错误。从熊猫 0.20.0 开始,这个错误可以通过设置参数errors='ignore',
来抑制,但是你的原始数据将原封不动地返回。
Pandas to_numeric()
函数可以更优雅地处理这些值。与其失败,我们可以设置参数errors='coerce'
将无效值强制为NaN
:
pd.to_numeric(df['mixed_col'], **errors='coerce'**)
结论
我们已经看到了如何使用astype()
和to_numeric()
将 Pandas 数据列转换为数字类型。astype()
是最简单的方法,在转换方式上提供了更多的可能性,而to_numeric()
具有更强大的错误处理功能。
我希望这篇文章能帮助你节省学习熊猫的时间。我建议您查看一下astypes()
和to_numeric()
API 的文档,并了解您可以做的其他事情。
感谢阅读。请查看笔记本获取源代码,如果你对机器学习的实用方面感兴趣,请继续关注。
你可能会对我的其他一些熊猫文章感兴趣:
- 熊猫 json_normalize()你应该知道的扁平化 JSON
- 所有熊猫切()你应该知道把数值数据转换成分类数据
- 使用熊猫方法链接提高代码可读性
- 如何对熊猫数据帧进行自定义排序
- 为了数据分析你应该知道的所有熊猫移位()
- 何时使用 Pandas transform()函数
- 你应该知道的熊猫串联()招数
- 所有熊猫合并()你应该知道
- 在 Pandas 数据帧中处理日期时间
- 熊猫阅读 _csv()你应该知道的招数
- 用 Pandas read_csv()解析日期列应该知道的 4 个技巧
更多教程可以在我的 Github 上找到
将表情符号转换为文本
使用表情符号将表情符号转换为文本描述
伯纳德·赫曼特在 Unsplash 上的照片
你有没有收到过一个你不知道或者不明白是什么的表情符号?我知道这发生在我们所有人身上。有很多我们不理解的表情符号,在创建 NLP 模型时使用这些数据会导致差异。
如果我告诉你,你可以把这些表情符号改成文字,会怎么样?是的,你没听错,你可以这么做。这不仅易于理解,而且可以为 NLP 建模准备好数据。
Emot 是一个开源的 python 库,我们可以用它来将表情符号转换成文本描述。在本文中,我们将探讨如何使用 Emot。
让我们开始吧…
安装所需的库
我们将从使用 pip 安装 Emot 开始。下面给出的命令可以做到这一点。
pip install emot
导入所需的库
在这一步,我们将导入所需的库,即 Emot。
import emot as e
将表情符号转换为文本
这是最后一步,我们将把一些包含表情符号的文本传递到表情库,并将表情符号转换为文本。
text = "I am a coder😎"
con = e.emoji(text)
con
来源:作者
这里你可以看到它是多么容易地将表情符号转换成文本。让我们尝试更多的表情符号。
text = "😁😆😅😂😇🙃"
con = e.emoji(text)
con
来源:作者
你看表情符号有多棒,用表情符号创造文字。让我们尝试最后一个非常独特的表情符号。
text = "🤡"
con = e.emoji(text)
con
来源:作者
在将文本数据传递给 NLP 模型之前,可以用它来预处理文本数据。继续尝试使用不同的表情符号,并在回复部分让我知道您的评论。
本文是与皮尤什·英加尔合作完成的。
在你走之前
感谢 的阅读!如果你想与我取得联系,请随时通过 hmix13@gmail.com 联系我或我的 LinkedIn 个人资料 。可以查看我的Github简介针对不同的数据科学项目和包教程。还有,随意探索 我的简介 ,阅读我写过的与数据科学相关的不同文章。
将科学 Kaggle 笔记本转换为友好的 Python 包
这篇文章展示了如何轻松地将笔记本转换为标准 Python 包,并在培训脚本中包含一个简单的命令行界面(CLI ),以实现更快的超参数交互。
在之前的文章中,我们分享了如何筛选给定的数据集,并展示了如何将类似文件的数据集包装到 PyTorch 类数据集,这是数据处理的核心。此外,我们基于一个 TorchVision 模型在 PyTorchLightning 中编写了一个基本的图像多标签分类模型,并在 GPU 上无缝地训练它,而无需在 Jupyter 笔记本中编写额外的代码。
笔记本电脑非常适合快速原型开发或数据探索,但对于大规模开发来说不太实用。笔记本电脑的单元式结构使得寻找不同的参数配置来优化模型具有挑战性。笔记本结果不一定是可重复的,因为您可能会交换一些编辑单元格或不按顺序运行它们。笔记本不支持版本控制,这使得与同事或更广泛的开源社区的协作变得困难。
将笔记本转换为可共享的 Python 包
使用 PyTorch Lightning,将一个笔记本转换成一个基本的 python 包是很简单的事情,并将其作为 GitHub 存储库共享。PyTorch Lightning 实施了最佳实践,并通过其功能(数据处理和模型架构)暗示了自然的代码组织。
python 包是一种组织函数、类等的结构。,到 python 模块中,可以在任何地方导入和使用它们。这种包装是紧凑的和单元的,适于与 Pip 或 Conda 一起分配。所以在我们的例子中,我们创建了两个 python 模块— data
和models
,并复制粘贴了之前实现的类。
使用 [setup.py](https://packaging.python.org/tutorials/packaging-projects/)
我们可以安装我们的软件包,并将其导入我们环境中的任何地方。否则,我们需要将包的路径添加到我们的 Python 系统路径中。通过将数据和模型实现移动到我们自己的包中,我们可以通过导入在我们的笔记本中使用它,并将所有培训简化为以下几行:
来自共享的库的代码片段。
这使您能够在 GitHub 上共享您的库/包,使人们能够轻松地在您的工作基础上进行构建。
将 PL 实现移动到包中,然后通过导入使用它们的模式。
模板/初学者存储库
作为你下一个项目的灵感,我创建了这个stater/template repository,它用一个演示包challenge_xyz
建立了一个基本的项目结构。它包括测试、代码格式化和 Github 问题/公关模板的基本工作流程。您可以从该模板创建以下存储库;参见分步文档。
https://github.com/Borda/kaggle_sandbox https://medium.com/geekculture/thought-on-why-code-formatting-is-important-even-more-for-open-source-476829b54eaf
包装实体的小技巧
现在我们开源了我们的代码,我们已经准备好让他们贡献或修复问题。这听起来不错,但是如何确保新的修改不会破坏其他东西呢?
标准的软件工程答案是通过测试(单元测试或集成测试)。在提交/发布每一个新的贡献之前,我们可以在存储库中运行所有的测试,并验证所有的工作都如预期的那样。就我个人而言,我在我的大部分项目中使用的都是 pytest 包,reed 紧随其后进入。
使用标准框架又一次变得很方便,在我们的例子中是 Pytoch Lightning,它已经进行了数百次测试来保证正确的行为。我们只需要编写一小组测试用例来覆盖我们的解决方案,因为 lightning 已经完成了剩下的工作。例如,我们可能想要实例化所有的类。
如下所示,我们添加了参数化,它使用不同的网络调用相同的测试两次——第一次获取 network 作为实例,第二次获取 networks 作为字符串名称:
为了测试训练能力,我们可以通过设置下面的Trainer
的标志fast_dev_run=True
,在一些样本图像上测试负载并运行您的模型。
测试的最后一个技巧是包括几个样本图像和类似注释的文件来模拟实际的数据集。这允许您更真实地测试正确的用例。
将笔记本转换为 CLI 可配置的 Python 脚本
将笔记本转换成 python 脚本的一种方法是通过 JupyterLab——选择“下载为-> Python”。简化笔记本应更短,主要关注数据/模型/教练配置(如果我们跳过所有数据探索部分)。
不幸的是,它会生成一个带有硬编码参数的脚本。为了解决这个限制,我们需要编写自己的argparser
,并将所有 CLI 参数映射到模型/数据模块/训练器。幸运的是,Lightning 最近推出了自己的极简LightningCLI
界面,为你处理争论投标。
https://devblog.pytorchlightning.ai/auto-structuring-deep-learning-projects-with-the-lightning-cli-9f40f1ef8b36 [## 使用 Lightning CLI 自动构建深度学习项目
devblog.pytorchlightning.ai](https://devblog.pytorchlightning.ai/auto-structuring-deep-learning-projects-with-the-lightning-cli-9f40f1ef8b36)
LightningCLI
几乎不需要额外的代码就能为 Lightning 脚本提供简单的参数解析!它被设计成接受这些主要的组件/角色:LightningModule
、LightningDataModule
和Trainer
,解析命令行参数并实例化这些角色。最终,LightningCLI
执行了训练。最终的脚本如下所示:
使用 CLI,您可以在任何终端中运行 python 脚本。
python kaggle_plantpatho/cli_train.py \
--model.model 'resnet34' \
--data.base_path /home/jirka/datasets/kaggle_plant-pathology \
--trainer.max_epochs 5
由于我们计划试验不同的模型架构,我们可能需要调整每个模型的批量大小,以使用最大的资源(对于较小的模型,例如 *resnet18*
,我们可以使用比 *resnet101*
大得多的批量)。这可以通过在方法 *before_fit(...)*
中实现的闪电训练器 [*tune*](https://pytorch-lightning.readthedocs.io/en/1.4.0/common/trainer.html?highlight=tune#auto-scale-batch-size)
来实现,因为它会找到适合 GPU 内存的最大 *batch_size*
并使用它进行训练。
在这篇文章中,我们展示了如何将科学笔记本转换成可以共享的标准 python 包。我们已经讨论了添加最小测试的动机,以便在不意外破坏它的情况下促进任何未来的包增强。最后,我们展示了从普通 python 脚本到公开所有命令行参数的通用脚本的简单转换。
在未来,我们将展示如何使用这个简单的脚本在 Grid.ai 上并行运行跨多台机器的超参数搜索,并在线观察训练表现。
敬请关注,并跟随我了解更多!
关于作者
Jirka boro vec已经在几家不同的 IT 公司从事机器学习和数据科学工作好几年了。特别是,他喜欢探索有趣的世界问题,并用最先进的技术解决它们。此外,他开发了几个开源 python 包,并积极参与其他知名项目。在 Grid.ai 工作,担任研究工程师,是pytorchlightning . ai的主要撰稿人。
使用 m2cgen (Python)将机器学习模型转换为 SAS
在 SAS 中部署经过训练的 ML 模型(如 XGBoost 和 LightGBM)的方法
m2cgen 是一个非常友好的包,可以将许多不同的训练模型转换成支持语言的语言,比如 R 和 VBA。但是,m2cgen 还不支持 SAS。本文是为那些需要在 SAS 环境中部署经过训练的模型的人准备的。本文介绍的方法是首先将模型转换为 VBA 代码,然后将 VBA 代码转换为 SAS 脚本。
本教程中使用的脚本上传到我的 Github repo ,随意克隆文件。
克里斯·劳顿在 Unsplash 上的照片
包裹
m2cgen
功能
m2cgen
(Model 2 Code Generator)——是一个轻量级的库,它提供了一种将训练好的统计模型转换成本机代码(Python、C、Java、Go、JavaScript、Visual Basic、C#、PowerShell、R、PHP、Dart、Haskell、Ruby、F#)的简单方法。
示范
- 将 XGBoost 模型转换为 VBA,然后转换为 SAS 脚本
- 将 XGBoost 模型转换为 VBA,然后转换为 SAS 脚本(缺少值)
数据
任务 1:将 XGBoost 模型转换为 VBA 模型
# import packages
import pandas as pd
import numpy as np
import os
import refrom sklearn import datasets
from xgboost import XGBClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_scoreimport m2cgen as m2c# import data
iris = datasets.load_iris()
X = iris.data
Y = iris.target
首先,我们导入这个任务所需的包和数据。
# split data into train and test sets
seed = 2020
test_size = 0.3
X_train, X_test, y_train, y_test = train_test_split(X, Y, test_size=test_size, random_state=seed)# fit model on training data
model = XGBClassifier()
model.fit(X_train, y_train)
然后,我们来训练一个简单的 XGBoost 模型。
code = m2c.export_to_visual_basic(model, function_name = 'pred')
接下来,将 XGBoost 模型转换为 VBA 模型。使用 m2cgen 的 export_to_visual_basic 函数,可以得到你训练好的 VBA 语言的 XGBoost 模型。转换成其他语言的脚本也和转换成 VBA 语言的脚本一样简单。
照片由 cyda 拍摄
接下来是本教程的核心,在将模型转换为 VBA 之后,需要一些步骤来将 VBA 代码转换为 SAS 脚本,例如删除 SAS 环境中不使用的许多不必要的行,如“模块 xxx”、“函数 yyy”和“Dim var Z As Double”,并插入“;”以遵循 SAS 中的语法规则。
# remove unnecessary things
code = re.sub('Dim var.* As Double', '', code)
code = re.sub('End If', '', code)# change the script to sas scripts
# change the beginning
code = re.sub('Module Model\nFunction pred\(ByRef inputVector\(\) As Double\) As Double\(\)\n',
'DATA pred_result;\nSET dataset_name;', code)# change the ending
code = re.sub('End Function\nEnd Module\n', 'RUN;', code)# insert ';'
all_match_list = re.findall('[0-9]+\n', code)
for idx in range(len(all_match_list)):
original_str = all_match_list[idx]
new_str = all_match_list[idx][:-1]+';\n'
code = code.replace(original_str, new_str)
all_match_list = re.findall('\)\n', code)
for idx in range(len(all_match_list)):
original_str = all_match_list[idx]
new_str = all_match_list[idx][:-1]+';\n'
code = code.replace(original_str, new_str)# replace the 'inputVector' with var name
dictionary = {'inputVector(0)':'sepal_length',
'inputVector(1)':'sepal_width',
'inputVector(2)':'petal_length',
'inputVector(3)':'petal_width'}
for key in dictionary.keys():
code = code.replace(key, dictionary[key])# change the prediction labels
code = re.sub('Math.Exp', 'Exp', code)
code = re.sub('pred = .*\n', '', code)
temp_var_list = re.findall(r"var[0-9]+\(\d\)", code)
for var_idx in range(len(temp_var_list)):
code = re.sub(re.sub('\\(', '\\(', re.sub('\\)', '\\)', temp_var_list[var_idx])), iris.target_names[var_idx]+'_prob', code)
逐步解释:
# remove unnecessary things
code = re.sub('Dim var.* As Double', '', code)
code = re.sub('End If', '', code)# change the beginning
code = re.sub('Module Model\nFunction pred\(ByRef inputVector\(\) As Double\) As Double\(\)\n',
'DATA pred_result;\nSET dataset_name;', code)# change the ending
code = re.sub('End Function\nEnd Module\n', 'RUN;', code)
前三部分非常简单。我们简单地使用正则表达式去掉不需要的行,然后将脚本的开头改为“DATA pred _ result\ n 设置数据集名称;"其中,pred_result 是指运行 SAS 脚本后的输出表名称,dataset_name 是指我们需要预测的输入表名称。最后一部分是将脚本的结尾改为“RUN”。
# insert ';'
all_match_list = re.findall('[0-9]+\n', code)
for idx in range(len(all_match_list)):
original_str = all_match_list[idx]
new_str = all_match_list[idx][:-1]+';\n'
code = code.replace(original_str, new_str)
all_match_list = re.findall('\)\n', code)
for idx in range(len(all_match_list)):
original_str = all_match_list[idx]
new_str = all_match_list[idx][:-1]+';\n'
code = code.replace(original_str, new_str)
遵循 SAS 中的语法规则,";"需要指示每个语句的结束。
照片由 cyda
# replace the 'inputVector' with var name
dictionary = {'inputVector(0)':'sepal_length',
'inputVector(1)':'sepal_width',
'inputVector(2)':'petal_length',
'inputVector(3)':'petal_width'}
for key in dictionary.keys():
code = code.replace(key, dictionary[key])
利用字典,我们可以将“输入向量”与输入数据集中的变量名进行映射,并一次性更改所有的“输入向量”。
# change the prediction labels
code = re.sub('Math.Exp', 'Exp', code)
code = re.sub('pred = .*\n', '', code)
temp_var_list = re.findall(r"var[0-9]+\(\d\)", code)
for var_idx in range(len(temp_var_list)):
code = re.sub(re.sub('\\(', '\\(', re.sub('\\)', '\\)', temp_var_list[var_idx])), iris.target_names[var_idx]+'_prob', code)
转换步骤的最后一部分是更改预测标签。
由 cyda 拍摄
# save output
vb = open('vb1.sas', 'w')
vb.write(code)
vb.close()
最后,我们可以用后缀“.”保存输出。sas "
第一个任务到此结束,现在,您应该能够将您训练的模型转换成 SAS 脚本了。要仔细检查创建的 SAS 脚本是否有任何问题,您可以使用以下脚本来检查 python 预测和 SAS 预测的差异。请注意,预测的概率(python vs SAS)显示了一点差异,但差异应该不是非常显著。
# python pred
python_pred = pd.DataFrame(model.predict_proba(X_test))
python_pred.columns = ['setosa_prob','versicolor_prob','virginica_prob']
python_pred# sas pred
sas_pred = pd.read_csv('pred_result.csv')
sas_pred = sas_pred.iloc[:,-3:]
sas_pred(abs(python_pred - sas_pred) > 0.00001).sum()
照片由 cyda 拍摄
任务 2:将 XGBoost 模型转换为 VBA,然后转换为 SAS 脚本(缺少值)
如果您的数据在训练数据中没有缺失值,XGBoost 在生成树时默认将“缺失值”放在左边的节点上(如下树图所示)(引用)。从 m2cgen 生成的脚本中,您可以发现,如果变量大于或等于给定的数字,测试的条件总是 be。因此,如果测试或预测数据集中有缺失值,脚本会将“缺失值”留给 else 部分。例如,在我们从任务 1 生成的 SAS 脚本中,第一个测试条件是“If(petal _ length)>=(2.45)Then var 0 =-0.21827246;否则 var0 = 0.42043796",所以如果 petal_length 缺失,不大于等于 2.45,var0 会赋为 0.42043796。另一个例子如下所示。
照片由 cyda
照片由 cyda
如果您的训练数据包含缺失值,该怎么办?XGBoost 会根据训练结果将“缺失值”放到左侧或右侧节点。(因此,您可以看到 SAS 脚本中显示的条件有时是" = ")
由 cyda 拍摄
由 cyda 拍摄
您可以使用以下脚本创建带有缺失值的数据集,并重复任务 1 中的步骤,以查看和比较 SAS 和 python 的预测输出。
from random import sample
from sklearn import datasets# import data
iris = datasets.load_iris()
X = iris.data
Y = iris.target# assume that there are missing values in the first 2 columns of the data
sequence = [i for i in range(len(X))]
subset0 = sample(sequence, 30)
subset1 = sample(sequence, 50)
subset2 = sample(sequence, 40)
subset3 = sample(sequence, 60)
X[[(subset0)],0] = np.nan
X[[(subset1)],1] = np.nan
X[[(subset0)],2] = np.nan
X[[(subset1)],3] = np.nan
我做了测试,发现一些行具有相同的预测输出,而一些行显示出很大的差异(见下图)。
照片由 cyda
我比较了 SAS 脚本的中间步骤中生成的 var。让我们以下面显示的第二行为例。如果测试的条件是“If (petal_length) >= (2.45)那么 var 0 =-0.217358515;Else …”并且缺少了 petal_length,所以不满足条件,转到 Else 语句,那么测试的第二个条件是“If(petal _ width)> =(0.84999996)Then var 0 =-0.155172437;Else …”并且 petal_width 是 0.2,同样,它不满足条件并转到 Else 语句。接下来,我们进入第三个条件,“If (sepal_length) < (11.600001) Then var0 = 0.411428601; Else …” and we see that sepal_length is missing, it should not fulfill the condition but SAS somehow accept it as True and the var0 is 0.411428601.
Photo by cyda
cyda 拍摄的照片
因此,为了迎合这种情况,我添加了一些脚本来强制脚本首先检查值是否丢失。
# handle missing values
all_match_list = re.findall('If.*Then', code)
for idx in range(len(all_match_list)):
original_str = all_match_list[idx]
new_str = ' '.join(original_str.split()[:-1] + [**'and not missing'**, original_str.split()[1], ' Then'])
code = code.replace(original_str, new_str)
因此,将 VBA 转换为服务协议的手动脚本将更改为以下脚本。你可以在我的 GitHub repo 中找到完整版。
# remove unnecessary things
code = re.sub('Dim var.* As Double', '', code)
code = re.sub('End If', '', code)# change the beginning
code = re.sub('Module Model\nFunction pred\(ByRef inputVector\(\) As Double\) As Double\(\)\n',
'DATA pred_result;\nSET dataset_name;', code)# change the ending
code = re.sub('End Function\nEnd Module\n', 'RUN;', code)# insert ';'
all_match_list = re.findall('[0-9]+\n', code)
for idx in range(len(all_match_list)):
original_str = all_match_list[idx]
new_str = all_match_list[idx][:-1]+';\n'
code = code.replace(original_str, new_str)
all_match_list = re.findall('\)\n', code)
for idx in range(len(all_match_list)):
original_str = all_match_list[idx]
new_str = all_match_list[idx][:-1]+';\n'
code = code.replace(original_str, new_str)# handle missing values
all_match_list = re.findall('If.*Then', code)
for idx in range(len(all_match_list)):
original_str = all_match_list[idx]
new_str = ' '.join(original_str.split()[:-1] + ['and not missing', original_str.split()[1], ' Then'])
code = code.replace(original_str, new_str)# replace the 'inputVector' with var name
dictionary = {'inputVector(0)':'sepal_length',
'inputVector(1)':'sepal_width',
'inputVector(2)':'petal_length',
'inputVector(3)':'petal_width'}
for key in dictionary.keys():
code = code.replace(key, dictionary[key])# change the prediction labels
code = re.sub('Math.Exp', 'Exp', code)
code = re.sub('pred = .*\n', '', code)
temp_var_list = re.findall(r"var[0-9]+\(\d\)", code)
for var_idx in range(len(temp_var_list)):
code = re.sub(re.sub('\\(', '\\(', re.sub('\\)', '\\)', temp_var_list[var_idx])), iris.target_names[var_idx]+'_prob', code)
在本教程中,我使用 m2cgen 包将 XGBoost 模型转换为 VBA 代码,然后转换为 SAS 脚本,但没有必要首先转换为 VBA 代码,如果您愿意,可以选择其他语言,如 C 或 JAVA。本教程只是演示了如何将脚本从一种语言转换成另一种语言。
想了解更多关于使用 m2cgen 的知识,请前往官方 Github 资源库。
用 Python 和 Numpy 实现卷积层黑客攻击
用 python 从头开始创建一个卷积层,用自定义内核破解其权重,并验证其结果与 pytorch 生成的结果相匹配
如果你开始在深度学习中使用卷积层,你可能会对所涉及的参数、计算和通道的混合感到困惑。从条纹到填充,输入和输出通道,内核和可学习的参数,有很多事情正在进行。在这篇文章中,我们将深入到这些 conv 层的最底层。我们将:
- 用 python 从头编码一个卷积层到一点一点理解当我们通过其中一个层传递数据时是怎么回事。
- 黑掉 convnet 的参数,生成自定义内核和随机内核的混合。
- 使用 Pytorch 和 Python-numpy 比较结果,验证我们获得了完全相同的输出。准备好了吗?让我们开始吧。
初始设置
跳到 https://colab.research.google.com/的 google colab 或者任何其他可以用 python 和 pytorch 编码的平台。
首先,我们初始化我们需要的库。这将允许我们用 pytorch 和 numpy 库进行编码,这样我们可以在以后比较它们的结果。
import torch
from torch import nn
from torchvision import transforms
from torchvision.transforms.functional import pad
import numpy as np
import time
import numpy.testing as npt
from PIL import Image
from matplotlib import pyplot as plt
好了,现在我们做两件事:
- 设置种子以便结果一致且可重复(可选)
- 上传图像到系统。我们使用 PIL 库加载它,并将其与 R、G 和 B 三个通道一起可视化。在这个例子中,我使用了一朵花的 128x128 像素的图像。
img = Image.open("flower128.jpg")
plt.figure()
plt.axis('off')
plt.imshow(img)
fig, axs = plt.subplots(nrows=1, ncols=3, figsize=(12,4))
for i, ax in enumerate(axs.flatten()):
plt.sca(ax)
plt.axis('off')
plt.imshow(np.array(img)[:, :, i], cmap='gray', vmin = 0, vmax = 255)
plt.show()
帕特里克·康纳·克洛夫在 Unsplash 上拍摄的照片
是时候设置我们卷积层的主要参数了:
- 首先,我们将图像的原始宽度和高度存储在 w_in 和 h_in 中,在本例中是 128x128 像素。
- c_in 为 3,因为输入数据有 3 个通道。 c_out 将会是 5 因为我们想要在 conv 层的输出中产生 5 个通道。这可能是 4,7,11 或任何数字。在这个例子中,我选择 5。
- k 是 3 因为我们将使用一个 3x3 的内核大小。
- 我决定在这个例子中我想产生一个宽度为高度为和宽度为 64 像素的输出**。为了做到这一点,我们将步幅设置为 2 并将填充设置为 1 ,因此我们将以 2 为增量移动我们的内核,并且我们将创建 1 的外部填充以确保最终尺寸是初始尺寸的一半。**
- 为了验证(并且预先计划)我们的输出宽度和高度,我们可以使用这个简化的公式: (n+2*pad-ks)//stride +1 ,我们将输出尺寸存储在 w_out 和 h_out 中。
w _ out=(128+2 * 1–3)//2+1 =64
h _ out=(128+2 * 1–3)//2+1 =64
w_in=img.size[0]
h_in=img.size[1]
c_in=3
c_out=5
k=3
stride=2
padding=1
# (n+2*pad-ks)//stride +1
w_out = (w_in+2*padding-k)//stride +1
h_out = (h_in+2*padding-k)//stride +1
pytorch conv2d 层
现在,我们创建一个 pytorch conv2d 层,并根据正态分布初始化其参数:
- 将图像数据转换成张量。这将产生形状为 3,128,128 的张量。然后我们使用 unsqueeze_(0)在开头添加一个额外的维度,然后获得最终的形状: 1,3,128,128 。第一维表示我们正在用一批图片做这个实验。
- 然后,我们声明函数 init_weights ,该函数将使用从正态分布采样的值初始化 conv 层的权重和偏置参数。
- 最后我们使用 pytorch nn。Conv2d 函数创建 Conv 层,我们调用 init_weights 函数初始化它的权重。这将创建 pytorch conv 层: 【Conv2d(3,5,kernel_size=(3,3),stride=(2,2),padding=(1,1))
#### Pytorch Conv2d layer
data = transforms.ToTensor()(img).unsqueeze_(0)def init_weights(m):
if isinstance(m, nn.Conv2d):
torch.nn.init.normal_(m.weight, 0.0, 0.02)
torch.nn.init.normal_(m.bias, 0.0, 0.02)convlayer=nn.Conv2d(c_in, c_out, k, stride=stride, padding=padding)
convlayer=convlayer.apply(init_weights)
print(convlayer)
numpy conv2d 层设置
挑战仍在继续。现在让我们设置使用 python 和 numpy 库创建 conv2d 层所需的数据。
- 我们复制了一张图片,并使用填充函数在其周围创建一个 1 像素的填充(遵循我们在 pytorch 中使用的填充策略)。请注意,我使用了 torchvision 函数来进行填充,但是您可以用许多其他方式来完成此操作,而无需使用 torchvision。
- 然后我们转置通道维度,并使用 expand_dims 在开头添加一个额外的维度。此时,我们将拥有:
Numpy 输入数据 : 1x3x130x130
Pytorch 输入数据 : 1x3x128x128
请注意,Numpy 数据包含填充,而 Pytorch 数据不包含填充,因为 pytorch convd2d 层会自行应用填充。 - 最后,我们将 numpy 数据除以 255.0,使其值介于 0 和 1 之间。
- 此时,pytorch 和 numpy 数据具有相似的结构和值。是行动的时候了!
img2= img.copy()
img2=pad(img2, (1,1))
data2 = np.array(img2).transpose((2, 0, 1))
data2 = np.expand_dims(data2, axis=0)
data2 = data2/255.0
破解重量
现在,我们将有一些乐趣黑客这些层的权重。
首先让我们打印 pytorch 层的权重和偏差的形状。
我们可以看到权重的结构是:5,3,3,3 ( c_out,c_in,k,k )。偏差要简单得多,我们只要有尽可能多的输出通道。
所以我们有 135+5=140 个参数,每个输出通道一个,每个输入通道一个,每个内核维度一个,加上偏置参数。然后我们使用 np.zeros 命令用 numpy 初始化类似的结构,最初用零填充它们。
print(convlayer.weight.shape)
print(convlayer.bias.shape)
# Print Output:
# torch.Size([5, 3, 3, 3])
# torch.Size([5])w=np.zeros((c_out, c_in, k, k))
b=np.zeros((c_out))
此时,在 pytorch 数据结构的情况下,我们的内核的参数值被初始化为从正态分布采样的值,在 numpy 结构的情况下被初始化为零。
我们知道,无论我们在内核中拥有什么,都将决定我们从图像中提取什么样的模式。因此,我们将执行以下操作:
- 我们将黑掉连接图像前 3 个输出通道和输入通道的前 3 个内核。
- 我们将让与最后 2 个输出通道连接的另外 2 个内核具有来自正态分布的随机值。
让我们首先在前 3 个内核中创建参数的值。
- 我们创建一个名为 ker 的列表。
- 然后我们给它添加三个内核结构。
- 第一个是一个将检测水平线的内核。
- 第二个是一个内核,它将模糊图像
- 第三个是内核,它将锐化图像的边缘
ker=[]
ker.append(np.array([[-1.1,-1.1,-1.1],[2.2,2.2,2.2],[-1.1,-1.1,-1.1]]))
ker.append(np.array([[1/9,1/9,1/9],[1/9,1/9,1/9],[1/9,1/9,1/9]]))
ker.append(np.array([[0.0,-1.0,0.0],[-1.0,6.0,-1.0],[0.0,-1.0,0.0]]))
现在是时候侵入 pytorch 层权重和改变参数的值,这些参数与连接到前 3 个输出通道的内核相对应,以便它们成为我们手动创建的内核。
最后的两个内核将保持它们之前具有的来自正态分布的相同随机值。
with torch.no_grad():
for w0 in range(convlayer.weight.shape[0]):
for w1 in range(convlayer.weight.shape[1]):
if (w0<len(ker)):
convlayer.weight[w0][w1]=torch.from_numpy(ker[w0])
现在我们对 numpy 卷积数据结构做同样的事情。我们对前 3 个输出通道和内核进行了同样的操作,对最后两个输出通道,我们从 pytorch conv2d 层复制了正态分布的采样数据(因此 pytorch 和 numpy conv 层具有完全相同的参数)。
for w0 in range(c_out):
for w1 in range(c_in):
if (w0<len(ker)):
w[w0][w1]=ker[w0]
else:
w[w0][w1]=np.around(convlayer.weight[w0][w1].detach().numpy(),decimals=4)
b[w0]=np.around(convlayer.bias[w0].detach().numpy(),decimals=4)
这样,两种数据结构具有相同的参数。
- 将前 3 个输出通道连接到数据输入通道的前 3 个内核具有我们创建的手动内核值,它将检测水平线,产生模糊并锐化边缘。
- 最终 2 个内核具有从正态分布中采样的随机值,并且在 pytorch 和 numpy 结构中是相同的。
如果我们打印 pytorch conv2d 层权重的值和 numpy 权重的值,我们会看到它们是相同的。例如,这些是 numpy 数据结构:
## Showing 2 decimals for visual clarity
print(np.around(w,decimals=2))###### FIRST KERNEL, detect horizontal lines #####
[[[[-1.1 -1.1 -1.1 ]
[ 2.2 2.2 2.2 ]
[-1.1 -1.1 -1.1 ]][[-1.1 -1.1 -1.1 ]
[ 2.2 2.2 2.2 ]
[-1.1 -1.1 -1.1 ]][[-1.1 -1.1 -1.1 ]
[ 2.2 2.2 2.2 ]
[-1.1 -1.1 -1.1 ]]]###### SECOND KERNEL, blurring #####
[[[ 0.11 0.11 0.11]
[ 0.11 0.11 0.11]
[ 0.11 0.11 0.11]][[ 0.11 0.11 0.11]
[ 0.11 0.11 0.11]
[ 0.11 0.11 0.11]][[ 0.11 0.11 0.11]
[ 0.11 0.11 0.11]
[ 0.11 0.11 0.11]]]###### THIRD KERNEL, sharpen #####
[[[ 0\. -1\. 0\. ]
[-1\. 6\. -1\. ]
[ 0\. -1\. 0\. ]][[ 0\. -1\. 0\. ]
[-1\. 6\. -1\. ]
[ 0\. -1\. 0\. ]][[ 0\. -1\. 0\. ]
[-1\. 6\. -1\. ]
[ 0\. -1\. 0\. ]]]###### FOURTH KERNEL, random values #####
[[[-0\. -0.01 -0\. ]
[ 0.04 -0.01 -0.03]
[-0.01 0.01 0.02]][[-0.01 0\. 0\. ]
[-0.02 -0\. 0.02]
[ 0.02 -0.02 0.01]][[ 0.02 -0.01 -0.02]
[-0.02 -0\. -0.02]
[ 0.02 0.02 -0.04]]]###### FIFTH KERNEL, random values #####
[[[-0.03 -0.02 0.01]
[ 0.01 0.03 0.01]
[ 0\. 0.01 -0.07]][[ 0.02 -0.03 0.01]
[-0.03 0.03 -0.01]
[ 0.01 -0.01 0.01]][[-0.01 -0\. -0.02]
[-0.01 -0\. -0.05]
[ 0.01 0.01 -0\. ]]]]
因此,当我们获得两个 conv 层的输出并可视化产生的 5 个通道时,我们应该在前三个通道中看到水平线检测、模糊和锐化的影响,在后两个通道中,我们应该看到后两个内核中的随机值产生的不同结果。
黑客攻击完成后,我们现在可以开始创建 numpy 卷积层。
python 中的 conv 图层
我们将创建一个函数来执行标准深度学习卷积层的整个过程,它用纯 python 来完成。事情是这样的:
- 首先,我们创建一个数据结构来保存我们的结果。它的结构会是: **1,c_out,w_out,h_out。**第一维是 1,因为我们用的是一批只有 1 张的图片。第二维是我们在输出中接收的通道数,最后二维是图像的输出宽度和高度。在我们的例子中,我们知道这些最终的维度是:1,5,64,64。
- 现在,有趣的事情来了。我们将用一种简单的方式来做这件事,以便尽可能清楚地看到正在发生的事情。女士们先生们,准备好你们的圈圈,系好安全带,开始狂野之旅。
- 该版本的代码旨在尽可能简单明了,以促进理解。我选择从结尾开始,然后后退。所以我们将从输出尺寸和开始,然后向后移向起点。这是我们可以用来解决这些计算的许多可能性之一。
- 对于我们将要产生的输出结果的输出尺寸的每个位置( w_out 和 h_out
- 输出通道 ( c_out )的每个的
- 以及连接到每个输出通道的输入通道的每个的
- 对于我们的 内核 ( k x k )的维度中的每一个,将在每个输入通道中移动数据
- 没错,那一共是 6 个循环,确实没错。现在,在我们看到这些循环的底部发生了什么之前,还有一些事情要考虑。
- 对于每个输出通道,您将初始化一个变量,该变量将累积数据,这些数据将从与该输出通道连接的所有相应输入通道中收集(姑且称之为总计)。****
- 对于每个输入通道,,您将初始化另一个变量,该变量将累积正在收集的值,,因为内核移动通过每个通道中的数据(让我们称之为 kt )。
- 所以现在,一直到那套循环的底部。
- 取对应于你正在查看的当前输出通道和当前输入通道以及你正在考虑的内核中的当前位置的权重。(储存在重量中)
- 通过结合与内核相关的位置、输出和使用的步幅,计算数据中的位置。(存储在位置 1 和位置 2 中)
- 访问与我们正在寻址的当前输入通道和我们刚刚计算的那些位置的组合相对应的数据值( 0,ci,pos1,pos2 ,并将其存储在值中。
- 最后,将权重乘以值,并将其添加到变量 kt 中,该变量为每个输入通道累积计算值。
- 在变量 kt 中收集了来自每个输入通道的所有计算后,我们会将它们添加到与每个输出通道相关的计算中(这些计算存储在每个输出通道的变量 total 中)。
- 然后加上与我们考虑的输出通道相对应的偏置值。
- 最后,我们将最终总计值分配到输出结构中的正确位置: res[0,co,o1,o2] ,我们正在查看的当前输出通道的组合以及我们正在考虑的输出结果中的当前位置。
作者贾维尔·Ideami@ideami.com 制图
**def python_conv(data):
res=np.zeros((1, c_out, w_out, h_out))
for o1 in range(w_out):
for o2 in range(h_out):
for co in range(c_out):
total=0
for ci in range(c_in):
kt=0
for k1 in range(k):
for k2 in range(k):
weight = w[co,ci,k1,k2]
pos1=k1+o1*stride
pos2=k2+o2*stride
value = data[0, ci, pos1 ,pos2]
kt+= weight * value
total+=kt
res[0,co,o1,o2]=total+b[co]
return res**
唷!我们成功了,恭喜:)
有一件特别简单的事情。我们还将创建一个 sigmoid 函数,它将允许我们获得从 0 到 1 范围内的输出数据。然后我们就可以将我们的结果与 Pytorch 产生的结果进行比较。
**def sigm(data):
z = 1/(1 + np.exp(-data))
return z**
所以当有人问你深度学习 conv 层发生了什么时,你可以这样说:
哦,那很简单,是的,让我告诉你。因此…对于每个输出维度、每个输出通道、每个输入通道以及每个内核维度,选择与所有这些值的当前组合相对应的权重,并将其乘以由内核和输出位置的组合确定的位置处的数据值(最后一个值由跨距值修改)。我们在所有输入通道中累计这些计算结果,并将相应的偏置参数值与该结果相加,以获得与特定输出通道和我们所观察的输出尺寸部分相对应的最终值。然后,我们继续相同的过程,继续通过其余的输出通道和输出维度。”
只有一个问题。如果你提供了这样的解释, 人们可能会担心你最近没有睡好,他们可能会建议你开始服用褪黑激素,并开始一个深度的瑜伽和冥想计划。因此,最好你只展示视觉图形,简单告诉他们:
“你瞧, 不过是用一组循环 包裹起来的一堆乘法和加法,这不是非常简单吗?”
让我告诉你,那样会更好。
这太令人兴奋了。我们准备运行两个 conv 层并比较结果。我们开始吧!
测试 pytorch 和 python conv 图层
是时候运行两个 conv 层了。准备,稳住,开始!
**start=time.time()
ptorch_out = convlayer(data)
ptorch_out = nn.Sigmoid()(ptorch_out)
end=time.time()
print("conv2d completed")
print("Pytorch: time taken: ",end-start)start=time.time()
numpy_out = python_conv(data2)
numpy_out = sigm(numpy_out)
end=time.time()
print("Python: time taken: ",end-start)**
****Pytorch : **耗时:**0.0009734630584716797
Python:耗时:0.5253609657287
当然 Pytorch 在速度上是最好的,但是我们的 python 版本是如此的甜美可爱!(因为这有助于我们一步一步地了解情况)。
那么现在,让我们来比较结果吧!
首先让我们检查输出尺寸。
**print("Pytorch in shape: ",data.shape)
print("Python in shape: ",data2.shape)
print("Pytorch out shape: ",ptorch_out.shape)
print("Python out shape: ",numpy_out.shape)**
******形状 py torch:**火炬。大小([1,3,128,128])
蟒蛇在形状: (1,3,130,130)
Pytorch 出来形状:火炬。尺寸(【1,5,64,64】)
蟒蛇出形: (1,5,64,64)
维度看起来不错,现在让我们来看看两种方法的输出数据片段的子集。
**pytorch_verify=ptorch_out[0][0][0].cpu().detach().numpy()
pytorch_verify=np.around(pytorch_verify,decimals=3)
numpy_verify=np.around(numpy_out[0][0][0],decimals=3)print("pytorch: ",pytorch_verify[:25])
print("python: ",numpy_verify[:25])
assert np.allclose(pytorch_verify, numpy_verify)**
py torch 0.997 0.997 0.997 0.997 0.997 0.997 0.997 0.997 0.997 0.997 0.997 0.997 0.997 0.997 0.997 0.997 0.997 0 0.997 0 0.997 0.997 0 0.997 0 0.997 0 7 0.997 0 7 0.997 0 7 0.9997 0 7 0.9997 0 7 0
看起来不错!但是最好的测试是简单地在 pytorch 和 python 输出中可视化 5 个输出通道。让我们去争取吧。
**%matplotlib inlinenc=5plt.figure(figsize=(15, 15), dpi=80)fig, axs = plt.subplots(nrows=1, ncols=nc, figsize=(12,4))
for i, ax in enumerate(axs.flatten()):
plt.sca(ax)
plt.axis('off')
im=transforms.ToPILImage(mode='L')(ptorch_out[0][i])
plt.imshow(im, cmap='gray')
#plt.tight_layout()
plt.show()fig, axs = plt.subplots(nrows=1, ncols=nc, figsize=(12,4))
for i, ax in enumerate(axs.flatten()):
plt.sca(ax)
plt.axis('off')
im = Image.fromarray(numpy_out[0][i]*255)
im = im.convert("L")
plt.imshow(im, cmap='gray')
plt.show()**
我们到了。py torch 和 python conv 图层的结果相同。在前 3 个输出通道中,在这两种情况下,我们都可以看到对水平线的检测,模糊和锐化内核处理。在最后 2 个通道中,我们看到 2 个相同的随机变换,它们来自我们在最后 2 个内核结构的参数中设置的随机正态分布值。
结论和下一步措施
从零开始构建事物是详细了解事物的最佳方式之一。通过这个简单的练习,我们可以一步一步地了解深度学习标准卷积层中的计算流程。现在你已经为下一步做好了准备:****
- 你可以将类似的策略应用于深度学习生态系统中你感兴趣的其他部分。
- 你也可能开始认为卷积层已经过时了。所以你可以发明一些新的方法来从数据中提取模式,就像我们在这里做的那样,使用普通的 python 和 numpy,然后使它适应像 JAX / JIT 这样的东西,使它飞行并将其集成到一个自动微分的过程中,等等。
这篇文章的所有代码都可以在这个 github 库获得:****
****https://github.com/javismiles/convolutional-layer-hacking
如果您喜欢这篇文章,您可能也会对我发表的关于在 transformer 架构中进行计算的方式的文章感兴趣:
****