PBL01のデータ分析アプローチ
inatani soichiroにより
1. クロスバリデーション
1.1. CVはstratified K fold Foldは5でlearning rate 0.05 各seed42なのでそのまま作れば再現できると思います
1.2. とにかく最後まで手元のvalidスコアとLBスコアが上下の動き含め一致せずに苦労した(ここらへんは上位者から勉強したいところ) validデータは2019/10のみとか170品目のみとかでやったがKFold(Shuffle)より良くならず TimeSeriesSplitは試していない(データ期間の短さと今回の予測が特殊な12月といったところでムダだと判断) 2018→2019の比率、2018/10→2018/12の比率を利用して擬似的にvalidデータ作ってみたが精度が悪すぎて断念 まともなvalidデータが作れないからハイパラチューニングもできない earlystopもほぼ効かず、最後のroundまで学習 validデータ意味ないじゃんと思って全てをtrainに回したがスコア上がらず
1.3. Python: 層化抽出法を使ったK-分割交差検証 (Stratified K-Fold CV) - CUBE SUGAR CONTAINER
2. 特徴量削減のテクニック
2.1. 最終的に5081個になったものを、LightGBMで「特徴量重要度」と「SHAP値」で抽出して、 値のとりうるバリエーションで表にしたものがこちらです。
2.2. Feature Importanceって結局何なの?|Yotaro Katayama|note Feature Importanceって結局何なの?|Yotaro Katayama|note
2.3. SHAPを用いて機械学習モデルを説明する l DataRobot
2.4. TEST RSMEが良すぎる場合、過学習しているかもしれません。 私はLightGBMを使用していますが、売り上げ列(販売個数×販売価格)を追加した時に、TEST RSMEが良すぎて過学習を起こしていました。 試しに売り上げ列を削除して計算した時にスコアが少し上がりました! みなさんも特徴量を色々増やしてみたけど、スコアが上がらない方で、1日の投稿数に余裕がある方は、必要なさそうな特徴量を抜いて提出してみるのもやってみてはいかがでしょうか??
2.5. もしLightGBMやXGBoostをお使いであれば、出来上がった木構造をもとに特徴量の評価を出すことができます。 ご参考: LightGBMの出力結果を解析したい!(SHAPのススメ) - Qiita https://knknkn.hatenablog.com/entry/2019/05/07/215124 自分の取り組み方は、ざっくりですがこんな感じです。 特徴量に関する評価を下記三つで考えます。 特徴量重要度:決定木に出てくる回数 SHAP:「正解予測にどれだけ貢献しているか」度 Permutation Importance:(定訳なし。順列の重要度?)「その特徴量をランダムに並び替えた場合にどれだけ性能劣化するか」度。失って初めてわかるありがたみ、みたいな話? この三つをもとに、「こいつはよく貢献しているから多分必要なんだろうか?(SHAP)」とか、「こいつに頼りすぎてるから他のパラメータの解釈が育たないんじゃないか?(特徴量重要度)」とか、「こいつめっちゃ登場する割にほぼ貢献してないから、要はおじゃま虫なんじゃないか?(特徴量重要度 x SHAP)」とか、あれこれ仮説を立てて実験してます。 ただ、結局実験しないとなんともいえないです。 あと、自分は「特徴量を大量に用意してから取捨選択をする」発想で上記のようにしましたが、「ベースラインに対して特徴量を一つずつ付けて外して吟味する」方法を取った人の方がPBL01では上位にいたと思います。
3. 前処理
3.1. 前処理:商品価格、売上個数の0、マイナスのデータ削除
4. 感想
4.1. スコアアップにもっとも寄与したのが、サンプルコードで捨てられていた2018年12月のデータ(ダントツでこれでした)と、後処理の定数倍であることを考慮すると、やはり12月は他の月よりも特殊要因が多く、他の月のデータを参照しすぎると結果が平均化されすぎてスコアが伸びない傾向にあると思います。 当たり前のことなんですが、12月の予想をしたいなら前年度以前の12月のデータから予想しようねって結果となったかなというのが感想です。
4.2. スコア2台まではいったが、過学習感が強すぎて大きくshake downしてもおかしくない
4.3. 【反省点】 「12月は無茶苦茶売れる!」を、特徴量エンジニアリングとAIモデルで予測できていないこと。 明らかに過学習しているのはわかっているが、そこから脱出できなかったこと。
5. ノウハウ
5.1. スコア伸ばしていく優先順位は ①クロスバリデーション ②特徴量エンジニアリング ③後処理 ④パラメータチューニング
5.2. ①データの分析力・特徴量の生成力 今回一番痛感したのはこれでした。 LightGBMのハイパーパラメータはほとんどいじらずとも高いスコアを出している方が多くいらっしゃったので、学習データをいかに整えるかが勝負だったのかと感じました。
5.3. 特徴量を増やす際にも、当てずっぽうというか根拠がなく効かない特徴量を量産してしまった気がします。(feature_importanceでも特定のカラムにグッと数値が寄ってしまって、バランスが非常に悪かったように思う) 本当はいらないデータがあるのに省けてないとかも多そう・・・。
5.4. できるようになったこと】 Target_Encording feature_importance Pandas_ProfileReport ラグ特徴量の生成 mergeやgroupbyなどの基本操作
6. EDA
6.1. LBスコアアップへの最終的な決め手はxgbとcatでしたが、自分の思考や方針を支えてくれたのが皆さんとの情報共有とPowerBIでのEDA+確認作業でした。評価指標がRMSEなので、売上個数が大きいところを上手く当てられるか勝負ってとこでしたね。
6.2. 一部の商品の影響(特に商品Code 2900075が19年10月比からどれだけ増えるか)がスコアボード数値に効くようだったので、特徴量などを加えたり、抜いたり。
7. 予測後の処理
7.1. AIモデルでの予測は「12月は無茶苦茶売れる!」というのが反映されておらず、対象商品の2018年12月売上個数と予測個数を比較して、 AIであるかは微妙なルールベースの予測(四則演算)を追加
7.2. ただの定数倍でスコアが良くなることもある
7.3. 後処理で定数倍を掛けられていたので、私も試しましたが私の場合は、定数倍(1.2, 1.4)を掛けない方がスコアが良かったです。 その他_7とその他_13だけで作成したモデルの予測値を後処理として、手動で補正を掛けたりしましたが、こちらもスコア向上しませんでした。
7.4. 後処理として、昨年の12月売上個数が1以上のものを+αした(12月に急激に売り上げが上がる分を考慮できてないと判断したので) 6.後処理として2,3,6,9,12か月前の売上個数が0のものは予測値も0に修正
7.5. 後処理で定数倍
7.6. 後処理:結果データのマイナス値を0へ変換し、定数倍 定数倍する理由としては、salesデータ全体で見ると売上個数は減少傾向だが、対象商品群で見ると増加傾向であるため。 倍数は、2018年12月と他の月の売上個数平均の比率に、対象商品群の2019年の変化率をかけた数字(最大、最小など色々試しましたが、平均が一番スコアがよかった)
8. グラフの日本語化
8.1. ・たったの3行でグラフへの文字入力を日本語に対応させられること(@y.oさんのメッセージより。投稿感謝いたします。) from matplotlib import rcParams rcParams[‘font.family’] = ‘sans-serif’ rcParams[‘font.sans-serif’] = [‘Hiragino Maru Gothic Pro’, ‘Yu Gothic’, ‘Meirio’, ‘Takao’, ‘IPAexGothic’, ‘IPAPGothic’, ‘VL PGothic’, ‘Noto Sans CJK JP’] matplotlibrcの書き換えもいいが、自分以外の人が見てもわかりやすい上、実行が容易。
9. モデルの作り方
9.1. LightGBM ・実装 : PyCaret(AutoML) ・チューニング: Optuna ・検証 : K-分割交差検証(fold=5)
9.2. モデルの作り方としては ・商品カテゴリ毎の予測 (テストデータの商品カテゴリ100のやつならtraindataも100しか使わない) ・全データで予測 のアンサンブル(出力結果を足して2で割っただけ)でした
9.3. kazumaro3rdさんがコメント下さっていた【2018年実績のない20品目だけ別モデルにする】
9.4. 僕の場合だと150品目予測用のモデルと20品目予測用モデルに分けてみたら精度上がりました。
9.5. 4位の方 上記のモデルを使用して三つのモデルで回帰予測 ・LGBM ・XGB ・CAT 上記の3モデルで予測した売上個数の予測値を特徴量に加え、CATで再度回帰予測 上記の予測値に以下の値を掛ける。 ・LGBM、XGB、CATで予測した値の平均絶対パーセント誤差の平均 上記結果を四捨五入し整数にする。
10. 仮説
10.1. 仮説】 そもそも売り上げが三か月無かったやつってクリスマスにしても売り上げないんじゃないか説 アニメとかのシーズンの切り替わりタイミングが三か月なので、三か月間売り上げなかったら流行としては切れていそう
11. 識別器
11.1. cat
11.2. xgb
11.3. ・LightGBMに初挑戦 → 平均を超えられた
12. 特徴量の選定
12.1. ※商品カテゴリのみダミー変数(One hot Encoding)、あとは数値として処理
12.2. 良く効いてくれた特徴量として、店舗IDと商品IDへのCountEncoding、2か月前のラグの計3点はブレずに効いてくれてた。
12.3. 店舗を店舗規模順にLabel Encodingするつもりで店舗ランキングを作っていたが、@Dera さんの作ってたような店舗規模の方が情報も多くなるのでそっちの方が効いた。Deraさんに感謝です!!
12.4. SHAP decision plotで個別予測値にどの特徴量がどう寄与しているか、SHAP summary plotで 全体傾向見たりしていると、各特徴量の貢献具合やプラス・マイナスが 想定した方向とは逆に働いていることもあるようだったので、学習データ・特徴量、予測値の中身を 見ながら、どうしてそうなるのか、どんな特徴量を加えたら良いか、で悩む、工夫考えてみる。。
12.5. ・ワイドフォーマット→ロングフォーマット、ロングフォーマット→ワイドフォーマットへの変換 ラグ特徴量作成や、要因(店舗や商品カテゴリ)ごとの時系列データの可視化に便利だった。
12.6. ※あまり貢献していないものも含まれているかもしれません。 ※当初は予測値が2000~3000までしかいきませんでしたが、以下の特徴量エンジニアリングをしてから5000ぐらいまで予測値があがりました。(結果スコアも向上。) 特徴量エンジニアリングは以下になります。 ・クリスマスというフラグという特徴量を作成、2018年の各月の売上工数の総数を計算し、トレーニングデータにマージ。 ・商品の販売開始時期(初めて売れてから)と販売時期から経過時間を記録。また販売開始時期から売れ行きが変わらないものを定番品、急激に廃れたものを流行品と分類。 また販売時期の売上個数からヒット商品かどうか、またどれくらいのヒット商品かを分類。 ・年間の店舗の売上個数から、繁盛店、普通、あまり売れない店と分類。各店舗の分類から、各月の売上個数をトレーニングデータにマージ。 ・商品の価格帯で分類し、価格帯毎の売上個数をトレーニングデータにマージ。 ・ラグ変数の期間を3カ月で作成。 ・上記で作成した分類の特徴量をターゲットエンコーディング。 ・小カテゴリーと店舗IDをマージし、その他_7とその他_13、それ以外でトレーニングデータを分割し、それぞれでモデルで予測し、最後にこちらの2つの予測値をマージし、提出。 ※理由はその他_7とその他_13(多分クリスマスソング)が12月に急激に売上をあげるが、それ以外との上昇率が異なるため、分割し、予測。
12.7. 使用した特徴量は 【商品ID, 商品カテゴリID, year, month, 月平均単価価格, 月最大単価価格, 月最小単価価格, 中古有無】中古有無は月最大と月最小が一致していなければ1フラグを建てるやつですね
12.8. 商品分類、月、年をワンホットエンコーディング
12.9. 2,3,6,9か月前の売上個数を追加
12.10. 4半期の特徴量を追加(2018-Q1, 2018-Q2, ...) 2019-10と2019-12は近いんだよ、ということをうまく学習させることができたのか(TargetEncodingとの組み合わせで効いているのかもしれませn) TargetEncoding(max, min, median, mean) year, month, 店舗ID、商品ID、店舗ID_商品ID、month_店舗ID_商品ID、商品カテゴリ、商品カテゴリ大分類、4半期、とかカテゴリ変数なるべくすべて minとmedianはほぼimportanceが0だった
12.11. ・ダミー変数化した商品カテゴリの大分類 ・商品価格のビニング ・12か月前の売上個数(サンプルコード参照) ・サンプルコードでは捨てられている、2018年12月(月ブロックでいうと11)のデータ ※月ブロック11のデータを再加工してtraindataへ組み入れ ・前年度からの月間売上個数の変化率(2019年12月は他の月の平均
12.12. 比較的に簡単に作成出来て、スコアに効きそうな特徴量を共有します!最後のスコア上げに使ってみてください!(汚いコードですいません!:sweat_smile:) 「店舗規模」列作成 from sklearn.preprocessing import MinMaxScaler sales['売り上げ']=sales['商品価格'] * sales['売上個数'] df_uriage = sales[['店舗ID','売り上げ']] df_uriage2 = df_uriage.groupby(['店舗ID']).sum() #正規化のクラスを準備 ms = MinMaxScaler() #特徴量の最大値と最小値を計算し変換 data_sc = ms.fit_transform(df_uriage2) df_uriage2['店舗規模'] = data_sc df_uriage2 = df_uriage2.reset_index() df_uriage3 = df_uriage2[['店舗ID','店舗規模']] dataset3 = pd.merge(dataset3, df_uriage3,left_on=['店舗ID'], right_on=['店舗ID'], how='left')
12.13. 4位のかた 特徴量 ・商品ID ・商品カテゴリID ・店舗ID ・年月 ・月 ・商品カテゴリ名A(商品カテゴリ名称のハイフンの左側) ・商品カテゴリ名B(商品カテゴリ名称のハイフンの右側) ・target_enc(月の売上個数平均)
13. 学習データの選定
13.1. テストデータに存在する商品(170点)に絞って学習 更に2018年に販売実績があるもの(150点)とないもの(20点)は分けて学習
13.2. ◆商品20点(2018年実績なし) 商品カテゴリ、商品ID、店舗ID、2ヶ月前の売上個数、12ヶ月前店舗別カテゴリ別売上個数、2ヶ月前店舗別カテゴリ別売上個数、月
13.3. ◆商品150点(2018年実績あり) 商品カテゴリ、商品ID、店舗ID、12ヶ月前の売上個数、12ヶ月前店舗別カテゴリ別売上個数、月