文章目录
前言
This week, I analysed an paper about the Natural language processing ,and the article proposes a novel model BERT(Bidirectional Encoder Representations from Transformers). BERT is designed to pretrain deep bidirectional representations from unlabeled text by jointly conditioning on both left and right context in all layers. And, the model adopts an additional sentence-level continuity prediction task, Next Sentence Prediction (NSP), which is based on the bidirectional language model.At the same time, I learned machine learning algorithms . I learned about the Elastic Net Regression Algorithm and Logistic Regression Algorithm in this week .
在论文学习之前,先了解几个概念:预训练(迁移学习)和微调(Fine tune);学习地址:预训练
一、论文阅读《BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding》
摘要
BERT:一个语言表征模型,利用transformer的encoder来进行预训练。BERT模型的全称是Bidirectional Encoder Representations from Transformers,它是一种新型的语言模型。之所以说是一种新型的语言模型,是因为它通过联合调节所有层中的双向Transformer来训练预训练深度双向表示。BERT旨在通过在所有层中联合调节左右上下文来预训练未标记文本的深度双向表示。因此,只需要一个额外的输出层,就可以对预训练的BERT模型进行微调,从而为各种任务(如问题回答和语言推断)创建最先进的模型,而无需对特定于任务的体系结构进行实质性修改。
Introduction
现有两种将预训练语言表示应用于下游任务的策略:基于特征的和微调。基于特征的方法,如ELMo (Peters等人,2018a),使用特定于任务的架构,其中包括预先训练的表示作为额外的特征。微调方法,如生成式预训练变压器(OpenAI GPT) (Radford等人,2018),引入最小的任务特定参数,并通过简单地微调所有预训练参数在下游任务上进行训练。这两种方法在预训练过程中共享相同的目标函数,其中它们使用单向语言模型来学习一般的语言表示。(这些模型目前都没有学习,日后会继续恶补)
BERT通过使用“蒙面语言模型”(MLM,masked language model)预训练目标缓解了前面提到的单向约束,该目标受到‘完形填空任务’(完形的任务)(Taylor, 1953)的启发。屏蔽语言模型从输入中随机屏蔽一些标记,目标是仅根据上下文预测屏蔽词的原始词汇表id。与从左到右的语言模型预训练不同,MLM目标使表示能够融合左右上下文,这允许我们预训练一个深度双向Transformer。除了屏蔽语言模型,我们还使用了“下一句预测”任务,联合预训练文本对表示。
除了屏蔽语言模型,我们还使用了“下一句预测”任务,联合预训练文本对表示。
1、token Embeddings:表示的是词向量,第一个单词是CLS标志,可以用于之后的分类任务,对于非分类任务,可以忽略词向量;
2、Segment Embeddings:句子向量,用来区别两种句子,因为预训练不只做语言模型还要做以两个句子为输入的分类任务;
3、Position Embeddings:位置向量,是通过模型学习得到的。
BERT主要贡献
- 文中证明了双向预训练对语言表征的重要性。与Radford等人(2018)使用单向语言模型进行预训练不同,BERT使用掩码语言模型来实现预训练的深度双向表示。
- 实验表明,预训练的表示减少了对许多高度工程化的特定于任务的架构的需求(heavily-engineered taskspecific architectures)。BERT是第一个基于优化的表示模型,它在大量句子级(sentence-level)和令牌级(token-level)任务上实现了最先进的性能,优于许多特定于任务的体系结构。
BERT : pre-training and fine-tuning
在预训练期间,模型在不同的预训练任务中使用未标记的数据进行训练。为了进行微调,BERT模型首先使用预训练的参数进行初始化,然后使用来自下游任务的标记数据对所有参数进行微调。每个下游任务都有单独的微调模型,即使它们是用相同的预训练参数初始化的。
**Model Architecture :**BERT的模型架构是一个多层双向变压器编码器,基于Vaswani等人(2017)中描述的原始实现,并在tensor2tensor库中发布.
文中使用了两个模型,其尺寸的结果:BERTBASE (L=12, H=768, A=12,总参数=110M)和BERTLARGE (L=24, H=1024, A=16,总参数=340M)。
**:BERTBASE :**为了进行比较,选择BERTBASE与OpenAI GPT具有相同的模型大小。然而,关键的是,BERT Transformer使用双向自注意,而GPT Transformer使用约束自注意,其中每个令牌只能关注其左侧的上下文。
为了使BERT处理各种下游任务,我们的输入表示法能够在一个标记序列中明确地表示单个句子和一对句子。
在整个作品中,“句子”可以是连续文本的任意跨度,而不是一个实际的语言句子。“序列”指的是BERT的输入标记序列,它可能是一个句子,也可能是两个句子组合在一起。
Masked LM(MLM):双向的语言模型任务
为了训练深度双向Transformer表示,采用了一种简单的方法:随机掩盖部分输入词,然后对那些被掩盖的词进行预测,此方法被称为“Masked LM”(MLM)。预训练的目标是构建语言模型,BERT模型采用的是bidirectional Transformer。bidirectional Transformer不仅要才处理左边的语言的连续性,也要考虑右边文章的相关性。在这种情况下,与掩码标记相对应的最终隐藏向量被送入词汇表上的输出softmax,就像在标准LM中一样。
把一篇文章中,15% 的词汇遮盖,让模型根据上下文全向地预测被遮盖的词。假如有 1 万篇文章,每篇文章平均有 100 个词汇,随机遮盖 15% 的词汇,模型的任务是正确地预测这 15 万个被遮盖的词汇。通过全向预测被遮盖住的词汇,来初步训练 Transformer 模型的参数。
简而言之:一开始先屏蔽掉输入词汇的 15%,然后通过深度双向的 Transformer 编码器运行整个序列,最后预测屏蔽的单词。
例如
Input: the man went to the [MASK1] . he bought a [MASK2] of milk.
Labels: [MASK1] = store; [MASK2] = gallon
在 Cloze task 中,需要mask掉一些词,这样预训练出来的模型是有缺陷的,因为在其它任务中不能mask掉这些词。而BERT通过随机的方式来解决了这个缺陷:80%加Mask,10%用其它词随机替换,10%保留原词。这样模型就具备了迁移能力。
Next Sentence Prediction(NSP):连贯性判断任务
许多重要的下游任务,如问答(QA)和自然语言推理(NLI),都是基于对两个句子之间关系的理解,而语言建模并不能直接捕捉到这一点。为了训练一个能够理解句子关系的模型,我们对一个二元化的下一句预测任务进行了预训练,该任务可以从任何单语言语料库中简单地生成。具体来说,在为每个预训练示例选择句子A和B时,50%的时间B是A之后的实际下一句句子(标记为IsNext), 50%的时间B是语料库中的随机句子(标记为NotNext)。
譬如从上述 1 万篇文章中,挑选 20 万对语句,总共 40 万条语句。挑选语句对的时候,其中 210 万对语句是连续的两条上下文语句,另外 210 万对语句不是连续的语句。然后让 Transformer 模型来识别这 20 万对语句,哪些是连续的,哪些不连续。
BERT的全面预培训和微调程序。除了输出层之外,在预训练和微调中也使用了相同的架构。相同的预训练模型参数用于初始化不同下游任务的模型。在微调过程中,对所有参数进行微调。[CLS]是在每个输入示例前添加的一个特殊符号,[SEP]是一个特殊的分隔符(例如分隔问题/答案)。
实验比较
ELMo、GPT、BERT都是近几年提出的模型,在各自提出的时候都取得了不错的成绩。并且相互之间也是相辅相成的关系。
Word2vec作为里程碑式的进步,对NLP的发展产生了巨大的影响,但Word2vec本身是一种浅层结构,而且其训练的词向量所“学习”到的语义信息受制于窗口大小,因此后续有学者提出利用可以获取长距离依赖的LSTM语言模型预训练词向量,而此种语言模型也有自身的缺陷,因为此种模型是根据句子的上文信息来预测下文的,或者根据下文来预测上文,直观上来说,我们理解语言都要考虑到左右两侧的上下文信息,但传统的LSTM模型只学习到了单向的信息。
Conclusion
BERT利用Transformer的encoder来进行预训练,BERT就是在OpenAI的GPT的基础上对预训练的目标进行了修改,并用更大的模型以及更多的数据去进行预训练,从而得到了目前为止最好的效果。
BERT的作者提出了采用MLM的方式来训练语言模型;BERT在双向语言模型的基础上额外增加了一个句子级别的连续性预测任务。
参考文章:文章
二、机器学习算法(六)- 弹性网络回归算法(Elastic Net Regression Algorithm)
上周学习了岭回归与 Lasso 回归两种正则化的方法,当多个特征存在相关时,Lasso 回归可能只会随机选择其中一个,岭回归则会选择所有的特征。于是提出了集合两种方法优势的算法,另一种正则化的方法—弹性网络回归算法(Elastic Net Regression Algorithm)。
模型介绍
弹性网络回归算法的代价函数结合了 Lasso 回归和岭回归的正则化方法,通过两个参数 λ 和 ρ 来控制惩罚项的大小。
同样是求使得代价函数最小时 w 的大小:
可以看到,当 ρ = 0 时,其代价函数就等同于岭回归的代价函数,当 ρ = 1 时,其代价函数就等同于 Lasso 回归的代价函数。与 Lasso 回归一样代价函数中有绝对值存在,不是处处可导的,所以就没办法通过直接求导的方式来直接得到 w 的解析解,不过还是可以用坐标下降法2(coordinate descent)来求解 w。
算法步骤
坐标下降法
坐标下降法的求解方法与 Lasso 回归所用到的步骤一样,唯一的区别只是代价函数不一样。
然后具体步骤和上周学习的步骤一样,遍历所有权重系数,依次将其中一个权重系数当作变量,其他权重系数固定为上一次计算的结果当作常量,然后不断迭代,直至收敛。
代码实现
import numpy as np
def elasticNet(X, y, lambdas=0.1, rhos=0.5, max_iter=1000, tol=1e-4):
"""
弹性网络回归,使用坐标下降法(coordinate descent)
args:
X - 训练数据集
y - 目标标签值
lambdas - 惩罚项系数
rhos - 混合参数,取值范围[0,1]
max_iter - 最大迭代次数
tol - 变化量容忍值
return:
w - 权重系数
"""
# 初始化 w 为零向量
w = np.zeros(X.shape[1])
for it in range(max_iter):
done = True
# 遍历所有自变量
for i in range(0, len(w)):
# 记录上一轮系数
weight = w[i]
# 求出当前条件下的最佳系数
w[i] = down(X, y, w, i, lambdas, rhos)
# 当其中一个系数变化量未到达其容忍值,继续循环
if (np.abs(weight - w[i]) > tol):
done = False
# 所有系数都变化不大时,结束循环
if (done):
break
return w
def down(X, y, w, index, lambdas=0.1, rhos=0.5):
"""
cost(w) = (x1 * w1 + x2 * w2 + ... - y)^2 / 2n + ... + λ * ρ * (|w1| + |w2| + ...) + [λ * (1 - ρ) / 2] * (w1^2 + w2^2 + ...)
假设 w1 是变量,这时其他的值均为常数,带入上式后,其代价函数是关于 w1 的一元二次函数,可以写成下式:
cost(w1) = (a * w1 + b)^2 / 2n + ... + λρ|w1| + [λ(1 - ρ)/2] * w1^2 + c (a,b,c,λ 均为常数)
=> 展开后
cost(w1) = [aa / 2n + λ(1 - ρ)/2] * w1^2 + (ab / n) * w1 + λρ|w1| + c (aa,ab,c,λ 均为常数)
"""
# 展开后的二次项的系数之和
aa = 0
# 展开后的一次项的系数之和
ab = 0
for i in range(X.shape[0]):
# 括号内一次项的系数
a = X[i][index]
# 括号内常数项的系数
b = X[i][:].dot(w) - a * w[index] - y[i]
# 可以很容易的得到展开后的二次项的系数为括号内一次项的系数平方的和
aa = aa + a * a
# 可以很容易的得到展开后的一次项的系数为括号内一次项的系数乘以括号内常数项的和
ab = ab + a * b
# 由于是一元二次函数,当导数为零是,函数值最小值,只需要关注二次项系数、一次项系数和 λ
return det(aa, ab, X.shape[0], lambdas, rhos)
def det(aa, ab, n, lambdas=0.1, rhos=0.5):
"""
通过代价函数的导数求 w,当 w = 0 时,不可导
det(w) = [aa / n + λ(1 - ρ)] * w + ab / n + λρ = 0 (w > 0)
=> w = - (ab / n + λρ) / [aa / n + λ(1 - ρ)]
det(w) = [aa / n + λ(1 - ρ)] * w + ab / n - λρ = 0 (w < 0)
=> w = - (ab / n - λρ) / [aa / n + λ(1 - ρ)]
det(w) = NaN (w = 0)
=> w = 0
"""
w = - (ab / n + lambdas * rhos) / (aa / n + lambdas * (1 - rhos))
if w < 0:
w = - (ab / n - lambdas * rhos) / (aa / n + lambdas * (1 - rhos))
if w > 0:
w = 0
return w
三、机器学习算法(七)- 对数几率回归算法(Logistic Regression Algorithm)
前面学习了标准线性回归、岭回归、Lasso回归,弹性网络回归,这些线性模型解决的都是回归的问题。而今天学习的算法可以用回归的方式来解决分类问题– 对数几率回归算法(Logistic Regression Algorithm),也有被直译为逻辑回归。
模型介绍
通过回归的方式来解决分类的问题,可以通过先进行回归分析,然后通过一个函数将连续的结果映射成离散的分类结果,例如下面的函数表达式:
在对数几率回归中,使用最多的是S函数,其中最常用的是逻辑函数,(Logistic function),函数图像如下图所示:
如图,函数自变量越大,函数值越趋近于1,函数自变量越小,函数值越趋近于0,自变量为 0 时,函数值为 0.5。
逻辑函数的函数表达式为:
接着把线性方程带入逻辑函数中,得到的就是对数几率回归的函数方程:
对数几率回归的代价函数
对数几率回归中,使用了似然函数(likelihood function)的对数形式来作为其代价函数,而不是使用前面学习到的均方差(mean-square error / MSE)。
最大似然估计
例子:
在一个袋子里面,装着很多很多的球,分别是白球和黑球,但不知道其中具体白球、黑球各自的数量,如何推断出具体的各种球的概率呢?
先假设抽白球的概率是a,黑球的概率是(1-a),接着在盒子中随机抽出五个球,结果是:白、白、黑、白、黑。于是得到了这组样品的概率就是他们是概率(假设概率)的乘积,也称似然函数:L(a)= a×a×a×(1-a)×(1-a) = a3(1-a)2
概率是达到最大的界限的时候才会发生的,既然上面抽出的样品已经发生了,所以当a达到最大时,就可以认为白球的概率a,就是真实的概率。现在对似然函数求导,得出极值,令其等于0,就可以求的a。但由于似然函数的次数较高,所以一般会对似然函数,两边都求对数Ln(),再求导。
这个a=3/5,也符合抽出来结果,所以可以偏执的认为3/5就是盒子里面白球的概率。
所以最大似然估计就是求概率,把每个样品的概率相乘得出似然函数,接着求其最大值,得出的概率就是根据样品得出的、最真实的概率。
所以在二元分类中可以将其概率作为判断的依据!
在二元分类的情况下
再接着就是对w的优化过程了,一般都是使用梯度下降法,但同时也存在多种选择,如牛顿法 (Newton’s method),随机平均梯度下降法(Stochastic Average Gradient / SAG)、随机平均梯度下降加速法(SAGA)、L-BFGS算法(Limited-memory Broyden–Fletcher–Goldfarb–Shanno / L-BFGS)等等,文章学习可参考:优化方法
算法步骤
梯度下降法:求出函数的梯度后,沿着梯度的反方向再找到一个合适的步长系数迭代更新 w 的值。通过一次一次更新权重系数 w,一步一步的逼近代价函数的最小值。
(1)初始化权重系数 w,例如初始化为零向量
(2)计算下降方向,梯度下降法将梯度的反方向作为下降方向:
(3)选择最合适的下降步长 α,以使每一步迭代都满足梯度下降。(可用线搜索法来计算步长,满足的迭代的条件可参考Wolfe条件,但目前没有学习)
(4)更新权重系数 w:(5)重复步骤(2)~(4)直到梯度的大小足够小
(5)重复步骤(2)~(4)直到梯度的大小足够小
代码实现
使用 Python 实现对数几率回归算法(梯度下降法):
import numpy as np
c_1 = 1e-4
c_2 = 0.9
def cost(X, y, w):
"""
对数几率回归的代价函数
args:
X - 训练数据集
y - 目标标签值
w - 权重系数
return:
代价函数值
"""
power = -np.multiply(y, X.dot(w))
p1 = power[power <= 0]
p2 = -power[-power < 0]
# 解决 python 计算 e 的指数幂溢出的问题
return np.sum(np.log(1 + np.exp(p1))) + np.sum(np.log(1 + np.exp(p2)) - p2)
def dcost(X, y, W):
"""
对数几率回归的代价函数的梯度
args:
X - 训练数据集
y - 目标标签值
w - 权重系数
return:
代价函数的梯度
"""
return X.T.dot(np.multiply(-y, 1 / (1 + np.exp(np.multiply(y, X.dot(w))))))
def direction(d):
"""
更新的方向
args:
d - 梯度
return:
更新的方向
"""
return -d
def sufficientDecrease(X, y, w, step):
"""
判断是否满足充分下降条件(sufficient decrease condition)
args:
X - 训练数据集
y - 目标标签值
w - 权重系数
step - 步长
return:
是否满足充分下降条件
"""
d = dcost(X, y, w)
p = direction(d)
return cost(X, y, w + step * p) <= cost(X, y, w) + c_1 * step * p.T.dot(d)
def curvature(X, y, w, step):
"""
判断是否满足曲率条件(curvature condition)
args:
X - 训练数据集
y - 目标标签值
w - 权重系数
step - 步长
return:
是否满足曲率条件
"""
d = dcost(X, y, w)
p = direction(d)
return -p.T.dot(dcost(X, y, w + step * p)) <= -c_2 * p.T.dot(d)
def select(step_low, step_high):
"""
在范围内选择一个步长,直接取中值
args:
step_low - 步长范围开始值
step_high - 步长范围结束值
return:
步长
"""
return (step_low + step_high) / 2
def lineSearch(X, y, w, step_init, step_max):
"""
线搜索步长,使其满足 Wolfe 条件
args:
X - 训练数据集
y - 目标标签值
w - 权重系数
step_init - 步长初始值
step_max - 步长最大值
return:
步长
"""
step_i = step_init
step_low = step_init
step_high = step_max
i = 1
d = dcost(X, y, w)
p = direction(d)
while (True):
# 不满足充分下降条件或者后面的代价函数值大于前一个代价函数值
if (not sufficientDecrease(X, y, w, step_i) or (cost(X, y, w + step_i * p) >= cost(X, y, w + step_low * p) and i > 1)):
# 将当前步长作为搜索的右边界
# return search(X, y, w, step_prev, step_i)
step_high = step_i
else:
# 满足充分下降条件并且满足曲率条件,即已经满足了 Wolfe 条件
if (curvature(X, y, w, step_i)):
# 直接返回当前步长
return step_i
step_low = step_i
# 选择下一个步长
step_i = select(step_low, step_high)
i = i + 1
def logisticRegressionGd(X, y, max_iter=1000, tol=1e-4, step_init=0, step_max=10):
"""
对数几率回归,使用梯度下降法(gradient descent)
args:
X - 训练数据集
y - 目标标签值
max_iter - 最大迭代次数
tol - 变化量容忍值
step_init - 步长初始值
step_max - 步长最大值
return:
w - 权重系数
"""
# 初始化 w 为零向量
w = np.zeros(X.shape[1])
# 开始迭代
for it in range(max_iter):
# 计算梯度
d = dcost(X, y, w)
# 当梯度足够小时,结束迭代
if np.linalg.norm(x=d, ord=1) <= tol:
break
# 使用线搜索计算步长
step = lineSearch(X, y, w, step_init, step_max)
# 更新权重系数 w
w = w + step * direction(d)
return w
总结
本周阅读了论文《BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding》,这就一篇关于NLP的文章,其内容大意是推出了一个BERT(Bidirectional Encoder Representations from Transformers)模型。其模型是采用了联合调节所有层中的,使用MLM (Masked LM )结构的双向Transformer来训练预训练深度双向表示。同时,该模型采用了在双向语言模型的基础上额外增加了一个句子级别的连续性预测任务,Next Sentence Prediction (NSP)。这两个是这个模型的创新点。同时,本周学习了弹性网络回归算法(Elastic Net Regression Algorithm),和对数几率回归算法(Logistic Regression Algorithm),回归算法基本学完,下周会开始学习机器学习的分类算法。