预训练模型(Pretrained model):一般情况下预训练模型都是大型模型,具备复杂的网络结构,众多的参数量,以及在足够大的数据集下进行训练而产生的模型.
在NLP领域,预训练模型往往是语言模型,因为语言模型的训练是无监督的,可以获得大规模语料,同时语言模型又是许多典型NLP任务的基础,如机器翻译,文本生成,阅读理解等,常见的预训练模型有BERT, GPT, roBERTa, transformer-XL等.
ALBERT通过因式分解和参数共享的方式削减了嵌入层的参数矩阵,后续为找补回损失的精度使用了许多技巧,虽然模型参数量大幅度缩减,但由于其推理链路长度未变,所以预测时长实际上与同体级模型相比并没有竞争力。
ALBERT的提出时间大约是在2019年10月,其第一作者为谷歌科学家蓝振忠博士。ALBERT的论文地址为:https://openreview.net/pdf?id=H1eA7AEtvS , Github项目地址为: https://github.com/brightmart/albert_zh 。
简单说来,ALBERT是BERT的一个精简版,它在BERT模型的基础上进行改造,减少了大量参数,使得其在模型训练和模型预测的速度上有很大提升,而模型的效果只会有微小幅度的下降。
ALBERT相对于BERT的改进如下:
- 对Embedding因式分解(Factorized embedding parameterization);
- 跨层的参数共享(Cross-layer parameter sharing);
- 句间连贯(Inter-sentence coherence loss);
- 移除dropout 。
一、ALBert概述
在计算机视觉领域,提升性能的主要方法是通过将网络加深实现的;
同样地,BERT也可以通过将网络变深变宽来提升性能;
因此,起初的想法是也让网络变大,但网络变宽导致参数爆炸;
由于预训练语言模型规模很大(参数很多),导致训练需要很多GPU/TPU,而且训练时间长。所以作者提出一种参数相较少很多的预训练语言模型:A Lite BERT (ALBERT)。
因此,“怎样做到减少参数,而不减弱performance”,成为ALBERT关注的主题;
1、Bert轻量级模型
1.1 DistillBert(蒸馏Bert)
1.2 TinyBert
2、ALBert的历史意义
- 增加model size一般都能提高模型在下游任务中的表现,但是同时也会增加训练时间并且受制于内存限制。
- 为了解决这种困难,我们提出来了两种减少参数的策略来降低内存消耗和提高训练bert的速度。
- 综合的证据表明,我们提出的方法使得模型的规模比原来的bert要好得多。我们用一种专注 于句子间的连贯性的自监督loss,并且它还有助于下游任务中的输入。
- 结果证明我们的模型取得比较好的效果并且参数要少很多。
二、ALBert相对于Bert的改进策略
减少参数首先要知道 B e r t Bert Bert 的参数来源于哪儿;
Bert参数主要来源于两个模块:
- Token Embedding Projection 映射模块(参数量大约占整体参数量的20%);
- Attention与 Feed-Forward 模块(参数量大约占整体参数量的80%);
ALBERT 架构的骨干网络与 BERT 是相似的,即使用 Transformer 编码器和 GELU 非线性激活函数。现在先约定一下 BERT 的表示方式,即指定词嵌入大小为 E、编码器层数为 L、隐藏层大小为 H。与 Devlin 等人的研究一样,这篇论文将前馈网络/滤波器大小设置为 4H,将注意力 Head 的数量设置为 H/64。如下将介绍 ALBERT 的三大特效。
1、Factorized embedding parametrization(Token Embedding因式分解)
在 BERT 以及后续的 XLNet 和 RoBERTa 中,WordPiece 词嵌入大小 E 和隐藏层大小 H 是相等的,即 E ≡ H。由于建模和实际使用的原因,这个决策看起来可能并不是最优的。
从建模的角度来说,WordPiece 词嵌入的目标是学习上下文无关的表示,而隐藏层嵌入的目标是学习上下文相关的表示。通过上下文相关的实验,BERT 的表征能力很大一部分来自于使用上下文为学习过程提供上下文相关的表征信号。因此,将 WordPiece 词嵌入大小 E 从隐藏层大小 H 分离出来,可以更高效地利用总体的模型参数,其中 H 要远远大于 E。
从实践的角度,自然语言处理使用的词典大小 V 非常庞大,如果 E 恒等于 H,那么增加 H 将直接加大嵌入矩阵的大小,这种增加还会通过 V 进行放大。
因此,对于 ALBERT 而言,研究者对词嵌入参数进行了因式分解,将它们分解为两个小矩阵。
- 研究者不再将 one-hot 向量直接映射到大小为 H 的隐藏空间,而是先将它们映射到一个低维词嵌入空间 E,
- 然后再映射到高维度的隐藏空间。
即:先经过一个维度很低的embedding matrix,然后再经过一个高维度matrix把维度转换到隐藏层的空间内,从而通过这种分解把参数量从 O ( V × H ) O(V × H) O(V×H) 降低到 O ( V × E + E × H ) O(V × E + E × H) O(V×E+E×H),这在 E < < H E<<H E<<H 的时候,参数量减少得非常明显。其中 V V V 是词表的大小, H H H 是隐层维度, E E E 是词向量的维度。
O(V * H) to O(V * E + E * H)
如以ALBert_xxlarge为例,V=30000, H=4096, E=128
那么原先参数为V * H= 30000 * 4096 = 1.23亿个参数,现在则为V * E + E * H = 30000*128+128*4096 = 384万 + 52万 = 436万,
词嵌入相关的参数变化前是变换后的28倍。
可以这样做的原因是token embeddings是上下文独立的(由one-hot向量转化成dense向量),而hidden-layer embeddings是上下文依赖的,因此hidden-layer embeddings应该包含更多的信息,所以可以利用一个小于H的E做中介将词的one-hot向量先经过一个低维的embedding矩阵再经过一个高维的embedding矩阵映射到隐藏层空间;
上图表示BERT的One-hot向量输入,第一次投影是词与词之间是没有交互的。只有到了做Attention时,词与词之间才有交互。因此第一次投影不需要很高维度的向量。
下图是ALBERT的改进,先把第一次的映射放到很低的维度,然后在映射为我们需要的维度。这样做有两个好处:
- 词的Context independent表示与Context dependent表示之间解锁,可以自由的对Context dependent表示进行加高,也就是网络变宽。
- One-hot向量到第一次映射的参数非常多,可以把这块参数变的非常小。
实验结果表明,Factorized embedding parametrization 并没有对减少模型的参数有重大改变,参数减少了19M,模型的性能下降了0.6。
矩阵分解本质上就是一个低秩分解的操作,其通过对Embedding 部分降维来达到降低参数的作用。在最初的BERT中,以Base为例,Embedding层的维度与隐层的维度一样都是768,但是我们知道,对于词的分布式表示,往往并不需要这么高的维度,比如在Word2Vec时代最多采用50或300这样的维度。那么一个很简单的思想就是,通过将Embedding部分分解来达到降低参数量的作用,其以公式表示如下:
:词表大小;:隐层维度;:词向量维度
我们以 BERT-Base 为例,Base中的Hidden size 为768, 词表大小为3w,此时的参数量为:768 * 3w = 23040000。如果将 Embedding 的维度改为 128,那么此时Embedding层的参数量为:128 * 3w + 128 * 768 = 3938304。二者的差为19101696,大约为19M。我们看到,其实Embedding参数量从原来的23M变为了现在的4M,似乎变化特别大,然而当我们放到全局来看的话,BERT-Base的参数量在110M,降低19M也不能产生什么革命性的变化。因此,可以说Embedding层的因式分解其实并不是降低参数量的主要手段。
2、Cross-layer parameter sharing(跨层参数共享)【参数量减少主要贡献】
ALBERT提出的另一个减少参数量的方法就是层之间的参数共享,即多个层使用相同的参数。
其实目前有很多方式来共享参数:
- 只共享 feed-forward network (FFN) 参数
- 只共享 attention 参数
- 以上两种都共享
而 ALBERT 采用的是共享所有层的所有参数。
可以理解为此时的权值共享并不是说12层transformer_encoder的值是一样的,只是占用了同一变量,所以模型的计算量并没有少,只是参数个数变成了原来的12分之一。参数量变化: O ( 12 × L × H × H ) O(12×L×H×H) O(12×L×H×H) → O ( 12 × H × H ) O(12×H×H) O(12×H×H)
- 这主要是为了减少参数量(性能轻微降低,参数大量减少,这整体上是好的事情)
- 论文里的消融实验的分数也说明no-share的分数是最高的
从下面实验结果比较看出,性能的下降主要是由于共享 feed-forward 参数导致的。
将模型的宽度增大,即 ALBERT xxlarge 的hidden层维度增加到4096,是 BERT large 的4倍,实验结果显示性能确实提高了,但是训练的时间比 BERT large 慢了3倍。
将模型的深度增加,实验结果发现48层的性能并不如24层,作者也不知道是什么原因导致,还在进一步研究中。
参数共享能显著减少参数。共享可以分为全连接层、注意力层的参数共享;注意力层的参数对效果的减弱影响小一点。
这种机制之前也是有的,但研究者的度量发现词嵌入的 L2 距离和余弦相似性是震荡而不是收敛。如下图 2 展示了每一层输入与输出嵌入矩阵间的 L2 距离与余弦相似性。
研究者发现 ALBERT 从一层到另一层的转换要比 BERT 平滑得多,结果表明,权重共享有效地提升了神经网络参数的鲁棒性。即使相比于 BERT 这两个指标都有所下降,但在 24 层以后,它们也不会收敛到 0。
3、句间连贯性损失(SOP/Sentence Order Prediction)
BERT中的NSP任务正样本是在同一个文档中选取的两段连续的文本,负样本是从不同文档中选取的两段文本。NSP合并了主题预测和连贯性预测任务,但是NSP设计的过于简单。
在ALBERT中设计了一个SOP任务取代NSP任务,它的证样本是在同一个文档中选取的两段连续的文本,而负样本将正样本的顺序反过来,这样的设计是为了学习到句子间的连贯性。
谷歌自己把它换成了 SOP。这个在百度 ERNIE 2.0 里也有,叫 Sentence Reordering Task,而且 SRT 比 SOP 更强,因为需要预测更多种句子片段顺序排列。ERNIE 2.0 中还有一些别的东西可挖,比如大小写预测 Captialization Prediction Task、句子距离 Sentence Distance Task。
- NSP:下一句预测, 正样本=上下相邻的2个句子,负样本=随机2个句子
- SOP:句子顺序预测,正样本=正常顺序的2个相邻句子,负样本=调换顺序的2个相邻句子
NSP任务过于简单,只要模型发现两个句子的主题不一样就行了,所以SOP预测任务能够让模型学习到更多的信息
BERT使用的NSP损失,是预测两个片段在原文本中是否连续出现的二分类损失。目标是为了提高如NLI等下游任务的性能,但是最近的研究都表示 NSP 的作用不可靠,都选择了不使用NSP,毕竟一些实验表示NSP非但没有作用,反而会对模型带来一些损害。
作者推测,NSP效果不佳的原因是其难度较小。将主题预测和连贯性预测结合在了一起,但主题预测比连贯性预测简单得多,并且它与LM损失学到的内容是有重合的。
SOP的正例选取方式与BERT一致(来自同一文档的两个连续段),而负例不同于BERT中的sample,同样是来自同一文档的两个连续段,但交换两段的顺序,从而避免了主题预测,只关注建模句子之间的连贯性。
具体的损失函数表达式读者可以查阅原论文,但研究者表示,在使用了该损失函数后,ALBERT 能显著提升下游多句子编码任务的性能。
使用段落连续性任务。正例,使用从一个文档中连续的两个文本段落;负例,使用从一个文档中连续的两个文本段落,但位置调换了。
避免使用原有的NSP任务,原有的任务包含隐含了预测主题这类过于简单的任务。
从实验结果上来看,NSP不能解决SOP任务,但是SOP可以解决NSP任务。
4、Dropout effects(Dropout 效果)
作者在实验过程中发现了在经过100万次 steps 后,模型并没有过拟合,于是作者尝试去除dropout来对比有dropout的效果。
结果显示去除掉dropout比含有dropout的效果还挺升了一点,而且去掉dropout后训练过程中大大减少了内存的使用。
Dropout 个人觉得可以一开始就不用,而不是训练一段时间再关掉。学都学不动,防啥过拟合啊。
- 模型的内部任务(MLM,SOP等等)都没有过拟合
- dropout是为了降低过拟合而增加的机制,所以对于bert而言是弊大于利的机制
如上图所示,ALBERT的最大模型在训练1M步后仍然没有过拟合,于是作者决定删除dropout,进一步提高模型能力。
5、Segments-Pair
BERT为了加速训练,前90%的steps使用了128个token的短句子,最后10%才使用512个token的长句子训练位置向量。
ALBERT在90%的情况下使用512的segment,10%的情况下使用比512短的segment。从数据上看,更长的数据提供更多的上下文信息,可能显著提升模型的能力。
6、Masked N-gram LM
BERT的MLM目标是随机MASK15%的词来预测,ALBERT预测的是N-gram片段,包含更多的语义信息,每个片段长度n(最大为3),根据概率公式计算得到。比如1-gram、2-gram、3-gram的的概率分别为6/11、3/11、2/11.越长概率越小:
三、总结
最后我们最终对比一下ALBERT和BERT的实现效果:
- 精度方面,ALBERT-xxlarge模型参数量仅仅是BERT-large模型的70%,但是下游各个任务上却都有较明显的效果提升;
- 速度方面,这里以BERT-large模型作为baseline,因为ALBERT因为更少的通信和更少的计算,与相应的BERT模型相比,ALBERT模型具有更高的数据吞吐量。所以从实验数据可以看到,ALBERT-base和ALBERT-large分别是BERT-large训练速度的5.6倍和1.7倍,但是精度却仅有略微的降低。
四、ALBERT_ZH
中文预训练ALBERT模型ALBERT_ZH上不是谷歌出的,而是由徐亮基于ALBERT开发,是一个更加精简的版本。相比ALBERT,ALBERT_ZH参数少了90%,但精度并没有损失。同时支持TensorFlow、PyTorch和Keras。虽然体积小,但却是建立在海量中文语料基础上,30G中文语料,超过100亿汉字,包括多个百科、新闻、互动社区。
预训练序列长度sequence_length设置为512,批次batch_size为4096,训练产生了3.5亿个训练数据(instance);每一个模型默认会训练125k步,ALBERT_xxlarge将训练更久。作为比较,roBERTa_zh预训练产生了2.5亿个训练数据、序列长度为256。由于ALBERT_zh预训练生成的训练数据更多、使用的序列长度更长。
import torch
from transformers import AlbertModel, BertTokenizer
# 一、加载 electra 的分词器
tokenizer = BertTokenizer.from_pretrained(r'D:\Pretrained_Model\albert_chinese_tiny')
# 二、加载 electra 模型,这个路径文件夹下有 electra_config.json配置文件和model.bin模型权重文件
model = AlbertModel.from_pretrained(r'D:\Pretrained_Model\albert_chinese_tiny', output_hidden_states=True, output_attentions=True)
# 三、使用tokenizer进行数值映射,将字符串型转为数值型张量
input_text = "我想吃鸡蛋。"
indexed_tokens = torch.tensor(tokenizer.encode(input_text)).unsqueeze(0) # 使用tokenizer进行数值映射
print("tokenizer映射后的结果:indexed_tokens = ", indexed_tokens) # tokenizer映射后的结果:indexed_tokens = tensor([[ 101, 1045, 2031, 2042, 2083, 1037, 2312, 2193, 1997, 7473, 3036, 3688, 2058, 1996, 2086, 1012, 102]]) 【 101和102是起止符, 中间的每个数字对应"i have been through a large number of pc security products over the years ."的每个单词和标点】
# 四、将序列化后得数据喂给model,生成bert的输出
bert_outputs = model(indexed_tokens)
bert_outputs_list = list(bert_outputs)
print("\nlen(bert_outputs_list) = {0}".format(len(bert_outputs_list)))
print("bert_outputs_list = {0}".format(bert_outputs_list))
last_hidden_state, pooler_output, hidden_states, attentions = bert_outputs[0], bert_outputs[1], bert_outputs[2], bert_outputs[3]
print("\nlast_hidden_state.shape = {0}----last_hidden_state = bert_outputs[0] = \n{1}".format(last_hidden_state.shape, last_hidden_state))
print("\npooler_output.shape = {0}----pooler_output = bert_outputs[1] = \n{1}".format(pooler_output.shape, pooler_output))
print("\nhidden_states[0].shape = {0}----hidden_states = bert_outputs[2] = \n{1}".format(hidden_states[0].shape, hidden_states))
print("\nlen(attentions) = {0}----attentions[0].shape = {1}".format(len(attentions), attentions[0].shape))
打印结果:
The tokenizer class you load from this checkpoint is 'AlbertTokenizer'.
The class this function is called from is 'BertTokenizer'.
tokenizer映射后的结果:indexed_tokens = tensor([[ 101, 2769, 2682, 1391, 7883, 6028, 511, 102]])
len(bert_outputs_list) = 4
bert_outputs_list = ['last_hidden_state', 'pooler_output', 'hidden_states', 'attentions']
last_hidden_state.shape = torch.Size([1, 8, 312])----last_hidden_state = bert_outputs[0] =
tensor([[[ 0.2187, 0.1725, -0.1285, ..., 0.4073, 0.2446, -0.4724],
[ 0.8228, -0.1324, -1.6674, ..., -0.5353, -1.2885, -0.6547],
[ 0.7796, 0.8730, -0.5060, ..., -1.4310, -0.0370, -1.1793],
...,
[-0.5700, 0.2896, -0.3374, ..., -0.4502, -0.2388, -0.8621],
[-0.4534, -0.2668, -0.3698, ..., 0.5735, -0.0530, -0.6480],
[ 0.4472, -0.1950, -0.6586, ..., -0.9599, -0.7927, 0.1658]]],
grad_fn=<NativeLayerNormBackward>)
pooler_output.shape = torch.Size([1, 312])----pooler_output = bert_outputs[1] =
tensor([[ 9.9983e-01, 8.6759e-01, 1.0276e-01, 9.9794e-01, 5.8447e-02,
-6.7096e-01, 1.2825e-01, 3.0039e-02, 9.9277e-01, -9.9972e-01,
-9.8488e-01, 3.4746e-02, 9.8622e-01, -9.6045e-01, 9.9807e-01,
9.9997e-01, 5.8497e-01, 9.9990e-01, 9.9951e-01, 6.7677e-02,
-4.8888e-02, 9.8508e-01, -8.1218e-01, -9.9947e-01, 9.9435e-01,
-8.7748e-01, -9.8779e-01, 9.9516e-01, -9.9977e-01, 9.9685e-01,
9.8987e-01, 1.5320e-01, 9.9966e-01, 9.2322e-01, 9.9519e-01,
9.9863e-01, -9.9220e-01, 1.8003e-01, -9.4129e-01, -3.5797e-02,
-9.1360e-02, 8.4320e-01, -3.6565e-03, -5.9994e-02, -9.5368e-01,
9.7767e-01, 6.9107e-03, -8.7169e-01, 9.2582e-01, -9.9963e-01,
4.8074e-01, 9.9851e-01, 9.7505e-01, -9.9648e-01, 9.0934e-01,
-9.8806e-01, 1.5751e-01, -1.0709e-01, -9.9491e-01, -9.6617e-01,
5.8229e-02, -9.8586e-01, -3.3495e-01, -9.9625e-01, -1.2447e-01,
-9.9821e-01, -9.5383e-01, 4.2981e-02, 9.6175e-01, 9.9621e-01,
8.2730e-01, 9.0217e-02, 6.0574e-02, 1.6087e-01, 9.7787e-01,
-9.9789e-01, 9.6070e-01, -9.1722e-02, -4.1715e-02, 9.9976e-01,
-9.9371e-01, 9.7840e-01, -6.3911e-01, 9.8493e-01, 1.0823e-01,
9.9200e-01, 9.8109e-03, -1.9065e-01, 9.8746e-01, 8.9293e-01,
-9.9992e-01, -4.5282e-02, 9.8728e-01, -3.9885e-02, 9.8826e-01,
-6.8158e-01, -9.4528e-01, 9.9276e-01, -8.3660e-01, 7.8820e-01,
9.9733e-01, 9.4145e-01, 9.9925e-01, 3.7331e-01, 9.6992e-01,
-9.7009e-02, 3.5448e-01, 1.1929e-01, 9.9967e-01, 7.1173e-01,
-9.6358e-01, 9.9864e-01, 7.8279e-02, 9.5887e-01, 9.7489e-01,
-9.3190e-01, -6.8871e-01, -2.0000e-01, -7.1076e-02, -9.9443e-01,
-7.6859e-01, 2.2059e-01, 7.2797e-01, -9.7139e-01, -1.7248e-02,
-9.9831e-01, -1.3908e-01, 9.8977e-01, 9.5141e-01, 2.1985e-02,
9.9906e-01, 7.2378e-02, -5.5644e-01, 9.9946e-01, 9.9556e-01,
-9.9949e-01, -9.9517e-01, 9.1207e-02, 9.5951e-01, 9.8470e-01,
-9.8295e-01, 1.1544e-01, 9.9952e-01, -1.2526e-01, -9.9991e-01,
9.9999e-01, -9.9998e-01, 9.7907e-01, 1.4110e-01, 9.9289e-01,
2.3961e-02, 2.1080e-01, 1.0474e-02, -9.9997e-01, -9.9829e-01,
9.2799e-03, 9.7307e-01, 9.9888e-01, 8.4499e-01, 9.1084e-01,
-9.9858e-01, -8.0023e-02, 9.9870e-01, 9.8142e-01, 3.6681e-02,
-9.9941e-01, -9.9974e-01, -3.2426e-01, 9.9856e-01, -1.0528e-01,
9.8488e-01, 9.8829e-01, -7.5379e-02, -9.9512e-01, 9.9969e-01,
-9.9993e-01, -9.4636e-01, 4.1075e-02, -9.6420e-01, 7.1149e-02,
-9.8177e-01, 2.8127e-02, 4.4165e-01, -9.9819e-01, -9.0052e-02,
-1.0000e+00, 2.9553e-01, -9.6718e-01, 6.9454e-02, -9.5142e-01,
-9.9740e-01, -2.2229e-01, -9.9905e-01, -9.5554e-01, -9.9968e-01,
-3.2546e-02, -9.7991e-01, -9.9966e-01, -9.9992e-01, -9.9076e-01,
9.9684e-01, -5.5782e-02, -9.8937e-01, 9.7003e-01, -1.4629e-01,
1.1605e-01, -7.9901e-01, 9.9986e-01, 7.5822e-01, -8.0275e-01,
-5.8375e-03, 1.0000e+00, 9.9609e-01, 1.8510e-01, -8.2824e-02,
3.1531e-02, 9.6572e-02, -9.7483e-01, -9.9970e-01, 9.8926e-01,
-9.8595e-01, -1.3146e-01, 9.9949e-01, 9.9881e-01, 7.7445e-02,
-9.9984e-01, 9.8191e-01, 9.9363e-01, 9.9563e-01, 7.5788e-02,
9.5444e-01, -6.8468e-02, 9.9566e-01, 9.9880e-01, 9.0812e-01,
-9.9027e-01, 9.7754e-01, -9.9443e-01, -9.9992e-01, -1.6556e-01,
8.4566e-01, 9.7587e-01, -5.3133e-02, 2.8409e-02, -1.2027e-01,
9.9854e-01, 9.5688e-01, -9.9979e-01, 6.7454e-02, -4.3727e-02,
-9.9985e-01, -6.7330e-02, 3.3357e-02, 9.9743e-01, -9.9451e-01,
-3.1412e-02, -5.1537e-03, -9.8076e-01, 9.9993e-01, 9.6656e-01,
-1.6594e-02, -7.4929e-02, 9.7860e-01, -1.0196e-01, 9.9967e-01,
-1.2881e-01, 9.9999e-01, -3.4053e-02, 9.9597e-01, 9.8895e-01,
-9.1503e-01, 8.2174e-04, 5.6775e-02, 5.9160e-02, -1.9323e-01,
-1.1775e-01, 8.9478e-01, -9.9910e-01, -3.0748e-01, -7.8942e-01,
2.0434e-01, -9.8274e-01, 9.9909e-01, -9.9826e-01, -9.9996e-01,
9.5170e-01, 2.5748e-02, 2.0685e-01, 6.6592e-02, 9.0657e-01,
-1.5842e-01, 9.9999e-01, 9.7968e-01, 9.9821e-01, 9.6827e-01,
-9.6462e-02, 9.9834e-01, 9.9979e-01, 9.6599e-01, 9.9917e-01,
-9.4399e-01, 9.9522e-01, 5.9802e-01, 9.8264e-01, 9.9995e-01,
8.9057e-02, 9.7858e-01, -1.9152e-02, 1.7309e-01, -9.8926e-01,
9.9902e-01, -6.9991e-01]], grad_fn=<TanhBackward>)
hidden_states[0].shape = torch.Size([1, 8, 312])----hidden_states = bert_outputs[2] =
(tensor([[[-0.5849, -0.3868, 0.0435, ..., -0.2816, 0.0104, -0.0875],
[-0.3733, -0.0795, 0.4863, ..., 0.3753, -1.2605, -0.0494],
[-0.3314, -0.1722, 0.5749, ..., -0.4436, -0.2515, -0.8988],
...,
[ 0.1424, 0.6708, 0.1327, ..., -0.2771, 0.4395, 0.4560],
[-0.3641, -0.2824, 0.6295, ..., -0.3545, 0.0983, 0.1928],
[ 0.2166, -0.1971, 0.3386, ..., -0.8133, 0.6088, -0.4776]]],
grad_fn=<AddBackward0>), tensor([[[-0.0415, -0.0566, -0.0981, ..., -0.2408, 0.1198, -0.4477],
[-0.2592, 0.2666, 0.4899, ..., -0.0462, -0.6679, -0.2027],
[-0.3340, -0.0106, 1.2458, ..., -0.1897, -0.4031, -0.1454],
...,
[ 0.3659, 0.0940, 0.5107, ..., -0.3708, 0.0067, 0.4427],
[-0.1246, -0.1261, -0.2024, ..., -0.1207, 0.2732, 0.2252],
[ 0.3361, -0.8395, -0.2926, ..., -0.4874, 0.6040, -0.1774]]],
grad_fn=<NativeLayerNormBackward>), tensor([[[-0.0190, -0.0321, -0.1321, ..., -0.1875, 0.0878, -0.0080],
[-0.1226, 0.4042, 0.0638, ..., -0.1783, -0.9978, -0.2302],
[ 0.1980, 0.4732, 0.8429, ..., -0.4991, -0.1517, -0.3859],
...,
[ 0.4840, 0.1474, 0.1313, ..., -0.4977, -0.2129, 0.1601],
[-0.0539, -0.0321, -0.1581, ..., 0.3006, 0.1831, 0.2627],
[ 0.2019, -1.0859, -0.5763, ..., -0.3722, 0.2537, 0.2666]]],
grad_fn=<NativeLayerNormBackward>), tensor([[[ 0.1125, -0.0185, -0.1048, ..., -0.1151, 0.1180, -0.3288],
[ 0.3037, 0.1122, -1.1527, ..., -0.6060, -1.3129, 0.0829],
[ 0.6628, 0.4029, 0.1890, ..., -0.9714, 0.4507, -0.4285],
...,
[-0.1353, 0.2643, 0.1970, ..., -0.3308, -0.0803, -0.2146],
[-0.1265, -0.1192, -0.3378, ..., 0.3479, 0.2341, 0.0621],
[ 0.4380, -0.8352, -0.4165, ..., -0.7407, -0.4048, 0.7498]]],
grad_fn=<NativeLayerNormBackward>), tensor([[[ 0.2187, 0.1725, -0.1285, ..., 0.4073, 0.2446, -0.4724],
[ 0.8228, -0.1324, -1.6674, ..., -0.5353, -1.2885, -0.6547],
[ 0.7796, 0.8730, -0.5060, ..., -1.4310, -0.0370, -1.1793],
...,
[-0.5700, 0.2896, -0.3374, ..., -0.4502, -0.2388, -0.8621],
[-0.4534, -0.2668, -0.3698, ..., 0.5735, -0.0530, -0.6480],
[ 0.4472, -0.1950, -0.6586, ..., -0.9599, -0.7927, 0.1658]]],
grad_fn=<NativeLayerNormBackward>))
len(attentions) = 4----attentions[0].shape = torch.Size([1, 12, 8, 8])
Process finished with exit code 0
参考资料:
ALBERT:轻量级BERT语言模型 ICLR2020
博客:ALBERT:A LITE BERT FOR SELF-SUPERVISED LEARNING OF LANGUAGE REPRESENTATIONS
Albert理解
解读ALBERT
用深度矩阵分解给词向量矩阵瘦身
一文揭开ALBERT的神秘面纱
12层的bert参数量_从BERT到ALBERT
广告行业中那些趣事系列6:BERT线上化ALBERT优化原理及项目实践(附github)
“瘦身成功”的ALBERT,能取代BERT吗?
【BERT蒸馏】DistilBERT、Distil-LSTM、TinyBERT、FastBERT(论文+代码)
NLP(二十二)利用ALBERT实现文本二分类