一句话总结: 近朱者赤近墨者黑!
基于电影中的亲吻、打斗出现的次数,使用 k-近邻算法构造程序,就可以自动划分电影的题材类型。
现在根据上面我们得到的样本集中所有电影与未知电影的距离,按照距离递增排序,可以找到 k 个距离最近的电影。 假定 k=3,则三个最靠近的电影依次是, He's Not Really into Dudes 、 Beautiful Woman 和 California Man。 knn 算法按照距离最近的三部电影的类型,决定未知电影的类型,而这三部电影全是爱情片,因此我们判定未知电影是爱情片。
KNN 通俗理解
给定一个训练数据集,对新的输入实例,在训练数据集中找到与该实例最邻近的 k 个实例,这 k 个实例的多数属于某个类,就把该输入实例分为这个类。
KNN 开发流程
收集数据: 任何方法 准备数据: 距离计算所需要的数值,最好是结构化的数据格式 分析数据: 任何方法 训练算法: 此步骤不适用于 k-近邻算法 测试算法: 计算错误率 使用算法: 输入样本数据和结构化的输出结果,然后运行 k-近邻算法判断输入数据分类属于哪个分类,最后对计算出的分类执行后续处理
KNN 算法特点
优点: 精度高、对异常值不敏感、无数据输入假定 缺点: 计算复杂度高、空间复杂度高 适用数据范围: 数值型和标称型
:
她希望:
现在她收集到了一些约会网站未曾记录的数据信息,这更有助于匹配对象的归类。
收集数据: 提供文本文件
海伦把这些约会对象的数据存放在文本文件 中,总共有 1000 行。海伦约会的对象主要包含以下 3 种特征:
文本文件数据格式如下:
40920 8.326976 0.953952 3 14488 7.153469 1.673904 2 26052 1.441871 0.805124 1 75136 13.147394 0.428964 1 38344 1.669788 0.134296 1
col_names = {'fly_per','play_game','cost_ic','num'}; data = readtable('datingTestSet2.csv'); data.Properties.VariableNames = col_names; num_best = 3; num_like = 2; num_hate = 1; data_best = table(); data_like = table(); data_hate = table(); data_erro = table(); for i = 1:length(data.Variables) current_data = data(i,:); current_data_number = current_data.num; if current_data_number == num_best data_best = [data_best;current_data]; elseif current_data_number == num_like data_like = [data_like;current_data]; elseif current_data_number == num_hate data_hate = [data_hate;current_data]; else disp('错误数据,检查') data_erro = [data_erro;current_data]; end end
%% 数据分析 figure ; hold on; scatter(data_best.fly_per, data_best.play_game, 'r','filled'); scatter(data_like.fly_per, data_like.play_game, 'g','filled'); scatter(data_hate.fly_per, data_hate.play_game, 'b','filled'); legend('BEST','LIKE','HATE'); xlabel('每年获得的飞行常客里程数'); ylabel('玩视频游戏所耗时间百分比') hold off;
下图中采用矩阵的第一和第二列属性得到很好的展示效果,清晰地标识了三个不同的样本分类区域,具有不同爱好的人其类别区域也不同。
max_fly = max(data.fly_per); max_play = max(data.play_game); max_cost_ic = max(data.cost_ic); min_fly = min(data.fly_per); min_play = min(data.play_game); min_cost_ic = min(data.cost_ic); data_new = table(); for j = 1:length(data.Variables) current_data = data(j,:); current_data.fly_per = (current_data.fly_per - min_fly) / (max_fly - min_fly); current_data.play_game = (current_data.play_game - min_play) / (max_play - min_play); current_data.cost_ic = (current_data.cost_ic - min_cost_ic) / (max_cost_ic - min_cost_ic); data_new = [data_new;current_data]; end
再对测试集数据进行归一化训练(使用训练集的数据作为Max值和Min值)
这里随机选取一组数据 作为测试集
x = [32000 12 0.21]; x(1) = (x(1) - min_fly) / (max_fly - min_fly); x(2) = (x(2) - min_play) / (max_play - min_play); x(3) = (x(3) - min_cost_ic) / (max_cost_ic - min_cost_ic); data_test = table(); for j = 1:length(data.Variables) current_data = data_new(j,:); current_data_value = sqrt((x(1) - current_data.fly_per)^2 + (x(2) - current_data.play_game)^2 + (x(3) - current_data.cost_ic)^2); input_data = table(current_data_value, current_data.num, 'VariableNames', {'distance', 'num'}); data_test = [data_test;input_data]; end
top_k_nums = data_test.num(1:k);
[unique_nums, ~, idx] = unique(top_k_nums);counts = accumarray(idx, 1);
[max_count, max_idx] = max(counts);most_frequent_num = unique_nums(max_idx);
fprintf('前%d个最近邻中,数字%d出现了%d次,是最频繁的\n', k, most_frequent_num, max_count);
输出为`前5个最近邻中,数字3出现了5次,是最频繁的` 总代码如下: ```matlab col_names = {'fly_per','play_game','cost_ic','num'}; data = readtable('datingTestSet2.csv'); data.Properties.VariableNames = col_names; num_best = 3; num_like = 2; num_hate = 1; data_best = table(); data_like = table(); data_hate = table(); data_erro = table(); for i = 1:length(data.Variables) current_data = data(i,:); current_data_number = current_data.num; if current_data_number == num_best data_best = [data_best;current_data]; elseif current_data_number == num_like data_like = [data_like;current_data]; elseif current_data_number == num_hate data_hate = [data_hate;current_data]; else disp('错误数据,检查') data_erro = [data_erro;current_data]; end end %% 数据分析 figure ; hold on; scatter(data_best.fly_per, data_best.play_game, 'r','filled'); scatter(data_like.fly_per, data_like.play_game, 'g','filled'); scatter(data_hate.fly_per, data_hate.play_game, 'b','filled'); legend('BEST','LIKE','HATE'); xlabel('每年获得的飞行常客里程数'); ylabel('玩视频游戏所耗时间百分比') hold off; %% 归一化处理 max_fly = max(data.fly_per); max_play = max(data.play_game); max_cost_ic = max(data.cost_ic); min_fly = min(data.fly_per); min_play = min(data.play_game); min_cost_ic = min(data.cost_ic); data_new = table(); for j = 1:length(data.Variables) current_data = data(j,:); current_data.fly_per = (current_data.fly_per - min_fly) / (max_fly - min_fly); current_data.play_game = (current_data.play_game - min_play) / (max_play - min_play); current_data.cost_ic = (current_data.cost_ic - min_cost_ic) / (max_cost_ic - min_cost_ic); data_new = [data_new;current_data]; end %% 输入数据 x = [32000 12 0.21]; x(1) = (x(1) - min_fly) / (max_fly - min_fly); x(2) = (x(2) - min_play) / (max_play - min_play); x(3) = (x(3) - min_cost_ic) / (max_cost_ic - min_cost_ic); data_test = table(); for j = 1:length(data.Variables) current_data = data_new(j,:); current_data_value = sqrt((x(1) - current_data.fly_per)^2 + (x(2) - current_data.play_game)^2 + (x(3) - current_data.cost_ic)^2); input_data = table(current_data_value, current_data.num, 'VariableNames', {'distance', 'num'}); data_test = [data_test;input_data]; end %% 分析数据 k = 5; data_test = sortrows(data_test,"distance"); top_k_nums = data_test.num(1:k); [unique_nums, ~, idx] = unique(top_k_nums); counts = accumarray(idx, 1); [max_count, max_idx] = max(counts); most_frequent_num = unique_nums(max_idx); fprintf('前%d个最近邻中,数字%d出现了%d次,是最频繁的\n', k, most_frequent_num, max_count);
使用上述即可代替 这一长串的代码,非常便捷。
接下来是对 的介绍
**A : **填入归一化的数据,可以是向量,矩阵,或者表(table)
Method : 选择归一化(标准化)的方法,是最大最小标准化,是z-cores标准化,是范数标准化。
较为简单比较适合本题,在标准机器学习中更常见。
具体步骤如下:
具体代码如下:
k = 3; % 训练 k-NN 模型 Mdl = fitcknn(normalized_features, labels, 'NumNeighbors', k); % 定义待分类的新数据 x_new = [32000, 12, 0.21]; % 对新数据进行归一化 (使用训练集的参数) x_new_normalized = (x_new - C(1,:)) ./ S(1,:); % 进行预测 predicted_label = predict(Mdl, x_new_normalized); % 显示结果 fprintf('对于输入数据 [%d, %d, %.2f],预测的分类结果是: %d\n', ... x_new(1), x_new(2), x_new(3), predicted_label);
代表输入的训练集数据
代表每个数据的真实标签
指定knn算法中k值,需要跟在后面
: 指定计算点与点之间距离的方法
: 设定邻居投票时的权重
: 是否在训练前对数据进行 z-score 标准化
: 使用训练好的模型进行分类预测
: 这必须是 (或其他训练函数) 创建的模型对象。 会使用 中存储的训练数据和配置参数
: 要进行分类的一个或多个新数据点
总代码如下:
%% 1. 数据加载和准备 col_names = {'fly_per', 'play_game', 'cost_ic', 'num'}; data = readtable('datingTestSet2.csv'); data.Properties.VariableNames = col_names; % 分离特征和标签 features = data{:, {'fly_per', 'play_game', 'cost_ic'}}; labels = data.num; %% 2. 数据归一化 [normalized_features, C, S] = normalize(features, 'range'); data{:, 1:3} = normalized_features; % 更新表中的数据为归一化后的值 %% 3. 数据可视化 (与您原脚本类似) figure; hold on; gscatter(data.fly_per, data.play_game, data.num, 'rgb', 'osd'); xlabel('每年获得的飞行常客里程数'); ylabel('玩视频游戏所耗时间百分比'); legend('非常喜欢', '有点喜欢', '不喜欢'); title('归一化后的约会数据散点图'); hold off; %% 4. k-NN 分类 % 设定 k 值 k = 3; % 训练 k-NN 模型 Mdl = fitcknn(normalized_features, labels, 'NumNeighbors', k); % 定义待分类的新数据 x_new = [32000, 12, 0.21]; % 对新数据进行归一化 (使用训练集的参数) x_new_normalized = (x_new - C(1,:)) ./ S(1,:); % 进行预测 predicted_label = predict(Mdl, x_new_normalized); % 显示结果 fprintf('对于输入数据 [%d, %d, %.2f],预测的分类结果是: %d\n', ... x_new(1), x_new(2), x_new(3), predicted_label);
对查询点标签影响显著(效果拔群)。k值小的时候 近似误差小,估计误差大。 k值大 近似误差大,估计误差小。
如果选择较小的 k 值,就相当于用较小的邻域中的训练实例进行预测,“学习”的近似误差(approximation error)会减小,只有与输入实例较近的(相似的)训练实例才会对预测结果起作用。
但缺点是“学习”的估计误差(estimation error)会增大,预测结果会对近邻的实例点非常敏感。如果邻近的实例点恰巧是噪声,预测就会出错。换句话说,k 值的减小就意味着整体模型变得复杂,容易发生过拟合。
如果选择较大的 k 值,就相当于用较大的邻域中的训练实例进行预测。其优点是可以减少学习的估计误差。
但缺点是学习的近似误差会增大。这时与输入实例较远的(不相似的)训练实例也会对预测起作用,使预测发生错误。 k 值的增大就意味着整体的模型变得简单。
太大太小都不太好,可以用交叉验证(cross validation)来选取适合的k值。近似误差和估计误差,请看这里:
**距离度量 Metric/Distance Measure **
距离度量 通常为 欧式距离(Euclidean distance),还可以是 Minkowski 距离 或者 曼哈顿距离。也可以是 地理空间中的一些距离公式。(更多细节可以参看 sklearn 中 valid_metric 部分)
分类决策 (decision rule)
分类决策 在 分类问题中 通常为通过少数服从多数 来选取票数最多的标签,在回归问题中通常为 K个最邻点的标签的平均值。
免责声明:本文系网络转载或改编,未找到原创作者,版权归原作者所有。如涉及版权,请联系删