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

結(jié)合小波變換的LSTM循環(huán)神經(jīng)網(wǎng)絡(luò)的稅收預(yù)測

評論