【Kaggle】ディープラーニングでパルサー星か分類してみた




データを見るのって楽しいですよね。データを可視化することも好きですし、予測精度が高い統計モデルを作れたときも嬉しいです。僕ら人間が直感的に把握できない法則のようなものが潜んでいると思うとワクワクします。

今仕事で使っているデータは、データ数が非常に少なく精度があまり出ません。そこでたまには、大量のデータを使って機械学習をしてみたいなぁと思いKaggleのデータを使って趣味として機械学習をしてみることにしました。

今回使ったデータセット

Predicting a Pulsar Star」というデータセットを使いました。パルサーというのは、非常に規則正しい周期で電波やX線を放射する星のことです。このデータセットは説明変数として計測器の信号データ8種、目的変数としてパルサーか否かの情報が含まれています。(0:パルサー以外、1:パルサー)

今回はデータを統計的に扱いたいというモチベーションなので、パルサーについての詳細は扱いません。詳しく知りたい方はWikipedia「パルサー」をご覧ください。

早速分析していきます

import numpy as np
import pandas as pd
from pandas import Series, DataFrame
import matplotlib.pyplot as plt
import seaborn as sns
import sklearn
from tensorflow import keras
import tensorflow as tf
from tensorflow.keras import layers
from keras.models import Sequential
from keras.layers.core import Dense, Activation
from tensorflow.keras.callbacks import EarlyStopping
%matplotlib inline

#データの読み込み
master_data = pd.read_csv('pulsar_stars.csv')
data = master_data

まずは必要なモジュールをインポートします。僕の場合、読み込み先のデータはmaster_dataとしてずっと保持しておき、dataのほうをいろいろ整形していきます。

data.head()

とりあえず、最初の5行だけ見てみます。

data.head()の結果

なんとなくそれっぽい数字が入っているのが分かりました。他にも情報を拾ってみます。

data.info()
data.info()の結果

これはすごい!欠損値Nanはあらかじめ落とされているようです。データ数は17898ですね。これだけあると楽しみです。

data.describe()
data.describe()の結果

mean(平均)を見ると、変数によってとる値の範囲が結構違いそうです。負の値になるものもあれば、正の値しか取らないものもありますね。一番右のtarget_class(パルサーか否か)は0か1しか取らないので気にしないでください。

#各列名の最初にある半角空白を削除する
column_list = [name.strip() for name in data.columns]
data.columns = column_list

#ヒストグラムに描画
plt.figure(figsize=(17, 35), dpi=200)
i = 1
for col in column_list:
    plt.subplot(5, 2, i)
    #plt.hist(data[col])
    sns.distplot(data[col])
    plt.title(col)
    i += 1

数字だけだとイメージしにくいので、グラフをつくってみます。その前に元データの列名には頭に半角空白” “が入っていて、ややこしいのでそれを取ることにします。

各変数のヒストグラム

正規分布のようなものや、ポアソン分布のようなものなどありました。明らかに異常と思われるデータもありませんので、どうやらこのまま使えそうです。曲線はカーネル密度推定を行ったものです。

X = data.drop(['target_class'],axis=1)
Y = data['target_class']

#標準化を行う
def norm(x):
    mean = np.mean(x)
    std = np.std(x)
    return (x - mean) / std

X_normed = norm(X)


train_x, test_x, train_y, test_y = sklearn.model_selection.train_test_split(X_normed, Y)

説明変数Xと目的変数Yに分けて、Xを正規化します。そして、正規化したX_normとYを使ってトレーニング用とテスト用にデータを分割します。

# エポックが終わるごとにドットを一つ出力することで進捗を表示
class PrintDot(keras.callbacks.Callback):
    def on_epoch_end(self, epoch, logs):
        if epoch % 100 == 0: print('')
        print('.', end='')

#DNNのモデル構築
def make_dnn_model(train_x, test_x, train_y, test_y):

    log_filepath = 'logs'
    tbcb = keras.callbacks.TensorBoard(log_dir=log_filepath)

    model = Sequential()
    model.add(Dense(20, activation="relu", input_shape=[len(train_x.keys(),)])),
    model.add(Dense(20, activation="relu")),
    model.add(Dense(2, activation="softmax"))
    model.compile(loss='sparse_categorical_crossentropy', optimizer="SGD", metrics=['accuracy'])

    EPOCHS = 1000
    history = model.fit(train_x, train_y, epochs=EPOCHS, validation_split = 0.2, verbose=0 ,batch_size=32,                      
    callbacks=[EarlyStopping(patience=50), PrintDot()])
    
    return model, history

さっそくKerasを使ってモデルを定義していきます。今回は説明変数が8つしかなかったので、層とニューロン数は抑えめに設定しました。

活性化関数はとりあえずReluを使い、出力層の活性化関数はsoftmaxです。損失関数はsparse categorical entropy,つまり目的変数をone-hotにした上でクロスエントロピーを利用します。出力層の数を2に設定していることを思い出してください。最適化アルゴリズムはとりあえずSGDです。

model,history = make_dnn_model(train_x, test_x, train_y, test_y)

レッツディープラーニング!

#DNNの学習結果をグラフに描画する
def plot_history(history):
    hist = pd.DataFrame(history.history)
    hist['epoch'] = history.epoch

    plt.figure(figsize=(8,3),dpi=150)
    plt.xlabel('Epoch()')
    plt.ylabel('Accuracy')
    plt.plot(hist['epoch'], hist['acc'],
               label='Train Error')
    plt.plot(hist['epoch'], hist['val_acc'],
               label = 'Val Error')
    plt.legend()
    
    plt.figure(figsize=(8,3),dpi=150)
    plt.xlabel('Epoch()')
    plt.ylabel('Loss')
    plt.plot(hist['epoch'], hist['loss'],
               label='Train Error')
    plt.plot(hist['epoch'], hist['val_loss'],
               label = 'Val Error')
    plt.legend()
plot_history(history)

学習が終わりました。今回はエポック50回連続でlossが改善しなかった場合は打ち切るように設定しましたが、念の為に学習曲線を見てみます。

トレーニング過程(正解率)

検証データに対する精度は98%を超えています。かなりいい精度です。

トレーニング過程(損失)

クロスエントロピーも0.05にかなり近いところまで来ました。今後の課題として、もう少し精度が上げられそうな気がします。

model.evaluate(test_x, test_y,  batch_size=8)

#[0.07484402688771653, 0.9781005586592179]

テストデータに対するlossは0.074、正解率は97.8%です。じっくりとハイパーパラメーターを選んだわけではありませんが、かなり高い正解率のモデルをつくることが出来ました!

感想

質も量も十分だったからなのか、簡単に90%以上の精度を出すことが出来ました。Kerasを使うことで簡単にディープラーニングを行うことが出来るというすごい時代になったなとしみじみ思うのでした。