tagging(品詞タグ付け)を行いました。LSTMとCRFを組み合わせたモデルを使ってNERを行います。PyTorch公式チュートリアルの以下のページに沿って進めます。コードは以下のページからダウンロードしたものを一部改変して使っています。NER固有表現抽出と訳されます。固有表現とは人名や地名などの固有名詞、日時や数量などの数的表現のことです。NERで使われるタグは2つの要素からなります。Yorkこのようにタグづけされます。York」という固有表現の始まりなのでB-LOC、Yorkは先のNewの続きなのでI-LOCです。NERではハードルとなる点がいくつかあります。Universityは単語の始まりと終わりを見つけなくてはいけないNERやNEについて詳しくは以下の書籍などを参照ください。LoperオライリージャパンAmazonで詳細を見る英語版はウェブにて無料で公開されているのでこちらをご覧になるのも良いでしょう。LSTMLSTMについては前回触れたので深くは解説しません。端的に表現すると、RNNの一種です。それまでのRNNで課題だった長期記憶のメカニズムを実現したモデルです。模式図は以下のようになります。Tagging編みかけの部分の内部でLSTMに特徴的な計算を行なっているのですが、ここでは紹介を省きます。詳しくは前回の記事をご覧ください。LSTMではその名の通り、双方向性のLSTMです。上記のLSTMと合わせて、系列データを反対の順から流すLSTMを用います。Taggingこれにより、LSTMを上回る精度の分析ができます。以下のような文で考えます。PM.LSTMではatの後ろに数字が来ていることから、この数字は時間を表していると判断します。bi-LSTMではそれに加えて、その数字の後ろにPMがあることからより確信を持って時間を表していると判断ができるのです。単純に考えて(本当に単純に考えて)判断材料が2倍になるということです。CRF条件付き確率場と訳します。言語学習のための機械学習入門では「対数線形モデルを系列ラベリング問題に適用したもの」というように定義しています。taggingで優れた結果を出しました。出力されるラベルに依存性があるような問題では難が残ります。固有表現以外が出現する場合B-PERの後にはI-LOCやI-ORGが来ては行けないという強い制約があるのです。そこで、bi-LSTMの出力をCRFに入力し、CRFが最終的な出力(ラベル)を計算するという手法が考案されました。これによってNERを高い精度で実行できるようになりました。今回のLSTMに続くCRFでの処理を例に取ってCRFの働きを確認します。CRFでは以下のような系列データX(今回の例ではxiは文章の各単語に対するLSTMの出力)が与えられます。予測されるラベルを以下のようなyと定義します。y)は以下のように定義されます。ここでAはタグの遷移スコアです。i番目のタグからj番目のタグへ遷移する時のスコアを表します。y0とynはそれぞれSTARTタグ、STOPタグと特別なタグが割り振られます。AはNEタグ(k個)にこの2つのタグを含めた(k+2)×(k+2)の正方行列になります。PはLSTMの出力をまとめたものになります。ある単語の分散表現がLSTMに入力されると、k個のタグに対するスコアすなわち要素k個のベクトルが出力されます。文章にはn個の単語が含まれているので、すべての出力をまとめたPはn×kの行列になります。第二項はLSTMによるyのスコア(LSTMが担保する各単語のタグの正しさ)であることがわかります。LSTMにとって確信度の高い予測yであっても、CRFにとってあり得ないタグの遷移であればスコアは下がるというわけです。y)を用いて以下のように表現できます。Yxは出力yが取り得るすべてのパターン(IOBフォーマットにそぐわないものも含めて)です。従って、分母はp(y|X)を0から1の範囲に収めるためのものです。学習過程では上記確率の対数、以下のlog(p(y|X))を最大化させるようネットワークのパラメータを更新します。学習したネットワークを用いて予測をするときは、スコアを最大化するyを選択します。このときyの組み合わせはn個の単語とk個のラベルがあれば(k+2)^nと計算量が大きいのですが、後述のヴィタビアルゴリズムを導入することで計算の効率化を実現しています。模式図は以下のようになります。bi-LSTMの出力をCRFに投入して、文章内の単語に対するNERを行います。Recognitionこれによって高い精度でNERができる、とのことです。詳しくは画像の引用元の論文を参照してみてください。Recognition(ただし論文ではcharacterレベルの情報も用いています。CRFモデルはwordレベルなので注意)では実際に公式チュートリアルのコードを紐解いてみます。後々使う便利関数を設定しておきます。ベクトルの最大要素のindexを返すargmax()。系列データをインデックのリストに変換するprepare_sequence()。log_sum_exp()モデルの実装部分です。メソッド名だけ見ます。長いですが、これは損失関数をクラスの中で定義していることが理由の一つです。コンストラクタ__init__()です。次に_forward_alg()です。これは損失関数の計算に使われます。小さい方が好ましいです。ここで使われてる関数については以下に補足します。与えられたシーケンスを繋げる、第二引数は軸の指定_get_lstm_features()はforward()でLSTMの部分の計算として実行されます。前回実装したLSTMの出力を取得します。_viterbi_decode()はLSTMの出力を受け入れ、それから入力文章に対応するNEタグのリストを予測します。これと合わせて出力NEタグのスコアを算出します。n個の単語がそれぞれk個のタグをとり得るので場合の数はk^nとなりますが、ヴィタビアルゴリズムで計算することにより効率的に計算しています。neg_log_likelihood()は_forward_alg()と_score_sentence()の返り値の差で表されます。損失関数なので当然小さい方が好ましいです。最後にforward()について見ます。_get_lstm_features()で入力した系列データに対するLSTMの出力を得ます。