ビジネスと技術の狭間で

データを活用して生きていく

kNN関連パッケージの紹介

皆様こんばんは。
今回、https://atnd.org/events/58648 13日目の記事を担当することになったので
久しぶりのブログを書いています。

本記事でご紹介するのはRでkNN(k最近傍法)を実行するためのパッケージです。
kNNについて知らない方はwikipediak近傍法 - Wikipediaに簡単にまとまっているので見てもらうと良いと思います。
要は、入力データに似ているものを訓練データの中からk個探してきて、
それらの多数決で出力を決めるアルゴリズムです。

個人的な印象としては検索のイメージが強く、
例えば、訓練データの中から似ているレコード(商品やユーザー、事例など)を引っ張ってくるときに使ったりしています。

それでは早速パッケージの紹介に入ります。

{class}パッケージ

RでkNNをやろうとすると最初に見つかるのがこのパッケージだと思います。
とりあえずkNNで分類をやってみるという意味ではこのパッケージで十分です。

本記事では{kernlab}パッケージのspamデータセットを使って実行してみます。
因みにこのデータセットは約5000件のe-mailに対してスパムかどうかを判定したものです。
各レコードは各ワードや記号などの出現回数から作った特徴量(1~54列目)と
大文字に関する単語の長さなどから作った特徴量(55~57列目)と
スパムかどうかのクラス(58列目)から成っており、
今回は話を単純にするために1~54列目と58列目を対象としています。

library(kernlab)
data(spam)
data <- spam[,-(55:57)] #55列目がクラスになる

library(class)
i <- 1
classres <- class::knn(train=data[-i,-55],test=data[i,-55],cl=data[-i,55],k=100,prob=T) 

実行結果:

> classres
[1] spam
attr(,"prob")
[1] 0.74
Levels: nonspam spam

trianとtestをセットし、求めるクラスを決めてあげると実行できます。
probで確率値を出力する様に設定でき、設定すると
勝ったクラスの割合がどのくらいだったかが分かります。
ただ、今回の場合の様に2クラスの場合は良いのですが、
3クラス以上の分類で2位以下の確率値が分からないのは残念です。

上記は1レコードに対してknnを実行する方法でしたが
全レコードに対してknnを実行する(leave-one-out)する場合は以下の様にします。

classrescv <- class::knn.cv(train=data[,-55],cl=data[,55],k=100,prob=T) 

実行結果:

> head(classrescv)
[1] spam    spam    spam    nonspam nonspam spam   
Levels: nonspam spam
> head(attr(classrescv,"prob"))
[1] 0.74 0.90 0.93 0.58 0.58 0.50

{class}パッケージのknnは各レコードごとに全探索を行っている様で、
spamデータぐらいではなんとも感じませんが、
データ量が大きくなってくるとパフォーマンスは辛くなってきます。
そこで、{FNN}パッケージです。

{FNN}パッケージ

FNNのFはFastのFなのですが、探索アルゴリズムを選択できる様になっており
全レコードに対してknnを実行する場合はこちらが速いです。
デフォルトのアルゴリズムであるkd_treeで実施した場合の計算時間は
理論的にはO(NlogN)となるそうです。

#classパッケージで実行
system.time(classrescv <- class::knn.cv(train=data[,-55],cl=data[,55],k=1,prob=T))
#FNNパッケージ"kd_tree"で実行
system.time(FNNrescv <- FNN::knn.cv(train=data[,-55],cl=data[,55],k=1,prob=T,algorithm="kd_tree"))

実行結果:

> system.time(classrescv <- class::knn.cv(train=data[,-55],cl=data[,55],k=1,prob=T))
   user  system elapsed 
   3.40    0.00    3.43 
> system.time(FNNrescv <- FNN::knn.cv(train=data[,-55],cl=data[,55],k=1,prob=T,algorithm="kd_tree"))
   user  system elapsed 
   2.69    0.00    2.70 

spamデータぐらいの規模では大きな差はでませんが、数十万件を超える様なデータですと明確な差がでてきます。

補足:この記事を書いていて気付きましたが、kの数を大きくするとそれほど計算時間の差は出なくなってくるみたいです。
アルゴリズムと実装について理解が追い付いていないので原因は不明です。
{FNN}パッケージはkd_treeを設定した場合、Approximate Near Neighbor(ANN)というC++のライブラリを呼んでいますが、
このパッケージにおいては近似していないとマニュアルに書いてあるので、
その辺り調整できればkの数が増えても差が保てるのかもしれません。
この調整を行うには{RANN}のnn2や{yaImpute}のannを使わないと難しそうです。私はまだ試していません。。


{FNN}を使う利点はもう1つあります。(他にもありそうですが)
FNNのknn.cvは最近傍k個のレコードのインデックスを返してくれるので、
本記事冒頭で述べた様に似ているレコードを引っ張ってくることができます。
また、引っ張ってきたレコードのクラスを集計すれば2位以下のクラスの確率値を知ることも可能です。

FNNrescv <- FNN::knn.cv(train=data[,-55],cl=data[,55],k=3,prob=T,algorithm="kd_tree")
i <- 1
data[c(i,attr(FNNrescv,"nn.index")[i,]),]

実行結果:

> data[c(i,attr(FNNrescv,"nn.index")[i,]),]
    make address  all num3d  our over remove internet order mail receive will people report addresses
1      0    0.64 0.64     0 0.32    0      0        0     0    0       0 0.64      0      0         0
13     0    0.69 0.34     0 0.34    0      0        0     0    0       0 0.69      0      0         0
312    0    0.68 0.34     0 0.34    0      0        0     0    0       0 0.68      0      0         0
168    0    0.71 0.35     0 0.35    0      0        0     0    0       0 0.71      0      0         0
    free business email  you credit your font num000 money hp hpl george num650 lab labs telnet num857
1   0.32        0  1.29 1.93      0 0.96    0      0     0  0   0      0      0   0    0      0      0
13  0.34        0  1.39 2.09      0 1.04    0      0     0  0   0      0      0   0    0      0      0
312 0.34        0  1.37 1.72      0 1.03    0      0     0  0   0      0      0   0    0      0      0
168 0.35        0  1.42 1.77      0 1.06    0      0     0  0   0      0      0   0    0      0      0
    data num415 num85 technology num1999 parts pm direct cs meeting original project re edu table
1      0      0     0          0       0     0  0      0  0       0        0       0  0   0     0
13     0      0     0          0       0     0  0      0  0       0        0       0  0   0     0
312    0      0     0          0       0     0  0      0  0       0        0       0  0   0     0
168    0      0     0          0       0     0  0      0  0       0        0       0  0   0     0
    conference charSemicolon charRoundbracket charSquarebracket charExclamation charDollar charHash type
1            0             0            0.000                 0           0.778          0        0 spam
13           0             0            0.056                 0           0.786          0        0 spam
312          0             0            0.055                 0           0.718          0        0 spam
168          0             0            0.058                 0           0.700          0        0 spam

上記、knn.cvを利用しましたが、インデックスが欲しいだけならば、
同パッケージ内のget.knnを使った方が速いです。

system.time(FNNrescv <- FNN::knn.cv(train=data[,-55],cl=data[,55],k=3,prob=F,algorithm="kd_tree"))
system.time(FNNresget <- FNN::get.knn(data=data[,-55],k=3,algorithm="kd_tree"))
> system.time(FNNrescv <- FNN::knn.cv(train=data[,-55],cl=data[,55],k=3,prob=F,algorithm="kd_tree"))
   user  system elapsed 
   3.07    0.00    3.12 
> system.time(FNNresget <- FNN::get.knn(data=data[,-55],k=3,algorithm="kd_tree"))
   user  system elapsed 
   0.76    0.00    0.76 

因みにget.knnの方はattrではなく、リストで値を返してくるのでご注意を。

{kknn}パッケージ

そして、最後に{kknn}パッケージの紹介をしようと思っていましたが、
長くなってしまったので省略します。
多数決を行う時に最近傍k個の重みを調整できたり
クロスバリデーションの機能が充実していたりするのですが、
実務的にはややオーバースペックな印象で、私は使ったことがないです。

本記事では割愛しましたが、多数決の重みやkの数の調整よりも
特徴量自体の正規化や重み付けの方が効くことが多いのかなと思っています。


それでは皆様、来年も宜しくお願い致します。

※間違い等ありましたらご指摘いただけますと幸いです。

TokyoR(第43回)でLTしてきました

半年以上空いての更新となってしまいました。
文章としてアウトプットする習慣がなかなかつかないです。

本題。昨日TokyoRでLTをしてきました。

内容はRのrandomForestの説明と、

使えるメモリに制限がある場合の工夫の仕方についてです。

今回の発表の準備を通して、randomForestが返すオブジェクトの

中の見方が分かったりしたので個人的には非常に有意義でした。

 

ご指摘もありましたが、さっさとOSを64bitにするというのが私もベストだと思います。(プライベートで使うPCへの投資はなかなか億劫。。)

ですが、ちょっとした工夫で省メモリになったり、高速になったりすることに喜びを感じる私の様な方も少なくないと思いますので、この資料がその様な方々のお役に立てば幸いです。

 

丸一年経ちました

また久しぶりの更新となってしまいました。

最近は、webサイトのクローリングをちょこちょこ行っています。

これについてはまた改めて書くかも、しれません。

 

データサイエンティストという言葉を知り、目指し始めたのが去年のこの頃だったと思うので、一年間の振り返りと今年の抱負などを書いてみたいと思います。

 

■データ整形

当時はまだ、ほとんどperlも触ったことがなくデータ整形を行うのにも非常に時間を費やしていましたが、現在はサクサクと手が動く様になりました。

ただ、複雑なデータ構造を扱うときに、稀にハマるのでまだまだスピード向上の余地があると思っています。

また、モジュールについての知識が全然ないので、今年は便利そうなモジュールはどんどん使ってみる様にしたいです。

 

■分析

学生の頃行っていた分析はExcelSPSSだったのですが、SPSSは会社で使っていないのでKnimeとRを主に使うことになりました。

最初は、集計や可視化を行うのも一苦労でしたが、現在は一先ず自分で確認する分には十分になりました。今年は、クライアントさんにそのままお見せできる様な綺麗な可視化、カッコいい可視化ができる様になりたいです。

また、モデリングに関してはlogistic regressionやrandom forestを中心として色々行ってきたので、一応なんとか使えるレベルにはなったと思っています。ただ、変数の作り方やチューニングの仕方についてはまだまだなので、今年はもっと上手くできる様に勉強したいと思います。

 

■理論

機械学習

 最初は全然理解できないことばかりでしたが、繰り返し繰り返し同じことについて説明している多数の資料を見ることで概念についてはなんとなく理解できる様になってきたと思っています。(数式アレルギーも徐々に克服中)

今年は概念だけでなく、情報理論や確率過程など基礎となる理論についても少しずつ理解を深めていきたいと考えています。

 

統計学

 統計学は学生時代から使っていたので、他部署の方に仮説検定などについて説明する機会があり、少しは役立てたのではないかと考えています。しかし、基礎的な検定であるχ2乗検定を行うことがあり、その時にすぐ計算ができなくて困ったことがありました。基礎的なものこそ当たり前の様に計算できなければならないと思うので、時間を作って一度振り返りたいものです。

 

以上、1年間の振り返りと今年の抱負でした。

組織的な色々な問題もあるのですが、環境に振り回されず着実にレベルアップしていきたいと思います。

pythonについて

業務で使う必要があったのでpython始めました。

ソースの可読性が高いなと早速感じていて

一人で勉強するのに向いているなと思います。勉強します。

 

CentOSに入れようとしたら依存関係の解決が色々面倒だったので断念して、

Windowsに入れました。

 

本体は本家(http://www.python.org/)から、

パッケージは先輩から教えてもらったところ(http://www.lfd.uci.edu/~gohlke/pythonlibs/)から落として入れました。

バイナリで入れられるので非常に楽でした。裏技みたいな感じ。

たまにここに上がっているバージョンじゃダメだったりするので適宜その場合はアンインストールして入れなおす感じの様です。

 

機械学習の諸々が充実しているのでモデリングの方はpythonに以降するかもです。

近況について

7月末からお盆辺りにかけて担当していた案件が忙しくブログの更新をサボっていました。何となく罪悪感があります。すみません。

 

最近は仕事が落ち着いたので、為替のデータに対して機械学習を行って儲けられないかとか色々やってました。そのうち結果が出たらブログにまとめようと考えています。

なかなか予測精度が上がらないので「どうして予測できないのかについて」語る可能性が高いですが。

 

昨日は恒例のTokyo.Rに参加してきました。

細かい感想や意見などは割愛しますが、アカデミア、ビジネスの各方面で活躍されている方々の話は面白かったです。

彼らの話についていくためには休んでいる暇はないなと感じました。努力を続けます。

 

とりあえず、生きていますということで。

MeCabの辞書編集について

MeCabは以前にも紹介しました形態素解析エンジンです。MeCabを使うと文章を単語に切り分けることができます。

デフォルトの辞書でもそこそこ上手く行くのですが、

固有名詞やあまり一般的でない語句などは変な場所で切ってしまったり、単語と単語を切り離しすぎてしまう場合があります。

例えば、「データサイエンティスト」で抽出したいのに

データ  名詞,一般,*,*,*,*,データ,データ,データ
サイエンティスト        名詞,一般,*,*,*,*,サイエンティスト,サイエンティスト,サイエンティスト

の様に分かれて出てしまうなど。

 

そこで、辞書の編集が必要になります。

システム辞書とユーザー辞書があるのですが

今回はユーザー辞書の編集について説明します。

ウェブ上に同じことを説明している資料はたくさんあったのですが、

バージョンの違いなどからそのままでは上手くいかなかったので再編という感じです。

因みに私が使っているMeCabはWindows版、バージョンは0,996で

インストール先はC:\Program Filesです。

 

以下手順を書いていきます。

[1]ユーザー辞書を作成します。

デフォルトの設定ではMeCabの辞書は以下のディレクトリに置かれています。

C:\Program Files\MeCab\dic\ipadic

ここにuser.csvというファイルを作成します。

このファイルに下のフォーマットに従って単語を書きます。

「表層形,左文脈ID,右文脈ID,コスト,品詞,品詞細分類1,品詞細分類2,品詞細分類3,活用形,活用型,原形,読み,発音」

例えばこんな感じです。

データサイエンティスト,-1,-1,1000,名詞,一般,*,*,*,*,データサイエンティスト,データサイエンティスト,データサイエンティスト

 

表層形:文章の中に出てくる形です。

文脈ID、コスト:説明すると長くなるので割愛しますが

 とりあえず-1,-1,1000で書いておいて問題ないです。

品詞:下のリンク先の一番下の部分を参考に入力してください。

 http://mecab.googlecode.com/svn/trunk/mecab/doc/posid.html

活用:名詞などの場合は入力する必要はありませんが、

 動詞など活用がある場合はデフォルトのVerb.csvを参考に

 活用の情報も入力してください。

 

[2]コンパイルします。

辞書があるフォルダへ移動して

>cd C:\Program Files\MeCab\dic\ipadicコンパイルします。

>mecab-dict-index -f SHIFT-JIS -t SHIFT-JIS  -u user.dic user.csv

-f:コンパイル前のファイルの文字コード

-t:コンパイル後のユーザー辞書の文字コード

-u:コンパイル後のユーザー辞書のファイル名

 

done!が表示されれば成功です。

念のため編集した単語が上手く切れる様になっているか確認します。

>mecab -u user.dic
データサイエンティスト
データサイエンティスト  名詞,一般,*,*,*,*,データサイエンティスト,データサイエンティスト,データサイエンティスト

-u:使うユーザー辞書の指定

 

今度は「データサイエンティスト」で抽出できていますね。

 

[3]毎回ユーザー辞書を指定するのが面倒であれば、デフォルトで使う様に登録をします。

同じディレクトリにあるdicrcというファイルに

userdic=C:\Program Files\MeCab\dic\ipadic\user.dic

と、一行書き足します。

 

以上で辞書の編集は終了です。

 

 

(参考)MeCab/CabChaによる 言語解析演習

http://chasen.naist.jp/chaki/t/2008-09-09/doc/mecab-cabocha-nlp-seminar-enshu-2008.ppt

ビッグデータについて

ビッグデータについて何を今更言うんだって感じですが

自分の考えをまとめるために書いておきます。

 

ビッグデータとは3Vと言われるVolume(大量)Variety(多種)Velocity(高頻度)の3つのうち1つ以上の概念を持つデータです。

上記の3つにVeracity(正確さ)を足して4Vと言う場合もあるそうですが、

個人的に意味が分からないです。レイヤーが違いますよね?って感じです。

 

要するにビッグデータとは「ちょっと前までは扱うのが絶望的に難しかったデータ」というものなのです。

それが、テクノロジーの進歩によって比較的容易に集計したり分析したりすることが可能になりました。そして、大手ITベンダー様の営業の甲斐もあり、国内でも意識の高い企業がビッグデータ分析に取り組み始めたというのが現状なのではないでしょうか。

 

個人的に、「ビッグデータ」が盛り上がることによって、データドリブンな考え方が啓蒙されるのであればそれは嬉しいことだと思っています。しかし、その一方で危惧していることもあります。

まだデータドリブンな考えが根付いていない企業が「ビッグデータを溜める箱」や「ビッグデータを分析できるソフト」を導入しても、その費用に対する効果を上げられないのではないかということです。

「高いお金を出したけれど、ビッグデータって思ってたより使えないし、データ分析とか意味ないね」なんてことになっても誰も幸せになりません。だから、ビッグデータを扱う環境を構築しようとしている企業はまず「データを何に使うのか?どう使うのか?」を社内で議論していただくのが良いのではないかと思っています。

ここで言うデータとは「ビッグデータ」に限りません。ビッグデータを分析しないと分からないこともありますが、スモールデータでもデータを利用する目的を達成できることは少なくないので。

 

以上などなどにより、市場で流行って欲しい言葉は「ビッグデータ」よりも「データドリブン」とか「データサイエンス」だよなぁと感じています。