Поиск работы, 69 дней 📣
Долго я не писал ничего на эту тему, хотя уже есть некоторые движения.
👀 За это время я полностью прошел курс по SQL: Практический курс для новичков по SQL и PostgreSQL (Илья Фофанов).
Опишу кратко: ранее в рамках вуза уже видел SQL, знал материал до INNER/OUTER JOIN и подзапросов, хотел заглянуть дальше, плюс расширить кругозор. Со своей задачей курс справился отлично: минимум теории, максимум практики, повествование сопровождается советами бывалого человека. Если нужен SQL на уровне побочной работы, типа Data Engineer, то хватит с головой. Курс занял 5 дней (Прямо как обещают на обложках учебников).
👀 Также я понял, что необходимо укреплять фундамент, поэтому повторно прошел курс теории вероятностей. Не так, как это часто в вузе бывает, а на любых не слишком понятных мне моментах я старался уйти вглубь (наконец-то руки дошли до доказательства, откуда же в нормальном распределении появляется множитель с числом 3⃣ ). В совокупности это заняло порядка 14 дней. Взял за основу вот эту книгу: Чернова Н. И. Теория Вероятностей.
Очень легко читается, много подробностей: местами лишних, местами спорных, но в совокупности для второго (❗️ ) прохода по ТВ очень приятная книга. Также вся эта тема сподвигла меня на ведение рукописного конспекта, который я буду со временем дополнять.
👀 Параллельно подбивал резюме и проекты, немного наполнял контентом свой Github, подавал резюме в компании.
Результат пока следующий: 4 заявки, из которых:
1 – без ответа;
1 – с негативным (уже нашли человека);
1 – с собеседованием и жду тестовое;
1 – с успешным первым тестовым и отправленным вторым.
Есть чувство, что второе тестовое тоже хорошее, время покажет.
🖼 Картинка к посту – шедевр. Мне всегда нравилось представлять, что происходит в этот момент у бедных фотомоделей в голове: "Господи, что я тут делаю?", да и эти улыбки – им анекдот на питоне написали?
class Man:
def _ _init_ _(self, hat):
self._hat = hat
self.fits = True
#hard #learn #progress #offer
Долго я не писал ничего на эту тему, хотя уже есть некоторые движения.
Опишу кратко: ранее в рамках вуза уже видел SQL, знал материал до INNER/OUTER JOIN и подзапросов, хотел заглянуть дальше, плюс расширить кругозор. Со своей задачей курс справился отлично: минимум теории, максимум практики, повествование сопровождается советами бывалого человека. Если нужен SQL на уровне побочной работы, типа Data Engineer, то хватит с головой. Курс занял 5 дней (Прямо как обещают на обложках учебников).
Очень легко читается, много подробностей: местами лишних, местами спорных, но в совокупности для второго (
Результат пока следующий: 4 заявки, из которых:
1 – без ответа;
1 – с негативным (уже нашли человека);
1 – с собеседованием и жду тестовое;
1 – с успешным первым тестовым и отправленным вторым.
Есть чувство, что второе тестовое тоже хорошее, время покажет.
class Man:
def _ _init_ _(self, hat):
self._hat = hat
self.fits = True
#hard #learn #progress #offer
Please open Telegram to view this post
VIEW IN TELEGRAM
Если кратко, то собеседование можно описать словом кринж (от англ. to cringe, прим. автора).
Мне пришло в голову начать рассказ о себе вообще не с профильных вещей: рассказать о левых ЯП, которые я тоже знаю и использовал, потом начал говорить про свой МаТеМаТиЧеСкИй бэкграунд, заявил, что хорош в теорвере, сразу же получил запрос "Сформулируйте ЦПТ Ляпунова", на что я неловко парировал "Знаю только в классической формулировке" и сказал что-то невнятно-неточное, но в общем по теме.
Когда дошло до матриц, попросили обратить матрицу размера 2х3. Я с умным видом заявил, что дострою рядом единичную матрицу и проведу элементарные преобразования, на что меня вполне справедливо прервали фразой "На самом деле, эта матрица необратима". МаТеМаТиК: забыл, что обратимость матриц требует квадратной формы. Через полчаса после собеседования вспомнил, что можно было бы отразить удар псевдообратной матрицей (да-да, тот самый момент, когда понимаешь через несколько часов, как надо было ответить хулигану в споре, чтобы все закончилось в твою пользу). Делаю вывод: надо пройтись и по линейной алгебре тоже.
Многие вопросы я сразу отсекал фразами наподобие "Теорию алгоритмов я не знаю, знаю только практику" (чукча не читатель — чукча писатель). По поводу TF и PyTorch были вопросы, но я даже ONNX еще не трогал: сказал, зачем он идейно, не более. Диалог получился тупиковый: спрашивать почти нечего, а обсуждать не выходит, итог: сошлись на тестовом задании.
Потом вопросы общего плана: как я отстаиваю свою точку зрения в споре, что мне нужно для комфортной работы, ожидаемая ЗП и т.д. Ознакомился с задачами компании, мне все понравилось, в целом очень близко к тому, что меня интересует, но дальше уже вопрос, насколько я буду интересен компании.
С теорией вероятностей у меня все хорошо в том смысле, что я достаточно точно оцениваю вероятности событий в своей жизни, поэтому не стал удивляться, что тестовое задание пока не прислали. Буду верить, что это потому что люди заняты своими делами...
Stay tuned
#offer
Please open Telegram to view this post
VIEW IN TELEGRAM
🌚1
Тестовое задание: часть 1 ✍️
Как я ранее писал, мне после собеседования обещали тестовое задание — вот и оно.
Формулировка задания📁
Используя PyTorch, обучить модель классификации лица по 5 основным эмоциям (angry, sad, neutral, happy, surprise). На выходе получить модель, логируя результаты в tensorboard, сравнить accuracy на train-е и валидации. Конвертировать модель в формат ONNX и выполнить инференс в ONNX Runtime. Скрипт должен иметь возможность получать изображение как с Web-камеры/видео, так и из файла.
Таким образом, требуется прислать архив (или ссылка на google colab), содерджащий все этапы обучения модели, а также конвертированный бинарный файл и скрипт инференса.
Срок — 2 дня.
Первое впечатление🐻
На собеседовании я признался, что:
1) Модели в PyTorch я не обучал: ранее обучал лишь на TF или использовал предобученные в PyTorch.
2) Из ONNX слышал только эти четыре буквы и знаю, зачем они нужны, но сам не трогал.
3) Вполне логичный вывод из пункта 2, что инференс моделей я не делал.
4) JavaScript не знаю (почти) и не пользовался (может, это фантомное воспоминание, но такой вопрос как будто был).
А также дополню:
5) С TensorBoard я ранее тоже не взаимодействовал.
Первый вопрос, который возник: что же я делал все эти годы?
Второй: Возможно ли за 2 дня изучить PyTorch, TensorBoard, ONNX, ONNX Runtime и JS?
Третий: Это стресс-тестовое задание, которое специально подбивалось под то, чего я не умею, чтобы проверить меня на прочность?🤔
Процесс работы⏲
0⃣ Нулевой день
На самом деле, у меня было еще несколько часов за день до решения задания, которые я потратил на изучение известных датасетов, моделей, SOTA-решений, в какую сторону правильнее копать, плюс куча полезных ссылок, связанных с подбором параметров для обучения, на которые было удобно ссылаться впоследствии.
Что в итоге:
Датасет – FER-2013. ЧБ-изображения размера 48х48 в количестве 36к.
Модели – VGG, ResNet. Показывали в среднем более высокое качество, часто используются. EfficientNet хорошо себя проявил на другом подобном датасете, но вполне возможно, что разница в 6 лет между этим датасетом и моделью сыграла свою роль в ее популярности.
1️⃣ Первый день
Сперва я занимался предобработкой датасета, изучив баланс классов и сами изображения для каждого класса. Вердикт: не оч. Неудивительно, что SOTA-решения показывают точность около 75%, с такими-то рожами... Но, в связи с тем, что это единственный хорошо изученный и одновременно небольшой датасет, полюбить пришлось и козла.
Первым выбором стала модель VGG19, усовершенствованная версия которой светилась среди SOTA-решений этого года (Спустя 10 лет не только я оказался в этом богом забытом месте). SOTA-версию идейно я понял, а вот технически не понял, поэтому не стал юзать, хотя можно было бы изучить. Просто не захотел напороться на подводные камни там, где имел слабое представление и кучу зависимостей от старых версий библиотек.
К концу первого дня у меня была первая готовая модель, с учетом первой пробы пера обучения на PyTorch и логгирования в TensorBoard, а также ударов всеми частами тела о❌ CUDA Out Of Memory посреди процесса, которые, конечно, не решались ни одним известным способом, потому что повторная загрузка модели съедала видеопамять, а назад не возвращала ни при каких обстоятельствах.
Точность: 73%. Близко к SOTA, за исключением того, что у меня лишь 5 классов, а не 7, как в изначальном датасете.
На этом день закончился.
#offer
Как я ранее писал, мне после собеседования обещали тестовое задание — вот и оно.
Формулировка задания
Используя PyTorch, обучить модель классификации лица по 5 основным эмоциям (angry, sad, neutral, happy, surprise). На выходе получить модель, логируя результаты в tensorboard, сравнить accuracy на train-е и валидации. Конвертировать модель в формат ONNX и выполнить инференс в ONNX Runtime. Скрипт должен иметь возможность получать изображение как с Web-камеры/видео, так и из файла.
Таким образом, требуется прислать архив (или ссылка на google colab), содерджащий все этапы обучения модели, а также конвертированный бинарный файл и скрипт инференса.
Срок — 2 дня.
Первое впечатление
На собеседовании я признался, что:
1) Модели в PyTorch я не обучал: ранее обучал лишь на TF или использовал предобученные в PyTorch.
2) Из ONNX слышал только эти четыре буквы и знаю, зачем они нужны, но сам не трогал.
3) Вполне логичный вывод из пункта 2, что инференс моделей я не делал.
4) JavaScript не знаю (почти) и не пользовался (может, это фантомное воспоминание, но такой вопрос как будто был).
А также дополню:
5) С TensorBoard я ранее тоже не взаимодействовал.
Первый вопрос, который возник: что же я делал все эти годы?
Второй: Возможно ли за 2 дня изучить PyTorch, TensorBoard, ONNX, ONNX Runtime и JS?
Третий: Это стресс-тестовое задание, которое специально подбивалось под то, чего я не умею, чтобы проверить меня на прочность?
Процесс работы
На самом деле, у меня было еще несколько часов за день до решения задания, которые я потратил на изучение известных датасетов, моделей, SOTA-решений, в какую сторону правильнее копать, плюс куча полезных ссылок, связанных с подбором параметров для обучения, на которые было удобно ссылаться впоследствии.
Что в итоге:
Датасет – FER-2013. ЧБ-изображения размера 48х48 в количестве 36к.
Модели – VGG, ResNet. Показывали в среднем более высокое качество, часто используются. EfficientNet хорошо себя проявил на другом подобном датасете, но вполне возможно, что разница в 6 лет между этим датасетом и моделью сыграла свою роль в ее популярности.
Сперва я занимался предобработкой датасета, изучив баланс классов и сами изображения для каждого класса. Вердикт: не оч. Неудивительно, что SOTA-решения показывают точность около 75%, с такими-то рожами... Но, в связи с тем, что это единственный хорошо изученный и одновременно небольшой датасет, полюбить пришлось и козла.
Первым выбором стала модель VGG19, усовершенствованная версия которой светилась среди SOTA-решений этого года (Спустя 10 лет не только я оказался в этом богом забытом месте). SOTA-версию идейно я понял, а вот технически не понял, поэтому не стал юзать, хотя можно было бы изучить. Просто не захотел напороться на подводные камни там, где имел слабое представление и кучу зависимостей от старых версий библиотек.
К концу первого дня у меня была первая готовая модель, с учетом первой пробы пера обучения на PyTorch и логгирования в TensorBoard, а также ударов всеми частами тела о
Точность: 73%. Близко к SOTA, за исключением того, что у меня лишь 5 классов, а не 7, как в изначальном датасете.
На этом день закончился.
#offer
Please open Telegram to view this post
VIEW IN TELEGRAM
Paperswithcode
Papers with Code - FER2013 Dataset
Fer2013 contains approximately 30,000 facial RGB images of different expressions with size restricted to 48×48, and the main labels of it can be divided into 7 types: 0=Angry, 1=Disgust, 2=Fear, 3=Happy, 4=Sad, 5=Surprise, 6=Neutral. The Disgust expression…
👍2🍌1
Приходишь пожарить картофель – уходишь Дата-саентистом
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