
Привет, Хабр! Я Наталья Тяжова, бакалавр ПМИ ФКН, NLP-исследователь. В VK занималась проектом про фактологическую точность LLM. А после выступила на DataFest с докладом, который лёг в основу этой статьи.
В статье расскажу про FActScore-turbo — инструмент, который призван улучшить фактологическую точность больших языковых моделей. Я пришла к этой теме, когда занималась исследованием в VK. Мы изучали природу галлюцинаций языковых моделей — ложных или некорректных фактов, которые они выдают с высокой уверенностью. Примеры таких ошибок повсюду, и если вы работаете с LLM, то наверняка сталкивались с ними.
FActScore-turbo — одна из попыток научить модель чаще генерировать правдивые, непротиворечивые и подтверждаемые утверждения. Идея мощная, но реализация... скажем, нестандартная. Расскажу всё по порядку.
Галлюцинации LLM
Галлюцинации языковых моделей — это ложные или вымышленные утверждения, которые модель генерирует с высокой уверенностью. Чтобы лучше понять, что именно они из себя представляют, рассмотрим примеры.

Один из распространённых типов галлюцинаций — устаревшая информация. Например, в 2024 году в ходе исследования модель утверждала, что президент США — Дональд Трамп. Это утверждение было правильным до 2020 года, но в 2024 оно уже неактуально.

Другой пример галлюцинаций — излишне категоричные утверждения. В том же исследовании модель заявляла о том, что единственным способом сбросить лишний вес являются занятия спортом. Хотя понятно, что нужно просто придерживаться дефицита калорий, а как именно вы его достигаете — это уже ваше дело.

Есть и более коварные галлюцинации — фиктивные ссылки. Модель либо указывает ссылку на источник, которого не существует, либо приписывает реальным источникам сведения, которых там на самом деле нет. Например, в том же исследовании модель попросили перечислить 10 книг по социально-когнитивной теории, и под номером 8 она указала книгу, которую буквально придумала.
Масштабы проблемы

В 2024 году всё в том же исследовании было протестировано 11 языковых моделей на датасете по биомедицине, финансам, образованию и open-domain вопросам. Анализировали две метрики: Macro-Hallucination Rate (MaHR), измеряющую долю генераций хотя бы с одной ошибкой; и Micro-Hallucination Rate (MiHR), оценивающую долю ложных фактов среди всех сгенерированных.
Результаты однозначны: закрытые модели показали значительно меньшую склонность к галлюцинациям по сравнению с open-source решениями. Среди LLM одного семейства (например, Llama-2-Chat) более крупные модели работали точнее. Но даже лучшие системы не избавились полностью от проблемы. Например, у ChatGPT в биомедицине MiHR составил 4 %, что означает, что 4 % всех фактов, предоставленных моделью, оказались ложными. В финансах показатель увеличился до 6 %, а каждая четвёртая генерация содержала хотя бы одну неточность.
Почему LLM галлюцинируют?
Есть четыре ключевые причины:
Качество обучающих данных. Модели учатся на гигантских массивах информации, и даже при тщательной фильтрации в них остаются конспирология, устаревшие сведения и случайные ошибки.
Устаревание информации. Обученные в 2023 году модели могут выдавать неактуальные факты уже в 2025, особенно в быстро меняющихся областях, таких как технологии или политика.
Авторегрессионная генерация. Если модель случайно сделает неверный выбор в нескольких токенах, то ошибка распространится дальше, ухудшая всю генерацию.
Ограниченное количество параметров яу маленьких моделей. Из-за относительно небольшого количества параметров такие модели запоминают меньше информации и могут игнорировать детали. Поскольку на этапе pre-train никто не учит модель говорить «я не знаю», нередко могут возникать ситуации, когда модель вынуждена буквально придумывать ответ, ибо в её весах не содержится информация по теме.
Авторы статьи от 2025 года доказывают, что галлюцинации в LLM неизбежны с точки зрения статистической теории обучения. Поэтому нужны новые подходы, позволяющие минимизировать проблему.
Один из них — это сделать то, что мы попробовали реализовать: дообучить модель на фактологическую точность. Такое дообучение, как и любой alignment, требует значительно меньше данных, чем pre-train, а значит, гораздо проще их фильтровать и следить за их качеством. Такой метод позволяет не только сократить количество ошибок, но и развить способность корректно признавать нехватку информации. Если модель не обладает нужными данными, то она должна прямо сказать: «Простите, я не знаю». В любом сценарии обучения важно проверять тексты и генерации на достоверность. К тому же, даже вне этого сетапа, было бы полезно уметь автоматически оценивать правдивость генераций. Для этого необходим надёжный автоматический инструмент верификации фактов.
Базовый FActScore

Так в нашей истории появляется FActScore. Его идея заключается в том, чтобы оценивать фактологическую точность генерации, сравнивая содержащиеся в ней факты с проверенной базой данных. В оригинальной реализации таким источником для проверки служит Википедия.

Как он работает? На вход подаём текст генерации и её тему. Сначала извлекаем из этого текста все факты. Затем ищем по заголовкам подходящие статьи, связанные с темой генерации. В каждой найденной статье выделяем релевантные фрагменты. После этого происходит верификация: отправляем эти фрагменты в стороннюю LLM и спрашиваем, поддерживаются ли факты предоставленной информацией. Когда процедура завершена, вычисляем итоговую оценку генерации — долю фактов, получивших подтверждение.
Этот инструмент выглядел логичным и удобным, поэтому мы решили встроить его в процесс обучения. Однако, попытавшись это сделать, мы внезапно столкнулись с серьёзными проблемами:
Он работал неприемлемо медленно: обрабатывал всего две-три генерации в минуту. Запуск обучения на небольшом датасете в 10 тысяч примеров занял бы примерно 65 часов.
Инструмент часто просто не находил нужную информацию в базе данных. На этапе поиска подходящих статей возникали ошибки, и генерация оставалась без оценки.
Даже когда данные находились, оценки получались странными. FActScore необоснованно занижал правдивость фактов. Например, как показано во фрагменте кода, я взяла случайное предложение с Википедии, слегка перефразировала его и отправила на проверку. В ответ получила оценку «ноль», что явно указывало на сбой в работе инструмента.
topics = ["Car"]
generations = ["The first prototype of the modern automobile was invented in the 17th century."]
out = fs.get_score(topics, generations)
print(out["init_score"]) # -> 0.0
Стало понятно, что в таком виде он не подходит для обучения, поэтому нам пришлось его серьёзно модифицировать.
FActScore-turbo: исправляем ошибки оригинала
Так появился FActScore-turbo. Первым делом мы устранили проблему медлительности, добавив асинхронную обработку запросов и батчинг, благодаря чему генерации стали обрабатываться параллельно. Это ускорило процесс в 45 раз: если раньше система справлялась с 2,7 генерации в минуту, то теперь этот показатель составил 95,7. И это ещё с батчем размером 128 — увеличив его, можно добиться ещё большей скорости, поскольку все генерации внутри батча обрабатываются асинхронно.
Следующий важный этап — борьба с занижением оценок. Ранее FActScore зависел от заданной темы, то есть генерация подавалась вместе с темой, которую указывал пользователь. Однако эту тему легко указать недостаточно точной, что неизбежно приведёт к ошибке в работе инструмента. Мы устранили эту зависимость: теперь поиск статьи по умолчанию осуществляется напрямую по эмбеддингам самих фактов, без привязки к теме. Если же пользователь всё-таки задал конкретную тему, тогда поиск будет происходить по соответствующим эмбеддингам.
С внедрением эмбеддингов встал вопрос эффективного поиска и хранения, поэтому мы интегрировали IVF-индекс из FAISS, что позволило значительно повысить точность самого поиска. Если в оригинальном FActScore доля релевантных статей составляла всего 39 %, то в FActScore-turbo она выросла до 78 % при заданной теме. А если искать заголовки напрямую по фактам, тогда точность достигает уже 90 %, что является достаточно серьёзным улучшением.
Кроме того, мы внесли ряд полезных изменений. Например, добавили поддержку прокси — оригинальный FActScore её не имел, а запросы шли напрямую на OpenAI API, который блокирует доступ из России. Теперь достаточно указать прокси в переменных окружения, и проблема решена. Помимо этого, реализовали логирование ошибок, чтобы пользователь мог видеть, где именно произошёл сбой.
Поработали над гибкостью настроек: теперь можно менять используемые модели и все ключевые гиперпараметры алгоритма (см. код: верхний фрагмент показывает, как использовать оригинальный FActScore, нижний — FActScore-turbo).
fs = FactScorer(model_name="retrieval+ChatGPT",
openai_key="openai_key",
data_dir=".cache2/factscore",
db_path="enwiki-20230401.db")
topics = ["Normal distribution"]
generations = ["Normal distribution has two parameters"]
out = fs.get_score(topics, generations, gamma=10)
fs = FactScorer(completions_model_name="Qwen2.5-72B-Instruct",
embeddings_model_name="text-embedding-3-small")
fs.register_knowledge_source(faiss_index="faiss.index",
data_db="enwiki-20230401.db")
generations = ["Normal distribution has two parameters"]
out = await fs.get_score(generations, k=3, n=5)
В оригинальном FActScore нигде не указывается, какая модель используется для проверки фактов, хотя это критически важно как для качества работы, так и для затрат на вычисления. В нашей же версии, FActScore-turbo, названия моделей для эмбеддингов и верификации передаются сразу в конструктор класса, что делает их выбор гораздо более гибким. В функции оценки генерации добавлены параметры k и n, которые определяют количество найденных статей и релевантных фрагментов в них.
Хотя эти улучшения заметно повысили удобство работы, главным достижением остаётся то, что мы радикально увеличили скорость обработки генераций и точность верификации фактов, сделав инструмент действительно практичным.
История одного исследования, или применение FActScore-turbo

Как только мы довели FActScore-turbo до рабочей версии, то вернулись к нашей основной задаче — снижению уровня галлюцинаций модели. Для дообучения мы выбрали instruct-версию SmolLM2 с 360 миллионами параметров, одну из лучших по качеству генерации среди всех компактных моделей. В качестве обучающего набора использовали два датасета: truthy тестирует модель на распространённых заблуждениях, а sciq оценивает продвинутые знания по химико-биологическим темам.

Для обучения использовали PPO — один из наиболее известных и применяемых алгоритмов в контексте alignment. Он работает следующим образом: берём набор запросов, отправляем их в модель и получаем ответы, которые затем передаём в модель награды. Её можно заранее обучить на своих данных, взять готовое open-source решение или использовать в качестве неё функцию или алгоритм — как в нашем случае с FActScore-turbo. Модель награды возвращала оценку качества ответа, которая далее используется в оптимизации весов обучаемой модели.
Для оценки результатов использовали четыре бенчмарка. TruthfulQA gen — классический тест на фактологическую точность, проверяющий базовые знания. FACTOR — относительно новый бенчмарк, оценивающий экспертные знания по различным областям. Дополнительно взяли два набора данных MMLU Pro: один тестировал биологию, другой — химию.

Мы измерили качество трёх моделей: SFT-бейзлайна, то есть того, с чего мы начали обучение. Чтобы понять, как были получены остальные две модели, посмотрим на формулу на картинке: RM_total — это модель награды, используемая в обучении, RM_fact — FActScore-turbo, RM_general — open-source модель награды, отвечающая за то, что LLM действительно отвечает на вопрос в полном объёме. Так вот, модель без FActScore-turbo (результаты которой на второй строке) была получена обучением только на RM_general, то есть с gamma=0
. Модель же с FActScore-turbo было получена обучением с gamma=0.9
, то есть наш инструмент вносил вклад 0,9, и 0,1 — RM_general, потому что совсем без неё обучение сводилось к генерированию одного и того же, верного с фактологической точки зрения, ответа. По результатам видно, что третья модель демонстрирует заметные улучшения по трём метрикам и сопоставимые показатели по четвёртой. Это не скачок на порядки, но всё же уверенное повышение точности.
Причины ограниченного роста показателей связаны с двумя факторами. Во-первых, модель имеет всего 360 млн параметров, поэтому непонятно, сколько знаний она физически могла усвоить. Во-вторых, PPO — алгоритм с высокой чувствительностью к гиперпараметрам, поэтому возможна ситуация, что мы не успели их доподбирать и используемые нами во время обучения гиперпараметры были не самыми оптимальными.
Заключение
Проблема фактологической точности больших языковых моделей остаётся актуальной, и её решение требует инструментов, способных достаточно точно оценивать достоверность генераций. Именно для этого мы разработали FActScore-turbo — open-source инструмент для верификации фактов в генерациях LLM на основе пользовательской базы данных.
Тема фактологической точности LLM заслуживает большего внимания. Исследования появляются, новые бенчмарки вводятся, но развитие этой области всё ещё идёт достаточно медленными темпами. Надеюсь, этот инструмент внесёт свой вклад и поможет улучшить ситуацию.