Куда делись 99% моего датасета

Я задался целью собрать датасет из нескольких миллионов обучающих примеров. Когда пайплайн завершился, он выдал мне пару процентов от этого — долю настолько малую, что она выглядела как опечатка. Ничего не упало. Ни одна ошибка не сработала. Данные не то чтобы не сгенерировались; они тихо выбросили почти всё самих себя, а затем отрапортовали об успехе.

Число, которое было слишком маленьким, чтобы быть верным

Странность такого рода багов в том, что они не заявляют о себе. Пайплайн, который падает, шумный. Пайплайн, который молча производит слишком мало, просто завершается, выдаёт тебе файл и ждёт, пока ты заметишь. Если бы я случайно не вспомнил, сколько примерно примеров я ожидал, я мог бы обучиться на выживших и так и не узнать, что остальные пропали.

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

«Почему так мало?» — это реальный класс багов

Нас учат отлаживать то, что ломается. У нас гораздо хуже получается отлаживать то, что просто отсутствует — данные, которые должны существовать, но не существуют, примеры, которые должны были быть созданы, но так и не были. Не существует трассировки стека для непройденной дороги. Пайплайн сделал ровно то, что я ему велел; просто оказалось, что велел я ему так, что по пути он отбрасывал большую часть входных данных.

Куда делись данные

Причина была почти до неловкости банальной. Где-то в процессе я шагал по таймлайну большими шагами — смотрел на один момент, затем перескакивал через длинный промежуток к следующему, затем перескакивал снова. Каждая позиция, через которую я перешагивал, была потенциальным примером, который так и не был рассмотрен и так и не был создан. Я задал этот шаг рано, по причине, которую уже не помню, и потом забыл, что он там есть.

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

Тихая потеря — это опасный вид потери

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

Опасность не только в том, что число неверно. Она в том, что число неверно, а система этому рада — всё зелёное, всё работает, всё построено на основе, которая предала тебя на самом первом шаге.

Решение: рассмотреть всё, затем выбрать, что оставить

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

Датасет вернулся к размеру, близкому к тому, каким он должен был быть. Ничего хитрого этого не сделало. Я просто перестал выбрасывать данные, не желая того.

Самая дешёвая проверка в работе с данными

Урок почти слишком прост, чтобы ощущаться уроком: сравни размер, который ты ожидал, с размером, который ты получил. Это дешёвая проверка на вменяемость, которую я сильно недоиспользовал, и она поймала бы это за секунды. Большой разрыв между тем, сколько данных я думал, что у меня есть, и тем, сколько данных существует на самом деле, заслуживает того, чтобы относиться к нему как к багу — даже, и особенно, когда ничего не упало, чтобы тебе об этом сказать.

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

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

— Никаких сигналов, никаких прибылей, не является инвестиционной рекомендацией.