线性判别分析_机器学习

前言:

      Linear Discriminant Analysis, 简称LDA。是一种子空间投影技术,目的是做分类,让投影后的向量

对分类任务有很好的区分度。

    这里要用到线性代数矩阵秩的三个基本性质

  R(A+B)\leqslant R(A)+R(B)

 R(AB)\leq min \begin{Bmatrix} R(A) ,& R(B) \end{Bmatrix}

目录:

  1.       用投影进行分类
  2.       投影矩阵一维
  3.       投影矩阵高维
  4.       例子

一   用投影进行分类

      基本思想是通过线性投影来   :

       最小化同类样本间的差异,

      最大化不同样本之间的差异。

 

     具体做法,寻找一个想低维度投影矩阵W,样本的特征向量x经过投影后得到新的向量

     y= W^Tx

   同一类样本投影后结果向量尽可能小,不同类投影后样本差异尽量大。

 

 

    


二  一维投影矩阵

     最后结果是一个标量

      假设 有n个样本,它们的特征向量x_i,属于两个不同类。

    类C_1,样本集为D_1,有n_1个样本

   类C_2,样本集为D_2,有n_2个样本

 

   有一个向量w,所有向量对该向量投影后得到一个标量

   y=w^Tx

 

  投影产生n个标量,分别属于C_1,C_2对应的集合Y_1,Y_2

我们希望投影后两个类内部的各样本差异最小化,类之间的差异最大化。

投影之前样本的均值为

   u_i =\frac{1}{n_i}\sum_{x \in D_i} x

 

投影之后的均值为

 u_i^*=\frac{1}{n}\sum_{x \in D_i}w^Tx

 

等价于样本均值在w上的投影,投影后两类样本的绝对值差为(类之间):

 |u_1^*-u_2^*|=|w^T(u_1-u_2)|

 

类间的差异可以用方差来衡量。定义类C_i的类内散布为(类间

S_i^2=\sum_{y \in Y_i}(y-u_i)^2

 

目标函数

   L(w)=\frac{(u_1^*-u_2^*)^2}{s_1^2+s_2^2}

 

 其中

     

          s_i^2=\sum_{x \in D_i}(w^Tx-w^Tm_i)^2

             =\sum_{x \in D_I}w^T(x-u_i)(x-u_i)^Tw

         =w^TS_iw

 

各类样本的均值差可以写为

   (u_1^*-u_2^*)^T=w^T(u_1-u_2)(u_1-u_2)^Tw

定义

  S_B=(u_1-u_2)(u_1-u_2)^T

则可以写成

   (u_1^*-u_2^*)=w^TS_Bw(u_1^*-u_2^*)^2=w^TS_Bw

   

则优化目标函数为求解最大值:

   L(w)=\frac{w^TS_Bw}{w^TS_ww}

        s.t    w^TS_w w=1

 

   构造拉格朗日函数

    L = w^TS_Bw+\lambda (w^TS_ww-1)

   对w 求偏导数:

       S_w^{-1}S_Bw=\lambda w

 

问题1: 特征值为什么只能取最大的那个

            max W^TS_BW

      =max_w W^T\lambda S_wW

       =max \lambda

       求解最大值等同求解最大特征值 

 

 问题2: 为什么特征值只能取一个,假设分类为K,最多取K-1个

            

 

    

算法流程:

  1.    对d维数据进行标准化处理(n为特征数量)
  2.   对于每一类别,计算n维的均值向量
  3.   构造类间的散布矩阵 S_B 以及 类内散布矩阵S_W
  4.   计算矩阵S_W^{-1}S_B的特征值以及对应的特征向量
  5.   选取最大特征值所对应的特征向量,构造一个 n∗1 维的转换矩阵 W,其中特征向量以列的形式排列
  6.   使用转换矩阵 W将样本映射到新的特征子空间上

分类:

    类似KNN 算法,因为有两个聚类中心,比较当前样本argmin_i|w^Tx-u_i^*|

   哪个更小,就是哪一类

   

 


  算法流程:

  1.    对d维数据进行标准化处理(n为特征数量)
  2.      对于每一类别,计算d维的均值向量,以及总的均值向量u
  3.      构造类间的散布矩阵 S_B 以及 类内散布矩阵S_W 

    S_B=\sum_{i=1}^{K}n_i(u_i-u)(u_i-u)^T        

    S_W=\sum_{i=1}^{K}S_i  

    S_i=\sum_{x\in D_i}(x-u_i)(x-u_i)^T

   u=\frac{1}{n}\sum_{i=1}^{n}x_i

  4  计算矩阵S_W^{-1}S_B的特征值以及对应的特征向量

  5  选取前c[k-1]个特征值所对应的特征向量,构造一个c∗d维的转换矩阵 W,其中特征向量以列的形式排列

  使用转换矩阵 W将样本映射到新的特征子空间上

   

   W=[w_1, w_2,...w_c] \in R^{d*c}

    W^Tx=\begin{bmatrix} w_1^Tx\\ w_2^Tx \\ .... \\ w_c^Tx \end{bmatrix}

 

 推导过程:

            max:    tr(W^TS_BW)    映射到低维后的类间散布矩阵

                       s.t : W^TS_WW=I_C

 

     拉格朗日对偶变换

       L = tr(W^TS_BW)+tr(\lambda(w^TS_WW-I_C))

     对w求偏导数,得到

    S_BW=\lambda S_WW

   S_W^{-1}S_BW=\lambda W

 

   其中S_B= \sum_{i=1}^{K}n_i(u_i-u)(u_i-u)^T

 

    其中R(u_i-u)=1 秩为1 ,根据性质

         R(A+B)\leqslant R(A)+R(B)

        R(S_B) \leq K

        又因为其中任意一项,可由K-1项线性表示

     所以  R(S_B)\leq K-1

 

     所以上式最多由K-1一个解,即降维后C最大为K-1

    

   取最大值依然如上:

    max : tr(W^TS_BW)

  = max: tr(W^T\lambda S_WW)

=max : tr(I_C\lambda)

= \lambda_1+\lambda_2+...\lambda_c  ,其中c<=K-1

      

 

     

四 例子

   一 乳房癌预测例子

        

# -*- coding: utf-8 -*-
"""
Created on Mon Oct 21 10:06:58 2019

@author: chengxf2
"""

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

#from sklearn.datasets import load_iris
from sklearn.preprocessing import StandardScaler
from sklearn.datasets  import load_breast_cancer
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis as LDA
import pickle


"""
二分类
"""

class LDAMe():


    """
    加载数据集
    Args
       None
    return 
       None
    """
    def LoadData(self):
        
        Breast = load_breast_cancer()
        data = Breast.data
        

        print("\n step1  数据标准化处理  \n")
        sc = StandardScaler()
        self.train_data = sc.fit_transform(data)
        self.train_label = Breast.target
        
        self.m, self.n = np.shape(self.train_data) ##2分类
        #print("\n m, ",self.m, "\t n",self.n)
        


    
        
    
    def __init__(self):
        
        self.m = 0
        self.n = 0
        self.newDim = 2 ##降维后的维度 
        self.U ={}
        self.LoadData()
       
 
  
        
        
    
    """
    获得均值向量
    Args
        None  
    return
        None 1行n列的均值
    """
    def GetMean(self):
        
        labelItem = [0,1] ##只有2类

        dictU ={}
        

            
        for label in labelItem:
            index = (self.train_label == label).ravel()##True False
            x = self.train_data[index,:]
            u = np.mean(x,axis=0)
            dictU[label]=u
            
        self.U[0]= dictU[0].reshape(1,self.n)
        self.U[1] = dictU[1].reshape(1,self.n)
      

    
    """
    总类内散布矩阵
    Args
       SW:
    return 
       S_W
    """
    def GetSW(self):
        
      
        S_W = np.zeros((self.n, self.n))
       
        
        for label in [0,1]:
            xu = np.zeros((self.n, self.n))

            for x in self.train_data[self.train_label == label]:
                x_u = x - self.U[label]
                xu+= x_u.T*x_u
            S_W+= xu
        
        return S_W



    """
    总类间散布矩阵
    Args
        None
    return
      None
    
    """
    def GetSB(self):
        u0 = self.U[0]
        u1 = self.U[1]
        
        u = u0-u1
        
        S_B = u.T*u
        
        return S_B
    
    
    

        

    """
    获取SB
    Args
        None
    return 
        Noe
    """
    def GetS(self):
        

        
        #Sw = None
        Sw = self.GetSW()
        SB = self.GetSB()
       
        self.S = np.linalg.inv(Sw)*SB
  
       
       


    def Train(self):
        
        print("\n step2: 计算每一类别的均值向量 ")
        self.GetMean()
        
        print("\n step3  构造散布矩阵SB, SW (n*n)")
        self.GetS()
        
        
        print("\n step4   计算矩阵的特征值以及对应的特征向量" )
        e,V = np.linalg.eig(self.S)
        
       # print("e ",e)
        
        print("\n step5  选取前k个特征值所对应的特征向量")
        
        W = np.zeros((self.n,1))
     
        
        index = np.argsort(-e)
        print("\n step6 使用转换矩阵 W将样本映射到新的特征子空间上")
       
        W[:,0]=V[:,index[0]]
       
        
            
        self.Perdict(W)
        

        
    """
    用w来预测
    Args
       W: 投影矩阵
    return 
       None
    """
    def Perdict(self, W):
        
        u0 = self.U[0]
        u1 = self.U[1]
        errornum = 0
        for  i in range(self.m):
            
            x = self.train_data[i]
            
            dist1 = np.dot((x-u0).reshape(1,30),W)[0,0]
            dist2 = np.dot((x-u1).reshape(1,30),W)[0,0]
            
            #print("dist1",dist1, "\n DIST2 ",dist2)
            if np.abs(dist1)<np.abs(dist2):
                perdict_label = 0
            else:
                perdict_label = 1
            label = self.train_label[i]
            
            if perdict_label != label:
                errornum = errornum+1
        error =100*(errornum/self.m)
        
        Result = '错误率 : {:.2%}'.format(error/100)
        
        print("\n ********************\n")
        print(Result)
            
            
        
  
       
         
        

class Data():

      def __init__(self, data, color):
          self.trainData = data
          self.color = color






def Test():
    x= np.array([[1,2,7],
                 [2,3,4],
                 [0,1,4]])
    
    u = np.mean(x,axis=0)
    print("u, ",u)
    x2 = x-u
    print("\n x2, ",x2)
   
    
    
#GeneData()
ld = LDAMe()
ld.Train()

预测结果

  

step1  数据标准化处理  


 step2: 计算每一类别的均值向量 

 step3  构造散布矩阵SB, SW (n*n)

 step4   计算矩阵的特征值以及对应的特征向量

 step5  选取前k个特征值所对应的特征向量

 step6 使用转换矩阵 W将样本映射到新的特征子空间上

 ********************

错误率 : 13.88%

 

 

例2 :

  数据集使用的是:

   https://www.jianshu.com/p/28da5c160230

 

 LDA 降维后效果

  

 

     数据集使用下面方式生成

  """
    生成数据
    Args
       None
    return
       数据
    """
    def GenereData(self):
        
        self.m = 600
        self.n = 2
        N = 300
        self.train_data= np.zeros((self.m, self.n))
        self.train_label= np.zeros(self.m)
        self.train_label[N:self.m]=1
        
        #print("\n self.train ",self.train_label)
        
        N = 300
        # 设椭圆中心center
        cx = 5
        cy = 6
        a = 1/8.0
        b = 4
        X,scale = 2*a*random_sample((N,))+cx-a,60
        Y = [2*b*np.sqrt(1.0-((xi-cx)/a)**2)*random_sample()+cy-b*np.sqrt(1.0-((xi-cx)/a)**2) for xi in X]
        
        self.train_data[0:N,0]= X
        self.train_data[0:N, 1] =Y
        
        #print("\n sp ", X,np.shape(Y))
        colors = ['green', 'green']*150
        fig, ax = plt.subplots()
        fig.set_size_inches(4, 6)
        ax.scatter(X, Y,c = "none",s=scale,alpha=1, edgecolors=['green']*N)
        
        X1,scale = 2*a*random_sample((N,))+cx-a,60
        Y1 = [2*b*np.sqrt(1.0-((xi-cx)/a)**2)*random_sample()+cy-b*np.sqrt(1.0-((xi-cx)/a)**2) for xi in X1]
        ax.scatter(X1+0.3, Y1,c = "none",s=scale,alpha=1, edgecolors=['red']*N)
        #plt.savefig('lda.png')
        plt.show()
        
        self.train_data[N:self.m, 0]= X1+0.3
        self.train_data[N:self.m, 1] =Y1

   

显示部分CODE
        newX = np.dot(self.train_data, W)
        
        data1 = newX[0:300]
        data2 = newX[300:self.m]
        
        plt.scatter(data1, [0]*data1.size, c='r')
        plt.scatter(data2, [0]*data2.size, c='g')

 

例二: 鸢尾花数据集 高维降维

       降维前

    

   降维后:

    

# -*- coding: utf-8 -*-
"""
Created on Wed Oct 23 15:50:16 2019

@author: chengxf2
"""

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

from sklearn.datasets import load_iris
from sklearn.preprocessing import StandardScaler
##https://www.cnblogs.com/yinheyi/p/6056314.html  颜色表

class LDAK:
    
    """
    绘制图形
    Args
       data: 数据集
       label: 标签
    return
        None
    """
    def Draw3D(self, data, label):
         fig = plt.figure()
         ax = fig.add_subplot(111, projection ='3d')
        
         x=  data[:,0]
         y = data[:,1]
         z = data[:,2]
        
         ax.scatter(x,y, z,c=label,cmap='cool')
         ax.set_xlabel("X ")
         ax.set_ylabel("Y ")
         ax.set_zlabel("Z ")
         ax.set_title("Scatter plot", alpha=0.6, color="b", size=25, weight='bold', backgroundcolor="y") 

         plt.show()
         
         
    """
    加载数据集
    Args
        None
    return
        None
    """
    def LoadData(self):
        
        Iris = load_iris()
        
        std = StandardScaler()
        data = std.fit_transform(Iris.data)
        self.trainData = data
        self.trainLabel = Iris.target
        
        self.n , self.d = np.shape(self.trainData)
        self.k = len(set(self.trainLabel))
        self.u = np.mean(self.trainData,axis=0)
        
        print("u:", self.u)
        print("\n self.k ",set(self.trainLabel))
        self.Draw3D(self.trainData, self.trainLabel)
        
        
       
    
    
    def __init__(self):
        
        self.n = 0 ##样本的个数
        self.d = 0 ##样本的维度 
        self.k = 0 ## 样本的总类
        self.LoadData()
        
    """
    降维
    Args
        None
    return
        None
    """
    def Reduce(self):
        
        print("\n step1  类内散布矩阵  SW  \n")
        SW = np.zeros((self.d, self.d))
        
        UList =[]
        NI =[]
        
        for label in range(self.k):
            
            index = (self.trainLabel == label).ravel()
           # print("\n index ",index )
            D_I = self.trainData[index]
            U_I = np.mean(D_I, axis=0)
            UList.append(U_I)
            NI.append(len(D_I))
            
            A= D_I-U_I
            
            S_I = np.dot(A.T,A)
            
            SW = SW+S_I
            
        print("\n U_I: \n ",SW)
            
           
        
        
        
        print("\n step2  计算SB  \n")
        
        
        SB = np.zeros((self.d, self.d))
        
        for i  in range(self.k):
            
         
            U_I = UList[i]
            B = U_I-self.u
            n = NI[i]
            
            B_I = n*np.dot(B.T, B)
            SB+=B_I
        print("\n SB:   \n", SB)
            
        
        
        
        print("\n step3  计算特征向量  \n")
        
        C = np.linalg.inv(SW)
        D = np.dot(C, SB)
        
        e,V = np.linalg.eig(D)
        print("\n a ",e)
        index = np.argsort(-e)  ##从大到小排序
        print("\n index:  ",index)
        
        W = np.zeros((self.d, self.k-1))
        
        for j in range(self.k-1) : ##取k-1个
        
             pos = index[j]
             W[:,j]=V[:,pos]
             
        print("\n W:   \n",W)
        
        newX = np.dot(self.trainData, W)
        x = newX[:,0]
        y = newX[:,1]
        Tip ="new Dim :%d"%(self.k-1)

        plt.title(Tip, fontsize=14)
        plt.scatter(x, y, c=self.trainLabel, cmap=plt.cm.cool)
        plt.xlabel("$z_1$", fontsize=18)
        plt.ylabel("$z_2$", fontsize=18)
        plt.grid(True)

        
        
        
        
        

ld = LDAK()
ld.Reduce()

         

参考文档

 《机器学习与应用》         

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值