解释:ML 变换和缩放
何时使用变换或缩放?哪个定标器?哪些算法?
在建模之前,所有不同的实体应该在相同的比例上;保罗·卡萨尔斯在 Unsplash 上的照片
缩放、标准化和变换是数字特征工程的重要步骤,它们正被用于处理倾斜的特征并为建模而重新缩放它们。机器学习和深度学习算法高度依赖于输入数据质量。如果数据质量不好,再高性能的算法也是白搭。就像垃圾进:垃圾出一样简单。
在建模之前,数据科学家有如此多的预处理步骤需要测试和验证。这进一步增加了围绕变换和缩放的效果和使用的混乱。在这里,我们将尝试使用公共数据集来回答一些问题
何时使用变换或缩放?使用哪个定标器?哪些算法受益?
本练习使用的数据是来自家庭信用违约风险的主数据文件*。数据被整理以预测客户的还款能力。数据是针对所有贷款申请的,在我们的数据示例中,一行代表一个贷款申请。*
数据集可以从数据下载。而 Jupyter 笔记本可以从— 笔记本下载。
公布数据集是为了说明每个申请人的还贷能力?下面是目标特征和一些独立特征的分布。目标特征存在数据不平衡问题,正类只占全部数据的 8%。
目标特征:贷款违约
以下是一些重要的数字独立特征及其直方图。选择它们只是为了解释练习。他们都在寻找不同的范围和规模,例如AMT _ annuity在 mn 范围内,但 OWN_CAR_AGE 最大可以开到 90。
AMT _ 年金;金额 _ 贷方;金额 _ 商品 _ 价格;金额 _ 收入 _ 总额;天数 _ 出生;OWN _ CAR _ AGE
数字特征直方图:原始数据
下面是原始数据的合并箱线图,由于一个空间中不同的比例和倾斜特征,它看起来高度倾斜。
组合箱线图:原始数据
转换
正态分布特征是统计算法中的一种假设。深度学习和回归型算法也受益于正态分布的数据。
需要进行变换来处理倾斜的要素并使其呈正态分布。使用平方根/立方根/ 对数变换可以将右斜特征变换为正态特征。
根据上述直方图金额 _ 年金、金额 _ 信用、金额 _ 商品 _ 价格、金额 _ 收入 _ 总额、&自有 _ 汽车 _ 年龄为偏态数字特征,天数 _ 出生为正态分布。
偏斜可能是由于以下两个原因之一
- 存在极端异常的异常值,对我们来说可能不重要。
- 或者说特征的自然分布是偏斜的,而尾部对我们很重要。这是大多数现实生活中的情况
**对数变换简介:**如左图所示,正值的对数函数输出增长非常缓慢。因此,与较低的观测值相比,较高的值被边缘化得更多。
**变换的效果:**对数变换后,倾斜的数字特征可能呈正态分布。例如,在下图中,金额信用经过对数变换后呈正态分布。
日志转换前后:金额 _ 信用
- 对数变换对倾斜目标特征的影响(回归的情况): 对数变换可以将倾斜特征视为常态。并且,如果我们的目标特征是正态分布的,该算法将给予所有样本同等的重要性。它也被称为同质性。这相当于处理分类目标特征中的不平衡数据问题,就像我们在给定的数据集中一样。所以有一个正态分布的目标特性是很好的。
- 对数变换对偏斜的独立特征的影响: 对数变换可以使独立特征像上面一样正态化,其中 AMT_CREDIT 在对数之后接近正态分布。但是它可能不会改善目标和独立特征之间的关系。因此,处理独立的倾斜特征可能有益于也可能无益于建模精度,这完全取决于两者之间的原始因果关系。
缩放比例
缩放是重新缩放数据所必需的,当我们希望在我们的算法中使用相同的缩放比例来比较要素时,会用到缩放。而且,当所有特征都处于相同的比例时,它也有助于算法更好地理解相对关系。
如果从属特征转换为正态,则应在转换后应用缩放。
缩放后哪些算法可能受益?缩放有助于基于距离的算法,也有助于加快收敛
*线性&逻辑回归、KMeans/ KNN、*神经网络、 PCA 将受益于规模化
**缩放后哪些算法可能不会受益?**有些算法与缩放无关。基于熵&信息增益的技术对单调变换不敏感。
基于树的算法、决策树、随机森林、增强树(GBM、light GBM、xgboost) 可能无法从扩展中受益。
D 在缩放/标准化/规范化过程中,我们将遵循 sklearn 词汇表,因此使用通用单词 S 缩放而不是标准化或规范化是一个不错的选择
训练数据上拟合的定标器模型将用于转换测试集。切勿在测试数据上再次安装定标器
Sklearn 主要有以下四种定标器
1.最小最大缩放器
2。鲁棒定标器
3。标准缩放器
4。标准化者。
Minmax 定标器应该是定标的首选。对于每个特征,每个值减去相应特征的最小值,然后除以同一特征的原始最大值和最小值的范围。它的默认范围在[0,1]之间。
下面是最小最大缩放后所有 6 个特征的直方图。在缩放之前,我们没有对任何功能进行对数转换。MinMaxScaler 没有改变特性的内部分布,也把每个人放在了同一个尺度上。
最小最大缩放后的直方图
下面是缩放后所有 6 个特征的组合箱线图。并且,都在[0,1 ]的范围内。每个要素值之间的内部空间得到了保持,并且与原始数据相比,它们的相对分布看起来也更好
最小最大缩放器后的箱线图
当数据含有大量异常值,并且我们希望消除它们的影响时,可以使用鲁棒定标器。但是不重要的离群值应该首先被移除。RobustScaler 减去列的中值,然后除以四分位数范围。
下图是鲁棒缩放器后的特征直方图。尽管直方图看起来与原始数据分布相似,但它们各自的内部距离空间并不像原始数据那样保持不变。
鲁棒缩放器后的直方图
此外,如下面的方框图所示,现在范围不在[0,1]内。并且每个特征的值之间的相对空间被扭曲并且现在不相同。在这种情况下,使用 robustscaler 会向建模过程传递关于底层数据的错误信息。
鲁棒定标器后的箱线图
StandardScaler 重新调整每一列,使其具有 0 个平均值和 1 个标准差。它通过减去平均值并除以标准差来标准化特征。如果原始分布不是正态分布,可能会扭曲要素之间的相对空间。
以下是应用标准缩放器后的要素直方图,分布看起来类似于原始数据分布,但它们并不相同,它们各自的内部观测距离在标准缩放过程中发生了变化。
以下是标准缩放后要素的组合箱线图,正如预期的那样,它扭曲了要素值之间的相对距离,而在最小-最大值后它们看起来更好
标准定标器后的方框图
规格化器应用于行,而不是列,所以 sklearn 用户不应该混淆,也不应该使用规格化器。Normalizer 的一些应用案例是在同一时间序列中比较多个实体,即在给定时期内多个股票的股票移动
结论
- 在建模之前,应该对偏斜的目标特征进行正态性处理,尤其是当异常值也很重要时
- 在分析过程中,应了解如何处理倾斜的从属特征及其影响
- MinMaxScaler 应该是缩放的首选
- 实验和观察可以帮助我们进一步决定正确的方法
感谢阅读。如果你喜欢过这篇文章,你可能也会喜欢 熊猫指数——隐形 灵魂、 熊猫支点&堆叠
使用 OpenCV 进行转换
关于如何用 Python 的 OpenCV 转换图像的指南
如果你曾经不得不使用一个可以位图化的图像增强器,你可能知道这有多困难。在本文中,我们将了解变换,并探索移动、旋转、裁剪和调整大小等内容。
要求
对于示例,我们将使用 OpenCV、NumPy 和 Matplotlib。
import cv2
import numpy as np
import matplotlib.pyplot as plt
在这里,我学习了 OpenCV 的一些基础知识,比如读取、显示和修改图像的一些属性。本文中的例子将从那里开始,但我认为你不需要阅读它来跟上这一点。
变换矩阵
从一些简单的东西开始,我们将读取图像并将颜色序列从 BGR 转换为 RGB。
然后,我们将构建一个包含图像变换信息的变换矩阵。为简单起见,我们可以将该矩阵视为:
[[size, rotation, location], ←x-axis
[rotation, size, location]] ←y-axis
默认矩阵或不会改变任何东西的矩阵是:
[[1, 0, 0]
[0, 1, 0]]
这意味着,100%的大小,零旋转,位置不变——再一次,我过于简化了。
在我们算出矩阵后,我们可以用。形状和用途。warpAffine 来转换图像。
让我们检查一个没有任何转换的例子,以了解这个过程。
# read img and convert color
img = cv2.imread('img3.jpeg')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)# build a transformation matrix
matrix = [[1, 0, 0], #x
[0, 1, 0]] #y
t = np.float32(matrix)# get the sizes
h, w = img.shape[:2]# transform
img = cv2.warpAffine(img, t, (w, h))# plot
fig, ax = plt.subplots(1, figsize=(12,8))
ax.axis('off')
plt.imshow(img)
好吧,我们可以试着移动它。我们会用不同的矩阵重复所有的事情。
# Change location
matrix = [[1, 0, **300**], #x
[0, 1, **-300**]] #y
移动图像。
现在,让我们改变一切,看看它是如何工作的。
# Change size, rotation, and location
matrix = [[0.5, 0.3, 450], #x
[-0.3, 0.5, 600]] #y
移动、旋转和缩放图像。
有意思。你可能已经注意到我们有两种不同的“尺寸”——让我们来看看它们有什么不同。
我们可以在矩阵中将宽度缩放 0.5。
matrix = [[**0.5**, 0, 0], #x
[0, 1, 0]] #y
按 0.5 缩放的宽度。
我们可以把宽度除以 2 英寸。
img = cv2.warpAffine(img, T, (**int(w/2)**, h))
宽度除以 2。
我想理解转换矩阵是必要的,但是它有点过于复杂了。
旋转
OpenCV 有一些更方便的方法来构建它,比如 getRotationMatrix2D 方法,我们可以在一个方法中设置所需的图像位置、旋转角度和缩放因子。
img = cv2.imread('Images/3.jpg')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)h, w = img.shape[:2]# (w/2, h/2) -> center of the image **T = cv2.getRotationMatrix2D((w/2, h/2), 90, .5)**img = cv2.warpAffine(img, T, (w, h))fig, ax = plt.subplots(1, figsize=(12,8))
ax.axis('off')
plt.imshow(img)
移动、旋转和缩放图像。
如果我们只需要将整个画面旋转 90 度。trasponse 是一种更简便的方法。
img = cv2.imread('Images/3.jpg')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)img = cv2.transpose(img)fig, ax = plt.subplots(1, figsize=(12,8))
ax.axis('off')
plt.imshow(img)
转置图像。
镜子
我们可以创造一种镜像效果,比如倒影。我们只需要。随着图像和我们想要反映的方向翻转。
img = cv2.imread('Images/2.jpeg')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)fig, ax = plt.subplots(1, figsize=(12,12))ax = plt.subplot(221)
plt.imshow(img)img2 = cv2.flip(img, 1)
ax = plt.subplot(222)
plt.imshow(img2)img3 = cv2.flip(img, 0)
ax = plt.subplot(223)
plt.imshow(img3)img4 = cv2.flip(img, -1)
ax = plt.subplot(224)
plt.imshow(img4)
原始图像和镜像图像。图片由赖爷 Subiyanto
种植
很多时候,我们可能需要图像的特定部分,而不是整个图像。如果是这种情况,我们可以通过分割 NumPy 数组轻松地裁剪图片。
切片的第一部分与高度有关,所以如果我们把它看作一个表格,它就像行号,第二部分指的是宽度,所以它就像列号。
img = cv2.imread('Images/2.jpeg')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)fig, ax = plt.subplots(1, figsize=(12,8))
ax.axis('off')# [height start: height end, width start: width end]
# [first row : last row, first column: last column]
plt.imshow(img**[500:1000, 250:500]**)
裁剪的图像。
猜测我们希望从哪个像素开始和结束裁剪并不是最佳选择,所以让我们构建一个简单的缩放函数来理解我们如何系统地完成这项工作。
# scale function
**def px(size, proportion):
return int(size * proportion)**# read, convert and get height/ width
img = cv2.imread('Images/2.jpeg')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
h, w = img.shape[:2]# crop
img = img[**px(h, .25):px(h, .40)**, #25% to 40%
**px(w, .10):px(w, .35)**] #10% to 35%# plot
fig, ax = plt.subplots(1, figsize=(12,8))
ax.axis('off')
plt.imshow(img)
用函数裁剪
调整大小
默认情况下,调整大小是通过线性插值实现的,但是我们也可以尝试其他方法来看看它们之间的区别。
让我们尝试使用。resize。我们需要图像和所需的大小,在这里我们可以指定我们想要的插值方法。尺寸可以是新照片的尺寸,也可以是 x 和 y 的比例因子。
# read, convert and get height/ width
img = cv2.imread('Images/2.jpeg')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
h, w = img.shape[:2]# crop
img = img[px(h, .33):px(h, .35),
px(w, .18):px(w, .25)]# figure
fig, ax = plt.subplots(1, figsize=(12,16))# resize - nearest
ax = plt.subplot(311) **img_scaled = cv2.resize(img, None, fx=3, fy=3, interpolation = cv2.INTER_NEAREST)**
plt.imshow(img_scaled)
plt.title('nearest')# resize - default/ linear
ax = plt.subplot(312)
**img_scaled = cv2.resize(img, None, fx=3, fy=3)** plt.imshow(img_scaled)
plt.title('default/ linear')# resize - cubic
ax = plt.subplot(313)
**img_scaled = cv2.resize(img, None, fx=3, fy=3, interpolation = cv2.INTER_CUBIC)** plt.imshow(img_scaled)
plt.title('cubic')
不同的插值。
另一种调整大小的方法是使用金字塔。让我们快速看一下这是如何工作的。
# read and convert
img = cv2.imread('Images/ufo.jpg')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)# crop
img = img[50:150, 200:400]# figure
fig, ax = plt.subplots(1, figsize=(12,12))# img
ax = plt.subplot(311)
plt.imshow(img)
plt.title('regular size')# downsize
**small = cv2.pyrDown(img)**ax = plt.subplot(312)
plt.imshow(small)
plt.title('smaller')# upsize
**regular = cv2.pyrUp(small)**ax = plt.subplot(313)
plt.imshow(regular)
plt.title('regular again')
金字塔法— 原图来自维基百科
我们可以看到,就像其他方法一样,分辨率有一些损失,但总的来说这是一个很好的方法。
好的,在我们结束前再来一个。
对我们的数组进行切片不仅对裁剪有用,我们还可以用它来操作图像的特定部分,甚至用切片来替换不同的部分。
# read and convert
img = cv2.imread('Images/ufo.jpg')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)img2 = cv2.imread('Images/8.jpg')
img2 = cv2.cvtColor(img2, cv2.COLOR_BGR2RGB)# replace
img2[200:250, 1000:1100] = small# plot
fig, ax = plt.subplots(1, figsize=(12,12))
plt.imshow(img2)
图片由马克·阿斯特霍夫(没有飞碟)
好了,我们看到了如何移动、旋转和调整图像大小。我们还了解了裁剪、镜像以及如何处理图片的切片。
感谢阅读我的文章。我希望你喜欢它。
参考资料:
OpenCV 几何图像变换;
OpenCV 对数组的操作;
OpenCV 转置示例;
OpenCV 金字塔;
变形金刚模型…这一切是如何开始的?
Transformer 模型彻底改变了自然语言处理领域,但是,这一切是如何开始的呢?为了理解当前最先进的架构,并真正理解为什么这些模型成为该领域的突破,我们必须在时间上走得更远,因为我们知道 NLP 是从哪里开始的:当我们第一次在 NLP 中引入神经网络时。
将神经模型引入 NLP 找到了克服传统方法无法解决的挑战的方法。最显著的进步之一是序列对序列模型:这种模型通过一次预测一个单词来生成输出序列。序列到序列模型对源文本进行编码,以减少歧义并实现上下文感知。
序列对序列模型(图片由作者提供)
在任何语言任务中,语境都起着至关重要的作用。为了理解单词的意思,我们必须了解一些使用它们的情况。Seq2Seq 模型通过查看标记级别来实现上下文:上一个单词/句子生成下一个单词/句子。引入这种嵌入在空间中的上下文表示具有多个优点,例如避免了由于相似的上下文数据被映射为彼此靠近而导致的数据稀疏,并且提供了生成合成数据的方式。
然而,语言中的语境是非常复杂的。很多时候,只关注前一句话是找不到上下文的。需要长程相关性来实现上下文感知。Seq2Seq 模型与递归神经网络(LSTM 或格鲁)一起工作。这些网络具有记忆机制,在处理序列时调节信息流,以实现“长期记忆”尽管如此,如果一个序列足够长,他们将很难将信息从较早的时间步骤传递到后面的时间步骤。
当试图处理文本的整个段落时,RNNs 将达不到要求。他们受到梯度消失问题的困扰。梯度值用于更新神经网络的权重,从而进行学习。当梯度随着时间反向传播而收缩时,就会出现消失梯度问题。如果一个梯度值变得极小,对学习贡献不大。此外,RNNs 的拓扑结构非常耗时,因为对于每个反向传播步骤,网络都需要看到整个单词序列。
CNN 对数路径(图片由作者提供)
作为尝试解决这些问题的一种方法,在 NLP 中引入了卷积神经网络的使用。使用卷积创建对数路径。网络可以在对数卷积层中“观察”整个序列。然而,这提出了一个新的挑战:位置偏差。我们如何确保我们在文本中观察到的立场是那些给出更多见解的立场?为什么关注序列的位置 X 而不是 X-1?
此外,挑战不仅在于找到一种编码大量文本序列的方法,还在于能够确定文本的哪些部分对于获得上下文感知是必不可少的。并非所有的文本对理解都同样重要。为了解决这个问题,Seq2Seq 模型中引入了注意机制。
注意力机制受到动物视觉注意力的启发,它们专注于视觉输入的特定部分来计算适当的反应。Seq2Seq 架构中使用的注意力寻求向解码器提供更多上下文信息。在每一个解码步骤中,解码器都被告知应该对每个输入单词给予多少“关注”。
Seq2Seq 车型中的关注度(图片由作者提供)
尽管环境意识有所提高,但仍有很大的提高空间。这些方法的最大缺点是这些体系结构的复杂性。
这就是 transformer 模型出现的原因。transformer 模型引入了这样一种思想,即不在已经很复杂的 Seq2Seq 模型上添加另一种复杂的机制(attention );我们可以通过忘记其他一切,只专注于注意力来简化解决方案。
这个模型消除了递归,它只使用矩阵乘法。它一次处理所有输入,而不必按顺序处理。为了避免失去顺序,它使用位置嵌入来提供每个元素在序列中的位置信息。尽管消除了递归,但它仍然提供了一种编码器-解码器架构,如 Seq2Seq 模型中所见
因此,在看到我们在以前的模型中面临的所有挑战之后,让我们深入研究 transformer 模型与 Seq2Seq 模型相比解决了什么问题。
变压器技术深度探讨
变压器架构(图片由作者提供)
当我们需要处理整个段落以获得上下文时,RNN 有所欠缺,而变形金刚能够识别长期依赖关系,实现上下文感知。我们还看到,RNN 自己努力确定文本的哪些部分给出了更多的信息,为此他们需要增加一个额外的层,一个双向 RNN 来实现注意力机制。相反,转换器只有在全神贯注的情况下才能工作,这样它才能在不同的层次上确定上下文的基本部分
另一个关键的区别是 transformer 模型消除了递归。通过消除递归,减少了顺序运算的次数,降低了计算复杂度。在 RNNs 中,对于每个反向传播步骤,网络需要看到整个单词序列。在变换器中,所有输入被同时处理,降低了计算复杂度。这也带来了一个新的优势,我们现在可以并行训练。能够将训练样本分割成几个独立处理的任务可以提高训练效率。
那么模型如何在不使用递归的情况下保持序列顺序呢?
使用位置嵌入。该模型采用 n 个单词嵌入的序列。为了模拟位置信息,将位置嵌入添加到每个单词嵌入中。
位置嵌入(作者图片)
使用不同维数的正弦和余弦函数创建位置嵌入。用由这些函数的组合产生的模式对单词进行编码;这导致序列中位置的连续二进制编码。
transformer 模型使用多头注意力对输入嵌入进行编码,这样做时,它会以向前和向后的方式处理输入,因此序列中的顺序会丢失。因此,它依赖于我们刚刚解释过的位置嵌入。
转换器有三种不同的注意机制:编码器注意、编码器-解码器注意和解码器注意。那么注意力机制是如何工作的呢?它基本上是一个向量乘法,根据向量的角度,可以确定每个值的重要性。如果向量的角度接近 90 度,那么点积将接近零,但是如果向量指向相同的方向,那么点积将返回更大的值。
每个键都有一个相关的值,对于每个新的输入向量,我们可以确定这个向量与值向量的关系,并使用 softmax 函数选择最接近的项。
多头关注(图片由作者提供)
变形金刚有多头注意力;我们可以把它想象成 CNN 的过滤器,每一个都学会注意一组特定的单词。一个人可以学会识别短期依赖性,而另一个人可以学会识别长期依赖性。这提高了上下文意识,当不清楚时,我们可以理解术语指的是什么;比如用代词之类的词。
transformer 架构有助于创建基于海量数据集的强大模型。即使不是每个人都可以训练这些模型。我们现在可以利用迁移学习来使用这些预先训练的语言模式,并根据我们的具体任务对它们进行微调。
变形金刚模型彻底改变了这个领域。他们已经在许多任务中超越了基于 RNN 的架构,并将继续在 NLP 领域产生巨大的影响。
变压器神经网络:一步一步的野兽崩溃
变压器神经网络是一种新颖的架构,旨在解决序列到序列的任务,同时轻松处理长距离依赖性。在论文2017【1】中提出“注意力是你所需要的一切”。这是自然语言处理领域目前最先进的技术。
在直接跳到 Transformer 之前,我会花一些时间来解释我们使用它的原因以及它是从哪里出现的。(如果您想跳过这一部分,直接进入 Transformer 主题,但我建议您按顺序阅读,以便更好地理解)。
所以,故事从 RNN(递归神经网络)开始。
RNN
什么是 RNN?和简单的 ANN 有什么区别?主要区别是什么?
rnn 是随着时间推移推出的前馈神经网络。
来源:科拉的 (CC0)。
与普通的神经网络不同,RNNs 被设计成接受一系列输入**而没有预先确定的大小限制*。“系列”作为该序列的任何输入,与它们相邻的输入有某种关系或对它们有某种影响。***
RNN 建筑。来源:科拉赫的 (CC0)。
基本的前馈网络也能“记住”东西,但它们记住的是在训练期间学到的东西。此外,虽然 rnn 在训练时学习类似,但是它们在生成输出时记住从先前输入中学到的东西。
图示长期依赖关系的图像。来源:科拉赫的 (CC0)。
它被用于不同类型的模型中-
1.)向量-序列模型- 它们以固定大小的向量作为任意长度的输入输出向量,例如在图像字幕中,图像作为输入给出,输出描述图像。
2.)序列-向量模型- 取任意大小的向量,输出固定大小的向量。电影的情感分析将对任何电影的评论评定为正面或负面,作为固定大小的向量。
*3.)**序列对序列模型——*最流行、使用最多的变体,把输入作为一个序列,把输出作为另一个序列,变量大小。例如,语言翻译,用于股票市场预测的时间序列数据。
其缺点-
- 训练缓慢。
- 长序列导致梯度消失,或者说,长期依赖性的问题。简单来说,在回忆旧联系的时候,它的记忆力并没有那么强。
对于 Eg 。“云在 ____ 里。”
很明显,下一个词将是天空,因为它与云联系在一起。这里我们可以看到云和预测词之间的距离更小,所以 RNN 可以很容易地预测它。
但是,再举一个例子,
“我和我的父母在德国长大,我在那里生活了很多年,对他们的文化有一定的了解,这就是为什么我能说一口流利的 ____ 语。”
这里预测的单词是德语,它与德国直接相连,但在这种情况下,德国与预测的单词之间的距离更大,因此 RNN 很难预测。
因此,不幸的是,随着这种差距的扩大,rnn 变得无法连接,因为它们的记忆随着距离而消失。
LSTM
来源:科拉赫的 (CC0)。
长短期记忆- 特殊种类的 RNN,专为解决消失梯度问题而制作。他们有能力学习长期的依赖性。长时间记住信息实际上是他们的默认行为,而不是他们努力学习的东西!
这个分支允许传递信息并跳过对单元的长时间处理。来源:科拉赫的 (CC0)。
LSTM 神经元与正常神经元不同,它有一个分支,允许传递信息并跳过当前细胞的长时间处理,这允许记忆保留更长的时间。它确实改善了消失梯度问题的情况,但并不令人惊讶,就像它在 100 个单词之前会很好,但在 1000 个单词之后,它开始失去控制。
但是像简单的 RNN 一样,它的训练速度也很慢,甚至更慢。
它们一个接一个地按顺序接受输入,这不能很好地用尽 GPU,GPU 是为并行计算而设计的。
如何并行化顺序数据?? (这个问题我会回去的。)
目前,我们正在处理两个问题-
- 消失梯度
- 慢速训练
解决渐变消失问题:
注意
它回答了我们应该关注输入的哪一部分的问题。
我将用一种稍微不同的方式来解释注意力。让我们来看一个例子-
假设有人给了我们一本机器学习的书,让我们给出分类交叉熵的信息。有两种做法,一是通读全书,带着答案回来。第二,转到索引,找到“损失”一章,转到交叉熵部分,阅读分类交叉熵部分。
你认为更快的方法是什么?
像第一种方法一样,可能要花整整一周的时间来读完整本书。而在第二档,几乎不需要 5 分钟。此外,我们从第一种方法得到的信息将更加模糊和多样,因为它基于太多的信息,而从第二种方法得到的信息将精确到需求。
我们在这里做了哪些不同的事情?
在前一种情况下,我们没有特别关注这本书的任何部分,而在后一种情况下,我们将注意力集中在损失一章,然后进一步将注意力集中在交叉熵部分,在那里解释了分类交叉熵的概念。其实这是我们人类大多数人都会做的事情。
神经网络中的注意力有点类似于我们在人类中发现的。他们关注输入的某些部分的高分辨率,而输入的其余部分是低分辨率的[2]。
假设我们正在制造一台 NMT(神经机器翻译机),
请看这个动画,它展示了一个简单的序列到序列模型是如何工作的。
经典序列到序列模型的工作原理。来源:贾勒马的 (CC BY-NC-SA 4.0)。
我们看到,对于编码器或解码器的每一步,RNN 都在处理其输入,并生成该时间步的输出。在每一个时间步中,RNN 根据它看到的输入和先前的输出更新它的隐藏状态。在动画中,我们看到隐藏状态实际上是我们传递给解码器的上下文向量。
“注意”的时间。
对于这些类型的模型,上下文向量被证明是有问题的。模型在处理长句时有一个问题。或者说他们正面临长句中消失梯度的问题。因此,在一篇论文[2]中出现了一个解决方案,注意力被引入。它极大地提高了机器翻译的质量,因为它允许模型根据需要专注于输入序列的相关部分。
注意使用序列对序列模型。来源:贾勒马的 (CC BY-NC-SA 4.0)。
这个注意力模型在两个方面不同于经典的序列对序列模型
- 与简单的 seq-to-seq 模型相比,编码器向解码器传递更多的数据。以前,只有编码部分的最后的隐藏状态被发送到解码器,但是现在编码器将所有的隐藏状态(甚至是中间状态)传递到解码器。
- 解码器部分在产生其输出之前进行额外的步骤。解释如下-
解码器最后一步如下进行
- 它检查它接收到的每个隐藏状态,因为编码器的每个隐藏状态大多与输入句子的特定单词相关联。
- 我给每个隐藏的州打分。
- 然后每个分数乘以各自的 softmax 分数,从而放大高分的隐藏状态,淹没低分的隐藏状态。(请参考下图以获得清晰的视觉效果。)**
来源:贾勒马的 (CC BY-NC-SA 4.0)。
这种评分练习在解码器端的每个时间步进行。
现在当我们把所有的东西放在一起:
- 注意力解码器层采用令牌的嵌入和初始解码器隐藏状态,RNN 处理其输入并产生输出和新的隐藏状态向量(h4)。
- 现在我们使用编码器隐藏状态和 h4 向量来计算这个时间步长的上下文向量 C4。这就是注意力概念被应用的地方,这就是为什么它被称为注意力步骤。
- 我们在一个向量中连接(h4)和 C4。
- 现在,这个向量被传递到一个前馈神经网络,前馈神经网络的输出指示这个时间步长的输出字。
- 这些步骤将在下一个时间步骤中重复。(请参考下面的幻灯片以获得清晰的视觉效果。)**
最后一步。来源:贾勒马的 (CC BY-NC-SA 4.0)。
所以,这就是注意力的工作方式。
例如,在图像字幕问题中的注意力工作:-
图像字幕问题中的注意加工。资料来源:百货公司 (CC0)。
现在,记住我之前提出的问题-
如何将 顺序数据并行化??
所以,我们的弹药来了——
变形金刚
2017 年发表的一篇名为“注意力是你所需要的一切”的论文进入了画面,它介绍了一种基于注意力层的编码器-解码器架构,称为变压器。
一个主要的区别是输入序列可以并行传递,这样可以有效地利用 GPU,也可以提高训练的速度。并且它基于多头注意层,消失梯度问题也被大幅度克服。本文是基于 transformer 在 NMT 上的应用。
所以,我们之前强调的两个问题在这里都得到了一定程度的解决。
例如,在由简单的 RNN 组成的翻译器中,我们以连续的方式输入序列或句子,一次一个单词,以生成单词嵌入。由于每一个单词都依赖于前一个单词,其隐藏状态也随之动作,所以需要一步一个脚印。而在 transformer 中则不是这样的,我们可以同时传递一个句子的所有单词,同时确定单词嵌入。所以,它实际上是如何工作的,让我们看看未来-
架构:-
来源:arXiv:1706.03762【cs。CL】。
1。编码器模块-
来源:arXiv:1706.03762【cs。CL】。
这是一个事实,计算机不理解单词,它对数字,向量或矩阵起作用。所以,我们需要把我们的单词转换成一个向量。但是这怎么可能。于是,嵌入空间的概念就来了。它就像一个开放的空间或字典,意思相似的单词被组合在一起,或者在该空间中彼此靠近。这个空间被称为嵌入空间,在这里,每个单词,根据其含义,被映射并被赋予特定的值。所以,这里我们把单词转换成向量。
来源:arXiv:1706.03762【cs。CL】。
但我们将面临的另一个问题是,不同句子中的每个单词都有不同的含义。因此,为了解决这个问题,我们借助了位置编码器。它是一个向量,根据单词在句子中的位置给出上下文。
词→嵌入→位置嵌入→最终向量,称为上下文。
我们的输入已经准备好,现在进入编码器模块。
多头注意力部分-
来源:arXiv:1706.03762【cs。CL】。
现在,变形金刚的主要本质来了,“自我关注”。
它关注的是一个特定的单词与句子中其他单词的相关程度。它被表示为一个注意力向量。对于每个单词,我们可以生成一个注意力向量,该向量捕获该句子中单词之间的上下文关系。
来源:百货公司 (CC0)。
它所面临的唯一问题是,对于每一个单词,它在句子中的自身价值要高得多,尽管我们倾向于它与句子中其他单词的相互作用。因此,我们确定每个单词的多个注意力向量,并进行加权平均,以计算每个单词的最终注意力向量。
资料来源:百货公司 (CC0)。
由于我们使用了多个注意力向量,它被称为多头注意力块。
前馈网络-
来源:arXiv:1706.03762【cs。CL】。
现在,第二步是前馈神经网络。这是应用于每个注意力向量的简单前馈神经网络,其主要目的是将注意力向量转换为下一个编码器或解码器层可接受的形式。
来源:arXiv:1706.03762【cs。CL】。
前馈网络“一次一个”地接受注意力向量。这里最棒的是不像 RNN 的情况,这里每个注意力向量都是彼此独立的。因此,并行化可以在这里应用,这就造成了所有的差异。
编码器的输出。来源:arXiv:1706.03762【cs。CL】。
现在,我们可以将所有单词同时传递到编码器模块,并同时获得每个单词的编码向量集。
2。解码器模块-
来源:arXiv:1706.03762【cs。CL】。
现在,就像我们正在训练一个从英语到法语的翻译,所以为了训练,我们需要给出一个英语句子和它翻译的法语句子,让模型学习。所以,我们的英语句子通过编码器模块,法语句子通过解码器模块。
来源:arXiv:1706.03762【cs。CL】。
首先,我们有嵌入层和位置编码器部分,它将单词转换为各自的向量,这与我们在编码器部分看到的类似。
蒙面多头注意力部分-
来源:arXiv:1706.03762【cs。CL】。
现在它将通过自我注意块,在这里为法语句子中的每个单词生成注意向量,以表示每个单词与同一个句子中的每个单词的相关程度。(就像我们在编码器部分看到的一样)。
但这个区块被称为掩蔽多头注意力区块,我将简单解释一下——
为此,我们需要知道学习机制是如何工作的。首先,我们给出一个英语单词,它将使用之前的结果翻译成法语版本,然后它将与实际的法语翻译(我们输入到解码器模块中)进行匹配和比较。在比较两者之后,它将更新它的矩阵值。这就是它在几次迭代后的学习方式。
我们观察到的是,我们需要隐藏下一个法语单词,这样一开始它会使用以前的结果来预测下一个单词本身,而不知道真正的翻译单词。如果它已经知道下一个法语单词,学习就没有意义了。因此,我们需要隐藏(掩盖)它。
这是一个英法翻译的例子。资料来源:百货公司 (CC0)。
我们可以从英语句子中取任意一个单词,但只能取法语句子的前一个单词来学习。因此,在执行矩阵运算的并行化时,我们确保矩阵应该屏蔽稍后出现的单词,将它们转换为 0,这样注意力网络就不能使用它们。
来源:arXiv:1706.03762【cs。CL】。
现在,来自前一层的结果注意力向量和来自编码器块的向量被传递到另一个多头注意力块。(这部分也是编码器模块的结果进入图像的地方。在该图中还可以清楚地看到,来自编码器模块的结果将传到这里。)。这就是为什么它被称为编解码器关注块的原因。
因为对于每个英语和法语句子,每个单词都有一个向量。这个模块实际上完成了英语和法语单词的映射,并找出了它们之间的关系。所以,这是主要的英语到法语单词映射发生的部分。
这个模块的输出是英语和法语句子中每个单词的注意力向量。每一个向量代表两种语言中与其他词的关系。
来源:arXiv:1706.03762【cs。CL】。
现在,我们将每个注意力向量传递到一个前馈单元,它将使输出向量形成易于被另一个解码器模块或线性层接受的形式。
线性层是另一个前馈层。它用于在翻译后将维度扩展为法语中的单词数。
现在,它通过 Softmax 层,将输入转换为人类可以理解的概率分布。
并且在翻译之后以最高的概率产生结果单词。
下面是谷歌的人工智能博客【6】中举例说明的例子,我把它放在这里供你参考。
概述-变压器网络的工作。来源:谷歌 AI (CC0)。
转换器从为每个单词生成初始表示或嵌入开始。这些由空心圆圈表示。然后,使用自我关注,它从所有其他单词中聚合信息,根据整个上下文信息为每个单词生成一个新的表示,用实心球表示。然后对所有单词并行重复该步骤多次,连续生成新的表示。
解码器的操作类似,但从左到右一次生成一个字。它不仅关注其他先前生成的单词,还关注由编码器生成的最终表示。
这就是变压器的工作原理,它现在是 NLP 中最先进的技术。它使用了一种 自关注机制 并解决了并行化问题,产生了非常好的结果。甚至谷歌也使用 BERT 来为普通的自然语言处理应用程序预先训练模型。
参考文献-
- 注意力是你所需要的一切;Ashish Vaswani,Noam Shazeer,Niki Parmar,Jakob Uszkoreit,Llion Jones,Aidan N. Gomez,Lukasz Kaiser,Illia Polosukhin。
- 基于注意力的神经机器翻译的有效方法;放大图片作者:Christopher D .
- 科拉的博客。
- 杰伊·阿拉玛的博客。
- 百货商店:Youtube。
Transformer-XL 回顾:超越定长上下文
“Transformer-XL:超越定长语境的注意力语言模型”述评
这篇论文(“Transformer-XL:固定长度上下文之外的注意力语言模型”)发表在顶级 NLP 会议之一的 ACL 2019 上,作者是谷歌 AI 的研究人员。它提出了 Transformer-XL,这是一种新的架构,能够在不破坏时间一致性的情况下,超越固定长度的上下文理解自然语言。它的主要创新是段级递归机制和新颖的位置编码方案。与传统的 Transformer 模型不同,它可以捕获长期的依赖关系,并解决上下文碎片问题,这是 vanilla Transformer 的主要局限性。实验表明,Transformer-XL 学习依赖的时间比 RNNs 和 vanilla Transformer 长得多。Transformer-XL 还在大型基准数据集的评估中取得了一流的结果。
论文链接:https://www.aclweb.org/anthology/P19-1285.pdf
1.背景
语言建模是自然语言处理中的一个重要课题。人们提出了许多像 BERT 和 ELMo 这样的无监督预训练方法。然而,建模长期依赖仍然是一个挑战。递归神经网络(RNNs),尤其是长短期记忆网络(LSTM)已经成为建模长期依赖的标准解决方案。在 LSTMs 中引入门控和梯度削波技术提高了对长期依赖性建模的能力,但不足以解决这一挑战。此外,由于梯度消失和爆炸,很难优化用于建模长期依赖性的 RNNs。
图一。线段长度为 4 的普通变形金刚。来源:【变压器-XL】【戴等,2019】
转换器被提出来解决这个问题,它允许单词对之间的直接连接,并且比 LSTMs 更好地捕捉长期依赖性。作者定义了原始变形金刚和香草变形金刚。然而,转换器是用固定长度的上下文实现的。它将输入分割成段,并在每个段内进行训练(图 1)。因此,转换器无法捕获超过预定义上下文长度的长期依赖关系。并且固定长度的片段不考虑句子边界,导致上下文碎片,从而导致低效的优化和性能损失。在评估过程中,它通过在每一步中将输入移动一个位置来一次在一个位置进行一次预测,其中数据段从头开始处理。所以评估程序是昂贵的。
为了解决这些限制,作者提出了 Transformer-XL。它重用先前片段中的隐藏状态来支持长期依赖并解决上下文碎片。并且它采用相对位置编码方案来避免时间混淆。
2.变压器-XL
图二。线段长度为 4 的 Transformer-XL 型号。来源: Transformer-XL 【戴等,2019】
2.1 分段级重现
在训练期间,为前一段计算的隐藏状态序列是固定的,并被缓存以作为扩展上下文重用(图 2)。在每个段中,每个隐藏层接收前一个隐藏层的输出和前一个段的输出。它通过使用来自几个先前片段的上下文信息来增加最大可能的依赖性。尽管解决了上下文碎片问题,但是这种片段级递归机制提高了评估速度,因为它可以前进整个长片段,并且使用来自先前片段的表示而无需重新计算。
2.2 相对位置编码
天真地应用递归引入了另一个技术挑战。也就是说,位置信息是不一致的,并且来自不同片段的标记具有相同的位置编码,这被称为时间混淆。为了应对这一挑战,Transformer-XL 采用了新的相对位置编码。位置信息偏差被编码在隐藏状态中,这不同于在初始嵌入中结合偏差的其他方法。使用带有可学习转换的固定嵌入使得它更直观,并且更可推广到更长的序列。相对位置编码使得段级递归成为可能,因此 Transformer-XL 可以比普通的 Transformer 模型建立更长期的依赖关系。
3.实验和结果
作者将 Transformer-XL 应用于单词级和字符级数据集,包括 WikiText-103、text8、enwik8、十亿单词和 Penn Treebank,并将其与其他模型进行比较。
表 1:WikiText-103 的结果。来源:【变压器-XL】【戴等,2019】
在 WikiText-103 数据集上,Transformer-XL 达到了 18.3 的困惑度,相比之下,之前最先进的(SoTA)结果(Baevski & Auli)达到了 20.5 的困惑度(表 1)。
表 2:环境观察 8 的结果。来源:【变压器-XL】T5【戴等,2019】
在 enwik8 数据集上,12 层 Transformer-XL 实现了 1.06 比特每字符(bpc),这与 Al-Rfou 等人之前的 SoTA 结果相似…24 层 Transformer-XL 将 SoTA bpc 从 1.06 提高到 0.99(表 2)。
表 3:文本 8 的结果。来源: Transformer-XL 【戴等,2019】
在 enwik8 上使用相同的超参数时,Transformer-XL 将 SoTA bpc 从 1.13 降至 1.08(表 3)。
表 4:十亿字的结果。来源:【变压器-XL】【戴等,2019】
十亿字数据集只有短期依赖性,但 Transformer-XL 也实现了新的 SoTA 结果,将 SoTA 困惑度从 23.7 降低到 21.8(表 4)。
表 5:宾夕法尼亚树库的结果。来源:【变压器-XL】T5【戴等,2019】
在只有 100 万训练令牌的单词级 Penn Treebank 数据集上,与没有两步微调的其他模型相比,Transformer-XL 将 SoTA 困惑度从 55.3 提高到 54.52(表 5)。这表明 Transformer-XL 可以很好地在小数据集上推广。
表 6:相对有效上下文长度比较。来源: Transformer-XL 【戴等,2019】
作者提出了一个新的度量,相对有效上下文长度(RECL),它定义在一个模型组上,长上下文的增益通过相对于最佳短上下文模型的相对改进来衡量。RECL 中的参数 r 限制了 top- r 硬示例上的比较。如表 6 所示,Transformer-XL 可以模拟比 RNN 长 80%到 133%的依赖性,比普通 Transformer 长 291%到 447%的依赖性。它表明段级递归和相对位置编码都有助于 Transformer-XL 的较长 RECL。在 WikiText-103 和十亿字数据集上的消融研究也表明,Transformer-XL 优于其他模型,因为它可以对递归和新编码的长期依赖性进行建模。
此外,由于不需要重新计算,Transformer-XL 在评估过程中比普通变压器快 1874 倍。
4.结论
Transformer-XL 在多个数据集上获得新的 SoTA 困惑或 bpc 结果。结合递归和相对位置编码,它可以模拟比 RNNs 和普通变压器更长期的依赖性,并在评估过程中大大降低计算成本。Transformer-XL 在其他领域也很有效,比如生成长文章和改进语言模型预处理方法,比如 BERT 和 ALBERT。
5.相关著作
你所需要的只是注意力
本文提出了 Transformer,这是一种完全依赖于注意力机制来模拟输入和输出之间的全局依赖关系的新型模型架构。Transformer 模型允许更多的并行化,因此需要更少的培训时间。Transformer 在 WMT 2014 年英语到法语翻译任务上取得了新的 SoTA 结果。原始变压器是本文介绍的 Transformer-XL 的基础。
引文:Ashish Vaswani、Noam Shazeer、Niki Parmar、Jakob Uszkoreit、Llion Jones、Aidan Gomez、ukasz Kaiser 和 Illia Polosukhin。2017.你需要的只是关注。在神经信息处理系统进展中,第 5998–6008 页。
(2)具有更深自我关注的字符级语言建模
本文提出了一种用于角色级建模的深度、非递归的 transformer 模型。具有因果注意的变压器自我注意层用于处理固定长度的输入和预测即将出现的字符。Al-Rfou 等人设计了三个辅助损耗来训练深度变压器网络,其性能优于 LSTMs,并在 text8 和 enwik8 数据集上获得了新的 SoTA 结果。但是,它使用固定长度的数据段,因此它无法捕获任何超过预定义上下文长度的长期依赖关系。这一限制促使作者设计 trans former-XL 来模拟长期依赖性。
引文:Rami Al-Rfou,Dokook Choe,Noah Constant,Mandy Guo 和 Llion Jones。2018.具有更深自我关注的字符级语言建模。在AAAI 人工智能会议记录,第 3159-3166 页。
(3) Bert:用于语言理解的深度双向转换器的预训练
介绍了一种新的语言表示模型——来自变压器的双向编码器表示(BERT)。它被设计成用未标记的文本预先训练双向语言表示。然后,预训练的 BERT 可以通过一个额外的输出层微调到各种任务,并实现 SoTA 结果。实际上,BERT 只是将长文本分成固定长度的较短片段,导致了上下文碎片问题。Transformer-XL 解决了上下文碎片问题,因此它可以用于改进 BERT,然后在不同类型的任务中获得新的 SoTA 结果。
引文:雅各布·德夫林、张明蔚、肯顿·李和克里斯蒂娜·图塔诺瓦。2019.Bert:用于语言理解的深度双向转换器的预训练。在计算语言学协会北美分会 2019 年会议论文集:人类语言技术,第 4171–4186 页。
(4)具有相对位置表征的自我注意
本文提出了一种将序列元素之间的相对位置表示或距离结合到变压器的自注意机制中的方法。与绝对位置表示相比,相对位置编码可以提高 WMT 2014 英德数据集上的翻译质量。它启发了 Transformer-XL 的作者们去衍生一种新形式的相对位置编码。Transformer-XL 的新的相对位置编码解决了时间混淆问题,并且在经验上具有更好的通用性。
引文:彼得·肖、雅各布·乌兹科雷特和阿希什·瓦斯瓦尼。2018.自我注意与相对位置表征。在计算语言学协会北美分会 2018 年会议记录:人类语言技术中,第 464-468 页。
(5) ALBERT:一个用于语言表达自我监督学习的 Lite BERT
本文提出了两种新的技术来减少 BERT 中的参数,有助于降低存储和训练时间的成本。它还引入了句子顺序预测的自我监督损失,重点是建立句子间的一致性模型。它用比 BERT-large 更少的参数在不同的基准数据集上建立新的 SoTA 结果。与 BERT 一样,它也将长文本分割成固定长度的片段,从而导致潜在的上下文碎片问题。Transformer-XL 可用于解决 ALBERT 中的上下文碎片问题,从而进一步提高其性能。
引文:兰,陈明达,萨巴斯蒂安古德曼,凯文金佩尔,皮尤什夏尔马,拉杜索里科特。2019.ALBERT:一个用于语言表达自我监督学习的 Lite BERT。在国际学术交流会议。
变形金刚(电影名)
或者我喜欢称之为类固醇引起的注意。💉💊
阿瑟尼·托古列夫在 Unsplash 上的照片
不,这篇文章不是关于美国科幻动作电影系列——这里没有擎天柱。它也与用于将能量从一个电路转移到另一个电路的电气设备无关。你会问,这是怎么回事?
这是有史以来最科幻的领域之一,人工智能——特别是自然语言处理,它在传递信息方面非常出色,并得到了很好的利用。(看我在那里做了什么。😛)
这篇文章基于一篇论文:注意力是你所需要的全部。附:作者选择这个标题并不是开玩笑,因为你需要所有的注意力来处理这个问题。但是不要让这吓到你,这是非常值得的!!
什么是变压器?
NLP 中的 Transformer 是一个新颖的架构,旨在解决序列到序列的任务,同时轻松处理长距离依赖。它完全依靠自我关注来计算其输入和输出的表示**,而不使用**序列对齐的 RNNs 或卷积。🤯
如果你还记得我以前的帖子,理解深度学习中的注意力,我们讨论了许多模型在处理长期依赖时如何以及为什么会失败。注意力的概念在某种程度上让我们克服了这个问题,现在在《变形金刚》中,我们将建立在注意力之上,并释放其全部潜力。
一点点关注怎么改变了 AI 游戏!
towardsdatascience.com](/attaining-attention-in-deep-learning-a712f93bdb1e)
潜入变形金刚之前需要知道的几件事
自我关注
让我们从重新审视 NLP 宇宙中的注意力开始。理解深度学习中的注意力。(我为这些明目张胆的自我广告道歉,但是认真读一读。在变形金刚下会对你帮助大很多。我保证。)
注意力让我们在预测我们的输出序列 时,专注于我们输入序列的部分。如果我们的模型预测单词“胭脂”(红色的法语翻译),我们很可能在我们的输入序列中找到单词“红色”的高权重年龄。所以,在某种程度上,注意力让我们在输入单词“ rouge ”和输出单词“ red ”之间建立了某种联系/关联。
自我注意,有时被称为内部注意,是一种与单个序列的不同位置相关的注意机制,以便计算该序列的表示。
简单来说, 自我关注帮助我们在同一个句子中建立相似的联系。 看下面这个例子:
“I poured water from the *bottle* into the ***cup*** until **it** was ***full***.”
it => cup“I poured water from the **bottle** into the cup until **it** was ***empty***.”
it=> bottle
通过改变一个词“满了”——>——空了”它的参照对象为变了。如果我们在翻译这样一个句子,我们就会想知道“这个词它指的是什么。
模型中可能存在的三种注意力:
- 编解码器注意 : 注意输入序列和输出序列之间的关系。
- 输入序列中的自我关注 : 关注输入序列中的所有单词。
- 输出序列中的自我注意: 这里我们要警惕的一点是,自我注意的范围仅限于给定单词之前出现的单词。这防止了模型训练期间的任何信息泄漏。这是通过屏蔽在每一步之后出现的单词来实现的。因此,对于步骤 1,只有输出序列的第一个字不被屏蔽,对于步骤 2,前两个字不被屏蔽,依此类推。
键、值和查询:
我刚才在这个标题中随机给你的三个词是向量,因为抽象对于计算自我注意力是有用的,下面会有更多的细节。这些通过将您的输入向量( X )乘以训练时学习的权重矩阵来计算。
- 查询向量:q=*X * Wq。*把这个当做现在的词。
- 关键向量*😗k=*X * Wk。*把这当成价值向量的索引机制。类似于哈希映射中的键值对,其中键用于唯一地索引值。
- 值向量: v = *X * Wv。*把这个当做输入单词中的信息。
我们想要做的是获取查询 q 并通过对 q 和 k 做点积来找到最相似的键 k 。最接近的查询关键字乘积将具有最高值,随后是 softmax,它将驱动具有接近 0 的较小值的 q.k 和具有较大值的 q.k 向 1 移动。这个 softmax 分布乘以v。乘以~1 的值向量将得到更多关注,而乘以~0 的值向量将得到较少关注。这些 q、k 和 v 向量的大小被各种实现称为“”。
这些值代表 q、k 和 I 的指数。
所有这些矩阵 Wq、Wk 和 Wv 都是在模型训练期间联合训练时学习到的。
从 q、k 和 v 计算自我注意力:
自我关注的公式。资料来源:论文。
如果我们为输入单词计算自我注意,
- 第一步: 将 qᵢ乘以 word 的 kⱼ关键向量。
- 第二步: 然后用这个乘积除以关键向量的维数的平方根。
完成此步骤是为了获得更好的梯度流量,这在前面步骤中点积值过大的情况下尤为重要。因为直接使用它们可能会将 softmax 推入梯度流量非常小的区域。 - 第三步: 一旦我们有了所有 j 的分数,我们就把这些通过一个 softmax。我们得到每个 j 的归一化值。
- 第四步: 用 v ᵢ 向量乘以每个 j 的 softmax 分数。
这里的想法/目的是,非常类似的注意,通过将它们乘以来自 softmax ~1 的高概率分数,仅保留我们想要关注的输入单词的值 v ,并且通过将它们驱向 0 来移除其余的值,即通过将它们乘以来自 softmax 的低概率分数~0 来使它们非常小。
计算第 I 个输入单词的自我注意输出。如果你正在寻找自我注意和注意之间的类比,考虑 z 服务于上下文向量的目的,而不是全局对齐权重。
变形金刚
⚠️ 我们将把这个可怕的野兽分解成小野兽,这一切都将变得有意义。(我承诺#2)
(左)变压器架构。资料来源:论文。(右图)为了更好地理解,对其进行了抽象。
Beast #1:编码器-解码器堆栈
编码器 :编码器将输入的符号表示序列 (x ₁ ,…,x ₙ ) 映射到表示序列 z = (z ₁ ,…,z ₙ ) 。把它们想象成经过某种后处理的自我关注的输出。
每个编码器有两个子层。
- 输入向量上的多头自我关注机制(想想并行化的和高效的自我关注的兄弟)。
- 一个简单的位置式全连接前馈网络(想想后处理)。
查看这张伯特中使用的编码器模块的绝对炸弹 3D 图。说真的,你不能错过这个!!!这就像一个全新层次的理解。
解码器 :给定 z ,解码器然后一次生成一个符号输出序列 (y ₁ ,…,y ₘ ) 。
每个解码器有三个子层。
- 一种 屏蔽 多头自关注机制对上一次迭代的输出向量。
- 编码器输出上的多头注意机制和解码器中的屏蔽多头注意。
- 一个简单的,位置式的全连接前馈网络(想想后处理)。
另外几点:
- 在原始论文中,编码器堆栈中有 6 层(2 个子层版本),解码器堆栈中有 6 层(3 个子层版本)。
- 模型中的所有子层以及嵌入层产生相同维度的输出。这样做是为了方便剩余的连接。
编码器-解码器堆栈中的 beast # 2—多头注意:
编码器和解码器中的三种注意以及前馈神经网络。
我们刚刚注意到,每个子层的输出需要具有相同的维度,在本文中为 512。zᵢ需要有 512 个维度。vᵢ需要有 512 个维度,因为 zᵢ只是 vᵢs.的加权和
此外,我们希望允许模型关注不同的位置,这是通过使用不同的 q、k 和 v 向量集多次计算自我关注度,然后取所有这些输出的平均值,以获得我们的最终 z 。
*因此,我们不是处理这些庞大的向量并平均多个输出,而是将我们的 k、q 和 v 向量的大小减少到某个更小的维度——同时减少 Wq、Wk 和 Wv 矩阵的大小。我们将 *k、q、 v、的多个集合( h )统称为一个注意力头,因此得名多头注意力。最后,我们将它们连接起来,而不是求平均值来得到最终的 z 。
级联向量的大小将会太大而不能馈送到下一个子层,所以我们通过将其乘以另一个学习矩阵 Wo 来缩小它。
(左)缩放的点积注意力。(右)多头注意力由几个并行运行的注意力层组成。资料来源:论文。
多个注意头允许模型在不同位置共同注意来自不同表示子空间的信息,这被平均在单个注意头中所抑制。
Beast #3—输入和输出预处理:
使用某种形式的嵌入来表示输入单词。编码器和解码器都是这样做的。
单词嵌入本身缺乏任何位置信息,而这在 rnn 中是通过它们的顺序性质实现的。同时,在自我关注中,由于 softmax,任何这样的位置信息都丢失了。
为了保留位置信息,转换器将向量注入到各个输入嵌入中(可以使用单词嵌入来对应于输入单词)。这些向量遵循模型学习的特定周期函数(例如:具有不同频率的各种正弦/余弦的组合,简而言之,彼此不同步),并且能够基于值确定单个单词彼此的位置。
这个注入的向量被称为“位置编码,并被添加到编码器和解码器堆栈底部的输入嵌入中。
Beast #4 —解码器堆栈:重访
解码器堆栈在每一步的输出都反馈到下一个时间步的解码器,这与 RNNs 中前一步的输出被用作下一个隐藏状态非常相似。正如我们对编码器输入所做的那样,我们将位置编码嵌入并添加到这些解码器输入中,以保留每个单词的位置。这种位置编码+单词嵌入组合然后被馈入一个被掩蔽的多头自我注意。
解码器堆栈中的这个自我关注子层被修改,以防止位置关注后续位置——你不能看未来的单词。该屏蔽确保位置 i 的预测仅依赖于小于 i 位置的已知输出。
编码器堆栈的输出随后被用作多组关键向量**【k】**和值向量 v ,用于“编码器解码器注意”——在图中以绿色显示——层。这有助于解码器关注该步骤的输入序列中上下文相关的部分。(类似于全局注意力向量的部分。)的 q 向量来自于“输出自我注意力”层。
一旦我们得到解码器的输出,我们再次进行 softmax 来选择单词的最终概率。
结论
让我们以一个快速的总结性复习来结束。
- 我们从理解什么是自我关注以及如何从这些 v,k,q 向量计算自我关注开始。
- 多头注意力是对自我注意力的有效修改,它使用多个更小的 v,k,q 集合,并连接每个集合的输出,以获得最终的 z.
- 然后我们看到了这三种自我关注是如何在模型中使用的。
- 随后对编码器和解码器堆栈的输入进行预处理。
参考文献+推荐阅读
-
了解深度学习中的深度注意力如果你面临注意力的问题。
-
图示变压器——有很好的视觉效果和解释。
-
YouTube 视频#1:获得模型直觉的绝佳资源。特别是如果你想进一步了解位置编码的话。
-
YouTube 视频# 2:Lukasz Kaiser 关于这篇解释自我关注的论文的讲话。他是这篇论文的作者之一。
我很高兴你坚持到了这篇文章的结尾。🎉我希望你的阅读体验和我写这篇文章时一样丰富。💖
如果你喜欢这篇文章,请点击这里查看我的其他文章。
如果你想联系我,我会选择推特。
变压器是图形神经网络
这篇文章的目的是在 NLP 的 Transformer 架构背后建立直觉,以及它与图上神经网络的联系。
阿瑟尼·托古列夫在 Unsplash 上的照片
工程师朋友经常问我:图深度学习听起来很棒,但是有没有大的商业成功案例?在实际应用中部署了吗?
除了那些显而易见的——在 Pinterest 、 Alibaba 和 Twitter 的推荐系统——一个稍微微妙的成功故事是Transformer architecture,它已经被 storm 带走了NLP行业 。
通过这篇文章,我想在图形神经网络(GNNs) 和变形金刚之间建立联系。我将谈论 NLP 和 GNN 社区中模型架构背后的直觉,使用等式和数字建立联系,并讨论我们如何一起工作来推动进展。
让我们从讨论模型架构的目的开始——表示学习。
自然语言处理中的表征学习
在高层次上,所有神经网络架构将输入数据的表示构建为向量/嵌入,其编码关于数据的有用的统计和语义信息。这些潜在的或隐藏的表征然后可以被用来执行一些有用的事情,比如对一幅图像进行分类或者翻译一个句子。神经网络学习通过接收反馈来建立越来越好的表示,通常是通过误差/损失函数。
对于自然语言处理(NLP),传统上,递归神经网络 (RNNs)以顺序方式构建句子中每个单词的表示,即、一次一个单词。直观地说,我们可以把 RNN 层想象成一条传送带,上面的文字从左到右进行自回归处理。最后,我们为句子中的每个单词获得一个隐藏的特征,我们将它传递给下一个 RNN 层或用于我们选择的 NLP 任务。
RNNs 以连续的方式构建句子中每个单词的表示,即、一次一个单词。另一方面,变形金刚使用一种注意力机制来计算出在句子中的重要性。
最初为机器翻译引入的变形金刚已经逐渐取代主流 NLP 中的 rnn。该架构采用了一种全新的表示学习方法:完全消除递归,变形金刚使用一种注意力 机制来构建每个单词的特征,以计算出在句子中的所有其他单词对于前述单词的重要性。知道了这一点,单词的更新特征就是所有单词的特征的线性变换的总和,通过它们的重要性来加权。
回到 2017 年,这个想法听起来非常激进,因为 NLP 社区非常习惯于用 RNNs 处理文本的顺序-一次一个单词-风格。论文标题大概是火上浇油吧!
为了重述,扬尼克·基尔彻制作了一部出色的 视频概述 。
分解变压器
让我们通过将前面的段落翻译成数学符号和向量的语言来发展对架构的直觉。我们从层 ℓ 到层 ℓ+1 更新句子 S 中第 i 个单词的隐藏特征 h 如下:
其中 j∈ S 表示句子中的单词集合,而 Q,K,V 是可学习的线性权重(分别表示用于注意力计算的 Q uery, K ey 和 V 值)。对句子中的每个单词并行执行注意机制,以在 one shot 中获得它们的更新特征 RNNs 上的变形金刚的另一个加分项,它逐词更新特征。
我们可以通过以下管道更好地理解注意机制:
吸收单词 h_i^ℓ 的特征和句子“h_j^ℓ”中的其他单词集合;∀ j∈ S,我们通过点积计算每一对(I,j)的注意力权重 w_{ij},随后是所有 j 的 softmax。最后,我们通过对由它们相应的 w_{ij}加权的所有 h_j^ℓ’s 求和来产生单词 I 的更新的单词特征 h_i^{ℓ+1}。句子中的每个单词并行地经历相同的流水线来更新其特征。
多头注意力机制
让这种点积注意力机制发挥作用被证明是棘手的——糟糕的随机初始化会破坏学习过程的稳定性。我们可以通过并行执行多个注意力“头部”并连接结果(每个头部现在有单独的可学习权重)来克服这一点:
其中 Qk,Kk、V^k 是第 k 注意头的可学习重量,而 O 是向下投影,以匹配各层的 h_i^{ℓ+1} 和 h_i^ℓ 的尺寸。
多个头部允许注意力机制实质上“对冲赌注”,从上一层查看隐藏特征的不同转换或方面。我们稍后会详细讨论这一点。
比例问题和前馈子层
推动最终变压器架构的一个关键问题是,在之后的单词的特征,注意机制可能处于不同的标度或大小 : (1)这可能是由于当对其他单词的特征求和时,一些单词具有非常尖锐或非常分散的注意权重 w_{ij} 。(2)在单个特征/向量条目级别,跨多个注意力头(每个注意力头可能以不同的比例输出值)进行连接可以导致最终向量 h_i^{ℓ+1} 的条目具有大范围的值。按照传统的 ML 智慧,在管道中添加一个规范化层似乎是合理的。
变形金刚用 图层名 克服了问题(2),该图层名在特征级标准化并学习仿射变换。此外,通过特征尺寸的平方根缩放点积注意力有助于抵消问题(1)。
最后,作者提出了另一种控制尺度问题的“技巧”:一种具有特殊结构的位置式两层 MLP 。在多头关注之后,他们通过一个可学习的权重将 h_i^{ℓ+1} 投射到一个(荒谬的)更高维度,在那里它经历了 ReLU 非线性,然后被投射回其原始维度,接着是另一个归一化:
老实说,我不确定过度参数化的前馈子层背后的确切直觉是什么,似乎也没有人对此提出疑问!我认为图层归一化和缩放点积没有完全解决突出显示的问题,所以大 MLP 是一种重新缩放彼此独立的特征向量的方法。
邮件我 如果你了解更多!
变形层的最终图片如下所示:
Transformer 架构也非常适合非常深的网络,使 NLP 社区能够在模型参数和数据方面扩展。每个多头注意力子层和前馈子层的输入和输出之间的剩余连接是堆叠变压器层的关键(但为了清楚起见在图中省略)。
gnn 构建图形的表示
让我们暂时离开 NLP 一步。
图形神经网络(gnn)或图形卷积网络(gcn)在图形数据中构建节点和边的表示。它们通过邻域聚合*(或消息传递)来实现,其中每个节点从其邻居收集特征,以更新其周围的局部图结构的表示。堆叠几个 GNN 图层使模型能够在整个图中传播每个节点的特征-从其邻居到邻居的邻居,等等。***
以这个表情符号社交网络为例:GNN 产生的节点特征可用于预测任务,如识别最有影响力的成员或提出潜在的联系。
在其最基本的形式中,gnn 更新节点 i 的隐藏特征 h (例如😆)在层 ℓ 通过节点自身特征的非线性变换 h_i^ℓ 添加到来自每个相邻节点的特征集合h_j^ℓj∈n(I):
其中 U,V 是 GNN 层的可学习权重矩阵,而 σ 是诸如 ReLU 的非线性。在这个例子中, N (😆) = { 😘,😎,😜,🤩 } 。
可以用其他输入大小不变的聚合函数来代替对邻域节点 j∈ N(i)】的求和,例如简单的平均值/最大值或者更强大的函数,例如经由注意机制和的加权求和。
听起来熟悉吗?
也许管道有助于建立联系:
如果我们要进行多个并行的邻居头聚合,并用关注机制即的加权和来代替邻居 j 上的求和,我们将得到图关注网络** (GAT)。加上归一化和前馈 MLP,瞧,我们有一个图形转换器!**
句子是完全连接的单词图
为了使这种联系更加明确,可以把一个句子看作一个完全连通图,其中每个单词都与其他每个单词相连。现在,我们可以使用 GNN 为图(句子)中的每个节点(单词)构建特征,然后我们可以用它来执行 NLP 任务。
大体上,这就是变形金刚正在做的事情:它们是具有多头注意力的 gnn,作为邻居聚合函数。标准 gnn 从它们的局部邻域节点 j∈ N(i) 聚集特征,而 NLP 的 Transformers 将整个句子 S 视为局部邻域,在每一层从每个单词 j∈ S 聚集特征。
重要的是,各种特定问题的技巧——如位置编码、因果/掩蔽聚合、学习率计划和广泛的前期培训——对变形金刚的成功至关重要,但在 GNN 社区中却很少出现。与此同时,从 GNN 的角度来看《变形金刚》可以启发我们摆脱架构中的许多花里胡哨。
我们能从彼此身上学到什么?
现在我们已经在变形金刚和 GNNs 之间建立了联系,让我来给大家一些想法…
全连通图是 NLP 的最佳输入格式吗?
在统计 NLP 和 ML 之前,像诺姆·乔姆斯基这样的语言学家专注于发展语言结构的形式化理论,例如语法树/图**。已经尝试过了,但是也许 Transformers/gnn 是更好的架构,让语言理论和统计 NLP 的世界更近一步?**
来源:维基百科
如何学习长期依赖?
全连通图的另一个问题是,它们使得学习单词之间的长期依赖关系变得困难。这仅仅是由于图中的边数与节点数即的平方比例关系,在一个 n 单词句子中,转换器/GNN 将对 n^2 单词对进行计算。事情失去了控制。****
NLP 社区对长序列和依赖性问题的观点很有趣:根据输入大小使注意力机制稀疏或自适应,在每一层中添加递归或压缩,以及使用位置敏感散列进行有效的注意力都是更好的转换器的有希望的新思路。
有趣的是,我们可以看到来自 GNN 社区的想法混合在一起,例如,,句子的二元划分,图稀疏化似乎是另一种令人兴奋的方法。
来源:叶等,2019
变形金刚在学习‘神经句法’吗?
已经有几篇来自 NLP 社区的有趣的 论文关于变形金刚可能在学习什么。基本前提是,对一个句子中的所有词对进行关注——目的是识别哪些词对最有趣——使变形金刚能够学习类似于任务特定语法的东西。多头注意力中的不同头也可能“看”不同的句法属性。
在图的术语中,通过在全图上使用 GNNs,我们能从 GNN 如何在每一层执行邻域聚合中恢复最重要的边——以及它们可能包含的内容吗?我还不太相信这个观点。
来源:克拉克等人,2019
为什么要多个头像关注?为什么关注?
我更赞同多头机制的优化观点——拥有多个注意力头可以提高学习能力并克服糟糕的随机初始化**。例如,这些 论文表明,在培训后,变压器头可以被“修剪”或移除*,而不会对性能产生重大影响。***
多头邻域聚合机制在 GNNs 中也被证明是有效的,例如,GAT 使用相同的多头注意力, MoNet 使用多个高斯核来聚合特征。虽然发明多头技巧是为了稳定注意力机制,但它能成为挤出额外模型性能的标准吗?
相反,具有更简单的聚合函数如 sum 或 max 的 gnn 不需要多个聚合头进行稳定的训练。如果我们不必计算句子中每个词对之间的成对兼容性,这对变形金刚不是很好吗?
变形金刚能从转移注意力中获益吗?Yann Dauphin 及其合作者的最近的 工作提出了一种替代的 ConvNet 架构。变形金刚也一样,最终可能会做与康文网类似的事情。
来源:吴等,2019
为什么训练变形金刚这么难?
阅读新的《变形金刚》论文让我觉得,在确定最佳学习率计划、预热策略和衰减设置时,训练这些模型需要类似于黑魔法的东西。这可能仅仅是因为模型是如此庞大,而所研究的 NLP 任务是如此具有挑战性。
但是最近的 结果 暗示这也可能是由于架构内归一化和剩余连接的特定排列。
在这一点上,我在咆哮,但这让我怀疑:我们真的需要多个昂贵的成对注意力、过度参数化的 MLP 子层和复杂的学习时间表吗?
我们真的需要碳足迹巨大的模型吗?
对于手头的任务,具有良好归纳偏差的架构难道不应该更容易训练吗?
进一步阅读
要从 NLP 的角度深入了解 Transformer 架构,请查看这些令人惊叹的博客帖子:图解 Transformer 和注释 Transformer 。
此外,这个博客并不是第一个将 gnn 和变形金刚联系起来的博客:这是 Arthur Szlam 关于注意力/记忆网络、gnn 和变形金刚的历史和联系的精彩演讲。同样,DeepMind 的众星云集的立场文件引入了图网络框架,统一了所有这些想法。对于代码演练,DGL 团队有一个关于 seq2seq 作为图问题和构建变形金刚作为 gnn 的很好的教程。
在我们的下一篇文章中,我们将做相反的事情:使用 GNN 架构作为 NLP 的变形金刚(基于 的变形金刚库)🤗HuggingFace )。
最后,我们写了一篇最近的论文将变形金刚应用于草图。一定要去看看!
更新
变形金刚 GNNs 与T3【渐变】 于 2020 年 9 月出版!
该帖子最初出现在 NTU 图形深度学习实验室网站,也被翻译成了中文和俄文。一定要在 Twitter 、 Reddit 或 HackerNews 上加入讨论!
引用
在学术语境或书籍中的归属,请将此作品引用为
柴坦尼亚·k·乔希,《变形金刚是图形神经网络》,The Gradient,2020。
BibTeX 引用:
@ article { Joshi 2020 Transformers,
作者= {Joshi,Chaitanya},
标题= { Transformers is Gradient 神经网络},
期刊= {The Gradient},
年份= {2020},
如何发布= { \ URL {https://The Gradient . pub/Transformers-are-gaph-Neural-Networks/},**
NeurIPS 2019 上的变形金刚
ml 评论
NeurIPS 2019 上与变压器相关的论文
在我之前在 EMNLP 2019 上关于 BERT 的报道之后,如果不对 NeurIPS 2019 上关于变形金刚的论文做一个简短的回顾,那将是一种犯罪。关于神经信息处理系统的会议和研讨会于 12 月 8 日至 14 日在温哥华举行。像往常一样,有很多令人惊叹的想法和先进的研究。以下是其中的一些。
维尔伯特:为视觉和语言任务预先训练与任务无关的视觉语言表征
http://arxiv.org/abs/1908.02265
介绍了一种学习图像内容和自然语言的任务不可知联合表示的新方法。
ViLBERT(视觉和语言 BERT)由两个并行的 BERT 风格模型组成,在图像区域和文本片段上运行。每个流都是一系列的变压器块和新颖的共注意变压器层,它们被引入以实现模态之间的信息交换。通过从预先训练的对象检测网络(更快的 R-CNN)提取边界框及其视觉特征,每个图像被表示为一组区域特征。
ViLBERT 模型由视觉(绿色)和语言(紫色)处理的两个并行流组成,它们通过新的共注意变压器层进行交互。
有两个预训练任务:掩蔽多模态建模和多模态比对预测。屏蔽的多模态建模任务遵循标准 BERT 中的屏蔽语言建模任务——屏蔽大约 15%的单词和图像区域输入,并在给定剩余输入的情况下给模型分配重构它们的任务。在多模态对齐任务中,模型已经呈现了图像-文本对,并且必须预测图像和文本是否对齐,即文本是否描述了图像。
在许多已建立的视觉和语言任务中,ViLBERT 模型优于最先进的模型:视觉问题回答、视觉常识推理、参考表达式和基于标题的图像检索。
将我们的 ViLBERT 模型的转移任务结果与现有的最先进和合理的建筑烧蚀进行比较。
Ouroboros:关于加速训练基于 Transformer 的语言模型
http://arxiv.org/abs/1909.06695
毫无疑问,变形金刚在许多任务中都取得了巨大的成就,但是训练它们可能是一个漫长而昂贵的过程。解决这个问题的一个可能的方法是并行化。
当训练时模型太大而不适合单个设备时,模型并行化仍然是一个公开的问题。当一个模型变得太大而不适合单个计算设备时,最简单的解决方案是将模型层分布在多个设备上。
提出了一种新的模型并行算法来并行化基于转换器的语言模型的训练。这种算法可以在不损失精度的情况下显著提高速度。
每批计算时间的加速(在 K 个 GPU 上)
可视化和测量伯特的几何
http://arxiv.org/abs/1906.02715
在本文中,作者试图证明变形器具有一组语义和句法信息的中间表示。为了找到句法信息,他们在注意力向量的顶端训练线性模型。该模型必须预测两个单词之间依赖关系的存在和类型。二元探针实现了 85.8%的准确度,而多类探针实现了 71.9%的准确度。这个简单的线性探测的成功表明句法信息被编码在注意向量中。
一对有序标记的全模型注意力向量包含所有注意力头部和层中该对的标量注意力值。
第二部分是关于语义信息。人们很自然地会推测,变形金刚捕捉到了一个词在特定句子中的特定含义。
对于具有 n 个义项的给定单词,他们制作最近邻分类器,其中每个邻居是训练数据中给定词义的 BERT 基嵌入的质心。为了对一个新单词进行分类,他们找到这些质心中最近的一个,如果该单词没有出现在训练数据中,则默认为最常用的意义。简单的最近邻分类器实现了 71.1 的 F1 分数,高于当前的技术水平,精度随着层单调增加。这是一个强有力的信号,表明语境嵌入代表了词义信息。
词义消歧任务的 F1 分数。
支持树形结构变压器的新颖位置编码
【https://openreview.net/pdf?id=SJerEhR5Km 号
变压器适用于顺序建模任务。但是在某些情况下,使用纯面向序列的方法可能会导致丢失有价值的结构信息,例如,当我们想要表示层次结构时。这项工作为树结构数据引入了新的位置编码。这有可能将转换器扩展到从自然语言解析树到程序抽象语法树的设置范围。
这种方法有两种评估方法:合成翻译任务和生成的 CoffeeScript 与 JavaScript 代码之间的翻译。实验表明,使用这种编码的模型在面向树的任务中可以胜过序列转换器。
与 tree2tree LSTMs 比较的综合任务的整个程序错误率。
CoffeeScript-JavaScript 翻译任务的整个程序错误率数据。
用于语言建模的张量化转换器
http://arxiv.org/abs/1906.09777
我认为这是最有趣的作品。也许不是结果,而是使用张量分解来降低模型复杂性的事实。
如您所知,考虑如何减少内存和计算转换器所需的数量是非常重要的。现有的模型压缩方法主要分为参数剪枝和共享、低秩逼近、知识转移、转移卷积滤波器和张量分解方法。
基于参数共享的思想,本文重点研究了 transformer 中多头注意力的压缩。同时,它们还结合了低秩近似方法来降低参数和计算复杂度。提出了一种新的自我注意方法——多线性注意。
(左)使用塔克分解的单块注意。(右)基于块项张量分解的多线性注意。
在所有实验中,新架构都取得了与 SoTA 相当的结果,但参数更少。
结果(PPL)和模型参数与十亿人的最新结果。
在 PTB 和 WikiText-103 上使用最新的结果进行结果和压缩。
结果和压缩与变压器对 WMT-16 英语到德语的翻译。
变形金刚解释
对谷歌 Transformer 模型的详尽解释;从理论到实施
这篇文章是对谷歌研究的著名论文“注意力是你所需要的全部”中的变压器模型的深入阐释。该模型是序列转导任务(任何涉及将一个序列转换成另一个序列的任务)中许多 SOTA(现有技术)方法的先驱。以下是这篇帖子的内容:
内容
- 序列对序列模型的概述
- 为什么是变压器?
- 注意?自我关注!
- 什么是变压器?
- 结论
这将是一个漫长的,所以坐稳了!
序列到序列模型概述
GNMT 中的 seq2seq,由谷歌人工智能博客可视化
序列到序列编码器-解码器结构是序列转换任务的基础。它实质上建议立即编码完整的序列,然后使用该编码作为产生解码序列或目标序列的上下文。
人们可能会把这与人类倾向于首先完整地“听”一个句子(序列),然后相应地作出反应,无论是对话、翻译还是任何类似的任务。
seq2seq 模型由分别位于编码器和解码器的独立 rnn 组成。编码序列是编码器网络中 RNN 的隐藏状态。使用这个编码序列和(通常)单词级生成模型,seq2seq 生成目标序列。由于编码是在单词级进行的,因此对于较长的序列,很难在编码器中保持上下文,因此将众所周知的注意机制与 seq2seq 结合,以*“注意”*序列中对目标序列的生成有显著贡献的特定单词。注意力是根据输入序列中的单个单词对目标序列生成的影响来衡量它们。
为什么是变形金刚?
使用 RNNs 的序列对序列是很棒的,注意它甚至更好。那变形金刚有什么了不起的?
rnn 的主要问题在于它们不能在处理时提供并行化。RNN 的处理是连续的,也就是说,我们不能计算下一个时间步长的值,除非我们有当前的输出。这使得基于 RNN 的方法进展缓慢。
然而,脸书研究中心解决了这个问题,他们建议使用基于卷积的方法,该方法允许将并行化与 GPU 相结合。这些模型建立了单词之间的层次表示,其中在序列中出现得较近的单词在较低的层次上相互作用,而出现得较远的单词在层次中的较高层次上起作用。ConvS2S 和 ByteNet 就是两个这样的型号。引入层次结构是为了解决长期的依赖性。
通过 Michal Chromiak 的博客形成 ConvS2S 的多步关注
虽然这实现了并行化,但它的计算量仍然很大。与 RNNs 和 CNN 提供的结果质量相比,它们每层的操作数量更不合理。最初的变压器论文已经提出了这些参数对于合格模型的比较:
基于计算效率度量的 RNN、CNN 和自我注意模型的比较
这里,d(或 d_model )是单词的表示维度或嵌入维度(通常在 128-512 的范围内),n 是序列长度(通常在 40-70 的范围内),k 是卷积的核大小,r 是受限自我注意的注意窗口大小。从表中,我们可以推断出以下几点:
- 显然,自我关注的每层计算复杂度远低于其他人。
- 关于顺序操作,除了 RNNs,所有其他方法都提供并行化,因此它们的复杂度是 O(1)。
- 最后一个度量是最大路径长度,它表面上意味着参与长期依赖或远距离单词的复杂性。因为卷积模型使用分层表示,所以它们的复杂度是 nlog(n ),而自我注意模型在同一个步骤中关注所有单词,因此它们的复杂度是 O(1)。
转换器使用自关注机制,其中关注权重是使用输入序列中的所有单词一次性计算的,因此它有利于并行化。除此之外,由于转换器中的每层操作是在相同序列的字之间进行的,所以复杂度不超过 O(n d)。因此,transformer 被证明是有效的(因为它使用了注意力),同时也是一个计算高效的模型。
注意?自我关注!
在上一节中,我们讨论了转换器使用自我关注作为有效计算的手段。在这一节,我们将破译,到底什么是自我关注,它是如何在变压器使用。
查询、键和值
作为一般惯例,注意机制遵循查询、键、值模式。所有这三个都是来自输入序列的单词,它们以某种模式相互操作。查询和键最初经历某些操作,然后输出(通常)与值相乘。这将在下一小节中变得更清楚,我们将看到自我关注如何工作的图示描述。
自我关注
自我关注
如前所述,自我注意是“注意”来自同一序列的单词。
从表面上看,自我关注决定了一个单词对句子的影响
在上图中,自我注意的运作是用一个句子的例子来解释的,“这就是注意”。单词“This”与句子中的其他单词连用。类似地,计算所有单词的注意力权重(这里是“是”和“注意力”)。这里没有*‘隐藏状态’的概念。输入被直接使用。这从架构中移除了的顺序性*,从而允许并行化。
在变形金刚的情况下, 多头注意 被使用,这将在后面的帖子中介绍。
什么是变压器?
到目前为止,我们已经看到了在 Transformer 中实现的机制。此后,我们将实际看到这些毗邻的机制和几个具体到模型的组成部分是如何被纳入。
我们将尝试自下而上地构建一个变压器
位置编码
通过“注意力是你所需要的一切”在 Transformer 中进行位置编码
如果你观察,自我注意的计算没有单词在序列中排序的概念。尽管 rnn 很慢,但它们的顺序性确保了单词的顺序得以保留。因此,为了引出单词在序列中定位的概念,将位置编码添加到常规输入嵌入中。位置编码的维度与嵌入( d_model )相同,便于两者的求和。在该论文中,位置编码是使用以下方法获得的:
通过“注意力是你所需要的一切”使用正弦波进行位置编码
这里, i 是维度, pos 是字的位置。我们用正弦表示维度的偶数(2i),余弦表示奇数(2i + 1)。位置编码有几种选择—学习的或固定的。这是固定的方法,因为论文陈述的学习和固定的方法获得了相同的结果。
背后的一般思想是,对于固定的偏移 k,PEₚₒₛ₊ₖ可以表示为 PEₚₒₛ.的线性函数
掩饰
通过“注意力就是你需要的全部”在多头注意力中使用掩蔽
在变形金刚的多头注意机制中使用了两种遮罩。
衬垫掩模的加工
- **填充掩码:序列的输入向量应该长度固定。因此, max_length 参数定义了转换器可以接受的序列的最大长度。长度大于 max_length 的所有序列都被截断,而较短的序列用零填充。然而,零填充既不应该有助于注意力计算,也不应该有助于目标序列生成。下图解释了填充遮罩的工作原理。这是变压器中的可选操作。
前瞻掩模的工作
- ***前瞻掩码:*在解码器处生成目标序列时,由于转换器使用自关注,它倾向于包括来自解码器输入的所有单词。但是,实际上这是不正确的。只有当前单词之前的单词可以有助于下一个单词的生成。掩蔽的多头注意力确保了这一点。下图解释了前瞻掩码的工作原理。
下面的代码片段解释了填充是如何起作用的:
*>>> a = tf.constant([0.6, 0.2, 0.3, 0.4, 0, 0, 0, 0, 0, 0])
>>> tf.nn.softmax(a)
<tf.Tensor: shape=(10,), dtype=float32, numpy=
array([0.15330984, 0.10276665, 0.11357471, 0.12551947, 0.08413821,
0.08413821, 0.08413821, 0.08413821, 0.08413821, 0.08413821],
dtype=float32)>
>>> b = tf.constant([0.6, 0.2, 0.3, 0.4, -1e9, -1e9, -1e9, -1e9, -1e9, -1e9])
>>> tf.nn.softmax(b)
<tf.Tensor: shape=(10,), dtype=float32, numpy=
array([0.3096101 , 0.20753784, 0.22936477, 0.25348732, 0\. ,
0\. , 0\. , 0\. , 0\. , 0\. ],
dtype=float32)>*
标度点积注意力
通过“关注是你所需要的一切”的成比例的点产品关注
这是主要的“注意力计算”步骤,我们之前在自我注意部分已经讨论过了。这包括几个步骤:
- 这是一个矩阵点积运算。首先,查询和键经历这个操作。该操作可以用数学方法表示如下:
马特穆尔(q,K) = Q.Kᵀ
- Scale: 点积运算的输出可能会导致较大的值,这可能会扰乱后面部分的 softmax。因此,我们通过将它们除以缩放因子 √dₖ.来缩放它们
- ***遮罩:*可选的填充遮罩已经在上面的遮罩部分讨论过了。
- ***soft max:*soft max 函数将数值降低到一个概率分布,即[0,1]。
通过“注意力是你所需要的全部”得出的比例点积注意力的最终等式
比例点积注意力是多头注意力的主要组成部分,我们将在下一小节中看到。
多头注意力
通过的多头关注
多头注意力本质上是前面讨论的所有微观概念的整合。
在相邻的图中, h 是头数。就数学而言,多头注意力的初始输入被分成 h 部分,每个部分都有查询、键和值,用于序列中的 max_length 字,用于 batch_size 序列。Q、K、V 的尺寸称为深度,计算如下:
深度= d _ 模型// h
这就是 d_model 需要被h整除的原因。因此,在分割时, d_model 形状矢量被分割成形状深度的 h 矢量。这些向量作为 Q、K、V 被传递给缩放的点积,并且通过再次将 h 向量整形为形状 d_model 的 1 个向量,输出为“ Concat ”。这个重组的向量然后通过前馈神经网络层。
点式前馈网络和残余漏失
通过“关注是你所需要的一切”的逐点 FFN 和剩余辍学
逐点前馈网络块本质上是两层线性变换,在整个模型架构中使用相同,通常在关注块之后。
对于正则化,在将每个子层的输出添加到子层的输入并进行归一化之前,对每个子层的输出应用缺失。
最后,我们有完整的变压器!
变压器 via “注意力是你所需要的一切”
所有的组件被组装在一起,以建立变压器。编码器模块在左边,解码器模块在右边。
编码器和解码器块可以可选地微调到 Nₓ单位以调整模型。
结论
在这篇文章中,我们看到了序列转导任务中基于 RNN 的方法的问题,以及革命性的 Transformer 模型如何解决这些问题。后来,我们研究了变压器的基本机制及其工作原理。最后,我们看到了各种组件和底层机制如何与转换器一起工作。
我已经在这里介绍了使用 TensorFlow 进行抽象文本摘要的 Transformer 的逐步实现。
参考
《注意力是你所需要的全部》变形金刚原纸:【https://arxiv.org/abs/1706.03762
*Seq2Seq 论文:【https://arxiv.org/abs/1409.3215 *
巴林岛注意事项:https://arxiv.org/abs/1409.0473
ConvS2S 和 ByteNet 论文:https://arxiv.org/abs/1705.03122,https://arxiv.org/abs/1610.10099
从关注到自我关注到变形金刚
towardsdatascience.com](/self-attention-and-transformers-882e9de5edda) [## 变形金刚-你需要的只是注意力。
变形金刚——不仅仅是看上去那么简单!我们到了吗?良好的…不完全是,但是…消除复发怎么样…
mchromiak.github.io](https://mchromiak.github.io/articles/2017/Sep/12/Transformer-Attention-is-all-you-need/#.XreFtRbhU5l)
一些很棒的变形金刚形象:
讨论:黑客新闻(65 分,4 条评论),Reddit r/MachineLearning (29 分,3 条评论)翻译…
jalammar.github.io](http://jalammar.github.io/illustrated-transformer/)*
简化多标签分类的变压器。
伯特、XLNet、罗伯塔等。对于多标签分类—逐步指南
作为一名数据科学家,我一直在学习文本分类的最新技术,我发现没有太多容易的例子来适应 transformers (BERT,XLNet 等。)进行多标签分类…所以我决定自己试试,这就是!
作为对其他多标签文本分类博客帖子的敬意,我将使用有毒评论分类挑战数据集。
这篇文章附有一个互动的谷歌 Colab 笔记本,所以你可以自己尝试一下。您所要做的就是将 train.csv、test.csv 和 test_labels.csv 文件上传到实例中。让我们开始吧。
在本教程中,我将使用拥抱脸的变形金刚库以及 PyTorch(带 GPU) ,尽管这可以很容易地适应 TensorFlow — 如果这与多类分类教程一起获得牵引力,我可能稍后会为此编写一个单独的教程。下面我将训练一个 BERT 模型,但是我将向你展示在这个过程中为其他 transformer 模型改编这个代码是多么容易。
导入库
加载和预处理训练数据
毒性数据集已经被清理并被分成训练集和测试集,因此我们可以加载训练集并直接使用它。
每个 transformer 模型需要不同的标记化编码,这意味着句子的标记化方式和注意力屏蔽的使用可能会因您使用的 transformer 模型而异。令人欣慰的是,HuggingFace 的变形金刚库使得为每个模型实现变得极其容易。在下面的代码中,我们加载了一个预训练的 BERT 记号赋予器,并使用“batch_encode_plus”方法来获取记号、记号类型和注意掩码。随意加载适合您想要用于预测的模型的记号赋予器。例如,
BERT:
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased', do_lower_case=True) XLNet:
tokenizer = XLNetTokenizer.from_pretrained('xlnet-base-cased', do_lower_case=False) RoBERTa:
tokenizer = RobertaTokenizer.from_pretrained('roberta-base', do_lower_case=False)
接下来,我们将使用 10%的训练输入作为验证集,这样我们就可以在训练时监控分类器的性能。在这里,我们希望确保我们利用了“分层”参数,这样就不会有看不见的标签出现在验证集中。为了进行适当的分层,我们将所有只在数据集中出现一次的标签,并强制它们进入训练集。我们还需要创建 PyTorch 数据加载器来加载用于训练/预测的数据。
加载模型和设置参数
加载适当的模型可以如下所示,每个模型已经包含了一个单一的密集层用于分类。
BERT:
model = BertForSequenceClassification.from_pretrained("bert-base-uncased", num_labels=num_labels)XLNet:
model = XLNetForSequenceClassification.from_pretrained("xlnet-base-cased", num_labels=num_labels)RoBERTa:
model = RobertaForSequenceClassification.from_pretrained('roberta-base', num_labels=num_labels)
优化器参数可以通过几种方式进行配置。这里我们使用了一个定制的优化参数(我在这方面做得更成功),但是,您可以只传递注释中所示的“model.parameters()”。
火车模型
使用**“分类交叉熵”**作为损失函数,将 HuggingFace 库配置为开箱即用的多类分类。因此,变压器模型的输出类似于:
outputs = model(batch_input_ids, token_type_ids=None, attention_mask=batch_input_mask, labels=batch_labels)**loss, logits = outputs[0], outputs[1]**
但是,如果我们避免传入标签参数,模型将只输出逻辑值,我们可以使用这些逻辑值来计算多标签分类的损失。
outputs = model(batch_input_ids, token_type_ids=None, attention_mask=batch_input_mask, labels=batch_labels)**logits = outputs[0]**
下面是这样做的代码片段。这里我们使用**“具有 Logits 的二元交叉熵”**作为我们的损失函数。我们可以很容易地使用标准的“二元交叉熵”、“汉明损失”等。
为了验证,我们将使用微 F1 精度来监控跨时代的训练表现。为此,我们必须利用模型输出中的逻辑值,将它们传递给 sigmoid 函数(给出[0,1]之间的输出),并对它们进行阈值处理(0.50)以生成预测。然后,这些预测可用于计算相对于真实标签的准确度。
维奥拉。我们已经准备好训练,现在运行它…我的训练时间在 20-40 分钟之间,取决于最大令牌长度和使用的 GPU。
预测和指标
我们的测试集的预测类似于我们的验证集。在这里,我们将加载、预处理和预测测试数据。
输出数据帧
创建显示句子及其分类的输出数据框架。
奖励—优化微 F1 精度的阈值
迭代阈值以最大化微 F1 精度。
就是这样!有问题请评论。这里是 Google Colab 笔记本的链接,以防你错过。如果您有任何私人问题,请随时通过 LinkedIn 或 Twitter 与我联系。
参考资料:
https://electricenjin . com/blog/look-out-Google-Bert-is-here-to-shake-up-search-queries
https://github.com/google-research/bert
https://github.com/huggingface/transformers
【https://pytorch.org/docs/stable/nn.html#bcewithlogitsloss 号
【https://arxiv.org/abs/1706.03762
变形金刚(最先进的自然语言处理)
《变形金刚》第 1/3 部分 vs 谷歌 QUEST 问答标注(Kaggle top 5%)。
作者图片
这是一个由 3 部分组成的系列,我们将经历变形金刚、伯特和动手 Kaggle 挑战—Google QUEST Q&A Labeling以观看变形金刚的行动(在排行榜上排名第 4.4%)。在这一部分(1/3)中,我们将了解变形金刚是如何成为各种现代自然语言处理任务及其工作方式的艺术。
TheTransformer是谷歌和多伦多大学的研究人员于 2017 年在论文 Attention is All You Need 中提出的深度学习模型,主要用于自然语言处理(NLP)领域。
像递归神经网络(RNNs)一样,转换器被设计为处理顺序数据,如自然语言,用于翻译和文本摘要等任务。然而,与 rnn 不同,转换器不要求按顺序处理顺序数据。例如,如果输入数据是自然语言句子,转换器不需要在结束之前处理它的开头。由于这个特性,Transformer 允许比 RNNs 更多的并行化,因此减少了训练时间。
变压器是围绕注意力机制的概念设计的,注意力机制是为了帮助记忆神经机器翻译中的长源句子而设计的。
听起来很酷,对吧?让我们看一看引擎盖下面,看看事情是如何运作的。
图像来源
变压器基于编码器-解码器架构,该架构由一组编码层组成的编码器和一组解码层组成的解码器组成,前者一层接一层地迭代处理输入,后者对编码器的输出执行相同的操作。
因此,当我们将一个句子传递到转换器中时,它被嵌入并传递到一个编码器堆栈中。最终编码器的输出随后被传递到解码器堆栈中的每个解码器模块。解码器堆栈随后产生输出。
变换器中的所有编码器模块都是相同的,同样,变换器中的所有解码器模块也是相同的。
来源:http://jalammar.github.io/illustrated-transformer/
这是一个非常高层次的变压器表示,当理解变压器如何在现代 NLP 任务中如此有效时,它可能没有多大意义。不要担心,为了让事情更清楚,我们现在将讨论编码器和解码器单元的内部结构…
编码器
编码器有两个部分,自我关注,和一个前馈神经网络。
来源:http://jalammar.github.io/illustrated-transformer/
编码器的输入首先流经自我关注层,该层帮助编码器在编码特定单词时查看输入句子中的其他单词。基本上,对于每个输入单词“x ”,自我关注层生成一个向量 Z ,使得它在生成 Z 之前将所有的输入单词(x1,x2,x3,…,xn)放入图片中。稍后我会在博客中解释为什么要将所有的输入单词嵌入到图片中,以及它是如何生成 Z 的,但是现在,只需要记住编码器子组件的这些简短的概要。
自我注意层的输出被馈送到一个前馈神经网络。前馈神经网络为每个输入 Z 生成一个输出,前馈神经网络的输出被传递到下一个编码器模块的自关注层,依此类推。
现在我们已经知道了编码器内部的所有内容,让我们来理解每个组件内部发生的张量运算。
首先是输入:
我们知道变压器用于 NLP 任务,因此我们处理的数据通常是句子的语料库,但由于机器学习算法都是关于矩阵运算的,我们首先需要将人类可读的句子转换为机器可读的格式(数字)。为了将句子转换成数字,我们使用“单词嵌入”。这一步很简单,句子中的每个单词都表示为一个 n 维向量(n 通常是 512),对于变形金刚,我们通常使用单词的手套嵌入表示。
还有一种叫做 位置编码 的东西,适用于这些嵌入,但我稍后会谈到。一旦我们有了每个输入单词的嵌入,我们将这些嵌入同时传递给自我关注层。
自我关注层的训练参数:
不同的层具有不同的学习参数,例如,密集层具有权重和偏差,而卷积层具有内核作为学习参数,类似地,在自我关注层中, 我们有 4 个学习参数:
-查询矩阵:【Wq】
*-关键矩阵:*Wk
*-值矩阵:*Wv
*-输出矩阵:*Wo
前 3 个可训练参数有特殊用途,它们用于生成 3 个新参数:
-查询:【Q】* *-关键:*K *-值:V这些参数稍后用于生成输出
需要记住的几点是:
-输入张量 x 具有 n 行和 m 列,其中 n 是输入单词的数量, m 是每个单词的向量大小,即 512。
-输出张量 Q、K、V、 和 Z 有 n 行和 dk列,其中 n 是输入字数, dk 是 64。 m 和 dk 的值不是随机值,而是提出这种架构的研究人员发现的最佳值。**
来源:http://jalammar.github.io/illustrated-transformer/
****在如上所述计算了 3 个参数 Q,K,V 之后,自我关注层然后为每个输入单词计算 分数,向量。
点状产品注意:
自我关注层的下一步是计算每个输入单词对应的向量分值。这个分数计算是将注意力机制带入生活的最关键的步骤之一(嗯…不是字面上的意思)。向量的大小为 n 其中n**是输入单词的数量,并且该向量的每个元素是一个数字,该数字表示其对应的单词对当前单词的贡献有多大
让我们考虑一个例子来得到直觉——
“动物没有过马路是因为它太累了”在上面的句子中, 这个词 it 指的是动物而不是路。对我们来说,这很容易掌握,但对一台没有注意力的机器来说就不一样了,因为我们知道语法是如何工作的,而且我们已经发展出一种感觉,那就是它指的是 动物 而不是像 穿越 或 道路这样的词。 这种语法感是经过训练后出现在变形金刚身上的,但事实是,对于一个给定的单词,它会考虑输入的所有单词,然后有能力选择它认为贡献最大的一个单词,这就是注意力机制的作用。 对于上面的句子,为单词 it 生成的得分向量会有 11 个数字,每个数字对应输入句子中的一个单词。对于训练有素的模型,该得分向量在位置 2 和 8 处将具有较大的数字,因为位置 2(动物)和 8(它)处的单词对 it 贡献最大。 可能看起来是这样的:
【2, 60 ,4,5,3,8,5, 90 ,7,6,3】
注意位置 2 和 8 的值大于其他位置的值。
来源:http://jalammar.github.io/illustrated-transformer/
我们来看看这些分数是如何在自我关注层产生的。到目前为止,对于每个单词,我们有 Q,K,V 向量。为了生成分数向量,我们使用一种叫做点积注意力的东西,其中我们在 Q 和 K 向量之间取点积来生成分数。 Q 的值对应于我们正在计算得分的查询词,在上面的例子中,该词是,而有 n 个的值 K、 分别对应于输入词的关键向量。
所以,如果我们想为单词 it 生成分数:******
- 我们取它的查询向量: Q
- 我们取输入句子的关键向量: K1,K2,K3,…,Kn。
- 我们取 Q 和 K 之间的点积,得到 n 的分数。
计算完分数后,我们通过将分数除以(【dk】)的平方根来对分数进行归一化,平方根是向量***【Q,K,V.*** 的列维度。这一步是强制性的,因为 transformer 的创建者发现通过 sqrt 对分数进行归一化。的 dk 的 给出更好的结果。******
标准化分数向量后,我们使用 softmax 函数对其进行编码,使得输出与原始分数成比例,但所有值的总和为 1。
一旦我们准备好“软最大化”分数,我们简单地将每个分数元素与对应的值向量 V 相乘,这样,在这个操作之后,我们得到 n 个值向量V**:V1,V2,V3,…,Vn 。
现在为了获得自我关注的输出 Z ,我们简单地把所有的 n 个值向量相加。****
简而言之,所有自我关注的步骤
来源:http://jalammar.github.io/illustrated-transformer/
上图说明了自我关注层的步骤。
多头关注:
现在我们知道了注意力头是如何工作的,以及它有多神奇,这就有一个陷阱了。单个注意力头有时会错过输入中对聚光灯单词贡献最大的一些单词,如在前面的例子中,有时注意力头在预测单词 it 时可能没有注意到单词动物,这可能会导致问题。为了解决这个问题,我们使用了多个注意力头,而不是单个注意力头,每个注意力头都以类似的方式工作。这个想法有助于我们减少任何单个注意力头的错误或误算。
这也被称为 多头关注 。
两种不同注意力的得分用橙色和绿色表示。我们可以看到,一个注意力集中者更关注像动物、十字路口这样的词,而另一个注意力集中者更关注像街道、疲倦这样的词。 ( 图像来源)。
在变形金刚中,多头注意力通常使用 8 个注意力头。
现在注意,单个注意力头的输出是 64 维的,但是如果我们使用多头注意力,我们将得到 8 个这样的 64 维向量作为输出。
来源:http://jalammar.github.io/illustrated-transformer/
原来有一个最后的可训练参数输出矩阵 Wo 我之前提到的在这里发挥作用。
在自我关注的最后一层,所有的输出【Z0,Z1,Z2,…,Z7】被连接并与 Wo 相乘,使得最终的输出 Z 的维数为 64。**
下图显示了上面讨论的所有步骤:
来源:http://jalammar.github.io/illustrated-transformer/
位置编码:
记得在首先是输入部分,我提到了位置编码,让我们看看它们是什么以及它们如何帮助我们。我们目前的 awesome transformer 的问题是,它没有考虑输入单词的位置。与 RNN 不同,我们用时间步长来表示哪个单词在前面和后面,在《变形金刚》中,因为单词是同时输入的,所以我们需要某种位置编码来定义哪个单词在哪个单词后面。
位置编码帮助了我们,因为它为输入嵌入提供了位置感,我们首先为每个输入单词生成位置嵌入,然后将这些位置嵌入添加到相应单词的单词嵌入中,以生成带有时间信号的嵌入。****
提出了许多方法来生成位置嵌入,如一键编码向量或二进制编码,但研究人员发现最有效的方法是使用以下等式来生成嵌入:
作者图片
当我们为最大长度为 50 的句子绘制 128 维位置编码时,它看起来类似于:
**每行代表嵌入向量(作者图片 )
剩余连接:
最后,编码器中还增加了一个称为剩余连接或跳过连接的功能,允许前一层的输出绕过中间的层。
它在有许多隐藏层的深层网络中很有帮助,如果中间的任何层没有太大用处或没有学到太多,跳过连接有助于绕过该层。
另一个需要注意的是,当剩余连接被添加并且结果被归一化时。
带跳跃连接的编码器单元。(图像来源)。
解码器
解码器与编码器非常相似。像编码器一样,它也有自关注和前馈网络,但它也有一个称为编码器-解码器关注的附加块夹在两者之间。
编码器-解码器关注层的工作方式与多头自我关注类似,只是它从其下一层创建查询矩阵,并从编码器堆栈的输出中获取键和值矩阵。
其余 2 层的工作方式与编码器单元中的完全相同。**
编码器解码器架构。(图片来源)。
解码器堆栈的输入是顺序的,不同于编码器堆栈中的同时输入,这意味着第一个输出字作为输入传递到解码器,解码器使用它生成第二个输出,现在该输出再次作为输入传递到解码器,并使用它生成第三个输出,依此类推…
变压器的工作。(图片来源)。
解码器的输出被传递到具有 softmax 激活的线性层,使用该线性层来预测正确的字。
来源:http://jalammar.github.io/illustrated-transformer/
一旦变换器使用前向传播来预测单词,就使用像交叉熵这样的损失函数将预测与实际标签进行比较,然后使用反向传播来更新所有可训练参数。
嗯,这是理解学习如何在变形金刚中发生的一种简化方式。有更多的变化,如采取完整的输出句子来计算损失。想了解更多,你可以看看杰伊·阿拉玛在《变形金刚》上的博客。
到此,我们就到此为止了。希望阅读愉快。
我要感谢所有的创作者,他们创作了我写这篇博客时提到的精彩内容。
参考链接:
- **应用人工智能教程:https://www.appliedaicourse.com/
- https://arxiv.org/abs/1706.03762
- http://jalammar.github.io/illustrated-transformer/T21
- http://primo.ai/index.php?title=Transformer
- https://en . Wikipedia . org/wiki/Transformer _(machine _ learning _ model)
- https://medium . com/inside-machine-learning/what-a-transformer-d 07 DD 1 fbec 04
最终注释
感谢您阅读博客。我希望它对那些渴望做项目或学习 NLP 新概念的人有用。
在第 2/3 部分中,我们将讨论 BERT(变压器的双向编码器表示)。
在第 3/3 部分中,我们将经历一次动手的 Kaggle 挑战——Google QUEST Q&A Labeling来看看变形金刚的行动(在排行榜上排名第 4.4%)。
在 LinkedIn 上找到我:www.linkedin.com/in/sarthak-vajpayee
和平!☮
将数据转化为行动
数据科学的商业价值取决于人工智能项目与企业战略、文化和资源的一致性
图片来源:EB 局
许多公司正在大力投资人工智能,因为它们认为有必要“对每天捕获的大量数据做点什么”。Gartner 计算出今年的投资为 3830 亿美元,尽管事实上绝大多数人工智能项目都没有现实世界的应用。如果人工智能的潜在好处很难被忽视,那么成功的项目取决于权衡人工智能的潜力和组织现实的能力。管理层如何将数据科学中的实验与企业战略结合起来,鼓励他们的数据科学团队开发直接适用于业务的模型,并展示他们的投资如何影响底线?
金融欺诈案是一个非常明显的例子。行业观察家估计,仅在线信用卡欺诈一项就将在当前日历年内给金融界造成 320 亿美元的损失。【ii】根据 Javelin Strategy &的研究,如今银行使用传统的基于规则的系统检测欺诈交易需要 40 多天。这些偏见的实际成本甚至更高,因为 20%的客户在经历诈骗后会更换银行。数据科学可以将这一挑战转化为机遇,促进实时索赔评估并提高欺诈检测的准确性。
然而,尽管过去十年有多个人工智能项目,传统银行和金融科技都无法消除这一挑战。在实践中,当前将数据科学应用于欺诈检测的工作遵循以下三种场景之一。异常检测建立在当前基于规则的系统之上,表明低效流程是问题所在,解决方案是检测不遵守财务流程的情况。第二种方法使用预测分析来探索购买行为,表明行为特征是欺诈检测的关键。最后,第三种方法试图利用支持向量机和神经网络的蛮力,这种方法基于这样一种信念,即规范分析的进步肯定会产生预期的结果。考虑到实际成本和潜在收益,管理层应该在哪个场景中投资?
商业价值矩阵
Kees Pronk 和我在几年前引入了 Business Value Matrix(BVM)来探索数据、环境和商业价值之间的关系。【iii】矩阵基于几个前提。经理和他们的客户看待数据的角度不同,因为他们看待价值的角度不同。数据的价值取决于企业战略、文化和背景。该算法的商业价值并不来自其精确性,而是来自其在解决客户挑战方面的用途。最后,管理者的工作不仅仅是分析数据,而是利用数据来激励行动。让我们检查一下矩阵的结构,然后应用它来评估欺诈检测中不同的基于数据的方案。
商业价值矩阵
业务价值矩阵(BVM)建议可以将数据汇总,以沿着三个轴探索客户的挑战:利益相关者认为价值来自哪里,他们在哪个汇总级别寻找价值证明,以及他们使用什么指标来建议成功。对于一个数据科学团队来说,矩阵可以用来提供三个可交付成果——探索人工智能项目的范围和企业文化之间的最佳匹配,建议他们需要在哪里向他们的利益相关者提供价值证明,以及提供可以激励管理层将数据转化为行动的指标。让我们依次探索每一把斧子。
*价值从何而来?*第一个问题强调了每个利益相关者如何确定问题,以及他或她认为解决方案在哪里。对于一些利益相关者来说,一个成功的企业是建立在良好记录的、组织良好的流程之上的——客户的挑战揭示了可以检测、阐明和解决的流程缺陷。对于其他利益相关者来说,成功的项目是围绕理解他们的内部和外部客户而建立的:业务挑战需要努力更好地理解推动人们想要与组织合作的目标、愿景和激励。最后,在第三种情况下,企业是围绕技术建立的,这是一种根深蒂固的信念,即信息和物理技术的创新本身就可以积极影响底线。考虑到他们对商业价值的特殊观点,涉众收集和监控不同种类的数据,作为组织当前问题和未来机会的证据。
*利益相关者在哪一级寻找价值证明?*第二个问题提出了提供概念证明的三种可能性。那些相信个人可以改变公司表现的人;个人生产力的数据是组织成功的代表。对其他人来说,一个企业的好坏取决于它最薄弱的环节——在这里,组织作为一个整体如何运作的数据是成功的最佳指标。最后,对于某些经理或股东来说,唯一真正的证据是市场本身——生产率指标必须根据客户满意度和市场份额来分析。这里的数据科学家需要了解哪些类型的数据对利益相关者来说是重要的,并在利益相关者寻求概念证明的层面上集中精力提供证据。
*利益相关者如何衡量成功?*对于一定数量的决策者来说,成功是一个效率问题,数据需要揭示相关活动的成本和收益。对于其他人来说,有效性是一个更好的衡量成功的标准——数据需要反映组织与其客户之间关系的质量。对于其他人来说,创新是关键指标,数据需要说明组织如何应对外部威胁和机遇。对于其他人来说,利用率提供了突出组织资源随着时间的推移使用得如何的主要度量。在每一种情况下,理解利益相关者如何衡量成功是提供数据的先决条件,这些数据将帮助他们将证据转化为个人或集体行动。
异常检测寻求组织流程
BVM 能帮助数据科学家改善信用卡欺诈吗?异常检测算法建立在传统的基于规则的方法之上。学习模型是在被标记为欺诈或合法的连续输入数据流上训练的。在测试阶段,人工代理会被告知偏离可接受模式的情况,以供审查。因此,该模型被训练为接受银行交易内容的“常态”基线。这个基线本身并不反映消费者行为,而是反映了对组织过程的遵守程度。异常检测将吸引专注于改进欺诈检测流程的利益相关者,而不是那些对更好地了解消费者行为或获得技术优势感兴趣的人。
图片来源:潘金淑·TRAN
自动化基于规则的欺诈检测
如果这里的价值在于改进过程,那么涉众会在哪里寻找概念证明呢?他们不太可能在个体的基础上寻找证据,这可能是在理解测试数据中真阳性的数量时获得的。也有可能,但也不太可能,他们会对使用异常检测来获得市场竞争优势感兴趣,因为无论数据模型多么高效,都不会对市场份额产生什么影响。最有可能为整个业务部门寻找概念证明:一个更好的过程将使所有员工受益,而不管他们的个人知识或贡献如何。
最后,对于对改进流程感兴趣的赞助商,他们将如何衡量成功?一方面,效率指标,包括减少欺诈交易或减少检查每笔交易的成本,肯定会受到欢迎。另一方面,度量有效性的指标在这里没有什么帮助,因为产生的数据对改善组织和客户之间的关系没有什么直接作用。同样,与创新相关的指标在这里也没有什么意义,因为目标是细化流程,而不是设计新的服务产品。为了迎合相信价值来自过程的涉众,异常检测通常需要在组织级别上提供围绕效率度量的概念证明。
聚类算法寻求改善对消费者行为的理解
哪种欺诈检测方法会吸引对了解消费者行为更感兴趣的利益相关方?预测分析利用模式聚类和/或神经网络来识别交易数据流中的异常值…包括 K 均值、基于密度或均值漂移聚类的方法可用于研究消费行为,允许机器学习模型阐明与零售购物或电子商务交易相关联的欺诈行为的足迹。探索消费模式将吸引赞助商,他们更感兴趣的是提高组织对消费者行为特殊性的理解,而不是标准化操作流程。
图片来源:Dhruv Sharma
探索消费者消费模式集群,了解欺诈行为
如果这里的价值集中于更好地理解人性,那么利益相关者在哪里寻找概念的证明呢?需要仔细考虑三种情况。使用人口统计和行为数据阐明个人消费模式的能力当然是一个优势,尽管对尊重消费者数据保护权的担忧可能会在这方面造成障碍。对整个市场进行概念验证可能被证明是不现实的,因为溢价很可能被放在组织为目标人群提供的个性化服务上。探索行为数据来识别消费者特征似乎是一个诱人的选择。
对于那些相信价值来自理解人类行为的利益相关者,他们将如何衡量成功。?效率指标在这里不太重要,因为它们衡量的是与现有组织流程相关的成本,而不是开发新市场和服务的机会。在描述组织和消费者之间的关系质量时,围绕服务提供的有效性的度量将具有更大的吸引力。最后,创新也将在设计新服务方面获得动力,使本组织能够更好地为其目标群体服务。开发人类和机器智能的预测分析为更相信人而不是过程或技术的利益相关者提供了一条有吸引力的前进道路。
单靠技术能提供竞争优势吗?
规范分析旨在为组织提供解决方案,在“欺诈签名”出现并造成持久损害之前识别它们。数据科学团队可以通过分析数亿笔交易并将其提交给预测分析引擎(集成随机森林、朴素贝叶斯、支持向量机等)来构建估算模型。)提供一旦发现欺诈行为应采取的措施的建议。规范分析从预测模型中获取输入,结合规则和基于约束的优化,自动做出决策。这种类型的机器学习有效地将“人类”判断从计算最高可能真阳性率(TPR)和最低可能假阳性率(FPR)的等式中去除。
图片来源:凯瑟琳·梅尔彻,罗莎丽亚·西里波
基于推荐代理的规定性分析
如果这里的价值主张是技术,那么利益相关者在哪里寻找概念证明呢?需要仔细考虑三种情况。识别个人消费模式的能力肯定是一个优势,尽管对尊重消费者数据保护权的担忧可能会带来障碍。采用说明性分析也可能会遭到员工和经理的抵制,他们认为欺诈检测是他们获得报酬提供的技能的一部分。尽管如此,向整个市场推销概念证明对寻找创造可持续竞争优势的途径的利益相关者来说还是很有吸引力的。
相信价值来自技术的赞助商将如何衡量成功?考虑到开发可行的欺诈检测解决方案的成本,很难确定效率指标。考虑到员工、消费者和监管机构可能的抵制,围绕技术有效性的衡量标准也没有多大意义。鉴于这些机器学习模型的透明度和可解释性的挑战,组织学习也是极不可能的。规范性技术的支持者只能用创新的标准来证明这些新的欺诈管理使用场景的价值。
结论
数据科学的终极目标不是分析数据,而是解决业务问题。成功的人工智能项目取决于管理能力,以将机器学习的潜力与企业战略、文化和可用资源中反映的组织现实相结合。商业价值矩阵(BVM)提供了一个框架,通过关注利益相关者对价值来源(流程、人员或技术)的理解来探索这些现实,他们在哪里寻找证明价值(在个人层面、组织或整个市场),以及他们如何定义成功(效率、有效性、利用率、创新……)。
我们探讨了 BVM 在欺诈管理中的潜在价值。基于异常检测、预测和规定分析的三种潜在方法对应于流程、人员和技术价值的对比视图。在通过矩阵探索利益相关方的观点时,数据科学团队可以提供他们的管理层正在寻找的数据,以增加其组织的价值。数据科学不是对每天捕获的大量数据做些什么,而是做些什么来帮助管理层将数据转化为行动。
— — -
【I】Denis co Rayome,(2019) 为什么 85%的人工智能项目会失败,Tech Republc
【iii】Schlenker,l .和 Pronk,K. (2009),价值之旅,Goodfellow
Lee Schlenker 是 BAI 的校长,也是商业分析和社区管理教授。他在 LinkedIn 上的个人资料可以在查看
用 GANs 将真实照片转化为艺术大师作品
创造力:人类独有的
艺术——创造原创事物的能力,利用一个人无限的创造力和想象力——这是我们人类愿意相信自己独有的东西。毕竟,就写实绘画而言,迄今为止还没有任何其他动物或计算机能与人类的艺术技巧相媲美。
我是说猴子们尽力了。这是…抽象艺术| 来源
即使人工智能最近取得了进步,计算机仍然在创造性方面苦苦挣扎。他们擅长计算、分类和执行编程任务,但他们永远无法真正达到人类的创造力水平。人类的创造力无疑是独一无二的,独一无二的……那是在生殖对立网络被构想出来之前。在 2014 年,关于 GANs 的原始论文提出了一个新的评估生成模型的系统——可以创建的模型——使用两个模型。
永恒的猫和老鼠的游戏
GAN 由两个模型组成,生成模型 G 和判别模型 D 以及真实数据集。你可以把 G 想象成造假者,努力制造越来越像真钱的钱 D 想象成 cop ,努力区分钱是真的还是假的。真实数据集充当 cop 与伪造者的输出进行比较的参考。
典型的甘氏架构| 来源
一开始,伪钞制造者会很糟糕,因为他不知道如何让这些钱看起来像真钱,但是在每次警察确定假钞后,他会变得更好,并从错误中学习。请记住,鉴别者在鉴别真币和假币方面也越来越好——他正在成为一名更好的警察。这种循环一遍又一遍地继续,直到生成器非常擅长创建看起来像训练数据的数据,甚至连鉴别器都分辨不出来。
使用循环 GANs 的图像-图像翻译
在创建新的、看起来相似的数据时,经典的 GAN 架构很好,但在尝试改变现有图像时,它就不那么好用了。此外,传统的图像-图像翻译方法需要带有成对示例的数据集。
同一地点冬季/夏季视图的配对示例| 来源
这些成对的例子是与直接相关的数据点——它们显示了原始图像和对它的期望修改。例如,训练数据集需要在冬季和夏季包含相同的景观。然而,这些数据集具有挑战性,并且难以准备——有时甚至是不可能的,艺术就是如此。根本没有蒙娜丽莎或其他伟大艺术品的摄影等价物。
为了解决这个问题,我们使用了一个**周期 GAN。**这是一种特殊类型的生成对抗网络,对 GAN 架构进行了扩展:循环一致性。这是一个概念,即第一个生成器输出的图像可以用作第二个生成器的输入,第二个生成器的输出应该与原始图像匹配——撤销第一个生成器对原始图像所做的操作。
一个循环 GAN 用于斑马到马的图像-图像翻译| 来源
可以这样想:你正在使用谷歌翻译把一些东西从英语翻译成西班牙语。然后你打开一个新的标签页,将西班牙语复制粘贴回谷歌翻译,谷歌翻译会将其翻译成英语。在这一切的最后,你会再次期待原来的句子。这是一个循环 GAN 的原理,它作为一个附加损耗来衡量第二个发生器产生的输出与原始图像之间的差异, 而不需要 成对的例子。
用于风格转换的循环 GAN 架构
如果我们的模特被训练成法国艺术家塞尚的风格的图像,那么甘的循环是如何工作的。
数据集
- 数据集 1 :真实照片
- 数据集 2 :塞尚的艺术作品
生成对抗网络
- 甘 1 :将真实照片(数据集 1)转换成塞尚风格的艺术品(数据集 2)
- 甘 2 :将塞尚风格的艺术品(数据集 2)转化为真实的照片(数据集 1)
正向周期一致性损失
- 将真实照片(集合 1)输入到 GAN 1
- 来自 GAN 1 的塞尚风格艺术品的输出照片
- GAN 1 至 GAN 2 生成的塞尚风格艺术品的输入照片
- 从 GAN 2 输出真实照片
- 使用鉴别器将真实照片(集合 1)与从 GAN 2 输出的真实照片进行比较
反向循环一致性丧失
- 将塞尚艺术品(收藏 2)的照片输入到 GAN 2
- 从 GAN 2 输出真实照片
- 将 GAN 2 生成的真实照片输入到 GAN 1
- 从 GAN 1 输出塞尚风格的艺术品
- 使用鉴别器将塞尚原画(系列 2)与甘 1 的塞尚风格作品进行比较
通过最大限度地减少这两种损失,我们的模型最终将学会如何将真实的照片转化为塞尚风格的艺术品——由于我们有不止一个而是两个甘,我们也可以将塞尚的原始艺术品转化为真实的照片。如果我们希望我们的模型将我们的照片转换成另一位艺术家的风格,我们只需要将数据集 2 中的塞尚作品替换为另一位艺术家的作品,比如梵高或毕加索。
真的——我们能够用甘斯做的只是坚果。我的意思是,即使对于一个人来说,试图模仿一个艺术家的风格,并以一张照片作为灵感,也是一件令人生畏的任务。有些人把他们的一生都奉献给了这项工作,但对于一个甘来说,他们可以在几分钟内将任何训练有素的风格应用到任何图片上。坚果!
结果呢
创造性计算的未来
虽然称它们为杰作可能有点夸张,但毫无疑问,在我们曾经认为安全的东西方面,人工智能正在迅速赶上人类——艺术天赋和创造力。我只解释了 GANs 的一个应用,我个人认为它很有趣,但 GANs 现在被用于无数的方面,从生成逼真的人脸到提高图像质量——在这一切的核心,只是一个骗子和警察之间的游戏。
用 Python 将文本文件转换成数据表
从任何文本文件中提取信息的可重用方法
Maksym Kaharlytskyi 在 Unsplash 上的照片
在本文中,我描述了如何将一组文本文件转换成可用于自然语言处理和机器学习的数据表。为了展示我的方法,我使用了 D. Greene 和 P. Cunningham 在 2006 年发表的原始 BBC 新闻文章数据集。
在进入 IDE 并开始编码之前,我通常会遵循一个过程,包括理解数据、定义输出以及将所有内容翻译成代码。我认为编码前的任务通常是最重要的,因为它们有助于更有效地构建和遵循编码过程。
我这个项目的三步流程
1.数据理解
在能够从文本文件中提取任何信息之前,我们希望知道它的信息是如何构造的,以及文本文件是如何存储的以及存储在哪里(例如,名称、目录)。
结构
为了理解这种结构,我们看一下一些文本文件,了解一下数据的结构。
Claxton hunting first major medal
British hurdler Sarah Claxton is confident she can win her first major medal at next month's European Indoor Championships in Madrid.
The 25-year-old has already smashed the British record over 60m hurdles twice this season, setting a new mark of 7.96 seconds to win the AAAs title. "I am quite confident," said Claxton. "But I take each race as it comes. "As long as I keep up my training but not do too much I think there is a chance of a medal." Claxton has won the national 60m hurdles title for the past three years but has struggled to translate her domestic success to the international stage.
...
在新闻文章的上下文中,可以容易地假设第一和第二部分分别对应于标题和副标题。以下各段是文章的正文。查看示例数据,我们还发现这些片段由新行分隔,这些新行可用于拆分文本。
储存;储备
要编写一个自动运行每个文本文件的脚本,我们需要知道文本文件是如何存储的。因此,我们对目录的命名和组织感兴趣。潜在地,我们需要重新构造东西,这样我们就可以更容易地遍历文件。
文本文件的命名和组织
幸运的是, BBC 新闻数据集已经为自动化信息提取构建好了结构。从上面的截图中可以看出,文本文件根据它们的类型存储在不同的目录中。每个流派的名称也是相似的,由前导零(如果文件号小于 100)、文件号和“.”组成。txt”。
2.输出定义
基于数据理解步骤的见解,我们可以定义输出中应该包含哪些信息。为了确定输出,我们必须考虑上一步的学习,并考虑输出的潜在用例。
基于我们可能从文本文件中提取的信息,我提出了两种不同的机器学习训练用例:
- 文本分类(基于文本的类型预测)
- 文本生成(基于文本的标题或副标题生成)
为了满足这两个潜在用例的需求,我建议提取以下信息。
文本文件信息抽取的目标输出
我还将包括文本的长度(以记号的数量表示),以便以后更容易过滤更短或更长的文本。为了存储提取的数据,我建议使用制表符分隔值(.tsv)文件,因为逗号或分号可以出现在文本列中。
3.编码
由于前面的步骤,我们知道我们正在处理的数据以及在转换过程结束时我们想要输出什么样的信息。现在你可能知道了,我喜欢把任务分成更小的部分。编码步骤也不例外:)通常,我会将编码分成至少三个不同的部分,并将它们包装在单独的函数中:
- 读取和分割文件
- 提取信息
- 构建数据框
为了使这个新闻文章提取器可重用,我创建了一个实现这些函数的新类。
读取和分割文件
为了用 python 读取文件,我们需要由目录和文件名组成的相应路径。正如我们在数据理解步骤中观察到的,文件存储在它们对应的流派目录中。这意味着要访问一个文件,我们需要基本路径(对我来说是“数据”)、类型和名称。
如果文件存在,我们要读取它,用新的行字符(’ \n ')分割它,过滤空字符串,并将剩余的文本部分作为列表返回。在文件不存在的情况下(例如,文件数大于可用文件数),我们希望返回一个空列表。如果文件不存在,我更喜欢这样做,而不是使用异常或不返回任何异常。
def read_and_split_file(self, genre: str, file_name: str) -> list:
text_data = list()
current_file = os.path.abspath(os.path.join('data', genre, file_name))
if os.path.exists(current_file):
open_file = open(current_file, 'r', encoding="latin-1")
text_data = open_file.read().split('\n')
text_data = list(filter(None, text_data))
return text_data
正如您在上面的代码中看到的,使用操作系统包。因此,我们需要导入这个包。
提取信息
为了提取文本文件的信息并为下一步做好准备,我建议对每种体裁都这样做。这意味着,我们循环遍历相应流派目录中的每个文件。通过保存一个 current_number 变量,我们可以用前导零来格式化文件名,然后通过调用上面实现的方法来读取和拆分文件。
如果返回的列表是空的,我们想要停止循环,因为这意味着我们到达了循环的末尾,并且在目录中没有任何新的文件。
否则,我们将阅读和拆分功能返回的信息添加到特定的数据容器中,如标题、副标题和文本。因为我建议在最终输出中也提供文本的令牌计数,所以我们可以使用 nltk 包对文本进行令牌化,并将令牌列表的长度添加到 token_counts 列表中。最后,我们将 current_number 加 1,继续提取下一个文件。
def extract_genre_files(self, genre: str) -> pd.DataFrame:
found = True
current_number = 1
titles = list()
subtitles = list()
texts = list()
token_counts = list()
while found:
file_name = "{:03d}.txt".format(current_number)
text_data = self.read_and_split_file(genre, file_name)
if len(text_data) != 0:
titles.append(text_data[0])
subtitles.append(text_data[1])
article_text = ' '.join(text_data[2:])
texts.append(article_text)
token_counts.append(len(nltk.word_tokenize(article_text)))
current_number += 1
else:
found = False
genres = [genre] * len(titles)
data = {'genre': genres, 'title': titles, 'subtitle': subtitles, 'text': texts, 'token_counts': token_counts}
data_frame = pd.DataFrame(data)
return data_frame
在完成类型文件的循环之后,我们基于存储在特定列表中的提取信息创建一个数据帧。与上一步类似,我们需要导入两个包(nltk 和 pandas)。还请确保您已经下载了 nltk 包的“punkt”数据,因为它是标记文本所必需的。
import nltk
# nltk.download('punkt')
import pandas as pd
构建数据框
在最后一步,我们必须在现有流派上创建一个循环,通过调用上面实现的方法提取每个流派的信息,连接每个流派的输出,最后将连接的数据帧保存为带有所需分隔符的 csv。
def transform_texts_to_df(self, name, genre_list, delimiter = '\t'):
article_df_list = list()
for genre in genre_list:
article_df_list.append(self.extract_genre_files(genre))
df = pd.concat(article_df_list)
df.to_csv(name, sep=delimiter)
return df
在实现了类及其方法之后,我们需要创建 ArticlecsvParser 类的一个实例,并调用 transform_texts_to_df 方法,方法是为生成的 CSV 提供所需的名称和一个包含所有流派的列表。就这样。
if __name__ == "__main__":
genre_list = ['business', 'entertainment', 'politics', 'sport', 'tech']
parser = ArticleCSVParser()
df = parser.transform_texts_to_df('bbc_articles.csv', genre_list)
print(df.head())
结论
在本文中,我展示了如何将文本文件转换成数据帧,并将其保存为 csv/tsv。为了对不同的数据集重用该类,只需创建一个从 ArticleCSVParser 继承的新类,并覆盖必须更改的方法。
您也可以在这个存储库中找到完整的代码和数据集。
我希望你喜欢并快乐编码!
如何转换熊猫数据框架中的变量
衍生和修改变量的方法,使其符合目的
无论是准备数据以提取洞察力,还是为模型设计特征,我认为处理数据的个人的基本技能之一是将数据可靠地转换为所需格式的能力。作为数据清理、数据准备、数据管理、数据操作、数据争论、数据丰富、数据预处理(咻!😅),经常需要将变量/列/特性转换成更合适的形式。换句话说,原始数据往往需要改头换面✨才能更有用。
在 Unsplash 上 Leone Venter 拍摄的照片
0.Python 设置🔧
我假设读者(👀是的,你!)可以访问并熟悉 Python,包括安装包、定义函数和其他基本任务。如果你是 Python 的新手,这个是一个入门的好地方。
我已经在 Jupyter Notebook 中使用并测试了 Python 3.7.1 中的脚本。在我们开始推导之前,让我们确保你有正确的工具。
⬜️确保安装了所需的软件包:熊猫和 nltk
我们将使用以下强大的第三方软件包:
- numpy: 数值计算库和
- 熊猫:数据分析库。
1.数据📦
为了使事情易于管理,我们将创建一个小的数据框架,它将允许我们在下一节中监视每个任务的输入和输出。在一个假设的世界里,我收集了一些弹珠🔮,让我们假设下面的数据框包含我拥有的每种弹珠的详细信息。(嘶!您可能需要将代码复制到 Jupyter 笔记本或代码编辑器中,以获得更好的格式。)
# Import packages
import numpy as np
import pandas as pd# Update default settings to show 2 decimal place
pd.options.display.float_format = '{:.2f}'.format# Create a small dataframe
df = pd.DataFrame({'name': ['bob 2012', 'Ava 2013', 'Aby 007', 'XYZ 8', 'GRZ x7', 'Boo VIII', 'Joy 2020'],
'p_date': ['2020-02-01', '2020-05-01', '2020-06-30', '2020-04-15', '2020-01-04', '2020-03-21', '2020-07-08'],
'count': [80, 30, 10, 60, 40, 20, np.nan],
'colour': ['pink', 'teal', 'velvet', 'pink', 'green', 'teal', 'pink'],
'radius': [1, 2, 3, 4, 5, 6, 7],
'unit': ['cm', 'inch', 'cm', 'cm', 'inch', 'cm', 'cm']})
df
1.1.数据字典📘
每行代表一种大理石。列定义为:
◼ **名称:**每颗弹珠的名称(第一部分是型号名称,第二部分是版本)
◼ **购买日期:**我购买一种弹珠的日期
◼ **计数:**我拥有多少颗特定种类的弹珠
◼ **颜色:**该种类的颜色
◼ **半径:**该种类的半径测量值(对🙊)
◼ **单位:**单位为半径
如果这没有多大意义,不要太担心,因为这只是一个玩具数据。
2.转换变量🎨
在这一节中,我们将看一些转换不同数据类型的例子。我们将创建包含转换的新列,以便原始变量不会被覆盖。在可能的情况下,提供了实现相同转换的替代代码以供参考。从这些备选方案列表中,希望您能找到一两个技巧,用于日常数据操作。现在,是时候改头换面了!✨
请注意,所示的一些方法的底层逻辑可以应用于任何数据类型。
2.1.转换数值变量
🔦**类型:**基于两个条件创建一个条件变量。
🔒任务: 半径不能直接跨种类比较,因为它们用不同的单位表示。让我们创建一个变量,以厘米为单位显示半径,以保持一致性。
**🔑答:我们将调用新变量 radius_cm 。假设 1 英寸等于 2.54 厘米,我们可以将条件总结如下:
1)如果单位是“厘米”,则radius _ cm = radius 2)如果单位是“英寸”,则*radius _ cm =2.54 radius
我们可以使用下面的脚本创建半径 _ 厘米:
# Method using np.where
df['radius_cm'] = np.where(df['unit']=='inch', 2.54 * df['radius'], df['radius'])# ============== ALTERNATIVE METHODS ==============
## Method using np.select
# df['radius_cm'] = np.select([df['unit']=='inch'], [2.54 * df['radius']], default=df['radius'])## Method applying lambda function with if
# df['radius_cm'] = df[['radius', 'unit']].apply(lambda x: 2.54 * x[0] if x[1]=='inch' else x[0], axis=1)## Method A using loc
# df.loc[df['unit']!='inch', 'radius_cm'] = df['radius']
# df.loc[df['unit']=='inch', 'radius_cm'] = 2.54 * df['radius']## Method B using loc (works as long as df['radius'] has no missing data)
# df['radius_cm'] = df['radius']
# df.loc[df['unit']=='inch', 'radius_cm'] = 2.54 * df['radius']# Inspect results
df
**💡快速提示:**要在 Jupyter 笔记本中注释或取消注释代码,如果你还不知道的话,选择一段代码并使用[Ctrl/Cmd + /]快捷键。
_________________________________________________________________🔦**类型:**根据 2 个条件创建一个条件变量(分类)。
**🔒任务:**根据其半径创建一个描述大理石尺寸的变量,单位为厘米。
**🔑答:**我们将新变量的大小叫做。让我们将“大”定义为半径为 5 厘米或更高的弹珠,任何低于 5 厘米的则定义为“小”。所以条件是:
1)如果 radius_cm ≥ 5 那么 size = '大’2)如果 radius_cm < 5 那么 size = ‘小’
我们可以使用下面的脚本创建大小:
# Method applying lambda function with if
df['size'] = df['radius_cm'].apply(lambda x: 'big' if x>=5 else 'small')# Inspect results
df.sort_values('radius_cm')
为了避免重复,我没有为这个任务提供任何替代方法,因为第一个任务中的任何方法都可以在这里使用。
**📌练习:**通过引用第一个任务中显示的方法,尝试使用不同的方法进行相同的转换。
🔦**类型:**创建一个计算变量。
**🔒任务:**为弹珠计算球体体积。
➗公式:
**🔑答:**我们可以使用下面的脚本创建卷:
# Method using eval
pi = np.pi
df.eval("volume = 4/3 * @pi * radius**3", inplace=True)# ============== ALTERNATIVE METHOD ==============
## Method using simple operators
# df['volume'] = 4/3 * np.pi * df['radius']**3## Method using pandas wrappers
# df['volume2'] = df['radius'].pow(3).mul(4/3*np.pi)## Short description for pow, mul and a few other wrappers:
## add: for addition
## sub: for subtractaction
## mul: for multiplication
## div: for division
## pow: for exponential power (Tip: can use pow(0.5) or pow(1/2) for square root)# Inspect results
df
_________________________________________________________________🔦**类型:**将数值分割成等宽的条块(Discritise)。
**🔒任务:**创建一个变量,根据它们的计数将弹珠分成两个等宽的格子。
**🔑答:**我们将新变量称为 cut 。
◼最小计数 = 10
◼最大计数 = 80
◼范围计数 =最大—最小= 70
◼箱宽度=范围/箱数= 70 / 2 = 35
由于计数的范围是从 10 到 80 个弹珠,具有 2 个箱将意味着第一个箱是 10 到 45,第二个箱是 45 到 80 目前,我们已经用默认设置right=True
将面元定义为包含最右边的边。这意味着,如果我们有 45 个弹珠,它将落入较低的箱子(即 10–45)。通过在下面的函数中添加right=False
,我们可以很容易地改变这种行为,以排除最右边的边缘。也请参见我在下一个任务中的注释。
我们可以使用下面的脚本创建切割:
# Method using pandas.cut
df['cut'] = pd.cut(df['count'], bins=2, labels=['bin1', 'bin2'])# Inspect results
df[['count', 'cut']].sort_values('count')
🔦**类型:**将数值分割成大小相等的块(Discritise)。
**🔒任务:**创建一个变量,根据弹球的计数,将弹球分成两个大小相等的桶(即分位数)。
**🔑答:**我们将调用新变量 qcut 。
◼排序计数升序:10、20、30、40、60、80
◼ #记录= 6
◼ #分位数= 2
◼ #每个分位数的记录数= #记录数/ #分位数= 6 / 2 = 3
因为 c 计数中有 6 个非缺失值,具有相等大小的存储桶意味着第一个分位数将包括:10、20、30 和
值得注意的是,如果您有兴趣查看等宽或等大小箱子的确切分界点,一种方法是从函数中省去label
参数。查看精确的截止点将使离散化时如何处理边缘上的点变得清晰。
所以无论如何…回到 qcut ,我们可以使用下面的脚本创建它:
# Method using pandas.qcut
df['qcut'] = pd.qcut(df['count'], q=2, labels=['q1', 'q2'])# Inspect results
df[['count', 'cut', 'qcut']].sort_values('count')
注意到切割和q 切割的区别了吗?
2.2.转换分类变量或字符串变量
🔦**类型:**基于 3+条件(组)创建一个条件变量。
**🔒任务:**创建一个变量,将粉色缩写为“PK”,将蓝绿色缩写为“TL”,将所有其他颜色(天鹅绒和绿色)缩写为“OT”。
**🔑答:**我们将新的变量叫做 colour_abr 。所以条件是:
1)如果颜色是‘粉色’那么颜色 _ ABR=‘PK’2)如果颜色是‘蓝绿色’那么颜色 _ ABR =‘TL’
3)如果颜色不是‘天鹅绒’就是‘绿色’那么颜色 _ ABR=‘OT’。
我们可以使用下面的脚本创建 colour_abr :
# Method using replace
df['colour_abr'] = df['colour'].replace({'pink': 'PK', 'teal': 'TL', 'velvet': 'OT', 'green': 'OT'})# ============== ALTERNATIVE METHODS ==============
## Method A using map
# mapping = {'pink':'PK', 'teal': 'TL', 'velvet': 'OT', 'green': 'OT'}
# df['colour_abr'] = df['colour'].map(mapping)## Method B using map (works as long as df['colour'] has no missing data)
# df['colour_abr'] = df['colour'].map({'pink':'PK', 'teal': 'TL'})
# df['colour_abr'].fillna('OT', inplace=True)## Method using nested np.wheres
# df['colour_abr'] = np.where(df['colour']=='pink', 'PK', np.where(df['colour']=='teal', 'TL', 'OT'))## Method using np.select
# df['colour_abr'] = np.select([df['colour']=='pink', df['colour']=='teal'], ['PK', 'TL'] , default='OT')## Method applying lambda function with nested ifs
# df['colour_abr'] = df['colour'].apply(lambda x: 'PK' if x=='pink' else ('TL' if x=='teal' else 'OT'))## Method using list comprehension
# df['colour_abr'] = ['PK' if x=='pink' else ('TL' if x=='teal' else 'OT') for x in df['colour']]## Method A using loc
# df.loc[df['colour'] == 'pink', 'colour_abr'] = 'PK'
# df.loc[df['colour'] == 'teal', 'colour_abr'] = 'TL'
# df.loc[df['colour'].isin(['velvet', 'green']), 'colour_abr'] = 'OT'## Method B using loc (works as long as df['colour'] has no missing data)
# df['colour_abr'] = 'OT'
# df.loc[df['colour'] == 'pink', 'colour_abr'] = 'PK'
# df.loc[df['colour'] == 'teal', 'colour_abr'] = 'TL'# Inspect results
df[['colour', 'colour_abr']].sort_values('colour_abr')
如果我们只是重命名类别而不是分组,我们也可以使用下面中的任何一种方法。cat 访问器除了上面显示的方法之外:
# Create a copy of colour and convert type to category
df['colour_abr'] = df['colour'].astype('category')# Method using .cat.rename_categories
df['colour_abr'].cat.rename_categories({'green':'GN', 'pink':'PK', 'teal':'TL', 'velvet': 'VT'}, inplace=True)# ============== ALTERNATIVE METHOD ==============
## Method using .cat.categories
## Make sure to get the order of the categories right
## Check the order with by running df['colour_abr'].cat.categories
# df['colour_abr'].cat.categories = ['GN', 'PK', 'TL','VT']# Inspect results
df[['colour', 'colour_abr']].sort_values('colour_abr')
有关的更多信息,请参见本文档。卡特彼勒存取器。🐱
🔦**类型:**解析字符串(从字符串中提取一部分)。
**🔒任务:**解析名称,这样我们就有了型号和版本的新列。
**🔑答:**我们现在将使用中的一个方法。str 访问器提取部件:
# Method using .str.split
df[['model', 'version']] = df['name'].str.split(' ', expand=True)# ============== ALTERNATIVE METHOD ==============
## Method applying lambda function
# df['model'] = df['name'].apply(lambda x: x.split(' ')[0])
# df['version'] = df['name'].apply(lambda x: x.split(' ')[1])# Inspect results
df[['name', 'model', 'version']]
🔦**类型:**连接或合并列(与上述任务相反)。
**🔒任务:**将模型(大写)和半径中的值合并到一个新列中。
**🔑答案:**我们现在将使用下面的脚本来连接:
# Method using + operator
df['mod_rad'] = df['model'].str.upper() + '_' + df['radius'].astype(str)# ============== ALTERNATIVE METHOD ==============
## Method using chained .str methods
# df['mod_rad'] = df['model'].str.upper().str.cat(df['radius'].astype(str), sep="_")# Inspect results
df
关于的更多信息,参见本文档。字符串存取器。🎻
2.3.转换日期时间变量
🔦**类型:**解析日期时间(从日期时间中提取一部分)。
**🔒任务:**提取一周的天,以及购买的年。
**🔑答:**我们现在将使用中的方法。dt 存取器提取零件:
# Convert type to datetime
df['p_date'] = pd.to_datetime(df['p_date'])# Method using .dt.day_name() and dt.year
df['p_dname'] = df['p_date'].dt.day_name()
df['p_year'] = df['p_date'].dt.year# Inspect results
df[['p_date', 'p_dname', 'p_year']]
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _**📌练习:**尝试从 p_date 中提取月和日,并找出如何将 p_year 、 p_month 、 p_day 组合成一个日期。
关于的更多信息,参见本文档。dt 存取器。🕟
通过滚动左边的窗格到这里,您可以浏览前面讨论的访问器的可用方法。当心熊猫。Series.xxx.yyy 其中 xxx 可以替换为 cat 、 str 或 dt,yyy 是指方法。
3.把所有东西放在一起📃
现在我们将熟悉*赋值,*它允许我们一次创建多个变量。测试完成后,我们可以结合如下步骤:
# Convert type to datetime
df['p_date'] = pd.to_datetime(df['p_date'])# Derive variables
df = df.assign(radius_cm = np.where(df['unit']=='inch', 2.54 * df['radius'], df['radius']), # Referenced radius as radius_cm hasn't been created yet
size = list(map(lambda r, u: 'big' if ((u=='cm') & (r>=5)) | ((u=='inch') & (2.54*r>=5))
else 'small', df['radius'], df['unit'])),
volume = 4/3 * np.pi * df['radius']**3,
cut = pd.cut(df['count'], bins=2, labels=['bin1', 'bin2']),
qcut = pd.qcut(df['count'], q=2, labels=['q1', 'q2']),
colour_abr = df['colour'].replace({'pink': 'PK', 'teal': 'TL', 'velvet': 'OT', 'green': 'OT'}),
# If you find a way to combine the following 2 lines in 1 line inside assign(), feel free to teach me how
model = df['name'].str.split(' ', expand=True)[0],
version = df['name'].str.split(' ', expand=True)[1],
mod_rad = df['name'].str.split(' ', expand=True)[0].str.upper() + '_' + df['radius'].astype(str),
p_dname = df['p_date'].dt.day_name(),
p_year = df['p_date'].dt.year)# Inspect results
df
这个脚本看起来是不是有点忙乱?如果你一行一行地看,你会发现每一行都是我们从第 2 节中学到的代码的一个稍微变形的版本。
最后一点,在创建变量时,如果你犯了一个错误,你可以用正确的变量覆盖不正确的变量,或者使用⛔️:下面的脚本删除它
# Method A using del
del(df['var_name'])# ============== ALTERNATIVE METHODS ==============
## Method B using del
# del df['var_name']## Method using drop
# df.drop(columns='var_name', inplace=True)
您想访问更多这样的内容吗?媒体会员可以无限制地访问媒体上的任何文章。如果您使用 我的推荐链接成为会员,您的一部分会费将直接用于支持我。
谢谢你看我的帖子。我希望你已经学到了一些东西,✂️.如果你感兴趣,这里有我其他一些帖子的链接:
◼️️NLP 介绍—第 1 部分:Python 中的预处理文本
◼️NLP 介绍—第 2 部分:引理满足和词干化的区别
◼️NLP 介绍—第 3 部分:TF-IDF 解释
◼️NLP 介绍—第 4 部分:Python 中的监督文本分类模型
继续改造!保持,保持变换变量!🎤(跟着唱!🙋)
你继续改造!你继续,继续变换变量!🎶
继续改造!保持,保持变换变量!🎵
去转换你的数据…🎼
你猜到我的歌参考了吗?(提示: L[a-z]{4} )
再见🏃💨