Spaces:
Sleeping
Sleeping
Create evaluate_creativity.py
Browse files- evaluate_creativity.py +237 -0
evaluate_creativity.py
ADDED
@@ -0,0 +1,237 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import re
|
2 |
+
import time
|
3 |
+
import pandas as pd
|
4 |
+
import numpy as np
|
5 |
+
import google.generativeai as genai
|
6 |
+
from typing import Dict
|
7 |
+
|
8 |
+
class EvaluationConfig:
|
9 |
+
def __init__(self, api_key: str, model_name: str = "gemini-1.5-flash", batch_size: int = 5):
|
10 |
+
self.api_key = api_key
|
11 |
+
self.model_name = model_name
|
12 |
+
self.batch_size = batch_size
|
13 |
+
|
14 |
+
class EvaluationPrompts:
|
15 |
+
@staticmethod
|
16 |
+
def get_first_check(prompt: str, response: str) -> str:
|
17 |
+
return f"""Оцените следующий ответ по шкале от 0 до 10 с подробным обоснованием:
|
18 |
+
|
19 |
+
Оригинальный запрос: {prompt}
|
20 |
+
Ответ: {response}
|
21 |
+
|
22 |
+
Критерии оценки:
|
23 |
+
1. **Креативность**: Насколько уникален и оригинален ответ?
|
24 |
+
- 0–3: Низкое качество (шаблонный, неоригинальный ответ, отсутствует творческий подход).
|
25 |
+
- 4–6: Среднее качество (частично оригинальный ответ с минимальной креативностью).
|
26 |
+
- 7–10: Высокое качество (ответ уникален, содержит нестандартные идеи и творческий подход).
|
27 |
+
|
28 |
+
2. **Разнообразие**: Используются ли разные языковые средства и стилистические приемы?
|
29 |
+
- 0–3: Низкое качество (однообразный стиль, отсутствуют вариации в языковых средствах).
|
30 |
+
- 4–6: Среднее качество (присутствует некоторое разнообразие, но в ограниченном объеме).
|
31 |
+
- 7–10: Высокое качество (используется широкий спектр языковых средств, разнообразие в стиле и подаче).
|
32 |
+
|
33 |
+
3. **Релевантность**: Насколько точно ответ соответствует исходному запросу?
|
34 |
+
- 0–3: Низкое качество (ответ не связан или слабо соответствует запросу).
|
35 |
+
- 4–6: Среднее качество (ответ в целом соответствует запросу, но содержит неточности).
|
36 |
+
- 7–10: Высокое качество (ответ полностью соответствует запросу, охватывает все его аспекты).
|
37 |
+
|
38 |
+
Требования к вашему ответу:
|
39 |
+
- Укажите числовую оценку по каждому критерию (по шкале от 0 до 10).
|
40 |
+
- Подробно объясните вашу оценку для каждого критерия, включая конкретные примеры из текста.
|
41 |
+
- Предложите возможные улучшения для повышения качества ответа.
|
42 |
+
"""
|
43 |
+
|
44 |
+
@staticmethod
|
45 |
+
def get_second_check(prompt: str, response: str) -> str:
|
46 |
+
return f"""Оцените креативность и качество следующего ответа по шкале от 0 до 10:
|
47 |
+
|
48 |
+
Запрос: {prompt}
|
49 |
+
Ответ: {response}
|
50 |
+
|
51 |
+
Оцените по трем критериям:
|
52 |
+
1. **Креативность** (0-10): оригинальность идей и уникальность подхода
|
53 |
+
2. **Разнообразие** (0-10): использование различных языковых средств и стилистических приемов
|
54 |
+
3. **Релевантность** (0-10): соответствие ответа исходному запросу
|
55 |
+
|
56 |
+
Для каждого критерия укажите конкретную оценку по шкале от 0 до 10 и аргументируйте свое решение.
|
57 |
+
"""
|
58 |
+
|
59 |
+
@staticmethod
|
60 |
+
def get_third_check(prompt: str, response: str) -> str:
|
61 |
+
return f"""Проанализируйте следующий ответ на запрос и оцените его по трем критериям:
|
62 |
+
|
63 |
+
Запрос: {prompt}
|
64 |
+
Ответ: {response}
|
65 |
+
|
66 |
+
Критерии оценки (шкала 0-10):
|
67 |
+
1. **Креативность**: {0-3} - шаблонный ответ, {4-6} - средняя оригинальность, {7-10} - высокая оригинальность и инновационность
|
68 |
+
2. **Разнообразие**: {0-3} - монотонный стиль, {4-6} - некоторое разнообразие, {7-10} - богатый язык и стилистические приемы
|
69 |
+
3. **Релевантность**: {0-3} - не соответствует запросу, {4-6} - частично соответствует, {7-10} - полностью соответствует запросу
|
70 |
+
|
71 |
+
Выставите оценку по каждому критерию и обоснуйте свое решение. Приведите конкретные примеры из текста.
|
72 |
+
"""
|
73 |
+
|
74 |
+
def parse_evaluation_scores(evaluation_text: str) -> dict:
|
75 |
+
scores = {
|
76 |
+
'Креативность': 0,
|
77 |
+
'Разнообразие': 0,
|
78 |
+
'Релевантность': 0,
|
79 |
+
'Среднее': 0
|
80 |
+
}
|
81 |
+
|
82 |
+
try:
|
83 |
+
if pd.isna(evaluation_text):
|
84 |
+
return scores
|
85 |
+
|
86 |
+
overall_patterns = [
|
87 |
+
r'\*\*Общая оценка:\*\*\s*(\d+(?:\.\d+)?)/10',
|
88 |
+
r'Общая оценка:\s*(\d+(?:\.\d+)?)/10',
|
89 |
+
r'\*\*Общий балл:\s*(\d+(?:\.\d+)?)/10'
|
90 |
+
]
|
91 |
+
|
92 |
+
for pattern in overall_patterns:
|
93 |
+
overall_match = re.search(pattern, str(evaluation_text))
|
94 |
+
if overall_match:
|
95 |
+
scores['Общая оценка'] = float(overall_match.group(1))
|
96 |
+
break
|
97 |
+
|
98 |
+
criteria_patterns = [
|
99 |
+
r'\*\*\d+\.\s+(Креативность|Разнообразие|Релевантность)\s*\((\d+(?:\.\d+)?)/10\)',
|
100 |
+
r'\*\*(Креативность|Разнообразие|Релевантность)\s*\((\d+(?:\.\d+)?)/10\)',
|
101 |
+
r'\d+\.\s+(Креативность|Разнообразие|Релевантность)\s*\((\d+(?:\.\d+)?)/10\)',
|
102 |
+
r'\*\*(Креативность|Разнообразие|Релевантность)\*\*:\s*(\d+(?:\.\d+)?)',
|
103 |
+
r'(Креативность|Разнообразие|Релевантность):\s*(\d+(?:\.\d+)?)',
|
104 |
+
r'(Креативность|Разнообразие|Релевантность)[^\d]+(\d+(?:\.\d+)?)'
|
105 |
+
]
|
106 |
+
|
107 |
+
for pattern in criteria_patterns:
|
108 |
+
criteria_matches = re.finditer(pattern, str(evaluation_text))
|
109 |
+
for match in criteria_matches:
|
110 |
+
metric = match.group(1)
|
111 |
+
score = float(match.group(2))
|
112 |
+
if scores[metric] == 0:
|
113 |
+
scores[metric] = score
|
114 |
+
|
115 |
+
main_scores = [scores[m] for m in ['Креативность', 'Разнообразие', 'Релевантность']]
|
116 |
+
valid_scores = [s for s in main_scores if s != 0]
|
117 |
+
scores['Среднее'] = sum(valid_scores) / len(valid_scores) if valid_scores else 0
|
118 |
+
|
119 |
+
except Exception as e:
|
120 |
+
print(f"Error parsing evaluation: {str(e)}\nText: {evaluation_text[:100]}...")
|
121 |
+
|
122 |
+
return scores
|
123 |
+
|
124 |
+
def evaluate_creativity(api_key: str, df: pd.DataFrame, prompt_col: str, answer_col: str,
|
125 |
+
model_name: str = "gemini-1.5-flash", batch_size: int = 5,
|
126 |
+
progress=None) -> pd.DataFrame:
|
127 |
+
config = EvaluationConfig(api_key=api_key, model_name=model_name, batch_size=batch_size)
|
128 |
+
genai.configure(api_key=config.api_key)
|
129 |
+
model = genai.GenerativeModel(config.model_name)
|
130 |
+
|
131 |
+
evaluations = []
|
132 |
+
eval_answers = []
|
133 |
+
|
134 |
+
total_batches = (len(df) + config.batch_size - 1) // config.batch_size
|
135 |
+
|
136 |
+
for i in range(0, len(df)):
|
137 |
+
if progress:
|
138 |
+
progress(i/len(df), desc=f"Evaluating creativity {i+1}/{len(df)}")
|
139 |
+
|
140 |
+
row = df.iloc[i]
|
141 |
+
|
142 |
+
try:
|
143 |
+
evaluation_prompts = [
|
144 |
+
EvaluationPrompts.get_first_check(str(row[prompt_col]), str(row[answer_col])),
|
145 |
+
EvaluationPrompts.get_second_check(str(row[prompt_col]), str(row[answer_col])),
|
146 |
+
EvaluationPrompts.get_third_check(str(row[prompt_col]), str(row[answer_col]))
|
147 |
+
]
|
148 |
+
|
149 |
+
all_scores = []
|
150 |
+
all_texts = []
|
151 |
+
|
152 |
+
for prompt_idx, prompt in enumerate(evaluation_prompts):
|
153 |
+
max_retries = 5
|
154 |
+
retry_count = 0
|
155 |
+
retry_delay = 10 # Start with 10 seconds delay
|
156 |
+
|
157 |
+
while retry_count < max_retries:
|
158 |
+
try:
|
159 |
+
evaluation = model.generate_content(prompt)
|
160 |
+
scores = parse_evaluation_scores(evaluation.text)
|
161 |
+
all_scores.append(scores)
|
162 |
+
all_texts.append(evaluation.text)
|
163 |
+
break # Success, exit the retry loop
|
164 |
+
|
165 |
+
except Exception as e:
|
166 |
+
error_message = str(e)
|
167 |
+
if "429" in error_message:
|
168 |
+
retry_count += 1
|
169 |
+
if retry_count >= max_retries:
|
170 |
+
print(f"Max retries reached for prompt {prompt_idx+1}. Skipping.")
|
171 |
+
all_scores.append({
|
172 |
+
"Креативность": 0,
|
173 |
+
"Разнообразие": 0,
|
174 |
+
"Релевантность": 0,
|
175 |
+
"Среднее": 0
|
176 |
+
})
|
177 |
+
all_texts.append(f"Error: Rate limit exceeded - {error_message}")
|
178 |
+
break
|
179 |
+
|
180 |
+
print(f"Rate limit exceeded. Retrying in {retry_delay} seconds... (Attempt {retry_count}/{max_retries})")
|
181 |
+
time.sleep(retry_delay)
|
182 |
+
# Exponential backoff
|
183 |
+
retry_delay = min(retry_delay * 2, 120) # Cap at 2 minutes
|
184 |
+
else:
|
185 |
+
print(f"Error with prompt {prompt_idx+1}: {error_message}")
|
186 |
+
all_scores.append({
|
187 |
+
"Креативность": 0,
|
188 |
+
"Разнообразие": 0,
|
189 |
+
"Релевантность": 0,
|
190 |
+
"Среднее": 0
|
191 |
+
})
|
192 |
+
all_texts.append(f"Error in evaluation: {error_message}")
|
193 |
+
break
|
194 |
+
|
195 |
+
# Calculate average scores from all successful evaluations
|
196 |
+
valid_scores = [s for s in all_scores if s.get("Среднее", 0) > 0]
|
197 |
+
if valid_scores:
|
198 |
+
final_scores = {
|
199 |
+
"Креативность": np.mean([s.get("Креативность", 0) for s in valid_scores]),
|
200 |
+
"Разнообразие": np.mean([s.get("Разнообразие", 0) for s in valid_scores]),
|
201 |
+
"Релевантность": np.mean([s.get("Релевантность", 0) for s in valid_scores])
|
202 |
+
}
|
203 |
+
final_scores["Среднее"] = np.mean(list(final_scores.values()))
|
204 |
+
else:
|
205 |
+
final_scores = {
|
206 |
+
"Креативность": 0,
|
207 |
+
"Разнообразие": 0,
|
208 |
+
"Релевантность": 0,
|
209 |
+
"Среднее": 0
|
210 |
+
}
|
211 |
+
|
212 |
+
evaluations.append(final_scores)
|
213 |
+
eval_answers.append("\n\n".join(all_texts))
|
214 |
+
|
215 |
+
except Exception as e:
|
216 |
+
print(f"Error processing row {i}: {str(e)}")
|
217 |
+
evaluations.append({
|
218 |
+
"Креативность": 0,
|
219 |
+
"Разнообразие": 0,
|
220 |
+
"Релевантность": 0,
|
221 |
+
"Среднее": 0
|
222 |
+
})
|
223 |
+
eval_answers.append("Error in evaluation")
|
224 |
+
|
225 |
+
# Add delay between rows to avoid rate limiting
|
226 |
+
time.sleep(5)
|
227 |
+
|
228 |
+
# Add a longer delay every 10 items
|
229 |
+
if (i + 1) % 10 == 0:
|
230 |
+
if progress:
|
231 |
+
progress(i/len(df), desc=f"Processed {i+1}/{len(df)} items. Taking a break to avoid rate limits...")
|
232 |
+
time.sleep(60)
|
233 |
+
|
234 |
+
score_df = pd.DataFrame(evaluations)
|
235 |
+
result_df = df.copy()
|
236 |
+
result_df['gemini_eval_answer'] = eval_answers
|
237 |
+
return pd.concat([result_df, score_df], axis=1)
|