Pythonではじめる機械学習ー2章まとめ

長らく更新が空いてしまいました。
読んだ本を忘れないように、章ごとにまとめていきます。

今回はPythonではじめる機械学習の2章(教師あり学習)です。
もっと細かい部分を見たい場合は書籍を見てください。

概要

教師あり学習

  • 学習用データとして、入力データと出力データのセットを用いる
  • クラス分類(classification)と、回帰(regression)に大別される
    • クラス分類
      あらかじめ定められた選択肢の中からクラスラベルを予測する(ex.スパムメールの判別)
    • 回帰
      連続値を予測する(ex.年収の予測)

モデルの汎化

  • 汎化  : 新たなデータにも適切に予測できている
  • 過剰適合: 学習用データセットにのみ過剰に適合し、新たなデータの予測精度が低い
  • 適合不足: 学習用データセットにも適合できていない

モデルの複雑さ

  • 訓練データセットに対して複雑すぎるモデル構築を行うと、過剰適合になりやすい
  • データセットのバリエーションが増えれば、複雑なモデルも構築しやすい
  • 個人的には、シンプルイズベスト。なるべく単純なモデル構築を心がけたい。

教師あり機械学習アルゴリズム

  • k-最近傍法(k-NN)
    • 予測したいデータに近いk個の学習用データポイントを参考に多数決で出力を決める
  • 線形回帰
    • y = w_0x_0 + w_1x_1 + ・・・+bで表される線形関数で予測を行う
    • 高次元のデータセットに適する
  • リッジ回帰
    • 線形回帰の重みにL2正則化を行う
    • L2正則化によって、各重みが0に近づく
  • Lasso
    • 線形回帰の重みにL1正則化を行う
    • L1正則化によって、いくつかの重みが0となる。(=自動で変数選択される)
  • ロジスティック回帰
    • シグモイド関数 \sigma(t) = \frac{1}{1 + e^{-t}})をベースに予測モデルを作成
    •  F(x) = \frac{1}{1 + e^{-(w_0x_0 + w_1x_1 + ・・・+b)}}  で表される
    • 2値分類に利用される
  • SVM
    • 判別する境界線を、各グループ間からの距離が最大になるように
      (どのグループからも離れた場所になるように)引く
    • 非線形の場合には、カーネル法を用いる方法がある。(例:RBF法)
    • 同じような意味を持つ特徴量からなる中規模データセットに強力
    • データのスケールを調整する必要がある。
    • パラメータに敏感
  • ナイーブベイズクラス分類器
    • 特徴量間に条件付き独立性を仮定し、ベイズの定理に基づく分類器
    • クラス分類にのみ適用可能
    • 大規模データセット、高次元のデータセットに適する
    • 線形モデルよりも高速だが、精度が劣ることが多い
  • 決定木
    • 非常に高速で、データのスケールを考慮する必要が無い。
    • 可視化が可能で説明しやすい
    • 決定木を拡張させたものに以下がある。
    • ランダムフォレスト
      • ほとんどの単一の決定木よりも高速で、頑健で、強力
      • データのスケールを考慮する必要が無いが、高次元の疎なデータには不適
    • XGBoost
      • ランダムフォレストよりも時間がかかるが、予測は早く、メモリ使用量も小さい
      • パラメータに敏感だが、強力で、Kaggleでもよく使用される
    • LightGBM
      • XGBoostと同じ勾配ブースティングの一種。XGBoostよりも計算時間が短い傾向がある
      • 決定木の構造が複雑になりやすく、過学習しやすい
  • ニューラルネットワーク
    • 非常に複雑なモデルを構築できる
    • 大規模データセットに有効
    • データのスケールを調整する必要があり、パラメータに敏感

後々、各アルゴリズムの詳細説明記事を書いていこうと思う。

精度比較(分類)

titanicデータセットでいくつかのアルゴリズムの精度を比較した。
実装の詳細はこちらを参照。
brskun.hatenablog.com
以下に各アルゴリズムの精度結果を示す。

f:id:brskun:20210202001328p:plain
アルゴリズムの精度結果
  • titanicの学習用データのうち、8:2の割合でtrain_data:test_dataに分割している。
  • k-NNはtrain_dataへの当てはまりは良いが、test_dataへの当てはまりは悪い。ベースラインとして手始めに構築してみる、というレベルで良いかも。
  • SVMでは、カーネル(RBF法)を使用する場合に、標準化によって精度が大きく向上している。
  • また、決定木系のアルゴリズムが、test_dataに対して適合率が高い傾向にある。
  • 決定木系の結果はハイパーパラメータのチューニングを全く実施していないので、精度向上が見込める。

まとめ

教師あり学習とその代表的なアルゴリズムについてtitanicで実装してみた。
決定木系が精度が高い傾向にあるが、標準化を実施したSVM(rbf法)も良い当てはまりの結果となっていた。

のちほど、回帰についても比較をした結果を更新したい。

titanicで色々なアルゴリズムを試してみた

いくつかのアルゴリズムの精度検証のため、titanicデータセットを使ってモデルを構築します。

今回は以下のアルゴリズムを実装します。

  • k-最近傍法(k-NN)
  • ロジスティック回帰
  • SVM(LinearSVC)
  • SVM(RBFカーネル
  • ナイーブベイズクラス分類器
  • 決定木
    • ランダムフォレスト
    • XGBoost
    • LightGBM

前処理

import numpy as np
import pandas as pd
train_csv = './dataset/train.csv'
df = pd.read_csv(train_csv)
print(df.isnull().sum() / len(df))

結果

PassengerId    0.000000
Survived       0.000000
Pclass         0.000000
Name           0.000000
Sex            0.000000
Age            0.198653
SibSp          0.000000
Parch          0.000000
Ticket         0.000000
Fare           0.000000
Cabin          0.771044
Embarked       0.002245
dtype: float64

Ageは20%がNull、Cabinは77%がNull、そのほか、EmbarkedにNullが存在

  • Ageは平均値で補完する。
  • Cabinは77%と多く欠損しているため、今回予測には使用しない。
  • Embarkedは欠損データ行を学習データから除外する。
#Ageの平均値補完
df['Age'] = df['Age'].fillna(df['Age'].mean())
#Cabinの除外
df = df.drop('Cabin', axis=1)
#Embarkedがnullの行を除外
df = df[df['Embarked'].isna() == False]

文字列データとして、Name、Sex、Ticket、Embarkedが存在し、これを予測に使用する場合は数値データに変換する必要がある。
今回は、Name、Ticketは学習データから除外し、Sex,Embarkedはダミー変数化する。
また、予測に関係ないPassengerIDも除外する。(これが予測にプラスに働く場合もあるが、現実問題では解釈できないので意味あるの?という感じ)

これらの処理を行って、train_test_splitでtrain_data:test_data=8:2にデータセットを分割する。
stratifyで指定すると、データ全体のバランスを維持してtrain_dataとtest_dataに分割できる。

#不要列の除外
df = df.drop(['Name', 'Ticket', 'PassengerId'], axis=1)
#ダミー変数化
df = pd.get_dummies(df,drop_first=True)
#変数
from sklearn.model_selection import train_test_split
df_x = df.drop('Survived', axis=1)
df_y = df['Survived']
X_train, X_test, y_train, y_test = train_test_split(
    df_x, df_y, test_size=0.2, random_state=42, stratify=df_y)

モデリング

かなり長くなってしまったので、詳細は最後にまとめて掲載する。
モデリングで最も基本的な記述は以下である。これで学習が行われる。(例として、k-NNをfitさせている。)

from sklearn.neighbors import KNeighborsClassifier
knn = KNeighborsClassifier(n_neighbors=k) #kは任意の数値
knn.fit(X_train, y_train)   
score_train = knn.score(X_train, y_train) #train_dataでの精度算出
score_test = knn.score(X_test, y_test) #test_dataでの精度算出

この「knn」や「KNeighborsClassifier」の部分がアルゴリズムによって異なる。

結果

train_dataとtest_dataでの各アルゴリズムでの精度を以下に示す。

f:id:brskun:20210202001328p:plain
アルゴリズム別の精度結果

決定木系がtest_dataに対しても高い精度が出ている。 ハイパーパラメータ調整を行えば精度向上が見込める。
SVMでは、カーネル(RBF法)を使用する場合に、標準化によって精度が大きく向上している。

ちなみに、決定木系のアルゴリズムは、そのモデルでの変数重要度を視覚化することが可能。

fi = pd.DataFrame([xgb.feature_importances_],columns=X_train.columns, index=['FeatureImportance']).T
fi = fi.sort_values(by='FeatureImportance', ascending=True)
fi.plot(kind='barh' )
f:id:brskun:20210202085452p:plain
XGBoostでのFeature Importances

XGBoostでは性別が重要視されていることがわかる。

まとめ

精度を出したい場合は決定木系、SVMが安定、という感じがある。
(もちろんデータの特性によるだろうから、まずはデータの観察が重要)
ベースラインとして、k-NNを手始めに組んでみるのもありかな。

実装コード

k-NN

from sklearn.neighbors import KNeighborsClassifier
list_k = []
list_train = []
list_test = []
for k in range(1,50): #kの値を1~50で試行
    knn = KNeighborsClassifier(n_neighbors=k)
    knn.fit(X_train, y_train)
    
    score_train = knn.score(X_train, y_train)
    score_test = knn.score(X_test, y_test)

    list_k.append(k)
    list_train.append(score_train)
    list_test.append(score_test)

# プロット
plt.xlabel("k")
plt.ylabel("score")
plt.plot(list_k, list_train)
plt.plot(list_k, list_test)

print(list_test)
a = max(list_test)
b = list_k[list_test.index(a)]
c = list_train[list_test.index(a)]
print("検証用データセットの最高精度: {}".format(a))
print("kの値: {}".format(b))
print("k: {} の学習用データセットの精度: {}".format(b,c))

ロジスティック回帰

from sklearn.linear_model import LogisticRegression
list_c = []
list_train = []
list_test = []
for c in [10 ** i for i in range(-5, 5)]: #ハイパーパラメータCの調整
    lr = LogisticRegression(C=c, random_state=42)
    lr.fit(X_train, y_train)
    
    score_train = lr.score(X_train, y_train)
    score_test = lr.score(X_test, y_test)

    list_c.append(c)
    list_train.append(score_train)
    list_test.append(score_test)

print(list_c)
print(list_test)
    
a = max(list_test)
b = list_c[list_test.index(a)]
t = list_train[list_test.index(a)]
print("検証用データセットの最高精度: {}".format(a))
print("cの値: {}".format(b))
print("c: {} の学習用データセットの精度: {}".format(b,t))

SVM

from sklearn.svm import SVC
list_c = []
list_train = []
list_test = []
for c in [10 ** i for i in range(-5, 5)]:
    svc = SVC(C=c, kernel='linear', random_state=42)#rbfの場合はkernel='rbf'とする
    svc.fit(X_train, y_train)
    
    score_train = svc.score(X_train, y_train)
    score_test = svc.score(X_test, y_test)

    list_c.append(c)
    list_train.append(score_train)
    list_test.append(score_test)

print(list_c)
print(list_test)
    
a = max(list_test)
b = list_c[list_test.index(a)]
t = list_train[list_test.index(a)]
print("検証用データセットの最高精度: {}".format(a))
print("cの値: {}".format(b))
print("c: {} の学習用データセットの精度: {}".format(b,t))

標準化する場合はStandardScalerで行う

from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
scaler.fit(X_train)
X_train_std = pd.DataFrame(scaler.transform(X_train), columns=X_train.columns)
X_test_std = pd.DataFrame(scaler.transform(X_test), columns=X_test.columns)

ナイーブベイズ

# 0,1の問題なので、ベルヌーイ分布を仮定する
# ハイパーパラメータとしてalphaが存在するが、精度は大きく変動しないため、調整は行わない
from sklearn.naive_bayes import BernoulliNB

bnb = BernoulliNB()
bnb.fit(X_train, y_train)

score_train = bnb.score(X_train, y_train)
score_test = bnb.score(X_test, y_test)

print("学習用データセットの精度: {}".format(score_train))
print("検証用データセットの精度: {}".format(score_test))

決定木

# 決定木はハイパーパラメータが多いため、今回はひとまずデフォルトで構築する
from sklearn.tree import DecisionTreeClassifier
dtc = DecisionTreeClassifier()
dtc = dtc.fit(X_train, y_train)

score_train = dtc.score(X_train, y_train)
score_test = dtc.score(X_test, y_test)

print("学習用データセットの精度: {}".format(score_train))
print("検証用データセットの精度: {}".format(score_test))

ランダムフォレスト

# 決定木はハイパーパラメータが多いため、今回はひとまずデフォルトで構築する
from sklearn.ensemble import RandomForestClassifier
rndf = RandomForestClassifier(random_state=42)
rndf = rndf.fit(X_train, y_train)

score_train = rndf.score(X_train, y_train)
score_test = rndf.score(X_test, y_test)

XGBoost

# 決定木はハイパーパラメータが多いため、今回はひとまずデフォルトで構築する
from xgboost import XGBClassifier
xgb = XGBClassifier(random_state=42)
xgb.fit(X_train, y_train)

score_train = xgb.score(X_train, y_train)
score_test = xgb.score(X_test, y_test)

print("学習用データセットの精度: {}".format(score_train))
print("検証用データセットの精度: {}".format(score_test))

LightGBM

# 決定木はハイパーパラメータが多いため、今回はひとまずデフォルトで構築する
from lightgbm import LGBMClassifier
lgb = LGBMClassifier(random_state=42)
lgb.fit(X_train, y_train)

score_train = lgb.score(X_train, y_train)
score_test = lgb.score(X_test, y_test)

print("学習用データセットの精度: {}".format(score_train))
print("検証用データセットの精度: {}".format(score_test))

時系列分析(ARIMAモデル実装)

ARIMAモデルの実装編です。

隼本を参考にしています。

前回の記事を前提に話を進めますので、まずはこちらを参照ください。
brskun.hatenablog.com

ARIMAモデル構築のステップ

前回の記事で、モデル構築のステップは以下のように進めると説明した。

  1. データを分析できる形に変換する
  2. ARIMAなどのモデルを適用し、次数を同定(=モデルの形を特定)する
  3. パラメータを推定する
  4. 推定されたモデルを評価する
  5. 予測する

この各ステップについて、Pythonで実装していく。
使用するデータはCOVID-19の日本における日次の新規感染者数のデータとする。
SIGNATEで開催中の下記コンペのデータを使用している。
(SIGNATE COVID-19 Case Dataset.xlsx)
signate.jp

==
COVID-19の感染者予測にこのモデルを適用することが適切かどうかは今回議論しない。
新規感染者数は検査数に依存し、その傾向をARIMAモデルでとらえることは難しいと考えられるが、私が時系列分析を学ぶきっかけになり、初めて時系列モデルを作ったデータであるため、このデータを使用する。

1.データを分析できる形に変換する

ARIMAモデルは定常過程に従うデータに適用することが前提となる。
定常性については前回記事を参照。

そのため、まずは分析したいデータが定常過程に従うかどうか、確認を行う。

データの確認

import pandas as pd
df = pd.read_excel("SIGNATE COVID-19 Case Dataset.xlsx", sheet_name=1)
df.head()

2020年1月16日~2020年8月8日までのデータが存在している。別途確認してほしいが、欠損値はない。(2020年8月8日現在)
f:id:brskun:20200810134734p:plain

この中から、新規感染者数の推移を確認する。

import matplotlib.pyplot as plt
df_ts = pd.concat([df['日付'][1:], df['日本国内\n新規罹患者数'][1:]], axis=1)
ts = df_ts.set_index('日付')

fig = plt.figure(figsize=(12,8))
plt.plot(ts)

f:id:brskun:20200810135757p:plain
見た目の感想としては、期待値が一定でなく、任意のk地点間での共分散も一定でない(=定常性の定義からは外れる)ように見える。
定常性を持つかどうか、統計的な検定を行うことで確認する。

定常性の確認

定常性を確認する検定として、KPSS検定、ADF検定がある。

  • KPSS検定の帰無仮説 : 対象データが単位根過程でない
  • ADF検定の帰無仮説 : 対象データが単位根過程である

※隼本のP.64には、上の説明と同時に以下の定義もある。

  • KPSS検定

  モデルをy_t = \alpha +\beta t + \sum_{i=1}^tu_i + \epsilon_tと仮定するとき、
  帰無仮説\sigma_u^2 = 0
  対立仮設:\sigma_u^2 \neq 0

  • ADF検定

  モデルをy_t = \phi_1y_{t-1} + \epsilon_tと仮定するとき、
  帰無仮説\phi_1 = 1
  対立仮設:|\phi_1| < 1

単位根過程とは、元データ(原系列)が非定常過程であり、原系列の差分を取ったデータ(差分系列)が定常過程となる過程のことである。
※差分系列: \Delta y_t = y_t - y_{t-1}

つまり、KPSS検定で帰無仮説を棄却できれば、単位根過程がある(ランダムウォークが原系列を表すモデルに存在する)、と判断し、差分系列を取るべきだと判断する。
差分系列がKPSS検定で棄却できないとき、単位根過程があるとは言えない。(単位根過程が無い、とは断言できない。帰無仮説は採択できない。)
その場合に、ダメ押しでADF検定を行い、棄却できれば、そのデータに単位根過程が無い、と判断する。
・・・と私は隼本から解釈した。

KPSS検定とADF検定は以下で実行できる。

from scipy import stats
import statsmodels.api as sm
#KPSS検定
stats, p_value, lags, crit = sm.tsa.kpss(ts, lags=1)
print(p_value)
#ADF検定
results = sm.tsa.stattools.adfuller(ts, maxlag=1)
print(results[0], results[1])
==
0.01
0.5141326404007417 0.9852963752883052

KPSS検定の結果有意水準5%で棄却できるため、1階の差分を取る。

また、以下で対象データの自己相関、偏自己相関を可視化することが可能。

import statsmodels.api as sm
fig = plt.figure(figsize=(12, 8))
ax1 = fig.add_subplot(211)
fig = sm.graphics.tsa.plot_acf(ts, lags=40, ax=ax1)
ax2 = fig.add_subplot(212)
fig = sm.graphics.tsa.plot_pacf(ts, lags=40, ax=ax2)

f:id:brskun:20200810135650p:plain
過去のデータの影響が多く残っていることがわかる。

差分を取る

diffで差分系列の生成が可能。

diff_1 = ts.diff(periods=1)
#1地点目がNaN(0地点はないため)になるため、dropna()
diff_1 = diff_1.dropna()

diff_1に対して、KPSS検定、ADF検定を行う。

from scipy import stats
import statsmodels.api as sm
#KPSS検定
stats, p_value, lags, crit = sm.tsa.kpss(diff_1, lags=1)
print(p_value)
#ADF検定
results = sm.tsa.stattools.adfuller(diff_1, maxlag=1)
print(results[0], results[1])
==
0.1
-11.507138919509886 4.348218666200526e-21

KPSS検定のp値は0.1で帰無仮説を棄却できない。(=単位根を持つといえない。)
ADF検定のp値は非常に小さく、帰無仮説を棄却できる。(=単位根を持たない)

今回のデータでは、1階差分データでモデリングを進める。
1階差分データと自己相関、偏自己相関のプロットを下に示す。

fig = plt.figure(figsize=(12, 8))
plt.plot(diff_1)

import statsmodels.api as sm
fig = plt.figure(figsize=(12, 8))
ax1 = fig.add_subplot(211)
fig = sm.graphics.tsa.plot_acf(diff_1, lags=40, ax=ax1)
ax2 = fig.add_subplot(212)
fig = sm.graphics.tsa.plot_pacf(diff_1, lags=40, ax=ax2)

f:id:brskun:20200810140050p:plain
f:id:brskun:20200810140108p:plain
図からは上昇や下降トレンドが除去されていること、7日周期で自己相関が残っていること(=季節性がある)が読み取れる。

そのため、SARIMAモデルで検討を進める。
続いてはモデルの次数を決定していく。

モデルの同定

SARIMAモデルでモデリングするにあたって、グリッドサーチで次数を同定していく。

まずは、最終的な精度確認用に、学習用と検証用でデータを分割する。
検証用データは2週間(7/26~8/8)分とする。

train_term = '2020-07-25'
test_term = '2020-07-26'
ts_train = ts[:train_term]
ts_test = ts[test_term:]

次に、次数の組み合わせのリストを作成し、グリッドサーチを行う。

import itertools

#SARIMA(p,d,q)(sp,sd,sq)[s]の次数の範囲を決める。
#範囲は計算量を減らすため、経験上、p,d,qを0~2、sp,sd,sqを0~1くらいに限定する。
p = q = range(0, 3)
sp = sd = sq = range(0, 2)

#グリッドサーチのために、p,q,sp,sd,sqの組み合わせのリストを作成する。
#定常性の確認よりd=1,周期sは決め打ちで7としている。
pdq = [(x[0], 1, x[1]) for x in list(itertools.product(p, q))]
seasonal_pdq = [(x[0], x[1], x[2], 7) for x in list(itertools.product(sp, sd, sq))]
from statsmodels.tsa.statespace.sarimax import SARIMAX
import warnings
warnings.filterwarnings("ignore") # warningsを表示させない

best_result = [0, 0, 10000000]
for param in pdq:
    for param_seasonal in seasonal_pdq:
        try:
            mod = SARIMAX(ts_train,
                          order = param,
                          seasonal_order = param_seasonal,
                          enforce_stationarity = True,
                          enforce_invertibility = True)
            results = mod.fit()
            print('order{}, s_order{} - AIC: {}'.format(param, param_seasonal, results.aic))
            
            if results.aic < best_result[2]:
                best_result = [param, param_seasonal, results.aic]
        except:
            continue

print('AICが最も良いモデル:', best_result)

結果

order(0, 1, 0), s_order(0, 0, 0, 7) - AIC: 2107.264372758358
order(0, 1, 0), s_order(0, 0, 1, 7) - AIC: 2064.6897094263486
order(0, 1, 0), s_order(0, 1, 0, 7) - AIC: 1978.745571580177
order(0, 1, 0), s_order(0, 1, 1, 7) - AIC: 1968.252072625655
order(0, 1, 0), s_order(1, 0, 0, 7) - AIC: 2032.60503887206
order(0, 1, 0), s_order(1, 0, 1, 7) - AIC: 2032.8747358608593
order(0, 1, 0), s_order(1, 1, 0, 7) - AIC: 1966.4668826409086
order(0, 1, 0), s_order(1, 1, 1, 7) - AIC: 1964.4015340247283
order(0, 1, 1), s_order(0, 0, 0, 7) - AIC: 2107.754977372526
order(0, 1, 1), s_order(0, 0, 1, 7) - AIC: 2065.501478375778
order(0, 1, 1), s_order(0, 1, 0, 7) - AIC: 1970.7779191569311
order(0, 1, 1), s_order(0, 1, 1, 7) - AIC: 1966.7078171565583
order(0, 1, 1), s_order(1, 0, 0, 7) - AIC: 2029.7706609442662
order(0, 1, 1), s_order(1, 0, 1, 7) - AIC: 2031.3583794985689
order(0, 1, 1), s_order(1, 1, 0, 7) - AIC: 1965.8745684654855
order(0, 1, 1), s_order(1, 1, 1, 7) - AIC: 1964.2989785834593
order(0, 1, 2), s_order(0, 0, 0, 7) - AIC: 2100.520943117397
order(0, 1, 2), s_order(0, 0, 1, 7) - AIC: 2058.7622249292563
order(0, 1, 2), s_order(0, 1, 0, 7) - AIC: 1968.5875014094959
order(0, 1, 2), s_order(0, 1, 1, 7) - AIC: 1962.6362919760677
order(0, 1, 2), s_order(1, 0, 0, 7) - AIC: 2024.8984699884954
order(0, 1, 2), s_order(1, 0, 1, 7) - AIC: 2026.2700117527102
order(0, 1, 2), s_order(1, 1, 0, 7) - AIC: 1961.4829569904323
order(0, 1, 2), s_order(1, 1, 1, 7) - AIC: 1959.9326195002907
order(1, 1, 0), s_order(0, 0, 0, 7) - AIC: 2108.3420616997973
order(1, 1, 0), s_order(0, 0, 1, 7) - AIC: 2065.9551822598414
order(1, 1, 0), s_order(0, 1, 0, 7) - AIC: 1973.473669971467
order(1, 1, 0), s_order(0, 1, 1, 7) - AIC: 1967.9196222187002
order(1, 1, 0), s_order(1, 0, 0, 7) - AIC: 2031.36552857066
order(1, 1, 0), s_order(1, 0, 1, 7) - AIC: 2032.6046698298364
order(1, 1, 0), s_order(1, 1, 0, 7) - AIC: 1966.8159040096539
order(1, 1, 0), s_order(1, 1, 1, 7) - AIC: 1965.0173038870148
order(1, 1, 1), s_order(0, 0, 0, 7) - AIC: 2108.547740215762
order(1, 1, 1), s_order(0, 0, 1, 7) - AIC: 2060.454795979599
order(1, 1, 1), s_order(0, 1, 0, 7) - AIC: 1968.1782914950297
order(1, 1, 1), s_order(0, 1, 1, 7) - AIC: 1963.348875889112
order(1, 1, 1), s_order(1, 0, 0, 7) - AIC: 2025.1095245679812
order(1, 1, 1), s_order(1, 0, 1, 7) - AIC: 2026.745736538373
order(1, 1, 1), s_order(1, 1, 0, 7) - AIC: 1962.4819894555606
order(1, 1, 1), s_order(1, 1, 1, 7) - AIC: 1960.7625196811855
order(2, 1, 0), s_order(0, 0, 0, 7) - AIC: 2100.6123883855353
order(2, 1, 0), s_order(0, 0, 1, 7) - AIC: 2063.0696896461995
order(2, 1, 0), s_order(0, 1, 0, 7) - AIC: 1972.3855591824868
order(2, 1, 0), s_order(0, 1, 1, 7) - AIC: 1966.122504692755
order(2, 1, 0), s_order(1, 0, 0, 7) - AIC: 2029.7308745018804
order(2, 1, 0), s_order(1, 0, 1, 7) - AIC: 2030.7667337581188
order(2, 1, 0), s_order(1, 1, 0, 7) - AIC: 1964.7360117412372
order(2, 1, 0), s_order(1, 1, 1, 7) - AIC: 1963.0500912538514
order(2, 1, 1), s_order(0, 0, 0, 7) - AIC: 2095.833672222477
order(2, 1, 1), s_order(0, 0, 1, 7) - AIC: 2057.6965130659783
order(2, 1, 1), s_order(0, 1, 0, 7) - AIC: 1969.9264899760578
order(2, 1, 1), s_order(0, 1, 1, 7) - AIC: 1963.3379332111565
order(2, 1, 1), s_order(1, 0, 0, 7) - AIC: 2025.2830018365714
order(2, 1, 1), s_order(1, 0, 1, 7) - AIC: 2026.4239847473932
order(2, 1, 1), s_order(1, 1, 0, 7) - AIC: 1961.9388180754122
order(2, 1, 1), s_order(1, 1, 1, 7) - AIC: 1960.1176123826067
order(2, 1, 2), s_order(0, 0, 0, 7) - AIC: 2061.4179226727774
order(2, 1, 2), s_order(0, 0, 1, 7) - AIC: 2035.6632837286115
order(2, 1, 2), s_order(1, 0, 0, 7) - AIC: 2015.9603720291452
order(2, 1, 2), s_order(1, 0, 1, 7) - AIC: 2015.9558978934915
AICが最も良いモデル: [(0, 1, 2), (1, 1, 1, 7), 1959.9326195002907]

以上の結果から、 SARIMA(0,1,2)(1,1,1)[7] が導かれた。

パラメータを推定する

モデルの形が特定できたので、次にパラメータの推定を行う。
書くコードとしては、上のグリッドサーチの内部でやっているようにfitを呼ぶだけである。

SARIMA_covid = SARIMAX(endog=ts_train, order=(0,1,2), seasonal_order=(1,1,1,7)).fit()
print(SARIMA_covid.summary())

結果

                                 Statespace Model Results                                
=========================================================================================
Dep. Variable:                       日本国内
新規罹患者数   No. Observations:                  192
Model:             SARIMAX(0, 1, 2)x(1, 1, 1, 7)   Log Likelihood                -974.966
Date:                           Mon, 10 Aug 2020   AIC                           1959.933
Time:                                   18:33:19   BIC                           1976.007
Sample:                               01-16-2020   HQIC                          1966.448
                                    - 07-25-2020                                         
Covariance Type:                             opg                                         
==============================================================================
                 coef    std err          z      P>|z|      [0.025      0.975]
------------------------------------------------------------------------------
ma.L1         -0.1578      0.058     -2.711      0.007      -0.272      -0.044
ma.L2         -0.1877      0.051     -3.711      0.000      -0.287      -0.089
ar.S.L7       -0.7121      0.169     -4.206      0.000      -1.044      -0.380
ma.S.L7        0.4902      0.204      2.399      0.016       0.090       0.891
sigma2      2331.5489    185.323     12.581      0.000    1968.323    2694.775
===================================================================================
Ljung-Box (Q):                       52.13   Jarque-Bera (JB):                54.54
Prob(Q):                              0.09   Prob(JB):                         0.00
Heteroskedasticity (H):              16.27   Skew:                            -0.59
Prob(H) (two-sided):                  0.00   Kurtosis:                         5.39
===================================================================================

Warnings:
[1] Covariance matrix calculated using the outer product of gradients (complex-step).

plot_diagnosticsメソッドで、残差が確認できる。

SARIMA_covid.plot_diagnostics(lags=20, figsize=(16,16));

f:id:brskun:20200810183914p:plain
Q-Qプロットから、残差が正規分布に従っていないように見える。
外生変数を追加することで、モデルの説明力を向上させることができるかもしれないため、SARIMAXでのモデリングも行い、精度を比較してみる。

外生変数として、以下を追加してみる。

  • 緊急事態宣言の影響(4/7発令から6日後の4/13 ~ 5/25全国解除の6日後の5/31までを1,それ以外を0)
  • 県境をまたぐ移動制限解除の影響(6/19解除から6日後である6/25以前を0,以降を1とするダミー変数)
import numpy as np

alert_on = pd.Series(data = 0, index = df[1:]['日付'])
alert_on['2020-04-13':] = 1
alert_on['2020-05-31':] = 0
moveRestrict_off = pd.Series(data = 0, index = df[1:]['日付'])
moveRestrict_off['2020-06-25':] = 1

sarimax_train = np.stack([alert_on[:train_term].values, moveRestrict_off[:train_term].values],1)
sarimax_test = np.stack([alert_on[test_term:].values, moveRestrict_off[test_term:].values],1)

SARIMAXモデルの同定、パラメータ推定の方法は、上で説明したSARIMAとほぼ同じになる。SARIMAXモデルのパラメータとして、exogパラメータを追加することで、SARIMAXモデルとなる。

warnings.filterwarnings("ignore") # warningsを表示させない

best_result = [0, 0, 10000000]
for param in pdq:
    for param_seasonal in seasonal_pdq:
        try:
            mod = SARIMAX(ts_train,
                          exog = sarimax_train,
                          order = param,
                          seasonal_order = param_seasonal,
                          enforce_stationarity = True,
                          enforce_invertibility = True)
            results = mod.fit()
            print('order{}, s_order{} - AIC: {}'.format(param, param_seasonal, results.aic))
            
            if results.aic < best_result[2]:
                best_result = [param, param_seasonal, results.aic]
        except:
            continue

print('AICが最も良いモデル:', best_result)

結果

order(0, 1, 0), s_order(0, 0, 0, 7) - AIC: 2105.8852264394372
order(0, 1, 0), s_order(0, 0, 1, 7) - AIC: 2063.516608402309
order(0, 1, 0), s_order(0, 1, 0, 7) - AIC: 1974.922167348323
order(0, 1, 0), s_order(0, 1, 1, 7) - AIC: 1964.0234570834143
order(0, 1, 0), s_order(1, 0, 0, 7) - AIC: 2029.9866441355844
order(0, 1, 0), s_order(1, 0, 1, 7) - AIC: 2029.9502273105097
order(0, 1, 0), s_order(1, 1, 0, 7) - AIC: 1962.4586283326307
order(0, 1, 0), s_order(1, 1, 1, 7) - AIC: 1960.9170337283738
order(0, 1, 1), s_order(0, 0, 0, 7) - AIC: 2106.34779409769
order(0, 1, 1), s_order(0, 0, 1, 7) - AIC: 2063.912796571045
order(0, 1, 1), s_order(0, 1, 0, 7) - AIC: 1967.04571335318
order(0, 1, 1), s_order(0, 1, 1, 7) - AIC: 1962.8026592227798
order(0, 1, 1), s_order(1, 0, 0, 7) - AIC: 2027.13694332749
order(0, 1, 1), s_order(1, 0, 1, 7) - AIC: 2028.6094563954866
order(0, 1, 1), s_order(1, 1, 0, 7) - AIC: 1961.951461076991
order(0, 1, 1), s_order(1, 1, 1, 7) - AIC: 1960.8816558344129
order(0, 1, 2), s_order(0, 0, 0, 7) - AIC: 2100.120865479744
order(0, 1, 2), s_order(0, 0, 1, 7) - AIC: 2057.6283589727036
order(0, 1, 2), s_order(0, 1, 0, 7) - AIC: 1965.1472366928083
order(0, 1, 2), s_order(0, 1, 1, 7) - AIC: 1959.050444293997
order(0, 1, 2), s_order(1, 0, 0, 7) - AIC: 2022.5953619074203
order(0, 1, 2), s_order(1, 0, 1, 7) - AIC: 2024.0191263044044
order(0, 1, 2), s_order(1, 1, 0, 7) - AIC: 1957.9599474261
order(0, 1, 2), s_order(1, 1, 1, 7) - AIC: 1956.792118467596
order(1, 1, 0), s_order(0, 0, 0, 7) - AIC: 2106.9133508037876
order(1, 1, 0), s_order(0, 0, 1, 7) - AIC: 2064.3537752469656
order(1, 1, 0), s_order(0, 1, 0, 7) - AIC: 1969.761032711971
order(1, 1, 0), s_order(0, 1, 1, 7) - AIC: 1963.9555458231923
order(1, 1, 0), s_order(1, 0, 0, 7) - AIC: 2028.7476936829569
order(1, 1, 0), s_order(1, 0, 1, 7) - AIC: 2029.8027076893425
order(1, 1, 0), s_order(1, 1, 0, 7) - AIC: 1962.823624633586
order(1, 1, 0), s_order(1, 1, 1, 7) - AIC: 1961.6219237522719
order(1, 1, 1), s_order(0, 0, 0, 7) - AIC: 2107.8449886846793
order(1, 1, 1), s_order(0, 0, 1, 7) - AIC: 2059.4916308976262
order(1, 1, 1), s_order(0, 1, 0, 7) - AIC: 1965.1149083203754
order(1, 1, 1), s_order(0, 1, 1, 7) - AIC: 1960.1263026585525
order(1, 1, 1), s_order(1, 0, 0, 7) - AIC: 2023.1412659619405
order(1, 1, 1), s_order(1, 0, 1, 7) - AIC: 2024.7071445871811
order(1, 1, 1), s_order(1, 1, 0, 7) - AIC: 1959.2628172347772
order(1, 1, 1), s_order(1, 1, 1, 7) - AIC: 1958.0026296432552
order(1, 1, 2), s_order(0, 1, 0, 7) - AIC: 1967.0735999245207
order(1, 1, 2), s_order(0, 1, 1, 7) - AIC: 1960.690289277761
order(1, 1, 2), s_order(1, 1, 0, 7) - AIC: 1959.5476296503423
order(1, 1, 2), s_order(1, 1, 1, 7) - AIC: 1958.1759097919055
order(2, 1, 0), s_order(0, 0, 0, 7) - AIC: 2100.1802086002217
order(2, 1, 0), s_order(0, 0, 1, 7) - AIC: 2061.562868383704
order(2, 1, 0), s_order(0, 1, 0, 7) - AIC: 1968.4476898969865
order(2, 1, 0), s_order(0, 1, 1, 7) - AIC: 1961.982545761374
order(2, 1, 0), s_order(1, 0, 0, 7) - AIC: 2026.9594531109456
order(2, 1, 0), s_order(1, 0, 1, 7) - AIC: 2028.0350371938757
order(2, 1, 0), s_order(1, 1, 0, 7) - AIC: 1960.7221500782944
order(2, 1, 0), s_order(1, 1, 1, 7) - AIC: 1959.5281067116762
order(2, 1, 1), s_order(0, 0, 0, 7) - AIC: 2096.0242366449816
order(2, 1, 1), s_order(0, 0, 1, 7) - AIC: 2056.917337152522
order(2, 1, 1), s_order(0, 1, 0, 7) - AIC: 1966.7988023910793
order(2, 1, 1), s_order(0, 1, 1, 7) - AIC: 1960.0911407068304
order(2, 1, 1), s_order(1, 0, 0, 7) - AIC: 2023.4714170764519
order(2, 1, 1), s_order(1, 0, 1, 7) - AIC: 2024.55047214786
order(2, 1, 1), s_order(1, 1, 0, 7) - AIC: 1958.8116544529967
order(2, 1, 1), s_order(1, 1, 1, 7) - AIC: 1957.364795327518
order(2, 1, 2), s_order(0, 0, 0, 7) - AIC: 2060.443008126662
order(2, 1, 2), s_order(0, 0, 1, 7) - AIC: 2032.3356492883665
order(2, 1, 2), s_order(0, 1, 0, 7) - AIC: 1966.547204725646
order(2, 1, 2), s_order(0, 1, 1, 7) - AIC: 1951.6710965940904
order(2, 1, 2), s_order(1, 0, 0, 7) - AIC: 2013.1466553406394
order(2, 1, 2), s_order(1, 0, 1, 7) - AIC: 2015.171462294734
order(2, 1, 2), s_order(1, 1, 0, 7) - AIC: 1950.89647489965
order(2, 1, 2), s_order(1, 1, 1, 7) - AIC: 1950.2435103274665
AICが最も良いモデル: [(2, 1, 2), (1, 1, 1, 7), 1950.2435103274665]

SARIMAモデルとは違う次数となった。この次数でモデリングを行う。

SARIMAX_covid = SARIMAX(endog=ts_train, exog=sarimax_train, order=(2,1,2), seasonal_order=(1,1,1,7)).fit()
print(SARIMAX_covid.summary())

結果

                                 Statespace Model Results                                
=========================================================================================
Dep. Variable:                       日本国内
新規罹患者数   No. Observations:                  192
Model:             SARIMAX(2, 1, 2)x(1, 1, 1, 7)   Log Likelihood                -966.122
Date:                           Mon, 10 Aug 2020   AIC                           1950.244
Time:                                   18:34:28   BIC                           1979.178
Sample:                               01-16-2020   HQIC                          1961.971
                                    - 07-25-2020                                         
Covariance Type:                             opg                                         
==============================================================================
                 coef    std err          z      P>|z|      [0.025      0.975]
------------------------------------------------------------------------------
x1           -58.4231     19.911     -2.934      0.003     -97.447     -19.399
x2           -59.4909     45.543     -1.306      0.191    -148.754      29.772
ar.L1          1.0240      0.066     15.430      0.000       0.894       1.154
ar.L2         -0.9289      0.070    -13.235      0.000      -1.066      -0.791
ma.L1         -1.0711      0.093    -11.519      0.000      -1.253      -0.889
ma.L2          0.8282      0.093      8.946      0.000       0.647       1.010
ar.S.L7       -0.6705      0.167     -4.006      0.000      -0.998      -0.342
ma.S.L7        0.3804      0.210      1.811      0.070      -0.031       0.792
sigma2      2146.9748    184.244     11.653      0.000    1785.864    2508.086
===================================================================================
Ljung-Box (Q):                       41.49   Jarque-Bera (JB):                56.22
Prob(Q):                              0.41   Prob(JB):                         0.00
Heteroskedasticity (H):              14.24   Skew:                            -0.72
Prob(H) (two-sided):                  0.00   Kurtosis:                         5.29
===================================================================================

Warnings:
[1] Covariance matrix calculated using the outer product of gradients (complex-step).

x1(緊急事態宣言の影響)は有意となっているが、x2(移動制限の緩和)は有意であるとは言えなかった。
そのためx1のみ採用し、モデリングする。

sarimax_train = np.stack([alert_on[:train_term].values],1)
sarimax_test = np.stack([alert_on[test_term:].values],1)

SARIMAX_covid = SARIMAX(endog=ts_train, exog=sarimax_train, order=(2,1,2), seasonal_order=(1,1,1,7)).fit()
print(SARIMAX_covid.summary())

結果

                                 Statespace Model Results                                
=========================================================================================
Dep. Variable:                       日本国内
新規罹患者数   No. Observations:                  192
Model:             SARIMAX(2, 1, 2)x(1, 1, 1, 7)   Log Likelihood                -967.710
Date:                           Mon, 10 Aug 2020   AIC                           1951.420
Time:                                   18:42:55   BIC                           1977.139
Sample:                               01-16-2020   HQIC                          1961.844
                                    - 07-25-2020                                         
Covariance Type:                             opg                                         
==============================================================================
                 coef    std err          z      P>|z|      [0.025      0.975]
------------------------------------------------------------------------------
x1           -58.5921     19.988     -2.931      0.003     -97.769     -19.416
ar.L1          1.0274      0.073     14.087      0.000       0.884       1.170
ar.L2         -0.9137      0.076    -12.006      0.000      -1.063      -0.765
ma.L1         -1.0725      0.102    -10.469      0.000      -1.273      -0.872
ma.L2          0.8157      0.099      8.216      0.000       0.621       1.010
ar.S.L7       -0.6976      0.159     -4.398      0.000      -1.009      -0.387
ma.S.L7        0.4092      0.201      2.036      0.042       0.015       0.803
sigma2      2164.7336    185.086     11.696      0.000    1801.971    2527.496
===================================================================================
Ljung-Box (Q):                       39.69   Jarque-Bera (JB):                48.98
Prob(Q):                              0.48   Prob(JB):                         0.00
Heteroskedasticity (H):              14.92   Skew:                            -0.64
Prob(H) (two-sided):                  0.00   Kurtosis:                         5.18
===================================================================================

Warnings:
[1] Covariance matrix calculated using the outer product of gradients (complex-step).
SARIMAX_covid.plot_diagnostics(lags=20, figsize=(16,16));

f:id:brskun:20200810184517p:plain
残念ながらSARIMAXでも残差が正規分布に従っていないように見える。
適切な外生変数の設計は簡単ではない。

推定されたモデルを評価する

作成した2モデルを、検証用データに適用してみる。

pred_sarima = SARIMA_covid.predict(ts.index[0], ts.index[-1])
pred_sarimax = SARIMAX_covid.predict(ts.index[0], ts.index[-1], exog=sarimax_test)

plt.figure(figsize=(20,10))
plt.plot(ts, linestyle='-')
plt.plot(pred_sarima, "red", linestyle=':')
plt.plot(pred_sarimax, "black", linestyle=':')

f:id:brskun:20200810184622p:plain

検証用データのみをプロットした図は以下の通り。

plt.figure(figsize=(20,10))
plt.plot(ts[test_term:], linestyle='-')
plt.plot(pred_sarima[test_term:], "red", linestyle=':')
plt.plot(pred_sarimax[test_term:], "black", linestyle=':')

f:id:brskun:20200810184718p:plain

データのおおよその傾向をつかめてはいるように見えるものの、数百人単位でズレが出ているところもあり、予測モデルとして使用していくにはさらなる改善が必要である。

予測する

時系列データの場合、作ったモデルで予測するためには過去のデータが必要となる。
そのため、最新のデータを取り込むために、都度モデルを更新して将来予測をしていくことが重要である。

おわりに

思っていたよりかなり長くなってしまいましたが、SARIMA、SARIMAXモデルでの時系列予測をPythonで実装してみました。
疲れた。。。

時系列分析(ARIMAモデルの紹介)

今回は時系列分析について書いてみたいと思います。

気温や株価のように日々変動するデータ(時系列データ)をモデリングする際、時系列モデルが使われます。
時系列モデルでは以下の手法がよく使われています。

  • Holt-Winters法(指数平滑化)
  • Box-Jenkins法(ARIMA)
  • 状態空間モデル

最近、ARIMAモデル(SARIMAXモデルを含む)について勉強をする機会があったので、今回はこちらを紹介したいと思います。
(長くなってしまったので、今回は時系列モデルとは?という部分の紹介、
次回でモデリングについて書いていきます。)

以下の書籍を参考にしています。

Box-Jenkins法とは

時系列分析のフレームワークのようなもの。
以下の手順でモデリングをする。

  1. データを分析できる形に変換する
  2. ARIMAなどのモデルを適用し、次数を同定(=モデルの形を特定)する
  3. パラメータを推定する
  4. 推定されたモデルを評価する
  5. 予測する

今回はARIMAモデルについて紹介し、次回、各ステップについて見ていきます。

ARIMAモデルとは

自身の過去のデータを用いて将来予測するモデル。
拡張したものにARIMAX,SARIMA,SARIMAXなどがある。
それぞれ、以下を組み合わせたものである。

  • S  :季節性(周期性変動)
    • 夏はビールが売れる、などの影響を考慮できる
  • AR  :自己回帰
    • 過去t期間のデータから受ける影響を考慮できる。
      ARだけのモデルは以下の数式で表される。


y_t = c + \sum_{i=1}^p\phi_iy_{t-i} + \epsilon_t  \epsilon_t \sim N(0, \sigma^2)

  • I   :和分過程への適用
    • 非定常データを分析する際に差分を取る。
  • MA :移動平均
    • 過去t期間の誤差から受ける影響を考慮できる。
      MAだけのモデルは以下の数式で表される。


y_t = \mu + \sum_{j=1}^q\theta_j\epsilon_{t-j} + \epsilon_t  \epsilon_t \sim N(0, \sigma^2)

  • X  :外生変数
    • 回帰分析のように、変数を取り込める。

定常性

定常性、という言葉が出てきたが、ARIMAモデルは定常データに対して適用することを想定している。
定常性の定義は以下の通り。


\begin{eqnarray} E(y_t) &=& \mu \\
Cov(y_t,y_{t-k}) &=& E[(y_t - \mu)(y_{t-k} - \mu)] = \gamma_k \end{eqnarray}

期待値が地点tによらず一定であり、共分散も地点によらず、時間差kにのみ依存する、ということを示している。
(k=0の場合は分散となり、地点によらず一定である。)

定常性を持たないデータ(上昇トレンドがあるデータなど)を当てはめる場合は、地点間の差分を取るなどして、定常データにしてから当てはめを行う。
(一階の差分系列で定常にならない場合はさらに差分を取り、二階の差分系列で当てはめを行う。)

モデルの表記

何地点前までのデータを使うか、差分を何回取るかにより、同じARIMAモデルでもモデルの形が異なる。一般的に、添え字p,d,qを用いて以下のように表現する。


ARIMA(p,d,q)

pはARの次数、dはIの次数、qはMAの次数を表す。

SARIMAモデルの場合、季節調整のために、さらにP,D,Q,sを追加して、以下のように表現する。


SARIMA(p,d,q)(P,D,Q)[s]

P:P周期前までのデータを参考にする
D:D階差分を取る
Q:Q周期前までの誤差を参考にする

おわりに

今回は、ARIMAモデルとは?という部分を紹介しました。
次回、対象データの定常性を確認し、上に挙げたようなp,d,qなどの次数を特定した後、モデリングを行うところまで確認していきたいと思います。

スケール変換をtrainデータ、testデータ別々に変換してはいけない

まぁ、当たり前といえば当たり前なのですが、、、
オライリーの「Pythonではじめる機械学習」では、標準化などの前処理をする際はテストセットを訓練セットと同じように変換することが重要と書かれています。


つまり訓練セットの分散、平均、最大値、最小値などの情報を使ってテストセットも変換する、ということですね。
自分が今まで書いたコードを見てみると、、、やっちゃってました。
 
今回は、Kaggleの House Prices: Advanced Regression Techniques | Kaggle を題材に、どの程度テストセットへの精度が変わるのか見てみたいと思います。
 

スケール変換とは?

  • 標準化
    • 平均0、分散1になるようにデータセットを変換すること。
    • Scikit-learnではpreprocessing.StandardScalerクラスが該当する。
  • 正規化
    • 決まった範囲に収まるようにデータセットを変換すること。
    • Scikit-learnではpreprocessing.MinMaxScalerクラスが該当する。
      (デフォルトは0.0~1.0)
    • 最大値、最小値の各データポイントに対する影響が大きく、外れ値に弱い。

その他、Scikit-learnには様々な前処理が用意されている。詳しくは以下を参照ください。
scikit-learn.org

今回はScikit-learnのStandardScalerで前処理を行います。

実際にやってみた

標準化した後にモデルのフィッティングを行い、テストデータに適用します。
(実際には欠損値補完や特徴量の追加を合間にしています。)

コードはこちら

import pandas as pd
from sklearn.preprocessing import StandardScaler

df = pd.read_csv('./train.csv')
/*欠損値補完などの前処理*/

scaler = StandardScaler()
scaler.fit(df)
X = pd.DataFrame(scaler.transform(df), columns=df.columns)
/*モデルのフィッティング*/

フィッティング後、テストデータへ適用する際には以下のようにする。

df_test = pd.read_csv('./test.csv')
/*欠損値補完などの前処理*/
df_test = pd.DataFrame(scaler.transform(df_test), columns=df_test.columns)

この際、再度fitせずにトレーニング時にfitしたscalerを使用する。
(私はここで再度fitしてしまっていた。)

適切な標準化とテストデータにfitさせて標準化したデータを見比べてみよう。

f:id:brskun:20200507005647p:plain
テストデータにを基に標準化
限りなく平均0,標準偏差1であることがわかる。

f:id:brskun:20200507005613p:plain
適切な標準化
こちらは、おおよそ平均0,標準偏差1であることがわかる。
やはり、データのばらつきに違いが生じているようだ。

精度の変化

Ridge回帰、SVR、XGBoostで精度がどのように変化するかを確認する。

  • テストデータにfitさせてしまった場合
    • Ridge回帰 RmSLE:0.15353
    • SVR    RmSLE:0.14805
    • XGBoost  RmSLE:0.14433
  • 適切な標準化の場合
    • Ridge回帰 RmSLE:0.15325
    • SVR    RmSLE:0.14798
    • XGBoost  RmSLE:0.13801

全てのモデルにおいて、RmSLEが小さくなっており、精度が向上した。
特に、XGBoostでの精度の上がり方が目に付く。
言い換えると、適切に標準化を行わないと精度が下がってしまうことを意味する。

おわりに

今回は、精度向上のためにせっかくやった標準化も、やり方を間違えれば精度を落としてしまうことを比較してみました。
今度機会があれば、モデリング部分についても書いてみようかな。

ブログ開設

初めまして。

昨年の途中からAIを扱う部署に配属になり、
ちょこちょこ勉強はしているもののアウトプットできていなかったので、
勉強したことを気ままに書いていきたいと思います。

 

大学時代は統計学をかじっていましたが、
SIerに入社して3年半のうちにすっかり忘れちゃいました。
転職して今の部署に半年、勉強することは山ほどあります。
怠惰な自分に鞭打つため、がこのブログを作った理由です。

 

新型コロナウィルスで大変なご時世ですが、
勉強に時間を使える良い機会と思ってこのブログを充実させていきたいと思います。
皆さんもお気をつけて。