Я веду семинары по машинному обучению на ФКН ВШЭ. Чтобы понять, как работает градиентный спуск, нужно написать его руками. Чтобы разобраться, почему модель переобучается, нужно самому поковырять гиперпараметры, посмотреть, как меняются кривые обучения, попробовать регуляризацию. Лекция даёт интуицию, но интуиция закрепляется через эксперимент.
И вот тут начинается проблема. Классический формат семинара: я стою перед аудиторией, пишу код, объясняю. Студенты смотрят. Кто-то конспектирует, кто-то фотографирует экран. Но не делает.
Мне хотелось, чтобы семинар был не демонстрацией, а совместным экспериментом. Чтобы я показывал идею, а студенты тут же пробовали: меняли параметры, ломали код, смотрели что получится. В том же ноутбуке, в реальном времени.
Почему не Colab и не всё остальное
Первый кандидат очевиден: Google Colab. Им пользуются все, он бесплатный, он в облаке. Маленькая деталь — в нём нет совместного редактирования. Сервис, в названии которого буквально стоит слово “collaborative”, не позволяет двум людям одновременно редактировать один ноутбук.
Я посмотрел на альтернативы. DeepNote, Hex, Noteable — каждый по-своему хорош, но у каждого нашлось то, что не подошло. Где-то ограничения бесплатного тарифа, где-то проблемы с кастомизацией среды, где-то избыточная привязка к облаку.
Поэтому я сделал своё.
Анатомия проекта
Весь проект — это Dockerfile, Makefile и пара конфигов.
Среда
Базовый образ — python:3.11-slim. Поверх него ставится JupyterLab и набор расширений, каждое из которых решает конкретную проблему:
pip install --no-cache-dir
jupyterlab jupyter-collaboration
jupyterlab-git jupyterlab-lsp python-lsp-server
jupyterlab-code-formatter black isort
ipywidgets
jupyter-ai[anthropic,openai] langchain-openai langchain-community
lckr-jupyterlab-variableinspector
jupyter-collaboration — ядро всей затеи. Это то, что превращает JupyterLab из одиночной IDE в подобие Google Docs: несколько человек одновременно редактируют один ноутбук, каждый видит курсоры остальных. Технически под капотом CRDT (Conflict-free Replicated Data Types) — та же штука, что работает в Figma и в том самом Google Docs.
jupyterlab-code-formatter + black + isort — единый стиль кода для всех. Когда тридцать человек пишут в одном ноутбуке, без автоформатирования начинается хаос. Black решает это радикально: один стиль, никаких споров.
Терминал
Отдельная тема — терминал. JupyterLab умеет открывать терминал прямо в интерфейсе, и по умолчанию это голый bash. Я настроил zsh с Oh My Zsh и Powerlevel10k:
RUN useradd -ms /bin/zsh jovyan
RUN sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" "" --unattended
&& git clone --depth=1 https://github.com/romkatv/powerlevel10k.git $ZSH/custom/themes/powerlevel10k
&& git clone --depth=1 https://github.com/zsh-users/zsh-autosuggestions $ZSH/custom/plugins/zsh-autosuggestions
&& git clone --depth=1 https://github.com/zsh-users/zsh-syntax-highlighting $ZSH/custom/plugins/zsh-syntax-highlighting
Зачем? Во-первых, автодополнение команд и подсветка синтаксиса реально ускоряют работу. Во-вторых, студенты видят, как может выглядеть терминал, если его настроить. Многие из них до этого пользовались только стандартным терминалом и не подозревали, что бывает иначе. Маленький бонус — zsh подключается как дефолтный шелл для Jupyter одной строчкой:
RUN mkdir -p ~/.jupyter
&& echo "c.ServerApp.terminado_settings = {'shell_command': ['/bin/zsh']}"
> ~/.jupyter/jupyter_server_config.py
Про AI
Здесь нужно сказать кое-что важное.
Студенты пользуются LLM. Это не проблема, которую нужно решать, это реальность, к которой нужно адаптироваться. Запрещать — глупо. Делать вид, что этого нет — ещё глупее.
Я за то, чтобы студенты пользовались всем, что есть. Но я хочу видеть как они это делают. Промпт — это тоже код, в каком-то смысле. Умение правильно сформулировать задачу для LLM — навык, который стоит развивать осознанно. Плохой промпт выдаёт плохой код, который студент бездумно копирует и не понимает. Хороший промпт — это уже полпути к решению, потому что чтобы его написать, нужно понимать задачу.
Поэтому в контейнер встроен jupyter-ai — AI-ассистент прямо в интерфейсе JupyterLab. Студенты могут общаться с моделью, не переключаясь в другое окно. А я вижу эти диалоги и могу комментировать: “смотри, ты спросил вот так, а если переформулировать — получится точнее”.
ENV OPENAI_API_BASE=https://your-api-provider.com/v1
ENV OPENAI_API_KEY=your-api-key
Провайдер — любой OpenAI-совместимый API. Конкретный выбор зависит от бюджета и предпочтений.
Хостинг за ноль рублей
Отдельный сервер для этого мне арендовать не хотелось. Семинар идёт полтора часа раз в неделю — держать ради этого VPS с GPU или даже без избыточно. Поэтому схема такая: контейнер запускается на моём ноутбуке, а в интернет выставляется через SSH-туннель.
У меня есть домен jupyter.sleep3r.ru и простой VPS, на котором стоит nginx. Вся связка:
Браузер студента → jupyter.sleep3r.ru → Nginx (VPS) → SSH-туннель → Мой ноутбук → Docker
В Makefile это выглядит предельно просто:
run:
docker stop jupyter-collab || true
docker build -t jupyter-collab .
docker run -it --rm -p 8888:8888 jupyter-collab
host:
ssh -R 9999:127.0.0.1:8888 root@your-vps-ip
make run — собирает образ и запускает контейнер. make host — пробрасывает порт на VPS. Nginx на VPS проксирует запросы с домена на localhost:9999. Приходишь на семинар, открываешь ноутбук, выполняешь две команды, скидываешь ссылку в чат группы. Всё.
Про безопасность
JupyterLab запущен без токена и пароля. Это осознанный компромисс: сервер работает только пока я на семинаре, доступ — только по ссылке, и всё крутится на моём ноутбуке, который я контролирую. Для продакшена так делать, конечно, не надо. Для полуторачасового семинара — вполне.
Если нужна авторизация — есть JupyterHub. Если нужен HTTPS — Let’s Encrypt на nginx. Но для моей задачи это было бы overengineering.
Как это меняет семинар
Раньше семинар выглядел так: я пишу код на проекторе, объясняю логику, студенты смотрят. Кто-то пытается повторить у себя, натыкается на проблему с окружением, отстаёт, теряет нить. Кто-то просто записывает видео экрана и надеется разобраться потом (обычно не разбирается).
Сейчас всё иначе. Я реализую, допустим, функцию потерь — они видят мой курсор. Я говорю: “попробуйте поменять learning rate на порядок” — они меняют прямо в том же ноутбуке и тут же видят результат. Кто-то застрял — я замечаю, что его курсор не двигается, и подхожу помочь. Кто-то нашёл интересную закономерность в данных — я показываю его график всей группе.
Проблемы с окружением исчезли полностью. Все работают в одном и том же контейнере. Нет “а у меня Windows”, нет “а у меня другая версия numpy”, нет “а я не могу установить torch”. Для ML-курса это особенно больная тема — зависимости тяжёлые, конфликтуют друг с другом, и на каждый семинар найдётся студент, у которого что-то не импортируется. Здесь всё решено на этапе сборки образа. Все материалы курса клонируются в контейнер:
RUN git clone https://github.com/darkydash/ml_hse_2024.git /home/jovyan/ml_hse
Студент открывает ссылку — и перед ним готовая среда с кодом, данными и работающими зависимостями.
Проект на GitHub: github.com/sleep3r/jupyter-collab
git clone https://github.com/sleep3r/jupyter-collab.git
cd jupyter-collab
make run
Если преподаёте ML, Data Science или что-то ещё, где ноутбуки — основной рабочий инструмент — попробуйте. Один вечер настройки, и семинар превращается из демонстрации в совместный эксперимент.
Автор: sleep3r


