Add files using upload-large-folder tool
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- lm-quant-toolkit/.deps/CLIP_benchmark/clip_benchmark/datasets/cupl_prompts.json +0 -0
- lm-quant-toolkit/.deps/CLIP_benchmark/probe_benchmark/scaling_experiment_data2.json +0 -0
- lm-quant-toolkit/.deps/hqq/examples/hf/llama2_chat_hf_hub_example.py +65 -0
- lm-quant-toolkit/.deps/hqq/examples/hf/whisper.py +98 -0
- lm-quant-toolkit/.deps/hqq/examples/llama2_benchmark/.gitignore +3 -0
- lm-quant-toolkit/.deps/hqq/examples/llama2_benchmark/.pdbrc +10 -0
- lm-quant-toolkit/.deps/hqq/examples/llama2_benchmark/adapter/autoawq.py +32 -0
- lm-quant-toolkit/.deps/hqq/examples/llama2_benchmark/adapter/awq.py +94 -0
- lm-quant-toolkit/.deps/hqq/examples/llama2_benchmark/data/fnorm-Llama-2-13b-hf.csv +0 -0
- lm-quant-toolkit/.deps/hqq/examples/llama2_benchmark/data/fnorm-Llama-2-70b-hf.csv +0 -0
- lm-quant-toolkit/.deps/hqq/examples/llama2_benchmark/data/fnorm-Llama-2-7b-hf.csv +0 -0
- lm-quant-toolkit/.deps/hqq/examples/llama2_benchmark/data/fnorm-Meta-Llama-3.1-405B-Instruct.csv +0 -0
- lm-quant-toolkit/.deps/hqq/examples/llama2_benchmark/debug-bench.sh +3 -0
- lm-quant-toolkit/.deps/hqq/examples/llama2_benchmark/eval_model.py +115 -0
- lm-quant-toolkit/.deps/hqq/examples/llama2_benchmark/fp16_llama2_demo.py +26 -0
- lm-quant-toolkit/.deps/hqq/examples/llama2_benchmark/quant_llama2_awq_demo.py +40 -0
- lm-quant-toolkit/.deps/hqq/examples/llama2_benchmark/run-bench.sh +8 -0
- lm-quant-toolkit/.deps/hqq/examples/lora/train_hqq_lora_example.py +220 -0
- lm-quant-toolkit/.deps/hqq/examples/open_clip/.pdbrc +4 -0
- lm-quant-toolkit/.deps/hqq/examples/open_clip/vit_clip_example.py +158 -0
- lm-quant-toolkit/.deps/hqq/examples/timm/vit_clip_example.py +73 -0
- lm-quant-toolkit/.deps/hqq/examples/vllm/llama2_example.py +25 -0
- lm-quant-toolkit/.deps/hqq/hqq.egg-info/PKG-INFO +23 -0
- lm-quant-toolkit/.deps/hqq/hqq.egg-info/SOURCES.txt +53 -0
- lm-quant-toolkit/.deps/hqq/hqq.egg-info/dependency_links.txt +1 -0
- lm-quant-toolkit/.deps/hqq/hqq.egg-info/requires.txt +7 -0
- lm-quant-toolkit/.deps/hqq/hqq.egg-info/top_level.txt +1 -0
- lm-quant-toolkit/.deps/hqq/hqq/__init__.py +3 -0
- lm-quant-toolkit/.deps/hqq/hqq/__pycache__/__init__.cpython-311.pyc +0 -0
- lm-quant-toolkit/.deps/hqq/hqq/backends/__init__.py +0 -0
- lm-quant-toolkit/.deps/hqq/hqq/backends/marlin.py +118 -0
- lm-quant-toolkit/.deps/hqq/hqq/backends/torchao.py +388 -0
- lm-quant-toolkit/.deps/hqq/hqq/core/__init__.py +0 -0
- lm-quant-toolkit/.deps/hqq/hqq/core/__pycache__/__init__.cpython-311.pyc +0 -0
- lm-quant-toolkit/.deps/hqq/hqq/core/__pycache__/bitpack.cpython-311.pyc +0 -0
- lm-quant-toolkit/.deps/hqq/hqq/core/__pycache__/optimize.cpython-311.pyc +0 -0
- lm-quant-toolkit/.deps/hqq/hqq/core/__pycache__/quantize.cpython-311.pyc +0 -0
- lm-quant-toolkit/.deps/hqq/hqq/core/__pycache__/utils.cpython-311.pyc +0 -0
- lm-quant-toolkit/.deps/hqq/hqq/core/bitpack.py +144 -0
- lm-quant-toolkit/.deps/hqq/hqq/core/optimize.py +476 -0
- lm-quant-toolkit/.deps/hqq/hqq/core/peft.py +545 -0
- lm-quant-toolkit/.deps/hqq/hqq/core/quantize.py +948 -0
- lm-quant-toolkit/.deps/hqq/hqq/core/utils.py +31 -0
- lm-quant-toolkit/.deps/hqq/hqq/engine/__init__.py +0 -0
- lm-quant-toolkit/.deps/hqq/hqq/engine/base.py +105 -0
- lm-quant-toolkit/.deps/hqq/hqq/engine/hf.py +73 -0
- lm-quant-toolkit/.deps/hqq/hqq/engine/open_clip.py +112 -0
- lm-quant-toolkit/.deps/hqq/hqq/engine/timm.py +67 -0
- lm-quant-toolkit/.deps/hqq/hqq/engine/vllm.py +149 -0
- lm-quant-toolkit/.deps/hqq/hqq/kernels/__init__.py +0 -0
lm-quant-toolkit/.deps/CLIP_benchmark/clip_benchmark/datasets/cupl_prompts.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
lm-quant-toolkit/.deps/CLIP_benchmark/probe_benchmark/scaling_experiment_data2.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
lm-quant-toolkit/.deps/hqq/examples/hf/llama2_chat_hf_hub_example.py
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
model_id = 'mobiuslabsgmbh/Llama-2-7b-chat-hf-4bit_g64-HQQ'
|
| 2 |
+
#model_id = 'mobiuslabsgmbh/Llama-2-13b-chat-hf-4bit_g64-HQQ'
|
| 3 |
+
#model_id = 'mobiuslabsgmbh/Llama-2-70b-chat-hf-2bit_g16_s128-HQQ'
|
| 4 |
+
|
| 5 |
+
from hqq.engine.hf import HQQModelForCausalLM, AutoTokenizer
|
| 6 |
+
tokenizer = AutoTokenizer.from_pretrained(model_id)
|
| 7 |
+
model = HQQModelForCausalLM.from_quantized(model_id)
|
| 8 |
+
|
| 9 |
+
##########################################################################################################
|
| 10 |
+
import transformers
|
| 11 |
+
from threading import Thread
|
| 12 |
+
|
| 13 |
+
from sys import stdout
|
| 14 |
+
def print_flush(data):
|
| 15 |
+
stdout.write("\r" + data)
|
| 16 |
+
stdout.flush()
|
| 17 |
+
|
| 18 |
+
#Adapted from https://huggingface.co/spaces/huggingface-projects/llama-2-7b-chat/blob/main/app.py
|
| 19 |
+
def process_conversation(chat):
|
| 20 |
+
system_prompt = chat['system_prompt']
|
| 21 |
+
chat_history = chat['chat_history']
|
| 22 |
+
message = chat['message']
|
| 23 |
+
|
| 24 |
+
conversation = []
|
| 25 |
+
if system_prompt:
|
| 26 |
+
conversation.append({"role": "system", "content": system_prompt})
|
| 27 |
+
for user, assistant in chat_history:
|
| 28 |
+
conversation.extend([{"role": "user", "content": user}, {"role": "assistant", "content": assistant}])
|
| 29 |
+
conversation.append({"role": "user", "content": message})
|
| 30 |
+
|
| 31 |
+
return tokenizer.apply_chat_template(conversation, return_tensors="pt").to('cuda')
|
| 32 |
+
|
| 33 |
+
def chat_processor(chat, max_new_tokens=100, do_sample=True):
|
| 34 |
+
tokenizer.use_default_system_prompt = False
|
| 35 |
+
streamer = transformers.TextIteratorStreamer(tokenizer, timeout=10.0, skip_prompt=True, skip_special_tokens=True)
|
| 36 |
+
|
| 37 |
+
generate_params = dict(
|
| 38 |
+
{"input_ids": process_conversation(chat)},
|
| 39 |
+
streamer=streamer,
|
| 40 |
+
max_new_tokens=max_new_tokens,
|
| 41 |
+
do_sample=do_sample,
|
| 42 |
+
top_p=0.90,
|
| 43 |
+
top_k=50,
|
| 44 |
+
temperature= 0.6,
|
| 45 |
+
num_beams=1,
|
| 46 |
+
repetition_penalty=1.2,
|
| 47 |
+
)
|
| 48 |
+
|
| 49 |
+
t = Thread(target=model.generate, kwargs=generate_params)
|
| 50 |
+
t.start()
|
| 51 |
+
|
| 52 |
+
outputs = []
|
| 53 |
+
for text in streamer:
|
| 54 |
+
outputs.append(text)
|
| 55 |
+
print_flush("".join(outputs))
|
| 56 |
+
|
| 57 |
+
return outputs
|
| 58 |
+
|
| 59 |
+
###################################################################################################
|
| 60 |
+
|
| 61 |
+
outputs = chat_processor({'system_prompt':"You are a helpful assistant.",
|
| 62 |
+
'chat_history':[],
|
| 63 |
+
'message':"How can I build a car?"
|
| 64 |
+
},
|
| 65 |
+
max_new_tokens=1000, do_sample=False)
|
lm-quant-toolkit/.deps/hqq/examples/hf/whisper.py
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Tested with torch nightly, 4090
|
| 2 |
+
# pip uninstall torch -y; pip install --pre torch torchvision torchaudio --index-url https://download.pytorch.org/whl/nightly/cu121
|
| 3 |
+
##############################################################################################
|
| 4 |
+
|
| 5 |
+
import torch
|
| 6 |
+
from transformers import AutoModelForSpeechSeq2Seq, AutoProcessor, pipeline
|
| 7 |
+
|
| 8 |
+
model_id = "openai/whisper-medium"
|
| 9 |
+
#model_id = "distil-whisper/distil-large-v3"
|
| 10 |
+
|
| 11 |
+
compute_dtype = torch.bfloat16
|
| 12 |
+
device = "cuda:0"
|
| 13 |
+
|
| 14 |
+
model = AutoModelForSpeechSeq2Seq.from_pretrained(model_id, torch_dtype=compute_dtype)
|
| 15 |
+
processor = AutoProcessor.from_pretrained(model_id)
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
##############################################################################
|
| 19 |
+
#No quantize
|
| 20 |
+
#model = model.to(device)
|
| 21 |
+
|
| 22 |
+
##############################################################################
|
| 23 |
+
#Quantize
|
| 24 |
+
from hqq.models.hf.base import AutoHQQHFModel
|
| 25 |
+
from hqq.core.quantize import *
|
| 26 |
+
|
| 27 |
+
quant_config = BaseQuantizeConfig(nbits=4, group_size=64, quant_scale=False, quant_zero=False, axis=1)
|
| 28 |
+
HQQLinear.set_backend(HQQBackend.PYTORCH)
|
| 29 |
+
|
| 30 |
+
AutoHQQHFModel.quantize_model(model.model.encoder, quant_config=quant_config, compute_dtype=compute_dtype, device=device)
|
| 31 |
+
AutoHQQHFModel.quantize_model(model.model.decoder, quant_config=quant_config, compute_dtype=compute_dtype, device=device)
|
| 32 |
+
|
| 33 |
+
from hqq.utils.patching import prepare_for_inference
|
| 34 |
+
prepare_for_inference(model.model.encoder)
|
| 35 |
+
prepare_for_inference(model.model.decoder, backend="torchao_int4")
|
| 36 |
+
|
| 37 |
+
model.model.encoder.forward = torch.compile(model.model.encoder.forward, mode="reduce-overhead", fullgraph=True)
|
| 38 |
+
model.model.decoder.forward = torch.compile(model.model.decoder.forward, mode="reduce-overhead", fullgraph=True)
|
| 39 |
+
# ##############################################################################
|
| 40 |
+
|
| 41 |
+
import time
|
| 42 |
+
import numpy as np
|
| 43 |
+
|
| 44 |
+
if(model_id=="openai/whisper-medium"):
|
| 45 |
+
encoder_input = torch.randn([1, 80, 3000], dtype=compute_dtype, device=device)
|
| 46 |
+
if(model_id=="distil-whisper/distil-large-v3"):
|
| 47 |
+
encoder_input = torch.randn([1, 128, 3000], dtype=compute_dtype, device=device)
|
| 48 |
+
|
| 49 |
+
def run_encoder():
|
| 50 |
+
with torch.no_grad():
|
| 51 |
+
model.model.encoder(encoder_input)
|
| 52 |
+
torch.cuda.synchronize()
|
| 53 |
+
|
| 54 |
+
t = []
|
| 55 |
+
for _ in range(200):
|
| 56 |
+
t1 = time.time()
|
| 57 |
+
run_encoder()
|
| 58 |
+
t2 = time.time()
|
| 59 |
+
t.append(t2-t1)
|
| 60 |
+
print("Encoder", np.mean(t[-100:]), "sec / sample")
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
decoder_input = torch.randint(0, 1000, [1, 1], dtype=torch.int64, device=device)
|
| 64 |
+
def run_decoder():
|
| 65 |
+
with torch.no_grad():
|
| 66 |
+
out = model.model.decoder(decoder_input)
|
| 67 |
+
torch.cuda.synchronize()
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
t = []
|
| 71 |
+
for _ in range(200):
|
| 72 |
+
t1 = time.time()
|
| 73 |
+
run_decoder()
|
| 74 |
+
t2 = time.time()
|
| 75 |
+
t.append(t2-t1)
|
| 76 |
+
print("Decoder", np.mean(t[-100:]), "sec / sample")
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
#openai/whisper-medium | RTX 4090
|
| 80 |
+
#Encoder: use default backend
|
| 81 |
+
#fp16 : 0.0234 sec / sample
|
| 82 |
+
#hqq 4-bit (default,compiled) : 0.0124 sec / sample | 1.89x faster
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
#Decoder: use torchao backend to decode 1 token at a time
|
| 86 |
+
#fp16 : 0.01080 sec / sample
|
| 87 |
+
#hqq 4-bit (ao_int4, compiled): 0.000928 sec / sample | 11.63x faster
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
#distil-whisper/distil-large-v3 | RTX 4090
|
| 91 |
+
#Encoder: use default backend
|
| 92 |
+
#fp16 : 0.03738 sec / sample
|
| 93 |
+
#hqq 4-bit (default,compiled) : 0.01869 sec / sample | 2x faster
|
| 94 |
+
|
| 95 |
+
|
| 96 |
+
#Decoder: use torchao backend to decode 1 token at a time
|
| 97 |
+
#fp16 : 0.002592 sec / sample
|
| 98 |
+
#hqq 4-bit (ao_int4, compiled): 0.000326 sec / sample | 7.95x faster
|
lm-quant-toolkit/.deps/hqq/examples/llama2_benchmark/.gitignore
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
snapshots/
|
| 2 |
+
snapshots-*/
|
| 3 |
+
results/
|
lm-quant-toolkit/.deps/hqq/examples/llama2_benchmark/.pdbrc
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# b hqq/core/quantize.py:677
|
| 2 |
+
# c
|
| 3 |
+
# b eval_model.py:34
|
| 4 |
+
# b bench.py:221
|
| 5 |
+
# b hqq/models/base.py:132
|
| 6 |
+
# b quant_llama2_hqq_demo.py:30
|
| 7 |
+
# b hqq/models/base.py:265
|
| 8 |
+
# b debug-quant-mem.py:18
|
| 9 |
+
# b hqq/models/hf/llama.py:71
|
| 10 |
+
# b hqq/models/base.py:260
|
lm-quant-toolkit/.deps/hqq/examples/llama2_benchmark/adapter/autoawq.py
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import time
|
| 3 |
+
import transformers
|
| 4 |
+
|
| 5 |
+
from awq import AutoAWQForCausalLM
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
def create_autoawq_model(model_id, quant_config, config_id, load_quantized, save_dir):
|
| 9 |
+
quantized = False
|
| 10 |
+
quant_path = f"{save_dir}/{model_id}-{config_id}-awq"
|
| 11 |
+
if load_quantized and os.path.exists(quant_path):
|
| 12 |
+
model = AutoAWQForCausalLM.from_quantized(quant_path, "", fuse_layers=False)
|
| 13 |
+
tokenizer = transformers.AutoTokenizer.from_pretrained(model_id)
|
| 14 |
+
quantized = True
|
| 15 |
+
model = model.cuda()
|
| 16 |
+
else:
|
| 17 |
+
tokenizer = transformers.AutoTokenizer.from_pretrained(model_id)
|
| 18 |
+
model = AutoAWQForCausalLM.from_pretrained(model_id)
|
| 19 |
+
return model, tokenizer, quantized
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
def quantize_autoawq_model(model, tokenizer, quant_config, model_id, config_id, save_dir):
|
| 23 |
+
t1 = time.time()
|
| 24 |
+
model.quantize(tokenizer, quant_config=quant_config)
|
| 25 |
+
t2 = time.time()
|
| 26 |
+
print('Took ' + str(t2 - t1) + ' seconds to quantize the model with AutoAWQ')
|
| 27 |
+
quant_path = f"{save_dir}/{model_id}-{config_id}-awq"
|
| 28 |
+
model.save_quantized(quant_path)
|
| 29 |
+
tokenizer.save_pretrained(quant_path)
|
| 30 |
+
return model, t2 - t1
|
| 31 |
+
|
| 32 |
+
|
lm-quant-toolkit/.deps/hqq/examples/llama2_benchmark/adapter/awq.py
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import time
|
| 3 |
+
import torch
|
| 4 |
+
|
| 5 |
+
from transformers import AutoModelForCausalLM
|
| 6 |
+
from transformers import AutoTokenizer
|
| 7 |
+
from transformers import AutoConfig
|
| 8 |
+
from accelerate import (
|
| 9 |
+
init_empty_weights,
|
| 10 |
+
infer_auto_device_map,
|
| 11 |
+
load_checkpoint_in_model,
|
| 12 |
+
)
|
| 13 |
+
|
| 14 |
+
from awq.quantize.pre_quant import run_awq
|
| 15 |
+
from awq.quantize.pre_quant import apply_awq
|
| 16 |
+
from awq.quantize.quantizer import real_quantize_model_weight
|
| 17 |
+
from awq.utils.utils import simple_dispatch_model
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
max_memory = {}
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
def create_awq_model(model_id, quant_config, config_id, load_quantized, save_dir):
|
| 24 |
+
quantized = False
|
| 25 |
+
quant_path = f"{save_dir}/{model_id}-{config_id}-awq"
|
| 26 |
+
|
| 27 |
+
tokenizer = AutoTokenizer.from_pretrained(
|
| 28 |
+
model_id, use_fast=False, trust_remote_code=True
|
| 29 |
+
)
|
| 30 |
+
config = AutoConfig.from_pretrained(model_id, trust_remote_code=True)
|
| 31 |
+
# Note (Haotian): To avoid OOM after huggingface transformers 4.36.2
|
| 32 |
+
config.use_cache = False
|
| 33 |
+
if load_quantized and os.path.exists(f"{quant_path}/qmodel.pth"):
|
| 34 |
+
with init_empty_weights():
|
| 35 |
+
model = AutoModelForCausalLM.from_config(
|
| 36 |
+
config=config, torch_dtype=torch.float16, trust_remote_code=True
|
| 37 |
+
)
|
| 38 |
+
# Infer device map
|
| 39 |
+
kwargs = {"max_memory": max_memory} if len(max_memory) else {}
|
| 40 |
+
device_map = infer_auto_device_map(
|
| 41 |
+
model,
|
| 42 |
+
no_split_module_classes=[
|
| 43 |
+
"OPTDecoderLayer",
|
| 44 |
+
"LlamaDecoderLayer",
|
| 45 |
+
"BloomBlock",
|
| 46 |
+
"MPTBlock",
|
| 47 |
+
"DecoderLayer",
|
| 48 |
+
],
|
| 49 |
+
**kwargs,
|
| 50 |
+
)
|
| 51 |
+
# Load checkpoint in the model
|
| 52 |
+
load_checkpoint_in_model(
|
| 53 |
+
model,
|
| 54 |
+
checkpoint=quant_path,
|
| 55 |
+
device_map=device_map,
|
| 56 |
+
offload_state_dict=True,
|
| 57 |
+
)
|
| 58 |
+
# Dispatch model
|
| 59 |
+
model = simple_dispatch_model(model, device_map=device_map)
|
| 60 |
+
quantized = True
|
| 61 |
+
model.eval()
|
| 62 |
+
else:
|
| 63 |
+
kwargs = {"torch_dtype": torch.float16, "low_cpu_mem_usage": True}
|
| 64 |
+
model = AutoModelForCausalLM.from_pretrained(
|
| 65 |
+
model_id, config=config, trust_remote_code=True, **kwargs
|
| 66 |
+
)
|
| 67 |
+
return model, tokenizer, quantized
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
def quantize_awq_model(model, tokenizer, quant_config, model_id, config_id, save_dir):
|
| 71 |
+
t1 = time.time()
|
| 72 |
+
nbits = quant_config.pop('w_bit')
|
| 73 |
+
awq_results = run_awq(
|
| 74 |
+
model,
|
| 75 |
+
tokenizer,
|
| 76 |
+
w_bit=nbits,
|
| 77 |
+
q_config=quant_config,
|
| 78 |
+
n_samples=128,
|
| 79 |
+
seqlen=512,
|
| 80 |
+
)
|
| 81 |
+
intermediate_fp = f"{save_dir}/{model_id}-{config_id}-awq/intermediate.pth"
|
| 82 |
+
dirpath = os.path.dirname(intermediate_fp)
|
| 83 |
+
os.makedirs(dirpath, exist_ok=True)
|
| 84 |
+
torch.save(awq_results, intermediate_fp)
|
| 85 |
+
awq_results = torch.load(intermediate_fp, map_location="cpu")
|
| 86 |
+
apply_awq(model, awq_results)
|
| 87 |
+
real_quantize_model_weight(model, w_bit=nbits, q_config=quant_config)
|
| 88 |
+
|
| 89 |
+
t2 = time.time()
|
| 90 |
+
print('Took ' + str(t2 - t1) + ' seconds to quantize the model with AWQ')
|
| 91 |
+
quant_path = f"{save_dir}/{model_id}-{config_id}-awq"
|
| 92 |
+
torch.save(model.cpu().state_dict(), f"{quant_path}/qmodel.pth")
|
| 93 |
+
tokenizer.save_pretrained(quant_path)
|
| 94 |
+
return model, t2 - t1
|
lm-quant-toolkit/.deps/hqq/examples/llama2_benchmark/data/fnorm-Llama-2-13b-hf.csv
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
lm-quant-toolkit/.deps/hqq/examples/llama2_benchmark/data/fnorm-Llama-2-70b-hf.csv
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
lm-quant-toolkit/.deps/hqq/examples/llama2_benchmark/data/fnorm-Llama-2-7b-hf.csv
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
lm-quant-toolkit/.deps/hqq/examples/llama2_benchmark/data/fnorm-Meta-Llama-3.1-405B-Instruct.csv
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
lm-quant-toolkit/.deps/hqq/examples/llama2_benchmark/debug-bench.sh
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/bin/bash
|
| 2 |
+
|
| 3 |
+
python -m pdb bench.py
|
lm-quant-toolkit/.deps/hqq/examples/llama2_benchmark/eval_model.py
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import gc
|
| 2 |
+
import time
|
| 3 |
+
|
| 4 |
+
import numpy as np
|
| 5 |
+
import torch
|
| 6 |
+
from datasets import load_dataset
|
| 7 |
+
from tqdm import tqdm
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
def cleanup():
|
| 11 |
+
torch.cuda.empty_cache()
|
| 12 |
+
gc.collect()
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
def eval_ptb(model, tokenizer, max_length=1024, stride=512, verbose=True):
|
| 16 |
+
dataset = load_dataset("ptb_text_only", "penn_treebank", split="test")
|
| 17 |
+
return eval_ppl(
|
| 18 |
+
"ptb",
|
| 19 |
+
model,
|
| 20 |
+
tokenizer,
|
| 21 |
+
dataset,
|
| 22 |
+
text_column="sentence",
|
| 23 |
+
max_length=max_length,
|
| 24 |
+
stride=stride,
|
| 25 |
+
verbose=verbose,
|
| 26 |
+
)
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def eval_c4(model, tokenizer, max_length=1024, stride=512, verbose=True):
|
| 30 |
+
dataset = load_dataset(
|
| 31 |
+
"allenai/c4",
|
| 32 |
+
data_files={"validation": "en/c4-validation.00000-of-00008.json.gz"},
|
| 33 |
+
split="validation",
|
| 34 |
+
download_mode="reuse_dataset_if_exists",
|
| 35 |
+
)
|
| 36 |
+
# pick first 1100
|
| 37 |
+
dataset = dataset[:1100]
|
| 38 |
+
return eval_ppl(
|
| 39 |
+
"C4",
|
| 40 |
+
model,
|
| 41 |
+
tokenizer,
|
| 42 |
+
dataset,
|
| 43 |
+
text_column="text",
|
| 44 |
+
max_length=max_length,
|
| 45 |
+
stride=stride,
|
| 46 |
+
verbose=verbose,
|
| 47 |
+
)
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
def eval_wikitext2(model, tokenizer, max_length=1024, stride=512, verbose=True):
|
| 51 |
+
dataset = load_dataset("wikitext", "wikitext-2-raw-v1", split="test")
|
| 52 |
+
return eval_ppl(
|
| 53 |
+
"wikitext",
|
| 54 |
+
model,
|
| 55 |
+
tokenizer,
|
| 56 |
+
dataset,
|
| 57 |
+
text_column="text",
|
| 58 |
+
max_length=max_length,
|
| 59 |
+
stride=stride,
|
| 60 |
+
verbose=verbose,
|
| 61 |
+
)
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
# Adapted from https://huggingface.co/transformers/v4.2.2/perplexity.html
|
| 65 |
+
def eval_ppl(
|
| 66 |
+
ds_type,
|
| 67 |
+
model,
|
| 68 |
+
tokenizer,
|
| 69 |
+
dataset,
|
| 70 |
+
text_column="text",
|
| 71 |
+
max_length=1024,
|
| 72 |
+
stride=512,
|
| 73 |
+
verbose=True,
|
| 74 |
+
):
|
| 75 |
+
model.eval()
|
| 76 |
+
tokenizer.pad_token = tokenizer.eos_token
|
| 77 |
+
tokenizer.padding_side = "right"
|
| 78 |
+
tokenizer.add_eos_token = False
|
| 79 |
+
|
| 80 |
+
encodings = tokenizer("\n\n".join(dataset[text_column]), return_tensors="pt")
|
| 81 |
+
|
| 82 |
+
encodings["input_ids"] = encodings["input_ids"].to("cuda")
|
| 83 |
+
|
| 84 |
+
lls, t = [], []
|
| 85 |
+
for i in tqdm(
|
| 86 |
+
range(0, encodings["input_ids"].size(1), stride),
|
| 87 |
+
desc=ds_type,
|
| 88 |
+
disable=not verbose,
|
| 89 |
+
):
|
| 90 |
+
begin_loc = max(i + stride - max_length, 0)
|
| 91 |
+
end_loc = min(i + stride, encodings["input_ids"].size(1))
|
| 92 |
+
trg_len = end_loc - i
|
| 93 |
+
input_ids = encodings["input_ids"][:, begin_loc:end_loc]
|
| 94 |
+
target_ids = input_ids.clone()
|
| 95 |
+
target_ids[:, :-trg_len] = -100 # ignore context
|
| 96 |
+
|
| 97 |
+
t1 = time.time()
|
| 98 |
+
with torch.no_grad():
|
| 99 |
+
log_likelihood = model(input_ids, labels=target_ids).loss * trg_len
|
| 100 |
+
torch.cuda.synchronize()
|
| 101 |
+
t2 = time.time()
|
| 102 |
+
t.append((t2 - t1))
|
| 103 |
+
lls.append(log_likelihood)
|
| 104 |
+
|
| 105 |
+
del input_ids, target_ids
|
| 106 |
+
|
| 107 |
+
ppl = np.round(float(torch.exp(torch.stack(lls).sum() / end_loc)), 4)
|
| 108 |
+
pred_time = np.round(np.mean(t), 3)
|
| 109 |
+
if verbose:
|
| 110 |
+
print(f"{ds_type} perplexity: {ppl}, time: {pred_time} sec")
|
| 111 |
+
|
| 112 |
+
del encodings
|
| 113 |
+
cleanup()
|
| 114 |
+
|
| 115 |
+
return ppl, pred_time
|
lm-quant-toolkit/.deps/hqq/examples/llama2_benchmark/fp16_llama2_demo.py
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
|
| 3 |
+
from transformers import AutoModelForCausalLM
|
| 4 |
+
from transformers import AutoTokenizer
|
| 5 |
+
from eval_model import eval_wikitext2
|
| 6 |
+
|
| 7 |
+
#Settings
|
| 8 |
+
######################################################################################
|
| 9 |
+
hf_auth = None #HuggingFace token
|
| 10 |
+
cache_path = '' #cache directory to store data
|
| 11 |
+
|
| 12 |
+
#Chose a model
|
| 13 |
+
# model_id = "meta-llama/Llama-2-7b-hf"
|
| 14 |
+
# model_id = "meta-llama/Meta-Llama-3-8B"
|
| 15 |
+
model_id = "meta-llama/Llama-2-13b-hf"
|
| 16 |
+
#model_id = "meta-llama/Llama-2-70b-hf"
|
| 17 |
+
|
| 18 |
+
#Load model on the CPU
|
| 19 |
+
######################################################################################
|
| 20 |
+
model = AutoModelForCausalLM.from_pretrained(model_id, device_map="auto", torch_dtype=torch.float16)
|
| 21 |
+
tokenizer = AutoTokenizer.from_pretrained(model_id)
|
| 22 |
+
|
| 23 |
+
#Evaluate the quantized model
|
| 24 |
+
######################################################################################
|
| 25 |
+
eval_wikitext2(model, tokenizer, verbose=True)
|
| 26 |
+
|
lm-quant-toolkit/.deps/hqq/examples/llama2_benchmark/quant_llama2_awq_demo.py
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch, transformers
|
| 2 |
+
|
| 3 |
+
#Settings
|
| 4 |
+
######################################################################################
|
| 5 |
+
hf_auth = None #HuggingFace token
|
| 6 |
+
cache_path = '' #cache directory to store data
|
| 7 |
+
|
| 8 |
+
#Chose a model
|
| 9 |
+
# model_id = "meta-llama/Meta-Llama-3-8B"
|
| 10 |
+
# model_id = "meta-llama/Llama-2-7b-hf"
|
| 11 |
+
model_id = "meta-llama/Llama-2-13b-hf"
|
| 12 |
+
#model_id = "meta-llama/Llama-2-70b-hf"
|
| 13 |
+
|
| 14 |
+
#AWQ settings
|
| 15 |
+
######################################################################################
|
| 16 |
+
from awq import AutoAWQForCausalLM
|
| 17 |
+
import gc, time
|
| 18 |
+
|
| 19 |
+
# Load model
|
| 20 |
+
tokenizer = transformers.AutoTokenizer.from_pretrained(model_id, use_auth_token=hf_auth)
|
| 21 |
+
model = AutoAWQForCausalLM.from_pretrained(model_id, use_auth_token=hf_auth, resume_download=True)
|
| 22 |
+
|
| 23 |
+
#quant_config = {"w_bit": 4, "q_group_size": 128, "zero_point": True, 'version':'GEMM'}
|
| 24 |
+
#quant_config = {"w_bit": 4, "q_group_size": 64, "zero_point": True, 'version':'GEMM'}
|
| 25 |
+
quant_config = {"w_bit": 3, "q_group_size": 64, "zero_point": True, 'version':'gemv_fast'}
|
| 26 |
+
|
| 27 |
+
t1 = time.time()
|
| 28 |
+
model.quantize(tokenizer, quant_config=quant_config)
|
| 29 |
+
t2 = time.time()
|
| 30 |
+
print('Took ' + str(t2-t1) + ' seconds to quantize the model with AWQ')
|
| 31 |
+
|
| 32 |
+
model = model.cuda()
|
| 33 |
+
torch.cuda.empty_cache()
|
| 34 |
+
gc.collect()
|
| 35 |
+
|
| 36 |
+
#Evaluate the quantized model
|
| 37 |
+
######################################################################################
|
| 38 |
+
from eval_model import eval_wikitext2
|
| 39 |
+
eval_wikitext2(model, tokenizer, verbose=True)
|
| 40 |
+
|
lm-quant-toolkit/.deps/hqq/examples/llama2_benchmark/run-bench.sh
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/bin/bash
|
| 2 |
+
|
| 3 |
+
# export HF_HOME=/data/hugginface
|
| 4 |
+
|
| 5 |
+
if [ ! -d logs ]; then
|
| 6 |
+
mkdir logs
|
| 7 |
+
fi
|
| 8 |
+
python bench.py 2>&1 | tee logs/bench-$(date +%Y%m%d%H%M%S).log
|
lm-quant-toolkit/.deps/hqq/examples/lora/train_hqq_lora_example.py
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#Settings
|
| 2 |
+
######################################################################################
|
| 3 |
+
hf_auth = None #HuggingFace token
|
| 4 |
+
cache_path = '' #cache directory to store data
|
| 5 |
+
|
| 6 |
+
#Chose a model
|
| 7 |
+
model_id = "meta-llama/Llama-2-7b-hf"
|
| 8 |
+
#model_id = "meta-llama/Llama-2-13b-hf"
|
| 9 |
+
#model_id = "meta-llama/Llama-2-70b-hf"
|
| 10 |
+
|
| 11 |
+
#HQQ Quantize
|
| 12 |
+
######################################################################################
|
| 13 |
+
from hqq.engine.hf import HQQModelForCausalLM, AutoTokenizer
|
| 14 |
+
model = HQQModelForCausalLM.from_pretrained(model_id, use_auth_token=hf_auth, cache_dir=cache_path)
|
| 15 |
+
tokenizer = AutoTokenizer.from_pretrained(model_id, use_auth_token=hf_auth, cache_dir=cache_path)
|
| 16 |
+
|
| 17 |
+
#Quantize the model
|
| 18 |
+
from hqq.core.quantize import *
|
| 19 |
+
quant_config = BaseQuantizeConfig(nbits=4, group_size=64, quant_scale=False, quant_zero=False)
|
| 20 |
+
model.quantize_model(quant_config=quant_config)
|
| 21 |
+
|
| 22 |
+
#Add Peft
|
| 23 |
+
######################################################################################
|
| 24 |
+
from hqq.core.peft import PeftUtils
|
| 25 |
+
from hqq.core.quantize import *
|
| 26 |
+
|
| 27 |
+
train_dtype = torch.float32
|
| 28 |
+
base_lora_params = {'lora_type':'default', 'r':32, 'lora_alpha':64, 'dropout':0.05, 'train_dtype':train_dtype}
|
| 29 |
+
lora_params = {'self_attn.q_proj': base_lora_params,
|
| 30 |
+
'self_attn.k_proj': base_lora_params,
|
| 31 |
+
'self_attn.v_proj': base_lora_params,
|
| 32 |
+
'self_attn.o_proj': base_lora_params,
|
| 33 |
+
'mlp.gate_proj' : None,
|
| 34 |
+
'mlp.up_proj' : None,
|
| 35 |
+
'mlp.down_proj' : None}
|
| 36 |
+
|
| 37 |
+
#Apply LoRA
|
| 38 |
+
PeftUtils.add_lora(model, lora_params)
|
| 39 |
+
|
| 40 |
+
#Dataset
|
| 41 |
+
######################################################################################
|
| 42 |
+
from datasets import load_dataset, Dataset
|
| 43 |
+
from tqdm import tqdm
|
| 44 |
+
import transformers
|
| 45 |
+
import numpy as np
|
| 46 |
+
import random
|
| 47 |
+
|
| 48 |
+
tokenizer.pad_token = tokenizer.eos_token
|
| 49 |
+
tokenizer.padding_side = "right"
|
| 50 |
+
tokenizer.add_bos_token = False
|
| 51 |
+
tokenizer.add_eos_token = False
|
| 52 |
+
|
| 53 |
+
batch_size = 1
|
| 54 |
+
num_epochs = 1
|
| 55 |
+
grad_acc = 1
|
| 56 |
+
max_tokens = 256
|
| 57 |
+
max_samples = 5000
|
| 58 |
+
|
| 59 |
+
#OpenAssistant
|
| 60 |
+
##########################################################################
|
| 61 |
+
dataset = load_dataset("timdettmers/openassistant-guanaco", split="train")
|
| 62 |
+
dataset_val = load_dataset("timdettmers/openassistant-guanaco", split="test")
|
| 63 |
+
|
| 64 |
+
def pre_process_chat(chat):
|
| 65 |
+
#add proper chat preprocessing (bos/eos tokens, etc.)
|
| 66 |
+
return chat
|
| 67 |
+
|
| 68 |
+
def assitant_prompt(prompt):
|
| 69 |
+
return '### Human:' + prompt + '\n### Assistant:'
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
random.seed(100)
|
| 73 |
+
idx = random.sample(range(len(dataset)), min(max_samples, len(dataset)))
|
| 74 |
+
dataset = Dataset.from_dict({'text':[pre_process_chat(dataset[i]['text']) for i in tqdm(idx)]})
|
| 75 |
+
dataset_val = Dataset.from_dict({'text':[pre_process_chat(dataset_val[i]['text']) for i in range(len(dataset_val))]})
|
| 76 |
+
|
| 77 |
+
#####################################################################################
|
| 78 |
+
#Train
|
| 79 |
+
from trl import SFTTrainer
|
| 80 |
+
|
| 81 |
+
grad_acc = 2
|
| 82 |
+
logging_st = 1
|
| 83 |
+
max_steps = -1
|
| 84 |
+
lr = 1e-4
|
| 85 |
+
batch_size = 1
|
| 86 |
+
n_epochs = 1
|
| 87 |
+
|
| 88 |
+
training_args = transformers.TrainingArguments(
|
| 89 |
+
output_dir='.',
|
| 90 |
+
per_device_train_batch_size=batch_size,
|
| 91 |
+
#per_device_eval_batch_size=batch_size,
|
| 92 |
+
gradient_accumulation_steps=grad_acc,
|
| 93 |
+
learning_rate=lr,
|
| 94 |
+
logging_steps=logging_st,
|
| 95 |
+
num_train_epochs=n_epochs,
|
| 96 |
+
max_steps=max_steps,
|
| 97 |
+
#evaluation_strategy = "epoch",
|
| 98 |
+
remove_unused_columns=False,
|
| 99 |
+
#logging_strategy="epoch",
|
| 100 |
+
fp16=train_dtype==torch.float32,
|
| 101 |
+
max_grad_norm=1.0,
|
| 102 |
+
save_steps=10000000,
|
| 103 |
+
lr_scheduler_type= "linear",
|
| 104 |
+
)
|
| 105 |
+
|
| 106 |
+
#Wrap model to avoid accelerate issues
|
| 107 |
+
class WrappedModel(torch.nn.Module):
|
| 108 |
+
def __init__(self, model):
|
| 109 |
+
super().__init__()
|
| 110 |
+
self.model = model
|
| 111 |
+
|
| 112 |
+
def forward(self, *args, **kwargs):
|
| 113 |
+
return self.model.forward(*args, **kwargs)
|
| 114 |
+
|
| 115 |
+
def train(self):
|
| 116 |
+
self.model.train()
|
| 117 |
+
|
| 118 |
+
def eval(self):
|
| 119 |
+
self.model.eval()
|
| 120 |
+
|
| 121 |
+
def parameters(self):
|
| 122 |
+
return self.model.parameters()
|
| 123 |
+
|
| 124 |
+
trainer = SFTTrainer(
|
| 125 |
+
model=WrappedModel(model),
|
| 126 |
+
tokenizer=tokenizer,
|
| 127 |
+
max_seq_length=max_tokens,
|
| 128 |
+
train_dataset=dataset,
|
| 129 |
+
eval_dataset=None,
|
| 130 |
+
peft_config=None,
|
| 131 |
+
args=training_args,
|
| 132 |
+
dataset_text_field="text",
|
| 133 |
+
)
|
| 134 |
+
|
| 135 |
+
model.is_parallelizable = False
|
| 136 |
+
trainer.is_model_parallel = False
|
| 137 |
+
trainer.place_model_on_device = False
|
| 138 |
+
|
| 139 |
+
model.train()
|
| 140 |
+
trainer.train()
|
| 141 |
+
|
| 142 |
+
#Prediction/Eval
|
| 143 |
+
######################################################################################
|
| 144 |
+
#from #https://huggingface.co/spaces/evaluate-metric/perplexity/blob/main/perplexity.py
|
| 145 |
+
def compute_perplexity_batched(model, tokenizer, predictions, encodings=None, batch_size=1, add_start_token=True, device='cuda', max_length=None):
|
| 146 |
+
if tokenizer.pad_token is None and batch_size > 1:
|
| 147 |
+
existing_special_tokens = list(tokenizer.special_tokens_map_extended.values())
|
| 148 |
+
# check that the model already has at least one special token defined
|
| 149 |
+
assert (len(existing_special_tokens) > 0), "If batch_size > 1, model must have at least one special token to use for padding. Please use a different model or set batch_size=1."
|
| 150 |
+
# assign one of the special tokens to also be the pad token
|
| 151 |
+
tokenizer.add_special_tokens({"pad_token": existing_special_tokens[0]})
|
| 152 |
+
|
| 153 |
+
if add_start_token and max_length:
|
| 154 |
+
# leave room for <BOS> token to be added:
|
| 155 |
+
assert (tokenizer.bos_token is not None), "Input model must already have a BOS token if using add_start_token=True. Please use a different model, or set add_start_token=False"
|
| 156 |
+
max_tokenized_len = max_length - 1
|
| 157 |
+
else:
|
| 158 |
+
max_tokenized_len = max_length
|
| 159 |
+
|
| 160 |
+
|
| 161 |
+
if(encodings is None):
|
| 162 |
+
encodings = tokenizer(
|
| 163 |
+
predictions,
|
| 164 |
+
add_special_tokens=False,
|
| 165 |
+
padding=True,
|
| 166 |
+
truncation=True if max_tokenized_len else False,
|
| 167 |
+
max_length=max_tokenized_len,
|
| 168 |
+
return_tensors="pt",
|
| 169 |
+
return_attention_mask=True).to(device)
|
| 170 |
+
|
| 171 |
+
encoded_texts = encodings["input_ids"]
|
| 172 |
+
attn_masks = encodings["attention_mask"]
|
| 173 |
+
|
| 174 |
+
# check that each input is long enough:
|
| 175 |
+
if add_start_token:
|
| 176 |
+
assert torch.all(torch.ge(attn_masks.sum(1), 1)), "Each input text must be at least one token long."
|
| 177 |
+
else:
|
| 178 |
+
assert torch.all(
|
| 179 |
+
torch.ge(attn_masks.sum(1), 2)
|
| 180 |
+
), "When add_start_token=False, each input text must be at least two tokens long. Run with add_start_token=True if inputting strings of only one token, and remove all empty input strings."
|
| 181 |
+
|
| 182 |
+
ppls = []
|
| 183 |
+
loss_fct = torch.nn.CrossEntropyLoss(reduction="none")
|
| 184 |
+
|
| 185 |
+
for start_index in tqdm(range(0, len(encoded_texts), batch_size)):
|
| 186 |
+
end_index = min(start_index + batch_size, len(encoded_texts))
|
| 187 |
+
encoded_batch = encoded_texts[start_index:end_index]
|
| 188 |
+
attn_mask = attn_masks[start_index:end_index]
|
| 189 |
+
|
| 190 |
+
if add_start_token:
|
| 191 |
+
bos_tokens_tensor = torch.tensor([[tokenizer.bos_token_id]] * encoded_batch.size(dim=0)).to(device)
|
| 192 |
+
encoded_batch = torch.cat([bos_tokens_tensor, encoded_batch], dim=1)
|
| 193 |
+
attn_mask = torch.cat([torch.ones(bos_tokens_tensor.size(), dtype=torch.int64).to(device), attn_mask], dim=1)
|
| 194 |
+
|
| 195 |
+
labels = encoded_batch
|
| 196 |
+
|
| 197 |
+
with torch.no_grad():
|
| 198 |
+
out_logits = model(encoded_batch, attention_mask=attn_mask).logits
|
| 199 |
+
|
| 200 |
+
shift_logits = out_logits[..., :-1, :].contiguous()
|
| 201 |
+
shift_labels = labels[..., 1:].contiguous()
|
| 202 |
+
shift_attention_mask_batch = attn_mask[..., 1:].contiguous()
|
| 203 |
+
|
| 204 |
+
perplexity_batch = torch.exp(
|
| 205 |
+
(loss_fct(shift_logits.transpose(1, 2), shift_labels) * shift_attention_mask_batch).sum(1)
|
| 206 |
+
/ shift_attention_mask_batch.sum(1))
|
| 207 |
+
|
| 208 |
+
ppls += perplexity_batch.tolist()
|
| 209 |
+
|
| 210 |
+
return np.mean(ppls)
|
| 211 |
+
|
| 212 |
+
|
| 213 |
+
tokenizer.add_bos_token = True
|
| 214 |
+
tokenizer.add_eos_token = False
|
| 215 |
+
model.eval()
|
| 216 |
+
|
| 217 |
+
#Convert lora weights to the same model dtype for faster inference
|
| 218 |
+
PeftUtils.cast_lora_weights(model, dtype=torch.half)
|
| 219 |
+
|
| 220 |
+
print('perplexity', compute_perplexity_batched(model=model, tokenizer=tokenizer, predictions=[s['text'] for s in dataset_val], batch_size=1, max_length=max_tokens))
|
lm-quant-toolkit/.deps/hqq/examples/open_clip/.pdbrc
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# b /home/justin/work/hqq/hqq/engine/open_clip.py:57
|
| 2 |
+
# b /home/justin/work/hqq/hqq/models/open_clip/base.py:77
|
| 3 |
+
# b /home/justin/work/hqq/examples/open_clip/vit_clip_example.py:27
|
| 4 |
+
b /home/justin/work/hqq/examples/open_clip/vit_clip_example.py:62
|
lm-quant-toolkit/.deps/hqq/examples/open_clip/vit_clip_example.py
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
from pathlib import Path
|
| 3 |
+
|
| 4 |
+
import open_clip
|
| 5 |
+
import torch
|
| 6 |
+
from open_clip import tokenizer
|
| 7 |
+
from PIL import Image
|
| 8 |
+
from skimage import data_dir
|
| 9 |
+
|
| 10 |
+
from hqq.core.quantize import BaseQuantizeConfig
|
| 11 |
+
from hqq.engine.open_clip import HQQOpenCLIP
|
| 12 |
+
|
| 13 |
+
model_ids = [
|
| 14 |
+
"laion/CLIP-ViT-B-16-laion2B-s34B-b88K",
|
| 15 |
+
"laion/CLIP-ViT-B-32-laion2B-s34B-b79K",
|
| 16 |
+
"laion/CLIP-ViT-H-14-laion2B-s32B-b79K",
|
| 17 |
+
"laion/CLIP-ViT-L-14-laion2B-s32B-b82K",
|
| 18 |
+
]
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def quant_models(model_ids):
|
| 22 |
+
for model_id in model_ids:
|
| 23 |
+
model = HQQOpenCLIP.create_model(model_id, device="cpu")
|
| 24 |
+
|
| 25 |
+
# Quantize settings
|
| 26 |
+
# quant_config = BaseQuantizeConfig(nbits=8, group_size=128)
|
| 27 |
+
quant_config = BaseQuantizeConfig(nbits=4, group_size=64)
|
| 28 |
+
# quant_config = BaseQuantizeConfig(nbits=3, group_size=64)
|
| 29 |
+
# quant_config = BaseQuantizeConfig(nbits=2, group_size=16, quant_scale=True)
|
| 30 |
+
|
| 31 |
+
# Quantize
|
| 32 |
+
model.quantize_model(quant_config=quant_config)
|
| 33 |
+
|
| 34 |
+
# Save model
|
| 35 |
+
save_dir = "snapshots/" + model_id
|
| 36 |
+
Path(save_dir).mkdir(parents=True, exist_ok=True)
|
| 37 |
+
model.save_quantized(save_dir=save_dir)
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
def load_quantized(model_id):
|
| 41 |
+
# Load model
|
| 42 |
+
save_dir = "snapshots/" + model_id
|
| 43 |
+
model = HQQOpenCLIP.from_quantized(save_dir)
|
| 44 |
+
return model
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
def compare_weights_raw(model_id):
|
| 48 |
+
qnt_pt_file = "snapshots/" + model_id + "/qmodel.pt"
|
| 49 |
+
ref_pt_file = "/home/justin/.cache/huggingface/hub/models--laion--CLIP-ViT-H-14-laion2B-s32B-b79K/snapshots/de081ac0a0ca8dc9d1533eed1ae884bb8ae1404b/open_clip_pytorch_model.bin"
|
| 50 |
+
qnt = torch.load(qnt_pt_file)
|
| 51 |
+
ref = torch.load(ref_pt_file)
|
| 52 |
+
print("*" * 72)
|
| 53 |
+
print("from quantized file")
|
| 54 |
+
for k in qnt:
|
| 55 |
+
print(k)
|
| 56 |
+
print("*" * 72)
|
| 57 |
+
print("from original file")
|
| 58 |
+
for k in ref:
|
| 59 |
+
print(k)
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
def compare_weights(model_id):
|
| 63 |
+
model, model_ref, _ = create_model_dual(model_id)
|
| 64 |
+
dict1 = dict(model.named_parameters())
|
| 65 |
+
dict2 = dict(model_ref.named_parameters())
|
| 66 |
+
for name, param in dict1.items():
|
| 67 |
+
param_ref = dict2.get(name, None)
|
| 68 |
+
if param_ref is not None:
|
| 69 |
+
if not torch.allclose(param_ref.data, param.data, rtol=0.0, equal_nan=True):
|
| 70 |
+
print(f"weight differs: {name}")
|
| 71 |
+
if name == "text_projection":
|
| 72 |
+
print(param)
|
| 73 |
+
print(param_ref)
|
| 74 |
+
else:
|
| 75 |
+
print(f"weight same: {name}")
|
| 76 |
+
else:
|
| 77 |
+
print(f"not in ref model: {name}")
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
def create_model_dual(model_id, mask=3):
|
| 81 |
+
model = None
|
| 82 |
+
model_ref = None
|
| 83 |
+
preprocess = None
|
| 84 |
+
|
| 85 |
+
if mask & 1:
|
| 86 |
+
model = load_quantized(model_id)
|
| 87 |
+
model = model.half().cuda()
|
| 88 |
+
|
| 89 |
+
# Load reference model to compare with
|
| 90 |
+
if mask & 2:
|
| 91 |
+
comps = model_id.split("/")
|
| 92 |
+
elems = comps[1].split("-")
|
| 93 |
+
model_name = "-".join(elems[1:4])
|
| 94 |
+
pretrained = "-".join(elems[4:])
|
| 95 |
+
model_ref, _, preprocess = open_clip.create_model_and_transforms(
|
| 96 |
+
model_name, pretrained=pretrained
|
| 97 |
+
)
|
| 98 |
+
model_ref = model_ref.half().cuda()
|
| 99 |
+
return model, model_ref, preprocess
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
def create_and_quant_model(model_id):
|
| 103 |
+
model = HQQOpenCLIP.create_model(model_id, device="cpu")
|
| 104 |
+
# Quantize settings
|
| 105 |
+
# quant_config = BaseQuantizeConfig(nbits=8, group_size=128)
|
| 106 |
+
quant_config = BaseQuantizeConfig(nbits=4, group_size=64)
|
| 107 |
+
# quant_config = BaseQuantizeConfig(nbits=3, group_size=64)
|
| 108 |
+
# quant_config = BaseQuantizeConfig(nbits=2, group_size=16, quant_scale=True)
|
| 109 |
+
# Quantize
|
| 110 |
+
model.quantize_model(quant_config=quant_config)
|
| 111 |
+
model = model.half().cuda()
|
| 112 |
+
return model
|
| 113 |
+
|
| 114 |
+
|
| 115 |
+
def compare(model_id):
|
| 116 |
+
model = create_and_quant_model(model_id)
|
| 117 |
+
_, model_ref, preprocess = create_model_dual(model_id, mask=2)
|
| 118 |
+
model.eval()
|
| 119 |
+
model_ref.eval()
|
| 120 |
+
|
| 121 |
+
descriptions = {
|
| 122 |
+
"page": "a page of text about segmentation",
|
| 123 |
+
"chelsea": "a facial photo of a tabby cat",
|
| 124 |
+
"astronaut": "a portrait of an astronaut with the American flag",
|
| 125 |
+
"rocket": "a rocket standing on a launchpad",
|
| 126 |
+
"motorcycle_right": "a red motorcycle standing in a garage",
|
| 127 |
+
"camera": "a person looking at a camera on a tripod",
|
| 128 |
+
"horse": "a black-and-white silhouette of a horse",
|
| 129 |
+
"coffee": "a cup of coffee on a saucer",
|
| 130 |
+
}
|
| 131 |
+
texts = descriptions.values()
|
| 132 |
+
text_processed = tokenizer.tokenize(texts).cuda()
|
| 133 |
+
|
| 134 |
+
# preprocess image and text
|
| 135 |
+
img = Image.open(os.path.join(data_dir, "astronaut.png")).convert("RGB")
|
| 136 |
+
img_preprocessed = preprocess(img).cuda().unsqueeze(0)
|
| 137 |
+
|
| 138 |
+
with torch.amp.autocast("cuda"):
|
| 139 |
+
img_embedding, text_embedding, _ = model_ref(img_preprocessed, text_processed)
|
| 140 |
+
probs = (100 * img_embedding @ text_embedding.T).softmax(dim=-1)
|
| 141 |
+
print(probs)
|
| 142 |
+
|
| 143 |
+
with torch.amp.autocast("cuda"):
|
| 144 |
+
img_embedding, text_embedding, _ = model(img_preprocessed, text_processed)
|
| 145 |
+
probs = (100 * img_embedding @ text_embedding.T).softmax(dim=-1)
|
| 146 |
+
print(probs)
|
| 147 |
+
|
| 148 |
+
|
| 149 |
+
def main():
|
| 150 |
+
# quant_models(model_ids)
|
| 151 |
+
# load_quantized(model_ids[0])
|
| 152 |
+
compare(model_ids[2])
|
| 153 |
+
# compare_weights(model_ids[2])
|
| 154 |
+
# compare_weights_raw(model_ids[2])
|
| 155 |
+
|
| 156 |
+
|
| 157 |
+
if __name__ == "__main__":
|
| 158 |
+
main()
|
lm-quant-toolkit/.deps/hqq/examples/timm/vit_clip_example.py
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
import timm
|
| 3 |
+
import torch
|
| 4 |
+
|
| 5 |
+
from hqq.core.quantize import BaseQuantizeConfig
|
| 6 |
+
from hqq.engine.timm import HQQtimm
|
| 7 |
+
|
| 8 |
+
# Model
|
| 9 |
+
# model_id = 'vit_base_patch32_clip_224.laion2b' #ViT-B-32
|
| 10 |
+
# model_id = 'vit_large_patch14_clip_224.laion2b'#ViT-L-14
|
| 11 |
+
model_id = "vit_huge_patch14_clip_224.laion2b" # ViT-H-14
|
| 12 |
+
|
| 13 |
+
# Load model (on CPU)
|
| 14 |
+
model = HQQtimm.create_model(model_id, pretrained=True)
|
| 15 |
+
|
| 16 |
+
# Quantize settings
|
| 17 |
+
# quant_config = BaseQuantizeConfig(nbits=8, group_size=128)
|
| 18 |
+
quant_config = BaseQuantizeConfig(nbits=4, group_size=64)
|
| 19 |
+
# quant_config = BaseQuantizeConfig(nbits=3, group_size=64)
|
| 20 |
+
# quant_config = BaseQuantizeConfig(nbits=2, group_size=16, quant_scale=True)
|
| 21 |
+
|
| 22 |
+
# Quantize
|
| 23 |
+
model.quantize_model(quant_config=quant_config)
|
| 24 |
+
|
| 25 |
+
###############################################################
|
| 26 |
+
# #Save model
|
| 27 |
+
# save_dir = "repo/" + model_id
|
| 28 |
+
# model.save_quantized(model, save_dir=save_dir)
|
| 29 |
+
|
| 30 |
+
# #Load model
|
| 31 |
+
# model = HQQtimm.from_quantized(save_dir)
|
| 32 |
+
###############################################################
|
| 33 |
+
|
| 34 |
+
# Load reference model to compare with
|
| 35 |
+
model_ref = timm.create_model(model_id, pretrained=True)
|
| 36 |
+
model_ref = model_ref.half().cuda()
|
| 37 |
+
model_ref.eval()
|
| 38 |
+
|
| 39 |
+
# Pre-processing
|
| 40 |
+
mean_clip = np.array([0.4815, 0.4578, 0.4082], "float32")
|
| 41 |
+
std_clip = np.array([0.2686, 0.2613, 0.2758], "float32")
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
def normalize_images_clip(data_np_in, BCHW=True):
|
| 45 |
+
data_t = (
|
| 46 |
+
torch.from_numpy(data_np_in).float()
|
| 47 |
+
if (type(data_np_in) is np.ndarray)
|
| 48 |
+
else data_np_in.float()
|
| 49 |
+
)
|
| 50 |
+
data_t = (data_t / 255.0 - mean_clip) / std_clip
|
| 51 |
+
data_t = data_t.swapaxes(2, 3).swapaxes(1, 2) if (BCHW) else data_t
|
| 52 |
+
return data_t
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
###############################################################
|
| 56 |
+
# Compare the compressed model with the original
|
| 57 |
+
x = np.random.rand(16, 224, 224, 3)
|
| 58 |
+
x = normalize_images_clip(x).half().cuda()
|
| 59 |
+
|
| 60 |
+
# Quantize
|
| 61 |
+
with torch.no_grad():
|
| 62 |
+
y_q = model(x)
|
| 63 |
+
y_q /= torch.norm(y_q, p=2, dim=-1, keepdim=True)
|
| 64 |
+
|
| 65 |
+
# Full-precision
|
| 66 |
+
with torch.no_grad():
|
| 67 |
+
y_r = model_ref(x)
|
| 68 |
+
y_r /= torch.norm(y_r, p=2, dim=-1, keepdim=True)
|
| 69 |
+
|
| 70 |
+
# We want the dot product to be as close as possible to 1
|
| 71 |
+
print(
|
| 72 |
+
"Average dot-product score", float(torch.diag(torch.matmul(y_q, y_r.t())).mean())
|
| 73 |
+
) # ~0.998 (ViT-H-14 @4bit)
|
lm-quant-toolkit/.deps/hqq/examples/vllm/llama2_example.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
model_id = 'meta-llama/Llama-2-7b-chat-hf'
|
| 2 |
+
|
| 3 |
+
#Load VLLM un-quantized model
|
| 4 |
+
from hqq.engine.vllm import HQQLLM
|
| 5 |
+
model = HQQLLM(model=model_id)
|
| 6 |
+
|
| 7 |
+
#Quantize the model
|
| 8 |
+
from hqq.core.quantize import *
|
| 9 |
+
model.quantize_model(BaseQuantizeConfig(nbits=4, group_size=64))
|
| 10 |
+
|
| 11 |
+
#Optional: Save the model
|
| 12 |
+
#model.save_quantized(model_id.split('/')[-1] + '_quantized')
|
| 13 |
+
|
| 14 |
+
#Optional: Set backend
|
| 15 |
+
HQQLinear.set_backend(HQQBackend.PYTORCH_COMPILE) #set backend
|
| 16 |
+
|
| 17 |
+
#Generation
|
| 18 |
+
from vllm.entrypoints.llm import SamplingParams
|
| 19 |
+
sampling_params = SamplingParams(temperature=0.6, top_p=0.90, max_tokens=1000, repetition_penalty=1.2)
|
| 20 |
+
|
| 21 |
+
prompt = "How can I build a car?"
|
| 22 |
+
|
| 23 |
+
output = model.generate([prompt], sampling_params)[0]
|
| 24 |
+
print(output.prompt)
|
| 25 |
+
print(output.outputs[0].text)
|
lm-quant-toolkit/.deps/hqq/hqq.egg-info/PKG-INFO
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Metadata-Version: 2.4
|
| 2 |
+
Name: hqq
|
| 3 |
+
Version: 0.1.7.post2
|
| 4 |
+
Summary: Half-Quadratic Quantization (HQQ)
|
| 5 |
+
Home-page: https://github.com/mobiusml/hqq/
|
| 6 |
+
Author: Dr. Hicham Badri
|
| 7 |
+
Author-email: hicham@mobiuslabs.com
|
| 8 |
+
License: Apache 2
|
| 9 |
+
License-File: LICENSE
|
| 10 |
+
Requires-Dist: numpy>=1.24.4
|
| 11 |
+
Requires-Dist: tqdm>=4.64.1
|
| 12 |
+
Requires-Dist: einops
|
| 13 |
+
Requires-Dist: accelerate
|
| 14 |
+
Requires-Dist: transformers>=4.36.1
|
| 15 |
+
Requires-Dist: huggingface_hub
|
| 16 |
+
Requires-Dist: termcolor
|
| 17 |
+
Dynamic: author
|
| 18 |
+
Dynamic: author-email
|
| 19 |
+
Dynamic: home-page
|
| 20 |
+
Dynamic: license
|
| 21 |
+
Dynamic: license-file
|
| 22 |
+
Dynamic: requires-dist
|
| 23 |
+
Dynamic: summary
|
lm-quant-toolkit/.deps/hqq/hqq.egg-info/SOURCES.txt
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
LICENSE
|
| 2 |
+
setup.py
|
| 3 |
+
hqq/__init__.py
|
| 4 |
+
hqq.egg-info/PKG-INFO
|
| 5 |
+
hqq.egg-info/SOURCES.txt
|
| 6 |
+
hqq.egg-info/dependency_links.txt
|
| 7 |
+
hqq.egg-info/requires.txt
|
| 8 |
+
hqq.egg-info/top_level.txt
|
| 9 |
+
hqq/backends/__init__.py
|
| 10 |
+
hqq/backends/marlin.py
|
| 11 |
+
hqq/backends/torchao.py
|
| 12 |
+
hqq/core/__init__.py
|
| 13 |
+
hqq/core/bitpack.py
|
| 14 |
+
hqq/core/optimize.py
|
| 15 |
+
hqq/core/peft.py
|
| 16 |
+
hqq/core/quantize.py
|
| 17 |
+
hqq/core/utils.py
|
| 18 |
+
hqq/engine/__init__.py
|
| 19 |
+
hqq/engine/base.py
|
| 20 |
+
hqq/engine/hf.py
|
| 21 |
+
hqq/engine/open_clip.py
|
| 22 |
+
hqq/engine/timm.py
|
| 23 |
+
hqq/engine/vllm.py
|
| 24 |
+
hqq/kernels/__init__.py
|
| 25 |
+
hqq/kernels/hqq_aten_cuda.cpp
|
| 26 |
+
hqq/kernels/hqq_aten_cuda_kernel.cu
|
| 27 |
+
hqq/kernels/hqq_aten_torch.cpp
|
| 28 |
+
hqq/kernels/setup_cuda.py
|
| 29 |
+
hqq/kernels/setup_torch.py
|
| 30 |
+
hqq/models/__init__.py
|
| 31 |
+
hqq/models/base.py
|
| 32 |
+
hqq/models/hf/__init__.py
|
| 33 |
+
hqq/models/hf/base.py
|
| 34 |
+
hqq/models/hf/llama.py
|
| 35 |
+
hqq/models/hf/mistral.py
|
| 36 |
+
hqq/models/hf/mixtral.py
|
| 37 |
+
hqq/models/hf/phi.py
|
| 38 |
+
hqq/models/hf/phi_opt.py
|
| 39 |
+
hqq/models/open_clip/__init__.py
|
| 40 |
+
hqq/models/open_clip/base.py
|
| 41 |
+
hqq/models/open_clip/vit_clip.py
|
| 42 |
+
hqq/models/timm/__init__.py
|
| 43 |
+
hqq/models/timm/base.py
|
| 44 |
+
hqq/models/timm/vit_clip.py
|
| 45 |
+
hqq/models/vllm/__init__.py
|
| 46 |
+
hqq/models/vllm/base.py
|
| 47 |
+
hqq/models/vllm/llama.py
|
| 48 |
+
hqq/utils/__init__.py
|
| 49 |
+
hqq/utils/generation_hf.py
|
| 50 |
+
hqq/utils/optimizer.py
|
| 51 |
+
hqq/utils/patching.py
|
| 52 |
+
tests/test_bitpack.py
|
| 53 |
+
tests/test_quantize.py
|
lm-quant-toolkit/.deps/hqq/hqq.egg-info/dependency_links.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
|
lm-quant-toolkit/.deps/hqq/hqq.egg-info/requires.txt
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
numpy>=1.24.4
|
| 2 |
+
tqdm>=4.64.1
|
| 3 |
+
einops
|
| 4 |
+
accelerate
|
| 5 |
+
transformers>=4.36.1
|
| 6 |
+
huggingface_hub
|
| 7 |
+
termcolor
|
lm-quant-toolkit/.deps/hqq/hqq.egg-info/top_level.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
hqq
|
lm-quant-toolkit/.deps/hqq/hqq/__init__.py
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
__version__ = "0.1.7.post2"
|
| 2 |
+
__author__ = 'Dr. Hicham Badri'
|
| 3 |
+
__credits__ = 'Mobius Labs GmbH'
|
lm-quant-toolkit/.deps/hqq/hqq/__pycache__/__init__.cpython-311.pyc
ADDED
|
Binary file (297 Bytes). View file
|
|
|
lm-quant-toolkit/.deps/hqq/hqq/backends/__init__.py
ADDED
|
File without changes
|
lm-quant-toolkit/.deps/hqq/hqq/backends/marlin.py
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Written by Dr. Hicham Badri @Mobius Labs GmbH - 2024
|
| 2 |
+
#####################################################
|
| 3 |
+
import torch
|
| 4 |
+
|
| 5 |
+
try:
|
| 6 |
+
import marlin
|
| 7 |
+
except Exception:
|
| 8 |
+
marlin = None
|
| 9 |
+
from ..core.quantize import Quantizer
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
class MarlinLinear(torch.nn.Module):
|
| 13 |
+
def __init__(
|
| 14 |
+
self, W: torch.Tensor, scales: torch.Tensor, u=None, bias=None, groupsize=-1
|
| 15 |
+
):
|
| 16 |
+
super().__init__()
|
| 17 |
+
|
| 18 |
+
m, n = W.shape
|
| 19 |
+
device = W.device
|
| 20 |
+
_linear = torch.nn.Linear(m, n)
|
| 21 |
+
_linear.weight.data = W.half().t()
|
| 22 |
+
|
| 23 |
+
effective_groupsize = m if (groupsize == -1) else groupsize
|
| 24 |
+
|
| 25 |
+
_layer = marlin.Layer(m, n, groupsize=groupsize)
|
| 26 |
+
_layer.k = m
|
| 27 |
+
_layer.n = n
|
| 28 |
+
_layer.groupsize = effective_groupsize
|
| 29 |
+
_layer.B = torch.empty((m // 16, n * 16 // 8), dtype=torch.int, device=device)
|
| 30 |
+
_layer.s = torch.empty(
|
| 31 |
+
(m // effective_groupsize, n), dtype=torch.half, device=device
|
| 32 |
+
)
|
| 33 |
+
_layer.pack(_linear, scales.t())
|
| 34 |
+
|
| 35 |
+
self.bias = bias.half() if (bias is not None) else None
|
| 36 |
+
self.Wq_packed = _layer.B.clone()
|
| 37 |
+
self.scales = _layer.s.clone()
|
| 38 |
+
self.workspace_fp = torch.zeros(n // 128 * 16, device=device)
|
| 39 |
+
self.in_features = m
|
| 40 |
+
self.out_features = n
|
| 41 |
+
self.group_size = effective_groupsize
|
| 42 |
+
self.axis = 1
|
| 43 |
+
self.device = device
|
| 44 |
+
self.compute_dtype = torch.float16
|
| 45 |
+
self.u = torch.nn.Parameter(u, requires_grad=False) if (u is not None) else None
|
| 46 |
+
|
| 47 |
+
del _linear, _layer
|
| 48 |
+
torch.cuda.empty_cache()
|
| 49 |
+
|
| 50 |
+
@torch.no_grad()
|
| 51 |
+
def matmul(self, x):
|
| 52 |
+
out = torch.empty(
|
| 53 |
+
x.shape[:-1] + (self.scales.shape[1],), dtype=x.dtype, device=x.device
|
| 54 |
+
)
|
| 55 |
+
marlin.mul(
|
| 56 |
+
x.view((-1, x.shape[-1])),
|
| 57 |
+
self.Wq_packed,
|
| 58 |
+
out.view((-1, out.shape[-1])),
|
| 59 |
+
self.scales,
|
| 60 |
+
self.workspace_fp,
|
| 61 |
+
)
|
| 62 |
+
return out
|
| 63 |
+
|
| 64 |
+
@torch.jit.ignore
|
| 65 |
+
def forward(self, x):
|
| 66 |
+
out = self.matmul(x)
|
| 67 |
+
|
| 68 |
+
if self.u is not None:
|
| 69 |
+
out += torch.matmul(x.sum(axis=-1, keepdim=True), self.u)
|
| 70 |
+
|
| 71 |
+
if self.bias is not None:
|
| 72 |
+
out += self.bias
|
| 73 |
+
|
| 74 |
+
return out
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
# ONLY WORKS WITH AXIS=1, group_size= - 1
|
| 78 |
+
def patch_hqq_to_marlin(layer, patch_params):
|
| 79 |
+
if marlin is None:
|
| 80 |
+
return layer
|
| 81 |
+
|
| 82 |
+
z_shift = 8.0
|
| 83 |
+
hqq_layer = layer.linear_layer if hasattr(layer, "linear_layer") else layer
|
| 84 |
+
|
| 85 |
+
# Check config suppport
|
| 86 |
+
if (
|
| 87 |
+
(hqq_layer.meta["axis"] == 0)
|
| 88 |
+
or (hqq_layer.meta["group_size"] is not None)
|
| 89 |
+
or (hqq_layer.meta["nbits"] != 4)
|
| 90 |
+
):
|
| 91 |
+
print("Skipping marlin conversion for", hqq_layer.name)
|
| 92 |
+
return layer
|
| 93 |
+
|
| 94 |
+
W_r = Quantizer.unpack[hqq_layer.meta["packing"]](
|
| 95 |
+
hqq_layer.W_q, dtype=hqq_layer.compute_dtype
|
| 96 |
+
).t()
|
| 97 |
+
z = hqq_layer.meta["zero"]
|
| 98 |
+
s = hqq_layer.meta["scale"].t()
|
| 99 |
+
W_r = (W_r - z_shift) * s
|
| 100 |
+
|
| 101 |
+
if type(z) in [torch.Tensor, torch.nn.Parameter]:
|
| 102 |
+
z = z.t()
|
| 103 |
+
u = (s * (-z + z_shift)).view([1, -1])
|
| 104 |
+
else:
|
| 105 |
+
u = None
|
| 106 |
+
|
| 107 |
+
marlin_layer = MarlinLinear(W_r, s, u=u, bias=hqq_layer.bias)
|
| 108 |
+
|
| 109 |
+
if hasattr(layer, "linear_layer"):
|
| 110 |
+
del layer.linear_layer
|
| 111 |
+
layer.linear_layer = marlin_layer
|
| 112 |
+
else:
|
| 113 |
+
del hqq_layer
|
| 114 |
+
layer = marlin_layer
|
| 115 |
+
|
| 116 |
+
torch.cuda.empty_cache()
|
| 117 |
+
|
| 118 |
+
return layer
|
lm-quant-toolkit/.deps/hqq/hqq/backends/torchao.py
ADDED
|
@@ -0,0 +1,388 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Written by Dr. Hicham Badri @Mobius Labs GmbH - 2024
|
| 2 |
+
#####################################################
|
| 3 |
+
|
| 4 |
+
# Makes HQQ 4-bit (axis=1) compatbile with torch.ops.aten._weight_int4pack_mm
|
| 5 |
+
|
| 6 |
+
# The code is partially based on: https://github.com/pytorch-labs/gpt-fast/blob/main/quantize.py (BSD-3-Clause license)
|
| 7 |
+
# Only the packing logic is copied, the rest is rewritten to support HQQ's logic
|
| 8 |
+
|
| 9 |
+
# Only works with: bfloat16, axis=1.
|
| 10 |
+
# Only tested on Ada gpus.
|
| 11 |
+
|
| 12 |
+
import torch
|
| 13 |
+
import copy
|
| 14 |
+
from torch import bfloat16, nn, Tensor
|
| 15 |
+
import torch.nn.functional as F
|
| 16 |
+
from typing import Union
|
| 17 |
+
|
| 18 |
+
from ..core.quantize import HQQLinear, Quantizer
|
| 19 |
+
from ..core.peft import HQQLinearLoRA
|
| 20 |
+
from ..core.utils import cleanup
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
class HQQLinearTorchWeightOnlynt4(torch.nn.Module):
|
| 24 |
+
def __init__(
|
| 25 |
+
self,
|
| 26 |
+
linear_layer: Union[nn.Module, None],
|
| 27 |
+
quant_config: dict,
|
| 28 |
+
del_orig: bool = True,
|
| 29 |
+
compute_dtype: torch.dtype = bfloat16,
|
| 30 |
+
device: str = "cuda",
|
| 31 |
+
initialize: bool = True,
|
| 32 |
+
inner_k_tiles=8,
|
| 33 |
+
padding=True,
|
| 34 |
+
):
|
| 35 |
+
super().__init__()
|
| 36 |
+
|
| 37 |
+
self.ready = False
|
| 38 |
+
self.in_gpu = False
|
| 39 |
+
self.bias = None
|
| 40 |
+
self.device = device
|
| 41 |
+
self.compute_dtype = compute_dtype
|
| 42 |
+
self.quant_config = (
|
| 43 |
+
copy.deepcopy(quant_config) if (quant_config is not None) else None
|
| 44 |
+
)
|
| 45 |
+
self.del_orig = del_orig
|
| 46 |
+
|
| 47 |
+
if (quant_config is None) and (linear_layer is None):
|
| 48 |
+
raise Exception(
|
| 49 |
+
"Invalid parameters: Both quant_config and linear_layer are None."
|
| 50 |
+
)
|
| 51 |
+
|
| 52 |
+
if (quant_config is None) and (initialize is True):
|
| 53 |
+
raise Exception(
|
| 54 |
+
"Invalid parameters: setting initialize to True requires a quant_config."
|
| 55 |
+
)
|
| 56 |
+
|
| 57 |
+
if self.quant_config is not None:
|
| 58 |
+
weight_quant_params = self.quant_config["weight_quant_params"]
|
| 59 |
+
self.groupsize = weight_quant_params["group_size"]
|
| 60 |
+
self.nbits = weight_quant_params["nbits"]
|
| 61 |
+
self.axis = weight_quant_params["axis"]
|
| 62 |
+
|
| 63 |
+
if linear_layer is not None:
|
| 64 |
+
self.groupsize = linear_layer.meta["group_size"]
|
| 65 |
+
self.nbits = linear_layer.meta["nbits"]
|
| 66 |
+
self.axis = linear_layer.meta["axis"]
|
| 67 |
+
|
| 68 |
+
self.inner_k_tiles = inner_k_tiles
|
| 69 |
+
self.padding = padding
|
| 70 |
+
|
| 71 |
+
assert self.axis==1, "Only axis==1 is supported"
|
| 72 |
+
assert self.nbits in [4], "Unsupported nbits."
|
| 73 |
+
assert (
|
| 74 |
+
self.compute_dtype is bfloat16
|
| 75 |
+
), "Only bfloat16 compute_dtype is supported."
|
| 76 |
+
assert self.groupsize in [None, 32, 64, 128, 256], "Unsupported groupsize."
|
| 77 |
+
assert self.inner_k_tiles in [2, 4, 8], "Unsupported tile."
|
| 78 |
+
|
| 79 |
+
self.linear_layer = linear_layer
|
| 80 |
+
|
| 81 |
+
if initialize:
|
| 82 |
+
self.initialize()
|
| 83 |
+
|
| 84 |
+
###################### Initializers ######################
|
| 85 |
+
def initialize_with_hqq_quants(self, W_q, meta, bias=None):
|
| 86 |
+
self.padding = (
|
| 87 |
+
False # Force padding off, a bit tricky to post-pad with grouping
|
| 88 |
+
)
|
| 89 |
+
|
| 90 |
+
self.set_shape(meta["shape"])
|
| 91 |
+
self.process_hqq_quants(W_q, meta)
|
| 92 |
+
self.bias = bias
|
| 93 |
+
self.ready = True
|
| 94 |
+
self.in_gpu = True
|
| 95 |
+
torch.cuda.empty_cache()
|
| 96 |
+
|
| 97 |
+
return self
|
| 98 |
+
|
| 99 |
+
def initialize(self):
|
| 100 |
+
if self.linear_layer is not None:
|
| 101 |
+
W = self.linear_layer.weight.data
|
| 102 |
+
self.set_shape(W.shape)
|
| 103 |
+
|
| 104 |
+
if self.in_features_diff > 0:
|
| 105 |
+
W = F.pad(W, pad=(0, self.in_features_diff), value=0)
|
| 106 |
+
|
| 107 |
+
W_q, meta = self.quantize(W, **self.quant_config)
|
| 108 |
+
self.process_hqq_quants(W_q, meta)
|
| 109 |
+
del W_q, meta
|
| 110 |
+
|
| 111 |
+
self.bias = (
|
| 112 |
+
None
|
| 113 |
+
if (self.linear_layer.bias is None)
|
| 114 |
+
else self.linear_layer.bias.to(
|
| 115 |
+
dtype=self.compute_dtype, device=self.device
|
| 116 |
+
)
|
| 117 |
+
)
|
| 118 |
+
|
| 119 |
+
if self.del_orig:
|
| 120 |
+
del self.linear_layer
|
| 121 |
+
|
| 122 |
+
self.ready = True
|
| 123 |
+
self.in_gpu = True
|
| 124 |
+
torch.cuda.empty_cache()
|
| 125 |
+
|
| 126 |
+
return self
|
| 127 |
+
|
| 128 |
+
###################### Quantize/packing ######################
|
| 129 |
+
|
| 130 |
+
def quantize(
|
| 131 |
+
self,
|
| 132 |
+
W: Tensor,
|
| 133 |
+
weight_quant_params: dict,
|
| 134 |
+
scale_quant_params=Union[dict,None],
|
| 135 |
+
zero_quant_params=Union[dict,None],
|
| 136 |
+
offload_meta=False,
|
| 137 |
+
):
|
| 138 |
+
W_q, meta = Quantizer.quantize(
|
| 139 |
+
W,
|
| 140 |
+
**weight_quant_params,
|
| 141 |
+
device=self.device,
|
| 142 |
+
compute_dtype=self.compute_dtype,
|
| 143 |
+
bitpack=False,
|
| 144 |
+
)
|
| 145 |
+
|
| 146 |
+
# ToDO: meta quantization
|
| 147 |
+
|
| 148 |
+
return W_q, meta
|
| 149 |
+
|
| 150 |
+
# TODO: move these to utils
|
| 151 |
+
@torch.no_grad()
|
| 152 |
+
def reshape_meta_axis1(self, meta_tensor, new_group_size, shape):
|
| 153 |
+
meta_tensor = meta_tensor.repeat([1, shape[1]]).reshape(shape)
|
| 154 |
+
meta_tensor = torch.mean(
|
| 155 |
+
meta_tensor.reshape([-1, new_group_size]), axis=1, keepdim=True
|
| 156 |
+
)
|
| 157 |
+
return meta_tensor
|
| 158 |
+
|
| 159 |
+
def find_multiple(self, n: int, k: int) -> int:
|
| 160 |
+
if n % k == 0:
|
| 161 |
+
return n
|
| 162 |
+
return n + k - (n % k)
|
| 163 |
+
|
| 164 |
+
def set_shape(self, shape):
|
| 165 |
+
self.shape = shape
|
| 166 |
+
self.in_features = shape[1]
|
| 167 |
+
self.out_features = shape[0]
|
| 168 |
+
|
| 169 |
+
self.origin_in_features = self.in_features
|
| 170 |
+
if self.padding:
|
| 171 |
+
self.in_features = self.find_multiple(self.in_features, 1024)
|
| 172 |
+
|
| 173 |
+
self.in_features_diff = self.in_features - self.origin_in_features
|
| 174 |
+
|
| 175 |
+
@torch.no_grad()
|
| 176 |
+
def process_hqq_quants(self, W_q, meta):
|
| 177 |
+
scales = meta["scale"]
|
| 178 |
+
zeros = meta["zero"]
|
| 179 |
+
shape = meta["shape"]
|
| 180 |
+
|
| 181 |
+
if meta["packing"] is not None:
|
| 182 |
+
W_q = Quantizer.unpack[meta["packing"]](W_q)
|
| 183 |
+
|
| 184 |
+
if self.groupsize is None:
|
| 185 |
+
self.groupsize = 128
|
| 186 |
+
W_q = W_q.reshape([-1, self.groupsize])
|
| 187 |
+
scales = self.reshape_meta_axis1(scales, self.groupsize, shape)
|
| 188 |
+
zeros = self.reshape_meta_axis1(zeros, self.groupsize, shape)
|
| 189 |
+
|
| 190 |
+
W_q_torch, scales_torch, zeros_torch = self.hqq_quants_to_torch_quants(
|
| 191 |
+
W_q=W_q, scales=scales, zeros=zeros, shape=shape, nbits=self.nbits
|
| 192 |
+
)
|
| 193 |
+
self.weight_int4pack = torch.ops.aten._convert_weight_to_int4pack(
|
| 194 |
+
W_q_torch, self.inner_k_tiles
|
| 195 |
+
)
|
| 196 |
+
self.scales_and_zeros = self.pack_scales_and_zeros(scales_torch, zeros_torch)
|
| 197 |
+
|
| 198 |
+
del W_q_torch, scales_torch, zeros_torch
|
| 199 |
+
torch.cuda.empty_cache()
|
| 200 |
+
|
| 201 |
+
@torch.no_grad()
|
| 202 |
+
def hqq_quants_to_torch_quants(
|
| 203 |
+
self, W_q: Tensor, scales: Tensor, zeros: Tensor, shape, nbits=4
|
| 204 |
+
):
|
| 205 |
+
W_q = W_q.to(dtype=self.compute_dtype, device=self.device)
|
| 206 |
+
scales = scales.to(dtype=self.compute_dtype, device=self.device)
|
| 207 |
+
zeros = zeros.to(dtype=self.compute_dtype, device=self.device)
|
| 208 |
+
|
| 209 |
+
max_int = 2**nbits - 1
|
| 210 |
+
min_int = 0
|
| 211 |
+
dump = 2 ** (nbits - 1)
|
| 212 |
+
|
| 213 |
+
# HQQ -> torch logic
|
| 214 |
+
new_zeros = (scales * dump) - zeros * scales
|
| 215 |
+
|
| 216 |
+
min_val = new_zeros - scales * dump
|
| 217 |
+
|
| 218 |
+
# group_quantize_tensor_from_qparams
|
| 219 |
+
W_r = (W_q - zeros) * scales
|
| 220 |
+
|
| 221 |
+
W_q = (
|
| 222 |
+
W_r.sub(min_val)
|
| 223 |
+
.div(scales)
|
| 224 |
+
.round()
|
| 225 |
+
.clamp_(min_int, max_int)
|
| 226 |
+
.to(torch.int32)
|
| 227 |
+
.reshape(shape)
|
| 228 |
+
.contiguous()
|
| 229 |
+
)
|
| 230 |
+
|
| 231 |
+
# group_dequantize_tensor_from_qparams
|
| 232 |
+
# W_r = W_q*scales + min_val
|
| 233 |
+
|
| 234 |
+
scales = scales.contiguous().reshape(shape[0], -1)
|
| 235 |
+
new_zeros = new_zeros.contiguous().reshape(shape[0], -1)
|
| 236 |
+
|
| 237 |
+
return W_q, scales, new_zeros
|
| 238 |
+
|
| 239 |
+
def pack_scales_and_zeros(self, scales, zeros):
|
| 240 |
+
assert scales.shape == zeros.shape
|
| 241 |
+
assert scales.dtype == bfloat16
|
| 242 |
+
assert zeros.dtype == bfloat16
|
| 243 |
+
return (
|
| 244 |
+
torch.cat(
|
| 245 |
+
[
|
| 246 |
+
scales.reshape(scales.size(0), scales.size(1), 1),
|
| 247 |
+
zeros.reshape(zeros.size(0), zeros.size(1), 1),
|
| 248 |
+
],
|
| 249 |
+
2,
|
| 250 |
+
)
|
| 251 |
+
.transpose(0, 1)
|
| 252 |
+
.contiguous()
|
| 253 |
+
)
|
| 254 |
+
|
| 255 |
+
###################### Forward/matmul ######################
|
| 256 |
+
|
| 257 |
+
# @torch.jit.ignore()
|
| 258 |
+
def matmul(self, x: Tensor) -> Tensor:
|
| 259 |
+
origin_x_size = x.size()
|
| 260 |
+
x = x.reshape(-1, origin_x_size[-1])
|
| 261 |
+
c = torch.ops.aten._weight_int4pack_mm(
|
| 262 |
+
x, self.weight_int4pack, self.groupsize, self.scales_and_zeros
|
| 263 |
+
)
|
| 264 |
+
new_shape = origin_x_size[:-1] + (self.out_features,)
|
| 265 |
+
c = c.reshape(new_shape)
|
| 266 |
+
return c
|
| 267 |
+
|
| 268 |
+
# TODO without matmul
|
| 269 |
+
def dequantize(self) -> Tensor:
|
| 270 |
+
return self.matmul(
|
| 271 |
+
torch.eye(self.in_features, dtype=self.compute_dtype, device=self.device)
|
| 272 |
+
)[: self.origin_in_features].t()
|
| 273 |
+
|
| 274 |
+
# TODO: backward
|
| 275 |
+
def forward(self, x: Tensor) -> Tensor:
|
| 276 |
+
if self.in_features_diff > 0:
|
| 277 |
+
x = F.pad(x, pad=(0, self.in_features_diff))
|
| 278 |
+
|
| 279 |
+
out = self.matmul(x)
|
| 280 |
+
|
| 281 |
+
if self.bias is not None:
|
| 282 |
+
out += self.bias
|
| 283 |
+
return out
|
| 284 |
+
|
| 285 |
+
|
| 286 |
+
###################### Patching ######################
|
| 287 |
+
def patch_linearlayers(model, fct, patch_param=None):
|
| 288 |
+
model.base_class.patch_linearlayers(
|
| 289 |
+
model,
|
| 290 |
+
fct,
|
| 291 |
+
{lin_tag: patch_param for lin_tag in model.base_class.get_linear_tags()},
|
| 292 |
+
)
|
| 293 |
+
|
| 294 |
+
|
| 295 |
+
def patch_hqq_to_aoint4(layer, patch_params):
|
| 296 |
+
hqq_layer = None
|
| 297 |
+
if type(layer) is HQQLinear:
|
| 298 |
+
hqq_layer = layer
|
| 299 |
+
if type(layer) is HQQLinearLoRA:
|
| 300 |
+
hqq_layer = layer.linear_layer
|
| 301 |
+
|
| 302 |
+
if hqq_layer is None:
|
| 303 |
+
return layer
|
| 304 |
+
|
| 305 |
+
if hqq_layer.meta["nbits"] != 4 or hqq_layer.meta["axis"] != 1:
|
| 306 |
+
print("Skipping aoint4 conversion for ", hqq_layer.name)
|
| 307 |
+
return layer
|
| 308 |
+
|
| 309 |
+
quant_config = getattr(hqq_layer, "quant_config", None)
|
| 310 |
+
|
| 311 |
+
hqq_aoint4_layer = HQQLinearTorchWeightOnlynt4(
|
| 312 |
+
None,
|
| 313 |
+
quant_config=quant_config,
|
| 314 |
+
compute_dtype=hqq_layer.compute_dtype,
|
| 315 |
+
device=hqq_layer.device,
|
| 316 |
+
del_orig=False,
|
| 317 |
+
initialize=False,
|
| 318 |
+
padding=False,
|
| 319 |
+
)
|
| 320 |
+
hqq_aoint4_layer.initialize_with_hqq_quants(
|
| 321 |
+
hqq_layer.W_q, hqq_layer.meta, hqq_layer.bias
|
| 322 |
+
)
|
| 323 |
+
|
| 324 |
+
del hqq_layer
|
| 325 |
+
torch.cuda.empty_cache()
|
| 326 |
+
|
| 327 |
+
if type(layer) is HQQLinear:
|
| 328 |
+
return hqq_aoint4_layer
|
| 329 |
+
|
| 330 |
+
if type(layer) is HQQLinearLoRA:
|
| 331 |
+
layer.linear_layer = hqq_aoint4_layer
|
| 332 |
+
|
| 333 |
+
return layer
|
| 334 |
+
|
| 335 |
+
|
| 336 |
+
def replace_with_torchInt4(model):
|
| 337 |
+
patch_linearlayers(model, patch_hqq_to_aoint4)
|
| 338 |
+
cleanup()
|
| 339 |
+
|
| 340 |
+
|
| 341 |
+
# Force requantize, mainly to check if the padding with int4mm is faster
|
| 342 |
+
def patch_hqq_to_aoint4_force_requantize(layer, patch_params):
|
| 343 |
+
hqq_layer = None
|
| 344 |
+
if type(layer) is HQQLinear:
|
| 345 |
+
hqq_layer = layer
|
| 346 |
+
if type(layer) is HQQLinearLoRA:
|
| 347 |
+
hqq_layer = layer.linear_layer
|
| 348 |
+
|
| 349 |
+
if hqq_layer is None:
|
| 350 |
+
return layer
|
| 351 |
+
|
| 352 |
+
if hqq_layer.meta["nbits"] != 4 or hqq_layer.meta["axis"] != 1:
|
| 353 |
+
print("Skipping aoint4 conversion for ", hqq_layer.name)
|
| 354 |
+
return layer
|
| 355 |
+
|
| 356 |
+
# Create dummy linear layer to store dequantize weights
|
| 357 |
+
dummy_linear = torch.nn.Linear(1, 1, bias=False)
|
| 358 |
+
dummy_linear.weight.data = hqq_layer.dequantize()
|
| 359 |
+
|
| 360 |
+
# Disable optimizer on already dequantized weights
|
| 361 |
+
quant_config = hqq_layer.quant_config
|
| 362 |
+
quant_config["weight_quant_params"]["optimize"] = False
|
| 363 |
+
|
| 364 |
+
hqq_aoint4_layer = HQQLinearTorchWeightOnlynt4(
|
| 365 |
+
dummy_linear,
|
| 366 |
+
quant_config=quant_config,
|
| 367 |
+
compute_dtype=hqq_layer.compute_dtype,
|
| 368 |
+
device=hqq_layer.device,
|
| 369 |
+
del_orig=True,
|
| 370 |
+
initialize=True,
|
| 371 |
+
padding=True,
|
| 372 |
+
)
|
| 373 |
+
|
| 374 |
+
del hqq_layer
|
| 375 |
+
torch.cuda.empty_cache()
|
| 376 |
+
|
| 377 |
+
if type(layer) is HQQLinear:
|
| 378 |
+
return hqq_aoint4_layer
|
| 379 |
+
|
| 380 |
+
if type(layer) is HQQLinearLoRA:
|
| 381 |
+
layer.linear_layer = hqq_aoint4_layer
|
| 382 |
+
|
| 383 |
+
return layer
|
| 384 |
+
|
| 385 |
+
|
| 386 |
+
def replace_with_torchInt4_force_requantize(model):
|
| 387 |
+
patch_linearlayers(model, patch_hqq_to_aoint4_force_requantize)
|
| 388 |
+
cleanup()
|
lm-quant-toolkit/.deps/hqq/hqq/core/__init__.py
ADDED
|
File without changes
|
lm-quant-toolkit/.deps/hqq/hqq/core/__pycache__/__init__.cpython-311.pyc
ADDED
|
Binary file (197 Bytes). View file
|
|
|
lm-quant-toolkit/.deps/hqq/hqq/core/__pycache__/bitpack.cpython-311.pyc
ADDED
|
Binary file (8.09 kB). View file
|
|
|
lm-quant-toolkit/.deps/hqq/hqq/core/__pycache__/optimize.cpython-311.pyc
ADDED
|
Binary file (21.9 kB). View file
|
|
|
lm-quant-toolkit/.deps/hqq/hqq/core/__pycache__/quantize.cpython-311.pyc
ADDED
|
Binary file (38.7 kB). View file
|
|
|
lm-quant-toolkit/.deps/hqq/hqq/core/__pycache__/utils.cpython-311.pyc
ADDED
|
Binary file (1.64 kB). View file
|
|
|
lm-quant-toolkit/.deps/hqq/hqq/core/bitpack.py
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Written by Dr. Hicham Badri @Mobius Labs GmbH - 2023
|
| 2 |
+
#####################################################
|
| 3 |
+
|
| 4 |
+
import torch
|
| 5 |
+
from torch import uint8, int32, Tensor
|
| 6 |
+
import numpy as np
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
# Bit packing logic. format: pack/unpack_nBits_target-<uint8 or int32>
|
| 10 |
+
class BitPack:
|
| 11 |
+
# 8-bit
|
| 12 |
+
################################################
|
| 13 |
+
@staticmethod
|
| 14 |
+
def pack_8bit_u8(W_q: Tensor) -> Tensor:
|
| 15 |
+
return W_q.to(uint8)
|
| 16 |
+
|
| 17 |
+
@staticmethod
|
| 18 |
+
def unpack_8bit_u8(W_q: Tensor, dtype=uint8) -> Tensor:
|
| 19 |
+
return W_q.to(dtype)
|
| 20 |
+
|
| 21 |
+
# 4-bit
|
| 22 |
+
################################################
|
| 23 |
+
@staticmethod
|
| 24 |
+
def pack_4bit_u8(W_q: Tensor) -> Tensor: # uint8 > uint8/2
|
| 25 |
+
W_q = W_q.to(uint8)
|
| 26 |
+
_step = int(len(W_q) / 2)
|
| 27 |
+
|
| 28 |
+
return (W_q[:_step] << 4) | W_q[_step:]
|
| 29 |
+
|
| 30 |
+
@staticmethod
|
| 31 |
+
def unpack_4bit_u8(W_q: Tensor, dtype=uint8) -> Tensor: # uint8/2 > uint8
|
| 32 |
+
_step = W_q.shape[0]
|
| 33 |
+
tmp = torch.empty([2 * _step, W_q.shape[1]], dtype=dtype, device=W_q.device)
|
| 34 |
+
|
| 35 |
+
tmp[:_step] = (W_q & 0b11110000) >> 4
|
| 36 |
+
tmp[_step:] = W_q & 0b00001111
|
| 37 |
+
|
| 38 |
+
return tmp
|
| 39 |
+
|
| 40 |
+
# 2-bit
|
| 41 |
+
################################################
|
| 42 |
+
@staticmethod
|
| 43 |
+
def pack_2bit_u8(W_q: Tensor) -> Tensor: # uint8 > uint8/4
|
| 44 |
+
W_q = W_q.to(uint8)
|
| 45 |
+
_step = int(len(W_q) / 4)
|
| 46 |
+
|
| 47 |
+
return (
|
| 48 |
+
W_q[:_step] << 6
|
| 49 |
+
| W_q[_step : 2 * _step] << 4
|
| 50 |
+
| W_q[2 * _step : 3 * _step] << 2
|
| 51 |
+
| W_q[3 * _step :]
|
| 52 |
+
)
|
| 53 |
+
|
| 54 |
+
@staticmethod
|
| 55 |
+
def unpack_2bit_u8(W_q: Tensor, dtype=uint8) -> Tensor:
|
| 56 |
+
_step = W_q.shape[0]
|
| 57 |
+
tmp = torch.empty([4 * _step, W_q.shape[1]], dtype=dtype, device=W_q.device)
|
| 58 |
+
|
| 59 |
+
tmp[0 * _step : 1 * _step] = (W_q & 0b11000000) >> 6
|
| 60 |
+
tmp[1 * _step : 2 * _step] = (W_q & 0b00110000) >> 4
|
| 61 |
+
tmp[2 * _step : 3 * _step] = (W_q & 0b00001100) >> 2
|
| 62 |
+
tmp[3 * _step : 4 * _step] = W_q & 0b00000011
|
| 63 |
+
|
| 64 |
+
return tmp
|
| 65 |
+
|
| 66 |
+
# 3-bit
|
| 67 |
+
################################################
|
| 68 |
+
@staticmethod
|
| 69 |
+
def pack_3bit_32(W_q_in: Tensor) -> Tensor:
|
| 70 |
+
W_q = torch.zeros(
|
| 71 |
+
[int(10 * np.ceil(W_q_in.shape[0] / 10.0)), W_q_in.shape[1]],
|
| 72 |
+
device=W_q_in.device,
|
| 73 |
+
dtype=int32,
|
| 74 |
+
)
|
| 75 |
+
W_q[: len(W_q_in)] = W_q_in
|
| 76 |
+
_step = int(len(W_q) / 10)
|
| 77 |
+
|
| 78 |
+
W_q = (
|
| 79 |
+
(W_q[:_step] << 27)
|
| 80 |
+
| (W_q[1 * _step : 2 * _step] << 24)
|
| 81 |
+
| (W_q[2 * _step : 3 * _step] << 21)
|
| 82 |
+
| (W_q[3 * _step : 4 * _step] << 18)
|
| 83 |
+
| (W_q[4 * _step : 5 * _step] << 15)
|
| 84 |
+
| (W_q[5 * _step : 6 * _step] << 12)
|
| 85 |
+
| (W_q[6 * _step : 7 * _step] << 9)
|
| 86 |
+
| (W_q[7 * _step : 8 * _step] << 6)
|
| 87 |
+
| (W_q[8 * _step : 9 * _step] << 3)
|
| 88 |
+
| (W_q[9 * _step : 10 * _step])
|
| 89 |
+
)
|
| 90 |
+
|
| 91 |
+
return W_q
|
| 92 |
+
|
| 93 |
+
# A bit faster than _cat version
|
| 94 |
+
@staticmethod
|
| 95 |
+
def unpack_3bit_32(W_q: Tensor, dtype=uint8) -> Tensor:
|
| 96 |
+
_step = W_q.shape[0]
|
| 97 |
+
tmp = torch.empty([10 * _step, W_q.shape[1]], dtype=dtype, device=W_q.device)
|
| 98 |
+
|
| 99 |
+
tmp[0 * _step : 1 * _step] = (W_q & 0b00111000000000000000000000000000) >> 27
|
| 100 |
+
tmp[1 * _step : 2 * _step] = (W_q & 0b00000111000000000000000000000000) >> 24
|
| 101 |
+
tmp[2 * _step : 3 * _step] = (W_q & 0b00000000111000000000000000000000) >> 21
|
| 102 |
+
tmp[3 * _step : 4 * _step] = (W_q & 0b00000000000111000000000000000000) >> 18
|
| 103 |
+
tmp[4 * _step : 5 * _step] = (W_q & 0b00000000000000111000000000000000) >> 15
|
| 104 |
+
tmp[5 * _step : 6 * _step] = (W_q & 0b00000000000000000111000000000000) >> 12
|
| 105 |
+
tmp[6 * _step : 7 * _step] = (W_q & 0b00000000000000000000111000000000) >> 9
|
| 106 |
+
tmp[7 * _step : 8 * _step] = (W_q & 0b00000000000000000000000111000000) >> 6
|
| 107 |
+
tmp[8 * _step : 9 * _step] = (W_q & 0b00000000000000000000000000111000) >> 3
|
| 108 |
+
tmp[9 * _step : 10 * _step] = W_q & 0b00000000000000000000000000000111
|
| 109 |
+
|
| 110 |
+
return tmp
|
| 111 |
+
|
| 112 |
+
# 1-bit
|
| 113 |
+
################################################
|
| 114 |
+
@staticmethod
|
| 115 |
+
def pack_1bit_u8(W_q: Tensor) -> Tensor:
|
| 116 |
+
W_q = W_q.to(uint8)
|
| 117 |
+
_step = int(len(W_q) / 8)
|
| 118 |
+
|
| 119 |
+
return (
|
| 120 |
+
W_q[:_step] << 7
|
| 121 |
+
| W_q[1 * _step : 2 * _step] << 6
|
| 122 |
+
| W_q[2 * _step : 3 * _step] << 5
|
| 123 |
+
| W_q[3 * _step : 4 * _step] << 4
|
| 124 |
+
| W_q[4 * _step : 5 * _step] << 3
|
| 125 |
+
| W_q[5 * _step : 6 * _step] << 2
|
| 126 |
+
| W_q[6 * _step : 7 * _step] << 1
|
| 127 |
+
| W_q[7 * _step : 8 * _step]
|
| 128 |
+
)
|
| 129 |
+
|
| 130 |
+
@staticmethod
|
| 131 |
+
def unpack_1bit_u8(W_q: Tensor, dtype=uint8) -> Tensor:
|
| 132 |
+
_step = W_q.shape[0]
|
| 133 |
+
tmp = torch.empty([8 * _step, W_q.shape[1]], dtype=dtype, device=W_q.device)
|
| 134 |
+
|
| 135 |
+
tmp[0 * _step : 1 * _step] = (W_q & 0b10000000) >> 7
|
| 136 |
+
tmp[1 * _step : 2 * _step] = (W_q & 0b01000000) >> 6
|
| 137 |
+
tmp[2 * _step : 3 * _step] = (W_q & 0b00100000) >> 5
|
| 138 |
+
tmp[3 * _step : 4 * _step] = (W_q & 0b00010000) >> 4
|
| 139 |
+
tmp[4 * _step : 5 * _step] = (W_q & 0b00001000) >> 3
|
| 140 |
+
tmp[5 * _step : 6 * _step] = (W_q & 0b00000100) >> 2
|
| 141 |
+
tmp[6 * _step : 7 * _step] = (W_q & 0b00000010) >> 1
|
| 142 |
+
tmp[7 * _step : 8 * _step] = W_q & 0b00000001
|
| 143 |
+
|
| 144 |
+
return tmp
|
lm-quant-toolkit/.deps/hqq/hqq/core/optimize.py
ADDED
|
@@ -0,0 +1,476 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Written by Dr. Hicham Badri @Mobius Labs GmbH - 2023
|
| 2 |
+
#####################################################
|
| 3 |
+
import torch
|
| 4 |
+
import numpy as np
|
| 5 |
+
from torch import float32, float16, Tensor
|
| 6 |
+
from functools import partial
|
| 7 |
+
from typing import Union
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
# re-estimate the scale based on the inverse median: Only tested with axis==0
|
| 11 |
+
def update_scale_inverse_median(
|
| 12 |
+
W_f: Tensor, scale: Tensor, zero: Tensor, axis: int, min_max: list
|
| 13 |
+
) -> tuple:
|
| 14 |
+
scale_rng = 2e4
|
| 15 |
+
z_val = 1e-4
|
| 16 |
+
delta = 1e-2
|
| 17 |
+
|
| 18 |
+
W_q = torch.round(W_f * scale + zero).clamp(min_max[0], min_max[1])
|
| 19 |
+
|
| 20 |
+
# Correct zero to avoid W_q==zero
|
| 21 |
+
zero_c = zero.clone()
|
| 22 |
+
zero_c_indx = torch.sum(1.0 * ((W_q - zero) == 0), axis=axis, keepdim=True) > 0
|
| 23 |
+
zero_c[zero_c_indx] = zero_c[zero_c_indx] + delta
|
| 24 |
+
|
| 25 |
+
# Build scale tensor
|
| 26 |
+
W_f_c = W_f.clone()
|
| 27 |
+
W_f_c_mask = torch.abs(W_f_c) < z_val
|
| 28 |
+
W_f_c[W_f_c_mask] = z_val
|
| 29 |
+
|
| 30 |
+
scale_tensor = (W_q - zero_c).float() / W_f_c.float()
|
| 31 |
+
# W_r = (W_q - zero_c)/scale_tensor
|
| 32 |
+
|
| 33 |
+
# Normalize scale_tensor
|
| 34 |
+
scale_b = torch.median(scale_tensor, axis=axis, keepdim=True)[0]
|
| 35 |
+
scale_b = scale_b.clamp(min=-scale_rng, max=scale_rng).half()
|
| 36 |
+
|
| 37 |
+
# Mix with older scale
|
| 38 |
+
W_r = (W_q - zero_c) / scale_b
|
| 39 |
+
err_b = torch.abs(W_f - W_r).mean(axis=axis, keepdim=True)
|
| 40 |
+
|
| 41 |
+
W_r = (W_q - zero_c) / scale
|
| 42 |
+
err_a = torch.abs(W_f - W_r).mean(axis=axis, keepdim=True)
|
| 43 |
+
|
| 44 |
+
mask = (err_b < err_a).half()
|
| 45 |
+
scale_b = mask * scale_b + (1 - mask) * scale
|
| 46 |
+
|
| 47 |
+
# W_r = (W_q - zero_c)/scale_b
|
| 48 |
+
return scale_b, zero_c
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
# Greedy local search: Only tested with axis==0
|
| 52 |
+
def update_scale_grid_search(
|
| 53 |
+
W_f: Tensor, scale: Tensor, zero: Tensor, axis: int, min_max: list, N: int = 128 + 1
|
| 54 |
+
) -> Tensor:
|
| 55 |
+
# Make sure it's an odd number so that the original scale is included
|
| 56 |
+
assert N % 2 == 1, "Please check whether N: odd number"
|
| 57 |
+
rng_dump = 0.05 # 0.05 / 1.
|
| 58 |
+
z_val = 2e-4
|
| 59 |
+
|
| 60 |
+
device = scale.device
|
| 61 |
+
dtype = scale.dtype
|
| 62 |
+
###############################
|
| 63 |
+
W_q = torch.round(W_f * scale + zero).clamp(min_max[0], min_max[1])
|
| 64 |
+
n_clusters = max(W_q.shape[0], W_q.shape[1])
|
| 65 |
+
rng = torch.abs(scale).mean() * rng_dump if (rng_dump < 1.0) else rng_dump
|
| 66 |
+
|
| 67 |
+
scale_shifted = (
|
| 68 |
+
torch.linspace(-rng, rng, N)[:, None]
|
| 69 |
+
.to(dtype=dtype, device=device)
|
| 70 |
+
.repeat(1, n_clusters)
|
| 71 |
+
+ scale
|
| 72 |
+
)
|
| 73 |
+
|
| 74 |
+
# Safe inverse
|
| 75 |
+
scale_shifted[
|
| 76 |
+
torch.logical_and(scale_shifted >= 0, torch.abs(scale_shifted) <= z_val)
|
| 77 |
+
] = z_val
|
| 78 |
+
scale_shifted[
|
| 79 |
+
torch.logical_and(scale_shifted < 0, torch.abs(scale_shifted) <= z_val)
|
| 80 |
+
] = -z_val
|
| 81 |
+
|
| 82 |
+
err = torch.empty([N, n_clusters], dtype=dtype, device=device)
|
| 83 |
+
for i in range(N):
|
| 84 |
+
W_r = (W_q - zero) / scale_shifted[i][None, :]
|
| 85 |
+
err[i] = torch.abs(W_f - W_r).mean(axis=axis, keepdim=True)
|
| 86 |
+
|
| 87 |
+
ind_r = torch.argmin(err, axis=axis).to(torch.int32)
|
| 88 |
+
ind_c = torch.arange(len(ind_r), dtype=torch.int32, device=device)
|
| 89 |
+
scale_b = scale_shifted[ind_r, ind_c]
|
| 90 |
+
|
| 91 |
+
return scale_b
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
# Shrinking operator
|
| 95 |
+
def shrink_lp_op(x: Tensor, beta: float, lp_norm: float) -> Tensor:
|
| 96 |
+
if lp_norm == 1:
|
| 97 |
+
return torch.sign(x) * torch.nn.functional.relu(torch.abs(x) - 1.0 / beta)
|
| 98 |
+
else:
|
| 99 |
+
return torch.sign(x) * torch.nn.functional.relu(
|
| 100 |
+
torch.abs(x) - (1.0 / beta) * torch.pow(torch.abs(x), lp_norm - 1)
|
| 101 |
+
)
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
# Proximal solver || W - dequantize(quantize(W))||_p^p - Experimental
|
| 105 |
+
@torch.inference_mode()
|
| 106 |
+
def optimize_weights_proximal_v2(
|
| 107 |
+
tensor: Tensor,
|
| 108 |
+
scale: Tensor,
|
| 109 |
+
zero: Tensor,
|
| 110 |
+
min_max: list,
|
| 111 |
+
axis: int = 0,
|
| 112 |
+
device: Union[str, None] = None,
|
| 113 |
+
dtype: Union[torch.dtype, None] = None,
|
| 114 |
+
opt_params: dict = {
|
| 115 |
+
"lp_norm": 0.7,
|
| 116 |
+
"beta": 1e1,
|
| 117 |
+
"kappa": 1.01,
|
| 118 |
+
"iters": 20,
|
| 119 |
+
"tol": 0.0,
|
| 120 |
+
"early_stop": True,
|
| 121 |
+
"scale_gridsearch": False,
|
| 122 |
+
},
|
| 123 |
+
verbose: bool = False,
|
| 124 |
+
) -> tuple:
|
| 125 |
+
# Params
|
| 126 |
+
lp_norm = max(opt_params["lp_norm"], 0.1)
|
| 127 |
+
beta = opt_params["beta"]
|
| 128 |
+
kappa = opt_params["kappa"]
|
| 129 |
+
iters = opt_params["iters"]
|
| 130 |
+
early_stop = opt_params["early_stop"]
|
| 131 |
+
tol = opt_params["tol"]
|
| 132 |
+
|
| 133 |
+
# Check
|
| 134 |
+
assert lp_norm <= 1.0, "lp_norm should be <=1"
|
| 135 |
+
assert beta > 0.0, "beta should be > 0"
|
| 136 |
+
assert kappa > 1.0, "kappa should be > 1"
|
| 137 |
+
assert iters > 1, "iters should be > 1"
|
| 138 |
+
|
| 139 |
+
# Cast/device
|
| 140 |
+
if device is None:
|
| 141 |
+
device = tensor.device
|
| 142 |
+
else:
|
| 143 |
+
device = torch.device(device)
|
| 144 |
+
|
| 145 |
+
if dtype is None:
|
| 146 |
+
dtype = float16 if (device.type == "cuda") else float32
|
| 147 |
+
|
| 148 |
+
W_f = tensor.to(device=device, dtype=dtype)
|
| 149 |
+
scale = scale.to(device=device, dtype=dtype)
|
| 150 |
+
zero = zero.to(device=device, dtype=dtype)
|
| 151 |
+
|
| 152 |
+
# Update scale: works slightly better. Tested on Llama2 only
|
| 153 |
+
if opt_params["scale_gridsearch"]:
|
| 154 |
+
scale = update_scale_grid_search(W_f, scale, zero, axis, min_max)
|
| 155 |
+
|
| 156 |
+
# Optimize for zero-point
|
| 157 |
+
best_error = 1e4
|
| 158 |
+
scale_prev, zero_prev = scale.clone(), zero.clone()
|
| 159 |
+
for i in range(iters):
|
| 160 |
+
W_q = torch.round(W_f * scale + zero).clamp(min_max[0], min_max[1])
|
| 161 |
+
W_r = (W_q - zero) / scale
|
| 162 |
+
|
| 163 |
+
# current_error = float(torch.pow(torch.abs(W_f - W_r), max(0.80, lp_norm)).mean())
|
| 164 |
+
current_error = float(torch.abs(W_f - W_r).mean())
|
| 165 |
+
|
| 166 |
+
if verbose:
|
| 167 |
+
print(i, np.round(current_error, 6))
|
| 168 |
+
|
| 169 |
+
if early_stop:
|
| 170 |
+
if best_error - current_error > tol:
|
| 171 |
+
best_error = current_error
|
| 172 |
+
scale_prev, zero_prev = scale.clone(), zero.clone()
|
| 173 |
+
else:
|
| 174 |
+
scale, zero = scale_prev.clone(), zero_prev.clone()
|
| 175 |
+
break
|
| 176 |
+
|
| 177 |
+
W_e = shrink_lp_op(W_f - W_r, beta, lp_norm)
|
| 178 |
+
zero = torch.mean(W_q - (W_f - W_e) * scale, axis=axis, keepdim=True)
|
| 179 |
+
beta *= kappa
|
| 180 |
+
|
| 181 |
+
# Clean-up
|
| 182 |
+
scale = scale.to(tensor.device)
|
| 183 |
+
zero = zero.to(tensor.device)
|
| 184 |
+
del W_f, W_q, W_r, W_e, scale_prev, zero_prev
|
| 185 |
+
torch.cuda.empty_cache()
|
| 186 |
+
|
| 187 |
+
W_q = torch.round(tensor * scale + zero).clamp(min_max[0], min_max[1])
|
| 188 |
+
|
| 189 |
+
return W_q, scale, zero
|
| 190 |
+
|
| 191 |
+
|
| 192 |
+
# Proximal solver || W - dequantize(quantize(W))||_p^p
|
| 193 |
+
@torch.inference_mode()
|
| 194 |
+
def optimize_weights_proximal_legacy(
|
| 195 |
+
tensor: Tensor,
|
| 196 |
+
scale: Tensor,
|
| 197 |
+
zero: Tensor,
|
| 198 |
+
min_max: list,
|
| 199 |
+
axis: int = 0,
|
| 200 |
+
device: Union[str, None] = None,
|
| 201 |
+
opt_params: dict = {"lp_norm": 0.7, "beta": 1e1, "kappa": 1.01, "iters": 20},
|
| 202 |
+
verbose: bool = False,
|
| 203 |
+
) -> tuple:
|
| 204 |
+
lp_norm, beta, kappa, iters = (
|
| 205 |
+
opt_params["lp_norm"],
|
| 206 |
+
opt_params["beta"],
|
| 207 |
+
opt_params["kappa"],
|
| 208 |
+
opt_params["iters"],
|
| 209 |
+
)
|
| 210 |
+
|
| 211 |
+
if device is None:
|
| 212 |
+
device = tensor.device
|
| 213 |
+
else:
|
| 214 |
+
device = torch.device(device)
|
| 215 |
+
|
| 216 |
+
dtype = float16 if (device.type == "cuda") else float32
|
| 217 |
+
W_f = tensor.to(dtype=dtype, device=device)
|
| 218 |
+
scale = scale.to(dtype=dtype, device=device)
|
| 219 |
+
zero = zero.to(dtype=dtype, device=device)
|
| 220 |
+
|
| 221 |
+
best_error = 1e4
|
| 222 |
+
for i in range(iters):
|
| 223 |
+
W_q = torch.round(W_f * scale + zero).clamp(min_max[0], min_max[1])
|
| 224 |
+
W_r = (W_q - zero) / scale
|
| 225 |
+
W_e = shrink_lp_op(W_f - W_r, beta, lp_norm)
|
| 226 |
+
zero = torch.mean(W_q - (W_f - W_e) * scale, axis=axis, keepdim=True)
|
| 227 |
+
beta *= kappa
|
| 228 |
+
|
| 229 |
+
current_error = float(torch.abs(W_f - W_r).mean())
|
| 230 |
+
if verbose:
|
| 231 |
+
print(i, np.round(current_error, 6))
|
| 232 |
+
if current_error < best_error:
|
| 233 |
+
best_error = current_error
|
| 234 |
+
else:
|
| 235 |
+
break
|
| 236 |
+
|
| 237 |
+
scale = scale.to(tensor.device)
|
| 238 |
+
zero = zero.to(tensor.device)
|
| 239 |
+
del W_f, W_q, W_r, W_e
|
| 240 |
+
torch.cuda.empty_cache()
|
| 241 |
+
|
| 242 |
+
W_q = torch.round(tensor * scale + zero).clamp(min_max[0], min_max[1])
|
| 243 |
+
return W_q, scale, zero
|
| 244 |
+
|
| 245 |
+
|
| 246 |
+
# Default: fast with early stopping
|
| 247 |
+
optimize_weights_proximal = optimize_weights_proximal_legacy
|
| 248 |
+
|
| 249 |
+
# Slower, better quality: no early stoppping, more iterations
|
| 250 |
+
optimize_weights_proximal_slow = partial(
|
| 251 |
+
optimize_weights_proximal_v2,
|
| 252 |
+
dtype=torch.float32,
|
| 253 |
+
opt_params={
|
| 254 |
+
"lp_norm": 0.7,
|
| 255 |
+
"beta": 1e1,
|
| 256 |
+
"kappa": 1.01,
|
| 257 |
+
"iters": 100,
|
| 258 |
+
"tol": 0.0,
|
| 259 |
+
"early_stop": False,
|
| 260 |
+
"scale_gridsearch": False,
|
| 261 |
+
},
|
| 262 |
+
)
|
| 263 |
+
|
| 264 |
+
##############################################################################################################
|
| 265 |
+
# L1 with SGD optimizer: supports scale and W_q updates. L{p<1} fails with SGD
|
| 266 |
+
|
| 267 |
+
|
| 268 |
+
class LinearSchedulerWithWarmStart(torch.optim.lr_scheduler._LRScheduler):
|
| 269 |
+
def __init__(self, optimizer, lr_start, lr_end, iters, warm_start=0, last_epoch=-1):
|
| 270 |
+
iters_wrm = max(0, int(iters * warm_start))
|
| 271 |
+
self.lr_wrm = (
|
| 272 |
+
np.linspace(lr_end, lr_start, iters_wrm)
|
| 273 |
+
if (iters_wrm > 0)
|
| 274 |
+
else np.array([])
|
| 275 |
+
)
|
| 276 |
+
self.lr_mid = np.linspace(lr_start, lr_end, iters - iters_wrm)
|
| 277 |
+
self.lr_sch = np.concatenate([self.lr_wrm, self.lr_mid])
|
| 278 |
+
self.idx = 0
|
| 279 |
+
super(LinearSchedulerWithWarmStart, self).__init__(optimizer, last_epoch)
|
| 280 |
+
|
| 281 |
+
def get_lr(self):
|
| 282 |
+
self.idx = min(self.idx, len(self.lr_sch) - 1)
|
| 283 |
+
out = [self.lr_sch[self.idx] for base_lr in self.base_lrs]
|
| 284 |
+
self.idx += 1
|
| 285 |
+
return out
|
| 286 |
+
|
| 287 |
+
|
| 288 |
+
# SGD solver || W - dequantize(quantize(W))||_1 (p=1 only, with additional fake inputs x)
|
| 289 |
+
def optimize_weights_autograd(
|
| 290 |
+
tensor: Tensor,
|
| 291 |
+
scale: Tensor,
|
| 292 |
+
zero: Tensor,
|
| 293 |
+
min_max: list,
|
| 294 |
+
axis: int = 0,
|
| 295 |
+
device: Union[str, None] = None,
|
| 296 |
+
dtype: Union[torch.dtype, None] = float32,
|
| 297 |
+
opt_params: dict = {
|
| 298 |
+
"lr": 2e-3,
|
| 299 |
+
"iters": 2500,
|
| 300 |
+
"lr_schedule": False,
|
| 301 |
+
"update_Wq": False,
|
| 302 |
+
"use_fake_data": False,
|
| 303 |
+
},
|
| 304 |
+
data_params: dict = {"normalize": False, "data_rng": 10.0, "data_ctx": 32},
|
| 305 |
+
compile: bool = True,
|
| 306 |
+
verbose: bool = False,
|
| 307 |
+
) -> tuple:
|
| 308 |
+
ref_device = scale.device
|
| 309 |
+
ref_dtype = scale.dtype
|
| 310 |
+
|
| 311 |
+
if device is None:
|
| 312 |
+
device = tensor.device
|
| 313 |
+
else:
|
| 314 |
+
device = torch.device(device)
|
| 315 |
+
|
| 316 |
+
W_f = tensor.to(dtype=dtype, device=device)
|
| 317 |
+
|
| 318 |
+
params = {}
|
| 319 |
+
params["scale"] = torch.nn.Parameter(
|
| 320 |
+
scale.to(dtype=dtype, device=device), requires_grad=True
|
| 321 |
+
)
|
| 322 |
+
params["zero"] = torch.nn.Parameter(
|
| 323 |
+
zero.to(dtype=dtype, device=device), requires_grad=True
|
| 324 |
+
)
|
| 325 |
+
|
| 326 |
+
if opt_params["update_Wq"]:
|
| 327 |
+
with torch.no_grad():
|
| 328 |
+
params["W_q"] = torch.round(W_f * params["scale"] + params["zero"]).clamp(
|
| 329 |
+
min_max[0], min_max[1]
|
| 330 |
+
)
|
| 331 |
+
params["W_q"] = torch.nn.Parameter(params["W_q"], requires_grad=True)
|
| 332 |
+
|
| 333 |
+
optimizer = torch.optim.AdamW(
|
| 334 |
+
[params[k] for k in params],
|
| 335 |
+
lr=opt_params["lr"],
|
| 336 |
+
betas=(0.9, 0.99),
|
| 337 |
+
eps=1e-06,
|
| 338 |
+
weight_decay=0.0,
|
| 339 |
+
)
|
| 340 |
+
|
| 341 |
+
if opt_params["lr_schedule"]:
|
| 342 |
+
scheduler = LinearSchedulerWithWarmStart(
|
| 343 |
+
optimizer,
|
| 344 |
+
lr_start=opt_params["lr"],
|
| 345 |
+
lr_end=1e-6,
|
| 346 |
+
iters=opt_params["iters"],
|
| 347 |
+
warm_start=0,
|
| 348 |
+
)
|
| 349 |
+
else:
|
| 350 |
+
scheduler = None
|
| 351 |
+
|
| 352 |
+
with torch.no_grad():
|
| 353 |
+
if data_params["normalize"]:
|
| 354 |
+
scale_loss = 1.0 / (tensor.abs().mean() + 1e-4)
|
| 355 |
+
else:
|
| 356 |
+
scale_loss = 1.0
|
| 357 |
+
|
| 358 |
+
def _loss_fct(output, target):
|
| 359 |
+
return torch.mean(torch.abs(scale_loss * (target - output))) # L1
|
| 360 |
+
|
| 361 |
+
def _fake_quant_fixed_Wq(W_f):
|
| 362 |
+
# Quantize
|
| 363 |
+
W_q = torch.round(W_f * params["scale"] + params["zero"]).clamp(
|
| 364 |
+
min_max[0], min_max[1]
|
| 365 |
+
)
|
| 366 |
+
# Dequantize
|
| 367 |
+
W_r = (W_q - params["zero"]) / params["scale"]
|
| 368 |
+
return W_r
|
| 369 |
+
|
| 370 |
+
def _fake_quant_update_Wq(W_f):
|
| 371 |
+
# Quantize
|
| 372 |
+
W_q = torch.round(params["W_q"]).clamp(min_max[0], min_max[1])
|
| 373 |
+
# Dequantize
|
| 374 |
+
W_r = (W_q - params["zero"]) / params["scale"]
|
| 375 |
+
return W_r
|
| 376 |
+
|
| 377 |
+
if opt_params["update_Wq"]:
|
| 378 |
+
_fake_quant = _fake_quant_update_Wq
|
| 379 |
+
else:
|
| 380 |
+
_fake_quant = _fake_quant_fixed_Wq
|
| 381 |
+
|
| 382 |
+
def _fake_quant_loss(W_f):
|
| 383 |
+
return _loss_fct(_fake_quant(W_f), W_f)
|
| 384 |
+
|
| 385 |
+
def _fake_quant_loss_with_fake_data(W_f):
|
| 386 |
+
x = (
|
| 387 |
+
torch.rand(
|
| 388 |
+
[data_params["data_ctx"], W_f.shape[1]], device=device, dtype=dtype
|
| 389 |
+
)
|
| 390 |
+
- 0.5
|
| 391 |
+
) * 2 ** data_params["data_rng"]
|
| 392 |
+
y_ref = torch.matmul(x, W_f.T)
|
| 393 |
+
y_pred = torch.matmul(x, _fake_quant(W_f).T)
|
| 394 |
+
return _loss_fct(y_pred, y_ref)
|
| 395 |
+
|
| 396 |
+
if opt_params["use_fake_data"]:
|
| 397 |
+
_fake_quant_loss = _fake_quant_loss_with_fake_data
|
| 398 |
+
else:
|
| 399 |
+
_fake_quant_loss = _fake_quant_loss
|
| 400 |
+
|
| 401 |
+
if compile:
|
| 402 |
+
_fake_quant_loss = torch.compile(_fake_quant_loss)
|
| 403 |
+
|
| 404 |
+
def _step(W_f):
|
| 405 |
+
optimizer.zero_grad()
|
| 406 |
+
loss = _fake_quant_loss(W_f)
|
| 407 |
+
loss.backward()
|
| 408 |
+
optimizer.step()
|
| 409 |
+
if scheduler is not None:
|
| 410 |
+
scheduler.step()
|
| 411 |
+
return np.round(loss.item(), 10)
|
| 412 |
+
|
| 413 |
+
with torch.no_grad():
|
| 414 |
+
_init_loss = _fake_quant_loss(W_f).item()
|
| 415 |
+
|
| 416 |
+
for i in range(opt_params["iters"]):
|
| 417 |
+
loss_out = _step(W_f)
|
| 418 |
+
if verbose and (i % 100) == 0:
|
| 419 |
+
print(i, loss_out)
|
| 420 |
+
|
| 421 |
+
with torch.no_grad():
|
| 422 |
+
_final_loss = _fake_quant_loss(W_f).item()
|
| 423 |
+
|
| 424 |
+
if _final_loss < _init_loss:
|
| 425 |
+
for k in params:
|
| 426 |
+
params[k] = params[k].data.detach()
|
| 427 |
+
else:
|
| 428 |
+
if verbose:
|
| 429 |
+
print("optimization failed...")
|
| 430 |
+
params = {"scale": scale, "zero": zero}
|
| 431 |
+
|
| 432 |
+
scale = params["scale"].to(device=ref_device, dtype=ref_dtype)
|
| 433 |
+
zero = params["zero"].to(device=ref_device, dtype=ref_dtype)
|
| 434 |
+
|
| 435 |
+
if "W_q" in params:
|
| 436 |
+
W_q = params["W_q"].to(device=ref_device, dtype=ref_dtype)
|
| 437 |
+
else:
|
| 438 |
+
W_q = (
|
| 439 |
+
torch.round(tensor * scale + zero)
|
| 440 |
+
.clamp(min_max[0], min_max[1])
|
| 441 |
+
.to(device=ref_device, dtype=ref_dtype)
|
| 442 |
+
)
|
| 443 |
+
|
| 444 |
+
del W_f
|
| 445 |
+
torch.cuda.empty_cache()
|
| 446 |
+
return W_q, scale, zero
|
| 447 |
+
|
| 448 |
+
|
| 449 |
+
optimize_weights_autograd_main = partial(
|
| 450 |
+
optimize_weights_autograd,
|
| 451 |
+
dtype=torch.float32,
|
| 452 |
+
opt_params={
|
| 453 |
+
"lr": 2e-3,
|
| 454 |
+
"iters": 1000,
|
| 455 |
+
"lr_schedule": True,
|
| 456 |
+
"update_Wq": True,
|
| 457 |
+
"use_fake_data": False,
|
| 458 |
+
},
|
| 459 |
+
verbose=False,
|
| 460 |
+
compile=True,
|
| 461 |
+
)
|
| 462 |
+
|
| 463 |
+
optimize_weights_autograd_fakedata = partial(
|
| 464 |
+
optimize_weights_autograd,
|
| 465 |
+
dtype=torch.float32,
|
| 466 |
+
opt_params={
|
| 467 |
+
"lr": 2e-3,
|
| 468 |
+
"iters": 1000,
|
| 469 |
+
"lr_schedule": True,
|
| 470 |
+
"update_Wq": True,
|
| 471 |
+
"use_fake_data": True,
|
| 472 |
+
},
|
| 473 |
+
data_params={"normalize": False, "data_rng": 10.0, "data_ctx": 32},
|
| 474 |
+
verbose=False,
|
| 475 |
+
compile=True,
|
| 476 |
+
)
|
lm-quant-toolkit/.deps/hqq/hqq/core/peft.py
ADDED
|
@@ -0,0 +1,545 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Written by Dr. Hicham Badri @Mobius Labs GmbH - 2023
|
| 2 |
+
#####################################################
|
| 3 |
+
import torch
|
| 4 |
+
from torch import float16, bfloat16, float32
|
| 5 |
+
from torch import Tensor, nn
|
| 6 |
+
import numpy as np
|
| 7 |
+
from .quantize import HQQLinear, Quantizer
|
| 8 |
+
from .utils import cleanup
|
| 9 |
+
from typing import Union
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
# Return trainable weight matrix
|
| 13 |
+
def _get_dense_param(
|
| 14 |
+
in_features: int,
|
| 15 |
+
out_features: int,
|
| 16 |
+
device="cuda",
|
| 17 |
+
trainable: bool = True,
|
| 18 |
+
dtype: torch.dtype = float32,
|
| 19 |
+
) -> Tensor:
|
| 20 |
+
W = (
|
| 21 |
+
nn.Linear(in_features, out_features, bias=None)
|
| 22 |
+
.weight.data.t()
|
| 23 |
+
.to(dtype)
|
| 24 |
+
.to(device)
|
| 25 |
+
.contiguous()
|
| 26 |
+
)
|
| 27 |
+
return nn.Parameter(W, requires_grad=trainable)
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
# Applies LoRA to a linear module (include HQQLinear)
|
| 31 |
+
class HQQLinearLoRA(nn.Module):
|
| 32 |
+
def __init__(self, linear_layer: nn.Module, peft_config: dict):
|
| 33 |
+
super().__init__()
|
| 34 |
+
|
| 35 |
+
# Device
|
| 36 |
+
self.device = (
|
| 37 |
+
linear_layer.device
|
| 38 |
+
if hasattr(linear_layer, "device")
|
| 39 |
+
else next(linear_layer.parameters()).device
|
| 40 |
+
)
|
| 41 |
+
self.train_dtype = (
|
| 42 |
+
peft_config["train_dtype"]
|
| 43 |
+
if ("train_dtype" in peft_config)
|
| 44 |
+
else torch.float
|
| 45 |
+
)
|
| 46 |
+
|
| 47 |
+
# Linear layer
|
| 48 |
+
self.linear_layer = linear_layer
|
| 49 |
+
self.in_features = linear_layer.in_features
|
| 50 |
+
self.out_features = linear_layer.out_features
|
| 51 |
+
|
| 52 |
+
# Bias
|
| 53 |
+
self.bias = None if (linear_layer.bias is None) else linear_layer.bias.clone()
|
| 54 |
+
self.linear_layer.bias = None
|
| 55 |
+
self.train_bias = (
|
| 56 |
+
peft_config["train_bias"] if ("train_bias" in peft_config) else False
|
| 57 |
+
)
|
| 58 |
+
|
| 59 |
+
if self.bias is not None:
|
| 60 |
+
self.bias = nn.Parameter(self.bias, requires_grad=self.train_bias)
|
| 61 |
+
if self.train_bias:
|
| 62 |
+
self.bias.data = self.bias.data.to(self.train_dtype)
|
| 63 |
+
|
| 64 |
+
if (self.bias is None) and self.train_bias:
|
| 65 |
+
self.bias = nn.Parameter(
|
| 66 |
+
torch.zeros(
|
| 67 |
+
(self.out_features,), device=self.device, dtype=self.train_dtype
|
| 68 |
+
),
|
| 69 |
+
requires_grad=True,
|
| 70 |
+
)
|
| 71 |
+
|
| 72 |
+
# Dropout
|
| 73 |
+
if "dropout" in peft_config:
|
| 74 |
+
self.peft_drop = (
|
| 75 |
+
nn.Dropout(p=peft_config["dropout"])
|
| 76 |
+
if (peft_config["dropout"] > 0.0)
|
| 77 |
+
else nn.Identity()
|
| 78 |
+
)
|
| 79 |
+
else:
|
| 80 |
+
self.peft_drop = nn.Identity()
|
| 81 |
+
|
| 82 |
+
# LoRA A/B
|
| 83 |
+
self.peft_config = peft_config
|
| 84 |
+
self.lora_alpha = peft_config["lora_alpha"]
|
| 85 |
+
self.r = peft_config["r"]
|
| 86 |
+
self.scaling = self.lora_alpha / self.r
|
| 87 |
+
|
| 88 |
+
self.lora_A = _get_dense_param(
|
| 89 |
+
self.in_features,
|
| 90 |
+
self.r,
|
| 91 |
+
device=self.device,
|
| 92 |
+
trainable=True,
|
| 93 |
+
dtype=self.train_dtype,
|
| 94 |
+
)
|
| 95 |
+
|
| 96 |
+
self.lora_B = _get_dense_param(
|
| 97 |
+
self.r,
|
| 98 |
+
self.out_features,
|
| 99 |
+
device=self.device,
|
| 100 |
+
trainable=True,
|
| 101 |
+
dtype=self.train_dtype,
|
| 102 |
+
)
|
| 103 |
+
|
| 104 |
+
# LoRA weights init
|
| 105 |
+
if "lora_init" in peft_config:
|
| 106 |
+
# Set lora init
|
| 107 |
+
assert (
|
| 108 |
+
peft_config["lora_init"]["lora_A"].shape[0],
|
| 109 |
+
peft_config["lora_init"]["lora_B"].shape[1],
|
| 110 |
+
) == (self.in_features, self.out_features), (
|
| 111 |
+
"Invalid init LoRA weight shapes. Expected: lora_A: "
|
| 112 |
+
+ str(self.in_features)
|
| 113 |
+
+ " x r , lora_B: r x "
|
| 114 |
+
+ str(self.out_features)
|
| 115 |
+
+ ")"
|
| 116 |
+
)
|
| 117 |
+
self.lora_A.data = peft_config["lora_init"]["lora_A"].to(
|
| 118 |
+
device=self.device, dtype=self.train_dtype
|
| 119 |
+
)
|
| 120 |
+
self.lora_B.data = peft_config["lora_init"]["lora_B"].to(
|
| 121 |
+
device=self.device, dtype=self.train_dtype
|
| 122 |
+
)
|
| 123 |
+
else:
|
| 124 |
+
# Init weights, as as the original LoRA implementation
|
| 125 |
+
nn.init.kaiming_uniform_(self.lora_A, a=np.sqrt(5))
|
| 126 |
+
nn.init.zeros_(self.lora_B)
|
| 127 |
+
|
| 128 |
+
def forward_lora(self, x: Tensor) -> Tensor: # output is self.train_dtype
|
| 129 |
+
return (
|
| 130 |
+
torch.matmul(
|
| 131 |
+
torch.matmul(self.peft_drop(x.to(self.lora_A.dtype)), self.lora_A),
|
| 132 |
+
self.lora_B,
|
| 133 |
+
)
|
| 134 |
+
* self.scaling
|
| 135 |
+
)
|
| 136 |
+
|
| 137 |
+
def forward(self, x: Tensor) -> Tensor:
|
| 138 |
+
x_dtype = x.dtype
|
| 139 |
+
|
| 140 |
+
# Forward with the base linear layer
|
| 141 |
+
out = self.linear_layer(x)
|
| 142 |
+
|
| 143 |
+
# LoRA
|
| 144 |
+
if self.train_bias:
|
| 145 |
+
out += (self.forward_lora(x) + self.bias).to(x_dtype)
|
| 146 |
+
|
| 147 |
+
else:
|
| 148 |
+
out += self.forward_lora(x).to(x_dtype)
|
| 149 |
+
if self.bias is not None:
|
| 150 |
+
out += self.bias
|
| 151 |
+
|
| 152 |
+
return out
|
| 153 |
+
|
| 154 |
+
def merge_and_quantize(self, quant_config: dict) -> nn.Module:
|
| 155 |
+
# not easy to figure out the type for "any" potential linear layer (nn.Linear, HQQLinear, etc.)
|
| 156 |
+
for _dtype in [float16, bfloat16, float32]:
|
| 157 |
+
try:
|
| 158 |
+
# Get initial weights
|
| 159 |
+
W = self.linear_layer(
|
| 160 |
+
torch.eye(self.in_features, device=self.device, dtype=_dtype)
|
| 161 |
+
).t() # == self.linear_layer.dequantize()
|
| 162 |
+
except Exception:
|
| 163 |
+
pass
|
| 164 |
+
|
| 165 |
+
# Merge weights
|
| 166 |
+
W += (
|
| 167 |
+
(torch.matmul(self.lora_A.data, self.lora_B.data) * self.scaling)
|
| 168 |
+
.t()
|
| 169 |
+
.to(W.dtype)
|
| 170 |
+
)
|
| 171 |
+
|
| 172 |
+
# New HQQ layer
|
| 173 |
+
new_hqq_layer = HQQLinear(None, quant_config)
|
| 174 |
+
new_hqq_layer.bias = None if (self.bias is None) else self.bias.clone()
|
| 175 |
+
new_hqq_layer.quantize(W, **quant_config)
|
| 176 |
+
|
| 177 |
+
return new_hqq_layer
|
| 178 |
+
|
| 179 |
+
def cast(self, dtype: torch.dtype = float16):
|
| 180 |
+
self.lora_A.data = self.lora_A.data.to(dtype)
|
| 181 |
+
self.lora_B.data = self.lora_B.data.to(dtype)
|
| 182 |
+
if self.bias is not None:
|
| 183 |
+
self.bias.data = self.bias.data.to(dtype)
|
| 184 |
+
if isinstance(self.scaling, nn.Parameter):
|
| 185 |
+
self.scaling.data = self.scaling.data.to(dtype)
|
| 186 |
+
if isinstance(self.scaling, Tensor):
|
| 187 |
+
self.scaling = self.scaling.to(dtype)
|
| 188 |
+
return self
|
| 189 |
+
|
| 190 |
+
def state_dict(self):
|
| 191 |
+
return {
|
| 192 |
+
"lora_A": self.lora_A.data,
|
| 193 |
+
"lora_B": self.lora_B.data,
|
| 194 |
+
"scaling": self.scaling,
|
| 195 |
+
"bias": self.bias,
|
| 196 |
+
}
|
| 197 |
+
|
| 198 |
+
def load_state_dict(self, state_dict):
|
| 199 |
+
self.lora_A.data = state_dict["lora_A"].data.to(self.device)
|
| 200 |
+
self.lora_B.data = state_dict["lora_B"].data.to(self.device)
|
| 201 |
+
|
| 202 |
+
if state_dict["bias"] is not None:
|
| 203 |
+
self.bias.data = state_dict["bias"].data.to(self.device)
|
| 204 |
+
|
| 205 |
+
# Handle different use-cases of scaling
|
| 206 |
+
if isinstance(state_dict["scaling"], (int, float)):
|
| 207 |
+
self.scaling = state_dict["scaling"]
|
| 208 |
+
if isinstance(state_dict["scaling"], nn.Parameter):
|
| 209 |
+
self.scaling.data = state_dict["scaling"].data.to(self.device)
|
| 210 |
+
if isinstance(state_dict["scaling"], Tensor):
|
| 211 |
+
self.scaling = state_dict["scaling"].to(self.device)
|
| 212 |
+
|
| 213 |
+
|
| 214 |
+
# LoRA with fake quantization
|
| 215 |
+
class HQQLinearLoRAWithFakeQuant(HQQLinearLoRA):
|
| 216 |
+
def __init__(self, linear_layer: nn.Module, peft_config: dict):
|
| 217 |
+
super(HQQLinearLoRAWithFakeQuant, self).__init__(linear_layer, peft_config)
|
| 218 |
+
self.quant_param = peft_config["quant_param"]
|
| 219 |
+
|
| 220 |
+
# @torch.no_grad()
|
| 221 |
+
# @torch.compile()
|
| 222 |
+
def fake_quant(self, weight):
|
| 223 |
+
if self.quant_param:
|
| 224 |
+
W_q, meta = Quantizer.quantize(
|
| 225 |
+
weight, **self.quant_param, bitpack=False
|
| 226 |
+
) # todo: clone() tensor
|
| 227 |
+
weight_est = Quantizer.dequantize(W_q, meta)
|
| 228 |
+
else:
|
| 229 |
+
weight_est = weight
|
| 230 |
+
return weight_est
|
| 231 |
+
|
| 232 |
+
def forward(self, x: Tensor) -> Tensor:
|
| 233 |
+
x_dtype = x.dtype
|
| 234 |
+
|
| 235 |
+
# Get initial weights
|
| 236 |
+
W = self.linear_layer(
|
| 237 |
+
torch.eye(self.in_features, device=self.device, dtype=x_dtype)
|
| 238 |
+
).t() # == self.linear_layer.dequantize()
|
| 239 |
+
|
| 240 |
+
# Merge weights
|
| 241 |
+
W += (torch.matmul(self.lora_A, self.lora_B) * self.scaling).t().to(W.dtype)
|
| 242 |
+
|
| 243 |
+
# Fake quant
|
| 244 |
+
W = self.fake_quant(W).to(x_dtype)
|
| 245 |
+
|
| 246 |
+
# Matmul
|
| 247 |
+
out = torch.matmul(x, W.t())
|
| 248 |
+
|
| 249 |
+
# Bias
|
| 250 |
+
if self.bias is not None:
|
| 251 |
+
out += self.bias
|
| 252 |
+
|
| 253 |
+
out = out.to(x_dtype)
|
| 254 |
+
|
| 255 |
+
return out
|
| 256 |
+
|
| 257 |
+
|
| 258 |
+
# Experimental
|
| 259 |
+
class HQQLinearGroupedProj(nn.Module):
|
| 260 |
+
def __init__(self, linear_layer: nn.Module, peft_config: dict):
|
| 261 |
+
super().__init__()
|
| 262 |
+
|
| 263 |
+
# Device
|
| 264 |
+
self.device = (
|
| 265 |
+
linear_layer.device
|
| 266 |
+
if hasattr(linear_layer, "device")
|
| 267 |
+
else next(linear_layer.parameters()).device
|
| 268 |
+
)
|
| 269 |
+
self.train_dtype = (
|
| 270 |
+
peft_config["train_dtype"]
|
| 271 |
+
if ("train_dtype" in peft_config)
|
| 272 |
+
else torch.float
|
| 273 |
+
)
|
| 274 |
+
|
| 275 |
+
# Linear layer
|
| 276 |
+
self.linear_layer = linear_layer
|
| 277 |
+
self.in_features = linear_layer.in_features
|
| 278 |
+
self.out_features = linear_layer.out_features
|
| 279 |
+
self.bias = None if (linear_layer.bias is None) else linear_layer.bias.clone()
|
| 280 |
+
|
| 281 |
+
# Turn-off bias in the linear layer
|
| 282 |
+
self.linear_layer.bias = None
|
| 283 |
+
|
| 284 |
+
# Group proj
|
| 285 |
+
self.peft_config = peft_config
|
| 286 |
+
self.proj_size = peft_config["proj_size"]
|
| 287 |
+
self.proj_num = peft_config["proj_num"]
|
| 288 |
+
self.proj = nn.Parameter(
|
| 289 |
+
torch.stack(
|
| 290 |
+
[torch.eye(self.proj_size, dtype=self.train_dtype, device=self.device)]
|
| 291 |
+
* self.proj_num
|
| 292 |
+
)
|
| 293 |
+
)
|
| 294 |
+
if peft_config["zero_trainable"]:
|
| 295 |
+
self.linear_layer.meta["zero"] = nn.Parameter(
|
| 296 |
+
self.linear_layer.meta["zero"].to(self.train_dtype), requires_grad=True
|
| 297 |
+
)
|
| 298 |
+
|
| 299 |
+
@torch.compile()
|
| 300 |
+
def forward(self, x: Tensor) -> Tensor:
|
| 301 |
+
x_dtype = x.dtype
|
| 302 |
+
|
| 303 |
+
# Forward with base linear
|
| 304 |
+
with torch.no_grad():
|
| 305 |
+
W = self.linear_layer.dequantize().clone()
|
| 306 |
+
# W = self.linear_layer(torch.eye(self.in_features, device=self.device, dtype=x_dtype)).t()
|
| 307 |
+
shape = W.shape
|
| 308 |
+
|
| 309 |
+
# Grouped proj
|
| 310 |
+
proj_b, gs = self.proj.shape[0], self.proj.shape[1]
|
| 311 |
+
W = (
|
| 312 |
+
torch.matmul(self.proj, W.reshape((proj_b, gs, -1)).to(self.proj.dtype))
|
| 313 |
+
.to(x_dtype)
|
| 314 |
+
.reshape(shape)
|
| 315 |
+
)
|
| 316 |
+
|
| 317 |
+
# Matmul
|
| 318 |
+
out = torch.matmul(x, W.t())
|
| 319 |
+
|
| 320 |
+
# Bias
|
| 321 |
+
if self.bias is not None:
|
| 322 |
+
out += self.bias
|
| 323 |
+
|
| 324 |
+
out = out.to(x_dtype)
|
| 325 |
+
|
| 326 |
+
return out
|
| 327 |
+
|
| 328 |
+
def cast(self, dtype: torch.dtype = float16):
|
| 329 |
+
self.proj.data = self.proj.data.to(dtype)
|
| 330 |
+
self.linear_layer.meta["zero"] = self.linear_layer.meta["zero"].to(dtype)
|
| 331 |
+
if self.bias is not None:
|
| 332 |
+
if self.bias.requires_grad:
|
| 333 |
+
self.bias.data = self.bias.data.to(dtype)
|
| 334 |
+
else:
|
| 335 |
+
self.bias = self.bias.to(dtype)
|
| 336 |
+
return self
|
| 337 |
+
|
| 338 |
+
def state_dict(self):
|
| 339 |
+
return {
|
| 340 |
+
"proj": self.proj.data,
|
| 341 |
+
"bias": self.bias,
|
| 342 |
+
"peft_config": self.peft_config,
|
| 343 |
+
}
|
| 344 |
+
|
| 345 |
+
def load_state_dict(self, state_dict):
|
| 346 |
+
self.proj.data = state_dict["proj"].data.to(self.device)
|
| 347 |
+
self.bias = state_dict["bias"] if ("bias" in state_dict) else None
|
| 348 |
+
self.bias = self.bias.to(self.device) if (self.bias is not None) else None
|
| 349 |
+
self.peft_config = state_dict["peft_config"]
|
| 350 |
+
|
| 351 |
+
|
| 352 |
+
_HQQ_LORA_CLASSES = [HQQLinearLoRA, HQQLinearLoRAWithFakeQuant, HQQLinearGroupedProj]
|
| 353 |
+
_HQQ_LORA_MAPPING = {
|
| 354 |
+
"default": HQQLinearLoRA,
|
| 355 |
+
"lora_with_fakequant": HQQLinearLoRAWithFakeQuant,
|
| 356 |
+
"grouped_proj": HQQLinearGroupedProj,
|
| 357 |
+
}
|
| 358 |
+
|
| 359 |
+
|
| 360 |
+
def is_hqq_lora_layer(layer):
|
| 361 |
+
return type(layer) in _HQQ_LORA_CLASSES
|
| 362 |
+
|
| 363 |
+
|
| 364 |
+
##################################################################################################################
|
| 365 |
+
def autoname_modules(model):
|
| 366 |
+
for name, module in model.named_modules():
|
| 367 |
+
module.name = name
|
| 368 |
+
|
| 369 |
+
|
| 370 |
+
# Patching functions
|
| 371 |
+
def patch_linear_add_peft(layer: nn.Module, patch_params: Union[dict,None]) -> nn.Module:
|
| 372 |
+
_peft_config = patch_params
|
| 373 |
+
if _peft_config:
|
| 374 |
+
lora_type = (
|
| 375 |
+
_peft_config["lora_type"] if ("lora_type" in _peft_config) else "default"
|
| 376 |
+
)
|
| 377 |
+
new_layer = _HQQ_LORA_MAPPING[lora_type](layer, _peft_config)
|
| 378 |
+
else:
|
| 379 |
+
new_layer = layer
|
| 380 |
+
return new_layer
|
| 381 |
+
|
| 382 |
+
|
| 383 |
+
def patch_linear_merge_peft(layer: nn.Module, patch_params: Union[dict,None]) -> nn.Module:
|
| 384 |
+
_quant_config = patch_params
|
| 385 |
+
if _quant_config:
|
| 386 |
+
new_layer = layer.merge_and_quantize(_quant_config)
|
| 387 |
+
del layer
|
| 388 |
+
cleanup()
|
| 389 |
+
else:
|
| 390 |
+
new_layer = layer
|
| 391 |
+
return new_layer
|
| 392 |
+
|
| 393 |
+
|
| 394 |
+
def patch_linear_cast_peft(layer: nn.Module, patch_params: Union[dict,None]) -> nn.Module:
|
| 395 |
+
if is_hqq_lora_layer(layer):
|
| 396 |
+
layer.cast(patch_params)
|
| 397 |
+
return layer
|
| 398 |
+
|
| 399 |
+
|
| 400 |
+
# Putting it all together
|
| 401 |
+
class PeftUtils:
|
| 402 |
+
@classmethod
|
| 403 |
+
def get_base_class(cls, model, base_class):
|
| 404 |
+
# Get base class
|
| 405 |
+
if base_class is None:
|
| 406 |
+
base_class = getattr(model, "base_class", None)
|
| 407 |
+
|
| 408 |
+
assert (
|
| 409 |
+
base_class is not None
|
| 410 |
+
), "You need to provide the base HQQ class (LlamaHQQ, MixtralHQQ, etc.) as model.base_class or as an argument base_class=LlamaHQQ. Use AutoHQQHFMode if the model architecture is not supported."
|
| 411 |
+
|
| 412 |
+
return base_class
|
| 413 |
+
|
| 414 |
+
@classmethod
|
| 415 |
+
def add_lora(
|
| 416 |
+
cls, model, peft_config, base_class: bool = None, verbose: bool = True
|
| 417 |
+
) -> None:
|
| 418 |
+
# Base classs
|
| 419 |
+
base_class = cls.get_base_class(model, base_class)
|
| 420 |
+
|
| 421 |
+
base_class.setup_model(model)
|
| 422 |
+
|
| 423 |
+
# Freeze
|
| 424 |
+
for param in model.parameters():
|
| 425 |
+
param.requires_grad = False
|
| 426 |
+
|
| 427 |
+
# Patch
|
| 428 |
+
base_class.patch_linearlayers(
|
| 429 |
+
model, patch_linear_add_peft, peft_config, verbose=verbose
|
| 430 |
+
)
|
| 431 |
+
|
| 432 |
+
# Rename modules
|
| 433 |
+
autoname_modules(model)
|
| 434 |
+
|
| 435 |
+
# add config to model
|
| 436 |
+
model.peft_config = peft_config
|
| 437 |
+
|
| 438 |
+
@classmethod
|
| 439 |
+
def merge_lora(
|
| 440 |
+
cls, model, merge_lora_params, base_class: bool = None, verbose: bool = True
|
| 441 |
+
) -> None:
|
| 442 |
+
# Base classs
|
| 443 |
+
base_class = cls.get_base_class(model, base_class)
|
| 444 |
+
|
| 445 |
+
# Patch
|
| 446 |
+
base_class.patch_linearlayers(
|
| 447 |
+
model, patch_linear_merge_peft, merge_lora_params, verbose=verbose
|
| 448 |
+
)
|
| 449 |
+
|
| 450 |
+
@classmethod
|
| 451 |
+
def cast_lora_weights(
|
| 452 |
+
cls, model, dtype: torch.dtype, base_class: bool = None, verbose: bool = True
|
| 453 |
+
) -> None:
|
| 454 |
+
# Base classs
|
| 455 |
+
base_class = cls.get_base_class(model, base_class)
|
| 456 |
+
|
| 457 |
+
base_class.setup_model(model)
|
| 458 |
+
|
| 459 |
+
# Linear tags
|
| 460 |
+
linear_tags = model.linear_tags
|
| 461 |
+
|
| 462 |
+
# Patch
|
| 463 |
+
base_class.patch_linearlayers(
|
| 464 |
+
model,
|
| 465 |
+
patch_linear_cast_peft,
|
| 466 |
+
dict([(linear_tag, dtype) for linear_tag in linear_tags]),
|
| 467 |
+
verbose=verbose,
|
| 468 |
+
)
|
| 469 |
+
|
| 470 |
+
@classmethod
|
| 471 |
+
def save_lora_weights(
|
| 472 |
+
cls, model, filename: str, base_class: bool = None, verbose: bool = True
|
| 473 |
+
) -> None:
|
| 474 |
+
# Base classs
|
| 475 |
+
base_class = cls.get_base_class(model, base_class)
|
| 476 |
+
|
| 477 |
+
base_class.setup_model(model)
|
| 478 |
+
|
| 479 |
+
lora_global_params = {}
|
| 480 |
+
|
| 481 |
+
def _patch_linear_save_weights(layer, patch_params, return_layer=True):
|
| 482 |
+
if is_hqq_lora_layer(layer):
|
| 483 |
+
lora_global_params[layer.name] = layer.state_dict()
|
| 484 |
+
if return_layer:
|
| 485 |
+
return layer
|
| 486 |
+
|
| 487 |
+
# Linear tags
|
| 488 |
+
linear_tags = model.linear_tags
|
| 489 |
+
|
| 490 |
+
# Patch
|
| 491 |
+
base_class.patch_linearlayers(
|
| 492 |
+
model,
|
| 493 |
+
_patch_linear_save_weights,
|
| 494 |
+
dict([(linear_tag, None) for linear_tag in linear_tags]),
|
| 495 |
+
verbose=verbose,
|
| 496 |
+
)
|
| 497 |
+
|
| 498 |
+
# save
|
| 499 |
+
torch.save(
|
| 500 |
+
{"peft_config": model.peft_config, "parameters": lora_global_params},
|
| 501 |
+
filename,
|
| 502 |
+
)
|
| 503 |
+
|
| 504 |
+
@classmethod
|
| 505 |
+
def load_lora_weights(
|
| 506 |
+
cls, model, filename: str, base_class: bool = None, verbose: bool = True
|
| 507 |
+
) -> None:
|
| 508 |
+
# Base classs
|
| 509 |
+
base_class = cls.get_base_class(model, base_class)
|
| 510 |
+
|
| 511 |
+
base_class.setup_model(model)
|
| 512 |
+
|
| 513 |
+
lora_data = torch.load(filename, map_location="cpu")
|
| 514 |
+
|
| 515 |
+
# V0.2 format: automatically creates lora modules if the model doesn't contain it
|
| 516 |
+
if ("peft_config" in lora_data) and ("parameters" in lora_data):
|
| 517 |
+
peft_config = lora_data["peft_config"]
|
| 518 |
+
lora_global_params = lora_data["parameters"]
|
| 519 |
+
if not hasattr(model, "peft_config"):
|
| 520 |
+
cls.add_lora(model=model, peft_config=peft_config)
|
| 521 |
+
|
| 522 |
+
# v0.1 format
|
| 523 |
+
else:
|
| 524 |
+
if not hasattr(model, "peft_config"):
|
| 525 |
+
raise Exception(
|
| 526 |
+
"Using older version of lora weights. LoRa modules should be manually added in this case."
|
| 527 |
+
)
|
| 528 |
+
lora_global_params = lora_data
|
| 529 |
+
|
| 530 |
+
def _patch_linear_load_weights(layer, patch_params, return_layer=True):
|
| 531 |
+
if is_hqq_lora_layer(layer):
|
| 532 |
+
layer.load_state_dict(lora_global_params[layer.name])
|
| 533 |
+
if return_layer:
|
| 534 |
+
return layer
|
| 535 |
+
|
| 536 |
+
# Linear tags
|
| 537 |
+
linear_tags = model.linear_tags
|
| 538 |
+
|
| 539 |
+
# Patch
|
| 540 |
+
base_class.patch_linearlayers(
|
| 541 |
+
model,
|
| 542 |
+
_patch_linear_load_weights,
|
| 543 |
+
dict([(linear_tag, None) for linear_tag in linear_tags]),
|
| 544 |
+
verbose=verbose,
|
| 545 |
+
)
|
lm-quant-toolkit/.deps/hqq/hqq/core/quantize.py
ADDED
|
@@ -0,0 +1,948 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Written by Dr. Hicham Badri @Mobius Labs GmbH - 2023
|
| 2 |
+
#####################################################
|
| 3 |
+
import copy
|
| 4 |
+
from enum import Enum
|
| 5 |
+
from typing import Union
|
| 6 |
+
|
| 7 |
+
import torch
|
| 8 |
+
from torch import Tensor, float16, int32, nn, uint8
|
| 9 |
+
|
| 10 |
+
from .bitpack import BitPack
|
| 11 |
+
from .optimize import optimize_weights_proximal
|
| 12 |
+
from .utils import is_divisible
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
# Main HQQ Quantizer
|
| 16 |
+
class Quantizer:
|
| 17 |
+
SUPPORTED_BITS = [8, 6, 5, 4, 3, 2, 1]
|
| 18 |
+
optimize_weights = optimize_weights_proximal
|
| 19 |
+
|
| 20 |
+
bit_to_packing = {
|
| 21 |
+
8: "8bit_u8",
|
| 22 |
+
6: "8bit_u8", # todo: bitpacking
|
| 23 |
+
5: "8bit_u8", # todo: bitpacking
|
| 24 |
+
4: "4bit_u8",
|
| 25 |
+
3: "3bit_32",
|
| 26 |
+
2: "2bit_u8",
|
| 27 |
+
1: "1bit_u8",
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
pack = {
|
| 31 |
+
"8bit_u8": BitPack.pack_8bit_u8,
|
| 32 |
+
"4bit_u8": BitPack.pack_4bit_u8,
|
| 33 |
+
"3bit_32": BitPack.pack_3bit_32,
|
| 34 |
+
"2bit_u8": BitPack.pack_2bit_u8,
|
| 35 |
+
"1bit_u8": BitPack.pack_1bit_u8,
|
| 36 |
+
}
|
| 37 |
+
|
| 38 |
+
unpack = {
|
| 39 |
+
"8bit_u8": BitPack.unpack_8bit_u8,
|
| 40 |
+
"4bit_u8": BitPack.unpack_4bit_u8,
|
| 41 |
+
"3bit_32": BitPack.unpack_3bit_32,
|
| 42 |
+
"2bit_u8": BitPack.unpack_2bit_u8,
|
| 43 |
+
"1bit_u8": BitPack.unpack_1bit_u8,
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
unpack_view_dtype = {
|
| 47 |
+
"8bit_u8": uint8,
|
| 48 |
+
"4bit_u8": uint8,
|
| 49 |
+
"3bit_32": int32,
|
| 50 |
+
"2bit_u8": uint8,
|
| 51 |
+
"1bit_u8": uint8,
|
| 52 |
+
}
|
| 53 |
+
|
| 54 |
+
@classmethod
|
| 55 |
+
def quantize(
|
| 56 |
+
cls,
|
| 57 |
+
tensor: Tensor,
|
| 58 |
+
nbits: int = 4,
|
| 59 |
+
channel_wise: bool = True,
|
| 60 |
+
group_size: int = 64,
|
| 61 |
+
optimize: bool = False,
|
| 62 |
+
round_zero: bool = False,
|
| 63 |
+
axis: int = 0,
|
| 64 |
+
bitpack: bool = True,
|
| 65 |
+
compute_dtype: Union[torch.dtype, None] = None,
|
| 66 |
+
view_as_float: bool = False,
|
| 67 |
+
device: str = "cuda",
|
| 68 |
+
) -> tuple:
|
| 69 |
+
assert nbits in Quantizer.SUPPORTED_BITS, (
|
| 70 |
+
"nbits=" + str(nbits) + " not supported."
|
| 71 |
+
)
|
| 72 |
+
assert axis in [0, 1], "axis should be either 0 or 1"
|
| 73 |
+
if group_size is not None:
|
| 74 |
+
assert is_divisible(tensor.numel(), group_size), (
|
| 75 |
+
"group_size should be divisble by the total tensor dimensions. shape: "
|
| 76 |
+
+ str(tensor.shape)
|
| 77 |
+
+ ", group_size: "
|
| 78 |
+
+ str(group_size)
|
| 79 |
+
)
|
| 80 |
+
|
| 81 |
+
W = tensor.float()
|
| 82 |
+
shape = W.shape
|
| 83 |
+
|
| 84 |
+
# Reshape for grouping
|
| 85 |
+
if (group_size is not None) and channel_wise:
|
| 86 |
+
W = (
|
| 87 |
+
W.reshape([-1, group_size])
|
| 88 |
+
if (axis == 1)
|
| 89 |
+
else W.reshape([group_size, -1])
|
| 90 |
+
)
|
| 91 |
+
|
| 92 |
+
# Get min/max values
|
| 93 |
+
if not channel_wise:
|
| 94 |
+
_min, _max = W.min(), W.max()
|
| 95 |
+
optimize = False
|
| 96 |
+
else:
|
| 97 |
+
_min = W.min(axis=axis, keepdim=True)[0]
|
| 98 |
+
_max = W.max(axis=axis, keepdim=True)[0]
|
| 99 |
+
|
| 100 |
+
max_v = 2**nbits - 1
|
| 101 |
+
min_v = 0
|
| 102 |
+
min_max = [min_v, max_v]
|
| 103 |
+
|
| 104 |
+
# Note: here we work with the inverse of the scale to avoid division and quantize instead via W*scale + zero, the scale is inverted later on.
|
| 105 |
+
scale = (max_v / (_max - _min)).clamp(
|
| 106 |
+
max=2e4
|
| 107 |
+
) # clamp to avoid half-precision problems
|
| 108 |
+
zero = -_min * scale
|
| 109 |
+
|
| 110 |
+
# Round zero as in: https://github.com/casper-hansen/AutoAWQ/blob/main/awq/quantize/quantizer.py#L42C9-L42C14
|
| 111 |
+
if round_zero:
|
| 112 |
+
zero = torch.round(zero)
|
| 113 |
+
|
| 114 |
+
# Fine-tune weights
|
| 115 |
+
# if False:
|
| 116 |
+
if optimize:
|
| 117 |
+
W_q, scale, zero = Quantizer.optimize_weights(
|
| 118 |
+
tensor=W,
|
| 119 |
+
scale=scale,
|
| 120 |
+
zero=zero,
|
| 121 |
+
min_max=min_max,
|
| 122 |
+
axis=axis,
|
| 123 |
+
device=device,
|
| 124 |
+
)
|
| 125 |
+
else:
|
| 126 |
+
W_q = torch.round(W * scale + zero).clamp(min_max[0], min_max[1])
|
| 127 |
+
|
| 128 |
+
# Store meta-data (we invert the scale for dequantization)
|
| 129 |
+
meta = {
|
| 130 |
+
"nbits": nbits,
|
| 131 |
+
"group_size": group_size,
|
| 132 |
+
"shape": shape,
|
| 133 |
+
"scale": 1.0 / scale,
|
| 134 |
+
"zero": zero,
|
| 135 |
+
"axis": axis,
|
| 136 |
+
"packing": Quantizer.bit_to_packing[nbits],
|
| 137 |
+
}
|
| 138 |
+
meta["unpack_view_dtype"] = Quantizer.unpack_view_dtype[meta["packing"]]
|
| 139 |
+
|
| 140 |
+
# Pack bits
|
| 141 |
+
meta["view_as_float"] = view_as_float
|
| 142 |
+
if bitpack:
|
| 143 |
+
W_q = Quantizer.pack[meta["packing"]](W_q)
|
| 144 |
+
if view_as_float:
|
| 145 |
+
W_q = W_q.view(
|
| 146 |
+
torch.float32 if compute_dtype is None else compute_dtype
|
| 147 |
+
) # store quantized weights as compute_dtype
|
| 148 |
+
else:
|
| 149 |
+
W_q = W_q.to(tensor.dtype)
|
| 150 |
+
meta["packing"] = None
|
| 151 |
+
|
| 152 |
+
# cleanup
|
| 153 |
+
del W, _min, _max
|
| 154 |
+
torch.cuda.empty_cache()
|
| 155 |
+
|
| 156 |
+
return W_q, meta
|
| 157 |
+
|
| 158 |
+
# Main dequantization: bit_unpacking > (W_q - z)*s > reshape
|
| 159 |
+
@classmethod
|
| 160 |
+
def dequantize(cls, W_q: Tensor, meta: dict) -> Tensor:
|
| 161 |
+
compute_dtype = meta["compute_dtype"] if ("compute_dtype" in meta) else float16
|
| 162 |
+
if meta["packing"]:
|
| 163 |
+
if meta["view_as_float"]:
|
| 164 |
+
W_q = W_q.view(meta["unpack_view_dtype"])
|
| 165 |
+
W_r = Quantizer.unpack[meta["packing"]](W_q, dtype=compute_dtype)
|
| 166 |
+
if meta["nbits"] == 3:
|
| 167 |
+
W_r = W_r[
|
| 168 |
+
: meta["group_size"]
|
| 169 |
+
if meta["axis"] == 0
|
| 170 |
+
else meta["shape"][0] * meta["shape"][1] // meta["group_size"]
|
| 171 |
+
]
|
| 172 |
+
else:
|
| 173 |
+
W_r = W_q.to(compute_dtype)
|
| 174 |
+
zero = meta["zero"].to(compute_dtype)
|
| 175 |
+
scale = meta["scale"].to(compute_dtype)
|
| 176 |
+
W_r = ((W_r - zero) * scale).reshape(meta["shape"])
|
| 177 |
+
return W_r
|
| 178 |
+
|
| 179 |
+
@classmethod
|
| 180 |
+
def to_inplace(cls, W_q: Tensor, meta: dict, device) -> tuple:
|
| 181 |
+
compute_dtype = meta["compute_dtype"] if ("compute_dtype" in meta) else float16
|
| 182 |
+
if W_q is not None:
|
| 183 |
+
W_q = W_q.to(device).contiguous()
|
| 184 |
+
for key in meta:
|
| 185 |
+
if type(meta[key]) == torch.Tensor:
|
| 186 |
+
meta[key] = (
|
| 187 |
+
(
|
| 188 |
+
meta[key].to(compute_dtype)
|
| 189 |
+
if torch.is_floating_point(meta[key])
|
| 190 |
+
else meta[key]
|
| 191 |
+
)
|
| 192 |
+
.to(device)
|
| 193 |
+
.contiguous()
|
| 194 |
+
)
|
| 195 |
+
return W_q, meta
|
| 196 |
+
|
| 197 |
+
@classmethod
|
| 198 |
+
def to_ooplace(cls, W_q: Tensor, meta: dict, device) -> tuple:
|
| 199 |
+
compute_dtype = meta["compute_dtype"] if ("compute_dtype" in meta) else float16
|
| 200 |
+
if W_q is not None:
|
| 201 |
+
W_q_c = W_q.to(device).contiguous()
|
| 202 |
+
else:
|
| 203 |
+
W_q_c = None
|
| 204 |
+
meta_c = {}
|
| 205 |
+
for key in meta:
|
| 206 |
+
if type(meta[key]) == torch.Tensor:
|
| 207 |
+
meta_c[key] = (
|
| 208 |
+
(
|
| 209 |
+
meta[key].to(compute_dtype)
|
| 210 |
+
if torch.is_floating_point(meta[key])
|
| 211 |
+
else meta[key]
|
| 212 |
+
)
|
| 213 |
+
.to(device)
|
| 214 |
+
.contiguous()
|
| 215 |
+
)
|
| 216 |
+
else:
|
| 217 |
+
meta_c[key] = meta[key]
|
| 218 |
+
return W_q_c, meta_c
|
| 219 |
+
|
| 220 |
+
@classmethod
|
| 221 |
+
def cuda(cls, W_q: Tensor, meta: dict, device) -> tuple:
|
| 222 |
+
return Quantizer.to_inplace(W_q, meta, device=device)
|
| 223 |
+
|
| 224 |
+
@classmethod
|
| 225 |
+
def cpu(cls, W_q: Tensor, meta: dict) -> tuple:
|
| 226 |
+
return Quantizer.to_ooplace(W_q, meta, device="cpu")
|
| 227 |
+
|
| 228 |
+
|
| 229 |
+
# Check Aten/CUDA backend if available
|
| 230 |
+
hqq_aten_is_available = False
|
| 231 |
+
try:
|
| 232 |
+
import hqq_aten
|
| 233 |
+
|
| 234 |
+
hqq_aten_is_available = True
|
| 235 |
+
except Exception:
|
| 236 |
+
hqq_aten = None
|
| 237 |
+
hqq_aten_is_available = False
|
| 238 |
+
|
| 239 |
+
|
| 240 |
+
class HQQBackend(Enum):
|
| 241 |
+
# Name of the forward functions
|
| 242 |
+
PYTORCH = "forward_pytorch_backprop"
|
| 243 |
+
PYTORCH_COMPILE = "forward_pytorch_backprop_compile"
|
| 244 |
+
ATEN = "forward_aten_backprop"
|
| 245 |
+
|
| 246 |
+
# Alias for backward compatibility
|
| 247 |
+
PYTORCH_BACKPROP = "forward_pytorch_backprop"
|
| 248 |
+
PYTORCH_BACKPROP_COMPILE = "forward_pytorch_backprop_compile"
|
| 249 |
+
ATEN_BACKPROP = "forward_aten_backprop"
|
| 250 |
+
|
| 251 |
+
PYTORCH_FORWARD = "forward_pytorch"
|
| 252 |
+
PYTORCH_FORWARD_COMPILE = "forward_pytorch_compile"
|
| 253 |
+
ATEN_FORWARD = "forward_aten"
|
| 254 |
+
|
| 255 |
+
# Experimental
|
| 256 |
+
ATEN_FORWARD_INT8 = "forward_aten_int8"
|
| 257 |
+
|
| 258 |
+
|
| 259 |
+
# No cache: less memory, slower
|
| 260 |
+
class HQQMatmulNoCacheDeq(torch.autograd.Function):
|
| 261 |
+
@staticmethod
|
| 262 |
+
def forward(x: Tensor, dequantize, bias: Tensor):
|
| 263 |
+
out = torch.matmul(x, dequantize().t())
|
| 264 |
+
if bias is not None:
|
| 265 |
+
out += bias
|
| 266 |
+
return out
|
| 267 |
+
|
| 268 |
+
@staticmethod
|
| 269 |
+
def setup_context(ctx, inputs, outputs):
|
| 270 |
+
x, dequantize, bias = inputs
|
| 271 |
+
ctx.save_for_backward(x, bias)
|
| 272 |
+
ctx.dequantize = dequantize
|
| 273 |
+
|
| 274 |
+
@staticmethod
|
| 275 |
+
def backward(ctx, grad_output):
|
| 276 |
+
x, bias = ctx.saved_tensors
|
| 277 |
+
|
| 278 |
+
grad_input = grad_weight = grad_bias = None
|
| 279 |
+
|
| 280 |
+
if ctx.needs_input_grad[0]:
|
| 281 |
+
grad_input = torch.matmul(grad_output, ctx.dequantize())
|
| 282 |
+
|
| 283 |
+
# weight grad for frozen quantized weights not defined
|
| 284 |
+
# if ctx.needs_input_grad[1]:
|
| 285 |
+
# grad_weight = torch.matmul(grad_output.t(), x)
|
| 286 |
+
|
| 287 |
+
if bias is not None and ctx.needs_input_grad[2]:
|
| 288 |
+
grad_bias = grad_output.sum(0)
|
| 289 |
+
|
| 290 |
+
return grad_input, grad_weight, grad_bias
|
| 291 |
+
|
| 292 |
+
|
| 293 |
+
class HQQMatmulNoCacheMul(torch.autograd.Function):
|
| 294 |
+
@staticmethod
|
| 295 |
+
def forward(x, matmul, bias):
|
| 296 |
+
out = matmul(x, transpose=True)
|
| 297 |
+
if bias is not None:
|
| 298 |
+
out += bias
|
| 299 |
+
return out
|
| 300 |
+
|
| 301 |
+
@staticmethod
|
| 302 |
+
def setup_context(ctx, inputs, outputs):
|
| 303 |
+
x, matmul, bias = inputs
|
| 304 |
+
ctx.save_for_backward(x, bias)
|
| 305 |
+
ctx.matmul = matmul
|
| 306 |
+
|
| 307 |
+
@staticmethod
|
| 308 |
+
def backward(ctx, grad_output):
|
| 309 |
+
x, bias = ctx.saved_tensors
|
| 310 |
+
|
| 311 |
+
grad_input = grad_weight = grad_bias = None
|
| 312 |
+
|
| 313 |
+
if ctx.needs_input_grad[0]:
|
| 314 |
+
grad_input = ctx.matmul(grad_output, transpose=False)
|
| 315 |
+
|
| 316 |
+
# weight grad for frozen quantized weights not defined
|
| 317 |
+
# if ctx.needs_input_grad[1]:
|
| 318 |
+
# grad_weight = torch.matmul(grad_output.t(), x)
|
| 319 |
+
|
| 320 |
+
if bias is not None and ctx.needs_input_grad[2]:
|
| 321 |
+
grad_bias = grad_output.sum(0)
|
| 322 |
+
|
| 323 |
+
return grad_input, grad_weight, grad_bias
|
| 324 |
+
|
| 325 |
+
|
| 326 |
+
# Cache dequantized tensor: Faster but needs more memory
|
| 327 |
+
class HQQMatmulCachedDeq(torch.autograd.Function):
|
| 328 |
+
@staticmethod
|
| 329 |
+
def forward(ctx, x, hqq_layer, bias):
|
| 330 |
+
weight_tmp = hqq_layer.dequantize()
|
| 331 |
+
out = torch.matmul(x, weight_tmp.t())
|
| 332 |
+
if bias is not None:
|
| 333 |
+
out += bias
|
| 334 |
+
|
| 335 |
+
ctx.save_for_backward(x, bias, weight_tmp)
|
| 336 |
+
return out
|
| 337 |
+
|
| 338 |
+
@staticmethod
|
| 339 |
+
def backward(ctx, grad_output):
|
| 340 |
+
x, bias, weight_tmp = ctx.saved_tensors
|
| 341 |
+
|
| 342 |
+
grad_input = grad_weight = grad_bias = None
|
| 343 |
+
|
| 344 |
+
if ctx.needs_input_grad[0]:
|
| 345 |
+
grad_input = torch.matmul(grad_output, weight_tmp)
|
| 346 |
+
|
| 347 |
+
del weight_tmp
|
| 348 |
+
|
| 349 |
+
if bias is not None and ctx.needs_input_grad[2]:
|
| 350 |
+
grad_bias = grad_output.sum(0)
|
| 351 |
+
|
| 352 |
+
return grad_input, grad_weight, grad_bias
|
| 353 |
+
|
| 354 |
+
|
| 355 |
+
# Main linear layer
|
| 356 |
+
class HQQLinear(nn.Module):
|
| 357 |
+
# Default backend
|
| 358 |
+
backend = HQQBackend.PYTORCH
|
| 359 |
+
|
| 360 |
+
def __init__(
|
| 361 |
+
self,
|
| 362 |
+
linear_layer: Union[nn.Module, None],
|
| 363 |
+
quant_config: dict,
|
| 364 |
+
del_orig: bool = True,
|
| 365 |
+
compute_dtype: torch.dtype = float16,
|
| 366 |
+
device: str = "cuda",
|
| 367 |
+
initialize: bool = True,
|
| 368 |
+
):
|
| 369 |
+
super().__init__()
|
| 370 |
+
self.ready = False
|
| 371 |
+
self.in_gpu = False
|
| 372 |
+
self.bias = None
|
| 373 |
+
self.device = device
|
| 374 |
+
self.compute_dtype = compute_dtype
|
| 375 |
+
self.quant_config = copy.deepcopy(quant_config)
|
| 376 |
+
self.del_orig = del_orig
|
| 377 |
+
self.offload_meta = (
|
| 378 |
+
self.quant_config.pop("offload_meta")
|
| 379 |
+
if (self.quant_config is not None)
|
| 380 |
+
else None
|
| 381 |
+
)
|
| 382 |
+
|
| 383 |
+
self.set_backend(HQQLinear.backend)
|
| 384 |
+
|
| 385 |
+
self.linear_layer = linear_layer
|
| 386 |
+
self.W_q = None
|
| 387 |
+
self.meta = None
|
| 388 |
+
|
| 389 |
+
if initialize:
|
| 390 |
+
self.initialize()
|
| 391 |
+
|
| 392 |
+
def initialize(self):
|
| 393 |
+
if self.linear_layer is not None:
|
| 394 |
+
self.quantize(self.linear_layer.weight.data, **self.quant_config)
|
| 395 |
+
self.bias = (
|
| 396 |
+
None
|
| 397 |
+
if (self.linear_layer.bias is None)
|
| 398 |
+
else self.linear_layer.bias.to(
|
| 399 |
+
device=self.device, dtype=self.compute_dtype
|
| 400 |
+
)
|
| 401 |
+
)
|
| 402 |
+
|
| 403 |
+
if self.del_orig:
|
| 404 |
+
del self.linear_layer
|
| 405 |
+
torch.cuda.empty_cache()
|
| 406 |
+
|
| 407 |
+
def extra_repr(self) -> str:
|
| 408 |
+
out = ""
|
| 409 |
+
if hasattr(self, "meta"):
|
| 410 |
+
if self.meta is not None:
|
| 411 |
+
in_features, out_features = self.meta["shape"][::-1]
|
| 412 |
+
out = f"in_features={in_features}, out_features={out_features}, bias={self.bias is not None}"
|
| 413 |
+
return out
|
| 414 |
+
|
| 415 |
+
# Set backends
|
| 416 |
+
@classmethod
|
| 417 |
+
def set_backend(cls, backend: HQQBackend):
|
| 418 |
+
if "aten" in backend.value:
|
| 419 |
+
if hqq_aten_is_available is False:
|
| 420 |
+
print(
|
| 421 |
+
"ATEN/CUDA backend not availabe. Make sure you install the hqq_aten library."
|
| 422 |
+
)
|
| 423 |
+
return
|
| 424 |
+
print(
|
| 425 |
+
"Warning: the ATEN/CUDA backend only supports axis=0 and GPU runtime."
|
| 426 |
+
)
|
| 427 |
+
HQQLinear.backend = backend
|
| 428 |
+
cls.forward = getattr(cls, backend.value)
|
| 429 |
+
|
| 430 |
+
# TODO: rewrite this mess
|
| 431 |
+
def cuda(self, device):
|
| 432 |
+
self.meta["compute_dtype"] = self.compute_dtype
|
| 433 |
+
|
| 434 |
+
if type(self.W_q) == nn.parameter.Parameter:
|
| 435 |
+
self.W_q.data, self.meta = Quantizer.cuda(self.W_q.data, self.meta, device)
|
| 436 |
+
else:
|
| 437 |
+
self.W_q, self.meta = Quantizer.cuda(self.W_q, self.meta, device)
|
| 438 |
+
|
| 439 |
+
if self.meta["quant_zero"]:
|
| 440 |
+
if "zero_q" in self.meta:
|
| 441 |
+
self.meta["zero_q"], self.meta["meta_zero"] = Quantizer.cuda(
|
| 442 |
+
self.meta["zero_q"], self.meta["meta_zero"], device
|
| 443 |
+
)
|
| 444 |
+
else:
|
| 445 |
+
_, self.meta["meta_zero"] = Quantizer.cuda(
|
| 446 |
+
None, self.meta["meta_zero"], device
|
| 447 |
+
)
|
| 448 |
+
else:
|
| 449 |
+
self.meta["zero"] = self.meta["zero"].to(device)
|
| 450 |
+
|
| 451 |
+
if self.meta["quant_scale"]:
|
| 452 |
+
if "scale_q" in self.meta:
|
| 453 |
+
self.meta["scale_q"], self.meta["meta_scale"] = Quantizer.cuda(
|
| 454 |
+
self.meta["scale_q"], self.meta["meta_scale"], device
|
| 455 |
+
)
|
| 456 |
+
else:
|
| 457 |
+
_, self.meta["meta_scale"] = Quantizer.cuda(
|
| 458 |
+
None, self.meta["meta_scale"], device
|
| 459 |
+
)
|
| 460 |
+
else:
|
| 461 |
+
self.meta["scale"] = self.meta["scale"].to(device)
|
| 462 |
+
|
| 463 |
+
# #Use zero/scale with streams for dequantization is faster than packing in "zero_scale"
|
| 464 |
+
# for key in ["zero", "zero_q", "scale", "scale_q"]:
|
| 465 |
+
# if((key in self.meta) and self.offload_meta):
|
| 466 |
+
# self.meta[key] = self.meta[key].contiguous().cpu().pin_memory()
|
| 467 |
+
|
| 468 |
+
if self.offload_meta:
|
| 469 |
+
if "zero_scale" not in self.meta:
|
| 470 |
+
if self.meta["quant_scale"] and self.meta["quant_zero"]:
|
| 471 |
+
self.meta["zero_scale"] = torch.stack(
|
| 472 |
+
(self.meta["zero_q"], self.meta["scale_q"])
|
| 473 |
+
)
|
| 474 |
+
del self.meta["scale_q"], self.meta["zero_q"]
|
| 475 |
+
else:
|
| 476 |
+
self.meta["zero_scale"] = torch.stack(
|
| 477 |
+
(self.meta["zero"], self.meta["scale"])
|
| 478 |
+
).to(self.compute_dtype)
|
| 479 |
+
del self.meta["scale"], self.meta["zero"]
|
| 480 |
+
|
| 481 |
+
self.meta["zero_scale"] = (
|
| 482 |
+
self.meta["zero_scale"].contiguous().cpu().pin_memory()
|
| 483 |
+
)
|
| 484 |
+
|
| 485 |
+
if self.bias is not None:
|
| 486 |
+
self.bias = self.bias.to(device=device, dtype=self.compute_dtype)
|
| 487 |
+
|
| 488 |
+
self.W_q = nn.Parameter(self.W_q, requires_grad=False)
|
| 489 |
+
self.device = device
|
| 490 |
+
self.in_gpu = True
|
| 491 |
+
|
| 492 |
+
torch.cuda.empty_cache()
|
| 493 |
+
|
| 494 |
+
return self
|
| 495 |
+
|
| 496 |
+
def to(self, *args, **kwargs):
|
| 497 |
+
# TODO: later
|
| 498 |
+
return self
|
| 499 |
+
|
| 500 |
+
# TODO: later
|
| 501 |
+
# def to_empty(self, device, recurse=True):
|
| 502 |
+
# return self.cuda(device)
|
| 503 |
+
|
| 504 |
+
def type(self, dst_type):
|
| 505 |
+
# TODO: later
|
| 506 |
+
return self
|
| 507 |
+
|
| 508 |
+
def half(self, *args, **kwargs):
|
| 509 |
+
return self
|
| 510 |
+
|
| 511 |
+
def bfloat16(self, *args, **kwargs):
|
| 512 |
+
# TODO: later
|
| 513 |
+
return self
|
| 514 |
+
|
| 515 |
+
def float(self, *args, **kwargs):
|
| 516 |
+
# TODO: later
|
| 517 |
+
return self
|
| 518 |
+
|
| 519 |
+
def double(self, *args, **kwargs):
|
| 520 |
+
return self
|
| 521 |
+
|
| 522 |
+
def cpu(self):
|
| 523 |
+
# TODO: later
|
| 524 |
+
return self
|
| 525 |
+
|
| 526 |
+
def state_dict(self, *args, **kwargs): # nn.Module override compatible
|
| 527 |
+
state = {"W_q": self.W_q, "meta": self.meta, "bias": self.bias}
|
| 528 |
+
if "destination" in kwargs and "prefix" in kwargs:
|
| 529 |
+
for key, value in state.items():
|
| 530 |
+
kwargs["destination"][kwargs["prefix"] + key] = value
|
| 531 |
+
return state
|
| 532 |
+
|
| 533 |
+
def _load_from_state_dict(
|
| 534 |
+
self,
|
| 535 |
+
state_dict,
|
| 536 |
+
prefix,
|
| 537 |
+
local_metadata,
|
| 538 |
+
strict,
|
| 539 |
+
missing_keys,
|
| 540 |
+
unexpected_keys,
|
| 541 |
+
error_msgs,
|
| 542 |
+
):
|
| 543 |
+
W_q_key = prefix + "W_q"
|
| 544 |
+
meta_key = prefix + "meta"
|
| 545 |
+
bias_key = prefix + "bias"
|
| 546 |
+
|
| 547 |
+
if W_q_key not in state_dict:
|
| 548 |
+
missing_keys.append(W_q_key)
|
| 549 |
+
if meta_key not in state_dict:
|
| 550 |
+
missing_keys.append(meta_key)
|
| 551 |
+
if missing_keys:
|
| 552 |
+
return # Can't load weights if either weight or meta is missing
|
| 553 |
+
|
| 554 |
+
W_q = nn.Parameter(state_dict.pop(W_q_key), requires_grad=False)
|
| 555 |
+
meta = state_dict.pop(meta_key)
|
| 556 |
+
bias = state_dict.pop(bias_key, None)
|
| 557 |
+
|
| 558 |
+
unexpected_keys += state_dict.keys()
|
| 559 |
+
|
| 560 |
+
self.load_state_dict({"W_q": W_q, "meta": meta, "bias": bias}, strict)
|
| 561 |
+
|
| 562 |
+
def load_state_dict(self, state_dict, strict=True, assign=False):
|
| 563 |
+
self.W_q = state_dict["W_q"]
|
| 564 |
+
self.meta = state_dict["meta"]
|
| 565 |
+
self.bias = state_dict["bias"] if ("bias" in state_dict) else None
|
| 566 |
+
|
| 567 |
+
# Meta-data offloading
|
| 568 |
+
self.offload_meta = False
|
| 569 |
+
for key in ["zero", "zero_q", "scale", "scale_q", "zero_scale"]:
|
| 570 |
+
if key in self.meta:
|
| 571 |
+
if self.meta[key].device.type == "cpu":
|
| 572 |
+
self.offload_meta = True
|
| 573 |
+
self.meta[key] = self.meta[key].contiguous().pin_memory()
|
| 574 |
+
|
| 575 |
+
# Float view settings
|
| 576 |
+
if "unpack_view_dtype" not in self.meta:
|
| 577 |
+
self.meta["unpack_view_dtype"] = Quantizer.unpack_view_dtype[
|
| 578 |
+
self.meta["packing"]
|
| 579 |
+
]
|
| 580 |
+
|
| 581 |
+
if "view_as_float" not in self.meta:
|
| 582 |
+
self.meta["view_as_float"] = False
|
| 583 |
+
|
| 584 |
+
if "meta_scale" in self.meta:
|
| 585 |
+
if "view_as_float" not in self.meta["meta_scale"]:
|
| 586 |
+
self.meta["meta_scale"]["view_as_float"] = False
|
| 587 |
+
|
| 588 |
+
if "meta_zero" in self.meta:
|
| 589 |
+
if "view_as_float" not in self.meta["meta_zero"]:
|
| 590 |
+
self.meta["meta_zero"]["view_as_float"] = False
|
| 591 |
+
|
| 592 |
+
# Check GPU
|
| 593 |
+
self.cuda(self.device)
|
| 594 |
+
self.ready = True
|
| 595 |
+
|
| 596 |
+
# Set in_features/out_features
|
| 597 |
+
self.in_features, self.out_features = self.meta["shape"][::-1]
|
| 598 |
+
|
| 599 |
+
def quantize(
|
| 600 |
+
self,
|
| 601 |
+
W: Tensor,
|
| 602 |
+
weight_quant_params: dict,
|
| 603 |
+
scale_quant_params: dict,
|
| 604 |
+
zero_quant_params: dict,
|
| 605 |
+
) -> None:
|
| 606 |
+
quant_scale = scale_quant_params is not None
|
| 607 |
+
quant_zero = zero_quant_params is not None
|
| 608 |
+
|
| 609 |
+
self.in_features, self.out_features = W.t().shape
|
| 610 |
+
|
| 611 |
+
# Quantize
|
| 612 |
+
W_q, meta = Quantizer.quantize(
|
| 613 |
+
W,
|
| 614 |
+
device=self.device,
|
| 615 |
+
compute_dtype=self.compute_dtype,
|
| 616 |
+
**weight_quant_params,
|
| 617 |
+
)
|
| 618 |
+
meta.update({"quant_scale": quant_scale, "quant_zero": quant_zero})
|
| 619 |
+
|
| 620 |
+
if meta["quant_zero"]:
|
| 621 |
+
meta["zero_q"], meta["meta_zero"] = Quantizer.quantize(
|
| 622 |
+
meta["zero"],
|
| 623 |
+
device=self.device,
|
| 624 |
+
view_as_float=False,
|
| 625 |
+
**zero_quant_params,
|
| 626 |
+
)
|
| 627 |
+
del meta["zero"]
|
| 628 |
+
meta["meta_zero"]["compute_dtype"] = self.compute_dtype
|
| 629 |
+
|
| 630 |
+
if meta["quant_scale"]:
|
| 631 |
+
meta["scale_q"], meta["meta_scale"] = Quantizer.quantize(
|
| 632 |
+
meta["scale"],
|
| 633 |
+
device=self.device,
|
| 634 |
+
view_as_float=False,
|
| 635 |
+
**scale_quant_params,
|
| 636 |
+
)
|
| 637 |
+
del meta["scale"]
|
| 638 |
+
meta["meta_scale"]["compute_dtype"] = self.compute_dtype
|
| 639 |
+
|
| 640 |
+
self.W_q = W_q
|
| 641 |
+
self.meta = meta
|
| 642 |
+
self.cuda(self.device)
|
| 643 |
+
self.ready = True
|
| 644 |
+
|
| 645 |
+
def dequantize(self):
|
| 646 |
+
assert self.ready, "model was not quantized"
|
| 647 |
+
W_q, meta = self.W_q, self.meta
|
| 648 |
+
device = W_q.device
|
| 649 |
+
del_keys = set()
|
| 650 |
+
|
| 651 |
+
# Zero/Scale packed together
|
| 652 |
+
if "zero_scale" in meta:
|
| 653 |
+
zero_scale = meta["zero_scale"].to(device=device)
|
| 654 |
+
|
| 655 |
+
if zero_scale.dtype == uint8:
|
| 656 |
+
meta["zero_q"], meta["scale_q"] = zero_scale[0], zero_scale[1]
|
| 657 |
+
del_keys.update({"zero_q", "scale_q"})
|
| 658 |
+
else:
|
| 659 |
+
meta["zero"], meta["scale"] = zero_scale[0], zero_scale[1]
|
| 660 |
+
del_keys.update({"zero", "scale"})
|
| 661 |
+
|
| 662 |
+
if meta["quant_zero"]:
|
| 663 |
+
meta["zero"] = Quantizer.dequantize(
|
| 664 |
+
meta["zero_q"].to(device=device), meta["meta_zero"]
|
| 665 |
+
)
|
| 666 |
+
del_keys.add("zero")
|
| 667 |
+
|
| 668 |
+
if meta["quant_scale"]:
|
| 669 |
+
meta["scale"] = Quantizer.dequantize(
|
| 670 |
+
meta["scale_q"].to(device=device), meta["meta_scale"]
|
| 671 |
+
)
|
| 672 |
+
del_keys.add("scale")
|
| 673 |
+
|
| 674 |
+
W_est = Quantizer.dequantize(W_q, meta)
|
| 675 |
+
|
| 676 |
+
# Cleanup
|
| 677 |
+
for key in del_keys:
|
| 678 |
+
del meta[key]
|
| 679 |
+
return W_est
|
| 680 |
+
|
| 681 |
+
def matmul(self, x: Tensor, transpose: bool = True) -> Tensor:
|
| 682 |
+
weight = self.dequantize()
|
| 683 |
+
return torch.matmul(x, weight.t() if (transpose) else weight)
|
| 684 |
+
|
| 685 |
+
@torch.compile()
|
| 686 |
+
def matmul_compile(self, *args, **kwargs):
|
| 687 |
+
return self.matmul(*args, **kwargs)
|
| 688 |
+
|
| 689 |
+
def forward_pytorch_backprop(self, x: Tensor) -> Tensor:
|
| 690 |
+
return HQQMatmulNoCacheMul.apply(x, self.matmul, self.bias)
|
| 691 |
+
|
| 692 |
+
def forward_pytorch_backprop_compile(self, x: Tensor) -> Tensor:
|
| 693 |
+
return HQQMatmulNoCacheMul.apply(x, self.matmul_compile, self.bias)
|
| 694 |
+
|
| 695 |
+
def forward_pytorch(self, x: Tensor) -> Tensor:
|
| 696 |
+
out = torch.matmul(x, self.dequantize().t())
|
| 697 |
+
if self.bias is not None:
|
| 698 |
+
out += self.bias
|
| 699 |
+
return out
|
| 700 |
+
|
| 701 |
+
@torch.compile()
|
| 702 |
+
def forward_pytorch_compile(self, x: Tensor) -> Tensor:
|
| 703 |
+
return self.forward_pytorch(x)
|
| 704 |
+
|
| 705 |
+
############################################################################################
|
| 706 |
+
# ATen C++ / CUDA Bacekdn
|
| 707 |
+
##########################################################################################
|
| 708 |
+
# Requires building the aten backend
|
| 709 |
+
@torch.jit.ignore
|
| 710 |
+
def dequantize_Wq_aten(self, W_q: Tensor, meta: dict):
|
| 711 |
+
if meta["view_as_float"]:
|
| 712 |
+
W_q = W_q.view(meta["unpack_view_dtype"])
|
| 713 |
+
|
| 714 |
+
return hqq_aten.dequantize(
|
| 715 |
+
W_q,
|
| 716 |
+
meta["scale"],
|
| 717 |
+
meta["zero"],
|
| 718 |
+
meta["shape"],
|
| 719 |
+
meta["group_size"] if (meta["group_size"]) else -1,
|
| 720 |
+
meta["nbits"],
|
| 721 |
+
meta["axis"],
|
| 722 |
+
meta["packing"],
|
| 723 |
+
)
|
| 724 |
+
|
| 725 |
+
def dequantize_aten(self):
|
| 726 |
+
# Dequantize
|
| 727 |
+
assert self.ready, "model was not quantized"
|
| 728 |
+
assert (
|
| 729 |
+
self.meta["axis"] == 0
|
| 730 |
+
), "only axis=0 is supported. Use HQQLinear.set_backend(HQQBackend.PYTORCH) instead."
|
| 731 |
+
|
| 732 |
+
W_q, meta = self.W_q, self.meta
|
| 733 |
+
device = W_q.device
|
| 734 |
+
del_keys = set()
|
| 735 |
+
|
| 736 |
+
# Zero/Scale packed together
|
| 737 |
+
if "zero_scale" in meta:
|
| 738 |
+
zero_scale = meta["zero_scale"].to(device=device, non_blocking=True)
|
| 739 |
+
if zero_scale.dtype == uint8:
|
| 740 |
+
meta["zero_q"], meta["scale_q"] = zero_scale[0], zero_scale[1]
|
| 741 |
+
del_keys.update({"zero_q", "scale_q"})
|
| 742 |
+
else:
|
| 743 |
+
meta["zero"], meta["scale"] = zero_scale[0], zero_scale[1]
|
| 744 |
+
del_keys.update({"zero", "scale"})
|
| 745 |
+
|
| 746 |
+
# Dequantize zero_q / scale_q with device loading
|
| 747 |
+
if meta["quant_zero"]:
|
| 748 |
+
if meta["meta_zero"]["group_size"]:
|
| 749 |
+
meta["zero"] = self.dequantize_Wq_aten(
|
| 750 |
+
meta["zero_q"].to(device=device), meta["meta_zero"]
|
| 751 |
+
)
|
| 752 |
+
else:
|
| 753 |
+
meta["zero"] = Quantizer.dequantize(
|
| 754 |
+
meta["zero_q"].to(device=device), meta["meta_zero"]
|
| 755 |
+
)
|
| 756 |
+
del_keys.add("zero")
|
| 757 |
+
|
| 758 |
+
if meta["quant_scale"]:
|
| 759 |
+
if meta["meta_scale"]["group_size"]:
|
| 760 |
+
meta["scale"] = self.dequantize_Wq_aten(
|
| 761 |
+
meta["scale_q"].to(device=device), meta["meta_scale"]
|
| 762 |
+
)
|
| 763 |
+
else:
|
| 764 |
+
meta["scale"] = Quantizer.dequantize(
|
| 765 |
+
meta["scale_q"].to(device=device), meta["meta_scale"]
|
| 766 |
+
)
|
| 767 |
+
del_keys.add("scale")
|
| 768 |
+
|
| 769 |
+
# Reconstruct the weights
|
| 770 |
+
W_est = self.dequantize_Wq_aten(W_q, meta)
|
| 771 |
+
|
| 772 |
+
# Cleanup
|
| 773 |
+
for key in del_keys:
|
| 774 |
+
del meta[key]
|
| 775 |
+
|
| 776 |
+
return W_est
|
| 777 |
+
|
| 778 |
+
# Much faster with data-offloading zero_q/scale_q but takes more VRAM
|
| 779 |
+
def dequantize_aten_with_streams(self):
|
| 780 |
+
# Create streams
|
| 781 |
+
if hasattr(self, "stream_zero") is False:
|
| 782 |
+
self.stream_zero = torch.cuda.Stream()
|
| 783 |
+
self.stream_scale = torch.cuda.Stream()
|
| 784 |
+
|
| 785 |
+
# Dequantize
|
| 786 |
+
assert self.ready, "model was not quantized"
|
| 787 |
+
W_q, meta = self.W_q, self.meta
|
| 788 |
+
device = W_q.device
|
| 789 |
+
del_keys = set()
|
| 790 |
+
|
| 791 |
+
# Zero/Scale packed together
|
| 792 |
+
if "zero_scale" in meta:
|
| 793 |
+
zero_scale = meta["zero_scale"].to(device=device, non_blocking=True)
|
| 794 |
+
if zero_scale.dtype == uint8:
|
| 795 |
+
meta["zero_q"], meta["scale_q"] = zero_scale[0], zero_scale[1]
|
| 796 |
+
del_keys.update({"zero_q", "scale_q"})
|
| 797 |
+
else:
|
| 798 |
+
meta["zero"], meta["scale"] = zero_scale[0], zero_scale[1]
|
| 799 |
+
del_keys.update({"zero", "scale"})
|
| 800 |
+
|
| 801 |
+
# Using non_blocking=False for the moment, otherwise it can result in strange behaviors
|
| 802 |
+
non_blocking = False
|
| 803 |
+
with torch.cuda.stream(self.stream_zero):
|
| 804 |
+
if meta["quant_zero"]:
|
| 805 |
+
if meta["meta_zero"]["group_size"]:
|
| 806 |
+
meta["zero"] = self.dequantize_Wq_aten(
|
| 807 |
+
meta["zero_q"].to(device=device, non_blocking=non_blocking),
|
| 808 |
+
meta["meta_zero"],
|
| 809 |
+
)
|
| 810 |
+
else:
|
| 811 |
+
meta["zero"] = Quantizer.dequantize(
|
| 812 |
+
meta["zero_q"].to(device=device, non_blocking=non_blocking),
|
| 813 |
+
meta["meta_zero"],
|
| 814 |
+
)
|
| 815 |
+
del_keys.add("zero")
|
| 816 |
+
|
| 817 |
+
with torch.cuda.stream(self.stream_scale):
|
| 818 |
+
if meta["quant_scale"]:
|
| 819 |
+
if meta["meta_scale"]["group_size"]:
|
| 820 |
+
meta["scale"] = self.dequantize_Wq_aten(
|
| 821 |
+
meta["scale_q"].to(device=device, non_blocking=non_blocking),
|
| 822 |
+
meta["meta_scale"],
|
| 823 |
+
)
|
| 824 |
+
else:
|
| 825 |
+
meta["scale"] = Quantizer.dequantize(
|
| 826 |
+
meta["scale_q"].to(device=device, non_blocking=non_blocking),
|
| 827 |
+
meta["meta_scale"],
|
| 828 |
+
)
|
| 829 |
+
del_keys.add("scale")
|
| 830 |
+
|
| 831 |
+
# Wait for streams to finish
|
| 832 |
+
torch.cuda.synchronize()
|
| 833 |
+
|
| 834 |
+
# Reconstruct the weights
|
| 835 |
+
W_est = self.dequantize_Wq_aten(W_q, meta)
|
| 836 |
+
|
| 837 |
+
# Cleanup
|
| 838 |
+
for key in del_keys:
|
| 839 |
+
del meta[key]
|
| 840 |
+
|
| 841 |
+
return W_est
|
| 842 |
+
|
| 843 |
+
def forward_aten(self, x: Tensor) -> Tensor:
|
| 844 |
+
W_est = self.dequantize_aten()
|
| 845 |
+
out = torch.matmul(x, W_est.t())
|
| 846 |
+
if self.bias is not None:
|
| 847 |
+
out += self.bias
|
| 848 |
+
|
| 849 |
+
return out
|
| 850 |
+
|
| 851 |
+
def forward_aten_backprop(self, x: Tensor) -> Tensor:
|
| 852 |
+
return HQQMatmulNoCacheDeq.apply(x, self.dequantize_aten, self.bias)
|
| 853 |
+
|
| 854 |
+
# TODO: as fused kernel in CUDA
|
| 855 |
+
def _get_int8_matrix(self, M):
|
| 856 |
+
scale = torch.abs(M).amax() / 127.0
|
| 857 |
+
return torch.round(M / scale).to(torch.int8), scale.float()
|
| 858 |
+
|
| 859 |
+
# TODO: in ATEN
|
| 860 |
+
@torch.compile()
|
| 861 |
+
def _matmul_int8(self, A, B):
|
| 862 |
+
dtype = A.dtype
|
| 863 |
+
A, scale_A = self._get_int8_matrix(A)
|
| 864 |
+
B, scale_B = self._get_int8_matrix(B)
|
| 865 |
+
return (torch._int_mm(A, B) * (scale_A * scale_B)).to(dtype)
|
| 866 |
+
|
| 867 |
+
def forward_aten_int8(self, x: Tensor) -> Tensor:
|
| 868 |
+
W_est = self.dequantize_aten()
|
| 869 |
+
out = self._matmul_int8(x[0], W_est.t())[None, ...]
|
| 870 |
+
if self.bias is not None:
|
| 871 |
+
out += self.bias
|
| 872 |
+
|
| 873 |
+
return out
|
| 874 |
+
|
| 875 |
+
# TODO: fix this hack later for open_clip
|
| 876 |
+
@property
|
| 877 |
+
def weight(self):
|
| 878 |
+
return self.dequantize()
|
| 879 |
+
|
| 880 |
+
|
| 881 |
+
def hqq_base_quant_config(
|
| 882 |
+
nbits: int = 4,
|
| 883 |
+
group_size: int = 64,
|
| 884 |
+
quant_zero: bool = True,
|
| 885 |
+
quant_scale: bool = False,
|
| 886 |
+
offload_meta: bool = False, # meta-data should be quantized with the same settings to use offload_meta
|
| 887 |
+
view_as_float: bool = False,
|
| 888 |
+
axis: int = 0,
|
| 889 |
+
mixed: bool = False, # Auto tune nbits and group_size according to weight distribution
|
| 890 |
+
budget: float = 0.0, # overall quantization budget as bits per parameter
|
| 891 |
+
):
|
| 892 |
+
assert (
|
| 893 |
+
nbits in Quantizer.SUPPORTED_BITS
|
| 894 |
+
), "nbits value not supported. Check Quantizer.SUPPORTED_BITS."
|
| 895 |
+
if group_size is not None:
|
| 896 |
+
assert is_divisible(
|
| 897 |
+
group_size, 8
|
| 898 |
+
), "Invalid group_size param: the value should be a multiple of 8."
|
| 899 |
+
weight_quant_params = {
|
| 900 |
+
"nbits": nbits,
|
| 901 |
+
"channel_wise": True,
|
| 902 |
+
"group_size": group_size,
|
| 903 |
+
"optimize": True,
|
| 904 |
+
"round_zero": True if nbits == 4 else False,
|
| 905 |
+
"axis": axis,
|
| 906 |
+
"view_as_float": view_as_float,
|
| 907 |
+
}
|
| 908 |
+
|
| 909 |
+
if offload_meta:
|
| 910 |
+
if quant_scale != quant_zero:
|
| 911 |
+
# print(colored("quant_zero and quant_scale must be the same when offload_meta is set to True. Setting quant_scale=quant_zero." , 'yellow'))
|
| 912 |
+
quant_scale = quant_zero
|
| 913 |
+
|
| 914 |
+
scale_quant_params = (
|
| 915 |
+
{"nbits": 8, "channel_wise": True, "group_size": 128, "optimize": False}
|
| 916 |
+
if (quant_scale)
|
| 917 |
+
else None
|
| 918 |
+
)
|
| 919 |
+
zero_quant_params = (
|
| 920 |
+
{"nbits": 8, "channel_wise": True, "group_size": 128, "optimize": False}
|
| 921 |
+
if (quant_zero)
|
| 922 |
+
else None
|
| 923 |
+
)
|
| 924 |
+
|
| 925 |
+
else:
|
| 926 |
+
scale_quant_params = (
|
| 927 |
+
{"nbits": 8, "channel_wise": True, "group_size": 128, "optimize": False}
|
| 928 |
+
if (quant_scale)
|
| 929 |
+
else None
|
| 930 |
+
)
|
| 931 |
+
zero_quant_params = (
|
| 932 |
+
{"nbits": 8, "channel_wise": False, "group_size": None, "optimize": False}
|
| 933 |
+
if (quant_zero)
|
| 934 |
+
else None
|
| 935 |
+
)
|
| 936 |
+
|
| 937 |
+
return {
|
| 938 |
+
"weight_quant_params": weight_quant_params,
|
| 939 |
+
"scale_quant_params": scale_quant_params,
|
| 940 |
+
"zero_quant_params": zero_quant_params,
|
| 941 |
+
"offload_meta": offload_meta,
|
| 942 |
+
"mixed": mixed,
|
| 943 |
+
"budget": budget,
|
| 944 |
+
}
|
| 945 |
+
|
| 946 |
+
|
| 947 |
+
# Alias: follow similar Auto-GPTQ naming
|
| 948 |
+
BaseQuantizeConfig = hqq_base_quant_config
|
lm-quant-toolkit/.deps/hqq/hqq/core/utils.py
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Written by Dr. Hicham Badri @Mobius Labs GmbH - 2023
|
| 2 |
+
#####################################################
|
| 3 |
+
import torch
|
| 4 |
+
import gc
|
| 5 |
+
import numpy as np
|
| 6 |
+
from typing import Union
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
def cleanup() -> None:
|
| 10 |
+
try:
|
| 11 |
+
torch.cuda.empty_cache()
|
| 12 |
+
except Exception:
|
| 13 |
+
pass
|
| 14 |
+
gc.collect()
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
def is_divisible(val1: int, val2: int) -> bool:
|
| 18 |
+
return int(val2 * np.ceil(val1 / val2)) == val1
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def zero_pad_row(
|
| 22 |
+
tensor: torch.Tensor, num_rows: int, dtype: Union[torch.dtype, None] = None
|
| 23 |
+
) -> torch.Tensor:
|
| 24 |
+
out = torch.zeros(
|
| 25 |
+
[num_rows, tensor.shape[1]],
|
| 26 |
+
device=tensor.device,
|
| 27 |
+
dtype=tensor.dtype if (dtype is None) else dtype,
|
| 28 |
+
)
|
| 29 |
+
out[: len(tensor)] = tensor
|
| 30 |
+
|
| 31 |
+
return out
|
lm-quant-toolkit/.deps/hqq/hqq/engine/__init__.py
ADDED
|
File without changes
|
lm-quant-toolkit/.deps/hqq/hqq/engine/base.py
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Written by Dr. Hicham Badri @Mobius Labs GmbH - 2023
|
| 2 |
+
#####################################################
|
| 3 |
+
from abc import abstractmethod
|
| 4 |
+
from typing import Union
|
| 5 |
+
|
| 6 |
+
import torch
|
| 7 |
+
from torch import float16
|
| 8 |
+
|
| 9 |
+
from ..models.base import BaseHQQModel
|
| 10 |
+
|
| 11 |
+
# Wrapper that makes it easier to add quantization support to different engines (HF, VLLM, etc.)
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
class HQQWrapper:
|
| 15 |
+
@abstractmethod
|
| 16 |
+
def _get_arch_key_from_save_dir(cls, save_dir: str):
|
| 17 |
+
pass
|
| 18 |
+
|
| 19 |
+
@classmethod
|
| 20 |
+
def _get_hqq_class(cls, arg):
|
| 21 |
+
arch = arg if (isinstance(arg, str)) else arg.arch_key
|
| 22 |
+
return cls._HQQ_REGISTRY[arch]
|
| 23 |
+
|
| 24 |
+
@classmethod
|
| 25 |
+
def _validate_params(cls, params: dict):
|
| 26 |
+
pass
|
| 27 |
+
|
| 28 |
+
@classmethod
|
| 29 |
+
def _is_quantizable(cls, model):
|
| 30 |
+
return hasattr(model, "hqq_quantized")
|
| 31 |
+
|
| 32 |
+
@classmethod
|
| 33 |
+
def _make_quantizable(cls, model, quantized: bool):
|
| 34 |
+
model.hqq_quantized = quantized
|
| 35 |
+
model.base_class = cls._get_hqq_class(model)
|
| 36 |
+
|
| 37 |
+
@classmethod
|
| 38 |
+
def _check_arch_support(cls, arg):
|
| 39 |
+
arch = arg if (isinstance(arg, str)) else getattr(arg, "arch_key", None)
|
| 40 |
+
assert arch in cls._HQQ_REGISTRY, (
|
| 41 |
+
"Model architecture " + arch + " not supported yet."
|
| 42 |
+
)
|
| 43 |
+
|
| 44 |
+
@classmethod
|
| 45 |
+
def _check_if_already_quantized(cls, model):
|
| 46 |
+
assert not model.hqq_quantized, "Model already quantized"
|
| 47 |
+
|
| 48 |
+
@classmethod
|
| 49 |
+
def _check_if_not_quantized(cls, model):
|
| 50 |
+
assert model.hqq_quantized, "Model not quantized."
|
| 51 |
+
|
| 52 |
+
@classmethod
|
| 53 |
+
def _set_quantized(cls, model, quantized: bool):
|
| 54 |
+
model.hqq_quantized = quantized
|
| 55 |
+
|
| 56 |
+
#####################################################
|
| 57 |
+
@classmethod
|
| 58 |
+
def quantize_model_(
|
| 59 |
+
cls, model, quant_config, compute_dtype: torch.dtype = float16, device="cuda"
|
| 60 |
+
):
|
| 61 |
+
if cls._is_quantizable(model) is False:
|
| 62 |
+
cls._make_quantizable(model, quantized=False)
|
| 63 |
+
cls._check_arch_support(model)
|
| 64 |
+
cls._check_if_already_quantized(model)
|
| 65 |
+
cls._get_hqq_class(model).quantize_model(
|
| 66 |
+
model, quant_config=quant_config, compute_dtype=compute_dtype, device=device
|
| 67 |
+
)
|
| 68 |
+
cls._set_quantized(model, True)
|
| 69 |
+
|
| 70 |
+
@classmethod
|
| 71 |
+
def save_quantized_(cls, model, save_dir: str):
|
| 72 |
+
cls._check_if_not_quantized(model)
|
| 73 |
+
cls._get_hqq_class(model).save_quantized(model, save_dir=save_dir)
|
| 74 |
+
|
| 75 |
+
@classmethod
|
| 76 |
+
def from_quantized(
|
| 77 |
+
cls,
|
| 78 |
+
save_dir_or_hub,
|
| 79 |
+
compute_dtype: torch.dtype = float16,
|
| 80 |
+
device="cuda",
|
| 81 |
+
cache_dir: Union[str, None] = "",
|
| 82 |
+
adapter: str = None,
|
| 83 |
+
**kwargs,
|
| 84 |
+
):
|
| 85 |
+
# Both local and hub-support
|
| 86 |
+
save_dir = BaseHQQModel.try_snapshot_download(
|
| 87 |
+
save_dir_or_hub, cache_dir=cache_dir
|
| 88 |
+
)
|
| 89 |
+
arch_key = cls._get_arch_key_from_save_dir(save_dir)
|
| 90 |
+
cls._check_arch_support(arch_key)
|
| 91 |
+
|
| 92 |
+
model = cls._get_hqq_class(arch_key).from_quantized(
|
| 93 |
+
save_dir,
|
| 94 |
+
compute_dtype=compute_dtype,
|
| 95 |
+
device=device,
|
| 96 |
+
cache_dir=cache_dir,
|
| 97 |
+
adapter=adapter,
|
| 98 |
+
)
|
| 99 |
+
|
| 100 |
+
cls._make_quantizable(model, quantized=True)
|
| 101 |
+
return model
|
| 102 |
+
|
| 103 |
+
@classmethod
|
| 104 |
+
def get_linear_tags(cls, model):
|
| 105 |
+
return cls._get_hqq_class(model).get_linear_tags()
|
lm-quant-toolkit/.deps/hqq/hqq/engine/hf.py
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Written by Dr. Hicham Badri @Mobius Labs GmbH - 2023
|
| 2 |
+
#####################################################
|
| 3 |
+
|
| 4 |
+
import transformers
|
| 5 |
+
import torch
|
| 6 |
+
from .base import HQQWrapper
|
| 7 |
+
|
| 8 |
+
from ..models.hf.llama import LlamaHQQ
|
| 9 |
+
from hqq.models.hf.mixtral import MixtralHQQ
|
| 10 |
+
from hqq.models.hf.phi import PhiHQQ
|
| 11 |
+
from hqq.models.hf.mistral import MistralHQQ
|
| 12 |
+
|
| 13 |
+
_HQQ_REGISTRY = {}
|
| 14 |
+
_HQQ_REGISTRY["LlamaForCausalLM"] = LlamaHQQ
|
| 15 |
+
_HQQ_REGISTRY["MixtralForCausalLM"] = MixtralHQQ
|
| 16 |
+
_HQQ_REGISTRY["PhiForCausalLM"] = PhiHQQ
|
| 17 |
+
_HQQ_REGISTRY["MistralForCausalLM"] = MistralHQQ
|
| 18 |
+
|
| 19 |
+
# Alias
|
| 20 |
+
AutoTokenizer = transformers.AutoTokenizer
|
| 21 |
+
|
| 22 |
+
# Used to call super() on classmethods
|
| 23 |
+
_Parent = transformers.AutoModelForCausalLM
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
class HQQModelForCausalLM(_Parent, HQQWrapper):
|
| 27 |
+
_HQQ_REGISTRY = _HQQ_REGISTRY
|
| 28 |
+
|
| 29 |
+
def __init__(self, *args, **kwargs):
|
| 30 |
+
super().__init__(*args, **kwargs)
|
| 31 |
+
|
| 32 |
+
@classmethod
|
| 33 |
+
def _make_quantizable(cls, model, quantized: bool) -> None:
|
| 34 |
+
model.hqq_quantized = quantized
|
| 35 |
+
model.arch_key = model.config.architectures[0]
|
| 36 |
+
model.quantize_model = (
|
| 37 |
+
lambda quant_config,
|
| 38 |
+
compute_dtype=torch.float16,
|
| 39 |
+
device="cuda": cls.quantize_model_(
|
| 40 |
+
model=model,
|
| 41 |
+
quant_config=quant_config,
|
| 42 |
+
compute_dtype=compute_dtype,
|
| 43 |
+
device=device,
|
| 44 |
+
)
|
| 45 |
+
)
|
| 46 |
+
model.save_quantized = lambda save_dir: cls.save_quantized_(
|
| 47 |
+
model=model, save_dir=save_dir
|
| 48 |
+
)
|
| 49 |
+
model.cuda = lambda *args, **kwargs: model if (quantized) else model.cuda
|
| 50 |
+
model.to = lambda *args, **kwargs: model if (quantized) else model.to
|
| 51 |
+
model.float = lambda *args, **kwargs: model if (quantized) else model.float
|
| 52 |
+
model.half = lambda *args, **kwargs: model if (quantized) else model.half
|
| 53 |
+
model.base_class = cls._get_hqq_class(model)
|
| 54 |
+
|
| 55 |
+
# Force loading the model on CPU and unquantized
|
| 56 |
+
@classmethod
|
| 57 |
+
def _validate_params(cls, params: dict) -> None:
|
| 58 |
+
for p in ["load_in_4bit", "load_in_8bit"]: # ignore these
|
| 59 |
+
if p in params:
|
| 60 |
+
params[p] = False
|
| 61 |
+
params["device_map"] = None
|
| 62 |
+
|
| 63 |
+
@classmethod
|
| 64 |
+
def from_pretrained(cls, *args, **kwargs):
|
| 65 |
+
cls._validate_params(kwargs)
|
| 66 |
+
model = super(_Parent, cls).from_pretrained(*args, **kwargs)
|
| 67 |
+
cls._make_quantizable(model, quantized=False)
|
| 68 |
+
return model
|
| 69 |
+
|
| 70 |
+
@classmethod
|
| 71 |
+
def _get_arch_key_from_save_dir(cls, save_dir: str):
|
| 72 |
+
config = transformers.AutoConfig.from_pretrained(save_dir)
|
| 73 |
+
return config.architectures[0]
|
lm-quant-toolkit/.deps/hqq/hqq/engine/open_clip.py
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
|
| 3 |
+
import torch
|
| 4 |
+
|
| 5 |
+
from ..models.base import BaseHQQModel
|
| 6 |
+
from ..models.open_clip.vit_clip import ViTCLIPHQQ
|
| 7 |
+
from .base import HQQWrapper
|
| 8 |
+
|
| 9 |
+
_HQQ_REGISTRY = {}
|
| 10 |
+
_HQQ_REGISTRY["ViT-B-16"] = ViTCLIPHQQ
|
| 11 |
+
_HQQ_REGISTRY["ViT-B-16-plus-240"] = ViTCLIPHQQ
|
| 12 |
+
_HQQ_REGISTRY["ViT-B-16-plus"] = ViTCLIPHQQ
|
| 13 |
+
_HQQ_REGISTRY["ViT-B-16-quickgelu"] = ViTCLIPHQQ
|
| 14 |
+
_HQQ_REGISTRY["ViT-B-16-SigLIP-256"] = ViTCLIPHQQ
|
| 15 |
+
_HQQ_REGISTRY["ViT-B-16-SigLIP-384"] = ViTCLIPHQQ
|
| 16 |
+
_HQQ_REGISTRY["ViT-B-16-SigLIP-512"] = ViTCLIPHQQ
|
| 17 |
+
_HQQ_REGISTRY["ViT-B-16-SigLIP-i18n-256"] = ViTCLIPHQQ
|
| 18 |
+
_HQQ_REGISTRY["ViT-B-16-SigLIP"] = ViTCLIPHQQ
|
| 19 |
+
_HQQ_REGISTRY["ViT-B-32-256"] = ViTCLIPHQQ
|
| 20 |
+
_HQQ_REGISTRY["ViT-B-32"] = ViTCLIPHQQ
|
| 21 |
+
_HQQ_REGISTRY["ViT-B-32-plus-256"] = ViTCLIPHQQ
|
| 22 |
+
_HQQ_REGISTRY["ViT-B-32-quickgelu"] = ViTCLIPHQQ
|
| 23 |
+
_HQQ_REGISTRY["ViT-bigG-14-CLIPA-336"] = ViTCLIPHQQ
|
| 24 |
+
_HQQ_REGISTRY["ViT-bigG-14-CLIPA"] = ViTCLIPHQQ
|
| 25 |
+
_HQQ_REGISTRY["ViT-bigG-14"] = ViTCLIPHQQ
|
| 26 |
+
_HQQ_REGISTRY["ViT-e-14"] = ViTCLIPHQQ
|
| 27 |
+
_HQQ_REGISTRY["ViT-g-14"] = ViTCLIPHQQ
|
| 28 |
+
_HQQ_REGISTRY["ViT-H-14-378-quickgelu"] = ViTCLIPHQQ
|
| 29 |
+
_HQQ_REGISTRY["ViT-H-14-CLIPA-336"] = ViTCLIPHQQ
|
| 30 |
+
_HQQ_REGISTRY["ViT-H-14-CLIPA"] = ViTCLIPHQQ
|
| 31 |
+
_HQQ_REGISTRY["ViT-H-14"] = ViTCLIPHQQ
|
| 32 |
+
_HQQ_REGISTRY["ViT-H-14-quickgelu"] = ViTCLIPHQQ
|
| 33 |
+
_HQQ_REGISTRY["ViT-H-16"] = ViTCLIPHQQ
|
| 34 |
+
_HQQ_REGISTRY["ViT-L-14-280"] = ViTCLIPHQQ
|
| 35 |
+
_HQQ_REGISTRY["ViT-L-14-336"] = ViTCLIPHQQ
|
| 36 |
+
_HQQ_REGISTRY["ViT-L-14-CLIPA-336"] = ViTCLIPHQQ
|
| 37 |
+
_HQQ_REGISTRY["ViT-L-14-CLIPA"] = ViTCLIPHQQ
|
| 38 |
+
_HQQ_REGISTRY["ViT-L-14"] = ViTCLIPHQQ
|
| 39 |
+
_HQQ_REGISTRY["ViT-L-14-quickgelu"] = ViTCLIPHQQ
|
| 40 |
+
_HQQ_REGISTRY["ViT-L-16-320"] = ViTCLIPHQQ
|
| 41 |
+
_HQQ_REGISTRY["ViT-L-16"] = ViTCLIPHQQ
|
| 42 |
+
_HQQ_REGISTRY["ViT-L-16-SigLIP-256"] = ViTCLIPHQQ
|
| 43 |
+
_HQQ_REGISTRY["ViT-L-16-SigLIP-384"] = ViTCLIPHQQ
|
| 44 |
+
_HQQ_REGISTRY["ViT-M-16-alt"] = ViTCLIPHQQ
|
| 45 |
+
_HQQ_REGISTRY["ViT-M-16"] = ViTCLIPHQQ
|
| 46 |
+
_HQQ_REGISTRY["ViT-M-32-alt"] = ViTCLIPHQQ
|
| 47 |
+
_HQQ_REGISTRY["ViT-M-32"] = ViTCLIPHQQ
|
| 48 |
+
_HQQ_REGISTRY["ViT-S-16-alt"] = ViTCLIPHQQ
|
| 49 |
+
_HQQ_REGISTRY["ViT-S-16"] = ViTCLIPHQQ
|
| 50 |
+
_HQQ_REGISTRY["ViT-S-32-alt"] = ViTCLIPHQQ
|
| 51 |
+
_HQQ_REGISTRY["ViT-S-32"] = ViTCLIPHQQ
|
| 52 |
+
_HQQ_REGISTRY["ViT-SO400M-14-SigLIP-384"] = ViTCLIPHQQ
|
| 53 |
+
_HQQ_REGISTRY["ViT-SO400M-14-SigLIP"] = ViTCLIPHQQ
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
class HQQOpenCLIP(HQQWrapper):
|
| 57 |
+
_HQQ_REGISTRY = _HQQ_REGISTRY
|
| 58 |
+
|
| 59 |
+
def __init__(self, *args, **kwargs):
|
| 60 |
+
super().__init__(*args, **kwargs)
|
| 61 |
+
|
| 62 |
+
@classmethod
|
| 63 |
+
def _make_quantizable(cls, model, quantized: bool):
|
| 64 |
+
model.hqq_quantized = quantized
|
| 65 |
+
model.quantize_model = (
|
| 66 |
+
lambda quant_config,
|
| 67 |
+
compute_dtype=torch.float16,
|
| 68 |
+
device="cuda": cls.quantize_model_(
|
| 69 |
+
model=model,
|
| 70 |
+
quant_config=quant_config,
|
| 71 |
+
compute_dtype=compute_dtype,
|
| 72 |
+
device=device,
|
| 73 |
+
)
|
| 74 |
+
)
|
| 75 |
+
model.save_quantized = lambda save_dir: cls.save_quantized_(
|
| 76 |
+
model=model, save_dir=save_dir
|
| 77 |
+
)
|
| 78 |
+
# model.cuda = lambda *args, **kwargs: model if (quantized) else model.cuda
|
| 79 |
+
# model.to = lambda *args, **kwargs: model if (quantized) else model.to
|
| 80 |
+
# model.float = lambda *args, **kwargs: model if (quantized) else model.float
|
| 81 |
+
# model.half = lambda *args, **kwargs: model if (quantized) else model.half
|
| 82 |
+
model.base_class = ViTCLIPHQQ
|
| 83 |
+
|
| 84 |
+
@classmethod
|
| 85 |
+
def _validate_params(cls, params: dict):
|
| 86 |
+
pass
|
| 87 |
+
|
| 88 |
+
@classmethod
|
| 89 |
+
def create_model(cls, model_id, **kwargs):
|
| 90 |
+
cls._validate_params(kwargs)
|
| 91 |
+
# check if the first positional argument is an directory that exists
|
| 92 |
+
comps = model_id.split("/")
|
| 93 |
+
elems = comps[1].split("-")
|
| 94 |
+
model_name = "-".join(elems[1:4])
|
| 95 |
+
pretrained = "-".join(elems[4:])
|
| 96 |
+
model = cls._get_hqq_class(model_name).create_model(
|
| 97 |
+
model_name, model_name=model_name, pretrained=pretrained, **kwargs
|
| 98 |
+
)
|
| 99 |
+
cls._make_quantizable(model, quantized=False)
|
| 100 |
+
return model
|
| 101 |
+
|
| 102 |
+
@classmethod
|
| 103 |
+
def wrap_model(cls, model, model_name):
|
| 104 |
+
model.arch_key = model_name
|
| 105 |
+
cls._make_quantizable(model, quantized=False)
|
| 106 |
+
return model
|
| 107 |
+
|
| 108 |
+
@classmethod
|
| 109 |
+
def _get_arch_key_from_save_dir(cls, save_dir: str):
|
| 110 |
+
with open(BaseHQQModel.get_config_file(save_dir), "r") as file:
|
| 111 |
+
config = json.load(file)
|
| 112 |
+
return config["architecture"]
|
lm-quant-toolkit/.deps/hqq/hqq/engine/timm.py
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Written by Dr. Hicham Badri @Mobius Labs GmbH - 2023
|
| 2 |
+
#####################################################
|
| 3 |
+
import timm
|
| 4 |
+
import json
|
| 5 |
+
import torch
|
| 6 |
+
from .base import HQQWrapper
|
| 7 |
+
|
| 8 |
+
from ..models.base import BaseHQQModel
|
| 9 |
+
from ..models.timm.vit_clip import ViTCLIPHQQ
|
| 10 |
+
|
| 11 |
+
_HQQ_REGISTRY = {}
|
| 12 |
+
_HQQ_REGISTRY["vit_huge_patch14_clip_336"] = ViTCLIPHQQ
|
| 13 |
+
_HQQ_REGISTRY["vit_huge_patch14_clip_224"] = ViTCLIPHQQ
|
| 14 |
+
_HQQ_REGISTRY["vit_large_patch14_clip_224"] = ViTCLIPHQQ
|
| 15 |
+
_HQQ_REGISTRY["vit_large_patch14_clip_336"] = ViTCLIPHQQ
|
| 16 |
+
_HQQ_REGISTRY["vit_base_patch16_clip_384"] = ViTCLIPHQQ
|
| 17 |
+
_HQQ_REGISTRY["vit_base_patch32_clip_448"] = ViTCLIPHQQ
|
| 18 |
+
_HQQ_REGISTRY["vit_base_patch32_clip_384"] = ViTCLIPHQQ
|
| 19 |
+
_HQQ_REGISTRY["vit_base_patch16_clip_224"] = ViTCLIPHQQ
|
| 20 |
+
_HQQ_REGISTRY["vit_base_patch32_clip_224"] = ViTCLIPHQQ
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
class HQQtimm(HQQWrapper):
|
| 24 |
+
_HQQ_REGISTRY = _HQQ_REGISTRY
|
| 25 |
+
|
| 26 |
+
def __init__(self, *args, **kwargs):
|
| 27 |
+
super().__init__(*args, **kwargs)
|
| 28 |
+
|
| 29 |
+
@classmethod
|
| 30 |
+
def _make_quantizable(cls, model, quantized: bool):
|
| 31 |
+
model.hqq_quantized = quantized
|
| 32 |
+
model.arch_key = model.default_cfg["architecture"]
|
| 33 |
+
model.quantize_model = (
|
| 34 |
+
lambda quant_config,
|
| 35 |
+
compute_dtype=torch.float16,
|
| 36 |
+
device="cuda": cls.quantize_model_(
|
| 37 |
+
model=model,
|
| 38 |
+
quant_config=quant_config,
|
| 39 |
+
compute_dtype=compute_dtype,
|
| 40 |
+
device=device,
|
| 41 |
+
)
|
| 42 |
+
)
|
| 43 |
+
model.save_quantized = lambda save_dir: cls.save_quantized_(
|
| 44 |
+
model=model, save_dir=save_dir
|
| 45 |
+
)
|
| 46 |
+
model.cuda = lambda *args, **kwargs: model if (quantized) else model.cuda
|
| 47 |
+
model.to = lambda *args, **kwargs: model if (quantized) else model.to
|
| 48 |
+
model.float = lambda *args, **kwargs: model if (quantized) else model.float
|
| 49 |
+
model.half = lambda *args, **kwargs: model if (quantized) else model.half
|
| 50 |
+
model.base_class = cls._get_hqq_class(model)
|
| 51 |
+
|
| 52 |
+
@classmethod
|
| 53 |
+
def _validate_params(cls, params: dict):
|
| 54 |
+
pass
|
| 55 |
+
|
| 56 |
+
@classmethod
|
| 57 |
+
def create_model(cls, *args, **kwargs):
|
| 58 |
+
cls._validate_params(kwargs)
|
| 59 |
+
model = timm.create_model(*args, **kwargs)
|
| 60 |
+
cls._make_quantizable(model, quantized=False)
|
| 61 |
+
return model
|
| 62 |
+
|
| 63 |
+
@classmethod
|
| 64 |
+
def _get_arch_key_from_save_dir(cls, save_dir: str):
|
| 65 |
+
with open(BaseHQQModel.get_config_file(save_dir), "r") as file:
|
| 66 |
+
config = json.load(file)
|
| 67 |
+
return config["architecture"]
|
lm-quant-toolkit/.deps/hqq/hqq/engine/vllm.py
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Written by Dr. Hicham Badri @Mobius Labs GmbH - 2023
|
| 2 |
+
#####################################################
|
| 3 |
+
|
| 4 |
+
# Import VLLM architectures here with the dummy_load trick
|
| 5 |
+
import torch
|
| 6 |
+
from torch import float16
|
| 7 |
+
import transformers
|
| 8 |
+
from ..models.vllm.llama import LlamaForCausalLM, LlamaHQQ
|
| 9 |
+
from ..models.base import BaseHQQModel
|
| 10 |
+
from .base import HQQWrapper
|
| 11 |
+
from termcolor import colored
|
| 12 |
+
|
| 13 |
+
# Set them in the model registry
|
| 14 |
+
import vllm
|
| 15 |
+
|
| 16 |
+
vllm.model_executor.model_loader._MODEL_REGISTRY["LlamaForCausalLM"] = LlamaForCausalLM
|
| 17 |
+
|
| 18 |
+
_HQQ_REGISTRY = {}
|
| 19 |
+
_HQQ_REGISTRY["LlamaForCausalLM"] = LlamaHQQ
|
| 20 |
+
|
| 21 |
+
# VLLM requires model input as string, so this is the fallback for a dummy init used to load the model from quantized weights + load the tokenizer
|
| 22 |
+
_ARCH_TO_DEFAULT = {}
|
| 23 |
+
_ARCH_TO_DEFAULT["LlamaForCausalLM"] = "meta-llama/Llama-2-7b-chat-hf"
|
| 24 |
+
|
| 25 |
+
_Parent = vllm.entrypoints.llm.LLM
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
# Similar to the from vllm import LLM class, with some extra parameters and only loads a dummy model
|
| 29 |
+
class HQQLLM(_Parent, HQQWrapper):
|
| 30 |
+
_HQQ_REGISTRY = _HQQ_REGISTRY
|
| 31 |
+
INIT_CACHE_MEM = 1.0 # Default cache mem in GB. This is extra-memory, but you need it otherwise runtime is slower
|
| 32 |
+
|
| 33 |
+
def __init__(self, *args, **kwargs):
|
| 34 |
+
self._validate_params(kwargs)
|
| 35 |
+
|
| 36 |
+
if "force_skip" in kwargs:
|
| 37 |
+
force_skip = kwargs["force_skip"]
|
| 38 |
+
del kwargs["force_skip"]
|
| 39 |
+
else:
|
| 40 |
+
force_skip = False
|
| 41 |
+
|
| 42 |
+
# This will init a dummy model
|
| 43 |
+
super().__init__(*args, **kwargs)
|
| 44 |
+
|
| 45 |
+
cache_dir = kwargs["cache_dir"] if ("cache_dir" in kwargs) else ""
|
| 46 |
+
model_id = kwargs["model"]
|
| 47 |
+
|
| 48 |
+
# Here we load the model on CPU so that we can quantize it while avoiding extra GPU cost
|
| 49 |
+
self.arch_key = self.llm_engine.model_config.hf_config.architectures[0]
|
| 50 |
+
model_config = self.llm_engine.model_config.hf_config
|
| 51 |
+
workers = self.llm_engine.workers
|
| 52 |
+
|
| 53 |
+
if force_skip is False:
|
| 54 |
+
for i in range(len(workers)):
|
| 55 |
+
workers[i].model.__init__(model_config, dummy_load=False)
|
| 56 |
+
workers[i].model.load_weights(
|
| 57 |
+
model_name_or_path=model_id, cache_dir=cache_dir
|
| 58 |
+
)
|
| 59 |
+
|
| 60 |
+
self.hqq_quantized = False
|
| 61 |
+
|
| 62 |
+
def _validate_params(self, kwargs: dict):
|
| 63 |
+
if "gpu_memory_utilization" not in kwargs:
|
| 64 |
+
total_gpu_mem = torch.cuda.get_device_properties(0).total_memory / 1e9 # GB
|
| 65 |
+
kwargs["gpu_memory_utilization"] = HQQLLM.INIT_CACHE_MEM / total_gpu_mem
|
| 66 |
+
|
| 67 |
+
# In case the user wants to use the fp16 model and skip quantization
|
| 68 |
+
def cuda(self):
|
| 69 |
+
self._check_if_already_quantized(self)
|
| 70 |
+
workers = self.llm_engine.workers
|
| 71 |
+
for i in range(len(workers)):
|
| 72 |
+
workers[i].model = workers[i].model.half().cuda(i)
|
| 73 |
+
return self
|
| 74 |
+
|
| 75 |
+
def quantize_model(
|
| 76 |
+
self, quant_config: dict, compute_dtype: torch.dtype = float16, device="cuda"
|
| 77 |
+
):
|
| 78 |
+
return self.quantize_model_(
|
| 79 |
+
model=self,
|
| 80 |
+
quant_config=quant_config,
|
| 81 |
+
compute_dtype=compute_dtype,
|
| 82 |
+
device=device,
|
| 83 |
+
)
|
| 84 |
+
|
| 85 |
+
def save_quantized(self, save_dir: str):
|
| 86 |
+
return self.save_quantized_(model=self, save_dir=save_dir)
|
| 87 |
+
|
| 88 |
+
@classmethod
|
| 89 |
+
def _get_arch_key_from_save_dir(cls, save_dir: str):
|
| 90 |
+
config = transformers.AutoConfig.from_pretrained(save_dir)
|
| 91 |
+
return config.architectures[0]
|
| 92 |
+
|
| 93 |
+
# This requires custom loading because VLLM requires a str input model to initalize the workers
|
| 94 |
+
@classmethod
|
| 95 |
+
def from_quantized(
|
| 96 |
+
cls,
|
| 97 |
+
save_dir_or_hub: str,
|
| 98 |
+
compute_dtype: torch.dtype = float16,
|
| 99 |
+
cache_dir: str = "",
|
| 100 |
+
tensor_parallel_size: int = 1,
|
| 101 |
+
):
|
| 102 |
+
assert tensor_parallel_size == 1, "Only single GPU is supported."
|
| 103 |
+
# Both local and hub-support
|
| 104 |
+
save_dir = BaseHQQModel.try_snapshot_download(save_dir_or_hub)
|
| 105 |
+
arch_key = cls._get_arch_key_from_save_dir(save_dir)
|
| 106 |
+
cls._check_arch_support(arch_key)
|
| 107 |
+
|
| 108 |
+
# Trick to initialize the tokenizer and a dummy model inside a VLLM instance
|
| 109 |
+
instance = cls(
|
| 110 |
+
model=_ARCH_TO_DEFAULT[arch_key], force_skip=True, tensor_parallel_size=1
|
| 111 |
+
)
|
| 112 |
+
workers = instance.llm_engine.workers
|
| 113 |
+
for i in range(len(workers)):
|
| 114 |
+
workers[i].model = cls._get_hqq_class(
|
| 115 |
+
arch_key
|
| 116 |
+
).from_quantized_single_worker(
|
| 117 |
+
save_dir_or_hub=save_dir_or_hub,
|
| 118 |
+
compute_dtype=compute_dtype,
|
| 119 |
+
cache_dir=cache_dir,
|
| 120 |
+
device="cuda:" + str(i),
|
| 121 |
+
)
|
| 122 |
+
|
| 123 |
+
cls._make_quantizable(instance, quantized=True)
|
| 124 |
+
return instance
|
| 125 |
+
|
| 126 |
+
|
| 127 |
+
# From https://github.com/langchain-ai/langchain/blob/master/libs/langchain/langchain/llms/vllm.py
|
| 128 |
+
|
| 129 |
+
|
| 130 |
+
try:
|
| 131 |
+
from langchain.llms import VLLM as LangchainVLLMBase
|
| 132 |
+
from langchain_core.pydantic_v1 import root_validator
|
| 133 |
+
|
| 134 |
+
class LangchainVLLM(LangchainVLLMBase):
|
| 135 |
+
def set(self, model):
|
| 136 |
+
self.client = model
|
| 137 |
+
return self
|
| 138 |
+
|
| 139 |
+
@root_validator()
|
| 140 |
+
def validate_environment(cls, values: dict) -> dict:
|
| 141 |
+
return values
|
| 142 |
+
except Exception:
|
| 143 |
+
LangchainVLLM = None
|
| 144 |
+
print(
|
| 145 |
+
colored(
|
| 146 |
+
'Langchain not installed. You can install it via "pip install langchain"',
|
| 147 |
+
"cyan",
|
| 148 |
+
)
|
| 149 |
+
)
|
lm-quant-toolkit/.deps/hqq/hqq/kernels/__init__.py
ADDED
|
File without changes
|