一. 神经网络介绍
神经网络概念
神经元构建
神经网络
人工神经网络是一种模仿生物神经网络结构和功能的计算模型, 由神经元构成
将神经元串联起来 -> 神经网络
输入层: 数据
输出层: 目标(加权和)
隐藏层: 加权和 + 激活
全连接
第N层的每个神经元和第N-1层的所有神经元相连(full connected的含义)
激活函数
激活函数作用: 向网络中添加非线性因素, 拟合更复杂场景, 曲线, 曲面等
激活函数用于对每层的输出数据进行变换, 进而为整个网络增加非线性因素, 所以神经网络就可以拟合各种网络, 提升网络对复杂问题的拟合能力
没有引入非线性因素的网络等价于使用一个线性模型来拟合
sigmoid
用于二分类
概念
将任意输入映射到(0, 1)区间
, 但是当输入的值在< -6或者> 6时梯度消失
, 输入值在[-6, 6]之间才会有效果, 在[-3, 3]之间才会有较好的效果
梯度小, 网络在5层之内会产生梯度消失现象, 函数图像并不是以0为中心, 导函数最大值为0.25
公式
代码演示
import torch
import matplotlib.pyplot as plt
import os
os.environ['KMP_DUPLICATE_LIB_OK']='True'
# 绘制sigmoid函数和它的导数
# 创建画布
fig, ax = plt.subplots(1, 2)
x = torch.linspace(-20, 20, 1000)
y = torch.sigmoid(x)
ax[0].plot(x, y)
ax[0].grid()
ax[0].set_title('sigmoid')
x = torch.linspace(-20, 20, 1000, requires_grad=True)
torch.sigmoid(x).sum().backward()
# x.detach() # x的数值
ax[1].plot(x.detach(), x.grad)
ax[1].grid()
ax[1].set_title('sigmoid"')
fig.show()
Tanh
可以但不是首选用于隐藏层, 当隐藏层要使用指数型激活函数时, 使用Tanh
概念
将输入映射到(-1, 1)之间
, 图像以0为中心, 在0点对称, 当输入值在< -3或> 3时被映射到-1或者1.导数范围[0, 1], 当输入值在< -3或> 3时导数近似0
与sigmoid函数比, 以0为中心, 梯度较大, 收敛快, 减少迭代次数, 但是也会造成梯度消失
公式
代码演示
# 绘制Tanh函数和它的导数
fig, ax = plt.subplots(1, 2)
x = torch.linspace(-20, 20, 1000)
y = torch.tanh(x)
ax[0].plot(x, y)
ax[0].grid()
ax[0].set_title('tanh')
x = torch.linspace(-20, 20, 1000, requires_grad=True)
torch.tanh(x).sum().backward()
ax[1].plot(x.detach(), x.grad)
ax[1].grid()
ax[1].set_title('tanh"')
fig.show()
ReLU
使用最多
概念
将小于0的值映射为0(神经元死亡), 大于0的值保持不变(不会造成梯度衰减)
, 注重正信号, 忽略负信号, 运算简单提高模型训练效率, 随着训练的推进, 输入落入小于0区域, 导致对应权重无法更新, 造成神经元死亡, ReLU死区现象
与sigmoid相比的优势: 节省很多计算, sigmoid产生的梯度消失会导致无法完成深层网络的训练. ReLU会使得一部分神经元的输出为0, 导致神经元稀疏
, 减少参数的相互依赖, 缓解过拟合问题的发生
.
公式
代码演示
# 绘制ReLU函数和它的导数
fig, ax = plt.subplots(1, 2)
x = torch.linspace(-20, 20, 1000)
y = torch.relu(x)
ax[0].plot(x, y)
ax[0].grid()
ax[0].set_title('relu')
x = torch.linspace(-20, 20, 1000, requires_grad=True)
torch.relu(x).sum().backward()
ax[1].plot(x.detach(), x.grad)
ax[1].grid()
ax[1].set_title('relu"')
fig.show()
Soft MAX
多分类, sigmoid的推广, 将分类的结果以概率形式展现
概念
Soft MAX是将网络输出的logits通过softmax函数, 映射成(0, 1)的值
, 这些值的累计和为1(满足概率性质), 可以将其理解为概率, 选取概率最大(输出的最大值), 作为预测目标类别
公式
代码演示
# softmax函数预测值
x = torch.tensor([0.23, 0.45, 0.67, 0.89, 0.12, 0.34, 0.56])
y_pred = torch.softmax(x, dim=0)
print(y_pred)
其他激活函数
隐藏层选择
-
优先选择ReLU激活函数
-
如果ReLU效果不好, 尝试其他激活函数, 如LeakyReLU等
-
若使用ReLU则需要注意ReLU死区问题
-
少用sigmoid, 可以尝试tanh
输出层选择
-
二分类问题选择sigmoid激活函数
-
多分类问题选择softmax激活函数
-
回归问题选择identity(恒等激活)激活函数
参数初始化
初始化weight和bias
均匀分布初始化
权重参数初始化从区间均匀随机取值
。即在(-1/√d,1/√d)均匀分布中生成当前神经元的权重,其中d为每个神经元的输入数量
import torch.nn as nn
# 均匀分布初始化
def my_uniform():
linear = nn.Linear(in_features=6, out_features=2)
nn.init.uniform_(linear.weight)
print('uniform', linear.weight.data)
>>>uniform tensor([[0.2326, 0.9061, 0.0694, 0.1362, 0.7270, 0.0389],
[0.1841, 0.0189, 0.0503, 0.4331, 0.0769, 0.6846]])
固定初始化
将神经网络中的所有权重参数初始化为某个固定值.
# 固定初始化
def my_constant():
linear = nn.Linear(6, 2)
nn.init.constant_(linear.weight, 10)
print('constant', linear.weight.data)
>>>constant tensor([[10., 10., 10., 10., 10., 10.],
[10., 10., 10., 10., 10., 10.]])
全0初始化
将神经网络中的所有权重参数初始化为 0
# 全0初始化
def my_zeros():
linear = nn.Linear(6, 2)
nn.init.zeros_(linear.weight)
print('zeros', linear.weight.data)
>>>zeros tensor([[0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0.]])
全1初始化
将神经网络中的所有权重参数初始化为 1.
# 全1初始化
def my_ones():
linear = nn.Linear(6, 3)
nn.init.ones_(linear.weight)
print('ones', linear.weight.data)
>>>ones tensor([[1., 1., 1., 1., 1., 1.],
[1., 1., 1., 1., 1., 1.],
[1., 1., 1., 1., 1., 1.]])
正太分布初始化
随机
初始化从均值为0,标准差是1的高斯分布中取样
,使用一些很小的值对参数W进行初始化
# 正太分布初始化
def my_normal():
linear = nn.Linear(6, 3)
nn.init.normal_(linear.weight, mean=0, std=1)
print('normal', linear.weight.data)
>>>normal tensor([[ 0.4047, 0.7500, 0.9120, 0.6850, -1.9469, 1.1815],
[ 0.7610, -0.3845, -2.1513, 2.4874, 2.2394, 3.8539],
[-1.9032, 0.7158, 2.0793, -1.1346, 2.2609, 3.2927]])
★kaiming初始化
HE 初始化分为正态分布的 HE 初始化、均匀分布的 HE 初始化.
★正态化的he初始化
stddev = sqrt(2 / fan_in)
均匀分布的he初始化
它从 [-limit,limit] 中的均匀分布中抽取样本
, limit是sqrt(6 / fan_in)
, fan_in 输入神经元的个数
# 凯明初始化
def my_kaiming():
linear1 = nn.Linear(6, 3)
nn.init.kaiming_normal_(linear1.weight)
print('kaiming_normal', linear1.weight.data)
linear2 = nn.Linear(6, 3)
nn.init.kaiming_uniform_(linear2.weight)
print('kaiming_uniform', linear2.weight.data)
>>>kaiming_normal tensor([[-1.0790, -0.2549, -1.0042, -0.4537, 0.2316, -0.6732],
[-0.4751, 0.7185, 0.5173, 0.3771, 0.6589, 0.8899],
[ 0.0927, -0.4816, -0.6176, -0.2689, -0.2861, -1.0924]])
>>>kaiming_uniform tensor([[ 0.8689, -0.3152, -0.0065, -0.1623, 0.7836, -0.8876],
[ 0.5449, 0.6405, 0.2201, -0.5927, -0.5317, 0.9914],
[-0.4956, 0.1218, 0.5516, 0.4448, 0.5495, -0.6692]])
★Xavier初始化
该方法也有两种,一种是正态分布的 xavier 初始化、一种是均匀分布的 xavier 初始化.
★正态化的Xavier初始化
stddev = sqrt(2 / (fan_in + fan_out))
均匀分布的Xavier初始化
它从[-limit,limit] 中的均匀分布中抽取样本
, limit 是 sqrt(6 / (fan_in + fan_out))
# Xavier初始化
def my_xavier():
linear1 = nn.Linear(6, 3)
nn.init.xavier_normal_(linear1.weight)
print('xavier_normal', linear1.weight.data)
linear2 = nn.Linear(6, 3)
nn.init.xavier_uniform_(linear2.weight)
print('xavier_uniform', linear2.weight.data)
>>>xavier_normal tensor([[ 1.3166, -1.0188, 0.8923, 0.2222, -0.2327, 0.8967],
[ 0.2300, 0.2561, -0.2898, 0.3206, 0.9131, -0.2498],
[-0.7890, -0.0296, 0.0836, 0.2697, 0.6994, 0.4209]])
>>>xavier_uniform tensor([[ 0.1771, 0.7031, -0.7461, -0.6401, -0.4325, -0.0085],
[-0.2334, 0.2687, 0.3438, 0.4502, -0.1237, -0.6410],
[-0.5314, -0.0369, -0.3987, -0.2400, -0.7561, -0.7719]])