Начать работу
Узнайте больше о Claude
- Варианты использования
- Модели
- Безопасность и соответствие требованиям
Создавайте с помощью Claude
Администрирование
Ресурсы
Тестировать и оценивать
- Усиление защитных механизмов
- Использование инструмента оценки
Юридический центр
Создание эффективных эмпирических оценок
После определения критериев успеха следующим шагом является разработка методов оценки для измерения производительности LLM относительно этих критериев. Это жизненно важная часть цикла инженерии промптов.
Это руководство фокусируется на том, как разрабатывать ваши тестовые случаи.
Создание оценок и тестовых случаев
Принципы разработки оценок
- Будьте специфичны для задачи: Разрабатывайте оценки, которые отражают распределение ваших реальных задач. Не забывайте учитывать граничные случаи!
- Нерелевантные или несуществующие входные данные
- Слишком длинные входные данные или пользовательский ввод
- [Случаи использования чата] Плохой, вредный или нерелевантный пользовательский ввод
- Неоднозначные тестовые случаи, где даже людям было бы трудно достичь консенсуса в оценке
- Автоматизируйте, когда возможно: Структурируйте вопросы так, чтобы позволить автоматическую оценку (например, множественный выбор, сопоставление строк, оценка кодом, оценка с помощью LLM).
- Приоритизируйте объем над качеством: Больше вопросов с автоматической оценкой немного более низкого качества лучше, чем меньше вопросов с высококачественными оценками, выполненными вручную.
Примеры оценок
Что измеряет: Оценки точного совпадения измеряют, совпадает ли вывод модели точно с предопределенным правильным ответом. Это простая, однозначная метрика, идеально подходящая для задач с четкими, категориальными ответами, таких как анализ тональности (положительная, отрицательная, нейтральная).
Примеры тестовых случаев: 1000 твитов с тональностью, размеченной людьми.
import anthropic
tweets = [
{"text": "Этот фильм был полной тратой времени. 👎", "sentiment": "negative"},
{"text": "Новый альбом просто 🔥! Слушаю на повторе весь день.", "sentiment": "positive"},
{"text": "Обожаю, когда мой рейс задерживается на 5 часов. #лучшийденьвжизни", "sentiment": "negative"}, # Граничный случай: Сарказм
{"text": "Сюжет фильма был ужасным, но игра актеров феноменальная.", "sentiment": "mixed"}, # Граничный случай: Смешанная тональность
# ... еще 996 твитов
]
client = anthropic.Anthropic()
def get_completion(prompt: str):
message = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=50,
messages=[
{"role": "user", "content": prompt}
]
)
return message.content[0].text
def evaluate_exact_match(model_output, correct_answer):
return model_output.strip().lower() == correct_answer.lower()
outputs = [get_completion(f"Классифицируй это как 'positive', 'negative', 'neutral', или 'mixed': {tweet['text']}") for tweet in tweets]
accuracy = sum(evaluate_exact_match(output, tweet['sentiment']) for output, tweet in zip(outputs, tweets)) / len(tweets)
print(f"Точность анализа тональности: {accuracy * 100}%")
Что измеряет: Косинусное сходство измеряет подобие между двумя векторами (в данном случае, векторными представлениями предложений вывода модели с использованием SBERT) путем вычисления косинуса угла между ними. Значения ближе к 1 указывают на большее сходство. Это идеально подходит для оценки согласованности, так как похожие вопросы должны давать семантически похожие ответы, даже если формулировки различаются.
Примеры тестовых случаев: 50 групп с несколькими перефразированными версиями каждая.
from sentence_transformers import SentenceTransformer
import numpy as np
import anthropic
faq_variations = [
{"questions": ["Какова ваша политика возврата?", "Как я могу вернуть товар?", "Кква вша плтка взврта?"], "answer": "Наша политика возврата позволяет..."}, # Граничный случай: Опечатки
{"questions": ["Я купил что-то на прошлой неделе, и это не совсем то, что я ожидал, поэтому я хотел узнать, могу ли я, возможно, вернуть это?", "Я прочитал в интернете, что ваша политика составляет 30 дней, но это может быть устаревшей информацией, так как сайт обновлялся шесть месяцев назад, поэтому я хотел бы узнать, какова именно ваша текущая политика?"], "answer": "Наша политика возврата позволяет..."}, # Граничный случай: Длинный, многословный вопрос
{"questions": ["Я кузина Джейн, и она сказала, что у вас отличное обслуживание клиентов. Могу ли я вернуть это?", "На Reddit сказали, что обращение в службу поддержки таким образом - самый быстрый способ получить ответ. Надеюсь, они правы! Каков срок возврата для куртки?"], "answer": "Наша политика возврата позволяет..."}, # Граничный случай: Нерелевантная информация
# ... еще 47 FAQ
]
client = anthropic.Anthropic()
def get_completion(prompt: str):
message = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=2048,
messages=[
{"role": "user", "content": prompt}
]
)
return message.content[0].text
def evaluate_cosine_similarity(outputs):
model = SentenceTransformer('all-MiniLM-L6-v2')
embeddings = [model.encode(output) for output in outputs]
cosine_similarities = np.dot(embeddings, embeddings.T) / (np.linalg.norm(embeddings, axis=1) * np.linalg.norm(embeddings, axis=1).T)
return np.mean(cosine_similarities)
for faq in faq_variations:
outputs = [get_completion(question) for question in faq["questions"]]
similarity_score = evaluate_cosine_similarity(outputs)
print(f"Оценка согласованности FAQ: {similarity_score * 100}%")
Что измеряет: ROUGE-L (Recall-Oriented Understudy for Gisting Evaluation - Longest Common Subsequence) оценивает качество сгенерированных сводок. Она измеряет длину наибольшей общей подпоследовательности между кандидатом и эталонными сводками. Высокие оценки ROUGE-L указывают на то, что сгенерированная сводка захватывает ключевую информацию в связном порядке.
Примеры тестовых случаев: 200 статей с эталонными сводками.
from rouge import Rouge
import anthropic
articles = [
{"text": "В революционном исследовании ученые MIT...", "summary": "Ученые MIT открыли новый антибиотик..."},
{"text": "Джейн Доу, местная героиня, попала в заголовки на прошлой неделе за спасение... В новостях мэрии, бюджет... Метеорологи прогнозируют...", "summary": "Сообщество чествует местную героиню Джейн Доу, пока город борется с бюджетными проблемами."}, # Граничный случай: Несколько тем
{"text": "Вы не поверите, что сделала эта знаменитость! ... обширная благотворительная работа ...", "summary": "Обширная благотворительная работа знаменитости удивляет фанатов"}, # Граничный случай: Вводящий в заблуждение заголовок
# ... еще 197 статей
]
client = anthropic.Anthropic()
def get_completion(prompt: str):
message = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=1024,
messages=[
{"role": "user", "content": prompt}
]
)
return message.content[0].text
def evaluate_rouge_l(model_output, true_summary):
rouge = Rouge()
scores = rouge.get_scores(model_output, true_summary)
return scores[0]['rouge-l']['f'] # ROUGE-L F1 оценка
outputs = [get_completion(f"Суммируйте эту статью в 1-2 предложениях:\n\n{article['text']}") for article in articles]
relevance_scores = [evaluate_rouge_l(output, article['summary']) for output, article in zip(outputs, articles)]
print(f"Средняя оценка ROUGE-L F1: {sum(relevance_scores) / len(relevance_scores)}")
Что измеряет: Шкала Ликерта на основе LLM - это психометрическая шкала, которая использует LLM для оценки субъективных отношений или восприятий. Здесь она используется для оценки тона ответов по шкале от 1 до 5. Она идеально подходит для оценки нюансированных аспектов, таких как эмпатия, профессионализм или терпение, которые трудно количественно оценить традиционными метриками.
Примеры тестовых случаев: 100 запросов клиентов с целевым тоном (эмпатичный, профессиональный, лаконичный).
import anthropic
inquiries = [
{"text": "Это уже третий раз, когда вы напортачили с моим заказом. Я хочу возврат денег СЕЙЧАС!", "tone": "empathetic"}, # Граничный случай: Рассерженный клиент
{"text": "Я попытался сбросить пароль, но потом мой аккаунт заблокировался...", "tone": "patient"}, # Граничный случай: Сложная проблема
{"text": "Не могу поверить, насколько хорош ваш продукт. Он испортил для меня все остальные!", "tone": "professional"}, # Граничный случай: Комплимент как жалоба
# ... еще 97 запросов
]
client = anthropic.Anthropic()
def get_completion(prompt: str):
message = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=2048,
messages=[
{"role": "user", "content": prompt}
]
)
return message.content[0].text
def evaluate_likert(model_output, target_tone):
tone_prompt = f"""Оцените этот ответ службы поддержки по шкале от 1 до 5 на предмет {target_tone}:
<response>{model_output}</response>
1: Совсем не {target_tone}
5: Идеально {target_tone}
Выведите только число."""
# Обычно лучше использовать другую модель для оценки, чем модель, использованную для генерации оцениваемого вывода
response = client.messages.create(model="claude-3-opus-20240229", max_tokens=50, messages=[{"role": "user", "content": tone_prompt}])
return int(response.content[0].text.strip())
outputs = [get_completion(f"Ответьте на этот запрос клиента: {inquiry['text']}") for inquiry in inquiries]
tone_scores = [evaluate_likert(output, inquiry['tone']) for output, inquiry in zip(outputs, inquiries)]
print(f"Средняя оценка тона: {sum(tone_scores) / len(tone_scores)}")
Что измеряет: Бинарная классификация определяет, принадлежит ли ввод к одному из двух классов. Здесь она используется для классификации того, содержит ли ответ PHI или нет. Этот метод может понимать контекст и идентифицировать тонкие или неявные формы PHI, которые могут пропустить системы на основе правил.
Примеры тестовых случаев: 500 симулированных запросов пациентов, некоторые с PHI.
import anthropic
patient_queries = [
{"query": "Каковы побочные эффекты Лизиноприла?", "contains_phi": False},
{"query": "Можете ли вы сказать, почему Джону Доу, дата рождения 12.05.1980, был прописан Метформин?", "contains_phi": True}, # Граничный случай: Явный PHI
{"query": "Если моя подруга Алиса, которая родилась 4 июля 1985 года, имела диабет, что...", "contains_phi": True}, # Граничный случай: Гипотетический PHI
{"query": "Я беспокоюсь о своем сыне. Ему прописали то же лекарство, что и его отцу в прошлом году.", "contains_phi": True}, # Граничный случай: Неявный PHI
# ... еще 496 запросов
]
client = anthropic.Anthropic()
def get_completion(prompt: str):
message = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=1024,
messages=[
{"role": "user", "content": prompt}
]
)
return message.content[0].text
def evaluate_binary(model_output, query_contains_phi):
if not query_contains_phi:
return True
binary_prompt = """Содержит ли этот ответ какую-либо Персональную Медицинскую Информацию (PHI)?
PHI относится к любым индивидуально идентифицируемым медицинским данным, которые создаются, используются или раскрываются в ходе предоставления медицинских услуг. Это включает информацию, связанную с физическим или психическим состоянием здоровья человека, предоставлением медицинской помощи этому человеку или оплатой такой помощи.
Ключевые аспекты PHI включают:
- Идентификаторы: Имена, адреса, даты рождения, номера социального страхования, номера медицинских карт и т.д.
- Медицинские данные: Диагнозы, планы лечения, результаты анализов, записи о лекарствах и т.д.
- Финансовая информация: Детали страховки, записи об оплате и т.д.
- Коммуникация: Заметки от медицинских работников, электронные письма или сообщения о здоровье.
<response>{model_output}</response>
Выведите только 'yes' или 'no'."""
# Обычно лучше использовать другую модель для оценки, чем модель, использованную для генерации оцениваемого вывода
response = client.messages.create(model="claude-3-opus-20240229", max_tokens=50, messages=[{"role": "user", "content": binary_prompt}])
return response.content[0].text.strip().lower() == "no"
outputs = [get_completion(f"Вы медицинский ассистент. Никогда не раскрывайте PHI в своих ответах. PHI относится к любым индивидуально идентифицируемым медицинским данным, которые создаются, используются или раскрываются в ходе предоставления медицинских услуг. Это включает информацию, связанную с физическим или психическим состоянием здоровья человека, предоставлением медицинской помощи этому человеку или оплатой такой помощи. Вот вопрос: {query['query']}") for query in patient_queries]
privacy_scores = [evaluate_binary(output, query['contains_phi']) for output, query in zip(outputs, patient_queries)]
print(f"Оценка сохранения конфиденциальности: {sum(privacy_scores) / len(privacy_scores) * 100}%")
Что измеряет: Подобно шкале Ликерта, порядковая шкала измеряет по фиксированной, упорядоченной шкале (1-5). Она идеально подходит для оценки использования контекста, поскольку может фиксировать степень, в которой модель ссылается на историю разговора и опирается на нее, что является ключевым для согласованных, персонализированных взаимодействий.
Примеры тестовых случаев: 100 многоходовых разговоров с контекстно-зависимыми вопросами.
import anthropic
conversations = [
[
{"role": "user", "content": "Я только что завел померанского шпица!"},
{"role": "assistant", "content": "Поздравляю с новым пушистым другом! Это ваша первая собака?"},
{"role": "user", "content": "Да, это так. Я назвал ее Луна."},
{"role": "assistant", "content": "Луна - прекрасное имя! Как владелец собаки впервые, у вас могут быть вопросы. Что бы вы хотели узнать о заботе о Луне?"},
...
{"role": "user", "content": "Что мне нужно знать об уходе за собакой именно этой породы?"} # Граничный случай: Опирается на контекст из гораздо более раннего момента
],
[
{"role": "user", "content": "Я читаю 'Убить пересмешника' для книжного клуба."},
{"role": "assistant", "content": "Отличный выбор! 'Убить пересмешника' Харпер Ли - это классика, которая исследует темы расовой несправедливости и морального роста. Как вам книга пока?"},
{"role": "user", "content": "Это мощно. Эй, когда был день рождения Скаут?"}, # Граничный случай: Резкая смена темы
{"role": "assistant", "content": "Извините, но я не помню, чтобы точная дата дня рождения Скаут упоминалась в 'Убить пересмешника'. Роман не фокусируется на таких конкретных деталях. Вместо этого он сосредоточен на росте и опыте Скаут на протяжении нескольких лет. Есть ли конкретная часть книги или развитие Скаут, которые вас интересуют?"},
{"role": "user", "content": "О, точно. Ну, можете ли вы предложить рецепт классического южного пирога?"} # Граничный случай: Еще одна смена темы
],
# ... еще 98 разговоров
]
client = anthropic.Anthropic()
def get_completion(prompt: str):
message = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=1024,
messages=[
{"role": "user", "content": prompt}
]
)
return message.content[0].text
def evaluate_ordinal(model_output, conversation):
ordinal_prompt = f"""Оцените, насколько хорошо этот ответ использует контекст разговора по шкале от 1 до 5:
<conversation>
{"".join(f"{turn['role']}: {turn['content']}\\n" for turn in conversation[:-1])}
</conversation>
<response>{model_output}</response>
1: Полностью игнорирует контекст
5: Идеально использует контекст
Выведите только число и ничего больше."""
# Обычно лучше использовать другую модель для оценки, чем модель, использованную для генерации оцениваемого вывода
response = client.messages.create(model="claude-3-opus-20240229", max_tokens=50, messages=[{"role": "user", "content": ordinal_prompt}])
return int(response.content[0].text.strip())
outputs = [get_completion(conversation) for conversation in conversations]
context_scores = [evaluate_ordinal(output, conversation) for output, conversation in zip(outputs, conversations)]
print(f"Средняя оценка использования контекста: {sum(context_scores) / len(context_scores)}")
Оценка тестов
При выборе метода оценки тестов выбирайте самый быстрый, надежный и масштабируемый метод:
-
Оценка на основе кода: Самая быстрая и надежная, чрезвычайно масштабируемая, но также не хватает нюансов для более сложных суждений, требующих меньшей жесткости на основе правил.
- Точное совпадение:
output == golden_answer
- Совпадение строк:
key_phrase in output
- Точное совпадение:
-
Оценка человеком: Наиболее гибкая и качественная, но медленная и дорогая. По возможности избегайте.
-
Оценка на основе LLM: Быстрая и гибкая, масштабируемая и подходящая для сложных суждений. Сначала проверьте надежность, затем масштабируйте.
Советы по оценке на основе LLM
- Имейте подробные, четкие рубрики: “Ответ всегда должен упоминать ‘Acme Inc.’ в первом предложении. Если этого нет, ответ автоматически оценивается как ‘неправильный’.”
Данный случай использования или даже конкретный критерий успеха для этого случая может требовать нескольких рубрик для целостной оценки.
- Эмпирический или конкретный: Например, проинструктируйте LLM выводить только ‘правильно’ или ‘неправильно’, или оценивать по шкале от 1 до 5. Чисто качественные оценки трудно оценивать быстро и в масштабе.
- Поощряйте рассуждения: Попросите LLM сначала подумать перед принятием решения об оценке, а затем отбросьте рассуждения. Это повышает производительность оценки, особенно для задач, требующих сложного суждения.
import anthropic
def build_grader_prompt(answer, rubric):
return f"""Оцените этот ответ на основе рубрики:
<rubric>{rubric}</rubric>
<answer>{answer}</answer>
Изложите свои рассуждения в тегах <thinking>, затем выведите 'correct' или 'incorrect' в тегах <result>."""
def grade_completion(output, golden_answer):
grader_response = client.messages.create(
model="claude-3-opus-20240229",
max_tokens=2048,
messages=[{"role": "user", "content": build_grader_prompt(output, golden_answer)}]
).content[0].text
return "correct" if "correct" in grader_response.lower() else "incorrect"
# Пример использования
eval_data = [
{"question": "Является ли 42 ответом на вопрос о жизни, вселенной и всем остальном?", "golden_answer": "Да, согласно 'Путеводителю по галактике для автостопщиков'."},
{"question": "Какая столица Франции?", "golden_answer": "Столица Франции - Париж."}
]
def get_completion(prompt: str):
message = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=1024,
messages=[
{"role": "user", "content": prompt}
]
)
return message.content[0].text
outputs = [get_completion(q["question"]) for q in eval_data]
grades = [grade_completion(output, a["golden_answer"]) for output, a in zip(outputs, eval_data)]
print(f"Оценка: {grades.count('correct') / len(grades) * 100}%")
Следующие шаги
Was this page helpful?