使用sigmoid激活与BinaryAccuracy始终获0准确率,模型形状不匹配
问题分析与解决方案
核心问题有两个:模型输出形状与标签不匹配导致准确率计算失效;loss为负说明训练逻辑存在异常。以下是具体修正方案:
1. 修正模型输入形状的定义错误
你的模型第一层是GRU,但input_shape参数却写在后面的Conv1D层中,这会导致Keras无法正确推断输入数据的形状,进而引发后续层的形状混乱。
修正:
将input_shape移到GRU层(模型的第一层),并移除Conv1D层的input_shape参数:
model.add( tf.keras.layers.GRU( gru_units, return_sequences=True, input_shape=(X_train_scaled.shape[1], X_train_scaled.shape[2]) # 移到这里 ) ) model.add( tf.keras.layers.Conv1D( filters=conv_filters, kernel_size=conv_kernel_size, activation="relu" # 移除input_shape ) )
2. 修正模型输出形状与标签不匹配的问题
你当前的模型结构中,GRU返回序列(return_sequences=True),后续Conv1D、MaxPooling1D都保留了序列维度,即使添加Flatten,也可能因为中间层的3D输出导致最终输出形状仍为3D([72,14,1]),而标签是2D的[72,1],这会导致BinaryAccuracy无法正确计算,直接输出0。
正确做法:
在序列层之后使用全局池化层(如GlobalMaxPool1D或GlobalAveragePool1D)压缩序列维度,将3D输出转为2D的样本级特征向量,确保最终输出与标签形状匹配:
model.add(tf.keras.layers.MaxPooling1D(pool_size=2)) if use_batch_norm: model.add(tf.keras.layers.BatchNormalization()) # 新增全局池化层,替换后续的Flatten model.add(tf.keras.layers.GlobalMaxPool1D()) model.add(tf.keras.layers.Dense(dense_units)) model.add(tf.keras.layers.Dropout(dense_dropout)) # 移除原来的Flatten层 model.add( tf.keras.layers.Dense( units=1, activation="sigmoid", ) )
修改后,模型最终输出形状为[batch_size, 1],与标签形状完全匹配。
3. 解决loss为负的异常问题
BinaryCrossentropy的loss值不可能为负,出现负数说明训练过程中存在数值异常:
- 确保标签是float类型:将y_train和y_val转换为float32:
y_train = y_train.astype('float32') y_val = y_val.astype('float32') - 调整学习率范围:你的lr上限是1e-2,部分超参数组合可能导致模型发散,可尝试将上限降至5e-3:
lr = hp.Float("lr", min_value=1e-4, max_value=5e-3, sampling="LOG") - 确认loss参数:因为最后一层用了sigmoid激活,
BinaryCrossentropy默认from_logits=False是正确的,不要修改这个参数。
4. 验证模型形状
修改完成后,可以在模型编译前添加一行代码,打印模型的输出形状,确认与标签匹配:
print("模型输出形状:", model.output_shape) # 应该输出 (None, 1)
修正后的完整模型代码
def search_model_gru_cnn(hp): # Hyperparameters lr = hp.Float("lr", min_value=1e-4, max_value=5e-3, sampling="LOG") dense_dropout = hp.Float("dense_dropout", min_value=0.0, max_value=0.5, step=0.05) use_batch_norm = hp.Boolean("use_batch_norm", default=False) conv_filters = hp.Int("conv_filters", min_value=32, max_value=128, step=16) conv_kernel_size = hp.Int("conv_kernel_size", min_value=3, max_value=7, step=2) gru_units = hp.Int("gru_units", min_value=32, max_value=256, step=8) dense_units = hp.Int("dense_units", min_value=16, max_value=128, step=4) model = tf.keras.models.Sequential() model.add( tf.keras.layers.GRU( gru_units, return_sequences=True, input_shape=(X_train_scaled.shape[1], X_train_scaled.shape[2]) ) ) model.add( tf.keras.layers.Conv1D( filters=conv_filters, kernel_size=conv_kernel_size, activation="relu", ) ) model.add(tf.keras.layers.MaxPooling1D(pool_size=2)) if use_batch_norm: model.add(tf.keras.layers.BatchNormalization()) model.add(tf.keras.layers.GlobalMaxPool1D()) model.add(tf.keras.layers.Dense(dense_units)) model.add(tf.keras.layers.Dropout(dense_dropout)) model.add( tf.keras.layers.Dense( units=1, activation="sigmoid", ) ) # Compile the model optimizer = tf.keras.optimizers.Adam(learning_rate=lr) model.compile( optimizer=optimizer, loss=tf.keras.losses.BinaryCrossentropy(), metrics=[ tf.keras.metrics.BinaryAccuracy( name="binary_accuracy", dtype=None, threshold=0.5 ) ], ) # 验证输出形状 print("模型输出形状:", model.output_shape) return model
内容的提问来源于stack exchange,提问作者Furkan Öztürk
相关产品推荐
相关产品推荐

