Files
paddlepaddle--paddlenlp/paddlenlp/trl/llm_utils.py
T
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

967 lines
35 KiB
Python

# Copyright (c) 2023 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.
from __future__ import annotations
import glob
import math
import os
import shutil
import struct
from typing import List, Optional
import numpy as np
import paddle
import paddle.distributed as dist
import paddle.incubate.multiprocessing as mp
from paddle.distributed import fleet
from sklearn.metrics import accuracy_score
from paddlenlp.datasets import ZeroPaddingIterableDataset
from paddlenlp.generation import GenerationConfig
from paddlenlp.trainer import TrainerCallback
from paddlenlp.trainer.trainer_utils import IterableDatasetShard
from paddlenlp.transformers import (
AutoTokenizer,
ChatGLMv2Tokenizer,
DeepseekV2ForCausalLMPipe,
DeepseekV3ForCausalLMPipe,
LlamaForCausalLMPipe,
PretrainedConfig,
Qwen2ForCausalLMPipe,
Qwen2MoeForCausalLMPipe,
)
from paddlenlp.transformers.tokenizer_utils import PretrainedTokenizer
from paddlenlp.utils.log import logger
def compute_metrics(eval_preds):
flattened_preds = np.array(eval_preds.predictions).flatten()
flattened_labels = np.array(eval_preds.label_ids).flatten()
filtered_preds = flattened_preds[flattened_labels != -100]
filtered_labels = flattened_labels[flattened_labels != -100]
accuracy = accuracy_score(y_true=filtered_labels, y_pred=filtered_preds)
return {
"accuracy": accuracy,
}
def get_prefix_tuning_params(model):
if model.base_model_prefix == "chatglm":
from paddlenlp.peft.prefix import chatglm_postprocess_past_key_value
num_attention_heads = model.config.num_attention_heads
num_hidden_layers = model.config.num_hidden_layers
hidden_size = model.config.hidden_size
postprocess_past_key_value = chatglm_postprocess_past_key_value
multi_query_group_num = None
elif model.base_model_prefix == "chatglm_v2":
from paddlenlp.peft.prefix import chatglm_postprocess_past_key_value
num_attention_heads = model.config.num_attention_heads
num_hidden_layers = model.config.num_layers
hidden_size = model.config.hidden_size
postprocess_past_key_value = chatglm_postprocess_past_key_value
multi_query_group_num = model.config.multi_query_group_num # num_key_value_heads
elif model.base_model_prefix == "bloom":
from paddlenlp.peft.prefix import bloom_postprocess_past_key_value
num_attention_heads = model.config.num_attention_heads
num_hidden_layers = model.config.n_layer
hidden_size = model.config.n_embed
postprocess_past_key_value = bloom_postprocess_past_key_value
multi_query_group_num = None
elif model.base_model_prefix == "llama":
from paddlenlp.peft.prefix import llama_postprocess_past_key_value
num_attention_heads = model.config.n_head
num_hidden_layers = model.config.n_layer
hidden_size = model.config.hidden_size
postprocess_past_key_value = llama_postprocess_past_key_value
multi_query_group_num = None
elif model.base_model_prefix == "mistral":
from paddlenlp.peft.prefix import mistral_postprocess_past_key_value
num_attention_heads = model.config.num_attention_heads
num_hidden_layers = model.config.num_hidden_layers
hidden_size = model.config.hidden_size
postprocess_past_key_value = mistral_postprocess_past_key_value
multi_query_group_num = model.config.num_key_value_heads
elif model.base_model_prefix == "qwen":
from paddlenlp.peft.prefix import qwen_postprocess_past_key_value
num_attention_heads = model.config.num_attention_heads
num_hidden_layers = model.config.num_hidden_layers
hidden_size = model.config.hidden_size
postprocess_past_key_value = qwen_postprocess_past_key_value
multi_query_group_num = None
elif model.base_model_prefix == "qwen2":
from paddlenlp.peft.prefix import qwen_postprocess_past_key_value
num_attention_heads = model.config.num_attention_heads
num_hidden_layers = model.config.num_hidden_layers
hidden_size = model.config.hidden_size
postprocess_past_key_value = qwen_postprocess_past_key_value
multi_query_group_num = model.config.num_key_value_heads # num_key_value_heads
else:
raise ValueError(f"Unknown base_model_prefix: {model.base_model_prefix}. ")
return dict(
num_attention_heads=num_attention_heads,
num_hidden_layers=num_hidden_layers,
hidden_size=hidden_size,
postprocess_past_key_value=postprocess_past_key_value,
multi_query_group_num=multi_query_group_num,
)
def get_lora_target_modules(model):
# Not yet support RowParallelLinear
if model.base_model_prefix == "chatglm":
target_modules = [".*query_key_value.*", ".*dense.*", ".*dense_h_to_4h.*", ".*dense_4h_to_h.*"]
elif model.base_model_prefix == "chatglm_v2":
target_modules = [
".*query.*",
".*key.*",
".*value.*",
".*dense.*",
".*dense_h_to_4h.*",
".*dense_4h_to_h.*",
]
elif model.base_model_prefix == "gpt":
target_modules = [
".*qkv_proj.*",
".*q_proj.*",
".*k_proj.*",
".*v_proj.*",
".*linear1.*",
".*linear2.*",
".*out_proj.*",
]
elif model.base_model_prefix == "bloom":
target_modules = [".*query_key_value.*", ".*dense.*", ".*dense_h_to_4h.*", ".*dense_4h_to_h.*"]
elif model.base_model_prefix in ["llama", "jamba"] or isinstance(model, LlamaForCausalLMPipe):
target_modules = [
".*q_proj.*",
".*v_proj.*",
".*k_proj.*",
".*o_proj.*",
".*qkv_proj.*",
".*gate_proj.*",
".*down_proj.*",
".*up_proj.*",
".*gate_up_fused_proj.*",
]
elif model.base_model_prefix == "opt":
target_modules = [
".*project_in.*",
".*project_out.*",
".*q_proj.*",
".*k_proj.*",
".*v_proj.*",
".*qkv_proj.*",
".*out_proj.*",
".*linear1.*",
".*linear2.*",
]
elif model.base_model_prefix == "qwen":
target_modules = [
".*attn.c_attn.*",
".*attn.c_proj.*",
".*mlp.w1.*",
".*mlp.w2.*",
".*mlp.c_proj.*",
]
# TODO maybe changed to use model.config.model_type for more generality as Paddleformers
elif model.base_model_prefix == "qwen2" or isinstance(model, Qwen2ForCausalLMPipe):
# qwen2 dense lora support
target_modules = [
# fused
".*qkv_proj.*",
".*gate_up_fused_proj.*",
# independent
".*q_proj.*",
".*k_proj.*",
".*v_proj.*",
".*gate_proj.*",
".*up_proj.*",
# always exist
".*o_proj.*",
".*down_proj.*",
]
elif getattr(model, "config", None) is not None and model.config.model_type == "qwen3":
# qwen3 dense lora support
target_modules = [
# fused
".*qkv_proj.*",
".*gate_up_fused_proj.*",
# independent
".*q_proj.*",
".*k_proj.*",
".*v_proj.*",
".*gate_proj.*",
".*up_proj.*",
# always exist
".*o_proj.*",
".*down_proj.*",
]
elif model.base_model_prefix == "mixtral":
target_modules = [
".*q_proj.*",
".*k_proj.*",
".*v_proj.*",
".*o_proj.*",
# ".*gate.*", # TODO(DrownFish19): Does the gate weight require training?
".*w1.*",
".*w2.*",
".*w3.*",
]
elif model.base_model_prefix == "mistral":
target_modules = [
".*q_proj.*",
".*k_proj.*",
".*v_proj.*",
".*o_proj.*",
".*gate.*",
".*w1.*",
".*w2.*",
".*w3.*",
]
elif model.base_model_prefix == "qwen2_moe" or isinstance(model, Qwen2MoeForCausalLMPipe):
target_modules = [
".*q_proj.*",
".*k_proj.*",
".*v_proj.*",
".*o_proj.*",
# ".*gate.*", # TODO(DrownFish19): Does the gate weight require training?
".*gate_proj.*",
".*up_proj.*",
".*down_proj.*",
]
elif model.base_model_prefix in ["deepseek_v2", "deepseek_v3"] or isinstance(
model, (DeepseekV2ForCausalLMPipe, DeepseekV3ForCausalLMPipe)
):
target_modules = [
".*q_proj.*",
".*q_a_proj.*",
".*q_b_proj.*",
".*kv_a_proj_with_mqa.*",
".*kv_b_proj.*",
".*kv_b_proj.*",
".*o_proj.*",
".*mlp.gate_proj.*",
".*mlp.up_proj.*",
".*mlp.down_proj.*",
]
elif model.base_model_prefix == "yuan":
target_modules = [
".*q_proj.*",
".*k_proj.*",
".*v_proj.*",
".*o_proj.*",
".*gate_proj.*",
".*up_proj.*",
".*down_proj.*",
]
else:
raise ValueError(f"Unknown base_model_prefix: {model.base_model_prefix}.")
return target_modules
class ZeroPaddingIterDatasetCallback(TrainerCallback):
"""
A [`TrainerCallback`] that handles early stopping.
"""
def on_step_end(self, args, state, control, **kwargs):
train_dataloader = kwargs["train_dataloader"]
if isinstance(train_dataloader.dataset, ZeroPaddingIterableDataset):
dataset = train_dataloader.dataset
elif isinstance(train_dataloader.dataset, IterableDatasetShard) and isinstance(
train_dataloader.dataset.dataset, ZeroPaddingIterableDataset
):
dataset = train_dataloader.dataset.dataset
else:
raise ValueError(
"Unexpected dataset format: ZeroPaddingIterDatasetCallback expects `paddlenlp.datasets.ZeroPaddingIterableDataset`"
)
if state.trial_params is None:
state.trial_params = {}
state.trial_params["zero_padding_global_step"] = dataset.zero_padding_global_step
def get_infer_model_path(input_dir, model_prefix):
if dist.get_world_size() > 1:
local_rank = dist.get_rank()
return os.path.join(input_dir, "rank_{}".format(local_rank), model_prefix)
else:
return os.path.join(input_dir, model_prefix)
def deserialize_from_file(fp):
x_type = fp.read(1)
x_type_out = struct.unpack("c", x_type)[0]
# data
data_list = []
if x_type_out == b"0":
data = fp.read(4)
data_out = struct.unpack("f", data)[0]
while data:
data_out = struct.unpack("f", data)[0]
data_list.append(data_out)
data = fp.read(4)
elif x_type_out == b"1":
data = fp.read(8)
while data:
data_out = struct.unpack("l", data)[0]
data_list.append(data_out)
data = fp.read(8)
elif x_type_out == b"2":
data = fp.read(4)
while data:
data_out = struct.unpack("i", data)[0]
data_list.append(data_out)
data = fp.read(4)
else:
print("type error")
data_arr = np.array(data_list)
return data_arr
def get_alibi_slopes(num_heads):
closest_power_of_2 = 2 ** math.floor(math.log2(num_heads))
base = 2 ** (-(2 ** -(math.log2(closest_power_of_2) - 3)))
powers = np.arange(1, 1 + closest_power_of_2)
slopes = np.power(base, powers)
if closest_power_of_2 != num_heads:
extra_base = 2 ** (-(2 ** -(math.log2(2 * closest_power_of_2) - 3)))
num_remaining_heads = min(closest_power_of_2, num_heads - closest_power_of_2)
extra_powers = np.arange(1, 1 + 2 * num_remaining_heads, 2)
slopes = np.concatenate([slopes, np.power(extra_base, extra_powers)], axis=0)
return slopes.astype("float32")
def pad_batch_data(insts, masks=None, pad_id=0, return_seq_len=False, pad_style="right"):
"""Pad sequences to the max sequence length in batch."""
max_len = max(map(len, insts))
if pad_style == "left":
inst_data = np.array([[pad_id] * (max_len - len(inst)) + list(inst) for inst in insts])
else:
inst_data = np.array([list(inst) + [pad_id] * (max_len - len(inst)) for inst in insts])
if masks is not None:
if pad_style == "left":
inst_mask = np.array([[0] * (max_len - len(inst)) + list(inst) for inst in masks])
else:
inst_mask = np.array([list(inst) + [0] * (max_len - len(inst)) for inst in masks])
if return_seq_len:
seq_len = np.array([len(inst) for inst in insts])
if masks is None:
return inst_data.astype("int64").reshape([-1, max_len]), seq_len
else:
return (
inst_data.astype("int64").reshape([-1, max_len]),
inst_mask.astype("int64").reshape([-1, max_len]),
seq_len,
)
else:
return inst_data.astype("int64").reshape([-1, max_len])
def dybatch_preprocess(
tokenizer,
texts: list[str],
src_length: int,
max_length: int,
architectures: str,
top_p: float,
temperature: float,
eos_token_id: int | list[list[int]],
pre_caches_length: int = 0,
benchmark: bool = False,
pad_style: str = "None",
):
"""Pre-process generation inputs."""
inputs = {}
if "chatglmforcausallm" == architectures.lower():
input_ids = []
position_ids = []
for text in texts:
tokens = tokenizer(
text,
return_tensors="np",
padding=True,
max_length=src_length,
# if use chat_template, it will not add special_tokens
add_special_tokens=tokenizer.chat_template is None or isinstance(tokenizer, ChatGLMv2Tokenizer),
)
input_ids.append(tokens["input_ids"][0])
position_ids.append(tokens["position_ids"][0])
pad_token_id = tokenizer([tokenizer.pad_token], return_tensors="np")["input_ids"][0][0]
inputs["input_ids"], seq_len = pad_batch_data(input_ids, pad_id=pad_token_id, return_seq_len=True)
bs = inputs["input_ids"].shape[0]
max_len = max(map(len, input_ids))
inst_data_pos = []
for i in range(len(position_ids)):
inst_data_pos.append(np.array([list(inst) + [0] * (max_len - len(inst)) for inst in position_ids[i]]))
inputs["position_ids"] = paddle.to_tensor(np.array(inst_data_pos))
elif "gpt" in architectures:
input_ids = []
if isinstance(texts, str):
texts = [texts]
for text in texts:
tokens = tokenizer(
text,
return_tensors="np",
padding=False,
max_length=src_length,
return_attention_mask=False,
return_token_type_ids=False,
)
input_ids.append(tokens["input_ids"][0])
pad_token_id = tokenizer([tokenizer.pad_token], return_tensors="np")["input_ids"][0][-1]
inputs["input_ids"], seq_len = pad_batch_data(input_ids, pad_id=pad_token_id, return_seq_len=True)
bs = inputs["input_ids"].shape[0]
max_len = max(map(len, input_ids))
position_ids = paddle.arange(sum(seq_len), dtype="int64")
pre_len = seq_len[0]
for length in seq_len[1:]:
position_ids[pre_len : length + pre_len] = position_ids[pre_len : length + pre_len] - pre_len
pre_len += length
inputs["position_ids"] = position_ids
else:
input_ids = []
attention_mask = []
if isinstance(texts, str):
texts = [texts]
if pad_style == "left":
return_attention_mask = True
truncation = True
for text in texts:
tokens = tokenizer(
text,
return_tensors="np",
padding="max_length",
max_length=src_length,
truncation=truncation,
return_attention_mask=return_attention_mask,
return_token_type_ids=False,
add_special_tokens=tokenizer.chat_template is None or isinstance(tokenizer, ChatGLMv2Tokenizer),
)
input_ids.append(tokens["input_ids"][0])
attention_mask.append(tokens["attention_mask"][0])
pad_token_id = tokenizer([tokenizer.pad_token], return_tensors="np")["input_ids"][0][-1]
inputs["input_ids"], inputs["attention_mask"], seq_len = pad_batch_data(
input_ids, attention_mask, pad_id=pad_token_id, return_seq_len=True, pad_style=pad_style
)
bs = inputs["input_ids"].shape[0]
max_len = max(map(len, input_ids))
position_ids = paddle.zeros(shape=[bs, max_length + src_length], dtype="int64")
for i in range(bs):
position_ids[
i, pre_caches_length + max_len - seq_len[i] : pre_caches_length + max_len
] = paddle.arange(seq_len[i]).unsqueeze(axis=0)
seq_len[i] = max_len
inputs["position_ids"] = position_ids
else:
for text in texts:
tokens = tokenizer(
text,
return_tensors="np",
padding=False,
max_length=src_length,
return_attention_mask=False,
return_token_type_ids=False,
add_special_tokens=tokenizer.chat_template is None or isinstance(tokenizer, ChatGLMv2Tokenizer),
)
input_ids.append(tokens["input_ids"][0])
pad_token_id = tokenizer([tokenizer.pad_token], return_tensors="np")["input_ids"][0][-1]
inputs["input_ids"], seq_len = pad_batch_data(input_ids, pad_id=pad_token_id, return_seq_len=True)
bs = inputs["input_ids"].shape[0]
max_len = max(map(len, input_ids))
position_ids = paddle.zeros(shape=[bs, max_length + src_length], dtype="int64")
for i in range(bs):
position_ids[i, pre_caches_length : pre_caches_length + seq_len[i]] = paddle.arange(seq_len[i])
inputs["position_ids"] = position_ids
tgt_ids = [input[-1:] for input in input_ids]
tgt_pos = []
for i, valid_len in enumerate(map(len, input_ids)):
tgt_pos.append(valid_len - 1)
step_idx = [
0,
] * bs
tgt_pos = np.array(tgt_pos).astype("int64")
if isinstance(eos_token_id, int):
eos_token_id = [eos_token_id]
inputs["eos_token_id"] = np.array(eos_token_id * bs).reshape(-1, 1).astype("int64")
inputs["top_p"] = (
np.array(
[
top_p,
]
* bs
)
.reshape(-1, 1)
.astype("float32")
)
inputs["temperature"] = (
np.array(
[
temperature,
]
* bs
)
.reshape(-1, 1)
.astype("float32")
)
inputs["seq_len_encoder"] = seq_len.astype("int32").reshape(-1, 1)
inputs["seq_len_decoder"] = (seq_len + pre_caches_length).astype("int32").reshape(-1, 1)
inputs["step_idx"] = np.array(step_idx).astype("int64").reshape(-1, 1)
inputs["tgt_ids"] = np.array(tgt_ids).astype("int64").reshape(-1, 1)
inputs["tgt_pos"] = tgt_pos.reshape(-1, 1)
inputs["max_length"] = np.array(max_length - pre_caches_length).astype("int64").reshape((-1, 1))
inputs["min_length"] = (
np.array(
[
1
if not benchmark
else max_length
- pre_caches_length, # Note(Zhengzekang): When in benchmark mode, we need to set a fixed decode length.
]
* bs
)
.astype("int64")
.reshape((-1, 1))
)
inputs["penalty_score"] = (
np.array(
[
1.0,
]
* bs
)
.astype("float32")
.reshape((-1, 1))
)
inputs["frequency_score"] = (
np.array(
[
0.0,
]
* bs
)
.astype("float32")
.reshape((-1, 1))
)
inputs["presence_score"] = (
np.array(
[
0.0,
]
* bs
)
.astype("float32")
.reshape((-1, 1))
)
inputs["stop_flags"] = (
np.array(
[
0,
]
* bs
)
.astype("bool")
.reshape((-1, 1))
)
inputs["stop_nums"] = np.array([bs]).astype("int64")
return inputs
def load_real_time_tokens():
tokens = []
files = glob.glob(os.path.join("./real_time_save.*"))
for j in range(1, len(files) + 1):
filename = "./real_time_save.temp_ids_rank_0_step_{}".format(j)
if not os.path.exists(filename):
break
fp = open(filename, "rb+")
fp.read(1)
data_list = deserialize_from_file(fp)
fp.close()
tokens.append(np.array(data_list).reshape(-1, 1))
os.system("rm -f ./real_time_save.temp_ids_rank_*")
tokens = np.concatenate(tokens, axis=1)
return tokens
def init_chat_template(
tokenizer: PretrainedTokenizer, model_name_or_path: str, chat_template_file: Optional[str] = None
):
"""init chat template for the given tokenizer.
If is None, it will not use `chat_template.json`;
If is equal with `model_name_or_path`, it will use the default loading;
If is directory, it will find the `chat_template.json` under the directory;
If is file, it will load it.
Args:
tokenizer (PretrainedTokenizer): the instance of tokenizer
model_name_or_path (str): _description_
chat_template_file (Optional[str], optional): _description_. Defaults to None.
"""
# 1. use the default chat_template file
if chat_template_file is None:
return
if str(chat_template_file).lower() == "none":
# delete the chat_template from tokenizer if not use chat_template.
# why do this: it will load the `chat_template.json` file by default
tokenizer.chat_template = None
return
# it will load the `chat_template.json` file by default, so do nothing
if chat_template_file == model_name_or_path:
if tokenizer.chat_template is None:
logger.warning(f"there is not `chat_template.json` file in the `{model_name_or_path}`")
return
if os.path.isdir(chat_template_file):
local_chat_template_file_path = os.path.join(chat_template_file, "chat_template.json")
if os.path.exists(local_chat_template_file_path):
chat_template_file = local_chat_template_file_path
else:
logger.warning(f"there is not `chat_template.json` file in the `{model_name_or_path}`")
return
if not os.path.exists(chat_template_file):
logger.warning(f"there is not `chat_template.json` file from path<`{model_name_or_path}`>")
return
logger.info(f"loading `chat_template.json` from `{chat_template_file}`")
tokenizer.init_chat_template(chat_template_file)
def get_model_max_position_embeddings(config: PretrainedConfig) -> Optional[int]:
names = [
"max_position_embeddings", # most of models
"max_sequence_length", # GLM model
"seq_length", # llama model
]
for name in names:
max_length = config.get(name, None)
if max_length is not None:
return max_length
return None
def read_res(
model_name_or_path: str,
tensor_queue: mp.Queue,
result_queue: mp.Queue,
done_event: mp.Event,
):
from paddlenlp.utils.env import USE_FAST_TOKENIZER
tokenizer = AutoTokenizer.from_pretrained(model_name_or_path, padding_side="left", use_fast=USE_FAST_TOKENIZER)
paddle.device.set_device("cpu")
paddle.disable_static()
outputs = []
output_tensor = tensor_queue.get(timeout=1)
done_event.set()
logger.info("Start read result message")
logger.info(f"Current path is {os.getcwd()}")
from paddlenlp_ops import get_output
while True:
get_output(output_tensor, 0, True)
if int(output_tensor[0, 0]) == -2: # read none
continue
bsz = int(output_tensor[1, 0])
output_numpy = output_tensor[2 : bsz + 2].numpy()
output_numpy[output_numpy == -1] = tokenizer.eos_token_id
outputs.append(output_numpy)
if int(output_tensor[0, 0]) == -1:
break
output = np.concatenate(outputs, axis=1).tolist()
seqs = tokenizer.batch_decode(output, skip_special_tokens=True, clean_up_tokenization_spaces=False)
for i, (out, seq) in enumerate(zip(output, seqs)):
result_queue.put([i, out, seq])
logger.info("Finish read result message")
def read_res_dynamic_insert(
model_name_or_path: str,
task_queue: mp.Queue,
result_queue: mp.Queue,
done_event: mp.Event,
total_request_num: int,
detokenize: bool,
):
from paddlenlp.utils.env import USE_FAST_TOKENIZER
tokenizer = AutoTokenizer.from_pretrained(model_name_or_path, padding_side="left", use_fast=USE_FAST_TOKENIZER)
paddle.device.set_device("cpu")
paddle.disable_static()
outputs = [[] for _ in range(total_request_num)]
count = 0
done_event.set()
logger.info("Start read result dynamic insert")
while count < total_request_num:
try:
task_id, token_ids = task_queue.get(block=True, timeout=None)
if task_id < 0 or task_id >= total_request_num:
logger.warning(f"Invalid task ID received: {task_id}")
continue
if len(outputs[task_id]) == 0:
output_numpy = token_ids.reshape([1, -1])
output_numpy[output_numpy == -1] = tokenizer.eos_token_id
outputs[task_id] = output_numpy
count += 1
logger.info(f"Post-processing task {task_id} ({count}/{total_request_num})")
except Exception as e:
logger.error(f"Error processing task: {str(e)}")
continue
output = np.concatenate(outputs, axis=0).tolist()
if detokenize:
seqs = tokenizer.batch_decode(output, skip_special_tokens=True, clean_up_tokenization_spaces=False)
else:
seqs = [None] * len(output)
for i, (out, seq) in enumerate(zip(output, seqs)):
result_queue.put([i, out, seq])
logger.info("Finish read result message")
def speculate_read_res(
model_name_or_path: str,
tensor_queue: mp.Queue,
result_queue: mp.Queue,
done_event: mp.Event,
):
from paddlenlp.utils.env import USE_FAST_TOKENIZER
tokenizer = AutoTokenizer.from_pretrained(model_name_or_path, padding_side="left", use_fast=USE_FAST_TOKENIZER)
paddle.device.set_device("cpu")
paddle.disable_static()
outputs = []
from paddlenlp.utils.env import MAX_DRAFT_TOKENS, SPECULATE_MAX_BSZ
for _ in range(SPECULATE_MAX_BSZ):
outputs.append([])
output_tensor = tensor_queue.get(timeout=1)
done_event.set()
logger.info("Start speculate read result message")
logger.info(f"Current path is {os.getcwd()}")
from paddlenlp_ops import speculate_get_output
while True:
speculate_get_output(output_tensor, 0, True)
if int(output_tensor[0, 0]) == -2: # read none
continue
bsz = int(output_tensor[1])
accept_num = output_tensor[2 : bsz + 2].numpy()
for bi in range(bsz):
output_numpy = output_tensor[
2
+ SPECULATE_MAX_BSZ
+ bi * MAX_DRAFT_TOKENS : 2
+ SPECULATE_MAX_BSZ
+ bi * MAX_DRAFT_TOKENS
+ int(accept_num[bi]),
0,
].numpy()
output_numpy[output_numpy == -1] = tokenizer.eos_token_id
outputs[bi].extend(output_numpy.tolist())
if int(output_tensor[0, 0]) == -1:
break
seqs = tokenizer.batch_decode(outputs, skip_special_tokens=True, clean_up_tokenization_spaces=False)
for i, (out, seq) in enumerate(zip(outputs, seqs)):
result_queue.put([i, out, seq])
logger.info("Finish read result message")
def get_rotary_position_embedding(position_ids, head_dim, rope_theta=10000.0, rope_scaling: dict = None):
"""
Pre-calculate rotary position embedding for position_ids.
Args:
position_ids: [1, S]
head_dim: D
Returns:
rot_emb: [2, 1, S, 1, D], cos + sin
"""
bsz, max_seq_len = position_ids.shape[:2]
rot_emb = paddle.zeros((2, bsz, max_seq_len, 1, head_dim), dtype="float32")
inv_freq = rope_theta ** (-paddle.arange(0, head_dim, 2, dtype="float32") / head_dim)
if rope_scaling is not None:
rope_type = rope_scaling.get("rope_type", None)
if rope_type is not None and rope_type == "llama3":
factor = rope_scaling.get("factor", 8.0)
low_freq_factor = rope_scaling.get("low_freq_factor", 1.0)
high_freq_factor = rope_scaling.get("high_freq_factor", 4.0)
original_max_position_embeddings = rope_scaling.get("original_max_position_embeddings", 8192)
low_freq_wavelen = original_max_position_embeddings / low_freq_factor
high_freq_wavelen = original_max_position_embeddings / high_freq_factor
new_freqs = []
for freq in inv_freq:
wavelen = 2 * math.pi / freq
if wavelen < high_freq_wavelen:
new_freqs.append(freq)
elif wavelen > low_freq_wavelen:
new_freqs.append(freq / factor)
else:
assert low_freq_wavelen != high_freq_wavelen
smooth = (original_max_position_embeddings / wavelen - low_freq_factor) / (
high_freq_factor - low_freq_factor
)
new_freqs.append((1 - smooth) * freq / factor + smooth * freq)
inv_freq = paddle.to_tensor(new_freqs, dtype=inv_freq.dtype)
# shape: [B, S, D/2]
freqs = paddle.einsum("ij,k->ijk", position_ids.cast("float32"), inv_freq)
# shape: [B, S, 1, D]
emb = paddle.concat([freqs, freqs], axis=-1).reshape((bsz, max_seq_len, 1, head_dim))
rot_emb[0] = paddle.cos(emb)
rot_emb[1] = paddle.sin(emb)
return rot_emb
def init_dist_env():
"""
Initialize the distributed environment and obtain tensor parallel degree and rank.
Returns:
tuple: A tuple containing tensor parallel rank and degree.
"""
world_size = paddle.distributed.get_world_size() # Get the total number of distributed nodes
if world_size > 1:
is_fleet_init = True
try:
# Try to get the hybrid communicate group to check if Fleet has been initialized
hcg = fleet.get_hybrid_communicate_group()
except AttributeError:
is_fleet_init = False # Fleet has not been initialized
if is_fleet_init:
# If Fleet is already initialized, get tensor parallel degree and rank
tensor_parallel_degree = hcg.get_model_parallel_world_size()
tensor_parallel_rank = hcg.get_model_parallel_rank()
else:
# If Fleet is not initialized, set up the distributed strategy and initialize Fleet
strategy = fleet.DistributedStrategy()
strategy.hybrid_configs = {
"dp_degree": 1, # Data parallelism degree
"mp_degree": world_size, # Model parallelism degree (to be determined or set)
"pp_degree": 1, # Pipeline parallelism degree
"sharding_degree": 1, # Sharding parallelism degree
}
fleet.init(is_collective=True, strategy=strategy) # Initialize Fleet
hcg = fleet.get_hybrid_communicate_group() # Get the hybrid communicate group after initialization
# Get tensor parallel degree and rank after Fleet initialization
tensor_parallel_degree = hcg.get_model_parallel_world_size()
tensor_parallel_rank = hcg.get_model_parallel_rank()
else:
# If not in a distributed environment, set tensor parallel degree and rank to 1 and 0 respectively
tensor_parallel_degree = 1
tensor_parallel_rank = 0
return tensor_parallel_rank, tensor_parallel_degree
def get_eos_token_id(
tokenizer: PretrainedTokenizer, generation_config: Optional[GenerationConfig] = None
) -> List[List[int]]:
"""get eos_token_id from generation_config or tokenizer
Returns:
List[int]: eos_token_id to stop the generation
"""
eos_token_ids = []
if tokenizer.eos_token_id is not None:
eos_token_ids.append(tokenizer.eos_token_id)
if generation_config is not None and generation_config.eos_token_id is not None:
if isinstance(generation_config.eos_token_id, int):
eos_token_ids.append(generation_config.eos_token_id)
else:
eos_token_ids.extend(generation_config.eos_token_id)
eos_token_ids_dict = {str(item): item for item in eos_token_ids}
return list(eos_token_ids_dict.values())
def set_triton_cache(model_name_or_path, mode):
"""
Set triton cache.
"""
valid_modes = {"export", "static", "dynamic"}
if mode not in valid_modes:
raise ValueError(f"Invalid mode: {mode}. Valid modes are: {valid_modes}")
mp_id = paddle.distributed.get_rank()
triton_dir = f"triton_ops_rank_{mp_id}"
triton_kernel_cache_dir = f"{model_name_or_path}/{triton_dir}"
if mode == "export":
os.environ["TRITON_KERNEL_CACHE_DIR"] = triton_kernel_cache_dir
if os.path.exists(triton_kernel_cache_dir):
# del old triton_ops
shutil.rmtree(triton_kernel_cache_dir)
elif mode == "static":
os.environ["TRITON_KERNEL_CACHE_DIR"] = triton_kernel_cache_dir
for root, dirs, files in os.walk(triton_kernel_cache_dir):
for file in files:
if file.endswith("_package.so"):
so_full_path = os.path.join(root, file)
paddle.utils.cpp_extension.load_op_meta_info_and_register_op(so_full_path)
else:
os.environ["TRITON_KERNEL_CACHE_DIR"] = f"/root/.paddlenlp/{triton_dir}"