PCA и градиентное восхождение в машинном обучении

машинное обучение алгоритм

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

Основы PCA

В качестве примера возьмем двумерную плоскость с 2 элементами, как показано на рисунке:

clipboard.png

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

clipboard.png

После сопоставления с разными осями расстояние между выборками будет другим, и необходима ось, которая максимизирует расстояние между выборками. Предположим, что это направление осиw = (w1, w2).

Дисперсия может быть использована для расстояния между образцамиVar(x) = \frac{1}{m}\sum_{i=1}^m(x_i-\overline x)^2Чтобы измерить, чем больше дисперсия, тем реже выборки.

Затем среднее значение возвращается к обработке 0 (ухудшение), т. е.\overline x = 0, так чтоVar(x) = \frac{1}{m}\sum_{i=1}^m(x_i)^2. Обработка среднего возврата к 0 изменяет исходный образец и преобразует его в новый образец.X. сопоставьте его с осьюwполучить новые образцыX_{pr}, дисперсия в это время составляет:

V ar( X p r )=1 м ∑ i =1 м (X ( i) p r ) 2

так какX_{pr}^{(i)}вектор(X_{pr1}^{(i)}, X_{pr2}^{(i)}), поэтому фактическая дисперсия выражается как:

V ar( X p r )=1 м ∑ i =1 м | | X ( i) p r | | 2

окончательный расчет||X_{pr}^{(i)}||, как показано на рисунке:

clipboard.png

||X_{pr}^{(i)}|| = X^{(i)}·w(w— единичный вектор), поэтому искомая целевая функция получается:

попрошайничествоwсделатьVar(X_{pr}) = \frac{1}{m}\sum_{i=1}^m(X^{(i)}·w)^2максимум; для многомерных признаков, т.е.\frac{1}{m}\sum_{i=1}^m(X_1^{(i)}w_1+X_2^{(i)}w_2+...+X_n^{(i)}w_n)^2максимум.

Метод градиентного подъема решает проблемы PAC

В градиентном восхождении,+\eta\nabla fпредставляет собойfнаправление увеличения.

Для целевой функции PAC: найтиwсделатьf(X) =  \frac{1}{m}\sum_{i=1}^m(X_1^{(i)}w_1+X_2^{(i)}w_2+...+X_n^{(i)}w_n)^2максимум, который можно решить с помощью градиентного восхождения.

f(X)Градиент:

∇ f = ⎛ ⎝⎜ ⎜⎜ ⎜⎜ ⎜ ∂ f ∂ w 1 ∂ f ∂ w 2 ⋯ ∂ f ∂ wn ⎞ ⎠⎟ ⎟⎟ ⎟⎟ ⎟ = 2 m ⎛ ⎝⎜ ⎜⎜ ⎜⎜ ⎜⎜ (i) 1 w 1 + X (i) 2 w 2 + ... + X (i) nwn) X (i) 1 Σ mi = 1 (X (i) 1 w 1 + X (i) 2 w 2 +... + X (i) nwn) X (i) 2 ⋯ Σ mi = 1 (X (i) 1 w 1 + X (i) 2 w 2 +... + X (i) nwn) X (i) n ⎞ ⎠ ⎟ ⎟ ⎟ ⎟ ⎟ = 2 m ⎛ ⎝ ⎜ ⎜ ⎜ ⎜ ⎜ Σ mi = 1 (X (i) w) X (i) 1 Σ mi = 1 (X (i) w) X (i) 2 ⋯ Σ mi = 1 (X (I) w) X (i) n ⎞ ⎠ ⎟ ⎟ ⎟ ⎟ ⎟ = 2 m ⋅ X T (X w)

Примечание. Процесс преобразования здесь опущен.

Используйте формулу градиента\nabla f =   \frac{2}{m}·X^T(Xw)Метод градиентного восхождения на самом деле представляет собой пакетное градиентное восхождение (существуют также стохастическое градиентное восхождение и мини-пакетное градиентное восхождение, которые не рассматриваются в этой статье).

Метод градиентного восхождения для нахождения главных компонент

Найдите первую главную компоненту

Сначала определите набор данных с двумя функциямиX, всего 100 образцов:

import numpy as np

X = np.empty((100, 2))
X[:, 0] = np.random.uniform(0., 100., size=100)
X[:, 1] = 0.75 * X[:, 0] + 3. + np.random.normal(0, 10., size=100)

XВизуализация выглядит следующим образом:

clipboard.png

demean()пара методовXВыполните обработку среднего возврата к 0:

def demean(X):
    return X - np.mean(X, axis=0)

X_demean = demean(X)

После того, как среднее возвращается к 0X\_demeanВизуализация выглядит следующим образом:

clipboard.png

f()способ найти функциюfЗначение:

def f(w, X):
    return np.sum(X.dot(w) ** 2) / len(X)

df()метод по градиентной формуле\nabla f =   \frac{2}{m}·X^T(Xw)Найдите градиент:

def df(w, X):
    return X.T.dot(X.dot(w)) * 2 / len(X)

gradient_ascent()Метод представляет собой процесс метода градиентного восхождения, вn_itersВ цикле каждый раз новыйw, если новыйwи старыйwсоответствующая функцияfРазница значений удовлетворяет точностиepsilon, значит подходящийw:

def gradient_ascent(X, initial_w, eta, n_iters=1e4, epsilon=1e-8):
    w = direction(initial_w)
    cur_iter = 0
    
    while cur_iter < n_iters:
        gradient = df(w, X)
        last_w = w
        w = w + eta * gradient
        w = direction(w)  # 将w转换成单位向量
        if(abs(f(w, X) - f(last_w, X)) < epsilon):
            break
            
        cur_iter += 1
        
    return w

который вызывает методdirection(), дляwПреобразовать в единичный вектор:

def direction(w):
    return w / np.linalg.norm(w)

Затем используйте метод градиентного подъема, сначала установите начальныйwи скорость обучения\eta:

initial_w = np.random.random(X.shape[1])
eta = 0.001

позвонить напрямуюgradient_ascent()метод:

w = gradient_ascent(X_demean, initial_w, eta)

получитеwс образцом визуализации, как показано (wПредставляет собой прямую линию):

clipboard.png

Найти первые n основных компонентов

Первая главная компонента была найдена ранее, как найти следующую главную компоненту? Возьмем в качестве примера двухмерные объекты, как показано на рисунке:

clipboard.png

образецX^{(i)}в первой главной компонентеwКомпонент на(X_{pr1}^{(i)}, X_{pr2}^{(i)}), компоненты на следующем главном компонентеX^{'(i)} = X^{(i)} - (X_{pr1}^{(i)}, X_{pr2}^{(i)}), поэтому необходимо только удалить компонент данных по первому главному компоненту, а затем вычислить первый главный компонент для новых данных.

first_n_component()способ найтиXПервые n главных компонент , в цикле for каждый раз, когда ищутся главные компоненты, случайныйw(initial_w), после получения основного компонента удалить компонент на этом основном компонентеX_pca = X_pca - X_pca.dot(w).reshape(-1, 1) * w:

def first_n_component(n, X, eta=0.001, n_iters=1e4, epsilon=1e-8):
    X_pca = X.copy()
    X_pca = demean(X_pca)
    
    res = []
    for i in range(n):
        initial_w = np.random.random(X_pca.shape[1])
        w = gradient_ascent(X_pca, initial_w, eta)
        res.append(w)
        
        X_pca = X_pca - X_pca.dot(w).reshape(-1, 1) * w
    
    return res

карта уменьшения размерности

После получения нескольких основных компонентов данные высокой размерности можно преобразовать в данные низкой размерности с помощью этих основных компонентов.

Для многомерных данных с m образцами и n признакамиX(m*n), первые k (k(н*к):

X =⎛ ⎝⎜ ⎜⎜ ⎜⎜ X ( 1 ) 1 X ( 1 ) 2 ⋯ X ( 1 ) n X ( 2 ) 1 X ( 2 ) 2 ⋯ X ( 2 ) n                X ( m ) 2 .. .X ( m) n ⎞ ⎠ ⎟ ⎟ ⎟ ⎟ ⎟ , W k = ⎛ ⎝ ⎜ ⎜ ⎜ ⎜ ⎜ W ( 1 ) 1 W ( 1 ) 2 ⋯ W ( 1 ) n W ( 2 ) 1 W ( 2 ) 2 ⋯ W ( 2 ) п           ⋯ W ( k ) 1 W ( k ) 2 . . . W ( k ) n ⎞ ⎠ ⎟ ⎟ ⎟ ⎟ ⎟

Новые данные после приведения n-размерности к k-мерности:X_k = X·W_k^T.

Информация может быть потеряна во время этого процесса уменьшения размерности. Если в исходных данных есть какая-то бесполезная информация, уменьшение размерности также может иметь эффект уменьшения сухости.

Также возможно восстановить новые данные после приведения размерности к исходной размерности:X_m = X_k·W_k.

Внедрить PCA

определить классPCA, в функции-конструктореn_componentsПредставляет количество главных компонентов, то есть измерение после уменьшения размера,components_представляет собой главный компонентW_k;

функцияfit()с вышеуказаннымfirst_n_component()Тот же метод используется для нахожденияW_k;

функцияtransform()будетXСопоставляя с каждым компонентом главного компонента, мы получаемX_k, то есть уменьшение размерности;

функцияtransform()будетX_kСопоставляя с исходным пространством признаков, мы получаемX_m.

import numpy as np


class PCA:
    def __init__(self, n_components):
        self.n_components = n_components
        self.components_ = None

    def fit(self, X, eta=0.001, n_iters=1e4):
        def demean(X):
            return X - np.mean(X, axis=0)

        def f(w, X):
            return np.sum(X.dot(w) ** 2) / len(X)

        def df(w, X):
            return X.T.dot(X.dot(w)) * 2 / len(X)

        def direction(w):
            return w / np.linalg.norm(w)

        def first_component(X, initial_w, eta, n_iters, epsilon=1e-8):
            w = direction(initial_w)
            cur_iter = 0
            
            while cur_iter < n_iters:
                gradient = df(w, X)
                last_w = w
                w = w + eta * gradient
                w = direction(w) 
                if(abs(f(w, X) - f(last_w, X)) < epsilon):
                    break
                    
                cur_iter += 1
                
            return w
        
        X_pca = demean(X)
        self.components_ = np.empty(shape=(self.n_components, X.shape[1]))

        for i in range(self.n_components):
            initial_w = np.random.random(X_pca.shape[1])
            w = first_component(X_pca, initial_w, eta, n_iters)
            self.components_[i:] = w
            
            X_pca = X_pca - X_pca.dot(w).reshape(-1, 1) * w
        
        return self

    def transform(self, X):
        return X.dot(self.components_.T)

    def inverse_transform(self, X):
        return X.dot(self.components_)

PCA в Scikit Learn

Вместо градиентного подъема PCA в Scikit Learn использует соответствующий математический метод. Он используется следующим образом:

from sklearn.decomposition import PCA

pca = PCA(n_components=1)

параметрn_componentsпредставляет число главных компонентов.

Поскольку уменьшение размерности PCA приведет к потере информации об исходных данных, PCA в Scikit Learn указывает, сколько информации сохраняется в процессе уменьшения размерности, когда 0

pca = PCA(0.95)

То есть сохраняется 95% исходных данных, и программа автоматически получитcomponents_.

Глядя на PCA с kNN

Теперь давайте посмотрим на преимущества PCA с помощью алгоритма классификации kNN. Сначала используйте обычный рукописный набор данных. Набор данных MNIST. Процедура получения образца набора данных:

import numpy as np
from sklearn.datasets import fetch_mldata

mnist = fetch_mldata("MNIST original")
X, y = mnist['data'], mnist['target']

# MNIST 数据集已经默认分配好了训练数据集和测试数据集,即前60000个数据为训练数据
X_train = np.array(X[:60000], dtype=float)
y_train = np.array(y[:60000], dtype=float)
X_test = np.array(X[60000:], dtype=float)
y_test = np.array(y[60000:], dtype=float)

Давайте посмотрим на эффективность алгоритма kNN без использования PCA:

from sklearn.neighbors import KNeighborsClassifier

knn_clf = KNeighborsClassifier()
%time knn_clf.fit(X_train, y_train)

В настоящее времяfitВремя, затраченное на процесс, равно:

CPU times: user 29.6 s, sys: 306 ms, total: 29.9 s
Wall time: 30.7 s

Затем проверьте точность набора тестовых данных:

%time knn_clf.score(X_test, y_test)

Время, затраченное на этот процесс, равно:

CPU times: user 10min 23s, sys: 2.05 s, total: 10min 25s
Wall time: 10min 31s

И правильная скорость в тестовом наборе данных составляет 0,9688.

Затем взгляните на преимущества PCA.При использовании PCA установите для хранения информации значение 90%:

from sklearn.decomposition import PCA

pca = PCA(0.9)
pca.fit(X_train)
X_train_reduction = pca.transform(X_train)

knn_clf = KNeighborsClassifier()
%time knn_clf.fit(X_train_reduction, y_train)

В настоящее времяfitВремя, затраченное на процесс, равно:

CPU times: user 317 ms, sys: 5.31 ms, total: 323 ms
Wall time: 323 ms

Затем проверьте точность набора тестовых данных:

X_test_reduction = pca.transform(X_test)
%time knn_clf.score(X_test_reduction, y_test)

Время, затраченное на этот процесс, равно:

CPU times: user 1min 15s, sys: 469 ms, total: 1min 15s
Wall time: 1min 17s

Видно, что затраты времени после использования PCA значительно сокращаются, а правильная скорость набора тестовых данных составляет 0,9728, что не только не уменьшается, но и увеличивается, так как здесь теряется информация в процессе уменьшения размерности. бесполезная информация эффект сушки.

Адрес источника

Github | ML-Algorithms-Action