# keras-templete **Repository Path**: fuxiaoy/keras-templete ## Basic Information - **Project Name**: keras-templete - **Description**: keras快速开发架构 - **Primary Language**: Python - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-10-09 - **Last Updated**: 2026-07-30 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # keras-templete 一个面向**六轴传感器时间序列动作分类**的 TensorFlow/Keras 开发模板。 项目当前覆盖以下完整流程: - 从文本文件读取六轴动作数据; - 将变长序列整理成固定的 `(128, 6)` 输入; - 构建并训练 1D-CNN 多分类模型; - 支持加载已有 Keras 模型继续微调; - 评估模型、输出预测示例并保存训练曲线; - 可选将模型量化并导出为 NNoM 使用的 C 头文件。 > 当前默认任务为 14 类动作识别。配置、网络、数据处理和训练流程已拆分到不同文件,修改入口见本文末尾的[维护速查](#维护速查想改什么应该改哪里)。 --- ## 目录 - [一、架构总览](#一架构总览) - [二、目录与文件职责](#二目录与文件职责) - [三、主程序调用链](#三主程序调用链) - [四、数据格式与预处理](#四数据格式与预处理) - [五、模型结构](#五模型结构) - [六、全新训练与预训练微调](#六全新训练与预训练微调) - [七、NNoM 量化导出](#七nnom-量化导出) - [八、安装与运行](#八安装与运行) - [九、配置项说明](#九配置项说明) - [十、运行产物](#十运行产物) - [十一、维护速查](#十一维护速查想改什么应该改哪里) - [十二、当前限制与注意事项](#十二当前限制与注意事项) --- ## 一、架构总览 项目分为五层: ```text ┌──────────────────────────────────────────────────────────────┐ │ 入口与编排层 │ │ src/auto.py │ │ 选择全新训练/微调,串联数据、模型、训练、评估和导出 │ └──────────────────────────────┬───────────────────────────────┘ │ ┌────────────────┼────────────────┐ │ │ │ ▼ ▼ ▼ ┌───────────────────┐ ┌───────────────────┐ ┌───────────────────┐ │ 配置层 │ │ 数据层 │ │ 模型与训练层 │ │ src/config.py │ │ src/data_utils.py │ │ src/model.py │ │ 输入/类别/路径 │ │ 加载/补齐/划分 │ │ 1D-CNN │ │ 训练/微调参数 │ │ 归一化/增强 │ │ │ │ │ │ │ │ src/framework.py │ │ │ │ │ │ 加载/冻结/训练 │ │ │ │ │ │ 评估/预测/绘图 │ └───────────────────┘ └───────────────────┘ └───────────────────┘ │ ▼ ┌─────────────────────────┐ │ 嵌入式导出层 │ │ src/nnom.py │ │ src/gen_config.py │ │ fully_connected_...py │ │ 量化并生成 weights.h │ └─────────────────────────┘ ``` 主流程可以概括为: ```text config.py │ ▼ auto.main() │ ├── 全新训练 ──> model.create_generic_model() │ └── 模型微调 ──> framework.CNNFramework(load_model + 冻结/重编译) │ ▼ data_utils.load_dataset_from_files() │ ▼ data_utils.preprocess_dataset() │ padding / one-hot / shuffle / 80:20 划分 ▼ CNNFramework.train_model() │ ├── 保存最佳模型 ├── 保存训练曲线 ├── 评估与预测示例 └── 可选调用 nnom.generate_model() │ ├── 量化激活与权重 ├── 生成 NNoM 层配置 └── 输出 weights.h + .layer_q_list ``` ### 默认任务参数 | 项目 | 默认值 | |---|---:| | 输入时间步 | `128` | | 输入通道数 | `6` | | 模型输入形状 | `(128, 6)` | | 动作类别数 | `14` | | 全新训练最大轮数 | `1000` | | 微调最大轮数 | `100` | | 批大小 | `32` | | 新模型输出 | `model.h5` | | 微调模型输出 | `finetuned_model.h5` | | 训练曲线 | `training_history.png` | | NNoM 输出 | `weights.h`、`.layer_q_list` | --- ## 二、目录与文件职责 ```text keras-templete/ ├── README.md # 中文架构与使用说明 ├── README.en.md # 英文 README(当前未与本文同步) ├── requirements.txt # Python 依赖版本 │ ├── src/ │ ├── auto.py # 主入口:编排训练、微调、评估、预测和导出 │ ├── config.py # 所有核心配置:输入、类别、路径、训练和微调 │ ├── model.py # 1D-CNN 模型定义与编译 │ ├── data_utils.py # 数据加载、补齐、划分、归一化、增强、Dataset │ ├── framework.py # 模型加载、兼容性检查、训练、评估、预测、绘图 │ ├── generate_mock_data.py # 生成可跑通流程的六轴模拟数据 │ │ │ ├── nnom.py # NNoM 量化校准、权重量化和 C 模型生成 │ ├── gen_config.py # 将 Keras 层/张量转换为 NNoM C 配置 │ └── fully_connected_opt_weight_generation.py │ # CMSIS-NN/NNoM 全连接权重重排 │ ├── train_data/ # 文本训练数据:每个文件是一个动作样本 │ ├── model.h5 # 全新训练生成的最佳 Keras 模型 ├── finetuned_model.h5 # 微调生成的最佳模型(启用微调后) ├── training_history.png # 训练/验证曲线 ├── weights.h # NNoM 模型、权重和量化参数头文件 └── .layer_q_list # 各层量化小数位与偏移记录 ``` > `model.h5`、`finetuned_model.h5`、`training_history.png`、`weights.h` 和 `.layer_q_list` 都是运行产物,不是需要手工维护的核心源码。 ### 每个源码文件做什么 | 文件 | 关键类/函数 | 核心职责 | 常见修改场景 | |---|---|---|---| | [`src/config.py`](src/config.py) | 配置常量 | 管理项目根路径、输入形状、动作标签、训练参数、微调参数和输出路径 | 改类别、数据路径、epoch、batch、冻结策略、输出文件名 | | [`src/model.py`](src/model.py) | `create_generic_model()` | 创建并编译 1D-CNN | 改卷积层、Dense 层、正则化、Dropout、优化器或损失 | | [`src/data_utils.py`](src/data_utils.py) | `load_dataset_from_files()`、`preprocess_dataset()`、`normalize_data()`、`augment_data()`、`create_dataset()` | 文本/NumPy 数据加载、序列补齐、标签编码、数据划分、归一化和增强 | 改文件格式、列选择、划分方式、归一化或增强 | | [`src/framework.py`](src/framework.py) | `CNNFramework` | 封装模型加载、兼容性检查、冻结、重编译、训练、评估、预测和绘图 | 改微调规则、训练接口、评估、预测或曲线样式 | | [`src/auto.py`](src/auto.py) | `main()`、`_create_framework()`、`_build_callbacks()`、`export_nnom_model()` | 串联完整工作流并区分全新训练/微调 | 改执行顺序、回调、校准样本数或是否导出 NNoM | | [`src/generate_mock_data.py`](src/generate_mock_data.py) | `_motion_signal()`、`main()` | 生成 14 类 × 16 个、每个 `128 × 6` 的模拟样本 | 改模拟样本数、信号形状、噪声或输出格式 | | [`src/nnom.py`](src/nnom.py) | `quantize_output()`、`quantize_weights()`、`generate_model()` | 校准各层范围、量化权重并生成 NNoM C 模型 | 改量化方式、支持层、权重布局或 C 输出 | | [`src/gen_config.py`](src/gen_config.py) | `gen_tensor()`、`gen_conv2d_config()`、`gen_dense_config()` 等 | 把 Keras 张量和层参数变成 NNoM C 配置结构 | 新增/调整 Keras 层到 NNoM 层的映射 | | [`src/fully_connected_opt_weight_generation.py`](src/fully_connected_opt_weight_generation.py) | `convert_to_x4_q7_weights()` 等 | 按 CMSIS-NN/NNoM 需要重排全连接和 RNN 权重 | 修改目标内核的数据排布 | --- ## 三、主程序调用链 入口是 [`src/auto.py`](src/auto.py) 中的 `main()`。文件带有: ```python if __name__ == '__main__': main() ``` 因此直接执行脚本会开始训练,但仅导入 `auto` 模块不会自动训练。 ### 实际执行顺序 1. **判断运行模式** - `PRETRAINED_MODEL_PATH is None`:全新训练; - `PRETRAINED_MODEL_PATH` 已设置:加载已有模型微调。 2. **创建 `CNNFramework`** - 全新训练调用 `create_generic_model(INPUT_SHAPE, NUM_CLASSES)`; - 微调加载 `.h5` 模型,并检查输入形状和类别数。 3. **读取文本数据** - `load_dataset_from_files(TRAIN_DATA_PATH, MOTION_TO_LABEL, ...)`; - 每个合法文本文件被加载为一个二维六轴序列。 4. **预处理数据** - `preprocess_dataset()` 统一时间长度; - 标签转为 one-hot; - 随机打乱后按 80%/20% 分为训练集和测试集。 5. **构建回调并训练** - `EarlyStopping`; - `ModelCheckpoint`; - `ReduceLROnPlateau`; - `CNNFramework.train_model()` 最终调用 Keras `model.fit()`。 6. **保存训练曲线** - 保存到项目根目录的 `training_history.png`; - 图形后端支持时同时尝试弹窗显示。 7. **构建测试 Dataset 并评估** - 最多取测试集前 100 个样本; - 调用 `model.evaluate()`。 8. **输出预测示例** - 取测试集前 5 个样本; - 打印预测动作、最高概率、真实动作和完整类别概率。 9. **尝试 NNoM 导出** - 调用 `nnom.generate_model()`; - 导出失败只打印警告,不回滚已完成的训练、曲线和评估结果。 ### `data_utils.py` 中还有一条备用数据管道 `auto.py` 当前走的是“多个动作文本文件”主链路。除此之外,[`src/data_utils.py`](src/data_utils.py) 还提供通用 `.npy` 管道: ```text preprocess_data() ├── load_data() # 加载 .npy 数据和标签 ├── normalize_data() # min-max 或 standard ├── augment_data() # 可选噪声与轴翻转 └── create_dataset() # shuffle / batch / prefetch ``` 这条备用管道**没有被当前 `auto.main()` 调用**。如果改为使用 `.npy`、归一化或在线增强,需要在入口中显式接入,并验证增强函数接收到的张量维度。 --- ## 四、数据格式与预处理 ### 4.1 文本数据目录 默认数据目录由 [`src/config.py`](src/config.py) 计算: ```text 项目根目录/train_data/ ``` 路径基于 `src/config.py` 所在位置推导出的 `PROJECT_ROOT`,不是基于终端当前工作目录。 ### 4.2 文件命名规则 每个文件代表一个动作样本,文件名格式为: ```text 动作名称_数字.txt ``` 示例: ```text train_data/ ├── flip_0.txt ├── flip_1.txt ├── RightAngle_0.txt ├── Letter_h_3.txt ├── Wave_8.txt └── NoMotion_15.txt ``` 合法条件: - 动作名称必须存在于 `MOTION_NAMES`; - 下划线后必须是数字; - 编号默认必须位于 `0` 到 `999`; - 后缀默认为 `.txt`; - 非法名称、未知动作、越界编号、空文件或读取失败的文件会被跳过并打印提示。 ### 4.3 单个样本内容 默认使用空格分隔的六列数值: ```text ax ay az gx gy gz ``` 可以理解为: | 列 | 含义 | |---:|---| | 0~2 | 三轴加速度 | | 3~5 | 三轴角速度 | 示例: ```text 0.012351 -0.101203 1.003512 2.103000 -0.881000 0.451000 0.014292 -0.096440 1.005201 2.218000 -0.793000 0.502000 ``` 实际读取列由 `DEF_USE_COLS` 控制,默认是 `(0, 1, 2, 3, 4, 5)`。 ### 4.4 当前 14 个动作类别 标签编号由 `MOTION_NAMES` 的列表顺序自动生成: | 标签 | 动作名 | 标签 | 动作名 | |---:|---|---:|---| | 0 | `flip` | 7 | `letter_W` | | 1 | `RightAngle` | 8 | `letter_phi` | | 2 | `SharpAngle` | 9 | `Circle` | | 3 | `Lightning` | 10 | `UpAndDown` | | 4 | `Triangle` | 11 | `Horn` | | 5 | `Letter_h` | 12 | `Wave` | | 6 | `letter_R` | 13 | `NoMotion` | > **类别顺序就是模型输出含义。** 不要在使用旧模型或微调时随意重排 `MOTION_NAMES`,否则同一个输出索引会被解释成不同动作。 ### 4.5 序列整理和数据划分 `preprocess_dataset()` 的主要处理如下: ```text 变长二维样本列表 │ ├── 检查/修复二维形状 ├── 统一通道数 ├── 固定为 128 个时间步 │ ├── 短序列:尾部补 0(post padding) │ └── 长序列:按 pad_sequences 默认规则从前部截断,保留末尾 128 行 ├── 整数标签转 14 维 one-hot ├── np.random.shuffle 随机打乱 └── 前 80% 为训练集,后 20% 为测试集 ``` 当前划分不是分层抽样,也没有固定随机种子,因此不同运行之间的训练/测试划分可能不同。 ### 4.6 生成模拟数据 [`src/generate_mock_data.py`](src/generate_mock_data.py) 用于快速生成能跑通流程的示例数据: - 14 个动作类别; - 每类 16 个文件; - 总计 224 个文件; - 每个文件形状为 `(128, 6)`; - 随机种子固定为 `42`。 模拟数据仅用于接口验证和流程演示,不代表真实传感器采集质量。 --- ## 五、模型结构 模型由 [`src/model.py`](src/model.py) 的 `create_generic_model(input_shape, num_classes, ...)` 创建。 ### 5.1 网络层级 ```text Input: (128, 6) │ ▼ Conv1D(filters=32, kernel=3, stride=3, padding=same) │ ▼ ReLU │ ▼ Conv1D(filters=15, kernel=3, stride=3, padding=same) │ ▼ ReLU │ ▼ MaxPooling1D(pool_size=3, stride=3) │ ▼ Flatten │ ▼ Dropout(0.5) │ ▼ Dense(128, activation=relu) │ ▼ Dropout(0.5) │ ▼ Dense(64, activation=relu) │ ▼ Dense(14) │ ▼ Softmax │ ▼ 14 类概率 ``` ### 5.2 编译和初始化 | 项目 | 当前实现 | |---|---| | 优化器 | `Adam` | | 损失函数 | `CategoricalCrossentropy` | | 指标 | `accuracy` | | 卷积层初始化 | `TruncatedNormal(mean=0, stddev=0.01, seed=42)` | | 中间 Dense 初始化 | `TruncatedNormal(mean=0, stddev=0.01, seed=42)` | | 正则化 | 两个卷积层和最终分类 Dense 使用 L2,默认 `0.01` | | 输出 | 14 维 Softmax 概率 | > `create_generic_model()` 虽然接收 `dropout_rate` 参数,但当前两处 Dropout 仍直接写成 `0.5`。如果希望该参数真正生效,需要同时修改 [`src/model.py`](src/model.py) 中两处 `layers.Dropout(0.5)`。 --- ## 六、全新训练与预训练微调 运行模式只由 `PRETRAINED_MODEL_PATH` 决定。 ### 6.1 全新训练 [`src/config.py`](src/config.py) 默认配置: ```python PRETRAINED_MODEL_PATH = None ``` 此时执行流程为: ```text create_generic_model() -> 使用全部训练数据训练 -> 监控 val_accuracy -> 最佳模型保存为 model.h5 ``` 全新训练回调: - `EarlyStopping`:监控 `val_loss`,10 轮无改善后停止,并恢复最佳权重; - `ModelCheckpoint`:监控 `val_accuracy`,保存最佳 `model.h5`; - `ReduceLROnPlateau`:监控 `val_loss`,10 轮无改善后学习率乘 `0.5`,最低 `1e-7`。 ### 6.2 预训练模型微调 将配置改为已有模型路径,例如: ```python PRETRAINED_MODEL_PATH = os.path.join(PROJECT_ROOT, 'model.h5') ``` 此时 `CNNFramework` 会: 1. 严格检查模型文件是否存在,路径错误直接报错,不会静默退回全新训练; 2. 检查模型必须是单输入、单输出; 3. 检查模型输入必须与 `INPUT_SHAPE` 一致; 4. 检查输出类别数必须与 `NUM_CLASSES` 一致; 5. 根据配置冻结指定层; 6. 可选裁剪权重; 7. 默认使用较低学习率重新编译模型; 8. 打印冻结层、可训练层、可训练参数量和学习率。 默认微调设置: ```python FINETUNE_LOAD_COMPILE = False FINETUNE_RECOMPILE = True FINETUNE_OPTIMIZER = 'adam' FINETUNE_LEARNING_RATE = 1e-4 FINETUNE_EPOCHS = 100 FINETUNE_BATCH_SIZE = 32 FINETUNE_FREEZE_THROUGH_LAYER = None FINETUNE_FREEZE_LAYER_NAMES = () FINETUNE_WEIGHT_CLIP_RANGE = None ``` 默认不冻结任何层,即用较低学习率微调全部含权重层。 #### 按网络位置冻结 例如冻结从输入到 `flatten`(含)之间的层: ```python FINETUNE_FREEZE_THROUGH_LAYER = 'flatten' ``` #### 按名称冻结 例如只额外冻结两个卷积层: ```python FINETUNE_FREEZE_LAYER_NAMES = ('conv1d_1', 'conv1d_2') ``` 两种冻结方式可以同时使用。层名不存在或所有含权重层都被冻结时,框架会直接报错。 #### 微调输出保护 微调模型默认保存为: ```text finetuned_model.h5 ``` `auto.py` 会拒绝让 `FINETUNE_CHECKPOINT_PATH` 与 `PRETRAINED_MODEL_PATH` 指向同一个文件,防止覆盖原始模型。 --- ## 七、NNoM 量化导出 训练、绘图、评估和预测完成后,[`src/auto.py`](src/auto.py) 会把测试集前最多 100 个样本作为校准数据,调用: ```python nnom.generate_model(model, calibration_data, format='hwc', name=DEF_MODEL_H_NAME) ``` ### 导出模块关系 ```text src/auto.py │ └── export_nnom_model() │ ▼ src/nnom.py ├── quantize_output() # 推理校准样本,确定各层 Q 格式 ├── quantize_weights() # 权重/偏置 int8 量化和移位计算 ├── src/gen_config.py # 生成张量和层的 NNoM C 配置 └── fully_connected_opt_weight_generation.py # 重排 Dense/RNN 权重 │ ├── weights.h └── .layer_q_list ``` ### `weights.h` 包含什么 生成的 C 头文件主要包含: - int8 权重和偏置宏; - 权重小数位、偏置左移、输出右移; - 每层输出 Q 格式与偏移; - NNoM 张量定义; - Conv1D、Pooling、Flatten、Dense、Softmax 等层配置; - `nnom_model_create()` 模型构建函数。 ### 导出失败边界 `export_nnom_model()` 捕获 `ImportError` 和其他导出异常。因此: - NNoM 脚本缺失; - TensorFlow/Keras API 与旧版导出器不兼容; - 某个网络层不被导出器支持; - 权重量化或 C 配置生成失败; 这些情况会打印警告并跳过 `weights.h`,但不会使已经完成的训练、评估和曲线失效。 --- ## 八、安装与运行 以下命令均在**项目根目录**执行。 ### 8.1 安装依赖 建议使用独立虚拟环境,然后安装固定版本: ```bash python -m venv .venv ``` Windows PowerShell: ```powershell .\.venv\Scripts\Activate.ps1 python -m pip install --upgrade pip python -m pip install -r requirements.txt ``` Linux/macOS: ```bash source .venv/bin/activate python -m pip install --upgrade pip python -m pip install -r requirements.txt ``` 主要依赖: | 依赖 | 用途 | |---|---| | TensorFlow/Keras | 模型定义、训练、评估、预测和量化校准 | | NumPy | 数据和权重数组处理 | | Matplotlib | 保存和显示训练曲线 | | scikit-learn | MinMax/Standard 归一化及 NNoM 评估工具 | | SciPy | NNoM KLD 量化计算 | | h5py | Keras `.h5` 模型读写支持 | ### 8.2 准备数据 #### 方式 A:生成模拟数据 ```bash python src/generate_mock_data.py ``` 生成结果: ```text train_data/<动作名>_<编号>.txt ``` #### 方式 B:放入真实数据 将真实六轴样本放入 `train_data/`,确保: - 一个文件只包含一个动作样本; - 文件名满足 `动作名_数字.txt`; - 动作名与 `MOTION_NAMES` 完全一致,包括大小写; - 默认至少有六个以空格分隔的数值列。 ### 8.3 完整运行 ```bash python src/auto.py ``` 默认全新训练最多执行 1000 个 epoch,虽然 EarlyStopping 可能提前结束,但第一次验证环境时不建议直接等待完整训练。 ### 8.4 一轮快速检查 不修改配置即可只训练一轮并禁止弹出曲线窗口: ```bash python -c "import sys; sys.path.insert(0, 'src'); import auto; auto.main(epochs=1, show_plot=False)" ``` 这个命令仍会: - 加载和划分数据; - 构建或加载模型; - 训练一轮; - 保存模型检查点和训练曲线; - 评估、预测; - 尝试生成 NNoM 文件。 ### 8.5 启用微调 1. 确保已有模型(例如项目根目录的 `model.h5`); 2. 在 [`src/config.py`](src/config.py) 中设置 `PRETRAINED_MODEL_PATH`; 3. 按需设置学习率、冻结层和微调轮数; 4. 再执行: ```bash python src/auto.py ``` --- ## 九、配置项说明 所有常用配置集中在 [`src/config.py`](src/config.py)。 ### 9.1 输入与类别 | 配置 | 默认值 | 作用 | |---|---|---| | `TIME_STEPS` | `128` | 每个样本统一后的时间长度 | | `CHANNELS` | `6` | 输入通道数 | | `INPUT_SHAPE` | `(128, 6)` | Keras 模型输入形状 | | `MOTION_NAMES` | 14 个动作名 | 决定标签顺序和输出含义 | | `NUM_CLASSES` | `len(MOTION_NAMES)` | 模型输出类别数 | | `MOTION_TO_LABEL` | 自动生成 | 动作名到整数标签的映射 | ### 9.2 数据读取 | 配置 | 默认值 | 作用 | |---|---|---| | `TRAIN_DATA_PATH` | `<项目根>/train_data` | 文本数据目录 | | `TRAIN_LABELS_PATH` | `None` | `.npy` 备用管道的独立标签路径 | | `DEF_FILE_FORMAT` | `.txt` | 文本后缀 | | `DEF_FILE_MAX` | `999` | 文件名允许的最大编号 | | `DEF_USE_COLS` | `(0,1,2,3,4,5)` | 从文本读取的列 | | `DEF_N_ROWS` | `None` | 每个文件最多读取行数;`None` 为全部 | ### 9.3 全新训练与输出 | 配置 | 默认值 | 作用 | |---|---|---| | `EPOCHS` | `1000` | 全新训练最大轮数 | | `BATCH_SIZE` | `32` | 全新训练批大小 | | `DEF_MODEL_NAME` | `<项目根>/model.h5` | 全新训练最佳模型 | | `DEF_MODEL_H_NAME` | `<项目根>/weights.h` | NNoM 输出文件 | | `TRAINING_HISTORY_PLOT_PATH` | `<项目根>/training_history.png` | 曲线保存路径 | | `SHOW_TRAINING_PLOT` | `True` | 是否尝试显示曲线窗口 | ### 9.4 微调 | 配置 | 默认值 | 作用 | |---|---|---| | `PRETRAINED_MODEL_PATH` | `None` | `None` 全新训练;设置路径则微调 | | `FINETUNE_LOAD_COMPILE` | `False` | 加载模型时是否恢复旧编译状态 | | `FINETUNE_RECOMPILE` | `True` | 是否按当前配置重新编译 | | `FINETUNE_OPTIMIZER` | `adam` | 微调优化器 | | `FINETUNE_LEARNING_RATE` | `1e-4` | 微调学习率 | | `FINETUNE_LOSS` | `categorical_crossentropy` | 微调损失函数 | | `FINETUNE_METRICS` | `('accuracy',)` | 微调评估指标 | | `FINETUNE_EPOCHS` | `100` | 微调最大轮数 | | `FINETUNE_BATCH_SIZE` | `32` | 微调批大小 | | `FINETUNE_FREEZE_THROUGH_LAYER` | `None` | 冻结到指定层(含) | | `FINETUNE_FREEZE_LAYER_NAMES` | `()` | 按名称额外冻结层 | | `FINETUNE_WEIGHT_CLIP_RANGE` | `None` | 可选权重裁剪范围 | | `FINETUNE_CHECKPOINT_PATH` | `<项目根>/finetuned_model.h5` | 微调最佳模型输出 | | `FINETUNE_CHECKPOINT_MONITOR` | `val_loss` | 微调检查点监控项 | | `FINETUNE_CHECKPOINT_MODE` | `min` | 监控项优化方向 | --- ## 十、运行产物 | 产物 | 由谁生成 | 用途 | |---|---|---| | `model.h5` | 全新训练的 `ModelCheckpoint` | 保存 `val_accuracy` 最佳模型 | | `finetuned_model.h5` | 微调的 `ModelCheckpoint` | 保存微调后的最佳模型,避免覆盖源模型 | | `training_history.png` | `CNNFramework.plot_training_history()` | 查看 accuracy/loss 与验证曲线 | | `weights.h` | `nnom.generate_model()` | 在 NNoM/C 工程中编译模型和量化权重 | | `.layer_q_list` | `nnom.generate_model()` | 保存各层量化小数位和偏移 | | `train_data/*.txt` | 数据采集或 `generate_mock_data.py` | 模型训练输入 | --- ## 十一、维护速查:想改什么,应该改哪里 | 目标 | 优先修改 | 同时检查 | |---|---|---| | 新增、删除或重排动作类别 | [`src/config.py`](src/config.py) 的 `MOTION_NAMES` | 训练文件名、`NUM_CLASSES`、旧模型输出顺序;类别变化后通常不能直接微调旧模型 | | 改输入时间长度 | `TIME_STEPS` / `INPUT_SHAPE` | 数据截断/补齐、旧模型输入形状、NNoM 内存需求 | | 改输入通道数或文本列 | `CHANNELS`、`INPUT_SHAPE`、`DEF_USE_COLS` | 实际文件列数、模拟数据生成器、旧模型输入形状 | | 改网络层或神经元数量 | [`src/model.py`](src/model.py) | NNoM 是否支持新增层、冻结层名称是否失效 | | 让 `dropout_rate` 参数生效 | [`src/model.py`](src/model.py) 两处 `Dropout(0.5)` | 调用 `create_generic_model()` 的参数 | | 修改文本文件命名规则 | [`src/data_utils.py`](src/data_utils.py) 的正则匹配 | 数据采集/生成脚本和 `MOTION_TO_LABEL` | | 使用分层划分或固定随机种子 | `preprocess_dataset()` | 训练可复现性与类别平衡 | | 在主流程启用归一化/增强 | [`src/auto.py`](src/auto.py)、[`src/data_utils.py`](src/data_utils.py) | 训练和推理必须使用一致预处理;验证增强函数维度 | | 调整 EarlyStopping/学习率 | [`src/auto.py`](src/auto.py) 的 `_build_callbacks()` | 全新训练和微调是两套回调参数 | | 加载旧模型继续训练 | [`src/config.py`](src/config.py) 的 `PRETRAINED_MODEL_PATH` | 输入形状、类别数和类别顺序必须一致 | | 冻结特征提取层 | `FINETUNE_FREEZE_THROUGH_LAYER` / `FINETUNE_FREEZE_LAYER_NAMES` | 使用 `model.summary()` 中的真实层名 | | 修改训练封装、数据校验 | [`src/framework.py`](src/framework.py) | `auto.py` 的调用参数 | | 改量化方式或校准逻辑 | [`src/nnom.py`](src/nnom.py) | `quantize_output()`、`quantize_weights()`、目标 NNoM 版本 | | 支持新的 Keras 层导出 | [`src/nnom.py`](src/nnom.py)、[`src/gen_config.py`](src/gen_config.py) | C 端是否有对应 NNoM 层实现 | | 改 Dense/RNN 权重布局 | [`src/fully_connected_opt_weight_generation.py`](src/fully_connected_opt_weight_generation.py) | CMSIS-NN/NNoM 内核期望格式 | | 改模拟数据规模或波形 | [`src/generate_mock_data.py`](src/generate_mock_data.py) | 与 `MOTION_NAMES`、`TIME_STEPS`、`CHANNELS` 同步 | ### 修改时建议遵循的依赖方向 ```text 先改配置/数据约定 │ ▼ 再改数据处理和模型 │ ▼ 再改训练入口与微调策略 │ ▼ 最后检查 NNoM 导出兼容性 ``` 不要让底层模块反向依赖 `auto.py`。`auto.py` 应只负责组合现有能力,不应堆积数据处理或模型层实现。 --- ## 十二、当前限制与注意事项 1. **随机划分不可复现**:`preprocess_dataset()` 使用全局 NumPy 随机状态,当前没有固定 seed。 2. **不是分层划分**:简单随机 80/20 切分可能导致小数据集中的类别分布不均。 3. **主链路未做归一化**:文本数据在当前 `auto.py` 中直接 padding 后训练;`normalize_data()` 属于备用工具,并未接入主流程。 4. **在线增强需要验证维度**:通用增强接口存在,但没有在当前文本主流程中使用。 5. **模拟数据不代表真实效果**:它适合 smoke test,不适合据此判断真实动作识别精度。 6. **默认训练轮数较高**:全新训练默认 1000 轮,建议先用一轮命令验证环境。 7. **类别顺序是模型协议**:微调、推理和嵌入式部署都必须使用相同的 `MOTION_NAMES` 顺序。 8. **微调是严格模式**:预训练路径无效、形状不匹配、类别数不一致或冻结配置错误都会直接报错。 9. **NNoM 导出是可选支线**:训练成功不等于量化导出一定成功;导出器使用较旧的 Keras 内部节点 API,与不同 TensorFlow/Keras 版本的兼容性需要实测。 10. **生成物应与源码分开管理**:模型、曲线、训练数据和 C 头文件可能较大或经常变化,提交前应按项目实际发布策略决定是否纳入版本控制。 11. **当前没有自动化测试目录**:修改数据形状、类别、网络或 NNoM 导出后,至少运行一轮快速检查并核对输出形状。 --- ## 最小验证清单 每次修改后建议依次确认: - [ ] `MOTION_NAMES`、数据文件名和模型输出类别一致; - [ ] 模型输入仍为预期的 `(None, TIME_STEPS, CHANNELS)`; - [ ] 预处理结果为 `(样本数, TIME_STEPS, CHANNELS)`; - [ ] 标签为 `(样本数, NUM_CLASSES)` 的 one-hot; - [ ] 一轮训练可以完成; - [ ] `model.h5` 或 `finetuned_model.h5` 正常生成; - [ ] `training_history.png` 正常生成; - [ ] 预测类别能按 `MOTION_NAMES` 正确还原; - [ ] 如果目标包含嵌入式部署,`weights.h` 能生成并在目标 NNoM 工程中编译。