2021/09/17:(試行)5回目の結果とデシジョンツリー分析を追加
###~データを放り込むだけで、データ前加工 ⇒ 学習 ⇒ モデル選択 ⇒ モデル構築 ⇒ 予測 までほぼ自動って・・・どうなってんの?!~
##はじめに
機械学習自動化ライブラリ「PyCaret」は、未来にワープしたかのような衝撃があります。
ご存じない方のために、
できるだけ専門用語はさけ、私なりに魅力をお伝えしつつ、あるデータセットでPyCaretを適用、奮闘した記録を報告させていただきたいと思います。
##PyCaretってなに?
PyCaretはPythonライブラリのひとつです。
ホームページでは以下のように謳われています。
”PyCaret is an open source, low-code machine learning library in Python that allows you to go from preparing your data to deploying your model within minutes in your choice of notebook environment.PyCaret is an open source, low-code machine learning library”
ざっくり訳すと、
Pycaretは、Pythonで使えるオープンソースの機械学習ライブラリ、わずかなコードでデータ準備からモデル展開までが数分で! という感じです。
また、『無料のAutoMLライブラリ』といったタイトルもよく目にします。
「AutoML」は、日本語で「機械学習の自動化」のことです。
ここまでの説明で、
”機械学習”なんて、私には関係ないから。。。
とスルーしてしまうと、将来とても後悔することになると思います。
「AutoML」の出現により、データ分析のありかたは、今後変わるかもしれないからです。
これまでは、
手法を知っている人が武器(ツール・アプリ)を持っていないと実施できなかったデータ分析が、AutoMLにより、知らない人でも簡単に実行できます。
例えば、
- データが「男/女」「晴/曇/雨」等と区分されている場合、「0/1」等、データ分析できる変数に勝手に変えてくれるのです。
- データが偏った分布であれば、正規分布に近づけるような処理も勝手にしてくれるのです。
- 「どの手法を使うか?」について迷う必要はないのです。「この場合に使える手法はこれだけあります。今回はこれが一番いいですよ。」と教えてくるのです。
- 予測のためのモデルも、手元データだけに過剰にフィットしたものにならないよう検証を行った上で、ギリギリまで追い込んでもくれるです。
これは革命といってよいと思います。
##一方、データを読む力は "ますます" 大事にもなる
先に紹介したAutoMLでできることをみれば、ほぼ手放しに近いように思えますが、どの様なデータを与えるかは、(これまで通り)よくよく考えないといけません。
これは、人に対しても機械に対しても同じだと思います。
「これはどんなデータ?」
と問われたとき、関係する変数をいっぱい挙げるだけでは、聞き手の方は「聞けば聞くほどわからない」ということになります。
挙げられた変数は意味のある変数もあるだろうけど、意味のない変数もあるかもしれない。。。
要は、結果に関係するであろう変数の特徴が知りたい。ただ、これは言うほど簡単ではありません。
例えば、「コンビニのアイスクリームの売行きを予測したい」という時、どのような特徴量が必要となるでしょうか?
「夏場に気温が上がると駅近くのコンビニに足が向く」でしょうから、季節、気温等もデータに取り込んだ方がよいでしょうし、気温と湿度から「猛暑日か否か?」とか、店舗の位置(住所)から「駅近くか否か?」等、手元にある変数同士をつなぎ合わせて新たな特徴を設定したり、結果に影響するであろう変数を新たに設けたり、より特徴ある変数に変身させたり・・・これらを『特徴量エンジニアリング』と呼ぶそうです。
これは容易くはないですが、AutoMLは とにかく簡単ではやいので、何度も試行でき、試行を繰り返す中で得た知見を『特徴量エンジニアリング』に反映し、また試行するということが現実的にできます。
このような試行錯誤は『特徴量エンジニアリング』のスキル向上にもつながりますので、AutoMLの活用は、まちがいなく生産性向上に直結するということになります。
##PyCaretでデータセット「Bike Sharing Demand」に挑んでみる
###実行条件など
・Google colabで実行
・**Kaggleの「Bike Sharing Demand」**で実行
###データセット「Bike Sharing Demand」について
PyCaret適用に挑んだ データセット「Bike Sharing Demand」は、
会員登録、レンタル、返却のプロセスを自動化した自転車シェアリングシステムのデータで、ワシントンD.C.における自転車レンタルの需要を過去の利用パターンと気象データを組み合わせて予測するというコンペで使われたものです。
**データセット「Bike Sharing Demand」の項目と内容**
|項目|内容|
|:-----------|:------------------|
|datetime|毎時の日付 + タイムスタンプ|
|season|1 = 春、2 = 夏、3 = 秋、4 = 冬|
|holiday|その日が休日であるかどうか|
|workingday|その日が週末でも休日でもないかどうか|
|weather|1: 晴れ/少ない雲/部分的に曇/一部曇り、2: 霧+曇り/霧+崩れた雲/霧+少ない雲/霧、3:小雪/小雨+雷雨+散らばる雲/小雨+散らばる雲、4:大雨+氷片+雷雨+霧/雪+霧|
|temp|摂氏温度|
|atemp|摂氏で表した「体感温度」|
|humidity|相対湿度|
|windspeed|風速|
|casual|未登録ユーザーによるレンタル数|
|registered|登録ユーザーによるレンタル数|
|count|総レンタル数|
##いきなりミス!
最初にデータをプロファイリング(方法は以下参照)にかけ、ざっと眺めました。
最初の印象は、
「欠損しているデータはないし、カテゴリーデータ、例えば「season」という項目でいえば、1 = 春、2 = 夏、3 = 秋、4 = 冬といった形にラベルエンコーディングされてもいて、前処理的には行き届いている感じだなぁ。」
そこで、早速、データセットをPyCaretに投入しました。
「count(総レンタル数)」を目的変数(Target)として設定したくらいで、あとはほぼPyCaretお任せでした。
予測までの過程は超順調!
しかし、いよいよ完了という手前、テストデータを受け付けてくれず、エラーで止まってしまいました。
トレイン(学習)データには、「casual(未登録ユーザーのレンタル数)」「registered(登録ユーザーのレンタル数)」という項目があります。
以下のように、総レンタル数は未登録ユーザーと登録ユーザーの和となっています。
- count(総レンタル数)⁼ casual + registered
いずれのレンタル数も結果ですから、トレイン(学習)データにある「casual」「registered」は、テストデータにはある訳がありません。
いきなり・・・ですが、選択項目を間違ったわけです。
早速、以下のように ignore_features で学習に取り上げない項目を指定しました。
from pycaret.regression import setup
clf = setup(data=train_data, target="count", session_id=123,
ignore_features = ['datetime','casual','registered'])
##初回
設定見直しで無事にモデル評価まで完了、早速kaggleでSubmitしてみました。
評価は、RMSLE(対数平均二乗誤差)で行われ、(実は正確に覚えていないのだが)Scoreは1.4あたりでした。
このデータセットの(Submitの)「エントリー数」と「スコア(RMSLE)」の関係をグラフ化してみました。
ほぼ完全にAutoML任せで実施しただけと考えると、悪くはないと思います。
※それにしても100回以上エントリーしている方がいることに驚きました。
##2回目
このデータセットの特徴には、temp|摂氏温度|、atemp|体感温度|、humidity|相対湿度|があります。
「よーし、自転車を借りるぞ」という時、それぞれ影響はあるだろうが、これら合わせ技で感じる快適さ、不快さの方が影響を与えるのではないか?と思い、不快指数を特徴量として追加することにしました。
不快指数は、このサイトの計算式を使わせていただきました。
ただ、これはPython初心者の私にとっては大変・・・
なんとか、計算結果をデータフレームに反映できました。
train_data = train_data.assign(fukai=train_data['temp']*0.81 + train_data['humidity']*0.01*(train_data['temp']*0.99-14.3)+46.3)
これにてモデル評価まで完了、早速kaggleでSubmitしてみました。
Scoreは1.362658と、すこしだけですが改善しました。
##3回目
つぎに時系列でレンタル数の傾向変化を見てみました。
レンタル数は、冬場は少なく、春以降に増加という季節変動がありそうです。また2011年よりも2012年の方がレンタル数が全体的に増加していることもわかります。
データには「datetime」という項目があります。
datetimeは、”2011-01-01 04:00:00” のような形式のデータです。この場合「2011年1月1日 4:00」です。
このdatetimeから、年, 月, 時間(hour)・・・を取りだし、特徴量に加えてみることにしました。
じつはこの作業、
自らのスキルに屈し、Excelで行ったのですが、Excelで例えばこのデータセットの「train.csv」を開くと、datetimeの表示が化けてしまいました。
csv:”2011-01-01 04:00:00” ⇒ excel:”2011/1/1 4:00:00”
これでは、submitできなくなりますのでPythonで対応する方法を調べることにしました。
Pythonに「datetime」という日付や時刻に対してさまざまな操作をすることができるモジュールがあることがわかりましたので、以下のように実行しました。
※以下は、trainデータで実行したものです。testデータでも同じことをしています。
import datetime
from datetime import timedelta
train_data["datetime"] = pd.to_datetime(train_data["datetime"])
train_data["year"] = train_data["datetime"].dt.year
train_data["month"] = train_data["datetime"].dt.month
train_data["day"] = train_data["datetime"].dt.day
train_data["dayofweek"] = train_data["datetime"].dt.dayofweek
train_data["hour"] = train_data["datetime"].dt.hour
train_data.head()
これにてPyCaretを実行し、前処理の結果を見たところ、hourが数値として認識されていました。
数値なのですが、1~12までのカテゴリーデータなので、以下のように設定しました。
from pycaret.regression import setup
clf = setup(data=train_data, target='count', session_id=123,
ignore_features = ['datetime','casual','registered'],
categorical_features= ['hour'])
PyCaretは、ほぼお任せで実行できますが、このようなチューニングが必要な場合もありますね。
これで、モデル評価まで完了、早速kaggleでSubmitしてみました。
しかし、
残念ながら、Scoreは1.39068と、すこし悪化しました。
う~ん、時期,時間に対する影響は少なからずあると思うのですが・・・
もっとカテゴリーに濃淡をつけた方がよいのかもしれないのかなぁ。
※詳しい方がおられたら、ぜひ助言をお願いいたします。
##4回目
3回目は、よい結果が得られませんでした。
時系列カテゴリーの特徴がより明確になるような処置を検討するのが筋なのかもしれませんが、3回目の結果は一度忘れ(^^;)、別のアプローチを行うことにしました。
先に述べましたが、count(総レンタル数)は以下の関係にあります。
- count(総レンタル数)⁼ casual + registered
3回目までは、count(総レンタル数)を目的変数(target)とした丼ぶり🍚勘定的アプローチでしたが、本来はcasual(非登録ユーザーのレンタル数)と registered(登録ユーザーのレンタル数)をそれぞれ予測し、この和をcount(総レンタル数)の予測とした方がよいと考えました。
これは、setup()で以下のように設定するだけなので何とかなりました。
- casualを予測するときは、casualをtargetに、registeredとcountはignoreに
- registerdを予測するときは、registeredをtargetに、casualとcountはignoreに
もっとよい方法があるのかもしれませんが、
casual予測結果は「submission_bike1.csv」に、registerd予測結果は「submission_bike2.csv」に出力しましたので、
① まずそれぞれをdf1,df2というデータフレームに格納
② df2に「count」列を設け、そこに df1['casual'] と df2['registered'] の和を格納
③ df2のregisteredの列はいらないので削除
としました。以下がこれを実行したコードです。
df1=pd.read_csv('submission_bike1.csv')
df2=pd.read_csv('submission_bike2.csv')
df2['count'] = df1['casual'] + df2['registered']
df2.drop("registered", axis = 1, inplace = True)
# 合計したsubmitファイル(csv)を保存
df2.to_csv("submission‗final.csv", index=False)
無事にこれにてモデル評価まで完了、早速kaggleでSubmitしてみました。
Scoreは1.30025 に改善することができました。
##5回目
時系列変数を取り入れた3回目は、よい結果が得られませんでしたので、リベンジすることにしました。
データ傾向から言えるのは
- 2011よりも2012年の方がレンタル数が上がっている。
- 冬場はレンタル数が他の季節よりも少ない。
- registered(登録ユーザー)は、朝(AM7-9),夕(17-19PM)のレンタル数が多い。
- いわゆるワーキングタイム以外(AM0-6,20-24PM)はレンタル数が少ない。
あと、
- データにはhoriday(祝日)という特徴量はあるが、weekend(週末)という特徴量はない。週末はレンタルされる方が増えるのではないか?
季節変化が見られたので、3回目では単純にmonth(月)を取り入れたが、すでにseason(季節)という特徴量がありますので、month(月)は取り上げず、year(年)を取り上げることにしました。
hour(時間)は、時間帯でレンタル数が多い・普通・少ないの3つに区分することにしました。※特徴量名はhot_hourとしました。
あと、土曜日,日曜日をweekend(週末)として設定することにしました。
なお、今回もcasual(非登録ユーザーのレンタル数)と registered(登録ユーザーのレンタル数)をそれぞれ予測し、この和をcount(総レンタル数)の予測としました。
#import datetime
from datetime import timedelta
train_data["datetime"] = pd.to_datetime(train_data["datetime"])
train_data["year"] = train_data["datetime"].dt.year
train_data["dayofweek"] = train_data["datetime"].dt.dayofweek
train_data["hour"] = train_data["datetime"].dt.hour
train_data['weekend'] = 0
train_data.loc[train_data["dayofweek"] > 5, 'weekend'] = 1
train_data.head()
train_data['hot_hour'] = 2
train_data.loc[train_data["hour"] == 7, 'hot_hour'] = 1
train_data.loc[train_data["hour"] == 8, 'hot_hour'] = 1
train_data.loc[train_data["hour"] == 9, 'hot_hour'] = 1
train_data.loc[train_data["hour"] == 17, 'hot_hour'] = 1
train_data.loc[train_data["hour"] == 18, 'hot_hour'] = 1
train_data.loc[train_data["hour"] == 19, 'hot_hour'] = 1
train_data.loc[train_data["hour"] < 7, 'hot_hour'] = 3
train_data.loc[train_data["hour"] > 19, 'hot_hour'] = 3
train_data.head()
※細かく区分したとはいえ・・・もっとよい方法がある気がします。どなたか教えてください。
特徴量重要度にyear_2012とhot_hour_3が出てきました。
trainとtestの残差に大きな差はありませんので、過学習は心配しなくてよさそうです。
無事にこれにてモデル評価まで完了、kaggleでSubmitしてみました。
Scoreは0.99989 に改善することができました。
##デシジョンツリー(決定木:dtreeviz)でも見てみる
デシジョンツリーでも見てみました。
「もう、最初からみときゃよかった」と思いました。
以下は、registered(登録ユーザー)を目的変数としたデシジョンツリーです。
まず、hot‗hourで分岐しています。1:出社・退社時間,2:日中,3:夜中~早朝 です。
夜中~早朝除いては、レンタル数が多いのは、冬(⁼season'1')を除いたシーズン、出社・退社の時間帯が多いことがわかります。
つぎに以下はcasual(非登録ユーザー)を目的としたデシジョンツリーです。
まず、atemp(体感温度)が高いか低いかで分岐しています。
atempが低い場合は、夜中~早朝を除き、レンタル利用者が多いことがわかります。
また、**なんと!レンタル数はfukai(不快指数)は高いほど多くなっています。**自転車に乗ることによって不快さを解消したいのかもしれません。
一方、気温による差はあまりなさそうですが、体感温度が高い場合は、レンタル数が下がっています。休日の日中に利用されることが多く、平日の利用者は休日の約半分となっています。
デシジョンツリーはいいですね。
registered(登録ユーザー)とcasual(非登録ユーザー)では利用シーンがぜんぜん違うということがよくわかりますし、これならばregistered(登録ユーザー)とcasual(非登録ユーザー)は別々に予測すべきと直感できます。
参考:デシジョンツリーに関してはこちらも見てください
それにしても、dtreevizで描けるデシジョンツリーは美しいなぁ。
##最後に
前処理で必要となる処理がもっとできるようにならないといけないな、特徴量エンジニアリングのスキルもより向上させないといけないなということはあるが、とにかく試行を何度も繰り返せることがPycaretの最大の魅力であると思いました。
私がPythonをもっと扱うことができればもっとスムーズだったともいますが、それでも半日程度で4回の試行ができました。
Pycaretがなければ、こんなにサクサクは試行できなかったですし、試行を重ねるにつれ、データに対する理解も深まってきました。
この記事は、精度をあげることが第一のように見えたかもしれませんが、ビジネスでは汎化性もとても大切と思います。
実験だけがうまくいき、実ビジネスでは使えないということでは意味がありませんし、精度が高いか低いかはその精度で活かせるかどうかとなります。
データ分析では「どのような特徴が結果に影響しているかを把握する」ということも、とても重要と思います。
結果に何が影響しているかがわかれば、その特徴を管理したり、高めたりすればよくなります。
AutoMLは、結果に対する影響も可視化してくれますので、実ビジネスではむしろこれが何よりも欲しかったという内容なのかもしれません。
PycaretのようなAotoMLの活用によりデータ分析の生産性は大きく変わります。
これは「ややこしそうだな・・・」というだけでスルーしたらダメだと思います。
私はプログラマーでもなく、これまで言語をまともに扱ったこともありませんので、Pythonのスキルはマダマダですが、何がしたいかが明確ならば見よう見まねで何とかできることも多いので、できることから始めて、このAutoMLすばらしさをひとりでも多くの方に味わっていただきたいとことで、この記事を結びたいと思います。
###参考サイト
###参考書籍