- 構建可靠的機器學習系統
- (美)凱茜·陳 (愛爾蘭)尼爾·理查德·墨菲 (美)克蘭蒂·帕里薩 (美)D.斯卡利 (美)托德·安德伍德
- 7141字
- 2025-06-26 18:00:40
2.3 數據的階段
大多數團隊會依靠他們所使用的平臺來提供他們需要的大部分功能,其中包括數據存儲和處理平臺。YarnIt不是一個大型組織,但我們仍然會讓負責業務管理、數據工程和運營的員工參與進來,幫助我們理解并滿足此處的要求。我們很幸運地擁有站點可靠性工程師,他們將解決與數據的存儲和處理有關的可靠性問題。
從根本上來說,數據管理階段是關于將我們所擁有的數據轉化為適合該過程之后階段的格式和存儲模式的。在這個過程中,我們也可能會應用一系列特定于模型(或至少是特定于模型領域)的數據轉換,以便為訓練準備數據。我們對數據的下一步操作是將其用于訓練機器學習模型,對某些敏感的數據內容進行匿名化處理,并在我們不再需要或被要求時刪除這些數據。為了準備對數據進行上述操作,我們將繼續從業務領導那里獲得他們的意見輸入,以回答關于數據的主要使用場景的問題,以及未來可能的探索領域。
與本書的大部分章節一樣,設計一個能夠運行且可靠的機器學習系統,深入了解機器學習并不是必需的,有時甚至是不值得的。然而,對模型訓練的基本理解確實直接告知我們在準備數據時要做什么。現代機器學習環境中的數據管理在將數據送入模型訓練管道之前包括多個階段,如圖2-2所示:
● 創建
● 提取
● 處理(包括驗證、清洗和豐富)
● 后期處理(包括數據管理、存儲和分析)

圖2-2:機器學習數據管理階段
2.3.1 創建
這可能是看起來要么很奇怪,要么很明顯的陳述,但機器學習訓練數據來自某處。也許你的數據集來自很多地方,比如另一個部門的同事或一個學術項目,并且是在那里創建的。但數據集都是在某個時間點通過某種過程創建的。這里的數據創建是指在一些數據存儲系統而不是我們的數據存儲系統中生成或捕獲數據的過程。例如,來自服務系統的日志、從一個事件中捕獲的大量圖像集合、來自醫療程序的診斷數據等。在這個過程中隱含的是,我們將希望設計新系統并調整現有系統以生成更多的數據,這樣我們的機器學習系統就有數據可用了。
有些數據集在靜態(或至少變化不頻繁)時效果很好,而其他數據集只有在頻繁更新時才有用。例如,一個照片識別數據集可能可以使用很多個月,只要它能準確地代表我們的模型想要識別的照片類型。另外,如果戶外的照片只代表溫帶氣候的冬季環境,那么照片集的分布將與我們需要識別的期望圖像集有很大的不同。因此,當這些地方的環境在春季變暖時,它將沒有用處。同樣,如果試圖自動識別yarnit.ai的交易中的欺詐行為,我們會希望在最近的交易中不斷訓練自己的模型,同時提供關于這些交易是否具有欺詐性的信息。否則,有人可能會想出難以檢測的欺詐方法偷走我們的所有編織用品,而我們可能永遠無法讓模型學會如何發現它。
我們收集的數據種類和創建的數據集可以是非結構化的、半結構化的,或者是結構化的(如圖2-3所示)。
結構化數據使用預定義的數據模型/模式進行量化,是高度組織化的,并且以表格格式(如電子表格或關系數據庫)存儲。姓名、地址、地理位置、日期和支付信息都是結構化數據的常見示例。由于結構化數據具有良好的格式,可以通過啟發法用相對簡單的代碼輕松處理。
另一方面,非結構化數據是定性的,沒有標準的數據模型或模式,所以它不能用傳統的數據方法和工具來處理和分析。非結構化數據的示例包括電子郵件正文、產品描述、網絡文本以及視頻和音頻文件。半結構化數據沒有特定的數據模型/模式,但包括標簽和語義標記,因此是介于結構化數據和非結構化數據之間的一種結構化數據類型。半結構化數據的示例包括電子郵件和社交媒體內容,前者可以按發件人、收件人、收件箱、已發送、草稿等進行檢索,而后者則可以被歸類為公共、私人和朋友,也可以從用戶維護的角度分類,如散列標簽。數據內部結構的特點對我們處理、存儲和使用數據的方式影響很大。

圖2-3:機器學習訓練數據的分類
盡管模型中的偏見來自模型的結構和數據,但數據創建的環境對正確性、公平性和道德也有深遠的影響。盡管我們會在第5章和第6章中對此進行更詳細的論述,但我們在此可以提出的主要建議是,你要有某種程序來確定你的模型是否有偏見。我們可以用許多方法來做這件事,最簡單的方法可能是模型卡(https://oreil.ly/h7E8h),然而無論用何種程序來做此事并且讓它在組織上被接受,都要比沒有這樣的程序好得多[4]。當我們開始在自己的機器學習系統中處理道德和公平性的問題時,這絕對是第一件要做的事。例如,將檢測偏見的工作結合到數據起源或數據生命周期會議或跟蹤過程中是相對容易的。但每個做機器學習的組織都應該建立這種程序,并將其作為持續改進的一部分來審查。
回顧一下,偏見來自許多方面,并且會在整個過程的很多階段顯現出來。沒有能夠完全保證數據公平性的方法。在這方面,成功的先決條件是一種有包容性的公司文化且公司內充滿了來自各種背景的人,他們有不同的和創造性的觀點。有充分的證據表明,具有非常不同的背景和觀點的人,在信任和尊重的環境中工作時,會產生比背景全都相似的團隊更好和更有用的想法。這可以成為針對經前述檢查后漏掉的各種偏見的有力防御的一部分。然而,在沒有任何流程和工具的幫助下阻止所有不好的結果,就像在沒有系統的幫助下,人類的努力不能阻止所有的壞結果一樣。
關于數據集的創建,或者說數據集的增加,還有最后一點需要注意:如果我們有少量的訓練數據,但不足以訓練出高質量的模型,那么可能需要增加數據。有些工具可以做到這一點。例如,Snorkel(https://www.snorkel.org/features)提供了一個程序化的接口,可以將少量的數據移植到更多的、變化更豐富的數據中,從本質上組成想象中的但統計上有效的訓練數據。這是一個很好的開始,因為它允許我們輕松地將一個小的數據集擴展成一個大的。盡管看起來這些通過程序創建的數據集在某種程度上不那么有價值或不那么有用,但存在十分有力的證據表明,這種方法可以以較低的成本產生十分出色的結果,即使這種方法確實需要謹慎使用[5]。
2.3.2 提取
數據需要被接收到系統中并寫入存儲,以便進一步處理。在這個階段,必定會對數據進行過濾和選擇。不是所有創建的數據都被提取或收集,因為可能其中某些數據我們不想要或不需要。
在這個階段,我們可以按不同類型過濾數據(我們認為對模型沒用的數據的字段或元素)。如果我們有太多的數據以至于我們沒有信心能負擔得起所有的數據處理,也可以在這個階段簡單地抽樣,因為機器學習訓練和其他數據處理的計算通常是非常昂貴的。對數據進行抽樣可以有效地節省中間處理和訓練的成本,但其中重要的是,要在抽樣導致的質量下降與所節省的成本之間進行權衡。數據的抽樣也應該與每個時間段或我們關心的數據中的每個其他切片的數量/速率成正比。這將避免在突發期錯過細節。然而,抽樣偶爾會丟失一些事件的細節,這是不可避免的。
通常來說,數據越多,機器學習訓練系統表現越好。雖然學究們會立即想到許多例外情況,但這是一個有用的出發點。因此,在降低成本的同時,任何數據的減少都可能對其質量產生影響。
根據數據量和服務的復雜性,提取階段可能是簡單的“在不同目錄中轉存一些文件”,或者是復雜的遠程過程調用(RPC)端點,接收特定格式的文件,并確認已收到相應的數據包,以便可以通過系統跟蹤其進展。在大多數情況下,我們希望至少有一個簡單的API可以用于數據提取,因為這提供了一個明顯的位置來確認數據的接收與存儲,記錄提取情況,并使用關于數據的任何治理策略。
提取階段的可靠性問題通常集中在數據的正確性和吞吐量上。正確性是一個通用屬性,指數據被正確地讀取和寫入正確的位置而不會被跳過或放錯位置。雖然數據放錯位置的想法聽起來很有趣,但它確實會發生,而且很容易看到它是如何發生的。存儲中以日期或時間為導向的分桶系統,再加上攝取過程中的偏差,可能會導致每天的數據都存儲在前一天的目錄中。在提取前和提取過程中監控數據是否存在及其存儲狀況是數據管道中最困難的部分。
2.3.3 處理
一旦我們成功地將數據加載(或提取)到一個有效的特征存儲系統中,大多數數據科學家或建模師會通過一系列常見的操作,使數據轉換為能夠進行訓練的數據。這些操作(驗證、清洗和確保數據一致性,以及豐富和擴展)將在接下來的內容中詳述。
驗證
無論我們的機器學習模型有多高效強大,它們都無法根據糟糕的數據完成預測。在生產中,數據出錯的一個常見原因是最開始收集數據的代碼存在錯誤。即使每個數據源都有一個定義好的模式,從外部提取的數據仍可能會有很多錯誤(例如,整型字段有一個浮點值)。因此,驗證傳入的數據是非常重要的,特別是當有一個確定模式或有能力與最后已知的有效數據進行比較時。
驗證是根據字段的通用定義來進行的——它是否是我們期望的那樣?為了進行這種驗證,我們需要預先存儲并能夠引用這些標準的定義。使用一個全面的元數據系統來管理字段的一致性并跟蹤定義,對于保持數據的準確性至關重要。這一話題將在第4章進行詳細介紹。
清洗和確保數據一致性
即使有一個全面的驗證框架,大多數數據仍然是混亂的。可能有缺失的字段、重復的數據以及錯誤的分類,甚至是編碼錯誤。我們擁有的數據越多,數據清洗與保持數據一致性就越有可能成為處理工作的重點。
這可能看起來很令人沮喪且沒有必要:建立整個系統只是為了檢查數據,對許多第一次這樣做的人來說,聽起來有點夸張了。但現實情況是,我們的機器學習管道肯定會有用于清洗數據的代碼。我們可以把這些代碼與其他代碼分隔開來,在那里它可以被審查并加以改進,或者把它的各個方面放在整個訓練管道中。第二種策略使管道變得非常脆弱,因為隨著對數據正確性的假設越來越多,我們確保滿足這些假設的能力卻沒有提升。此外,當我們改進一些用于驗證和改正數據的代碼時,我們可能忽略了在其他正在進行這項工作的地方實施這些改進。或者更糟的是,我們可能會適得其反。例如,我們可能多次“改正”相同的數據,導致數據中的原始信息被消除。也可能存在潛在的競態條件,即流程的不同部分會以不同的方式清洗數據或保證數據一致性。
在這一環節,另一組數據一致性的任務是數據的歸一化。歸一化一般指的是一組用于將輸入數據轉換為同樣尺度的技術,這對于像深度學習這樣依賴梯度下降或類似數值優化方法進行訓練的算法是非常有用的。關于歸一化的一些標準技術如圖2-4所示,主要包括:
縮放到一個范圍
將數據的所有X值映射到一個固定范圍,通常是0到1,但有時(對于像身高或年齡這樣的東西)會有其他值來表示常見的最大、最小值。
裁剪
除去數據的極端值。當數據集有少量的極端值時,這種方法很有效。
對數縮放
x'=log(x)。當數據符合冪次分布時,會有少量非常大的數值和大量非常小的數值,這種方法很有用。
Z值標準化
將變量映射為平均值的標準差的個數。
值得注意的是,如果這些技術中的任何取值范圍、分布或平均值是在一組測試數據上計算的,而這些數據的屬性與后來應用的數據集不同,那么這些方法會造成風險。
最后一個常見的相關技術是將數據放入儲存桶中:我們將一定范圍內的數據映射到一個更小的代表相同范圍的組中。例如,我們可以用年來衡量年齡,但在訓練時,我們可以把數據放在按10年分開的桶里,這樣,所有30到39歲的人都被放入“30歲”的桶里。但分桶可能是許多難以察覺的錯誤的來源。例如,想象一下,一個系統以10年為界限對年齡進行分類,另一個系統以5年為界限進行分類。當我們對數據進行分桶時,必須認真考慮將現有的數據保存,并將每條數據轉換為一個新的、格式正確的數據。如果(什么時候?)我們改變了分桶策略,這樣做會很棒;否則,就不能轉換。
豐富和擴展
在這個階段,我們會將自己的數據與其他來源的數據相結合。擴展數據的最常見和最基本的方式是打標簽。這個過程通過引入外部數據源(有時是人工參與)的信息來識別一個特定的事件或記錄。帶標簽的數據是所有監督式機器學習的關鍵驅動力,而且往往是整個機器學習過程中最具挑戰性和最昂貴的部分之一。如果沒有足夠數量的高質量標簽數據,監督學習就不會成功。(標簽和標簽系統將在第4章詳細介紹。)

圖2-4:“Google Developers Data Prep”課程中介紹的歸一化技術(https://oreil.ly/0cgBm)
但打標簽只是擴展數據的一種方式。我們可能會使用許多外部數據源來擴展我們的訓練數據。比方說,出于某種原因我們相信根據人們所在地的溫度能預測他們將買什么[6]。我們可以利用yarnit.ai網站上的搜索日志,添加用戶在訪問網頁時的大致地理位置的溫度。這可以通過創建或找一個記錄歷史溫度的服務或數據集來達成。這會使我們能夠將搜索所在地的溫度作為一個特征來訓練一個模型,看看我們能用它做出什么樣的預測。這有可能是一個糟糕的想法,但并不是完全不切實際的。
2.3.4 存儲
最后,我們需要將數據存儲在某個地方。我們如何以及在哪里存儲數據主要是由我們如何使用它來決定的,這實際上是一組關于訓練和服務系統的問題。我們在第4章對這個問題有更多的探討,而這里有兩個主要的問題:存儲的效率和元數據。
存儲系統的效率是由訪問模式決定的,而訪問模式受到模型結構、團隊結構和訓練過程的影響。為了對我們的存儲系統做出明智的決定,這里有一些我們需要回答的基本問題:
● 模型是對這些數據進行一次訓練還是多次訓練?
● 每個模型都將讀取所有數據還是只讀取部分數據?如果只讀取部分數據,被讀取的子集是由數據類型(哪些字段)選擇的,還是由隨機抽樣的數據(所有記錄的30%)選擇的?
● 特別是,相關團隊是否讀取了數據中略有不同的字段子集?
● 我們是否需要以任何特定的順序來讀取數據?
關于數據的重復使用問題,事實證明,幾乎所有的數據都會被多次讀取,存儲系統應該支持這種功能,即使模型所有者斷言他們只會對數據進行一次模型訓練。為什么呢?模型開發本質上是一個迭代的過程。一個機器學習工程師構建模型(讀取必要的數據),測量模型在其設計的任務中表現如何,然后部署它。接著他們會有另一個想法:一個關于如何改進模型的想法。很快,他們又重新讀取同樣的數據來嘗試他們的新想法。我們建立的每一個系統,從數據到訓練一直到服務,都應該假定模型開發者為了改進模型都會半持續地重新訓練相同的模型。事實上,當他們這樣做的時候,可能每次都會讀取不同的數據子集。
鑒于此,每個特征一列的面向列的存儲方案(每個特征占一列)是一種常見的設計架構,特別適用于對結構化數據進行訓練的模型[7]。大多數讀者都熟悉面向行的存儲模式,在這種存儲中,每次從數據庫中獲取數據都會檢索并匹配行中的所有字段。對于那些使用大部分或全部數據的應用程序集合,或者一些類似的程序集合來說,這是一個合適的架構。面向列的數據有利于只檢索字段的一個子集。這對每個使用特定數據子集的應用程序(本例中的機器學習訓練管道)的集合更有用。換句話說,面向列的數據存儲允許不同模型有效地讀取不同的特征子集,并且不用每次都讀入整行數據。我們在一個地方收集的數據越多,就越可能擁有使用不同數據子集的模型。
然而,這種方法對于某些訓練系統來說太復雜了。舉個例子,如果在大量未經預處理的圖像上進行訓練,我們其實并不需要有一個列式存儲系統,這意味著我們可以直接從一個目錄或一個桶中讀取圖像文件。然而,假設我們正在讀取一些更結構化的東西,比如交易數據日志,其中有時間戳、來源、推薦人、金額、項目、運輸方式和支付機制等字段。那么,假設一些模型會使用其中的一些特征,而另一些會使用其他的特征,這將促使我們使用列式存儲結構。
元數據幫助人類與存儲的數據進行互動。當多人在同一數據上建立模型時(或者當同一個人長期從事這項工作時),存儲特征的元數據會為他們提供巨大的幫助。它是理解之前的模型是如何構造出來,以及我們應當如何構造模型的路線圖。存儲系統的元數據是機器學習系統中常常被低估的部分之一。
本節會說明我們的數據管理系統主要是由兩個因素促成的:
我們打算將數據用于何種商業目的
我們要解決的是什么問題?這個問題對我們整個組織或客戶的價值是什么?
模型結構和策略
我們計劃構建什么模型?它們是如何組合的?它們多久更新一次?它們有多少個?它們之間的相似度如何?
我們對數據管理系統的每一個決定都受到這兩個因素的制約,同時也制約著這兩個因素。如果數據管理是關于我們如何以及為什么寫入數據,那么機器學習訓練管道是關于我們如何以及為什么讀取數據。
2.3.5 管理
通常,數據存儲系統實施基于憑證的訪問控制,用以限制未經授權的用戶訪問數據。這種簡單的技術只能為基本的機器學習實現服務。在更高級的場景中,特別是當數據包含機密信息時,我們需要有更細粒度的數據訪問管理方法。例如,我們可能希望模型開發人員只能訪問與他們工作直接相關的特征,或以其他方式限制他們對數據子集的訪問(也許只有最近的數據)。或者,我們可以在靜止狀態下或在訪問時對數據進行匿名處理或假名處理。最后,我們有時會允許生產工程師訪問所有的數據,但只有在證明他們在某次事件中需要這樣做,且由一個單獨的團隊仔細記錄和監控他們的訪問時才可以。(關于這方面的一些有趣話題將在第11章討論。)
站點可靠性工程師可以在生產中的存儲系統上配置數據訪問限制,允許數據科學家通過虛擬專用網絡(VPN)等授權網絡安全地讀取數據,并實施審計記錄以跟蹤哪些用戶和訓練任務在訪問哪些數據,生成報告,并監控使用模式。企業主或產品經理可以根據用例來定義用戶權限。我們可能需要為這些分布并不均勻的維度生成和使用不同種類的元數據,以最大限度地提高后續階段訪問和修改數據的能力。
2.3.6 分析與可視化
數據分析與可視化是使用統計或圖形技術以及工具將大量數據轉化為易于導航的展示形式的過程[8]。使數據不那么混亂且更容易獲取是機器學習架構和知識發現技術的一項基本任務。僅顯示一個餅圖或柱狀圖是不夠的。我們需要為讀者提供解釋,說明數據集中的每條記錄是什么意思,它與其他數據集中的記錄是如何聯系的,以及它是否干凈,是否可以安全地用于訓練模型。如果沒有定義明確且性能高的數據可視化工具和流程,數據科學家實際上是不可能直接查看大型數據集的。