Python 数据科学与 AI:Pandas/Polars、PyTorch 推理与 ONNX 部署

Python 数据科学生态全景:Pandas vs Polars vs NumPy 选型与性能对比、PyTorch 模型推理与优化、Transformers pipeline 实战、ONNX 导出与跨平台推理、与 Rust(PyO3)互操作加速。覆盖从数据处理到生产部署的完整链路。

Python 是 AI 时代的英语。无论你用 PyTorch 训练大模型,还是用 Polars 处理十亿行日志,这篇文章提供从数据到智能的完整工程地图。


1. 数据处理:Pandas、Polars 与 NumPy

1.1 三剑客选型

工具适用场景性能内存特点
NumPy数值计算、矩阵运算⭐⭐⭐⭐低C 底层,广播机制
Pandas表格数据、ETL 、分析⭐⭐高成熟生态,索引灵活
Polars大数据集(>1GB)⭐⭐⭐⭐⭐低Rust 编写,惰性计算

1.2 NumPy 核心技巧

import numpy as np

# 向量化替代循环(1000x 加速)
# ❌ 慢
result = []
for x in range(1_000_000):
    result.append(x ** 2 + 2 * x + 1)

# ✅ 快
x = np.arange(1_000_000)
result = x ** 2 + 2 * x + 1  # 广播,无 Python 循环

# 高级索引
arr = np.random.randn(1000, 1000)
mask = arr > 2  # 布尔掩码
outliers = arr[mask]  # 只取大于 2 的元素

# 内存视图(零拷贝)
sub = arr[:100, :100]  # 不复制数据

1.3 Pandas 实战模式

import pandas as pd

# 读取大文件的 4 个技巧
df = pd.read_csv(
    "large.csv",
    usecols=["user_id", "amount", "timestamp"],  # 只读需要的列
    dtype={"user_id": "int32", "amount": "float32"},  # 降精度
    parse_dates=["timestamp"],
    chunksize=100_000  # 分块处理
)

# 高效分组聚合
# ❌ 慢:apply 调用 Python 函数
df.groupby("category").apply(lambda x: x["amount"].sum())

# ✅ 快:向量化聚合
agg = df.groupby("category").agg({
    "amount": ["sum", "mean", "count"],
    "user_id": "nunique"
})

# 时间序列重采样
# 按小时统计订单量
df.set_index("timestamp").resample("H")["order_id"].count()

1.4 Polars:下一代 DataFrame

import polars as pl

# 惰性查询(查询优化类似 SQL 执行计划)
df = pl.scan_csv("huge.csv")  # 不立即读数据

result = (
    df
    .filter(pl.col("amount") > 100)
    .group_by("category")
    .agg([
        pl.col("amount").sum().alias("total"),
        pl.col("user_id").n_unique().alias("unique_users")
    ])
    .sort("total", descending=True)
    .limit(10)
    .collect()  # 这里才真正执行
)

# 与 Pandas 互操作
pandas_df = result.to_pandas()
polars_df = pl.from_pandas(pandas_df)

Polars vs Pandas 性能对比(1GB CSV):

操作PandasPolars加速比
读取4.2s0.8s5.2x
筛选+分组2.1s0.15s14x
内存占用3.2GB0.9GB3.5x

2. PyTorch:从加载到推理优化

2.1 模型推理基础

import torch
from torchvision import models, transforms
from PIL import Image

# 加载预训练模型(ResNet50)
model = models.resnet50(weights=models.ResNet50_Weights.IMAGENET1K_V2)
model.eval()

# 推理模式(禁用 dropout、batchnorm 更新)
with torch.no_grad():
    # 预处理管道
    preprocess = transforms.Compose([
        transforms.Resize(256),
        transforms.CenterCrop(224),
        transforms.ToTensor(),
        transforms.Normalize(
            mean=[0.485, 0.456, 0.406],
            std=[0.229, 0.224, 0.225]
        )
    ])
    
    img = Image.open("cat.jpg")
    input_tensor = preprocess(img).unsqueeze(0)
    
    # 推理
    output = model(input_tensor)
    probabilities = torch.nn.functional.softmax(output[0], dim=0)
    
    # Top 5 预测
    top5_prob, top5_idx = torch.topk(probabilities, 5)

2.2 推理优化技巧

# 1. 半精度加速(FP16)
model.half()  # 权重转 FP16
input_tensor = input_tensor.half()

# 2. 批处理推理(摊平启动开销)
def batch_predict(model, images: list[Image.Image], batch_size: int = 32):
    results = []
    for i in range(0, len(images), batch_size):
        batch = images[i:i + batch_size]
        tensors = torch.stack([preprocess(img) for img in batch])
        with torch.no_grad():
            outputs = model(tensors)
        results.extend(outputs)
    return results

# 3. TorchScript 编译(图优化)
scripted = torch.jit.script(model)
# 或 traced = torch.jit.trace(model, example_input)

# 4. 推理服务封装
class InferenceService:
    def __init__(self, model_path: str, device: str = "cpu"):
        self.device = torch.device(device)
        self.model = torch.load(model_path, map_location=self.device)
        self.model.eval().to(self.device)
    
    @torch.inference_mode()
    def predict(self, image: Image.Image) -> dict:
        tensor = preprocess(image).unsqueeze(0).to(self.device)
        output = self.model(tensor)
        return {" logits": output.cpu().numpy().tolist()}

3. Transformers:大模型推理

3.1 Pipeline 快速上手

from transformers import pipeline

# 文本分类
classifier = pipeline(
    "sentiment-analysis",
    model="distilbert-base-uncased-finetuned-sst-2-english"
)
result = classifier("I love FastAPI!")
# [{'label': 'POSITIVE', 'score': 0.9998}]

# 命名实体识别
ner = pipeline("ner", model="dslim/bert-base-NER")
ner("Apple Inc. was founded by Steve Jobs in Cupertino.")

# 文本生成(LLM)
generator = pipeline(
    "text-generation",
    model="microsoft/Phi-3-mini-4k-instruct",
    torch_dtype="auto",
    device_map="auto"  # 自动分配到 GPU/CPU
)

prompt = "Write a Python function to calculate fibonacci:"
output = generator(prompt, max_new_tokens=100, temperature=0.7)

3.2 量化推理(减少 75% 显存)

from transformers import AutoModelForCausalLM, BitsAndBytesConfig

quantization_config = BitsAndBytesConfig(
    load_in_4bit=True,  # 4-bit 量化
    bnb_4bit_compute_dtype=torch.float16,
    bnb_4bit_quant_type="nf4",  # Normal Float 4
    bnb_4bit_use_double_quant=True,
)

model = AutoModelForCausalLM.from_pretrained(
    "meta-llama/Llama-2-7b",
    quantization_config=quantization_config,
    device_map="auto",
)
# 7B 模型从 14GB 降至 4GB

3.3 vLLM:高吞吐 LLM 服务

from vllm import LLM

# 比 Transformers 快 10-20x 的批处理推理
llm = LLM(model="meta-llama/Llama-2-7b", tensor_parallel_size=1)

prompts = [
    "What is Python used for?",
    "Explain async/await in one sentence.",
]
outputs = llm.generate(prompts)

for output in outputs:
    print(output.outputs[0].text)

4. ONNX:跨平台部署

4.1 PyTorch → ONNX 导出

import torch

dummy_input = torch.randn(1, 3, 224, 224)

torch.onnx.export(
    model,
    dummy_input,
    "resnet50.onnx",
    input_names=["input"],
    output_names=["output"],
    dynamic_axes={"input": {0: "batch_size"}, "output": {0: "batch_size"}},
    opset_version=17,
)

4.2 ONNX Runtime 推理

import onnxruntime as ort
import numpy as np

session = ort.InferenceSession("resnet50.onnx")

# 输入准备
input_name = session.get_inputs()[0].name
input_data = np.random.randn(1, 3, 224, 224).astype(np.float32)

# 推理
outputs = session.run(None, {input_name: input_data})

# 不同执行提供器
providers = [
    "CUDAExecutionProvider",  # GPU
    "TensorRTExecutionProvider",  # NVIDIA TensorRT
    "CPUExecutionProvider",  # 回退
]
session = ort.InferenceSession("model.onnx", providers=providers)

4.3 部署场景对比

部署方式优点缺点场景
PyTorch灵活、可训练依赖大、慢研究/实验
TorchScript无 Python 依赖动态图受限移动端
ONNX跨框架/跨平台简化图可能生产服务
TensorRTGPU 极致优化仅 NVIDIA高吞吐 GPU
OpenVINOIntel CPU 加速仅 Intel边缘设备

5. Python × Rust:PyO3 加速

当 Python 成为性能瓶颈时,用 Rust 重写核心逻辑:

// Rust 代码(lib.rs)
use pyo3::prelude::*;

#[pyfunction]
fn fast_sum(numbers: Vec<f64>) -> f64 {
    numbers.iter().sum()
}

#[pymodule]
fn my_rust_lib(_py: Python, m: &PyModule) -> PyResult<()> {
    m.add_function(wrap_pyfunction!(fast_sum, m)?)?;
    Ok(())
}
# Python 调用
import maturin  # pip install maturin
# maturin develop  # 编译并安装

from my_rust_lib import fast_sum

# 比 Python sum() 快 100x
result = fast_sum([1.0] * 10_000_000)

PyO3 适用场景:

  • 大规模数值计算
  • 高性能解析器(JSON/CSV)
  • 密码学操作
  • 与 Rust 生态(如 Polars 的方式)集成

6. 完整数据流水线

from pathlib import Path
import polars as pl
from transformers import pipeline
import torch

class DataPipeline:
    def __init__(self):
        self.classifier = pipeline(
            "sentiment-analysis",
            model="nlptown/bert-base-multilingual-uncased-sentiment",
            device=0 if torch.cuda.is_available() else -1
        )
    
    def process_csv(self, path: Path) -> pl.DataFrame:
        # 1. 读取(惰性)
        df = pl.scan_csv(path)
        
        # 2. 清洗
        df = df.filter(
            pl.col("text").is_not_null() &
            (pl.col("text").str.len_chars() > 10)
        )
        
        # 3. 采样(开发时)
        # df = df.limit(1000)
        
        # 4. 收集为 Pandas(兼容性)
        return df.collect().to_pandas()
    
    def analyze_sentiment(self, df: pd.DataFrame) -> pd.DataFrame:
        texts = df["text"].tolist()
        
        # 批处理推理
        results = self.classifier(texts, batch_size=32)
        
        df["sentiment"] = [r["label"] for r in results]
        df["confidence"] = [r["score"] for r in results]
        return df
    
    def run(self, input_path: Path, output_path: Path):
        df = self.process_csv(input_path)
        df = self.analyze_sentiment(df)
        df.to_parquet(output_path)  # 列式存储,比 CSV 快 10x

# 执行
pipeline = DataPipeline()
pipeline.run(Path("reviews.csv"), Path("reviews_analyzed.parquet"))

延伸阅读

7. 特征工程与数据预处理

特征质量直接决定模型天花板。以下是生产环境常用的特征工程流程:

7.1 特征缩放与编码

from sklearn.preprocessing import StandardScaler, RobustScaler, OneHotEncoder
from sklearn.compose import ColumnTransformer
import numpy as np

# 数值列:对异常值鲁棒的缩放
numeric_features = ["age", "income", "account_age_days"]
numeric_transformer = RobustScaler(quantile_range=(5, 95))

# 类别列:高频截断 + One-Hot
categorical_features = ["country", "device_type"]
categorical_transformer = OneHotEncoder(
    handle_unknown="ignore",
    max_categories=50,  # 只保留 top 50 类别
    sparse_output=False
)

# 组合预处理管道
preprocessor = ColumnTransformer([
    ("num", numeric_transformer, numeric_features),
    ("cat", categorical_transformer, categorical_features),
])

# 与模型组合为完整管道
from sklearn.pipeline import Pipeline
from sklearn.ensemble import GradientBoostingClassifier

clf = Pipeline([
    ("preprocess", preprocessor),
    ("classifier", GradientBoostingClassifier(n_estimators=100)),
])

7.2 时间特征提取

def extract_time_features(df: pd.DataFrame, col: str) -> pd.DataFrame:
    dt = pd.to_datetime(df[col])
    df[f"{col}_hour"] = dt.dt.hour
    df[f"{col}_dayofweek"] = dt.dt.dayofweek
    df[f"{col}_month"] = dt.dt.month
    df[f"{col}_is_weekend"] = dt.dt.dayofweek >= 5
    df[f"{col}_quarter"] = dt.dt.quarter
    # 周期性编码(正弦/余弦,保持周期连续性)
    df[f"{col}_hour_sin"] = np.sin(2 * np.pi * dt.dt.hour / 24)
    df[f"{col}_hour_cos"] = np.cos(2 * np.pi * dt.dt.hour / 24)
    return df

8. 模型评估与超参数调优

8.1 交叉验证策略

from sklearn.model_selection import StratifiedKFold, cross_val_score
from sklearn.metrics import roc_auc_score, classification_report

# 时序数据:TimeSeriesSplit;类别不平衡:StratifiedKFold
cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)

scores = cross_val_score(clf, X, y, cv=cv, scoring="roc_auc", n_jobs=-1)
print(f"AUC: {scores.mean():.4f} (+/- {scores.std() * 2:.4f})")

8.2 Optuna 超参数搜索

import optuna
from sklearn.model_selection import cross_val_score

def objective(trial):
    params = {
        "n_estimators": trial.suggest_int("n_estimators", 50, 500),
        "max_depth": trial.suggest_int("max_depth", 3, 10),
        "learning_rate": trial.suggest_float("learning_rate", 0.01, 0.3, log=True),
        "subsample": trial.suggest_float("subsample", 0.6, 1.0),
        "colsample_bytree": trial.suggest_float("colsample_bytree", 0.6, 1.0),
    }
    model = GradientBoostingClassifier(**params, random_state=42)
    score = cross_val_score(model, X_train, y_train, cv=3, scoring="roc_auc").mean()
    return score

study = optuna.create_study(direction="maximize")
study.optimize(objective, n_trials=50, show_progress_bar=True)
print(f"Best score: {study.best_value:.4f}, params: {study.best_params}")

搜索算法选择:

算法特点适用场景
TPE(默认)树形 Parzen 估计,采样高效大多数场景,< 100 维度
CMA-ES协方差矩阵自适应高维连续参数
随机搜索简单无偏极大规模超参数空间
网格搜索穷举,可复现参数空间很小

9. MLOps:从实验到生产

9.1 MLflow 实验追踪

import mlflow
import mlflow.sklearn

mlflow.set_experiment("fraud-detection")

with mlflow.start_run():
    # 记录参数
    mlflow.log_param("model_type", "xgboost")
    mlflow.log_param("n_estimators", 200)

    # 记录指标
    mlflow.log_metric("auc", 0.943)
    mlflow.log_metric("f1", 0.891)

    # 记录模型 + 签名
    mlflow.sklearn.log_model(
        model,
        artifact_path="model",
        registered_model_name="fraud_detection_v1"
    )

9.2 模型服务化(FastAPI + ONNX)

from fastapi import FastAPI
import onnxruntime as ort
import numpy as np

app = FastAPI()
session = ort.InferenceSession("model.onnx")
input_name = session.get_inputs()[0].name

@app.post("/predict")
def predict(features: list[float]):
    arr = np.array([features], dtype=np.float32)
    outputs = session.run(None, {input_name: arr})
    return {"prediction": float(outputs[0][0])}

9.3 模型监控与漂移检测

from evidently.report import Report
from evidently.metric_preset import DataDriftPreset, TargetDriftPreset

# 比较参考数据集与当前生产数据
report = Report(metrics=[DataDriftPreset(), TargetDriftPreset()])
report.run(reference_data=df_ref, current_data=df_prod)

# 生成 HTML 报告
report.save_html("drift_report.html")

# 若 drift > 阈值,触发重训练管道
if report.as_dict()["metrics"][0]["result"]["dataset_drift"]:
    trigger_retraining_pipeline()

10. GPU 训练优化

10.1 PyTorch GPU 最佳实践

import torch

# 自动混合精度(AMP):减少显存占用 + 加速
def train_step_amp(model, data, target, optimizer, scaler):
    optimizer.zero_grad()
    with torch.autocast(device_type="cuda", dtype=torch.float16):
        output = model(data)
        loss = criterion(output, target)

    scaler.scale(loss).backward()
    scaler.step(optimizer)
    scaler.update()

# Gradient Accumulation:小显存模拟大 batch
accumulation_steps = 4
for i, (data, target) in enumerate(dataloader):
    loss = model(data, target) / accumulation_steps
    loss.backward()
    if (i + 1) % accumulation_steps == 0:
        optimizer.step()
        optimizer.zero_grad()

10.2 显存监控

# 实时监控显存使用
torch.cuda.memory_summary(device=0)
# 或
!nvidia-smi --query-gpu=memory.used,memory.total --format=csv
优化技术显存节省速度提升实现复杂度
AMP (FP16)~40%1.5–2x低
Gradient Checkpointing~60%0.7x低
8-bit Optimizer (bitsandbytes)~75%0.9x中
DeepSpeed ZeRO-3~90%1.0x高

从数据清洗到特征工程,从实验追踪到生产部署,Python 数据科学生态提供了贯穿全链路的工具支持。掌握这些工具的组合使用,是构建可靠 AI 系统的核心能力。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「python」更多文章

  1. 《Python高级编程》目录
  2. 《Python高级编程》11.3 PEP 流程与版本迁移策略
  3. 《Python高级编程》11.2 嵌入式与自由线程运行时