目录
PyTorch是一个基于Python的开源深度学习框架,通过其强大的工具和库,可以方便地构建、训练和部署神经网络模型。以下是PyTorch构建神经网络的优势:
-
动态图机制:PyTorch使用动态图机制,其中动态图机制指的是先计算前面的值,再根据计算的值来搭建后面的计算图,这种方法优点是灵活,方便调试
-
易于学习和使用:PyTorch语法与标准Python语法非常相似,因此对于熟悉Python编程的开发人员来说非常友好。
-
丰富的工具和库:PyTorch提供了许多用于构建和训练神经网络的工具和库,例如torch.nn,在下面的例子中我们主要使用torch.nn,来调用需要的网络层和激活函数
-
可扩展性:PyTorch允许开发人员根据自己的需求自定义神经网络的结构和行为。它提供了丰富的扩展接口,使得开发人员可以轻松地添加自定义层、损失函数等。
-
计算速度快:PyTorch使用了高效的C++后端,可以获得较快的计算速度。此外,PyTorch还提供了在GPU上进行并行计算的支持,可以充分利用GPU的计算能力。
安装torch
点击官网安装,选择合适的版本在命令行运行
下载完成后即可载入使用的库
import torch
from torch import nn
from torch.nn import functional as F
构建全连接神经网络
全连接神经网络
全连接神经网络是一种最基础和常见的神经网络结构。它具有一下特点:
- 由多个神经元组成,每个神经元与前一层的所有神经元都有连接,其输出值与相邻层的每个神经元都有关。
- 在全连接神经网络中,每个神经元接收来自前一层神经元的输入,并通过激活函数将输入转化为输出。
- 全连接神经网络通常由输入层、隐藏层和输出层组成。输入层接收来自外部的数据,并将其传递给隐藏层。隐藏层经过一系列的计算和激活函数后,将结果传递给输出层。输出层的神经元负责产生网络的最终输出。
如图所示:
在上面的中
以此类推,从图上看全连接神经网络的优点是具有较高的灵活性和表达能力,可以对复杂的非线性关系进行建模。然而,全连接神经网络也存在一些问题,例如参数数量较大,网络的训练量过于庞大,此时难以得到满意的结果,而参数的数量过小,此时网络预测的效果变差容易产生过拟合
激活函数
激活函数是神经网络中的一种非线性函数,用于在神经网络的每个神经元中引入非线性。激活函数对输入信号进行转换,并生成输出信号,它的作用是为神经网络引入非线性,使得神经网络能够学习和表示复杂的模式和关系。
常见的激活函数有以下几种:
1. Sigmoid函数:Sigmoid函数的输出在0和1之间,具有平滑的非线性特性。它常用于二分类任务或作为中间层的激活函数。但是,Sigmoid函数存在梯度饱和的问题,导致在反向传播中梯度消失。
2. Tanh函数:Tanh函数是Sigmoid函数的变种,输出在-1和1之间。它的形状类似于Sigmoid函数,但是在坐标原点附近更为对称。Tanh函数在二分类任务和中间层中也有广泛应用。
3. ReLU函数:ReLU函数是一种简单的非线性激活函数,它仅在输入大于零时激活,将负值部分置为零。ReLU函数计算简单,不存在梯度消失问题,并且在实践中表现良好。然而,ReLU函数存在一个问题,就是在输入小于零时,梯度变为零,导致部分神经元失活,称为"死亡神经元"。
4. Softmax函数:Softmax函数主要应用于多分类任务中,它将多个输入映射为0到1之间的概率分布,用于预测样本属于每个类别的概率。Softmax函数通常作为输出层的激活函数。
代码实现
net=nn.Sequential(nn.Linear(20,256),nn.ReLU(),nn.Linear(256,10))
这个例子构建了一个全连接神经网络,其中包括了20*256的输入层,Relu激活函数和256*10的输出层,其中输入层和输出层都是线性的,我们可以跟据网络来输入数据查看它的输出
X=torch.rand(2,20)
net(X)
我们可以得到结果
tensor([[ 0.0176, -0.1641, 0.0067, 0.0323, -0.0758, -0.0545, 0.0763, -0.0895,
-0.0445, 0.0114],
[ 0.0187, -0.2968, -0.0619, 0.0046, -0.0601, -0.0204, 0.1305, -0.0208,
-0.0193, 0.0606]], grad_fn=<AddmmBackward0>)
分析可以得出我们生成了一个2*20的数据,可以看作两组大小为20的数据,把数据放入20*256和256*10的网络,可以得到大小为10的输出,共生成2组,它是由前向传播得到的
得到生成的偏置和权重
在机器学习和神经网络中,偏置和权重是两个重要的概念,用于模型的参数调整和预测结果的计算。
偏置(bias)是一个常数项,表示在没有输入时的输出值。我们可以将偏置理解为模型对于某种特定输出的基本偏倚,例如在使用线性回归模型时,偏置可以表示截距。偏置的存在可以使模型能够更好地适应训练数据,提高模型的拟合能力。
权重(weight)是模型中输入特征的系数,用于对特征进行加权求和的过程。权重决定了每个特征对预测结果的贡献程度,较大的权重表示该特征对结果的影响更大,而较小的权重则表示影响较小。权重的作用是将不同特征的重要性进行量化,并用于计算预测结果。
在神经网络中,偏置和权重是每个神经元的参数。每个神经元都有一个偏置和对应的权重,用于计算输入的加权和。偏置和权重通过反向传播算法进行优化,以使神经网络能够更好地拟合训练数据,提高预测的准确性。
使用代码查看
print(net[2].state_dict())
print(type(net[2].bias))
得到
OrderedDict([('weight', tensor([[ 0.0105, -0.0379, -0.0204, ..., -0.0109, -0.0494, 0.0280],
[ 0.0012, -0.0600, -0.0149, ..., -0.0595, 0.0257, 0.0522],
[ 0.0572, 0.0396, 0.0273, ..., -0.0538, -0.0540, 0.0197],
...,
[ 0.0476, 0.0594, 0.0500, ..., -0.0455, -0.0305, -0.0174],
[ 0.0188, -0.0166, 0.0424, ..., -0.0030, 0.0307, -0.0434],
[-0.0152, 0.0176, -0.0351, ..., -0.0508, 0.0509, -0.0126]])), ('bias', tensor([-0.0100, -0.0383, -0.0284, -0.0117, 0.0074, -0.0003, -0.0316, -0.0037,
0.0309, -0.0152]))])
<class 'torch.nn.parameter.Parameter'>
由此我们得到了网络第二层的偏置和权重
初始化偏置和权重
初始化偏置和权重是神经网络训练过程中的重要步骤。偏置是神经元激活函数中的常数项,用于调整激活函数的位置,从而改变神经元的输出。权重是神经元接收输入的连接强度,它们决定了不同输入对神经元的影响程度。
初始化偏置和权重的目的是为了使神经网络能够在初始阶段具有合适的学习能力。如果偏置和权重初始化得太小,可能导致梯度消失的问题,即反向传播时梯度逐渐减小并最终消失,使得神经网络无法学习。相反,如果偏置和权重初始化得太大,可能导致梯度爆炸的问题,即反向传播时梯度逐渐增大并最终失控,使得神经网络不稳定。
合适的偏置和权重初始化可以帮助神经网络在初始阶段更好地学习数据的特征,从而更快地收敛并得到更好的预测结果。
def init_normal(m):
if type(m)==nn.Linear:
nn.init.normal_(m.weight,mean=0,std=0.01)
#初始化权重
nn.init.zeros_(m.bias)
#初始化偏重
net.apply(init_normal)
#应用偏重和权重
net[0].weight.data[0],net[0].bias.data[0]
得到
(tensor([-1.1678e-02, -2.9808e-04, -4.9431e-03, -5.4561e-03, 1.7118e-03,
3.4664e-05, 1.7356e-02, -1.6628e-02, 4.5574e-03, -1.2613e-02,
1.8476e-02, 3.1264e-03, -1.2955e-02, -1.1355e-02, 1.4726e-02,
-8.4488e-03, 4.5292e-03, 1.2484e-02, -1.2478e-02, 8.2060e-03]),
tensor(0.))
显然权重为正态分布,偏置为0
利用类构建全连接神经网络
class MLP(nn.Module):
#继承nn.Module类
def __init__(self):
super().__init__()
#调用父类的构造函数
self.hidden=nn.Linear(20,256)
self.out=nn.Linear(256,10)
def forward(self,X):
return self.out(F.relu(self.hidden(X)))
如代码所示,构建了和上文一样的全连接神经网络
net=MLP()
net(X)
自定义层
class CenteredLayer(nn.Module):
def __init__(self):
super().__init__()
def forward(self,X):
return X-X.mean()
#返回X-X的均值
调用
layer=CenteredLayer()
layer(torch.FloatTensor([1,2,3,4,5]))
其中均值为3,预测输出为tensor([-2,-1,0,1,2])
将自定义层与线性层连接
net=nn.Sequential(nn.Linear(2,4),CenteredLayer())
net(torch.rand(1,2))
构建有权重和偏置的层
class MyLinear(nn.Module):
def __init__(self,in_units,units):
#in_units为输入维度,units为输出维度
super().__init__()
self.weight=nn.Parameter(torch.randn(in_units,units))
#让权重为in_units行,units列的正态分布
self.bias=nn.Parameter(torch.randn(units,))
#让偏置为units列的正态分布
def forward(self,X):
linear=torch.matmul(X,self.weight.data)+self.bias.data
#定义linear等于X*weight+bias
return F.relu(linear)
示例化
dense=MyLinear(5,3)
dense(torch.randn(2,5))
如图定义了一个5*3的线型网络,输入了一个2*5的数据应得到一个2*3的输出
tensor([[0.0000, 1.8537, 0.0000],
[0.6503, 0.0000, 0.0000]])
保存数据
写入
x1=torch.tensor([1,2,3,4])
torch.save(x1,'x1-file')
把x1保存到该目录下的'x1-file'文件
读取
x2=torch.load('x1-file')
print(x2)
把x1-file内容载入x2中
通过字典写入读取
mydict={'x1':x1,'x2':x2}
torch.save(mydict,'mydict')
mydict2=torch.load('mydict')
mydict2
保存网络数据
class MLP(nn.Module):
def __init__(self):
super().__init__()
self.hidden=nn.Linear(2,4)
self.out=nn.Linear(4,2)
def forward(self,X):
return self.out(self.hidden(X))
net=MLP()
X=torch.randn(4,2)
Y=net(X)
保存网络数据到文件'mlp.params'中
torch.save(net.state_dict(),'mlp.params')
载入网络数据
clone=MLP()
clone.load_state_dict(torch.load('mlp.params'))
clone.eval()#查看MLP类型
先克隆一个MLP()类(因为层的输入与输出要与之前相同),在将文件的是数据载入网络