mirror of
https://github.com/nomic-ai/gpt4all.git
synced 2026-07-17 10:58:08 +00:00
Compare commits
42 Commits
mosaic
...
accel_eval
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a7d4bdb7ea | ||
|
|
506b0511d1 | ||
|
|
1eeaa5c8ee | ||
|
|
846f4cdf84 | ||
|
|
c62312f82e | ||
|
|
cec1fda6ec | ||
|
|
8e7ce1f7c7 | ||
|
|
9ac9de7e0a | ||
|
|
6f89d8a2aa | ||
|
|
f07b1362ad | ||
|
|
d9a678dd3d | ||
|
|
1a451445a2 | ||
|
|
78321adf45 | ||
|
|
1d5f6af634 | ||
|
|
67e19bccb0 | ||
|
|
6524fec7ff | ||
|
|
e1357c3720 | ||
|
|
b8f39c5104 | ||
|
|
8cbc63b017 | ||
|
|
06ad467b7d | ||
|
|
16ce7396c1 | ||
|
|
d1bb2aac29 | ||
|
|
b9861f7510 | ||
|
|
bc7eb80e02 | ||
|
|
0429db0244 | ||
|
|
210bf3c9cf | ||
|
|
5556de9152 | ||
|
|
708e0b486d | ||
|
|
ad782620ac | ||
|
|
e8c6aeeea2 | ||
|
|
987c694afa | ||
|
|
822fa0c47a | ||
|
|
d8921f835d | ||
|
|
54932a51cb | ||
|
|
cfcb101443 | ||
|
|
73dbd34310 | ||
|
|
03b9e2004e | ||
|
|
98ae021ea6 | ||
|
|
147094e892 | ||
|
|
fa91e2b980 | ||
|
|
ed35d1fbb0 | ||
|
|
2db43570ae |
11
.gitignore
vendored
11
.gitignore
vendored
@@ -1,6 +1,4 @@
|
||||
*.pkl
|
||||
ckpts*
|
||||
.deepspeed_env
|
||||
eval_data/*.pkl
|
||||
*.jsonl
|
||||
*tar.gz
|
||||
ckpts**
|
||||
@@ -164,4 +162,9 @@ cython_debug/
|
||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#.idea/
|
||||
#.idea/
|
||||
|
||||
|
||||
# vs code
|
||||
.vscode
|
||||
*.bin
|
||||
106
README.md
106
README.md
@@ -4,8 +4,14 @@
|
||||
<p align="center">
|
||||
<a href="https://s3.amazonaws.com/static.nomic.ai/gpt4all/2023_GPT4All_Technical_Report.pdf">:green_book: Technical Report</a>
|
||||
</p>
|
||||
|
||||
|
||||
<p align="center">
|
||||
<a href="https://discord.gg/kvmy6dQB">Discord</a>
|
||||
<a href="https://github.com/nomic-ai/pyllamacpp">:snake: Official Python Bindings</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://discord.gg/mGZE39AS3e">Discord</a>
|
||||
</p>
|
||||
|
||||
|
||||
@@ -16,20 +22,83 @@ Run on M1 Mac (not sped up!)
|
||||
|
||||
# Try it yourself
|
||||
|
||||
Download the CPU quantized gpt4all model checkpoint: [gpt4all-lora-quantized.bin](https://the-eye.eu/public/AI/models/nomic-ai/gpt4all/gpt4all-lora-quantized.bin).
|
||||
Here's how to get started with the CPU quantized GPT4All model checkpoint:
|
||||
|
||||
1. Download the `gpt4all-lora-quantized.bin` file from [Direct Link](https://the-eye.eu/public/AI/models/nomic-ai/gpt4all/gpt4all-lora-quantized.bin) or [[Torrent-Magnet]](https://tinyurl.com/gpt4all-lora-quantized).
|
||||
2. Clone this repository, navigate to `chat`, and place the downloaded file there.
|
||||
3. Run the appropriate command for your OS:
|
||||
- M1 Mac/OSX: `cd chat;./gpt4all-lora-quantized-OSX-m1`
|
||||
- Linux: `cd chat;./gpt4all-lora-quantized-linux-x86`
|
||||
- Windows (PowerShell): `cd chat;./gpt4all-lora-quantized-win64.exe`
|
||||
- Intel Mac/OSX: `cd chat;./gpt4all-lora-quantized-OSX-intel`
|
||||
|
||||
Clone this repository down and place the quantized model in the `chat` directory and start chatting by running:
|
||||
For custom hardware compilation, see our [llama.cpp](https://github.com/zanussbaum/gpt4all.cpp) fork.
|
||||
|
||||
- `cd chat;./gpt4all-lora-quantized-OSX-m1` on M1 Mac/OSX
|
||||
- `cd chat;./gpt4all-lora-quantized-linux-x86` on Linux
|
||||
- `cd chat;./gpt4all-lora-quantized-win64.exe` on Windows (PowerShell)
|
||||
- `cd chat;./gpt4all-lora-quantized-OSX-intel` on Intel Mac/OSX
|
||||
-----------
|
||||
|
||||
To compile for custom hardware, see our fork of the [Alpaca C++](https://github.com/zanussbaum/gpt4all.cpp) repo.
|
||||
[Secret Unfiltered Checkpoint](https://the-eye.eu/public/AI/models/nomic-ai/gpt4all/gpt4all-lora-unfiltered-quantized.bin) - [[Torrent]](https://the-eye.eu/public/AI/models/nomic-ai/gpt4all/gpt4all-lora-unfiltered-quantized.bin.torrent)
|
||||
|
||||
This model had all refusal to answer responses removed from training. Try it with:
|
||||
- `cd chat;./gpt4all-lora-quantized-OSX-m1 -m gpt4all-lora-unfiltered-quantized.bin`
|
||||
|
||||
-----------
|
||||
Note: the full model on GPU (16GB of RAM required) performs much better in our qualitative evaluations.
|
||||
|
||||
# Python Client
|
||||
## CPU Interface
|
||||
To run GPT4all in python, see the new [official Python bindings](https://github.com/nomic-ai/pyllamacpp).
|
||||
|
||||
The old bindings are still available but now deprecated. They will not work in a notebook environment.
|
||||
To get running using the python client with the CPU interface, first install the [nomic client](https://github.com/nomic-ai/nomic) using `pip install nomic`
|
||||
Then, you can use the following script to interact with GPT4All:
|
||||
```
|
||||
from nomic.gpt4all import GPT4All
|
||||
m = GPT4All()
|
||||
m.open()
|
||||
m.prompt('write me a story about a lonely computer')
|
||||
```
|
||||
|
||||
## GPU Interface
|
||||
There are two ways to get up and running with this model on GPU.
|
||||
The setup here is slightly more involved than the CPU model.
|
||||
1. clone the nomic client [repo](https://github.com/nomic-ai/nomic) and run `pip install .[GPT4All]` in the home dir.
|
||||
2. run `pip install nomic` and install the additional deps from the wheels built [here](https://github.com/nomic-ai/nomic/tree/main/bin)
|
||||
|
||||
Once this is done, you can run the model on GPU with a script like the following:
|
||||
```
|
||||
from nomic.gpt4all import GPT4AllGPU
|
||||
m = GPT4AllGPU(LLAMA_PATH)
|
||||
config = {'num_beams': 2,
|
||||
'min_new_tokens': 10,
|
||||
'max_length': 100,
|
||||
'repetition_penalty': 2.0}
|
||||
out = m.generate('write me a story about a lonely computer', config)
|
||||
print(out)
|
||||
```
|
||||
Where LLAMA_PATH is the path to a Huggingface Automodel compliant LLAMA model.
|
||||
Nomic is unable to distribute this file at this time.
|
||||
We are working on a GPT4All that does not have this limitation right now.
|
||||
|
||||
You can pass any of the [huggingface generation config params](https://huggingface.co/docs/transformers/main_classes/text_generation#transformers.GenerationConfig) in the config.
|
||||
|
||||
# Roadmap
|
||||
## Short Term
|
||||
- <span style="color:green">(IN PROGRESS)</span> Train a GPT4All model based on GPTJ to alleviate llama distribution issues.
|
||||
- <span style="color:green">(IN PROGRESS)</span> Create improved CPU and GPU interfaces for this model.
|
||||
- <span style="color:red">(NOT STARTED)</span> Integrate llama.cpp bindings
|
||||
- <span style="color:red">(NOT STARTED)</span> Create a good conversational chat interface for the model.
|
||||
- <span style="color:red">(NOT STARTED)</span> Allow users to opt in and submit their chats for subsequent training runs
|
||||
|
||||
## Medium Term
|
||||
- <span style="color:red">(NOT STARTED)</span> Integrate GPT4All with [Atlas](https://atlas.nomic.ai) to allow for document retrieval.
|
||||
- BLOCKED by GPT4All based on GPTJ
|
||||
- <span style="color:red">(NOT STARTED)</span> Integrate GPT4All with Langchain.
|
||||
- <span style="color:green">(IN PROGRESS)</span> Build easy custom training scripts to allow users to fine tune models.
|
||||
|
||||
## Long Term
|
||||
- <span style="color:red">(NOT STARTED)</span> Allow anyone to curate training data for subsequent GPT4All releases using Atlas.
|
||||
- <span style="color:green">(IN PROGRESS)</span> Democratize AI.
|
||||
|
||||
# Reproducibility
|
||||
|
||||
Trained LoRa Weights:
|
||||
@@ -37,9 +106,9 @@ Trained LoRa Weights:
|
||||
- gpt4all-lora-epoch-2 (three full epochs of training) https://huggingface.co/nomic-ai/gpt4all-lora-epoch-2
|
||||
|
||||
Raw Data:
|
||||
- [Training Data Without P3](https://s3.amazonaws.com/static.nomic.ai/gpt4all/2022_03_27/gpt4all_curated_data_without_p3_2022_03_27.tar.gz)
|
||||
- [Training Data Without P3](https://huggingface.co/datasets/nomic-ai/gpt4all_prompt_generations)
|
||||
- Explorer: https://atlas.nomic.ai/map/gpt4all_data_clean_without_p3
|
||||
- [Full Dataset with P3](https://s3.amazonaws.com/static.nomic.ai/gpt4all/2022_03_27/gpt4all_curated_data_full_2022_03_27.tar.gz)
|
||||
- [Full Dataset with P3](https://huggingface.co/datasets/nomic-ai/gpt4all_prompt_generations_with_p3)
|
||||
- Explorer: https://atlas.nomic.ai/map/gpt4all_data_clean
|
||||
|
||||
We are not distributing a LLaMa 7B checkpoint.
|
||||
@@ -50,9 +119,10 @@ You can reproduce our trained model by doing the following:
|
||||
|
||||
Clone the repo
|
||||
|
||||
`git clone --recurse-submodules https://github.com/nomic-ai/gpt4all.git`
|
||||
|
||||
`git submodule configure && git submodule update`
|
||||
```
|
||||
git clone --recurse-submodules https://github.com/nomic-ai/gpt4all.git
|
||||
git submodule update --init
|
||||
```
|
||||
|
||||
Setup the environment
|
||||
|
||||
@@ -78,6 +148,10 @@ accelerate launch --dynamo_backend=inductor --num_processes=8 --num_machines=1 -
|
||||
python generate.py --config configs/generate/generate.yaml --prompt "Write a script to reverse a string in Python"
|
||||
```
|
||||
|
||||
## Need Help?
|
||||
|
||||
Join the <a href="https://discord.gg/kvmy6dQB"> Discord </a> and ask for help in `#gpt4all-help`
|
||||
|
||||
# Sample Generations
|
||||
|
||||
### Provide instructions for the given exercise. Leg Raises
|
||||
@@ -147,7 +221,7 @@ python generate.py --config configs/generate/generate.yaml --prompt "Write a scr
|
||||
### What is a three word topic describing the following keywords: baseball, football, soccer:
|
||||
>Sports, athletics, games
|
||||
|
||||
|
||||
## Citation
|
||||
|
||||
If you utilize this reposistory, models or data in a downstream project, please consider citing it with:
|
||||
```
|
||||
@@ -160,7 +234,3 @@ If you utilize this reposistory, models or data in a downstream project, please
|
||||
howpublished = {\url{https://github.com/nomic-ai/gpt4all}},
|
||||
}
|
||||
```
|
||||
|
||||
### Alternative Download Locations
|
||||
#### gpt4all-lora-quantized.bin Backup Torrent Link
|
||||
magnet:?xt=urn:btih:1F11A9691EE06C18F0040E359361DCA0479BCB5A&dn=gpt4all-lora-quantized.bin&tr=udp%3A%2F%2Ftracker.opentrackr.org%3A1337%2Fannounce&tr=udp%3A%2F%2Fopentracker.i2p.rocks%3A6969%2Fannounce
|
||||
|
||||
@@ -160,7 +160,7 @@ We realized that we had two bugs however:
|
||||
- We accidentally duplicated data and effectively trained for 2 epochs instead of 1
|
||||
- We added an eos token to every sequence, even those that we truncated (e.g. long code that exceeds the 1024).
|
||||
|
||||
## Conditonal EOS and 1 Epoch
|
||||
## Conditional EOS and 1 Epoch
|
||||
|
||||
Using the same parameters, we then trained a model using a "conditional" eos token where we only add an `eos` when the inputs are less than the maximum sequence length for one epoch.
|
||||
|
||||
|
||||
1
clean.py
1
clean.py
@@ -64,7 +64,6 @@ for file in glob.glob(os.path.join(prompt_generation_dir, "*.jsonl")):
|
||||
df = df.dropna(subset=['prompt', 'response'])
|
||||
df = df[df['prompt'] != '']
|
||||
df = df[df['response'] != '']
|
||||
df = df[df["prompt"].str.len() > 1]
|
||||
curr_len = len(df)
|
||||
|
||||
print(f"Removed {prev_len - curr_len} rows")
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
{
|
||||
"train_batch_size": "auto",
|
||||
"gradient_accumulation_steps": "auto",
|
||||
"train_micro_batch_size_per_gpu": "auto",
|
||||
"fp16": {
|
||||
"enabled": "auto",
|
||||
"min_loss_scale": 1,
|
||||
"loss_scale_window": 1000,
|
||||
"hysteresis": 2,
|
||||
"initial_scale_power": 32
|
||||
},
|
||||
"bf16": {
|
||||
"enabled": "auto"
|
||||
},
|
||||
"gradient_clipping": 1.0,
|
||||
"zero_optimization": {
|
||||
"stage": 2,
|
||||
"offload_param": {
|
||||
"device": "none"
|
||||
},
|
||||
"offload_optimizer": {
|
||||
"device": "none"
|
||||
},
|
||||
"allgather_partitions": true,
|
||||
"allgather_bucket_size": 5e8,
|
||||
"contiguous_gradients": true
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
{
|
||||
"train_batch_size": "auto",
|
||||
"gradient_accumulation_steps": "auto",
|
||||
"train_micro_batch_size_per_gpu": "auto",
|
||||
"fp16": {
|
||||
"enabled": "auto",
|
||||
"min_loss_scale": 1,
|
||||
"loss_scale_window": 1000,
|
||||
"hysteresis": 2,
|
||||
"initial_scale_power": 32
|
||||
},
|
||||
"bf16": {
|
||||
"enabled": "auto"
|
||||
},
|
||||
"gradient_clipping": 1,
|
||||
"zero_optimization": {
|
||||
"stage": 2,
|
||||
"offload_param": {
|
||||
"device": "cpu"
|
||||
},
|
||||
"offload_optimizer": {
|
||||
"device": "cpu"
|
||||
},
|
||||
"allgather_partitions": true,
|
||||
"allgather_bucket_size": 5e8,
|
||||
"contiguous_gradients": true
|
||||
},
|
||||
"optimizer": {
|
||||
"type": "AdamW",
|
||||
"params": {
|
||||
"lr": "auto",
|
||||
"betas": [
|
||||
0.9,
|
||||
0.999
|
||||
],
|
||||
"eps": 1e-08
|
||||
}
|
||||
},
|
||||
"scheduler": {
|
||||
"type": "WarmupLR",
|
||||
"params": {
|
||||
"warmup_min_lr": 0,
|
||||
"warmup_max_lr": "auto",
|
||||
"warmup_num_steps": "auto",
|
||||
"warmup_type": "linear"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
# model/tokenizer
|
||||
model_name: # update with llama 7b
|
||||
tokenizer_name: # update with llama 7b
|
||||
lora: true
|
||||
lora_path: "nomic-ai/gpt4all-lora"
|
||||
|
||||
max_new_tokens: 512
|
||||
temperature: 0.001
|
||||
prompt: |
|
||||
#this code prints a string reversed
|
||||
my_string = "hello how are you"
|
||||
print(len(my_string))
|
||||
|
||||
|
||||
My code above does not work. Can you help me?
|
||||
@@ -1,11 +1,13 @@
|
||||
# model/tokenizer
|
||||
model_name: # update with llama model name
|
||||
tokenizer_name: # update with llama model name
|
||||
model_name: "zpn/llama-7b"
|
||||
tokenizer_name: "zpn/llama-7b"
|
||||
lora: true
|
||||
lora_path: "tloen/alpaca-lora-7b"
|
||||
|
||||
|
||||
|
||||
stride: 512
|
||||
num_proc: 64
|
||||
batch_size: 1
|
||||
max_new_tokens: 512
|
||||
temperature: 0.001
|
||||
prompt: |
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
# model/tokenizer
|
||||
model_name: # update
|
||||
tokenizer_name: # update
|
||||
lora_path: "no-lora"
|
||||
|
||||
max_new_tokens: 512
|
||||
temperature: 0.001
|
||||
prompt: |
|
||||
#this code prints a string reversed
|
||||
my_string = "hello how are you"
|
||||
print(len(my_string))
|
||||
|
||||
|
||||
My code above does not work. Can you help me?
|
||||
9
configs/eval/generate_gpt4all.yaml
Normal file
9
configs/eval/generate_gpt4all.yaml
Normal file
@@ -0,0 +1,9 @@
|
||||
# model/tokenizer
|
||||
model_name: "nomic-ai/gpt4all-gptj-multinode-deepspeed-epoch_0"
|
||||
tokenizer_name: "EleutherAI/gpt-j-6B"
|
||||
lora: false
|
||||
lora_path: null
|
||||
|
||||
stride: 512
|
||||
num_proc: 64
|
||||
batch_size: 1
|
||||
9
configs/eval/generate_gpt4all_lora.yaml
Normal file
9
configs/eval/generate_gpt4all_lora.yaml
Normal file
@@ -0,0 +1,9 @@
|
||||
# model/tokenizer
|
||||
model_name: "zpn/llama-7b"
|
||||
tokenizer_name: "zpn/llama-7b"
|
||||
lora: true
|
||||
lora_path: "nomic-ai/gpt4all-lora"
|
||||
|
||||
stride: 512
|
||||
num_proc: 64
|
||||
batch_size: 1
|
||||
@@ -1,15 +0,0 @@
|
||||
# model/tokenizer
|
||||
model_name: # update
|
||||
tokenizer_name: # update
|
||||
lora: true
|
||||
lora_path: # update
|
||||
|
||||
max_new_tokens: 512
|
||||
temperature: 0.001
|
||||
prompt: |
|
||||
#this code prints a string reversed
|
||||
my_string = "hello how are you"
|
||||
print(len(my_string))
|
||||
|
||||
|
||||
My code above does not work. Can you help me?
|
||||
@@ -1,15 +0,0 @@
|
||||
# model/tokenizer
|
||||
model_name: # update
|
||||
tokenizer_name: # update
|
||||
lora: true
|
||||
lora_path: # update
|
||||
|
||||
max_new_tokens: 512
|
||||
temperature: 0.001
|
||||
prompt: |
|
||||
#this code prints a string reversed
|
||||
my_string = "hello how are you"
|
||||
print(len(my_string))
|
||||
|
||||
|
||||
My code above does not work. Can you help me?
|
||||
@@ -1,14 +0,0 @@
|
||||
# model/tokenizer
|
||||
model_name: "nomic-ai/gpt4all-gptj-multinode-deepspeed-finetuned-epoch_0"
|
||||
tokenizer_name: "EleutherAI/gpt-j-6B"
|
||||
|
||||
# dataset
|
||||
streaming: false
|
||||
num_proc: 64
|
||||
dataset_path: "data_multiplus"
|
||||
max_length: 1024
|
||||
batch_size: 32
|
||||
|
||||
# logging
|
||||
seed: 42
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
# model/tokenizer
|
||||
model_name: "EleutherAI/gpt-j-6B"
|
||||
tokenizer_name: "EleutherAI/gpt-j-6B"
|
||||
gradient_checkpointing: true
|
||||
save_name: "nomic-ai/gpt4all-mosaic"
|
||||
|
||||
# dataset
|
||||
streaming: false
|
||||
num_proc: 64
|
||||
dataset_path: "nomic-ai/turbo-500k-multi"
|
||||
max_length: 1024
|
||||
batch_size: 8
|
||||
|
||||
# train dynamics
|
||||
lr: 2.0e-5
|
||||
min_lr: 0
|
||||
weight_decay: 0.0
|
||||
eval_every: 500
|
||||
eval_steps: 105
|
||||
save_every: 500
|
||||
log_grads_every: 500
|
||||
output_dir: "ckpts/gpt4all-gptj-multinode"
|
||||
checkpoint: null
|
||||
lora: false
|
||||
warmup_steps: 500
|
||||
num_epochs: 2
|
||||
|
||||
# logging
|
||||
wandb: true
|
||||
wandb_entity: vicuna
|
||||
wandb_project_name: vicuna
|
||||
seed: 42
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
# model/tokenizer
|
||||
model_name: "EleutherAI/gpt-j-6b"
|
||||
tokenizer_name: "EleutherAI/gpt-j-6b"
|
||||
gradient_checkpointing: false
|
||||
save_name: "nomic-ai/gpt4all-mosaic"
|
||||
|
||||
# dataset
|
||||
streaming: false
|
||||
num_proc: 64
|
||||
dataset_path: "nomic-ai/turbo-500k-multi"
|
||||
max_length: 1024
|
||||
batch_size: 2
|
||||
|
||||
# train dynamics
|
||||
lr: 2.0e-5
|
||||
min_lr: 0
|
||||
weight_decay: 0.0
|
||||
eval_every: 500
|
||||
eval_steps: 105
|
||||
save_every: 500
|
||||
log_grads_every: 500
|
||||
output_dir: "ckpts/gpt4all-gptj-multinode"
|
||||
checkpoint: null
|
||||
lora: true
|
||||
warmup_steps: 500
|
||||
num_epochs: 2
|
||||
|
||||
# logging
|
||||
wandb: true
|
||||
wandb_entity: zanussbaum
|
||||
wandb_project_name: mosaic
|
||||
seed: 42
|
||||
|
||||
@@ -2,19 +2,17 @@
|
||||
model_name: # update
|
||||
tokenizer_name: # update
|
||||
gradient_checkpointing: false
|
||||
save_name: "nomic-ai/gpt4all-lora-llama"
|
||||
save_name: "nomic-ai/gpt4all-lora-multi-turn"
|
||||
|
||||
# dataset
|
||||
streaming: false
|
||||
num_proc: 64
|
||||
dataset_path: "nomic-ai/turbo-500k-multi"
|
||||
dataset_path: "data_multiturn"
|
||||
max_length: 1024
|
||||
batch_size: 4
|
||||
|
||||
# train dynamics
|
||||
lr: 5.0e-5
|
||||
min_lr: 0
|
||||
weight_decay: 0.0
|
||||
eval_every: 2000
|
||||
eval_steps: 100
|
||||
save_every: 2000
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
export WORKER_IP=$1
|
||||
N_GPUS=8
|
||||
# create dir if doesn't exist
|
||||
sudo mkdir -p /job
|
||||
printf "localhost slots=$N_GPUS\n$WORKER_IP slots=$N_GPUS" | sudo tee /job/hostfile
|
||||
echo /job/hostfile
|
||||
117
data.py
117
data.py
@@ -9,49 +9,44 @@ from transformers import DefaultDataCollator
|
||||
|
||||
def tokenize_inputs(config, tokenizer, examples):
|
||||
max_length = config["max_length"]
|
||||
input_ids = torch.full((len(examples["prompt"]), max_length), tokenizer.pad_token_id)
|
||||
# ignore bos
|
||||
newline_tokens = tokenizer("\n", return_tensors="pt")["input_ids"][0, 1:]
|
||||
|
||||
# hacky backward compatible
|
||||
different_eos = tokenizer.eos_token != "</s>"
|
||||
out = {"labels": [], "input_ids": []}
|
||||
for prompt, response in zip(examples["prompt"], examples["response"]):
|
||||
if different_eos:
|
||||
if response.count("</s>") > 0:
|
||||
response = response.replace("</s>", tokenizer.eos_token)
|
||||
out = {"labels": [], "attention_mask": []}
|
||||
for i, (prompt, response) in enumerate(zip(examples["prompt"], examples["response"])):
|
||||
input_tokens = tokenizer(prompt, truncation=True, max_length=max_length // 2, return_tensors="pt")["input_ids"].squeeze()
|
||||
input_len = len(input_tokens)
|
||||
|
||||
prompt_len = len(tokenizer(prompt + "\n", return_tensors="pt")["input_ids"][0])
|
||||
# plus one since we remove bos from response
|
||||
# but we subtract one since we want to add eos token
|
||||
remaining_tokens = max_length - input_len - len(newline_tokens) + 1
|
||||
# remove bos
|
||||
target_tokens = tokenizer(response, truncation=True, max_length=remaining_tokens, return_tensors="pt")["input_ids"].squeeze()[1:]
|
||||
|
||||
# hack if our prompt is super long
|
||||
# we need to include some labels so we arbitrarily trunacate at max_length // 2
|
||||
# if the length is too long
|
||||
if prompt_len >= max_length // 2:
|
||||
# if prompt is too long, truncate
|
||||
# but make sure to truncate to at max 1024 tokens
|
||||
new_len = min(max_length // 2, len(prompt) // 2)
|
||||
prompt = prompt[:new_len]
|
||||
# get new prompt length
|
||||
prompt_len = tokenizer(prompt + "\n", return_tensors="pt", max_length=max_length // 2, truncation=True).input_ids.ne(tokenizer.pad_token_id).sum().item()
|
||||
input_ids[i, :input_len] = input_tokens
|
||||
# add newline between prompt and response
|
||||
newline_plus_inputs = input_len + len(newline_tokens)
|
||||
input_ids[i, input_len: newline_plus_inputs] = newline_tokens
|
||||
|
||||
assert prompt_len <= max_length // 2, f"prompt length {prompt_len} exceeds max length {max_length}"
|
||||
# add target tokens, remove bos
|
||||
input_ids[i, newline_plus_inputs: newline_plus_inputs + len(target_tokens)] = target_tokens
|
||||
# add eos token, enforce stopping if we don't truncate
|
||||
# we don't want long code to stop generating if truncated during training
|
||||
if newline_plus_inputs + len(target_tokens) < max_length:
|
||||
input_ids[i, newline_plus_inputs + len(target_tokens)] = tokenizer.eos_token_id
|
||||
|
||||
input_tokens = tokenizer(prompt + "\n" + response + tokenizer.eos_token,
|
||||
truncation=True, max_length=max_length, return_tensors="pt")["input_ids"].squeeze()
|
||||
labels = input_ids[i].clone()
|
||||
labels[: newline_plus_inputs] = -100
|
||||
labels[labels == tokenizer.pad_token_id] = -100
|
||||
# to debug this, can set all values == -100 to the pad token, then assert that tokenizer.decode(labels, skip_special_tokens=True).strip() == response
|
||||
|
||||
labels = input_tokens.clone()
|
||||
labels[:prompt_len] = -100
|
||||
if len(labels) < max_length:
|
||||
# pad to max_length with -100
|
||||
labels = torch.cat([labels, torch.full((max_length - len(labels),), -100)])
|
||||
attention_mask = input_ids[i].ne(tokenizer.pad_token_id).int()
|
||||
|
||||
assert (labels == -100).sum() < len(labels), f"Labels are all -100, something wrong. prompt length {prompt_len} exceeds max length {max_length}"
|
||||
|
||||
if (labels == -100).sum() == len(labels) - 1:
|
||||
print(prompt)
|
||||
print(response)
|
||||
raise
|
||||
|
||||
input_tokens = tokenizer.pad({"input_ids": input_tokens}, padding="max_length", max_length=max_length)["input_ids"]
|
||||
out["labels"].append(labels)
|
||||
out["input_ids"].append(input_tokens)
|
||||
out["attention_mask"].append(attention_mask)
|
||||
|
||||
out["input_ids"] = input_ids
|
||||
|
||||
out = {k: torch.stack(v) if isinstance(v, list) else v for k, v in out.items()}
|
||||
|
||||
@@ -73,7 +68,7 @@ def load_data(config, tokenizer):
|
||||
dataset = load_dataset("json", data_files=files, split="train")
|
||||
|
||||
else:
|
||||
dataset = load_dataset(dataset_path, split="train")
|
||||
dataset = load_dataset(dataset_path,split='train')
|
||||
|
||||
dataset = dataset.train_test_split(test_size=.05, seed=config["seed"])
|
||||
|
||||
@@ -116,53 +111,3 @@ def load_data(config, tokenizer):
|
||||
)
|
||||
|
||||
return train_dataloader, val_dataloader
|
||||
|
||||
|
||||
def load_data_for_inference(config, tokenizer):
|
||||
dataset_path = config["dataset_path"]
|
||||
|
||||
if os.path.exists(dataset_path):
|
||||
# check if path is a directory
|
||||
if os.path.isdir(dataset_path):
|
||||
files = glob.glob(os.path.join(dataset_path, "*_clean.jsonl"))
|
||||
else:
|
||||
files = [dataset_path]
|
||||
|
||||
print(f"Reading files {files}")
|
||||
|
||||
dataset = load_dataset("json", data_files=files, split="train")
|
||||
|
||||
else:
|
||||
dataset = load_dataset(dataset_path, split="train")
|
||||
|
||||
dataset = dataset.train_test_split(test_size=.05, seed=config["seed"])
|
||||
|
||||
train_dataset, val_dataset = dataset["train"], dataset["test"]
|
||||
|
||||
train_dataset = train_dataset.add_column("index", list(range(len(train_dataset))))
|
||||
# select first N batches that are divisible by batch_size
|
||||
# gather is a bit annoying (or the way I'm using it) to get uneven batches as it duplicates data
|
||||
train_dataset = train_dataset.select(range((len(train_dataset) // config["batch_size"]) * config["batch_size"]))
|
||||
val_dataset = val_dataset.add_column("index", list(range(len(val_dataset))))
|
||||
val_dataset = val_dataset.select(range((len(val_dataset) // config["batch_size"]) * config["batch_size"]))
|
||||
|
||||
if config["streaming"] is False:
|
||||
kwargs = {"num_proc": config["num_proc"]}
|
||||
else:
|
||||
kwargs = {}
|
||||
|
||||
# tokenize inputs and return labels and attention mask
|
||||
train_dataset = train_dataset.map(
|
||||
lambda ele: tokenize_inputs(config, tokenizer, ele),
|
||||
batched=True,
|
||||
**kwargs
|
||||
)
|
||||
val_dataset = val_dataset.map(
|
||||
lambda ele: tokenize_inputs(config, tokenizer, ele),
|
||||
batched=True,
|
||||
**kwargs
|
||||
)
|
||||
train_dataset = train_dataset.with_format("torch")
|
||||
val_dataset = val_dataset.with_format("torch")
|
||||
|
||||
return train_dataset, val_dataset
|
||||
|
||||
@@ -5,8 +5,8 @@ from matplotlib import pyplot as plt
|
||||
|
||||
plt.figure()
|
||||
for fpath in glob.glob('./eval_data/*.pkl'):
|
||||
parts = fpath.split('__')
|
||||
model_name = parts[1].replace('model-', '').replace('.pkl', '')
|
||||
parts = fpath.split('_')
|
||||
model_name = "-".join(fpath.replace(".pkl", "").split("_")[2:])
|
||||
lora_name = parts[2].replace('lora-', '').replace('.pkl', '')
|
||||
with open(fpath, 'rb') as f:
|
||||
data = pickle.load(f)
|
||||
@@ -14,7 +14,7 @@ for fpath in glob.glob('./eval_data/*.pkl'):
|
||||
perplexities = np.nan_to_num(perplexities, 100)
|
||||
perplexities = np.clip(perplexities, 0, 100)
|
||||
if 'nomic' in fpath:
|
||||
label = 'GPT4all-lora'
|
||||
label = f'GPT4all-{model_name}'
|
||||
else:
|
||||
label = 'alpaca-lora'
|
||||
plt.hist(perplexities, label=label, alpha=.5)
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
#!/bin/sh
|
||||
WORKER_IP=$1
|
||||
N_GPUS=$2
|
||||
|
||||
sudo apt install -y nfs-kernel-server
|
||||
sudo mkdir -p ./data_multiplus
|
||||
sudo chmod 777 ./data_multiplus
|
||||
printf "${PWD}/data_multiplus ${WORKER_IP}(rw,sync,no_subtree_check)" | sudo tee -a /etc/exports
|
||||
sudo systemctl restart nfs-kernel-server
|
||||
|
||||
sudo apt-get install -y pdsh
|
||||
export DSHPATH=$PATH
|
||||
export PDSH_RCMD_TYPE=ssh
|
||||
|
||||
ssh-keygen -t rsa -N ''
|
||||
cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
|
||||
|
||||
sudo mkdir -p /job
|
||||
printf "localhost slots=$N_GPUS\n$WORKER_IP slots=$N_GPUS" | sudo tee /job/hostfile
|
||||
207
inference.py
207
inference.py
@@ -1,207 +0,0 @@
|
||||
from transformers import AutoModelForCausalLM, AutoTokenizer
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
from argparse import ArgumentParser
|
||||
from read import read_config
|
||||
from accelerate.utils import set_seed
|
||||
from data import load_data_for_inference
|
||||
from tqdm import tqdm
|
||||
from datasets import Dataset
|
||||
import torch.distributed as dist
|
||||
from transformers.trainer_pt_utils import nested_numpify
|
||||
from transformers import DefaultDataCollator
|
||||
from torch.utils.data import DataLoader, DistributedSampler
|
||||
import numpy as np
|
||||
import pyarrow as pa
|
||||
from pyarrow import compute as pc
|
||||
|
||||
|
||||
def calc_cross_entropy_no_reduction(lm_logits, labels):
|
||||
# calculate cross entropy across batch dim
|
||||
shift_logits = lm_logits[..., :-1, :].contiguous()
|
||||
shift_labels = labels[..., 1:].contiguous()
|
||||
# Flatten the tokens
|
||||
loss_fct = nn.CrossEntropyLoss(reduction='none')
|
||||
loss = loss_fct(shift_logits.permute(0, 2, 1), shift_labels).mean(dim=1)
|
||||
|
||||
return loss
|
||||
|
||||
|
||||
def rank0_print(msg):
|
||||
if dist.get_rank() == 0:
|
||||
print(msg)
|
||||
|
||||
|
||||
def inference(config):
|
||||
set_seed(config['seed'])
|
||||
|
||||
rank0_print(f"World size: {dist.get_world_size()}")
|
||||
|
||||
tokenizer = AutoTokenizer.from_pretrained(config['tokenizer_name'], model_max_length=config['max_length'])
|
||||
# llama has no pad token, set it to new token
|
||||
if tokenizer.pad_token is None:
|
||||
tokenizer.pad_token = tokenizer.eos_token
|
||||
|
||||
|
||||
train_dataset, val_dataset = load_data_for_inference(config, tokenizer)
|
||||
|
||||
num_processes = dist.get_world_size()
|
||||
local_rank = dist.get_rank()
|
||||
|
||||
train_sampler = DistributedSampler(train_dataset, shuffle=False, drop_last=True, num_replicas=num_processes, rank=local_rank)
|
||||
train_dataloader = DataLoader(
|
||||
train_dataset,
|
||||
collate_fn=DefaultDataCollator(),
|
||||
batch_size=config["batch_size"],
|
||||
sampler=train_sampler,
|
||||
drop_last=True
|
||||
)
|
||||
|
||||
val_sampler = DistributedSampler(val_dataset, shuffle=False, drop_last=True, num_replicas=num_processes, rank=local_rank)
|
||||
val_dataloader = DataLoader(
|
||||
val_dataset,
|
||||
collate_fn=DefaultDataCollator(),
|
||||
batch_size=config["batch_size"],
|
||||
sampler=val_sampler,
|
||||
drop_last=True
|
||||
)
|
||||
|
||||
|
||||
model = AutoModelForCausalLM.from_pretrained(config["model_name"],
|
||||
trust_remote_code=True,
|
||||
torch_dtype=torch.bfloat16,
|
||||
)
|
||||
model.to(f"cuda:{local_rank}")
|
||||
|
||||
with torch.no_grad():
|
||||
train_outputs = {"loss": [], "embeddings": [], "index": []}
|
||||
for batch in tqdm(train_dataloader, disable=local_rank != 0):
|
||||
batch["input_ids"] = batch["input_ids"].to(f"cuda:{local_rank}")
|
||||
batch["labels"] = batch["labels"].to(f"cuda:{local_rank}")
|
||||
outputs = model(input_ids=batch["input_ids"], labels=batch["labels"], output_hidden_states=True)
|
||||
loss = calc_cross_entropy_no_reduction(outputs.logits, batch["labels"])
|
||||
train_outputs["loss"].extend(loss)
|
||||
|
||||
embeddings = outputs.hidden_states[-1]
|
||||
batch_size = batch["input_ids"].shape[0]
|
||||
sequence_lengths = []
|
||||
# since we use mutiturn with multiple <|endoftext|>, we need to find the place where
|
||||
# <|endoftext|> is repeated
|
||||
for item in batch["input_ids"]:
|
||||
indices = torch.where(item == tokenizer.pad_token_id)[0]
|
||||
found = False
|
||||
for index in indices:
|
||||
# case where sequence is less than max length
|
||||
if torch.all(item[index:] == tokenizer.pad_token_id):
|
||||
sequence_lengths.append(index)
|
||||
found = True
|
||||
break
|
||||
# case where sequence is >= max length
|
||||
if not found:
|
||||
sequence_lengths.append(len(item) - 1)
|
||||
|
||||
sequence_lengths = torch.tensor(sequence_lengths)
|
||||
pooled_logits = embeddings[torch.arange(batch_size, device=embeddings.device), sequence_lengths]
|
||||
|
||||
train_outputs["embeddings"].append(pooled_logits)
|
||||
train_outputs["index"].extend(batch["index"].to(model.device))
|
||||
|
||||
torch.cuda.empty_cache()
|
||||
|
||||
train_outputs = nested_numpify(train_outputs)
|
||||
# stack since they're 0-dim arrays
|
||||
train_outputs["index"] = np.stack(train_outputs["index"])
|
||||
train_outputs["loss"] = np.stack(train_outputs["loss"])
|
||||
train_outputs["embeddings"] = np.concatenate(train_outputs["embeddings"])
|
||||
|
||||
df_train = Dataset.from_dict(train_outputs)
|
||||
df_train = df_train.sort("index")
|
||||
curr_idx = df_train["index"]
|
||||
|
||||
# compute mask in pyarrow since it's super fast
|
||||
# ty @bmschmidt for showing me this!
|
||||
table = train_dataset.data
|
||||
mask = pc.is_in(table['index'], value_set=pa.array(curr_idx, pa.int32()))
|
||||
filtered_table = table.filter(mask)
|
||||
# convert from pyarrow to Dataset
|
||||
filtered_train = Dataset.from_dict(filtered_table.to_pydict())
|
||||
|
||||
filtered_train = filtered_train.add_column("embeddings", df_train["embeddings"])
|
||||
filtered_train = filtered_train.add_column("loss", df_train["loss"])
|
||||
filtered_train = filtered_train.add_column("is_train", [True] * len(filtered_train))
|
||||
|
||||
filtered_train.to_json(f"inference/epoch_2_embeddings_train_shard_{local_rank}.jsonl", lines=True, orient="records", num_proc=64)
|
||||
|
||||
val_outputs = {"loss": [], "embeddings": [], "index": []}
|
||||
for batch in tqdm(val_dataloader, disable=local_rank != 0):
|
||||
batch["input_ids"] = batch["input_ids"].to(f"cuda:{local_rank}")
|
||||
batch["labels"] = batch["labels"].to(f"cuda:{local_rank}")
|
||||
outputs = model(input_ids=batch["input_ids"], labels=batch["labels"])
|
||||
loss = calc_cross_entropy_no_reduction(outputs.logits, batch["labels"])
|
||||
val_outputs["loss"].extend(loss)
|
||||
|
||||
logits = outputs.logits
|
||||
batch_size = batch["input_ids"].shape[0]
|
||||
sequence_lengths = []
|
||||
# since we use mutiturn with multiple <|endoftext|>, we need to find the place where
|
||||
# <|endoftext|> is repeated
|
||||
for item in batch["input_ids"]:
|
||||
indices = torch.where(item == tokenizer.pad_token_id)[0]
|
||||
found = False
|
||||
for index in indices:
|
||||
if torch.all(item[index:] == tokenizer.pad_token_id):
|
||||
sequence_lengths.append(index)
|
||||
found = True
|
||||
break
|
||||
|
||||
# no match found
|
||||
if not found:
|
||||
sequence_lengths.append(len(item) - 1)
|
||||
|
||||
sequence_lengths = torch.tensor(sequence_lengths)
|
||||
pooled_logits = logits[torch.arange(batch_size, device=logits.device), sequence_lengths]
|
||||
|
||||
val_outputs["embeddings"].append(pooled_logits)
|
||||
val_outputs["index"].extend(batch["index"].to(model.device))
|
||||
|
||||
torch.cuda.empty_cache()
|
||||
|
||||
val_outputs = nested_numpify(val_outputs)
|
||||
val_outputs["index"] = np.stack(val_outputs["index"])
|
||||
val_outputs["loss"] = np.stack(val_outputs["loss"])
|
||||
val_outputs["embeddings"] = np.concatenate(val_outputs["embeddings"])
|
||||
|
||||
df_val = Dataset.from_dict(val_outputs)
|
||||
df_val = df_val.sort("index")
|
||||
curr_idx = df_val["index"]
|
||||
|
||||
# compute mask in pyarrow since it's super fast
|
||||
# ty @bmschmidt for showing me this!
|
||||
table = val_dataset.data
|
||||
mask = pc.is_in(table['index'], value_set=pa.array(curr_idx, pa.int32()))
|
||||
filtered_table = table.filter(mask)
|
||||
# convert from pyarrow to Dataset
|
||||
filtered_val = Dataset.from_dict(filtered_table.to_pydict())
|
||||
|
||||
filtered_val = filtered_val.add_column("embeddings", df_val["embeddings"])
|
||||
filtered_val = filtered_val.add_column("loss", df_val["loss"])
|
||||
filtered_val = filtered_val.add_column("is_train", [False] * len(filtered_val))
|
||||
|
||||
filtered_val.to_json(f"inference/epoch_2_embeddings_val_shard_{local_rank}.jsonl", lines=True, orient="records", num_proc=64)
|
||||
|
||||
|
||||
def main():
|
||||
dist.init_process_group("nccl")
|
||||
parser = ArgumentParser()
|
||||
parser.add_argument("--config", type=str, default="config.yaml")
|
||||
|
||||
args = parser.parse_args()
|
||||
config = read_config(args.config)
|
||||
|
||||
inference(config)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# parse arguments by reading in a config
|
||||
main()
|
||||
|
||||
88
launcher.sh
Normal file
88
launcher.sh
Normal file
@@ -0,0 +1,88 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Display header
|
||||
echo "=========================================================="
|
||||
echo " ██████ ██████ ████████ ██ ██ █████ ██ ██ "
|
||||
echo "██ ██ ██ ██ ██ ██ ██ ██ ██ ██ "
|
||||
echo "██ ███ ██████ ██ ███████ ███████ ██ ██ "
|
||||
echo "██ ██ ██ ██ ██ ██ ██ ██ ██ "
|
||||
echo " ██████ ██ ██ ██ ██ ██ ███████ ███████ "
|
||||
echo " └─> https://github.com/nomic-ai/gpt4all"
|
||||
|
||||
# Function to detect macOS architecture and set the binary filename
|
||||
detect_mac_arch() {
|
||||
local mac_arch
|
||||
mac_arch=$(uname -m)
|
||||
case "$mac_arch" in
|
||||
arm64)
|
||||
os_type="M1 Mac/OSX"
|
||||
binary_filename="gpt4all-lora-quantized-OSX-m1"
|
||||
;;
|
||||
x86_64)
|
||||
os_type="Intel Mac/OSX"
|
||||
binary_filename="gpt4all-lora-quantized-OSX-intel"
|
||||
;;
|
||||
*)
|
||||
echo "Unknown macOS architecture"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Detect operating system and set the binary filename
|
||||
case "$(uname -s)" in
|
||||
Darwin*)
|
||||
detect_mac_arch
|
||||
;;
|
||||
Linux*)
|
||||
if grep -q Microsoft /proc/version; then
|
||||
os_type="Windows (WSL)"
|
||||
binary_filename="gpt4all-lora-quantized-win64.exe"
|
||||
else
|
||||
os_type="Linux"
|
||||
binary_filename="gpt4all-lora-quantized-linux-x86"
|
||||
fi
|
||||
;;
|
||||
CYGWIN*|MINGW32*|MSYS*|MINGW*)
|
||||
os_type="Windows (Cygwin/MSYS/MINGW)"
|
||||
binary_filename="gpt4all-lora-quantized-win64.exe"
|
||||
;;
|
||||
*)
|
||||
echo "Unknown operating system"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
echo "================================"
|
||||
echo "== You are using $os_type."
|
||||
|
||||
|
||||
# Change to the chat directory
|
||||
cd chat
|
||||
|
||||
# List .bin files and prompt user to select one
|
||||
bin_files=(*.bin)
|
||||
echo "== Available .bin files:"
|
||||
for i in "${!bin_files[@]}"; do
|
||||
echo " [$((i+1))] ${bin_files[i]}"
|
||||
done
|
||||
|
||||
# Function to get user input and validate it
|
||||
get_valid_user_input() {
|
||||
local input_valid=false
|
||||
|
||||
while ! $input_valid; do
|
||||
echo "==> Please enter a number:"
|
||||
read -r user_selection
|
||||
if [[ $user_selection =~ ^[0-9]+$ ]] && (( user_selection >= 1 && user_selection <= ${#bin_files[@]} )); then
|
||||
input_valid=true
|
||||
else
|
||||
echo "Invalid input. Please enter a number between 1 and ${#bin_files[@]}."
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
get_valid_user_input
|
||||
selected_bin_file="${bin_files[$((user_selection-1))]}"
|
||||
|
||||
# Run the selected .bin file with the appropriate command
|
||||
./"$binary_filename" -m "$selected_bin_file"
|
||||
@@ -10,4 +10,4 @@ nodelist-inflator
|
||||
deepspeed
|
||||
sentencepiece
|
||||
jsonlines
|
||||
nomic
|
||||
matplotlib
|
||||
108
train.py
108
train.py
@@ -1,7 +1,8 @@
|
||||
import os
|
||||
from transformers import AutoModelForCausalLM, AutoTokenizer, get_scheduler, LlamaForCausalLM
|
||||
from transformers import AutoModelForCausalLM, AutoTokenizer
|
||||
from transformers.trainer_pt_utils import get_parameter_names
|
||||
import torch
|
||||
from torch.optim import AdamW
|
||||
import torch.nn as nn
|
||||
from argparse import ArgumentParser
|
||||
from read import read_config
|
||||
from accelerate import Accelerator
|
||||
@@ -10,9 +11,7 @@ from peft import get_peft_model, LoraConfig, TaskType
|
||||
from data import load_data
|
||||
from torchmetrics import MeanMetric
|
||||
from tqdm import tqdm
|
||||
import wandb
|
||||
|
||||
torch.backends.cuda.matmul.allow_tf32 = True
|
||||
|
||||
def format_metrics(metrics, split, prefix=""):
|
||||
log = f"[{split}]" + prefix
|
||||
@@ -21,12 +20,17 @@ def format_metrics(metrics, split, prefix=""):
|
||||
return log
|
||||
|
||||
|
||||
def evaluate(model, val_dataloader):
|
||||
def evaluate(config, model, val_dataloader):
|
||||
model.eval()
|
||||
val_loss = MeanMetric(nan_strategy="error").to(model.device)
|
||||
val_loss = MeanMetric().to(model.device)
|
||||
|
||||
with torch.no_grad():
|
||||
for batch in tqdm(val_dataloader):
|
||||
for i, batch in enumerate(
|
||||
tqdm(val_dataloader),
|
||||
):
|
||||
if i == config["eval_steps"]:
|
||||
break
|
||||
|
||||
loss = model(**batch).loss
|
||||
|
||||
loss_values = accelerator.gather_for_metrics({"loss": loss.detach()})
|
||||
@@ -42,20 +46,25 @@ def train(accelerator, config):
|
||||
accelerator.print(config)
|
||||
accelerator.print(f"Using {accelerator.num_processes} GPUs")
|
||||
|
||||
tokenizer = AutoTokenizer.from_pretrained(config['tokenizer_name'], model_max_length=config['max_length'])
|
||||
# if no pad token, set it to eos
|
||||
tokenizer = AutoTokenizer.from_pretrained(config['tokenizer_name'])
|
||||
# llama has no pad token, set it to new token
|
||||
if tokenizer.pad_token is None:
|
||||
tokenizer.pad_token = tokenizer.eos_token
|
||||
# these tokens are already in the vocab, just not mapped correctly
|
||||
added_tokens = tokenizer.add_special_tokens({"bos_token": "<s>", "eos_token": "</s>", "pad_token": "<pad>"})
|
||||
|
||||
|
||||
with accelerator.main_process_first():
|
||||
train_dataloader, val_dataloader = load_data(config, tokenizer)
|
||||
|
||||
|
||||
|
||||
checkpoint = config["gradient_checkpointing"]
|
||||
model = AutoModelForCausalLM.from_pretrained(config["model_name"],
|
||||
use_cache=False if checkpoint else True,
|
||||
trust_remote_code=True)
|
||||
|
||||
if added_tokens > 0:
|
||||
model.resize_token_embeddings(len(tokenizer))
|
||||
|
||||
if checkpoint:
|
||||
model.gradient_checkpointing_enable()
|
||||
|
||||
@@ -68,7 +77,7 @@ def train(accelerator, config):
|
||||
model.print_trainable_parameters()
|
||||
|
||||
optimizer_cls = (
|
||||
AdamW
|
||||
torch.optim.AdamW
|
||||
if accelerator.state.deepspeed_plugin is None
|
||||
or "optimizer" not in accelerator.state.deepspeed_plugin.deepspeed_config
|
||||
else DummyOptim
|
||||
@@ -76,35 +85,11 @@ def train(accelerator, config):
|
||||
|
||||
# karpathy doesn't decay embeddding, maybe we should exclude
|
||||
# https://github.com/karpathy/minGPT/commit/bbbdac74fa9b2e55574d70056163ffbae42310c1#diff-2075fa9c224b395be5bda85544dd36572b59c76c54562819eadadbf268602834R157s
|
||||
optimizer = optimizer_cls(model.parameters(), lr=config["lr"], weight_decay=config["weight_decay"])
|
||||
optimizer = optimizer_cls(model.parameters(), lr=config["lr"])
|
||||
|
||||
if accelerator.state.deepspeed_plugin is not None:
|
||||
gradient_accumulation_steps = accelerator.state.deepspeed_plugin.deepspeed_config[
|
||||
"gradient_accumulation_steps"
|
||||
]
|
||||
|
||||
# decay to min_lr instead of 0
|
||||
lr_ratio = config["min_lr"] / config["lr"]
|
||||
accelerator.print(f"Len of train_dataloader: {len(train_dataloader)}")
|
||||
total_num_steps = (len(train_dataloader) / gradient_accumulation_steps) * config["num_epochs"]
|
||||
# instead of decaying to zero, decay to ratio of min_lr / lr
|
||||
total_num_steps += int(total_num_steps * lr_ratio) + config["warmup_steps"]
|
||||
accelerator.print(f"Total training steps: {total_num_steps}")
|
||||
|
||||
# Creates Dummy Scheduler if `scheduler` was spcified in the config file else creates `args.lr_scheduler_type` Scheduler
|
||||
if (
|
||||
accelerator.state.deepspeed_plugin is None
|
||||
or "scheduler" not in accelerator.state.deepspeed_plugin.deepspeed_config
|
||||
):
|
||||
scheduler = get_scheduler(
|
||||
name="cosine",
|
||||
optimizer=optimizer,
|
||||
num_warmup_steps=config["warmup_steps"] * accelerator.num_processes,
|
||||
num_training_steps=total_num_steps * accelerator.num_processes,
|
||||
)
|
||||
else:
|
||||
scheduler = DummyScheduler(
|
||||
optimizer, total_num_steps=config["warmup_steps"], warmup_num_steps=config["warmup_steps"]
|
||||
# scheduler defined in Deepspeed config
|
||||
scheduler = DummyScheduler(
|
||||
optimizer, warmup_num_steps=config["warmup_steps"],
|
||||
)
|
||||
|
||||
model, optimizer, train_dataloader, val_dataloader, scheduler = accelerator.prepare(
|
||||
@@ -123,25 +108,21 @@ def train(accelerator, config):
|
||||
accelerator.skip_first_batches(train_dataloader, resume_step)
|
||||
accelerator.print(f"Resuming from step {resume_step}")
|
||||
|
||||
train_loss = MeanMetric().to(model.device)
|
||||
|
||||
# log gradients
|
||||
if accelerator.is_main_process and config["wandb"]:
|
||||
wandb.watch(model, log_freq=config["log_grads_every"], log="all")
|
||||
if accelerator.state.deepspeed_plugin is not None:
|
||||
gradient_accumulation_steps = accelerator.state.deepspeed_plugin.deepspeed_config[
|
||||
"gradient_accumulation_steps"
|
||||
]
|
||||
|
||||
for epoch in range(config["num_epochs"]):
|
||||
train_loss = MeanMetric(nan_strategy="error").to(model.device)
|
||||
for step, batch in enumerate(tqdm(train_dataloader)):
|
||||
model.train()
|
||||
outputs = model(**batch)
|
||||
loss = outputs.loss
|
||||
|
||||
# gather loss before backprop in case of gradient accumulation
|
||||
loss_values = accelerator.gather_for_metrics({"loss": loss.detach().float()})
|
||||
train_loss.update(loss_values["loss"])
|
||||
|
||||
loss = loss / gradient_accumulation_steps
|
||||
|
||||
accelerator.backward(loss)
|
||||
# get gradient norm of all params
|
||||
|
||||
# log LR in case something weird happens
|
||||
if step > 0 and step % (config["eval_every"] // 10) == 0:
|
||||
@@ -154,13 +135,14 @@ def train(accelerator, config):
|
||||
scheduler.step()
|
||||
optimizer.zero_grad()
|
||||
|
||||
loss_values = accelerator.gather_for_metrics({"loss": loss.detach()})
|
||||
train_loss.update(loss_values["loss"])
|
||||
|
||||
if step > 0 and step % config["save_every"] == 0:
|
||||
curr_step = step + epoch * len(train_dataloader)
|
||||
accelerator.save_state(f"{config['output_dir']}/step_{curr_step}")
|
||||
accelerator.save_state(f"{config['output_dir']}/step_{step}")
|
||||
|
||||
if step > 0 and (step % config["eval_every"] == 0 or step == len(train_dataloader) - 1):
|
||||
val_loss = evaluate(model, val_dataloader)
|
||||
if step > 0 and step % config["eval_every"] == 0:
|
||||
val_loss = evaluate(config, model, val_dataloader)
|
||||
|
||||
log_train = {
|
||||
"train_loss": train_loss.compute()
|
||||
@@ -183,20 +165,9 @@ def train(accelerator, config):
|
||||
accelerator.print(f"Pushing to HF hub")
|
||||
accelerator.wait_for_everyone()
|
||||
unwrapped_model = accelerator.unwrap_model(model)
|
||||
try:
|
||||
if accelerator.is_main_process:
|
||||
unwrapped_model.push_to_hub(config["save_name"] + f"-epoch_{epoch}", private=True)
|
||||
if accelerator.is_main_process:
|
||||
unwrapped_model.push_to_hub(config["save_name"] + "_first_epoch", private=True)
|
||||
|
||||
except Exception as e:
|
||||
accelerator.print(e)
|
||||
accelerator.print(f"Failed to push to hub")
|
||||
|
||||
unwrapped_model.save_pretrained(
|
||||
f"{config['output_dir']}/-epoch_{epoch}",
|
||||
is_main_process=accelerator.is_main_process,
|
||||
save_function=accelerator.save,
|
||||
state_dict=accelerator.get_state_dict(model),
|
||||
)
|
||||
|
||||
accelerator.wait_for_everyone()
|
||||
unwrapped_model = accelerator.unwrap_model(model)
|
||||
@@ -207,6 +178,9 @@ def train(accelerator, config):
|
||||
state_dict=accelerator.get_state_dict(model),
|
||||
)
|
||||
|
||||
if accelerator.is_main_process:
|
||||
unwrapped_model.push_to_hub(config["save_name"], private=True)
|
||||
|
||||
accelerator.end_training()
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
#!/bin/sh
|
||||
HEAD_IP=$1
|
||||
|
||||
sudo apt install -y nfs-common
|
||||
sudo mkdir -p ./data_multiplus
|
||||
sudo mount ${HEAD_IP}:${PWD}/data_multiplus ./data_multiplus
|
||||
Reference in New Issue
Block a user