CNN训练形状不兼容求助:(None,5754,4)与(None,360,4)
解决时序动作分类中输出与标签序列长度不匹配的问题
针对你遇到的模型输出序列长度(360)与标签序列长度(5754)不兼容的问题,提供两种可行方案:
方案一:修改模型结构,保持输入序列长度(推荐)
核心思路是避免池化层缩短序列,让模型输出的时间步与输入完全一致,适配逐时间步的标签。
1. 移除所有池化层,用卷积+Dropout控制复杂度
删除所有AveragePooling1D层,所有卷积层保持strides=1和padding='same',确保序列长度始终为5754:
model = Sequential() # 输入层:保持序列长度5754 model.add(layers.Conv1D(512, 3, strides=1, activation='relu', padding='same', input_shape=(5754, 96))) model.add(layers.Dropout(0.2)) model.add(layers.BatchNormalization(axis=-1)) model.add(layers.Conv1D(512, 3, strides=1, activation='relu', padding='same')) model.add(layers.Conv1D(256, 3, strides=1, activation='relu', padding='same')) model.add(layers.Dropout(0.2)) model.add(layers.BatchNormalization(axis=-1)) model.add(layers.Conv1D(128, 3, strides=1, activation='relu', padding='same')) model.add(layers.Dropout(0.2)) model.add(layers.Conv1D(64, 3, strides=1, activation='relu', padding='same')) # 映射到输出维度 model.add(layers.Dense(32, activation='relu')) model.add(layers.TimeDistributed(Dense(4, activation='softmax'))) model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy']) model.summary()
注:将tanh替换为relu可缓解深层网络的梯度消失问题,提升训练稳定性
2. 保留池化,添加上采样层还原序列长度
若需保留池化的特征压缩能力,可在模型末尾添加上采样层,将序列长度还原为5754:
model = Sequential() model.add(layers.Conv1D(512, 3, strides=1, activation='relu', padding='same', input_shape=(5754, 96))) model.add(layers.Dropout(0.2)) model.add(layers.AveragePooling1D(pool_size=2, strides=2, padding='same')) # 5754→2877 model.add(layers.BatchNormalization(axis=-1)) model.add(layers.Conv1D(512, 3, strides=1, activation='relu', padding='same')) model.add(layers.Conv1D(256, 3, strides=1, activation='relu', padding='same')) model.add(layers.Dropout(0.2)) model.add(layers.AveragePooling1D(pool_size=2, strides=2, padding='same')) # 2877→1439 model.add(layers.BatchNormalization(axis=-1)) model.add(layers.Conv1D(128, 3, strides=1, activation='relu', padding='same')) model.add(layers.Dropout(0.2)) model.add(layers.AveragePooling1D(pool_size=2, strides=2, padding='same')) #1439→720 model.add(layers.Conv1D(64, 3, strides=1, activation='relu', padding='same')) model.add(layers.AveragePooling1D(pool_size=2, strides=2, padding='same')) #720→360 # 上采样还原序列长度 model.add(layers.UpSampling1D(size=2)) #360→720 model.add(layers.UpSampling1D(size=2)) #720→1440 model.add(layers.Conv1DTranspose(64, 3, strides=2, padding='same')) #1440→2880 # 最后一步精确匹配5754长度 model.add(layers.Conv1DTranspose(32, 3, strides=2, padding='same', output_shape=(None, 5754, 32))) model.add(layers.TimeDistributed(Dense(4, activation='softmax'))) model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])
方案二:调整标签序列长度,匹配模型输出
若不想修改模型结构,可将标签序列下采样到360长度,与模型输出对齐:
1. 平均池化标签(适用于连续动作)
对原始标签的连续时间步取平均后生成下采样标签:
import numpy as np # 计算下采样倍数:5754 ≈ 360×16 downsample_factor = 16 # 裁剪标签到可被16整除的长度(5760) Y_train_cropped = Y_train[:, :5760, :] # 重塑为(样本数, 下采样后步数, 每段步数, 类别数) Y_reshaped = Y_train_cropped.reshape(637, 360, downsample_factor, 4) # 对每段时间步的标签取平均,再转成one-hot编码 Y_downsampled = np.argmax(Y_reshaped.mean(axis=2), axis=-1) Y_downsampled_onehot = np.eye(4)[Y_downsampled] # 使用裁剪后的输入和下采样标签训练 history = model.fit(X_train[:, :5760, :], Y_downsampled_onehot, validation_split=0.2, epochs=2000, batch_size=16, verbose=2)
2. 间隔采样标签(适用于离散动作)
直接每隔N个时间步取一个标签,生成360长度的标签序列:
# 计算采样间隔:5754//360 ≈16 sample_step = 16 Y_downsampled_onehot = Y_train[:, ::sample_step, :][:, :360, :] # 训练时使用原输入和采样后的标签 history = model.fit(X_train, Y_downsampled_onehot, validation_split=0.2, epochs=2000, batch_size=16, verbose=2)
额外优化建议
- 加入
EarlyStopping回调,监控val_loss,避免2000 epochs的过度训练:from tensorflow.keras.callbacks import EarlyStopping early_stop = EarlyStopping(monitor='val_loss', patience=20, restore_best_weights=True) history = model.fit(..., callbacks=[early_stop]) - 若标签是整数类型,可改用
sparse_categorical_crossentropy损失,减少内存占用。
内容的提问来源于stack exchange,提问作者Daniel
相关产品推荐
相关产品推荐

