Главная » Статьи » Тонкое искусство прогностической предварительной выборки

Тонкое искусство прогностической предварительной выборки

Тонкое искусство прогностической предварительной выборки

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

Подобно тому, как официант предварительно оптимизировал ваш выбор соуса, современные браузеры имеют различные методы, такие как подсказки ресурсов, для повышения производительности, угадывая, куда пользователь перейдет дальше. Такие подсказки ресурсов, как prefetch позволяют заблаговременно получать критические ресурсы и экономить ценное время при (следующем) рендеринге. Тем не менее, решение о том, что предварительно извлекать, по своей природе является спекулятивным. Мы, как разработчики, делаем предположения о пути пользователя и маршрутах, по которым он пойдет. Как мы наблюдали в случае инцидента с приправой ранее, спекулятивная предварительная выборка может быть расточительной, поскольку извлеченные ресурсы никогда не будут использоваться. Здесь может пригодиться прогностическая предварительная выборка. Объединяя достижения в области машинного обучения с данными аналитики, мы можем значительно повысить эффективность наших выборок.

Параметры предварительной выборки

Прежде чем мы углубимся в то, как мы можем реализовать прогностические предварительные выборки в приложениях, давайте уделим немного времени, чтобы изучить наши варианты, когда дело доходит до предварительной выборки. Существует три основных типа предварительной выборки, доступных в большинстве современных браузеров: предварительная выборка ссылок, предварительная выборка DNS и предварительный рендеринг. Из этих трех, предварительная выборка ссылок является самой популярной.

Предварительная выборка DNS

Предварительная выборка DNS — это механизм, который позволяет браузеру выполнять поиск DNS для будущих запросов страниц в фоновом режиме, пока пользователь просматривает страницу. Обычно, когда пользователь переходит на другую страницу, браузер должен выполнить поиск DNS и преобразовать имя домена в IP-адрес, прежде чем устанавливать соединение для будущей передачи данных. Этот процесс, конечно, требует времени. Хотя DNS-запросы в целом невелики с точки зрения использования ресурсов пропускной способности, задержка может быть довольно высокой, особенно в мобильных сетях. Более того, более сложные страницы требуют большего количества запросов DNS и могут замедлить общую скорость загрузки страниц. Предварительная выборка DNS уменьшает задержку, поскольку разрешение DNS выполняется еще до того, как пользователь переходит на следующую страницу.

Предварительная выборка ссылок

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

Предварительный рендеринг

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

Переход от умозрительного к прогностическому

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

Прогнозирование будущих кликов

В случае упреждающей предварительной выборки мы определяем наилучший возможный результат на основе данных, которые собрали для отслеживания навигации пользователя. Допустим, у нас есть информация о просмотре страницы пользователя до того, как он перейдет на текущую страницу. Основываясь на этих данных, мы можем получить общее представление о потоках пользователей и рассчитать вероятность того, что они перейдут на страницу с учетом их текущей страницы. Из приведенных ниже примеров данных мы знаем, что 2 из 4 пользователей перешли по маршруту /menu с маршрута /about, поэтому вероятность того, что любой пользователь перейдет к маршруту /menu с маршрута /about, составляет около 50%. Точно так же мы можем рассчитать, что 25% пользователей будут переходить с /menu на /contact, потому что 1 из 4 пользователей сделал именно это.

Прогностический irl

Теория этого типа работает, но давайте посмотрим, работает ли это на практике. Данные по отслеживанию навигации пользователя могут быть легко получены из Google Analytics (GA). Вы можете просмотреть эти данные в панели мониторинга GA, перейдя «Поведение» > «Содержание сайта» > «Все страницы» и кликнув вкладку «Обзор переходов». Для ясности рассмотрим необработанные данные, полученные из Google Analytics API. Мы сосредоточимся в первую очередь на части строк данных (см. ниже). Атрибут dimensions является массивом, который показывает путь к предыдущей странице, и к следующей странице соответственно. Атрибут values метрики представляет собой массив с просмотрами страниц и выходами соответственно.

{ "columnHeader": { ... }, "data": { "rows": [ ... { "dimensions": [ "/", "/about" ], //previous page, next page "metrics": [ { "values": [ "1", "0" ] } // pageviews, exits ] }, { "dimensions": [ "/", "/posts/" ], //previous page, next page "metrics": [ { "values": [ "4", "0" ] } // pageviews, exits ] }, { "dimensions": [ "/", "/contact/" ], //previous page, next page "metrics": [ { "values": [ "1", "0" ] } // pageviews, exits ] }, ... ], ... }
}

Обратите внимание, что в нашем примере приложения есть 3 возможных пути, по которым пользователи перешли с /. Давайте сгруппируем это так, чтобы было немного понятнее. Если вам интересно, как я это сделал, вы можете посмотреть этот фрагмент кода, это во многом вдохновлено тем, как GuessJS обрабатывает GA.

{ "/": { "pagePath": "/", "pageviews": 6, // exits + nextPageviews "exits": 0, "nextPageviews": 6, //all pageviews from next pages array "nextExits": 0, "nextPages": [ { "pagePath": "/posts/", "pageviews": 4 }, { "pagePath": "/about/", "pageviews": 1 }, { "pagePath": "/contact/", "pageviews": 1 } ], "percentExits": 0, "topNextPageProbability": 0.6666666666666666 } ...
}

Из наших агрегированных данных мы видим, что 4 пользователя перешли к /posts, 1 пользователь перешел к /about и 1 пользователь перешел к /contact. Исходя из этого, мы можем вычислить, что из в общей сложности 6 просмотров страниц, 4 перешли с / к /postsс, что является наибольшим количеством просмотров из всех вторичных просмотров. Таким образом, вероятность перехода пользователя к /posts составляет 4/6 или ~0,6666.

Вычисление вероятности — это хорошо, но реальная техника оптимизации производительности заключается в использовании этой метрики для реализации следующих выборок страниц. Для этого мы будем реализовать скрипт сборки в 11ty.js, который является замечательным, высокопроизводительным генератором статических сайтов, созданным несравненным Заком Лезерманом. Если вы никогда не работали с 11ty, не волнуйтесь, документация и стартовые проекты помогут вам.

Ввод данных в 11ty

Для начала мы создадим файл данных, доступный глобально в 11ty. В 11ty глобальные файлы данных обычно хранятся в каталоге _data. Таким образом, ваша файловая директория будет выглядеть примерно так:

├── .eleventy.js
├── package.json
├── src
│ ├── _data
│ │ ├── prefetchLinks.js // file we're creating
│ │ └── metadata.json
│ ├── _includes │ └── ...

Мы создадим файл с именем prefetchLinks.js, который будет вызывать GA API, объединять данные и возвращать массив объектов, содержащих все страницы, наиболее вероятную следующую страницу, связанную с ней, и процент вероятности этого конкретного действия. Вывод этой функции должен выглядеть примерно так:

{ "pagePath": "/about/", "nextPagePath": "/contact/", "nextPageCertainty": 0.5 },
{ "pagePath": "/menu/", "nextPagePath": "/contact/", "nextPageCertainty": 1 },
...

Пользовательские данные, извлекаемые из внешнего API, доступны через глобальную сеть 11ty module.exports. Итак, начнем с создания функции, которая пытается вызвать GA API. Чтобы получить доступ к API Google, мы сначала должны подтвердить подлинность нашего запроса. Для этой демонстрации мы будем использовать служебные ключи учетной записи в формате PEM. Помните, что это закрытый ключ, поэтому не добавляйте его в систему контроля версий! Для получения дополнительной информации о настройке этой части смотрите примечания по аутентификации в README демонстрационного проекта.

const {google} = require('googleapis')
const fs = require('fs')
const path = require('path') const queryParams = {
...
} const aggregatePages = async (pages) => { ... return predictions
} const authClient = new google.auth.JWT({ email: process.env.SERVICE_ACCOUNT_EMAIL, key: fs.readFileSync(path.join(__dirname, "../../key.pem"), 'utf8'), scopes: ['https://www.googleapis.com/auth/analytics.readonly']
}) module.exports = async function() { try { await authClient.authorize() const analytics = google.analyticsreporting({ version: 'v4', auth: authClient }) const response = await analytics.reports.batchGet(queryParams) let [report] = response.data.reports const predictions = await aggregatePages(report); return predictions } catch (err) { console.log("err") return err }
}

Теперь, когда часть аутентификации подключена, мы можем выполнить вызов к GA, чтобы получить необработанные аналитические данные, вызвав analytics.reports.batchGet и передав некоторые параметры запроса, такие как даты начала и окончания. После этого мы можем передать эти необработанные данные в функцию-агрегатор, которая агрегирует данные и выдает нам массив прогнозов.

Изменение разметки в 11ty

Благодаря нашим прогнозам, доступным в глобальных данных, теперь мы можем обновить файлы разметки соответствующими тегами предварительной выборки. Чтобы сделать это, нам нужно будет создать некую шаблонную логику, которая добавляет ссылку предварительной выборки в зависимости от того, высока ли вероятность того, что страница будет посещена следующей. 11ty использует по умолчанию nunjucks, поэтому мы добавим немного магии nunjucks в наш базовый шаблон nunjucks.

<!doctype html>
<html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>{{ renderData.title or title or metadata.title }}</title> ... {% for entry in prefetchLinks %} {# {% if entry.pagePath.replace("-", "") == page.url %} #} <link rel="prefetch" href="{{ entry | prefetchNextURL }}"> {# {% endif %} #} {%- endfor -%} </head>

В нашей частичной базе, расположенной по адресу src/_includes/layouts, мы перебираем доступный массив prefetchLinks. Поскольку данные, определенные в папке _data, доступны глобально в 11ty, мы можем просто использовать данные в шаблоне. Обратите внимание, что имя переменной должно совпадать с именем файла, prefetchLinks в нашем случае. Внутри итератора мы проверим, соответствует ли текущий URL какому-либо из указанных URL из аналитики, и если это так, добавим ссылку предварительной выборки. Для того, чтобы это стало полностью работоспособным, нам нужно добавить еще один шаг в сборку 11ty, использовать ссылку на следующую страницу, которая нам нужна. Помните, что элемент данных в настоящее время является объектом:

{ "pagePath": "/about/", "nextPagePath": "/contact/", "nextPageCertainty": 0.5 }

Чтобы получить ссылки, мы специально будем использовать фильтры, через конфигурации 11ty API. Это также позволяет нам предварительно выбирать ссылку, если она превышает определенный порог. Таким образом, принимая пороговое значение 0,5, ссылки предварительно извлекаются только, когда мы на 50% уверены, что пользователь туда перейдет.

var THRESHOLD = 0.5;
eleventyConfig.addFilter('prefetchNextURL', (entry, url) => { // check threshold // if (entry.nextPageCertainty > THRESHOLD) { return entry.nextPagePath }
})

Благодаря этому у нас теперь есть рабочий сайт, который использует интеллектуальную предварительную выборку! Тада!

Используйте с осторожностью

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

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

Если вы хотите больше узнать об этой концепцию, посмотрите GuessJS, проект, над которым работали Минко Гечев и команда Google, а также полный доклад, который я сделал на эту тему на Performance.now().

Автор: Divya

Источник: https://calendar.perfplanet.com

Редакция: Команда webformyself.