多标签K近邻(ML-KNN)是经典KNN算法在多标签学习场景下的直接扩展,也是非常有效和常用的基准方法。
一、 算法核心思想
传统的KNN用于多分类问题时,会找出K个最近邻,然后通过"投票法"决定样本的单一类别。而多标签KNN(ML-KNN)的核心思想是:
对于每一个标签(Label),独立地判断样本属于该标签的概率。这个判断依据是其K个最近邻样本在该标签上的表现(即哪些邻居有这个标签)。
它本质上为每个标签都训练了一个独立的分类器(基于邻居信息),因此可以输出样本的多个标签。
二、 算法步骤详解
假设我们有训练集 Train_X (特征), Train_Y (标签,0/1矩阵),要预测测试样本 Test_X 的标签 Test_Y。
-
Step 1: 计算最近邻
- 对于每一个测试样本
Test_X(i),计算它与所有训练样本Train_X的距离(如欧氏距离)。 - 找出与
Test_X(i)距离最近的 K 个训练样本,记其索引为N_i(大小为 K 的集合)。
- 对于每一个测试样本
-
Step 2: 统计邻居的标签信息(核心)
- 对于每一个标签
l(即标签矩阵的每一列):- 统计这 K 个邻居中,拥有该标签
l的邻居个数。记作C_l。
C_l = sum(Train_Y(N_i, l) == 1)
- 统计这 K 个邻居中,拥有该标签
- 对于每一个标签
-
Step 3: 应用贝叶斯规则进行预测
- 这是ML-KNN区别于简单投票法的关键。它使用先验概率来做出更稳健的决策。
- 先验概率 (Prior) : 从整个训练集中计算。
P1_prior(l) = (s + sum(Train_Y(:, l) == 1)) / (s*2 + n_train)P0_prior(l) = 1 - P1_prior(l)(通常用拉普拉斯平滑,s=1防止概率为0)
- 后验概率 (Posterior) :基于当前样本的邻居信息计算。
P1 = P(H_l^1) * P(E_l^c | H_l^1)P0 = P(H_l^0) * P(E_l^c | H_l^0)- 其中:
H_l^1: 事件"测试样本有标签l"H_l^0: 事件"测试样本没有标签l"E_l^c: 事件"测试样本的K个邻居中,恰好有c个拥有标签l"(这里c = C_l)
- 决策 :如果
P1 > P0,则预测该测试样本拥有标签l,否则没有。
在实际代码实现中,我们通常预先计算整个训练集每个标签的邻居数量分布(似然概率),然后查表。
三、 MATLAB
matlab
function Predicted_Labels = ML_KNN(Train_X, Train_Y, Test_X, K, Smooth)
% ML-KNN 多标签K近邻算法
% 输入:
% Train_X, Train_Y - 训练数据和标签 (多标签,0/1矩阵)
% Test_X - 测试数据
% K - 近邻数
% Smooth - 拉普拉斯平滑参数 (通常为1)
% 输出:
% Predicted_Labels - 测试集的预测标签 (0/1矩阵)
[n_train, n_label] = size(Train_Y);
n_test = size(Test_X, 1);
% 初始化预测结果矩阵
Predicted_Labels = zeros(n_test, n_label);
% 1. 计算所有测试样本与所有训练样本的距离
% 注意:对于大数据集,此处需要优化以防止内存溢出 (例如使用循环或pdist2)
Dist = pdist2(Test_X, Train_X); % 需要 Statistics and Machine Learning Toolbox
% 或者自己实现欧氏距离:
% for i=1:n_test
% Dist(i,:) = sqrt(sum((Train_X - Test_X(i,:)).^2, 2));
% end
% 2. 对每个标签,预先计算先验概率
P1_prior = zeros(1, n_label);
for l = 1:n_label
P1_prior(l) = (Smooth + sum(Train_Y(:, l) == 1)) / (Smooth * 2 + n_train);
end
P0_prior = 1 - P1_prior;
% 3. 对每个测试样本进行预测
for i = 1:n_test
% a. 获取当前测试样本的K个最近邻的索引
[~, sorted_idx] = sort(Dist(i, :));
neighbor_idx = sorted_idx(1:K);
% b. 获取这些邻居的标签子集
Neighbor_Labels = Train_Y(neighbor_idx, :);
% c. 对于每一个标签l,进行贝叶斯推断
for l = 1:n_label
% 统计K个邻居中拥有标签l的个数 (c)
c = sum(Neighbor_Labels(:, l));
% --- 此处应使用预计算的似然概率 P(c | H_l^1) 和 P(c | H_l^0) ---
% 为了简化示例,我们做一个近似估计:
% 假设似然概率正比于二项分布,并从训练集中估计其参数
% 计算整个训练集中,每个样本的K近邻拥有标签l的个数的分布
% (在实际完整实现中,这里需要预先用交叉验证等方法计算一个概率表)
% 本例简化: 使用邻居中标签l的频率作为概率的估计
% 注意:这是一个简化,完整的ML-KNN需要预先计算整个训练集的这个分布。
% 简单频率估计 (带平滑)
prob_c_given_H1 = (Smooth + c) / (Smooth * 2 + K);
prob_c_given_H0 = (Smooth + (K - c)) / (Smooth * 2 + K);
% 计算后验概率
P1 = P1_prior(l) * prob_c_given_H1;
P0 = P0_prior(l) * prob_c_given_H0;
% 决策
if P1 > P0
Predicted_Labels(i, l) = 1;
else
Predicted_Labels(i, l) = 0;
end
end
end
end
如何使用:
matlab
% 假设您已有数据
% Train_X: n_train x d_features 矩阵
% Train_Y: n_train x n_labels 矩阵 (元素为0或1)
% Test_X: n_test x d_features 矩阵
K = 10; % 选择近邻数
Smooth = 1; % 拉普拉斯平滑参数
Predicted_Y = ML_KNN(Train_X, Train_Y, Test_X, K, Smooth);
% 评估性能 (例如计算Hamming Loss)
% True_Y 是测试集真实的标签
hamming_loss = sum(sum(Predicted_Y ~= True_Y)) / (size(True_Y, 1) * size(True_Y, 2));
fprintf('Hamming Loss: %.4f\n', hamming_loss);
参考代码 多标签K近邻方法实现对多标签数据进行分类 www.3dddown.com/csa/53349.html
四、 事项与优化
-
距离度量 : 欧氏距离是默认选择,但对于高维或特定类型数据(如文本),余弦距离可能更合适。务必对特征数据进行标准化(Z-score或Min-Max),防止某些特征主导距离计算。
-
参数选择:
K(近邻数):是最关键的参数。太小容易过拟合,太大会平滑过度,导致性能下降。需要通过交叉验证在验证集上选择最佳K值。Smooth(平滑参数):通常设为1(拉普拉斯平滑)即可,用于处理概率为0的情况。
-
计算效率:
- ML-KNN的预测阶段很慢,因为需要为每个测试样本 计算与所有训练样本的距离。对于大规模数据集,这是主要瓶颈。
- 优化方法 :使用KD树、Ball Tree等数据结构进行近邻搜索(MATLAB中可使用
knnsearch或fitcknn);或采用近似最近邻算法(ANN)。
-
完整实现:
- 上面的代码是简化版 。完整的ML-KNN实现需要在训练阶段预先为每个标签
l计算一个概率分布表,即P(c | H_l^1)和P(c | H_l^0)(c从0到K)。这个表是通过对训练集本身进行K近邻统计得到的。上述示例中用简单频率估计代替了这一步。
- 上面的代码是简化版 。完整的ML-KNN实现需要在训练阶段预先为每个标签
-
评估指标:
- 多标签学习的评估指标与单标签不同,常用:
- Hamming Loss(汉明损失):被错误预测的标签比例(越小越好)。
- F1-Score(宏平均/微平均):精确率和召回率的调和平均。
- Subset Accuracy(子集准确率):预测的标签集合与真实集合完全一致的样本比例(非常严格)。
- 多标签学习的评估指标与单标签不同,常用: