前些日子,參加了一個解放號的行業大數據創新應用大賽,
https://1024.jfh.com/question/detail?contestId=6
一.問題描述
賽題是根據西安機場上半年的航班起降信息,建立適當預測模型,預測未來七天的航班准點率,(航班實際起飛時間-航班計划起飛時間)< 15 分鍾即為准點。
二.問題分析
為解決這個問題,首先需要對比賽數據進行深入了解。賽會提供的數據是一個csv文件,由15萬條航班起降數據組成。每條記錄包含如下九種屬性:[航班號,出發地,到達地,機型,計划起飛時間,計划到達時間,實際起飛時間,實際到達時間,進出港類型]。
下面是幾條數據示例:
HV2380,XIY,KUL,A330,2018/1/1 0:30,2018/1/1 5:40,2018/1/1 0:39,2018/1/1 5:59,出港
IU6364,XIY,DMK,B737,2018/1/1 1:35,2018/1/1 5:30,2018/1/1 1:40,2018/1/1 5:17,出港
IU5438,XIY,HKT,B737,2018/1/1 1:35,2018/1/1 6:35,2018/1/1 3:18,2018/1/1 7:51,出港
其中,可以用於預測的特征只有 航班號、機型、出發地、到達地、計划起飛時間這5種特征,僅使用這些特征來建立預測模型的話是不夠充分的。
2.1 特征補充——爬取天氣數據
為此,我們額外收集了西安機場半年以來的天氣數據,根據日常經驗可知,天氣對於航班起降的影響是巨大的。在給定的數據文件中,僅有2018年1月1日到2018年6月30日的數據,因此,我們在網上爬取了這些日期的天氣情況。經過瀏覽網頁,決定使用”逍遙天氣”網站的數據,對每天天氣新聞進行爬取(如:http://www.xaoyo.com/news/xian/20180629.html)。
圖1.天氣網站示例
我們使用selenium+phantomJS的框架來對網站的標題進行爬取,發現每天的新聞的URL格式相對固定,僅后方的日期略有差別;此外,每天的新聞標題都存儲在<div class=’hed_tit’>中的<h1>標簽下,可以使用BeautifulSoup將其解析出來。
1 title = BeautifulSoup(driver.page_source,'html.parser').find('div',class_='hed_tit').find('h1').text
獲取了標題字符串之后,我們對其進行解析切分,使用python自帶的split()函數,將三種屬性:天氣、氣溫、風力 解析出來。經過粗略分析,感覺氣溫屬性對航班的准點率影響不大,便僅留下天氣與風力屬性,保存在weather.csv文件中。
這里爬蟲的具體實現我就不再進行細致講解,對爬蟲沒有太多基礎的同學可以看我之前的博文(基於selenium+phantomJS的動態網站全站爬取)。
2.2非線性特征數據處理
現在,我們有了航班號、機型、出發地、到達地、計划起飛時間、天氣、風力這7種屬性。
第一個問題是,神經往往要求我們的輸入在[0~1]之間,但航班號、機型、出發地、到達地、天氣這5個屬性並不是線性的,我們無法使用算數計算的方式將其歸一化。為了解決這個問題,直覺上想到One-Hot Vector編碼。即將該特征編碼成一條N*1的向量,該特征所處的位置賦值為1,向量中的其他位置賦值為0。One-Hot Vector 在網上已有豐富的資料可供查閱,這里我們就簡單舉例說明一下。
假設現在一共有“HV2380”,“IW5079”,“SY6658”,“WJ5723”,“VQ9535”共5種航班,那么對於數據文件中的第一條數據:
HV2380,XIY,KUL,A330,2018/1/1 0:30,2018/1/1 5:40,2018/1/1 0:39,2018/1/1 5:59,出港
可以將航班號這個特征編碼為 [1, 0, 0, 0, 0]。 對於另一條數據:
WJ5723,XIY,TGO,B737,2018/1/1 13:50,2018/1/1 16:30,2018/1/1 13:58,2018/1/1 16:04,出港
可以將航班號這個特征編碼為 [0, 0, 0, 1, 0]。
為了准確的對所有航班號屬性進行One-Hot編碼,我們遍歷所有數據條目,統計出一共有1266中不同的航班。也就是說,對於這一種特征來說,就會產生一個1266*1的One-Hot向量,這會導致在神經網絡訓練的時候產生一個相當大的稀疏矩陣。
三.基於數據統計的多步預測模型建立
3.1數據統計及特征提取
上文講到直接對航班進行One-Hot編碼,會產生一個較大的稀疏向量,進而在搭建網絡的過程中會出現一個相當大的稀疏矩陣。直觀上我會懷疑模型能否可以正確的對其進行訓練,達到理想的效果。
因為稀疏矩陣的存在,看起來對每一條航班信息進行准點率預測的模型不是那么好實現。等等,我們再重新審一下題~~賽題的要求是: 預測未來七天的航班准點率!
即,我們不是預測每個航班的起降准點率,而是預測每日所有航班的准點率:(准點航班數量/當日航班總數)*100%。也就是說,對於給定數據文件中的數據,我們沒有必要對每一條航班信息建模訓練,我們完全可以統計出每一天的航班起降情況,並以天為單位來進行建模預測。
因此,我們着手開始進行數據統計的工作。首先,就是以天為單位,統計每天的航班起降總數與准點航班數目。為達到這個目的,我先初始化了兩個長度為181的全0向量(賽題數據一共包含有181天),分別表示第n天的航班總數與准點航班數目。之后開始對數據文檔中的15萬條數據進行一次遍歷,對於每條數據,計算其所屬的日期,與其是否准點,分別在對應向量的位置中+=1。
1 count_list = list(np.zeros(day_num)) # 計算每天有多少班次飛機的list 2 accurate_num_list = list(np.zeros(day_num)) # 每天准點的飛機數量list 3 for i in range(len(fin))[1:]: # 首行是標注,非數據 4 plan_fly_time = time.mktime(time.strptime(fin[4][i],'%Y/%m/%d%H:%M')) 5 day_index = int((plan_fly_time-begin_stamp)/(3600*24)) 6 count_list[day_index] += 1 7 actual_fly_time = time.mktime(time.strptime(fin[6][i],'%Y/%m/%d %H:%M')) 8 if actual_fly_time-plan_fly_time < 15*60: 9 accurate_num_list[day_index] += 1
在進行統計的過程中,還會遇見一些有問題的數據,例如說有部分數據的機型一項為NULL,有一部分數據的起飛、降落時間為NULL:
圖3.1 值為NA的臟數據示例
圖3.2 值為#NA的臟數據示例
在數據處理的過程中,我們選擇對這些數據條目進行刪除。整體刪除量較小(總共刪除<20條數據),對建模不會產生任何影響。
在統計完每日航班數目與每日准點率之后,畫出其圖像,來對其關系進行直觀的對比:
圖3.3 每日起降航班數目與准點率關系統計圖
根據圖像,我們可以得出,航班准點率整體趨於一個穩定序列,即沒有明顯上升、下降趨勢的序列,平均准點率大概在60%左右;此外,可以略微的看出每日起降數量與准點率呈反比(如第45~75日),這也與我們的直覺相契合,即航運壓力大的時候飛機准點率不高。我們對前兩個准點率低谷進行分析,發現在1月3日和1月24日有降雪,降雪不但對當天的航班准點率有影響,還會繼續影響后續幾天的航班,尤其是降雪過后第二日,航班准點率會比降雪當天還要低。
圖3.4 降雪會明顯影響后續幾日航班准點率
此外,繼續觀察天氣數據與航班准點率統計圖,可以發現氣溫對航班准點率幾乎沒有影響(數據從冬天到夏天,准點率沒有明顯上升、下降趨勢)。因此,在對氣象特征進行提取的時候,我們僅僅對天氣和風力進行建模。
對於天氣建模,我們使用One-Hot編碼的方式,將天氣分為3類,第一類是{陰,晴,多雲},第二類是{小雨,中雨,大雨,暴雨},第三類是{雨夾雪,小雪,中雪,大雪,暴雪}。最終生成一個形狀為3*1的向量。如某日天氣為“中雨”,特征向量為[0,1,0],某日天氣為“多雲”,其特征向量為[1,0,0]。而對於風力,我們將其風力的級別提取出來,根據其級別0~12級將其歸一化到[0,1]之間。此時,我們就可以將統計數據抽象提取出6個特征,即航班數,准點率,陰晴,雨,雪,風力。
注: /* 接下來對剩下來的航班號、機型、出發地、降落地進行篩選,考慮到航班號確定時,其機型,出發地,將落地也將是確定的,在構造模型時,我們僅僅保留航班號這一個特征。最終,我們的預測模型擁有。 */
3.2多步預測模型建立
Nguyen Hoang An曾對神經網絡進行多步預測的策略進行過總結[1], 包括直接預測策略、迭代策略,MIMO策略,DirREC策略以及DirMO策略。其中,直接預測策略、DirREC策略和DirMO策略都需要建立多個預測模型,訓練開銷比較大。而MIMO策略的預測效果並不好,因此我們使用迭代策略來進行多步預測。國內也有學者對傳統Airline Passenger數據進行多步預測[2],取得了不錯的效果。迭代策略就是訓練出一個預測模型,預測出下一個時間節點的數據,之后將預測輸出作為輸入,對第二個時間節點進行預測,如此迭代反復,獲得多步預測結果。
在上一節的處理中,我們最終留下了航班數,准點率,陰晴,雨,雪,風力6個特征用於預測。其中,模型並不能預測天氣情況,僅僅能對航班數、准點率兩個特征進行預測,在這次建模中,假設我們可以獲得第二天准確的天氣情況。每次將預測得到的航班數和准點率與已知的天氣數據進行拼接,迭代的預測后續的航班數和准點率,預測得到未來7天的航班起降准點率。
預測模型搭建如下:
1 model = Sequential() 2 model.add(LSTM(128, input_shape=(self.window_len,6),return_sequences=False)) # TODO: input_shape=(timesteps ,data_dim) 3 model.add(Dropout(0.2)) 4 model.add(Dense(2)) 5 model.add(Activation('sigmoid')) 6 model.compile(loss='binary_crossentropy',optimizer='rmsprop',metrics=['accuracy'])
這里使用keras的序貫模型來構造訓練模型。考慮到天氣會對准點率有持續的影響,這里使用LSTM循環神經網絡來進行預測,時間窗口(self.window_len)設置為6,輸入特征數目也是6(即input_shape中的第二個參數)。尤其注意最后一層一定要使用Dense(2),使我們的輸出模型可以輸出兩個結果,第一個結果是第二天航班數的預測效果,第二個結果是第二天准點率的預測結果。
本次數據共有181天,排除掉最后7天的數據(最后七天的僅僅包含入港的數據、沒有出港的數據),再減去時間窗口6,得到168個樣本。其中划出10個樣本作為測試集,158個樣本作為訓練集。使用這158個樣本進行訓練,訓練2000個epoch,獲得模型並保存。
使用這個模型來對10個測試樣本進行預測,每次僅預測第二天的航班數和准點率:
圖3.5 航班數目預測結果(單點預測)
圖3.6 航班准點率預測結果(單點預測)
使用倒數第7天的樣本輸出作為初始樣本,使用迭代策略來對未來7天的航班數目和准點率進行預測(假設已有未來7天准確的天氣預報):
圖3.7 航班數目預測結果(連續預測7天)
圖3.8 准點率預測結果(連續預測7天)
通過上圖可以看出,不論是對航班數目還是准點率,模型都可以對其趨勢進行較好的把握,7天准點率預測結果的均方根誤差約為5.3%。
當然,在模型訓練的時候,我們還可以對其窗口大小、訓練次數、批處理大小、dropout比例等進行調整。時間充裕的情況下可以反復調整模型,獲得更好的預測效果。
Reference:
[1] NH An, DT Anh. Comparison of Strategies for Multi-step-Ahead Prediction of Time Series Using Neural Network[C]. 2015 International Conference on Advanced Computing and Applications (ACOMP), Ho Chi Minh City, 2015, pp. 142-149.
[2] Liu YP, Hou D, Bao JP, et al. Multi-step Ahead Time Series Forecasting for Different Data Patterns Based on LSTM Recurrent Neural Network[C]. 2017 14th Web Information Systems and Applications Conference (WISA), Liuzhou, Guangxi Province, China, 2017, pp. 305-310.
后記:此次也有嘗試已知未來7日每天航班計划(即已知每日航班數)來僅對航班准點率這一個屬性進行預測。但預測效果不如上述模型理想。也許是因為數據較少(僅181天的數據,包括最后7日的殘缺數據)的原因,可以后續對該模型繼續留待觀察。