机器学习---K最近邻(k-Nearest Neighbour,KNN)分类算法
K最近邻(k-Nearest Neighbour,KNN)分类算法
1.K最近邻(k-Nearest Neighbour,KNN)
K最近邻(k-Nearest Neighbour,KNN)分类算法,是一个理论上比较成熟的方法,也是最简单的机器学习算法之一。该方法的思路是:如果一个样本在特征空间中的k个最相似(即特征空间中最邻近)的样本中的大多数属于某一个类别,则该样本也属于这个类别。用官方的话来说,所谓K近邻算法,即是给定一个训练数据集,对新的输入实例,在训练数据集中找到与该实例最邻近的K个实例(也就是上面所说的K个邻居), 这K个实例的多数属于某个类,就把该输入实例分类到这个类中。
2.算法原理
如上图所示,有两类不同的样本数据,分别用蓝色的小正方形和红色的小三角形表示,而图正中间的那个绿色的圆所标示的数据则是待分类的数据。也就是说,现在, 我们不知道中间那个绿色的数据是从属于哪一类(蓝色小正方形or红色小三角形),下面,我们就要解决这个问题:给这个绿色的圆分类。
我们常说,物以类聚,人以群分,判别一个人是一个什么样品质特征的人,常常可以从他/她身边的朋友入手,所谓观其友,而识其人。我们不是要判别上图中那个绿色的圆是属于哪一类数据么,好说,从它的邻居下手。但一次性看多少个邻居呢?从上图中,你还能看到:
- 如果K=3,绿色圆点的最近的3个邻居是2个红色小三角形和1个蓝色小正方形,少数从属于多数,基于统计的方法,判定绿色的这个待分类点属于红色的三角形一类。
- 如果K=5,绿色圆点的最近的5个邻居是2个红色三角形和3个蓝色的正方形,还是少数从属于多数,基于统计的方法,判定绿色的这个待分类点属于蓝色的正方形一类。
KNN算法不仅可以用于分类,还可以用于回归。通过找出一个样本的k个最近邻居,将这些邻居的属性的平均值赋给该样本,就可以得到该样本的属性。更有用的方法是将不同距离的邻居对该样本产生的影响给予不同的权值(weight), 如权值与距离成反比。 该算法在分类时有个主要的不足是,当样本不平衡时,如一个类的样本容量很大,而其他类样本容量很小时,有可能导致当输入一个新样本 时,该样本的K个邻居中大容量类的样本占多数。 该算法只计算“最近的”邻居样本,某一类的样本数量很大,那么或者这类样本并不接近目标样本,或者这类样本很靠近目标样本。无论怎样,数量并不能影响运行 结果。可以采用权值的方法(和该样本距离小的邻居权值大)来改进。
3.算法不足
4.程序代码
以下两个片段一个是算法的主题,一个是主程序调用
/************************************************************************/
/*KNN分类算法C++实现版本 2015/11/5 */
/************************************************************************/
#include<iostream>
#include<fstream> #include<map>
#include<vector>
#include <string>
#include<cmath>
#include<algorithm> using namespace std; typedef string tLabel; //标签
typedef double tData; //数据
typedef pair<int,double> PAIR; //位置和使用率(概率)
const int MaxColLen = ;
const int MaxRowLen = ;
const int COLNUM = ;
ifstream fin;
ofstream fout; class KNN
{
private:
tData dataSet[MaxRowLen][MaxColLen]; //数据集
tLabel labels[MaxRowLen]; //分类结果的标签
tData testData[MaxColLen];//保留测试数据归一化的结果,或者原来的测试数据
int rowLen;
int k; //临近的K值
int test_data_num; //测试数据数量
map<int,double> map_index_dis; //训练数据集中各个数据到被测试数据的距离,并以训练数据的序号为索引
map<tLabel,int> map_label_freq; //KNN中的K个距离最近的特征分类结果与对应的频率
double get_distance(tData *d1,tData *d2);
public:
KNN();
//预处理,获得相应的距离
void get_all_distance();
//获取最大的标签使用频率作为最大的概率
tLabel get_max_freq_label();
//归一化训练数据
void auto_norm_data();
//测试误差率
void get_error_rate();
//构造比较器,内部类型
struct CmpByValue
{
bool operator() (const PAIR& lhs,const PAIR& rhs)
{
return lhs.second < rhs.second;
}
}; ~KNN();
}; KNN::~KNN()
{
fin.close();
fout.close();
map_index_dis.clear();
map_label_freq.clear();
}
/************************************************************************/
/* KNN算法构造函数 */
/************************************************************************/
KNN::KNN()
{
this->rowLen = ;
//this->colLen = col;
this->k = ;
test_data_num = ;
//训练数据的读取
fin.open("D:\\VC_WorkSpace\\KNNAlg\\datingTestTrainingData.txt");
//输出的结果到一个文件中保存
fout.open("D:\\VC_WorkSpace\\KNNAlg\\result.txt"); if( !fin || !fout )
{
cout << "can not open the file"<<endl;
exit();
} for(int i = ; i < rowLen; i++)
{
for(int j = ; j < COLNUM; j++)
{
//cout << dataSet[i][j] << "_";
fin >> dataSet[i][j];
fout << dataSet[i][j] << " ";
}
//输入样本中的每个值向量对应的分类结果到“分类结果空间”
fin >> labels[i];
fout << labels[i]<< endl;
//cout << endl;
} } void KNN:: get_error_rate()
{
int i,j,count = ;
tLabel label;
cout << "please input the number of test data : "<<endl;
cin >> test_data_num;//测试数据的数量
//以训练数据的前test_data_num作为测试样本
for( i = ; i < test_data_num; i++ )
{
for(j = ; j < COLNUM; j++)
{
testData[j] = dataSet[i][j];
}
//训练test_data_num之后的数据作为训练数据
get_all_distance();
label = get_max_freq_label();//返回分类结果
cout << "******* the lable = " << label << endl;
if( label != labels[i] )
count++;//分类失败统计器
map_index_dis.clear();
map_label_freq.clear();
}
//计算误差
cout<<"the error rate is = "<<(double)count/(double)test_data_num<<endl;
}
/************************************************************************/
/* 以欧式距离来表示 */
/************************************************************************/
double KNN:: get_distance(tData *d1,tData *d2)
{
double sum = ;
for(int i=;i<COLNUM;i++)
{
sum += pow((d1[i] - d2[i]) , );
}
//输出结果显示
//cout<<"the sum is = "<< sum << endl;
return sqrt(sum);
} /************************************************************************/
/* 以测试test_data_num序号之后的样本作为训练数据 */
/************************************************************************/
void KNN:: get_all_distance()
{
double distance;
int i;
for(i = test_data_num; i < rowLen; i++)
{
distance = get_distance(dataSet[i],testData);
map_index_dis[i] = distance;
} // 打开注释,可以查看索引距离集合中的内容
// map<int,double>::const_iterator it = map_index_dis.begin();
// while(it!=map_index_dis.end())
// {
// cout<<"index = "<<it->first<<" distance = "<<it->second<<endl;
// it++;
// } } tLabel KNN:: get_max_freq_label()
{
vector<PAIR> vec_index_dis(map_index_dis.begin(), map_index_dis.end());
//对结果进行排序操作,由小到大的顺序排列
sort(vec_index_dis.begin(), vec_index_dis.end(), CmpByValue());
//取前K个距离最近的特征标签作为分类的参考依据
for(int i = ; i < k; i++)
{
cout << "Index = " << vec_index_dis[i].first << " Distance = " << vec_index_dis[i].second << " Label = " << labels[vec_index_dis[i].first] << " \nCoordinate(";
int j;
for(j=; j < COLNUM - ; j++)
{
cout << dataSet[vec_index_dis[i].first][j]<<",";
}
cout << dataSet[vec_index_dis[i].first][j] << " )" << endl;
//统计K邻域的使用频率
map_label_freq[ labels[ vec_index_dis[i].first ] ]++;
} map<tLabel,int>::const_iterator map_it = map_label_freq.begin();
tLabel label; //保留频率最大的分类结果信息
int max_freq = ; //最大的使用频率
while( map_it != map_label_freq.end())
{
if( map_it->second > max_freq)
{
max_freq = map_it->second;
label = map_it->first;
}
map_it++;
}
cout << "The test data belongs to the : " << label << " label" << endl;
return label;
}
/************************************************************************/
/* 归一化处理 */
/************************************************************************/
void KNN::auto_norm_data()
{
tData maxa[COLNUM]; //
tData mina[COLNUM];
tData range[COLNUM];
int i,j;
//遍历训练数据,找出数据向量的各个极值,为后续归一化处理
for( i = ; i < COLNUM; i++ )
{
maxa[i] = max(dataSet[][i],dataSet[][i]);
mina[i] = min(dataSet[][i],dataSet[][i]);
} for( i = ; i < rowLen; i++ )
{
for(j = ; j < COLNUM; j++)
{
if( dataSet[i][j]>maxa[j] )
{
maxa[j] = dataSet[i][j];
}
else if( dataSet[i][j]<mina[j] )
{
mina[j] = dataSet[i][j];
}
}
} for( i = ; i < COLNUM; i++ )
{
range[i] = maxa[i] - mina[i] ;
//归一化测试数据
testData[i] = ( testData[i] - mina[i] )/range[i] ;
} //归一化训练数据的各个分量
for(i=;i<rowLen;i++)
{
for(j=;j<COLNUM;j++)
{
dataSet[i][j] = ( dataSet[i][j] - mina[j] )/range[j];
}
}
}
主程序调用
// KNNAlg.cpp : 定义控制台应用程序的入口点。
#include "stdafx.h"
#include <iostream>
#include "knnbody.h"
using namespace std; int _tmain(int argc, _TCHAR* argv[])
{ cout << "the KNN algothm is running ... " << endl;
//生成KNN算法对象
KNN knn = KNN();
//训练数据的预处理
knn.auto_norm_data();
//对测试样本进行分类操作以及进行错误率统计
knn.get_error_rate(); system("pause");
return ;
}
备注:以上代码主要是呈现KNN算法的大致结构,程序中有些许细节不尽完善,后续需要更改。
参考网址:
(1)http://baike.baidu.com/link?url=B_MWlciVjI4Oz2UJQaa09C3xkdkOHHH5OOg3uxE1UiMtG_P4Eq3dMlVQiRqqTqpASFZV8sk1jjiS2gmQDPvZ__ (2)http://blog.csdn.net/lavorange/article/details/16924705机器学习---K最近邻(k-Nearest Neighbour,KNN)分类算法的更多相关文章
- knn分类算法学习
K最近邻(k-Nearest Neighbor,KNN)分类算法,是一个理论上比较成熟的方法,也是最简单的机器学习算法之一.该方法的思路是:如果一个样本在特征空间中的k个最相似(即特征空间中最邻近)的 ...
- 后端程序员之路 12、K最近邻(k-Nearest Neighbour,KNN)分类算法
K最近邻(k-Nearest Neighbour,KNN)分类算法,是最简单的机器学习算法之一.由于KNN方法主要靠周围有限的邻近的样本,而不是靠判别类域的方法来确定所属类别的,因此对于类域的交叉或重 ...
- KNN分类算法
K邻近算法.K最近邻算法.KNN算法(k-Nearest Neighbour algorithm):是数据挖掘分类技术中最简单的方法之一 KNN的工作原理 所谓K最近邻,就是k个最近的邻居的意思,说的 ...
- KNN分类算法及python代码实现
KNN分类算法(先验数据中就有类别之分,未知的数据会被归类为之前类别中的某一类!) 1.KNN介绍 K最近邻(k-Nearest Neighbor,KNN)分类算法是最简单的机器学习算法. 机器学习, ...
- KNN分类算法实现手写数字识别
需求: 利用一个手写数字“先验数据”集,使用knn算法来实现对手写数字的自动识别: 先验数据(训练数据)集: ♦数据维度比较大,样本数比较多. ♦ 数据集包括数字0-9的手写体. ♦每个数字大约有20 ...
- KNN分类算法--python实现
一.kNN算法分析 K最近邻(k-Nearest Neighbor,KNN)分类算法可以说是最简单的机器学习算法了.它采用测量不同特征值之间的距离方法进行分类.它的思想很简单:如果一个样本在特征空间中 ...
- OpenCV——KNN分类算法 <摘>
KNN近邻分类法(k-Nearest Neighbor)是一个理论上比较成熟的方法,也是最简单的机器学习算法之一. 这个算法首先贮藏所有的训练样本,然后通过分析(包括选举,计算加权和等方式)一个新样本 ...
- 在Ignite中使用k-最近邻(k-NN)分类算法
在本系列前面的文章中,简单介绍了一下Ignite的线性回归算法,下面会尝试另一个机器学习算法,即k-最近邻(k-NN)分类.该算法基于对象k个最近邻中最常见的类来对对象进行分类,可用于确定类成员的关系 ...
- kNN分类算法实例1:用kNN改进约会网站的配对效果
目录 实战内容 用sklearn自带库实现kNN算法分类 将内含非数值型的txt文件转化为csv文件 用sns.lmplot绘图反映几个特征之间的关系 参考资料 @ 实战内容 海伦女士一直使用在线约会 ...
随机推荐
- DELPHI WM_CopyData 用法
unit Unit1; interface usesWindows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, ...
- scala -- 层级
层级 层级的顶端是Any 类,定义了如下方法 final def ==(that:Any):Boolean final def !=(that:Any):Boolean def equals(that ...
- 使用github的流程
使用github的流程 在实际项目开发中,按照如下步骤使用git进行代码管理 1.项目经理在开发之初,创建好仓库,上传项目的框架.组员分支 2.组员克隆项目框架,同步分支,按分工开发,在分支提交代码 ...
- selenium常用的断言
断言: 验证应用程序的状态是否同期望的一致,常见的断言包括验证页面内容,如标题是否与预期一致,当前的位置是否正确等等 断言常被用的4种模式+5种手段:Assert 断言失败的时候,该测试终止 veri ...
- go语言中make和new的区别
make用于内建类型(map.slice 和channel)的内存分配.new用于各种类型的内存分配. 内建函数new本质上说跟其他语言中的同名函数功能一样:new(T)分配了零值填充的T类型的内存空 ...
- Java的Reflection机制
什么时候使用Reflection: 在java语言中,创建一个类的对象通常使用new operator,但是如果预先不知道Class的名字,类名是在程序运行过程中通过参数传递过来,就没法使用这种方法了 ...
- preset
preset - 必应词典 美[.pri'set]英[.priː'set] v.预置:事先安排:预调:给…预定时间 网络预设:预先装置:预置位
- linux用户和组
1.用户隶属于用户组的. 2.用户与用户组配置文件 1)用户组配置文件 /etc/group 第一列:用户组的组名 第二列:组密码(真正的密码存储在了gshadow中) 第三列:用户组组ID,用户组唯 ...
- Spring框架的事务管理之声明式事务管理的类型
1. 声明式事务管理又分成两种方式 * 基于AspectJ的XML方式(重点掌握)(具体内容见“https://www.cnblogs.com/wyhluckdog/p/10137712.html”) ...
- Linux动态共享库
Linux操作系统上面的动态共享库大致分为三类: 一.操作系统级别的共享库和基础的系统工具库 libc.so, libz.so, libpthread.so等等,这些系统库会被放在/lib和/us ...