关于fisher判别的一点理解

最近一个朋友问这方面的一些问题,其实之前也就很粗略的看了下fisher,真正帮别人解答问题的时候才知道原来自己也有很多东西不懂。下面小结下自己对fisher判别的理解:

其实fisher和PCA差不多,熟悉PCA的人都知道,PCA其实就是在寻找一个子空间。这个空间怎么来的呢,先求协方差矩阵,然后求这个协方差矩阵的特征空间(特征向量对应的空间),选取最大的特征值对应的特征向量组成特征子空间(比如说k个,相当于这个子空间有k维,每一维代表一个特征,这k个特征基本上可以涵盖90%以上的信息)。那么我们把样本投影在这个子空间,原来那么多维的信息就可以用这k维的信息代替了,也就是说降维了。至于PCA为啥要用求协方差矩阵然后求特征子空间的方法,这个数学上有证明,记得在某篇文章上看过,有兴趣的可以找找,看看证明。

那么fisher空间又是怎么一回事呢,其实fisher判别和PCA是在做类似的一件事,都是在找子空间。不同的是,PCA是找一个低维的子空间,样本投影在这个空间基本不丢失信息。而fisher是寻找这样的一个空间,样本投影在这个空间上,类内距离最小,类间距离最大。那么怎么求这个空间呢,类似于PCA,求最大特征值对应的特征向量组成的空间。

假设我们给定C类样本,我们先求sw(类内离散度)和sb(类间离散度),如下所示:

那么样本投影在新的投影空间中类间离散度为:

样本投影在新的投影空间中类内离散度为:

那么只要我们最大化就可以使得样本投影在这个空间上类内离散度最小,类间离散度最大,其中,w就是我们要找的投影方向。但是问题就来了,如果sw是一个奇异矩阵,那么这个式子是求不出来的。所以就有高人想到用这个式子代替:

这里的B是一个权衡因子,权衡类间距离和类内距离谁的比重大,试验中可以根据需要调节。也就是说,只要我们可以根据梯度下降法迭代w,使得最大化,这个w就是我们所要求的最好的投影方向。在fisher中我们去w为最大特征值对应的那个特征向量,也就是说w是一个nx1的向量。当然我们也可以取w为最大几个特征值对应的那几个特征向量,但是这样我们所求的就是一个矩阵,达不到求损失(一般是具体的数)函数的目的。

如果sw是非奇异矩阵,那么我们可以用更为简单的公式求解w,如下:

好了,下面来给出fisher的一个简单的matlab代码:

main.m

   1: clear; clc;
   2: %定义两类样本的空间范围 

   3: x1min=2;x1max=6; 

   4: y1min=-4,y1max=0; 

   5: x2min=6,x2max=10; 

   6: y2min=2,y2max=6; 

   7: %产生两类2D空间的样本 

   8: c1=createSwatch(x1min,x1max,y1min,y1max,100); 

   9: c2=createSwatch(x2min,x2max,y2min,y2max,80); 

  10: %获取最佳投影方向 

  11: w=fisher_w(c1,c2); 

  12: %计算将样本投影到最佳方向上以后的新坐标 

  13: cm1=c1(1,:)*w(1)+c1(2,:)*w(2); 

  14: cm2=c2(1,:)*w(1)+c2(2,:)*w(2); 

  15: cc1=[w(1)*cm1;w(2)*cm1]; 

  16: cc2=[w(1)*cm2;w(2)*cm2]; 

  17: %打开图形窗口 

  18: figure; 

  19: %绘制多图 

  20: hold on; 

  21: %绘制第一类的样本 

  22: plot(c1(1,:),c1(2,:),‘rp‘); 

  23: %绘制第二类的样本 

  24: plot(c2(1,:),c2(2,:),‘bp‘); 

  25: %绘制第一类样本投影到最佳方向上的点 

  26: plot(cc1(1,:),cc1(2,:),‘r+‘); 

  27: %绘制第二类样本投影到最佳方向上的点 

  28: plot(cc2(1,:),cc2(2,:),‘b+‘); 

  29: w=10*w; 

  30: %画出最佳方向 

  31: line([-w(1),w(1)],[-w(2),w(2)],‘color‘,‘k‘); 

  32: axis([-10,10,-10,10]); 

  33: grid on; 

  34: hold off; 

fisher_w.m

   1: function w = fisher_w(c1,c2) 
   2: %利用Fisher准则函数确定最佳投影方向 

   3: %c1和c2分别为两类样本的样本矩阵

   4: %得到样本矩阵的尺寸信息 

   5: %样本矩阵的行数代表样本的维数 

   6: %样本矩阵的列数代表样本的个数 

   7:     size1=size(c1); 

   8:     size2=size(c2); 

   9:     %计算两类样本的均值向量 

  10:     m1=sum(c1,2)/size1(2); 

  11:     m2=sum(c2,2)/size2(2); 

  12:     %样本向量减去均值向量 

  13:     c1=c1-m1(:,ones(1,size1(2))); 

  14:     c2=c2-m2(:,ones(1,size2(2))); 

  15:     %计算各类的类内离散度矩阵 

  16:     S1=c1*c1.‘; 

  17:     S2=c2*c2.‘; 

  18:     %计算总类内离散度矩阵 

  19:     Sw=S1+S2; 

  20:     %计算最佳投影方向向量 

  21:     w=Sw^-1*(m1-m2); 

  22:     %将向量长度变成1 

  23:     w=w/sqrt(sum(w.^2)); 

  24: end

createSwatch.m

   1: function swatch=createSwatch(xmin,xmax,ymin,ymax,num,varargin) 
   2:  

   3: xlen = abs(xmax - xmin);

   4: ylen = abs(ymax - ymin);

   5:  

   6: if numel(varargin)>0 && isa(varargin{1},‘function_handle‘) 

   7:     f = varargin{1}; 

   8: else

   9:     f = @rand;

  10: end 

  11: swatch=[xlen*f(1,num)+min(xmax,xmin);ylen*f(1,num)+min(ymax,ymin)]; 

  12:  

  13: end

  14:  

实验效果如下:

时间: 2024-10-11 23:01:41

关于fisher判别的一点理解的相关文章

线程的一点理解

一.线程起源 线程的产生基于通过共享公共的内存空间来交换数据可提高协作进程间的通信效率这一思想.线程是程序执行流的最小单元,是进程中的一个实体,一个标准的线程由线程ID.寄存器集合和堆栈组成.是被系统独立调度和分配的基本单位.线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源.一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行.由于线程之间的相互制约,致使线程在运行中呈现出间断性. 在某一时刻,进程内只有一个称为

我对直接插入排序的一点理解

今天复习排序,发现自己还是没什么长进,不过对于直接插入排序,还是有更深入的一点理解,这里留下点痕迹. 插入排序分为3类,即直接插入排序.二分排序以及希尔排序. 这里简单描述一下直接插入排序的思路.假如你现在是一名新生,早上全学院的学生去文体馆前集合,一个专业一列总队,而你去晚了,不知道自己该站哪,于是需要把自己插入到一个队列中去,这里模拟一下大脑的思路.因为你去晚了,所以你的同学都已经按照高矮顺序站好了,你从队尾开始比较,假如他比你低,ok,没他啥事了,如果他比你高,那他就得往后退,给你留下你站

Android measure和layout的一点理解

首先,推荐文章,http://blog.csdn.net/hqdoremi/article/details/9980481,http://www.docin.com/p-571954086.html 我理解measure的作用有2个:一个就是调用子view的measure函数,生成他们的高度和宽度,以便在自己的layout阶段参考:另一个是为父view提供关于自己的测量的width和height(这个宽度和高度往往和子view的测量高度和宽度有关),以便父view在layout阶段参考. lay

关于进程的一点理解

一.什么是进程? 进程包含存储在文件中的一组指令,该文件被读入内存并执行.正在执行的每个唯一的实例被称为进程,并且给它唯一一个标识,成为进程ID,它由操作系统确定.比如你在电脑中同时打开两个QQ,那么这两个程序就叫做进程,而且有两个不同的ID号. 二.进程如何创建? 被称为子进程的新进程由父进程的已存在的进程通过调用fork函数创建. pid=fork();//fork函数回传给pid的值是新进程的ID,数据类型为pid_t,属于int型. 子进程被创建为父进程的一个副本. fork函数在成功时

关于销售的一点理解

[关于销售的一点理解] 1.大多数人存在这样的偏见:销售人员和其他“中间人”很碍事,优良产品应该在生产出来后直接被神奇地分销出去. 这句话反着说,就正确了:销售人员和其他“中间人”很重要,优良产品应该在生产出来后由销售人员分销出去. 2.工程师喜欢制造东西,而不喜欢销售这些东西.但顾客并不会因为你生产了就来买.让顾客来买,并不像看起来那么简单. 3.广告之所以重要,是因为它确实有效.拒绝明显的推销术并不难,但广告不会立刻让你购买产品,而是为以后的购买埋下伏笔. 4.销售人员精心设计活动来改变表象

convertView&setTag方法的一点理解

前言 首先我们要知道setTag方法是干什么的,SDK解释为 Tags Unlike IDs, tags are not used to identify views. Tags are essentially an extra piece of information that can be associated with a view. They are most often used as a convenience to store data related to views in th

[转载] 对函数式编程的一点理解

原文: https://github.com/nixzhu/dev-blog/blob/master/2015-07-30-functional-programming.md 对函数式编程的一点理解 虽然有不少对“函数式编程”的解释,但我没有遇到让我满意的.我不是说解释要多么的具体.全面,或者让我立马会使用.变得很厉害,我就想知道它是怎么一回事儿. 作者:@nixzhu 先考虑一个过程.假设您性别男,爱好女.有位优雅的女士愿意与您做爱,不过这位女士对做爱有些要求:前戏至少要15分钟,性交要求在3

对JS回调函数的一点理解

之前写的异步JS是纯译文,现在刚好工作了2个月,想谈谈我自己对JS回调函数的一点理解,欢迎渴望大家的指正和交流. 回调函数从形式上看就是把函数b作为参数传给函数a,在a的函数体里调用函数b 1 function a(b) { 2 b(); 3 } 4 function b() { 5 ... 6 } 这样做的意义是什么呢 1.控制执行流程,函数b只能在函数a执行后才执行 2.根据不同的需求,可以有各种各样的函数b 3.最重要的,是可以把通过函数a获取的数据传递给函数b 1 function a(

关于web开发的一点理解

对于web开发上的一点理解 1 宏观上的一点理解 网页从请求第地址 到获得页面的过程:从客户端(浏览器)通过地址 从soket把请求报文封装发往服务端   服务端通过解析报文并处理报文最后把处理的结果 封装成响应报文 发送给客户端(浏览器)  ,客户端解析响应报文  把正文(html css  javascript)渲染成我们见到的页面. 这就是我们如何看到页面的过程 图解 2对于网页的开发的框架的一点理解 上面提到了网页处理的过程  中 服务端解析请求报文 处理请求报文的过程 就是网站开发的框