目录
前言
前馈神经网络(feedforward neural network,FNN),简称前馈网络,是人工神经网络的一种。前馈神经网络采用一种单向多层结构。其中每一层包含若干个神经元。在此种神经网络中,各神经元可以接收前一层神经元的信号,并产生输出到下一层。第0层叫输入层,最后一层叫输出层,其他中间层叫做隐含层(或隐藏层、隐层)。隐层可以是一层。也可以是多层 。
一、神经元
神经网络的基本组成单元为带有非线性激活函数的神经元,其结构如如下图所示。神经元是对生物神经元的结构和特性的一种简化建模,接收一组输入信号并产生输出。
1、净活性值
假设一个神经元接收的输入为,其权重向量为
,神经元所获得的输入信号,即净活性值
的计算方法为
,其中
为偏置
为了提高预测样本的效率,我们通常会将个样本归为一组进行成批地预测。
,其中
为
个样本的特征矩阵,
为
个预测值组成的列向量。
使用pytorch计算一组输入的净活性值:
import torch
# 2个特征数为5的样本
X = torch.rand([2, 5])
#参数
w = torch.rand([5, 1])
b = torch.rand([1, 1])
# 使用'torch.matmul'实现矩阵相乘
z = torch.matmul(X, w) + b
print("input X:", X)
print("weight w:", w, "\nbias b:", b)
print("output z:", z)
执行结果:
思考题:
加权求和与仿射变换之间有什么区别和联系?
在我看来加权求和在本质是就是一个线性变换,线性变换 ,变换前是直线,变换后依然是直线; 直线比例保持不变; 变换前是原点,变换后依然是原点。而仿射变换是线性变换接了一个平移,从一个向量空间进入另一个向量空间计算; 变换前是直线,变换后依然是直线; 直线比例保持不变。
2、激活函数
净活性值再经过一个非线性函数
后,得到神经元的活性值
。
,
激活函数通常为非线性函数,可以增强神经网络的表示能力和学习能力。常用的激活函数有S型函数和ReLU函数。
2.1 Sigmoid 型函数
Sigmoid 型函数是指一类S型曲线函数,为两端饱和函数。常用的 Sigmoid 型函数有 Logistic 函数和 Tanh 函数,其数学表达式为
Logistic 函数:
.
Tanh 函数:
.
实现并可视化“Logistic函数、Tanh函数”
实现如下:
import torch
import matplotlib.pyplot as plt
# Logistic函数
def logistic(z):
return 1.0 / (1.0 + torch.exp(-z))
# Tanh函数
def tanh(z):
return (torch.exp(z) - torch.exp(-z)) / (torch.exp(z) + torch.exp(-z))
# 在[-10,10]的范围内生成10000个输入值,用于绘制函数曲线
z = torch.linspace(-10, 10, 10000)
plt.figure()
plt.plot(z.tolist(), logistic(z).tolist(), color='#e4007f', label="Logistic Function")
plt.plot(z.tolist(), tanh(z).tolist(), color='#f19ec2', linestyle ='--', label="Tanh Function")
ax = plt.gca() # 获取轴,默认有4个
# 隐藏两个轴,通过把颜色设置成none
ax.spines['top'].set_color('none')
ax.spines['right'].set_color('none')
# 调整坐标轴位置
ax.spines['left'].set_position(('data',0))
ax.spines['bottom'].set_position(('data',0))
plt.legend(loc='lower right', fontsize='large')
plt.savefig('fw-logistic-tanh.pdf')
plt.show()
2.2 ReLU型函数
常见的ReLU函数有ReLU和带泄露的ReLU(Leaky ReLU),数学表达式分别为:
,
,
其中为超参数。
实现并可视化可视化“ReLU、带泄露的ReLU的函数”
实现如下:
def relu(z):
return torch.maximum(z, torch.tensor(0.))
def leaky_relu(z, negative_slope=0.1):
# 当前版本torch暂不支持直接将bool类型转成int类型,因此调用了torch的cast函数来进行显式转换
a1 = (torch.can_cast((z > 0).dtype, to=torch.float32) * z)
a2 = (torch.can_cast((z <= 0).dtype, to=torch.float32) * (negative_slope * z))
return a1 + a2
# 在[-10,10]的范围内生成一系列的输入值,用于绘制relu、leaky_relu的函数曲线
z = torch.linspace(-10, 10, 10000)
plt.figure()
plt.plot(z.tolist(), relu(z).tolist(), color="#e4007f", label="ReLU Function")
plt.plot(z.tolist(), leaky_relu(z).tolist(), color="#f19ec2", linestyle="--", label="LeakyReLU Function")
ax = plt.gca()
ax.spines['top'].set_color('none')
ax.spines['right'].set_color('none')
ax.spines['left'].set_position(('data',0))
ax.spines['bottom'].set_position(('data',0))
plt.legend(loc='upper left', fontsize='large')
plt.savefig('fw-relu-leakyrelu.pdf')
plt.show()
二、基于前馈神经网络的二分类任务
前馈神经网络的网络结构如下图所示。每一层获取前一层神经元的活性值,并重复上述计算得到该层的活性值,传入到下一层。整个网络中无反馈,信号从输入层向输出层逐层的单向传播,得到网络最后的输出 。
1、数据集构建
使用上次实验中构建的二分类数据集:Moon1000数据集,其中训练集640条、验证集160条、测试集200条。该数据集的数据是从两个带噪音的弯月形状数据分布中采样得到,每个样本包含2个特征。
from nndl2.dataset import make_moons
n_samples = 1000
X, y = make_moons(n_samples=n_samples, shuffle=True, noise=0.15)
num_train = 640
num_dev = 160
num_test = 200
X_train, y_train = X[:num_train], y[:num_train]
X_dev, y_dev = X[num_train:num_train + num_dev], y[num_train:num_train + num_dev]
X_test, y_test = X[num_train + num_dev:], y[num_train + num_dev:]
y_train = y_train.reshape([-1,1])
y_dev = y_dev.reshape([-1,1])
y_test = y_test.reshape([-1,1])
2、模型构建
为了更高效的构建前馈神经网络,先定义每一层的算子,然后再通过算子组合构建整个前馈神经网络。
假设网络的第层的输入为第
层的神经元活性值
,经过一个仿射变换,得到该层神经元的净活性值
,再输入到激活函数得到该层神经元的活性值
。
在实践中,为了提高模型的处理效率,通常将个样本归为一组进行成批地计算。假设网络第ll层的输入为
,其中每一行为一个样本,则前馈网络中第
层的计算公式为
其中为NN个样本第ll层神经元的净活性值,
为NN个样本第ll层神经元的活性值,
为第
层的权重矩阵,
为第
层的偏置。
2.1 线性算子
from nndl.op import Op
# 实现线性层算子
class Linear(Op):
def __init__(self, input_size, output_size, name, weight_init=torch.normal, bias_init=torch.zeros):
self.params = {}
# 初始化权重
self.

最低0.47元/天 解锁文章
924

被折叠的 条评论
为什么被折叠?



