2018年google推出了bert模型,這個模型的性能要遠超於以前所使用的模型,總的來說就是很牛。但是訓練bert模型是異常昂貴的,對於一般人來說並不需要自己單獨訓練bert,只需要加載預訓練模型,就可以完成相應的任務。下面我將以情感分類為例,介紹使用bert的方法。這里與我們之前調用API寫代碼有所區別,已經有大神將bert封裝成.py文件,我們只需要簡單修改一下,就可以直接調用這些.py文件了。
官方文檔
具體實現
我這里使用的是pytorch版本。
前置需要
- 安裝pytorch和tensorflow。
- 安裝PyTorch pretrained bert。(pip install pytorch-pretrained-bert)
- 將pytorch-pretrained-BERT提供的文件,整個下載。
- 選擇並且下載預訓練模型。地址:請點擊
注意這里的model是tensorflow版本的,需要進行相應的轉換才能在pytorch中使用
無論是tf版還是pytorch版本,預訓練模型都需要三個文件(或者功能類似的)
- 預訓練模型文件,里面保存的是模型參數。
- config文件,用來加載預訓練模型。
- vocabulary文件,用於后續分詞。
模型轉換
文檔里提供了convert_tf_checkpoint_to_pytorch.py 這個腳本來進行模型轉換。使用方法如下:
export BERT_BASE_DIR=/path/to/bert/uncased_L-12_H-768_A-12
pytorch_pretrained_bert convert_tf_checkpoint_to_pytorch \
$BERT_BASE_DIR/bert_model.ckpt \
$BERT_BASE_DIR/bert_config.json \
$BERT_BASE_DIR/pytorch_model.bin
修改源碼
這里是需要實現情感分類。只需要用到run_classifier_dataset_utils.py和run_classifier.py這兩個文件。run_classifier_dataset_utils.py是用來處理文本的輸入,我們只需要添加一個類用來處理輸入即可。
class MyProcessor(DataProcessor):
'''Processor for the sentiment classification data set'''
def get_train_examples(self, data_dir):
"""See base class."""
logger.info("LOOKING AT {}".format(os.path.join(data_dir, "train.tsv")))
return self._create_examples(
self._read_tsv(os.path.join(data_dir, "train.tsv")), "train")
def get_dev_examples(self, data_dir):
"""See base class."""
return self._create_examples(
self._read_tsv(os.path.join(data_dir, "dev.tsv")), "dev")
def get_labels(self):
"""See base class."""
return ["-1", "1"]
def _create_examples(self, lines, set_type):
"""Creates examples for the training and dev sets."""
examples = []
for (i, line) in enumerate(lines):
if i == 0:
continue
guid = "%s-%s" % (set_type, i)
text_a = line[0]
label = line[1]
examples.append(
InputExample(guid=guid, text_a=text_a, text_b=None, label=label))
return examples
train.tsv和dev.tsv分別表示訓練集和測試集。記得要在下面的代碼加上之前定義的類。
def compute_metrics(task_name, preds, labels):
assert len(preds) == len(labels)
if task_name == "cola":
return {"mcc": matthews_corrcoef(labels, preds)}
elif task_name == "sst-2":
return {"acc": simple_accuracy(preds, labels)}
elif task_name == "mrpc":
return acc_and_f1(preds, labels)
elif task_name == "sts-b":
return pearson_and_spearman(preds, labels)
elif task_name == "qqp":
return acc_and_f1(preds, labels)
elif task_name == "mnli":
return {"acc": simple_accuracy(preds, labels)}
elif task_name == "mnli-mm":
return {"acc": simple_accuracy(preds, labels)}
elif task_name == "qnli":
return {"acc": simple_accuracy(preds, labels)}
elif task_name == "rte":
return {"acc": simple_accuracy(preds, labels)}
elif task_name == "wnli":
return {"acc": simple_accuracy(preds, labels)}
elif task_name == "my":
return acc_and_f1(preds, labels)
else:
raise KeyError(task_name)
processors = {
"cola": ColaProcessor,
"mnli": MnliProcessor,
"mnli-mm": MnliMismatchedProcessor,
"mrpc": MrpcProcessor,
"sst-2": Sst2Processor,
"sts-b": StsbProcessor,
"qqp": QqpProcessor,
"qnli": QnliProcessor,
"rte": RteProcessor,
"wnli": WnliProcessor,
"my": MyProcessor
}
output_modes = {
"cola": "classification",
"mnli": "classification",
"mrpc": "classification",
"sst-2": "classification",
"sts-b": "regression",
"qqp": "classification",
"qnli": "classification",
"rte": "classification",
"wnli": "classification",
"my": "classification"
}
運行bert
編輯shell腳本:
#!/bin/bash
export TASK_NAME=my
python run_classifier.py \
--task_name $TASK_NAME \
--do_train \
--do_eval \
--do_lower_case \
--data_dir /home/garvey/Yuqinfenxi/ \
--bert_model /home/garvey/uncased_L-12_H-768_A-12 \
--max_seq_length 410 \
--train_batch_size 8 \
--learning_rate 2e-5 \
--num_train_epochs 3.0 \
--output_dir /home/garvey/bertmodel
運行即可。這里要注意max_seq_length和train_batch_size這兩個參數,設置過大是很容易爆掉顯存的,一般來說運行bert需要11G左右的顯存。
備注
max_seq_length是指詞的數量而不是指字符的數量。參考代碼中的注釋:
The maximum total input sequence length after WordPiece tokenization. Sequences longer than this will be truncated, and sequences shorter than this will be padded.
對於sequence的理解,網上很多博客都把這個翻譯為句子,我個人認為是不准確的,序列是可以包含多個句子的,而不只是單獨一個句子。
注意
Bert開源的代碼中,只提供了train和dev數據,也就是訓練集和驗證集。對於評測論文標准數據集的時候,只需要把訓練集和測試集送進去就可以得到結果,這一過程是沒有調參的(沒有驗證集),都是使用默認參數。但是如果用Bert來打比賽,注意這個時候的測試集是沒有標簽的,這就需要在源碼中加上一個處理test數據集的部分,並且通過驗證集來選擇參數。
補充
在大的預訓練模型例如像bert-large在對小的訓練集進行精細調整的時候,往往會導致性能退化:模型要么運行良好,要么根本不起作用,在我們用bert-large對一些小數據集進行微調,直接使用默認參數的話二分類的准確率只有0.5,也就是一點作用也沒有,這個時候需要對學習率和迭代次數進行一個調整才會有一個正常的結果,這個問題暫時還沒有得到解決。