3. 为模型API添加请求参数校验与错误处理
任务目标
训练一个简单的Scikit-learn模型,分别使用joblib和pickle完成序列化与反序列化,验证加载后模型预测结果一致,并对比两种方法的文件体积与加载耗时。
环境依赖
需要安装:scikit-learn、joblib(scikit-learn自带依赖)、numpy。Python版本≥3.8。
完整实现代码
import os
import time
import pickle
import joblib
import numpy as np
from sklearn.datasets import load_iris
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
# 1. 准备数据并训练模型
data = load_iris()
X_train, X_test, y_train, y_test = train_test_split(
data.data, data.target, test_size=0.2, random_state=42
)
model = RandomForestClassifier(n_estimators=50, random_state=42)
model.fit(X_train, y_train)
# 2. 使用 pickle 保存与加载
pickle_path = "model.pkl"
with open(pickle_path, "wb") as f:
pickle.dump(model, f)
with open(pickle_path, "rb") as f:
model_pickle = pickle.load(f)
# 3. 使用 joblib 保存与加载
joblib_path = "model.joblib"
joblib.dump(model, joblib_path)
model_joblib = joblib.load(joblib_path)
# 4. 验证:两种方式加载的模型预测结果与原始模型一致
pred_original = model.predict(X_test)
pred_pickle = model_pickle.predict(X_test)
pred_joblib = model_joblib.predict(X_test)
assert np.array_equal(pred_original, pred_pickle), "pickle 加载模型预测不一致"
assert np.array_equal(pred_original, pred_joblib), "joblib 加载模型预测不一致"
print("预测一致性验证通过")
# 5. 对比文件体积与加载耗时
pickle_size = os.path.getsize(pickle_path) / 1024 # KB
joblib_size = os.path.getsize(joblib_path) / 1024 # KB
# 多次加载取平均耗时
n_repeat = 100
start = time.perf_counter()
for _ in range(n_repeat):
with open(pickle_path, "rb") as f:
pickle.load(f)
pickle_time = (time.perf_counter() - start) / n_repeat * 1000 # ms
start = time.perf_counter()
for _ in range(n_repeat):
joblib.load(joblib_path)
joblib_time = (time.perf_counter() - start) / n_repeat * 1000 # ms
print(f"pickle 文件大小: {pickle_size:.2f} KB, 平均加载耗时: {pickle_time:.3f} ms")
print(f"joblib 文件大小: {joblib_size:.2f} KB, 平均加载耗时: {joblib_time:.3f} ms")
运行方式
pip install scikit-learn joblib numpy
python serialize_compare.py
验证方法与判定标准
- 功能验证:程序输出
预测一致性验证通过,且无AssertionError,说明两种序列化方式均能完整保留模型预测能力。 - 对比验证:程序输出两种方式的文件大小与平均加载耗时。通常
joblib文件更小、加载更快(因内部使用numpy数组的专用序列化),但具体数值取决于模型复杂度与运行环境,以实际输出为准。
两种方法优缺点对比
| 维度 | pickle | joblib |
|---|---|---|
| 适用对象 | 通用Python对象 | 针对NumPy数组优化的科学计算对象 |
| 文件体积 | 较大 | 通常更小(对大数组更明显) |
| 加载速度 | 较慢 | 通常更快 |
| 跨版本兼容 | Python版本间可能不兼容 | 与pickle类似,但依赖joblib版本 |
| 安全性 | 两者均不安全,不可加载不可信来源的序列化文件 | |
常见问题
- 跨环境加载失败:pickle/joblib文件与Python版本、库版本强相关。升级scikit-learn或Python后建议重新训练并保存模型。
- 大模型内存溢出:若模型包含超大numpy数组,pickle可能占用较高内存,此时优先使用joblib。
- 安全警告:切勿加载来源不明的.pkl或.joblib文件,可能包含恶意代码执行漏洞。