測試驅動開發(Test-Driven Development, TDD)或是行為驅動開發(Behavioral-Driven Developement)是當代軟體工程裡面常見的方法論,然而以資料科學或是數據分析的專案來說,通常主要的「測試」會以模型的準確性或是資料分布的模式為核心,較少處理軟體工程裡面常見的單元測試(Unit Test)或是整合測試(Integration Test / Behavior Test),我一般跟案主在初期討論案子的時候也會先溝通這一點,通常在一開始導入資料科學技術的時候先不要花太多時間在軟體工程的測試上面,而是先以最小可行的方式驗證資料科學技術在原有商業模式上是否可以發揮價值,其優點是可以在最小的時間成本下儘速確認是否可以在既有的商業模式上透過資料技術產生正向的投資效益,然而缺點是隨著資料的增長與程式碼的增加,技術債(Technical Debt)的風險也會持續上升,因此我目前認為比較好的作法是在進行專案上架一到三個月已經產生初始的可驗證效益資料之後,立刻開始著手進行重構(Refactoring),事實上應該要說是重寫(Rewrite),因為沒有單元測試的程式碼是無法重構的。
一般來說,軟體專案的測試根據其目的可以分為以下幾類:
- 單元測試(Unit Test):著重於單一功能的測試,邏輯明確,是TDD的基礎。
- 探索性測試(Exploratory Test):在開始寫測試程式碼以前針對規格與功能面進行的試驗型測試。
- 整合測試(Intergration Test):著重於功能面,測試許多單元整合起來以後是否能符合預期運作。
- 效能測試(Performance Test):通常包含CPU使用、記憶體使用與延遲的測試。
- 使用者介面與使用者經驗測試(UI & UX Test):屬於前端的測試工作。
- 壓力測試(Pressure Test):著重於大流量情況下的測試,了解程式運作的硬體界限,以利未來進行擴張(Scale)的計畫準備。
- 滲透測試(Penetration Test):著重於資安防護的測試。
就機器學習的專案來說,通常會使用以下幾種測試:
- 交互驗證(Cross Validation):避免模型在訓練之後沒有產生過度適應(Overfitting)於訓練資料集的情況,通常使用RMSE(Root Mean Square Error)作為衡量的方式。
- 混淆矩陣(Confusion Matrix)與ROC Curve:比較常聽見的用法是偽陰性(False Negative)與偽陽性(False Positive)這種說法,詳細探討可自成一章,可參考其他人的文章。
- AB testing:確認模型在實際的應用場景當中能夠優於對照組,或是進行參數調整。
- 曲面圖(Surface Plot):通常透過擴大輸入輸出的範圍或是粒度,檢視模型是否有過度適應或是錯失全域最佳解的情況。
常見用於評估模型預測結果分布的曲面圖(surface plot),比較典型的其實會是有很多山峰與山谷的情形。
通常我在進行專案的時候會先以交互驗證為主,然後透過曲面圖去了解輸入輸出的侷限以及有沒有跟預期不符的情況,然後才會開始進行AB testing與confusion matrix,因為這兩個測試都要等到模型真正上線之後去擷取使用者的回應才能夠有效的判斷,以訓練資料集與交互驗證資料集做出來的數據意義比較有限。
之前因為在模型佈署的時候直接包成API給案主使用,因此效能測試也成為其中一個項目,我所使用的flask原始設定並不適合production環境使用,因此會需要做一定的調整才能符合一般線上服務在concurrency的要求,不像nodejs原生就有很好的非同步能力,這部份就有像是
locust這種開源的工具可以導入,未來有機會可能可以多探討一些以flask架構的服務應該如何調整以及如何作這方面的測試。
簡單的小結一下,以Google對員工撰寫python程式碼的code review標準而言,好的文件化跟好的測試都是相當重要的環節,然而在資料專案的部分測試與傳統的CI/CD流程相比,更多了一些偏向數據分布方面的需求,而非單純資料I/O的測試,如何妥善的將數據的測試流程整合到程式的工程測試流程與妥善的自動化測試與監控,也算是近年相當重要的題目之一,如果有興趣的話可以看看這篇關於據說是取代了資料科學家成為最性感職業的「
DataOps工程師」