これは 日本情報クリエイト Engineers Advent Calendar 2016 の17日目の記事です。
はじめに
年末です。もう少しでまっさらな年が始まるこの時期は、自分自身を見直すいいチャンスだと思います。
…なんてことを会社帰りの車内で考えながら、と同時に「とはいえ自分自身の事ってよく分からないよな、なんでだろう」とワクワク。
古代ギリシアを生きた自然哲学者タレスは、このような言葉を残しています。
「最も困難なことは自分自身を知ることであり、最も容易なことは他人に忠告することである」
人間誰しも自分自身のことが一番よく分からないというのは、2600年前も現在も変わらないようです。
まず、まとめ
- 自分のことは何でも良く分かってしまうと思い込むから、分からない。
- 他人の脳内のモデルを使って自分を知ろう(コードレビューなどは良い例ですね)。
- 自分の脳内のモデルの可塑性を信じて、明日の自分に解釈を委ねてみよう。
「分かる」とは
「分かる」ということは「ある物事を認識し、うまく解釈できること」と言い換えられます(以降、まとめて認識と呼びます)。流行りの歌は多くの人が聞いて「分かる」ものですが、聞き慣れない前衛音楽は「分からない」人が殆どだと思います。
では、「分かる」と「分からない」の違いはどこにあるのでしょう。答えを先に言ってしまうと、対象の物事を解釈する良いモデルを脳の中に持っているか否かです。最近また頓に色めきだっているAI技術も、いかに良いモデルを機械に学習させるかの積み重ねなのです。
確率的生成モデル
基本
「ところで、モデルって何だよ!」という話です。
なんだか小難しいキーワードが出てきましが、「確率的生成モデル」とはその名の通り、確率的に物事を生成する能力を持ったモデルです。このモデルを使って私たちの脳を紐解いてみると、本記事タイトルの疑問に答えられそうです。
具体的な例で考えてみましょう。「5」という数字を手で書いてみてください。何回でも「5」を書けるかと思います。しかし、二度と同じ「5」を書くことはできません。これはまさに、脳の中にある事前確率分布 $P(\boldsymbol{x})$ から、目に見える「5」という文字のデータ $\boldsymbol{y}$ をサンプリングしていると言えます。$\boldsymbol{x}$ は私たちの目には見えない確率変数です。頭の中にあるモヤモヤっとした概念だと思ってください。
昔、漢字ノートにたくさん漢字を書いて覚えたように、生成と認識を繰り返しながら $P(\boldsymbol{x})$, データモデル $P(\boldsymbol{y}|\boldsymbol{x})$ を少しずつ変化させることを学習と呼びます。
事後確率最大化
観測可能なデータ $\boldsymbol{y}$ は手書き文字以外にもいろいろ挙げられます。楽器の演奏や車の運転、仕事の仕方など脳によって生み出されるもの全てに及びます。他人の生成したデータ $\boldsymbol{y}$ を観測した上で、自分の持つ $\boldsymbol{x}$ がどれくらい良いかを表す事後確率は次のように計算できます。
P(\boldsymbol{x}|\boldsymbol{y}) = \frac{P(\boldsymbol{x})P(\boldsymbol{y}|\boldsymbol{x})}{P(\boldsymbol{y})}
ベイズ推定を用いたパターン認識は、$P(\boldsymbol{x}|\boldsymbol{y})$ を最大にするような $\boldsymbol{x}$ を求める問題として定式化できます( $P(\boldsymbol{y})$ は $\boldsymbol{x}$ に関係しないので、実際は $P(\boldsymbol{x})P(\boldsymbol{y}|\boldsymbol{x})$ を計算します)。面白いことに、人間の脳内でも同じようなことが行われているようです。
Practice
前提
「人はなぜ自分の事がよく分からないのか」に答えるために、例によって簡単なモデルを使って考えてみましょう。次の表はAさんとBさんの脳内にある【りんご】のモデルを覗き見したものです。$\boldsymbol{x}$ はりんごの印象を表す確率変数です。
▼ Aさん(趣味:スイーツ巡り)とBさん(青森出身)の脳内にある「りんご」のモデル $P_{A}(\boldsymbol{x}), P_{B}(\boldsymbol{x})$
$\boldsymbol{x}$ | $P_{A}(\boldsymbol{x})$ | $P_{B}(\boldsymbol{x})$ |
---|---|---|
赤い | 0.24 | 0.1 |
酸っぱい | 0.3 | 0.05 |
丸い | 0.1 | 0.05 |
甘い | 0.35 | 0.2 |
青森 | 0.01 | 0.6 |
また、データモデル $P(\boldsymbol{y}|\boldsymbol{x})$ は、$P(\boldsymbol{x})$ の重みを加味せずに、次のような感じで決め打ってしまいます。
\begin{align}
P({\rm 赤い}|{\rm 赤い}) &= 0.8 \\
P({\rm 赤い}|{\rm 赤くない}) &= 0.05 \\
P({\rm 赤くない}|{\rm 赤い}) &= 0.05 \\
P({\rm 赤くない}|{\rm 赤くない}) &= 0.8 \\
\end{align}
やること
- Aさん、Bさんの脳内のリンゴのモデル $P_{A}(\boldsymbol{x}), P_{B}(\boldsymbol{x})$ からデータ $\boldsymbol{x_{\rm{org}}}$ を $n = 100000$ 個サンプリングします。
すなわち、以下のようなデータをAさんとBさんのモデルから確率的に生成します。
そして、インチキですが、$\boldsymbol{y} = \boldsymbol{x_{\rm{org}}}$ とします。
$i$ | 赤い | 酸っぱい | 丸い | 甘い | 青森 |
---|---|---|---|---|---|
1 | 0 | 0 | 1 | 1 | 1 |
2 | 1 | 1 | 0 | 1 | 0 |
… | … | … | … | … | … |
100000 | 1 | 0 | 0 | 1 | 1 |
- データ $\boldsymbol{y}$ から、事後確率 $P_{A}(\boldsymbol{x}|\boldsymbol{y}), P_{B}(\boldsymbol{x}|\boldsymbol{y})$を計算します。
プログラムに落とし込みやすいように、冒頭の式をもう少し書き下してみましょう。
P(\boldsymbol{y}|\boldsymbol{x}) = \prod_{j=1}^5 P(x_j)P(y_j | x_j)
確率変数の取りうる値は今回5通りですので、掛け算のままの計算で問題ないかと思います。確率の掛け算ですので、一般的にプログラムで計算する場合アンダーフローを起こさないように対数を取って足し算にする、定石もあります。
- 計算した 100000 個の $P(\boldsymbol{x}|\boldsymbol{y})$ の平均と分散を調べます。
Python でさっくり計算
# -*- coding: utf-8 -*-
import numpy as np
def main():
# モデルの定義
p_x = {'a':[.24, .3, .1, .35, .01], 'b':[.1, .05, .05, .2, .6]}
# サンプリング(100000 patterns)
n = 100000
y = {'a':[], 'b':[]}
for i in xrange(n):
y['a'].append([1 if np.random.rand() <= j else 0 for j in p_x['a']])
y['b'].append([1 if np.random.rand() <= j else 0 for j in p_x['b']])
# P(x)P(Y|X) を計算
pp_aa = np.array([calc_pp(p_x['a'], i, j) for i, j in zip(y['a'], y['a'])])
pp_ab = np.array([calc_pp(p_x['a'], i, j) for i, j in zip(y['a'], y['b'])])
pp_ba = np.array([calc_pp(p_x['b'], i, j) for i, j in zip(y['b'], y['a'])])
pp_bb = np.array([calc_pp(p_x['b'], i, j) for i, j in zip(y['b'], y['b'])])
print 'Data A, Model A, mean %e, sigma %e' % (pp_aa.mean(), pp_aa.var())
print 'Data A, Model B, mean %e, sigma %e' % (pp_ab.mean(), pp_ab.var())
print 'Data B, Model A, mean %e, sigma %e' % (pp_ba.mean(), pp_ba.var())
print 'Data B, Model B, mean %e, sigma %e' % (pp_bb.mean(), pp_bb.var())
''' output
Data A, Model A, mean 8.257536e-06, sigma 0.000000e+00
Data A, Model B, mean 9.916470e-07, sigma 5.840342e-12
Data B, Model A, mean 1.180532e-06, sigma 8.277128e-12
Data B, Model B, mean 9.830400e-06, sigma 0.000000e+00
'''
def calc_pp(p_x, x, y):
pp = 1.0
for _p_x, _x, _y in zip(p_x, x, y):
pp *= _p_x * (0.8 if _x is _y else 0.05)
return pp
if __name__ == "__main__":
main()
結果と考察
プログラムで計算した結果を表に示します。
A/B さんの生成したデータを A/B さんのモデルで解釈した場合の $P(\boldsymbol{x}|\boldsymbol{y})$ の平均と分散
データ | モデル | 平均 | 分散 |
---|---|---|---|
A | A | $8.26 × 10^{-6} $ | $0 $ |
A | B | $9.92 × 10^{-7} $ | $5.84 × 10^{-12} $ |
B | A | $1.18 × 10^{-6} $ | $8.28 × 10^{-12} $ |
B | B | $9.83 × 10^{-6} $ | $0 $ |
表から分かるように、自分の生成したデータを使った場合の事後確率(厳密には事後確率ではないですが…)の平均は、他人の場合より高く分散は 0 です。この事実の意味することは、自分のことはなんでも良く評価してしまうということです。
そして、他人の生成したデータを使った場合の事後確率には分散(ばらつき)が見られます。これは、データによって良い悪いを判断できているということです(その正当性は別にして)。
つまり、何にでも高い自分の評価と、他人の自分に対する評価との間にギャップがあることが、「自分の事がよく分からない」原因だと思われます。
まとめ(再掲)
- 自分のことは何でも良く分かってしまうと思い込むから、分からない。
- 他人の脳内のモデルを使って自分を知ろう(コードレビューなどは良い例ですね)。
- 自分の脳内のモデルの可塑性を信じて、明日の自分に解釈を委ねてみよう。
※ 本記事の内容は、あくまで一考察です。人間の脳がどう情報を処理しているのかは、この世界の誰もよく分かっていません。いろいろとツッコミをお待ちしています。