Spaces:
Sleeping
Sleeping
feat: add problem types and monitoring
Browse files- app.py +107 -39
- monitoring.py +63 -0
app.py
CHANGED
@@ -3,11 +3,15 @@ import torch
|
|
3 |
from transformers import AutoModelForCausalLM, AutoTokenizer
|
4 |
from peft import PeftModel
|
5 |
import spaces
|
|
|
6 |
|
7 |
# Model configurations
|
8 |
BASE_MODEL = "HuggingFaceTB/SmolLM2-1.7B-Instruct" # Base model
|
9 |
ADAPTER_MODEL = "Joash2024/Math-SmolLM2-1.7B" # Our LoRA adapter
|
10 |
|
|
|
|
|
|
|
11 |
print("Loading tokenizer...")
|
12 |
tokenizer = AutoTokenizer.from_pretrained(BASE_MODEL)
|
13 |
tokenizer.pad_token = tokenizer.eos_token
|
@@ -30,18 +34,30 @@ model = PeftModel.from_pretrained(
|
|
30 |
)
|
31 |
model.eval()
|
32 |
|
33 |
-
def format_prompt(
|
34 |
"""Format input prompt for the model"""
|
35 |
-
|
|
|
36 |
|
37 |
-
Function: {
|
38 |
The derivative of this function is:"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
39 |
|
40 |
@spaces.GPU
|
41 |
-
|
42 |
-
|
|
|
43 |
# Format prompt
|
44 |
-
prompt = format_prompt(
|
45 |
|
46 |
# Tokenize
|
47 |
inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
|
@@ -50,75 +66,127 @@ def generate_derivative(function: str, max_length: int = 100) -> str:
|
|
50 |
with torch.no_grad():
|
51 |
outputs = model.generate(
|
52 |
**inputs,
|
53 |
-
max_length=
|
54 |
num_return_sequences=1,
|
55 |
temperature=0.1,
|
56 |
do_sample=False, # Deterministic generation
|
57 |
pad_token_id=tokenizer.eos_token_id
|
58 |
)
|
59 |
|
60 |
-
# Decode and extract
|
61 |
generated = tokenizer.decode(outputs[0], skip_special_tokens=True)
|
62 |
-
|
63 |
|
64 |
-
return
|
65 |
|
66 |
@spaces.GPU
|
67 |
-
def
|
68 |
-
"""Solve
|
69 |
-
if not
|
70 |
-
return "Please enter a
|
71 |
|
72 |
-
|
73 |
-
|
74 |
|
75 |
-
#
|
76 |
-
|
|
|
|
|
|
|
|
|
77 |
|
78 |
Let's verify this step by step:
|
79 |
-
1. Starting with f(x) = {
|
80 |
2. Applying differentiation rules
|
81 |
-
3. We get f'(x) = {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
82 |
|
83 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
84 |
|
85 |
# Create Gradio interface
|
86 |
-
with gr.Blocks(title="Mathematics
|
87 |
-
gr.Markdown("# Mathematics
|
88 |
-
gr.Markdown("Using our fine-tuned model to solve
|
89 |
|
90 |
with gr.Row():
|
91 |
with gr.Column():
|
92 |
-
|
93 |
-
|
94 |
-
|
|
|
|
|
|
|
|
|
|
|
95 |
)
|
96 |
-
solve_btn = gr.Button("
|
97 |
|
98 |
with gr.Row():
|
99 |
-
|
100 |
label="Solution with Steps",
|
101 |
lines=6
|
102 |
)
|
103 |
|
104 |
-
#
|
|
|
|
|
|
|
|
|
105 |
gr.Examples(
|
106 |
examples=[
|
107 |
-
["x^2"],
|
108 |
-
["
|
109 |
-
["
|
|
|
|
|
|
|
110 |
],
|
111 |
-
inputs=
|
112 |
-
outputs=
|
113 |
-
fn=
|
114 |
cache_examples=False # Disable caching
|
115 |
)
|
116 |
|
117 |
# Connect the interface
|
118 |
solve_btn.click(
|
119 |
-
fn=
|
120 |
-
inputs=[
|
121 |
-
outputs=
|
122 |
)
|
123 |
|
124 |
if __name__ == "__main__":
|
|
|
3 |
from transformers import AutoModelForCausalLM, AutoTokenizer
|
4 |
from peft import PeftModel
|
5 |
import spaces
|
6 |
+
from monitoring import PerformanceMonitor, measure_time
|
7 |
|
8 |
# Model configurations
|
9 |
BASE_MODEL = "HuggingFaceTB/SmolLM2-1.7B-Instruct" # Base model
|
10 |
ADAPTER_MODEL = "Joash2024/Math-SmolLM2-1.7B" # Our LoRA adapter
|
11 |
|
12 |
+
# Initialize performance monitor
|
13 |
+
monitor = PerformanceMonitor()
|
14 |
+
|
15 |
print("Loading tokenizer...")
|
16 |
tokenizer = AutoTokenizer.from_pretrained(BASE_MODEL)
|
17 |
tokenizer.pad_token = tokenizer.eos_token
|
|
|
34 |
)
|
35 |
model.eval()
|
36 |
|
37 |
+
def format_prompt(problem: str, problem_type: str) -> str:
|
38 |
"""Format input prompt for the model"""
|
39 |
+
if problem_type == "Derivative":
|
40 |
+
return f"""Given a mathematical function, find its derivative.
|
41 |
|
42 |
+
Function: {problem}
|
43 |
The derivative of this function is:"""
|
44 |
+
elif problem_type == "Addition":
|
45 |
+
return f"""Solve this addition problem.
|
46 |
+
|
47 |
+
Problem: {problem}
|
48 |
+
The solution is:"""
|
49 |
+
else: # Roots or Custom
|
50 |
+
return f"""Find the roots of this equation.
|
51 |
+
|
52 |
+
Equation: {problem}
|
53 |
+
The roots are:"""
|
54 |
|
55 |
@spaces.GPU
|
56 |
+
@measure_time
|
57 |
+
def get_model_response(problem: str, problem_type: str) -> str:
|
58 |
+
"""Generate response from model"""
|
59 |
# Format prompt
|
60 |
+
prompt = format_prompt(problem, problem_type)
|
61 |
|
62 |
# Tokenize
|
63 |
inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
|
|
|
66 |
with torch.no_grad():
|
67 |
outputs = model.generate(
|
68 |
**inputs,
|
69 |
+
max_length=100,
|
70 |
num_return_sequences=1,
|
71 |
temperature=0.1,
|
72 |
do_sample=False, # Deterministic generation
|
73 |
pad_token_id=tokenizer.eos_token_id
|
74 |
)
|
75 |
|
76 |
+
# Decode and extract response
|
77 |
generated = tokenizer.decode(outputs[0], skip_special_tokens=True)
|
78 |
+
response = generated[len(prompt):].strip()
|
79 |
|
80 |
+
return response
|
81 |
|
82 |
@spaces.GPU
|
83 |
+
def solve_problem(problem: str, problem_type: str) -> tuple:
|
84 |
+
"""Solve math problem and track performance"""
|
85 |
+
if not problem:
|
86 |
+
return "Please enter a problem", None
|
87 |
|
88 |
+
# Record problem type
|
89 |
+
monitor.record_problem_type(problem_type)
|
90 |
|
91 |
+
# Get model response with timing
|
92 |
+
response, time_taken = get_model_response(problem, problem_type)
|
93 |
+
|
94 |
+
# Format output with steps
|
95 |
+
if problem_type == "Derivative":
|
96 |
+
output = f"""Generated derivative: {response}
|
97 |
|
98 |
Let's verify this step by step:
|
99 |
+
1. Starting with f(x) = {problem}
|
100 |
2. Applying differentiation rules
|
101 |
+
3. We get f'(x) = {response}"""
|
102 |
+
elif problem_type == "Addition":
|
103 |
+
output = f"""Solution: {response}
|
104 |
+
|
105 |
+
Let's verify this step by step:
|
106 |
+
1. Starting with: {problem}
|
107 |
+
2. Adding the numbers
|
108 |
+
3. We get: {response}"""
|
109 |
+
else: # Roots
|
110 |
+
output = f"""Found roots: {response}
|
111 |
+
|
112 |
+
Let's verify this step by step:
|
113 |
+
1. Starting with equation: {problem}
|
114 |
+
2. Solving for x
|
115 |
+
3. Roots are: {response}"""
|
116 |
+
|
117 |
+
# Record metrics
|
118 |
+
monitor.record_response_time("model", time_taken)
|
119 |
+
monitor.record_success("model", not response.startswith("Error"))
|
120 |
|
121 |
+
# Get updated statistics
|
122 |
+
stats = monitor.get_statistics()
|
123 |
+
|
124 |
+
# Format statistics for display
|
125 |
+
stats_display = f"""
|
126 |
+
### Performance Metrics
|
127 |
+
|
128 |
+
#### Response Times
|
129 |
+
- Average: {stats.get('model_avg_response_time', 0):.2f} seconds
|
130 |
+
|
131 |
+
#### Success Rate
|
132 |
+
- {stats.get('model_success_rate', 0):.1f}%
|
133 |
+
|
134 |
+
#### Problem Types Used
|
135 |
+
"""
|
136 |
+
for ptype, percentage in stats.get('problem_type_distribution', {}).items():
|
137 |
+
stats_display += f"- {ptype}: {percentage:.1f}%\n"
|
138 |
+
|
139 |
+
return output, stats_display
|
140 |
|
141 |
# Create Gradio interface
|
142 |
+
with gr.Blocks(title="Mathematics Problem Solver") as demo:
|
143 |
+
gr.Markdown("# Mathematics Problem Solver")
|
144 |
+
gr.Markdown("Using our fine-tuned model to solve mathematical problems")
|
145 |
|
146 |
with gr.Row():
|
147 |
with gr.Column():
|
148 |
+
problem_type = gr.Dropdown(
|
149 |
+
choices=["Derivative", "Addition", "Roots"],
|
150 |
+
value="Derivative",
|
151 |
+
label="Problem Type"
|
152 |
+
)
|
153 |
+
problem_input = gr.Textbox(
|
154 |
+
label="Enter your problem",
|
155 |
+
placeholder="Example: x^2 + 3x"
|
156 |
)
|
157 |
+
solve_btn = gr.Button("Solve", variant="primary")
|
158 |
|
159 |
with gr.Row():
|
160 |
+
solution_output = gr.Textbox(
|
161 |
label="Solution with Steps",
|
162 |
lines=6
|
163 |
)
|
164 |
|
165 |
+
# Performance metrics display
|
166 |
+
with gr.Row():
|
167 |
+
metrics_display = gr.Markdown("### Performance Metrics\n*Solve a problem to see metrics*")
|
168 |
+
|
169 |
+
# Example problems
|
170 |
gr.Examples(
|
171 |
examples=[
|
172 |
+
["x^2 + 3x", "Derivative"],
|
173 |
+
["235 + 567", "Addition"],
|
174 |
+
["x^2 - 4", "Roots"],
|
175 |
+
["\\sin{\\left(x\\right)}", "Derivative"],
|
176 |
+
["e^x", "Derivative"],
|
177 |
+
["\\frac{1}{x}", "Derivative"]
|
178 |
],
|
179 |
+
inputs=[problem_input, problem_type],
|
180 |
+
outputs=[solution_output, metrics_display],
|
181 |
+
fn=solve_problem,
|
182 |
cache_examples=False # Disable caching
|
183 |
)
|
184 |
|
185 |
# Connect the interface
|
186 |
solve_btn.click(
|
187 |
+
fn=solve_problem,
|
188 |
+
inputs=[problem_input, problem_type],
|
189 |
+
outputs=[solution_output, metrics_display]
|
190 |
)
|
191 |
|
192 |
if __name__ == "__main__":
|
monitoring.py
ADDED
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import time
|
2 |
+
from functools import wraps
|
3 |
+
from collections import defaultdict
|
4 |
+
import numpy as np
|
5 |
+
|
6 |
+
class PerformanceMonitor:
|
7 |
+
def __init__(self):
|
8 |
+
self.response_times = defaultdict(list)
|
9 |
+
self.success_counts = defaultdict(int)
|
10 |
+
self.total_counts = defaultdict(int)
|
11 |
+
self.problem_types = defaultdict(int)
|
12 |
+
self.total_problems = 0
|
13 |
+
|
14 |
+
def record_response_time(self, model_type: str, time: float):
|
15 |
+
"""Record response time for a model"""
|
16 |
+
self.response_times[model_type].append(time)
|
17 |
+
|
18 |
+
def record_success(self, model_type: str, success: bool):
|
19 |
+
"""Record success/failure for a model"""
|
20 |
+
self.total_counts[model_type] += 1
|
21 |
+
if success:
|
22 |
+
self.success_counts[model_type] += 1
|
23 |
+
|
24 |
+
def record_problem_type(self, problem_type: str):
|
25 |
+
"""Record problem type"""
|
26 |
+
self.problem_types[problem_type] += 1
|
27 |
+
self.total_problems += 1
|
28 |
+
|
29 |
+
def get_statistics(self) -> dict:
|
30 |
+
"""Get current performance statistics"""
|
31 |
+
stats = {}
|
32 |
+
|
33 |
+
# Calculate average response times
|
34 |
+
for model_type, times in self.response_times.items():
|
35 |
+
if times:
|
36 |
+
stats[f'{model_type}_avg_response_time'] = np.mean(times)
|
37 |
+
|
38 |
+
# Calculate success rates
|
39 |
+
for model_type in self.total_counts.keys():
|
40 |
+
total = self.total_counts[model_type]
|
41 |
+
if total > 0:
|
42 |
+
success_rate = (self.success_counts[model_type] / total) * 100
|
43 |
+
stats[f'{model_type}_success_rate'] = success_rate
|
44 |
+
|
45 |
+
# Calculate problem type distribution
|
46 |
+
if self.total_problems > 0:
|
47 |
+
distribution = {
|
48 |
+
ptype: (count / self.total_problems) * 100
|
49 |
+
for ptype, count in self.problem_types.items()
|
50 |
+
}
|
51 |
+
stats['problem_type_distribution'] = distribution
|
52 |
+
|
53 |
+
return stats
|
54 |
+
|
55 |
+
def measure_time(func):
|
56 |
+
"""Decorator to measure function execution time"""
|
57 |
+
@wraps(func)
|
58 |
+
def wrapper(*args, **kwargs):
|
59 |
+
start_time = time.time()
|
60 |
+
result = func(*args, **kwargs)
|
61 |
+
end_time = time.time()
|
62 |
+
return result, end_time - start_time
|
63 |
+
return wrapper
|