本文為你介紹,如何從 Waze 交通事件開放數據中,利用序列模型找到規(guī)律,進行分類預測。以便相關部門可以未雨綢繆,提前有效干預可能發(fā)生的嚴重擁堵。
尋找
之前在《文科生如何理解循環(huán)神經網絡(RNN)?》一文中,我為你講解過循環(huán)神經網絡的含義。《如何用 Python 和循環(huán)神經網絡做中文文本分類?》一文,我又為你介紹了如何用循環(huán)神經網絡對文本做分類。
我不希望給你一種錯誤的簡單關聯,即“循環(huán)神經網絡只能用來處理文本數據”。
事實上,只要是序列數據,你都可以考慮一下循環(huán)神經網絡。
我一直打算找個其他序列數據的樣例,給你展示循環(huán)神經網絡的更多應用場景。
但是這個數據不太好選擇。
目前一個熱門的應用場景,就是金融產品的價格預測。
每時每秒,金融產品的價格都在變動。把它匯集起來,是個典型的序列數據。
但是我一直不看好這種應用。因為金融產品的定價,應該是面向未來的?;跉v史價格信息尋找波動規(guī)律,并對未來價格進行預測,實際上如同看著后視鏡開車一般危險。
但是,還有很多人依然樂此不疲地嘗試。很多時候,他們也能嘗到成功的甜頭。
這是為什么?
原因在于,金融市場的參與者,并非理性的機器,而是由人組成的群體。從行為金融學的角度來看,進化給人類思考與行為帶來了一些“快捷方式”,你可以利用它們從中漁利。
陸蓉教授的《行為金融學》欄目,對此有詳細介紹。
例如,人們追漲殺跌,認為歷史會重演;
例如,吸引大眾關注到事件,總會帶來買入;
例如,人們會傾向于投資于自己熟悉的標的;
例如,人們會購買下跌的已持倉標的,來攤薄成本。
……
如果沒有大風浪,這種對市場參與者行為規(guī)律的洞察,確實可以幫你賺錢。你可以從價格的歷史波動中,挖掘出這些規(guī)律的影響。但是這對沒有模型可用的人來說,不公平。教你建模,就如同教你考試作弊。
如果遇到黑天鵝事件,其影響大概率會超過市場參與者行為偏誤帶來的歷史價格波動規(guī)律。那么你,可能會因為應用模型,而遭遇虧損。你大約不會認為這是自己的錯誤,而直接把我當做騙子,朝我扔雞蛋。
理性權衡后,我決定不用金融產品價格趨勢分析,作為循環(huán)神經網絡的應用樣例。
其他開放的序列數據,當然也有很多。例如共享單車租用數據、氣溫變化數據等。
不過這些應用,一來別人都寫過了,不新鮮。二來,氣溫變化,你看天氣預報就好了。共享單車租用數量……你真的關心這里的規(guī)律嗎?
正在我猶豫的時候,一次偶然的機會,我接觸到了一個新的序列數據樣例——交通事件數據。我覺得,把它作為應用案例分享給你,可能更合適一些。
比賽
拿到這個數據,是因為我參與了一次編程馬拉松(hackathon)比賽。
比賽在 Frisco 的 UNT Inspire Park 舉辦。從早上8點開始,一直到晚上9點多才結束。中間可以自由吃免費提供的點心和水果,也可以到院子里曬曬太陽放放風。大家還可以自由交流和組隊。
主辦方為參賽者提供了若干種開放數據,也提了一些問題供大家參考解答。當然,實際參賽的時候,你也可以自己擬定新的題目。
這其中,就包括了 Waze 數據。
我在中國開車,平時用的都是高德導航,對于 Waze 這款 App 不大熟悉。
簡而言之,這個 Waze 應用除了提供一般的導航功能之外,還有一個類似于眾包的功能——讓司機們自由提交路況信息。
這樣一來,Waze 就利用群體智慧形成了一個眼觀六路耳聽八方的巨大網絡,隨時依據用戶提供的情況,匯總成實時交通參考。并且匯報給用戶,以便于大家調整自己的行車路線。
我覺得最有用的特點是,在堵車的時候,你可以了解到前面究竟發(fā)生了什么。其他導航也有實時交通狀況提示,但是你對前面的情況一無所知。道路半幅施工?交通事故?
信息的對稱,可以在很大程度上,讓司機避免焦慮。
Waze 從幾年前開始,就和政府部門合作,進行數據開放共享。這樣一來,政府可以通過 Waze 的數據了解交通實時狀況,對于問題進行快速的響應處理;與此同時, Waze 用戶也因為可以獲取整合其他相關類型的政府開放數據(例如道路規(guī)劃等),更加有效合理安排出行。
這次比賽,主辦方提供的數據,是 DFW (達拉斯-沃斯堡都會區(qū))區(qū)域,11月1日到29日的 Waze 交通事件(Incidents)開放數據,這是政府開放數據的一部分。這些數據基本都是來自于 Waze 用戶的提交。
原始的數據,接近 300 MB。每一條事件信息,都包含了提交的經緯度,以及時間。因此在探索性數據分析階段,我做了幾個可視化圖形。
這是我當天跟新認識的編程高手 Jesse 學的 QGIS 分析結果。
看看圖上的點,每一個都對應一次事件匯報。這叫一個密密麻麻啊。
因為 QGIS 初學,用得不熟,我還是用 Python 進行了分類繪圖。
這只是前 3000 條數據中部分類型的可視化。其中紅色代表交通擁堵,黃色代表事故發(fā)生,藍色代表有車停在了路肩上。
可以看到,紅色的數據量最大。這說明交通擁堵是個大問題。
我把全部的數據都拿了出來,提煉出包含的事件類型,包括以下這些類:
我看到,其中單是交通阻塞,也是分為若干級別的。其中最嚴重的,分別是“大型交通擁堵”(large traffic jam)和“超大型交通擁堵”(huge traffic jam)。
于是,我把所有這兩種嚴重交通擁堵事件,合并成一個集合;其他剩余事件,作為另一個集合。
對于每一個嚴重擁堵事件,我追溯30分鐘,把之前同一條道路上,發(fā)生的事件,按照順序存成一個列表。這樣的列表,有987個;但是,其中有一些,是驟然發(fā)生的,30分鐘的區(qū)間里面,沒有任何其他事件作為先兆。這樣的空列表,我進行了清除。剩下了861個有效序列。
同樣,從剩余事件集合中,我們隨機找到了861個非空有效序列。這些序列,后續(xù)緊隨事件,都不是嚴重擁堵。
我們對嚴重擁堵之前30分鐘的事件序列,標記為1;對于非嚴重擁堵之前30分鐘的事件序列,標記為0。
于是,我們就把問題轉換成了,能否利用事件序列,進行分類,預測后續(xù)是否會發(fā)生嚴重擁堵。
靠著這個模型,我們團隊(UNT IIA lab代表隊,其實不過就是我和春迎倆人,團隊昵稱 watch-dumpling )在這次比賽中,獲得第一名。
這是 HackNTX 官網的報道(http://t.cn/EUbS9m5) 。
UNT 網站也正式發(fā)布了這則新聞(http://t.cn/EUbS127),于是我周圍盡人皆知。我才剛拿到手的獎金,立即就因為請客被掃蕩一空了。
奪冠純屬是個意外,幸運占得比重很大。但是我覺得我們做的這個模型,還是有些應用價值的。
下面,我就以這組 Waze 交通事件數據,詳細給你講解一下,如何用 Python, Keras 和循環(huán)神經網絡,來實現這個序列數據分類模型。
環(huán)境
要運行深度學習,你需要有 GPU 或者 TPU 的支持,否則會累壞你的筆記本電腦的。Google Colab 是個不錯的實驗平臺,可以讓你免費使用 TPU 來進行深度學習訓練。你可以閱讀《如何免費云端運行Python深度學習框架?》一文,查詢更為詳細的介紹。
這里,請你使用 Chrome 瀏覽器,點擊這個鏈接,安裝一個插件 Colaboratory 。
把它添加到 Google Chrome 之后,你會在瀏覽器的擴展工具欄里面,看見下圖中間的圖標:
然后,請到本范例的github repo 主頁面。
打開其中的demo.ipynb文件。
點擊 Colaboratory 擴展圖標。Google Chrome 會自動幫你開啟 Google Colab,并且裝載這個 ipynb 文件。
點擊上圖中紅色標出的“復制到云端硬盤”按鈕。Google 會為你新建一個屬于你自己的副本。
點擊菜單欄里面的“代碼執(zhí)行程序”,選擇“更改運行時類型”。
在出現的對話框中,確認選項如下圖所示。
點擊“保存”即可。
下面,你就可以依次執(zhí)行每一個代碼段落了。
注意第一次執(zhí)行的時候,可能會有警告提示。
出現上面這個警告的時候,點擊“仍然運行”就可以繼續(xù)了。
如果再次出現警告提示,反勾選“在運行前充值所有代碼執(zhí)行程序”選項,再次點擊“仍然運行”即可。
環(huán)境準備好了,下面我們來一步步運行代碼。
代碼
首先,我們讀入 Pandas 軟件包,以便進行結構化數據的處理。
importpandasaspd
這次還要讀入的一個軟件包,是 Python 中間進行數據存取的利器,叫做 pickle 。
importpickle
它可以把 Python 數據,甚至是許多組數據,一起存儲到指定文件。然后讀出的時候,可以完全恢復原先數據的格式。這一點上,它比用 csv 進行數據存儲和交換的效果更好,效率也更高。
下面我們從本文配套的 github 項目中,把數據傳遞過來。
!gitclonehttps://github.com/wshuyi/demo_traffic_jam_prediction.git
數據的下載,很快就可以完成。
Cloninginto'demo_traffic_jam_prediction'...remote:Enumeratingobjects:6,done.[Kremote:Countingobjects:100%(6/6),done.[Kremote:Compressingobjects:100%(4/4),done.[Kremote:Total6(delta0),reused3(delta0),pack-reused0[KUnpackingobjects:100%(6/6),done.
我們告訴 Jupyter Notebook ,數據文件夾的位置。
frompathlibimportPathdata_dir=Path('demo_traffic_jam_prediction')
打開數據文件,利用 pickle 把兩組數據分別取出。
withopen(data_dir/'data.pickle','rb')asf:[event_dict,df]=pickle.load(f)
先看其中的事件詞典event_dict:
event_dict
以下就是全部的事件類型。
{1:'roadclosedduetoconstruction',2:'trafficjam',3:'stoppedcarontheshoulder',4:'roadclosed',5:'other',6:'objectonroadway',7:'majorevent',8:'pothole',9:'trafficheavierthannormal',10:'roadconstruction',11:'fog',12:'accident',13:'slowdown',14:'stoppedcar',15:'smalltrafficjam',16:'stoppedtraffic',17:'heavytraffic',18:'minoraccident',19:'mediumtrafficjam',20:'malfunctioningtrafficlight',21:'missingsignontheshoulder',22:'animalontheshoulder',23:'animalstruck',24:'largetrafficjam',25:'hazardontheshoulder',26:'hazardonroad',27:'iceonroadway',28:'weatherhazard',29:'flooding',30:'roadclosedduetohazard',31:'hail',32:'hugetrafficjam'}
同樣,我們來看看存儲事件序列的數據框。
先看前10個:
df.head(10)
注意,每一行,都包含了標記。
再看結尾部分:
df.tail(10)
讀取無誤。
下面我們來看看,最長的一個序列,編號是多少。
這里,我們利用的是 Pandas 的一個函數,叫做idxmax(),它可以幫助我們,把最大值對應的索引編號,傳遞回來。
max_len_event_id=df.events.apply(len).idxmax()max_len_event_id
結果為:
105
我們來看看,這個編號對應的事件序列,是什么樣子的:
max_len_event=df.iloc[max_len_event_id]max_len_event.events
下面是長長的反饋結果:
['stoppedcarontheshoulder','heavytraffic','heavytraffic','heavytraffic','slowdown','stoppedtraffic','heavytraffic','heavytraffic','heavytraffic','heavytraffic','trafficheavierthannormal','stoppedcarontheshoulder','trafficjam','heavytraffic','stoppedtraffic','stoppedtraffic','stoppedtraffic','heavytraffic','trafficjam','stoppedcarontheshoulder','stoppedtraffic','stoppedtraffic','stoppedtraffic','heavytraffic','trafficheavierthannormal','trafficheavierthannormal','trafficheavierthannormal','trafficheavierthannormal','heavytraffic','stoppedtraffic','trafficheavierthannormal','pothole','stoppedcarontheshoulder','trafficjam','slowdown','stoppedtraffic','heavytraffic','trafficheavierthannormal','trafficjam','trafficjam','stoppedcarontheshoulder','majorevent','trafficjam','trafficjam','stoppedtraffic','heavytraffic','trafficheavierthannormal','stoppedcarontheshoulder','slowdown','heavytraffic','heavytraffic','stoppedcarontheshoulder','trafficjam','slowdown','slowdown','heavytraffic','stoppedcarontheshoulder','heavytraffic','minoraccident','stoppedcarontheshoulder','heavytraffic','stoppedcarontheshoulder','heavytraffic','stoppedtraffic','heavytraffic','trafficheavierthannormal','heavytraffic','stoppedcarontheshoulder','trafficheavierthannormal','stoppedtraffic','heavytraffic','heavytraffic','heavytraffic','stoppedcarontheshoulder','slowdown','stoppedtraffic','heavytraffic','stoppedcarontheshoulder','trafficheavierthannormal','heavytraffic','minoraccident','majorevent','stoppedcarontheshoulder','stoppedcarontheshoulder']
讀一遍,你就會發(fā)現,在超級擁堵發(fā)生之前,確實還是有一些先兆的。當然,這是由人來閱讀后,獲得的觀感。我們下面需要做的,是讓機器自動把握這些列表的特征,并且做出區(qū)別分類。
我們看看,這個最長列表的長度。
maxlen=len(max_len_event.events)maxlen
結果為:
84
這里的前導事件,還真是不少啊。
下面我們要做的,是把事件轉換成數字編號,這樣后面更容易處理。
我們使用以下的一個小技巧,把原先的事件詞典倒置,即變“序號:事件名稱”,為“事件名稱:序號”。這樣,以事件名稱查詢起來,效率會高很多。
reversed_dict={}fork,vinevent_dict.items():reversed_dict[v]=k
我們看看倒置的結果詞典:
reversed_dict
這是反饋結果:
{'accident':12,'animalontheshoulder':22,'animalstruck':23,'flooding':29,'fog':11,'hail':31,'hazardonroad':26,'hazardontheshoulder':25,'heavytraffic':17,'hugetrafficjam':32,'iceonroadway':27,'largetrafficjam':24,'majorevent':7,'malfunctioningtrafficlight':20,'mediumtrafficjam':19,'minoraccident':18,'missingsignontheshoulder':21,'objectonroadway':6,'other':5,'pothole':8,'roadclosed':4,'roadclosedduetoconstruction':1,'roadclosedduetohazard':30,'roadconstruction':10,'slowdown':13,'smalltrafficjam':15,'stoppedcar':14,'stoppedcarontheshoulder':3,'stoppedtraffic':16,'trafficheavierthannormal':9,'trafficjam':2,'weatherhazard':28}
成功了。
下面我們編寫一個函數,輸入一個事件列表,返回對應的事件編號列表。
defmap_event_list_to_idxs(event_list):list_idxs=[]foreventin(event_list):idx=reversed_dict[event]list_idxs.append(idx)returnlist_idxs
然后,我們在剛才是找到的最長列表上,實驗一下:
map_event_list_to_idxs(max_len_event.events)
結果是這樣的:
[3,17,17,17,13,16,17,17,17,17,9,3,2,17,16,16,16,17,2,3,16,16,16,17,9,9,9,9,17,16,9,8,3,2,13,16,17,9,2,2,3,7,2,2,16,17,9,3,13,17,17,3,2,13,13,17,3,17,18,3,17,3,17,16,17,9,17,3,9,16,17,17,17,3,13,16,17,3,9,17,18,7,3,3]
看來功能實現上,沒問題。
讀入 numpy 和 Keras 的一些工具。
importnumpyasnpfromkeras.utilsimportto_categoricalfromkeras.preprocessing.sequenceimportpad_sequences
系統(tǒng)自動提示我們,Keras 使用了 Tensorflow 作為后端框架。
UsingTensorFlowbackend.
我們需要弄清楚,一共有多少種事件類型。
len(event_dict)
結果是:
32
因此,我們需要對32種不同的事件類型,進行轉換和處理。
我們把整個數據集里面的事件類型,都變成事件編號。
df.events.apply(map_event_list_to_idxs)
結果如下:
0[9,17,18,14,13,17,3,13,16,3,17,17,...1[2,10,3]2[2]3[2]4[2,2,2,2,2,2,2,9]5[3,2,17]6[3,2,17]7[2,15,2,17,2,2,13,17,2]8[17,2,2,16,17,2]9[17,2,2,16,17,2]10[17,16,17,2,17,3,17,17,16,17,16,18,...11[17]12[17]13[24,24]14[24,2,24,24,2]15[24,2,24,24,2]16[2,10,2,2,2,18,16,16,7,2,16,2,2,9...17[2,10,2,2,2,18,16,16,7,2,16,2,2,9...18[24,24,24,16,2,16]19[24,24,24,16,2,16]20[2,2]21[2,16,2]22[2,16,2]23[2,2]24[2,2]25[24,24]26[2,2]27[2,2,2,17]28[2,19,2]29[24]...831[9,9,9,2,9,9,17,2,9,17]832[3,3,3]833[2,9,2,17,17,2]834[3,3,17,3,13,3,3,23,9,3,3,25,3,3]835[3,17,9,14,9,17,14,9,2,9,3,2,2,17]836[2]837[17,2,16,3,9,17,17,17,13,17,9,17]838[13,17,17,3,3,16,17,16,17,16,3,9,1...839[2]840[3]841[2]842[17,17,17,3,17,23,16,17,17,3,2,13,...843[3,3]844[2]845[2,17,2,2,2,2,2,17,2,2]846[7,17,3,18,17]847[3,3,3]848[2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,...849[2,2]850[2,2,2,2,2,2,2,2,2,2,2,13,3,2]851[2,2,2]852[16,2,16]853[3,16,5,3,17,3,16,9,3,2,17]854[16]855[3,3,3,3,3,3,3,3,2,13,3,6,3,6,3,...856[17,17,17,2,3,2,2,2,2,2]857[2,2]858[2,2,9,17,2,2]859[17,3,2,2,2,2,2,2]860[17,3,3,17,3,17,2,3,18,14,3,3,16,...Name:events,Length:1722,dtype:object
現在,作為人類,我們確實是看不清楚,列表里面的事件都是什么了。好在計算機對于數字,更加喜聞樂見。
我們把該列表,起名為 sequences ,并且顯示前5項內容。
sequences=df.events.apply(map_event_list_to_idxs).tolist()sequences[:5]
下面是結果:
[[9,17,18,14,13,17,3,13,16,3,17,17,16,3,16,17,9,17,2,17,2,7,16,17,17,17,17,13,5,17,9,9,16,16,3],[2,10,3],[2],[2],[2,2,2,2,2,2,2,9]]
注意,第一行,明顯比后幾行都要長。
對于輸入序列,我們希望它的長度都是一樣的。因此,下面我們就用最長的序列長度作為標準,用 0 來填充其他短序列。
data=pad_sequences(sequences,maxlen=maxlen)data
這是結果:
array([[0,0,0,...,16,16,3],[0,0,0,...,2,10,3],[0,0,0,...,0,0,2],...,[0,0,0,...,17,2,2],[0,0,0,...,2,2,2],[0,0,0,...,3,3,2]],dtype=int32)
注意,所有的0,都補充到了序列的最前端。序列都一樣長了。
下面,我們把全部的分類標記,存儲到 labels 變量里面。
labels=np.array(df.label)
后面,我們有好幾個函數,需要用到隨機變量。
為了咱們運行結果的一致性。我這里指定隨機種子數值。你第一次嘗試運行的時候,不要動它。但是后面自己動手操作的時候,可以任意修改它。
np.random.seed(12)
好了,下面我們“洗牌”。打亂數據的順序,但是注意序列和對應標記之間,要保持一致性。
indices=np.arange(data.shape[0])np.random.shuffle(indices)data=data[indices]labels=labels[indices]
然后,我們取 80% 的數據,作為訓練;另外 20% 的數據,作為驗證。
training_samples=int(len(indices)*.8)validation_samples=len(indices)-training_samples
我們正式劃分訓練集和驗證集。
X_train=data[:training_samples]y_train=labels[:training_samples]X_valid=data[training_samples:training_samples+validation_samples]y_valid=labels[training_samples:training_samples+validation_samples]
看看訓練集的內容。
X_train
結果為:
array([[0,0,0,...,15,15,3],[0,0,0,...,0,2,2],[0,0,0,...,0,0,16],...,[0,0,0,...,2,15,16],[0,0,0,...,2,2,2],[0,0,0,...,0,0,2]],dtype=int32)
注意由于我們補充了“0”,作為填充,因此原先的32種事件類型的基礎上,又加了一種。
這就是我們新的事件類型數量:
num_events=len(event_dict)+1
我們使用嵌入層,把事件標號,轉換成一系列數字組成的向量。這樣,可以避免模型把事件序號,當成數值型數據來處理。
這里,我們指定每一個標號,轉換成 20 個數字組成的向量。
embedding_dim=20
利用事件類型數量,和事件向量長度,我們隨機構造初始的嵌入矩陣。
embedding_matrix=np.random.rand(num_events,embedding_dim)
下面我們搭建一個循環(huán)神經網絡模型。其中的 LSTM 層,包含了32位輸出數字。
fromkeras.modelsimportSequentialfromkeras.layersimportEmbedding,Flatten,Dense,LSTMunits=32model=Sequential()model.add(Embedding(num_events,embedding_dim))model.add(LSTM(units))model.add(Dense(1,activation='sigmoid'))
這里,我假設你已經看過了《如何用 Python 和循環(huán)神經網絡做中文文本分類?》一文,所以就不對細節(jié)進行講述了。如果你沒有看過,或者已經遺忘,可以點擊這個鏈接復習一下。
如果你對 Keras 的使用方法還不熟悉,我再次向你推薦 Fran?ois Chollet 的《Deep Learning with Python》。
下面,是處理其中的嵌入層參數。我們直接把剛才隨機生成的嵌入矩陣挪進來。而且,不讓模型在訓練中對嵌入層參數進行修改。
model.layers[0].set_weights([embedding_matrix])model.layers[0].trainable=False
下面,我們開始訓練。并且把模型運行結果保存起來。
model.compile(optimizer='rmsprop',loss='binary_crossentropy',metrics=['acc'])history=model.fit(X_train,y_train,epochs=50,batch_size=32,validation_data=(X_valid,y_valid))model.save("mymodel_embedding_untrainable.h5")
可以看到,因為有 TPU 的強力支持,程序在歡快地運行中。
訓練過程結束之后,我們利用 matplotlib 繪圖功能,看一下訓練中,準確率和損失值的變化。
importmatplotlib.pyplotaspltacc=history.history['acc']val_acc=history.history['val_acc']loss=history.history['loss']val_loss=history.history['val_loss']epochs=range(1,len(acc)+1)plt.plot(epochs,acc,'bo',label='Trainingacc')plt.plot(epochs,val_acc,'b',label='Validationacc')plt.title('Trainingandvalidationaccuracy')plt.legend()plt.figure()plt.plot(epochs,loss,'bo',label='Trainingloss')plt.plot(epochs,val_loss,'b',label='Validationloss')plt.title('Trainingandvalidationloss')plt.legend()plt.show()
這是準確率變化曲線。
可以看到,效果還是不錯的。因為我們數據中,不同標記各占一半。因此如果構建一個 dummy model 作為標準線的話,對所有的輸入都猜測0或者1,準確率應該只有50%。
這里的準確率,已經達到了65%-75%之間,證明我們的模型是有意義的。只不過,抖動比較厲害,穩(wěn)定性差。
這是損失值變化曲線。
這個圖看起來,就不是很美妙了。因為雖然訓練集上面的損失值一路下降,但是驗證集上,這個效果并不是很明顯,一直劇烈波動。
看到結果,不是最重要的。關鍵是我們得分析出目前遇到問題,原因是什么。
注意我們前面使用了嵌入矩陣。它隨機生成,卻又沒有真正進行訓練調整,這可能是個問題。
因此,我們這里再次構建和跑一下模型。唯一改動的地方,在于讓嵌入矩陣的參數也可以隨著訓練進行自動調整。
fromkeras.modelsimportSequentialfromkeras.layersimportEmbedding,Flatten,Dense,LSTMunits=32model=Sequential()model.add(Embedding(num_events,embedding_dim))model.add(LSTM(units))model.add(Dense(1,activation='sigmoid'))
注意這里的差別,就是trainable設置為真值。
model.layers[0].set_weights([embedding_matrix])model.layers[0].trainable=True
構建模型,再次運行。
model.compile(optimizer='rmsprop',loss='binary_crossentropy',metrics=['acc'])history=model.fit(X_train,y_train,epochs=50,batch_size=32,validation_data=(X_valid,y_valid))model.save("mymodel_embedding_trainable.h5")
繪圖看看。
importmatplotlib.pyplotaspltacc=history.history['acc']val_acc=history.history['val_acc']loss=history.history['loss']val_loss=history.history['val_loss']epochs=range(1,len(acc)+1)plt.plot(epochs,acc,'bo',label='Trainingacc')plt.plot(epochs,val_acc,'b',label='Validationacc')plt.title('Trainingandvalidationaccuracy')plt.legend()plt.figure()plt.plot(epochs,loss,'bo',label='Trainingloss')plt.plot(epochs,val_loss,'b',label='Validationloss')plt.title('Trainingandvalidationloss')plt.legend()plt.show()
這次的準確率曲線,看起來好多了。驗證集波動沒有這么劇烈,模型穩(wěn)定性好了許多。而且,準確率的取值,也獲得了提升。后半程穩(wěn)定在了75%以上。這樣的模型,就有應用價值了。
但是我們看看損失值曲線,可能就不這么樂觀了。
注意從半程之后,訓練集和驗證集的損失值變化,就發(fā)生了分叉。
這是典型的過擬合(over-fitting)。
發(fā)生過擬合,主要原因就是相對于復雜的模型,訓練數據不夠用。
這時候,要么增加訓練數據,要么降低模型復雜度。
立即增加數據,不太現實。因為我們手中,目前只有那29天里積攢的數據。但是降低模型復雜度,是可以利用 Dropout 來嘗試完成的。
Dropout 的實現機理,是在訓練的時候,每次隨機把一定比例的模型中神經元對應權重參數,設置為0,讓它不起作用。這樣,模型的復雜度,就會降低。
下面,我們輕微修改一下,在 LSTM 層上,加入dropout=0.2, recurrent_dropout=0.2這兩個參數。
fromkeras.modelsimportSequentialfromkeras.layersimportEmbedding,Flatten,Dense,LSTMunits=32model=Sequential()model.add(Embedding(num_events,embedding_dim))model.add(LSTM(units,dropout=0.2,recurrent_dropout=0.2))model.add(Dense(1,activation='sigmoid'))
依然保持嵌入層可以被訓練。
model.layers[0].set_weights([embedding_matrix])model.layers[0].trainable=True
再次運行。
model.compile(optimizer='rmsprop',loss='binary_crossentropy',metrics=['acc'])history=model.fit(X_train,y_train,epochs=50,batch_size=32,validation_data=(X_valid,y_valid))model.save("mymodel_embedding_trainable_with_dropout.h5")
繪制圖形的函數跟之前兩次完全一致。
importmatplotlib.pyplotaspltacc=history.history['acc']val_acc=history.history['val_acc']loss=history.history['loss']val_loss=history.history['val_loss']epochs=range(1,len(acc)+1)plt.plot(epochs,acc,'bo',label='Trainingacc')plt.plot(epochs,val_acc,'b',label='Validationacc')plt.title('Trainingandvalidationaccuracy')plt.legend()plt.figure()plt.plot(epochs,loss,'bo',label='Trainingloss')plt.plot(epochs,val_loss,'b',label='Validationloss')plt.title('Trainingandvalidationloss')plt.legend()plt.show()
這次的準確率曲線,看起來達到的數值,跟沒有加入 Dropout 的差不多。
然而,我們可以感受到訓練集和驗證集達到的準確率更加貼近。曲線更加平滑。
下面我們看看損失值曲線的變化。
這個曲線上,過擬合的去除效果就更為明顯了??梢钥吹接柧毤万炞C集兩條曲線的波動基本保持了一致。這樣我們更可以確信,模型預測能力是穩(wěn)定的,對外界新的輸入信息,適應性更好。
如果把咱們的模型放在交通管理部門那里,可以期望它根據 Waze 獲得的新序列數據,能以大約 75% 的準確率,預測嚴重交通擁堵的發(fā)生。這樣,交管部門就可以未雨綢繆,提前做出干預了。
用序列模型,欺負金融市場的散戶,屬于零和博弈。然而這種在交通管理上的應用,大概更能造福社會,體現科技的價值吧。
小結
通過本文的學習和實際上手操作,希望你已了解了以下知識點:
不只是文本,其他序列數據,也可以利用循環(huán)神經網絡來進行分類預測。
對定類數據(categorical data)進行嵌入表示,如果用隨機數初始,那么在建模過程中把嵌入層一起訓練,效果會更好。
數據量不夠的情況下,深度學習很可能會發(fā)生過擬合。使用 Dropout ,可以降低過擬合的影響,讓模型具有更好的穩(wěn)定性和可擴展性。
希望這篇文章,可以幫助你了解循環(huán)神經網絡的更多應用場景。在實際的工作和學習中,靈活運用它來處理序列數據的分類等任務。
-
神經網絡
+關注
關注
42文章
4814瀏覽量
103648 -
數據
+關注
關注
8文章
7256瀏覽量
91894 -
python
+關注
關注
56文章
4827瀏覽量
86773
原文標題:如何用Python和循環(huán)神經網絡預測嚴重交通擁堵?
文章出處:【微信號:rgznai100,微信公眾號:rgznai100】歡迎添加關注!文章轉載請注明出處。
發(fā)布評論請先 登錄
用matlab編程進行BP神經網絡預測時如何確定最合適的,BP模型
關于BP神經網絡預測模型的確定!!
如何構建神經網絡?
GABP神經網絡在交通流預測中的應用研究
改進人工蜂群算法優(yōu)化RBF神經網絡的短時交通流預測模型

結合小波變換的LSTM循環(huán)神經網絡的稅收預測

評論