aciang commited on
Commit
a94c11b
·
verified ·
1 Parent(s): 3c8e2b5

Upload folder using huggingface_hub

Browse files
Files changed (3) hide show
  1. README.md +11 -6
  2. app.py +181 -0
  3. requirements.txt +10 -0
README.md CHANGED
@@ -1,12 +1,17 @@
1
  ---
2
- title: LanguageBridge MathHybrid Phi2
3
- emoji: 🐨
4
- colorFrom: pink
5
- colorTo: yellow
6
  sdk: gradio
7
- sdk_version: 5.49.1
8
  app_file: app.py
9
  pinned: false
10
  ---
11
 
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
1
  ---
2
+ title: LanguageBridge Math Hybrid (Phi + SymPy)
3
+ emoji: 🧮
4
+ colorFrom: yellow
5
+ colorTo: blue
6
  sdk: gradio
7
+ sdk_version: "4.44.1"
8
  app_file: app.py
9
  pinned: false
10
  ---
11
 
12
+ 一個**小型 LLM(Phi-2 / Phi-3-mini)+ SymPy** 的數學混合推理 Space:
13
+ - LLM:把**文字題**解析為可計算的數學式或步驟
14
+ - SymPy:遇到**可符號計算**的部分(方程、微積分、因式分解…)直接算出精準解
15
+ - 自動偵測:若輸入就是算式/方程 → 直接 SymPy;否則走 LLM→SymPy 流程
16
+
17
+ > 預設模型:`microsoft/phi-2`(可在 app.py 換成你喜歡的小型模型)
app.py ADDED
@@ -0,0 +1,181 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os, sys, re, json
2
+ import gradio as gr
3
+ import sympy as sp
4
+
5
+ from transformers import (
6
+ AutoTokenizer, AutoModelForCausalLM, pipeline
7
+ )
8
+
9
+ TITLE = "LanguageBridge — Math Hybrid (Phi + SymPy)"
10
+ MODEL_ID = "microsoft/phi-2"
11
+ MAX_NEW_TOKENS = 512
12
+
13
+ def try_sympy_direct(q: str):
14
+ """若使用者輸入就是算式/方程,走 SymPy 精準計算。支援多行 / 分號分隔 / 聯立。"""
15
+ q = (q or "").strip()
16
+ if not q:
17
+ return None
18
+
19
+ # 粗略偵測:若含 = 或 明顯算式符號
20
+ if any(s in q for s in ["=", "+", "-", "*", "/", "^", "sin", "cos", "tan", "log", "sqrt", "∫", "d/dx"]):
21
+ try:
22
+ # 支援多式/聯立:以分號或換行切
23
+ parts = [s.strip() for seg in q.split(";") for s in seg.split("\n")]
24
+ eqs, syms = [], set()
25
+ for s in parts:
26
+ if not s:
27
+ continue
28
+ if "=" in s:
29
+ left, right = s.split("=", 1)
30
+ eq = sp.Eq(sp.sympify(left), sp.sympify(right))
31
+ eqs.append(eq)
32
+ syms |= eq.free_symbols
33
+ syms |= eq.rhs.free_symbols if hasattr(eq, "rhs") else set()
34
+ else:
35
+ # 非方程,當作一般表達式,做一輪常見操作
36
+ expr = sp.sympify(s)
37
+ tips = []
38
+ try:
39
+ tips.append(f"簡化:{sp.simplify(expr)}")
40
+ except Exception:
41
+ pass
42
+ try:
43
+ x = list(expr.free_symbols)[0] if expr.free_symbols else sp.symbols("x")
44
+ tips.append(f"對 {x} 微分:{sp.diff(expr, x)}")
45
+ tips.append(f"對 {x} 積分:{sp.integrate(expr, x)}")
46
+ except Exception:
47
+ pass
48
+ if tips:
49
+ return "\n".join(tips) # 只要有一行就回傳
50
+ else:
51
+ return f"結果:{expr}"
52
+
53
+ if eqs:
54
+ if not syms:
55
+ x = sp.symbols("x")
56
+ syms = {x}
57
+ sol = sp.solve(eqs, list(syms), dict=True)
58
+ if not sol:
59
+ return "SymPy:無解或需要更多條件。"
60
+ lines = []
61
+ for i, sdict in enumerate(sol, 1):
62
+ lines.append("解 {}: ".format(i) + ", ".join([f"{k} = {sp.simplify(v)}" for k, v in sdict.items()]))
63
+ return "\n".join(lines)
64
+
65
+ except Exception as e:
66
+ # 交給 LLM 流程
67
+ return None
68
+
69
+ return None
70
+
71
+
72
+ def build_llm():
73
+ """嘗試以 4-bit 啟動(有 CUDA 時),否則退回 CPU。"""
74
+ import torch
75
+ has_cuda = torch.cuda.is_available()
76
+ load_kwargs = {"device_map":"auto"}
77
+
78
+ if has_cuda:
79
+ try:
80
+ import bitsandbytes as bnb # 檢查有無 bnb
81
+ load_kwargs.update({"load_in_4bit": True})
82
+ except Exception:
83
+ # 沒有 bnb 就用 fp16
84
+ load_kwargs.update({"torch_dtype": torch.float16})
85
+ else:
86
+ # CPU:讓 transformers 自行決定 dtype
87
+ pass
88
+
89
+ tok = AutoTokenizer.from_pretrained(MODEL_ID, trust_remote_code=True)
90
+ if tok.pad_token_id is None and tok.eos_token_id is not None:
91
+ tok.pad_token = tok.eos_token
92
+
93
+ mdl = AutoModelForCausalLM.from_pretrained(
94
+ MODEL_ID,
95
+ trust_remote_code=True,
96
+ **load_kwargs
97
+ )
98
+
99
+ pipe = pipeline(
100
+ "text-generation",
101
+ model=mdl,
102
+ tokenizer=tok,
103
+ do_sample=False,
104
+ max_new_tokens=MAX_NEW_TOKENS,
105
+ temperature=0.2,
106
+ top_p=0.9
107
+ )
108
+ return pipe
109
+
110
+ LLM = None
111
+
112
+ SYSTEM_PROMPT = (
113
+ "You are a math teacher. When the user asks a word problem,\n"
114
+ "1) parse it into a clean mathematical expression or a system of equations;\n"
115
+ "2) if it is solvable by SymPy, output a single line starting with 'SymPy:' followed by a Python/SymPy expression;\n"
116
+ "3) then give a concise final answer on the next line starting with 'Answer:'."
117
+ )
118
+
119
+ def llm_to_sympy_and_answer(pipe, q: str):
120
+ prompt = (
121
+ f"<s>System:\n{SYSTEM_PROMPT}\n</s>\n"
122
+ f"User: {q}\n"
123
+ f"Assistant:"
124
+ )
125
+ out = pipe(prompt, pad_token_id=pipe.tokenizer.eos_token_id)[0]["generated_text"]
126
+ # 嘗試抓 SymPy: 行
127
+ sym_line = None
128
+ ans_line = None
129
+ for line in out.splitlines():
130
+ if line.strip().startswith("SymPy:"):
131
+ sym_line = line.split("SymPy:",1)[-1].strip()
132
+ if line.strip().startswith("Answer:"):
133
+ ans_line = line.split("Answer:",1)[-1].strip()
134
+
135
+ checked = ""
136
+ if sym_line:
137
+ try:
138
+ val = eval(sym_line, {"sp": sp, "sympy": sp})
139
+ # 若是可列印的結果(非方程),試著數值化或簡化
140
+ if isinstance(val, (int, float, sp.Basic)):
141
+ checked = f"SymPy 檢算:{sp.simplify(val)}"
142
+ except Exception as e:
143
+ checked = f"SymPy 檢算失敗:{e}"
144
+
145
+ merge = []
146
+ if sym_line: merge.append(f"SymPy: {sym_line}")
147
+ if ans_line: merge.append(f"Answer: {ans_line}")
148
+ if checked: merge.append(checked)
149
+ return "\n".join(merge) if merge else out
150
+
151
+
152
+ def solve(q: str):
153
+ global LLM
154
+ q = (q or "").strip()
155
+ if not q:
156
+ return "請輸入題目或算式。"
157
+
158
+ # 1) 先嘗試 SymPy 直接處理
159
+ direct = try_sympy_direct(q)
160
+ if direct:
161
+ return direct
162
+
163
+ # 2) 走 LLM → SymPy
164
+ if LLM is None:
165
+ LLM = build_llm()
166
+ try:
167
+ return llm_to_sympy_and_answer(LLM, q)
168
+ except Exception as e:
169
+ return f"[LLM流程失敗] {e}"
170
+
171
+ with gr.Blocks(title=TITLE) as demo:
172
+ gr.Markdown(f"## {TITLE}\n貼上文字題或算式:LLM 解析 → SymPy 精算(可聯立)")
173
+ with gr.Row():
174
+ q = gr.Textbox(label="題目 / 算式", lines=8, placeholder="例如:一個數加上 5 等於 11,求這個數。\n或:2*x + 5 = 11;或:sin(x)**2 + cos(x)**2")
175
+ with gr.Row():
176
+ out = gr.Textbox(label="輸出", lines=12)
177
+ btn = gr.Button("送出 🚀")
178
+ btn.click(fn=solve, inputs=q, outputs=out)
179
+
180
+ if __name__ == "__main__":
181
+ demo.launch()
requirements.txt ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ gradio==4.44.1
2
+ transformers==4.44.2
3
+ accelerate>=0.31.0
4
+ bitsandbytes==0.43.3
5
+ sentencepiece
6
+ sympy>=1.12
7
+ huggingface_hub>=0.24.0
8
+ safetensors
9
+ einops
10
+ numpy<2