kaggle-Corporación Favorita Grocery Sales Forecasting


https://blog.csdn.net/bitcs_zt/article/details/79256688

該項比賽1月15日就已經結賽了,但由於之后進入期末,備考花費了大量的時間,沒來得及整理相關內容。現在終於有時間好好回顧比賽,並對這次比賽的過程進行記錄。


Corporación Favorita Grocery Sales Forecasting

本次比賽是預測商品銷量,給出的訓練數據為<單位銷量,日期,商店ID,商品ID,推銷活動標簽>,其中單位銷量是待預測值,基本上屬於回歸問題。同時給出的額外數據表有:

  • “商店信息表”—<商店ID,所在城市,所在州,類型,聚類簇>
  • “商品表”—<商品ID,所屬類別,所屬子類別,易腐爛標簽>
  • “交易信息表”—<商店ID,日期,總交易筆數>
  • “石油價格”—<日期、石油價格>
  • “節假日信息”

值得注意的是,同一商品可能在不同的商店均有銷售,而最后的測試數據為<日期,商店ID,商品ID,推銷活動標簽>,我們需要預測的是某商品在指定商店在某天的銷量。訓練數據給出了從2013年-2017年8月15日近一千余天的數據條目,每一天都包含各類商品在各個商店的數據條路,而很多商品只在某一些時間階段出現過。測試數據需要預測的是2017年8月16日—31日銷量情況。這就使得數據的組織十分重要。

和以前比賽一樣,我還是在比賽過半左右的時間參與的比賽,這個時間參加,基本上forum已經有了解決該題的大方向,不至於跑偏。由於還要兼顧課業,從最開始慢慢實驗測試是不太可能了,這也是kaggle比賽比較友好的地方,forum在比賽的各個時段都會有kaggler的集思廣益,使得我們這類以學習為目的的人能用最小的成本學到最多的東西。


切入點

如上所述,如何組織數據至關重要。剛參賽時,我在做了簡單的EDA后,發現數據並無特別的規律,原始訓練數據如下(數據量巨大,只取了2017年的):
這里寫圖片描述
可以看到,在如上這種數據組織方式下,難以“下手”,特征數目太少不可能直接用模型訓練,且其中的日期、ID等特征都不是數值型數據。思考幾日后,在forum上發現了一個帖子Ceshine Lee-LGBM Starter
,將數據按<商店ID、商品ID、日期>三級索引形式重新組織,得到數據表如下:
這里寫圖片描述
此時每個數據條目便有了真實意義——某商店的某商品在一段時間內的銷量。如此將該問題轉變為了類似“股票價格預測”的問題,這與我們的最終目標“預測某商店的某商品在2017年8月16日-31日的銷量”也能對應上。該文還做出了其他一些嘗試:抽取了銷量均值作為特征來訓練LGBM,針對16日-31日的每一天分別訓練模型進行預測,選取與16日-31日有相同周期的(16日為周三)作為訓練數據。最后排名靠前的選手基本都參照了該文的思想。


分析

利用機器學習來解決實際問題,關鍵無外乎四點,對應到本題有如下分析:

  • data。1、觀察原始數據,並進行適當的變換,使得數據適用於機器學習模型。在本題中,原始數據的呈現形式並不直觀,數據條目之間是獨立的,特征數量不夠且不是數值型。將其轉化成為銷量序列后,自然而然地成為了一個時序預測問題,這個領域是我們較為熟悉的。2、組織訓練集和驗證集。本題需要預測共16日的銷量,因此訓練集和驗證集的標簽也要是16日,這不免在時間段上帶來重疊,合理利用更多日期的信息顯然會對最后結果有幫助。
  • feature。有了數據后,應考慮如何組織成為特征,將其變為適合模型的訓練數據。在本題中,每一數據條目為某商店某商品數百日內每天的銷量,我們顯然不能將其全部運用到模型中,一來數據量過於龐大,二來會引入許多噪聲(如許多商品在一長段時間內都沒有銷量)。日均銷量就是一個很好的特征,既能壓縮特征數量,也能一定程度上降噪。我們應該考慮還能構造哪些feature,能很好的表示數據同時又不丟失太多信息。
  • model。選取什么樣的模型來解決這個問題比較合適?針對時間序列問題,第一想法是RNN,深層RNN抗噪聲和表示能力極強,但原始數據對應下每個timestamp只有1天的銷量,feature還需要自己構造。而且每個數據條目的scale很不一樣,調參成本應該會很高;第二想法是決策樹模型,樹模型解決問題的邏輯是歸類,把類似的商品(同一商店、不同商店的同一商品、同一類別的商品等)歸在一起,其本質是“強平均”,即通過決策樹歸類相似的商品,再作平均。這種方法不能直接使用銷量序列,需要人為構造有意義的特征;但由於樹模型有XGBoost和LGBM的高效實現,參數意義也較為明顯,試錯成本會比NN低。
  • ensemble。模型融合是比賽進行到最后必不可少的一步,簡單的可以多個模型結果做bagging,復雜的可以用第一層模型結果來訓練第二層模型。在kaggle比賽中bagging是很tricky的,一定要有着好的cv做輔助,否則極容易過擬合。

data、feature、model、ensemble四個部分不可能嚴格意義上串行工作,有可能選用了model后發現不合適,更換model的同時數據可能也需要重新整理。這需要我們建立良好的pipeline,減少反復調整帶來的額外工作量,並記錄好重要步驟作log。


Data

先將原始數據轉變為銷量序列。由於時間相關序列在訓練集和驗證集的組織上需要嚴格根據時間決定,給出2017年5-8月日歷如下:
這里寫圖片描述
可以看到,待預測日為16-31共計16日,要注意到以周三為起點這一周期信息,並且要最大化地利用信息。由於之前做EDA,單個商品沒有反映出長期的周期性,故可以認為,訓練集越接近待預測日,整合越多的信息,越能准確預測。比如選擇7.26-8.9的每個商品的價格作為訓練集標簽,7.26之前的價格數據抽取feature作訓練集,這段時間是符合周期分布(周三開始、周四結束)下時間上最接近測試集的16日,將其稱為一個batch。考慮獲取多個batch形成訓練集,可以選取7.5-7.20、7.12-7.27、7.26-8.9三個batch,即間隔一周或若干周選取一個batch,保持了周期性。雖然batch之間可能有一定的重復性,但這是必要的。一來使數據量增多,模型不容易過擬合,二來針對某個商品,我們有了若干個數據條目(一個條目即為上圖中某個商店的某個商品在一段時間內的銷量),無論是使用樹模型還是NN,這都能使模型更加robust。

實驗中batch為6-15不等,batch之間間隔試了1、2、4周。使用7.26-8.9做validation set,在比賽后期,利用帶validation set來確定模型訓練輪數R(validation set上准確率確定是否結束訓練),再將validation set的數據加入到training set,這樣我們可以利用更靠近8.16的數據,實際上也這樣做也確實帶來了准確率的提升。

Feature

針對某一batch,比如7.26-8.9,根據商品價格序列抽取如下特征:

  • 前10,20,…,100天(指7.26之前,下同)的均銷量-mean_{}
  • 前3,7,14,21,28,…,100天參與推銷活動的次數和-promo_{}
  • 前7,14,28,56,84,112,140天銷量為0的天數和-zero_{}
  • 前7,14,28,56,96,140天的最高銷量-max_{}
  • 最近一周每天的銷量-day_{}
  • 過去四個月每個月的銷量-mean_month_{}
  • 近一段時間周x的均銷量-mean_week_{}
  • 前14,28,42,56,70,84,100,120天銷量不為0天數的均銷量-sale_mean_{}
  • 前14,28,42,56,70,84,100,120天銷量不為0天數的銷量方差-std-{}
  • 待預測16天每天是否參與推銷活動-promo_future_{}

由於在比賽過程中常想到新的特征,加入特征又需要重新實驗,耗時耗力。故在比賽之初,就應該盡量把特征工程做好,同時建好pipeline,以減小增刪特征重復試驗帶來的精力損耗。同時要結合樹模型所產生的feature importance判斷特征好壞。Data和Feature的實驗在比賽早期要盡可能做完善,雖然不可能盡善盡美,一定要建好pipeline,血的教訓!!!比賽進行到后期時,model和ensemble也要做實驗時,在jupyter里寫代碼的弊端就體現出來了,往往上下翻閱很久。良好的pipeline會讓功能架構清晰很多。

Model

其實剛看到時間序列模型是想用RNN的,找了forum里面一個LSTM網絡跑了一下,筆記本跑了一晚上,成本太高了,就放棄了。主要還是使用的樹模型。這次嘗試了LGBM,據說是比XGBoost快,這次時間緊,也就沒做對比。總的來說還是挺好用的,速度也很快。由於比賽過程中,除了模型調參還要調整data和feature,所以最終跑出來的單個模型也沒有表現特別好的。而且在比賽后期由於提交次數限制,很多模型調參只是根據本地cv判別了優劣,而這次比賽的本地cv和提交結果不是完全同步提升的,可能錯過了一些潛在的好模型,這也是以后要注意的。對於LGBM的調參還是太稚嫩了,只能說多經歷多學習吧。

還使用了CatBoost,據說對類別型數據支持較好,但應該是我還不太了解一些參數的意義,只是簡單地做了一些設置,最終該模型表現效果不如LGBM,且速度比較慢。

比賽初始還嘗試用auto encoder,效果不理想就放棄了。花費時間成本也比較高。理論上來說直接調參RNN是更好的選擇,上次比賽優勝選手使用了該模型,自己也想試試。也算嘗試了一下吧。

Ensemble

時間緣故,也沒有時間做兩層的ensemble了,只是將結果進行了簡單的加權bagging。最終參與bagging的模型有7個LGBM+1個CatBoost+1個Median-based。最后放榜的成績只有小數點后三位,有很多簡單的bagging和單個模型與最終提交的bagging是一樣的成績,也無法得知孰優孰劣,本着bagging模型多樣性的原則,還是選擇了一個較多模型的bagging。


結賽后,也看了forum上很多排名靠前選手給出的解決辦法。很多使用了NN based的方法,不過多數都來自於之前一些時間序列比賽的經驗,也說明了經驗對於神經網絡的設計構建是多么的重要。不過讓人欣慰的是排名第1的選手給出的得分最高的模型是LGBM,結合了很好的特征工程。讓我這種現階段沒有DNN實驗條件的人有了很多希冀。

總結下從這個比賽學到的東西:

  • 時間序列的問題轉化
  • 基於日期的訓練集、驗證集分割和選取(周期特征,batch的獲得,如何利用更多數據)
  • 特征工程(時序統計特征)
  • CV(驗證集的設置)
  • LGBM的調參
  • 不同預處理、特征工程、batch設置下的ensemble
  • CatBoost、LSTM的簡單使用

待提高之處:

  • pipeline的建設!!!(一定早點設計,省去后面實驗的大麻煩,尤其是在jupyter下編程)
  • 特征工程(規范地結合feature importance來篩選特征,不同類型特征分批實驗)
  • CV和LB的同步規律(即如何通過設置、分析本地CV來提高模型最終效果)
  • LGBM的調參(自動化調參,減少調參精力)
  • 神經網絡模型的使用(希望早點具備實驗條件)
  • 合理把握比賽時間和節奏,及時“止損”(比賽后期花了很多時間調參卻沒有太大進展,收獲效率低)

給出賽后排名靠前選手的解決方案匯總:
Top Solutions


以上是這次比賽我自己方面的一些總結,一分耕耘一分收獲吧。最后40/1675的成績,獲了一枚銀牌,心里也很開心。以后還是多打比賽,多寫代碼,多看forum,多學東西。

但做好事,不問前程。

--------------------- 本文來自 ZSYGOOOD 的CSDN 博客 ,全文地址請點擊:https://blog.csdn.net/bitcs_zt/article/details/79256688?utm_source=copy 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM