Views
No views yet
| Dataset | GPTQ-4bit | FP16 |
|---|---|---|
| mmlu | 65.20 | 65.9 |
| cmmlu | 69.23 | 69.9 |
| arc_challenge | 45.48 | 47.9(0) |
1
2# Copyright 2024-2025 ModelCloud.ai
3# Copyright 2024-2025 qubitium@modelcloud.ai
4# Contact: qubitium@modelcloud.ai, x.com/qubitium
5#
6# Licensed under the Apache License, Version 2.0 (the "License");
7# you may not use this file except in compliance with the License.
8# You may obtain a copy of the License at
9#
10# http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS,
14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15# See the License for the specific language governing permissions and
16# limitations under the License.
17
18import torch
19from datasets import load_dataset
20from gptqmodel import GPTQModel, QuantizeConfig, BACKEND
21from gptqmodel.models.base import BaseGPTQModel
22from transformers import AutoTokenizer, AutoModelForCausalLM, AutoModel
23from gptqmodel.models.auto import MODEL_MAP
24import torch.nn.functional as F
25import numpy as np
26
27
28
29
30
31pretrained_model_id = '/home/chentianqi/model/GSAI-ML/LLaDA-8B-Base' # "TinyLlama/TinyLlama-1.1B-Chat-v1.0"
32quantized_model_id = "FunAGI/LLaDA-8B-Base-gptqmodel-4bit"
33
34
35
36class LladaGPTQ(BaseGPTQModel):
37 # Non-repeating layers at the root level: same level as `layers_node`
38 # Excluding `layers_node`.
39 base_modules = ["model.transformer.wte", "model.transformer.ln_f"]
40 pre_lm_head_norm_module = "model.transformer.ln_f"
41 lm_head = "model.transformer.ff_out"
42 # Below describes all the repeating layers in this transformer model
43 # `model.layers` is a node/module that hold all the repeating layers. The parent node for all n-layers.
44 layers_node = "model.transformer.blocks"
45 # Each repeating layer in `model.layers` is of type `LlamaDecoderLayer`
46 layer_type = "LLaDALlamaBlock"
47 # Inside each `LlamaDecoderLayer` layer are many internal modules
48 # List them in the order executed in model forward() code
49 # Many models have same execution order of: attention (q_k_v) projection, attention (output) projection, mlp (n) projections
50 layer_modules = [
51 ["attn_out", "k_proj", "v_proj", "q_proj"],
52 ["ff_proj", "up_proj"],
53 ["ff_out"],
54 ]
55MODEL_MAP ["llada"] = LladaGPTQ
56
57# os.makedirs(quantized_model_dir, exist_ok=True)
58def get_wikitext2(tokenizer, nsamples, seqlen):
59 traindata = load_dataset("wikitext", "wikitext-2-raw-v1", split="train").filter(
60 lambda x: len(x["text"]) >= seqlen)
61
62 return [tokenizer(example["text"]) for example in traindata.select(range(nsamples))]
63
64
65@torch.no_grad()
66def calculate_avg_ppl(model, tokenizer):
67 from gptqmodel.utils import Perplexity
68
69 ppl = Perplexity(
70 model=model,
71 tokenizer=tokenizer,
72 dataset_path="wikitext",
73 dataset_name="wikitext-2-raw-v1",
74 split="train",
75 text_column="text",
76 )
77
78 all = ppl.calculate(n_ctx=512, n_batch=512)
79
80 # average ppl
81 avg = sum(all) / len(all)
82
83 return avg
84
85dynamic = {
86
87 }
88
89def add_gumbel_noise(logits, temperature):
90 '''
91 The Gumbel max is a method for sampling categorical distributions.
92 According to arXiv:2409.02908, for MDM, low-precision Gumbel Max improves perplexity score but reduces generation quality.
93 Thus, we use float64.
94 '''
95 logits = logits.to(torch.float64)
96 noise = torch.rand_like(logits, dtype=torch.float64)
97 gumbel_noise = (- torch.log(noise)) ** temperature
98 return logits.exp() / gumbel_noise
99
100
101def get_num_transfer_tokens(mask_index, steps):
102 '''
103 In the reverse process, the interval [0, 1] is uniformly discretized into steps intervals.
104 Furthermore, because LLaDA employs a linear noise schedule (as defined in Eq. (8)),
105 the expected number of tokens transitioned at each step should be consistent.
106
107 This function is designed to precompute the number of tokens that need to be transitioned at each step.
108 '''
109 mask_num = mask_index.sum(dim=1, keepdim=True) #
110
111 base = mask_num // steps
112 remainder = mask_num % steps
113
114 num_transfer_tokens = torch.zeros(mask_num.size(0), steps, device=mask_index.device, dtype=torch.int64) + base
115
116 for i in range(mask_num.size(0)):
117 num_transfer_tokens[i, :remainder[i]] += 1
118
119 return num_transfer_tokens
120
121def forward_process(batch, prompt_index, mask_id):
122 b, l = batch.shape
123
124 target_len = (l - prompt_index.sum()).item()
125 k = torch.randint(1, target_len + 1, (), device=batch.device)
126
127 x = torch.round(torch.linspace(float(k), k + (b - 1) * (target_len / b), steps=b, device=batch.device)).long()
128 x = ((x - 1) % target_len) + 1
129 assert x.min() >= 1 and x.max() <= target_len
130
131 indices = torch.arange(target_len, device=batch.device).repeat(b, 1)
132 is_mask = indices < x.unsqueeze(1)
133 for i in range(b):
134 is_mask[i] = is_mask[i][torch.randperm(target_len)]
135
136 is_mask = torch.cat((torch.zeros(b, prompt_index.sum(), dtype=torch.bool, device=batch.device), is_mask), dim=1)
137 noisy_batch = torch.where(is_mask, mask_id, batch)
138
139 # Return the masked batch and the mask ratio
140 return noisy_batch, (x / target_len).unsqueeze(1).repeat(1, l)
141
142
143def get_logits(model, batch, prompt_index, cfg_scale, mask_id):
144 if cfg_scale > 0.:
145 assert len(prompt_index) == batch.shape[1]
146 prompt_index = prompt_index.unsqueeze(0).repeat(batch.shape[0], 1)
147 un_batch = batch.clone()
148 un_batch[prompt_index] = mask_id
149 batch = torch.cat([batch, un_batch])
150
151 input = batch
152 logits = model(input).logits
153
154 if cfg_scale > 0.:
155 logits, un_logits = torch.chunk(logits, 2, dim=0)
156 logits = un_logits + (cfg_scale + 1) * (logits - un_logits)
157 return logits
158
159
160
161@ torch.no_grad()
162def get_log_likelihood(model, prompt, answer, mc_num=128, batch_size=32, cfg_scale=0., mask_id=126336):
163 '''
164 Args:
165 model: Mask predictor.
166 prompt: A tensor of shape (l1).
167 answer: A tensor of shape (l2).
168 mc_num: Monte Carlo estimation times.
169 As detailed in Appendix B.5. Since MMLU, CMMLU, and C-EVAL only require the likelihood of a single token, a
170 single Monte Carlo estimate is sufficient for these benchmarks. For all other benchmarks, we find that 128
171 Monte Carlo samples are adequate to produce stable results.
172 batch_size: Mini batch size.
173 cfg_scale: Unsupervised classifier-free guidance scale.
174 mask_id: The toke id of [MASK] is 126336.
175 '''
176
177 seq = torch.concatenate([prompt, answer])[None, :]
178 seq = seq.repeat((batch_size, 1)).to(model.device)
179 prompt_index = torch.arange(seq.shape[1], device=model.device) < len(prompt)
180
181 loss_ = []
182 for _ in range(mc_num // batch_size):
183
184 perturbed_seq, p_mask = forward_process(seq, prompt_index, mask_id)
185 mask_index = perturbed_seq == mask_id
186
187 logits = get_logits(model, perturbed_seq, prompt_index, cfg_scale, mask_id)
188
189 loss = F.cross_entropy(logits[mask_index], seq[mask_index], reduction='none') / p_mask[mask_index]
190 loss = loss.sum() / batch_size
191
192 loss_.append(loss.item())
193
194 return - sum(loss_) / len(loss_)
195
196
197
198
199
200
201
202
203@ torch.no_grad()
204def generate(model, prompt, steps=128, gen_length=128, block_length=128, temperature=0.,
205 cfg_scale=0., remasking='low_confidence', mask_id=126336):
206 '''
207 Args:
208 model: Mask predictor.
209 prompt: A tensor of shape (1, l).
210 steps: Sampling steps, less than or equal to gen_length.
211 gen_length: Generated answer length.
212 block_length: Block length, less than or equal to gen_length. If less than gen_length, it means using semi_autoregressive remasking.
213 temperature: Categorical distribution sampling temperature.
214 cfg_scale: Unsupervised classifier-free guidance scale.
215 remasking: Remasking strategy. 'low_confidence' or 'random'.
216 mask_id: The toke id of [MASK] is 126336.
217 '''
218 x = torch.full((1, prompt.shape[1] + gen_length), mask_id, dtype=torch.long).to(model.device)
219 x[:, :prompt.shape[1]] = prompt.clone()
220
221 prompt_index = (x != mask_id)
222
223 assert gen_length % block_length == 0
224 num_blocks = gen_length // block_length
225
226 assert steps % num_blocks == 0
227 steps = steps // num_blocks
228
229 for num_block in range(num_blocks):
230 block_mask_index = (x[:, prompt.shape[1] + num_block * block_length: prompt.shape[1] + (num_block + 1) * block_length:] == mask_id)
231 num_transfer_tokens = get_num_transfer_tokens(block_mask_index, steps)
232 for i in range(steps):
233
234 mask_index = (x == mask_id)
235 if cfg_scale > 0.:
236 un_x = x.clone()
237 un_x[prompt_index] = mask_id
238 x_ = torch.cat([x, un_x], dim=0)
239 logits = model(x_).logits
240 logits, un_logits = torch.chunk(logits, 2, dim=0)
241 logits = un_logits + (cfg_scale + 1) * (logits - un_logits)
242 else:
243 logits = model(x).logits
244
245 logits_with_noise = add_gumbel_noise(logits, temperature=temperature)
246 x0 = torch.argmax(logits_with_noise, dim=-1) # b, l
247
248 if remasking == 'low_confidence':
249 p = F.softmax(logits.to(torch.float64), dim=-1)
250 x0_p = torch.squeeze(
251 torch.gather(p, dim=-1, index=torch.unsqueeze(x0, -1)), -1) # b, l
252 elif remasking == 'random':
253 x0_p = torch.rand((x0.shape[0], x0.shape[1]), device=x0.device)
254 else:
255 raise NotImplementedError(remasking)
256
257 x0_p[:, prompt.shape[1] + (num_block + 1) * block_length:] = -np.inf
258
259 x0 = torch.where(mask_index, x0, x)
260 confidence = torch.where(mask_index, x0_p, -np.inf)
261
262 transfer_index = torch.zeros_like(x0, dtype=torch.bool, device=x0.device)
263 for j in range(confidence.shape[0]):
264 _, select_index = torch.topk(confidence[j], k=num_transfer_tokens[j, i])
265 transfer_index[j, select_index] = True
266 x[transfer_index] = x0[transfer_index]
267
268 return x
269
270def main():
271 tokenizer = AutoTokenizer.from_pretrained(pretrained_model_id, use_fast=False)
272
273 traindataset = get_wikitext2(tokenizer, nsamples=128, seqlen=1024)
274
275 quantize_config = QuantizeConfig(
276 dynamic=dynamic,
277 bits=8, # quantize model to 4-bit
278 group_size=128, # it is recommended to set the value to 128,
279 desc_act = True,
280 sym=False
281 )
282 device = "cuda:0" if torch.cuda.is_available() else "cpu"
283 prompt = "Question: Lily can run 12 kilometers per hour for 4 hours. After that, she runs 6 kilometers per hour. How many kilometers can she run in 8 hours? The answer: "
284
285 # # Add special tokens for the Instruct model. The Base model does not require the following two lines.
286 # m = [{"role": "user", "content": prompt}, ]
287 # prompt = tokenizer.apply_chat_template(m, add_generation_prompt=True, tokenize=False)
288
289 input_ids = tokenizer(prompt)['input_ids']
290 input_ids = torch.tensor(input_ids).to(device).unsqueeze(0)
291
292
293
294 device = "cuda:0" if torch.cuda.is_available() else "cpu"
295 model = GPTQModel.load(quantized_model_id, device=device , trust_remote_code=True )
296
297 steps=128
298 out = generate(model, input_ids, steps=steps , gen_length=128, block_length=32, temperature=0., cfg_scale=0., remasking='low_confidence')
299 print("*"*30+ f"GPTQ-4bit Steps {steps}"+ "*"*30)
300 print(input_ids.shape)
301 print( tokenizer.batch_decode(out[:, input_ids.shape[1]:], skip_special_tokens=True)[0])
302 del model
303
304 model =AutoModel.from_pretrained(pretrained_model_id, trust_remote_code=True ).cuda()
305
306 out = generate(model, input_ids, steps=steps , gen_length=128, block_length=32, temperature=0., cfg_scale=0., remasking='low_confidence')
307 print("*"*30+ f"FP16 Steps {steps}"+ "*"*30)
308 print(input_ids.shape)
309 print( tokenizer.batch_decode(out[:, input_ids.shape[1]:], skip_special_tokens=True)[0])
310
311
312if __name__ == "__main__":
313 import logging
314
315 logging.basicConfig(
316 format="%(asctime)s %(levelname)s [%(name)s] %(message)s",
317 level=logging.INFO,
318 datefmt="%Y-%m-%d %H:%M:%S",
319 )
320
321 main()
322
323
324