PageRank на пальцах

Важность веб-страницы — это собственный вектор матрицы ссылок: степенной метод находит его, и так рождается ранжирование Google.

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

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

Модель случайного сёрфера

Представим пользователя, который кликает по случайным ссылкам. С вероятностью $d$ (обычно $0{,}85$) он переходит по одной из ссылок текущей страницы, а с вероятностью $1 - d$ — перепрыгивает на случайную страницу (это спасает от «тупиков» и зацикливаний). PageRank страницы — это доля времени, которое сёрфер на ней проводит в долгой перспективе. Вектор этих долей — собственный вектор матрицы переходов с собственным значением 1, и его ищут итерациями.

def pagerank(links, d=0.85, iters=50):
    n = len(links)
    out = [sum(row) for row in links]      # сколько ссылок выходит
    rank = [1 / n] * n                     # равный старт
    for _ in range(iters):
        new = [(1 - d) / n] * n
        for i in range(n):
            for j in range(n):
                if links[i][j] and out[i]:
                    new[j] += d * rank[i] / out[i]
        rank = new
    return rank

# 0->1, 0->2, 1->2, 2->0
L = [[0, 1, 1], [0, 0, 1], [1, 0, 0]]
r = pagerank(L)
print("ранги:", [round(x, 4) for x in r])
print("сумма:", round(sum(r), 4))

Вывод:

ранги: [0.3878, 0.2148, 0.3974]
сумма: 1.0

Кто победил

Страница 2 получила наибольший ранг ($0{,}3974$): на неё ссылаются и страница 0, и страница 1. Страница 0 близко позади, потому что на неё ссылается важная страница 2. А страница 1 в аутсайдерах — на неё ведёт лишь одна ссылка. Ранги суммируются в единицу — это вероятности, и алгоритм сошёлся к собственному вектору матрицы переходов.

Как работает под капотом

Каждая итерация распределяет текущий вес каждой страницы поровну между страницами, на которые она ссылается (делим на out[i] — число исходящих ссылок), домножая на коэффициент затухания $d$. Слагаемое $(1 - d)/n$ — это вклад «телепортации» случайного сёрфера, оно же гарантирует, что процесс сходится к единственному решению независимо от старта. По сути pagerank — это степенной метод для матрицы переходов: повторное применение матрицы притягивает вектор к доминирующему собственному направлению с $\lambda = 1$. Для миллиардов страниц матрица разреженная, и именно такой итерационный подход делает задачу вычислимой.

Частые ошибки

  • Забывать делить на число исходящих ссылок. Страница раздаёт свой вес поровну, а не целиком каждой цели.
  • Опускать коэффициент затухания $d$. Без телепортации «висячие узлы» и циклы ломают сходимость.
  • Думать, что PageRank считает просто число входящих ссылок. Он учитывает важность ссылающихся — отсюда рекурсия и собственный вектор.

Итог

  • PageRank — доминирующий собственный вектор матрицы ссылок (переходов).
  • Вес страницы зависит от важности ссылающихся на неё страниц — рекурсивно.
  • Коэффициент затухания $d$ моделирует случайные прыжки и обеспечивает сходимость.
  • Алгоритм — это степенной метод; ранги суммируются в 1 как вероятности.
Проверьте себя
1. Чем математически является вектор PageRank?
Aопределителем матрицы ссылок
Bдоминирующим собственным вектором матрицы переходов
Cсуммой строк
Dобратной матрицей
2. Почему страница раздаёт свой вес, деля на число исходящих ссылок?
Aдля красоты
Bпотому что вес распределяется поровну между всеми целями ссылок
Cчтобы увеличить ранг
Dэто ошибка алгоритма
3. Зачем нужен коэффициент затухания d (телепортация с вероятностью 1-d)?
Aчтобы ускорить интернет
Bчтобы обеспечить сходимость и обойти тупики/циклы в графе ссылок
Cчтобы обнулить ранги
Dон не нужен