hnswlib 代码分析 hnswlib 源码分析 

train过程说明

*
* 主要是生成hnsw模型
* Hnsw中,storage中存储的原始的中心点向量
* 生成hnsw
* 为每层分配空间
* 每层的中心点在当前层及下面的每一层都有中心点,例如第n层在n、n-1、…1层都有临近点,第3层只在3、2、1层有临近点。
* 同一个临近点在各层的数据是连续存储在一起的。
* 新加入的数据在下面的各层查找对应的临近点的候选集,选出最后的临近点,然后建立link
* 调整其他临近点的link,因为临近是相互的

76 void HNSW::set_default_probas(int M, float levelMult)
 77 {
 78   int nn = 0;
 79   cum_nneighbor_per_level.push_back (0);
 80   for (int level = 0; ;level++) {
 81     float proba = exp(-level / levelMult) * (1 - exp(-1 / levelMult));
///点落在每一层的概率,层越高概率越低,第0层概率为0.5
 82     if (proba < 1e-9) break;
 83     assign_probas.push_back(proba); ///向量落入每一层的概率
 84     nn += level == 0 ? M * 2 : M;
 85     cum_nneighbor_per_level.push_back (nn); ///每一层临近点的个数(每一层保存的是前几层的和)
 86   }
 87 }
 

203 /*n:向量个数, 为offsets,neighbors赋值*/
204 int HNSW::prepare_level_tab(size_t n, bool preset_levels)
205 { 
206   size_t n0 = offsets.size() - 1;
207   
208   if (preset_levels) { 
209     FAISS_ASSERT (n0 + n == levels.size());
210   } else {
211     FAISS_ASSERT (n0 == levels.size());
212     for (int i = 0; i < n; i++) { ///为每条数据指定放到第几层
213       int pt_level = random_level(); ///数据放入第几层
214       levels.push_back(pt_level + 1); ///根据概率为数据指定level
215     }
216   }
217   
218   int max_level = 0;
219   for (int i = 0; i < n; i++) { ///感觉这里是为offsets neighbors分配空间
220     int pt_level = levels[i + n0] - 1; ///第几层
221     if (pt_level > max_level) max_level = pt_level;
222     offsets.push_back(offsets.back() +
223                       cum_nb_neighbors(pt_level + 1)); ///偏移是前面所有个数的累加
224     neighbors.resize(offsets.back(), -1);
225   }
226   
227   return max_level; ///
}

439 /// Finds neighbors and builds links with them, starting from an entry
440 /// point. The own neighbor list is assumed to be locked.
pt_id:向量(query向量)对应的id, nearest:最近点的idx(可以理解为中心点id),d_nearest:
    最近距离,level:对应的层。在当前层查找最近的临近点,然后更新相关的临近点(随着新中心点加入,已加入的中心点的临近点也会发生变化)       
441 void HNSW::add_links_starting_from(DistanceComputer& ptdis,
442                                    storage_idx_t pt_id,
443                                    storage_idx_t nearest,
444                                    float d_nearest,
445                                    int level,
446                                    omp_lock_t *locks,
447                                    VisitedTable &vt)
448 {
449   std::priority_queue<NodeDistCloser> link_targets;
450 
451   search_neighbors_to_add(*this, ptdis, link_targets, nearest, d_nearest,
452                           level, vt); ///当前层中和query最近的中心点放到link_targets队列中
453 
454   // but we can afford(给予) only this many neighbors
455   int M = nb_neighbors(level); ///当前层的连接点数 
456 
457   ::faiss::shrink_neighbor_list(ptdis, link_targets, M);
///根据需要缩减临近点的数量(保留有代表性的临近点),把最优的放入link_targets中
458 
459   while (!link_targets.empty()) {
460     int other_id = link_targets.top().id;
 

462     omp_set_lock(&locks[other_id]);
463     add_link(*this, ptdis, other_id, pt_id, level);
///调整other_id对应临近点(临近点是有限的)
464     omp_unset_lock(&locks[other_id]);
465 
466     add_link(*this, ptdis, pt_id, other_id, level); ///调整新的链接(临近点是有限的)
467 
468     link_targets.pop();
469   }
470 }
 

技术
下载桌面版
GitHub
Gitee
SourceForge
百度网盘(提取码:draw)
云服务器优惠
华为云优惠券
腾讯云优惠券
阿里云优惠券
Vultr优惠券
站点信息
问题反馈
邮箱:[email protected]
吐槽一下
QQ群:766591547
关注微信