学习使用Python中的Keras包,利用自动编码器Autoencoder,分析手写字体数据。
首先是准备工作,程序如下:
## 加载包
%matplotlib inline
%config InlineBackend.figure_format='jpeg'
from IPython.display import Image
import numpy as np
import pandas as pd
from keras.models import Model
from keras.layers import Dense, Dropout, Input
import keras as K
from sklearn.preprocessing import StandardScaler,OneHotEncoder
from sklearn import metrics
import matplotlib.pyplot as plt
import seaborn as sns
## 导入数据
(x_train, y_train), (x_test, y_test) = K.datasets.mnist.load_data()
print(x_train.shape)
print(y_train.shape)
print(x_test.shape)
print(y_test.shape)
(60000, 28, 28)
(60000,)
(10000, 28, 28)
(10000,)
数据中一共有60000张28x28的训练数据,10000张28x28的测试数据。因为下面将要使用的层都是全连接层,所以要将每张图像转化为1维向量。
# 图像数据进行预处理
x_train = x_train.astype('float32') / 255.
x_test = x_test.astype('float32') / 255.
## 更改数据尺寸
x_train = x_train.reshape((x_train.shape[0], -1))
x_test = x_test.reshape((x_test.shape[0], -1))
经过变化话,训练集为60000x784的矩阵,测试集为10000x784的矩阵。
搭建自动编码器网络框架
## 使用全连接层进行连接
input_img = Input(shape=(784,))
## encoded部分
encoded = Dense(128,activation="relu",name = "enco1")(input_img)
encoded = Dense(64,activation="relu",name = "enco2")(encoded)
encoded = Dense(32,activation="relu",name = "enco3")(encoded)
encoded = Dense(16,activation="relu",name = "enco4")(encoded)
encoded = Dense(8,activation="relu",name = "enco5")(encoded)
## decoded部分
decoded = Dense(16,activation="relu",name = "deco1")(encoded)
decoded = Dense(32,activation="relu",name = "deco2")(decoded)
decoded = Dense(64,activation="relu",name = "deco3")(decoded)
decoded = Dense(128,activation="relu",name = "deco4")(decoded)
decoded = Dense(784,activation="sigmoid",name = "deco5")(decoded)
## 连接为自编码模型
autoencoder = Model(input=input_img, output=decoded)
autoencoder.summary()
Layer (type) Output Shape Param #
=================================================================
input_1 (InputLayer) (None, 784) 0
_________________________________________________________________
enco1 (Dense) (None, 128) 100480
_________________________________________________________________
enco2 (Dense) (None, 64) 8256
_________________________________________________________________
enco3 (Dense) (None, 32) 2080
_________________________________________________________________
enco4 (Dense) (None, 16) 528
_________________________________________________________________
enco5 (Dense) (None, 8) 136
_________________________________________________________________
deco1 (Dense) (None, 16) 144
_________________________________________________________________
deco2 (Dense) (None, 32) 544
_________________________________________________________________
deco3 (Dense) (None, 64) 2112
_________________________________________________________________
deco4 (Dense) (None, 128) 8320
_________________________________________________________________
deco5 (Dense) (None, 784) 101136
=================================================================
Total params: 223,736
Trainable params: 223,736
Non-trainable params: 0
_________________________________________________________________
上面的自编码器框架中,会将784维的图像编码为8维,然后再解码到784维。在模型编译时,使用逐像素的交叉熵作为损失函数,优化器为adam。
## 使用逐像素的交叉熵作为损失函数,优化器为adam
autoencoder.compile(optimizer='adadelta', loss='binary_crossentropy')
针对0~1范围内的取值进行训练
## 模型训练
autoencoder_fit = autoencoder.fit(x_train, x_train,nb_epoch=100,batch_size=256,
shuffle=True,validation_data=(x_test,x_test),verbose=0)
## 绘制迭代次数和loss之间的关系
## 绘制图像
plt.figure(figsize=(10,6.5))
plt.plot(autoencoder_fit.epoch,autoencoder_fit.history["loss"],"ro-",lw = 2)
plt.plot(autoencoder_fit.epoch,autoencoder_fit.history["val_loss"],"bs-",lw = 2)
plt.grid()
plt.xlabel("Model epoch")
plt.ylabel("binary_crossentropy")
plt.title("Autoencoder")
plt.show()
上面是针对数据进行epoch=100的迭代训练。冰晶损失函数的取值进行可视化。
可以发现,经过100轮的训练后,模型几乎已经收敛。下面获取测试数据集经过编码后的8维特征,并对其进行可视化。
## 计算测试数据的Encoded,并可视化
## 先定义encoder模型作为输出
encoder = Model(input_img, encoded)
## 获取测试集的encoded
encoded_imgs = encoder.predict(x_test)
print(encoded_imgs.shape)
colna = ["feature"+str(ii+1) for ii in range(8)]
encoded_feture = pd.DataFrame(data = encoded_imgs,columns=colna)
encoded_feture["lab"] = ["cla_"+str(i) for i in y_test]
print(encoded_feture.head())
## 使用矩阵散点图可视化自编码的点分布
sns.pairplot(encoded_feture,hue="lab",size=3,diag_kind=None)
plt.title("Encoded feature")
plt.show()
feature1 feature2 feature3 feature4 feature5 feature6 feature7 \
0 12.116272 0.0 12.858566 15.669122 2.838675 10.398287 9.041433
1 1.157638 0.0 5.085598 7.185266 8.355832 4.039139 3.556945
2 4.089868 0.0 16.886538 20.305134 12.612089 17.451355 10.378060
3 12.908182 0.0 6.505641 5.012246 14.547909 6.277741 5.777000
4 18.666761 0.0 12.316364 8.246981 6.462950 10.300065 9.926409
feature8 lab
0 0.0 cla_7
1 0.0 cla_2
2 0.0 cla_1
3 0.0 cla_0
4 0.0 cla_4编码后特征矩阵散点图
下面对比编码前原始图像和解码后的图像,对比前几张图像。
## 可视化对比自编码前后的图像
decoded_imgs = autoencoder.predict(x_test)
n = 10
plt.figure(figsize=(20, 4))
for i in range(n):
# display original
plt.subplot(2, n, i+1)
plt.imshow(x_test[i].reshape(28, 28))
plt.gray()
plt.axis("off")
# display reconstruction
plt.subplot(2, n, i + n+1)
plt.imshow(decoded_imgs[i].reshape(28, 28))
plt.gray()
plt.axis("off")
plt.show()自编码图像前后对比
图像中第一行是原始的图像,第二行是对应的重新编码后的输出图像。上面的处理使用的数据没有经过标准化,下面对数据进行标准化处理,然后使用自编码器进行处理。
数据标准化的自动编码器
## 数据标准化
scale = StandardScaler()
x_train_s = scale.fit_transform(x_train)
x_test_s = scale.fit_transform(x_test)
## 使用逐像素的交叉熵作为损失函数,优化器为adam
autoencoder.compile(optimizer='adadelta', loss='binary_crossentropy')
## 模型训练,使用标准化后的数据
autoencoder_fit = autoencoder.fit(x_train_s, x_train_s,nb_epoch=100,batch_size=256,
shuffle=True,validation_data=(x_test_s,x_test_s),verbose=0)
## 绘制迭代次数和loss之间的关系
## 绘制图像
plt.figure(figsize=(10,6.5))
plt.plot(autoencoder_fit.epoch,autoencoder_fit.history["loss"],"ro-",lw = 2)
plt.plot(autoencoder_fit.epoch,autoencoder_fit.history["val_loss"],"bs-",lw = 2)
plt.grid()
plt.xlabel("Model epoch")
plt.ylabel("binary_crossentropy")
plt.title("Autoencoder")
plt.show()
不知为何损失函数的取值是负值。同样经过100轮的训练,模型收敛。
## 计算测试数据的Encoded,并可视化
## 先定义encoder模型作为输出
encoder = Model(input_img, encoded)
## 获取测试集的encoded
encoded_imgs = encoder.predict(x_test_s)
print(encoded_imgs.shape)
colna = ["feature"+str(ii+1) for ii in range(8)]
encoded_feture = pd.DataFrame(data = encoded_imgs,columns=colna)
encoded_feture["lab"] = ["cla_"+str(i) for i in y_test]
print(encoded_feture.head())
## 使用矩阵散点图可视化自编码的点分布
sns.pairplot(encoded_feture,hue="lab",size=3,diag_kind=None)
plt.title("Encoded feature")
plt.show()
直观上感受,编码得到的8维特征的矩阵散点图,并没有上面的矩阵散点图效果好。
## 可视化对比自编码前后的图像
decoded_imgs = autoencoder.predict(x_test_s)
n = 10
plt.figure(figsize=(20, 4))
for i in range(n):
# display original
plt.subplot(2, n, i+1)
plt.imshow(x_test_s[i].reshape(28, 28))
plt.gray()
plt.axis("off")
# display reconstruction
plt.subplot(2, n, i + n+1)
plt.imshow(decoded_imgs[i].reshape(28, 28))
plt.gray()
plt.axis("off")
plt.show()
第一行为标准化后的图像数据,第二行为自动编码器重建的图像。