BERT DataLoader:shuffle=True 与 Sampler 之间的区别?
BERT DataLoader: Difference between shuffle=True vs Sampler?
我在 ConLL 数据上使用 DistilBertForTokenClassification 训练了一个 DistilBERT 模型来预测 NER。培训似乎已经顺利完成,但我在评估阶段遇到了 2 个问题。
我得到负损失值
训练时,我对DataLoader使用了shuffle=True。但是在评估期间,当我为 DataLoader 执行 shuffle=True 时,我得到的指标结果非常差(f_1、准确性、召回率等)。但是,如果我执行 shuffle = False 或使用 Sampler 而不是洗牌,我会得到非常好的度量结果。我想知道我的代码是否有问题。
这里是评价码:
print('Prediction started on test data')
model.eval()
eval_loss = 0
predictions , true_labels = [], []
for batch in val_loader:
b_input_ids = batch['input_ids'].to(device)
b_input_mask = batch['attention_mask'].to(device)
b_labels = batch['labels'].to(device)
with torch.no_grad():
outputs = model(b_input_ids,
attention_mask=b_input_mask)
logits = outputs[0]
logits = logits.detach().cpu().numpy()
label_ids = b_labels.detach().cpu().numpy()
predictions.append(logits)
true_labels.append(label_ids)
eval_loss += outputs[0].mean().item()
print('Prediction completed')
eval_loss = eval_loss / len(val_loader)
print("Validation loss: {}".format(eval_loss))
输出:
Prediction started on test data
Prediction completed
Validation loss: -0.2584906197858579
我相信我在这里计算的损失是错误的。是否可以使用 BERT 获得负损失值?
对于 DataLoader,如果我使用下面的代码片段,我的指标结果没有问题。
val_sampler = SequentialSampler(val_dataset)
val_loader = DataLoader(val_dataset, sampler=val_sampler, batch_size=128)
但如果我这样做,我会得到很差的指标结果
val_loader = DataLoader(val_dataset, batch_size=128, shuffle=True)
使用 shuffle=True 与 shuffle=False 得到的结果大不相同是否正常?
指标计算代码:
metric = load_metric("seqeval")
results = metric.compute(predictions=true_predictions, references=true_labels)
results
输出:
{'LOCATION': {'f1': 0.9588207767898924,
'number': 2134,
'precision': 0.9574766355140187,
'recall': 0.9601686972820993},
'MISC': {'f1': 0.8658965344048217,
'number': 995,
'precision': 0.8654618473895582,
'recall': 0.8663316582914573},
'ORGANIZATION': {'f1': 0.9066332916145182,
'number': 1971,
'precision': 0.8947628458498024,
'recall': 0.9188229325215627},
'PERSON': {'f1': 0.9632426988922457,
'number': 2015,
'precision': 0.9775166070516096,
'recall': 0.9493796526054591},
'overall_accuracy': 0.988255561629313,
'overall_f1': 0.9324058459808882,
'overall_precision': 0.9322748349023465,
'overall_recall': 0.932536893886156}
当我使用 Sampler 或 shuffle=False 时会打印上述指标。如果我使用 shuffle=True,我得到:
{'LOCATION': {'f1': 0.03902284263959391,
'number': 2134,
'precision': 0.029496402877697843,
'recall': 0.057638238050609185},
'MISC': {'f1': 0.010318142734307824,
'number': 995,
'precision': 0.009015777610818933,
'recall': 0.012060301507537688},
'ORGANIZATION': {'f1': 0.027420984269014285,
'number': 1971,
'precision': 0.019160951996772892,
'recall': 0.04819888381532217},
'PERSON': {'f1': 0.02119907254057635,
'number': 2015,
'precision': 0.01590852597564007,
'recall': 0.03176178660049628},
'overall_accuracy': 0.5651741788003777,
'overall_f1': 0.02722600361161272,
'overall_precision': 0.020301063389034663,
'overall_recall': 0.041321152494729445}
更新:我修改了损失代码以进行评估。这段代码似乎没有问题。您可以在下面看到新代码:
print('Prediction started on test data')
model.eval()
eval_loss = 0
predictions , true_labels = [], []
for batch in val_loader:
b_labels = batch['labels'].to(device)
batch = {k:v.type(torch.long).to(device) for k,v in batch.items()}
with torch.no_grad():
outputs = model(**batch)
loss, logits = outputs[0:2]
logits = logits.detach().cpu().numpy()
label_ids = b_labels.detach().cpu().numpy()
predictions.append(logits)
true_labels.append(label_ids)
eval_loss += loss
print('Prediction completed')
eval_loss = eval_loss / len(val_loader)
print("Validation loss: {}".format(eval_loss))
虽然我仍然没有得到 DataLoader 问题的答案。
我还意识到当我这样做时 print(model.eval())
我仍然在评估模式下从模型中退出。
您没有正确计算损失。首先,我想知道为什么您使用对数的均值作为损失,但这可能是我不熟悉的特定任务。
但是您肯定没有正确地累积损失,或者根本不准确。您打印出的损失只是来自最后一批。这解释了为什么使用 shuffle 时结果不同。如果正确实施,绝对不会出现这种情况。
你得到负损失的事实是因为你只是使用对数的均值作为损失,这当然可以是负的。
准确性等其他指标不应受此影响,但您没有提供计算这些指标的代码,因此无法发现故障点。
据我了解,答案很简单:
“我看到我爸爸是这样做的,他爸爸也是这样做的,所以我也是这样做的”。
我查看了很多笔记本,看看人们是如何加载数据进行验证的,在每个笔记本中,我都看到人们在使用顺序采样器进行验证。在验证期间没有人使用洗牌或随机抽样。我不完全知道为什么,但就是这样。因此,如果访问此 post 的任何人都想知道同样的事情,答案基本上就是我上面引用的内容。
另外,我编辑了原来的 post 以解决我遇到的丢失问题。我计算错了。如果您还为模型提供原始标签,显然 Bert 会在输出 (outputs[0]) 的索引 0 处返回损失。在第一个代码片段中,当我从模型中获取输出时,我没有为模型提供原始标签,因此它没有返回索引 0 处的损失值,而是仅返回对数。
基本上你需要做的是:
outputs = model(input_ids, mask, label=label)
loss = outputs[0]
logits = outputs[1]
我在 ConLL 数据上使用 DistilBertForTokenClassification 训练了一个 DistilBERT 模型来预测 NER。培训似乎已经顺利完成,但我在评估阶段遇到了 2 个问题。
我得到负损失值
训练时,我对DataLoader使用了shuffle=True。但是在评估期间,当我为 DataLoader 执行 shuffle=True 时,我得到的指标结果非常差(f_1、准确性、召回率等)。但是,如果我执行 shuffle = False 或使用 Sampler 而不是洗牌,我会得到非常好的度量结果。我想知道我的代码是否有问题。
这里是评价码:
print('Prediction started on test data')
model.eval()
eval_loss = 0
predictions , true_labels = [], []
for batch in val_loader:
b_input_ids = batch['input_ids'].to(device)
b_input_mask = batch['attention_mask'].to(device)
b_labels = batch['labels'].to(device)
with torch.no_grad():
outputs = model(b_input_ids,
attention_mask=b_input_mask)
logits = outputs[0]
logits = logits.detach().cpu().numpy()
label_ids = b_labels.detach().cpu().numpy()
predictions.append(logits)
true_labels.append(label_ids)
eval_loss += outputs[0].mean().item()
print('Prediction completed')
eval_loss = eval_loss / len(val_loader)
print("Validation loss: {}".format(eval_loss))
输出:
Prediction started on test data
Prediction completed
Validation loss: -0.2584906197858579
我相信我在这里计算的损失是错误的。是否可以使用 BERT 获得负损失值?
对于 DataLoader,如果我使用下面的代码片段,我的指标结果没有问题。
val_sampler = SequentialSampler(val_dataset)
val_loader = DataLoader(val_dataset, sampler=val_sampler, batch_size=128)
但如果我这样做,我会得到很差的指标结果
val_loader = DataLoader(val_dataset, batch_size=128, shuffle=True)
使用 shuffle=True 与 shuffle=False 得到的结果大不相同是否正常?
指标计算代码:
metric = load_metric("seqeval")
results = metric.compute(predictions=true_predictions, references=true_labels)
results
输出:
{'LOCATION': {'f1': 0.9588207767898924,
'number': 2134,
'precision': 0.9574766355140187,
'recall': 0.9601686972820993},
'MISC': {'f1': 0.8658965344048217,
'number': 995,
'precision': 0.8654618473895582,
'recall': 0.8663316582914573},
'ORGANIZATION': {'f1': 0.9066332916145182,
'number': 1971,
'precision': 0.8947628458498024,
'recall': 0.9188229325215627},
'PERSON': {'f1': 0.9632426988922457,
'number': 2015,
'precision': 0.9775166070516096,
'recall': 0.9493796526054591},
'overall_accuracy': 0.988255561629313,
'overall_f1': 0.9324058459808882,
'overall_precision': 0.9322748349023465,
'overall_recall': 0.932536893886156}
当我使用 Sampler 或 shuffle=False 时会打印上述指标。如果我使用 shuffle=True,我得到:
{'LOCATION': {'f1': 0.03902284263959391,
'number': 2134,
'precision': 0.029496402877697843,
'recall': 0.057638238050609185},
'MISC': {'f1': 0.010318142734307824,
'number': 995,
'precision': 0.009015777610818933,
'recall': 0.012060301507537688},
'ORGANIZATION': {'f1': 0.027420984269014285,
'number': 1971,
'precision': 0.019160951996772892,
'recall': 0.04819888381532217},
'PERSON': {'f1': 0.02119907254057635,
'number': 2015,
'precision': 0.01590852597564007,
'recall': 0.03176178660049628},
'overall_accuracy': 0.5651741788003777,
'overall_f1': 0.02722600361161272,
'overall_precision': 0.020301063389034663,
'overall_recall': 0.041321152494729445}
更新:我修改了损失代码以进行评估。这段代码似乎没有问题。您可以在下面看到新代码:
print('Prediction started on test data')
model.eval()
eval_loss = 0
predictions , true_labels = [], []
for batch in val_loader:
b_labels = batch['labels'].to(device)
batch = {k:v.type(torch.long).to(device) for k,v in batch.items()}
with torch.no_grad():
outputs = model(**batch)
loss, logits = outputs[0:2]
logits = logits.detach().cpu().numpy()
label_ids = b_labels.detach().cpu().numpy()
predictions.append(logits)
true_labels.append(label_ids)
eval_loss += loss
print('Prediction completed')
eval_loss = eval_loss / len(val_loader)
print("Validation loss: {}".format(eval_loss))
虽然我仍然没有得到 DataLoader 问题的答案。
我还意识到当我这样做时 print(model.eval())
我仍然在评估模式下从模型中退出。
您没有正确计算损失。首先,我想知道为什么您使用对数的均值作为损失,但这可能是我不熟悉的特定任务。 但是您肯定没有正确地累积损失,或者根本不准确。您打印出的损失只是来自最后一批。这解释了为什么使用 shuffle 时结果不同。如果正确实施,绝对不会出现这种情况。
你得到负损失的事实是因为你只是使用对数的均值作为损失,这当然可以是负的。
准确性等其他指标不应受此影响,但您没有提供计算这些指标的代码,因此无法发现故障点。
据我了解,答案很简单:
“我看到我爸爸是这样做的,他爸爸也是这样做的,所以我也是这样做的”。
我查看了很多笔记本,看看人们是如何加载数据进行验证的,在每个笔记本中,我都看到人们在使用顺序采样器进行验证。在验证期间没有人使用洗牌或随机抽样。我不完全知道为什么,但就是这样。因此,如果访问此 post 的任何人都想知道同样的事情,答案基本上就是我上面引用的内容。
另外,我编辑了原来的 post 以解决我遇到的丢失问题。我计算错了。如果您还为模型提供原始标签,显然 Bert 会在输出 (outputs[0]) 的索引 0 处返回损失。在第一个代码片段中,当我从模型中获取输出时,我没有为模型提供原始标签,因此它没有返回索引 0 处的损失值,而是仅返回对数。
基本上你需要做的是:
outputs = model(input_ids, mask, label=label)
loss = outputs[0]
logits = outputs[1]