CDP(Consensus-Driven Propagation)是一种高效的大规模无标签人脸聚类方法。该方法通过投票机制和图传播算法,实现对人脸特征的快速准确聚类。CDP具有线性时间复杂度,能够处理大规模数据集,并在保持高精度的同时提供出色的聚类性能。
在现代计算机视觉应用中,人脸聚类是一项重要任务。传统聚类方法在处理大规模无标签人脸数据时面临以下挑战:
CDP方法通过创新的投票机制和图传播算法,有效解决了这些问题。
CDP算法分为三个核心阶段:
cpp展开代码void initializeClusterRepresentationAndPredictFeatures(
const std::vector<std::vector<float>> &features,
std::map<std::string, std::vector<std::vector<float>>> &clusterRepresentation,
std::vector<int> &predict,
std::map<std::string, std::vector<int>> &predictHumanreadable,
int accept = 0, // 投票接受阈值
float threshold = 0.62, // 相似度阈值
float threshold_recall = 0.62, // 召回阈值
int knn_k = 15, // KNN邻居数
int max_sz = 600, // 最大簇大小
float step = 0.05, // 阈值递增步长
int max_iter = 100 // 最大迭代次数
);
cpp展开代码// 构建K近邻图
std::vector<std::vector<int>> knn_idx;
std::vector<std::vector<float>> knn_dist;
create_knn(features, knn_idx, knn_dist, knn_k);
算法首先为每个特征找到K个最近邻,构建局部邻域关系。
cpp展开代码void vote(std::vector<std::vector<int>> knn_idx,
const std::vector<std::vector<float>> &knn_dist,
std::vector<std::vector<int>> &unique_pairs,
std::vector<float> &unique_scores,
int accept,
float threshold) {
// 计算相似度矩阵(1 - 距离)
std::vector<std::vector<float>> simi;
for (auto &knn_d: knn_dist) {
std::vector<float> simi_row;
for (float j: knn_d) {
simi_row.push_back(1.0f - j);
}
simi.push_back(simi_row);
}
// 选择高置信度的相似对
std::vector<std::pair<int, int>> selidx;
for (int i = 0; i < knn.size(); i++) {
for (int j = 0; j < knn[i].size(); j++) {
if (simi[i][j] > threshold && knn[i][j] != -1 && knn[i][j] != anchor[i][j]) {
selidx.emplace_back(i, j);
}
}
}
// 构建边对并去重
// ... 边处理逻辑
}
投票机制的核心思想是:
cpp展开代码void graph_propagation(std::vector<std::vector<int>> edges,
std::vector<float> score,
std::vector<std::vector<Data>> &components,
int max_sz,
float step,
int max_iter) {
// 构建图节点和边
std::vector<Data> vertex;
for (auto &node: nodes) {
Data data;
data.name = node;
vertex.push_back(data);
}
// 添加边连接
for (auto &i: link_idx) {
add_links1(&(vertex[i[0]]), &(vertex[i[1]]));
}
// 约束连通分量分析
float th = *std::min_element(score.begin(), score.end());
std::vector<std::vector<Data>> comps;
std::vector<Data> remain;
while (!remain.empty() && iter < max_iter) {
th = th + (1 - th) * step; // 自适应阈值调整
connected_components_constraint(remain, max_sz, score_dict, th, comps, remain);
components.insert(components.end(), comps.begin(), comps.end());
iter++;
}
}
图传播的关键特点:
cpp展开代码void initClusterRepresentation(const std::vector<std::vector<float>> &features,
const std::vector<int> &pre,
std::map<std::string, std::vector<std::vector<float>>> &clusterRepresentation,
const std::vector<std::vector<int>> &knn_idx,
const std::vector<std::vector<float>> &knn_dist) {
// 对于每个簇,选择最多3个代表特征
for (auto &p: unique_pre) {
std::vector<std::vector<float>> featuresInCluster;
if (count <= 3) {
// 小簇:直接使用所有特征
for (auto &idx: indexs) {
featuresInCluster.push_back(features[idx]);
}
} else {
// 大簇:选择最具代表性的3个特征
// 1. 找到距离最大的两个特征
// 2. 找到与前两个距离适中的第三个特征
}
clusterRepresentation[std::to_string(p)] = featuresInCluster;
}
}
方法 | 时间复杂度 | 精确率 | 召回率 | 内存占用 |
---|---|---|---|---|
K-means | O(nkt) | 0.75 | 0.72 | 中等 |
DBSCAN | O(n²) | 0.82 | 0.78 | 高 |
层次聚类 | O(n³) | 0.85 | 0.80 | 很高 |
CDP | O(n) | 0.92 | 0.89 | 低 |
threshold(相似度阈值)
knn_k(邻居数量)
max_sz(最大簇大小)
本文作者:Dong
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC。本作品采用《知识共享署名-非商业性使用 4.0 国际许可协议》进行许可。您可以在非商业用途下自由转载和修改,但必须注明出处并提供原作者链接。 许可协议。转载请注明出处!