Multi-label Text Classification using BERT – The Mighty Transformer
今天要來芝麻街上英文課囉!
最近芝麻街的腳色紛紛重出江湖,又引起大家的熱烈討論,在做語意相關研究的一定都聽過這個創造語意新世代的模型 - BERT
以前在做語意分類任務時,常常就是先斷詞斷句,接著透過自己用一個大的corpus 做 word embedding,或是使用一些別人訓練好的模型,像是 google-news pre-trained 模型,最後接一個分類模型常常就可以有不錯的表現,但後來大家也發現一些問題,像是同字異義的詞,往往也用同一組詞向量表示,但其實他們的意思可能天差地遠。
以現在很多大廠爭相在做的智慧音箱為例,他的功能可能有購物、聽音樂、查詢天氣......等等,所以如果這時你對音箱說Q: 小寶小寶 我想買東西
應該是可以順利分到購物的任務裡面,但如果這時你對音箱說
Q: 小寶小寶 你是什麼東西?
這時很有可能其他的字沒看過,他只學過 東西 這個詞,應此也進到購物的任務裡,這時後顧客可能會想要退貨,並想說你這個音箱太爛了吧,還號稱人工智慧...
而今天要介紹的主角 - BERT ,能力遠不止這樣,其架構精神為Transfomer Encoder。BERT主要的應用會是以Transfer learning後接著Fine tune model。接著會帶大家一步一步實際去 fine tune 一個語意分類任務。 如果是想更進階的知道BERT內部的架構,可以參考李宏毅老師的ELMO&BERT 以及 Transformer,不是變形金剛唷,裡面有非常完整的介紹。
這邊會使用常出現在情感分析教學的資料集 - IMDB
comment: Oh yeah! Jenna Jameson did it again! Yeah Baby! This movie rocks. It was one of the 1st movies i saw of her.
label: 1
資料包含使用者對於電影的評論,還有該評論是屬於正面還是負面的標注,
接著照著bert tf2.0專案裡面的教學安裝好環境
PS: 因為BERT 這個專案一開始是搭配 tensor flow 1.11,所以當你在使用 tensorflow 2.0 時可能會誤觸很多雷,所以請小心服用。
包括像是 pre-trained model 下載,2.0的專案目前也還沒提供多國語言版本,另外如果在訓練過程中有遇到 out-of-memory 問題,請參考裡面也提到 when using a GPU with 12GB - 16GB of RAM, you are likely to encounter out-of-memory issues if you use the same hyperparameters described in the paper.
總之,訓練一定有風險,訓練前請詳閱 github 的 readme
接著我們選用一個比較小的 pre-trained model
BERT-Base, Uncased : 12-layer, 768-hidden, 12-heads, 110M parameters
把剛剛的專案下載下來
把剛剛的專案下載下來git clone https://github.com/tensorflow/models/tree/master/official/nlp/bert
然後把剛剛下載好的模型移進去,接下來我們需要改動到的部分就只要下面的這些檔案
├── classifier_data_lib.py(需要新增一個自己的 dataprocessor)
├── create_finetuning_data.py(小修改設定)
├── create_finetuning_data.sh(config)
├── dataset(轉換成 tf_record格式的資料)
│ ├── IMDB_eval.tf_record
│ ├── IMDB_meta_data
│ └── IMDB_train.tf_record
├── uncased_L-12_H-768_A-12(下載的 pre-trained model)
│ ├── bert_config.json
│ ├── bert_model.ckpt.data
│ └── bert_model.ckpt.index
│ └── vocab.txt
├── imdb_data(imdb 整理好的 .tsv檔 )
│ ├── train.tsv
│ ├── test.tsv
│ └── dev.tsv
一開始我們先在 classifier_data_lib.py
新增自己的 data processor,這邊可以參考其他幾個預設的 processor,主要就是定義一下每個資料的路徑,然後就是你的 sentence 和 label 各在哪個欄位,guid就只是一個 unique的 id。
class ImdbProcessor(DataProcessor):
"""Processor for the IMDB 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.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 ["0", "1"]
@staticmethod
def get_processor_name():
"""See base class."""
return "IMDB"
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
else:
guid = "%s-%s" % (set_type, i)
text_a = tokenization.convert_to_unicode(line[2])
label = tokenization.convert_to_unicode(line[1])
examples.append(
InputExample(guid=guid, text_a=text_a, text_b=None, label=label))
return examples
在 create_fintuning_data.py
加上剛剛新增的 ImdbProcessor
Line 44 ["COLA", "MNKI", "MRPC", "XNLI"]
Lines 101-106
processor = {
"cola": classifier_data.lib.ColaProcessor,
"mnli": classifier_data.lib.MnliProcessor,
"mrpc": classifier_data.lib.MrpcProcessor,
"xnli": classifier_data.lib.XnliProcessor,
"imdb": classifier_data.lib.ImdbProcessor,
}
然後就可以透過跑 create_fintuning_data.py
把一開始的 tsv 資料,轉換成 tf_record格式,會是以二進位存取,加快整個訓練,在設定檔中只要填上剛剛 data存放的路徑、模型路徑、剛剛創建的任務及輸出檔案路徑。
export IMDB_DIR=./imdb_data
export BERT_BASE_DIR=./uncased_L-12_H-768_A-12
export TASK_NAME=IMDB
export OUTPUT_DIR=./dataset
python3 create_finetuning_data.py \
--input_data_dir=${IMDB_DIR}/ \
--vocab_file=${BERT_BASE_DIR}/vocab.txt \
--train_data_output_path=${OUTPUT_DIR}/${TASK_NAME}_train.tf_record \
--eval_data_output_path=${OUTPUT_DIR}/${TASK_NAME}_eval.tf_record \
--meta_data_file_path=${OUTPUT_DIR}/${TASK_NAME}_meta_data \
--fine_tuning_task_type=classification --max_seq_length=128 \
--classification_task_name=${TASK_NAME}
最後我們就進入重頭戲,開始 fine tune 我們的 BERT,這邊只是稍微示範訓練了3個 epoch,而 batch_size 只有設定 4,但在訓練時 GPU 的 memory 就會需要超過 31GB (跟友人借了 NVIDIA Tesla V100的環境)
export BERT_BASE_DIR=./uncased_L-12_H-768_A-12
export MODEL_DIR=./model_output
export IMDB_DIR=./dataset
export TASK=IMDB
CUDA_VISIBLE_DEVICES=6 \ # 如果要指定特定 GPU
python run_classifier.py \
--mode='train_and_eval' \
--input_meta_data_path=${IMDB_DIR}/${TASK}_meta_data \
--train_data_path=${IMDB_DIR}/${TASK}_train.tf_record \
--eval_data_path=${IMDB_DIR}/${TASK}_eval.tf_record \
--bert_config_file=${BERT_BASE_DIR}/bert_config.json \
--init_checkpoint=${BERT_BASE_DIR}/bert_model.ckpt \
--train_batch_size=4 \
--eval_batch_size=16 \
--steps_per_loop=1 \
--learning_rate=2e-5 \
--num_train_epochs=3 \
--model_dir=${MODEL_DIR} \
--strategy_type=mirror # 如果要使用TPU的話這邊要做更改
我們總共拿了 20000筆資料訓練,5000筆驗證,這邊可以看到雖然我們只訓練了3個 epoch,但是在驗證資料集的準確率也有 88%,當然這邊只是最粗淺的拿 raw data 直接丟到 BERT裡面,完全沒有做任何的前處理,之後有興趣的朋友,可以自己先做一些資料前處理,像評論中其實包含了很多 HTML tag、特殊符號,這些都是要先做處理的,還有也可以考慮自己先做斷詞斷句。
{'train_loss': 0.0003554773866198957, 'total_training_steps': 14997, 'last_train_metrics': 1.0, 'eval_metrics': 0.8838232159614563}
今天只是讓大家快速的了解,怎麼在最短時間執行你的 BERT 訓練,之後有時間也會再細講裡面的每個處理,讓大家對 BERT 的印象不再是只可遠觀。這次的Colab並非我自己寫的,是google所提供!這次跑的都在local端,但還是附上google大神所提供的Colab!有興趣可以拿來玩玩看~
LeeMeng - 進擊的 BERT:NLP 界的巨人之力與遷移學習
Bert_predict_Movie_Revies_Colab