Debarun12 commited on
Commit
39a970e
Β·
verified Β·
1 Parent(s): da899b6

Update README.md

Browse files
Files changed (1) hide show
  1. README.md +138 -0
README.md CHANGED
@@ -1,3 +1,141 @@
1
  ---
2
  license: apache-2.0
3
  ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
  license: apache-2.0
3
  ---
4
+ ---
5
+ license: apache-2.0
6
+ base_model: Qwen/Qwen2.5-3B
7
+ tags:
8
+ - qwen2
9
+ - java
10
+ - qlora
11
+ - domain-adaptation
12
+ - code
13
+ - fine-tuned
14
+ language:
15
+ - en
16
+ library_name: transformers
17
+ ---
18
+
19
+ # JavaExpert-Qwen2.5-3B
20
+
21
+ **Domain-locked Java QA via QLoRA on consumer hardware.**
22
+
23
+ Fine-tuned from `Qwen/Qwen2.5-3B` on 7,921 Java QA pairs using QLoRA β€” trained entirely on a single NVIDIA RTX 5050 (8 GB VRAM), no cloud, no A100s. The model answers Java questions accurately and refuses everything else, without external guardrails.
24
+
25
+ ## Evaluation Results
26
+
27
+ | Metric | Target | Achieved |
28
+ |--------|--------|----------|
29
+ | Validation Perplexity | < 10 | **2.40** |
30
+ | Peak Training VRAM | ≀ 8 GB | **< 7 GB** |
31
+ | Inference VRAM | β€” | **< 2 GB** |
32
+ | Java Correctness | β€” | **8.5 / 10** |
33
+ | Domain Restriction | β€” | **8.5 / 10** |
34
+ | Hallucination Control | β€” | **8.0 / 10** |
35
+
36
+ ## Quick Start
37
+
38
+ ```python
39
+ from transformers import AutoModelForCausalLM, AutoTokenizer
40
+
41
+ model = AutoModelForCausalLM.from_pretrained("Debarun12/JavaExpert-Qwen2.5-3B")
42
+ tokenizer = AutoTokenizer.from_pretrained("Debarun12/JavaExpert-Qwen2.5-3B")
43
+
44
+ messages = [
45
+ {"role": "system", "content": "You are JavaExpert, a Java programming assistant."},
46
+ {"role": "user", "content": "What is the difference between HashMap and LinkedHashMap in Java?"}
47
+ ]
48
+
49
+ input_ids = tokenizer.apply_chat_template(messages, return_tensors="pt")
50
+ output = model.generate(input_ids, max_new_tokens=300)
51
+ print(tokenizer.decode(output[0], skip_special_tokens=True))
52
+ ```
53
+
54
+ ## Domain Restriction β€” What It Does
55
+
56
+ The model was trained with explicit negative examples (unanswerable/out-of-domain questions), so it refuses non-Java queries without any post-processing filter:
57
+
58
+ | Query | Response |
59
+ |-------|----------|
60
+ | What is the capital of France? | Refuses cleanly |
61
+ | Who won the FIFA World Cup? | Refuses cleanly |
62
+ | How do I train a neural network in Python? | Partial refusal (known limitation β€” see below) |
63
+
64
+ ## Training Details
65
+
66
+ **Base model:** `Qwen/Qwen2.5-3B` β€” selected over the 7B (OOM risk on 8 GB) and 1.5B (higher hallucination tendency).
67
+
68
+ **Method:** QLoRA β€” base model quantized to 4-bit NF4 (~1.9 GB), adapters trained in bf16.
69
+
70
+ **Hardware:** NVIDIA GeForce RTX 5050, 8 GB VRAM. Peak usage < 7 GB.
71
+
72
+ ### Memory Optimisation Decisions
73
+
74
+ Every config choice was driven by measured VRAM impact:
75
+
76
+ | Component | Initial | Final | VRAM Saved |
77
+ |-----------|---------|-------|------------|
78
+ | LoRA rank | 32 across 7 modules | 16 on q_proj, v_proj | βˆ’1.5 GB |
79
+ | Batch size | 4 | 1 + grad accum Γ—8 | βˆ’2.0 GB |
80
+ | Optimizer | AdamW | Adafactor | βˆ’1.0 GB |
81
+ | Compute dtype | fp16 | bf16 | Stable on Blackwell |
82
+
83
+ ### Hyperparameters
84
+
85
+ ```
86
+ per_device_train_batch_size = 1
87
+ gradient_accumulation_steps = 8 # effective batch = 8
88
+ optim = "adafactor"
89
+ bf16 = True
90
+ learning_rate = 2e-4
91
+ num_train_epochs = 3
92
+ lora_r = 16
93
+ lora_target_modules = ["q_proj", "v_proj"]
94
+ ```
95
+
96
+ ### Training Loss
97
+
98
+ | Epoch | Train Loss | Val Loss | Val Perplexity |
99
+ |-------|-----------|----------|----------------|
100
+ | 1 | 0.8511 | 0.9343 | 2.55 |
101
+ | 2 | 0.8367 | 0.8839 | 2.42 |
102
+ | 3 | 0.7780 | 0.8772 | **2.40** |
103
+
104
+ Validation loss decreased monotonically. No overfitting across 3 epochs β€” the train/val gap remained narrow and stable throughout.
105
+
106
+ ## Dataset
107
+
108
+ Built from scratch β€” not an off-the-shelf dataset:
109
+
110
+ - **Source:** 42,000+ lines of Java documentation, extracted from PDFs
111
+ - **Pipeline:** Custom preprocessing β†’ sliding-window chunking (150 words, 30-word overlap) β†’ LLM-assisted QA generation via `qwen2.5:7b`
112
+ - **QA pairs:** 7,921 chat-formatted (system + user + assistant)
113
+ - **Question types:** 36% definitional / 32% procedural / 32% reasoning + explicit unanswerable/refusal examples
114
+ - **Estimated cleanliness:** 95%+
115
+
116
+ Dataset: [Debarun12/JavaExpert-Qwen2.5-3B-DATASET](https://huggingface.co/datasets/Debarun12/JavaExpert-Qwen2.5-3B-DATASET)
117
+
118
+ ## Bugs Found and Fixed During Training
119
+
120
+ **SFTTrainer step-count explosion** β€” `packing=True` with pre-tokenized input caused 2,139 steps on 90 samples (~24Γ— expected). Root cause: SFTTrainer misinterprets packed sequence lengths. Fix: replaced with standard `Trainer` + `DataCollatorForLanguageModeling`. Steps normalized to 2,673 across 3 epochs.
121
+
122
+ **PyTorch 2.6 checkpoint incompatibility** β€” `weights_only=True` default change raised `UnpicklingError` on resume. Fix: automated removal of `rng_state.pth` from checkpoint directories before resumption.
123
+
124
+ ## Limitations
125
+
126
+ - **Domain-adjacent leakage:** Python and general algorithm queries sometimes receive partial responses rather than clean refusals. The boundary between the Java corpus and the 3B's pretraining knowledge on general programming topics is not perfectly sharp.
127
+ - **Corpus grounding:** The model answers Java questions correctly but draws on pretraining generalisation rather than reproducing specific training corpus facts β€” a fundamental limitation of generative fine-tuning without retrieval.
128
+
129
+ **Recommended next steps:** RAG layer over the source corpus for strict factual grounding; DPO with curated refusal preference pairs to sharpen the domain boundary on adjacent technical topics.
130
+
131
+ ## Production Deployment
132
+
133
+ LoRA adapters were merged into base weights via `merge_and_unload()` β€” this is a single self-contained checkpoint with no adapter dependency at runtime. Inference runs at under 2 GB VRAM.
134
+
135
+ ## Repository
136
+
137
+ Training notebooks and dataset generation pipeline: [GitHub β€” debarun23](https://github.com/debarun23)
138
+
139
+ ---
140
+
141
+ *Trained on NVIDIA GeForce RTX 5050 (8 GB VRAM) Β· June 2026 Β· Author: Debarun Das*