(四)tensorflow2.0 - 实战稀疏自动编码器SAE
先简单介绍稀疏自动编码器SAE,其架构如下图所示(图源网络,侵删),三层结构,输出层应尽量和输入层接近,其重点在于中间的隐藏层,隐藏层将数据进行了重新编码,这样做的目的是获得输入数据更好的数据表示。在普通自动编码器中,往往要求隐藏层元素个数要比输入层元素个数少,但是稀疏自动编码器的要求不同,它可以允许隐藏层元素比输入层元素多,但是要保证稀疏性,即大多数隐藏层节点的输出值为0。

而SAE的损失函数,则为:

第一部分为预测值与真实值的SAE,第二部分为对权重的惩罚项,第三部分ρ和ρ帽分别代表期望的稀疏度和实际的稀疏度。
下面放上代码:
import tensorflow as tf
import numpy as np
import pandas as pd
from tensorflow.keras import *
import tensorflow.keras.backend as kb
import sys
import matplotlib.pyplot as plt
def SAEFC(inputList):
inputList = np.array(inputList)
inputFeatureNum = len(inputList[0])
hiddenNum = 3 * inputFeatureNum
density = 0.1
lossList = []
saeModel = SAEModel(inputList.shape[-1], hiddenNum)
for i in range(1000):
loss = saeModel.network_learn(tf.constant(inputList))
lossList.append(loss)
print(loss)
x = np.arange(len(lossList)) + 1
plt.plot(x, lossList)
plt.show()
return saeModel
class SAELayer(layers.Layer):
def __init__(self, num_outputs):
super(SAELayer, self).__init__()
self.num_outputs = num_outputs
def build(self, input_shape):
self.kernel = self.add_variable("kernel",
shape=[int(input_shape[-1]),
self.num_outputs - 1])
self.bias = self.add_variable("bias",
shape=[self.num_outputs - 1])
def call(self, input):
output = tf.matmul(input, self.kernel) + self.bias
output = tf.nn.sigmoid(output)
bias_list = tf.ones([input.shape[0], 1])
output = tf.concat([output, bias_list], 1)
self.result = output
return output
class SAEModel(Model):
def __init__(self, input_shape, hidden_shape=None):
if hidden_shape == None:
hidden_shape = 3 * input_shape
super(SAEModel, self).__init__()
self.train_loss = None
self.layer_2 = SAELayer(hidden_shape)
self.layer_3 = layers.Dense(input_shape, activation=tf.nn.sigmoid)
def call(self, input_tensor, training=False):
bias_list = tf.ones([len(input_tensor), 1])
input_tensor = tf.concat([input_tensor, bias_list], 1)
hidden = self.layer_2(input_tensor)
output = self.layer_3(hidden)
return output
def get_loss(self, input_tensor):
bias_list = tf.ones([len(input_tensor), 1])
new_input = tf.concat([input_tensor, bias_list], 1)
hidden = self.layer_2(new_input)
output = self.layer_3(hidden)
mse = (1 / 2) * tf.reduce_sum(kb.square(input_tensor - output))
alpha = 0.1
W1 = self.layer_2.kernel
W2 = self.layer_3.kernel
weightPunish = (alpha / 2) * (tf.reduce_sum(kb.square(W1)) + tf.reduce_sum(kb.square(W2)))
beita = 0.1
desired_density = 0.1
layer2_output = self.layer_2.result
actual_density = tf.reduce_mean(tf.math.count_nonzero(layer2_output, axis=1) / layer2_output.shape[1])
actual_density = tf.cast(actual_density, tf.float32)
if actual_density == tf.constant(1.0, dtype=tf.float32):
actual_density = tf.constant(0.999)
actual_density = actual_density.numpy()
KL = desired_density * np.log(desired_density / actual_density)
KL += (1 - desired_density) * np.log((1 - desired_density) / (1 - actual_density))
KL *= beita
ans = tf.constant(mse + weightPunish + KL)
return ans
def get_grad(self, input_tensor):
with tf.GradientTape() as tape:
tape.watch(self.variables)
L = self.get_loss(input_tensor)
self.train_loss = L
g = tape.gradient(L, self.variables)
return g
def network_learn(self, input_tensor):
g = self.get_grad(input_tensor)
optimizers.Adam().apply_gradients(zip(g, self.variables))
return self.train_loss
def getReprestation(self, input_tensor):
bias_list = tf.ones([len(input_tensor), 1])
new_input = tf.concat([input_tensor, bias_list], 1)
hidden = self.layer_2(new_input)
return hidden