Разложение по сингулярным значениям, или SVD, имеет широкий спектр применений. К ним относятся уменьшение размерности, сжатие изображения и шумоподавление данных. По сути, SVD утверждает, что матрица может быть представлена как произведение трех других матриц. В математическом плане SVD можно записать следующим образом:
гдеNколичество строк (то есть выборок) иппредставляет количество измерений.
Предположим, у нас была матрица,
Для того, чтобы определить связанныйUматрица, мы должны сначала найти собственные векторы матрицыумножается на транспонирование матрицы,
поскольку
Напомним, как определяется определение собственных векторов и собственных значений:
Последнее также может быть выражено как:
Итак, возвращаясь к нашему примеру:
Мы получаем полином четвертой степени.
После решения мы заменим лямбду одним из собственных значений.
После умножения матрицы на векторИксполучаем следующее:
При решении уравнений получаем:
Мы подключаем собственные векторы в столбцыU,
Затем мы повторим тот же процесс для транспонирования матрицыумножается на матрицу,
После решения получим выражение дляВ,
В заключение,Sявляется диагональной матрицей, значения которой являются квадратным корнем из собственных значений либо
Здесь все становится интересно. Мы знаем, что произведение всех трех матриц эквивалентно матрице в левой части.
Мы можем исключить особенности и при этом сохранить аппроксимацию исходной матрицы. Скажи, что матрицаэто набор данных столбцов и строк или пикселей, которые составляют изображение, мы можем теоретически обучить модели, используя вновь сформированную матрицу, и достичь сопоставимой, если не лучшей (из-за проклятия размерности) точности.
Давайте посмотрим, как мы можем применить декомпозицию Singular Value в Python. Для начала импортируйте следующие библиотеки.
import numpy as np
from sklearn.datasets import load_digits
from matplotlib import pyplot as plt
from sklearn.decomposition import TruncatedSVD
float_formatter = lambda x: "%.2f" % x
np.set_printoptions(formatter={'float_kind':float_formatter})
from sklearn.ensemble import RandomForestClassifier
В следующем уроке мы попытаемся классифицировать рукописные цифры. К счастью,scikit-learn
Библиотека предоставляет функцию-оболочку для импорта набора данных в нашу программу.
X, y = load_digits(return_X_y=True)
Набор данных содержит 1797 8x8 изображений. Если вы укажетеreturn_X_y=True
функция вернет пиксели в виде одномерного массива.
X.shape
Yсодержит метки для каждой цифры.
y
Давайте посмотрим на первую цифру. Как мы видим, это просто массив длиной 64, содержащий интенсивность пикселей.
image = X[0]
Если мы хотим просмотреть изображение с помощьюmatplotlib
, мы должны сначала изменить массив.
image = image.reshape((8, 8))plt.matshow(image, cmap = 'gray')
Далее мы будем использовать разложение по сингулярным значениям, чтобы увидеть, сможем ли мы восстановить изображение, используя только 2 функции для каждой строки.sматрица, возвращаемая функцией, должна быть преобразована в диагональную матрицу с помощьюdiag
метод. По умолчанию,diag
создаст матрицу, котораян х нотносительно исходной матрицы. Это вызывает проблему, так как размер матриц больше не соответствует правилу умножения матриц, когда количество столбцов в матрице должно соответствовать количеству строк в другой матрице. Поэтому мы создаем новыйм х нматрица и заполнить первыйн х нчасть этого с диагональной матрицей.
U, s, V = np.linalg.svd(image)S = np.zeros((image.shape[0], image.shape[1]))S[:image.shape[0], :image.shape[0]] = np.diag(s)n_component = 2S = S[:, :n_component]
VT = VT[:n_component, :]A = U.dot(Sigma.dot(VT))print(A)
plt.matshow(A, cmap = 'gray')
Мы можем получить уменьшенное пространство признаков, взяв точечное произведениеUа такжеSматрицы.
U.dot(S)
Давайте сравним точность модели случайного леса, когда она обучается с использованием оригинальных рукописных цифр и когда она обучается с использованием уменьшенного пространства признаков, полученного из разложения по сингулярным значениям.
Мы можем измерить точность модели, взглянув на показатель Out-Of-Bag. Если вы не знакомы с концепцией OOB, я рекомендую вам оформить заказ это пост на Случайный Лес.
rf_original = RandomForestClassifier(oob_score=True)rf_original.fit(X, y)rf_original.oob_score_
Далее мы создаем и подгоняем экземплярTruncatedSVD
класс с 2 компонентами. Стоит отметить, что в отличие от предыдущего примера, мы используем функции 2/64.
svd = TruncatedSVD(n_components=2)X_reduced = svd.fit_transform(X)
Каждое изображение (то есть строка) в сокращенном наборе данных содержит 2 элемента.
X_reduced[0]
Взглянув на изображение, трудно различить, из какой цифры состоит изображение, вполне может быть 5, а не 0.
image_reduced = svd.inverse_transform(X_reduced[0].reshape(1,-1))image_reduced = image_reduced.reshape((8,8))plt.matshow(image_reduced, cmap = 'gray')
После обучения классификатора случайных лесов на сокращенном наборе данных мы получаем скудную точность 36,7%.
rf_reduced = RandomForestClassifier(oob_score=True)rf_reduced.fit(X_reduced, y)rf_reduced.oob_score_
Мы можем получить общую дисперсию, объясняя, взяв суммуexplained_variance_ratio_
свойство. Как правило, мы хотим стремиться к 80-90 процентам.
svd.explained_variance_ratio_.sum()
Давайте попробуем еще раз, только на этот раз мы используем 16 компонентов. Мы проверяем количество информации, содержащейся в 16 функциях.
svd = TruncatedSVD(n_components=16)X_reduced = svd.fit_transform(X)svd.explained_variance_ratio_.sum()
Мы получаем точность, сопоставимую с моделью, обученной с использованием исходных изображений, и мы использовали 16/64 = 0,25 объем данных.
rf_reduced = RandomForestClassifier(oob_score=True)rf_reduced.fit(X_reduced, y)rf_reduced.oob_score_