【TensorFlow2.0】感知机、全连接层、神经网络、激活函数、输出层、误差计算、神经网络类型、汽车油耗预测实战

一、感知机

  感知机模型接受长度为n的一维向量𝒙 = [𝑥1, 𝑥2, … , 𝑥𝑛],每个输入节点通过权值为𝑤𝑖,i∈[1,n]的连接汇集为变量为z,即𝑧 = 𝑤1 𝑥1 + 𝑤2 𝑥2 + ⋯ + 𝑤𝑛 𝑥𝑛 + 𝑏。𝑏称为感知机的偏置(Bias),一维向量𝒘 = [𝑤1, 𝑤2, … , 𝑤𝑛]称为感知机的权值(Weight)。通过在线性模型后添加激活函数后得到活性值(Activation) : a= 𝜎(𝑧) = 𝜎(𝒘T𝒙 + 𝑏)
          在这里插入图片描述
  添加激活函数后的感知机能解决简单二分类问题,但是不能解决异或问题或者线性不可分问题。
  感知机的激活函数:
  (1)阶跃函数:阶跃函数的输出只有0和1两个值,当z<0时输出0,代表类别0;当z>=0时输出1,代表类别1。即:
          在这里插入图片描述
  (2)符号函数:
          在这里插入图片描述
  阶跃函数和符号函数在Z=0时是不连续的,其他位置的导数为0,无法利用梯度下降算法进行参数优化。

二、全连接层

  感知机模型:不可导特性严重约束了它的潜力,使得它只能解决极其简单的任务。
  现代深度学习:核心结构与感知机并没有多大差别,它在感知机的基础上,将不连续的阶跃激活函数换成了其它平滑连续可导的激活函数,并通过堆叠多个网络层来增强网络的表达能力。

  全连接层:
  每个输出节点与全部的输入节点相连接,这种网络层称为全连接层(Fully-connected Layer),或者稠密连接层(Dense Layer),𝑾矩阵叫做全连接层的权值矩阵,𝒃向量叫做全连接层的偏置向量。
            在这里插入图片描述

2.1 张量方式实现全连接层

  在 TensorFlow 中,要实现全连接层,只需要定义好权值张量𝑾和偏置张量𝒃,并利用TensorFlow 提供的批量矩阵相乘函数 tf.matmul()即可完成全连接网络层的计算。

import tensorflow as tf

 # 创建 W,b 张量
x = tf.random.normal([2,784])
w1 = tf.Variable(tf.random.truncated_normal([784, 256], stddev=0.1))#权重
b1 = tf.Variable(tf.zeros([256]))#偏置
o1 = tf.matmul(x,w1) + b1 # 线性变换
o1 = tf.nn.relu(o1) # 激活函数
print(o1)

2.2 层方式实现全连接层

  全连接层:作为最常用的网络层之一,TensorFlow 中使用更方便的层实现方式:layers.Dense(units, activation)。通过 layer.Dense类,只需要指定输出节点数 Units 和激活函数类型 activation 即可。
  输入节点数根据上一次运算时的输输出 shape 确定,同时根据输入、输出节点数自动创建并初始化权值张量𝑾和偏置张量𝒃,完成网络参数的创建。
  通过类内部的成员名 kernel 和 bias 来获取权值张量𝑾和偏置张量
  参数列表:
  (1)通过类的trainable_variables成员返回待优化的参数列表
  (2)通过类的non_trainable_variables成员返回不需要优化的参数列表
  (3)通过类的variables成员返回所有内部张量的列表

import tensorflow as tf

x = tf.random.normal([4,28*28])
from tensorflow.keras import layers # 导入层模块
# 创建全连接层,指定输出节点数和激活函数
fc = layers.Dense(512, activation=tf.nn.relu)
h1 = fc(x) # 通过 fc 类实例完成一次全连接层的计算,返回输出张量

print(h1)#输出值
print(fc.kernel)#权值
print(fc.bias)#偏置
print(fc.trainable_variables)#待优化参数列表
print(fc.non_trainable_variables)#不需要优化的参数列表
print(fc.variables)#返回所有参数的列表

  对于全连接层,内部张量都参与梯度优化,故 variables 返回的列表与 trainable_variables 相同。

三、神经网络

  由很多层连接而成的网络叫做神经网络。
    在这里插入图片描述

3.1 张量方式实现神经网络

  对于多层神经网络,分别定义各层的权值矩阵𝑾和偏置向量𝒃。有多少个全连接层,则需要相应地定义数量相当的𝑾和𝒃,并且每层的参数只能用于对应的层,不能混用。

import tensorflow as tf
# 隐藏层 1 张量
w1 = tf.Variable(tf.random.truncated_normal([784, 256], stddev=0.1))
b1 = tf.Variable(tf.zeros([256]))
# 隐藏层 2 张量
w2 = tf.Variable(tf.random.truncated_normal([256, 128], stddev=0.1))
b2 = tf.Variable(tf.zeros([128]))
# 隐藏层 3 张量
w3 = tf.Variable(tf.random.truncated_normal([128, 64], stddev=0.1))
b3 = tf.Variable(tf.zeros([64]))
# 输出层张量
w4 = tf.Variable(tf.random.truncated_normal([64, 10], stddev=0.1))
b4 = tf.Variable(tf.zeros([10]))

x = tf.random.normal([4,28*28])
with tf.GradientTape() as tape: # 梯度记录器
    # x: [b, 28*28]
    # 隐藏层 1 前向计算,[b, 28*28] => [b, 256]
    h1 = x @ w1 + tf.broadcast_to(b1, [x.shape[0], 256])
    h1 = tf.nn.relu(h1)
    # 隐藏层 2 前向计算,[b, 256] => [b, 128]
    h2 = h1 @ w2 + b2
    h2 = tf.nn.relu(h2)
    # 隐藏层 3 前向计算,[b, 128] => [b, 64]
    h3 = h2 @ w3 + b3
    h3 = tf.nn.relu(h3)
    # 输出层前向计算,[b, 64] => [b, 10]
    h4 = h3 @ w4 + b4
'''
在使用 TensorFlow 自动求导功能计算梯度时,需要将前向计算过程放置在
tf.GradientTape()环境中,从而利用 GradientTape 对象的 gradient()方法自动求解参数的梯
度,并利用 optimizers 对象更新参数。
'''

3.2 层方式实现神经网络

  以全连接网络为例:
  (1)网络层:tf.keras.layers.Dense(输出节点数,activation)
  (2)网络模型:tf.keras.Sequential(【层1,层2…】】)

import tensorflow as tf
from tensorflow.keras import layers,Sequential

# 导入常用网络层 layers
fc1 = layers.Dense(256, activation=tf.nn.relu) # 隐藏层 1,输出节点为256
fc2 = layers.Dense(128, activation=tf.nn.relu) # 隐藏层 2,输出节点为128
fc3 = layers.Dense(64, activation=tf.nn.relu) # 隐藏层 3,输出节点为64
fc4 = layers.Dense(10, activation=None) # 输出层

x = tf.random.normal([4,28*28])

'''方法1:'''
# h1 = fc1(x) # 通过隐藏层 1 得到输出
# h2 = fc2(h1) # 通过隐藏层 2 得到输出
# h3 = fc3(h2) # 通过隐藏层 3 得到输出
# h4 = fc4(h3) # 通过输出层得到网络输出

'''方法2:'''
# 通过 Sequential 容器封装为一个网络类
model = Sequential([
 layers.Dense(256, activation=tf.nn.relu) , # 创建隐藏层 1
 layers.Dense(128, activation=tf.nn.relu) , # 创建隐藏层 2
 layers.Dense(64, activation=tf.nn.relu) , # 创建隐藏层 3
 layers.Dense(10, activation=None) , # 创建输出层
])
out = model(x) # 前向计算得到输出

3.3 神经网络优化目标

  (1)前向传播:把神经网络从输入到输出的计算过程叫做前向传播(Forward Propagation)或前向计算。
  (2)误差计算:前向传播的最后一步就是完成误差的计算ℒ = 𝑔(𝑓𝜃(𝒙),𝒚)
其中𝑓𝜃(∙)代表利用𝜃参数化的神经网络模型,𝑔(∙)称之为误差函数,用来描述当前网络的
预测值𝑓𝜃(𝒙)与真实标签𝒚之间的差距度量。ℒ称为网络的误差(Error,或损失 Loss),一般为标量。
  (3)反向传播:通过在训练集𝔻train上面学习到一组参数𝜃使
得训练的误差ℒ最小,这个最小化优化问题一般采用误差反向传播(Backward Propagation,简称 BP)算法来求解。网络参数𝜃,利用梯度下降(Gradient Descent,简称 GD)算法迭代更新参数:𝜃′ = 𝜃 − 𝜂 ∙ ∇𝜃ℒ。

四、激活函数

  神经网络中的常见激活函数,与阶跃函数和符号函数不同,这些
函数都是平滑可导的,适合于梯度下降算法。

4.1 Sigmoid函数

  (1)形式:
          在这里插入图片描述
  (2)作用:
  把𝑥 ∈ 𝑅的输入“压缩”到𝑥 ∈ (0,1)区间。可以通过 Sigmoid 函数将输出
转译为概率输出。
  (3)语法:tf.nn.sigmoid(x)
          在这里插入图片描述
  (4)缺点:
  Sigmoid 函数在输入值较大或较小时容易出现梯度值接近于 0 的现象,称为梯度弥散现象。出现梯度弥散现象时,网络参数长时间得不到更新,导致训练不收敛或停滞不动的现象 发生,较深层次的网络模型中更容易出现梯度弥散现象。

4.2 ReLU函数

  (1)形式:
          在这里插入图片描述

  (2)作用:
  ,ReLU 对小于 0 的值全部抑制为 0;对于正数则直接输出
  (3)语法:tf.nn.relu(x)
          在这里插入图片描述
  (4)缺点:
  relu函数在在𝑥 < 0时导数值恒为 0,也可能会造成梯度弥散现象。

4.3 LeakyReLU函数

  (1)形式:
          在这里插入图片描述
  (2)作用:
  ,ReLU 对小于 0 的值全部抑制为 0;对于正数则直接输出
  (3)语法:tf.nn.leaky_relu(x)
          在这里插入图片描述

4.4 Tanh函数

  (1)形式:
          在这里插入图片描述

  (2)作用:
  ,Tanh 函数能够将𝑥 ∈ 𝑅的输入“压缩”到(−1,1)区间
  (3)语法:tf.nn.tanh(x)
          在这里插入图片描述

五、输出层设计

  根据具体的任务场景来决定是否使用激活函数,以及使用什么类型的激活函数等。

5.1 普通实数空间

  预测属于整个或者部分连续的实数空间,输出层可以不加激活函数。

5.2 【0,1】区间

  输出值属于[0,1]区间,比如图片的生成、二分类问题等,将图片的像素值归一化到[0,1]区间。这些任务需要在输出层后添加某个合适的激活函数𝜎,其中 Sigmoid 函数刚好具有此功能。

5.3 【0,1】区间,和为1

  输出值𝑜𝑖 ∈ [0,1],且所有输出值之和为 1,这种设定以多分类问题最为常见。实现此约束逻辑,可以通过在输出层添加 Softmax 函数。Softmax 函数定义:
          在这里插入图片描述
   语法:tf.nn.softmax

5.4 【-1,1】区间

  希望输出值的范围分布在(−1,1)区间,可以简单地使用 tanh 激活函数。

六、误差计算

  常见的误差函数有均方差、交叉熵、KL 散度、Hinge Loss 函数等,其中均方差函数和交叉熵函数在深度学习中比较常见,均方差函数主要用于回归问题,交叉熵函数主要用于分类问题。

6.1 均方差误差函数

  (1)定义:
  均方差(Mean Squared Error,简称 MSE)误差函数把输出向量和真实向量映射到笛卡尔
坐标系的两个点上,通过计算这两个点之间的欧式距离(准确地说是欧式距离的平方)来衡
量两个向量之间的差距:
          在这里插入图片描述

  (2)特点:
  MSE 误差函数的值总是大于等于 0,当 MSE 函数达到最小值 0 时,输出等于真实标签,
此时神经网络的参数达到最优状态。
  (3)应用:
  均方差误差函数广泛应用在回归问题中
  (4)语法:
  1.tf.keras.losses.MSE(y_pred,y_true)计算批量中的每个样本的均方差
  2.tf.keras.losses.MeanSquareError()计算整个批量的均方差

  • 使用函数方式实现 MSE 计算:
import tensorflow as tf
o = tf.random.normal([2,10]) # 构造网络输出
y_onehot = tf.constant([1,3]) # 构造真实值
y_onehot = tf.one_hot(y_onehot, depth=10)
'''方法1:通过函数方式实现批量的均方差'''
loss = tf.keras.losses.MSE(y_onehot, o) # 计算均方差
print(loss)#MSE 函数返回的是每个样本的均方差,需要在样本维度上再次平均来获得平均样本的均方差
loss = tf.reduce_mean(loss) # 计算 batch 均方差
print(loss)
  • 使用层实现:
import tensorflow as tf

o = tf.random.normal([2,10]) # 构造网络输出
y_onehot = tf.constant([1,3]) # 构造真实值
y_onehot = tf.one_hot(y_onehot, depth=10)
'''方法2:通过层方式实现批量的均方差'''
criteon = tf.keras.losses.MeanSquaredError()
loss = criteon(y_onehot,o) # 计算 batch 均方差
print(loss)#batch的均方差

6.2 交叉熵误差函数

  (1)信息熵:衡量信息的不确定度。熵越大,代表不确定性越大,信息量也就越大。熵𝐻(𝑃)总是大于等于 0。当熵取得最小值 0 时,不确定性为 0。在 TensorFlow 中利用 tf.math.log 来计算熵。
          在这里插入图片描述
  (2)交叉熵:
          在这里插入图片描述
  交叉熵可以分解为𝑝的信息熵𝐻(𝑝)和𝑝与𝑞的 KL 散度(Kullback-Leibler Divergence)的
和:
          在这里插入图片描述
  KL 散度是用于衡量 2 个分布之间距离的指标。𝑝 = 𝑞时,𝐷𝐾𝐿(𝑝||𝑞)取得最小值 0,𝑝与𝑞之间的差距越大,𝐷𝐾𝐿(𝑝||𝑞)也越大。
  分类问题中 y 的编码分布𝑝采用 One-hot 编码𝒚时:𝐻(𝑝) = 0,此时𝐻(𝑝||𝑞) = 𝐻(𝑝) + 𝐷𝐾𝐿(𝑝||𝑞) = 𝐷𝐾𝐿(𝑝||𝑞)退化到真实标签分布𝒚与输出概率分布𝒐之间的 KL 散度。 根据 KL 散度的定义,推导分类问题中交叉熵的计算表达式:
          在这里插入图片描述
  最小化交叉熵损失函数的过程也是最大化正确类别的预测概率的过程

七、神经网络类型

7.1 卷积神经网络

  识别、分析并理解图片、视频等数据是计算机视觉的一个核心问题,全连接层在处理高维度的图片、视频数据时往往出现网络参数量巨大,训练非常困难的问题。通过利用局部相关性和权值共享的思想,提出了卷积神经网络在计算机视觉中的表现大大地超越了其它算法模型,呈现统治计算机视觉领域之势用于图片分类的 AlexNet、VGG、GoogLeNet、ResNet、DenseNet 等,用于目标识别的 RCNN、Fast RCNN、Faster RCNN、Mask RCNN、YOLO、SSD 等。

7.2 循环神经网络

  序列信号也是非常常见的一种数据类型,一个最具代表性的序列信号就是文本数据。如何处理并理解文本数据是自然语言处理的一个核心问题。卷积神经网络缺乏 Memory 机制和处理不定长序列信号的能力,并不擅长序列信号的任务。循环神经网络非常擅长处理序列信号。LSTM 网络,作为 RNN 的变种,它较好地克服了 RNN 缺乏长期记忆、不擅长处理长序列的问题。在自然语言处理中得到了广泛的应用。基于LSTM 模型,Google 提出了用于机器翻译的 Seq2Seq 模型,并成功商用于谷歌神经机器翻译系统(GNMT)。其他的 RNN 变种还有 GRU、双向 RNN 等。

7.3 注意力机制网络

  RNN 并不是自然语言处理的最终解决方案,注意力机制(Attention Mechanism)的提出,克服了 RNN 训练不稳定、难以并行化等缺陷,在自然语言处理和图片生成等领域中逐渐崭露头角。注意力机制最初在图片分类任务上提出,但逐渐开始侵蚀NLP 各大任务。2017 年,Google 提出了第一个利用纯注意力机制实现的网络模型Transformer,随后基于 Transformer 模型相继提出了一系列的用于机器翻译的注意力网络模型,如 GPT、BERT、GPT-2 等。在其它领域,基于注意力机制,尤其是自注意力(SelfAttention)机制构建的网络也取得了不错的效果,比如基于自注意力机制的 BigGAN 模型等。

7.4 图卷积神经网络

  图片、文本等数据具有规则的空间、时间结构,称为 Euclidean Data(欧几里德数据)。
卷积神经网络和循环神经网络被证明非常擅长处理这种类型的数据。而像类似于社交网络、通信网络、蛋白质分子结构等一系列的不规则空间拓扑结构的数据,它们显得力不从心。2016 年,Thomas Kipf 等人基于前人在一阶近似的谱卷积算法上提出了图卷积网络(Graph Convolution Network,GCN)模型。GCN 算法实现简单,从空间一阶邻居信息聚合的角度也能直观地理解,在半监督任务上取得了不错效果。随后,一系列的网络模型相继被提出,如 GAT,EdgeConv,DeepGCN 等。

八、汽车油耗预测实战

  采用 Auto MPG 数据集,Auto MPG 数据集一共记录了 398 项数据,们从 UCI 服务器下载并读取数据集到DataFrame 对象。


from __future__ import absolute_import, division, print_function, unicode_literals
import pathlib
import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers, losses

# 在线下载汽车效能数据集
dataset_path = keras.utils.get_file("auto-mpg.data", "http://archive.ics.uci.edu/ml/machine-learning-databases/auto-mpg/auto-mpg.data")

# 利用pandas读取数据集,字段为效能(公里数每加仑),气缸数,排量,马力,重量加速度,型号年份,产地
column_names = ['MPG','Cylinders','Displacement','Horsepower','Weight','Acceleration', 'Model Year', 'Origin']
raw_dataset = pd.read_csv(dataset_path, names=column_names,
                      na_values = "?", comment='\t',
                      sep=" ", skipinitialspace=True)

dataset = raw_dataset.copy()

#原始表格中的数据可能含有空字段(缺失值)的数据项,需要清除这些记录项:统计空白数据,并清除。
#数据集记录项减为 392 项。
dataset.isna().sum()#统计空白数据
dataset = dataset.dropna()#删除空白数据
dataset.isna().sum()#再次统计空白数据


#Origin 字段为类别类型数据,我们将其移除,并转换为新的 3 个字段:USA、Europe 和 Japan,分别代表是否来自此产地:
origin = dataset.pop('Origin')##先弹出(删除并返回)origin 这一列
# 根据origin列来写入新列
dataset['USA'] = (origin == 1)*1.0
dataset['Europe'] = (origin == 2)*1.0
dataset['Japan'] = (origin == 3)*1.0


# 切分为训练集和测试集,按 8:2 的比例:
train_dataset = dataset.sample(frac=0.8,random_state=0)
test_dataset = dataset.drop(train_dataset.index) 


#统计数据
sns.pairplot(train_dataset[["Cylinders", "Displacement", "Weight", "MPG"]], 
diag_kind="kde")

# 查看训练集的输入X的统计数据
train_stats = train_dataset.describe()
train_stats.pop("MPG")
train_stats = train_stats.transpose()
train_stats

# 移动MPG油耗效能这一列为真实标签Y
train_labels = train_dataset.pop('MPG')
test_labels = test_dataset.pop('MPG')

# 标准化数据
def norm(x):
  return (x - train_stats['mean']) / train_stats['std']
normed_train_data = norm(train_dataset)#标准化训练集
normed_test_data = norm(test_dataset)#标准化测试集


class Network(keras.Model):
    # 回归网络模型
    def __init__(self):
        super(Network, self).__init__()
        # 创建3个全连接层
        self.fc1 = layers.Dense(64, activation='relu')
        self.fc2 = layers.Dense(64, activation='relu')
        self.fc3 = layers.Dense(1)

    def call(self, inputs, training=None, mask=None):
        # 依次通过3个全连接层
        x = self.fc1(inputs)
        x = self.fc2(x)
        x = self.fc3(x)
        return x

model = Network()#创建网络实例
model.build(input_shape=(None, 9))# 通过 build 函数完成内部张量的创建,其中 4 为任意设置的 batch 数量,9 为输入特征长度
model.summary()#打印网络结构
optimizer = tf.keras.optimizers.RMSprop(0.001)#优化器
train_db = tf.data.Dataset.from_tensor_slices((normed_train_data.values, train_labels.values))#将数据转换为Dataset对象,以便tensorflow进行处理
train_db = train_db.shuffle(100).batch(32)#打乱数据,设置批量

# # 未训练时测试
# example_batch = normed_train_data[:10]
# example_result = model.predict(example_batch)
# example_result

train_mae_losses = []
test_mae_losses = []
for epoch in range(200):
    for step, (x,y) in enumerate(train_db):
        with tf.GradientTape() as tape:
            out = model(x)
            loss = tf.reduce_mean(losses.MSE(y, out))
            mae_loss = tf.reduce_mean(losses.MAE(y, out)) 

        if step % 10 == 0:
            print(epoch, step, float(loss))

        grads = tape.gradient(loss, model.trainable_variables)
        optimizer.apply_gradients(zip(grads, model.trainable_variables))

    train_mae_losses.append(float(mae_loss))
    out = model(tf.constant(normed_test_data.values))
    test_mae_losses.append(tf.reduce_mean(losses.MAE(test_labels, out)))


plt.figure()
plt.xlabel('Epoch')
plt.ylabel('MAE')
plt.plot(train_mae_losses,  label='Train')

plt.plot(test_mae_losses, label='Test')
plt.legend()
 
# plt.ylim([0,10])
plt.legend()
plt.savefig('auto.svg')
plt.show() 



  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值