初始化项目,由ModelHub XC社区提供模型
Model: Kotichitturu/slm-125m-sft-raft Source: Original Platform
This commit is contained in:
35
.gitattributes
vendored
Normal file
35
.gitattributes
vendored
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
*.7z filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.arrow filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.bin filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.ftz filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.gz filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.h5 filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.joblib filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.model filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.npy filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.npz filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.onnx filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.ot filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.parquet filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.pb filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.pickle filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.pkl filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.pt filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.pth filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.rar filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
||||||
|
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.tar filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.tflite filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.tgz filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.wasm filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.xz filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.zip filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.zst filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
||||||
175
README.md
Normal file
175
README.md
Normal file
@@ -0,0 +1,175 @@
|
|||||||
|
---
|
||||||
|
language: en
|
||||||
|
license: apache-2.0
|
||||||
|
base_model: Kotichitturu/slm-125m-base
|
||||||
|
tags:
|
||||||
|
- legal
|
||||||
|
- financial
|
||||||
|
- small-language-model
|
||||||
|
- llama
|
||||||
|
- sft
|
||||||
|
- raft
|
||||||
|
- rag
|
||||||
|
---
|
||||||
|
|
||||||
|
# SLM-125M SFT (RAFT — grounded)
|
||||||
|
|
||||||
|
Answers legal/financial questions **only from a context passage you supply**, and
|
||||||
|
**refuses when the answer isn't in it**. Trained with RAFT (Retrieval-Augmented
|
||||||
|
Fine-Tuning): 20% of training examples had no answer in the context, teaching the
|
||||||
|
model that "I don't know" is a correct response.
|
||||||
|
|
||||||
|
## The number that matters
|
||||||
|
|
||||||
|
A model that never refuses can post a great loss and be useless — it hallucinates
|
||||||
|
exactly when you most need it not to. So refusal is measured as a confusion
|
||||||
|
matrix, because refusal rate alone is gameable: a model that refuses *everything*
|
||||||
|
scores 100% recall and is worthless.
|
||||||
|
|
||||||
|
```
|
||||||
|
answer ABSENT -> refused : 88/91 (96.7%) <- the point of RAFT
|
||||||
|
answer ABSENT -> answered : 3/91 ( 3.3%) <- hallucination
|
||||||
|
answer PRESENT -> refused : 7/394 ( 1.8%) <- over-refusal
|
||||||
|
answer PRESENT -> answered : 387/394
|
||||||
|
overall accuracy: 97.9%
|
||||||
|
```
|
||||||
|
|
||||||
|
**Both halves matter.** 96.7% refusal recall would be worthless from a model that
|
||||||
|
refuses everything — over-refusal is only 1.8%. It refuses when it should and
|
||||||
|
answers when it should.
|
||||||
|
|
||||||
|
### ⚠️ What the 97.9% does NOT say — read this before trusting an answer
|
||||||
|
|
||||||
|
**It measures whether the model REFUSES when it should. Not whether the answer it
|
||||||
|
gives is correct.** Those are different claims, and only the first is in that
|
||||||
|
number. A confidently wrong answer on an answer-present row scores in the
|
||||||
|
*correct* cell of the matrix above, leaving 97.9% untouched.
|
||||||
|
|
||||||
|
Measured separately on the 394 answer-present validation rows:
|
||||||
|
|
||||||
|
| Metric | Result |
|
||||||
|
|---|---|
|
||||||
|
| Exact match (normalized) | 1.8% |
|
||||||
|
| Token F1 vs gold answer | 46.3% |
|
||||||
|
| Numeric match (all gold numbers reproduced) | 33.7% |
|
||||||
|
| Prediction contained **no number at all** | **24.3%** |
|
||||||
|
| **Single-number answers — the clean test** | **37.0%** |
|
||||||
|
|
||||||
|
**When exactly one figure is being asked for, this model produces it 37% of the
|
||||||
|
time.** No truncation excuse, no metric strictness — it fails nearly two times in
|
||||||
|
three. The dominant failure is **omission**: it hedges into fluent, grounded-
|
||||||
|
sounding, figure-free prose. Asked *"what specific article of the Code of Criminal
|
||||||
|
Procedure?"* it replied *"the article of the Code of Criminal Procedure states that
|
||||||
|
the trial judge is re…"* — dodging the number entirely.
|
||||||
|
|
||||||
|
**Why this coexists with 96.7% refusal:** refusal is a coarse binary judgement
|
||||||
|
(*does this passage address the question at all?*). Extraction demands precision
|
||||||
|
(*which figure, exactly?*). A 125M model learns the first and not the second.
|
||||||
|
|
||||||
|
**What that means for you:** trust this model to tell you when an answer **isn't**
|
||||||
|
in the passage. Do **not** trust the specific figures it reports without checking
|
||||||
|
them against the passage yourself. It is a triage tool, not an extraction tool.
|
||||||
|
|
||||||
|
*(Caveat: gold answers are teacher-generated and judge-filtered — a proxy for
|
||||||
|
truth. Some numeric misses are defensible. The 37% is not.)*
|
||||||
|
|
||||||
|
## Val loss and the alignment tax
|
||||||
|
|
||||||
|
Val loss 1.9551 → **0.9286** (−52%), best at epoch 3. That loss is computed over
|
||||||
|
response tokens only — just **8.3%** of RAFT's tokens, since the context is read
|
||||||
|
but never trained on. It is not comparable to the base's 2.2521, and its low
|
||||||
|
value partly reflects that extractive answers are easy, not that the model is
|
||||||
|
four times better.
|
||||||
|
|
||||||
|
The comparison that *is* fair — same held-out pretraining text the base was
|
||||||
|
measured on, every token, no masking:
|
||||||
|
|
||||||
|
| Model | Perplexity | vs base |
|
||||||
|
|---|---|---|
|
||||||
|
| Base | 9.50 | — |
|
||||||
|
| **This model** | **10.07** | **+6.0%** |
|
||||||
|
|
||||||
|
A small, real alignment tax: it gave up 6% of its raw domain modelling to become
|
||||||
|
grounded and refusal-capable. (Control: the base reproduces its published 9.51 on
|
||||||
|
this harness.)
|
||||||
|
|
||||||
|
**10.07 is not `e^0.9286`.** It is `e^2.3097` — this model's loss on held-out
|
||||||
|
**pretraining** text, every token counted. Note the direction: this model has the
|
||||||
|
**lower** val loss of the two fine-tunes (0.9286) and the **higher** perplexity here
|
||||||
|
(10.07). Nothing is contradictory. 0.9286 is scored on 8.3% of its tokens, copying
|
||||||
|
answers out of a passage sitting in the prompt; 10.07 is scored on every token of
|
||||||
|
unseen legal text with nothing to copy. Two different questions wearing the same units.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```python
|
||||||
|
from transformers import AutoModelForCausalLM, AutoTokenizer
|
||||||
|
|
||||||
|
tok = AutoTokenizer.from_pretrained("Kotichitturu/slm-125m-sft-raft")
|
||||||
|
model = AutoModelForCausalLM.from_pretrained("Kotichitturu/slm-125m-sft-raft")
|
||||||
|
|
||||||
|
SYSTEM = "Answer the question based only on the provided context. If the answer is not in the context, say 'The answer is not available in the provided context.'"
|
||||||
|
user = f"Context: {passage}\n\nQuestion: {question}"
|
||||||
|
prompt = f"<|bos|><|system|>{SYSTEM}<|user|>{user}<|assistant|>"
|
||||||
|
|
||||||
|
ids = tok(prompt, return_tensors="pt", add_special_tokens=False)
|
||||||
|
out = model.generate(**ids, max_new_tokens=90, do_sample=False,
|
||||||
|
eos_token_id=tok.convert_tokens_to_ids("<|eos|>"))
|
||||||
|
print(tok.decode(out[0][ids["input_ids"].shape[1]:], skip_special_tokens=True))
|
||||||
|
```
|
||||||
|
|
||||||
|
Use greedy decoding — the 97.9% above was measured with `do_sample=False`.
|
||||||
|
Sampling degrades refusal behaviour.
|
||||||
|
|
||||||
|
When the answer is absent it emits:
|
||||||
|
|
||||||
|
```
|
||||||
|
The answer is not available in the provided context.
|
||||||
|
```
|
||||||
|
|
||||||
|
### Limits
|
||||||
|
|
||||||
|
- Context + question + answer must fit **1,024 tokens** (training max was 964).
|
||||||
|
- Retrieval is your job. This model is the *reader*, not the retriever.
|
||||||
|
- It is not a lawyer. Verify anything that matters.
|
||||||
|
|
||||||
|
### System prompt (must match exactly)
|
||||||
|
|
||||||
|
```
|
||||||
|
Answer the question based only on the provided context. If the answer is not in the context, say 'The answer is not available in the provided context.'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
Identical to the base — fine-tuning changes weights, never shape.
|
||||||
|
|
||||||
|
| | |
|
||||||
|
|---|---|
|
||||||
|
| Params | 125.8M |
|
||||||
|
| Layers / hidden / heads | 12 / 768 / 12 |
|
||||||
|
| Vocab | 16,384 (byte-level BPE) |
|
||||||
|
| Context | 1,024 |
|
||||||
|
|
||||||
|
## Prompt format
|
||||||
|
|
||||||
|
The chat format is **not** a standard template — it uses custom special tokens
|
||||||
|
that exist in this tokenizer. Render exactly:
|
||||||
|
|
||||||
|
```
|
||||||
|
<|bos|><|system|>{system}<|user|>{user}<|assistant|>
|
||||||
|
```
|
||||||
|
|
||||||
|
Then generate. Stop at `<|eos|>`. Sending a different system prompt than the one
|
||||||
|
below moves the model off-distribution and quality degrades silently.
|
||||||
|
|
||||||
|
## Provenance
|
||||||
|
|
||||||
|
- Base: [Kotichitturu/slm-125m-base](https://huggingface.co/Kotichitturu/slm-125m-base)
|
||||||
|
- SFT data: 8,000 passages (US case law + SEC filings), teacher-generated by
|
||||||
|
gpt-4o-mini and gemini-3.1-flash-lite, then filtered by an LLM judge
|
||||||
|
(gpt-5.4-mini) that rejected every answer not supported by its passage.
|
||||||
|
Judge coverage 100%; keep rate 88%.
|
||||||
|
- Decontaminated against CaseHOLD/LexGLUE (13-gram) during pretraining.
|
||||||
|
- Full SFT (not LoRA), 3 epochs, lr 2e-5 cosine, bf16, 1×A100.
|
||||||
|
- **Loss is masked to response tokens only** — the model never trains on the
|
||||||
|
prompt it is given.
|
||||||
29
config.json
Normal file
29
config.json
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"architectures": [
|
||||||
|
"LlamaForCausalLM"
|
||||||
|
],
|
||||||
|
"attention_bias": false,
|
||||||
|
"attention_dropout": 0.0,
|
||||||
|
"bos_token_id": 1,
|
||||||
|
"eos_token_id": 2,
|
||||||
|
"head_dim": 64,
|
||||||
|
"hidden_act": "silu",
|
||||||
|
"hidden_size": 768,
|
||||||
|
"initializer_range": 0.02,
|
||||||
|
"intermediate_size": 3072,
|
||||||
|
"max_position_embeddings": 1024,
|
||||||
|
"mlp_bias": false,
|
||||||
|
"model_type": "llama",
|
||||||
|
"num_attention_heads": 12,
|
||||||
|
"num_hidden_layers": 12,
|
||||||
|
"num_key_value_heads": 12,
|
||||||
|
"pretraining_tp": 1,
|
||||||
|
"rms_norm_eps": 1e-05,
|
||||||
|
"rope_scaling": null,
|
||||||
|
"rope_theta": 10000.0,
|
||||||
|
"tie_word_embeddings": true,
|
||||||
|
"torch_dtype": "bfloat16",
|
||||||
|
"transformers_version": "4.52.4",
|
||||||
|
"use_cache": true,
|
||||||
|
"vocab_size": 16384
|
||||||
|
}
|
||||||
6
generation_config.json
Normal file
6
generation_config.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"_from_model_config": true,
|
||||||
|
"bos_token_id": 1,
|
||||||
|
"eos_token_id": 2,
|
||||||
|
"transformers_version": "4.52.4"
|
||||||
|
}
|
||||||
3
model.safetensors
Normal file
3
model.safetensors
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:dc424167f9dff16b0277809da9020aa5f4c894a972c644011c35e17e2837c2a1
|
||||||
|
size 251708904
|
||||||
35
special_tokens_map.json
Normal file
35
special_tokens_map.json
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"additional_special_tokens": [
|
||||||
|
"<|user|>",
|
||||||
|
"<|assistant|>",
|
||||||
|
"<|system|>"
|
||||||
|
],
|
||||||
|
"bos_token": {
|
||||||
|
"content": "<|bos|>",
|
||||||
|
"lstrip": false,
|
||||||
|
"normalized": false,
|
||||||
|
"rstrip": false,
|
||||||
|
"single_word": false
|
||||||
|
},
|
||||||
|
"eos_token": {
|
||||||
|
"content": "<|eos|>",
|
||||||
|
"lstrip": false,
|
||||||
|
"normalized": false,
|
||||||
|
"rstrip": false,
|
||||||
|
"single_word": false
|
||||||
|
},
|
||||||
|
"pad_token": {
|
||||||
|
"content": "<|pad|>",
|
||||||
|
"lstrip": false,
|
||||||
|
"normalized": false,
|
||||||
|
"rstrip": false,
|
||||||
|
"single_word": false
|
||||||
|
},
|
||||||
|
"unk_token": {
|
||||||
|
"content": "<|unk|>",
|
||||||
|
"lstrip": false,
|
||||||
|
"normalized": false,
|
||||||
|
"rstrip": false,
|
||||||
|
"single_word": false
|
||||||
|
}
|
||||||
|
}
|
||||||
80966
tokenizer.json
Normal file
80966
tokenizer.json
Normal file
File diff suppressed because it is too large
Load Diff
73
tokenizer_config.json
Normal file
73
tokenizer_config.json
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
{
|
||||||
|
"added_tokens_decoder": {
|
||||||
|
"0": {
|
||||||
|
"content": "<|bos|>",
|
||||||
|
"lstrip": false,
|
||||||
|
"normalized": false,
|
||||||
|
"rstrip": false,
|
||||||
|
"single_word": false,
|
||||||
|
"special": true
|
||||||
|
},
|
||||||
|
"1": {
|
||||||
|
"content": "<|eos|>",
|
||||||
|
"lstrip": false,
|
||||||
|
"normalized": false,
|
||||||
|
"rstrip": false,
|
||||||
|
"single_word": false,
|
||||||
|
"special": true
|
||||||
|
},
|
||||||
|
"2": {
|
||||||
|
"content": "<|pad|>",
|
||||||
|
"lstrip": false,
|
||||||
|
"normalized": false,
|
||||||
|
"rstrip": false,
|
||||||
|
"single_word": false,
|
||||||
|
"special": true
|
||||||
|
},
|
||||||
|
"3": {
|
||||||
|
"content": "<|unk|>",
|
||||||
|
"lstrip": false,
|
||||||
|
"normalized": false,
|
||||||
|
"rstrip": false,
|
||||||
|
"single_word": false,
|
||||||
|
"special": true
|
||||||
|
},
|
||||||
|
"4": {
|
||||||
|
"content": "<|user|>",
|
||||||
|
"lstrip": false,
|
||||||
|
"normalized": false,
|
||||||
|
"rstrip": false,
|
||||||
|
"single_word": false,
|
||||||
|
"special": true
|
||||||
|
},
|
||||||
|
"5": {
|
||||||
|
"content": "<|assistant|>",
|
||||||
|
"lstrip": false,
|
||||||
|
"normalized": false,
|
||||||
|
"rstrip": false,
|
||||||
|
"single_word": false,
|
||||||
|
"special": true
|
||||||
|
},
|
||||||
|
"6": {
|
||||||
|
"content": "<|system|>",
|
||||||
|
"lstrip": false,
|
||||||
|
"normalized": false,
|
||||||
|
"rstrip": false,
|
||||||
|
"single_word": false,
|
||||||
|
"special": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additional_special_tokens": [
|
||||||
|
"<|user|>",
|
||||||
|
"<|assistant|>",
|
||||||
|
"<|system|>"
|
||||||
|
],
|
||||||
|
"bos_token": "<|bos|>",
|
||||||
|
"clean_up_tokenization_spaces": false,
|
||||||
|
"eos_token": "<|eos|>",
|
||||||
|
"extra_special_tokens": {},
|
||||||
|
"model_max_length": 1000000000000000019884624838656,
|
||||||
|
"pad_token": "<|pad|>",
|
||||||
|
"tokenizer_class": "PreTrainedTokenizer",
|
||||||
|
"unk_token": "<|unk|>"
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user