やりたいこと

mutable.Mapが複数あるとして、このMapを連結してしまいたい。

PythonでいうところのUpdateメソッドを利用したい。

書き方

++=メソッドを利用する。


val Map1 = mutable.Map[String, Int]()
val Map2 = mutable.Map[String, Int]()

Map1 += ("a" -> 1) Map2 += ("aa" -> 11, "bb" -> 22)

として、Map1とMap2を用意しておき、


scala> Map1 ++= Map2
res2: Map1.type = Map(bb -> 22, a -> 1, aa -> 11)

で、Map2がMap1にくっつけられた(^^)/

ちなみに、コップ本を探したら、17.3.1の節にしっかりサンプルが載っていた。

scalaの命名規則 - 新kensuke-miの日記

| コメント(0)

あるとき、ふと「そういえば、scalaの命名規則ってどうなっているんだろう?」と思ったので、調べてみた。

すると、割りと簡単に見つかった。「キャメルケースを使いなさい」とのことだ。

原文をここからもってくると、


Scalaは「キャメルケース」命名規約を用います。
(中略)
アンダースコア(_)はScalaの文法で特別な意味を持つため,使わないように厳しく自制してください。

いかにもマニュアルっぽい日本語なのだが、かなり強い言い方で、アンダースコアを使わないように注意を呼びかけている。

と、いうわけで、scalaではキャメルケースでオブジェクト名をつけましょう。という話でした。

やりたいこと

コードを別ファイルに分離したい。

pythonでいうところの、モジュール化を行ないたい。

どうやって解決するか

クラスをパッケージ内に定義して、別ファイルに分離する。

別ファイルに定義したクラスの書き方


package パッケージ名

class クラス名{ クラスの記述 }

別ファイルに定義したクラスを読み込むとき


import パッケージ名.クラス名   // クラスの読み込み
 

Javaだとパッケージ名とクラス名は同じ名前でないといけないらしいが、scalaではパッケージ名とクラス名が異なっていてもよいとのことだ。

参考

witten-bell smoothingを実装することがあったのだが、「えっ、何するんだっけ?」となってしまったので、忘れないうちに書いておく。

そもそもスムージングとは?

確率的言語モデルを使うときに役に立つ便利道具。

もっともベーシックな言語モデルは学習コーパスから、モデルを構築する。

で、N-gram系列の確率を計算して、実際に利用する。

でも、実際は、実際に利用するN-gram系列が学習コーパスに出現しないことは、普通にありえる。

じゃあ、学習コーパスにほとんど出現しないN-gram系列の確率はどうすんの?たまたま学習コーパスに出現しなかっただけじゃないの?

「それは違うよね。学習コーパスに出現しなかっただけで、真の確率ではないよね」という思想で、学習コーパスにほとんど出現しなかったN-gram系列にも確率を割り当てる作業のことをスムージングという。

例えば(bi-gramの例)

Tokyo city is big city in JapanNara city is big city in Japanというテスト文を考えてみる。 学習コーパスが東京に偏ったコーパスだったとしたら、学習コーパスの中にTokyo cityはよく出現するが、Nara cityはそんなに出現しない。

でも、確率は学習コーパスに依存するので、このままだとNara cityが低確率になってしまう。(もしかしたら0になってしまうかもしれない。)

で、Nara cityに確率をもうちょい割り当ててやりましょう。

って感じ。

どうやって確率を割り当てるの?

線形補完っていう黒魔術を使う。

黒魔術の正体はただの式なのだが、bi-gramの場合、式は

P(w_i | w_{i-1}) = lambda_2 * P-ML(w_i | w_{i-1}) + lambda_1 * P(w_i)[1]

ただし、

P(w_i) = lambda_1 * P-ML(w_i) + (1 - lambda_1) * (1 / N)

P-ML(w_i | w_{i-1})が学習コーパスでのbi-gram確率

P-ML(w_i)が学習コーパスでのunigram確率

Nがテストコーパスの総語数

で、lambda_1lambda_2は何?っていう話になるのだが、このパラメタを調整できるのが、線形補完のミソ。

で、lambdaを推定するために、witten-bell smoothingが出現する。

つまり、witen-bell smoothingはlambdaを計算するアレ

witten-bell smoothingの基本的な考え方は、「後に続く単語の種類数も考えてやろうよ」という考え方。で、種類数が大きほど、lambdaは低くなるはずだよね。と考える。

式は

lambda = 1 - (unique_after(w_i) / (unique_after(w_i) + freq(w_i))[2]

1 - lambda = unique_after(w_i) / (unique_after(w_i) + freq(w_i)) と紹介しているスライドもあるけど、要は同じ

ただし、

unique_after(w_i)w_iの後(+1インデックス)に出現する単語種類数を返す関数

freq(w_i)w_iが出現する回数を返す関数

天下りになるが、ここにめっちゃわかりやすいpdfの資料があったので、この例に従う。

splitconstantは両方ともEuroparlコーパスで993回出現する。

でも後に続く単語も考えてみると,splitの後(+1インデックス)には9種類の単語しかなかった。対して、constantの後には268種類の単語があった。

後につづく(+1インデックス)に出現する種類が多い方ほど、lambdaも小さくすべきだろう。(lambdaを小さくすると、確率が低くなるので)

witten-bell smoothingの式では、後につづく単語種類数が増えると、分子が大きくなって、1 - (unique_after(w_i) / (unique_after(w_i) + freq(w_i))の数値が小さくなる仕組みになっている。

で、計算されたlambdaを線形補完式[1]に代入して、bi-gramの確率を計算する。

まとめ

これで、黒づくめの組織に追い詰められたときも「へっ・・・バーロォ・・・言語モデルはスムージングされてるんだよ・・・」ってセリフは吐けると思います。

やりたいこと

movable typeで記事を書くときにmarkdownを使うが、コードを表記したい。

でも、毎行4つのスペース挿入はかったるいし、複数行コードを記載する表記(バッククオート3つ)は機能しない。

解決策

<pre><code>タグを使用する。

こんな感じ。


   <pre><code>
         print('hoge')
    </pre><code>

よく見たら、ちゃんとここに書いてあった。

やりたいこと

メソッドのパラメタで、Mapを使いたい。

で、Mapのキー(または値)の型を明示的にしておきたくない。

いくつかの種類の型をキー(または値に持てるようにしておきたい)

解決法

Higher Kinded Typesを使う。

日本語にすると、「高階型変数」というらしい。。。(絶対に、一発で変換できない漢字だ)


def func(map_arg: Map[String,Float]): Unit = println(Map.keys())

val map_input = Map[String, Int]
func(map_input)

とかすると、「定義されたパラメタと入力のパラメタが違うで」と怒られる。

パラメタを抽象化(?)するには


def func(map_arg: Map[String,_]): Unit = println(Map.keys())

val map_input = Map[String, Int]
func(map_input)

とすると通った。

こういう風に(どういう風なんだ。。)パラメタを抽象化(?)してしまうことをHigher Kinded Typesと呼ぶのだとか。

Javaでも同じ表記でいけるらしい。

Javaをほとんど書いたことないので、知らなかった。。。

いま、巷で話題(3年くらい前からだいぶ話題だけど)のDeep learningをア◯でも使えるpylearn2を使って見る。

Deep learningとは?

一言で言うと「教師なしのニューラルネットをいっぱいつなげて多層ネットワーク化したもの。」

いままでは

前処理職人の丹精を込めた特徴量作り → 分類器(SVMとか、ロジスティック回帰とか、なんでもいいので関数)の学習

だったのが、

黒魔術で特徴量作り → 分類器(SVMとか、ロジスティック回帰とか、なんでもいいので関数)の学習

にできる。

黒魔術をもうちょっと紐解く

「黒魔術」って言ってるところでやっていることは「ベストな写像関数の学習」

もう少し、言葉を厳密に表現すると、「入力/ノイズ付き出力の変換をうまく表現できる関数の学習」

この関数はできるだけ良い[1]条件で別の空間に写像する。

なので、この関数が学習できると、特徴量空間に射影する関数を学習できたことになる。

[1] 「何が、どういいのか?」はやはり黒魔術である

え、そんなのわかんねえし、という人向け

「そんなのわかんねえよksが」っていう人は「画像をいっぱい入力したら、似たところを自動でとってきてくれる便利なツール」って認識しておけばいいんじゃないっすかね(鼻くそほじほじ)

お手軽Deep learningをやってみよう

口でいうのは簡単だが、それをコードを落とすとなると難しい。

しかし、No problemだ。どっかモントリオール大学の優秀な学生達が、Deep learning用汎化ライブラリ(Theano)を作成してくれた。

しかし、これでもやや敷居が高い。無能な私にはチュートリアルで終わるのが精一杯だった。

という状況の中、さらに便利なツールが登場した!それがPylearn2である。

Theanoのラッパーとして、アホでも使える程度の簡単さにしてくれた。まだ、開発版ではあるが、十分に遊べるレベルになっている。

今回は、Pylearn2のテスト使用ということで、ちょっと遊んでみた。

顔の特徴量抽出をしてみよう

Deep Learningは元々は画像認識分野から出てきた(Hintonという偉いおっさんが初めて使った)。

その時は、画像認識(画像を入力して、写っている物の名前を当てるタスク)で使われていた。

そこで、脅威の性能を出してしまったばかりに、例によってGoogleが興味をもって、ここにGoogleネコが誕生した

この時のGoogleのアイディアは「いっぱいネコ画像を入力に与えたら、ネコの顔だけ抽出してくれる関数を学習してくれんだろ。バーロ」みたいな感じだった。

ここに歪んだ欲望と男のロマンをちょっとだけ加えると次のようになる。

「いっぱい好みの女優さんの顔画像を与えたら、自分の好みの顔を学習してくれんだろ。バーロ」

と、いうことでやってみた。きっとHintonも喜んでくれるだろう。

pylearn2で顔パーツを学習してみよう

ごくごく簡単にpylearn2を使うまでの手順を書くと、こうなる

  • (前提) python, git, pipを使える状態。numpyとscipyがインストールされている。
  • 1 theanoをインストールする
  • 2 pylearn2をインストールする。(とはいってもgit cloneをする)

python, git, pip, numpy, scipyの使用準備の説明は今回、省く。

theanoのインストール

pip install theano

でおしまいなはず(設定がうまくいっていれば)

pylearn2のインストール

インストールと準備の段階では以下のサイトも参考になる

  • http://deeplearning.jp/?p=196

  • http://shower.human.waseda.ac.jp/~asaitaku/toolsManual/_build/html/python/pylearn2/main.html

必要なライブラリ

  • PIL
  • PyYAML
  • IPython
  • Cython

全部、pip installコマンドで楽勝である。

ただし、PILはpip install PILではダメでpip install pillowと打たないとダメらしい。参考

pylearn2のインストール

次の2つのコマンドを実行すれば、うまくいく(はず)

  • git clone git://github.com/lisa-lab/pylearn2.git

  • python setup.py develop

pylearn2のテスト

pylearn2の使い方は、簡単にまとめると、

  • データセットを用意する(.plkなり、.csvなり)
  • 訓練データのパスと訓練方法のレシピを記述する(*.yamlファイルに記述)
  • train レシピ書いたyamlファイルを実行

このサイトの下の方に、CIFAR10とMNISTという画像データセットでのサンプル実行例がある。

  • http://deeplearning.jp/?p=196
  • http://shower.human.waseda.ac.jp/~asaitaku/toolsManual/_build/html/python/pylearn2/main.html

pylearn2で、女優さんの顔から特徴量を抽出してみよう

画像の学習のために、pylearn2を更に簡単に使えるコードが用意されてる。ここ

このコードがあれば、画像から色情報csvファイルへの変換、レシピyamlを使った学習までとっても簡単。

やってみること

  • DMM女優名鑑から好み(主観)の女優さんの画像を集めてくる。
  • 女優さんの画像を入力にして、Deep learningでモデルを構築してみる。
  • 共通する特徴量を抽出する

顔画像の収集

入力画像はDMM女優名鑑から集めてくる。顔画像は155*155で保存されているので、好都合。

wgetコマンドで簡単に画像を落とせる。

URLの構成規則はhttp://pics.dmm.co.jp/mono/actjpgs/姓_名.jpgになっているのと、割りと簡単。

マシンは型落ちMacBookなので、学習量を少なくするため、10枚ぐらいに

集めてきた顔画像はこんな感じ。(あくまで私の好みです) スクリーンショット 2014-11-04 4.35.30.png

画像のグレースケール化とcsvへの変換

画像のグレースケール変換は、PILライブラリを使って簡単にできる。

こんなコードを書いた。

#! /usr/bin/python

from PIL import Image
from PIL import ImageOps
import os
import sys

if __name__ == "__main__":
    if len(sys.argv)==3:
        input_path = sys.argv[1]
        output_path = sys.argv[2]
    else:
        sys.exit("Need 2 args. Input ans Output path")

    input_image = Image.open(input_path)
    output_image = ImageOps.grayscale(input_image)
    output_image.save(output_path)

色情報csvファイルへの変換はここのスクリプトを使う。

python img2csv.py グレースケール変換した.jpgのリスト train.csv

を実行する。

クラスの用意とパス設定

クラスファイルをpylearn2で呼び出せるようにしておく。

ここからクラスが記述されたファイルを落としてきて、pylearn2/datasets/に配置する

環境変数は

export PYLEARN2_DATA_PATH=train.csvとgrbm.yamlが置いてあるパス

export PYLEARN2_VIEWER_COMMAND="opne -Wn"[2]

[2]はmac専用のコマンド。windowsの場合は・・・・知らね

モデルの学習

今回はグレースケール化して、サイズも同だし、いろいろ考えるのも面倒なので、「制約付きボルツマンマシン(RBM)」で片付ける。(コード中のアルゴリズムではgrbm)

カラー化していれば、grbm_smd、サイズが違えばmultimodalを使った方がいいだろう(って、公式マニュアルに書いてあったお。)

実行するコマンドは、grbm.yamlが置いてある場所で、

train.py grbm.yaml

を実行するだけ。

逐次学習をしている様子がコマンドラインに流れていくはず。

1イテレーションはこんな画面 スクリーンショット 2014-11-04 4.29.53.png

学習結果の確認

学習した関数の出力を画像で表示するには

show_weights.py grbm.pkl

を実行する。

結果はこんな感じだった

tmp_aP8j6.jpg

夜に見たら、おしっこちびりそう・・・・

椎名りくが強すぎワロタ

いくつかの特徴量では、うまく人間共通の顔パーツを学習できている様子。

あくまで考察だけど、椎名りくの画像だけ他と違って、セーラー服で、ラインが入っていたため、外れ値が多かったのでは・・・とかそんなことを思いました。

ちなみに私はブレザー派です

まとめ

Deep learningって楽しいね!

やりたいこと

データフレームをグループ化して集計(平均)し、その上で、各グループについて上位N件を取得したい。

つまり、やることを分解すると、

  • 1 グループ化して数値集計(今回は平均)
  • 2 集計した数値で、各グループごとに上位N件取得

1 グループ化して集計

pandasの場合、groupbyメソッドが用意されており、これで解決できる。

dfをデータフレームとした時、

df.groupby([列番号]).集計関数

と記述する。

戻り値は、集計済みのデータフレームである。

2 集計した上で、各グループごとに上位N件

これが頭を悩ませた。

と、いうのも、データフレームのままでやろうとすると、上位N件だけ残す。ということができないからだ。

各グループで、違う項目が上位N件に来るので、表という形式をもっている限り、どうしてもソートはできない。 (各グループごとに、数値の順位を記載した行を用意する。という解決索はあるが、スマートでない気がした。)

そこで、一度、データフレームをバラして、dictにしてから、グループごとにソートする。ということを行なった。

で、面倒なのだが、ソートしてから、またデータフレームにして、出力をした。 (こんな面倒なことやらんでも、jsonで渡せたら最良だったのだが、OUTPUTにexcelで使えるデータを求められていたので、どうしてもcsvで保存する必要があった)

まずmelt

元のデータは行がキー(グループID)、列が値のインデックス。という巨大なデータフレームなので、まずは、これを変形する。

pandas.melt(df, id_vars=['列名'])

でmeltができる

dictにする

さて、これがちょっと面倒くさい。

と、いうのも、一発で簡単にdictに変換できなかったからだ。

そこで、forループを回して、行ごとに処理をすることにした。

my_dict = {}

for x in range(len(df)):                                                                                       

    row_info = df.iloc[x]
    line_info = row_info.tolist()
    c_key = line_info[0]  # キーになる要素
    currentvalue1 = line_info[1]  #  値の要素1
    currentvalue2 = line_info[2]  #  値の要素2

    if c_key in my_dict: my_dict[c_key].append( [currentvalue1, currentvalue2] )
    else: my_dict[c_key] = [[currentvalue1, currentvalue2]]

ソートして上位N件取得

上の処理でつくったdictの各キーで、ソートをし、上位N件だけ残したdict(キー:インデックス名、値:数値)を値にして、新しくdictを作ることにした。(キーは同じ)

わざわざ2重のdict構造にするのは、この後の処理でpandasのメソッドを使って、一発で目的の表に変換できるからだ。

top_n_map = {}

for key in df_map:
    values_list = df_map[key]
    values_list.sort(key=lambda x:(x[1]), reverse = True)

    value_map = {}
    for item in values_list[0:9]:
        value_map[item[0]] = item[1]  # インデックス名をキー、数値を値にして、2次元目のdictを保存
    top_n_map[key] = value_map

データフレームにもどして、csvに保存

{キー1:{キー2:値} }

という形のdictなら、一発で下の表に変換してくれる。

_____| キー2 | キー2 | .....
キー1 | 値     |    値     | .....
キー1 | 値     |    値     | .....
キー1 | 値     |    値     | .....

これをする命令は

pandas.DataFrame.from_dict(dict)

まとめ

グループ化集計して、上位N件だけ残す処理をした。

が、ふと思ったのは、別にmeltはしなくてもよかったような気がする。

別にmeltしなくともdict {キー:list [ list [インデックス名, 数値] ] }の形は構築できる。

無駄足を踏んだ気がするが、meltをどこかで使う機会があるかもしれないので、記録に残しておく。

恥ずかしながら、パッケージユーザーなもので、PCAがきちっとwかっていない状態で使おうとしてハマっていた感があった。

そこで、「何をしているか?」を明確にして、扱ってみることにする。

ちなみに、この記事で扱ってるscikit-learnはバージョンが0.15です。

他のバージョンだと、メソッドが存在してなかったりするんで、ご注意を。

scikit-learnでやってみる

まず、scikit-learnの基本的な使い方にそって、やってみる。

In [55]: a = np.array([[ 0,  1,  2,  3, 4, 2, 4, 1],[ 2, 1, 4, 2, 4,  5,  6,  7],[ 8, 2, 1, 2, 1 ,9, 10, 11]])

In [55]: pca = PCA(a, n_components=8)

In [55]: pca.fit(a)

In [56]: pca.components_
Out[56]: 
array([[ 0.50488641,  0.06499704, -0.08008693, -0.05745209, -0.19499111,
         0.43234443,  0.37489233,  0.60470071],
       [ 0.3453956 ,  0.1116729 , -0.65966052,  0.16232091, -0.33501871,
        -0.04027111,  0.1220498 , -0.52723383],
       [ 0.40199019,  0.14317705, -0.06778525, -0.19504746,  0.18034548,
         0.27022208, -0.81834129, -0.00624017]])

In [57]: pca.components_.shape
Out[57]: (3, 8)

In [58]: pca.transform(a)
Out[58]: 
array([[ -7.57714864e+00,   1.42522380e+00,  -2.22044605e-16],
       [ -9.95075408e-01,  -2.40574369e+00,   1.59594560e-15],
       [  8.57222405e+00,   9.80519892e-01,  -6.21031004e-16]])

In [59]: pca.transform(a).shape
Out[59]: (3, 3)

In [272]: pca.explained_variance_
Out[272]: array([  4.39621273e+01,   2.92676162e+00,   3.36224570e-31])

scikit-learnの一部だけ流用してやってみる

では、自分できちんとやってみよう。

PCAの手順はそんなに難しいことでない。手順的には

1 入力行列(n * m)の共分散行列(m * m)を作成する

2 共分散行列を固有値分解する。固有値(1 * m)と固有ベクトル(m * m)に分離できる

3 固有値の高い順に欲しい次元数だけ、固有値に対応する固有ベクトルを取る。

4 とった固有ベクトルで入力行列を写像する。(n * m と m * m の内積計算)

これをscikit-learnを使ってやってみよう。

    In [61]: cov_matrix = pca.get_covariance()  # 共分散行列の生成

    In [13]: import numpy as np

    In [14]: [E, V] = np.linalg.eig(cov_matrix)  #  固有値分解
    Out[16]: 
    array([[ 0.36158968, -0.65653988, -0.58099728,  0.31725455],
       [-0.08226889, -0.72971237,  0.59641809, -0.32409435],
       [ 0.85657211,  0.1757674 ,  0.07252408, -0.47971899],
       [ 0.35884393,  0.07470647,  0.54906091,  0.75112056]])

これで、共分散行列の固有値分解ができた。

まず、主成分得点に対応する固有値から確認してみる。

In [67]: E
Out[67]: 
array([  4.39621273e+01 +0.00000000e+00j,
         2.92676162e+00 +0.00000000e+00j,
         1.76593839e-15 +3.86799686e-15j,
         1.76593839e-15 -3.86799686e-15j,
        -2.29569145e-15 +0.00000000e+00j,
        -1.29253764e-16 +3.91441638e-16j,
        -1.29253764e-16 -3.91441638e-16j,   5.35881238e-18 +0.00000000e+00j])

In [102]: projection_matrix = V[0:2,]  # 固有ベクトルから2次元までの空間をとってくる
In [103]: projection_matrix
array([[-0.50488641+0.j        ,  0.34539560+0.j        ,
         0.19411192+0.41056554j,  0.19411192-0.41056554j,
         0.07143826+0.j        , -0.01791936-0.00421049j,
        -0.01791936+0.00421049j, -0.00091420+0.j        ],
       [-0.06499704+0.j        ,  0.11167290+0.j        ,
        -0.76012933+0.j        , -0.76012933-0.j        ,
         0.20260054+0.j        ,  0.03205764+0.06085498j,
         0.03205764-0.06085498j,  0.00976708+0.j        ]])

ところで、思い出していただきたいのは、scikit-learnの演算結果である。

scikit-learnで演算した結果で、主成分(つまり固有値)を表示すると、

    In [272]: pca.explained_variance_
    Out[272]: array([  4.39621273e+01,   2.92676162e+00,   3.36224570e-31])

おいおい、自分で算出してみたやつと違うで!

pca.transfrom()メソッドの結果をみても

    In [59]: pca.transform(a).shape
    Out[59]: (3, 3)

勝手に、3次元に落とされている。

どうやら、scikit-learnは累積寄与率ってので、勝手に切った結果を返しているらしい。

まぁ、ちょっと試すくらいならいいんだろうが、あまり自由度は高くないようだ。

自分でやった部分が正しいのか?確認してみる

一応、この固有値と固有ベクトルが正しいのか?の確認をしてみる。

と、その前に、固有値分解の基本をちょっとおさらいしてみる。

Aを元の行列、xを固有ベクトル、λを固有値とする。

この時、Ax = λxを満たすλとxを見つければよい。

xが0にならないためには、| λE - A | = 0の固有方程式を解けばいい。

この結果、スカラ値のλとベクトルのxが算出される。

行列演算で行うときには、行列Aの固有ベクトルを束ねた行列をX、固有値を対角成分にもつ行列をDとした時、

AX = XDとなるので、A = XDX^(-1)とすると、元の行列Aに復元できる。

ここまでの参考はこのサイト

なので、この検算をしてみよう。

In [332]: cov_matrix
Out[332]: 
array([[ 11.55555556,   1.55555556,  -2.44444444,  -1.11111111,
         -4.66666667,   9.55555556,   8.44444444,  12.88888889],
       [  1.55555556,   0.22222222,  -0.44444444,  -0.11111111,
         -0.66666667,   1.22222222,   1.11111111,   1.55555556],
       [ -2.44444444,  -0.44444444,   1.55555556,  -0.11111111,
          1.33333333,  -1.44444444,  -1.55555556,  -1.11111111],
       [ -1.11111111,  -0.11111111,  -0.11111111,   0.22222222,
          0.33333333,  -1.11111111,  -0.88888889,  -1.77777778],
       [ -4.66666667,  -0.66666667,   1.33333333,   0.33333333,
       2.        ,  -3.66666667,  -3.33333333,  -4.66666667],
       [  9.55555556,   1.22222222,  -1.44444444,  -1.11111111,
         -3.66666667,   8.22222222,   7.11111111,  11.55555556],
       [  8.44444444,   1.11111111,  -1.55555556,  -0.88888889,
         -3.33333333,   7.11111111,   6.22222222,   9.77777778],
        [ 12.88888889,   1.55555556,  -1.11111111,  -1.77777778,
         -4.66666667,  11.55555556,   9.77777778,  16.88888889]])

In [333]: origin_matrix = np.dot(np.dot(V ,np.diag(E)), np.linalg.inv(V))    # A = XDX^(-1)の演算

In [334]: cov_matrix.astype("int32") == origin_matrix.astype("int32")    # 型が違ったぽいので、統一して比較演算
Out[334]: 
array([[ True,  True,  True,  True,  True,  True,  True,  True],
       [ True,  True,  True,  True,  True,  True,  True,  True],
       [ True,  True,  True,  True,  True,  True,  True,  True],
       [ True,  True,  True,  True,  True,  True,  True,  True],
       [ True,  True,  True,  True,  True,  True,  True,  True],
       [ True,  True,  True,  True,  True,  True,  True,  True],
       [ True,  True,  True,  True,  True,  True,  True,  True],
       [ True,  True,  True,  True,  True,  True,  True,  True]], dtype=bool)

同じになった。

まとめ

scikit-learnの一部だけ使って、PCAをやってみたよ。

scikit-learnには、好きなだけ次元数を残す。っていう機能がないらしいので、自分でやるの大切。

でも、scikit-learn使ったとこは、共分散行列を算出しただけだから、実は無理してscikit-learnを使う必要もなかった・・・という話。