Русский
Русский
English
Статистика
Реклама

Fast.ai

Аналог фейсбучной ленты для Телеграма. Тупенький ИИ OLEG

09.05.2021 14:14:20 | Автор: admin

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


И сделал: OLEG AI


Идея


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


Я люблю Телеграм. И люблю иногда потупить в какую-нибудь ленту "информационного корма". В разное время я любил поразлагаться на Лепре, Дёти, Пикабу, но в итоге всеми этими источниками сладкого яда я остался недоволен.


И тогда я подумал: в Телеграме ведь куча источников, но Телеграм их не агрегирует по типу Фейсбука. Телеграм не собирает с нас лайки. Да, лайки это чистое зло и гореть им в аду, но иногда так хочется лайкнуть жопу фотомодели, нет?


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


Стоит заметить, что я не профессиональный программист, и опыта в программировании у меня не было примерно с 2004 года. Так что, помимо собственно нейросетей, мне пришлось еще и быстренько расчухать основы Питона, вспомнить SQL, погрузиться в Докер и практику CI/CD. Это было потрясающе.


Процесс


Начал я с того, что убедился, что задуманное мной в принципе возможно.
Мне нужно было слушать некий набор каналов (пабликов) Телеграма, и передавать посты подписчикам бота. Поизучал доки Telegram Bot API, понял, что при помощи одного только Telegram Bot API сделать это не получится. Бот не может подписываться на каналы по своему выбору.


Придется писать своего клиента для Telegram. К счастью, с нуля писать не пришлось: есть неплохая основа в виде либы python-telegram. Апишка там не самая проработанная, но самое тяжелое она делает за нас: процесс логина, предоставляет классы для создания запросов и получения асинхронных ответов от TDLib. TDLib это сишная либа-Телеграм-клиент. Так что я вооружился доками от TDLib, и принялся ковырять ее. Заодно разобрался как работает Телеграм, прикольно.


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


Как я представлял себе то, что хочу сделать:


Довольно быстро я накидал приложение, схема которого в итоге оказалась примерно такой:


Блоки объекты.
Тут не все объекты, конечно, а только основные.
Admin это обычный аккаунт Телеги, залогиненный через TDLib и подписанный на интересующие нас каналы. Слушает апдейты каналов и записывает. Причем он не пишет контент, а только метаданные сообщения: tg_channel_id и tg_msg_id. По этим двум полям можно найти любое сообщение в Телеге (если оно было показано аккаунту).
Bot аккаунт бота, общение с которым тоже происходит через TDLib (до этого я даже не знал, что так можно, думал, что только через Bot API можно работать с ботом).
К ним подключены TDLibUtils всякие методы для работы с TDLib низкого уровня. Типа, найти юзера, найти сообщение, вытащить имя канала из инфы о канале и тп.
OlegDBAdapter методы для работы с базой (get_users, get_posts etc)
OlegNN то, ради чего всё затевалось алгоритм коллаборативной фильтрации. Правда, по итогу никакой нейросети там внутри не осталось, но об этом позже.
Joiner логика подписки на каналы. Нельзя так просто взять список каналов и подписаться на него: быстро срабатывает рейт лимитинг. На вычисление безопасной логики подписки, логирование, организацию базы ушло около недели.
APScheduler сторонняя либа-планировщик тасков. Использую для периодической рассылки сообщений подписоте.


Контент


Схемы работы с контентом могло быть две:


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

Чтобы не выкачивать медиа, в Телеграме у каждого ассета есть свой уникальный айди. Когда собираешь сообщение, можно вместо файла указать этот айди. Я думал, что этого достаточно, но посты не отправлялись. Оказалось, дело в том, что аккаунт отправляющий медиа с помощью айдишника, должен сначала этот айдишник встретить в Телеграме. Например, получить сообщение с этой картинкой. Проблема в том, что у меня за получение сообщений отвечает аккаунт Admin, а за отсылку Bot. Я долго думал, и в итоге придумал: а что, если каждое полученное сообщение Админ будет форвардить Боту, таким образом Бот "увидит" всё медиа. Это сработало. Я боялся, что за такое количество форвардов поймаю рейтлимит и огребу гемморой, обходя это дело, но в итоге обошлось.


Каналы


Не мудрствуя лукаво, я купил список топовых каналов по количеству подписчиков у TGStat.ru. 45 категорий по 100 каналов, вышло 4500 каналов. Пока этого хватает, возможно допишу еще паука, который сам лезет в каналы упомянутые в постах, и подписывается на них. Я сразу сделал логику Joiner'а так, чтобы можно было легко докинуть ему в пул свежих каналов, а он с ними сам разберется.


С каналами уперся в неожиданный лимит: один аккаунт Телеграма может быть подписан только на 500 каналов. Придется теперь превращать Admin'а в хаб, управляющий несколькими аккаунтами, и добавлять аккаунты, следить какой аккаунт подписан на какой канал, и всё в таком духе. Пока отложил это, решил, что для старта 500 каналов хватит.


Нейросеть, которой нет


Затевал я всё это ради практики программирования нейросетей, как вы помните.


Когда внедряешь машинное обучение, сталкиваешься с тем, что это всё-таки черный ящик, у которого наружу торчат некие гиперпараметры, а на выходе получается какой-то результат, причем, если при разработке ты правильно учел всякие там размерности тензоров и не наделал глупых ошибок, оно будет работать. Как-то будет. Будет ли результат, выдаваемый ящиком, иметь какой-то смысл это вопрос. Чтобы ответить на этот вопрос, приходится сидеть, расковыривать ее так и эдак, смотреть на результаты разных этапов вычисления, читать тематические статьи, узнавать практику, прикидывать baseline результат и сравнивать с тем, что получилось у тебя. Короче, это весело. На первых этапах машинное обучение создает гораздо больше проблем, чем решает. И заставить его реально решать какие то проблемы, создавая прибавленную стоимость для вашего проекта большая работа, не очень похожая на обычное программирование и дебаг.


Так что трижды подумайте, прежде чем бросаться решать проблемы этим методом. ML требует жирных проблем, которые точно не решаются классическими методами и сулят повышенный профит. ML ради ML истощает бюджеты и роняет мораль!


Прежде чем прикрутить ML к Олежке, я долго крутил системы коллаборативной фильтрации на тренировочном датасете MovieLens (ой, только не надо).
Я делал решение как для задачи регрессии (в конце один нейрон который угадывает рейтинг фильма по шкале 1..5), так и для задачи классификации (в конце 5 нейронов, каждый отвечает за свой рейтинг 1..5, и какой нейрон сильнее активируется, тот рейтинг мы и считаем за предсказание).
Эти изыскания заняли прилично времени, кажется 2-3 недели. По ходу дела я даже с нуля написал классификатор MNIST, благодаря чему сильно продвинулся в понимании работы нейронок. Кто еще не делал этого: очень рекомендую. Времени занимает от силы 2 дня, а пользу приносит годами.


В итоге стало ясно, что если задачу рассматривать как регрессию (а не классификацию), предсказательная сила модели будет намного лучше, так я и сделал.


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


Embeddings


Идея простая, но гениальная: каждой сущности (юзеру, посту) противопоставляется вектор параметров. Например, K=10 параметров. Они еще называются латентные факторы (latent factors).
Получается, что если у нас N юзеров, то мы можем расположить все вектора друг под другом, и получить тензор формы (N,K). Матрицу из N рядов и K колонок, если по-колхозному.
Для постов получится такой же тензор, только другой.
Эти тензоры называются эмбеддингами.
Cистема спрашивает у алгоритма фильтрации: вот есть юзер U, пост P, предскажи мне рейтинг, который юзер поставит посту.
В самом простом варианте (без нейронки) мы ищем в тензоре юзеров нужный ряд соответствующий юзеру, в тензоре постов вектор поста, и перемножаем эти два вектора (как в школе). На выходе получается скаляр (число) это и есть предсказанный рейтинг.
В варианте посложнее, эти два вектора подаются на вход нейросети, и дальше сигнал продвигается уже механизмом нейросети. На выходе один нейрон, величина активации которого скаляр и есть предсказанный рейтинг.


Почему эти тензоры назвали отдельным словом "эмбеддинг", что в них особенного? Дело в том, что эмбеддинг это первый шаг вычисления результата работы нейронки.
Когда мы ищем нужный ряд для юзера или поста в тензоре, мы могли бы просто взять индекс нужного ряда, и вытащить вектор соответствующий этому индексу. Но операция "взять ряд по индексу" не является алгебраической, это программатик-операция работы с памятью. Это означает, что данная операция разрывает граф вычислений градиента нейросети. Градиент можно вычислить только для алгебраических операций. То есть, во время обучения механизм back propagation не сможет дойти до самого конца непосредственно до эмбеддинга. Как же быть?
Вместо того, чтобы брать ряд тензора по индексу, мы можем умножить этот тензор на one-hot-encoded vector. Например, нам нужен третий по счету ряд тензора. Умножаем тензор на вектор [0 0 1 0 ...] На выходе получаем нужный нам ряд тензора. Это алгебраическая операция (умножение тензора на тензор), она не ломает граф вычислений! Но это компутационно дорогая операция. Каждый раз так умножать GPU замучается. Поэтому в PyTorch и других пакетах нейровычислений есть специальный компутационный шорткат для эмбеддингов, который с одной стороны и граф вычислений не ломает, и компутационно дешев.


Где же взять эмбеддинги?
Допустим, у нас есть обучающий датасет: энное количество оценок, которые юзеры поставили постам.
Давайте проинициализируем эмбеддинги случайным образом, и для каждой оценки из датасета будем вычислять предсказание и сравнивать с оценкой. У нас получатся некие дельты, на этих дельтах мы и будем обучать нейросеть и эмбеддинги с помощью стохастического градиентного спуска (SGD).


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


Каждый из параметров эмбеддинга действительно приобретает некий смысл. Какой не совсем понятно, да и не нужно понимать. Будем считать, что этот смысл ведом только самой системе рекомендаций. Для нас главное, что в итоге система работает: рекомендует юзерам более релевантные посты. А какой "смысл" она видит в этих постах какая разница?


Длина вектора латентных параметров


Число параметров K какое оно должно быть? Лучший ответ, что мне удалось найти, был в этой статье: A social recommender system using item asymmetric correlation
Если коротко, единого ответа нет, всё зависит от глубины сложности ваших item'ов, постановки задачи, функции потерь, короче, от всего. Rule of thumb: 5 маловато, 50 многовато, где-то посередине в самый раз. Надо пробовать: смотреть, насколько гладко обучается модель, не провоцирует ли выбранное количество параметров переобучение, обучается ли модель вообще.


Я выбрал K=13


Где нейросеть?


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


Обучение


Самый большой мой просчёт в архитектуре был связан с тем, что я думал, будто результаты обучения очень ценная инфа, и её надо хранить персистентно, на диске в виде бинарника или в базе. Я написал код, который сохраняет вектора для каждого поста и юзера в базу в виде BLOB'а. Написал, потестил время выполнения, оказалось, что процесс обучения занимает 1-2 секунды, а процесс записи результатов около минуты. Сама модель занимает в памяти 52 мегабайта для 1 млн постов. Так и зачем вообще сохранять ее в памяти? В любой момент можно обучить ее с нуля за пару секунд. Пришлось переписать.


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



Мини-батч это некоторое число оценок, которые одновременно подаются на вход модели для обучения. В моем случае batch_size = 512.
Каждый пик это старт обучения с нуля. Видно, как по мере обучения падает ошибка.


Вот так выглядит инференс (процесс предсказания) модели:



Каждая точка это пост из базы. По вертикали предсказанная оценка. Из 11 115 постов всего 338 имеют предсказанную оценку выше 1, из них в итоге модель и выберет тот пост, который попадет юзеру в ленту.


Обучение с нуля и инкрементное обучение


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


  • Как быть, если приходит новая оценка от юзера? Стартовать обучение сразу, или ждать N новых оценок?
  • При получении новой оценки, обучать модель "на старые дрожжи" (инкрементно), то есть взять те веса и эмбеддинги, которые есть, и на них натравить датасет из новых оценок? Или просто переобучить всё с нуля?

Когда передо мной встали эти вопросы, беглый поиск ответов мне ничего не принес, поэтому я вооружился здравым смыслом и практикой. Соображения у меня были такие:


  • Инкрементное обучение плохо тем, что если запускать только его, и никогда не обучать с нуля, параметры застревают в некой области значений. Кривая обучения становится очень резкой. На практике это означает, что при поступлении новых данных и запуске инкрементного обучения, несколько эпох ошибка почти не меняется, потом делает резкий скачок вниз или вверх, и потом опять стоит как вкопанная. Это очень нездровая ситуация, если вы столкнулись с таким, ищите как сделать обучение гладким, вплоть до переформулирования задачи как таковой.
  • Обучение "с нуля" почти ничего нам не стоит, поэтому не надо этого стесняться.
  • Глядя в будущее, можно предположить, что когда-нибудь у Олежки скопится датасет из 1 млн оценок, который будет занимать заметное время на обучение (десятки секунд). Юзеров будет достаточно много, и при получении каждой новой оценки запускать обучение с нуля мы не сможем.

Поэтому я сделал интерфейс и для инкрементного обучения, и для обучения с нуля. И написал логику, которая запускает полное обучение раз в N циклов инкрементного. А также сделал таймаут между циклами обучения. Всё это вынесено в конфиг. По мере взросления сервиса я смогу легко поменять эти настройки.


Холодный старт и взросление сервиса


Если вам доводилось читать о рекомендательных системах, вам должно быть известно, что одной из самых больших проблем в них является "холодный старт" и бутсртап свежего юзера. Холодный старт это когда в системе в принципе мало оценок, а свежий юзер это юзер, о вкусах которого мы ничего пока не знаем.
Действительно, если в системе мало оценок (и много постов), как ее ни обучай, данных будет недостаточно, чтобы выученные эмбеддинги имели какой-то смысл. Рекомендации продуцируемые этой системой будут малорелевантными.
Со свежим юзером похожая проблема.
Как решить эти вопросы? Единого решения не существует. Все пользуются здравым смыслом и той информацией, которая доступна.
Например, для свежего юзера мы можем сделать некий опросник (так сделано в Netflix), который сможет быстро сообщить нам хоть что-то о юзере, чтобы сделать первый опыт пользования системой не ужасным.
Я решил, что опросник для Олежки будет слишком тяжеловесным решением, юзеры хотят просто тыкать лайки, а не отвечать на вопросы типа "ваш любимый цвет". Поэтому я решил так: первые 30 постов, которые Олежка присылает юзеру, я выбираю из числа тех, которые максимально нравятся всем. Говоря строго, они имеют максимальный bias.


Железо


Сейчас Олежка работает на самом дешевом инстансе Digital Ocean ($5/мес), без GPU. База расположена на этом же инстансе, в другом докер-контейнере. Думать о скейлинге пока рановато.


Итог


Олежка стабильно работает, перестал жрать память и чувствует себя хорошо.
Юзеров пока маловато, так что, пожалуйста, лайк-шер. Можно просто шерить посты из ленты друзьям в Телеграме.
Больше юзеров релевантнее рекомендации!

Подробнее..

Из песочницы Data Science блог с помощью fastpages

14.07.2020 12:05:49 | Автор: admin

Как запустить свой DS/ML/AI/Tech блог с минимумом сложностей связанных с хостингом и деплойем этого блога.


В конце февраля 2020 года ребята из fast.ai представили миру fastpages платформу для ведения блога. Отмечу, что fastpages основан на Jekyll, о котором на Хабре есть множество постов.


Примером блога на движке fastpages является данный блог.


Главное отличительная черта и преимущество fastpages состоит в поддерживаемых из коробки форматах постов:


  • Jupyter ноутбуки (расширение .ipynb);
  • Markdown файлы (расширение .md);
  • Word файлы (расширение .docx)

Таким образом, автору блога необходимо сохранить пост в любом из перечисленных выше форматах в соответствующей директории:


  • './_notebooks/' для .ipynb;
  • './_posts/' для .md;
  • './_word/' для .docx.

А все остальное сделает fastpages, как утверждают его авторы.


fastpages использует Github Pages для хостинга и Github Actions для автоматизации публикации постов.


Как я понимаю, fastpages является доработкой связки Github Pages + Jekyll, где можно сразу же из Jupyter ноутбука получить опубликованный пост.


Создание блога с помощью fastpages и GitHub


Если хотите самостоятельно разобраться, то вот официальная инструкция по настройке в репозитории fastpages.


Процесс настройки fastpages:


  1. Создать собственную копию репозитория из шаблона fastpages по ссылке

    image
  2. Далее автоматически откроется pull request (через ~ 30 секунд), который отвечает за настройку вашего блога, чтобы он мог начать работать.

    image
  3. Вам нужно выполнить инструкции из полученного pull request'a и вы получите свою собственную уже работающую платформу для блога.

Видео туториал



Настройка блога


Есть возможность для персонализированной конфигурации вашего блога. Параметры конфигурации находятся в файле ./_config.yml, некоторые из них приведены ниже:


  • title название вашего блога, которое отображается в верхнем левом углу на каждой странице;


  • description описание, которое будет отображаться в разных местах при предварительном просмотре вашего сайта (например, в социальных сетях);


  • github_username позволяет вашему сайту отображать ссылку на вашу страницу GitHub в нижнем колонтитуле;


  • github_repo позволяет вашему сайту отображать ссылки на ваш репозиторий для различных функций, таких как ссылки на GitHub, Google Colab и Binder для Jupyter ноутбуков;


  • default_badges по умолчанию ссылки GitHub, Google Colab и Binder будут отображаться в постах созданных из Jupyter ноутбуков. Вы можете задать, какие из них будут отображаться по умолчанию, установив для соответствующего значения в default_badges значение true или false. Например, если вы хотите отключить ссылки на Binder, вы должны поправить default_badges:


    default_badges:github: truebinder: falsecolab: true
    

  • url это не нужно менять, если у вас нет собственного домена;


  • baseurl см. комментарии в /_config.yml для получения инструкций ("Special Instructions for baseurl"). Если у вас нет настраиваемого домена, вы можете игнорировать эту опцию;


  • twitter_username создает ссылку в нижнем колонтитуле на страницу Twitter;


  • use_math установите значение true, чтобы получить поддержку математических формул LaTeX;


  • show_description отображает на домашней странице описание под заголовком ваших постов в блоге. По умолчанию установлено значение true;


  • google_analytics опционально можно использовать идентификатор Google Analytics;


  • pagination максимальное количество постов, отображаемых на каждой странице вашей домашней страницы. Значение по умолчанию равно 15. Когда число постов превысит заданное значение, тогда произойдет разбивка на страницы, которая выглядит так:
    image


  • show_tags включает отображение тегов внутри постов, которые выглядят следующим образом:

    image


  • show_image при значении true включается возможность добавления изображений к постам на домашней странице. Выглядит следующим образом (первые 2 поста сопровождаются изображениями):

    image



Публикация постов из .ipynb с помощью fastpages


  1. Сохраните исходный файл вашего поста (в одном из форматов: .ipynb, .md или .docx) в соответствующей папке репозитория (./_notebooks, ./_posts или ./_word). Пример имени для поста 2020-05-26-DS-fastpages-blog.ipynb. Такое наименование является необходимым для отображения поста движком Jekyll (больше деталей).
    Важные аспекты наименования постов:
    • Вначале имени поста указывается дата в формате YYYY-MM-DD-;
    • Символ, следующий сразу за тире, должен быть буквой алфавита.
  2. Сделайте commit и push ваших файлов на удаленный репозиторий GitHub в ветку master.
  3. GitHub автоматически конвертирует ваши файлы в посты блога. Процесс конвертации займет ~5 минут. Можно перейти на вкладку Actions в репозитории на GitHub. Вы увидите три workflow, которые запускаются при каждом push в ветку master:
    • Check Configurations процесс проверки ваших файлов (например, ссылок на изображения), перед обновлением контента в блоге;
    • CI процесс непрерывного деплоя вашего блога;
    • GH Pages Status процесс проверки доступа к блогу.
      Если эти процессы завершаются зеленой галочкой для последнего коммита, то сайт блога успешно обновился.
  4. Для предварительного локального просмотра того, как ваш блог будет выглядеть, см. этот раздел.
    Ниже представлены различные возможности форматирования, которые fastpages поддерживает из коробки.

Возможности форматирования постов


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


Первая ячейка в вашем Jupyter ноутбуке (а также первые строки в Markdown файлах) содержит метаданные, которые могут включать/выключать опции связанные с постом.


# "Title"> "Awesome summary"- toc: true- branch: master- badges: true- comments: true- author: Hamel Husain & Jeremy Howard- categories: [fastpages, jupyter]

Для указания таких в Markdown файлах необходимо в начале файла задать опции как и в ноутбуке, только поместив эти метаданные между строк содержащих по три минуса, т.е. ---.


Выглядит это так:


---title: "Title"description: "Awesome description"toc: truelayout: postcategories: [markdown]---

Все, что определено в начале поста, должно соответствовать YAML разметке. Поэтому если вы хотите использовать двоеточие в заголовке, вы должны экранировать его двойными кавычками: - title: "Deep learning: A tutorial"


Для большего понимания советую ознакомиться с туториалом по YAML.


Перечень управляющих конструкций для форматирования поста (взято отсюда):


  • toc при значении true автоматически будет сгенерировано оглавление поста из заголовков, обозначенных Markdown разметкой;
  • badges [notebooks only] при значении true отображаются ссылки Google Colab, Binder и GitHub, не работает при приватном репозитории;
  • hide_github_badge [notebooks only] при значении true скроет ссылку на GitHub;
  • hide_colab_badge [notebooks only] при значении true скроет ссылку на Google Colab;
  • hide_binder_badge [notebooks only] при значении true скроет ссылку на Binder;
  • branch [notebooks only] используется для дополнительной ссылки на ваш Jupyter ноутбук на Colab и GitHub. Значение по умолчанию: master;
  • comments при значении true будут включены комментарии (больше деталей);
  • author при значении true отображаются имена авторов;
  • categories позволяют группировать посты по тегам (на странице "Tags").
  • image задает изображение для поста, которое будет отображаться на главной странице блога и в соц. сетях (Twitter) вместе с ссылкой на пост:
    • пример задания изображения к посту images/figure.png;
    • изображение обязательно должно находиться внутри папке /images вашего репозитория;
  • search_exclude позволяет скрывать пост в поиске блога (страница Search), стоит заменить, поиск работает только с латиницей;
  • hide при значении true пост будет скрыт на главной странице блога, но будет доступен по прямой ссылке:
    • рекомендуется использовать permalinks для создания предсказуемых ссылок на сам пост;
    • если search_exclude будет иметь значение true, то пост можно будет найти через поиск блога (страница Search);
  • sticky_rank позволяет закрепить пост на конкретной позиции, задав ему порядковый номер. Если двум постам задать одинаковый номер, то между собой они будут отсортированы по дате.

Скрытие и сворачивание кода


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


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


  • #hide скрывает как ввод, так и вывод текущей ячейки;
  • #hide_input скрывает только ввод текущей ячейки;
  • #collapse-hide скрывает код этой ячейки внутри поста, но добавляет кнопку, позволяющую показать эту ячейку;
  • #collapse-show добавляет кнопку, позволяющую показать эту ячейку.

Интерактивные графики с помощью Altair


Графики построенные с помощью библиотеки Altair внутри поста остаются интерактивными как в ноутбуке. Для проверки интерактивности опять же см. мой пост.


Отображение таблиц


Таблицы в опубликованных постах отображаются примерно как и в Jupyter ноутбуках.
image


Вставка изображений


Вы можете добавлять изображения с подписями следующим образом:


![](http://personeltest.ru/aways/www.fast.ai/images/fastai_paper/show_batch.png "Credit: https://www.fast.ai/2020/02/13/fastai-A-Layered-API-for-Deep-Learning/")

Результат выглядит так:
image


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


Анимированные гифки


Гифки вставляются как изображения и полноценно отображаются в постах.



Видео Youtube


Чтобы красиво вставить видео с Youtube достаточно использовать конструкцию:


> youtube: https://youtu.be/L0boq3zqazI

Посты из Twitter


Есть возможность отображать посты из Twitter.


Например, ссылка на этот пост


> twitter: https://twitter.com/jakevdp/status/1204765621767901185?s=20

отобразит следующее:
image


LaTeX формулы


Jupyter ноутбуки поддерживают синтаксис LaTeX формул. Чтобы формулы отображались в постах, нужно убедиться, что опция use_math включена внутри _config.yml (см. Настройка блога).


Следующий LaTeX код:


> $$L(\theta) = \frac{1}{N} \sum_i^N{(y_i - \hat{y_i})^2} \rightarrow \min_{\theta}$$

будет отображен таким образом:


$$display$$L(\theta) = \frac{1}{N} \sum_i^N{(y_i - \hat{y_i})^2} \rightarrow \min_{\theta}$$display$$


Примечания


Есть возможность отображать примечания различных типов.


Предупреждение: > Warning: There will be no second warning!

image
Важно: > Important: Pay attention! It's important.

image
Подсказка: > Tip: This is my tip.

image
Заметка: > Note: Take note of this.

image


Если вставить в любое из примечаний ссылку, то она будет работать.
Например,


> Note: A doc link to [an example website: fast.ai](http://personeltest.ru/aways/www.fast.ai/) should also work fine.

отобразится так:
image


Отображение Emoji


Если написать


Сейчас будет эмоджи :robot:.

то получится:
Сейчас будет эмоджи :robot:.


Шпаргалка по Emoji.


Сноски


В jupyter ноутбуках можно использовать сноски, однако синтаксис отличается от Markdown разметки. Это руководство содержит более подробную информацию об этом синтаксисе, который выглядит следующим образом:


{% raw %}For example, here is a footnote {% fn 1 %}.And another {% fn 2 %}{{ 'This is the footnote.' | fndetail: 1 }}{{ 'This is the other footnote. You can even have a [link](http://personeltest.ru/aways/fastpages.fast.ai/jupyter/2020/02/20/test.html#Footnotes)' | fndetail: 2 }}{% endraw %}

image


Как fastpages конвертирует исходные файлы посты


Для этого fastpages использует nbdev для преобразования jupyter ноутбуков, word и .md файлов в посты блога. После того, как вы сохраните исходные файлы своих постов в папках /_notebooks, /_word или /_posts, то GitHub Actions c помощью nbdev автоматически преобразует их в конечный вид, в котором посты отображаются на сайте вашего блога.


fast_template младший брат fastpages


Стоит упомянуть, что ранее fast.ai выпустили аналогичный проект под названием fast_template, который еще проще в настройке, но не поддерживает автоматическое создание постов из Word и Jupyter файлов, а также многие другие функции перечисленные выше. Поскольку fastpages более гибок и расширяем, его авторы рекомендуют использовать его там, где это возможно.


Авторы предполагают, что fast_template может быть лучшим вариантом для тех, кто ведет не технические блоги. В этом случае посты можно создавать только с помощью встроенного онлайн-редактора Github, не заморачиваясь с использованием git.


Плюсы и минусы


Что понравилось


  • простота создания и размещения блога и публикации контента;
  • возможность публиковать Jupyter ноутбуки в качестве постов + удобства оформления:
    • поддержка отображения интерактивных графиков;
    • скрытие/сворачивание кода;
    • поддержка отображения GIF-анимации;
    • интеграция видео с youtube и тд.
  • нет зависимости от сторонней платформы по типу Medium;
  • возможность разместить блог по собственному url;
  • параметр badges в метаинформации к посту позволяет прикрепить ссылки на GitHub, Binder, Google Colab, что позволяет сразу перейти от поста к коду и его исполнению;
  • комментарии для блога из коробки;
  • возможность прикрепить пост на конкретную позицию на общей странице с помощью sticky_rank, смотреть тут;
  • отсутствие сторонней рекламы;

Что не понравилось или вызывало вопросы


  • непонятно, как сделать структурированный блог с вложенностью:
    • возможное решение permalinks;
    • структура нужна для объединения нескольких постов общей темой;
    • хочется структуру, чтобы в одной директорий хранить все, что связанно с постом (данные, изображения для ноутбуков) в одной папке, а не искать их в куче общих файлов и не городить какую-то структуру в этих общих для всех постов папках.
  • нет WYSIWYG (What You See Is What You Get):
  • в Jekyll в заголовке и описании поста не поддерживаются обратные кавычки, квадратные скобки и тд.
  • Jekyll подразумевает использование git для публикации постов;
  • целесообразность хранения Jupyter ноутбуков в репозитории под вопросом;
  • непонятно, как привязать spell checker для Jupyter ноутбуков.

Резюме


Команда fast.ai предложили DS сообществу интересный и достаточно функциональный инструмент для ведения блога, автору которого остается думать только о том, какой контент публиковать.


Сложность использования практически минимальная, нужны базовые знания git, разметки Markdown и Jupyter Notebook. И никаких проблем с тем, как и где хостить и деплоить сам блог.


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


В заключение хочу сказать, что сам пользуюсь и всем советую.


DS/ML/AI блоги



Блоги компаний



Полезные ссылки


Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru