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
posted @ 2022-04-30 23:12  JustinRochester  阅读(106)  评论(0编辑  收藏  举报