FaroukTomori commited on
Commit
01db2f8
ยท
verified ยท
1 Parent(s): 7c21b97

Update src/fine.py

Browse files
Files changed (1) hide show
  1. src/fine.py +971 -949
src/fine.py CHANGED
@@ -1,949 +1,971 @@
1
- """
2
- Generative AI for Enhancing Programming Education
3
- ================================================
4
-
5
- This project implements a fine-tuned CodeLlama-7B model to provide structured,
6
- educational code feedback for programming students.
7
-
8
- Problem Statement:
9
- - High dropout rates in programming education
10
- - Inefficient feedback loops
11
- - Lack of personalized learning
12
- - Limited instructor bandwidth
13
- - Current AI tools prioritize productivity over learning
14
-
15
- Solution:
16
- - Fine-tuned CodeLlama-7B for educational feedback
17
- - Structured, actionable code reviews
18
- - Beginner-friendly explanations
19
- - Personalized adaptation based on skill level
20
- - Educational focus with ethical safeguards
21
-
22
- Author: [Your Name]
23
- Date: [Current Date]
24
- """
25
-
26
- import re
27
- from dataclasses import dataclass
28
- from typing import Dict, List, Optional, Tuple
29
- import logging
30
- import json
31
- from transformers import AutoTokenizer, AutoModelForCausalLM
32
- import os
33
- import gc
34
- import torch
35
- import warnings
36
- warnings.filterwarnings("ignore", category=UserWarning)
37
-
38
- # --- Critical Environment Setup (Must be before imports) ---
39
- os.environ["TOKENIZERS_PARALLELISM"] = "false"
40
- os.environ["DATASETS_DISABLE_MULTIPROCESSING"] = "1"
41
-
42
- # Clear any existing CUDA cache (only if CUDA is available)
43
- if torch.cuda.is_available():
44
- os.environ["PYTORCH_CUDA_ALLOC_CONF"] = "max_split_size_mb:128,garbage_collection_threshold:0.6"
45
- torch.cuda.empty_cache()
46
- gc.collect()
47
-
48
-
49
- # Configure logging
50
- logging.basicConfig(level=logging.INFO)
51
- logger = logging.getLogger(__name__)
52
-
53
-
54
- def clear_cuda_cache():
55
- """Clear CUDA cache and run garbage collection"""
56
- if torch.cuda.is_available():
57
- torch.cuda.empty_cache()
58
- torch.cuda.synchronize()
59
- gc.collect()
60
-
61
-
62
- def get_system_memory():
63
- """Get system memory information"""
64
- try:
65
- import psutil
66
- memory = psutil.virtual_memory()
67
- print(
68
- f"System RAM: {memory.used / (1024**3):.1f}GB / {memory.total / (1024**3):.1f}GB used ({memory.percent:.1f}%)")
69
- except Exception as e:
70
- print(f"Could not get system memory info: {e}")
71
-
72
-
73
- def get_gpu_memory():
74
- """Get GPU memory information (if available)"""
75
- if torch.cuda.is_available():
76
- try:
77
- import subprocess
78
- result = subprocess.run(['nvidia-smi', '--query-gpu=memory.used,memory.total', '--format=csv,nounits,noheader'],
79
- capture_output=True, text=True)
80
- lines = result.stdout.strip().split('\n')
81
- for i, line in enumerate(lines):
82
- used, total = map(int, line.split(', '))
83
- print(
84
- f"GPU {i}: {used}MB / {total}MB used ({used/total*100:.1f}%)")
85
- except Exception as e:
86
- print(f"Could not get GPU memory info: {e}")
87
- else:
88
- print("No GPU available - using CPU only")
89
-
90
-
91
- @dataclass
92
- class CodeFeedback:
93
- """Data structure for storing code feedback"""
94
- code_snippet: str
95
- feedback_type: str # 'syntax', 'logic', 'optimization', 'style', 'explanation'
96
- feedback_message: str
97
- suggested_improvement: Optional[str] = None
98
- difficulty_level: str = 'beginner' # 'beginner', 'intermediate', 'advanced'
99
- learning_objectives: List[str] = None
100
-
101
-
102
- @dataclass
103
- class ComprehensiveFeedback:
104
- """Comprehensive feedback structure with all educational components"""
105
- code_snippet: str
106
- student_level: str
107
-
108
- # Analysis
109
- strengths: List[str]
110
- weaknesses: List[str]
111
- issues: List[str]
112
-
113
- # Educational content
114
- step_by_step_improvement: List[str]
115
- learning_points: List[str]
116
- review_summary: str
117
-
118
- # Interactive elements
119
- comprehension_question: str
120
- comprehension_answer: str
121
- explanation: str
122
-
123
- # Code fixes
124
- improved_code: str
125
- fix_explanation: str
126
-
127
- # Metadata
128
- difficulty_level: str
129
- learning_objectives: List[str]
130
- estimated_time_to_improve: str
131
-
132
-
133
- class ProgrammingEducationAI:
134
- """
135
- Main class for the fine-tuned CodeLlama model for programming education
136
- """
137
-
138
- def __init__(self, model_path: str = "TomoriFarouk/codellama-7b-programming-education"):
139
- """
140
- Initialize the fine-tuned model and tokenizer
141
-
142
- Args:
143
- model_path: Path to your fine-tuned CodeLlama-7B model
144
- """
145
- self.model_path = model_path
146
- self.tokenizer = None
147
- self.model = None
148
- self.feedback_templates = self._load_feedback_templates()
149
- self.code_review_prompt_template = self._load_code_review_prompt()
150
- self.code_feedback_prompt_template = self._load_code_feedback_prompt()
151
- self.comprehensive_feedback_prompt = self._load_comprehensive_feedback_prompt()
152
- self.comprehension_question_prompt = self._load_comprehension_question_prompt()
153
- self.code_fix_prompt = self._load_code_fix_prompt()
154
-
155
- def _load_code_review_prompt(self) -> str:
156
- """Load the code review prompt template used during fine-tuning"""
157
- return """You are an expert programming tutor. Review the following student code and provide educational feedback.
158
-
159
- Student Code:
160
- {code}
161
-
162
- Student Level: {level}
163
-
164
- Please provide:
165
- 1. Syntax errors (if any)
166
- 2. Logic errors (if any)
167
- 3. Style improvements
168
- 4. Optimization suggestions
169
- 5. Educational explanations
170
-
171
- Feedback:"""
172
-
173
- def _load_code_feedback_prompt(self) -> str:
174
- """Load the code feedback prompt template used during fine-tuning"""
175
- return """You are a helpful programming tutor. The student has written this code:
176
-
177
- {code}
178
-
179
- Student Level: {level}
180
-
181
- Provide constructive, educational feedback that helps the student learn. Focus on:
182
- - What they did well
183
- - What can be improved
184
- - Why the improvement matters
185
- - How to implement the improvement
186
-
187
- Feedback:"""
188
-
189
- def _load_feedback_templates(self) -> Dict[str, str]:
190
- """Load predefined feedback templates for different scenarios"""
191
- return {
192
- "syntax_error": "I notice there's a syntax issue in your code. {error_description}. "
193
- "Here's what's happening: {explanation}. "
194
- "Try this correction: {suggestion}",
195
-
196
- "logic_error": "Your code has a logical issue. {problem_description}. "
197
- "The problem is: {explanation}. "
198
- "Consider this approach: {suggestion}",
199
-
200
- "optimization": "Your code works, but we can make it more efficient! "
201
- "Current complexity: {current_complexity}. "
202
- "Optimized version: {optimized_complexity}. "
203
- "Here's how: {explanation}",
204
-
205
- "style_improvement": "Great work! Here's a style tip: {tip}. "
206
- "This makes your code more readable and maintainable.",
207
-
208
- "concept_explanation": "Let me explain this concept: {concept}. "
209
- "In simple terms: {simple_explanation}. "
210
- "Example: {example}"
211
- }
212
-
213
- def load_model(self):
214
- """
215
- Load the fine-tuned model and tokenizer optimized for HF Spaces
216
- """
217
- try:
218
- # Get HF token for private model access
219
- hf_token = os.getenv("HF_TOKEN", None)
220
-
221
- logger.info("Loading tokenizer...")
222
- self.tokenizer = AutoTokenizer.from_pretrained(
223
- self.model_path,
224
- trust_remote_code=True,
225
- token=hf_token # Use token for private models
226
- )
227
-
228
- # Set padding token
229
- if self.tokenizer.pad_token is None:
230
- self.tokenizer.pad_token = self.tokenizer.eos_token
231
- self.tokenizer.pad_token_id = self.tokenizer.eos_token_id
232
-
233
- logger.info(
234
- f"Tokenizer loaded - Vocab size: {len(self.tokenizer)}")
235
-
236
- # Load model optimized for HF Spaces (16GB RAM, 2 vCPU)
237
- print("Loading model optimized for HF Spaces (16GB RAM, 2 vCPU)...")
238
- self.model = AutoModelForCausalLM.from_pretrained(
239
- self.model_path,
240
- torch_dtype=torch.float32,
241
- device_map=None, # Force CPU for HF Spaces
242
- low_cpu_mem_usage=True,
243
- trust_remote_code=True,
244
- offload_folder="offload", # Offload to disk if needed
245
- token=hf_token # Use token for private models
246
- )
247
- # Enable gradient checkpointing for memory savings
248
- self.model.gradient_checkpointing_enable()
249
-
250
- logger.info("Fine-tuned model loaded successfully")
251
- logger.info(f"Model loaded on devices: {self.model.hf_device_map}")
252
-
253
- except Exception as e:
254
- logger.error(f"Error loading fine-tuned model: {e}")
255
- raise
256
-
257
- def generate_code_review(self, code: str, student_level: str = "beginner") -> str:
258
- """
259
- Generate code review using the fine-tuned model
260
-
261
- Args:
262
- code: Student's code to review
263
- student_level: Student's skill level
264
-
265
- Returns:
266
- Generated code review feedback
267
- """
268
- if not self.model or not self.tokenizer:
269
- raise ValueError("Model not loaded. Call load_model() first.")
270
-
271
- # Format the prompt using the template from fine-tuning
272
- prompt = self.code_review_prompt_template.format(
273
- code=code,
274
- level=student_level
275
- )
276
-
277
- # Tokenize input
278
- inputs = self.tokenizer(
279
- prompt, return_tensors="pt", truncation=True, max_length=2048)
280
-
281
- # Generate response
282
- with torch.no_grad():
283
- outputs = self.model.generate(
284
- inputs.input_ids,
285
- max_new_tokens=512,
286
- temperature=0.7,
287
- do_sample=True,
288
- pad_token_id=self.tokenizer.eos_token_id
289
- )
290
-
291
- # Decode response
292
- response = self.tokenizer.decode(outputs[0], skip_special_tokens=True)
293
-
294
- # Extract only the generated part (after the prompt)
295
- generated_text = response[len(prompt):].strip()
296
-
297
- return generated_text
298
-
299
- def generate_educational_feedback(self, code: str, student_level: str = "beginner") -> str:
300
- """
301
- Generate educational feedback using the fine-tuned model
302
-
303
- Args:
304
- code: Student's code to provide feedback on
305
- student_level: Student's skill level
306
-
307
- Returns:
308
- Generated educational feedback
309
- """
310
- if not self.model or not self.tokenizer:
311
- raise ValueError("Model not loaded. Call load_model() first.")
312
-
313
- # Format the prompt using the template from fine-tuning
314
- prompt = self.code_feedback_prompt_template.format(
315
- code=code,
316
- level=student_level
317
- )
318
-
319
- # Tokenize input
320
- inputs = self.tokenizer(
321
- prompt, return_tensors="pt", truncation=True, max_length=2048)
322
-
323
- # Generate response
324
- with torch.no_grad():
325
- outputs = self.model.generate(
326
- inputs.input_ids,
327
- max_new_tokens=512,
328
- temperature=0.7,
329
- do_sample=True,
330
- pad_token_id=self.tokenizer.eos_token_id
331
- )
332
-
333
- # Decode response
334
- response = self.tokenizer.decode(outputs[0], skip_special_tokens=True)
335
-
336
- # Extract only the generated part (after the prompt)
337
- generated_text = response[len(prompt):].strip()
338
-
339
- return generated_text
340
-
341
- def analyze_student_code(self, code: str, student_level: str = "beginner") -> List[CodeFeedback]:
342
- """
343
- Analyze student code and provide educational feedback using the fine-tuned model
344
-
345
- Args:
346
- code: The student's code to analyze
347
- student_level: Student's skill level ('beginner', 'intermediate', 'advanced')
348
-
349
- Returns:
350
- List of CodeFeedback objects
351
- """
352
- feedback_list = []
353
-
354
- # Use fine-tuned model for comprehensive code review
355
- try:
356
- code_review = self.generate_code_review(code, student_level)
357
- educational_feedback = self.generate_educational_feedback(
358
- code, student_level)
359
-
360
- # Create structured feedback from model output
361
- feedback_list.append(CodeFeedback(
362
- code_snippet=code,
363
- feedback_type="comprehensive_review",
364
- feedback_message=code_review,
365
- difficulty_level=student_level,
366
- learning_objectives=["code_analysis", "best_practices"]
367
- ))
368
-
369
- feedback_list.append(CodeFeedback(
370
- code_snippet=code,
371
- feedback_type="educational_guidance",
372
- feedback_message=educational_feedback,
373
- difficulty_level=student_level,
374
- learning_objectives=["learning", "improvement"]
375
- ))
376
-
377
- except Exception as e:
378
- logger.warning(
379
- f"Fine-tuned model failed, falling back to rule-based analysis: {e}")
380
- # Fallback to rule-based analysis if model fails
381
- feedback_list = self._fallback_analysis(code, student_level)
382
-
383
- return feedback_list
384
-
385
- def _fallback_analysis(self, code: str, student_level: str) -> List[CodeFeedback]:
386
- """Fallback analysis using rule-based methods if fine-tuned model fails"""
387
- feedback_list = []
388
-
389
- # Analyze syntax
390
- syntax_feedback = self._check_syntax(code, student_level)
391
- if syntax_feedback:
392
- feedback_list.append(syntax_feedback)
393
-
394
- # Analyze logic and structure
395
- logic_feedback = self._check_logic(code, student_level)
396
- if logic_feedback:
397
- feedback_list.extend(logic_feedback)
398
-
399
- # Check for optimization opportunities
400
- optimization_feedback = self._check_optimization(code, student_level)
401
- if optimization_feedback:
402
- feedback_list.append(optimization_feedback)
403
-
404
- # Provide style suggestions
405
- style_feedback = self._check_style(code, student_level)
406
- if style_feedback:
407
- feedback_list.append(style_feedback)
408
-
409
- return feedback_list
410
-
411
- def _check_syntax(self, code: str, student_level: str) -> Optional[CodeFeedback]:
412
- """Check for syntax errors and provide educational feedback"""
413
- # This would integrate with the fine-tuned model
414
- # For now, using basic pattern matching as placeholder
415
-
416
- common_syntax_errors = {
417
- r"print\s*\([^)]*\)\s*$": "Remember to add a colon after print statements in some contexts",
418
- r"if\s+[^:]+$": "Don't forget the colon after your if condition",
419
- r"for\s+[^:]+$": "Don't forget the colon after your for loop",
420
- }
421
-
422
- for pattern, message in common_syntax_errors.items():
423
- if re.search(pattern, code):
424
- return CodeFeedback(
425
- code_snippet=code,
426
- feedback_type="syntax",
427
- feedback_message=message,
428
- difficulty_level=student_level,
429
- learning_objectives=["syntax", "basic_python"]
430
- )
431
-
432
- return None
433
-
434
- def _check_logic(self, code: str, student_level: str) -> List[CodeFeedback]:
435
- """Check for logical errors and provide educational feedback"""
436
- feedback_list = []
437
-
438
- # Check for infinite loops
439
- if "while True:" in code and "break" not in code:
440
- feedback_list.append(CodeFeedback(
441
- code_snippet=code,
442
- feedback_type="logic",
443
- feedback_message="This while loop will run forever! Make sure to include a break statement or condition to exit the loop.",
444
- difficulty_level=student_level,
445
- learning_objectives=["control_flow", "loops"]
446
- ))
447
-
448
- # Check for unused variables
449
- # This is a simplified check - the actual model would be more sophisticated
450
- if "x = " in code and "x" not in code.replace("x = ", ""):
451
- feedback_list.append(CodeFeedback(
452
- code_snippet=code,
453
- feedback_type="logic",
454
- feedback_message="You created variable 'x' but didn't use it. Consider removing unused variables to keep your code clean.",
455
- difficulty_level=student_level,
456
- learning_objectives=["variables", "code_cleanliness"]
457
- ))
458
-
459
- return feedback_list
460
-
461
- def _check_optimization(self, code: str, student_level: str) -> Optional[CodeFeedback]:
462
- """Check for optimization opportunities"""
463
- # Check for nested loops that could be optimized
464
- if code.count("for") > 1 and code.count("in range") > 1:
465
- return CodeFeedback(
466
- code_snippet=code,
467
- feedback_type="optimization",
468
- feedback_message="You have nested loops here. Consider if you can optimize this to O(n) instead of O(nยฒ).",
469
- suggested_improvement="Use a hashmap or set to reduce complexity",
470
- difficulty_level=student_level,
471
- learning_objectives=["algorithms",
472
- "complexity", "optimization"]
473
- )
474
-
475
- return None
476
-
477
- def _check_style(self, code: str, student_level: str) -> Optional[CodeFeedback]:
478
- """Check for style improvements"""
479
- # Check for meaningful variable names
480
- if "x" in code or "y" in code or "z" in code:
481
- return CodeFeedback(
482
- code_snippet=code,
483
- feedback_type="style",
484
- feedback_message="Consider using more descriptive variable names instead of x, y, z. This makes your code easier to understand.",
485
- difficulty_level=student_level,
486
- learning_objectives=["naming_conventions", "readability"]
487
- )
488
-
489
- return None
490
-
491
- def generate_explanation(self, concept: str, student_level: str) -> str:
492
- """
493
- Generate explanations for programming concepts based on student level
494
-
495
- Args:
496
- concept: The concept to explain
497
- student_level: Student's skill level
498
-
499
- Returns:
500
- Explanation tailored to the student's level
501
- """
502
- explanations = {
503
- "variables": {
504
- "beginner": "Variables are like labeled boxes where you store information. Think of 'name = \"John\"' as putting \"John\" in a box labeled 'name'.",
505
- "intermediate": "Variables are memory locations that store data. They have a name, type, and value. Python is dynamically typed, so the type is inferred.",
506
- "advanced": "Variables in Python are references to objects in memory. They're dynamically typed and use reference counting for memory management."
507
- },
508
- "loops": {
509
- "beginner": "Loops repeat code multiple times. 'for' loops are great when you know how many times to repeat, 'while' loops when you don't.",
510
- "intermediate": "Loops control program flow. 'for' iterates over sequences, 'while' continues until a condition is False. Consider time complexity.",
511
- "advanced": "Loops are fundamental control structures. Python's 'for' is actually a foreach loop. Consider iterator patterns and generator expressions."
512
- }
513
- }
514
-
515
- return explanations.get(concept, {}).get(student_level, f"Explanation for {concept} at {student_level} level")
516
-
517
- def _load_comprehensive_feedback_prompt(self) -> str:
518
- """Load the comprehensive feedback prompt template"""
519
- return """You are an expert programming tutor. Provide comprehensive educational feedback for the following student code.
520
-
521
- Student Code:
522
- {code}
523
-
524
- Student Level: {level}
525
-
526
- Please provide a detailed analysis in the following JSON format:
527
-
528
- {{
529
- "strengths": ["strength1", "strength2", "strength3"],
530
- "weaknesses": ["weakness1", "weakness2", "weakness3"],
531
- "issues": ["issue1", "issue2", "issue3"],
532
- "step_by_step_improvement": [
533
- "Step 1: Description of first improvement",
534
- "Step 2: Description of second improvement",
535
- "Step 3: Description of third improvement"
536
- ],
537
- "learning_points": [
538
- "Learning point 1: What the student should understand",
539
- "Learning point 2: Key concept to grasp",
540
- "Learning point 3: Best practice to follow"
541
- ],
542
- "review_summary": "A comprehensive review of the code highlighting key areas for improvement",
543
- "learning_objectives": ["objective1", "objective2", "objective3"],
544
- "estimated_time_to_improve": "5-10 minutes"
545
- }}
546
-
547
- Focus on educational value and constructive feedback that helps the student learn and improve."""
548
-
549
- def _load_comprehension_question_prompt(self) -> str:
550
- """Load the comprehension question generation prompt"""
551
- return """Based on the learning points and improvements discussed, generate a comprehension question to test the student's understanding.
552
-
553
- Learning Points: {learning_points}
554
- Code Issues: {issues}
555
- Student Level: {level}
556
-
557
- Generate a question that tests understanding of the key concepts discussed. The question should be appropriate for the student's level.
558
-
559
- Format your response as JSON:
560
- {{
561
- "question": "Your comprehension question here",
562
- "answer": "The correct answer",
563
- "explanation": "Detailed explanation of why this answer is correct"
564
- }}
565
-
566
- Make the question challenging but fair for the student's level."""
567
-
568
- def _load_code_fix_prompt(self) -> str:
569
- """Load the code fix generation prompt"""
570
- return """You are an expert programming tutor. Based on the analysis and learning points, provide an improved version of the student's code.
571
-
572
- Original Code:
573
- {code}
574
-
575
- Issues Identified: {issues}
576
- Learning Points: {learning_points}
577
- Student Level: {level}
578
-
579
- Provide an improved version of the code that addresses the issues while maintaining educational value. Include comments to explain the improvements.
580
-
581
- Format your response as JSON:
582
- {{
583
- "improved_code": "The improved code with comments",
584
- "fix_explanation": "Detailed explanation of what was changed and why"
585
- }}
586
-
587
- Focus on educational improvements that help the student understand better practices."""
588
-
589
- def adapt_feedback_complexity(self, feedback: CodeFeedback, student_level: str) -> CodeFeedback:
590
- """
591
- Adapt feedback complexity based on student level
592
-
593
- Args:
594
- feedback: Original feedback
595
- student_level: Student's skill level
596
-
597
- Returns:
598
- Adapted feedback
599
- """
600
- if student_level == "beginner":
601
- # Simplify language and add more examples
602
- feedback.feedback_message = feedback.feedback_message.replace(
603
- "O(nยฒ)", "quadratic time (slower)"
604
- ).replace(
605
- "O(n)", "linear time (faster)"
606
- )
607
- elif student_level == "advanced":
608
- # Add more technical details
609
- if "optimization" in feedback.feedback_type:
610
- feedback.feedback_message += " Consider the space-time tradeoff and cache locality."
611
-
612
- return feedback
613
-
614
- def generate_comprehensive_feedback(self, code: str, student_level: str = "beginner") -> ComprehensiveFeedback:
615
- """
616
- Generate comprehensive educational feedback with all components
617
-
618
- Args:
619
- code: Student's code to analyze
620
- student_level: Student's skill level
621
-
622
- Returns:
623
- ComprehensiveFeedback object with all educational components
624
- """
625
- if not self.model or not self.tokenizer:
626
- raise ValueError("Model not loaded. Call load_model() first.")
627
-
628
- try:
629
- # Step 1: Generate comprehensive analysis
630
- comprehensive_analysis = self._generate_comprehensive_analysis(
631
- code, student_level)
632
-
633
- # Step 2: Generate comprehension question
634
- comprehension_data = self._generate_comprehension_question(
635
- comprehensive_analysis["learning_points"],
636
- comprehensive_analysis["issues"],
637
- student_level
638
- )
639
-
640
- # Step 3: Generate improved code
641
- code_fix_data = self._generate_code_fix(
642
- code,
643
- comprehensive_analysis["issues"],
644
- comprehensive_analysis["learning_points"],
645
- student_level
646
- )
647
-
648
- # Create comprehensive feedback object
649
- return ComprehensiveFeedback(
650
- code_snippet=code,
651
- student_level=student_level,
652
- strengths=comprehensive_analysis["strengths"],
653
- weaknesses=comprehensive_analysis["weaknesses"],
654
- issues=comprehensive_analysis["issues"],
655
- step_by_step_improvement=comprehensive_analysis["step_by_step_improvement"],
656
- learning_points=comprehensive_analysis["learning_points"],
657
- review_summary=comprehensive_analysis["review_summary"],
658
- comprehension_question=comprehension_data["question"],
659
- comprehension_answer=comprehension_data["answer"],
660
- explanation=comprehension_data["explanation"],
661
- improved_code=code_fix_data["improved_code"],
662
- fix_explanation=code_fix_data["fix_explanation"],
663
- difficulty_level=student_level,
664
- learning_objectives=comprehensive_analysis["learning_objectives"],
665
- estimated_time_to_improve=comprehensive_analysis["estimated_time_to_improve"]
666
- )
667
-
668
- except Exception as e:
669
- logger.error(f"Error generating comprehensive feedback: {e}")
670
- # Return a basic comprehensive feedback if model fails
671
- return self._create_fallback_comprehensive_feedback(code, student_level)
672
-
673
- def _generate_comprehensive_analysis(self, code: str, student_level: str) -> Dict:
674
- """Generate comprehensive analysis using the fine-tuned model"""
675
- prompt = self.comprehensive_feedback_prompt.format(
676
- code=code,
677
- level=student_level
678
- )
679
-
680
- response = self._generate_model_response(prompt)
681
-
682
- try:
683
- # Try to parse JSON response
684
- import json
685
- return json.loads(response)
686
- except json.JSONDecodeError:
687
- logger.warning("Failed to parse JSON response, using fallback")
688
- return self._create_fallback_analysis(code, student_level)
689
-
690
- def _generate_comprehension_question(self, learning_points: List[str], issues: List[str], student_level: str) -> Dict:
691
- """Generate comprehension question using the fine-tuned model"""
692
- prompt = self.comprehension_question_prompt.format(
693
- learning_points=", ".join(learning_points),
694
- issues=", ".join(issues),
695
- level=student_level
696
- )
697
-
698
- response = self._generate_model_response(prompt)
699
-
700
- try:
701
- import json
702
- return json.loads(response)
703
- except json.JSONDecodeError:
704
- logger.warning(
705
- "Failed to parse comprehension question JSON, using fallback")
706
- return {
707
- "question": "What is the main concept you learned from this code review?",
708
- "answer": "The main concept is understanding code structure and best practices.",
709
- "explanation": "This question tests your understanding of the key learning points discussed."
710
- }
711
-
712
- def _generate_code_fix(self, code: str, issues: List[str], learning_points: List[str], student_level: str) -> Dict:
713
- """Generate improved code using the fine-tuned model"""
714
- prompt = self.code_fix_prompt.format(
715
- code=code,
716
- issues=", ".join(issues),
717
- learning_points=", ".join(learning_points),
718
- level=student_level
719
- )
720
-
721
- response = self._generate_model_response(prompt)
722
-
723
- try:
724
- import json
725
- return json.loads(response)
726
- except json.JSONDecodeError:
727
- logger.warning("Failed to parse code fix JSON, using fallback")
728
- return {
729
- "improved_code": "# Improved version of your code\n# Add comments and improvements here",
730
- "fix_explanation": "This is a fallback improved version. The model should provide specific improvements."
731
- }
732
-
733
- def _generate_model_response(self, prompt: str) -> str:
734
- """Generate response from the fine-tuned model"""
735
- inputs = self.tokenizer(
736
- prompt, return_tensors="pt", truncation=True, max_length=2048)
737
-
738
- # Move to CPU if no GPU available
739
- if not torch.cuda.is_available():
740
- inputs = {k: v.cpu() for k, v in inputs.items()}
741
-
742
- with torch.no_grad():
743
- outputs = self.model.generate(
744
- inputs.input_ids,
745
- max_new_tokens=512,
746
- temperature=0.7,
747
- do_sample=True,
748
- pad_token_id=self.tokenizer.eos_token_id
749
- )
750
-
751
- response = self.tokenizer.decode(outputs[0], skip_special_tokens=True)
752
- return response[len(prompt):].strip()
753
-
754
- def _create_fallback_analysis(self, code: str, student_level: str) -> Dict:
755
- """Create fallback analysis when model fails"""
756
- return {
757
- "strengths": ["Your code has a clear structure", "You're using appropriate data types"],
758
- "weaknesses": ["Could improve variable naming", "Consider adding comments"],
759
- "issues": ["Basic syntax and style issues"],
760
- "step_by_step_improvement": [
761
- "Step 1: Add descriptive variable names",
762
- "Step 2: Include comments explaining your logic",
763
- "Step 3: Consider code optimization"
764
- ],
765
- "learning_points": [
766
- "Good variable naming improves code readability",
767
- "Comments help others understand your code",
768
- "Always consider efficiency in your solutions"
769
- ],
770
- "review_summary": "Your code works but could be improved with better practices.",
771
- "learning_objectives": ["code_quality", "best_practices", "readability"],
772
- "estimated_time_to_improve": "10-15 minutes"
773
- }
774
-
775
- def _create_fallback_comprehensive_feedback(self, code: str, student_level: str) -> ComprehensiveFeedback:
776
- """Create fallback comprehensive feedback when model fails"""
777
- fallback_analysis = self._create_fallback_analysis(code, student_level)
778
-
779
- return ComprehensiveFeedback(
780
- code_snippet=code,
781
- student_level=student_level,
782
- strengths=fallback_analysis["strengths"],
783
- weaknesses=fallback_analysis["weaknesses"],
784
- issues=fallback_analysis["issues"],
785
- step_by_step_improvement=fallback_analysis["step_by_step_improvement"],
786
- learning_points=fallback_analysis["learning_points"],
787
- review_summary=fallback_analysis["review_summary"],
788
- comprehension_question="What is the importance of good variable naming in programming?",
789
- comprehension_answer="Good variable naming makes code more readable and maintainable.",
790
- explanation="Descriptive variable names help other developers (and yourself) understand what the code does.",
791
- improved_code="# Improved version\n# Add your improvements here",
792
- fix_explanation="This is a fallback version. The model should provide specific improvements.",
793
- difficulty_level=student_level,
794
- learning_objectives=fallback_analysis["learning_objectives"],
795
- estimated_time_to_improve=fallback_analysis["estimated_time_to_improve"]
796
- )
797
-
798
-
799
- def main():
800
- """Main function to demonstrate the system with fine-tuned model"""
801
- print("Generative AI for Programming Education")
802
- print("Using Fine-tuned CodeLlama-7B Model")
803
- print("=" * 50)
804
-
805
- # System information
806
- print(f"Available GPUs: {torch.cuda.device_count()}")
807
- if torch.cuda.is_available():
808
- print("GPU Memory before loading:")
809
- get_gpu_memory()
810
- else:
811
- print("System Memory before loading:")
812
- get_system_memory()
813
-
814
- # Initialize the system with your fine-tuned model path
815
- # Update this path to point to your actual fine-tuned model
816
- model_path = r"C:\Users\farou\OneDrive - Aston University\finetunning"
817
- ai_tutor = ProgrammingEducationAI(model_path)
818
-
819
- try:
820
- # Load the fine-tuned model
821
- print("Loading fine-tuned model...")
822
- ai_tutor.load_model()
823
- print("โœ“ Model loaded successfully!")
824
-
825
- # Clear cache after loading
826
- clear_cuda_cache()
827
- if torch.cuda.is_available():
828
- print("GPU Memory after loading:")
829
- get_gpu_memory()
830
- else:
831
- print("System Memory after loading:")
832
- get_system_memory()
833
-
834
- # Example student code for testing
835
- student_code = """
836
- def find_duplicates(numbers):
837
- x = []
838
- for i in range(len(numbers)):
839
- for j in range(i+1, len(numbers)):
840
- if numbers[i] == numbers[j]:
841
- x.append(numbers[i])
842
- return x
843
-
844
- # Test the function
845
- result = find_duplicates([1, 2, 3, 2, 4, 5, 3])
846
- print(result)
847
- """
848
-
849
- print(f"\nAnalyzing student code:\n{student_code}")
850
-
851
- # Get feedback using fine-tuned model
852
- feedback_list = ai_tutor.analyze_student_code(student_code, "beginner")
853
-
854
- print("\n" + "="*50)
855
- print("FINE-TUNED MODEL FEEDBACK:")
856
- print("="*50)
857
-
858
- for i, feedback in enumerate(feedback_list, 1):
859
- print(f"\n{i}. {feedback.feedback_type.upper()}:")
860
- print(f" {feedback.feedback_message}")
861
- if feedback.suggested_improvement:
862
- print(f" Suggestion: {feedback.suggested_improvement}")
863
- print(
864
- f" Learning objectives: {', '.join(feedback.learning_objectives)}")
865
-
866
- # Demonstrate direct model calls
867
- print("\n" + "="*50)
868
- print("DIRECT MODEL GENERATION:")
869
- print("="*50)
870
-
871
- # Code review
872
- print("\n1. CODE REVIEW:")
873
- code_review = ai_tutor.generate_code_review(student_code, "beginner")
874
- print(code_review)
875
-
876
- # Educational feedback
877
- print("\n2. EDUCATIONAL FEEDBACK:")
878
- educational_feedback = ai_tutor.generate_educational_feedback(
879
- student_code, "beginner")
880
- print(educational_feedback)
881
-
882
- # Demonstrate comprehensive feedback system
883
- print("\n" + "="*50)
884
- print("COMPREHENSIVE EDUCATIONAL FEEDBACK SYSTEM:")
885
- print("="*50)
886
-
887
- comprehensive_feedback = ai_tutor.generate_comprehensive_feedback(
888
- student_code, "beginner")
889
-
890
- # Display comprehensive feedback
891
- print("\n๐Ÿ“Š CODE ANALYSIS:")
892
- print("="*30)
893
-
894
- print("\nโœ… STRENGTHS:")
895
- for i, strength in enumerate(comprehensive_feedback.strengths, 1):
896
- print(f" {i}. {strength}")
897
-
898
- print("\nโŒ WEAKNESSES:")
899
- for i, weakness in enumerate(comprehensive_feedback.weaknesses, 1):
900
- print(f" {i}. {weakness}")
901
-
902
- print("\nโš ๏ธ ISSUES:")
903
- for i, issue in enumerate(comprehensive_feedback.issues, 1):
904
- print(f" {i}. {issue}")
905
-
906
- print("\n๐Ÿ“ STEP-BY-STEP IMPROVEMENT GUIDE:")
907
- print("="*40)
908
- for i, step in enumerate(comprehensive_feedback.step_by_step_improvement, 1):
909
- print(f" Step {i}: {step}")
910
-
911
- print("\n๐ŸŽ“ LEARNING POINTS:")
912
- print("="*25)
913
- for i, point in enumerate(comprehensive_feedback.learning_points, 1):
914
- print(f" {i}. {point}")
915
-
916
- print("\n๐Ÿ“‹ REVIEW SUMMARY:")
917
- print("="*20)
918
- print(f" {comprehensive_feedback.review_summary}")
919
-
920
- print("\nโ“ COMPREHENSION QUESTION:")
921
- print("="*30)
922
- print(f" Question: {comprehensive_feedback.comprehension_question}")
923
- print(f" Answer: {comprehensive_feedback.comprehension_answer}")
924
- print(f" Explanation: {comprehensive_feedback.explanation}")
925
-
926
- print("\n๐Ÿ”ง IMPROVED CODE:")
927
- print("="*20)
928
- print(comprehensive_feedback.improved_code)
929
-
930
- print("\n๐Ÿ’ก FIX EXPLANATION:")
931
- print("="*20)
932
- print(f" {comprehensive_feedback.fix_explanation}")
933
-
934
- print("\n๐Ÿ“Š METADATA:")
935
- print("="*15)
936
- print(f" Student Level: {comprehensive_feedback.student_level}")
937
- print(
938
- f" Learning Objectives: {', '.join(comprehensive_feedback.learning_objectives)}")
939
- print(
940
- f" Estimated Time to Improve: {comprehensive_feedback.estimated_time_to_improve}")
941
-
942
- except Exception as e:
943
- print(f"Error: {e}")
944
- print(
945
- "Make sure to update the model_path variable to point to your fine-tuned model.")
946
-
947
-
948
- if __name__ == "__main__":
949
- main()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Generative AI for Enhancing Programming Education
3
+ ================================================
4
+
5
+ This project implements a fine-tuned CodeLlama-7B model to provide structured,
6
+ educational code feedback for programming students.
7
+
8
+ Problem Statement:
9
+ - High dropout rates in programming education
10
+ - Inefficient feedback loops
11
+ - Lack of personalized learning
12
+ - Limited instructor bandwidth
13
+ - Current AI tools prioritize productivity over learning
14
+
15
+ Solution:
16
+ - Fine-tuned CodeLlama-7B for educational feedback
17
+ - Structured, actionable code reviews
18
+ - Beginner-friendly explanations
19
+ - Personalized adaptation based on skill level
20
+ - Educational focus with ethical safeguards
21
+
22
+ Author: [Your Name]
23
+ Date: [Current Date]
24
+ """
25
+
26
+ import re
27
+ from dataclasses import dataclass
28
+ from typing import Dict, List, Optional, Tuple
29
+ import logging
30
+ import json
31
+ from transformers import AutoTokenizer, AutoModelForCausalLM
32
+ import os
33
+ import gc
34
+ import torch
35
+ import warnings
36
+ warnings.filterwarnings("ignore", category=UserWarning)
37
+
38
+ # --- Critical Environment Setup (Must be before imports) ---
39
+ os.environ["TOKENIZERS_PARALLELISM"] = "false"
40
+ os.environ["DATASETS_DISABLE_MULTIPROCESSING"] = "1"
41
+
42
+ # Clear any existing CUDA cache (only if CUDA is available)
43
+ if torch.cuda.is_available():
44
+ os.environ["PYTORCH_CUDA_ALLOC_CONF"] = "max_split_size_mb:128,garbage_collection_threshold:0.6"
45
+ torch.cuda.empty_cache()
46
+ gc.collect()
47
+
48
+
49
+ # Configure logging
50
+ logging.basicConfig(level=logging.INFO)
51
+ logger = logging.getLogger(__name__)
52
+
53
+
54
+ def clear_cuda_cache():
55
+ """Clear CUDA cache and run garbage collection"""
56
+ if torch.cuda.is_available():
57
+ torch.cuda.empty_cache()
58
+ torch.cuda.synchronize()
59
+ gc.collect()
60
+
61
+
62
+ def get_system_memory():
63
+ """Get system memory information"""
64
+ try:
65
+ import psutil
66
+ memory = psutil.virtual_memory()
67
+ print(
68
+ f"System RAM: {memory.used / (1024**3):.1f}GB / {memory.total / (1024**3):.1f}GB used ({memory.percent:.1f}%)")
69
+ except Exception as e:
70
+ print(f"Could not get system memory info: {e}")
71
+
72
+
73
+ def get_gpu_memory():
74
+ """Get GPU memory information (if available)"""
75
+ if torch.cuda.is_available():
76
+ try:
77
+ import subprocess
78
+ result = subprocess.run(['nvidia-smi', '--query-gpu=memory.used,memory.total', '--format=csv,nounits,noheader'],
79
+ capture_output=True, text=True)
80
+ lines = result.stdout.strip().split('\n')
81
+ for i, line in enumerate(lines):
82
+ used, total = map(int, line.split(', '))
83
+ print(
84
+ f"GPU {i}: {used}MB / {total}MB used ({used/total*100:.1f}%)")
85
+ except Exception as e:
86
+ print(f"Could not get GPU memory info: {e}")
87
+ else:
88
+ print("No GPU available - using CPU only")
89
+
90
+
91
+ @dataclass
92
+ class CodeFeedback:
93
+ """Data structure for storing code feedback"""
94
+ code_snippet: str
95
+ feedback_type: str # 'syntax', 'logic', 'optimization', 'style', 'explanation'
96
+ feedback_message: str
97
+ suggested_improvement: Optional[str] = None
98
+ difficulty_level: str = 'beginner' # 'beginner', 'intermediate', 'advanced'
99
+ learning_objectives: List[str] = None
100
+
101
+
102
+ @dataclass
103
+ class ComprehensiveFeedback:
104
+ """Comprehensive feedback structure with all educational components"""
105
+ code_snippet: str
106
+ student_level: str
107
+
108
+ # Analysis
109
+ strengths: List[str]
110
+ weaknesses: List[str]
111
+ issues: List[str]
112
+
113
+ # Educational content
114
+ step_by_step_improvement: List[str]
115
+ learning_points: List[str]
116
+ review_summary: str
117
+
118
+ # Interactive elements
119
+ comprehension_question: str
120
+ comprehension_answer: str
121
+ explanation: str
122
+
123
+ # Code fixes
124
+ improved_code: str
125
+ fix_explanation: str
126
+
127
+ # Metadata
128
+ difficulty_level: str
129
+ learning_objectives: List[str]
130
+ estimated_time_to_improve: str
131
+
132
+
133
+ class ProgrammingEducationAI:
134
+ """
135
+ Main class for the fine-tuned CodeLlama model for programming education
136
+ """
137
+
138
+ def __init__(self, model_path: str = "TomoriFarouk/codellama-7b-programming-education"):
139
+ """
140
+ Initialize the fine-tuned model and tokenizer
141
+
142
+ Args:
143
+ model_path: Path to your fine-tuned CodeLlama-7B model
144
+ """
145
+ self.model_path = model_path
146
+ self.tokenizer = None
147
+ self.model = None
148
+ self.feedback_templates = self._load_feedback_templates()
149
+ self.code_review_prompt_template = self._load_code_review_prompt()
150
+ self.code_feedback_prompt_template = self._load_code_feedback_prompt()
151
+ self.comprehensive_feedback_prompt = self._load_comprehensive_feedback_prompt()
152
+ self.comprehension_question_prompt = self._load_comprehension_question_prompt()
153
+ self.code_fix_prompt = self._load_code_fix_prompt()
154
+
155
+ def _load_code_review_prompt(self) -> str:
156
+ """Load the code review prompt template used during fine-tuning"""
157
+ return """You are an expert programming tutor. Review the following student code and provide educational feedback.
158
+
159
+ Student Code:
160
+ {code}
161
+
162
+ Student Level: {level}
163
+
164
+ Please provide:
165
+ 1. Syntax errors (if any)
166
+ 2. Logic errors (if any)
167
+ 3. Style improvements
168
+ 4. Optimization suggestions
169
+ 5. Educational explanations
170
+
171
+ Feedback:"""
172
+
173
+ def _load_code_feedback_prompt(self) -> str:
174
+ """Load the code feedback prompt template used during fine-tuning"""
175
+ return """You are a helpful programming tutor. The student has written this code:
176
+
177
+ {code}
178
+
179
+ Student Level: {level}
180
+
181
+ Provide constructive, educational feedback that helps the student learn. Focus on:
182
+ - What they did well
183
+ - What can be improved
184
+ - Why the improvement matters
185
+ - How to implement the improvement
186
+
187
+ Feedback:"""
188
+
189
+ def _load_feedback_templates(self) -> Dict[str, str]:
190
+ """Load predefined feedback templates for different scenarios"""
191
+ return {
192
+ "syntax_error": "I notice there's a syntax issue in your code. {error_description}. "
193
+ "Here's what's happening: {explanation}. "
194
+ "Try this correction: {suggestion}",
195
+
196
+ "logic_error": "Your code has a logical issue. {problem_description}. "
197
+ "The problem is: {explanation}. "
198
+ "Consider this approach: {suggestion}",
199
+
200
+ "optimization": "Your code works, but we can make it more efficient! "
201
+ "Current complexity: {current_complexity}. "
202
+ "Optimized version: {optimized_complexity}. "
203
+ "Here's how: {explanation}",
204
+
205
+ "style_improvement": "Great work! Here's a style tip: {tip}. "
206
+ "This makes your code more readable and maintainable.",
207
+
208
+ "concept_explanation": "Let me explain this concept: {concept}. "
209
+ "In simple terms: {simple_explanation}. "
210
+ "Example: {example}"
211
+ }
212
+
213
+ def load_model(self, load_in_8bit=False, device_map="auto"):
214
+ """
215
+ Load the fine-tuned model and tokenizer optimized for HF Spaces
216
+
217
+ Args:
218
+ load_in_8bit: Whether to load model in 8-bit precision for memory savings
219
+ device_map: Device mapping strategy ("auto", "cpu", or specific device)
220
+ """
221
+ try:
222
+ # Get HF token for private model access
223
+ hf_token = os.getenv("HF_TOKEN", None)
224
+
225
+ logger.info("Loading tokenizer...")
226
+ self.tokenizer = AutoTokenizer.from_pretrained(
227
+ self.model_path,
228
+ trust_remote_code=True,
229
+ token=hf_token # Use token for private models
230
+ )
231
+
232
+ # Set padding token
233
+ if self.tokenizer.pad_token is None:
234
+ self.tokenizer.pad_token = self.tokenizer.eos_token
235
+ self.tokenizer.pad_token_id = self.tokenizer.eos_token_id
236
+
237
+ logger.info(
238
+ f"Tokenizer loaded - Vocab size: {len(self.tokenizer)}")
239
+
240
+ # Load model with memory optimization options
241
+ print(
242
+ f"Loading model with 8-bit: {load_in_8bit}, device_map: {device_map}...")
243
+
244
+ # Prepare model loading arguments
245
+ model_kwargs = {
246
+ "torch_dtype": torch.float32,
247
+ "low_cpu_mem_usage": True,
248
+ "trust_remote_code": True,
249
+ "offload_folder": "offload", # Offload to disk if needed
250
+ "token": hf_token # Use token for private models
251
+ }
252
+
253
+ # Add 8-bit quantization if requested
254
+ if load_in_8bit:
255
+ model_kwargs["load_in_8bit"] = True
256
+ print("๐Ÿ”ง Using 8-bit quantization for memory optimization")
257
+
258
+ # Add device mapping
259
+ if device_map:
260
+ model_kwargs["device_map"] = device_map
261
+ print(f"๐Ÿ”ง Using device map: {device_map}")
262
+
263
+ self.model = AutoModelForCausalLM.from_pretrained(
264
+ self.model_path,
265
+ **model_kwargs
266
+ )
267
+
268
+ # Enable gradient checkpointing for memory savings
269
+ self.model.gradient_checkpointing_enable()
270
+
271
+ logger.info("Fine-tuned model loaded successfully")
272
+ logger.info(
273
+ f"Model loaded on devices: {getattr(self.model, 'hf_device_map', 'CPU')}")
274
+
275
+ except Exception as e:
276
+ logger.error(f"Error loading fine-tuned model: {e}")
277
+ raise
278
+
279
+ def generate_code_review(self, code: str, student_level: str = "beginner") -> str:
280
+ """
281
+ Generate code review using the fine-tuned model
282
+
283
+ Args:
284
+ code: Student's code to review
285
+ student_level: Student's skill level
286
+
287
+ Returns:
288
+ Generated code review feedback
289
+ """
290
+ if not self.model or not self.tokenizer:
291
+ raise ValueError("Model not loaded. Call load_model() first.")
292
+
293
+ # Format the prompt using the template from fine-tuning
294
+ prompt = self.code_review_prompt_template.format(
295
+ code=code,
296
+ level=student_level
297
+ )
298
+
299
+ # Tokenize input
300
+ inputs = self.tokenizer(
301
+ prompt, return_tensors="pt", truncation=True, max_length=2048)
302
+
303
+ # Generate response
304
+ with torch.no_grad():
305
+ outputs = self.model.generate(
306
+ inputs.input_ids,
307
+ max_new_tokens=512,
308
+ temperature=0.7,
309
+ do_sample=True,
310
+ pad_token_id=self.tokenizer.eos_token_id
311
+ )
312
+
313
+ # Decode response
314
+ response = self.tokenizer.decode(outputs[0], skip_special_tokens=True)
315
+
316
+ # Extract only the generated part (after the prompt)
317
+ generated_text = response[len(prompt):].strip()
318
+
319
+ return generated_text
320
+
321
+ def generate_educational_feedback(self, code: str, student_level: str = "beginner") -> str:
322
+ """
323
+ Generate educational feedback using the fine-tuned model
324
+
325
+ Args:
326
+ code: Student's code to provide feedback on
327
+ student_level: Student's skill level
328
+
329
+ Returns:
330
+ Generated educational feedback
331
+ """
332
+ if not self.model or not self.tokenizer:
333
+ raise ValueError("Model not loaded. Call load_model() first.")
334
+
335
+ # Format the prompt using the template from fine-tuning
336
+ prompt = self.code_feedback_prompt_template.format(
337
+ code=code,
338
+ level=student_level
339
+ )
340
+
341
+ # Tokenize input
342
+ inputs = self.tokenizer(
343
+ prompt, return_tensors="pt", truncation=True, max_length=2048)
344
+
345
+ # Generate response
346
+ with torch.no_grad():
347
+ outputs = self.model.generate(
348
+ inputs.input_ids,
349
+ max_new_tokens=512,
350
+ temperature=0.7,
351
+ do_sample=True,
352
+ pad_token_id=self.tokenizer.eos_token_id
353
+ )
354
+
355
+ # Decode response
356
+ response = self.tokenizer.decode(outputs[0], skip_special_tokens=True)
357
+
358
+ # Extract only the generated part (after the prompt)
359
+ generated_text = response[len(prompt):].strip()
360
+
361
+ return generated_text
362
+
363
+ def analyze_student_code(self, code: str, student_level: str = "beginner") -> List[CodeFeedback]:
364
+ """
365
+ Analyze student code and provide educational feedback using the fine-tuned model
366
+
367
+ Args:
368
+ code: The student's code to analyze
369
+ student_level: Student's skill level ('beginner', 'intermediate', 'advanced')
370
+
371
+ Returns:
372
+ List of CodeFeedback objects
373
+ """
374
+ feedback_list = []
375
+
376
+ # Use fine-tuned model for comprehensive code review
377
+ try:
378
+ code_review = self.generate_code_review(code, student_level)
379
+ educational_feedback = self.generate_educational_feedback(
380
+ code, student_level)
381
+
382
+ # Create structured feedback from model output
383
+ feedback_list.append(CodeFeedback(
384
+ code_snippet=code,
385
+ feedback_type="comprehensive_review",
386
+ feedback_message=code_review,
387
+ difficulty_level=student_level,
388
+ learning_objectives=["code_analysis", "best_practices"]
389
+ ))
390
+
391
+ feedback_list.append(CodeFeedback(
392
+ code_snippet=code,
393
+ feedback_type="educational_guidance",
394
+ feedback_message=educational_feedback,
395
+ difficulty_level=student_level,
396
+ learning_objectives=["learning", "improvement"]
397
+ ))
398
+
399
+ except Exception as e:
400
+ logger.warning(
401
+ f"Fine-tuned model failed, falling back to rule-based analysis: {e}")
402
+ # Fallback to rule-based analysis if model fails
403
+ feedback_list = self._fallback_analysis(code, student_level)
404
+
405
+ return feedback_list
406
+
407
+ def _fallback_analysis(self, code: str, student_level: str) -> List[CodeFeedback]:
408
+ """Fallback analysis using rule-based methods if fine-tuned model fails"""
409
+ feedback_list = []
410
+
411
+ # Analyze syntax
412
+ syntax_feedback = self._check_syntax(code, student_level)
413
+ if syntax_feedback:
414
+ feedback_list.append(syntax_feedback)
415
+
416
+ # Analyze logic and structure
417
+ logic_feedback = self._check_logic(code, student_level)
418
+ if logic_feedback:
419
+ feedback_list.extend(logic_feedback)
420
+
421
+ # Check for optimization opportunities
422
+ optimization_feedback = self._check_optimization(code, student_level)
423
+ if optimization_feedback:
424
+ feedback_list.append(optimization_feedback)
425
+
426
+ # Provide style suggestions
427
+ style_feedback = self._check_style(code, student_level)
428
+ if style_feedback:
429
+ feedback_list.append(style_feedback)
430
+
431
+ return feedback_list
432
+
433
+ def _check_syntax(self, code: str, student_level: str) -> Optional[CodeFeedback]:
434
+ """Check for syntax errors and provide educational feedback"""
435
+ # This would integrate with the fine-tuned model
436
+ # For now, using basic pattern matching as placeholder
437
+
438
+ common_syntax_errors = {
439
+ r"print\s*\([^)]*\)\s*$": "Remember to add a colon after print statements in some contexts",
440
+ r"if\s+[^:]+$": "Don't forget the colon after your if condition",
441
+ r"for\s+[^:]+$": "Don't forget the colon after your for loop",
442
+ }
443
+
444
+ for pattern, message in common_syntax_errors.items():
445
+ if re.search(pattern, code):
446
+ return CodeFeedback(
447
+ code_snippet=code,
448
+ feedback_type="syntax",
449
+ feedback_message=message,
450
+ difficulty_level=student_level,
451
+ learning_objectives=["syntax", "basic_python"]
452
+ )
453
+
454
+ return None
455
+
456
+ def _check_logic(self, code: str, student_level: str) -> List[CodeFeedback]:
457
+ """Check for logical errors and provide educational feedback"""
458
+ feedback_list = []
459
+
460
+ # Check for infinite loops
461
+ if "while True:" in code and "break" not in code:
462
+ feedback_list.append(CodeFeedback(
463
+ code_snippet=code,
464
+ feedback_type="logic",
465
+ feedback_message="This while loop will run forever! Make sure to include a break statement or condition to exit the loop.",
466
+ difficulty_level=student_level,
467
+ learning_objectives=["control_flow", "loops"]
468
+ ))
469
+
470
+ # Check for unused variables
471
+ # This is a simplified check - the actual model would be more sophisticated
472
+ if "x = " in code and "x" not in code.replace("x = ", ""):
473
+ feedback_list.append(CodeFeedback(
474
+ code_snippet=code,
475
+ feedback_type="logic",
476
+ feedback_message="You created variable 'x' but didn't use it. Consider removing unused variables to keep your code clean.",
477
+ difficulty_level=student_level,
478
+ learning_objectives=["variables", "code_cleanliness"]
479
+ ))
480
+
481
+ return feedback_list
482
+
483
+ def _check_optimization(self, code: str, student_level: str) -> Optional[CodeFeedback]:
484
+ """Check for optimization opportunities"""
485
+ # Check for nested loops that could be optimized
486
+ if code.count("for") > 1 and code.count("in range") > 1:
487
+ return CodeFeedback(
488
+ code_snippet=code,
489
+ feedback_type="optimization",
490
+ feedback_message="You have nested loops here. Consider if you can optimize this to O(n) instead of O(nยฒ).",
491
+ suggested_improvement="Use a hashmap or set to reduce complexity",
492
+ difficulty_level=student_level,
493
+ learning_objectives=["algorithms",
494
+ "complexity", "optimization"]
495
+ )
496
+
497
+ return None
498
+
499
+ def _check_style(self, code: str, student_level: str) -> Optional[CodeFeedback]:
500
+ """Check for style improvements"""
501
+ # Check for meaningful variable names
502
+ if "x" in code or "y" in code or "z" in code:
503
+ return CodeFeedback(
504
+ code_snippet=code,
505
+ feedback_type="style",
506
+ feedback_message="Consider using more descriptive variable names instead of x, y, z. This makes your code easier to understand.",
507
+ difficulty_level=student_level,
508
+ learning_objectives=["naming_conventions", "readability"]
509
+ )
510
+
511
+ return None
512
+
513
+ def generate_explanation(self, concept: str, student_level: str) -> str:
514
+ """
515
+ Generate explanations for programming concepts based on student level
516
+
517
+ Args:
518
+ concept: The concept to explain
519
+ student_level: Student's skill level
520
+
521
+ Returns:
522
+ Explanation tailored to the student's level
523
+ """
524
+ explanations = {
525
+ "variables": {
526
+ "beginner": "Variables are like labeled boxes where you store information. Think of 'name = \"John\"' as putting \"John\" in a box labeled 'name'.",
527
+ "intermediate": "Variables are memory locations that store data. They have a name, type, and value. Python is dynamically typed, so the type is inferred.",
528
+ "advanced": "Variables in Python are references to objects in memory. They're dynamically typed and use reference counting for memory management."
529
+ },
530
+ "loops": {
531
+ "beginner": "Loops repeat code multiple times. 'for' loops are great when you know how many times to repeat, 'while' loops when you don't.",
532
+ "intermediate": "Loops control program flow. 'for' iterates over sequences, 'while' continues until a condition is False. Consider time complexity.",
533
+ "advanced": "Loops are fundamental control structures. Python's 'for' is actually a foreach loop. Consider iterator patterns and generator expressions."
534
+ }
535
+ }
536
+
537
+ return explanations.get(concept, {}).get(student_level, f"Explanation for {concept} at {student_level} level")
538
+
539
+ def _load_comprehensive_feedback_prompt(self) -> str:
540
+ """Load the comprehensive feedback prompt template"""
541
+ return """You are an expert programming tutor. Provide comprehensive educational feedback for the following student code.
542
+
543
+ Student Code:
544
+ {code}
545
+
546
+ Student Level: {level}
547
+
548
+ Please provide a detailed analysis in the following JSON format:
549
+
550
+ {{
551
+ "strengths": ["strength1", "strength2", "strength3"],
552
+ "weaknesses": ["weakness1", "weakness2", "weakness3"],
553
+ "issues": ["issue1", "issue2", "issue3"],
554
+ "step_by_step_improvement": [
555
+ "Step 1: Description of first improvement",
556
+ "Step 2: Description of second improvement",
557
+ "Step 3: Description of third improvement"
558
+ ],
559
+ "learning_points": [
560
+ "Learning point 1: What the student should understand",
561
+ "Learning point 2: Key concept to grasp",
562
+ "Learning point 3: Best practice to follow"
563
+ ],
564
+ "review_summary": "A comprehensive review of the code highlighting key areas for improvement",
565
+ "learning_objectives": ["objective1", "objective2", "objective3"],
566
+ "estimated_time_to_improve": "5-10 minutes"
567
+ }}
568
+
569
+ Focus on educational value and constructive feedback that helps the student learn and improve."""
570
+
571
+ def _load_comprehension_question_prompt(self) -> str:
572
+ """Load the comprehension question generation prompt"""
573
+ return """Based on the learning points and improvements discussed, generate a comprehension question to test the student's understanding.
574
+
575
+ Learning Points: {learning_points}
576
+ Code Issues: {issues}
577
+ Student Level: {level}
578
+
579
+ Generate a question that tests understanding of the key concepts discussed. The question should be appropriate for the student's level.
580
+
581
+ Format your response as JSON:
582
+ {{
583
+ "question": "Your comprehension question here",
584
+ "answer": "The correct answer",
585
+ "explanation": "Detailed explanation of why this answer is correct"
586
+ }}
587
+
588
+ Make the question challenging but fair for the student's level."""
589
+
590
+ def _load_code_fix_prompt(self) -> str:
591
+ """Load the code fix generation prompt"""
592
+ return """You are an expert programming tutor. Based on the analysis and learning points, provide an improved version of the student's code.
593
+
594
+ Original Code:
595
+ {code}
596
+
597
+ Issues Identified: {issues}
598
+ Learning Points: {learning_points}
599
+ Student Level: {level}
600
+
601
+ Provide an improved version of the code that addresses the issues while maintaining educational value. Include comments to explain the improvements.
602
+
603
+ Format your response as JSON:
604
+ {{
605
+ "improved_code": "The improved code with comments",
606
+ "fix_explanation": "Detailed explanation of what was changed and why"
607
+ }}
608
+
609
+ Focus on educational improvements that help the student understand better practices."""
610
+
611
+ def adapt_feedback_complexity(self, feedback: CodeFeedback, student_level: str) -> CodeFeedback:
612
+ """
613
+ Adapt feedback complexity based on student level
614
+
615
+ Args:
616
+ feedback: Original feedback
617
+ student_level: Student's skill level
618
+
619
+ Returns:
620
+ Adapted feedback
621
+ """
622
+ if student_level == "beginner":
623
+ # Simplify language and add more examples
624
+ feedback.feedback_message = feedback.feedback_message.replace(
625
+ "O(nยฒ)", "quadratic time (slower)"
626
+ ).replace(
627
+ "O(n)", "linear time (faster)"
628
+ )
629
+ elif student_level == "advanced":
630
+ # Add more technical details
631
+ if "optimization" in feedback.feedback_type:
632
+ feedback.feedback_message += " Consider the space-time tradeoff and cache locality."
633
+
634
+ return feedback
635
+
636
+ def generate_comprehensive_feedback(self, code: str, student_level: str = "beginner") -> ComprehensiveFeedback:
637
+ """
638
+ Generate comprehensive educational feedback with all components
639
+
640
+ Args:
641
+ code: Student's code to analyze
642
+ student_level: Student's skill level
643
+
644
+ Returns:
645
+ ComprehensiveFeedback object with all educational components
646
+ """
647
+ if not self.model or not self.tokenizer:
648
+ raise ValueError("Model not loaded. Call load_model() first.")
649
+
650
+ try:
651
+ # Step 1: Generate comprehensive analysis
652
+ comprehensive_analysis = self._generate_comprehensive_analysis(
653
+ code, student_level)
654
+
655
+ # Step 2: Generate comprehension question
656
+ comprehension_data = self._generate_comprehension_question(
657
+ comprehensive_analysis["learning_points"],
658
+ comprehensive_analysis["issues"],
659
+ student_level
660
+ )
661
+
662
+ # Step 3: Generate improved code
663
+ code_fix_data = self._generate_code_fix(
664
+ code,
665
+ comprehensive_analysis["issues"],
666
+ comprehensive_analysis["learning_points"],
667
+ student_level
668
+ )
669
+
670
+ # Create comprehensive feedback object
671
+ return ComprehensiveFeedback(
672
+ code_snippet=code,
673
+ student_level=student_level,
674
+ strengths=comprehensive_analysis["strengths"],
675
+ weaknesses=comprehensive_analysis["weaknesses"],
676
+ issues=comprehensive_analysis["issues"],
677
+ step_by_step_improvement=comprehensive_analysis["step_by_step_improvement"],
678
+ learning_points=comprehensive_analysis["learning_points"],
679
+ review_summary=comprehensive_analysis["review_summary"],
680
+ comprehension_question=comprehension_data["question"],
681
+ comprehension_answer=comprehension_data["answer"],
682
+ explanation=comprehension_data["explanation"],
683
+ improved_code=code_fix_data["improved_code"],
684
+ fix_explanation=code_fix_data["fix_explanation"],
685
+ difficulty_level=student_level,
686
+ learning_objectives=comprehensive_analysis["learning_objectives"],
687
+ estimated_time_to_improve=comprehensive_analysis["estimated_time_to_improve"]
688
+ )
689
+
690
+ except Exception as e:
691
+ logger.error(f"Error generating comprehensive feedback: {e}")
692
+ # Return a basic comprehensive feedback if model fails
693
+ return self._create_fallback_comprehensive_feedback(code, student_level)
694
+
695
+ def _generate_comprehensive_analysis(self, code: str, student_level: str) -> Dict:
696
+ """Generate comprehensive analysis using the fine-tuned model"""
697
+ prompt = self.comprehensive_feedback_prompt.format(
698
+ code=code,
699
+ level=student_level
700
+ )
701
+
702
+ response = self._generate_model_response(prompt)
703
+
704
+ try:
705
+ # Try to parse JSON response
706
+ import json
707
+ return json.loads(response)
708
+ except json.JSONDecodeError:
709
+ logger.warning("Failed to parse JSON response, using fallback")
710
+ return self._create_fallback_analysis(code, student_level)
711
+
712
+ def _generate_comprehension_question(self, learning_points: List[str], issues: List[str], student_level: str) -> Dict:
713
+ """Generate comprehension question using the fine-tuned model"""
714
+ prompt = self.comprehension_question_prompt.format(
715
+ learning_points=", ".join(learning_points),
716
+ issues=", ".join(issues),
717
+ level=student_level
718
+ )
719
+
720
+ response = self._generate_model_response(prompt)
721
+
722
+ try:
723
+ import json
724
+ return json.loads(response)
725
+ except json.JSONDecodeError:
726
+ logger.warning(
727
+ "Failed to parse comprehension question JSON, using fallback")
728
+ return {
729
+ "question": "What is the main concept you learned from this code review?",
730
+ "answer": "The main concept is understanding code structure and best practices.",
731
+ "explanation": "This question tests your understanding of the key learning points discussed."
732
+ }
733
+
734
+ def _generate_code_fix(self, code: str, issues: List[str], learning_points: List[str], student_level: str) -> Dict:
735
+ """Generate improved code using the fine-tuned model"""
736
+ prompt = self.code_fix_prompt.format(
737
+ code=code,
738
+ issues=", ".join(issues),
739
+ learning_points=", ".join(learning_points),
740
+ level=student_level
741
+ )
742
+
743
+ response = self._generate_model_response(prompt)
744
+
745
+ try:
746
+ import json
747
+ return json.loads(response)
748
+ except json.JSONDecodeError:
749
+ logger.warning("Failed to parse code fix JSON, using fallback")
750
+ return {
751
+ "improved_code": "# Improved version of your code\n# Add comments and improvements here",
752
+ "fix_explanation": "This is a fallback improved version. The model should provide specific improvements."
753
+ }
754
+
755
+ def _generate_model_response(self, prompt: str) -> str:
756
+ """Generate response from the fine-tuned model"""
757
+ inputs = self.tokenizer(
758
+ prompt, return_tensors="pt", truncation=True, max_length=2048)
759
+
760
+ # Move to CPU if no GPU available
761
+ if not torch.cuda.is_available():
762
+ inputs = {k: v.cpu() for k, v in inputs.items()}
763
+
764
+ with torch.no_grad():
765
+ outputs = self.model.generate(
766
+ inputs.input_ids,
767
+ max_new_tokens=512,
768
+ temperature=0.7,
769
+ do_sample=True,
770
+ pad_token_id=self.tokenizer.eos_token_id
771
+ )
772
+
773
+ response = self.tokenizer.decode(outputs[0], skip_special_tokens=True)
774
+ return response[len(prompt):].strip()
775
+
776
+ def _create_fallback_analysis(self, code: str, student_level: str) -> Dict:
777
+ """Create fallback analysis when model fails"""
778
+ return {
779
+ "strengths": ["Your code has a clear structure", "You're using appropriate data types"],
780
+ "weaknesses": ["Could improve variable naming", "Consider adding comments"],
781
+ "issues": ["Basic syntax and style issues"],
782
+ "step_by_step_improvement": [
783
+ "Step 1: Add descriptive variable names",
784
+ "Step 2: Include comments explaining your logic",
785
+ "Step 3: Consider code optimization"
786
+ ],
787
+ "learning_points": [
788
+ "Good variable naming improves code readability",
789
+ "Comments help others understand your code",
790
+ "Always consider efficiency in your solutions"
791
+ ],
792
+ "review_summary": "Your code works but could be improved with better practices.",
793
+ "learning_objectives": ["code_quality", "best_practices", "readability"],
794
+ "estimated_time_to_improve": "10-15 minutes"
795
+ }
796
+
797
+ def _create_fallback_comprehensive_feedback(self, code: str, student_level: str) -> ComprehensiveFeedback:
798
+ """Create fallback comprehensive feedback when model fails"""
799
+ fallback_analysis = self._create_fallback_analysis(code, student_level)
800
+
801
+ return ComprehensiveFeedback(
802
+ code_snippet=code,
803
+ student_level=student_level,
804
+ strengths=fallback_analysis["strengths"],
805
+ weaknesses=fallback_analysis["weaknesses"],
806
+ issues=fallback_analysis["issues"],
807
+ step_by_step_improvement=fallback_analysis["step_by_step_improvement"],
808
+ learning_points=fallback_analysis["learning_points"],
809
+ review_summary=fallback_analysis["review_summary"],
810
+ comprehension_question="What is the importance of good variable naming in programming?",
811
+ comprehension_answer="Good variable naming makes code more readable and maintainable.",
812
+ explanation="Descriptive variable names help other developers (and yourself) understand what the code does.",
813
+ improved_code="# Improved version\n# Add your improvements here",
814
+ fix_explanation="This is a fallback version. The model should provide specific improvements.",
815
+ difficulty_level=student_level,
816
+ learning_objectives=fallback_analysis["learning_objectives"],
817
+ estimated_time_to_improve=fallback_analysis["estimated_time_to_improve"]
818
+ )
819
+
820
+
821
+ def main():
822
+ """Main function to demonstrate the system with fine-tuned model"""
823
+ print("Generative AI for Programming Education")
824
+ print("Using Fine-tuned CodeLlama-7B Model")
825
+ print("=" * 50)
826
+
827
+ # System information
828
+ print(f"Available GPUs: {torch.cuda.device_count()}")
829
+ if torch.cuda.is_available():
830
+ print("GPU Memory before loading:")
831
+ get_gpu_memory()
832
+ else:
833
+ print("System Memory before loading:")
834
+ get_system_memory()
835
+
836
+ # Initialize the system with your fine-tuned model path
837
+ # Update this path to point to your actual fine-tuned model
838
+ model_path = r"C:\Users\farou\OneDrive - Aston University\finetunning"
839
+ ai_tutor = ProgrammingEducationAI(model_path)
840
+
841
+ try:
842
+ # Load the fine-tuned model
843
+ print("Loading fine-tuned model...")
844
+ ai_tutor.load_model()
845
+ print("โœ“ Model loaded successfully!")
846
+
847
+ # Clear cache after loading
848
+ clear_cuda_cache()
849
+ if torch.cuda.is_available():
850
+ print("GPU Memory after loading:")
851
+ get_gpu_memory()
852
+ else:
853
+ print("System Memory after loading:")
854
+ get_system_memory()
855
+
856
+ # Example student code for testing
857
+ student_code = """
858
+ def find_duplicates(numbers):
859
+ x = []
860
+ for i in range(len(numbers)):
861
+ for j in range(i+1, len(numbers)):
862
+ if numbers[i] == numbers[j]:
863
+ x.append(numbers[i])
864
+ return x
865
+
866
+ # Test the function
867
+ result = find_duplicates([1, 2, 3, 2, 4, 5, 3])
868
+ print(result)
869
+ """
870
+
871
+ print(f"\nAnalyzing student code:\n{student_code}")
872
+
873
+ # Get feedback using fine-tuned model
874
+ feedback_list = ai_tutor.analyze_student_code(student_code, "beginner")
875
+
876
+ print("\n" + "="*50)
877
+ print("FINE-TUNED MODEL FEEDBACK:")
878
+ print("="*50)
879
+
880
+ for i, feedback in enumerate(feedback_list, 1):
881
+ print(f"\n{i}. {feedback.feedback_type.upper()}:")
882
+ print(f" {feedback.feedback_message}")
883
+ if feedback.suggested_improvement:
884
+ print(f" Suggestion: {feedback.suggested_improvement}")
885
+ print(
886
+ f" Learning objectives: {', '.join(feedback.learning_objectives)}")
887
+
888
+ # Demonstrate direct model calls
889
+ print("\n" + "="*50)
890
+ print("DIRECT MODEL GENERATION:")
891
+ print("="*50)
892
+
893
+ # Code review
894
+ print("\n1. CODE REVIEW:")
895
+ code_review = ai_tutor.generate_code_review(student_code, "beginner")
896
+ print(code_review)
897
+
898
+ # Educational feedback
899
+ print("\n2. EDUCATIONAL FEEDBACK:")
900
+ educational_feedback = ai_tutor.generate_educational_feedback(
901
+ student_code, "beginner")
902
+ print(educational_feedback)
903
+
904
+ # Demonstrate comprehensive feedback system
905
+ print("\n" + "="*50)
906
+ print("COMPREHENSIVE EDUCATIONAL FEEDBACK SYSTEM:")
907
+ print("="*50)
908
+
909
+ comprehensive_feedback = ai_tutor.generate_comprehensive_feedback(
910
+ student_code, "beginner")
911
+
912
+ # Display comprehensive feedback
913
+ print("\n๐Ÿ“Š CODE ANALYSIS:")
914
+ print("="*30)
915
+
916
+ print("\nโœ… STRENGTHS:")
917
+ for i, strength in enumerate(comprehensive_feedback.strengths, 1):
918
+ print(f" {i}. {strength}")
919
+
920
+ print("\nโŒ WEAKNESSES:")
921
+ for i, weakness in enumerate(comprehensive_feedback.weaknesses, 1):
922
+ print(f" {i}. {weakness}")
923
+
924
+ print("\nโš ๏ธ ISSUES:")
925
+ for i, issue in enumerate(comprehensive_feedback.issues, 1):
926
+ print(f" {i}. {issue}")
927
+
928
+ print("\n๐Ÿ“ STEP-BY-STEP IMPROVEMENT GUIDE:")
929
+ print("="*40)
930
+ for i, step in enumerate(comprehensive_feedback.step_by_step_improvement, 1):
931
+ print(f" Step {i}: {step}")
932
+
933
+ print("\n๐ŸŽ“ LEARNING POINTS:")
934
+ print("="*25)
935
+ for i, point in enumerate(comprehensive_feedback.learning_points, 1):
936
+ print(f" {i}. {point}")
937
+
938
+ print("\n๐Ÿ“‹ REVIEW SUMMARY:")
939
+ print("="*20)
940
+ print(f" {comprehensive_feedback.review_summary}")
941
+
942
+ print("\nโ“ COMPREHENSION QUESTION:")
943
+ print("="*30)
944
+ print(f" Question: {comprehensive_feedback.comprehension_question}")
945
+ print(f" Answer: {comprehensive_feedback.comprehension_answer}")
946
+ print(f" Explanation: {comprehensive_feedback.explanation}")
947
+
948
+ print("\n๐Ÿ”ง IMPROVED CODE:")
949
+ print("="*20)
950
+ print(comprehensive_feedback.improved_code)
951
+
952
+ print("\n๐Ÿ’ก FIX EXPLANATION:")
953
+ print("="*20)
954
+ print(f" {comprehensive_feedback.fix_explanation}")
955
+
956
+ print("\n๐Ÿ“Š METADATA:")
957
+ print("="*15)
958
+ print(f" Student Level: {comprehensive_feedback.student_level}")
959
+ print(
960
+ f" Learning Objectives: {', '.join(comprehensive_feedback.learning_objectives)}")
961
+ print(
962
+ f" Estimated Time to Improve: {comprehensive_feedback.estimated_time_to_improve}")
963
+
964
+ except Exception as e:
965
+ print(f"Error: {e}")
966
+ print(
967
+ "Make sure to update the model_path variable to point to your fine-tuned model.")
968
+
969
+
970
+ if __name__ == "__main__":
971
+ main()