# 文法開発の例

English version

ここでは例として，次のようなコーパス "small.txt" を用いて Enju の文法開発の流れを見ていきます。

>     (S (NP-SBJ Ms./NNP Haag/NNP) (VP plays/VBZ (NP Elianti/NNP)) ./.)
>     ...

1. コーパスの変形
2. 辞書の抽出

- derivation の作成
- 辞書の作成
- 辞書の精製

3. Unigram モデルの推定

- 確率イベントの出力
- マスクによる素性の出力
- 素性の重みの計算

4. Feature forest モデルの推定

- 確率イベントの出力
- マスクによる素性の出力
- 素性の重みの計算

## コーパスの変形

Enju の文法開発では，まず構文情報つきのコーパス Penn Treebank の構文木を変形して， HPSG 構文木に似た形の木を作ります． 例のコーパス "small.txt" を treetrans ツールに入力して， 構文木を変形し HPSG 構文木に似た形の木を作るには次のようなコマンドを用います．

>     treetrans enju-devel/transmain.lil small.txt small.treebank

この場合引数は次のようになります．

- "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

この場合，各引数は次のようになります．

- "enju-devel/lexextract.lil" は構文木から導出木への変換方法や導出木末端ノードから語彙素テンプレートの抽出方法を指定するモジュール
- "small.lexicon" は出力する辞書
- "small.templates" は出力するテンプレートデータベース
- "small.derivbank" は作られた導出木を格納するデータベース(後で曖昧性解消モデルの推定に使われる)
- "small.lexbank" は作られた導出木の末端ノードが格納されるデータベース

### 導出木の作成

lexextract では入力された構文木から導出木を作る際に， 導出木の根ノードにインターフェース述語 `root_constraints/2` による 制約をかけます． Enju 文法の場合，根ノードの sign は次のような制約がかけられます．

| SYNSEM |
| LOCAL|CAT |
| HEAD|MOD | [ ] |
VAL |
| SUBJ | [ ] |
COMPS | [ ]
SPR | [ ]
SPEC | [ ]
CONJ | [ ]
NONLOCAL |
| INHER |
| REL | [ ] |
SLASH | [ ]
F_REL | [ ]
TO_BIND |
| QUE | [ ] |
REL | [ ]
SLASH | [ ]
F_REL | [ ]

その後は入力の木を上から見ていき，親ノードからスキーマを逆適用して 娘ノードを作っていきます． このとき，親ノードの 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.lexicon" にこのキーと 上の語彙素テンプレートの名前のエントリを 追加します． またテンプレートデータベース "small.templates" には， 語彙素テンプレートの名前 "[NP.nom< V.bse> NP.acc]_lxm" と 語彙素テンプレートの素性構造のエントリを追加します．

この処理を "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

この場合，各引数は次のようになります．

- "enju-devel/lexrefine" は語彙素から語彙項目を生成する語彙規則を指定するモジュール
- "small.lexicon" は精製前の辞書
- "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"

このキーは原形が "play" POSが "VBN" なので "played/VBN" に 対応していることになります． このとき，このキー (の表す単語) の頻度に元の語彙素テンプレートの 頻度が足されます．

最後に辞書に未知語のためのエントリを作ります． 辞書のキー (の表す単語) のうち頻度が閾値以下のものが未知語とみなされます． 未知語とされたキーに `unknown_word_key/2` を適用して， 未知語のキーを作ります． 例えば上の "played/VBN" のキーに適用すると，次のような未知語キーのなります．

| word |  |
POS | "VBN"

元のキーより制約を少なくして，POS が "VBN" の単語全てに対応するキーになっています． 元のキーに割り当てられたテンプレートと未知語キーを対応させる辞書エントリを 追加します． つまり，上の受動態のテンプレートが POS が "VBN" の単語全てに割り当てられます． また，元のキーの頻度が閾値以下ならば元のキーのエントリを削除します．

## Unigram モデルの推定

ここでは，構文解析で曖昧性解消をするために使う確率モデルのひとつ， Unigram モデルの推定をします． Unigram モデルは，各単語に対して語彙項目を割り当てる確率のモデルです． 単語をw，周辺の単語列をs，語彙項目をl とすると， p(l|w,s) を求めます． Enju の確率モデルは最大エントロピーモデルになっているので， モデルの推定では素性の最適な重みを amis を使って 計算することになります．

### 確率イベントの出力

Unigram モデルの推定では，まず unimaker ツールにより 確率イベントをファイルに出力します． 確率イベントとは，以下のように // で区切られたフィールドを持つ文字列です．

>     plays//VBZ//[NP.nomNP.acc]_lxm-singular3rd_verb_rule//uni

最後のフィールドはこのイベントのカテゴリ を表しています． その他のフィールドはイベントの特徴を表す記号です． unimaker は derivation 中の単語とその語彙項目を表す確率イベントの リストをファイルに出力します． このとき正例の確率イベントとして，単語とその単語に実際に割り当てられた語彙項目の 確率イベントを出力します．実際に割り当てられた語彙項目は， 上の手順で得られた derivation の末端ノードから計算します． また負例の確率イベントとして，同じ単語に割り当てることのできる他の語彙項目の 確率イベントを出力します．

>     unimaker uni devel/unimake small.derivbank uni.uevent

この場合，各引数は次のようになります．

- "uni" はこれから推定するモデルの名前
- "devel/unimake" は確率イベントを抽出するための述語等を実装したモジュール
- "small.derivbank" は lexextract ツールが出力した derivbation データベース
- "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 | < |
| word |
INPUT | "plays"
SURFACE | "plays"
BASE | "play"
INPUT_POS 1 | "VBZ"
POS 1
BASE_POS | "VB"
POSITION | 2
| >

この word lattice は構文解析器 up のチャートに入力されます． これにより，後で確率イベントを抽出するときに 周辺の単語を特徴フィールドに含めることができます．

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_WORD |
| word |
INPUT | "plays"
SURFACE | "plays"
BASE | "play"
INPUT_POS 1 | "VBZ"
POS 1
BASE_POS | "VB"
POSITION | 2
LEX_TEMPLATE |
| lex_template |  |
LEXEME_NAME | [NP.nom< V.bse> NP.acc]_lxm
LEXICAL_RULES | <singular3rd_verb_rule>

また同じ単語に割り当てることのできる，他の語彙項目の名前を `um_complement_lexical_entry/2` によって得ます． 例の場合次のような名前が含まれます．

| lex_entry |  |
LEX_WORD |
| word |
INPUT | "plays"
SURFACE | "plays"
BASE | "play"
INPUT_POS 1 | "VBZ"
POS 1
BASE_POS | "VB"
POSITION | 2
LEX_TEMPLATE |
| lex_template |  |
LEXEME_NAME | [NP.nom< V.bse> NP.acc]_lxm
LEXICAL_RULES | <movement_rule, singular3rd_verb_rule>

こうして得た語彙項目の名前を `extract_lexical_event/4` に 与えて，確率イベントを取り出します． このとき正しい語彙項目の名前から取り出した確率イベントは， 正例として出力します． それ以外の語彙項目から取り出した確率イベントは負例として出力します． unimaker の出力は次のようになります．

>     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
>     ...

この例の場合， event_2_2 は単語 "plays" に "[NP.nom< V.bse> NP.acc]_lxm-singular3rd_verb_rule" で 表される語彙項目が対応する確率イベントを表しています． 2行目の最初の "1" が正例であることを，3，4, 5, 6行目の最初の "0" が 負例であることを表しています． "EOS" は文の終わりを表しています．

### マスクによる素性の出力

次に上のステップで出力された確率イベントにマスクをかけ， 素性を取り出し， amis 形式のデータファイル出力します．

>     amisfilter lex grammar/lexmask uni.uevent lex.count lex.model lex.event

この場合，各引数は次のようになります．

- "lex" はこれから推定するモデルの名前
- "grammar/lexmask" は確率イベントをかけるマスクを実装したモジュール
- "uni.uevent" は確率イベントが記されたファイル
- "lex.count" は素性の頻度を出力するファイル (テキスト形式)
- "lex.model" は amis のモデルファイル
- "lex.event" は amis のイベントファイル

まず確率イベントファイル "uni.uevent" 中の正例の確率イベントに対して， 確率イベントのカテゴリにしたがってマスクをかけます． マスクはモジュール "grammar/lexmask" の中で， `feature_mask/3` を使って定義されています． 例えばカテゴリ "uni" に対して， (0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) というマスクが定義されているとします． これを上で出てきた正例の確率イベントにかけたとします．

>     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

このとき次のような素性が得られます．

>     _//_//_//_//haag//_//_//_//plays//_//[NP.nom< V.bse> NP.acc]_lxm-singul
>     ar3rd_verb_rule//_//_//_//_//_//_//_//_//_//_//_//_//_//_//_//uni

このように，確率イベントファイルの中の全ての正例にマスクをかけて素性を作ります． 作った素性の頻度を記録しカウントファイル "lex.count" に出力します． 作った素性のうち頻度が閾値以上のものを採用し， モデルファイルとイベントファイルを作ります． モデルファイル "lex.model" には，採用した素性とその重みの初期値が記されます． 例えば次のようになります．

>     _//_//_//_//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
>     ...

イベントファイル "lex.event" には確率イベントファイル "uni.uevent" と 同じ形式になっています． ただし，各確率イベントの代わりにマスクをかけて作った素性が記されています． また，採用されなかった素性は出力されていません．

>     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

この場合，各引数は次のようになります．

- "devel/lexmodel.conf" は amis の設定ファイル
- "lex.model" は amisfilter が出力したモデルファイル
- "lex.event" は amisfilter が出力したイベントファイル
- "lex.output" は amis が出力するファイル

出力ファイル "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 モデルの推定をします． feature forest モデルは，文に対してその構文木の確率を与えるモデルです． 文をs，その構文木を d とすると， p(d|s) を求めます． Enju の確率モデルは最大エントロピーモデルになっているので， モデルの推定では素性の最適な重みを amis を使って計算することになります．

### 確率イベントの出力

feature forest モデルの推定では，まず forestmaker ツールにより 確率イベントを feature forest 形式でファイルに出力します． 文法開発で使ったコーパスの中の文を，開発した文法で構文解析して 文法上許される構文木を列挙します． その中でも文法開発の過程で得た derivation と同様の構文木を正例とし， 構文木の特徴を表す確率イベントを出力します． 他の構文木は負例として，確率イベントを出力します．

>     forestmaker forest devel/forestmake small.derivbank forest.uevent

この場合，各引数は次のようになります．

- "forest" はこれから推定するモデルの名前
- "devel/forestmake" は構文解析のための文法や 構文木から確率イベントを抽出する述語を含むモジュール
- "small.derivbank" は lexextract ツールが出力した derivbation データベース
- "forest.event" は feature forest 形式の確率イベントを出力するファイル

正例として正解の構文木を "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 | < |
| word |
INPUT | "plays"
SURFACE | "plays"
BASE | "play"
INPUT_POS 1 | "VBZ"
POS 1
BASE_POS | "VB"
POSITION | 2
| >

この word lattice は構文解析器の前処理に使われます．

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` 等により 確率イベントを取り出します． 結果として一文に対する出力は次のようになります．

>     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
>     ...

前半が正例の確率イベントを列挙したもので，後半がチャートの中身を feature forest 形式で表したものです． 全ての文の出力をあわせたものが，出力ファイル "forest.uevent" となります．

### マスクによる素性の出力

forestmaker の出力した確率イベントのいくつかのフィールドを組み合わせて 素性をつくり，amis 用のモデルファイル，イベントファイルを作ります． 組み合わせるフィールドを指定するためにマスクを定義します．

>     amisfilter forest grammar/fullmask forest.uevent full.count full.model full.event

この場合，各引数は次のようになります．

- "forest" はこれから推定するモデルの名前
- "grammar/fullmask" はマスクを定義したモジュール
- "forest.uevent" は forestmaker が出力した確率イベントファイル
- "full.model" は出力するモデルファイル
- "full.event" は出力するイベントファイル

まず正例の確率イベントに，その確率イベントのカテゴリに定義されたマスクをかけ， 素性を作ります．マスクは `feature_mask/3` でカテゴリごとに定義されます． 例えば，"grammar/fullmask" モジュールで "root" カテゴリに対して マスク (0, 0, 1, 1, 0, 0, 0, 0, 0, 0) が定義されているとします． このとき，次の確率イベントにこのマスクを適用すると，

>     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

次のような素性を得ることになります．

>     _//_//VBZ//[NP.nom< V.bse> NP.acc]_lxm-singular3rd_verb_rule//_//_//_//
>     _//_//_//root

このようにして作られる素性の頻度を記録し，閾値以上の頻度の素性のみを採用します． その上で amis のモデルファイルと feature forest 形式のイベントファイルを出力します． モデルファイル "full.model" には，採用した素性とその重みの初期値が記されます． 例えば次のようになります．

>     _//_//VBZ//[NP.nom< V.bse> NP.acc]_lxm-singular3rd_verb_rule//_//_//_//
>     _//_//_//root    1.0
>     ...

イベントファイル "full.event" は次のようになります．

>     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
>     ......

### 素性の重みの計算

amisfilter の出力したモデルファイルと feature forest 形式のイベントファイルを amis に入力し，各素性の最適な重みを計算します．

>     amis devel/fullmodel.conf -m full.model -e full.event -o full.output

この場合，各引数は次のようになります．

- "devel/fullmodel.conf" は amis の設定ファイル
- "full.model" は amisfilter が出力したモデルファイル
- "full.event" は amisfilter が出力したイベントファイル
- "full.output" は amis が出力するファイル

出力ファイル "full.output" には，モデルファイルと同じ形式で 計算した素性の重みが記されます．

>     _//_//VBZ//[NP.nom< V.bse> NP.acc]_lxm-singular3rd_verb_rule//_//_//_//
>     _//_//_//root    7.699405e-01
>     ...

Enju 開発者用マニュアル Enju ホームページ 辻井研究室