Wednesday, September 21, 2022

Linear Discriminant Analysis

 Линейный дискриминантный анализ (обычно сокращенно LDA, и его не следует путать с другим LDA) является очень распространенным методом уменьшения размерности для задач классификации. Однако это немного преуменьшение: он делает гораздо больше, чем «просто» уменьшение размерности.

Проще говоря, если у вас есть многомерные данные (т. е. большое количество признаков), по которым вы хотите классифицировать наблюдения, LDA поможет вам преобразовать ваши данные, чтобы сделать классы как можно более разными. Более строго, LDA найдет линейную проекцию ваших данных в подпространство более низкого измерения, которое оптимизирует некоторую меру разделения классов. Размерность этого подпространства обязательно строго меньше числа классов.

Это свойство LDA максимизировать разделение делает его настолько хорошим в своей работе, что иногда его считают классификационным алгоритмом сам по себе, что приводит к некоторой путанице. Линейный дискриминантный анализ — это форма уменьшения размерности, но с несколькими дополнительными предположениями его можно превратить в классификатор. (Отказ от этих предположений дает относительный, квадратичный дискриминантный анализ ) 

LDA как алгоритм: что он делает и как он это делает?

LDA как теорема: математический вывод LDA

LDA как метод машинного обучения.

Постановка задачи

Прежде чем мы углубимся в LDA, полезно получить интуитивное представление о том, чего пытается достичь LDA.Предположим, что: У вас многомерные данные, и Вы имеете дело с проблемой классификации. Это может означать, что количество объектов больше, чем количество наблюдений, или это может означать, что вы подозреваете наличие зашумленных объектов, которые содержат мало информации, или что-то среднее между ними.

Вы хотите выполнить две вещи:

1.Уменьшите количество функций (т.е. уменьшите размерность вашего пространства функций) и

2.Сохранить (или даже увеличьте!) «различимость» ваших классов или «отделенность» классов в вашем пространстве признаков.

Сначала несколько определений:

n - количество классов

μ — среднее значение всех наблюдений

Ni — количество наблюдений в i-м классе

µi — среднее значение i-го класса

Σi — матрица рассеяния i-го класса

Матрица рассеяния внутри класса SW









В качестве альтернативы можно использовать ковариационные матрицы классов путем добавления коэффициента масштабирования к матрице рассеяния внутри класса 1/(N-1) внутри класса






определим SB как матрицу рассеяния между классами, заданную формулой





Диагонализируйте SW^(-1)*SB, чтобы получить его собственные значения и собственные векторы cледующим образом:

Вычислите d-мерные средние векторы для различных классов из набора данных. Вычислите матрицы рассеяния (межклассовые и внутриклассовые матрицы рассеяния).

Вычислите собственные векторы (e1,e2,...,ed) и соответствующие собственные значения (λ1,λ2,...,λd) для матриц рассеяния.

Отсортируйте собственные векторы по убыванию собственных значений и выберите k собственных векторов с наибольшими собственными значениями, чтобы сформировать матрицу размерности d × k: W (где каждый столбец представляет собственный вектор).

Используйте эту матрицу собственных векторов d×k, чтобы преобразовать выборки в новое подпространство. Это можно резюмировать путем умножения матриц: Y=X*W (где X - это n×d-мерная матрица, представляющая n отсчетов, а Y - преобразованные n×k-мерные отсчеты в новом подпространстве)

W позволит нам преобразовать наши наблюдения в новое подпространство с помощью уравнения y=ATx, где y — наше преобразованное наблюдение, а x — наше исходное наблюдение.

Есть еще одна особенность LDA, о которой очень важно знать. Предположим, у вас есть 10 классов и вы запускаете LDA. Получается, что максимальное количество функций, которые может дать вам LDA, на единицу меньше, чем количество классов, то есть в данном случае 9!

Утверждение: 

SW^(−1)*SB имеет не более n−1 ненулевых собственных значений, из чего следует, что LDA должен уменьшить размерность по крайней мере до n−1.

=====================================

Алгоритм выше реализованный на Пайтон

=====================================

Линейный дискриминантный анализ можно разбить на следующие этапы:

1.Вычислите матрицы рассеяния внутри класса и между классами

2.Вычислите собственные векторы и соответствующие собственные значения для матриц рассеяния

3.Отсортируйте собственные значения и выберите лучший k

4.Создайте новую матрицу, содержащую собственные векторы, которые сопоставляются с собственными значениями k

5.Получите новые функции (например, компоненты LDA), взяв скалярное произведение данных и матрицы из шага 4.

(.env) boris@UbuntuLTS:~/LDA$ cat AnalysisLDA1.py

from sklearn.datasets import load_wine

import pandas as pd

import numpy as np

np.set_printoptions(precision=4)

from matplotlib import pyplot as plt

import seaborn as sns

sns.set()

from sklearn.preprocessing import LabelEncoder

from sklearn.tree import DecisionTreeClassifier

from sklearn.model_selection import train_test_split

from sklearn.metrics import confusion_matrix

import warnings

warnings.simplefilter(action='ignore', category=FutureWarning)

wine = load_wine()

X = pd.DataFrame(wine.data, columns=wine.feature_names)

y = pd.Categorical.from_codes(wine.target, wine.target_names)

print(X.shape)

print(X.head())

print(wine.target_names)

df = X.join(pd.Series(y, name='class'))


class_feature_means = pd.DataFrame(columns=wine.target_names)

for c, rows in df.groupby('class'):

    class_feature_means[c] = rows.mean()

print(class_feature_means)


within_class_scatter_matrix = np.zeros((13,13))

for c, rows in df.groupby('class'):

    rows = rows.drop(['class'], axis=1)

s = np.zeros((13,13))

for index, row in rows.iterrows():

    x, mc = row.values.reshape(13,1), class_feature_means[c].values.reshape(13,1)

    s += (x - mc).dot((x - mc).T)

    within_class_scatter_matrix += s

feature_means = df.mean()

between_class_scatter_matrix = np.zeros((13,13))

for c in class_feature_means:    

    n = len(df.loc[df['class'] == c].index)

    mc, m = class_feature_means[c].values.reshape(13,1), feature_means.values.reshape(13,1)

    between_class_scatter_matrix += n * (mc - m).dot((mc - m).T)

# Getting eigen_values, eigen_vectors

eigen_values, eigen_vectors = np.linalg.eig(np.linalg.inv(within_class_scatter_matrix).dot(between_class_scatter_matrix))

pairs = [(np.abs(eigen_values[i]), eigen_vectors[:,i]) for i in range(len(eigen_values))]

pairs = sorted(pairs, key=lambda x: x[0], reverse=True)

for pair in pairs:

    print(pair[0])

eigen_value_sums = sum(eigen_values)

print('Explained Variance')

for i, pair in enumerate(pairs):

    print('Eigenvector {}: {}'.format(i, (pair[0]/eigen_value_sums).real))

w_matrix = np.hstack((pairs[0][1].reshape(13,1), pairs[1][1].reshape(13,1))).real

X_lda = np.array(X.dot(w_matrix))

le = LabelEncoder()

y = le.fit_transform(df['class'])

plt.xlabel('LD1')

plt.ylabel('LD2')

plt.scatter(

    X_lda[:,0],

    X_lda[:,1],

    c=y,

    cmap = 'rainbow',

    alpha=0.7,

    edgecolors='b'

)

plt.show()

"""

Вместо того, чтобы каждый раз реализовывать алгоритм линейного дискриминантного анализа с нуля, мы можем использовать предопределенный класс LinearDiscriminantAnalysis, предоставленный нам библиотекой scikit-learn.

"""

from sklearn.discriminant_analysis import LinearDiscriminantAnalysis

lda = LinearDiscriminantAnalysis()

X_lda = lda.fit_transform(X, y)

plt.xlabel('LD1')

plt.ylabel('LD2')

plt.scatter(

    X_lda[:,0],

    X_lda[:,1],

    c=y,

    cmap='rainbow',

    alpha=0.7,

    edgecolors='b'

)

plt.show()
































No comments:

Post a Comment