画像提供:マイナビニュース

写真拡大

○全体の流れを意識して機械学習に取り組む

前回は、教師あり学習/分類に関して、「決定木」「ロジスティック回帰」手法の違いを紐解いていきました。手法による違いがどういったものなのかが直感的にイメージできましたでしょうか。

これまでは、機械学習を直感的に理解してもらうために、簡易的なデータを準備し、機械学習の部分だけを取り扱ってきました。しかし、本来は、1)機械学習を使うための目的や解決すべき課題の設計、2)データ設計、3)データの収集・加工、4)データの把握を行い、ようやく5)機械学習を実行できます。今回は、これまでよりも複雑なデータを用意したので、全体の流れ(1〜5の工程)を意識しながら機械学習に取り組んでいきましょう。

○データの準備・概要把握

今回は、消費者物価指数から、消費者支出が増加するかを予測(分類)してみます。データは、これまでと同様に政府統計の総合窓口e-statのデータを使用します。

e-stat URL

目的変数として、月次の消費者支出を家計調査から取得し、先月よりも支出が増加した場合はフラグ1を、減少した場合はフラグ0を付与しました。説明変数としては、月次の消費者物価指数を2015年基準消費者物価指数から取得し、加工してあります。今回は冒頭にあまり細かくデータの説明を行いません。どんなデータなのかを把握していく作業も一緒にやっていきましょう。

まずは、こちらからデータをダウンロードし、Jupyter Notebookの作業フォルダにコピーしてください。Jupyter Notebookを立ち上げ、右上のNewから、Notebookを開き、タイトル名を変更しておきましょう。今回は、ConsumerExpendituresという名前にしました。

まずは、データを読み込んでみます。

import pandas as pd

data = pd.read_csv('ConsumerExpenditures.csv')

data.head()

まずは、先頭5行のみを表示します。これまでと違い、全データが画面に表示されておらず、「…」で省略されています。列数は、下の63columnsから63列存在することがわかります。これまでは10列程度でしたが、大幅に列数、すなわち説明変数の数が増えています。このままでは、データ数や、どんな列があるかを完全に把握できないので、まずは全体把握に努めましょう。下記、コードを実行してみてください。

data.count()

countを指定することで、列名とその列に入っているデータ数が表示されます。どのデータも387とあるように、今回のデータは387行存在することがわかります。これで、行数に関しても、これまでの50行程度から大きく増えていることが理解できたかと思います。また、今回のデータでは存在しないのですが、データが入力されておらずデータ欠損(null)が生じていることが多々あります。countを実行すると、データの欠損値は数に含まれないので、欠損があるかどうかの把握を簡易的に行えます。

さて、列名もcountによって大分見えてきましたが、せっかくなので列名だけ取り出してみましょう。

data.columns

これによって、列名がすべて取得できました。この列名を見ていくと、今回の目的変数である消費支出増加フラグと、それ以降の説明変数に大きく分かれることがわかります。説明変数を見ていくと、第3回で扱った消費者物価指数の項目と同じです。ただし、当月(Date)に対して1カ月前から6カ月前まで遡って消費者物価指数が用意されています。このようなデータを用意した理由は、冒頭に述べた1)機械学習の目的と関連しています。

何度か述べていますが、機械学習の本来の目的は、未知な事例を予測することにあります。今回の例でいうと、来月の消費者支出を予測することです。来月の予測を行う際に、来月のデータを使うことはできるのでしょうか。もうおわかりのように、来月のデータはまだ存在しないため、今月のデータのみで、来月の予測を行う必要があります。そのため、過去に遡って消費者物価指数を用意しているのです。物価が下がれば、お金を出しやすくなるというのはイメージしやすいかと思います。今回は取り扱いませんが、正確には、過去の消費者物価指数が支出に寄与しているのをグラフ化したりして確認するとなお良いと思います。何カ月前の物価が支出に寄与しているかわからないため、1〜6カ月前までのデータをそれぞれ抽出し機械学習できる形にデータを設計し(2.データ設計工程)、加工を行なっています(3.データの収集・加工工程)。

○データの準備

それでは、機械学習のために、データ準備をしていきましょう。先ほど調べてわかったかと思いますが、目的変数は消費支出増加フラグ、説明変数は食料から諸雑費、1カ月前から6カ月前までのデータとなります。

data_tmp = data.copy()

del data_tmp['Date']

del data_tmp['消費支出']

data_tmp_X = data_tmp.copy()

del data_tmp_X['消費支出増加フラグ']

data_tmp_Y = data_tmp['消費支出増加フラグ']

data_tmp_X.columns

まずは、読み込んだデータを残すために、datatmpを作成します。そこから、不要なデータを削除した後、説明変数datatmpX、目的変数datatmpYを作成します。最後に、datatmp_X.columnsで、列がしっかり用意できているか確認しています。次に、データを訓練データと、テストデータに分割します。

from sklearn.model_selection import train_test_split

X_train, X_test, Y_train, Y_test = train_test_split(data_tmp_X, data_tmp_Y,random_state=0, test_size=0.3)

print(len(X_train))

print(len(Y_train))

print(len(X_test))

print(len(Y_test))

分割には、traintestsplitを使用しています。サイズは、訓練70%、テスト30%に分割しました。しっかり分割できているか確かめるために、lenを用いてデータ数を単純に合算し、printで出力しています。Xtrain、Ytrainは270件、Xtest、Ytestは110件となっており、387件を指定通り分割出来ていることがわかりました。これで、機械学習のための準備は整いました。

○決定木

まずは、決定木による予測モデル作成をやっていきましょう。

from sklearn.tree import DecisionTreeClassifier

treeModel = DecisionTreeClassifier(max_depth=3, random_state=0)

treeModel.fit(X_train, Y_train)

from sklearn import metrics

print(metrics.accuracy_score(Y_train, treeModel.predict(X_train)))

print(metrics.accuracy_score(Y_test, treeModel.predict(X_test)))

1行目で、決定木のインポート、2行目で予測モデルの定義を行なっています。今回の木の深さは、とりあえず3でやってみました。その後、Fitで、予測モデルを作成し、さらに、精度を把握するために、accuracy_scoreで最も簡易的なモデル評価(精度)を行ないました。訓練データで78%、テストデータで60%となりました。訓練データでの精度が高いため、訓練データに過剰適合(過学習)傾向にありますが、訓練、テストデータどちらを用いても60%以上の精度は出ています。

過学習傾向なため、モデルの複雑さを減らしてみましょう。モデルをシンプルにする方法として、max_depthを小さくする方法があります。

treeModel = DecisionTreeClassifier(max_depth=2, random_state=0)

treeModel.fit(X_train, Y_train)

print(metrics.accuracy_score(Y_train, treeModel.predict(X_train)))

print(metrics.accuracy_score(Y_test, treeModel.predict(X_test)))

max_depthを2にすると、訓練データの精度は減少し、テストデータの精度は向上しました。これによって、精度は下がりましたが、モデルは汎化され、より良いモデルになったと考えられます。最後に、モデルに寄与している変数を抽出してみましょう。

Importance = pd.DataFrame({'変数名':data_tmp_X.columns, '重要度':treeModel.feature_importances_})

Importance[Importance['重要度'] != 0]

これだけ見ると、すべて衣類関係の物価が寄与しているという結果になりました。解釈は難しいところですが、衣類関連だけですべての消費支出を決めているとは考えにくいです。まだモデル精度が低いことも考慮する必要があると思います。しかし、このデータセットを使用する限り、これ以上決定木のモデル精度が向上させるのは困難なため、ロジスティック回帰に挑戦し、モデル精度を比較してみましょう。

○ロジスティック回帰

使用するデータは、決定木と全く同じで良いので、訓練データ、テストデータに分割されているものを使用します。

from sklearn.linear_model import LogisticRegression

logModel = LogisticRegression()

logModel.fit(X_train, Y_train)

print(metrics.accuracy_score(Y_test, logModel.predict(X_test)))

print(metrics.accuracy_score(Y_train, logModel.predict(X_train)))

決定木のときと同様に、1行目でロジスティック回帰のインポート、2行目で予測モデルの定義を行なっています。決定木と異なり、特にパラメータ指定はせず、デフォルトのものを使用しています。その後、Fitで予測モデルを作成し、さらに、簡易的な精度評価を行いました。こちらは驚くことに、訓練データで83%、テストデータでも79%を示しました。決定木よりも全体的な精度が高いだけでなく、訓練データとテストデータの精度に開きがないため、汎化されている良いモデルとなっています。

前回述べたように、手法によって分類の方法が異なるため、今回のケースのように大きくモデル精度が異なることがあります。いくつかの手法を用いて適切に評価を行い、最適なモデルを作成していくことの重要性を理解できたのではないでしょうか。

せっかくなので、ロジスティック回帰のモデルについて、もう少し触れていきましょう。前回、ロジスティック回帰は、決定木のように条件分岐によって線を引いていくのではなく、あくまでも多変数の式を元に分割線を引くものであるとお伝えしました。ロジスティック回帰は、線形回帰の関数Y=aX1+bX2+zを0から1の範囲に押し込めてしまう関数を使用しています。そのため、線形回帰と同様に、モデルを作成することは、線を引くことであり、線を引くことは係数(a、b、z)を決めることと同義です。

ロジスティック回帰には、決定木のようにfeature_importancesが存在しませんが、係数によってある程度変数の寄与がわかるようになっています。早速、出力してみましょう。

Coef = pd.DataFrame({'変数名':data_tmp_X.columns, '係数':logModel.coef_[0]})

Coef.sort_values('係数')

1行目で、今回作成したモデルから、係数を抽出しています。これらの係数は、Y=aX1+bX2+zのaやbに相当します。つまり、それぞれの変数に掛かっており、絶対値が大きければ大きいほど寄与は大きく、正であれば支出増加に、負であれば支出減少に寄与していることになります。絶対値が大きい部分には、1カ月前の物価指数は存在しておらず、1カ月前の物価指数の寄与は小さいことが考えられます。つまり、支出増減には少なくとも2カ月以上前の物価が大きく影響していると予想できます。ただし、住宅の物価指数が、正にも負にも入っており、景気の波等が関係している可能性があります。さらにより良いモデルにしていく場合、データの粒度や説明変数の見直しも必要になると思います。

さて、今回は、ここまで学んできたことを活かし、より実践に近い形で、データを扱い、機械学習によるモデル作成まで行いました。前回までの復習に加えて、データの扱い方のイメージや、機械学習のモデル構築プロセスの理解は進みましたか。ここからは、自分で悩みながら、解決したい課題を考えて、いろんなデータに挑戦していくことが最も重要だと思います。いろんな課題に取り組むことで、機械学習の精度だけにとらわれず、真に世の中に必要な機械学習モデルを作れるようになると思います。また、自分で課題を考えて、解決手段を考えることで、自然と機械学習手法のバリエーションも増えていくと思います。

次回は、これまでとは違ったデータを扱いながら、機械学習の未来にも触れていきたいと思います。

著者プロフィール

下山輝昌

大手電機メーカーにて、ハードウェアの研究開発に従事した後、独立。独立後はソフトウェア、データ分析等において実務経験を積むとともに、数社を共同創業。その中でも合同会社アイキュベータでは、人工知能・IoTなどの可能性や方向性を研究している。最近では、オープンデータに着目し、オープンデータ活用のためのwebサービスの立ち上げ、オープンデータ×IoTによる価値創出を1つのテーマに取り組んでいる。