硬件自动编码器与 PCA

keras autoencoder vs PCA

我正在玩一个玩具示例来了解 PCA 与 keras 自动编码器

我有以下理解PCA的代码:

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from sklearn import decomposition
from sklearn import datasets

iris = datasets.load_iris()
X = iris.data
pca = decomposition.PCA(n_components=3)
pca.fit(X)

pca.explained_variance_ratio_
array([ 0.92461621,  0.05301557,  0.01718514])

pca.components_
array([[ 0.36158968, -0.08226889,  0.85657211,  0.35884393],
       [ 0.65653988,  0.72971237, -0.1757674 , -0.07470647],
       [-0.58099728,  0.59641809,  0.07252408,  0.54906091]])

我用 keras 做了一些阅读和播放代码,包括 this one

但是,参考代码对我的理解水平来说感觉太高了。

有没有人有一个简短的自动编码器代码可以告诉我

(1) 如何从自动编码器中提取前 3 个组件

(2) 如何理解自动编码器捕获的方差量

(3) 自动编码器组件与 PCA 组件的比较

首先,自动编码器的目的是学习一组数据的表示(编码),通常是为了降维。因此,自动编码器的目标输出是自动编码器输入本身。

在[1]中表明,如果有一个线性隐藏层并且使用均方误差准则来训练网络,那么k个隐藏单元学习将输入投射到跨度中first k principal components 的数据。 在 [2] 中你可以看到,如果隐藏层是非线性的,自动编码器的行为与 PCA 不同,具有捕获输入分布的多模态方面的能力。

自动编码器是特定于数据的,这意味着它们只能压缩与它们所训练的数据相似的数据。因此,隐藏层学习到的特征的有用性可以用来评估该方法的有效性。

出于这个原因,评估自动编码器在降维方面的功效的一种方法是削减中间隐藏层的输出,并通过此降维数据而不是使用原始数据来比较所需算法的 accuracy/performance。 通常,PCA 是一种线性方法,而自动编码器通常是非线性的。从数学上讲,很难将它们放在一起比较,但凭直觉,我提供了一个使用自动编码器对 MNIST 数据集进行降维的示例,以便您更好地理解。代码在这里:

from keras.datasets import mnist 
from keras.models import Model 
from keras.layers import Input, Dense 
from keras.utils import np_utils 
import numpy as np

num_train = 60000
num_test = 10000

height, width, depth = 28, 28, 1 # MNIST images are 28x28
num_classes = 10 # there are 10 classes (1 per digit)

(X_train, y_train), (X_test, y_test) = mnist.load_data()

X_train = X_train.reshape(num_train, height * width)
X_test = X_test.reshape(num_test, height * width)
X_train = X_train.astype('float32') 
X_test = X_test.astype('float32')

X_train /= 255 # Normalise data to [0, 1] range
X_test /= 255 # Normalise data to [0, 1] range

Y_train = np_utils.to_categorical(y_train, num_classes) # One-hot encode the labels
Y_test = np_utils.to_categorical(y_test, num_classes) # One-hot encode the labels

input_img = Input(shape=(height * width,))

x = Dense(height * width, activation='relu')(input_img)

encoded = Dense(height * width//2, activation='relu')(x)
encoded = Dense(height * width//8, activation='relu')(encoded)

y = Dense(height * width//256, activation='relu')(x)

decoded = Dense(height * width//8, activation='relu')(y)
decoded = Dense(height * width//2, activation='relu')(decoded)

z = Dense(height * width, activation='sigmoid')(decoded)
model = Model(input_img, z)

model.compile(optimizer='adadelta', loss='mse') # reporting the accuracy

model.fit(X_train, X_train,
      epochs=10,
      batch_size=128,
      shuffle=True,
      validation_data=(X_test, X_test))

mid = Model(input_img, y)
reduced_representation =mid.predict(X_test)

out = Dense(num_classes, activation='softmax')(y)
reduced = Model(input_img, out)
reduced.compile(loss='categorical_crossentropy',
          optimizer='adam', 
          metrics=['accuracy']) 

reduced.fit(X_train, Y_train,
      epochs=10,
      batch_size=128,
      shuffle=True,
      validation_data=(X_test, Y_test))


 scores = reduced.evaluate(X_test, Y_test, verbose=1) 
 print("Accuracy: ", scores[1])

它产生一个 $y\in \mathbb{R}^{3}$ (几乎和你通过 decomposition.PCA(n_components=3) 得到的一样)。例如,在这里您可以看到数据集 5 中数字 5 实例的层 y 的输出:

  class  y_1    y_2     y_3     
  5      87.38  0.00    20.79

正如您在上面的代码中看到的,当我们将层 y 连接到 softmax 密集层时:

mid = Model(input_img, y)
reduced_representation =mid.predict(X_test)

新模型 mid 为我们提供了关于 95% 的良好分类精度。因此,可以合理地说 y 是数据集的有效提取特征向量。


参考文献:

[1]:Bourlard、Hervé 和 Yves Kamp。 "Auto-association by multilayer perceptrons and singular value decomposition." 生物控制论 59.4 (1988): 291-294.

[2]:Japkowicz、Nathalie、Stephen Jose Hanson 和 Mark A. Gluck。 "Nonlinear autoassociation is not equivalent to PCA." 神经计算 12.3 (2000): 531-545.

较早的答案涵盖了整个问题,但是我正在对 Iris 数据进行分析 - 我的代码对 this post 进行了轻微修改,进一步深入探讨了该主题。按照要求,让我们加载数据

from sklearn.datasets import load_iris
from sklearn.preprocessing import MinMaxScaler

iris = load_iris()
X = iris.data
y = iris.target
target_names = iris.target_names

scaler = MinMaxScaler()
scaler.fit(X)
X_scaled = scaler.transform(X)

让我们做一个常规的 PCA

from sklearn import decomposition
pca = decomposition.PCA()
pca_transformed = pca.fit_transform(X_scaled)
plot3clusters(pca_transformed[:,:2], 'PCA', 'PC') 

一个非常简单的带有线性层的 AE 模型,正如前面的答案所指出的... 第一个参考,一个线性隐藏层和均方误差标准用于训练网络,然后 k 个隐藏单元学习将输入投影到数据的前 k 个主成分的范围内。

from keras.layers import Input, Dense
from keras.models import Model
import matplotlib.pyplot as plt

#create an AE and fit it with our data using 3 neurons in the dense layer using keras' functional API
input_dim = X_scaled.shape[1]
encoding_dim = 2  
input_img = Input(shape=(input_dim,))
encoded = Dense(encoding_dim, activation='linear')(input_img)
decoded = Dense(input_dim, activation='linear')(encoded)
autoencoder = Model(input_img, decoded)
autoencoder.compile(optimizer='adam', loss='mse')
print(autoencoder.summary())

history = autoencoder.fit(X_scaled, X_scaled,
                epochs=1000,
                batch_size=16,
                shuffle=True,
                validation_split=0.1,
                verbose = 0)

# use our encoded layer to encode the training input
encoder = Model(input_img, encoded)
encoded_input = Input(shape=(encoding_dim,))
decoder_layer = autoencoder.layers[-1]
decoder = Model(encoded_input, decoder_layer(encoded_input))
encoded_data = encoder.predict(X_scaled)

plot3clusters(encoded_data[:,:2], 'Linear AE', 'AE')

有需要的可以去看看损失

#plot our loss 
plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.title('model train vs validation loss')
plt.ylabel('loss')
plt.xlabel('epoch')
plt.legend(['train', 'validation'], loc='upper right')
plt.show()

绘制数据的函数

def plot3clusters(X, title, vtitle):
    import matplotlib.pyplot as plt
    plt.figure()
    colors = ['navy', 'turquoise', 'darkorange']
    lw = 2

    for color, i, target_name in zip(colors, [0, 1, 2], target_names):
        plt.scatter(X[y == i, 0], X[y == i, 1], color=color, alpha=1., lw=lw, label=target_name)

    plt.legend(loc='best', shadow=False, scatterpoints=1)
    plt.title(title)  
    plt.xlabel(vtitle + "1")
    plt.ylabel(vtitle + "2")
    return(plt.show())

关于解释可变性,使用非线性隐藏函数,导致类似于 ICA / TSNE 等的其他近似。在不存在方差解释的想法的地方,仍然可以研究收敛性。