Files
wehub-resource-sync 2aaeece67c
Codestyle Check / Lint (push) Has been cancelled
Codestyle Check / Check bypass (push) Has been cancelled
Pipelines-Test / Pipelines-Test (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:37:14 +08:00

364 lines
14 KiB
Python

# Copyright (c) 2024 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import numpy as np
import paddle
def check_preference_data(data):
if isinstance(data["src"], str):
data["src"] = [data["src"]]
if isinstance(data["tgt"], str):
data["tgt"] = [data["tgt"]]
if len(data["src"]) != len(data["tgt"]) + 1:
raise ValueError(
"The number of src and tgt should differ by 1, but got {} and {}".format(
len(data["src"]), len(data["tgt"])
)
)
if (len(data["response"]) != 2) or (len(data["response"]) != len(data["sort"])):
raise ValueError(
"The number of response and sort should be 2, but got {} and {}".format(
len(data["response"]), len(data["sort"])
)
)
if len(data["response"][0]) == 0 or len(data["response"][1]) == 0:
raise ValueError(f"The response should not be empty, buut got {data}.")
if data["sort"][0] == data["sort"][1]:
raise ValueError("The two sort should be different.")
return data
def preprocess_preference_data(data, tokenizer, data_args, model_args):
"""Convert raw format example to Example."""
# 1. Check data format
data = check_preference_data(data)
if data["sort"][0] > data["sort"][1]:
chosen = data["response"][0]
rejected = data["response"][1]
else:
chosen = data["response"][1]
rejected = data["response"][0]
chosen_token_ids = tokenizer(chosen)["input_ids"] + [tokenizer.eos_token_id]
rejected_token_ids = tokenizer(rejected)["input_ids"] + [tokenizer.eos_token_id]
prompt_tokens_ids = tokenizer(data["src"][-1], add_special_tokens=True)["input_ids"]
for idx in range(len(data["tgt"])):
src_token_ids = tokenizer(data["src"][-idx - 1], add_special_tokens=True)["input_ids"]
tgt_token_ids = tokenizer(data["tgt"][-idx])["input_ids"] + [tokenizer.eos_token_id]
prompt_tokens_ids = src_token_ids + tgt_token_ids + prompt_tokens_ids
if len(prompt_tokens_ids) + len(rejected_token_ids) + len(chosen_token_ids) > data_args.max_seq_len:
prompt_tokens_ids = prompt_tokens_ids[-data_args.max_prompt_len :]
if len(prompt_tokens_ids) + len(rejected_token_ids) + len(chosen_token_ids) > data_args.max_seq_len:
max_response_len = data_args.max_seq_len - len(prompt_tokens_ids)
# 按比例截断
max_chosen_len = int(
len(chosen_token_ids) / (len(chosen_token_ids) + len(rejected_token_ids)) * max_response_len
)
max_rejected_len = max_response_len - max_chosen_len
chosen_token_ids = chosen_token_ids[:max_chosen_len]
rejected_token_ids = rejected_token_ids[:max_rejected_len]
input_ids = prompt_tokens_ids + chosen_token_ids + rejected_token_ids
prompt_len, chosen_len, rejected_len, seq_len = (
len(prompt_tokens_ids),
len(chosen_token_ids),
len(rejected_token_ids),
len(input_ids),
)
position_ids = (
list(range(prompt_len)) # prompt
+ list(range(prompt_len, prompt_len + chosen_len)) # chosen
+ list(range(prompt_len, prompt_len + rejected_len)) # rejected
)
# response index
response_indexs = [prompt_len + chosen_len - 1, seq_len - 1]
output_dict = {
"input_ids": input_ids,
"position_ids": position_ids,
"response_indexs": response_indexs,
}
# attention mask
if model_args.flash_mask:
output_dict["attn_mask_startend_row_indices"] = (
[seq_len] * prompt_len + [prompt_len + chosen_len] * chosen_len + [seq_len] * rejected_len
)
else:
attention_mask = np.tri(seq_len, seq_len, dtype=bool)
attention_mask[(prompt_len + chosen_len) :, prompt_len : (prompt_len + chosen_len)] = False
output_dict["attention_mask"] = attention_mask
return output_dict
def preference_collate_fn(batch, max_seq_len=None, pad_token_id=0):
"""Convert batch data into tensor."""
if max_seq_len is None:
raise ValueError("max_seq_len is None.")
input_dict = {
"input_ids": [],
"position_ids": [],
"response_indexs": [],
}
sequence = batch[0]
if "attn_mask_startend_row_indices" in sequence:
input_dict["attn_mask_startend_row_indices"] = []
use_attn_mask_startend_row_indices = True
elif "attention_mask" in sequence:
input_dict["attention_mask"] = []
use_attn_mask_startend_row_indices = False
else:
raise ValueError("attention_mask and attn_mask_startend_row_indices are both None.")
for i, sequence in enumerate(batch):
difference = max_seq_len - len(sequence["input_ids"])
input_dict["input_ids"].append(sequence["input_ids"] + [pad_token_id] * difference)
input_dict["position_ids"].append(sequence["position_ids"] + [0] * difference)
if use_attn_mask_startend_row_indices:
input_dict["attn_mask_startend_row_indices"].append(
[
sequence["attn_mask_startend_row_indices"]
+ [sequence["attn_mask_startend_row_indices"][-1]] * difference
]
)
else:
input_dict["attention_mask"].append(
np.pad(
sequence["attention_mask"],
pad_width=((0, 0), (0, difference), (0, difference)),
mode="constant",
constant_values=False,
)
)
for ri in sequence["response_indexs"]:
input_dict["response_indexs"].append(
[
i, # bs
ri[0], # chosen_response_start_index
ri[1], # rejeted_response_start_index
]
)
for key in input_dict:
if key == "attention_mask":
input_dict[key] = np.array(input_dict[key], dtype=bool)
elif key == "attn_mask_startend_row_indices":
input_dict[key] = np.array(input_dict[key], dtype=np.int32)
else:
input_dict[key] = np.array(input_dict[key])
return input_dict
def check_process_data(data):
"""
"src" : ["prompt"],
"tgt" : [],
"responses" : ["step_1", ..., "step_k"]
"labels" : ["label_1", ..., "label_k"]
"""
if isinstance(data["src"], str):
data["src"] = [data["src"]]
if isinstance(data["tgt"], str):
data["tgt"] = [data["tgt"]]
if len(data["src"]) != len(data["tgt"]) + 1:
raise ValueError(
"The number of src and tgt should differ by 1, but got {} and {}".format(
len(data["src"]), len(data["tgt"])
)
)
if len(data["responses"]) != len(data["labels"]):
raise ValueError(
"The number of responses and labels should be equal, but got {} and {}".format(
len(data["responses"]), len(data["labels"])
)
)
if "" in data["responses"] or "" in data["labels"]:
raise ValueError(f"Any step in the responses or labels should not be empty, but got {data}.")
return data
def preprocess_process_data(data, tokenizer, data_args, model_args):
"""Convert raw format example to Example."""
# Check data format
data = check_process_data(data)
placeholder_token_id = tokenizer(model_args.placeholder_token, add_special_tokens=False)["input_ids"]
placeholder_token_id = placeholder_token_id[-1]
prompt_token_ids = tokenizer(data["src"][-1], add_special_tokens=False)["input_ids"]
for idx in range(len(data["tgt"])):
src_token_ids = tokenizer(data["src"][-idx - 1], add_special_tokens=False)["input_ids"]
tgt_token_ids = tokenizer(data["tgt"][-idx], add_special_tokens=False)["input_ids"] + [tokenizer.eos_token_id]
prompt_token_ids = src_token_ids + tgt_token_ids + prompt_token_ids
response_token_ids = tokenizer(
f" {model_args.placeholder_token}\n".join(data["responses"]) + f" {model_args.placeholder_token}",
add_special_tokens=False,
)["input_ids"]
# NOTE: Truncation may leads to incompleteness of the last CoT step, however, the prm will not predict the
# corresponding reward either. So it is ok then.
if len(prompt_token_ids) + len(response_token_ids) > data_args.max_seq_len:
prompt_token_ids = prompt_token_ids[-data_args.max_prompt_len :]
if len(prompt_token_ids) + len(response_token_ids) > data_args.max_seq_len:
max_response_len = data_args.max_seq_len - len(prompt_token_ids)
response_token_ids = response_token_ids[-max_response_len:]
input_ids = paddle.to_tensor(prompt_token_ids + response_token_ids)
label_token_ids = []
for local_label in data["labels"]:
if local_label not in model_args.reward_tokens:
raise ValueError(
f"The label {local_label} should be in reward tokens {model_args.reward_tokens}, got {data}."
)
label_token_ids.append(tokenizer(local_label, add_special_tokens=False)["input_ids"][-1])
labels = paddle.full_like(input_ids, -100, dtype=input_ids.dtype)
indices = paddle.nonzero(input_ids == placeholder_token_id).flatten()
for idx, replacement_value in zip(indices, label_token_ids):
labels[idx] = replacement_value
prompt_len, seq_len = (len(prompt_token_ids), len(input_ids))
position_ids = list(range(prompt_len)) + list(range(prompt_len, seq_len))
output_dict = {
"input_ids": input_ids,
"position_ids": position_ids,
"labels": labels,
}
if model_args.flash_mask:
output_dict["attn_mask_startend_row_indices"] = [seq_len] * seq_len
else:
attention_mask = np.tri(seq_len, seq_len, dtype=bool)
output_dict["attention_mask"] = attention_mask
return output_dict
def zero_padding_process_collate_fn(batch, max_seq_len=None, pad_token_id=0):
"""Convert batch data into tensor."""
if max_seq_len is None:
raise ValueError("max_seq_len is None.")
input_dict = {
"input_ids": [],
"position_ids": [],
"labels": [],
}
sequence = batch[0]
if "attn_mask_startend_row_indices" in sequence:
input_dict["attn_mask_startend_row_indices"] = []
use_attn_mask_startend_row_indices = True
elif "attention_mask" in sequence:
input_dict["attention_mask"] = []
use_attn_mask_startend_row_indices = False
else:
raise ValueError("attention_mask and attn_mask_startend_row_indices are both None.")
for i, sequence in enumerate(batch):
difference = max_seq_len - len(sequence["input_ids"])
input_dict["input_ids"].append(sequence["input_ids"] + [pad_token_id] * difference)
input_dict["position_ids"].append(sequence["position_ids"] + [0] * difference)
input_dict["labels"].append(sequence["labels"] + [-100] * difference)
if use_attn_mask_startend_row_indices:
input_dict["attn_mask_startend_row_indices"].append(
[
sequence["attn_mask_startend_row_indices"]
+ [sequence["attn_mask_startend_row_indices"][-1]] * difference
]
)
else:
input_dict["attention_mask"].append(
np.pad(
sequence["attention_mask"],
pad_width=((0, 0), (0, difference), (0, difference)),
mode="constant",
constant_values=False,
)
)
for key in input_dict:
if key == "attention_mask":
input_dict[key] = np.array(input_dict[key], dtype=bool)
elif key == "attn_mask_startend_row_indices":
input_dict[key] = np.array(input_dict[key], dtype=np.int32)
else:
input_dict[key] = np.array(input_dict[key])
return input_dict
def process_collate_fn(batch, pad_token_id=0):
"""Convert batch data into tensor."""
max_seq_len = max([len(sequence["input_ids"]) for sequence in batch])
input_dict = {
"input_ids": [],
"position_ids": [],
"labels": [],
}
sequence = batch[0]
if "attn_mask_startend_row_indices" in sequence:
input_dict["attn_mask_startend_row_indices"] = []
use_attn_mask_startend_row_indices = True
elif "attention_mask" in sequence:
input_dict["attention_mask"] = []
use_attn_mask_startend_row_indices = False
else:
raise ValueError("attention_mask and attn_mask_startend_row_indices are both None.")
for i, sequence in enumerate(batch):
difference = max_seq_len - len(sequence["input_ids"])
# input_ids: Tensor(seqL, ); position_ids: list, len(seqL); labels: Tensor(seqL, )
input_dict["input_ids"].append(sequence["input_ids"].tolist() + [pad_token_id] * difference)
input_dict["position_ids"].append(sequence["position_ids"] + [0] * difference)
input_dict["labels"].append(sequence["labels"].tolist() + [-100] * difference)
if use_attn_mask_startend_row_indices:
input_dict["attn_mask_startend_row_indices"].append(
[
sequence["attn_mask_startend_row_indices"]
+ [sequence["attn_mask_startend_row_indices"][-1]] * difference
]
)
else:
input_dict["attention_mask"].append(
np.pad(
sequence["attention_mask"],
pad_width=((0, difference), (0, difference)),
mode="constant",
constant_values=False,
)
)
for key in input_dict:
if key == "attention_mask":
input_dict[key] = np.array(input_dict[key], dtype=bool)
elif key == "attn_mask_startend_row_indices":
input_dict[key] = np.array(input_dict[key], dtype=np.int32)
else:
input_dict[key] = np.array(input_dict[key])
return input_dict