CV

深度神经网络-上

定义、激活函数、常见损失、优化方法

Posted by 新宇 on July 17, 2020

一、神经网络基础

1. 简介

  • 人工神经网络
    • 也简称为神经网络(NN),是一种模仿生物神经网络结构和功能的 计算模型。
  • 常用的神经网络
    • 卷积神经网络(Convolutional Neural Network)
    • 循环神经网络(Recurrent Neural Network)
    • 生成对抗网络(Generative Adversarial Networks)
    • 深度强化学习(Deep Reinforcement Learning)

2. 结构分层

  • 结构分层:
    • 输入层:即输入x的那一层
    • 输出层:即输出y的那一层
    • 隐藏层:输入层和输出层之间都是隐藏层
  • 特点
    • 同一层的神经元之间没有连接。
    • 第N层的每个神经元和第N-1层的所有神经元相连(这就是full connected的含义),第N-1层神经元的输出就是第N层神经元的输入。
    • 每个连接都有一个权值。

二. 神经网络如何工作

1. 激活函数定义

  • 本质是向神经网络中引入非线性因素的,通过激活函数,神经网络就可以拟合各种曲线,可以逼近任意函数。
  • 如果不用激活函数,每一层输出都是上层输入的线性函数,无论神经网络有多少层,输出都是输入的线性组合;

2. 常见激活函数

2.1 Sigmoid/logistics函数

# 导入相应的工具包

import tensorflow as tf
import tensorflow.keras as keras
import matplotlib.pyplot as plt
import numpy as np
# 定义x的取值范围

x = np.linspace(-10, 10, 100)
# 直接使用tensorflow实现

y = tf.nn.sigmoid(x)
# 绘图

plt.plot(x,y)
plt.grid()

2.1.1 梯度消失的推导证明

2.2 tanh(双曲正切曲线)

# 导入相应的工具包

import tensorflow as tf
import tensorflow.keras as keras
import matplotlib.pyplot as plt
import numpy as np
# 定义x的取值范围

x = np.linspace(-10, 10, 100)
# 直接使用tensorflow实现

y = tf.nn.tanh(x)
# 绘图

plt.plot(x,y)
plt.grid()

2.3 RELU

# 导入相应的工具包
import tensorflow as tf
import tensorflow.keras as keras
import matplotlib.pyplot as plt
import numpy as np
# 定义x的取值范围
x = np.linspace(-10, 10, 100)
# 直接使用tensorflow实现
y = tf.nn.relu(x)
# 绘图
plt.plot(x,y)
plt.grid()

2.4 LeakReLu

# 导入相应的工具包

import tensorflow as tf
import tensorflow.keras as keras
import matplotlib.pyplot as plt
import numpy as np
# 定义x的取值范围

x = np.linspace(-10, 10, 100)
# 直接使用tensorflow实现

y = tf.nn.leaky_relu(x)
# 绘图

plt.plot(x,y)
plt.grid()

2.5 SoftMax

可以看作是归一化操作,但不是等比例的

# 导入相应的工具包

import tensorflow as tf
import tensorflow.keras as keras
import matplotlib.pyplot as plt
import numpy as np
# 数字中的score

x = tf.constant([0.2,0.02,0.15,1.3,0.5,0.06,1.1,0.05,3.75])
# 将其送入到softmax中计算分类结果

y = tf.nn.softmax(x) 
# 将结果进行打印

print(y)

2.6 其他激活函数

2.7 如何选择激活函数

  • 隐藏层
    • 优先选择RELU激活函数
    • 如果ReLu效果不好,那么尝试其他激活,如Leaky ReLu等。
    • 如果你使用了Relu, 需要注意一下Dead Relu问题, 避免出现大的梯度从而导致过多的神经元死亡。
    • 不要使用sigmoid激活函数,可以尝试使用tanh激活函数
  • 输出层
    • 二分类问题选择sigmoid激活函数
    • 多分类问题选择softmax激活函数
    • 回归问题选择identity激活函数

3. 参数初始化

3.1 随机初始化

随机初始化从均值为0,标准差是1的高斯分布中取样,使用一些很小的值对参数W进行初始化。

3.2 标准初始化

权重参数初始化从区间均匀随机取值。即在(-1/√d,1/√d)均匀分布中生成当前神经元的权重,其中d为每个神经元的输入数量。

3.3.Xavier初始化

  • 该方法的基本思想是各层的激活值和梯度的方差在传播过程中保持一致,也叫做Glorot初始化。在tf.keras中实现的方法有两种: 正态化Xavier初始化:
  • Glorot 正态分布初始化器,也称为 Xavier 正态分布初始化器。它从以 0 为中心,标准差为 stddev = sqrt(2 / (fan_in + fan_out)) 的正态分布中抽取样本, 其中 fan_in 是输入神经元的个数, fan_out 是输出的神经元个数。
# 导入工具包

import tensorflow as tf
# 进行实例化

initializer = tf.keras.initializers.glorot_normal()
# 采样得到权重值

values = initializer(shape=(9, 1))
# 打印结果

print(values)

3.3.1 标准化Xavier初始化(均匀分布)

  • Glorot 均匀分布初始化器,也称为 Xavier 均匀分布初始化器。它从 [-limit,limit] 中的均匀分布中抽取样本, 其中 limit 是 sqrt(6 / (fan_in + fan_out)), 其中 fan_in 是输入神经元的个数, fan_out 是输出的神经元个数。
# 导入工具包

import tensorflow as tf
# 进行实例化

initializer = tf.keras.initializers.glorot_uniform()
# 采样得到权重值

values = initializer(shape=(9, 1))
# 打印结果

print(values)

3.4 He初始化

  • he初始化,也称为Kaiming初始化,出自大神何恺明之手,它的基本思想是正向传播时,激活值的方差保持不变;反向传播时,关于状态值的梯度的方差保持不变。在tf.keras中也有两种:
  • 正态化的he初始化
    • He 正态分布初始化是以 0 为中心,标准差为 stddev = sqrt(2 / fan_in) 的截断正态分布中抽取样本, 其中 fan_in是输入神经元的个数,在tf.keras中的实现方法为:
# 导入工具包

import tensorflow as tf
# 进行实例化

initializer = tf.keras.initializers.he_normal()
# 采样得到权重值

values = initializer(shape=(9, 1))
# 打印结果

print(values)
  • 标准化的he初始化
    • He 均匀方差缩放初始化器。它从 [-limit,limit] 中的均匀分布中抽取样本, 其中 limit 是 sqrt(6 / fan_in), 其中 fan_in 输入神经元的个数。实现为:
# 导入工具包

import tensorflow as tf
# 进行实例化

initializer = tf.keras.initializers.he_uniform()
# 采样得到权重值

values = initializer(shape=(9, 1))
# 打印结果

print(values)

三、神经网络的搭建

  • tf.keras.layers.Dense(units, activation=None, use_bias=True, kernel_initializer=’glorot_uniform’,bias_initializer=’zeros’)
    • units: 当前层中包含的神经元个数
    • Activation: 激活函数,relu,sigmoid等
    • use_bias: 是否使用偏置,默认使用偏置
    • Kernel_initializer: 权重的初始化方式,默认是Xavier初始化
    • bias_initializer: 偏置的初始化方式,默认为0

1. 通过Sequential构建

# 导入相关的工具包

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

# 定义一个Sequential模型,包含3层

model = keras.Sequential(
    [
        # 第一层:激活函数为relu,权重初始化为he_normal

        layers.Dense(3, activation="relu",
                     kernel_initializer="he_normal", name="layer1",input_shape=(3,)),
        # 第二层:激活函数为relu,权重初始化为he_normal

        layers.Dense(2, activation="relu",
                     kernel_initializer="he_normal", name="layer2"),
        # 第三层(输出层):激活函数为sigmoid,权重初始化为he_normal

        layers.Dense(2, activation="sigmoid",
                     kernel_initializer="he_normal", name="layer3"),
    ],
    name="my_Sequential"
)

# 展示模型结果

model.summary()

2. 利用function API构建

tf.keras 提供了 Functional API,建立更为复杂的模型,使用方法是将层作为可调用的对象并返回张量,并将输入向量和输出向量提供给 tf.keras.Model 的 inputs 和 outputs 参数,实现方法如下:

# 导入工具包

import tensorflow as tf
# 定义模型的输入

inputs = tf.keras.Input(shape=(3,),name = "input")
# 第一层:激活函数为relu,其他默认

x = tf.keras.layers.Dense(3, activation="relu",name = "layer1")(inputs)
# 第二层:激活函数为relu,其他默认

x = tf.keras.layers.Dense(2, activation="relu",name = "layer2")(x)
# 第三层(输出层):激活函数为sigmoid

outputs = tf.keras.layers.Dense(2, activation="sigmoid",name = "layer3")(x)
# 使用Model来创建模型,指明输入和输出

model = tf.keras.Model(inputs=inputs, outputs=outputs,name="my_model")

# 展示模型结果

model.summary()


keras.utils.plot_model(model,show_shapes=True)

3. 通过model的子类构建

通过model的子类构建模型,此时需要在__init__中定义神经网络的层,在call方法中定义网络的前向传播过程,实现方法如下:

# 导入工具包

import tensorflow as tf
# 定义model的子类

class MyModel(tf.keras.Model):
    # 在init方法中定义网络的层结构

    def __init__(self):
        super(MyModel, self).__init__()
        # 第一层:激活函数为relu,权重初始化为he_normal

        self.layer1 = tf.keras.layers.Dense(3, activation="relu",
                     kernel_initializer="he_normal", name="layer1",input_shape=(3,))
        # 第二层:激活函数为relu,权重初始化为he_normal

        self.layer2 =tf.keras.layers.Dense(2, activation="relu",
                     kernel_initializer="he_normal", name="layer2")
        # 第三层(输出层):激活函数为sigmoid,权重初始化为he_normal

        self.layer3 =tf.keras.layers.Dense(2, activation="sigmoid",
                     kernel_initializer="he_normal", name="layer3")
    # 在call方法中万完成前向传播

    def call(self, inputs):
        x = self.layer1(inputs)
        x = self.layer2(x)
        return self.layer3(x)
# 实例化模型

model = MyModel()
# 设置一个输入,调用模型(否则无法使用summay())

x = tf.ones((1, 3))
y = model(x)

4. 神经网络的优缺点

  • 优点
    • 精度高,性能优于其他的机器学习方法,甚至在某些领域超过了人类
    • 可以近似任意的非线性函数
    • 随之计算机硬件的发展,近年来在学界和业界受到了热捧,有大量的框架和库可供调用
  • 缺点
    • 黑箱,很难解释模型是怎么工作的
    • 训练时间长,需要大量的计算力
    • 网络结构复杂,需要调整超参数
    • 小数据集上表现不佳,容易发生过拟合

四、损失函数

损失函数又叫做代价函数、目标函数或误差函数;

1. 分类任务

1.1 多分类任务

在多分类任务通常使用softmax将logits转换为概率的形式,所以多分类的交叉熵损失也叫做softmax损失,它的计算方法是:

# 导入相应的包

import tensorflow as tf
# 设置真实值和预测值

y_true = [[0, 1, 0], [0, 0, 1]]
y_pred = [[0.05, 0.95, 0], [0.1, 0.8, 0.1]]
# 实例化交叉熵损失

cce = tf.keras.losses.CategoricalCrossentropy()
# 计算损失结果

cce(y_true, y_pred).numpy()

1.2 二分类任务

# 导入相应的包

import tensorflow as tf
# 设置真实值和预测值

y_true = [[0], [1]]
y_pred = [[0.4], [0.6]]
# 实例化二分类交叉熵损失

bce = tf.keras.losses.BinaryCrossentropy()
# 计算损失结果

bce(y_true, y_pred).numpy()

2. 回归任务

2.1 MAE损失

# 导入相应的包

import tensorflow as tf
# 设置真实值和预测值

y_true = [[0.], [0.]]
y_pred = [[1.], [1.]]
# 实例化MAE损失

mae = tf.keras.losses.MeanAbsoluteError()
# 计算损失结果

mae(y_true, y_pred).numpy()

2.2 MSE损失

# 导入相应的包

import tensorflow as tf
# 设置真实值和预测值

y_true = [[0.], [1.]]
y_pred = [[1.], [1.]]
# 实例化MSE损失

mse = tf.keras.losses.MeanSquaredError()
# 计算损失结果

mse(y_true, y_pred).numpy()

2.3 smooth L1 损失

# 导入相应的包

import tensorflow as tf
# 设置真实值和预测值

y_true = [[0], [1]]
y_pred = [[0.6], [0.4]]
# 实例化smooth L1损失

h = tf.keras.losses.Huber()
# 计算损失结果

h(y_true, y_pred).numpy()