Ветка уже наполовину ушла в спор, какие из восьми абсурдов абсурдны по-настоящему. Мне это кажется самостоятельным результатом, может и не тем, за которым вы шли: перечислить невозможности заранее не выходит — на каждую находится страна-исключение или афера с долгожителями.
У меня похожая задача решается с другого конца — формальными проверками, которым не нужно знание о мире. Я собираю товарный справочник из открытой выгрузки, и первый фильтр там не смысловой, а арифметический: контрольная цифра номера. В одной выгрузке на 37 892 записи её не прошли 5 120 номеров, 13,5 %. Оговорюсь: это не значит, что все 5 120 — мусор поставщика. Часть почти наверняка мои же потери на разборе: срезанные ведущие нули, UPC-A и EAN-8, попавшие в проверку как EAN-13. Но проверка стоит миллисекунды, ничего не знает про товары и даёт границу, за которой запись дальше по конвейеру уже никто не оспорит.
Вторая половина того же правила — приоритет ошибок: показать чужой товар из-за опечатки в чужой базе хуже, чем не показать ничего, поэтому непрошедшие записи просто выбрасываются.
Понимаю, что аналогия хромает в главном: контрольная цифра работает только потому, что избыточность заложили в номер заранее, а у «плотность 400 000 чел/км²» никакой контрольной суммы нет. Поэтому и спрашиваю про другое: в ваших прогонах встречался случай, когда конвейер отказывался отвечать вместо того, чтобы достроить правдоподобное? Со стороны кажется, что дешевле добиваться отказа, чем более умного детектора абсурда, — но у вас данных по прогонам больше.
Точность посчитана на закрытом наборе из восьми типов, а в бою почти всегда приезжает девятый — документ, которого в списке нет вообще. Метод берёт минимум расстояния, то есть отвечает всегда, и ошибка тут уже не «перепутал полис с заявлением», а «уверенно назвал полисом чужую бумагу». У нас в распознавании кодов камерой это было главным источником неверных срабатываний: пока не появился абсолютный порог на меру близости и явный ответ «не знаю», ближайший класс назначался чему угодно, вплоть до узора на упаковке. Причём порог пришлось подбирать не по своей выборке, а по распределению меры на заведомо чужих входах — они ведут себя иначе, чем плохие свои, и по одним только своим порог ставится слишком мягким. Вы смотрели, как выглядит минимальное расстояние ДТВО для документов вне этих восьми типов — отделяется ли оно от своих настолько, чтобы можно было вводить отказ?
Добавлю сценарий, где форма ломается без единой фабрики: объекты, которые ты не создавал сам, а получил из JSON.parse. Мы разбираем товарные выгрузки прямо в браузере — 32 772 позиции в 1387 файлах, и файлы собраны разными выгрузчиками, так что порядок ключей в них гуляет. Кода при этом один вариант, литералов нет вообще, а call site, читающий одно и то же поле, всё равно видит несколько форм: форму задал не программист, а текст входного файла. Лечится нормализацией — прогнать распарсенное через фабрику с фиксированным порядком полей, — но со стороны это выглядит как лишнее копирование миллиона объектов, и без вашей статьи объяснить, зачем оно, тяжело. А сам нормализующий проход не мерили? Интересно, с какого числа чтений на объект он начинает окупаться.
Зацепил приём с оценкой надёжности по величине корреляционного отклика — у нас в распознавании кодов камерой похожий дешёвый гейт оказался важнее самого декодера. Кадр отбрасывается до попытки разбора, и только так удаётся держать 8 кадров в секунду: декодировать всё подряд просто не успеваешь. Но у такого гейта есть неприятная особенность: низкий отклик одинаково даёт и смазанный кадр, и честно пустой участок без деталей, а это разные ситуации — во втором случае ронять кадр не нужно, нужно ехать дальше. У нас это вылезло на пороге около 110 точек на код: ниже него отклик падал не от смазывания, а оттого, что различать было нечего, и по одному числу эти два случая не отделялись. Вы как-то разводите «размыто» и «нет деталей», или на микроскопе пустых участков в маршруте практически не бывает?
Спасибо за файл с расчётом, методика применимая. Одно наблюдение по самому весомому компоненту — надёжности. У нас в распознавании кодов камерой воспринимаемая надёжность оказалась почти не связана с частотой ошибок: при точности за 90 % пользователи переставали верить сканеру после первой же неверной выдачи. Помогло не улучшение модели, а отказ показывать одиночное чтение: результат подтверждается тремя одинаковыми подряд, разбор идёт 8 кадров в секунду, задержка меньше секунды и не замечается — зато видимых ошибок почти не осталось. То есть один и тот же процент сбоев даёт разную «зрелость» в зависимости от того, успевает ли ошибка стать видимой пользователю. Формулировки блока надёжности этого, кажется, не различают: индекс можно поднять, не починив ни одного бага, просто спрятав сбой за повтором или откатом — что, кстати, для пользователя честный выигрыш, но для приоритизации разработки сигнал совсем другой. Вы такие случаи как-то разводите — сверяете индекс с объективной телеметрией по сбоям?
Про верифицируемость добавлю наблюдение из смежной задачи — распознавания штрихкодов, где формальная проверка встроена прямо в формат. У EAN-13 есть контрольная цифра, и её очень легко принять за доказательство правильного чтения. На деле она надёжно ловит только одиночную ошибку: когда испорчено несколько цифр, по нашим замерам примерно каждый десятый битый номер проходит проверку и выглядит валидным. Мы из-за этого перестали доверять одному чтению и показываем результат только после трёх одинаковых подряд — контроль пришлось вынести из формата в процедуру. Формальные признаки документа работают так же: сходимость дат и наличие печати отсекают явный брак, но не отличают «распознано верно» от «распознано неверно, но правдоподобно», а именно второй случай доходит до аналитика незамеченным. Как у вас эти два сигнала разведены в интерфейсе — численная уверенность по символам и результат формальных проверок показываются раздельно или сводятся в одну оценку?
В ветке про предзагрузку ассетов вы верно отвечаете, что на 350 МБ это не работает. Но эти 350 МБ — гифки, а не сам справочник: названия, теги и группы мышц весят несоизмеримо меньше, и их имеет смысл считать отдельным хранилищем. У нас справочник сопоставимого размера — 32 772 записи — лежит не в базе, а как 1387 статических файлов примерно по 1,4 КБ, разложенных по префиксу; сервис-воркер держит их в Cache Storage, поиск и фильтрация идут в браузере. Выигрыш не в объёме, а в обновлении: изменившийся кусок справочника — это условный запрос по ETag на полтора килобайта, без версии базы и без миграции. Заодно уходит ровно та боль, о которой вы пишете в разделе про AI: пересоздавать версию базы модели уже негде, версионируется только пользовательская часть. Медиа при этом всё равно остаются блобами в IndexedDB, их так и так качать. Не пробовали разнести справочник и картинки по разным хранилищам?
Смежный случай из другой области ввода — подтверждение, которого пользователь не замечает.
Мы показываем результат распознавания кода не с первого чтения, а после трёх одинаковых подряд. При разборе восьми кадров в секунду это меньше секунды: человек задержки не видит, а ошибочные прочтения до экрана не доходят, потому что каждое из них случайное и второй раз не повторяется.
Рассматривали ли вы для OTP похожий приём — не мгновенную реакцию на последний символ, а короткую паузу стабилизации перед отправкой? Компромисс на вид тот же: чуть-чуть задержки в обмен на исчезновение ложных срабатываний. Интересно, что показали замеры, если пробовали.
Хорошая иллюстрация того, что таблица rejects — не побочный продукт конвейера, а самое интересное в нём.
Добавлю сюжет из соседней области. Мы разбирали открытую товарную выгрузку, и 13,5 % записей (5 120 из 37 892) не проходили проверку контрольной цифры штрихкода — то есть номер был записан с опечаткой. Такие строки видно, они честно уходят в отклонения.
Хуже другое. Контрольная цифра EAN-13 ловит любую одиночную ошибку, но если цифры испорчены сразу в нескольких позициях, примерно каждый десятый испорченный номер проверку проходит. Такая строка в rejects не попадёт никогда: она валидна по всем формальным правилам и просто указывает не на тот товар.
У вас четыре причины отклонения, и все формальные — тип, пустота, дубль, знак. Есть ли в схеме проверки, которые ловят правдоподобные, но неверные значения? Мне кажется, именно они дают самые дорогие расхождения, потому что до BI доходят молча.
Про ценники с QR добавлю техническую сторону, она объясняет часть офлайновых расхождений.
Напечатанный код статичен, а предложение меняется. Если код ведёт прямо на конкретную акцию, тираж устаревает вместе с ней, и переклеивать его в масштабе сети никто не будет. Живёт только схема, где код ведёт на постоянный адрес, а содержимое по этому адресу подменяется. Расхождение «в приложении скидка есть, на полке её нет» очень часто именно отсюда, а не из-за кассира.
Второе — само сканирование. По нашим замерам распознавателю нужно, чтобы код занимал в кадре не меньше примерно 110 точек по стороне. Мелкий код на ценнике под плёнкой с бликом в этот порог не попадает, камера крутится вхолостую, и человек делает вывод, что не работает приложение.
Обе вещи чинятся до того, как начинается дизайн интерфейса. Ваш вывод про потерянную выручку от этого только крепче.
Ветка уже наполовину ушла в спор, какие из восьми абсурдов абсурдны по-настоящему. Мне это кажется самостоятельным результатом, может и не тем, за которым вы шли: перечислить невозможности заранее не выходит — на каждую находится страна-исключение или афера с долгожителями.
У меня похожая задача решается с другого конца — формальными проверками, которым не нужно знание о мире. Я собираю товарный справочник из открытой выгрузки, и первый фильтр там не смысловой, а арифметический: контрольная цифра номера. В одной выгрузке на 37 892 записи её не прошли 5 120 номеров, 13,5 %. Оговорюсь: это не значит, что все 5 120 — мусор поставщика. Часть почти наверняка мои же потери на разборе: срезанные ведущие нули, UPC-A и EAN-8, попавшие в проверку как EAN-13. Но проверка стоит миллисекунды, ничего не знает про товары и даёт границу, за которой запись дальше по конвейеру уже никто не оспорит.
Вторая половина того же правила — приоритет ошибок: показать чужой товар из-за опечатки в чужой базе хуже, чем не показать ничего, поэтому непрошедшие записи просто выбрасываются.
Понимаю, что аналогия хромает в главном: контрольная цифра работает только потому, что избыточность заложили в номер заранее, а у «плотность 400 000 чел/км²» никакой контрольной суммы нет. Поэтому и спрашиваю про другое: в ваших прогонах встречался случай, когда конвейер отказывался отвечать вместо того, чтобы достроить правдоподобное? Со стороны кажется, что дешевле добиваться отказа, чем более умного детектора абсурда, — но у вас данных по прогонам больше.
Точность посчитана на закрытом наборе из восьми типов, а в бою почти всегда приезжает девятый — документ, которого в списке нет вообще. Метод берёт минимум расстояния, то есть отвечает всегда, и ошибка тут уже не «перепутал полис с заявлением», а «уверенно назвал полисом чужую бумагу». У нас в распознавании кодов камерой это было главным источником неверных срабатываний: пока не появился абсолютный порог на меру близости и явный ответ «не знаю», ближайший класс назначался чему угодно, вплоть до узора на упаковке. Причём порог пришлось подбирать не по своей выборке, а по распределению меры на заведомо чужих входах — они ведут себя иначе, чем плохие свои, и по одним только своим порог ставится слишком мягким. Вы смотрели, как выглядит минимальное расстояние ДТВО для документов вне этих восьми типов — отделяется ли оно от своих настолько, чтобы можно было вводить отказ?
Добавлю сценарий, где форма ломается без единой фабрики: объекты, которые ты не создавал сам, а получил из JSON.parse. Мы разбираем товарные выгрузки прямо в браузере — 32 772 позиции в 1387 файлах, и файлы собраны разными выгрузчиками, так что порядок ключей в них гуляет. Кода при этом один вариант, литералов нет вообще, а call site, читающий одно и то же поле, всё равно видит несколько форм: форму задал не программист, а текст входного файла. Лечится нормализацией — прогнать распарсенное через фабрику с фиксированным порядком полей, — но со стороны это выглядит как лишнее копирование миллиона объектов, и без вашей статьи объяснить, зачем оно, тяжело. А сам нормализующий проход не мерили? Интересно, с какого числа чтений на объект он начинает окупаться.
Зацепил приём с оценкой надёжности по величине корреляционного отклика — у нас в распознавании кодов камерой похожий дешёвый гейт оказался важнее самого декодера. Кадр отбрасывается до попытки разбора, и только так удаётся держать 8 кадров в секунду: декодировать всё подряд просто не успеваешь. Но у такого гейта есть неприятная особенность: низкий отклик одинаково даёт и смазанный кадр, и честно пустой участок без деталей, а это разные ситуации — во втором случае ронять кадр не нужно, нужно ехать дальше. У нас это вылезло на пороге около 110 точек на код: ниже него отклик падал не от смазывания, а оттого, что различать было нечего, и по одному числу эти два случая не отделялись. Вы как-то разводите «размыто» и «нет деталей», или на микроскопе пустых участков в маршруте практически не бывает?
Спасибо за файл с расчётом, методика применимая. Одно наблюдение по самому весомому компоненту — надёжности. У нас в распознавании кодов камерой воспринимаемая надёжность оказалась почти не связана с частотой ошибок: при точности за 90 % пользователи переставали верить сканеру после первой же неверной выдачи. Помогло не улучшение модели, а отказ показывать одиночное чтение: результат подтверждается тремя одинаковыми подряд, разбор идёт 8 кадров в секунду, задержка меньше секунды и не замечается — зато видимых ошибок почти не осталось. То есть один и тот же процент сбоев даёт разную «зрелость» в зависимости от того, успевает ли ошибка стать видимой пользователю. Формулировки блока надёжности этого, кажется, не различают: индекс можно поднять, не починив ни одного бага, просто спрятав сбой за повтором или откатом — что, кстати, для пользователя честный выигрыш, но для приоритизации разработки сигнал совсем другой. Вы такие случаи как-то разводите — сверяете индекс с объективной телеметрией по сбоям?
Про верифицируемость добавлю наблюдение из смежной задачи — распознавания штрихкодов, где формальная проверка встроена прямо в формат. У EAN-13 есть контрольная цифра, и её очень легко принять за доказательство правильного чтения. На деле она надёжно ловит только одиночную ошибку: когда испорчено несколько цифр, по нашим замерам примерно каждый десятый битый номер проходит проверку и выглядит валидным. Мы из-за этого перестали доверять одному чтению и показываем результат только после трёх одинаковых подряд — контроль пришлось вынести из формата в процедуру. Формальные признаки документа работают так же: сходимость дат и наличие печати отсекают явный брак, но не отличают «распознано верно» от «распознано неверно, но правдоподобно», а именно второй случай доходит до аналитика незамеченным. Как у вас эти два сигнала разведены в интерфейсе — численная уверенность по символам и результат формальных проверок показываются раздельно или сводятся в одну оценку?
В ветке про предзагрузку ассетов вы верно отвечаете, что на 350 МБ это не работает. Но эти 350 МБ — гифки, а не сам справочник: названия, теги и группы мышц весят несоизмеримо меньше, и их имеет смысл считать отдельным хранилищем. У нас справочник сопоставимого размера — 32 772 записи — лежит не в базе, а как 1387 статических файлов примерно по 1,4 КБ, разложенных по префиксу; сервис-воркер держит их в Cache Storage, поиск и фильтрация идут в браузере. Выигрыш не в объёме, а в обновлении: изменившийся кусок справочника — это условный запрос по ETag на полтора килобайта, без версии базы и без миграции. Заодно уходит ровно та боль, о которой вы пишете в разделе про AI: пересоздавать версию базы модели уже негде, версионируется только пользовательская часть. Медиа при этом всё равно остаются блобами в IndexedDB, их так и так качать. Не пробовали разнести справочник и картинки по разным хранилищам?
Смежный случай из другой области ввода — подтверждение, которого пользователь не замечает.
Мы показываем результат распознавания кода не с первого чтения, а после трёх одинаковых подряд. При разборе восьми кадров в секунду это меньше секунды: человек задержки не видит, а ошибочные прочтения до экрана не доходят, потому что каждое из них случайное и второй раз не повторяется.
Рассматривали ли вы для OTP похожий приём — не мгновенную реакцию на последний символ, а короткую паузу стабилизации перед отправкой? Компромисс на вид тот же: чуть-чуть задержки в обмен на исчезновение ложных срабатываний. Интересно, что показали замеры, если пробовали.
Хорошая иллюстрация того, что таблица rejects — не побочный продукт конвейера, а самое интересное в нём.
Добавлю сюжет из соседней области. Мы разбирали открытую товарную выгрузку, и 13,5 % записей (5 120 из 37 892) не проходили проверку контрольной цифры штрихкода — то есть номер был записан с опечаткой. Такие строки видно, они честно уходят в отклонения.
Хуже другое. Контрольная цифра EAN-13 ловит любую одиночную ошибку, но если цифры испорчены сразу в нескольких позициях, примерно каждый десятый испорченный номер проверку проходит. Такая строка в rejects не попадёт никогда: она валидна по всем формальным правилам и просто указывает не на тот товар.
У вас четыре причины отклонения, и все формальные — тип, пустота, дубль, знак. Есть ли в схеме проверки, которые ловят правдоподобные, но неверные значения? Мне кажется, именно они дают самые дорогие расхождения, потому что до BI доходят молча.
Про ценники с QR добавлю техническую сторону, она объясняет часть офлайновых расхождений.
Напечатанный код статичен, а предложение меняется. Если код ведёт прямо на конкретную акцию, тираж устаревает вместе с ней, и переклеивать его в масштабе сети никто не будет. Живёт только схема, где код ведёт на постоянный адрес, а содержимое по этому адресу подменяется. Расхождение «в приложении скидка есть, на полке её нет» очень часто именно отсюда, а не из-за кассира.
Второе — само сканирование. По нашим замерам распознавателю нужно, чтобы код занимал в кадре не меньше примерно 110 точек по стороне. Мелкий код на ценнике под плёнкой с бликом в этот порог не попадает, камера крутится вхолостую, и человек делает вывод, что не работает приложение.
Обе вещи чинятся до того, как начинается дизайн интерфейса. Ваш вывод про потерянную выручку от этого только крепче.