File size: 5,346 Bytes
ffd7a98 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 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 136 137 138 | # Agri-SLM — inference
A 134M-parameter Qwen3-style decoder trained from scratch on Indian agriculture
text (11.8B tokens, 3 epochs). Two variants differing only in tokenizer.
`agri_slm.py` is self-contained: it needs `torch` and `tokenizers` only, and has
no dependency on the training code. It is verified bit-for-bit identical to the
training implementation.
## Install
```bash
pip install torch tokenizers
pip install huggingface_hub # only if loading from the Hub
```
## Run
```bash
# download and generate
python agri_slm.py --prompt "Rice blast disease is caused by"
# interactive, streaming
python agri_slm.py --interactive
# local files
python agri_slm.py --ckpt final.pt --tokenizer tokenizer_final.json --interactive
```
As a library:
```python
from agri_slm import AgriSLM
slm = AgriSLM.from_pretrained("luffy19/custom_tokenizer")
print(slm.generate("Integrated pest management in cotton involves"))
# or from local files
slm = AgriSLM.from_files("final.pt", "tokenizer_final.json")
```
## Decoding — read this before reporting results
**This model loops badly under greedy decoding.** That is a property of greedy
search on small base models, not a defect in the weights: `argmax` has no way out
of a high-probability cycle. A repetition penalty fixes it completely.
Measured over 300-token generations on 4 prompts:
| setting | repeat-4gram | longest verbatim loop | distinct-2 | sentences well-formed |
|---|---|---|---|---|
| greedy, no penalty | 0.636 | **37 tokens** | 0.285 | 91% |
| greedy + rep 1.2 | 0.023 | 0 | 0.884 | 84% |
| greedy + rep 1.2 + no-repeat-4gram | 0.000 | 0 | 0.939 | 78% |
| T 0.7 | 0.173 | 0 | 0.633 | 89% |
| T 0.7 + rep 1.15 | 0.011 | 0.8 | 0.919 | 80% |
| T 0.9 + rep 1.15 + ng4 | 0.000 | 0 | 0.902 | 85% |
| **T 1.0 + rep 1.1** | **0.006** | **0** | **0.910** | **87%** |
A repetition penalty alone takes greedy from a 37-token loop to essentially zero
repetition — a 27x reduction.
### Presets
```bash
python agri_slm.py --preset balanced # T=1.0 top_p=0.95 rep=1.10 (default)
python agri_slm.py --preset safe # T=0.9 top_p=0.95 rep=1.15 ngram=4 (zero loops)
python agri_slm.py --preset focused # T=0.7 top_p=0.90 rep=1.15 (deterministic-ish)
python agri_slm.py --preset greedy # T=0 rep=1.20 ngram=3 (reproducible)
python agri_slm.py --preset raw-greedy # no penalty — demonstrates the loop
```
Any preset value can be overridden: `--temperature`, `--top-k`, `--top-p`,
`--repetition-penalty`, `--no-repeat-ngram`.
**`balanced` is the recommended default.** Use `safe` when you need a hard
guarantee of no repetition; it costs a little naturalness. `raw-greedy` exists
only to reproduce the failure.
## Models
| repo | tokenizer | notes |
|---|---|---|
| `luffy19/custom_tokenizer` | hybrid 40k (KG + AGROVOC entity injection) | `hybrid40k/final.pt` |
| `luffy19/agri-slm_qwen40k` | Qwen BPE pruned 151k→40k | `qwen40k/final.pt` |
Both: 133,476,480 params, 40,000 vocab, 20 layers, 640-dim, GQA (10 heads /
5 KV groups), SwiGLU, RMSNorm, RoPE base 1e6, rank-256 factorized embedding tied
to the output head.
### Measured differences
On 2,000 identical held-out documents (3.72M characters):
| metric | Qwen 40k | Hybrid 40k | delta |
|---|---|---|---|
| bits per character | 0.9208 | **0.9168** | **−0.43%** |
| fertility (tok/1k chars) | 229.16 | **212.76** | **−7.16%** |
| tokens for same text | 853,383 | **792,312** | **−7.16%** |
| agriculture probe | 14/15 | **15/15** | +1 |
| perplexity | 16.20 | 19.82 | *not comparable* |
**Do not compare the two models by perplexity.** It is per-token, and the hybrid's
tokens each carry ~7% more text, so it is predicting harder units. Bits-per-char
normalises this and reverses the apparent ordering. The 7.16% fertility gain is
the large, robust effect; the 0.43% BPC difference is small and comes from single
runs with one seed, so treat it as directional rather than significant.
## What this model is and is not
It is a **base language model**: it continues text. It is not instruction-tuned
and does not answer questions. Prompt it with sentence openings a textbook would
contain ("Rice blast disease is caused by"), not questions ("What causes rice
blast?").
Known limitations, all measured:
- **Confidently wrong on some facts.** It defined Minimum Support Price as "a
measure of the quality of a product", which is incorrect. Domain-term recall is
strong (14–15 of 15 probes) but definitions can be wrong.
- **Invents citations.** ~11% of the training corpus is citation-dense academic
text, so it produces plausible-looking references like "(Khan et al., 2015)"
that may not exist.
- **Drifts into thesis format.** ~13% of the corpus is dissertation text, so long
generations can wander into "REVIEW OF LITERATURE / Chapter II".
- **Reproduces a PDF extraction artifact.** ~1% of training documents contain
doubled letters from bad PDF extraction ("RREEVVIIEEWW"); the model occasionally
emits this.
- **No KV cache.** Generation recomputes the full sequence each step, so cost
grows with length. Fine to a few hundred tokens; slow beyond that.
Do not use its output as agricultural advice without verification.
## Performance
~25 tokens/sec on an H200 at ~1 GB in bf16. Runs on CPU (fp32) at a few tokens
per second.
|