Kaggleのエネルギー使用量予測コンペ(ASHRAEコンペ)に参加しました
はじめに
はじめまして、開発チームの白井です。
Kaggleのエネルギー使用量予測コンペ(ASHRAEコンペ)に参加し、ソロ銀メダル(70位 / 3614チーム)を獲得できました。今回はASHRAEコンペの概要や解法について紹介したいと思います。
コンペの概要
1448棟のビルの4種類のメーター(電力、冷水、スチーム、温水)の値を予測する回帰問題です。
コンペの背景としては、エネルギー使用量(上記の4種類のメーター値)が正確に予測できるようになることで、省エネ投資を活発化させて環境問題に貢献したいという狙いがあるようです。
ビルの所有者は、ビルのエネルギー効率を改善するような投資を行うことで、エネルギー効率が改善された分の見返りを得ることができます。
どれくらい見返りが得られるかは、ビルのエネルギー消費量の予測値と実測値の差から算出されます。現状は予測の精度が悪く、特定のビル・特定のメーター種別でしか予測値が提供されていない状態であるため、省エネ投資に消極的なビルのオーナーが多く、それを改善したいというモチベーションのようです。
回帰問題なので、評価指標はRMSLE(Root Means Squared Logarithmic Error:対数平均二乗誤差)です。
2016年から2018年の3年分のデータが用意されていて、2016年のデータがtrainデータセット、2017年および2018年のデータがtestデータセットとなっています。
trainデータセットが約2000万レコード、testデータセットが約4000万レコード用意されています。
データセットについて
3つのcsvファイルが提供されています。1つ目のcsv(train.csv)は、1時間毎のメーター値を保持しています。
building_id | meter | timestamp | meter_reading |
---|---|---|---|
648 | 0 | 2016-01-01 00:00:00 | 838.8 |
648 | 0 | 2016-01-01 01:00:00 | 837.5 |
648 | 0 | 2016-01-01 02:00:00 | 836.75 |
648 | 0 | 2016-01-01 03:00:00 | 838.25 |
648 | 0 | 2016-01-01 04:00:00 | 839.25 |
building_idはどのビルのデータなのかを表し、meterカラムはメーター種別(0:電力、1:冷水、2:スチーム、3:温水)を表します。meter_readingがメーター使用量であり、今回のコンペで予測する値となります。
2つ目のcsv(building_metadata.csv)はビルのマスタデータです。サイト(site_id)はビルの物理的な位置を表します。他にもビルの用途(primary_use)などの情報を保持しています。
site_id | building_id | primary_use | square_feet | year_built | floor_count |
---|---|---|---|---|---|
0 | 0 | Education | 7432 | 2008 | nan |
0 | 1 | Education | 2720 | 2004 | nan |
0 | 2 | Education | 5376 | 1991 | nan |
0 | 3 | Education | 23685 | 2002 | nan |
0 | 4 | Education | 116607 | 1975 | nan |
3つ目のcsv(weather_train.csv)は気象情報です。各サイトの1時間ごとの気象情報を保持しています。
site_id | timestamp | air_temperature | cloud_coverage | dew_temperature | precip_depth_1_hr | sea_level_pressure | wind_direction | wind_speed |
---|---|---|---|---|---|---|---|---|
0 | 2016-01-01 00:00:00 | 25 | 6 | 20 | nan | 1019.7 | 0 | 0 |
0 | 2016-01-01 01:00:00 | 24.4 | nan | 21.1 | -1 | 1020.2 | 70 | 1.5 |
0 | 2016-01-01 02:00:00 | 22.8 | 2 | 21.1 | 0 | 1020.2 | 0 | 0 |
0 | 2016-01-01 03:00:00 | 21.1 | 2 | 20.6 | 0 | 1020.1 | 0 | 0 |
0 | 2016-01-01 04:00:00 | 20 | 2 | 20 | -1 | 1020 | 250 | 2.6 |
まずはtrain.csvとbuilding_metadata.csvをbuilding_idで結合し、さらにsite_idとtimestampを使ってweather_train.csvと結合します。
building_id | meter | timestamp | meter_reading | site_id | primary_use | square_feet | year_built | floor_count | air_temperature | cloud_coverage | dew_temperature | precip_depth_1_hr | sea_level_pressure | wind_direction | wind_speed |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 2016-01-01 00:00:00 | 0 | 0 | Education | 7432 | 2008 | nan | 25 | 6 | 20 | nan | 1019.7 | 0 | 0 |
1 | 0 | 2016-01-01 00:00:00 | 0 | 0 | Education | 2720 | 2004 | nan | 25 | 6 | 20 | nan | 1019.7 | 0 | 0 |
2 | 0 | 2016-01-01 00:00:00 | 0 | 0 | Education | 5376 | 1991 | nan | 25 | 6 | 20 | nan | 1019.7 | 0 | 0 |
3 | 0 | 2016-01-01 00:00:00 | 0 | 0 | Education | 23685 | 2002 | nan | 25 | 6 | 20 | nan | 1019.7 | 0 | 0 |
4 | 0 | 2016-01-01 00:00:00 | 0 | 0 | Education | 116607 | 1975 | nan | 25 | 6 | 20 | nan | 1019.7 | 0 | 0 |
このデータフレームを使って、meter_reading変数を予測するモデルを開発します。すなわち、ビル情報(用途など)および気象情報(気温、気圧など)をもとに、エネルギー消費量(meter_reading)を予測するモデルを開発します。
ビルやメーター種別ごとに目的変数であるmeter_readingの振る舞いが大きく異なります。いくつか例を見てみましょう。
こちらはbuilding_id=648, meter=0のmeter_readingをプロットした図になります。パターンがあり、ある程度の精度で予測できそうに見えます。
こちらはbuilding_id=1021, meter=3です。Y軸を見ると非常に変動が大きく、精度よく予測するのは難しそうに見えます。また5月から7月にかけて、meter_readingが0になっている区間が存在します。
こちらはbuilding_id=758, meter=2です。ほとんどのmeter_readingが0になっていることが分かります。後述の外れ値処理でも説明しますが、このmeter_readingが0になっているレコードをどう処理するかが今回のコンペのポイントの1つだったと思います。
私の解法(有効だった取り組み)
目的変数の変換
meter_readingを直接予測するのではなく、meter_readingに1を加算して自然対数を取ったもの(meter_readingは0以上なので、自然対数計算時にエラーにならないようにするため1を加算する)を予測するようにしました。評価指標にもログが含まれていますし、カーネルや上位解法を参照したところ、ほぼ全参加者が採用していた手法でした。
外れ値の処理
今回のコンペで一番重要だったのは外れ値処理、特にmeter_reading=0のレコードの処理だったと思います。コンペ序盤にmeter_reading=0のレコードを全て除去して学習し、予測結果をサブミットしてみたところ、publicスコアは芳しくありませんでした。つまり、除去するべき0と除去してはいけない0がある、ということです。様々な試行錯誤を行った結果、以下のルールで外れ値を処理することにしました。
- meter=0(電力)
meter_reading=0のレコードはメーターの故障であると考えられ、学習のノイズになるため除去することにしました。(学校やオフィスが休暇期間であろうと、ビルの消費電力が0になることはないでしょう) - meter=1(冷水)
meter_reading=0の区間が冬にある場合、学習に使う。(冬に冷水のメーターをOFFにしている可能性があるため)
そうでない場合、ノイズとして除去する。 - meter=2, 3(スチーム、温水)
meter_reading=0の区間が夏にある場合、学習に使う。(夏にスチームや温水のメーターをOFFにしている可能性があるため)
そうでない場合、ノイズとして除去する。 - パーセンタイル値に基づく外れ値処理
上記のルール適用後、1パーセンタイル点と99パーセンタイル点をビル×メーター種別毎に計算し、1パーセンタイル未満、99パーセンタイルより大きいレコードを除去。
除去対象のレコードが多い場合は学習に使えるデータが減ってしまうため、いくつかのビルに対しては後処理をしない、などの工夫も実施しました。
前半部分のアイデアは以下のカーネルを参考にしました。
https://www.kaggle.com/purist1024/ashrae-simple-data-cleanup-lb-1-08-no-leaks
上記カーネルをベースに、夏冬の期間の微調整などを実施しました。
その他の前処理
https://www.kaggle.com/nz0722/aligned-timestamp-lgbm-by-meter-type
で指摘されているように、weather_train.csvのtimestampを各サイトのローカルタイムに補正しました。線形補間を使ってwether_train.csvの気温カラムなどの欠損値処理を行いました。
特徴量エンジニアリング
ラグ系の特徴量(1週間前の気温との差など)を追加しました。
日付に関する特徴量として休日フラグや祝日フラグを追加しました。
気象に関する特徴量として相対湿度や体感温度を追加しました。
バリデーション戦略
「Kaggleで勝つデータ分析の技術」の5.3節に記載されているようなシンプルなクロスバリデーションではローカルCVとpublicスコアが相関しませんでした。
非常にアドホックなやり方ですが、クロスバリデーションについては以下のカーネルのバリデーション戦略を利用することにしました。 https://www.kaggle.com/purist1024/strategy-evaluation-what-helps-and-by-how-much
4月から11月のデータでtrainして1月から2月のデータでvalidation
6月から12月と1月のデータでtrainして3月から4月のデータでvalidation
・・・(これがあと4ペア)
学習の粒度
学習の粒度として、以下のように複数のパターンが考えられます。
- trainデータセット全体に対してモデルを作る(1個のモデル)
- meter毎にモデルを作る(合計4個のモデル)
- site_id毎にモデルを作る(合計16個のモデル)
- building_id毎にモデルを作る(合計1448個のモデル)
- meter×site_id毎にモデルを作る(合計4×16=64個のモデル)
etc
いくつかのパターンを試しましたが、一番ローカルCVが良かった「trainデータセット全体でモデルを学習させる」パターンを採用しました。
※上位陣は学習の粒度を変えたモデルをアンサンブルに組み込むことで精度を上げており、なるほどと思いました。
アルゴリズム
LightGBMを使って学習を行いました。building_idという多水準かつ極めて重要な特徴量をうまくエンコーディングできず、ニューラルネットワークやSVMなどの非ツリーモデルはうまく活用できませんでした。Optunaを使ってLightGBMのハイパーパラメータチューニングを行い、結果のよかった上位5つでアンサンブルを行いました。
試したけれどダメだった取り組み
トレンド抽出
statsmodelsライブラリを使ってトレンドを抽出し、トレンドを減算して学習を行う。そうして得られた学習済みモデルの予測結果にトレンドを加算して最終的な予測値とするやり方を試しました。
ローカルCVは改善しましたがpublicスコアは悪化しました。train全体に対してトレンドを計算しているため、オーバーフィットしているものと考えられますが、うまい対策も思いつかず、不採用としました。
【参考記事】
ターゲットエンコーディング
以下のようなパターンでターゲットエンコーディングを試しましたが、ローカルCVが改善せず、不採用としました。
- building_id×meter毎にmeter_readingの平均や中央値、分散を計算
- primary_use×meter毎にmeter_readingの平均や中央値、分散を計算
etc
ニューラルネットワークなどの非ツリーモデル
ターゲットエンコーディングがうまくいかなかったので、今回はニューラルネットワークなどの非ツリーモデルをうまく活用できませんでした。上位陣のアンサンブルにはニューラルネットワークがほぼ確実に含まれていたので、Entity Embeddingを使うなりして、building_idやmeterといったカテゴリカル変数の特徴量エンコーディングを試してみるべきでした。
上位解法の紹介
1位の解法
https://www.kaggle.com/c/ashrae-energy-prediction/discussion/124709
以下のような手法が参考になりました。
- CatBoostやニューラルネットワークもアンサンブルに追加
- 学習の粒度を変えたモデルをアンサンブルに追加
- 気温の2次微分などを特徴量に追加
- ベイジアンターゲットエンコーディング
2位の解法
https://www.kaggle.com/c/ashrae-energy-prediction/discussion/123481
ロバストなバリデーション戦略を立てるのが難しいコンペだったため、特徴量エンジニアリングは程々にして、アンサンブルに力を注いだそうです。
9位の解法
https://www.kaggle.com/c/ashrae-energy-prediction/discussion/123525
シンプルな解法で参考になります。
欠損値を予測するためのモデル(LightGBM)を作成し、欠損値処理を行うとスコアが良くなったそうです。
まとめ
- ビル情報(用途など)および気象情報(気温、気圧など)をもとに、エネルギー消費量(電力、冷水、スチーム、温水)を予測するコンペに参加しました。
- 時系列のテーブルデータに対する典型的なアプローチを組み合わせたシンプルなソリューションで銀メダルを取得することができました。
- ビルやメーター種別毎にそれぞれ個性があり、外れ値処理などを行うためにmeter_readingの時系列プロット図をひとつひとつ観察しました(合計2380個)。EDAの大切さ、大変さを痛感しました。
- ローカルCVとpublicスコアが相関するようなバリデーション戦略がなかなか見つからず、苦労しました。(最終的に採用した手法もある程度は相関していたが、完璧には程遠いものでした)
- 今回のコンペで得られた知見を、電力予測はもちろん、様々な業界に多数存在する時系列データの分析に活かしていきたいと思います。
この記事の投稿者
白井 翔太(しらい しょうた)
データエンジニアです。データ分析基盤の設計・構築・運用に携わっています。
組織としてデータドリブンな意思決定を行っていくためには、データ分析基盤の構築が必要不可欠です。
本ブログではデータ分析基盤に関する知見を中心に紹介していきたいと思っています。
関連記事
-
2020/06/09