mcamargo00 commited on
Commit
d49524c
·
verified ·
1 Parent(s): 568059e

Upload 5 files

Browse files
Files changed (5) hide show
  1. README.md +8 -13
  2. app.py +148 -0
  3. deployment_files.py +49 -0
  4. requirements.txt +6 -0
  5. static/index.html +314 -0
README.md CHANGED
@@ -1,13 +1,8 @@
1
- ---
2
- title: Math Solution Classifier
3
- emoji: 🔥
4
- colorFrom: blue
5
- colorTo: blue
6
- sdk: gradio
7
- sdk_version: 5.39.0
8
- app_file: app.py
9
- pinned: false
10
- short_description: Model for classifying solutions to math problems
11
- ---
12
-
13
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
+ title: Math Solution Classifier
2
+ emoji: 🧮
3
+ colorFrom: blue
4
+ colorTo: purple
5
+ sdk: gradio
6
+ sdk_version: 4.7.1
7
+ app_file: app.py
8
+ pinned: false
 
 
 
 
 
app.py ADDED
@@ -0,0 +1,148 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # app.py - FastAPI backend for Math Solution Classifier
2
+
3
+ from fastapi import FastAPI, HTTPException
4
+ from fastapi.middleware.cors import CORSMiddleware
5
+ from fastapi.staticfiles import StaticFiles
6
+ from fastapi.responses import FileResponse
7
+ from pydantic import BaseModel
8
+ import torch
9
+ from transformers import AutoTokenizer, AutoModelForSequenceClassification
10
+ import logging
11
+
12
+ # Set up logging
13
+ logging.basicConfig(level=logging.INFO)
14
+ logger = logging.getLogger(__name__)
15
+
16
+ app = FastAPI(title="Math Solution Classifier API")
17
+
18
+ # Add CORS middleware
19
+ app.add_middleware(
20
+ CORSMiddleware,
21
+ allow_origins=["*"],
22
+ allow_credentials=True,
23
+ allow_methods=["*"],
24
+ allow_headers=["*"],
25
+ )
26
+
27
+ # Global variables for model and tokenizer
28
+ model = None
29
+ tokenizer = None
30
+ label_mapping = {0: "correct", 1: "conceptually-flawed", 2: "computationally-flawed"}
31
+
32
+ class ClassificationRequest(BaseModel):
33
+ question: str
34
+ solution: str
35
+
36
+ class ClassificationResponse(BaseModel):
37
+ classification: str
38
+ confidence: float
39
+
40
+ def load_model():
41
+ """Load your trained model here"""
42
+ global model, tokenizer
43
+
44
+ try:
45
+ # Replace these with your actual model path/name
46
+ # Option 1: Load from local files
47
+ # model = AutoModelForSequenceClassification.from_pretrained("./your_model_directory")
48
+ # tokenizer = AutoTokenizer.from_pretrained("./your_model_directory")
49
+
50
+ # Option 2: Load from Hugging Face Hub (if you upload your model there)
51
+ # model = AutoModelForSequenceClassification.from_pretrained("your-username/your-model-name")
52
+ # tokenizer = AutoTokenizer.from_pretrained("your-username/your-model-name")
53
+
54
+ # For now, we'll use a placeholder - replace this with your actual model loading
55
+ logger.warning("Using placeholder model loading - replace with your actual model!")
56
+
57
+ # Placeholder model loading (replace this!)
58
+ model_name = "distilbert-base-uncased" # Replace with your model
59
+ tokenizer = AutoTokenizer.from_pretrained(model_name)
60
+ model = AutoModelForSequenceClassification.from_pretrained(
61
+ model_name,
62
+ num_labels=3,
63
+ ignore_mismatched_sizes=True
64
+ )
65
+
66
+ logger.info("Model loaded successfully")
67
+
68
+ except Exception as e:
69
+ logger.error(f"Error loading model: {e}")
70
+ raise
71
+
72
+ def classify_solution(question: str, solution: str) -> tuple:
73
+ """
74
+ Classify the math solution
75
+ Returns: (classification_label, confidence_score)
76
+ """
77
+ try:
78
+ # Combine question and solution for input
79
+ text_input = f"Question: {question}\nSolution: {solution}"
80
+
81
+ # Tokenize input
82
+ inputs = tokenizer(
83
+ text_input,
84
+ return_tensors="pt",
85
+ truncation=True,
86
+ padding=True,
87
+ max_length=512
88
+ )
89
+
90
+ # Get model prediction
91
+ with torch.no_grad():
92
+ outputs = model(**inputs)
93
+ predictions = torch.nn.functional.softmax(outputs.logits, dim=-1)
94
+ predicted_class = torch.argmax(predictions, dim=-1).item()
95
+ confidence = predictions[0][predicted_class].item()
96
+
97
+ classification = label_mapping[predicted_class]
98
+
99
+ return classification, confidence
100
+
101
+ except Exception as e:
102
+ logger.error(f"Error during classification: {e}")
103
+ raise HTTPException(status_code=500, detail=f"Classification error: {str(e)}")
104
+
105
+ @app.on_event("startup")
106
+ async def startup_event():
107
+ """Load model on startup"""
108
+ logger.info("Loading model...")
109
+ load_model()
110
+
111
+ @app.post("/classify", response_model=ClassificationResponse)
112
+ async def classify_math_solution(request: ClassificationRequest):
113
+ """
114
+ Classify a math solution as correct, conceptually flawed, or computationally flawed
115
+ """
116
+ if not model or not tokenizer:
117
+ raise HTTPException(status_code=503, detail="Model not loaded")
118
+
119
+ if not request.question.strip() or not request.solution.strip():
120
+ raise HTTPException(status_code=400, detail="Both question and solution are required")
121
+
122
+ try:
123
+ classification, confidence = classify_solution(request.question, request.solution)
124
+
125
+ return ClassificationResponse(
126
+ classification=classification,
127
+ confidence=confidence
128
+ )
129
+
130
+ except Exception as e:
131
+ logger.error(f"Classification failed: {e}")
132
+ raise HTTPException(status_code=500, detail="Classification failed")
133
+
134
+ @app.get("/health")
135
+ async def health_check():
136
+ """Health check endpoint"""
137
+ return {"status": "healthy", "model_loaded": model is not None}
138
+
139
+ # Serve the frontend (for Hugging Face Spaces)
140
+ app.mount("/static", StaticFiles(directory="static"), name="static")
141
+
142
+ @app.get("/")
143
+ async def serve_frontend():
144
+ return FileResponse("static/index.html")
145
+
146
+ if __name__ == "__main__":
147
+ import uvicorn
148
+ uvicorn.run(app, host="0.0.0.0", port=7860) # Port 7860 is standard for HF Spaces
deployment_files.py ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # requirements.txt
2
+ fastapi==0.104.1
3
+ uvicorn==0.24.0
4
+ torch==2.1.0
5
+ transformers==4.35.0
6
+ python-multipart==0.0.6
7
+ pydantic==2.5.0
8
+
9
+ # README.md for your Hugging Face Space
10
+ ---
11
+ title: Math Solution Classifier
12
+ emoji: 🧮
13
+ colorFrom: blue
14
+ colorTo: purple
15
+ sdk: gradio
16
+ sdk_version: 4.7.1
17
+ app_file: app.py
18
+ pinned: false
19
+ ---
20
+
21
+ # Math Solution Classifier
22
+
23
+ This application classifies math solutions into three categories:
24
+ - **Correct**: Solution is mathematically sound
25
+ - **Conceptually Flawed**: Wrong approach or understanding
26
+ - **Computationally Flawed**: Right approach, calculation errors
27
+
28
+ ## Usage
29
+
30
+ 1. Enter a math question
31
+ 2. Enter the proposed solution
32
+ 3. Click "Classify Solution"
33
+ 4. Get instant feedback on the solution quality
34
+
35
+ Built with FastAPI and your custom trained model.
36
+
37
+ # Dockerfile (optional, for other hosting platforms)
38
+ FROM python:3.9-slim
39
+
40
+ WORKDIR /app
41
+
42
+ COPY requirements.txt .
43
+ RUN pip install --no-cache-dir -r requirements.txt
44
+
45
+ COPY . .
46
+
47
+ EXPOSE 7860
48
+
49
+ CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ fastapi==0.104.1
2
+ uvicorn==0.24.0
3
+ torch==2.1.0
4
+ transformers==4.35.0
5
+ python-multipart==0.0.6
6
+ pydantic==2.5.0
static/index.html ADDED
@@ -0,0 +1,314 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Math Solution Classifier</title>
7
+ <style>
8
+ * {
9
+ box-sizing: border-box;
10
+ margin: 0;
11
+ padding: 0;
12
+ }
13
+
14
+ body {
15
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
16
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
17
+ min-height: 100vh;
18
+ padding: 20px;
19
+ }
20
+
21
+ .container {
22
+ max-width: 800px;
23
+ margin: 0 auto;
24
+ background: rgba(255, 255, 255, 0.95);
25
+ border-radius: 20px;
26
+ padding: 40px;
27
+ box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
28
+ backdrop-filter: blur(10px);
29
+ }
30
+
31
+ h1 {
32
+ text-align: center;
33
+ color: #333;
34
+ margin-bottom: 30px;
35
+ font-size: 2.5em;
36
+ background: linear-gradient(135deg, #667eea, #764ba2);
37
+ -webkit-background-clip: text;
38
+ -webkit-text-fill-color: transparent;
39
+ background-clip: text;
40
+ }
41
+
42
+ .form-group {
43
+ margin-bottom: 25px;
44
+ }
45
+
46
+ label {
47
+ display: block;
48
+ margin-bottom: 8px;
49
+ font-weight: 600;
50
+ color: #333;
51
+ font-size: 1.1em;
52
+ }
53
+
54
+ textarea {
55
+ width: 100%;
56
+ min-height: 120px;
57
+ padding: 15px;
58
+ border: 2px solid #e1e5e9;
59
+ border-radius: 12px;
60
+ font-size: 16px;
61
+ font-family: 'Courier New', monospace;
62
+ background: #fafbfc;
63
+ transition: all 0.3s ease;
64
+ resize: vertical;
65
+ }
66
+
67
+ textarea:focus {
68
+ outline: none;
69
+ border-color: #667eea;
70
+ background: white;
71
+ box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
72
+ }
73
+
74
+ .classify-btn {
75
+ width: 100%;
76
+ padding: 18px;
77
+ background: linear-gradient(135deg, #667eea, #764ba2);
78
+ color: white;
79
+ border: none;
80
+ border-radius: 12px;
81
+ font-size: 18px;
82
+ font-weight: 600;
83
+ cursor: pointer;
84
+ transition: all 0.3s ease;
85
+ text-transform: uppercase;
86
+ letter-spacing: 1px;
87
+ }
88
+
89
+ .classify-btn:hover:not(:disabled) {
90
+ transform: translateY(-2px);
91
+ box-shadow: 0 10px 25px rgba(102, 126, 234, 0.3);
92
+ }
93
+
94
+ .classify-btn:disabled {
95
+ background: #ccc;
96
+ cursor: not-allowed;
97
+ transform: none;
98
+ }
99
+
100
+ .result {
101
+ margin-top: 30px;
102
+ padding: 25px;
103
+ border-radius: 12px;
104
+ text-align: center;
105
+ font-size: 18px;
106
+ font-weight: 600;
107
+ opacity: 0;
108
+ transform: translateY(20px);
109
+ transition: all 0.5s ease;
110
+ }
111
+
112
+ .result.show {
113
+ opacity: 1;
114
+ transform: translateY(0);
115
+ }
116
+
117
+ .result.correct {
118
+ background: linear-gradient(135deg, #4facfe, #00f2fe);
119
+ color: white;
120
+ border: 3px solid #4facfe;
121
+ }
122
+
123
+ .result.conceptually-flawed {
124
+ background: linear-gradient(135deg, #fa709a, #fee140);
125
+ color: white;
126
+ border: 3px solid #fa709a;
127
+ }
128
+
129
+ .result.computationally-flawed {
130
+ background: linear-gradient(135deg, #ff6b6b, #ffa500);
131
+ color: white;
132
+ border: 3px solid #ff6b6b;
133
+ }
134
+
135
+ .result.error {
136
+ background: #f8f9fa;
137
+ color: #dc3545;
138
+ border: 3px solid #dc3545;
139
+ }
140
+
141
+ .loading {
142
+ display: inline-block;
143
+ width: 20px;
144
+ height: 20px;
145
+ border: 3px solid rgba(255, 255, 255, 0.3);
146
+ border-radius: 50%;
147
+ border-top-color: white;
148
+ animation: spin 1s ease-in-out infinite;
149
+ margin-right: 10px;
150
+ }
151
+
152
+ @keyframes spin {
153
+ to { transform: rotate(360deg); }
154
+ }
155
+
156
+ .example {
157
+ background: #f8f9fa;
158
+ border-left: 4px solid #667eea;
159
+ padding: 15px;
160
+ margin: 20px 0;
161
+ border-radius: 0 8px 8px 0;
162
+ }
163
+
164
+ .example h3 {
165
+ color: #667eea;
166
+ margin-bottom: 10px;
167
+ }
168
+
169
+ .example-text {
170
+ font-family: 'Courier New', monospace;
171
+ font-size: 14px;
172
+ color: #555;
173
+ }
174
+ </style>
175
+ </head>
176
+ <body>
177
+ <div class="container">
178
+ <h1>🧮 Math Solution Classifier</h1>
179
+
180
+ <div class="example">
181
+ <h3>How to use:</h3>
182
+ <p>Enter a math question and a solution attempt. The AI will classify the solution as:</p>
183
+ <ul style="margin: 10px 0 0 20px;">
184
+ <li><strong>Correct:</strong> Solution is mathematically sound</li>
185
+ <li><strong>Conceptually Flawed:</strong> Wrong approach or understanding</li>
186
+ <li><strong>Computationally Flawed:</strong> Right approach, calculation errors</li>
187
+ </ul>
188
+ </div>
189
+
190
+ <form id="classifyForm">
191
+ <div class="form-group">
192
+ <label for="question">Math Question:</label>
193
+ <textarea
194
+ id="question"
195
+ name="question"
196
+ placeholder="e.g., Solve for x: 2x + 5 = 13"
197
+ required
198
+ ></textarea>
199
+ </div>
200
+
201
+ <div class="form-group">
202
+ <label for="solution">Proposed Solution:</label>
203
+ <textarea
204
+ id="solution"
205
+ name="solution"
206
+ placeholder="e.g., 2x + 5 = 13&#10;2x = 13 - 5&#10;2x = 8&#10;x = 4"
207
+ required
208
+ ></textarea>
209
+ </div>
210
+
211
+ <button type="submit" class="classify-btn" id="submitBtn">
212
+ Classify Solution
213
+ </button>
214
+ </form>
215
+
216
+ <div id="result" class="result"></div>
217
+ </div>
218
+
219
+ <script>
220
+ const form = document.getElementById('classifyForm');
221
+ const submitBtn = document.getElementById('submitBtn');
222
+ const resultDiv = document.getElementById('result');
223
+
224
+ // Replace this URL with your actual API endpoint
225
+ const API_URL = 'https://your-huggingface-space.hf.space/classify';
226
+
227
+ form.addEventListener('submit', async (e) => {
228
+ e.preventDefault();
229
+
230
+ const question = document.getElementById('question').value.trim();
231
+ const solution = document.getElementById('solution').value.trim();
232
+
233
+ if (!question || !solution) {
234
+ showResult('Please fill in both fields', 'error');
235
+ return;
236
+ }
237
+
238
+ // Show loading state
239
+ submitBtn.disabled = true;
240
+ submitBtn.innerHTML = '<div class="loading"></div>Classifying...';
241
+ resultDiv.className = 'result';
242
+ resultDiv.textContent = '';
243
+
244
+ try {
245
+ const response = await fetch(API_URL, {
246
+ method: 'POST',
247
+ headers: {
248
+ 'Content-Type': 'application/json',
249
+ },
250
+ body: JSON.stringify({
251
+ question: question,
252
+ solution: solution
253
+ })
254
+ });
255
+
256
+ if (!response.ok) {
257
+ throw new Error(`HTTP error! status: ${response.status}`);
258
+ }
259
+
260
+ const data = await response.json();
261
+
262
+ // Display result
263
+ showResult(formatResult(data.classification), data.classification);
264
+
265
+ } catch (error) {
266
+ console.error('Error:', error);
267
+ showResult('Error connecting to the classification service. Please try again.', 'error');
268
+ } finally {
269
+ // Reset button
270
+ submitBtn.disabled = false;
271
+ submitBtn.innerHTML = 'Classify Solution';
272
+ }
273
+ });
274
+
275
+ function formatResult(classification) {
276
+ const messages = {
277
+ 'correct': '✅ Correct Solution!\nThe mathematical approach and calculations are both sound.',
278
+ 'conceptually-flawed': '🤔 Conceptually Flawed\nThe approach or understanding has fundamental issues.',
279
+ 'computationally-flawed': '🔢 Computationally Flawed\nThe approach is correct, but there are calculation errors.'
280
+ };
281
+
282
+ return messages[classification] || `Classification: ${classification}`;
283
+ }
284
+
285
+ function showResult(message, type) {
286
+ resultDiv.textContent = message;
287
+ resultDiv.className = `result ${type} show`;
288
+ }
289
+
290
+ // For demo purposes - remove this when you have a real API
291
+ if (API_URL.includes('your-huggingface-space')) {
292
+ // Mock API for testing
293
+ form.addEventListener('submit', async (e) => {
294
+ e.preventDefault();
295
+
296
+ submitBtn.disabled = true;
297
+ submitBtn.innerHTML = '<div class="loading"></div>Classifying...';
298
+
299
+ // Simulate API delay
300
+ await new Promise(resolve => setTimeout(resolve, 2000));
301
+
302
+ // Mock classification (randomly choose one for demo)
303
+ const classifications = ['correct', 'conceptually-flawed', 'computationally-flawed'];
304
+ const randomClassification = classifications[Math.floor(Math.random() * classifications.length)];
305
+
306
+ showResult(formatResult(randomClassification), randomClassification);
307
+
308
+ submitBtn.disabled = false;
309
+ submitBtn.innerHTML = 'Classify Solution';
310
+ });
311
+ }
312
+ </script>
313
+ </body>
314
+ </html>