Совершенно точно не щ

Санкт-Петербург, ноябрь 2014 года


Буква не давалась третий вечер.

На экране висел скан страницы из старой отцовской тетради — рабочий журнал девяносто девятого года. Тогда отцу было девятнадцать, он учился на втором курсе ИТМО и подрабатывал на кафедре — писал программу для испытательного стенда, и в тетради вперемешку шли адреса портов, куски кода и показания датчиков. Синяя шариковая ручка, торопливый почерк человека, который записывает не для других, а чтобы не забыть. Эхо уже умела читать его почти целиком. Почти.

В слове «давление» вторая буква была кругом.

Не «а». Не «о». Просто кругом с хвостиком, который мог быть хвостиком «а», а мог быть случайным росчерком.

Программа честно выдавала «дOвление» и ставила рядом крошечную отметку: уверенность сорок один процент.

Зара смотрела на эту отметку и злилась.

Сорок один процент — это не ответ. Это признание. Программа сказала «не знаю», но сказала так, будто знает: выбрала «о» и спряталась за цифрой.

А Зара знала больше программы. Она видела, что буква круглая. Значит, это могло быть «а», «о», «с», «е». И она видела, что вертикальных палок там нет. Значит, это совершенно точно не «ш», не «т», не «м».

Почему программа не может сказать так же?

Она открыла новый файл и написала первую строку.

```
struct glyph_guess {
    wchar_t candidates[8];
    float   weight[8];
    uint32_t excluded;   /* shape classes: definitely NOT */
};
```

Потом посмотрела на часы — без двадцати час — и пошла на форум.

***

Ветка называлась скромно: «OCR: storing doubt».

Первое сообщение PhoeNIX было в её обычной манере — так она писала с первого дня форума, и все давно привыкли:

```
// problem: OCR picks one char, hides doubt behind %
// 41% "o" is not knowledge. it is a guess with a badge
// proposal: keep candidates + weights
// + keep what it is NOT (shape class exclusions)
// round glyph w/o stems: {a,o,c,e} likely
// {b,d,f} possible
// {sh, t, m, zh, shch} -- impossible. say so
// question: why nobody stores the "impossible" part?
```

Ниже она приложила тот самый скан с кругом.

Первый ответ пришёл через десять минут. Ник — Hagrith. Он появился на форуме три года назад и с тех пор спорил с ней почти обо всём.

«Сначала научи её читать матричный принтер на нашем заводе, — написал Hagrith. — Там у нас половина накладных такого качества, что я сам их угадываю. Но идея с "чем это не может быть" мне нравится. Я так ищу неисправности: сначала вычёркиваю, что точно исправно».

PhoeNIX ответила одной строкой:

```
// exactly. diagnosis by exclusion. thx
```

А потом, в половине второго, в ветке появился ник, который Зара знала, но никогда не видела на этом форуме.

Kerning.

Человек, который пятнадцать лет назад начал писать свободную программу распознавания текста, ту самую, которой пользовалась половина открытых проектов в мире. Ту самую, на исходниках которой Зара в тринадцать лет училась понимать, что такое сегментация.

Она перечитала ник дважды. Потом встала, налила воды, вернулась и только тогда открыла его сообщение.

***

«Интересная постановка, — писал Kerning. — Но позвольте возразить как человек, который этим живёт.

Пользователю не нужно облако вариантов. Пользователю нужен текст. Он хочет скопировать абзац и вставить в письмо. Если я выдам ему "д{а|о|с|е}вление", он меня проклянёт.

Неуверенность мы снимаем словарём. В русском языке нет слова "довление", а слово "давление" есть. Языковая модель выбирает "а", и в девяноста девяти случаях из ста она права. Зачем хранить сомнение, если словарь его разрешает?»

Зара почувствовала, как у неё горят уши.

Он был прав. Почти во всём. Именно поэтому было трудно.

Она писала ответ минут двадцать, стирала, писала снова. В итоге оставила короткое:

```
// agree: user wants text. give him text. best guess
// disagree: dictionary is also a guess
// dictionary fails on:
//   - surnames
//   - code, formulas, part numbers
//   - rare words, old spelling, dialect
//   - lab journals (see scan)
// in these places dictionary is CONFIDENTLY wrong
// confident wrong > honest unsure? no
//
// so: show best guess. KEEP alternatives underneath
// user sees text. search sees doubt
```

И отдельной строкой, подумав:

```
// use case: search "davlenie" in 10 years of handwritten notes
// OCR said "dovlenie". your search finds nothing
// mine finds it. because I kept "a" as 2nd candidate
```

***

Ответ пришёл не сразу. Зара успела проверить, не заснул ли отец — свет у него не горел, — и вернуться.

«Поиск, — написал Kerning. — Да. Это аргумент.

Мы десять лет оптимизируем то, что видит глаз, и почти не думаем о том, что ищет поиск. Человек, который сканирует личный архив, ищет в нём потом всю жизнь. Ошибка распознавания там — не опечатка, а потерянная страница.

Хорошо. Убедили наполовину. Теперь моя половина.

Вы пишете, что знаете, чем буква НЕ является. Это сильная вещь, и я хотел бы, чтобы моя программа это умела. Но вы ошибаетесь в одном: вы считаете, что знаете, где буква начинается и где кончается.

Пятнадцать лет назад меня больше всего мучила не "о" против "а". Меня мучила латинская пара "rn" и "m". Две буквы, слепленные почерком, выглядят как одна. И наоборот: одна "m", разрубленная по ошибке, превращается в "rn".

В кириллице у вас то же самое. "ы" против "ьі". "ю" против "іо". "ш" против "лл" или "пі".

Ваше "совершенно точно не ш" верно — если сегментация правильная. А если программа неправильно разрезала слово, то ваш круг может оказаться половиной "ш", которую отрезали не там.

Сомнение бывает не только в том, какая это буква. Оно бывает в том, сколько здесь букв».

Зара долго смотрела на экран.

Потом перечитала. Потом взяла тетрадь, нашла на скане отца слово «шина» и посмотрела на «ш». У отца она была почти «лл». Программа три дня назад прочла её правильно — повезло.

Повезло.

Она написала:

```
// you are right. I store doubt per glyph
// but doubt is also in the cuts
// so not array of glyphs. graph of cuts
// each path through the graph = one reading of the word
// keep top paths with weights
// exclusions belong to the PATH, not to the char
//
// thank you. this costs me a week. worth it
```

И почти сразу пришёл ответ:

«Решётка вариантов. Да. Именно так это и называется у тех, кто занимается распознаванием речи. Они давно это поняли: звук тоже не знает, где кончается слово.

Если вы это сделаете — пришлите патч и мне. Я хочу, чтобы моя программа отдавала не только лучший текст, но и всё, в чём она сомневалась. Как дополнительный слой. Для тех, кому нужен поиск, а не письмо».

***

Под утро, когда Зара уже собиралась закрыть ноутбук, в ветке появилось ещё одно сообщение от Kerning.

«Кстати. Можно нескромный вопрос? Вы из университета? Из какой лаборатории? Ваш подход выглядит как часть большой исследовательской работы, а я хотел бы прочитать остальное».

Зара улыбнулась.

Из университета был отец — через стенку, спал. А ей до университета оставалось ещё полгода школы.

Этот вопрос ей задавали все три года — с тех пор, как при её книге появился форум. Отвечала она всегда одинаково.

```
/* discuss code, not author */
// patch will be ready in ~10 days
```

Через минуту он ответил:

«Справедливо. Жду код».

***

Она выключила монитор. За окном начинало сереть, внизу по линии проехала первая поливальная машина.

Дверь приоткрылась.

— Ты вообще ложилась? — спросил отец.

— Сейчас.

— Что-то получилось?

Зара подумала, как это объяснить.

— Я узнала, что не знала не того, что думала, — сказала она. — Я думала, что не знаю, какая это буква. А на самом деле я не знала, сколько там букв.

Отец посмотрел на неё долго. Он десятый год преподавал программирование в ИТМО и лучше многих знал, что значит, когда задача оказывается не той, которую решал. Половина его студентов узнавала это только на защите диплома.

— Это хорошая ночь, — сказал он. — Спи.

***

Патч был готов через одиннадцать дней.

Эхо научилась держать слово не как ряд букв, а как сеть возможных прочтений, и подсвечивать сомнительные места маленьким облачком. В облачке стояли варианты с весами. А внизу, мелким серым шрифтом, — то, чем это место совершенно точно не являлось.

Через месяц в свободной программе Kerning появился новый формат выдачи. В описании изменений было написано: «Хранение альтернатив и исключений, предложено пользователем PhoeNIX».

Отец показал ей эту строчку утром за завтраком, повернув к ней ноутбук. Программу Kerning он третий год давал студентам как образец хорошо написанного кода.

Зара посмотрела, кивнула и вернулась к каше.

Ей было шестнадцать, и она считала, что это нормально: если человек прав, его патч принимают.

О том, что так бывает далеко не всегда, она узнала гораздо позже.


Рецензии