據(jù)預(yù)處理實(shí)戰(zhàn):破解大數(shù)據(jù)項(xiàng)目效率瓶頸與數(shù)據(jù)質(zhì)量難題)
講一個(gè)大多數(shù)做過大數(shù)據(jù)項(xiàng)目的同行都有共鳴的場(chǎng)景項(xiàng)目啟動(dòng)會(huì)上算法組的同學(xué)信誓旦旦地說模型方案已經(jīng)驗(yàn)證過兩周內(nèi)可以出第一版效果。結(jié)果真正一開工大家才發(fā)現(xiàn)卡點(diǎn)根本不在模型而在數(shù)據(jù)預(yù)處理。業(yè)務(wù)系統(tǒng)的數(shù)據(jù)一進(jìn)來缺字段的、重復(fù)記錄的、單位不統(tǒng)一的、主鍵沖突的各種問題輪番轟炸。最后第一版模型拖了一個(gè)半月才跑通其中真正調(diào)參的時(shí)間不到一周。在行業(yè)里摸爬滾打這些年我越來越確認(rèn)一件事大數(shù)據(jù)領(lǐng)域的競(jìng)爭(zhēng)壁壘很多時(shí)候不是模型多先進(jìn)而是數(shù)據(jù)預(yù)處理做得有多扎實(shí)。這篇內(nèi)容算是我對(duì)數(shù)據(jù)預(yù)處理常見挑戰(zhàn)的一次系統(tǒng)復(fù)盤包括問題分類、排查思路、落地策略和一些從實(shí)際項(xiàng)目里總結(jié)的經(jīng)驗(yàn)適合剛轉(zhuǎn)入數(shù)據(jù)方向的工程師也適合正在被臟數(shù)據(jù)折磨的團(tuán)隊(duì)參考。1. 數(shù)據(jù)預(yù)處理為什么比建模更耗時(shí)間先理解這個(gè)反直覺的現(xiàn)象1.1 一個(gè)項(xiàng)目里數(shù)據(jù)準(zhǔn)備通常吃掉60%以上的排期有人統(tǒng)計(jì)過真實(shí)的數(shù)據(jù)分析項(xiàng)目里數(shù)據(jù)采集、清洗、轉(zhuǎn)換、校驗(yàn)這幾個(gè)環(huán)節(jié)加起來通常會(huì)占到整個(gè)項(xiàng)目周期的60%到80%。這不是夸張。我之前參與過某金融風(fēng)控方向的模擬項(xiàng)目X數(shù)據(jù)來源是多個(gè)渠道的信貸申請(qǐng)記錄覆蓋電商行為、運(yùn)營(yíng)商授權(quán)信息、歷史借貸記錄等。表面上看每個(gè)渠道的數(shù)據(jù)都有標(biāo)準(zhǔn)接口文檔也寫得齊全??烧娴铰?lián)調(diào)階段才發(fā)現(xiàn)同一個(gè)客戶在不同系統(tǒng)里的手機(jī)號(hào)格式不一樣一個(gè)帶區(qū)號(hào)一個(gè)不帶歷史借貸記錄的逾期字段不同渠道一個(gè)用數(shù)字0和1一個(gè)用字符串“Y”和“N”還有一批早年數(shù)據(jù)的時(shí)間戳竟然用的是13位毫秒級(jí)Unix時(shí)間而新系統(tǒng)給的是字符串日期。這些差異全部要靠數(shù)據(jù)預(yù)處理階段消化。建模環(huán)節(jié)反而不復(fù)雜就是常規(guī)的邏輯回歸加決策樹三天能跑完。但是為了讓這三天的建模能順利跑起來團(tuán)隊(duì)花了一個(gè)多月清理數(shù)據(jù)。這就是數(shù)據(jù)預(yù)處理在大數(shù)據(jù)項(xiàng)目中的真實(shí)地位。1.2 數(shù)據(jù)質(zhì)量直接決定模型上限這不是口號(hào)機(jī)器學(xué)習(xí)里有一句老話Garbage In, Garbage Out。數(shù)據(jù)質(zhì)量差再好的算法也救不回來。從數(shù)學(xué)角度看模型的性能上限受限于數(shù)據(jù)本身包含的信息量。如果預(yù)處理階段把關(guān)鍵字段的錯(cuò)誤值留著、把缺失值粗暴刪光、把有偏樣本當(dāng)成全量分布模型學(xué)到的規(guī)律大概率是錯(cuò)的。更隱蔽的問題是泄漏。特征里如果包含了未來信息或者預(yù)處理時(shí)用了全樣本統(tǒng)計(jì)量去填充缺失值離線評(píng)估時(shí)指標(biāo)會(huì)異常漂亮上線后立刻現(xiàn)原形。這類問題不發(fā)生在模型代碼里而發(fā)生在數(shù)據(jù)處理邏輯里排查起來特別麻煩。理解這一點(diǎn)再看團(tuán)隊(duì)里為什么大家愿意在數(shù)據(jù)預(yù)處理上花時(shí)間就順理成章了這不是流程冗長(zhǎng)而是數(shù)據(jù)工程的基本盤。1.3 看一個(gè)具體的時(shí)間賬我習(xí)慣在項(xiàng)目開始前先給團(tuán)隊(duì)算一筆時(shí)間賬數(shù)據(jù)接入與探查1周數(shù)據(jù)清洗規(guī)則開發(fā)2周數(shù)據(jù)質(zhì)量校驗(yàn)與修正1周特征工程屬于預(yù)處理的一部分2周模型訓(xùn)練與調(diào)優(yōu)1周結(jié)果驗(yàn)證與返工緩沖1周加起來八周建模只占八分之一。這不是某個(gè)團(tuán)隊(duì)的個(gè)例而是大數(shù)據(jù)項(xiàng)目的常態(tài)。承認(rèn)這一點(diǎn)之后團(tuán)隊(duì)心態(tài)會(huì)好很多不會(huì)盲目壓縮預(yù)處理時(shí)間換取一個(gè)注定不靠譜的“快速上線”。2. 數(shù)據(jù)質(zhì)量問題的完整分類從缺失值到數(shù)據(jù)傾斜數(shù)據(jù)預(yù)處理之所以難是因?yàn)椤芭K數(shù)據(jù)”并不是單一問題而是整整一個(gè)家族。我習(xí)慣把常見問題分成五類每一類下又有不同的變體。2.1 缺失值先搞懂缺失機(jī)制再?zèng)Q定處理方法缺失值是最常見的數(shù)據(jù)質(zhì)量問題但很多人的處理方式過于粗暴要么直接刪除含缺失的行要么全部用均值填充。這兩種方式在特定場(chǎng)景下都有問題。從統(tǒng)計(jì)角度缺失機(jī)制大致分三種完全隨機(jī)缺失MCAR缺失與任何變量無關(guān)比如錄入人員隨機(jī)漏填。這種情況刪行影響最小。隨機(jī)缺失MAR缺失與其他已觀測(cè)變量有關(guān)比如收入越高的用戶越不愿意填收入。如果直接刪行會(huì)引入選擇偏差。非隨機(jī)缺失MNAR缺失與缺失值本身有關(guān)比如收入極高的人故意不填收入。這種情況最麻煩任何簡(jiǎn)單填充都會(huì)帶來系統(tǒng)性偏誤。實(shí)操中很少有人做嚴(yán)謹(jǐn)?shù)娜笔C(jī)制檢驗(yàn)但至少要觀察一下“缺失行”和“非缺失行”在其他字段上的分布有沒有顯著差異。我經(jīng)手的某用戶畫像項(xiàng)目里缺失年齡的用戶在活躍度上的分布明顯不同于有年齡的用戶直接用全局均值填充年齡導(dǎo)致后續(xù)分箱特征完全失真。后來改用“按活躍度分層的條件填充”才把偏差控制住。處理策略上可以按優(yōu)先級(jí)排列能查源頭補(bǔ)的盡量回源數(shù)據(jù)系統(tǒng)補(bǔ)錄而不是在分析層猜。不能補(bǔ)的根據(jù)業(yè)務(wù)含義選擇填充或插值。時(shí)序數(shù)據(jù)用前后插值類別數(shù)據(jù)用眾數(shù)或單獨(dú)標(biāo)記為“未知”類。填充時(shí)注意不能引入未來信息尤其在時(shí)序場(chǎng)景只能用歷史窗口內(nèi)的統(tǒng)計(jì)量。2.2 重復(fù)數(shù)據(jù)精確去重好辦近似重復(fù)才考驗(yàn)功力重復(fù)數(shù)據(jù)在大數(shù)據(jù)場(chǎng)景里極其普遍尤其是多源數(shù)據(jù)集成時(shí)。同一個(gè)客戶在A系統(tǒng)里叫“張三”手機(jī)號(hào)138xxxx在B系統(tǒng)里叫“張先生”手機(jī)號(hào)138xxxx但郵箱不同。嚴(yán)格按主鍵去重根本去不掉這種記錄。精確重復(fù)可以通過對(duì)全字段哈希然后groupBy解決簡(jiǎn)單高效。但近似重復(fù)需要用到記錄鏈接的思想選擇關(guān)鍵字段做相似度計(jì)算比如編輯距離、Jaccard相似度、Soundex音標(biāo)匹配再設(shè)定閾值判斷是否為同一實(shí)體。這里有個(gè)性能現(xiàn)實(shí)兩兩比較的復(fù)雜度是O(n2)數(shù)據(jù)量大時(shí)扛不住。緩解辦法是分塊Blocking比如先按手機(jī)號(hào)前三位和姓氏拼音首字母分組只在組內(nèi)做兩兩比較復(fù)雜度大幅下降。某電商訂單數(shù)據(jù)模擬項(xiàng)目中我們用這個(gè)方法把上億條記錄的近似去重控制在小時(shí)級(jí)完成。2.3 異常值不是所有離群點(diǎn)都是需要清除的臟數(shù)據(jù)很多新人看到一根箱線圖上有離群點(diǎn)條件反射就想刪掉。但異常值可能來自三種完全不同的原因真實(shí)的數(shù)據(jù)波動(dòng)比如大促期間訂單量暴漲、觀測(cè)或錄入錯(cuò)誤比如年齡填成負(fù)數(shù)、系統(tǒng)故障比如傳感器讀數(shù)跳變。判斷該不該處理唯一可靠的標(biāo)準(zhǔn)是業(yè)務(wù)語(yǔ)義。3σ原則和IQR方法只是輔助工具不能替代業(yè)務(wù)判斷。運(yùn)營(yíng)活動(dòng)中突然飆升的流量不是錯(cuò)誤是信號(hào)經(jīng)常性業(yè)務(wù)里突然出現(xiàn)比中位數(shù)高100倍的金額才需要警惕是錯(cuò)誤。我的經(jīng)驗(yàn)是異常值處理分兩步走。第一步用統(tǒng)計(jì)方法圈出候選集第二步逐個(gè)結(jié)合上下文確認(rèn)。處理動(dòng)作可以是剔除、截?cái)郬insorize、單獨(dú)標(biāo)記成特征或者完全不處理取決于后續(xù)模型是否對(duì)這個(gè)字段敏感。2.4 數(shù)據(jù)傾斜分布式環(huán)境下特有的隱形殺手大數(shù)據(jù)處理用到分布式引擎時(shí)數(shù)據(jù)傾斜是繞不開的坑。表面癥狀是跑一個(gè)join或groupBy所有節(jié)點(diǎn)都完成了就卡在最后幾個(gè)任務(wù)上跑不動(dòng)。原因是某些key的數(shù)據(jù)量遠(yuǎn)超其他key導(dǎo)致少數(shù)節(jié)點(diǎn)負(fù)載過高。常見的傾斜場(chǎng)景和應(yīng)對(duì)方式groupBy傾斜先按key加鹽添加隨機(jī)后綴分兩次聚合第一次按加鹽后的key聚第二次去掉后綴再聚。join傾斜把小表廣播Broadcast到每個(gè)節(jié)點(diǎn)避免shuffle或者把大key單獨(dú)拆出來走廣播join??罩祪A斜空值會(huì)被聚到同一個(gè)key上處理時(shí)可以給空值加隨機(jī)前綴分散。某日志分析項(xiàng)目中線上日志里有個(gè)“來源渠道”字段渠道為空的值占了將近一半直接groupBy時(shí)所有空值都擠在同一節(jié)點(diǎn)。后來按“coalesce(渠道, 隨機(jī)值)”處理任務(wù)執(zhí)行時(shí)間從40分鐘降到11分鐘效果立竿見影。除了這四類數(shù)據(jù)質(zhì)量還包括一致性同一實(shí)體在不同系統(tǒng)的口徑差異、時(shí)效性數(shù)據(jù)延遲到達(dá)、完整性關(guān)鍵字段為空等維度。分類的意義在于處理手段不同排查路徑也不同。3. 三個(gè)最常見的“預(yù)處理翻車現(xiàn)場(chǎng)”與完整排查鏈路講完問題分類說一下我親眼見過、也親自排查過的三個(gè)翻車現(xiàn)場(chǎng)。希望這些描述能幫你建立一套“出了問題先往哪個(gè)方向想”的直覺。3.1 翻車現(xiàn)場(chǎng)一訓(xùn)練集指標(biāo)很好看上線后效果立刻崩盤某營(yíng)銷響應(yīng)模型的離線AUC做到0.82團(tuán)隊(duì)信心滿滿地上線結(jié)果真實(shí)點(diǎn)擊率比隨機(jī)略好。排查了兩周最后定位到預(yù)處理階段的缺陷。具體問題是缺失值填充時(shí)用了全量樣本的中位數(shù)來填充而全量樣本包含未來數(shù)據(jù)。在時(shí)間序列場(chǎng)景中t時(shí)刻做預(yù)測(cè)時(shí)根本無法知道t之后的分布。這屬于典型的數(shù)據(jù)泄漏。訓(xùn)練時(shí)看起來“填得很準(zhǔn)”但上線后預(yù)測(cè)分布和訓(xùn)練分布出現(xiàn)偏移效果自然崩。處理辦法所有統(tǒng)計(jì)類填充值都嚴(yán)格按時(shí)間窗口內(nèi)“過去”的數(shù)據(jù)計(jì)算保證訓(xùn)練、驗(yàn)證、上線三個(gè)環(huán)節(jié)使用同一套口徑。這也引申出一個(gè)通用原則訓(xùn)練和預(yù)測(cè)時(shí)的預(yù)處理邏輯必須完全一致最好封裝成同一個(gè)函數(shù)而不是訓(xùn)練一套代碼、上線再抄一遍。我把這個(gè)原則稱為“邏輯單一來源”。凡是發(fā)生過線上線下不一致的團(tuán)隊(duì)多半是兩套代碼并行維護(hù)導(dǎo)致的。3.2 翻車現(xiàn)場(chǎng)二新接入的數(shù)據(jù)源讓管道直接中斷某項(xiàng)目已經(jīng)穩(wěn)定跑了一個(gè)月某天ETL管道突然在深夜告警任務(wù)全部失敗。打開日志一看是某個(gè)新增字段format解析異常上游系統(tǒng)把日期從“2024-03-15”改成了“2024/3/15”解析函數(shù)不認(rèn)識(shí)新格式。這類問題在接入新數(shù)據(jù)源時(shí)特別常見根因往往不是代碼邏輯而是對(duì)上游schema變更沒有約束。排查鏈路如下先看失敗任務(wù)的日志定位到具體字段和解析函數(shù)。到上游系統(tǒng)的變更記錄里核對(duì)近期字段格式變化。發(fā)現(xiàn)是上游調(diào)整了導(dǎo)出格式但沒有同步通知下游。修復(fù)解析函數(shù)兼容兩種格式。更關(guān)鍵的是補(bǔ)上“schema變更監(jiān)控”對(duì)字段類型、枚舉值個(gè)數(shù)、日期格式做自動(dòng)檢查產(chǎn)生告警而不是直接中斷。后來我推動(dòng)團(tuán)隊(duì)做了一個(gè)簡(jiǎn)單策略每次管道跑批完成后自動(dòng)生成數(shù)據(jù)畫像摘要每字段空值率、類型分布、枚舉值列表與前一天對(duì)比。差異超過閾值就觸發(fā)告警。這能提前一天發(fā)現(xiàn)大多數(shù)上游變更問題。3.3 翻車現(xiàn)場(chǎng)三數(shù)據(jù)量漲了一個(gè)量級(jí)原來跑得動(dòng)的管道跑不動(dòng)了這是所有大數(shù)據(jù)團(tuán)隊(duì)的“幸福的煩惱”。某流量分析項(xiàng)目日數(shù)據(jù)量從每天2000萬條漲到2億條原先基于單機(jī)處理的方式直接失效加載數(shù)據(jù)要10分鐘處理要半小時(shí)時(shí)不時(shí)OOM。排查思路其實(shí)很清楚需要區(qū)分瓶頸在哪里如果是單機(jī)內(nèi)存受限考慮升級(jí)為分布式處理或改為增量計(jì)算。如果是重復(fù)全量掃描考慮建立分區(qū)、分桶策略減少掃描數(shù)據(jù)量。如果是計(jì)算邏輯本身有O(n2)復(fù)雜度比如全表兩兩匹配優(yōu)化算法或者用近似算法。該項(xiàng)目最終做了三件事把主干管道遷移到分布式批處理引擎按時(shí)間字段做分區(qū)每次只處理當(dāng)天增量對(duì)近似去重部分按前述分塊策略改寫。整體處理時(shí)間從40分鐘降到6分鐘還不再擔(dān)心內(nèi)存不夠。這背后有個(gè)通用原則預(yù)處理管道的設(shè)計(jì)要預(yù)留數(shù)據(jù)量增長(zhǎng)的空間一開始就別寫死在單機(jī)內(nèi)存里跑全量。4. 應(yīng)對(duì)策略的落地實(shí)踐規(guī)則、管道、工具三件套每次團(tuán)隊(duì)問我要一份“數(shù)據(jù)預(yù)處理最佳實(shí)踐”我給的答案都不是某個(gè)具體函數(shù)而是一套組合拳數(shù)據(jù)質(zhì)量規(guī)則做約束管道架構(gòu)做流程工具選型做承載。4.1 數(shù)據(jù)質(zhì)量規(guī)則從“發(fā)現(xiàn)臟數(shù)據(jù)”到“定義什么是臟”很多團(tuán)隊(duì)處理數(shù)據(jù)質(zhì)量是“消防式”的線上出問題才去修。更合理的做法是提前定義規(guī)則庫(kù)把“臟數(shù)據(jù)”的標(biāo)準(zhǔn)細(xì)化成可執(zhí)行的檢查項(xiàng)。我在實(shí)戰(zhàn)中常用六項(xiàng)檢查維度維度含義檢查示例完整性關(guān)鍵字段是否有空值用戶ID、訂單號(hào)不允許為空唯一性主鍵或業(yè)務(wù)鍵是否重復(fù)同一訂單編號(hào)只能出現(xiàn)一次有效性數(shù)據(jù)格式是否合法手機(jī)號(hào)必須是11位數(shù)字準(zhǔn)確性數(shù)值是否在合理范圍年齡區(qū)間(0, 120)一致性同一實(shí)體的字段口徑是否一致各系統(tǒng)客戶性別編碼要一致時(shí)效性數(shù)據(jù)是否及時(shí)可用業(yè)務(wù)日數(shù)據(jù)在T1天早上必須到位規(guī)則最好用聲明式配置管理而不是硬編碼在腳本里。一個(gè)示例配置片段rules: - name: check_order_id_not_null table: order_detail field: order_id rule_type: not_null severity: error - name: check_age_range table: member_info field: age rule_type: range min: 0 max: 120 severity: warning這樣數(shù)據(jù)團(tuán)隊(duì)可以隨業(yè)務(wù)變化快速增刪規(guī)則不需要重新發(fā)版。規(guī)則庫(kù)本身也是積累新人來了照著規(guī)則維護(hù)即可。4.2 預(yù)處理管道的分層設(shè)計(jì)每一層只干一件事我習(xí)慣把預(yù)處理管道切成五層職責(zé)清晰問題容易定位。接入層負(fù)責(zé)從不同數(shù)據(jù)源拉取數(shù)據(jù)統(tǒng)一格式生成原始數(shù)據(jù)快照。清洗層處理缺失、重復(fù)、異常值輸出干凈數(shù)據(jù)。轉(zhuǎn)換層做標(biāo)準(zhǔn)化、歸一化、離散化、編碼等特征變換。校驗(yàn)層跑數(shù)據(jù)質(zhì)量規(guī)則不符合的進(jìn)告警或回退流程。發(fā)布層把結(jié)果寫到特征庫(kù)或數(shù)據(jù)倉(cāng)庫(kù)供下游模型調(diào)度消費(fèi)。每一層之間通過存儲(chǔ)解耦比如清洗層輸出Parquet文件轉(zhuǎn)換層讀取后輸出特征寬表。這樣某一層掛了不會(huì)連累其他層重跑。特別是數(shù)據(jù)量大之后全鏈路重跑的成本很高分層后可以單獨(dú)重跑某一段。除了分層管道還應(yīng)該有“冪等性”同一份輸入不管跑多少遍結(jié)果一致。實(shí)現(xiàn)方式很簡(jiǎn)單寫結(jié)果時(shí)用覆蓋寫并記錄每批次的數(shù)據(jù)版本號(hào)。這樣就算半夜任務(wù)失敗重跑也不會(huì)產(chǎn)生重復(fù)數(shù)據(jù)。4.3 工具選型按數(shù)據(jù)規(guī)模和時(shí)效要求來不追求最潮預(yù)處理工具的選擇我見過太多團(tuán)隊(duì)踩的坑是“別人用什么我就用什么”。實(shí)際應(yīng)該按數(shù)據(jù)量和時(shí)效需求來單機(jī)、數(shù)據(jù)量在幾千萬行以內(nèi)、結(jié)構(gòu)靈活用內(nèi)存型數(shù)據(jù)分析庫(kù)最順手生態(tài)豐富適合探索和建模前的快速清洗。數(shù)據(jù)量過億、需要跑批調(diào)度用分布式批處理引擎穩(wěn)定、適合離線管道。要秒級(jí)或分鐘級(jí)延遲、數(shù)據(jù)持續(xù)流入用流式處理框架做窗口聚合和實(shí)時(shí)清洗。團(tuán)隊(duì)規(guī)模大、指標(biāo)口徑統(tǒng)一把輕量轉(zhuǎn)換邏輯用SQL管理在數(shù)倉(cāng)里數(shù)據(jù)團(tuán)隊(duì)維護(hù)起來負(fù)擔(dān)最小。我把常見選項(xiàng)整理成一張表供參考應(yīng)用場(chǎng)景代表工具適用規(guī)模主要局限探索式清洗單機(jī)DataFrame類庫(kù)單機(jī)內(nèi)存可承載數(shù)據(jù)量大或分布式環(huán)境不適用離線批處理分布式SQL引擎或Spark類框架海量離線數(shù)據(jù)任務(wù)調(diào)度和運(yùn)維成本稍高實(shí)時(shí)計(jì)算流處理框架流式數(shù)據(jù)、低延遲需求狀態(tài)管理和窗口調(diào)優(yōu)有門檻數(shù)倉(cāng)輕轉(zhuǎn)換SQL建模工具標(biāo)準(zhǔn)數(shù)倉(cāng)模型復(fù)雜清洗邏輯表達(dá)受限一個(gè)爛俗但正確的建議是能用SQL表達(dá)的清洗邏輯優(yōu)先用SQL因?yàn)樗烊宦暶魇?、易讀、好維護(hù)邏輯復(fù)雜到SQL寫起來很費(fèi)勁再下沉到編程語(yǔ)言處理。5. 從實(shí)戰(zhàn)中沉淀的經(jīng)驗(yàn)元數(shù)據(jù)、版本控制與自動(dòng)化測(cè)試最后這部分是三個(gè)我剛開始做數(shù)據(jù)項(xiàng)目時(shí)沒人提醒、后來吃了虧才補(bǔ)上的東西。它們不直接處理任何一條臟數(shù)據(jù)但決定整個(gè)預(yù)處理體系能不能長(zhǎng)期穩(wěn)定運(yùn)轉(zhuǎn)。5.1 元數(shù)據(jù)管理是預(yù)處理的“大腦”數(shù)據(jù)預(yù)處理做得久了你會(huì)發(fā)現(xiàn)很多問題不是“怎么處理”的問題而是“這個(gè)字段原先是什么意思”的問題。某次聯(lián)合建模業(yè)務(wù)方給了一個(gè)字段叫l(wèi)ast_login_interval直覺是“距離上次登錄的時(shí)間間隔”。結(jié)果上游系統(tǒng)定義的是“距今天數(shù)”而另一個(gè)數(shù)據(jù)源里同名含義是“距上次登錄的小時(shí)數(shù)”。如果沒有字段字典兩列一join計(jì)算結(jié)果完全錯(cuò)了。所以我強(qiáng)烈建議團(tuán)隊(duì)從第一天就維護(hù)字段級(jí)元數(shù)據(jù)包含字段名、業(yè)務(wù)含義、來源系統(tǒng)、類型、單位、枚舉值、更新頻率、負(fù)責(zé)人。不要等出了問題再補(bǔ)。元數(shù)據(jù)不只是給人看的更可以喂給校驗(yàn)規(guī)則自動(dòng)生成一部分檢查項(xiàng)。5.2 數(shù)據(jù)管道也要做測(cè)試尤其是回歸測(cè)試代碼有單測(cè)很多人卻從沒給數(shù)據(jù)管道寫過測(cè)試。結(jié)果就是某天你改了一個(gè)缺失值填充邏輯自我感覺沒問題卻導(dǎo)致下游特征分布劇烈變化模型效果波動(dòng)一周才發(fā)現(xiàn)。給數(shù)據(jù)管道做測(cè)試關(guān)鍵不是寫多少斷言而是建立“黃金數(shù)據(jù)集”。做法是挑一批固定的、有代表性的樣本數(shù)據(jù)手工核驗(yàn)清洗結(jié)果把人工判斷過的正確輸出作為黃金標(biāo)準(zhǔn)。以后每次改代碼把這個(gè)黃金數(shù)據(jù)集跑一遍比對(duì)輸出是否一致。不一致就說明改動(dòng)有影響。在此基礎(chǔ)上還可以做差分測(cè)試同一份數(shù)據(jù)新老代碼各跑一遍比較輸出分布的差異統(tǒng)計(jì)?;叶鹊臇|西未必是錯(cuò)的但值得人工確認(rèn)一遍。5.3 數(shù)據(jù)版本控制模型可復(fù)現(xiàn)的最后一道保險(xiǎn)模型上線后如果有人問三個(gè)星期前那版模型用的是什么特征版本數(shù)據(jù)是什么時(shí)候的快照如果團(tuán)隊(duì)沒有數(shù)據(jù)版本控制這個(gè)問題幾乎沒法回答。做法不難預(yù)處理最終產(chǎn)出的特征表每次寫入都打上批次號(hào)并記錄對(duì)應(yīng)的上游數(shù)據(jù)時(shí)間范圍、代碼版本、規(guī)則版本。訓(xùn)練模型時(shí)記錄用到的特征表版本號(hào)。這樣任何時(shí)間點(diǎn)的實(shí)驗(yàn)結(jié)果只要回溯版本就能完整復(fù)現(xiàn)。我們團(tuán)隊(duì)后來做了一個(gè)很輕的方案每次管道發(fā)布把關(guān)鍵配置文件和輸出數(shù)據(jù)清單存一份到版本庫(kù)命名規(guī)則是“業(yè)務(wù)名_日期_批次號(hào)”。成本極低收益極高在排查歷史效果異常時(shí)幾乎每次都用得上。5.4 一點(diǎn)額外的體會(huì)嵌入式工程師思維很重要數(shù)據(jù)預(yù)處理做久了我的一個(gè)強(qiáng)烈體會(huì)是這項(xiàng)工作非常像嵌入式開發(fā)——你面對(duì)的不是“理想輸入”而是各種不可控的現(xiàn)實(shí)信號(hào)。上游系統(tǒng)說改口就改口數(shù)據(jù)源的采集時(shí)間不穩(wěn)定同事對(duì)同一個(gè)字段的理解各不相同。預(yù)處理的本質(zhì)就是在這堆不確定的輸入里持續(xù)穩(wěn)定地輸出系統(tǒng)可信的數(shù)據(jù)。我見過優(yōu)秀的數(shù)據(jù)工程師基本都具備兩種特質(zhì)一是計(jì)較計(jì)較每一個(gè)字段的口徑、每一個(gè)單位的定義、每一個(gè)負(fù)數(shù)的來源二是敬畏敬畏數(shù)據(jù)的復(fù)雜性哪怕一個(gè)看似簡(jiǎn)單的“用戶ID”都可能藏著你沒見過的邊界情況。如果你正在搭建一套新的預(yù)處理流程我給的具體建議是從定義數(shù)據(jù)質(zhì)量規(guī)則開始而不是從寫清洗代碼開始。規(guī)則定清楚了代碼只是執(zhí)行規(guī)則的過程。規(guī)則沒定清楚代碼越寫越亂最后所有人都在“猜”數(shù)據(jù)應(yīng)該是什么樣的。數(shù)據(jù)預(yù)處理這份工作不會(huì)消失尤其在數(shù)據(jù)源越來越多、口徑越來越復(fù)雜的現(xiàn)實(shí)里它的重要性只會(huì)越來越高。把自己從“洗數(shù)據(jù)的”定位提升到“數(shù)據(jù)質(zhì)量的守門人”工作方式會(huì)完全不同產(chǎn)出的價(jià)值也會(huì)超出大多數(shù)人的預(yù)期。