scikit-learn (自習)

もう借りてきた本を返却しちゃったので、書名は失念しちゃったけど、外国人相手に やってはいけない仕草を、5段階に分けて解説してた本が有った。

これをやると殺されても文句を言えないって言う超危険なサインに始まって、個人的には 微妙ーーと思うものまで、150項目ぐらいに渡って解説されてた。

外国人と言っても、そのバックグラウンド(国とか人種とか宗教等)によって皆違うから 一概には言えないけど、著者は米人。バイアスがかかっている事を承知で、割り引いて 読む必要がある。

ネットで検索してみると、たとえば、 海外でそのジェスチャーは危険かも!覚えておきたいNGハンドサイン12個なのが出てきた。

その著者によれば、中指を立てるのは絶対にタブーと言ってた。それから写真を撮る時に よくやる仕草、ピースサイン。これは、餓鬼のやる仕草なんで、大人だと品格を疑われるぞと 危険視してた。

後は、かわいい子供の頭をなでるやつ。日本人思わずやりそう。撫でていいのはペットだけって 注意喚起してたぞ。また、8等身美人を誉める言葉として、頭が小さい、あるいは顔が小さい(小顔) ですねってのがある。

これ、相手はどう取るか? 顔が小さい ー> 頭が小さい -> 脳が小さい -> 馬鹿 と、解釈される そうですよ。(この連想で行けば、うちのは利口って事になるな)

眼は口程に物を言うってのを思い出したぞ。脳コンピュータの入出力装置は顔になるな。 だから、顔の表情を深層学習で読み取るのか。これぞ、manーマシン・インターフェースだな。

etags

前回やったscikit-learnのPythonコードで、疑問に思った所が有るんで、sourceに 当たってみようと思ったんだ。ソース探検モードのスイッチが入ったって訳。 お供はemacsなんだけど、すいすい飛び回れるのかなあ?

そんな訳で何かヒントは無いものかとREADMEやらを見てたら、Makefileに面白い記述を 見つけた。なんとぴったりなやつじゃん。生憎、現場は人里離れたOpenBSDな所なんだけどね。

ctags:
        # make tags for symbol based navigation in emacs and vim
        # Install with: sudo apt-get install exuberant-ctags
        $(CTAGS) --python-kinds=-i -R sklearn

ctagsってC語しか使えないと思っていたら、拡張されてるのか。portsに入っているのかと 思って探してみたら、devel/ectags なんていう思わせぶりな奴が見つかった。

pkg/DESCRしたら、

Support for Assembler, AWK, ASP, BETA, Bourne/Korn/Z Shell, C,
C++, COBOL, Eiffel, Fortran, Java, Lisp, Makefile, Pascal, Perl,
PHP, Python, REXX, Ruby, S-Lang, Scheme, Tcl, and Vim is present;
in particular, the C/C++ parser is far less easily fooled by pre-
processor conditional constructs than many other ctags(1) imple-
mentations.

これを入れると、emacs用のtagを作成出来るようになるんかな? pkg/PLISTしたら、ctagsは 有るのにetagsは列挙されていない。上のMakefileの案内はガセネタ?

portsのMakefileを確認して、原本の場所を発見。 exuberant-ctags configureする時に、etagsを作るように指示すればいいのだな。出来上がったetagsは 下記のように使う。(etags -R *.py でもいいかな)

[ob: sklearn]$ find . -name '*.py' | xargs etags

で、調べたかったのは、train_test_splitの分割手法。この関数は何処に在るんや? そんなの簡単。折角作ったTAGSファイルが有るんだから、それをsearchして、前の方に 辿っていけば、cross_validation.py なんてのに引っかかるよ。

で、スイスイと見てくと、分割サイズを指定しないと25%のデフォルトだとか書いてあって、 そこを通りすぎて、ShuffleSplitなんて所に来て、

    .. deprecated:: 0.18
        This module will be removed in 0.20.
        Use :class:`sklearn.model_selection.ShuffleSplit` instead.

こんなdocに出くわしたりするよ。まだ、ちょっとPythonのイテレータ回りの勉強が 足りない事が判明したよ。とほほ。Pythonのイテレータとジェネレータ

etagsにちょいと補足しとく

emacs入れれば、ついてくるじゃん。専用命令ですからemacsを使う人しか用が無い。 上では、TAGSファイルを検索して、開くべきファイルを探し出していた。面倒っぽい。

いきなり、M-. して、関数名を入力すれば、後はemacsが良きに計らってくれるぞ。 でも、Python場合は、関数名の衝突を防ぐためにモジュールが導入されてるじゃん。 同名の関数が有ったらどうする?

そんな事もあろうかと、C-u M-. すれば、同名の関数が定義されてる別なファイルを 探し出して、飛んでいくよ。

TAGSファイルを検索して、ファイルを開いてくれる。このTAGSファイルを対象にした 動作は、M-x tag-search って関数が担ってくれているよ。便利な仕組みだな。

ipython config

ipython profile create
vi ~/.python/profile_default/ipython_config.py
c.TerminalInteractiveShell.editor = 'emacs'
# List of files to run at IPython startup.
c.InteractiveShellApp.exec_files = ["/home/my_name/bin/python_startup.py"]

とかやると、デフォのconfigを設定しておける。ipythonって、複数行入力出来ない みたいなんで、editorに書いてそれを実行する。だったら、ジュビター使えってのは、 CUI人間には耐えられないわけですよ。

勝手に分類

今までは、教師有りデータを使って学習し、その頭を利用して未知のデータを分類する事を やった。

ワインソムリエが舌と鼻と目で品質ランクを決めたワイン4000本強を使って学習し、それを 元にコンピュータに利き酒させた訳だけど、余り良い成績は得られなかった。映す価値も ない芸人に分類されちゃうんでしょうかね。

だったら、コンピュータが真っ新な所から分類させてみよう。似た者同士が群れと言うか、 クラスターを作るに違いない。

こういうのをクラスタリング分析と言うそうな。世の中の進歩で大量にデータが得られる けど、それを一つづつ吟味して分類なんてやってられないぞ。大体、4000本のワインの 利き酒なんて、どんだけ時間がかかるんだ。一日10本づつ、コンスタントに鑑定したと すると、10年以上かかる。だったら、みんなで手分けしてやればいいじゃん。こういう 官能検査は主観的だからなあ。非現実的だよ。

毎年開かれる、利き酒大会が有るんだけど、20種を確かめると、べろんべろんに酔っぱらって、 センサーが狂ってしまうのを身を持って経験してますよ。

ワインのデータは、化学実験室のフラスコや試験管で分析? 今の時代なら、田中フェローが居る島津さん所のガスクロマトグラフィーを使うなりして分析すれば良い。

後は、scikit-learnにお任せあり。よう知らないのでぐぐる。

scikit-learn による最も基本的なクラスタリング分析

scikit-learn によるクラスタリング (2)

scikit-learn でクラスタ分析 (K-means 法)

Scikit-learnのモデルをまとめてみた

Machine Learning with Scikit Learn (Part I)

高次元データの次元削減および2次元プロット手法

そして、でっちあげた。もとえ、組み立てた。

[ob: sci]$ cat cls.py
import pandas as pd
from sklearn import preprocessing
from sklearn.cluster import KMeans
from sklearn.mixture import GMM
from sklearn.decomposition import PCA

def get_wine(file):
    a = pd.read_csv(file, delimiter=';')
    return a.iloc[:,:-1], a.iloc[:,-1]

X, Y = get_wine('white.csv')
Xs = preprocessing.scale(X)

pca = PCA(n_components=5)
pca.fit(Xs)
Fs = pca.transform(Xs)

model = GMM(n_components=7)
# model = KMeans(n_jobs=-1, n_clusters=7, random_state=30)
model.fit(Fs)

labels = model.predict(Fs)
for label, person in zip(labels, Y):
    print(label)

次元削減で、5次元データに変換。それをガウス分布になるように考慮して7つに分類。 こんなに分けなくても、松竹梅と極上ぐらいでいいんでないかい。

sakae@debian:~/ML/sci$ python cls.py | sort | uniq -c | sort -r
 :
  warnings.warn(msg, category=DeprecationWarning)
/home/sakae/anaconda3/lib/python3.5/site-packages/sklearn/utils/deprecation.py:70: DeprecationWarning: Function log_multivariate_normal_density is deprecated; The function log_multivariate_normal_density is deprecated in 0.18 and will be removed in 0.20.
  warnings.warn(msg, category=DeprecationWarning)
   1333 3
    809 0
    802 5
    785 6
    708 1
    348 2
    113 4

こんな警告が出てきてるんで、近いうちにscikit-learnもverupするんですかね。で、データは 正規分布になってるっぽい。

sakae@debian:~/ML/sci$ python cls.py | sort | uniq -c | sort -r
   1596 1
   1513 0
   1321 2
    468 3

こちらはKMeansを使って、4つに分類したもの。数が少ない物は極上品かな?

ワインはもういい

いい加減にワインに酔ってしまった。次元が多すぎる。そこで、血圧・脈拍データに 切り替えて、次元の縮小を確認してみる。

In [22]: pca.fit(X)
Out[22]:

PCA(copy=True, iterated_power='auto', n_components=2, random_state=None,
  svd_solver='auto', tol=0.0, whiten=False)

In [23]: Fs = pca.transform(X)

In [26]: X.iloc[0:3, :]
Out[28]:

    hi  lo  pl
0  128  73  55
1  111  63  64
2  126  73  52

In [29]: Fs[0:3, :]
Out[29]:

array([[ -5.37728702,   2.12638345],
       [ 15.99384131,   0.17416956],
       [ -4.46218255,   4.42374187]])

これ、3つの次元を縮小して2つにしてみたセッション図。このデータを使って、クラスタリング しても、正しい群れにほぼ分類出来た。(朝の測定か夜の測定か正しく推測)

縮小されたデータには、元の3次元のデータが隠されているんだな。但し元のデータからは 想像出来ないものになっている。果たしてこのからくりは?

なお、縮小されたデータの各列については、平均がZERO、分散が1のデータになってたぞ。

次元縮小

上で出てきた次元縮小って、どういう根拠で出来上がっているんだろう? 機械学習と言えども 根っこに統計学あるはずなんで、統計言語R PCAで検索してみたよ。

そしたら、こんなのが、 主成分分析

そして、 R vs Python:データ解析を比較 同様な趣旨で、 統計: Python と R で重回帰分析してみるとかも。これはもう、GNU Rにどんな関数が有るか調べておくしかあるまい。 R 基本統計関数マニュアルが基本に なるな。

> bld <- read.csv("bld.csv", header=T)
> bld
     hi lo pl
1   132 85 55
2   109 73 60
     :
99  111 72 61
100 114 66 68
> re <- prcomp(bld, scale=T)
> summary(re)
Importance of components:
                          PC1    PC2     PC3
Standard deviation     1.4750 0.7357 0.53202
Proportion of Variance 0.7252 0.1804 0.09435
Cumulative Proportion  0.7252 0.9056 1.00000
> biplot(re)
> round(re$rotation, 4)
       PC1    PC2     PC3
hi -0.5905 0.4703  0.6559
lo -0.6069 0.2770 -0.7449
pl  0.5320 0.8379 -0.1218
> round(re$x, 3)
          PC1    PC2    PC3
  [1,] -2.604 -0.212  0.153
  [2,]  0.307 -1.070 -0.321
   :
 [99,]  0.360 -0.860 -0.101
[100,]  1.335  0.102  0.570

R、R言語、R環境・・・・・・これ、分析の 仕方が一望できる良サイト。

統計科学研究所の、統計アラカルトに ある、人の祖先の推定がワクワクするぞ。

次元縮小、なかなか面白いな。その底辺には、線形代数学の行列の部とかをみると、 無意味?に話が進む、行列の固有値だとか行列の固有値ベクトルが息をひそめているのか。

固有値,固有ベクトルの定義と具体的な計算方法 なんて言う、高校数学の美しい物語に、忘れていた定義が出ていたぞ。

今の高校生は、こんな難しい事を勉強してるのか? オイラーの頃は、こんなの無かったぞ。 田舎の学校で偏差値が低かったから、先生面倒臭くなって省いちゃったの?

kmeans on R

と、深入りしそうな塩梅なので、軌道修正。Rでクラスタリングが出来るか、調べてみた。

何を読み取るのかが重要--統計言語「R」でクラスター分析

R言語プログラミング: クラスター分析 - k-means

bld <- read.csv("2016.csv", header=T)
> bld
          dt  hi lo pl
1   16062904 123 69 53
2   16062921 119 64 65
:
199 16100621 114 75 68
200 16100704 136 81 59
> km <- kmeans(bld[, 2:4], 2)
> km
K-means clustering with 2 clusters of sizes 88, 112

Cluster means:
        hi       lo       pl
1 111.1591 63.46591 62.90909
2 129.8036 75.25893 55.97321

Clustering vector:
  [1] 2 1 2 1 2 1 2 1 2 1 2 2 2 1 2 2 2 1 2 1 2 1 2 1 2 1 2 1 2 1 1 1 2 1 2 1 2
 [38] 1 2 1 2 1 2 1 2 1 1 1 2 1 2 1 2 2 2 1 2 1 2 1 2 1 2 1 2 1 2 2 2 1 2 1 2 1
 [75] 2 1 2 2 2 2 2 2 2 1 2 1 2 1 2 1 2 2 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2
[112] 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 1 1 2 2 2 1 2 1 2 1 2 1
[149] 2 1 2 1 2 2 1 2 1 2 2 2 2 2 2 2 1 2 2 2 1 1 1 2 1 2 2 2 1 2 1 2 1 2 1 2 1
[186] 2 1 2 2 2 1 1 2 2 2 2 1 1 1 2

Within cluster sum of squares by cluster:
[1] 6292.943 6412.089
 (between_SS / total_SS =  67.5 %)

Available components:

[1] "cluster"      "centers"      "totss"        "withinss"     "tot.withinss"
[6] "betweenss"    "size"         "iter"         "ifault"

もう、簡単に分類出来るのね。pythonのオブジェクト指向と言うか、面倒な手続きが 不要で、思った通りにさっと実行出来る。(慣れれば、だけど)

> ans <- bld[, 1] %% 100 < 12
> table(km$cluster, ans)
   ans
    FALSE TRUE
  1    82    6
  2    17   95
> table(ans)
ans
FALSE  TRUE
   99   101

そして、正解とクロス統計を取ってみた。朝測定をTRUE、寝るときをFLSEに変換。 クラスタリングしか結果は、朝が2で寝る時は1とラベル付けしたんだな。

正解率としては、寝る時の方が高い。

> 82 / 99
[1] 0.8282828
> 95 / 101
[1] 0.9405941

勝手に分類させてみて、これだけ出れば、なかなか統計頑張っているな。