作者:Marifel
編譯:ronghuaiyang
導讀
這篇文章介紹了爲生産系統構建機器學習過程的很多方面的內容,都是從實踐中總結出來的。
隨著數據和計算能力的崛起,“機器學習”(ML)和“深度學習”(deep learning)這兩個術語已經熱議了好幾年。雖然追隨ML的潮流似乎很酷,但公司的第一步是評估業務是否真的能從中受益 —— 這是一個獨立的職位。既然你的公司已經決定ML是一個必要的下一步,那麽作爲一個ML工程師,現在是時候考慮爲生産系統構建ML過程的真正內容了。希望這篇文章能幫助你理解這些問題。
在這篇文章中,只要提到“初創”這個詞,就意味著軟件“産品”公司,除非特別提到軟件“服務”公司。軟件産品公司專注于構建自己的軟件産品,而軟件服務公司(如代理或咨詢公司)則爲客戶構建軟件。雖然這篇文章是爲早期軟件産品初創公司的ML工程師寫的,但其中的一些考慮可能仍然適用于其他階段或公司類型。
找到合適的工具
有多個機器學習軟件可供選擇,從開源工具如PyTorch, TensorFlow以及scikit-learn到管理機器學習服務的平台如,Google AI平台,亞馬遜SageMaker和Azure機器學習平台。僅使用PyTorch和TensorFlow,就有許多像Hugging Face的transformer這樣的開源庫,它們提供了現成的模型作爲起點。此外,ML在不同領域的研究論文每年都會發表,其中一些提供了開源代碼。Papers With Code:https://paperswithcode.com/是查找帶代碼的論文的好資源。
在選擇合適的工具時要考慮的關鍵因素有:
- 文檔的質量
- 工具的開發狀態(maintained vs. halted or deprecated,問題的嚴重程度,等等)
- 圍繞該工具的其他工具的生態系統
- 開發人員社區對這個工具的參與是否積極
- 你對該工具的熟悉程度
- 使用該工具的團隊規模
- 與工具有關的貨幣成本
就我個人而言,我覺得如果你是一家處于早期階段的初創公司,你不需要權衡和找出所有這些因素。你總是可以從一個強大的候選工具開始,然後從那裏開始。此外,如果你認爲收益大于成本,則可以從一開始就使用托管ML服務。
何時開始做機器學習 vs. 機器學習周圍的操作
在第一次開始時,最好從一個簡單的基線模型開始。從更簡單的模型開始可以幫助你調試pipeline中的問題,並幫助你確定更耗時的解決方案是否值得。那麽如何建立一個簡單的基線模型呢?
首先,“簡單”是相對的。在某些情況下,簡單實際上意味著一個簡單的模型,例如硬編碼一些啓發式。而在其他情況下,模型本身可能很複雜,但是很容易應用。一些最廣泛使用的數據集擁有最先進的模型,這些模型是開源的,列在研究論文或排行榜等地方,斯坦福問答數據集(SQuAD)就有這樣一個排行榜。一種方法是查看一些頂級的解決方案,看看你是否能找到附加到相關研究論文的代碼。
在早期啓動階段,你可能沒有時間立即構建ML流程。通常情況下,你需要專注于讓你的投資者和客戶很容易就能看到的東西運行起來。調優過程很少出現在他們的腦海中。所以不要擔心你最初的部署是否完美,只需要有一個工作成果 —— 一個看得見的最終産品。在基本産品構建之後,你可以更多地擔心ML周圍的流程,因爲通常需要更多的停機時間來對ML流程進行小的、漸進的改進。
相反,如果你是一個代理商,出錯的空間就更小,因爲你要將完成的産品交付給不同的客戶,並試圖事先修複所有的bug。在交付一個或一組客戶端産品之後,你將轉移到下一個客戶端合同上,並且通常沒有足夠的精力來進行進一步的改進。盡管如此,你還是要迅速行動。爲了進展得更快,最好有更精細的ML過程。因此,對于代理商模型,從長遠來看,也許在優化和自動化上預先花費更多的時間可以節省時間。
實驗管理中的考慮
在ML中管理實驗不是一件小事,當你在運行盡可能多的實驗時,你的項目工作區很容易變得混亂。然而,在初創階段,你沒有幾個月的時間來做數百個實驗。你只需要推動一些更好的東西,然後盡快更新。不管怎樣,進行某種實驗管理總比什麽都不做要好。以下是管理ML實驗時需要考慮的一些事項。
模型版本
在Toucan AI,我們使用GitHub來存儲我們代碼的版本。GitHub很棒,但它不是用來對大型數據文件進行版本控制的。盡管存儲庫可以達到100GB,但GitHub建議將存儲庫的大小保持在1GB以下,此外,單個文件不能超過100MB。
你可以使用其他雲存儲選項,如谷歌雲存儲或Amazon S3。使用雲提供商的命令行工具或web用戶界面,只需創建一個bucket(文件夾),允許對其對象(文件或文件夾)進行版本控制。但是,如果希望將雲存儲中的文件與GitHub上的項目存儲庫同步,則需要額外的手工工作。
因此,我們選擇了最自然的集成,它結合了Git平台和其他雲存儲選項的最佳特性:數據版本控制(DVC),被標記爲“機器學習項目的開源版本控制系統”。DVC是一個命令行工具,它的子命令與Git子命令非常相似。在Git平台和雲存儲設置完成後,你可以運行DVC的‘add’和‘push’命令來設置版本,並在雲存儲中存儲文件或文件夾。同時,可以通過DVC文件引用在Git項目存儲庫中跟蹤大型數據文件。DVC的一個優點是只需要一些額外的類Git命令,這與現有的Git工作流沒有太大的區別。
實驗文檔化
如果你正在進行超參數調優,則很容易忽略在指定日期爲某個模型運行的特定設置。你可能還需要回顧你爲准備或預處理上述模型所需的數據集所做的工作。你的Jupyter Notebooks有描述性的文件名,但是它仍然需要相當多的時間來處理首先發生的事情,或者如果你將預處理A或B應用于實驗7。
一種解決方案是,在創建新的Notebook時,將Notebooks編號作爲文件名的一部分(我喜歡使用“01_”步驟),以後可以重新編號。對你的Notebooks編號有一個明確的命名約定對你的同事(以及未來的你)了解你是如何進行實驗的非常有幫助。除了在實驗中爲Notebooks編號外,我們還使用開源平台MLflow來提供查看實驗超參數和度量結果的web界面。
此外,在記錄實驗時,力求邏輯結構和簡潔。充分利用文件夾結構和名稱來組織你的Notebooks和訓練腳本。假設當讀者查看你的Notebook時,他們會從頭到尾地閱讀,所以要刪除你臨時插入的任何“草稿”單元格。根據經驗,將Notebook上的試驗限制在一個模型和一個數據集上,如果當前的筆記本太長,則創建一個新的筆記本。盡量讓你最後的筆記本版本不包含訓練或推理代碼,這些應該放在可以在Notebook中調用的獨立腳本中。最後,當使用像MLflow這樣的軟件生成實驗記錄時,嘗試將運行實驗的筆記本自動引用到生成的實驗輸出文件中。
測試框架
更好的度量結果並不總是與真實樣本中改進的推理性能相關。此外,在生産ML系統中,ML模型並不是獨立操作的:例如,你可能將啓發式、預處理和緩存作爲pipeline的一部分。因此,當嘗試改進你已經擁有的ML模型時,你會意識到,生成適合真實世界的推理樣本需要大量的時間。你需要深入研究更大的生産代碼,以發現你試圖改進的模型實際上在什麽地方被調用。然後,你不希望只檢查模型本身的輸入和輸出,還要檢查整個ML系統的pipeline。你的“更好”模型如何影響整個系統,它是更好還是更壞?
爲了將重點放在模型改進上,而不是提出推理樣本或擔心破壞生産pipeline中的某些東西,我們需要有一個自動化的系統或端到端測試框架。
在Toucan AI,由于我們的主要産品是AI銷售代理,測試覆蓋主要邏輯分支的樣本對話就足夠了,同時也提供了一種回歸測試的形式。我們目前正在開發一個命令行接口(CLI)工具,它將在一系列示例對話中運行pytest斷言。使用一個命令,所有的對話都可以被測試,如果任何測試用例中斷,我們可以手動更新測試或者認爲我們的“更好的”模型實際上並不更適合生産。
簡而言之,有一個適當的測試框架對于理解當前和實驗模型在生産ML系統中的表現是至關重要的。有了一個合適的測試框架,你的模型改進pipeline應該更有效地推進,允許你比以前運行更多的實驗。
使用工具快速演進
從快速發展的庫中提取代碼並將其寫入使用該庫的修改過的舊版本的生産系統是很困難的。如何修改一個快速發展的庫以滿足你的需要並盡可能高效地應用它的最新更新?
我覺得沒有正確的答案,只有許多不同的途徑。一種方法是把他們的代碼和你的代碼結合起來,讓它工作。另一種方法是使用他們的代碼並完全升級舊版本,但這通常需要更長的時間。簡而言之,考慮一下你有多少時間進行重構,以及重構的優先級是什麽。在你自己的代碼庫和快速發展的工具變得更加穩定之後,你應該關注優先級,並考慮完整的重構。
實驗整理
當你專注于取得成果時,往往很容易忽略整潔。考慮下一組要運行的實驗,以及它的超參數集。發生了一個錯誤?沒問題,更改輸出文件夾上的時間戳並重新運行實驗。然而,你最終得到的是由于試驗不完整而生成的額外文件或文件夾。之後,你在MLflow中滾動一長串記錄,尋找完成的實驗,結果卻讓他們摸不著頭腦。
解決方案是自動刪除不想保存的所有試運行。例如,最好在第一次訓練叠代完成執行之前就刪除失敗的運行。爲了我們的同事和未來的自己,我們都應該盡我們最大的努力保持實驗池的整潔。
關注點的分離
當你研究並嘗試各種ML項目以希望改進你的模型時,你將遇到相互沖突的Python包需求。你最初可能在兩個開發人員之間共享一個雲服務器,但這很快就變得不方便了,因爲你的安裝可能會覆蓋你的同事的運行環境。
進入Docker生態系統,這是一個輕量級的容器化軟件平台,用于管理你的項目環境和依賴項。通過爲每個ML模型和應用程序服務使用單獨的Docker容器,我們可以主動減少“它在我的機器上工作”問題的數量,並防止項目之間發生依賴沖突。與其設置更多的開發服務器,你的每個同事可以在單個共享服務器上設置自己的Docker容器,如果這樣做更劃算的話。
此外,你可能想知道,爲什麽選擇Docker而不是Conda,而且Conda還允許你使用不同的包版本創建不同的環境。我們選擇Docker是因爲它提供的工具更適合于生産和處理雲的環境。如果要在遠程機器上使用Conda,則必須先與機器連接並處理文件傳輸。但是,只需在Docker中使用幾個命令,你就可以對本地文件進行更改,並將它們反映到遠程機器的Docker容器中。此外,運行項目所需的一切都在Dockerfile或Docker Compose文件中指定。
另一方面,對于Conda,如果不引用README,就不清楚是否需要其他步驟。最後,利用Docker Compose的強大功能,如果ML項目需要運行其他服務,它們可以在其他Docker容器中單獨運行,並根據Docker Compose文件設置相互通信。據我所知,在Conda中不能跨環境通信。
准備好做擴展
作爲一個處于早期階段的初創公司,你現在可能不需要擴大規模,但最好是開始考慮可以擴大規模的技術。其中一種技術是Celery,這是一個異步任務隊列系統,可以將任務分配給多個工作者。目前,對于每種類型的服務(服務器、客戶機、embeddings模型等),我們都有一個單獨的worker,但是如果有必要的話,爲相同的服務啓動更多的worker應該不需要太多的工作。通過嵌入進行緩存會成爲瓶頸嗎?沒問題,讓我們啓動另一個嵌入的Celery工作程序,或者增加當前工作程序的“並發”計數,它允許多個子進程並行運行。在我們的Toucan AI配置中,一個Celery worker在一個Docker容器中運行,因此也遵循關注點分離。
除了允許你的生産系統擴展之外,Celery還非常適合執行長時間運行的任務,比如ML模型推斷任務。與允許服務器響應挂起不同,服務器響應(代理的應答)可以立即返回給與Toucan AI代理對話的最終用戶,而異步任務(如緩存機制)可以在後台悄悄運行。此外,我們使用Celery beat來運行我們每天計劃的分析工作者任務。
與同事和未來的你合作
隨著ML研究的不斷發布,作爲ML工程師,你如何讓你的團隊成員保持一個從嘗試到使用的模型或技術的循環?沒有什麽魔法可以將他們獲得的所有知識、經驗和洞察力傳授給你。但是你能做的就是交流。經常交流。
盡可能的交流,尤其是在寫文檔的時候。因爲通常情況下,你是在做你自己的項目,所以你現在正在做的事情可能和你的同事正在做的事情並不完全相關。然而,將來他們可能需要審查或擴展你已經實現的內容。甚至可能是你幾個月後,不得不對你自己的項目做出改變,而你已經忘記了其中的關鍵部分。文檔,文檔,文檔。怎麽強調都不爲過。
另一方面,肯定會有文檔不夠用的時候。如果你有一些東西不確定,你想要他們的誠實的意見,你覺得說話是一個更有效的溝通媒介的時候,注意你的同事的精神集中的狀態,尋求與他們討論項目的方向。從一開始就盡可能的清晰,這對于防止誤解,徒勞的工作,和悔恨是非常重要的。
作爲機器學習工程師的內部鬥爭
作爲一名機器學習工程師,你必須學會權衡好你想要修複的東西的想法以及讓流程變得更好以完成當前的需求之間的關系。你必須學會接受采取最直接的方式來完成工作。例如,我很想花時間改進第三方的訓練/評估代碼,但當時,我只需要采用最短路徑來查看我們的推理結果是否會得到改進。
由于我有web開發的背景,所以大多數情況下我必須編寫自己的代碼,但是在ML工程中,我必須學習如何應用其他人的代碼。當你經常使用的代碼不是你自己的 —— 通常是學生和研究人員花了幾個月或幾年研究出來的代碼 —— 有時很難不覺得自己很失敗,尤其是當你試圖理解不是直接部署到生産系統中的那方面的代碼的時候。
在一天結束的時候,只要記住我們是天性好奇的生物,想要學習比你需要學習的更多是可以的。如果有你想探索的途徑,與你的隊友保持透明是關鍵。一個好的工作環境不會因爲你想要學習更多而責備你,如果你足夠及時地實現了目標。只要你有優先考慮的事情,盡量少擔心,多享受。
結論
爲生産系統構建ML流程並不簡單。盡管在這篇文章中提到了所有的內容,有時候,你能做的最好的決定就是簡單地做一個決定。如果沒有成功,那就繼續下一條路。不管怎樣,我希望這篇文章能夠幫助我們更好地理解各種不同的想法。
英文原文:https://www.toucanai.com/blog/post/building-production-ml/