1. 程式人生 > >如何使用BERT實現中文的文字分類(附程式碼)

如何使用BERT實現中文的文字分類(附程式碼)

如何使用BERT模型實現中文的文字分類

前言

  1. Google官方BERT程式碼(Tensorflow)
  2. 本文章參考的BERT分類程式碼(Pytorch)
  3. 本文章改進的BERT中文文字分類程式碼(Pytorch)
  4. BERT模型介紹

Pytorch

readme

  • 請先安裝pytorch的BERT程式碼,程式碼源見前言(2)
    pip install pytorch-pretrained-bert
    

引數表

data_dir bert_model task_name
輸入資料目錄 載入的bert模型,對於中文文字請輸入’bert-base-chinese 輸入資料預處理模組,最好根據應用場景自定義
model_save_pth
max_seq_length* train_batch_size
模型引數儲存地址 最大文字長度 batch大小
learning_rate num_train_epochs
Adam初始學習步長 最大epoch數

* max_seq_length = 所設定的文字長度 + 2 ,BERT會給每個輸入文字開頭和結尾分別加上[CLS][SEP]識別符號,因此會佔用2個字元空間,其作用會在後續進行詳細說明。

演算法流程

1. 概述

2. 讀取資料

  • 對應於引數表中的task_name,是用於資料讀取的模組
  • 可以根據自身需要自定義新的資料讀取模組
  • 以輸入資料為json檔案時為例,資料讀取模組包含兩個部分:
    • 基類DataProcessor
      class DataProcessor(object):		
          def get_train_examples(self, data_dir):
              raise NotImplementedError()
      
          def get_dev_examples(self, data_dir):
              raise NotImplementedError()
      
          def get_test_examples(self, data_dir):
              raise NotImplementedError()
      
          def get_labels(self):
              raise NotImplementedError()
      
          @classmethod
          def _read_json(cls, input_file, quotechar=None):
              """Reads a tab separated value file."""
              dicts = []
              with codecs.open(input_file, 'r', 'utf-8') as infs:
                  for inf in infs:
                      inf = inf.strip()
                      dicts.append(json.loads(inf))
              return dicts
      
    • 用於資料讀取的模組MyPro
      class MyPro(DataProcessor):
          def get_train_examples(self, data_dir):
              return self._create_examples(
                  self._read_json(os.path.join(data_dir, "train.json")), 'train')
      
          def get_dev_examples(self, data_dir):
              return self._create_examples(
                  self._read_json(os.path.join(data_dir, "val.json")), 'dev')
      
          def get_test_examples(self, data_dir):
              return self._create_examples(
                  self._read_json(os.path.join(data_dir, "test.json")), 'test')
      
          def get_labels(self):
              return [0, 1]
      
          def _create_examples(self, dicts, set_type):
              examples = []
              for (i, infor) in enumerate(dicts):
                  guid = "%s-%s" % (set_type, i)
                  text_a = infor['question']
                  label = infor['label']
                  examples.append(
                      InputExample(guid=guid, text_a=text_a, label=label))
              return examples
      
  • 需要注意的幾點是:
    1. data_dir目錄下應包含名為trainvaltest的三個檔案,根據檔案格式不同需要對讀取方式進行修改
    2. get_labels()返回的是所有可能的類別label_list,比如['數學', '英語', '語文'][1, 2, 3]
    3. 模組最終返回一個名為examples的列表,每個列表元素中包含序號、中文文字、類別三個元素

3. 特徵轉換

  • convert_examples_to_features是用於將examples轉換為特徵,也即features的函式。
  • features包含4個數據:
    • input_ids:分詞後每個詞語在vocabulary中的id,補全符號對應的id為0,[CLS][SEP]的id分別為101和102。應注意的是,在中文BERT模型中,中文分詞是基於字而非詞的分詞
    • input_mask:真實字元/補全字元識別符號,真實文字的每個字對應1,補全符號對應0,[CLS][SEP]也為1。
    • segment_ids:句子A和句子B分隔符,句子A對應的全為0,句子B對應的全為1。但是在多數文字分類情況下並不會用到句子B,所以基本不用管。
    • label_id :將label_list中的元素利用字典轉換為index標識,即
      label_map = {}
      for (i, label) in enumerate(label_list):
          label_map[label] = i
      
  • features中一個元素的例子是:
    在這裡插入圖片描述
  • 轉換完成後的特徵值就可以作為輸入,用於模型的訓練和測試

4. 模型訓練

  • 完成讀取資料、特徵轉換之後,將特徵送入模型進行訓練
  • 訓練演算法為BERT專用的Adam演算法
  • 訓練集、測試集、驗證集比例為6:2:2
  • 每一個epoch後會在驗證集上進行驗證,並給出相應的f1值,如果f1值大於此前最高分則儲存模型引數,否則flags加1。如果flags大於6,也即連續6個epoch模型的效能都沒有繼續優化,停止訓練過程。
    f1 = val(model, processor, args, label_list, tokenizer, device)
    if f1 > best_score:
        best_score = f1
        print('*f1 score = {}'.format(f1))
        flags = 0
        checkpoint = {
            'state_dict': model.state_dict()
        }
        torch.save(checkpoint, args.model_save_pth)
    else:
        print('f1 score = {}'.format(f1))
        flags += 1
        if flags >=6:
            break
    
  • 如果epoch數超過先前設定的num_train_epochs,同樣會停止迭代。

5. 模型測試

  • 先載入模型
  • 送資料,取得分,完事
  • 暫時還沒加列印測試結果到檔案的功能,後續會加上

6. 測試結果

val_F1 test_F1
Fast text 0.7218 0.7094
Text rnn + bigru 0.7383 0.7194
Text cnn 0.7292 0.7088
bigru + attention 0.7335 0.7146
RCNN 0.7355 0.7213
BERT 0.7938 0.787
  • 基於真實資料做的文字分類,用過不少模型,BERT的效能可以說是獨一檔
  • BERT確實牛逼,不過一部分原因也是模型量級就不一樣

7. 總結

  • 使用程式碼的時候按照引數表修改下引數,把資料按照命名規範放data_dir目錄下一般就沒啥問題了
  • 最多還要修改下讀取資料的程式碼(如果資料不是.json格式的),就可以跑通了
  • 最後可以根據個人需要,對模型訓練邏輯、epoch數、學習步長等地方做進一步修改
  • 程式碼地址已經放在前言(3)裡了

Tensorflow

readme

塗壁抗體紐的