scikit-learnで決定木分析(CART)を試す
とにかく試して見るシリーズ第一弾。
なぜやるのか
いつまでもデータマート拵えおじさんのままではマズいため、比較的難易度が低くビジネスで幅広く応用が効くらしい決定木分析をやってみる
決定木分析とは
概要
決定木分析 (Decision Tree Analysis) は、機械学習の手法の一つ。木を逆にしたようなデータ構造を用いて分類と回帰を行う。
決定木分析の特徴
- 樹木状の構造で学習結果を視覚化でき、ルールをシンプルに表現できるため、論理的な解釈が容易
- データの標準化 (正規化) やダミー変数の作成を必要としないため、前処理の手間がほとんど不要
- カテゴリカルデータと数値データの両方を扱うことが可能
- 検定を行って、作成したモデルの正しさを評価することが可能
参考にしたサイト*1
ビジネスでの活用例
- 顧客別の購買履歴から自社の製品を購入している顧客の特徴を分析
- 金融機関の取引履歴から顧客属性別の貸し倒れリスクを分析
- 機械の動作ログから故障につながる指標を分析
参考にしたサイト*2
取り組んだ課題
Rの標準データセット[HairEyeColor]のデータを使ってHair[髪の色]、Eye Color[瞳の色]から、Sex[性別]を予測する決定木のモデルをPythonで作成する*3
試行過程と結果
1.データセットを確認する
# 編集前のcsvファイルを読み込んで、DataFrame化 import pandas as pd import_df = pd.read_csv('HairEyeColor.csv') import_df.head()
Unnamed: 0 | Hair | Eye | Sex | Freq | |
---|---|---|---|---|---|
0 | 1 | Black | Brown | Male | 32 |
1 | 2 | Brown | Brown | Male | 53 |
2 | 3 | Red | Brown | Male | 10 |
3 | 4 | Blond | Brown | Male | 3 |
4 | 5 | Black | Blue | Male | 11 |
決定木分析をするにあたり、このデータセットでは2工程の下処理が必要
- 各レコードをFreqの数だけ生成する
- String型の各項目をint型に置換する
2.CSVデータを加工する
参考にしたサイト*4
in_file = open("HairEyeColor.csv","r") out_file = open("HairEyeColor_Edited.csv","w") # アウトプットファイルにヘッダーを書き込み out_file.write("Hair,Eye,Sex\n") # インプットファイルのヘッダーを読み飛ばす in_file.readline() # インプットファイルの全レコードを読み込み lines = in_file.readlines() # for文で1行ずつ処理 for line in lines: # 改行コードはブランクに置換 line = line.replace("\n","") # カンマ区切りでリストに変換する line = line.split(",") # 変換処理した値を、更にカンマ区切りへ変換 row = "{},{},{}\n".format(line[1],line[2],line[3]) # 書き出し用のファイルに"Freq"の数だけ出力 freq = int(line[4]) for i in range(0,freq): out_file.write(row) in_file.close() out_file.close()
# 編集後のcsvファイルを読み込んで、DataFrame化 import_df = pd.read_csv('HairEyeColor_Edited.csv') import_df.head()
Hair | Eye | Sex | |
---|---|---|---|
0 | Black | Brown | Male |
1 | Black | Brown | Male |
2 | Black | Brown | Male |
3 | Black | Brown | Male |
4 | Black | Brown | Male |
# 加工前との整合性を確認する check = import_df.groupby(['Hair','Eye','Sex']).size() check
Hair Eye Sex
Black Blue Female 9
Male 11
Brown Female 36
Male 32
Green Female 2
Male 3
Hazel Female 5
Male 10
Blond Blue Female 64
Male 30
Brown Female 4
Male 3
Green Female 8
Male 8
Hazel Female 5
Male 5
Brown Blue Female 34
Male 50
Brown Female 66
Male 53
Green Female 14
Male 15
Hazel Female 29
Male 25
Red Blue Female 7
Male 10
Brown Female 16
Male 10
Green Female 7
Male 7
Hazel Female 7
Male 7
dtype: int64
# データの特徴を見てみる
import_df.describe()
Hair | Eye | Sex | |
---|---|---|---|
count | 592 | 592 | 592 |
unique | 4 | 4 | 2 |
top | Brown | Brown | Female |
freq | 286 | 220 | 313 |
# String型の各項目をint型に置換するための辞書を作って変換 dict = {'Hair':{'Black' :1,'Blond':2,'Brown':3,'Red' :4}, 'Eye' :{'Blue' :1,'Brown':2,'Green':3,'Hazel':4}, 'Sex' :{'Female':1,'Male' :2}} import_df['Hair'] = import_df["Hair"].map(dict['Hair']) import_df['Eye'] = import_df["Eye"].map(dict['Eye']) import_df['Sex'] = import_df["Sex"].map(dict['Sex']) import_df.head()
Hair | Eye | Sex | |
---|---|---|---|
0 | 1 | 2 | 2 |
1 | 1 | 2 | 2 |
2 | 1 | 2 | 2 |
3 | 1 | 2 | 2 |
4 | 1 | 2 | 2 |
3.決定木の分類器を作成して可視化する
参考にしたサイト*5
from sklearn import tree # 説明変数は'Hair','Eye' variables = ['Hair','Eye'] # 決定木の分類器を作成 classifier = tree.DecisionTreeClassifier() # 目的変数は'Sex' # サンプルデータで学習 classifier = classifier.fit(import_df[variables],import_df['Sex'])
# 作成した決定木を可視化する import pydotplus from sklearn.externals.six import StringIO dot_data = StringIO() tree.export_graphviz(classifier, out_file=dot_data, filled=True,rounded=True) graph = pydotplus.graph_from_dot_data(dot_data.getvalue()) from IPython.display import Image Image(graph.create_png())
4.交差検証
今回は決定木の深さだけを変えて交差検証してみる
# 交差検証1(max_depth指定なし) import numpy as np from sklearn import cross_validation as cv data = import_df.reindex(np.random.permutation(import_df.index)) variables = ['Hair','Eye'] classifier = tree.DecisionTreeClassifier() scores = cv.cross_val_score(classifier, data[variables], data['Sex'], cv=5) print(scores.mean(), scores)
0.517007950507 [ 0.48739496 0.57142857 0.47058824 0.49152542 0.56410256]
# 交差検証2(max_depth=3) data = import_df.reindex(np.random.permutation(import_df.index)) variables = ['Hair','Eye'] classifier = tree.DecisionTreeClassifier(max_depth=3) scores = cv.cross_val_score(classifier, data[variables], data['Sex'], cv=5) print(scores.mean(), scores)
0.545807753784 [ 0.46218487 0.52941176 0.56302521 0.59322034 0.58119658]
# 交差検証3(max_depth=4) data = import_df.reindex(np.random.permutation(import_df.index)) variables = ['Hair','Eye'] classifier = tree.DecisionTreeClassifier(max_depth=4) scores = cv.cross_val_score(classifier, data[variables], data['Sex'], cv=5) print(scores.mean(), scores)
0.526948093449 [ 0.58823529 0.47058824 0.55462185 0.50847458 0.51282051]
感想
- インプットしたHair, EyeのColorに対して、Sexをアウトプットする仕組みを作れないと意味がない
- パラメータ設定によるチューニングができるほどの理解には及んでいないため、もう少し理解を深めたい
- Grid Searchなるものを実行できるようになれば、パラメータを最適化できるらしい(理解度浅くても使えるということか)
- レコメンドに応用するとして、説明変数と目的変数の粒度をどうするのかが重要そうだ(ファッションのように品目がかなり細かい商品を扱うならカテゴリで、ラインナップがファッションなどに比べて限定的な金融商品などなら商品そのものでも分類できそう)
- 顧客の属性情報を説明変数に様々な切り口で分析をかければ、マーケティング対象をより詳細に理解して施策を練るのに役立てられそう
参考にしたサイト
*1:こちらのサイトを参考にしました
scikit-learn で決定木分析 (CART 法) – Python でデータサイエンス
*2:こちらのサイトを参考にしましたanalytics-news.jp
*4:こちらのサイトを参考にしましたmemopy.hatenadiary.jp
*5:こちらのサイトを参考にしました
todoa2c.github.io
このままでは一端の何者にもなれない
ブログを始める。 何かに深い知見があるわけでもなく、不特定多数の誰かの為になるような記事をすぐには書けそうもない。
システムエンジニアからデータアナリストに転身してはや数ヶ月が経った。日常業務をこなすのに必要な最低限のキャッチアップを終え、すっかりデータマート拵えおじさんと化している。
このままではいかん。この危機感はなんなんだ。この記事では転職を少しだけ振返り、このブログを始めた経緯を綴っておく。
転職したきっかけ
事業を理解して自ら課題を発見し、仕事を作れる人間になりたい
新卒でシステムエンジニアになったのは、「チームで達成感を共有できる」「顧客の課題に対し、企画・提案ができる」と考えたからだった。
しかし、私のいた現場は古くから基幹業務システムを抱えている大企業、配属された部署は汎用機系アプリケーションの保守案件が多かったこともあり、大きなミスマッチがあった。状況の打開には、転職して環境を変える他に選択肢はないと考えたので転職した。
「チームで達成感を共有できる」か?
ほとんどできなかった。職場を見渡しても仕事で達成感(≒ やりがい)を感じる瞬間があって、それがあるから活き活きと働けてる、みたいな人はいなかった。私自身もこれから取り組もうという仕事に、よし!やるか!みたいな気持ちも抱くこともなくなっていった。
なぜあんなにやりがいを感じられない仕事だったのだろう。。これは次項と深い関係があるというのが私の持論である。
「顧客の課題に対し、企画・提案ができる」か?
これが最も辛かったポイントで、そもそも課題をヒアリングして深掘りし、解決策を提案したり協議したりするという仕事ではなかった。実態は、事業側で解決策まで検討してシステム改善要望を提示する。我々は工数内に収まるように要望を削ぎ落としながら要件定義することが仕事。基幹業務システムの保守案件をメインに請け負っており、案件はいくらでもある状態だったため仕方がないのだが、顧客が求めていることに理由をみつけて断っていく作業の繰り返しは前向きではないし、大方の人にとって(おそらく)気持ちの良いものではないと思う。
加えて事業サイド、情報システム部門各部署と長い歴史で策定された障害を防ぐための細かいルール、開発を担当するパートナー企業との間を柵に足を取られながら行ったり来たり、、みな年々疲弊していく。その仕事に自分の意思はなくなっていく、次々に降りてくる仕事に面倒な折衝をこなす日々。自分が何に貢献する仕事をしているのかわからなくなる、やりがいを見失っていく。。。私は人一倍自分の意思が反映されるかに価値を置いている人間であることを自覚しているが、同僚の方々も少なからず同じような感覚を抱いていたのではないかと思う。
転職で実現したかったこと
データ分析スキルが身につく職に就くこと
課題を見つけ解決策を打ち出す力をつけるために、人並み以上のデータ分析スキルを身につけたいと考えた。マーケティングに興味(オンラインよりもオフライン寄りなのだが...)があり、自分でデータを自在に操り、施策を生み出せる人に憧れを抱いていた。そして、特定の会社に依存せず、どこへ行っても通用する力をつけたいと考えた。
企画・提案ができる
個人的な価値観として、自分の手がけることに自分の意志が反映されているか否かは、達成感ややりがいに大きく影響を与えている。そのため、転職先ですぐに実現はできなくても、降りて来る仕事をこなすのではなく、仕事を作る側に回るための道筋となる転職にしたかった。
「チームで達成感を共有できる」は?
これは追い求めなくなった。仕事に達成感を求めていない人も多い気がするし、優れたビジネスモデルの元で自分でチームを作って率いれるようにでもならない限りは運次第としか言いようがない。これは夢に格上げ?格下げ?することにした。
転職してみて
データ分析スキルは身についたか
最低限のデータの加工スキルは身についたと思う。このままいけば、正確さとスピードを上げるための技術習得と経験は得られるだろう。「データ分析」のスキルという意味では全く身につけていない。データアナリストというよりは、データマート拵えおじさんなのである。
企画・提案ができているか
マーケティングに近い領域で、事業に深く踏み込めそうな仕事につくには、スキルセットが足りなかったようで妥協した部分もある転職になった。そのため、企画・提案の機会が乏しいことも想定していた。 まずはデータ分析基盤を理解し、データを扱う技術を身に付けさせたいという上長の意向もあり、企画や提案の機会は少ない部署に業務委託として客先常駐で配置されている。データマート拵えおじさんなのである。
危機感はどこから来ているのか
環境の変化
常駐先の企業も絡んだ職場の天変地異により人員が大きく減り、データアナリスト歴数ヶ月の自分一人で他社パートナーと仕事をしなければならない環境が目の前に迫っていることが大きい。仕事をこなすスキルは身についたが、+αを上乗せして提案できる技術も知識も持ち合わせておらず、降りて来る仕事をこなすだけ状態に陥りそうなのだ。
成長の鈍化
自己研鑽への意欲も翳り、成長が鈍化し始めている。インプットしても現場で活かす機会が見当たらず、アウトプットの機会に乏しいことから知識が定着していかない。そもそも自己学習の戦略そのものがうまくない気がしているが、それはまた別の機会に。
ブログを始める理由
今抱えている危機感は、決して環境のせいだけでなく自分の意識づけの問題も大いにあると思う。
とにかくアウトプットの場が必要、ということでブログを始めることにした。
キャリアについて考えていること、技術的なメモなど、巷に星の数ほど溢れるブログに仲間入りするとしよう。
文章を綴るのは好きだったのだが、すっかり衰えているなと実感した。。。