自然言語処理の分野で昔から研究され、実際に使われている技術として固有表現認識があります。固有表現認識は、テキスト中で固有表現が出現する位置を特定し、人名や地名などのラベルを付与するタスクです。情報抽出や質問応答、対話システムなどへの幅広い応用が可能なため、今でも盛んに研究され、使われている技術です。本記事では、日本語の固有表現認識をFlairと呼ばれるPythonパッケージを使って実現する方法について紹介します。
準備
本記事では Flair を使って固有表現認識のモデルを学習させます。Flairは最先端の自然言語処理のモデルを簡単に使い始められる形で提供してくれているパッケージです。その中で提供されている機能として、固有表現認識や品詞タグ付け、文書分類のモデルを学習するための機能があります。使い始めるために、以下のようにしてFlairをインストールしておく必要があります。
$ pip install flair
モデルを学習するためのデータも用意しておきましょう。本記事を読んでいる方はおそらく日本人なので、日本語の固有表現認識を行うことにします。以下のリポジトリからコーパスをダウンロードしてください。
wget
でダウンロードすると簡単なのでおすすめです。
$ wget https://raw.githubusercontent.com/Hironsan/IOB2Corpus/master/ja.wikipedia.conll
実装
今回は、2018年にAkbikらがContextual String Embeddings for Sequence Labelingという論文の中で提案したモデルを実装します。以下にモデルのアーキテクチャを示しました。このモデルでは、文字ベースの言語モデルを使って単語分散表現を構築し、それを使ってラベルを予測します。固有表現認識に携わる者であれば、まず知っているモデルであり、とても良い性能を出すことで知られています。
ちなみに、以下の記事で今回実装するモデルのパワーアップ版を紹介しているので、本記事と合わせて読むと知識の幅が広がります。
では実装していきましょう。まずは必要なクラスをインポートします。
from flair.datasets import ColumnCorpus from flair.embeddings import StackedEmbeddings, FlairEmbeddings from flair.data import Sentence from flair.models import SequenceTagger from flair.trainers import ModelTrainer
次に、ダウンロードしたコーパスを読み込みます。data_folder
にはダウンロードしたコーパスが存在するディレクトリを指定してください。
columns = {0: 'text', 1: 'ner'} data_folder = '.' corpus = ColumnCorpus(data_folder, columns, train_file='ja.wikipedia.conll')
コーパスの読み込みが終わったら、タグを用意します。今回は固有表現認識を行うので、tag_type='ner'
を指定します。
tag_type = 'ner'
tag_dictionary = corpus.make_tag_dictionary(tag_type=tag_type)
タグの一覧を表示してみましょう。コーパス中に存在するタグに加えて、<unk>
、<START>
、<STOP>
という3つのタグが用意されていることを確認できます。
>>> print(tag_dictionary.idx2item) [b'<unk>', b'O', b'B-PERSON', b'I-PERSON', ..., b'<START>', b'<STOP>']
次に使用する分散表現を用意します。今回は論文中で使われている文字ベースの言語モデルから得られる分散表現を使いたいのでFlairEmbeddings
を指定します。事前学習済みモデルとしてja-forward
とja-backward
を指定することで、日本語の事前学習済み言語モデルを使うことができます。
embedding_types = [ FlairEmbeddings('ja-forward'), FlairEmbeddings('ja-backward'), ] embeddings = StackedEmbeddings(embeddings=embedding_types)
分散表現を用意したら、学習させるモデルを定義します。ラベル間の依存関係を考慮するため、use_crf=True
でCRFを使うようにします。
tagger = SequenceTagger(hidden_size=256, embeddings=embeddings, tag_dictionary=tag_dictionary, tag_type=tag_type, use_crf=True)
モデルを定義できたので、学習させていきます。今回は小さなコーパスを使っているので、CPUで計算しても数時間で学習は完了します。そんなに待てないという方はGPUやTPUを使いましょう。
trainer = ModelTrainer(tagger, corpus) trainer.train('resources/taggers/example-ner', learning_rate=0.1, mini_batch_size=32, max_epochs=150)
学習が終わると以下のようにスコアを表示してくれます。今回はF1のマイクロ平均で0.7886という結果でした。以前に同じデータセットに対して文字情報なしのBiLSTM-CRFで実験した際には0.55程度だったので、それに比べると遥かに良い性能となりました。
0.8156 0.7633 0.7886 MICRO_AVG: acc 0.6509 - f1-score 0.7886 MACRO_AVG: acc 0.6436 - f1-score 0.7638666666666667 ARTIFACT tp: 40 - fp: 19 - fn: 47 - tn: 40 - precision: 0.6780 - recall: 0.4598 - accuracy: 0.3774 - f1-score: 0.5480 DATE tp: 123 - fp: 11 - fn: 15 - tn: 123 - precision: 0.9179 - recall: 0.8913 - accuracy: 0.8255 - f1-score: 0.9044 EVENT tp: 21 - fp: 7 - fn: 11 - tn: 21 - precision: 0.7500 - recall: 0.6562 - accuracy: 0.5385 - f1-score: 0.7000 LOCATION tp: 242 - fp: 45 - fn: 26 - tn: 242 - precision: 0.8432 - recall: 0.9030 - accuracy: 0.7732 - f1-score: 0.8721 NUMBER tp: 76 - fp: 13 - fn: 13 - tn: 76 - precision: 0.8539 - recall: 0.8539 - accuracy: 0.7451 - f1-score: 0.8539 ORGANIZATION tp: 86 - fp: 41 - fn: 60 - tn: 86 - precision: 0.6772 - recall: 0.5890 - accuracy: 0.4599 - f1-score: 0.6300 OTHER tp: 19 - fp: 7 - fn: 25 - tn: 19 - precision: 0.7308 - recall: 0.4318 - accuracy: 0.3725 - f1-score: 0.5429 PERCENT tp: 13 - fp: 0 - fn: 0 - tn: 13 - precision: 1.0000 - recall: 1.0000 - accuracy: 1.0000 - f1-score: 1.0000 PERSON tp: 70 - fp: 13 - fn: 17 - tn: 70 - precision: 0.8434 - recall: 0.8046 - accuracy: 0.7000 - f1-score: 0.8235
学習が完了したので、最も良いモデルを読み込みます。
model = SequenceTagger.load('resources/taggers/example-ner/final-model.pt')
読み込んだモデルを使って予測を行います。
sentence = Sentence('私 は 田中 と 東京 駅 へ 行っ た')
model.predict(sentence)
結果を表示すると以下のようになりました。正しく認識できていますね。
>>> print(sentence.to_tagged_string())
私 は 田中 <B-PERSON> と 東京 <B-LOCATION> 駅 <I-LOCATION> へ 行っ た
See also
Colaboratoryにノートブックを用意したので、試してみてください。