如何使用BERT實現中文的文字分類(附程式碼)
阿新 • • 發佈:2018-12-26
如何使用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
- 基類DataProcessor:
- 需要注意的幾點是:
- data_dir目錄下應包含名為train、val、test的三個檔案,根據檔案格式不同需要對讀取方式進行修改
- get_labels()返回的是所有可能的類別label_list,比如
['數學', '英語', '語文']
、[1, 2, 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
塗壁抗體紐的