人工智能原理代码实战

前置理论

神经网络的前向传播:核心每一层都是加权求和激活函数,实质为矩阵运算+非线性运算

常见的激活函数:

  • Sigmoid()函数:可以将数据转换成0~1的概率 $$f(x)=\frac{1}{1+e^{-x}}$$
  • ReLU()函数:当输入小于0时,输出0;输入大于0时则为线性函数,直接输出值$$f(x)=max(0,x)$$
  • Tanh()函数:是一种三角函数,其输出范围是-1~1,对于神经网络来说,其优点是更容易处理负值(Sigmoid()和ReLU()函数没有负值) $$f(x)=\frac{\sin x}{\cos x}$$
  • Softmax()函数:讲输入值转换成0~1的实数,如同是对应输入值的概率程序

image-20260621113225021

神将网络损失函数:预测误差+正则化惩罚,损失函数越小,表示模型越好。

深度学习的回归问题经常使用均方误差,而分类问题则是使用交叉熵

image-20260621113331824

神将网络反向传播高效计算所有参数的误差梯度,从而指导网络向更小损失的方向更新

梯度下降法:知道参数更新方向,沿着使频率下降最快的方向更新参数

就是每一个隐藏层中的加权求和部分,神经网络最开始每一条边上的权值肯定不是一开始就好的(甚至可以说最开始每条边的权值就是随便设置的),然后通过反向传播对每条边的权值进行调整,最终是它的预测达到预期误差范围内。

image-20260621113356815

神经网络的学习过程:

image-20260621114044762

上面这几个图中的理论看懂个大概就可以写代码了

神经网络

这里采用:TensorFlow/Keras实战入门—训练第一个神经网络模型 进行关键程序分析

神经网络模型

分类

基础网络模型

tf.keras.Sequential这里实现了一个简单的多层感知机 MLP ,Keras 中最基础的模型容器,用于按线性顺序堆叠网络层(一层接一层,无分支、无跳跃连接等复杂拓扑)

1
2
3
4
5
6
7
8
9
10
model = tf.keras.Sequential([
#输入层 将二维特征图展开为一维向量 28*28 = 784
tf.keras.layers.Flatten(input_shape=[28,28]),
#添加一个层 隐藏层 300个神经元 激活函数relu
tf.keras.layers.Dense(300, activation="relu"),
#Dense 全连接层(又称密集层),是神经网络的基础单元。该层每个神经元都与上一层所有神经元相连,通过权重矩阵对输入做线性变换,再通过激活函数引入非线性表达能力。
tf.keras.layers.Dense(100, activation="relu"),
#输出层 10个神经元(输出10类的概率) 激活函数softmax
tf.keras.layers.Dense(10, activation="softmax"),
])

配置模型

model.compile编译模型,为模型设定训练规则,确定优化器、损失函数、评估指标三大核心要素

使用 Adam 优化器(自适应学习率)和 categorical_crossentropy损失函数。

1
2
3
model.compile(optimizer='adam', 
loss='categorical_crossentropy',
metrics=['accuracy'])
  • optimizer='adam' :优化器 在模型训练过程中,通过计算损失函数的梯度来更新网络权重,从而使误差最小化

  • loss='categorical_crossentropy':损失函数(多类交叉熵)

    • 1.用于多分类问题
    • 2.真实标签是整数
    • 3.计算预测和真是之间的差距
  • metrics=['accuracy']) :评估指标 是计算模型预测正确的样本占总样本的比例(即准确率),从而直观地告诉你模型的表现如何

训练模型

model.fit:启动训练循环,将训练数据输入模型,按设定的轮次和批次迭代更新权重,同时可实时验证模型效果

1
2
3
4
history = model.fit(train_images, train_labels_cat, #模型的输入数据和对应的真实标签
epochs=20,
batch_size=64,
validation_split=0.2)
  • epochs=20: 遍历数据集20次,训练20轮
  • batch_size=64: 每次更新参数使用64张图片
  • validation_split=0.2: 划出20%训练数据用于验证集,不参与训练,仅用于评估

如果对已知数据有很高的正确性,但是对位置数据的预测很差,就成为过度拟合,即一个没有泛化性的预测模型。

即当训练集的表现效果很好,但验证集趋于平缓,此时就会造成过拟合

回归

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
tf.random.set_seed(42)
norm_layer = tf.keras.layers.Normalization()

model = tf.keras.Sequential([
norm_layer,
tf.leras.layers.Dense(50,actication="relu"),
tf.leras.layers.Dense(50,actication="relu"),
tf.leras.layers.Dense(50,actication="relu"),
tf.keras.layers.Dense(1) # 回归嘛,最后预测它值是多少就行了
])
# 回归任务的配置
model.compile(
loss="mse", # 均方误差
optimizer="Adam",
metrics=["RMSE"]
)

image-20260621152936826

随机搜索

1
2
3
random_search_tuner = kt.RandomSearch(build_model, objective="val_accurancy", max_trials=5, 							overwrite=True, directory="my_fashion_mnist", project_name="my_rnd_search", seed=42)

random_search_tuner.search(X_train, y_train, epochs=10, validation_data=(X_valid, y_valid))

参数说明

  • objective:以验证集准确率(val_accuracy)作为优化目标
  • max_trials=5:最多尝试5组超参数组合
  • directory:保存调优结果的目录
  • project_name:本次调优项目名称
  • seed=42:随机种子,保证可复现
  • epochs=10:每个超参数组合训练10个epoch
  • validation_data:验证集,用于评估模型

image-20260621153722724

代码

多分类任务 —— Fashion MNIST 服饰识别

采用Keras内置数据集fashion_mnist来进行训练

数据预加载和数据预处理

采用keras中内部数据进行本次实验train_images, train_labels这部分将用于训练,这部分将用于训练后的测验test_images, test_labels

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
## 数据加载
(train_images, train_labels),(test_images, test_labels) = tf.keras.datasets.fashion_mnist.load_data()

print(f"训练集图片形状: {train_images.shape}") # (60000, 28, 28)
print(f"训练集标签形状: {train_labels.shape}") # (60000,)
print(f"标签示例(原始): {train_labels[:5]}") # 如 [9 0 0 3 0]

## 数据预处理
# 归一化
train_images = train_images / 255.0
test_images = test_images / 255.0
print(f"\n归一化后像素值范围: {train_images.min():.1f} ~ {train_images.max():.1f}")

# 标签 One-hot 编码
train_labels_cat = tf.keras.utils.to_categorical(train_labels, num_classes=10)
test_labels_cat = tf.keras.utils.to_categorical(test_labels, num_classes=10)
print(f"\nOne-Hot前: {train_labels[0]}") # 如 9
print(f"One-Hot后: {train_labels_cat[0]}") # 如 [0. 0. 0. 0. 0. 0. 0. 0. 0. 1.]

构建并配置模型

前面已经讲过这部分代码的作用,再简单提一下

构建模型部分:

  • Flatten 仅用作数据格式转换,将n维的转换为一维的
  • 两个Dense分别是
    • 第 1 层隐藏层,300 个神经元,提取低级特征。输入 784 × 神经元 300 + 偏置 300 = 235500
    • 第 2 层隐藏层,100 个神经元,组合低级特征形成更高级特征。输入 300 × 神经元 100 + 偏置 100 = 30100
  • 第三个Dense是输出层,10 个神经元对应 10 个类别。输入 100 × 神经元 10 + 偏置 10 = 1010

编译模型部分:

  • 优化器 adam

    本质是自适应学习率的梯度下降算法。深度学习训练的目标是 “最小化损失”,梯度下降就是沿着损失下降的方向,一步步调整网络里的所有权重。

  • 损失函数 categorical_crossentropy

    损失是衡量 “模型预测错了多少” 的量化标准。多分类任务使用分类交叉熵,它计算 “预测概率分布” 和 “真实 One-Hot 分布” 之间的差距:预测越准,损失值越小;预测偏差越大,损失值越大。

  • 评估指标 accuracy

    准确率 = 预测正确的样本数 / 总样本数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
## 构建模型
model = tf.keras.Sequential([
tf.keras.layers.Flatten(input_shape=[28, 28]),
tf.keras.layers.Dense(300, activation='relu'),
tf.keras.layers.Dense(100, activation='relu'),
tf.keras.layers.Dense(10, activation='softmax')
])

model.summary()#打印神经网络模型信息

## 编译模型
model.compile(optimizer='adam',
loss='categorical_crossentropy',
metrics=['accuracy'])

训练模型并测试

model.fit() 是训练的核心,背后是前面理论的最后一张图的前向传播 + 反向传播的循环迭代

epoch:把全部训练数据完整过一遍,称为 1 个 epoch。这里设置 20,就是让模型把全部训练样本反复学习 20 轮。

batch_size=64:一次性喂入 64 张图片,计算一次损失、更新一次权重。相比单张更新,批量更新提升了训练效率和梯度稳定性。

validation_split=0.2:从训练集中划出 20% 作为验证集,训练时不参与权重更新,专门用来实时评估模型在 “未见过的数据” 上的表现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
## 训练模型
history = model.fit(
train_images,train_labels_cat,
epochs=20,
batch_size=64,
validation_split=0.2
)

test_loss, test_acc = model.evaluate(test_images,test_labels_cat,verbose=0)
print(f"\n【分类】测试集准确率: {test_acc:.4f}")

predictions = model.predict(test_images[:3])
print(f"预测结果(概率):\n{np.round(predictions, 2)}")
print(f"预测类别: {np.argmax(predictions, axis=1)}") # 取概率最大的类别
print(f"真实类别: {test_labels[:3]}")

结合结果分析

运行结果:

一、数据加载和数据预处理

1
2
3
4
5
6
7
8
9
PART 1: 分类任务
训练集图片形状: (60000, 28, 28)
训练集标签形状: (60000,)
标签示例(原始): [9 0 0 3 0]

归一化后像素值范围: 0.0 ~ 1.0

One-Hot前: 9
One-Hot后: [0. 0. 0. 0. 0. 0. 0. 0. 0. 1.]

这里是model.summary() 输出的是网络的完整结构和参数量,对应你代码里搭建的三层全连接网络:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
flatten (Flatten) (None, 784) 0

dense (Dense) (None, 300) 235500

dense_1 (Dense) (None, 100) 30100

dense_2 (Dense) (None, 10) 1010

=================================================================
Total params: 266,610
Trainable params: 266,610
Non-trainable params: 0
_________________________________________________________________

二、训练过程详解:20 轮 Epoch 的核心规律

这是份结果最关键的部分,20 行训练日志完整呈现了深度学习「学习→拟合→过拟合」的曲线

阶段 1:正常拟合期(Epoch 1 ~ Epoch 8)

1
2
3
4
5
6
7
8
9
10
11
Epoch 1/20
750/750 [==============================] - 2s 2ms/step - loss: 0.5064 - accuracy: 0.8193 - val_loss: 0.4630 - val_accuracy: 0.8282
Epoch 2/20
750/750 [==============================] - 1s 2ms/step - loss: 0.3691 - accuracy: 0.8649 - val_loss: 0.3757 - val_accuracy: 0.8609
......
Epoch 6/20
750/750 [==============================] - 2s 2ms/step - loss: 0.2734 - accuracy: 0.8988 - val_loss: 0.3182 - val_accuracy: 0.8852
Epoch 7/20
750/750 [==============================] - 2s 2ms/step - loss: 0.2574 - accuracy: 0.9028 - val_loss: 0.3372 - val_accuracy: 0.8768
Epoch 8/20
750/750 [==============================] - 2s 2ms/step - loss: 0.2479 - accuracy: 0.9057 - val_loss: 0.3312 - val_accuracy: 0.8819

这是模型真正在「学规律」的阶段:

  • 训练损失 loss:从 0.5135 持续下降到 0.2487
  • 训练准确率 accuracy:从 0.8191 稳步上升到 0.9078
  • 验证损失 val_loss:从 0.4132 下降到全程最低点 0.3048
  • 验证准确率 val_accuracy:从 0.8535 上升到 0.8923

这个阶段里,训练集和验证集的表现同步变好,说明模型正在从数据中提取通用的服饰特征,泛化能力持续提升,是训练的最优区间。

阶段 2:过拟合显现期(Epoch 9 ~ Epoch 20)

1
2
3
4
5
6
7
8
9
10
11
12
13
Epoch 9/20
750/750 [==============================] - 2s 3ms/step - loss: 0.2374 - accuracy: 0.9101 - val_loss: 0.3309 - val_accuracy: 0.8863
Epoch 10/20
750/750 [==============================] - 2s 3ms/step - loss: 0.2285 - accuracy: 0.9133 - val_loss: 0.3162 - val_accuracy: 0.8898
Epoch 11/20
750/750 [==============================] - 2s 3ms/step - loss: 0.2168 - accuracy: 0.9185 - val_loss: 0.3232 - val_accuracy: 0.8907
......
Epoch 19/20
750/750 [==============================] - 2s 2ms/step - loss: 0.1622 - accuracy: 0.9380 - val_loss: 0.3314 - val_accuracy: 0.8912
Epoch 20/20
750/750 [==============================] - 2s 2ms/step - loss: 0.1588 - accuracy: 0.9389 - val_loss: 0.3549 - val_accuracy: 0.8875

【分类】测试集准确率: 0.8808

从第 9 轮开始,训练出现了明显的分化,也是深度学习最经典的现象:

  • 训练集表现继续一路变好:损失从 0.2357 降到 0.1527,准确率从 0.9118 升到 0.9414
  • 验证集表现不再提升,甚至开始变差:
    • 验证损失不再下降,整体震荡走高,最后一轮涨到 0.3656
    • 验证准确率在 0.88~0.89 之间反复波动,最后一轮甚至掉到 0.8823

过拟合

模型容量足够大,训练轮数过多后,它不再只学通用规律,开始「死记硬背」训练集里的噪声、个别样本的独有细节;面对从未见过的验证集数据,泛化能力不再提升,反而下降。

三、最终测试集结果

测试集是模型全程完全没接触过的数据,相当于「最终闭卷考试」

1
2
3
4
5
6
7
8
9
1/1 [==============================] - 0s 43ms/step
预测结果(概率):
[[0. 0. 0. 0. 0. 0. 0. 0. 0. 1.]
[0. 0. 1. 0. 0. 0. 0. 0. 0. 0.]
[0. 1. 0. 0. 0. 0. 0. 0. 0. 0.]]
预测类别: [9 2 1]
真实类别: [9 2 1]

进程已结束,退出代码为 0

总结

模型不是训练越久越好,前期快速学习通用规律,中期达到泛化能力峰值,继续训练就会进入过拟合

过拟合的常见解决方案

  1. 早停法:监控验证损失,停止下降时立即终止训练
  2. 正则化:L2 正则化惩罚过大权重,Dropout 随机失活神经元,降低模型对个别样本的依赖
  3. 降低模型复杂度:减少层数、减少神经元数量
  4. 增加训练数据:数据量越大,模型越难记住全部样本,越不容易过拟合

回归任务