今天来讲一讲怎么用bert做文本分类,比赛是今年的CCF-BDCI
https://www.datafountain.cn/competitions/350
第一次接触bert,比较菜,线上macro_f1得分0.79:
附上代码和数据集,数据集是tsv格式,我做了一些预处理,保存在data文件夹下面,验证集是从原训练集中按0.1的比例划分出来的。测试集test是没有标签的,想跑线下可以用train_test_split自己划分测试集:
链接:https://pan.baidu.com/s/1o44-3cAPcSXS8k4zA5A0pw
提取码:kvkl
很多人使用bert的时候都会担心配置要求,其实正常的内存和cpu就是可以跑的,数据集精简一下也不会太慢,我8G内存I7,跑batch_size=1,max_seq_length=32的1/10数据集20分钟就跑完了。
其实bert的使用非常简单,首先下载一下中文bert的模型:
https://storage.googleapis.com/bert_models/2018_11_03/chinese_L-12_H-768_A-12.zip
如果你做的不是中文的或者想跑bert-large,这篇博文有下载地址:
https://blog.csdn.net/foxcow2012/article/details/87856557
然后去下载一下google的开源代码:
https://github.com/google-research/bert
先来看看bert模型本身的文件结构(chinese_L-12_H-768_A-12文件夹),不妨称之为模型文件夹。
1.bert_config是bert的配置文件,这个不用改,后面运行的时候可以通过参数调整。
2.ckpt文件就是bert的模型
3.vocab.txt是bert的词汇表。打开会发现中文bert的词汇表都是单字的,这当然不如用词汇效果好。据说哈工大发布过一版词汇的,我没用过,可以去搜搜看。
把这个文件夹(chinese_L-12_H-768_A-12)放到刚刚的另一个文件夹(很多文件那个)里面,我们会通过那个文件夹的代码调用chinese_L-12_H-768_A-12里的模型和文件。不妨称之为接口文件夹。
因为我们做的是分类任务,接口文件夹里比较重要的是这个:
所以我们也需要修改的也只有这个文件夹。
参考的这篇博文:
https://blog.csdn.net/clnjust/article/details/100514231
1.再run_classifier.py中添加一个类,这个类就用来执行我们自定义的分类任务:
class MyTaskProcessor(DataProcessor):
"""Processor for my task-news classification """
def __init__(self):
self.labels = ['0.0', '1.0', '2.0']
def get_train_examples(self, data_dir):
return self._create_examples(
self._read_tsv(os.path.join(data_dir, 'train.tsv')), 'train')
def get_dev_examples(self, data_dir):
return self._create_examples(
self._read_tsv(os.path.join(data_dir, 'val.tsv')), 'val')
def get_test_examples(self, data_dir):
return self._create_examples(
self._read_tsv(os.path.join(data_dir, 'test.tsv')), 'test')
def get_labels(self):
return self.labels
def _create_examples(self, lines, set_type):
"""create examples for the training and val sets"""
examples = []
for (i, line) in enumerate(lines):
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, label=label))
return examples
注意代码第4行是写任务的标签,是以字符串的形式读取的,我的数据文件里标签是0.0,1.0,2.0,你的是0,1,2就改成self.labels = [‘0’, ‘1’, ‘2’],因为是字符串类型读取,0.0≠0!
这个文件里会有一些配置变量,比如这段代码中的data_dir,会在运行时通过参数传入值。
2.然后在程序入口main里做一下修改,把我们的任务加进去:
def main(_):
tf.logging.set_verbosity(tf.logging.INFO)
processors = {
"cola": ColaProcessor,
"mnli": MnliProcessor,
"mrpc": MrpcProcessor,
"xnli": XnliProcessor,
"mytask": MyTaskProcessor,
}
我是在根目录(接口文件夹)下面新建了一个data文件夹来保存,这样待会儿写参数的时候比较好写:
直接在cmd里用命令行运行,cd进根目录,我这个就是E:\python\bert\bert-masterContent,
1.训练命令:
python run_classifier.py --task_name=mytask --do_train=true --do_eval=true --data_dir=data --vocab_file=chinese_L-12_H-768_A-12\vocab.txt --bert_config_file=chinese_L-12_H-768_A-12\bert_config.json --init_checkpoint=chinese_L-12_H-768_A-12\bert_model.ckpt --max_seq_length=128 --train_batch_size=32 --learning_rate=1e-5 --num_train_epochs=3.0 --output_dir=output
task_name是main函数里的任务名(是前面的key);do_train就是是否做训练;do_eval是是否做验证,要求有验证集,做的话训练结束之后会在output目录下生成一个预测文件(eval_results.txt),长这样:
data_dir就是你存放数据集文件的文件夹;vocab_file、bert_config_file、init_checkpoint就都是之前说的对bert模型的几个文件的调用路径;max_seq_length是最大句长,就是一句话最多多少个字,batch_size就是一批几条文本;learning_rate是学习率,我在上一篇博文中详细说过;num_train_epochs就是迭代次数;output_dir即输出到哪个文件夹下面,我是输出到output文件夹下面。
2.测试命令:
python run_classifier.py --task_name=mytask --do_predict=true --data_dir=data --vocab_file=chinese_L-12_H-768_A-12\vocab.txt --bert_config_file=chinese_L-12_H-768_A-12\bert_config.json --init_checkpoint=output --max_seq_length=128 --output_dir=output
参数和上面差不多,我就不详细讲解了。生成的输出文件会是m*n的浮点数组,m是测试集文本数,n是分类的类数。第i行第j列的数据表示第i条文本属于第j类的概率。
输出文件叫test_results.tsv,长这样:
没错,其实就是带参数的运行一下run_classifier.py。这里值得注意的是linux环境目录间的分隔符是/,而windows环境是\。
训练的时候控制台是这样的:
如果你想一次执行掉训练和预测可以写批处理文件,windows是.bat,linux是.sh。
内存不够就把max_seq_length和batch_size调小。