Hugging Face Spaces部署实战完全教程
更新时间:2025年 | 适用对象:ML工程师、全栈开发者、AI产品经理、独立开发者
目录
- 一、Spaces平台概述
- 二、三种SDK架构详解:Gradio / Streamlit / Docker
- 三、模型Demo快速搭建
- 四、自定义Docker部署
- 五、GPU Spaces使用
- 六、环境变量与密钥管理
- 七、CI/CD集成
- 八、流量与资源管理
- 九、与Vercel / Railway对比
- 十、商业化部署方案
- 十一、性能优化
- 十二、最佳实践总结
一、Spaces平台概述
1.1 什么是Hugging Face Spaces
Hugging Face Spaces是Hugging Face提供的免费托管平台,专门用于部署和展示机器学习应用。它的核心价值:
- 零运维部署:无需管理服务器,推送代码即部署
- 原生ML支持:原生支持GPU推理、模型加载、Gradio/Streamlit
- 社区曝光:部署在Hugging Face生态内,容易获得社区关注
- 免费额度:CPU Spaces完全免费,GPU Spaces有免费额度
- Git工作流:基于Git的部署方式,开发者熟悉
1.2 Spaces的核心概念
Space = Git仓库 + 运行环境 + 持久存储 + 可选GPU
一个Space包含:
├── app.py (或其他入口文件)
├── requirements.txt / Dockerfile
├── README.md (含YAML元数据)
├── .gitattributes
└── 其他资源文件
README.md中的YAML元数据是Space的配置核心:
---
title: My Awesome App
emoji: 🚀
colorFrom: blue
colorTo: purple
sdk: gradio
sdk_version: 4.44.0
app_file: app.py
pinned: false
license: apache-2.0
---
1.3 资源规格
| 规格 | CPU | 内存 | GPU | 价格 |
|---|---|---|---|---|
| Free CPU | 2 vCPU | 16GB RAM | 无 | 免费 |
| CPU Basic | 2 vCPU | 16GB RAM | 无 | 免费 |
| CPU Upgrade | 8 vCPU | 32GB RAM | 无 | $0.03/h |
| T4 Small | 4 vCPU | 15GB RAM | T4 16GB | $0.06/h |
| T4 Medium | 8 vCPU | 30GB RAM | T4 16GB | $0.10/h |
| A10G Small | 4 vCPU | 15GB RAM | A10G 24GB | $0.20/h |
| A10G Large | 12 vCPU | 46GB RAM | A10G 24GB | $0.40/h |
| A100 Large | 12 vCPU | 142GB RAM | A100 80GB | $1.50/h |
二、三种SDK架构详解:Gradio / Streamlit / Docker
2.1 Gradio(推荐入门)
Gradio是Hugging Face官方的ML Demo框架,与Spaces深度集成。
优势:
- 3行代码即可创建ML Demo
- 自动生成美观的Web界面
- 内置分享功能(生成公开链接)
- 支持输入组件(文本、图片、音频、文件)和输出组件
- 与Hugging Face Hub模型无缝集成
基础示例:
import gradio as gr
from transformers import pipeline
# 加载模型
classifier = pipeline("sentiment-analysis")
def analyze_sentiment(text):
result = classifier(text)
label = result[0]["label"]
score = result[0]["score"]
return f"情感:{label}\n置信度:{score:.2%}"
# 创建界面
demo = gr.Interface(
fn=analyze_sentiment,
inputs=gr.Textbox(label="输入文本", placeholder="请输入要分析的文本..."),
outputs=gr.Textbox(label="分析结果"),
title="📊 情感分析 Demo",
description="使用Hugging Face Transformers进行文本情感分析",
examples=[
["今天天气真好,心情很愉快!"],
["这个产品质量太差了,非常失望。"],
["今天的会议还行,没什么特别的。"]
],
theme=gr.themes.Soft()
)
demo.launch()
requirements.txt:
gradio==4.44.0
transformers==4.44.0
torch==2.4.0
高级Gradio:Blocks布局
import gradio as gr
with gr.Blocks(title="AI多功能工具箱", theme=gr.themes.Monochrome()) as demo:
gr.Markdown("# 🛠️ AI多功能工具箱")
gr.Markdown("集成多种AI能力的一站式工具")
with gr.Tabs():
with gr.TabItem("📝 文本分析"):
with gr.Row():
text_input = gr.Textbox(label="输入文本", lines=5)
text_output = gr.Textbox(label="分析结果", lines=5)
analyze_btn = gr.Button("分析", variant="primary")
analyze_btn.click(fn=analyze_text, inputs=text_input, outputs=text_output)
with gr.TabItem("🖼️ 图片生成"):
with gr.Row():
prompt_input = gr.Textbox(label="描述你想要的图片")
image_output = gr.Image(label="生成结果")
generate_btn = gr.Button("生成", variant="primary")
generate_btn.click(fn=generate_image, inputs=prompt_input, outputs=image_output)
with gr.TabItem("🎵 音频处理"):
audio_input = gr.Audio(label="上传音频")
audio_output = gr.Audio(label="处理结果")
process_btn = gr.Button("处理", variant="primary")
process_btn.click(fn=process_audio, inputs=audio_input, outputs=audio_output)
demo.launch()
2.2 Streamlit
Streamlit适合数据应用和仪表盘场景。
优势:
- 适合数据可视化和交互式仪表盘
- 语法简洁,快速原型开发
- 丰富的组件库(图表、地图、数据表格)
- 支持实时更新和流式输出
基础示例:
import streamlit as st
from transformers import pipeline
import pandas as pd
st.set_page_config(page_title="AI数据分析", layout="wide")
st.title("📊 AI数据分析平台")
# 侧边栏配置
with st.sidebar:
st.header("配置")
model_name = st.selectbox(
"选择模型",
["bert-base-chinese", "roberta-base", "distilbert-base"]
)
threshold = st.slider("置信度阈值", 0.0, 1.0, 0.5)
# 主界面
tab1, tab2, tab3 = st.tabs(["文本分析", "数据可视化", "模型对比"])
with tab1:
text = st.text_area("输入文本", height=150)
if st.button("分析", type="primary"):
with st.spinner("正在分析..."):
result = analyze(text)
st.json(result)
with tab2:
# 数据可视化
chart_data = pd.DataFrame(
np.random.randn(20, 3),
columns=["准确率", "召回率", "F1分数"]
)
st.line_chart(chart_data)
with tab3:
col1, col2 = st.columns(2)
with col1:
st.subheader("模型A")
st.metric("准确率", "92.3%", "1.2%")
with col2:
st.subheader("模型B")
st.metric("准确率", "94.1%", "2.8%")
Streamlit的requirements.txt:
streamlit==1.38.0
transformers==4.44.0
torch==2.4.0
pandas==2.2.0
plotly==5.24.0
2.3 Docker(完全自定义)
Docker SDK提供最大的灵活性,适合复杂应用。
优势:
- 完全控制运行环境
- 可以运行任何框架(Flask、FastAPI、Next.js等)
- 支持自定义端口和服务
- 可以运行多个服务
基础Dockerfile:
FROM python:3.11-slim
WORKDIR /app
# 安装系统依赖
RUN apt-get update && apt-get install -y \
git \
ffmpeg \
libsm6 \
libxext6 \
&& rm -rf /var/lib/apt/lists/*
# 安装Python依赖
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 复制应用代码
COPY . .
# 暴露端口(Spaces要求7860)
EXPOSE 7860
# 启动应用
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860"]
FastAPI应用示例(main.py):
from fastapi import FastAPI, UploadFile, File
from fastapi.responses import HTMLResponse, JSONResponse
from transformers import pipeline
import tempfile
import os
app = FastAPI(title="ML API Service")
# 全局模型加载
model_cache = {}
def get_model(task: str):
if task not in model_cache:
model_cache[task] = pipeline(task)
return model_cache[task]
@app.get("/", response_class=HTMLResponse)
async def root():
return """
<html>
<head><title>ML API</title></head>
<body>
<h1>🤖 ML API Service</h1>
<ul>
<li><a href="/docs">API文档 (Swagger)</a></li>
<li><a href="/redoc">API文档 (ReDoc)</a></li>
</ul>
</body>
</html>
"""
@app.post("/api/sentiment")
async def sentiment_analysis(text: str):
model = get_model("sentiment-analysis")
result = model(text)
return JSONResponse(content={"result": result})
@app.post("/api/ner")
async def named_entity_recognition(text: str):
model = get_model("ner")
result = model(text)
return JSONResponse(content={"entities": result})
@app.post("/api/transcribe")
async def transcribe_audio(audio: UploadFile = File(...)):
with tempfile.NamedTemporaryFile(delete=False, suffix=".wav") as tmp:
content = await audio.read()
tmp.write(content)
tmp_path = tmp.name
try:
model = get_model("automatic-speech-recognition")
result = model(tmp_path)
return JSONResponse(content={"transcription": result["text"]})
finally:
os.unlink(tmp_path)
@app.get("/health")
async def health_check():
return {"status": "healthy", "models_loaded": list(model_cache.keys())}
2.4 三种SDK选择决策
你的应用是什么类型?
│
├─ ML模型Demo/原型 → Gradio(最快上手)
│
├─ 数据仪表盘/可视化 → Streamlit
│
├─ 复杂Web应用/API服务 → Docker
│
├─ 需要自定义前端 → Docker(FastAPI + 静态前端)
│
└─ 多服务架构 → Docker(Docker Compose)
三、模型Demo快速搭建
3.1 从零到部署:5分钟搭建文本分类Demo
完整项目结构:
my-text-classifier/
├── app.py
├── requirements.txt
└── README.md
app.py:
import gradio as gr
from transformers import pipeline
# 使用pipeline加载预训练模型
classifier = pipeline(
"zero-shot-classification",
model="MoritzLaurberbert-base-chinese"
)
CANDIDATE_LABELS = ["科技", "体育", "娱乐", "财经", "教育", "健康", "时政"]
def classify_text(text):
if not text.strip():
return {"请输入文本": 0.0}
result = classifier(text, CANDIDATE_LABELS)
# 格式化输出
output = {}
for label, score in zip(result["labels"], result["scores"]):
output[label] = round(score, 4)
return output
# 创建界面
demo = gr.Interface(
fn=classify_text,
inputs=gr.Textbox(
label="📰 新闻文本",
placeholder="粘贴一段新闻文本...",
lines=5
),
outputs=gr.Label(label="分类结果", num_top_classes=5),
title="📰 中文新闻分类器",
description="基于BERT的中文新闻零样本分类",
examples=[
["苹果公司今日发布了全新的iPhone 16系列,搭载A18芯片,性能提升30%。"],
["中国女排在世界杯小组赛中以3:0战胜巴西队,取得开门红。"],
["教育部发布通知,要求各地加强中小学人工智能教育。"],
],
allow_flagging="never"
)
demo.launch()
requirements.txt:
gradio==4.44.0
transformers==4.44.0
torch==2.4.0
README.md:
---
title: 中文新闻分类器
emoji: 📰
colorFrom: blue
colorTo: green
sdk: gradio
sdk_version: 4.44.0
app_file: app.py
pinned: true
license: apache-2.0
short_description: 基于BERT的中文新闻零样本分类
tags:
- text-classification
- chinese
- nlp
---
# 中文新闻分类器
这是一个基于BERT的中文新闻零样本分类Demo。
## 支持的分类标签
- 科技、体育、娱乐、财经、教育、健康、时政
## 使用方法
1. 在输入框中粘贴一段新闻文本
2. 点击"Submit"按钮
3. 查看分类结果和置信度
3.2 图像分类Demo
import gradio as gr
from transformers import pipeline
from PIL import Image
# 图像分类pipeline
image_classifier = pipeline(
"image-classification",
model="google/vit-base-patch16-224"
)
def classify_image(image):
if image is None:
return {"请上传图片": 0.0}
results = image_classifier(image)
return {r["label"]: round(r["score"], 4) for r in results[:5]}
demo = gr.Interface(
fn=classify_image,
inputs=gr.Image(label="上传图片", type="pil"),
outputs=gr.Label(label="分类结果", num_top_classes=5),
title="🖼️ 图像分类器",
description="基于ViT的图像分类,支持1000个ImageNet类别",
examples=[
["examples/cat.jpg"],
["examples/dog.jpg"],
["examples/car.jpg"],
],
allow_flagging="never"
)
demo.launch()
3.3 语音识别Demo
import gradio as gr
from transformers import pipeline
# 语音识别pipeline
transcriber = pipeline(
"automatic-speech-recognition",
model="openai/whisper-base",
chunk_length_s=30
)
def transcribe(audio):
if audio is None:
return "请上传或录制音频"
result = transcriber(audio)
return result["text"]
demo = gr.Interface(
fn=transcribe,
inputs=gr.Audio(label="上传或录制音频", type="filepath"),
outputs=gr.Textbox(label="转录结果", lines=10),
title="🎤 语音识别 (Whisper)",
description="基于OpenAI Whisper的语音转文字",
allow_flagging="never"
)
demo.launch()
四、自定义Docker部署
4.1 复杂应用的Docker部署
当应用需要:
- 自定义Web框架(FastAPI、Flask、Express)
- 多个服务(前端+后端+数据库)
- 系统级依赖(FFmpeg、Tesseract、LibreOffice)
- 自定义构建流程
此时需要使用Docker SDK。
4.2 完整的Docker项目结构
my-ml-app/
├── Dockerfile
├── docker-compose.yml
├── requirements.txt
├── main.py
├── models/
│ └── download_models.py
├── static/
│ ├── index.html
│ ├── style.css
│ └── app.js
├── utils/
│ ├── __init__.py
│ └── preprocessing.py
└── README.md
4.3 生产级Dockerfile
# 多阶段构建
FROM python:3.11-slim as builder
WORKDIR /app
# 安装构建依赖
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
&& rm -rf /var/lib/apt/lists/*
# 安装Python依赖
COPY requirements.txt .
RUN pip install --no-cache-dir --user -r requirements.txt
# 运行阶段
FROM python:3.11-slim
WORKDIR /app
# 安装运行时系统依赖
RUN apt-get update && apt-get install -y --no-install-recommends \
ffmpeg \
libsm6 \
libxext6 \
tesseract-ocr \
tesseract-ocr-chi-sim \
&& rm -rf /var/lib/apt/lists/*
# 复制Python包
COPY --from=builder /root/.local /root/.local
ENV PATH=/root/.local/bin:$PATH
# 复制应用
COPY . .
# 预下载模型(可选,避免首次请求慢)
RUN python models/download_models.py
# 健康检查
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
CMD python -c "import requests; requests.get('http://localhost:7860/health')" || exit 1
EXPOSE 7860
# 使用gunicorn + uvicorn workers
CMD ["gunicorn", "main:app", \
"--workers", "2", \
"--worker-class", "uvicorn.workers.UvicornWorker", \
"--bind", "0.0.0.0:7860", \
"--timeout", "120"]
4.4 模型预下载策略
# models/download_models.py
"""在Docker构建阶段预下载模型,避免运行时下载"""
from transformers import pipeline, AutoModel, AutoTokenizer
import os
MODELS_TO_DOWNLOAD = [
{
"name": "sentiment-analysis",
"model": "lxyuan/distilbert-base-multilingual-cased-sentiments-student",
"task": "text-classification"
},
{
"name": "summarization",
"model": "facebook/bart-large-cnn",
"task": "summarization"
},
{
"name": "translation-zh-en",
"model": "Helsinki-NLP/opus-mt-zh-en",
"task": "translation"
}
]
def download_all():
cache_dir = os.environ.get("HF_HOME", "/app/models/cache")
os.makedirs(cache_dir, exist_ok=True)
for model_info in MODELS_TO_DOWNLOAD:
print(f"Downloading {model_info['name']}...")
try:
# 下载并缓存模型
AutoModel.from_pretrained(
model_info["model"],
cache_dir=cache_dir
)
AutoTokenizer.from_pretrained(
model_info["model"],
cache_dir=cache_dir
)
print(f" ✓ {model_info['name']} downloaded")
except Exception as e:
print(f" ✗ Failed to download {model_info['name']}: {e}")
if __name__ == "__main__":
download_all()
4.5 多服务Docker部署
# Dockerfile for multi-service app
FROM python:3.11-slim
WORKDIR /app
# 安装所有依赖
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
# 使用supervisor管理多服务
RUN apt-get update && apt-get install -y supervisor && rm -rf /var/lib/apt/lists/*
COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
EXPOSE 7860
CMD ["/usr/bin/supervisord"]
; supervisord.conf
[supervisord]
nodaemon=true
logfile=/var/log/supervisor/supervisord.log
[program:api]
command=uvicorn api.main:app --host 0.0.0.0 --port 7860
directory=/app
autostart=true
autorestart=true
stdout_logfile=/var/log/supervisor/api.log
[program:worker]
command=celery -A worker.app worker --loglevel=info
directory=/app
autostart=true
autorestart=true
stdout_logfile=/var/log/supervisor/worker.log
[program:scheduler]
command=python scheduler.py
directory=/app
autostart=true
autorestart=true
stdout_logfile=/var/log/supervisor/scheduler.log
五、GPU Spaces使用
5.1 何时需要GPU
以下场景必须使用GPU:
- 大模型推理(7B+参数模型)
- 实时图像生成(Stable Diffusion、DALL-E)
- 视频处理
- 实时语音识别
- 微调模型
5.2 GPU Space配置
方式1:在README.md中配置
---
title: Stable Diffusion XL
emoji: 🎨
colorFrom: purple
colorTo: pink
sdk: gradio
sdk_version: 4.44.0
app_file: app.py
pinned: true
hardware: gpu-a10g-small # 指定GPU规格
---
方式2:在Space设置页面配置
进入Space → Settings → Hardware → 选择GPU规格
5.3 GPU优化示例:Stable Diffusion
import gradio as gr
import torch
from diffusers import StableDiffusionPipeline, DPMSolverMultistepScheduler
# GPU检测与优化
device = "cuda" if torch.cuda.is_available() else "cpu"
dtype = torch.float16 if device == "cuda" else torch.float32
# 加载模型
model_id = "stabilityai/stable-diffusion-2-1"
pipe = StableDiffusionPipeline.from_pretrained(
model_id,
torch_dtype=dtype,
safety_checker=None, # 禁用NSFW检查以加速
)
pipe = pipe.to(device)
# 使用更快的调度器
pipe.scheduler = DPMSolverMultistepScheduler.from_config(
pipe.scheduler.config
)
# 启用优化
if device == "cuda":
pipe.enable_attention_slicing() # 减少显存占用
# pipe.enable_xformers_memory_efficient_attention() # 如果安装了xformers
def generate_image(prompt, negative_prompt, steps, guidance_scale, width, height):
image = pipe(
prompt=prompt,
negative_prompt=negative_prompt,
num_inference_steps=steps,
guidance_scale=guidance_scale,
width=width,
height=height,
).images[0]
return image
demo = gr.Interface(
fn=generate_image,
inputs=[
gr.Textbox(label="正向提示词", placeholder="一只可爱的猫咪,水彩画风格"),
gr.Textbox(label="负向提示词", placeholder="模糊,低质量"),
gr.Slider(minimum=1, maximum=50, value=20, label="推理步数"),
gr.Slider(minimum=1.0, maximum=20.0, value=7.5, label="引导系数"),
gr.Slider(minimum=256, maximum=1024, value=512, step=64, label="宽度"),
gr.Slider(minimum=256, maximum=1024, value=512, step=64, label="高度"),
],
outputs=gr.Image(label="生成结果"),
title="🎨 Stable Diffusion 图像生成",
allow_flagging="never"
)
demo.launch()
5.4 GPU显存优化技巧
import torch
# 1. 使用半精度浮点
model = model.half() # 或 torch.float16
# 2. 启用梯度检查点(训练时)
model.gradient_checkpointing_enable()
# 3. 使用attention slicing(推理时)
pipe.enable_attention_slicing(slice_size="auto")
# 4. 使用sequential CPU offload(显存不足时)
pipe.enable_sequential_cpu_offload()
# 5. 使用model CPU offload(平衡速度和显存)
pipe.enable_model_cpu_offload()
# 6. 清理显存缓存
torch.cuda.empty_cache()
# 7. 监控显存使用
def log_gpu_memory():
if torch.cuda.is_available():
allocated = torch.cuda.memory_allocated() / 1024**3
reserved = torch.cuda.memory_reserved() / 1024**3
print(f"GPU Memory: {allocated:.2f}GB allocated, {reserved:.2f}GB reserved")
5.5 GPU Spaces的睡眠与唤醒
GPU Spaces有自动睡眠机制:
- 48小时无请求后自动休眠
- 休眠后再次访问会自动唤醒(约30-60秒)
- 休眠期间不计费
防止睡眠的方法:
# 方法1:使用keepalive脚本
import threading
import time
import requests
def keep_alive():
"""定期发送请求防止Space休眠"""
while True:
try:
requests.get("https://your-space.hf.space/health")
except:
pass
time.sleep(3600) # 每小时ping一次
# 方法2:使用外部定时服务(如cron-job.org)
六、环境变量与密钥管理
6.1 为什么需要环境变量
在Spaces中使用环境变量的场景:
- API密钥(OpenAI、Hugging Face Token)
- 数据库连接字符串
- 第三方服务配置
- 环境标识(dev/prod)
6.2 配置环境变量
方式1:通过Web界面
Space → Settings → Repository secrets → New secret
方式2:通过Python API
from huggingface_hub import HfApi
api = HfApi()
# 设置secret
api.add_space_secret(
repo_id="username/my-space",
key="OPENAI_API_KEY",
value="sk-..."
)
方式3:通过CLI
# 安装huggingface_hub
pip install huggingface_hub
# 登录
huggingface-cli login
# 设置secret
huggingface-cli repo secret set OPENAI_API_KEY --repo username/my-space
6.3 在代码中使用环境变量
import os
# 读取环境变量
openai_key = os.environ.get("OPENAI_API_KEY")
hf_token = os.environ.get("HF_TOKEN")
database_url = os.environ.get("DATABASE_URL")
# 带默认值
debug = os.environ.get("DEBUG", "false").lower() == "true"
port = int(os.environ.get("PORT", "7860"))
# 安全检查
if not openai_key:
raise ValueError("OPENAI_API_KEY environment variable is required")
6.4 多环境配置管理
# config.py
import os
from dataclasses import dataclass
from typing import Optional
@dataclass
class Config:
"""应用配置"""
# 环境
environment: str = os.environ.get("ENVIRONMENT", "development")
# API配置
openai_api_key: str = os.environ.get("OPENAI_API_KEY", "")
hf_token: str = os.environ.get("HF_TOKEN", "")
# 数据库
database_url: str = os.environ.get("DATABASE_URL", "sqlite:///local.db")
# 功能开关
enable_caching: bool = os.environ.get("ENABLE_CACHING", "true").lower() == "true"
max_upload_size: int = int(os.environ.get("MAX_UPLOAD_SIZE", "10485760")) # 10MB
# 日志
log_level: str = os.environ.get("LOG_LEVEL", "INFO")
@property
def is_production(self) -> bool:
return self.environment == "production"
@property
def is_development(self) -> bool:
return self.environment == "development"
# 全局配置实例
config = Config()
七、CI/CD集成
7.1 Git推送即部署
Spaces默认的部署方式就是Git推送:
# 克隆Space仓库
git clone https://huggingface.co/spaces/username/my-space
cd my-space
# 修改代码
# ...
# 推送即部署
git add .
git commit -m "Update: add new feature"
git push
7.2 GitHub Actions自动同步
如果你的代码在GitHub上,可以配置自动同步到Spaces:
# .github/workflows/sync-to-space.yml
name: Sync to Hugging Face Space
on:
push:
branches: [main]
jobs:
sync:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Sync to HF Space
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./space
destination_dir: .
external_repository: username/my-space
personal_token: ${{ secrets.HF_TOKEN }}
force_orphan: true
7.3 自动化测试 + 部署
# .github/workflows/test-and-deploy.yml
name: Test and Deploy to Spaces
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install dependencies
run: |
pip install -r requirements.txt
pip install pytest pytest-cov
- name: Run tests
run: |
pytest tests/ -v --cov=app --cov-report=xml
- name: Upload coverage
uses: codecov/codecov-action@v4
with:
file: ./coverage.xml
deploy:
needs: test
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Deploy to HF Space
run: |
pip install huggingface_hub
huggingface-cli login --token ${{ secrets.HF_TOKEN }}
# 使用API部署
python -c "
from huggingface_hub import HfApi
api = HfApi()
api.upload_folder(
repo_id='username/my-space',
repo_type='space',
folder_path='./space',
)
"
7.4 自动化模型更新
# scripts/update_model.py
"""自动更新Space中的模型版本"""
from huggingface_hub import HfApi, hf_hub_download
import json
import os
def update_model():
api = HfApi()
# 检查最新模型版本
model_info = api.model_info("my-org/my-model")
latest_version = model_info.sha[:8]
# 读取当前版本
config_path = "space/model_config.json"
if os.path.exists(config_path):
with open(config_path) as f:
current_version = json.load(f).get("version", "unknown")
else:
current_version = "none"
if latest_version != current_version:
print(f"New model version found: {latest_version}")
# 更新配置
with open(config_path, "w") as f:
json.dump({"version": latest_version, "model_id": "my-org/my-model"}, f)
# 触发Space重新部署
api.restart_space(repo_id="username/my-space")
print("Space restart triggered")
else:
print("Model is up to date")
if __name__ == "__main__":
update_model()
八、流量与资源管理
8.1 免费Space的限制
| 限制项 | 免费CPU | 付费CPU | 付费GPU |
|---|---|---|---|
| CPU | 2 vCPU | 8 vCPU | 4-12 vCPU |
| 内存 | 16GB | 32GB | 15-142GB |
| 存储 | 50GB | 50GB | 50GB |
| 带宽 | 有限 | 较高 | 较高 |
| 并发 | 有限 | 较高 | 取决于GPU |
| 睡眠 | 48h无请求休眠 | 可配置 | 48h无请求休眠 |
8.2 流量优化策略
策略1:请求限流
from collections import defaultdict
import time
class RateLimiter:
"""简单的请求限流器"""
def __init__(self, max_requests: int = 10, window_seconds: int = 60):
self.max_requests = max_requests
self.window_seconds = window_seconds
self.requests = defaultdict(list)
def is_allowed(self, client_id: str) -> bool:
now = time.time()
# 清理过期记录
self.requests[client_id] = [
t for t in self.requests[client_id]
if now - t < self.window_seconds
]
if len(self.requests[client_id]) >= self.max_requests:
return False
self.requests[client_id].append(now)
return True
# 在Gradio中使用
rate_limiter = RateLimiter(max_requests=10, window_seconds=60)
def predict_with_limit(text, request: gr.Request):
client_id = request.client.host
if not rate_limiter.is_allowed(client_id):
raise gr.Error("请求过于频繁,请稍后再试")
return predict(text)
策略2:结果缓存
import hashlib
from functools import lru_cache
import diskcache
# 内存缓存(适合小规模)
@lru_cache(maxsize=1000)
def cached_predict(text: str) -> str:
return model(text)
# 磁盘缓存(适合大规模)
cache = diskcache.Cache("/tmp/prediction_cache")
def predict_with_cache(text: str) -> str:
# 计算缓存key
cache_key = hashlib.md5(text.encode()).hexdigest()
# 检查缓存
result = cache.get(cache_key)
if result is not None:
return result
# 计算结果
result = model(text)
# 缓存结果(1小时过期)
cache.set(cache_key, result, expire=3600)
return result
策略3:资源监控
import psutil
import GPUtil
def get_resource_status() -> dict:
"""获取当前资源使用状态"""
cpu_percent = psutil.cpu_percent(interval=1)
memory = psutil.virtual_memory()
status = {
"cpu_percent": cpu_percent,
"memory_percent": memory.percent,
"memory_available_gb": memory.available / (1024**3),
}
# GPU信息(如果有)
try:
gpus = GPUtil.getGPUs()
if gpus:
gpu = gpus[0]
status["gpu_name"] = gpu.name
status["gpu_load"] = gpu.load * 100
status["gpu_memory_used"] = gpu.memoryUsed
status["gpu_memory_total"] = gpu.memoryTotal
except:
pass
return status
# 在应用中暴露资源状态
@app.get("/status")
async def status():
return get_resource_status()
8.3 持久化存储
Spaces提供持久化存储,数据在重启后不会丢失:
import os
# 持久化存储路径
PERSISTENT_DIR = "/data"
CACHE_DIR = os.path.join(PERSISTENT_DIR, "cache")
MODEL_DIR = os.path.join(PERSISTENT_DIR, "models")
DATA_DIR = os.path.join(PERSISTENT_DIR, "data")
# 确保目录存在
for dir_path in [CACHE_DIR, MODEL_DIR, DATA_DIR]:
os.makedirs(dir_path, exist_ok=True)
# 使用示例:保存用户上传的文件
def save_upload(file_content: bytes, filename: str) -> str:
filepath = os.path.join(DATA_DIR, filename)
with open(filepath, "wb") as f:
f.write(file_content)
return filepath
九、与Vercel / Railway对比
9.1 平台对比总览
| 维度 | HF Spaces | Vercel | Railway |
|---|---|---|---|
| 定位 | ML应用托管 | 前端/JAMstack | 通用应用托管 |
| 免费额度 | CPU免费 | 100GB带宽/月 | $5信用/月 |
| GPU支持 | ✅ 多种规格 | ❌ | ❌ |
| 冷启动 | 有(免费版) | 无(Edge) | 有 |
| 自定义域名 | ✅ | ✅ | ✅ |
| 数据库 | 外部 | Vercel Postgres | 内置 |
| ML生态 | ⭐⭐⭐⭐⭐ | ⭐ | ⭐⭐ |
| 前端部署 | ⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ |
| 部署速度 | 中等 | 快 | 快 |
| 并发能力 | 中等 | 高 | 高 |
9.2 适用场景对比
选HF Spaces的场景:
- ML模型Demo和原型
- 需要GPU推理
- 面向AI/ML社区展示
- 不需要高并发
选Vercel的场景:
- 前端应用(React、Next.js)
- 需要Edge Functions
- 高性能Web应用
- JAMstack架构
选Railway的场景:
- 全栈应用(需要数据库)
- 后端API服务
- 需要持久化运行
- 团队协作项目
9.3 混合部署方案
实际项目中,可以组合使用多个平台:
┌─────────────────────────────────────────────┐
│ 架构示例 │
├─────────────────────────────────────────────┤
│ │
│ ┌──────────┐ ┌──────────┐ │
│ │ Vercel │ │ HF Space │ │
│ │ 前端应用 │───→│ ML推理 │ │
│ │ (Next.js)│ │ (Gradio) │ │
│ └──────────┘ └──────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────────────────┐ │
│ │ Railway │ │
│ │ 后端API + 数据库 │ │
│ └─────────────────────────┘ │
│ │
└─────────────────────────────────────────────┘
十、商业化部署方案
10.1 Spaces的商业化限制
免费Spaces不适合直接用于商业产品,原因:
- 可能有排队等待
- 48小时休眠
- 无法保证SLA
- 带宽和并发有限制
10.2 商业化方案一:升级Spaces
使用付费GPU Spaces + 自定义域名:
# README.md
---
title: Commercial AI Service
sdk: docker
app_file: Dockerfile
hardware: gpu-a10g-large
sleep_timeout: 7200 # 2小时无请求才休眠
---
优点: 简单,无需运维 缺点: 成本较高,灵活性有限
10.3 商业化方案二:自托管
从Spaces导出,部署到自己的服务器:
# 1. 导出Space代码
git clone https://huggingface.co/spaces/username/my-space
# 2. 构建Docker镜像
docker build -t my-ml-app .
# 3. 推送到容器仓库
docker tag my-ml-app registry.example.com/my-ml-app:latest
docker push registry.example.com/my-ml-app:latest
# 4. 部署到Kubernetes
kubectl apply -f k8s-deployment.yaml
Kubernetes部署配置:
# k8s-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: ml-app
spec:
replicas: 3
selector:
matchLabels:
app: ml-app
template:
metadata:
labels:
app: ml-app
spec:
containers:
- name: ml-app
image: registry.example.com/my-ml-app:latest
ports:
- containerPort: 7860
resources:
requests:
memory: "8Gi"
cpu: "2"
nvidia.com/gpu: "1"
limits:
memory: "16Gi"
cpu: "4"
nvidia.com/gpu: "1"
env:
- name: OPENAI_API_KEY
valueFrom:
secretKeyRef:
name: ml-secrets
key: openai-api-key
readinessProbe:
httpGet:
path: /health
port: 7860
initialDelaySeconds: 60
periodSeconds: 10
---
apiVersion: v1
kind: Service
metadata:
name: ml-app-service
spec:
type: LoadBalancer
ports:
- port: 80
targetPort: 7860
selector:
app: ml-app
10.4 商业化方案三:Inference API
使用Hugging Face Inference API,无需管理基础设施:
import requests
API_URL = "https://api-inference.huggingface.co/models/my-org/my-model"
headers = {"Authorization": "Bearer hf_xxxxxxxxxxxxxxxxxxxxx"}
def query(payload):
response = requests.post(API_URL, headers=headers, json=payload)
return response.json()
# 使用
result = query({"inputs": "分析这段文本的情感"})
print(result)
Inference API定价(2025年参考):
- 免费额度:有限的调用次数
- 按需付费:根据模型大小和调用量计费
- 专用端点:独占GPU,按小时计费
十一、性能优化
11.1 模型加载优化
# 问题:每次请求都加载模型(慢)
def bad_predict(text):
model = pipeline("text-classification") # 每次都加载!
return model(text)
# 方案1:全局变量
model = None
def get_model():
global model
if model is None:
model = pipeline("text-classification")
return model
def good_predict(text):
return get_model()(text)
# 方案2:使用FastAPI的lifespan
from contextlib import asynccontextmanager
from fastapi import FastAPI
@asynccontextmanager
async def lifespan(app: FastAPI):
# 启动时加载模型
app.state.model = pipeline("text-classification")
yield
# 关闭时清理
del app.state.model
app = FastAPI(lifespan=lifespan)
@app.post("/predict")
async def predict(text: str):
result = app.state.model(text)
return {"result": result}
11.2 批处理优化
import asyncio
from collections import deque
import time
class BatchProcessor:
"""批量处理请求,提高GPU利用率"""
def __init__(self, model, max_batch_size=32, max_wait_ms=100):
self.model = model
self.max_batch_size = max_batch_size
self.max_wait_ms = max_wait_ms
self.queue = deque()
self.results = {}
async def predict(self, text: str, request_id: str) -> str:
"""添加到批处理队列"""
future = asyncio.Future()
self.queue.append((text, request_id, future))
# 等待批处理完成
result = await future
return result
async def process_batch(self):
"""持续处理批次"""
while True:
if len(self.queue) == 0:
await asyncio.sleep(0.01)
continue
# 收集批次
batch = []
while len(batch) < self.max_batch_size and self.queue:
batch.append(self.queue.popleft())
if not batch:
continue
# 批量推理
texts = [item[0] for item in batch]
results = self.model(texts, batch_size=len(texts))
# 返回结果
for (_, request_id, future), result in zip(batch, results):
future.set_result(result)
# 使用
batch_processor = BatchProcessor(pipeline("sentiment-analysis"))
@app.post("/predict")
async def predict(text: str):
request_id = str(time.time())
result = await batch_processor.predict(text, request_id)
return {"result": result}
11.3 推理优化
import torch
# 1. 使用ONNX Runtime
from optimum.onnxruntime import ORTModelForSequenceClassification
from transformers import AutoTokenizer, pipeline
model = ORTModelForSequenceClassification.from_pretrained(
"my-model",
export=True
)
tokenizer = AutoTokenizer.from_pretrained("my-model")
classifier = pipeline("text-classification", model=model, tokenizer=tokenizer)
# 2. 使用TensorRT(NVIDIA GPU)
# 需要额外安装torch-tensorrt
# 3. 模型量化
from transformers import BitsAndBytesConfig
quantization_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_compute_dtype=torch.float16,
bnb_4bit_quant_type="nf4"
)
model = AutoModelForCausalLM.from_pretrained(
"my-model",
quantization_config=quantization_config,
device_map="auto"
)
# 4. 使用Flash Attention
model = AutoModel.from_pretrained(
"my-model",
attn_implementation="flash_attention_2",
torch_dtype=torch.float16
)
11.4 前端性能优化
# Gradio优化配置
demo = gr.Interface(
fn=predict,
inputs=gr.Textbox(),
outputs=gr.Textbox(),
# 启用队列
concurrency_limit=5, # 最大并发数
)
# 启动时配置
demo.queue(
max_size=20, # 队列最大长度
default_concurrency_limit=5
)
demo.launch(
max_threads=10, # 最大线程数
share=False, # 不创建公开链接
server_name="0.0.0.0",
server_port=7860
)
11.5 性能监控
import time
import logging
from functools import wraps
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
def monitor_performance(func):
"""性能监控装饰器"""
@wraps(func)
def wrapper(*args, **kwargs):
start_time = time.time()
start_memory = get_memory_usage()
try:
result = func(*args, **kwargs)
status = "success"
except Exception as e:
status = "error"
raise
finally:
end_time = time.time()
end_memory = get_memory_usage()
latency = end_time - start_time
memory_delta = end_memory - start_memory
logger.info(
f"Function: {func.__name__} | "
f"Latency: {latency:.3f}s | "
f"Memory delta: {memory_delta:.2f}MB | "
f"Status: {status}"
)
return result
return wrapper
def get_memory_usage() -> float:
"""获取当前内存使用(MB)"""
import psutil
process = psutil.Process()
return process.memory_info().rss / (1024 * 1024)
# 使用
@monitor_performance
def predict(text):
return model(text)
十二、最佳实践总结
12.1 部署清单
在部署Space之前,检查以下清单:
## 部署前检查清单
### 代码质量
- [ ] 所有依赖版本固定(requirements.txt)
- [ ] 没有硬编码的密钥或token
- [ ] 错误处理完善
- [ ] 日志记录合理
### 性能
- [ ] 模型在启动时加载(而非每次请求)
- [ ] 启用了结果缓存(适用场景)
- [ ] 设置了合理的并发限制
- [ ] GPU显存优化(如适用)
### 安全
- [ ] 敏感信息使用secrets管理
- [ ] 输入验证完善
- [ ] 文件上传有大小限制
- [ ] API有基本的认证机制
### 可靠性
- [ ] 健康检查端点正常
- [ ] 超时设置合理
- [ ] 有优雅的错误提示
- [ ] 持久化数据在/data目录
### 文档
- [ ] README.md包含使用说明
- [ ] 有API文档(如适用)
- [ ] 包含示例数据
- [ ] 标注了模型来源和许可
12.2 常见问题解决
问题1:Space构建失败
# 检查构建日志
# Space页面 → Logs → Build logs
# 常见原因:
# 1. requirements.txt格式错误
# 2. 依赖版本冲突
# 3. 系统依赖缺失
# 4. 内存不足(构建时)
# 解决方案:
# - 简化requirements.txt
# - 使用Docker SDK精确控制环境
# - 使用多阶段构建减少最终镜像大小
问题2:应用运行缓慢
# 诊断步骤:
# 1. 检查模型加载时间
import time
start = time.time()
model = pipeline("text-classification")
print(f"Model load time: {time.time() - start:.2f}s")
# 2. 检查推理时间
start = time.time()
result = model("test input")
print(f"Inference time: {time.time() - start:.2f}s")
# 3. 检查内存使用
import psutil
print(f"Memory: {psutil.virtual_memory().percent}%")
问题3:GPU显存不足
# 方案1:减小batch size
result = model(texts, batch_size=1) # 逐个处理
# 方案2:使用更小的模型
model = pipeline("text-classification", model="distilbert-base-uncased")
# 方案3:启用量化
model = pipeline("text-classification", model="my-model", device_map="auto", load_in_8bit=True)
# 方案4:使用CPU offload
pipe.enable_sequential_cpu_offload()
12.3 进阶技巧
技巧1:使用Gradio的chatbot组件
import gradio as gr
def respond(message, history):
# 调用LLM
response = llm.generate(message)
return response
demo = gr.ChatInterface(
fn=respond,
title="🤖 AI助手",
examples=["你好", "今天天气怎么样?", "帮我写一首诗"],
retry_btn="重新生成",
undo_btn="撤销",
clear_btn="清空"
)
demo.launch()
技巧2:多模型路由
import gradio as gr
MODELS = {
"快速模型": {"model": "distilbert", "speed": "fast", "accuracy": "medium"},
"精准模型": {"model": "roberta-large", "speed": "slow", "accuracy": "high"},
"平衡模型": {"model": "bert-base", "speed": "medium", "accuracy": "medium"},
}
def predict(text, model_choice):
model_info = MODELS[model_choice]
model = get_model(model_info["model"])
return model(text)
demo = gr.Interface(
fn=predict,
inputs=[
gr.Textbox(label="输入"),
gr.Radio(choices=list(MODELS.keys()), value="平衡模型", label="选择模型")
],
outputs=gr.Label(label="结果")
)
技巧3:实时进度显示
import gradio as gr
import time
def process_with_progress(text, progress=gr.Progress()):
steps = ["加载模型", "预处理", "推理", "后处理"]
results = []
for i, step in enumerate(steps):
progress((i + 1) / len(steps), desc=f"正在{step}...")
time.sleep(1) # 模拟处理
results.append(f"{step}: 完成")
return "\n".join(results)
demo = gr.Interface(
fn=process_with_progress,
inputs=gr.Textbox(label="输入"),
outputs=gr.Textbox(label="结果")
)
参考资源
- Hugging Face Spaces文档:https://huggingface.co/docs/hub/spaces
- Gradio文档:https://www.gradio.app/docs
- Streamlit文档:https://docs.streamlit.io
- Hugging Face Hub Python库:https://huggingface.co/docs/huggingface_hub
- Spaces GPU规格和价格:https://huggingface.co/pricing#spaces
- Docker SDK示例:https://huggingface.co/docs/hub/spaces-sdks-docker
声明:本教程中的价格信息和功能细节可能随时间变化,请以Hugging Face官方最新文档为准。代码示例经过简化,生产环境使用请添加完整的错误处理和日志记录。