-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathcodebert_evaluator.py
More file actions
310 lines (252 loc) · 9.62 KB
/
Copy pathcodebert_evaluator.py
File metadata and controls
310 lines (252 loc) · 9.62 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
"""
CodeBERT Evaluator Module
Handles CodeBERT model loading, embedding generation, and similarity calculation.
"""
import numpy as np
from typing import List, Dict, Tuple, Optional
import warnings
try:
import torch
from transformers import AutoTokenizer, AutoModel
from sklearn.metrics.pairwise import cosine_similarity
DEPENDENCIES_AVAILABLE = True
except ImportError:
DEPENDENCIES_AVAILABLE = False
warnings.warn("Required dependencies not available. Install transformers, torch, and scikit-learn.")
class CodeBERTEvaluator:
"""Evaluates code similarity using CodeBERT embeddings."""
def __init__(self, model_name: str = "microsoft/codebert-base"):
"""
Initialize CodeBERT Evaluator.
Args:
model_name: HuggingFace model name (default: microsoft/codebert-base)
"""
if not DEPENDENCIES_AVAILABLE:
raise ImportError("Required dependencies not installed. Run: pip install transformers torch scikit-learn")
self.model_name = model_name
self.tokenizer = None
self.model = None
self.device = "cuda" if torch.cuda.is_available() else "cpu"
print(f"Initializing CodeBERT Evaluator with model: {model_name}")
print(f"Using device: {self.device}")
def load_model(self):
"""Load CodeBERT model and tokenizer."""
if self.model is not None:
print("Model already loaded.")
return
print(f"Loading tokenizer and model from {self.model_name}...")
try:
self.tokenizer = AutoTokenizer.from_pretrained(self.model_name)
self.model = AutoModel.from_pretrained(self.model_name)
self.model.to(self.device)
self.model.eval() # Set to evaluation mode
print("Model loaded successfully!")
except Exception as e:
raise Exception(f"Failed to load CodeBERT model: {str(e)}")
def generate_embedding(self, code: str, max_length: int = 512) -> np.ndarray:
"""
Generate embedding for a code snippet using CodeBERT.
Args:
code: Source code string
max_length: Maximum token length (default: 512)
Returns:
Numpy array containing the code embedding
"""
if self.model is None:
self.load_model()
# Tokenize the code
inputs = self.tokenizer(
code,
return_tensors="pt",
max_length=max_length,
truncation=True,
padding="max_length"
)
# Move inputs to device
inputs = {k: v.to(self.device) for k, v in inputs.items()}
# Generate embeddings
with torch.no_grad():
outputs = self.model(**inputs)
# Use [CLS] token embedding as code representation
# Shape: (batch_size, hidden_size)
embedding = outputs.last_hidden_state[:, 0, :].cpu().numpy()
return embedding[0] # Return first (and only) embedding
def calculate_similarity(
self,
code1: str,
code2: str,
max_length: int = 512
) -> float:
"""
Calculate cosine similarity between two code snippets.
Args:
code1: First code snippet
code2: Second code snippet
max_length: Maximum token length
Returns:
Similarity score between 0 and 1
"""
# Generate embeddings for both codes
embedding1 = self.generate_embedding(code1, max_length)
embedding2 = self.generate_embedding(code2, max_length)
# Calculate cosine similarity
similarity = cosine_similarity(
embedding1.reshape(1, -1),
embedding2.reshape(1, -1)
)[0][0]
return float(similarity)
def calculate_similarity_to_reference(
self,
student_code: str,
reference_code: str,
requirements: Optional[str] = None,
max_length: int = 512
) -> Dict[str, float]:
"""
Calculate similarity between student code and reference code.
Optionally also compare against requirements.
Args:
student_code: Student's submitted code
reference_code: Reference/expected code
requirements: Optional requirements text
max_length: Maximum token length
Returns:
Dictionary with similarity scores
"""
results = {}
# Calculate student vs reference similarity
code_similarity = self.calculate_similarity(
student_code,
reference_code,
max_length
)
results['code_similarity'] = code_similarity
results['code_similarity_percent'] = code_similarity * 100
# If requirements provided, calculate semantic alignment
if requirements:
# Generate embeddings
student_emb = self.generate_embedding(student_code, max_length)
reference_emb = self.generate_embedding(reference_code, max_length)
req_emb = self.generate_embedding(requirements, max_length)
# Calculate similarities to requirements
student_req_sim = cosine_similarity(
student_emb.reshape(1, -1),
req_emb.reshape(1, -1)
)[0][0]
reference_req_sim = cosine_similarity(
reference_emb.reshape(1, -1),
req_emb.reshape(1, -1)
)[0][0]
results['student_requirements_similarity'] = float(student_req_sim)
results['reference_requirements_similarity'] = float(reference_req_sim)
results['student_requirements_similarity_percent'] = float(student_req_sim * 100)
results['reference_requirements_similarity_percent'] = float(reference_req_sim * 100)
return results
def batch_evaluate(
self,
student_codes: List[str],
reference_code: str,
max_length: int = 512
) -> List[Dict[str, float]]:
"""
Evaluate multiple student codes against a reference.
Args:
student_codes: List of student code snippets
reference_code: Reference code
max_length: Maximum token length
Returns:
List of evaluation results
"""
results = []
# Generate reference embedding once
reference_emb = self.generate_embedding(reference_code, max_length)
print(f"Evaluating {len(student_codes)} student submissions...")
for i, student_code in enumerate(student_codes, 1):
try:
# Generate student embedding
student_emb = self.generate_embedding(student_code, max_length)
# Calculate similarity
similarity = cosine_similarity(
student_emb.reshape(1, -1),
reference_emb.reshape(1, -1)
)[0][0]
results.append({
'index': i - 1,
'code_similarity': float(similarity),
'code_similarity_percent': float(similarity * 100),
'status': 'success'
})
if i % 10 == 0:
print(f"Processed {i}/{len(student_codes)} submissions")
except Exception as e:
results.append({
'index': i - 1,
'error': str(e),
'status': 'failed'
})
print(f"Batch evaluation complete!")
return results
def get_confidence_metrics(self, similarity_score: float) -> Dict[str, any]:
"""
Generate confidence metrics based on similarity score.
Args:
similarity_score: Similarity score (0-1)
Returns:
Dictionary with confidence metrics
"""
# Define thresholds
if similarity_score >= 0.9:
confidence = "Very High"
evaluation = "Excellent match"
elif similarity_score >= 0.75:
confidence = "High"
evaluation = "Good match"
elif similarity_score >= 0.6:
confidence = "Medium"
evaluation = "Moderate match"
elif similarity_score >= 0.4:
confidence = "Low"
evaluation = "Poor match"
else:
confidence = "Very Low"
evaluation = "Very poor match"
return {
'confidence_level': confidence,
'evaluation': evaluation,
'similarity_score': similarity_score,
'similarity_percent': similarity_score * 100
}
# Example usage
if __name__ == "__main__":
# Sample codes for testing
code1 = """
def calculate_sum(a, b):
return a + b
def main():
result = calculate_sum(5, 3)
print(result)
"""
code2 = """
def add_numbers(x, y):
total = x + y
return total
def run():
answer = add_numbers(5, 3)
print(answer)
"""
code3 = """
def multiply(a, b):
return a * b
"""
print("Initializing CodeBERT Evaluator...")
evaluator = CodeBERTEvaluator()
print("\nCalculating similarity between similar codes...")
sim1 = evaluator.calculate_similarity(code1, code2)
print(f"Similarity (code1 vs code2): {sim1:.4f} ({sim1*100:.2f}%)")
print("\nCalculating similarity between different codes...")
sim2 = evaluator.calculate_similarity(code1, code3)
print(f"Similarity (code1 vs code3): {sim2:.4f} ({sim2*100:.2f}%)")
print("\nGenerating confidence metrics...")
metrics = evaluator.get_confidence_metrics(sim1)
print(f"Confidence: {metrics['confidence_level']}")
print(f"Evaluation: {metrics['evaluation']}")