2 Commits

32 changed files with 1517229 additions and 0 deletions
Split View
  1. +2
    -0
      zhangxinyue2024103833/.gitignore
  2. +74
    -0
      zhangxinyue2024103833/2024103833.md
  3. BIN
      zhangxinyue2024103833/finance_demo.db
  4. +185
    -0
      zhangxinyue2024103833/finetune.py
  5. +190
    -0
      zhangxinyue2024103833/init_sqlite.py
  6. +56
    -0
      zhangxinyue2024103833/logs/finetune.log
  7. +60
    -0
      zhangxinyue2024103833/logs/middleware.log
  8. +252
    -0
      zhangxinyue2024103833/middleware.py
  9. +207
    -0
      zhangxinyue2024103833/models/deepseek-sql-lora/README.md
  10. +42
    -0
      zhangxinyue2024103833/models/deepseek-sql-lora/adapter_config.json
  11. BIN
      zhangxinyue2024103833/models/deepseek-sql-lora/adapter_model.safetensors
  12. +1
    -0
      zhangxinyue2024103833/models/deepseek-sql-lora/chat_template.jinja
  13. +207
    -0
      zhangxinyue2024103833/models/deepseek-sql-lora/checkpoint-200/README.md
  14. +42
    -0
      zhangxinyue2024103833/models/deepseek-sql-lora/checkpoint-200/adapter_config.json
  15. BIN
      zhangxinyue2024103833/models/deepseek-sql-lora/checkpoint-200/adapter_model.safetensors
  16. +1
    -0
      zhangxinyue2024103833/models/deepseek-sql-lora/checkpoint-200/chat_template.jinja
  17. BIN
      zhangxinyue2024103833/models/deepseek-sql-lora/checkpoint-200/optimizer.pt
  18. BIN
      zhangxinyue2024103833/models/deepseek-sql-lora/checkpoint-200/rng_state.pth
  19. BIN
      zhangxinyue2024103833/models/deepseek-sql-lora/checkpoint-200/scheduler.pt
  20. +23
    -0
      zhangxinyue2024103833/models/deepseek-sql-lora/checkpoint-200/special_tokens_map.json
  21. +757497
    -0
      zhangxinyue2024103833/models/deepseek-sql-lora/checkpoint-200/tokenizer.json
  22. +194
    -0
      zhangxinyue2024103833/models/deepseek-sql-lora/checkpoint-200/tokenizer_config.json
  23. +174
    -0
      zhangxinyue2024103833/models/deepseek-sql-lora/checkpoint-200/trainer_state.json
  24. BIN
      zhangxinyue2024103833/models/deepseek-sql-lora/checkpoint-200/training_args.bin
  25. +23
    -0
      zhangxinyue2024103833/models/deepseek-sql-lora/special_tokens_map.json
  26. +757497
    -0
      zhangxinyue2024103833/models/deepseek-sql-lora/tokenizer.json
  27. +194
    -0
      zhangxinyue2024103833/models/deepseek-sql-lora/tokenizer_config.json
  28. +74
    -0
      zhangxinyue2024103833/readme.md
  29. +9
    -0
      zhangxinyue2024103833/requirements.txt
  30. +32
    -0
      zhangxinyue2024103833/train.jsonl
  31. +193
    -0
      zhangxinyue2024103833/ui/index.html
  32. BIN
      zhangxinyue2024103833/ui/screenshot.png

+ 2
- 0
zhangxinyue2024103833/.gitignore View File

@@ -0,0 +1,2 @@
deepseek1.5B/
__pycache__/

+ 74
- 0
zhangxinyue2024103833/2024103833.md View File

@@ -0,0 +1,74 @@
# 财务问答中间件(自然语言 → SQL → 结果)

一句话说明:这是一个**把中文问题自动翻译成 SQL,查询 SQLite,再用人话解释结果**的小系统,“中间件 + 大模型”的结合。

---

## 🧠 原理简述
1. 输入中文问题(例:8 月份市场部的费用是多少?)
2. 大模型生成对应的 SQL
3. SQLite 执行 SQL,得到结果
4. 后端把结果转成通俗解释
5. 网页展示:问题 → 解释 → SQL → 结果表

---

## 📂 目录结构
<pre>
中间件/
├── train.jsonl 微调样本(每行:问题→SQL 的 JSON)
├── finetune.py 微调脚本:用 train.jsonl 对基础模型做 LoRA 微调
├── middleware.py 后端中间件(FastAPI):问句→SQL→查询→解释→返回给前端
├── init_db.py 初始化示例库:创建/写入演示用财务数据
├── finance_demo.db SQLite 数据库(运行 init_db.py 生成)
├── deepseek1.5B/ 基础模型目录(你本地已有)
├── models/
│ └── deepseek-sql-lora/ 微调产物(LoRA 适配器)
├── ui/ 前端网页
│ ├── index.html 主页面(输入→显示解释/SQL/结果)
│ ├── screenshot.png 演示截图(下方“演示案例”会引用)
│ └── ...
└── logs/
└── middleware.log 运行日志
└── finetune.log 微调日志
</pre>

---

## 🚀 使用步骤(通俗说明)

### 1)初始化数据库
运行 `python init_db.py`
👉 生成或覆盖 `finance_demo.db`,写入示例财务数据(费用、部门、日期、预算等)。

### 2)微调小模型(可选)
运行 `python finetune.py`
👉 读取 `train.jsonl`,在 `deepseek1.5B/` 上做 LoRA 微调,结果存到 `models/deepseek-sql-lora/`。

### 3)启动后端中间件
运行 `python middleware.py`
👉 启动 FastAPI 服务,默认访问地址是 [http://127.0.0.1:8000/ui/](http://127.0.0.1:8000/ui/)。

### 4)打开前端页面演示
用浏览器访问 `http://127.0.0.1:8000/ui/`,输入问题或点击示例按钮,页面会显示:解释 → SQL → 查询结果。
推荐测试问题:
- 8月份市场部的费用是多少?
- 2024年8月各部门费用从高到低排序

---

## 🎬 演示案例


<img src="ui/screenshot.png" alt="演示页面" width="40%">

---

## ✨ 项目亮点
- **大模型驱动**:利用大模型的强大语义理解和生成能力,把自然语言问题准确转化为 SQL,降低非技术人员使用数据库的门槛。
- **结构完整**:自然语言 → SQL → 数据库 → 解释,完整演示中间件与大模型结合;
- **可用与可维护**:前端可视化输出让业务人员能直接理解查询结果,后端日志记录则方便技术人员排查问题、进行审计,保证系统可靠运行。


BIN
zhangxinyue2024103833/finance_demo.db View File


+ 185
- 0
zhangxinyue2024103833/finetune.py View File

@@ -0,0 +1,185 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# 本脚本用于在CPU上对本地模型进行LoRA微调
# 任务:把自然语言问题转换成SQLite的SQL查询

import os
import json
import logging
import argparse
from typing import Dict

import torch
from datasets import load_dataset, Dataset
from transformers import (
AutoTokenizer,
AutoModelForCausalLM,
DataCollatorForLanguageModeling,
Trainer,
TrainingArguments,
set_seed,
)
from peft import LoraConfig, get_peft_model

# 默认路径(根据你的目录结构)
DEFAULT_BASE_MODEL = r"D:/人大/结课作业/中间件/deepseek1.5B"
DEFAULT_TRAIN_FILE = r"D:/人大/结课作业/中间件/train.jsonl"
DEFAULT_OUTPUT_DIR = r"models/deepseek-sql-lora"

# 日志
os.makedirs("logs", exist_ok=True)
logging.basicConfig(
filename="logs/finetune.log",
level=logging.INFO,
format="%(asctime)s %(levelname)s %(message)s",
)
logging.getLogger("").addHandler(logging.StreamHandler())

# 系统提示
SYS_PROMPT = "你是一个SQL助手,只输出SQLite的SELECT语句,不要其他内容。"

def format_example(ex: Dict, eos_token: str) -> str:
"""把一条样本转成训练用文本"""
inst = (ex.get("instruction") or "").strip()
_in = (ex.get("input") or "").strip()
out = (ex.get("output") or "").strip()
user_block = inst if not _in else f"{inst}\n用户问题:{_in}"
return (
f"<s>[SYSTEM]\n{SYS_PROMPT}\n[/SYSTEM]\n"
f"[USER]\n{user_block}\n[/USER]\n"
f"[ASSISTANT]\n{out}\n[/ASSISTANT]{eos_token}"
)

def robust_load_jsonl(path: str) -> Dataset:
"""读取jsonl文件,忽略坏行"""
rows = []
with open(path, "r", encoding="utf-8") as f:
for i, line in enumerate(f, 1):
s = line.strip()
if not s:
continue
try:
rows.append(json.loads(s))
except Exception as e:
logging.warning(f"跳过第{i}行:{e}")
if not rows:
raise RuntimeError("train.jsonl 里没有有效样本")
ds = Dataset.from_list(rows)
return ds.add_column("raw", [dict(x) for x in ds])

def tokenize_function(batch, tokenizer, max_len):
"""分词"""
texts = [format_example(x, tokenizer.eos_token or "</s>") for x in batch["raw"]]
return tokenizer(texts, max_length=max_len, truncation=True, padding=False)

def main():
ap = argparse.ArgumentParser()
ap.add_argument("--base_model", type=str, default=DEFAULT_BASE_MODEL)
ap.add_argument("--train_file", type=str, default=DEFAULT_TRAIN_FILE)
ap.add_argument("--output_dir", type=str, default=DEFAULT_OUTPUT_DIR)
ap.add_argument("--eval_ratio", type=float, default=0.1)
ap.add_argument("--max_steps", type=int, default=200)
ap.add_argument("--per_device_train_batch_size", type=int, default=1) # CPU友好
ap.add_argument("--per_device_eval_batch_size", type=int, default=1)
ap.add_argument("--gradient_accumulation_steps", type=int, default=1) # CPU友好
ap.add_argument("--learning_rate", type=float, default=1e-4)
ap.add_argument("--lr_scheduler_type", type=str, default="cosine")
ap.add_argument("--warmup_ratio", type=float, default=0.05)
ap.add_argument("--lora_r", type=int, default=4)
ap.add_argument("--lora_alpha", type=int, default=8)
ap.add_argument("--lora_dropout", type=float, default=0.05)
ap.add_argument("--target_modules", type=str,
default="q_proj,k_proj,v_proj,o_proj,gate_proj,up_proj,down_proj")
ap.add_argument("--max_seq_length", type=int, default=512)
ap.add_argument("--seed", type=int, default=42)
args = ap.parse_args()

set_seed(args.seed)

# 读数据
ds = robust_load_jsonl(args.train_file)
if 0 < args.eval_ratio < 1:
split = ds.train_test_split(test_size=args.eval_ratio, seed=args.seed)
train_ds, eval_ds = split["train"], split["test"]
else:
train_ds, eval_ds = ds, None

# 加载分词器和模型
tokenizer = AutoTokenizer.from_pretrained(args.base_model, use_fast=True)
if tokenizer.pad_token is None:
tokenizer.pad_token = tokenizer.eos_token

model = AutoModelForCausalLM.from_pretrained(
args.base_model,
device_map=None,
torch_dtype=torch.float32,
).to("cpu")

if hasattr(model, "enable_input_require_grads"):
model.enable_input_require_grads()
if hasattr(model, "config"):
model.config.use_cache = False

# LoRA配置
target_modules = [t.strip() for t in args.target_modules.split(",") if t.strip()]
lora_cfg = LoraConfig(
r=args.lora_r,
lora_alpha=args.lora_alpha,
lora_dropout=args.lora_dropout,
bias="none",
task_type="CAUSAL_LM",
target_modules=target_modules,
)
model = get_peft_model(model, lora_cfg)
model.print_trainable_parameters()

# 数据tokenize
def _map(b): return tokenize_function(b, tokenizer, args.max_seq_length)
train_tok = train_ds.map(_map, batched=True, remove_columns=train_ds.column_names)
eval_tok = eval_ds.map(_map, batched=True, remove_columns=eval_ds.column_names) if eval_ds else None
collator = DataCollatorForLanguageModeling(tokenizer=tokenizer, mlm=False)

# 训练参数(CPU)
train_kwargs = dict(
output_dir=args.output_dir,
learning_rate=args.learning_rate,
lr_scheduler_type=args.lr_scheduler_type,
warmup_ratio=args.warmup_ratio,
per_device_train_batch_size=args.per_device_train_batch_size,
per_device_eval_batch_size=args.per_device_eval_batch_size,
gradient_accumulation_steps=args.gradient_accumulation_steps,
num_train_epochs=1,
max_steps=args.max_steps,
logging_steps=10,
save_steps=200,
save_total_limit=2,
bf16=False,
fp16=False,
gradient_checkpointing=False,
report_to=["none"],
remove_unused_columns=False,
dataloader_pin_memory=False,
)
training_args = TrainingArguments(**train_kwargs)

# Trainer
trainer = Trainer(
model=model,
args=training_args,
data_collator=collator,
train_dataset=train_tok,
eval_dataset=eval_tok,
tokenizer=tokenizer,
)
trainer.train()

# 保存
os.makedirs(args.output_dir, exist_ok=True)
trainer.model.save_pretrained(args.output_dir)
tokenizer.save_pretrained(args.output_dir)

print(f"[完成] 模型已保存到: {args.output_dir}")

if __name__ == "__main__":
main()

+ 190
- 0
zhangxinyue2024103833/init_sqlite.py View File

@@ -0,0 +1,190 @@
import os
import sqlite3
import argparse
from typing import List, Tuple

DB_FILE = "finance_demo.db"

DEPARTMENTS = ["市场部", "研发部", "行政部", "财务部", "销售部"]
CATEGORIES = ["广告", "人力", "办公", "差旅", "培训"]
VENDORS = {
"广告": ["XX传媒", "广告联盟", "新媒体投放"],
"人力": ["外包A", "外包B", "人才中介"],
"办公": ["办公城", "文具行", "设备商"],
"差旅": ["航空公司", "铁路公司", "酒店集团"],
"培训": ["培训机构", "认证中心"],
}

EXPENSES_ROWS: List[Tuple[str, str, str, str, float]] = [
# 2024-07
("市场部", "2024-07-05", "广告", "XX传媒", 15000),
("研发部", "2024-07-15", "人力", "外包A", 25000),
("行政部", "2024-07-08", "办公", "办公城", 5000),
("财务部", "2024-07-22", "差旅", "航空公司", 12000),
("销售部", "2024-07-18", "培训", "培训机构", 6000),

# 2024-08
("市场部", "2024-08-10", "广告", "XX传媒", 18000),
("研发部", "2024-08-20", "人力", "外包B", 30000),
("行政部", "2024-08-12", "办公", "办公城", 8000),
("财务部", "2024-08-25", "差旅", "铁路公司", 9000),
("销售部", "2024-08-03", "培训", "培训机构", 7000),
("销售部", "2024-08-18", "广告", "广告联盟", 11000),
("市场部", "2024-08-22", "广告", "新媒体投放", 12000), # 便于供应商聚合/TopN

# 2024-09
("市场部", "2024-09-06", "广告", "XX传媒", 12000),
("研发部", "2024-09-10", "人力", "外包A", 28000),
("行政部", "2024-09-13", "办公", "文具行", 3000),
("财务部", "2024-09-21", "差旅", "酒店集团", 6000),
("销售部", "2024-09-02", "培训", "认证中心", 5000),
("销售部", "2024-09-27", "广告", "广告联盟", 9000),

# 假期段 2024-10-01 ~ 2024-10-07(用于节假日区间查询示例)
("市场部", "2024-10-01", "广告", "XX传媒", 5000),
("研发部", "2024-10-03", "人力", "外包B", 7000),
("行政部", "2024-10-05", "办公", "设备商", 4000),
]

# 月度预算
BUDGETS_ROWS: List[Tuple[str, str, float]] = [
# 2024-07
("市场部", "2024-07", 20000),
("研发部", "2024-07", 26000),
("行政部", "2024-07", 6000),
("财务部", "2024-07", 15000),
("销售部", "2024-07", 8000),
# 2024-08
("市场部", "2024-08", 22000),
("研发部", "2024-08", 31000),
("行政部", "2024-08", 9000),
("财务部", "2024-08", 10000),
("销售部", "2024-08", 12000),
# 2024-09
("市场部", "2024-09", 16000),
("研发部", "2024-09", 29000),
("行政部", "2024-09", 7000),
("财务部", "2024-09", 8000),
("销售部", "2024-09", 11000),
]

CREATE_EXPENSES_SQL = """
CREATE TABLE IF NOT EXISTS expenses (
id INTEGER PRIMARY KEY AUTOINCREMENT,
department TEXT NOT NULL,
date TEXT NOT NULL, -- YYYY-MM-DD
category TEXT NOT NULL, -- 差旅/广告/人力/办公/培训 等
vendor TEXT NOT NULL,
amount REAL NOT NULL
);
"""

CREATE_BUDGETS_SQL = """
CREATE TABLE IF NOT EXISTS budgets (
id INTEGER PRIMARY KEY AUTOINCREMENT,
department TEXT NOT NULL,
month TEXT NOT NULL, -- YYYY-MM
budget_amount REAL NOT NULL
);
"""

def ensure_db(reset: bool) -> sqlite3.Connection:
"""创建/重置数据库文件并建表。"""
if reset and os.path.exists(DB_FILE):
os.remove(DB_FILE)
print(f"已删除旧数据库: {DB_FILE}")

need_create = not os.path.exists(DB_FILE)
conn = sqlite3.connect(DB_FILE)
cur = conn.cursor()

if need_create:
print(f"创建新数据库: {DB_FILE}")

cur.execute(CREATE_EXPENSES_SQL)
cur.execute(CREATE_BUDGETS_SQL)
conn.commit()
return conn

def table_count(cur: sqlite3.Cursor, table: str) -> int:
cur.execute(f"SELECT COUNT(*) FROM {table}")
return cur.fetchone()[0]

def insert_if_empty(conn: sqlite3.Connection):
"""仅在表为空时插入初始化数据;若已有数据则跳过。"""
cur = conn.cursor()
exp_cnt = table_count(cur, "expenses")
bud_cnt = table_count(cur, "budgets")

if exp_cnt == 0:
cur.executemany(
"INSERT INTO expenses(department,date,category,vendor,amount) VALUES(?,?,?,?,?)",
EXPENSES_ROWS
)
print(f"插入 expenses: {len(EXPENSES_ROWS)} 条")
else:
print(f"expenses 已有 {exp_cnt} 条,跳过插入。")

if bud_cnt == 0:
cur.executemany(
"INSERT INTO budgets(department,month,budget_amount) VALUES(?,?,?)",
BUDGETS_ROWS
)
print(f"插入 budgets: {len(BUDGETS_ROWS)} 条")
else:
print(f"budgets 已有 {bud_cnt} 条,跳过插入。")

conn.commit()

def quick_selfcheck(conn: sqlite3.Connection):
"""跑几条典型 SQL 做快速自检,与你的 Demo 问题一致。"""
cur = conn.cursor()

checks = [
("检查 2024-08 市场部费用总额",
"SELECT IFNULL(SUM(amount),0) FROM expenses WHERE department='市场部' AND date LIKE '2024-08%';"),
("检查 2024-08 各部门费用从高到低",
"SELECT department, IFNULL(SUM(amount),0) AS total FROM expenses WHERE date LIKE '2024-08%' GROUP BY department ORDER BY total DESC;"),
("检查 2024-08 预算与实际对比",
"SELECT b.department, b.budget_amount, IFNULL(e.actual,0) AS actual, (IFNULL(e.actual,0)-b.budget_amount) AS diff "
"FROM budgets b LEFT JOIN (SELECT department, SUM(amount) AS actual FROM expenses WHERE date LIKE '2024-08%' GROUP BY department) e "
"ON b.department=e.department WHERE b.month='2024-08' ORDER BY diff DESC;"),
("检查 2024-10-01 到 2024-10-07 区间记录",
"SELECT date, department, category, vendor, amount FROM expenses WHERE date BETWEEN '2024-10-01' AND '2024-10-07' ORDER BY date;"),
]

print("\n==== 快速自检(部分查询结果预览) ====")
for title, sql in checks:
cur.execute(sql)
rows = cur.fetchall()
print(f"- {title}: {len(rows)} 行")
# 打印最多前 3 行
for r in rows[:3]:
print(" ", tuple(r))
print("==== 自检结束 ====\n")

def parse_args():
ap = argparse.ArgumentParser(description="初始化 finance_demo.db")
g = ap.add_mutually_exclusive_group()
g.add_argument("--reset", action="store_true", help="若存在旧数据库则删除重建(默认)")
g.add_argument("--keep", action="store_true", help="若存在旧数据库则保留,仅在表为空时插入")
return ap.parse_args()

def main():
args = parse_args()
reset = True
if args.keep:
reset = False

conn = ensure_db(reset=reset)
insert_if_empty(conn)
quick_selfcheck(conn)
conn.close()

print(f"✅ 初始化完成,数据库文件: {DB_FILE}")
print("下一步:")
print(" 1) 运行 uvicorn middleware:app --reload")
print(" 2) 打开 http://127.0.0.1:8000/ui/ 进行演示")

if __name__ == "__main__":
main()

+ 56
- 0
zhangxinyue2024103833/logs/finetune.log View File

@@ -0,0 +1,56 @@
2025-09-15 20:24:23,055 INFO Base model : D:/人大/结课作业/中间件/deepseek1.5B
2025-09-15 20:24:23,057 INFO Train file : D:/人大/结课作业/中间件/train.jsonl
2025-09-15 20:24:23,057 INFO Output dir : models/deepseek-sql-lora
2025-09-15 20:24:23,057 INFO Loading dataset ...
2025-09-15 20:29:15,899 INFO Base model : D:/人大/结课作业/中间件/deepseek1.5B
2025-09-15 20:29:15,899 INFO Train file : D:/人大/结课作业/中间件/train.jsonl
2025-09-15 20:29:15,899 INFO Output dir : models/deepseek-sql-lora
2025-09-15 20:29:15,899 INFO Loading dataset ...
2025-09-15 20:29:16,746 INFO Loading tokenizer ...
2025-09-15 20:29:17,191 INFO Loading base model ...
2025-09-15 20:29:29,793 WARNING The 8-bit optimizer is not available on your device, only available on CUDA for now.
2025-09-15 20:29:31,261 INFO Trainable params (LoRA): 18.46 M
2025-09-15 20:29:31,262 INFO Tokenizing dataset ...
2025-09-15 20:29:31,892 INFO Setting TrainingArguments ...
2025-09-15 20:35:55,947 INFO Base model : D:/人大/结课作业/中间件/deepseek1.5B
2025-09-15 20:35:55,948 INFO Train file : D:/人大/结课作业/中间件/train.jsonl
2025-09-15 20:35:55,948 INFO Output dir : models/deepseek-sql-lora
2025-09-15 20:35:55,948 INFO Loading dataset ...
2025-09-15 20:35:56,700 INFO Loading tokenizer ...
2025-09-15 20:35:57,126 INFO Loading base model ...
2025-09-15 20:36:07,530 WARNING The 8-bit optimizer is not available on your device, only available on CUDA for now.
2025-09-15 20:36:08,720 INFO Trainable params (LoRA): 18.46 M
2025-09-15 20:36:08,720 INFO Tokenizing dataset ...
2025-09-15 20:36:09,158 INFO Setting TrainingArguments ...
2025-09-15 20:40:51,009 INFO Base model : D:/人大/结课作业/中间件/deepseek1.5B
2025-09-15 20:40:51,010 INFO Train file : D:/人大/结课作业/中间件/train.jsonl
2025-09-15 20:40:51,010 INFO Output dir : models/deepseek-sql-lora
2025-09-15 20:40:51,010 INFO Loading dataset ...
2025-09-15 20:40:51,815 INFO Loading tokenizer ...
2025-09-15 20:40:52,189 INFO Loading base model ...
2025-09-15 20:41:01,229 WARNING The 8-bit optimizer is not available on your device, only available on CUDA for now.
2025-09-15 20:41:02,830 INFO Trainable params (LoRA): 18.46 M
2025-09-15 20:41:02,831 INFO Tokenizing dataset ...
2025-09-15 20:41:03,189 INFO Setting TrainingArguments ...
2025-09-15 20:43:26,543 INFO Base model : D:/人大/结课作业/中间件/deepseek1.5B
2025-09-15 20:43:26,544 INFO Train file : D:/人大/结课作业/中间件/train.jsonl
2025-09-15 20:43:26,544 INFO Output dir : models/deepseek-sql-lora
2025-09-15 20:43:26,544 INFO Loading dataset ...
2025-09-15 20:43:27,630 INFO Loading tokenizer ...
2025-09-15 20:43:28,663 INFO Loading base model ...
2025-09-15 20:43:46,265 WARNING The 8-bit optimizer is not available on your device, only available on CUDA for now.
2025-09-15 20:43:52,811 INFO Trainable params (LoRA): 18.46 M
2025-09-15 20:43:52,811 INFO Tokenizing dataset ...
2025-09-15 20:43:53,799 INFO Setting TrainingArguments ...
2025-09-15 20:44:45,101 INFO Base model : D:/人大/结课作业/中间件/deepseek1.5B
2025-09-15 20:44:45,101 INFO Train file : D:/人大/结课作业/中间件/train.jsonl
2025-09-15 20:44:45,102 INFO Output dir : models/deepseek-sql-lora
2025-09-15 20:44:45,102 INFO Loading dataset ...
2025-09-15 20:44:45,831 INFO Loading tokenizer ...
2025-09-15 20:44:46,189 INFO Loading base model ...
2025-09-15 20:44:58,632 WARNING The 8-bit optimizer is not available on your device, only available on CUDA for now.
2025-09-15 20:45:00,345 INFO Trainable params (LoRA): 18.46 M
2025-09-15 20:45:00,345 INFO Tokenizing dataset ...
2025-09-15 20:45:00,761 INFO Setting TrainingArguments ...
2025-09-15 20:45:00,786 INFO Start training ...
2025-09-15 20:54:43,614 WARNING The 8-bit optimizer is not available on your device, only available on CUDA for now.

+ 60
- 0
zhangxinyue2024103833/logs/middleware.log View File

@@ -0,0 +1,60 @@
2025-09-15 21:24:10,404 INFO 加载分词器与模型...
2025-09-15 21:24:19,835 WARNING The 8-bit optimizer is not available on your device, only available on CUDA for now.
2025-09-15 21:24:21,772 INFO 模型就绪,模式:lora
2025-09-15 21:24:30,223 INFO 加载分词器与模型...
2025-09-15 21:24:41,658 WARNING The 8-bit optimizer is not available on your device, only available on CUDA for now.
2025-09-15 21:24:45,478 INFO 模型就绪,模式:lora
2025-09-15 21:24:45,641 INFO 加载分词器与模型...
2025-09-15 21:24:54,390 INFO 模型就绪,模式:lora
2025-09-15 21:29:16,420 INFO 加载分词器与模型...
2025-09-15 21:29:27,926 WARNING The 8-bit optimizer is not available on your device, only available on CUDA for now.
2025-09-15 21:29:31,834 INFO 模型就绪,模式:lora
2025-09-15 21:29:32,033 INFO 加载分词器与模型...
2025-09-15 21:29:43,891 INFO 模型就绪,模式:lora
2025-09-15 22:55:21,180 INFO 鍔犺浇鍒嗚瘝鍣ㄤ笌妯″瀷锛圕PU锛�...
2025-09-15 22:55:32,511 WARNING The 8-bit optimizer is not available on your device, only available on CUDA for now.
2025-09-15 22:55:34,631 INFO 妯″瀷灏辩华锛屾ā寮忥細lora
2025-09-15 23:03:09,623 INFO 加载分词器与模型...
2025-09-15 23:03:19,099 WARNING The 8-bit optimizer is not available on your device, only available on CUDA for now.
2025-09-15 23:03:22,261 INFO 模型就绪,模式:lora
2025-09-15 23:03:30,455 INFO 加载分词器与模型...
2025-09-15 23:03:43,372 WARNING The 8-bit optimizer is not available on your device, only available on CUDA for now.
2025-09-15 23:03:47,665 INFO 模型就绪,模式:lora
2025-09-15 23:03:48,008 INFO 加载分词器与模型...
2025-09-15 23:38:43,199 INFO 加载分词器与模型...
2025-09-15 23:38:57,493 WARNING The 8-bit optimizer is not available on your device, only available on CUDA for now.
2025-09-15 23:39:01,160 INFO 模型就绪,模式:lora
2025-09-15 23:39:01,469 INFO 加载分词器与模型...
2025-09-16 00:07:10,421 INFO 加载分词器与模型...
2025-09-16 00:07:21,365 WARNING The 8-bit optimizer is not available on your device, only available on CUDA for now.
2025-09-16 00:07:23,732 INFO 模型就绪,模式:lora
2025-09-16 00:07:34,461 INFO 加载分词器与模型...
2025-09-16 00:07:47,042 WARNING The 8-bit optimizer is not available on your device, only available on CUDA for now.
2025-09-16 00:07:49,841 INFO 模型就绪,模式:lora
2025-09-16 00:07:50,038 INFO 加载分词器与模型...
2025-09-16 00:14:14,875 INFO 启动时加载分词器与模型(一次)...
2025-09-16 00:14:26,581 WARNING The 8-bit optimizer is not available on your device, only available on CUDA for now.
2025-09-16 00:14:29,926 INFO 模型就绪,模式:lora
2025-09-16 00:23:11,134 INFO 启动时加载分词器与模型(一次)...
2025-09-16 00:23:21,760 WARNING The 8-bit optimizer is not available on your device, only available on CUDA for now.
2025-09-16 00:23:25,757 INFO 模型就绪,模式:lora
2025-09-16 00:24:39,344 INFO [OK] 8月份市场部的费用是多少 | SELECT IFNULL(SUM(amount),0) AS total FROM expenses WHERE date LIKE '2024-08%'; | 1行 | 55483.7ms | lora
2025-09-16 00:36:16,271 INFO 启动时加载分词器与模型(一次)...
2025-09-16 00:36:26,659 WARNING The 8-bit optimizer is not available on your device, only available on CUDA for now.
2025-09-16 00:36:29,581 INFO 模型就绪,模式:lora
2025-09-16 00:37:47,562 INFO [OK] 8月份市场部的费用是多少? | SELECT IFNULL(SUM(amount),0) AS total FROM expenses WHERE date LIKE '2024-08%'; | 1行 | 69119.7ms | lora
2025-09-16 22:08:16,139 INFO [OK] 8月份市场部的费用是多少? | SELECT IFNULL(SUM(amount),0) AS total FROM expenses WHERE date LIKE '2024-08%'; | 1行 | 85474.0ms | lora
2025-09-16 22:13:43,585 INFO [OK] 8月份市场部的费用是多少? | SELECT IFNULL(SUM(amount),0) AS total FROM expenses WHERE date LIKE '2024-08%'; | 1行 | 76405.4ms | lora
2025-09-16 22:25:11,903 INFO 启动时加载分词器与模型(一次)...
2025-09-16 22:25:22,578 WARNING The 8-bit optimizer is not available on your device, only available on CUDA for now.
2025-09-16 22:25:25,524 INFO 模型就绪,模式:lora
2025-09-16 22:26:39,689 INFO [OK] 8月份市场部的费用是多少? | SELECT IFNULL(SUM(amount),0) AS total FROM expenses WHERE date LIKE '2024-08%'; | 1行 | 63840.8ms | lora
2025-09-20 13:26:41,948 INFO 启动时加载分词器与模型(一次)...
2025-09-20 13:27:10,782 WARNING The 8-bit optimizer is not available on your device, only available on CUDA for now.
2025-09-20 13:27:14,219 INFO 模型就绪,模式:lora
2025-09-20 13:29:03,984 INFO [OK] 8月份市场部的费用是多少? | SELECT IFNULL(SUM(amount),0) AS total FROM expenses WHERE date LIKE '2024-08%'; | 1行 | 46152.7ms | lora
2025-09-20 14:18:08,500 INFO 启动时加载分词器与模型(一次)...
2025-09-20 14:18:43,174 INFO 启动时加载分词器与模型(一次)...
2025-09-20 14:20:08,883 INFO 启动时加载分词器与模型(一次)...
2025-09-20 14:20:21,555 WARNING The 8-bit optimizer is not available on your device, only available on CUDA for now.
2025-09-20 14:20:27,136 INFO 模型就绪,模式:lora

+ 252
- 0
zhangxinyue2024103833/middleware.py View File

@@ -0,0 +1,252 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# 中间件:自然语言 -> 生成SQL -> 执行SQLite -> 返回结果(含结果导向解释)
import os, re, time, logging, sqlite3
from typing import Optional, List, Dict, Any
from contextlib import asynccontextmanager

import torch
from fastapi import FastAPI
from fastapi.responses import JSONResponse, HTMLResponse, RedirectResponse
from fastapi.staticfiles import StaticFiles
from pydantic import BaseModel
from transformers import AutoTokenizer, AutoModelForCausalLM
from peft import PeftModel

# ====== 路径与常量(按你的目录)======
BASE_MODEL_PATH = r"./deepseek1.5B"
LORA_ADAPTER_PATH = r"models/deepseek-sql-lora"
DB_PATH = r"finance_demo.db"
MAX_NEW_TOKENS = 128
RESULT_LIMIT = 1000

# ====== 日志 ======
os.makedirs("logs", exist_ok=True)
logging.basicConfig(
filename="logs/middleware.log",
level=logging.INFO,
format="%(asctime)s %(levelname)s %(message)s",
)
logging.getLogger("").addHandler(logging.StreamHandler())

# ====== UI 静态目录(绝对路径,避免工作目录问题)======
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
UI_DIR = os.path.join(BASE_DIR, "ui")

# ====== 全局模型句柄 ======
tokenizer: Optional[AutoTokenizer] = None
model: Optional[torch.nn.Module] = None
MODE = "base"
model_ready = False

# ====== SQL 安全与提示模板 ======
SYS_PROMPT = "你是一个SQL助手,只输出可在SQLite执行的SELECT语句,不要解释。"
PAT_ALLOW = re.compile(r"^\s*SELECT\b", re.IGNORECASE)
PAT_FORBID = re.compile(r"\b(UPDATE|DELETE|INSERT|DROP|ALTER|TRUNCATE|PRAGMA)\b", re.IGNORECASE)

def build_prompt(q: str) -> str:
return f"<s>[SYSTEM]\n{SYS_PROMPT}\n[/SYSTEM]\n[USER]\n{q}\n[/USER]\n[ASSISTANT]\n"

def extract_sql(text: str) -> str:
# 抓第一行SELECT;否则全局兜底
for ln in (x.strip() for x in text.splitlines() if x.strip()):
if PAT_ALLOW.search(ln) and not PAT_FORBID.search(ln):
return re.sub(r";+\s*$", ";", ln)
m = re.search(r"(SELECT.+)", text, flags=re.IGNORECASE | re.DOTALL)
if not m: return ""
chunk = m.group(1).strip()
semi = chunk.find(";")
if semi != -1: chunk = chunk[:semi+1]
return re.sub(r";+\s*$", ";", chunk)

def generate_sql(question: str) -> str:
assert tokenizer is not None and model is not None
inputs = tokenizer(build_prompt(question), return_tensors="pt")
inputs = {k: v.to("cpu") for k, v in inputs.items()}
with torch.no_grad():
out = model.generate(
**inputs, max_new_tokens=MAX_NEW_TOKENS, do_sample=False,
eos_token_id=tokenizer.eos_token_id, pad_token_id=tokenizer.eos_token_id
)
text = tokenizer.decode(out[0], skip_special_tokens=True)
sql = extract_sql(text)
if not sql or not PAT_ALLOW.match(sql) or PAT_FORBID.search(sql):
return ""
return sql

def run_sql(sql: str) -> List[Dict[str, Any]]:
q = sql.strip()
if not re.search(r"\bLIMIT\b", q, re.IGNORECASE):
q = q.rstrip(";") + f" LIMIT {RESULT_LIMIT};"
conn = sqlite3.connect(DB_PATH)
conn.row_factory = sqlite3.Row
cur = conn.cursor()
cur.execute(q)
rows = [dict(r) for r in cur.fetchall()]
conn.close()
return rows

# ====== 结果导向的人话解释 ======
RE_MONTH = re.compile(r"date\s+LIKE\s*'(\d{4}-\d{2})%'", re.IGNORECASE)
RE_RANGE = re.compile(r"date\s+BETWEEN\s*'(\d{4}-\d{2}-\d{2})'\s+AND\s*'(\d{4}-\d{2}-\d{2})'", re.IGNORECASE)
RE_DEPT = re.compile(r"department\s*=\s*'([^']+)'", re.IGNORECASE)
RE_CAT = re.compile(r"category\s*=\s*'([^']+)'", re.IGNORECASE)
RE_VEND = re.compile(r"vendor\s*=\s*'([^']+)'", re.IGNORECASE)

def _first_numeric(row: Dict[str, Any]):
for k in ["total", "sum", "actual", "budget_amount", "diff", "SUM(amount)", "COUNT(*)"]:
if k in row and isinstance(row[k], (int, float)):
return k, row[k]
for k, v in row.items():
if isinstance(v, (int, float)):
return k, v
return None, None

def _fmt_num(v):
if isinstance(v, (int, float)):
return int(v) if float(v).is_integer() else round(float(v), 2)
return v

def _ctx_from_sql(sql: str) -> str:
parts = []
m = RE_MONTH.search(sql); m and parts.append(f"{m.group(1)} 月")
r = RE_RANGE.search(sql); r and parts.append(f"{r.group(1)} 至 {r.group(2)}")
d = RE_DEPT.search(sql); d and parts.append(d.group(1))
c = RE_CAT.search(sql); c and parts.append(c.group(1))
v = RE_VEND.search(sql); v and parts.append(v.group(1))
return "、".join(parts)

def make_explanation(sql: str, rows: List[Dict[str, Any]]) -> str:
if not sql:
return "未生成有效SQL。"
ctx = _ctx_from_sql(sql)

if not rows:
return (ctx + " 没有匹配到记录。").strip()

sl = sql.lower()

# 1) 单值聚合(SUM/AVG/COUNT… 或仅一行且存在数值列)
if " sum(" in sl or " avg(" in sl or " count(" in sl or " total(" in sl or len(rows) == 1:
row = rows[0]
k, v = _first_numeric(row)
if v is not None:
label_map = {
"total": "费用",
"SUM(amount)": "费用",
"actual": "实际费用",
"budget_amount": "预算",
"COUNT(*)": "条数",
}
label = label_map.get(k, "结果")
v = _fmt_num(v)
prefix = (ctx + " ") if ctx else ""
if label in ["费用", "实际费用", "预算", "结果"] and isinstance(v, (int, float)):
return f"{prefix}{label}为 {v} 元。"
return f"{prefix}{label}为 {v}。"

# 2) 分组聚合(GROUP BY)
if " group by " in sl and rows:
key = None
for cand in ["department", "category", "vendor", "month", "date"]:
if cand in rows[0]:
key = cand; break
if not key:
for k, v in rows[0].items():
if not isinstance(v, (int, float)):
key = k; break
samples = []
for r in rows[:3]:
_, val = _first_numeric(r)
samples.append(f"{r.get(key)} { _fmt_num(val) }")
head = (ctx + " ") if ctx else ""
return f"{head}按 {key} 统计,共 {len(rows)} 行。示例:{ ','.join(samples) }。"

# 3) 明细(尝试汇总金额)
if "amount" in rows[0]:
s = sum(float(r.get("amount", 0) or 0) for r in rows)
s = _fmt_num(s)
head = (ctx + " ") if ctx else ""
return f"{head}共 {len(rows)} 条,合计 {s} 元。"

# 兜底
return f"{(ctx + ' ') if ctx else ''}明细查询,返回 {len(rows)} 行。"

# ====== 模型加载(启动时一次)======
def load_model_once():
global tokenizer, model, MODE, model_ready
if model_ready:
return
logging.info("启动时加载分词器与模型(一次)...")
tok = AutoTokenizer.from_pretrained(BASE_MODEL_PATH, use_fast=True)
base = AutoModelForCausalLM.from_pretrained(
BASE_MODEL_PATH, dtype=torch.float32, device_map=None
).to("cpu")
if os.path.isdir(LORA_ADAPTER_PATH):
mdl = PeftModel.from_pretrained(base, LORA_ADAPTER_PATH).to("cpu")
MODE = "lora"
else:
mdl = base
MODE = "base"
if hasattr(mdl, "config"):
mdl.config.use_cache = False
tokenizer, model = tok, mdl
model_ready = True
logging.info(f"模型就绪,模式:{MODE}")

# ====== FastAPI ======
@asynccontextmanager
async def lifespan(app: FastAPI):
load_model_once()
yield

app = FastAPI(title="NL→SQL 中间件", lifespan=lifespan)

# 静态页
if os.path.isdir(UI_DIR):
app.mount("/ui", StaticFiles(directory=UI_DIR, html=True), name="ui")

class AskIn(BaseModel):
query: Optional[str] = None
question: Optional[str] = None

@app.get("/")
def root():
if os.path.isfile(os.path.join(UI_DIR, "index.html")):
return RedirectResponse(url="/ui/")
return HTMLResponse("<p>未找到 ui/index.html,请检查 ui 目录。</p>")

@app.get("/health")
def health():
return {"db_exists": os.path.isfile(DB_PATH), "mode": MODE, "model_ready": model_ready}

@app.post("/ask")
def ask(body: AskIn):
if not model_ready:
return JSONResponse({"error": "服务初始化中,请稍后重试"}, status_code=503)
q = (body.query or body.question or "").strip()
if not q:
return JSONResponse({"error": "请输入问题"}, status_code=400)

t0 = time.time()
sql = generate_sql(q)
if not sql:
logging.warning(f"[BLOCKED] {q}")
return {
"query": q, "sql": sql, "rows": [],
"explanation": "未生成有效的SELECT或被安全策略拦截。"
}
try:
rows = run_sql(sql)
expl = make_explanation(sql, rows)
logging.info(f"[OK] {q} | {sql} | {len(rows)}行 | {round((time.time()-t0)*1000,1)}ms | {MODE}")
return {"query": q, "sql": sql, "rows": rows, "explanation": expl, "mode": MODE}
except Exception as e:
logging.exception(f"[ERROR] {q} | {sql} | {e}")
return JSONResponse({"query": q, "sql": sql, "error": str(e)}, status_code=500)

# 直接运行
if __name__ == "__main__":
import uvicorn
uvicorn.run("middleware:app", host="127.0.0.1", port=8000, reload=False)

+ 207
- 0
zhangxinyue2024103833/models/deepseek-sql-lora/README.md View File

@@ -0,0 +1,207 @@
---
base_model: D:/人大/结课作业/中间件/deepseek1.5B
library_name: peft
pipeline_tag: text-generation
tags:
- base_model:adapter:D:/人大/结课作业/中间件/deepseek1.5B
- lora
- transformers
---

# Model Card for Model ID

<!-- Provide a quick summary of what the model is/does. -->



## Model Details

### Model Description

<!-- Provide a longer summary of what this model is. -->



- **Developed by:** [More Information Needed]
- **Funded by [optional]:** [More Information Needed]
- **Shared by [optional]:** [More Information Needed]
- **Model type:** [More Information Needed]
- **Language(s) (NLP):** [More Information Needed]
- **License:** [More Information Needed]
- **Finetuned from model [optional]:** [More Information Needed]

### Model Sources [optional]

<!-- Provide the basic links for the model. -->

- **Repository:** [More Information Needed]
- **Paper [optional]:** [More Information Needed]
- **Demo [optional]:** [More Information Needed]

## Uses

<!-- Address questions around how the model is intended to be used, including the foreseeable users of the model and those affected by the model. -->

### Direct Use

<!-- This section is for the model use without fine-tuning or plugging into a larger ecosystem/app. -->

[More Information Needed]

### Downstream Use [optional]

<!-- This section is for the model use when fine-tuned for a task, or when plugged into a larger ecosystem/app -->

[More Information Needed]

### Out-of-Scope Use

<!-- This section addresses misuse, malicious use, and uses that the model will not work well for. -->

[More Information Needed]

## Bias, Risks, and Limitations

<!-- This section is meant to convey both technical and sociotechnical limitations. -->

[More Information Needed]

### Recommendations

<!-- This section is meant to convey recommendations with respect to the bias, risk, and technical limitations. -->

Users (both direct and downstream) should be made aware of the risks, biases and limitations of the model. More information needed for further recommendations.

## How to Get Started with the Model

Use the code below to get started with the model.

[More Information Needed]

## Training Details

### Training Data

<!-- This should link to a Dataset Card, perhaps with a short stub of information on what the training data is all about as well as documentation related to data pre-processing or additional filtering. -->

[More Information Needed]

### Training Procedure

<!-- This relates heavily to the Technical Specifications. Content here should link to that section when it is relevant to the training procedure. -->

#### Preprocessing [optional]

[More Information Needed]


#### Training Hyperparameters

- **Training regime:** [More Information Needed] <!--fp32, fp16 mixed precision, bf16 mixed precision, bf16 non-mixed precision, fp16 non-mixed precision, fp8 mixed precision -->

#### Speeds, Sizes, Times [optional]

<!-- This section provides information about throughput, start/end time, checkpoint size if relevant, etc. -->

[More Information Needed]

## Evaluation

<!-- This section describes the evaluation protocols and provides the results. -->

### Testing Data, Factors & Metrics

#### Testing Data

<!-- This should link to a Dataset Card if possible. -->

[More Information Needed]

#### Factors

<!-- These are the things the evaluation is disaggregating by, e.g., subpopulations or domains. -->

[More Information Needed]

#### Metrics

<!-- These are the evaluation metrics being used, ideally with a description of why. -->

[More Information Needed]

### Results

[More Information Needed]

#### Summary



## Model Examination [optional]

<!-- Relevant interpretability work for the model goes here -->

[More Information Needed]

## Environmental Impact

<!-- Total emissions (in grams of CO2eq) and additional considerations, such as electricity usage, go here. Edit the suggested text below accordingly -->

Carbon emissions can be estimated using the [Machine Learning Impact calculator](https://mlco2.github.io/impact#compute) presented in [Lacoste et al. (2019)](https://arxiv.org/abs/1910.09700).

- **Hardware Type:** [More Information Needed]
- **Hours used:** [More Information Needed]
- **Cloud Provider:** [More Information Needed]
- **Compute Region:** [More Information Needed]
- **Carbon Emitted:** [More Information Needed]

## Technical Specifications [optional]

### Model Architecture and Objective

[More Information Needed]

### Compute Infrastructure

[More Information Needed]

#### Hardware

[More Information Needed]

#### Software

[More Information Needed]

## Citation [optional]

<!-- If there is a paper or blog post introducing the model, the APA and Bibtex information for that should go in this section. -->

**BibTeX:**

[More Information Needed]

**APA:**

[More Information Needed]

## Glossary [optional]

<!-- If relevant, include terms and calculations in this section that can help readers understand the model or model card. -->

[More Information Needed]

## More Information [optional]

[More Information Needed]

## Model Card Authors [optional]

[More Information Needed]

## Model Card Contact

[More Information Needed]
### Framework versions

- PEFT 0.17.1

+ 42
- 0
zhangxinyue2024103833/models/deepseek-sql-lora/adapter_config.json View File

@@ -0,0 +1,42 @@
{
"alpha_pattern": {},
"auto_mapping": null,
"base_model_name_or_path": "D:/\u4eba\u5927/\u7ed3\u8bfe\u4f5c\u4e1a/\u4e2d\u95f4\u4ef6/deepseek1.5B",
"bias": "none",
"corda_config": null,
"eva_config": null,
"exclude_modules": null,
"fan_in_fan_out": false,
"inference_mode": true,
"init_lora_weights": true,
"layer_replication": null,
"layers_pattern": null,
"layers_to_transform": null,
"loftq_config": {},
"lora_alpha": 8,
"lora_bias": false,
"lora_dropout": 0.05,
"megatron_config": null,
"megatron_core": "megatron.core",
"modules_to_save": null,
"peft_type": "LORA",
"qalora_group_size": 16,
"r": 4,
"rank_pattern": {},
"revision": null,
"target_modules": [
"v_proj",
"q_proj",
"o_proj",
"gate_proj",
"k_proj",
"down_proj",
"up_proj"
],
"target_parameters": null,
"task_type": "CAUSAL_LM",
"trainable_token_indices": null,
"use_dora": false,
"use_qalora": false,
"use_rslora": false
}

BIN
zhangxinyue2024103833/models/deepseek-sql-lora/adapter_model.safetensors View File


+ 1
- 0
zhangxinyue2024103833/models/deepseek-sql-lora/chat_template.jinja View File

@@ -0,0 +1 @@
{% if not add_generation_prompt is defined %}{% set add_generation_prompt = false %}{% endif %}{% set ns = namespace(is_first=false, is_tool=false, is_output_first=true, system_prompt='') %}{%- for message in messages %}{%- if message['role'] == 'system' %}{% set ns.system_prompt = message['content'] %}{%- endif %}{%- endfor %}{{bos_token}}{{ns.system_prompt}}{%- for message in messages %}{%- if message['role'] == 'user' %}{%- set ns.is_tool = false -%}{{'<|User|>' + message['content']}}{%- endif %}{%- if message['role'] == 'assistant' and message['content'] is none %}{%- set ns.is_tool = false -%}{%- for tool in message['tool_calls']%}{%- if not ns.is_first %}{{'<|Assistant|><|tool▁calls▁begin|><|tool▁call▁begin|>' + tool['type'] + '<|tool▁sep|>' + tool['function']['name'] + '\n' + '```json' + '\n' + tool['function']['arguments'] + '\n' + '```' + '<|tool▁call▁end|>'}}{%- set ns.is_first = true -%}{%- else %}{{'\n' + '<|tool▁call▁begin|>' + tool['type'] + '<|tool▁sep|>' + tool['function']['name'] + '\n' + '```json' + '\n' + tool['function']['arguments'] + '\n' + '```' + '<|tool▁call▁end|>'}}{{'<|tool▁calls▁end|><|end▁of▁sentence|>'}}{%- endif %}{%- endfor %}{%- endif %}{%- if message['role'] == 'assistant' and message['content'] is not none %}{%- if ns.is_tool %}{{'<|tool▁outputs▁end|>' + message['content'] + '<|end▁of▁sentence|>'}}{%- set ns.is_tool = false -%}{%- else %}{% set content = message['content'] %}{% if '</think>' in content %}{% set content = content.split('</think>')[-1] %}{% endif %}{{'<|Assistant|>' + content + '<|end▁of▁sentence|>'}}{%- endif %}{%- endif %}{%- if message['role'] == 'tool' %}{%- set ns.is_tool = true -%}{%- if ns.is_output_first %}{{'<|tool▁outputs▁begin|><|tool▁output▁begin|>' + message['content'] + '<|tool▁output▁end|>'}}{%- set ns.is_output_first = false %}{%- else %}{{'\n<|tool▁output▁begin|>' + message['content'] + '<|tool▁output▁end|>'}}{%- endif %}{%- endif %}{%- endfor -%}{% if ns.is_tool %}{{'<|tool▁outputs▁end|>'}}{% endif %}{% if add_generation_prompt and not ns.is_tool %}{{'<|Assistant|><think>\n'}}{% endif %}

+ 207
- 0
zhangxinyue2024103833/models/deepseek-sql-lora/checkpoint-200/README.md View File

@@ -0,0 +1,207 @@
---
base_model: D:/人大/结课作业/中间件/deepseek1.5B
library_name: peft
pipeline_tag: text-generation
tags:
- base_model:adapter:D:/人大/结课作业/中间件/deepseek1.5B
- lora
- transformers
---

# Model Card for Model ID

<!-- Provide a quick summary of what the model is/does. -->



## Model Details

### Model Description

<!-- Provide a longer summary of what this model is. -->



- **Developed by:** [More Information Needed]
- **Funded by [optional]:** [More Information Needed]
- **Shared by [optional]:** [More Information Needed]
- **Model type:** [More Information Needed]
- **Language(s) (NLP):** [More Information Needed]
- **License:** [More Information Needed]
- **Finetuned from model [optional]:** [More Information Needed]

### Model Sources [optional]

<!-- Provide the basic links for the model. -->

- **Repository:** [More Information Needed]
- **Paper [optional]:** [More Information Needed]
- **Demo [optional]:** [More Information Needed]

## Uses

<!-- Address questions around how the model is intended to be used, including the foreseeable users of the model and those affected by the model. -->

### Direct Use

<!-- This section is for the model use without fine-tuning or plugging into a larger ecosystem/app. -->

[More Information Needed]

### Downstream Use [optional]

<!-- This section is for the model use when fine-tuned for a task, or when plugged into a larger ecosystem/app -->

[More Information Needed]

### Out-of-Scope Use

<!-- This section addresses misuse, malicious use, and uses that the model will not work well for. -->

[More Information Needed]

## Bias, Risks, and Limitations

<!-- This section is meant to convey both technical and sociotechnical limitations. -->

[More Information Needed]

### Recommendations

<!-- This section is meant to convey recommendations with respect to the bias, risk, and technical limitations. -->

Users (both direct and downstream) should be made aware of the risks, biases and limitations of the model. More information needed for further recommendations.

## How to Get Started with the Model

Use the code below to get started with the model.

[More Information Needed]

## Training Details

### Training Data

<!-- This should link to a Dataset Card, perhaps with a short stub of information on what the training data is all about as well as documentation related to data pre-processing or additional filtering. -->

[More Information Needed]

### Training Procedure

<!-- This relates heavily to the Technical Specifications. Content here should link to that section when it is relevant to the training procedure. -->

#### Preprocessing [optional]

[More Information Needed]


#### Training Hyperparameters

- **Training regime:** [More Information Needed] <!--fp32, fp16 mixed precision, bf16 mixed precision, bf16 non-mixed precision, fp16 non-mixed precision, fp8 mixed precision -->

#### Speeds, Sizes, Times [optional]

<!-- This section provides information about throughput, start/end time, checkpoint size if relevant, etc. -->

[More Information Needed]

## Evaluation

<!-- This section describes the evaluation protocols and provides the results. -->

### Testing Data, Factors & Metrics

#### Testing Data

<!-- This should link to a Dataset Card if possible. -->

[More Information Needed]

#### Factors

<!-- These are the things the evaluation is disaggregating by, e.g., subpopulations or domains. -->

[More Information Needed]

#### Metrics

<!-- These are the evaluation metrics being used, ideally with a description of why. -->

[More Information Needed]

### Results

[More Information Needed]

#### Summary



## Model Examination [optional]

<!-- Relevant interpretability work for the model goes here -->

[More Information Needed]

## Environmental Impact

<!-- Total emissions (in grams of CO2eq) and additional considerations, such as electricity usage, go here. Edit the suggested text below accordingly -->

Carbon emissions can be estimated using the [Machine Learning Impact calculator](https://mlco2.github.io/impact#compute) presented in [Lacoste et al. (2019)](https://arxiv.org/abs/1910.09700).

- **Hardware Type:** [More Information Needed]
- **Hours used:** [More Information Needed]
- **Cloud Provider:** [More Information Needed]
- **Compute Region:** [More Information Needed]
- **Carbon Emitted:** [More Information Needed]

## Technical Specifications [optional]

### Model Architecture and Objective

[More Information Needed]

### Compute Infrastructure

[More Information Needed]

#### Hardware

[More Information Needed]

#### Software

[More Information Needed]

## Citation [optional]

<!-- If there is a paper or blog post introducing the model, the APA and Bibtex information for that should go in this section. -->

**BibTeX:**

[More Information Needed]

**APA:**

[More Information Needed]

## Glossary [optional]

<!-- If relevant, include terms and calculations in this section that can help readers understand the model or model card. -->

[More Information Needed]

## More Information [optional]

[More Information Needed]

## Model Card Authors [optional]

[More Information Needed]

## Model Card Contact

[More Information Needed]
### Framework versions

- PEFT 0.17.1

+ 42
- 0
zhangxinyue2024103833/models/deepseek-sql-lora/checkpoint-200/adapter_config.json View File

@@ -0,0 +1,42 @@
{
"alpha_pattern": {},
"auto_mapping": null,
"base_model_name_or_path": "D:/\u4eba\u5927/\u7ed3\u8bfe\u4f5c\u4e1a/\u4e2d\u95f4\u4ef6/deepseek1.5B",
"bias": "none",
"corda_config": null,
"eva_config": null,
"exclude_modules": null,
"fan_in_fan_out": false,
"inference_mode": true,
"init_lora_weights": true,
"layer_replication": null,
"layers_pattern": null,
"layers_to_transform": null,
"loftq_config": {},
"lora_alpha": 8,
"lora_bias": false,
"lora_dropout": 0.05,
"megatron_config": null,
"megatron_core": "megatron.core",
"modules_to_save": null,
"peft_type": "LORA",
"qalora_group_size": 16,
"r": 4,
"rank_pattern": {},
"revision": null,
"target_modules": [
"v_proj",
"q_proj",
"o_proj",
"gate_proj",
"k_proj",
"down_proj",
"up_proj"
],
"target_parameters": null,
"task_type": "CAUSAL_LM",
"trainable_token_indices": null,
"use_dora": false,
"use_qalora": false,
"use_rslora": false
}

BIN
zhangxinyue2024103833/models/deepseek-sql-lora/checkpoint-200/adapter_model.safetensors View File


+ 1
- 0
zhangxinyue2024103833/models/deepseek-sql-lora/checkpoint-200/chat_template.jinja View File

@@ -0,0 +1 @@
{% if not add_generation_prompt is defined %}{% set add_generation_prompt = false %}{% endif %}{% set ns = namespace(is_first=false, is_tool=false, is_output_first=true, system_prompt='') %}{%- for message in messages %}{%- if message['role'] == 'system' %}{% set ns.system_prompt = message['content'] %}{%- endif %}{%- endfor %}{{bos_token}}{{ns.system_prompt}}{%- for message in messages %}{%- if message['role'] == 'user' %}{%- set ns.is_tool = false -%}{{'<|User|>' + message['content']}}{%- endif %}{%- if message['role'] == 'assistant' and message['content'] is none %}{%- set ns.is_tool = false -%}{%- for tool in message['tool_calls']%}{%- if not ns.is_first %}{{'<|Assistant|><|tool▁calls▁begin|><|tool▁call▁begin|>' + tool['type'] + '<|tool▁sep|>' + tool['function']['name'] + '\n' + '```json' + '\n' + tool['function']['arguments'] + '\n' + '```' + '<|tool▁call▁end|>'}}{%- set ns.is_first = true -%}{%- else %}{{'\n' + '<|tool▁call▁begin|>' + tool['type'] + '<|tool▁sep|>' + tool['function']['name'] + '\n' + '```json' + '\n' + tool['function']['arguments'] + '\n' + '```' + '<|tool▁call▁end|>'}}{{'<|tool▁calls▁end|><|end▁of▁sentence|>'}}{%- endif %}{%- endfor %}{%- endif %}{%- if message['role'] == 'assistant' and message['content'] is not none %}{%- if ns.is_tool %}{{'<|tool▁outputs▁end|>' + message['content'] + '<|end▁of▁sentence|>'}}{%- set ns.is_tool = false -%}{%- else %}{% set content = message['content'] %}{% if '</think>' in content %}{% set content = content.split('</think>')[-1] %}{% endif %}{{'<|Assistant|>' + content + '<|end▁of▁sentence|>'}}{%- endif %}{%- endif %}{%- if message['role'] == 'tool' %}{%- set ns.is_tool = true -%}{%- if ns.is_output_first %}{{'<|tool▁outputs▁begin|><|tool▁output▁begin|>' + message['content'] + '<|tool▁output▁end|>'}}{%- set ns.is_output_first = false %}{%- else %}{{'\n<|tool▁output▁begin|>' + message['content'] + '<|tool▁output▁end|>'}}{%- endif %}{%- endif %}{%- endfor -%}{% if ns.is_tool %}{{'<|tool▁outputs▁end|>'}}{% endif %}{% if add_generation_prompt and not ns.is_tool %}{{'<|Assistant|><think>\n'}}{% endif %}

BIN
zhangxinyue2024103833/models/deepseek-sql-lora/checkpoint-200/optimizer.pt View File


BIN
zhangxinyue2024103833/models/deepseek-sql-lora/checkpoint-200/rng_state.pth View File


BIN
zhangxinyue2024103833/models/deepseek-sql-lora/checkpoint-200/scheduler.pt View File


+ 23
- 0
zhangxinyue2024103833/models/deepseek-sql-lora/checkpoint-200/special_tokens_map.json View File

@@ -0,0 +1,23 @@
{
"bos_token": {
"content": "<|begin▁of▁sentence|>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false
},
"eos_token": {
"content": "<|end▁of▁sentence|>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false
},
"pad_token": {
"content": "<|end▁of▁sentence|>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false
}
}

+ 757497
- 0
zhangxinyue2024103833/models/deepseek-sql-lora/checkpoint-200/tokenizer.json
File diff suppressed because it is too large
View File


+ 194
- 0
zhangxinyue2024103833/models/deepseek-sql-lora/checkpoint-200/tokenizer_config.json View File

@@ -0,0 +1,194 @@
{
"add_bos_token": true,
"add_eos_token": false,
"add_prefix_space": null,
"added_tokens_decoder": {
"151643": {
"content": "<|end▁of▁sentence|>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": true
},
"151644": {
"content": "<|User|>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": false
},
"151645": {
"content": "<|Assistant|>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": false
},
"151646": {
"content": "<|begin▁of▁sentence|>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": true
},
"151647": {
"content": "<|EOT|>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": false
},
"151648": {
"content": "<think>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": false
},
"151649": {
"content": "</think>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": false
},
"151650": {
"content": "<|quad_start|>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": true
},
"151651": {
"content": "<|quad_end|>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": true
},
"151652": {
"content": "<|vision_start|>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": true
},
"151653": {
"content": "<|vision_end|>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": true
},
"151654": {
"content": "<|vision_pad|>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": true
},
"151655": {
"content": "<|image_pad|>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": true
},
"151656": {
"content": "<|video_pad|>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": true
},
"151657": {
"content": "<tool_call>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": false
},
"151658": {
"content": "</tool_call>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": false
},
"151659": {
"content": "<|fim_prefix|>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": false
},
"151660": {
"content": "<|fim_middle|>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": false
},
"151661": {
"content": "<|fim_suffix|>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": false
},
"151662": {
"content": "<|fim_pad|>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": false
},
"151663": {
"content": "<|repo_name|>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": false
},
"151664": {
"content": "<|file_sep|>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": false
}
},
"bos_token": "<|begin▁of▁sentence|>",
"clean_up_tokenization_spaces": false,
"eos_token": "<|end▁of▁sentence|>",
"extra_special_tokens": {},
"legacy": true,
"model_max_length": 16384,
"pad_token": "<|end▁of▁sentence|>",
"sp_model_kwargs": {},
"tokenizer_class": "LlamaTokenizerFast",
"unk_token": null,
"use_default_system_prompt": false
}

+ 174
- 0
zhangxinyue2024103833/models/deepseek-sql-lora/checkpoint-200/trainer_state.json View File

@@ -0,0 +1,174 @@
{
"best_global_step": null,
"best_metric": null,
"best_model_checkpoint": null,
"epoch": 7.142857142857143,
"eval_steps": 500,
"global_step": 200,
"is_hyper_param_search": false,
"is_local_process_zero": true,
"is_world_process_zero": true,
"log_history": [
{
"epoch": 0.35714285714285715,
"grad_norm": 1.7812830209732056,
"learning_rate": 9e-05,
"loss": 3.0968,
"step": 10
},
{
"epoch": 0.7142857142857143,
"grad_norm": 1.735021710395813,
"learning_rate": 9.944739353007344e-05,
"loss": 2.3665,
"step": 20
},
{
"epoch": 1.0714285714285714,
"grad_norm": 1.8056219816207886,
"learning_rate": 9.755282581475769e-05,
"loss": 1.6148,
"step": 30
},
{
"epoch": 1.4285714285714286,
"grad_norm": 1.0633488893508911,
"learning_rate": 9.43611409721806e-05,
"loss": 1.0062,
"step": 40
},
{
"epoch": 1.7857142857142856,
"grad_norm": 1.0730570554733276,
"learning_rate": 8.995939984474624e-05,
"loss": 0.7507,
"step": 50
},
{
"epoch": 2.142857142857143,
"grad_norm": 1.1507912874221802,
"learning_rate": 8.44676704559283e-05,
"loss": 0.5951,
"step": 60
},
{
"epoch": 2.5,
"grad_norm": 1.0625510215759277,
"learning_rate": 7.803575286758364e-05,
"loss": 0.5993,
"step": 70
},
{
"epoch": 2.857142857142857,
"grad_norm": 1.0893510580062866,
"learning_rate": 7.083909302476453e-05,
"loss": 0.479,
"step": 80
},
{
"epoch": 3.2142857142857144,
"grad_norm": 1.101180911064148,
"learning_rate": 6.307399704769099e-05,
"loss": 0.4169,
"step": 90
},
{
"epoch": 3.571428571428571,
"grad_norm": 1.3419430255889893,
"learning_rate": 5.495227651252315e-05,
"loss": 0.3729,
"step": 100
},
{
"epoch": 3.928571428571429,
"grad_norm": 1.4156843423843384,
"learning_rate": 4.669547078371504e-05,
"loss": 0.3188,
"step": 110
},
{
"epoch": 4.285714285714286,
"grad_norm": 1.3531630039215088,
"learning_rate": 3.852880399766243e-05,
"loss": 0.2944,
"step": 120
},
{
"epoch": 4.642857142857143,
"grad_norm": 1.1633448600769043,
"learning_rate": 3.0675041535377405e-05,
"loss": 0.2422,
"step": 130
},
{
"epoch": 5.0,
"grad_norm": 1.4601385593414307,
"learning_rate": 2.3348413563600325e-05,
"loss": 0.2885,
"step": 140
},
{
"epoch": 5.357142857142857,
"grad_norm": 0.9815771579742432,
"learning_rate": 1.6748771394307585e-05,
"loss": 0.2463,
"step": 150
},
{
"epoch": 5.714285714285714,
"grad_norm": 1.2873295545578003,
"learning_rate": 1.1056136061894384e-05,
"loss": 0.1919,
"step": 160
},
{
"epoch": 6.071428571428571,
"grad_norm": 1.52511727809906,
"learning_rate": 6.425787818636131e-06,
"loss": 0.2503,
"step": 170
},
{
"epoch": 6.428571428571429,
"grad_norm": 1.0356954336166382,
"learning_rate": 2.9840304941919415e-06,
"loss": 0.2002,
"step": 180
},
{
"epoch": 6.785714285714286,
"grad_norm": 1.298083782196045,
"learning_rate": 8.247462563808817e-07,
"loss": 0.2331,
"step": 190
},
{
"epoch": 7.142857142857143,
"grad_norm": 1.026187777519226,
"learning_rate": 6.834750376549792e-09,
"loss": 0.1929,
"step": 200
}
],
"logging_steps": 10,
"max_steps": 200,
"num_input_tokens_seen": 0,
"num_train_epochs": 8,
"save_steps": 200,
"stateful_callbacks": {
"TrainerControl": {
"args": {
"should_epoch_stop": false,
"should_evaluate": false,
"should_log": false,
"should_save": true,
"should_training_stop": true
},
"attributes": {}
}
},
"total_flos": 255102932520960.0,
"train_batch_size": 1,
"trial_name": null,
"trial_params": null
}

BIN
zhangxinyue2024103833/models/deepseek-sql-lora/checkpoint-200/training_args.bin View File


+ 23
- 0
zhangxinyue2024103833/models/deepseek-sql-lora/special_tokens_map.json View File

@@ -0,0 +1,23 @@
{
"bos_token": {
"content": "<|begin▁of▁sentence|>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false
},
"eos_token": {
"content": "<|end▁of▁sentence|>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false
},
"pad_token": {
"content": "<|end▁of▁sentence|>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false
}
}

+ 757497
- 0
zhangxinyue2024103833/models/deepseek-sql-lora/tokenizer.json
File diff suppressed because it is too large
View File


+ 194
- 0
zhangxinyue2024103833/models/deepseek-sql-lora/tokenizer_config.json View File

@@ -0,0 +1,194 @@
{
"add_bos_token": true,
"add_eos_token": false,
"add_prefix_space": null,
"added_tokens_decoder": {
"151643": {
"content": "<|end▁of▁sentence|>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": true
},
"151644": {
"content": "<|User|>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": false
},
"151645": {
"content": "<|Assistant|>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": false
},
"151646": {
"content": "<|begin▁of▁sentence|>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": true
},
"151647": {
"content": "<|EOT|>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": false
},
"151648": {
"content": "<think>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": false
},
"151649": {
"content": "</think>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": false
},
"151650": {
"content": "<|quad_start|>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": true
},
"151651": {
"content": "<|quad_end|>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": true
},
"151652": {
"content": "<|vision_start|>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": true
},
"151653": {
"content": "<|vision_end|>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": true
},
"151654": {
"content": "<|vision_pad|>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": true
},
"151655": {
"content": "<|image_pad|>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": true
},
"151656": {
"content": "<|video_pad|>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": true
},
"151657": {
"content": "<tool_call>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": false
},
"151658": {
"content": "</tool_call>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": false
},
"151659": {
"content": "<|fim_prefix|>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": false
},
"151660": {
"content": "<|fim_middle|>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": false
},
"151661": {
"content": "<|fim_suffix|>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": false
},
"151662": {
"content": "<|fim_pad|>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": false
},
"151663": {
"content": "<|repo_name|>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": false
},
"151664": {
"content": "<|file_sep|>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": false
}
},
"bos_token": "<|begin▁of▁sentence|>",
"clean_up_tokenization_spaces": false,
"eos_token": "<|end▁of▁sentence|>",
"extra_special_tokens": {},
"legacy": true,
"model_max_length": 16384,
"pad_token": "<|end▁of▁sentence|>",
"sp_model_kwargs": {},
"tokenizer_class": "LlamaTokenizerFast",
"unk_token": null,
"use_default_system_prompt": false
}

+ 74
- 0
zhangxinyue2024103833/readme.md View File

@@ -0,0 +1,74 @@
# 财务问答中间件(自然语言 → SQL → 结果)

一句话说明:这是一个**把中文问题自动翻译成 SQL,查询 SQLite,再用人话解释结果**的小系统,“中间件 + 大模型”的结合。

---

## 🧠 原理简述
1. 输入中文问题(例:8 月份市场部的费用是多少?)
2. 大模型生成对应的 SQL
3. SQLite 执行 SQL,得到结果
4. 后端把结果转成通俗解释
5. 网页展示:问题 → 解释 → SQL → 结果表

---

## 📂 目录结构
<pre>
中间件/
├── train.jsonl 微调样本(每行:问题→SQL 的 JSON)
├── finetune.py 微调脚本:用 train.jsonl 对基础模型做 LoRA 微调
├── middleware.py 后端中间件(FastAPI):问句→SQL→查询→解释→返回给前端
├── init_db.py 初始化示例库:创建/写入演示用财务数据
├── finance_demo.db SQLite 数据库(运行 init_db.py 生成)
├── deepseek1.5B/ 基础模型目录(你本地已有)
├── models/
│ └── deepseek-sql-lora/ 微调产物(LoRA 适配器)
├── ui/ 前端网页
│ ├── index.html 主页面(输入→显示解释/SQL/结果)
│ ├── screenshot.png 演示截图(下方“演示案例”会引用)
│ └── ...
└── logs/
└── middleware.log 运行日志
└── finetune.log 微调日志
</pre>

---

## 🚀 使用步骤(通俗说明)

### 1)初始化数据库
运行 `python init_db.py`
👉 生成或覆盖 `finance_demo.db`,写入示例财务数据(费用、部门、日期、预算等)。

### 2)微调小模型(可选)
运行 `python finetune.py`
👉 读取 `train.jsonl`,在 `deepseek1.5B/` 上做 LoRA 微调,结果存到 `models/deepseek-sql-lora/`。

### 3)启动后端中间件
运行 `python middleware.py`
👉 启动 FastAPI 服务,默认访问地址是 [http://127.0.0.1:8000/ui/](http://127.0.0.1:8000/ui/)。

### 4)打开前端页面演示
用浏览器访问 `http://127.0.0.1:8000/ui/`,输入问题或点击示例按钮,页面会显示:解释 → SQL → 查询结果。
推荐测试问题:
- 8月份市场部的费用是多少?
- 2024年8月各部门费用从高到低排序

---

## 🎬 演示案例


<img src="ui/screenshot.png" alt="演示页面" width="40%">

---

## ✨ 项目亮点
- **大模型驱动**:利用大模型的强大语义理解和生成能力,把自然语言问题准确转化为 SQL,降低非技术人员使用数据库的门槛。
- **结构完整**:自然语言 → SQL → 数据库 → 解释,完整演示中间件与大模型结合;
- **可用与可维护**:前端可视化输出让业务人员能直接理解查询结果,后端日志记录则方便技术人员排查问题、进行审计,保证系统可靠运行。


+ 9
- 0
zhangxinyue2024103833/requirements.txt View File

@@ -0,0 +1,9 @@
torch>=2.2.0
transformers>=4.44.0
datasets>=2.20.0
accelerate>=0.33.0
peft>=0.11.1
bitsandbytes>=0.43.1
fastapi>=0.110.0
uvicorn>=0.27.0
pydantic>=2.0.0

+ 32
- 0
zhangxinyue2024103833/train.jsonl View File

@@ -0,0 +1,32 @@
{"instruction":"请将自然语言问题转换为 SQLite 查询,并且只输出 SQL,不要其他内容。","input":"8月份市场部的费用是多少?","output":"SELECT IFNULL(SUM(amount),0) AS total FROM expenses WHERE department='市场部' AND date LIKE '2024-08%';"}
{"instruction":"请将自然语言问题转换为 SQLite 查询,并且只输出 SQL,不要其他内容。","input":"2024年第三季度研发部的总支出","output":"SELECT IFNULL(SUM(amount),0) AS total FROM expenses WHERE department='研发部' AND date BETWEEN '2024-07-01' AND '2024-09-30';"}
{"instruction":"请将自然语言问题转换为 SQLite 查询,并且只输出 SQL,不要其他内容。","input":"列出2024年7月市场部的费用明细","output":"SELECT date,category,vendor,amount FROM expenses WHERE department='市场部' AND date LIKE '2024-07%';"}
{"instruction":"请将自然语言问题转换为 SQLite 查询,并且只输出 SQL,不要其他内容。","input":"各部门在2024年8月分别花了多少钱,按从高到低排序","output":"SELECT department, IFNULL(SUM(amount),0) AS total FROM expenses WHERE date LIKE '2024-08%' GROUP BY department ORDER BY total DESC;"}
{"instruction":"请将自然语言问题转换为 SQLite 查询,并且只输出 SQL,不要其他内容。","input":"2024年截至9月的累计广告费用","output":"SELECT IFNULL(SUM(amount),0) AS total FROM expenses WHERE category='广告' AND date BETWEEN '2024-01-01' AND '2024-09-30';"}
{"instruction":"请将自然语言问题转换为 SQLite 查询,并且只输出 SQL,不要其他内容。","input":"行政部2024年上半年的平均单笔报销金额","output":"SELECT IFNULL(AVG(amount),0) AS avg_amount FROM expenses WHERE department='行政部' AND date BETWEEN '2024-01-01' AND '2024-06-30';"}
{"instruction":"请将自然语言问题转换为 SQLite 查询,并且只输出 SQL,不要其他内容。","input":"找出2024年8月金额超过2万的差旅费用","output":"SELECT date,department,vendor,amount FROM expenses WHERE category='差旅' AND date LIKE '2024-08%' AND amount > 20000 ORDER BY amount DESC;"}
{"instruction":"请将自然语言问题转换为 SQLite 查询,并且只输出 SQL,不要其他内容。","input":"2024年7月到8月,财务部在办公类支出总额","output":"SELECT IFNULL(SUM(amount),0) AS total FROM expenses WHERE department='财务部' AND category='办公' AND date BETWEEN '2024-07-01' AND '2024-08-31';"}
{"instruction":"请将自然语言问题转换为 SQLite 查询,并且只输出 SQL,不要其他内容。","input":"按月份统计2024年研发部的总费用","output":"SELECT substr(date,1,7) AS month, IFNULL(SUM(amount),0) AS total FROM expenses WHERE department='研发部' AND date BETWEEN '2024-01-01' AND '2024-12-31' GROUP BY month ORDER BY month;"}
{"instruction":"请将自然语言问题转换为 SQLite 查询,并且只输出 SQL,不要其他内容。","input":"市场部哪家供应商在2024年收款最多?返回前3名","output":"SELECT vendor, IFNULL(SUM(amount),0) AS total FROM expenses WHERE department='市场部' AND date BETWEEN '2024-01-01' AND '2024-12-31' GROUP BY vendor ORDER BY total DESC LIMIT 3;"}
{"instruction":"请将自然语言问题转换为 SQLite 查询,并且只输出 SQL,不要其他内容。","input":"2024年8月相较7月,公司整体费用环比","output":"SELECT '2024-08' AS month, IFNULL(SUM(amount),0) AS total FROM expenses WHERE date LIKE '2024-08%' UNION ALL SELECT '2024-07' AS month, IFNULL(SUM(amount),0) AS total FROM expenses WHERE date LIKE '2024-07%';"}
{"instruction":"请将自然语言问题转换为 SQLite 查询,并且只输出 SQL,不要其他内容。","input":"2024年每个部门的广告费用占比(总额和占比)","output":"WITH dept AS (SELECT department, IFNULL(SUM(amount),0) AS total FROM expenses WHERE category='广告' AND date BETWEEN '2024-01-01' AND '2024-12-31' GROUP BY department), allsum AS (SELECT IFNULL(SUM(total),0) AS s FROM dept) SELECT d.department, d.total, ROUND(d.total*1.0/a.s,4) AS ratio FROM dept d, allsum a ORDER BY d.total DESC;"}
{"instruction":"请将自然语言问题转换为 SQLite 查询,并且只输出 SQL,不要其他内容。","input":"2024年9月销售部是否超预算?","output":"SELECT b.month, b.department, b.budget_amount, IFNULL(e.actual,0) AS actual, CASE WHEN IFNULL(e.actual,0) > b.budget_amount THEN 1 ELSE 0 END AS is_over FROM budgets b LEFT JOIN (SELECT substr(date,1,7) AS month, department, SUM(amount) AS actual FROM expenses WHERE date LIKE '2024-09%' GROUP BY department) e ON b.month=e.month AND b.department=e.department WHERE b.month='2024-09' AND b.department='销售部';"}
{"instruction":"请将自然语言问题转换为 SQLite 查询,并且只输出 SQL,不要其他内容。","input":"列出2024年8月所有部门的预算与实际对比","output":"SELECT b.department, b.budget_amount, IFNULL(e.actual,0) AS actual, (IFNULL(e.actual,0)-b.budget_amount) AS diff FROM budgets b LEFT JOIN (SELECT department, SUM(amount) AS actual FROM expenses WHERE date LIKE '2024-08%' GROUP BY department) e ON b.department=e.department WHERE b.month='2024-08' ORDER BY diff DESC;"}
{"instruction":"请将自然语言问题转换为 SQLite 查询,并且只输出 SQL,不要其他内容。","input":"请给出2024年每月的总费用(时间升序)","output":"SELECT substr(date,1,7) AS month, IFNULL(SUM(amount),0) AS total FROM expenses WHERE date BETWEEN '2024-01-01' AND '2024-12-31' GROUP BY month ORDER BY month;"}
{"instruction":"请将自然语言问题转换为 SQLite 查询,并且只输出 SQL,不要其他内容。","input":"找出2024-10-01到2024-10-07期间的支出","output":"SELECT date,department,category,vendor,amount FROM expenses WHERE date BETWEEN '2024-10-01' AND '2024-10-07' ORDER BY amount DESC;"}
{"instruction":"请将自然语言问题转换为 SQLite 查询,并且只输出 SQL,不要其他内容。","input":"2024年培训类费用最高的部门是哪个?","output":"SELECT department FROM expenses WHERE category='培训' AND date BETWEEN '2024-01-01' AND '2024-12-31' GROUP BY department ORDER BY SUM(amount) DESC LIMIT 1;"}
{"instruction":"请将自然语言问题转换为 SQLite 查询,并且只输出 SQL,不要其他内容。","input":"过去三个月(截至2024-09-30)各部门总费用","output":"SELECT department, IFNULL(SUM(amount),0) AS total FROM expenses WHERE date BETWEEN '2024-07-01' AND '2024-09-30' GROUP BY department ORDER BY total DESC;"}
{"instruction":"请将自然语言问题转换为 SQLite 查询,并且只输出 SQL,不要其他内容。","input":"列出2024年8月金额超过10000的所有记录","output":"SELECT date,department,category,vendor,amount FROM expenses WHERE date LIKE '2024-08%' AND amount > 10000 ORDER BY amount DESC;"}
{"instruction":"请将自然语言问题转换为 SQLite 查询,并且只输出 SQL,不要其他内容。","input":"2024年8月各费用类别的支出占比","output":"WITH cat AS (SELECT category, IFNULL(SUM(amount),0) AS total FROM expenses WHERE date LIKE '2024-08%' GROUP BY category), s AS (SELECT IFNULL(SUM(total),0) AS allsum FROM cat) SELECT c.category, c.total, ROUND(c.total*1.0/s.allsum,4) AS ratio FROM cat c, s ORDER BY c.total DESC;"}
{"instruction":"请将自然语言问题转换为 SQLite 查询,并且只输出 SQL,不要其他内容。","input":"市场部2024年每月的平均单笔金额","output":"SELECT substr(date,1,7) AS month, IFNULL(AVG(amount),0) AS avg_amount FROM expenses WHERE department='市场部' AND date BETWEEN '2024-01-01' AND '2024-12-31' GROUP BY month ORDER BY month;"}
{"instruction":"请将自然语言问题转换为 SQLite 查询,并且只输出 SQL,不要其他内容。","input":"2024年上半年公司总费用、下半年公司总费用","output":"SELECT 'H1' AS half, IFNULL(SUM(amount),0) AS total FROM expenses WHERE date BETWEEN '2024-01-01' AND '2024-06-30' UNION ALL SELECT 'H2' AS half, IFNULL(SUM(amount),0) AS total FROM expenses WHERE date BETWEEN '2024-07-01' AND '2024-12-31';"}
{"instruction":"请将自然语言问题转换为 SQLite 查询,并且只输出 SQL,不要其他内容。","input":"2024年8月哪天的支出最高?","output":"SELECT date, SUM(amount) AS total FROM expenses WHERE date LIKE '2024-08%' GROUP BY date ORDER BY total DESC LIMIT 1;"}
{"instruction":"请将自然语言问题转换为 SQLite 查询,并且只输出 SQL,不要其他内容。","input":"列出2024年每个部门的最高单笔支出","output":"SELECT department, MAX(amount) AS max_amount FROM expenses WHERE date BETWEEN '2024-01-01' AND '2024-12-31' GROUP BY department ORDER BY max_amount DESC;"}
{"instruction":"请将自然语言问题转换为 SQLite 查询,并且只输出 SQL,不要其他内容。","input":"2024年8月人力费用总额和笔数","output":"SELECT IFNULL(SUM(amount),0) AS total, COUNT(*) AS cnt FROM expenses WHERE category='人力' AND date LIKE '2024-08%';"}
{"instruction":"请将自然语言问题转换为 SQLite 查询,并且只输出 SQL,不要其他内容。","input":"供应商“XX传媒”在2024年收了多少钱?","output":"SELECT IFNULL(SUM(amount),0) AS total FROM expenses WHERE vendor='XX传媒' AND date BETWEEN '2024-01-01' AND '2024-12-31';"}
{"instruction":"请将自然语言问题转换为 SQLite 查询,并且只输出 SQL,不要其他内容。","input":"2024年8月预算执行率(按部门)","output":"SELECT b.department, b.budget_amount, IFNULL(e.actual,0) AS actual, CASE WHEN b.budget_amount>0 THEN ROUND(IFNULL(e.actual,0)*1.0/b.budget_amount,4) ELSE NULL END AS exec_ratio FROM budgets b LEFT JOIN (SELECT department, SUM(amount) AS actual FROM expenses WHERE date LIKE '2024-08%' GROUP BY department) e ON b.department=e.department WHERE b.month='2024-08' ORDER BY exec_ratio DESC;"}
{"instruction":"请将自然语言问题转换为 SQLite 查询,并且只输出 SQL,不要其他内容。","input":"2024年Q1公司总费用(1-3月)","output":"SELECT IFNULL(SUM(amount),0) AS total FROM expenses WHERE date BETWEEN '2024-01-01' AND '2024-03-31';"}
{"instruction":"请将自然语言问题转换为 SQLite 查询,并且只输出 SQL,不要其他内容。","input":"2024年7月市场部与销售部费用对比","output":"SELECT department, IFNULL(SUM(amount),0) AS total FROM expenses WHERE department IN ('市场部','销售部') AND date LIKE '2024-07%' GROUP BY department ORDER BY total DESC;"}
{"instruction":"请将自然语言问题转换为 SQLite 查询,并且只输出 SQL,不要其他内容。","input":"最近三个月每月广告费用(截至2024-09)","output":"SELECT substr(date,1,7) AS month, IFNULL(SUM(amount),0) AS total FROM expenses WHERE category='广告' AND date BETWEEN '2024-07-01' AND '2024-09-30' GROUP BY month ORDER BY month;"}
{"instruction":"请将自然语言问题转换为 SQLite 查询,并且只输出 SQL,不要其他内容。","input":"2024年每个部门的人力费用,从高到低","output":"SELECT department, IFNULL(SUM(amount),0) AS total FROM expenses WHERE category='人力' AND date BETWEEN '2024-01-01' AND '2024-12-31' GROUP BY department ORDER BY total DESC;"}
{"instruction":"请将自然语言问题转换为 SQLite 查询,并且只输出 SQL,不要其他内容。","input":"2024年8月各部门单笔均值、最大最小","output":"SELECT department, IFNULL(AVG(amount),0) AS avg_amount, MAX(amount) AS max_amount, MIN(amount) AS min_amount FROM expenses WHERE date LIKE '2024-08%' GROUP BY department ORDER BY avg_amount DESC;"}

+ 193
- 0
zhangxinyue2024103833/ui/index.html View File

@@ -0,0 +1,193 @@
<!doctype html>
<html lang="zh">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>财务自然语言 → SQL 演示</title>
<style>
:root{
--text:#0f172a; --muted:#64748b; --border:#e5e7eb;
--brand:#2563eb; --brand2:#7c3aed; --accent:#06b6d4;
--card:#ffffff; --shadow:0 14px 30px rgba(2,6,23,.08);
--mono: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
}
*{box-sizing:border-box}
body{
margin:0; color:var(--text);
font:14px/1.6 system-ui,-apple-system,"Segoe UI",Roboto,"PingFang SC","Hiragino Sans GB","Microsoft YaHei",sans-serif;
background:#f6f8fc;
}
.container{max-width:900px;margin:30px auto;padding:0 18px}

.header{display:flex;align-items:center;gap:14px;margin:0 0 20px}
.logo{width:44px;height:44px;border-radius:50%;
background:conic-gradient(from 210deg, var(--brand), var(--accent), var(--brand2), var(--brand));
box-shadow:var(--shadow)}
.title{font-weight:900;font-size:22px;}
.sub{color:var(--muted);font-size:13px}

.card{
background:var(--card); border:1px solid var(--border);
border-radius:20px; box-shadow:var(--shadow); overflow:hidden;
margin-bottom:18px;
}
.card-h{padding:14px 18px 10px; font-weight:800;}
.card-b{padding:16px 18px 18px}

/* 输入区 */
.input-wrap{position:relative}
.input{
width:100%; height:54px; padding:0 18px;
border:1px solid var(--border); border-radius:999px; outline:none; background:#fff; font-size:15px;
}
.actions{display:flex; gap:10px; margin-top:12px; flex-wrap:wrap}
.btn{
appearance:none; border:0; color:#fff; cursor:pointer;
background:linear-gradient(135deg, var(--brand), var(--brand2));
border-radius:999px; padding:10px 16px; font-weight:800;
box-shadow:0 8px 18px rgba(37,99,235,.18);
}
.btn-gray{background:#94a3b8; box-shadow:none}
.btn-round{width:42px;height:42px;display:flex;align-items:center;justify-content:center;
border-radius:50%; font-weight:900;}

.status{min-height:18px;color:var(--muted);font-size:13px;margin-top:6px}

/* 内容模块 */
.pill{
border:1px solid var(--border); border-radius:18px;
background:#f9fafb; padding:16px 20px;
}
.pill.mono{font-family:var(--mono); white-space:pre-wrap; word-break:break-all;}

/* 复制按钮 */
.copy-btn{
float:right; margin:-6px -6px 0 0;
border:none; border-radius:50%;
width:34px;height:34px; background:linear-gradient(135deg,var(--brand),var(--brand2));
color:#fff; font-weight:800; cursor:pointer;
}

/* 表格 */
.table-wrap{overflow:auto; border:1px solid var(--border); border-radius:16px}
table{border-collapse:collapse; width:100%}
th,td{border-bottom:1px solid var(--border); padding:10px; text-align:left}
th{background:#f8fafc}
tr:nth-child(even) td{background:#fbfdff}
.empty{color:var(--muted); padding:14px}
</style>
</head>
<body>
<div class="container">
<div class="header">
<div class="logo"></div>
<div>
<div class="title">财务自然语言 → SQL 演示</div>
<div class="sub">输入问题 → 查看解释 → SQL → 结果</div>
</div>
</div>

<!-- 输入 -->
<div class="card">
<div class="card-h">输入问题</div>
<div class="card-b">
<div class="input-wrap">
<input id="q" class="input" placeholder="点击示例或输入…" />
</div>
<div class="actions">
<button id="btnAsk" class="btn">查询</button>
<button id="btnClear" class="btn btn-gray">清空</button>
<button class="btn btn-round" data-q="8月份市场部的费用是多少?">1</button>
<button class="btn btn-round" data-q="2024年8月各部门费用从高到低排序">2</button>
</div>
<div id="status" class="status"></div>
</div>
</div>

<!-- 解释 -->
<div class="card">
<div class="card-h">解释</div>
<div class="card-b">
<div class="pill" id="explain">(等待查询)</div>
</div>
</div>

<!-- SQL -->
<div class="card">
<div class="card-h">生成的 SQL
<button id="btnCopy" class="copy-btn" title="复制 SQL">⧉</button>
</div>
<div class="card-b">
<div class="pill mono" id="sql">(等待查询)</div>
</div>
</div>

<!-- 结果 -->
<div class="card">
<div class="card-h">查询结果</div>
<div class="card-b">
<div id="result" class="empty">(暂无结果)</div>
</div>
</div>
</div>

<script>
const $ = s=>document.querySelector(s);
const qInput=$('#q'), btnAsk=$('#btnAsk'), btnClear=$('#btnClear'), btnCopy=$('#btnCopy');
const explainBox=$('#explain'), sqlBox=$('#sql'), resultBox=$('#result'), status=$('#status');

document.querySelectorAll('[data-q]').forEach(el=>{
el.addEventListener('click', ()=>{ qInput.value = el.getAttribute('data-q'); });
});

function renderTable(rows){
if(!rows || !rows.length) return '<div class="empty">无结果</div>';
const cols=Object.keys(rows[0]);
const head='<thead><tr>'+cols.map(c=>`<th>${c}</th>`).join('')+'</tr></thead>';
const body='<tbody>'+rows.map(r=>'<tr>'+cols.map(c=>`<td>${r[c]}</td>`).join('')+'</tr>').join('')+'</tbody>';
return `<div class="table-wrap"><table>${head}${body}</table></div>`;
}

function askBackend(query){
const url = location.protocol==='file:' ? 'http://127.0.0.1:8000/ask' : '/ask';
return fetch(url,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({query})})
.then(r=>r.json());
}

btnAsk.addEventListener('click', async ()=>{
const q=qInput.value.trim();
if(!q){ status.textContent='请输入问题'; return; }
status.textContent='查询中…'; btnAsk.disabled=true;

explainBox.textContent='(生成中…)';
sqlBox.textContent='(生成中…)';
resultBox.innerHTML='<div class="empty">生成中…</div>';

try{
const data=await askBackend(q);
explainBox.textContent=data.explanation||'(无解释)';
sqlBox.textContent=data.sql||'(无SQL)';
resultBox.innerHTML=renderTable(data.rows||[]);
status.textContent='完成';
}catch(e){
status.textContent='请求失败'; status.style.color='#ef4444';
console.error(e);
}finally{btnAsk.disabled=false;}
});

btnClear.addEventListener('click', ()=>{
qInput.value=''; status.textContent='';
explainBox.textContent='(等待查询)';
sqlBox.textContent='(等待查询)';
resultBox.innerHTML='(暂无结果)';
});

btnCopy.addEventListener('click', async ()=>{
const t=sqlBox.textContent.trim();
if(!t||t.startsWith('(')) return;
try{await navigator.clipboard.writeText(t); btnCopy.textContent='✓'; setTimeout(()=>btnCopy.textContent='⧉',1200);}
catch{btnCopy.textContent='!'; setTimeout(()=>btnCopy.textContent='⧉',1200);}
});
</script>
</body>
</html>

BIN
zhangxinyue2024103833/ui/screenshot.png View File

Before After
Width: 1438  |  Height: 1376  |  Size: 114 KiB

Loading…
Cancel
Save
Baidu
map