Wir haben eine Selbstcodierer- und Hauptkomponentenanalyse als Dimensionskomprimierungsmethode implementiert. Als Lehrbücher ["Deep Learning"](https://www.amazon.co.jp/ Deep Learning - Maschinelles Lernen Professional Series-Okaya-Takayuki / dp / 4061529021) und ["First Pattern Recognition"](https: / /www.amazon.co.jp/ Erste Mustererkennung (Hirai-Yuzo / dp / 4627849710) wurde verwendet.
Struktur dieses Artikels
** Auto Encoder ** ist ein neuronales Netzwerk, das Eingaben als Trainingsdaten verwendet und Funktionen erfasst, die die Daten gut darstellen. Es wird als unbeaufsichtigtes Lernen klassifiziert, bei dem keine Lehrerdaten verwendet werden. Es wird zum Vorlernen neuronaler Netze und zum Ermitteln von Anfangswerten verwendet. Kommen wir nun zur Erklärung des Selbstcodierers. Stellen Sie sich ein dreischichtiges Netzwerk vor, wie in der folgenden Abbildung dargestellt. Die Aktivierungsfunktion ist die konstante Zuordnungsfunktion $ g (a) = a $.
Der Wert der Einheit in der mittleren Schicht $ z_j $ und der Wert der Einheit in der Ausgangsschicht $ y_k $ werden erhalten, indem die Eingabe vorwärts weitergegeben wird.
\begin{align}
& z_j = \sum_i w_{ji} x_i \\
& \\
& y_k = \sum_j \tilde w_{kj} z_j
\end{align}
Der Vektor mit den Werten der Einheiten in der mittleren Ebene ist $ \ boldsymbol z $, und der Vektor mit den Werten der Einheiten in der Ausgabeschicht ist $ \ boldsymbol y $. Wenn die Matrix mit $ w_ {ji} $ als $ (\ j, i ) $ -Komponente $ \ boldsymbol W $ ist, kann sie wie folgt ausgedrückt werden.
\begin{align}
& \boldsymbol z = \boldsymbol W \boldsymbol x \\
& \\
& \boldsymbol y = \boldsymbol{\tilde W} \boldsymbol z
\end{align}
Die Konvertierung in die mittlere Schicht $ \ boldsymbol z $ wird als ** Codierung ** bezeichnet, und die Konvertierung in die Ausgabeschicht $ \ boldsymbol y $ wird als ** Entschlüsselung ** bezeichnet. Der Selbstcodierer trainiert das entschlüsselte $ \ boldsymbol y $ so, dass es nahe am Eingang $ \ boldsymbol x $ liegt. Wenn die Anzahl der Einheiten in der mittleren Ebene kleiner als die Eingabedimension ist und die Eingabe reproduziert werden kann, kann die Dimension komprimiert werden. Ausführliche Informationen zur Lernmethode finden Sie unter "Implementieren eines neuronalen Netzwerks mit Python". Da die Aktivierungsfunktion eine konstante Zuordnungsfunktion ist, beträgt der Differenzwert immer $ 1 $, was einfach zu berechnen ist.
** Hauptkomponentenanalyse (PCA) ** ist eine lineare Transformation von Trainingsdaten $ \ boldsymbol x_i = (x_ {i1}, ..., x_ {id}) ^ T $ in Richtung maximaler Streuung. Dies ist die Methode zu finden. $ N $ Datenmatrix bestehend aus $ \ boldsymbol X = (\ boldsymbol x_1, ..., \ boldsymbol x_N) ^ T $, durchschnittlicher Vektor $ \ boldsymbol {\ bar x} = (\ bar x_1, ..., \ bar x_d) ^ T $. Die Datenmatrix $ \ boldsymbol {\ bar X} $, die die Datenmatrix minus dem mittleren Vektor ist, und die Kovarianzmatrix $ \ boldsymbol \ Sigma $ sind wie folgt.
\begin{align}
& \boldsymbol{\bar X} = (\boldsymbol x_1 - \boldsymbol{\bar x}, ..., \boldsymbol x_N - \boldsymbol{\bar x})^T \\
& \\
& \boldsymbol \Sigma = Var\bigl\{\boldsymbol{\bar X}\bigr\} = \frac{1}{N} \boldsymbol{\bar X}^T \boldsymbol{\bar X}
\end{align}
Wenn die Datenmatrix $ \ boldsymbol {\ bar X} $ linear durch einen bestimmten Koeffizientenvektor $ \ boldsymbol a_j $ konvertiert wird und $ \ boldsymbol s_j $ verwendet wird, ist die Verteilung der konvertierten Daten wie folgt.
\begin{align}
& \boldsymbol s_j = (s_{1j}, ..., s_{Nj})^T \\
& \\
& Var\bigl\{\boldsymbol s_j \bigr\} \varpropto \boldsymbol s_j^T \boldsymbol s_j = \bigl(\boldsymbol{\bar X} \boldsymbol a_j \bigr)^T \boldsymbol{\bar X} \boldsymbol a_j = \boldsymbol a_j^T \boldsymbol{\bar X}^T \boldsymbol{\bar X} \boldsymbol a_j \varpropto \boldsymbol a_j^T Var\bigl\{\boldsymbol{\bar X}\bigr\} \boldsymbol a_j
\end{align}
Der Projektionsvektor $ \ boldsymbol a_j $, der diese Varianz maximiert, wird durch Maximieren der Lagrange-Funktion mit der auf $ 1 $ beschränkten Norm erhalten.
\begin{align}
& E(\boldsymbol a_j) = \boldsymbol a_j^T Var\bigl\{\boldsymbol{\bar X}\bigr\} \boldsymbol a_j - \lambda (\boldsymbol a_j^T \boldsymbol a_j - 1) \\
& \\
& \frac{\partial E(\boldsymbol a_j)}{\partial \boldsymbol a_j} = 2 Var\bigl\{\boldsymbol{\bar X}\bigr\} \boldsymbol a_j - 2 \lambda \boldsymbol a_j = 0 \\
& \\
& Var\bigl\{\boldsymbol{\bar X}\bigr\} \boldsymbol a_j = \lambda \boldsymbol a_j \tag{*}
\end{align}
Die Gleichung ($ \ ast $) zeigt, dass durch Lösen des Eigenwertproblems für die Kovarianzmatrix der Originaldaten ein Projektionsvektor $ \ boldsymbol a_j $ erhalten werden kann, der die Varianz maximiert. Der durch Lösen der Gleichung ($ \ ast $) erhaltene Eigenwert sei $ \ lambda_1 \ geq ... \ geq \ lambda_d $, und der entsprechende Eigenvektor sei $ \ boldsymbol a_1, ..., \ boldsymbol a_d $. Da die Kovarianzmatrix eine reelle symmetrische Matrix ist, sind die Eigenvektoren orthogonal zueinander.
\boldsymbol a_i^T \boldsymbol a_j = \delta_{ij} = \begin{cases}
1 \ ( \ i = j \ ) \\
\\
0 \ ( \ i \ne j \ )
\end{cases}
Die Merkmalsgröße, die vom Eigenvektor entsprechend dem maximalen Eigenwert linear transformiert wird, ist die Hauptkomponente $ 1 $. Die Merkmalsgröße, die vom Eigenvektor entsprechend dem $ k $ -ten Eigenwert linear transformiert wird, wird als $ k $ Hauptkomponente bezeichnet. Der Gesamtstreuungsbetrag $ V_ {total} $, der Beitragssatz der Hauptkomponente $ k $ c_k $ und der kumulative Beitragssatz $ r_k $ bis zur Hauptkomponente $ k $ sind wie folgt.
\begin{align}
& V_{total} = \sum_{i = 1}^d \lambda_i \\
& \\
& c_k = \frac{\lambda_k}{V_{total}} \\
& \\
& r_k = \frac{\sum_{i = 1}^k \lambda_i}{V_{total}}
\end{align}
Wählen Sie $ D_y $ Eigenvektoren von $ \ boldsymbol \ Sigma $ in absteigender Reihenfolge der Eigenwerte aus und lassen Sie die Matrix, die dies als Zeilenvektor speichert, $ \ boldsymbol U_ {D_y} $ sein. Wenn die Anzahl der Dimensionen der Eingabedaten $ D_x $ ist, werden die Dimensionen komprimiert, wenn $ D_y <D_x $. $ \ Boldsymbol U _ {D_y} $ und $ \ boldsymbol {\ bar x} $ sind die Lösungen für das folgende Minimierungsproblem $ (\ boldsymbol \ Gamma, \ boldsymbol \ xi) = (\ boldsymbol U_ {D_y}, \ boldsymbol Es ist {\ bar x}) $.
\min_{\boldsymbol \Gamma, \boldsymbol \xi} \sum_{n = 1}^N \bigl\| \ (\boldsymbol x_n - \boldsymbol \xi) - \boldsymbol \Gamma^T \boldsymbol \Gamma(\boldsymbol x_n - \boldsymbol \xi) \ \bigr\|^2 \tag{**}
In der Hauptkomponentenanalyse können alle Daten im $ D_x $ -Dimensionsraum so interpretiert werden, dass sie den $ D_y $ -Dimensionsunterraum ergeben, der am besten in dem Sinne darstellt, dass der quadratische Abstand minimiert wird.
$ \ Boldsymbol W = \ Boldsymbol \ Gamma, \ Boldsymbol b = - \ Boldsymbol \ Gamma \ Boldsymbol \ xi, \ Boldsymbol {\ Tilde W} = \ Boldsymbol \ Gamma ^ T, \ Wenn boldsymbol {\ tilte b} = \ boldsymbol \ xi
Ich habe es wie folgt implementiert. Der Selbstcodierer und die Hauptkomponentenanalyse werden separat aufgeführt.
auto encoder
Die Komprimierungsdimension wird als $ 50 $ verarbeitet. Mit einer Lernrate von $ \ varepsilon = 0,00001 $ drehen wir die Epoche von $ 10000 $.
auto_encoder.py
import numpy
class AutoEncoder:
def __init__(self, n_input, n_hidden):
self.hidden_weight = numpy.random.randn(n_hidden, n_input + 1)
self.output_weight = numpy.random.randn(n_input, n_hidden + 1)
def fit(self, X, epsilon, epoch):
self.error = numpy.zeros(epoch)
N = X.shape[0]
for epo in range(epoch):
print u'epoch: %d' % epo
for x in X:
self.__update_weight(x, epsilon)
self.error[epo] = self.__calc_error(X)
def encode(self, X):
Z = numpy.zeros((X.shape[0], self.hidden_weight.shape[0]))
Y = numpy.zeros(X.shape)
for (i, x) in enumerate(X):
z, y = self.__forward(x)
Z[i, :] = z
Y[i, :] = y
return (Z, Y)
def __forward(self, x):
z = self.hidden_weight.dot(numpy.hstack((1, x)))
y = self.output_weight.dot(numpy.hstack((1, z)))
return (z, y)
def __update_weight(self, x, epsilon):
z, y = self.__forward(x)
# update output_weight
output_delta = y - x
self.output_weight -= epsilon * output_delta.reshape(-1, 1) * numpy.hstack((1, z))
# update hidden_weight
hidden_delta = self.output_weight[:, 1:].T.dot(output_delta)
self.hidden_weight -= epsilon * hidden_delta.reshape(-1, 1) * numpy.hstack((1, x))
def __calc_error(self, X):
N = X.shape[0]
err = 0.0
for x in X:
_, y = self.__forward(x)
err += (y - x).dot(y - x) / 2.0
return err
main.py
import numpy
import cv2
from sklearn.datasets import fetch_mldata
from auto_encoder import AutoEncoder
if __name__ == '__main__':
print 'read data...'
mnist = fetch_mldata('MNIST original', data_home = '.')
_max = mnist.data[:, :-1].max()
X = mnist.data[:, :-1] * 1.0 / _max
input_size = X.shape[1]
hidden_size = 50
epsilon = 0.00001
epoch = 10000
stride = 50
print 'auto encoder init...'
auto = AutoEncoder(input_size, hidden_size)
print 'train...'
auto.fit(X[::stride], epsilon, epoch)
print 'encode...'
Z, Y = auto.encode(X[::stride])
for (i, y) in enumerate(Y * _max):
cv2.imwrite('result/%04d.png' % i, y.reshape(28, 28))
PCA
Die Komprimierungsdimension wird als $ 50 $ verarbeitet. Der Projektionsvektor und das decodierte Bild werden gespeichert.
pca.py
import numpy
class PCA:
def __init__(self, n_components):
self.n_components = n_components
def fit(self, X):
self.bar = self.__bar(X)
self.cov = self.__cov()
self.lamda, self.A, self.ccr = self.__solve_eigen_porblem()
self.S = self.__transformation()
def decode(self):
return self.S.dot(self.A.T)
def __bar(self, X):
return X - X.mean(axis = 0)
def __cov(self):
return numpy.cov(self.bar, rowvar = 0)
def __solve_eigen_porblem(self):
_lamda, _A = numpy.linalg.eig(self.cov)
lamda = _lamda[:self.n_components]
A = _A[:, :self.n_components]
ccr = lamda.sum() / _lamda.sum()
return (lamda, A, ccr)
def __transformation(self):
return self.bar.dot(self.A)
main.py
import cv2
from matplotlib import pyplot
from sklearn.datasets import fetch_mldata
import os
from pca import PCA
if __name__ == '__main__':
print 'read data...'
mnist = fetch_mldata('MNIST original', data_home = '.')
X = mnist.data
n_components = 50
p_dir = 'projection/'
d_dir = 'decoded/'
print 'train...'
pca = PCA(n_components)
pca.fit(X)
print 'cumulative contribution ratio: %f' % pca.ccr
print 'decode...'
Xhat = pca.decode()
print 'save projection vector...'
if not os.path.exists(p_dir):
os.mkdir(p_dir)
for (i, a) in enumerate(pca.A.T):
fig, ax = pyplot.subplots()
heatmap = ax.pcolor(a.reshape(28, 28)[:, ::-1], cmap = pyplot.cm.RdYlBu)
pyplot.savefig(p_dir + '%04d.png' % i, dpi = 25)
pyplot.clf()
print 'save decoded images...'
if not os.path.exists(d_dir):
os.mkdir(d_dir)
for (i, xhat) in enumerate(Xhat[::50]):
cv2.imwrite(d_dir + '%04d.png' % i, xhat.reshape(28, 28))
n_components = 30
pca = PCA(n_components)
pca.fit(X)
Xhat = pca.A.dot(pca.S.T)
for (i, xhat) in enumerate(Xhat.T):
cv2.imwrite('result/%04d.png' % i, xhat.reshape(28, 28))
Die folgenden Ergebnisse wurden erhalten.
auto encoder
Entschlüsseltes Bild
Error
Ich habe ein gut aussehendes Bild für das dekodierte Bild ausgewählt. Die auf die Dimension $ 50 $ komprimierte Feature-Menge kann in die Dimension $ 784 $ dekodiert werden. Im Fehlerdiagramm ist die horizontale Achse die Anzahl der Epochen und die vertikale Achse der Fehlerwert. Die Differenz zwischen den Ziffern des Fehlers ist zu groß, daher wird der Logarithmus verwendet. Es ist kein exakter Fehler, aber Sie können sehen, dass er monoton abnimmt. ..
PCA
Visualisierter Projektionsvektor
Entschlüsseltes Bild
Für den Projektionsvektor wurden 10 entsprechende Vektoren in absteigender Reihenfolge des Eigenwerts ausgewählt. Der Projektionsvektor oben links projiziert zur ersten Hauptkomponente mit der größten Streuung. Das decodierte Bild liefert gute Ergebnisse sowie den Selbstcodierer.
Wir konnten die Abmessungen durch Selbstcodierer- und Hauptkomponentenanalyse komprimieren.
Recommended Posts