logo资料库

BERT实现情感分析..docx

第1页 / 共7页
第2页 / 共7页
第3页 / 共7页
第4页 / 共7页
第5页 / 共7页
第6页 / 共7页
第7页 / 共7页
资料共7页,全文预览结束
使用BERT实现情感分析
使用 BERT 实现情感分析 在 CV 问题中,目前已经有了很多成熟的模型供大家使用,只需要结合特定 的业务场景修改结尾的全连接层或添加 softmax 层即可满足需求,也就是我们常 说的迁移学习。那么在 NLP 领域是否有这样泛化能力很强的模型呢,答案是肯定 的。 2018 年 底 , Google 推 出 了 一 个 打 破 11 项 NLP 任 务 的 模 型 BERT (Bidirectional Encoder Representation from Transformers),该模型一经 问世就火遍 AI 领域并受到了广大开发者的青睐,可以说是 NLP 领域中具有里程 碑意义的模型,目前 BERT 依旧是比赛中或者工业界首选的模型,各大公司也均 基于 BERT 进行了更多的升级与优化。 BERT 是一个预训练模型,该模型的训练阶段分为两个部分,预训练与微调, 预训练阶段 Google 已经处理好,如果要使用该模型,只需要针对特定场景进行 微调即可。在本章节中,我们会先介绍 BERT 的原理,再以一个实际的例子来讲 解如何微调。 1. Transformer 模型 在 NLP 任务中,常用的特种提取器有 RNN 及其变体、CNN 搭配池化层、 Transformer 等,RNN 类型的提取器有一个最大的优点能捕捉长依赖信息,但是 其速度很慢,CNN 搭配池化层能有效获取一些重要的特征并忽略没有意义的特征, 但是却无法捕捉长依赖信息,Transformer 兼具了 RNN 与 CNN 的优点,在保留长 依赖信息的同时速度也很快,其中的 attention 机制也使其具有了类似最大池化 层捕捉重要特征的能力。BERT 的特征提取器实际上就是采用的 Transformer 的 encoder 层,Google 提供了两个版本的 BERT,其中 base 版本的是由 12 层的 Transformer 的 encoder 堆叠在一起,large 版本的是由 24 层的 Transformer 的 encoder 堆叠在一起。 Transformer 模型是基于 encoder-decoder 结构的,如图 10.1-1 所示。其中 encoder 层由 6 层图中所示的结构堆叠而成,每一个层又包括了两个子层,第一 个子层是 multi-head self-attention,第二个子层是一个全连接层,这两个子 层 均 采 用 了 residual connection 来 进 行 连 接 , 并 且 还 有 一 层 layer normalization 层。decoder 和 encoder 的结构类似,也是由 6 层图中所示的结 构堆叠而成,除了 encoder 提到的两层结构外,decoder 层还有一层额外的 masked multi-head attention。 attention 是一种加权机制,针对候选值进行加权求和。Attention 能表述 为一个 query 与一个 key-value 集合的映射关系,其中 query,keys,values 都是向量,输出值是 values 的加权求和,这个权重是根据 query 与 key 来计算 出来的。
图 10.1-1 Transformer 结构 图 10.1-2 Scaled Dot-Product Attention 在 Transformer 中也运用了 attention 机制,并称为 Scaled Dot-Product Attention,如图 10.1-2 所示。其输入值是由维度为的 queries、keys,维度 为的 values 构成,首先对 query 与 keys 做一个点积的运算再除以 ,然后 使用 softmax 计算出其权重并运用在 values 上。通常,计算 attention 值的时 候会采用以集合的形式来降低运算次数,多个 queries、keys 与 values 组合在 一起构成矩阵 Q、K、V,最终计算 attention 值的时候即可以如下公式来表示。 attentionQ,K,V =softmax()V attention 的计算方法其实有很多种,最常用的有 additive attention 与
dot-product attention,Transformer 中提到的 Scaled Dot-Product Attention 其实就是 dot-product attention 的一个变体,添加了一个缩放因子 ,如果 的值很小,那么这两种 attention 操作得到的结果差不多,但是当的值很大 的时候,那么点积之后的结果某些值也会很大,softmax 之后就很容易出现一个 值全盘通吃的情况,而缩放因子就是为了解决这个问题而提出来的。 对于普通的 Attention 机制只能得到一个维度的加权求和值,transformer 采用了一种叫做 Multi-Head Attention 的方式,把 Q,K,V 做多种简单的线性变 换再用 Scaled Dot-Product Attention 计算 attention 的值,最终的结果则是 由多个 attention 的值拼接在一起并进行一个维度变换如图 10.1-3 所示。 图 10.1-3 Multi-Head Attention Multi-head attention 让模型能从多个不同的角度去获取文本的信息,其计 算过程也可以用以下公式来表示: MutilHeadQ,K,V =Concat(ℎ1…ℎℎ) ℎ=Attention(Q,K,V) 其中,,对应的是 Q,K,V 的三个线性变换矩阵,的作用是用来对 拼接后的结果进行降维,head 的个数选择的是 8。 2. BERT 预训练 BERT 的 base 版本是由 12 个 transformer 的 encoder 层堆叠在一起,没有用 到 decoder 层,因此 transformer 的 decoder 这里就不再赘述,感兴趣的读者可 自己阅读 transformer 的论文。接下来我们就一起来看下 BERT 的训练阶段是怎 么做的。
BERT 的预训练阶段采用了两个独有的非监督任务,一个是 Masked Language Model,还有一个是 Next Sentence Prediction。 Masked Language Model 可以理解为完形填空,随机 mask 掉训练预料中 15% 的词,用其上下文来做预测,例如: my dog is hairy → my dog is [MASK] 此处将 hairy 进行了 mask 处理,再采用非监督学习的方法预测 mask 位置的 词是什么,但是该方法有一个问题,因为是 mask 掉 15%的词,其数量已经很高 了,这样就会导致某些词在微调阶段从未见过,为了解决这个问题,作者做了如 下的处理。  80%的时间是采用[mask],my dog is hairy → my dog is [MASK]  10%的时间是随机取一个词来代替 mask 的词,my dog is hairy -> my dog is apple  10%的时间保持不变,my dog is hairy -> my dog is hairy 那么为啥要以一定的概率使用随机词呢?这是因为 transformer 要保持对每 个输入 token 分布式的表征,否则 Transformer 很可能会记住这个[MASK]就是 "hairy"。至于使用随机词带来的负面影响,Google 认为所有其他的 token(即非 "hairy"的 token)共享 15%*10% = 1.5%的概率,其影响是可以忽略不计的。 BERT 另一个预训练阶段的任务是 Next Sentence Prediction,选一些句子 对 A 与 B,其中 50%的数据 B 是 A 的下一条句子,剩余 50%的数据 B 是语料库中 随机选择的,学习其中的相关性,添加这样的预训练的目的是目前很多 NLP 的任 务比如 QA 和 NLI 都需要理解两个句子之间的关系,从而能让预训练的模型更好 的适应这样的任务。 3. 使用 BERT 进行文本分类 接下来我们以一个情感分析的例子来介绍如何在 keras 中使用对 bert 进行 微调。情感分析的应用场景很多,也是 nlp 领域一个最常见的任务,本文将会以 IMDB 数据集为例,该数据集是一个开源的电影评价数据集,我们的任务是根据 文本信息判断评价是正面情绪还是负面情绪。 在 keras 中,如果需要使用 BERT,只需要安装一个第三方库 keras-bert, 执行以下命令:pip install keras-bert 接下来需要下载 Google 官方的 BERT 预训练模型,下载好后解压到同级目录, 下载地址请参考以下页面:https://github.com/google-research/bert 数据集的下载地址为:http://ai.stanford.edu/~amaas/data/sentiment/ 接下来就是编码部分,首先定义一些需要用到的数据,包括 BERT 的位置, 序列的最大长度,batch_size 的值。
1 2 3 4 5 config_path = 'uncased_L-12_H-768_A-12/bert_config.json' checkpoint_path = 'uncased_L-12_H-768_A-12/bert_model.ckpt' vocab_path = 'uncased_L-12_H-768_A-12/vocab.txt' max_len = 32 batch_size = 64 def remove_html(text): 定义使用正则表达式去除一些异常的 HTML 标签的函数 1 2 3 r = re.compile(r'<[^>]+>') return r.sub('', text) 定义读取数据的函数,该函数能根据数据的格式做一些处理并读取到内存中 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 def read_file(filetype): Path = './aclImdb/' file_list = [] positive = path + filetype + '/pos/' for f in os.listdir(positive): file_list += [positive + f] negative = path + filetype + '/neg/' for f in os.listdir(negative): file_list += [negative + f] label = ([1] * 12500 + [0] * 12500) text = [] for f_ in file_list: with open(f_, encoding='utf8') as f: text += [remove_html(''.join(f.readlines()))] return label, text 将 BERT 官方提供的词典中的词添加到 keras-bert 的字典中,以便分词时使用。 19 token_dict = {} 20 with codecs.open(vocab_path, 'r', 'utf8') as reader: 21 22 23 24 tokenizer = Tokenizer(token_dict) token = line.strip() token_dict[token] = len(token_dict) for line in reader: 为了保证序列的长度一样,需要对小于最大长度的序列补 0。 25 def seq_padding(text, padding=0): 26 27 max_len = max([len(t) for t in text]) return np.array([ np.concatenate([t,[padding]*(max_len len(t) < max_len else t for t in text - len(t))]) if ])
while True: idx = list(range(len(data[0]))) np.random.shuffle(idx) X, S, Y = [], [], [] for i in idx: 因为数据量比较大,这里新建一个生成器,根据 batch_size 的大小来准备当前 显存可用的数据。 28 def data_generator(data, batch_size=64): 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 text = data[0][i][:max_len] x, s = tokenizer.encode(first=text) y = data[1][i] X.append(x) S.append(s) Y.append([y]) if len(X) == batch_size or i == idx[-1]: X = seq_padding(X) S = seq_padding(S) Y = seq_padding(Y) yield [X, S], Y [X, S, Y] = [], [], [] 接下来调用 keras-bert 封装的方法,加载 BERT 模型到内存中,并把每一层设置 为可训练,这样在我们微调的过程中参数才会进行更新。 46 bert_model=load_trained_model_from_checkpoint(config_path, checkpoint_path, seq_len=None) 47 for l in bert_model.layers: 48 l.trainable = True 然后开始构建模型,模型的输入包括两个输入值,其中 x 对应的是分词在词典中 对应的下标 index,s 表示的是 BERT 需要的段落向量,这里只要一个序列,因此 s 的值全为 0。然后把 x 与 s 作为 BERT 的输入值,得到 BERT 的输出,这里我们 只需要取句向量,也就是 BERT 中[CLS]对应的值,X[:0]表示的即是该值,最后 接上一个全连接层与一个 sigmoid 层做二分类即可。 49 x = Input(shape=(None,)) 50 s = Input(shape=(None,)) 51 52 out = bert_model([x, s]) 53 out = Lambda(lambda x: x[:, 0])(out) 54 out = Dense(1, activation=’sigmoid’)(out) 55 model = Model([x, s], out) 模型定义完成后,即可定义其训练所需参数,其中损失函数采用的是交叉熵损失, 优化器采用的是 Adam 学习率为 1e-4,评估指标为 accuracy。然后调用上文定义
好的生成器来生成与 batch_size 一样长的训练数据与验证数据。最后执行 fit_generator 方法即可开始训练。 56 model.compile( loss='binary_crossentropy', optimizer=Adam(1e-4), metrics=['accuracy'] ) 57 model.summary() 58 train_data = read_file('train') 59 test_data = read_file('test') 60 train_gen = data_generator(train_data, batch_size) test_gen = data_generator(test_data, batch_size) 25 model.fit_generator( train_gen, steps_per_epoch=len(train_data[0]) // batch_size, epochs=5, validation_data=test_gen, validation_steps=len(test_data[0]) // batch_size ) 4. 小结 本节主要讲解了 BERT 模型的原理,并采用 keras 微调 BERT 实现了情感分析。 BERT 作为一个目前热门的预训练模型,其效果突出,在文本特征提取阶段均可 采用该模型,再根据具体的业务场景对损失函数进行修改即可实现对应的模型搭 建。当然在使用 keras-bert 之前建议读者务必弄清楚其原理,毕竟知其然还需 知其所以然。
分享到:
收藏