(四)tensorflow2.0 - 实战稀疏自动编码器SAE

前文已经介绍完了tensorflow2.0自定义layer、model、loss function,本文将结合前述知识,搭建一个稀疏自动编码器SAE。
(一)tensorflow2.0 - 自定义layer
(二)tensorflow2.0 - 自定义Model
(三)tensorflow2.0 - 自定义loss function(损失函数)
(四)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


# 输入输出为16 × 1的列表
# inputList为输入列表
def SAEFC(inputList):
    inputList = np.array(inputList)
    # 输入特征个数
    inputFeatureNum = len(inputList[0])
    # 隐藏层参数个数:输入特征3倍
    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__()
        # 该层最后一个节点,其值固定为1,
        # 前期可以按照同样的手段让该节点和其他节点一样进行计算,
        # 最后在传递给下一层前,将其设置为1即可(即其值固定为1)
        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
        # sigmoid函数
        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):
    # 可以传入一些超参数,用以动态构建模型
    # __init_——()方法在创建模型对象时被调用
    # input_shape: 输入层和输出层的节点个数(输入层实际要比这多1,因为有个bias)
    # hidden_shape: 隐藏层节点个数,隐藏层节点的最后一个节点值固定为1,也是bias
    # 使用方法:直接传入实际的input_shape即可,在call中也直接传入原始Input_tensor即可
    # 一切关于数据适配模型的处理都在模型中实现
    def __init__(self, input_shape, hidden_shape=None):
        # print("init")
        # 隐藏层节点个数默认为输入层的3倍
        if hidden_shape == None:
            hidden_shape = 3 * input_shape
        # 调用父类__init__()方法
        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):
        # 将input_tensor最后加一列1
        bias_list = tf.ones([len(input_tensor), 1])
        input_tensor = tf.concat([input_tensor, bias_list], 1)
        # 输入数据
        # x = self.layer_1(input_tensor)
        hidden = self.layer_2(input_tensor)
        output = self.layer_3(hidden)
        return output
    
    def get_loss(self, input_tensor):
        # print("get_loss")
        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)
        
        # 计算loss
        # 计算MSE
        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)))

        # 计算KL散度
        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)
            # 保存一下loss,用于输出
            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
posted @ 2020-02-03 13:14  _吟游诗人  阅读(839)  评论(0编辑  收藏  举报