makenowjust-labs/blog

MakeNowJust Laboratory Tech Blog

L*やその派生アルゴリズムたち (Rivest-Schapire、Kearns-Vazirani)

2024-07-22 (更新: 2024-08-19) / 読むのにかかる時間: 約57.0

Angluinの LL^\ast はオートマトン学習 (automata learning) のアルゴリズムとして最も有名なものです。 以前、このブログでもAngluinの LL^\ast について説明しました。

LL^\ast は歴史のあるアルゴリズムのため、派生アルゴリズムがいくつか存在します。 この記事ではAngluinの LL^\ast について簡単に説明した上で、派生アルゴリズムとしてRivest-SchapireKearns-Vaziraniを紹介します。

  • Rivest-SchapireLL^\ast の反例 (counterexample) の解析手法の提案で、二分探索を用いて反例の中のobservation tableに追加すべき接頭辞を探すことで、計算量を改善します。
  • Kearns-Vaziraniはobservation tableを置き換えるデータ構造としてdiscrimination treeというデータ構造を提案するものです。

これらの手法により LL^\ast の様々な効率が改善できます。

想定読者: オートマトン理論やオートマトン学習について理解・関心がある。

準備

真偽値とは true,false\mathbf{true}, \mathbf{false} のことで、この2つからなる集合を B={true,false}\mathbb{B} = \{ \mathbf{true}, \mathbf{false} \} で表します。

Σ\Sigma をアルファベットとして固定し、その元を文字と呼びます。 文字を1列に並べたものを文字列と呼び、空文字列を ε\varepsilon で表します。 文字列全体からなる集合を Σ\Sigma^\ast で表します。 2つの文字列 w1,w2Σw_1, w_2 \in \Sigma^\ast の結合を w1w2w_1 \cdot w_2 で表しますが、単に w1w2w_1 w_2 と表すこともあります。

遷移関数 δ\deltaδ(q,ε)=q,δ(q,σw)=δ(δ(q,σ),w)\delta(q, \varepsilon) = q,\,\delta(q, \sigma w) = \delta(\delta(q, \sigma), w) (σΣ,wΣ\sigma \in \Sigma, w \in \Sigma^\ast)として文字列に拡張されます。 DFA A\mathcal{A} の出力関数 γ ⁣:Q×ΣB\gamma\colon Q \times \Sigma^\ast \to \mathbb{B}δ(q0,w)F\delta(q_0, w) \in F のとき true\mathbf{true}δ(q0,w)F\delta(q_0, w) \notin F のとき false\mathbf{false} を返す関数とします。

Angluinの LL^\ast

まず始めに、AngluinLL^\ast について簡単に説明します。

Angluinの LL^\ast[Angluin, 1987]で提案されたオートマトン学習 (automata learning) のアルゴリズムです。

LL^\ast は、学習対象の (正規) 言語について、次の2つのクエリーに応答できる MAT (minimally adequate teacher) と呼ばれる教師を与えることで、言語をDFAとして学習します。

  • MEMBER(w)\mathrm{MEMBER}(w) (membership query): ww が学習対象の言語に含まれるかどうかを真偽値で返す。
  • EQUIV(H)\mathrm{EQUIV}(\mathcal{H}) (equivalence query): 学習中の仮説 (hypothesis) のオートマトン H\mathcal{H} が学習対象の言語に等しいか判定する。等しい場合 true\mathbf{true} を返し、異なる場合はその反例となる文字列 (一方に含まれて一方に含まれないような文字列) を返す。

observation table

LL^\ast の重要なデータ構造であるobservation tableと、consistencyclosednessという概念について説明します。

補助的な関数として row ⁣:(S(SΣ))BE\mathrm{row}\colon (S \cup (S \cdot \Sigma)) \to \mathbb{B}^E を次のように定義します。

row(s)=eT(se)\mathrm{row}(s) = e \mapsto T(s \cdot e)

ここで eF(e)e \mapsto F(e)ee を引数とする関数を表します。

sSs \in S に対する row(s)\mathrm{row}(s) の値が、仮説 (対応するDFA) での状態に相当します。

observation tableは次のconsistencyclosednessという制約を満たしているとき、仮説 (対応するDFA) を考えることができます。

つまり、

  • consistencyは、row(s1)=row(s2)\mathrm{row}(s_1) = \mathrm{row}(s_2) なら2つの接頭辞 s1,s2Ss_1, s_2 \in S が表す状態は同じはずだが、1文字遷移した先が異なる、といった変な状態にはならない、ということを意味し、
  • closednessは、ss の遷移先に対応する接頭辞が SS に存在せず遷移できない、ということが起こらないことを意味します。

observation tableがconsistentかつclosedであるとき、対応する仮説 (hypothesis) のDFAが考えられます。

アルゴリズム

observation tableをconsistentかつclosedになるように更新して仮説 H\mathcal{H} を構築し、equivalence queryを投げて、返ってきた反例でobservation tableを修正する、ということを繰り返すのが LL^\ast の実際の流れになります。

[Angluin, 1987]の§2.2には疑似コードが掲載されています。 下のAlgorithm 1 (Angluin) は、その疑似コードをより具体的に書き直したものになります。

Algorithm 1 Angluin's LL^\ast

function Angluin(MEMBER,EQUIV\mathrm{MEMBER}, \mathrm{EQUIV})

S,E{ε}S, E \gets \{ \varepsilon \}

T(t)MEMBER(t)T(t) \gets \mathrm{MEMBER}(t) for each t{ε}Σt \in \{ \varepsilon \} \cup \Sigma // i.e. for each tS(SΣ)t \in S \cup (S \cdot \Sigma)

repeat

while (S,E,T)(S, E, T) is not consistent or not closed do

if (S,E,T)(S, E, T) is not consistent then

Find s1,s2Ss_1, s_2 \in S, σΣ\sigma \in \Sigma, and eEe \in E

\quads.t. row(s1)=row(s2)\mathrm{row}(s_1) = \mathrm{row}(s_2) and T(s1σe)T(s2σe)T(s_1 \cdot \sigma \cdot e) \ne T(s_2 \cdot \sigma \cdot e)

EE{σe}E \gets E \cup \{ \sigma \cdot e \}

T(tσe)MEMBER(tσe)T(t \cdot \sigma \cdot e) \gets \mathrm{MEMBER}(t \cdot \sigma \cdot e) for each tS(SΣ)t \in S \cup (S \cdot \Sigma)

end if

if (S,E,T)(S, E, T) is not closed then

Find sSs \in S and σΣ\sigma \in \Sigma

\quads.t. row(sσ)row(s)\mathrm{row}(s \cdot \sigma) \ne \mathrm{row}(s') for all sSs' \in S

SS{sσ}S \gets S \cup \{ s \cdot \sigma \}

T(sσd)MEMBER(sσd)T(s \cdot \sigma \cdot d) \gets \mathrm{MEMBER}(s \cdot \sigma \cdot d) for each dE(ΣE)d \in E \cup (\Sigma \cdot E)

end if

end while

// Now, (S,E,T)(S, E, T) is consistet and closed.

Let H\mathcal{H} be a hypothesis automaton for (S,E,T)(S, E, T)

if EQUIV(H)true\mathrm{EQUIV}(\mathcal{H}) \ne \mathbf{true} then

Let PP be a set of all prefixes of EQUIV(H)\mathrm{EQUIV}(\mathcal{H})

SSPS \gets S \cup P

T(te)MEMBER(te)T(t \cdot e) \gets \mathrm{MEMBER}(t \cdot e) for each tP(PΣ)t \in P \cup (P \cdot \Sigma) and eEe \in E

end if

until EQUIV(H)=true\mathrm{EQUIV}(\mathcal{H}) = \mathbf{true}

return H\mathcal{H}

end function

このアルゴリズムは正しく動作し停止します。 まず、一番外側の繰り返しの終了条件から、停止した場合、正しいオートマトンを返すのは明らかです。 そして、学習結果のオートマトンの状態数を nn とすると、1周毎に新しい row(s)\mathrm{row}(s) の値が1つは増えていくはずなので、高々 nn 回の繰り返しで停止するはずです。

次に計算効率として、MEMBER(w)\mathrm{MEMBER}(w) が呼ばれる回数を考えてみます。 MEMBER(w)\mathrm{MEMBER}(w)TT の値を更新する際に呼び出されるので、TT に記録される文字列の個数を数えれば良さそうです。 TT に記録される文字列は (S(SΣ))E(S \cup (S \cdot \Sigma)) \cdot E なので、SSEE がどのように更新されるのかに注目してみます。 ここで、EQUIV(H)\mathrm{EQUIV}(\mathcal{H}) の返す反例の長さの最大値を mm、アルファベットの大きさを kk とします。

  • SS はclosedでない場合に1つ値が追加され、EQUIV(H)\mathrm{EQUIV}(\mathcal{H}) は反例を返した場合に反例の接頭辞がすべて追加されます。 closedでない場合の追加は高々 n1n - 1 回起こり、反例は一度に最大 mm 個の追加が高々 n1n - 1 回起こります。 SS は最初に空文字列で初期化されているので +1+ 1 されて、S|S| は高々 n+m(n1)n + m (n - 1) と分かります。
  • EE はconsistentでない場合に1つ値が追加され、この追加はclosedでない場合と同様に高々 n1n - 1 回起こります。 E|E| も最初に空文字列で初期化されているので、E|E| は高々 nn と分かります。

よって、(S(SΣ))E|(S \cup (S \cdot \Sigma)) \cdot E| は高々 (k+1)(n+m(n1))n=O(kn2m)(k + 1) (n + m (n - 1)) n = O(k n^2 m) となります。

Rivest-Schapire

Rivest-Schapireは1993年にRivest2Schapireが発表した、Angluinの LL^\ast アルゴリズムの改良手法です。

[Rivest & Schapire, 1993]で発表されたもの3で、LL^\ast の反例を解析する部分に対する改良方法を提案しています。 というのも疑問2で提示したように、Angluinのアルゴリズムでは反例の全ての接頭辞をobservation tableへと追加していますが、これは明らかに無駄になります。 そこで、反例の接頭辞のうちobservation tableに追加すべきものを反例の解析によって見つけ、学習を効率的にしたのが、Rivest-Schapireになります。

反例の分析

それではRivest-Schapireで導入された反例の分析について説明していきます。

前述の通り、Angluinの LL^\ast アルゴリズムでは反例のすべての接頭辞が SS に追加されます。 しかしこの場合、同じ状態を表す (row(s)\mathrm{row}(s) の値が同じ) 接頭辞が SS に追加される可能性があります。 そのような接頭辞の SS への追加は無駄なので、ちゃんと新しい状態を表す接頭辞のみを SS に追加したいわけです。

例として L={an  n=0(mod4)}L = \{ a^n\ \mid\ n = 0 \pmod 4 \} を学習する場合を考えてみます。 このとき、最初の繰り返しでは E1={ε}E_1 = \{ \varepsilon \} で、仮説のオートマトン H1\mathcal{H}_1 とobservation tableの表 T1T_1 は次のようになります5

S(SΣ) / ES \cup (S \cdot \Sigma)\ /\ Eε\varepsilon
ε\varepsilontrue\color{Red}{\mathbf{true}}
aafalse\color{Blue}{\mathbf{false}}
aaa \cdot afalse\color{Blue}{\mathbf{false}}

これに対する反例の文字列は EQUIV(H1)=aaaa\mathrm{EQUIV}(\mathcal{H}_1) = aaaa となります。 この反例の接頭辞のうち、新しい状態を表すものを SS に追加しようと思うと、問題が生じます。 というのも、接尾辞の集合は E1={ε}E_1 = \{ \varepsilon \} なので、EQUIV(H1)\mathrm{EQUIV}(\mathcal{H}_1) のどの接頭辞を取っても既存の row(s)\mathrm{row}(s) と異なる値にはなりません。

そのため、反例の接頭辞を新しい状態を表すように追加する場合、EE に新しい接尾辞を追加する必要もあることが分かります。 この EE に追加する接尾辞も反例から取れると良さそうです。 そこで、反例の文字列を接頭辞と接尾辞に分解して、SSEE に追加することを考えます。

ここで、新しい状態が生まれる反例の接頭辞と接尾辞の追加の仕方は、次の2通りが考えられます。

  1. s=aa,e=aas = aa,\,e = aa
  2. s=aaa,e=as' = aaa,\,e' = a

しかし、ここで s=aaa,e=as' = aaa,\,e' = a を追加すると、SS がprefix-closedではなくなってしまいます。 そのため、追加できるのは s=aa,e=aas = aa,\,e = aa となります。 これらを追加した場合の仮説のオートマトン H2\mathcal{H}_2 とobservation tableの表 T2T_2 は次のようになります。

S(SΣ) / ES \cup (S \cdot \Sigma)\ /\ Eε\varepsilonaaaa
ε\varepsilontrue\color{Red}{\mathbf{true}}false\color{Blue}{\mathbf{false}}
aafalse\color{Blue}{\mathbf{false}}false\color{Blue}{\mathbf{false}}
aaaafalse\color{Blue}{\mathbf{false}}true\color{Red}{\mathbf{true}}
aaaaa \cdot afalse\color{Blue}{\mathbf{false}}false\color{Blue}{\mathbf{false}}

これに対する反例の文字列も EQUIV(H2)=aaaa\mathrm{EQUIV}(\mathcal{H}_2) = aaaa であり、今度は s=aaa,e=as' = aaa,\,e' = a を追加できます。 最終的に、仮説のオートマトン H3\mathcal{H}_3 とobservation tableの表 T3T_3 は次のようになります。

S(SΣ) / ES \cup (S \cdot \Sigma)\ /\ Eε\varepsilonaaaaaa
ε\varepsilontrue\color{Red}{\mathbf{true}}false\color{Blue}{\mathbf{false}}false\color{Blue}{\mathbf{false}}
aafalse\color{Blue}{\mathbf{false}}false\color{Blue}{\mathbf{false}}false\color{Blue}{\mathbf{false}}
aaaafalse\color{Blue}{\mathbf{false}}true\color{Red}{\mathbf{true}}false\color{Blue}{\mathbf{false}}
aaaaaafalse\color{Blue}{\mathbf{false}}false\color{Blue}{\mathbf{false}}true\color{Red}{\mathbf{true}}
aaaaaaa \cdot atrue\color{Red}{\mathbf{true}}false\color{Blue}{\mathbf{false}}false\color{Blue}{\mathbf{false}}

そして、H3\mathcal{H}_3 は学習対象の言語と一致するため、EQUIV(H3)=true\mathrm{EQUIV}(\mathcal{H}_3) = \mathbf{true} となり、学習が終了します。

というわけで、EQUIV(H)\mathrm{EQUIV}(\mathcal{H}) により反例が返ってきたときに、反例をいい感じの位置で分割して、SSEE に追加していけばいいことが分かります。

この反例をいい感じの位置で分解することを反例の解析と呼ぶことにします。

反例の解析を行う愚直なアルゴリズムは線形探索で反例の文字列の先頭から確認していく方法です。 具体的には次のようなアルゴリズム (Linear-Split) になります。

Algorithm 2 An algorithm for analyzing a counterexample using linear search

function Linear-Split(w,H,MEMBERw, \mathcal{H}, \mathrm{MEMBER})

i1i \gets 1

while true\mathbf{true} do

ii+1i \gets i + 1

Let ss be w1w2wi1w_1 w_2 \cdots w_{i - 1} and ee be wiwi+1www_i w_{i+1} \cdots w_{|w|}

if MEMBER(w)MEMBER(sHe)\mathrm{MEMBER}(w) \ne \mathrm{MEMBER}({\lfloor s \rfloor}_{\mathcal{H}} \cdot e) then

Let ss' be w1w2wi2w_1 w_2 \cdots w_{i - 2}

return (sHwi1,e)({\lfloor s' \rfloor}_{\mathcal{H}} \cdot w_{i - 1}, e)

end if

end while

end function

ここで、wH{\lfloor w \rfloor}_{\mathcal{H}}δ(q0,w)=row(s)\delta(q_0, w) = \mathrm{row}(s) となる ss を一意に表すものとします。 この関数の戻り値は2つ組 (s,e)(s, e) で、ssSS に追加する接頭辞、eeEE に追加する接尾辞になります。 引数として渡されるのが反例の文字列であれば、この関数は正しく値を返します。

重要なのが、接頭辞として返している値が、単に反例の文字列の接頭辞を返すわけではなく、sHwi1{\lfloor s' \rfloor}_{\mathcal{H}} w_{i - 1} を返しているところです。 このような値を返すことで、SS がprefix-closedであるように接頭辞を追加していくことができます。

しかし、この方法では最悪の場合、反例の文字列の長さだけ MEMBER(w)\mathrm{MEMBER}(w) を呼び出す必要があり、効率の面ではAngluinのアルゴリズムからあまり変わっていません。 これを改善するにはどうすればよいでしょうか?

二分探索による解析

上の線形探索で探している条件は MEMBER(w)MEMBER(sHe)\mathrm{MEMBER}(w) \ne \mathrm{MEMBER}({\lfloor s \rfloor}_{\mathcal{H}} \cdot e) で、返す接頭辞は sHwi1{\lfloor s' \rfloor}_{\mathcal{H}} w_{i - 1} でした。 線形探索なので、ss の1つ前の接頭辞 ss' では MEMBER(w)=MEMBER(sHwi1e)\mathrm{MEMBER}(w) = \mathrm{MEMBER}({\lfloor s' \rfloor}_{\mathcal{H}} \cdot w_{i-1} \cdot e) であることから、この探索の条件は次のようにも書けるはずです。

MEMBER(sHwi1e)MEMBER(sHe)\mathrm{MEMBER}({\lfloor s' \rfloor}_{\mathcal{H}} \cdot w_{i-1} \cdot e) \ne \mathrm{MEMBER}({\lfloor s \rfloor}_{\mathcal{H}} \cdot e)

この条件を満たすときは、s1=sHwi1s_1 = {\lfloor s' \rfloor}_{\mathcal{H}} \cdot w_{i-1}s2=sHs_2 = {\lfloor s \rfloor}_{\mathcal{H}} は、EEee を追加したときに row(s1)row(s2)\mathrm{row}(s_1) \ne \mathrm{row}(s_2) となります。 つまり、この条件を満たす位置を見つけられれば、S,ES, E に追加する意味のある (s,e)(s, e) の組を返せます。

次に、ww は反例の文字列なので、MEMBER(w)=MEMBER(εHw)\mathrm{MEMBER}(w) = \mathrm{MEMBER}(\lfloor \varepsilon \rfloor_\mathcal{H} w)MEMBER(w)MEMBER(wH)\mathrm{MEMBER}(w) \ne \mathrm{MEMBER}(\lfloor w \rfloor_\mathcal{H}) であることに注目します。 これを不変条件とした二分探索として、次のようなアルゴリズム (Split) が考えられます。

Algorithm 3 An algorithm for analyzing a counterexample using binary search

function Split(w,H,MEMBERw, \mathcal{H}, \mathrm{MEMBER})

low1\mathrm{low} \gets 1

highw+1\mathrm{high} \gets |w| + 1

while highlow>1\mathrm{high} - \mathrm{low} > 1 do

Let mid\mathrm{mid} be low+high2\lfloor \frac{\mathrm{low} + \mathrm{high}}{2} \rfloor

Let ss be w1w2wmid1w_1 w_2 \cdots w_{\mathrm{mid}-1} and ee be wmidwmid+1www_{\mathrm{mid}} w_{\mathrm{mid}+1} \cdots w_{|w|}

if MEMBER(w)=MEMBER(sHe)\mathrm{MEMBER}(w) = \mathrm{MEMBER}({\lfloor s \rfloor}_{\mathcal{H}} \cdot e) then

lowmid\mathrm{low} \gets \mathrm{mid}

else

highmid\mathrm{high} \gets \mathrm{mid}

end if

end while

Let ss' be w1w2wlow1w_1 w_2 \cdots w_{\mathrm{low}-1} and ee be whighwhigh+1www_{\mathrm{high}} w_{\mathrm{high}+1} \cdots w_{|w|}

return (sHwlow,e)(\lfloor s' \rfloor_{\mathcal{H}} \cdot w_{\mathrm{low}}, e)

end function

Split では、次の不変条件を満たすように二分探索をしています。

  • MEMBER(w)=MEMBER(w1wlow1Hwlowww)\mathrm{MEMBER}(w) = \mathrm{MEMBER}({\lfloor w_1 \cdots w_{\mathrm{low}-1} \rfloor}_{\mathcal{H}} \cdot w_{\mathrm{low}} \cdots w_{|w|})
  • MEMBER(w)MEMBER(w1whigh1Hwhighww)\mathrm{MEMBER}(w) \ne \mathrm{MEMBER}({\lfloor w_1 \cdots w_{\mathrm{high}-1} \rfloor}_{\mathcal{H}} \cdot w_{\mathrm{high}} \cdots w_{|w|})

よって、Split は正しく S,ES, E に追加する (s,e)(s, e) を返すようになっています。

最後に反例の解析を取り入れた LL^\ast の改良版のアルゴリズム (Rivest-Schapire) を疑似コードで示します。

Algorithm 4 Rivest and Schapire's LL^\ast

function Rivest-Schapire(MEMBER,EQUIV\mathrm{MEMBER}, \mathrm{EQUIV})

S,E{ε}S, E \gets \{ \varepsilon \}

T(t)MEMBER(t)T(t) \gets \mathrm{MEMBER}(t) for each t{ε}Σt \in \{ \varepsilon \} \cup \Sigma // i.e. for each tS(SΣ)t \in S \cup (S \cdot \Sigma)

repeat

while (S,E,T)(S, E, T) is not closed do

Find sSs \in S and σΣ\sigma \in \Sigma

\quads.t. row(sσ)row(s)\mathrm{row}(s \cdot \sigma) \ne \mathrm{row}(s') for all sSs' \in S

SS{sσ}S \gets S \cup \{ s \cdot \sigma \}

T(sσd)MEMBER(sσd)T(s \cdot \sigma \cdot d) \gets \mathrm{MEMBER}(s \cdot \sigma \cdot d) for each dE(ΣE)d \in E \cup (\Sigma \cdot E)

end while

// Now, (S,E,T)(S, E, T) is consistet and closed.

Let H\mathcal{H} be a hypothesis automaton for (S,E,T)(S, E, T)

if EQUIV(H)true\mathrm{EQUIV}(\mathcal{H}) \ne \mathbf{true} then

Let (s,e)(s, e) be Split(EQUIV(H),H,MEMBER\mathrm{EQUIV}(\mathcal{H}), \mathcal{H}, \mathrm{MEMBER})

T(te)MEMBER(te)T(t \cdot e) \gets \mathrm{MEMBER}(t \cdot e) for each tS(SΣ)t \in S \cup (S \cdot \Sigma)

T(sd)MEMBER(sd)T(s \cdot d) \gets \mathrm{MEMBER}(s \cdot d) for each dE(ΣE)d \in E \cup (\Sigma \cdot E)

SS{s}S \gets S \cup \{ s \} and EE{e}E \gets E \cup \{ e \}

end if

until EQUIV(H)=true\mathrm{EQUIV}(\mathcal{H}) = \mathbf{true}

return H\mathcal{H}

end function

SS に不要な文字列を追加しないようになった結果、consistencyのチェックが必要無くなっています。 さらに Split で二分探索を用いるようにしていることから、MEMBER\mathrm{MEMBER} の呼び出し回数は O(kn2+nlogm)O(k n^2 + n \log m) となります (closedにするのに高々 O(kn)O(k n) 回の呼び出しがあり、Rivest-Schapirelogm\log m 回の呼び出しがあるのを nn 回繰り返すのでこのようになります)。

以上がRivest-Schapireのアイディアの説明になります。

Kearns-Vazirani

ここからはKearns-Vaziraniについて説明していきます。

Kearns-VaziraniKearnsVaziraniが1994年に出版したAn introduction to computational learning theoryという本 ([Kearns & Vazirani, 1994]) のChapter 8. Learning Finite Automata by Experimentationで説明された LL^\ast の変種の1つです。

LL^\ast ではobservation tableを用いて MEMBER(w)\mathrm{MEMBER}(w) の結果を記録し、どの接頭辞と接尾辞の組み合わせで状態が区別できるかを判断します。 ですが、Kearns-Vaziraniではdiscrimination treeというデータ構造を用いてこれらの情報を管理します6

Rivest-Schapireの例で出した仮説のオートマトン H3\mathcal{H}_3 に対応するobservation tableの表 T3T_3 をもう一度見てください。

S(SΣ) / ES \cup (S \cdot \Sigma)\ /\ Eε\varepsilonaaaaaa
ε\varepsilontrue\color{Red}{\mathbf{true}}false\color{Blue}{\mathbf{false}}false\color{Blue}{\mathbf{false}}
aafalse\color{Blue}{\mathbf{false}}false\color{Blue}{\mathbf{false}}false\color{Blue}{\mathbf{false}}
aaaafalse\color{Blue}{\mathbf{false}}true\color{Red}{\mathbf{true}}false\color{Blue}{\mathbf{false}}
aaaaaafalse\color{Blue}{\mathbf{false}}false\color{Blue}{\mathbf{false}}true\color{Red}{\mathbf{true}}
aaaaaaa \cdot atrue\color{Red}{\mathbf{true}}false\color{Blue}{\mathbf{false}}false\color{Blue}{\mathbf{false}}

この表をよく見ると、false\color{Blue}{\mathbf{false}} で埋められている部分が多く、ほとんどの接頭辞が1つの接尾辞をつなげたときに受理されるかどうかで区別できています。 具体的には、

  • row(ε)\mathrm{row}(\varepsilon) かどうかは ε\varepsilon をつなげて受理されるかどうかで区別できる (row(ε)=row(aaaa)\mathrm{row}(\varepsilon) = \mathrm{row}(aaa \cdot a) なことに注意)、
  • row(aa)\mathrm{row}(aa) かどうかは aaaa をつなげて受理されるかどうかで区別できる、
  • row(aaa)\mathrm{row}(aaa) かどうかは aa をつなげて受理されるかどうかで区別できる。

そして、どれでも受理されないとき row(a)\mathrm{row}(a) だと分かります。

この区別の流れをフローチャートにすると次のようになります。

そして、このフローチャートは決定木のように見えないでしょうか?

また、observation tableの表による表現は疑問1で上げたように、木による表現と比べると無駄が多いのではないかというように思えます。

この考察を元に、observation tableに記録する情報を木構造を用いて管理するのがKearns-Vaziraniになります。

discrimination tree

まずはdiscrimination treeを定義します。

誤解がない場合、二分木 T\mathcal{T} のみでdiscrimination treeを表すことにします。

次に、discrimination treeに対する操作として Sift を定義します。 Sift は次のような関数です。

Algorithm 5 The "sift" function for discrimination trees

function Sift(s,Ts, \mathcal{T})

Let NN be the root node of T\mathcal{T}

while NN is a branch node do

Let dd be the label string of NN

if MEMBER(sd)\mathrm{MEMBER}(s \cdot d) then

NN \gets the right (true) node of NN

else

NN \gets the left (false) node of NN

end if

end while

return the label string of the leaf node NN

end function

直感的には、Sift は与えられた文字列 ss と節にラベル付けられた文字列 dd をつなげて MEMBER(sd)\mathrm{MEMBER}(s \cdot d) を呼び出し、その結果で二分木を辿っていき、葉に到達したらそのラベルを返す関数になっています。

この Sift を使って、discrimintation treeに対応する仮説のオートマトンを定義します。

そして、反例の文字列を受け取ってdiscrimination treeを更新する手続き (Update-Tree) を定義します。

Algorithm 6 A procedure to update a discrimination tree with a given counterexample string

procedure Update-Tree(w,Tw, \mathcal{T})

Let H=(Q,q0,F,δ)\mathcal{H} = (Q, q_0, F, \delta) be a hypothesis automaton of T\mathcal{T}

p,sεp', s' \gets \varepsilon // Sift(p,Tp, \mathcal{T}) =δ(q0,ε)=ε= \delta(q_0, \varepsilon) = \varepsilon

for i2,,wi \coloneqq 2, \cdots, |w| do

Let pp be w1wiw_1 \cdots w_i

Let ss be Sift(p,Tp, \mathcal{T}) and s^\hat{s} be δ(q0,p)\delta(q_0, p)

if ss^s \ne \hat{s} then

Let dDd \in D be a label string to distinguish between ss and s^\hat{s}

\quads.t. MEMBER(sd)MEMBER(s^d)\mathrm{MEMBER}(s \cdot d) \ne \mathrm{MEMBER}(\hat{s} \cdot d)

Let N1N_1 be a new leaf node labelled with ss'

\quad and N2N_2 be a new leaf node labelled with pp'

// MEMBER(swid)MEMBER(pwid)\mathrm{MEMBER}(s' \cdot w_i \cdot d) \ne \mathrm{MEMBER}(p' \cdot w_i \cdot d)

// Here, we assume MEMBER(swid)=true\mathrm{MEMBER}(s' \cdot w_i \cdot d) = \mathbf{true} without loss of generality.

Let NN be a new branch node labelled with widw_i \cdot d and connected to N1N_1 and N2N_2.

Replace the lead node labelled with ss' by the new node NN.

return

end if

ppp' \gets p and sss' \gets s

end for

end procedure

Update-Tree の中では次のようなことをしています。

  • s=Sift(w1wi,T),s^=δ(q0,w1wi)s = \text{\htmlClass{katex-ps-funcname}{Sift}}(w_1 \cdots w_i, \mathcal{T}),\,\hat{s} = \delta(q_0, w_1 \cdots w_i) として ss^s \ne \hat{s} となる位置を探す
  • MEMBER(sd)MEMBER(s^d)\mathrm{MEMBER}(s \cdot d) \ne \mathrm{MEMBER}(\hat{s} \cdot d) となる文字列 dDd \in D を探す
  • 1つ前の接頭辞 p=w1wi1p' = w_1 \cdots w_{i-1} に対する Sift の結果 s=Sift(p,T)s' = \text{\htmlClass{katex-ps-funcname}{Sift}}(p', \mathcal{T}) に対応する葉ノードを、次のような節ノードで置換する
    • widw_i \cdot d でラベル付けられている
    • ss' でラベル付けられた葉ノードと pp' でラベル付けられた葉ノードに接続している

この説明だけでは分かりにくいので図で説明します。 これは、Rivest-Schapireの例で出した仮説のオートマトン H2\mathcal{H}_2 に対応するdiscrimination tree T2\mathcal{T}_2 の図です。

これに対する反例の文字列は w=aaaaw = aaaa で、 Update-Tree(w,T)\text{\htmlClass{katex-ps-funcname}{Update-Tree}}(w, \mathcal{T}) を呼んだ場合を考えます。 ww の各接頭辞に対する δ\deltaSift の戻り値を計算します。

  • s1=Sift(aaaa,T)=aa,s^1=δ(q0,aaaa)=aas_1 = \text{\htmlClass{katex-ps-funcname}{Sift}}(a\phantom{aaa}, \mathcal{T}) = a\phantom{a},\, \hat{s}_1 = \delta(q_0, a\phantom{aaa}) = a\phantom{a}
  • s2=Sift(aaaa,T)=aa,s^2=δ(q0,aaaa)=aas_2 = \text{\htmlClass{katex-ps-funcname}{Sift}}(aa\phantom{aa}, \mathcal{T}) = aa,\, \hat{s}_2 = \delta(q_0, aa\phantom{aa}) = aa
  • s3=Sift(aaaa,T)=aa,s^3=δ(q0,aaaa)=aas_3 = \text{\htmlClass{katex-ps-funcname}{Sift}}(aaa\phantom{a}, \mathcal{T}) = a\phantom{a},\, \hat{s}_3 = \delta(q_0, aaa\phantom{a}) = a\phantom{a}
  • s4=Sift(aaaa,T)=εa,s^4=δ(q0,aaaa)=aas_4 = \text{\htmlClass{katex-ps-funcname}{Sift}}(aaaa, \mathcal{T}) = \varepsilon\phantom{a},\, \hat{s}_4 = \delta(q_0, aaaa) = aa

よって s4s^4s_4 \ne \hat{s}_4 なので s3=as_3 = a のノードを次のようなノードで置換します。

  • MEMBER(s4d)MEMBER(s^4d)\mathrm{MEMBER}(s_4 \cdot d) \ne \mathrm{MEMBER}(\hat{s}_4 \cdot d) となる文字列 dDd \in Dd=εd = \varepsilon なので、節ノードのラベルは ad=aa \cdot d = a
  • 葉ノードは true\mathbf{true} 側が aaaaaa でラベル付けられたもので、false\mathbf{false} 側が aa でラベル付けられたものが接続される

そして、最終的にdiscrimination treeは次の図のようになります。 赤いノードが新しく追加された (置換された) ノードになります。

やっていることとしては、Rivest-Schapireのときの線形探索による反例の解析と同じようなものです。

Update-Tree が正しく動作することを確かめるには、新しく SS に追加した p=w1wi1p' = w_1 \cdots w_{i-1}s=Sift(p,T)s' = \text{\htmlClass{katex-ps-funcname}{Sift}}(p', \mathcal{T}) が、widw_i \cdot d で区別できている、つまり、次の式を満たしていることを確認したいです。

MEMBER(swid)MEMBER(pwid)\mathrm{MEMBER}(s' \cdot w_i \cdot d) \ne \mathrm{MEMBER}(p' \cdot w_i \cdot d)

これには SiftMEMBER\mathrm{MEMBER} の関係を表す次の補題を用います。

これは Sift の二分木の辿り方を考えれば自明なものです。

dds=Sift(pwi,T)s = \text{\htmlClass{katex-ps-funcname}{Sift}}(p' w_i, \mathcal{T})s^=δ(q0,pwi)\hat{s} = \delta(q_0, p' w_i) を区別する文字列であることから、

MEMBER(δ(q0,pwi)d)MEMBER(Sift(pwi,T)d)\mathrm{MEMBER}(\delta(q_0, p' w_i) \cdot d) \ne \mathrm{MEMBER}(\text{\htmlClass{katex-ps-funcname}{Sift}}(p' w_i, \mathcal{T}) \cdot d)

この式の右辺に補題を用いて、

MEMBER(Sift(pwi,T)d)=MEMBER(pwid)\mathrm{MEMBER}(\text{\htmlClass{katex-ps-funcname}{Sift}}(p' w_i, \mathcal{T}) \cdot d) = \mathrm{MEMBER}(p' \cdot w_i \cdot d)

左辺は、まず拡張された δ\delta の定義が δ(q,wσ)=δ(δ(q,w),σ)\delta(q, w \sigma) = \delta(\delta(q, w), \sigma) (wΣ,σΣw \in \Sigma^\ast,\, \sigma \in \Sigma) であり、さらにdiscrimination treeの仮説のオートマトンが Sift で定義されていること、Update-Tree の繰り返しから s=Sift(p,T)=δ(q0,p)s' = \text{\htmlClass{katex-ps-funcname}{Sift}}(p', \mathcal{T}) = \delta(q_0, p') であることに注意すると、補題を用いて

MEMBER(δ(q0,pwi)d)=MEMBER(δ(δ(q0,p),wi)d)=MEMBER(Sift(δ(q0,p)wi,T)d)=MEMBER(δ(q0,p)wid)=MEMBER(Sift(p,T)wid)=MEMBER(swid)\begin{align*} \mathrm{MEMBER}(\delta(q_0, p' w_i) \cdot d) &= \mathrm{MEMBER}(\delta(\delta(q_0, p'), w_i) \cdot d) \\ &= \mathrm{MEMBER}(\text{\htmlClass{katex-ps-funcname}{Sift}}(\delta(q_0, p') \cdot w_i, \mathcal{T}) \cdot d) \\ &= \mathrm{MEMBER}(\delta(q_0, p') \cdot w_i \cdot d) \\ &= \mathrm{MEMBER}(\text{\htmlClass{katex-ps-funcname}{Sift}}(p', \mathcal{T}) \cdot w_i \cdot d) \\ &= \mathrm{MEMBER}(s' \cdot w_i \cdot d) \end{align*}

となり、目的の式が得られます。

最後に、これらの手続き・関数を使って、実際の学習のアルゴリズムを定義します。

Algorithm 7 Kearns and Vazirani's LL^\ast algorithm using a disrimination tree

function Kearns-Vazirani(MEMBER,EQUIV\mathrm{MEMBER}, \mathrm{EQUIV})

// Initialization:

// Do a membership query on ε\varepsilon to determine whether

// the start state of H\mathcal{H} is accepting or rejecting

if MEMBER(ε)=true\mathrm{MEMBER}(\varepsilon) = \mathbf{true} then

// Construct a hypothesis automaton that consists simply of this

// single (accepting or rejecting) state with self-loops

H({ε},ε,{ε},(ε,σ)ε for σΣ)\mathcal{H} \gets (\{ \varepsilon \}, \varepsilon, \{ \varepsilon \}, (\varepsilon, \sigma) \mapsto \varepsilon\ \mathbf{for}\ \sigma \in \Sigma)

else

H({ε},ε,,(ε,σ)ε for σΣ)\mathcal{H} \gets (\{ \varepsilon \}, \varepsilon, \emptyset, (\varepsilon, \sigma) \mapsto \varepsilon\ \mathbf{for}\ \sigma \in \Sigma)

end if

if EQUIV(H)=true\mathrm{EQUIV}(\mathcal{H}) = \mathbf{true} then

return H\mathcal{H}

end if

wEQUIV(H)w \gets \mathrm{EQUIV}(\mathcal{H})

Let T\mathcal{T} be a discrimination tree having a root node labelled with ε\varepsilon

\quadtwo leaves labelled with ww and ε\varepsilon

// Main loop:

repeat

Let H\mathcal{H} be a hypothesis automaton of T\mathcal{T}

if EQUIV(H)true\mathrm{EQUIV}(\mathcal{H}) \ne \mathbf{true} then

wEQUIV(H)w \gets \mathrm{EQUIV}(\mathcal{H})

Call Update-Tree(w,Tw, \mathcal{T})

end if

until EQUIV(H)=true\mathrm{EQUIV}(\mathcal{H}) = \mathbf{true}

return H\mathcal{H}

end function

最初の方が妙にややこしいですが、空文字列で自明なオートマトンを作って、それに対して EQUIV(H)\mathrm{EQUIV}(\mathcal{H}) を呼び、反例の文字列と合わせてdiscrimination treeを構築しています。 また、本体の繰り返しの部分は EQUIV(H)\mathrm{EQUIV}(\mathcal{H}) を呼んで、反例の文字列 wwUpdate-Tree(w,T)\text{\htmlClass{katex-ps-funcname}{Update-Tree}}(w, \mathcal{T}) を呼び出して、discrimination treeを更新していきます。

このアルゴリズム中での MEMBER(w)\mathrm{MEMBER}(w) の呼び出し回数は O(kn2+n2m)O(kn^2 + n^2 m) となります。 理由はRivest-Schapireのときと同じで二分探索でなく線形探索をしている分、logm\log mmm になっています。

このようにdiscrimination treeを用いてオートマトン学習を行うのがKearns-Vaziraniになります。

あとがき

今回はAngluinの LL^\ast アルゴリズムと、その派生アルゴリズムのRivest-SchapireとKearns-Vaziraniについて説明しました。 Angluinの LL^\ast アルゴリズムは有名なものなのでご存知の方も多いと思うのですが、元論文に載っているアルゴリズムが接頭辞をすべて追加するものであることや、それに対して反例の解析という手法があることはあまり知られていないのではないでしょうか。 また LL^\ast と言えばobservation tableを使うものとして知られていますが、discrimination treeというデータ構造を使うことで、さらに効率的に学習ができることも興味深いです。

反例の解析については[Isberner & Steffen, 2014]で、今回説明した二分探索による反例の解析以外にも、exponential searchのような手法など、様々なアイディアが示されています。

近年の LL^\ast を改良したアルゴリズムとしては、observation pack [Howar, 2012] やTTT [Isberner et al., 2014] が有名です。 これらは、今回説明したRivest-SchapireとKearns-Vaziraniをベースとしたアルゴリズムとなっています。 他にも L#L^\# と呼ばれるapartnessという概念を導入したオートマトン学習の手法も提案されています [Vaandrager, 2022]

この記事を書くのに3週間近くかかってしまったのですが、なんとかまとめることができました。 途中、Mermaidの図を表示できる機能をblogに追加したりしていて、中々大変でした。 こちらについてもどこかで説明できればと思います。

それでは、最後まで目を通していただきありがとうございました。

更新履歴

  • 2024/08/09: 初版公開。
  • 2024/08/10: MEMBER\mathrm{MEMBER}Sift についての補題についてのあやしい記述を修正。Kearns-Vaziraniの計算量についての記述を追加。
  • 2024/08/11: 細かなtypoを修正。
  • 2024/08/19: Rivest-Schapireの説明の分かりにくい部分とあやしい部分を修正。

参考文献

[Angluin, 1987]:

Angluin, Dana. "Learning regular sets from queries and counterexamples." Information and computation 75.2 (1987): 87-106.

https://dl.acm.org/doi/10.1016/0890-5401%2887%2990052-6

[Rivest & Schapire, 1993]:

Rivest, Schapire. "Rivest RL, Schapire RE, Inference of finite automata using homing sequences." Machine Learning: From Theory to Applications (1993): 51-73.

https://www.sciencedirect.com/science/article/pii/S0890540183710217

[Isberner & Steffen, 2014]:

Isberner, Malte, and Bernhard Steffen. "An abstract framework for counterexample analysis in active automata learning." International Conference on Grammatical Inference. PMLR, 2014.

https://proceedings.mlr.press/v34/isberner14a

[Kearns & Vazirani, 1994]

Kearns, Michael J., and Umesh Vazirani. An introduction to computational learning theory. MIT press, 1994.

https://direct.mit.edu/books/monograph/2604

[Howar, 2012]

Howar, Falk M. "Active learning of interface programs." (2012).

https://d-nb.info/1099294959/34

[Isberner et al., 2014]

Isberner, Malte, Falk Howar, and Bernhard Steffen. "The TTT algorithm: a redundancy-free approach to active automata learning." Runtime Verification: 5th International Conference, RV 2014, Toronto, ON, Canada, September 22-25, 2014. Proceedings 5. Springer International Publishing, 2014.

https://link.springer.com/chapter/10.1007/978-3-319-11164-3_26

[Vaandrager, 2022]

Vaandrager, Frits, et al. "A new approach for active automata learning based on apartness." International Conference on Tools and Algorithms for the Construction and Analysis of Systems. Cham: Springer International Publishing, 2022.

https://link.springer.com/chapter/10.1007/978-3-030-99524-9_12

脚注

  1. Angluinの論文では EE もsuffix-closedとしていますが、説明上の都合から今回は含めていません。

  2. Ronald Rivest。余談ですが、RC暗号の "R" は彼の名前から取られています。

  3. 1989年に同タイトルの論文がSTOC '89のproceedingとして出版されていますが、こちらには反例の解析についての改良は書かれていません。このタイトルで調べるといくつかPDFが出てくるのですが、反例の解析について言及していないものも多いので注意してください。

  4. そこまでちゃんと読めているわけではないので間違っていたらごめんなさい。

  5. 本来の状態は row(s)\mathrm{row}(s) ですが、図では対応する文字列 ss で表示しています。

  6. KearnsとVaziraniの本ではclassification treeと呼ばれています。これだと機械学習で用いる分類木 (classification tree) とややこしいからだと思いますが、いつからdiscrimination treeと呼ばれるようになったのかはよく分かっていません。