こんにちは
AIチームの戸田です
今回は日本語NLPライブラリであるGiNZAのv5から実装されたTransformerモデルによる固有表現抽出を試します。
固有表現抽出とは、入力となる自然文から地名や製品名などの固有名詞を抽出するタスクです。今回固有表現抽出に使用するデータセットとして、ストックマーク株式会社が作成したWikipediaを用いた日本語の固有表現抽出データセットを使用します。また、Transformerモデルは処理に時間がかかるので、環境はGoogle ColaboratoryのGPU環境を利用しました。
事前準備
wgetを使ってデータセットをダウンロードします。
wget https://raw.githubusercontent.com/stockmarkteam/ner-wikipedia-dataset/main/ner.json
必要なpythonライブラリをダウンロードします。ここで気をつけたいのが、spacyをダウンロードする際に指定するcudaのバージョンです。nvidia-smiでcudaのバージョンを確認すると、2022年1月現在は11.2と出るのですが、なぜか10.0を指定しないとWARNINGが出てしまいます。詳しいことはよくわからないのですが、おそらくColaboratoryの仮想環境が関係しているのではないかな、と考えています。
pip install spacy[cuda100]
pip install -U ginza https://github.com/megagonlabs/ginza/releases/download/latest/ja_ginza_electra-latest-with-model.tar.gz
pip install -U ja_ginza
固有表現抽出の実行
必要なライブラリをインポートし、GPUが有効になっているか確認します。
import json
import spacy
import pandas as pd
from tqdm.auto import tqdm
gpu = spacy.prefer_gpu()
print('GPU:', gpu)
# "GPU: True" とでればOK
次に解析器を設定します
# 従来のモデルを使う場合はja_ginzaを指定する
nlp = spacy.load('ja_ginza_electra')
次にデータを読み込みます
with open('ner.json', 'r') as f:
ner_data = json.load(f)
print(len(ner_data))
# 5343
データはjsonのリストで格納されています。試しに1件目のサンプルをprintしてみると以下のような出力が得られます。
{'curid': '3572156',
'entities': [{'name': 'SPRiNGS', 'span': [0, 7], 'type': 'その他の組織名'}],
'text': 'SPRiNGSと最も仲の良いライバルグループ。'}
- curid:ユニークID
- entities:固有表現情報のリスト
- name:固有表現
- span:その固有表現が何文字目から何文字目に出現するか
- type:固有表現の種類
- text:抽出元のテキスト
今回のデータには固有表現が含まれていないサンプルもあるので、そのようなサンプルは除外します。加えてGiNZAに解析させるためにテキストのリストを作成します。
texts, entities = [], []
for data in tqdm(ner_data):
text = data['text']
entity = [entity['name'] for entity in data['entities']]
if len(entity) == 0:
continue
texts.append(text)
entities.append(entity)
print(len(len(texts)))
# 4859
GiNZAにテキストを解析させます。GiNZAの公開ページには1件ずつ処理するコードが例として挙げられているのですが、以下のように記述することで、内部でバッチ処理に変換され、高速に実行することができます。
docs = list(nlp.pipe(texts))
今回は特にGPUを使うので、バッチ処理の恩恵を大きく受ける事ができます。
参考までに、今回のデータをCPUで処理した場合、また従来のモデルで処理した場合の実行時間を記載しておきます。
CPU | GPU | |
従来モデル | 1min 7s | 20.5 s |
Transformerモデル | 18min 11s | 36.0 s |
Transformerモデルを使いたい場合、GPUはほぼ必須と言えるのではないでしょうか。
最後に固有表現の抽出率を計算します。
scores = []
for idx, doc in enumerate(docs):
pred_ent = [ent.text for ent in doc.ents]
true_ent = entities[idx]
s = sum([1 for e in true_ent if e in pred_ent]) / len(true_ent)
scores.append(s)
print('score =', sum(scores) / len(scores))
従来のモデルとの抽出率の比較を以下に示します。
従来モデル | 0.6438 |
Transformerモデル | 0.7945 |
Transformerモデルだと、抽出率がかなり改善されているのがわかりました。
おわりに
本記事では日本語NLPライブラリであるGiNZAのv5から実装されたTransformerモデルを使って固有表現抽出を試してみました。
従来モデルよりかなりよい精度で抽出を行うことができましたが、大きなモデルなため、運用で利用する際の計算コストを考慮する必要がありそうです。
最後までお読みいただきありがとうございました。