LSTM自定义损失函数报错:ValueError: too many values to unpack (expected 4)
问题:LSTM模型自定义损失函数报错:too many values to unpack (expected 4)
我尝试用以下代码实现带有自定义损失函数的LSTM模型:
tf.random.set_seed(7) model = Sequential() model.add(LSTM(100, input_shape=(18,1 ), return_sequences=True)) model.add(Dropout(0.2)) #model.add(LSTM(100)) #model.add(Dropout(0.2)) model.add(Dense(4, activation='tanh')) def LossQuat(y_true, y_pred): a, b = y_true.get_shape() error = [] for i in range(a): w0,x0,y0,z0 = y_true[i,:] w1,x1,y1,z1 = y_pred[i,:]/tf.norm(y_pred[i,:], ord='euclidean', axis=None, keepdims=None, name=None) w = w0 * w1 - x0 * x1 - y0 * y1 - z0 * z1 error.append(tf.square(2*tf.math.acos(w))) err = tf.reduce_sum(error) return tf.reduce_mean(err) model.compile(loss=LossQuat, optimizer = tf.keras.optimizers.Adam(learning_rate=1e-5),run_eagerly=True) model.summary()
但训练时出现如下错误:
Epoch 1/150 --------------------------------------------------------------------------- ValueError Traceback (most recent call last) <ipython-input-17-8e2657a2f86a> in <module>() ----> 1 history=model.fit(X,Y, verbose=1,epochs=150, batch_size=32) 1 frames /usr/local/lib/python3.7/dist-packages/keras/utils/traceback_utils.py in error_handler(*args, **kwargs) 65 except Exception as e: # pylint: disable=broad-except 66 filtered_tb = _process_traceback_frames(e.__traceback__) ---> 67 raise e.with_traceback(filtered_tb) from None 68 finally: 69 del filtered_tb /usr/local/lib/python3.7/dist-packages/tensorflow/python/autograph/impl/api.py in wrapper(*args, **kwargs) 690 except Exception as e: # pylint:disable=broad-except 691 if hasattr(e, 'ag_error_metadata'): ---> 692 raise e.ag_error_metadata.to_exception(e) 693 else: 694 raise ValueError: in user code: File "<ipython-input-16-820055073eb4>", line 15, in LossQuat * w1,x1,y1,z1 = y_pred[i,:]/tf.norm(y_pred, ord='euclidean', axis=None, keepdims=None, name=None) ValueError: too many values to unpack (expected 4)
该自定义损失函数训练FFN时可正常运行,我知道错误和LSTM层相关,但不清楚具体原因。
问题原因与解决方法
问题根源
你给LSTM层设置了return_sequences=True,这会让LSTM输出三维张量:(batch_size, timesteps, features),而FFN模型的输出是二维张量:(batch_size, features)。你的损失函数是按二维张量写的,遍历batch维度时,y_pred[i,:]实际是形状为(timesteps, 4)的张量,不是4个单独的值,所以尝试解包成w1,x1,y1,z1就会触发"too many values to unpack"错误。
两种解决方式
方式1:关闭序列输出(推荐,若不需要后续序列输入)
如果你的模型不需要LSTM返回完整序列,把return_sequences=True改成return_sequences=False,这样LSTM输出和FFN一致是二维张量,原损失函数可以直接使用:
model.add(LSTM(100, input_shape=(18,1 ), return_sequences=False))
方式2:修改损失函数适配三维张量(需保留序列输出时)
如果必须保留LSTM的序列输出(比如后续还有LSTM层需要序列输入),需要修改损失函数,用张量操作替代循环,适配三维输入:
def LossQuat(y_true, y_pred): # y_true: (batch_size, timesteps, 4),y_pred: (batch_size, timesteps, 4) # 对每个样本的每个时间步的四元数做归一化 y_pred_normalized = y_pred / tf.norm(y_pred, ord='euclidean', axis=-1, keepdims=True) # 计算四元数的点积 dot_product = tf.reduce_sum(y_true * y_pred_normalized, axis=-1) # 限制点积范围在[-1,1],避免acos出现数值错误 clip_dot = tf.clip_by_value(dot_product, -1.0, 1.0) # 计算每个时间步的损失 loss_per_step = tf.square(2 * tf.math.acos(clip_dot)) # 先对时间步求平均,再对batch求平均得到最终损失 return tf.reduce_mean(loss_per_step)
这里的关键改进:
- 用
axis=-1指定对四元数维度求范数,keepdims=True保证形状匹配,能直接做除法 - 用
tf.clip_by_value避免acos输入超出有效范围,防止NaN - 去掉Python循环,改用TensorFlow原生张量操作,效率更高且符合计算图模式要求
内容的提问来源于stack exchange,提问作者Arman Asgharpoor
相关产品推荐
相关产品推荐

