未経験からデータサイエンティスト

未経験からデータサイエンティストの勉強したことの備忘録とか雑記とか

「異常音検知の現状と展望」まとめ

こんにちは。 sue124です。

先日産総研の第46回AIセミナー「AIによる音環境理解を目的とした環境音分析」というセミナーを受けたので、それに関してまとめてみたいと思います。

第46回AIセミナー「AIによる音環境理解を目的とした環境音分析」リンク

このセミナーは2つプレゼンがありました。

今回は資料がすでに公開されている後半のプレゼン「異常音検知の現状と展望」についてをまとめます。

「異常音検知の現状と展望」資料 リンク

今回のテーマ

今回のプレゼンのゴールは「機械の異常、故障を検知するために、未知のものも含めてどうやって異常音を検知するか」でした。

機械学習に詳しい方ならお気づきかと思いますが、「未知のものも含めて」というのがこの問題の非常に難しいところです。

今回のプレゼンは「正常な状態と、未知の異常を見分けるための現状のノウハウの紹介」と言ってもいいかもしれません。

未知の異常を見分けるための方策の変遷

「異常音」というのは滅多に発生しないので、そもそも既知の異常でもデータは少ないです。 まして未知の異常に関しては、データがありません。

そのような状態に対して、以下のような方策が取られたようです。

DCASE2020以前

「DCASE2020」というコンペ以前は、以下のような方策を試したそうですが、うまくいかなかったそうです。

  • エンコードした音に対して、正常音を元通りに復元できるようにトレーニングしたエンコーダを作って、元の音とエンコードした音の差を「異常度」として異常を見分ける

 → エンコーダの汎化性能が良いと、異常音の異常度が上がらず。

  • 正常音に別の音(叩く音、擦れる音など)を足したものを「異常音」として、正常/異常を見分けるモデルを作成

 → 正常を異常と判定する誤報が増えた。

DCASE2020で出た解法

そんな中「DCASE2020」というコンペが開かれました。

このコンペは「ミニ四駆やファンなどの全個体の正常音と一部個体の異常音が与えられた状態で、異常音が与えられていない個体の正常音と異常音(未知の異常含む)を見分ける」というものです。

興味深いのは、上位チームがすべて以下のような解法に辿り着いた、というところです。

  • ある個体Aの正常音に対して、別の個体の正常音を「個体A」の異常音として、モデルを作る

イメージとしては「下図のように各個体の正常音をマッピングして、別個体の正常音は異常とする」というものです。

f:id:sue124:20210228072355p:plain

一見奇抜に見えますが、よくよく考えると合理的だな、と感じました。

「異常」を見分ける問題は、「異常と正常を分ける境界面をどうやってうまく作るか」という点に尽きます。 そしてその「境界面」をうまく引くためには「ギリギリ正常でない異常音」がないと、妥当なところに境界面を置けず、誤報や異常見逃しが増える、という自体を招きます。

それに対して「別個体の正常音をその個体の異常音とする」というのは「正常音と少ししか違わないが、人間が明確に『正常とは異なる』と明確にアノテーションできる」ものとして、最適だったわけです。

ただこの手法に問題がないわけではなく、各個体の音が違いすぎる、または違いが少ない場合は機能しないらしく、この問題にうまく対処できなかったチームがコンペでは順位を落としたそうです。

今後の展望

色々あったのですが要約すると

「DCASE2021 を今年やるからよろしく!」

とのことです。

あとは「どこで異常音が発生しているか」を判別する取り組みをやっていくそうです。

まとめ

  • 異常検知においては「正常音と少ししか違わないが、人間が明確に『正常とは異なる』と明確にアノテーションできる」データを如何にして用意できるかが大事
  • DCASE2020では、「ある個体Aの正常音に対して、別の個体の正常音を「個体A」の異常音として、モデルを作る」という解法を使ったチームが上位に
  • DCASE2021も開催されるとのこと

安定している大企業をやめてデータサイエンティストを志した経緯

こんにちは、sue124です。
この1月にデータサイエンティストへの転職を果たしましたので、今回は私がデータサイエンティストを志すようになった経緯を書いていきたいと思います。

1. 大企業に入った経緯

まずは大企業に入った経緯から書いていきたいと思います。

高校までの私は受験勉強に明け暮れている学生でした。
今思うと「高い点を取る」ことにばかり執心していて「何のために学ぶか」という大切なことが欠落していました。
(この頃の自分を引っ叩きたい)

高校を卒業して行きたかった大学に入れたわけですが、「何のために学ぶか」が欠落していた私はすっかり向上心を失います。
かと言って大学生の間に何かやりたいことを見つけることもできませんでした。

ギリギリ留年しない程度に勉強し、あとは遊んでるというようなダメな大学生活を送っていました。

「能ある鷹は爪を隠す」とはよく言ったものですが、厳重に隠しすぎてもはやそもそも爪はなかったのではないか、という状態でした。

時間が過ぎて就職先を考えない時期が来ました。
高校時代に「物理が得意」という理由だけで選んだ物理工学科では「ものづくり」に関する知識を学ぶことが多かったので、何となく「安定しているメーカーに就職して、安定した暮らしができればいいや」というところに一旦落ち着きました。
幸い所属した研究室の推薦枠があり、希望していた企業に入ることができました。

2. 大企業に入るも「ここではやってけない」

大企業に入って経済的には安定した生活を送ることができました。
ただ学生時代に「やりたいこと」を見つけられず、自分と真面目に向き合うことを疎かにしたまま選んだ仕事は、私には合っていませんでした。

私は正直なところコミュ力が高い方ではないので、「調整」「折衝」といったものは苦手なのですが、「調整」や「折衝」が業務の大部分を占めていました。組織が大きかっただけに「調整」「折衝」に割かなければいけない労力は膨大なものでした。
技術的なことに取り組めると思っていただけに、これは入社前に想像していたこととかなりギャップがありました。


調整業務の辛かった例の1つとして、こんなことがありました。
特に私のいた会社は、その業界の景気が良かった頃に入社した世代(50歳前後)が一番社員数が多く、私が入社した頃にその世代の方々がいわゆる「担当部長(部下なし部長)」になっていて、それぞれが社内の影響力をかなり持っている状態でした。
組織が大きくて正直自分では関係部署や「担当部長」の方を全て把握できない状態だったのですが、そういった方が何か決まりかけた頃にあちこちから出てきて「俺は何も聞いていない」と言って、全てを白紙に戻して行くことがままありました。しかもそういう方々は「用があるなら担当者の方から逐一説明に来い」という感じで、協力的とは言えないスタンスであったので、かなり苦労しました。

また説明をしに行ってももらえるのは「評論」だけで、プラスになることがあまりなかったことも、徒労感を増幅させていました。


上記のようなことや、「無理難題を担当者ひとりに負わせて、周りは知らんぷり」だったり、「新入社員を全然入れてくれないのにインターンシップの受け入れは年2回(各2週間)しないといけない」などなど、理不尽なことが多くあり、モチベーションを上げられない日々が何年か続きました。

そんな時発表された会社の中期事業計画にこんな言葉が。


 _人人人人人人人_
 > 人員3割減 <
  ̄Y^Y^Y^Y^Y^Y^Y ̄


これを見て、完全に会社から心が離れました。

3. データサイエンスとの出会い

会社の中期事業計画が発表された頃、会社の上層部が世間でビッグデータの活用で成果を上げているのを見て「我が社でもビッグデータの活用を進めよう」と言い始めたのが、私とデータサイエンスとの出会いでした。

前述の通り調整業務が大半を占める仕事が合っていないと感じていた私にとって、「データ解析」「データ活用」を専門にできるデータサイエンティストはかなり魅力的でした。
私は迷わず社内で開かれていた勉強会に参加し、必要な知識を身につけて業務に活かしたそうとしました。

ただ最初は中々うまくいかないもの。初めて扱うPythonのエラーと向き合って、時間がいつもよりかかる、ということがざらに起きました。 
そして当時調整業務をかなり詰め込まれていた私に起こったことは、「上層部からビッグデータの活用が推奨されているのに、それをしようとすると『そんなことに時間を使うな』と先輩から説教される」というなんとも矛盾した事態でした。

こんなことが続いて、前述の通り会社から心が離れていた私は、「データサイエンティスト」としてのキャリアを歩むべく転職する決心をしました。

4. さいごに

こうやって言葉にして振り返ってみると「こんな決心をするなら、就職する前にちゃんと仕事の中身をちゃんと調べてから就職しろよ」と突っ込まざるをえないキャリアを歩んでますね。

このような経緯で「データサイエンティスト」になる決心をし、ベンチャー企業での全自動ドローンの試験担当を経て、今は某IT企業でデータサイエンティストをしています。
直接データサイエンティストに転職しなかったのは、いきなり未経験から転職するのが厳しかったため、これまでの生産技術の経験を活かせてデータサイエンスに少しずつシフトできる仕事を選んだためです。
(まぁこのベンチャーが色々酷かったのですが、それはまた別の話)


正直なところ新卒で入った会社は仕事が合ってなさすぎて、ストレスが酷くて鬱の一歩手前で白髪がかなり増えていたのですが、今ではそれらはなくなりました。
データサイエンティストとしてやってくには、これからも最新の技術のキャッチアップしていくために勉強し続けないといけないですが、それは全然苦ではなく、むしろ楽しみながらやってます。

データサイエンティストになるまで勉強したこと、今勉強していることも、またの機会に書いていきたいと思います。

チラシの裏の落書きみたいなモンでしたが、お読みいただきありがとうございました。

機械学習による異常検知 2−1. 1変数正規分布

どうも、sue124です。

今回は前回に引き続き書籍「入門 機械学習による異常検知」のことを書いていこうと思います。
今回は1変数の正規分布の場合の異常検知について、異常度の閾値設定するまでの数式導出と、それを使ったPythonでの異常検知をやっていきたいと思います。

1.異常検知の閾値設定までの数式導出

以前の記事で紹介した通り、以下の3ステップで異常検知の閾値設定を行います。
sue124.hatenablog.com


ステップ1:分布推定
ステップ2:異常度の算出
ステップ3:閾値の設定

以下では正常とわかっているN個のデータ D = \{ x^{(1)}, x^{(2)}, ...,x^{(N)} \} から分布推定をして、新しく観測したデータ x' の判定をするケースを考えます。
それぞれのステップを詳しく見ていきます。

1ー1.分布推定

ここではデータの性質に応じた適切な確率分布のモデルを仮定します。
今回は確率分布のモデルに、平均値を中心として左右最小な分布となっている、正規分布を仮定して進めます。
1変数の場合の正規分布の式は、平均を \mu 、分散を \sigma^{2} とすると以下の通りです。

{ \displaystyle
N (x | \mu, \sigma^{2}) \equiv \frac{1}{(2\pi\sigma^{2})^{1/2}} \exp\left(-\frac{1}{2\sigma^{2}}(x - \mu)^{2}\right)
}

正規分布の式の導出は省略します。
(書籍の付録に、ラグランジュの未乗数法を使った導出が載っています。)

次に確率分布の式に出てくるパラメータ \mu\sigma を求めます。
これらのパラメータは通常、データに対して最も当てはまりが良い値を求める(最尤推定する)のですが、今回は1変数の場合ですので、一般に知られている平均値と標本分散の式から、以下の通り求めます。

{ \displaystyle
\hat{\mu} = \frac{1}{N} \sum_{n=1}^{N} x^{(n)}
}

{ \displaystyle
\hat{\sigma}^{2} = \frac{1}{N} \sum_{n=1}^{N} \left(x^{(n)} - \hat{\mu}\right)^{2}
}

ここで ^(ハット)はデータから推定した値であるということを示します。

1ー2.異常度の算出

新たな観測値 x' の異常度 a(x') を求めます。
それにあたってまず、1で求めた確率分布の負の対数尤度を取ると、以下の通りとなります。

{ \displaystyle
\frac{1}{2\hat{\sigma}^{2}} (x' - \hat{\mu})^{2} + \frac{1}{2}\ln({2\pi\hat{\sigma}^{2}})
}

x' に依存しない第二項を無視して、式の形を整えたものを以下の通り異常度と定義します。

{ \displaystyle
a(x') \equiv \left( \frac{x^{} - \hat{\mu}}{\hat{\sigma}} \right) ^{2}
}

上式の意味を考えると、以下の通りとなります。

  • 分子は標本平均からのずれ(=分布の中心からの距離)であり、これが大きくなるほど異常度が高い
  • 分母は標本標準偏差であり、元々のばらつきが大きい場合は多少のずれでは異常度は大きくなりにくく、ばらつきが小さいと、少しのずれで異常度が大きくなる

1ー3.閾値の設定

閾値を設定するにあたって、まずは異常度  a(x') の確率分布を考えます。
ここで下記の「ホテリング統計量の分布」の定理を使うと、 N \gg 1 の場合、異常度  a(x') は自由度1、スケール因子1のカイ二乗分布に従うことがわかります。

<定理:ホテリング統計量の分布>
1次元の観測データ D = \{ x^{(1)}, x^{(2)}, ...,x^{(N)} \} が独立に同じ分布 N(\mu, \sigma^{2} に従い、新たな観測値 x' も同じ分布に従うする。
このとき、異常度  a(x') の定数倍は自由度 (1, N-1) のF分布に従う。

{\displaystyle
\frac{N-1}{N+1} a(x') \sim F(1, N-1)
}

特に、 N \gg 1 の時は、  a(x') は自由度1、スケール因子1のカイ二乗分布に従う。

{\displaystyle
a(x') \sim \chi^{2}(1, 1)
}

書籍内では、異常度  a(x') の分母と分子がそれぞれカイ二乗分布に従って、その比がF分布に従うことの証明がありましたが、ここでは省略します。

カイ二乗分布の性質は既によく知られているので、それを活用します。
Rであれば、次のコードでカイ二乗分布のグラフを描画できます。

curve(dchisq(x, 1), xlim=c(0,4))

初めてRを使ってみたのですが、たったの1行でグラフを出力できることに驚きました。
出力結果は、下図のようになります。

f:id:sue124:20210131150347p:plain
カイ二乗分布

新たに観測した値の異常値  a(x') が異常かどうかを考えるには、それを正常時の分布に照らしてどの程度「あり得ない」のかを計算する必要があります。
そのためには、「その異常値より右側の面積が全体の面積( a(x') の全区間積分)に占める割合」を求めます。
 a(x') = 2 の場合ですと、下図の赤斜線部の面積の割合を求めることになります。

f:id:sue124:20210131151950p:plain
 a(x') = 2 の場合

これによって、物理量などの特性に依存せずに客観的に異常判定の閾値を決めることができます。

ここで話を異常度の閾値の設定に戻します。
例えば、「閾値を1%値に選ぶ」場合、「赤斜線部の面積が全体の1%になるような  a(x')閾値にする」ということになります。

これで異常度がこの値より大きい場合は、それを「異常」と判定することができるようになりました。

2.Pythonでの異常検知

では上記を活かしてPythonで異常検知をしていきたいと思います。
データセットとしては、irisを使っていきます。
まずはデータを準備します。

import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns
from scipy.stats import chi2

iris = sns.load_dataset('iris')

# 種ごとにDataFrameを分割する
setosa = iris[iris['species']=='setosa']
versicolor = iris[iris['species']=='versicolor']
virginica = iris[iris['species']=='virginica']
# setosaのデータに他の種のデータを1つずつ混入させる
df = pd.concat([setosa, versicolor.head(1), virginica.head(1)]).reset_index()
display(df.tail())

setosaのデータに他の種のデータを1つずつ混入させたDataFrameの末尾はこんな感じになっています。

f:id:sue124:20210211230144p:plain

ヒストグラムを見てみます。

plt.hist(df['sepal_length'])
plt.xlabel('sepal length (cm)')
plt.ylabel('count')
plt.show()

混入させたデータは、元のヒストグラムの山から外れた位置にいます。
※setosaデータの山が左右対象でないので厳密には正規分布を当てはめるのは本当は相応しくないのですが、正規分布以外の場合はまた別の機会に書きます。

f:id:sue124:20210211230442p:plain


データを分布推定用のものと、異常度評価用に分けて異常度を算出していきます。
異常度の閾値は1%値にすることにします。

 データ分割
df_train = df[:45]
df_test = df[45:].reset_index(drop=True)
# %%
# 異常度算出
mu = df_train['sepal_length'].mean()
s2 = ((df_train['sepal_length'] - mu)**2).mean()
a = (df_test['sepal_length'] - mu)**2 / s2

print(f'標本平均:{mu:.3g}')
print(f'標本分散:{s2:.3g}')
print(f'異常度:\n{a}')

# 閾値の設定
th = chi2.isf(0.01, 1)
print(f'カイ2乗分布による1%水準の閾値: {th:.3g}')
# %%
plt.plot(range(len(a)), a, 'bo')
plt.axhline(y=th, color='red')
plt.ylabel('anomaly score')
plt.show()

出力はこのようになり、最後に付け足した種が異なる2つがうまく異常と判別できていることがわかります。
このようにすることで異常なデータを見つけることができるので、測定ミスなどで混入した不要なデータを取り除く作業(クレンジング)に使うことができます。

標本平均:5.01
標本分散:0.129
異常度:
0     0.346715
1     0.061468
2     1.314829
3     0.649251
4     0.000960
5    30.773146
6    12.923550
カイ2乗分布による1%水準の閾値: 6.63

f:id:sue124:20210211231411p:plain

3.まとめ

今回は1変数の正規分布の場合の異常検知についてを書きました。
数式を導出し、Pythonでirisデータセットを使って、異常検知を順を追ってみていきました。
次回はこれを多変数の場合に拡張したいと思います。
お読みいただきありがとうございました。

機械学習による異常検知 1. 異常検知ことはじめ

どうも、sue124です。
久しぶりにブログを書いていこうと思います。

今回は書籍「入門 機械学習による異常検知」をちょうど読み終わったので、この本をもとに「異常検知」に関して、自分が理解したことを備忘録的に書いていこうと思います。

この本は、数式での導出が丁寧になされてる良書でした。
(その分内容がヘビーで、線形代数の本と行ったり来たりして、大変でしたが)

このブログでは数式の細かい導出までは丁寧に書かないつもりなので、そこが気になる方は是非一読ください。


今回は冒頭の「異常検知の基本的な考え方」についてまとめます。

1-1. 異常検知の基本的な流れ

与えられたデータ(変数)を基に異常検知をしていくわけですが、異常検知のモデル構築は一般的に以下の3ステップで行われます。

ステップ1:分布推定

まずは正常のモデルを作ります。
全てのデータが一致することはないので、ばらつきを含んだモデル(確率分布)をつくることになります。

ステップ2:異常度の定義

正常からのずれ度合い、すなわち異常度を定義します。

ステップ3:閾値の定義

正常 or 異常 を判定するために、異常度に閾値を設定し、異常度が閾値を超えたら異常と判定します。


上記の「分布」「異常度」「閾値」が異常検知の三大要素と本の中では呼ばれています。
この本で紹介されている異常検知の手法は、基本的に上記の3ステップに沿って行われます。

1-2 機械学習で確率分布を求める

データから正常モデルを構築するには、対象となる系の性質に応じて、解くべき問題が以下のように変わります。
対応する章も、併せて記載します。

  • 密度分布問題:2, 3章

入出力に区別がなく、観測順序が重要でない場合

  • 次元削減問題:5章

データが多次元で、パターンの把握に役立つ次元とそうでない次元が混在している場合

  • 回帰問題:6章

入力と出力の関係が重要だが、観測データの順序が重要でない場合

  • 分類問題:3.3節と8.3節

出力が離散値(カテゴリー)をとる場合
(基本的には出力が離散値出なく、どの程度そのカテゴリーに近いかを知りたいことが多いので、この本では深堀はされていない)

  • 時系列問題:7章

入力と出力の関係に加えて、観測データの順序が重要である場合

1-3. 異常の度合いを数値で表す

正常時の確率分布 p(x) が与えられているとすると、以下のことが言えます。

  • 正常時に出現確率が大きい観測値は、異常度が低い
  • 正常時に出現確率が小さい観測値は、異常度が高い

このことから、観測値 x' の一つの自然な異常度 p(x') として、以下を採用します。


a(x') = -\ln p(x')

上式では自然対数のマイナス(負の対数尤度)を取っているので、観測値の出現確率が低い場合に高い異常度が与えられることになります。
また、負の対数尤度を取ることには、以下のメリットがあります。

  • マハラノビス距離と直接の対応がつく(正規分布の場合)
  • -\ln p(x')x' の確率分布にわたり平均したものは、平均情報量※になる

※平均情報量は、以前私が記事にした決定木に関する記事でも出てきたものですね。
sue124.hatenablog.com



今後の記事では、上記の考えに基づいて異常検知を考えていきます。
次回は1変数の正規分布の異常検知に関してを書く予定です。

エントロピーとジニ不純度の比較

こんにちは、sue124です。
昔書いた「Python実践データ分析100本ノック」に関して書いた記事で、「決定木のモデル作成には平均情報量(エントロピーを使っている」と書いたものの、書籍の中で使用している「sklearn.tree.DecisionTreeClassifier」のモジュールではCARTというアルゴリズムジニ不純度を指標にして決定木を構築していることがわかったので、今回はエントロピーとジニ不純度の違いをまとめてみました。

決定木の基本的なことは以前書いた以下の記事を参照願います。

sue124.hatenablog.com


ノード t内でクラス i(全 c個)に属するサンプル数を n_{i}とするとクラス iに属するサンプルの割合を以下のように書くとします。

{ \displaystyle
 p(i|t) = \frac{n_{i}}{n}
}

このとき、エントロピー、ジニ不純度の定義はそれぞれ以下の通りとなります。

エントロピー
{ \displaystyle
 I_{H} = -\sum_{i=1}^{c} p(i|t)\ log\ p(i|t)
}

<ジニ不純度>
{ \displaystyle
 I_{G} = 1 - \sum_{i=1}^{c}p(i|t)^{2}
}


ここで以下の場合を考えてみます。

  • ノード tに単一のクラスしか入っていない場合(最も不純度が低い状態)

エントロピー
{ \displaystyle
 I_{H} = -\sum_{i=1}^{1}\frac{n}{n} \ log\ (\frac{n}{n}) = 0
}

<ジニ不純度>
{ \displaystyle
 I_{G} = 1 - \sum_{i=1}^{1}(\frac{n}{n})^{2} = 0
}

いずれの場合も0になります。

  • ノード tの全てのサンプルが異なるクラスである場合(最も不純度が高い状態)

エントロピー
{ \displaystyle
 I_{H} = -\sum_{i=1}^{c}\frac{1}{c} \ log\ (\frac{1}{c}) = log\ c
}

<ジニ不純度>
{ \displaystyle
 I_{G} = 1 - \sum_{i=1}^{c}(\frac{1}{c})^{2} = 1 - \frac{1}{c}
}

それぞれの上限はクラス数 c次第。


ここで簡単の為にクラスが2個の場合を考え、片方のクラスのデータ数を xとおくと、
エントロピー
{ \displaystyle
 I_{H}(x) = -\frac{x}{n}\ log\ \frac{x}{n} - \frac{n-x}{n}\ log\ \frac{n-x}{n}
}

<ジニ不純度>
{ \displaystyle
 I_{G}(x) = 1 - (\frac{x}{n})^{2} - (\frac{n-x}{n})^{2} = \frac{2nx-2x^{2}}{n^{2}}
}


これらを x微分すると、
エントロピー

{ \displaystyle \frac{d}{dx}I_{H}(x) = -\frac{2}{n}\ log\ \frac{x}{n} + \frac{2}{n}\ log\ \frac{n-x}{n}
}

<ジニ不純度>
{ \displaystyle
 \frac{d}{dx}I_{G}(x) = \frac{2-4x}{n^{2}}
}


これらから、ともに 0\leq x <\frac{1}{2}n で単調増加、 \frac{1}{2} < x \leq nで単調減少となることがわかります。
{ \displaystyle \frac{d}{dx}I_{H}(x)} x=0,nで∞となるので、このエントロピーの方が 0,n周辺での立ち上がりが急になりますね。

以上、エントロピーとジニ不純度の違いを比べてみました。
数式に若干の違いはあれど、表しているものの意味としては大差のない印象でした。

Pythonで作った決定木のモデルを可視化する

こんにちは、sue124です。
前回は以下の書籍の写経を終えた感想を書きましたが、その中で本書の良くない点として挙げた「特に決定木のモデルの可視化が省略されている」ことに関して、自分でやり方を調べたので、書いていきたいと思います。

今回は巷で良く使われているアヤメのデータから品種を特定する決定木のモデルを作って、それを可視化していきます。

import pandas as pd
#対応する名前に変換する関数
def name(num):
    if num == 0:
        return 'Setosa'
    elif num == 1:
        return 'Veriscolour'
    else:
        return 'Virginica'

#アヤメのデータを格納する(説明変数をdataX, 目的変数をdataYとする)
from sklearn import datasets

data = datasets.load_iris()

iris_dataX_df = pd.DataFrame(data=data.data, columns=data.feature_names)

iris_dataY_df = pd.DataFrame(data=data.target)
iris_dataY_df = iris_dataY_df.rename(columns={0: "Species"})
iris_dataY_df["Species"] = iris_dataY_df["Species"].apply(name)

iris_dataX_df, iris_dataY_df の中身はそれぞれこんな感じです。

iris_dataX_df.head()

f:id:sue124:20200419205659p:plain

iris_dataY_df.head()

f:id:sue124:20200419205723p:plain

このデータから決定木のモデルを作ります。

#データを分割する(訓練用:評価用 = 7:3)
from sklearn.model_selection import train_test_split
X_train, X_test, Y_train, Y_test = train_test_split(iris_dataX_df, iris_dataY_df, test_size=0.3)

#決定木モデル作成
from sklearn.tree import DecisionTreeClassifier
model = DecisionTreeClassifier()
model.fit(X_train, Y_train)

上記で作ったモデルの可視化するには、以下のようにします。

#決定木モデルの可視化
import matplotlib.pyplot as plt
from sklearn.tree import plot_tree
%matplotlib inline

fig = plt.figure(figsize=(20, 10))
plot_tree(model, fontsize=14);

f:id:sue124:20200419210050p:plain

たったこれだけで可視化できます。数行でかけるのだから、書籍の中で省略してほしくなかったなーと思います。
決定木って作ったモデルを可視化してナンボですし。
あと図中には明示されていませんが、左に分岐すると枠内に書いている条件が「True」、右に分岐すると「False」です。


ところで図を良く見ると「gini」の文字が。よくよく確認すると、このモジュールでは決定木のモデル生成に「エントロピー」でなく「ジニ不純度」を使っているそうです。公式ドキュメントを見てても良く「impurity」という単語が出てくるなぁと思ったら、そういうことだったらしいです。

###2020/06/07 追記###
エントロピーとジニ不純度の違いについてまとめてみました。
sue124.hatenablog.com

「Python実践データ分析100本ノック」をやり切った感想

こんにちは、sue124です。
また前回ブログを更新してから1ヶ月、その間に↓の書籍の写経をやり終えたので、今回はその感想(レビュー?)を書いていきたいと思います。


本書の良かった点
本に付いているサンプルデータを使ってとにかく手を動かして、Pythonのモジュールを使ってみる、という構成でした。ノック10本ごとにアウトプットが出るので、達成感があります。
私はこの本をやるまでは「pandasでcsvのデータを少し触れる」程度でしたが、この本の写経で「機械学習や画像分析、最適化問題自然言語分析等、一通りなんとなくはやり方がわかったかな?」ぐらいにはなりました。データ分析をやってみたいけど、取っ掛かりがないという人に本書はとてもおすすめ本書の良くなかった点
機械学習最適化問題の数学的な解説は全カット。(自分で調べてカバーするしかない)
・サンプルデータを使ってのデータ分析はもう少し深堀して欲しかった。特に決定木のモデルの可視化は一番肝心なところなのに省略とは。。
・サンプルコード内の記法が安定しない。特に演算子の前後にスペースがあったりなかったりする。ひどいときは1行のスペース有無が混在している。
・サンプルコードのデバッグで使ったと思われる「#print(変数)」がそのまま本に書かれている。
・ノック87本目から90本目までのサンプルコードがグダグダで、「書きかけのものを本に掲載しちゃったの?」ってぐらいの内容が載っている。サンプルデータと一緒にサンプルコードをダウンロードできるので、そちらから書き写せばなんとかなるが、これは売り物としてちょっと、、というレベル。


色々書いたら良かった点より良くなかった点の方が内容が多くなりましたが、Pythonでのデータ分析初学者が本書で勉強することでデータ分析実践への取っ掛かりができてレベルアップできることは間違いないと思います。
最後に本書を具体的におすすめしたい人を書きたいと思います。

おすすめしたい人
Pythonの基本的な部分をある程度勉強して、次のステップとしてデータ分析をやってみたい人
・画像分析とか機械学習に興味があるけど、Pythonのモジュールを動かしたことがない人

おすすめできない人
・画像分析も機械学習自然言語処理もすでにPythonでやったことのある人
 (物足りないと思います)