如何计算多对多依赖图模型的特征向量?
好问题!要计算这个依赖图的特征向量(通常我们指的是主特征向量,用来衡量节点的重要性,类似PageRank的逻辑),我们可以分步骤来实现,结合你的Rails模型结构,具体如下:
第一步:明确图的节点与边
你的模型里,每个Article是图的节点,Relation代表一条边:dependent(依赖方)指向dependency(被依赖方)——也就是说,如果文章A依赖文章B,那么A会给B的“重要性”投票,我们的特征向量会反映这种投票后的权重分布。
如果你的业务逻辑需要反过来(比如被依赖方指向依赖方),只需要后续代码中调换dependency_id和dependent_id的角色即可。
第二步:构建转移矩阵
首先我们需要把所有文章映射到矩阵的索引,然后构建转移矩阵(矩阵中的元素M[i][j]表示从节点i跳转到节点j的概率):
# 获取所有文章,建立ID到矩阵索引的映射 articles = Article.all id_to_index = articles.each_with_index.to_h { |article, idx| [article.id, idx] } total_nodes = articles.size # 初始化全0的转移矩阵 transition_matrix = Array.new(total_nodes) { Array.new(total_nodes, 0.0) } # 先统计每个节点的出度(即该节点依赖了多少其他文章) out_degree = Hash.new(0) Relation.find_each do |rel| from_idx = id_to_index[rel.dependent_id] next unless from_idx # 跳过已删除的文章(如果存在的话) out_degree[from_idx] += 1 end # 填充转移矩阵:如果节点i有出度d,每条出边的概率是1/d Relation.find_each do |rel| from_idx = id_to_index[rel.dependent_id] to_idx = id_to_index[rel.dependency_id] next unless from_idx && to_idx if out_degree[from_idx] > 0 transition_matrix[from_idx][to_idx] = 1.0 / out_degree[from_idx] end end
第三步:用幂迭代法计算主特征向量
主特征向量对应图的最大特征值,最适合用幂迭代法计算(简单易实现,适合大规模图)。我们还可以加入阻尼因子(模拟用户随机跳转的行为,通常设为0.85):
def power_iteration(transition_matrix, damping_factor = 0.85, max_iterations = 100, tolerance = 1e-6) total_nodes = transition_matrix.size # 初始化权重向量:每个节点初始权重相等 current_vector = Array.new(total_nodes, 1.0 / total_nodes) max_iterations.times do new_vector = Array.new(total_nodes, (1 - damping_factor) / total_nodes) # 计算新的权重向量 total_nodes.times do |i| total_nodes.times do |j| new_vector[j] += damping_factor * transition_matrix[i][j] * current_vector[i] end end # 判断是否收敛:向量变化量小于阈值就停止迭代 diff = new_vector.each_with_index.sum { |val, idx| (val - current_vector[idx]).abs } current_vector = new_vector break if diff < tolerance end current_vector end # 计算特征向量(即各文章的重要性权重) feature_vector = power_iteration(transition_matrix) # 把结果映射回文章对象,按权重降序排序 article_scores = articles.each_with_index.to_h { |article, idx| [article, feature_vector[idx]] } sorted_articles = article_scores.sort_by { |_article, score| -score }
关键注意事项
- 边方向调整:如果你的业务逻辑中,被依赖的文章需要指向依赖它的文章(比如想衡量“被多少文章依赖”的反向权重),只需要在构建矩阵时把
from_idx设为id_to_index[rel.dependency_id],to_idx设为id_to_index[rel.dependent_id]。 - 孤立节点处理:没有任何依赖或被依赖的文章,我们通过阻尼因子的均匀分布项处理,确保它们也能获得基础权重。
- 性能优化:如果你的文章数量很大(比如上万级),纯Ruby的矩阵操作会比较慢。可以考虑用
NMatrix(Ruby的线性代数库)来加速,或者把数据导出到Python用numpy/scipy计算后再导回Rails。 - 按用户过滤:如果需要计算某个特定用户的依赖图特征向量,只需要在查询
Relation时加上where(user_id: target_user_id)即可。
内容的提问来源于stack exchange,提问作者Amadeus Pagel
相关产品推荐
相关产品推荐

