業界 | 資料科學家“恐怖故事”

大資料文摘發表於2018-12-20

業界 | 資料科學家“恐怖故事”

大資料文摘出品

編譯:張秋玥、蔣寶尚

文字語音轉換圈內流傳這麼一則真假未知的故事:一個研究者花了數月(甚至數年)調整他/她的語音生成模型,使其語音樣本聽起來效果非常好。最後他們發現,他們從頭到尾都誤用同一語音檔案進行訓練,最終模型只是完全符合該語音檔案特徵所以才擁有如此流暢的語音樣本輸出。這個故事到現在都讓人不寒而慄。

想象一下另一則恐怖故事:你是個小實習生,老闆讓你搭建一個判斷識別“Yes”與“No”的語音識別分類器。你有這些音訊檔案:yes1.wav,no1.wav,yes2.wav,no2.wav,yes3.wav等等。你建好了分類器,效果也很好。就在你要展示工作成果之前,你發現這個模型唯一在做的事情就是通過讀取檔名裡的yes或者no來預測結果,壓根不會聽檔案裡面的音訊。你嚇傻了,大哭一場,準備跑路。

這就是本文作者Vincent Vanhoucke所經歷的恐怖故事,完全真實,這些小事故也決定了這位Google首席科學家的職業生涯。

以下是他以第一人稱講述的更多小故事,讓我們看看能夠從中得到哪些經驗:

那是我作為研究者的第一份工作。任務很明確,提供了大量資料以及優秀的預測準確度標準來評估模型效果。模型的基準結果很強,我最後甚至和一位客戶一起在生產實踐中部署了這個模型。

我有試圖根據我覺得很聰明很厲害的方法來改進模型表現指標——它沒有很完美但每一天都在進步。我都能看到我腦子裡慢慢形成的一篇優秀學術論文啦。生活真美好。

這算是一項產業研究,所以在開始撰寫論文之前我還需要通過最後一項測試:使用真實顧客資料來評估模型,以便於快速在生產實踐中部署改進方案。在真實資料集上我的模型達成了零精確度成就。我可是一直在提高我覺得超級厲害的表現指標來著。

業界 | 資料科學家“恐怖故事”

八成是出了bug,要不就是真實顧客資料質量很糟糕——我腦子這麼想著,覺得沒多大關係就急著開始上手寫論文了。但實際上我又並沒有辦法完全放下這個糟糕結果,所以我就開始研究到底是怎麼回事。我最後發現的是全世界資料科學家共同的噩夢:準確度就是零,這一點毫無疑問。我其他所有的準確度資料都是所謂的“幽靈”數字。我簡直不敢信:這些數字看起來超可信啊,它們比基準高但並沒有高到不可能的地步。

人們常說,災難一般不會“成單”出現,而是在有兩件事一起出錯時,因為我們總體來說很擅長預判並改正單個失誤。為了完全瞭解到底是什麼樣幾乎不可能發生的系列事件導致了這些看似可信的精確度數字的出現,我必須得從細節開始分析。

模型目標是改善用來識別人名的語法資料結構。比如說,假如你叫“Robert Moore”,語音識別系統可能將會把你的名字編譯成為一個語音圖,大致看起來像是某種正規表示式:“/(ˈɹɑb.əɹt|ˈbob|ˈɹɑb) mʊɹ/”——它還相容類似於“Rob”或“Bob”的暱稱呢。我的任務是生成更好的語音圖。我的資料被儲存為鍵值對資料庫的形式:


record {
key (string): “robert_moore”
value (Grammar): /(ˈɹɑb.əɹt|ˈbob|ˈɹɑb)mʊɹ/
}


這裡有一個bug:有些我的語法資料結構裡用到的語音符號並不會被髮音引擎識別。系統嘗試把語法資料結構編譯為一個應當代表正規表示式的影像物件,但它失敗了。在層層程式碼的深處,有人曾嘗試將系統變得對於這些失敗更加穩健:畢竟,只要可能,你永遠不希望系統在生產實踐中突然垮掉嘛。那段程式碼看起來類似於這樣:


Graph* graph = compile(record->value);
if (!graph) { // Failed to compile.
graph = compile(record->key); // (什麼鬼???)
}


這可真的讓我大吃一驚措手不及:怎麼會有人覺得只要一條資料庫記錄損壞了就代表這條記錄的鍵包含真正的負載?而且這怎麼可能可行嘛?“值”就是一條序列化的型語法,“鍵”就只是一串字元而已。再深挖一點——看,更“穩健”的在這裡:


Grammar* grammar = parse(record);
if (!grammar) { // Failed to parse.
grammar = parse(pronounce(record)); // (啥???)
}


如果資料不是我們預想的型別,我們就會盡量提取那條記錄的內容為單詞進行發音。為什麼不呢,反正已經毫無希望了嘛。而且,發音生成是一項非常耗時耗計算力的操作。想象一下,不管出於什麼原因,一大串沒有任何意義的垃圾字元(包括對於拒絕服務的報復性操作)突然被輸入到系統裡,這對於系統意味著什麼。系統將會立刻過載,而非“逐漸失敗”。

你可能已經意識到接下來要發生什麼了。我的資料的鍵都是使用者的真名,比如“robert_moore”。發音引擎很容易就將其近似於“/ˈɹɑb.əɹt mʊɹ/.”。所以,我的資料的問題直接來自於決定模型評估標準的事實。

理論上來說這就與我在前文提到的根據檔名預測音訊是yes還是no一個道理。我沒預料到的是,發音模型的隨機試驗看起來確實改善了結果。然而,那其實只是取決於每次實驗中未編譯成功的資料比例而已。我的模型失敗次數越多,生成的錯誤就更多,真實鍵值使用的更多,我的模型精確度就越好。至於解鎖零精確度成就的真實資料?那個資料庫裡的鍵都是亂七八糟的字串,看起來類似於“h4a7n6ks2l”這種發音模型?

我還算是幸運的。我對符號檢索問題進行的修復確實提高了效果,新系統確實得到了改進。數週的實驗最後都是竹籃打水一場空,我還得跟同事解釋我這個模型差點就上線執行害了所有客戶,以及為啥我越傷害這個模型線下精確度就越高。必須得說一句,他們最後只是大笑了一場這事兒就過去了,還是很客氣的。

下面是我學到的經驗教訓:

第一,不要相信任何人、任何事情。

誰都想抓住你的把柄,尤其是資料科學界。大多數問題會將預測結果變得看起來糟糕很多,但有時結果看起來還是足夠好且真實讓人無法起疑心。實際上在語言建模領域,這是一個超級常見的問題。計算以及比較困惑度階段超多陷阱,極小的錯誤都時常能夠提高實驗數字(而非降低!)。因此,這個領域的人對於證明的要求都很高;基於這個原因,我建議在將模型推廣之前你最好多在開源評估工具上試驗試驗。

第二,更不要相信你自己。

在我整個學術生涯中,我很快就學到了我需要過分質疑任何我得到的結果,儘管我本性並非如此。我現在會為了模型結果持續尋求外部意見,最好是使用一個完全不同的程式碼庫。

第三,寫簡單的防衛程式碼

不要自作聰明。你的程式碼應該跟你本人一樣偏執,就算是合同裡最小的細節有一點不符,你的模型都該立刻引人注目地垮掉。每個程式設計師都肯定經歷過這樣的事:閱讀堆疊跟蹤到一段標註為“這就永遠不該發生”的程式碼。大量資料被寫入磁碟,甚至位翻轉這種事情都時有發生。我以前有過一個生產系統,因為XML分析錯誤就直接崩潰了。磁碟上配置檔案(程式自動生成的)看起來就像這樣:

<item/>
<item/>
…類似的一百萬行…
<item/>
<itel/>
<item/>
<item/>
…再省略一百萬行…
<item/>

看到哪裡不同了嘛?我都等不及下一次日冕物質拋射活動來讓我們都變成更厲害的程式設計師啦(譯者注:這個作者只是在這裡發洩怨氣…)。

第四,不要相信你的程式碼,更不要相信你自己的資料處理能力。

想讓你的資料出錯,方法超多的。即使你只有1%的資料出錯,你的A/B測試結果可能也完全不對。比如,有些著名網路資料集裡的某些圖片就是無法被某些影像解析器讀取。如果你使用另一個解析器,或者你將這些圖片計入分母,最後的結果都會與別人不同。很長一段時間內,我都在評估結果內重複計入了某些測試影像,因此得到了壓根不正確卻看似很可信的數字結果。

第五,儘量故意擾亂你的實驗。

把標籤打亂,計算概率層面精確度。在1%的資料上進行訓練,確保你過度擬合。更好的方法是:把你的模型交給別人,讓他們自己上手使用。每個實驗室都有這麼一個永遠能第一時間掛掉你完美程式碼的人。盯住他們就對啦!

這種對結果的有益的懷疑論可能是我在博士與非博士之間發現的品質上的最大區別。我們博士都經受過這樣的打擊。回想起來,我很幸運能夠以這麼一種尷尬又沒有很不合適的方式在職業生涯早期收到驚訝,以確保我永遠都小心翼翼地進行研究。必須說不幸的是,資料科學界從來沒有“幸福小事故”這種事情(西方公眾名人Bob Ross名言:世上沒有“錯誤”一說,它們只是“幸福小事故”)。

相關報導:

https://medium.com/s/story/no-happy-little-accidents-8663540763f8

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31562039/viewspace-2285939/,如需轉載,請註明出處,否則將追究法律責任。

相關文章