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"))

延伸阅读

继续阅读

探索更多技术文章

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

全部文章 返回首页

「python」更多文章