Speech and Language Processing: ノート1(2章の一部)

以下の本を読みます。キャラクターは架空のものです。解釈の誤りは筆者に帰属します。お気付きの点がありましたらコメントでご指摘いただけますと幸いです。

まとめ(ていない)

  • 2章では正規表現、正規化、編集距離―「テキストデータをどこで区切るべきか」や「区切ったらどの単語とどの単語を同じと扱うべきか」を与える確立されたツールが紹介されている。

f:id:cookie-box:20200101101603p:plain:w60

スピーチと言語の処理、ですか。28章もあるんですね…。1章のイントロダクションはまだ執筆されていませんね。2章は Regular Expressions, Text Normalization, Edit Distance ということですが…冒頭にユーザと ELIZA さんという方との会話が出てきますね。…この ELIZA さんあたりがきつくないですか? 「助けがほしい」という人に「助けられたら何だというのか」といいますか??

f:id:cookie-box:20200101101614p:plain:w60

イライザっていうみたいだね。実に1966年につくられた自動応答システムらしい。

パターンマッチを活用して心理療法士の会話を模倣していて、例えば「X が必要だ」といわれたら「X を得られたらあなたにとってどんな意味がありますか?」というように返すみたい。そんな単純な仕組みにもかかわらず、イライザと対話した人々は本当にイライザが自分で考えて応答しているのだと信じ込んだというようにあるね。教科書にもウィキペディアにも。

f:id:cookie-box:20200101101603p:plain:w60

ええ…イライザさんは心理療法士ボットだったんですか…? 心の治療に行ったのに逆にストレスを負いそうな…。

f:id:cookie-box:20200101101614p:plain:w60

どうかな、「やせなければならない」という強迫観念をもっている人には「やせたらどうなりますか?」と訊いて治療していくんじゃないのかな。知らないけど。

f:id:cookie-box:20200101101603p:plain:w60

そんなものですかね…なるほど、この導入で正規表現と正規化が出てくるんですね。正規化はより細かくみるとトークン化、レンマ化(単純なレンマ化の方法の一つがステミング)、文区切りなどの手続きが含まれているということですが…イライザさんの例では、いわれたことに応答するという目的を達成するために、「X が必要だ」というパターンに合致するかどうかを判定しますが、きっと(正規化と)正規表現を利用していますよね。自然言語処理って細かいプラクティスが多くて面倒そうですね…。

f:id:cookie-box:20200101101614p:plain:w60

裏を返せばそれだけデータの構造と意味を予め知っているからなんじゃないかな。もしテキストデータをそのまま数値(ASCIIコードか何か)の配列として扱って「入力されたテキストに対する心理療法士の応答」を学ぼうとするときっと途方もなく効率が悪いけど、実際の会話にかなり構造のパターンがあることがわかっていれば1960年代でも人間らしいボットが実現できるんだから。この頃の画像認識とかなんて人間っぽく何の画像か色々見分けたりはできなかったんじゃないの? 知らないけど。

f:id:cookie-box:20200101101603p:plain:w60

さっきから教科書への相槌が雑ですね…それに、テキストデータへの理解には分があるといってもそれ以上にタスクも曖昧で困難なものが求められがちじゃないんですか? 質問に答えるとか翻訳するとか…。そういえば、アラビア語って活用形が多いんですか? レンマ化のくだりに morphologically complex languages like Arabic とありますが。

f:id:cookie-box:20200101101614p:plain:w60

ウィキペディアをみると、「現代アラビア語では、派生形は基本的に第10形までしかなく、古典アラビア語が持つ第15形までの派生形はほとんど用いられない」ってあるよ。

f:id:cookie-box:20200101101603p:plain:w60

いや、「10個しかないので大丈夫ですよ」みたいにいわれても…。導入部の最後に編集距離(Edit Distance)というのが出てきますが、スペルミス検出とかスピーチ認識において同一のものを指す単語の検出とかにつかうんですね。ある文字列を別の文字列にするのに何回の挿入、削除、置換を要したかというようなものなのでしょうか。…これって最短経路を求めるの大変なんじゃないですか?

f:id:cookie-box:20200101101614p:plain:w60

うん、2.5.1節に "The space of all possible edits is enormous, so we can’t search naively." とあるね。だから、動的計画法(具体的にはビタビアルゴリズムやCKYアルゴリズムなど)で解いたりするってある。

f:id:cookie-box:20200101101603p:plain:w60

ビタビアルゴリズム放送大学教材の「自然言語処理」で名前は目にしたことがあります。39ページです。しかし、このときは編集距離という文脈ではなかったような…日本語の文章の形態素解析で複数の解釈がありえる場合がありますよね。教科書の例が以下です。以下のように列挙すると4パターンありますが(単純な例なので少ないですが実際にはとても多い)、途中の「元気」まで、「ね(根)‐たら(鱈)」を経由するのと「ねたら(寝たら)」を経由するのではコスト(ノードやエッジに対して適切に定義してください)が小さいのは後者なのでこの時点で「ね(根)‐たら(鱈)」の可能性は刈り取れるということです。

  • 《文頭》‐ね(根)‐たら(鱈)‐元気‐に‐なった‐《文末》
  • 《文頭》‐ねたら(寝たら)‐元気‐に‐なった‐《文末》 ※ 正解
  • 《文頭》‐ね(根)‐たら(鱈)‐元気‐になった(担った)‐《文末》
  • 《文頭》‐ねたら(寝たら)‐元気‐になった(担った)‐《文末》
…しかし、これはスペルミス検出ではないですよね?

f:id:cookie-box:20200101101614p:plain:w60

うん、こちらの教科書で Viterbi Algorithm が出てくる8.4.5節でもむしろその放送大学の教科書の例に近い品詞タグ付けタスクの例で説明されている。編集距離に沿った説明については2.5.1節のつづきを読んだ方がよさそう…2.5.1節で最初に出てくる数式がその方法かな。この数式に具体的に X="inten" と Y="execu" をあてはめるとこうだね。

  • 「inten を execu にする最小コスト」は、以下のうち最小のものである。
    • 【1】「n を削除するコスト」+「inte を execu にする最小コスト」
    • 【2】「inten を exec にする最小コスト」+「u を挿入するコスト」
    • 【3】「inte を exec にする最小コスト」+「n を u に置換するコスト」
文字列から文字列への最短経路(最小コスト)を求める問題を、より短い文字列から文字列への最小コストを求める問題にできている。これを利用すればどんどん短い文字列に対する問題にできる。

f:id:cookie-box:20200101101603p:plain:w60

え、うーん、わかるような気はしますが…場合分けってそれでMECEになっているんですか?

f:id:cookie-box:20200101101614p:plain:w60

文字列を文字を参照しているポインタの配列と考えるとわかりやすいかも。つまり、「削除」「挿入」「置換」を、「削除」は配列からポインタを取り除くこと、「挿入」は新しいポインタを配列のどこかに挿入すること、「置換」はポインタの参照先の文字を書き換えることって考える。このとき、以下の場合分けはMECEだよね。

  • n 文字の元文字列を m 文字の新文字列にするような操作は、以下の3パターンに場合分けできる。
    • 元文字列の n 番目のポインタが取り除かれる。
    • 元文字列の n 番目のポインタが新文字列において m-1 番目までにいる。
    • 元文字列の n 番目のポインタが新文字列において m 番目にいる。
これと以下の事実を組み合わせればいい。
  • 元文字列の n 番目のポインタが取り除かれるならば、そのポインタがいつ取り除かれるにせよ、そのポインタが取り除かれる操作を除いた残りの操作は「元文字列の n-1 文字目までを新文字列の m 文字目までにする」操作になっている。
  • 新文字列において元文字列の n 番目のポインタが生き残っていて、かつ、それより後ろにポインタがあるならば、それらのポインタは全て挿入されたものである(「削除」「挿入」「置換」は元文字列のポインタの順序を変えない)。

f:id:cookie-box:20200101101603p:plain:w60

その説明で伝わりますかね…? とにかく、intention から execution へのレーベンシュタイン距離(編集距離であって、コストの定義は削除コストが1、挿入コストが1、異なる文字への置換コストが2)を求めてみましょう。面倒なので、intent から execut へのレーベンシュタイン距離を測ります。同じなので。先に図2.16から正解を確認しておくと、8ですね。delete が1回、substitute が3回、insert が1回ですから。では D("intent", "execut") を求めていきますが、レーベンシュタイン距離では異なる文字への置換は挿入+削除としてもコストが同じなので、同じ文字への置換でない限り上の【3】を無視してかいています。

  • D("inte", "e") = 3 ①②③
  • D("inte", "ex") = min{ 1+D("int", "ex")=6, D("inte", "e")+1=4 } = 4
  • D("inte", "exe") = min{ 1+D("int", "exe")=7, D("inte", "ex")+1=5 } = 5
  • D("inte", "exec") = min{ 1+D("int", "exec")=8, D("inte", "exe")+1=6 } = 6
  • D("inte", "execu") = min{ 1+D("int", "execu")=9, D("inte", "exec")+1=7 } = 7
  • D("inte", "execut") = min{ 1+D("int", "execut")=8, D("inte", "execu")+1=8 } = 8
  • D("inten", "ex") = min{ 1+D("inte", "ex")=5, D("inten", "e")+1=5 } = 5
  • D("inten", "exe") = min{ 1+D("inte", "exe")=6, D("inten", "ex")+1=6 } = 6
  • D("inten", "exec") = min{ 1+D("inte", "exec")=7, D("inten", "exe")+1=7 } = 7
  • D("inten", "execu") = min{ 1+D("inte", "execu")=8, D("inten", "exec")+1=8 } = 8
  • D("inten", "execut") = min{ 1+D("inte", "execut")=9, D("inten", "execu")+1=9 } = 9
  • D("intent", "ex") = min{ 1+D("inten", "ex")=6, D("intent", "e")+1=6 } = 6
  • D("intent", "exe") = min{ 1+D("inten", "exe")=7, D("intent", "ex")+1=7 } = 7
  • D("intent", "exec") = min{ 1+D("inten", "exec")=8, D("intent", "exe")+1=8 } = 8
  • D("intent", "execu") = min{ 1+D("inten", "execu")=9, D("intent", "exec")+1=9 } = 9
  • D("intent", "execut") = min{ 1+D("inten", "execut")=10, D("intent", "execu")+1=10, D("inten", "execu")=8 } = 8 ★ ここから出発
以上のようになります。D("intent", "execut") から最短経路を逆にたどってピンク色に塗りました。しかし、最短経路はこれ以外にもあります(削除や挿入の順序に自由度がありますから)。あくまでこの経路は一例です。この経路をかきくだすと以下のようになります。レーベンシュタイン距離は8です。
  • intention
    →(①②③ int を削除する)→ ention
    →(④ x を挿入する)→ xention
    →(⑤ e を挿入する)→ exention
    →(⑥ c を挿入する)→ execntion
    →(⑦ u を挿入する)→ execuntion
    →(⑧ n を削除する)→ execution

次回があればつづく