面向演算法的架構設計

xiaocheng.li發表於2018-10-25

人工智慧技術在過去幾年的飛速發展,在研發領域帶來的最大不同,或許是對演算法價值的重新認識。過去的軟體工程師在工作中說起演算法,大多數情況指的是連結串列、二叉樹、排序、時間複雜度等內容,而今天提及,第一反應則是機器學習和深度學習,以及它們在計算機視覺、自然語言處理、語音識別,又或推薦、廣告、視訊編碼、路徑規劃等相關領域的應用。

人們期待演算法解決的,一是以往無從實現的功能,二是優化現有解決方案的效率,為了讓整個系統發揮最大的力量,很可能整個軟體領域,從網際網路服務、雲端計算平臺到客戶端、嵌入式裝置的設計都應當被重新考慮。

在這裡插入圖片描述
(常見軟體架構模組圖)

以單體軟體而言,架構設計,傳統上可能意味著提取、合併同類項,意味著抽象化和層次化,意味著元件關係的固化,也意味著對擴充套件的提挈與規約。如果模組定義合理,一份軟體很快就將形成固定的結構,而功能的增加則在某幾個維度上平鋪開去,直觀的例子可以是以模組化著稱的多媒體框架,當定義了編碼器型別的模組後,一個框架中可以支援數十種不同的編碼器,在工作流程中依據需要能夠很簡單地替換。

然而當把演算法考慮進來之後,事情變得有些不同:

比如,即使為了相同的目標,演算法也可能需要格式和內容上完全不同的輸入和輸出,例如需要詞語的嵌入式表達而非頻率統計,決策不同路徑的相對優劣而非絕對評分。

比如,為了獲取力所能及的最佳效果,演算法可能需要更頻繁地打破原有的模組和層次劃分,譬如進行聯合優化,將影象的分割和分類合二為一,又或將以往固定的環節拆解,僅對其中的部分環節進行處理,例如基於深度學習模型的量化和運動估計。

以及,演算法甚至可能導致軟體目標和結構的重新定位,當對話任務系統成為主要的使用者使用方式,一個客戶端應用的模組必然將圍繞任務插槽擴充其分類、定義和設計。

總而言之,人們對演算法所能做到的邊界不再那麼有把握,又或者說,對演算法的效力存在持續、長久的期待。由此,以往的設計思路不再一定是“好”的,因為以往預期的變化和不變的方向不再成立,一個後端訪問的session是否只使用一種協議,應以什麼流程順序進行視覺元素的載入,都是可以商榷的事情。

演算法的功能由輸入與輸出定義,其中輸入可能是當前的狀態,上游獲取的資料,亦可能是自身的歷史行為,而輸出是行動決策,是狀態記錄,也是處理過的資料。

“更多資料,更多智慧”並非一句空話,當加入一個有效的、全新維度的特徵資料,推薦演算法的準確性或召回率確實有可能上一個臺階,演算法如果被定義為一個模組,最需要穩定的無疑是其輸入介面,而所擴充套件的將是其樣式。

另一方面,演算法的效果體現在其輸出,進行知識提取時,其格式是否組織良好體現設計者對問題的理解,大多數情況下動態格式可能更能滿足需求,但特定問題也許固定格式更好,進行行為預測時,其閾值是否設定合理,是否允許配置、過濾和再處理,對於行動計劃,挑戰可能在於如果需要響應模組協調地行動,如何保持時序和一致性,如果需要延遲決策,如何保證響應模組的狀態和決策資訊的持久,如果需要傳遞資訊,如何保證實時性和可靠性。

在這裡插入圖片描述
(以演算法為中心進行設計)

如果改變看待演算法的觀念,即不再視其為解決某個特殊問題的獨立環節,而是將其看作軟體設計的核心,則推論很可能是這樣的:抽象和層次應該保持在最低的程度,儘可能使用聚合而非繼承,使用拍平的模組結構,模組之間使用連線關係代替從屬關係,模組在必要的範疇之外保持無狀態,使用統一且共享的上下文交換資訊,更多地自底向上地進行歸類、抽象,取代自上而下地根據產品功能進行“頂層設計”,以便於演算法的輸入和輸出為設計導向,方便頻繁和大範圍地重新組合和迭代進化。

以後端服務而論,演算法所期待的,同樣是統一的輸入側介面以幫助自由地運用資料,簡潔靈活的輸出側方法以供多方引用。微服務體系在應對元件重構上具備天然的優勢,可能需要新增的僅是統合資料的服務及對應的資料庫、快取。

由於工作鏈條、價值鏈條持續地變得複雜,構建工作流(很多時候以DAG即有向無環圖的形式體現)超出過去較為狹窄的一些應用領域變得遠為普遍。仍以影象和視訊處理為例,原本固定的DAG極為常見,例如從檔案讀取、分析、解碼到渲染的過程,而資料領域流式處理同樣並不新鮮,依據實時性需求的不同,如Spark或Storm之類的計算框架中往往跑著許多很好的範例,但演算法運用範圍和能力的擴大可能會對工作流賦予一些新的含義。

首先是在服務中嵌入演算法變得普遍,例如上傳圖片進行美顏這樣的功能,多半需要被實現為獨立的服務,而如果需求變成在識別圖片中的人臉後再進行判別,或許可以拆分為檢測和判別兩個服務,反過來看,一項客戶端請求,可能需要在後端構建某一工作流完成,而工作流的每一節點均可能由不同的演算法節點響應。

其次,工作流可能呈現出以往少見的動態性,不僅是工作流本身可能時常變化,增加或減少處理環節,而在於許多情況下,前一環節的處理結果將決定後一環節使用何種邏輯,呼叫何種演算法來完成工作。一個例子是基於內容的編碼,和以往預先定義的編碼過程不同,為了選取最佳的編碼方式,很可能最佳方式是先對進行掃描再構建後續處理的工作流。

許多有效的、大規模應用的演算法本身就不是以黑盒方式運作,而是考慮到現實性,設計為不同的階段或組合而成,甚至分佈在不同環境中執行,比如對TopN推薦問題,很多系統就劃分為兩個階段執行,第一階段得到一些候選的推薦條目集合,第二階段再行打分排序,另一個例子是CDN下載路徑動態切換的問題,初始的響應節點可能更多地由全域性排程服務考慮快取狀態分配,而會話過程中的路徑切換其決策可能由邊緣節點甚至客戶端作出。

由於工作流的路徑變長,而演算法往往以概率方式工作,誤檢、漏檢、錯誤分類等容易產生累積效應,此外,以篩選為目的的演算法也期望在更早的階段去除不必要的候選資料,提高效率,為達成以上目標,往往需要設定演算法甚至人工的檢測、過濾。鑑於對輸入輸出重用性的期待,工作流的分支情況可能像刺蝟身上的刺一樣複雜。

在這裡插入圖片描述
(在上千個微服務中保持工作流和資料流的走向)

為避免過高的耦合性,也避免發生迴圈依賴,假設失效,能夠倚仗的原則或許只有確定工作流和資料的“方向”,即給予資料內涵嚴格的定義和過程劃分,並盡力找到“正確”地符合要求的資料,從源頭使用它們,而在工作流中產生許多“飛線”,以此保證一致性和時序性,保證全域性過程仍在可以把握的範疇之內。

演算法對資料的依賴,較之純粹的資料體系有所不同,大而化之的話,可以稱為面向演算法的資料準備和麵向報告、面向Dashboard的資料準備之間的區別:

一則吞吐量的期望可能更大,因為餵給模型的資料往往是全量資料,甚至同一份資料的不同表達,也因為資料可能是直接的聲音、圖片甚至視訊,還因為訓練過程往往反覆持續地進行。

二來其資料結構可能需要變化頻繁,不遜於一些業務變化頻繁的網際網路服務的頻率,對應資料往往存在版本的要求,針對資料的標籤,針對標籤的標籤,資料集合之間的依賴等等,並和模型更新結合緊密。

第三,需要考慮如何連線訓練和推理環境,使得其中的資料可以有最佳的儲存和使用效率。

演算法在儲存方面往往也有獨特的需求,例如推薦系統稀疏的關係表,大到需要分散式儲存的模型,亦或連續的視訊幀檔案等等。

根據上文列出的不同點,存在許多技術上新的需求和手段:

由於對資料的消費許多時候(尤其是訓練中)是純粹本地化的,並不需要進行跨叢集、跨資料中心的傳遞,一定程度的計算和資料的繫結將十分有效,排程計算任務直接到資料所在的節點,或者相近的機架、子網能夠消除I/O瓶頸,同時,使用PCI-E介面的SSD方案不僅在頻寬上而且在IOPS方面可以消除硬碟和網路卡之間吞吐量的級差,為榨乾標稱傳輸能力(例如百G網路卡甚至雙百G)進行的協議棧和作業系統改造,其價值也變得誘人。

在超大規模模型、樣本大小和資料量的條件下,計算、資料樣本、模型引數必須完全分離,這可能是以往分散式系統概念的推進,而為了避開可能的網路瓶頸,提升效率,或許還需要額外的分割槽和智慧路由能力。

在這裡插入圖片描述
(分散式模型訓練)

針對資料集合的依賴圖管理和自動模型更新觸發系統可能成為基礎的支撐系統。

為最大限度地避免重複勞動,面向特定維度設立的專用資料儲存將能發揮更大的作用,例如根據視訊內容時間線對齊,並提供充足檢索能力的多模態資料庫。

為巨大資料量增加副本並不經濟,資料傳輸亦會帶來額外開銷,能夠提供細粒度鎖、許可權管控和觸發程式能力的儲存可能更為適合在訓練到推理、離線到線上等不同環境中進行共享。

行文到此,回過頭來看,演算法帶來的許多改變,如面向輸入和輸出的設計,複雜多變的工作流,特別的支援體系等等,大致都是因為某些前提和假設不復存在,因為需要解決與以往不同的難題,目標不同,架構和實現自然需要變化,但更本質的東西,即從釐清需求入手,從有效地對未來演進作出評估入手,從定義變與不變的維度入手,以及運用分拆、解耦、聚合、聯結、隔離等方法,仍然是最可倚為憑仗的手段,由此,重估以往的觀念,形成新的設計並不為難。

首先需要變革的是思想。

tshshrk@gmail.com

相關文章