聚类的步骤
- 初始化中心点(因为聚类效果和初始中心点相关,容易出现局部最优,所以可以多初始化几次)。
- 根据中心点,计算每个向量到中心点的距离。
- 根据距离,把向量归属于不同的中心点
- 然后对中心点周围的向量,累加取平均形成新的中心点。
- 然后开始新一轮的聚类。
- 从多次聚类中,选择最优的中心点,训练完成。
代码详解
for (int redo = 0; redo < nredo; redo++) { ///切换初始的中心点(避免局部最优),
if (verbose && nredo > 1) {
printf("Outer iteration %d / %d\n", redo, nredo);
}
// initialize remaining centroids with random points from the dataset
centroids.resize (d * k);
std::vector<int> perm (nx);
rand_perm (perm.data(), nx, seed + 1 + redo * 15486557L);
for (int i = n_input_centroids; i < k ; i++) ///n_input_centroids:已有中心点的个数
memcpy (¢roids[i * d], x + perm[i] * d,
d * sizeof (float)); ///初始化中心点
post_process_centroids (); ///归一化中心点,中心点取整
if (index.ntotal != 0) {
index.reset();
}
if (!index.is_trained) {
index.train (k, centroids.data()); ///
}
for (int i = 0; i < niter; i++) { ///
优化中心点(在当前中心点的周围聚集一批点,形成新的中心点,根据新中心点再聚类,直到中心点不再变化,可能会形成局部最优,因为和起始的中心点选择有关>
系)
double t0s = getmillisecs();
index.search (nx, x, 1, dis, assign); ///搜索每个向量对应的最近的1个向量(第0层对应的向量)
InterruptCallback::check(); ///
t_search_tot += getmillisecs() - t0s;
err = 0;
for (int j = 0; j < nx; j++)
err += dis[j];
obj.push_back (err);
///更新中心点, 把每个中心点周围的点取平均,另外对中心点周围没有点的情况做了处理(d:子空间维度; k:中心点个数)
int nsplit = km_update_centroids (
x, centroids.data(),
assign, d, k, nx, frozen_centroids ? n_input_centroids : 0);
if (verbose) {
printf (" Iteration %d (%.2f s, search %.2f s): "
"objective=%g imbalance=%.3f nsplit=%d \r",
i, (getmillisecs() - t0) / 1000.0,
t_search_tot / 1000,
err, imbalance_factor (nx, k, assign),
nsplit);
fflush (stdout);