线性判别分析LDA用于降维

一、原理

LDA线性判别分析,LDA是一种监督学习的降维技术,LDA的思想可以用一句话概括,就是“投影后类内方差最小,类间方差最大”。

二、实现

1、基于奇异值分解svd的LDA降维

import numpy as np
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis

if __name__ == '__main__':
    X_ = np.array([[-1, -1],
                   [-2, -1],
                   [-3, -2],
                   [1, 1],
                   [2, 1],
                   [3, 2]])
    y_ = np.array([1, 1, 1, 2, 2, 2])

    clf = LinearDiscriminantAnalysis(solver='svd')
    clf.fit(X_, y_)
    print(clf.transform(X_))
    # [[-1.732], [-1.732], [-3.464], [1.732], [1.732], [3.464]]  --- svd

分解后代码(python)

import numpy as np
from scipy import linalg

if __name__ == '__main__':
    print('Hello world!')
    X_ = np.array([[-1, -1],
                   [-2, -1],
                   [-3, -2],
                   [1, 1],
                   [2, 1],
                   [3, 2]])
    y_ = np.array([1, 1, 1, 2, 2, 2])

    # class mean
    classes, y = np.unique(y_, return_inverse=True)      # unique -- classes list, indexes
    cnt = np.bincount(y)                                 # cnt -- cnt for every class
    means = np.zeros(shape=(len(classes), X_.shape[1]))  # n_c * n_f
    np.add.at(means, y, X_)                              # every class data sum
    means /= cnt[:, None]                                # every class means

    n_c = len(classes)                                   # class number
    n_s = X_.shape[0]                                    # samples number
    priors = np.bincount(y) / float(len(y_))             # 1*n_c
    xbar = np.dot(priors, means)                         # 1*nc * nc*nf  =  1*nf   class-weight means

    # every class center simple
    Xc = []
    for idx, group in enumerate(classes):
        Xg = X_[y_ == group, :]
        Xc.append(Xg - means[idx])
    Xc = np.concatenate(Xc, axis=0)                # ns * nf   center simple

    std = Xc.std(axis=0)                           # 1*nf   every feature std
    std[std == 0] = 1.                             # ignore zero
    fac = 1. / (n_s - n_c)                         # fac
    X = np.sqrt(fac) * (Xc / std)                  # X standardization   ns * nf
    U, S, Vt = linalg.svd(X, full_matrices=False)  # svd ---- [1.67303261 0.44828774]  U*S*Vt=X
    rank = np.sum(S > 1e-4)                        # feature value > 0 cnt
    scaling = (Vt[:rank] / std).T / S[:rank]       # Vt/std/S ---- scale

    fac2 = np.sqrt((n_s * priors) * fac)           # [0.8660254 0.8660254]  1*nc
    X = np.dot((fac2*(means-xbar).T).T, scaling)   #
    _, S, Vt = linalg.svd(X, full_matrices=False)  # svd ---- [2.8284e+00 8.3677e-18]
    rank = np.sum(S > 1e-4 * S[0])                 # feature value > 0 cnt
    scaling_ = np.dot(scaling, Vt.T[:, :rank])     # Scale * Vt
    X_new = np.dot(X_-xbar, scaling_)

分解后代码(matlab)

clear all
clc

% 基于奇异值分解的LDA降维实现
X = [[-1, -1]; [-2, -1]; [-3, -2]; [1, 1]; [2, 1]; [3, 2]];
y = [1, 1, 1, 2, 2, 2];

labels = unique(y);               % 所有类别
nc = length(labels);              % 类别数量
[ns, nf] = size(X);               % 样本数 特征维度
mu = zeros(nc, nf);               % 每个类别的均值 nc*nf
p = zeros(1, nc);                 % 各类别样本所占比例
Xc = zeros(ns, nf);               % 各类去中心化的数据
ind = 1;                          % 下标索引
for i = 1:nc
    index = find(y==labels(i));   % 类别i的样本下标
    ni = length(index);           % 类别i的样本数
    Xi = X(index, :);             % 类别i的样本 n*n_f
    mui = mean(Xi);               % 类别i的样本均值
    mu(i, :) = mui;               % 类别i的均值
    mui = repmat(mui, ni, 1);     % 类别i的均值矩阵 ni*n_f
    Xi = Xi - mui;                % 去中心化  ni*n_f
    Xc(ind:ind+ni-1, :) = Xi;     % 各类去中心化的数据
    ind = ind + ni;               % 更新下标
    p(i) = ni / ns;               % 各类别样本所占比例
end

xbar = p * mu;                    % class-weight特征均值 1*nf
s = std(Xc, 1);                   % s=((1/n)*sum(xi-mu)^2)^(1/2)  1*nf
s(s==0) = 1;                      % ignore zero
fac = 1 / (ns - nc);              % factor
ss = repmat(s, ns, 1);            % ns*nf
Xs = sqrt(fac) * (Xc./ss);        % Xs  ns*nf
[U, S, V] = svd(Xs, 0);           % SVD   U*S*V'=Xs
V = V';                           % 转置
S = diag(S)';                     % 对角元素
d = sum(S>0.0001);                % 特征值>0的个数
V = V(1:d, :);                    % 前d行
ss = repmat(s, d, 1);             % d*nf
sss = repmat(S(1:d), nf, 1);      % nf*d
scale = (V ./ ss)' ./ sss;        % nc*nf

fac2 = sqrt(fac*ns*p);                  % 1*nc
fac2s = repmat(fac2, nf, 1);            % nf*nc  
xbars = repmat(xbar, nc, 1);            % nc*nf
fac3 = (fac2s .* (mu - xbars)')';       % nc*nf
Xt = fac3 * scale;                      % nc*nf

[U1, S1, V1] = svd(Xt, 0);
S1 = diag(S1)';                     % 对角元素
d1 = sum(S1>0.0001*S1(1));          % 特征值>0的个数
scale2 = scale * V1(:, 1:d1);       % nc*d1

xbars2 = repmat(xbar, ns, 1); 
Xnew = (X-xbars2) * scale2;

2、基于特征分解eig的LDA降维实现

import numpy as np
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis

if __name__ == '__main__':
    X_ = np.array([[-1, -1],
                   [-2, -1],
                   [-3, -2],
                   [1, 1],
                   [2, 1],
                   [3, 2]])
    y_ = np.array([1, 1, 1, 2, 2, 2])

    clf = LinearDiscriminantAnalysis(solver='eigen')
    clf.fit(X_, y_)
    print(clf.transform(X_))
    # [[2.121], [2.121], [4.243], [-2.121], [-2.121], [-4.243]] --- eigen

分解后代码(python)

import numpy as np
from scipy import linalg


def class_means_(X, y):
    classes, y = np.unique(y, return_inverse=True)
    cnt = np.bincount(y)
    means = np.zeros(shape=(len(classes), X.shape[1]))
    np.add.at(means, y, X)
    means /= cnt[:, None]
    return means


def cov_(X):
    X = np.asarray(X)
    covariance = np.cov(X.T, bias=True)  # by N-1 if bias=False;  by N if bias=True
    return covariance


def class_cov_(X, y, priors):
    cls = np.unique(y)
    cov = np.zeros(shape=(X.shape[1], X.shape[1]))
    for idx, group in enumerate(cls):
        Xg = X[y == group, :]
        cov += priors[idx] * np.atleast_2d(cov_(Xg))
    return cov


if __name__ == '__main__':
    print('Hello world!')
    X_ = np.array([[-1, -1],[-2, -1],[-3, -2],[1, 1],[2, 1],[3, 2]])
    y_ = np.array([1, 1, 1, 2, 2, 2])

    classes, y_t = np.unique(y_, return_inverse=True)
    n_c = len(classes)                          # class number
    n_s = X_.shape[0]                           # samples number
    n_f = X_.shape[1]                           # feature number
    max_components = min(n_c - 1, n_f)          # nax dim
    priors = np.bincount(y_t) / float(len(y_))  # 1*n_c

    means_ = class_means_(X_, y_)             # class means   n_c*n_f
    covariance_ = class_cov_(X_, y_, priors)  # class cov     n_f*nf

    Sw = covariance_  # within scatter    [[0.6667, 0.3333], [0.3333, 0.2222]]
    St = cov_(X_)     # total scatter
    Sb = St - Sw      # between scatter    [[4.0000, 2.6667], [2.6667, 1.7778]]

    evals, evecs = linalg.eigh(Sb, Sw)  # only used by Sym Matrix    Sb*v=lambda*Sw*v  v.T*Sb*v=lambda  v.T*Sw*v=1
    # [0, 8]  [[2.4495, 0], [-3.6742, -2.1213]]
    evals2, evecs2 = linalg.eig(Sb, Sw)  # any matrix    [0+0.j, 8+0.j]    [[-0.5547, 0], [-0.8321, 1]]

    evecs = evecs[:, np.argsort(evals)[::-1]]  # sort eigenvectors
    X_new = np.dot(X_, evecs)                  # X*V   n_s*n_f * n_f*n_f = n_s*n_f
    print(X_new[:, :max_components])           # decrease dim
    # [[2.121], [2.121], [4.243], [-2.121], [-2.121], [-4.243]]

分解后代码(matlab)

clear all
clc

% 基于特征分解的LDA降维实现
X = [[-1, -1]; [-2, -1]; [-3, -2]; [1, 1]; [2, 1]; [3, 2]];
y = [1, 1, 1, 2, 2, 2];

labels = unique(y);               % 所有类别
nc = length(labels);              % 类别数量
nf = size(X, 2);                  % 特征维度
ns = size(X, 1);                  % 样本数
m = mean(X);                      % 所有样本均值 1*nf
ms = repmat(m, ns, 1);            % ns*nf
Xc = X - ms;                      % 去中心
St = (1/ns)*(Xc'*Xc);             % 整体散度
Sw = zeros(nf, nf);               % 类内散度矩阵
Sb = zeros(nf, nf);               % 类间散度
for i = 1:nc
    ind = find(y==labels(i));      % 类别i的样本下标
    ni = length(ind);              % 类别i的样本数ni
    Xi = X(ind, :);                % 类别i的样本 ni*nf
    mi = mean(Xi);                 % 类别i的样本均值 1*nf
    mis = repmat(mi, ni, 1);       % 类别i的均值矩阵 ni*nf
    Xi = Xi - mis;                 % 去中心化 ni*nf
    Sigma = (1/ni)*(Xi'*Xi);       % 协方差矩阵 nf*nf
    Sw = Sw + (ni/ns)*Sigma;       % 类内散度,越小越好
    Beta = (ni/ns)*(mi-m)'*(mi-m); % 协方差矩阵 nf*nf
    Sb = Sb + Beta;                % 类间散度,越大越好
end
Sb2 = St - Sw;                     % 类间散度--等价Sb
[Vec, Val] = eig(Sb, Sw);          % 广义特征分解
index = nf:-1:1;                   % 逆序
Vec = Vec(:, index);               % 逆序
d = min(nc-1, nf);                 % 降维后的维度
R = X*Vec(:,1:d);                  % 降维后数据

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>