本文為你介紹,如何從 Waze 交通事件開(kāi)放數(shù)據(jù)中,利用序列模型找到規(guī)律,進(jìn)行分類預(yù)測(cè)。以便相關(guān)部門可以未雨綢繆,提前有效干預(yù)可能發(fā)生的嚴(yán)重?fù)矶隆?/p>
尋找
之前在《文科生如何理解循環(huán)神經(jīng)網(wǎng)絡(luò)(RNN)?》一文中,我為你講解過(guò)循環(huán)神經(jīng)網(wǎng)絡(luò)的含義?!度绾斡?Python 和循環(huán)神經(jīng)網(wǎng)絡(luò)做中文文本分類?》一文,我又為你介紹了如何用循環(huán)神經(jīng)網(wǎng)絡(luò)對(duì)文本做分類。
我不希望給你一種錯(cuò)誤的簡(jiǎn)單關(guān)聯(lián),即“循環(huán)神經(jīng)網(wǎng)絡(luò)只能用來(lái)處理文本數(shù)據(jù)”。
事實(shí)上,只要是序列數(shù)據(jù),你都可以考慮一下循環(huán)神經(jīng)網(wǎng)絡(luò)。
我一直打算找個(gè)其他序列數(shù)據(jù)的樣例,給你展示循環(huán)神經(jīng)網(wǎng)絡(luò)的更多應(yīng)用場(chǎng)景。
但是這個(gè)數(shù)據(jù)不太好選擇。
目前一個(gè)熱門的應(yīng)用場(chǎng)景,就是金融產(chǎn)品的價(jià)格預(yù)測(cè)。
每時(shí)每秒,金融產(chǎn)品的價(jià)格都在變動(dòng)。把它匯集起來(lái),是個(gè)典型的序列數(shù)據(jù)。
但是我一直不看好這種應(yīng)用。因?yàn)榻鹑诋a(chǎn)品的定價(jià),應(yīng)該是面向未來(lái)的?;跉v史價(jià)格信息尋找波動(dòng)規(guī)律,并對(duì)未來(lái)價(jià)格進(jìn)行預(yù)測(cè),實(shí)際上如同看著后視鏡開(kāi)車一般危險(xiǎn)。
但是,還有很多人依然樂(lè)此不疲地嘗試。很多時(shí)候,他們也能嘗到成功的甜頭。
這是為什么?
原因在于,金融市場(chǎng)的參與者,并非理性的機(jī)器,而是由人組成的群體。從行為金融學(xué)的角度來(lái)看,進(jìn)化給人類思考與行為帶來(lái)了一些“快捷方式”,你可以利用它們從中漁利。
陸蓉教授的《行為金融學(xué)》欄目,對(duì)此有詳細(xì)介紹。
例如,人們追漲殺跌,認(rèn)為歷史會(huì)重演;
例如,吸引大眾關(guān)注到事件,總會(huì)帶來(lái)買入;
例如,人們會(huì)傾向于投資于自己熟悉的標(biāo)的;
例如,人們會(huì)購(gòu)買下跌的已持倉(cāng)標(biāo)的,來(lái)攤薄成本。
……
如果沒(méi)有大風(fēng)浪,這種對(duì)市場(chǎng)參與者行為規(guī)律的洞察,確實(shí)可以幫你賺錢。你可以從價(jià)格的歷史波動(dòng)中,挖掘出這些規(guī)律的影響。但是這對(duì)沒(méi)有模型可用的人來(lái)說(shuō),不公平。教你建模,就如同教你考試作弊。
如果遇到黑天鵝事件,其影響大概率會(huì)超過(guò)市場(chǎng)參與者行為偏誤帶來(lái)的歷史價(jià)格波動(dòng)規(guī)律。那么你,可能會(huì)因?yàn)閼?yīng)用模型,而遭遇虧損。你大約不會(huì)認(rèn)為這是自己的錯(cuò)誤,而直接把我當(dāng)做騙子,朝我扔雞蛋。
理性權(quán)衡后,我決定不用金融產(chǎn)品價(jià)格趨勢(shì)分析,作為循環(huán)神經(jīng)網(wǎng)絡(luò)的應(yīng)用樣例。
其他開(kāi)放的序列數(shù)據(jù),當(dāng)然也有很多。例如共享單車租用數(shù)據(jù)、氣溫變化數(shù)據(jù)等。
不過(guò)這些應(yīng)用,一來(lái)別人都寫過(guò)了,不新鮮。二來(lái),氣溫變化,你看天氣預(yù)報(bào)就好了。共享單車租用數(shù)量……你真的關(guān)心這里的規(guī)律嗎?
正在我猶豫的時(shí)候,一次偶然的機(jī)會(huì),我接觸到了一個(gè)新的序列數(shù)據(jù)樣例——交通事件數(shù)據(jù)。我覺(jué)得,把它作為應(yīng)用案例分享給你,可能更合適一些。
比賽
拿到這個(gè)數(shù)據(jù),是因?yàn)槲覅⑴c了一次編程馬拉松(hackathon)比賽。
比賽在 Frisco 的 UNT Inspire Park 舉辦。從早上8點(diǎn)開(kāi)始,一直到晚上9點(diǎn)多才結(jié)束。中間可以自由吃免費(fèi)提供的點(diǎn)心和水果,也可以到院子里曬曬太陽(yáng)放放風(fēng)。大家還可以自由交流和組隊(duì)。
主辦方為參賽者提供了若干種開(kāi)放數(shù)據(jù),也提了一些問(wèn)題供大家參考解答。當(dāng)然,實(shí)際參賽的時(shí)候,你也可以自己擬定新的題目。
這其中,就包括了 Waze 數(shù)據(jù)。
我在中國(guó)開(kāi)車,平時(shí)用的都是高德導(dǎo)航,對(duì)于 Waze 這款 App 不大熟悉。
簡(jiǎn)而言之,這個(gè) Waze 應(yīng)用除了提供一般的導(dǎo)航功能之外,還有一個(gè)類似于眾包的功能——讓司機(jī)們自由提交路況信息。
這樣一來(lái),Waze 就利用群體智慧形成了一個(gè)眼觀六路耳聽(tīng)八方的巨大網(wǎng)絡(luò),隨時(shí)依據(jù)用戶提供的情況,匯總成實(shí)時(shí)交通參考。并且匯報(bào)給用戶,以便于大家調(diào)整自己的行車路線。
我覺(jué)得最有用的特點(diǎn)是,在堵車的時(shí)候,你可以了解到前面究竟發(fā)生了什么。其他導(dǎo)航也有實(shí)時(shí)交通狀況提示,但是你對(duì)前面的情況一無(wú)所知。道路半幅施工?交通事故?
信息的對(duì)稱,可以在很大程度上,讓司機(jī)避免焦慮。
Waze 從幾年前開(kāi)始,就和政府部門合作,進(jìn)行數(shù)據(jù)開(kāi)放共享。這樣一來(lái),政府可以通過(guò) Waze 的數(shù)據(jù)了解交通實(shí)時(shí)狀況,對(duì)于問(wèn)題進(jìn)行快速的響應(yīng)處理;與此同時(shí), Waze 用戶也因?yàn)榭梢垣@取整合其他相關(guān)類型的政府開(kāi)放數(shù)據(jù)(例如道路規(guī)劃等),更加有效合理安排出行。
這次比賽,主辦方提供的數(shù)據(jù),是 DFW (達(dá)拉斯-沃斯堡都會(huì)區(qū))區(qū)域,11月1日到29日的 Waze 交通事件(Incidents)開(kāi)放數(shù)據(jù),這是政府開(kāi)放數(shù)據(jù)的一部分。這些數(shù)據(jù)基本都是來(lái)自于 Waze 用戶的提交。
原始的數(shù)據(jù),接近 300 MB。每一條事件信息,都包含了提交的經(jīng)緯度,以及時(shí)間。因此在探索性數(shù)據(jù)分析階段,我做了幾個(gè)可視化圖形。
這是我當(dāng)天跟新認(rèn)識(shí)的編程高手 Jesse 學(xué)的 QGIS 分析結(jié)果。
看看圖上的點(diǎn),每一個(gè)都對(duì)應(yīng)一次事件匯報(bào)。這叫一個(gè)密密麻麻啊。
因?yàn)?QGIS 初學(xué),用得不熟,我還是用 Python 進(jìn)行了分類繪圖。
這只是前 3000 條數(shù)據(jù)中部分類型的可視化。其中紅色代表交通擁堵,黃色代表事故發(fā)生,藍(lán)色代表有車停在了路肩上。
可以看到,紅色的數(shù)據(jù)量最大。這說(shuō)明交通擁堵是個(gè)大問(wèn)題。
我把全部的數(shù)據(jù)都拿了出來(lái),提煉出包含的事件類型,包括以下這些類:

我看到,其中單是交通阻塞,也是分為若干級(jí)別的。其中最嚴(yán)重的,分別是“大型交通擁堵”(large traffic jam)和“超大型交通擁堵”(huge traffic jam)。
于是,我把所有這兩種嚴(yán)重交通擁堵事件,合并成一個(gè)集合;其他剩余事件,作為另一個(gè)集合。
對(duì)于每一個(gè)嚴(yán)重?fù)矶率录易匪?0分鐘,把之前同一條道路上,發(fā)生的事件,按照順序存成一個(gè)列表。這樣的列表,有987個(gè);但是,其中有一些,是驟然發(fā)生的,30分鐘的區(qū)間里面,沒(méi)有任何其他事件作為先兆。這樣的空列表,我進(jìn)行了清除。剩下了861個(gè)有效序列。
同樣,從剩余事件集合中,我們隨機(jī)找到了861個(gè)非空有效序列。這些序列,后續(xù)緊隨事件,都不是嚴(yán)重?fù)矶隆?/p>
我們對(duì)嚴(yán)重?fù)矶轮?0分鐘的事件序列,標(biāo)記為1;對(duì)于非嚴(yán)重?fù)矶轮?0分鐘的事件序列,標(biāo)記為0。
于是,我們就把問(wèn)題轉(zhuǎn)換成了,能否利用事件序列,進(jìn)行分類,預(yù)測(cè)后續(xù)是否會(huì)發(fā)生嚴(yán)重?fù)矶隆?/p>
靠著這個(gè)模型,我們團(tuán)隊(duì)(UNT IIA lab代表隊(duì),其實(shí)不過(guò)就是我和春迎倆人,團(tuán)隊(duì)昵稱 watch-dumpling )在這次比賽中,獲得第一名。
這是 HackNTX 官網(wǎng)的報(bào)道(http://t.cn/EUbS9m5) 。
UNT 網(wǎng)站也正式發(fā)布了這則新聞(http://t.cn/EUbS127),于是我周圍盡人皆知。我才剛拿到手的獎(jiǎng)金,立即就因?yàn)檎?qǐng)客被掃蕩一空了。
奪冠純屬是個(gè)意外,幸運(yùn)占得比重很大。但是我覺(jué)得我們做的這個(gè)模型,還是有些應(yīng)用價(jià)值的。
下面,我就以這組 Waze 交通事件數(shù)據(jù),詳細(xì)給你講解一下,如何用 Python, Keras 和循環(huán)神經(jīng)網(wǎng)絡(luò),來(lái)實(shí)現(xiàn)這個(gè)序列數(shù)據(jù)分類模型。
環(huán)境
要運(yùn)行深度學(xué)習(xí),你需要有 GPU 或者 TPU 的支持,否則會(huì)累壞你的筆記本電腦的。Google Colab 是個(gè)不錯(cuò)的實(shí)驗(yàn)平臺(tái),可以讓你免費(fèi)使用 TPU 來(lái)進(jìn)行深度學(xué)習(xí)訓(xùn)練。你可以閱讀《如何免費(fèi)云端運(yùn)行Python深度學(xué)習(xí)框架?》一文,查詢更為詳細(xì)的介紹。
這里,請(qǐng)你使用 Chrome 瀏覽器,點(diǎn)擊這個(gè)鏈接,安裝一個(gè)插件 Colaboratory 。
把它添加到 Google Chrome 之后,你會(huì)在瀏覽器的擴(kuò)展工具欄里面,看見(jiàn)下圖中間的圖標(biāo):
然后,請(qǐng)到本范例的github repo 主頁(yè)面。
打開(kāi)其中的demo.ipynb文件。
點(diǎn)擊 Colaboratory 擴(kuò)展圖標(biāo)。Google Chrome 會(huì)自動(dòng)幫你開(kāi)啟 Google Colab,并且裝載這個(gè) ipynb 文件。
點(diǎn)擊上圖中紅色標(biāo)出的“復(fù)制到云端硬盤”按鈕。Google 會(huì)為你新建一個(gè)屬于你自己的副本。
點(diǎn)擊菜單欄里面的“代碼執(zhí)行程序”,選擇“更改運(yùn)行時(shí)類型”。
在出現(xiàn)的對(duì)話框中,確認(rèn)選項(xiàng)如下圖所示。
點(diǎn)擊“保存”即可。
下面,你就可以依次執(zhí)行每一個(gè)代碼段落了。
注意第一次執(zhí)行的時(shí)候,可能會(huì)有警告提示。

出現(xiàn)上面這個(gè)警告的時(shí)候,點(diǎn)擊“仍然運(yùn)行”就可以繼續(xù)了。
如果再次出現(xiàn)警告提示,反勾選“在運(yùn)行前充值所有代碼執(zhí)行程序”選項(xiàng),再次點(diǎn)擊“仍然運(yùn)行”即可。
環(huán)境準(zhǔn)備好了,下面我們來(lái)一步步運(yùn)行代碼。
代碼
首先,我們讀入 Pandas 軟件包,以便進(jìn)行結(jié)構(gòu)化數(shù)據(jù)的處理。
importpandasaspd
這次還要讀入的一個(gè)軟件包,是 Python 中間進(jìn)行數(shù)據(jù)存取的利器,叫做 pickle 。
importpickle
它可以把 Python 數(shù)據(jù),甚至是許多組數(shù)據(jù),一起存儲(chǔ)到指定文件。然后讀出的時(shí)候,可以完全恢復(fù)原先數(shù)據(jù)的格式。這一點(diǎn)上,它比用 csv 進(jìn)行數(shù)據(jù)存儲(chǔ)和交換的效果更好,效率也更高。
下面我們從本文配套的 github 項(xiàng)目中,把數(shù)據(jù)傳遞過(guò)來(lái)。
!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')
打開(kāi)數(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'}
同樣,我們來(lái)看看存儲(chǔ)事件序列的數(shù)據(jù)框。
先看前10個(gè):
df.head(10)

注意,每一行,都包含了標(biāo)記。
再看結(jié)尾部分:
df.tail(10)

讀取無(wú)誤。
下面我們來(lái)看看,最長(zhǎng)的一個(gè)序列,編號(hào)是多少。
這里,我們利用的是 Pandas 的一個(gè)函數(shù),叫做idxmax(),它可以幫助我們,把最大值對(duì)應(yīng)的索引編號(hào),傳遞回來(lái)。
max_len_event_id=df.events.apply(len).idxmax()max_len_event_id
結(jié)果為:
105
我們來(lái)看看,這個(gè)編號(hào)對(duì)應(yīng)的事件序列,是什么樣子的:
max_len_event=df.iloc[max_len_event_id]max_len_event.events
下面是長(zhǎng)長(zhǎng)的反饋結(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']
讀一遍,你就會(huì)發(fā)現(xiàn),在超級(jí)擁堵發(fā)生之前,確實(shí)還是有一些先兆的。當(dāng)然,這是由人來(lái)閱讀后,獲得的觀感。我們下面需要做的,是讓機(jī)器自動(dòng)把握這些列表的特征,并且做出區(qū)別分類。
我們看看,這個(gè)最長(zhǎng)列表的長(zhǎng)度。
maxlen=len(max_len_event.events)maxlen
結(jié)果為:
84
這里的前導(dǎo)事件,還真是不少啊。
下面我們要做的,是把事件轉(zhuǎn)換成數(shù)字編號(hào),這樣后面更容易處理。
我們使用以下的一個(gè)小技巧,把原先的事件詞典倒置,即變“序號(hào):事件名稱”,為“事件名稱:序號(hào)”。這樣,以事件名稱查詢起來(lái),效率會(huì)高很多。
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}
成功了。
下面我們編寫一個(gè)函數(shù),輸入一個(gè)事件列表,返回對(duì)應(yīng)的事件編號(hào)列表。
defmap_event_list_to_idxs(event_list):list_idxs=[]foreventin(event_list):idx=reversed_dict[event]list_idxs.append(idx)returnlist_idxs
然后,我們?cè)趧偛攀钦业降淖铋L(zhǎng)列表上,實(shí)驗(yàn)一下:
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]
看來(lái)功能實(shí)現(xiàn)上,沒(méi)問(wèn)題。
讀入 numpy 和 Keras 的一些工具。
importnumpyasnpfromkeras.utilsimportto_categoricalfromkeras.preprocessing.sequenceimportpad_sequences
系統(tǒng)自動(dòng)提示我們,Keras 使用了 Tensorflow 作為后端框架。
UsingTensorFlowbackend.
我們需要弄清楚,一共有多少種事件類型。
len(event_dict)
結(jié)果是:
32
因此,我們需要對(duì)32種不同的事件類型,進(jìn)行轉(zhuǎn)換和處理。
我們把整個(gè)數(shù)據(jù)集里面的事件類型,都變成事件編號(hào)。
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í)是看不清楚,列表里面的事件都是什么了。好在計(jì)算機(jī)對(duì)于數(shù)字,更加喜聞樂(lè)見(jiàn)。
我們把該列表,起名為 sequences ,并且顯示前5項(xiàng)內(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]]
注意,第一行,明顯比后幾行都要長(zhǎng)。
對(duì)于輸入序列,我們希望它的長(zhǎng)度都是一樣的。因此,下面我們就用最長(zhǎng)的序列長(zhǎng)度作為標(biāo)準(zhǔn),用 0 來(lái)填充其他短序列。
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,都補(bǔ)充到了序列的最前端。序列都一樣長(zhǎng)了。
下面,我們把全部的分類標(biāo)記,存儲(chǔ)到 labels 變量里面。
labels=np.array(df.label)
后面,我們有好幾個(gè)函數(shù),需要用到隨機(jī)變量。
為了咱們運(yùn)行結(jié)果的一致性。我這里指定隨機(jī)種子數(shù)值。你第一次嘗試運(yùn)行的時(shí)候,不要?jiǎng)铀?。但是后面自己?dòng)手操作的時(shí)候,可以任意修改它。
np.random.seed(12)
好了,下面我們“洗牌”。打亂數(shù)據(jù)的順序,但是注意序列和對(duì)應(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ù),作為驗(yàn)證。
training_samples=int(len(indices)*.8)validation_samples=len(indices)-training_samples
我們正式劃分訓(xùn)練集和驗(yà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)
注意由于我們補(bǔ)充了“0”,作為填充,因此原先的32種事件類型的基礎(chǔ)上,又加了一種。
這就是我們新的事件類型數(shù)量:
num_events=len(event_dict)+1
我們使用嵌入層,把事件標(biāo)號(hào),轉(zhuǎn)換成一系列數(shù)字組成的向量。這樣,可以避免模型把事件序號(hào),當(dāng)成數(shù)值型數(shù)據(jù)來(lái)處理。
這里,我們指定每一個(gè)標(biāo)號(hào),轉(zhuǎn)換成 20 個(gè)數(shù)字組成的向量。
embedding_dim=20
利用事件類型數(shù)量,和事件向量長(zhǎng)度,我們隨機(jī)構(gòu)造初始的嵌入矩陣。
embedding_matrix=np.random.rand(num_events,embedding_dim)
下面我們搭建一個(gè)循環(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)看過(guò)了《如何用 Python 和循環(huán)神經(jīng)網(wǎng)絡(luò)做中文文本分類?》一文,所以就不對(duì)細(xì)節(jié)進(jìn)行講述了。如果你沒(méi)有看過(guò),或者已經(jīng)遺忘,可以點(diǎn)擊這個(gè)鏈接復(fù)習(xí)一下。
如果你對(duì) Keras 的使用方法還不熟悉,我再次向你推薦 Fran?ois Chollet 的《Deep Learning with Python》。
下面,是處理其中的嵌入層參數(shù)。我們直接把剛才隨機(jī)生成的嵌入矩陣挪進(jìn)來(lái)。而且,不讓模型在訓(xùn)練中對(duì)嵌入層參數(shù)進(jìn)行修改。
model.layers[0].set_weights([embedding_matrix])model.layers[0].trainable=False
下面,我們開(kāi)始訓(xùn)練。并且把模型運(yùn)行結(jié)果保存起來(lái)。
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")
可以看到,因?yàn)橛?TPU 的強(qiáng)力支持,程序在歡快地運(yùn)行中。

訓(xùn)練過(guò)程結(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)確率變化曲線。
可以看到,效果還是不錯(cuò)的。因?yàn)槲覀償?shù)據(jù)中,不同標(biāo)記各占一半。因此如果構(gòu)建一個(gè) dummy model 作為標(biāo)準(zhǔn)線的話,對(duì)所有的輸入都猜測(cè)0或者1,準(zhǔn)確率應(yīng)該只有50%。
這里的準(zhǔn)確率,已經(jīng)達(dá)到了65%-75%之間,證明我們的模型是有意義的。只不過(guò),抖動(dòng)比較厲害,穩(wěn)定性差。
這是損失值變化曲線。
這個(gè)圖看起來(lái),就不是很美妙了。因?yàn)殡m然訓(xùn)練集上面的損失值一路下降,但是驗(yàn)證集上,這個(gè)效果并不是很明顯,一直劇烈波動(dòng)。
看到結(jié)果,不是最重要的。關(guān)鍵是我們得分析出目前遇到問(wèn)題,原因是什么。
注意我們前面使用了嵌入矩陣。它隨機(jī)生成,卻又沒(méi)有真正進(jìn)行訓(xùn)練調(diào)整,這可能是個(gè)問(wèn)題。
因此,我們這里再次構(gòu)建和跑一下模型。唯一改動(dòng)的地方,在于讓嵌入矩陣的參數(shù)也可以隨著訓(xùn)練進(jìn)行自動(dòng)調(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)建模型,再次運(yùn)行。
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)確率曲線,看起來(lái)好多了。驗(yàn)證集波動(dòng)沒(méi)有這么劇烈,模型穩(wěn)定性好了許多。而且,準(zhǔn)確率的取值,也獲得了提升。后半程穩(wěn)定在了75%以上。這樣的模型,就有應(yīng)用價(jià)值了。
但是我們看看損失值曲線,可能就不這么樂(lè)觀了。
注意從半程之后,訓(xùn)練集和驗(yàn)證集的損失值變化,就發(fā)生了分叉。
這是典型的過(guò)擬合(over-fitting)。
發(fā)生過(guò)擬合,主要原因就是相對(duì)于復(fù)雜的模型,訓(xùn)練數(shù)據(jù)不夠用。
這時(shí)候,要么增加訓(xùn)練數(shù)據(jù),要么降低模型復(fù)雜度。
立即增加數(shù)據(jù),不太現(xiàn)實(shí)。因?yàn)槲覀兪种校壳爸挥心?9天里積攢的數(shù)據(jù)。但是降低模型復(fù)雜度,是可以利用 Dropout 來(lái)嘗試完成的。
Dropout 的實(shí)現(xiàn)機(jī)理,是在訓(xùn)練的時(shí)候,每次隨機(jī)把一定比例的模型中神經(jīng)元對(duì)應(yīng)權(quán)重參數(shù),設(shè)置為0,讓它不起作用。這樣,模型的復(fù)雜度,就會(huì)降低。
下面,我們輕微修改一下,在 LSTM 層上,加入dropout=0.2, recurrent_dropout=0.2這兩個(gè)參數(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
再次運(yùn)行。
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)確率曲線,看起來(lái)達(dá)到的數(shù)值,跟沒(méi)有加入 Dropout 的差不多。
然而,我們可以感受到訓(xùn)練集和驗(yàn)證集達(dá)到的準(zhǔn)確率更加貼近。曲線更加平滑。
下面我們看看損失值曲線的變化。
這個(gè)曲線上,過(guò)擬合的去除效果就更為明顯了??梢钥吹接?xùn)練集和驗(yàn)證集兩條曲線的波動(dòng)基本保持了一致。這樣我們更可以確信,模型預(yù)測(cè)能力是穩(wěn)定的,對(duì)外界新的輸入信息,適應(yīng)性更好。
如果把咱們的模型放在交通管理部門那里,可以期望它根據(jù) Waze 獲得的新序列數(shù)據(jù),能以大約 75% 的準(zhǔn)確率,預(yù)測(cè)嚴(yán)重交通擁堵的發(fā)生。這樣,交管部門就可以未雨綢繆,提前做出干預(yù)了。
用序列模型,欺負(fù)金融市場(chǎng)的散戶,屬于零和博弈。然而這種在交通管理上的應(yīng)用,大概更能造福社會(huì),體現(xiàn)科技的價(jià)值吧。
小結(jié)
通過(guò)本文的學(xué)習(xí)和實(shí)際上手操作,希望你已了解了以下知識(shí)點(diǎn):
不只是文本,其他序列數(shù)據(jù),也可以利用循環(huán)神經(jīng)網(wǎng)絡(luò)來(lái)進(jìn)行分類預(yù)測(cè)。
對(duì)定類數(shù)據(jù)(categorical data)進(jìn)行嵌入表示,如果用隨機(jī)數(shù)初始,那么在建模過(guò)程中把嵌入層一起訓(xùn)練,效果會(huì)更好。
數(shù)據(jù)量不夠的情況下,深度學(xué)習(xí)很可能會(huì)發(fā)生過(guò)擬合。使用 Dropout ,可以降低過(guò)擬合的影響,讓模型具有更好的穩(wěn)定性和可擴(kuò)展性。
希望這篇文章,可以幫助你了解循環(huán)神經(jīng)網(wǎng)絡(luò)的更多應(yīng)用場(chǎng)景。在實(shí)際的工作和學(xué)習(xí)中,靈活運(yùn)用它來(lái)處理序列數(shù)據(jù)的分類等任務(wù)。
-
神經(jīng)網(wǎng)絡(luò)
+關(guān)注
關(guān)注
42文章
4832瀏覽量
107370 -
數(shù)據(jù)
+關(guān)注
關(guān)注
8文章
7328瀏覽量
94410 -
python
+關(guān)注
關(guān)注
57文章
4867瀏覽量
89845
原文標(biāo)題:如何用Python和循環(huán)神經(jīng)網(wǎng)絡(luò)預(yù)測(cè)嚴(yán)重交通擁堵?
文章出處:【微信號(hào):rgznai100,微信公眾號(hào):rgznai100】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
有提供編寫神經(jīng)網(wǎng)絡(luò)預(yù)測(cè)程序服務(wù)的嗎?
用matlab編程進(jìn)行BP神經(jīng)網(wǎng)絡(luò)預(yù)測(cè)時(shí)如何確定最合適的,BP模型
關(guān)于BP神經(jīng)網(wǎng)絡(luò)預(yù)測(cè)模型的確定!!
如何構(gòu)建神經(jīng)網(wǎng)絡(luò)?
GABP神經(jīng)網(wǎng)絡(luò)在交通流預(yù)測(cè)中的應(yīng)用研究
改進(jìn)人工蜂群算法優(yōu)化RBF神經(jīng)網(wǎng)絡(luò)的短時(shí)交通流預(yù)測(cè)模型
結(jié)合小波變換的LSTM循環(huán)神經(jīng)網(wǎng)絡(luò)的稅收預(yù)測(cè)
如何用Python和循環(huán)神經(jīng)網(wǎng)絡(luò)預(yù)測(cè)嚴(yán)重交通擁堵?
評(píng)論