使用bert進行情感分類

2018年google推出了bert模型,這個模型的性能要遠超於之前所使用的模型,總的來講就是很牛。可是訓練bert模型是異常昂貴的,對於通常人來講並不須要本身單獨訓練bert,只須要加載預訓練模型,就能夠完成相應的任務。下面我將以情感分類爲例,介紹使用bert的方法。這裏與咱們以前調用API寫代碼有所區別,已經有大神將bert封裝成.py文件,咱們只須要簡單修改一下,就能夠直接調用這些.py文件了。python

官方文檔

  1. tensorflow版:點擊傳送門
  2. pytorch版(注意這是一個第三方團隊實現的):點擊傳送門
  3. 論文:點擊傳送門
    一切以官方論文爲準,若是有什麼疑問,請仔細閱讀官方文檔

具體實現

我這裏使用的是pytorch版本。git

前置須要

  1. 安裝pytorch和tensorflow。
  2. 安裝PyTorch pretrained bert。(pip install pytorch-pretrained-bert)
  3. 將pytorch-pretrained-BERT提供的文件,整個下載。
  4. 選擇而且下載預訓練模型。地址:請點擊
    注意這裏的model是tensorflow版本的,須要進行相應的轉換才能在pytorch中使用

不管是tf版仍是pytorch版本,預訓練模型都須要三個文件(或者功能相似的)github

  1. 預訓練模型文件,裏面保存的是模型參數。
  2. config文件,用來加載預訓練模型。
  3. vocabulary文件,用於後續分詞。

模型轉換

文檔裏提供了convert_tf_checkpoint_to_pytorch.py 這個腳原本進行模型轉換。使用方法以下:shell

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是用來處理文本的輸入,咱們只須要添加一個類用來處理輸入便可。json

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分別表示訓練集和測試集。記得要在下面的代碼加上以前定義的類。bash

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腳本:app

#!/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,也就是一點做用也沒有,這個時候須要對學習率和迭代次數進行一個調整纔會有一個正常的結果,這個問題暫時尚未獲得解決。

相關文章
相關標籤/搜索