dimhold.by
← Статьи

Я задал своим заметкам 15 вопросов без ответа и получил 13 ответов

Этот блог ближе всего к тому, что у меня можно назвать заметками: 16 постов с 2011 по 2014 год. Когда мне нужно оттуда число, например сколько заняла вставка 10 миллионов случайных uuid, я открываю пост и ищу по странице. Это работает, если я помню, в каком это посте.

Retrieval augmented generation, идея Льюиса и соавторов 2020 года, должна решать ровно эту задачу. Находишь абзацы, которые выглядят подходящими, отдаёшь их модели вместе с вопросом и она пишет ответ. Я хотел посмотреть, что она делает на моих собственных текстах.

Перед первым прогоном я записал, чего жду. Поиск потеряет больше ответов, чем модель, которая читает. На вопросах, ответа на которые в заметках нет, обе модели всё равно ответят, почти каждый раз. Большую часть вопросов без ответа я взял из раздела в конце почти каждого поста, “What I did not check”. Я писал эти разделы как списки того, чего не знаю. И считал, что машина прочитает их так же.

Как устроен замер

Заметки это английские версии 16 постов, разрезанные на 383 абзаца. До первого прогона я написал 75 вопросов. У 60 ответ в постах есть. Для каждого из них я отметил тот абзац, где он лежит. У 15 ответа нет. 12 спрашивают о том, что посты перечисляют как непроверенное, например как тест с uuid прошёл бы на InnoDB. 1 спрашивает о детали, которую я нигде не записал, версию PostgreSQL. 2 про темы, о которых я не писал вообще.

Вопросы я писал сразу после того, как перечитал посты, поэтому в них мои же слова. Чтобы увидеть, насколько это помогает, я разделил результаты по медиане доли слов вопроса, которые есть в нужном абзаце. Медиана 0,5.

Для поиска я взял BM25 из rank_bm25 0.2.2, он считает общие слова. Рядом 2 модели sentence-transformers прошлого августа, all-MiniLM-L6-v2 и multi-qa-MiniLM-L6-cos-v1, они сравнивают смысл как векторы. Четвёртый вариант сливает список BM25 со списком второй векторной модели через reciprocal rank fusion. Работа по воспроизведению DPR в прошлом году показала, что смесь оценок BM25 и плотного поиска лучше одного плотного. Я смешиваю места в списке вместо оценок.

Для ответа я взял UnifiedQA-v2 на T5-large, которую AllenAI выпустил в феврале и обучил отвечать по поданному ему тексту. Второй моделью должна была стать OPT-1.3B, которую Meta выложила в этом месяце как открытую альтернативу GPT-3. Всё считается на процессоре жадным декодированием, на transformers 4.19.2, sentence-transformers 2.2.0 и torch 1.11. У машины 4 ядра и 8 ГБ памяти, из них свободно около 4,5 ГБ, остальное держат другие сервисы.

Эта память и стала первой проблемой. torch 1.11 не смог поднять T5-large через from_pretrained в свободную память, ядро убило процесс. Я разрезал веса на куски и загрузил их по одному. OPT-1.3B во float32 это 5,3 ГБ весов, больше свободного. В bfloat16 она помещается, но один ответ в 40 токенов на вопрос вообще без заметок занял 9 минут. Так второй моделью стала OPT-350m из того же релиза, во float32.

Поиск находит пост, но не тот абзац

С гибридом нужный абзац попадает в тройку для 40 вопросов из 60. Один BM25 даёт 38, 2 векторные модели 38 и 39. Это меньше, чем я ждал, но промахи близкие. С гибридом нужный пост в тройке для всех 60. В 13 промахах из 20 в тройке лежит абзац в 1 или 2 абзацах от нужного.

нужный абзац в тройке, по доле общих слов вопроса и абзаца общих слов половина и больше (32) BM25 28/32 all-MiniLM 25/32 multi-qa 24/32 гибрид 26/32 общих слов меньше половины (28) 10/28 13/28 15/28 14/28
60 вопросов с ответом, разделённые по медиане доли общих слов с нужным абзацем.

Когда у вопроса с абзацем общая хотя бы половина слов, BM25 находит 28 из 32, векторные модели 24 или 25. Ниже этой границы BM25 падает до 10 из 28, а вторая векторная модель даёт 15. Векторы помогают там, где я искал бы не тем словом, но в среднем они не лучше.

Я попробовал одно очевидное улучшение. Я дописал заголовок поста к каждому абзацу перед индексацией. Гибрид сдвинулся с 40 на 39, это шум. Заголовок плюс предыдущий абзац уронили его до 35. Думаю, абзацы одного поста начинают походить друг на друга.

Модель читает нормально, если ей дать нужный абзац

UnifiedQA с нужным абзацем отвечает верно на 52 вопроса из 60. С тройкой из гибрида на 34, совсем без контекста на 2.

После поиска она ошибается на 26 вопросах. В 18 из них нужного абзаца в тройке не было. Из 20 вопросов, где поиск промахнулся, модель всё равно ответила верно на 2. В обоих случаях тот же факт есть в посте ещё в одном месте, причём в одном из них только в заголовке. Значит, большая часть провалов это провалы поиска, как я и ждал.

60 вопросов с ответом, UnifiedQA дан нужный абзац 52 8 тройка из поиска 32 8 2 18 абзац в тройке: 40 абзаца в тройке нет: 20 без контекста 2 58 верно неверно
Из 26 неверных ответов после поиска 18 приходятся на вопросы, где поиск не вернул нужный абзац.

6 из 8 провалов там, где поиск нужный абзац вернул, это неверное число из того самого текста, который ей дали. Я спросил, сколько запросов из пачки в 300 пропустит ведро ёмкостью 10. Она ответила 90, а это число запросов, которые ведро отбило. Я спросил, через сколько раундов медиана стала гулять меньше чем на 3 процента. Она ответила 30. Это последняя строка списка, а нужна была первая, где медиана уже укладывалась в 3 процента.

OPT-350m здесь вообще не годится в читатели. С нужным абзацем она даёт 20 из 60 по автоматической проверке. Как минимум половина из них это длинный кусок абзаца, переписанный слово в слово, с нужным числом где-то внутри. С тройкой из поиска 7. В промпте было сказано отвечать “not in my notes”, если в заметках ответа нет. Она не сделала этого ни разу. Однажды она случайно оказалась близко: на вопрос про InnoDB переписала мою строку “I have not run this on InnoDB”. Дальше речь только о UnifiedQA.

13 ответов на 15 вопросов без ответа

Из 15 вопросов, ответа на которые в заметках нет, UnifiedQA отказалась отвечать на 2. Остальные 13 получили короткий уверенный ответ.

В 12 случаях из 15 в тройке был абзац о том, чего я не проверял. 11 из них из раздела “What I did not check”. 5 ответов списаны прямо из таких абзацев. На “How did BigDecimal handle currencies with 3 decimals?” модель ответила “splitting one amount across several invoice lines”. Взято это из строки “Still open: currencies with 3 decimals. Splitting one amount across several invoice lines…”. Это список открытых вопросов. Модель ответила следующим пунктом.

На “What does the load average show inside a container?” пришло “the load average belongs to the host, while the limits belong to the cgroup”. Это почти дословно моя фраза из абзаца, который начинается с “Whether” и кончается словами “I expect that to be its own kind of misleading”. Этот абзац в посте стоит внутри списка того, что я не проверял.

Ещё 5 ответов это число из другого абзаца тройки, который вообще про другое. На “How much slower were random uuid inserts on InnoDB?” пришло 172414,407 ms. Это число PostgreSQL из того же поста, с запятой, которую напечатал psql. Последних 3 ответов в этих 3 абзацах нет совсем. На вопрос, сколько живёт недостижимый коммит git до prune, модель ответила “15 minutes”. Откуда это, я не знаю, в моих постах такого нет.

15 вопросов, ответа на которые в заметках нет, UnifiedQA на тройке 2 отказа 5 взято из абзаца "What I did not check" 5 число из другого абзаца тройки 3 в 3 абзацах этого нет вообще
Точка значит, что в тройке был абзац о том, чего я не проверял. В 11 случаях из 12 он из раздела "What I did not check".

Лучший косинус второй векторной модели у вопросов без ответа ниже, медиана 0,56 против 0,66, но 2 группы перекрываются. Лучший единый порог оставляет 59 из 60 хороших вопросов и отсекает только 5 из 15 плохих. По оценкам BM25 тот же приём отсекает не больше 1.

Чего я не проверял

Модели крупнее, в том числе GPT-3. На 18 вопросах, где поиск не вернул абзац, более сильная модель может только угадывать.

Вопросы, написанные кем-то другим или мной через год. Деление по общим словам показывает направление, но эту проверку не заменяет.

Реранкер, другой размер абзацев, другие промпты. И это один прогон на 75 вопросах, так что разница в 1 или 2 вопроса между методами ничего не значит.

Первое, что я поменяю, это сами посты. Разделам с открытыми вопросами нужна метка, по которой поиск сможет их пропустить. Или метка, которая скажет модели, что она читает список того, чего я не знаю. Ни то, ни другое я ещё не пробовал.