Instructions to use anandkaman/gemma3_12b_deedexp_te with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- PEFT
How to use anandkaman/gemma3_12b_deedexp_te with PEFT:
from peft import PeftModel from transformers import AutoModelForCausalLM base_model = AutoModelForCausalLM.from_pretrained("unsloth/gemma-3-12b-it-bnb-4bit") model = PeftModel.from_pretrained(base_model, "anandkaman/gemma3_12b_deedexp_te") - Notebooks
- Google Colab
- Kaggle
Telugu (Andhra Pradesh) property-document extraction β Gemma 3 12B QLoRA
A LoRA adapter for Gemma 3 12B Instruct that reads the OCR of an Andhra Pradesh property document and returns one JSON object: the parties and their identifiers, the property, the amounts and the registration details.
Sibling of the Kannada model anandkaman/gemma3_12b_deedexp. This is a separate specialist for AP documents, not a multi-state model β the two are trained apart on purpose, because the document language, the fee table and the role vocabulary differ.
Measured accuracy
11 held-out documents, none seen in training, scored against labels derived from the documents themselves rather than from the accompanying register.
| result | |
|---|---|
| buyer/seller role swaps | 0 |
| buyer count exact | 11/11 |
| seller count exact | 11/11 |
| sale consideration exact | 11/11 |
| registration date exact | 11/11 |
| registration office exact | 11/11 |
| state exact | 11/11 |
| stamp duty exact | 9/11 |
| registration fee exact | 9/11 |
| party names recovered | 24/32 |
| output failed to parse as JSON | 0/11 |
Read the limits with the numbers. n = 11, against the Kannada model's 60. These are the harder documents by construction β they were held out for length, and length tracks party count β and 6 of the 11 prompts still exceed the 11k-token training window, so part of the OCR is truncated at generation and the model still recovers the fields above.
Two fields score 0 and are not model weaknesses: pincode and the schedule address. Both have an
unsettled labelling convention in the source register (the pincode values trace to an architect's
letterhead, a sub-registrar's address and a tax receipt, among others), so neither can be scored until
the convention is fixed. The address field recovers ~0.4 of the gold's tokens while scoring 0 on exact
match.
Prompts β full and subset
Three prompts are trained. All three use the same rules block; they differ in one sentence that names the schema to return.
| file | returns |
|---|---|
PROMPT_full.txt |
everything: parties, property, document details |
PROMPT_subset_1.txt |
parties only β buyer, seller, confirming party |
PROMPT_subset_2.txt |
property and registration details only |
Where the saving is. The subset prompts are not shorter on input β they are 66 characters longer. The saving is in the output: a subset answer averages 465 characters against 1017 for the full schema, 46 % of the size. Because decoding is sequential, that is roughly a 2x faster call when you only need one half of the record. Use a subset prompt when you only need parties, or only need the property and amounts; it is not a way to shorten the input.
18 % of the training rows are subset-schema variants, which also lifts per-field accuracy on the full schema rather than only enabling the short form.
Use
from unsloth import FastLanguageModel
from unsloth.chat_templates import get_chat_template
model, tok = FastLanguageModel.from_pretrained(
"anandkaman/gemma3_12b_deedexp_te", max_seq_length=11008, load_in_4bit=True)
FastLanguageModel.for_inference(model)
tok = get_chat_template(tok, chat_template="gemma-3")
rules = open("PROMPT_full.txt").read() # or PROMPT_subset_1.txt / _2.txt
prompt = rules + "\n\nOCR:\n" + ocr_text
text = tok.apply_chat_template(
[{"role": "user", "content": [{"type": "text", "text": prompt}]}],
tokenize=False, add_generation_prompt=True)
ids = tok(text, return_tensors="pt", add_special_tokens=False).input_ids.to("cuda")
out = model.generate(input_ids=ids, max_new_tokens=2600, do_sample=False)
print(tok.decode(out[0][ids.shape[1]:], skip_special_tokens=True))
Greedy decoding. Trained at max_seq_length=11008; longer OCR should be cut from the middle,
keeping the instruction block and the start and end of the document.
Runs in about 11.4 GB of VRAM in 4-bit.
Design rules it was trained to follow
- Roles come from what a party does, never from where it appears on the page. The registration endorsement states the role directly (executant = seller, claimant = buyer, witness = neither).
- A witness is never a party, and a power-of-attorney holder is not a party either.
- People named in recited earlier transactions are not parties to this sale.
- Missing data is acceptable; invented data is a critical error. Any field the document does not
carry is
null. A malformed identifier isnullrather than a guess.
Base model, quantization and merging
Read MERGE.md before merging. The essentials:
| base | unsloth/gemma-3-12b-it-bnb-4bit β bitsandbytes NF4, double quant, bf16 compute. Not the dynamic -unsloth-bnb-4bit variant |
| identity | verified by content hash against the Hub: config.json blob ce6ea5ebβ¦, both shards byte-identical in size |
| adapter | plain LoRA, r=16, alpha=32 β merge scaling 2.0; no DoRA / rsLoRA / QALoRA, no modules_to_save |
| adapted | 417 modules = 336 text (48 layers x 7 projections) + 81 vision (27 SigLIP layers x q/k/v) |
| merge target | bf16 unsloth/gemma-3-12b-it (google/gemma-3-12b-it is gated) |
| lineage | three phases, each loading the previous phase's weights into a fresh same-rank LoRA. This adapter is self-contained β there is no second adapter to stack |
Three things that will bite you, all covered in MERGE.md:
- Attach to
Gemma3ForConditionalGeneration, not the text-only class. 81 of the 417 adapted modules are in the SigLIP vision tower, matched by name. A text-only load leaves them unresolved. - Key layout. The adapter was saved from the
model.language_model.layers.*module tree; the Hub checkpoints still uselanguage_model.model.layers.*on disk. Verified 417/417 resolved on transformers 5.5.0; runcheck_adapter_keys.pyon your own stack first β it builds the model on the meta device, so it costs no download and no GPU. - A bf16 merge is not the model that was evaluated. The accuracy table above was measured with this adapter on the 4-bit base. The LoRA was fitted against NF4-dequantized weights, so merging into bf16 is the standard QLoRA path but not numerically identical β re-score after merging. For exactly the evaluated model, do not merge: attach the adapter to the 4-bit base.
Two scripts ship here: check_adapter_keys.py (seconds, no weights) and
merge_lora_streaming.py, which merges shard by shard so it runs on a 16 GB machine, refuses an
ambiguous key mapping, and spot-checks the arithmetic afterwards.
Training
QLoRA (rank 16, alpha 32) on unsloth/gemma-3-12b-it-bnb-4bit, 49 documents / 60 rows, 36 steps,
built on a Telugu-adapted base. max_seq_length=11008, batch 1 x grad-accum 8, lr 2e-4 cosine,
warmup_ratio 0.03, weight_decay 0.01, adamw_8bit, seed 42, packing=False, bf16, unsloth
offloading gradient checkpointing, on one 15.478 GiB RTX 5060 Ti. Stack: unsloth 2026.5.2 /
torch 2.10.0 / transformers 5.5.0 / trl 0.24.0 / peft 0.19.1 / bitsandbytes 0.49.2. Labels were derived from each document's own registration endorsement rather than
from the accompanying register β correcting 11 buyer/seller role swaps and removing 5 identifying
witnesses that a register-derived label set had recorded as parties.
No document text, identifier or party name from the training corpus is published here.
Contact: kamananand98@gmail.com
- Downloads last month
- -
Model tree for anandkaman/gemma3_12b_deedexp_te
Base model
google/gemma-3-12b-pt