别再死记硬背了!用Python+Scikit-learn复现机器学习期末考点(决策树/神经网络/SVM实战)
用Python实战复现机器学习核心算法:从理论到代码的深度理解
期末考试临近,许多同学面对厚厚的机器学习教材和公式推导感到无从下手。传统的死记硬背不仅效率低下,更难以真正理解算法背后的数学之美。本文将带你用Python和Scikit-learn库,通过编写代码的方式,亲手实现决策树、神经网络和支持向量机(SVM)三大核心算法,让抽象的理论变得触手可及。
1. 环境准备与数据生成
在开始编码之前,我们需要搭建一个适合机器学习实验的环境。推荐使用Anaconda创建独立的Python环境,避免库版本冲突:
conda create -n ml_exam python=3.8
conda activate ml_exam
pip install numpy matplotlib scikit-learn pandas
为了演示算法原理,我们将使用Scikit-learn的
make_classification
函数生成模拟数据集。这种方法可以灵活控制数据特征,便于观察算法行为:
from sklearn.datasets import make_classification
# 生成二分类数据集
X, y = make_classification(
n_samples=500,
n_features=2,
n_informative=2,
n_redundant=0,
n_classes=2,
random_state=42
)
提示:设置
random_state参数可以确保每次生成相同的数据,便于结果复现
2. 决策树:从信息增益到可视化分类边界
决策树是机器学习中最直观的算法之一,它通过一系列if-then规则对数据进行分类。理解决策树的关键在于掌握三种划分标准:信息增益、增益率和基尼指数。
2.1 信息增益计算实战
信息增益基于信息熵的概念,衡量特征对数据纯度的提升程度。让我们手动实现信息熵和信息增益的计算:
import numpy as np
def entropy(y):
"""计算信息熵"""
hist = np.bincount(y)
ps = hist / len(y)
return -np.sum([p * np.log2(p) for p in ps if p > 0])
def information_gain(X, y, feature_idx, threshold):
"""计算信息增益"""
parent_entropy = entropy(y)
# 根据阈值划分数据集
left_indices = X[:, feature_idx] <= threshold
right_indices = X[:, feature_idx] > threshold
n = len(y)
n_left, n_right = sum(left_indices), sum(right_indices)
if n_left == 0 or n_right == 0:
return 0
# 计算子节点熵
e_left = entropy(y[left_indices])
e_right = entropy(y[right_indices])
# 计算信息增益
child_entropy = (n_left/n) * e_left + (n_right/n) * e_right
return parent_entropy - child_entropy
2.2 决策树可视化与剪枝策略
Scikit-learn提供了完整的决策树实现,我们可以训练并可视化决策边界:
from sklearn.tree import DecisionTreeClassifier, plot_tree
import matplotlib.pyplot as plt
# 训练决策树
dt = DecisionTreeClassifier(max_depth=3, criterion='gini')
dt.fit(X, y)
# 可视化决策树
plt.figure(figsize=(12,8))
plot_tree(dt, filled=True, feature_names=['Feature 1', 'Feature 2'])
plt.show()
决策树容易过拟合,剪枝是提高泛化能力的关键技术。Scikit-learn提供了多种剪枝参数:
| 参数 | 作用 | 推荐值 |
|---|---|---|
max_depth
| 限制树的最大深度 | 3-10 |
min_samples_split
| 节点分裂所需最小样本数 | 2-20 |
min_samples_leaf
| 叶节点最少样本数 | 1-10 |
ccp_alpha
| 复杂度剪枝参数 | 0.01-0.1 |
3. 神经网络:从感知机到多层网络
神经网络通过模拟人脑神经元的工作方式实现复杂函数逼近。理解神经网络需要从最简单的感知机开始。
3.1 感知机实现与局限性
感知机是最简单的神经网络,只能解决线性可分问题。我们用Python实现一个感知机:
class Perceptron:
def __init__(self, learning_rate=0.01, n_iters=1000):
self.lr = learning_rate
self.n_iters = n_iters
self.weights = None
self.bias = None
def fit(self, X, y):
n_samples, n_features = X.shape
# 初始化参数
self.weights = np.zeros(n_features)
self.bias = 0
# 训练过程
for _ in range(self.n_iters):
for idx, x_i in enumerate(X):
linear_output = np.dot(x_i, self.weights) + self.bias
y_pred = np.where(linear_output >= 0, 1, 0)
# 参数更新
update = self.lr * (y[idx] - y_pred)
self.weights += update * x_i
self.bias += update
def predict(self, X):
linear_output = np.dot(X, self.weights) + self.bias
return np.where(linear_output >= 0, 1, 0)
3.2 多层神经网络与BP算法
对于非线性问题,我们需要多层神经网络。Scikit-learn的MLPClassifier实现了BP算法:
from sklearn.neural_network import MLPClassifier
# 创建多层感知机
mlp = MLPClassifier(
hidden_layer_sizes=(5, 3), # 两个隐藏层,分别有5和3个神经元
activation='relu', # 使用ReLU激活函数
solver='adam', # 优化算法
max_iter=1000,
random_state=42
)
mlp.fit(X, y)
# 可视化决策边界
def plot_decision_boundary(model, X, y):
# 设置绘图范围
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 = model.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.show()
plot_decision_boundary(mlp, X, y)
4. 支持向量机:从最大间隔到核技巧
SVM通过寻找最大间隔超平面实现分类,是机器学习中的经典算法。
4.1 线性SVM与间隔最大化
让我们用Scikit-learn实现线性SVM并观察支持向量:
from sklearn.svm import SVC
# 训练线性SVM
svm = SVC(kernel='linear', C=1.0)
svm.fit(X, y)
# 获取支持向量
support_vectors = svm.support_vectors_
# 绘制决策边界和支持向量
plt.scatter(X[:, 0], X[:, 1], c=y, s=30, cmap=plt.cm.Paired)
ax = plt.gca()
xlim = ax.get_xlim()
ylim = ax.get_ylim()
# 创建网格来评估模型
xx = np.linspace(xlim[0], xlim[1], 30)
yy = np.linspace(ylim[0], ylim[1], 30)
YY, XX = np.meshgrid(yy, xx)
xy = np.vstack([XX.ravel(), YY.ravel()]).T
Z = svm.decision_function(xy).reshape(XX.shape)
# 绘制决策边界和间隔
ax.contour(XX, YY, Z, colors='k', levels=[-1, 0, 1], alpha=0.5,
linestyles=['--', '-', '--'])
# 绘制支持向量
ax.scatter(support_vectors[:, 0], support_vectors[:, 1], s=100,
linewidth=1, facecolors='none', edgecolors='k')
plt.show()
4.2 核函数与非线性SVM
对于非线性可分数据,核函数可以将数据映射到高维空间。常用的核函数包括:
-
多项式核:
kernel='poly' -
高斯核(RBF):
kernel='rbf' -
Sigmoid核:
kernel='sigmoid'
# 生成非线性可分数据
X_nonlinear, y_nonlinear = make_classification(
n_samples=500,
n_features=2,
n_informative=2,
n_redundant=0,
n_classes=2,
n_clusters_per_class=1,
random_state=42,
class_sep=0.8
)
# 使用RBF核训练SVM
svm_rbf = SVC(kernel='rbf', gamma=0.5, C=1.0)
svm_rbf.fit(X_nonlinear, y_nonlinear)
# 可视化决策边界
plot_decision_boundary(svm_rbf, X_nonlinear, y_nonlinear)
5. 算法对比与实战建议
通过前面的实践,我们已经实现了三种核心算法。下表总结了它们的特性和适用场景:
| 算法 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 决策树 | 解释性强,无需特征缩放 | 容易过拟合 | 结构化数据,需要解释性的场景 |
| 神经网络 | 强大的表示能力 | 需要大量数据,调参复杂 | 图像、语音等复杂模式识别 |
| SVM | 小样本效果好,核技巧强大 | 大规模数据效率低 | 中小规模数据,特别是高维数据 |
在实际项目中,我通常会遵循以下流程选择算法:
- 首先尝试简单的线性模型作为基准
- 对于结构化数据,使用决策树或随机森林
- 对于复杂模式识别问题,考虑神经网络
- 当数据量不大但特征维度高时,优先尝试SVM
注意:模型训练后一定要在独立的测试集上评估性能,避免使用训练数据评估导致的乐观估计
更多推荐



所有评论(0)