rainstonee commited on
Commit
c5d3e8d
·
verified ·
1 Parent(s): d17aaea

Upload 33 files

Browse files
README.md CHANGED
@@ -1,3 +1,101 @@
1
- ---
2
- license: cc-by-4.0
3
- ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # CDKA: Component Designed Kronecker Adapters
2
+
3
+ Official PyTorch implementation of **Diving into Kronecker Adapters: Component Design Matters**.
4
+
5
+ CDKA is a parameter-efficient fine-tuning framework for large-scale models. It studies how the component design of Kronecker adapters affects adapter capacity and downstream performance. In particular, CDKA exposes the component dimensions and the number of Kronecker components as controllable hyperparameters:
6
+
7
+ $$
8
+ \Delta W = \sum_{i=1}^{r} B^{(i)} \otimes A^{(i)},
9
+ $$
10
+
11
+ where `r1`, `r2`, and `r` determine the shape and number of Kronecker components.
12
+
13
+
14
+ ## Installation
15
+
16
+ ### 1. Clone the repository
17
+
18
+ ```bash
19
+ git clone https://github.com/rainstonee/CDKA.git
20
+ cd CDKA
21
+ ```
22
+
23
+ ### 2. Create a Python environment
24
+
25
+ ```bash
26
+ conda create -n cdka python=3.10 -y
27
+ conda activate cdka
28
+ ```
29
+
30
+ ### 3. Install dependencies
31
+
32
+ ```bash
33
+ pip install --upgrade pip
34
+ pip install -r requirements.txt
35
+ ```
36
+
37
+ ### 4. Install the modified PEFT package
38
+
39
+ This repository includes a modified PEFT implementation in `peft.zip`. The custom PEFT version is required because CDKA extends the standard LoRA configuration with Kronecker-adapter-specific arguments such as `r1`, `r2`, and `r`.
40
+
41
+ ```bash
42
+ unzip peft.zip
43
+ pip install -e peft
44
+ ```
45
+
46
+ ## Training with CDKA
47
+
48
+ Run the example script:
49
+
50
+ ```bash
51
+ bash run.sh
52
+ ```
53
+
54
+ The default example runs the Llama-2-7B model on `meta_math` dataset with PEFT enabled and Kronecker adapter hyperparameters specified through Hydra overrides.
55
+
56
+ The script is equivalent to:
57
+
58
+ ```bash
59
+ CUDA_VISIBLE_DEVICES=0 python run_exp.py \
60
+ +model=llama \
61
+ +peft=all \
62
+ +init=default \
63
+ +dataset_name=meta_math \
64
+ +seed=333 \
65
+ ++peft.lora_r1=2 \
66
+ ++peft.lora_r2=2 \
67
+ ++peft.lora_r=8 \
68
+ ++peft.lora_alpha=64
69
+ ```
70
+
71
+ ### Recommended default component configuration
72
+
73
+ For most CDKA experiments, we recommend starting with the following default component configuration:
74
+
75
+ ```bash
76
+ ++peft.lora_r1=2 \
77
+ ++peft.lora_r2=8 \
78
+ ++peft.lora_r=4 \
79
+ ++peft.lora_alpha=64
80
+ ```
81
+
82
+ This setting follows the main component-design principle of CDKA: use a small `r1`, a large `r2`, and a moderate number of Kronecker components `r`. In practice, this provides a balanced default configuration before task-specific tuning.
83
+
84
+
85
+ ## Citation
86
+
87
+ If you find this repository useful, please cite:
88
+
89
+ ```bibtex
90
+ @article{bai2026diving,
91
+ title={Diving into Kronecker Adapters: Component Design Matters},
92
+ author={Bai, Jiayu and Yu, Danchen and Liao, Zhenyu and Hou, TianQi and Zhou, Feng and Qiu, Robert C and Ling, Zenan},
93
+ journal={arXiv preprint arXiv:2602.01267},
94
+ year={2026}
95
+ }
96
+ ```
97
+
98
+ ## Acknowledgement
99
+
100
+ This codebase is built on [LoRA-GA](https://github.com/Outsider565/LoRA-GA). We thank the authors of LoRA-GA for releasing their implementation.
101
+
conf/config.yaml ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ defaults:
2
+ - _self_
3
+ # - model: llama
4
+ dry_run: False
5
+ wandb:
6
+ project: "glue_new"
7
+ name: null
8
+ hydra:
9
+ sweeper:
10
+ params:
11
+ ++dataset_name: mnli
12
+ ++seed: 0
conf/init/default.yaml ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ mode: simple
2
+ lora_A: kaiming
3
+ lora_B: zeros
4
+ # lora_A: zeros
5
+ # lora_B: kaiming
6
+ # This is the default way to initialize the model parameters.
7
+ dtype: fp32
conf/init/gradient.yaml ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ mode: gradient
2
+ bsz: 1
3
+ iters: 8
4
+ direction: ArBr
5
+ max_length: 1024
6
+ dtype: fp32
7
+ scale: stable
8
+ stable_gamma: 16
conf/init/kaiming.yaml ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ mode: simple
2
+ lora_A: kaiming
3
+ lora_B: kaiming
4
+ dtype: fp32
5
+ stable_gamma: 16
conf/init/qr.yaml ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ mode: qr
2
+ dtype: fp32
conf/init/svd.yaml ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ mode: svd
2
+ weight: default
3
+ dtype: fp32
4
+ stable_gamma: 16
5
+ scale: default
conf/model/llama.yaml ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: llama/llama-2-7b-hf
2
+ type: CausalLM
3
+ epochs: 1
4
+ per_device_batch_size: 1
5
+ real_batch_size: 32
6
+ bf16: True
7
+ eval_epochs: 1
8
+ early_stopping_patience: 3
9
+ max_length: 1024
10
+ logging_steps: 1
11
+ learning_rate: 2e-4
conf/model/llama3.yaml ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: llama/llama-3.1-8b-hf
2
+ type: CausalLM
3
+ epochs: 1
4
+ per_device_batch_size: 1
5
+ real_batch_size: 64
6
+ bf16: True
7
+ eval_epochs: 1
8
+ early_stopping_patience: 3
9
+ max_length: 1024
10
+ logging_steps: 1
11
+ learning_rate: 2e-4
conf/model/t5base.yaml ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: t5-base
2
+ type: ConditionalGeneration
3
+ epochs: 1
4
+ per_device_batch_size: 32
5
+ real_batch_size: 32
6
+ bf16: False
7
+ eval_epochs: 1
8
+ early_stopping_patience: 5
9
+ max_length: 128
10
+ logging_steps: 1
11
+ learning_rate: 2e-3
conf/model/t5large.yaml ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: t5-large
2
+ type: ConditionalGeneration
3
+ epochs: 1
4
+ per_device_batch_size: 32
5
+ real_batch_size: 1
6
+ bf16: True
7
+ eval_epochs: 0.1
8
+ early_stopping_patience: 3
9
+ max_length: 128
10
+ logging_steps: 1
11
+ learning_rate: 5e-5
conf/model/t5small.yaml ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: t5-small
2
+ type: ConditionalGeneration
3
+ epochs: 1
4
+ per_device_batch_size: 32
5
+ real_batch_size: 32
6
+ bf16: True
7
+ eval_epochs: 0.1
8
+ early_stopping_patience: 3
9
+ max_length: 128
10
+ logging_steps: 1
11
+ learning_rate: 5e-5
conf/model/t5xl.yaml ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: google/flan-t5-xl
2
+ type: ConditionalGeneration
3
+ epochs: 5
4
+ per_device_batch_size: 1
5
+ real_batch_size: 32
6
+ bf16: True
7
+ eval_epochs: 1
8
+ early_stopping_patience: 3
9
+ max_length: 512
10
+ logging_steps: 1
11
+ learning_rate: 5e-5
conf/model/tinyllama.yaml ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: TinyLlama/TinyLlama-1.1B-intermediate-step-1431k-3T
2
+ type: CausalLM
3
+ epochs: 1
4
+ per_device_batch_size: 1
5
+ real_batch_size: 32
6
+ bf16: True
7
+ eval_epochs: 1
8
+ early_stopping_patience: 3
9
+ max_length: 512
10
+ logging_steps: 1
11
+ learning_rate: 1e-5
conf/peft/adalora.yaml ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ use_peft: true
2
+ lora_r: null
3
+ lora_relative_r: null
4
+ lora_target_modules: all
5
+ train_embeddings: false
6
+ use_rslora: false
7
+ use_loraplus: false
8
+ loraplus_lr_ratio: 16
9
+ lora_alpha: 16
10
+ adalora: true
conf/peft/all.yaml ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ use_peft: true
2
+ lora_r1: null
3
+ lora_r2: null
4
+ lora_r: null
5
+ lora_relative_r: null
6
+ lora_target_modules: all
7
+ train_embeddings: false
8
+ use_rslora: True
9
+ use_loraplus: false
10
+ loraplus_lr_ratio: 16
11
+ lora_alpha: 64
conf/peft/all_w_emb.yaml ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ use_peft: true
2
+ lora_r: null
3
+ lora_relative_r: null
4
+ lora_target_modules: all
5
+ train_embeddings: true
6
+ use_rslora: false
7
+ use_loraplus: false
8
+ loraplus_lr_ratio: 16
9
+ lora_alpha: 16
conf/peft/att.yaml ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ use_peft: true
2
+ lora_r1: null
3
+ lora_r2: null
4
+ lora_r: null
5
+ lora_relative_r: null
6
+ lora_target_modules: [q_proj,k_proj,v_proj,o_proj]
7
+ train_embeddings: false
8
+ use_rslora: True
9
+ use_loraplus: false
10
+ loraplus_lr_ratio: 16
11
+ lora_alpha: 64
conf/peft/dora.yaml ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ use_peft: true
2
+ lora_r: null
3
+ lora_relative_r: null
4
+ lora_target_modules: all
5
+ train_embeddings: false
6
+ use_rslora: false
7
+ use_loraplus: false
8
+ loraplus_lr_ratio: 16
9
+ lora_alpha: 16
10
+ dora: true
conf/peft/full_ft.yaml ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ use_peft: false
2
+ lora_r: null
3
+ lora_relative_r: null
4
+ lora_target_modules: null
5
+ train_embeddings: false
6
+ use_rslora: false
7
+ use_loraplus: false
8
+ loraplus_lr_ratio: 16
conf/peft/qv.yaml ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ use_peft: true
2
+ lora_r: null
3
+ lora_relative_r: null
4
+ lora_target_modules: [q,v]
5
+ train_embeddings: false
6
+ use_rslora: false
7
+ use_loraplus: false
8
+ loraplus_lr_ratio: 16
9
+ lora_alpha: 16
conf/peft/qv_w_emb.yaml ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ use_peft: true
2
+ lora_r: null
3
+ lora_relative_r: null
4
+ lora_target_modules: [q,v]
5
+ train_embeddings: true
6
+ use_rslora: false
7
+ use_loraplus: false
8
+ loraplus_lr_ratio: 16
9
+ lora_alpha: 16
data.py ADDED
@@ -0,0 +1,488 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from datasets import load_dataset, Dataset
2
+ import typing as tp
3
+ import functools
4
+ import os
5
+ import pickle
6
+ import logging
7
+ import datasets
8
+
9
+ log = logging.getLogger(__name__)
10
+
11
+ def cache_to_disk(root_datadir):
12
+ def decorator_cache(func):
13
+ @functools.wraps(func)
14
+ def wrapper_cache(*args, **kwargs):
15
+ if not os.path.exists(root_datadir):
16
+ os.makedirs(root_datadir)
17
+
18
+ func_name = func.__name__.replace("/", "")
19
+ cache_file = os.path.join(root_datadir, f"{func_name}.pkl")
20
+ if os.path.exists(cache_file):
21
+ with open(cache_file, "rb") as f:
22
+ log.info(f"Loading cached data for {func.__name__}")
23
+ return pickle.load(f)
24
+
25
+ result = func(*args, **kwargs)
26
+ with open(cache_file, "wb") as f:
27
+ pickle.dump(result, f)
28
+ log.info(f"Cached data for {func.__name__}")
29
+ return result
30
+
31
+ return wrapper_cache
32
+
33
+ return decorator_cache
34
+
35
+ @cache_to_disk("data_cache")
36
+ def load_emo():
37
+ dataset = load_dataset("emo")
38
+ label_map = {0: "others", 1: "happy", 2: "sad", 3: "angry"}
39
+ instruction = "classify the emotion of the text: "
40
+ dataset = dataset.map(
41
+ lambda e: {
42
+ "x": f'{instruction}{e["text"]}\nresult: ',
43
+ "y": label_map[e["label"]],
44
+ }
45
+ )
46
+ train_set = dataset["train"]
47
+ test_set = dataset["test"]
48
+ return train_set, test_set, test_set
49
+
50
+ @cache_to_disk("data_cache")
51
+ def load_sst2():
52
+ dataset = load_dataset("glue", "sst2")
53
+ instruction = "classify the sentiment of the text: "
54
+ label_map = {0: "negative", 1: "positive", -1: "other"}
55
+ dataset = dataset.map(
56
+ lambda e: {
57
+ "x": f'{instruction}{e["sentence"]}\nresult: ',
58
+ "y": label_map[e["label"]],
59
+ }
60
+ )
61
+ train_set = dataset["train"]
62
+ validation_set = dataset["validation"]
63
+ return train_set, validation_set, validation_set
64
+
65
+ @cache_to_disk("data_cache")
66
+ def load_cola():
67
+ dataset = load_dataset("glue", "cola")
68
+ instruction = "classify the grammaticality of the text: "
69
+ label_map = {0: "unacceptable", 1: "acceptable", -1: "other"}
70
+ dataset = dataset.map(
71
+ lambda e: {
72
+ "x": f'{instruction}{e["sentence"]}\nresult: ',
73
+ "y": label_map[e["label"]],
74
+ }
75
+ )
76
+ train_set = dataset["train"]
77
+ validation_set = dataset["validation"]
78
+ return train_set, validation_set, validation_set
79
+
80
+ @cache_to_disk("data_cache")
81
+ def load_qqp():
82
+ dataset = load_dataset("glue", "qqp")
83
+ instruction = "classify the semantic similarity of the text: "
84
+ label_map = {0: "different", 1: "duplicate", -1: "other"}
85
+ dataset = dataset.map(
86
+ lambda e: {
87
+ "x": f'{instruction}{e["question1"]}\n{e["question2"]}\nresult: ',
88
+ "y": label_map[e["label"]],
89
+ }
90
+ )
91
+ train_set = dataset["train"]
92
+ validation_set = dataset["validation"]
93
+ return train_set, validation_set, validation_set
94
+
95
+ @cache_to_disk("data_cache")
96
+ def load_mrpc():
97
+ dataset = load_dataset("glue", "mrpc")
98
+ instruction = "classify the semantic similarity of the text: "
99
+ label_map = {0: "different", 1: "equivalent", -1: "other"}
100
+ dataset = dataset.map(
101
+ lambda e: {
102
+ "x": f'{instruction}{e["sentence1"]}\n{e["sentence2"]}\nresult: ',
103
+ "y": label_map[e["label"]],
104
+ }
105
+ )
106
+ train_set = dataset["train"]
107
+ validation_set = dataset["validation"]
108
+ return train_set, validation_set, validation_set
109
+
110
+ @cache_to_disk("data_cache")
111
+ def load_mnli():
112
+ dataset = load_dataset("glue", "mnli",download_mode="force_redownload")
113
+ instruction = "classify the semantic similarity of the text: "
114
+ label_map = {0: "entailment", 1: "neutral", 2: "contradiction", -1: "other"}
115
+ dataset = dataset.map(
116
+ lambda e: {
117
+ "x": f'{instruction}{e["premise"]}\n{e["hypothesis"]}\nresult: ',
118
+ "y": label_map[e["label"]],
119
+ }
120
+ )
121
+ train_set = dataset["train"]
122
+ validation_set = dataset["validation_matched"]
123
+ return train_set, validation_set, validation_set
124
+
125
+ @cache_to_disk("data_cache")
126
+ def load_squad():
127
+ dataset = load_dataset("rajpurkar/squad")
128
+ instruction = "answer the question: "
129
+ dataset = dataset.map(
130
+ lambda e: {
131
+ "x": f'{instruction}{e["question"]}\ncontext: {e["context"]}\nresult: ',
132
+ "y": ", ".join(e["answers"]["text"]),
133
+ }
134
+ )
135
+ train_set = dataset["train"]
136
+ validation_set = dataset["validation"]
137
+ return train_set, validation_set, validation_set
138
+
139
+ @cache_to_disk("data_cache")
140
+ def load_qnli():
141
+ dataset = load_dataset("glue", "qnli")
142
+ instruction = "classify the semantic similarity of the question and the sentence: "
143
+ label_map = {0: "entailment", 1: "not_entailment", -1: "other"}
144
+ dataset = dataset.map(
145
+ lambda e: {
146
+ "x": f'{instruction}{e["question"]}\n{e["sentence"]}\nresult: ',
147
+ "y": label_map[e["label"]],
148
+ }
149
+ )
150
+ train_set = dataset["train"]
151
+ validation_set = dataset["validation"]
152
+ test_set = dataset["test"]
153
+ return train_set, validation_set, test_set
154
+
155
+
156
+ template_with_input = '''### Instruction:
157
+ {instruction}
158
+
159
+ ### Input:
160
+ {input}
161
+
162
+ ### Response:
163
+ '''
164
+
165
+ template_wo_input = '''Below is an instruction that describes a task. Write a response that appropriately completes the request.
166
+
167
+ ### Instruction:
168
+ {instruction}
169
+
170
+ ### Response:
171
+ '''
172
+
173
+ @cache_to_disk("data_cache")
174
+ def load_alpaca():
175
+ dataset = load_dataset("tatsu-lab/alpaca")
176
+ def alpaca_preprocess(instruction, input, output):
177
+ if input == "":
178
+ x = template_wo_input.format(instruction=instruction)
179
+ else:
180
+ x = template_with_input.format(instruction=instruction, input=input)
181
+ return {"x": x, "y": output}
182
+ dataset = dataset.map(
183
+ lambda e: alpaca_preprocess(e["instruction"], e["input"], e["output"])
184
+ )
185
+ # we sample 10% of the training set as validation set
186
+ train_set = dataset["train"].train_test_split(test_size=0.1)['train']
187
+ validation_set = dataset["train"].train_test_split(test_size=0.1)['test']
188
+ return train_set, validation_set, validation_set
189
+
190
+ @cache_to_disk("data_cache")
191
+ def load_gsm8k():
192
+ dataset = load_dataset("gsm8k", "main")
193
+ #x = "Q: " + x[0] + "\n" + "A:"
194
+ dataset = dataset.map(
195
+ lambda e: {
196
+ "x": f'Q: {e["question"]}\nA: ',
197
+ "y": e["answer"],
198
+ }
199
+ )
200
+ train_set = dataset["train"]
201
+ validation_set = dataset["test"]
202
+ return train_set, validation_set, validation_set
203
+
204
+ @cache_to_disk("data_cache")
205
+ def load_alpaca_gpt4():
206
+ dataset = load_dataset("tatsu-lab/alpaca")
207
+ def alpaca_preprocess(instruction, input, output):
208
+ if input == "":
209
+ x = template_wo_input.format(instruction=instruction)
210
+ else:
211
+ x = template_with_input.format(instruction=instruction, input=input)
212
+ return {"x": x, "y": output}
213
+ dataset = dataset.map(
214
+ lambda e: alpaca_preprocess(e["instruction"], e["input"], e["output"])
215
+ )
216
+ # we sample 10% of the training set as validation set
217
+ train_set = dataset["train"].train_test_split(test_size=0.1)['train']
218
+ validation_set = dataset["train"].train_test_split(test_size=0.1)['test']
219
+ return train_set, validation_set, validation_set
220
+
221
+ @cache_to_disk("data_cache")
222
+ def load_flan():
223
+ dataset = load_dataset("Muennighoff/flan", split='train', streaming=True)
224
+ def preprocess(data):
225
+ return {
226
+ "x": template_wo_input.format(instruction=data['inputs']),
227
+ "y": data['targets'],
228
+ }
229
+ train_samples = []
230
+ eval_samples = []
231
+ count = 0
232
+ dataset.shuffle(buffer_size=5000, seed=42)
233
+ from tqdm import tqdm
234
+ for sample in tqdm(dataset, total=110000):
235
+ processed_sample = preprocess(sample)
236
+ if count < 100000: # First 100,000 samples for training
237
+ train_samples.append(processed_sample)
238
+ elif 100000 <= count < 110000: # Next 10,000 samples for evaluation
239
+ eval_samples.append(processed_sample)
240
+ elif count >= 110000: # Stop processing after collecting enough samples
241
+ break
242
+ count += 1
243
+ # convert to hf dataset
244
+ train_set = Dataset.from_list(train_samples)
245
+ eval_set = Dataset.from_list(eval_samples)
246
+ return train_set, eval_set, eval_set
247
+
248
+ @cache_to_disk("data_cache")
249
+ def load_meta_math(max_tokens=512):
250
+ dataset = load_dataset("meta-math/MetaMathQA", split='train')
251
+ from transformers import AutoTokenizer
252
+ tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-2-7b-hf")
253
+ def preprocess(data):
254
+ return {
255
+ "x": f'Q: {data["query"]}\nA: ',
256
+ "y": data["response"].split("\nThe answer is:")[0]
257
+ }
258
+ train_samples = []
259
+ eval_samples = []
260
+ count = 0
261
+ dataset.shuffle(seed=42)
262
+ from tqdm import tqdm
263
+ bar = tqdm(dataset, total=110000)
264
+ total = 0
265
+ ok = 0
266
+ for sample in dataset:
267
+ total += 1
268
+ temp = preprocess(sample)
269
+ if len(tokenizer(temp['x']+' '+temp['y'])['input_ids']) >= max_tokens or "GSM" not in sample["type"]:
270
+ continue
271
+ bar.update(1)
272
+ bar.set_description(f"ok: {ok}/{total}")
273
+ ok += 1
274
+ processed_sample = preprocess(sample)
275
+ if count < 100000: # First 100,000 samples for training
276
+ train_samples.append(processed_sample)
277
+ elif 100000 <= count < 110000: # Next 10,000 samples for evaluation
278
+ eval_samples.append(processed_sample)
279
+ elif count >= 110000: # Stop processing after collecting enough samples
280
+ break
281
+ count += 1
282
+ # convert to hf dataset
283
+ train_set = Dataset.from_list(train_samples)
284
+ eval_set = Dataset.from_list(eval_samples)
285
+ return train_set, eval_set, eval_set
286
+
287
+ @cache_to_disk("data_cache")
288
+ def load_flan_v2(max_tokens=512):
289
+ dataset = load_dataset("SirNeural/flan_v2", split='train', streaming=True)
290
+ from transformers import AutoTokenizer
291
+ tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-2-7b-hf")
292
+ def preprocess(data):
293
+ return {
294
+ "x": data['inputs'],
295
+ "y": data['targets'],
296
+ }
297
+ train_samples = []
298
+ eval_samples = []
299
+ count = 0
300
+ dataset.shuffle(buffer_size=5000, seed=42)
301
+ from tqdm import tqdm
302
+ bar = tqdm(dataset, total=110000)
303
+ total = 0
304
+ ok = 0
305
+ for sample in dataset:
306
+ total += 1
307
+ temp = preprocess(sample)
308
+ if len(tokenizer(temp['x']+' '+temp['y'])['input_ids']) >= max_tokens:
309
+ continue
310
+ bar.update(1)
311
+ bar.set_description(f"ok: {ok}/{total}")
312
+ ok += 1
313
+ processed_sample = preprocess(sample)
314
+ if count < 100000: # First 100,000 samples for training
315
+ train_samples.append(processed_sample)
316
+ elif 100000 <= count < 110000: # Next 10,000 samples for evaluation
317
+ eval_samples.append(processed_sample)
318
+ elif count >= 110000: # Stop processing after collecting enough samples
319
+ break
320
+ count += 1
321
+ # convert to hf dataset
322
+ train_set = Dataset.from_list(train_samples)
323
+ eval_set = Dataset.from_list(eval_samples)
324
+ return train_set, eval_set, eval_set
325
+
326
+ @cache_to_disk("data_cache")
327
+ def load_codefeedback(max_tokens=1024):
328
+ dataset = load_dataset("m-a-p/CodeFeedback-Filtered-Instruction", split='train')
329
+ from transformers import AutoTokenizer
330
+ tokenizer = AutoTokenizer.from_pretrained("llama/llama-2-7b-hf")
331
+ def preprocess(data):
332
+ y = data['answer']
333
+ y = "```".join(y.split("```")[:2]) + "```" # only keep the first code block
334
+ return {
335
+ "x": template_wo_input.format(
336
+ instruction=data['query']
337
+ ),
338
+ "y": y,
339
+ }
340
+ train_samples = []
341
+ eval_samples = []
342
+ count = 0
343
+ dataset.shuffle(seed=42)
344
+ from tqdm import tqdm
345
+ bar = tqdm(dataset, total=110000)
346
+ total = 0
347
+ ok = 0
348
+ for sample in dataset:
349
+ total += 1
350
+ temp = preprocess(sample)
351
+ if "```" not in sample['answer']:
352
+ continue
353
+ if len(tokenizer(temp['x']+' '+temp['y'])['input_ids']) >= max_tokens:
354
+ continue
355
+ bar.update(1)
356
+ bar.set_description(f"ok: {ok}/{total}")
357
+ ok += 1
358
+ processed_sample = preprocess(sample)
359
+ if count < 100000:
360
+ train_samples.append(processed_sample)
361
+ elif 100000 <= count < 110000:
362
+ eval_samples.append(processed_sample)
363
+ elif count >= 110000: # Stop processing after collecting enough samples
364
+ break
365
+ count += 1
366
+ # convert to hf dataset
367
+ train_set = Dataset.from_list(train_samples)
368
+ eval_set = Dataset.from_list(eval_samples)
369
+ return train_set, eval_set, eval_set
370
+
371
+ @cache_to_disk("data_cache")
372
+ def load_wizardlm(max_tokens=1024):
373
+ dataset = load_dataset("silk-road/Wizard-LM-Chinese-instruct-evol", split='train')
374
+ from transformers import AutoTokenizer
375
+ tokenizer = AutoTokenizer.from_pretrained("llama/llama-2-7b-hf")
376
+ def preprocess(data):
377
+ y = data['output']
378
+ return {
379
+ "x": template_wo_input.format(
380
+ instruction=data['instruction']
381
+ ),
382
+ "y": y,
383
+ }
384
+ train_samples = []
385
+ eval_samples = []
386
+ count = 0
387
+ dataset.shuffle(seed=42)
388
+ from tqdm import tqdm
389
+ bar = tqdm(dataset, total=70000)
390
+ total = 0
391
+ ok = 0
392
+ for sample in dataset:
393
+ total += 1
394
+ temp = preprocess(sample)
395
+ if "sorry" in temp['y'].lower() or "as an ai" in temp['y'].lower():
396
+ continue
397
+ if len(tokenizer(temp['x']+' '+temp['y'])['input_ids']) >= max_tokens:
398
+ continue
399
+ bar.update(1)
400
+ bar.set_description(f"ok: {ok}/{total}")
401
+ ok += 1
402
+ processed_sample = temp
403
+ if count < 52000:
404
+ train_samples.append(processed_sample)
405
+ elif 52000 <= count < 70000:
406
+ eval_samples.append(processed_sample)
407
+ elif count >= 70000: # Stop processing after collecting enough samples
408
+ break
409
+ count += 1
410
+ # convert to hf dataset
411
+ train_set = Dataset.from_list(train_samples)
412
+ eval_set = Dataset.from_list(eval_samples)
413
+ return train_set, eval_set, eval_set
414
+
415
+
416
+ @cache_to_disk("data_cache")
417
+ def load_common(max_tokens=1024):
418
+ # dataset = load_dataset("zwhe99/commonsense_170k", split='train')
419
+ dataset = load_dataset("json", data_files="commonsense_170k.json")['train']
420
+ from transformers import AutoTokenizer
421
+ tokenizer = AutoTokenizer.from_pretrained("llama/llama-2-7b-hf")
422
+ def preprocess(data):
423
+ y = data['output']
424
+ return {
425
+ "x": template_wo_input.format(
426
+ instruction=data['instruction']
427
+ ),
428
+ "y": y,
429
+ }
430
+ i = 0
431
+ train_samples = []
432
+ eval_samples = []
433
+ for sample in dataset:
434
+ i += 1
435
+ temp = preprocess(sample)
436
+ # print(temp)
437
+ if len(tokenizer(temp['x']+' '+temp['y'])['input_ids']) >= max_tokens:
438
+ continue
439
+ processed_sample = temp
440
+ train_samples.append(processed_sample)
441
+ if i == 1:
442
+ eval_samples.append(processed_sample)
443
+ # convert to hf dataset
444
+ train_set = Dataset.from_list(train_samples)
445
+ eval_set = Dataset.from_list(eval_samples)
446
+ return train_set, eval_set, eval_set
447
+
448
+ DATASET_MAP = {
449
+ "sst2": load_sst2,
450
+ "cola": load_cola,
451
+ "qqp": load_qqp,
452
+ "mrpc": load_mrpc,
453
+ "mnli": load_mnli,
454
+ "emo": load_emo,
455
+ "squad": load_squad,
456
+ "alpaca": load_alpaca,
457
+ "qnli": load_qnli,
458
+ "gsm8k": load_gsm8k,
459
+ "alpaca_gpt4": load_alpaca_gpt4,
460
+ "flan": load_flan,
461
+ "flan_v2": load_flan_v2,
462
+ "meta_math": load_meta_math,
463
+ "codefeedback": load_codefeedback,
464
+ "wizard_lm": load_wizardlm,
465
+ "common": load_common,
466
+ }
467
+
468
+
469
+ if __name__ == "__main__":
470
+ # for dataset in [load_emo, load_sst2, load_cola, load_qqp, load_mrpc, load_mnli]:
471
+ # train_set, val_set, test_set = dataset()
472
+ # print(train_set[0])
473
+ # print(val_set[0])
474
+ # print(test_set[0])
475
+ # print()
476
+ # print(load_alpaca())
477
+ # for name, dataset in DATASET_MAP.items():
478
+ # train_set, val_set, test_set = dataset()
479
+ # print(name)
480
+ # print(train_set[0])
481
+ # print(val_set[0])
482
+ # print(test_set[0])
483
+ # print()
484
+ x, r, _ = load_common()
485
+ print(x[0]['x'])
486
+ print(x[0]['y'])
487
+ print(len(x))
488
+ print(len(r))
eval_gsm8k.py ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from data import load_gsm8k
2
+ from utils import model_inference, initialize_text_to_text_model
3
+ from fire import Fire
4
+ import re
5
+ import os
6
+ from tqdm import tqdm
7
+ from peft import PeftModel
8
+
9
+ def extract_num(text):
10
+ # Regex pattern to find the number following '####'
11
+ pattern = r'####\s*(\d+)'
12
+ # Using re.search to find the first match
13
+ match = re.search(pattern, text)
14
+ if match:
15
+ result = match.group(1)
16
+ print(text)
17
+ else:
18
+ print(text)
19
+ result = ""
20
+ try:
21
+ return int(result.replace(",", ""))
22
+ except:
23
+ print(f"'{result}' can't be converted")
24
+ return 0
25
+
26
+ def main(model_name = "llama/llama-2-7b-hf"):
27
+ _, _, test_set = load_gsm8k()
28
+ model_type = "CausalLM"
29
+ model, tokenizer = initialize_text_to_text_model(
30
+ model_name, model_type, True, flash_attention=True
31
+ )
32
+ model = PeftModel.from_pretrained(model, "checkpoint_dir")
33
+ model.generation_config.pad_token_id = tokenizer.pad_token_id
34
+ all = 0
35
+ correct = 0
36
+ t = tqdm(test_set)
37
+ for example in t:
38
+ # print(example['x'])
39
+ pred_text = model_inference(model, tokenizer, example['x'], model_type, max_target_length=512)
40
+ gt = extract_num(example["y"])
41
+ pred = extract_num(pred_text)
42
+ correct += int(gt == pred)
43
+ all += 1
44
+ t.set_description(f"Accuracy: {correct / all * 100:02f}%")
45
+
46
+ print("Acc:", correct / all)
47
+ # append to gsm8k_results.txt (create if not exists)
48
+ if not os.path.exists("gsm8k_results.txt"):
49
+ with open("gsm8k_results.txt", "w") as f:
50
+ f.write("Model Acc\n")
51
+ with open("gsm8k_results.txt", "a") as f:
52
+ f.write(f"{model_name} {correct / all}\n")
53
+
54
+ if __name__ == "__main__":
55
+ Fire(main)
56
+
57
+
58
+
eval_humaneval.py ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from human_eval.data import write_jsonl, read_problems
2
+ from fire import Fire
3
+ from tqdm import trange, tqdm
4
+ from utils import initialize_text_to_text_model, model_inference
5
+ import re
6
+ import os
7
+ from human_eval.evaluation import evaluate_functional_correctness
8
+ from peft import PeftModel
9
+
10
+ ALPACA_PREFIX_TEMPLATE_MD = """Below is an instruction that describes a task.\n Write a response that appropriately completes the request.
11
+
12
+ ### Instruction:
13
+ Complete the following Python code:
14
+ Notes: respond with the entire complete function definition
15
+ do not add any comments, be as concise in your code as possible
16
+ use only built-in libraries, assume no additional imports other than those provided (if any)
17
+ use ` ` (4 spaces) for each level of indentation
18
+
19
+ code:
20
+ ```python
21
+ {PROMPT}
22
+ ```
23
+
24
+ ### Response:
25
+ ```python
26
+ """
27
+
28
+ def post_process(text):
29
+ text = text.replace("```", "")
30
+ text = text.replace("\t", " ")
31
+ text = re.sub(r'(""".*?"""|\'\'\'.*?\'\'\')', '', text, flags=re.DOTALL)
32
+ text = "\n".join([ll.rstrip() for ll in text.splitlines() if ll.strip()])
33
+ lines = text.split("\n")
34
+ spaces_for_each_line = []
35
+ for line in lines:
36
+ match = re.match(r'^( *)', line)
37
+ if match:
38
+ leading_spaces = len(match.group(1))
39
+ spaces_for_each_line.append(leading_spaces)
40
+ try:
41
+ def_line = [i for i, line in enumerate(lines) if "def" in line][0]
42
+ def_line_space = spaces_for_each_line[def_line]
43
+ except:
44
+ print("No def line found")
45
+ print(text)
46
+ def_line_space = 0
47
+ rank_unique_spaces = sorted(list(set(spaces_for_each_line)))
48
+ indentation_level = {}
49
+ i = 0
50
+ for space in rank_unique_spaces:
51
+ if space <= def_line_space:
52
+ indentation_level[space] = 0
53
+ else:
54
+ i += 1
55
+ indentation_level[space] = i
56
+ new_lines = []
57
+ for line, space in zip(lines, spaces_for_each_line):
58
+ new_lines.append(" " * indentation_level[space] + line.lstrip())
59
+ return "\n".join(new_lines)
60
+
61
+ def generate_one_completion(model, tokenizer, model_type, prompt, template=True):
62
+ if template:
63
+ prompt_in = ALPACA_PREFIX_TEMPLATE_MD.format(PROMPT=prompt)
64
+ pred_text = model_inference(model, tokenizer, prompt_in, model_type, max_target_length=512)
65
+ post_pred = post_process(pred_text)
66
+ return post_pred
67
+
68
+
69
+
70
+
71
+ def humaneval(model, tokenizer, save_dir, model_type = "CausalLM", model_name="llama/llama-2-7b-hf"):
72
+
73
+ import os
74
+ os.environ["TOKENIZERS_PARALLELISM"] = "false"
75
+
76
+ problems = read_problems()
77
+ num_samples_per_task = 1
78
+ samples = [
79
+ dict(task_id=task_id, completion=generate_one_completion(model, tokenizer, model_type, problems[task_id]["prompt"]))
80
+ for task_id in tqdm(problems, desc="Tasks")
81
+ for _ in range(num_samples_per_task)
82
+ ]
83
+
84
+ target_name = os.path.join(save_dir, f"{model_name.replace('/', '_')}_humaneval_samples.jsonl")
85
+ write_jsonl(target_name, samples)
86
+ results = evaluate_functional_correctness(target_name, [1])
87
+ print(results)
88
+
logTrainer.py ADDED
@@ -0,0 +1,238 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from dataclasses import dataclass, field
2
+ from functools import reduce
3
+ from typing import Callable, Dict, List, Optional, Tuple, Union, Any
4
+
5
+ import numpy as np
6
+ import torch
7
+ import wandb
8
+ import torch.nn as nn
9
+ from torch.utils.data import Dataset
10
+
11
+ from transformers import Trainer, Seq2SeqTrainingArguments
12
+ from transformers.data.data_collator import DataCollator
13
+ from transformers.pytorch_utils import ALL_LAYERNORM_LAYERS
14
+ from transformers.trainer import (
15
+ EvalPrediction,
16
+ PreTrainedModel,
17
+ PreTrainedTokenizerBase,
18
+ TrainerCallback,
19
+ )
20
+ from transformers.trainer_pt_utils import get_parameter_names
21
+ from transformers.utils import is_sagemaker_mp_enabled, logging
22
+ from peft.tuners.lora.layer import Linear as LoraLinear
23
+
24
+ # include_keywords = ["block.0", "block.4"]
25
+ include_keywords = ["encoder.block.2","encoder.block.3","encoder.block.4"] # for T5
26
+ # include_keywords = ["layers.27", "layers.6"] # for Llama
27
+ do_log = False
28
+
29
+
30
+ def get_forward_hook(name):
31
+ def hook(module, input, output):
32
+ wandb.log(
33
+ {
34
+ f"{name}/input_mean": input[0].mean().item(),
35
+ f"{name}/input_std": input[0].std().item(),
36
+ f"{name}/output_mean": output.mean().item(),
37
+ f"{name}/output_std": output.std().item(),
38
+ },
39
+ commit=False,
40
+ )
41
+ return hook
42
+
43
+ class LogTrainer(Trainer):
44
+ def __init__(
45
+ self,
46
+ model: Union[PreTrainedModel, nn.Module] = None,
47
+ args: Seq2SeqTrainingArguments = None,
48
+ data_collator: Optional[DataCollator] = None,
49
+ train_dataset: Optional[Dataset] = None,
50
+ eval_dataset: Optional[Union[Dataset, Dict[str, Dataset]]] = None,
51
+ tokenizer: Optional[PreTrainedTokenizerBase] = None,
52
+ model_init: Optional[Callable[[], PreTrainedModel]] = None,
53
+ compute_metrics: Optional[Callable[[EvalPrediction], Dict]] = None,
54
+ callbacks: Optional[List[TrainerCallback]] = None,
55
+ optimizers: Tuple[torch.optim.Optimizer, torch.optim.lr_scheduler.LambdaLR] = (
56
+ None,
57
+ None,
58
+ ),
59
+ preprocess_logits_for_metrics: Optional[
60
+ Callable[[torch.Tensor, torch.Tensor], torch.Tensor]
61
+ ] = None,
62
+ ):
63
+ super().__init__(
64
+ model,
65
+ args,
66
+ data_collator,
67
+ train_dataset,
68
+ eval_dataset,
69
+ tokenizer,
70
+ model_init,
71
+ compute_metrics,
72
+ callbacks,
73
+ optimizers,
74
+ preprocess_logits_for_metrics,
75
+ )
76
+ self.is_peft = "PeftModel" in type(model).__name__
77
+ if self.is_peft:
78
+ for name, module in model.named_modules():
79
+ if isinstance(module, LoraLinear):
80
+ self.scaling = module.scaling["default"]
81
+ break
82
+ self.orig_A = None
83
+ self.orig_B = None
84
+ self.orig_W = None
85
+ self.gradient_accumulation_counter = 0
86
+
87
+ def training_step(
88
+ self, model: nn.Module, inputs: Dict[str, Union[torch.Tensor, Any]]
89
+ ) -> torch.Tensor:
90
+ if not do_log:
91
+ return super().training_step(model, inputs)
92
+ if self.is_peft:
93
+ if self.orig_A is None:
94
+ self.orig_A = {}
95
+ self.orig_B = {}
96
+ for name, param in model.named_parameters():
97
+ if param.requires_grad and any(
98
+ [kw in name for kw in include_keywords]
99
+ ):
100
+ if "lora_A" in name:
101
+ self.orig_A[name.split("lora_A.")[0]] = (
102
+ param.detach().clone()
103
+ )
104
+ elif "lora_B" in name:
105
+ self.orig_B[name.split("lora_B.")[0]] = (
106
+ param.detach().clone()
107
+ )
108
+ for name, module in model.named_modules():
109
+ if any([kw in name for kw in include_keywords]) and isinstance(module, LoraLinear):
110
+ breakpoint()
111
+ hook = get_forward_hook(name)
112
+ module.register_forward_hook(hook)
113
+ else:
114
+ if self.orig_W is None:
115
+ self.orig_W = {}
116
+ for name, param in model.named_parameters():
117
+ if param.requires_grad and any(
118
+ [kw in name for kw in include_keywords]
119
+ ):
120
+ self.orig_W[name] = param.detach().clone()
121
+
122
+ model.train()
123
+ inputs = self._prepare_inputs(inputs)
124
+
125
+ with self.compute_loss_context_manager():
126
+ loss = self.compute_loss(model, inputs)
127
+
128
+ if self.args.n_gpu > 1:
129
+ loss = loss.mean() # mean() to average on multi-gpu parallel training
130
+
131
+ self.accelerator.backward(loss)
132
+ with torch.no_grad():
133
+ if (
134
+ self.gradient_accumulation_counter
135
+ % self.args.gradient_accumulation_steps
136
+ == self.args.gradient_accumulation_steps - 1
137
+ ):
138
+ if self.is_peft:
139
+ A_dict = {}
140
+ B_dict = {}
141
+ for name, param in model.named_parameters():
142
+ if param.requires_grad and any(
143
+ [kw in name for kw in include_keywords]
144
+ ):
145
+ if "lora_A" in name:
146
+ A_dict[name.split("lora_A.")[0]] = param
147
+ elif "lora_B" in name:
148
+ B_dict[name.split("lora_B.")[0]] = param
149
+ assert (
150
+ len(A_dict)
151
+ == len(self.orig_A)
152
+ == len(B_dict)
153
+ == len(self.orig_B)
154
+ ), (
155
+ len(A_dict),
156
+ len(self.orig_A),
157
+ len(B_dict),
158
+ len(self.orig_B),
159
+ )
160
+ for key in A_dict.keys():
161
+ A = A_dict[key]
162
+ B = B_dict[key]
163
+ lora_r = A.shape[0]
164
+ A_grad = A_dict[key].grad
165
+ B_grad = B_dict[key].grad
166
+ A_0 = self.orig_A[key]
167
+ B_0 = self.orig_B[key]
168
+ A_diff = A - A_0
169
+ B_diff = B - B_0
170
+ BA = torch.matmul(B, A)
171
+ BA_0 = torch.matmul(B_0, A_0)
172
+ BA_diff = BA - BA_0
173
+ BA_diff_norm = torch.norm(BA_diff).item()
174
+ A_diff_norm = torch.norm(A_diff).item()
175
+ B_diff_norm = torch.norm(B_diff).item()
176
+ A_norm = torch.norm(A).item()
177
+ B_norm = torch.norm(B).item()
178
+ A_grad_norm = torch.norm(A_grad).item()
179
+ B_grad_norm = torch.norm(B_grad).item()
180
+ # BA_singular_values = torch.svd(BA_diff.float(), compute_uv=False).S[:lora_r]
181
+ BA_singular_values = torch.svd_lowrank(
182
+ BA_diff.float(), q=2 * lora_r
183
+ )[1][:lora_r]
184
+ top_1_ratio = (
185
+ BA_singular_values[0] / BA_singular_values.sum()
186
+ ).item()
187
+ top_4_ratio = (
188
+ BA_singular_values[:4].sum() / BA_singular_values.sum()
189
+ ).item()
190
+ wandb.log(
191
+ {
192
+ f"A_norm/{key}": A_norm,
193
+ f"B_norm/{key}": B_norm,
194
+ f"A_grad_norm/{key}": A_grad_norm,
195
+ f"B_grad_norm/{key}": B_grad_norm,
196
+ f"A_diff_norm/{key}": A_diff_norm,
197
+ f"B_diff_norm/{key}": B_diff_norm,
198
+ f"BA_diff_norm/{key}": BA_diff_norm,
199
+ f"scaled_BA_diff_norm/{key}": self.scaling
200
+ * BA_diff_norm,
201
+ f"BA_top_1_ratio/{key}": top_1_ratio,
202
+ f"BA_top_4_ratio/{key}": top_4_ratio,
203
+ "train/global_step": self.state.global_step,
204
+ }
205
+ )
206
+ else:
207
+ W_dict = {}
208
+ for name, param in model.named_parameters():
209
+ if (
210
+ param.requires_grad
211
+ and any([kw in name for kw in include_keywords])
212
+ and len(param.shape) == 2
213
+ ):
214
+ W_dict[name] = param
215
+ for key in W_dict.keys():
216
+ W = W_dict[key]
217
+ W_grad = W.grad
218
+ W_0 = self.orig_W[key]
219
+ W_diff = W - W_0
220
+ W_diff_norm = torch.norm(W_diff).item()
221
+ W_norm = torch.norm(W).item()
222
+ W_grad_norm = torch.norm(W_grad).item()
223
+ U, S, V = torch.svd(W_diff.float())
224
+ top_1_ratio = S[0] / S.sum()
225
+ top_4_ratio = S[:4].sum() / S.sum()
226
+ wandb.log(
227
+ {
228
+ f"W_norm/{key}": W_norm,
229
+ f"W_grad_norm/{key}": W_grad_norm,
230
+ f"W_diff_norm/{key}": W_diff_norm,
231
+ "train/global_step": self.state.global_step,
232
+ f"W_top_1_ratio/{key}": top_1_ratio.item(),
233
+ f"W_top_4_ratio/{key}": top_4_ratio.item(),
234
+ }
235
+ )
236
+ self.gradient_accumulation_counter += 1
237
+
238
+ return loss.detach() / self.args.gradient_accumulation_steps
lora_plus.py ADDED
@@ -0,0 +1,210 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from dataclasses import dataclass, field
2
+ from functools import reduce
3
+ from typing import Callable, Dict, List, Optional, Tuple, Union
4
+
5
+ import torch
6
+ import torch.nn as nn
7
+ from torch.utils.data import Dataset
8
+
9
+ from peft.tuners import lora
10
+ from transformers import Trainer, Seq2SeqTrainingArguments
11
+ from transformers.data.data_collator import DataCollator
12
+ from transformers.pytorch_utils import ALL_LAYERNORM_LAYERS
13
+ from transformers.trainer import (EvalPrediction, PreTrainedModel,
14
+ PreTrainedTokenizerBase, TrainerCallback)
15
+ from transformers.trainer_pt_utils import get_parameter_names
16
+ from transformers.utils import is_sagemaker_mp_enabled, logging
17
+ from logTrainer import LogTrainer
18
+
19
+ logger = logging.get_logger(__name__)
20
+
21
+
22
+ @dataclass
23
+ class LoraPlusTrainingArguments(Seq2SeqTrainingArguments):
24
+ loraplus_lr_ratio: Optional[float] = field(
25
+ default=None, metadata={"help": "loraplus learning rate ratio lr_B / lr_A."}
26
+ )
27
+ loraplus_lr_embedding: Optional[float] = field(
28
+ default=1e-6,
29
+ metadata={"help": "loraplus learning rate for lora embedding layers."},
30
+ )
31
+
32
+
33
+ def get_module(name, opt_model):
34
+ """
35
+ Retrieve a module from a model using its parameter name.
36
+ Args:
37
+ name (str): Full name of the parameter, typically including module path.
38
+ opt_model (torch.nn.Module): The model from which to retrieve the module.
39
+
40
+ Returns:
41
+ Module corresponding to the given name.
42
+ """
43
+ parent_idx = 2 if "lora" in name else 1
44
+ module_names = name.split(sep=".")[:-parent_idx]
45
+ module = reduce(getattr, module_names, opt_model)
46
+ return module
47
+
48
+
49
+ def create_loraplus_optimizer(
50
+ opt_model,
51
+ optimizer_cls,
52
+ optimizer_kwargs,
53
+ loraplus_lr_ratio,
54
+ loraplus_lr_embedding=None,
55
+ ):
56
+ """
57
+ Creates an optimizer for the given model, applying LoRA-specific learning rate adjustments to different parameter groups.
58
+
59
+ Args:
60
+ opt_model (torch.nn.Module): The model for which the optimizer is being created.
61
+ optimizer_cls (class): The class of the optimizer to be used (e.g., torch.optim.Adam).
62
+ optimizer_kwargs (dict): A dictionary of keyword arguments for the optimizer's initialization.
63
+ loraplus_lr_ratio (float): The learning rate ratio to be applied to LoRA parameters.
64
+ loraplus_lr_embedding (float, optional): A specific learning rate for embedding parameters, with a default value if not provided.
65
+
66
+ Returns:
67
+ An instance of the specified optimizer class configured with the model's parameters organized into groups with custom learning rates.
68
+ """
69
+
70
+ assert loraplus_lr_ratio is not None, "loraplus_lr_ratio must be provided."
71
+
72
+ if loraplus_lr_embedding is None:
73
+ loraplus_lr_embedding = 1e-6
74
+
75
+ decay_parameters = get_parameter_names(opt_model, ALL_LAYERNORM_LAYERS)
76
+ decay_parameters = [name for name in decay_parameters if "bias" not in name]
77
+ param_groups = {
78
+ "groupA": {},
79
+ "groupB": {},
80
+ "groupB_no_decay": {},
81
+ "embedding": {},
82
+ }
83
+
84
+ for name, param in opt_model.named_parameters():
85
+ if not param.requires_grad:
86
+ continue
87
+
88
+ module = get_module(name, opt_model)
89
+ if isinstance(module, lora.Embedding):
90
+ param_groups["embedding"][name] = param
91
+ elif "lora_B" in name or param.ndim == 1:
92
+ if name in decay_parameters:
93
+ param_groups["groupB"][name] = param
94
+ else:
95
+ param_groups["groupB_no_decay"][name] = param
96
+ else:
97
+ param_groups["groupA"][name] = param
98
+
99
+ assigned_param_groups = ""
100
+ for group in param_groups:
101
+ assigned_param_groups += f"{group}\n {list(param_groups[group].keys())}\n\n"
102
+ logger.debug(assigned_param_groups)
103
+
104
+ lr = optimizer_kwargs["lr"]
105
+ weight_decay = optimizer_kwargs.get("weight_decay", 0.0)
106
+
107
+ optimizer_grouped_parameters = [
108
+ {
109
+ "params": list(param_groups["groupA"].values()),
110
+ "weight_decay": weight_decay,
111
+ "lr": lr,
112
+ },
113
+ {
114
+ "params": list(param_groups["embedding"].values()),
115
+ "weight_decay": weight_decay,
116
+ "lr": loraplus_lr_embedding,
117
+ },
118
+ {
119
+ "params": list(param_groups["groupB"].values()),
120
+ "weight_decay": weight_decay,
121
+ "lr": lr * loraplus_lr_ratio,
122
+ },
123
+ {
124
+ "params": list(param_groups["groupB_no_decay"].values()),
125
+ "weight_decay": 0.0,
126
+ "lr": lr * loraplus_lr_ratio,
127
+ },
128
+ ]
129
+
130
+ optimizer = optimizer_cls(optimizer_grouped_parameters, **optimizer_kwargs)
131
+ if optimizer_cls.__name__ == "Adam8bit":
132
+ import bitsandbytes
133
+
134
+ manager = bitsandbytes.optim.GlobalOptimManager.get_instance()
135
+
136
+ skipped = 0
137
+ for module in opt_model.modules():
138
+ if isinstance(module, nn.Embedding):
139
+ skipped += sum(
140
+ {p.data_ptr(): p.numel() for p in module.parameters()}.values()
141
+ )
142
+ logger.info(f"skipped {module}: {skipped/2**20}M params")
143
+ manager.register_module_override(module, "weight", {"optim_bits": 32})
144
+ logger.debug(f"bitsandbytes: will optimize {module} in fp32")
145
+ logger.info(f"skipped: {skipped/2**20}M params")
146
+
147
+ return optimizer
148
+
149
+
150
+ class LoraPlusTrainer(LogTrainer):
151
+ def __init__(
152
+ self,
153
+ model: Union[PreTrainedModel, nn.Module] = None,
154
+ args: LoraPlusTrainingArguments = None,
155
+ data_collator: Optional[DataCollator] = None,
156
+ train_dataset: Optional[Dataset] = None,
157
+ eval_dataset: Optional[Union[Dataset, Dict[str, Dataset]]] = None,
158
+ tokenizer: Optional[PreTrainedTokenizerBase] = None,
159
+ model_init: Optional[Callable[[], PreTrainedModel]] = None,
160
+ compute_metrics: Optional[Callable[[EvalPrediction], Dict]] = None,
161
+ callbacks: Optional[List[TrainerCallback]] = None,
162
+ optimizers: Tuple[torch.optim.Optimizer, torch.optim.lr_scheduler.LambdaLR] = (
163
+ None,
164
+ None,
165
+ ),
166
+ preprocess_logits_for_metrics: Optional[
167
+ Callable[[torch.Tensor, torch.Tensor], torch.Tensor]
168
+ ] = None,
169
+ ):
170
+ assert isinstance(
171
+ args, LoraPlusTrainingArguments
172
+ ), "args must be of type LoraPlusTrainingArguments"
173
+ super().__init__(
174
+ model,
175
+ args,
176
+ data_collator,
177
+ train_dataset,
178
+ eval_dataset,
179
+ tokenizer,
180
+ model_init,
181
+ compute_metrics,
182
+ callbacks,
183
+ optimizers,
184
+ preprocess_logits_for_metrics,
185
+ )
186
+
187
+ def create_optimizer(self):
188
+ """
189
+ Overrides the method to create an optimizer with LoRA+ specific adjustments.
190
+ """
191
+ if self.args.loraplus_lr_ratio is None:
192
+ return super().create_optimizer()
193
+
194
+ opt_model = self.model_wrapped if is_sagemaker_mp_enabled() else self.model
195
+ if self.optimizer is None:
196
+ optimizer_cls, optimizer_kwargs = Trainer.get_optimizer_cls_and_kwargs(
197
+ self.args
198
+ )
199
+
200
+ loraplus_lr_ratio = getattr(self.args, "loraplus_lr_ratio", None)
201
+ loraplus_lr_embedding = getattr(self.args, "loraplus_lr_embedding", None)
202
+ self.optimizer = create_loraplus_optimizer(
203
+ opt_model,
204
+ optimizer_cls,
205
+ optimizer_kwargs,
206
+ loraplus_lr_ratio,
207
+ loraplus_lr_embedding,
208
+ )
209
+
210
+ return self.optimizer
peft.zip ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:3a2008a24660d3a4e89498528727c16cad435e3549cae135ad3ca25afdcebefd
3
+ size 6737683
requirements.txt ADDED
@@ -0,0 +1,149 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ accelerate==0.30.1
2
+ aiohttp==3.9.5
3
+ aiosignal==1.3.1
4
+ annotated-types==0.6.0
5
+ anthropic==0.25.8
6
+ antlr4-python3-runtime==4.9.3
7
+ anyio==4.3.0
8
+ asttokens==2.4.1
9
+ attrs==23.2.0
10
+ cachetools==5.3.3
11
+ calflops==0.2.9
12
+ certifi==2024.2.2
13
+ charset-normalizer==3.3.2
14
+ click==8.1.7
15
+ comm==0.2.2
16
+ datasets==2.19.1
17
+ debugpy==1.8.1
18
+ decorator==5.1.1
19
+ dill==0.3.8
20
+ distro==1.9.0
21
+ dnspython==2.6.1
22
+ docker-pycreds==0.4.0
23
+ einops==0.8.0
24
+ email_validator==2.1.1
25
+ executing==2.0.1
26
+ fastapi==0.111.0
27
+ fastapi-cli==0.0.3
28
+ filelock==3.14.0
29
+ fire==0.6.0
30
+ flash-attn==2.5.8
31
+ frozenlist==1.4.1
32
+ fsspec==2024.3.1
33
+ gitdb==4.0.11
34
+ GitPython==3.1.43
35
+ h11==0.14.0
36
+ httpcore==1.0.5
37
+ httptools==0.6.1
38
+ httpx==0.27.0
39
+ huggingface-hub==0.23.0
40
+ hydra-core==1.3.2
41
+ idna==3.7
42
+ ipykernel==6.29.4
43
+ ipython==8.24.0
44
+ jedi==0.19.1
45
+ Jinja2==3.1.4
46
+ jsonschema==4.22.0
47
+ jsonschema-specifications==2023.12.1
48
+ jupyter_client==8.6.1
49
+ jupyter_core==5.7.2
50
+ markdown-it-py==3.0.0
51
+ markdown2==2.4.13
52
+ MarkupSafe==2.1.5
53
+ matplotlib-inline==0.1.7
54
+ mdurl==0.1.2
55
+ mpmath==1.3.0
56
+ msgpack==1.0.8
57
+ multidict==6.0.5
58
+ multiprocess==0.70.16
59
+ nest-asyncio==1.6.0
60
+ networkx==3.3
61
+ nh3==0.2.17
62
+ ninja==1.11.1.1
63
+ numpy==1.26.4
64
+ nvidia-cublas-cu12==12.1.3.1
65
+ nvidia-cuda-cupti-cu12==12.1.105
66
+ nvidia-cuda-nvrtc-cu12==12.1.105
67
+ nvidia-cuda-runtime-cu12==12.1.105
68
+ nvidia-cudnn-cu12==8.9.2.26
69
+ nvidia-cufft-cu12==11.0.2.54
70
+ nvidia-curand-cu12==10.3.2.106
71
+ nvidia-cusolver-cu12==11.4.5.107
72
+ nvidia-cusparse-cu12==12.1.0.106
73
+ nvidia-ml-py==12.535.161
74
+ nvidia-nccl-cu12==2.20.5
75
+ nvidia-nvjitlink-cu12==12.4.127
76
+ nvidia-nvtx-cu12==12.1.105
77
+ nvitop==1.3.2
78
+ omegaconf==2.3.0
79
+ openai==0.28.1
80
+ orjson==3.10.3
81
+ packaging==24.0
82
+ pandas==2.2.2
83
+ parso==0.8.4
84
+ peft==0.10.0
85
+ pexpect==4.9.0
86
+ pillow==10.3.0
87
+ platformdirs==4.2.1
88
+ prompt-toolkit==3.0.43
89
+ protobuf==4.25.3
90
+ psutil==5.9.8
91
+ ptyprocess==0.7.0
92
+ pure-eval==0.2.2
93
+ pyarrow==16.0.0
94
+ pyarrow-hotfix==0.6
95
+ pydantic==2.7.1
96
+ pydantic_core==2.18.2
97
+ Pygments==2.18.0
98
+ python-dateutil==2.9.0.post0
99
+ python-dotenv==1.0.1
100
+ python-multipart==0.0.9
101
+ pytz==2024.1
102
+ PyYAML==6.0.1
103
+ pyzmq==26.0.3
104
+ ray==2.21.0
105
+ referencing==0.35.1
106
+ regex==2024.5.10
107
+ requests==2.31.0
108
+ rich==13.7.1
109
+ rpds-py==0.18.1
110
+ safetensors==0.4.3
111
+ scipy==1.13.0
112
+ sentencepiece==0.2.0
113
+ sentry-sdk==2.1.1
114
+ setproctitle==1.3.3
115
+ shellingham==1.5.4
116
+ shortuuid==1.0.13
117
+ six==1.16.0
118
+ smmap==5.0.1
119
+ sniffio==1.3.1
120
+ stack-data==0.6.3
121
+ starlette==0.37.2
122
+ svgwrite==1.4.3
123
+ sympy==1.12
124
+ termcolor==2.4.0
125
+ tiktoken==0.6.0
126
+ tokenizers==0.19.1
127
+ torch==2.3.0
128
+ torchaudio==2.3.0
129
+ torchprofile==0.0.4
130
+ torchvision==0.18.0
131
+ tornado==6.4
132
+ tqdm==4.66.4
133
+ traitlets==5.14.3
134
+ transformers==4.44.0
135
+ triton==2.3.0
136
+ typer==0.12.3
137
+ typing_extensions==4.11.0
138
+ tzdata==2024.1
139
+ ujson==5.9.0
140
+ urllib3==2.2.1
141
+ uvicorn==0.29.0
142
+ uvloop==0.19.0
143
+ wandb==0.17.0
144
+ watchfiles==0.21.0
145
+ wavedrom==2.0.3.post3
146
+ wcwidth==0.2.13
147
+ websockets==12.0
148
+ xxhash==3.4.1
149
+ yarl==1.9.4
run.sh ADDED
@@ -0,0 +1 @@
 
 
1
+ CUDA_VISIBLE_DEVICES=0 python run_exp.py +model=llama +peft=all ++peft.lora_r1=2 ++peft.lora_r2=2 ++peft.lora_r=8 ++peft.lora_alpha=64 +dataset_name=meta_math +init=default +seed=333
run_exp.py ADDED
@@ -0,0 +1,705 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from peft import get_peft_model, LoraConfig, AdaLoraConfig, TaskType
2
+ import os
3
+ import hydra
4
+ from omegaconf import DictConfig, OmegaConf
5
+ from utils import (
6
+ train_text_to_text_model,
7
+ model_inference,
8
+ initialize_text_to_text_model,
9
+ transform_dataset,
10
+ merge_llama,
11
+ )
12
+ import json
13
+ import math
14
+ from datasets import load_dataset
15
+ import wandb
16
+ from data import *
17
+ from typing import List
18
+ import torch
19
+ from copy import deepcopy
20
+ import logging
21
+ from tqdm import tqdm, trange
22
+ from typing import Tuple, List, Dict
23
+ from peft.tuners.lora.layer import Linear as LoraLinear
24
+ from split import rebuild
25
+ import re
26
+ import itertools
27
+ import matplotlib.pyplot as plt
28
+ from commonsense_evaluate import common_evaluate
29
+ from eval_humaneval import humaneval
30
+ # from eval_mtbench import evaluate_mtbench_from_model
31
+ log = logging.getLogger(__name__)
32
+
33
+ s = 0
34
+
35
+ def kron(A, B):
36
+ return (A[:, None, :, None] * B[None, :, None, :]).reshape(A.shape[0] * B.shape[0], A.shape[1] * B.shape[1])
37
+
38
+ def modified_gram_schmidt(W, eps=1e-12):
39
+ """
40
+ Modified Gram–Schmidt QR
41
+ W: (m, n)
42
+ Returns:
43
+ Q: (m, n)
44
+ R: (n, n)
45
+ """
46
+ m, n = W.shape
47
+ Q = W.clone()
48
+ R = torch.zeros(n, n, device=W.device, dtype=W.dtype)
49
+
50
+ for i in range(n):
51
+ R[i, i] = torch.norm(Q[:, i])
52
+ if R[i, i] < eps:
53
+ raise RuntimeError("Linearly dependent columns")
54
+
55
+ Q[:, i] = Q[:, i] / R[i, i]
56
+
57
+ for j in range(i + 1, n):
58
+ R[i, j] = torch.dot(Q[:, i], Q[:, j])
59
+ Q[:, j] = Q[:, j] - R[i, j] * Q[:, i]
60
+
61
+ return Q, R
62
+
63
+ def seed_everything(seed: int):
64
+ import random, os
65
+ import numpy as np
66
+ import torch
67
+
68
+ random.seed(seed)
69
+ os.environ["PYTHONHASHSEED"] = str(seed)
70
+ np.random.seed(seed)
71
+ torch.manual_seed(seed)
72
+ torch.cuda.manual_seed(seed)
73
+ torch.backends.cudnn.deterministic = True
74
+ torch.backends.cudnn.benchmark = True
75
+
76
+
77
+ def find_all_linear_modules(model) -> List[str]:
78
+ r"""
79
+ Finds all available modules to apply lora.
80
+ """
81
+ linear_cls = torch.nn.Linear
82
+
83
+ output_layer_names = ["lm_head", "embed_tokens"]
84
+
85
+ module_names = set()
86
+ for name, module in model.named_modules():
87
+ if isinstance(module, linear_cls) and not any(
88
+ [output_layer in name for output_layer in output_layer_names]
89
+ ):
90
+ module_names.add(name.split(".")[-1])
91
+ return list(module_names)
92
+
93
+
94
+ def find_hidden_state_size(model):
95
+ for name, module in model.named_modules():
96
+ if isinstance(module, torch.nn.Linear):
97
+ return min(module.weight.shape)
98
+ return None
99
+
100
+
101
+ def calculate_gain(
102
+ nonlinearity, param
103
+ ) -> float:
104
+ linear_fns = [
105
+ "linear",
106
+ "conv1d",
107
+ "conv2d",
108
+ "conv3d",
109
+ "conv_transpose1d",
110
+ "conv_transpose2d",
111
+ "conv_transpose3d",
112
+ ]
113
+ if nonlinearity in linear_fns or nonlinearity == "sigmoid":
114
+ return 1
115
+ elif nonlinearity == "tanh":
116
+ return 5.0 / 3
117
+ elif nonlinearity == "relu":
118
+ return math.sqrt(2.0)
119
+ elif nonlinearity == "leaky_relu":
120
+ if param is None:
121
+ negative_slope = 0.01
122
+ elif (
123
+ not isinstance(param, bool)
124
+ and isinstance(param, int)
125
+ or isinstance(param, float)
126
+ ):
127
+ # True/False are instances of int, hence check above
128
+ negative_slope = param
129
+ else:
130
+ raise ValueError(f"negative_slope {param} not a valid number")
131
+ return math.sqrt(2.0 / (1 + negative_slope**2))
132
+ elif nonlinearity == "selu":
133
+ return (
134
+ 3.0 / 4
135
+ ) # Value found empirically (https://github.com/pytorch/pytorch/pull/50664)
136
+ else:
137
+ raise ValueError(f"Unsupported nonlinearity {nonlinearity}")
138
+
139
+ def kaimings(weight, a=math.sqrt(5), fan=4096):
140
+ nonlinearity = "leaky_relu"
141
+ generator = None
142
+ gain = calculate_gain(nonlinearity, a)
143
+ std = gain / math.sqrt(fan)
144
+ bound = math.sqrt(3.0) * std # Calculate uniform bounds from standard deviation
145
+ with torch.no_grad():
146
+ return weight.uniform_(-bound, bound, generator=generator)
147
+
148
+ @torch.no_grad()
149
+ def reinit_lora_modules(name, module, init_config, **kwargs):
150
+ r"""
151
+ Reinitialize the lora model with the given configuration.
152
+ """
153
+ lora_r1 = kwargs["lora_r1"]
154
+ lora_r2 = kwargs["lora_r2"]
155
+ lora_r = kwargs["lora_r"]
156
+ # lora_r1 = min(module.lora_A.default.weight.shape)
157
+ # lora_r2 = min(module.lora_B.default.weight.shape)
158
+ a_dim = max(module.lora_A.default.weight.shape)
159
+ b_dim = max(module.lora_B.default.weight.shape)
160
+ if init_config.mode == "simple":
161
+ match init_config.lora_A:
162
+ case "gaussian":
163
+ torch.nn.init.normal_(
164
+ module.lora_A.default.weight, mean=0.0, std=init_config.lora_A_std
165
+ )
166
+ case "kaiming":
167
+ # https://github.com/microsoft/LoRA/blob/a0a92e0f26c067cf94747bdbf1ce73793fa44d19/loralib/layers.py#L124
168
+ torch.nn.init.kaiming_uniform_(module.lora_A.default.weight, a=math.sqrt(5))
169
+ case "kaimings":
170
+ kaimings(module.lora_A.default.weight, a=math.sqrt(5), fan=module.weight.size(1))
171
+ case "fan_out_kaiming":
172
+ torch.nn.init.kaiming_normal_(
173
+ module.lora_A.default.weight, mode="fan_out"
174
+ )
175
+ case "xavier":
176
+ torch.nn.init.xavier_normal_(module.lora_A.default.weight)
177
+ case "zeros":
178
+ torch.nn.init.zeros_(module.lora_A.default.weight)
179
+ case "unit":
180
+ torch.nn.init.normal_(
181
+ module.lora_A.default.weight, mean=0.0, std=1.0 / (a_dim**0.5)
182
+ )
183
+ case "orthogonal":
184
+ torch.nn.init.orthogonal_(module.lora_A.default.weight)
185
+ case _:
186
+ raise ValueError(f"Unknown lora_A initialization: {init_config.lora_A}")
187
+ match init_config.lora_B:
188
+ case "gaussian":
189
+ torch.nn.init.normal_(
190
+ module.lora_B.default.weight, mean=0.0, std=init_config.lora_B_std
191
+ )
192
+ case "kaiming":
193
+ torch.nn.init.kaiming_normal_(module.lora_B.default.weight.T, a=math.sqrt(5))
194
+ case "fan_out_kaiming":
195
+ torch.nn.init.kaiming_normal_(
196
+ module.lora_B.default.weight, mode="fan_out"
197
+ )
198
+ case "xavier":
199
+ torch.nn.init.xavier_normal_(module.lora_B.default.weight)
200
+ case "zeros":
201
+ torch.nn.init.zeros_(module.lora_B.default.weight)
202
+ case "unit":
203
+ torch.nn.init.normal_(
204
+ module.lora_B.default.weight, mean=0.0, std=1.0 / (b_dim**0.5)
205
+ )
206
+ case "orthogonal":
207
+ torch.nn.init.orthogonal_(module.lora_B.default.weight)
208
+ case _:
209
+ raise ValueError(f"Unknown lora_B initialization: {init_config.lora_B}")
210
+ if init_config.get("scale", "") == "stable":
211
+ gamma = init_config.stable_gamma
212
+ #module.lora_B.default.weight.data *= (m**0.25) / gamma**0.5
213
+ #module.lora_A.default.weight.data *= (n**0.25) / gamma**0.5
214
+ #module.lora_B.default.weight.data *= (m**0.25)
215
+ #module.lora_A.default.weight.data *= (n**0.25)
216
+ module.lora_B.default.weight.data *= 1
217
+ module.lora_A.default.weight.data *= 1
218
+
219
+
220
+ elif init_config.mode == "svd":
221
+ U, S, V = torch.svd_lowrank(module.weight.float(), q=4 * lora_r, niter=4)
222
+ V = V.T
223
+ m, n = module.weight.shape
224
+ if init_config.scale == "default":
225
+ S = S / module.scaling["default"]
226
+ module.lora_B.default.weight = torch.nn.Parameter(
227
+ (U[:, :lora_r] * torch.sqrt(S[:lora_r])).contiguous()
228
+ )
229
+ module.lora_A.default.weight = torch.nn.Parameter(
230
+ (V[:lora_r, :].T * torch.sqrt(S[:lora_r])).T.contiguous()
231
+ )
232
+ elif init_config.scale == "stable":
233
+ gamma = init_config.stable_gamma
234
+ module.lora_B.default.weight = torch.nn.Parameter(
235
+ (U[:, :lora_r] * (m**0.25) / gamma**0.5).contiguous()
236
+ )
237
+ module.lora_A.default.weight = torch.nn.Parameter(
238
+ (V[:lora_r, :] * (n**0.25) / gamma**0.5).contiguous()
239
+ )
240
+ elif init_config.scale == "unit":
241
+ module.lora_B.default.weight = torch.nn.Parameter(
242
+ (U[:, :lora_r]).contiguous()
243
+ )
244
+ module.lora_A.default.weight = torch.nn.Parameter(
245
+ (V[:lora_r, :]).contiguous()
246
+ )
247
+ elif init_config.scale == "normalized":
248
+ S_sum = S[:lora_r].sum()
249
+ module.lora_B.default.weight = torch.nn.Parameter(
250
+ (U[:, :lora_r] * torch.sqrt(S[:lora_r])/torch.sqrt(S_sum)*lora_r**0.5).contiguous()
251
+ )
252
+ module.lora_A.default.weight = torch.nn.Parameter(
253
+ (V[:lora_r, :].T * torch.sqrt(S[:lora_r])/torch.sqrt(S_sum)*lora_r**0.5).T.contiguous()
254
+ )
255
+
256
+ elif init_config.mode == "qr":
257
+ W = module.weight.float()
258
+ k,d = W.shape
259
+ Q, R = torch.linalg.qr(W, mode="reduced")
260
+ diag = torch.sign(torch.diag(R))
261
+ diag[diag == 0] = 1.0
262
+
263
+ D = torch.diag(diag)
264
+
265
+ Q = Q @ D
266
+ R = D @ R
267
+ print(torch.min(torch.diag(R)))
268
+ lambda_vals = torch.abs(torch.diag(R))
269
+ perm = torch.argsort(lambda_vals, descending=True)
270
+
271
+ I1 = perm[:lora_r2]
272
+ I2 = perm[lora_r2:lora_r1+lora_r2]
273
+ Q1 = Q[:, I1] # (m, r_high)
274
+ R1 = R[I1]
275
+ Q2 = Q[:, I2]
276
+ R2 = R[I2]
277
+ B = Q1[:k // lora_r1] @ R1[:, :lora_r2]
278
+ A = (Q2[:d // lora_r2] @ R2[:, :lora_r1]).T
279
+ module.lora_B.default.weight = torch.nn.Parameter(B.contiguous().to(module.lora_B.default.weight.device))
280
+ module.lora_A.default.weight = torch.nn.Parameter(A.contiguous().to(module.lora_A.default.weight.device))
281
+
282
+ elif init_config.mode == "gradient":
283
+ named_grad = kwargs["named_grads"]
284
+ grad_name = ".".join(name.split(".")[2:]) + ".weight"
285
+ grads = named_grad[grad_name]
286
+ # print(grads.shape)
287
+ if lora_r1 == 1 and lora_r2 == 1:
288
+ U, S, V = torch.svd_lowrank(-grads.cuda().float(), q=512, niter=16)
289
+ else:
290
+ U, S, V = torch.svd_lowrank(rebuild(-grads.float(),lora_r1, lora_r2), q=4*lora_r, niter=16)
291
+ V = V.T
292
+ # set direction
293
+ if init_config.direction == "ArBr":
294
+ if lora_r1 == 1 and lora_r2 == 1:
295
+ B = U[:, :lora_r] @ torch.diag(torch.sqrt(S[:lora_r])) / torch.sqrt(S[0]) / 128.0 **0.5
296
+ A = torch.diag(torch.sqrt(S[:lora_r])) @ V[:lora_r, :] / torch.sqrt(S[0]) / 128.0 **0.5
297
+ module.lora_B.default.weight = torch.nn.Parameter(B.contiguous().to(module.lora_B.default.weight.device))
298
+ module.lora_A.default.weight = torch.nn.Parameter(A.contiguous().to(module.lora_A.default.weight.device))
299
+ else:
300
+ for i in range(lora_r):
301
+ B = (S[i] / S[0] / 1024)**0.5 * V[i, :].reshape([lora_r2, grads.shape[0]//lora_r1]).T
302
+ A = (S[i] / S[0] / 1024)**0.5 * U[:, i].reshape([grads.shape[1]//lora_r2,lora_r1]).T
303
+ module.lora_A.default.weight[i::lora_r] = torch.nn.Parameter(A.contiguous().to(module.lora_A.default.weight.device))
304
+ module.lora_B.default.weight[:,i::lora_r] = torch.nn.Parameter(B.contiguous().to(module.lora_B.default.weight.device))
305
+ elif init_config.direction == "A2rBr":
306
+ B = U[:, :lora_r]
307
+ A = V[lora_r : 2 * lora_r, :]
308
+ elif init_config.direction == "ArB2r":
309
+ B = U[:, lora_r : 2 * lora_r]
310
+ A = V[:lora_r, :]
311
+ scaling_factor = module.scaling["default"]
312
+ if init_config.scale == "gd":
313
+ A = A / scaling_factor
314
+ B = B / scaling_factor
315
+ elif init_config.scale == "unit":
316
+ # Because A,B is orthogonal, do not need to scale
317
+ pass
318
+ elif init_config.scale == "stable":
319
+ m, n = grads.shape # m: feature_out, n: feature_in
320
+ # the scale of output is only related to the feature_out
321
+ gamma = init_config.stable_gamma
322
+
323
+
324
+ elif init_config.scale == "weightS":
325
+ _, S, _ = torch.svd_lowrank(module.weight.float(), q=4 * lora_r, niter=4)
326
+ S = S / module.scaling["default"]
327
+ avg_s = torch.sqrt(S[:lora_r]).mean().to(A.device)
328
+ B = B * avg_s
329
+ A = A * avg_s
330
+ # module.lora_B.default.weight = torch.nn.Parameter(B.contiguous().to(module.lora_B.default.weight.device))
331
+ # module.lora_A.default.weight = torch.nn.Parameter(A.contiguous().to(module.lora_A.default.weight.device))
332
+
333
+ with torch.no_grad():
334
+ # consider dtype not in init_config
335
+ if "dtype" not in init_config:
336
+ pass
337
+ elif init_config.dtype == "bf16":
338
+ module.lora_A.default.weight.data = module.lora_A.default.weight.data.to(
339
+ torch.bfloat16
340
+ )
341
+ module.lora_B.default.weight.data = module.lora_B.default.weight.data.to(
342
+ torch.bfloat16
343
+ )
344
+ elif init_config.dtype == "fp32":
345
+ module.lora_A.default.weight.data = module.lora_A.default.weight.data.to(
346
+ torch.float32
347
+ )
348
+ module.lora_B.default.weight.data = module.lora_B.default.weight.data.to(
349
+ torch.float32
350
+ )
351
+ # If lora_A@lora_B is not zero, then we need to subtract lora_A@lora_B from the original weight matrix
352
+ if init_config.mode == "qr":
353
+ offset = (kron(module.lora_B.default.weight.contiguous(),module.lora_A.default.weight.contiguous())).to(
354
+ module.weight.data.device
355
+ )
356
+ else:
357
+ offset = 0
358
+ # offset = (module.lora_B.default.weight @ module.lora_A.default.weight).to(
359
+ # module.weight.data.device
360
+ # )
361
+
362
+ scaling_factor = module.scaling["default"]
363
+ offset *= scaling_factor
364
+ if "norm_clip" in init_config and init_config.norm_clip:
365
+ # for numerical stability, offset's largest value must be less then weight's largest value
366
+ ratio = torch.max(torch.abs(module.weight.data)) / torch.max(
367
+ torch.abs(offset)
368
+ )
369
+ if ratio < 1:
370
+ offset *= ratio
371
+ module.lora_A.default.weight.data *= ratio**0.5
372
+ module.lora_B.default.weight.data *= ratio**0.5
373
+ log.warning(f"Clipping offset by {ratio}")
374
+ try:
375
+ module.weight.data -= offset
376
+ except:
377
+ breakpoint()
378
+
379
+
380
+ def reinit_lora(model, init_config, **kwargs):
381
+ r"""
382
+ Reinitialize the lora model with the given configuration.
383
+ """
384
+ for name, module in tqdm(
385
+ model.named_modules(),
386
+ desc="Reinitializing Lora",
387
+ total=len(list(model.named_modules())),
388
+ ):
389
+ if isinstance(module, LoraLinear):
390
+ reinit_lora_modules(name, module, init_config, **kwargs)
391
+
392
+ return model
393
+
394
+
395
+ def get_record_gradient_hook(model, record_dict):
396
+ def record_gradient_hook(grad):
397
+ for n, p in model.named_parameters():
398
+ if p.requires_grad and p.grad is not None:
399
+ if n not in record_dict:
400
+ record_dict[n] = p.grad.cpu()
401
+ else:
402
+ record_dict[n] += p.grad.cpu()
403
+ p.grad = None
404
+ return grad
405
+
406
+ return record_gradient_hook
407
+
408
+
409
+ def estimate_gradient(
410
+ model, dataset, batch_size: int = 4
411
+ ) -> Dict[str, List[torch.Tensor]]:
412
+ r"""
413
+ Estimate the gradient of the model on the given dataset
414
+ """
415
+ log.info("Estimating gradient")
416
+ model.train()
417
+ named_grads = {}
418
+ hooks = []
419
+ for name, param in model.named_parameters():
420
+ hook = param.register_hook(get_record_gradient_hook(model, named_grads))
421
+ hooks.append(hook)
422
+ dataloader = torch.utils.data.DataLoader(dataset, batch_size=batch_size)
423
+ num = 0
424
+ for batch in tqdm(dataloader, desc="Estimating gradient"):
425
+ num += 1
426
+ batch = {k: v.to(model.device) for k, v in batch.items()}
427
+ outputs = model(**batch)
428
+ outputs.loss.backward()
429
+ get_record_gradient_hook(model, named_grads)(None) # get gradient of last layer
430
+ # make sure the gradient is cleared
431
+ for n, p in model.named_parameters():
432
+ if p.grad is not None:
433
+ p.grad = None
434
+ for n, g in named_grads.items():
435
+ named_grads[n] /= num
436
+ for hook in hooks:
437
+ hook.remove()
438
+ torch.cuda.empty_cache()
439
+ return named_grads
440
+
441
+
442
+
443
+
444
+
445
+
446
+ def extract_num(text):
447
+ # Regex pattern to find the number following '####'
448
+ pattern = r'####\s*(\d+)'
449
+ # Using re.search to find the first match
450
+ match = re.search(pattern, text)
451
+ if match:
452
+ result = match.group(1)
453
+ print(text)
454
+ else:
455
+ print(text)
456
+ result = ""
457
+ try:
458
+ return int(result.replace(",", ""))
459
+ except:
460
+ print(f"'{result}' can't be converted")
461
+ return 0
462
+
463
+
464
+ def eval_gsm8k(model,tokenizer,model_type, test_set):
465
+ all = 0
466
+ correct = 0
467
+ t = tqdm(test_set)
468
+ for example in t:
469
+ # print(example['x'])
470
+ pred_text = model_inference(model, tokenizer, example['x'], model_type, max_target_length=512)
471
+ gt = extract_num(example["y"])
472
+ pred = extract_num(pred_text)
473
+ correct += int(gt == pred)
474
+ all += 1
475
+ t.set_description(f"Accuracy: {correct / all * 100:02f}%")
476
+
477
+ print("Acc:", correct / all)
478
+ # append to gsm8k_results.txt (create if not exists)
479
+ if not os.path.exists("gsm8k_results.txt"):
480
+ with open("gsm8k_results.txt", "w") as f:
481
+ f.write("Model Acc\n")
482
+ with open("gsm8k_results.txt", "a") as f:
483
+ f.write(f"{model_name} {correct / all}\n")
484
+
485
+ @hydra.main(version_base="1.2", config_path="conf", config_name="config")
486
+ def run_exp(cfg: DictConfig):
487
+ log.info(OmegaConf.to_yaml(cfg))
488
+ seed_everything(cfg.seed)
489
+ model_name = cfg.model.name
490
+ model_type = cfg.model.type
491
+ dataset_name = cfg.dataset_name
492
+ dataset_func = DATASET_MAP[dataset_name]
493
+ use_peft = cfg.peft.use_peft
494
+ if_use_rslora = cfg.peft.use_rslora
495
+ lora_r = cfg.peft.lora_r
496
+ lora_r1 = cfg.peft.lora_r1
497
+ lora_r2 = cfg.peft.lora_r2
498
+ lora_relative_r = cfg.peft.lora_relative_r
499
+ lora_target_modules = cfg.peft.lora_target_modules
500
+ train_embeddings = cfg.peft.train_embeddings
501
+ if cfg.dry_run:
502
+ return
503
+ if use_peft:
504
+ lora_r = cfg.peft.lora_r
505
+ lora_r1 = cfg.peft.lora_r1
506
+ lora_r2 = cfg.peft.lora_r2
507
+ lora_alpha = cfg.peft.lora_alpha
508
+ lora_relative_r = None
509
+ init = cfg.init.mode
510
+ else:
511
+ lora_r = None
512
+ lora_target_modules = None
513
+ lora_relative_r = None
514
+ train_embeddings = True
515
+ config = {
516
+ "model_name": model_name,
517
+ "dataset_name": dataset_name,
518
+ "use_peft": use_peft,
519
+ "lora_r1": lora_r1,
520
+ "lora_r2": lora_r2,
521
+ "lora_r": lora_r,
522
+ "lora_alpha": lora_alpha,
523
+ "init": init,
524
+ "lora_target_modules": str(lora_target_modules),
525
+ "lora_relative_r": lora_relative_r,
526
+ "train_embeddings": train_embeddings,
527
+ }
528
+ if cfg.wandb.name:
529
+ name = cfg.wandb.name
530
+ else:
531
+ name = "_".join([f"{k}={v}" for k, v in config.items()])
532
+ cfg.wandb.project += "_" + cfg.dataset_name
533
+ wandb.init(
534
+ project=cfg.wandb.project,
535
+ name=name,
536
+ config=config,
537
+ )
538
+ train_set, val_set, eval_set = dataset_func()
539
+ model, tokenizer = initialize_text_to_text_model(
540
+ model_name, model_type, cfg.model.bf16, cfg.peft.use_peft, flash_attention=True
541
+ )
542
+ additional_kwargs = {}
543
+ if use_peft and cfg.init.mode == "gradient":
544
+ if isinstance(train_set, list):
545
+ temp_set = train_set[: cfg.init.bsz * cfg.init.iters]
546
+ else:
547
+ temp_set = train_set.select(range(cfg.init.bsz * cfg.init.iters))
548
+ transform_dataset(
549
+ model_type=model_type,
550
+ dataset=temp_set,
551
+ tokenizer=tokenizer,
552
+ max_length=cfg.init.max_length,
553
+ )
554
+ # named_grads = estimate_layer_inputs(model, temp_set, cfg.init.bsz)
555
+ named_grads = estimate_gradient(model, temp_set, cfg.init.bsz)
556
+ additional_kwargs["named_grads"] = named_grads
557
+
558
+ additional_kwargs["lora_r1"] = lora_r1
559
+ additional_kwargs["lora_r"] = lora_r
560
+ additional_kwargs["lora_r2"] = lora_r2
561
+
562
+ if lora_target_modules == "all":
563
+ lora_target_modules = find_all_linear_modules(model)
564
+ else:
565
+ lora_target_modules = list(lora_target_modules) if lora_target_modules else []
566
+ if lora_relative_r is not None:
567
+ hidden_size = find_hidden_state_size(model)
568
+ lora_r = int(hidden_size * lora_relative_r)
569
+ log.info(f"lora_r is set to {hidden_size} * {lora_relative_r} = {lora_r}")
570
+ if use_peft and cfg.peft.get("dora", False):
571
+ log.info("Using Dora")
572
+ peft_config = LoraConfig(
573
+ r1=lora_r1,
574
+ r2=lora_r2,
575
+ lora_alpha=cfg.peft.lora_alpha,
576
+ target_modules=lora_target_modules,
577
+ use_rslora=if_use_rslora,
578
+ use_dora=True,
579
+ )
580
+ orig_model_params = sum(p.numel() for p in model.parameters())
581
+ model = get_peft_model(model, peft_config)
582
+ trainable_params, all_param = model.get_nb_trainable_parameters()
583
+ rate = {
584
+ "trainable_params": trainable_params,
585
+ "orig_params": orig_model_params,
586
+ "all_params": all_param,
587
+ "trainable_ratio": trainable_params / all_param,
588
+ "param_ratio": trainable_params / orig_model_params,
589
+ }
590
+ elif use_peft and cfg.peft.get("adalora", False):
591
+ log.info("Using AdaLora")
592
+ peft_config = AdaLoraConfig(
593
+ task_type=TaskType.CAUSAL_LM,
594
+ target_r=lora_r,
595
+ lora_alpha=cfg.peft.lora_alpha,
596
+ target_modules=lora_target_modules,
597
+ total_step=int(len(train_set)/cfg.model.real_batch_size)*cfg.model.epochs,
598
+ )
599
+ orig_model_params = sum(p.numel() for p in model.parameters())
600
+ model = get_peft_model(model, peft_config)
601
+ trainable_params, all_param = model.get_nb_trainable_parameters()
602
+ rate = {
603
+ "trainable_params": trainable_params,
604
+ "orig_params": orig_model_params,
605
+ "all_params": all_param,
606
+ "trainable_ratio": trainable_params / all_param,
607
+ "param_ratio": trainable_params / orig_model_params,
608
+ }
609
+ elif use_peft:
610
+ peft_config = LoraConfig(
611
+ r1=lora_r1,
612
+ r2=lora_r2,
613
+ r= lora_r,
614
+ lora_alpha=cfg.peft.lora_alpha,
615
+ target_modules=lora_target_modules,
616
+ use_rslora=if_use_rslora,
617
+ )
618
+ orig_model_params = sum(p.numel() for p in model.parameters())
619
+ model = get_peft_model(model, peft_config)
620
+ reinit_lora(model, cfg.init, **additional_kwargs)
621
+ if train_embeddings:
622
+ model.lm_head.weight.requires_grad = True
623
+ trainable_params, all_param = model.get_nb_trainable_parameters()
624
+ rate = {
625
+ "trainable_params": trainable_params,
626
+ "orig_params": orig_model_params,
627
+ "all_params": all_param,
628
+ "trainable_ratio": trainable_params / all_param,
629
+ "param_ratio": trainable_params / orig_model_params,
630
+ }
631
+ save_dir = os.path.join(
632
+ "results", f"{cfg.wandb.project}/{name}/{cfg.seed}", "orig_checkpoint"
633
+ )
634
+ model.save_pretrained(save_dir)
635
+ adapter_config = json.load(open(os.path.join(save_dir, "adapter_config.json")))
636
+ adapter_config["lora_alpha"] = -adapter_config["lora_alpha"]
637
+ json.dump(
638
+ adapter_config, open(os.path.join(save_dir, "adapter_config.json"), "w")
639
+ )
640
+ else:
641
+ # full finetune
642
+ all_param = sum(p.numel() for p in model.parameters())
643
+ trainable_params = sum(p.numel() for p in model.parameters() if p.requires_grad)
644
+ rate = {
645
+ "trainable_params": trainable_params,
646
+ "orig_params": all_param,
647
+ "all_params": all_param,
648
+ "trainable_ratio": trainable_params / all_param,
649
+ "param_ratio": 1,
650
+ }
651
+ log.info(rate)
652
+ wandb.summary.update(rate)
653
+ training_loop = train_text_to_text_model
654
+ global s
655
+ print(s)
656
+
657
+ model = training_loop(
658
+ f"{cfg.wandb.project}/{name}",
659
+ train_set,
660
+ val_set,
661
+ model,
662
+ tokenizer,
663
+ model_type,
664
+ num_train_epochs=cfg.model.epochs,
665
+ per_device_batch_size=cfg.model.per_device_batch_size,
666
+ real_batch_size=cfg.model.real_batch_size,
667
+ bf16=cfg.model.bf16,
668
+ eval_epochs=cfg.model.eval_epochs,
669
+ early_stopping_patience=cfg.model.early_stopping_patience,
670
+ max_length=cfg.model.max_length,
671
+ logging_steps=cfg.model.logging_steps,
672
+ use_loraplus=cfg.peft.use_loraplus,
673
+ loraplus_lr_ratio=cfg.peft.loraplus_lr_ratio,
674
+ learning_rate=cfg.model.learning_rate,
675
+ # deepspeed=(
676
+ # "z3_offload_all_bf16.json" if cfg.peft == False else None
677
+ # ),
678
+ gradient_checkpointing=cfg.get("gradient_checkpointing", False),
679
+ seed=cfg.seed,
680
+ )
681
+
682
+
683
+
684
+ save_dir = os.path.join(
685
+ "results", f"{cfg.wandb.project}/{name}/{cfg.seed}"
686
+ )
687
+ if not use_peft:
688
+ model.save_pretrained(save_dir)
689
+ tokenizer.save_pretrained(save_dir)
690
+ else:
691
+ # merge_llama(os.path.join("results", f"{cfg.wandb.project}/{name}/{cfg.seed}"))
692
+ pass
693
+ log.info(f"Saving model to {save_dir}")
694
+ if dataset_name == 'meta_math':
695
+ train_set, val_set, eval_set = load_gsm8k()
696
+ model.generation_config.pad_token_id = tokenizer.pad_token_id
697
+ eval_gsm8k(model,tokenizer,model_type,eval_set)
698
+ if dataset_name == 'codefeedback':
699
+ model.generation_config.pad_token_id = tokenizer.pad_token_id
700
+ humaneval(model,tokenizer,save_dir, model_type)
701
+ wandb.finish()
702
+
703
+
704
+ if __name__ == "__main__":
705
+ run_exp()
split.py ADDED
@@ -0,0 +1,136 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from typing import List, Tuple
3
+
4
+
5
+ # def vec(K):
6
+ # return K.T.flatten().reshape(-1, 1)
7
+
8
+ # def rebuild(K, r1, r2):
9
+ # """
10
+ # Implements the R(K) operation from the image.
11
+ # K: input matrix (k x d)
12
+ # r1: block height
13
+ # r2: number of block columns
14
+ # """
15
+ # k, d = K.shape
16
+ # num_block_rows = k // r1
17
+ # num_block_cols = r2
18
+ # bw = d // r2 # block width
19
+
20
+ # blocks = []
21
+ # # R(K) stacks vec(Ki,j) as columns.
22
+ # # The image shows column-major order through the blocks.
23
+ # for j in range(num_block_cols):
24
+ # for i in range(num_block_rows):
25
+ # # Extract block Ki,j
26
+ # Ki_j = K[i*r1:(i+1)*r1, j*bw:(j+1)*bw]
27
+ # # Vectorize (column-major) and add to list
28
+ # blocks.append(vec(Ki_j))
29
+
30
+ # return torch.hstack(blocks)
31
+
32
+
33
+ def rebuild(K, r1, r2):
34
+ k, d = K.shape
35
+ Br = k // r1 # number of block rows
36
+ bw = d // r2 # block width
37
+
38
+ # Step 1: reshape to (Br, r1, r2, bw)
39
+ K_view = K.view(Br, r1, r2, bw)
40
+
41
+ # Step 2: we want to vectorize each (r1, bw) block in COLUMN-MAJOR order.
42
+ # That is equivalent to transposing the block and flattening in row-major.
43
+ # So we permute to (Br, r2, bw, r1) and then flatten last two dims.
44
+
45
+ # But better: move r1 and bw to end, then transpose those two
46
+ # Actually: to get column-major flatten of (r1, bw), we can do:
47
+ # block.transpose(-2, -1).contiguous().view(-1)
48
+ # So let's transpose the last two dims of the block
49
+
50
+ # Current: (Br, r1, r2, bw) → we want to treat (r1, bw) as block → transpose to (bw, r1)
51
+ # So permute to (Br, r2, bw, r1)
52
+ K_transposed_blocks = K_view.permute(0, 2, 3, 1) # (Br, r2, bw, r1)
53
+
54
+ # Now flatten the last two dims (bw, r1) → (bw * r1,) → this is column-major of original block
55
+ vecs = K_transposed_blocks.reshape(Br, r2, bw * r1) # (Br, r2, vec_len)
56
+
57
+ # Now, we have vecs[i, j] = vectorized block (i,j)
58
+ # But we want to output columns in order: j=0: i=0,1,...,Br-1; j=1: i=0,...
59
+ # So we need to **transpose the first two dimensions** and then **flatten in row-major**
60
+
61
+ # Transpose to (r2, Br, vec_len)
62
+ vecs = vecs.permute(1, 0, 2) # (r2, Br, vec_len)
63
+
64
+ # Now flatten first two dims: (r2*Br, vec_len), then transpose to (vec_len, r2*Br)
65
+ result = vecs.reshape(r2 * Br, -1).t()
66
+
67
+ return result
68
+
69
+ # def rebuild(grad, block_size: [int, int]):
70
+ # new_matrix_rows = []
71
+ # if grad.dim() == 2: # 只处理二维梯度(矩阵)
72
+ # # 获取梯度矩阵的尺寸
73
+ # rows, cols = grad.size()
74
+ # # 遍历分块
75
+ # for j in range(0, cols, block_size[1]):
76
+ # for i in range(0, rows, block_size[0]):
77
+ # # 获取当前块
78
+ # block = grad[i:i + block_size[0], j:j + block_size[1]]
79
+ # # 如果块的大小不足,填充零
80
+ # if block.size(0) < block_size[0] or block.size(1) < block_size[1]:
81
+ # padding = (
82
+ # 0, block_size[1] - block.size(1), # 列填充
83
+ # 0, block_size[0] - block.size(0) # 行填充
84
+ # )
85
+ # block = torch.nn.functional.pad(block, padding, "constant", 0)
86
+ # # 向量化并添加到新矩阵的行中
87
+ # new_matrix_rows.append(block.T.flatten())
88
+
89
+ # # 将所有行堆叠成一个新矩阵
90
+ # if new_matrix_rows: # 如果有数据
91
+ # new_gad = torch.stack(new_matrix_rows)
92
+ # else:
93
+ # new_gad = torch.empty(0) # 如果没有梯度数据,返回空矩阵
94
+
95
+ # return new_gad
96
+
97
+ # if __name__ == "__main__":
98
+ # # 定义一个简单的模型
99
+ # class SimpleModel(torch.nn.Module):
100
+ # def __init__(self):
101
+ # super(SimpleModel, self).__init__()
102
+ # self.fc1 = torch.nn.Linear(10, 20)
103
+ # self.fc2 = torch.nn.Linear(20, 10)
104
+
105
+ # def forward(self, x):
106
+ # x = self.fc1(x)
107
+ # x = self.fc2(x)
108
+ # return x
109
+
110
+ # # 初始化模型和损失函数
111
+ # model = SimpleModel()
112
+ # criterion = torch.nn.MSELoss()
113
+ # optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
114
+
115
+ # # 模拟输入数据
116
+ # inputs = torch.randn(5, 10) # batch_size=5, input_size=10
117
+ # targets = torch.randn(5, 10) # batch_size=5, output_size=10
118
+
119
+ # # 前向传播
120
+ # outputs = model(inputs)
121
+ # loss = criterion(outputs, targets)
122
+
123
+ # # 反向传播计算梯度
124
+ # loss.backward()
125
+
126
+ # # 调用函数将梯度分块并构造新矩阵
127
+ # for param in model.parameters():
128
+ # if param.grad is not None: # 检查是否有梯度
129
+ # grad = param.grad # 获取梯度
130
+ # print("旧矩阵的内容:\n", grad)
131
+ # print("新矩阵的形状:", grad.shape)
132
+ # block_size = (2, 2) # 分块大小为 2x2
133
+ # new_matrix = rebuild(grad, block_size)
134
+ # print("新矩阵��形状:", new_matrix.shape)
135
+ # print("新矩阵的内容:\n", new_matrix)
136
+
utils.py ADDED
@@ -0,0 +1,427 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import os
3
+ import typing as tp
4
+ import numpy as np
5
+ import pandas as pd
6
+ from tqdm import tqdm
7
+ from transformers import (
8
+ AutoTokenizer,
9
+ AutoModelForCausalLM,
10
+ AutoModelForSeq2SeqLM,
11
+ Seq2SeqTrainingArguments,
12
+ Seq2SeqTrainer,
13
+ EarlyStoppingCallback,
14
+ TrainerCallback,
15
+ TrainerControl,
16
+ TrainerState,
17
+ )
18
+ from transformers.trainer_utils import PredictionOutput
19
+ from datasets import Dataset, load_dataset
20
+ from torch.utils.data import DataLoader
21
+ from transformers import AdamW, get_linear_schedule_with_warmup
22
+ from lora_plus import LoraPlusTrainingArguments, LoraPlusTrainer
23
+ from logTrainer import LogTrainer
24
+ import logging
25
+ import wandb
26
+ from peft import PeftModel
27
+ from data import load_alpaca
28
+
29
+ log = logging.getLogger(__name__)
30
+
31
+
32
+ def causalLMEncode(example, tokenizer, max_length=-1, ignore_masked_token=True):
33
+ is_list_input = isinstance(example["x"], list)
34
+ # Combine text and add EOS token
35
+ combined_text = (
36
+ [
37
+ x + " " + y + tokenizer.eos_token
38
+ for (x, y) in zip(example["x"], example["y"])
39
+ ]
40
+ if is_list_input
41
+ else example["x"] + " " + example["y"] + tokenizer.eos_token
42
+ )
43
+ # Tokenize combined text
44
+ encodings = tokenizer(
45
+ combined_text,
46
+ return_tensors="pt",
47
+ padding=True,
48
+ truncation=True,
49
+ max_length=max_length if max_length != -1 else None,
50
+ )
51
+ # Calculate input text length in tokens
52
+ input_text_length = (
53
+ [
54
+ len(tokenizer(example["x"][i], return_tensors="pt")["input_ids"][0])
55
+ for i in range(len(example["x"]))
56
+ ]
57
+ if is_list_input
58
+ else len(tokenizer(example["x"], return_tensors="pt")["input_ids"][0])
59
+ )
60
+ if input_text_length[0] >= max_length:
61
+ log.warning(
62
+ f"Input text length >= max_length: {input_text_length} >= {max_length}. "
63
+ "Consider increasing max_length to avoid truncation."
64
+ )
65
+ # Create labels
66
+ labels = encodings["input_ids"].clone()
67
+ if is_list_input:
68
+ for i, l in enumerate(input_text_length):
69
+ labels[i, :l] = -100
70
+ else:
71
+ labels[0, :input_text_length] = -100
72
+ if ignore_masked_token:
73
+ labels[encodings["attention_mask"] == 0] = -100
74
+ # Update example dictionary
75
+ results = {
76
+ "input_ids": encodings["input_ids"],
77
+ "attention_mask": encodings["attention_mask"],
78
+ "labels": labels,
79
+ # "input_text_length": input_text_length,
80
+ }
81
+
82
+ return results
83
+
84
+
85
+ def SeqToSeqEncode(example, tokenizer, max_length=None, ignore_masked_token=False):
86
+ inputs = tokenizer(
87
+ example["x"],
88
+ return_tensors="pt",
89
+ padding=True,
90
+ truncation=True,
91
+ max_length=max_length,
92
+ )
93
+ outputs = tokenizer(
94
+ example["y"],
95
+ return_tensors="pt",
96
+ padding=True,
97
+ truncation=True,
98
+ max_length=max_length,
99
+ )
100
+
101
+ results = {
102
+ "input_ids": inputs["input_ids"],
103
+ "attention_mask": inputs["attention_mask"],
104
+ "labels": outputs["input_ids"],
105
+ "decoder_attention_mask": outputs["attention_mask"],
106
+ }
107
+
108
+ if ignore_masked_token:
109
+ results["labels"][outputs["attention_mask"] == 0] = -100
110
+
111
+ return results
112
+
113
+
114
+ def preprocess_dataset(
115
+ dataset: tp.Union[Dataset, tp.List[tp.Tuple[str, str]], tp.List[tp.Dict[str, str]]]
116
+ ) -> Dataset:
117
+ if isinstance(dataset, list) and isinstance(dataset[0], tuple):
118
+ dataset = Dataset.from_pandas(pd.DataFrame(dataset, columns=["x", "y"]))
119
+ elif isinstance(dataset, list) and isinstance(dataset[0], dict):
120
+ dataset = Dataset.from_dict(
121
+ {k: [dic[k] for dic in dataset] for k in dataset[0]}
122
+ )
123
+ elif isinstance(dataset, dict):
124
+ dataset = Dataset.from_dict(dataset)
125
+ elif isinstance(dataset, Dataset):
126
+ pass
127
+ else:
128
+ raise ValueError("Wrong format")
129
+ return dataset
130
+
131
+
132
+ def initialize_text_to_text_model(
133
+ model_name: str,
134
+ model_type: str,
135
+ bf16: bool,
136
+ use_peft: bool = True,
137
+ tokenizer: str = None,
138
+ flash_attention: bool = False,
139
+ ):
140
+ if model_type == "CausalLM":
141
+ if flash_attention:
142
+ log.info("Using flash attention 2")
143
+ model = AutoModelForCausalLM.from_pretrained(
144
+ model_name,
145
+ trust_remote_code=True,
146
+ torch_dtype=torch.bfloat16 if bf16 else torch.float32,
147
+ device_map="auto" if use_peft else None,
148
+ attn_implementation="flash_attention_2",
149
+ )
150
+ else:
151
+ model = AutoModelForCausalLM.from_pretrained(
152
+ model_name,
153
+ trust_remote_code=True,
154
+ torch_dtype=torch.bfloat16 if bf16 else torch.float32,
155
+ device_map="auto" if use_peft else None,
156
+ )
157
+ elif model_type == "ConditionalGeneration":
158
+ model = AutoModelForSeq2SeqLM.from_pretrained(
159
+ model_name,
160
+ torch_dtype=torch.bfloat16 if bf16 else torch.float32,
161
+ device_map="auto" if use_peft else None,
162
+ )
163
+ if tokenizer:
164
+ tokenizer = AutoTokenizer.from_pretrained(tokenizer)
165
+ else:
166
+ tokenizer = AutoTokenizer.from_pretrained(model_name)
167
+ if tokenizer.eos_token is None:
168
+ tokenizer.add_special_tokens({"eos_token": "<|endoftext|>"})
169
+ model.resize_token_embeddings(len(tokenizer))
170
+ if tokenizer.pad_token is None:
171
+ tokenizer.pad_token = tokenizer.eos_token
172
+ return model, tokenizer
173
+
174
+
175
+ def compute_metrics(p: PredictionOutput):
176
+ predictions = p.predictions
177
+ label_ids = p.label_ids # shape (batch_size, seq_len)
178
+ if False:
179
+ # Hard metric: the model must output exactly the same as the target
180
+ # This should be the default evaluation metric for most tasks
181
+ pred = np.argmax(predictions[0], axis=-1)
182
+ num_correct = sum([np.array_equal(pred[i], label_ids[i]) for i in range(len(pred))])
183
+ accuracy = num_correct / len(pred)
184
+ else:
185
+ # Soft metric: we limit the output space to the target space
186
+ # i.e. the model classify the one with higher prob in positive and negative
187
+ # **Use it in cola and mrpc, because it's too hard for vanilla lora**
188
+ # Only suit for the binary classification with each label of 1 token
189
+ label_ids = label_ids[:, 0] # remove the eos token
190
+ unique_labels = np.unique(label_ids)
191
+ flipped_labels = np.ones_like(label_ids) * unique_labels.sum() - label_ids
192
+ predictions = predictions[0][:, 0, :] # remove the eos token # seq_len, tokens
193
+ label_prob = predictions[np.arange(len(predictions)), label_ids]
194
+ flipped_label_prob = predictions[np.arange(len(predictions)), flipped_labels]
195
+ num_correct = sum(label_prob > flipped_label_prob)
196
+ accuracy = num_correct / len(label_prob)
197
+
198
+ return {"accuracy": accuracy}
199
+
200
+
201
+ def transform_dataset(model_type, tokenizer, dataset, max_length):
202
+ if model_type == "CausalLM":
203
+ dataset.set_transform(lambda x: causalLMEncode(x, tokenizer, max_length))
204
+ elif model_type == "ConditionalGeneration":
205
+ dataset.set_transform(lambda x: SeqToSeqEncode(x, tokenizer, max_length))
206
+ else:
207
+ raise ValueError("Wrong model type")
208
+ return dataset
209
+
210
+
211
+ def train_text_to_text_model(
212
+ run_name: str,
213
+ train_dataset: Dataset,
214
+ valid_dataset: Dataset,
215
+ model: torch.nn.Module,
216
+ tokenizer: AutoTokenizer,
217
+ model_type: str,
218
+ per_device_batch_size: int = 1,
219
+ real_batch_size: int = 32,
220
+ max_length: int = None,
221
+ **kwargs,
222
+ ) -> torch.nn.Module:
223
+ # Preprocess the dataset
224
+ train_dataset = preprocess_dataset(train_dataset)
225
+ valid_dataset = preprocess_dataset(valid_dataset)
226
+
227
+ assert (
228
+ real_batch_size % per_device_batch_size == 0
229
+ ), "real_batch_size must be divisible by per_device_batch_size"
230
+ accu_step = real_batch_size // per_device_batch_size
231
+
232
+ train_dataset, valid_dataset = transform_dataset(
233
+ model_type, tokenizer, train_dataset, max_length
234
+ ), transform_dataset(model_type, tokenizer, valid_dataset, max_length)
235
+
236
+ eval_steps = (
237
+ int(len(train_dataset) * kwargs.get("eval_epochs", 1)) // real_batch_size
238
+ )
239
+ # Special for lorqplus
240
+ use_loraplus = kwargs.get("use_loraplus", False)
241
+ TrainingArgumentsClass = (
242
+ LoraPlusTrainingArguments if use_loraplus else Seq2SeqTrainingArguments
243
+ )
244
+ TrainerClass = LoraPlusTrainer if use_loraplus else LogTrainer
245
+ if use_loraplus:
246
+ additional_kwargs = {
247
+ "loraplus_lr_ratio": kwargs.get("loraplus_lr_ratio", 1.0),
248
+ }
249
+ log.info(
250
+ f"Begin training using LoraPlusTrainer with additional kwargs: {additional_kwargs}"
251
+ )
252
+ else:
253
+ additional_kwargs = {}
254
+ log.info("Begin training using Seq2SeqTrainer")
255
+
256
+ # Training arguments
257
+ output_dir = f"./results/{run_name}/{kwargs.get('seed')}"
258
+ training_args = TrainingArgumentsClass(
259
+ output_dir=output_dir, # output directory
260
+ num_train_epochs=kwargs.get(
261
+ "num_train_epochs", 3
262
+ ), # total number of training epochs
263
+ per_device_train_batch_size=per_device_batch_size,
264
+ per_device_eval_batch_size=per_device_batch_size,
265
+ gradient_accumulation_steps=accu_step,
266
+ logging_dir="./logs", # directory for storing logs
267
+ logging_steps=kwargs.get("logging_steps", 10), # when to print log
268
+ bf16=kwargs.get("bf16", False),
269
+ gradient_checkpointing=kwargs.get("gradient_checkpointing", False),
270
+ optim=kwargs.get("optim", "adamw_torch"),
271
+ evaluation_strategy="no",
272
+ eval_steps=eval_steps,
273
+ save_steps=eval_steps,
274
+ save_strategy="steps",
275
+ save_total_limit=1, # No need for saving
276
+ load_best_model_at_end=False,
277
+ metric_for_best_model=kwargs.get("metric_for_best_model", "eval_loss"),
278
+ greater_is_better=kwargs.get("greater_is_better", False),
279
+ do_eval=False,
280
+ learning_rate=kwargs.get("learning_rate", 5e-5),
281
+ remove_unused_columns=False, # We tokenize the dataset on the fly
282
+ eval_accumulation_steps=kwargs.get("eval_accumulation_steps", real_batch_size),
283
+ label_names=[
284
+ "labels"
285
+ ], # Peft are not compatible with HF's default label names yet
286
+ # Ref: https://discuss.huggingface.co/t/eval-with-trainer-not-running-with-peft-lora-model/53286
287
+ # weight_decay = 0, # No weight decay
288
+ weight_decay = 5e-4,
289
+ warmup_ratio = 0.03,
290
+ lr_scheduler_type = "cosine",
291
+ seed = kwargs.get("seed", 42),
292
+ **additional_kwargs,
293
+ )
294
+
295
+ trainer = TrainerClass(
296
+ model=model,
297
+ args=training_args,
298
+ train_dataset=train_dataset,
299
+ eval_dataset=valid_dataset,
300
+ compute_metrics=compute_metrics if "llama" not in run_name else None,
301
+ # callbacks=[
302
+ # EarlyStoppingCallback(
303
+ # early_stopping_patience=kwargs.get("early_stopping_patience", 1)
304
+ # ),
305
+ # ],
306
+ )
307
+
308
+ trainer.train()
309
+ # eval_results = trainer.evaluate()
310
+ # eval_accuracy = eval_results.get("eval_accuracy", 0)
311
+ # print(f"FINAL_EVAL_ACCURACY: {eval_accuracy:.4f}")
312
+ return model
313
+
314
+
315
+ def model_inference(
316
+ model: torch.nn.Module,
317
+ tokenizer: AutoTokenizer,
318
+ input_text: str,
319
+ model_type: str,
320
+ max_source_length: str = 768,
321
+ max_target_length: str = 256,
322
+ ):
323
+ if model_type == "CausalLM":
324
+ inputs = tokenizer(
325
+ input_text + " ",
326
+ return_tensors="pt",
327
+ max_length=max_source_length,
328
+ truncation=True,
329
+ return_token_type_ids=False,
330
+ )
331
+ inputs = {k: v.cuda() for k, v in inputs.items()}
332
+ with torch.no_grad():
333
+ outputs = model.generate(
334
+ **inputs,
335
+ return_dict_in_generate=True,
336
+ output_scores=False,
337
+ max_new_tokens=max_target_length,
338
+ eos_token_id=tokenizer.eos_token_id,
339
+ top_p=0.95,
340
+ temperature=0.8,
341
+ )
342
+ pred_text = tokenizer.decode(
343
+ outputs.sequences[0][len(inputs["input_ids"][0]) :],
344
+ skip_special_tokens=True,
345
+ )
346
+ elif model_type == "ConditionalGeneration":
347
+ inputs = tokenizer(input_text, return_tensors="pt").to("cuda")
348
+ with torch.no_grad():
349
+ outputs = model.generate(**inputs, max_new_tokens=max_target_length)
350
+ pred_text = tokenizer.decode(outputs[0], skip_special_tokens=True)
351
+
352
+ return pred_text
353
+
354
+
355
+ def load_peft_model(model, peft_path: str):
356
+ peft_paths = [f"{peft_path}/{i}" for i in os.listdir(peft_path) if "merge" not in i]
357
+ for peft_path in peft_paths:
358
+ print(f"loading and merging from {peft_path}")
359
+ model: PeftModel = PeftModel.from_pretrained(model, peft_path)
360
+ model = model.merge_and_unload()
361
+ return model
362
+
363
+
364
+ def test_train():
365
+ # Example usage using emo dataset
366
+ dataset = load_dataset("emo")
367
+ label_map = {0: "others", 1: "happy", 2: "sad", 3: "angry"}
368
+ dataset = dataset.map(lambda e: {"x": e["text"], "y": label_map[e["label"]]})
369
+ train_set = dataset["train"]
370
+ test_set = dataset["test"]
371
+
372
+ model_name = "t5-small"
373
+ model_type = "ConditionalGeneration"
374
+ model, tokenizer = initialize_text_to_text_model(model_name, model_type)
375
+
376
+ model = train_text_to_text_model(
377
+ train_set,
378
+ test_set,
379
+ model,
380
+ tokenizer,
381
+ model_type,
382
+ num_train_epochs=1,
383
+ per_device_batch_size=64,
384
+ real_batch_size=64,
385
+ )
386
+ # Use the model for inference in the testset, print the first 10 examples
387
+ for i in range(10):
388
+ print("Input:", test_set[i]["x"])
389
+ print("Target:", test_set[i]["y"])
390
+ print(
391
+ "Prediction:",
392
+ model_inference(model, tokenizer, test_set[i]["x"], model_type),
393
+ )
394
+ print()
395
+
396
+
397
+ def test_llama_alpaca():
398
+ model_name = "meta-llama/Llama-2-7b-hf"
399
+ model_type = "CausalLM"
400
+ peft_path = "results/llama-alpaca_alpaca/gradient-ArB2r-adam/0"
401
+ model, tokenizer = initialize_text_to_text_model(model_name, model_type, True)
402
+ model = load_peft_model(model, peft_path)
403
+ _, _, test_set = load_alpaca()
404
+ for i in range(10):
405
+ print("Input:", test_set[i]["x"])
406
+ # print("Target:", test_set[i]["y"])
407
+ print(
408
+ "Prediction:",
409
+ model_inference(model, tokenizer, test_set[i]["x"], model_type),
410
+ )
411
+ print()
412
+
413
+
414
+ def merge_llama(peft_path):
415
+ model_name = "meta-llama/Llama-2-7b-hf"
416
+ model_type = "CausalLM"
417
+ model, tokenizer = initialize_text_to_text_model(model_name, model_type, True)
418
+ model = load_peft_model(model, peft_path)
419
+ print("Save model to ", os.path.join(peft_path, "merged_checkpoint"))
420
+ model.save_pretrained(os.path.join(peft_path, "merged_checkpoint"))
421
+ tokenizer.save_pretrained(os.path.join(peft_path, "merged_checkpoint"))
422
+ del model, tokenizer
423
+
424
+
425
+ if __name__ == "__main__":
426
+ merge_llama("results/llama-alpaca_alpaca/default/0")
427
+ # merge_llama("results/llama-alpaca_alpaca/gradient-ArB2r-adam/0")