本课程主要介绍NLP相关,包括RNN、LSTM、Attention、Transformer、BERT等模型,以及情感识别、文本生成、机器翻译等应用
ShusenWang的个人空间-ShusenWang个人主页-哔哩哔哩视频 (bilibili.com)
数值特征(Numeric Features,特点是可以比较大小)和类别特征(Categorical Features)
类别特征需转换成数值特征,但不能只用一个标量表示(因为类别特征做数值运算无意义),需要用one-hot编码
文本处理:
每个单词就是一个类别,单词就是categorical feature,把单词变为数值向量
为什么要去掉低频词?
一种情况是Name entities(姓名实体,无意义),或拼写错误
另一种原因是不希望vocabulary太大,vocabulary越大,one-hot向量维度越高,会让计算变慢,同时模型参数也会越多,容易过拟合
The IMDB Movie Review Dataset,判断电影评论的情感是正面还是负面(二分类问题)
5w条电影评论,2w5k作为训练数据,2w5k作为测试数据
(1)tokenization(一个token就是一个单词,或一个字符):
(2)dictionary:统计词频、去掉低频词;让每个单词对应一个正整数。有了字典,就可以把每个单词映射为一个整数。这样一来,一句话就可以用一个正整数的列表表示,称为sequences序列
(3)one-hot encoding:
(4)align sequences(序列对齐):sequence长度不同,训练数据没有对齐,因为要把数据存储在矩阵或者张量里,序列需要对齐,每条序列有相同的长度
总结:一条评论用一个正整数的序列(sequence)来表示,sequence就是神经网络中Embedding层的输入。还需要对齐不同sequence的长度
(1)One-Hot Encoding:字典里一共有v个单词,需要维度为v的one-hot向量(很容易维度过高;RNN的参数数量正比于输入向量的维度)
(2)Word Embedding:把高维one-hot向量映射为低维向量(d为词向量的维度,由用户自己决定;v是vocabulary,即字典里单词的数量)
参数矩阵P的每一行都是一个词向量,矩阵的行数是v,每一行对应一个单词;d由用户决定,d的大小会影响机器学习模型的表现,应由cross validation来选择一个比较好的d
参数矩阵是从训练数据中学习出来的
(3)keras提供Embedding层
from keras.models import Sequential
from keras.layers import Flatten, Dense, Embedding
embedding_dim = 8
# vocabulary大小 v
# 词向量维度 d (通过cross validation选出)
# 每个Sequence的长度
model.add(Embedding(vocabulary, embedding_dim, input_length=word_num))
Embedding层的输出是一个 (word_num, embedding_dim) 的矩阵,Embedding层的参数数量 = vocabulary × embedding_dim
总结:Embedding层把每个单词映射成一个 embedding_dim 的词向量
(1)用keras实现一个分类器:
from keras.models import Sequential
from keras.layers import Flatten, Dense, Embedding
embedding_dim = 8
model = Sequential()
# vocabulary大小 v
# 词向量维度 d (通过cross validation选出)
# 每个Sequence的长度
model.add(Embedding(vocabulary, embedding_dim, input_length=word_num))
model.add(Flatten())
model.add(Dense(1, activation='sigmoid'))
model.summary() # 打印出模型概要
(2)接下来编译模型:
from keras import optimizers
epochs = 50
model.compile(optimizer=optimizers.RMSprop(lr=0.0001), loss='binary_crossentropy', metrics=['acc'])
# 用训练数据来拟合模型
history = model.fit(x_train, y_train, epochs=epochs, batch_size=32, validation_data=(x_valid, y_valid))
x_train是个2w*20的矩阵(20指每条电影评论中有20个单词,每个单词用正整数表示)
Performance on the training and validation sets(3)在测试集上检验模型表现:
loss_and_acc = model.evaluate(x_test, labels_test)
print('loss=' + str(loss_and_acc[0]))
print('acc=' + str(loss_and_acc[1]))
电影评论texts,首先做tokenization,变为tokens;
然后把每个tokens编码为一个数字,这样一来,一条电影评论就可以用一个正整数的序列sequence来表示(sequence即神经网络中Embedding层的输入);
由于电影评论的长短不一,得到的sequence长短也不一,故还需要对齐(长度>w,只保留后w个词;长度
输入Embedding层【参数数量 = vocabulary × embedding_dim】,把每个单词映射到一个 embedding_dim 维的词向量;
再用Flatten,将矩阵压扁成向量;
最后用Logistic分类器【参数数量 = word_num × embedding_dim + 1】输出一个0-1之间的数
训练数据足够多时,RNN效果不如Transformer;但在小规模问题,RNN很有用
每次把一个词向量输入RNN,RNN就会更新状态h,把新的输入积累到状态h里(h0包含了第一个词的信息,h1包含了前两个词的信息,以此类推)
更新状态h的时候需要用到参数矩阵A(不论链路多长,都只有一个参数矩阵A。A随机初始化,利用训练数据来学习A)
SimpleRNN怎么把输入的词向量x结合到状态h里?
激活函数是 tanh(双曲正切函数),输入是任意实数,输出在 -1 ~ 1。为什么要用tanh?【每次更新状态h之后,做一个normalization,让h恢复到 -1~1 之间】
可训练参数:参数矩阵A(可能还有intercept vector,即偏置项)
# (1)搭建模型
from keras.models import Sequential
from keras.layers import SimpleRNN, Embedding, Dense
vocabulary = 10000 # unique words in the dictionary
embedding_dim = 32 # shape(x) 词向量x的维度
word_num = 500 # sequence length 每个评论长度为500个单词
state_dim = 32 # shape(h) 状态向量h的维度
model = Sequential()
model.add(Embedding(vocabulary, embedding_dim, input_length=word_num)) # 词映射为向量
# return_sequences=False 指RNN只输出最后一个状态向量,之前的状态向量全扔掉
model.add(SimpleRNN(state_dim, return_sequences=False)) # 指定状态向量h的维度 state_dim
model.add(Dense(1, activation='sigmoid')) # 全连接层,输入RNN的最后一个状态h,输出一个0-1之间的数
model.summary()
# (2)编译模型
from keras import optimizers
epochs = 3 # Early stopping防止过拟合(在validation accuracy变差之前就停止)
model.compile(optimizer=optimizers.RMSprop(lr=0.001), loss='binary_crossentropy', metrics=['acc'])
# 用训练数据来拟合模型
history = model.fit(x_train, y_train, epochs=epochs, batch_size=32, validation_data=(x_valid, y_valid))
# (3)用测试数据评价模型表现
loss_and_acc = model.evaluate(x_test, labels_test)
print('loss=' + str(loss_and_acc[0]))
print('acc=' + str(loss_and_acc[1]))
RNN层参数数量:shape(h) × (shape(h) + shape(x)) + bias/intercept ,第一项为矩阵A的大小,第二项为RNN默认使用intercept(偏移量)
上述做法是只保留了最后一个状态ht,丢弃了前面所有状态。也可以保留h0~ht,此时RNN输出为一个矩阵(每行就是一个状态h),需要加Flatten层把状态矩阵变成向量。向量作为分类器的输入,来判断电影是正面的还是负面的
from keras.models import Sequential
from keras.layers import SimpleRNN, Embedding, Dense
vocabulary = 10000 # unique words in the dictionary
embedding_dim = 32 # shape(x) 词向量x的维度
word_num = 500 # sequence length 每个评论长度为500个单词
state_dim = 32 # shape(h) 状态向量h的维度
model = Sequential()
model.add(Embedding(vocabulary, embedding_dim, input_length=word_num)) # 词映射为向量
model.add(SimpleRNN(state_dim, return_sequences=True)) # 指定状态向量h的维度 state_dim
model.add(Flatten())
model.add(Dense(1, activation='sigmoid')) # 全连接层
model.summary()
不擅长long-term dependence:状态h100跟100步之前的输入x1几乎没关系(即后面的状态会遗忘之前的输入)
RNN适合文本、语音等时序序列数据
RNN按顺序读取每一个词向量,并在状态向量h中积累看到过的信息,如h1中包含x0和x1的信息,以此类推,ht中包含了之前所有输入的信息,可以认为,ht就是RNN从整个输入序列中抽取的特征向量
RNN记忆很短,会遗忘很久之前的输入x。若时间序列很长,比如好几十步,最终的ht已经忘了早先的输入
SimpleRNN有一个参数矩阵A,维度是 shape(h) × (shape(h)+shape(x)),一开始随机初始化,从训练数据中学习。可能还有一个intercept向量
注意,不管时序多长,参数矩阵只有一个,所有模块里的参数都是一样的
LSTM的记忆会比SimpleRNN长很多,但也还是有遗忘的问题。LSTM是一种RNN模型,可以避免梯度消失的问题,可以有更长的记忆(一般用RNN,都是LSTM,SimpleRNN基本不用)
传输带Ct:过去的信息直接送到下一个时刻,以此避免梯度消失
LSTM中有很多个gate(遗忘门、输入门、输出门),可以有选择地让信息通过
遗忘门有一个参数矩阵Wf,需要通过反向传播从训练数据里学习
更新传输带C:
计算状态向量ht:一份传到下一步,另一份是LSTM的输出
到第t步为止,一共有t个向量x输入了LSTM,可以认为所有这些x向量的信息都积累在了状态ht里
遗忘门、输入门、new value、输出门,共有4个参数矩阵,共有 4 × shape(h) × [shape(h) + shape(x)]
让LSTM只输出最后一个状态向量ht,即从电影评论中提取出的特征向量,再输入线性分类器,来判断评论是正面的还是负面的
from keras.models import Sequential
from keras.layers import LSTM, Embedding, Dense, Flatten
vocabulary = 10000 # unique words in the dictionary
embedding_dim = 32 # shape(x) 词向量x的维度
word_num = 500 # sequence length 每个评论长度为500个单词
state_dim = 32 # shape(h) 状态向量h的维度
model = Sequential()
model.add(Embedding(vocabulary, embedding_dim, input_length=word_num)) # 词映射为向量
# return_sequences=False 指RNN只输出最后一个状态向量,之前的状态向量全扔掉
model.add(LSTM(state_dim, return_sequences=False)) # 指定状态向量h的维度 state_dim
model.add(Dense(1, activation='sigmoid')) # 全连接层
model.summary()
只输出最后一个状态向量h
可以加dropout(设置为某个0-1之间的数字即可):
model.add(LSTM(state_dim, return_sequences=False), dropout=0.2)
若加dropout没有提升测试准确率,原因:虽然训练时出现了overfitting,但overfitting不是由LSTM造成的,而是由Embedding层造成的,故对LSTM使用dropout正则化没有用
LSTM和SimpleRNN的区别是用了一条传输带,让过去的信息可以很容易的传输到下一时刻,这样就有了更长的记忆
LSTM有4个组件,分别是:forget gate(遗忘门)、input gate(输入门)、new value(新的输入)、output gate(输出门),这4个组件各自有一个参数矩阵,所以一共有4个参数矩阵,参数数量为 4 × shape(h) × [shape(h) + shape(x)]
三个技巧来提升RNN的效果(对所有RNN都适用)
神经网络每一步都会更新状态h,有两份:一份送到下一时刻,一份作为输出(同时也是下一层的输入)
# 多层LSTM 用keras实现
from keras.models import Sequential
from keras.layers import LSTM, Embedding, Dense
vocabulary = 10000 # unique words in the dictionary
embedding_dim = 32 # shape(x) 词向量x的维度
word_num = 500 # sequence length 每个评论长度为500个单词
state_dim = 32 # shape(h) 状态向量h的维度
model = Sequential()
model.add(Embedding(vocabulary, embedding_dim, input_length=word_num)) # 词映射为向量
# return_sequences=True 第一层的输出会成为第二层的输入,故要输出所有的状态向量h
model.add(LSTM(state_dim, return_sequences=True, dropout=0.2)) # 指定状态向量h的维度 state_dim
model.add(LSTM(state_dim, return_sequences=True, dropout=0.2))
model.add(LSTM(state_dim, return_sequences=False, dropout=0.2)) # 只输出最后一个状态向量
model.add(Dense(1, activation='sigmoid')) # 全连接层,输入第三层LSTM最后一个状态向量,输出分类结果
实验结果跟单层RNN效果差不多,猜想是由于Embedding层参数太多,没有足够的数据把这一层训练好,出现overfitting,加再多LSTM层也无济于事
训练两条RNN,一条从左往右,一条从右往左,两条RNN完全独立,不共享参数和状态。两条RNN各自输出自己的状态向量,然后把它们的状态向量做concat,记为向量y
双向RNN总是比单向的效果好,原因:不管是SimpleRNN还是LSTM,都会或多或少遗忘掉早先的输入。而双向RNN左右结合,就不会遗忘一开始的词
# 双向LSTM 用keras实现
from keras.models import Sequential
from keras.layers import LSTM, Embedding, Dense, Bidirectional
vocabulary = 10000 # unique words in the dictionary
embedding_dim = 32 # shape(x) 词向量x的维度
word_num = 500 # sequence length 每个评论长度为500个单词
state_dim = 32 # shape(h) 状态向量h的维度
model = Sequential()
model.add(Embedding(vocabulary, embedding_dim, input_length=word_num)) # 词映射为向量
# return_sequences=False 只保留两条链最后的状态,输出两个状态向量的concat,其余状态向量都被扔掉了
model.add(Bidirectional(LSTM(state_dim, return_sequences=False, dropout=0.2))) # 指定状态向量h的维度 state_dim
model.add(Dense(1, activation='sigmoid')) # 全连接层
model.summary()
比如在训练卷积神经网络时,如果网络太大,而训练集不够大,可以先在ImageNet等大数据上预训练,这样可以让神经网络比较好的初始化,也可以避免overfitting
若Embedding层参数>>训练样本数量,会导致overfitting。可以对Embedding层做预训练
总结循环神经网络RNN:
SimpleRNN和LSTM都属于RNN
(1)SimpleRNN很容易遗忘,效果不好,实践中不用
(2)LSTM的记忆比SimpleRNN长很多,实践中都用LSTM(还有GRU,但是效果不如LSTM)
(1)双向LSTM效果比单向好
(2)RNN层可以像全连接层和卷积层那样累加起来,搭成一个深度神经网络。多层RNN容量比单层RNN更大,如果训练数据够多,多层RNN效果更好
(3)想把RNN用在文本问题上,需要有一个Embedding层把词变成向量,Embedding层有一个参数矩阵(大小是vocabulary×词向量的维度)。这个参数矩阵通常很大,若训练数据集比较小,Embedding层就不会训练的很好,会overfitting。解决办法是在大数据集上预训练Embedding Layer
输入半句话,预测 input text 的下一个字符。拿训练好的RNN来生成文本:
想要生成文本,首先需要训练一个RNN
划分segment 和 next_char:
len(text) = 600893 / stride = 3 约等于 200278 pairs
greedy selection,哪个字符的概率最大,就选择哪个字符 | 确定性的,没有随机性。给定初始的几个字符,后面生成的字符完全是确定的(完全取决于初始输入) |
从多项式分布中随机抽取。假如一个字符的概率值是0.3,那么它被选中的概率就是0.3 | 抽样过于随机,生成的文本会有很多拼写和语法错误 |
用介于0~1之间的temperature调整概率值:把概率值做幂变换,再归一化(大的概率值会变大,小的会变小。极端情况下,最大的概率值会变为1,其余都变为0,就相当于第一种确定性的选择) | 有随机性,但随机性不大,介于前两者之间。temperature越小,变换后的概率分布越极端 |
# greedy selection
next_index = np.argmax(pred)
# sample from the multinomial distribution
next_onehot = np.random.multinomial(1, pred, 1)
next_index = np.argmax(next_onehot)
# adjust the multinomial distribution
pred = pred ** (1/temperature) # controlled temperature
pred = pred/np.sum(pred)
temperature越小,变换后的概率分布越极端
文本生成是随机的,所以每次生成的都不一样
训练一个神经网络:
- 将文本划分为 (segment, next_char) pairs
- one-hot:
- char —> v×1 vector
- segment —> l×v matrix
- 构建+训练神经网络:l×v矩阵 —> LSTM —> Dense —> v×1 vector
文本生成:
- 输入一个seed segment
- 重复以下:
- 将one-hot后的segment输入神经网络
- 神经网络输出概率值
- 从概率值中采样生成next_char
- 将next_char append到segment后
机器翻译模型有很多种,这里介绍Seq2Seq(例:英译德)
机器翻译是个Many to Many的问题,输入、输出长度都大于1且不固定
例子里用的是char-level(比较方便,不用Embedding层),但最好用word-level(前提是需要有足够大的数据集)。原因:
- 英文平均每个单词有4.5个字母,用单词代替字符,输入序列就会短4.5倍。序列越短,越不容易遗忘
- word-level得到的Vocabulary大约为1w(也是one-hot的维度),必须要用word Embedding得到低维词向量(Embedding层参数数量太大,小数据量无法训练,会有overfitting的问题;或对Embedding层做预训练)
Seq2Seq有一个Encoder编码器(是个LSTM或其他RNN模型,用来从输入的句子中提取特征)和一个Decoder解码器(用来生成德语,就是文本生成器)
最后一轮:
用Seq2Seq做机器翻译:
模型有一个Encoder(每输入一个词就更新状态,把输入信息积累在状态里。最后一个状态就是从英文句子里积累的特征。只保留最后一个状态)和一个Decoder(Encoder的最后一个状态是Decoder的初始状态,初始化后Decoder就知道输入的英文句子了;然后Decoder就作为文本生成器,生成一句德语:首先把起始符\t作为Decoder RNN的输入,会更新状态为s1,全连接层输出预测概率为p1,根据概率分布做抽样生成下一个字符为z1;Decoder拿z1做输入,更新状态为s2,输出概率p2,得到新的字符z2,以此类推,直到输出停止符\n)
Seq2Seq的原理是Encoder处理输入的英语句子,把信息都压缩到状态向量里,最后一个状态是整句话的概要(包含整句英语的完整信息)。但若英语句子很长,早期的输入就会被遗忘
四种改进方法:
评估机器翻译的效果可以用BLEU(BiLingual Evaluation Understudy)指标,范围应该在0.1~0.5
- Wikipedia:https://en.wikipedia.org/wiki/BLEU
- Blog:A Gentle Introduction to Calculating the BLEU Score for Text in Python - MachineLearningMastery.com
有两个RNN网络,一个编码器Encoder(输入英语)和一个解码器Decoder(把英语翻译成德语)
缺陷:若输入句子很长,Encoder会记不住完整的句子,那么Decoder也就不可能产生正确的翻译
BLEU score是评价机器翻译好坏的标准,越高说明机器翻译越准确
解决Seq2Seq遗忘问题最有效的方法:Attention(Decoder每次更新状态的时候,都会再看一遍Encoder所有状态,这样就不会遗忘;Attention还会告诉Decoder应该关注Encoder哪个状态)
Attention可以大幅提高准确率,但计算量较大
在Encoder结束工作后,Attention和Decoder同时开始工作
Encoder的所有状态都要保留,并计算s0与每个状态的相关性α(也叫权重,介于0~1,求和为1)计算 hi 和 s0 的相关性,有2种方法:
(1)原论文提出:
tanh把每一个元素都压到 -1~1(2)更常用,同Transformer:
c0是Encoder所有状态的加权平均,故c0知道Encoder输入x1~xm的完整信息;
Decoder新状态s1依赖于c0,故Decoder也知道Encoder的完整输入,解决了RNN遗忘的问题
Attention的时间复杂度(也是weights的数量):Encoder 和 Decoder 状态数量的乘积
可视化:
每当Decoder想要生成一个状态时,都会看一遍Encoder的所有状态,同时权重weights会告诉Decoder要关注Encoder的哪个状态
Seq2Seq:Decoder基于当前状态来生成下一个状态,这样产生的新状态可能已经忘了Encoder的部分输入
Attention:Decoder在产生下一个状态之前,会先看一遍Encoder的所有状态,于是Decoder就知道Encoder的完整信息,并不会遗忘;除此之外,还能告诉Decoder应该关注Encoder的哪个状态
Attention可以大幅提升Seq2Seq模型的表现,缺点是计算量太大
假设输入Encoder的序列长度为m,Decoder输出序列长度为t
Seq2Seq:只需要Encoder读一遍输入序列,之后不会再看Encoder的输入或状态;Decoder依次生成输出序列,时间复杂度O(m+t)
Attention:Decoder每次更新状态,都要把Encoder的m个状态都看一遍,Decoder又有t个状态,故时间复杂度为O(mt)
Attention用在Seq2Seq上,Seq2Seq有2个RNN网络(一个Encoder一个Decoder)
而Self-Attention是把Attention用在一个RNN网络上
初始状态向量h0 和 Context Vector c0 都为全零向量
RNN读入第一个输入x1,需要更新状态h1:
计算新的Context Vector c1:是已有状态h的加权平均(由于初始状态h0是全零向量,故忽略h0,此时c1=h1)
计算新的状态h2:
计算新的Context Vector c2:
以此类推
RNN从左往右读一句话,红色是当前输入,高亮是权重很大的位置(说明前文中最相关的词是什么)初始状态向量h0 和 Context Vector c0 都为全零向量
重复以下步骤:
- 读入向量xi
- 用 xi 与 ci-1 计算出新的状态hi:hi = tanh(A·[xi ci-1]^T + b)
- 拿当前状态hi与h1~hi(h0为全零向量,不考虑)作对比,计算权重α1~αi
- 计算i个状态向量h的加权平均,得到新的context vector ci
RNN都有遗忘的问题,Self-Attention可以解决RNN遗忘的问题(每一轮更新状态之前,都会用Context Vector c看一遍之前所有的状态,这样就不会遗忘之前的信息了)
Self-Attention不局限于Seq2Seq模型,可以用在所有RNN上
除了避免遗忘,Self-Attention还能帮助RNN关注相关的信息
i是Encoder状态h的下标,j是Decoder状态s的下标
计算过程:
Attention中一共有3个参数矩阵:
Transformer里用的:
Transformer就是由Attention层(Seq2Seq)和Self-Attention层组成的
一共有3个参数矩阵,Encoder中有K和V,Decoder中有Q
如英译德,英语里有m个词变为词向量(即x1~xm),把当前生成的德语单词作为下一轮的输入:
Attention与RNN做机器翻译的不同在于:
Attention层:有两个输入序列X和X',有一个输出序列C,每个c向量对应一个x'向量
- Attention用于Seq2Seq,有2个输入序列(如英译德,英文一个输入序列,德语一个输入序列)
- Self-Attention不是Seq2Seq,它只有一个输入序列,其他跟Attention完全一样
以此类推计算得到其他α
以此类推计算得到其他c
ci 并非只依赖于 xi,而是依赖于所有m个x(改变任何一个x,输出的ci都会发生变化)
用于Seq2Seq,可以做机器翻译,输入是两个序列 输入只有1个序列,输出的c向量类比于RNN输出的状态向量。Single-Head Self-AttentionAttention最初提出是用在Seq2Seq模型,但Attention不局限于Seq2Seq,而是可以用在所有RNN上
若只有一个RNN网络,Attention就是Self-Attention
不用RNN,只用Attention,就是Transformer
由 l 个单头组成(不共享参数),每个单头有3个参数矩阵,故多头共有 3l 个参数矩阵
所有单头Self-Attention都有相同的输入x1~xm序列,但它们的参数矩阵各不相同,故输出的c序列也各不相同。把 l 个单头的输出(d×m)堆叠起来,作为多头的输出(ld×m)
所有单头Attention的输入都是两个序列x1~xm以及x1'~xt'
每个单头Attention都有各自的参数矩阵(不共享参数)
每个单头都有自己的输出序列c,把单头输出的序列c堆叠起来,就是多头的输出
输入x1~xm,输出u1~um。但ui依赖于x1~xm,而不是仅仅依赖于xi
Decoder的一个Block如图所示,需要两个输入序列,输出一个序列
Encoder:
Decoder:
两者输入、输出大小完全一样:
Encoder:有6个block(block之间不共享参数,block之间还有skip-connection的技巧),每个block = 多头self-attention + dense,每个block的输入、输出都是512×m(m是输入序列的长度)
Decoder:有6个block(block之间不共享参数,block之间还有skip-connection的技巧),每个block = 多头self-attention + 多头attention + dense,每个block的输入是两个序列:(512×m,512×t),输出一个序列512×t
Transformer:
- Seq2Seq模型,有Encoder和Decoder,可以用来做机器翻译;
- 不是RNN,无循环结构;
- 完全基于Attention和Self-Attention和全连接层;
- 和RNN的输入、输出大小一样
一种用来预训练Transformer Encoder网络的方法,从而大幅提高准确率。有以下2个任务:
Um不仅依赖于Xm,而是依赖于所有X向量。即 Um在 [MASK] 位置上,但它包含整句话的上下文信息
用反向传播算出损失函数关于模型参数的梯度,然后做梯度下降来更新模型参数
二分类:0代表False(两句话不相邻),1代表True
例1:
例2:
50%是确实相邻的两句话(标签True),还有50%的第二句话是随机抽取的(标签False)
向量c在[CLS]位置上,但它包含两句话的全部信息,所以靠向量c就能判断两句话是否真实相邻
这样做预训练有什么用呢?
把两句话拼接起来,并随机遮挡15%的单词
目标函数是多个损失函数的加和。把目标函数关于模型参数求梯度,然后做梯度下降来更新模型参数
bert可以利用海量数据来训练一个超级大的模型
bert的Embedding层不是简单的word embedding,还有一些技巧
Dosovitskiy. An image is worth 16×16 words: transformers for image recognition at scale. In ICLR.
就是Transformer Encoder网络
把图片划分为大小相同的patches(可以有重叠overlap,也可以没有)
对图片划分的时候,需要指定两个超参数:
每个小块都是一张彩色图片,有RGB三通道,即每个小块都是一个张量
把张量拉伸为向量:
设图片被划分为了n块,变为了n个向量,首先用全连接层对向量x做线性变换
对图片每一块的位置做编码
为什么要用PE?
输出n+1个向量c,其中c1~cn没有用,c0可以看做是从图片中提取的特征向量,用作分类任务
向量p的大小为类别的数量随机初始化神经网络参数 -> 在大数据集A(如JFT,三亿张图片)上做预训练 -> 在小数据集B(任务/目标数据集,如ImageNet图片分类,30w张图片)训练集上做微调 -> 在数据集B测试集上评价模型表现,得到测试准确率