前言
网上有很多关于BERT代码运行的文章,但是关于ALBERT的却很少,好在是先试着用过了BERT的代码,因为种种问题才决定换ALBERT。写下这篇博客记录一下遇到的各种问题。
相比于BERT,ALBERT有多方面的提升。应用层面来看,主要是由于参数的共享,使得在模型体积明显的减小了。以base版的模型为例,BERT的模型接近ALBERT的10倍,虽然没有理论上的28倍那么夸张,运行的时候对资源的占用确实小了不少。之前用titan才能跑的BERT,现在用1080ti就能跑ALBERT了。
一、代码下载
谷歌官方代码(TF版):google-research/albert
Pytorch版:albert_pytorch
brightmart版albert:albert_zh
二、运行过程
笔者使用的是官方的TensorFlow版的代码,代码从github下载即可。
1.下载模型文件和数据集
官方提供的模型分为Base、Large、Xlarge、Xxlarge四种大小:
Base
Large
Xlarge
Xxlarge
下载好合适的版本解压出来即可,笔者用的是albert_base。
数据集用的是一个英文的文本数据,分为两句话有关联则为1,无关则为0,这样的一个二分类。和代码一起上传到网盘了(数据集、base模型和改好的代码打包在一起)。
链接: https://pan.baidu.com/s/1ZqbcwP205-LFTjAfETPiDw
提取码: 6bde
2.修改代码
本文以分类任务为例,主要是看run_classifier.py。
打开就能看到,from albert import ***,一看三行红线,赶紧去pip了albert包,还真下载到了。其实这里的albert并不是指能够在pip或者conda上下载到的albert包。这里就是指这些代码本身,看到网上有个方法是把这些代码放到一个文件夹里,我也没太明白,直接暴力删除from albert,直接从根目录import所需的文件。
然后直接运行run_classifier.py,把相同的报错的地方的from albert全部删除。
解决了这个问题,就可以开始写自己的类了。这和BERT里基本一样,都是定义了多个Processor,在main函数里声明。在run_classifier.py的main函数里找到processors,在最后加一个
"selfsort": classifier_utils.SelfProcessor,
接下来就是要写自己的processor类了,相比于BERT的源码,ALBERT将这部分的内容写在了classifier_utils.py文件里。在此,我们仿着源码里的Porcessor,加入自己的类SelfProcessor。代码如下:
class SelfProcessor(DataProcessor): #英文数据集
def get_train_examples(self, data_dir):
index = 0
examples = []
file_path = os.path.join(data_dir + "/train.csv")
f = csv.reader(open(file_path, 'r'))
for i in f:
guid = 'train-%d' % index
text = i[0].split("\t")
text_a = self.process_text(text[3])
text_b = self.process_text(text[4].replace("\n",""))
label = self.process_text(text[0])
examples.append(InputExample(guid=guid, text_a=text_a,
text_b=text_b, label=label))
index += 1
return examples
def get_dev_examples(self, data_dir):
index = 0
examples = []
file_path = os.path.join(data_dir + "/val.csv")
f = csv.reader(open(file_path, 'r'))
for i in f:
guid = 'val-%d' % index
text = i[0].split("\t")
text_a = self.process_text(text[3])
text_b = self.process_text(text[4].replace("\n",""))
label = self.process_text(text[0])
examples.append(InputExample(guid=guid, text_a=text_a,
text_b=text_b, label=label))
index += 1
return examples
def get_test_examples(self, data_dir):
"""See base class."""
index = 0
examples = []
file_path = os.path.join(data_dir + "/test.csv")
f = csv.reader(open(file_path, 'r', encoding='utf-8'))
for i in f:
guid = 'test-%d' % index
text = i[0].split("\t")
text_a = self.process_text(text[3])
text_b = self.process_text(text[4].replace("\n", ""))
label = self.process_text(text[0])
examples.append(InputExample(guid=guid, text_a=text_a,
text_b=text_b, label=label))
index += 1
return examples
def get_labels(self):
"""See base class."""
return ["0", "1"]
因为是判断两句话之间关系的分类任务,所以text_a和text_b都有值。
想用GPU跑的话,可以在run_classifier.py里加这么一句
os.environ["CUDA_VISIBLE_DEVICES"] = "0"
就可以调用单卡训练了,否则是使用CPU训练。
3.运行代码
按照官方给出的方法,在文件所在路径运行下面指令即可:
python albert.run_classifier \
--data_dir='./data'\
--output_dir='./output' \
--init_checkpoint=None \
--albert_config_file='./albert_base/albert_config.json' \
--task_name = 'selfsort' \
--vocab_file = './albert_base/30k-clean.vocab' \
--spm_model_file='./albert_base/30k-clean.model'\
--do_train = True\
--do_eval = True\
--do_predict = False\
--do_lower_case = True\
--max_seq_length=128 \
--optimizer=adamw \
--task_name= 'selfsort'\
--warmup_step=1000 \
--learning_rate=3e-5 \
--train_step=1000 \
--save_checkpoints_steps=100 \
--train_batch_size=128
而本人比较懒,不想每次运行代码都用这么长的指令,这些超参数我都写到了代码里,想偷懒的也可以这么做,因为文件的位置总是不变的。
4.运行结果
训练了1000步,正确率66%,也不是特别高,训练100次的时候就达到差不多的水平。
遇到的问题
1.各种库的安装
按照官方的说法,代码推荐的tensorflow版本是1.15,笔者最后也是下载的这个,还有类似tensorflow_hub和sentencepiece,pip install装不上就用conda install,总有一款适合你。
2.显存不足
改小超参数就好了,max_seq_length、train_batch_size、eval_batch_size这些改小点。
3.改了代码和数据集但预测结果一直不变
这个问题坑了我一下午,最后发现是缓存在cached文件夹下的tfrecord文件导致的。第一次运行的时候读取出来的数据会缓存到cached_dir路径下,便于训练的时候读取。但是这个第一次写入后,再次运行的时候并不会覆写掉,而是直接调用。所以在测试的过程中,运行一次代码清空一下output和cached文件夹。
如需转载请标记出处。