BERT序列分类损失计算:训练值与手动计算值不符的疑问
BERT序列分类任务损失计算不符问题排查与正确流程
问题描述
在BERT序列分类任务中,训练时模型报告的损失为0.5368,但手动计算得到的损失为0.6769,两者数值不符。训练代码与手动计算代码如下:
训练代码
tokenized_data1= {'input_ids': array([[ 101, 19278, 15091, ..., 0, 0, 0], [ 101, 19278, 15091, ..., 0, 0, 0], [ 101, 8084, 11318, ..., 0, 0, 0], ..., [ 101, 7708, 14381, ..., 0, 0, 0], [ 101, 5379, 787, ..., 0, 0, 0], [ 101, 22327, 1987, ..., 0, 0, 0]]), 'token_type_ids': array([[0, 0, 0, ..., 0, 0, 0], [0, 0, 0, ..., 0, 0, 0], [0, 0, 0, ..., 0, 0, 0], ..., [0, 0, 0, ..., 0, 0, 0], [0, 0, 0, ..., 0, 0, 0], [0, 0, 0, ..., 0, 0, 0]]), 'attention_mask': array([[1, 1, 1, ..., 0, 0, 0], [1, 1, 1, ..., 0, 0, 0], [1, 1, 1, ..., 0, 0, 0], ..., [1, 1, 1, ..., 0, 0, 0], [1, 1, 1, ..., 0, 0, 0], [1, 1, 1, ..., 0, 0, 0]])} labels1=[0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0] from transformers import TFAutoModelForSequenceClassification model = TFAutoModelForSequenceClassification.from_pretrained("bert-base-cased") model.compile(optimizer=Adam(3e-5)) model.fit(tokenized_data1, labels1) # 训练输出:1/1 [==============================] - 40s 40s/step - loss: 0.5368
手动计算代码
# Obtaining logits outputs = model(input_ids=tf.constant(tokenized_data1['input_ids']), attention_mask=tf.constant(tokenized_data1['attention_mask'])) logits = torch.Tensor(outputs.logits.numpy()) labels1=torch.tensor(labels1) # Calculating loss using cross-entropy loss function loss_fct = CrossEntropyLoss() loss = loss_fct(logits.view(-1, 2), torch.tensor(labels1).view(-1)) # 得到损失:0.6769
错误排查
手动计算存在三个核心错误:
- 模型状态不一致:训练时的损失基于初始未更新的模型参数计算,但手动计算调用的是
model.fit之后的模型,此时参数已经经过一轮训练更新,输出的logits与训练时完全不同,损失自然不匹配。 - 跨框架混用导致损失计算差异:使用TensorFlow版本的BERT模型(
TFAutoModelForSequenceClassification),却用PyTorch的CrossEntropyLoss计算损失。两者的损失函数实现细节有差异,比如TensorFlow默认的SparseCategoricalCrossentropy针对稀疏整数标签优化,而PyTorch的CrossEntropyLoss在数值计算、归约方式上可能存在细微差别。 - 未对齐内置损失的配置:
TFAutoModelForSequenceClassification默认使用SparseCategoricalCrossentropy(from_logits=True)计算损失(因为输入标签是整数形式),手动计算时没有匹配这一配置,导致计算逻辑不一致。
正确的损失计算流程
1. BERT序列分类内置损失逻辑
当使用TFAutoModelForSequenceClassification训练时,若未指定损失函数,模型会自动采用:
tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True, reduction=tf.keras.losses.Reduction.AUTO)
计算流程为:
- 对每个样本的logits计算softmax,得到类别概率分布
- 取对应真实标签的负对数概率
- 对batch内所有样本的损失值取均值,得到最终的batch损失
2. 正确的手动计算步骤
要得到与训练时一致的损失,需做到:
- 在
model.fit之前获取初始模型的logits - 使用TensorFlow的损失函数,保持框架一致
- 匹配内置损失的配置(
from_logits=True、归约方式为均值)
修正后的手动计算代码:
import tensorflow as tf from transformers import TFAutoModelForSequenceClassification # 初始化模型(未训练的初始状态) model = TFAutoModelForSequenceClassification.from_pretrained("bert-base-cased") # 转换数据为TensorFlow张量 input_ids = tf.constant(tokenized_data1['input_ids']) attention_mask = tf.constant(tokenized_data1['attention_mask']) labels = tf.constant(labels1) # 获取初始模型的logits outputs = model(input_ids=input_ids, attention_mask=attention_mask) logits = outputs.logits # 使用与内置一致的损失函数计算 loss_fct = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True, reduction='mean') loss = loss_fct(labels, logits) print(loss.numpy()) # 此时结果应与训练时报告的0.5368一致
内容的提问来源于stack exchange,提问作者krishna kaushik
相关产品推荐
相关产品推荐

