背景
在對大樣本進行聚類時,由於k-means的計算開銷問題,通常隨機選取部分樣本進行聚類,得到聚類中心。然而往往要得到每個樣本最近聚類中心,這常用在檢索索引構建中,eg. OPQ (PAMI 2014),Inverted Multi-Index(PAMI 2014)。
算法步驟
設一個特徵向量p(1*2000),2000是特徵維數。聚類中心矩陣爲C(256*2000),256爲中心數,2000爲特徵維數。
1. 數據歸一化
2. 聚類中心歸一化
3. p_norm 擴展至 1*256
4. 計算p_norm = p_norm+c_norm;
5. 計算點p到256箇中心的距離向量
6. 計算dis中最小的那個數據的index,即爲離點p最近的聚類中心
分析
爲什麼第5步中的算式就得到距離了呢?
答:設C中的一個聚類中心向量爲
即:
C++代碼
要用到blas庫, blas函數的功能解釋請查詢官網document
// 設有一個數據點vector<float> point
// 聚類中心vector<float> vocabs, float* vocabs_matrices
// centroids_norms_爲第2步中的聚類中心norm
float p_norm = cblas_sdot(feature_dim, &(point[0]), 1, &(point[0]), 1);
cblas_saxpy(vocabs_[0].size(), 1,
(centroids_norms_[0]), 1, &(p_norms_[0]), 1);
cblas_sgemv(CblasRowMajor, CblasNoTrans,
vocabs_[0].size(), subspace_dimension, -2.0,
vocabs_matrices_[0], feature_dim, &(point[0]), 1, 1, &(p_norms_[0]), 1);
Matlab代碼
Matlab for循環的效率低,用矩陣運算。
設數據矩陣P爲10000*2000,其中2000是特徵維數,10000是樣本數聚類中心矩陣爲C爲256*2000,256爲中心數,2000爲特徵維數。
1. 數據歸一化
2. 聚類中心歸一化
3. P_norm 擴展至 10000*256(將原來的10000*1的P_norm複製9999遍,按列填充矩陣), C_norm擴展至256*10000(同上,也是複製填充向量得出)。
4. 其後步驟同上。
% 將數據點分配到附近的聚類中心
% Input:
% points vocab 都按列組織,eg. points中一列位一個特徵,行數爲特徵維數,列數爲樣本數
% Output:
% idx_table 爲每個樣本分配到的最近的聚類中心的下標(從0開始!)
function idx_table = calidx(points, vocab)
disp('Cal coarse ids...');
n_vocab = size(vocab, 2);
N = size(points, 2);
idx_table = zeros(N, 1);
vocab_norm = sum(vocab .^ 2, 1); % get a row vector
p_norm = repmat(sum(points .^ 2, 1), [n_vocab, 1]) + repmat(vocab_norm', [1, N]); % get n_vocab*N
dis = -2.0 * vocab' * points + p_norm; % get n_vocab * N dis
[~, idx_table] = min(dis, [], 1);
idx_table = idx_table - 1; % Matlab下標從1開始,轉成從0開始的