Сделал подборку туториалов по промпт инжинирингу для LLM:
Anthropic
https://docs.anthropic.com/en/docs/build-with-claude/prompt-engineering/overview
https://github.com/anthropics/prompt-eng-interactive-tutorial
OpenAI
https://help.openai.com/en/articles/6654000-best-practices-for-prompt-engineering-with-the-openai-api
Супер методичка от Гугла по Gemini
https://services.google.com/fh/files/misc/gemini-for-google-workspace-prompting-guide-101.pdf
Материалы на русском
https://www.promptingguide.ai/ru
Хотя каждый пишет доки по промптам под свою конкретную архитектуру и апишку, но в целом практики применимы ко всем моделям.
Сохраняем себе!
Anthropic
https://docs.anthropic.com/en/docs/build-with-claude/prompt-engineering/overview
https://github.com/anthropics/prompt-eng-interactive-tutorial
OpenAI
https://help.openai.com/en/articles/6654000-best-practices-for-prompt-engineering-with-the-openai-api
Супер методичка от Гугла по Gemini
https://services.google.com/fh/files/misc/gemini-for-google-workspace-prompting-guide-101.pdf
Материалы на русском
https://www.promptingguide.ai/ru
Хотя каждый пишет доки по промптам под свою конкретную архитектуру и апишку, но в целом практики применимы ко всем моделям.
Сохраняем себе!
Anthropic
Prompt engineering overview - Anthropic
Bid Cap, или как связать CPM и CPA в одной формуле? 💵
У каждого объявления есть общая ценность за целевое действие (допустим в случаю с CPA за конверсию). Назовем ее
Мы ограничиваем
Ставку за показ мы можем связать с CPM достаточно просто
В результате получим
Альтернативно, если оценить вероятность конверсии при условии показа напрямую мы не можем, то вместо этого воспользуемся цепочкой показ -> клики -> конверсия и запишем в формулу CTR (клики при условии показа) и CR (конверсия при условии клика), залогированные в DSP (или таргеты РК)
Тогда bid cap пересчитается следующим образом
Пример:
Рассмотрим гео с CPM $15, и РК с таргетами CTR 3% и CR 2%. Посчитаем bid cap
Т.е. с такой максимальной ставкой мы сможем себе обеспечить необходимые таргеты CTR, CR. Мы выставили bid cap, исходя из цены конверсии. Если в процессе открутки РК начинает задирать вверх наблюдаемый дневной CPM, то умеряем аппетит и постепенно снижаем bid cap с шагом, например 10%
У каждого объявления есть общая ценность за целевое действие (допустим в случаю с CPA за конверсию). Назовем ее
bid_per_action
. Умножив эту ценность на estimated_action_rate
частоту целевых действий при условии показа (поскольку DSP списывает с нас деньги за показ), то получим следующую формулу
bid_per_impression = bid_per_action x estimated_action_rate
Мы ограничиваем
bid_per_action
, когда устанавливаем bid cap. Для будущей открутки estimated_action_rate мы можем оценить, как p(conversion | features)
вероятность конверсии для текущего показа, за который идет борьба в аукционе при условии признаков исторической эффективности слота, аудитории, контекста etc. p(conversion | features)
может предсказываться для каждого слота классической бинарной ML-моделью.
estimated_action_rate = p(conversion | features)
Ставку за показ мы можем связать с CPM достаточно просто
CPM = bid_per_impression x 1000
В результате получим
bid_cap = CPM / (1000 x p(conversion | features))
Альтернативно, если оценить вероятность конверсии при условии показа напрямую мы не можем, то вместо этого воспользуемся цепочкой показ -> клики -> конверсия и запишем в формулу CTR (клики при условии показа) и CR (конверсия при условии клика), залогированные в DSP (или таргеты РК)
estimated_action_rate = CTR x CR
Тогда bid cap пересчитается следующим образом
bid_cap = CPM / (1000 x CTR x CR)
Пример:
Рассмотрим гео с CPM $15, и РК с таргетами CTR 3% и CR 2%. Посчитаем bid cap
bid cap = $15 / (1000 x 0.03 x 0.02) = $25
Т.е. с такой максимальной ставкой мы сможем себе обеспечить необходимые таргеты CTR, CR. Мы выставили bid cap, исходя из цены конверсии. Если в процессе открутки РК начинает задирать вверх наблюдаемый дневной CPM, то умеряем аппетит и постепенно снижаем bid cap с шагом, например 10%
Forwarded from FaangTalk Channel (Dima V)
Привет! Сегодня стрим в 21:15 мск
https://youtube.com/live/E_0V6CApFq4?feature=share
Жмите Notify чтобы не пропустить
В гостях Evgenii Munin, автор канала @dsinsights
В выпуске поговорим об архитектуре рекламных платформ ставок (RTB — Real-Time Bidding), включая программматик-цепочки, ML Pipeline, Data Pipeline и механизм формирования ставок (Bidder). Евгений расскажет о фильтрации трафика, динамическом ценообразовании и оптимизации резервной цены аукциона. Также будут рассмотрены практические подходы к построению высоконагруженных систем в сфере AdTech.
Ключевые слова: Программматик-реклама,SSP (Supply Side Platform),DSP (Demand Side Platform),Ad Server,ML Pipeline,Обучение моделей,Фильтрация трафика,Динамическое ценообразование,ONNX Runtime,Биддер,Резервная цена (floor price), Data Pipeline, Kafka,S3, BigQuery, Flink, Traffic Shaping, Оптимизация маржинальности, Аукцион ставок (RTB), Потоковая обработка данных
https://youtube.com/live/E_0V6CApFq4?feature=share
Жмите Notify чтобы не пропустить
В гостях Evgenii Munin, автор канала @dsinsights
В выпуске поговорим об архитектуре рекламных платформ ставок (RTB — Real-Time Bidding), включая программматик-цепочки, ML Pipeline, Data Pipeline и механизм формирования ставок (Bidder). Евгений расскажет о фильтрации трафика, динамическом ценообразовании и оптимизации резервной цены аукциона. Также будут рассмотрены практические подходы к построению высоконагруженных систем в сфере AdTech.
Ключевые слова: Программматик-реклама,SSP (Supply Side Platform),DSP (Demand Side Platform),Ad Server,ML Pipeline,Обучение моделей,Фильтрация трафика,Динамическое ценообразование,ONNX Runtime,Биддер,Резервная цена (floor price), Data Pipeline, Kafka,S3, BigQuery, Flink, Traffic Shaping, Оптимизация маржинальности, Аукцион ставок (RTB), Потоковая обработка данных
YouTube
#FaangTalk 80 - System Design AdTech платформы
Канал с анонсами https://www.tgoop.com/faangtalk_news
Чат по подготовке к интервью: https://www.tgoop.com/faangtalk
В гостях
Evgenii Munin https://www.tgoop.com/dsinsights
Выпуск посвящён основам работы рекламных платформ ставок (RTB — Real-Time Bidding), включая программматик…
Чат по подготовке к интервью: https://www.tgoop.com/faangtalk
В гостях
Evgenii Munin https://www.tgoop.com/dsinsights
Выпуск посвящён основам работы рекламных платформ ставок (RTB — Real-Time Bidding), включая программматик…
Мы провели стрим с FaangTalk
Вышло классно, я сделал многовато материала по ML части, но зато дальше хорошо обсудили Кафку, кэширование и хранилища данных. Спасибо всем, кто был на стриме 🥰
Хочу отдельно поблагодарить ребят:
Ника @analytics_engineer ⬅️
Рекомендую его канал по Data Engineering'у и DE практикам в крупных компаниях. Также здесь проводятся митапы по dbt
Диму @faangtalk @javaswag ⬅️
Всем, кто готовится или планирует готовиться к собесам по литкоду, System Design в FAANG и не только, нужно в этот чат и в одноименный канал на ютубе
Вышло классно, я сделал многовато материала по ML части, но зато дальше хорошо обсудили Кафку, кэширование и хранилища данных. Спасибо всем, кто был на стриме 🥰
Хочу отдельно поблагодарить ребят:
Ника @analytics_engineer ⬅️
Рекомендую его канал по Data Engineering'у и DE практикам в крупных компаниях. Также здесь проводятся митапы по dbt
Диму @faangtalk @javaswag ⬅️
Всем, кто готовится или планирует готовиться к собесам по литкоду, System Design в FAANG и не только, нужно в этот чат и в одноименный канал на ютубе
Предсказываем клики по данным Criteo
10 лет назад на Каггле проводилось соревнование по предсказанию кликов на рекламные слоты на сайтах пользователей на мобилках, организованное Criteo. С того момента прошло много времени, но датасет не утратил своей актуальности в среде адтеха и до сих пор служит бенчмарком для сравнения ML моделей, пресказывающих клики. Актуальный лидерборд архитектур можно найти на PapersWithCode
И сегодня мы разбирем одну из таких SOTA архитектур предсказания кликов FinalMLP
Что с данными?
Для начала разберемся, какие данные доступны для обучения. Criteo опубликовала данные с 7 дней трафика своих пользователей mobile web. Это примерно 340Gb данных. Все фичи поделены на 2 категории: пользовательские (user) и контекстные (publisher, инфа о сайте). Значения всех фичей анонимизированы. И собственно бинарный таргет о наличии/ отсутствии клика на рекламный слот.
Как работает?
Как заявлено в статье на Arxiv, архитектура FinalMLP состоит из следующих основных компонент
➡️ Feature Embedding Layer
Преобразуем разреженные признаки в плотный (dense) вектора. Категориальные признаки one-hot encod'им и перемножаем на обучаемую матрицу весов эмбеддингов. Числовые фичи бакетизируем и дальше обрабатываем, как категориальные. Multi-valued фичи (или те же списки, типа
➡️ Feature Selection Layer
Слой отбора признаков: оценивает важность признаков, понижает влияние зашумленных, усиливает значимые. Здесь используется т.н. gating-based feature selection, который выдает веса важности каждого признака, пропустив их через сигмоиду и умножив на 2. В итоге эти веса лежат в диапазоне 0..2. Далее они перемножаются с эмбеддингами фичей. Разбиваем фичи на 2 ветки на пользовательские и контекстные и прогоняем весь процесс отдельно на каждую ветку. Так мы получаем входы в MLP (персептрон) два потока.
➡️ Stream-Level Fusion Layer
Теперь нам нужно объединить эти 2 потока (пользовательский и контекстный). Вместо простого сложения или конкатенации в FinalMLP предлагают т.н. bilinear fusion. Если в двух слова, как это работает, то вектора с 2х веток пользовательских и контекстиных эмбеддингов разбиваются на k частей (голов). В каждой части применяется билинейное преоблазование. Далее результаты пропускаются через сигмоиду, чтобы получить вероятности.
Билинейное преобразование для одной головы записывается следующим образом
Здесь
- w1, w2 - это веса модели для ветки o1, o2 соответственно
- W3 - это матрица билинейного преобразования (тоже обучаемая)
- b - intersect
Для k голов выходная вероятность запишется
Такой подход обработки признаков на два потока дает следующие преимущества по сравнению с классической табличной моделью, например тем же CatBoost'ом:
- Взаимодействие фичей задается с помощью отдельной матрицы W3, что дает возможность обучать более сложные зависимости
- В FinalMLP мы конвертируем все фичи в эмбеддинги, что особенно помогает в случае разреженных фичей
Далее мы разберем имплементацию FinalMLP на PyTorch и запустим ее с помощью либы FuxiCTR
10 лет назад на Каггле проводилось соревнование по предсказанию кликов на рекламные слоты на сайтах пользователей на мобилках, организованное Criteo. С того момента прошло много времени, но датасет не утратил своей актуальности в среде адтеха и до сих пор служит бенчмарком для сравнения ML моделей, пресказывающих клики. Актуальный лидерборд архитектур можно найти на PapersWithCode
И сегодня мы разбирем одну из таких SOTA архитектур предсказания кликов FinalMLP
Что с данными?
Для начала разберемся, какие данные доступны для обучения. Criteo опубликовала данные с 7 дней трафика своих пользователей mobile web. Это примерно 340Gb данных. Все фичи поделены на 2 категории: пользовательские (user) и контекстные (publisher, инфа о сайте). Значения всех фичей анонимизированы. И собственно бинарный таргет о наличии/ отсутствии клика на рекламный слот.
Как работает?
Как заявлено в статье на Arxiv, архитектура FinalMLP состоит из следующих основных компонент
➡️ Feature Embedding Layer
Преобразуем разреженные признаки в плотный (dense) вектора. Категориальные признаки one-hot encod'им и перемножаем на обучаемую матрицу весов эмбеддингов. Числовые фичи бакетизируем и дальше обрабатываем, как категориальные. Multi-valued фичи (или те же списки, типа
adomain
) обрабатываем следующим образом: обрезаем списки длиной до средней длины k. Далее заводим эти фичи в one-hot вектора длины k и снова перемножаем на веса эмбеддинга.➡️ Feature Selection Layer
Слой отбора признаков: оценивает важность признаков, понижает влияние зашумленных, усиливает значимые. Здесь используется т.н. gating-based feature selection, который выдает веса важности каждого признака, пропустив их через сигмоиду и умножив на 2. В итоге эти веса лежат в диапазоне 0..2. Далее они перемножаются с эмбеддингами фичей. Разбиваем фичи на 2 ветки на пользовательские и контекстные и прогоняем весь процесс отдельно на каждую ветку. Так мы получаем входы в MLP (персептрон) два потока.
➡️ Stream-Level Fusion Layer
Теперь нам нужно объединить эти 2 потока (пользовательский и контекстный). Вместо простого сложения или конкатенации в FinalMLP предлагают т.н. bilinear fusion. Если в двух слова, как это работает, то вектора с 2х веток пользовательских и контекстиных эмбеддингов разбиваются на k частей (голов). В каждой части применяется билинейное преоблазование. Далее результаты пропускаются через сигмоиду, чтобы получить вероятности.
Билинейное преобразование для одной головы записывается следующим образом
BF(o1, o2) = b + w1 * o1 + w2 * o + o1 * W3 * o2
Здесь
- w1, w2 - это веса модели для ветки o1, o2 соответственно
- W3 - это матрица билинейного преобразования (тоже обучаемая)
- b - intersect
Для k голов выходная вероятность запишется
proba_pred = sigma(sum_k(BF(o1i, o2i)))
Такой подход обработки признаков на два потока дает следующие преимущества по сравнению с классической табличной моделью, например тем же CatBoost'ом:
- Взаимодействие фичей задается с помощью отдельной матрицы W3, что дает возможность обучать более сложные зависимости
- В FinalMLP мы конвертируем все фичи в эмбеддинги, что особенно помогает в случае разреженных фичей
Далее мы разберем имплементацию FinalMLP на PyTorch и запустим ее с помощью либы FuxiCTR
Kaggle
Display Advertising Challenge
Predict click-through rates on display ads
Как размечать данные для промышленных ML моделей правильно?
Зачем это надо?
В индустрии существует такая парадигма Data-Centric AI. Ее в свое время сформулировал великий Andrew Ng. Она гласит, что важна не гонка за сложными моделями, а системная работа с данными. Т.е. большинство ошибок моделей происходит из-за некачественных, грязных данных. Не забываем правило: garbage in — garbage out.
При этом мало просто передать данные разметчикам, получить аннотации. Чаще всего появятся нюансы, из-за которых качественной разметки получить не удастся.
➡️В чем проблема разметки данных в промышленном масштабе?
- Плавающие инструкции: инструкция живёт в голове у тимлида, меняется в процессе, а команда может размечать по старой. В результате имеем в одном датасете — разные логики.
- Отсутствие версионирования: не фиксируется, кто, когда и по какой логике размечал. При проверке сложно понять: ошибка ли это или просто старая версия правила.
- Отсутствие описания пограничных случаев: инструкция охватывает только «идеальные» примеры. Разметчики по-разному трактуют сложные случаи → низкое согласие между ними.
- Отсутствие обратной связи: разметчик не знает, что он ошибается. Ошибки повторяются, система не обучается.
- Один разметчик — один мир: без перекрёстной проверки (двое размечают одно и то же) невозможно отследить согласие.
- Отсутствие контрольных примеров (golden set): Без заранее проверенного набора эталонных аннотаций невозможно оценить качество работы всей команды.
- Человеческий фактор: усталость, выгорание, желание «быстрее сдать». Если не учитывать мотивацию и рутину, качество страдает даже у отличных специалистов.
➡️ Настраиваем процесс разметки, начиная с коммуникаций
Четко ставим задачу и делаем пошаговый онбординг для разметчиков. Проходимся ручками по калибровочным задачам. Также наладиживаем канал обратной связи: куда задавать вопросы, как быстро получать ответы.
➡️ Валидация разметки
Проверка — обязательный этап, организовать ее можно следующим способами:
- Peer review: один разметчик проверяет другого
- Экспертная проверка: отдельный специалист валидирует выборку
- Перекрёстная аннотация: две разметки сравниваются, рассчитывается согласие (Cohen's Kappa)
- Модельный консенсус: если есть обученная модель, используйте её для нахождения расхождений.
К примеру, если мы сравниваем две разметки и рассчитываем согласие, то можем в случае разногласия передать на экспертную оценку. Или можем собрать консилиум из нескольких разметчиков, и выяснисть, кто прав. Или если меняются инструкции разметки, и определяются более чёткие границы для минимизации расхождения в пограничных случаях.
➡️ Версионирование инструкций
Поступаем аналогично, как если мы версионируем код в гите. Новые/ правленные инструкции ревьюим. Обязательно указываем, какая версия применялась при разметке конкретной партии данных. Если в процессе возникали спорные кейсы — добавляем их в инструкцию с пометкой «обновлено».
➡️ Контроль и метрики
Управлять можно только тем, что измеряется. Метрики помогают выявить слабые места и улучшить процесс:
- Согласие между аннотаторами (Inter-Annotator Agreement)
- Процент правок после валидации
- Скорость + качество: лучше жертвовать скоростью ради стабильности
- Количество уточнений по инструкции: если их много, нужно менять инструкцию
➡️Автоматизация процесса
- Предразметка моделью: человек проверяет вместо разметки с нуля
- Автопроверки на лету: например, логические правила: «если выбрано А, поле B не может быть пустым»
- Интеграция с тулзами: Label Studio, Prodigy, doccano позволяют встроить автоматическую проверку и экспорт / импорт в пайплайн
- Контроль качества через скрипты: сравнение с эталонными разметками, метрики качества, алерты на резкое падение согласия.
Зачем это надо?
В индустрии существует такая парадигма Data-Centric AI. Ее в свое время сформулировал великий Andrew Ng. Она гласит, что важна не гонка за сложными моделями, а системная работа с данными. Т.е. большинство ошибок моделей происходит из-за некачественных, грязных данных. Не забываем правило: garbage in — garbage out.
При этом мало просто передать данные разметчикам, получить аннотации. Чаще всего появятся нюансы, из-за которых качественной разметки получить не удастся.
➡️В чем проблема разметки данных в промышленном масштабе?
- Плавающие инструкции: инструкция живёт в голове у тимлида, меняется в процессе, а команда может размечать по старой. В результате имеем в одном датасете — разные логики.
- Отсутствие версионирования: не фиксируется, кто, когда и по какой логике размечал. При проверке сложно понять: ошибка ли это или просто старая версия правила.
- Отсутствие описания пограничных случаев: инструкция охватывает только «идеальные» примеры. Разметчики по-разному трактуют сложные случаи → низкое согласие между ними.
- Отсутствие обратной связи: разметчик не знает, что он ошибается. Ошибки повторяются, система не обучается.
- Один разметчик — один мир: без перекрёстной проверки (двое размечают одно и то же) невозможно отследить согласие.
- Отсутствие контрольных примеров (golden set): Без заранее проверенного набора эталонных аннотаций невозможно оценить качество работы всей команды.
- Человеческий фактор: усталость, выгорание, желание «быстрее сдать». Если не учитывать мотивацию и рутину, качество страдает даже у отличных специалистов.
➡️ Настраиваем процесс разметки, начиная с коммуникаций
Четко ставим задачу и делаем пошаговый онбординг для разметчиков. Проходимся ручками по калибровочным задачам. Также наладиживаем канал обратной связи: куда задавать вопросы, как быстро получать ответы.
➡️ Валидация разметки
Проверка — обязательный этап, организовать ее можно следующим способами:
- Peer review: один разметчик проверяет другого
- Экспертная проверка: отдельный специалист валидирует выборку
- Перекрёстная аннотация: две разметки сравниваются, рассчитывается согласие (Cohen's Kappa)
- Модельный консенсус: если есть обученная модель, используйте её для нахождения расхождений.
К примеру, если мы сравниваем две разметки и рассчитываем согласие, то можем в случае разногласия передать на экспертную оценку. Или можем собрать консилиум из нескольких разметчиков, и выяснисть, кто прав. Или если меняются инструкции разметки, и определяются более чёткие границы для минимизации расхождения в пограничных случаях.
➡️ Версионирование инструкций
Поступаем аналогично, как если мы версионируем код в гите. Новые/ правленные инструкции ревьюим. Обязательно указываем, какая версия применялась при разметке конкретной партии данных. Если в процессе возникали спорные кейсы — добавляем их в инструкцию с пометкой «обновлено».
➡️ Контроль и метрики
Управлять можно только тем, что измеряется. Метрики помогают выявить слабые места и улучшить процесс:
- Согласие между аннотаторами (Inter-Annotator Agreement)
- Процент правок после валидации
- Скорость + качество: лучше жертвовать скоростью ради стабильности
- Количество уточнений по инструкции: если их много, нужно менять инструкцию
➡️Автоматизация процесса
- Предразметка моделью: человек проверяет вместо разметки с нуля
- Автопроверки на лету: например, логические правила: «если выбрано А, поле B не может быть пустым»
- Интеграция с тулзами: Label Studio, Prodigy, doccano позволяют встроить автоматическую проверку и экспорт / импорт в пайплайн
- Контроль качества через скрипты: сравнение с эталонными разметками, метрики качества, алерты на резкое падение согласия.
LandingAI
Data-Centric AI
Data-centric AI is transforming machine learning by prioritizing data quality over model tweaks. Explore how it enhances AI accuracy, efficiency, and more.
Как подготовить ТЗ для разработчика? 🗂
Хотя я официально не числюсь на позиции менеджера, мне периодически приходится привлекать коллег-разработчиков на проекты, которые я веду или фичи, которые находятся в периметре моей ответственности, например алгоритмы для паблишеров, Header Bidding, фильтрация трафика и с некоторого времени алгоритмы ставок для YouTube'а.
При этом если человеку попробовать сумбурно накидать идей off top of my head, то запросто можно получить кота в мешке и впоследствии дружно плясать с бубном, объясняя менеджерам, как работает продукт. Чтобы этого избежать, готовим ТЗ. Сейчас расскажу, как.
1️⃣ Даем контекст. Почему возникла потребность в фиче, какие боли мы решаем. Упоминаем контекст, и что идет не так на данный момент. Например, хотим повысить долю in-view для формата shorts'ов и infeed для видео рекламы на YouTube. Делаем это, поскольку досмотры на этих форматах не оптимизируешь (они максимум 2%), а дать возможность заводить РК под эти форматы нужно. Для этого хотим по умному рулить ставкой, чтобы выдать высокий view-through rate и при этом не срезать трафик показов и открученный таргет по бюджету.
2️⃣Определяем клиентов, которые будут пользоваться фичей. Кто они и где обитают. Например бренды-рекламодатели, которые хотят повысить brand-awareness и заводят РК через Google DV360 под трафик ютуба.
3️⃣ Описываем сценарии использования (user flow). Здесь всегда хочется впихнуть невпихуемое, но вместо этого определяем базовые сценарии - без которых продукт попросту не имеет смысла. Желаемые вещи - их хорошо бы иметь, но можно допилить после релиза. Например клиент заводит РК в интерфейсе DSP и ставит галку напротив поля autobidding. На нашей стороне мы эту настройку читаем по API DSP и запускаем алгоритм на данного клиента.
4️⃣ Фиксируем основной стек. Фреймворки, тулзы, языки программирования, OLAP vs OLTP базы etc. Здесь все достаточно понятно, используем тот стек, который наша команда умеет поддерживать и знает практики.
5️⃣ Описываем зависимости. Вероятнее всего ваш продукт будет крутиться не в вакууме, и нужно будет тянуть часть данных из корпоративной базы, метрики из DSP, залогировать дополнительный ивент в очереди. Для всего этого прикладываем нужные API и схемы к ТЗ.
6️⃣ Делаем макеты. Не чураемся использовать Jupyter Notebook и расписать, как алгоритм будет работать на примере семплированных или синтетических данных. Прикладываем нужные графики в ТЗ.
7️⃣ Поясняем Definition of Done. Какие критерии сделанной работы? Мы запускаем джобу на Airflow? Или мы релизим сборку в Container Registry? Как мы будем рулить релизом и запуском РК клиентов?
8️⃣ Делаем чек-лист для приема работы. На каких сценариях, РК, бюджетах мы прогоним продукт перед тем, как сказать, что он работает хорошо. Какие бизнес метрики будем мониторить, например VTR, budget spent, показы, CPM, CPV.
Объединяя вместе все вышеперечисленные разделы, у нас получится мощное ТЗ, от которого менеджеры будут в восторге, а от исполнителей вам будет респект и уважение.
Хотя я официально не числюсь на позиции менеджера, мне периодически приходится привлекать коллег-разработчиков на проекты, которые я веду или фичи, которые находятся в периметре моей ответственности, например алгоритмы для паблишеров, Header Bidding, фильтрация трафика и с некоторого времени алгоритмы ставок для YouTube'а.
При этом если человеку попробовать сумбурно накидать идей off top of my head, то запросто можно получить кота в мешке и впоследствии дружно плясать с бубном, объясняя менеджерам, как работает продукт. Чтобы этого избежать, готовим ТЗ. Сейчас расскажу, как.
1️⃣ Даем контекст. Почему возникла потребность в фиче, какие боли мы решаем. Упоминаем контекст, и что идет не так на данный момент. Например, хотим повысить долю in-view для формата shorts'ов и infeed для видео рекламы на YouTube. Делаем это, поскольку досмотры на этих форматах не оптимизируешь (они максимум 2%), а дать возможность заводить РК под эти форматы нужно. Для этого хотим по умному рулить ставкой, чтобы выдать высокий view-through rate и при этом не срезать трафик показов и открученный таргет по бюджету.
2️⃣Определяем клиентов, которые будут пользоваться фичей. Кто они и где обитают. Например бренды-рекламодатели, которые хотят повысить brand-awareness и заводят РК через Google DV360 под трафик ютуба.
3️⃣ Описываем сценарии использования (user flow). Здесь всегда хочется впихнуть невпихуемое, но вместо этого определяем базовые сценарии - без которых продукт попросту не имеет смысла. Желаемые вещи - их хорошо бы иметь, но можно допилить после релиза. Например клиент заводит РК в интерфейсе DSP и ставит галку напротив поля autobidding. На нашей стороне мы эту настройку читаем по API DSP и запускаем алгоритм на данного клиента.
4️⃣ Фиксируем основной стек. Фреймворки, тулзы, языки программирования, OLAP vs OLTP базы etc. Здесь все достаточно понятно, используем тот стек, который наша команда умеет поддерживать и знает практики.
5️⃣ Описываем зависимости. Вероятнее всего ваш продукт будет крутиться не в вакууме, и нужно будет тянуть часть данных из корпоративной базы, метрики из DSP, залогировать дополнительный ивент в очереди. Для всего этого прикладываем нужные API и схемы к ТЗ.
6️⃣ Делаем макеты. Не чураемся использовать Jupyter Notebook и расписать, как алгоритм будет работать на примере семплированных или синтетических данных. Прикладываем нужные графики в ТЗ.
7️⃣ Поясняем Definition of Done. Какие критерии сделанной работы? Мы запускаем джобу на Airflow? Или мы релизим сборку в Container Registry? Как мы будем рулить релизом и запуском РК клиентов?
8️⃣ Делаем чек-лист для приема работы. На каких сценариях, РК, бюджетах мы прогоним продукт перед тем, как сказать, что он работает хорошо. Какие бизнес метрики будем мониторить, например VTR, budget spent, показы, CPM, CPV.
Объединяя вместе все вышеперечисленные разделы, у нас получится мощное ТЗ, от которого менеджеры будут в восторге, а от исполнителей вам будет респект и уважение.
Как настроить DataLake с медальной архитектурой данных на платформе ставок?
Стандартный способ построения DataLake в компании — это использование медальной архитектуры, при которой данные трансформируются по этапам.
1️⃣ Из каких этапов состоит медальон?
- 🥉Bronze : логируем данные as-is на выходе с очередей, БД, внешних запросов по API (Salesforce, DMP etc.)
- 🥈Silver : сырые логи вычищаются. Удаляем дубликаты, аукционы с отсутсвующим auction_id, невалидные реквесты
- 🥇Gold : аггрегируем данные Spark джобами (groupby user, country, campaign, publisher etc.), подготавливаем для записи в витрины
2️⃣Где могут возникнуть проблемы?
- Gold слой хаотичен и непригоден для общего пользования. В компании могут быть десятки команд, которые считают одни и те же вещи разными метриками и по разному их трактуют. Например, определение eCPM для supply/ demand команд разное. Или могут присутствовать два разных расчета относительной маржи (revenue - cost) / cost и (revenue - cost) / revenue в бухгалтерии и на платформе. Эти вещи наводят путаницу в использовании Gold слоя
- На Silver слой накладывается слишком много задач. Сюда могут накидать импорт данных из сырых логов, очистку, дедублирование, приведение к аналитике, предобработка для витрин. Т.е. когда в слой, который должен выполнять только техническую работу дополнительно накручивают бизнес-логику
- Мнение о том, что gold - "лучше", чем silver, а он "лучше", чем bronze. Здесь может дойти до того, что события или отдельные датасеты копируются по слоям потому, что дашборды аналитики разрешено строить только по gold слою
- Ownership. Кто управляет и поддерживает каждый слой?
3️⃣А как надо?
- Bronze: отвечает за запись данных. Схема данных соответствует источнику (очереди, API etc.). Данные партицируются по времени. Пишем либо в S3, либо в Snowflake, поскольку в нем можно создавать таблицы на основе сырых данных без необходимости делать дополнительную копию.
- Silver: Очищаем данные, делаем join'ы. Если есть вложенные полуструктурированные данные, то делаем их плоскими (explode). При этом сюда НЕ заводим бизнес-логику, а оставляем ее для следующего этапа. За этот и предыдущий этап отвечает, как правило, DE команда, которая поддерживает в том числе и стриминговую запись в bronze.
- Gold: Имплементим бизнес-логику и аггрегации для витрин. Чтобы они между командами не расходились, нужно согласовать схему данных, и назначение аггрегированных метрик между всеми стейкхолдорами в компании. Держим его максимально компактным и централизованным
Стандартный способ построения DataLake в компании — это использование медальной архитектуры, при которой данные трансформируются по этапам.
1️⃣ Из каких этапов состоит медальон?
- 🥉Bronze : логируем данные as-is на выходе с очередей, БД, внешних запросов по API (Salesforce, DMP etc.)
- 🥈Silver : сырые логи вычищаются. Удаляем дубликаты, аукционы с отсутсвующим auction_id, невалидные реквесты
- 🥇Gold : аггрегируем данные Spark джобами (groupby user, country, campaign, publisher etc.), подготавливаем для записи в витрины
2️⃣Где могут возникнуть проблемы?
- Gold слой хаотичен и непригоден для общего пользования. В компании могут быть десятки команд, которые считают одни и те же вещи разными метриками и по разному их трактуют. Например, определение eCPM для supply/ demand команд разное. Или могут присутствовать два разных расчета относительной маржи (revenue - cost) / cost и (revenue - cost) / revenue в бухгалтерии и на платформе. Эти вещи наводят путаницу в использовании Gold слоя
- На Silver слой накладывается слишком много задач. Сюда могут накидать импорт данных из сырых логов, очистку, дедублирование, приведение к аналитике, предобработка для витрин. Т.е. когда в слой, который должен выполнять только техническую работу дополнительно накручивают бизнес-логику
- Мнение о том, что gold - "лучше", чем silver, а он "лучше", чем bronze. Здесь может дойти до того, что события или отдельные датасеты копируются по слоям потому, что дашборды аналитики разрешено строить только по gold слою
- Ownership. Кто управляет и поддерживает каждый слой?
3️⃣А как надо?
- Bronze: отвечает за запись данных. Схема данных соответствует источнику (очереди, API etc.). Данные партицируются по времени. Пишем либо в S3, либо в Snowflake, поскольку в нем можно создавать таблицы на основе сырых данных без необходимости делать дополнительную копию.
- Silver: Очищаем данные, делаем join'ы. Если есть вложенные полуструктурированные данные, то делаем их плоскими (explode). При этом сюда НЕ заводим бизнес-логику, а оставляем ее для следующего этапа. За этот и предыдущий этап отвечает, как правило, DE команда, которая поддерживает в том числе и стриминговую запись в bronze.
- Gold: Имплементим бизнес-логику и аггрегации для витрин. Чтобы они между командами не расходились, нужно согласовать схему данных, и назначение аггрегированных метрик между всеми стейкхолдорами в компании. Держим его максимально компактным и централизованным
Почему я перестал читать AdExchanger?
Как известно в адтехе все меняется быстро, и чтобы твой продукт, над которым ты работаешь не устарел уже к моменту релиза, нужно быть в курсе рынка. А для этого нужно выделять время на чтение актуальных новостей.
Но сейчас от избытка новостных ресурсов можно запросто словить FOMO (удачи пересмотреть весь AdExchanger, AdWeek, AdMonsters, Business Insider etc). А статьям, написаным адтех журналистами, как правило, не хватает остроты и персонального взгляда.
Поэтому, уже на протяжении года за новостями я иду в канал Ивана AdTech: персональное мнение и НеAdfox. Здесь несколько раз в неделю я читаю подборки из топ 3..5 новостей индустрии. Самое главное с авторскими заключениями и оценкой импакта той или иной фичи для всех стейкхолдеров: паблишеров, SSP/DSP, рекламодателей, пользователей.
Обсуждается все
- новые полезные и не очень AI фичи адтеха
- борьба паблишеров с монополией поисковиков на трафик
- скандалы, интриги, расследования среди walled gardens, M&A, и как на них влияет идущая рецессия
В общем, рекомендую обратить внимание. Хочется, чтобы такого контента в русскоязычном адтехе было больше!
Как известно в адтехе все меняется быстро, и чтобы твой продукт, над которым ты работаешь не устарел уже к моменту релиза, нужно быть в курсе рынка. А для этого нужно выделять время на чтение актуальных новостей.
Но сейчас от избытка новостных ресурсов можно запросто словить FOMO (удачи пересмотреть весь AdExchanger, AdWeek, AdMonsters, Business Insider etc). А статьям, написаным адтех журналистами, как правило, не хватает остроты и персонального взгляда.
Поэтому, уже на протяжении года за новостями я иду в канал Ивана AdTech: персональное мнение и НеAdfox. Здесь несколько раз в неделю я читаю подборки из топ 3..5 новостей индустрии. Самое главное с авторскими заключениями и оценкой импакта той или иной фичи для всех стейкхолдеров: паблишеров, SSP/DSP, рекламодателей, пользователей.
Обсуждается все
- новые полезные и не очень AI фичи адтеха
- борьба паблишеров с монополией поисковиков на трафик
- скандалы, интриги, расследования среди walled gardens, M&A, и как на них влияет идущая рецессия
В общем, рекомендую обратить внимание. Хочется, чтобы такого контента в русскоязычном адтехе было больше!
Telegram
Adtech: персональное мнение
Персональное мнение о новостях с рынка Adtech: всё новое и интересное что касается programmatic, openRTB, pDOOH, CTV и смежных технологий будут оседать здесь и иногда тщательно пережёвываться мною!
Подписывайтесь, будет немножно скучно, но в целом весело!
Подписывайтесь, будет немножно скучно, но в целом весело!
NVIDIA Triton Inference Server
Сегодня разбираем фреймворк NVIDIA Triton Inference Server для развёртывания ML моделей в прод. Допустим, у нас уже есть обученная модель. Нам нужно реализовать инференс, и стоит задача запустить как можно больше типов моделей в рамках одной платформы. Для этих целей подойдет Triton.
➡️ Чем хорош?
- Оптимизирован по памяти: Triton позволяет передавать данные между моделями без копирования. Он не грузит модели, которые не используются и использует shared memory, чтобы получать данные и отдавать ответы.
- Поддерживает широкий набор моделей: доступен для всех базовых фреймворков (PyTorch, TensorFlow, ONNX формат).
- Работает, как в батчевом, так и в конкурентном режиме на GPU
➡️ Как запускать?
Triton поставляется в виде готового docker-образа, где предустановлен весь необходимый функционал. Запускаем через docker-compose
Далее подгружаем модель. Для ее запуска на сервере нам нужны:
- чекпоинт модели
- информация о входном слое модели, его названии и размерностях, а также о выходных слоях
- конфиг модели
- репозиторий, организованный по определённой структуре
Первые два пункта вполне понятны: нам нужно обучить модель и, например, визуализировать её с помощью netron.app, чтобы узнать названия и размерности необходимых нод. Для запуска модели полезно кастовать в ONNX формат. Конфиг в качестве примера может выглядеть следующим образом:
Cтруктура хранилища моделей может быть следующей:
Когда модель подгружена, готовим данные к передаче на сервер. Для формирования входных и приёма выходных сообщений здесь реализованы такие классы, как InferInput и InferRequestedOutput. Для создания объектов классов нам нужно указать название входной и выходной ноды (исходя из конфигурации модели), а также размер и тип данных.
После создания объектов входа и выхода необходимо заполнить обьект Inputs данными:
InferInput принимает данные в виде numpy-массива, однако его элементами могут быть не только числа, но и строки, а также байтовое представление данных.
После подготовки данных отправляем их на сервер для инференса. Под клиент-серверное приложение лучше всего подходит ассинхронная отправка с использованием asyncio.
Для получения результата
Сегодня разбираем фреймворк NVIDIA Triton Inference Server для развёртывания ML моделей в прод. Допустим, у нас уже есть обученная модель. Нам нужно реализовать инференс, и стоит задача запустить как можно больше типов моделей в рамках одной платформы. Для этих целей подойдет Triton.
➡️ Чем хорош?
- Оптимизирован по памяти: Triton позволяет передавать данные между моделями без копирования. Он не грузит модели, которые не используются и использует shared memory, чтобы получать данные и отдавать ответы.
- Поддерживает широкий набор моделей: доступен для всех базовых фреймворков (PyTorch, TensorFlow, ONNX формат).
- Работает, как в батчевом, так и в конкурентном режиме на GPU
➡️ Как запускать?
Triton поставляется в виде готового docker-образа, где предустановлен весь необходимый функционал. Запускаем через docker-compose
version: "3.2"
services:
image: nvcr.io/nvidia/tritonserver:24.12-py3
command: tritonserver --model-repository=/models
ipc: "host"
pid: "host"
ports:
- "8000:8000"
- "8001:8001"
- "8002:8002"
shm_size: '1024mb'
volumes:
- ${PWD}/models_repository:/models
Далее подгружаем модель. Для ее запуска на сервере нам нужны:
- чекпоинт модели
- информация о входном слое модели, его названии и размерностях, а также о выходных слоях
- конфиг модели
- репозиторий, организованный по определённой структуре
Первые два пункта вполне понятны: нам нужно обучить модель и, например, визуализировать её с помощью netron.app, чтобы узнать названия и размерности необходимых нод. Для запуска модели полезно кастовать в ONNX формат. Конфиг в качестве примера может выглядеть следующим образом:
name: "densenet_onnx"
platform: "onnxruntime_onnx"
max_batch_size: 8
dynamic_batching {
### preferred_batch_size: 4 # только для TensorRT
max_queue_delay_microseconds: 200000
}
instance_group [
{
count: 3
kind: KIND_GPU
gpus: [0]
}
input [
{
name: "data_0"
data_type: TYPE_FP32
format: FORMAT_NCHW
dims: [ -1,3, 224, 224 ]}
}
]
output [
{
name: "fc6_1"
data_type: TYPE_FP32
dims: [ -1,1000 ]
}
]
Cтруктура хранилища моделей может быть следующей:
├─── ...
└───<model_repo>
└───<model_name>
├───config.pbtxt
└───<version>
└───model.py
Когда модель подгружена, готовим данные к передаче на сервер. Для формирования входных и приёма выходных сообщений здесь реализованы такие классы, как InferInput и InferRequestedOutput. Для создания объектов классов нам нужно указать название входной и выходной ноды (исходя из конфигурации модели), а также размер и тип данных.
input_node_name = "input:0"
output_node_name = "output:0"
shape = [1,3,224,224]
dtype = TYPE_FP32
inputs = [grpcclient.InferInput(input_node_name, shape, dtype)]
outputs = [grpcclient.InferRequestedOutput(output_node_name)]
После создания объектов входа и выхода необходимо заполнить обьект Inputs данными:
inputs[0].set_data_from_numpy(img.astype(np.float32))
InferInput принимает данные в виде numpy-массива, однако его элементами могут быть не только числа, но и строки, а также байтовое представление данных.
После подготовки данных отправляем их на сервер для инференса. Под клиент-серверное приложение лучше всего подходит ассинхронная отправка с использованием asyncio.
model_name = "densenet_onnx"
results = await triton_client.infer(
model_name=model_name,
inputs=inputs,
outputs=outputs
)
Для получения результата
outputs = results.as_numpy(output_node_name)
В перерывах между задачками на биддере я продолжаю изучать LLM. В более раннем посте я писал про RAG (Retrieval Augmented Generation). Сегодня рассмотрим, как дообучить LLM под задачу RAG.
Можно условно выделить три этапа:
- Pretraining. Стадия, где модель обучается на огромном количестве неразмеченных текстовых данных. Задача предобучения - это дать модели возможность изучить закономерности языка, и научиться предсказывать следующий токен в последовательности
- Supervised Fine-Tuning (SFT). После этапа претрейна модель дообучается на данных, с размеченными парами вопрос-ответ. Здесь цель - адаптировать модель под конкретную задачу
- Alignment. Здесь дообучаем модель под формат в запросе пользователя, и если надо этическим нормам. Существует целый зоопарк методов alignment'а, некоторые из них построены на методах RL (PPO, GRPO etc.), некоторые нет (Rejection Sampling, DPO и etc.). Каждый из них выдает разное качество, стабильность, сходимость и скорость
Также как и в классических ML задачах при дообучении LLM смотрим на распределение данных прода и трейна. Через продовую RAG-систему всегда проходит поток запросов в определённом соотношении тематик, стилей, длины, ошибок и других свойств, и точно также определенными свойствами обладают ожидаемые ответы в этом потоке. Стоит озаботиться тем, чтобы обучающая выборка имела несмещённое относительно реального потока распределение. Это часто справедливо для 1го и 2го этапов дообучения
Можно условно выделить три этапа:
- Pretraining. Стадия, где модель обучается на огромном количестве неразмеченных текстовых данных. Задача предобучения - это дать модели возможность изучить закономерности языка, и научиться предсказывать следующий токен в последовательности
- Supervised Fine-Tuning (SFT). После этапа претрейна модель дообучается на данных, с размеченными парами вопрос-ответ. Здесь цель - адаптировать модель под конкретную задачу
- Alignment. Здесь дообучаем модель под формат в запросе пользователя, и если надо этическим нормам. Существует целый зоопарк методов alignment'а, некоторые из них построены на методах RL (PPO, GRPO etc.), некоторые нет (Rejection Sampling, DPO и etc.). Каждый из них выдает разное качество, стабильность, сходимость и скорость
Также как и в классических ML задачах при дообучении LLM смотрим на распределение данных прода и трейна. Через продовую RAG-систему всегда проходит поток запросов в определённом соотношении тематик, стилей, длины, ошибок и других свойств, и точно также определенными свойствами обладают ожидаемые ответы в этом потоке. Стоит озаботиться тем, чтобы обучающая выборка имела несмещённое относительно реального потока распределение. Это часто справедливо для 1го и 2го этапов дообучения
Telegram
ML Advertising
RAG в LLM
Продолжаем тему языковых моделей. Первый пост был по различию базовых и инструктивных LLM. Сегодня рассмотрем понятие RAG.
RAG (Retrieval Augmented Generation) - это подход, который позволяет использовать Большие языковые модели (LLM) для ответов…
Продолжаем тему языковых моделей. Первый пост был по различию базовых и инструктивных LLM. Сегодня рассмотрем понятие RAG.
RAG (Retrieval Augmented Generation) - это подход, который позволяет использовать Большие языковые модели (LLM) для ответов…
Как там поживает игровой трафик? 👾
Я помню, были в детстве бесплатные треш-игры, сделанные под Adobe Flash, монетизирующиеся исключительно рекламой. С приходом iPhone и inApp покупок в AppStore в 2009, Flash канул в небытие, и настала пора мобильных free-to-play игр.
Сегодня браузерные Web-игры возвращаются.
Чем это вызвано?
- Flash был заменен на HTML5 стандарт
- Выход за пределы App Store и Google Play. Разработчики получают доступ к игрокам напрямую через аггрегаторы Poki и Itch.io, минуя Apple, Google
- Графические карты WebGPU и новый стандарт делают браузерки мощнее и приближают их к уровню консолей. Всякие топовые free-to-play игры вполне могут быть портированы.
- Рост рекламы и монетизации. Web-игры привлекают рекламодателей, а модели подписки и внутриигровых покупок становятся удобнее.
Если тренд продолжится, браузерные игры могут стать реальной альтернативой мобильным приложениям, а разработчики — более независимыми от экосистем Apple и Google.
Я помню, были в детстве бесплатные треш-игры, сделанные под Adobe Flash, монетизирующиеся исключительно рекламой. С приходом iPhone и inApp покупок в AppStore в 2009, Flash канул в небытие, и настала пора мобильных free-to-play игр.
Сегодня браузерные Web-игры возвращаются.
Чем это вызвано?
- Flash был заменен на HTML5 стандарт
- Выход за пределы App Store и Google Play. Разработчики получают доступ к игрокам напрямую через аггрегаторы Poki и Itch.io, минуя Apple, Google
- Графические карты WebGPU и новый стандарт делают браузерки мощнее и приближают их к уровню консолей. Всякие топовые free-to-play игры вполне могут быть портированы.
- Рост рекламы и монетизации. Web-игры привлекают рекламодателей, а модели подписки и внутриигровых покупок становятся удобнее.
Если тренд продолжится, браузерные игры могут стать реальной альтернативой мобильным приложениям, а разработчики — более независимыми от экосистем Apple и Google.
Naavik
Web Gaming Strikes Back
In the web gaming era of the 2000s, firms like Miniclip developed their own arcade-style games and published them on their eponymous platformCarson Taylor
ML Advertising pinned «Привет и добро пожаловать! Я Женя, Senior ML Engineer в Ad Tech. В этом канале буду делиться с вами полезной информацией и опытом в области Machine Learning, DE, MLOps в частности в программатик рекламе. Также будет много актуальных новостeй из мира Ad Tech…»
DB Index и партиционирование
Некоторое время работаю с датамартами на Postgres. В этой связи, решил собрать в кучу мысли по партиционированию данных в DB. Сошлюсь на тред в stackoverflow и постгресовскую документацию
Организация индексов
- Один многоколоночный индекс лучше, чем несколько одноколоночных, если фильтруем по нескольким колонкам
- Несколько одноколоночных индексов эффективнее, если часто фильтруем только по одной колонке
- Если данные временные, то партиционируем. Ставим фильтр по временной колонке + добавляем в индекс те, по которым чаще всего фильтруем. В итоге получаем многоколоночный индекс вида
Что по датамартам?
- Для датамартов делаем партиции по колонкам, по которым чаще всего фильтруем
- Таблицы с разной степенью агрегации: raw час, hourly день, daily месяц
- materialized view поверх них
- Redash или Superset делает запрос уже к materialized view
Какие альтернативы Постгресу?
TimescaleDB
Это расширение под постгрес для временных данных. Разбиваем данные на гипертаблицы, которые автоматически партиционируются по времени + можно выбрать опционально партиционирование по ключу. При этом так же Постгрес не скейлится горизонтально
Apache Druid
Колоночная OLAP DB. Ее уже можно скейлить горизонтально. Но не поддерживает join'ы. SQL синтаксис доступен через Druid SQL
Некоторое время работаю с датамартами на Postgres. В этой связи, решил собрать в кучу мысли по партиционированию данных в DB. Сошлюсь на тред в stackoverflow и постгресовскую документацию
Организация индексов
- Один многоколоночный индекс лучше, чем несколько одноколоночных, если фильтруем по нескольким колонкам
- Несколько одноколоночных индексов эффективнее, если часто фильтруем только по одной колонке
- Если данные временные, то партиционируем. Ставим фильтр по временной колонке + добавляем в индекс те, по которым чаще всего фильтруем. В итоге получаем многоколоночный индекс вида
(date, publisher, country, ...)
.Что по датамартам?
- Для датамартов делаем партиции по колонкам, по которым чаще всего фильтруем
- Таблицы с разной степенью агрегации: raw час, hourly день, daily месяц
- materialized view поверх них
- Redash или Superset делает запрос уже к materialized view
Какие альтернативы Постгресу?
TimescaleDB
Это расширение под постгрес для временных данных. Разбиваем данные на гипертаблицы, которые автоматически партиционируются по времени + можно выбрать опционально партиционирование по ключу. При этом так же Постгрес не скейлится горизонтально
Apache Druid
Колоночная OLAP DB. Ее уже можно скейлить горизонтально. Но не поддерживает join'ы. SQL синтаксис доступен через Druid SQL
Stack Overflow
Postgresql: multicolumn indexes vs single column index
I have a table that will grow by 10M of rows every year.
The table has 10 columns, call them c1, c2, c3, ..., c10.
I will use the WHERE clause, potentially on 8 of them.
To be more specific: every ...
The table has 10 columns, call them c1, c2, c3, ..., c10.
I will use the WHERE clause, potentially on 8 of them.
To be more specific: every ...