转载自:https://zhuanlan.zhihu.com/p/55856680
Google 前段时间开源了他们的 BERT -- BidirectionalEncoderRepresentations fromTransformers 模型,使用多头注意力和位置嵌入,替换不易并行的循环神经网络,一举打破自然语言处理领域11个不同问题的最好记录。这个模型的开源,直接将自然语言处理推动到了一个新的时代,
google-research/bertgithub.com
上周末试着用 Google BERT 的阅读理解功能提取了《红楼梦》中对话的人物,
hahakity:BERT 应用之《红楼梦》中对话人物提取zhuanlan.zhihu.com
觉得此预训练模型真是NLP界的屠龙刀倚天剑。不需要结巴分词,不需要自己训练或者下载词向量,不需要Bi-LSTM,就那么粗鲁的把 BERT 的微调任务数据改成我们的数据,就可以得到比条件随机场好得多的结果。最关键的是,很多自然语言处理的判别和序列标注任务都可以用阅读理解来完成。再接再厉,这个周末尝试用BERT做中文文本分类。训练数据是从网上下载的新浪微博情感极性分类数据。此数据的每一条都包含一段新浪微博评论,以及评论的极性 -- 正向 或 负向。本次实验会先构建一个作为对比基准的 LSTM分类器,然后修改 Google BERT 的 run_classfier.py 文件,训练并分析微博情感极性。本次实验的代码和数据集可以开放获取,
https://gitlab.com/snowhitiger/weibo_sentiment_analysis.gitgitlab.com
基准模型
先建立基准模型,使用循环神经网络的单层 LSTM 分类器,来自Keras自带的对 IMDB 电影英文评论做极性分析的例子。此模型会作为对比BERT表现的基准模型,
x_train = sequence.pad_sequences(x_train, maxlen=maxlen)
x_test = sequence.pad_sequences(x_test, maxlen=maxlen)
model = Sequential()
model.add(Embedding(max_features, 128))
model.add(LSTM(128, dropout=0.2, recurrent_dropout=0.2))
model.add(Dense(1, activation='sigmoid'))
# try using different optimizers and different optimizer configs
model.compile(loss='binary_crossentropy',
optimizer='adam',
metrics=['accuracy'])
在单机上训练两个Epoch,得到测试精度为 ~ 95%。训练15个Epoch,测试精度为 ~97%。原则上说,这个精度已经非常之高,但是按20000组测试数据来看,3%的误差也对应着600多组错误分类的数据。改用双向 LSTM 可能会改进预言结果,不过我们现在想尝试的是 Google的预训练微调模型 BERT,所以就先省略其他的尝试。
准备数据
感谢这位昵称为SophonPlus的朋友在Github上共享了很多中文NLP训练语料,
SophonPlus/ChineseNlpCorpusgithub.com
这次实验使用其中的数据集:weibo_senti_100k
下载数据集后,下面的脚本可以把微博情感极性分类数据拆分成训练,验证,测试集,保证其可以被 BERT 的 runclassifier.py 中的 _read_tsv() 函数正确读入
#!/usr/bin/env python
import os
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.utils import shuffle
def train_valid_test_split(x_data, y_data,
validation_size=0.1, test_size=0.1, shuffle=True):
x_, x_test, y_, y_test = train_test_split(x_data, y_data, test_size=test_size, shuffle=shuffle)
valid_size = validation_size / (1.0 - test_size)
x_train, x_valid, y_train, y_valid = train_test_split(x_, y_, test_size=valid_size, shuffle=shuffle)
return x_train, x_valid, x_test, y_train, y_valid, y_test
if __name__ == '__main__':
path = "weibo_data/"
pd_all = pd.read_csv(os.path.join(path, "weibo_senti_100k.csv"))
pd_all = shuffle(pd_all)
x_data, y_data = pd_all.review, pd_all.label
x_train, x_valid, x_test, y_train, y_valid, y_test = \
train_valid_test_split(x_data, y_data, 0.1, 0.1)
train = pd.DataFrame({'label':y_train, 'x_train': x_train})
train.to_csv("train.csv", index=False, sep='\t')
valid = pd.DataFrame({'label':y_valid, 'x_valid': x_valid})
valid.to_csv("dev.csv", index=False, sep='\t')
test = pd.DataFrame({'label':y_test, 'x_test': x_test})
test.to_csv("test.csv", index=False, sep='\t')
修改模型
在 runclassifier.py 中有好几个文本分类的模型,我们只需要照葫芦画瓢,写一个自己的微博情感分类模型即可,所做的只不过是按格式读入我们准备的文本与标签数据。将下述代码加入到 run_classfier.py,
class WeiboProcessor(DataProcessor):
"""Processor for the WeiBo data set ."""
def get_train_examples(self, data_dir):
"""See base class."""
return self._create_examples(
self._read_tsv(os.path.join(data_dir, "train.csv")), "train")
def get_dev_examples(self, data_dir):
"""See base class."""
return self._create_examples(
self._read_tsv(os.path.join(data_dir, "dev.csv")), "dev")
def get_test_examples(self, data_dir):
"""See base class."""
return self._create_examples(
self._read_tsv(os.path.join(data_dir, "test.csv")), "test")
def get_labels(self):
"""See base class."""
return ["0", "1"]
def _create_examples(self, lines, set_type):
"""Creates examples for the training and dev sets."""
examples = []
for (i, line) in enumerate(lines):
# All sets have a header
if i == 0: continue
guid = "%s-%s" % (set_type, i)
text_a = tokenization.convert_to_unicode(line[1])
label = tokenization.convert_to_unicode(line[0])
examples.append(
InputExample(guid=guid, text_a=text_a, text_b=None, label=label))
return examples
此外,还要在这个文件的 main 函数中注册我们添加的模型,注意 cola, mnli, mrpc 和 xnli 都是BERT自带的文本分类任务,weibo 那一项是我们新加的,
processors = {
"cola": ColaProcessor,
"mnli": MnliProcessor,
"mrpc": MrpcProcessor,
"xnli": XnliProcessor,
"weibo":WeiboProcessor
}
至此,模型已修改完毕。
训练 BERT 微调任务
在 test_weibo.sh 中做相应的修改,运行: sh test_weibo.sh 即可开训,预测数据及精度放在weibo_output文件夹。
export BERT_BASE_DIR="path_to/chinese_L-12_H-768_A-12"
export WEIBO_DIR="path_to/weibo_data/"
python path_to/bert/run_classifier.py \
--task_name=WeiBo \
--do_train=True \
--do_eval=True \
--data_dir=$WEIBO_DIR \
--vocab_file=$BERT_BASE_DIR/vocab.txt \
--bert_config_file=$BERT_BASE_DIR/bert_config.json \
--init_checkpoint=$BERT_BASE_DIR/bert_model.ckpt \
--train_batch_size=32 \
--learning_rate=5e-5 \
--num_train_epochs=2.0 \
--max_seq_length=128 \
--output_dir=path_to/weibo_output/
运行结果
eval_accuracy = 0.97816485
eval_loss = 0.06019879
结果与LSTM相比并没有提高很多,可能是任务太简单了吧。检查预测错误的最后几个案例,发现标签本身也有问题,前面的标签 1 表示 高兴,积极的情绪,0 表示生气,消极的情绪。如下所示是BERT预言错误的例子,很多数据的原始标签是错的,所以此任务的分类精度不能说明太多问题,仅做参考。手动标错的数据会加黑 --
1 各种噩耗,速生鸡、塑化剂...我对洋洋说:"咱吃什么呢?吃草吧!"他回:"有农药!"我又说;...
1 上个月从德国定的eisenmann排气等了一个多月了,还没到货,真起急。今天过来西国贸这边只...
0 回复xxx:这个要吃,有计划[哈哈] //xxx:?得?有好吃的?仁冰[哈哈...
1 我的长假很开心?[嘻嘻]我的长假不尽兴?[抓狂]不管你是哪样,周六来看大本营,让陈奕迅为你的...
1 绝了[哈哈][哈哈] //xxx: [哈哈]笑暴了 //xxx: [哈哈] 玛滴!...
0 回复xxx:惭愧、惭愧,实事求是吧了! //xxx:你倒是会冲些呢嘛 /...
0 可能是拍电视剧吧,爱情雨。//xxx: 呵呵,心跳过快,脑子一片空百//xxxxx...
1 芒果[花心]芒果我爱你[爱你],可惜不能吃掉你~痛苦!想吃不能吃~[馋嘴][泪]
0 //xxx:xxxxx-海上梦 是个跨界作品是个新生儿,编导们还在创新还在提高演员们大进...
0 好不容易有我想看的比赛,晚上守着电视机等看马术盛装舞步个人决赛,结果从快结束处才开始直播,并...
1 疑惑的转~[思考] //xxx:很和谐!// xxx: [晕] // xxx...
1 #单城双恋#超级好看,看完很想去马拉玩????[鼓掌][抓狂]
0 这个我必须给你扎针了. 我可是娘家人//xxx: 分明是婚前最后的微笑[嘻嘻] //@...
1 必须转发[转发]//xxx: [晕]怎么被发出来啦。。同学们低调低调[害羞...
1 回复xxx:别灰心。。多转转哈 还有三次机会呢[嘻嘻] //@那女-ren-小...
1 怕老汉我死了,你成孤儿哈?[酷] //xxx:回复xxx:一派胡言、你个超级大神...
1 左边的你也跑不了//xxx: 右边的真像你![哈哈]//xxx: [笑哈哈]...
1 !!!!!妈呀,不是吧?!!! //xxx:?得胸啊~~~~ //xxx: ...
1 男男女女女好几个,没一个会打架的//xxx: //xxx:好[鼓掌] //xxx:...
1 [哈哈]很有娱乐精神 //xxx:[哇哈哈]卧槽!必须一位!!xxxxxxxxx ...
1 回复xxx:苦甜口吧![晕][晕] //xxx:苞米用火烤糊了是甜的还是苦的? //...
1 干扰的我不能工作。[晕]。公猫没了蛋蛋以后黏力加倍![爱你]
1 回复xxx:说得好,绿色多了,没注意搭配,光想着牙口不好滴小白鲨喜欢吃了,下...
1 人生已多风雨...接受现实吧[偷笑] //xxx: 往事不要再提[悲伤][悲伤]//@D...
0 回复xxx: 哈哈,我想说来着,没敢,还是你有魄力!不过猪同学那么温婉脾气,后果...
1 [哈哈][哈哈][哈哈]//xxx:[怒]
1 [花心][爱你][围观]我今天。。。[泪] //xxx:良师益友![赞]
1 [太阳][愤怒][熊猫][嘻嘻][猪头]
0 家里新房装修,这孩子上蹿下跳的到处玩,开心的不得了~ 两岁了,已经会跟我玩玩对话了。骗他也越...
为了用BERT训练出更好的中文文本情感分类器,可能需要更好的训练数据集了 :)