Python机器学习模型部署:piku实现预测服务上线
痛点与解决方案
数据科学家训练出高精度模型后,常面临部署难题:缺乏DevOps经验、云服务成本高、配置复杂。piku作为轻量级PaaS(Platform as a Service,平台即服务)工具,通过Git Push实现一键部署,无需复杂配置即可将模型转化为生产级API服务。本文将带你完成从模型训练到线上预测的全流程,掌握在廉价服务器甚至树莓派上部署机器学习服务的技能。
读完本文你将掌握
- 用piku构建机器学习模型部署环境
- 设计预测服务API接口
- 实现模型版本控制与自动部署
- 配置HTTPS与性能优化
- 监控与日志分析
piku工作原理
piku通过Git钩子实现代码推送即部署,自动处理环境配置、依赖安装、进程管理。其核心流程如下:
关键技术特性:
- 多语言支持:原生支持Python、Node.js等多种语言
- 自动资源隔离:为每个应用创建独立环境
- 进程管理:自动处理服务启停与崩溃恢复
- 零停机部署:更新时无缝切换新版本
环境准备
服务器要求
- 操作系统:Linux(推荐Ubuntu 20.04+)
- 硬件:最低512MB内存(推荐2GB+,支持模型加载)
- 网络:开放22(SSH)、80(HTTP)、443(HTTPS)端口
安装piku
# 克隆仓库
git clone https://gitcode.com/GitHub_Trending/pi/piku
cd piku
# 安装依赖
sudo apt update && sudo apt install -y python3-pip python3-venv nginx uwsgi
# 运行安装脚本
sudo ./piku setup
# 配置SSH密钥(本地执行)
ssh-copy-id -i ~/.ssh/id_rsa.pub piku@your_server_ip
验证安装
# 检查piku版本
piku --version
# 查看状态
piku info
模型部署工程结构
ml-prediction-service/
├── model/ # 模型文件目录
│ ├── __init__.py
│ ├── model.pkl # 序列化模型
│ └── preprocessing.py # 数据预处理逻辑
├── api/ # API接口目录
│ ├── __init__.py
│ └── server.py # FastAPI服务
├── tests/ # 单元测试
│ └── test_api.py
├── requirements.txt # 依赖列表
├── Procfile # piku部署配置
├── ENV # 环境变量
└── .gitignore
关键文件说明
1. Procfile配置
web: uvicorn api.server:app --host 0.0.0.0 --port $PORT
指定启动命令,$PORT由piku自动分配
2. requirements.txt
fastapi==0.104.1
uvicorn==0.24.0
scikit-learn==1.3.2
pandas==2.1.3
numpy==1.26.2
joblib==1.3.2
python-multipart==0.0.6
3. ENV文件
PYTHON_VERSION=3.9
PIKU_WSGI_MODULE=api.server:app
MAX_REQUEST_SIZE=10485760
预测服务实现
数据预处理模块
# model/preprocessing.py
import pandas as pd
import numpy as np
from sklearn.preprocessing import StandardScaler
class Preprocessor:
def __init__(self):
self.scaler = StandardScaler()
def fit(self, X):
self.scaler.fit(X)
return self
def transform(self, X):
# 处理缺失值
X = X.fillna(X.mean())
# 特征缩放
return self.scaler.transform(X)
def save(self, path):
import joblib
joblib.dump(self, path)
@classmethod
def load(cls, path):
import joblib
return joblib.load(path)
模型加载与预测类
# model/__init__.py
import joblib
import os
class PredictionModel:
_instance = None
@classmethod
def get_instance(cls):
"""单例模式加载模型,避免重复加载"""
if cls._instance is None:
model_path = os.path.join(os.path.dirname(__file__), 'model.pkl')
preprocessor_path = os.path.join(os.path.dirname(__file__), 'preprocessor.pkl')
cls._instance = cls()
cls._instance.model = joblib.load(model_path)
cls._instance.preprocessor = joblib.load(preprocessor_path)
return cls._instance
def predict(self, data):
"""预测接口"""
processed_data = self.preprocessor.transform(data)
return self.model.predict(processed_data)
def predict_proba(self, data):
"""概率预测接口"""
processed_data = self.preprocessor.transform(data)
return self.model.predict_proba(processed_data)
FastAPI服务实现
# api/server.py
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from typing import List, Dict, Any
import pandas as pd
import numpy as np
from model import PredictionModel
app = FastAPI(title="ML Prediction Service")
model = PredictionModel.get_instance()
# 输入数据模型定义
class PredictionRequest(BaseModel):
features: List[Dict[str, Any]]
# 批量预测请求
class BatchPredictionRequest(BaseModel):
data: List[List[float]]
@app.post("/predict", response_model=Dict[str, List[int]])
async def predict(request: PredictionRequest):
try:
df = pd.DataFrame(request.features)
predictions = model.predict(df)
return {"predictions": predictions.tolist()}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@app.post("/predict_proba", response_model=Dict[str, List[List[float]]])
async def predict_proba(request: PredictionRequest):
try:
df = pd.DataFrame(request.features)
probabilities = model.predict_proba(df)
return {"probabilities": probabilities.tolist()}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@app.post("/batch_predict", response_model=Dict[str, List[int]])
async def batch_predict(request: BatchPredictionRequest):
try:
data = np.array(request.data)
predictions = model.predict(data)
return {"predictions": predictions.tolist()}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@app.get("/health")
async def health_check():
return {"status": "healthy", "model_version": "1.0.0"}
@app.get("/")
async def root():
return {"message": "ML Prediction Service API", "endpoints": ["/predict", "/predict_proba", "/batch_predict", "/health"]}
本地测试
# 安装依赖
pip install -r requirements.txt
# 启动服务
uvicorn api.server:app --reload
测试请求示例:
curl -X POST "http://localhost:8000/predict" \
-H "Content-Type: application/json" \
-d '{"features": [{"sepal_length": 5.1, "sepal_width": 3.5, "petal_length": 1.4, "petal_width": 0.2}]}'
部署到piku服务器
配置Git仓库
# 初始化Git仓库
git init
git add .
git commit -m "Initial commit: ML prediction service"
# 添加piku远程仓库
git remote add piku piku@your_server_ip:ml-service
首次部署
git push piku master
piku部署过程输出:
Counting objects: 20, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (15/15), done.
Writing objects: 100% (20/20), 2.12 KiB | 0 bytes/s, done.
Total 20 (delta 5), reused 0 (delta 0)
remote: -----> Deploying app 'ml-service'
remote: -----> Python app detected.
remote: -----> Creating virtualenv for 'ml-service'
remote: -----> Running pip for 'ml-service'
remote: Collecting fastapi==0.104.1
remote: Downloading fastapi-0.104.1-py3-none-any.whl (65 kB)
remote: Installing collected packages: ...
remote: Successfully installed ...
remote: -----> Spawning workers
remote: -----> Application deployed successfully
To piku@your_server_ip:ml-service
* [new branch] master -> master
配置HTTPS与域名
设置域名
# 配置应用域名
piku config:set ml-service NGINX_SERVER_NAME=predict.yourdomain.com
启用HTTPS
# 安装acme.sh
curl https://get.acme.sh | sh
# 获取SSL证书
acme.sh --issue -d predict.yourdomain.com --webroot /home/piku/.piku/acme
# 配置piku使用HTTPS
piku config:set ml-service NGINX_SSL=443
piku config:set ml-service NGINX_SSL_CERT=/home/piku/.acme.sh/predict.yourdomain.com/fullchain.cer
piku config:set ml-service NGINX_SSL_KEY=/home/piku/.acme.sh/predict.yourdomain.com/predict.yourdomain.com.key
性能优化配置
调整UWSGI参数
创建uwsgi.ini文件:
[uwsgi]
http-timeout = 600
socket-timeout = 600
harakiri = 600
processes = 4
threads = 2
max-requests = 1000
max-requests-delta = 50
在ENV中引用配置:
UWSGI_INI=uwsgi.ini
启用缓存
# 配置API缓存
piku config:set ml-service CACHE_PATH=/home/piku/.piku/cache/ml-service
piku config:set ml-service CACHE_SIZE=100m
piku config:set ml-service CACHE_TIME_CONTENT=300
监控与日志管理
查看应用状态
# 查看进程状态
piku ps ml-service
# 查看日志
piku logs ml-service web
设置日志轮转
创建日志轮转配置文件/etc/logrotate.d/piku-ml-service:
/home/piku/.piku/logs/ml-service/*.log {
daily
missingok
rotate 14
compress
delaycompress
notifempty
create 0640 piku piku
}
模型更新与版本控制
模型版本管理流程
实现蓝绿部署
# 创建新版本分支
git checkout -b model-v2
# 修改模型文件后提交
git add model/model.pkl
git commit -m "Update model to version 2"
# 部署新版本
git push piku model-v2:master
常见问题解决
模型加载过慢
- 问题:大型模型导致启动时间过长
- 解决方案:实现模型懒加载,在首次请求时加载模型
# 修改model/__init__.py
class PredictionModel:
_instance = None
_model_loaded = False
@classmethod
def get_instance(cls):
if cls._instance is None:
cls._instance = cls()
return cls._instance
def _load_model(self):
if not self._model_loaded:
model_path = os.path.join(os.path.dirname(__file__), 'model.pkl')
self.model = joblib.load(model_path)
self._model_loaded = True
def predict(self, data):
self._load_model()
# 预测逻辑...
内存占用过高
- 问题:多进程模式下每个进程加载一份模型,内存占用过高
- 解决方案:使用共享内存或模型服务化(如TensorFlow Serving)
监控与告警
设置健康检查端点
@app.get("/metrics")
async def metrics():
from prometheus_client import generate_latest
return Response(generate_latest(), media_type="text/plain")
配置Prometheus监控
scrape_configs:
- job_name: 'ml-service'
static_configs:
- targets: ['localhost:8000']
metrics_path: '/metrics'
总结与展望
本文详细介绍了使用piku部署Python机器学习模型的全过程,从环境搭建到性能优化,涵盖生产环境所需的关键步骤。piku的轻量级特性使其特别适合资源受限场景,如边缘计算设备或低成本服务器。
未来可进一步探索:
- 实现A/B测试框架
- 构建模型性能监控面板
- 开发自动扩缩容功能
- 集成CI/CD流水线
收藏与分享
如果本文对你有帮助,请点赞、收藏并关注,下期将带来《piku多模型服务编排与负载均衡》。
附录:完整配置文件
Procfile
web: uvicorn api.server:app --host 0.0.0.0 --port $PORT
requirements.txt
fastapi==0.104.1
uvicorn==0.24.0
scikit-learn==1.3.2
pandas==2.1.3
numpy==1.26.2
joblib==1.3.2
python-multipart==0.0.6
prometheus-client==0.17.1
ENV配置
PYTHON_VERSION=3.9
PIKU_WSGI_MODULE=api.server:app
MAX_REQUEST_SIZE=10485760
NGINX_SERVER_NAME=predict.yourdomain.com
NGINX_SSL=443
NGINX_SSL_CERT=/home/piku/.acme.sh/predict.yourdomain.com/fullchain.cer
NGINX_SSL_KEY=/home/piku/.acme.sh/predict.yourdomain.com/predict.yourdomain.com.key
UWSGI_INI=uwsgi.ini
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



