用Python实战解锁机器学习核心算法:从决策树到神经网络的代码化复习

当厚厚的复习资料铺满书桌,那些抽象的概念和公式仿佛在眼前跳动——信息增益、核函数、反向传播、正则化...与其在纸面上反复誊写定义,不如打开Jupyter Notebook,让代码替你"理解"这些算法。本文将带你用Python和Scikit-learn,把枯燥的考试重点转化为可运行的代码实验,在模型训练过程中自然掌握那些易混淆的理论要点。

1. 环境准备与数据工程

工欲善其事,必先利其器。我们先搭建一个可复现的机器学习实验环境:

# 基础环境配置
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn import datasets
from sklearn.model_selection import train_test_split

# 示例数据集加载
iris = datasets.load_iris()
X = iris.data[:, 2:]  # 只取后两个特征方便可视化
y = iris.target

# 数据集划分的最佳实践
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42, stratify=y)

注意:stratify参数确保训练集和测试集的类别分布一致,这在样本不平衡时尤为重要

机器学习三要素中, 数据质量往往比算法选择更重要 。考试中常出现的几个数据预处理考点:

  • 特征缩放:MinMaxScaler vs StandardScaler
  • 类别编码:OneHotEncoder vs LabelEncoder
  • 缺失值处理:简单填充 vs 模型预测填充
from sklearn.preprocessing import StandardScaler
from sklearn.impute import SimpleImputer

# 典型的数据预处理流水线
preprocessor = Pipeline([
    ('imputer', SimpleImputer(strategy='median')),  # 中位数填充缺失值
    ('scaler', StandardScaler())  # 标准化处理
])

2. 决策树:从信息论到可视化

决策树是考试中的常客,特别是关于节点划分标准的各种计算题。让我们用代码实现三种划分准则:

from sklearn.tree import DecisionTreeClassifier, export_graphviz
import graphviz

# 三种划分准则对比
criteria = ['gini', 'entropy', 'log_loss']
for criterion in criteria:
    tree = DecisionTreeClassifier(criterion=criterion, max_depth=3, random_state=42)
    tree.fit(X_train, y_train)
    print(f"{criterion}准则的测试集准确率:{tree.score(X_test, y_test):.2f}")
    
    # 可视化决策边界
    plt.figure()
    x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
    y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1
    xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.02),
                         np.arange(y_min, y_max, 0.02))
    Z = tree.predict(np.c_[xx.ravel(), yy.ravel()])
    Z = Z.reshape(xx.shape)
    plt.contourf(xx, yy, Z, alpha=0.4)
    plt.scatter(X[:, 0], X[:, 1], c=y, s=20, edgecolor='k')
    plt.title(f"Decision Tree ({criterion})")

考试重点中的剪枝处理,在实际代码中对应着这些关键参数:

参数 预剪枝作用 对应考试概念
max_depth 限制树的最大深度 控制模型复杂度
min_samples_split 节点分裂的最小样本数 防止过拟合
min_samples_leaf 叶节点的最小样本数 保证统计显著性
max_leaf_nodes 最大叶节点数量 直接限制树规模

后剪枝的实现相对复杂,Scikit-learn中需要通过cost_complexity_pruning_path计算最佳α值:

path = tree.cost_complexity_pruning_path(X_train, y_train)
ccp_alphas = path.ccp_alphas

# 为每个alpha值训练一棵树
trees = []
for ccp_alpha in ccp_alphas:
    tree = DecisionTreeClassifier(random_state=42, ccp_alpha=ccp_alpha)
    tree.fit(X_train, y_train)
    trees.append(tree)

3. 神经网络:从感知机到深度学习

感知机是神经网络的基石,但考试中常混淆它与逻辑回归的区别。让我们用代码揭示它们的联系:

from sklearn.linear_model import Perceptron, LogisticRegression

# 感知机实现
perceptron = Perceptron(eta0=0.1, random_state=42)
perceptron.fit(X_train, y_train)

# 逻辑回归实现
log_reg = LogisticRegression(multi_class='multinomial', solver='lbfgs')
log_reg.fit(X_train, y_train)

# 可视化决策边界对比
models = [('Perceptron', perceptron), ('Logistic Regression', log_reg)]
fig, axes = plt.subplots(1, 2, figsize=(10, 4))
for idx, (name, model) in enumerate(models):
    Z = model.predict(np.c_[xx.ravel(), yy.ravel()])
    Z = Z.reshape(xx.shape)
    axes[idx].contourf(xx, yy, Z, alpha=0.4)
    axes[idx].scatter(X[:, 0], X[:, 1], c=y, s=20, edgecolor='k')
    axes[idx].set_title(name)

当遇到线性不可分问题时,多层神经网络的优势就显现出来了。以下是使用MLPClassifier实现的三层神经网络:

from sklearn.neural_network import MLPClassifier

# 三层神经网络配置
mlp = MLPClassifier(
    hidden_layer_sizes=(10,),  # 单隐层10个神经元
    activation='relu',         # ReLU激活函数
    solver='adam',             # 优化算法
    alpha=0.001,              # L2正则化系数
    batch_size=32,            # 小批量梯度下降
    learning_rate_init=0.001,
    max_iter=500,
    random_state=42
)

# 训练过程可视化
mlp.fit(X_train, y_train)
plt.plot(mlp.loss_curve_)
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.title('Training Loss over Epochs')

BP算法中的关键参数对应着这些考试重点:

  • 学习率 :影响参数更新的步长,太大导致震荡,太小收敛慢
  • 批量大小 :全批量 vs 小批量 vs 随机梯度下降
  • 激活函数 :Sigmoid的梯度消失问题,ReLU的优势
  • 正则化 :L2权重衰减与Dropout的实现

4. 支持向量机:从最大间隔到核技巧

SVM的核心思想是寻找最大间隔超平面,这在代码中体现为:

from sklearn.svm import SVC

# 线性SVM
svm_linear = SVC(kernel='linear', C=1.0)
svm_linear.fit(X_train, y_train)

# 高斯核SVM
svm_rbf = SVC(kernel='rbf', gamma=0.7, C=1.0)
svm_rbf.fit(X_train, y_train)

# 可视化对比
svm_models = [('Linear SVM', svm_linear), ('RBF Kernel SVM', svm_rbf)]
fig, axes = plt.subplots(1, 2, figsize=(10, 4))
for idx, (name, model) in enumerate(svm_models):
    Z = model.predict(np.c_[xx.ravel(), yy.ravel()])
    Z = Z.reshape(xx.shape)
    axes[idx].contourf(xx, yy, Z, alpha=0.4)
    axes[idx].scatter(X[:, 0], X[:, 1], c=y, s=20, edgecolor='k')
    axes[idx].set_title(name)

考试中常考的核函数选择与参数影响:

核类型 数学形式 适用场景 关键参数
线性 K(x,y)=x·y 线性可分 C(惩罚系数)
多项式 K(x,y)=(γx·y+r)^d 适度非线性 d(次数)
高斯 K(x,y)=exp(-γ x-y
Sigmoid K(x,y)=tanh(γx·y+r) 特定场景 γ,r

软间隔SVM的正则化参数C对模型的影响可以通过网格搜索验证:

from sklearn.model_selection import GridSearchCV

param_grid = {'C': [0.1, 1, 10, 100], 'gamma': [0.01, 0.1, 1, 10]}
grid_search = GridSearchCV(SVC(kernel='rbf'), param_grid, cv=5)
grid_search.fit(X_train, y_train)

# 最佳参数组合
print(f"Best parameters: {grid_search.best_params_}")

5. 模型评估与过拟合诊断

考试中模型评估部分常出现的几个关键指标,我们用代码实现它们的计算:

from sklearn.metrics import (accuracy_score, precision_score, 
                             recall_score, f1_score, roc_auc_score,
                             confusion_matrix, classification_report)

# 以决策树为例
y_pred = tree.predict(X_test)
y_proba = tree.predict_proba(X_test)

print("准确率:", accuracy_score(y_test, y_pred))
print("精确率(宏平均):", precision_score(y_test, y_pred, average='macro'))
print("召回率(宏平均):", recall_score(y_test, y_pred, average='macro'))
print("F1分数(宏平均):", f1_score(y_test, y_pred, average='macro'))
print("\n分类报告:\n", classification_report(y_test, y_pred))
print("\n混淆矩阵:\n", confusion_matrix(y_test, y_pred))

过拟合诊断的实用技巧:

  1. 学习曲线分析 :观察训练/验证误差随数据量变化
  2. 验证曲线 :分析超参数对性能的影响
  3. 特征重要性 :识别冗余或无关特征
from sklearn.model_selection import learning_curve

train_sizes, train_scores, test_scores = learning_curve(
    DecisionTreeClassifier(max_depth=5),
    X, y, cv=5, scoring='accuracy',
    train_sizes=np.linspace(0.1, 1.0, 10))

plt.plot(train_sizes, np.mean(train_scores, axis=1), 'o-', label="Training score")
plt.plot(train_sizes, np.mean(test_scores, axis=1), 'o-', label="Cross-validation score")
plt.xlabel("Training examples")
plt.ylabel("Accuracy")
plt.legend()

在项目实践中发现,很多同学容易混淆验证集和测试集的用途。验证集用于模型选择和调参,而测试集只应在最终评估时使用一次。这种数据划分策略在Scikit-learn中可以通过train_test_split多次实现:

# 完整的数据流划分示例
X_train_val, X_test, y_train_val, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
X_train, X_val, y_train, y_val = train_test_split(X_train_val, y_train_val, test_size=0.25, random_state=42)

print(f"训练集: {X_train.shape[0]}样本, 验证集: {X_val.shape[0]}样本, 测试集: {X_test.shape[0]}样本")

更多推荐