konchangakita

KPSを一番楽しんでいたブログ 会社の看板を背負いません 転載はご自由にどうぞ

機械学習の分類モデルを比較してみる(決定木、ランダムフォレスト、ロジスティックス回帰、SVM、k近傍法)その2 Python実装編

というわけで、前回の機械学習の分類モデルPython実装にチャレンジです

分類するデータ

今回は statsmodelsのデータセットを使います
https://www.statsmodels.org/stable/index.html

1974年に行われた、女性に対して不倫の有無を聞いた調査とやらです
自己申告での聞き取り調査のようなので、信ぴょう性はともかくです。
※他意はございません、決して

モジュールのインストール

pip install statsmodels

or

conda install -c conda-forge statsmodels

データ読み込み

import pandas as pd
from pandas import Series, DataFrame
import statsmodels as sm

X = sm.datasets.fair.load_pandas().data


データ内容はこんな感じ

Number of observations: 6366
Number of variables: 9
Variable name definitions:

  • rate_marriage: How rate marriage, 1 = very poor, 2 = poor, 3 = fair, 4 = good, 5 = very good
  • age: Age
  • yrs_married: No. years married. Interval approximations. See original paper for detailed explanation.
  • children: No. children
  • religious: How relgious, 1 = not, 2 = mildly, 3 = fairly, 4 = strongly
  • educ : Level of education, 9 = grade school, 12 = high school, 14 = some college, 16 = college graduate, 17 = some graduate school, 20 = advanced degree
  • occupation: 1 = student, 2 = farming, agriculture; semi-skilled, or unskilled worker; 3 = white-colloar; 4 = teacher counselor social worker, nurse; artist, writers; technician, skilled worker, 5 = managerial, administrative, business, 6 = professional with advanced degree
  • occupation_husb : Husband's occupation. Same as occupation.
  • affairs : measure of time spent in extramarital affairs

f:id:konchangakita:20200211001412p:plain


機械学習させて、この人は不倫するの(してるの)? or しない?を分類するモデルを作ります

データの前処理

として、数字の大小に意味がない変数を変換してやります

  1. occupation , occupation_husb が数字で職業を表しているので、職業ごとの項目を作る
  2. affairs が不倫している時間を示しているので、「0:不倫していない」「1:不倫している」 に変換して、目的変数y(正解ラベル)とします

その他の数字は、Lv感をあらわしているので問題なさそうです

まずは、職業をダミー変数化します

# 本人の職業
occs = pd.get_dummies(X.occupation)
occs.columns = ['occ1','occ2','occ3','occ4','occ5','occ6']

# 旦那の職業
occs_husb = pd.get_dummies(X.occupation_husb)
occs_husb.columns = ['occ_husb1','occ_husb2','occ_husb3','occ_husb4','occ_husb5','occ_husb6']

# Stundent同志は多重共線性が強いの削除(強く相関しすぎ)
occs.drop("occ1", axis=1, inplace=True)
occs_husb.drop("occ_husb1", axis=1, inplace=True)

occs.head()

f:id:konchangakita:20200211003720p:plain

次に不倫時間の項目を0,1へ、これが目的変数yとなります

# 0以上の値が入ってれば1を返すだけの関数
def affair_check(affairs):
    return 1 if affairs > 0 else 0

y = X['affairs'].apply(affair_check)
y.head()

f:id:konchangakita:20200211215118p:plain

元のデータから変換した項目の削除と追加を行う

X.drop(["occupation", "occupation_husb","affairs"], inplace=True, axis=1)
X = pd.concat([X, occs, occs_husb], axis=1)
X.head()

f:id:konchangakita:20200211220303p:plain


各分類モデルの Python実装

を行っていきます
まず最初のお作法として、学習用のデータとテスト用のデータに80%:20%に分割します

from sklearn.metrics import accuracy_score #スコア確認用
from sklearn.model_selection import train_test_split

# 訓練データ80%、テストデータ20%
x_train, x_test, y_train, y_test = train_test_split(X, y, test_size = 0.2, random_state=0)

それぞれ、Python でライブラリが用意されているので、実装自体はめっちゃ簡単です

決定木
# 決定木
from sklearn.tree import DecisionTreeClassifier

decisiontree = DecisionTreeClassifier()
decisiontree.fit(x_train, y_train)

# テストデータを予測、予測結果を格納
y_pred = decisiontree.predict(x_test) 
acc_decisiontree = round(accuracy_score(y_test, y_pred)*100, 2) #予測結果の正解率
ランダムフォレスト
from sklearn.ensemble import RandomForestClassifier

randomforest = RandomForestClassifier()
randomforest.fit(x_train, y_train)

# テストデータを予測、予測結果を格納
y_pred = randomforest.predict(x_test)
acc_decisiontree = round(accuracy_score(y_test, y_pred)*100, 2) #予測結果の正解率
ロジスティックス回帰
from sklearn.linear_model import LogisticRegression

logreg = LogisticRegression()
logreg.fit(x_train, y_train)

# テストデータを予測、予測結果を格納
y_pred = logreg.predict(x_test)
acc_decisiontree = round(accuracy_score(y_test, y_pred)*100, 2) #予測結果の正解率
SVMサポートベクターマシン
from sklearn.svm import LinearSVC

svc = LinearSVC()
svc.fit(x_train, y_train)

# テストデータを予測、予測結果を格納
y_pred = svc.predict(x_test)
acc_decisiontree = round(accuracy_score(y_test, y_pred)*100, 2) #予測結果の正解率
k近傍法
from sklearn.neighbors import KNeighborsClassifier

knn = KNeighborsClassifier()
knn.fit(x_train, y_train)

# テストデータを予測、予測結果を格納
y_pred = knn.predict(x_test)
acc_decisiontree = round(accuracy_score(y_test, y_pred)*100, 2) #予測結果の正解率

なんて簡単なんでしょう。。。
重要なのはデータを集めることと、前処理であるということがよく分かりますね

では

比較結果を表示

してみましょう

# 5つの分類モデルの結果比較
models = pd.DataFrame({
    'Model': ['Logistic Regression', 'Random Forest', 'Decision Tree', 'SVM', 'kNN'],
    'Score': [acc_logreg, acc_randomforest, acc_decisiontree, acc_svc, acc_knn]})

f:id:konchangakita:20200212000946p:plain

今回のデータでは、ロジスティックス回帰が一番予測の精度が高いようです

では、適当にオカン世代なデータで予測してみましょう

# 適当なデータ
my_mother = pd.DataFrame({
    'rate_marriage': [4],
    'age': [63],
    'yrs_married': [40],
    'children': [2],
    'religious': [1], #宗教無し
    'educ': [12], #高卒
    'occ2': [0],
    'occ3': [1], #事務
    'occ4': [0], 
    'occ5': [0],
    'occ6': [0],
    'occ_husb2': [0],
    'occ_husb3': [0],
    'occ_husb4': [1], #自営業
    'occ_husb5': [0],
    'occ_husb6': [1],
})
logreg.predict(my_mother)

# 出力
array([1], dtype=int64)

おっと、”1”ということ不倫しているッッ!!

いったいどのあたりが影響しているのか気になるので、ここでちょっと内部の特徴量を確認みましょう

# モデルそれぞれ特徴量の重要度と名前を抽出
feature_imp = logreg.coef_.tolist()[0]
feature_name = X.columns.values.tolist()
# 特徴量の名前と重要度を一つデータフレームにまとめる
features = pd.DataFrame({
    "feature name": feature_name,
    "feature importance": feature_imp
})
features.sort_values(by='feature importance', ascending=False)

f:id:konchangakita:20200212004320p:plain

ははーん、この結婚期間がポイントなってそうですね
職業のところは0, 1なのに、ここは結婚期間が長いほど大きくなっていきます
他の入力数値もいろいろいじってみましたが、この数値が大きく影響しているようです
単純に年数のままじゃなく、期間ごと(1-3年は"1"、10年以上は"5")みたいにすれば変わるのかな?
そこはまた勉強していきます

以上です