手写数字识别之训练和保存模型
1 课程概览
本课讲解手写数字识别案例的模型训练和保存。详细讲解数据预处理(归一化)、分层抽样(stratify参数)、模型训练、模型评估,以及使用joblib保存训练好的模型。重点强调先归一化再切分数据集的优化思路,以及模型保存的重要性。
2 核心概念与定义
- 归一化(Normalization):将像素值除以255,缩放到0~1范围。
- stratify:train_test_split的参数,参考Y轴进行分层抽样,保持标签比例均衡。
- joblib:sklearn的外部扩展库,用于保存和加载模型。
- dump:joblib的保存方法,将模型对象保存到文件。
- pkl文件:Python(pandas)独有的文件格式,效率更快。
3 算法与模型详解
3.1 模型训练流程
- 加载数据集
- 数据预处理:
- 拆分特征列和标签列
- 对特征列进行归一化(除以255)
- 拆分训练集和测试集(使用stratify)
- 模型训练:
- 创建KNN模型对象
- fit训练模型
- 模型评估:
- score方法计算准确率
- accuracy_score计算准确率
- 保存模型:
- joblib.dump保存到pkl文件
3.2 归一化原理
公式: $$X_{norm} = \frac{X}{255}$$
说明:
- 像素值范围:0~255
- 归一化后范围:0~1
- 最小值为0,所以省略减法步骤
- 直接除以255即可
为什么先归一化再切分:
- 先归一化:只需做一次
- 先切分再归一化:训练集和测试集各做一次(两次)
3.3 分层抽样(stratify)
| 参数 | 说明 |
|---|---|
| stratify=y | 参考Y轴(标签)进行抽取 |
作用:保持训练集和测试集中各类别标签的比例均衡,避免出现某一类别全部在训练集或全部在测试集的情况。
示例:
- 不使用stratify:可能训练集全是0~7,测试集全是8~9
- 使用stratify:0~9各类别按比例分布在训练集和测试集
3.4 模型评估方式
| 方式 | 方法 | 说明 |
|---|---|---|
| 方式1 | estimator.score(x_test, y_test) | 直接评分 |
| 方式2 | accuracy_score(y_test, y_predict) | 对比真实值和预测值 |
3.5 模型保存
joblib.dump参数:
| 参数 | 说明 | 示例 |
|---|---|---|
| 参1 | 模型对象 | estimator |
| 参2 | 保存路径 | './model/手写数字识别.pkl' |
文件格式:
- .pkl:Python独有格式,效率快
- .pth:效果相同
- .pickle:效果相同
4 数学原理与推导
4.1 归一化公式
$$X_{norm} = \frac{X - X_{min}}{X_{max} - X_{min}} = \frac{X - 0}{255 - 0} = \frac{X}{255}$$
由于最小值为0,公式简化为: $$X_{norm} = \frac{X}{255}$$
5 代码示例
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.metrics import accuracy_score
import joblib
def train_model():
# 1. 加载数据集
df = pd.read_csv('./data/手写数字识别.csv')
# 2. 数据预处理
# 2.1 拆分特征列和标签列
X = df.iloc[:, 1:] # 特征:第2列到最后(784列)
y = df.iloc[:, 0] # 标签:第1列
print("X形状:", X.shape) # (42000, 784)
print("y形状:", y.shape) # (42000,)
# 2.2 对特征列进行归一化(除以255)
X = X / 255
# 2.3 拆分训练集和测试集(使用stratify保持标签比例)
x_train, x_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=22, stratify=y
)
# 3. 模型训练
# 3.1 创建模型对象
estimator = KNeighborsClassifier(n_neighbors=3)
# 3.2 训练模型
estimator.fit(x_train, y_train)
# 4. 模型评估
# 方式1:直接评分
print("准确率(方式1):", estimator.score(x_test, y_test))
# 方式2:accuracy_score
y_predict = estimator.predict(x_test)
print("准确率(方式2):", accuracy_score(y_test, y_predict))
# 5. 保存模型
joblib.dump(estimator, './model/手写数字识别.pkl')
print("模型保存成功")
if __name__ == '__main__':
train_model()
输出示例:
X形状: (42000, 784)
y形状: (42000,)
准确率(方式1): 0.9666666666666667
准确率(方式2): 0.9666666666666667
模型保存成功
6 重难点与易错提醒
- ❗重点:归一化公式为X/255,不能整除(//),否则全为0。
- ❗重点:stratify=y保持训练集和测试集中标签比例均衡。
- ❗重点:先归一化再切分,只需做一次归一化操作。
- ❗重点:joblib.dump保存模型,参1为模型对象,参2为路径。
- ⚠️易错:使用整除(//)会导致所有像素值变为0。
- ⚠️易错:忘记创建model目录,导致保存失败。
- ⚠️易错:pkl文件不能手动修改,否则无法读取。
- 💡深入理解:模型保存后可重复使用,避免每次重新训练。
7 课堂问答精选
Q: 为什么先归一化再切分数据集?
A:
- 先归一化再切分:只需做一次归一化操作。
- 先切分再归一化:训练集和测试集各做一次归一化(两次操作)。 先归一化再切分可以减少操作次数,提高效率。
Q: stratify参数的作用是什么?
A: stratify=y参考Y轴(标签)进行分层抽样,保持训练集和测试集中各类别标签的比例均衡。避免出现某一类别全部在训练集或全部在测试集的情况,提高模型训练效果。
Q: 为什么不能使用整除(//)?
A: 像素值范围是0~255,如果使用整除(//255),大部分值都会变为0(如60//255=0),导致数据丢失。必须使用普通除法(/255),将像素值缩放到0~1范围。
8 本课小结
- 归一化:X/255,将像素值缩放到0~1范围,不能整除。
- stratify=y:分层抽样,保持标签比例均衡。
- 先归一化再切分:减少操作次数,提高效率。
- 模型评估:score方法或accuracy_score函数。
- 模型保存:joblib.dump(estimator, path),保存为pkl文件。
- pkl文件不能手动修改,否则无法读取。
9 延伸思考与实践
- 实践:修改n_neighbors值,观察准确率变化。
- 思考:为什么模型保存后可以重复使用?
- 预习:如何加载保存的模型进行预测。