假设输入空间到输出空间的映射函数为:
其中:x为n维特征向量,w为n维权重向量,b为偏置,w·x 是w和x的内积。sign是符号函数:
那么对于误分类的数据来说:
该点到超平面的距离为:
所有误分类的点到超明面的距离和为:
其中,M为误分类点的集合。
不考虑,就得到了感知机学习的损失函数:
于是在给定训练数据集的情况下,原问题转化为求参数,使其为损失函数极小化问题的解:
其中,M为误分类点的集合。
感知机学习算法是误分类驱动的,具体采用随机梯度下降法。首先,任意选取一个超平面,然后用梯度下降法不断极小化目标函数(损失函数)。在极小化的过程中不是一次性使M中的所有误分类点的梯度下降,而是一次随机选取一个误分类点使其梯度下降。
假设误分类点M的集合是固定的,那么损失函数的梯度由以下给出
随机选取一个误分类点,对于
进行更新:
其中,是步长,在统计学习中也称为学习率。这样,通过迭代可以期待损失函数不断减小,直到为0。
C++不调库代码如下:
#include <bits/stdc++.h>
#define vi vector<int>
#define vd vector<double>
#define vvi vector<vi>
#define vvd vector<vd>
#define ull long long unsigned int
#define MY_RAND_MAX ((RAND_MAX<<15)|RAND_MAX)
using namespace std;
//随机数
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;
}
//向量运算
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;
}
template <class T>
vector<T>& operator += (vector<T>& res , const vector<T>& add){
for(int i=0;i<res.size()&&i<add.size();i++){
res[i]+=add[i];
}
return res;
}
//训练集数量和向量维度
int n,m;
//训练集
vvd x;
vd y;
//超平面S
double b;
vd omega;
//预测超平面S
double b_star;
vd omega_star;
//学习率
double eta=0.8;
//单步学习
void step(int pos){
omega_star += eta * y[pos] * x[pos];
b_star += eta * y[pos];
}
//核心函数
void run(){
bool flag=true;
while(flag){
flag=false;
for(int i=0;i<n;i++)if(y[i]*(omega_star*x[i]+b_star)<=0){
flag=true;
step(i);
}
}
}
int main()
{
//生成训练集
n=70;
m=3;
omega.resize(m);
omega[0]=2;
omega[1]=-0.5;
omega[2]=-1.5;
omega_star.resize(m);
b=1;
x.resize(n,vd(m));
y.resize(n);
for(int i=0;i<n;i++){
for(int j=0;j<m;j++)
x[i][j]=my_rand_double(-10,10);
}
for(int i=0;i<n;i++){
double sum=omega*x[i];
if(sum+b>0)
y[i]=1;
else
y[i]=-1;
}
//机器学习过程
run();
//输出训练结果
for(int i=0;i<n;i++){
for(int j=0;j<m;j++)
cout<<x[i][j]<<' ';
cout<<endl;
double sum=0;
sum=omega_star*x[i]+b_star;
cout<<y[i]<<' '<<sum<<endl;
}
return 0;
}