データサイエンティストにはなれなかった

データ活用について考えていくブログ

振り返り

今年、人生初の転職をしました。
良い機会だと思うので
これまでの仕事観、キャリアなどについて振り返ってみます。
無駄に長くなってしまいました。

高校時代

将来の夢などありませんでしたが
進学校の雰囲気に飲まれて3年間それなりに勉強をしました。

進学先の決定にあたっては、諸々の制約から大学はすぐに決まりましたが
学部・学科については社会を知らない十代なりに悩みました。
将来のイメージがないまま安易に進学先を選ぶのは良くないのではと考えて
一時は浪人することも考えたほどです。

でも結局は、浪人中も勉強を続けられる自信が持てなかったため
進学することに決めました。
私は理系のコースでしたが化学も物理も興味が持てませんでした。
それらの勉強が避けられない学部・学科を外していった結果、
いくつかの候補が残りました。
残った候補のうち、情報系は将来食いっぱぐれなさそうだなと思い、
工学部の情報系の専攻がある学科を選択し、入学しました。

余談ですが、入試の点数を開示したところ、
数学の点数が同じ学科の友人達の半分くらいしか取れておらず
やっぱり自分は理系に向いていないのだろうなと思いました。

大学&大学院時代

上記のような消去法の連続で進学したこともあり、
1年次必修の基礎科目はほとんど興味を持てませんでした。
出席カードだけ記入して退室、居眠りなどをよくしていました。
今となっては非常にもったいないことをしていたなと思います。

そんな不真面目な学生でしたが、
週に1度だけある専攻の基礎科目日は少し楽しみでした。
その日はプログラミングの実習や
論理学、アルゴリズム、コンピュータアーキテクチャなどがあり、
これらについては純粋に面白いと思えるものが多かったです。
2年次以降も専攻の科目は興味をそそるものが多く、
学校に行くのが億劫でない日はそれなりに講義に出席しました。

それなりの興味と出席でほとんど単位を落とすこともなく
順調に3年次の単位まで取得し、研究室選びのタイミングが訪れます。

当初私はロボティクス系の研究室を志望しようとしていました。
深い理由はなく、動作から知的さを感じるロボットを見て単純に面白いと感じた、
卒業後の産業分野での活用イメージが分かりやすかったぐらいの理由でした。

しかし、例年その研究室は成績上位者で枠が埋まり、
私の年も上位の何名かの同級生が志望しているという情報が入っていました。
そこで、リスクを取ってまでして本当に行きたい研究室なのか考え直しました。

その結果、私は知能や情報処理の仕組みには興味があるが、
機械を動かすことには興味がないということに気付きました。
また、基礎的な数学力の低さから
アルゴリズムなどの理論研究も向いていないだろうと認識しました。

ここも消去法で絞り込んでいった結果、
実験心理学のアプローチで人間の認知について研究している研究室に辿り着きました。
研究内容がやや基礎研究寄りであることなどから
就職面で不利になるかもしれないことを覚悟して志望し、
無事に研究室生活をスタートすることができました。

大学院進学に際しては近い分野の研究者が多く在籍し
実験設備の面でも充実していた同大学の別の大学院に進学しました。

大学院で所属した研究室は他大学から進学してくる方も多く、
理系文系問わず様々なバックグラウンドの方々と互いに
知識や考え方を共有したり議論したりと、とても面白かったです。

就職活動

大学院進学を決めたのはモラトリアムを得たいという不純な動機が大きく、
博士課程まで進む気はありませんでした。
また、研究内容を生かして就職先を見つけるのが難しいことも早々に分かったため、
これまでの研究は趣味だったと割り切って就職活動を進めました。

この時点でも相も変わらずやりたいことはなく、
楽に働けてほどほどお給料が貰える企業に入って
プライベートを充実させたいなーなんて考えていました。

そんな企業を探し当てるために就職四季報を全ページめくりました。
年収や退職率、業績の変化などを確認してめぼしい企業を書き出し
そこから自身の適正や競争倍率などを予想しながらフィルタをかけていきました。
データに基づいて判断するという習性は当時からあったのだと思います。

消去法で残ったものは大きく括るとメーカーとSIerでした。
就職活動期間中に起こった3.11の震災に影響を受けた私は
工場や設備などの固定資産から利益を生み出すメーカーに脆さを感じ、
人間の能力で利益を生み出すSIerの方が良いなと、SIerを中心に選考を進めました。

面接を受ける中でもっとも雰囲気がゆるく、
業績も好調でお給料も悪くないなーと思っていた企業から
最初に内定をいただき、それで就職活動を終えました。

因みに、当時からデータ分析専業の某B社の業務内容は
非常に面白そうだなあと感じていました。
しかし、ユーザーとして古典的な統計学や多変量解析を使っていたに過ぎない自分には
もし入社できたとしても入ってから大変だろうなあと思って見送りました。

1社目(SIer

入社してみて、ゆるくて風通しも良くてかつ20代のうちはお給料も高く、
予想通り働きやすい会社だったと思いました。

ただ、配属後の数か月はSEとしてシステムの改修やテストを担当していましたが、
私は業務内容に全く興味が持てず、この仕事は向いてないなーと感じていました。
そう悶々としていた頃に、先輩がレセプトデータの集計分析を行っていることを知り、
少しずつ仕事を振ってもらう様にお願いしました。
Accessでクエリを書いて集計してレポートにまとめるだけの仕事だったのですが、
この経験が繋がって、新規で発足したデータサイエンス領域でのビジネス探索チームに
異動することができました。
チームのリーダーと、私と同時期に中途採用で加わった先輩の2人は
データサイエンティストとしてもビジネスマンとしても非常に優秀で
刺激的な日々を過ごすことができました。
彼らと一緒に働く中で仕事に対する捉え方が
「お給料をもらうために仕方なく取り組まなければならないもの」から
「本気で取り組めば面白いもの」に変わりました。
勉強会に参加したり、自分で書籍を買って学習するようになったのも
これがきっかけです。

その後、先輩方が会社を去り、出資先にマネージャーが出向したりと、
もはやチームとしての体を成していませんでしたが
私がリーダー的なポジションになりました。

ビジネス上は出口が見えず詰みに近い形でしたが、
結果として約2年間に渡って探索を継続させてもらえました。
自身のデータサイエンティストとして歩み始めたキャリアを途絶えさせないためにも、
データ分析案件の掘起こしから中途採用による人員補充、新人のOJT指導など
できることは色々と実施しました。

昨年の4月に組織の再編があり、
メインのミッションがデータ活用周りのビジネス化ではなく
IoTやAIに関する調査やビジネス検討になりました。
それからの1年と数か月間は、グループ会社の技術交流会で発表したり
他部署の研究開発の支援をしたりと
以前に比べればだいぶ技術者寄りのポジションで働きました。

転職活動

あまり大きな不満はなかったのですが、
将来のことを考えると、居心地の良い環境で
のらりくらり生きてる場合じゃないなと漠然と感じていました。

そんな中、年度が変わってOJTで見ていた後輩が異動になり、
研究開発として携わっていたものがある程度形になったり、
社内外でお世話になった方々が退職されたりと
色々タイミングとしてキリが良いなと感じ、出ていくことにしました。

これまで他社のデータ分析のお手伝いしている中で感じていた色々を
転職によって解消したいと考えていました。
具体的には以下です。
 ・データサイエンスの力で直接的に自社ビジネスを拡大したい。
 ・ビジネス拡大に貢献できるという前提で、お給料も上げたい。
 ・施策側まで意識したプロジェクト運営をしたい。
 ・大企業の中の仕組みを体験して理解したい。

履歴書もまだ書き上がっていないうちに見事に合致する求人を見つけ、
一番最初に応募したその企業から内定をいただき、決めました。

2社目(金融系)

現在です。転職して良かったことはいくつかあります。
 ・自身の工夫によって売上に貢献できるポジションになった。
 ・年収が4割ぐらい上がった。
 ・データ分析以降の施策部分の難しさを感じられるようになった。
 ・大企業の価値観、働き方を知ることができた。
 ・朝が早くなった分、家に帰る時間が早くなって夜の時間が生まれた。
 ・SQLの読み書きに慣れた。

悪かったことは書いていると新年を迎えそうなので、また今度。

今後

今の仕事の仕方(スピード感、技術への触れなさなど)をずっと続けていると
市場価値は下がっていくだろうなあという危機感があります。
採用された責任は果たそうと考えていますが
その後のことについては色んな可能性を考えたいと思っています。

可能性を考えるにあたっては
以下の2つを大事にしたいなと考えています。
 ・自分の知見や技術を十分に活用する。
 ・コスト削減などの効率化だけでなく、新しい価値を生み出す。

これらを本業として実現するために転職するのか、
本業とは別に実施できる副業しやすい企業に転職するのか、
はたまたフリーランスになるのかとか
色々ありますがこれらはじっくり考えていこうと思います。

この年末年始は上記検討の材料とするため、
ボランティアとして某プロジェクトに参加しています。

総括

これまで成りたい自分を設定しないまま
その時々の消極的な理由に基づいて人生の選択を行ってきました。
しかし、その割には人に恵まれたりタイミングに恵まれたりと
色々な偶然が重なったお陰で
良い生き方ができているのではないのかなと思いました。

これからも助け助けられながら生きていきたいと思いますので
皆さま来年もどうぞ宜しくお願い致します。

しばらくRから離れることになりました

いつかまた使う日のために
お世話になったパッケージについてまとめておきます。

統計検定2級を受けてきました

梅雨らしい雨が降りそうな天気の中、統計検定2級を受けてきました。

1週間前から「やばいやばい」と言いながら勉強し始めて
まぁ落ちずに済んだのではないか、というところです。
解答は2日程度で発表されるみたいなので、楽しみです。
(すでに2問も間違っていることが判明しているが、、)

モチベーション

統計検定の存在は前から知っていたのですが
レベル感がよく分からず、世の中にどの様に認められているものなのかも
不明だったのでスルーしていました。

そんな感じだったのですが、今年に入って
某分析力をコアとするほにゃららな企業の求人情報の欄に
「理論的な知識(統計検定2級程度以上)」と記載されているのをみつけました。
上位の職種を見ても2級程度以上と記載されていたので、安直ではありますが
「まぁ2級受かっていれば実務で分析してる人間として恥ずかしくはないのかなー」
なんて思ったのがきっかけです。

それで試験内容を確認してみると、
「データを扱うための初歩的なリテラシーの問題」と
「仮説検定などの推測と呼ばれる問題」の
大きく2つに分けられそうなことが分かりました。
http://www.toukei-kentei.jp/about/pdf/grade2_hani_140901.pdf

前者に関しては改めて勉強する必要はなさそうでしたが、
後者に関しては普段使わないのでいくらか勉強する必要(勉強する価値)がありそうでした。
今回から始まる準1級を受けてみようかなという気持ちもありましたが、
受験料が高かったのに加えて、1級に比べてだいぶ範囲が広く、
勉強する時間が確保できるか怪しかったので今回は手堅く2級を受けてみることにしました。
(我ながらナイス判断だったと思います)

勉強方法

まずは、過去問を解いてみようと思い公式問題集を買いました。

日本統計学会公式認定 統計検定 2級 公式問題集[2012〜2014年]

日本統計学会公式認定 統計検定 2級 公式問題集[2012〜2014年]

「過去問も買ったし余裕で受かるわー」なんて思ってたら
いつの間にか試験の1週間前になっていて、
いざ2012年の問題を解いてみると、6割程度の正答率で焦りました。
間違える問題の多くは信頼区間の計算だったり検定統計量に関わるもので、
「推測」の問題がやはり弱いということが確認できました。

間違った問題については解説をじっくり読んで理解を深める様にしました。
ただ、公式問題集の解説では各問題の「解き方」についてしか分からないため、
体系的に解き方を理解するのは難しかったです。当然と言えば当然ですが。

ですので、時折Wikipediaを見て頭を整理しつつ
足りない部分については別途ググって知識を補っていくという感じになりました。

これを何度か繰り返していくと、
直近2回の過去問では9割弱合う様になり、
落ちることはないかなーという感じで試験に臨みました。

所感

試験までの時間が残り少ないという状況で、
理論の本質的な理解よりも計算方法の暗記に走ってしまったことは若干後悔しています。
各検定統計量でなぜ検定ができるのかとかそういうことは全く分かっていません。
仮説検定が使われるケース自体が減少していきそうな世の流れに逆行して勉強する
機会を逃してしまったという喪失感がややあります。
(試験が終わった今となっては仮説検定について全く勉強する気が起きません)

また、逆に考えると大して理解していなくても
2級は取れそうなレベルであることが分かったので
2級を受かること自体にそれほど意味はないなーとも思いました。

さらに余談ですが、問題の中にはRを使えば一瞬で分かるような問題もちらほらあり、
「こんなのの計算の仕方を覚えるの?」って感じで辛かったです。
例えば、確率分布の上側確率の表を使って確率を求める方法とかです。
(紙と鉛筆(と電卓)があれば計算できることは凄いなとは思いますけど、、)

今後の話

今回2級に受かったとして次に準1級や1級を受けるかという話ですが、
多分受けないと思います。
「データ分析する人は博士号か統計検定1級持ってないとダメ」
なんて世の中になったら考えますが(笑)

試験合格を目標にすると合格することに最適化し
中身の本質的な理解は軽視してしまうことを今回改めて実感したので、
わざわざ高いお金を払ってそんな目標設定をする必要はないかなあと
私は思いましたという感じです。

追記

結果が出ていました。
統計検定:正解発表
自己採点してみると4問ミスでした。まぁこんなもんですか。

追記2

最優秀成績賞というのをもらいました。
嬉しいのは間違いないのですが
4問ミスで最優秀ってどうなの、、

分析のレベル

5日は長いなーと思っていたGWも残すところあと1日になりました。
このGW今のところ進捗ゼロなので
ブログぐらい更新するかという気持ちで書いています。

最近「データサイエンティストブーム」が下火になってきた関係か、
ポジショントークっぽいツイートやブログを目にすることが増えてきたなぁと感じています。

例えば「クロス集計だけでは気付けないことがある」とか
「時系列データを眺めてるだけではうんぬんかんぬん」とかですね。
どれも分析者の視点からは正しいことだと思うのですが、
ビジネス全体の動きにおいて常にこれらの指摘が正しいかと言うと
そうではないケースも多々あるのではないかと思っています。

一般的に「正しい分析」を行うためには
リテラシーの高い分析者に業務を依頼し、依頼された分析者は、
専門外の方からすると「どうしてそんな面倒なことするの?」と言われる様な
高度で複雑な手法を駆使してリアルデータに立ち向かうことが必要になったりします。

その結果「正しい分析」が出来たとして、
現場担当者が表計算ソフトで作ったクロス集計表や時系列グラフなどの
「現場の分析」と比べてどうなのかが、ポイントになるのではないでしょうか。

一般的には、「正しい分析」の方が精度や信頼性に関しては高い一方で、
現場からすると外部に流出するコストが発生したり、
高度で複雑な手法を駆使した分析は作業自体に時間がかかるのはもちろん、
結果に対して上司や関係者などから理解を得ることに手間取ったりして、
次のアクションに繋げにくかったりします。

次のアクションの決定に莫大な費用がかかる場合や
ビジネスの将来性に大きく影響する様な場面では
いくらか時間や費用をかけてでも「正しい分析」を実施すべきでしょう。
一方で、ある程度現場の裁量で動けて、
短期間でPDCAを回すことが是とされている様な状況ならば
「現場の分析」の方が適していると私は考えます。

両極端な例を挙げましたが、
実際は現場に求められる分析のレベルはもっと曖昧だと思います。

単なる「集計・可視化」で十分なのか、
数式1本で表現できる様な「モデル化」で十分なのか、
状態空間モデルや構造方程式モデルなどのもっと「複雑で精緻なモデル化」が必要なのか、
(細かい話をすれば変数選択や正則化、モデルの検証などをどこまでやるかなんて話もある)
これを現場の方が判断するのは非常に難しいことだと思います。

この様な状況に対し、常に「複雑で精緻なモデル化」を押し付けるのではなくて、
ちょうど良いレベルを探り当ててそちらに導くというのが
分析者に求められる役割だと思います。骨が折れることは承知ですが、、

長くなってしまった。。

グリッドサーチ結果の可視化

春がもう少しで来そうでなかなか来ないもどかしい季節ですね。こんばんは。
最近Rのパッケージの関数を見るのが面白いなと感じる様になりました。
私はあまりコーディングをしないので得意ではないのですが、
下手なりに色々書き換えたりして、自分の欲求を満たすものに作り変えていくのは
なかなか悪くない趣味だなと思っています。

というところで、以前id:hoxo_mさんのブログ
SVM のチューニングのしかた(2) - ほくそ笑む
を読んでtune.plotカッコいいなあ、
もっと汎用的に使えたら嬉しいなぁと思ったのを思い出し、
勉強がてら色々書き換えてみました。

本当は元のe1071で行っている様にfilled.contourを使いたかったのですが、
私の力不足で図の配置が上手くコントロールできなかったため断念しました。
そのためカッコよさは激減しています。
グラフィックパラメータ周り難しい。。

library(e1071)
plot.tune.mat <- function(data,#scoreを指定しない場合最も右の列を評価値
                          score = NULL,
                          error = TRUE,#評価値が小さいほど良い場合はTRUE
                          main = NULL,
                          xlab = NULL,
                          ylab = NULL,
                          transform = NULL,#パラメータ全体を変換する関数
                          color.palette = hsv_palette(),
                          nlevels = 20,
                          ...){
  param <- as.data.frame(data)
  k <- ncol(param)
  
  if(is.null(score)){
    score <- param[,k]
    param <- param[,-k]
    k <- ncol(param)
  }
  
  if (!is.null(transform))
    param <- transform(param)
  
  if(!error){
    col <- rev(color.palette(n =nlevels))
  }
  else{
    col <- (color.palette(n =nlevels))
  }
  
  #グラフィックパラメータ設定
  plot.new()
  par(mfcol=c(k,k))  
  par(mar=par("mar")/sqrt(k-1))
  par(mgp=c(1.5,0.5,0))
  
  #描画
  lapply(1:ncol(param),function(i){
    lapply(1:ncol(param),function(j){
      
      paramtab <- tapply(score,param[,c(i,j)],mean)
      
      if (is.null(xlab)) xl <- colnames(param)[i]
      if (is.null(ylab)) yl <- colnames(param)[j]
      
      image(x=as.double(rownames(paramtab)),
            y=as.double(colnames(paramtab)),
            z=paramtab,
            xlab=xl,ylab=yl,
            col = col)
    })})
  
  #グラフィックパラメータを初期化
  on.exit(par(mfcol=c(1,1)))
  on.exit(par(mgp=c(3,1,0)))
  on.exit(par(mar=c(5.1,4.1,4.1,2.1)))
}

使い方としてはこんな感じです。

plot.tune.mat(data,error=T,transform=log2,nlevels=10)

結果
f:id:fqz7c3:20150215222456p:plain
濃いところが良いところです。
濃さのスケールは全プロットで共通です。

表示したデータ

> data
     p1 p2  p3 error_rate
1   100  5 100 0.06585525
2   100  5 100 0.06455118
3   100  5 100 0.06302978
4   100  5 200 0.05520539
5   100  5 200 0.05498805
6   100  5 200 0.05629211
7   100  5 400 0.04694632
8   100  5 400 0.04803304
9   100  5 400 0.04542491
10  100  5 800 0.04629428
11  100  5 800 0.04759835
12  100  5 800 0.04520756
13  100 10 100 0.06020430
14  100 10 100 0.06346446
15  100 10 100 0.06042165
16  100 10 200 0.05390133
17  100 10 200 0.05107585
18  100 10 200 0.05455336
19  100 10 400 0.04629428
20  100 10 400 0.04694632
21  100 10 400 0.04303412
22  100 10 800 0.04868507
23  100 10 800 0.04542491
24  100 10 800 0.04846772
25  100 20 100 0.05824821
26  100 20 100 0.05933493
27  100 20 100 0.06259509
28  100 20 200 0.05129320
29  100 20 200 0.05107585
30  100 20 200 0.05346664
31  100 20 400 0.04998913
32  100 20 400 0.04694632
33  100 20 400 0.04955444
34  100 20 800 0.04998913
35  100 20 800 0.04694632
36  100 20 800 0.04672897
37  100 40 100 0.06216040
38  100 40 100 0.05824821
39  100 40 100 0.05976962
40  100 40 200 0.05346664
41  100 40 200 0.05085851
42  100 40 200 0.05346664
43  100 40 400 0.05107585
44  100 40 400 0.05433601
45  100 40 400 0.05368398
46  100 40 800 0.05020648
47  100 40 800 0.05194523
48  100 40 800 0.05107585
49  200  5 100 0.06476853
50  200  5 100 0.06302978
51  200  5 100 0.06433384
52  200  5 200 0.05737883
53  200  5 200 0.05585742
54  200  5 200 0.05629211
55  200  5 400 0.04781569
56  200  5 400 0.04651163
57  200  5 400 0.04607694
58  200  5 800 0.04607694
59  200  5 800 0.04499022
60  200  5 800 0.04585960
61  200 10 100 0.06107368
62  200 10 100 0.05998696
63  200 10 100 0.06129102
64  200 10 200 0.05194523
65  200 10 200 0.05129320
66  200 10 200 0.05172789
67  200 10 400 0.04694632
68  200 10 400 0.04672897
69  200 10 400 0.04738100
70  200 10 800 0.04651163
71  200 10 800 0.04716366
72  200 10 800 0.04672897
73  200 20 100 0.05911758
74  200 20 100 0.05890024
75  200 20 100 0.05911758
76  200 20 200 0.04977179
77  200 20 200 0.05107585
78  200 20 200 0.05151054
79  200 20 400 0.04977179
80  200 20 400 0.04651163
81  200 20 400 0.04803304
82  200 20 800 0.04759835
83  200 20 800 0.04716366
84  200 20 800 0.04781569
85  200 40 100 0.06107368
86  200 40 100 0.06042165
87  200 40 100 0.06063899
88  200 40 200 0.05129320
89  200 40 200 0.05064116
90  200 40 200 0.05281461
91  200 40 400 0.05042382
92  200 40 400 0.05020648
93  200 40 400 0.04846772
94  200 40 800 0.04977179
95  200 40 800 0.05129320
96  200 40 800 0.05107585
97  400  5 100 0.06324712
98  400  5 100 0.06324712
99  400  5 100 0.06324712
100 400  5 200 0.05585742
101 400  5 200 0.05303195
102 400  5 200 0.05390133
103 400  5 400 0.04520756
104 400  5 400 0.04651163
105 400  5 400 0.04455553
106 400  5 800 0.04716366
107 400  5 800 0.04651163
108 400  5 800 0.04672897
109 400 10 100 0.05955227
110 400 10 100 0.05976962
111 400 10 100 0.06129102
112 400 10 200 0.05237992
113 400 10 200 0.05172789
114 400 10 200 0.05303195
115 400 10 400 0.04542491
116 400 10 400 0.04651163
117 400 10 400 0.04651163
118 400 10 800 0.04564225
119 400 10 800 0.04694632
120 400 10 800 0.04585960
121 400 20 100 0.05803086
122 400 20 100 0.06042165
123 400 20 100 0.05868290
124 400 20 200 0.05151054
125 400 20 200 0.05020648
126 400 20 200 0.04933710
127 400 20 400 0.04846772
128 400 20 400 0.04694632
129 400 20 400 0.04781569
130 400 20 800 0.04803304
131 400 20 800 0.04629428
132 400 20 800 0.04716366
133 400 40 100 0.06042165
134 400 40 100 0.06107368
135 400 40 100 0.05998696
136 400 40 200 0.05216257
137 400 40 200 0.05151054
138 400 40 200 0.05194523
139 400 40 400 0.04977179
140 400 40 400 0.04933710
141 400 40 400 0.05107585
142 400 40 800 0.05085851
143 400 40 800 0.05129320
144 400 40 800 0.05172789
145 800  5 100 0.06433384
146 800  5 100 0.06216040
147 800  5 100 0.06129102
148 800  5 200 0.05455336
149 800  5 200 0.05433601
150 800  5 200 0.05411867
151 800  5 400 0.04672897
152 800  5 400 0.04433819
153 800  5 400 0.04629428
154 800  5 800 0.04455553
155 800  5 800 0.04629428
156 800  5 800 0.04477288
157 800 10 100 0.06020430
158 800 10 100 0.05998696
159 800 10 100 0.06129102
160 800 10 200 0.05346664
161 800 10 200 0.05216257
162 800 10 200 0.05324929
163 800 10 400 0.04520756
164 800 10 400 0.04607694
165 800 10 400 0.04607694
166 800 10 800 0.04542491
167 800 10 800 0.04672897
168 800 10 800 0.04651163
169 800 20 100 0.05998696
170 800 20 100 0.05737883
171 800 20 100 0.05976962
172 800 20 200 0.05042382
173 800 20 200 0.05085851
174 800 20 200 0.04977179
175 800 20 400 0.04672897
176 800 20 400 0.04672897
177 800 20 400 0.04629428
178 800 20 800 0.04672897
179 800 20 800 0.04738100
180 800 20 800 0.04759835
181 800 40 100 0.05868290
182 800 40 100 0.05955227
183 800 40 100 0.06063899
184 800 40 200 0.05107585
185 800 40 200 0.05151054
186 800 40 200 0.05129320
187 800 40 400 0.04998913
188 800 40 400 0.04933710
189 800 40 400 0.05172789
190 800 40 800 0.04955444
191 800 40 800 0.05064116
192 800 40 800 0.04998913

現時点ではテストもほとんどしていないので
色々不具合あると思います。
(lapplyの2重ループもなんか気持ち悪いので書き換えたいし)
そのうち修正していくと思います。

そもそもこんな関数caretとかにありそうな気がするがどうなのでしょうか。



今回のブログを書くにあたってこの辺りを参考にしました。
http://cse.naro.affrc.go.jp/takezawa/r-tips/r/52.html
http://cse.naro.affrc.go.jp/takezawa/r-tips/r/53.html
http://cse.naro.affrc.go.jp/takezawa/r-tips/r/55.html

ブログのタイトルを変えました。

どうも明けましておめでとうございます。
昨年末は少々やらかしてしまって散々な目にあいましたが
この流れを断ち切って2015年は健康で安心に過ごせると良いなと思っています。
皆様今年もどうぞよろしくお願いします。

で、本題。当ブログのタイトルを変更しました。
以前は「SE採用の新入社員がデータサイエンティストになるまで」というタイトルでした。
4月を迎えれば入社4年目ですし、そろそろ新入社員という言葉にも違和感があるなと感じていました。また、2014年様々な社外の活動に参加して、
私は「データサイエンティスト」になりたかったのではないという認識が強まりました。

私は現職(データ分析コンサルの様なもの)で、「ビジネスとしての価値を出す」ことが最重要であると考えて取り組んでいます。
その中で行う分析は事実に基づいてビジネスや課題を理解するということが
目的であることが多く、クロス集計や単純な可視化といったものが基本となります。
お客様に見せるかどうかは別にして主成分分析や一般化線形モデルなども使ったりしますが、精々その程度のことしかしません。
高度な統計学機械学習アルゴリズムの知識などは業務ではほとんど使っていない様に思います。
案外その様な知識よりも、私のバックグラウンドでもある実験心理学の様な
データ取得に関するバイアスに気付いたり、それを生じさせない様にする工夫などの方が役立っている様な気さえします。

この様に複雑なアルゴリズムや手法を使わず、また、ディープラーニングなど最新の研究成果にも疎い私は
とても「データサイエンティスト」であるとは思えないし、これからもきっとなれないのだろうと感じています。

一方で、私は今の仕事や働き方に対してある程度満足しています。
上でも述べましたが、私は「ビジネスとしての価値を出す」ということが最重要であると考えています。
「データサイエンティスト」でなければ出せない価値はもちろんありますが、
そうでなくてもデータをしっかり見て理解することで生み出せる価値もあると考えています。

私の当面の目標としては、データ活用によって価値が生まれるという体験を
草の根的に広げていける人間になりたいと考えています。
そのためには、予め多くの業界や業種に対する理解を深めていたり
海外のビジネスやソリューションの知識を得て持ち球を増やしたりということが
狭義の分析力を高めるよりも必要なことだと感じています。
もはや、データにこだわる必要ももしかしたらないのかもしれませんが、
私個人としては事実(データはこの一部)に基づいて理解を深めていくというプロセスを重視したいのでこれからもデータにこだわっていきたいと思います。

kNN関連パッケージの紹介

皆様こんばんは。
今回、R Advent Calendar 2014 : ATND 13日目の記事を担当することになったので
久しぶりのブログを書いています。

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

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

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

{class}パッケージ

Rで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の数の調整よりも
特徴量自体の正規化や重み付けの方が効くことが多いのかなと思っています。


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

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