Triển Khai và DevOps¶
Triển khai (deployment) là nơi mô hình của bạn không còn là một artifact nghiên cứu và bắt đầu là một sản phẩm. File này bao gồm Docker cho ML, phục vụ mô hình (model serving), theo dõi thử nghiệm (experiment tracking), khả năng tái tạo (reproducibility), giám sát trong sản xuất (monitoring), cửa hàng đặc trưng (feature stores) và điều phối pipeline (pipeline orchestration) — cơ sở hạ tầng đưa một mô hình đã huấn luyện từ notebook đến hàng triệu người dùng.
-
Một mô hình chỉ chạy trên laptop của bạn là một nguyên mẫu. Một mô hình chạy đáng tin cậy ở quy mô lớn, phục vụ dự đoán trong mili giây, phục hồi sau sự cố và có thể được cập nhật mà không có thời gian chết là một sản phẩm. Khoảng cách giữa hai thứ này là triển khai và DevOps.
-
Hầu hết các kỹ sư ML dành nhiều thời gian hơn cho việc triển khai, giám sát và gỡ lỗi các vấn đề sản xuất hơn là huấn luyện mô hình. Hiểu về cơ sở hạ tầng này không phải là tùy chọn cho bất kỳ ai xây dựng các hệ thống ML thực tế.
Docker cho ML¶
-
Chúng ta đã đề cập container về mặt khái niệm trong chương 13 (Hệ điều hành). Ở đây chúng ta tập trung vào mặt thực tế: viết Dockerfile cho khối lượng công việc ML.
-
Một Dockerfile là một công thức để xây dựng một image container:
# Bắt đầu từ official CUDA base image
FROM nvidia/cuda:12.1.0-cudnn8-runtime-ubuntu22.04
# Các phụ thuộc hệ thống
RUN apt-get update && apt-get install -y \
python3.11 python3-pip git \
&& rm -rf /var/lib/apt/lists/*
# Các phụ thuộc Python (cài riêng để cache)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy mã nguồn (thay đổi thường xuyên, layer này ở cuối)
COPY src/ /app/src/
COPY configs/ /app/configs/
WORKDIR /app
# Entry point
CMD ["python3", "src/scripts/serve.py", "--config", "configs/serve.yaml"]
-
Layer caching: Docker cache mỗi layer. Nếu
requirements.txtkhông thay đổi,pip installđược bỏ qua khi xây dựng lại. Đặt các layers hiếm khi thay đổi (gói hệ thống, pip install) trước các layers thay đổi thường xuyên (mã nguồn). Điều này biến một bản build 10 phút thành một rebuild 10 giây. -
Truy cập GPU: sử dụng các base image
nvidia/cudavà chạy vớidocker run --gpus all.nvidia-container-toolkitcung cấp khả năng truyền GPU từ máy chủ vào container. -
Multi-stage builds giảm kích thước image bằng cách tách môi trường build khỏi runtime:
# Build stage: cài đặt công cụ build, biên dịch phụ thuộc
FROM python:3.11 AS builder
COPY requirements.txt .
RUN pip install --user -r requirements.txt
# Runtime stage: chỉ phụ thuộc runtime
FROM nvidia/cuda:12.1.0-cudnn8-runtime-ubuntu22.04
COPY --from=builder /root/.local /root/.local
COPY src/ /app/src/
ENV PATH=/root/.local/bin:$PATH
-
Image cuối cùng chỉ chứa các thư viện runtime, không phải trình biên dịch, header hay công cụ build. Một image build 5 GB trở thành image runtime 2 GB.
-
Docker Compose chạy các thiết lập nhiều container (máy chủ mô hình + cân bằng tải + giám sát):
# docker-compose.yml
services:
model:
build: .
ports:
- "8080:8080"
deploy:
resources:
reservations:
devices:
- capabilities: [gpu]
prometheus:
image: prom/prometheus
ports:
- "9090:9090"
Phục Vụ Mô Hình (Model Serving)¶
-
Phục vụ mô hình là chạy suy luận như một dịch vụ: nhận yêu cầu, chạy mô hình, trả về dự đoán.
-
FastAPI (đã đề cập trong file 03) là cách tiếp cận đơn giản nhất cho thông lượng thấp đến trung bình. Đối với thông lượng cao và phục vụ tối ưu GPU, hãy sử dụng các công cụ chuyên dụng:
-
Triton Inference Server (NVIDIA): phục vụ các mô hình ở định dạng TensorRT, ONNX, PyTorch và TensorFlow. Các tính năng:
- Dynamic batching: thu thập các yêu cầu riêng lẻ và gộp chúng thành batch để tận dụng GPU. Một luồng các yêu cầu đơn được gộp thành các nhóm 32, cải thiện đáng kể thông lượng.
- Model ensembles: xâu chuỗi nhiều mô hình (preprocessor → model → postprocessor) trong một yêu cầu duy nhất.
- Multi-model serving: phục vụ nhiều mô hình trên cùng một GPU, chia sẻ tài nguyên.
- Concurrent model execution: chạy nhiều yêu cầu suy luận song song trên cùng một GPU.
-
TorchServe (PyTorch): phục vụ các mô hình PyTorch với API REST/gRPC. Hỗ trợ quản lý phiên bản mô hình, kiểm thử A/B và các custom handler.
-
vLLM: chuyên biệt cho phục vụ LLM. Hiện thực PagedAttention (quản lý KV cache hiệu quả), continuous batching và tensor parallelism trên nhiều GPU. Đạt thông lượng cao hơn 10-20x so với phục vụ ngây thơ cho các mô hình ngôn ngữ lớn.
-
Cactus (github.com/cactus-compute/cactus): một engine AI độ trễ thấp cho phục vụ trên thiết bị (on-device) trên di động và edge. Cactus cung cấp một API tương thích OpenAI (chat completion, streaming, tool calling, transcription, embeddings, RAG, vision) chạy hoàn toàn trên thiết bị, với khả năng dự phòng đám mây (cloud fallback) tự động khi mô hình cục bộ không thể xử lý một yêu cầu. Kiến trúc lai này có nghĩa là mã ứng dụng của bạn sử dụng cùng một API bất kể suy luận chạy cục bộ hay trên đám mây — engine quyết định dựa trên độ tin cậy của mô hình và khả năng của thiết bị. SDK có sẵn cho Python, Swift, Kotlin, Flutter, React Native và Rust, với các trọng số mô hình đã được chuyển đổi trước trên HuggingFace. Hỗ trợ suy luận đa phương thức (LLMs, thị giác, giọng nói) với các kernel ARM SIMD tùy chỉnh cho suy luận nhanh nhất trên CPU ARM và ánh xạ bộ nhớ zero-copy cho mức sử dụng RAM thấp hơn 10x (chương 16, chương 17).
-
Tối ưu hóa định dạng mô hình:
- ONNX: định dạng mở cho khả năng tương tác. Xuất từ PyTorch/TensorFlow, chạy ở bất kỳ đâu.
- TensorRT: bộ tối ưu hóa của NVIDIA. Hợp nhất các layers, chọn kernel tối ưu, lượng tử hóa trọng số. Thường nhanh hơn 2-5x so với PyTorch trên GPU NVIDIA.
- GGUF/GGML: các định dạng cho suy luận CPU hiệu quả, phổ biến để chạy LLM trên phần cứng tiêu dùng.
Theo Dõi Thử Nghiệm (Experiment Tracking)¶
-
Không có theo dõi thử nghiệm, nghiên cứu ML thoái hóa thành: "Tôi nghĩ mô hình từ thứ Ba tuần trước với cấu hình tôi đã thay đổi gì đó là tốt nhất, nhưng tôi không nhớ tôi đã thay đổi gì."
-
Weights & Biases (W&B): trình theo dõi thử nghiệm phổ biến nhất. Ghi log bất cứ thứ gì từ script huấn luyện của bạn:
import wandb
wandb.init(project="my-project", config={
"model": "transformer",
"lr": 3e-4,
"batch_size": 64,
})
for epoch in range(num_epochs):
train_loss = train_one_epoch()
val_loss = validate()
wandb.log({
"train/loss": train_loss,
"val/loss": val_loss,
"epoch": epoch,
})
# Log model as artifact
if val_loss < best_loss:
wandb.save("best_model.pt")
wandb.finish()
-
W&B cung cấp: bảng điều khiển để so sánh các lần chạy, công cụ quét siêu tham số, model registry, quản lý phiên bản tập dữ liệu và cộng tác nhóm.
-
MLflow: giải pháp thay thế mã nguồn mở. Chạy cục bộ hoặc trên máy chủ:
import mlflow
mlflow.set_experiment("my-experiment")
with mlflow.start_run():
mlflow.log_params({"lr": 3e-4, "batch_size": 64})
mlflow.log_metric("val_loss", 0.042, step=epoch)
mlflow.pytorch.log_model(model, "model")
- Model registry: một kho chứa trung tâm các mô hình đã huấn luyện với quản lý phiên bản, giai đoạn (dev → staging → production) và siêu dữ liệu. Cả W&B và MLflow đều cung cấp registry. Registry trả lời: "mô hình nào hiện đang trong sản xuất, ai đã huấn luyện nó, độ chính xác xác thực của nó là bao nhiêu, và mã/dữ liệu nào đã tạo ra nó?"
Khả Năng Tái Tạo (Reproducibility)¶
-
Khả năng tái tạo nghĩa là: với cùng mã, dữ liệu và cấu hình, tạo ra cùng một mô hình. Điều này đáng ngạc nhiên là khó trong ML do tính không tất định trong các thao tác GPU, xáo trộn dữ liệu và tích lũy dấu phẩy động.
-
Danh sách kiểm tra khả năng tái tạo:
| Cái gì | Làm thế nào |
|---|---|
| Phiên bản mã | Git commit hash |
| Cấu hình / siêu tham số | File cấu hình (được version trong git hoặc log lên W&B) |
| Hạt giống ngẫu nhiên | Đặt và log tất cả seeds (Python, NumPy, PyTorch, CUDA) |
| Phiên bản dữ liệu | DVC hash, thẻ phiên bản tập dữ liệu hoặc S3 object version |
| Phụ thuộc | pip freeze, Docker image hash hoặc lockfile |
| Phần cứng | Loại GPU, số lượng GPU, phiên bản CUDA |
| Tính không tất định | torch.backends.cudnn.deterministic = True (chậm hơn nhưng tái tạo được) |
-
Ghim mọi thứ:
pip install torch==2.2.1chứ không phảitorch>=2.0. Một bản bump phiên bản nhỏ có thể thay đổi hành vi số học, hiện thực bộ tối ưu hoặc siêu tham số mặc định. -
Docker cho khả năng tái tạo: Docker image ghim OS, thư viện hệ thống, phiên bản Python và pip packages. Hash của image là một dấu vân tay môi trường hoàn chỉnh. Nếu bạn có thể tái tạo Docker image, bạn có thể tái tạo việc huấn luyện.
Giám Sát trong Sản Xuất¶
-
Triển khai một mô hình không phải là kết thúc — nó là sự khởi đầu của một loạt vấn đề mới. Các mô hình xuống cấp theo thời gian khi thế giới thực thay đổi (concept drift) và khi phân bố dữ liệu đầu vào thay đổi (data drift).
-
Những gì cần giám sát:
-
Độ trễ (Latency): suy luận mất bao lâu? Theo dõi p50 (trung vị), p95 và p99. Một p99 là 500ms nghĩa là 1 trong 100 người dùng chờ nửa giây, điều này có thể không chấp nhận được.
-
Thông lượng (Throughput): bao nhiêu yêu cầu mỗi giây? Hệ thống có theo kịp nhu cầu không?
-
Tỷ lệ lỗi (Error rate): bao nhiêu phần trăm yêu cầu thất bại (ngoại lệ, timeout, đầu vào không hợp lệ)?
-
Chỉ số mô hình: độ chính xác (accuracy), precision, recall trên một tập holdout. Nếu dữ liệu có nhãn có sẵn trong sản xuất (ví dụ, hiệu chỉnh người dùng), hãy theo dõi các chỉ số trực tuyến.
-
Data drift: phân bố dữ liệu đến có thay đổi không? Một mô hình được huấn luyện trên ảnh ban ngày có thể thất bại trên ảnh ban đêm. Các kiểm định thống kê (KS test, PSI) so sánh phân bố huấn luyện với phân bố trực tiếp.
-
Feature drift: phân bố của các đặc trưng riêng lẻ có thay đổi không? Một đặc trưng có phân bố chuẩn trong huấn luyện nhưng giờ có hai đỉnh (bimodal) báo hiệu một vấn đề pipeline dữ liệu.
-
-
Công cụ:
- Prometheus + Grafana: tiêu chuẩn cho giám sát cơ sở hạ tầng. Prometheus thu thập các chỉ số, Grafana trực quan hóa chúng trên bảng điều khiển với cảnh báo.
- Evidently AI: giám sát ML mã nguồn mở. Tạo báo cáo về data drift, hiệu suất mô hình và chất lượng dữ liệu.
-
Cảnh báo: đừng chỉ làm dashboard — hãy thiết lập cảnh báo tự động. "Nếu độ trễ p99 vượt quá 200ms trong 5 phút, gửi thông báo Slack." "Nếu điểm data drift vượt quá ngưỡng, gọi kỹ sư trực."
Cửa Hàng Đặc Trưng (Feature Stores)¶
-
Một cửa hàng đặc trưng (feature store) là một kho lưu trữ tập trung các đặc trưng được tính toán trước, được chia sẻ giữa huấn luyện và phục vụ. Nó giải quyết hai vấn đề:
-
Độ lệch huấn luyện-phục vụ (Training-serving skew): các đặc trưng được sử dụng trong huấn luyện phải giống hệt với các đặc trưng được sử dụng trong phục vụ. Nếu huấn luyện dùng
user_age_at_signuptính theo một cách và phục vụ tính theo cách khác, các dự đoán của mô hình sai một cách lặng lẽ. -
Tái sử dụng đặc trưng: nhiều mô hình thường dùng cùng một đặc trưng (nhân khẩu học người dùng, embedding vật phẩm, thống kê tổng hợp). Tính toán chúng một lần và chia sẻ tránh trùng lặp và không nhất quán.
-
-
Feast là cửa hàng đặc trưng mã nguồn mở phổ biến nhất. Nó quản lý các đặc trưng trực tuyến (độ trễ thấp, được phục vụ từ Redis hoặc DynamoDB) và các đặc trưng ngoại tuyến (batch, được lưu trong kho dữ liệu cho huấn luyện).
-
Các cửa hàng đặc trưng rất quan trọng cho hệ thống gợi ý, phát hiện gian lận và bất kỳ ứng dụng nào nơi các đặc trưng được tính toán từ các pipeline dữ liệu thô.
Điều Phối Pipeline¶
-
Một hệ thống ML sản xuất không chỉ là một mô hình. Nó là một pipeline: thu thập dữ liệu → tiền xử lý → tính toán đặc trưng → huấn luyện → đánh giá → triển khai → giám sát. Mỗi bước phụ thuộc vào bước trước, có thể thất bại độc lập và có thể cần chạy theo các lịch trình khác nhau.
-
Bộ điều phối (Orchestrators) quản lý các pipeline này:
-
Apache Airflow: tiêu chuẩn cho điều phối pipeline dữ liệu. DAGs (Directed Acyclic Graphs) định nghĩa các phụ thuộc tác vụ. Mỗi tác vụ chạy độc lập, có thể thử lại khi thất bại và được giám sát qua web UI.
# airflow DAG example (simplified)
from airflow import DAG
from airflow.operators.python import PythonOperator
dag = DAG("training_pipeline", schedule="@daily")
preprocess = PythonOperator(task_id="preprocess", python_callable=preprocess_data, dag=dag)
train = PythonOperator(task_id="train", python_callable=train_model, dag=dag)
evaluate = PythonOperator(task_id="evaluate", python_callable=evaluate_model, dag=dag)
deploy = PythonOperator(task_id="deploy", python_callable=deploy_model, dag=dag)
preprocess >> train >> evaluate >> deploy
-
Kubeflow Pipelines: điều phối ML cụ thể trên Kubernetes. Mỗi bước chạy trong một container, tài nguyên GPU được cấp phát theo yêu cầu và các thử nghiệm được theo dõi tự động.
-
Prefect và Dagster: các giải pháp thay thế hiện đại cho Airflow với trải nghiệm nhà phát triển tốt hơn, API Python gốc và dòng dõi dữ liệu (data lineage) tích hợp sẵn.
-
Khi nào nên điều phối: khi pipeline của bạn có nhiều hơn 2-3 bước, chạy theo lịch trình, liên quan đến nhiều nhóm hoặc dịch vụ, hoặc cần phục hồi tự động sau thất bại. Một công việc huấn luyện script đơn không cần bộ điều phối. Một pipeline huấn luyện hàng ngày thu thập dữ liệu từ 5 nguồn, huấn luyện 3 mô hình, đánh giá chúng và triển khai mô hình tốt nhất chắc chắn có cần.