在 Python 開發中,有效管理依賴關係至關重要,它直接影響專案的可維護性、可重現性和佈署效率。本文將探討如何使用 Poetry 精確管理 Python 依賴,並結合 Docker 容器化技術,構建一致且可移植的開發與佈署環境。同時,我們將介紹如何利用 Docker 建立多平臺映像,解決不同 CPU 架構的相容性問題,並使用 batect 簡化容器管理流程,提升團隊協作效率。最後,我們將探討一些 Docker 的核心概念和在不同開發環境中的應用,幫助讀者更深入地理解容器技術的優勢和最佳實務。

在機器學習專案中,管理依賴關係非常重要,它可以確保程式碼在不同環境中穩定執行。使用 piprequirements.txt 管理依賴關係是一種常見的做法,但它存在一些缺點,例如無法精確指定版本、難以管理間接依賴關係等。Poetry 是一款現代化的 Python 依賴管理工具,它可以解決這些問題,並提供更便捷的依賴管理方式。Poetry 使用 pyproject.toml 檔案來管理依賴關係,並使用 poetry.lock 檔案來鎖定依賴關係的版本,確保專案的可重現性。

實踐依賴管理

透過遵循這些原則,我們可以建立一致、可重現和高效的依賴管理系統。這樣可以幫助我們減少錯誤,提高開發效率,同時確保應用程式在不同環境中的一致性。

# 依賴管理示例
import os
import sys

# 定義依賴關係
dependencies = {
    'numpy': '1.20.0',
    'pandas': '1.3.5'
}

# 安裝依賴關係
for package, version in dependencies.items():
    os.system(f'pip install {package}=={version}')

# 驗證依賴關係
for package, version in dependencies.items():
    import importlib
    module = importlib.import_module(package)
    if module.__version__ != version:
        print(f'依賴關係 {package} 版本不正確')
        sys.exit(1)

print('依賴關係驗證完成')

內容解密:

這個示例展示瞭如何定義和管理依賴關係。首先,我們定義了一個字典 dependencies 來儲存依賴關係的名稱和版本。然後,我們使用 os.system 函式來安裝這些依賴關係。最後,我們使用 importlib 函式來驗證依賴關係的版本是否正確。如果版本不正確,則離開程式。

圖表翻譯:

  graph LR
    A[定義依賴關係] --> B[安裝依賴關係]
    B --> C[驗證依賴關係]
    C --> D[完成]

這個圖表展示了依賴管理的過程。首先,我們定義依賴關係,然後安裝它們,最後驗證它們的版本是否正確。

玄貓:環境依賴管理的重要性

在進行機器學習(ML)開發時,管理環境依賴是一個至關重要的步驟。環境依賴是指在不同應用程式之間分享的設定和依賴項,例如Python版本、套件和作業系統設定。當一個應用程式意外地修改了另一個應用程式的環境依賴時,可能會導致錯誤和不一致性,從而減慢了我們實作想法的速度。

環境隔離工具

有許多工具可以幫助我們建立和管理隔離的虛擬環境,例如Python的內建venv模組、poetrypipenvconda。無論您選擇哪種工具,請確保每個應用程式都有自己的依賴項,可以在其自己的虛擬環境中安裝。

作業系統級別的環境隔離

在大多數情況下,我們需要在多個作業系統上執行ML開發,例如在Windows或MacOS上開發,在Linux上測試,在雲端或目標裝置上佈署。即使整個團隊使用相同的作業系統(例如Linux),我們仍需要確保OS級別的依賴項可以在臨時目標例項上重現,例如在佈署期間。

玄貓:管理OS級別依賴

ML從業者通常熟悉Python中的應用級別依賴管理,但我們經常忽略以類似的方式管理OS級別依賴(即作為程式碼)。更常見的是在README中提供手動安裝某些依賴項(例如Python 3)的指示,並將具體版本(例如Python 3.x)留給機會。

忽略OS級別依賴管理的成本是浪費時間排除由雪花環境引起的問題和錯誤。如果這一切聽起來太抽象,只需回想一下您曾經克隆了一個儲存函式庫以嘗試執行某些程式碼或筆記本,但卻花了數小時甚至數天排除由 玄貓 引起的錯誤。

工具和技術

現在您已經掌握了指導原則,讓我們來看看可以幫助我們實踐這些原則的工具和技術。

玄貓:指定OS級別依賴

要有效地管理給定專案的依賴項,我們需要:

  • 以程式碼指定OS級別依賴(例如,使用Docker)
  • 以程式碼指定應用級別依賴(例如,使用Poetry)

在這本章中,我們決定使用Docker和Poetry。雖然我們在過去幾年中使用了Python生態系統中的幾種工具,但Docker和Poetry在其他工具中脫穎而出,對我們來說效果很好。然而,我們承認任何專案或團隊中選擇任何工具都取決於多種因素。無論您選擇哪種工具,只要確保以符合前面描述的四個原則的方式使用它們即可。

讓我們開始使用Docker。

玄貓:管理OS級別依賴

容器封裝您的程式碼以及整個依賴堆積疊,這給您提供了一個可攜帶、可重現和一致的執行時環境,適用於您的程式碼和機器。以下是使用Docker管理OS級別依賴的範例:

# 使用Dockerfile指定OS級別依賴
FROM python:3.9-slim

# 安裝所需套件
RUN pip install -r requirements.txt

# 複製程式碼到容器中
COPY . /app

# 設定工作目錄
WORKDIR /app

# 執行命令
CMD ["python", "app.py"]
# 使用Poetry指定應用級別依賴
import os

# 安裝所需套件
os.system("poetry install")

# 執行命令
os.system("poetry run python app.py")

圖表翻譯:

此圖示 Docker 和 Poetry 如何協同工作,以管理 OS 級別和應用級別依賴。

  flowchart TD
    A[指定OS級別依賴] --> B[使用Docker]
    B --> C[封裝程式碼和依賴]
    C --> D[建立可攜帶和可重現的執行時環境]
    E[指定應用級別依賴] --> F[使用Poetry]
    F --> G[安裝所需套件]
    G --> H[執行命令]

在這個範例中,我們使用Docker指定OS級別依賴,並使用Poetry指定應用級別依賴。這樣可以確保我們的程式碼在不同的機器上都可以正常執行,並且可以輕鬆地重現和分享。

使用Docker簡化ML開發環境

在機器學習(ML)開發中,開發環境的複雜性和不一致性往往會導致許多問題。Docker是一種容器化技術,可以幫助解決這些問題。透過使用Docker,開發者可以確保其程式碼在不同的計算環境中(例如本地開發機、雲端訓練例項、生產API伺服器、CI/CD管道)的一致性和可靠性。

Docker的優點

Docker可以幫助開發者:

  • 確保環境的一致性:Docker可以確保開發環境的一致性,無論是在本地還是在雲端。
  • 簡化依賴管理:Docker可以幫助管理依賴項,包括作業系統級別的依賴項,如Python、gcc和CUDA。
  • 提高開發效率:Docker可以幫助開發者快速地建立和刪除開發環境,減少環境組態的時間和努力。

常見的誤解

有三個常見的誤解可能會阻礙開發者使用Docker:

  1. Docker過於複雜和不必要:一些開發者認為Docker過於複雜和不必要,但事實上,Docker是一種相對簡單的工具,可以帶來很多好處。
  2. 不需要Docker,因為已經使用了X(例如conda):Docker和其他Python依賴管理工具(如conda、pip、poetry和pip-tools)是不同的工具,解決不同的問題。Docker可以幫助管理作業系統級別的依賴項,而其他工具主要關注應用級別的Python依賴項。
  3. Docker會對效能產生重大影響:事實上,Docker的效能影響相對較小,尤其是與其帶來的好處相比。

Mermaid圖表

  graph LR
    A[開發環境] -->|Docker|> B[容器化]
    B --> C[環境的一致性]
    C --> D[簡化依賴管理]
    D --> E[提高開發效率]

圖表翻譯

這個Mermaid圖表展示了使用Docker的過程。開發環境透過Docker容器化,從而實作環境的一致性、簡化依賴管理和提高開發效率。這個圖表簡單地展示了Docker的好處和其在機器學習開發中的應用。

最佳化 Docker 容器的效能

當面對執行時間長且速度慢的程式碼時,我們有足夠的時間思考許多相關問題——生命的意義是什麼?我的程式碼為什麼這麼慢?就像時間匱乏的遊牧民族在時間稀缺的沙漠中,我們可能開始看到海市蜃樓,甚至想要質問 Docker:你是否減慢了我的程式碼的速度?

在思考最佳化時,千萬不要忘記最佳化的第三規則:最佳化前先衡量。在進行基準分析時,對於計算密集型的深度學習任務,無論是在容器內還是容器外執行,都沒有發現明顯的效能差異。另一項基準分析比較了在容器內和主機上執行各種深度學習模型(例如 InceptionV3、VGG16)的效能,發現效能差異接近於零。

Docker 有時會減慢 Python 的效能,但效能下降並不總是保持一致。效能下降可能是由於 Docker 的安全功能之一——seccomp 引起的。使用本章的程式碼範例,我們重現了一個效能影響——平均減慢 1.7%——當在 Docker 和主機上執行 python src/train.py 時(見表 3-1)。在兩種情況下,我們都使用 Python 3.10.6。然而,當我們檢視效能分解時,減慢主要是由於從磁碟讀取資料。處理資料和訓練模型的程式碼在效能上是相同的,如果不是更好。

表 3-1. 基準測試本章模型訓練的執行時間

執行程式碼平均執行時間(10 次執行,秒)主機執行時間容器執行時間
import time
import numpy as np

# 執行時間基準測試
def benchmark_run(code_to_run, num_runs=10):
    durations = []
    for _ in range(num_runs):
        start_time = time.time()
        exec(code_to_run)
        end_time = time.time()
        durations.append(end_time - start_time)
    return np.mean(durations)

# 主機和容器執行時間基準測試
host_duration = benchmark_run("python src/train.py")
container_duration = benchmark_run("docker run python src/train.py")

print(f"主機執行時間:{host_duration} 秒")
print(f"容器執行時間:{container_duration} 秒")

內容解密:

上述程式碼使用 timenumpy 函式庫進行基準測試。benchmark_run 函式執行指定的程式碼多次,計算每次執行的時間,並傳回平均執行時間。然後,程式碼使用這個函式基準測試主機和容器上的 python src/train.py 執行時間。

圖表翻譯:

  flowchart TD
    A[基準測試] --> B[主機執行時間]
    A --> C[容器執行時間]
    B --> D[計算平均執行時間]
    C --> D
    D --> E[比較主機和容器執行時間]

此圖表展示了基準測試的流程,從基準測試開始,分別計算主機和容器上的執行時間,然後比較兩者的平均執行時間。

處理不同CPU架構下的Docker容器化應用

在使用Docker進行容器化佈署時,常常會遇到一個問題:如何確保應用程式可以在不同CPU架構下的機器上正確執行。這個問題尤其是在團隊合作中更加明顯,因為團隊成員可能使用不同的機器,例如Intel Mac和M1/M2 Mac。

問題根源

問題的根源在於不同的CPU架構使用不同的指令集。例如,Intel Mac使用AMD64(x86_64)指令集,而M1/M2 Mac使用ARM64(aarch64)指令集。這意味著在一臺機器上編譯的程式碼可能無法在另一臺使用不同CPU架構的機器上執行。

解決方案

為瞭解決這個問題,可以採用以下幾種方法:

  1. 使用多架構Docker映像: 可以建立一個包含多個架構的Docker映像,例如同時包含x86_64和aarch64架構的映像。這樣可以確保應用程式可以在不同CPU架構下的機器上執行。
  2. 使用ARM64相容的Docker映像: 如果應用程式只需要在ARM64架構下的機器上執行,可以使用ARM64相容的Docker映像。
  3. 使用雲端服務: 可以使用雲端服務,例如AWS或Google Cloud,提供多架構支援的Docker容器化服務。

實踐案例

下面是一個使用多架構Docker映像的實踐案例:

# 使用多架構Docker映像
FROM --platform=linux/amd64 python:3.9-slim
FROM --platform=linux/arm64 python:3.9-slim

# 安裝依賴項
RUN pip install -r requirements.txt

# 複製應用程式程式碼
COPY . /app

# 執行應用程式
CMD ["python", "app.py"]

在這個例子中,建立了一個包含多個架構的Docker映像,同時包含x86_64和aarch64架構的映像。這樣可以確保應用程式可以在不同CPU架構下的機器上執行。

跨平臺 Docker 影像建置與應用級別相依性管理

在開發和佈署多平臺應用時,團隊需要確保 Docker 影像可以在不同 CPU 架構(如 AMD64 和 ARM64)上順暢執行。然而,Python 套件維護者可能沒有為所有 CPU 指令集提供預編譯的 wheels,導致套件安裝失敗。

套件安裝失敗的典型情景

當開發者新增新的 Python 套件到專案中時,可能會因為套件維護者沒有提供相應的 CPU 架構 wheels 而導致安裝失敗。例如,Dana 在她的 AMD64 機器上成功安裝了套件 xyz,但 Ted 的 ARM64 機器上安裝失敗,因為套件維護者沒有提供 ARM64 wheels。

解決方案:在 Dockerfile 中安裝 gcc 編譯器

為瞭解決這個問題,可以在 Dockerfile 中安裝 gcc 編譯器,以便在沒有預編譯 wheels 的情況下編譯套件。這可以透過以下 Dockerfile 指令實作:

FROM python:3.10-slim-bookworm

# ...

RUN apt-get update && apt-get -y install gcc
RUN poetry install

# ...

建立多平臺 Docker 影像

除了安裝 gcc 編譯器外,團隊還需要確保 Docker 影像可以在多個平臺上執行。為此,可以使用 Docker 的 buildx 工具建立多平臺 Docker 影像。這需要確保基礎影像支援多個架構,例如 ARM64 和 AMD64,跨越相關的作業系統(Linux、MacOS、Windows)。

應用級別相依性管理

在管理應用級別相依性時,需要選擇適合的工具,以確保它們在開發和生產環境中均能正常工作。例如,conda 可以用於開發,但需要確保它也能在生產環境中工作。

圖表翻譯:

  graph LR
    A[開發環境] -->|套件安裝|> B[套件維護者]
    B -->|預編譯 wheels|> C[PyPI]
    C -->|套件下載|> D[生產環境]
    D -->|套件安裝|> E[應用執行]
    style A fill:#f9f,stroke:#333,stroke-width:4px
    style B fill:#f9f,stroke:#333,stroke-width:4px
    style C fill:#f9f,stroke:#333,stroke-width:4px
    style D fill:#f9f,stroke:#333,stroke-width:4px
    style E fill:#f9f,stroke:#333,stroke-width:4px

內容解密:

以上 Dockerfile 指令和 Mermaid 圖表展示瞭如何建立多平臺 Docker 影像和管理應用級別相依性。透過安裝 gcc 編譯器和使用 buildx 工具,團隊可以確保 Docker 影像可以在多個平臺上執行,並管理應用級別相依性以確保應用在生產環境中順暢執行。

使用Poetry進行依賴管理

在進行機器學習模型的開發和佈署時,依賴管理是一個非常重要的方面。傳統上,我們可能會使用conda來管理依賴,但這種方法有其侷限性。例如,conda的映象檔案可能很大(3-6 GB),且包含許多不必要的依賴。此外,使用conda進行開發和佈署時,可能會引入訓練和服務的不對稱性,以及依賴管理解決方案的分岔。

為瞭解決這些問題,我們可以使用Poetry進行依賴管理。Poetry是一個現代化的Python依賴管理工具,具有以下優點:

  • 依賴釘選:Poetry可以自動解析依賴樹,包括間接依賴,並生成一個poetry.lock檔案來釘選所有依賴。這可以防止間接依賴在未來發生變化時破壞程式碼。
  • 自動更新pyproject.toml:當我們使用poetry add命令新增新的依賴時,Poetry會自動更新pyproject.toml檔案,以反映最新的依賴版本。
  • 單一的pyproject.toml檔案:Poetry使用pyproject.toml檔案來定義生產和開發依賴,這是一個標準化的格式,符合PEP 518。

此外,Poetry還提供了方便的包裝和發布功能,使我們可以輕鬆地將包發布到PyPI或私有倉函式庫。

Docker和batect入門

在機器學習工作流程中,Docker和batect是兩個非常重要的工具。Docker是一個容器化平臺,允許我們封裝和佈署應用程式,而batect是一個命令列工具,簡化了我們與Docker的互動。

以下是Docker和batect的一些基本概念:

  • Docker:Docker是一個容器化平臺,允許我們封裝和佈署應用程式。它提供了一個輕量級和可移植的方式來佈署應用程式。
  • batect:batect是一個命令列工具,簡化了我們與Docker的互動。它允許我們定義和管理Docker容器, 以及簡化了Docker的使用。

使用Docker和batect,可以簡化機器學習工作流程中的容器化和佈署過程。以下是一個簡單的例子:

# 安裝Docker和batect
pip install docker batect

# 定義Docker容器
docker run -it --name my-container my-image

# 使用batect管理Docker容器
batect run my-container

這只是Docker和batect的一個簡單入門。隨著我們深入探索這兩個工具,我們將看到它們如何簡化機器學習工作流程中的容器化和佈署過程。

圖表翻譯:

  graph LR
    A[Docker] --> B[Containerization]
    B --> C[Deployment]
    C --> D[batect]
    D --> E[Container Management]
    E --> F[Simplified Docker Usage]

這個圖表展示了Docker、Containerization、Deployment、batect、Container Management和Simplified Docker Usage之間的關係。它表明Docker和batect如何簡化機器學習工作流程中的容器化和佈署過程。

容器技術簡介

容器技術是一種讓應用程式能夠在不同的環境中執行的方法,無論是本地還是雲端。這項技術的核心概念是將應用程式和其依賴的軟體包裝成一個容器,讓它可以在任何支援容器的環境中執行。

容器的優點

容器技術有許多優點,包括:

  • 環境隔離:容器可以提供一個隔離的環境,讓應用程式可以在不影響主機系統的情況下執行。
  • 依賴管理:容器可以管理應用程式的依賴,讓開發人員可以專注於開發應用程式,而不需要擔心依賴的問題。
  • 輕量化:容器比傳統的虛擬機器更輕量化,讓它可以更快速地啟動和停止。
  • 可擴充套件性:容器可以輕易地擴充套件,讓應用程式可以在多個容器中執行。

容器的應用

容器技術在許多領域中都有應用,包括:

  • 網頁開發:容器可以用來開發和測試網頁應用程式,讓開發人員可以在本地環境中模擬生產環境。
  • 雲端計算:容器可以用來佈署應用程式到雲端,讓應用程式可以在多個節點中執行。
  • DevOps:容器可以用來自動化開發和佈署的流程,讓開發人員可以更快速地推出新功能。

容器技術的實踐

容器技術的實踐包括以下幾個步驟:

  1. 建立容器映像:建立一個包含應用程式和其依賴的容器映像。
  2. 啟動容器:啟動容器,讓應用程式可以執行。
  3. 管理容器:管理容器,包括啟動、停止和刪除容器。
內容解密:

以上內容介紹了容器技術的基本概念和優點。容器技術是一種讓應用程式能夠在不同的環境中執行的方法,無論是本地還是雲端。它的核心概念是將應用程式和其依賴的軟體包裝成一個容器,讓它可以在任何支援容器的環境中執行。容器技術的優點包括環境隔離、依賴管理、輕量化和可擴充套件性。容器技術在許多領域中都有應用,包括網頁開發、雲端計算和DevOps。

# 容器技術示例
import docker

# 建立一個容器映像
client = docker.from_env()
image = client.images.build(path=".", tag="my_image")

# 啟動容器
container = client.containers.run(image, detach=True)

# 管理容器
print(container.status)

圖表翻譯:

以下是容器技術的流程圖:

  graph LR
    A[建立容器映像] --> B[啟動容器]
    B --> C[管理容器]
    C --> D[刪除容器]

此圖表示了容器技術的流程,包括建立容器映像、啟動容器、管理容器和刪除容器。透過這個流程,開發人員可以更快速地推出新功能,讓應用程式可以在多個環境中執行。

Docker 概念解析

在 Docker 的世界中,可能會遇到一些看似複雜的術語。瞭解這些術語的意義是克服初學者恐懼的第一步。讓我們來看看一些常見的 Docker 概念:

1. Image

Image 是一個輕量級、獨立的可執行軟體包,包含了執行程式碼所需的所有東西。

2. Container

Container 是 Image 的執行例項。當你執行一個 Image 時,就會產生一個 Container。

3. Docker Daemon(或 Docker Engine)

Docker Daemon 是一個在主機上執行的背景程式,負責管理 Image 和 Container,例如啟動和停止 Container。

4. Docker CLI Client

Docker CLI Client 是用於與 Docker Daemon 互動的命令列工具。當我們執行 Docker 命令時,例如 docker run,就會使用 Docker Client。

5. Docker Desktop

Docker Desktop 是一個為 Mac、Windows 和 Linux 平臺提供的一致的 Docker 開發環境。它包含了 Docker Engine、Docker CLI Client、Docker Compose、Docker Content Trust、Kubernetes 和 Credential Helper。

6. Docker Registry

Docker Registry 是一個允許我們推播和提取 Image 的主機服務。

7. Host

Host 是一個執行 Docker Daemon 和 Container 的機器,例如開發者的筆電或 CI 建置代理。

Docker 概念之間的關係

以下是 Docker 概念之間的關係圖:

  flowchart TD
    A[Image] --> B[Container]
    B --> C[Docker Daemon]
    C --> D[Docker CLI Client]
    D --> E[Docker Desktop]
    E --> F[Docker Registry]
    F --> G[Host]

圖表翻譯:

這個圖表顯示了 Docker 概念之間的關係。Image 是最基本的單位,Container 是 Image 的執行例項。Docker Daemon 負責管理 Image 和 Container,Docker CLI Client 是用於與 Docker Daemon 互動的工具。Docker Desktop 是一個提供一致的 Docker 開發環境的平臺,Docker Registry 是一個允許我們推播和提取 Image 的主機服務。Host 是一個執行 Docker Daemon 和 Container 的機器。

Docker 在開發環境中的應用

Docker 可以在各種開發環境中使用,例如:

1. 本地機器

本地機器可以執行 Docker 容器,或者觸發雲端或分散式叢集上的工作。

2. 雲端平臺

雲端平臺可以提供 Docker 容器的主機服務,例如 AWS、Google Cloud 和 Azure。

內容解密:

Docker 的概念和技術可以應用於各種開發環境中,提供了一種一致的和高效的方式來管理和佈署應用程式。透過瞭解 Docker 的概念和技術,開發者可以更好地利用 Docker 的功能和優勢,提高開發效率和應用程式的品質。

隨著容器化技術的普及,Docker 已成為機器學習開發流程中不可或缺的工具。本文深入探討了 Docker 從依賴管理、跨平臺相容性到效能最佳化的各個導向,並佐以程式碼範例和圖表說明,展現其在簡化開發流程、提升環境一致性方面的顯著優勢。然而,Docker 並非萬靈丹,仍需注意不同 CPU 架構的相容性問題,並搭配 Poetry 等工具進行應用級別的依賴管理,才能最大程度發揮其效能。對於追求高效能的深度學習任務,更需仔細評估 Docker 的效能影響,並針對性地進行最佳化。玄貓認為,熟練掌握 Docker 等容器化技術,並與其他開發工具協同使用,將是未來機器學習工程師的必備技能,有效提升開發效率並降低維護成本。