Text Generation
Transformers
English
machine-learning
small-language-model
slm
edge
on-device
domain-adaptation
continued-pretraining
arxiv
webgpu
Instructions to use vigneshwar234/Vashtra-0.6B with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use vigneshwar234/Vashtra-0.6B with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-generation", model="vigneshwar234/Vashtra-0.6B")# pip install -U transformers accelerate # Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("vigneshwar234/Vashtra-0.6B", device_map="auto") - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use vigneshwar234/Vashtra-0.6B with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "vigneshwar234/Vashtra-0.6B" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "vigneshwar234/Vashtra-0.6B", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }'Use Docker
docker model run hf.co/vigneshwar234/Vashtra-0.6B
- SGLang
How to use vigneshwar234/Vashtra-0.6B with SGLang:
Install from pip and serve model
# Install SGLang from pip: pip install sglang # Start the SGLang server: python3 -m sglang.launch_server \ --model-path "vigneshwar234/Vashtra-0.6B" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "vigneshwar234/Vashtra-0.6B", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }'Use Docker images
docker run --gpus all \ --shm-size 32g \ -p 30000:30000 \ -v ~/.cache/huggingface:/root/.cache/huggingface \ --env "HF_TOKEN=<secret>" \ --ipc=host \ lmsysorg/sglang:latest \ python3 -m sglang.launch_server \ --model-path "vigneshwar234/Vashtra-0.6B" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "vigneshwar234/Vashtra-0.6B", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }' - Docker Model Runner
How to use vigneshwar234/Vashtra-0.6B with Docker Model Runner:
docker model run hf.co/vigneshwar234/Vashtra-0.6B
Larger corpus: full-text papers, smoltalk2, resumable training
Browse files- README.md +25 -15
- Vashtra_training.ipynb +40 -18
- build_corpus.py +128 -69
- train_vashtra.py +72 -14
README.md
CHANGED
|
@@ -18,9 +18,10 @@ tags:
|
|
| 18 |
- webgpu
|
| 19 |
datasets:
|
| 20 |
- vigneshwar234/vashtra-ml-corpus
|
|
|
|
| 21 |
- CShorten/ML-ArXiv-Papers
|
| 22 |
- donfu/oa-stackexchange
|
| 23 |
-
- HuggingFaceTB/
|
| 24 |
---
|
| 25 |
|
| 26 |
# Vashtra-0.6B
|
|
@@ -43,19 +44,29 @@ does.
|
|
| 43 |
Two stages, both full-parameter. At 0.6B a full fine-tune beats LoRA for domain
|
| 44 |
adaptation and still fits in a free Colab T4, using about 10GB of 16GB.
|
| 45 |
|
| 46 |
-
**Stage A, continued pretraining.**
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 50 |
|
| 51 |
**Stage B, supervised fine-tuning.** ML, stats and CS StackExchange answers that were
|
| 52 |
-
accepted with score 3 or higher,
|
| 53 |
-
|
|
|
|
| 54 |
|
| 55 |
### The general chat anchor
|
| 56 |
|
| 57 |
Fine-tuning a 0.6B model only on domain data reliably destroys its ability to hold a
|
| 58 |
-
normal conversation. About
|
| 59 |
mixture to stop that. It costs a little domain sharpness and keeps the model usable.
|
| 60 |
|
| 61 |
### A tokenisation detail
|
|
@@ -104,16 +115,15 @@ Useful for explaining ML concepts, summarising papers, and on-device or edge cas
|
|
| 104 |
where 0.6B is the size you can afford.
|
| 105 |
|
| 106 |
It is still a 0.6B model. It gets specifics wrong, especially exact numbers, named
|
| 107 |
-
papers, and anything recent.
|
| 108 |
-
|
| 109 |
-
that matters without checking the answer.
|
| 110 |
|
| 111 |
## Licence
|
| 112 |
|
| 113 |
-
Apache-2.0, same as the Qwen3-0.6B base. The corpus mixes licences:
|
| 114 |
-
are
|
| 115 |
-
|
| 116 |
-
CC-BY-SA-4.0.
|
| 117 |
|
| 118 |
Built by [vigneshwar234](https://huggingface.co/vigneshwar234).
|
| 119 |
Code on [GitHub](https://github.com/vignesh2027/Vashtra).
|
|
|
|
| 18 |
- webgpu
|
| 19 |
datasets:
|
| 20 |
- vigneshwar234/vashtra-ml-corpus
|
| 21 |
+
- common-pile/arxiv_papers_filtered
|
| 22 |
- CShorten/ML-ArXiv-Papers
|
| 23 |
- donfu/oa-stackexchange
|
| 24 |
+
- HuggingFaceTB/smoltalk2
|
| 25 |
---
|
| 26 |
|
| 27 |
# Vashtra-0.6B
|
|
|
|
| 44 |
Two stages, both full-parameter. At 0.6B a full fine-tune beats LoRA for domain
|
| 45 |
adaptation and still fits in a free Colab T4, using about 10GB of 16GB.
|
| 46 |
|
| 47 |
+
**Stage A, continued pretraining.** Full-text arXiv ML papers from `common-pile`
|
| 48 |
+
(openly licensed only) plus 117k arXiv `cs.LG` titles and abstracts, packed into
|
| 49 |
+
dense 1024-token blocks so no compute goes to padding. LR 5e-5, cosine, one epoch.
|
| 50 |
+
The LR is low on purpose. The goal is to absorb the domain without wrecking the
|
| 51 |
+
base model's general ability.
|
| 52 |
+
|
| 53 |
+
Full text matters here. Abstracts teach the model what papers claim. Full text
|
| 54 |
+
teaches it how the argument is built, which is what you want when someone asks it
|
| 55 |
+
to explain a method.
|
| 56 |
+
|
| 57 |
+
The corpus is about 90M tokens, more than a free Colab session will finish, so
|
| 58 |
+
stage A takes a `--cpt_tokens` budget and pushes checkpoints to the Hub. Passing
|
| 59 |
+
`--resume` picks up where a dropped session stopped.
|
| 60 |
|
| 61 |
**Stage B, supervised fine-tuning.** ML, stats and CS StackExchange answers that were
|
| 62 |
+
accepted with score 3 or higher, arXiv explanation and titling tasks, and three
|
| 63 |
+
subsets of `smoltalk2`: science reasoning, instruction following, and general chat.
|
| 64 |
+
LR 1e-5, two epochs, loss masked so only assistant turns count.
|
| 65 |
|
| 66 |
### The general chat anchor
|
| 67 |
|
| 68 |
Fine-tuning a 0.6B model only on domain data reliably destroys its ability to hold a
|
| 69 |
+
normal conversation. About 22k general examples from `smoltalk2` stay in the SFT
|
| 70 |
mixture to stop that. It costs a little domain sharpness and keeps the model usable.
|
| 71 |
|
| 72 |
### A tokenisation detail
|
|
|
|
| 115 |
where 0.6B is the size you can afford.
|
| 116 |
|
| 117 |
It is still a 0.6B model. It gets specifics wrong, especially exact numbers, named
|
| 118 |
+
papers, and anything recent. Do not cite it and do not use it for anything that
|
| 119 |
+
matters without checking the answer.
|
|
|
|
| 120 |
|
| 121 |
## Licence
|
| 122 |
|
| 123 |
+
Apache-2.0, same as the Qwen3-0.6B base. The corpus mixes licences: full-text papers
|
| 124 |
+
are CC-BY, CC-BY-SA or CC0 through `common-pile/arxiv_papers_filtered`, abstracts are
|
| 125 |
+
AFL-3.0 through `CShorten/ML-ArXiv-Papers`, StackExchange content is CC-BY-SA-4.0,
|
| 126 |
+
and `smoltalk2` is Apache-2.0. Anything derived from StackExchange stays CC-BY-SA-4.0.
|
| 127 |
|
| 128 |
Built by [vigneshwar234](https://huggingface.co/vigneshwar234).
|
| 129 |
Code on [GitHub](https://github.com/vignesh2027/Vashtra).
|
Vashtra_training.ipynb
CHANGED
|
@@ -4,17 +4,17 @@
|
|
| 4 |
"cell_type": "markdown",
|
| 5 |
"metadata": {},
|
| 6 |
"source": [
|
| 7 |
-
"# Vashtra-0.6B
|
| 8 |
"\n",
|
| 9 |
"Two-stage domain adaptation of **Qwen3-0.6B** into a machine-learning specialist.\n",
|
| 10 |
"\n",
|
| 11 |
"| Stage | Data | What it teaches |\n",
|
| 12 |
"|---|---|---|\n",
|
| 13 |
-
"| A
|
| 14 |
-
"| B
|
| 15 |
"\n",
|
| 16 |
"**Runtime \u2192 Change runtime type \u2192 T4 GPU**, then **Runtime \u2192 Run all**.\n",
|
| 17 |
-
"Everything pushes to the Hub automatically. Budget ~2
|
| 18 |
]
|
| 19 |
},
|
| 20 |
{
|
|
@@ -22,7 +22,7 @@
|
|
| 22 |
"metadata": {},
|
| 23 |
"source": [
|
| 24 |
"### 1. Confirm you have a GPU\n",
|
| 25 |
-
"If this cell fails you are on CPU
|
| 26 |
]
|
| 27 |
},
|
| 28 |
{
|
|
@@ -102,12 +102,12 @@
|
|
| 102 |
"source": [
|
| 103 |
"### 5. Build the corpus\n",
|
| 104 |
"\n",
|
| 105 |
-
"Streams arXiv cs.LG, ML/stats/CS StackExchange, and
|
| 106 |
-
"`cpt.jsonl` + `sft.jsonl`, then pushes them to\n",
|
| 107 |
-
"`vigneshwar234/vashtra-ml-corpus`. Takes ~
|
| 108 |
-
"StackExchange
|
| 109 |
"\n",
|
| 110 |
-
"Skip this cell if the corpus is already built
|
| 111 |
]
|
| 112 |
},
|
| 113 |
{
|
|
@@ -128,8 +128,30 @@
|
|
| 128 |
"Stage A (continued pretraining) then Stage B (SFT), then the model is pushed to\n",
|
| 129 |
"`vigneshwar234/Vashtra-0.6B`.\n",
|
| 130 |
"\n",
|
| 131 |
-
"
|
| 132 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 133 |
]
|
| 134 |
},
|
| 135 |
{
|
|
@@ -138,7 +160,7 @@
|
|
| 138 |
"execution_count": null,
|
| 139 |
"outputs": [],
|
| 140 |
"source": [
|
| 141 |
-
"!python train_vashtra.py --stage all"
|
| 142 |
]
|
| 143 |
},
|
| 144 |
{
|
|
@@ -147,7 +169,7 @@
|
|
| 147 |
"source": [
|
| 148 |
"### 7. Smoke-test what you trained\n",
|
| 149 |
"\n",
|
| 150 |
-
"Sanity check, not an evaluation. You are looking for coherent, on-topic ML answers
|
| 151 |
"and for the general question to still work, which is what the anchor data buys."
|
| 152 |
]
|
| 153 |
},
|
|
@@ -183,8 +205,8 @@
|
|
| 183 |
"### 8. Measure it against the base model\n",
|
| 184 |
"\n",
|
| 185 |
"Compare perplexity on held-out arXiv ML text. If stage A worked, Vashtra should be\n",
|
| 186 |
-
"meaningfully lower than Qwen3-0.6B here. Put the real number in the model card
|
| 187 |
-
"
|
| 188 |
]
|
| 189 |
},
|
| 190 |
{
|
|
@@ -228,7 +250,7 @@
|
|
| 228 |
"### 9. Export to ONNX for the in-browser demo\n",
|
| 229 |
"\n",
|
| 230 |
"Produces `vigneshwar234/Vashtra-0.6B-ONNX`, which the static WebGPU Space loads\n",
|
| 231 |
-
"directly
|
| 232 |
]
|
| 233 |
},
|
| 234 |
{
|
|
@@ -257,7 +279,7 @@
|
|
| 257 |
"\n",
|
| 258 |
"**Last step:** put the real perplexity number from cell 8 into the model card, and\n",
|
| 259 |
"replace the \"weights are training\" banner. Measured results are what make people\n",
|
| 260 |
-
"trust a small model
|
| 261 |
]
|
| 262 |
}
|
| 263 |
],
|
|
|
|
| 4 |
"cell_type": "markdown",
|
| 5 |
"metadata": {},
|
| 6 |
"source": [
|
| 7 |
+
"# Vashtra-0.6B: build the corpus, train the model, ship the demo\n",
|
| 8 |
"\n",
|
| 9 |
"Two-stage domain adaptation of **Qwen3-0.6B** into a machine-learning specialist.\n",
|
| 10 |
"\n",
|
| 11 |
"| Stage | Data | What it teaches |\n",
|
| 12 |
"|---|---|---|\n",
|
| 13 |
+
"| A, continued pretraining | full-text arXiv ML papers + 117k cs.LG abstracts | ML vocabulary, concepts, how arguments are made |\n",
|
| 14 |
+
"| B, supervised fine-tuning | ML Q&A + general chat | answering, instruction following |\n",
|
| 15 |
"\n",
|
| 16 |
"**Runtime \u2192 Change runtime type \u2192 T4 GPU**, then **Runtime \u2192 Run all**.\n",
|
| 17 |
+
"Everything pushes to the Hub automatically. Budget ~2 to 3 hours on the free tier.\n"
|
| 18 |
]
|
| 19 |
},
|
| 20 |
{
|
|
|
|
| 22 |
"metadata": {},
|
| 23 |
"source": [
|
| 24 |
"### 1. Confirm you have a GPU\n",
|
| 25 |
+
"If this cell fails you are on CPU. Switch the runtime first, or nothing below will finish."
|
| 26 |
]
|
| 27 |
},
|
| 28 |
{
|
|
|
|
| 102 |
"source": [
|
| 103 |
"### 5. Build the corpus\n",
|
| 104 |
"\n",
|
| 105 |
+
"Streams full-text arXiv papers, cs.LG abstracts, ML/stats/CS StackExchange, and\n",
|
| 106 |
+
"the smoltalk2 mixture into `cpt.jsonl` + `sft.jsonl`, then pushes them to\n",
|
| 107 |
+
"`vigneshwar234/vashtra-ml-corpus`. Takes ~25 to 40 minutes, mostly the\n",
|
| 108 |
+
"StackExchange and full-text scans.\n",
|
| 109 |
"\n",
|
| 110 |
+
"Skip this cell if the corpus is already built. Training pulls it from the Hub."
|
| 111 |
]
|
| 112 |
},
|
| 113 |
{
|
|
|
|
| 128 |
"Stage A (continued pretraining) then Stage B (SFT), then the model is pushed to\n",
|
| 129 |
"`vigneshwar234/Vashtra-0.6B`.\n",
|
| 130 |
"\n",
|
| 131 |
+
"The corpus is about 90M tokens, which is more than a free Colab session will get\n",
|
| 132 |
+
"through. Stage A therefore takes a token budget, defaulting to 30M, which is\n",
|
| 133 |
+
"roughly two hours on a T4. Checkpoints are pushed to the Hub as it goes.\n",
|
| 134 |
+
"\n",
|
| 135 |
+
"If the session drops, open a new one and run the same cell with `--resume`. It\n",
|
| 136 |
+
"pulls the last checkpoint back down and carries on instead of starting over.\n",
|
| 137 |
+
"To train on more of the corpus, raise `--cpt_tokens` (or pass `0` for all of it)\n",
|
| 138 |
+
"and expect to resume across two or three sessions."
|
| 139 |
+
]
|
| 140 |
+
},
|
| 141 |
+
{
|
| 142 |
+
"cell_type": "code",
|
| 143 |
+
"metadata": {},
|
| 144 |
+
"execution_count": null,
|
| 145 |
+
"outputs": [],
|
| 146 |
+
"source": [
|
| 147 |
+
"!python train_vashtra.py --stage all --cpt_tokens 30000000"
|
| 148 |
+
]
|
| 149 |
+
},
|
| 150 |
+
{
|
| 151 |
+
"cell_type": "markdown",
|
| 152 |
+
"metadata": {},
|
| 153 |
+
"source": [
|
| 154 |
+
"If the session above was cut off, run this instead of starting over."
|
| 155 |
]
|
| 156 |
},
|
| 157 |
{
|
|
|
|
| 160 |
"execution_count": null,
|
| 161 |
"outputs": [],
|
| 162 |
"source": [
|
| 163 |
+
"# !python train_vashtra.py --stage all --cpt_tokens 30000000 --resume"
|
| 164 |
]
|
| 165 |
},
|
| 166 |
{
|
|
|
|
| 169 |
"source": [
|
| 170 |
"### 7. Smoke-test what you trained\n",
|
| 171 |
"\n",
|
| 172 |
+
"Sanity check, not an evaluation. You are looking for coherent, on-topic ML answers,\n",
|
| 173 |
"and for the general question to still work, which is what the anchor data buys."
|
| 174 |
]
|
| 175 |
},
|
|
|
|
| 205 |
"### 8. Measure it against the base model\n",
|
| 206 |
"\n",
|
| 207 |
"Compare perplexity on held-out arXiv ML text. If stage A worked, Vashtra should be\n",
|
| 208 |
+
"meaningfully lower than Qwen3-0.6B here. Put the real number in the model card.\n",
|
| 209 |
+
"This is the claim that makes the model credible."
|
| 210 |
]
|
| 211 |
},
|
| 212 |
{
|
|
|
|
| 250 |
"### 9. Export to ONNX for the in-browser demo\n",
|
| 251 |
"\n",
|
| 252 |
"Produces `vigneshwar234/Vashtra-0.6B-ONNX`, which the static WebGPU Space loads\n",
|
| 253 |
+
"directly, so the demo costs nothing to host and runs on the visitor's own GPU."
|
| 254 |
]
|
| 255 |
},
|
| 256 |
{
|
|
|
|
| 279 |
"\n",
|
| 280 |
"**Last step:** put the real perplexity number from cell 8 into the model card, and\n",
|
| 281 |
"replace the \"weights are training\" banner. Measured results are what make people\n",
|
| 282 |
+
"trust a small model, so ship them.\n"
|
| 283 |
]
|
| 284 |
}
|
| 285 |
],
|
build_corpus.py
CHANGED
|
@@ -1,16 +1,21 @@
|
|
| 1 |
"""
|
| 2 |
Build the Vashtra training corpus and push it to the Hub.
|
| 3 |
|
| 4 |
-
Runs on Colab
|
| 5 |
-
vigneshwar234/vashtra-ml-corpus:
|
| 6 |
|
| 7 |
-
cpt.jsonl {"text": ...}
|
| 8 |
-
sft.jsonl {"messages": [..]}
|
| 9 |
|
| 10 |
Sources
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
"""
|
| 15 |
import json, random, re, os, collections
|
| 16 |
from datasets import load_dataset
|
|
@@ -20,36 +25,78 @@ OUT = "corpus"
|
|
| 20 |
os.makedirs(OUT, exist_ok=True)
|
| 21 |
REPO = "vigneshwar234/vashtra-ml-corpus"
|
| 22 |
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 26 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 27 |
|
| 28 |
-
|
| 29 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 30 |
ax = load_dataset("CShorten/ML-ArXiv-Papers", split="train")
|
| 31 |
papers, seen_title = [], set()
|
| 32 |
for r in ax:
|
| 33 |
t, a = clean(r.get("title")), clean(r.get("abstract"))
|
| 34 |
-
if not t or not a:
|
| 35 |
continue
|
| 36 |
-
|
|
|
|
| 37 |
continue
|
| 38 |
-
|
| 39 |
-
if key in seen_title:
|
| 40 |
-
continue
|
| 41 |
-
seen_title.add(key)
|
| 42 |
papers.append((t, a))
|
| 43 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 44 |
|
| 45 |
-
with open(f"{OUT}/cpt.jsonl", "w") as fh:
|
| 46 |
-
for t, a in papers:
|
| 47 |
-
fh.write(json.dumps({"text": f"# {t}\n\n{a}"}) + "\n")
|
| 48 |
-
print(f" cpt.jsonl: {len(papers)} docs "
|
| 49 |
-
f"(~{sum(len(t)+len(a) for t, a in papers)/4/1e6:.1f}M tokens)")
|
| 50 |
|
| 51 |
-
#
|
| 52 |
-
# task rather than the template.
|
| 53 |
EXPLAIN = [
|
| 54 |
'Explain the main contribution of the paper "{t}".',
|
| 55 |
'What problem does the paper "{t}" address, and how?',
|
|
@@ -63,7 +110,6 @@ TITLE = [
|
|
| 63 |
"What would this machine-learning paper be titled?\n\n{a}",
|
| 64 |
"Read this abstract and give the paper's title:\n\n{a}",
|
| 65 |
]
|
| 66 |
-
|
| 67 |
sft, stats = [], collections.Counter()
|
| 68 |
for i, (t, a) in enumerate(random.sample(papers, min(26000, len(papers)))):
|
| 69 |
if i % 4 == 0:
|
|
@@ -72,12 +118,12 @@ for i, (t, a) in enumerate(random.sample(papers, min(26000, len(papers)))):
|
|
| 72 |
u, r = random.choice(EXPLAIN).format(t=t), a
|
| 73 |
sft.append({"messages": [{"role": "user", "content": u},
|
| 74 |
{"role": "assistant", "content": r}]})
|
| 75 |
-
stats["
|
| 76 |
-
print(f"
|
| 77 |
|
| 78 |
|
| 79 |
-
# ----------------------------------------------------
|
| 80 |
-
print("[
|
| 81 |
SITES = {
|
| 82 |
"stackexchange-datascience", "stackexchange-ai", "stackexchange-stats",
|
| 83 |
"stackexchange-cs", "stackexchange-cstheory",
|
|
@@ -85,8 +131,7 @@ SITES = {
|
|
| 85 |
"stackexchange-crossvalidated",
|
| 86 |
}
|
| 87 |
se = load_dataset("donfu/oa-stackexchange", split="train", streaming=True)
|
| 88 |
-
seen_q,
|
| 89 |
-
by_site = collections.Counter()
|
| 90 |
for r in se:
|
| 91 |
if r["SOURCE"] not in SITES:
|
| 92 |
continue
|
|
@@ -96,56 +141,70 @@ for r in se:
|
|
| 96 |
q, a = clean(r["INSTRUCTION"]), clean(r["RESPONSE"])
|
| 97 |
if not (40 < len(q) < 1200) or not (80 < len(a) < 1600):
|
| 98 |
continue
|
| 99 |
-
|
| 100 |
-
if
|
| 101 |
continue
|
| 102 |
-
seen_q.add(
|
| 103 |
sft.append({"messages": [{"role": "user", "content": q},
|
| 104 |
{"role": "assistant", "content": a}]})
|
| 105 |
by_site[r["SOURCE"]] += 1
|
| 106 |
-
|
| 107 |
-
stats[
|
| 108 |
-
print(f" StackExchange SFT: {n_se}")
|
| 109 |
for s, c in by_site.most_common():
|
| 110 |
print(f" {c:>6} {s}")
|
| 111 |
|
| 112 |
|
| 113 |
-
# -------------------------------------------------------
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
print("
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 136 |
random.shuffle(sft)
|
| 137 |
with open(f"{OUT}/sft.jsonl", "w") as fh:
|
| 138 |
for r in sft:
|
| 139 |
fh.write(json.dumps(r) + "\n")
|
| 140 |
|
| 141 |
-
print(f"
|
| 142 |
for k, v in stats.most_common():
|
| 143 |
-
print(f"
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 149 |
|
| 150 |
from huggingface_hub import HfApi
|
| 151 |
api = HfApi()
|
|
|
|
| 1 |
"""
|
| 2 |
Build the Vashtra training corpus and push it to the Hub.
|
| 3 |
|
| 4 |
+
Runs on Colab, where network and disk are not a constraint. Produces two files
|
| 5 |
+
in vigneshwar234/vashtra-ml-corpus:
|
| 6 |
|
| 7 |
+
cpt.jsonl {"text": ...} ML text for continued pretraining
|
| 8 |
+
sft.jsonl {"messages": [..]} chat data for instruction tuning
|
| 9 |
|
| 10 |
Sources
|
| 11 |
+
common-pile/arxiv_papers_filtered full-text openly-licensed arXiv papers
|
| 12 |
+
CShorten/ML-ArXiv-Papers 117k arXiv cs.LG titles + abstracts
|
| 13 |
+
donfu/oa-stackexchange ML / stats / CS Q&A, accepted answers
|
| 14 |
+
HuggingFaceTB/smoltalk2 science reasoning, instruction following,
|
| 15 |
+
general chat anchor
|
| 16 |
+
|
| 17 |
+
Full text is the point. Abstracts teach the model what papers claim; full text
|
| 18 |
+
teaches it how the argument is actually made.
|
| 19 |
"""
|
| 20 |
import json, random, re, os, collections
|
| 21 |
from datasets import load_dataset
|
|
|
|
| 25 |
os.makedirs(OUT, exist_ok=True)
|
| 26 |
REPO = "vigneshwar234/vashtra-ml-corpus"
|
| 27 |
|
| 28 |
+
# How much full-text to keep. Each paper is truncated to CHARS_PER_PAPER, so
|
| 29 |
+
# the budget is predictable rather than dominated by a few 80-page papers.
|
| 30 |
+
FULLTEXT_PAPERS = 22000
|
| 31 |
+
CHARS_PER_PAPER = 12000
|
| 32 |
+
ANCHOR_TARGET = 22000
|
| 33 |
+
SCIENCE_TARGET = 30000
|
| 34 |
+
IFEVAL_TARGET = 12000
|
| 35 |
|
| 36 |
+
WS = re.compile(r"\s+")
|
| 37 |
+
def clean(s): return WS.sub(" ", (s or "")).strip()
|
| 38 |
+
|
| 39 |
+
# Terms that mark a paper as machine-learning rather than, say, cosmology.
|
| 40 |
+
ML_TERMS = [
|
| 41 |
+
"neural network", "deep learning", "machine learning", "gradient descent",
|
| 42 |
+
"training data", "transformer", "convolutional", "reinforcement learning",
|
| 43 |
+
"classifier", "supervised", "unsupervised", "embedding", "backpropagation",
|
| 44 |
+
"loss function", "overfitting", "attention mechanism", "language model",
|
| 45 |
+
"generalization", "stochastic gradient", "fine-tuning", "benchmark dataset",
|
| 46 |
+
]
|
| 47 |
|
| 48 |
+
cpt_lines = 0
|
| 49 |
+
cpt_chars = 0
|
| 50 |
+
fh_cpt = open(f"{OUT}/cpt.jsonl", "w")
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
# ------------------------------------------- 1. full-text arXiv (the upgrade)
|
| 54 |
+
print("[1/5] full-text arXiv (common-pile) ...")
|
| 55 |
+
kept = 0
|
| 56 |
+
try:
|
| 57 |
+
ft = load_dataset("common-pile/arxiv_papers_filtered", split="train",
|
| 58 |
+
streaming=True)
|
| 59 |
+
for r in ft:
|
| 60 |
+
txt = r.get("text") or ""
|
| 61 |
+
if len(txt) < 4000:
|
| 62 |
+
continue
|
| 63 |
+
head = txt[:4000].lower()
|
| 64 |
+
score = sum(1 for t in ML_TERMS if t in head)
|
| 65 |
+
if score < 2: # not an ML paper
|
| 66 |
+
continue
|
| 67 |
+
body = clean(txt)[:CHARS_PER_PAPER]
|
| 68 |
+
fh_cpt.write(json.dumps({"text": body}) + "\n")
|
| 69 |
+
cpt_lines += 1; cpt_chars += len(body); kept += 1
|
| 70 |
+
if kept >= FULLTEXT_PAPERS:
|
| 71 |
+
break
|
| 72 |
+
print(f" full-text papers kept: {kept}")
|
| 73 |
+
except Exception as e:
|
| 74 |
+
print(f" full-text SKIPPED ({type(e).__name__}: {e})")
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
# --------------------------------------------------- 2. arXiv cs.LG abstracts
|
| 78 |
+
print("[2/5] arXiv cs.LG abstracts ...")
|
| 79 |
ax = load_dataset("CShorten/ML-ArXiv-Papers", split="train")
|
| 80 |
papers, seen_title = [], set()
|
| 81 |
for r in ax:
|
| 82 |
t, a = clean(r.get("title")), clean(r.get("abstract"))
|
| 83 |
+
if not t or not a or not (300 < len(a) < 2200) or len(t) < 15:
|
| 84 |
continue
|
| 85 |
+
k = t.lower()[:90]
|
| 86 |
+
if k in seen_title:
|
| 87 |
continue
|
| 88 |
+
seen_title.add(k)
|
|
|
|
|
|
|
|
|
|
| 89 |
papers.append((t, a))
|
| 90 |
+
for t, a in papers:
|
| 91 |
+
doc = f"# {t}\n\n{a}"
|
| 92 |
+
fh_cpt.write(json.dumps({"text": doc}) + "\n")
|
| 93 |
+
cpt_lines += 1; cpt_chars += len(doc)
|
| 94 |
+
fh_cpt.close()
|
| 95 |
+
print(f" abstracts: {len(papers)}")
|
| 96 |
+
print(f" cpt.jsonl: {cpt_lines} docs, ~{cpt_chars/4/1e6:.0f}M tokens")
|
| 97 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 98 |
|
| 99 |
+
# ------------------------------------------------ 3. arXiv instruction tasks
|
|
|
|
| 100 |
EXPLAIN = [
|
| 101 |
'Explain the main contribution of the paper "{t}".',
|
| 102 |
'What problem does the paper "{t}" address, and how?',
|
|
|
|
| 110 |
"What would this machine-learning paper be titled?\n\n{a}",
|
| 111 |
"Read this abstract and give the paper's title:\n\n{a}",
|
| 112 |
]
|
|
|
|
| 113 |
sft, stats = [], collections.Counter()
|
| 114 |
for i, (t, a) in enumerate(random.sample(papers, min(26000, len(papers)))):
|
| 115 |
if i % 4 == 0:
|
|
|
|
| 118 |
u, r = random.choice(EXPLAIN).format(t=t), a
|
| 119 |
sft.append({"messages": [{"role": "user", "content": u},
|
| 120 |
{"role": "assistant", "content": r}]})
|
| 121 |
+
stats["arxiv_tasks"] += 1
|
| 122 |
+
print(f"[3/5] arXiv SFT tasks: {stats['arxiv_tasks']}")
|
| 123 |
|
| 124 |
|
| 125 |
+
# ---------------------------------------------------- 4. ML/CS StackExchange
|
| 126 |
+
print("[4/5] StackExchange (ML / stats / CS) ...")
|
| 127 |
SITES = {
|
| 128 |
"stackexchange-datascience", "stackexchange-ai", "stackexchange-stats",
|
| 129 |
"stackexchange-cs", "stackexchange-cstheory",
|
|
|
|
| 131 |
"stackexchange-crossvalidated",
|
| 132 |
}
|
| 133 |
se = load_dataset("donfu/oa-stackexchange", split="train", streaming=True)
|
| 134 |
+
seen_q, by_site = set(), collections.Counter()
|
|
|
|
| 135 |
for r in se:
|
| 136 |
if r["SOURCE"] not in SITES:
|
| 137 |
continue
|
|
|
|
| 141 |
q, a = clean(r["INSTRUCTION"]), clean(r["RESPONSE"])
|
| 142 |
if not (40 < len(q) < 1200) or not (80 < len(a) < 1600):
|
| 143 |
continue
|
| 144 |
+
k = q.lower()[:110]
|
| 145 |
+
if k in seen_q:
|
| 146 |
continue
|
| 147 |
+
seen_q.add(k)
|
| 148 |
sft.append({"messages": [{"role": "user", "content": q},
|
| 149 |
{"role": "assistant", "content": a}]})
|
| 150 |
by_site[r["SOURCE"]] += 1
|
| 151 |
+
stats["stackexchange"] += 1
|
| 152 |
+
print(f" StackExchange SFT: {stats['stackexchange']}")
|
|
|
|
| 153 |
for s, c in by_site.most_common():
|
| 154 |
print(f" {c:>6} {s}")
|
| 155 |
|
| 156 |
|
| 157 |
+
# ---------------------------------------------------------- 5. smoltalk2 mix
|
| 158 |
+
def take(subset, target, label):
|
| 159 |
+
"""Pull `target` conversations from a smoltalk2 subset."""
|
| 160 |
+
n = 0
|
| 161 |
+
try:
|
| 162 |
+
d = load_dataset("HuggingFaceTB/smoltalk2", subset, split="train",
|
| 163 |
+
streaming=True)
|
| 164 |
+
for row in d:
|
| 165 |
+
m = row.get("messages")
|
| 166 |
+
if not m or not (2 <= len(m) <= 6):
|
| 167 |
+
continue
|
| 168 |
+
if sum(len(x["content"]) for x in m) > 4000:
|
| 169 |
+
continue
|
| 170 |
+
if m[0]["role"] not in ("user", "system"):
|
| 171 |
+
continue
|
| 172 |
+
sft.append({"messages": m})
|
| 173 |
+
n += 1
|
| 174 |
+
if n >= target:
|
| 175 |
+
break
|
| 176 |
+
except Exception as e:
|
| 177 |
+
print(f" {label} SKIPPED ({type(e).__name__}: {e})")
|
| 178 |
+
stats[label] = n
|
| 179 |
+
print(f" {label}: {n}")
|
| 180 |
+
return n
|
| 181 |
+
|
| 182 |
+
print("[5/5] smoltalk2 ...")
|
| 183 |
+
take("SFT/Mixture_of_Thoughts_science_no_think", SCIENCE_TARGET, "science_reasoning")
|
| 184 |
+
take("SFT/tulu_3_sft_personas_instruction_following_no_think", IFEVAL_TARGET,
|
| 185 |
+
"instruction_following")
|
| 186 |
+
take("SFT/smoltalk_smollm3_smol_magpie_ultra_no_think", ANCHOR_TARGET, "general_anchor")
|
| 187 |
+
|
| 188 |
+
|
| 189 |
+
# ------------------------------------------------------------------- write
|
| 190 |
+
print("shuffle + write ...")
|
| 191 |
random.shuffle(sft)
|
| 192 |
with open(f"{OUT}/sft.jsonl", "w") as fh:
|
| 193 |
for r in sft:
|
| 194 |
fh.write(json.dumps(r) + "\n")
|
| 195 |
|
| 196 |
+
print(f"sft.jsonl: {len(sft)} examples")
|
| 197 |
for k, v in stats.most_common():
|
| 198 |
+
print(f" {v:>7} {k} ({v/len(sft)*100:.1f}%)")
|
| 199 |
+
|
| 200 |
+
json.dump({"cpt_docs": cpt_lines,
|
| 201 |
+
"cpt_approx_tokens": int(cpt_chars / 4),
|
| 202 |
+
"fulltext_papers": kept,
|
| 203 |
+
"abstracts": len(papers),
|
| 204 |
+
"sft_total": len(sft),
|
| 205 |
+
"sft_breakdown": dict(stats),
|
| 206 |
+
"stackexchange_by_site": dict(by_site)},
|
| 207 |
+
open(f"{OUT}/stats.json", "w"), indent=2)
|
| 208 |
|
| 209 |
from huggingface_hub import HfApi
|
| 210 |
api = HfApi()
|
train_vashtra.py
CHANGED
|
@@ -7,10 +7,15 @@ Vashtra-0.6B - two-stage domain adaptation of Qwen3-0.6B for machine learning.
|
|
| 7 |
Full-parameter fine-tune with 8-bit Adam. At 0.6B this beats LoRA for domain
|
| 8 |
adaptation and still fits a free Colab T4 (~10GB of 16GB).
|
| 9 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
Run:
|
| 11 |
-
python train_vashtra.py
|
| 12 |
-
python train_vashtra.py --stage sft
|
| 13 |
-
python train_vashtra.py --
|
|
|
|
| 14 |
"""
|
| 15 |
import argparse, os, json
|
| 16 |
import torch
|
|
@@ -28,11 +33,16 @@ SEED = 17
|
|
| 28 |
def parse():
|
| 29 |
p = argparse.ArgumentParser()
|
| 30 |
p.add_argument("--stage", choices=["all", "cpt", "sft"], default="all")
|
| 31 |
-
p.add_argument("--
|
|
|
|
|
|
|
|
|
|
| 32 |
p.add_argument("--sft_examples", type=int, default=0, help="0 = all")
|
| 33 |
p.add_argument("--bs", type=int, default=8)
|
| 34 |
p.add_argument("--accum", type=int, default=4)
|
| 35 |
p.add_argument("--sft_epochs", type=float, default=2.0)
|
|
|
|
|
|
|
| 36 |
p.add_argument("--no_push", action="store_true")
|
| 37 |
return p.parse_args()
|
| 38 |
|
|
@@ -92,9 +102,15 @@ def load_model(path):
|
|
| 92 |
return m
|
| 93 |
|
| 94 |
|
| 95 |
-
def targs(out, epochs, lr, args):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 96 |
return TrainingArguments(
|
| 97 |
output_dir=out,
|
|
|
|
| 98 |
per_device_train_batch_size=args.bs,
|
| 99 |
gradient_accumulation_steps=args.accum,
|
| 100 |
num_train_epochs=epochs,
|
|
@@ -117,12 +133,38 @@ def targs(out, epochs, lr, args):
|
|
| 117 |
)
|
| 118 |
|
| 119 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 120 |
# --------------------------------------------------------------- Stage A: CPT
|
| 121 |
def stage_cpt(args, out="vashtra-cpt"):
|
| 122 |
ds = load_dataset(CORPUS, data_files="cpt.jsonl", split="train")
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
print(f"[cpt] documents: {len(ds)}")
|
| 126 |
|
| 127 |
eos = tok.eos_token_id
|
| 128 |
|
|
@@ -142,11 +184,23 @@ def stage_cpt(args, out="vashtra-cpt"):
|
|
| 142 |
|
| 143 |
ds = ds.map(pack, batched=True, batch_size=2000,
|
| 144 |
remove_columns=ds.column_names, num_proc=2)
|
| 145 |
-
print(f"[cpt]
|
| 146 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 147 |
model = load_model(BASE_MODEL)
|
| 148 |
-
Trainer(model=model, args=targs(out, 1, 5e-5, args),
|
| 149 |
-
train_dataset=ds, data_collator=pad_collate
|
|
|
|
| 150 |
model.save_pretrained(out); tok.save_pretrained(out)
|
| 151 |
print(f"[cpt] saved -> {out}")
|
| 152 |
return out
|
|
@@ -164,9 +218,13 @@ def stage_sft(args, init_path, out="vashtra-sft"):
|
|
| 164 |
ds = ds.filter(lambda r: any(l != -100 for l in r["labels"]), num_proc=2)
|
| 165 |
print(f"[sft] with supervised tokens: {len(ds)}")
|
| 166 |
|
|
|
|
|
|
|
| 167 |
model = load_model(init_path)
|
| 168 |
-
Trainer(model=model, args=targs(out, args.sft_epochs, 1e-5, args
|
| 169 |
-
|
|
|
|
|
|
|
| 170 |
model.save_pretrained(out); tok.save_pretrained(out)
|
| 171 |
print(f"[sft] saved -> {out}")
|
| 172 |
return out
|
|
|
|
| 7 |
Full-parameter fine-tune with 8-bit Adam. At 0.6B this beats LoRA for domain
|
| 8 |
adaptation and still fits a free Colab T4 (~10GB of 16GB).
|
| 9 |
|
| 10 |
+
The corpus is larger than one free Colab session can train on, so stage A takes
|
| 11 |
+
a token budget. Checkpoints are pushed to the Hub, so --resume continues a run
|
| 12 |
+
that was cut short in an earlier session.
|
| 13 |
+
|
| 14 |
Run:
|
| 15 |
+
python train_vashtra.py # both stages, push to Hub
|
| 16 |
+
python train_vashtra.py --stage sft # SFT only, from base
|
| 17 |
+
python train_vashtra.py --cpt_tokens 60000000 # longer stage A
|
| 18 |
+
python train_vashtra.py --resume # continue a dropped run
|
| 19 |
"""
|
| 20 |
import argparse, os, json
|
| 21 |
import torch
|
|
|
|
| 33 |
def parse():
|
| 34 |
p = argparse.ArgumentParser()
|
| 35 |
p.add_argument("--stage", choices=["all", "cpt", "sft"], default="all")
|
| 36 |
+
p.add_argument("--cpt_tokens", type=int, default=30_000_000,
|
| 37 |
+
help="token budget for stage A; 0 = use the whole corpus. "
|
| 38 |
+
"The corpus is ~90M tokens, which is more than one free "
|
| 39 |
+
"Colab session can finish. 30M is about 2 hours on a T4.")
|
| 40 |
p.add_argument("--sft_examples", type=int, default=0, help="0 = all")
|
| 41 |
p.add_argument("--bs", type=int, default=8)
|
| 42 |
p.add_argument("--accum", type=int, default=4)
|
| 43 |
p.add_argument("--sft_epochs", type=float, default=2.0)
|
| 44 |
+
p.add_argument("--resume", action="store_true",
|
| 45 |
+
help="resume from the last checkpoint pushed to the Hub")
|
| 46 |
p.add_argument("--no_push", action="store_true")
|
| 47 |
return p.parse_args()
|
| 48 |
|
|
|
|
| 102 |
return m
|
| 103 |
|
| 104 |
|
| 105 |
+
def targs(out, epochs, lr, args, hub_id=None):
|
| 106 |
+
"""Checkpoints go to the Hub so a dropped Colab session can be resumed."""
|
| 107 |
+
hub = {}
|
| 108 |
+
if hub_id and not args.no_push:
|
| 109 |
+
hub = dict(push_to_hub=True, hub_model_id=hub_id,
|
| 110 |
+
hub_strategy="checkpoint", hub_private_repo=False)
|
| 111 |
return TrainingArguments(
|
| 112 |
output_dir=out,
|
| 113 |
+
**hub,
|
| 114 |
per_device_train_batch_size=args.bs,
|
| 115 |
gradient_accumulation_steps=args.accum,
|
| 116 |
num_train_epochs=epochs,
|
|
|
|
| 133 |
)
|
| 134 |
|
| 135 |
|
| 136 |
+
def fetch_checkpoint(hub_id, out):
|
| 137 |
+
"""Pull `last-checkpoint` from the Hub into out/ so training can resume.
|
| 138 |
+
|
| 139 |
+
Trainer's resume_from_checkpoint only looks on local disk. A fresh Colab
|
| 140 |
+
session has none, so without this a dropped run restarts from zero.
|
| 141 |
+
Returns the local checkpoint path, or None if there is nothing to resume.
|
| 142 |
+
"""
|
| 143 |
+
import glob
|
| 144 |
+
from huggingface_hub import snapshot_download
|
| 145 |
+
|
| 146 |
+
local = glob.glob(os.path.join(out, "checkpoint-*")) or \
|
| 147 |
+
glob.glob(os.path.join(out, "last-checkpoint"))
|
| 148 |
+
if local:
|
| 149 |
+
print(f"[resume] using local checkpoint {local[0]}")
|
| 150 |
+
return sorted(local)[-1]
|
| 151 |
+
try:
|
| 152 |
+
p = snapshot_download(repo_id=hub_id, allow_patterns="last-checkpoint/*",
|
| 153 |
+
local_dir=out)
|
| 154 |
+
p = os.path.join(p, "last-checkpoint")
|
| 155 |
+
if os.path.isdir(p):
|
| 156 |
+
print(f"[resume] downloaded checkpoint from {hub_id}")
|
| 157 |
+
return p
|
| 158 |
+
except Exception as e:
|
| 159 |
+
print(f"[resume] nothing to resume from {hub_id} ({type(e).__name__})")
|
| 160 |
+
return None
|
| 161 |
+
|
| 162 |
+
|
| 163 |
# --------------------------------------------------------------- Stage A: CPT
|
| 164 |
def stage_cpt(args, out="vashtra-cpt"):
|
| 165 |
ds = load_dataset(CORPUS, data_files="cpt.jsonl", split="train")
|
| 166 |
+
ds = ds.shuffle(seed=SEED) # mix full-text papers and abstracts together
|
| 167 |
+
print(f"[cpt] documents available: {len(ds)}")
|
|
|
|
| 168 |
|
| 169 |
eos = tok.eos_token_id
|
| 170 |
|
|
|
|
| 184 |
|
| 185 |
ds = ds.map(pack, batched=True, batch_size=2000,
|
| 186 |
remove_columns=ds.column_names, num_proc=2)
|
| 187 |
+
print(f"[cpt] corpus packs to {len(ds)} blocks (~{len(ds)*SEQ/1e6:.0f}M tokens)")
|
| 188 |
+
|
| 189 |
+
if args.cpt_tokens:
|
| 190 |
+
keep = min(len(ds), args.cpt_tokens // SEQ)
|
| 191 |
+
if keep < len(ds):
|
| 192 |
+
print(f"[cpt] budget {args.cpt_tokens/1e6:.0f}M tokens -> training on "
|
| 193 |
+
f"{keep} of {len(ds)} blocks. Raise --cpt_tokens (or set 0) to "
|
| 194 |
+
f"use the whole corpus over multiple sessions.")
|
| 195 |
+
ds = ds.select(range(keep))
|
| 196 |
+
print(f"[cpt] training on {len(ds)} blocks (~{len(ds)*SEQ/1e6:.1f}M tokens)")
|
| 197 |
+
|
| 198 |
+
ckpt_repo = f"{HUB_MODEL}-cpt-ckpt"
|
| 199 |
+
resume = fetch_checkpoint(ckpt_repo, out) if args.resume else None
|
| 200 |
model = load_model(BASE_MODEL)
|
| 201 |
+
Trainer(model=model, args=targs(out, 1, 5e-5, args, hub_id=ckpt_repo),
|
| 202 |
+
train_dataset=ds, data_collator=pad_collate
|
| 203 |
+
).train(resume_from_checkpoint=resume)
|
| 204 |
model.save_pretrained(out); tok.save_pretrained(out)
|
| 205 |
print(f"[cpt] saved -> {out}")
|
| 206 |
return out
|
|
|
|
| 218 |
ds = ds.filter(lambda r: any(l != -100 for l in r["labels"]), num_proc=2)
|
| 219 |
print(f"[sft] with supervised tokens: {len(ds)}")
|
| 220 |
|
| 221 |
+
ckpt_repo = f"{HUB_MODEL}-sft-ckpt"
|
| 222 |
+
resume = fetch_checkpoint(ckpt_repo, out) if args.resume else None
|
| 223 |
model = load_model(init_path)
|
| 224 |
+
Trainer(model=model, args=targs(out, args.sft_epochs, 1e-5, args,
|
| 225 |
+
hub_id=ckpt_repo),
|
| 226 |
+
train_dataset=ds, data_collator=pad_collate
|
| 227 |
+
).train(resume_from_checkpoint=resume)
|
| 228 |
model.save_pretrained(out); tok.save_pretrained(out)
|
| 229 |
print(f"[sft] saved -> {out}")
|
| 230 |
return out
|