SVM支持向量机分类算法C++实现 零调库

代码中的alpha和有些教材中的lamda是同意义的值,在此记录说明

bits/stdc++.h是标准C和标准C++库的汇总库,不属于标准C/C++,但是省事。很多编译器是支持的,VS不支持

样本等均为随机生成

可以通过更改K函数修改核函数,此处为线性核

#include <bits/stdc++.h>
#define vd vector<double>
#define vvd vector<vd>
#define ull long long unsigned int
#define MY_RAND_MAX ((RAND_MAX<<15)|RAND_MAX)
using namespace std;

//#define DEBUG
//向量运算
template <class T>
T operator* (const vector<T>&AT,const vector<T>&B){
    T sum=0;
    for(ull i=0;i<AT.size()&&i<B.size();i++){
        sum+=AT[i]*B[i];
    }
    return sum;
}
template <class T>
vector<T> operator*(const vector<T>&A,T k){
    vector<T> kA(A);
    for(auto&x:kA)
        x*=k;
    return kA;
}

template <class T>
vector<T> operator*(T k,const vector<T>&A){
    vector<T> kA(A);
    for(auto&x:kA)
        x*=k;
    return kA;
}

//生成随机数
inline double my_rand(){
    return (rand()<<15)|rand();
}

inline double my_rand_double(){
    return my_rand() / double(MY_RAND_MAX);
}

inline double my_rand_double(double st,double ed){
    return my_rand_double()*(ed-st)+st;
}

//esp
const double esp=1e-7;
const double inf=DBL_MAX;
//α表 E表 K表
vd alpha,E;
vvd K;
//训练集
vvd x;
vd y;
//测试集
vvd x_test;
vd y_test;
//生成训练集样本的分类标准
double b;
vd omega;

//训练集样本容量与维度,测试集容量
int n,m;
int n_test;

//训练结果
double b_star=0;

//返回由alpha计算得到的omega_star
void omega_star(vd&res){
    res.clear();
    res.resize(m,0);
    for(int i=0;i<n;i++)if(alpha[i]>esp){
        for(int j=0;j<m;j++) {
            res[j]+=alpha[i]*y[i]*x[i][j];
        }
    }
}


//初始化K表
void init_K(){
    K.resize(n,vd(n));
    for(int i=0;i<n;i++)for(int j=0;j<n;j++){
        K[i][j]=x[i]*x[j];
    }
}

//g函数
double g(int i){
//E[i]=g(i)-y[i];
    return E[i]+y[i];
}

//更新E表
void set_E(){
    for(int i=0;i<n;i++){
        double res=b_star-y[i];
        for(int j=0;j<n;j++)if(alpha[j]>esp){
            if(abs(y[j]-1.0)<esp)
                res+=alpha[j]*K[i][j];
            else
                res-=alpha[j]*K[i][j];
        }
        E[i]=res;
    }
}

//一步更新
void step(int pos1,int pos2){
    double alpha_p1_new;
    double alpha_p2_new;
    double alpha_p1_old=alpha[pos1];
    double alpha_p2_old=alpha[pos2];

    double L,H;

    double K11=K[pos1][pos1];
    double K12=K[pos1][pos2];
    double K22=K[pos2][pos2];

    double y1=y[pos1];
    double y2=y[pos2];
    double E1=E[pos1];
    double E2=E[pos2];

    //计算L和H
    if(y1==y2){
        L=0;
        H=alpha_p1_old+alpha_p2_old;
    }
    else {
        L=max(0.0,alpha_p2_old-alpha_p1_old);
        H=inf;
    }

    //更新两个alpha
    alpha_p2_new=alpha_p2_old+y2*(E1-E2)/(K11+K22-K12-K12);

    if(alpha_p2_new>H)
        alpha_p2_new=H;
    else if(alpha_p2_new<L)
        alpha_p2_new=L;
    alpha_p1_new=alpha_p1_old+y1*y2*(alpha_p2_old-alpha_p2_new);

    alpha[pos1]=alpha_p1_new;
    alpha[pos2]=alpha_p2_new;

    //更新b_star
    double b_p1_new=-E1-y1*K11*(alpha_p1_new-alpha_p1_old)-y2*K12*(alpha_p2_new-alpha_p2_old)+b_star;
    double b_p2_new=-E2-y1*K12*(alpha_p1_new-alpha_p1_old)-y2*K22*(alpha_p2_new-alpha_p2_old)+b_star;
    b_star=(b_p1_new+b_p2_new)/2.0;

    //更新E
    set_E();

}

bool KKT(int i){
    if(abs(alpha[i])<=esp){
        if(y[i]==1)
            return g(i)>=1-esp;
        else
            return g(i)<=-1+esp;
    }
    else {
        return abs(y[i]-g(i))<=esp;
    }
}

bool test(int i,int j){
    return E[i]*E[j]<0;
    /*return abs(E[i]-E[j])>0.1;*/
}

void run(){
    bool flag=true;
    int ct=0;
    #ifdef DEBUG
    int loop = 0;
    while(flag){
        cout<<loop++<<endl;
    #else
    while(flag){
    #endif // DEBUG
        flag=false;
        for(int i=0;i<n;i++)if(!KKT(i)){
            for(int j=0;j<n;j++)if((!KKT(i)&&i!=j)&&((ct!=2&&test(i,j))||(alpha[j]>esp&&alpha[i]>esp))){
                flag=true;
                step(i,j);
#ifdef DEBUG
                cout<<"(i,j) = "<<'('<<i<<','<<j<<")\n";
                for(ull i=0;i<alpha.size();i++){
                    cout<<alpha[i]<<' ';
                }
                cout<<'\n';
                vd o_star;
                omega_star(o_star);
                cout<<o_star[0]<<' '<<o_star[1]<<' '<<b_star<<"\n";
                cout<<"E:\n";
                for(ull i=0;i<E.size();i++)
                    cout<<E[i]<<' ';
                cout<<"\n\n";
#endif // DEBUG
                int cntf0=0;
                for(int k=0; k<n; k++) if(alpha[k]>esp) cntf0++;
                if(cntf0>=n*0.2&&ct==0) ct=1;
                if(ct==1&&cntf0<=n*0.1) ct=2;

//                if(KKT(i))
//                    break;
            }
        }
    }
    #ifdef DEBUG
    cout<<"loop="<<loop<<endl;
    #endif
}

int main(){
#ifdef DEBUG
    //freopen("log.txt","w",stdout);
#endif
    srand(time(NULL));
    n=70;
    m=3;
    omega.resize(m);
    omega[0]=2;
    omega[1]=-0.5;
    omega[2]=-1.5;
    b=1;
    x.resize(n,vd(m));
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++)
            x[i][j]=my_rand_double(-10,10);
    }

    y.resize(n);
    for(int i=0;i<n;i++){
        double sum=0;
        for(int j=0;j<m;j++)
            sum+=x[i][j]*omega[j];
        if(sum+b>0)
            y[i]=1;
        else
            y[i]=-1;
    }

    init_K();
    alpha.resize(n);
    E.resize(n);

    set_E();

#ifdef DEBUG
    cout<<"train_set:"<<endl;
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++)
            cout<<x[i][j]<<' ';
        cout<<':';
        cout<<y[i]<<endl;
    }
    cout<<endl;
    cout<<"K:\n"<<endl;
    for(ull i=0;i<K.size();i++){
        for(ull j=0;j<K[i].size();j++)
            cout<<K[i][j]<<' ';
        cout<<endl;
    }

#endif //DEBUG





    run();

    cout<<"alpha:\n";
    for(ull i=0;i<alpha.size();i++){
        cout<<alpha[i]<<' ';
    }
    cout<<"\n\n";

    cout<<"ans:\n";
    vd o_star;
    omega_star(o_star);
    cout<<"ans_omega:\n";
    for(ull i=0;i<o_star.size();i++)
        cout<<o_star[i]<<' ';
    cout<<"\nb_star: "<<b_star<<"\n\n";



#ifdef DEBUG
    cout<<"E:\n";
    for(ull i=0;i<E.size();i++){
        cout<<E[i]<<' ';
    }
    cout<<"\n\n";

    cout<<"G:\n";
    for(int i=0;i<n;i++){
        cout<<g(i)<<' ';
    }
    cout<<"\n\n";
#endif //DEBUG

//测试集
    n_test=1000;
    x_test.resize(n_test,vd(m));
    for(int i=0;i<n_test;i++){
        for(int j=0;j<m;j++)
            x_test[i][j]=my_rand_double(-10,10);
    }
    y_test.resize(n_test);
    for(int i=0;i<n_test;i++){
        double sum=0;
        for(int j=0;j<m;j++)
            sum+=x_test[i][j]*omega[j];
        if(sum+b>0)
            y_test[i]=1;
        else
            y_test[i]=-1;
    }
    int t=0;
    int f=0;
    for(int i=0;i<n_test;i++){
        double sum=0;
        for(int j=0;j<m;j++)
            sum+=x_test[i][j]*o_star[j];
        double ans;
        if(sum+b_star>0)
            ans=1;
        else
            ans=-1;
        if(ans==y_test[i])
            t++;
        else {
            f++;
            cout<<"false: ";
            for(int j=0;j<m;j++)
                cout<<x_test[i][j]<<' ';
            cout<<endl;
        }
    }
    cout<<"number of true/false:"<<t<<"/"<<f<<endl;
}

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值