Блог об аналитике, визуализации данных, data science и BI

Наши проекты:

Как и зачем мы сделали три дашборда по LinkedIn

Время чтения текста – 11 минут


Скажу без лишней скромности — мне есть что рассказать про аналитику и про свою работу. Опытом и кейсами я и моя команда делимся в социальных сетях и в том числе — на LinkedIn, который активно ведем с 2023 года. У нас там три аккаунта: мой личный профиль, страницы Valiotti Analytics и кипрского дата-коммьюнити.

Соцсети ­— это инструмент, эффективность которого надо контролировать:

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

Это можно делать вручную, но это будет долго и неэффективно, и чем больше становится соцсетей, тем больше времени это отнимает. Да и в конце концов, аналитики мы или нет? Зачем делать руками то, что можно автоматизировать?

Так мы решили создать дашборд.

Наш подход

Делали для себя, но по тому же алгоритму, что для клиентов.

  1. Собрали команду из BI-специалиста и дата-инженеров. Подобные дашборды часто создаются силами одного человека, но у нас была возможность выделить нескольких сотрудников, чтобы ускорить работу.
  2. Они познакомились с доступными данными, API LinkedIn и статистикой, которую предоставляет сайт. Не надо недооценивать этот этап — перед первым интервью с заказчиком разобраться, с чем предстоит иметь дело. Это поможет сразу понять, какие требования из его ТЗ выполнимы.
  3. Затем BI-специалист провел серию интервью с главными пользователями — со мной и с лидом отдела контента. На этом этапе он выясняет контекст использования дашборда — об этом ниже.
  4. Работали итеративно: интервью — первый макет дашборда — согласование и внесение правок — следующая версия — и так до создания финального, рабочего варианта.

Что отличает хороший дашборд от плохого?

При создании дашборда главное — понять контекст использования.

  • Кто будет пользоваться дашбордом?
  • Для каких целей он нужен?
  • Какие метрики и зачем пользователь будет отслеживать?
  • Как часто пользователь будет обращаться к дашборду?
  • Будет ли он использовать дашборд только для своих рабочих задач или собирается показывать его на конференциях?
  • Какие нужны показатели, фильтры, периоды и гранулярность?

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

Отличный фреймворк для создания дашбордов создал Роман Бунин — Dashboard Canvas. Он описал алгоритм работы, подготовил примеры вопросов для интервью с заказчиком и шаблоны.

Инжиниринг: два решения для одного проекта

Стек проекта:

  • Python,
  • Airflow (оркестратор процессов),
  • AWS (облако, где это все развернуто),
  • PostgreSQL (база данных),
  • Tableau (BI-инструмент),
  • Selenium (надо было установить драйвер для парсинга, мы использовали Firefox; скрипты писали на Python)
  • Fivetran

Из наших аккаунтов два корпоративные, и один личный. В зависимости от типа страниц LinkedIn по-разному «делится» аналитикой.

Данные из корпоративных аккаунтов Valiotti Analytics и Cyprus Data Community мы собираем с помощью Fivetran.

Это инструмент для автоматической выгрузки данных в базу из разных источников. А еще — наши партнеры, с которыми мы давно работаем над разными проектами для наших клиентов. Так что мы и не сомневались, что будем использовать для своего дашборда.

Чтобы настроить передачу данных, Необходимо было просто подключить Fivetran к аккаунту и базе данных. Дальше он автоматически вытягивает данные из соцсети, преобразует и передает базе.

С моим личным аккаунтом все оказалось сложнее. LinkedIn неохотно делится данными по таким страницам.

  1. Подключить к нему Fivetran нельзя — это вариант только для корпоративных страниц.
  2. Сервис, специально созданный для сбора информации с личных аккаунтов на LinkedIn, inlytics.io через какое-то время начал требовать заполнить капчу. Для нас была важна полная автоматизация процесса, поэтому такой вариант нам категорически не подходил.
  3. Токен к официальному API LinkedIn не дал — их перестали раздавать из-за повышенного спроса.

В итоге пришлось ограничиться тем, что LinkedIn выдает сам — Excel-файл с данными по приросту подписчиков, демографии и взаимодействиям, а также топ-50 самых популярных постов.

  • Выгрузку Excel-файлов автоматизировали через связку Airflow + Selenium.
  • В Notion ведется таблица с постами — в ней указывается заголовок, тема и ссылка на публикацию.
  • Данные выгружаются с помощью Airflow и объединяются их со статистикой из LinkedIn. Чтобы сметчить данные используются ссылки — они в обеих таблицах одинаковые.

Вот такой дашборд получился в итоге.

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

Визуализация в Tableau

Ну вот, контекст использования выяснили, данные собрали, макеты согласовали… пора наконец-то делать дашборд!

Мы выбрали Tableau, во-первых, за богатый функционал. Его возможностей хватит, чтобы воплотить практически любые идеи в жизнь.

Для дашборда для другого нашего проекта мы даже сделали календарь! Это наше изобретение — в Tableau такой функции нет.

Во-вторых, в Tableau приятные решения для визуализации. Даже без специальных знаний о дизайне и датавизе в нем можно собрать визуально приятный, читабельный дашборд.

Ну а у нас-то знания есть. А еще — свой стайлгайд, по которому мы оформляем все свои дашборды. Можете убедиться, взглянув на скрины выше: дашборды в корпоративных цветах не только выглядят хорошо, но и получаются очень наглядными — а значит, функцию свою выполняют.

Выводы

Со стороны может показаться, что задача тривиальная — всего-то визуализировать данные по нескольким аккаунтам в соцсетях. Но она подкинула немало челленджей и несколько раз заставила хорошо задуматься над поиском решения.

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

Что на практике дают эти дашборды?

  1. Анализ аудитории. Дашборд собирает данные, откуда меня читают мои подписчики, в каких компаниях и на каких должностях работают. Это позволяет строить гипотезы, какие темы и форматы будут наиболее интересны.
  2. Регулярная проверка основных показателей аккаунта: роста аудитории, вовлеченности подписчиков, охватов с детальной статистикой по каждому посту. Если я вижу спад или рост какого-то из них, можно отследить, когда он произошел, и корректировать контент-стратегию. Например, поэкспериментировать с подачей или темами.
  3. Выделение трендовых тем. Провожу долгосрочный анализ по 10 темам постов и выясняю, про что аудитории интереснее читать — нейросети, аналитику и данные или же больше про бизнес и предпринимательство. Затем список тем сужается до 5 самых популярных, на которых я концентрируюсь в дальнейшем. Уже успел сделать вывод, что моей аудитории интереснее всего читать про лайфхаки из анализа данных, личные истории и бизнес-кейсы.
  4. AB-тест визуального оформления — экспериментирую с оформлением постов и смотрю на реакцию.

Как вам такой проект? Стали бы делать дашборд для сбора данных из соцсетей или вам хватает их встроенной аналитики? Пишите в комментариях!

 Нет комментариев    3798   20 дн  

Что такое ACID и причем тут базы данных?

Время чтения текста – 17 минут

Реляционные СУБД могут применяться для решения аналитических и транзакционных задач, и сегодня мы хотим рассказать вам о последних.

Транзакция — это несколько операций чтения и записи, сгруппированные в одну логически неделимую операцию. Самый наглядный пример — банковский перевод, для которого нужно совершить несколько действий: снять деньги с одного счета и перекинуть их на другой.

Пример исчерпывающе доносит смысл, но дьявол, как всегда, кроется в деталях, а точнее в деталях реализации. Во-первых, СУБД, поддерживающие транзакции, должны удовлетворять гарантиям функциональной безопасности ACID (это аббревиатура, которая ничего общего с химией не имеет). Во-вторых, есть понятие уровней изоляции транзакции, всего существует 4 различных уровня. Давайте начинать!

A — Atomicity

Как мы уже сказали, ACID — аббревиатура, каждая буква которой обозначает свойство транзакций. Первая буква «А» — atomicity, то есть атомарность.

В большинстве СУБД для того, чтобы начать транзакцию, необходимо выполнить запрос с выражением START TRANSACTION. Все дальнейшие действия будут относиться к начатой транзакции. Чтобы завершить транзакцию, нужно выполнить выражение COMMIT, а если вы хотите отменить все действия транзакции, то можно «откатиться», используя выражение ROLLBACK.

И именно атомарность гарантирует, что ВСЕ операции между START TRANSACTION и COMMIT либо выполнятся, либо не выполнятся — промежуточного состояния не будет.

В качестве примера вспомним опять про банковское приложение:

Николай хочет перевести с одного счета на другой 200 рублей. Для этого сначала с первого счета будет списано 200 рублей, а уже потом на второй счет эти деньги будут зачислены. На первом счету у него 400 рублей, на втором — 500.

Если после первого UPDATE питание в здании отключится и сервер базы данных выключится, то на счетах останется 400 и 500 рублей, как будто никакой транзакции и не было.

C — Consistency

Вторая буква «C» — consistency, то есть согласованность или консистентность.

Согласованность в рамках баз данных можно понимать как выполнение некоторых утверждений относительно данных в таблицах. На примере про перевод между счетами Николая можно сформировать такое утверждение: «При переводе между счетами сумма денег на обоих счетах не должна измениться». Это мы и наблюдаем: до транзакции было 400+500=900 рублей, а после стало 600+300=900 рублей.

I — Isolation

Третья буква — «I» — isolation, то есть изолированность одной транзакции от любой другой.

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

Чтобы разобраться, чем чревато отсутствие изоляции двух транзакций, достаточно в наш пример добавить Маргариту, которая хочет через банкомат внести на счет Николая 300 рублей в момент перевода Николая. Если реализовать ее действия через механизм считывания текущего баланса счета, прибавления к нему 300 рублей и его перезаписи, то появляется риск нарушения консистентности.

Последнее действие, выполненное в рамках начисления через банкомат, перезатрёт все изменения от другой транзакции, что вызовет нарушение целостности данных в базе: у Николая на первом счету будет 600 рублей, а на втором 800 рублей, хотя должно быть 600. Эта ситуация называется «потерей обновления».

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

D — Durability

Последняя буква аббревиатуры отвечает за принцип сохраняемости.

Тут все просто: все транзакции, которые были успешно «закоммичены», не должны быть потеряны ни при каких сбоях. Если база данных реплицируется (запись идет на еще один или несколько работающих БД), то сохраняемость может означать успешную запись в БД на другом сервере. Если база данных не реплицируется, то сохраняемость означает запись на энергонезависимый носитель информации (например, жесткий диск).

Уровни изоляции
READ UNCOMMITED

Самый первый уровень изоляции, который мы рассмотрим — READ UNCOMMITED. То есть он позволяет читать все изменения незавершенных (или «незакоммиченных») транзакций.

Чтение изменений незафиксированных транзакций называется «грязным чтением». Этот уровень изоляции не позволяет изменять внутри транзакции объекты (строки или таблицы или что-то другое, это зависит от конкретной СУБД и движка таблицы), уже измененные другой одновременной транзакцией («грязная запись»). Транзакция просто повиснет в ожидании COMMITа другой транзакции.

Рассмотрим похожий пример, в котором Николай хочет оплатить покупку на 100 рублей с первого счет, а чуть позже Маргарита захотела начислить ему 100 рублей через банкомат. Изначально у него на счетах было по 500 рублей. Тогда схема выполнения будет следующей:

Обновление баланса второй транзакции — это «грязная» запись, именно на этом моменте транзакция и повиснет, изменение произойдет после фиксации перевода Николая, но его можно будет отменить командой ROLLBACK, ведь будет понятно, что произошла «грязная запись».

READ COMMITED

Следующий уровень изоляции READ COMMITED. Как можно догадаться из названия, он позволяет читать только те изменения, которые были зафиксированы (или «закоммичены»).

Этот уровень изоляции, как и read uncommitted, позволяет избежать ситуации «грязной записи», но также позволяет избежать ситуации «грязного чтения». То есть внутри транзакции можно прочитать только те изменения объектов, которые были совершены внутри уже завершенных транзакций.

Вспоминая пример с предыдущей карточки, если бы уровень изоляции был «READ COMMITED», то чтение внутри операции пополнения счета Маргаритой вернуло бы неизмененные балансы счетов Николая (по 500 рублей на каждом, а не 500 и 400, как в предыдущем примере).

Как же эти два уровня изоляции реализованы технически?

Для предотвращения «грязных записей» транзакция перед изменением данных в объекте устанавливает на него блокировку. Таким образом, никакая другая транзакция не сможет изменить содержимое заблокированного объекта, но читать эти изменения можно.

Применять блокировки для предотвращения «грязного чтения» слишком затратно, ведь тогда доступ может быть закрыт для множества читающих транзакций. Для решения этой проблемы после изменения объекта БД запоминает старую версию объекта, которую видят читающие транзакции.

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

REPEATABLE READ

И вот, кажется, что вроде бы достаточно таких уровней для работы с базой данных, ведь никто не увидит незафиксированные изменения других.

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

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

Сначала аудитор узнал, что на втором счету Николая 500 рублей (до перевода), затем увидел на втором счету уже 400 рублей, так как транзакция Николая была зафиксирована. У аудитора сложится впечатление, что у Николая 900 рублей суммарно на двух счетах. Но это не так, а возникшая ситуация называется «асимметрией чтения». Пример может показаться надуманным, ведь аудитор может заново прочитать баланс второго аккаунта, но если представить, что длинная транзакция — копирование данных на резервную копию, могут возникнуть большие проблемы.

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

MVCC

Для реализации такого уровня используется мультиверсионное управление конкурентным доступом (MVCC).

При таком механизме хранится несколько версий каждого зафиксированного объекта, вместе с ней хранятся номер транзакции — txid, которая создала версию объекта, и номер транзакции, которая ее удалила. Каждой новой транзакции присваивается свой номер, и она читает только те версии объектов, номер создания которых меньше собственного.

Идентификатор транзакции аудитора 9. Таким образом, все данные, которые будут прочитаны или изменены, должны иметь идентификатор не больше 9. Txid транзакции Николая — 10, внутри нее было произведено изменение двух объектов, которые были созданы транзакциями с txid 6 и 4. Изменение будет означать удаление этих версий объектов и создание новых. Когда аудитор попытается прочитать баланс первого счета Николая, СУБД увидит, что объект был создан транзакцией с бОльшим txid и возьмет версию, созданную ранее.

SERIALIZABLE

Закончим нашу статью рассказом про самый сильный уровень изоляции — SERIALIZABLE.

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

Этот уровень изоляции может быть реализован тремя способами:

  1. Действительно последовательное выполнение транзакций
  2. Применение двухфазной блокировки (2PL)
  3. Использование сериализуемой изоляции снимков состояния (SSI).

Обо всем по порядку:

  1. По-настоящему последовательное выполнение предполагает выполнение всех транзакций одна за другой, в одном потоке. Такой уровень изоляции имеет смысл применять только если транзакции выполняются быстро, чтобы другим не пришлось ждать выполнения одной большой и долгой транзакции. К тому же, если объем данных одной транзакции не помещается в память, то обращение к диску может очень сильно замедлить работу с БД.
  1. Двухфазная блокировка — это усиленная версия уже упоминавшейся блокировки. Во время одновременного выполнения нескольких транзакций допускается чтение одного объекта (строки или нескольких строк), но для его изменения нужен монопольный доступ. То есть если транзакция «А» читает строки, которые транзакция «В» хочет изменить, то вторая должна дождаться завершения первой. И наоборот.
  1. Сериализуемая изоляция снимков состояния — относительно свежий способ обеспечения сериализуемости транзакций. По сравнению с предыдущими двумя этот метод обеспечивает оптимистичное управление доступом, то есть без блокировок. Все чтение из базы выполняется из согласованного снимка состояния базы данных, а изменения происходят свободно, в расчете, что они не затрагивают объекты других транзакций. При фиксации транзакции происходит проверка, не были ли прочитаны или изменены уже измененные другой транзакцией объекты. Если да, то транзакция прерывается и ее выполнение приходится выполнить еще раз.
    Все транзакции при этом методе должны иметь уровень изоляции Serializable.

Уровень изоляции Serializable может очень сильно ударить по производительности, хотя последний метод является самым перспективным среди всех трех, и может быть в дальнейшем будет улучшен по производительности до слабых уровней изоляции.

Контакты автора: LinkedIn | Telegram

 Нет комментариев    2491   1 мес  

Привет, Hydra!

Время чтения текста – 20 минут

Сегодня мы хотим познакомить вас с еще одной СУБД для аналитики данных. Мы поговорим про Hydra. Расскажем, как начать ее использовать, объясним основные концепции, загрузим данные и погоняем запросы.


Итак, что же такое Hydra? Это аналитическая СУБД с открытым исходным кодом, который вы можете найти на GitHub. Hydra разработана на основе другой очень популярной транзакционной СУБД, PostgreSQL, что позволяет ей быть более универсальной по сравнению с прочими СУБД, созданными для аналитики данных. Но как такое возможно? Фундаментальное различие между аналитической и транзакционной СУБД заключается в том, что первая хранит данные каждого поля в отдельном файле, а вторая — хранит данные нескольких записей в одном файле. С Hydra все немного интереснее.

Создатели Hydra предлагают облачные решения на любой вкус в зависимости от ваших потребностей.

Но вы можете собрать СУБД из исходного кода с GitHub. Мы же для написания этой статьи воспользовались бесплатной версией доступа к облачной платформе.

Особенности хранения данных

Как и во многих аналитических СУБД, в Hydra реализовано колоночное хранение данных. Такой подход позволяет лучше сжимать данные, ведь в одном поле могут быть значения только одного типа. Эффективное сжатие не только сокращает занимаемое место, но и позволяет читать данные более эффективно. Для выполнения аналитических запросов требуется прочитать только несколько полей из всей таблицы — при колоночном хранении файлы ненужных столбцов не будут прочитаны вообще в отличие от строкового подхода к хранению данных.

В то же время, для решения транзакционных задач, когда нужно прочитать одну запись целиком и заменить в ней значения некоторых полей, наиболее эффективно строковое хранение, когда записи таблицы лежат в одном файле одна за другой. Но что самое интересное — в Hydra вы можете выбрать, какой из этих двух вариантов использовать. Тот факт, что данная СУБД была разработана на основе PostgreSQL, делает ее полноценной транзакционной СУБД. В то же время возможность колоночного хранения таблиц добавляет Hydra универсальности.

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

Давайте приведем пример создания таблиц с колоночным и со строковым хранением данных. Создадим таблицу для известного по серии статей о ClickHouse набора данных о проданном жилье в Великобритании с 1995 по настоящее время.

Вот так будет выглядеть скрипт создания стандартной таблицы из PostgreSQL со строковым хранением. Этот же скрипт подойдет и для Hydra.

CREATE TABLE uk_price_paid_row (
    price INT,
    date DATE,
    postcode1 TEXT,
    postcode2 TEXT,
    TYPE SMALLINT,
    is_new SMALLINT,
    duration SMALLINT,
    addr1 TEXT,
    addr2 TEXT,
    street TEXT,
    locality TEXT,
    town TEXT,
    district TEXT,
    county TEXT
);

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

CREATE TABLE uk_price_paid_col (
    price INT,
    date DATE,
    postcode1 TEXT,
    postcode2 TEXT,
    TYPE SMALLINT,
    is_new SMALLINT,
    duration SMALLINT,
    addr1 TEXT,
    addr2 TEXT,
    street TEXT,
    locality TEXT,
    town TEXT,
    district TEXT,
    county TEXT
) USING columnar;

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

В англоязычных источниках транзакционные задачи обычно описываются таким термином, как «OLTP нагрузка» (OLTP — аббревиатура, которая расшифровывается как OnLine Transaction Processing). С другой стороны, решение аналитических задач характеризуется OLAP нагрузкой (OnLine Analytical Processing). Зачастую каждая СУБД выбирается под свою нагрузку, но Hydra отличается тем, что предназначена как для OLTP, так и для OLAP нагрузки. Такое смешение двух типов нагрузки породило термин «HTAP нагрузки» (Hybrid Transaction/Analytical Processing) — как раз под такую нагрузку и была разработана Hydra.

Партиционирование

Еще одной очень интересной функцией таблиц Hydra является партиционирование, то есть разделение таблицы на несколько логически независимых друг от друга кусков. Партиционирование — это логическое разбиение всех записей таблицы на группы. Это очень удобный функционал для работы сразу с большим количеством строк.

В Hydra партиционирование концептуально похоже на партиционирование таблицы из PostgreSQL. При создании таблицы необходимо указать поле, по значениям которого будет разделение на партиции, а также тип разделения; подробнее можете узнать в русскоязычной документации к  документации к PostgreSQL. Давайте при создании нашей таблицы из предыдущего примера укажем партиционирование по месяцам продажи дома, тогда наш скрипт будет выглядеть так:

CREATE TABLE uk_price_paid (
    price INT,
    date DATE,
    postcode1 TEXT,
    postcode2 TEXT,
    TYPE SMALLINT,
    is_new SMALLINT,
    duration SMALLINT,
    addr1 TEXT,
    addr2 TEXT,
    street TEXT,
    locality TEXT,
    town TEXT,
    district TEXT,
    county TEXT
) PARTITION BY RANGE(date);

Мы намеренно не указывали в скрипте тип хранения данных таблицы, об этом позже. В одной партиции будут храниться данные за определенный промежуток значений поля date. Нам бы хотелось хранить данные за каждый год в своей партиции. Далее нам необходимо создать каждую партицию вручную, с указанием границ поля date; например, вот так выглядел бы скрипт создания партиции для записей 1995 года:

CREATE TABLE uk_1995 PARTITION OF uk_price_paid 
FOR VALUES FROM ('1995-01-01') TO ('1996-01-01');

Скрипт создания партиции недвусмысленно говорит о том, что мы создали таблицу, которая является партицией изначальной таблицы. Здесь кроется еще одна «киллер-фича» СУБД Hydra. Раз каждая партиция — это отдельная таблица, то для каждой партиции можно указать свой способ хранения данных. В примере выше партиция создана со строковым типом хранения данных, но можно создать ее же с колоночным способом хранения. Скрипт такого запроса будет следующий:

CREATE TABLE uk_1995 PARTITION OF uk_price_paid 
FOR VALUES FROM ('1995-01-01') TO ('1996-01-01')
USING COLUMNAR;

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

То есть указывать в изначальной таблице способ хранения не нужно, ведь у каждой партиции он будет свой.

Такая гибкость Hydra очень удобна при совмещении двух разных типов нагрузки OLTP и OLAP, чего часто тяжело добиться в классических колоночных СУБД.

Обновление и удаление данных

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

Сейчас процесс удаления данных реализован следующим образом: при удалении из таблицы запросом DELETE запись помечается просто удаленной и не будет прочитана при следующем SELECT запросе. Физическое удаление произойдет при вызове запроса на сборку мусора VACUUM FULL, который хорошо известен пользователям PostgreSQL PostgreSQL. Это рабочий способ, но малоэффективный, потому что при вызове сборщика мусора с таким параметром произойдет полная блокировка таблицы.

Процесс обновления записей в таблице при колоночном хранении мало отличается от удаления. Запрос UPDATE помечает каждую обновляемую строку как удаленную (как и при удалении данных запросом DELETE), а новые версии записей просто будут дописаны в конце. По сути, запрос UPDATE — это запрос DELETE, за которым следует запрос INSERT.

Если, например, мы захотим обновить цену во второй записи из такого куска таблицы:

То нам придется удалить строку целиком, а потом вставить такую же, но с новым значением поля price:

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

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

Как выполняются аналитические запросы?

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

Давайте разберемся с этим механизмом на примере вот такого простого запроса к нашей таблице uk_price_paid, которая была создана с колоночным хранением:

SELECT
    DATE_PART('year', date) AS year,
    ROUND(AVG(price)) AS avg_price
FROM
    uk_price_paid
GROUP BY
    year
ORDER BY
    year ASC;

Запрос определяет среднюю стоимость недвижимости за каждый год. Если таблица была предварительно разделена на партиции по году, то агрегатная функция AVG будет рассчитываться одновременно для каждой партиции параллельно.

Данные каждого столбца для вычисления разделяются на векторы, и каждый поток будет обрабатывать свой вектор. Мы обозначили каждый вектор своим цветом (всего на рисунке 6 векторов). Так как таблица была создана с партиционированием по годам, то скорее всего вычисление каждого столбца изначально будет разделено между потоками по партициям. Если в распоряжении Hydra потоков будет больше, чем партиций, то файлы столбцов будут разделены на еще бОльшее число векторов, тем самым увеличивая параллельность выполнения запроса. Такой алгоритм выполнения запроса сильно уменьшает время отклика, а соответственно, увеличивает время выполнения запроса.

Для сравнения: в PostgreSQL такой запрос выполнялся бы в один поток, который бы читал все строки (так как у PostgreSQL строковый тип хранения данных) и делал вычисление среднего арифметического каждого года последовательно.

Источники данных

В Hydra также есть средства для интеграции с другими СУБД, такими как MySQL и PostgreSQL. Эти СУБД могут быть источником данных для таблиц Hydra. Также источником могут служить данные, хранящиеся в S3 и в Google Sheets.

Для каждого способа есть своя инструкция по интеграции, но все они очень сильно похожи друг на друга, так как реализованы при помощи модуля обертки для внешнего источника данных, например, для интеграции с PostgreSQL используется модуль postgres_fdw.

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

Испытание запросами

Давайте наконец посмотрим, на что способна Hydra в деле. Мы потестим ее производительность на тестовых данных о продаже жилья в Великобритании с 1995 года. Структуру таблицы мы приводили ранее. Наша таблица будет поделена на партиции по году, каждая партиция при этом содержит данные по колонкам. Всего в таблице 28 миллионов записей.

Ну и встречайте сами запросы!

  1. Посчитать общее число размещенных объявлений за все время.
SELECT COUNT(*)
FROM uk_price_paid;
  1. Найти среднюю цену за каждый год.
SELECT
DATE_PART('year', date) AS year,
ROUND(AVG(price)) AS avg_price
FROM uk_price_paid
GROUP BY year
ORDER BY year ASC;
  1. Найти среднюю цену за каждый год в Лондоне.
SELECT
DATE_PART('year', date) AS year,
ROUND(AVG(price)) AS avg_price
FROM uk_price_paid
WHERE town = 'LONDON'
GROUP BY year
ORDER BY year ASC;
  1. Топ 100 районов городов по средней цене с 2020 года с более чем 100 объявлений.
SELECT
town,
district,
COUNT(*) AS c,
ROUND(AVG(price)) AS avg_price
FROM uk_price_paid
WHERE date >= '2020-01-01'
GROUP BY town, district
HAVING COUNT(*) >= 100
ORDER BY avg_price DESC
LIMIT 100;
  1. Вывести все возможные комбинации типов домов и количество объявлений с ними по убыванию их суммарной цены из объявлений.
SELECT
    t.name AS type,
    d.name AS duration,
    COUNT(*) AS c,
    ROUND(SUM(price)) AS sum_price
FROM uk_price_paid AS upp
INNER JOIN type AS t ON upp.type = t.id
INNER JOIN duration AS d ON upp.duration
= d.id
WHERE date >= '2010-01-01'
GROUP BY t.name, d.name
HAVING COUNT(*) >= 100
ORDER BY sum_price DESC;
  1. Вывести информацию об улицах с самым дорогим и самым дешевым жильем.
SELECT
    price,
    date,
    postcode1,
    t.name AS type,
    d.name AS duration,
    street,
    locality,
    town,
    district,
    county
FROM uk_price_paid AS upp
INNER JOIN type AS t ON upp.type = t.id
INNER JOIN duration AS d ON upp.duration = d.id
WHERE street IN (
    (SELECT street
    FROM uk_price_paid
    ORDER BY price DESC
    LIMIT 1)
 
    UNION ALL
 
    (SELECT street
    FROM uk_price_paid
    ORDER BY price ASC
    LIMIT 1)
)
ORDER BY price DESC;

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

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

Заключение

СУБД Hydra — это отличное решение для построения вашего хранилища данных. Ее поддержка нативного диалекта PostgreSQL многим поможет быстро внедрить Hydra в свои проекты, а колоночный тип хранения данных позволит быстро и эффективно выполнять аналитические запросы высоких степеней сложности. В то же время возможность создавать обыкновенные PostgreSQL таблицы ставит Hydra на один уровень с одной из самых часто используемых СУБД для OLTP нагрузки.

Hydra — это очень молодой проект, который вобрал в себя все преимущества PostgreSQL и дополнил их колоночным способом хранения данных таблиц и параллельным выполнением запросов. Очень ждем, когда разработчики этой СУБД представят новый функционал, который поможет выполнять более сложные аналитические запросы.

Неочевидный выбор: количественная или качественная цветовая палитра?

Время чтения текста – 22 минуты

Перевод статьи «When to use quantitative and when to use qualitative color scales»

Лиза Шарлотта Мут, дизайнер из Берлина, опубликовала на Datawrapper статью о сложностях выбора между качественной и количественной цветовой палитрой при визуализации данных разных типов. Заинтересовавшись ее мнением и аргументами по данному вопросу, мы решили перевести ее для вас.

Эта статья попытается ответить на следующий вопрос: когда следует использовать количественную цветовую палитру (последовательную или расходящуюся; например, голубой, синий, темно-синий), а когда — качественную палитру (например, красный, желтый, голубой)?

И небольшой дисклеймер: чтобы мне не оперировать длинными терминами количественная цветовая схема и качественная цветовая схема в этой статье, я буду часто употреблять менее корректные, но более удобные для чтения слова «оттенки» и «градиенты» для количественных цветовых схем и «тона» для качественных.

Итак, когда надо использовать оттенки? А когда — тона?

Используйте тона, когда у значений нет изначального порядка

Тона необходимо использовать, когда невозможно упорядочить данные. Например, если в графике представлены промышленности или страны (Иран, Марокко, Пакистан): Марокко ничем не уступает Пакистану, как и наоборот.

Используйте оттенки или градиенты, когда у значений есть порядок

А вот когда вы можете упорядочить данные, представляемые цветом, используйте последовательную или расходящуюся цветовую палитру. Если, например, необходимо передать данные о безработице (4%; 6%; 8%), лучше отдать предпочтение именно количественной цветовой палитре.

Это правило применимо не только к тексту или числам. У шкалы Лайкерта («полностью не согласен», «не согласен», «где-то посередине», «согласен», «полностью согласен») или размеров одежды (XS, S, M, L, XL, XXL) также есть специфический порядок. Они тоже относятся к количественному типу данных, поэтому для их визуализации крайне рекомендуется использовать именно оттенки.

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

Используйте оттенки, чтобы подчеркнуть подразумеваемый порядок

Здесь разобраться помогут древовидные карты:

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

И тут вы могли бы подумать: «Но я хочу обозначить цветом совершенно новое значение!» Например, если вы отсортируете ячейки по показателю безработицы, возможно, вам захочется покрасить их по ВВП на душу населения. Но такой график было бы крайне тяжело прочитать:

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

Старайтесь не красить графики по значениям, которые не отображаются дополнительно с помощью, например, позиции или порядка.

А если все-таки покрасить подразумеваемые значения в удобочитаемом графике?

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

Давайте посмотрим на один пример, чтобы прокачать ваше интуитивное понимание, когда стоит прибегнуть к такому решению.

Страница с деталями графика «Алгоритм Google» из The Economist от 8 июня, 2019

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

Точечную диаграмму проблематично разобрать в частности из-за того, что точки покрашены расходящейся, а не последовательной цветовой палитрой. По сути, The Economist использует цвета для передачи двух различных переменных: тон для презентации политической идеологии («левые»/«правые») и насыщенность и яркость для демонстрации политической крайности. Тот же самый график, который передавал лишь политическую крайность (не важно, в сторону «левых» или «правых») с помощью последовательной цветовой палитры было бы проще читать.

И все же, чтобы понять этот график из The Economist, требуется несколько секунд. Обратите внимание, что точечную диаграмму справа проще прочитать, так как в ней совмещены два приема визуализации: политическая идеология показана позицией («левые» и «правые») и оттенком.

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

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

Итак, мы рассмотрели древовидные карты, столбчатые графики и точечные диаграммы. Теперь, давайте возьмем в качестве примера линейный график:

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

Используйте оттенки для выделения подкатегорий

Есть и другие причины красить качественные данные количественными цветовыми палитрами, а не качественными. Например, чтобы выделить подкатегории. Ниже представлен график из The Economist, который показывает, как это сделать:

Население в занимаемых территориях, сгруппированное по религиозной принадлежности (The Economist, PDF)

Мы видим две категории: «евреи» (голубой) и «арабы» (желтый). Подкатегориями же являются территории, которые представлены оттенками синего и желтого.

Вот похожий пример:

График NZZ

NZZ использует оттенки (зеленый и синий), чтобы помочь читателям заметить разницу между двумя категориями: Ältere («пожилые») и Jüngere («молодые»). Оттенки помогают различать подкатегории: какие слова пожилые люди и молодежь используют, чтобы сказать «сердцевина яблока».

(Кстати, и тому и другому графику цвета, в целом-то, и не нужны: все секции могут быть серыми с разделяющими их белыми линиями, так как каждая из подкатегорий объяснена текстом. Слова Gaza, Israel, Ältere, Gräutschi и так далее показаны на самих графиках, делая цветовое решение лишним.)

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

Используйте оттенки, чтобы разграничить первостепенные и второстепенные категории

Вам не нужно давать всем категориям одинаковый вес. Если вы хотите выделить какую-то из них, вы вполне можете покрасить остальные в один цвет (зачастую, градиентами серого):

График Лизы Шарлотты Рост

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

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

Существует такая мантра в визуализации данных, которая призвана помочь специалистам сделать их графики более доступными для людей с проблемами зрения: «Сделай так, чтобы визуализация работала в черно-белом исполнении». Суть в том, что цвета должны иметь разные уровни яркости, чтобы их было легко отличить при переводе в серую шкалу.

Ну, допустим. Тогда какой же цвет темнее, этот или этот ? И как насчет и ? Сюрприз! Два первых превратятся в  в серой градации. А два последних будут выглядеть как .

Для простых смертных судить о светлоте двух цветов, если у них разные тона, не так-то и просто. Поэтому «сделать так, чтобы визуализация работала в черно-белом исполнении» проще, если специалист по дата визуализации использует градиенты только одного тона.

Две причины — доступность и строгость — могут убедить вас выбрать оттенки, а не тона, при работе с категориями. Вот пример из The Financial Times:

График из The Financial Times

Но имейте в виду следующие особенности: во-первых, скорее всего, некоторые читатели будут пытаться придать цветам какой-то смысл. Даже если это не входило в ваши планы, они могут попытаться найти объяснение тому, почему использован градиент. «США изображены более темным цветом, так как имеют самые высокие показатели». Или: «... так как их данные имеют большую значимость в этой статье». Красить наобум нельзя.
Синтия Брюэр отмечала то же самое, когда говорила о представлении бинарных данных («да»/«нет», «общественный»/«частный», «присутствует»/«отсутствует») на картах:

Синтия Брюэр: «Рекомендации по выбору цвета для карт и визуализации», с. 136 по цитате в: «Визуализация в современной картографии», том 2, 1994. Курсив автора статьи.

«Создатель карты решает, какая из категории значительней, основываясь на цели создания карты, и самая важная из двух категорий должна быть темнее цветом. Если бинарные категории обладают одинаковым весом, красьте график качественной цветовой палитрой с незначительной разницей по светлоте между тонами».

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

В-третьих, если хочется использовать градиент, старайтесь использовать оттенки только одного тона, если нет необходимости вводить еще один.

Спросите себя, какое ключевое послание вы хотите передать, и выделите участки, которые нужно сделать более заметными, с помощью другого тона и его оттенков, как «Europe» и «US» в этом графике:

График The Financial Times, который я слегка отредактировала

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

До этого момента, мы в основном смотрели на причины использовать оттенки вместо тонов. А вот причина использовать тона вместо оттенков:

Используйте тона, чтобы лучше обособить категории

В 2020 году, The Financial Times опубликовали график, в котором отобранные страны Европы были сгруппированы в зависимости от значительного/умеренного/небольшого роста чисел новых случаев заболевания Covid-19.

График The Financial Times

Стивен Бернард и Джон Берн-Мердок, создатели этого графика, решили использовать тона для разных стран в каждой группе.

Я спросила Стивена Бернарда, почему он предпочел тона вместо оттенков. И вот что он ответил:

«Логика в такой покраске в том, чтобы сделать линии максимально отличными друг от друга на каждом графике, но, в то же время, сохранить сортировку во всех четырех графиках. [...] Тот подход, который я применил, позволяет зрителю понять из первого графика, что темно-синий представляет собой первое место, розовый — второе, голубой — третье, а зеленый — четвертое. Так, зритель может легко считать данные с других трех графиков».

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

И все же мне кажется, что тона неплохо работают в графике Стивена. Ведь, как всегда, стоит вопрос: чем оправдан ваш выбор типа палитры? В данном случае, The Financial Times выбрали качественную палитру, чтобы помочь читателям лучше различать линии, переплетающиеся на графике, вместо того, чтобы подчеркнуть их порядок.

Выводы

Итак, благодаря Лизе Шарлотте Мут, мы можем подвести следующие итоги:
• Используйте тона, когда у значений нет изначального порядка
• Используйте оттенки или градиенты, когда у значений есть порядок
• Используйте оттенки, чтобы подчеркнуть подразумеваемый порядок
• Используйте оттенки для выделения подкатегорий
• Используйте оттенки, чтобы разграничить первостепенные и второстепенные категории
• Используйте оттенки, чтобы сделать отдельные категории менее яркими и более удобными для чтения для людей, страдающих дальтонизмом
• Используйте тона, чтобы лучше обособить категории

Список литературы

  1. Синтия Брюэр: «Рекомендации по выбору цвета для карт и визуализации», с. 136 по цитате в: «Визуализация в современной картографии», том 2, 1994.
  2. Adobe Spectrum: Цвет для визуализации данных, 2019.
  3. Юусо Копонен, Джонатан Хильден: «Руководство по визуализации данных», 2019, с. 73-79.

How-to: модель GPT-2 для получения логических выводов с помощью Amazon SageMaker

Время чтения текста – 23 минуты

Не так давно, на конференции Linq мы представили генератор твитов в стиле Илона Маска. Для его создания мы взяли готовую модель GPT-2 Medium и дообучили/стилизовали ее на своем датасете. По-английски такой процесс называется fine-tuning. Модель требует достаточное количество ресурсов для этого, так что не каждый может это сделать локально на своем ПК. Однако, вопрос решается, если использовать, например, Google Colab. Мы же дообучали модель на платформе Kaggle.

Но после этого появляется новая задача — модель нужно развернуть, чтобы создать полноценный сервис. Конечно, существует множество различных решений. Ранее, при создании генератора телеграм-постов в стиле Артемия Лебедева мы обращались к сервису Yandex DataSphere, а после даже написали небольшой гайд о том, как там развернуть модель. Теперь же мы расскажем о развертывании модели GPT-2 для получения логических выводов в режиме реального времени (Real-time inference) с помощью Amazon SageMaker.

  1. Заходим в аккаунт AWS.
  1. Так как мы хотим задеплоить стилизованную модель, нам необходимо загрузить в облако необходимые файлы. В поиске вбиваем S3, выбираем первый сервис.

  1. Нажимаем Create bucket.

  1. Далее проводим конфигурацию бакета: вводим его имя, выбираем регион, настраиваем права доступа и т. д. Для простоты достаточно ввести данные только в графе General configuration.

  1. После конфигурирования нажимаем Create bucket.

  1. Затем нас перебросит на страницу Amazon S3, где можно будет увидеть что-то подобное:

  1. Далее надо загрузить сами файлы модели. Нажимаем на имя созданного бакета и в открывшемся окне нажимаем Upload.

  1. Перетаскиваем архив с файлами модели в нужную область или нажимаем Add files. При необходимости можно выстроить иерархию внутри бакета путем создания папок с помощью Add folders. Здесь важно отметить, что файлы модели должны быть в архиве с расширением tar.gz.

  1. Нажимаем Upload и ждем завершение загрузки.

  1. После успешной загрузки архива, перейдем непосредственно к деплою модели. В поиске вбиваем SageMaker, выбираем первый сервис.

  1. Для работы с с этим сервисом необходимо предварительно настроить SageMaker Domain, для этого нажимаем Get Started на баннере New to Sagemaker?.

  1. Для простой конфигурации 1 пользователя выбираем Quick setup и нажимаем Set up SageMaker Domain.

  1. Заполняем имя пользователя и настраиваем роль для исполнения. Для этого можем создать новую роль и указать в ней то, к каким бакетам S3 у пользователя будет доступ. Для простоты дадим доступ ко всем бакетам.

  1. Нажимаем Submit.

  1. Придется немного подождать, пока SageMaker Domain и пользователь будут сконфигурированы.

  1. После завершения настройки, среди пользователей появится созданный нами и можно будет запустить Studio, нажав на Launch app. SageMaker Studio — IDE, позволяющая работать работать с Jupyter ноутбуками в облаке AWS.

  1. Тут тоже придется немного подождать.

  1. Наконец, мы попадем в SageMaker Studio. Переключаясь между вкладками с помощью панели слева, можно:
    • Просмотреть рабочий репозиторий, где будут храниться ноутбуки и прочие файлы;
    • Просмотреть запущенные инстансы и приложения, Kernel и Terminal Sessions;
    • Работать с Git репозиторием;
    • Управлять ресурсами SageMaker;
    • Устанавливать разрешения для Jupyter ноутбуков.

  1. Отдельно выделим SageMaker JumpStart. Этот сервис предлагает предварительно обученные модели с открытым исходным кодом для широкого спектра задач. Вы можете обучить и настроить эти модели перед тем как развернуть их. JumpStart также предоставляет шаблоны решений для настройки инфраструктуры для распространенных случаев использования и исполняемые ноутбуки для машинного обучения с помощью SageMaker.

  1. Несмотря на наличие готовых решений, для деплоя нашей fine-tuned модели GPT-2 мы создадим новый ноутбук, где пропишем все, что нам нужно. Для этого нажмем на + в голубом прямоугольнике сверху слева. Откроется вкладка Launcher, пролистаем вниз до секции Notebooks and compute resources и выберем там Notebook Python 3.

  1. Придется немного подождать, прежде чем ядро ноутбука будет готово к работе.
  1. Наконец, можно писать код.

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

  1. Во время работы с ноутбуком вы платите за время его использования с учетом типа выбранного инстанса.
  1. Для простого деплоя нашей модели можем воспользоваться готовой конфигурацией от Hugging Face. Нажимаем на кнопку Deploy, выбираем там Amazon SageMaker, выбираем задачу (в нашем случае это Text Generation) и конфигурацию (в нашем случае это AWS), копируем код в наш ноутбук.

  1. Так как мы используем свою дообученную модель, а не готовую из репозитория Hugging Face, нам надо сделать небольшие изменения в коде. Комментируем в словаре hub строку с ключом ‘HF_MODEL_ID’ и в конструкторе HuggingFaceModel добавляем ключ model_data, куда пишем путь до нашего архива с файлами модели:
# Hub Model configuration. https://huggingface.co/models
hub = {
	# 'HF_MODEL_ID':'gpt2-medium',
	'HF_TASK':'text-generation'
}
 
# create Hugging Face Model Class
huggingface_model = HuggingFaceModel(
	transformers_version='4.17.0',
	pytorch_version='1.10.2',
	py_version='py38',
	env=hub,
	role=role, 
      model_data='s3://my-bucket-for-gpt2/gpt2-medium-musk.tar.gz',
)
  1. В методе deploy объекта huggingface_model мы можем выбрать, на каком инстансе произойдет развертывание нашей модели, указав его в параметре instance_type. Большинство инстансов может быть недоступно в связи с отсутствием нужных квот и их придется запрашивать в поддержке AWS. В этом случае вы увидите подобную ошибку:

  1. Если модель была успешно создана и развернута (для этого придется немного подождать), то можно вызвать метод predict.

  1. Для того, чтобы обращаться к инстансу извне AWS, придется создать Access key.
  • В поиске вбиваем IAM, выбираем первый сервис.

  • В открывшемся окне выбираем вкладку User и нажимаем на имя пользователя, под которым мы работаем.

  • Переходим на вкладку Security credentials и нажимаем Create access key.

  • Копируем Access key ID и Secret access key и сохраняем их в надежном месте.
  1. Далее нужно узнать имя созданного эндпоинта с моделью. В студии на левой панели выбираем вкладку SageMaker resources, выбираем ресурс Endpoints и дважды кликаем по имени нашего эндпоинта. Откроется вкладка с деталями, откуда мы сможем скопировать его имя.

  1. Теперь напишем код для обращения к модели извне.
import boto3
import json
import time
 
endpoint_name = '<my_endpoint_name>'
aws_access_key_id = '<my_aws_access_key_id>'
aws_secret_access_key = '<my_aws_secret_access_key>'
 
sagemaker_runtime = boto3.client(
    "sagemaker-runtime", 
    region_name='us-east-1',
    aws_access_key_id=aws_access_key_id, 
    aws_secret_access_key=aws_secret_access_key
)
 
data = {
    "inputs": "Weed is",
}
 
response = sagemaker_runtime.invoke_endpoint(
    EndpointName=endpoint_name, 
    ContentType='application/json',
    Body=json.dumps(data, ensure_ascii=False).encode('utf8')
)
 
print(response['Body'].read().decode('utf-8'))

И протестируем:

  1. Стоит отметить, что если следовать описанным выше шагам, то модель будет использовать для генерации параметры по умолчанию. Чтобы добавить кастомную логику загрузки модели, пред- и постобработки данных, предсказания, можно создать файл inference.py в студии рядом с вашим ноутбуком и там переопределить нужные вам методы. Подробнее о них можно почитать тут.

  • Чтобы этот скрипт использовался при развертывании модели, в конструкторе HuggingFaceModel нужно добавить еще один параметр:
huggingface_model = HuggingFaceModel(
	transformers_version='4.17.0',
	pytorch_version='1.10.2',
	py_version='py38',
	env=hub,
	role=role, 
      model_data='s3://my-bucket-for-gpt2/gpt2-medium-musk.tar.gz',
      entry_point='inference.py'
)
  • Разумеется, для уже созданных эндпоинтов такое изменение не будет учтено. Нужно будет заново задеплоить модель.
  • Приведем пример файла inference.py, который можно использовать для модели GPT-2:
import json
import torch
from transformers import GPT2Config, GPT2Tokenizer, GPT2LMHeadModel
 
def model_fn(model_dir):
    configuration = GPT2Config.from_pretrained(model_dir, output_hidden_states=False)
    tokenizer = GPT2Tokenizer.from_pretrained(
        model_dir,
        bos_token='<|sos|>', 
        eos_token='<|eos|>', 
        pad_token='<|pad|>'
    )
    model = GPT2LMHeadModel.from_pretrained(model_dir, config=configuration)
    model.resize_token_embeddings(len(tokenizer))
    model.eval()
    return (model, tokenizer)
 
def input_fn(request_body, request_content_type):
    if request_content_type == "application/json":
        request = json.loads(request_body)
    else:
        request = request_body
    return request
 
def predict_fn(data, model_tokenizer):
    model, tokenizer = model_tokenizer
 
    inputs = data.pop("inputs", "")
    max_length = data.pop("max_length", 50)
 
    input_ids = torch.tensor(tokenizer.encode(f'<|sos|>{inputs}')).unsqueeze(0)
    outputs = model.generate(
                input_ids, 
                max_length=max_length,
                bos_token_id=tokenizer.bos_token_id,
                pad_token_id=tokenizer.pad_token_id,
                eos_token_id=tokenizer.eos_token_id, 
                do_sample=True,
                top_k=0,
                top_p=0.95,
                no_repeat_ngram_size=4
    )
    decoded_output = tokenizer.decode(outputs[0])
 
    return {"decoded_output": decoded_output}
  1. В конце работы с ноутбуком в студии нужно будет обязательно вырубить все используемые для этого ресурсы. К сожалению, при простом закрытии вкладки со студией, ресурсы не освобождаются, поэтому приходится это делать самостоятельно. В противном случае, с вас будет списываться плата за их использование. Итак, вырубить все ненужное можно в самой студии, выбрав на панели слева вкладку Running Terminal and Kernels.

  • После закрытия ноутбука проверить то, что все ресурсы освобождены, можно на странице Amazon SageMaker. Для этого нужно будет нажать на имя пользователя и посмотреть на статус вашего приложения, тип которого KernelGateway. Статус должен быть Deleted.

  1. После того, как вы перестанете нуждаться в развернутой модели, нужно будет удалить эндпоинт. Если вы не освободили ресурсы, используемые ноутбуком в студии, то это можно будет сделать прямо оттуда, прописав строку:
predictor.delete_endpoint()
  • Иначе вы можете удалить эндпоинт, перейдя на страницу сервиса Amazon SageMaker. Там на левой панели нужно будет выбрать вкладку Inference, в выпадающем списке нажать Endpoints, затем справа выбрать нужный эндпоинт, нажать Actions и Delete.

  • Также можно будет удалить созданные модели, перейдя в Inference→Models, и конфигурации эндпоинтов, перейдя в Inference→Enpoint Configurations.

Итак, мы рассказали о том, как развертывать стилизованную модель GPT-2 для получения логических выводов в режиме реального времени (Real-time inference) с помощью Amazon SageMaker. Стоит отметить, что существует несколько вариантов развертывания, каждый из которых имеет свои особенности, например, асинхронность, пакетная обработка, наличие холодного старта, т.д. Использование того или иного варианта зависит от поставленных требований.

Подробнее про другие механизмы деплоя с помощью Amazon SageMaker читайте тут.

Ранее Ctrl + ↓