SilverIce Toolbox
Back to course

Stage 2 / Chapter 6

第6章:TensorFlow/Keras 实现与训练 | Chapter 6: TensorFlow/Keras Implementation

阶段定位 | Stage: 第二阶段 — 神经网络与进阶算法 预计学时 | Duration: 3~4 小时

---

学习目标 | Learning Objectives

中文:

  • 掌握 Keras Sequential API 的基本用法
  • 理解 model.compile 中 optimizer、loss、metrics 的含义
  • 能对比 NumPy 手写与框架实现的效率差异
  • 理解 model.fit 返回的 history 对象及其可视化
  • 掌握模型保存/加载和基本推理流程

English:

  • Master basic Keras Sequential API usage
  • Understand optimizer, loss, and metrics in model.compile
  • Compare efficiency between NumPy from-scratch and framework implementation
  • Understand the history object from model.fit and its visualization
  • Master model save/load and basic inference pipeline

---

6.1 框架 vs 手写 | Framework vs From-Scratch

中文解释

为什么需要框架?

方面NumPy 手写TensorFlow/PyTorch
自动求导手动推导 + 实现自动微分(Autograd)
GPU 加速不支持原生支持 CUDA
工程化自己管理维度、保存、部署内置模型保存、Serving、量化
调试容易需要学习工具(TensorBoard)
学习价值极高(理解原理)高(工程实践)

学习路径建议

NumPy 手写 → TensorFlow/Keras 快速验证 → PyTorch 研究/生产
  • 手写:理解梯度、维度、前向/反向传播
  • Keras:快速搭建、调参、验证想法
  • PyTorch:动态图、自定义研究、工业部署

English Explanation

Why use frameworks?

  • Automatic differentiation (no manual gradient derivation)
  • Native GPU acceleration (CUDA)
  • Built-in model saving, serving, quantization
  • Production-ready tooling

Learning path:

NumPy (understand principles) → Keras (rapid prototyping) → PyTorch (research/production)

---

6.2 Keras 核心 API | Keras Core API

中文解释

Sequential 模型

最简单的模型类型:层按顺序堆叠。

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

model = Sequential([
    Dense(25, activation='relu', input_shape=(20,)),
    Dense(15, activation='relu'),
    Dense(1, activation='sigmoid')
])

层参数解析

参数含义
units神经元数量 = 输出维度
activation激活函数:relu, sigmoid, tanh, softmax
input_shape第一层必需:输入特征的维度
name层的名称(可选,调试有用)

编译模型

python
model.compile(
    optimizer='adam',
    loss='binary_crossentropy',
    metrics=['accuracy']
)
参数选项适用场景
optimizer'sgd', 'adam', 'rmsprop', 'adamw'默认 Adam;大规模 CV 用 SGD+Momentum
loss'mse', 'binary_crossentropy', 'categorical_crossentropy'回归/二分类/多分类
metrics'accuracy', 'precision', 'recall', 'auc'监控指标

训练模型

python
history = model.fit(
    X_train, y_train,
    epochs=20,
    batch_size=32,
    validation_split=0.1,
    verbose=1
)
参数含义
epochs遍历完整训练集的次数
batch_size每步更新的样本数
validation_split从训练集中划出多少比例做验证
verbose0=静默, 1=进度条, 2=每 epoch 一行

English Explanation

Model Compilation:

python
model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])

Training:

python
history = model.fit(X_train, y_train, epochs=20, batch_size=32, validation_split=0.1)

---

6.3 完整实现:Keras 神经网络训练

代码案例

python
import numpy as np
import tensorflow as tf
from tensorflow.keras import Sequential
from tensorflow.keras.layers import Dense
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt

np.random.seed(42)
tf.random.set_seed(42)

# ========== 1. 生成数据 ==========
X, y = make_classification(n_samples=1000, n_features=20, n_classes=2,
                           n_informative=15, random_state=42)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

print(f"训练集: {X_train.shape}, 测试集: {X_test.shape}")

# ========== 2. 构建模型 ==========
model = Sequential([
    Dense(25, activation='relu', input_shape=(20,), name='hidden1'),
    Dense(15, activation='relu', name='hidden2'),
    Dense(1, activation='sigmoid', name='output')
])

model.compile(
    optimizer='adam',
    loss='binary_crossentropy',
    metrics=['accuracy']
)

print("\n模型结构:")
model.summary()

# 计算总参数量
print(f"\n总参数量: {model.count_params()}")
print("对比 NumPy 手写:你需要手动管理 W1(25×20), b1(25), W2(15×25), b2(15), W3(1×15), b3(1)")

# ========== 3. 训练 ==========
print("\n开始训练...")
history = model.fit(
    X_train, y_train,
    epochs=20,
    batch_size=32,
    validation_split=0.1,
    verbose=0
)

# ========== 4. 评估 ==========
loss, acc = model.evaluate(X_test, y_test, verbose=0)
print(f"\n测试集 Loss: {loss:.4f}")
print(f"测试集准确率: {acc:.4f}")

# ========== 5. 训练过程可视化 ==========
fig, axes = plt.subplots(1, 2, figsize=(12, 4))

# 损失曲线
axes[0].plot(history.history['loss'], label='Train Loss')
axes[0].plot(history.history['val_loss'], label='Val Loss')
axes[0].set_xlabel('Epoch')
axes[0].set_ylabel('Loss')
axes[0].set_title('Loss Curve')
axes[0].legend()
axes[0].grid(True, alpha=0.3)

# 准确率曲线
axes[1].plot(history.history['accuracy'], label='Train Acc')
axes[1].plot(history.history['val_accuracy'], label='Val Acc')
axes[1].set_xlabel('Epoch')
axes[1].set_ylabel('Accuracy')
axes[1].set_title('Accuracy Curve')
axes[1].legend()
axes[1].grid(True, alpha=0.3)

plt.tight_layout()
plt.savefig('ch06_training_history.png')
print("\n训练历史已保存到 ch06_training_history.png")

# ========== 6. 单样本推理 ==========
sample = X_test[0:1]
pred_prob = model.predict(sample, verbose=0)[0, 0]
pred_class = 1 if pred_prob >= 0.5 else 0
print(f"\n单样本推理:")
print(f"  预测概率: {pred_prob:.4f}")
print(f"  预测类别: {pred_class}")
print(f"  真实类别: {y_test[0]}")

# ========== 7. 模型保存与加载 ==========
model.save('ch06_model.keras')
print("\n模型已保存到 ch06_model.keras")

# 加载
loaded_model = tf.keras.models.load_model('ch06_model.keras')
loaded_pred = loaded_model.predict(sample, verbose=0)[0, 0]
print(f"加载后预测概率: {loaded_pred:.4f} (应与保存前一致)")

典型输出:

模型结构:
Model: "sequential"
_________________________________________________________________
 Layer (type)                Output Shape              Param #
=================================================================
 hidden1 (Dense)             (None, 25)                525
 hidden2 (Dense)             (None, 15)                390
 output (Dense)              (None, 1)                 16
=================================================================
Total params: 931
Trainable params: 931

测试集 Loss: 0.2345
测试集准确率: 0.9100

单样本推理:
  预测概率: 0.8765
  预测类别: 1
  真实类别: 1

---

6.4 训练诊断:History 对象 | Training Diagnostics

中文解释

model.fit 返回的 history 包含每 epoch 的所有指标:

python
history.history = {
    'loss': [0.69, 0.45, 0.32, ...],         # 训练损失
    'accuracy': [0.52, 0.78, 0.85, ...],     # 训练准确率
    'val_loss': [0.70, 0.48, 0.35, ...],     # 验证损失
    'val_accuracy': [0.51, 0.75, 0.83, ...]  # 验证准确率
}

四种典型曲线:

训练损失验证损失诊断对策
下降下降正常训练继续
下降上升过拟合早停、正则化、更多数据
持平持平欠拟合增大模型、训练更久、调学习率
震荡震荡学习率太大减小学习率

早停(Early Stopping)

监控验证损失,连续 N 个 epoch 不下降就停止训练:

python
from tensorflow.keras.callbacks import EarlyStopping

early_stop = EarlyStopping(
    monitor='val_loss',
    patience=5,          # 连续 5 个 epoch 不改善就停
    restore_best_weights=True  # 恢复最优权重
)

model.fit(..., callbacks=[early_stop])

English Explanation

Four typical curve patterns:

  • Train↓ Val↓ → normal, continue
  • Train↓ Val↑ → overfitting, early stopping / regularization
  • Train→ Val→ → underfitting, bigger model / longer training
  • Train~ Val~ → learning rate too high, decrease LR

---

6.5 常见误区 | Common Pitfalls

1. 在测试集上调参

错误: 用测试集准确率来选择模型、调整超参数。 正确: 只用验证集调参,测试集仅在最终评估时使用一次。

2. 忽视数据预处理

Keras 不会自动做特征缩放。如果输入特征尺度差异大,训练会极慢或不收敛。务必先标准化:

python
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)  # 用训练集的 scaler!

3. Batch Size 与 GPU 内存

Batch size 不是越小越好。在 GPU 上,batch size 太小(如 1, 2)会导致 GPU 利用率极低,训练反而更慢。通常 32, 64, 128 是甜点。

---

本章总结 | Chapter Summary

中文:

  • Keras Sequential 是最简单的模型构建方式
  • compile 配置优化器、损失函数、监控指标
  • fit 返回 history,包含训练和验证的完整历史
  • 通过 loss/accuracy 曲线诊断:过拟合、欠拟合、学习率问题
  • Early Stopping 是防止过拟合的实用技巧
  • 数据预处理(缩放)在框架中不会自动完成,必须手动处理

English:

  • Keras Sequential is the simplest model building API
  • compile configures optimizer, loss, and metrics
  • fit returns history with complete training/validation records
  • Diagnose via curves: overfitting, underfitting, learning rate issues
  • Early Stopping prevents overfitting practically
  • Data preprocessing (scaling) is not automatic — must be done manually

---

课后练习 | Homework

  1. 框架对比:用相同数据,分别用 Keras 和 PyTorch 训练一个两层网络。对比代码量、训练速度、最终准确率。
  1. 学习率实验:在 Keras 中使用 Adam(learning_rate=...),分别尝试 0.1, 0.01, 0.001, 0.0001。画出四条验证损失曲线。
  1. 早停调参:用 EarlyStopping,分别尝试 patience=3, 5, 10。观察对最终测试集准确率的影响。
  1. Batch Size 对比:固定总训练步数,对比 batch_size=8, 32, 128, 512 的训练时间和最终准确率。
  1. 自定义 Metric:实现一个 F1-score metric 并传入 model.compile(metrics=[...])。对比它与 accuracy 在类别不平衡数据上的差异。