Keras中CosineSimilarity指标与手动计算余弦相似度的差异排查
先梳理下你遇到的核心矛盾:训练时验证集的余弦相似度指标、evaluate方法的指标、手动计算的平均余弦相似度三者数值不一致,而且差异还不小。下面我逐个拆解原因,并给出对应的排查和解决思路:
1. fit验证指标与evaluate指标的细微差异:批次累积逻辑的区别
你的val_loss和evaluate返回的loss几乎完全一致(-0.4152 vs -0.415217),这说明模型对验证集的预测输出是完全相同的,那指标的细微差异(0.4607 vs 0.4468)大概率来自TensorFlow内部的指标计算逻辑:
- fit阶段计算验证指标时,是按批次累积统计量,每处理一个批次就更新一次指标的平均值(加权平均,按批次样本数);
- evaluate方法计算指标时,会先重置指标状态,再重新遍历验证集计算。虽然逻辑上应该和fit的验证阶段一致,但如果验证集最后一个批次样本数不足,或者TensorFlow内部在累积统计时的精度处理有细微差别,就会导致结果出现小幅度偏差。
这种差异通常不影响模型评估,属于正常的数值波动。
2. loss对应的余弦相似度与指标的差异:平均方式的本质不同
这是最关键的差异来源!你用的loss='cosine_similarity'对应tf.keras.losses.CosineSimilarity,而指标是tf.keras.metrics.CosineSimilarity,两者的平均逻辑完全不一样:
- loss的计算逻辑:默认采用
sum_over_batch_size归约方式——先计算每个批次内样本的余弦相似度平均值,再对所有批次的这个平均值取算术平均(不管每个批次的样本数多少,每个批次的权重相同); - 指标的计算逻辑:计算所有验证样本的余弦相似度的算术平均值(总余弦相似度之和除以总样本数,按样本数加权)。
举个直观的例子:假设验证集有5个样本,批次大小3,第一个批次3个样本的余弦相似度是[0.4,0.4,0.4](批次平均0.4),第二个批次2个样本的余弦相似度是[0.6,0.6](批次平均0.6)。那么loss对应的平均是(0.4+0.6)/2=0.5(对应loss=-0.5),而指标的平均是(30.4 +20.6)/5=0.48,两者差异明显。你的val_loss是-0.4152,对应的是批次平均的余弦相似度0.4152,而val_cosine_similarity是0.4607,完全符合这种平均方式的差异。
3. 手动计算与指标的较大差异:代码或数据的潜在问题
你的手动计算结果比指标低了0.04以上,这说明大概率存在代码疏漏或数据问题,建议从以下几点排查:
- 零向量导致的异常值:如果某些样本的预测向量或目标向量是零向量,
np.linalg.norm会返回0,导致余弦相似度计算为NaN。如果statistics.mean忽略了这些NaN(或者你遍历的时候跳过了部分样本),会拉低平均结果。可以添加代码检查:
如果存在print(df['cos_pred_target'].isna().sum())NaN,找到对应的样本,要么过滤掉这些零向量样本,要么给范数加一个极小的epsilon避免除以0:def cos_sim(a, b): dot_product = np.dot(a, b) norm_a = np.linalg.norm(a) + 1e-10 norm_b = np.linalg.norm(b) + 1e-10 return dot_product / (norm_a * norm_b) - 数据类型的精度差异:TensorFlow默认用
float32计算,而你的手动计算用的是numpy的float64,虽然单样本差异很小,但300维向量的累积误差可能被放大。可以尝试将预测结果转换为float32后再计算:prediction_mul = model_mul.predict(padded_inputs_tr).astype(np.float32) - 目标向量的预处理不一致:确认测试集的
target_tr_r_array和训练时的target_multil_array是否经过了完全相同的预处理(比如是否做过归一化、缩放等)。虽然余弦相似度不受向量长度影响,但如果目标向量存在异常值(比如某些维度数值过大),可能会干扰计算结果。
快速验证方法
取一小部分验证样本(比如100个),分别用TensorFlow的指标和手动计算每个样本的余弦相似度,逐样本对比数值。这样能快速定位是整体平均方式的问题,还是个别样本的计算差异导致的。
内容的提问来源于stack exchange,提问作者a_gdevr




