僕とNERとFlairと

この記事は[MYJLabアドベントカレンダー](https://qiita.com/advent-calendar/2019/myjlab)23日目です。
## はじめに
こんにちは。MYJLab m1のmarutakuです。今回は、固有表現抽出と呼ばれる技術を使う機会があったので、その時に使ったライブラリの紹介をしようと思います。

## 固有表現抽出(NER)について
固有表現抽出(Named Entity Recognition)とは、企業名や地名などの固有名詞を企業・地名などのラベル付きで取得してくる技術です。
例として、

```
米航空機大手ボーイングは23日、ミュイレンバーグ最高経営責任者（CEO）の即時辞任を発表した。
2度の墜落事故を起こして運航停止中の最新鋭機「737MAX」の問題の責任を取ったとみられる。
カルフーン会長が来年1月13日付でCEO職に就くという。
```
[https://news.yahoo.co.jp/pickup/6346205](https://news.yahoo.co.jp/pickup/6346205)
この時、人名は`ミュイレンバーグ, カルフーン`であり、組織名は`ボーイング`ということがわかります。このように、文章の中に存在する固有名詞を取得するのが固有表現抽出のタスクです。

### データセット
[このリポジトリ](https://github.com/juand-r/entity-recognition-datasets)にまとまってます。よく使われるのはCoNLL2003とOntonotes v5だと思っています。他にも、医療系のデータやwikipedhiaのアノテーション済みデータなどがあります。
日本語のデータセットはあまり公開されていませんが、売ってはいるようです。

### 固有表現抽出の難しいところ
最近の固有表現抽出の論文は、実装するのが大変難しいです。[Paper With Code](https://paperswithcode.com/task/named-entity-recognition-ner)をみてもらえればわかると思いますが、大量のモデルをスタックさせて使用したり、複数の事前学習済みモデルの単語表現を使用したいりしているので大変面倒です。`LSTM-CRF+ELMo+BERT+Flair`とかモウナニイッテルノカワカラナイ。
そんな時に、NERのState of the artを達成したモデルを簡単に実装できるFlairというライブラリに出会いました。

## Flairとは
[Flair](https://github.com/flairNLP/flair)は、簡単にState of the artのモデルを実装できるようにと作られた自然言語処理用のライブラリです。学習済みのモデルが豊富に用意されており、過去のSoTAモデルをすぐに自分のシステムに組み込むことができます。
チュートリアルにも書いてありますが、Flairで学習済みの固有表現抽出モデルを試す時は以下のコードだけで実現できます

```python
from flair.data import Sentence
from flair.models import SequenceTagger

# make a sentence
sentence = Sentence('I love Berlin .')

# load the NER tagger
tagger = SequenceTagger.load('ner')

# run NER over sentence
tagger.predict(sentence)
```

ここで用いられている`ner`というモデルの他にも、CPUでも高速に動作する`ner-fast`というモデルなど多様なモデルが存在しています。

## Flairを用いた自作モデル
Flairは学習済みモデルの実行だけではなく、自作のモデルを作成することも可能です。
Flairは学習済みモデルだけでなく、有名なモデルの埋め込み表現も簡単に扱えるようにしています。(https://github.com/flairNLP/flair/blob/master/resources/docs/TUTORIAL_4_ELMO_BERT_FLAIR_EMBEDDING.md)
自作のモデルの例を以下に示します。

```python
from flair.data import Corpus
from flair.datasets import WNUT_17
from flair.embeddings import TokenEmbeddings, WordEmbeddings, StackedEmbeddings
from typing import List

# 1. get the corpus
corpus: Corpus = WNUT_17().downsample(0.1)
print(corpus)

# 2. what tag do we want to predict?
tag_type = 'ner'

# 3. make the tag dictionary from the corpus
tag_dictionary = corpus.make_tag_dictionary(tag_type=tag_type)
print(tag_dictionary.idx2item)

# 4. initialize embeddings
embedding_types: List[TokenEmbeddings] = [

    WordEmbeddings('glove'),

    # comment in this line to use character embeddings
    # CharacterEmbeddings(),

    # comment in these lines to use flair embeddings
    # FlairEmbeddings('news-forward'),
    # FlairEmbeddings('news-backward'),
]

embeddings: StackedEmbeddings = StackedEmbeddings(embeddings=embedding_types)

# 5. initialize sequence tagger
from flair.models import SequenceTagger

tagger: SequenceTagger = SequenceTagger(hidden_size=256,
                                        embeddings=embeddings,
                                        tag_dictionary=tag_dictionary,
                                        tag_type=tag_type,
                                        use_crf=True)

# 6. initialize trainer
from flair.trainers import ModelTrainer

trainer: ModelTrainer = ModelTrainer(tagger, corpus)

# 7. start training
trainer.train('resources/taggers/example-ner',
              learning_rate=0.1,
              mini_batch_size=32,
              max_epochs=150)

# 8. plot weight traces (optional)
from flair.visual.training_curves import Plotter
plotter = Plotter()
plotter.plot_weights('resources/taggers/example-ner/weights.txt')
```

上のモデルは`glove`というモデルの埋め込み表現をモデル内部で使用していますが、これを増やすことで複数の単語埋め込み表現を利用することができます。
まだ試してはいないですが、埋め込み表現のファインチューニングもできるみたいです。

## 終わりに
今回は、NERの話とFlairの話をしました。FlairはNERの他にもネガポジ判定やテキスト分類などのタスクに応用が可能です。自然言語処理使いたいけど難しそう…と思っている方はぜひ使ってみてください。

## 参考文献
* https://www.rondhuit.com/apache-opennlp-1-9-0-ja-ner.html
* https://qiita.com/Hironsan/items/62b493d92712e862e5aa