pagerank基本原理numpy演示
Random Walks
import numpy as np
np.set_printoptions(precision=6)
np.set_printoptions(suppress=True)
print("参考资料")
print("http://pi.math.cornell.edu/~mec/Winter2009/RalucaRemus/Lecture3/lecture3.html")
print("trans_mat 是转移概率矩阵")
trans_mat = np.array([[0, 0, 1, 1 / 2], [1 / 3, 0, 0, 0], [1 / 3, 1 / 2, 0, 1 / 2], [1 / 3, 1 / 2, 0, 0]])
print("ini_vec 是用户初始位于某一点的概率, 在没有搜索引擎的情况下, 可以认为用户位于各点的概率是相同的")
ini_vec = np.array([[1 / 4], [1 / 4], [1 / 4], [1 / 4]])
print("假设经过4次转移之后, 用户位于各个点的概率趋于稳定 0.387 0.129 0.291 0.194.")
print("这个概率可以理解成大量用户随机行走最终落到某个点的概率")
print(trans_mat @ trans_mat @ trans_mat @ trans_mat @ ini_vec)
print("换一个思路, 对角化 diag = eig_vecs_r * trans_mat * eig_vecs => trans_mat =eig_vecs * diag * eig_vecs_r")
eig_vals, eig_vecs = np.linalg.eig(trans_mat)
diag = np.diag(eig_vals)
eig_vecs_r = np.linalg.inv(eig_vecs)
print(diag)
print(eig_vecs)
print(eig_vecs_r)
print("多次转移等同于 trans_mat ^ 4 = eig_vecs * diag^4 * eig_vecs_r")
print(eig_vecs @ diag @ diag @ diag @ diag @ eig_vecs_r @ ini_vec)
print("根据Perron–Frobenius定理, diag 的特征值除了第一项, 其他都小于0")
print("所以直接将 D^4 非1项归零")
diag_ = np.array([[1, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]])
print("近似计算的稳定规律如下")
print(eig_vecs @ diag_ @ eig_vecs_r @ ini_vec)
print("进一步简化: Q的第一列(即特征值1对应的特征向量) 点乘 Q_ 的第一行 点乘 I, 后两项结合后为常量")
print(eig_vecs[:, 0])
print((eig_vecs_r[0, :]))
print(eig_vecs[:, 0].reshape((-1, 1)) * eig_vecs_r[0, :] @ ini_vec)
print("再考虑一种情形, 用户每次查询结束, 都有0.8的概率进行下一次查询. 否则随机跳转到别的节点, 也就是结束了本轮查询")
cont_pr = 0.8
one_col = np.array([[1], [1], [1], [1]])
col_size = one_col.size
def lazy_random_walk(trans_mat_, ini_vec_):
return cont_pr * trans_mat_ @ ini_vec_ + (1 - cont_pr) * one_col / col_size
cnt = 0
N = 4
ini_vec1 = ini_vec
while cnt < N:
ini_vec1 = lazy_random_walk(trans_mat, ini_vec1)
cnt = cnt + 1
print(ini_vec1)
print("最终情形, 用户本身开始查询是就有一定的偏好, 比如只关注某些特定的作者, 这种情况下初始概率 I 的各项不相等")
print("比如下面的例子, 用户开始查询时只会从节点1和节点2开始")
ini_vec2 = np.array([[1 / 3], [2 / 3], [0], [0]])
cnt = 0
while cnt < N:
ini_vec2 = lazy_random_walk(trans_mat, ini_vec2)
cnt = cnt + 1
print(ini_vec2)
print("end")
print("计算各个点的概率和, 正好为1")
print(ini_vec.sum(axis=0))
print(ini_vec1.sum(axis=0))
print(ini_vec2.sum(axis=0))
猜想
2和3计算方式迭代足够多次,似乎会收敛于同一个结果