Привет! Вы когда-нибудь задумывались, почему некоторые современные приложения кажутся волшебно умными? Большая часть этой магии исходит от чего-то под названием RAG и LLM. Думайте о RAG (Retrival-Augmented Generation) как о мозговитом книжном черве в мире искусственного интеллекта. Он просматривает тонны информации, чтобы найти именно то, что нужно для вашего вопроса. Кроме того, есть LLM (большая языковая модель), например, знаменитая серия GPT, которая генерирует плавный ответ на основе своих впечатляющих возможностей генерации текста. Объединив эти два фактора, вы получаете ИИ, который не только умен, но и очень актуален и учитывает контекст. Это все равно, что объединить сверхбыстрого научного сотрудника с остроумным собеседником. Эта комбинация отлично подходит для чего угодно: от помощи в быстром поиске конкретной информации до чата, который кажется удивительно реальным.
Но вот в чем загвоздка: как нам узнать, действительно ли наш ИИ полезен, а не просто произносит причудливый жаргон? Вот тут-то и приходит на помощь оценка. Это важно, а не просто приятно иметь. Нам необходимо убедиться, что наш ИИ не только точен, но и актуален, полезен и не отклоняется от странных направлений. В конце концов, какой смысл в умном помощнике, если он не понимает, что вам нужно, или дает неправильные ответы?
Оценка нашего приложения RAG + LLM похожа на проверку реальности. Это говорит нам, действительно ли мы на правильном пути в создании ИИ, который будет действительно полезным, а не просто технически впечатляющим. Итак, в этом посте мы углубимся в то, как именно это сделать – обеспечить, чтобы наш ИИ на практике был таким же замечательным, как и в теории!
На этапе разработки важно мыслить в духе типичного конвейера оценки модели машинного обучения. В стандартной настройке AI/ML мы обычно работаем с несколькими наборами данных, такими как наборы данных для разработки, обучения и тестирования, и используем количественные показатели для оценки эффективности модели. Однако оценка моделей большого языка (LLM) представляет собой уникальные проблемы. Традиционные количественные показатели с трудом отражают качество результатов LLM, потому что эти модели превосходно создают язык, который одновременно разнообразен и креативен. Следовательно, сложно иметь полный набор ярлыков для эффективной оценки.
В академических кругах исследователи могут использовать тесты и оценки, такие как MMLU, для ранжирования LLM, а для оценки качества результатов LLM могут быть привлечены эксперты. Однако эти методы не легко переходят в производственную среду, где темпы разработки высоки, а практическое применение требует немедленных результатов. Речь идет не только об эффективности LLM; требования реального мира учитывают весь процесс, который включает в себя поиск данных, быстрое составление и вклад LLM. Создание контрольного теста, курируемого человеком, для каждой новой итерации системы или при внесении изменений в документы или домены непрактично. Кроме того, быстрые темпы развития отрасли не позволяют себе роскошь длительного ожидания, пока тестировщики оценят каждое обновление перед развертыванием. Таким образом, корректировка стратегий оценки, которые работают в академических кругах, для приведения их в соответствие с быстрой и ориентированной на результат производственной средой, представляет собой серьезную проблему.
Поэтому, если вы попадете в этот случай, вы можете подумать о чем-то вроде псевдооценки, предоставляемой мастером LLM. Эта оценка может отражать комбинацию показателей автоматизированной оценки и очищенной сущности человеческого суждения. Такой гибридный подход направлен на преодоление разрыва между тонким пониманием человеческих оценщиков и масштабируемым систематическим анализом машинной оценки.
Например, если ваша команда разрабатывает собственную программу LLM, обученную работе с вашей конкретной областью и данными, этот процесс обычно предполагает совместные усилия разработчиков, инженеров по оперативному реагированию и специалистов по обработке данных. Каждый участник играет решающую роль:
Девелоперы — архитекторы. Они создают структуру приложения, обеспечивая плавную интеграцию цепочки RAG + LLM и возможность легко перемещаться по различным сценариям.
Быстрые инженеры — это креативщики. Они разрабатывают сценарии и подсказки, имитирующие взаимодействие с пользователем в реальном мире. Они обдумывают «а что, если» и заставляют систему работать с широким спектром тем и вопросов.
Специалисты по данным — это стратеги. Они анализируют ответы, углубляются в данные и используют свой статистический опыт, чтобы оценить, соответствует ли производительность ИИ качеству.
Здесь важна обратная связь. Пока наш ИИ реагирует на подсказки, команда тщательно изучает каждый результат. Понял ли ИИ вопрос? Был ли ответ точным и актуальным? Может ли язык быть более плавным? Эта обратная связь затем возвращается в систему для улучшений.
Чтобы подняться на ступеньку выше, представьте себе, что вы используете мастер LLM, такой как OpenAI GPT-4, в качестве эталона для оценки вашего самостоятельно разработанного LLM. Вы стремитесь соответствовать или даже превзойти производительность серии GPT, известной своей надежностью и универсальностью. Вот как вы можете действовать:
Создание релевантного набора данных. Начните с создания набора данных, отражающего нюансы вашего домена. Этот набор данных может быть обработан экспертами или синтезирован с помощью GPT-4, чтобы сэкономить время и гарантировать, что он соответствует вашему золотому стандарту.
Определение показателей успеха: используйте сильные стороны основного LLM, чтобы помочь в определении ваших показателей. У вас есть свобода выбора показателей, которые лучше всего соответствуют вашим целям, поскольку главный LLM может решать более сложные задачи. В стандарте сообщества вы можете увидеть некоторые работы Langchain и некоторых других библиотек, таких как Ragas . У них есть некоторые показатели, такие как достоверность, запоминание контекста, точность контекста, сходство ответов и т. д.
Автоматизация конвейера оценки. Чтобы идти в ногу с быстрыми циклами разработки, создайте автоматизированный конвейер. Это позволит последовательно оценивать производительность приложения по заранее заданным показателям после каждого обновления или изменения. Автоматизируя этот процесс, вы гарантируете, что ваша оценка будет не только тщательной, но и эффективно итеративной, что позволит быстро оптимизировать и усовершенствовать ее.
Например, в следующей демонстрации я покажу вам, как автоматически оценивать различные LLM с открытым исходным кодом в простой задаче диалога по поиску документов с использованием OpenAI GPT-4.
Сначала мы используем OpenAI GPT-4 для создания синтезированного набора данных, полученного из документа, как показано ниже:
import os import json import pandas as pd from dataclasses import dataclass from langchain.chat_models import ChatOpenAI from langchain.chains import LLMChain from langchain.prompts import PromptTemplate from langchain.document_loaders import PyPDFLoader from langchain.text_splitter import CharacterTextSplitter from langchain.output_parsers import JsonOutputToolsParser, PydanticOutputParser from langchain.prompts import ChatPromptTemplate, HumanMessagePromptTemplate QA_DATASET_GENERATION_PROMPT = PromptTemplate.from_template( "You are an expert on generate question-and-answer dataset based on a given context. You are given a context. " "Your task is to generate a question and answer based on the context. The generated question should be able to" " to answer by leverage the given context. And the generated question-and-answer pair must be grammatically " "and semantically correct. Your response must be in a json format with 2 keys: question, answer. For example," "\n\n" "Context: France, in Western Europe, encompasses medieval cities, alpine villages and Mediterranean beaches. Paris, its capital, is famed for its fashion houses, classical art museums including the Louvre and monuments like the Eiffel Tower." "\n\n" "Response: {{" "\n" " \"question\": \"Where is France and what is it's capital?\"," "\n" " \"answer\": \"France is in Western Europe and it's capital is Paris.\"" "\n" "}}" "\n\n" "Context: The University of California, Berkeley is a public land-grant research university in Berkeley, California. Established in 1868 as the state's first land-grant university, it was the first campus of the University of California system and a founding member of the Association of American Universities." "\n\n" "Response: {{" "\n" " \"question\": \"When was the University of California, Berkeley established?\"," "\n" " \"answer\": \"The University of California, Berkeley was established in 1868.\"" "\n" "}}" "\n\n" "Now your task is to generate a question-and-answer dataset based on the following context:" "\n\n" "Context: {context}" "\n\n" "Response: ", ) OPENAI_API_KEY = os.getenv("OPENAI_API_KEY") if OPENAI_API_KEY is None: raise ValueError("OPENAI_API_KEY is not set") llm = ChatOpenAI( model="gpt-4-1106-preview", api_key=OPENAI_API_KEY, temperature=0.7, response_format={ "type": "json_object" }, ) chain = LLMChain( prompt=QA_DATASET_GENERATION_PROMPT, llm=llm ) file_loader = PyPDFLoader("./data/cidr_lakehouse.pdf") text_splitter = CharacterTextSplitter(chunk_size=1000) chunks = text_splitter.split_documents(file_loader.load()) questions, answers = [], [] for chunk in chunks: for _ in range(2): response = chain.invoke({ "context": chunk }) obj = json.loads(response["text"]) questions.append(obj["question"]) answers.append(obj["answer"]) df = pd.DataFrame({ "question": questions, "answer": answers }) df.to_csv("./data/cidr_lakehouse_qa.csv", index=False)
После запуска упомянутого выше кода мы получаем в результате CSV-файл. Этот файл содержит пары вопросов и ответов, относящиеся к вводимому нами документу, а именно:
Затем мы создаем простые цепочки DocumentRetrivalQA с использованием Langchain и заменяем их несколькими LLM с открытым исходным кодом, которые работают локально через Ollama. Вы можете найти мой предыдущий урок по этому вопросу здесь.
from tqdm import tqdm from langchain.chains import RetrievalQA from langchain.chat_models import ChatOllama from langchain.vectorstores import FAISS from langchain.embeddings import HuggingFaceEmbeddings vector_store = FAISS.from_documents(chunks, HuggingFaceEmbeddings()) retriever = vector_store.as_retriever() def test_local_retrieval_qa(model: str): chain = RetrievalQA.from_llm( llm=ChatOllama(model=model), retriever=retriever, ) predictions = [] for it, row in tqdm(df.iterrows(), total=len(df)): resp = chain.invoke({ "query": row["question"] }) predictions.append(resp["result"]) df[f"{model}_result"] = predictions test_local_retrieval_qa("mistral") test_local_retrieval_qa("llama2") test_local_retrieval_qa("zephyr") test_local_retrieval_qa("orca-mini") test_local_retrieval_qa("phi") df.to_csv("./data/cidr_lakehouse_qa_retrieval_prediction.csv", index=False)
Таким образом, приведенный выше код устанавливает простую цепочку поиска документов. Эту цепочку мы реализуем на нескольких моделях, таких как Mistral, Llama2, Zephyr, Orca-mini и Phi. В результате мы добавляем пять дополнительных столбцов в наш существующий DataFrame для хранения результатов прогнозирования каждой модели LLM.
Теперь давайте определим главную цепочку, используя GPT-4 OpenAI для оценки результатов прогнозирования. В этой настройке мы рассчитаем оценку правильности, аналогичную приблизительной оценке F1, как это обычно бывает в традиционных задачах AI/ML. Для этого мы применим параллельные концепции, такие как истинные положительные результаты (TP), ложные положительные результаты (FP) и ложные отрицательные результаты (FN), определяемые следующим образом:
Используя эти определения, мы можем вычислить точность, полноту и оценку F1, используя приведенные ниже формулы:
import os import numpy as np import pandas as pd from tqdm import tqdm from langchain.chains import LLMChain from langchain.chat_models import ChatOpenAI from langchain.prompts import PromptTemplate OPENAI_API_KEY = os.getenv("OPENAI_API_KEY") if OPENAI_API_KEY is None: raise ValueError("OPENAI_API_KEY is not set") CORRECTNESS_PROMPT = PromptTemplate.from_template( """ Extract following from given question and ground truth. Your response must be in a json format with 3 keys and does not need to be in any specific order: - statements that are present in both the answer and the ground truth - statements present in the answer but not found in the ground truth - relevant statements found in the ground truth but omitted in the answer Please be concise and do not include any unnecessary information. You should classify the statements as claims, facts, or opinions with semantic matching, no need exact word-by-word matching. Question:What powers the sun and what is its primary function? Answer: The sun is powered by nuclear fission, similar to nuclear reactors on Earth, and its primary function is to provide light to the solar system. Ground truth: The sun is actually powered by nuclear fusion, not fission. In its core, hydrogen atoms fuse to form helium, releasing a tremendous amount of energy. This energy is what lights up the sun and provides heat and light, essential for life on Earth. The sun's light also plays a critical role in Earth's climate system and helps to drive the weather and ocean currents. Extracted statements: [ {{ "statements that are present in both the answer and the ground truth": ["The sun's primary function is to provide light"], "statements present in the answer but not found in the ground truth": ["The sun is powered by nuclear fission", "similar to nuclear reactors on Earth"], "relevant statements found in the ground truth but omitted in the answer": ["The sun is powered by nuclear fusion, not fission", "In its core, hydrogen atoms fuse to form helium, releasing a tremendous amount of energy", "This energy provides heat and light, essential for life on Earth", "The sun's light plays a critical role in Earth's climate system", "The sun helps to drive the weather and ocean currents"] }} ] Question: What is the boiling point of water? Answer: The boiling point of water is 100 degrees Celsius at sea level. Ground truth: The boiling point of water is 100 degrees Celsius (212 degrees Fahrenheit) at sea level, but it can change with altitude. Extracted statements: [ {{ "statements that are present in both the answer and the ground truth": ["The boiling point of water is 100 degrees Celsius at sea level"], "statements present in the answer but not found in the ground truth": [], "relevant statements found in the ground truth but omitted in the answer": ["The boiling point can change with altitude", "The boiling point of water is 212 degrees Fahrenheit at sea level"] }} ] Question: {question} Answer: {answer} Ground truth: {ground_truth} Extracted statements:""", ) judy_llm = ChatOpenAI( model="gpt-4-1106-preview", api_key=OPENAI_API_KEY, temperature=0.0, response_format={ "type": "json_object" }, ) judy_chain = LLMChain( prompt=CORRECTNESS_PROMPT, llm=judy_llm ) def evaluate_correctness(column_name: str): chain = LLMChain( prompt=CORRECTNESS_PROMPT, llm=ChatOpenAI( model="gpt-4-1106-preview", api_key=OPENAI_API_KEY, temperature=0.0, response_format={ "type": "json_object" }, ) ) key_map = { "TP": "statements that are present in both the answer and the ground truth", "FP": "statements present in the answer but not found in the ground truth", "FN": "relevant statements found in the ground truth but omitted in the answer", # noqa: E501 } TP, FP, FN = [], [], [] for it, row in tqdm(df.iterrows(), total=len(df)): resp = chain.invoke({ "question": row["question"], "answer": row[column_name], "ground_truth": row["answer"] }) obj = json.loads(resp["text"]) TP.append(len(obj[key_map["TP"]])) FP.append(len(obj[key_map["FP"]])) FN.append(len(obj[key_map["FN"]])) # convert to numpy array TP = np.array(TP) FP = np.array(FP) FN = np.array(FN) df[f"{column_name}_recall"] = TP / (TP + FN) df[f"{column_name}_precision"] = TP / (TP + FP) df[f"{column_name}_correctness"] = 2 * df[f"{column_name}_recall"] * df[f"{column_name}_precision"] / (df[f"{column_name}_recall"] + df[f"{column_name}_precision"]) evaluate_correctness("mistral_result") evaluate_correctness("llama2_result") evaluate_correctness("zephyr_result") evaluate_correctness("orca-mini_result") evaluate_correctness("phi_result") print("|====Model====|=== Recall ===|== Precision ==|== Correctness ==|") print(f"|mistral | {df['mistral_result_recall'].mean():.4f} | {df['mistral_result_precision'].mean():.4f} | {df['mistral_result_correctness'].mean():.4f} |") print(f"|llama2 | {df['llama2_result_recall'].mean():.4f} | {df['llama2_result_precision'].mean():.4f} | {df['llama2_result_correctness'].mean():.4f} |") print(f"|zephyr | {df['zephyr_result_recall'].mean():.4f} | {df['zephyr_result_precision'].mean():.4f} | {df['zephyr_result_correctness'].mean():.4f} |") print(f"|orca-mini | {df['orca-mini_result_recall'].mean():.4f} | {df['orca-mini_result_precision'].mean():.4f} | {df['orca-mini_result_correctness'].mean():.4f} |") print(f"|phi | {df['phi_result_recall'].mean():.4f} | {df['phi_result_precision'].mean():.4f} | {df['phi_result_correctness'].mean():.4f} |") print("|==============================================================|") df.to_csv("./data/cidr_lakehouse_qa_retrieval_prediction_correctness.csv", index=False)
Хорошо, теперь у нас есть простой тест для нескольких моделей. Это можно рассматривать как предварительный показатель того, как каждая модель справляется с задачей поиска документов. Хотя эти цифры дают общее представление, они — лишь начало истории. Они служат основой для понимания того, какие модели лучше извлекают точную и актуальную информацию из данного корпуса. Исходный код вы можете найти здесь .
Когда дело доходит до настройки нашего ИИ с помощью обратной связи с человеком в цикле, решающее значение имеет взаимодействие между тестировщиками-людьми и мастером LLM. Эти отношения направлены не просто на сбор обратной связи, а на создание гибкой системы искусственного интеллекта, которая адаптируется и учится на основе человеческого вклада.
Master LLM функционирует как эталон для LLM, разработанного собственными силами, и как активный участник цикла обратной связи. Он оценивает обратную связь, настраивает подсказки или параметры модели и, по сути, «учится» на взаимодействии между людьми.
Этот процесс является преобразующим. Это позволяет ИИ адаптироваться в режиме реального времени, делая его более гибким и адаптированным к сложностям человеческого языка и мыслительных процессов. Такая адаптация в реальном времени гарантирует, что кривая обучения ИИ будет крутой и непрерывной.
Благодаря этому циклу взаимодействия, обратной связи и адаптации наш ИИ становится больше, чем просто инструментом; он становится обучающимся объектом, способным совершенствоваться при каждом взаимодействии с тестировщиком-человеком. Эта модель «человек в цикле» гарантирует, что наш ИИ не застаивается, а развивается, становясь более эффективным и интуитивно понятным помощником.
Подводя итог, обратная связь с человеком в цикле — это не просто сбор человеческой информации — это создание динамичного и адаптируемого искусственного интеллекта, который может точно настроить свое поведение, чтобы лучше обслуживать пользователей. Этот итеративный процесс гарантирует, что наши приложения RAG + LLM остаются на переднем крае, предоставляя не просто ответы, но и контекстно-ориентированные, детализированные ответы, которые отражают подлинное понимание потребностей пользователя.
В качестве простой демонстрации вы можете посмотреть, как ClearML использует эту концепцию для улучшения Promptimizer, в этом видео .
Переход к этапу операции подобен переходу от генеральной репетиции к премьере. Здесь наши приложения RAG + LLM больше не являются гипотетическими объектами; они становятся активными участниками ежедневных рабочих процессов реальных пользователей. Этот этап является лакмусовой бумажкой всей подготовки и тонкой настройки, выполненной на этапе разработки.
На этом этапе наши команды — специалисты по эксплуатации, продукту и аналитики — объединяются для развертывания приложений и управления ими, гарантируя, что все, что мы создали, не только функционирует, но и процветает в реальной среде. Именно здесь мы можем рассмотреть возможность реализации стратегий A/B-тестирования для контролируемого измерения эффективности наших приложений.
Платформа A/B-тестирования. Мы разделили нашу пользовательскую базу на два сегмента: контрольный сегмент, который продолжает использовать установленную версию приложения (Версия 1), и тестовый сегмент, который тестирует новые функции Версии 2 (на самом деле вы также можете запустить несколько A/B-тестов одновременно). Это позволяет нам собирать сравнительные данные о пользовательском опыте, восприимчивости функций и общей производительности.
Оперативное развертывание. Операционной группе поручено обеспечить плавное развертывание обеих версий, гарантируя надежность инфраструктуры и плавность перехода между версиями для пользователя.
Эволюция продукта. Команда разработчиков продукта, держа руку на пульсе отзывов пользователей, работает над итерацией продукта. Эта команда следит за тем, чтобы новые функции соответствовали потребностям пользователей и общему видению продукта.
Аналитическая информация: команда аналитиков тщательно изучает данные, собранные в ходе A/B-тестирования. Их мнение имеет решающее значение для определения того, превосходит ли новая версия старую и готова ли она к более широкому выпуску.
Показатели производительности: ключевые показатели эффективности (KPI) отслеживаются для измерения успеха каждой версии. К ним относятся показатели вовлеченности пользователей, оценки удовлетворенности и точность результатов приложения.
Этап эксплуатации динамичен и основан на непрерывной обратной связи, которая не только улучшает приложения, но и повышает вовлеченность и удовлетворенность пользователей. Это этап, характеризующийся мониторингом, анализом, итерациями и, прежде всего, обучением на реальных данных.
На этом этапе наша цель — не только поддерживать высокие стандарты, установленные на этапе разработки, но и превосходить их, гарантируя, что наше приложение RAG + LLM останется на переднем крае инноваций и удобства использования.
Таким образом, интеграция расширенной генерации данных (RAG) и моделей большого языка (LLM) знаменует собой значительный прогресс в области искусственного интеллекта, сочетающий глубокий поиск данных со сложной генерацией текста. Однако нам нужен правильный и эффективный метод оценки и стратегия итеративной разработки. На этапе разработки особое внимание уделяется настройке оценки ИИ и ее усовершенствованию за счет обратной связи с человеком, гарантируя, что эти системы будут чуткими и адаптируемыми к реальным сценариям. Этот подход подчеркивает эволюцию ИИ из простого инструмента в партнера по сотрудничеству. На этапе эксплуатации эти приложения тестируются в реальных сценариях с использованием таких стратегий, как A/B-тестирование и непрерывные циклы обратной связи, чтобы гарантировать эффективность и постоянное развитие на основе взаимодействия с пользователем.