机器学习---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绘图反映几个特征之间的关系 参考资料 @ 实战内容 海伦女士一直使用在线约会 ...
随机推荐
- ssh 设置反向代理
远程主机上/etc/ssh/sshd_config中,开启 GatewayPorts yes systemctl reload sshd 本地: ssh -CqTnN -R 0.0.0.0:9000: ...
- Ansible Playbook Conditionals
通常,play的结果可能取决于变量的值,facts(有关远程系统的知识)或先前的任务结果. 在某些情况下,变量的值可能取决于其他变量. 此外,可以创建其他组,以根据主机是否与其他条件匹配来管理主机. ...
- MyBatis传入参数
在MyBatis的select.insert.update.delete这些元素中都提到了parameterType这个属性.MyBatis现在可以使用的parameterType有基本数据类型和Ja ...
- 双活部署前收集EMC存储设备信息
以0.68服务器为例 1.拷贝emcgrab_Linux_v4.7.10.tar到linux服务器并将其解压到/tmp目录下 tar -xvf emcgrab_Linux_v4.7.10.tar -C ...
- Zabbix安装(server和agent)及基本配置
简介 zabbix([`zæbiks])是一个基于WEB界面的提供分布式系统监视以及网络监视功能的企业级的开源解决方案. zabbix能监视各种网络参数,保证服务器系统的安全运营:并提供灵活的通知机制 ...
- 第七章 二叉搜索树(c)平衡与等价
- Python str() 函数
Python str() 函数 Python 内置函数 描述 str() 函数将对象转化为适于人阅读的形式. 语法 以下是 str() 方法的语法: class str(object='') 参数 ...
- Winform关于OpenFileDialog的使用方法
1.OpenFileDialog控件有以下基本属性InitialDirectory 对话框的初始目录Filter 要在对话框中显示的文件筛选器,例如,"文本文件(*.txt)|*.txt|所 ...
- java web 常用正则
什么是 RegExp? RegExp 是正则表达式(Regular expression)的缩写,作用是对字符串执行模式匹配. 通常用于格式验证.正则替换.查找子串等 各种编程语言的正则表达式基本相同 ...
- jquery datatables api
原文地址 学习可参考:http://www.guoxk.com/node/jquery-datatables http://yuemeiqing2008-163-com.iteye.com/blog/ ...