DjTechzz commited on
Commit
6d0cc7c
·
verified ·
1 Parent(s): 9a7d1dc

LFM2.5-VL-1.6B Japanese financial-statement extraction LoRA + model card

Browse files
README.md ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ base_model: LiquidAI/LFM2.5-VL-1.6B
3
+ library_name: peft
4
+ pipeline_tag: image-text-to-text
5
+ language:
6
+ - ja
7
+ tags:
8
+ - lora
9
+ - peft
10
+ - vision-language
11
+ - japanese
12
+ - finance
13
+ - document-extraction
14
+ - on-device
15
+ - liquid-ai
16
+ - lfm2
17
+ license: other
18
+ license_name: lfm-open-license-1.0
19
+ license_link: https://huggingface.co/LiquidAI/LFM2.5-VL-1.6B
20
+ ---
21
+
22
+ # LFM2.5-VL-1.6B · Japanese Financial-Statement Extraction (LoRA)
23
+
24
+ A **LoRA adapter** for [`LiquidAI/LFM2.5-VL-1.6B`](https://huggingface.co/LiquidAI/LFM2.5-VL-1.6B)
25
+ that teaches the on-device vision-language model to read **Japanese financial statements
26
+ (決算書 / 決算短信)** and return the key figures as structured JSON — entirely on-device.
27
+
28
+ Built for an on-premise bank credit-underwriting tool where customer financials can't leave the
29
+ machine (APPI / FISC). The stock 1.6B model reads clean statements but fails on dense, real
30
+ multi-column 決算短信; this adapter fixes that.
31
+
32
+ ## Results (held-out real 短信, never seen in training)
33
+ Field accuracy on 7 core figures across 3 real filings (明治HD / エディオン / カプコン),
34
+ graded against EDINET-exact gold:
35
+
36
+ | | Field accuracy |
37
+ |---|---|
38
+ | Base `LFM2.5-VL-1.6B` | **14.3%** |
39
+ | **+ this LoRA** | **85.7%** |
40
+
41
+ Trajectory across training iterations: **14.3 → 28.6 → 38.1 → 52.4 → 76.2 → 85.7 %** (≈6×).
42
+ Residual errors are single-digit slips in 6–7-digit numbers (a precision limit of a 1.6B VL),
43
+ not structural failures.
44
+
45
+ ## What it extracts
46
+ Per statement, as JSON (¥ millions): 決算期, 証券コード, 売上高, 営業利益, 経常利益, 当期純利益,
47
+ 総資産, 純資産, 有利子負債, 現預金, 前期売上高, 前期営業利益, 営業CF, 投資CF, 財務CF.
48
+
49
+ ## Usage
50
+ ```python
51
+ import torch
52
+ from transformers import AutoProcessor, AutoModelForImageTextToText
53
+ from peft import PeftModel
54
+ from PIL import Image
55
+
56
+ BASE = "LiquidAI/LFM2.5-VL-1.6B"
57
+ ADAPTER = "DjTechzz/LFM2.5-VL-1.6B-JP-Financial-Extract-LoRA"
58
+
59
+ proc = AutoProcessor.from_pretrained(BASE, trust_remote_code=True)
60
+ model = AutoModelForImageTextToText.from_pretrained(
61
+ BASE, trust_remote_code=True, dtype=torch.bfloat16, device_map="cuda")
62
+ model = PeftModel.from_pretrained(model, ADAPTER).eval()
63
+
64
+ prompt = ("あなたは財務書類の読み取り専用エンジンです。添付の決算書画像から"
65
+ "売上高・営業利益・経常利益・当期純利益・総資産・純資産・現預金 等を読み取り、"
66
+ "JSONのみを出力。金額は百万円単位の整数。前期/当期が並ぶ場合は当期を採用。")
67
+ img = Image.open("kessan.png").convert("RGB")
68
+ msgs = [{"role": "user", "content": [{"type": "image"}, {"type": "text", "text": prompt}]}]
69
+ text = proc.apply_chat_template(msgs, tokenize=False, add_generation_prompt=True)
70
+ inp = proc(text=[text], images=[[img]], return_tensors="pt").to(model.device)
71
+ out = model.generate(**inp, max_new_tokens=512, do_sample=False)
72
+ print(proc.decode(out[0][inp["input_ids"].shape[1]:], skip_special_tokens=True))
73
+ ```
74
+ Runs on-device (works on an AMD Ryzen AI / NVIDIA laptop). Pair the JSON with a deterministic
75
+ ratio/rating engine for an auditable credit memo.
76
+
77
+ ## Training
78
+ - **Method:** LoRA (rank 16, α 32; q/k/v/o + gate/up/down proj), bf16, grad-checkpointing, lr 1e-4.
79
+ - **Stack:** `transformers` + `peft` (no unsloth/triton), CUDA. ~30 min on an RTX PRO 5000.
80
+ - **Data (no manual labels):** synthetic statements rendered from real **EDINET** financials (exact
81
+ labels, incl. a realistic 決算短信 multi-column template) + **Qwen2.5-VL-7B distillation** on real
82
+ IR 決算短信; real labels later replaced with **EDINET-exact** values to remove teacher digit-noise.
83
+ - **Tracking:** Weights & Biases.
84
+
85
+ ## Intended use & limitations
86
+ For on-device extraction of standardized Japanese financial statements to assist credit analysis.
87
+ Outputs are a drafting aid — **not** a lending decision; verify figures before use. Occasional
88
+ single-digit OCR slips on very large numbers. Best on summary-style 短信 / 決算書 layouts.
89
+
90
+ ## License
91
+ This is a derivative LoRA adapter; use is subject to the base model's license
92
+ ([`LiquidAI/LFM2.5-VL-1.6B`](https://huggingface.co/LiquidAI/LFM2.5-VL-1.6B)). Built during the
93
+ Liquid AI × WAY × AMD hackathon (Tokyo, 2026).
adapter_config.json ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "alora_invocation_tokens": null,
3
+ "alpha_pattern": {},
4
+ "arrow_config": null,
5
+ "auto_mapping": {
6
+ "base_model_class": "Lfm2VlForConditionalGeneration",
7
+ "parent_library": "transformers.models.lfm2_vl.modeling_lfm2_vl"
8
+ },
9
+ "base_model_name_or_path": "LiquidAI/LFM2.5-VL-1.6B",
10
+ "bias": "none",
11
+ "corda_config": null,
12
+ "ensure_weight_tying": false,
13
+ "eva_config": null,
14
+ "exclude_modules": null,
15
+ "fan_in_fan_out": false,
16
+ "inference_mode": true,
17
+ "init_lora_weights": true,
18
+ "layer_replication": null,
19
+ "layers_pattern": null,
20
+ "layers_to_transform": null,
21
+ "loftq_config": {},
22
+ "lora_alpha": 32,
23
+ "lora_bias": false,
24
+ "lora_dropout": 0.05,
25
+ "lora_ga_config": null,
26
+ "megatron_config": null,
27
+ "megatron_core": "megatron.core",
28
+ "modules_to_save": null,
29
+ "peft_type": "LORA",
30
+ "peft_version": "0.19.1",
31
+ "qalora_group_size": 16,
32
+ "r": 16,
33
+ "rank_pattern": {},
34
+ "revision": null,
35
+ "target_modules": [
36
+ "q_proj",
37
+ "k_proj",
38
+ "up_proj",
39
+ "down_proj",
40
+ "v_proj",
41
+ "o_proj",
42
+ "gate_proj"
43
+ ],
44
+ "target_parameters": null,
45
+ "task_type": null,
46
+ "trainable_token_indices": null,
47
+ "use_bdlora": null,
48
+ "use_dora": false,
49
+ "use_qalora": false,
50
+ "use_rslora": false
51
+ }
adapter_model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:88dab80e2b9d2fa7f3d4a2e3f609a0040d3c0e3fbe0e19f34fafb2e13d4099b7
3
+ size 15513232
chat_template.jinja ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {{- bos_token -}}
2
+ {%- set keep_past_thinking = keep_past_thinking | default(false) -%}
3
+ {%- set ns = namespace(system_prompt="") -%}
4
+ {%- if messages[0]["role"] == "system" -%}
5
+ {%- set sys_content = messages[0]["content"] -%}
6
+ {%- if sys_content is not string -%}
7
+ {%- for item in sys_content -%}
8
+ {%- if item["type"] == "text" -%}
9
+ {%- set ns.system_prompt = ns.system_prompt + item["text"] -%}
10
+ {%- endif -%}
11
+ {%- endfor -%}
12
+ {%- else -%}
13
+ {%- set ns.system_prompt = sys_content -%}
14
+ {%- endif -%}
15
+ {%- set messages = messages[1:] -%}
16
+ {%- endif -%}
17
+ {%- if tools -%}
18
+ {%- set ns.system_prompt = ns.system_prompt + ("\n" if ns.system_prompt else "") + "List of tools: [" -%}
19
+ {%- for tool in tools -%}
20
+ {%- if tool is not string -%}
21
+ {%- set tool = tool | tojson -%}
22
+ {%- endif -%}
23
+ {%- set ns.system_prompt = ns.system_prompt + tool -%}
24
+ {%- if not loop.last -%}
25
+ {%- set ns.system_prompt = ns.system_prompt + ", " -%}
26
+ {%- endif -%}
27
+ {%- endfor -%}
28
+ {%- set ns.system_prompt = ns.system_prompt + "]" -%}
29
+ {%- endif -%}
30
+ {%- if ns.system_prompt -%}
31
+ {{- "<|im_start|>system\n" + ns.system_prompt + "<|im_end|>\n" -}}
32
+ {%- endif -%}
33
+ {%- set ns.last_assistant_index = -1 -%}
34
+ {%- for message in messages -%}
35
+ {%- if message["role"] == "assistant" -%}
36
+ {%- set ns.last_assistant_index = loop.index0 -%}
37
+ {%- endif -%}
38
+ {%- endfor -%}
39
+ {%- for message in messages -%}
40
+ {{- "<|im_start|>" + message["role"] + "\n" -}}
41
+ {%- if message["content"] is not string -%}
42
+ {%- set ns.content = "" -%}
43
+ {%- for item in message["content"] -%}
44
+ {%- if item["type"] == "image" -%}
45
+ {%- set ns.content = ns.content + "<image>" -%}
46
+ {%- elif item["type"] == "text" -%}
47
+ {%- set ns.content = ns.content + item["text"] -%}
48
+ {%- else -%}
49
+ {%- set ns.content = ns.content + item | tojson -%}
50
+ {%- endif -%}
51
+ {%- endfor -%}
52
+ {%- set content = ns.content -%}
53
+ {%- else -%}
54
+ {%- set content = message["content"] -%}
55
+ {%- endif -%}
56
+ {%- if message["role"] == "assistant" and not keep_past_thinking and loop.index0 != ns.last_assistant_index -%}
57
+ {%- if "</think>" in content -%}
58
+ {%- set content = content.split("</think>")[-1] | trim -%}
59
+ {%- endif -%}
60
+ {%- endif -%}
61
+ {{- content + "<|im_end|>\n" -}}
62
+ {%- endfor -%}
63
+ {%- if add_generation_prompt -%}
64
+ {{- "<|im_start|>assistant\n" -}}
65
+ {%- endif -%}
processor_config.json ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "image_processor": {
3
+ "data_format": "channels_first",
4
+ "do_image_splitting": true,
5
+ "do_normalize": true,
6
+ "do_pad": true,
7
+ "do_rescale": true,
8
+ "do_resize": true,
9
+ "downsample_factor": 2,
10
+ "encoder_patch_size": 16,
11
+ "image_mean": [
12
+ 0.5,
13
+ 0.5,
14
+ 0.5
15
+ ],
16
+ "image_processor_type": "Lfm2VlImageProcessor",
17
+ "image_std": [
18
+ 0.5,
19
+ 0.5,
20
+ 0.5
21
+ ],
22
+ "max_image_tokens": 256,
23
+ "max_num_patches": 1024,
24
+ "max_pixels_tolerance": 2.0,
25
+ "max_tiles": 10,
26
+ "min_image_tokens": 64,
27
+ "min_tiles": 2,
28
+ "resample": 2,
29
+ "rescale_factor": 0.00392156862745098,
30
+ "return_row_col_info": true,
31
+ "size": {
32
+ "height": 512,
33
+ "width": 512
34
+ },
35
+ "tile_size": 512,
36
+ "use_thumbnail": true
37
+ },
38
+ "processor_class": "Lfm2VlProcessor"
39
+ }
tokenizer.json ADDED
The diff for this file is too large to render. See raw diff
 
tokenizer_config.json ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "backend": "tokenizers",
3
+ "bos_token": "<|startoftext|>",
4
+ "clean_up_tokenization_spaces": true,
5
+ "eos_token": "<|im_end|>",
6
+ "image_end_token": "<|image_end|>",
7
+ "image_start_token": "<|image_start|>",
8
+ "image_thumbnail": "<|img_thumbnail|>",
9
+ "image_token": "<image>",
10
+ "is_local": false,
11
+ "legacy": false,
12
+ "local_files_only": false,
13
+ "model_max_length": 1000000000000000019884624838656,
14
+ "model_specific_special_tokens": {
15
+ "image_end_token": "<|image_end|>",
16
+ "image_start_token": "<|image_start|>",
17
+ "image_token": "<image>"
18
+ },
19
+ "pad_token": "<|pad|>",
20
+ "processor_class": "Lfm2VlProcessor",
21
+ "return_token_type_ids": false,
22
+ "sp_model_kwargs": {},
23
+ "spaces_between_special_tokens": false,
24
+ "tokenizer_class": "TokenizersBackend",
25
+ "use_default_system_prompt": false,
26
+ "use_fast": true
27
+ }
training_args.bin ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:3f14edc01fdacdeda89dcf598c1a06d61c54a242276b0cfcd653c1b945462a25
3
+ size 5265