Keras:过程序列自编码器模型正确性与自定义指标问题
嘿,我看到你在构建事件序列的自编码器时遇到了准确率计算的形状匹配问题,还有一些模型构建的疑问,我来帮你一步步解决:
一、批量将y_true转换为one-hot编码(解决自定义指标的形状问题)
你提到单个事件可以用K.gather(K.eye(462), tf.cast(number, tf.int32))转换,但批量处理其实不用这么麻烦,Keras后端提供了K.one_hot()函数直接支持批量操作。不过更高效的方式是先对y_pred取最大概率对应的事件编号,再和原y_true比较(不用完全转one-hot),修改你的symbol_acc函数如下:
def symbol_acc(y_true, y_pred): # 从y_pred的概率分布中取出预测的事件编号,形状变回(?,60) y_pred_argmax = K.argmax(y_pred, axis=-1) # 比较每个位置的预测值和真实值,计算平均准确率 isEqual = K.cast(K.equal(y_true, y_pred_argmax), K.floatx()) return K.mean(isEqual)
如果一定要转one-hot再比较,也可以直接用:
y_true_one_hot = K.one_hot(tf.cast(y_true, tf.int32), depth=EVENTS_CNT) # 此时y_true_one_hot形状为(?,60,462),和y_pred一致
二、更简便的解决方案:用Keras自带的稀疏分类准确率
其实你完全不用自己写自定义指标,Keras已经内置了sparse_categorical_accuracy,专门处理**整数型真实标签(你的y_true:(?,60))和概率分布型预测结果(你的y_pred:(?,60,462))**的准确率计算,直接用它就行:
model.compile( loss=tf.losses.sparse_softmax_cross_entropy, optimizer='adam', target_tensors=[tf.placeholder(tf.int32, [None, 60])], metrics=['sparse_categorical_accuracy'] )
这样既省去了自定义指标的麻烦,也能准确计算每个位置的事件预测准确率。
三、模型构建的几个优化点
作为Keras新手,你的模型整体思路是对的,但有几个细节可以调整:
Embedding层的input_dim设置:
你的事件编号是0-461,共462个不同事件,所以input_dim设置为EVENTS_CNT(即462)就足够了,不需要加1,除非你用了462作为padding的特殊标记。修改后的代码:embedded = Embedding(input_dim=EVENTS_CNT, input_length=BLOCK_LEN, output_dim=200)(input)Seq2Seq模型的输出层补充:
你之前加TimeDistributed(Dense(EVENTS_CNT, activation='softmax'))时出现形状错误,大概率是因为Seq2Seq的输出维度和Dense层不匹配。建议明确补充输出层,确保输出是每个位置的462维概率分布:seq_model = Seq2Seq( batch_input_shape=(None, BLOCK_LEN, 200), hidden_dim=200, output_length=BLOCK_LEN, output_dim=200 # 先输出200维隐向量 ) # 补充TimeDistributed层转成事件概率 model = Sequential() model.add(emb_model) model.add(seq_model) model.add(TimeDistributed(Dense(EVENTS_CNT, activation='softmax')))调整后,你可以不用
target_tensors的方式,直接用常规的model.fit(x_train, y_train)训练,更符合Keras的常规用法。Seq2Seq的替代方案(更灵活):
如果你用的是新版本Keras,官方推荐用LSTM手动构建编码器-解码器结构,比旧的Seq2Seq模块更可控。比如:# 编码器 encoder_inputs = Input(shape=(BLOCK_LEN, 200)) encoder_lstm = LSTM(200, return_state=True) encoder_outputs, state_h, state_c = encoder_lstm(encoder_inputs) encoder_states = [state_h, state_c] # 解码器(自编码器结构下,解码器输入和编码器输入相同) decoder_inputs = Input(shape=(BLOCK_LEN, 200)) decoder_lstm = LSTM(200, return_sequences=True, return_state=True) decoder_outputs, _, _ = decoder_lstm(decoder_inputs, initial_state=encoder_states) decoder_dense = TimeDistributed(Dense(EVENTS_CNT, activation='softmax')) decoder_outputs = decoder_dense(decoder_outputs) # 组合模型 seq_model = Model([encoder_inputs, decoder_inputs], decoder_outputs) model = Sequential() model.add(emb_model) # 训练时传入两次嵌入后的序列作为编码器和解码器输入 model.add(seq_model)
内容的提问来源于stack exchange,提问作者Anna

