Gaurav8HF commited on
Commit
610bc49
·
verified ·
1 Parent(s): 684f0c5

Upload Qwen3-0.6B fintech LoRA adapter

Browse files
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ tokenizer.json filter=lfs diff=lfs merge=lfs -text
README.md ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ base_model: Qwen/Qwen3-0.6B
3
+ library_name: peft
4
+ license: apache-2.0
5
+ pipeline_tag: text-generation
6
+ tags:
7
+ - lora
8
+ - peft
9
+ - qwen3
10
+ - fintech
11
+ - structured-generation
12
+ ---
13
+
14
+ # Fintech-Fine-Tune — Qwen3-0.6B + LoRA
15
+
16
+ A LoRA adapter for [Qwen3-0.6B](https://huggingface.co/Qwen/Qwen3-0.6B) that turns natural-language
17
+ fintech commands/questions into a fixed JSON schema, so a downstream system can act on them
18
+ without a rule-based parser.
19
+
20
+ ```
21
+ "can you pay the rent bill, it's $1,249?"
22
+ -> {"intent": "pay_bill", "entity": "rent", "amount": 1249, "account": null}
23
+
24
+ "dump 20 shares of META"
25
+ -> {"intent": "sell_stock", "entity": "META", "amount": 20, "account": "investment"}
26
+ ```
27
+
28
+ Trained entirely on a local CPU (no GPU) using [peft](https://github.com/huggingface/peft) LoRA —
29
+ full training code, dataset, and inference script: [GitHub repo](https://github.com/Gaurav23p24/Fintech-Fine-Tune).
30
+
31
+ ## Output schema
32
+
33
+ ```json
34
+ {"intent": "<one of 20 intents>", "entity": "<string or null>", "amount": "<number or null>", "account": "checking | savings | credit | investment | null"}
35
+ ```
36
+
37
+ Intents: `check_balance`, `transfer_funds`, `pay_bill`, `dispute_transaction`, `freeze_card`,
38
+ `unfreeze_card`, `report_lost_card`, `request_new_card`, `view_transaction_history`,
39
+ `set_spending_alert`, `update_credit_limit`, `apply_for_loan`, `check_loan_status`, `buy_stock`,
40
+ `sell_stock`, `check_portfolio`, `schedule_recurring_payment`, `cancel_recurring_payment`,
41
+ `open_account`, `close_account`.
42
+
43
+ ## Training
44
+
45
+ | | |
46
+ |---|---|
47
+ | LoRA rank / alpha | 8 / 16 |
48
+ | Target modules | `q_proj, k_proj, v_proj, o_proj, gate_proj, up_proj, down_proj` |
49
+ | Trainable params | 5,046,272 / 601,096,192 (0.84%) |
50
+ | Epochs | 3 |
51
+ | Effective batch size | 16 (batch 4 × grad-accum 4) |
52
+ | Learning rate | 2e-4, cosine schedule, 3% warmup |
53
+ | Hardware | CPU only, float32 |
54
+ | Data | 450 train / 60 val, synthetic, 20 intents, roughly balanced |
55
+
56
+ | Epoch | eval_loss |
57
+ |---|---|
58
+ | 1 | 0.0789 |
59
+ | 2 | 0.0265 |
60
+ | 3 (final) | 0.0214 |
61
+
62
+ ## Known limitations
63
+
64
+ The eval loss above is measured on validation examples generated by the same synthetic process as
65
+ training, so it mostly reflects whether the model learned the *output format*. Testing separately
66
+ on 10 hand-written prompts with fresh wording/entities not in train or val told a more honest
67
+ story:
68
+
69
+ - **10/10** produced syntactically valid JSON with exactly the 4 expected keys.
70
+ - **7/10** predicted a correct, valid intent.
71
+ - **3/10** hallucinated a plausible-looking `intent` value that isn't in the 20-label taxonomy at
72
+ all (e.g. `lock_renewable_document` instead of `freeze_card` for "lock my visa, someone jacked
73
+ it"; `check_revenue` instead of `check_balance` for "how much runway do I have left in
74
+ checking").
75
+
76
+ Solid for cleanly-phrased commands close to the training distribution; not yet reliable enough to
77
+ trust blindly on informal or unusual phrasing. Validate `intent` against the known list of 20
78
+ before acting on it downstream.
79
+
80
+ ## Usage
81
+
82
+ ```python
83
+ import torch
84
+ from peft import PeftModel
85
+ from transformers import AutoModelForCausalLM, AutoTokenizer
86
+
87
+ BASE_MODEL = "Qwen/Qwen3-0.6B"
88
+ ADAPTER = "Gaurav8HF/Fintech-Fine-Tune"
89
+
90
+ tokenizer = AutoTokenizer.from_pretrained(ADAPTER)
91
+ base_model = AutoModelForCausalLM.from_pretrained(BASE_MODEL, dtype=torch.float32)
92
+ model = PeftModel.from_pretrained(base_model, ADAPTER)
93
+
94
+ messages = [{"role": "user", "content": "freeze my card, I think I lost it"}]
95
+ prompt_ids = tokenizer.apply_chat_template(
96
+ messages, add_generation_prompt=True, return_tensors="pt", return_dict=False,
97
+ enable_thinking=False, # must match training -- Qwen3 normally "thinks" before answering
98
+ )
99
+ out = model.generate(prompt_ids, max_new_tokens=64, do_sample=False, pad_token_id=tokenizer.pad_token_id)
100
+ print(tokenizer.decode(out[0, prompt_ids.shape[1]:], skip_special_tokens=True))
101
+ ```
102
+
103
+ ### Framework versions
104
+
105
+ - PEFT 0.19.1
adapter_config.json ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "alora_invocation_tokens": null,
3
+ "alpha_pattern": {},
4
+ "arrow_config": null,
5
+ "auto_mapping": null,
6
+ "base_model_name_or_path": "models/qwen3-0.6b",
7
+ "bias": "none",
8
+ "corda_config": null,
9
+ "ensure_weight_tying": false,
10
+ "eva_config": null,
11
+ "exclude_modules": null,
12
+ "fan_in_fan_out": false,
13
+ "inference_mode": true,
14
+ "init_lora_weights": true,
15
+ "layer_replication": null,
16
+ "layers_pattern": null,
17
+ "layers_to_transform": null,
18
+ "loftq_config": {},
19
+ "lora_alpha": 16,
20
+ "lora_bias": false,
21
+ "lora_dropout": 0.05,
22
+ "lora_ga_config": null,
23
+ "megatron_config": null,
24
+ "megatron_core": "megatron.core",
25
+ "modules_to_save": null,
26
+ "peft_type": "LORA",
27
+ "peft_version": "0.19.1",
28
+ "qalora_group_size": 16,
29
+ "r": 8,
30
+ "rank_pattern": {},
31
+ "revision": null,
32
+ "target_modules": [
33
+ "up_proj",
34
+ "v_proj",
35
+ "down_proj",
36
+ "k_proj",
37
+ "q_proj",
38
+ "o_proj",
39
+ "gate_proj"
40
+ ],
41
+ "target_parameters": null,
42
+ "task_type": "CAUSAL_LM",
43
+ "trainable_token_indices": null,
44
+ "use_bdlora": null,
45
+ "use_dora": false,
46
+ "use_qalora": false,
47
+ "use_rslora": false
48
+ }
adapter_model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:db2ba2e050e01f0649e5d7f097dcd106f82670a6bcecfa8cc42602a39be110a3
3
+ size 20236472
chat_template.jinja ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {%- if tools %}
2
+ {{- '<|im_start|>system\n' }}
3
+ {%- if messages[0].role == 'system' %}
4
+ {{- messages[0].content + '\n\n' }}
5
+ {%- endif %}
6
+ {{- "# Tools\n\nYou may call one or more functions to assist with the user query.\n\nYou are provided with function signatures within <tools></tools> XML tags:\n<tools>" }}
7
+ {%- for tool in tools %}
8
+ {{- "\n" }}
9
+ {{- tool | tojson }}
10
+ {%- endfor %}
11
+ {{- "\n</tools>\n\nFor each function call, return a json object with function name and arguments within <tool_call></tool_call> XML tags:\n<tool_call>\n{\"name\": <function-name>, \"arguments\": <args-json-object>}\n</tool_call><|im_end|>\n" }}
12
+ {%- else %}
13
+ {%- if messages[0].role == 'system' %}
14
+ {{- '<|im_start|>system\n' + messages[0].content + '<|im_end|>\n' }}
15
+ {%- endif %}
16
+ {%- endif %}
17
+ {%- set ns = namespace(multi_step_tool=true, last_query_index=messages|length - 1) %}
18
+ {%- for message in messages[::-1] %}
19
+ {%- set index = (messages|length - 1) - loop.index0 %}
20
+ {%- if ns.multi_step_tool and message.role == "user" and message.content is string and not(message.content.startswith('<tool_response>') and message.content.endswith('</tool_response>')) %}
21
+ {%- set ns.multi_step_tool = false %}
22
+ {%- set ns.last_query_index = index %}
23
+ {%- endif %}
24
+ {%- endfor %}
25
+ {%- for message in messages %}
26
+ {%- if message.content is string %}
27
+ {%- set content = message.content %}
28
+ {%- else %}
29
+ {%- set content = '' %}
30
+ {%- endif %}
31
+ {%- if (message.role == "user") or (message.role == "system" and not loop.first) %}
32
+ {{- '<|im_start|>' + message.role + '\n' + content + '<|im_end|>' + '\n' }}
33
+ {%- elif message.role == "assistant" %}
34
+ {%- set reasoning_content = '' %}
35
+ {%- if message.reasoning_content is string %}
36
+ {%- set reasoning_content = message.reasoning_content %}
37
+ {%- else %}
38
+ {%- if '</think>' in content %}
39
+ {%- set reasoning_content = content.split('</think>')[0].rstrip('\n').split('<think>')[-1].lstrip('\n') %}
40
+ {%- set content = content.split('</think>')[-1].lstrip('\n') %}
41
+ {%- endif %}
42
+ {%- endif %}
43
+ {%- if loop.index0 > ns.last_query_index %}
44
+ {%- if loop.last or (not loop.last and reasoning_content) %}
45
+ {{- '<|im_start|>' + message.role + '\n<think>\n' + reasoning_content.strip('\n') + '\n</think>\n\n' + content.lstrip('\n') }}
46
+ {%- else %}
47
+ {{- '<|im_start|>' + message.role + '\n' + content }}
48
+ {%- endif %}
49
+ {%- else %}
50
+ {{- '<|im_start|>' + message.role + '\n' + content }}
51
+ {%- endif %}
52
+ {%- if message.tool_calls %}
53
+ {%- for tool_call in message.tool_calls %}
54
+ {%- if (loop.first and content) or (not loop.first) %}
55
+ {{- '\n' }}
56
+ {%- endif %}
57
+ {%- if tool_call.function %}
58
+ {%- set tool_call = tool_call.function %}
59
+ {%- endif %}
60
+ {{- '<tool_call>\n{"name": "' }}
61
+ {{- tool_call.name }}
62
+ {{- '", "arguments": ' }}
63
+ {%- if tool_call.arguments is string %}
64
+ {{- tool_call.arguments }}
65
+ {%- else %}
66
+ {{- tool_call.arguments | tojson }}
67
+ {%- endif %}
68
+ {{- '}\n</tool_call>' }}
69
+ {%- endfor %}
70
+ {%- endif %}
71
+ {{- '<|im_end|>\n' }}
72
+ {%- elif message.role == "tool" %}
73
+ {%- if loop.first or (messages[loop.index0 - 1].role != "tool") %}
74
+ {{- '<|im_start|>user' }}
75
+ {%- endif %}
76
+ {{- '\n<tool_response>\n' }}
77
+ {{- content }}
78
+ {{- '\n</tool_response>' }}
79
+ {%- if loop.last or (messages[loop.index0 + 1].role != "tool") %}
80
+ {{- '<|im_end|>\n' }}
81
+ {%- endif %}
82
+ {%- endif %}
83
+ {%- endfor %}
84
+ {%- if add_generation_prompt %}
85
+ {{- '<|im_start|>assistant\n' }}
86
+ {%- if enable_thinking is defined and enable_thinking is false %}
87
+ {{- '<think>\n\n</think>\n\n' }}
88
+ {%- endif %}
89
+ {%- endif %}
tokenizer.json ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:be75606093db2094d7cd20f3c2f385c212750648bd6ea4fb2bf507a6a4c55506
3
+ size 11422650
tokenizer_config.json ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "add_prefix_space": false,
3
+ "backend": "tokenizers",
4
+ "bos_token": null,
5
+ "clean_up_tokenization_spaces": false,
6
+ "eos_token": "<|im_end|>",
7
+ "errors": "replace",
8
+ "extra_special_tokens": [
9
+ "<|im_start|>",
10
+ "<|im_end|>",
11
+ "<|object_ref_start|>",
12
+ "<|object_ref_end|>",
13
+ "<|box_start|>",
14
+ "<|box_end|>",
15
+ "<|quad_start|>",
16
+ "<|quad_end|>",
17
+ "<|vision_start|>",
18
+ "<|vision_end|>",
19
+ "<|vision_pad|>",
20
+ "<|image_pad|>",
21
+ "<|video_pad|>"
22
+ ],
23
+ "is_local": true,
24
+ "local_files_only": false,
25
+ "model_max_length": 131072,
26
+ "pad_token": "<|endoftext|>",
27
+ "split_special_tokens": false,
28
+ "tokenizer_class": "Qwen2Tokenizer",
29
+ "unk_token": null
30
+ }