PyTorch是一个开源的Python机器学习库,是一个相当简洁且高效快速的框架,是Torch在Python上的衍生。
因为Torch是一个使用Lua语言的神经网络库,Torch很好用,但是 Lua 又不是特别流行,所以开发团队将Lua的Torch移植到了更流行的语言——Python上。PyTorch一发布就引起了剧烈的反响。
下载网址:https://pytorch.org/。
Anaconda是由Continuum Analytics开发的一款集成了Conda、Python以及一大堆安装好的工具包的开源软件。
其中Conda是一个包、环境管理器,可以用于在同一个机器上安装不同版本的软件包及其依赖,并能够在不同的环境之间切换。
下载网址:https://www.anaconda.com/。
GPU(可选):一张高性能NVIDIA显卡。
具体安装教程可以参考B站up:我是土堆 的视频BV1hE411t7RN。
IDE推荐:PyCharm
自带汉化包插件
可更换Python解释器,这里要选择第二行的Anaconda自带的Python(在这个Python环境中安装了Pytorch框架)。
可编辑脚本文件,也可在Python控制台实现交互式编辑
在下面菜单栏的Python Packages中可以查看本解释器已安装的第三方包。
Torch 自称为神经网络界的 Numpy, 因为他能将 Torch 产生的 tensor 放在 GPU 中加速运算 (前提是你有合适的 GPU), 就像 Numpy 会把 array 放在 CPU 中加速运算。
所以神经网络的话, 当然是用 Torch 的 tensor 形式数据最好。
张量tensor分为0维、1维、2维和多维。
0维张量就是标量。
1维张量就是数组。
2维张量就是矩阵。
除了简单的计算, 矩阵运算才是神经网络中最重要的部分。
tensor.dot()是把两个张量的每一对元素都分别对应相乘再相加。
最后结果应该是30.0
如何在数据当中找到他们的关系, 然后用神经网络模型来建立一个可以代表他们关系的线条。
torch.unsqueeze(input, dim, out=None):起升维的作用,参数dim表示在哪个地方加一个维度,dim范围在:[-input.dim() - 1, input.dim() + 1]之间。比如输入input是一维,则dim=0时数据为行方向扩,dim=1时为列方向扩,再大会出现错误。
tensor.size(x)用于返回tensor第 x 维的长度,不放参数即返回tensor的形状(同tensor.shape)。
torch.rand(x.size()) →随机生成一个与x形状相同的矩阵,取值在(0,1)区间。
1.首先,我们创建一些假数据来模拟真实的情况. 比如一个一元二次函数: y = a * x^2 + b, 我们给 y 数据加上一点噪声来更加真实的展示它(如果不加噪声,得到的y仅仅是一条x平方的曲线,而不是一些需要拟合的点)
2.然后,我们建立一个神经网络,这里可以直接运用 torch 中的体系。先定义所有的层属性(init()), 然后再一层层搭建(forward(x))层与层的关系链接。
多少个输入,多少个隐藏层神经元,多少个输出
self.hidden(x) → 使用隐藏层加工一下x,x作为输入经过隐藏层,得到输出为n_hidden。
F.relu() → 用relu激励函数激励n_hidden。
上页搭建的神经网络的结构如下:
3.接下来,我们开始训练搭建好的神经网络。
损失函数的实例化:
损失函数输入是一个输入的pair(对):(output, target),然后计算出一个数值来评估output和target之间的差距大小;
其中output代表的是神经网络评估出来的输出值yk,>target代表(有监督训练的,真实的)标签tk。
torch.nn中有若干个不同的损失函数可供使用,比如nn.MSELoss就是通过计算均方损失来评估输入和目标值之间从差距【越小越好】
Sequential 本质是一个模块(即 Module),根据Pytorch中的约定,模块中可以继续添加模块。
这意味着我们可以在 Sequential 中添加其它的模块(自然也就可以添加其他的 Sequential)。
添加完成后,Sequential 会将这些模块组成一个流水线,输入将依次通过这些模块得到一个输出。
这种方式很简单,不需要再构造一个网络,直接net2=torch.load(‘net.pkl’)(名字要和保存的一致)。
这种方式则需要构建一个层数,神经元数一致的网络。
之后再net2.load_state_dict(torch.load(‘net_params.pkl’))即可。
state_dict()将layer_name : layer_param的键值信息存储为dict形式。
load_state_dict是使用反序列化的state_dict加载模型的参数字典,用来加载模型参数。(load_state_dict函数的参数是字典对象,而不是保存对象的路径)
格式:torch.nn.Module.load_state_dict(state_dict, strict=True)
对net123用完全相同的方式训练100步,并进行可视化。
运行程序并验证,发现三张图完全一致。
首先介绍一下import torch.utils.data as Data,这在训练过程中基本都会用到。该接口大多用来读取数据和把数据封装成Tensor,之后的DataLoader用来做mini-batch训练。
DataLoader 是 torch 给我们用来包装数据的工具。
所以要将自己的 (numpy array 或其他) 数据形式装换成 Tensor, 然后再放进这个包装器中。使用 DataLoader 的好处是:他们帮你有效地迭代数据。
TensorDataset 基于一系列张量构建数据集。这些张量的形状可以不尽相同,但第一个维度必须具有相同大小。( TensorDataset 将张量的第一个维度视为数据集大小的维度,数据集在传入 DataLoader 后,该维度也是 batch_size 所在的维度)
实际上还有第五个参数drop_last: 当样本数不能被batchsize整除时, 是否舍弃最后一批数据。(默认不舍弃)
DataLoader构建可迭代的数据装载器,我们在训练的时候,每一个for循环,每一次iteration(迭代),就是从DataLoader中获取一个batch_size大小的数据的。
Epoch: 所有训练样本都已输入到模型中,称为一个Epoch。
Iteration(step): 一批样本输入到模型中,称为一个Iteration。
Batchsize: 一批样本的大小, 决定一个Epoch有多少个Iteration。
例:假设样本总数80, Batchsize是8, 那么1Epoch=10Iteration。 假设样本总数是87, Batchsize是8, 如果drop_last=True, 那么1Epoch=10Iteration;如果等于False, 那么1Epoch=11Iteration, 最后1个Iteration有7个样本。
enumerate() 函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标。
输出结果如下:
若将BATCH_SIZE改为8,则结果如下:
多次运行上述代码,发现结果均一致,而不是随机的。
这是因为我们在程序开头加入了这句:
torch.manual_seed()手动设置种子,一般可用于固定随机初始化的权重值(一旦固定种子,后面依次生成的随机数其实都是固定的)
这样就可以让每次重新训练网络时的权重的初始值虽然是随机生成的,但结果却是固定的。
深度学习的优化算法主要有GD,SGD,Momentum,RMSProp和Adam算法等。
深度学习中绝大多数目标函数都很复杂。因此,很多优化问题并不存在解析解,而需要使用基于数值方法的优化算法找到近似解,即数值解。以下讨论的优化算法都是这类基于数值方法的算法。
如果使用梯度下降(GD),每次自变量迭代的计算开销为O(n),它随着n线性增长。因此,当训练数据样本数很大时,梯度下降每次迭代的计算开销很高。
而随机梯度下降(stochastic gradient descent,SGD)减少了每次迭代的计算开销。在随机梯度下降的每次迭代中,我们随机均匀采样的一个样本索引i∈{1,…,n},并计算梯度∇fi(x)来迭代x。于是每次迭代的计算开销从梯度下降的O(n)降到了常数O(1)。
我们还可以在每轮迭代中随机均匀采样多个样本来组成一个小批量,然后使用这个小批量来计算梯度。称为小批量随机梯度下降。通常,小批量随机梯度在每个迭代周期的耗时介于梯度下降和随机梯度下降的耗时之间。
在SGD的每次迭代中,梯度下降根据自变量当前位置,沿着当前位置的梯度更新自变量。然而,如果自变量的迭代方向仅仅取决于自变量当前位置,这可能会带来一些问题。
同一位置上,目标函数在竖直方向比在水平方向的斜率的绝对值更大。因此,给定学习率,梯度下降迭代自变量时会使自变量在竖直方向比在水平方向移动幅度更大。
动量法依赖指数加权移动平均(exponentially weighted moving average)使得自变量的更新方向更加一致,从而降低发散的可能。
其中,动量超参数γ满足0≤γ<1。当γ=0时,动量法等价于小批量随机梯度下降。
AdaGrad算法根据自变量在每个维度的梯度值的大小来调整各个维度上的学习率,并让目标函数自变量中每个元素都分别拥有自己的学习率,从而避免统一的学习率难以适应所有维度的问题。
ϵ是为了维持数值稳定性而添加的常数,如10−6。
因为调整学习率时分母上的变量st一直在累加按元素平方的小批量随机梯度,所以目标函数自变量每个元素的学习率在迭代过程中一直在降低(或不变)。因此,当学习率在迭代早期降得较快且当前解依然不佳时,AdaGrad算法在迭代后期由于学习率过小,可能较难找到一个有用的解。
RMSProp算法对AdaGrad算法做了一点小小的修改。
不同于AdaGrad算法里状态变量st是截至时间步t所有小批量随机梯度gt按元素平方和,RMSProp算法将这些梯度按元素平方做指数加权移动平均。
RMSProp同时具备Momentum& AdaGrad两种方法的优势。
但我们还没把 Momentum合并完全, RMSProp 缺少了 Momentum 中的红框部分。
Adam方法的基本思路更像是融合Momentum和AdaGrad二者的优点, Adam算法在RMSProp算法的基础上对小批量随机梯度也做了指数加权移动平均。
Adam的优点主要在于经过偏置校正(使过去各时间步小批量随机梯度权值之和为1)后,每一次迭代学习率都有个确定范围,使得参数比较平稳。
大多数时候, 使用 Adam 都能又快又好的达到目标, 迅速收敛。