CNN(卷积神经网络)的理解+基于Tensorflow2.0卷积自编码器

1.卷积神经网络是神经网络的一种,可以很好的处理图像数据。卷积神经网络的思想是通过卷积核(滤波器),过滤不感兴趣的信息,提取数据的特征,也就是我们感兴趣的数据。卷积核一般是3*3或者5*5的矩阵,图像的像素矩阵和卷积核进行内积,得到原始图像的基本特征。CNN的主要流程有:卷积(卷积本质是一种线性运算,所以此步骤需要激活函数进行非线性化)、padding、池化、反卷积。一般没有设置移动步长的时候,默认是移动卷积核/池化的行的维度大小。卷积神经网络结构图如下:

CNN(卷积神经网络)的理解+基于Tensorflow2.0卷积自编码器_第1张图片

2.在CNN中主要有卷积层、padding层、池化层和反卷积层。卷积层是使用卷积核和图像的像素做内积,得到新的值,新的值代表了图像的基本特征。如下图:I中的每个点和K中的对应点做内积的过程就是卷积的过程。

CNN(卷积神经网络)的理解+基于Tensorflow2.0卷积自编码器_第2张图片

CNN(卷积神经网络)的理解+基于Tensorflow2.0卷积自编码器_第3张图片

3.padding层:如上图,在做卷积的过程中,图像的边缘点不好卷积,所以需要padding层填充。padding层的作用是在像素矩阵的边缘填充0或者1,方便边缘点进行卷积运算。如下图,在边缘添加0,方便边缘部分的卷积:

CNN(卷积神经网络)的理解+基于Tensorflow2.0卷积自编码器_第4张图片

4.池化层:抓住主要矛盾,忽略次要矛盾。取像素中能够进一步代表特征的值,忽略其他的值。通常池化层也是一个矩阵,在此矩阵对应大小的像素矩阵中,一般是选择最大值或者平均值来代表池化矩阵中的所有值,达到减少特征矩阵维度的目的。如下图,池化矩阵的大小是2*2,选择的是最大值,在覆盖左边原始图像的第一部分的时候,选择4(此部分的最大值),往右移动,再选择6(此部分的最大值),依次进行池化。

CNN(卷积神经网络)的理解+基于Tensorflow2.0卷积自编码器_第5张图片

5.反卷积层:也叫解卷积,还原卷积之后图像的维度。将原来矩阵分别沿着原来的数据阵列对应的倍数复制。比如维度为(7,7,1)的矩阵经过(2,2)的反卷积之后,其中维度会变成(14,14,1)

6.在设计卷积神经网络的过程中,经常涉及到卷积核和池化矩阵大小的设计问题,因此补充部分关于卷积层和池化层输出维度的知识。参考链接如下:卷积神经网络(CNN)张量(图像)的尺寸和参数计算(深度学习) - 冂冋冏囧 - 博客园

CNN(卷积神经网络)的理解+基于Tensorflow2.0卷积自编码器_第6张图片

CNN(卷积神经网络)的理解+基于Tensorflow2.0卷积自编码器_第7张图片

7.代码:

#基于卷积神经网络的自编码器
#使用函数式API的一个好处是可以调用自己需要的层,从而达到共享参数的目的:
#比如只需要训练一个完整的auto_coder模型,通过函数式API去调用:auto_coder.layers[x]
#就可以共享auto_coder.layers[x]的参数
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
import matplotlib.pyplot as plt
import numpy as np

#导入数据集
(x_train,y_train),(x_test,y_test)=keras.datasets.mnist.load_data()

#数据进行归一化处理,方便计算
#x_train.astype('float32')改变数据的类型
#expand_dims(array,axis)增加数据的维度,这里相当于把数据变成,每个数值是一个向量的列向量
#如[1,4]变成:[[1],
#              [4]]

x_train=tf.expand_dims(x_train.astype('float32'),-1)/255.0
x_test=tf.expand_dims(x_test.astype('float32'),-1)/255.0

print("x_train的数据类型:",type(x_train))
print(x_train.shape,'  ',y_train.shape)
print(x_test.shape,'  ',y_test.shape)

#模型构建
inputs=layers.Input(shape=(x_train.shape[1],x_train.shape[2],
                           x_train.shape[3]),name='inputs')

#layers.Conv2D(16,(3,3)):卷积核的个数=16,影响的是最后输入结果的第三个维度的变化;
# 卷积核的大小=(3,3)

#code=layers.Conv2D(1,(3,3),activation='relu',padding='same')(inputs)
code=layers.Conv2D(16,(3,3),activation='relu',padding='same')(inputs)
code=layers.MaxPool2D((2,2),padding='same')(code)

#加入四行代码:
code=layers.Conv2D(8,(3,3),activation='relu',padding='same')(code)
code=layers.MaxPool2D((2,2),padding='same')(code)
code=layers.Conv2D(1,(3,3),activation='relu',padding='same')(code)
# code=layers.MaxPool2D((2,2),padding='same')(code)
# print("当前code的shape:",code.shape)

#测试代码:
#code_test=layers.Dense(10,activation='softmax')(code)

#加入四行代码:
decode=layers.Conv2D(8,(3,3),activation='relu',padding='same',)(code)
decode=layers.UpSampling2D((2,2))(decode)
# decode=layers.Conv2D(8,(3,3),activation='relu',padding='same')(decode)
# decode=layers.UpSampling2D((2,2))(decode)

decode=layers.Conv2D(16,(3,3),activation='relu',padding='same',)(decode)

#解卷积
#pSampling2D((2,2))将原来矩阵分别沿着原来的数据阵列对应的倍数复制
decode=layers.UpSampling2D((2,2))(decode)
print("此处decode的维度是:",decode.shape)

outputs=layers.Conv2D(1,(1,3),activation='sigmoid',padding='same')(decode)
#outputs=layers.Conv2D(1,(6,6),activation='sigmoid',padding='same')(decode)
print("此处outputs的维度是:",outputs.shape)

CAE_coder=keras.Model(inputs,outputs)

CAE_encoder=keras.Model(inputs,code)

#加入解码器代码:
# print("layer[6]的shape:",np.array(CAE_coder.layers[6]).shape)
inputs1=keras.Input((7,7,1))

# decode1=layers.Conv2D(8,(3,3),activation='relu',padding='same',)(inputs1)
# decode1=layers.UpSampling2D((2,2))(decode1)
# decode1=layers.Conv2D(16,(3,3),activation='relu',padding='same',)(decode1)
# decode1=layers.UpSampling2D((2,2))(decode1)
# outputs1=layers.Conv2D(1,(1,3),activation='sigmoid',padding='same')(decode1)

decode1=CAE_coder.layers[6](inputs1)
decode1=CAE_coder.layers[7](decode1)
decode1=CAE_coder.layers[8](decode1)
decode1=CAE_coder.layers[9](decode1)
# decode1=CAE_coder.layers[10](decode1)
# print("layer[10]的shape:",CAE_coder.layers[10])
# decode1=CAE_coder.layers[11](decode1)
outputs1=CAE_coder.layers[-1](decode1)
# outputs1=CAE_coder.layers[10](decode1)
# outputs1=CAE_coder.layers[6:](decode1)

CAE_decoder=keras.Model(inputs1,outputs1)

CAE_coder.summary()

print("此处code的维度是:",code.shape) #code.shape返回的是一个元组
#构建编码器,下面是我的尝试代码:
# my_test_encoder=keras.Model(inputs,code)
# my_test_encoder.compile(optimizer=keras.optimizers.Adam(),
#                         loss=keras.losses.MAE,metrics='accuracy')
#
# print("my_test_encoder的结构如下:")
# my_test_encoder.summary()
#
# plt.imshow(x_test[0])
# plt.show()
# print("当前图像的维度是:",x_test[0].shape)
#
# my_test_result=my_test_encoder.predict(x_test)
# print("经过压缩后的数据的维度是:",my_test_result.shape)

#配置训练过程
CAE_coder.compile(optimizer=keras.optimizers.Adam(),
                    loss=keras.losses.BinaryCrossentropy(),metrics='accuracy')

#keras.utils.plot_model(CAE_encoder,show_shapes=True)

#EarlyStopping是callbacks的一种,用于提前停止训练,比如可以当loss不再减少的时候,停止训练
#patience:可以容忍多少个epoch内没有(准确率等判断精度的标准)提升
#monitor:监控数据的接口,比如准确率、损失值等等
early_stoping=keras.callbacks.EarlyStopping(patience=2,monitor='loss')

history=CAE_coder.fit(x_train,x_train,batch_size=64,epochs=1,
                        validation_split=0.1,validation_freq=10,
                        callbacks=early_stoping)

# #保存模型:
# CAE_encoder.save('CAE_encoder.h5')
# #CAE_encoder.summary()
#
# #加载模型:
# CAE_encoder=tf.keras.models.load_model('CAE_encoder.h5')
#
# #显示原始数据和重构数据:
# decode=CAE_coder.predict(x_test)

encode=CAE_encoder.predict(x_test)
decode=CAE_decoder.predict(encode)

n=5
plt.figure(figsize=(10,4))
for i in range(n):
    #显示原始数据
    ax=plt.subplot(2,n,i+1)
    plt.imshow(tf.reshape(x_test[i+1],(28,28)))
    plt.gray()
    ax.get_xaxis().set_visible(False)
    ax.get_yaxis().set_visible(False)

    #显示重构数据
    ax=plt.subplot(2,n,i+n+1)
    plt.imshow(tf.reshape(decode[i+1],(28,28)))
    plt.gray()
    ax.get_xaxis().set_visible(False)
    ax.get_yaxis().set_visible(False)

plt.show()






8.运行结果:

CNN(卷积神经网络)的理解+基于Tensorflow2.0卷积自编码器_第8张图片

 

 

9.总结:卷积神经网络是通过卷积核去提取图像的基本特征,为了方便边缘部分的卷积,加入了padding层;而加入的池化层可以进一步的减少数据的维度;反卷积是还原数据的维度。关于卷积神经网络的知识,自己的理解只是冰山一角,希望通过博客的方式记录自己学习的进程,同时,期待和大家交流更多这方面的知识。

在使用函数API构建模型的时候,注意输入和输出部分的名称要和中间部分不一样。具体原因可能是因为如果输入/输出的名称和中间部分一样,在调用的时候会导致错误。自己在写代码的过程中就是因为输入/输出和中间部分起的名称一样,导致找了好久的错误。

CNN(卷积神经网络)的理解+基于Tensorflow2.0卷积自编码器_第9张图片

 

 

关于代码,在第7部分中对于代码的每一部分都有解释。如果我写的不够清晰,可以联系我哈。

你可能感兴趣的