bert as service のベクトル表現を用いて文書分類メモ
日本語BERTで文表現ベクトルを得る環境を作る
日本語BERTによってベクトルを出せるようにする
以下の記事の通りに、日本語BERTによって文表現ベクトルを計算するサーバーを作ります。
https://qiita.com/shimaokasonse/items/97d971cd4a65eee43735
※Google Colabolatoryでやる場合は
!pip install bert-serving-client !pip install -U bert-serving-server[http] !nohup bert-serving-start -model_dir=./bert-jp/ > out.file 2>&1 &
from bert_serving.client import BertClient bc = BertClient()
としないと動かないです。
以上でbert-as-serviceから文表現ベクトルを得ることができました。
文書分類
分類するためのデータセットの準備
live コーパスを使っていきます(9クラス分類)。
live コーパスをダウンロードし,解凍します。
$ wget https://www.rondhuit.com/download/ldcc-20140209.tar.gz $ tar zxf ldcc-20140209.tar.gz > /dev/null
データセットの整形
とりあえず以下のような形にもっていきます。
Text | Label | |
---|---|---|
0 | text1 | label1 |
1 | text2 | label2 |
2 | text3 | label3 |
... | ... | ... |
import os import random import pandas as pd from sklearn.preprocessing import LabelEncoder from sklearn.model_selection import train_test_split import numpy as np import glob def parse_to_wakati(text): return text categories = [ "sports-watch", "topic-news", "dokujo-tsushin", "peachy", "movie-enter", "kaden-channel", "livedoor-homme", "smax", "it-life-hack", ] docs = [] for category in categories: for f in glob.glob(f"./text/{category}/{category}*.txt"): # 1ファイルごとに処理 with open(f, "r") as fin: # nextで1行取得する(__next__を呼ぶ) url = next(fin).strip() date = next(fin).strip() title = next(fin).strip() body = "\n".join([line.strip() for line in fin if line.strip()]) docs.append((category, url, date, title, body)) df = pd.DataFrame( docs, columns=["category", "url", "date", "title", "body"], dtype="category" ) # 日付は日付型に変更(今回使うわけでは無い) df["date"] = pd.to_datetime(df["date"]) # wakati body df = df.assign( Text=lambda df: df['body'].apply(parse_to_wakati) ) # ラベルエンコーダは、ラベルを数値に変換する le = LabelEncoder() # ラベルをエンコードし、エンコード結果をyに代入する df = df.assign( Label=lambda df: pd.Series(le.fit_transform(df.category)) ) labels = np.sort(df['Label'].unique()) labels = [str(f) for f in labels] idx = df.index.values idx_train, idx_val = train_test_split(idx, random_state=123) train_df = df.loc[idx_train, ['Text', 'Label']] val_df = df.loc[idx_val, ['Text', 'Label']]
↑のコードを実行するとTextとLabelのペアを作ることができます。
↓train_dfの一例
自作データとかの場合train_dfのような形 Text, Label となるようにCSVファイルを既に用意してもらった方が簡単化と思います。
Kerasで分類する
Kerasを用いてbert as serviceから得られたベクトル表現に対して分類をします。
以下でネットワークを構築します。
import numpy as np from sklearn import datasets from sklearn.model_selection import train_test_split from keras.models import Sequential from keras.layers.core import Dense, Activation from keras.utils import np_utils from sklearn import preprocessing # BERTから得るベクトル表現をこのモデルに流し込む def build_multilayer_perceptron(): model = Sequential() # 隠れ層512は好きに変えて良い model.add(Dense(512, input_shape=(768,))) model.add(Activation('relu')) # liveコーパスは9クラス分類なので9 model.add(Dense(9)) model.add(Activation('softmax')) return model x = train_df["Text"].values.tolist() x = list(map(parse,x)) X = bc.encode(x,is_tokenized=True) Y = train_df["Label"].values train_X, test_X, train_Y, test_Y = train_test_split(X, Y, train_size=0.8) print(train_X.shape, test_X.shape, train_Y.shape, test_Y.shape) model = build_multilayer_perceptron() model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])
分類を実行します。
# epoch数,batch_sizeは適宜変更する model.fit(train_X, train_Y, nb_epoch=200, batch_size=16, verbose=1) """ 4420/4420 [==============================] - 1s 184us/step - loss: 0.0265 - acc: 0.9930 Epoch 198/200 4420/4420 [==============================] - 1s 177us/step - loss: 0.1637 - acc: 0.9493 Epoch 199/200 4420/4420 [==============================] - 1s 184us/step - loss: 0.2781 - acc: 0.9120 Epoch 200/200 4420/4420 [==============================] - 1s 179us/step - loss: 0.0248 - acc: 0.9943 """
評価します。
# モデル評価 loss, accuracy = model.evaluate(test_X, test_Y, verbose=0) print("Accuracy = {:.2f}".format(accuracy))
transformersのBERT
色々めんどくさいことしましたが transformers を使えば日本語学習済BERTを非常に簡単に利用できるようになりました(つい最近)。
https://twitter.com/huggingface/status/1205283603128758277
huggingface/transformers の日本語BERTで文書分類器を作成する記事 https://qiita.com/nekoumei/items/7b911c61324f16c43e7e
試してみたら非常に簡単でした。
分類 BertForSequenceClassification だけでなく以下から色々なタスクを扱えるようです。
# Each architecture is provided with several class for fine-tuning on down-stream tasks, e.g.
BERT_MODEL_CLASSES = [BertModel, BertForPreTraining, BertForMaskedLM, BertForNextSentencePrediction,
BertForSequenceClassification, BertForTokenClassification, BertForQuestionAnswering]
bert as serviceは一応CPUでも動くようなのでGPUないけど回したいみたいな人にはいいのかなと思いました。
NLP界隈の人間ではないので詳しいことまでは分かりませんが。