Warning: Undefined array key 0 in /var/www/tgoop/function.php on line 65

Warning: Trying to access array offset on value of type null in /var/www/tgoop/function.php on line 65
20 - Telegram Web
Telegram Web
Channel photo updated
Привет!

Я работаю инженером по машинному обучению и хочу делиться тут своими мыслями, наблюдениями и просто интересными вещами из мира программирования.

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

Полетели 🙃
Уже давно меня посещают мысли, что надо бы попробовать что-то кроме matplotlib для визуализации данных. Но вместо этого я посмотрел плейлист по её использованию 🌝

Чего-то сверхъестественного не узнал, но было полезно, например:
- здание стиля через plt.style.use('fivethirtyeight')
- задание цветов через шестнадцатеричное значение
- заливка пространства между несколькими графиками
- возможность постоянного обновления графика matplotlib.animation.FuncAnimation
- уменьшение рамок через plt.tight_layout()

Также у меня иногда возникало ощущение, что какой-то матплотлиб нелогичный в плане настроек. Однако после просмотра плейлиста, кажется, что это было из-за моих пробелов.

Вообще, советую канал Кори Шафера. Объясняет очень понятно и без воды.
Есть такая библиотечка Pydantic. Обычно её используют, когда нужно валидировать какие-то данные (запросы к апи и ответы от него как наиболее частый случай).

Однако Pydantic также помогает организовать хранение настроек вашего приложения. Для этого есть класс BaseSetting.

До того, как я перевёл на него настройки, в проекте было много классов, в которых атрибутами хранились конфигурации. Что-то типа

class RedisConfig():
host = "redis"
port = 5678

Что я получил после перехода на настройки через Pydantic?

1. Получил одну точку доступа ко всем настройкам, собрав их в один класс. Также удобно логировать ваши настройки, например, при старте приложения.

class Settings(BaseSettings):
REDIS: RedisSettings = RedisSettings()
...

2. Есть валидация значений. Можно задать ограничения, что мол NUM_WORKERS у вас не более 4. Также он умеет парсить строку false, 0 в False. Можно определять валидацию на основе комбинации полей.
3. Пайдантик умеет читать настройки из нескольких мест. Удобно задавать чтение из .env. При этом если вы явно задали переменную в окружении, то она в файле будет проигнорирована.
4. При парсинге понимает, что строка false - это булевый False. Сколько раз я уже наступал на bool('false') is True
5. Можете также задать префикс. Допустим, у вас есть настройка PORT и префикс REDIS. Тогда в .env файле у вас будет REDIS_PORT, а в коде вы префикс не пишите.

class RedisSettings(BaseSettings):
...
class Config:
prefix = 'REDIS_'

Вроде мелочи, а появляется порядок. Если интересно, то вот хороший видосик на эту тему.
Channel name was changed to «Записки MLEшника»
Не так давно я стал тимлидом небольшой команды. Я был основным контрибьютером в проект, и когда он приближался к продакшену, в него добавили людей. Стал лидить.

Для меня это было в новинку. Считаю, что идёт не без косяков, но я стараюсь.

Чтобы побыстрее вникнуть, решил почитать книжки по менеджменту (вот уж не думал, что до такого дойдёт 😅). Вчера дочитал "45 татуировок менеджера" Максима Батырева.

Книга читается легко. В ней 45 поучительных историй из карьеры автора, каждая из которых оставила на авторе "татуировку" на всю жизнь. Все мысли доносятся достаточно прямолинейно. Как всегда нет каких-то секретных знаний. Всё вроде бы банально. Например, "То, что очевидно для вас, не очевидно для других", "Не делайте работу за подчиненных" или "Хвалите людей".

Однако я нашёл и много новых для себя мыслей. Например, мне понравилась татуировка "Любые ваши слова могут стать задачей". Автор как-то пожурил свою подчиненную, что ей нужно больше стараться. Сказал и забыл. Девушка же начала работать над собой и показывать хорошие результаты. Вроде бы всё гуд, да? Вот только без обратной связи (доброго слова или похвалы) она впала в депрессию. Ведь так старалась для начальника, а ему хоть бы что. Вот так вот. 👀

Были также и про действия в кризис, и про ошибки, и про выращивание людей.

В общем, мне зашло. А вы что читали в последний раз?
Вокруг столько шума о PyTorch 2.0. Решил узнать, что там новенького

PyTorch стал быстрее 💁‍♀️

Разработчики комментируют это так:
PyTorch 2.x: faster, more pythonic and as dynamic as ever

Добавили torch.compile, который автомагически ускоряет ваши модельки. Использовать его так: model = torch.compile(model)

Эту магию обеспечивают 4 новых технологии:
- TorchDynamo (разбирает ваш код: слои, if-ы, for-ы)
- AOTAutograd (строит ahead-of-time backward traces. То есть, способен наперед понять, какой backward у вас будет)
- PrimTorch (пересобирает ~2000 операторов PyTorch в ~250 примитивов PrimTorch)
- TorchInductor (компилирует код моделек в высокопроизводительный код бекенда)

Работает в следующей последовательности:
1. Построение графа
TorchDynamo и AOTAutograd строят граф вычислений вашей модели (forward, backward)
2. Оптимизация графа
PrimTorch разбивает операторы PyTorch на примитивы (Conv2d -> Conv + bias, BatchNorm -> mean + std, и т.д.)
3. Компиляция графа
TorchInductor компилирует граф в какой-либо бекенд (nvFuse, XLA, TensorRT, и др.)

Написано это на питоне и поддерживает dynamic shapes (вам не нужно перекомилировать модель, если поменялась, например, высота и ширина картинок в батче).

Обращаю внимание, что это работает не только на инференсе, но и на обучении 😯

Самый сок в том, что несмотря на новую версию (1 -> 2) обещают полную совместимость со старым кодом! Тестили это на 150 модельках для разных задач из библиотек HuggingFace, TIMM и TorchBench. Говорят, что не меняли ничего, кроме model = torch.compile(model) 🤩. Завелось в каждых 9 из 10 случаев. Говорят, что на NVIDIA A100 GPU ускорение в 20 / 50% на float32 / float16.

Как мы любим, параметров у torch.compile мало:
- mode
Говорит, что оптимизировать компилятору. default - быстро копилируется, модель не использует дополнительной памяти. reduce-overhead уменьшает оверхед от фреймворка, модель занимает немного дополнительной памяти. max-autotune дает самую быструю модель, но долго копилися, модель занимает еще больше памяти.
- dynamic
Нужно ли поддерживать разные шейпы входных данных. Если нет, то еще чуть быстрее.
- fullgraph
Копилирует всё в один граф. Скорость модели приближается к первой космической, но вероятно нужно будет допилить код, чтобы заработало.
- backend
Бекенд, в который компилить модель. Зависит от железа. Говорят, что сделали всё, чтобы было легко добавить новые. Астрологи прогнозируют рост числа бекендов.

А еще говорят, что DistributedDataParallel и FullyShardedDataParallel полностью работают, и вам не придется ничего менять.

Ну что сказать, пацаны вообще ребята 💪 Ждём, когда 2.0 станет stable версией.
Хочу рассказать о горячо любомом мной инструменте - pre-commit хуках. Начну немного издалека.

Если меня спросят: "В чем сила, брат?", я отвечу: "В унификации и стандартизации". Врядли бы мы достигли текущего уровня развития техники, не стандартизируй предки болты, транзисторы и еще куча всего. Программное обеспечение - не исключение.

Рекомендации по оформлению кода содержатся в PEP8 (стайлгайд к языку). Но лень же каждый раз дотошно редактировать все строки кода. Для автоматизации форматирования кода есть соответствующие инструменты - форматеры (formatters). Например, black (форматирует классы, функции, литералы и др.), isort (сортирует импорты по алфавиту) или pycln (удаляет неиспользуемые импорты). Запустив их по очереди, вы получите отформатированный по код. Чтобы дополнительно проверить на соответствие PEP8, можем запустить flake8 (проверяет, что код соответствует PEP8). Также flake находит неиспользуемые переменные, повтореную инициализацию или просто слишком сложные функции. Красота 🌝

Возник резонный вопрос: "А не многовато всего руками на каждый коммит запускать?!". Ответ убил - pre-commit хуки.

Эти ребята будут за вас запускать весь этот паровоз кода при каждом коммите (пуше, пуле - как настроите). Я влюбился в это с первого взгляда.

Очень советую данный инструмент командам. С момента добавления хуков в репозиторий у нас не было ни одного спора в пиарах по поводу кавычек, переноса строк и тому подобного 💁‍♀️. Также мы добавили эту проверку в CI, чтобы наверняка.

Также в хуки можно добавить mypy или даже dvc. Например, если хотите делать dvc push на каждый git push.

Тут пример моего конфига, который без особых изменений кочует из проекта в проект. На каждый коммит:
1. Запускает набор хуков из репы pre-commit (форматирует жейсоны, ямли, иксемели, рекваерменты)
2. Запускает pycln (удаляются все неиспользуемые импорты)
3. Запускает black (форматируется код по PEP8. Тут же, например, кавычки заменяются на двойные)
4. Запускает isort (переупорядочивает и сортирует импорты)
5. Запускает flake8 (проверяет, все ли у нас по PEP8. Также покажет, если остались неиспользуемые переменные)

Пайплайн использования такой:
1. Делаете комит
2. Хуки запускаются. Видят, что есть косяки. Редактируют код. Отменяют ваш комит.
3. Добавляете изменения, которые внесли хуки в комит.
4. Делаете комит

Вся эта красота устанавливается в две команды:
pip install pre-commit
pre-commit install
Когда дело касается установки или переустановки драйверов, куд и кудэнэнов, у меня возникает паника.

Сначала шёл по гайдам из интернета. Пока они свежие - всё ок. Как только чуть устареют - ловлил ошибки и с грустным лицом пытался чинить 🥲

Затем стал идти по гайдам Nvidia. Стало получше, потому что там описано, что менять под вашу версию. Если ставить на чистую систему, то в основном проблем нет. Нюансы возникают, когда надо обновить существующие версии. Выполнял действия из pre-installation actions, но то ли не хватает понимания, то ли руки не от туда растут 🌝

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

sudo apt-get --purge remove "*cublas*" "cuda*" "nsight*"
sudo apt-get purge nvidia*
sudo apt-get autoremove
sudo apt-get autoclean
sudo rm -rf /usr/local/cuda*
sudo reboot now

Дальше уже копируете команды по установке куды, куднн, докера, нвидиа тулкита. Красота
Внезапно Nvidia сделала отдельный нормальный сайт с документацией для Nvidia Triton Inference Server. Класс.

Вроде есть всё, что и на их гитхабе
Посмотрел лайвкодинг от создателя библиотеки FastAPI (Себастьян aka tiangolo) с громким названием "FastAPI for Machine Learning: Live coding an ML web application"

Ожидал секретного секрета по построению мл API, но увы. Себастьян сделал максимально базовый API.

Приложение представляло собой один эндпоинт /generate. В нем вызывается функция, которая генерирует нейронкой ответ (это был stable diffusion).

Если коротко, то рецепт простого ML web API из этого видео:
1. Сделайте отдельный модуль, в котором есть функция загрузки модели и получения предсказания. На функцию загрузки модели повесьте декоратор @lru_cache, чтобы грузить ее лишь раз.
2. Грузите модель на уровне модуля. То есть просто model = load_model() в начале скрипта.
3. Вызывайте функцию для получения предсказания в вашем эндпоинте. Эта функция не будет получать модель входным параметром, а использовать ту, которая объявлена в вашем модуле.

Вы получили API 🎉, в котором модель грузится один раз при старте. Быстро и достаточно эффективно. Если нужно что-то сложнее, то советует использовать уже специализированные фреймворки для сервинга мл.

Интересные наблюдения:
1. Для форматирования вместо black Себастьян использует ruff. Она должна быть сильно быстрее, потому что на RUST. Попробую на досуге.
2. Также использует автодополнение нейронками (наверное Copylot).
3. Тут объяснил как читать ошибку валидации от FastAPI. До этого я так и не прошарился в этом 🙂
По наводке коллеги прочитал годную статью по библиотеке requests.

Чего полезного умеют:
1. Можно задать базовый URL, а дальше передавать только путь до ресурса. Почему я об этом узнал только сейчас? 🥲
2. Можно проверить, что запрос отработал без ошибок (400ые и 500ые статус кодов). Если ошибка, то поднимается питонячее исключение. Если хочется, то есть возможность сделать такую проверку автоматической на каждый ответ через хуки.
3. Хуки 🙂. Получается, можно вешать выполнение любого вашего кода на все запросы, ответы и др.
4. Выставлять ретраи на определённые коды ошибок. При том время повторных запросов экспонентально увеличивается. По умолчанию делается 10 ретраев без какой-либо задержки по времени 🙈
5. Как мокать ответы при написании юниттестов.


Крч, годная и к тому же весьма короткая статья. Рекомендую к прочтению.
Просматривая видосики (1, 2) на ютубе, наткнулся на интересную библиотечку для инференса моделек от avito - Акведук.

Идея решения стандартная - разбить работу модели на этапы (например, препроцессинг, предсказание и постпроцессинг) и скейлить их отдельно. Этапы работают в отдельных процессах. Скейлить можно за счёт добавления процессов на конкретный этап. GPU экономится, потому что в CPU этапах вообще не будет дл фреймворков, а соответственно и пожирания ресурсов видеокарты.

Фишки:
- Pure python
Работает на основе multiprocessing из питона и имеет всего одну внешнюю зависимость. "No vendor lock" - хвалятся нам из доклада
- Plug-and-play
От датасаентистов требуется установить библиотеку, реализовать пару функций у класса Task (пример) и определить пайплайн обработки.
Flow(
FlowStep(PreProcessorHandler()),
FlowStep(ClassifierHandler()),
FlowStep(PostProcessorHandler()),
)
- Таски переходят между этапами через очереди. При этом реализована возможность немного подождать, чтобы накопить батч
- Передача данных между этапами происходит через SharedMemory
- Production ready
Есть метрики (размеры очередей, время перехода между этапами и др.), подключается Sentry, Graceful Shutdown (если одна таска умерла, то начатые продолжат выполнение и завершатся), хелсчеки процессов.

Выглядит прикольно. 140 звезд на гите, комиты каждый месяц. Надо бы попробовать
Новогодние праздники подошли к концу, и я снова в деле 🙂

Не так давно писал, что наткнулся на быструю библиотечку-замену flake8 - ruff. Хайп вокруг неё крепнет, поэтому пришло время попробовать.

Позиционируется как замена целой кучи инструментов на спидах
Ruff can be used to replace Flake8 (plus a variety of plugins), isort, pydocstyle, yesqa, eradicate, pyupgrade, and autoflake, all while executing tens or hundreds of times faster than any individual tool.

На рабочем проекте работает реально заметно быстрее flake8. Ruff отрабатывал мгновенно, когда flake думал секунды две.

Как запустить:
- pip install ruff
- добавляете в прекоммит конфиг
- пару настроек в pyproject.toml (например, вот и вот)

Основные настройки (имхо):
- line-length
- select = ["E", "F"] (какие правила ключить. Выбор большой)
- fix (включаете форматирование)

Кайфуете

Попробую добавить в рабочий проект и плотненько попользоваться. Выглядит очень интересно 👀
Прочитал книжку Чистый Python

Выбор на нее пал из-за высоких оценок, и потому что коллеги устроили ее совместное чтение. На них я не пошел, но интерес возник 👀

Книга родилась из серии публикаций автора в твитере. Этакий сборник фишек и советов. Кстати, у него прикольный блог.

На мой взгляд, фраза "Тонкости программирования для профи" не подходит книге. Я ее интерпретирую так: если ты профи, то сейчас узнаешь тонкости программирования. А по факту тут скорее: прочтешь и узнаешь тонкости, которые знают профи.

Мне понравилось, что после каждой главы есть ключевые выводы, где прямо говорят - если А, то используй list, если B, то deque. Люблю когда прямо и по делу. Потом можно использовать как справочник.

В общем, если вы понимаете декораторы, итераторы, распаковку и использовали defaultdict, то я бы прошел мимо. А если нет, то вполне хорошая книга, чтобы познакомиться без глубокого погружения в детали.
Последнее время занимался тем, что реализовывал нейронку с нуля на numpy. В своё время я как-то перескочил этот момент и сразу начал делать эксперименты. Со временем я понял, что мой мозг часто переоценивает глубину понимания темы, и проникся подходом "Реализуй свой X" (есть прикольный репозиторий с примерами таких реализаций).

Я поставил себе цели:
1. Сделать персептрон, в котором я руками без абстракций буду считать все градиенты
2. Сделать CNN, чтобы разобраться в LayerNorm (хотел запилить batchnorm и к нему за компанию layernrom)
3. Сделать resnet
4. Сделать unetr

Последние две я отложил. Отчасти потому, что уже появляются разветвления в структуре сетки, и не так красиво реализовывается без автограда (а я хотел явно определять градиенты слоев), отчасти потому, что затянул. Думаю, что когда-нибудь сделаю это. Может быть даже на jax 🙃.

Перцептрон оформлен. Лежит тут одним файлом. Реализация хорошо дополняет вот это видео из курса deep learning на пальцах.

CNN на подходе. Планирую закончить на этой неделе.

Очень помогла разобраться эта книга, где ты шаг за шагом реализуешь компоненты нейронок с нуля. Всем советую. Хорошая подача и без воды.

Вообще, я написал пост на хабр, но оказывается, там нельзя с улицы написать статью. Кажется, что модераторов мой контент не впечатлил (еще бы, очередная нейронка с нуля ))0) ), поэтому будет пост в телеграме.

P.S. Со следующей недели астрологи объявят месяц инфры. Буду учить новое и писать код. Ждите годноты

UPD. Статью на хабр все-таки одобрили
Как говорил Сократ: "Я знаю только то, что ничего не знаю". Словил сегодня вечером это чувство, когда блуждал по репе Тинькоф.

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

Ех, вот и как развиваться, когда глаза разбегаются 🥲
Снова годнота.

Репа с фишками, чтобы стать джедаем командной строки.

Я большой любитель шорткатов, поэтому дико кайфанул с этого
Используйте ctrl-w в Баше для того, чтобы удалить последнее слово в команде; ctrl-u для того, чтобы удалить команду полностью. Используйте alt-b и alt-f для того, чтобы бегать между словами команды, ctrl-a и ctrl-e для того, чтобы переместиться к началу и концу строки соответственно, ctrl-k для того, чтобы удалить часть команды от текущей позиции до конца строки, ctrl-l для того, чтобы очистить экран. Гляньте на man readline, чтобы узнать о всех клавиатурных сочетаниях Баша. Их много! Например, alt-. бежит по предыдущим аргументам команды, а alt-* раскрывает глоб (globbing).

Крч, это мы смотрим 👀
2025/06/25 17:49:08
Back to Top
HTML Embed Code: