【Faiss】源码阅读(四)——IVFPQ更低的内存占用
1. 理解PQ乘积量化请参考:https://zhuanlan.zhihu.com/p/114029796https://www.cnblogs.com/mafuqiang/p/7161592.html2. 整体思路这种索引方式比前两种复杂多了。现在还是不够清晰。下面对主要思路做下记录训练训练第一阶段的kmean随机下采样 100×256个样本训练kmean训练PQ量化随机下采样256×256(2
·
1. 理解PQ乘积量化
请参考:
2. 整体思路
这种索引方式比前两种复杂多了。现在还是不够清晰。下面对主要思路做下记录
- 训练
-
训练第一阶段的kmean
- 随机下采样 100×256个样本
- 训练kmean
-
训练PQ量化
- 随机下采样256×256(256个类别,每个类最多有256个样本)个样本
- 计算下采样样本和kmean聚类中心的匹配关系/向量差
- 对向量差矩阵,切分为M段,做聚类(256个中心),共M×256个聚类中心
- 对M×265个PQ聚类中心和kmean聚类中心求向量差/PQ量化表。
训练主要为了得到PQ量化表
- 添加底库
- 计算query和kmean聚类中心的匹配
- 计算query和匹配的kmean聚类中心的向量差
- 用向量差和PQ聚类中心求L2距离做PQ量化
- 将每个样本的PQ量化值写入倒序索引
主要为了得到每个底库样本的编码信息
- 查询
- 计算query和kmean聚类中心的匹配关系key
- 计算query和PQ聚类中心的内积
- 更新当前key kmeans聚类中心的PQ查询表
- 在key kmean聚类的所有样本中,利用PQ查询表计算距离
3. 具体实现
3.2 添加底库
if (precomputed_idx) {
idx = precomputed_idx;
} else {
idx_t * idx0 = new idx_t [n];
del_idx.set (idx0);
quantizer->assign (n, x, idx0); // 和第一阶段聚类中心的匹配
idx = idx0;
}
double t1 = getmillisecs ();
uint8_t * xcodes = new uint8_t [n * code_size]; // code_size=8,PQ的量化编码
ScopeDeleter<uint8_t> del_xcodes (xcodes);
const float *to_encode = nullptr;
ScopeDeleter<float> del_to_encode;
if (by_residual) {
to_encode = compute_residuals (quantizer, n, x, idx); // 采用残差的方式, 计算query和第一阶段聚类中心的 向量差
del_to_encode.set (to_encode);
} else {
to_encode = x;
}
pq.compute_codes (to_encode, xcodes, n); // 根据PQ聚类中心编码
3.3 查询
// 1. 计算query和PQ聚类中心的内积
void init_query_L2 () {
if (!by_residual) {
pq.compute_distance_table (qi, sim_table);
} else if (use_precomputed_table) {
pq.compute_inner_prod_table (qi, sim_table_2); // 计算 query和PQ聚类中心的内积,sim_table_2:256*8
}
}
// 2. 更新PQ查询表
fvec_madd (pq.M * pq.ksub,
&ivfpq.precomputed_table [key * pq.ksub * pq.M], // = r_norm + 2tab
-2.0, sim_table_2, // sim_table_2:query与PQ聚类中心的内积
sim_table); // sim_table = precomputed_tab -2*sim_table_2
// 3. 计算距离
void scan_list_with_table (size_t ncode, const uint8_t *codes,
SearchResultType & res) const
{
for (size_t j = 0; j < ncode; j++) {
float dis = dis0;
const float *tab = sim_table; // 256×8维,这里是一种查表的方式,sim_table表示query到底库样本的距离。sim_table采用非对称计算的方式
for (size_t m = 0; m < pq.M; m++) {
dis += tab[*codes++]; // 距离 , 需要tab + codes两个东西, codes表示
tab += pq.ksub;
}
res.add(j, dis); // 用堆存放结果
}
}
其他
- 类的继承太多了,阅读跳转太多,没有面向过程的清晰
更多推荐


所有评论(0)