基於BERT做中文文本分類(情感分析)

Bert:

BERT是一種預訓練語言表示的方法,這意味着我們在大型文本語料庫(例如Wikipedia)上訓練通用的“語言理解”模型,然後將該模型用於我們關心的下游NLP任務,BERT優於之前的方法,因爲它是第一個用於預訓練NLP的無監督,深度雙向系統。

相關論文:

《Attention Is All You Need》

《BERT:Pre-training of Deep Bidirectional Transformers for Language Understanding》

之後可能會出一篇詳解bert原理的文章。

一、環境搭建:

Tensorflow>=1.11.0 我使用的1.12.0

Python 3.6.8

使用GPU訓練(官網說顯存要求大於12g)

服務器:1080Ti 32G

二、下載模型:

下載bert:https://github.com/google-research/bert
下載bert預訓練模型:https://storage.googleapis.com/bert_models/2018_11_03/chinese_L-12_H-768_A-12.zip

三、數據準備:

將你的語料分成3個文件,分別爲train.csv,test.csv,dev.csv三個(我使用的是csv文件,它與tsv區別就是分隔符號的不同,我直接將csv的分隔符‘,’轉成‘\t’),放入新建data文件夾下。

具體操作:

我的語料來自於情感分析比賽的,是判斷新聞標題情感積極消極還是中性,首先使用pandas對語料進行處理,最終處理成“label+content”的格式。如圖所示:

 

將語料分割成三個文件:我分割的比例是8:1:1,可以按照自己的比例進行分割。

#!/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 = "data/"
    pd_all = pd.read_csv(os.path.join(path, "outcleanfile.csv"))
    pd_all = shuffle(pd_all)
    x_data, y_data = pd_all.title, 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("data/train.csv", index=False,  encoding='utf-8',sep='\t')
    valid = pd.DataFrame({'label': y_valid, 'x_valid': x_valid})
    valid.to_csv("data/dev.csv", index=False,  encoding='utf-8',sep='\t')
    test = pd.DataFrame({'label': y_test, 'x_test': x_test})
    test.to_csv("data/test.csv", index=False, encoding='utf-8',sep='\t')

最終文件結構如圖:

四、修改代碼:

1.新定義處理類:

class NewsProcessor(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", "2"]

    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

2.處理類註冊:

在bert文件夾下的run_classifier.py中的def main(_):函數中將processors的內容增加爲:

processors = {
    "cola": ColaProcessor,
    "mnli": MnliProcessor,
    "mrpc": MrpcProcessor,
    "xnli": XnliProcessor,
    "news": NewsProcessor
}

五、訓練模型:

網上很多使用shell腳本運行,但是我試了n次總是傳不進去參數,直接修改了python文件裏的參數,不過還是把腳本放在這了,知道問題的小夥伴可以告知一聲。執行腳本或python文件前新建output文件用於訓練輸出。

export DATA_DIR=數據所在的路徑
export BERT_BASE_DIR=預訓練模型所在的路徑

python run_classifier.py \

 --task_name=news \

 --do_train=true \

 --do_eval=true \

 --data_dir=$DATA_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 \

 --max_seq_length=128 \

 --train_batch_size=32 \

 --learning_rate=2e-5 \

 --num_train_epochs=3.0 \

 --output_dir=/output

內存不足調整這兩個參數:

max_seq_length:發佈的模型經過訓練,序列長度最大爲512,但是您可以使用更短的最大序列長度進行微調,以節省大量內存。train_batch_size:內存使用也與批處理大小成正比。

訓練時長根據配置及數據情況而定,我的應該幾個小時就跑完了。訓練結果保存在output的eval_results.txt。如下:

六、分類預測

將剛纔的腳本文件修改爲如下:

python run_classifier.py 
  --task_name=news 
  --do_predict=true 
  --data_dir=./glue 
  --vocab_file=./uncased/uncased_L-12_H-768_A-12/vocab.txt 
  --bert_config_file=./uncased/uncased_L-12_H-768_A-12/bert_config.json 
  --init_checkpoint=./tmp/emotion/bert_model.ckpt
  --max_seq_length=128 
  --output_dir=./output/emotion_out/

或者直接更改run_classifier.py中的參數,將do_predict改爲True,do_train和do_eval改爲False。

最終得到一個tsv文件,文件中每一條是預測各個類(0、1、2)的概率,如下圖所示:

顯然,概率並不是我們想要的,我們需要將概率最終轉換成類別:

import os
import pandas as pd


if __name__ == '__main__':
    path = "output/emotion_out/"
    pd_all = pd.read_csv(os.path.join(path, "test_results.tsv") ,sep='\t',header=None)

    data = pd.DataFrame(columns=['polarity'])
    print(pd_all.shape)

    for index in pd_all.index:
        neutral_score = pd_all.loc[index].values[0]
        positive_score = pd_all.loc[index].values[1]
        negative_score = pd_all.loc[index].values[2]

        if max(neutral_score, positive_score, negative_score) == neutral_score:
            # data.append(pd.DataFrame([index, "neutral"],columns=['id','polarity']),ignore_index=True)
            data.loc[index+1] = ["neutral"]
        elif max(neutral_score, positive_score, negative_score) == positive_score:
            #data.append(pd.DataFrame([index, "positive"],columns=['id','polarity']),ignore_index=True)
            data.loc[index+1] = [ "positive"]
        else:
            #data.append(pd.DataFrame([index, "negative"],columns=['id','polarity']),ignore_index=True)
            data.loc[index+1] = [ "negative"]
        #print(negative_score, positive_score, negative_score)

    data.to_csv(os.path.join(path, "pre_sample.tsv"),sep = '\t')
    #print(data)

最終得到預測結果:

最後數據可能因爲不是均勻分佈,導致結果有些不準確,接下來從數據入手,整理下數據集。

參考鏈接:

https://github.com/google-research/bert

https://blog.csdn.net/qq874455953/article/details/90276116

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章