Ouzhang's picture
Add files using upload-large-folder tool
3a464db verified
Raw
History Blame Contribute Delete
11.5 kB
# Copyright 2025 NVIDIA CORPORATION & AFFILIATES
#
# 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.
#
# SPDX-License-Identifier: Apache-2.0
# Modified from LLaDA repos: https://github.com/ML-GSAI/LLaDA
'''
This file is inspired by the code from https://github.com/ML-GSAI/SMDM
'''
import accelerate
import torch
import re
from pathlib import Path
import random
import numpy as np
import torch.nn.functional as F
from datasets import Dataset
from lm_eval.__main__ import cli_evaluate
from lm_eval.api.model import LM
from lm_eval.api.registry import register_model
from tqdm import tqdm
import os
from transformers import AutoTokenizer, AutoModelForCausalLM, AutoConfig
import json
import time
import types
import generation_functions
def set_seed(seed):
torch.manual_seed(seed)
random.seed(seed)
np.random.seed(seed)
torch.backends.cudnn.deterministic = True
torch.backends.cudnn.benchmark = False
@register_model("fast_dllm_v2")
class Fast_dLLM_v2EvalHarness(LM):
def __init__(
self,
model_path='Efficient-Large-Model/Fast_dLLM_v2_7B',
device="cuda",
show_speed=False,
max_new_tokens=2048,
batch_size=32,
mask_id=151665,
use_block_cache=False,
small_block_size=8,
bd_size=32,
threshold=0.9,
**kwargs,
):
super().__init__()
accelerator = accelerate.Accelerator()
if accelerator.num_processes > 1:
self.accelerator = accelerator
else:
self.accelerator = None
model_kwargs = {}
if self.accelerator is not None:
model_kwargs.update({'device_map': {'': f'{self.accelerator.device}'}})
self.model = AutoModelForCausalLM.from_pretrained(
model_path,
trust_remote_code=True,
torch_dtype=torch.bfloat16,
**model_kwargs
)
self.model.eval()
self.model.mdm_sample = types.MethodType(generation_functions.Fast_dLLM_QwenForCausalLM.batch_sample, self.model)
self.device = torch.device(device)
if self.accelerator is not None:
self.model = self.accelerator.prepare(self.model)
self.device = torch.device(f'{self.accelerator.device}')
self._rank = self.accelerator.local_process_index
self._world_size = self.accelerator.num_processes
else:
self.model = self.model.to(device)
self._rank = 0
self._world_size = 1
self.tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True)
self.show_speed = show_speed
self.max_new_tokens = max_new_tokens
self.batch_size = int(batch_size)
self.mask_id = mask_id
self.model_path = model_path
self.use_block_cache = use_block_cache
self.small_block_size = small_block_size
self.threshold = threshold
self.bd_size = bd_size
@property
def rank(self):
return self._rank
@property
def world_size(self):
return self._world_size
@property
def tokenizer_name(self):
return self.model_path
def apply_chat_template(self, chat_history, add_generation_prompt=True):
return self.tokenizer.apply_chat_template(chat_history, add_generation_prompt=add_generation_prompt, tokenize=False)
def loglikelihood_rolling(self, requests):
raise NotImplementedError
def _encode_pair(self, context, continuation):
whole_enc = self.tokenizer(context + continuation)["input_ids"]
context_enc = self.tokenizer(context)["input_ids"]
context_enc_len = len(context_enc)
continuation_enc = whole_enc[context_enc_len:]
return context_enc, continuation_enc
def _forward_process(self, batch, prompt_index):
b, l = batch.shape
batch[:, prompt_index.sum()] = self.mask_id
batch = torch.cat([batch.to(self.device), torch.full((b, self.bd_size-batch.shape[1]%self.bd_size), self.mask_id, dtype=torch.long, device=self.device)], dim=1)
if batch.shape[1] > l:
batch[:, l] = self.tokenizer.eos_token_id
return batch
@torch.no_grad()
def get_logits(self, batch):
logits = self.model(batch).logits
logits = torch.cat([logits[:, :1, :], logits[:, :-1, :]], dim=1)
return logits[:, :batch.shape[1]]
@torch.no_grad()
def get_loglikelihood(self, prefix, target):
seq = torch.concatenate([prefix, target])[None, :]
prompt_index = torch.arange(seq.shape[1], device=self.device) < len(prefix)
loss_acc = []
perturbed_seq = self._forward_process(seq.clone(), prompt_index)
mask_indices = perturbed_seq == self.mask_id
logits = self.get_logits(perturbed_seq)
seq = torch.cat([seq.to(self.device), torch.full((seq.shape[0], self.bd_size-seq.shape[1]%self.bd_size), -100, dtype=torch.long, device=self.device)], dim=1)
loss = F.cross_entropy(logits[mask_indices], seq[mask_indices], reduction='none')
loss = loss.sum()
loss_acc.append(loss.item())
return - sum(loss_acc) / len(loss_acc)
def loglikelihood(self, requests):
def _tokenize(e):
prefix, target = self._encode_pair(e["prefix"], e["target"])
return {
"prefix_text": e["prefix"],
"target_text": e["target"],
"prefix": prefix,
"target": target,
}
ds = []
ds = [{"prefix": req.args[0], "target": req.args[1]} for req in requests]
ds = Dataset.from_list(ds)
ds = ds.map(_tokenize)
ds = ds.with_format("torch")
prompt_len = [len(x["prefix"]) + len(x["target"]) for x in ds]
assert max(prompt_len) <= 4096
out = []
with torch.no_grad():
for elem in tqdm(ds, desc="Computing likelihood..."):
prefix = elem["prefix"]
target = elem["target"]
ll = self.get_loglikelihood(prefix, target)
out.append((ll, 0.0))
torch.cuda.empty_cache()
return out
def generate_until(self, requests):
output = [None] * len(requests) # pre-allocate output list
num_tokens = 0
start_time = time.time()
requests_with_indices = [(i, req) for i, req in enumerate(requests)]
requests_with_indices.sort(key=lambda x: len(x[1].args[0]))
batched_requests = []
current_batch = []
for i, req in requests_with_indices:
current_batch.append((i, req))
if len(current_batch) == self.batch_size:
batched_requests.append(current_batch)
current_batch = []
if current_batch:
batched_requests.append(current_batch)
for _, batch in enumerate(tqdm(batched_requests, desc="Generating...")):
batched_input_ids = []
max_len = 0
min_len = 1e9
seq_len = []
for orig_idx, req in batch:
question = req.args[0]
if req.task_name.startswith('minerva_math'):
question = question.replace("Solution:", "Please reason step by step, and put your final answer within \\boxed{{}}.")
elif req.task_name.startswith('gsm8k'):
question = question.replace("Answer:", "Please reason step by step, and put your final answer within \\boxed{{}}.")
model_inputs = self.tokenizer([question], return_tensors="pt").to(self.device)
batched_input_ids.append(model_inputs["input_ids"])
max_len = max(max_len, model_inputs["input_ids"].shape[1])
min_len = min(min_len, model_inputs["input_ids"].shape[1])
seq_len.append(model_inputs["input_ids"].shape[1])
# pad batched_input_ids to the same length
batched_input_ids = [torch.cat([input_ids, torch.full((1, max_len - input_ids.shape[1]), self.mask_id, dtype=torch.long, device=self.device)], dim=1) for input_ids in batched_input_ids]
batched_input_ids = torch.cat(batched_input_ids, dim=0)
batched_input_ids = batched_input_ids.to(self.device)
with torch.no_grad():
if self.accelerator is not None:
generated_ids = self.accelerator.unwrap_model(self.model).mdm_sample(
batched_input_ids,
tokenizer=self.tokenizer,
block_size=self.bd_size,
small_block_size=self.small_block_size,
max_new_tokens=self.max_new_tokens,
mask_id=self.mask_id,
min_len=min_len,
seq_len=torch.tensor(seq_len, device=self.device),
use_block_cache=self.use_block_cache,
threshold=self.threshold,
)
else:
generated_ids = self.model.mdm_sample(
batched_input_ids,
tokenizer=self.tokenizer,
block_size=self.bd_size,
small_block_size=self.small_block_size,
max_new_tokens=self.max_new_tokens,
mask_id=self.mask_id,
min_len=min_len,
seq_len=torch.tensor(seq_len, device=self.device),
use_block_cache=self.use_block_cache,
threshold=self.threshold,
)
# extract new generated tokens, and keep original index order
for batch_pos, (orig_idx, req) in enumerate(batch):
generated_answer = self.tokenizer.decode(
generated_ids[batch_pos][seq_len[batch_pos]:],
skip_special_tokens=True
)
# count token number
if self.show_speed:
num_tokens += (generated_ids[batch_pos][seq_len[batch_pos]:] != self.mask_id).sum()
# put result in the correct original index position
output[orig_idx] = generated_answer
print('=' * 20)
print('question: ', req.args[0])
print('answer: ', generated_answer)
print('=' * 20, end='\n\n')
end_time = time.time()
if self.show_speed:
print(f"Total number of tokens generated: {num_tokens}")
print(f"Total time taken: {end_time - start_time} seconds")
print(f"Tokens per second: {num_tokens / (end_time - start_time)}")
return output
if __name__ == "__main__":
cli_evaluate()