Привет, в этом посте я собираюсь объяснить, как методы уменьшения размерности влияют на модель прогнозирования. Здесь мы используем набор данных Iris и классификатор K-NN. Мы собираемся сравнить PCA и LDA на наборе данных Iris.

Прежде чем приступить к экспериментам, лучше бы освежить в памяти концепции PCA и LDA. Поэтому я постараюсь объяснить их короткими заметками. Начнем с PCA.

Анализ главных компонентов (PCA)

PCA - это статистический инструмент, часто используемый для уменьшения размерности. Это помогает преобразовать данные с более высокой размерностью в данные с более низкой размерностью, прежде чем применять какую-либо модель машинного обучения. Это алгоритм обучения без учителя. Позвольте мне начать с объяснения того, что делает PCA. На изображении ниже мы видим объект сверху, снизу и сбоку. Обратите внимание, что объект может иметь 360 просмотров. Если эта кружка (объект) является данными, то PCA помогает нам найти виды (направления), где видна самая большая часть чашки. Например, если есть только вид сбоку и снизу, PCA дает нам вид сбоку, потому что видна большая площадь чая. Здесь вид сбоку рассматривается как первая основная составляющая. Получив первый главный компонент, мы вращаем чашу в направлениях, перпендикулярных первому главному компоненту. Направление, которое охватывает самую большую часть и перпендикулярно первому главному компоненту, называется вторым главным компонентом. Таким образом мы можем найти третий, четвертый и так далее.

Шаги для выполнения PCA:

Нормализуйте данные и найдите матрицу ковариации. Затем найдите собственные векторы и соответствующие собственные значения. Первый главный компонент - это не что иное, как собственный вектор с наибольшим собственным значением и так далее.

Линейный дискриминантный анализ (LDA):

LDA - это метод контролируемого уменьшения размерности. Он делает предположения на основе данных. Это обобщение линейного дискриминанта Фишера. LDA не находит основных компонентов. Вместо этого он увеличивает межклассовое расстояние и уменьшает внутриклассовое расстояние. Подробное описание LDA можно найти здесь.

Загрузите необходимые библиотеки

from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from tqdm import tqdm
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
import seaborn as sn
from sklearn.metrics.pairwise import euclidean_distances
import warnings
warnings.filterwarnings("ignore")

Загрузите данные IRIS и выполните стандартизацию

dataset = pd.read_csv('iris.csv') #read the data into dataframe
X = dataset.iloc[:, :-1].values   #store the dependent features in X
y = dataset.iloc[:, 4].values   #store the independent variable in y
X = StandardScaler().fit_transform(X)

Выполните PCA и визуализируйте данные

# initializing the pca
from sklearn import decomposition
pca = decomposition.PCA()
# configuring the parameteres
# the number of components = 2
# we have taken only 2 components as it is easy to visualize
pca.n_components = 2
# pca_reduced will contain the 2-d projects of simple data
pca_data = pca.fit_transform(X)
print("shape of pca_reduced.shape = ", pca_data.shape)
#>>>   shape of pca_reduced.shape =  (150, 2)
# attaching the label for each 2-d data point
pca_data = np.vstack((pca_data.T, y)).T
# creating a new data from which help us in ploting the result data
pca_df = pd.DataFrame(data=pca_data, columns=("1st_principal", "2nd_principal", "label"))
sn.FacetGrid(pca_df, hue="label", size=4).map(plt.scatter, '1st_principal', '2nd_principal').add_legend()
plt.show()

Построение графика зависимости количества основных компонентов от максимальной кумулятивной дисперсии

# PCA for dimensionality redcution (not-visualization)
pca.n_components = 4
pca_data = pca.fit_transform(X)
percentage_var_explained = pca.explained_variance_ / np.sum(pca.explained_variance_)
cum_var_explained = np.cumsum(percentage_var_explained)
# Plot the PCA spectrum
plt.figure(1, figsize=(6, 4))
plt.xticks(np.arange(0, 4, step=1),(1,2,3,4))
plt.plot(cum_var_explained, linewidth=2)
plt.axis('tight')
plt.grid()
plt.xlabel('n_components')
plt.ylabel('Cumulative_explained_variance')
plt.show()

Если взять 1-мерное, прибл. 72% расхождения объясняются, а если взять 2-х мерную величину, прибл. Объяснено 95% отклонений.

Выполните LDA и визуализируйте данные

from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
lda = LinearDiscriminantAnalysis(n_components=2)
lda_data = lda.fit(X, y).transform(X)
# attaching the label for each 2-d data point
lda_data = np.vstack((lda_data.T, y)).T
# creating a new data fram which help us in ploting the result data
lda_df = pd.DataFrame(data=lda_data, columns=("1st_principal", "2nd_principal", "label"))
sn.FacetGrid(lda_df, hue="label", size=4).map(plt.scatter, '1st_principal', '2nd_principal').add_legend()
plt.show()

Применение K-NN к исходным ДАННЫМ IRIS

def divide_training_dataset_to_k_folds(x_train,y_train,folds):
 temp = len(x_train)/folds
 x_train = x_train.tolist()
 y_train = y_train.tolist()
 group = []
 label = []
 end = 0.0
 while end < len(x_train):
  group.append(x_train[int(end):int(end + temp)])
  label.append(y_train[int(end):int(end + temp)])
  end += temp
 return group,label

Определите метод случайной перекрестной проверки:

from sklearn.metrics import accuracy_score
def RandomSearchCV(x_train,y_train,classifier, param_range, folds):
 # x_train: its numpy array of shape, (n,d)
 # y_train: its numpy array of shape, (n,) or (n,1)
 # classifier: its typically KNeighborsClassifier()
 # param_range: its a tuple like (a,b) a < b
 # folds: an integer, represents number of folds we need to devide the data and test our model
 params = list(range(1,51))
 #1.divide numbers ranging from  0 to len(X_train) into groups= folds
 # ex: folds=3, and len(x_train)=100, we can devide numbers from 0 to 100 into 3 groups i.e: group 1: 0-33, group 2:34-66, group 3: 67-100
 temp = len(x_train)/folds
 temp = int(temp)
groups,labels = divide_training_dataset_to_k_folds(x_train,y_train, folds)
 #2.for each hyperparameter that we generated in step 1 and using the above groups we have created in step 2 you will do cross-validation as follows:
 # first we will keep group 1+group 2 i.e. 0-66 as train data and group 3: 67-100 as test data, and find train and test accuracies
 # second we will keep group 1+group 3 i.e. 0-33, 67-100 as train data and group 2: 34-66 as test data, and find train and test accuracies
 # third we will keep group 2+group 3 i.e. 34-100 as train data and group 1: 0-33 as test data, and find train and test accuracies
 # based on the 'folds' value we will do the same procedure
 # find the mean of train accuracies of above 3 steps and store in a list "train_scores"
 # find the mean of test accuracies of above 3 steps and store in a list "test_scores"
 train_scores = []
 test_scores  = []
 for k in tqdm(params):
  trainscores_folds = []
  testscores_folds = []  
  for i in range(folds):
   X_train = [groups[iter] for iter in range(folds) if iter != i]
   X_train = [j for sublist in X_train for j in sublist]
   Y_train = [labels[iter] for iter in range(folds) if iter != i]
   Y_train = [j for sublist in Y_train for j in sublist]
   X_test  = groups[i]
   Y_test  = labels[i]
   classifier.n_neighbors = k
   classifier.fit(X_train,Y_train)
   Y_predicted = classifier.predict(X_test)
   testscores_folds.append(accuracy_score(Y_test, Y_predicted))
   Y_predicted = classifier.predict(X_train)
   trainscores_folds.append(accuracy_score(Y_train, Y_predicted))
  train_scores.append(np.mean(np.array(trainscores_folds)))
  test_scores.append(np.mean(np.array(testscores_folds)))
#3. return both "train_scores" and "test_scores"
 return train_scores, test_scores,params

Классификатор К НН

from sklearn.metrics import accuracy_score
from sklearn.neighbors import KNeighborsClassifier
import matplotlib.pyplot as plt
classifier = KNeighborsClassifier()
param_range = (1,50)
folds = 3
X = dataset.iloc[:, :-1].values#store the dependent features in X
y = dataset.iloc[:, 4].values  #store the independent variable in y
X_train, X_test, y_train, y_test = train_test_split(X, y,stratify=y, random_state=42,test_size=0.30)
trainscores,testscores,params=RandomSearchCV(X_train,y_train,classifier, param_range, folds)
#  plot hyper-parameter vs accuracy plot as shown in reference notebook and choose the best hyperparameter
plt.plot(params,trainscores, label='train curve')
plt.plot(params,testscores, label='test curve')
plt.title('Hyper-parameter VS accuracy plot')
plt.legend()
plt.show()

Применение K-NN к модифицированным данным IRIS с использованием PCA

X = pca_df.iloc[:, :-1].values#store all the dependent features in X
y = pca_df.iloc[:, -1].values   #store the independent variable in y
X_train, X_test, y_train, y_test = train_test_split(X, y,stratify=y, random_state=42,test_size=0.30) #training data = 70% and test data = 30%
trainscores,testscores,params=RandomSearchCV(X_train,y_train,classifier, param_range, folds)
#  plot hyper-parameter vs accuracy plot as shown in reference notebook and choose the best hyperparameter
plt.plot(params,trainscores, label='train curve')
plt.plot(params,testscores, label='test curve')
plt.title('Hyper-parameter VS accuracy plot')
plt.legend()
plt.show()

Применение K-NN к модифицированным данным IRIS с использованием LDA

X_train, X_test, y_train, y_test = train_test_split(X,y,stratify=y, random_state=42,test_size=0.30)
trainscores,testscores,params=RandomSearchCV(X_train,y_train,classifier, param_range, folds)
#  plot hyper-parameter vs accuracy plot as shown in reference notebook and choose the best hyperparameter
plt.plot(params,trainscores, label='train curve')
plt.plot(params,testscores, label='test curve')
plt.title('Hyper-parameter VS accuracy plot')
plt.legend()
plt.show()

Вывод:

Подводя итог, мы можем наблюдать из приведенных выше результатов, что PCA плохо работает с помеченными данными. С другой стороны, LDA не снизило производительность модели KNN, а также уменьшило сложность набора данных. Поскольку PCA является неконтролируемым методом, он не принимает во внимание метки классов. Таким образом, мы можем сделать вывод, что LDA - лучший метод уменьшения размерности, чем PCA для помеченных данных.

Ссылка для кода: github

Примечание: сокращенный набор данных с использованием LDA дал ту же точность, что и исходный набор данных, то есть 97%. Однако сокращенный набор данных с использованием PCA дал точность 91%, что очень мало !!!