[fix] PretrainDataset 按 token id 屏蔽 padding,会误伤正文里字面的 <|endoftext|>

pad_token 是 <|endoftext|>(id 0)。正文里字面出现这个串时会被编成同一个 id,
而 labels[input_ids == pad_token_id] = -100 是按 token id 屏蔽的,
于是这个真实的正文 token 被当成 padding 从 loss 里丢掉。

改为按位置屏蔽 labels[n_real:] = -100,n_real = len(bos + 正文 + eos)。
padding 只可能出现在序列尾部,所以按位置屏蔽在语义上更准确,也更便宜。

SFTDataset 不受影响:它的 generate_labels 从全 -100 出发、扫描
assistant 区间来填,不依赖 pad id。
pull/861/head
Linxiushen 2026-09-20 10:36:32 +08:00
parent cc312c1cc6
commit d6401cbb92
1 changed files with 7 additions and 2 deletions

View File

@ -48,10 +48,15 @@ class PretrainDataset(Dataset):
sample = self.samples[index] sample = self.samples[index]
tokens = self.tokenizer(str(sample['text']), add_special_tokens=False, max_length=self.max_length - 2, truncation=True).input_ids tokens = self.tokenizer(str(sample['text']), add_special_tokens=False, max_length=self.max_length - 2, truncation=True).input_ids
tokens = [self.tokenizer.bos_token_id] + tokens + [self.tokenizer.eos_token_id] tokens = [self.tokenizer.bos_token_id] + tokens + [self.tokenizer.eos_token_id]
input_ids = tokens + [self.tokenizer.pad_token_id] * (self.max_length - len(tokens)) n_real = len(tokens) # bos + 正文 + eos,后面才是 padding
input_ids = tokens + [self.tokenizer.pad_token_id] * (self.max_length - n_real)
input_ids = torch.tensor(input_ids, dtype=torch.long) input_ids = torch.tensor(input_ids, dtype=torch.long)
labels = input_ids.clone() labels = input_ids.clone()
labels[input_ids == self.tokenizer.pad_token_id] = -100 # 按位置屏蔽,而不是按 token id。pad_token 是 <|endoftext|>(id 0),
# 正文里字面出现这个串时会被编成同一个 id
# (tokenizer('前面<|endoftext|>后面') -> [938, 605, 0, 701, 605]),
# 用 `input_ids == pad_token_id` 会把这个真实的正文 token 一起屏蔽掉。
labels[n_real:] = -100
return input_ids, labels return input_ids, labels