多次元正規分布からd次元ベクトルからなる標本を作成(python.numpy)

2クラスの分類問題に対して確率的なアプローチを取るとき, クラス0とクラス1の平均がほぼ同じであるような場合に遭遇することが多い気がする(個人的に). こういう時,k-NNとかは歯が立たないような印象を受ける. さらに,標本平均ってかなりの分類アルゴリズムで用いられると思うので,これにあまり差がない場合,どうしたらいいのかわからない. 素朴な意見として,確率的アプローチを自分なりに考えてみる. 例えばクラス0を生成する分布p0と,クラス1を生成する分布p1を考えるとする (ここで,標本はpx0,px1の混合分布から生成されたと考えるのが素直). そしてこれらが二つとも正規分布だとする. そうすると,パラメータはmu0, mu1, sigma0, sigma1の4つであり,これらを推定することで,分類問題を解くことになる. このとき,上で述べた僕が遭遇する状況はmu0=mu1の場合のことである (=というのは言い過ぎかもしれないが).

つまり,推定するパラメータはmu(mu=mu0=mu1), sigma0, sigma1の3つである. こういう状況を人工的に作り出すためにタイトルの実験を行う. 環境はOSX10.8, Python2.7である.

実装は以下のようになった.rand_vcmでランダムな正定値対称行列を作成して,それを分散共分散行列にしている.

import numpy as np
import matplotlib.pyplot as plt

# create random variance-covariance matrix (positive-definite symmetric matrix)
def rand_vcm(d):
    A = np.random.normal(size=(d,d)) + np.eye(d)
    return A.T.dot(A)

d = 2
mu = np.random.normal(size=d)
sigma0 = rand_vcm(d)
sigma1 = rand_vcm(d)
print mu
print sigma0
print sigma1

n0, n1 = 100, 100
N = n0 + n1
x0 = np.random.multivariate_normal(mu, sigma0, n0)
x1 = np.random.multivariate_normal(mu, sigma1, n1)
x = np.concatenate((x0, x1))

plt.figure()
plt.plot(x0, "ro")
plt.plot(x1, "bx")
plt.show()

プロットした結果は以下のようになった. mixture_test2.png

Summary

今回は,2クラス分類問題の確率的アプローチを検討するために,2つの分布(平均は等しく分散共分散行列が異なる)から標本を作成した. 具体的には,Pythonで多次元正規分布に従う標本を2つ作成した. 次回から,この人口データを利用して平均が同じで,分散共分散行列が異なる2つ分布からなる混合分布から生成されたという仮定のもとでの分類問題を考えていきたい.

広告

ロイターコーパスを使った文書分類(4)|主成分分析後のデータでの分類

前回

の続きです.

準備

bag-of-wordsにより,1つの文書を6459次元ベクトルで表しましたが,これからはデータを視ながら実験をしたいので, 主成分分析によってそれぞれを2次元ベクトルに変換したデータを使います. トレーニングデータ,テストデータをそれぞれ以下の手順でpickleモジュールにしておきます. (get_vocaburary, features, pca_return_matrixは,今までに作成した関数です.)

class1, class2 = "ship", "wheat"
train_fileids = [(fileid, 1) for fileid in reuters.fileids(categories=class1) if "train" in fileid] \
                + [(fileid, 0) for fileid in reuters.fileids(categories=class2) if "train" in fileid]
test_fileids = [(fileid, 1) for fileid in reuters.fileids(categories=class1) if "test" in fileid] \
               + [(fileid, 0) for fileid in reuters.fileids(categories=class2) if "test" in fileid]

V = get_vocaburary([reuters.words(fileid) for fileid, labels in train_fileids])
train_data = [(np.array(features(reuters.words(fileid), V), dtype=float), label) for fileid, label in train_fileids]
test_data = [(np.array(features(reuters.words(fileid), V), dtype=float), label) for fileid, label in test_fileids]
X = np.array([vector for vector, label in train_data])
B = pca_return_matrix(X)
train_data = [(B.dot(vector), label) for vector, label in train_data]
test_data = [(B.dot(vector), label) for vector, label in test_data]
pickle.dump(train_data, open("train_dim2.pickle", "wb"))
pickle.dump(test_data, open("test_dim2.pickle", "wb"))

実験

さて,これでトレーニングデータとテストデータはそれぞれ2次元ベクトルの集合となりました. ここで前回

で,行った代表ベクトルを用いた方法で分類してみます.結果は期待できませんが.

train = pickle.load(open("ship_wheat/train_dim2.pickle"))
x0 = np.array([vector for vector, label in train if label == 0])
x1 = np.array([vector for vector, label in train if label == 1])
mu0 = x0.mean(axis=0)
mu1 = x1.mean(axis=0)

test = pickle.load(open("ship_wheat/test_dim2.pickle"))
labels = [label for vector, label in test]
def predict(x): # using euclidean distance
    if (x-mu0).dot(x-mu0) < (x-mu1).dot(x-mu1):
        return 0
    return 1
predicted = [predict(x) for x, label in test]
print predicted
print labels

print "accuracy:", accuracy(predicted, labels)
print "F measure:", F_measure(predicted, labels)

結果は以下のようになりました.

[1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0]
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
accuracy: 0.50625
F measure: 0.521212121212

これには驚きました.前回,ただのbag-of-wordsで同じ実験をしたとき精度は0.45, F値は 0.02 でした.
それが,PCAで次元削減をするだけで,精度 0.51 ,F値 0.52 まで向上したのです.

Summary

今回は,bag-of-wordsで作成したベクトルを,主成分分析により2次元に圧縮し,それを代表ベクトルのみを使うという非常にシンプルな方法で分類する実験を行った. 結果は驚くべきもので,精度 0.51 ,F値 0.52 となった. 次元を削減するだけで,これほど効果があるのかと,正直驚いた. ここで作成した2次元データは今後も使って行く予定. (補足)ひょっとして,前回コサイン類似度を用いていたが,そこをユークリッド距離に変えてしまったことに起因するかもしれないと思い, 前回のスクリプトを書き直して検証してみたが,やはりユークリッド距離を使ってもF値,精度は変わらなかった.

「近づける」と「小さくする」

どうやら僕は言葉を異常に気にするようだ. 微積分を復習していて,気になったことがある. まずは,微分係数や導関数の定義に出てくる,「近づける」という言葉だ. ちなみに,導関数のある値での関数値が微分係数だと理解している.

さて,「xをaに近づける」というと,「どう近づけるのか」,すなわちその経路が問題になる. x,aがスカラーなら,プラス方向とマイナス方向が考えられる. これが,2次元以上になると,その経路は数えきれないほど存在する. そこで,「近づける」という表現は「小さくする」という表現で書き直される.

また,「近づける」は「小さくする」に比べて抽象度が高いように思われる. 「近づける」をより具体的に表すためにもこれを「小さくする」に書き直される.

ちなみに,「ベクトルxを小さくする」,という表現は意味がわからない(ある方向にそって,というのであればわからないこともない). ここで小さくすると言ったらその対象はスカラーであることとする.

では,どのように「近づける」を「小さくする」に書き換えるのか. 「xをaに近づける」という表現を考える. これを「小さくする」に書き換えるために,|x-a|を考える. 距離だとかいう言葉はあえて使わない. 「xをaに近づける」を「|x-a|を小さくする」に書き換えることは,僕にも納得ができそうだ. ちなみに,「小ささ」は正であることにも納得ができる. これならば,2次元以上のベクトルに対しても使えそうだ. こうすると,いわゆる極限のときに使われる矢印記号\rightarrowを表現できそうな気がするが,まだ気が早いか. x \rightarrow aは「xをaに限りなく近づける」と読むと教わった気がする(これは非常に抽象的で,さっぱりわからないのだが). これを表現するには,「限りなく」の部分をどうするかを考えなくてはならない.
すなわち「どのくらい小さくするか」を考えなくてはならない. また,「xをaに限りなく近づける」の「限りなく」は「近づける」に係っているのだから,決してxはaにはならないことも暗に示されている. これをある正の数\epsilonを用いて表すこととする.

\displaystyle |x-a| < \epsilon,

すなわち,「正の数\epsilonより小さいほどに小さくする」と書くことで,「小さくする程度」を表現する. これにも納得ができそうだ. さらに,決してxはaにはならないことも考慮に入れると,

\displaystyle 0 < |x-a| < \epsilon,

と表される.
これで,「xをaに限りなく近づける」を表現できただろうか,いや,まだな気がする. 「小さくする程度」が表現できた気がするが,「限りなく小さくする」とはどういうことだろう. これはさっぱりわからない.本当にわからない.少し見方を変えてみる.
\epsilonにどんな(小さい)数を選んでも,上の式が成り立つとはどういうことか. これは「xをaに限りなく近づけ 得る 」ということではないか. すなわち「|x-a|を限りなく小さくし 得る 」ということではないか. これはなんとか納得ができそうだ. 「小さくする」と言うと,ある操作のように聞こえるが,「小さくし得る」と言えばまた違ってくる.

まとめ

「近づける」と「小さくする」という表現,また「限りなく」という表現について考えた. 「xをaに限りなく近づける」という表現は意味がわからないものとして,
「xをaに限りなく近づけ得る」という表現について考えることにする(ただし,xはaにはならない). そうするとこれは,
「どんなに(小さい)正の数\epsilonに対しても,0 < |x-a| < \epsilonが成り立つ」 という表現と等価であることに納得した. ここから更なる理解が深まることを期待したい.それにしても無限はわからないことだらけだ.

emacs24にemacs-w3mをインストール(OSX 10.8)

環境はタイトルの通りです.
少しはまったのでポストしておきます.
結論から言えば余計なことをしていたせいなのですが.

w3m

これは一発です.

brew install w3m

emacs-w3m

cvs -d :pserver:anonymous@cvs.namazu.org:/storage/cvsroot co emacs-w3m
wget http://emacs-w3m.namazu.org/emacs-w3m-1.4.4.tar.gz
tar xfz emacs-w3m-1.4.4.tar.gz
cp -pr emacs-w3m/* emacs-w3m-1.4.4
cd emacs-w3m-1.4.4
./configure
make EMACS=/Applications/Emacs.app/Contents/MacOS/Emacs 
make EMACS=/Applications/Emacs.app/Contents/MacOS/Emacs install

上の通りやれば,インストールはOKなはずです.
make時に自分の使っているEmacsを指定してやらないとはまります.
Macには最初から入っているEmacsとおそらく後で自分で入れたであろうEmaacsが混在していると思いますので.
init.elに以下を記述してください.

;emacs-w3mへのパスを通す
(require 'w3m-load)

そしてM-x w3mすると,

Searching for program: No such file or directory, /usr/bin/w3m

エラー発生.w3mがないと言われるのです.下のように自分のw3mを探して下さい.

which w3m
/usr/local/bin/w3m
sudo ln -s /usr/local/bin/w3m /usr/bin/w3m

これで動くはずです