Почему потери продолжают уменьшаться, а производительность остается неизменной?
Я использую модель bert-lstm-crf, с моделью bert из https://github.com/huggingface/pytorch-pretrained-BERT/ и модели lstm crf написаны мной.
После тренировки модели bert-lstm-crf для 25 эпох производительность на тренировочном наборе, наборе разработчиков и тестовом наборе остается неизменной, но потери продолжают уменьшаться. Где я должен внести изменения?
Вот производительность:
25 эпоха:
tensor(10267.6279, device='cuda:0')
(0.42706720346856614, 0.4595134955014995, 0.4426966292134832)
(0.43147208121827413, 0.4271356783919598, 0.42929292929292934)
(0.4460093896713615, 0.4668304668304668, 0.4561824729891957)
26 эпоха:
tensor(10219.3398, device='cuda:0')
(0.44544364508393286, 0.4951682772409197, 0.46899163642101943)
(0.4469135802469136, 0.4547738693467337, 0.45080946450809467)
(0.45871559633027525, 0.4914004914004914, 0.4744958481613286)
27-я эпоха:
tensor(10169.0742, device='cuda:0')
(0.44544364508393286, 0.4951682772409197, 0.46899163642101943)
(0.4469135802469136, 0.4547738693467337, 0.45080946450809467)
(0.45871559633027525, 0.4914004914004914, 0.4744958481613286)
больше эпох: меньшие потери при той же производительности:
(0.44544364508393286, 0.4951682772409197, 0.46899163642101943)
(0.4469135802469136, 0.4547738693467337, 0.45080946450809467)
(0.45871559633027525, 0.4914004914004914, 0.4744958481613286)
Это действительно странная проблема, я понятия не имею, как с этим справиться. Любое предложение будет очень полезно.
Вот связанный код:
for epoch in tqdm(range(200)):
{loss = train_one_epoch(dataloader=source_train_dataloader,
model=model, optimizer=optimizer)
train_perf = test_one_epoch(dataloader=source_train_dataloader_for_test,
model=model)
dev_perf = test_one_epoch(dataloader=source_dev_dataloader, model=model)
test_perf = test_one_epoch(dataloader=source_test_dataloader,
model=model)
base_result_loc = "bert_char_ps/bert_char_result"
# store performance result
add_model_result(
base_result_loc,
epoch,
loss,
train_perf,
dev_perf,
test_perf)
}
Производительность должна меняться с потерей, но теперь это не так
1 ответ
Я изменил эту реализацию PyTorch модели BERT-NER и добавил CRF. У меня есть следующий класс, который отлично работает в моем случае.
class BertWithCRF(BertPreTrainedModel):
def __init__(self, config, labels, dropout=0.1):
super(BertWithCRF, self).__init__(config)
self.tagset_size = len(labels)
self.tag_to_ix = {k: v for v, k in enumerate(labels)}
self.bert = BertModel(config)
self.dropout = nn.Dropout(dropout)
self.classifier = nn.Linear(config.hidden_size, self.tagset_size)
self.apply(self.init_bert_weights)
self.transitions = nn.Parameter(
torch.zeros(self.tagset_size, self.tagset_size))
self.transitions.data[self.tag_to_ix[START_TAG], :] = -10000
self.transitions.data[:, self.tag_to_ix[STOP_TAG]] = -10000
def _batch_forward_alg(self, feats, mask):
assert mask is not None
# calculate in log domain
# feats is batch_size * len(sentence) * tagset_size
# initialize alpha with a Tensor with values all equal to -10000.
score = torch.Tensor(feats.size(0), self.tagset_size).fill_(-10000.)
score[:, self.tag_to_ix[START_TAG]] = 0.
if feats.is_cuda:
score = score.cuda()
mask = mask.float()
trans = self.transitions.unsqueeze(0) # [1, C, C]
for t in range(feats.size(1)): # recursion through the sequence
mask_t = mask[:, t].unsqueeze(1)
emit_t = feats[:, t].unsqueeze(2) # [B, C, 1]
score_t = score.unsqueeze(1) + emit_t + trans # [B, 1, C] -> [B, C, C]
score_t = batch_log_sum_exp(score_t) # [B, 1, C] -> [B, C, C]
score = score_t * mask_t + score * (1 - mask_t)
score = batch_log_sum_exp(score + self.transitions[self.tag_to_ix[STOP_TAG]])
return score # partition function
def _batch_score_sentence(self, feats, tags, mask):
assert mask is not None
score = torch.Tensor(feats.size(0)).fill_(0.)
if feats.is_cuda:
score = score.cuda()
feats = feats.unsqueeze(3)
mask = mask.float()
trans = self.transitions.unsqueeze(2)
add_start_tags = torch.empty(tags.size(0), 1).fill_(self.tag_to_ix[START_TAG]).type_as(tags)
tags = torch.cat([add_start_tags, tags], dim=-1)
for t in range(feats.size(1)): # recursion through the sequence
mask_t = mask[:, t]
emit_t = torch.cat([h[t, y[t + 1]] for h, y in zip(feats, tags)])
trans_t = torch.cat([trans[y[t + 1], y[t]] for y in tags])
score += (emit_t + trans_t) * mask_t
last_tag = tags.gather(1, mask.sum(1).long().unsqueeze(1)).squeeze(1)
score += self.transitions[self.tag_to_ix[STOP_TAG], last_tag]
return score
def _batch_viterbi_decode(self, feats, mask):
# initialize backpointers and viterbi variables in log space
bptr = torch.LongTensor()
score = torch.Tensor(feats.size(0), self.tagset_size).fill_(-10000.)
score[:, self.tag_to_ix[START_TAG]] = 0.
if feats.is_cuda:
score = score.cuda()
bptr = bptr.cuda()
mask = mask.float()
for t in range(feats.size(1)): # recursion through the sequence
mask_t = mask[:, t].unsqueeze(1)
score_t = score.unsqueeze(1) + self.transitions # [B, 1, C] -> [B, C, C]
score_t, bptr_t = score_t.max(2) # best previous scores and tags
score_t += feats[:, t] # plus emission scores
bptr = torch.cat((bptr, bptr_t.unsqueeze(1)), 1)
score = score_t * mask_t + score * (1 - mask_t)
score += self.transitions[self.tag_to_ix[STOP_TAG]]
best_score, best_tag = torch.max(score, 1)
# back-tracking
bptr = bptr.tolist()
best_path = [[i] for i in best_tag.tolist()]
for b in range(feats.size(0)):
x = best_tag[b] # best tag
y = int(mask[b].sum().item()) # no. of non-pad tokens
for bptr_t in reversed(bptr[b]):
x = bptr_t[x]
best_path[b].append(x)
best_path[b].pop()
best_path[b].reverse()
best_path = torch.LongTensor(best_path)
if feats.is_cuda:
best_path = best_path.cuda()
return best_path
def _get_bert_features(self, input_ids, token_type_ids, attention_mask):
sequence_output, _ = self.bert(input_ids, token_type_ids, attention_mask, output_all_encoded_layers=False)
sequence_output = self.dropout(sequence_output)
bert_feats = self.classifier(sequence_output)
return bert_feats
def forward(self, input_ids, token_type_ids=None, attention_mask=None, labels=None):
bert_feats = self._get_bert_features(input_ids, token_type_ids, attention_mask)
if labels is not None:
forward_score = self._batch_forward_alg(bert_feats, attention_mask)
gold_score = self._batch_score_sentence(bert_feats, labels, attention_mask)
return (forward_score - gold_score).mean()
else:
tag_seq = self._batch_viterbi_decode(bert_feats, attention_mask)
return tag_seq