dimhold.by
← Статьи

Flan-T5 верно прочитала 1393 строки счетов из 1413, а сложила верно 1 счёт из 400

С тех пор как 2 недели назад вышел ChatGPT, в финтехе я чаще всего слышу одну идею: пусть языковая модель проверяет документы. Берём счёт и смотрим, сходятся ли строки с итогом. У меня было своё представление об этом и я его ни разу не проверял. Я думал, что трудная часть это чтение. Модели надо найти нужное число рядом с нужным названием. Сложить после этого 4 числа казалось простым шагом.

У ChatGPT нет API, поэтому отправить ему несколько тысяч счетов я не могу. Вместо этого я запустил модель на своей машине и измерил, где она перестаёт отвечать верно.

Стенд

Модель — Flan-T5 от Google. Я взял 2 размера, base (250M параметров) и large (780M), обе вышли в октябре. Работает на 4 ядрах виртуальной машины AMD EPYC: Python 3.9, torch 1.13, transformers 4.25.1, жадное декодирование. Один вопрос занимает около 0,4 с на base и 1,5 с на large.

Скрипт сгенерировал 400 счетов с seed 2022, так что каждый правильный ответ известен заранее. Строк в каждом от 2 до 5. В половине счетов суммы целые, до 99, например PLN 45. В другой половине суммы с грошами, до 999,99, например PLN 412.35. Счёт выглядит так:

Invoice No. 55049
Seller: Marek Nowak Consulting
cable ties: PLN 9
paper reams: PLN 68
drill bits: PLN 90
desk lamps: PLN 92

По одним и тем же счетам я задал вопросы 6 видов. Для 2 из них достаточно прочитать: сумма одной строки и итог, когда в счёте есть строка Total:. Для 4 надо посчитать. 2 из них работают с 2 строками, это сумма и разница. 3-й это итог, когда строки итога нет. 4-й это произведение количества на цену для строки вида 6 x PLN 73.82. Каждый промпт состоит из документа и вопроса, а за ними идёт Answer with the amount only. Ответ верен, только если в нём ровно 1 число и это число правильное.

Чтение

Large верно прочитала 1393 суммы строк из 1413 и 388 напечатанных итогов из 400. Base справилась хуже, 1154 строки и 360 итогов. Она часто не останавливалась на сумме и выдавала ещё и следующие строки.

верных ответов из всех вопросов каждого вида base large ответ напечатан сумма строки 1154/1413 1393/1413 напечатанный итог 360/400 388/400 ответ надо посчитать сумма 2 строк 0/400 2/400 разница 2 строк 0/400 3/400 количество x цена 0/400 0/400 итог без строки итога 0/400 1/400
Одни и те же 400 счетов. Large верно ответила на 6 из 1600 вопросов с вычислением, base на 0.

Ошибки у large не случайные. Из 20 неверных ответов про сумму строки 19 состоят из одной неверной суммы. В 18 из этих 19 это сумма строки выше. 14 из этих 19 приходятся на счета из 5 строк с целыми суммами. Все 12 неверных итогов не меньше 1000. Только у таких итогов и есть разделитель тысяч. Вместо 2188,24 large ответила PLN 1,218.24, вместо 3005,03 PLN 30005.03. Итоги меньше 1000 она прочитала верно 228 раз из 228.

Вычисления

Large верно ответила на 6 из 1600 вопросов с вычислением. Base ни на один. Все 6 пришлись на счета с целыми суммами, так что даже там это 6 из 800. В 2 из 6 правильная сумма совпала с суммой другой строки счёта, 79 и 91. Large вернула это число. Сложила она его или списала, я сказать не могу.

Больше меня беспокоили неверные ответы. На вопрос про итог, когда строки итога нет, large в 201 случае из 400 вернула сумму одной из строк. Ещё в 16 она вернула номер счёта. В остальных 182 она вернула число, которого в счёте нет. Все они выглядят в точности как итог. Ответ это 1 число с PLN перед ним и парсер принимает его без ошибки. С количеством на цену было иначе. Large во всех 400 случаях просто повторила строку, 6 x PLN 73.82. У large это единственный вид вопросов, где ошибка громкая, потому что в ответе 2 числа.

Просьба показать шаги

Flan-T5 обучали на примерах с рассуждением по шагам, поэтому следующим логично было дописать к вопросам про сумму Let's think step by step. Тогда large расписывает вычисление. По счёту выше она ответила:

The total amount of all items is PLN 9 + 68 + 90 + 92 = PLN 340. The answer: 340.

Правильный итог 259. Здесь каждое число слева от = верное и неверно только сложение.

счёт cable ties: PLN 9 paper reams: PLN 68 drill bits: PLN 90 desk lamps: PLN 92 large пишет 9 + 68 + 90 + 92 = 340 ответ large то же выражение считает Python 9 + 68 + 90 + 92 = 259 верно выражение записано 398/400 собственный ответ верен 3/400 выражение верно, если его посчитает Python 223/400
По шагам на первых 200 счетах, 400 вопросов про сумму, только Flan-T5 large.

Я прогнал это только на первых 200 счетах, это 400 вопросов про сумму, потому что 1 ответ занимает около 8 секунд. Large записала выражение в 398 из них. Её собственный итоговый ответ был верен в 3. Потом я посчитал записанное ею выражение в Python. Верными оказались 223 из 400. На минуту мне показалось, что это и есть выход, но итоги всё испортили. В вопросах про итог без строки итога выражение содержало все строки счёта только в 89 случаях из 198. При 2 строках это 51 из 55, при 5 строках 8 из 43. В 99 из остальных 109 модель выбросила часть строк из суммы. В оставшихся она неверно прочитала одну из сумм или повторила её.

Читает модель, складывает код

Так я и разделил работу. Модель читает сумму каждой строки, по одному вопросу на строку, а Python их складывает. Тогда large дала 386 верных итогов из 400 против 1 из 400, когда её спрашивали про итог напрямую. В каждом из 13 неверных итогов есть неверно прочитанная строка. В 1 счёте строка вернулась с 2 числами, поэтому код отказался её складывать.

итог 400 счетов 2 способами верно неверно, тихо громко, код отказывается спросить у модели итог flan-t5-base 0/355/45 flan-t5-large 1/399/0 модель читает строки, код складывает flan-t5-base 211/18/171 flan-t5-large 386/13/1
Громко значит, что в ответе больше 1 числа и код видит, что что-то не так. Во втором способе для этого хватает 1 строки.

При таком разделении base дала 211 верных итогов и 18 неверных. В остальных 171 какая-то строка вернулась в виде PLN 9 paper reams 68, поэтому код не смог её сложить. При прямом вопросе про итог base ошиблась так же громко 45 раз, а в остальных 355 тихо.

Идея не моя. Статья прошлого месяца, PAL от Gao с соавторами, делает то же самое в общем виде. Модель пишет программу, а интерпретатор её выполняет. Модели, построенные для FinQA и TAT-QA, 2 наборов 2021 года с такими вопросами по финансовым отчётам, тоже отдают вычисление коду.

Где я бы провёл границу

Для меня граница теперь яснее. Если число, которое вернула модель, напечатано в документе, я могу хотя бы проверить, что оно там есть. Ответ со строки выше такая проверка не поймает, но это хоть что-то. Если число надо посчитать, Flan-T5 на этих счетах почти никогда не считает его верно. Она возвращает что-то правильной формы. Проверка, что ответ не является числом из документа, поймала бы 217 из 399 неверных итогов large. Остальные 182 прошли бы.

Правила для ошибок чтения у меня до сих пор нет: строка выше в длинных счетах и итоги больше 1000. Они редкие и парсер принимает их как любой верный ответ.

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

  • Модели крупнее. text-davinci-003 от OpenAI доступна через API около 2 недель. Для этого прогона мне нужна была модель на своей машине, поэтому davinci я не пробовал. Крупные модели, может быть, сложат 4 числа верно.
  • Настоящие счета. Мои синтетические, на английском, по одной строке на позицию. В них нет сканов и ошибок OCR.
  • Другие промпты. Я использовал 1 формулировку на вопрос и жадное декодирование. Примеры в промпте могли бы изменить числа, особенно у base.