跳到主要内容

GPU K-means示例

单卡GPU上的K-means聚类示例

以下是一个在单个GPU上运行K-means聚类的C++示例代码,该示例表明GPU相关代码几乎可以直接替换CPU代码:

#include <cstdio>
#include <vector>

#include <faiss/Clustering.h>
#include <faiss/utils.h>
#include <faiss/gpu/GpuIndexFlat.h>
#include <faiss/gpu/StandardGpuResources.h>

// 在超立方体中生成一些随机向量(在CPU上操作)
std::vector<float> makeRandomVecs(size_t numVecs, int dim) {
std::vector<float> vecs(numVecs * dim);
faiss::float_rand(vecs.data(), vecs.size(), 1);
return vecs;
}

int main(int argc, char** argv) {
// 默认会预留18%的GPU内存用于临时计算工作;
// 可以根据实际需要调整这个比例,
// 也可以自定义GpuResources对内存进行不同方式的管理。
faiss::gpu::StandardGpuResources res;

int dim = 128; // 向量维度
int numberOfEMIterations = 20; // EM迭代次数
size_t numberOfClusters = 20000; // 聚类中心个数
size_t numVecsToCluster = 5000000; // 参与聚类的数据向量总数

// 生成一批随机向量;注意:这里生成的数据是在CPU上
std::vector<float> vecs = makeRandomVecs(numVecsToCluster, dim);

faiss::gpu::GpuIndexFlatConfig config;
config.device = 0; // 使用第0号GPU(默认)
config.useFloat16 = false; // 不使用float16精度(默认)
faiss::gpu::GpuIndexFlatL2 index(&res, dim, config);

faiss::ClusteringParameters cp;
cp.niter = numberOfEMIterations;
cp.verbose = true; // 输出每轮迭代的统计信息

// 如果需要球面(spherical)k-means,请使用GpuIndexFlatIP,并设置cp.spherical = true

// :::note
// Faiss默认情况下,每个聚类中心采样256个向量,以避免在聚类中心数较少、向量数巨大的情况下资源耗尽。
// 例如,numberOfClusters = 1000,而numVecsToCluster = 1000000时,
// 实际只会采样256000个向量参与聚类。
// 你可以通过设置cp.max_points_per_centroid
// 覆盖此采样逻辑,使每个聚类中心使用自定义数量的数据:
// cp.max_points_per_centroid = ((numVecsToCluster + numberOfClusters - 1) / numberOfClusters);
// :::

faiss::Clustering kMeans(dim, numberOfClusters, cp);

// 开始执行聚类
kMeans.train(numVecsToCluster, vecs.data(), index);

// kMeans.centroids 包含最终得到的聚类中心(数据保存在CPU上)
printf("centroid 3 dim 6 is %f\n", kMeans.centroids[3 * dim + 6]);

return 0;
}

聚类输出示例

在 P100-PCIe GPU 上运行这段代码,会输出如下内容:

Clustering 5000000 points in 128D to 20000 clusters, redo 1 times, 20 iterations Preprocessing in 0.585711 s Iteration 19 (109.14 s, search 106.78 s): objective=4.51621e+07 imbalance=1.012 nsplit=0 centroid 3 dim 6 is 0.468013

使用说明与注意事项

提示

示例代码中的待聚类向量数据实际上来源于CPU。换句话说,数据只需要在计算时临时放入GPU,不会一直占用GPU内存。因此,Faiss能够在GPU上处理超大规模的数据集聚类任务,且显存消耗有限。

备注

k-means部分的代码在GPU和CPU之间共享(即faiss::Clustering对象)。理论上,如果将更多的聚类计算移到GPU上,可以进一步提升速度。但对于超大规模的K-means任务(如DEEP1B k-NN图的聚类过程),CPU部分的耗时只占整体的很小一部分(通常小于5%)。目前,faiss::Clustering只接受CPU内存中的输入,输出也是CPU内存,而不像GpuIndexFlatL2那样支持纯GPU内存。
如果你想把上面代码改为在CPU上执行,只需把GpuIndexFlatL2替换为IndexFlatL2即可。

多卡GPU聚类

如果你需要多卡(multi-GPU)并行聚类,可把GpuIndexFlatL2替换为多个GpuIndexFlatL2组合形成的IndexProxy对象。

有关使用多卡的Python示例,请参考:benchs/kmeans_mnist.py

注意

目前暂未补充C++版多卡聚类代码示例。如需C++多卡支持,请关注后续更新。