共计 2707 个字符,预计需要花费 7 分钟才能阅读完成。
原文链接:http://tecdat.cn/?p=6054
这篇文章是如何使用几种不同的矩阵分解算法计算相关艺术家。代码用 Python 编写,以交互方式可视化结果。
加载数据
这可以使用 Pandas 加载到稀疏矩阵中:
# read in triples of user/artist/playcount from the input datasetdata = pandas.read_table("usersha1-artmbid-artname-plays.tsv",
usecols=[0, 2, 3],
names=['user', 'artist', 'plays'])# map each artist and user to a unique numeric valuedata['user'] = data['user'].astype("category")data['artist'] = data['artist'].astype("category")# create a sparse matrix of all the artist/user/play triplesplays = coo_matrix((data['plays'].astype(float),
(data['artist'].cat.codes,
data['user'].cat.codes)))
这里返回的矩阵有 300,000 名艺术家和 360,000 名用户,总共有大约 1700 万条目。每个条目都是用户播放艺术家的次数,其中的数据是从 2008 年的 Last.fm API 收集的。
矩阵分解
通常用于此问题的一种技术是将用户 – 艺术家 – 戏剧的矩阵投影到低等级近似中,然后计算该空间中的距离。
我们的想法是采用原始的播放计数矩阵,然后将其减少到两个小得多的矩阵,这些矩阵在乘以时接近原始矩阵:
Artist/User/Play CountsArtist FactorsUser Factors=×
代替将每个艺术家表示为所有 360,000 个可能用户的游戏计数的稀疏向量,在对矩阵进行因式分解之后,每个艺术家将由 50 维密集向量表示。
通过减少这样的数据的维数,我们实际上将输入矩阵压缩为两个小得多的矩阵。
潜在语义分析
出于本文的目的,我们只需要知道 SVD 生成输入矩阵的低秩近似。
像这样使用 SVD 称为潜在语义分析(LSA)。所有真正涉及的是在这个分解空间中通过余弦距离获得最相关的艺术家:
class TopRelated(object): def __init__(self, artist_factors): # fully normalize artist_factors, so can compare with only the dot product norms = numpy.linalg.norm(artist_factors, axis=-1) self.factors = artist_factors / norms[:, numpy.newaxis] def get_related(self, artistid, N=10): scores = self.factors.dot(self.factors[artistid]) best = numpy.argpartition(scores, -N)[-N:] return sorted(zip(best, scores[best]), key=lambda x: -x[1])
潜在语义分析之所以得名,是因为在对矩阵进行分解之后,可以输入数据中潜在的隐藏结构 – 这可以被认为是揭示输入数据的语义。
LSA
类似于 LSA 的 ’Arcade Fire’:
虽然 LSA 成功地概括了我们数据的某些方面,但这里的结果并不是那么好。
隐含的交替最小二乘法
已发现这些模型在推荐项目时效果很好,并且可以很容易地重复用于计算相关艺术家。
推荐系统中使用的许多 MF 模型都采用了明确的数据,用户使用类似 5 星级评定标准评估了他们喜欢和不喜欢的内容。
第一个挑战是有效地进行这种因式分解:通过将未知数视为负数,天真的实现将查看输入矩阵中的每个条目。由于此处的维度大约为 360K 乘 300K – 总共有超过 1000 亿条目要考虑,而只有 1700 万非零条目。
第二个问题是我们不能确定没有听艺术家的用户实际上意味着他们不喜欢它。可能还有其他原因导致艺术家没有被收听,特别是考虑到我们在数据集中每个用户只有最多 50 位艺术家。
使用二元偏好的不同置信水平来学习分解矩阵表示:看不见的项目被视为负面且置信度低,其中当前项目被视为正面更高的信心。
那么目标是通过最小化平方误差损失函数的置信加权和来学习用户因子 X u 和艺术家因子 Y i:
def alternating_least_squares(Cui, factors, regularization, iterations=20): users, items = Cui.shape
X = np.random.rand(users, factors) * 0.01 Y = np.random.rand(items, factors) * 0.01 Ciu = Cui.T.tocsr() for iteration in range(iterations): least_squares(Cui, X, Y, regularization) least_squares(Ciu, Y, X, regularization) return X, Ydef least_squares(Cui, X, Y, regularization): users, factors = X.shape
YtY = Y.T.dot(Y) for u in range(users): # accumulate YtCuY + regularization * I in A A = YtY + regularization * np.eye(factors) # accumulate YtCuPu in b b = np.zeros(factors) for i, confidence in nonzeros(Cui, u): factor = Y[i] A += (confidence - 1) * np.outer(factor, factor) b += confidence * factor
# Xu = (YtCuY + regularization * I)^-1 (YtCuPu) X[u] = np.linalg.solve(A, b)
为了调用它,我使用与 LSA 中使用的置信矩阵相同的权重,然后以相同的方式计算相关的艺术家:
artist_factors,user_factors = alternating_least_squares(bm25_weight(plays),50)
与仅使用 LSA 相比,该方法可以产生明显更好的结果。比较 Bob Dylan 的结果作为一个例子: