4. Keras模型训练入门:全连接网络构建、激活函数选择、损失函数与优化器、训练与验证

好,咱们进入实战环节了。前面聊了那么多理论,现在该动手了。这一章,我会带你走一遍Keras模型训练的完整流程。说白了,就是教会你怎么用几行代码,搭出一个能跑的全连接网络。

我个人习惯,学新东西先跑通一个最小例子。有了手感,再深入细节。咱们就从最基础的全连接网络开始。

4.1 全连接网络构建:从零搭积木

全连接网络,也叫密集连接网络。每一层的每个神经元,都和上一层的所有神经元相连。你想想看,这就像公司里每个人都能直接给老板汇报——信息传递很充分,但层级多了就乱套。

在Keras里,构建全连接网络用 Dense 层。我一般这样写:

from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense

model = Sequential([
    Dense(64, activation='relu', input_shape=(10,)),
    Dense(32, activation='relu'),
    Dense(1, activation='sigmoid')
])

这段代码干了三件事:

  • 输入层:接收10个特征的数据
  • 隐藏层:第一层64个神经元,第二层32个神经元
  • 输出层:1个神经元,用sigmoid做二分类
我的小经验:隐藏层的神经元数量,我一般从2的幂次开始试。64、128、256,效果不行再翻倍或减半。别一上来就搞个1000个神经元,训练慢还容易过拟合。

嗯,这里要注意 input_shape 参数。它只在第一层指定,后面的层会自动推导输入维度。我曾经见过有人每层都写 input_shape,结果维度对不上,报错半天找不出原因。

4.2 激活函数选择:给网络注入非线性

激活函数,说白了就是给神经网络引入非线性能力。没有它,再多层也只是线性变换的堆叠,解决不了复杂问题。

常用的激活函数就这几个:

函数名 公式 输出范围 适用场景
ReLU max(0, x) [0, +∞) 隐藏层首选
Sigmoid 1/(1+e⁻ˣ) (0, 1) 二分类输出层
Tanh (eˣ-e⁻ˣ)/(eˣ+e⁻ˣ) (-1, 1) RNN、归一化数据
Softmax eˣⁱ/∑eˣʲ (0,1) 和为1 多分类输出层

我个人习惯,隐藏层无脑用ReLU。为什么?计算简单,梯度不容易消失。但要注意——ReLU有个毛病叫「神经元死亡」。如果输入一直为负,梯度就是0,这个神经元就再也学不动了。

避坑指南:我曾经在一个项目里,用了ReLU做隐藏层,结果训练到一半loss不动了。查了半天,发现学习率设太大,导致大量神经元输出为负,直接「死」了。后来换成LeakyReLU,问题解决。

输出层的选择就明确多了:

  • 二分类 → sigmoid
  • 多分类 → softmax
  • 回归问题 → 不用激活函数(linear)

4.3 损失函数与优化器:训练的「方向盘」和「油门」

损失函数告诉模型「你错得有多离谱」,优化器告诉模型「怎么调整才能更好」。两者缺一不可。

损失函数怎么选?

我总结了一个简单对照表:

问题类型 损失函数 Keras名称
二分类 二元交叉熵 binary_crossentropy
多分类 分类交叉熵 categorical_crossentropy
回归 均方误差 mse
回归(鲁棒) 平均绝对误差 mae

你想想看,如果分类问题用了MSE,梯度会很小,训练慢得像蜗牛。我刚开始学的时候犯过这个错,训练了100个epoch loss还在0.5以上,后来换成交叉熵,10个epoch就收敛了。

优化器怎么选?

优化器这块,我推荐新手直接上 Adam。它自适应学习率,大部分场景表现都不错。

from tensorflow.keras.optimizers import Adam

model.compile(
    optimizer=Adam(learning_rate=0.001),
    loss='binary_crossentropy',
    metrics=['accuracy']
)

常用的优化器对比:

  • SGD:经典但慢,需要精细调学习率
  • Adam:自适应,收敛快,适合大多数场景
  • RMSprop:适合RNN,对非平稳目标效果好
  • Adagrad:适合稀疏数据,但学习率会衰减到0
我的建议:先用Adam跑通,再尝试SGD + 学习率衰减。很多比赛冠军都用SGD,但需要更多调参经验。

4.4 训练与验证:看模型到底学得怎么样

模型搭好了,损失函数和优化器也选好了。接下来就是训练。Keras里训练就一行代码:

history = model.fit(
    x_train, y_train,
    epochs=50,
    batch_size=32,
    validation_split=0.2,
    verbose=1
)

这里几个参数我说一下:

  • epochs:遍历整个训练集的次数。不是越多越好,多了会过拟合。
  • batch_size:每次更新参数用的样本数。32、64、128是常用值。
  • validation_split:从训练集里划出20%做验证集。
  • verbose:1显示进度条,0安静模式,2每个epoch一行。

训练完成后,history.history 里保存了每个epoch的loss和accuracy。我一般会画个图看看:

import matplotlib.pyplot as plt

plt.plot(history.history['loss'], label='train_loss')
plt.plot(history.history['val_loss'], label='val_loss')
plt.legend()
plt.show()
注意:如果训练loss一直下降,但验证loss开始上升,那就是过拟合了。这时候要早点停止,或者加正则化。Keras提供了EarlyStopping回调,可以自动检测。

最后,用测试集做最终评估:

test_loss, test_acc = model.evaluate(x_test, y_test)
print(f'测试集准确率: {test_acc:.4f}')

嗯,到这里,一个完整的Keras训练流程就走通了。从搭网络、选激活函数、定损失函数和优化器,再到训练和验证。你想想看,其实核心代码也就十几行。但每一行背后,都有很多经验和坑。

下一章,我们会把这些知识用到STM32的嵌入式场景里。到时候你会发现,模型训练只是第一步,怎么把它塞进单片机跑起来,才是真正的挑战。