機械学習・自然言語処理の勉強メモ

学んだことのメモやまとめ

Snorkelの生成モデルについて(実装編)

はじめに



前回はラベリング関数の作り方を確認した。
kento1109.hatenablog.com

今回はいよいよ生成モデルに関するチュートリアルを読んでいく。
尚、理論的なことはこっちにまとめた。
Snorkelの生成モデルについて(理論編) - 機械学習・自然言語処理の勉強メモ

前回定義したラベリング関数であるが、個々の関数は弱教師(精度が低く単独で使えない識別器)だった。生成モデルでは、これらを統合して精度の高い識別器の生成を目指す(アンサンブル学習に近い学習手法)。
最も簡単な統合方法は、ラベリング関数の結果で多数決を取ることである。しかし、この場合、個々のラベリング関数の精度を無視することになる。
例えば下記の2つのラベリング関数の精度を比較する。

  1. 「人物名の周りにmarryという単語があれば1、なければ0」を返す。
  2. 「人物名のlast nameが同じであれば1、同じでなければ0」を返す。

真のラベルが「1(関係あり)」と仮定した場合、2.のラベリング関数の方が、「1(関係あり)」を返す可能性(あくまで経験的な予測)が高いと考えられる。そうなると、単なる多数決ではなく、2.の投票をより重視(重みづけ)すべきである。
つまり、この潜在的な精度を考慮して分類するべきであり、このような考えに基づくモデルが生成モデルである。
f:id:kento1109:20180109162304p:plain
[1605.07723] Data Programming: Creating Large Training Sets, Quicklyより引用

真のラベルなしに潜在的な精度を学習する際、各ラベリング関数のoverlapconflictが重要になる。
生成モデルの推定方法に関しては後で確認する。

コードを読んでいく



今回のチュートリアルは以下にある。
github.com

前回のラベル行列をロード

from snorkel.annotations import LabelAnnotator

labeler = LabelAnnotator(lfs=LFs)
L_train = labeler.load_matrix(session, split=0)
L_dev   = labeler.load_matrix(session, split=1)
pos/neg    196:2603 7.0%/93.0%
precision  35.75
recall     32.65
f1         34.13
Generative Model

ラベリング関数の精度を生成モデルにより学習する。
学習後、生成モデルは各候補のノイズ対応トレーニングラベルを生成する。これは、この後の識別モデルで使用される。
f:id:kento1109:20180116143903p:plain
https://hazyresearch.github.io/snorkel/pdfs/snorkel_demo.pdfより引用
1)Training the Model
グリッドサーチにより最適なパラメータを求める。
自分のライブラリにはListParameter, RangeParameterクラスが存在しないので、インポートエラーが発生する・・とりあえず、グリッドサーチは省略して生成モデルを訓練する。

from snorkel.learning import GenerativeModel

gen_model = GenerativeModel()
gen_model.train(L_train, epochs=100, decay=0.95, step_size=0.1 / L_train.shape[0], reg_param=1e-6)

2)Model Accuracies
精度の確認

gen_model.learned_lf_stats()

f:id:kento1109:20180116151609p:plain:w250
3)Plotting Marginal Probabilities
生成モデルを訓練データに適用

train_marginals = gen_model.marginals(L_train)

訓練データは0~1の値を取る二項分布となる。一番右の分布のように2つの山が出来ていてそれらが離れているほど理想的(TrueラベルとFalseラベルの分布が良く分かれている)。一番左は多くの候補がTrueラベルの確率が0.5の状態(ほとんどランダムによる識別と変わらない)。これは、LFscoverageが低い場合に頻繁にみられる分布であり、この場合は候補全体を網羅するラベリング関数を定義する必要がある。真ん中の分布は、LFsがFalseラベルの識別は出来ているが、Tureラベルの識別ができていない状態(Tureラベルを返すためのラベリング関数をもっと定義する必要がある。)
f:id:kento1109:20180116152347p:plain:w1000
4)Generative Model Metrics
真のラベルを使って生成モデルを評価する。

dev_marginals = gen_model.marginals(L_dev)
_, _, _, _ = gen_model.error_analysis(session, L_dev, L_gold_dev)
========================================
Scores (Un-adjusted)
========================================
Pos. class accuracy: 0.327
Neg. class accuracy: 0.955
Precision            0.352
Recall               0.327
F1                   0.339
----------------------------------------
TP: 64 | FP: 118 | TN: 2485 | FN: 132
========================================
Structure Learning

ラベリング関数の相関性を考慮した学習。
これまではラベリング関数は独立を仮定していたが、実際はユーザーが自由に定義できるため似たようなラベリング関数が作られることもある。例えば、A, B, C, Dの4つラベリング関数のうち、C, Dの結果はBに依存していたとする。その場合、A, B, C, Dのラベリング関数の結果を同じように扱うのは危険である。
Snorkelでは、ラベリング関数間の依存性をグラフで表現し、マルコフ確率場のアルゴリズムにより最適化する。
ラベリング関数間の依存性を考慮した場合は下記となる。

from snorkel.learning.structure import DependencySelector
ds = DependencySelector()
deps = ds.select(L_train, threshold=0.1)

from snorkel.learning import GenerativeModel
gen_model = GenerativeModel()
gen_model.train(L_train, deps=deps)

マルコフ確率場のアルゴリズムは別の機会に勉強したい。