机器学习入门:手写数字识别与算法对比
前言 :在前面的一系列文章中,我们陆续学习了 KNN、逻辑回归、SVM、决策树、随机森林、朴素贝叶斯、AdaBoost、XGBoost 等算法。本篇我们将把这些算法放在同一个任务上------手写数字识别,通过实际运行对比它们的表现。这既是对前面所学算法的回顾总结,也能帮助理解不同算法的适用场景和性能差异。
目录
- 一、案例背景
- 二、数据集与预处理
- 三、模型训练与评估
- 四、结果分析
- 五、总结
一、案例背景
手写数字识别是机器学习领域的经典入门问题。给定一张包含手写数字的图片,让计算机自动判断它写的是 0-9 中的哪一个数字。
本案例使用的手写数字图片包含 5000 个样本(每个数字 500 个),其中 2500 个作为训练集,2500 个作为测试集。我们将用 8 种不同的算法分别训练模型,对比它们的识别准确率。
二、数据集与预处理
2.1 数据说明
digits.png 是一张 1000 × 2000 像素的图片,布局如下:
- 共有 50 行 × 100 列 = 5000 个数字
- 每个数字是一个 20 × 20 像素的小方块
- 每个数字出现 500 次
- 前 50 列作为训练集,后 50 列作为测试集
| 数据集 | 样本数 | 说明 |
|---|---|---|
| 训练集 | 2500 | 前 50 列 |
| 测试集 | 2500 | 后 50 列 |

2.2 数据预处理
python
import numpy as np
import cv2
from sklearn.neighbors import KNeighborsClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC
from sklearn.ensemble import RandomForestClassifier, AdaBoostClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.naive_bayes import GaussianNB
from xgboost import XGBClassifier
# 读取图片
img = cv2.imread('digits.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 分割图片:纵向切成50行,横向切成100列
a = np.vsplit(gray, 50)
cells = [np.hsplit(row, 100) for row in a]
x = np.array(cells)
# 划分训练集和测试集
train = x[:, :50]
test = x[:, 50:100]
# 展平为特征向量(每个数字20×20=400维)
train_new = train.reshape(-1, 400).astype(np.float32)
test_new = test.reshape(-1, 400).astype(np.float32)
# 生成标签:每个数字出现250次(50行 × 5列)
k = np.arange(10)
train_labels = np.repeat(k, 250)
test_labels = np.repeat(k, 250)
三、模型训练与评估
3.1 完整代码
python
# 算法列表
models = {
'KNN': KNeighborsClassifier(n_neighbors=9),
'逻辑回归': LogisticRegression(max_iter=1000),
'SVM': SVC(kernel='rbf'),
'随机森林': RandomForestClassifier(n_estimators=50),
'决策树': DecisionTreeClassifier(max_depth=10),
'朴素贝叶斯': GaussianNB(),
'AdaBoost': AdaBoostClassifier(n_estimators=50),
'XGBoost': XGBClassifier(n_estimators=50, eval_metric='mlogloss')
}
print("=" * 50)
print("手写数字识别各算法准确率对比")
print("=" * 50)
for name, model in models.items():
model.fit(train_new, train_labels)
result = model.predict(test_new)
acc = np.count_nonzero(result == test_labels) * 100.0 / len(test_labels)
print(f"{name:10} 准确率: {acc:.2f}%")
3.2 运行结果
==================================================
手写数字识别各算法准确率对比
==================================================
KNN 准确率: 91.00%
逻辑回归 准确率: 86.20%
SVM 准确率: 94.20%
随机森林 准确率: 92.20%
决策树 准确率: 74.16%
朴素贝叶斯 准确率: 59.28%
AdaBoost 准确率: 63.28%
XGBoost 准确率: 92.64%
四、结果分析
4.1 算法性能排名
| 排名 | 算法 | 准确率 | 算法类型 |
|---|---|---|---|
| 1 | SVM | 94.20% | 支持向量机 |
| 2 | XGBoost | 92.64% | 集成学习(Boosting) |
| 3 | 随机森林 | 92.20% | 集成学习(Bagging) |
| 4 | KNN | 91.00% | 基于距离 |
| 5 | 逻辑回归 | 86.20% | 线性模型 |
| 6 | 决策树 | 74.08% | 树模型(单棵) |
| 7 | AdaBoost | 63.28% | 集成学习(Boosting) |
| 8 | 朴素贝叶斯 | 59.28% | 概率模型 |
4.2 关键结论
| 算法类型 | 表现 | 原因分析 |
|---|---|---|
| SVM | 最佳 | 核函数能有效处理手写数字这类高维非线性问题 |
| 集成学习 | 优秀 | XGBoost 和随机森林通过组合多棵树,泛化能力强 |
| KNN | 良好 | 简单有效,但计算量大,不适合大规模数据 |
| 逻辑回归 | 一般 | 线性模型处理非线性问题能力有限 |
| 决策树 | 较差 | 单棵树容易过拟合,泛化能力弱 |
| 朴素贝叶斯 | 最差 | 特征独立假设在图像数据中不成立,导致效果差 |
总结 :在手写数字识别这类图像分类任务中,SVM 和 集成学习算法 (XGBoost、随机森林)表现最为出色,而朴素贝叶斯因特征独立性假设不成立,效果最差。
五、总结
核心知识点速查
| 知识点 | 说明 |
|---|---|
| 数据预处理 | 图像分割 → 展平为特征向量 → 生成标签 |
| 训练/测试划分 | 训练集和测试集样本量 1:1(各 2500 个) |
| 八种算法对比 | 从准确率、算法类型两个维度分析 |
| 最佳算法 | SVM(94.20%) |
| 最差算法 | 朴素贝叶斯(59.28%) |
注意事项
| 要点 | 说明 |
|---|---|
| 数据形状 | 确保 reshape 后维度为 (2500, 400) |
| 标签生成 | np.repeat(k, 250) 确保每个数字有 250 个标签 |
| 算法参数 | 不同算法可能需要调参才能达到最佳效果 |
| 图像质量 | 手写数字图片质量影响最终识别率 |