机器学习线性分类算法:感知器原理
感知器PLA是一种最简单,最基本的线性分类算法(二分类)。其前提是数据本身是线性可分的。
模型可以定义为
,sign函数是阶跃函数,阈值决定取0或1。
模型选择的策略,利用经验损失函数衡量算法性能,由于该算法最后得到一个分离超平面,所以损失函数可以定义为
,由于对于误分类点,yi和wx+b的正负属性相反,所以,所以加一个符号,来表征样例点与超平面的距离(此处不利用误分类点的个数的原因,由于不可导,不易优化)。
算法选择,最终的目标是求损失函数的最小值,利用机器学习中最常用的梯度下降GD或者随机梯度下降SGD来求解(相关优化算法的理解请自行百度)。
SGD算法的流程如下:输入训练集和学习率
1、初始化w0,b0,确定初始化超平面,并确定各样例点是否正确分类(利用yi和wx+b的正负性关系);
2、随机在误分类点中选择一个样例点,计算L关于w和b在该点处的梯度值;
3、更新w,b,按照如下方向
;
4、迭代运行,直到满足停止条件(限定迭代次数或者定义可接受误差最大值);
可知,初值的选择,误分类点的选择顺序都影响算法的性能和运行时间。PLA是一个很基本的算法,应用场景很受限,只是作为一个引子来了解机器学习,后面有很多高级的算法,比如SVM和MLP,以及大热的deep learning,都是感知器的扩展。
此外,根据novikoff定理,可得到一些结论:1、存在|w|=1的超平面可以将案例分开,且
;2、
表示误分类次数;此处w和x是扩展后的样例数据,将权值和1分别连接到原来的w和b后形成一个大向量。
对于PLA,还有一个对偶问题,此处,简单介绍一下对偶问题相关的知识。
对偶问题:
每一个线性规划问题,我们称之为原始问题,都有一个与之对应的线性规划问题我们称之为对偶问题。原始问题与对偶问题的解是对应的,得出一个问题的解,另一个问题的解也就得到了。并且原始问题与对偶问题在形式上存在很简单的对应关系:目标函数对原始问题是极大化,对对偶问题则是极小化
原始问题目标函数中的收益系数(优化函数中变量前面的系数)是对偶问题约束不等式中的右端常数,而原始问题约束不等式中的右端常数则是对偶问题中目标函数的收益系数
原始问题和对偶问题的约束不等式的符号方向相反
原始问题约束不等式系数矩阵转置后即为对偶问题的约束不等式的系数矩阵 原始问题的约束方程数对应于对偶问题的变量数,而原始问题的变量数对应于对偶问题的约束方程数
对偶问题的对偶问题是原始问题 总之他们存在着简单的矩阵转置,系数变换的关系。当问题通过对偶变换后经常会呈现许多便利,如约束条件变少、优化变量变少,使得问题的求解证明更加方便计算可能更加方便。
对偶问题中,此处将w和b看成是x和y的函数,w和b可表示为
,ni表示更新次数,模型
,算法流程如下:输入训练集,学习率
1、
;
2、随机选取误分类点对,并更新计算
,具体更新,依据上面的表达式;
3、直至没有误分类点,停止计算,返回相应的参数;
原始问题和对偶问题都是严格可收敛的,在线性可分的条件下,一定可以停止算法运行,会达到结果,存在多个解。
如果线性不可分,可以利用口袋算法,每次迭代更新错误最小的权值,且规定迭代次数。口袋算法基于贪心的思想。他总是让遇到的最好的线拿在自己的手上。。。 就是我首先手里有一条分割线wt,发现他在数据点(xn,yn)上面犯了错误,那我们就纠正这个分割线得到wt+1,我们然后让wt与wt+1遍历所有的数据,看哪条线犯的错误少。如果wt+1犯的错误少,那么就让wt+1替代wt,否则wt不变。 那怎样让算法停下来呢??——–我们就 自己规定迭代的次数 由于口袋算法得到的线越来越好(PLA就不一定了,PLA是最终结果最好,其他情况就说不准了),所以我们就 自己规定迭代的次数 。
以下是PLA简单的代码实现
#include<iostream>
#include <stdio.h>
using namespace std;
double hypothose(double w[],int feature_num,double* training_set){
double sum=0;
for(int i=0;i<feature_num;i++){
sum+=w[i]*training_set[i];
}
if (sum>0) return 1;
else return 0;
}
//以下函数为感知器算法真正函数,参数分别是特征个数,训练样本数,学习速率,迭代次数,训练样本,初始w数组
void perception(int feature_num,int training_num,double a,int times,double** training_set,double w[]){
int dimentions=feature_num+1;
while(times--){
double* delta_w=new double[feature_num];
for(int i=0;i<feature_num;i++){
delta_w[i]=0;
}
for(int i=0;i<training_num;i++){
for(int j=0;j<feature_num;j++){
delta_w[j]+=(training_set[i][feature_num]-hypothose(w,feature_num,training_set[i]))*training_set[i][j]*a;
}
}
for(int i=0;i<feature_num;i++){
w[i]+=delta_w[i];
}
delete[] delta_w;
}
}
int main(){
int feature_num,training_num,times;
double a;
freopen("in.txt","r",stdin);
while(cin>>feature_num>>training_num>>a>>times){
double** training_set=new double*[ training_num];
for(int i=0;i<training_num;i++){
training_set[i]=new double[training_num+2];
}
double* w=new double[feature_num+1];
for(int i=0;i<training_num;i++){
training_set[i][0]=1;
}
for(int i=0;i<training_num;i++){
for(int j=1;j<=feature_num+1;j++){
cin>>training_set[i][j];
}
}
for(int i=0;i<=feature_num;i++){
cin>>w[i];
}
perception(feature_num+1,training_num,a,times,training_set,w);
for(int i=0;i<feature_num;i++){
cout<<w[i]<<' ';
}
cout<<w[feature_num]<<endl;
delete[] w;
for(int i=0;i<training_num;i++){
delete[] training_set[i];
}
delete[] training_set;
}
return 0;
}
至此,第一个具体的算法PLA就已经总结完毕。
- 对于JSONObject,我只是临时抱佛脚
- 总结切面编程AOP的注解式开发和XML式开发
- SpringMVC注解@RequestMapping之produces属性导致的406错误
- SpringBoot集成MyBatis的分页插件PageHelper(回头草)
- SpringBoot整合Mybatis之进门篇
- Tomcat和Java Virtual Machine的性能调优总结
- 一次浴火重生的MySQL优化(EXPLAIN命令详解)
- 简单聊聊不可或缺的Nginx反向代理服务器--实现负载均衡【上篇】
- Java设计模式之适配器设计模式(项目升级案例)
- Java设计模式之模板方法设计模式(银行计息案例)
- 多线程之策略模式
- 文件上传的动作不能太俗,必须页面无刷新上传
- 这次真的忽略了一些ActiveMQ内心的娇艳
- 多线程编程:阻塞、并发队列的使用总结
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- aof数据的恢复和rdb数据在不同服务器之间的迁移
- redis的sort命令详解
- 推荐系统基础:使用PyTorch进行矩阵分解进行动漫的推荐
- go语言TCP编程入门
- 聊聊claudb的NotificationManager
- PowerBI RFM 4.0 - 第一篇 - 滚动连续评估法-业务解释
- [梁山好汉说IT] 如何理解脑裂问题
- DNS原理和CentOS7上bind域名服务器配置详解
- 前沿测试平台之mock接口实现及应用
- mysql远程连接错误。1044 解决
- 通过JSONP实现跨域访问
- Java编写http请求发送Json报文案例
- 详解 mysql int 类型的长度值问题
- 常见排序算法及golang 实现
- CentOS 8 安装Git及基本配置