【ML学习笔记】19:原始的LDA线性判别式分析

简述LDA

LDA线性判别式分析也叫Fisher线性判别,其思路和感知机不同。感知机是根据自己的分类错误去调整决策线的位置,而Fisher线性判别的思想是把空间中的样本点向一个向量上投影,最终在这个向量确定的一维空间中就可以根据点的散布确定阈值,从而做线性判别了。
这里写图片描述

显然这个投影方向应尽可能让投影后的点类间相隔较远,而类内比较聚集,这样才能较好地分类。

原样本空间

如样本i的特征向量是xi:
这里写图片描述
其d个特征说明是在d维空间中的列向量,则要寻找的那个投影到的向量也是d维的:
这里写图片描述

在其上的投影,就是两个向量的内积(模乘模乘夹角余弦):
这里写图片描述
即上图中量段相乘,是一个标量:
这里写图片描述
w向量的模设为一个固定值时,这个标量的值就只和上图中红色段,也就是投影向量和特征向量的夹角有关了。所以这个值可以表征投影后的一维空间上样本的刻度

原来的d维样本特征空间中的类均值向量
这里写图片描述

类内离散度矩阵,即是协方差矩阵的求期望项之和来表征一个类内样本的离散程度:
这里写图片描述
这里之所以不用除以N,是因为它比较的不过是在不同投影向量下的类内离散程度,样本始终这么多,它们都除以N或者都不除以N,不影响比较。

则总的类内离散程度就是用总类内离散度矩阵描述(w是within的意思):
这里写图片描述
在比较总的类内离散程度时,各个类的离散度矩阵是否要除以自己的样本数是有影响的!之所以不除以样本数,是因为必须考虑到不同类的样本数目差异带来的影响。

而类间的离散程度用类间离散度矩阵描述(b是between的意思):
这里写图片描述

投影后的一维空间

一维空间中的类内均值也就成了标量:
这里写图片描述

类内离散度也是标量:
这里写图片描述

总类内离散度也是标量:
这里写图片描述

类间离散度也是标量:
这里写图片描述

如何寻优

投影到一维空间以后,为了类间离散度尽可能大,类内离散度尽可能小,可以使用它们的比值作为Fisher准则函数
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述

寻找使其最大的方式有很多(如拉格朗日乘子法),这里只列出结论:

这里写图片描述
是Fisher判别准则下的最优投影方向,也即未增广的权重向量

这里写图片描述
偏置 bais,不考虑先验概率不同,常取:
这里写图片描述

代码实现

#-*-coding:utf-8-*-
from numpy import *
import operator
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

#获取男女数据集
def getData(pathstr1,pathstr0):
    #fr1=open(r'myboy.txt')
    #fr0=open(r'mygirl.txt')
    fr1=open(pathstr1)
    fr0=open(pathstr0)
    arrayOLines1=fr1.readlines() #读取文件
    arrayOLines0=fr0.readlines()
    #特征矩阵
    dataMat1=[]
    dataMat0=[]
    #男同学
    for line in arrayOLines1:
        line=line.strip() #strip()去掉首尾空格
        listFromLine=line.split() #按空白字符分割成列表
        #string变float
        for i in range(len(listFromLine)):
            listFromLine[i]=float(listFromLine[i])
        #加入特征矩阵
        dataMat1.append(listFromLine)
    #女同学
    for line in arrayOLines0:
        line=line.strip() #strip()去掉首尾空格
        listFromLine=line.split() #按空白字符分割成列表
        #string变float
        for i in range(len(listFromLine)):
            listFromLine[i]=float(listFromLine[i])
        #加入特征矩阵
        dataMat0.append(listFromLine)
    return dataMat1,dataMat0


#求均值
def getUSgm(xMat):
    #行数1,列数xMat的列数
    u=zeros((1,xMat.shape[1]))
    u=mat(u)
    for vec in xMat:
        u+=vec
    u/=xMat.shape[0] #除以行数
    sgm=zeros((xMat.shape[1],xMat.shape[1]))
    sgm=mat(sgm)
    for vec in xMat:
        cha=vec-u
        sgm+=cha.T*cha
    return u,sgm

#演示
def Go():
    #获取训练集
    dataMat1,dataMat0=getData(r'.\boynew.txt',r'.\girlnew.txt')
    x1=[dataMat1[i][0] for i in range(len(dataMat1))]
    y1=[dataMat1[i][1] for i in range(len(dataMat1))]
    z1=[dataMat1[i][2] for i in range(len(dataMat1))]
    x0=[dataMat0[i][0] for i in range(len(dataMat0))]
    y0=[dataMat0[i][1] for i in range(len(dataMat0))]
    z0=[dataMat0[i][2] for i in range(len(dataMat0))]
    #绘制男女离散的点
    fig=plt.figure()
    ax=Axes3D(fig)
    ax.scatter(x1,y1,z1,c=u'b',label='male')
    ax.scatter(x0,y0,z0,c=u'g',label='female')
    #男女特征矩阵
    xMat1=mat(dataMat1)
    xMat0=mat(dataMat0)
    #均值u,各类离散度矩阵
    u1,sgm1=getUSgm(xMat1)
    u0,sgm0=getUSgm(xMat0)
    #总的类内离散度矩阵
    sw=sgm1+sgm0
    #类间离散度矩阵
    cha=u1-u0
    sb=cha.T*cha
    #权向量(乘完是一个列向量)
    w=sw.I*cha.T
    print '权向量:'
    print w
    u11=w.T*u1.T
    u00=w.T*u0.T
    w0=(-1.0/2)*(float(u11)+float(u00)) #偏置
    print '偏置:',w0
    #绘制决策面
    X=arange(140,210,10) #采样
    Y=arange(30,100,10)
    X,Y=meshgrid(X,Y) #笛卡尔积
    w1=float(w[0,0])
    w2=float(w[1,0])
    w3=float(w[2,0])
    Z=-(w0+w1*X+w2*Y)/w3
    ax.plot_surface(X,Y,Z,rstride=1,cstride=1,cmap='rainbow')
    #给三个坐标轴注明
    ax.set_xlabel('Height',color='r')
    ax.set_ylabel('Weight',color='g')
    ax.set_zlabel('Shoe Size',color='b')
    err,lnth=errTst(w0,w) #传入增广的权向量,以测试错误率
    print lnth,'次测试错误率是:',err/lnth
    plt.show() #显示


#测试,传入的w是列向量!
def errTst(w0,w):
    #获取测试集
    dataMat1,dataMat0=getData(r'.\boytst.txt',r'.\girltst.txt')
    errCount=0.0 #记录错误分类次数
    for line in dataMat1:
        xVec=mat(line) #转换成矩阵(向量)以方便运算
        if xVec*w+w0<0: #对男生而言,<0是错误分类
            errCount+=1
    for line in dataMat0:
        xVec=mat(line)
        if xVec*w+w0>0: #对女生而言,>0是错误分类
            errCount+=1
    #返回测试错误次数,总测试次数
    return errCount,len(dataMat1)+len(dataMat0)

测试

这里写图片描述

这里写图片描述

换个角度看下
这里写图片描述

2018年1月9日更新

低维的情况

这个是一开始做的只有2个特征而且线性可分的情况,可以和感知机得到的线比较一下(和感知机用的训练集相同)。

#-*-coding:utf-8-*-
from numpy import *
import operator
from matplotlib import pyplot as plt

#获取数据
def getData():
    fr1=open(r'myboy.txt')
    fr0=open(r'mygirl.txt')
    arrayOLines1=fr1.readlines() #读取文件
    arrayOLines0=fr0.readlines()
    #特征矩阵
    dataMat1=[]
    dataMat0=[]
    #男同学
    for line in arrayOLines1:
        line=line.strip() #strip()去掉首尾空格
        listFromLine=line.split() #按空白字符分割成列表
        #string变float
        for i in range(len(listFromLine)):
            listFromLine[i]=float(listFromLine[i])
        #加入特征矩阵
        dataMat1.append(listFromLine)
    #女同学
    for line in arrayOLines0:
        line=line.strip() #strip()去掉首尾空格
        listFromLine=line.split() #按空白字符分割成列表
        #string变float
        for i in range(len(listFromLine)):
            listFromLine[i]=float(listFromLine[i])
        #加入特征矩阵
        dataMat0.append(listFromLine)
    return dataMat1,dataMat0


#求均值
def getUSgm(xMat):
    #行数1,列数xMat的列数
    u=zeros((1,xMat.shape[1]))
    u=mat(u)
    for vec in xMat:
        u+=vec
    u/=xMat.shape[0] #除以行数
    sgm=zeros((xMat.shape[1],xMat.shape[1]))
    sgm=mat(sgm)
    for vec in xMat:
        cha=vec-u
        sgm+=cha.T*cha
    return u,sgm

#演示
def Go(i,j):
    dataMat1,dataMat0=getData()
    #i,j号特征在男女类各自的列表
    Ftr1i=[line[i] for line in dataMat1]
    Ftr1j=[line[j] for line in dataMat1]
    Ftr0i=[line[i] for line in dataMat0]
    Ftr0j=[line[j] for line in dataMat0]
    #绘制男女离散的点
    plt.scatter(Ftr1i,Ftr1j,c=u'b',label='male')
    plt.scatter(Ftr0i,Ftr0j,c=u'g',label='female')
    #男的特征矩阵
    xMat1=[[Ftr1i[k],Ftr1j[k]] for k in range(len(Ftr1i))]
    xMat1=mat(xMat1)
    #女的特征矩阵
    xMat0=[[Ftr0i[k],Ftr0j[k]] for k in range(len(Ftr0i))]
    xMat0=mat(xMat0)
    #均值u,各类离散度矩阵
    u1,sgm1=getUSgm(xMat1)
    u0,sgm0=getUSgm(xMat0)
    #总的类内离散度矩阵
    sw=sgm1+sgm0
    #类间离散度矩阵
    cha=u1-u0
    sb=cha.T*cha


    w=sw.I*cha.T #权向量(乘完是一个列向量)
    print w
    u11=w.T*u1.T
    u00=w.T*u0.T
    w0=(-1.0/2)*(float(u11)+float(u00)) #偏置
    print w0

    #绘制曲线用的横坐标(i特征)
    if i==0:
        lft=140
        rgt=210
    elif i==1:
        lft=40
        rgt=90
    else:
        lft=30
        rgt=47
    x1=linspace(lft,rgt,10) #反正是直线,不用采样太多
    #绘制得到的直线
    wi=float(w[0,0])
    wj=float(w[1,0])
    plt.plot(x1,-(wi*x1+w0)/wj,c=u'r')  
    plt.show()

这里写图片描述
这里写图片描述

代码重构

#-*-coding:utf-8-*-
from numpy import *
import operator
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

#获取男女数据集
def getData(pathstr1,pathstr0):
    #fr1=open(r'myboy.txt')
    #fr0=open(r'mygirl.txt')
    fr1=open(pathstr1)
    fr0=open(pathstr0)
    arrayOLines1=fr1.readlines() #读取文件
    arrayOLines0=fr0.readlines()
    #特征矩阵
    dataMat1=[]
    dataMat0=[]
    #男同学
    for line in arrayOLines1:
        line=line.strip() #strip()去掉首尾空格
        listFromLine=line.split() #按空白字符分割成列表
        #string变float
        for i in range(len(listFromLine)):
            listFromLine[i]=float(listFromLine[i])
        #加入特征矩阵
        dataMat1.append(listFromLine)
    #女同学
    for line in arrayOLines0:
        line=line.strip() #strip()去掉首尾空格
        listFromLine=line.split() #按空白字符分割成列表
        #string变float
        for i in range(len(listFromLine)):
            listFromLine[i]=float(listFromLine[i])
        #加入特征矩阵
        dataMat0.append(listFromLine)
    return dataMat1,dataMat0


#求均值
def getUSgm(xMat):
    #行数1,列数xMat的列数
    u=zeros((1,xMat.shape[1]))
    u=mat(u)
    for vec in xMat:
        u+=vec
    u/=xMat.shape[0] #除以行数
    sgm=zeros((xMat.shape[1],xMat.shape[1]))
    sgm=mat(sgm)
    for vec in xMat:
        cha=vec-u
        sgm+=cha.T*cha
    return u,sgm

#绘制分类面和散点图
def myFig(dataMat1,dataMat0,w0,w):
    x1=[dataMat1[i][0] for i in range(len(dataMat1))]
    y1=[dataMat1[i][1] for i in range(len(dataMat1))]
    z1=[dataMat1[i][2] for i in range(len(dataMat1))]
    x0=[dataMat0[i][0] for i in range(len(dataMat0))]
    y0=[dataMat0[i][1] for i in range(len(dataMat0))]
    z0=[dataMat0[i][2] for i in range(len(dataMat0))]
    #绘制男女离散的点
    fig=plt.figure()
    ax=Axes3D(fig)
    ax.scatter(x1,y1,z1,c=u'b',label='male')
    ax.scatter(x0,y0,z0,c=u'g',label='female')
    #绘制决策面
    X=arange(140,210,10) #采样
    Y=arange(30,100,10)
    X,Y=meshgrid(X,Y) #笛卡尔积
    w1=float(w[0,0])
    w2=float(w[1,0])
    w3=float(w[2,0])
    Z=-(w0+w1*X+w2*Y)/w3
    ax.plot_surface(X,Y,Z,rstride=1,cstride=1,cmap='rainbow')
    #给三个坐标轴注明
    ax.set_xlabel('Height',color='r')
    ax.set_ylabel('Weight',color='g')
    ax.set_zlabel('Shoe Size',color='b')
    plt.show() #显示

#训练(获取偏置和权向量)
def Train(dataMat1,dataMat0):
    #男女特征矩阵
    xMat1=mat(dataMat1)
    xMat0=mat(dataMat0)
    #均值u,各类离散度矩阵
    u1,sgm1=getUSgm(xMat1)
    u0,sgm0=getUSgm(xMat0)
    #总的类内离散度矩阵
    sw=sgm1+sgm0
    #类间离散度矩阵
    cha=u1-u0
    sb=cha.T*cha
    #计算权向量(乘完是一个列向量)
    w=sw.I*cha.T
    #计算偏置
    u11=w.T*u1.T
    u00=w.T*u0.T
    w0=(-1.0/2)*(float(u11)+float(u00))
    return w0,w

#演示入口
def Go():
    #获取训练集
    dataMat1,dataMat0=getData(r'.\boynew.txt',r'.\girlnew.txt')
    #训练
    w0,w=Train(dataMat1,dataMat0)
    print '权向量:'
    print w
    print '偏置:',w0
    err,lnth=errTst(w0,w) #传入增广的权向量,以测试错误率
    print lnth,'次测试错误率是:',err/lnth
    myFig(dataMat1,dataMat0,w0,w) #绘制散点图和分类面
    return w0,w


#测试,传入的w是列向量!
def errTst(w0,w):
    #获取测试集
    dataMat1,dataMat0=getData(r'.\boytst.txt',r'.\girltst.txt')
    errCount=0.0 #记录错误分类次数
    for line in dataMat1:
        xVec=mat(line) #转换成矩阵(向量)以方便运算
        if xVec*w+w0<0: #对男生而言,<0是错误分类
            errCount+=1
    for line in dataMat0:
        xVec=mat(line)
        if xVec*w+w0>0: #对女生而言,>0是错误分类
            errCount+=1
    #返回测试错误次数,总测试次数
    return errCount,len(dataMat1)+len(dataMat0)

#绘制ROC曲线
def getROC(w0,w):
    #绘制ROC曲线用的采样点
    X=[]
    Y=[]
    #分类面采样阈值从w0-0.1到w0+0.1,步长0.001
    #但range只能用int类型,所以乘以1000倍,传入时再除以1000
    for b in range(int(1000*w0-100),int(1000*w0+100),1):
            #获取假阳率和真阳率
            FPR,RECALL=getFprRecall(float(b)/1000,w)
            #分别加入到坐标采样列表中
            X.append(FPR)
            Y.append(RECALL)
    #绘制ROC曲线
    plt.plot(X,Y,"g-",linewidth=2)
    #横纵坐标名称,标题名称
    plt.xlabel('FPR')
    plt.ylabel('RECALL')
    plt.show()

#获取假阳性率FPR和召回率RECALL
def getFprRecall(w0,w):
    #获取测试集
    dataMat1,dataMat0=getData(r'.\boytst.txt',r'.\girltst.txt')
    errPstv=0.0 #记录假阳性样本数
    relPstv=0.0 #记录真阳性样本数
    for line in dataMat1:
        xVec=mat(line) #转换成矩阵(向量)以方便运算
        if xVec*w+w0>0: #对男生而言,>0正确分类是真阳性样本
            relPstv+=1
    for line in dataMat0:
        xVec=mat(line)
        if xVec*w+w0>0: #对女生而言,>0错误分类是假阳性样本
            errPstv+=1
    #print errPstv,relPstv
    return errPstv/len(dataMat0),relPstv/len(dataMat1)

#留一法获取交叉验证错误率
def getCrossValidationOnly1():
    print '正在进行留一法交叉验证...'
    #重获训练集
    dataMat1,dataMat0=getData(r'.\boynew.txt',r'.\girlnew.txt')
    '''
    #建立标签向量
    labelVec1=[1 for i in range(len(dataMat1))]
    labelVec0=[-1 for i in range(len(dataMat0))]
    #标签向量合并
    labelVec=labelVec1 #用男的
    labelVec.extend(labelVec0) #合并女的
    #训练集合并
    dataMat=dataMat1
    dataMat.extend(dataMat0)
    '''
    errCount=0.0 #错误分类次数
    #遍历作留一法交叉验证
    #发挥python特性,用切片合并的方式获取新的训练集子集
    #留的是男生
    for i in range(len(dataMat1)):
        subMat=dataMat1[0:i]
        subMat_End=dataMat1[i+1:]
        subMat.extend(subMat_End) #男生抽离第i个
        w0,w=Train(subMat,dataMat0) #训练
        xVec=mat(dataMat1[i])
        if xVec*w+w0<0: #对男生而言,<0是错误分类
            errCount+=1
    #留的是女生
    for i in range(len(dataMat0)):
        subMat=dataMat0[0:i]
        subMat_End=dataMat0[i+1:]
        subMat.extend(subMat_End) #女生抽离第i个
        w0,w=Train(dataMat1,subMat) #训练
        xVec=mat(dataMat0[i])
        if xVec*w+w0>0: #对女生而言,>0是错误分类
            errCount+=1
    lnth=len(dataMat1)+len(dataMat0)
    print lnth,'个样本的留一法交叉验证错误率:',errCount/lnth

这里写图片描述
(散点和分类面图没变,不贴了)
这里写图片描述
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值