-
硬體: NVIDIA DGX Spark
- GPU: NVIDIA GB10 (Blackwell 架構, sm_121)
- 記憶體: 128GB Unified Memory
- CUDA: 12.8
-
目標: 部署 Google TranslateGemma-4B-IT 翻譯模型,支援 55 種語言互譯
DGX Spark 的 GB10 是 Blackwell 架構 (sm_121),一般的 PyTorch CUDA 版本不支援。
# 錯誤訊息
NVIDIA GB10 with CUDA capability sm_121 is not compatible with the current PyTorch installation.解法: 需要安裝 PyTorch nightly cu128 版本
pip install --pre torch torchvision --index-url https://download.pytorch.org/whl/nightly/cu128用 conda 安裝的 PyTorch 可能是 CPU-only 版本。
import torch
print(torch.cuda.is_available()) # False解法: 用 pip 指定 CUDA wheel 安裝
用 HuggingFace transformers 直接載入模型,第一次推理要 60 秒(CUDA kernel 編譯),之後每次也要 9-14 秒。
即使加上 4-bit quantization (bitsandbytes),還是要 9 秒左右。
# bitsandbytes 4-bit 設定
quantization_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_compute_dtype=torch.bfloat16,
bnb_4bit_quant_type="nf4",
bnb_4bit_use_double_quant=True,
)TranslateGemma 不是一般的 chat model,OpenAI-compatible /v1/chat/completions API 不直接支援。
需要用 /v1/completions 搭配特殊的 prompt 格式。
vLLM 的 stop token 對 TranslateGemma 不生效,會一直生成到 max_tokens 為止。
# 錯誤做法
max_tokens=2048 # 即使翻譯只需要 10 個 token,也會生成 2048 個 → 超級慢!
解法: 根據輸入長度動態計算 max_tokens
# 正確做法:根據輸入估算
estimated_tokens = min(max(len(text) * 2, 20), 500)TranslateGemma 支援圖片翻譯(OCR + 翻譯),但 vLLM 的 OpenAI-compatible API 不支援其特殊格式。
# TranslateGemma 圖片格式(vLLM 不支援)
messages = [
{
"role": "user",
"content": [
{
"type": "image",
"source_lang_code": "en",
"target_lang_code": "zh-TW",
"url": image_url,
}
],
}
]解法: 另外啟動一個 transformers pipeline server 處理圖片翻譯(見下方「圖片翻譯」章節)
TranslateGemma 是 gated model,需要:
- 去 https://huggingface.co/google/translategemma-4b-it 同意條款
- 取得 HF Token: https://huggingface.co/settings/tokens
export HF_TOKEN="your_token_here"確保沒有其他程式佔用 GPU 記憶體:
# 查看 GPU 使用狀況
nvidia-smi
# 如果有其他 Python 程式佔用,kill 掉
pkill -f "python.*model"docker run -d \
--name tg-vllm \
--gpus all \
-v ~/.cache/huggingface:/root/.cache/huggingface \
-e HF_TOKEN=$HF_TOKEN \
-p 8603:8000 \
--ipc=host \
nvcr.io/nvidia/vllm:25.11-py3 \
vllm serve google/translategemma-4b-it \
--quantization fp8 \
--gpu-memory-utilization 0.7參數說明:
--gpus all: 使用 GPU-v ~/.cache/huggingface:/root/.cache/huggingface: 掛載 HF cache 避免重複下載-e HF_TOKEN: 傳入 HuggingFace token-p 8603:8000: 對外開放 8603 port--ipc=host: 共享記憶體,提升效能vllm serve: 使用 vLLM 的 serve 命令--quantization fp8: FP8 量化,模型從 8GB 降到 5GB,速度提升 3 倍!--gpu-memory-utilization 0.7: 使用 70% GPU 記憶體
模型載入需要幾分鐘,可以用 logs 追蹤:
docker logs -f tg-vllm看到以下訊息表示準備完成:
INFO: Application startup complete.
完整啟動流程:
- 下載模型權重 (~77 秒)
- 載入 safetensors shards (~66 秒)
- torch.compile 優化 (~19 秒)
- 分配 KV cache (~72 GiB)
- 捕捉 CUDA graphs (~4 秒)
TranslateGemma 需要特殊的 prompt 格式:
curl -s http://localhost:8603/v1/completions \
-H "Content-Type: application/json" \
-d '{
"model": "google/translategemma-4b-it",
"prompt": "<bos><start_of_turn>user\n<translategemma><source_lang_code>en</source_lang_code><target_lang_code>zh-Hans</target_lang_code><text>Hello, how are you?</text></translategemma><end_of_turn>\n<start_of_turn>model\n",
"max_tokens": 50
}'回應:
{
"choices": [{
"text": "你好,你好吗?\n"
}],
"usage": {
"prompt_tokens": 59,
"completion_tokens": 50
}
}| 方案 | 翻譯速度 | 備註 |
|---|---|---|
| transformers (CPU) | 224 秒 | 不可用 |
| transformers (CUDA, 首次) | 60 秒 | CUDA kernel 編譯 |
| transformers (CUDA, warmup 後) | 9-14 秒 | bfloat16 |
| transformers + bitsandbytes 4-bit | 9 秒 | NF4 量化 |
| vLLM bfloat16 (本地) | ~1 秒 | 模型 8.15 GiB |
| vLLM FP8 量化 (本地) | 0.3-1 秒 | 模型 5.03 GiB,推薦! |
TranslateGemma 支援 55 種語言,常用的包括:
| 代碼 | 語言 |
|---|---|
en |
English |
zh-Hans |
簡體中文 |
zh-Hant |
繁體中文 |
ja |
日本語 |
ko |
한국어 |
es |
Español |
fr |
Français |
de |
Deutsch |
ru |
Русский |
ar |
العربية |
import requests
class TranslateGemmaClient:
def __init__(self, base_url="http://localhost:8603"):
self.base_url = base_url
def translate(self, text: str, source_lang: str = "en", target_lang: str = "zh-Hans") -> str:
prompt = f"""<bos><start_of_turn>user
<translategemma><source_lang_code>{source_lang}</source_lang_code><target_lang_code>{target_lang}</target_lang_code><text>{text}</text></translategemma><end_of_turn>
<start_of_turn>model
"""
# 動態計算 max_tokens,避免生成過多無用 token
estimated_tokens = min(max(len(text) * 2, 20), 500)
response = requests.post(
f"{self.base_url}/v1/completions",
json={
"model": "google/translategemma-4b-it",
"prompt": prompt,
"max_tokens": estimated_tokens,
}
)
result = response.json()
return result["choices"][0]["text"].strip()
# 使用範例
client = TranslateGemmaClient("http://spark-bbba.local:8603")
print(client.translate("Hello, how are you?", "en", "zh-Hans"))
# 輸出: 你好,你好吗?提供一個 Gradio 網頁介面方便測試:
# gradio_app.py
import gradio as gr
import requests
import time
API_URL = "http://spark-bbba.local:8603"
LANGUAGES = {
"English": "en",
"簡體中文": "zh-Hans",
"繁體中文": "zh-Hant",
"日本語": "ja",
"한국어": "ko",
}
def translate(text, source_lang, target_lang):
if not text.strip():
return "", ""
src_code = LANGUAGES.get(source_lang, "en")
tgt_code = LANGUAGES.get(target_lang, "zh-Hans")
prompt = f"""<bos><start_of_turn>user
<translategemma><source_lang_code>{src_code}</source_lang_code><target_lang_code>{tgt_code}</target_lang_code><text>{text}</text></translategemma><end_of_turn>
<start_of_turn>model
"""
# 動態計算 max_tokens
estimated_tokens = min(max(len(text) * 2, 20), 500)
start = time.time()
response = requests.post(
f"{API_URL}/v1/completions",
json={
"model": "google/translategemma-4b-it",
"prompt": prompt,
"max_tokens": estimated_tokens,
}
)
elapsed = time.time() - start
result = response.json()
translation = result["choices"][0]["text"].strip()
info = f"⏱ {elapsed:.2f}s"
return translation, info
with gr.Blocks(title="TranslateGemma") as demo:
gr.Markdown("# TranslateGemma 翻譯測試")
with gr.Row():
source_lang = gr.Dropdown(list(LANGUAGES.keys()), value="English", label="來源語言")
target_lang = gr.Dropdown(list(LANGUAGES.keys()), value="繁體中文", label="目標語言")
input_text = gr.Textbox(label="輸入", lines=5)
output_text = gr.Textbox(label="翻譯結果", lines=5)
info_text = gr.Textbox(label="資訊")
btn = gr.Button("翻譯", variant="primary")
btn.click(translate, [input_text, source_lang, target_lang], [output_text, info_text])
demo.launch(server_name="0.0.0.0", server_port=7861)啟動:
python gradio_app.py
# 開啟 http://localhost:7861TranslateGemma 支援從圖片中提取文字並翻譯。由於 vLLM 不支援其特殊格式,需要另外啟動一個 transformers server。
┌─────────────────────────────────────────────────┐
│ Client (Gradio) │
│ ├── 文字翻譯 → vLLM (port 8603) │
│ └── 圖片翻譯 → image_server (port 8604) │
└─────────────────────────────────────────────────┘
#!/usr/bin/env python3
"""TranslateGemma 圖片翻譯 Server"""
import base64
import time
import io
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
import torch
import uvicorn
app = FastAPI(title="TranslateGemma Image API")
pipe = None
class ImageTranslateRequest(BaseModel):
image_base64: str
source_lang: str = "en"
target_lang: str = "zh-TW"
class ImageTranslateResponse(BaseModel):
translation: str
source_lang: str
target_lang: str
elapsed_seconds: float
def load_pipeline():
global pipe
from transformers import pipeline
print("Loading TranslateGemma pipeline...")
pipe = pipeline(
"image-text-to-text",
model="google/translategemma-4b-it",
device="cuda" if torch.cuda.is_available() else "cpu",
torch_dtype=torch.bfloat16,
)
print("Ready!")
@app.on_event("startup")
async def startup_event():
load_pipeline()
@app.get("/health")
async def health():
return {"status": "ok", "pipeline_loaded": pipe is not None}
@app.post("/translate_image", response_model=ImageTranslateResponse)
async def translate_image(request: ImageTranslateRequest):
from PIL import Image
start_time = time.time()
# Decode image
image_data = base64.b64decode(request.image_base64)
image = Image.open(io.BytesIO(image_data))
# Resize if needed
max_size = 896
if image.width > max_size or image.height > max_size:
ratio = min(max_size / image.width, max_size / image.height)
new_size = (int(image.width * ratio), int(image.height * ratio))
image = image.resize(new_size, Image.Resampling.LANCZOS)
if image.mode in ("RGBA", "P"):
image = image.convert("RGB")
# TranslateGemma 圖片格式
messages = [
{
"role": "user",
"content": [
{
"type": "image",
"source_lang_code": request.source_lang,
"target_lang_code": request.target_lang,
"image": image,
}
],
}
]
output = pipe(text=messages, max_new_tokens=200, generate_kwargs={"do_sample": False})
translation = output[0]["generated_text"][-1]["content"]
return ImageTranslateResponse(
translation=translation,
source_lang=request.source_lang,
target_lang=request.target_lang,
elapsed_seconds=round(time.time() - start_time, 3),
)
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8604)# 在 Spark 上執行
cd ~/apps/translategemma
conda activate translategemma
python image_server.py
# 或背景執行
nohup python image_server.py > image_server.log 2>&1 &# 將圖片轉成 base64
IMAGE_BASE64=$(base64 -i test_image.jpg)
# 呼叫 API
curl -X POST http://localhost:8604/translate_image \
-H "Content-Type: application/json" \
-d "{
\"image_base64\": \"$IMAGE_BASE64\",
\"source_lang\": \"en\",
\"target_lang\": \"zh-TW\"
}"- 圖片翻譯會自動 OCR 提取文字再翻譯
- 繁體中文輸出可能需要額外用 OpenCC 轉換(模型訓練資料以簡體為主)
- 建議圖片解析度不要太高(會自動縮到 896x896)
如果不需要量化,可以用原始 bfloat16 精度(模型較大,速度較慢):
docker run -d \
--name tg-vllm \
--gpus all \
-v ~/.cache/huggingface:/root/.cache/huggingface \
-e HF_TOKEN=$HF_TOKEN \
-p 8603:8000 \
--ipc=host \
nvcr.io/nvidia/vllm:25.11-py3 \
vllm serve google/translategemma-4b-it \
--dtype bfloat16 \
--gpu-memory-utilization 0.7# 查看容器狀態
docker ps
# 查看 logs
docker logs -f tg-vllm
# 停止容器
docker stop tg-vllm
# 刪除容器
docker rm tg-vllm
# 重啟容器
docker restart tg-vllmtorch.OutOfMemoryError: CUDA out of memory
解法: 降低 --gpu-memory-utilization 或清理其他 GPU 程式
Cannot access gated repo
解法: 確認 HF_TOKEN 正確且已同意模型條款
curl: (7) Failed to connect
解法: 檢查 container 是否啟動完成 (docker logs)
在 DGX Spark 上部署 TranslateGemma 最佳方案:
使用 NVIDIA vLLM Container + FP8 量化:
- 翻譯速度:0.3-1 秒
- 模型大小:5GB(FP8 量化)
使用 transformers pipeline server:
- 支援 OCR + 翻譯
- 需要額外啟動 image_server.py
┌──────────────────────────────────────────────────────────────┐
│ DGX Spark Server │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Docker: tg-vllm (port 8603) │ │
│ │ └── vLLM + FP8 量化 │ │
│ │ └── 文字翻譯:0.3-1 秒 │ │
│ └─────────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Python: image_server.py (port 8604) │ │
│ │ └── transformers pipeline │ │
│ │ └── 圖片翻譯 (OCR + 翻譯) │ │
│ └─────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────┘
↑
HTTP Request
↑
┌──────────────────────────────────────────────────────────────┐
│ Client (Mac/PC) │
│ └── Gradio UI (port 7861) │
│ ├── 📝 文字翻譯 Tab → port 8603 │
│ └── 🖼️ 圖片翻譯 Tab → port 8604 │
└──────────────────────────────────────────────────────────────┘
- vLLM FP8 量化:
--quantization fp8 - 動態 max_tokens:
min(max(len(text) * 2, 20), 500) - 圖片自動縮放: 896x896 以內
- 分離服務: 文字用 vLLM(快),圖片用 transformers(支援特殊格式)
# 1. 啟動文字翻譯 (vLLM)
docker start tg-vllm # 或用 docker run 指令
# 2. 啟動圖片翻譯 (transformers)
cd ~/apps/translategemma
nohup python image_server.py > image_server.log 2>&1 &
# 3. 本地啟動 Gradio
python gradio_app.py