ITPub博客

首页 > 数据库 > 数据库开发技术 > 自动编码器是如何完成图像、序列等一系列预测任务的

自动编码器是如何完成图像、序列等一系列预测任务的

原创 数据库开发技术 作者:quincyqiang 时间:2021-04-20 08:06:00 0 删除 编辑

前言

在之前的文章中,我们介绍了自动编码器的半监督学习用于欺诈检测任务,这是一个出色的学习方法,那么自动编码器是如何实现这些任务的呢?本篇文章将从下面几个方面进行阐述并给出图像、序列的案例应用:

目录

1.介绍

  • 1.1.什么是自动编码器

  • 1.2.自动编码器是如何工作的

2.实现和使用案例

  • 2.1.使用案例1:图像重建

  • 2.2.使用案例2:图像去噪技术

  • 2.3.使用案例3:序列到序列预测

1.介绍

1.1.什么是自动编码器

自动编码器是一种特殊类型的神经网络结构,其中的输出和输入是相同的。自动编码器以无监督的方式进行训练,以便学习输入数据的极低级别表示。然后,这些低级特征会被转换,以映射实际数据。一个自动编码器是一个回归任务,网络被要求预测它的输入(换句话说,模型定义函数)。这些网络中间有几个神经元构成的瓶颈,迫使它们产生有效的表示,将输入压缩成低编码,以便解码器重现原始输入。

一个典型的自动编码器架构由三个主要组件组成:

  • 编码体系结构:编码器体系结构由一系列层组成,节点数量逐渐减少,最终减少为一个潜在的视图表示。

  • 潜在视图表示:潜在视图表示输入减少和信息保存的最低层空间。

  • 解码体系结构:解码体系结构是编码体系结构的镜像,但在其中每一层的节点数量增加,并最终输出几乎类似的输入。

一个高度微调的自编码器模型应该能够重建在第一层传递的相同输入。在这个内核中,我将带领你了解自动编码器的工作和它们的实现。自动编码器被广泛地用于图像数据,它们的一些用例是:

  • 边缘减少
  • 图像压缩
  • 图像去噪
  • 图像生成
  • 特征提取

1.2.自动编码器是如何工作的

让我们理解自动编码器背后的数学原理。自动编码器背后的主要思想是学习高层次数据的低层再现。让我们通过一个例子来理解编码过程。考虑一个数据表示空间(用于表示数据的N维空间),并考虑由两个变量 和 表示的数据点。数据流形是数据表示空间中真实数据所在的空间。

from plotly.offline import init_notebook_mode, iplot
import plotly.graph_objs as go
import numpy as np
init_notebook_mode(connected=True)

## generate random data
N = 50
random_x = np.linspace(2, 10, N)
random_y1 = np.linspace(2, 10, N)
random_y2 = np.linspace(2, 10, N)

trace1 = go.Scatter(x = random_x, y = random_y1, mode="markers", name="Actual Data")
trace2 = go.Scatter(x = random_x, y = random_y2, mode="lines", name="Model")
layout = go.Layout(title="2D Data Repersentation Space", xaxis=dict(title="x2", range=(0,12)), 
                   yaxis=dict(title="x1", range=(0,12)), height=400, 
                   annotations=[dict(x=5, y=5, xref='x', yref='y', text='This 1D line is the Data Manifold (where data resides)',
                   showarrow=True, align='center', arrowhead=2, arrowsize=1, arrowwidth=2, arrowcolor='#636363',
                   ax=-120, ay=-30, bordercolor='#c7c7c7', borderwidth=2, borderpad=4, bgcolor='orange', opacity=0.8)])
figure = go.Figure(data = [trace1], layout = layout)
iplot(figure)

为了表示这些数据,我们目前使用的是二维——X和Y。但可以将这个空间的维数减少为更低的维数1 d。如果我们能定义以下:

  • 直线上的参考点:A

  • 与水平轴的角度L

那么任何其他点,从A和角L出发,比如A线上的B点,都可以用距离“d”来表示; 。

random_y3 = [2 for i in range(100)]
random_y4 = random_y2 + 1
trace4 = go.Scatter(x = random_x[4:24], y = random_y4[4:300], mode="lines")
trace3 = go.Scatter(x = random_x, y = random_y3, mode="lines")
trace1 = go.Scatter(x = random_x, y = random_y1, mode="markers")
trace2 = go.Scatter(x = random_x, y = random_y2, mode="lines")
layout = go.Layout(xaxis=dict(title="x1", range=(0,12)), yaxis=dict(title="x2", range=(0,12)), height=400,
                   annotations=[dict(x=2, y=2, xref='x', yref='y', text='A', showarrow=True, align='center', arrowhead=2, arrowsize=1, arrowwidth=2, 
                                     arrowcolor='#636363', ax=20, ay=-30, bordercolor='#c7c7c7', borderwidth=2, borderpad=4, bgcolor='orange', opacity=0.8), 
                                dict(x=6, y=6, xref='x', yref='y', text='B', showarrow=True, align='center', arrowhead=2, arrowsize=1, arrowwidth=2, arrowcolor='#636363',
                                     ax=20, ay=-30, bordercolor='#c7c7c7', borderwidth=2, borderpad=4, bgcolor='yellow', opacity=0.8), dict(
                                     x=4, y=5, xref='x', yref='y',text='d', ay=-40), 
                                dict(x=2, y=2, xref='x', yref='y', text='angle L', ax=80, ay=-10)], title="2D Data Repersentation Space", showlegend=False)
data = [trace1, trace2, trace3, trace4]
figure = go.Figure(data = data, layout = layout)
iplot(figure)



#################

random_y3 = [2 for i in range(100)]
random_y4 = random_y2 + 1
trace4 = go.Scatter(x = random_x[4:24], y = random_y4[4:300], mode="lines")
trace3 = go.Scatter(x = random_x, y = random_y3, mode="lines")
trace1 = go.Scatter(x = random_x, y = random_y1, mode="markers")
trace2 = go.Scatter(x = random_x, y = random_y2, mode="lines")
layout = go.Layout(xaxis=dict(title="u1", range=(1.5,12)), yaxis=dict(title="u2", range=(1.5,12)), height=400,
                   annotations=[dict(x=2, y=2, xref='x', yref='y', text='A', showarrow=True, align='center', arrowhead=2, arrowsize=1, arrowwidth=2, 
                                     arrowcolor='#636363', ax=20, ay=-30, bordercolor='#c7c7c7', borderwidth=2, borderpad=4, bgcolor='orange', opacity=0.8), 
                                dict(x=6, y=6, xref='x', yref='y', text='B', showarrow=True, align='center', arrowhead=2, arrowsize=1, arrowwidth=2, arrowcolor='#636363',
                                     ax=20, ay=-30, bordercolor='#c7c7c7', borderwidth=2, borderpad=4, bgcolor='yellow', opacity=0.8), dict(
                                     x=4, y=5, xref='x', yref='y',text='d', ay=-40), 
                                dict(x=2, y=2, xref='x', yref='y', text='angle L', ax=80, ay=-10)], title="Latent Distance View Space", showlegend=False)
data = [trace1, trace2, trace3, trace4]
figure = go.Figure(data = data, layout = layout)
iplot(figure)

但是这里的关键问题是用什么逻辑或规则,B点可以表示A和角L,或以其他的术语来说,什么是方程中B, A和L。答案是否定的,没有固定的方程, 但最好的获得的方程是无监督学习过程。简单来说,学习过程可以定义为一个规则/方程,它将B转换成a和L的形式。让我们从自动编码器的角度来理解这个过程。

考虑没有隐藏层的自动编码器,输入x1和x2被编码到较低的表示d,然后进一步映射到 和 。

Step1:表示潜在视图空间中的点

如果A点和B点在数据表示空间中的坐标为:

  • Point A : (x1A, x2A)

  • Point B : (x1B, x2B)

则它们在潜在视图空间中的坐标为:

(x1A, x2A) ---> (0, 0)

(x1B, x2B) ---> (u1B, u2B)

  • Point A : (0, 0)

  • Point B : (u1B, u2B)

其中u1B和u2B可以用点到参考点的距离来表示:

  • u1B = x1B - x1A

  • u2B = x2B - x2A

Step2:表示距离为d,角度为L的点

现在,u1B和u2B可以表示为距离d和角度L的组合,如果我们将它旋转角度L,朝向水平轴,L将变成0。ie:

=> (d, L)

=> (d, 0) (after rotation)

这是编码过程的输出,用低维表示我们的数据。如果我们回忆一个神经网络的基本方程,每个层的权值和偏差,那么:

=> (d, 0) = W. (u1B, u2B)

==> (encoding)

其中W为隐含层的权重矩阵。因为,我们知道解码过程是编码过程的镜像:

=> (u1B, u2B) = Inverse (W) . (d, 0)

==> (decoding)

从编码体系结构得到的潜在视图空间中,数据()的约简形式为(d, 0)。类似地,解码架构将这个表示转换回原始形式(u1B, u2B),然后()。重要的一点是,规则/学习函数/编码-解码方程对于不同类型的数据是不同的。例如,考虑二维空间中的以下数据:

对于不同的数据采用不同的规则

同样的规则不能应用于所有类型的数据。例如,在前面的例子中,我们在一维中映射一个线性数据流形,并消除了角度L。但是,如果数据流形不能正确映射怎么办?例如,考虑下面的数据流形视图:

import matplotlib.pyplot as plt 
import numpy as np
fs = 100 # sample rate 
f = 2 # the frequency of the signal
x = np.arange(fs) # the points on the x axis for plotting
y = [ np.sin(2*np.pi*f * (i/fs)) for i in x]

% matplotlib inline
plt.figure(figsize=(15,4))
plt.stem(x,y, 'r', );
plt.plot(x,y);

在这类数据中,关键问题是如何在不丢失信息的情况下获得数据的一维映射。当这类数据被映射到潜在空间时,大量的信息丢失了,几乎不可能变形和映射到原始的形状。无论多少转化和倾斜角度,原始数据都无法恢复。

那么神经网络是如何解决这个问题的呢?我们的直觉是,在流形空间中,深度神经网络具有弯曲空间以获得线性数据折叠视图的性质。自动编码器架构在其隐藏层中应用了这个属性,这允许他们学习潜在视图空间中的低级表示。

下图描述了该属性:

2.实现

2.1.图像重建

1.)加载需要的包

## load the libraries 
from keras.layers import Dense, Input, Conv2D, LSTM, MaxPool2D, UpSampling2D
from sklearn.model_selection import train_test_split
from keras.callbacks import EarlyStopping
from keras.utils import to_categorical
from numpy import argmax, array_equal
import matplotlib.pyplot as plt
from keras.models import Model
from imgaug import augmenters
from random import randint
import pandas as pd
import numpy as np

2.)数据准备

加载数据集,分离预测器和目标,规范化输入。

### read dataset 
train = pd.read_csv("../input/fashion-mnist_train.csv")
train_x = train[list(train.columns)[1:]].values
train_y = train['label'].values

## normalize and reshape the predictors  
train_x = train_x / 255

## create train and validation datasets
train_x, val_x, train_y, val_y = train_test_split(train_x, train_y, test_size=0.2)

## reshape the inputs
train_x = train_x.reshape(-1, 784)
val_x = val_x.reshape(-1, 784)

3.)创建Autoencoder架构

在本节中,让我们创建一个自动编码器架构。编码部分包括2000、1200、500个节点的三层。编码架构连接到由10个节点组成的潜在视图空间,然后连接到有500个节点、1200个节点和2000个节点的解码架构。最后一层包括作为输入层的精确节点数量。

## input layer
input_layer = Input(shape=(784,))

## encoding architecture
encode_layer1 = Dense(1500, activation='relu')(input_layer)
encode_layer2 = Dense(1000, activation='relu')(encode_layer1)
encode_layer3 = Dense(500, activation='relu')(encode_layer2)

## latent view
latent_view   = Dense(10, activation='sigmoid')(encode_layer3)

## decoding architecture
decode_layer1 = Dense(500, activation='relu')(latent_view)
decode_layer2 = Dense(1000, activation='relu')(decode_layer1)
decode_layer3 = Dense(1500, activation='relu')(decode_layer2)

## output layer
output_layer  = Dense(784)(decode_layer3)

model = Model(input_layer, output_layer)

下面是我们的自动编码器架构的摘要。

model.summary()
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_1 (InputLayer)         (None, 784)               0         
_________________________________________________________________
dense_1 (Dense)              (None, 1500)              1177500   
_________________________________________________________________
dense_2 (Dense)              (None, 1000)              1501000   
_________________________________________________________________
dense_3 (Dense)              (None, 500)               500500    
_________________________________________________________________
dense_4 (Dense)              (None, 10)                5010      
_________________________________________________________________
dense_5 (Dense)              (None, 500)               5500      
_________________________________________________________________
dense_6 (Dense)              (None, 1000)              501000    
_________________________________________________________________
dense_7 (Dense)              (None, 1500)              1501500   
_________________________________________________________________
dense_8 (Dense)              (None, 784)               1176784   
=================================================================
Total params: 6,368,794
Trainable params: 6,368,794
Non-trainable params: 0
_________________________________________________________________

接下来,我们将用早期停止回调训练模型。

model.compile(optimizer='adam', loss='mse')
early_stopping = EarlyStopping(monitor='val_loss', min_delta=0, patience=10, verbose=1, mode='auto')
model.fit(train_x, train_x, epochs=20, batch_size=2048, validation_data=(val_x, val_x), callbacks=[early_stopping])
Train on 48000 samples, validate on 12000 samples
Epoch 1/20
48000/48000 [==============================] - 5s 104us/step - loss: 0.0966 - val_loss: 0.0697
Epoch 2/20
48000/48000 [==============================] - 2s 35us/step - loss: 0.0646 - val_loss: 0.0579
Epoch 3/20
48000/48000 [==============================] - 2s 35us/step - loss: 0.0511 - val_loss: 0.0440
Epoch 4/20
48000/48000 [==============================] - 2s 34us/step - loss: 0.0416 - val_loss: 0.0392
Epoch 5/20
48000/48000 [==============================] - 2s 35us/step - loss: 0.0373 - val_loss: 0.0356
Epoch 6/20
48000/48000 [==============================] - 2s 35us/step - loss: 0.0341 - val_loss: 0.0322
Epoch 7/20
48000/48000 [==============================] - 2s 35us/step - loss: 0.0308 - val_loss: 0.0295
Epoch 8/20
48000/48000 [==============================] - 2s 34us/step - loss: 0.0284 - val_loss: 0.0268
Epoch 9/20
48000/48000 [==============================] - 2s 35us/step - loss: 0.0261 - val_loss: 0.0254
Epoch 10/20
48000/48000 [==============================] - 2s 35us/step - loss: 0.0245 - val_loss: 0.0241
Epoch 11/20
48000/48000 [==============================] - 2s 35us/step - loss: 0.0239 - val_loss: 0.0233
Epoch 12/20
48000/48000 [==============================] - 2s 35us/step - loss: 0.0229 - val_loss: 0.0227
Epoch 13/20
48000/48000 [==============================] - 2s 35us/step - loss: 0.0223 - val_loss: 0.0219
Epoch 14/20
48000/48000 [==============================] - 2s 35us/step - loss: 0.0217 - val_loss: 0.0217
Epoch 15/20
48000/48000 [==============================] - 2s 35us/step - loss: 0.0213 - val_loss: 0.0209
Epoch 16/20
48000/48000 [==============================] - 2s 35us/step - loss: 0.0209 - val_loss: 0.0209
Epoch 17/20
48000/48000 [==============================] - 2s 35us/step - loss: 0.0202 - val_loss: 0.0201
Epoch 18/20
48000/48000 [==============================] - 2s 35us/step - loss: 0.0199 - val_loss: 0.0199
Epoch 19/20
48000/48000 [==============================] - 2s 35us/step - loss: 0.0196 - val_loss: 0.0198
Epoch 20/20
48000/48000 [==============================] - 2s 35us/step - loss: 0.0193 - val_loss: 0.0190
<keras.callbacks.History at 0x7f41366a19e8>
  • 生成验证数据的预测。
preds = model.predict(val_x)
  • 让我们绘制原始图像和预测图像

input:真实图像

from PIL import Image 
f, ax = plt.subplots(1,5)
f.set_size_inches(80, 40)
for i in range(5):
    ax[i].imshow(val_x[i].reshape(28, 28))
plt.show()

output:自动编码输出

f, ax = plt.subplots(1,5)
f.set_size_inches(80, 40)
for i in range(5):
    ax[i].imshow(preds[i].reshape(28, 28))
plt.show()

所以我们可以看到一个用20个epochs训练的自动编码器能够很好地重建输入图像。让我们看看自动编码器的其他用例-图像去噪或从图像中去除噪声。

2.2.图像去噪

自动编码器是非常有用的,让我们看看另一个自动编码器的应用-图像去噪。很多时候输入的图像数据中含有噪声,自动编码器可以用来去除这些图像。让我们看看它的实际应用。首先让我们准备包含图像像素的train_x和val_x数据。

## recreate the train_x array and val_x array
train_x = train[list(train.columns)[1:]].values
train_x, val_x = train_test_split(train_x, test_size=0.2)

## normalize and reshape
train_x = train_x/255.
val_x = val_x/255.

在这个自动编码器网络中,我们将添加卷积层,因为卷积网络在图像输入方面工作得非常好。为了在图像数据上应用卷积,我们将把我们的输入重塑为28 * 28矩阵的形式。

train_x = train_x.reshape(-1, 28, 28, 1)
val_x = val_x.reshape(-1, 28, 28, 1)

噪声图像我们可以有意地在图像中引入噪声。我使用的imaug包,可以用来增强图像与不同的变化。其中一种变化就是引入噪声。不同类型的噪声可以添加到图像。例如

  • Salt and Pepper Noise
  • Gaussian Noise
  • Periodic Noise
  • Speckle Noise

让我们把Salt and Pepper噪声引入到我们的数据中,它也被称为脉冲噪声。这种噪声在图像信号中引入尖锐和突然的干扰。它以稀疏出现的白色和黑色像素呈现自己。

# Lets add sample noise - Salt and Pepper
noise = augmenters.SaltAndPepper(0.1)
seq_object = augmenters.Sequential([noise])

train_x_n = seq_object.augment_images(train_x * 255) / 255
val_x_n = seq_object.augment_images(val_x * 255) / 255
  • 添加噪声之前:
# Lets add sample noise - Salt and Pepper
noise = augmenters.SaltAndPepper(0.1)
seq_object = augmenters.Sequential([noise])

train_x_n = seq_object.augment_images(train_x * 255) / 255
val_x_n = seq_object.augment_images(val_x * 255) / 255
  • 添加噪声之后
f, ax = plt.subplots(1,5)
f.set_size_inches(80, 40)
for i in range(5,10):
    ax[i-5].imshow(train_x_n[i].reshape(28, 28))
plt.show()

现在让我们为自动编码器创建模型架构。让我们了解需要为这个问题创建什么类型的网络。

编码结构

编码架构由3个卷积层和3个最大池化层依次堆叠而成。Relu在卷积层中作为激活函数,填充保持“相同”。最大池化层的作用是向下采样图像的尺寸。这一层对初始表示的非重叠子区域应用最大过滤器。

解码结构

类似地,在解码体系结构中,卷积层将使用与编码体系结构相同的维度(以相反的方式)。但我们将添加3个upsampling层,而不是3个maxpooling层。同样,激活函数将是相同的(relu),卷积层中的填充也将是相同的。上采样层的作用是将输入向量的维度上采样到更高的分辨率/维度。最大池操作是不可逆转的,但是可以通过记录最大值在每个池区域内的位置来获得近似的逆。Umsampling层利用这一特性从低维特征空间投射重构。

input_layer = Input(shape=(28, 28, 1))

# encoding architecture
encoded_layer1 = Conv2D(64, (3, 3), activation='relu', padding='same')(input_layer)
encoded_layer1 = MaxPool2D( (2, 2), padding='same')(encoded_layer1)
encoded_layer2 = Conv2D(32, (3, 3), activation='relu', padding='same')(encoded_layer1)
encoded_layer2 = MaxPool2D( (2, 2), padding='same')(encoded_layer2)
encoded_layer3 = Conv2D(16, (3, 3), activation='relu', padding='same')(encoded_layer2)
latent_view    = MaxPool2D( (2, 2), padding='same')(encoded_layer3)

# decoding architecture
decoded_layer1 = Conv2D(16, (3, 3), activation='relu', padding='same')(latent_view)
decoded_layer1 = UpSampling2D((2, 2))(decoded_layer1)
decoded_layer2 = Conv2D(32, (3, 3), activation='relu', padding='same')(decoded_layer1)
decoded_layer2 = UpSampling2D((2, 2))(decoded_layer2)
decoded_layer3 = Conv2D(64, (3, 3), activation='relu')(decoded_layer2)
decoded_layer3 = UpSampling2D((2, 2))(decoded_layer3)
output_layer   = Conv2D(1, (3, 3), padding='same')(decoded_layer3)

# compile the model
model_2 = Model(input_layer, output_layer)
model_2.compile(optimizer='adam', loss='mse')
  • 模型结构参数
model_2.summary()
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_2 (InputLayer)         (None, 28, 28, 1)         0         
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 28, 28, 64)        640       
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 14, 14, 64)        0         
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 14, 14, 32)        18464     
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 7, 7, 32)          0         
_________________________________________________________________
conv2d_3 (Conv2D)            (None, 7, 7, 16)          4624      
_________________________________________________________________
max_pooling2d_3 (MaxPooling2 (None, 4, 4, 16)          0         
_________________________________________________________________
conv2d_4 (Conv2D)            (None, 4, 4, 16)          2320      
_________________________________________________________________
up_sampling2d_1 (UpSampling2 (None, 8, 8, 16)          0         
_________________________________________________________________
conv2d_5 (Conv2D)            (None, 8, 8, 32)          4640      
_________________________________________________________________
up_sampling2d_2 (UpSampling2 (None, 16, 16, 32)        0         
_________________________________________________________________
conv2d_6 (Conv2D)            (None, 14, 14, 64)        18496     
_________________________________________________________________
up_sampling2d_3 (UpSampling2 (None, 28, 28, 64)        0         
_________________________________________________________________
conv2d_7 (Conv2D)            (None, 28, 28, 1)         577       
=================================================================
Total params: 49,761
Trainable params: 49,761
Non-trainable params: 0
_________________________________________________________________

对模型进行早停回调训练。增加epochs的数量,以获得更好的结果。

early_stopping = EarlyStopping(monitor='val_loss', min_delta=0, patience=10, verbose=5, mode='auto')
history = model_2.fit(train_x_n, train_x, epochs=10, batch_size=2048, validation_data=(val_x_n, val_x), callbacks=[early_stopping])
Train on 48000 samples, validate on 12000 samples
Epoch 1/10
48000/48000 [==============================] - 11s 227us/step - loss: 0.0993 - val_loss: 0.0614
Epoch 2/10
48000/48000 [==============================] - 6s 125us/step - loss: 0.0488 - val_loss: 0.0397
Epoch 3/10
48000/48000 [==============================] - 6s 125us/step - loss: 0.0362 - val_loss: 0.0326
Epoch 4/10
48000/48000 [==============================] - 6s 125us/step - loss: 0.0302 - val_loss: 0.0277
Epoch 5/10
48000/48000 [==============================] - 6s 125us/step - loss: 0.0265 - val_loss: 0.0253
Epoch 6/10
48000/48000 [==============================] - 6s 125us/step - loss: 0.0246 - val_loss: 0.0237
Epoch 7/10
48000/48000 [==============================] - 6s 125us/step - loss: 0.0233 - val_loss: 0.0226
Epoch 8/10
48000/48000 [==============================] - 6s 125us/step - loss: 0.0223 - val_loss: 0.0217
Epoch 9/10
48000/48000 [==============================] - 6s 125us/step - loss: 0.0216 - val_loss: 0.0212
Epoch 10/10
48000/48000 [==============================] - 6s 125us/step - loss: 0.0209 - val_loss: 0.0204
  • 让我们得到模型的预测
preds = model_2.predict(val_x_n[:10])
f, ax = plt.subplots(1,5)
f.set_size_inches(80, 40)
for i in range(5,10):
    ax[i-5].imshow(preds[i].reshape(28, 28))
plt.show()

在这个实现中,我没有为更长的epochs训练这个网络,但是为了更好的预测,您可以为更大数量的epochs训练网络,比如在500 - 1000的某个范围内。

2.3.使用自动编码器的序列到序列预测

这个用例是序列到序列预测。在之前的例子中,我们输入的图像基本上是一个二维数据,在这个例子中,我们将输入一个序列数据作为输入,它将是一维的。序列数据的例子是时间序列数据和文本数据。该用例可以应用于机器翻译。与图像示例中的CNNs不同,在这个用例中,我们将使用LSTMs。

自动编码结构

这个用例的架构将包含一个编码器来编码源序列,第二个编码器将被编码的源序列解码为目标序列,称为解码器。首先让我们了解将在这个体系结构中使用的LSTMs的内部工作。

  • 长短期记忆,简称LSTM,是一种由内部门组成的循环神经网络。
  • 与其他递归神经网络不同的是,该网络的内部门允许通过时间反向传播(BPTT)成功地训练模型,并避免了梯度消失问题。
  • 我们可以在LSTM层中定义LSTM内存单元的数量,该层中的每个单元或单元都有一个内部内存/单元状态,通常缩写为c,并输出一个隐藏状态,通常缩写为h。
  • 通过Keras,我们既可以访问LSTM层的输出状态,也可以访问LSTM层的当前状态。

现在让我们创建一个用于学习和产生由LSTM层组成的序列的自动编码器架构。有两个组成部分:

  • 一种编码器架构,以序列作为输入,并返回LSTM的当前状态作为输出
  • 一个以序列和编码器LSTM状态作为输入并返回解码后的输出序列的编码器架构
  • 我们正在保存和访问LSTM的隐藏和内存状态,以便在对不可见数据生成预测时使用它们。

首先,生成一个包含固定长度的随机序列的序列数据集。我们将创建一个函数来生成随机序列。

  • X1表示包含随机数的输入序列,
  • X2表示填充序列,填充序列用作复制序列其他元素的种子,
  • y表示目标序列或实际序列
def dataset_preparation(n_in, n_out, n_unique, n_samples):
    X1, X2, y = [], [], []
    for _ in range(n_samples):
        ## create random numbers sequence - input 
        inp_seq = [randint(1, n_unique-1) for _ in range(n_in)]
        
        ## create target sequence
        target = inp_seq[:n_out]
    
        ## create padded sequence / seed sequence 
        target_seq = list(reversed(target))
        seed_seq = [0] + target_seq[:-1]  
        
        # convert the elements to categorical using keras api
        X1.append(to_categorical([inp_seq], num_classes=n_unique))
        X2.append(to_categorical([seed_seq], num_classes=n_unique))
        y.append(to_categorical([target_seq], num_classes=n_unique))
    
    # remove unnecessary dimention
    X1 = np.squeeze(np.array(X1), axis=1) 
    X2 = np.squeeze(np.array(X2), axis=1) 
    y  = np.squeeze(np.array(y), axis=1) 
    return X1, X2, y

samples = 100000
features = 51
inp_size = 6
out_size = 3

inputs, seeds, outputs = dataset_preparation(inp_size, out_size, features, samples)
print("Shapes: ", inputs.shape, seeds.shape, outputs.shape)
print ("Here is first categorically encoded input sequence looks like: ", )
inputs[0][0]
Shapes:  (100000, 6, 51) (100000, 3, 51) (100000, 3, 51)
Here is first categorically encoded input sequence looks like: 
array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
       0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
       0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
      dtype=float32)

接下来,让我们在Keras中创建模型的架构

def define_models(n_input, n_output):
    ## define the encoder architecture 
    ## input : sequence 
    ## output : encoder states 
    encoder_inputs = Input(shape=(None, n_input))
    encoder = LSTM(128, return_state=True)
    encoder_outputs, state_h, state_c = encoder(encoder_inputs)
    encoder_states = [state_h, state_c]

    ## define the encoder-decoder architecture 
    ## input : a seed sequence 
    ## output : decoder states, decoded output 
    decoder_inputs = Input(shape=(None, n_output))
    decoder_lstm = LSTM(128, return_sequences=True, return_state=True)
    decoder_outputs, _, _ = decoder_lstm(decoder_inputs, initial_state=encoder_states)
    decoder_dense = Dense(n_output, activation='softmax')
    decoder_outputs = decoder_dense(decoder_outputs)
    model = Model([encoder_inputs, decoder_inputs], decoder_outputs)
    
    ## define the decoder model
    ## input : current states + encoded sequence
    ## output : decoded sequence
    encoder_model = Model(encoder_inputs, encoder_states)
    decoder_state_input_h = Input(shape=(128,))
    decoder_state_input_c = Input(shape=(128,))
    decoder_states_inputs = [decoder_state_input_h, decoder_state_input_c]
    decoder_outputs, state_h, state_c = decoder_lstm(decoder_inputs, initial_state=decoder_states_inputs)
    decoder_states = [state_h, state_c]
    decoder_outputs = decoder_dense(decoder_outputs)
    decoder_model = Model([decoder_inputs] + decoder_states_inputs, [decoder_outputs] + decoder_states)

    return model, encoder_model, decoder_model

autoencoder, encoder_model, decoder_model = define_models(features, features)
  • encoder model结构和参数
encoder_model.summary()
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_3 (InputLayer)         (None, None, 51)          0         
_________________________________________________________________
lstm_1 (LSTM)                [(None, 128), (None, 128) 92160     
=================================================================
Total params: 92,160
Trainable params: 92,160
Non-trainable params: 0
_________________________________________________________________
  • decoder model结构和参数
__________________________________________________________________________________________________
Layer (type)                    Output Shape         Param #     Connected to                     
==================================================================================================
input_4 (InputLayer)            (None, None, 51)     0                                            
__________________________________________________________________________________________________
input_5 (InputLayer)            (None, 128)          0                                            
__________________________________________________________________________________________________
input_6 (InputLayer)            (None, 128)          0                                            
__________________________________________________________________________________________________
lstm_2 (LSTM)                   [(None, None, 128),  92160       input_4[0][0]                    
                                                                 input_5[0][0]                    
                                                                 input_6[0][0]                    
__________________________________________________________________________________________________
dense_9 (Dense)                 (None, None, 51)     6579        lstm_2[1][0]                     
==================================================================================================
Total params: 98,739
Trainable params: 98,739
Non-trainable params: 0
__________________________________________________________________________________________________
  • autoencoder model结构和参数
__________________________________________________________________________________________________
Layer (type)                    Output Shape         Param #     Connected to                     
==================================================================================================
input_3 (InputLayer)            (None, None, 51)     0                                            
__________________________________________________________________________________________________
input_4 (InputLayer)            (None, None, 51)     0                                            
__________________________________________________________________________________________________
lstm_1 (LSTM)                   [(None, 128), (None, 92160       input_3[0][0]                    
__________________________________________________________________________________________________
lstm_2 (LSTM)                   [(None, None, 128),  92160       input_4[0][0]                    
                                                                 lstm_1[0][1]                     
                                                                 lstm_1[0][2]                     
__________________________________________________________________________________________________
dense_9 (Dense)                 (None, None, 51)     6579        lstm_2[0][0]                     
==================================================================================================
Total params: 190,899
Trainable params: 190,899
Non-trainable params: 0
__________________________________________________________________________________________________

现在,让我们使用adam优化器和分类交叉熵损失函数来训练自编码器模型

autoencoder.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['acc'])
autoencoder.fit([inputs, seeds], outputs, epochs=1)
Epoch 1/1
100000/100000 [==============================] - 66s 661us/step - loss: 0.6408 - acc: 0.7948
<keras.callbacks.History at 0x7f40d76eefd0>

让我们写一个函数,根据输入序列预测序列

def reverse_onehot(encoded_seq):
    return [argmax(vector) for vector in encoded_seq]

def predict_sequence(encoder, decoder, sequence):
    output = []
    target_seq = np.array([0.0 for _ in range(features)])
    target_seq = target_seq.reshape(1, 1, features)

    current_state = encoder.predict(sequence)
    for t in range(out_size):
        pred, h, c = decoder.predict([target_seq] + current_state)
        output.append(pred[0, 0, :])
        current_state = [h, c]
        target_seq = pred
    return np.array(output)
  • 生成一些预测
for k in range(5):
   X1, X2, y = dataset_preparation(inp_size, out_size, features, 1)
   target = predict_sequence(encoder_model, decoder_model, X1)
   print('\nInput Sequence=%s SeedSequence=%s, PredictedSequence=%s' 
         % (reverse_onehot(X1[0]), reverse_onehot(y[0]), reverse_onehot(target)))
Input Sequence=[41, 6, 50, 9, 22, 45] SeedSequence=[50, 6, 41], PredictedSequence=[50, 6, 41]

Input Sequence=[32, 43, 16, 48, 4, 44] SeedSequence=[16, 43, 32], PredictedSequence=[16, 43, 32]

Input Sequence=[15, 10, 38, 31, 34, 15] SeedSequence=[38, 10, 15], PredictedSequence=[38, 10, 15]

Input Sequence=[37, 20, 42, 18, 39, 22] SeedSequence=[42, 20, 37], PredictedSequence=[42, 20, 37]

Input Sequence=[49, 8, 12, 35, 31, 37] SeedSequence=[12, 8, 49], PredictedSequence=[12, 8, 49]

总结

本文从自动编码器的背后数学原理进行阐述,并通过图像重建、图像去噪、序列到序列预测三个案例应用表现了自动编码器和解码器的强大性能

如果这对你的实际的学习工作中有用,请应用到实践当中,也欢迎点赞评论,支持我的工作。

扫码加我好友进微信群|QQ群



原文来自我的知乎:https://zhuanlan.zhihu.com/p/365783839,或者点击阅读原文获取链接。


原文作者:ChallengeHub;如有侵权,请联系管理员删除。

请登录后发表评论 登录
全部评论

注册时间:2019-01-07

  • 博文量
    350
  • 访问量
    161002