Пошаговая теория и реализация PCA

Анализ основных компонентов — один из самых популярных методов повышения производительности алгоритма машинного обучения, который обрабатывает большое количество данных и функций. Тем не менее, иногда PCA может быть слишком сложным, слишком техническим или даже слишком утомительным, чтобы правильно понять основные принципы, поэтому я решил написать эту статью, в которой практически сформулирован каждый шаг и которая легко усваивается новичками.

Во-первых, нам нужно лучше понять, почему нам нужно использовать PCA в машинном обучении:

  1. Избавьтесь от шумовых данных. Иногда в наборе данных слишком много данных для анализа, независимо от того, нужно ли нам включить их или удалить, чтобы улучшить производительность алгоритма. Однако с PCA он отфильтрует шумовые данные и оставит только заметные данные.
  2. Повышает производительность.Избавление от огромного количества несвязанных функций из набора данных автоматически значительно сократит время обучения машинному обучению.
  3. Упрощение визуализации: слишком много функций вынуждают нас анализировать слишком много графиков, которые иногда кажутся запутанными, будь то для нас или для клиентов, чтобы переварить данные. Избавившись от этих несущественных функций, мы сможем упростить визуализацию данных и сделать ее более приятной для понимания.
  4. Уменьшение переобучения. Во многих случаях переобучение происходит просто из-за слишком большого числа задействованных функций или измерений. PCA явно помогает смягчить эту проблему.

Выполнение

В этой статье мы реализуем PCA через следующий набор данных Kaggle, который можно скачать: здесь. Используя функцию головы Panda, мы можем видеть особенности данных и данные следующим образом:

Очистка данных

Как мы видим, исходный набор данных состоит из 10 признаков и еще есть некоторые категориальные данные. Если вы читали мою предыдущую статью о наборе данных Titanic здесь, алгоритм машинного обучения не может обрабатывать категориальные данные, и нам нужно преобразовать их в порядковые данные. Тот же случай для PCA, нам нужно преобразовать функции продаж и зарплаты в порядковые данные, как показано ниже:

data['salary'] = data['salary'].map( {'low': 1, 'medium': 2, 'high':3} ).astype(int)
data['sales'] = data['sales'].map( {'accounting': 1, 'hr': 2, 'IT':3, 'management':4, 'marketing':5, 'product_mng':6, 'RandD':7, 'sales':8, 'support':9, 'technical':10} ).astype(int)

Что будет использовать новый набор данных без каких-либо категориальных данных, как показано ниже:

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

columns = data.columns.tolist()

Затем мы перетасовываем левый признак в самый первый столбец.

columns.insert(0, columns.pop(columns.index('left')))

Затем мы снова конвертируем форму списка в столбцы.

data = data.reindex(columns= columns)

Корреляционная матрица

Впоследствии мы можем проанализировать корреляцию между каждой функцией с помощью матрицы корреляции, которую можно построить с помощью морской библиотеки ниже:

correlation = data.corr()
plt.figure(figsize=(10,10))
sns.heatmap(correlation, vmax=1, square=True,annot=True,cmap='viridis')
plt.title('Correlation between features')

Разделение данных

Поскольку левая функция является меткой, мы можем разделить данные, более удобные для обучения, и пометить после описанного выше метода перетасовки, как показано ниже: X в качестве данных обучения и y в качестве данных метки:

X = data.iloc[:,1:10].values
y = data.iloc[:,0].values
X

С тренировочными данными показано ниже:

Теперь у нас есть 9 функций для обучающих данных.

Стандартизация данных

Теперь мы сделали наш первый шаг в PCA, который представляет собой стандартизацию данных, и это необходимый шаг перед выполнением PCA. По сути, цель стандартизации данных состоит в том, чтобы уравнять все данные с использованием среднего значения и стандартного отклонения. Например, Энди из класса 1-A получил 80 баллов на экзамене по математике из 100 и стандартное отклонение 6, однако Хелен из класса 1-B получила 320 баллов за экзамен по математике, потому что ее учитель использовал шкалу из 450 со стандартным отклонением 68. Итак, чтобы понять, у кого более высокий балл, мы стандартизируем балл, используя проценты, например, Энди набрал 80%, а Хелен набрала 71%. Таким образом, мы знаем, что у Энди более высокий балл по сравнению с Хелен.

В Python мы можем стандартизировать данные, используя функцию sklearn для StandardScaller, как показано ниже:

from sklearn.preprocessing import StandardScaler
X_std = StandardScaler().fit_transform(X)

Принцип StandardScaler от Sklearn заключается в вычитании значения из его среднего значения и делении на стандартное отклонение:

  • z: стандартизированные данные
  • х: Исходные данные
  • µ: Среднее значение
  • σ: значение стандартного отклонения

Чтобы найти среднее:

  • N: количество данных

Чтобы найти стандартное отклонение:

Теперь наш набор данных был стандартизирован с использованием функции sklearn со следующим результатом:

Ковариационная матрица

После того, как они были стандартизированы, мы хотим снова выяснить корреляцию между каждым признаком. Это можно сделать, используя следующую формулу:

Или мы можем перевести его на Python следующим образом:

mean_vec = np.mean(X_std)
cov_mat = (X_std - mean_vec).T.dot((X_std - mean_vec)) / (X_std.shape[0]-1)
print('Covariance matrix', cov_mat)

Или просто с помощью ковариационной функции Numpy:

print('NumPy covariance matrix:', np.cov(X_std.T))

Где оба кода дадут одинаковый результат:

Собственные векторы и собственные значения для поиска главных компонентов

Теперь, когда мы понимаем взаимосвязь между каждой функцией через ковариационную матрицу, мы можем определить ее основные компоненты путем вычисления собственных векторов и собственных значений.

Из приведенного выше понимания матрица ковариации рассматривается как A, следовательно, мы можем определить собственный вектор и собственное значение, используя функцию Numpy ниже:

eig_vals, eig_vecs = np.linalg.eig(cov_mat)
print('Eigenvectors', eig_vecs)
print('\nEigenvalues', eig_vals)

Которые показывают нам обе матрицы как:

Сортировка собственных значений

Теперь, когда мы узнали как собственные значения, так и собственные векторы, нам нужно отсортировать собственные значения, чтобы определить, какой собственный вектор является наиболее подходящим в наборе данных. Во-первых, нам нужно объединить каждое собственное значение и собственный вектор в один столбец собственной пары:

eig_pairs = [(np.abs(eig_vals[i]), eig_vecs[:,i]) for i in range(len(eig_vals))]

После этого мы можем отсортировать собственные пары от самых высоких до самых низких значений:

eig_pairs.sort(key=lambda x: x[0], reverse=True)

Мы можем получить упорядоченные собственные значения, как показано ниже:

print('Sorted Eigenvalues:')
for i in eig_pairs:
    print(i[0])

Определите главные компоненты

Теперь мы можем получить основные компоненты из отсортированных собственных значений. Значение основного компонента, которое называется объясненной дисперсией, указывает, насколько заметен признак. Следовательно, с 9 функциями, которые у нас есть в настоящее время, мы создадим 9 основных компонентов с каждой отдельной объясненной дисперсией в процентах.

Объясненную дисперсию можно определить по следующей формуле:

tot = sum(eig_vals)
var_exp = [(i / tot)*100 for i in sorted(eig_vals, reverse=True)]

Затем мы можем вывести значение каждого основного компонента с помощью Matplotlib:

with plt.style.context('bmh'):
    plt.figure(figsize=(6, 4))
plt.bar(range(9), var_exp, alpha=0.5, align='center')
    plt.ylabel('Explained Variance')
    plt.xlabel('Principal components')
    plt.tight_layout()

На приведенном выше графике мы можем определить, что максимальная дисперсия составляет около 20,5%. Последние 2 функции явно демонстрируют очень минимальное влияние по сравнению с остальными, поскольку их дисперсия составляет менее 7,5%. Поэтому мы можем отказаться от обеих функций.

Построить новую матрицу

Теперь, когда мы решили отказаться от последних двух функций, мы можем построить новую матрицу, состоящую только из первых 7 функций.

PCA_matrix = np.hstack((eig_pairs[0][1].reshape(9,1), 
                      eig_pairs[1][1].reshape(9,1),
                      eig_pairs[2][1].reshape(9,1), 
                      eig_pairs[3][1].reshape(9,1),
                      eig_pairs[4][1].reshape(9,1), 
                      eig_pairs[5][1].reshape(9,1),
                      eig_pairs[6][1].reshape(9,1),
                    ))

Затем мы используем скалярное произведение, чтобы создать новое пространство признаков, используя Y = X•W.

Y = X_std.dot(PCA_matrix)

Затем мы можем создать новый набор данных, который состоит из данных каждого основного компонента, включая метку, как показано ниже:

principalDf = pd.DataFrame(data = Y , columns = ['principal component 1', 'principal component 2', 'principal component 3', 'principal component 4', 'principal component 5', 'principal component 6', 'principal component 7']) 
finalDf = pd.concat([principalDf,pd.DataFrame(y,columns = ['left'])], axis = 1)

Это даст нам новый набор данных, который был обработан PCA как:

Вывод

Есть много вариантов того, сколько функций вы можете решить удалить, будь то 2, 3 или даже оставить только 2 функции, мы поняли, что по крайней мере первая функция является наиболее заметной. Следовательно, эти данные могут быть обработаны с помощью алгоритма машинного обучения, такого как линейная регрессия, случайный лес и т. д., намного быстрее, чем обработка всего набора данных.

Я надеюсь, что эта статья поможет вам больше узнать о PCA, и спасибо за чтение.