MICD 分类器
实现代码:
MICD.h
#include <iostream>
#include <vector>
#include <valarray>
#include <assert.h>
#include <iomanip>
typedef long double real;
template<size_t N, size_t M> class mat;
template<size_t N>
class vec : public std::valarray<real> {//向量的定义
public:
vec() {
assert(N>0);
resize(N);
}
vec(const std::pair<real, real> &p) {
assert(N>0);
resize(N);
(*this)[0]=p.first;
if(N>1)
(*this)[1]=p.second;
}
real dot(const vec& rhs) const {//向量点积
assert(size()==rhs.size());
real res=0;
for(int i=size()-1; ~i; --i)
res+=(*this)[i]*rhs[i];
return res;
}
real len2() const {
return dot(*this);
}
real len() const {//向量模长
return sqrtl(len2());
}
friend std::ostream& operator << (std::ostream& out, const vec<N> &v) {//向量输出
out<<"("<<v[0];
for(int i=1; i<N; ++i)
out<<","<<v[i];
return out<<")";
}
operator mat<N, 1> () const {//向量转矩阵
mat<N, 1> m;
for(int i=0; i<N; ++i)
m[i][0]=(*this)[i];
return m;
}
};
template<size_t N, size_t M>
class mat : public std::array<vec<M> , N> {//矩阵的定义
protected:
public:
mat() {
}
mat operator + (const mat &rhs) {//矩阵加法
mat res;
for(int i=0; i<N; ++i)
for(int j=0; j<M; ++j)
res[i][j]=(*this)[i][j]+rhs[i][j];
return res;
}
mat operator - (const mat &rhs) {//矩阵减法
mat res;
for(int i=0; i<N; ++i)
for(int j=0; j<M; ++j)
res[i][j]=(*this)[i][j]-rhs[i][j];
return res;
}
mat<M, N> T() const {//矩阵转置
mat<M, N> res;
for(int i=0; i<N; ++i)
for(int j=0; j<M; ++j)
res[j][i]=(*this)[i][j];
return res;
}
friend std::ostream& operator << (std::ostream& out, const mat& m) {//矩阵输出
for(int i=0; i<N; ++i) {
out<<"\t";
for(int j=0; j<M; ++j)
out<<std::fixed<<std::setprecision(3)<<m[i][j]<<"\t";
out<<std::endl;
}
return out;
}
};
template<size_t N, size_t M, size_t P>
mat<N, P> operator * (const mat<N, M> &lhs, const mat<M, P> &rhs) {//矩阵乘法
mat<N, P> res;
for(int i=0; i<N; ++i)
for(int k=0; k<M; ++k)
for(int j=0; j<P; ++j)
res[i][j]+=lhs[i][k]*rhs[k][j];
return res;
}
template<size_t N>
mat<N, N> inv(mat<N, N> m) {//高斯消元求逆矩阵
mat<N, N> res;
for(int i=0; i<N; ++i)
res[i][i]=1;
real x;
for(int i=0; i<N; ++i) {
int pos=i;
for(int j=i+1; j<N&&fabs(m[j][i])>fabs(m[pos][i]); ++j)
pos=j;
swap(res[i], res[pos]);
swap(m[i], m[pos]);
x=m[i][i];
res[i]/=x;
m[i]/=x;
for(int j=0; j<N; ++j) if(j!=i) {
x = m[j][i];
res[j]-=x*res[i];
m[j]-=x*m[i];
}
}
return res;
}
template<size_t N>
class Class {//类的定义
protected:
std::vector<vec<N> > spec;
vec<N> sumvec;
vec<N> Mu;
mat<N, N> Cov, iCov;
public:
bool updated;//标记上次计算出 mu, cov, inv_cov 后是否发生更新
Class() {
updated=1;
}
void add(const vec<N>& obj) {//加入样本
spec.emplace_back(obj);
sumvec+=obj;
updated=1;
}
vec<N> mu() const {//求均值向量
if(updated==0)
return Mu;
vec<N> v=sumvec;
for(int i=0, x=spec.size(); i<N; ++i)
v[i]/=x;
return v;
}
mat<N, N> sigma() const {//求协方差矩阵
if(updated==0)
return Cov;
mat<N, N> sig;
mat<N, 1> tmp;
vec<N> m = mu(), v;
for(int i=0; i<spec.size(); ++i) {
v=spec[i];
v-=m;
tmp = mat<N, 1>(v);
sig = sig+tmp*tmp.T();
}
for(int i=0, x=spec.size()-1; i<N; ++i)
for(int j=0; j<N; ++j)
sig[i][j]/=x;
return sig;
}
void recalc() {
if(updated==0)
return ;
Mu = mu();
Cov = sigma();
iCov = inv(Cov);
updated = 0;
}
real dis(const vec<N>& obj) const {//求点到类的马氏距离
vec<N> tmp=mu();
tmp-=obj;
mat<N, 1> now=mat<N, 1>(tmp);
return (now.T()*inv(sigma())*now)[0][0];
}
friend std::ostream& operator << (std::ostream& out, const Class &c) {//类的输出
assert(c.updated==0);
out<<"\tsize = "<<c.spec.size()<<std::endl;
out<<"\tmu = "<<c.Mu<<std::endl;
out<<"\tsigma = "<<std::endl<<c.Cov<<std::endl;
out<<"\tinv_sigma = "<<std::endl<<c.iCov<<std::endl;
return out;
}
};
template<size_t M, size_t N>
class MICD {//MICD 分类器的定义
std::vector<Class<N> > c;
public:
MICD() {
assert(M>1);
c.resize(M);
}
void add(int id, const vec<N>& obj) {//往某个类加入样本
assert(id<M);
c[id].add(obj);
}
void recalc() {
for(int i=0; i<M; ++i)
c[i].recalc();
}
int classified(const vec<N>& obj) const {//识别特征向量所属类
int minid=0;
real minlen=c[0].dis(obj);
for(int i=0; i<M; ++i) {
real nowlen=c[i].dis(obj);
std::cout<<"distance with Class "<<i<<":\t"<<sqrtl(nowlen)<<std::endl;
if(nowlen<minlen) {
minlen=nowlen;
minid=i;
}
}
return minid;
}
friend std::ostream& operator << (std::ostream& out, const MICD &m) {//MICD 分类器的参数输出
for(int i=0; i<M; ++i)
out<<"Class "<<i<<":\n"<<m.c[i]<<"\n";
return out;
}
};
测试代码:
test.cpp
#include <iostream>
#include <map>
#include "MICD.h"
class Test {
MICD<2, 2> micd;
public:
Test() {
load();
micd.recalc();
std::cout<<micd<<std::endl;
}
int classified(const vec<2>& v) {
return micd.classified(v);
}
void load() {
std::map< std::pair<real, real> , int> train;
train[std::make_pair(1, 4)]=0;
train[std::make_pair(7, 0)]=0;
train[std::make_pair(3.5, 1)]=0;
train[std::make_pair(4.5, 3)]=0;
train[std::make_pair(4, 4)]=1;
train[std::make_pair(8, 5)]=1;
train[std::make_pair(8, 3)]=1;
train[std::make_pair(12, 4)]=1;
for(auto [v, t] : train)
micd.add(t, v);
}
}test;
int main() {
vec<2> v=std::pair<real, real>(4, 5);
int res = test.classified(v);
std::cout<<"Belong to Class "<<res<<std::endl;
return 0;
}
运行结果:
Class 0:
size = 4
mu = (4,2)
sigma =
6.167 -3.667
-3.667 3.333
inv_sigma =
0.469 0.516
0.516 0.867
Class 1:
size = 4
mu = (8.000,4.000)
sigma =
10.667 0.000
0.000 0.667
inv_sigma =
0.094 0.000
0.000 1.500
distance with Class 0: 2.794
distance with Class 1: 1.732
Belong to Class 1