Приходишь пожарить картофель – уходишь Дата-саентистом
P.S. автор рецепта хоть бы лямбду сказал, чтобы понимать, какие жертвы ждать среди картофеля...
#offtopic
P.S. автор рецепта хоть бы лямбду сказал, чтобы понимать, какие жертвы ждать среди картофеля...
#offtopic
🍾1
Тестовое задание: часть 2 ✍️
2️⃣ Второй день
Проснулись — улыбнулись: все шло по моему рабочему плану "К концу первого дня первая модель, к концу второго – готовый инференс".
Я решил довести обучение до ума: чуток подрегулировать параметры модели, learning rate и прочие вещи. По итогу на все той же ласточке VGG19 достаточно быстро довел до 76%, после чего успокоился и решил, что для тестового задания качество будет достаточным. Дальше — веселее, пришло время конвертации модели в ONNX.
Сразу же вылез тот факт, что модель я тренировал на изображениях размера 48x48, а такая интересная штука, как AdaptiveAvgPool2d (который и позволяет подавать модели фотографии изображения другого размера для обучения) конвертируется в ONNX только для изображений формата 224x224. В противном же случае возникает ошибка, которая в действительности сейчас практически не решается. Таким образом, модель сама по себе работает, а в ONNX перевести не вариант. Перепробовал 4-5 наиболее адекватных решений из обсуждений этой проблемы на гитхабе, ни одно из них не помогло (одно даже заработало, только вот точность упала до 50%).
Итог: время на исходе, часы потрачены, а модели нет. Tо есть, случилась ситуация как в этих детских играх с бросанием кубика и ходами по количеству выпавших очков, где перед финишем ты попадаешь на клетку, возвращающую тебя на самое начало. Делать нечего — решил учить другую модель, остановился на ResNet. Благо, там этот же AdaptiveAvgPool имел размерность 1х1, за счет чего не возникло проблем при конвертации (и это я проверил, разумеется, до обучения).
С помощью лома и какой-то там матери обучил модель, точность которой составила 72%. Падением на 4% я удовлетворился.
Далее — инференс. Сразу же при поиске, как подключить результат к вебке, нашел ONNX Runtime Web Demo от Microsoft, в котором по факту была задача из моего ТЗ. Практически тупо один в один: модель для классификации эмоций, позволяющая загружать картинки и включать вебку. Более того, обучена она была на том же датасете. Опять же, к гадалке не ходи, скорее всего при выборе задачи эту демку там же и нашли. Разумеется, я не стал далеко уходить от примера — установил Node JS, запустил у себя этот проект, вырезал из него все остальные модели, наладил связи и запустил свою.
В результате было что-то несусветное: счастливые люди превратились в дед-инсайдов, грустные – в агрессивных, и далее по списку. Написал инференс на Python, стал проверять на картинках — там все в порядке. Главное и единственное подозрение пало на входные данные. Подозрения подтвердились проверкой и выводом информации в консоль, дальше я примерно имитируя трансформации, которые производились с помощью PyTorch перед подачей в модель, дописал их на JavaScript, основываясь на значениях тензоров, и в итоге получил результат близкий, но все же не идентичный.
✅ Итог: на часах 5 утра, я освоил PyTorch и TensorBoard за один день, ONNX, ONNX Runtime за второй день, а JavaScript — за одну ночь. Модель обучена и работает, хотя и слегка калечно. Тестовое задание упаковано и отправлено.
#offer
Проснулись — улыбнулись: все шло по моему рабочему плану "К концу первого дня первая модель, к концу второго – готовый инференс".
Я решил довести обучение до ума: чуток подрегулировать параметры модели, learning rate и прочие вещи. По итогу на все той же ласточке VGG19 достаточно быстро довел до 76%, после чего успокоился и решил, что для тестового задания качество будет достаточным. Дальше — веселее, пришло время конвертации модели в ONNX.
Сразу же вылез тот факт, что модель я тренировал на изображениях размера 48x48, а такая интересная штука, как AdaptiveAvgPool2d (который и позволяет подавать модели фотографии изображения другого размера для обучения) конвертируется в ONNX только для изображений формата 224x224. В противном же случае возникает ошибка, которая в действительности сейчас практически не решается. Таким образом, модель сама по себе работает, а в ONNX перевести не вариант. Перепробовал 4-5 наиболее адекватных решений из обсуждений этой проблемы на гитхабе, ни одно из них не помогло (одно даже заработало, только вот точность упала до 50%).
Итог: время на исходе, часы потрачены, а модели нет. Tо есть, случилась ситуация как в этих детских играх с бросанием кубика и ходами по количеству выпавших очков, где перед финишем ты попадаешь на клетку, возвращающую тебя на самое начало. Делать нечего — решил учить другую модель, остановился на ResNet. Благо, там этот же AdaptiveAvgPool имел размерность 1х1, за счет чего не возникло проблем при конвертации (и это я проверил, разумеется, до обучения).
С помощью лома и какой-то там матери обучил модель, точность которой составила 72%. Падением на 4% я удовлетворился.
Далее — инференс. Сразу же при поиске, как подключить результат к вебке, нашел ONNX Runtime Web Demo от Microsoft, в котором по факту была задача из моего ТЗ. Практически тупо один в один: модель для классификации эмоций, позволяющая загружать картинки и включать вебку. Более того, обучена она была на том же датасете. Опять же, к гадалке не ходи, скорее всего при выборе задачи эту демку там же и нашли. Разумеется, я не стал далеко уходить от примера — установил Node JS, запустил у себя этот проект, вырезал из него все остальные модели, наладил связи и запустил свою.
В результате было что-то несусветное: счастливые люди превратились в дед-инсайдов, грустные – в агрессивных, и далее по списку. Написал инференс на Python, стал проверять на картинках — там все в порядке. Главное и единственное подозрение пало на входные данные. Подозрения подтвердились проверкой и выводом информации в консоль, дальше я примерно имитируя трансформации, которые производились с помощью PyTorch перед подачей в модель, дописал их на JavaScript, основываясь на значениях тензоров, и в итоге получил результат близкий, но все же не идентичный.
#offer
Please open Telegram to view this post
VIEW IN TELEGRAM
GitHub
GitHub - microsoft/onnxruntime-web-demo: demos to show the capabilities of ONNX Runtime Web
demos to show the capabilities of ONNX Runtime Web - microsoft/onnxruntime-web-demo
Тестовое задание: часть 3 ✍️
Давно я не выходил на связь, не хотел раньше времени говорить, чтобы не сглазить.
Обратная связь пришла уже через два дня: тестовое задание выполнено хорошо, я интересен команде, оставались лишь формальные вопросы. На днях выслали оффер, съездил пару раз с документами в офис, а уже сегодня вышел на работу.💼
P.S. как-то бездарно я веду канал: мол, буду продираться через тернии, а по итогу конвертация собеса в работу оказалась стопроцентной. Обещаю исправиться – теперь будет о чем рассказать уже изнутри.📂
К посту прикрепил пару фотографий с места событий📍
#offer #progress #path
Давно я не выходил на связь, не хотел раньше времени говорить, чтобы не сглазить.
Обратная связь пришла уже через два дня: тестовое задание выполнено хорошо, я интересен команде, оставались лишь формальные вопросы. На днях выслали оффер, съездил пару раз с документами в офис, а уже сегодня вышел на работу.
P.S. как-то бездарно я веду канал: мол, буду продираться через тернии, а по итогу конвертация собеса в работу оказалась стопроцентной. Обещаю исправиться – теперь будет о чем рассказать уже изнутри.
К посту прикрепил пару фотографий с места событий
#offer #progress #path
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
❤🔥6👍1
Прошла первая рабочая неделя в компании, спешу рассказать (спустя почти две недели
Я устроился в ЮГПА в отдел ML-разработки. Таким образом, моя основная работа состоит в том, чтобы находить данные и обучать модели. В общем, это то, чего я хотел и искал. Наш продукт – CubicCV. Если кратко, то делаем систему видеонаблюдения с кучей плюшек сверху: от отслеживания людей в кадре, их пола и возраста до проверки износа оборудования, Face ID и иных полезностей.
Команда мне понравилась, все ребята очень приятные, руководитель отличный, местоположение и условия в офисе хорошие.
Что мне особенно понравилось – моя тестовая задача с определением эмоций оказалась не высосанным из пальца заданием, а реальной задачей на проекте. Поэтому, неудивительно, меня назначили на эту задачу, чтобы теперь уже не сумбурно, а адекватно её допилить. За пару месяцев до меня этой задачей занимался другой человек, даже сделал её, поэтому нужно было сравнить наши модели и выбрать лучшую.
В связи с тем, что я вышел на работу в пятницу, еще и в неполный рабочий день, я только и успел, что настроить рабочее пространство, познакомиться с людьми, получить доступы и сходить после работы в бар с коллегами буквально на часок.
Понедельник. Я открываю репозиторий, в котором другой человек уже решал ту же задачу. Первое ощущение – паника, потому что передо мной куча непонятного кода, логики, модулей, структур и иных вещей. Сам код в целом хороший, но при условии околонулевой документации и неочевидных взаимодействий разруливать было непросто.
Вроде начинаю чё-то робко понимать. К концу дня я изучил код вдоль и поперек, нашел датасеты для задачи, дальше оставалось действовать.
Жаль, что я веду канал не каждый день – эти 4 дня смешались, потому что я решал все ту же задачу. Расскажу чуть подробнее про поиск данных: сперва нашел замену своему прошлому датасету – FER+ – оказывается, есть хорошие люди, которые сели и переразметили старый FER, изменив классы для неправильных изображений, а также убрав изображения без лиц. Затем я нашел кусочек AffectNet на 40к картинок, а потом еще 5.5 тыс. изображений, найденных и размеченных каким-то святым человеком, потому что их качество оказалось на порядки выше того, что есть в FER и AffectNet.
После этого я, изучив чужой код, понял, что изначально мы обучали наши модели на разных данных, на разное число классов, с различными предобработками. То есть, модели вообще сравнивать смысла нет.
Таким образом, дальше предстояло сделать предобработку данных по подобию, но лучше той, что была ранее: выровнять лица по горизонтали (для этой задачи взял RetinaFace, чтобы на основе местоположения глаз осуществлять поворот).
Работа с данными удалась, я с подсказкой руководителя в очень нужный момент даже не успел упереться в одну нетривиальную проблему, и к утру понедельника брошенная мной обучаться на ночь с пятницы на субботу модель выдала 78.7% точности.
В сравнении с тем, что было ранее, прирост составил 10% (он, конечно, в целом не такой высокий, потому что я ведь обучал на меньшее число классов), а с моим личным результатом на то же число классов прирост составил около 5%.
В общем, результат очень неплохой, руководитель меня похвалил, так что я, можно сказать, сразу же себя проявил с хорошей стороны.
Попытки играться с архитектурами, параметрами и т.д. ни к чему особо не привели – простой ResNet18 решил эту задачу наилучшим образом, даже его модификации не показали никаких улучшений.
Теперь мне остается сконвертировать модели в ONNX, TRT и подбить код для удобного сравнения моделей между собой.
То ли еще будет...
#progress #hard
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2❤1👌1🏆1
Впервые мне предстояло решить задачу, всецело опираясь на чужой проект и модернизируя его код. Несмотря на то, что он по большей части был хорошим, я столкнулся с различными проблемами, и даже пару раз больно ударился. Собственно, ниже несколько выводов:
Это очень полезная штука, хотя бы ей надо пользоваться, если не пишешь документацию, потому что из нее можно понять примерный вид входа/выхода функции.
Может показаться, что документирование отнимает много времени, но на самом деле правило простое: написали функцию, сделали аннотации типов – сделайте и docstring, с учетом помощи IDE это займет 3-4 минуты и впоследствии сэкономит не менее 10-15 при повторном изучении кода. Также не забывайте проставлять однострочные комментарии в неочевидных местах, если такие есть.
Вот здесь я получил удар под дых, потому что при обучении модели постфактум один раз не увидел, что там вручную выставлено 7 классов, а не 5. Решение: сразу завести файл с набором констант, и в случае их зависимости друг от друга делать так, чтобы при изменении одной изменялись и зависимые.
Если в коде в различных файлах есть повторяющиеся функции или действия, их тоже необходимо минимизировать до нуля, чтобы при малейшем изменении не пришлось бегать между файлами и делать замены, или, не дай бог, вообще не найти до поры до времени возникшую из-за этого ошибку.
Казалось бы, настолько очевидные вещи, которые сами собой разумеются – зачем их вообще озвучивать? Я просто надеюсь, что прочитавшие и причастные узнали, согласны и помогут своим коллегам не умереть под завалами вашего же кода
#hard
Please open Telegram to view this post
VIEW IN TELEGRAM
❤1👍1👌1
Сегодня я наконец расскажу о чем-то познавательном, а то о себе да о себе
Да и вообще наконец-то о чем-то расскажу, потому что уже давно ничего не писал, хотя событий было много.
В рамках задачи по классификации изображений возникает еще одна подзадача: нужно не только классифицировать объекты, но и сделать так, чтобы модель была инвариантна к вращениям, масштабу и иным премудростям. Никому, очевидно, не нужен классификатор, который перестает работать, если чуть повернуть или подвинуть изображение.
Конечно, для решения этой задачи уже давно существуют аугментации (вращения, изменения масштаба, цветового баланса изображений и т.д., которые случайным образом применяются к изображениям). Они позволяют как увеличить тренировочную выборку, так и за счет различных искажений сделать модель более устойчивой. Однако, у них тоже есть свой предел, при этом достаточно низкий. То есть, картинку можно повращать, условно, на 30 градусов туда-сюда, но если увлечься, то классификатор вообще перестает понимать, что от него хотят.
И вот тут, откуда не ждали, приходит на помощь STN
Если простыми словами: модель в себе содержит две нейросети, одна из которых отвечает за выравнивание картинки для другой, а вторая уже классифицирует объект. На самом деле, несложно представить себе сам процесс.
Вот посмотрите на этого парня:
А что, если я теперь скажу, что у STN совсем не одна архитектура? Есть сети, которые берут фичи с первых слоев классификатора себе в помощь и поворачивают именно их (cтр. 4 с различными архитектурами), а есть сети, которые вместо аффинных преобразований работают с перспективой или со сплайнами (тут готовая модель на PyTorch), также есть сети с вероятностным подходом.
Все это
К сожалению, не обошлось здесь и без проблем. Я подробнее опишу уже в комментариях, чем отличается суровая реальность от нежной виртуальности.
Тем не менее, штука перспективная, и весьма интересная, в том числе и для дипломной работы.
До новых встреч,
#hard #recommend #papers #learn #code
Please open Telegram to view this post
VIEW IN TELEGRAM
👏3
Представьте, что вы обучаете модель для решения задачи классификации. Вы уже нашли достаточное для вас количество данных, перебрали все интересующие вас архитектуры вплоть до SOTA последних трех лет, опробовали все мыслимые и немыслимые претрейны, провели работу с аугментациями, и так далее. А на выходе по итогу точность поднялась с 75 до 80%, и баста. А вас это вообще не устраивает (Разумеется, если устраивает, спокойно можете игнорировать дальнейший текст
Я оказался ровно в такой же ситуации при решении задачи по классификации эмоций. Данные подобраны и выровнены, чтобы получить сколько-нибудь оптимальное положение лиц. Практически все настраиваемые параметры перебраны, найдены оптимальные аугментации, архитектуры проверены, но 80% — это всё еще потолок. А что такое 80%? —Каждый пятый уйдет с неверным ответом.
Я думаю, что те, кто прошел через это, уже поняли, о чем пойдет речь — о данных
Все началось с того, что я вполне логично решил проверить пары true-predict для изображений, на которых были допущены ошибки. И если честно, я даже не догадывался, что все будет так плохо. Оказалось, что моя модель почти всегда выдавала более-менее правильные ответы, а изначальные классы вообще не соответствовали действительности. То удивленные люди были злыми, то умирающие от депрессии считались счастливыми, то попадалось одно и то же изображение с лейблами: веселый, грустный. А модели-то как понять, что от нее хотят? Вот то-то и оно.
#learn #hard
Please open Telegram to view this post
VIEW IN TELEGRAM
Я решил, что переразмечу исключительно те данные, на которых были допущены грубые ошибки. Начал с тренировочной части, чтобы понять, получу ли я хоть какой-нибудь эффект. Не трогал валидационные, потому что были переживания о возможном эффекте подтасовки — вдруг я просто пойду на поводу у модели и она мне впоследствии выдаст нужную точность? Сперва я переразметил 900 train-изображений, у 600 из которых был неверный true-класс. Относительно всех данных это было что-то в районе 1%. Эффект — колоссальный. Пропали жуткие просадки по точности между эпохами, резко возросли темпы обучения модели, но... потолок остался практически тем же. Это крайне логичный результат, потому что валидация не изменилась, и ошибки те же, как ни крути. Но результат вдохновил. Доразметил оставшиеся train-изображения, взялся за валидацию, после чего произошло нечто ожидаемое, но не настолько.
В совокупности я потратил 4.5 рабочих дня на переразметку чуть более 5000 изображений. Относительно всех данных вышло около 7.5%. Если честно, работа жуткая — к концу рабочего дня все плывет, темпы работы низкие, взгляд замыленный, никаких сил и желаний уже не остается💀 . Но я чувствовал, что это нужно сделать, и просто не останавливался.
📈 Итог: +8% точности. Отскок с 80 до 88 процентов, причем не просто отскок, а еще и снова резкое поднятие темпов обучения, отсутствие просадок. 80% модель брала за 40 эпох, когда ранее она едва-едва брала 70%, и дальше росла крайне медленно.
Из 7.5% переразмеченных данных реально пришлось менять чуть более половины: положим, 4%. А модель дала прирост на все 8. Почему? Потому что теперь она имела лучшее представление о правильных данных, и перекрыла тот процент ошибок, который возникал в результате обучения на неправильных данных. То есть, ранее, если модель подстроилась под неправильные — точность 80%. Если она подстроилась под правильные — все равно те же 80.
✍️ А вывод здесь простой: чтобы кого-то учить, нужно сперва самому знать, чему учить. Это я прочувствовал на себе прекрасно, чего и вам желаю 🙂
#learn #hard
В совокупности я потратил 4.5 рабочих дня на переразметку чуть более 5000 изображений. Относительно всех данных вышло около 7.5%. Если честно, работа жуткая — к концу рабочего дня все плывет, темпы работы низкие, взгляд замыленный, никаких сил и желаний уже не остается
Из 7.5% переразмеченных данных реально пришлось менять чуть более половины: положим, 4%. А модель дала прирост на все 8. Почему? Потому что теперь она имела лучшее представление о правильных данных, и перекрыла тот процент ошибок, который возникал в результате обучения на неправильных данных. То есть, ранее, если модель подстроилась под неправильные — точность 80%. Если она подстроилась под правильные — все равно те же 80.
#learn #hard
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3👏1
Порой в рамках работы возникают и относительно простые задачи (хотя, если посмотреть на существующие в открытом доступе решения и их качество, сомневаешься в их простоте).
Примерная формулировка
У нас есть видеопоток, к которому применяются детекция, трекинг автомобилей, определение их номеров и цвета. Т.е. система в целом спокойно существует себе на проде. Нужно поверх этого научиться приблизительно оценивать скорость движения. Решение, разумеется, нужно не итоговое, а MVP в рамках демки, насколько эта штука может быть рабочей.
Related works
Я изучил несколько уже существующих решений, некоторые даже из продакшена других организаций — очень многие не заморачивались даже с тем, что у автомобиля разброс скорости может составлять 10-15 км/ч с разницей в долю секунды. Из всего, что нашел, понравилась по результату одна выпускная работа, выложенная на Github, но там пошли чуть дальше в плане оценки точек плоскости и наоборот перемудрили — сопоставление идет по координатам из Google Maps, т.е. для запуска такого проекта нужно уже слишком много усилий приложить. В разделе Issues автор ответов не дает, что в общем-то неудивительно — я бы тоже, выпав из контекста такой мудреной работы не захотел бы снова в него возвращаться.
#hard
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Начало работы
Я сразу сел и начал сочинять космолет, как мы на основе траектории движения автомобилей и изменения размера бокса будем производить оценку плоскости, по которой движется транспорт. Делаем три в общем и целом достоверных предположения:
1) Автомобили движутся по плоскости. То есть, хрен бы с ним, что Земля круглая, в рамках видимости камеры это нам погоды не делает.
2) Автомобиль имеет статичный размер (и на том спасибо), поэтому при его приближении Bounding Box автомобиля будет меняться пропорционально автомобилю.
3) Автомобиль движется по кусочно-линейной траектории.
Через час оказалось, что нас с головой устраивает вручную задать четыре точки прямоугольника, его ширину и высоту — итого шесть параметров. Задача безбожно упростилась, да и я понял, что моя первоначальная идея это скорее overthinking (все как обычно — сперва задай все вопросы, а затем уже думай над решением). Остальное оказалось лишь делом техники.
1) Разметка
Сперва отмечаем четыре точки на исходной картинке, затем оцениваем ширину и длину размеченного участка (
2) Проекция
После построения прямоугольника мы прикручиваем проекцию исходных точек на плоскость нашего прямоугольника ([0, 0], [w, 0], [w, h], [0, h]). (Спасибо
cv2.getPerspectiveTransform за то, что он есть). Таким образом, мы получаем матрицу для проекции точки с картинки на нашу плоскость. То есть, сразу же умеем определять расстояние между объектами, точность которого зависит лишь от исходной оценки или удачного ориентира (3) Вычисления
Для детекции и трекинга я не стал изобретать велосипед — взял связку Ultralytics YOLOv8 + ByteTracker.
Сложнее становится на этапе вычисления скорости, потому что и ежу понятно, что если каждый кадр вычислять скорость, с учетом дрожания боксов и иных погрешностей скорость будет прыгать от 10 до 300 км/ч. А нам не нужно, чтобы чуваки по парковке гнали по 200+ км/ч, даже если они расстались с девушками. Самая простая и рабочая идея решения состоит в том, чтобы раз в n кадров узнавать дистанцию между некоторой точкой автомобиля на i и (i + n)-ом кадре, а затем, имея дистанцию и время, считать скорость. Так сказать, принцип "время расставит все по местам" в действии.
При этом важно отметить, что нельзя просто производить общий для всех автомобилей замер скорости раз в n кадров, потому что тогда, если автомобиль появился в кадре сразу после замера скорости, для него уже потребуется 2n - 1 кадров. Здесь тоже все просто — создаем свой небольшой трекер и для каждого автомобиля храним кадр его появления, отсчитывая n уже относительно этого кадра.
Ну и конечно же, для компенсации погрешности замеров не грешно применить скользящее среднее с окном k, где каждый последующий замер — среднее арифметическое предыдущих k значений.
#hard
Please open Telegram to view this post
VIEW IN TELEGRAM
❤🔥1👍1🆒1
Media is too big
VIEW IN TELEGRAM
Визуализация
Вот здесь была убита чуть ли не бóльшая часть времени. Хотелось сделать гибкую функцию построения сетки, которая бы строила ее относительно четырех выбранных точек, чтобы можно было задать количество клеток по x или по y (При этом, чтобы при задании одного числа второе находилось пропорционально сторонам заданного прямоугольника), чтобы можно было выделить маску и не делать отрисовку вне этой маски. По большей части эта функциональность была мне нужна для валидации того, правильно ли сделаны замеры, да и просто чтобы демка была не колхозной. Результат прикрепил.
#hard #results
Please open Telegram to view this post
VIEW IN TELEGRAM
❤🔥1👍1
В общем и целом, моя реализация страдает в первую очередь тем, что это полуавтоматический процесс, требующий задания человеком шести значений. В связи с тем, что нас это устраивает, я углубляться не стал, но можно было бы делать оценку плоскости с помощью решений в области DL, а длину и ширину определять относительно ширины полосы, разметки, дистанцией между колес автомобиля. Это уже очень неприятная работа, потому что у нас очень разные дороги в стране, разметка местами вообще отсутствует, а еще мы можем столкнуться с паркингом и прочими выпадающими из общего домена объектами. Поэтому здесь вполне резонно для начала сделать ручную разметку, тем более, что 6 значений для статичной камеры можно посчитать за пару минут один раз, а работать это будет пока по камере не врежут так, чтобы она поменяла положение.
Для решения же проблемы значимого сдвига камеры можно сохранять скорости автомобилей, строить временной ряд по времени суток, и в случае значительного отклонения относительно выбранного порога кричать: "что-то произошло, проверьте камеру". Ну и там будет либо авария в 13 часов в воскресенье, из-за чего возникнут пробки, когда их обычно нет, либо же действительно что-то произошло. В общем говоря, это тоже overthinking, такие задачи надо решать по факту, но заранее иметь в виду возможные нюансы.
Потрачено 2 рабочих дня, готова сносная демка, а результат отправлен на подготовку в прод (Он будет совсем-совсем не так выглядеть, разумеется: на проде не Python). Я немного передохнул между большими задачами и поупражнялся в визуализации.
#hard
Please open Telegram to view this post
VIEW IN TELEGRAM
❤🔥1👍1
Между последними постами прошло почти аж два месяца: к сожалению, сил и желания писать тексты после плотной рабочей недели остается немного.
Однако я-таки развиваюсь и скоро расскажу о следующих вещах:
Если у вас есть какие-либо пожелания к постам или вопросы, всегда рад обратной связи, мб смогу ответить и раскрыть темы, пропустив через призму личного опыта
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2❤1
Как идеи из математики и программирования помогают в приготовлении к новогодним праздникам? 🧢
На самом деле, идеи относятся не только к праздникам, но и к повседневным задачам. Сами идеи, безусловно, первоначально не относятся к программированию, но нашли хорошее применение именно в этой сфере. Приведу пару примеров за последний день:
1) Решение проблем с нехваткой RAM🔃
Если у нас не хватает ресурсов для обработки всей информации, но нам нужно это сделать, то мы обрабатываем информацию пачками или по мере надобности.
Проблема: все напитки не влезают в холодильник, однако нужны будут лишь вечером.
Решение: охлаждать напитки партиями, чтобы по мере использования загружать новые, таким образом высвободив ресурс.
2) Распределение нагрузки🍑
Если мы хотим, чтобы на один сервер не ложилась вся нагрузка, то мы грузим все понемногу, чтобы они успевали справляться.
Проблема: нужно затарить кучу всего на новый год
Решение: обнести все магазины на районе по чуть-чуть
3) Бинарный поиск↔️
Если примеры выше выглядят скорее шуточно, то здесь очень любопытное применение идеи из алгоритма.
Проблема: нужно повесить гирлянду-штору, но она длиннее карниза. Есть лишь две руки, табуретка, скотч, ножницы (и, слава богу, гирлянда).
Решение: повесить сперва края гирлянды, затем середину (ее легко найти, потому что Ньютон придумал гравитацию), затем середины оставшихся половинок, и далее, пока алгоритм не сойдется к нужному результату. Итог: гирлянда висит, огоньки распределены равномерно, времени потрачено 7 минут.
На самом деле, крайне удивительно то, насколько высока применимость некоторых идей к реальной жизни: как много процессов можно выразить через степенную или же, наоборот, логарифмическую функцию; как часто мы даже неосознанно используем то, что формализовано и активно применяется совершенно в других сферах. На эти мысли меня еще раз натолкнула недавняя статья GPT-like модель «впервые сделала научное открытие», с которой причастным советую ознакомиться.
Всем желаю побольше новых идей в новом году!🎁
#offtopic
На самом деле, идеи относятся не только к праздникам, но и к повседневным задачам. Сами идеи, безусловно, первоначально не относятся к программированию, но нашли хорошее применение именно в этой сфере. Приведу пару примеров за последний день:
1) Решение проблем с нехваткой RAM
Если у нас не хватает ресурсов для обработки всей информации, но нам нужно это сделать, то мы обрабатываем информацию пачками или по мере надобности.
Проблема: все напитки не влезают в холодильник, однако нужны будут лишь вечером.
Решение: охлаждать напитки партиями, чтобы по мере использования загружать новые, таким образом высвободив ресурс.
2) Распределение нагрузки
Если мы хотим, чтобы на один сервер не ложилась вся нагрузка, то мы грузим все понемногу, чтобы они успевали справляться.
Проблема: нужно затарить кучу всего на новый год
Решение: обнести все магазины на районе по чуть-чуть
3) Бинарный поиск
Если примеры выше выглядят скорее шуточно, то здесь очень любопытное применение идеи из алгоритма.
Проблема: нужно повесить гирлянду-штору, но она длиннее карниза. Есть лишь две руки, табуретка, скотч, ножницы (и, слава богу, гирлянда).
Решение: повесить сперва края гирлянды, затем середину (ее легко найти, потому что Ньютон придумал гравитацию), затем середины оставшихся половинок, и далее, пока алгоритм не сойдется к нужному результату. Итог: гирлянда висит, огоньки распределены равномерно, времени потрачено 7 минут.
На самом деле, крайне удивительно то, насколько высока применимость некоторых идей к реальной жизни: как много процессов можно выразить через степенную или же, наоборот, логарифмическую функцию; как часто мы даже неосознанно используем то, что формализовано и активно применяется совершенно в других сферах. На эти мысли меня еще раз натолкнула недавняя статья GPT-like модель «впервые сделала научное открытие», с которой причастным советую ознакомиться.
Всем желаю побольше новых идей в новом году!
#offtopic
Please open Telegram to view this post
VIEW IN TELEGRAM
😁2❤🔥1❤1👍1
Сейчас занимаюсь поиском интересных аугментаций изображений для обучения и оценки моделей, наткнулся на настоящий клад: kornia.enhance.jpeg_codec_differentiable
Чем хороша данная аугментация — в продакшене часто приходится работать с изображениями плохого качества, и это не абстрактное размытие или шум, а именно сжатие, в связи с чем теперь можно успешнее искать и устранять слабые стороны моделей для прода.
Полагаю, многие знакомы с библиотекой Kornia, но все же поясню — это библиотека для работы с изображениями, очень близкая по функциональности к OpenCV, но, во-первых, сделана именно для PyTorch тензоров, а во-вторых, поддерживает работу с GPU и может принимать на вход батчи изображений. Т.е. если у вас перед PyTorch-моделью есть препроцессинг на OpenCV, то скорее всего его можно очень быстро переписать на Kornia, помимо этого перекинув нагрузку на GPU.
И еще один важный момент — эта библиотека заявлена как дифференцируемая. Если говорить корректнее, то дифференцируемы функции, которые реализованы в библиотеке. И новая функция — не исключение (пока все комьюнити поражается тому, на что способны новые генеративные сетки, я поражаюсь тому, что даже шакалов научились дифференцировать
Вот здесь статья, код и постер, которые легли в основу. В силу того, что работа свежая, аугментация на момент написания этого поста еще не дошла до релиза (ожидается в kornia==0.7.2), поэтому ей можно воспользоваться лишь стянув библиотеку напрямую с GitHub.
P.S. Я уже все опробовал, нашел ошибку, исправил, закинул PR и его уже даже оперативно смерджили, пользуйтесь на здоровье
#papers #hard #code
Please open Telegram to view this post
VIEW IN TELEGRAM
❤🔥4
Вроде бы очевидная вещь, но почему-то не приходившая мне на ум, пока лично с ней не столкнулся. Представим себе, что мы обучили классификационную модель, получили метрики, которые нас устраивают, тестируем модель на сервере, а там... часть вердиктов адекватна, а часть — совершенно мимо. Изучили ошибки подробнее: уверенность модели в одном классе — 0.25, в другом — 0.24, в третьем — 0.23, а остальные 0.28 распределились между оставшимися классами. То есть, модель нам недвусмысленно говорит: "я не знаю, к какому классу отнести этот объект". Что делать?
В этой ситуации на помощь приходит порог принятия решений — если confidence модели ниже этого значения, мы говорим: "хз", а если выше, то утвердительно выдаем результат классификации. Вот так все просто. Не считая, конечно, того, что этот порог нужно как-то выбрать.
Здесь можно, конечно, вспомнить про Precision/Recall и пытаться отнести результаты "хз" к False Negative (FN), но мне эта мысль сразу же концептуально не понравилась, потому что отказ от принятия решения не равен допущению ошибки. Важно, что это высказывание может быть истинным или ложным в зависимости от задачи, и в рассматриваемой задаче оно истинно — мы можем отказаться от части вердиктов в угоду точности.
Идея следующая: отказываясь от принятия решения, мы хотим, пожертвовав наименьшей долей всех предсказаний, в награду избавиться от наибольшей доли ошибок. То есть, мы стремимся к ситуации, где мы в 1 из 10 случаев скажем "я не знаю", но среди этих "я не знаю" 9 из 10 были бы ошибками, если бы мы дали уверенный ответ.
Графически это можно представить себе так: по оси абсцисс значение порога от 0 до 1, по оси ординат для каждого значения порога два столбика: первый — доля ответов от всех данных, второй — доля ошибок от изначальных ошибочных ответов. При значении порога, равном 0, мы всегда говорим ответ, даже если не имеем понятия, о чем речь, а при значении 1 мы отказываемся от ответов, и нашему скептицизму завидует даже Пиррон. Ну и разумеется нам хочется, чтобы столбик ошибок опускался заметно быстрее, чем столбик ответов.
#hard #cases
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Как известно, суровая реальность и нежная виртуальность едва ли когда-нибудь соприкоснутся, однако мы продолжаем оптимистично смотреть в будущее. Описанную идею я применил на практике, и на графике выше можно видеть, что на пороге в 0.55 мы, отказавшись от ~20% данных, потеряли ~80% ошибок — наглядная демонстрация принципа Парето в действии.
Результат хороший, обоснование порога есть, добавили. Но затем видим, что на тестовом сервере вердикты даются дай бог для 2-3 из 10 изображений. Потому что есть разница доменов между обучающим набором данных, на котором считался порог, и изображениями, которые действительно будут в работе. На всякий случай: домен — это множество всех возможных объектов. На тренировочных данных мы имели дело с изображениями с фотостоков
Выбран был второй вариант — посмотрели, на каком пороге мы отказываемся от ~20% входящих данных, и зафиксировали результат. Решение банальное, временное, потому что стремиться нужно к первому варианту, однако реалистичное и без прикрас. Собственно, чем и хотел поделиться. Если есть похожий опыт, буду рад услышать ваши решения
#hard #cases
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Сегодня наконец расскажу, как решал реальную задачу кластеризации временных рядов и даже получил приемлемый результат (сам не верил).
Постановка задачи кластеризации
Здесь я буду краток, потому что либо вы уже знакомы с этой темой, либо нет и подробностей вы не хотите. Итак: имеем некоторый набор данных, хотим разделить данные на группы – так называемые кластеры. Все бы хорошо, но разметки у нас нет, следовательно, нет и возможности доподлинно оценить, насколько хорошо мы это сделали. А сделать надо.
Теория
Как правило, этой задачей в вузах вводят в ужас, да и требуют немного, потому что результат – хз. Лично я сидел и думал: "Ну мне-то эта задача никогда не попадется
Практика
А теперь реальность – задача есть, сделать надо, так еще и не просто кластеризацию, а для временных рядов. Таким образом, нам надо определить понятие похожести рядов между собой, чтобы затем уже на его основе делить данные на кластеры.
Плохо здесь то, что данные, в которых есть временная зависимость, в чистом виде классическим методам кластеризации скормить не получится, потому что наличие шума может сильно сказаться на работе, различия в длине рядов компенсировать тоже не так уж легко (вот эти ваши нормализации не всегда работают), а если нужно найти еще и смещенные во времени паттерны, так вообще хоть вешайся.
Каждый раз, подходя к такой задаче, нужно предварительно оценить особенности рядов и возможные подходы к решению. Вот отличная статья, а мы вернемся к конкретике.
В моем случае есть очень большое количество небольших временных рядов (50-100 значений). Каждый из них представляет из себя результаты замеров датчика во время работы механизма. Задача состоит в том, чтобы на фоне 95% корректных результатов выделить как можно большее число видов некорректной работы механизма, чтобы впоследствии передать результаты специалистам и получить от них пояснение, что это за проблема и проблема ли (получить разметку). Кластеризация здесь нужна для того, чтобы выделить именно группы ошибок, а не единичные странности.
Итого: много небольших рядов близкой длины и похожего поведения. Из особенностей – резкий пик в начале работы механизма, затем возврат к практически константным значениям до конца работы, после чего значения возвращаются к нулю. Очень несбаланстрованные данные – более 95% рядов нормальные. Обрезка данных не идеальная, по времени бывают сдвиги на ± 5 значений.
Предлагаю предположить, на каком подходе я остановился, исходя из этих вводных
#hard #progress #cases
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Загрузка
Ряды пришли ко мне в виде дампа базы данных PostgreSQL в количестве ~1 млн. Я радостно экспортировал нужные ряды в .csv, чтобы пандас их смог проглотить, попытался открыть и... разумеется, получил RAM OOM
P.S. Однозначно, есть решения и покруче, но конкретно это было выбрано мной исходя из желания предпринять минимальное количество действий для достижения цели. На мой взгляд, получилось оптимально.
Снижение размерности
В силу того, что ряды, во-первых, небольшие, а во-вторых, имели вполне конкретные визуально отличимые особенности, я решил выделить различные признаки у ряда: позиция пика, высота пика, матожидание/дисперсия бокового движения, 25-75 перцентили и прочее, что только в голову пришло. В результате сокращения размерности удалось из 50 значений получить 15-20 потенциально значимых характеристик, что позволило, как математики любят, вылить воду из чайника и свести задачу к предыдущей — работать с небольшим числом признаков и в ус не дуть.
Предобработка
Разумеется, перед тем, как приступать непосредственно к кластеризации, необходимо отфильтровать мусор. Pairplots, поиск и определение аутлаеров по комбинациям признаков, а также по некоторым эвристикам, касающимся домена рядов (если по-русски, то выкинуть те ряды, которые не могли бы в принципе существовать при нормальной записи данных) — ничего сверхъестественного, рутина, которую надо было кому-то делать. В ходе предобработки выяснилось, что порядка 15% данных не представляют никакого научного интереса, что в пределах адекватного, исходя из моего опыта.
Кластеризация
Когда у нас на руках чистенькие и красивые ряды, выбираем метод кластеризации и в путь. Я выбрал K-means++, потому что мог себе его позволить. Он интуитивно понятный, в моем случае адекватно применимый, и что самое главное — быстрый (1 млн рядов за 7-10 секунд разбивался на кластеры). Для выбора оптимального числа кластеров использовались как банальные предположения, так и методы локтя и силуэта, но в конечном итоге пришлось посмотреть и на иные варианты, потому что порой большее число кластеров выделяло интересную группу рядов, которая при меньшем числе оставалась безликой массой внутри другого кластера.
Далее оставалось построение графиков по признакам и кластерам, всякие там распределения, а также визуальное отделение кластеров с нормальными и аварийными рядами, формирование и отправка отчетов специалистам-разметчикам — рутина, да и только.
Однако кластеризация была лишь подводкой к основной задаче — это уже совсем другая история, которую я обещаю рассказать не через полгода...
#hard #cases
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2👌2
Ближайшие два дня принимаю участие в своей первой конференции по ML, которая проходит в онлайн-режиме.
Первые впечатления отличные — послушал доклад от Яндекса на тему компьютерного зрения в складской робототехнике и в ходе рассказа у меня возник вопрос, не думали ли ребята в сторону адаптивной предобработки изображений — например, использования параметров входного изображения, а-ля яркость/контраст, в качестве гиперпараметров модели. Идейно это был просто взгляд с другой стороны на ту проблему, которую подняли в докладе, где они игрались с аугментациями. Ребята подтвердили, что идея огонь, они на нее обязательно посмотрят и поблагодарили за мысль 👉👈. Ну и потом еще часа пол с ними пообщались неформально, несколько не менее интересных тем подняли — например, они думают сейчас в сторону пересмотра метрик, потому что бесконечно гоняться за ними, конечно, хорошо, но возводить их в абсолют неправильно. Короче — классно, интересно, познавательно.
На второй секции было скучновато: два из трех параллельно идущих докладов были не в кассу, а третий так и вовсе утомительный, но это потому что все доклады были слегка мимо специализации, тут уж всем не угодишь.
А на третьей секции был доклад от Wildberries, за время которого я понял, что ML-ный пайплайн у нас с ребятами очень похож: от хранения эмбеддингов в Milvus до использования Torch -> ONNX -> TensorRT конвертации и инференса в Triton Inference Server. Сам доклад был о выдаче похожих товаров и о том, как они ее улучшили с помощью кропа основного объекта с изображения. Доклад по своей сути был лаконичным и понятным, концептуально все ок, поэтому в дискуссии мы быстро перешли к наболевшему и полчаса обсуждали скорее какие-то локальные вещи.
Если подводить черту, то я, к своему удивлению, получил много положительных эмоций и в целом зарядился от общения с людьми, которые занимаются тем же и так же. Почему к удивлению — не думал, что в принципе захочу задавать вопросы и обсуждать что-либо, однако рад, что прогноз не оправдался. Если завтра будет, чем поделиться, обязательно поделюсь.
#self #soft
Please open Telegram to view this post
VIEW IN TELEGRAM
IML 2025. ML-конференция: от обучения до эксплуатации моделей
IML 2025 | IT-конференция по ML | Теория + практика
Конференция по ML от обучения до эксплуатации моделей | Мониторинг качества, оркестрация, обучение и переобучение в продакшене.
👍1