感知机原始形式C++实现

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

感知机的对偶形式请参考我的另一篇blog: 感知机对偶形式C++实现
1
2
3
4
5
6
7
感知机的原始形式如下:
8
9
下面直接上代码,此处我用的是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();
///****w、b、步长和训练集********
vector<int> data[1000];
vector<int> w;
int b=0;
int len=1;  ///步长
///****w、b、步长和训练集********
int n; //特征向量的维数
int N; //输入实例的个数
int k=0; //迭代次数
int main()
{
    freopen("in.txt","r",stdin);
    input_init();
    train();
    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);
            if(i==0&&j<n)
                w.push_back(0);
        }
    }
    cout<<endl;
    cout<<"迭代次数|"<<"误分类点"<<" w\t"<<"b\t"<<"w.x+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)){ //判断是否误分类
            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
            cout<<++k<<"\t"<<" x"<<i+1<<"    ";
            output();
            i = -1; ///切记是-1不是0
        }
    cout<<++k<<"\t"<<" 0 ";
    output();
}
bool judge(int i){
    int sum = 0;
    for(int j=0;j<n;j++)
        sum += w[j]*data[i][j]; //w内积xi
    return (data[i][n]*(sum+b)>0)? true:false;  //yi(w.xi+b)<=0
}
void output(){
    cout<<"\t";
    for(int i=0;i<n;i++){
        if(i==0) cout<<"("<<w[i];
        else if(i==n-1)cout<<","<<w[i]<<")\t"<<b<<"\t";
        else cout<<","<<w[i];
    }
    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;
}

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

bool judge(int i){
    int sum = 0;
    for(int j=0;j<n;j++)
        sum += w[j]*data[i][j]; //w内积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)){ //判断是否误分类
            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 = -1; ///切记是-1不是0
        }
}

归纳总结:

①判断误分类函数的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循环来执行。故此,更新复杂度也就比对偶形式高。

  • 7
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
感知机是二分类的线性分类模型,其输入为实例的特征向量,输出为实例的类别,取 +1 和 -1 二值。感知机对应于输入空间(特征空间)中将实例划分为正负两类的分离超平面,属于判别模型。 感知机学习算法是基于随机梯度下降法的。具体地,首先任意选取一个超平面,然后用梯度下降法不断地极小化目标函数,找出最优超平面。 以下是感知机算法的C++实现及案例代码: ```c++ #include <iostream> #include <vector> #include <random> using namespace std; class Perceptron { public: Perceptron(int feature_num) : w(feature_num), b(0) {} void train(const vector<vector<double>>& X, const vector<double>& y, int max_iter = 100) { int n_samples = X.size(); int n_features = X[0].size(); mt19937 rng(0); uniform_int_distribution<int> dist(0, n_samples - 1); for (int iter = 0; iter < max_iter; iter++) { int i = dist(rng); double wx = 0; for (int j = 0; j < n_features; j++) { wx += X[i][j] * w[j]; } double yi = y[i]; if (yi * (wx + b) <= 0) { for (int j = 0; j < n_features; j++) { w[j] += yi * X[i][j]; } b += yi; } } } double predict(const vector<double>& x) { double wx = 0; int n_features = x.size(); for (int i = 0; i < n_features; i++) { wx += x[i] * w[i]; } return wx + b > 0 ? 1 : -1; } void print_weights() const { cout << "w = ["; for (double wi : w) { cout << wi << ", "; } cout << "], b = " << b << endl; } private: vector<double> w; double b; }; int main() { vector<vector<double>> X{ {3, 3}, {4, 3}, {1, 1} }; vector<double> y{1, 1, -1}; Perceptron model(X[0].size()); model.train(X, y); model.print_weights(); cout << "predict([3, 4]) = " << model.predict({3, 4}) << endl; return 0; } ``` 在上述代码中,Perceptron类代表感知机模型。train函数接受训练数据X和y,以及最大迭代次数max_iter,默认为100。predict函数接受一个样本特征向量x,返回其预测的类别标签。print_weights函数打印训练后得到的权重和偏置项。 本例中,使用学习率为1的随机梯度下降法进行模型训练。训练数据X是一个3x2的矩阵,y是一个包含3个元素的向量,表示3个样本的类别标签。模型训练完毕后,使用predict函数对特定样本进行预测。 以上是感知机算法的C++实现及案例代码,希望对你有所帮助。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

SL_World

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

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

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

打赏作者

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

抵扣说明:

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

余额充值