Files
2026-07-13 13:24:13 +08:00

197 lines
7.4 KiB
Python

import torch
from torch import nn
from transformers.models.llama.modeling_llama import (
LlamaForCausalLM,
LlamaModel,
)
from transformers.models.gemma.modeling_gemma import (
GemmaForCausalLM,
GemmaModel,
)
from transformers.models.mistral.modeling_mistral import (
MistralForCausalLM,
MistralPreTrainedModel,
)
from typing import Union
def sft_loss_on_logits(logits: torch.FloatTensor, labels: torch.LongTensor, pad_token_id: int, macro_average: bool = False, row_weights: torch.Tensor = None):
batch_size = labels.size(0)
labels = labels[:, 1:].contiguous()
logits = logits[:, :-1].contiguous()
logits = logits.view(-1, logits.size(-1))
labels = labels.view(-1)
pad_mask = labels.eq(pad_token_id)
if pad_mask.sum() == labels.numel(): # To tackle all problems that the response are empty, or the pad_token equals eos_token so no response.
return 0.
labels[pad_mask] = -100
if macro_average:
loss_fct = nn.CrossEntropyLoss(reduction='none')
loss = loss_fct(logits, labels)
row_num_element = (~pad_mask).reshape(batch_size, -1).sum(-1).float()
row_mask = row_num_element > 0
loss = loss.view(batch_size, -1)
loss = loss.sum(-1) / row_num_element
if row_weights is not None:
loss = loss * row_weights
loss = loss[row_mask].mean()
else:
loss_fct = nn.CrossEntropyLoss()
loss = loss_fct(logits, labels)
return loss
def llama_dpo_batch_forward(model: Union[LlamaForCausalLM, GemmaForCausalLM, MistralForCausalLM],
input_ids: torch.LongTensor, attention_mask: torch.Tensor, labels: torch.LongTensor, pad_token_id: int = None,
average_log_prob: bool = False):
outputs = model.model(
input_ids=input_ids,
attention_mask=attention_mask,
)
hidden_states = outputs[0]
logits = model.lm_head(hidden_states)
logits = logits.float()
labels = labels[:, 1:].clone()
if pad_token_id is None:
pad_token_id = model.config.pad_token_id
loss_mask = labels.ne(pad_token_id)
labels[~loss_mask] = 0
per_token_logprobs = torch.gather(logits.log_softmax(-1), dim=2, index=labels.unsqueeze(2)).squeeze(2)
if average_log_prob:
log_ps = (per_token_logprobs * loss_mask).sum(-1) / loss_mask.sum(-1)
else:
log_ps = (per_token_logprobs * loss_mask).sum(-1)
return logits, log_ps, loss_mask
def llama_batch_forward(model: Union[LlamaForCausalLM, GemmaForCausalLM, MistralForCausalLM], input_ids: torch.LongTensor, attention_mask: torch.Tensor):
outputs = model.model(
input_ids=input_ids,
attention_mask=attention_mask,
)
hidden_states = outputs[0]
logits = model.lm_head(hidden_states)
return logits
def tdpo_get_batch_logps(logits: torch.FloatTensor, reference_logits: torch.FloatTensor, labels: torch.LongTensor, pad_token_id: int,
average_log_prob: bool = False):
"""Compute the kl divergence/log probabilities of the given labels under the given logits.
Args:
logits: Logits of the model (unnormalized). Shape: (batch_size, sequence_length, vocab_size)
reference_logits: Logits of the reference model (unnormalized). Shape: (batch_size, sequence_length, vocab_size)
labels: Labels for which to compute the log probabilities. Label tokens with a value of -100 are ignored. Shape: (batch_size, sequence_length)
pad_token_id: The id of the padding token.
average_log_prob: If True, return the average log probability per (non-masked) token. Otherwise, return the sum of the log probabilities of the
(non-masked) tokens.
Returns:
Several tensors of shape (batch_size,) containing the average/sum kl divergence/log probabilities of the given labels under the given logits.
"""
assert logits.shape[:-1] == labels.shape
assert reference_logits.shape[:-1] == labels.shape
labels = labels[:, 1:].clone()
logits = logits[:, :-1, :]
reference_logits = reference_logits[:, :-1, :]
loss_mask = labels.ne(pad_token_id)
# dummy token; we'll ignore the losses on these tokens later
labels[~loss_mask] = 0
vocab_logps = logits.log_softmax(-1)
reference_vocab_ps = reference_logits.softmax(-1)
reference_vocab_logps = reference_vocab_ps.log()
per_position_kl = (reference_vocab_ps * (reference_vocab_logps - vocab_logps)).sum(-1)
per_token_logps = torch.gather(vocab_logps, dim=2, index=labels.unsqueeze(2)).squeeze(2)
per_reference_token_logps = torch.gather(reference_vocab_logps, dim=2, index=labels.unsqueeze(2)).squeeze(2)
logps_margin = per_token_logps - per_reference_token_logps
if average_log_prob:
return (logps_margin * loss_mask).sum(-1) / loss_mask.sum(-1), \
(per_position_kl * loss_mask).sum(-1) / loss_mask.sum(-1), \
(per_token_logps * loss_mask).sum(-1) / loss_mask.sum(-1)
else:
return (logps_margin * loss_mask).sum(-1), \
(per_position_kl * loss_mask).sum(-1), \
(per_token_logps * loss_mask).sum(-1)
def llama_last_token_cls_batch_forward(model: Union[LlamaModel, GemmaForCausalLM, MistralPreTrainedModel], linear: nn.Linear,
input_ids: torch.LongTensor, attention_mask: torch.Tensor,
pad_token_id: int, return_full_logits: bool = False):
transformer_outputs = model(
input_ids,
attention_mask=attention_mask,
)
hidden_states = transformer_outputs[0]
batch_size = input_ids.shape[0]
sequence_lengths = (torch.eq(input_ids, pad_token_id).long().argmax(-1) - 1).to(device=hidden_states.device)
if return_full_logits:
rewards = linear(hidden_states)
return rewards, sequence_lengths
last_token_states = hidden_states[torch.arange(batch_size, device=hidden_states.device), sequence_lengths]
rewards = linear(last_token_states)
return rewards, sequence_lengths
def llama_token_batch_forward(model: Union[LlamaModel, GemmaModel], linear: nn.Linear,
input_ids: torch.LongTensor, attention_mask: torch.Tensor, pad_token_id: int = None, average: bool = False):
outputs = model(
input_ids=input_ids,
attention_mask=attention_mask,
)
hidden_states = outputs[0]
logits = linear(hidden_states).squeeze(-1)
logits = logits.float()
if pad_token_id is None:
pad_token_id = model.config.pad_token_id
loss_mask = input_ids.ne(pad_token_id)
if average:
rewards = (logits * loss_mask).sum(-1) / loss_mask.sum(-1)
else:
rewards = (logits * loss_mask).sum(-1)
return rewards
def llama_last_token_forward_value(model: Union[LlamaModel, GemmaForCausalLM], linear: nn.Linear,
input_ids: torch.LongTensor, attention_mask: torch.Tensor,
pad_token_id: int):
transformer_outputs = model(
input_ids,
attention_mask=attention_mask,
)
hidden_states = transformer_outputs[0]
batch_size = input_ids.shape[0]
sequence_lengths = (torch.eq(input_ids, pad_token_id).long().argmax(-1) - 1).to(device=hidden_states.device)
values = linear(hidden_states)
rewards = values[torch.arange(batch_size, device=hidden_states.device), sequence_lengths]
return values, rewards, sequence_lengths