こんにちは。秋田県のIT企業、北日本コンピューターサービスのR&Dチーム「AUL(アウル)」に所属しています。トラフクロウです。
少し前に、「ベイズ推論」をつかって僕自身のジンクスを計算してみた、という記事を書きました。
膨大な統計結果に基づく確率だけでなく、個人の主観さえも計算できてしまうという「うさんくささ」がとても印象的で面白いと思いました。
一方で次のような期待感もあります。
- ベイズ推論を使えばニューラルネットに頼らなくても人間の感性を模倣したAI作れそうじゃない?
- 人間のフィードバックを踏まえて確率を更新すれば簡単に追加学習もできそうじゃないか?
- シンプルなロジックかつ、自己成長性も兼ね備えた最強のモデルになるのでは?
そう思ったらワクワクしてしまったので、プログラムを書いて実験をしてみることにしました。
実験をする中で、新しく見えてきたことがたくさんあったので、何をしたのかと結果を簡単にまとめてみたいと思います。
作ったモデルと実験の概要
【作成するモデル】
2つの文章A, Bが同じトピックについて書かれているかを、ベイズ推論を使って判定するモデルをつくります。
【具体例1】
文章A: ラッコは人間よりも毛量が多い
文章B: 北太平洋沿岸に生息する哺乳類の例としてラッコが挙げられます。
→ ラッコについて書かれているので「同じトピック」
【具体例2】
文章A: 鶏肉に衣をまぶし油で揚げることで、おいしいフライドチキンができる
文章B: 友人は私を「チキン野郎」とののしった
→ チキン違いなので「違うトピック」
※ 何をもって「同じ」とするかの基準がガバガバなのはご愛嬌です。
【学習方法の概要】
10件分の文章A,Bのペアを「コサイン類似度(cos)」、「ジャッカード係数(Jaccard)」、「僕オリジナルの類似度(original)」で評価し、それぞれの評価結果をもとに、同じトピックか否かの統計を取り、未知のペアに対して予想できるようにします。
ベクトル生成のための埋め込みモデルには、AWSのAmazon Bedrockの Titan Embeddings G1 - Text、単語分割にはPythonのspacyで、ja_ginzaを使っています。
用意した文章のうち、「同じトピック」に分類されるペアに対し、上記3つの類似度を計算し、各中央値を閾値として「事象の観測条件」を3つ作成しました。
条件1: コサイン類似度が閾値以上か否か
条件2: ジャッカード係数が閾値以上か否か
条件3: オリジナル類似度が閾値以上か否か
用意した10件の学習データで各3つの事象が観測できたかの統計をとります。この統計結果がモデルです。
未知のペアが与えられたときは、そのペアに対し上記の3条件を観測できるかを調べます。
そしてモデル内の統計をもとに「そのペアが同じトピックである事前確率」をベイズ更新し、最終的な判断を行います。
最初は2つの文章が同じトピックであるか否かは判断できないはずなので、事前確率はフィフティーフィフティー($0.5$)に設定しています。
ベイズ更新は、僕が以前書いた↓の記事で取り上げている正方形の面積ベースの方法です(そっちも面白いのでぜひ見てください!)。
🔍各類似度についてざっくり説明
【コサイン類似度】
2つの文章A,Bの文脈的な類似性を $-1$ から $1$ の範囲で計算するというものです($1$に近いほど似ている)。
【ジャッカード係数】
2つの文章A,Bがどれだけ同じ単語を含んでいるかの割合を示した数字です。
分母は両文章の総単語数、分子は両文章内で共通する単語数を使います。($1$に近いほど同じ単語を含んでいる)。
【僕オリジナルの類似度】
僕(トラフクロウ)が作った2つの文章を比較するための1つの評価指標です。
詳細は割愛しますが、2つの文章が、どれほど意味的に類似したキーワードを持っているかに注目し、$-1$ から $1$ の範囲で両者の類似性を評価します($1$に近いほど似ている)。
📚学習に使った文章のペア
{
"label": 同じトピック,
"texts": [
"生成AIを業務に活用する企業が増えている",
"LLMを社内システムへ導入する事例が増加している"
]
},
{
"label": 同じトピック,
"texts": [
"ベイズ更新を利用した文章分類モデルを作成する",
"確率モデルによるテキスト分類の精度改善を検討する"
]
},
{
"label": 同じトピック,
"texts": [
"今日は曇り空で、気温も低い",
"しばらくは晴れの日が続く見込みである"
]
},
{
"label": 同じトピック,
"texts": [
"コウテイペンギンはとても大きい",
"キングペンギンのヒナはデカいキウイに見える"
]
},
{
"label": 同じトピック,
"texts": [
"微分は科学と共に発展してきた",
"短冊状の矩形を足しわせることで面積を近似する手法をリーマン積分という"
]
},
{
"label": 違うトピック,
"texts": [
"生成AIを業務に活用する企業が増えている",
"秋田県の温泉地と観光スポットを紹介する",
],
},
{
"label": 違うトピック,
"texts": [
"ベイズ更新を利用した文章分類モデルを作成する",
"鶏肉を使った簡単な夕食レシピを解説する",
],
},
{
"label": 違うトピック,
"texts": [
"野球は9人でやるスポーツである",
"2人で歌うことをデュエットという",
],
},
{
"label": 違うトピック,
"texts": [
"シロナガスクジラは地球上でもっとも大きな哺乳類である",
"木星は太陽系でもっとも大きな惑星である",
],
},
{
"label": 違うトピック,
"texts": [
"彼は患者の状態を確認した後、必要な処置をすぐに開始した",
"彼はサーバーの状態を確認した後、必要な修正をすぐに開始した。",
],
},
🔭各類似度の閾値
{
"cos": 0.693298876156252,
"Jaccard": 0.16666666666666666,
"original": 0.7694591016811198
}
【実験の概要】
今回は、次の2つを確認します。
- 各事象を観測することによって「同じトピックか否か」の確率が変化していること
- データを観察する視点によって、データの分類の精度は変わるのか
まず前提として、事象の観測の順番は最終的な確率の値に影響を与えません。つまり、コサイン類似度を判定してから、ジャッカード係数を判定しても、ジャッカード係数を判定してからからコサイン類似度を判定しても計算後の確率は同じになります(事象としては2つの値を観測したことに変わらないから)。
このことを踏まえると考えられる観測のパターンは、
- どれか1つの事象を観測する(3パターン)
- 3つの事象のうち、どれか2つを観測する(3パターン)
- 3つの事象をすべて観測する(1パターン)
- どれも観測しなかった(1パターン)
の計8パターンになります。毎回すべての条件を検証するのも芸がないと思ったので、観測を狙う条件を絞って、どの事象に注目するのがもっとも精度が高いのかも見てみることにしました。
つまり、コサイン類似度のみを見ると決めたら、他の類似度の観測値は完全に無視するということです。
さっきも書いたように、観測の順序は確率の計算に影響を与えないので、今回は
cos → Jaccard → original
の順に観測するようにしています。
⚠️【注意】今回の分類方法はナイーブベイズではありません
ベイズ推論を使った分類器を作る方法としてよく「ナイーブベイズ」が使われます。
ナイーブベイズとは、「各事象の発生が互いに関係しあっていない(各事象が独立)」という条件を仮定したベイズ推論です。
例えば、「サイコロの目を振って1の目が出ること」と「僕が明日、ケガをすること」は互いの発生に影響を与えません。サイコロの目で何が出ようが、僕はケガをするかもしれないし、僕がケガをしようとしまいと、1の目の出やすさは変わりません。
このように「互いに独立した事象」を観測対象にするのがナイーブベイズです(こうした方が事後確率の計算がラクになります)。
しかし、今回観測する事象は「各類似度が閾値を超えたか否か」です。これらは(おそらく)完全に独立ではありません。
共通のキーワードをたくさん含む文章のペアは、ジャッカード係数が大きくなりますし、そうすればコサイン類似度も高くなる可能性があります。
実験結果
まずは、観測に応じて確率が変化している様子を見てみます。3つの類似度すべてを観測対象にして、次の文章ペアを判定してみます。
文章A: バナナはおやつに入りますか?
文章B: バナナは消化吸収しやすい食べ物です
結果は次のようになりました。
【事前情報なし】
同じトピックである確率:0.5
違うトピックである確率:0.5
【cosを観測】
同じトピックである確率:0.7499999999999999
違うトピックである確率:0.25
【Jaccardを観測せず】
同じトピックである確率:0.6666666666666666
違うトピックである確率:0.33333333333333337
【originalを観測】
同じトピックである確率:0.8
違うトピックである確率:0.20000000000000004
##########################################
結果:同じトピック
今回はコサイン類似度と、オリジナルの類似度が閾値を超えたようです。
観測を重ねるごとに確率が変化していることがわかりますね。
次に、どの類似度に注目すれば、もっとも精度が高くなるのかも見たいと思います。
実験のために4つのテストデータを用意しました。
{
"label": 同じトピック,
"texts": [
"今日は雨が降る予報です",
"台風12号が接近しています"
]
},
{
"label": 違うトピック,
"texts": [
"新しいモデルを追加学習した結果、分類精度が大幅に向上した",
"新しい模型用ツールで塗装を練習した結果、作品の精度が大幅に向上した"
]
},
{
"label": 同じトピック,
"texts": [
"メンフクロウはリンゴの断面のようなハート形の顔が特徴的だ",
"畑のネズミ退治のためにメンフクロウの巣箱を設置する場合もある"
]
},
{
"label": 違うトピック,
"texts": [
"射影平面は、向き付け不可能な閉曲面である",
"クロールは水泳の中でもっとも基本となる泳ぎ方です"
]
}
これら4問のうち、各注目パターンでの正解数は以下のようになりました。
| 観測を狙う類似度 | 正解数 |
|---|---|
| cos に注目 | 3 |
| Jaccard に注目 | 2 |
| original に注目 | 1 |
| cos, Jaccard に注目 | 3 |
| cos, original に注目 | 3 |
| Jaccard, original に注目 | 1 |
| cos, Jaccard, original に注目 | 3 |
やはり、どの観測を狙うかで結果は変わりましたね。
学習データの質や量も関係ありそうですが、今回はコサイン類似度を絡めた方が良い分類ができていそうです。
⚗️各パターンでの分類結果の詳細
文章A: 今日は雨が降る予報です
文章B: 台風12号が接近しています
【cos】→ 同じトピック
【Jaccard】→ 違うトピック
【original】→ 違うトピック
【cos, Jaccard】→ 同じトピック
【cos, original】→ 同じトピック
【Jaccard, original】→ 違うトピック
【cos, Jaccard, original】→ 同じトピック
###########################################
文章A: 新しいモデルを追加学習した結果、分類精度が大幅に向上した
文章B: 新しい模型用ツールで塗装を練習した結果、作品の精度が大幅に向上した
【cos】→ 違うトピック
【Jaccard】→ 違うトピック
【original】→ 同じトピック
【cos, Jaccard】→ 違うトピック
【cos, original】→ 違うトピック
【Jaccard, original】→ 同じトピック
【cos, Jaccard, original】→ 違うトピック
###########################################
文章A: メンフクロウはリンゴの断面のようなハート形の顔が特徴的だ
文章B: 畑のネズミ退治のためにメンフクロウの巣箱を設置する場合もある
【cos】→ 違うトピック
【Jaccard】→ 違うトピック
【original】→ 違うトピック
【cos, Jaccard】→ 違うトピック
【cos, original】→ 違うトピック
【Jaccard, original】→ 違うトピック
【cos, Jaccard, original】→ 違うトピック
###########################################
文章A: 射影平面は、向き付け不可能な閉曲面である
文章B: クロールは水泳の中でもっとも基本となる泳ぎ方です
【cos】→ 違うトピック
【Jaccard】→ 違うトピック
【original】→ 違うトピック
【cos, Jaccard】→ 違うトピック
【cos, original】→ 違うトピック
【Jaccard, original】→ 違うトピック
【cos, Jaccard, original】→ 違うトピック
今回わかったこと
ロジックの構成、データ集計、コーディング、実験をひとしきりやってみて
わかったことは次の通りです。
- プログラミングで実現する都合上、観測条件を定量的にする必要がある
- 事象の候補が複数あった場合、最適な組み合わせを選択する労力がかかる
- LLMを絡めることで、もっと効果的にベイズ推論ができるかも
今回の実験はもともと
「ベイズ推論なら人間のような判断をするプログラムを簡単に作れるのでは?」
という疑問からスタートしました。
しかしやってみて気づいたのですが、人間が「なんとなく」で済ませてしまう観測も、
プログラミングでは「観測できた/できなかった」の白黒をつけなくてはいけません。
これがジミに難しく、当初考えていたような柔軟性や自由度がかなり制限された印象でした。
また、今回の実験で見たように「観測を狙う事象の選択」によって観測結果が変化します。
評価指標の候補が3つあるだけで7通りのパターンが考えられるので、「どのパターンで評価するか」の選択にも労力がかかりそうです。
ひと昔前のエキスパートシステムのように人間が特徴量を用意してあげる必要があるので、
「ニューラルネットが一般的なのは、このあたりも関係しているのかな?」と思いました。
ゼロからスタートは大変そうですが、すでに一定の特徴データが集まっている既存システムや、LLM、エキスパートモデルと組み合わせることで、人間の感覚と乖離した観測の問題を解消し、効果的に活用できるかも?という印象です。
一方で、「ハルシネーションが発生しないこと」や、「最終結果が確率で表現できること(むやみに白黒つけない)」、「出力結果の理由を統計的に説明できること」は、やはり利点だなと感じました。
最後まで読んでいただきありがとうございました!
最後まで読み進めていただき、ありがとうございました!
ベイズ推論を使ったプログラミングはずっとやってみたかったのですが、準備に時間がかかりそうで敬遠しておりました。
ですので今回ようやっと、動くものを作ることができ大満足です!(ソースコードは人様に見せられたものではありませんが。。。)
ロジックの確認や、データ集計方法の検討は正直ちょっとしんどかったですが、実験を始めるまでは見えていなかった課題や可能性を感じることができたので「まぁ、よいでしょう」という感じです。
ちなみに僕は勉強や検証の際に
「車輪の再発明上等!!」の精神で基本なんでも自作しますが(今回もそう)、
今回取り上げたベイズ推論にはちゃんとPythonのライブラリがあるそうなので、
簡単に試したい方はそちらを使うことをオススメします。