ML Advertising
1.21K subscribers
134 photos
13 videos
2 files
192 links
Пишу про AdTech, AI и разработку

Для связи: @evgenii_munin
Download Telegram
👍1
Floor Price Optimization in RTB Auctions

Хочу сделать разбор статьи по оптимизации floor прайса в RTB аукционах в Yahoo Ad Exchange.

1️⃣ Какая мотивация?
Floor price (или еще Reserved price) - минимальная цена, ниже которой ставки не принимаются. Это также минимальная цена, которую выплачивает биддер в случае победы. Выставляется паблишером и характеризует в какой-то степени ценность продаваемого инвентаря.

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

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

2️⃣ Что по статье?
Задача оптимизации ставится следующим образом:

Целевая функция 

eRev(floor | X) = rev(floor | X) + ADXRev * proba (bids < floor | X)


- т.е. берется выручка как функция от флора при условии фичей (девайс, user-agent, dsp)
- если на бид реквест все биды ниже флора, то Yahoo отправляют реквесты на Google Ad Exchange и через него продают инвентарь

постановка задачи: найти такой флор, чтобы максимзировать суммарную выручку floor * = argmax eRev(floor | X)
- rev(floor | X) представляется в виде интеграла
- rev(floor | X) = sum(k_i * sum(bid_i * mult(F(bid_i | floor, X))). Здесь суммарную выручку считают как интеграл от бида ДСП  умноженного на функциую распределения бида ДСП при условии флора. k_i = {0, 1} и нужен для того, если ДСП бидить или не бидит. Аналогичный интеграл приводится и для Google ADX

далее оценивают распределение F(bid_i | floor, X)  из исторических данных как распределение Weibull’а с параметрами scale, shape .
- чтобы нащупать кривую F рандомизируют флоры и логируют значения бидов. При этом отсекают 95% квантиль, чтобы убрать выбросы. Данные берут за 7 дней
- далее оценивают параметры  scale, shape для F. Здесь просто фитят это распределение на гистограмму, полученную с прошлого шага
- далее решают задачу максимизации rev(floor | X) , после того как узнали параметры распределения F. При этом считают флор отдельно для связки (placement, site, publisher, DSP)
- после этого пушат флор в SSP
🔥32👍2
Yahoo Ad Exchange- Optimizing Floors in First Price Auctions.pdf
1.3 MB
Также сама статья по оптимизации флора
👍2🔥1
Привет! Если вы интересуетесь внутренней кухней и инсайтами Avito.Tech, то рекомендую вам канал big_ledovsky.

Его ведет Александр Ледовский, Team Lead команды монетизации Avito.

У себя в постах я уже рассказывал о его выступлении на DataFest'е, например по дискуссии на тему рекламных аукционов с ребятами из Яндекс Директ и Ozon. Несмотря на то, что бизнесы маркетплейса и display-рекламы отличаются друг от друга, многие технические задачи и модели очень схожи, например,

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

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

Кроме технической части здесь вы сможете узнать о буднях DS команд в Avito.Tech, актуальные вакансии, а также полезные советы о том, как сохранять work-life баланс, будучи менеджером команды.

Го подписываться! 🔥
👍3🔥2
Ранее я рассказывал про метод Adversarial Validation. Он помогает проверять, приходятся ли фичи из train/ test из одного распределение. Эта задача также называется ковариационный сдвиг между фичами в выборках

Несмотря на то, что это очевидный недостаток в данных, мы можем попробовать использовать его в свою пользу
- С помощью классификатора Adversarial Validation на train/ test для каждого объекта трен. выборки возьмем его вероятность принадлежности к тесту P(y=1)
- Далее будем использовать полученную вероятность как вес сэмпла при обучении модели
- Модель при обучении будет обращать больше внимания на примеры, схожие с тестовой выборкой

Такой метод заходит на Каггле, где наша цель - это заточиться под приватный лидерборд с долей тестовой выборки. В индустриальных проектах с такими финтами нужно быть осторожней, поскольку появляется риск переобучения, но в целом метод докидывает в модель
🔥5👍2
Как работает SSP?

# Part2

Итак, в прошлом посте о работе SSP мы остановились на отправке RTB запросов на DSP после их предварительной фильтрации.

▶️ Auction Resolution
Допустим, мы получили ответ от некоторых DSP, и теперь можем разрешить аукцион и выбрать победителя. Внутренний аукцион, как правило, первой цены, значит победитель платит свою ставку.

На этом же этапе происходят следующие вещи
- SSP создает xml файл VAST, в котором прописываются ставка, ссылка на креатив, его формат, и tracking ивенты (это более актуально для видео, где трекаются inview, start, midpoint, complete)
- Если SSP закупает инвентарь у паблишера напрямую, и цель рекламной кампании забидевшего рекламодателя CPM, т.е. branding показа, то мы минуем Prebid аукцион. И в этот же момент триггериться биллинг ивент, и SSP платит паблишеру.
- Также проверяем креатив на наличие ошибок error-vast перед тем, как его отправить паблишеру (например, формат креатива не соответствует заявленному на слоте, или ломаная ссылка в VAST)

Ниже приведен пример, как может выглядеть VAST файл

<VAST version="3.0">
<Ad id="20001" sequence="1">
<InLine>
<AdSystem version="1.0">Criteo</AdSystem>
<AdTitle>Adidas Video Ad</AdTitle>
<Description>VAST 3.0 compliant video ad</Description>
<Advertiser>Adidas</Advertiser>
<Pricing model="CPM" currency="USD">25.00</Pricing>
<Impression><![CDATA[https://criteo.com/impression]]></Impression>
<Creatives>
<Creative sequence="1" AdID="20001">
<Linear>
<Duration>00:00:30</Duration>
<TrackingEvents>
<Tracking event="complete"><![CDATA[https://sample-ssp.com/tracking/complete]]></Tracking>
</TrackingEvents>
<MediaFiles>
<MediaFile delivery="progressive" type="video/mp4" bitrate="500" width="640" height="360" scalable="true" maintainAspectRatio="true">
<![CDATA[https://sample-ssp.com/mediafile.mp4]]>
</MediaFile>
</MediaFiles>
</Linear>
</Creative>
</Creatives>
</InLine>
</Ad>
</VAST>



▶️ Header Bidding SSP Bid Response
В случае, если SSP бидит на Prebid, то она прокидывает полученную ранее ставку DSP с модификаторами и вычтенной маржой. Формулу ставки SSP можно представить в следующем виде


ssp_price = clearing_price * (1 - margin) * P(impression|auction_resolved) * bid_modifier


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


▶️ SSP Header Bidding Win
Если SSP победила в HB аукционе, то триггерится ивент ssp_hb_win. Prebid отсылает уведомление о победе, а SSP отправляет ему tag VAST с креативом. В этот момент SSP оплачивает инвентарь паблишера в размере своей ставки.


▶️ Impression
Собственно, сам ивент показа. Он триггерится в момент, когда пользователь доскролил до Ad слота и ему виден пиксель, заранее размещенный на слот. На показе DSP платит SSP clearing_price. Из этой цены аккумулируется средний CPM, который можно видеть в Yandex Direct или Google DV360 при отслеживании кампании

Также на моменте показа тригеррятся ML модели предсказывающие полноту показа (для видео) и клики для performance кампаний. В зависимости от цели кампании ее биллинг ивенты смещаются соответсвенно на complete или click
- Viewability: P(inview | impression)
- Compeltion: P(complete | impression)
- CTR: P(click | impression)

#adtech
🔥4👍2
▶️ Разбираем форматы рекламы на YouTube

In-Stream рекламные слоты YouTub'а, это отдельная эко-система форматов, которая почти всегда закупается через DV360 DSP Гугла. Сегодня разберем эти форматы, их назначение и эффективность.

Среди нашей аудитории есть ребята, работающие в AdOps, поэтому этот материал будет интересен для вас.

Заодно поэкспериментирую с новыми постами

Форматы In-Stream рекламы
- Trueview Skippable
- Trueview Unskippable
- Bumper
- Short
- In-Feed

Каждый адаптирован под разные KPI цели кампании CPM (показы + охват) или CPCV (полнота досмотров). Также можно закупать пакеты форматов


Video Reach Campaign = bumper + skippable + shorts + in-feed
Video Views Campaign = skippable + shorts + in-feed


На skippable формате удобно применять модификаторы ставки, которая выставляется в DV360, чтобы рулить скоростью расхода суточного бюджета кампании. На других форматах, заточенных больше на показы модификаторы также работают, но не так эффективно.
👍2🔥2
Немного инфографики. Подкапотное описание команд Docker для образов и контейнеров вдобавок к шпаргалке.

Сохраняем к себе!
👍5
ONNXRuntime для ускорения ML моделей ⚡️

Сегодня речь пойдет про формат ONNX. Он помогает портировать ML модели в стандартном формате на другие языки, например на JVM. Для запуска моделей в этом формате используется фреймворк ONNXRuntime. Кроме того ONNXRuntime позволяет ускорить инференс. Об этом мы сегодня и поговорим. В качестве примера возьмем модель на PyTorch и сравним скорость инференса до и после конвертация в ONNX.

Как работает ускорение?

В PyTorch обработка данных ленивая, т.е. команды сначала добавляются в очередь и потом из нее выполняются. Для выполнения всех запланированных вычислений нужен torch.cuda.synchronize(). Запустим torch модель на входном массиве (1,3,224,224) , запишем время инференса и оценим FPS. Также удалим, из результатов первые 20 запусков, которые как правило уходят на прогрев.


n_steps = 200
n_warmup_steps = 20
times = []
data = torch.ones((1,3,224,224), device="cuda")

torch.cuda.synchronize()

for step in range(nsteps):
start_inference = time.time()
result = model(data)
torch.cuda.synchronize()
end_inference = time.time()
times.append(end_inference - start_inference)
times = times[n_warmup_steps:]
print(np.mean(times), np.std(times))


PyTorch выдает 400 FPS. Этот результат получился в следствие того, что граф модели PyTorch - динамический, что ограничивает его скорость инференса. Теперь скастуем из динамического графа torch'а статический граф и запишем в формат ONNX


torch.onnx.export(
model,
torch.randn(1, 224, 224).to(device),
'mobilenet_v2.onnx',
input_names = ['input'],
output_names = ['output'],
)


Для того, чтобы подгрузить ONNX модель, нам нужно создать InferenceSession. Далее читаем модель и запускаем инференс на ONNXRuntime. Также засечем время инференса, чтобы проверить ускорение.


import onnxruntime as ort
import numpy as np

providers = [('CUDAExecutionProvider', {'device_id': 0})]
ort_sess = ort.InferenceSession("mobilenet_v2.onnx", providers=providers)

n_steps = 200
n_warmup_steps = 20
times = []
image_as_numpy = np.ones((1,3,224,224))

for step in range(n_steps):
start_inference = time.time()
outputs = ort_sess.run(None, {'input': image_as_numpy})
end_inference = time.time()
times.append(end_inference - start_inference)

times = times[n_warmup_steps:]
print(np.mean(times), np.std(times))


ONNXRuntime выдает 630 FPS, т.е. мы получаем прирост скорости инференса в полтора раза. Здесь запуск проводился на CUDA, но ONNX также поддерживает и запуски на CPU
👍4🔥2
Мы собрали папку тг каналов про AdTech

Что вы здесь найдете
- Авторские мнения, дайджесты новостей: Сookie, Connected TV, Direct Sold контракты в обход SSP и многое другое
- Инженерная часть: как технически устроены SSP/DSP, как работает Prebid, как применять ML модели в программатике
- Монетизация: какими метриками измерять, как влиять на ставки в Header Bidding, настройки форматов в рекламной сети Яндекса
- Инфографика, разбор отчетности крупных игроков, и что происходит в мире AI-based стартапов в рекламе и ecomm
- Sales в маркетинге и рекламе, как обрабатывать лиды и закрывать сделки

В общем, подборка закрывает примерно весь спектр задач по рекламе. Каждый, интересующийся темой, сможет найти для себя полезное
3🔥2👍1
Ранее я уже писал про функционал Optun'ы, что ее можно использовать, как для оптимизации параметров модели, так и для препроцессинга

Сегодня обсудим прунинг экспериментов с Optuna. Прунинг здесь - это прерывание эксперимента, по какой-либо метрике, когда она уже вышла на плато и перестала значимо менятсья. Т.е. если мы поставили CatBoost модель обучаться на 500 шагов и видим, что после 50 шагов лучший скор уже получен и его не превзойти, то логично обучение остановить и тестировать следующую итерацию параметров

Какие Pruner'ы есть в Оптуне?
- MedianPruner: останавливает испытание, если скор ниже медианы скоров с предыдущих шагов
- PercentilePruner: останавливает испытание, если скор находится в нижних персентилях с предыдущих шагов
- SuccessiveHalvingPruner: прунер на основе многоруких бандитов. Помогает не только во время остановить обучение, но и выбрать лучшую комбинацию гиперпараметров
- ThresholdPruner: останавливает испытание по пороговому значению

Лучший вариант для классических моделей SuccessiveHalvingPruner. Здесь пример, как его можно реализовать

X, y = load_iris(return_X_y=True)
X_train, X_valid, y_train, y_valid = train_test_split(X, y)
classes = np.unique(y)


def objective(trial):
alpha = trial.suggest_float("alpha", 0.0, 1.0)
clf = SGDClassifier(alpha=alpha)
n_train_iter = 100

for step in range(n_train_iter):
clf.partial_fit(X_train, y_train, classes=classes)

intermediate_value = clf.score(X_valid, y_valid)
trial.report(intermediate_value, step)

if trial.should_prune():
raise optuna.TrialPruned()

return clf.score(X_valid, y_valid)


study = optuna.create_study(
direction="maximize", pruner=optuna.pruners.SuccessiveHalvingPruner()
)
study.optimize(objective, n_trials=20)
👍8🔥31
Сегодня у нас минутка новостей

Программатик платформа Outbrain покупает Teads за $1B. Как заявлено в официальном пресс релизе это поможет создать единую площадку, покрывающую все рекламные форматы, основанную на ИИ и ведущих решениях для видео и брендинга

▶️ Что я про это думаю?
Мотивация, зачем для Outbrain нужен Teads, вполне понятна
- Основная выручка Outbrain - это perf кампании на клик. Они монетизируются как правило на низкокачественных Made For Advertsing сайтах, которые скорее заточены под арбитраж. Поскольку Teads обладает пулом премиум паблишеров, то интеграция с ними поможет дистанцировать Outbrain от MFA в глазах рекламодателей
- Outbrain нужен доступ к более дорогому CTV инвентарю. Этот инвентарь можно покупать для CPM кампаний и таким образом повышать охват. Также настройка интеграций Connected TV технически сложна, и вместо того, чтобы строить ее с нуля, проще купить готовую платформу

В Teads изменения также напрашивались уже давно.
- Этому предшествовала попытка заявка на IPO для выхода на биржу в 2021, которая была отозвана группой Altice, в которую входит Teads
- Манипуляции с делением и закрытием направлений, постепенный сдвиг приоритета на Teads Ad Manager (TAM, ad server, аналог Google Ad Manager) и отказ от новых Deal ID контрактов с внешними DSP

▶️ Как покупка повлияет на команды и сервисы?
Поскольку данная покупка - это не столько поглощение крупной компанией более мелкой, а скорее слияние двух компаний одинакового размера, то предстоит сложный процесс интеграции команд друг с другом. Если на уровне директоров все уже решено, то многим менеджерам среднего звена и лидам команд придется потесниться. Это может вызвать терки и напряженности на первых порах

В плане команд, которых изменения коснуться в первую очередь, скорее всего, это будут Performance и Teads Ad Manager. Поскольку Outbrain специализируется на perf трафике (закупкам по кликам CPC), то они будут навязывать в первую очередь свои практики

Касательно Teads SSP, где мне довелось поработать, то считаю, что изменения организации коснуться платформы в меньшей степени по двум причинам.
- Во-первых, это критическая инфраструктура, без которой просто нарушится цепочка покупки инвентаря и поставки рекламы
- Во-вторых, Outbrain нацелена в первую очередь на CTV инвентарь закупаемый по CPM для branding кампаний. А это именно то, что реализовано в Teads SSP с интеграциями с Publica, FreeWheel и Spring-Serve

Как будут развиваться события, будем мониторить и держать руку на пульсе
👍5
Ранее я уже рассказывал о формате ONNX и о том, как его использовать для ускорения моделей на предсказании. Сегодня речь пойдет про портирование питонячих моделей на Java. Из своей практики Real Time Bidding платформы (SSP и DSP), как правило, реализованы на языках под JVM и стандартные ML фреймворки (кроме разве что Apache Spark'а) становятся недоступными. Выхода здесь два

- либо пробовать делать рукописные модели сразу на Java
- прибегать к конвертации в форматы совместимые с JVM

О том, как портировать Sklearn модель на Java, и пойдет речь в статье, которую я подготовил

Буду рад вашим реакциям и фидбеку 🔥
🔥8👍1