import os from groq import Groq import gradio as gr import pytesseract from sentence_transformers import SentenceTransformer, util from PIL import Image from typing import List import torch from transformers import BertTokenizer, BertModel, T5ForConditionalGeneration, T5Tokenizer import torch.nn.functional as F # Load pre-trained models tokenizer = BertTokenizer.from_pretrained('bert-base-uncased') bert_model = BertModel.from_pretrained('bert-base-uncased') sentence_model = SentenceTransformer('paraphrase-MiniLM-L6-v2') # Load the pre-trained T5 model and tokenizer for grammar error detection grammar_model = T5ForConditionalGeneration.from_pretrained('t5-base') grammar_tokenizer = T5Tokenizer.from_pretrained('t5-base') # Initialize Groq client client = Groq(api_key=os.environ.get("GROQ_API_KEY")) # System prompt for Groq system_prompt = { "role": "system", "content": "You are a useful assistant. You reply with efficient answers." } async def chat_groq(message, history): messages = [system_prompt] for msg in history: messages.append({"role": "user", "content": str(msg[0])}) messages.append({"role": "assistant", "content": str(msg[1])}) messages.append({"role": "user", "content": str(message)}) response_content = '' stream = client.chat.completions.create( model="llama3-70b-8192", messages=messages, max_tokens=1024, temperature=1.3, stream=True ) for chunk in stream: content = chunk.choices[0].delta.content if content: response_content += chunk.choices[0].delta.content yield response_content def extract_text_from_image(filepath: str, languages: List[str]): image = Image.open(filepath) lang_str = '+'.join(languages) return pytesseract.image_to_string(image=image, lang=lang_str) def assign_badge(grade): if grade == 5: return "Gold Badge 🌟" elif grade == 4: return "Silver Badge 🥈" elif grade == 3: return "Bronze Badge 🥉" else: return "Keep Improving Badge 💪" def detailed_feedback(similarity_score): if similarity_score >= 0.9: return {"Clarity": "Excellent", "Completeness": "Complete", "Accuracy": "Accurate"} elif similarity_score >= 0.8: return {"Clarity": "Good", "Completeness": "Almost Complete", "Accuracy": "Mostly Accurate"} elif similarity_score >= 0.7: return {"Clarity": "Fair", "Completeness": "Partial", "Accuracy": "Some Errors"} else: return {"Clarity": "Needs Improvement", "Completeness": "Incomplete", "Accuracy": "Inaccurate"} def get_grade(similarity_score): if similarity_score >= 0.9: return 5 elif similarity_score >= 0.8: return 4 elif similarity_score >= 0.7: return 3 elif similarity_score >= 0.6: return 2 else: return 1 def get_bert_embedding(text): inputs = tokenizer(text, return_tensors='pt', truncation=True, padding=True) with torch.no_grad(): outputs = bert_model(**inputs) embeddings = outputs.last_hidden_state.mean(dim=1) return embeddings def calculate_cosine_similarity(embedding1, embedding2): similarity = F.cosine_similarity(embedding1, embedding2) return similarity.item() def calculate_sentence_similarity(text1, text2): embedding1 = sentence_model.encode(text1, convert_to_tensor=True) embedding2 = sentence_model.encode(text2, convert_to_tensor=True) return util.pytorch_cos_sim(embedding1, embedding2).item() # Grammar detection and penalization using T5 model def detect_grammar_errors(text): input_text = f"grammar: {text}" inputs = grammar_tokenizer.encode(input_text, return_tensors='pt', max_length=512, truncation=True) outputs = grammar_model.generate(inputs, max_length=512, num_beams=4, early_stopping=True) grammar_analysis = grammar_tokenizer.decode(outputs[0], skip_special_tokens=True) # Compare the corrected sentence with the original one corrected_sentence = grammar_analysis # Simple heuristic to count changes as errors (e.g., difference in word count) # You can also use edit distance or other sophisticated techniques to count errors original_words = text.split() corrected_words = corrected_sentence.split() # Calculate a rough estimate of grammar errors based on differences in word count error_count = abs(len(original_words) - len(corrected_words)) return error_count, corrected_sentence def penalize_for_grammar(student_answer): grammar_errors, _ = detect_grammar_errors(student_answer) # Only take the first element (error count) print(f"errors given by grammer:{grammar_errors}") # Apply a penalty based on the number of grammar errors (max 70% penalty) penalty = max(0, 1 - 0.25 * grammar_errors) return penalty def compare_answers(student_answer, teacher_answer): bert_similarity = calculate_cosine_similarity(get_bert_embedding(student_answer), get_bert_embedding(teacher_answer)) print(f"BERT similarity: {bert_similarity}") sentence_similarity = calculate_sentence_similarity(student_answer, teacher_answer) print(f"Sentence similarity: {sentence_similarity}") # Use a higher weight for BERT similarity semantic_similarity = (0.50 * bert_similarity + 0.50 * sentence_similarity) print(f"Semantic similarity: {semantic_similarity}") # Apply grammar penalty grammar_penalty = penalize_for_grammar(student_answer) final_similarity = semantic_similarity - grammar_penalty print(f"grammar penalty: {grammar_penalty}") print(f"Final similarity (after grammar penalty): {final_similarity}") return final_similarity def extract_keywords(text): return set(text.lower().split()) def check_keywords(student_answer, model_answer): student_keywords = extract_keywords(student_answer) teacher_keywords = extract_keywords(model_answer) keyword_overlap = len(student_keywords.intersection(teacher_keywords)) keyword_similarity = keyword_overlap / (len(teacher_keywords) if len(teacher_keywords) > 0 else 1) print(f"Keyword similarity: {keyword_similarity}") return keyword_similarity def evaluate_answer(image, languages, model_answer): student_answer = extract_text_from_image(image, languages) print(f"Extracted student answer: {student_answer}") # Calculate semantic similarity semantic_similarity = compare_answers(student_answer, model_answer) # Calculate keyword similarity keyword_similarity = check_keywords(student_answer, model_answer) # Adjust the weight of keyword similarity combined_similarity = (0.9 * semantic_similarity + 0.1 * keyword_similarity) print(f"Combined similarity score: {combined_similarity}") grade = get_grade(combined_similarity) feedback = f"Student's answer: {student_answer}\nTeacher's answer: {model_answer}" badge = assign_badge(grade) detailed_feedback_msg = detailed_feedback(combined_similarity) prompt = f"The student got grade: {grade} when the student's answer is: {student_answer} and the teacher's answer is: {model_answer}. Justify the grade given to the student." return grade, combined_similarity * 100, feedback, badge, detailed_feedback_msg, prompt async def gradio_interface(image, languages: List[str], model_answer="The process of photosynthesis helps plants produce glucose using sunlight.", prompt="", history=[]): grade, similarity_score, feedback, badge, detailed_feedback_msg, prompt = evaluate_answer(image, languages, model_answer) response = "" async for result in chat_groq(prompt, history): response = result return grade, similarity_score, feedback, badge, detailed_feedback_msg, response language_choices = pytesseract.get_languages() interface = gr.Interface( fn=gradio_interface, inputs=[ gr.Image(type="filepath", label="Input"), gr.CheckboxGroup(language_choices, type="value", value=['eng'], label='Language'), gr.Textbox(lines=2, placeholder="Enter your model answer here", label="Model Answer"), gr.Textbox(lines=2, placeholder="Enter your prompt here", label="Prompt") ], outputs=[ gr.Text(label="Grade"), gr.Number(label="Similarity Score (%)"), gr.Text(label="Feedback"), gr.Text(label="Badge"), gr.JSON(label="Detailed Feedback"), gr.Text(label="Generated Response") ], title="Enhanced Automated Grading System", description="Upload an image of your answer sheet to get a grade from 1 to 5, similarity score, visual feedback, badge, and detailed feedback based on the model answer.", live=True ) if __name__ == "__main__": interface.queue() interface.launch()