日本語形態素解析 - 筑波大学 情報学群 情報科学類

「日本語形態素解析」学生実験テキスト
筑波大学 情報メディア創成学類・情報科学類
山本幹雄
2016.9
概要
自然言語処理とは日本語や英語のように人間が使用する言語で書かれたテキストを計算機で
処理する技術である。本実験では、日本語テキストを対象とした単語分割(専門用語では形態
素解析という)の実験を行う。日本語テキストは英語等とは異なり、テキスト中の単語境界が
明示的ではなく、何をするにもまず単語に分割する必要があるため、日本語単語分割技術は高
度な日本語自然言語処理の基本技術となっている。本実験の目的は以下の 2 つを体験的に学習
することである。
• 様々な日本語単語分割法
• 辞書を計算機上で実装するためのデータ構造とアルゴリズム
具体的な課題は、いくつかの分割手法を用いた日本語単語分割システムの作成と、その応用と
して日本語テキストを入力とする簡単な日英辞書引きシステムを作成することである。様々な
単語分割手法があるが、本実験では、以下のような単語分割手法を取り上げる(実際に作成す
るのは “*” が付いた3つ)。
•
•
•
•
•
•
字種による分割*
最長一致法による分割*
単語数最小法による分割
文節数最小法による分割
コスト最小法*
確率的形態素解析
実装に関しては、計算機上での辞書の実装方法(トライや二分探索)が重要である。本実験を
通して、基本的なデータ構造とアルゴリズムの理解を深める。
1
自然言語処理と単語分割
自然言語処理とは人間が日常使用する日本語や英語のような言語で書かれたテキストを計算機処
理する技術のうち、意味内容まで少し踏み込むという意味でやや高度なものを言う。自然言語処理
の応用は多岐に渡るが、最近では情報ネットワークの発達に伴う情報過多に対処するための支援シ
ステムとして期待されている。インターネットでは、マルチメディアによる情報伝達が注目されて
いるが、最も多くの知識・情報を表現しているメディアが自然言語であることには変わりはない。
自然言語で書かれた多量の文書を効率的に変換(翻訳)・検索・要約・抽出・分類する自然言語処
1
理技術は これからの情報ネットワーク時代に欠かせないものとなると考えられている。ワードプ
ロセッサ(ワープロ)は自然言語処理システムとは言わないが、最近のワープロには翻訳機能や要
約機能、推敲支援機能等を持つものもあり、これらは自然言語処理を要素技術として使っていると
言える。
自然言語処理の唯一最大の問題は自然言語文の持つ曖昧性である。ただし、ここでいう「曖昧
性」とは、一般に人間が「曖昧な文」と感じる場合の曖昧性とはニュアンスが異なる。人間が感じ
る自然言語の曖昧性はかなりレベルが高いものであり、機械処理においてまず問題となるのは、人
間が気付いてもいないレベルの曖昧性である。人間は大量の背景知識・常識と高度な状況処理能力
によって多くの曖昧性を無意識のうちに解決している。自然言語を機械処理してみてはじめて、意
識していなかった自然言語の曖昧性に気づくのである。
例えば、本実験のテーマでもある単語分割について、次のような例文を考える。
「赤い四角の上の三角を青い積み木の上に置け。
」
この文章は普通の人間であれば、若干の意見の相違はあるかもしれないが、おおよそ次のような単
語から成っていると考える。左から、文章の単語、読み、原形、品詞である。
赤い
(あかい)
赤い
形容詞
四角
(しかく)
四角
普通名詞
の
(の)
の
格助詞
上
(うえ)
上
普通名詞
の
(の)
の
格助詞
三角
(さんかく)
三角
普通名詞
を
(を)
を
格助詞
青い
(あおい)
青い
形容詞
積み木
(つみき)
積み木
普通名詞
の
(の)
の
格助詞
上
(うえ)
上
普通名詞
に
(に)
に
数詞
置け
(おけ)
置く
動詞
。
(。)
。
句点
この分割は成人している人間にとってきわめて容易である。しかし、機械ではそうは行かない。以
下は 10 万単語レベルの辞書を持つシステムが出力した、単語の候補(の一部!!)である。
2
赤い
赤
い
四
四角
四角
角
角
の
の
上
上
の
の
三
三角
三角
三角
角
角
を
(あかい)
(あか)
(い)
(よん)
(しかく)
(よつかど)
(かど)
(つの)
(の)
(の)
(うえ)
(かみ)
(の)
(の)
(さん)
(さんかく)
(みつかど)
(みすみ)
(かど)
(つの)
(を)
赤い
形容詞
青い
赤
普通名詞
青
いる
動詞
い
四
数詞
積
四角
普通名詞
積み
四角
普通名詞
積み木
角
普通名詞
み
角
普通名詞
み
の
格助詞
み
の
名詞接続助詞
木
上
普通名詞
の
上
普通名詞
の
の
格助詞
上
の
名詞接続助詞
上
三
数詞
に
三角
普通名詞
に
三角
普通名詞
に
三角
地名
に
角
普通名詞
置け
角
普通名詞
置け
を
格助詞
。
(あおい)
(あお)
(い)
(せき)
(つみ)
(つみき)
(み)
(み)
(み)
(き)
(の)
(の)
(うえ)
(かみ)
(に)
(に)
(に)
(に)
(おけ)
(おけ)
(。)
青い
形容詞
青
普通名詞
いる
動詞
積
普通名詞
積む
動詞
積み木
普通名詞
み
名詞接頭辞
み
ナ形容詞接頭辞
みる
動詞
木
普通名詞
の
格助詞
の
名詞接続助詞
上
普通名詞
上
普通名詞
に
数詞
に
格助詞
に
名詞接続助詞
にる
動詞
置く
動詞
置ける
動詞
。
句点
この中から正しい単語列を選ぶのであるが、常識というものを持たない機械にとっては、これが思
いのほか苦手である。これは、かな漢字変換がよく間違えるのと理由は同じである。自然な日本語
というものがどういうものかの知識がなければ、とんでもない組み合わせを出力する。例えば、で
きのよくない単語分割システムは以下のような単語列を上記の単語候補から選択する。
3
赤*
(あか)
赤
普通名詞
い*
( い)
いる
動詞
四角
(よつかど)*
四角
普通名詞
の
(の)
の
名詞接続助詞
上
(うえ)
上
普通名詞
の
(の)
の
格助詞*
三角
(さんかく)
三角
普通名詞
を
(を)
を
格助詞
青*
(あお)
青
普通名詞
い*
( い)
いる
動詞
積み*
(つみ)
積む
動詞
木*
( き)
木
普通名詞
の
(の)
の
名詞接続助詞
上
(かみ)*
上
普通名詞
に
(に)
に
数詞*
置け
(おけ)
置く
動詞
。
(。)
。
句点
‘*’ が付いている箇所は間違いである。
「赤い」という形容詞の活用語尾「い」を「いる」という動詞
の連用形と間違ったり、
「積み木」を「積む」の連用形と「木」の組み合わせと解釈している。
「積
み木」の例では、
「荷を積み (,) 木を切り倒せ」のような文を考えた場合、
「積み」と「木」で分割す
ることが妥当であるため、周りの文脈を見ずに、1単語とするか、2単語とするかを決定できない
(もちろんこの例文の場合「積み」と「木」の間に読点を入れるのが普通であるが、絶対に入れる
とは限らない)。人間は、日本語としての自然性の知識を無意識のうちに使い、苦もなく(読点が
ないと少し悩むかもしれないが)文全体からどちらかを決定できる。
機械に同じことをやらせるには、人間が無意識に使っている知識を明示的に取り出し、プログラ
ムに埋めこむ必要がある。無意識に使っている知識を明示的かつ体系的に取り出すことは容易では
なく、人間が使用している知識は膨大な量であることが知られている。また、完全な分割システム
を構築するには、機械がいわゆる文の「理解」をする必要があるが、これは現状の技術では困難で
ある。このため、現在最高レベルの日本語分割システムでも 99% 程度の正解精度*1 である。すな
わち、100 単語に1つくらいは間違う。本実験では、効果的な手法として知られている手法の簡易
版を用いて、区切りだけの正解精度で 90% 以上の正解精度を目指す。
*1
この場合の正解精度とは、システムが分割し、出力した単語のうち正しかった単語の割合である。ただし、単語区切
りだけでなく品詞等の付加情報に関しても正解してはじめて正しい単語に区切れたとみなした場合の数値である。
4
2
日本語単語分割手法
日本語の単語分割手法は 1960 年代から精力的に研究されており、以下のようなさまざまなもの
が提案されている。
• 字種による分割
• 最長一致法
• 単語数最小法
• 文節数最小法
• コスト最小法
• 確率的形態素解析
最初の「字種による分割」以外は辞書を用いる手法である。以下の節で各手法を解説する。最後の
「確率的形態素解析」は「コスト最小法」の確率論的な見地からの再形式化であり、やや高度なの
で本実験では取り上げない。
2.1
字種による分割
ここでいう字種とは、漢字、平仮名、片仮名、アルファベット、記号等の字の種類のことである。
日本語の名詞の多くは、単一の字種で書かれることが多い。例えば、本節の最初の2文には、
「字種」、「漢字」、「平仮名」
、「片仮名」、「アルファベット」、「記号」
、「字」
、「種類」、「日本
語」、「名詞」
、
「単一」
といった名詞が含まれるが、漢字だけかアルファベットだけから構成されている。日本語の文は半
分程度は名詞であろうという推測から字種が変化する場所を単語の区切りとする方法が「字種によ
る分割」である。この考え方で、本節の最初の2文を区切ってみると以下のようになる。
「ここでいう/字種/とは/、/漢字/、/平仮名/、/片仮名/、/アルファベット/、/
記号等/の/字/の/種類/のことである/。/日本語/の/名詞/の/多/くは/、/単
一/の/字種/で/書/かれることが/多/い/。
」
非常に単純な方法の割には、おおよそ妥当で、特に名詞に関してはよい結果のように思える。動詞
や形容詞に関しても、活用しても変化しない語幹の部分を単語として認めればおおよそ正しいと言
える。しかし、1 章の例は、以下のようになってしまう。
「赤/い/四角/の/上/の/三角/を/青/い/積/み/木/の/上/に/置/け/。
」
「積み木」は3つの単語に分割されてしまうし、後半はすべて1文字の単語と見なされている。あ
まりよい結果とは言えない。
5
この手法のもう一つの大きな問題は、単語分割だけでは様々なアプリケーションにとって不十分
な点である。たとえば WWW 上の情報検索システムのインデックスとして、各文書から名詞だけ
をキーワードとして取り出したい場合、この手法では分割された単語の品詞が分からないので、ど
れが名詞であるかの情報が得られない。また、動詞や形容詞については、活用によって語尾変化す
るため、同じ意味の単語が活用によって異なる単語と見なされてしまう。活用する単語に関しては
基本型(「置け」であれば「置く」
)の情報が必要である。さらには、日本語テキストの音声読み上
げシステムでは、各単語の読みが必要であるが、この手法ではこれも出せない。一般に、自然言語
処理の前処理としては、単語の分割の他、上であげた「品詞」
、
「原形」
、
「読み」の情報を付加する
ことが重要である。これらの付加情報を得るためには、単語辞書が必要である。以下の節では、単
語辞書を使い、性能を上げながら、付加情報を付与する手法を解説する。
2.2
最長一致法
1 章の例から「青い積み木」の部分を考える。この部分から単語辞書を引いて取り出される候補
の単語は以下のようになる。
青い
(あおい)
青い
形容詞
青
(あお)
青
普通名詞
い
(い)
いる
動詞
積
(せき)
積
普通名詞
積み
(つみ)
積む
動詞
積み木
(つみき)
積み木
普通名詞
み
(み)
み
名詞接頭辞
み
(み)
み
ナ形容詞接頭辞
み
(み)
みる
動詞
木
(き)
木
普通名詞
このうち、
「青い」と「積み木」が正解であるが、これは最も長い単語を選んでいると言える。以下
のような観察が得られる。
「辞書に載っている単語のうち、最も長い単語が正解である可能性が高い」
この観察を利用した最も単純な方法が「最長一致」による単語分割である。アルゴリズムは単純
で、まず文の頭から始まる文字列のうち、辞書にある単語をすべて調べる。その中から、最も長い
もの(最長一致)を最初の単語と決定し、決定された単語の次から始まる単語を同様に決定してい
く。最も長い単語が複数あった場合は、ランダムに選ぶか、品詞の出現頻度の高いものとする。品
詞の出現頻度は、分析したデータに依存するが、おおよそ以下のような順序で出現頻度が高い。
1. 助詞
2. 名詞
6
3. 動詞
4. 記号 (句読点など)
5. 助動詞
6. 接尾語
7. 数字
8. 副詞
9. 形容動詞
10. 形容詞
11. 連体詞
12. 接続詞
13. 接頭語
14. 感動詞
もっと大量の日本語テキストから統計データが得られる場合は、単語そのものの出現頻度を使うと
もっと性能を向上させることができる。
先頭から最も長い単語、複数あった場合は出現頻度の多い品詞を持つ単語を優先させた場合の 1
章の例文の解析結果は以下のようになる。
赤い
(あかい)
赤い
形容詞
四角
(よつかど)*
四角
普通名詞
の
( の)
の
格助詞*
上
(うえ)
上
普通名詞
の
( の)
の
格助詞*
三角
(さんかく)
三角
普通名詞
を
( を)
を
格助詞
青い
(あおい)
青い
形容詞
積み木
(つみき)
積み木
普通名詞
の
( の)
の
格助詞*
上
(うえ)
上
普通名詞
に
( に)
に
格助詞
置け
(おけ)
置く
動詞
。
( 。)
。
句点
ほぼ完璧であるが、
「の」に関して「格助詞」を「名詞接続助詞」より優先させたため品詞が誤って
いるし、
「四角」の読みが「よつかど」となっている。しかし、細かい品詞の分類や読みが必要ない
場合は、これで完璧である。この手法ではうまく解析できない例文は以下である。
「その日直方体について学んだ。
」
7
これを最長一致法で解析すると以下のようになる。
「その/日直/方/体/に/ついて/学んだ/。
」
正解はもちろん、
「その/日/直方体/に/ついて/学んだ/。
」
である。敗因は、文全体を見ずに文頭から長い単語を決定してしまったことである。
「日」以下の解
析のところで、長い単語である「日直」を選択することを我慢して「日」を選べば、その次の段階
で長い単語「直方体」を得ることができたはずである。解決策として、先頭から単語を決定するの
ではなく、決定は保留して、文全体で長い単語を選択するようにした手法が次の「単語数最小法」
である。
2.3
単語数最小法
手法の名前から分かるように、「単語数最小法」は単語分割したときに、文全体の単語数が最小
になるような分割を優先する方法である。単語数を最小にするということは、できるだけ長い単語
を選ぶことを意味するが、文全体を見て決定する点が前節の最長一致法と異なる。
この手法を理解するために、各文字位置から辞書を引き、見つかった単語候補をすべて列挙した
図を考える。この図を単語ラティスと呼ぶ。前節で考えた例文の一部「その日直方体」を考えた場
合の単語ラティスを図 1 に示す。単語ラティスから、入力文全体を覆う適切な単語候補列を見つけ
ることが単語分割の仕事である。この例では、入力全体を覆う候補単語の列は3つの可能性がある
(図 1 下)。単語数が最小となる分割を優先すると、「その日直方体」に対しては、単語数が3とな
る単語分割候補3を優先する(この場合、正解)
。
単語数最小法は、文全体を見て最も妥当な分割を得る方法であるため、最長一致法よりも性能が
改善される可能性が高いが、計算コストが増加する。各文字位置で、 M 個の単語候補が辞書から
得られるとし、入力文が N 文字からなっているとする。このとき、文全体の単語分割の可能性の
数は、各文字位置で M 個の候補から1つの単語を決定する必要があるため、 M N 個の組み合わせ
の中から正しい分割を得ることになる。1 章の例を見れば分かるように、10 万単語規模の辞書を使
うと M は少なくとも 2 程度にはなる。また、新聞記事の文章はやや長めで平均で 50 文字程度は
ある。すると、 M N = 250 は 1000 兆を越える数となる。現代の計算機は 10 年前に比べてもはる
かに高速であるが、一文ごとに 1000 兆もの組み合わせを調べていては非効率である。
この問題を高速に解決する手法が、ビタービアルゴリズムである。ビタービアルゴリズムは動的
計画法の一種であり、以下のようなアイデアに基づく。
文頭からある分割候補箇所 x の間の文字列を考える。この文字列に対して、単語数最小とす
る分割が求まっているとし、かつ全体の単語数最小分割が分割箇所 x を含むとすると、すで
に求まっている文頭と x の間の単語数最小分割は、全体に対しても単語数最小分割である。
8
図1
「その日直方体」の単語ラティスと可能な単語分割集合
文頭と x の間の単語数最小分割が求まっており、その数を K とする。もし、全体の単語数最小分
割が x を含むとすると、全体の単語数は、まだ求まっていない「 x の直後から文末までの最小単語
数」に K を足したものとなる。ここで、文頭と x の間の分割をすでに求まっている単語数最小分
割と異なる分割としてしまうと、その単語数は K より大きくなることはあっても小さくなること
はあり得ないため、全体の単語数も小さくなることはあり得ない。よって、文頭と x の間の単語数
最小分割は、全体の単語数最小分割に必ず含まれる(もちろん、全体の分割が x を含む場合に限ら
れるが)。
図 1 の例で考えると、もし全体の文字列「その日直方体について学んだ。」の分割が文字位置 6
の直後を含む場合、文頭から文字位置 6 までの最適分割(図 1 中の単語分割候補 3)を必ず含む。
また、もし文字位置 4 の直後を全体の分割が含むとする場合は、
「その日直」の最小単語分割である
「その/日直」
(図 1 中の単語分割候補 2 の一部)を全体が含むことになる。この場合誤りとなるが、
文全体で最適化するため、局所的に単語数が少ない誤った分割が選択される可能性は低くなる。
全体の最適分割を求めるには、 x を1文字づつ文末に向けて移動させていけばよい。ある文字位
置 x に関して、文頭から x までの単語数最小分割を求めるには、これまで求まっている文頭から
y まで (y < x) の単語数最小分割したときの単語数を N (y) とすると、次のようにすればよい。ま
ず、x で終わる単語の候補 w1 , w2 , ..., wn を集めてくる。それぞれの単語の開始位置を y1 , y2 , ..., yn
とすれば、文頭から yi までの単語数最小分割はすでに求まっているので、単語 wi を採用した場合
9
の文頭から x までの最小分割数 N 0 ( x, wi ) は、次のように求まる。
N 0 ( x, wi ) = N (yi ) + 1
N 0 ( x, wi ) の中で最小となる値が、文頭から x までの単語数最小分割時の単語数 N ( x ) であり、
N 0 ( x, wi ) を最小とする wi と文頭から yi までの単語数最小分割を合わせた分割が、文頭から x ま
での単語数最小分割である。
x を 1 からはじめて文末に向けて移動させながら、上記のアルゴリズムを繰り返せば、文末に
至ったときに、文全体の単語数最小分割が求まる。文末に至ったときに、単語数最小分割の単語列
を出力するためには、各 N ( x ) の計算時に、最小の N 0 ( x, wi ) を与える wi と yi を各 x ごとに記憶
しておけば、長さ L の入力文としたときに x = L から文頭に向けて、再生することができる。
以上の手法を厳密に記述すると以下のようになる。まずは、単語数最小法による単語分割問題の
定義である。
問題の定義:単語数最小法
長さ L の入力文字列: S L = c1 , c2 , c3 , ..., c L (ci は文字).
入力の部分文字列: Si = 入力文字列中の i 番めから j 番めの部分文字列.
単語 w の表記: str (w) = 単語 w の表記文字列.
j
単語 w の表記の長さ: |w| = 単語 w の表記 str (w) の長さ(文字数).
辞書: dic(w) =
(
1 if str (w) が辞書の中にある
0 otherwise
.
単語列: W = w1 , w2 , ..., w|W | , ここで、dic(wk ) = 1.
単語列 W の長さ: |W | = W 中の単語の数.
単語列 W の表記: str (W ) = W の各単語 wk の表記 str (wk ) を接続した文字列.
単語数最小法による入力文字列 S L の単語分割:
Ŵ = ŵ1 , ŵ2 , ... = arg
min
W:str (W )=S L
|W | .
次は、Viterbi アルゴリズムによる上記問題の解法である。以下のアルゴリズムでの各変数は、
文頭から文字位置 x までの単語数最小分割による(部分)単語分割を W 0 としたとき、次のような
意味を持つ値を格納している。W 0 の単語数を N ( x )、W 0 の最後(右端)の単語を W ( x )、同様に
最後の単語の開始文字位置(入力文字列中の)を B( x ) に格納しながら、計算が進む。arg は引数の
値を返す。例えば、argi mini,j f (i, j) のような場合、最小の f (i, j) となる i を返すことを意味する。
Viterbi アルゴリズムによる Ŵ の計算(単語数最小法)
(1) 初期化
N (0) = 0
10
表 1 Viterbi アルゴリズムによる単語数最小分割の計算
x
0
1
2
3
4
5
6
N (x)
0
∞
1
2
2
3
3
W (x)
–
–
その
日
日直
方
直方体
B(w)
–
–
1
3
3
5
4
for i = 1 to L do, N (i ) = ∞ (実際には L を代入しておけばよい)
(2) 繰り返し: x = 1〜 L
N (x) =
min
{w,i |dic(w)=1,Six =str (w)}
W ( x ) = argw
B( x ) = argi
N (i − 1) + 1.
min
{w,i |dic(w)=1,str (w)=Six }
min
{w,i |dic(w)=1,str (w)=Six }
N (i − 1) + 1.
N (i − 1) + 1.
(3) 終了
M = |Ŵ | = N ( L)
ŵ M = W ( L).
i ( M ) = B( L) :ŵ M の開始位置.
(4) バックトラック
ŵk = W (i (k + 1) − 1)
i ( k ) = B ( i ( k + 1) − 1)
)
k = M − 1, M − 2, ..., 1.
Viterbi アルゴリズムによる「その日直方体」の最小単語数分割の計算の様子を表 1 に示す。こ
れは上記アルゴリズムの (2) 繰り返しの中で計算された N ( x ),W ( x ),B(w) の値をまとめたもので
ある。 「(2) 繰り返し」中の x の各段階において計算される各変数( N, W, B)を細かく説明する
と以下のようになる。 x = n のとき、 x = 0〜n − 1 の各変数の値(少なくとも N )は確定してい
ることに注意せよ。
x = 1 のとき: 文字位置 1 で終わる単語は図 1 を見ると存在しない(あるとしたら、「そ」とい
う単語であるがこれは辞書にのっていなかったと仮定する)
。このため、N (1) は初期化のま
まの値( N (1) = ∞)となる。W (1), B(1) は未定義のままである。
x = 2 のとき: 文字位置 2 で終わる単語は、
「その」と「の」の2つである。この2つの単語の直
前の文字位置はそれぞれ、0 と 1 であるから、N (0) + 1 = 1 と N (1) + 1 = ∞ が比較され、
11
文字位置 2 の直後を単語境界とする場合は、「その」の方を選択した方が単語が少なくなる
ことが分かる。W (2) には「その」
、 B(2) には「その」の開始文字位置を格納する。
x = 3 のとき: 文字位置 3 で終わる単語は、「日」のみであり、「日」の直前の文字位置は 2 であ
るため、 N (3) = N (2) + 1 = 2, W (3) =「日」, B(3) = 3 となる。
x = 4 のとき: 文字位置 4 で終わる単語は、
「直」と「日直」の2つであり、それぞれの直前の文
字位置は 3 と 2 である。N (3) + 1 = 3 と N (2) + 1 = 2 を比較すると、
「日直」を選んだ方
が単語数が少なくなるため、文字位置 4 の直後を単語境界とする場合は、「日直」を選択す
べきでぁり、このとき文頭から文字位置 4 までの最小単語数は 2 である。この 2 を N (4) の
値として格納する。
x = 5 のとき: 文字位置 5 で終わる単語は、「方」のみであり、「方」の直前の文字位置は 4 であ
るため、 N (5) = N (4) + 1 = 3, W (5) =「方」, B(5) = 5 となる。
x = 6 のとき: 文字位置 6 で終わる単語は、
「体」と「直方体」の2つであり、それぞれの直前の
文字位置は 5 と 3 である。N (5) + 1 = 4 と N (3) + 1 = 3 を比較すると、
「直方体」を選ん
だ方が単語数が少なくなることため、文字位置 6 の直後を単語境界とする場合は、
「直方体」
を選択すべきである。 N (6) = N (3) + 1 = 3, W (6) =「直方体」, B(6) = 4 となる。
最後に最小単語となる単語分割を、文末位置から逆にたどって再構成する。上の例では文末位置
を 6 と仮定しているため、N (6) の値が最小単語数であり(この場合 3)
、3番目の単語が「直方体」
であることが W (6) の値から分かる。先頭から2番目の単語は、「直方体」の直前の文字位置、す
なわち B(6) − 1 = 3 の直後で分割すると仮定した場合の最適単語 W (3) =「日」であることが分
かる。1番目の単語は B(3) − 1 = 2 から、W (2) = 「その」であることが分かる。 B(2) − 1 = 0
であるため、
「その」の前は文頭である。以上の結果から、最小単語分割された結果は、先頭から
「その」
、
「日」
、
「直方体」
であることが計算された。
最後に単語数最小法でうまくいかない例を示す。
「花子を家におくりました。
」
これを単語数最小法で解析すると以下のようになる。
花子
(はなこ)
花子
固有名詞
を
(を)
を
格助詞
家
(いえ)
家
普通名詞
におくり
(におくり) 荷送り
普通名詞
ました
(ました)
真下
普通名詞
。
(。)
。
句点
12
「荷送り」は新明解国語辞典(三省堂)にのっている立派な名詞である。また、
「ました」に関して、
名詞としての「真下」と、助動詞としての「まし + た」があるが、名詞の方が頻度が多いので「真
下」になっている*2 。この問題は、
「文節数最小法」で改善され、
「コスト最小法」で解決される。
2.4
文節数最小法
文節数最小法は、単語数ではなく文節の数を最小とする分割基準である。文節の定義を厳密に行
うことは難しいが、ここでは一つの自立語の前後に任意の数の付属語が接続したものであると定義
する。自立語とは名詞、動詞、形容詞等のそれだけで意味を持つ単語、付属語とは助詞、助動詞、
接辞等のそれだけで意味を持たない単語である。例えば、前節で述べた「花子を家におくりまし
た。」であれば、
「花子を」, 「家に」, 「おくりました」が文節に相当する。
上記の文節の定義を用いれば、ある単語分割における文節数はその単語分割中の自立語の数と等
しい。W の長さ(単語数)ではなく、W 中の自立語数に対して最小化するように単語数最小法の
枠組みを変更すると文節数最小法となる。
問題の定義:文節数最小法
自立語関数: jiritsu(w) =
(
1 if w が自立語
0 otherwise
.
文節数最小法による入力文字列 S L の単語分割:
|W |
Ŵ = arg
min
∑ jiritsu(wi ) .
W:str (W )=S L i =1
Viterbi アルゴリズムも同様であり、
「(2) 繰り返し」のステップを以下のように変更することで、
文節数最小法の Viterbi アルゴリズムが作成できる。
(2) 繰り返し: x = 1〜 L
N (x) =
min
{w,i |dic(w)=1,Six =str (w)}
W ( x ) = argw
B( x ) = argi
N (i − 1) + jiritsu(w).
min
{w,i |dic(w)=1,str (w)=Six }
min
{w,i |dic(w)=1,str (w)=Six }
N (i − 1) + jiritsu(w).
N (i − 1) + jiritsu(w).
単語数最小化法でうまく行かなかった前節の例文「花子を家におくりました。」が、文節数最小
*2
品詞の頻度ではなく、単語の頻度を使えば、
「真下」を「ました」と平仮名表記する場合より、助動詞としての「まし
た」の方がはるかに多いのでこの問題は解決される。しかし、
「ましたにある」のような場合も助動詞「ました」を優
先してしまうことになる。
13
法では改善される。
花子
(はなこ)
花子
固有名詞
を
(を)
を
格助詞
家
(いえ)
家
普通名詞
におくり
(におくり) 荷送り
普通名詞
ました
(ました)
真下
普通名詞
。
(。)
。
句点
という分割では、自立語数が4である。これに対して、正しい分割である
花子
(はなこ)
花子
固有名詞
を
(を)
を
格助詞
家
(いえ)
家
普通名詞
に
(に)
に
格助詞
おくり
(おくり)
送る
動詞
ました
(ました)
ました
助動詞
。
(。)
。
句点
は、自立語数3で上記の誤った例は出力されなくなる。しかし、「におくり」の部分を一つの名詞
と見なしても、全体の自立語数は3のままであり、依然「におくり」を一つの単語と出力される可
能性は残る。これを解決するには、言語学的な知識を導入して「日本語らしさ」の観点から単語分
割を評価する必要がある。
2.5
コスト最小法
「家におくりました」の部分は、
「家/におくり/ました」と分割するより、
「家/に/おくり/ま
した」と分割する方が自然である。それぞれの分割は、品詞で書くと、次のような可能性がある。
(
「家/におくり/ました」→
(1)「名詞/名詞/名詞」or
(2)「名詞/名詞/助動詞」
(
「家/に/おくり/ました」→
(3)「名詞/助詞/動詞/名詞」or
(4)「名詞/助詞/動詞/助動詞」
4つの品詞列パターンがあるが、この中では (4) の「名詞/助詞/動詞/助動詞」のパターンが
最も日本語らしいと言えるのではないだろうか。(3) のパターンも悪くないが(例えば「家/に/
14
送った/人」)
、動詞の後に助動詞が来るパターンの方がより日本語らしいと言える*3 。また、(1)
に関しても、あり得ない訳ではないが(例えば「自然/言語/処理」)
、(4) の方がより日本語らし
い(と私は思う)
。(2) に関しては、名詞の直後に助動詞が来ているため、これは日本語として明ら
かにおかしい。
同様に、各々の単語(表記等)に関しても日本語らしさの観点から評価できる。例えば、「荷送
り」を「におくり」
、
「真下」を「ました」と平仮名で記述することはめったにないと言える。また、
「荷送り」という単語そのものが「送る」や助詞の「に」よりも、はるかに出現する可能性は低い。
このように、品詞列と単語そのものの日本語らしさを総合的に評価すれば、より妥当な分割を得
ることができる。これを実現する一つの手法がコスト最小法である。自然な日本語からはずれる単
語列を仮定する場合高いコストがかかり、日本語らしい単語列を仮定する場合はコストが低くなる
ように考える。すなわち、日本語らしい品詞列パターン、よく出現する単語には低いコスト、日本
語らしくない品詞列パターン、あまり出現しない単語には高いコストを割り振る。単語分割全体の
コストを合計し、最も低いコストになる分割を分割結果とするのが「コスト最小法」である。ここ
で、任意の数の品詞から成る品詞列パターンをすべて記憶したり、コストを設定することは困難で
ある。このため、実際には、隣り合った2つの品詞間の接続コストを設定し、各品詞間のコストを
合計することにより、品詞列パターン全体のコストを計算する。
例文「花子を家におくりました。
」の単語分割で出現する品詞接続コストを考えると、
「名詞/名
詞」や「動詞/名詞」の接続コストよりも、
「名詞/助詞」や「動詞/助動詞」の接続コストを少し
低く設定する。また、「名詞/助動詞」には非常に高いコストを設定すべきである。これらは上記
例文だけでなく、一般的な日本語にもあてはまると思われる。表 2 に、品詞接続コスト例を示す。
この中で、「名詞+助詞」には ‘5’ という低いコスト、「名詞+名詞」には ‘10’ という少し高いコス
トを割り振っている。「名詞/助動詞」はまずありえないので、‘1000’ という非常に高いコストを
設定している。絶対ありえない接続には無限大のコストを割り振り、可能性の候補として扱わない
ことにより、計算効率を高めることもできる。しかし、自然言語の常として例外が存在するため、
「絶対にありえない」と言い切ることは困難な場合も多い。その場合は、非常に高いコストを設定
すればよい。他に妥当な候補がない場合は、高いコストでも選択される余地は残る。
単語のコスト例を表 3 に示す。単語コストは、ある品詞を仮定した場合のある単語(表記)の出
現のしにくさを表している。品詞を仮定したのは、品詞接続コストと連携してきめ細かい評価を行
うためである。
「ました」に関して、名詞(真下)と助動詞の可能性があるが、名詞の場合、平仮名
表記はまれであるが、助動詞の場合は平仮名表記のみである。品詞を考慮しないと、「ました」に
対してどのくらいの単語コストを設定すればよいかがはっきりしなくなる。品詞別にすれば、名詞
としての「ました」は高コスト、助動詞としての「ました」には低コストを付与できる。なお、本
表では以下の議論を簡単にするため、いくつかの妥当な単語を無視している。例えば、「した」は
「勉強した」のようにサ変名詞の語尾、あるいは動詞としても妥当であるが、無視した。
*3
この例では、動詞といっても「おくり」は連用形であり、連用形の後に名詞が続くことはまれである。このように、
単なる品詞でなく、その活用形や品詞の細かな分類を行えばより妥当な評価が可能である。
15
表2
表3
品詞接続コスト例:縦の品詞の後に横の品詞が後続する場合のコスト.
名詞
動詞
助詞
助動詞
句読点
名詞
10
100
5
1000
10
動詞
10
10
10
5
5
助詞
5
5
5
100
5
助動詞
10
10
5
5
5
句読点
5
10
1000
1000
1000
単語コスト例:横の品詞を仮定した場合、縦の単語が出現するコスト. ‘–’ は辞書にないことを表す.
名詞
動詞
助詞
助動詞
句読点
花
5
–
–
–
–
子
5
–
–
–
–
花子
10
–
–
–
–
家
5
–
–
–
–
下
5
–
–
–
–
した
50
–
–
–
–
りま
1000
–
–
–
–
真下
5
–
–
–
–
ました
100
–
–
5
–
荷送り
20
–
–
–
–
におくり
100
–
–
–
–
送る
–
5
–
–
–
おくる
–
10
–
–
–
置く
–
5
–
–
–
おく
–
5
–
–
–
を
–
–
2
–
–
に
–
–
5
–
–
。
–
–
–
–
2
16
図 2 例文に対する単語候補の接続可能性とコストを表した単語ネットワーク
以上のコストを仮定し、コスト最小法を用いた 例文「花子を家におくりました。」の単語分割を
以下に解説する。ここでは、接続を明示的に表現するために、例文の単語ラティスを等価なネット
ワークに変換したものを考える(図 2)
。この図のなかで、ノードは候補単語、アークは可能な接続
を表しており、各アーク、各ノードに付与された数値が、表 2,3 から得られたコストである。文頭
から文末にいたる一つのパスが単語分割の候補を表現している。単語のネットワーク上で、最も日
本語らしいパス、すなわち品詞接続コストと単語コストの総合計を最小とするパスを見つけること
がコスト最小法による単語分割である。図 2 中で、最小コスト(総コスト 69)のパスが実線で示さ
れている。このパスの分割は、
「花子(名詞)/を(助詞)/家(名詞)/に(助詞)/おくり(動
詞)/ました(助動詞)/。
(句点)
」となり、正解である。
実際に計算機で最小コストのパスを見つけるには、単語数最小法・文節数最小法と同様に Viterbi
アルゴリズムを利用する。単語数最小法に比べて、最小コスト法の Viterbi アルゴリズムは前後の
単語品詞に影響を受けるためやや複雑となる (詳しくは授業「知識・自然言語処理」または「自然
言語処理」で教えた通り)。
本実験では、品詞接続コストと単語コストの両者を考慮する完全なコスト最小法ではなく、実現
が比較的容易な単語コストのみを考えたコスト最小法(以下、単語コスト最小法と呼ぶ)を実現す
る。単語コスト最小法では、各分割候補中に現れる単語コストの総計が最小となるような候補を解
とする。ここで、各単語コストをすべて「1」と仮定すると単語数最小法とまったく等価である。
逆に、単語数最小法のアルゴリズムを単語コスト最小法に変更するには、単語数最小法のアルゴリ
ズムにおいて選択した単語ごとに「1」を加えていた箇所を「1」の代わりに単語コストを加える
ように変更すれば、単語コスト最小法の高速なアルゴリズムとなる。
単語コスト最小法を厳密に記述すると以下のようになる。単語数最小法とほぼ同じである (アン
ダーラインの箇所が単語数最小法との違い)。
問題の定義:単語コスト最小法
長さ L の入力文字列: S L = c1 , c2 , c3 , ..., c L (ci は文字).
17
入力の部分文字列: Si = 入力文字列中の i 番めから j 番めの部分文字列.
単語 w の表記: str (w) = 単語 w の表記文字列.
j
単語 w の表記の長さ: |w| = 単語 w の表記 str (w) の長さ(文字数).
辞書: dic(w) =
1 if str (w) が辞書の中にある
(
0 otherwise
.
単語コスト: wordcost(wk ) = 単語 wk の単語コスト
単語列: W = w1 , w2 , ..., w|W | , ここで、dic(wk ) = 1.
単語列 W の長さ: |W | = W 中の単語の数.
単語列 W の表記: str (W ) = W の各単語 wk の表記 str (wk ) を接続した文字列.
単語コスト最小法による入力文字列 S L の単語分割:
|W |
Ŵ = ŵ1 , ŵ2 , ... = arg
min
∑ wordcost(wi ) .
W:str (W )=S L i =1
次は、Viterbi アルゴリズムによる上記問題の解法である (単語数最小法と異なる箇所を下線で
示す)。単語数最小法とは異なり、(3) 終了の時点で N ( L) を見ても解 (Ŵ) の長さが分からないの
で、最後の単語から順にバックトラックした後に単語を反転させる操作が必要となる。
Viterbi アルゴリズムによる Ŵ の計算(単語コスト最小法)
(1) 初期化
N (0) = 0
for i = 1 to L do, N (i ) = ∞
(実際には「単語コストの最大値 × L + 1」を代入しておけばよい)
(2) 繰り返し: x = 1〜 L
N (x) =
min
{w,i |dic(w)=1,Six =str (w)}
W ( x ) = argw
B( x ) = argi
N (i − 1) + wordcost(w).
min
{w,i |dic(w)=1,str (w)=Six }
min
{w,i |dic(w)=1,str (w)=Six }
N (i − 1) + wordcost(w).
N (i − 1) + wordcost(w).
(3) 終了
ŵ0 1 = W ( L).
i (1) = B( L) :ŵ0 1 の開始位置.
(4) バックトラック
18
以下を k = 2, 3, ... と、 B(k ) = 1 となるまで繰り返す。
ŵ0 k = W (i (k − 1) − 1)
i ( k ) = B ( i ( k − 1) − 1)
)
k = 2, 3, ...
最後の k を M とすると、最終的な分割単語列 Ŵ は次のように得られる。
Ŵ = ŵ1 , ŵ2 , ..., ŵ M
ここで ŵ j は次のように定義される。
ŵ0 j = ŵ0 M− j+1
3
日本語文字コード
計算機の中ではすべての情報が{0,1}の bit の列で表現されている。文字については、文字と bit
列との対応表を作成し、同じ表を全員が共通に使えば文字の伝達ができる。対応表が文字コード規
格であり、日本では JIS、世界的には ISO で規格が定義されている。
具体的な日本語処理を行うプログラムを書く場合には、日本語が計算機の中でどのように表現さ
れているか、すなわち、日本語のコード規格を把握しておくことが必須である。本節では日本語
コード規格の概略と、課題のプログラムを作成するために必要な日本語符号化方式の一つである日
本語 EUC(Extended Unix Code) コードについて解説する。具体的な EUC の処理プログラムに
ついては課題 1 において作成する。
3.1
日本語文字コードの概略
日本語の文字コードは多少込み入っている。英語は「アルファベットの大文字・小文字+数字+
記号」の数十の文字で表現できるため文字コードは非常に単純であり、ASCII コードといわれる標
準がある。ASCII コードではすべての文字が 1byte(実は 7bit) で表現されており、計算機処理も楽
である。しかし、日本語は、漢字を含めると数千〜数万の文字を含むため、1byte(1〜256) では表
現できず 2byte 以上の情報が各文字に必要である。この場合、すべての文字を 2byte で表現すると
決めればそれほどややこしくはならないのであるが、ASCII コードの文書も世の中に溢れており、
ASCII コードとの互換性を確保しなければならないところに問題がある。ここで言う互換性とは、
ある日本語コードを処理するシステム(例えばエディター)があった場合、入力として ASCII コー
ドだけで作られた文書を与えても、日本語の文書を与えても、あるいは 2 つが混ざった文書を与え
ても同じように動作する性質のことである。互換性がない場合、ASCII テキストと日本語の文書を
なんらかの付加的な情報で区別する必要が生じ、ユーザに負担を強いる。このため、日本語コード
というのは、あくまでも ASCII コードの拡張であり、ASCII テキストもその日本語コード体系の
一部としなければならない。このため、一般に使われている日本語コードは 1byte の ASCII コー
ドと日本語用の 2byte のコードが 1 つのファイル内に混在することを許す。このため、日本語コー
19
ドはややこしくなる。
まずは文字の区別に関係する以下の 4 つの言葉を定義する。
字形
文字の具体的な形。実際書かかれた一つ一つの文字。同じ「あ」でも手書きの場合完全に
まったく同じ字形では 2 度と書けない。
文字
同じ意味や形状を持つとされる個別字形の集合を指す抽象概念
字体
文字と同様、個別字形の集合を指す抽象概念であるが、文字より細かい単位。例えば、
「学」
は「學」を簡単化した文字であり歴史的には同じ文字であるが、後者を「旧字体」と呼び区
別している。旧字体を使う場面もあるからである。文字は同じで字体の異なる字体を「異字
体」と呼ぶ。
書体
字形または字形の集まりの装飾的な様式
• 明朝体, ゴシック体
• セリフ, サンセリフ
やや抽象的で分かりにくいかもしれないが、ようするに「文字」というのは、きれいに書いても、
汚く書いても同じ文字は同じであり、また書体が異なっても(丸文字、楷書体等)同じ文字は同じ
である。そういう意味で、
「文字」は「具体的」に定義することは困難であるため、
「抽象的」な定
義であるということになる。
文字コード規格では、この 4 つ中で「字体」のレベルで文字を区別する。このため、文字コード
規格における「文字」とは「字体」を指すと考えてよい。実際の文字コード規格は次の 2 つに分け
ることができる。
文字集合
文字の集合を定義したもの. 正確には「符号化文字集合」あるいは「電子化文字集合」
と呼ぶ。
符号化方式
文字集合を計算機上で表現する符号 (bit パターン) の定義
文字集合は使用する文字の種類をある範囲に限定するという点が重要である。実際の{0,1}の表現
を規定する符号化方式が文字の総数に大きく影響されるからである。たとえば、英語のように「ア
ルファベットの大文字・小文字+数字+記号」の数十の文字ですむ言語では、各文字 1byte(8bit)
あれば十分であるが、日本語のように数千〜数万の文字を含む場合は 1byte では表現できないの
で、1 文字を表現する bit 長そのものが違ってくる。さらに、日本語用漢字の中のどの漢字を許容
するか、その総数で bit 長が変化する。計算機処理とは独立に、日本では常用漢字 1945 文字が定義
されており、新聞等は原則常用漢字しか漢字としてはいない。ただ、あくまでも原則であり、実際
の新聞では 5,000 種類くらいの文字を使用している。また、さまざまな計算機応用を考えた場合、
常用漢字では足りない場面も多い。日本では、JIS(日本工業規格)でいくつかの文字集合が定義さ
れれている。以下の 6 つが最も重要であろう。
• ASCII/JIS ローマ字 (アルファベット+記号+制御文字)
• 半角片仮名
20
表 4 3 つの日本語符号化方式の特徴
符号化方式
文字集合切り替え方式
特徴
欠点
JIS コード
文字集合を切り替えるた
各バイト 7bit で表現
同じ文字列に対して
めのコードを挿入する
できる
異なる符号化があり
える
SJIS
最初の1バイトで文字集
半 角 片 仮 名 を 1byte
JIS X 0212 を 使 え
合の種類が決定される
で表現可能
ない (JIS X 0213 は
OK)。文字集合の判別
が EUC に比べて少し
厄介。
EUC
最初の1バイトで文字集
文字集合の判別が容
半 角 片 仮 名 が 2byte
合の種類が決定される
易
表現となる
• JIS X 0208 (漢字 6355 文字, その他(仮名, アルファベット, 記号等)524 文字)
• JIS X 0212 (補助漢字文字集合 6067 文字)
• JIS X 0213 (上記の 0208 と 0212 を包含する最新規格)
• JIS X 0221 (ISO 10646 の日本版。いわゆる Unicode を含む規格)
この中で、我々が日常使っている文字は ASCII/JIS ローマ字と JIS X 0208 の一部(第一水準漢
字 2965 文字のさらに一部)であろう。
「半角片仮名」は計算機の記憶能力が低く漢字が表現できな
かったころ(片仮名だけでも処理しようとした)の名残りである(が、一部ではいまだ使われてい
る)。最近では、ISO 10646 あるいは Unicode と呼ばれる世界の主要な言語で使用される文字を1
つの文字集合で規定しようという試みがあり、急速に普及しつつある。
符号化方式は上記の文字集合の1つ、あるいは複数の文字集合を符号化({0,1}列で表現)する
方式である。日本では、以下の 4 つが多く使われている。
• JIS コード
• Shift JIS コード(略称 SJIS)
• Extended Unix Code (略称 EUC)
• UCS (または Unicode) Transfomation Format (略称 UTF. 具体的には UTF-8, UTF-16,
UTF-32 の 3 種類がある)
最初の 3 つの符号化方式は複数の文字集合に対応しているが、最後の UTF は ISO 10647(いわゆ
る Unicode) 専用である。最初の 3 つは複数の文字集合をどのようにして切り替えるかが方式の主
な違いになっている。特徴を表 4 に示す。 EUC の特徴である文字集合の判別が容易という利点
は、日本語を処理するプログラムが書きやすいことを意味しており、この利点によって EUC はプ
21
ログラムの日本語テキストの内部表現として利用されることが多い。最終的に JIS や SJIS の符号
化方式を扱わなければならない場合も、入出力の時点で EUC に変換し、内部では EUC を用いて
いる場合も多い。本実験でも、EUC を用いることとする。EUC を使って課題 2 以降を行うための
基本的な事項を課題 1 によって把握してもらう。EUC の詳細、または日本語コードに関する包括
的な解説は、最後の章で挙げた Ken Lunde または矢野啓介の本を参照のこと。
3.1.1
EUC
EUC はもともと日本語の文字だけのための符号化方式ではない。言語を問わず、複数の文字集
合を符号化するために開発された枠組みである。さらに、文字集合を切り替えるために複数の方式
を含んでいるため、EUC の仕様全体はかなり大きなものである。全体の話は本実験の範囲を越え
るので、本実験に関係する部分のみに限り以下に解説する。
EUC のいくつかの符号化方式のなかで、日本語に対して主に用いられるのは「EUC 圧縮フォー
マット」と呼ばれるものである。このフォーマットでは、日本語の標準的な文字集合の4つをすべ
て表現できる。また、このフォーマットは可変長の符号であり、各文字集合中の文字を次のような
バイト数で表現する。
ASCII/JIS ローマ字: 1byte
半角片仮名:
2byte
JIS X 0208 2byte
JIS X 0212 3byte
しかし、半角片仮名と JIS X 0212 の文字が使われることはまれなので、本実験では ASCII/JIS
ローマ字と JIS X 0208 の文字集合に限ることとする。この2つの文字集合に限ると、EUC 圧
縮フォーマットでは、各文字は 1byte か 2byte かのどちらかで表現されており、1byte の場合は
ASCII/JIS ローマ字、2byte の場合は JIS X 0208 の文字を表していることになる。
テキストを計算機処理する場合、テキストの先頭から 1byte づつ処理することが基本であるが、
EUC 圧縮フォーマットでは1つの文字が可変長 byte で表現されるため、1文字に相当する符号の
切れ目を認識することが最初に必要となる。先頭、あるいはある文字符号の最後を認識した直後の
1byte は、次の2通りのいずれかである。
• 1byte 文字
• 2byte 文字の 1byte 目の符号
EUC 圧縮フォーマットでは、1byte の数値を符号なしの 8bit 整数と見なした場合、次のようにし
て判別できる。
• 1byte の値が 127 以下 (すなわち先頭 bit が 0) の場合:1byte 文字
• 1byte の値が 128 以上 (すなわち先頭 bit が 1) の場合:2byte 文字の 1byte 目
すなわち、先頭 bit が 0 であるか 1 であるかによって判別できる。先頭 bit が 1 である場合は、次
22
表 5 文字種による EUC 圧縮フォーマットのコード範囲 (16 進数)
文字種
1byte 目
2byte 目
記号
a1 or a2
a1-fe
数字
a3
bo-b9
アルファベット
a3
c1-fa
平仮名
a4
a1-f3
片仮名
a5
a1-f6
ギリシャ文字
a6
a1-d8
キリル文字
a7
a1-f1
罫線
a8
a1-c0
漢字
b0-f4
a1-fe
表 6 EUC 圧縮フォーマットの記号に対するコード (16 進数)
記号
1byte 目
2byte 目
スペース
a1
a1
、
a1
a2
。
a1
a3
,
a1
a4
.
a1
a5
?
a1
a9
!
a1
aa
ー
a1
bc
の 1byte を含む 2byte で JIS X 0208 文字集合中の1文字を表す。
以上の条件を満たすために、EUC 圧縮フォーマットでは JIS X 0208 の文字は 2byte で各 byte
共に 128 以上となるように設計されている。また、ASCII コードほ 127 以下である。すなわち、
全体を見渡したとき、127 以下の byte はすべて 1byte の ASCII/JIS ローマ字1つを表しており、
128 以上の byte は前後のいずれかの 1byte と組み合わさって 2byte の文字を表現する。
JIS X 0208 文字集合中の文字の種類による各 byte の数値範囲を表 5 に示す。また、重要な記号
については、具体的な数値を表 6 に示す。
例えば、各バイトが次のような値のシーケンスはその下に書いてあるような文字に対応する。1
行目が 16 進数で、2行目が 10 進数、3行目が各部分に対応する文字である。
23
16 進
a4
b3
a4
ec
a4
cf
45
55
43
c9
e4
b9
e6
10 進
164
179
164
236
164
207
69
85
67
201
228
185
230
E
U
C
文字列
4
こ
れ
は
符
号
課題
本実験では、日本語テキストを入力し、単語分割を行うシステムを複数構築する。前節ではかな
りたくさんの手法を説明したが、作成するのは以下の方法のみでよい。
• 字種による分割
• 最長一致法
• 単語コスト最小法
課題は 5 つに別れており、各課題の概要は以下である。
課題1 日本語文字コード (EUC):
まずは、日本語のテキストが計算機内でどのように表現され
いるかを学ぶ。特に、最も計算機プログラムが書きやすいと言われる EUC コードの処理方
法を学習する。
課題2 字種による分割
EUC コードの知識を利用して、簡単な日本語分割システムを作成する。
さらに、日本語単語から英単語に変換する簡単なプログラムを与えるので、それを利用して
簡単な日英辞書引きソフトを作成する。
課題3 最長一致法
辞書を用いた単語分割システムを作成し、課題2で作成した辞書引きソフト
を改良する。辞書を読み込んで、単語を引くプログラムの作成が中心である。辞書の内容は
与える。
課題4 単語コスト最小法
Viterbi アルゴリズムを用いた、単語コスト最小法による単語分割シス
テムを作成する。
課題5 高速化
課題4で作成した形態素解析システムの辞書のデータ構造を改良することにより
高速化する。データ構造は何を用いてもよい。(最終的に長めのテキストで他の学生と速度
比較 (競争) を行う)
4.1
課題 1: 日本語文字コード:EUC
課題1は、日本語のテキストを文字に分割し、各文字の種類を出力するプログラムを書くことで
EUC コードに慣れる*4 。
*4
多少難易度が上がるが UTF-8 などの Unicode で実験を進めることも可能である。UTF-8 で実験を進めたい場合は
担当教員に相談してください。
24
4.1.1 課題 1-1: 文字切り出しプログラムの理解
入力テキストを標準入力から読み込み、文字に分割し、1行に1文字を標準出力に出力するプロ
グラムを解説する。改行文字は出力しないようにしている。例えば、次のような入力:
このコードは CR
EUC 符号。
に対して、次のような出力を出すようにすればよい。 CR は改行文字であるので無視される。
こ
の
コ
ー
ド
は
E
U
C
符
号
。
手始めに、標準入力から読み込んだテキストを 1byte ごとにその値の 10 進と 16 進の数値を出
力するプログラムを示す*5 。いろいろなテキストの実際の数値を見てみると EUC コードの実際が
よく分かるかもしれない。
各バイトの数値を表示するプログラム
#include <stdio.h>
main(){
int c;
while ((c = getchar()) != EOF) {
printf("%5d %5x\n", c, c);
}
*5
}
プログラムはここ! /home/prof/myama/jikken/nlplab/src/dechex.c
25
この例では、各バイトは getchar() で読み込まれる。この際注意するのは、getchar() の返り
値は int 型である点である。char 型がデフォルトで signed である機種 (Mac の unix である
Darwin 等) では、char 型は-128〜+127 の値しか表現できない。このため、getchar() の返り値
を char 型に入れると、128 以上の値が負の値と解釈される (先頭 bit が 1 であるため) *6 。
[以下、横線に挟まれた部分は、非常に細かい話なので、興味がなければ飛ばしてよい。]
明示的に unsigned char 型を使えば、表現できる範囲は 0〜+255 までとなるので getchar()
の返り値を代入しても int 型に代入したときと同じ結果が得られる (0 以上の値)。いずれにせよ、
代入されるビット列としては unsigned char でも singed char でも同じである。ただ、型が異
なるとその同じビット列を数値として解釈する場合に違いが生じるのである。文字列として見た場
合はどちらでも同じであり、特に問題は生じない。
この様子をよく理解するためには、以下のプログラムに EUC のテキストを入力するとよい。
getchar() が読み込んだ値の解釈の違いが分かるであろう。
変数の型の違いに解釈の違いを表示するプログラム
#include <stdio.h>
main(){
char c;
int
ic;
unsigned char uc;
while ((ic = getchar()) != EOF) {
c = ic;
uc = ic;
printf("int:%3d, char:%3d, unsigned_char:%3d\n", ic, c, uc);
}
}
繰り返すが、この違いは{0,1}パターンの違いによるものではない。すべての型で (int 型は最後の
1byte) 内容は同じであるが、数値としての解釈が異なるだけである。
課題 1-1 のプログラム例を以下に示す*7 。getchar() で読み込んだ各 byte を putchar(c) でど
んどん出力していきながら、必要なところにリターンコード'\n' を挿入する方法もあるが、今回
は以後の課題への応用も考えて、一旦 char 型の配列上で、1文字からなる文字列を構成してから
出力する方法をとった。
*6
ただし、char 型のデフォルトが unsigned であるような OS(IRIX 等) では、char 型が 0〜+255 の値を格納でき、
getchar() の返り値を char 型の変数に代入しても同じ結果になる。自宅の計算機で本実験を行う場合は、char 型
がデフォルトで signed なのか unsigned なのかをチェックした方がよい。
*7 プログラムはここ! /home/prof/myama/jikken/nlplab/src/chseg.c
26
EUC 圧縮フォーマットテキストの文字を切り出すプログラム
#include <stdio.h>
#include <stdlib.h>
main(){
int
c;
char euc_ch[3];
while ((c = getchar()) != EOF) {
if(c >= 128) {
euc_ch[0] = c;
if((c = getchar()) != EOF){
euc_ch[1] = c;
euc_ch[2] = '\0'; /* C 言語における文字列の終端を表す文字 */
} else {
fprintf(stderr, "Input text isn't encoded in EUC code.\n");
exit(2);
}
} else if(c == '\n') {
continue;
} else {
euc_ch[0] = c;
euc_ch[1] = '\0';
}
printf("%s\n", euc_ch);
}
}
このプログラムを十分理解することが、課題 1-1 である。
最後に入力テキストが EUC コードになっているかどうか分からないときに EUC コードのテキ
ストに変換するコマンド nkf の使い方の例を示す。
未知の符号化方式のファイル text を EUC コードのテキスト text.euc へ変換:
% nkf -e < text > text.euc
未知の符号化方式のファイル text を上記で作成したプログラムに入力する:
% nkf -e < text | command
27
4.1.2 課題 1-2: 文字種判別プログラムの作成
文字に区切るプログラムを拡張し、各文字に文字の種類を付与するプログラムを書け。文字種と
しては、以下の 9 種類を識別せよ。の中は出力するべき文字種の記号である。
• ASCII 文字(数字、アルファベット、記号を含む 1byte 文字)
• 平仮名
• 片仮名
• 横棒(‘ー’)
• 数字
• 2byte コードとしてのアルファベット
• 漢字
• 句読点(‘。’, ‘、’, ‘! ’, ‘? ’ の4種)
• その他
前節の入力「このコードは EUC 符号。
」に対しては、次のような出力となるようにせよ。
こ
平仮名
の
平仮名
コ
片仮名
ー
横棒
ド
片仮名
は
平仮名
E
ASCII
U
ASCII
C
ASCII
符
漢字
号
漢字
。
句読点
各文字・文字種の数値・数値の範囲は表 5,6 (23 ページ) を参考にせよ。
なお、例文は以下のところにたくさんあるので、テストに使用してよい。1 行目が EUC のファ
イル。2 行目は UTF-8 で符号化されているファイルである。
/home/prof/myama/jikken/nlplab/sentences-euc
/home/prof/myama/jikken/nlplab/sentences-utf8
1 文/ 1 ファイルとなっている。もちろん、自分で作成したメール等の文章も使ってみるとよい。
28
4.2
課題 2: 字種による単語分割
本課題では、字種が変化するところで分割する最も単純な分割プログラムを作成し、日英辞書引
きコマンドを通して、第1バージョンの自動日英辞書引きシステムを作成する。
4.2.1 課題 2-1: 字種による単語分割プログラムの作成
課題 1-2 で作成した文字切り出し・字種付与プログラムを改造して字種が変化するところを単語
境界と仮定する単語分割プログラムを作成せよ。字種としては、課題 1-2 で用いた 9 種類を用いれ
ば十分であるが、自分で工夫してより細かい字種を判別するようにしてもよい。横棒 ‘ー’ は片仮名
の一部とした方がよいことに注意せよ。例えば、
サッカー、サーキット、エルニーニョ、サンドペーパー、...
のように、片仮名の名詞の中に横棒はよく現れる。この他、平仮名列の中にも現れることがあるの
で(例えば、
「えーと」等)
、考慮した方がよいかもしれない。結果を見て、簡単に回避できる誤り
があれば、プログラムを改良するとよいだろう。
入出力は課題 1 と同様、標準入出力とする。出力は、分割された単語を1行ごとに改行されてい
ること。例えば、入力「このコードは EUC 符号。
」に対しては、次のような出力となるようにせよ。
この
コード
は
EUC
符号
。
4.2.2 課題 2-2: 日英自動辞書引きシステム version 1
各行の文字列を単語とみなして日英辞書を引き、その結果を出力するコマンド ‘jedic’ を使用で
きるようにするために、以下のディレクトリをコマンド・パスに追加せよ。
/home/prof/myama/jikken/nlplab/bin
課題 2-1 で作成した分割プログラムとパイプで接続することにより、以下のような出力が得られ
る。課題 2-1 で作成したプログラムが ckseg という名前で、ファイル text.txt に日本語テキスト
「このコードは EUC 符号。
」が入っているものとする。
29
% nkf -e text.txt | ckseg | jedic
この
コード
/code/cord/chord/
は
EUC
符号
/sign/mark/symbol/
。
このように、jedic コマンドは、もとの日本語と英語をペアで各行に表示するが、辞書にない単語
には英語を出力しないようになっている。jedic が引く単語のリストは、以下の場所にある。
/home/prof/myama/jikken/nlplab/dic/jedic.txt
この辞書は、James Willian Breen(オーストラリア)が中心になって活動している EDICT プロジェ
クトの成果であり、フリーで公開されている*8 。2000 年 1 月現在のエントリー数 (Ver.V00-001)
は、68,264 単語(日本語)である。
作成されたプログラムを shell script でまとめて1つのコマンドとして利用できるようにした方
がテストが楽である。このコマンドを用いて、様々なテキストの辞書引きを行い、問題点を把握せ
よ。特に、動詞については字種による分割ではほとんどうまくいかないと予想されるが、実際はど
うか?
4.3
課題 3: 最長一致法
課題 3 では、課題 2 の単語分割プログラムを最長一致法による分割プログラムに変更し、辞書
引き性能を改善する。本格的な日本語の単語辞書を使うことによって、以下のような改善が見込ま
れる。
• 長い漢字だけの名詞をより短い単語に分割できるため、日英辞書のエントリーにある可能性
が高まる。例えば、
「自然言語処理」は全体では辞書に掲載されている可能性は低いが、
「自
然|言語|処理」と分割できれば、それぞれの単語は辞書のエントリーとして存在する可能性
が高まる。
• 動詞を基本形にもどすことにより、動詞に対する辞書引き性能を向上できる。
4.3.1 課題 3-1: 日本語辞書引きプログラムの作成
以下のファイルは日本語の辞書として用意したものである。
*8
http://www.dgs.monash.edu.au/~jwb/edict.html
30
/home/prof/myama/jikken/nlplab/dic/jdic.txt
この辞書を用いて、日本語テキストに現れる表記から、「読み、品詞、基本形」のの情報を返す関
数を作成せよ。jdic.txt の内容は以下のようになっている。
これ
コレ
これ
名詞-代名詞-7.05215
一般
符号
フゴー
符号
名詞-一般
14.0819
家
イエ/ウチ
家
名詞-一般
7.88928
送り
オクリ
送る
動詞-自立
10.7799
は
ワ
は
助詞-係助詞 3.56106
。
。
。
記号-句点
3.27649
左から、
「表記、読み、基本形、品詞、単語コスト」の順序である。各カラムはタブ ('\t') で区切
られている。品詞中の ‘-’ の後に記述してあるものは、品詞の細分類と呼ばれるもので、コスト最
小法で細かな接続コストを設定するときに用いられる。本課題では、品詞は単なる文字列と見なし
て、そのまま出力すればよい。また、残念ながら、この辞書には ASCII 文字の単語が登録されて
いないので、入力に ASCII コード文字が入っていると単語が見つからない。入力には ASCII コー
ドの文字が入っていないと仮定してよい。
辞書を計算機上で実現するためにはさまざまな方法が考えられるが、課題 3-1 では、線形探索を
用いたプログラム (プログラム例を与えるができるだけ見ない方がよい) と二分探索を用いたプロ
グラムの 2 種類を実現せよ (次の課題 3-2 で、探索方法による実行時間の違いを測定する)。
4.3.2 課題 3-2: 最長一致法による単語分割プログラムの作成
課題 3-1 で作成した関数を用いて、最長一致法の単語分割プログラムを作成せよ。出力は分割さ
れた単語に対応する辞書の情報をすべて出力せよ。すなわち、
「表記、読み、基本形、品詞」を各単
語に関して出力する。例えば、
「これは符号。
」に対して、次のような出力が期待される。
これ
コレ
これ
名詞-代名詞-一般
は
ワ
は
助詞-係助詞
符号
フゴー
符号
名詞-一般
。
。
。
記号-句点
実現において問題となるのは、ある文字位置まで分割が進んだときに、日本語辞書の中に後続す
る単語が一つも見つけられない場合である。2つの対処法がある。
1. 1文字スキップする。後続する1文字を単語と見なして、出力し、その先を進める。
2. 前の単語分割が誤りであったと考え、1つ前の単語分割を取り消す。全体をうまく分割でき
るまで、この取り消しを再帰的に行う。これは、人工知能の分野でバックトラックと呼ばれ
31
る手法に相当する。これを実現するには、1単語の分割を1つの C 言語の関数として実現
し、この関数の再帰的な呼び出しによって全体を分割するように作れば、簡単に作成できる。
ここでは、(1) の1文字スキップする実現で十分である。単語コスト最小法による課題 4 のプログ
ラムは (2) の方法を包含するためである。
さらに、課題 3-1 で作成した 2 つの辞書引き関数 (線形探索と二分探索) で、どの程度実行速度
が異なるか比較せよ。
4.3.3 課題 3-3: 日英自動辞書引きシステム version 2
課題 3-2 で作成した単語分割プログラムを用いて、辞書引きシステム ver.2 を作成せよ。jedic コ
マンドは、オプション ‘-n’(ここで n は数値)を取り、入力行の n 番めの文字列を辞書に探しに行
く。「表記、読み、基本形、品詞」の順で出力されている場合、
「基本形」で辞書引きするのがよい
ので、この場合、‘-3’ というオプションを付ける。デフォルトは ‘-1’ である。
辞書引き結果を観察し、字種による分割による方法から改善された点をまとめよ。
4.4
課題 4: 単語コスト最小法
4.4.1 課題 4-1: 単語コスト最小法による単語分割プログラムの作成
単語コスト最小法による単語分割プログラムを作成せよ。単語コストとしては、与えた辞書の中
にある値 (右端の数値) を用いよ。2.5 節で述べた Viterbi アルゴリズムを使用せよ。
4.4.2 課題 4-2: 日英自動辞書引きシステム version 3
課題 4-1 で作成した単語分割プログラムを用いて、辞書引きシステム ver.3 を作成せよ。辞書引
き結果を観察し、性能を評価せよ。
4.5
課題 5: 高速化
単語コスト最小法に基づくシステムの辞書データ構造を改良して高速化せよ(辞書引きシステム
の実験はしなくてよい)。辞書のデータ構造としては何を用いてもよい。課題 4 のシステムとある
程度長いテキストを用いて、処理速度を比較せよ。処理速度を理論的に解析してレポートに書くと
評価が高くなる。また、同一の長めのテキストを用いて、他の人と処理速度の比較をする予定で
ある。
データ構造としては、2年生の授業「データ構造とアルゴリズム」および実習や 3 年生の授業
「知識・自然言語処理」あるいは「自然言語処理」で習得したデータ探索手法を使うとよいだろう。
例えば、「ハッシュ法」,「トライ」, 「パトリシア木」は、いずれも辞書の実現に使用できる(が、
二分探索より早くなるかどうかはやってみないと分からない)
。
32
5
文献案内
以下に、本実験および自然言語処理に関する理解を深めるための参考文献を挙げる。自然言語処
理に関してはたくさんの本があるが、カテゴリーごとに代表的と思うものを日本語と英語の本をそ
れぞれ一冊ずつ挙げた。
自然言語処理一般について:以下の本が代表的な入門書である(形態素解析を含む)
。
長尾真編. 1996. 「自然言語処理」, 岩波書店.
C.D.Manning and H. Schutze. 1999. “Foundations of Statistical Natural Language
Processing.”
日本語文字コードに関して以下の本が包括的である。
矢野啓介. 2010. 「プログラマのための文字コード技術入門」, 技術評論社.
Ken Lunde. 2008. “CJKV Information Processing, 2nd Ed.,” O’Reilly & Associates,
Inc.
日本語および品詞体系について:
益岡隆志, 田窪行則. 1992.「基礎日本語文法」, くろしお出版.
Masayoshi Shibatani. 1990. “The Language of Japan,” Cambridge University Press.
辞書構造とデータ探索を含むデータ構造とアルゴリズムの入門書:
A.V. エイホ, J.E. ホップクロフト, J.D. ウルマン. 1987. 「データ構造とアルゴリズム」,
培風館.
T.H.Cormen, C.E.Leiserson and R.L.Rivest. 2009. “Introduction to Algorithms, 3rd
Ed.,” MIT Press.
33