返回文章列表

Ray 分散式運算框架技術綜述

Ray 是一個以 Python 為主的分散式運算框架,專為資料科學和機器學習工作負載設計。它提供簡潔的 API 和高效能的執行環境,讓開發者能輕鬆擴充套件 Python 應用程式到多核心和多機器叢集。Ray 支援各種機器學習任務,並與現有工具鏈無縫整合,簡化資料處理、模型訓練、超引數調校和模型佈署流程。

分散式運算 機器學習

Ray 作為新興的分散式運算框架,解決了資料科學家在擴充套件機器學習工作負載時面臨的挑戰。它允許開發者使用熟悉的 Python 工具和程式函式庫,同時將運算任務分配到多個核心或多台機器上,大幅提升效率。Ray 的設計理念強調簡潔性、靈活性、高效能和與 Python 生態系統的整合,使其成為處理複雜資料科學和機器學習任務的理想選擇。Ray 的核心功能包括任務平行化、Actor 模型、分散式資料集和機器學習函式庫,為資料科學家提供了一個完整的工作流程解決方案。

Ray 技術概述

在當今資料驅動的時代,企業面臨著處理海量資料的挑戰。隨著資料收集速度和種類別的增加,高效的分散式計算變得至關重要。在過去的十年中,出現了許多儲存系統、資料處理和分析引擎,這些技術對於許多公司的成功至關重要。值得注意的是,大多數「大資料」技術都是為資料工程師設計的,他們負責資料收集和處理任務,從而讓資料科學家能夠專注於他們最擅長的工作。

資料科學家的需求

作為一名資料科學從業人員,您可能希望專注於訓練複雜的機器學習模型、執行高效的超引數選擇、構建全新的自定義模型或模擬,或是佈署您的模型以展示其價值。同時,將這些工作負載擴充套件到計算叢集可能是不可避免的。為了實作這一點,您所選擇的分散式系統需要支援所有這些細粒度的「大計算」任務,並可能需要在專用硬體上執行。理想情況下,它還應該融入您正在使用的「大資料」工具鏈,並且足夠快以滿足您的延遲需求。換句話說,分散式計算必須足夠強大和靈活,以滿足複雜的資料科學工作負載——而 Ray 可以幫助您實作這一點。

Python 在資料科學中的角色

Python 可能是當今資料科學中最流行的語言;它確實是我們日常工作中最有用的語言。Python 現在已經超過 30 年的歷史,但它仍然擁有一個不斷成長和活躍的社群。豐富的 PyData 生態系統是資料科學家工具箱中的重要組成部分。如何在擴充套件工作負載的同時仍然利用您需要的工具?這是一個艱難的問題,尤其是因為不能強迫社群拋棄他們的工具箱或程式語言。這意味著為資料科學構建的分散式計算工具必須為他們現有的社群而設計。

為什麼選擇 Ray?

Ray 是一個強大的分散式計算框架,專為滿足複雜的資料科學工作負載而設計。它允許您擴充套件您的 Python 應用程式,使其能夠在多個核心甚至多台機器上執行,從而大大提高了處理速度和效率。Ray 的設計理念是讓您能夠繼續使用您熟悉的工具和語言,同時提供必要的擴充套件能力,以滿足日益增長的計算需求。

Ray 的優勢

  1. 高效的分散式計算:Ray 提供了一個高效的分散式計算框架,能夠支援複雜的機器學習模型訓練、超引數調優等任務。
  2. 靈活性和相容性:Ray 設計為能夠與現有的「大資料」工具鏈無縫整合,並且支援在專用硬體上執行。
  3. 對 Python 的友好支援:作為一個 Python 為主的分散式計算框架,Ray 能夠讓資料科學家繼續使用他們熟悉的語言和工具,同時享受分散式計算的好處。

內容解密:

Ray 的設計使其能夠滿足資料科學家對於高效分散式計算的需求。它不僅支援複雜的機器學習任務,還能夠與現有的工具鏈無縫整合。透過使用 Ray,資料科學家可以繼續使用 Python 和他們熟悉的工具,同時將工作負載擴充套件到計算叢集,從而大大提高了工作效率和模型的訓練速度。

Ray 簡介:Python 主導的分散式運算框架

Ray 是專為 Python 資料科學社群開發的靈活分散式運算框架。它具備簡單易用、核心 API 精簡等特點,讓開發者能夠有效地推理和撰寫分散式程式。使用者可以在筆電上平行化 Python 程式,並在叢集上執行經過本地測試的程式碼,幾乎無需任何修改。Ray 的高階函式庫易於組態,可以無縫協同工作,其中一些函式庫如強化學習函式庫,可能會成為獨立專案並擁有光明前景。

Ray 的特點與優勢

Ray 的核心是以 C++ 構建,但自始就以 Python 為主的框架設計,使其能夠與許多重要的資料科學工具整合,並依靠不斷成長的生態系統。雖然分散式 Python 並非新鮮事物,但 Ray 在其提供的功能上具有特殊性。特別是在結合多個模組並處理自定義的機器學習密集工作負載時,Ray 展現出強大的能力,使分散式運算變得足夠簡單,以利用熟悉的 Python 工具靈活執行複雜的工作負載。

Ray 的由來

程式設計分散式系統非常具有挑戰性,需要特定的知識和經驗。理想情況下,這些系統應該提供抽象化,讓開發者專注於工作本身。然而,在實踐中,正如 Joel Spolsky 所指出的,「所有非平凡的抽象化都在某種程度上是漏抽象」,要讓電腦叢集按照預期運作無疑是困難的。許多軟體系統需要遠超單一伺服器所能提供的資源,即使單一伺服器足夠,現代系統也需要具備高用性等功能。

近年來,機器學習(ML)和人工智慧(AI)領域取得了重大突破,如 Deepmind 的 AlphaFold 和 OpenAI 的 Codex。這些系統通常需要大量資料進行訓練,且模型規模越來越大。OpenAI 的研究顯示,訓練 AI 模型所需的計算量呈指數級增長,自 2012 年以來,每 3.4 個月就翻倍。相比之下,摩爾定律預測電腦電晶體數量每兩年翻倍。即使對摩爾定律持樂觀態度,也可以看出分散式運算在 ML 中的迫切需求。

為何選擇 Ray?

研究人員在加州大學柏克利分校的 RISELab 實驗室建立了 Ray,以解決這些問題。他們尋找有效的方法來加速工作負載,分配任務,並希望建立一個能夠處理分散式工作的系統,具有合理的預設行為,讓研究人員能夠專注於他們的工作,無論叢集的具體情況如何。Ray 強調高效能和異構工作負載,能夠滿足研究人員對 Python 工具的需求。

Ray 的設計哲學

Ray 的設計哲學圍繞著高效能、異構工作負載和以 Python 為主的易用性。這使得 Ray 成為一個強大的分散式運算框架,能夠支援各種資料科學和機器學習任務。

import ray

# 初始化 Ray
ray.init()

# 定義一個簡單的遠端函式
@ray.remote
def hello_world():
    return "Hello, World!"

# 呼叫遠端函式
result = hello_world.remote()
print(ray.get(result))  # 輸出:Hello, World!

內容解密:

此範例展示瞭如何使用 Ray 初始化分散式環境,並定義一個簡單的遠端函式 hello_world。透過 @ray.remote 修飾符,我們將 hello_world 函式標記為可在遠端執行的任務。當呼叫 hello_world.remote() 時,Ray 會將任務分派到可用的工作節點上執行,並傳回一個物件參考。最後,使用 ray.get(result) 取得任務的執行結果。

Ray 的設計原則與架構概述

Ray 的設計建立在多項核心原則之上,其 API 強調簡單性與通用性,而其計算模型則致力於提供靈活性。系統架構方面,Ray 著重於效能與可擴充套件性。以下將探討這些設計原則。

簡潔性與抽象化

Ray 的 API 不僅簡單易用,還具備高度的直觀性(詳見第 2 章)。無論是利用筆電上的所有 CPU 核心,還是呼叫叢集中的多台機器,Ray 的程式碼始終保持高度的一致性。只需修改一兩行程式碼,即可完成從單機到分散式運算的轉換。作為一個優秀的分散式系統,Ray 在底層管理任務分配與協調,讓開發者無需為分散式運算的複雜機制煩惱。這種良好的抽象層設計使開發者能夠專注於工作本身。

由於 Ray 的 API 具備通用性和 Python 風格,易於與其他工具整合。例如,Ray 的 Actor 可以呼叫現有的分散式 Python 工作負載,或被後者呼叫。因此,Ray 也可用作分散式工作負載的「膠水程式碼」,具備高效能和靈活性,能夠在不同系統和框架之間進行通訊。

靈活性與異質性

對於 AI 工作負載,特別是在強化學習等正規化中,需要靈活的程式設計模型。Ray 的 API 設計使其易於撰寫靈活且可組成的程式碼。簡而言之,只要能夠用 Python 表達的工作負載,都可以透過 Ray 進行分散式處理。當然,仍需確保有足夠的資源可用,並謹慎考慮分散式策略。

Ray 在計算異質性方面也具備靈活性。例如,在複雜模擬場景中,各任務或步驟的執行時間可能從幾毫秒到數小時不等,但都需要快速排程和執行。有時單一任務可能耗時較長,但其他較小的任務應能平行執行而不被阻塞。此外,下游任務可能依賴於上游任務的結果,因此需要一個能處理任務依賴關係的動態執行框架。Ray 提供了執行異質工作流程的完全靈活性。

同時,Ray 支援異質硬體,以滿足不同的資源需求。例如,部分任務可能需要在 GPU 上執行,而其他任務則更適合在多個 CPU 核心上執行。Ray 為此提供了靈活的支援。

速度與可擴充套件性

Ray 的另一個設計原則是高效執行任務。它能夠處理每秒數百萬個任務,且延遲極低。Ray 的任務執行延遲僅為毫秒級別。

為了實作高效能,分散式系統必須具備良好的可擴充套件性。Ray 能夠高效地在運算叢集中分配和排程任務,並且具備容錯能力。如第 9 章所述,Ray 叢集支援自動擴充套件,以適應高度彈性的工作負載。Ray 的自動擴充套件器會根據當前需求啟動或停止叢集中的機器,以降低成本並確保叢集具備足夠的資源。

在分散式系統中,故障是必然發生的。機器可能會當機、任務可能會中止,甚至機器可能會完全損壞。Ray 能夠快速從故障中還原,這也是其整體速度的重要組成部分。

Ray 的三層架構

瞭解了 Ray 的設計原則後,接下來探討 Ray 的三層架構:

  1. Ray Core:一個底層的分散式運算框架,提供簡潔的核心 API 和叢集佈署工具。
  2. Ray Libraries:由 Ray 開發者維護的一組高階函式庫,包括 Ray AIR,讓使用者能夠以統一的 API 處理常見的機器學習工作負載。
  3. Ecosystem:不斷成長的整合專案和合作夥伴計畫,涵蓋前兩層的多個方面。

本章將逐一探討這些層級,以全面瞭解 Ray 的功能與應用。

Ray 的架構與應用:從分散式運算到資料科學

Ray 是一個強大的分散式運算框架,能夠無縫地管理電腦叢集並執行分散式任務。其核心 API 簡潔直觀,為開發者提供了靈活的工具來建構各種應用和函式庫。

分散式運算框架

Ray 的核心是一個分散式運算框架,主要負責設定和管理電腦叢集,使開發者能夠在其上執行分散式任務。一個 Ray 叢集由多個節點組成,這些節點透過網路相互連線。開發者透過所謂的「驅動程式」來編寫程式,這個驅動程式執行在頭節點上。驅動程式可以執行多個任務,這些任務會被分配到叢集中的工作節點上執行。

Ray 叢集的基本組成

  • 頭節點(Head Node):執行驅動程式,負責協調任務的分配和執行。
  • 工作節點(Worker Nodes):執行工作程式,實際執行由驅動程式分配的任務。

值得注意的是,Ray 叢集也可以是本地叢集,即僅在本地電腦上執行的單節點叢集。在這種情況下,頭節點同時具備驅動程式和工作程式,預設的工作程式數量等於本地電腦的 CPU 核心數。

安裝與初始化 Ray

要開始使用 Ray,首先需要透過 pip 安裝:

pip install "ray[rllib, serve, tune]==2.2.0"

安裝完成後,可以在 Python 環境中匯入並初始化 Ray:

import ray
ray.init()

這兩行程式碼將在本地電腦上啟動一個 Ray 叢集,利用所有可用的 CPU 核心作為工作程式。

初始化 Ray 的重要性

ray.init() 是 Ray Core API 中的六個基本呼叫之一,用於初始化 Ray 叢集。這個呼叫非常關鍵,因為它決定了 Ray 如何與叢集互動。如果需要在「真正的」叢集上執行 Ray,則需要向 ray.init() 提供額外的引數。

Ray 的資料科學函式庫

Ray 不僅是一個分散式運算框架,還提供了一套完整的資料科學函式庫。這些函式庫建立在 Ray Core 之上,為特定的領域提供了更高層次的抽象。

資料科學的工作流程

資料科學是一個實踐性很強的領域,涉及從資料中取得洞察並構建實用的應用。Ray 的資料科學函式庫旨在簡化這個過程,提供了一套統一的框架來處理常見的 AI 工作負載。

內容解密:

上述內容介紹了 Ray 的基本架構和其在資料科學領域的應用。首先,我們瞭解了 Ray 作為一個分散式運算框架的基本組成和運作原理,包括如何設定和管理叢集,以及如何透過驅動程式來執行分散式任務。接著,我們學習瞭如何安裝和初始化 Ray,以及 ray.init() 呼叫的重要性。最後,我們對 Ray 提供的資料科學函式庫進行了簡要介紹,瞭解了其如何簡化資料科學的工作流程。

Ray 叢集的基本結構

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultTextAlignment center
skinparam rectangleBackgroundColor #F5F5F5
skinparam rectangleBorderColor #333333
skinparam arrowColor #333333

title Ray 叢集的基本結構

rectangle "協調任務" as node1
rectangle "執行任務" as node2

node1 --> node2

@enduml

圖表翻譯: 此圖示展示了 Ray 叢集的基本結構。頭節點負責協調任務,將任務分配給多個工作節點。工作節點執行這些任務並傳回結果。這個結構使得 Ray 能夠高效地進行分散式運算。

隨著資料科學和分散式運算技術的不斷發展,Ray 有望在未來扮演更加重要的角色。透過不斷簡化開發流程和提高運算效率,Ray 能夠幫助開發者和研究人員更快速地實作創新。

資料科學與機器學習的迭代過程

進行資料科學是一個迭代的過程,涵蓋了需求工程、資料收集與處理、模型建立與評估,以及解決方案的佈署。機器學習(ML)並非此過程的必要部分,但經常被納入其中。如果涉及機器學習,可以進一步細化某些步驟:

資料處理

要訓練機器學習模型,需要將資料轉換成模型可理解的格式。這個過程稱為特徵工程(feature engineering),可能相當複雜。如果能依賴通用工具來完成這項工作,將會大有裨益。

模型訓練

在機器學習中,需要使用經過處理的資料來訓練演算法。這包括選擇適合工作的演算法,並且能夠從多種選擇中挑選。

超引數調校

機器學習模型具有在模型訓練步驟中調整的引數。此外,大多數機器學習模型還有另一組稱為超引數(hyperparameters)的引數,可以在訓練前進行修改。這些引數會對最終模型的效能產生重大影響,因此需要適當調校。有優秀的工具可以幫助自動化這個過程。

模型佈署

訓練好的模型需要被佈署。佈署模型意味著使其可供需要者存取,無論採用何種方式。在原型設計中,經常使用簡單的 HTTP 伺服器,但有許多專門的軟體套件可用於機器學習模型佈署。

Ray 為上述四個與機器學習相關的步驟提供了專門的函式庫。具體來說,可以使用 Ray Datasets 處理資料需求,使用 Ray Train 進行分散式模型訓練,使用 Ray RLlib 執行強化學習工作負載,使用 Ray Tune 高效調校超引數,並使用 Ray Serve 佈署模型。由於 Ray 的設計,所有這些函式庫都是分散式的。

使用 Ray Datasets 進行資料處理

Ray Datasets 是 Ray 的第一個高階函式庫,包含了一個名為 Dataset 的資料結構、多個用於從各種格式和系統載入資料的聯結器、一個用於轉換資料集的 API,以及與其他資料處理框架的整合。Dataset 抽象建立在強大的 Arrow 框架之上。

要使用 Ray Datasets,需要安裝 Arrow for Python,例如執行 pip install pyarrow

import ray

# 從 Python 字典建立資料集
items = [{"name": str(i), "data": i} for i in range(10000)]
ds = ray.data.from_items(items)
ds.show(5)

程式碼解析:

  1. import ray:匯入 Ray 函式庫。
  2. items = [{"name": str(i), "data": i} for i in range(10000)]:建立一個包含 10,000 個字典的列表,每個字典包含一個字串名稱和一個整數值。
  3. ds = ray.data.from_items(items):使用 from_items 方法從 Python 列表建立一個分散式的 Dataset。
  4. ds.show(5):列印 Dataset 的前五個元素。

輸出結果應該是:

{'name': '0', 'data': 0}
{'name': '1', 'data': 1}
{'name': '2', 'data': 2}
{'name': '3', 'data': 3}
{'name': '4', 'data': 4}

資料轉換

Dataset API 大量採用函式式程式設計,因為這種正規化非常適合資料轉換。例如,可以使用 mapfilterflat_map 等功能來轉換資料。

squares = ds.map(lambda x: x["data"] ** 2)
evens = squares.filter(lambda x: x % 2 == 0)
evens.count()
cubes = evens.flat_map(lambda x: [x, x**3])
sample = cubes.take(10)
print(sample)

程式碼解析:

  1. squares = ds.map(lambda x: x["data"] ** 2):將每個資料集中的元素對映到其資料值的平方。
  2. evens = squares.filter(lambda x: x % 2 == 0):過濾出偶數值。
  3. evens.count():計算偶數值的數量。
  4. cubes = evens.flat_map(lambda x: [x, x**3]):將每個偶數值對映到其自身和其立方值的列表,並展平結果。
  5. sample = cubes.take(10):取出結果的前 10 個元素。
  6. print(sample):列印結果。

這個例子展示瞭如何使用 Ray Datasets 的 API 對資料進行各種轉換和操作。