机器学习工程实践

第一章:ML 工程概述

1.1 ML 工程师 vs 数据科学家

角色重点核心技能
数据科学家发现洞察、建模统计、可视化、实验设计
ML 工程师部署、扩展、维护软件工程、MLOps、系统设计
研究科学家新算法、新架构数学、论文阅读、实验

1.2 ML 系统生命周期

数据管道 → 特征工程 → 模型训练 → 评估验证 → 部署上线 → 监控运维 → 迭代优化

第二章:特征工程

2.1 数值特征

from sklearn.preprocessing import StandardScaler, MinMaxScaler, PowerTransformer

# 标准化 (z-score)
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X_train)

# 归一化 (min-max)
scaler = MinMaxScaler(feature_range=(0, 1))
X_norm = scaler.fit_transform(X_train)

# Box-Cox 变换(处理偏态分布)
transformer = PowerTransformer(method='box-cox')
X_transformed = transformer.fit_transform(X_train)

2.2 类别特征

from sklearn.preprocessing import OneHotEncoder, OrdinalEncoder, LabelEncoder

# 独热编码(无序类别)
encoder = OneHotEncoder(sparse_output=False, drop='first')
X_encoded = encoder.fit_transform(df[['city', 'department']])

# 有序编码(有序类别)
encoder = OrdinalEncoder(categories=[['低', '中', '高']])
X_ordered = encoder.fit_transform(df[['level']])

# 目标编码(高基数类别)
from category_encoders import TargetEncoder
encoder = TargetEncoder()
X_target = encoder.fit_transform(df['product_category'], y)

2.3 特征选择

from sklearn.feature_selection import (
    SelectKBest, f_classif, RFE, mutual_info_classif
)

# 统计检验
selector = SelectKBest(score_func=f_classif, k=10)
X_selected = selector.fit_transform(X, y)

# 递归特征消除
from sklearn.ensemble import RandomForestClassifier
rfe = RFE(RandomForestClassifier(), n_features_to_select=10)
X_rfe = rfe.fit_transform(X, y)

# 互信息
mi_scores = mutual_info_classif(X, y, random_state=42)

第三章:模型训练与调优

3.1 交叉验证

from sklearn.model_selection import (
    cross_val_score, StratifiedKFold, GridSearchCV, RandomizedSearchCV
)

# K 折交叉验证
cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
scores = cross_val_score(model, X, y, cv=cv, scoring='f1_macro')
print(f"F1: {scores.mean():.3f} ± {scores.std():.3f}")

# 网格搜索
param_grid = {
    'n_estimators': [100, 200, 500],
    'max_depth': [3, 5, 7, None],
    'learning_rate': [0.01, 0.05, 0.1],
}
grid = GridSearchCV(model, param_grid, cv=5, scoring='roc_auc', n_jobs=-1)
grid.fit(X_train, y_train)
print(f"Best params: {grid.best_params_}")
print(f"Best score: {grid.best_score_:.4f}")

# 随机搜索(更高效)
from scipy.stats import uniform
param_dist = {
    'n_estimators': [100, 200, 500, 1000],
    'learning_rate': uniform(0.01, 0.2),
    'subsample': uniform(0.6, 0.4),
}
random_search = RandomizedSearchCV(model, param_dist, n_iter=50, cv=5)

3.2 集成方法

from sklearn.ensemble import (
    VotingClassifier, StackingClassifier, 
    BaggingClassifier, AdaBoostClassifier
)
from xgboost import XGBClassifier
from lightgbm import LGBMClassifier

# 投票集成
voting = VotingClassifier(estimators=[
    ('rf', RandomForestClassifier()),
    ('xgb', XGBClassifier()),
    ('lgb', LGBMClassifier()),
], voting='soft')

# 堆叠集成
stacking = StackingClassifier(estimators=[
    ('rf', RandomForestClassifier()),
    ('xgb', XGBClassifier()),
], final_estimator=LogisticRegression(), cv=5)

3.3 深度学习框架

import torch
import torch.nn as nn

# 定义网络
class Classifier(nn.Module):
    def __init__(self, input_dim, hidden_dim, num_classes):
        super().__init__()
        self.net = nn.Sequential(
            nn.Linear(input_dim, hidden_dim),
            nn.BatchNorm1d(hidden_dim),
            nn.ReLU(),
            nn.Dropout(0.3),
            nn.Linear(hidden_dim, num_classes)
        )
    
    def forward(self, x):
        return self.net(x)

# 训练循环
model = Classifier(784, 256, 10)
optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)
criterion = nn.CrossEntropyLoss()

for epoch in range(20):
    model.train()
    for batch_x, batch_y in train_loader:
        pred = model(batch_x)
        loss = criterion(pred, batch_y)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

第四章:MLOps 实践

4.1 实验追踪

import mlflow

mlflow.set_experiment("my-ml-project")

with mlflow.start_run():
    # 记录参数
    mlflow.log_params({"lr": 0.01, "epochs": 50, "batch_size": 32})
    
    # 记录指标
    for epoch in range(50):
        train_loss = train_one_epoch(model)
        val_acc = evaluate(model)
        mlflow.log_metrics({"train_loss": train_loss, "val_acc": val_acc}, step=epoch)
    
    # 记录模型
    mlflow.sklearn.log_model(model, "model")

4.2 模型服务

# FastAPI 模型服务
from fastapi import FastAPI
from pydantic import BaseModel
import joblib

app = FastAPI()
model = joblib.load("model.pkl")

class InputData(BaseModel):
    features: list[float]

@app.post("/predict")
def predict(data: InputData):
    import numpy as np
    X = np.array(data.features).reshape(1, -1)
    prediction = model.predict(X)
    probability = model.predict_proba(X).max()
    return {
        "prediction": int(prediction[0]),
        "confidence": float(probability),
    }

4.3 Docker 部署

FROM python:3.11-slim

WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .
EXPOSE 8000

CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
# docker-compose.yml
version: '3.8'
services:
  ml-api:
    build: .
    ports:
      - "8000:8000"
    environment:
      - MODEL_PATH=/models/latest
    volumes:
      - ./models:/models
  
  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"

第五章:模型监控

5.1 数据漂移检测

from scipy.stats import ks_2samp

def detect_drift(reference_data, current_data, threshold=0.05):
    """KS 检验检测数据分布漂移"""
    drift_features = []
    for col in reference_data.columns:
        stat, p_value = ks_2samp(reference_data[col], current_data[col])
        if p_value < threshold:
            drift_features.append({
                'feature': col,
                'ks_statistic': stat,
                'p_value': p_value
            })
    return pd.DataFrame(drift_features)

5.2 A/B 测试

from scipy.stats import ttest_ind

def ab_test(control_metrics, treatment_metrics):
    """A/B 测试显著性检验"""
    t_stat, p_value = ttest_ind(treatment_metrics, control_metrics)
    
    control_mean = control_metrics.mean()
    treatment_mean = treatment_metrics.mean()
    lift = (treatment_mean - control_mean) / control_mean
    
    return {
        'control_mean': control_mean,
        'treatment_mean': treatment_mean,
        'lift_pct': lift * 100,
        'p_value': p_value,
        'significant': p_value < 0.05
    }

学习资源