• LDA——线性判别分析基本推导与实验


    介绍与推导

      LDA是线性判别分析的英文缩写,该方法旨在通过将多维的特征映射到一维来进行类别判断。映射的方式是将数值化的样本特征与一个同维度的向量做内积,即:

    $y=w^Tx$

      因此,建立模型的目标就是找到一个最优的向量,使映射到一维后的不同类别的样本之间“距离”尽可能大,而同类别的样本之间“距离”尽可能小,使分类尽可能准确。

      具体来说,就是使映射后类内样本方差尽可能小,类间样本方差尽可能大。也就是(这里为二分类,多分类类似):

    $ egin{align*} &quad minlimits_w left[sumlimits_{xin X_0}(w^Tx-w^Tmu_0)^2+sumlimits_{xin X_1}(w^Tx-w^Tmu_1)^2 ight]\ &=minlimits_w w^T left[sumlimits_{xin X_0}(x-mu_0)(x-mu_0)^T+sumlimits_{xin X_1}(x-mu_1)(x-mu_1)^T ight]w \ &=minlimits_w w^TS_ww \ end{align*} $

      和

    $ egin{align*} &quad maxlimits_w left[(w^Tmu_0-frac{w^Tmu_0+w^Tmu_1}{2})^2+(w^Tmu_1-frac{w^Tmu_0+w^Tmu_1}{2})^2 ight]\ &=maxlimits_w frac{1}{2}w^T(mu_0-mu_1)(mu_0-mu_1)^Tw\ &=maxlimits_w frac{1}{2}w^TS_bw \ end{align*}  $

       因为自变量只有$w$,不一定二者都能同时达到最优,所以整合到一起取下式的最大值:

    $J = displaystyle frac{w^TS_bw}{w^TS_ww}$

       也就是:

    $ egin{align*} &minlimits_w -w^TS_bw\ & ext{s.t.}\,\, w^TS_ww = 1 end{align*}   $

       因为$S_w$正定,$S_b$半正定,所以使用拉格朗日乘子法(点击链接),最终得到:

    $w = S_w^{-1}(mu_0-mu_1)$

      其中$S_w^{-1}$是$S_w$的伪逆。

    实验

    西瓜数据集

      实验用数据集为西瓜数据集:  

      将数据填入Excel中后,在python中读取,然后使用处理好的数据计算出$w$,最后进行测试。

      各个样本点、映射平面以及映射后的样本点如下图所示:

      可以看到两类的样本点明显不是线性可分的,因此,不论如何选取一次的线性映射,都不可能将两类样本完全分开。而找到的映射平面将样本映射到一维后(即在右图的Z轴上),依然是很多不同类别的点穿插在一起。

      因此,判别训练集的正确率较低:

      仅0.7。

    线性数据集

      为了测试LDA在线性可分特征数据集上的性能,以二维正态分布生成如下样本点:

       其中蓝色点均值为$[1,5]$,红色为$[5,1]$;两类样本的协方差矩阵都为:

    $left[egin{matrix}1.4&1\1&5\end{matrix} ight]$

       映射图如下:

      判断结果如下:

      正确率提高到了0.99,可见LDA在线性可分数据上的性能还是不错的。

    实验代码

      LDA代码(数据输入data.xlsx中第一个表即可):

     1 #%%
     2 import matplotlib.pyplot as plt
     3 import numpy as np 
     4 import xlrd 
     5 
     6 table = xlrd.open_workbook('test.xlsx').sheets()[0]#读取Excel数据
     7 data = []
     8 for i in range(1,table.nrows):#假设第一行是表头不读入
     9     data.append(table.row_values(i))  
    10 class0 = []
    11 class1 = []
    12 #划分正反特征集,编号第一列,类别最后一列,特征在中间
    13 for i in data:
    14     if i[-1] == 0:
    15         class0.append(i[1:-1])
    16     else:
    17         class1.append(i[1:-1])
    18 data = np.array(data) #转为数字矩阵
    19 class0 = np.array(class0) #特征都是行向量,组成矩阵
    20 class1 = np.array(class1) 
    21 
    22 # %%
    23 #计算相应类别特征的平均
    24 n0 = len(class0)
    25 n1 = len(class1)
    26 miu0 = np.dot(np.ones([1,n0]),class0)/n0
    27 miu1 = np.dot(np.ones([1,n1]),class1)/n1 
    28 
    29 #%%
    30 #计算类内散度矩阵
    31 s0 = class0 - miu0  
    32 s1 = class1 - miu1
    33 Sw = np.dot(s0.transpose(),s0)+np.dot(s1.transpose(),s1)  
    34 W = np.dot(np.linalg.pinv(Sw),(miu0-miu1).transpose()) #计算W
    35 #输出W、miu0和miu1在映射后的值
    36 miu0_LDA = np.dot(miu0,W)
    37 miu1_LDA = np.dot(miu1,W)
    38 print("变换向量W:")
    39 print(W)
    40 print("0类的LDA均值:"+str(miu0_LDA[0,0]))
    41 print("1类的LDA均值:"+str(miu1_LDA[0,0]))
    42 
    43 #%%
    44 #判断类别
    45 c_discrim = np.dot(data[:,1:-1],W)  
    46 #统计正确率
    47 right = 0
    48 for i in range(len(data)):
    49     if np.abs(miu0_LDA[0,0] - c_discrim[i]) < np.abs(miu1_LDA[0,0] - c_discrim[i]):
    50         if data[i][-1] == 0:
    51             right +=1 
    52     else:
    53         if data[i][-1] == 1:
    54             right +=1 
    55 print("正确率:"+str(right / len(data)))
    56 
    57 #%%
    58 #画图(仅适用于二维特征) 
    59 ##################图一
    60 fig = plt.figure() 
    61 ax = fig.add_subplot(121,projection = '3d')  
    62 plt.xlabel("Feature 1")
    63 plt.ylabel("Feature 2") 
    64 ax.plot(class0[:,0],class0[:,1],'o',label = 'Class0',color = "red") #0类
    65 ax.plot([miu0[0,0]],[miu0[0,1]],'*',label = 'Class0 average',color = "black",markersize = 10) #0类平均
    66 ax.plot(class1[:,0],class1[:,1],'o',label = 'Class1',color = "blue") #1类  
    67 ax.plot([miu1[0,0]],[miu1[0,1]],'*',label = 'Class1 average',color = "green",markersize = 10) #1类平均  
    68 #映射平面
    69 t = np.linspace(-5,10,10) 
    70 X,Y = np.meshgrid(t,t)
    71 ax.plot_surface(X,Y,X*W[0]+Y*W[1],alpha = 0.5)
    72 ax.legend(loc = 'upper left')
    73 
    74 ##################图二
    75 ax = fig.add_subplot(122,projection = '3d')  
    76 plt.xlabel("Feature 1")
    77 plt.ylabel("Feature 2") 
    78 ax.plot(class0[:,0],class0[:,1],np.dot(class0,W)[:,0],'o',label = 'Mapping Class0',color = "red") #0类映射
    79 ax.plot([miu0[0,0]],[miu0[0,1]],np.dot(miu0[0],W),'*',label = 'Mapping class0 average',color = "black",markersize = 10) #0类平均映射
    80 ax.plot(class1[:,0],class1[:,1],np.dot(class1,W)[:,0],'o',label = 'Mapping Class1',color = "blue") #1类映射
    81 ax.plot([miu1[0,0]],[miu1[0,1]],np.dot(miu1[0],W),'*',label = 'Mapping class1 average',color = "green",markersize = 10) #1类平均映射
    82 ax.plot(np.zeros([len(class0)]),np.zeros([len(class0)]),np.dot(class0,W)[:,0],'o',color = "red",alpha = 0.5)  #0类映射值
    83 ax.plot(np.zeros([len(class1)]),np.zeros([len(class1)]),np.dot(class1,W)[:,0],'o',color = "blue",alpha = 0.5)   #1类映射值
    84 ax.plot([np.zeros([1])],np.zeros([1]),np.dot(miu0[0],W),'*',color = "black",alpha = 0.5,markersize = 10)  #0类平均映射值
    85 ax.plot([np.zeros([1])],np.zeros([1]),np.dot(miu1[0],W),'*',color = "green",alpha = 0.5,markersize = 10) #1类平均映射值
    86 #映射平面 
    87 X,Y = np.meshgrid(t,t)
    88 ax.plot_surface(X,Y,X*W[0]+Y*W[1],alpha = 0.5)
    89 ax.legend(loc = 'upper left')
    90  
    91 plt.show() 

      生成线性数据集代码:

     1 import openpyxl 
     2 import numpy as np
     3 import matplotlib.pyplot as plt
     4  
     5 sampleNum = 200
     6  
     7 # 二维正态分布
     8 mu = np.array([[1, 5]])
     9 Sigma = np.array([[1.4, 1], [1, 5]])
    10 s1 = np.dot(np.random.randn(sampleNum, 2), Sigma) + mu 
    11 plt.plot(s1[:,0],s1[:,1],'+',color='blue') 
    12 
    13 mu = np.array([[5, 1]]) 
    14 Sigma = np.array([[1.4, 1], [1, 5]])
    15 s2 = np.dot(np.random.randn(sampleNum, 2), Sigma) + mu 
    16 plt.plot(s2[:,0],s2[:,1],'+',color='red')
    17 plt.xlabel('Feature1')
    18 plt.ylabel('Feature2')
    19 
    20 plt.show()
    21 data = openpyxl.Workbook()
    22 table = data.create_sheet('test')
    23 table.cell(1,1,'id')
    24 table.cell(1,2,'feature1')
    25 table.cell(1,3,'feature2')
    26 table.cell(1,4,'class')
    27 for i in range(sampleNum):
    28     table.cell(i+2,1,i+1)
    29     table.cell(i+2,2,s1[i][0])
    30     table.cell(i+2,3,s1[i][1])
    31     table.cell(i+2,4,0)
    32 for i in range(sampleNum):
    33     table.cell(i+1+sampleNum,1,i+1)
    34     table.cell(i+1+sampleNum,2,s2[i][0])
    35     table.cell(i+1+sampleNum,3,s2[i][1])
    36     table.cell(i+1+sampleNum,4,1)
    37 data.remove(data['Sheet'])
    38 data.save('test.xlsx')
  • 相关阅读:
    【概念】构造函数和析构函数
    【概念】使用Fixed创建固定大小的缓冲区
    Sqoop
    Flume组件
    Hive节点及原理
    Yarn
    Hive数据倾斜
    单例
    工厂设计模式
    JVM对象创建
  • 原文地址:https://www.cnblogs.com/qizhou/p/12809010.html
Copyright © 2020-2023  润新知