Help us understand the problem. What is going on with this article?

pythonではじめるデータ分析 (データ前処理〜機械学習)

はじめに

初心者向けにpythonでのデータ前処理から機械学習モデル構築までを解説したいと思います。
機械学習には勾配ブースティングを使用します。

ソースコード
https://gitlab.com/ceml/qiita/-/blob/master/src/python/notebook/first_time_ml.ipynb

本記事の内容

目次

1.データの前処理
  1-1.データの読み込み
  1-2.データの結合
  1-3.欠損地補完
  1-4.特徴量作成
  1-5.データ分割
2.機械学習
  2-1.データセット作成とモデルの定義
  2-2.モデルの訓練と評価
  2-3.特徴量の重要度を確認

データセットについて

・提供元:カルフォルニア工科大学
・内容:心臓病患者の検査データ
・URL :https://archive.ics.uci.edu/ml/datasets/Heart+Disease
・上記URLにあるprocessed.cleveland.data, reprocessed.hungarian.data, reprocessed.hungarian.data, processed.switzerland

※ データのダウンロードは以下参照
pythonではじめるデータ分析 (データの可視化1)
https://qiita.com/CEML/items/d673713e25242e6b4cdb

解析目的

データセットは患者の病態を5つのクラスに分類しています。
この5つのクラスを機械学習に予測させます。
機械学習モデルは勾配ブースティングを使用します。

1. データ前処理

1.1 データの読み込み

import pandas as pd

columns_name = ["age", "sex", "cp", "trestbps", "chol", "fbs", "restecg", "thalach", "exang", "oldpeak","slope","ca","thal","class"]

cleveland = pd.read_csv("/Users/processed.cleveland.data",header=None, names=columns_name)
hungarian = pd.read_csv("/Users/reprocessed.hungarian.data",sep=' ',header=None, names=columns_name)
va = pd.read_csv("/Users/processed.va.data",header=None, names=columns_name)
switzerland = pd.read_csv("/Users/processed.switzerland.data",sep=",",header=None, names=columns_name)

hubgarianとswitzerlandで引数に与えているsepは文字区切りです。
この二つのデータは一つのカラムに全てのデータが入ってしまっているので,sepでカラム毎に切り分けています。
※ カラムの詳細はデータダウンロード参照記事を参照してください。

1-2. データの結合

全てのデータを結合して一括で扱えるようにします。

merge_data = pd.concat([cleveland,hungarian,va,switzerland],axis=0)
print(merge_data.shape)

# output
'''(921, 14)'''

1-3. 欠損地補完

このデータには欠損部に'?'が入力されているので,nullに変換し、その後データを数値型に変換します。
今回は勾配ブースティングを使用するのでnull値は変換せずにそのまま使用しますが、他のモデルであればnullをなんらかの数値に置換しなければなりません。

merge_data.replace({"?":np.nan},inplace=True)
merge_data = merge_data.astype("float")

# クラスが欠損している行を削除
merge_data.dropna(subset=["class"], inplace=True)

# 欠損値確認
print(merge_data.isnull().sum())

# output
''' 
age           0
sex           0
cp            0
trestbps     58
chol          7
fbs          82
restecg       1
thalach      54
exang        54
oldpeak      62
slope       119
ca          320
thal        220
class         0
dtype: int64 
'''

1-4. 特徴量作成

特徴量から新たな特徴量を作成するステップです。
通常まずこのステップは飛ばして、集めたデータだけを使用してモデルを作るかと思います。
そこでの精度をベースとして,精度向上のために新たな特徴量を作ります。
今回はチュートリアルという事で最初から作成してみます。
例として、平均年齢からの差分を特徴量として作成します。

merge_data['diff_age'] = merge_data['age'] - merge_data['age'].mean()

1-5. データ分割

データをtrain, test, validationの3つに分割します。
この時注意しなければならないのが、病態クラスは不均衡であるという点です。
具体的にみていきます。

import matplotlib.pyplot as plt
import seaborn as sns
sns.set()

merge_data["class"].hist()
plt.xlabel("class")
plt.ylabel("number of sample")

スクリーンショット 2020-04-29 15.57.13.png

健常者の0クラスが多く、病態の重い患者は少なくなっています。
現実問題においてこのような不均衡問題はとても多いです。

この割合を変えずにデータを分割する必要があります。

from sklearn.model_selection import StratifiedShuffleSplit

# 目的変数を分離
X = merge_data.drop("class",axis=1).values
y = merge_data["class"].values
columns_name = merge_data.drop("class",axis=1).columns

# 分類するための関数を定義
sss = StratifiedShuffleSplit(n_splits=1, test_size=0.2, random_state=0) 
def data_split(X,y):
    for train_index, test_index in sss.split(X, y):
        X_train, X_test = X[train_index], X[test_index]
        y_train, y_test = y[train_index], y[test_index]

    X_train = pd.DataFrame(X_train, columns=columns_name)
    X_test = pd.DataFrame(X_test, columns=columns_name)

    return X_train, y_train, X_test, y_test

# train, test, valに分離
X_train, y_train, X_test, y_test = data_split(X, y)
X_train, y_train, X_val, y_val = data_split(X_train.values, y_train)

# shape 確認
print("train shape", X_train.shape)
print("test shape", X_test.shape)
print("validation shape", X_val.shape)

# クラスの割合を確認
plt.figure(figsize=(20,5))
plt.subplot(1,3,1)
plt.hist(y_train)

plt.subplot(1,3,2)
plt.hist(y_test)

plt.subplot(1,3,3)
plt.hist(y_val)

# output
'''
train shape (588, 14)
test shape (184, 14)
validation shape (148, 14)
'''

スクリーンショット 2020-04-29 16.39.05.png
割合を変えずに分割出来ています。

2. 機械学習

2.1 データセット作成とモデルの定義

データセットを作成して、パラメータを与えます。

import lightgbm as lgb

# データセットを作成
train = lgb.Dataset(X_train, label=y_train)
valid = lgb.Dataset(X_val, label=y_val)

# モデルのパラメータを設定
params = {
    'reg_lambda' : 0.2,
    'objective': 'multiclass',
    'metric': 'multi_logloss',
    'num_class': 5,
    'reg_alpha': 0.1,
    'min_data_leaf': 100,
    'learning_rate': 0.025,
#     'feature_fraction': 0.8,
#     'bagging_fraction': 0.8
}

2.2 モデルの訓練と評価

モデル訓練時にはアーリーストッピングを指定して、ロスが下がらなくなったところで学習をストップします。
予測はargmaxで最尤をとります。
評価は混合行列とkappa係数により評価します。
クロスバリデーションは行わずホールドアウト検証のみ行います。

# モデルを訓練
model = lgb.train(params,
                  train,
                  valid_sets=valid,
                  num_boost_round=5000,
                  early_stopping_rounds=500)

# 予測
y_pred = model.predict(X_test, num_iteration=model.best_iteration)
y_pred = np.argmax(y_pred, axis=1)

#--------------------------モデルの評価-----------------------------------------------
from sklearn.metrics import confusion_matrix
from sklearn.metrics import cohen_kappa_score

# 混合行列を作成
result_matrix = pd.DataFrame(confusion_matrix(y_test,y_pred))
# クラス毎の正解率を計算
class_accuracy = [(result_matrix[i][i]/result_matrix[i].sum())*1 for i in range(len(result_matrix))]
result_matrix[5] = class_accuracy
#kappa係数を計算
kappa = cohen_kappa_score(y_test,y_pred)
print("kappa score:",kappa)

# plot
plt.figure(figsize=(7,5))
sns.heatmap(result_matrix,annot=True,cmap="Blues",cbar=False)
plt.xticks([5.5,4.5,3.5,2.5,1.5,0.5], ["accuracy",4, 3, 2, 1,0])
plt.ylabel('Truth',fontsize=13)
plt.xlabel('Prediction',fontsize=13)
plt.tick_params(labelsize = 13)

# output
'''
kappa score: 0.3368649587494572
'''

スクリーンショット 2020-04-29 18.17.18.png

結果はあまり良くないですが、それはさておき、特徴量の重要度を確認します。

2-3. 特徴量の重要度を確認

lgb.plot_importance(model, figsize=(8, 6))
plt.show()

スクリーンショット 2020-04-29 18.29.42.png

まさかの作成した特徴量が最も低いという結果でした。

おわりに

今回はデータの前処理からホールドアウトによるモデルの評価までを行いました。
より精度を向上させるためには特徴量エンジニアリングや、ハイパラメータの検索が必要です。
また、精度を担保するためにはクロスバリデーションによる評価が必要になってきます。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした