ここでは例として,次のようなコーパス "small.txt" を用いて Enju の文法開発の流れを見ていきます。
(S (NP-SBJ Ms./NNP Haag/NNP) (VP plays/VBZ (NP Elianti/NNP)) ./.) ...
Enju の文法開発では,まず構文情報つきのコーパス Penn Treebank の構文木を変形して, HPSG 構文木に似た形の木を作ります. 例のコーパス "small.txt" を treetrans ツールに入力して, 構文木を変形し HPSG 構文木に似た形の木を作るには次のようなコマンドを用います.
この場合引数は次のようになります.treetrans enju-devel/transmain.lil small.txt small.treebank
treetrans では初めに, コーパス "small.txt" の構文木を input_parse_tree/2 によって素性構造にします. 例えば,一行目の "Ms. Haag plays Elianti." の木も 素性構造となります. (実際は木の親子関係も素性構造で表されます) 次に変形の前処理として, 述語 nonterminal_mapping/2 による 各ノードの非終端記号の変換等をします.
この後,述語 tree_transform_class/3 等で 設定されたパターンルールを用いて,素性構造の木を変形します. 上の例の構文木の場合,まず空範疇にマークをつけるルールが適用され 木が得られます. 変更された木では各ノードの HEAD_MARK 素性が 'non_empty' 型になっています. また次に句読点に関するルールが適用され,さらに 木が得られます.
このようにパターンルールの適用を繰り返し, 最終的に HPSG 構文木に近い形の木 が得られます. 最終的に得られた木は出力データベース "small.treebank" に格納されます.
Enju の文法開発の次の段階では辞書を抽出します. 上で変形した構文木から実際に HPSG 導出木を作り, 導出木の末端ノードの単語情報と語彙項目から辞書とテンプレートデータベースを作ります. その後,作った辞書とテンプレートデータベースに精製処理をして 最終的な辞書とテンプレートデータベースを得ます.
導出木,辞書とテンプレートの作成には lexextract ツールを使います.
この場合,各引数は次のようになります.lexextract enju-devel/lexextract.lil small.treebank small.derivbank small.lexcon small.templates small.lexbank
lexextract では入力された構文木から導出木を作る際に, 導出木の根ノードにインターフェース述語 root_constraints/2 による 制約をかけます. Enju 文法の場合,根ノードの sign は次のような制約がかけられます.
SYNSEM |
|
その後は入力の木を上から見ていき,親ノードからスキーマを逆適用して 娘ノードを作っていきます. このとき,親ノードの SCHEMA_NAME 素性に指定されたスキーマを逆適用します. また,逆適用の方法は inverse_schema_binary/4 と inverse_schema_binary/4によって定めておきます. 例えば "Ms. Haag plays Elianti." の木の ルートノードでは,SCHEMA_NAME 素性が "subj_head_schema" であり 2個の娘ノードがあります. よって inverse_schema_binary/4 に "subj_head_schema" と derivation の ルートノードを与え,左右の娘として2個のノードを得ます.
最後に入力の木の末端ノードまでたどり着いたら, lexical_constraints/2 によって derivation の末端ノードに制約を加えます. このようにして例の "Ms. Haag plays Elianti." の木からの derivation が得られます.
lexextract は次に,derivation の末端ノードを見て, 単語と語彙項目の対応表である辞書と, 語彙項目のテンプレートデータベースを抽出します.
例えば,"Ms. Haag plays Elianti." に対応する derivation には plays に対応する末端ノードがあります. このノードから lexical_entry_template/3 によって 単語の情報や細かな制約を省き, このような語彙項目テンプレートが得られます.
この語彙項目テンプレートに reduce_lexical_template/5 を適用すると, 語彙規則 "singular3rd_verb_rule" が逆適用されて 動詞の原型に当たる語彙素テンプレートが得られます. つまり "plays/VBZ" の原形 "play/VB" に対応するテンプレートが得られます. この語彙素テンプレートに,語彙規則 "singular3rd_verb_rule" を 順適用すると先ほどの語彙項目テンプレート が得られることになります.
またこのとき,単語 "play/VB" から 上の語彙素テンプレートへの対応を 辞書に登録します. 単語 "play/VB" を表す辞書のキーを derivation 末端ノードの単語情報から計算します. 例えば上のテンプレートのキーは次のような素性構造になります.
word | |||
BASE | "play" | ||
POS | "VB" |
この処理を "small.treebank" の全構文木に行い, 辞書 "small.lexicon" とテンプレートデータベース "small.templates" を 作っていきます. また得られた derivation は出力データベース "small.derivbank" に, その derivation の末端ノードは出力データベース "small.lexbank" に格納します.
ここでは lexextract で作った辞書とテンプレートデータベースを精製します.
つまり,頻度による足切りをしたり,
語彙素に語彙規則を適用して語彙項目を作るなどの処理をします.
この作業には lexrefine ツールを使います.
この場合,各引数は次のようになります.
lexrefine enju-devel/lexrefine small.lexcon small.templates new.lexicon new.templates
まずテンプレートデータベースで語彙素テンプレートの頻度による足切りが行われます. ある語彙素テンプレートの頻度は,辞書の作成時に reduce_lexical_template/5 によってそのテンプレートが出力された回数です.
次に,残った語彙素テンプレートに expand_lexical_template/5 によって語彙規則を適用し, 語彙項目テンプレートを生成します. 例えば上で作った語彙素テンプレートに 語彙規則 "passive_verb_rule" を適用すると, 新たな語彙項目テンプレート を得ることができます. この語彙項目テンプレートは,動詞の受動態に対応します. このようにして生成した語彙項目をテンプレートデータベースに新たに登録します. このとき語彙項目テンプレートの頻度は,元の語彙素テンプレートと同じ頻度に設定されます.
次に,辞書をテンプレートデータベースの変更に対応させます. 上で足切りされたテンプレートを含む辞書のエントリを削除します. また,新たに登録された語彙項目テンプレートのための辞書エントリを作ります. 例の場合,動詞 "play/VB" の語彙素テンプレートから 受動態の語彙項目テンプレートを生成したので, 辞書に受動態の単語 "played/VBN" をキーにして この語彙項目テンプレートを登録する必要があります. 受動態の単語のキーを作るには,expand_lexicon/3 を使います. expand_lexicon/3 は元の語彙素テンプレートの辞書エントリと 新しい語彙項目テンプレートを見て新たなキーを計算します. 例の場合,元の語彙素テンプレートのキーは単語 "play/VB" を表すものだったので, 新たなキーは次のようになります.
word | |||
BASE | "play" | ||
POS | "VBN" |
最後に辞書に未知語のためのエントリを作ります. 辞書のキー (の表す単語) のうち頻度が閾値以下のものが未知語とみなされます. 未知語とされたキーに unknown_word_key/2 を適用して, 未知語のキーを作ります. 例えば上の "played/VBN" のキーに適用すると,次のような未知語キーのなります.
word | |||
POS | "VBN" |
ここでは,構文解析で曖昧性解消をするために使う確率モデルのひとつ, Unigram モデルの推定をします. Unigram モデルは,各単語に対して語彙項目を割り当てる確率のモデルです. 単語をw,周辺の単語列をs,語彙項目をl とすると, p(l|w,s) を求めます. Enju の確率モデルは最大エントロピーモデルになっているので, モデルの推定では素性の最適な重みを amis を使って 計算することになります.
Unigram モデルの推定では,まず unimaker ツールにより 確率イベントをファイルに出力します. 確率イベントとは,以下のように // で区切られたフィールドを持つ文字列です.
最後のフィールドはこのイベントのカテゴリ を表しています. その他のフィールドはイベントの特徴を表す記号です. unimaker は derivation 中の単語とその語彙項目を表す確率イベントの リストをファイルに出力します. このとき正例の確率イベントとして,単語とその単語に実際に割り当てられた語彙項目の 確率イベントを出力します.実際に割り当てられた語彙項目は, 上の手順で得られた derivation の末端ノードから計算します. また負例の確率イベントとして,同じ単語に割り当てることのできる他の語彙項目の 確率イベントを出力します.plays//VBZ//[NP.nomNP.acc]_lxm-singular3rd_verb_rule//uni
この場合,各引数は次のようになります.unimaker uni devel/unimake small.derivbank uni.uevent
まず確率イベントを出力する準備として,derivbank 中の derivatoin から derivation word lattice と word lattice を作ります. word lattice は um_derivation_to_word_lattice/2 によって 作られます.例文 "Ms. Haag plays Elianti." から作られる word lattice の要素には次のものがあります.
extent_word | ||||||||||||||||||||
left_pos | 2 | |||||||||||||||||||
right_pos | 3 | |||||||||||||||||||
word | < |
| > |
derivation word lattice は um_derivation_to_word_lattice/2 によって作ります. 上の word lattice と異なる点は, word 素性に 対応する derivation の末端ノードが 格納されていることです.
次に,derivation word lattice の各要素から正例と負例の確率イベントを 抽出します. derivation word lattice の要素に格納された derivation 末端ノードから um_correct_lexical_entry/2 によって, 実際に割り当てられた語彙項目の名前 ('lex_entry' 型)を得ます. 例文の "play" に対応する末端ノードからは次の名前が得られます.
lex_entry | ||||||||||||||||||
|
||||||||||||||||||
LEX_TEMPLATE |
|
lex_entry | ||||||||||||||||||
|
||||||||||||||||||
LEX_TEMPLATE |
|
こうして得た語彙項目の名前を extract_lexical_event/4 に 与えて,確率イベントを取り出します. このとき正しい語彙項目の名前から取り出した確率イベントは, 正例として出力します. それ以外の語彙項目から取り出した確率イベントは負例として出力します. unimaker の出力は次のようになります.
この例の場合, event_2_2 は単語 "plays" に "[NP.nom< V.bse> NP.acc]_lxm-singular3rd_verb_rule" で 表される語彙項目が対応する確率イベントを表しています. 2行目の最初の "1" が正例であることを,3,4, 5, 6行目の最初の "0" が 負例であることを表しています. "EOS" は文の終わりを表しています.event_2_2 1 ms-period-//NNP//ms-period-//NNP//haag//NNP//haag//NNP//plays //VBZ//[NP.nom< V.bse> NP.acc]_lxm-singular3rd_verb_rule//play//VB//[NP .nom< V.bse> NP.acc]_lxm//elianti//NNP//elianti//NNP//EOS//EOS//EOS//EO S//EOS//EOS//EOS//EOS//uni 0 ms-period-//NNP//ms-period-//NNP//haag//NNP//haag//NNP//plays //VBZ//[NP.nom< V.bse> NP.acc]_lxm-movement_rule-singular3rd_verb_rule/ /play//VB//[NP.nom< V.bse> NP.acc]_lxm//elianti//NNP//elianti//NNP//EOS //EOS//EOS//EOS//EOS//EOS//EOS//EOS//uni 0 ms-period-//NNP//ms-period-//NNP//haag//NNP//haag//NNP//plays //VBZ//[NP.nom< VP.bse> ]_lxm-singular3rd_verb_rule//play//VB//[NP.nom< VP.bse> ]_lxm//elianti//NNP//elianti//NNP//EOS//EOS//EOS//EOS//EOS//EO S//EOS//EOS//uni 0 ms-period-//NNP//ms-period-//NNP//haag//NNP//haag//NNP//plays //VBZ//[NP.nom< V.bse> NP.accNP.acc]_lxm-singular3rd_verb_rule//play//V B//[NP.nom< V.bse> NP.accNP.acc]_lxm//elianti//NNP//elianti//NNP//EOS// EOS//EOS//EOS//EOS//EOS//EOS//EOS//uni 0 ms-period-//NNP//ms-period-//NNP//haag//NNP//haag//NNP//plays //VBZ//[NP.nom< V.bse> NP.accNP.acc]_lxm-movement_rule-singular3rd_verb _rule//play//VB//[NP.nom< V.bse> NP.accNP.acc]_lxm//elianti//NNP//elian ti//NNP//EOS//EOS//EOS//EOS//EOS//EOS//EOS//EOS//uni event_2_3 ...
この場合,各引数は次のようになります.amisfilter lex grammar/lexmask uni.uevent lex.count lex.model lex.event
このとき次のような素性が得られます.ms-period-//NNP//ms-period-//NNP//haag//NNP//haag//NNP//plays//VBZ//[ NP.nom< V.bse> NP.acc]_lxm-singular3rd_verb_rule//play//VB//[NP.nom< V.b se< NP.acc]_lxm//elianti//NNP//elianti//NNP//EOS//EOS//EOS//EOS//EOS// EOS//EOS//EOS//uni
このように,確率イベントファイルの中の全ての正例にマスクをかけて素性を作ります. 作った素性の頻度を記録しカウントファイル "lex.count" に出力します. 作った素性のうち頻度が閾値以上のものを採用し, モデルファイルとイベントファイルを作ります. モデルファイル "lex.model" には,採用した素性とその重みの初期値が記されます. 例えば次のようになります._//_//_//_//haag//_//_//_//plays//_//[NP.nom< V.bse> NP.acc]_lxm-singul ar3rd_verb_rule//_//_//_//_//_//_//_//_//_//_//_//_//_//_//_//uni
イベントファイル "lex.event" には確率イベントファイル "uni.uevent" と 同じ形式になっています. ただし,各確率イベントの代わりにマスクをかけて作った素性が記されています. また,採用されなかった素性は出力されていません._//_//_//_//haag//_//_//_//plays//_//[NP.nom< V.bse> NP.acc]_lxm-singul ar3rd_verb_rule//_//_//_//_//_//_//_//_//_//_//_//_//_//_//_//uni 1.0 _//_//_//_//_//NNP//_//_//plays//_//[NP.nom< V.bse> NP.acc]_lxm-singul ar3rd_verb_rule//_//_//_//_//_//_//_//_//_//_//_//_//_//_//_//uni 1.0 ...
event_2_2 1 _//_//_//_//haag//_//_//_//plays//_//[NP.nom< V.bse> NP.acc]_lx m-singular3rd_verb_rule//_//_//_//_//_//_//_//_//_//_//_//_//_//_//_/ /uni _//_//_//_//_//NNP//_//_//plays//_//[NP.nom< V.bse> NP.acc]_lxm-si ngular3rd_verb_rule//_//_//_//_//_//_//_//_//_//_//_//_//_//_//_//uni ... 0 _//_//_//_//haag//_//_//_//plays//_//[NP.nom< V.bse> NP.acc]_lx m-movement_rule-singular3rd_verb_rule//_//_//_//_//_//_//_//_//_//_// _//_//_//_//_//uni _//_//_//_//_//NNP//_//_//plays//_//[NP.nom< V.bse> NP.acc]_lxm-movement_rule-singular3rd_verb_rule//_//_//_//_//_//_//_/ /_//_//_//_//_//_//_//_//uni ... 0 _//_//_//_//haag//_//_//_//plays//_//[NP.nom< V.bse> ]_lxm-sing ular3rd_verb_rule//_//_//_//_//_//_//_//_//_//_//_//_//_//_//_//uni _ //_//_//_//_//NNP//_//_//plays//_//[NP.nom< V.bse> ]_lxm-singular3rd_ve rb_rule//_//_//_//_//_//_//_//_//_//_//_//_//_//_//_//uni ... 0 _//_//_//_//haag//_//_//_//plays//_//[NP.nom< V.bse> NP.accNP.a cc]_lxm-singular3rd_verb_rule//_//_//_//_//_//_//_//_//_//_//_//_//_/ /_//_//uni _//_//_//_//_//NNP//_//_//plays//_//[NP.nom< V.bse> NP.accNP .acc]_lxm-singular3rd_verb_rule//_//_//_//_//_//_//_//_//_//_//_//_// _//_//_//uni ... 0 _//_//_//_//haag//_//_//_//plays//_//[NP.nom< V.bse> NP.accNP.a cc]_lxm-movement_rule-singular3rd_verb_rule//_//_//_//_//_//_//_//_// _//_//_//_//_//_//_//uni _//_//_//_//_//NNP//_//_//plays//_//[NP.nom< V.bse> NP.accNP.acc]_lxm-movement_rule-singular3rd_verb_rule//_//_//_/ /_//_//_//_//_//_//_//_//_//_//_//_//uni ... event_2_3 ...
上のステップで得られた Amis 形式のモデルファイルとイベントファイルから
Amis を使って
素性の最適な重みを計算します.
この場合,各引数は次のようになります.
amis devel/lexmodel.conf -m lex.model -e lex.event -o lex.output
出力ファイルにはモデルファイルと同じ形式で,計算された素性の重みが示されています.
_//_//_//_//haag//_//_//_//plays//_//[NP.nom< V.bse> NP.acc]_lxm-singul ar3rd_verb_rule//_//_//_//_//_//_//_//_//_//_//_//_//_//_//_//uni 1.0 _//_//_//_//_//NNP//_//_//plays//_//[NP.nom< V.bse> NP.acc]_lxm-singul ar3rd_verb_rule//_//_//_//_//_//_//_//_//_//_//_//_//_//_//_//uni 1.111370e+00 ...
ここでは,構文解析で曖昧性解消をするために使う確率モデルのひとつ, feature forest モデルの推定をします. feature forest モデルは,文に対してその構文木の確率を与えるモデルです. 文をs,その構文木を d とすると, p(d|s) を求めます. Enju の確率モデルは最大エントロピーモデルになっているので, モデルの推定では素性の最適な重みを amis を使って計算することになります.
feature forest モデルの推定では,まず forestmaker ツールにより
確率イベントを feature forest 形式でファイルに出力します.
文法開発で使ったコーパスの中の文を,開発した文法で構文解析して
文法上許される構文木を列挙します.
その中でも文法開発の過程で得た derivation と同様の構文木を正例とし,
構文木の特徴を表す確率イベントを出力します.
他の構文木は負例として,確率イベントを出力します.
この場合,各引数は次のようになります.
forestmaker forest devel/forestmake small.derivbank forest.uevent
正例として正解の構文木を "small.derivbank" の derivation から作ります. まず,正解語彙項目テンプレートの名前 ('lex_entry' 型) を fm_correct_lexical_entry/2 で計算します. 正解構文木の中間ノードは, derivation の中間ノードの DERIV_SCHEMA 素性に指定されたスキーマを使って作っていきます. 例の "Ms. Haag plays Elinati." の文の場合も 構文木が得られます.
また負例として,文法上許される構文木を列挙するため, derivation の文を獲得した文法と UP に似た解析器で構文解析します. このとき,列挙される構文木の中に正解の構文木が含まれる必要があります.
そのために,derivation から derivation word lattice と word lattice の 2種類の word lattic を構成します. word lattice は,fm_derivation_to_word_lattice/2 で作られ, 次のような要素を持ちます.
extent_word | ||||||||||||||||||||
left_pos | 1 | |||||||||||||||||||
right_pos | 2 | |||||||||||||||||||
word | < |
| > |
derivation word lattice は fm_derivation_to_deriv_word_lattice/2 で 作られます. 上の word lattice と異なる点は, word 素性に derivation の末端ノードが格納されていることです.
構文解析器に語彙項目を入力するときには, fm_lexical_entry/2 に deriv word lattice の要素を与え, 返り値の語彙項目を入力します. fm_lexical_entry/2 は deriv word lattice の要素に格納された derivation 末端ノードの sign を見て, 返り値の中に正解の語彙項目テンプレートの名前が必ず含まれるようにしています. また,正解でない語彙項目テンプレートは,Unigram モデルでの FOM を計算して 足切りしています.これは計算コストを小さくするためです. よって例の "plays" に対しては [NP.nom< V.bse> NP.acc]_lxm-singular3rd_verb_rule で 表される正解語彙項目テンプレートと, [NP.nom< VP.bse>]_lxm-singular3rd_verb_rule 等の 正解でない語彙項目テンプレートが入力されます.
この後構文解析をすると, チャート上に derivation forest が得られます. つまり,チャート上に正解構文木と正解でない構文木が重なって表現されます. これと先ほど作られた正解構文木を使って確率イベントを出力します. まず,正解構文木の各ノードから extract_root_event/4 等により 確率イベントを取り出し,正例として出力します. 次に構文解析結果の構文木を列挙するため,チャートの中身を feature forest 形式で 出力します.このときチャート上の各ノードから extract_root_event/4 等により 確率イベントを取り出します. 結果として一文に対する出力は次のようになります.
前半が正例の確率イベントを列挙したもので,後半がチャートの中身を feature forest 形式で表したものです. 全ての文の出力をあわせたものが,出力ファイル "forest.uevent" となります.event_2 1 S_fin//plays//VBZ//[NP.nom< V.bse> NP.acc]_lxm-singular3rd_ver b_rule//play//VB//[NP.nom< V.bse> NP.acc]_lxm//2//1//m//root plays//VBZ //[NP.nom< V.bse> NP.acc]_lxm//haag//NNP//[D< N.3sg> ]_lxm//ARG1//1//sem plays//VBZ//[NP.nom< V.bse> NP.acc]_lxm//elianti//NNP//[D< N.3sg> ]_lxm// ARG2//1//sem SUBJ//1//1//m//m//NNP//VBZ//VP//plays//VBZ//[NP.nom< V.bs e> NP.acc]_lxm-singular3rd_verb_rule//play//VB//[NP.nom< V.bse> NP.acc]_ ... { _ ( root22 S_fin//plays//VBZ//[NP.nom< V.bse> NP.acc]_lxm-singular3rd _verb_rule//play//VB//[NP.nom< V.bse> NP.acc]_lxm//2//1//m//root { node 22 ( b22_0 plays//VBZ//[NP.nom< V.bse> NP.acc]_lxm//haag//NNP//[D< N.3sg > ]_lxm//ARG1//1//sem plays//VBZ//[NP.nom< V.bse> NP.acc]_lxm//elianti// ... event_3 ...
forestmaker の出力した確率イベントのいくつかのフィールドを組み合わせて
素性をつくり,amis 用のモデルファイル,イベントファイルを作ります.
組み合わせるフィールドを指定するためにマスクを定義します.
この場合,各引数は次のようになります.
amisfilter forest grammar/fullmask forest.uevent full.count full.model full.event
次のような素性を得ることになります.S_fin//plays//VBZ//[NP.nom< V.bse> NP.acc]_lxm-singular3rd_verb_rule//p lay//VB//[NP.nom< V.bse> NP.acc]_lxm//2//1//m//root
このようにして作られる素性の頻度を記録し,閾値以上の頻度の素性のみを採用します. その上で amis のモデルファイルと feature forest 形式のイベントファイルを出力します. モデルファイル "full.model" には,採用した素性とその重みの初期値が記されます. 例えば次のようになります._//_//VBZ//[NP.nom< V.bse> NP.acc]_lxm-singular3rd_verb_rule//_//_//_// _//_//_//root
イベントファイル "full.event" は次のようになります._//_//VBZ//[NP.nom< V.bse> NP.acc]_lxm-singular3rd_verb_rule//_//_//_// _//_//_//root 1.0 ...
event_2 1 _//_//_//[NP.nom< V.bse> NP.acc]_lxm-singular3rd_verb_rule//_// _//_//_//_//_//root _//_//VBZ//_//_//_//_//_//_//_//root _//_//VBZ//[ NP.nom< V.bse> NP.acc]_lxm-singular3rd_verb_rule//_//_//_//_//_//_//roo t S_fin//_//_//_//_//_//_//_//_//_//root SUBJ//1//_//m//m//_//_//_//_ //_//[NP.nom< V.bse> NP.acc]_lxm-singular3rd_verb_rule//_//_//_//_//_// ... { _ ( root22 _//_//_//[NP.nom< V.bse> NP.acc]_lxm-singular3rd_verb_rule //_//_//_//_//_//_//root _//_//VBZ//_//_//_//_//_//_//_//root _//_//V BZ//[NP.nom< V.bse> NP.acc]_lxm-singular3rd_verb_rule//_//_//_//_//_//_ //root S_fin//_//_//_//_//_//_//_//_//_//root { node22 ( b22_0 SUBJ// 1//_//m//m//_//_//_//_//_//[NP.nom< V.bse> NP.acc]_lxm-singular3rd_verb ... event_3 ......
この場合,各引数は次のようになります.amis devel/fullmodel.conf -m full.model -e full.event -o full.output
_//_//VBZ//[NP.nom< V.bse> NP.acc]_lxm-singular3rd_verb_rule//_//_//_// _//_//_//root 7.699405e-01 ...