PQ (Product Quantizer), 相对与普通的Quantizer而言
普通Quantizer 将整个向量通过单次聚类达到量化。
Product Quantizer 则通过对向量分段(这里的分段是针对向量本身的,例如128维的向量分为2个64维的向量),每段分别聚类得到多个量化结果(每段一个量化结果),这样的好处是同较小的码本来表达非常大量的码
把256个128维的向量分为4段,每段有256个32维的向量,每段独立训练中心点,这样在查询时从每个段中选一个最近的中心点(可以采用排列组合的思想理解),所以可能的情况有256*256*256*256, 这样可以用较少的向量,训练出更多的中心点,而且还省空间。
而普通Quantizer就只有256个中心点。
void ProductQuantizer::train (int n, const float * x)
{
if (train_type != Train_shared) { ///共享词典
train_type_t final_train_type;
final_train_type = train_type;
if (train_type == Train_hypercube ||
train_type == Train_hypercube_pca) { ///使用超立方体初始化中心点
if (dsub < nbits) { ///猜测是形成不了nbits个空间
final_train_type = Train_default;
printf ("cannot train hypercube: nbits=%ld > log2(d=%ld)\n",
nbits, dsub);
}
}
///为每个子空间分配内存(向量个数 * 子向量维度)
float * xslice = new float[n * dsub];
ScopeDeleter<float> del (xslice);
for (int m = 0; m < M; m++) { ///处理每个子空间
for (int j = 0; j < n; j++)
memcpy (xslice + j * dsub,
x + j * d + m * dsub,
dsub * sizeof(float));
Clustering clus (dsub, ksub, cp); /*dsub:子向量空间维度,ksub:子向量空间中心点个数 cp:参数*/
// we have some initialization for the centroids
if (final_train_type != Train_default) {
clus.centroids.resize (dsub * ksub); ///为中心点分配内存(空间维度 * 中心点个数)
}
switch (final_train_type) {
case Train_hypercube:
init_hypercube (dsub, nbits, n, xslice,
clus.centroids.data ());
break;
case Train_hypercube_pca:
init_hypercube_pca (dsub, nbits, n, xslice,
clus.centroids.data ());
break;
case Train_hot_start:
memcpy (clus.centroids.data(),
get_centroids (m, 0),
dsub * ksub * sizeof (float)); ///只是从子空间中赋了值
break;
default: ;
}
if(verbose) {
clus.verbose = true;
printf ("Training PQ slice %d/%zd\n", m, M);
}
IndexFlatL2 index (dsub); ///
clus.train (n, xslice, assign_index ? *assign_index : index); ///子空间
set_params (clus.centroids.data(), m); ///保存每个子空间的中心点
}
} else {
Clustering clus (dsub, ksub, cp);
if(verbose) {
clus.verbose = true;
printf ("Training all PQ slices at once\n");
}
IndexFlatL2 index (dsub);
clus.train (n * M, x, assign_index ? *assign_index : index);
for (int m = 0; m < M; m++) {
set_params (clus.centroids.data(), m);
}
}
}