神经网络(①MNIST 手写数字识别)
🛠️ 创建虚拟环境
python -m venv ai
这会在当前目录下生成一个名为 ai/
的文件夹,里面包含独立的 Python 解释器和库环境。
🛠️ 激活虚拟环境
.\ai\Scripts\Activate
🛠️ 安装依赖
pip install tensorflow matplotlib numpy
MNIST 数据集会自动从网络下载一次,并缓存在本地是这个代码
(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()
📂 默认下载位置
Windows: C:\Users\<你的用户名>\.keras\datasets\
当你在代码里加上:
import os
os.environ["KERAS_HOME"] = "."
然后运行
from tensorflow.keras.datasets import mnist
(x_train, y_train), (x_test, y_test) = mnist.load_data()
Keras 会检查当前目录下是否存在 datasets/ 文件夹:
如果没有,它会自动新建 datasets/ 文件夹;
然后把 mnist.npz 下载到 ./datasets/mnist.npz;
以后再运行时,就直接从这个文件读取,不会重复下载。
MNIST 手写数字识别
import os
# 设置 Keras 数据集缓存目录为当前目录
os.environ["KERAS_HOME"] = "."import tensorflow as tf
from tensorflow.keras import layers, models
import matplotlib.pyplot as plt
import numpy as np# 1. 加载数据集(会下载到 ./datasets/mnist.npz)
(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()# 2. 数据预处理:归一化到 0-1
x_train = x_train.astype("float32") / 255.0
x_test = x_test.astype("float32") / 255.0# 3. 构建模型(全连接网络)
model = models.Sequential([layers.Flatten(input_shape=(28, 28)), # 把28x28展开成784维layers.Dense(128, activation="relu"), # 隐藏层layers.Dense(10, activation="softmax") # 输出层,10个类别
])# 4. 编译模型
model.compile(optimizer="adam",loss="sparse_categorical_crossentropy",metrics=["accuracy"])# 5. 训练模型
history = model.fit(x_train, y_train,epochs=5, batch_size=128,validation_split=0.2)# 6. 评估模型
test_loss, test_acc = model.evaluate(x_test, y_test)
print("测试集准确率:", test_acc)# 7. 预测示例
sample = x_test[0].reshape(1, 28, 28)
prediction = model.predict(sample)
print("预测结果:", np.argmax(prediction), "真实标签:", y_test[0])# 8. 可视化预测结果
plt.imshow(x_test[0], cmap="gray")
plt.title(f"预测: {np.argmax(prediction)} / 真实: {y_test[0]}")
plt.show()
训练集 (train)
(x_train, y_train)用来“教”模型,让模型在这些数据上学习规律。x_train:输入数据(手写数字图片)y_train:对应的标签(真实数字 0–9)比如:一张 28×28 的图片 → 标签是 7。
测试集 (test)
(x_test, y_test)用来“考”模型,检验它学得好不好。这些数据模型在训练过程中没见过,所以能反映模型的泛化能力。x_test:测试图片y_test:真实标签
train 是“教科书”,test 是“考试卷”。模型要在教科书上学习,在考试卷上验证。
像素值归一化 ≠ “非黑即白”。
🔎 正确理解
原始像素值:灰度图里每个像素是 0–255 的整数。
0 = 黑255 = 白中间值(比如 128)= 灰色
归一化:把 0–255 的范围缩放到 0–1 的小数范围。
0 → 0.0128 → 0.5255 → 1.0
🖼️ 1. 28×28 → Flatten → 784 维
28×28:一张 MNIST 手写数字图片就是一个 28 行 × 28 列的“像素矩阵”。
可以想象成一张小小的方格纸,每个格子里有一个像素值(0–255)。
Flatten:把二维的 28×28 矩阵“拉直”成一行。
28×28 = 784,所以就变成了一个长度为 784 的向量。
类比:把一张 28 行的表格一行一行抄下来,拼成一长串数字。
输入层:神经网络的第一层就是接收这 784 个数字作为输入。
🧩 2. 隐藏层(Hidden Layer)
位置:输入层和输出层之间的“中间计算层”。
作用:提取特征。
比如:某些神经元可能学会识别“竖线”,某些学会识别“弯曲”,某些学会识别“交叉”。
128 个神经元:就像有 128 个小专家,每个专家都在关注不同的特征。
激活函数 ReLU:让这些神经元能学到非线性特征(比如弯曲,而不仅仅是直线)。
类比:
输入层 = 原始像素(原材料)
隐藏层 = 工厂里的工人(加工原材料,提取有用特征)
🎯 3. 输出层(Output Layer)
10 个神经元:对应数字 0–9。
Softmax:把输出转成概率分布。
比如模型输出:
[0.01, 0.02, 0.95, 0.01, 0.01, 0, 0, 0, 0, 0]
→ 意思是“95% 的概率是数字 2”。
最终预测:取概率最大的那个数字。
Softmax
作用:把一堆数字,变成“概率分布”。
👉 举个例子: 模型最后输出了 10 个数(对应数字 0–9):
[2.0, 1.0, 0.1, 3.0, 0.5, ...]
Softmax 会把它们转成概率:
[0.1, 0.05, 0.02, 0.7, 0.03, ...]
每个数都在 0–1 之间
所有数加起来 = 1
最大的那个概率就是模型的预测结果
👉 类比: 就像一场选举,10 个候选人(数字 0–9)都拿到一些票数,Softmax 会把票数换算成“得票率”,最后得票率最高的就是预测结果。
ReLU
公式:f(x) = max(0, x)
如果输入是正数 → 原样输出
如果输入是负数 → 直接变成 0
所以 ReLU 的作用就是:只保留有用的正信号,把负的干扰信号清零。
隐藏层
一个神经元的计算:
h = 激活函数( 权重 × 输入 + 偏置 )
1. 输入(784 个像素)
想象你要判断一张手写数字图片是不是“数字 7”。
输入就是这张图片的 784 个像素值(28×28)。
每个像素就是一个小信号:黑、灰、白。
2. 权重(重要性系数)
神经元不会把所有像素都当成一样重要。
比如:
“数字 7 的斜线”区域 → 权重大(更重要)。
“空白背景”区域 → 权重小(不重要)。
所以每个输入像素都会乘上一个“权重”。
👉 类比: 你在考试判卷时,“大题”分值高(权重大),小题分值低(权重小)。
3. 加权求和 + 偏置
把所有像素 × 权重 的结果加起来,得到一个总分。
然后再加一个 偏置 b,相当于“调节门槛”。
👉 类比:
就像你要判断一个人是不是“高个子”:
输入 = 身高(比如 180cm)
权重 = 1(身高很重要)
偏置 = -170(门槛)
结果 = 180×1 - 170 = 10 → 大于 0,说明“是高个子”。
4. 激活函数 (ReLU)
现在我们有了一个数值(可能是正的,也可能是负的)。
ReLU 的规则:
如果结果 ≤ 0 → 输出 0(神经元不激活,沉默)。
如果结果 > 0 → 输出这个数(神经元激活,点亮)。
👉 类比:
就像一个“灯泡开关”:
电流不足(≤0) → 灯泡不亮。
电流够大(>0) → 灯泡亮起来。
权重和偏置不是你手动一个个去设置的,而是模型在训练过程中自动学出来的参数。
🔹 训练前
权重 (Weights):一开始会被随机初始化成一些很小的数(不是全 0,也不是你自己设定的固定值)。
偏置 (Biases):通常初始化为 0 或一个小数。
👉 这样做的目的是:让每个神经元一开始“看法不同”,避免大家都学到一样的东西。
🔹 训练中
当你把训练数据(比如 MNIST 的手写数字)输入网络:
前向传播:网络根据当前的权重和偏置算出预测结果。
计算误差:预测结果和真实标签对比,得到“损失值”。
反向传播:根据误差,计算每个权重和偏置应该往哪个方向调整。
梯度下降(优化器 Adam/SGD 等):一步步更新权重和偏置,让预测越来越准。
👉 所以,权重和偏置是 自动更新的,不是你手动调的。