PyFlink 最新進展解讀及典型應用場景介紹

ApacheFlink發表於2023-01-17

摘要:本文整理自阿里巴巴高階技術專家付典,在 FFA 核心技術專場的分享。本篇內容主要分為四個部分:

  1. PyFlink 發展現狀介紹
  2. PyFlink 最新功能解讀
  3. PyFlink 典型應用場景介紹
  4. PyFlink 下一步的發展規劃

點選檢視直播回放 & 演講PPT

一、PyFlink 發展現狀介紹

1

很多 PyFlink 的新使用者都會問這樣一些問題,PyFlink 是否成熟?功能是否齊全?效能怎麼樣?在這裡,我們針對使用者的這樣一些問題,進行一個詳細的解讀。

首先,在功能層面,PyFlink 已經對齊了 Flink Java API 中的絕大多數功能。使用者使用 Java API 可以實現的功能,基本上都可以用 Python API 實現得出來。

同時,PyFlink 還面向 Python 使用者提供了很多特有的能力,比如說 Python UDF、Pandas UDF 等,允許使用者在 PyFlink 作業中使用各種 Python 三方庫。

在部署模式上,PyFlink 支援各種常見的部署模式,比如說 YARN、kubernetes、Standalone 等,這意味著使用者可以根據需要,靈活地選擇作業的部署模式。

除了功能層面之外,效能也是很多使用者非常關心的。在效能上,PyFlink 也做了很多最佳化,首先,在執行計劃層面,PyFlink 做了一系列的最佳化,儘可能最佳化作業的物理執行計劃,比如運算元融合。

當作業的物理執行計劃確定之後,在 Python 執行時,PyFlink 透過 Cython 實現了 Python 執行時中核心鏈路的程式碼,儘量降低 Python 執行時中框架部分的開銷。對於 Cython 有所瞭解的同學應該知道,Cython 在執行時會被編譯成 native 程式碼來執行,效能非常高。

同時,PyFlink 還在現有的程式模式的基礎之上,引入了執行緒執行模式,以進一步提升 Python 執行時的效能。執行緒執行模式在 JVM 中執行使用者的 Python 程式碼,透過這種方式,在一些典型應用場景中,效能甚至可以追平 Java,這一塊後面我們還會詳細介紹。

經過這一系列的最佳化之後,目前 PyFlink 無論在功能上還是在效能上,都已經基本完備,達到了生產可用的狀態。

2

PyFlink 達到目前這樣一個狀態,並不是一蹴而就的,從 Flink 1.9 開始引入 PyFlink,到目前為止,PyFlink 已經累計釋出了 8 個大版本,20 多個小版本。

從 Flink 1.9 到 Flink 1.11 這幾個版本中,我們重點在完善 Python Table API,基本上對齊了 Java Table API 中的絕大多數功能,同時也支援了 Python UDF、Pandas UDF 等功能。

在 Flink 1.12 至 Flink 1.14 版本中,社群主要是在完善 Python DataStream API,目前已經基本上對齊了 Python DataStream API 上的絕大部分常用功能。

在 Flink 1.15 至 Flink 1.16 版本中,PyFlink 的重點是在效能最佳化上,在原有的程式執行模式的基礎之上,為 Python 執行時引入了執行緒執行模式,以進一步地提升 Python 執行時的效能。

隨著 PyFlink 功能的逐漸完善,我們也看到 PyFlink 的使用者數也在逐漸增長,PyPI 的日均下載量在過去一年也有了顯著的增長,從最開始的日均 400 多次,已經增長到日均 2000 多次。

二、PyFlink 最新功能解讀

3

接下來,我們看一下 PyFlink 在 Flink 1.16 中的功能。PyFlink 在 Flink 1.16 中支援的功能主要是圍繞使 PyFlink 在功能及效能上全面生產可用這樣一個目的。為此,我們重點補齊了 PyFlink 在功能以及效能上的最後幾處短板。

3

如上圖所示,PyFlink 在 Flink 1.16 中支援了 side output 功能。使用者可以把一條資料流,切分成多條資料流。以機器學習為例,使用者可以透過該功能,把一份資料集給切分成多份,分別用於模型訓練和模型驗證。

除此之外,使用者也可以透過 side output 處理遲到資料或者髒資料。將遲到資料或者髒資料透過 side output 拆分出來,單獨進行處理。使用者也可以透過 side output 把遲到資料或髒資料,寫入外部儲存,進行離線分析。

5

PyFlink 在 Flink 1.16 中,還支援了 broadcast state。透過該功能,使用者可以將一條資料流中的資料,廣播傳送到另一條資料流運算元中的多個併發例項上,並透過 broadcast state 儲存廣播流的狀態,以確保作業在 failover 時,所有運算元恢復的狀態是一致的。

比如我們用 PyFlink 做近線預測,當模型更新後,可以將最新的模型檔案地址,廣播傳送到所有的預測運算元,來實現模型的熱更新,並透過 broadcast state 確保在作業 failover 時,所有運算元載入的模型檔案是一致的。

6

PyFlink 在 Flink 1.16 中,對於 DataStream API 上 Window 的支援也做了很多完善,原生支援了各種視窗,比如滾動視窗、滑動視窗、會話視窗等等。Window 可以將無限流中的資料,劃分成不同的時間視窗進行計算,在流計算中是非常重要的功能,有著非常豐富的應用場景。

比如機器學習使用者可以使用 Window 來計算實時特徵。在短影片應用中,可以透過 Window,計算使用者最近五分鐘的有效影片觀看列表,也可以透過 Window,來計算最近 30 分鐘,某個影片在各個人群中的點選分佈等。

7

除此之外,在 Flink 1.16 中,PyFlink 還新增了對於很多 DataStream API 上 connector 的支援,包括 Elasticsearch、Kinesis、Pulsar、Hybrid source 等。與此同時,也支援 Orc、Parquet 等 format。

有了這些 connector 以及 format 的支援,PyFlink 基本上已經對齊了所有 Table API 以及 DataStream API 上 Flink 官方所支援的 connector。

需要說明的是,對於 PyFlink 中沒有原生提供支援的 connector,如果有對應的 Java 實現,也是可以在 PyFlink 作業中使用的,其中 Table API 以及 SQL 上的 connector,可以直接在 PyFlink 作業中使用,不需要任何開發。

對於 DataStream API connector,使用者只需要非常少量的開發即可在 PyFlink 作業中使用。如果使用者有需求的話,可以參考一下 PyFlink 中現有的 DataStream API connector 是如何支援的,基本上只需要一兩個小時即可完成一個 connector 的支援。

8

除了前面介紹的這些功能層面的增強之外,在效能層面,Flink 1.16 也做了很多工作,基本完成了 Python 執行時執行緒執行模式的支援。相比於程式執行模式,執行緒執行模式的效能更好。

執行緒執行模式透過 JNI 呼叫的方式,執行 Python 程式碼,節省序列化/反序列化開銷及通訊開銷。特別是當單條資料比較大時,效果更加明顯。

由於不涉及跨程式通訊,執行緒執行模式目前採用同步執行的方式,不需要在運算元中進行攢批操作,沒有攢批延遲,適用於對延遲敏感的場景,比如量化交易。

與此同時,與其他 Java/Python 互呼叫方案相比,PyFlink 所採用的方案相容性更好。很多 Java/Python 互呼叫方案對於所能支援的 Python 庫,都有一定程度的限制。PyFlink 所採用的方案,對於使用者在作業中所使用的 Python 庫沒有任何限制。

9

如上圖左側所示,展示了程式模式和執行緒模式架構的區別。程式模式需要啟動一個獨立的 Python 程式,用於執行使用者的 Python 程式碼。執行緒執行模式在 JVM 中,透過 JNI 呼叫的方式執行 Python 程式碼。PEMJA 是 PyFlink 中 Java 程式碼和 Python 程式碼之間互呼叫的庫。

如上圖右側所示,在處理時延上,相比於程式模式,執行緒模式有顯著的降低。因為程式模式不需要攢資料,來一條處理一條。與此同時,在處理效能上,執行緒模式相比程式模式也有較大的提升,在某些情況下,效能甚至可以追平 Java。

這裡需要說明的是,Python UDF 的執行效能既取決於 PyFlink 執行框架的效能,也跟 Python UDF 的實現是否高效息息相關。透過各種最佳化手段,目前 PyFlink 執行框架的效能已經非常高效,開銷非常小。使用者的 PyFlink 作業的執行效能很大程度取決於,使用者作業中的 Python UDF 實現得是否高效。

如果使用者的 Python UDF 實現得足夠高效,比如說實現的過程中針對一些耗時操作,有針對性地進行來一些最佳化或者利用一些高效能的 Python 三方庫,那麼 PyFlink 作業的效能其實是可以實現的非常好的。

三、PyFlink 典型應用場景介紹

10

接下來,講一講 PyFlink 的應用場景。目前,實時機器學習是 PyFlink 使用者的重點應用場景。以推薦系統為例,上圖是實時推薦系統的一個典型架構。使用者的行為日誌,透過 APP 埋點等手段,實時採集到訊息佇列中,經過實時資料清洗,歸一化處理之後,在特徵生成、樣本拼接等模組使用。實時的使用者行為日誌,可以用來計算實時特徵。

11

首先,實時使用者行為日誌可以被用來計算實時特徵。實時特徵是 Flink 非常重要的應用場景。實時特徵對於推薦效果的提升非常明顯,建設難度相對來說比較小,是當前很多公司投入的重點。

比如在短影片應用中,使用者最近 N 分鐘的有效影片觀看列表就是短影片應用中,非常重要的使用者實時特徵。這個特徵可以透過一個 Flink 作業,實時分析使用者的行為日誌得到。

一般來說,使用者的行為日誌還會同步一份到離線儲存中,用於生成離線特徵。這塊主要是用於計算一些複雜特徵或者是說長週期特徵。不管是離線特徵還是實時特徵,最終都會儲存到特徵庫中供線上推薦系統使用。

12

實時的使用者行為日誌不但可以用來構造實時特徵,而且可以用來構造實時樣本,用於模型訓練。

透過分析使用者的行為日誌,可以自動完成對樣本打標籤。比如在推薦系統中,給使用者推薦了 10 個 item,如果使用者點選了某個 item,那麼在行為日誌中就會出現這個 item 的點選事件。有了這個點選事件,我們就可以得到一條正樣本。同理,如果對於某個 item,只有曝光事件,沒有點選事件,我們就可以將其看成是一條負樣本。

除了區分正負樣本之外,還需要拼接上使用者的特徵以及 item 的特徵之後,才能得到一條完整的樣本。這裡需要注意的是,做樣本拼接時所用的特徵不是來自於實時特徵庫,而是來自於歷史特徵庫。

由於實時特徵庫中的特徵是不斷更新的,比如在短影片應用中,使用者最近 N 分鐘的影片點選列表特徵,隨著時間的推移,在不斷髮生變化。因此在樣本拼接時,我們希望拼接推薦發生時所用到的特徵,而不是當前時刻的特徵。樣本拼接可能發生在推薦事件過去一段時間之後,此時在實時特徵庫中儲存的特徵可能已經發生了變化,因此這裡拼接的是歷史特徵庫。因為歷史特徵庫中的資料來自於推薦發生時所用到的特徵。

13

樣本經過訓練之後,最終生成模型。經過驗證,如果沒有問題,就可以把模型部署到線上,供線上推理服務使用。

在推薦系統中,線上推理服務包括召回、排序等多個環節。其中,在召回環節使用比較廣泛的一種手段是多路召回技術,每一路召回使用不同的策略。比如說可以根據使用者畫像、當前的熱點內容、運營策略等,分別生成不同的召回結果。對於這些召回結果,合併之後,再經過排序等環節之後展示給使用者。

由此可見,多路召回的好處是顯而易見的。透過多路召回,可以增強推薦結果的多樣性。這裡需要指出的是,由於推薦系統對於延時比較敏感,對於召回策略或者模型的效能要求非常高。

因此,召回中使用的模型或者策略一般都比較簡單。目前一些公司也在探索,在多路召回系統中引入近線召回。近線召回可以預先計算召回結果,並將召回結果快取,作為多路召回中的一路,供線上推理服務直接使用。因此近線召回沒有時延約束,使用者可以在近線召回中使用一些比較複雜的模型或者策略。

14

接下來,介紹一下在上述步驟中,如何使用 PyFlink 完成各項功能的開發。在實時資料清洗部分,機器學習應用中,輸入資料中往往包含很多列。

15

Flink 的其他功能也可以用於資料清洗,比如 SQL。SQL 本身也是非常方便的,那麼和 SQL 相比,PyFlink 可以提供哪些附加價值呢?

首先,在機器學習場景中,普遍有一個共性的特點,資料中的列非常多,可能有幾十列甚至上百列。在這種情況下 SQL 語句可能寫起來非常長,比如在這個例子中,使用者可能希望對第 9 列和第 10 列進行一個合併操作,其他列保留。

但是在 SELECT 語句中,需要把所有的其他的無關列都寫出來,如果資料中的列非常多,寫起來非常繁瑣,同時可讀性、可維護性也會變得比較差,PyFlink 對於這塊提供了完善的支援。

另外,機器學習使用者通常對於 Pandas 比較熟悉,習慣於使用 Pandas 進行資料處理,很多機器學習相關的庫的資料結構都是採用 Pandas 或者 Numpy 的資料結構,PyFlink 在這塊也提供了很好的支援,支援使用者在 Python UDF 的實現中使用 Pandas 庫。

接下來,我們透過幾個具體的例子看一下如何在 PyFlink 中使用上述功能。

16

首先,PyFlink 提供了行操作和列操作的 API,從而簡化使用者的程式碼邏輯。透過列操作,使用者可以非常方便的增加列、刪除列或替換列。列操作適用於輸入資料的列很多,且只有個別列發生變化的場景。

比如在上述例子中,我們透過 add_or_replace_columns 操作,對資料中的 item_id 一列進行歸一化後,替換原有的 item_id 列,資料中的其他列不需要再顯式列出來。

除了列操作之外,PyFlink 還支援行操作,可以以行為單位對資料進行變換。在行操作的 UDF 中,可以直接透過列名引用對應列,使用起來非常方便,適用於輸入資料中的列很多,且需要對多個列進行處理的場景。

在行操作中,不需要在 UDF 的輸入引數中,把所有用到的列都顯式列出來,而是把一行資料都作為輸入傳進來,供 UDF 使用。

在上述例子中,我們透過 map 操作對資料進行變換,map 的輸入是一個 Python 函式,Python 函式的輸入輸出型別都是 Row 型別,Row 是 PyFlink 中定義的一個資料結構,在 Python 函式的實現中可以透過列名引用輸入資料中對應列的值,使用起來非常方便。

除了 map 之外,PyFlink 中還提供了多個行操作相關的 API,如果有需要的話,大家可以從 PyFlink 的官方文件中瞭解詳細資訊。

17

如果使用者熟悉 Pandas 庫,也可以在 Python 函式中使用 Pandas 庫。使用者只需要將 Python 函式的型別,標記成 Pandas 即可。

在這種情況下,Python 函式的輸入型別是 Pandas 的 DataFrame。PyFlink 執行時框架會在呼叫使用者的 Python 函式之前,將輸入資料轉換成 Pandas 的 DataFrame 結構,方便使用者使用。除此之外,Python 函式的輸出型別也需要是 Pandas DataFrame。

18

接下來,我們看一下實時特徵計算部分。

19

當前有很多公司,開發一個實時特徵任務的流程通常是這樣的:

首先,演算法團隊的同學透過資料探勘等手段,發現某個特徵會比較有用。然後,找到資料開發團隊的同學,進行需求溝通。將特徵的詳細描述資訊,甚至 Python 程式碼參考實現,提給資料開發團隊的同學。

然後,資料開發團隊的同學進行需求排期並實現。在實現的過程中,演算法團隊的同學和資料開發團隊的同學可能還需要進行多輪溝通,確保資料開發團隊的同學的理解和實現沒有問題。

另外,演算法團隊同學提供的 Python 參考實現,有可能不太容易翻譯成 Java 程式碼。比如裡面用到了一些 Python 三方庫,找不到合適的 Java 實現等等。

最後,當特徵任務開發好之後,演算法同學經過一系列的驗證,很有可能發現這個特徵可能並沒有預期中的效果那麼好,這樣的一個特徵任務很可能就廢棄了。資料開發團隊的同學也白忙活了。

從這個過程中,我們看到特徵的開發成本是非常高的,涉及到跨團隊的溝通、開發語言的轉換,特徵上線的週期也非常長,通常以周甚至月為單位。

而 PyFlink 可以顯著降低實時特徵任務的開發門檻、縮短實時特徵的上線週期。有了 PyFlink,演算法團隊的同學完全可以自己來開發實時特徵任務。同時,在特徵任務開發的過程中,可以使用各種 Python 庫,沒有任何限制。

20

在推薦場景中,可能會用到這樣一個特徵,計算使用者最近 5 分鐘的訪問物品列表。

為此,PyFlink 提供了多種實現手段。

21

首先,使用者可以透過 SQL+Pandas UDAF 的方式來實現上述功能。

上述 SQL 語句定義了一個長度為 5 分鐘、步長為 30 秒的滑動視窗,針對視窗中的資料,定義了一個 Pandas UDAF 來計算使用者在這個視窗中的訪問序列。Pandas UDAF 的主要邏輯是對視窗中使用者訪問的 item 進行排序,並使用|作為分隔符,生成訪問序列字串。

22

除此之外,使用者可以透過 DataStream API 計算序列特徵,實現上述功能。透過使用 DataStream API,定義了一個視窗大小為 5 分鐘、步長為 30 秒的滑動視窗,並定義了一個聚合函式來處理每一個視窗中的資料。聚合函式需要實現 create_accumulator、add、get_result、merge,定義如何針對視窗中的資料進行聚合運算。

針對視窗中的每一條資料,框架會依次呼叫聚合函式的 add 方法,當視窗中所有的資料都處理完後,框架會呼叫聚合函式的 get_result 方法來獲得聚合值,因此使用者只需要根據業務邏輯的需要實現這幾個方法即可。

在 add 方法中,我們將資料快取起來,在 get_result 中對於所有資料進行排序,並以|作為分隔符,生成訪問序列字串。

23

接下來,我們來看一下實時樣本生成部分。在實時樣本生成部分,主要有正負樣本判斷和特徵拼接。

24

首先,我們看一下正負樣本的構造。在推薦場景中,當給使用者推薦了一批 item 之後,如果使用者點選了某個 item,就會成為一個正樣本,而如果使用者沒有點選,則成為一個負樣本。

在離線場景中,判別正負樣本是非常容易的,而在實時場景中,就不那麼容易了。給使用者展現了某個 item 之後,使用者有可能不點選,也有可能隔了很久之後才點選。

在 Flink 中,使用者可以透過定時器解決正負樣本問題。針對每條曝光事件,使用者可以註冊一個定時器。定時器的時間間隔,可以根據業務的需要確定。

在這個例子中,我們定義了一個 10 分鐘的定時器。在 10 分鐘內,如果收到了這條曝光事件對應的點選事件,則可以將其看成是一個正樣本,否則,如果在 10 分鐘內還沒有收到對應的點選事件,則可以將其看成一個負樣本。

25

正負樣本的問題解決了之後,樣本的標籤也就確定了,接下來,還需要拼接上推薦發生時所用到的特徵,才能成為一條完整的樣本。

這裡,我們可以使用 Flink 中的維表 Join 功能,來進行特徵的拼接。為了解決特徵穿越問題,也就是說在拼接使用者的實時特徵時,使用者的實時特徵相比推薦發生時可能已經發生了變化,前面我們提到,線上推理服務在推薦時,可以將所用到的實時特徵儲存到歷史特徵庫中。為了便於區分特徵的版本,可以給特徵加一個唯一的標識,比如 trace_id,然後在做特徵拼接時,透過 trace_id 來定位推薦發生時所使用的特徵,解決特徵穿越的問題。

26

接下來,我們來看一下近線推理。近線推理是非常典型的應用場景。目前,很多使用者在用 PyFlink 做近線推理。

27

首先,使用者可以透過 Table API 做近線推理。在 Table API 裡,使用者可以透過 Select 語句做近線推理。推理邏輯可以封裝在使用者的自定義函式中。在自定義函式里,使用者可以透過 open 方法,載入機器學習模型。

open 方法只會在作業啟動階段呼叫一次,因此可以確保機器學習模型只 load 一次。實際的預測邏輯可以定義在 eval 方法中。

28

除了 Table API 之外,使用者也可以透過 DataStream API 做近線推理。跟 Table API 類似,DataStream API 中的自定義函式中也提供了一個 open 方法。使用者可以在 open 方法裡,載入機器學習模型。使用方式跟 Table API 比較像,使用者可以根據自己的需求,選擇使用 Table API,還是 DataStream API。

29

除此之外,使用者可以透過 timer 提升推理的時效性。在某些場景中,為了提高時效性,可以透過定時器來做週期性推理。該方法適用於活躍使用者的範圍比較確定,且使用者訪問比較頻繁的場景。

在這些場景中,可以針對活躍使用者,或者圈選一批重點使用者,週期性地進行近線推理,以進一步提升推薦效果。在這裡,我們每 5 分鐘對於活躍使用者進行一次近線推理。

30

某些公司可能是 Java 技術棧。演算法團隊訓練出模型之後,由開發團隊再去負責部署使用。在這種情況下,使用者可能會傾向於使用 Flink 的 Java API 進行預測。

在 PyFlink 支援執行緒模式的過程中,抽象出了一個 library PEMJA,支援 Java 和 Python 之間的互呼叫,跟其他的 Java/Python 互呼叫庫相比,PEMJA 的效能更好,並且對於各種 Python 庫的支援也比較好,相容所有的 Python 庫。

這個例子展示瞭如何利用 PEMJA 提供的 API,在 Flink Java 作業中載入機器學習模型、並進行預測。從這個例子可以看出,透過 PEMJA,使用者可以在 JAVA 程式碼中呼叫並執行 Python 程式碼。

四、PyFlink 下一步發展規劃

31

接下來,PyFlink 的建設重點,會逐步從功能以及效能,轉向易用性、穩定性以及文件,幫助使用者更好的使用 PyFlink。我們接下來會重點完善以下幾個方面。

首先,由於當前 PyFlink 的端到端示例相對來說還比較少,不利於新使用者快速上手。接下來,我們會建設一個獨立的 PyFlink 網站,結合具體場景,展示更多的端到端使用示例。

其次,在易用性方面,接下來會重點最佳化作業執行過程中的報錯提示,讓報錯資訊更友好,使使用者在開發作業的過程中更容易定位問題。

與此同時,我們也在重構當前 Python API 的文件。這塊主要是參考一些其他成熟的 Python 專案的經驗,比如 Pandas。使得使用者在 Python API 文件中,更容易找到 PyFlink 中各個 API 的使用方式。

最後,作業執行的穩定性也非常重要。我們也會持續改進並加強 PyFlink 作業執行的穩定性,比如降低 PyFlink 作業在程式模式下 checkpoint 的耗時等。

最後,也歡迎大家加入 【PyFlink 交流群】交流和反饋 PyFlink 相關的問題和想法。

點選檢視直播回放 & 演講PPT


更多內容


活動推薦

阿里雲基於 Apache Flink 構建的企業級產品-實時計算Flink版現開啟活動:
99 元試用 實時計算Flink版(包年包月、10CU)即有機會獲得 Flink 獨家定製衛衣;另包 3 個月及以上還有 85 折優惠!
瞭解活動詳情:https://www.aliyun.com/produc...

image.png

相關文章