降维算法-LDA线性判别分析实例

内容简介

  • 线性判别分析LDA的基本概念
  • 代码实例:第一部分使用python详细说明了LDA的计算过程; 第二部分记录了如何使用sklearn完成LDA。

什么是线性判别分析?

LDA,全名 Linear Discrimination Analysis, 是一种由监督学习的降维算法
LDA关心的是能够最大化类间的区分度的坐标轴成分。降特征投影到一个维度更小的k维子空间中,同时保持区分类别的信息。

原理:投影到维度更低的空间,是的投影后的点,会形成按类别区分一簇一簇的情况,相同类别的点,将会在投影后的空间中更接近。

用人话说,就是怎么样最简单地将数据分类。如左边的图,红色和蓝色在原来的坐标轴内不能很好地区分;因此重新画了一条坐标轴(带点的那条斜线)这条线上可以看到,红点和蓝点分别在两边;根据新的坐标轴,将数据投影到维度更低的空间中,即右图, 两种点分成了两个簇。

降维算法-LDA线性判别分析实例_第1张图片
课程中老师还举了这个游戏作为例子:我们的数据集就跟图中的铁坨坨一样,长得乱七八糟的,投影就是找到更适合分类的空间,是的数据集的信息可以被提取和理解;与PCA更关心方差,而LDA更关心分类。
降维算法-LDA线性判别分析实例_第2张图片

LDA计算过程理解

(这里涉及很多公式,简单复述一下自己的理解,如果有错或者描述不清楚的地方,请评论告诉我一声)

LDA目标:投影后的两类样本中心点尽量分离,即使类间距离最大化,

使用类间距离存在的问题: 如下图情况,按X1方向可以最大化两中心点的距离,但是分的不好,可见红圈和蓝圈在x1轴有很大的重叠部分;X2轴,虽然距离小,但是分类效果更好。
降维算法-LDA线性判别分析实例_第3张图片
散列值:样本点的密集程度,值越大,越分散,反之越集中; 即使得类内距离(类别里面个点的距离)尽量小。

python代码实例

依旧使用鸢尾花数据集来练习, 鸢尾花数据集导入方法有很多种;直接下载文档后用read_csv();原来老师的代码是引用链接方法;个人觉得直接dataset 更方便,这里开头替换了一下取数的代码。在之前鸢尾花数据分析项目博客中有比较详细的解释这个方法,可以戳[链接]看看(https://blog.csdn.net/WHYbeHERE/article/details/106843722)

1.导入数据

#导入模块 
import numpy as np
import pandas as pd
from sklearn import datasets

iris = datasets.load_iris() #直接从datasets 导出数据
X, y = iris.data, iris.target #切分数据集

df = pd.DataFrame(np.hstack((X, y.reshape(-1, 1))),index = range(X.shape[0]),columns=['sepal_length_cm','sepal_width_cm','petal_length_cm','petal_width_cm','class'] )

df.tail()

降维算法-LDA线性判别分析实例_第4张图片

2.特征转换

from sklearn.preprocessing import LabelEncoder

#这里原本是的X和y的赋值;被注释掉了,因为我们在上一步就分好了X和y
#X = df[['sepal_length_cm','sepal_width_cm','petal_length_cm','petal_width_cm']].values
#y = df['class'].values

#y原本的值是三种花的名称:'Setosa','Versicolor','Virginica';需要将它们转换为电脑可以计算的数值。
enc = LabelEncoder()
label_encoder = enc.fit(y)
y = label_encoder.transform(y) + 1

#label_dict = {1: 'Setosa', 2: 'Versicolor', 3:'Virginica'}

降维算法-LDA线性判别分析实例_第5张图片

3. 求均值向量mi

接下来需要分别求三种鸢尾花数据在不同特征维度上的均值向量mi
降维算法-LDA线性判别分析实例_第6张图片

import numpy as np
np.set_printoptions(precision=4)

mean_vectors = []
for cl in range(1,4):
    mean_vectors.append(np.mean(X[y==cl], axis=0))
    print('Mean Vector class %s: %s\n' %(cl, mean_vectors[cl-1]))

结果输出:
降维算法-LDA线性判别分析实例_第7张图片
每一种花都有四个值,因为我们有四个特征。

4.计算类内散步矩阵和类间散步矩阵

降维算法-LDA线性判别分析实例_第8张图片

#用代码实现上面的公式
S_W = np.zeros((4,4))
for cl,mv in zip(range(1,4), mean_vectors):
    class_sc_mat = np.zeros((4,4))                  # scatter matrix for every class
    for row in X[y == cl]:
        row, mv = row.reshape(4,1), mv.reshape(4,1) # make column vectors
        class_sc_mat += (row-mv).dot((row-mv).T)
    S_W += class_sc_mat                             # sum class scatter matrices
print('within-class Scatter Matrix:\n', S_W)

降维算法-LDA线性判别分析实例_第9张图片
继续计算类间散布矩阵
降维算法-LDA线性判别分析实例_第10张图片

overall_mean = np.mean(X, axis=0)

S_B = np.zeros((4,4))
for i,mean_vec in enumerate(mean_vectors):  
    n = X[y==i+1,:].shape[0]
    mean_vec = mean_vec.reshape(4,1) # make column vector
    overall_mean = overall_mean.reshape(4,1) # make column vector
    S_B += n * (mean_vec - overall_mean).dot((mean_vec - overall_mean).T)

print('between-class Scatter Matrix:\n', S_B)

降维算法-LDA线性判别分析实例_第11张图片

5.求解矩阵的特征值:两个矩阵相乘

在这里插入图片描述

eig_vals, eig_vecs = np.linalg.eig(np.linalg.inv(S_W).dot(S_B))

for i in range(len(eig_vals)):
    eigvec_sc = eig_vecs[:,i].reshape(4,1)   
    print('\nEigenvector {}: \n{}'.format(i+1, eigvec_sc.real))
    print('Eigenvalue {:}: {:.2e}'.format(i+1, eig_vals[i].real))

结果得到四个特征值,对应四个特征向量
降维算法-LDA线性判别分析实例_第12张图片

6.特征值与特征向量:

  • 特征向量:表示映射方向
  • 特征值:特征向量的重要程度
#Make a list of (eigenvalue, eigenvector) tuples
eig_pairs = [(np.abs(eig_vals[i]), eig_vecs[:,i]) for i in range(len(eig_vals))]

# Sort the (eigenvalue, eigenvector) tuples from high to low
eig_pairs = sorted(eig_pairs, key=lambda k: k[0], reverse=True)

# Visually confirm that the list is correctly sorted by decreasing eigenvalues

print('Eigenvalues in decreasing order:\n')
for i in eig_pairs:
    print(i[0])

将特征值的结果排序:第一和第二个特征比较重要
降维算法-LDA线性判别分析实例_第13张图片

print('Variance explained:\n')
eigv_sum = sum(eig_vals)
for i,j in enumerate(eig_pairs):
    print('eigenvalue {0:}: {1:.2%}'.format(i+1, (j[0]/eigv_sum).real))

算一下百分比:结果也是选择前两个
降维算法-LDA线性判别分析实例_第14张图片

W = np.hstack((eig_pairs[0][1].reshape(4,1), eig_pairs[1][1].reshape(4,1)))
print('Matrix W:\n', W.real)

两个特征向量,所以组合一起是一个4*2的矩阵
降维算法-LDA线性判别分析实例_第15张图片

X_lda = X.dot(W)
assert X_lda.shape == (150,2), "The matrix is not 150x2 dimensional."
#画图
from matplotlib import pyplot as plt

def plot_step_lda():

    ax = plt.subplot(111)
    for label,marker,color in zip(
        range(1,4),('^', 's', 'o'),('blue', 'red', 'green')):

        plt.scatter(x=X_lda[:,0].real[y == label],
                y=X_lda[:,1].real[y == label],
                marker=marker,
                color=color,
                alpha=0.5,
                label=label
                )

    plt.xlabel('LD1')
    plt.ylabel('LD2')

    leg = plt.legend(loc='upper right', fancybox=True)
    leg.get_frame().set_alpha(0.5)
    plt.title('LDA: Iris projection onto the first 2 linear discriminants')

    # hide axis ticks
    plt.tick_params(axis="both", which="both", bottom="off", top="off",  
            labelbottom="on", left="off", right="off", labelleft="on")

    # remove axis spines
    ax.spines["top"].set_visible(False)  
    ax.spines["right"].set_visible(False)
    ax.spines["bottom"].set_visible(False)
    ax.spines["left"].set_visible(False)    

    plt.grid()
    plt.tight_layout
    plt.show()

plot_step_lda()

两个坐标轴 LD 1和LD2 意味着我们将数据降到了二维
降维算法-LDA线性判别分析实例_第16张图片

*直接使用sklearn中的LDA

from sklearn.discriminant_analysis import LinearDiscriminantAnalysis as LDA

# LDA
sklearn_lda = LDA(n_components=2)
X_lda_sklearn = sklearn_lda.fit_transform(X, y)

from matplotlib import pyplot as plt
def plot_scikit_lda(X, title):

    ax = plt.subplot(111)
    for label,marker,color in zip(
        range(1,4),('^', 's', 'o'),('blue', 'red', 'green')):

        plt.scatter(x=X[:,0][y == label],
                    y=X[:,1][y == label] * -1, # flip the figure
                    marker=marker,
                    color=color,
                    alpha=0.5,
                    label=label
                   )

    plt.xlabel('LD1')
    plt.ylabel('LD2')

    leg = plt.legend(loc='upper right', fancybox=True)
    leg.get_frame().set_alpha(0.5)
    plt.title(title)

    # hide axis ticks
    plt.tick_params(axis="both", which="both", bottom="off", top="off",  
            labelbottom="on", left="off", right="off", labelleft="on")

    # remove axis spines
    ax.spines["top"].set_visible(False)  
    ax.spines["right"].set_visible(False)
    ax.spines["bottom"].set_visible(False)
    ax.spines["left"].set_visible(False)    

    plt.grid()
    plt.tight_layout
    plt.show()


plot_step_lda()
plot_scikit_lda(X_lda_sklearn, title='Default LDA via scikit-learn')

结果输出:虽然用sklearn的结果倒了过来,但是 对比两个图可以看出其实两个方法的结果都是一样的。
降维算法-LDA线性判别分析实例_第17张图片
降维算法-LDA线性判别分析实例_第18张图片

你可能感兴趣的:(算法)