FeatHub:流批一體的實時特徵工程平臺

ITPUB社群發表於2023-03-14

摘要:本文整理自阿里巴巴高階技術專家、Apache Flink/Kafka PMC 林東,在 FFA 2022 AI 特徵工程專場的分享。本篇內容主要分為三個部分:

  1. 為什麼需要 FeatHub

  2. FeatHub 架構和概念

  3. FeatHub API 展示



01

為什麼需要 FeatHub


1.1 目標場景


FeatHub:流批一體的實時特徵工程平臺

上圖中展示的是 Feathub 需要支援的目標場景。

首先,考慮到機器學習開發者通常是熟悉 Python 環境的資料科學家,他們通常使用諸如 TensorFlow、PyTorch 和 Scikit-Learn 等 Python 演算法庫進行機器學習的訓練和推理作業開發。因此,我們希望繼續支援他們使用 Python 進行特徵工程開發,以便他們的特徵生成作業程式碼可以與現有的 Python 演算法庫進行無縫對接。

其次,考慮到目前各個業務領域(如推薦和風控)正在逐漸向實時方向發展,我們希望能夠使這些業務的特徵工程作業能夠像生成離線特徵一樣輕鬆地生成實時特徵。

第三,考慮到越來越多的企業使用者不想受限於特定的雲廠商,他們對於多雲部署有著強烈的需求。因此,我們計劃透過將專案的核心程式碼開源,允許使用者在任意的公有云和私有云上部署 FeatHub。

1.2 實時特徵工程的痛點


FeatHub:流批一體的實時特徵工程平臺

與離線特徵相比,使用實時特徵會涉及到額外的痛點,包括實時特徵工程的開發、部署、監控和分享四個階段的覆蓋。

在開發階段,由於特徵隨著時間而變化,可能會發生特徵穿越的問題。為了確保作業生成正確的特徵,使用者需要在 Flink 作業中編寫程式碼處理時間戳資訊,以避免出現特徵穿越的情況。對於許多使用者,特別是那些專注於演算法開發的資料科學家,避免特徵穿越的開發成本相對較高。

當資料科學家完成特徵開發後,他們需要將特徵作業部署到生產環境,並實現高吞吐和低延遲的特徵生產。通常情況下,平臺方需要提供一些軟體工程師來協助資料科學家將實驗程式轉換為高效能、高可用的分散式程式進行執行,這些作業可能是 Flink 或 Spark。這個轉換階段引入了額外的人力和時間成本,可能會引入更多的錯誤,同時也可能導致開發週期延長、浪費人力資源等問題。

在監控階段,使用者需要監控已經完成線上部署的實時特徵工程作業。監控的難度較大,因為特徵質量不僅取決於程式碼是否存在 bug,還取決於是否受到上游資料來源數值分佈變化的影響。

當整個作業的推薦效果下降時,通常需要手動檢查每個階段的特徵分佈變化,以定位問題產生的階段並進一步進行除錯。目前,這一過程需要投入大量人力,導致效率低下。為了加速實時特徵工程的部署和運作,我們希望進一步降低監控的難度和人力消耗。

在分享階段,不同組開發實時特徵工程時常常需要定義相似甚至相同的特徵。缺乏後設資料中心使得開發者難以註冊、搜尋和複用特徵定義和特徵資料。因此,他們不得不重複進行開發工作和執行相同的作業,導致資源浪費。為了解決這個問題,我們希望支援使用者在一箇中心化的後設資料中心中搜尋、分享和複用已有特徵,以降低重複開發的成本。

1.3 point-in-time correct 語義


FeatHub:流批一體的實時特徵工程平臺

接下來將舉例為大家介紹什麼是特徵穿越。在上圖的示例中,我們有兩個資料來源,分別代表維表特徵和樣本資料。維表特徵描述了使用者在最近兩分鐘內點選網頁的次數,而樣本資料描述了使用者在看了一個網頁後是否有點選廣告的行為。

我們希望將這兩個資料來源中的資料進行拼接,以產生訓練樣本,進而用於 TensorFlow、PyTorch 等機器學習程式的訓練和推理。如果我們在進行樣本拼接時,未正確考慮資料的時間因素,可能會影響模型的推理效果。

在上圖的例子中,我們可以使用使用者 ID 作為 Join Key,將這兩個表進行拼接。然而,如果在拼接維表特徵時未考慮時間戳,只使用表中最新的點選數,那麼我們得到的訓練資料中,所有樣本的“最近 2 分鐘點選數”特徵值都會是 6。這個結果與真實情況不符,會降低模型的推理效果。

FeatHub:流批一體的實時特徵工程平臺

具有“point-in-time correct”語義的拼接,會比對樣本資料和維表特徵的時間戳,找到維表特徵中時間戳小於樣本時間戳、但最接近樣本時間戳、並且具有對應 Join Key 的特徵作為拼接後的特徵值。

在上圖的例子中,6:03 時刻的特徵值應該來自於 6:00 時刻使用者的點選數 10。因此,在生成的訓練資料中,6:03 時刻的樣本資料中的點選數是 10。

而在 7:05 時刻,由於特徵值在 7:00 時刻變為 6,因此在生成的訓練資料中,這個樣本對應的特徵值也是 6。這就是使用”point-in-time correct“語義進行拼接所得到的結果。這種方式可以避免特徵穿越問題。

1.4 Feature Store 的核心場景


FeatHub:流批一體的實時特徵工程平臺

上圖展示了 Feature Store 的核心場景。Feature Store 是一個近兩年興起的概念,旨在解決剛才描述的特徵工程場景中的核心痛點,包括特徵的開發、部署、監控、共享等階段。那麼,Feature Store 是如何解決這些階段的痛點的呢?

FeatHub:流批一體的實時特徵工程平臺

下面我們將分階段說明 Feature Store 的核心價值。

在特徵開發階段,Feature Store 可以提供一個簡單易用的 SDK,讓使用者專注於定義特徵的計算邏輯,例如拼接、聚合、UDF 呼叫,而無需實現處理特徵穿越問題的邏輯。這將極大簡化資料科學家定義和使用實時特徵的工作。

在特徵部署階段,Feature Store 將使用內建的執行引擎來計算並生成使用者定義好的特徵,這樣使用者就不需要直接面對 Spark 或 Flink 等專案的程式設計 API 來開發分散式作業了。執行引擎需要支援高吞吐、低延遲的特徵計算。同時,Feature Store 可以提供豐富的聯結器選擇,支援使用者從不同的資料來源和資料儲存中讀取和寫入資料。

在特徵監控階段,Feature Store 可以提供一些通用的標準化指標,讓使用者可以實時監控特徵數值的分佈變化,並支援告警引入人工干預來檢查和維護機器學習鏈路的推理效果。

在特徵分析階段,Feature Store 將提供特徵後設資料中心,支援使用者註冊、搜尋和複用特徵,鼓勵大家合作分享,以減少重複的開發工作。

1.5 為什麼需要 FeatHub


FeatHub:流批一體的實時特徵工程平臺

雖然已經有幾個開源的 Feature Store,例如 Feast 和 Feather,但為什麼我們還要開發 FeatHub 呢?

在開發 FeatHub 之前,我們調研了現有的開源 Feature Store 和一些雲廠商的 Feature Store(例如 Google Vertex AI、Amazon SageMaker),發現它們提供的 SDK 在能力和易用性方面並不能滿足我們之前描述的需求(例如實時特徵、Python SDK、開源)。因此,我們重新設計了一套 Python SDK,進一步支援實時特徵,並且讓 SDK 更加簡單易用。我們將在介紹 FeatHub API 時進一步討論其易用性。

除了支援實時特徵和更加易用之外,FeatHub 的架構能夠支援多種執行引擎,例如 Flink、Spark 以及基於 Pandas 庫實現的單機執行引擎。這使得 FeatHub 能夠在單機上快速進行實驗和開發,並隨時切換到分散式的 Flink/Spark 叢集中進行分散式高效能運算。使用者可以根據需求選擇最合適的執行引擎。這也使得 FeatHub 具有非常好的可擴充套件性。這些特性是其他開源 Feature Store 所不具備的。

在生產部署階段,我們希望 FeatHub 能夠以最高效的方式計算實時特徵。目前大部分開源 Feature Store 只支援使用 Spark 計算離線特徵。而 FeatHub 支援使用 Flink,這是目前流計算領域最好的執行引擎,來計算實時特徵。這個能力是其他 Feature Store 所沒有的。

02

FeatHub 架構和概念


2.1 架構


FeatHub:流批一體的實時特徵工程平臺

這張架構圖展示了 FeatHub 平臺的核心元件和架構。最上層是 SDK,目前基於 Python 語言開發,未來將支援 Java 或其他語言的 SDK。使用者可以使用 SDK 編寫宣告式的特徵定義,指定特徵的資料來源和目標儲存位置,以及特徵計算邏輯,例如基於滑動視窗的聚合和特徵拼接。我們預期該 SDK 能夠表達所有已知的特徵計算邏輯。

中間層是多種執行引擎。其中,Local Processor 支援使用者在單機上利用 CPU、磁碟等資源計算特徵,以方便使用者在單機上完成實驗。Flink Processor 可以將使用者的特徵定義翻譯成 Flink 作業,在高可用性、分散式的叢集環境中進行低延遲、高吞吐的特徵計算。Spark Processor 則可以將使用者的特徵定義翻譯成 Spark 作業,支援使用者使用 Spark 執行高吞吐的離線特徵計算。

在執行引擎之下是特徵的儲存,包括離線儲存(例如 HDFS)、流式儲存(例如 Kafka)和線上儲存(例如 Redis)。

FeatHub:流批一體的實時特徵工程平臺

上圖展示了 FeatHub 平臺如何與機器學習的訓練和推理程式對接。

使用者可以使用 FeatHub SDK 定義特徵。一旦部署了 FeatHub 作業,FeatHub 將啟動對應的 Flink/Spark ETL 作業,並從線上或離線儲存中讀寫特徵資料。如果使用者需要進行離線訓練,則訓練程式(例如 TensorFlow)可以從離線儲存(例如 HDFS)中批次讀取相應的特徵資料。如果使用者需要進行線上推理,則線上推理作業可以從線上儲存(例如 Redis)中讀取特徵資料。

此外,存在需要進行線上計算的場景,其中特徵的值需要在接收到使用者請求後進行線上計算。例如,在地圖手機應用中,當伺服器收到使用者請求時,可能需要根據使用者當前請求和上一次請求的地理位置相對距離計算使用者的移動速度特徵。為滿足這一場景需求,Feature Service 可以提供線上計算服務。

2.2 核心概念


FeatHub:流批一體的實時特徵工程平臺

上圖中的 Table Descriptor 表示一個具有 Schema 的特徵表,其概念類似於 Flink Table。我們可以基於資料來源(例如 Kafka Topic)定義一個 Table Descriptor,並對其應用計算邏輯(例如滑動視窗聚合),從而產生一個新的 Table Descriptor,並將 Table Descriptor 的資料輸出到外部儲存(例如 Redis)中。

Table Descriptor 可以分為 FeatureTable 和 FeatureView。FeatureTable 是指特徵儲存中的一個物理特徵表。例如,使用者可以將 FeatureTable 定義為 Kafka 中的一個 Topic。而 FeatureView 則是對 FeatureTable 應用一個或多個計算邏輯後得到的結果。例如,使用者可以對來自 Kafka 的 FeatureTable 應用不同的計算邏輯,從而得到以下三類不同的 FeatureView:

1. DerivedFeatureView:它的輸入行和輸出行是一一對應的。使用者可以使用這類 FeatureView 進行樣本拼接,生成訓練樣本。DerivedFeatureView 可以包含基於單行計算和 Table Join 獲得的特徵。
2. SlidingFeatureView:這是一個輸出隨時間變化的 FeatureView。它的輸入行和輸出行通常沒有一一對應的關係。例如,我們需要計算使用者最近兩分鐘內點選某商品的次數,輸入的資料是使用者的點選行為。即使使用者不再產生新的點選行為,隨著時間的推移,我們知道這個特徵的數值也會逐漸遞減,直至降為零。SlidingFeatureView 可以包含基於單行計算和滑動視窗聚合獲得的特徵。
3. OnDemandFeatureView:它可以使用來自線上請求的特徵作為輸入來計算新的特徵。因此,我們需要 Feature Service 來線上計算這個 FeatureView 包含的特徵。OnDemandFeatureView 可以包含基於單行計算獲得的特徵。
FeatHub 支援多種特徵計算邏輯,包括:

1. ExpressionTransform:支援宣告式的計算表示式,類似於 SQL Select 語句中的表示式。使用者可以對特徵進行加減乘除等操作,並呼叫內建的 UDF 函式。
2. JoinTransform:支援拼接不同 Table Descriptor 的特徵,使用者可以指定樣本資料表和維表,以獲得訓練樣本。
3. PythonUDFTransform:支援使用者在 FeatHub SDK 中自定義和呼叫 Python 函式,方便熟悉 Python 的資料科學家進行特徵開發。
4. OverWindowTransform:支援基於 Over 視窗的聚合計算,類似於 SQL 中所支援的 Over 視窗聚合。例如,對於一個代表使用者購買行為的輸入表,使用 OverWindowTransform 找到這一時刻之前的2分鐘內的該使用者的行為資料,並統計購買總額度,作為該使用者的特徵。
5. SlidingWindowTransform:支援基於滑動視窗的集合計算,可以隨著時間變化輸出新的實時特徵資料。該計算邏輯可以將結果輸出到線上特徵儲存 (e.g. Redis),方便下游機器學習推理程式實時查詢和使用。與 OverWindowTransform 不同的是,SlidingWindowTransform 可以在沒有新的輸入時過期資料。

FeatHub:流批一體的實時特徵工程平臺

上圖展示了 FeatHub 的執行引擎與特徵儲存的工作流程。

首先,使用者需要定義 Source 來表達資料來源。Source 中的資料會被執行引擎按照所定義的各種 Transform 邏輯進行處理,再輸出到外部特徵儲存 Sink,以便下游呼叫。這個過程類似於傳統的 ETL。

Source 可以對接常見的離線或線上儲存,例如 FileSystem、Kafka、Hive 等等。而 Sink 同樣可以對接常見的離線或線上儲存,例如 FileSystem、Kafka、Redis等等。Redis 是目前特徵工程領域使用比較多的線上儲存。

FeatHub 支援多種執行引擎,包括 LocalProcessor、FlinkProcessor 和 SparkProcessor。使用者可以根據自己生產環境的具體情況,選擇最合適的引擎來生成所需的特徵。

1. LocalProcessor 會在本地機器上執行特徵計算邏輯,基於 Pandas 庫實現。使用者可以在單機上開發特徵定義和執行實驗,無需部署和使用分散式叢集(例如 Flink)。LocalProcessor 僅支援離線特徵計算。
2. FlinkProcessor 會將特徵定義翻譯成可以分散式執行的 Flink 作業,可以基於流計算模式來生成實時特徵。FeatHub 支援 Flink Session 模式和 Flink Application 模式。

3. SparkProcessor 會將特徵定義翻譯成可以分散式執行的 Spark 作業,可以基於批計算模式來生成離線特徵。


03

FeatHub API 展示


3.1  特徵計算功能


FeatHub:流批一體的實時特徵工程平臺

接下來,我們將透過一些示例程式,展示如何使用 FeatHub 完成特徵開發,並展示使用者程式碼的簡潔性和易讀性。

左上角的影像顯示了使用 JoinTransform 完成特徵拼接的程式碼段。如果使用者需要將來自維表的特定列連線作為特徵,則可以在特徵上提供新的特徵名稱、特徵資料型別、連線鍵以及 JoinTransform 例項。在 JoinTransform 上,使用者可以提供維表名稱和列名稱。需要注意的是,使用者只需提供連線所需的基本資訊,這種宣告式的定義非常簡潔。

中間的影像展示了使用 OverWindowTransform 完成 Over 視窗聚合的程式碼段。如果我們需要計算最近兩分鐘內使用者購買商品的消費金額總數,則使用者除了提供特徵名稱和資料型別外,還可以提供一個 OverWindowTransform 例項。在 OverWindowTransform 上,使用者可以提供一個宣告式的計算表示式 item_count * price,用於計算每個訂單的消費金額,並設定 agg_function = SUM,以將所有訂單的消費金額相加。此外,使用者可以將 window_size 設定為 2 分鐘,即每次特徵計算都會聚合前 2 分鐘內的原始資料輸入。group_by_key = ["user_id"] 表示對每個使用者 ID 進行聚合計算。將所有資訊結合起來,即可完整地表達所需的 Over 視窗聚合邏輯。這種宣告式的表達非常簡潔,類似於 SQL 的 select / from / where / groupBy。

上圖右上角展示了使用 SlidingWindowTransform 完成滑動視窗聚合的程式碼片段,與 Over 視窗聚合非常相似。區別在於需要指定 step_size,這裡設為 1 分鐘,表示每隔 1 分鐘視窗會滑動一次,移除過期資料,重新計算並輸出新特徵值。

上圖左下角展示了使用 ExpressionTransform 呼叫內建函式的程式碼片段。FeatHub 提供常用內建函式,類似於 Flink SQL 或 Spark SQL。程式碼片段中的 UNIX_TIMESTAMP 將輸入的時間戳字串轉換為整數型別的 Unix 時間戳。例如,在這個例子中,使用者可以將計程車的上車和下車時間的字串型別特徵分別轉換為整數型別,並相減得到代表本次行程總時間的特徵。

上圖右下角展示了使用 PythonUdfTransform 呼叫使用者自定義 Python 函式的程式碼片段。在此例中,使用者使用 Python lambda 函式將輸入字串轉換為小寫字來獲得新的特徵。

3.2 樣例場景


FeatHub:流批一體的實時特徵工程平臺

為了更清晰地展示如何使用 Feathub 完成端到端的作業開發,我們將以生成機器學習訓練資料集為例,並提供詳細的講解。在本例中,我們假設使用者資料來自於兩個表:Purchase Events 和 Item Price Events。

Purchase Events 中的每行資料代表一次購買商品的行為。其中,user_id 代表使用者,item_id 代表被購買的商品,item_count 代表購買的商品個數,而 timestamp 代表購買商品的時間。

Item Price Events 中的每行資料代表一次商品價格變化的事件。其中,item_id 代表價格發生變化的商品,price 代表最新的價格,timestamp 代表價格變化的時間。

為了為機器學習訓練生成一個資料集,我們需要在這兩個表格中的資訊的基礎上建立一個新的資料集。該資料集需要在每次購買商品的行為發生時,記錄購買商品的使用者在當前時刻之前的 30 分鐘內的總消費金額作為新特徵。

為了建立這個資料集,我們可以使用 JoinTransform,使用 item_id 作為 Join Key,將 Item Price Events 中的價格以 point-in-time-correct 的方式拼接到每個 Purchase Events 行的資料中。然後,我們可以使用OverWindowTransform,以 user_id 為 group_by_key,並設定 window_size 為 30 分鐘,計算每行的 item_count 乘以價格,並使用 SUM 函式進行聚合,以獲得所需的新特徵。


3.3 樣例程式碼


FeatHub:流批一體的實時特徵工程平臺

上圖展示了完成樣例場景的程式碼片段。

首先,使用者需要建立一個 FeatHubClient 例項。FeatHubClient 包含了核心 FeatHub 元件的配置。在這個例子中,我們配置 FeatHubClient 使用 Flink 作為執行引擎。使用者還可以進一步提供 Flink 相關的配置資訊,例如 Flink 的埠和 IP 地址等。

接下來,使用者需要建立 Source 來指定特徵的資料來源。在這個示例中,特徵資料來自本地檔案,因此可以使用 FileSystemSource 表達。在需要實時計算特徵的場景中,使用者可以使用 KafkaSource 從 Kafka 實時讀取特徵資料。為了能夠在之後的特徵計算中引用這些特徵,使用者需要指定 Source 的名稱。

在 FileSystemSource 上,data_format 表示檔案的資料格式,例如 JSON、Avro 等。timestamp_field 則指定了代表資料時間戳的列名。有了時間戳資訊,FeatHub 就可以執行遵循 point-in-time-correct 語義的拼接和聚合計算,避免特徵穿越問題。

在建立了 Source 後,使用者可以建立 FeatureView 來定義所需的拼接和聚合邏輯。在程式碼片段中,item_price_events.price 表示需要拼接來自 item_price_events 表上的 price 列。total_payment_last_two_minutes 表示 Over 視窗聚合得到的特徵。對於來自 purchase_event_source 的每一行,它會找到之前 2 分鐘內具有相同 user_id 的所有行,計算 item_count * price,然後將計算結果相加,作為特徵的數值。

FeatHub:流批一體的實時特徵工程平臺

定義 FeatureView 後,使用者就可以從中獲取特徵資料,並將資料輸出到特徵儲存中。使用者可以呼叫 FeatHub 提供的 table#to_pandas 函式獲取包含特徵資料的 Pandas DataFrame,然後檢查資料的正確性,或者將獲得的資料傳遞給 Scikit-learn 等 Python 演算法庫進行訓練。

完成特徵作業開發後,使用者需要將作業部署到生產環境中的分散式叢集來處理大規模的特徵資料。使用者需要建立一個 Sink 例項來表達特徵輸出的儲存位置和相關格式資訊。在樣例程式碼中,FileSystemSink 指定了 HDFS 路徑以及 CSV 資料格式。

最後,使用者可以呼叫 FeatHub 提供的 table#execute_insert 函式將作業部署到遠端的 Flink 叢集進行非同步執行。

3.4 FeatHub 效能最佳化


除了提供 Python SDK 方便使用者開發特徵作業外,FeatHub 還為常見的特徵作業提供多種內建效能最佳化,以降低特徵作業的生產部署成本並提高效率。

在實時搜尋推薦等領域,經常會用到多個滑動視窗聚合的特徵,它們的定義幾乎相同,只是視窗大小不同。例如,為了判斷是否要向使用者推薦某個商品,需要知道該使用者在最近 2 分鐘、60 分鐘、5 小時和 24 小時內點選某類商品的次數。如果直接使用 Flink API 來生成這些特徵,每個特徵都會有一個獨立的 Window 運算元來記錄視窗範圍內的資料。在這個例子中,最近 2 分鐘內的資料會被複制多份存放在所有運算元中,導致浪費記憶體和磁碟空間。而 FeatHub 提供了最佳化,使用一個運算元來記錄最大視窗範圍內的資料,並複用這些資料來計算這些特徵的數值,從而降低 CPU 和記憶體成本。

此外,針對前文提到的滑動視窗聚合的特徵,我們發現它們的輸入資料通常具有稀疏性。以計算使用者最近 24 小時中點選商品的次數為例,大多數使用者的點選事件可能只集中在其中某一小時,而其他時間段則沒有點選行為,因此不會產生特徵數值的變化。如果直接使用 Flink API 生成此特徵並要求步長為 1 秒,則 Flink 的滑動視窗每秒都會輸出資料。相比之下,FeatHub 會提供最佳化,僅在特徵數值發生變化時才輸出資料。這樣可以顯著減少 Flink 輸出的資料所佔用的網路頻寬,並降低下游計算和儲存資源的消耗。

在特徵工程方面,我們計劃在 FeatHub 中增加更多的最佳化措施,例如透過使用 SideOutput 獲取遲到的資料來更新滑動視窗特徵,提高特徵的線上和離線一致性。我們還將提供機制來減少爬蟲造成的 hot-key 對 Flink 作業效能的影響。

3.5 FeatHub 未來工作


我們正在積極開發 FeatHub,希望儘早為使用者提供生產可用的能力。以下是我們計劃完成的工作。

1. 完善基於 Pandas、Spark 和 Flink 的執行引擎實現,並提供儘可能多的內建效能最佳化,以加速特徵計算的過程並提高生產環境中的效能表現。
2. 擴充套件支援更多的離線和線上儲存系統,例如 Redis、Kafka、HBase 等,使得 FeatHub 能夠覆蓋更廣泛的場景和應用。
3. 提供視覺化 UI,幫助使用者訪問特徵後設資料中心,輕鬆註冊、查詢和複用特徵,從而提高開發和部署的效率。
4. 提供常見的指標和監控功能,例如特徵的覆蓋率和缺失率等,支援開箱即用的特徵質量監控和告警機制,保證特徵的穩定性和準確性。

FeatHub:流批一體的實時特徵工程平臺

歡迎嘗試使用 FeatHub 並提供您的開發建議。FeatHub 是一個開源庫,其地址為 。還可以訪問 獲取更多的 FeatHub 使用程式碼樣例。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70024420/viewspace-2939560/,如需轉載,請註明出處,否則將追究法律責任。

相關文章