感知机对偶形式C++实现

首先,分享以下我学习李航老师的《统计学习方法》中感知机对偶形式学习笔记,如有错误或者其他见解,恳请指正

感知机的原始形式请参考我的另一篇blog:《感知机原始形式C++实现》
1
2
3
4
下面直接上代码,此处我用的是C++代码用STL中的向量实现存储,当然也可以用数组或者其他方式,感知机的对偶形式代码如下:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <vector>
using namespace std;
void input_init();
void train();
bool judge(int i);
void output();
void gram();
void output_Gram();
void fun_output();
///****α、b、步长和训练集和Gram矩阵********
vector<int> Gram[1000];
vector<int> data[1000];
vector<int> a;
vector<int> w;
int b=0;
int len=1;  ///步长
///****α、b、步长和训练集和Gram矩阵********
int n; //特征向量的维数
int N; //输入实例的个数
int k=0; //迭代次数
int main()
{
    freopen("in.txt","r",stdin);
    input_init();
    train();
    fun_output();
    system("pause");
    return 0;
}
void input_init(){
    cout<<"请输入特征向量的维数:"<<endl;
    cin>>n;
    cout<<"请输入实例的个数:"<<endl;
    cin>>N;
    int tmp;
    for(int i=0;i<N;i++){
        cout<<"请输入第"<<i+1<<"个实例:"<<endl;
        for(int j=0;j<=n;j++){
            cin>>tmp;
            data[i].push_back(tmp);
        }
        a.push_back(0);  ///α表示第i个训练数据被误分类的次数
    }
    gram();
    output_Gram();
    cout<<endl<<"k\t"<<"data\t";
    for(int i=0;i<N;i++)
        cout<<"a"<<i+1<<"\t";
    cout<<"b"<<endl;
    cout<<"0"<<"\t \t"<<"0\t"<<"0\t"<<"0\t"<<endl;
}
void train(){
    for(int i=0;i<N;i++)
        if(!judge(i)){ //判断是否误分类
            a[i] += len;           //a<-a+n  此时步长len=1
            b += len*data[i][n];   //b<-b+n*yi
            cout<<++k<<"\t"<<" x"<<i+1<<"\t";
            output();
            i = -1; ///切记是-1不是0
        }
}
bool judge(int i){
    int sum = 0;
    for(int j=0;j<N;j++)   //原始形式中是for(int j=0;j<n;j++)
        if(a[j]==0) continue;
        else sum += a[j]*data[j][n]*Gram[j][i];  //sum = ∑aj*yj*xj*xi
    return (data[i][n]*(sum+b)>0)? true:false;  //yi(w.xi+b)<=0
}
void gram(){
    for(int i=0;i<N;i++)
        for(int j=0;j<N;j++){
            int sum = 0;
            for(int k=0;k<n;k++)  //n表示特征空间维数
                sum += data[i][k]*data[j][k];
            Gram[i].push_back(sum);
        }
}
void output_Gram(){
    cout<<endl<<"Gram matrix如下:"<<endl;
    for(int i=0;i<N;i++){
        for(int j=0;j<N;j++){
            cout<<Gram[i][j]<<"\t";
        }
        cout<<endl;
    }
}
void output(){
    for(int i=0;i<N;i++)
         cout<<a[i]<<"\t";
    cout<<b<<endl;
}
void fun_output(){
    cout<<endl<<"感知机对偶形式模型:";
    for(int i=0;i<n;i++){
        w.push_back(0);
        for(int j=0;j<N;j++)
            w[i] += a[j]*data[j][n]*data[j][i]; //w = ∑aj*yj*xj
    }
    for(int i=0;i<n;i++){
        if(w[i]>0){
            if(i!=0) cout<<"+";
        }
        else if(w[i]<0)
            cout<<"-";
        else continue;
        if(fabs(w[i])!=1.0)
            cout<<abs(w[i]);
        cout<<"x"<<i+1;
    }
    if(b!=0){
        if(b>=0)
            cout<<"+";
        cout<<b;
    }
    cout<<endl<<endl;
}

此处我输入的文件in.txt内容如下:
5
运行结果如下:
6
以上核心代码其实只有judge()train()gram(),去掉冗余输入输出后如下:

bool judge(int i){
    int sum = 0;
    for(int j=0;j<N;j++)   //原始形式中是for(int j=0;j<n;j++)
        if(a[j]==0) continue;
        else sum += a[j]*data[j][n]*Gram[j][i];  //sum = ∑aj*yj*xj*xi
    return (data[i][n]*(sum+b)>0)? true:false;  //yi(w.xi+b)<=0
}
void train(){
    for(int i=0;i<N;i++)
        if(!judge(i)){ //判断是否误分类
            a[i] += len;           //a<-a+n  此时步长len=1
            b += len*data[i][n];   //b<-b+n*yi
            i = -1; ///切记是-1不是0
        }
}
void gram(){
    for(int i=0;i<N;i++)
        for(int j=0;j<N;j++){
            int sum = 0;
            for(int k=0;k<n;k++)  //n表示特征空间维数
                sum += data[i][k]*data[j][k];
            Gram[i].push_back(sum);
        }
}

归纳总结:

①判断误分类函数的for循环函数复杂度不同:

**对偶形式:**判断是否误分类函数judge()for循环是:

for(int j=0;j<N;j++)   //原始形式中是for(int j=0;j<n;j++)
        if(a[j]==0) continue;
        else sum += a[j]*data[j][n]*Gram[j][i];  //sum = ∑aj*yj*xj*xi

**原始形式:**判断是否误分类函数judge()for循环是:

for(int j=0;j<n;j++)
        sum += w[j]*data[i][j]; //w内积xi

由此可以看出对偶形式是[0,N-1],原始形式是[0,n-1],当n(特征向量维数)>>N(输入实例个数)时,对偶形式循环复杂度较低,且对偶形式在训练数据之前先对训练数据求Gram矩阵,使得在此处本需要求xj*xi内积时,直接调用Gram矩阵中的值即可,使得运算量降低

②更新系数的方式不同:

**对偶形式:**训练数据的函数train()更新系数的代码:

a[i] += len;           //a<-a+n  此时步长len=1
b += len*data[i][n];   //b<-b+n*yi

**原始形式:**训练数据的函数train()更新系数的代码:

for(int j=0;j<n;j++)
   w[j] += len*data[i][n]*data[i][j];  //w<-w+n*yi*xi
b += len*data[i][n];                   //b<-b+n*yi

由此可以看出对偶形式αi的更新是αi <- αi + len;而原始形式w的更新是w <- w + len*yi*xi,其中αi一个值,代表第i个实例被误分类的次数(或称为被误分类而更新的次数),而w一个n维的向量,所以更新会比αi多需要一个for循环来执行。故此,更新复杂度也就比对偶形式高。

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SL_World

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值