C++基于特征向量的KNN分類算法
K最近鄰(k-Nearest Neighbor,KNN)分類算法,是一個理論上比較成熟的方法,也是最簡單的機器學習算法之一。該方法的思路是:如果一個樣本在特征空間中的k個最相似(即特征空間中最鄰近)的樣本中的大多數(shù)屬于某一個類別,則該樣本也屬于這個類別。KNN算法中,所選擇的鄰居都是已經(jīng)正確分類的對象。該方法在定類決策上只依據(jù)最鄰近的一個或者幾個樣本的類別來決定待分樣本所屬的類別。 KNN方法雖然從原理上也依賴于極限定理,但在類別決策時,只與極少量的相鄰樣本有關(guān)。由于KNN方法主要靠周圍有限的鄰近的樣本,而不是靠判別類域的方法來確定所屬類別的,因此對于類域的交叉或重疊較多的待分樣本集來說,KNN方法較其他方法更為適合。
#include "stdafx.h"
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include <set>
#include <map>
#include <cmath>
using namespace std;
//樣本特征結(jié)構(gòu)體
struct sample
{
string type;
vector<double> features;
};
//讀取訓(xùn)練樣本train.txt,訓(xùn)練樣本格式:類型名+特征向量
void readTrain(vector<sample>& train, const string& file)
{
ifstream fin(file.c_str()); //file是存儲希望讀寫的文件名的string對象,fin是讀的流
if(!fin)
{
cerr<<"Unable to open the input file: "<<file<<endl;
exit(1);
}
string line;
double d=0.0;
while(getline(fin,line)) //fin是讀入流,getline從輸入流fin讀入一行到line
{
istringstream stream(line); //bind to stream to the line we read
sample ts;
stream>>ts.type;
while(stream>>d) //read a word from line
{
ts.features.push_back(d); //在trains.features的末尾添加一個值為d的元素
}
train.push_back(ts); //在train的末尾添加一個值為ts的元素
}
fin.close();
}
//讀取測試樣本test.txt,每行都是一個特征向量
void readTest(vector<sample>& test, const string& file)
{
ifstream fin(file.c_str());
if(!fin)
{
cerr<<"Unable to open the input file: "<<file<<endl;
exit(1);
}
string line;
double d=0.0;
while(getline(fin,line))
{
istringstream stream(line); //bind to stream to the line we read
sample ts;
while(stream>>d)
{
ts.features.push_back(d);
}
test.push_back(ts);
}
fin.close();
}
//輸出結(jié)果,為每一個向量賦予一個類型,寫入result.txt中
void writeResult(const vector<sample>& test, const string& file)
{
ofstream fout(file.c_str());
if(!fout)
{
cerr<<"Unable to write the input file: "<<endl;
exit(1);
}
for(vector<sample>::size_type i=0;i!=test.size();++i)
{
fout << test[i].type << '\t';
for(vector<double>::size_type j=0;j!=test[j].features.size();++j)
{
fout<<test[i].features[j]<<' ';
}
fout<<endl;
}
}
//KNN算法的實現(xiàn)
void knnProcess(vector<sample>& test, const vector<sample>& train, const vector<vector<double> >& dm, unsigned int k)
{
for (vector<sample>::size_type i = 0; i != test.size(); ++i)
{
multimap<double, string> dts; //保存與測試樣本i距離最近的k個點
for (vector<double>::size_type j = 0; j != dm[i].size(); ++j)
{
if (dts.size() < k) //把前面k個插入dts中
{
dts.insert(make_pair(dm[i][j], train[j].type)); //插入時會自動排序,按dts中的double排序,最小的排在最后
}
else
{
multimap<double, string>::iterator it = dts.end();
--it;
if (dm[i][j] < it->first) //把當前測試樣本i到當前訓(xùn)練樣本之間的歐氏距離與dts中最小距離比較,若更小就更新dts
{
dts.erase(it);
dts.insert(make_pair(dm[i][j], train[j].type));
}
}
}
map<string, double> tds;
string type = "";
double weight = 0.0;
//下面for循環(huán)主要是求出與測試樣本i最鄰近的k個樣本點中大多數(shù)屬于的類別,即將其作為測試樣本點i的類別
for (multimap<double, string>::const_iterator cit = dts.begin(); cit != dts.end(); ++cit)
{
// 不考慮權(quán)重的情況,在 k 個樣例中只要出現(xiàn)就加 1
// ++tds[cit->second];
// 這里是考慮距離與權(quán)重的關(guān)系,距離越大權(quán)重越小
tds[cit->second] += 1.0 / cit->first;
if (tds[cit->second] > weight)
{
weight = tds[cit->second];
type = cit->second; //保存一下類別
}
}
test[i].type = type;
}
}
// 計算歐氏距離
double euclideanDistance(const vector<double>& v1, const vector<double>& v2)
{
if(v1.size() != v2.size())
{
cerr<<"Unable to get a distance! "<<endl;
}
else
{
double distance = 0.0;
for (vector<double>::size_type i = 0; i != v1.size(); ++i)
{
distance += (v1[i] - v2[i]) * (v1[i] - v2[i]);
}
return sqrt(distance);
}
}
/*初始化距離矩陣,該矩陣是根據(jù)訓(xùn)練樣本和測試樣本而得,
矩陣的行數(shù)為測試樣本的數(shù)目,列數(shù)為訓(xùn)練樣本的數(shù)目,
每一行為一個測試樣本到各個訓(xùn)練樣本之間的歐式距離組成的數(shù)組*/
void initDistanceMatrix(vector<vector<double> >& dm, const vector<sample>& train, const vector<sample>& test)
{
for (vector<sample>::size_type i = 0; i != test.size(); ++i)
{
vector<double> vd;
for (vector<sample>::size_type j = 0; j != train.size(); ++j)
{
vd.push_back(euclideanDistance(test[i].features, train[j].features));
}
dm.push_back(vd);
}
}
//封裝
void xfxKnn(const string& file1, const string& file2, const string& file3, int k)
{
vector<sample> train,test;
readTrain(train, file1.c_str());
readTest(test, file2.c_str());
vector< vector<double> > dm;
initDistanceMatrix(dm, train, test);
knnProcess(test, train, dm, k);
writeResult(test, file3.c_str());
}
// 測試
int main()
{
xfxKnn("train.txt", "test.txt", "result.txt", 5);
return 0;
}
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
vscode C++開發(fā)環(huán)境配置步驟詳解(教你如何用vscode編寫寫C++)
這篇文章主要介紹了vscode C++開發(fā)環(huán)境配置詳細教程(教你如何用vscode編寫寫C++),本文通過圖文并茂的形式給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-03-03
數(shù)據(jù)結(jié)構(gòu) 雙機調(diào)度問題的實例詳解
這篇文章主要介紹了數(shù)據(jù)結(jié)構(gòu) 雙機調(diào)度問題的實例詳解的相關(guān)資料,雙機調(diào)度問題,又稱獨立任務(wù)最優(yōu)調(diào)度:用兩臺處理機A和B處理n個作業(yè)的實例,需要的朋友可以參考下2017-08-08
C++實現(xiàn)LeetCode(103.二叉樹的之字形層序遍歷)
這篇文章主要介紹了C++實現(xiàn)LeetCode(103.二叉樹的之字形層序遍歷),本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下2021-07-07

