Skip to main content

moregeek program

4个例子帮你梳理pytorch的nn module-多极客编程

本文延续前一篇文章的例子。只是例子一样,代码实现是逐步优化的,但是知识点没什么必然关联。


几个例子帮你梳理PyTorch知识点(张量、autograd)




nn


计算图和autograd是定义复杂算子和自动求导的一个非常强大的范例;但是对于一些大型神经网络来说,原始的autograd可能有点低级。


在我们创建神经网络的时候,我们通常希望将其组织成一层一层的网络,以便进行运算和理解。这些网络层其中一些具有在模型学习过程中的可学习参数。


在TensorFlow中,像KerasTensorFlow-SlimTFLearn这样的包提供了对原始计算图的更高级别的抽象,这样构建神经网络变得更加的便捷。


当然在PyTorch中肯定也有这样的包了——nn,这个包定义了一组模块,这些模块大致相当于神经网络层。模块接收输入张量并计算输出张量,同时还可以保持内部状态,例如包含可学习参数的张量。nn还定义了一些在模型训练过程中常用的损失函数。


在这个例子中,我们使用nn继续来实现我们的$y=a+bx+cx^2+dx^3$到$sin(x)$的多项式模型网络:


import torch
import math

# 创建输入输出数据,这里是x和y代表[-π,π]之间的sin(x)的值
x = torch.linspace(-math.pi, math.pi, 2000)
y = torch.sin(x)

# 在这个例子里,输出y是一个关于(x, x^2, x^3)的线性函数
# 所以我们可以将其看做是一个线性神经网络
# 我们先准备好(x, x^2, x^3)的张量
p = torch.tensor([1, 2, 3])
xx = x.unsqueeze(-1).pow(p)

# 上边代码 x.unsqueeze(-1)之后,x形状变为(2000, 1),p形状是(3,)。
# 这样才能用到pytorch的矩阵计算广播机制获得形状为(2000, 3)的张量

# 使用nn定义我们的网络,网络就变成一个个层级结构
# nn.Sequential是一个包括其他模型的模型,将一层一层的网络或者模型按照顺序组织起来
# 线性模型使用线性函数从输入计算得到输出结果,并保持内部的weight和bias张量
# Flatten层是展开层,将输出转化为一维向量以适应y的形状

model = torch.nn.Sequential(
torch.nn.Linear(3, 1),
torch.nn.Flatten(0, 1)
)

# nn package里也包含一些常用的损失函数
# 在这里我们使用Mean Squared Error(MSE)作为我们的损失函数
loss_fn = torch.nn.MSELoss(reduction='sum')

learning_rate = 1e-6
for t in range(2000):

# 前向过程:将x传递给模型,让模型算出预测的y
# 模型已经写好了__call__操作,所以你可以像执行函数一样调用模型
# 当你这样做的时候,你要给模型传递一个输入张量,模型就能给你生成一个输出张量
y_pred = model(xx)

# 计算并输出loss
# 我们将预测值和真实值y传递给loss,loss函数会计算并返回一个包含loss结果的张量
loss = loss_fn(y_pred, y)
if t % 100 == 99:
print(t, loss.item())

# 在下一轮梯度更新之前清空一下
model.zero_grad()

# 反向过程:计算模型loss关于可学习参数的梯度
# 所有设置了requires_grad=True的张量的参数都被保留到张量中, 所以这个调用是更新所有的可学习参数
loss.backward()

# 使用梯度下降更新参数,每个参数都是一个张量,所以我们这一步和之前一样写就OK了
with torch.no_grad():
for param in model.parameters():
param -= learning_rate * param.grad

# 你可以像使用python的列表一样,使用索引获得模型的不同层
linear_layer = model[0]

# 在线性层中,参数是存储在weight和bias里边的
print(f'Result: y = {linear_layer.bias.item()} + {linear_layer.weight[:, 0].item()} x + {linear_layer.weight[:, 1].item()} x^2 + {linear_layer.weight[:, 2].item()} x^3')

结果:



99 1359.427978515625 <br>
199 903.5037841796875<br>
299 601.5579223632812<br>
399 401.5660705566406<br>
499 269.087890625<br>
599 181.32131958007812<br>
699 123.16854858398438<br>
799 84.63236999511719<br>
899 59.091712951660156<br>
999 42.16147994995117<br>
1099 30.937110900878906<br>
1199 23.49416160583496<br>
1299 18.55787467956543<br>
1399 15.28339958190918<br>
1499 13.110761642456055<br>
1599 11.668910026550293<br>
1699 10.711766242980957<br>
1799 10.076279640197754<br>
1899 9.654217720031738<br>
1999 9.373825073242188<br>
Result: y = 0.007346955593675375 + 0.8348207473754883 x + -0.0012674720492213964 x^2 + -0.09021244943141937 x^3





优化


到目前为止,我们已经通过使用torch.no_grad()自行改变含有可学习参数的张量来更新我们的模型权重。对于随机梯度下降等简单优化算法来说,这样实现起来也不是很困难,但是在实际实践中,我们可能需要用到更复杂的优化器,比如AdaGrad、RMSProp、Adam等来训练神经网络。


PyTorch中的optim包抽象了一些优化算法的思想,并对其进行了实现,以供我们直接调用。


接下来的这个例子中,我们将使用nn来定义我们的模型,然后在这里不再自己写优化了,而是直接使用optim包提供的RMSprop算法来优化模型:


import torch
import math

# 创建输入输出数据,这里是x和y代表[-π,π]之间的sin(x)的值
x = torch.linspace(-math.pi, math.pi, 2000)
y = torch.sin(x)

# 在这个例子里,输出y是一个关于(x, x^2, x^3)的线性函数
# 所以我们可以将其看做是一个线性神经网络
# 我们先准备好(x, x^2, x^3)的张量
p = torch.tensor([1, 2, 3])
xx = x.unsqueeze(-1).pow(p)

# 使用nn定义我们的网络,网络就变成一个个层级结构
model = torch.nn.Sequential(
torch.nn.Linear(3, 1),
torch.nn.Flatten(0, 1)
)

# 使用nn package提供的MSE loss
loss_fn = torch.nn.MSELoss(reduction='sum')

# 借助optim package定义一个优化器来更新我们模型的参数
# 在这里使用RMSprop优化
# optim package还有许多其他优化算法,感兴趣的自己去看文档
# RMSprop constructor的第一个参数是告诉优化器 需要去更新哪些张量
learning_rate = 1e-3
optimizer = torch.optim.RMSprop(model.parameters(), lr=learning_rate)

for t in range(2000):
# 前向过程,给模型输入x,计算获得对应的预测的y值
y_pred = model(xx)

# 计算并输出loss
loss = loss_fn(y_pred, y)
if t % 100 == 99:
print(t, loss.item())

# 在反向过程之前,使用优化器将所有待更新变量的梯度清零
# 待更新变量指的就是模型的可学习参数
# 因为模型情况下调用.backward()的时候不同步骤的梯度会进行积累,而不是每一步都重写
# 详细原因感兴趣的自己去查一下torch.autograd.backward的文档
optimizer.zero_grad()

# 反向过程,计算loss关于模型参数的梯度
loss.backward()

# 调用优化器的step方法,让其更新参数
optimizer.step()

linear_layer = model[0]
print(
f'Result: y = {linear_layer.bias.item()} + {linear_layer.weight[:, 0].item()} x + {linear_layer.weight[:, 1].item()} x^2 + {linear_layer.weight[:, 2].item()} x^3')

输出结果:



99 5328.826171875<br>
199 1865.436279296875<br>
299 1073.1275634765625<br>
399 827.1493530273438<br>
499 666.9290161132812<br>
599 517.2598876953125<br>
699 384.5140380859375<br>
799 275.61785888671875<br>
899 190.3560028076172<br>
999 125.3379898071289<br>
1099 77.24494171142578<br>
1199 43.95640563964844<br>
1299 23.847721099853516<br>
1399 13.23300552368164<br>
1499 9.694506645202637<br>
1599 8.976227760314941<br>
1699 8.994707107543945<br>
1799 8.907635688781738<br>
1899 8.882875442504883<br>
1999 8.901941299438477 <br>
Result: y = -6.768441807025738e-09 + 0.8572093844413757 x + -4.319689494991508e-09 x^2 + -0.09283572435379028 x^3



自定义nn Modules


通过前边的代码我们知道,我们可以使用pytorch提供的现有的网络层堆叠出我们自己的模型。但是有时候你可能需要更复杂的模型结构,不是简单的模块的堆叠,在这种时候你可以构建nn.Module的子类创造自己的模型结构,在其中定义好 forward 过程,接受输入张量,对其处理并得到输出张量,可以用到其他的模型或者autograd操作。


这个例子我们看一下怎么使用自定义的Module子类实现我们的三次多项式:


import torch
import math

class Polynomial3(torch.nn.Module):
def __init__(self):
# 在这个构造函数中我们复制四个参数
super().__init__()
self.a = torch.nn.Parameter(torch.randn(()))
self.b = torch.nn.Parameter(torch.randn(()))
self.c = torch.nn.Parameter(torch.randn(()))
self.d = torch.nn.Parameter(torch.randn(()))

def forward(self, x):
# 在forward函数中,我们接受一个输入张量,同时我们必须返回一个输出张量
# 计算张量的过程中我们可以使用构造函数中的模块以及任意的运算
return self.a + self.b * x + self.c * x ** 2 + self.d * x ** 3

def string(self):
# 和别的Python类一样,你可以在pytorch module中自定义方法
return f'y = {self.a.item()} + {self.b.item()} x + {self.c.item()} x^2 + {self.d.item()} x^3'

# 创建输入输出数据,这里是x和y代表[-π,π]之间的sin(x)的值
x = torch.linspace(-math.pi, math.pi, 2000)
y = torch.sin(x)

# 实例化我们上边自己实现的类来构造我们的模型
model = Polynomial3()

# 构造损失函数和优化器
#调用 SDG中的model.parameters(),将会自动报包含模型的可学习参数
criterion = torch.nn.MSELoss(reduction='sum')
optimizer = torch.optim.SGD(model.parameters(), lr=1e-6)
for t in range(2000):
# 前向过程,给模型输入x,计算获得对应的预测的y值
y_pred = model(x)

# 计算并输出loss
loss = criterion(y_pred, y)
if t % 100 == 99:
print(t, loss.item())

# 清空梯度,反向传播,更新参数!
optimizer.zero_grad()
loss.backward()
optimizer.step()

print(f'Result: {model.string()}')


输出结果:



99 719.6984252929688<br>
199 484.8442077636719<br>
299 327.8070068359375<br>
399 222.7333984375<br>
499 152.38116455078125<br>
599 105.24345397949219<br>
699 73.6366195678711<br>
799 52.42745590209961<br>
899 38.184112548828125<br>
999 28.61081886291504<br>
1099 22.170948028564453<br>
1199 17.835002899169922<br>
1299 14.912996292114258<br>
1399 12.941986083984375<br>
1499 11.611226081848145<br>
1599 10.711792945861816<br>
1699 10.103262901306152<br>
1799 9.691162109375<br>
1899 9.411775588989258<br>
1999 9.222151756286621 <br>
Result: y = 0.01420027855783701 + 0.8421593308448792 x + -0.002449784893542528 x^2 + -0.09125629812479019 x^3



流控制和权重共享


为了这个动态图和权重分享的例子,我们来实现一个比较奇怪的模型:实现一个3-5阶的多项式。每次前向过程都随机选一个3-5之间的整数,然后共享权重计算四阶和五阶多项式。


对于这个模型,我们可以使用普通的Python流控制来实现循环过程。对于实现权重共享,我们可以通过简单多次重用同样的参数来定义我们的前向过程。


我们可以通过Modele的子类实现这个模型:


import torch
import math


class DynamicNet(torch.nn.Module):
def __init__(self):
# 构造函数中我们要实例化五个参数
super().__init__()
self.a = torch.nn.Parameter(torch.randn(()))
self.b = torch.nn.Parameter(torch.randn(()))
self.c = torch.nn.Parameter(torch.randn(()))
self.d = torch.nn.Parameter(torch.randn(()))
self.e = torch.nn.Parameter(torch.randn(()))

def forward(self, x):
# 模型的前向过程,我们随机选择4或5,并重复使用e参数来计算这些阶的贡献。
# 由于每个前向传递都构建一个动态计算图,所以在定义模型的前向传递时,
# 我们可以使用普通的Python控制流操作符,如循环或条件语句。
# 这里我们还看到,在定义计算图时,多次重复使用同一参数是完全安全的。
y = self.a + self.b * x + self.c * x ** 2 + self.d * x ** 3
for exp in range(4, random.randint(4, 6)):
y = y + self.e * x ** exp
return y

def string(self):
# 和别的Python类一样,你可以在pytorch module中自定义方法
return f'y = {self.a.item()} + {self.b.item()} x + {self.c.item()} x^2 + {self.d.item()} x^3 + {self.e.item()} x^4 ? + {self.e.item()} x^5 ?'

# 创建输入输出数据,这里是x和y代表[-π,π]之间的sin(x)的值
x = torch.linspace(-math.pi, math.pi, 2000)
y = torch.sin(x)

# 实例化我们上边自己实现的类来构造我们的模型
model = DynamicNet()

# 构造我们的损失函数和优化器
# 训练这个奇怪的模型使用普通的梯度下降很困难,所以这里使用动量编码器
criterion = torch.nn.MSELoss(reduction='sum')
optimizer = torch.optim.SGD(model.parameters(), lr=1e-8, momentum=0.9)
for t in range(30000):
# 前向过程,给模型输入x,计算获得对应的预测的y值
y_pred = model(x)

# 计算并输出loss
loss = criterion(y_pred, y)
if t % 2000 == 1999:
print(t, loss.item())

# 清空梯度,反向传播,更新参数!
optimizer.zero_grad()
loss.backward()
optimizer.step()

print(f'Result: {model.string()}')

输出结果:



1999 899.025146484375<br>
3999 414.2640075683594<br>
5999 199.9871826171875<br>
7999 98.45460510253906<br>
9999 51.89286422729492<br>
11999 29.33873748779297<br>
13999 18.758146286010742<br>
15999 13.488505363464355<br>
17999 11.053787231445312<br>
19999 9.995283126831055<br>
21999 9.340238571166992<br>
23999 9.19015884399414<br>
25999 9.015266418457031<br>
27999 8.884875297546387<br>
29999 8.613398551940918 <br>
Result: y = 0.006221930030733347 + 0.8557982444763184 x + -0.0016655048821121454 x^2 + -0.09348824620246887 x^3 + 0.00011010735033778474 x^4 ? + 0.00011010735033778474 x^5 ?
》>



©著作权归作者所有:来自51CTO博客作者LolitaAnn的原创作品,请联系作者获取转载授权,否则将追究法律责任

【数据预处理】基于pandas的数据预处理技术【前七个任务】-多极客编程

一.需求分析 本文主题:基于Pandas的数据预处理技术 本次任务共分为16个任务,将其分为前七个任务和后11个任务,本文探讨其前七个任务。 本次实验内容: 本次实验以california_housing加州房价数据集为例,下载数据集 查看数据集的描述、特征及目标数据名称、数据条数、特征数量 将数据读入pandas的DataFrame并转存到csv文件 查看数据集各个特征的类型以及

消除视觉transformer与卷积神经网络在小数据集上的差距-多极客编程

摘要:本文通过多种操作构建混合模型,增强视觉Transformer捕捉空间相关性的能力和其进行通道多样性表征的能力,弥补了Transformer在小数据集上从头训练的精度与传统的卷积神经网络之间的差距。本文分享自华为云社区《​​[NeurIPS 2022] 消除视觉Transformer与卷积神经网络在小数据集上的差距​​》,作者:Hint。本文简要介绍NeurIPS 2022录用的论文“Brid

详解聚类算法kmeans-概述 & 工作原理【菜菜的sklearn课堂笔记】-多极客编程

视频作者:[菜菜TsaiTsai] 链接:[【技术干货】菜菜的机器学习sklearn【全85集】Python进阶_哔哩哔哩_bilibili] 有监督学习:模型在训练的时候,即需要特征矩阵X,也需要真实标签y。 无监督学习:监督的算法在训练的时候只需要特征矩阵X,不需要标签。PCA降维算法就是无监督学习中的一种,聚类算法,也是无监督学习的代表算法之一。 聚类算法又叫做“无监督分类”,其目的是将

基于u-net网络的图像分割的mindstudio实践-多极客编程

摘要:本实践是基于Windows版MindStudio 5.0.RC3,远程连接ECS服务器使用,ECS是基于官方分享的CANN6.0.RC1_MindX_Vision3.0.RC3镜像创建的。本文分享自华为云社区《​​【MindStudio训练营第一季】基于U-Net网络的图像分割的MindStudio实践​​》,作者:Tianyi_Li 。1.U-Net网络介绍:U-Net模型基于二维图像分割

图计算引擎分析 ——gemini-多极客编程

作者:京东科技 王军前言Gemini 是目前 state-of-art 的分布式内存图计算引擎,由清华陈文光团队的朱晓伟博士于 2016 年发表的分布式静态数据分析引擎。Gemini 使用以计算为中心的共享内存图分布式 HPC 引擎。通过自适应选择双模式更新(pull/push),实现通信与计算负载均衡 [‎1]。图计算研究的图是数据结构中的图,非图片。实际应用中遇到的图,如社交网络中的好友关系、

vivo 低代码平台【后羿】的探索与实践-多极客编程

作者:vivo 互联网前端团队- Wang Ning本文根据王宁老师在“2022 vivo开发者大会"现场演讲内容整理而成。本文主要从前后端分离的低代码方案、自研高性能渲染引擎、高效的可视化配置方案、千亿级内容投放、低代码如何与传统开发共存等五个维度vivo在低代码平台方面的实践经验,其中也会涉及到动态交互如何运用低代码来编排和我们在提高配置效率方面的全面探索。一、前言青春才几年,疫情占三年,后疫

【数据预处理】基于pandas的数据预处理技术【前七个任务】-多极客编程

一.需求分析 本文主题:基于Pandas的数据预处理技术 本次任务共分为16个任务,将其分为前七个任务和后11个任务,本文探讨其前七个任务。 本次实验内容: 本次实验以california_housing加州房价数据集为例,下载数据集 查看数据集的描述、特征及目标数据名称、数据条数、特征数量 将数据读入pandas的DataFrame并转存到csv文件 查看数据集各个特征的类型以及

消除视觉transformer与卷积神经网络在小数据集上的差距-多极客编程

摘要:本文通过多种操作构建混合模型,增强视觉Transformer捕捉空间相关性的能力和其进行通道多样性表征的能力,弥补了Transformer在小数据集上从头训练的精度与传统的卷积神经网络之间的差距。本文分享自华为云社区《​​[NeurIPS 2022] 消除视觉Transformer与卷积神经网络在小数据集上的差距​​》,作者:Hint。本文简要介绍NeurIPS 2022录用的论文“Brid

详解聚类算法kmeans-概述 & 工作原理【菜菜的sklearn课堂笔记】-多极客编程

视频作者:[菜菜TsaiTsai] 链接:[【技术干货】菜菜的机器学习sklearn【全85集】Python进阶_哔哩哔哩_bilibili] 有监督学习:模型在训练的时候,即需要特征矩阵X,也需要真实标签y。 无监督学习:监督的算法在训练的时候只需要特征矩阵X,不需要标签。PCA降维算法就是无监督学习中的一种,聚类算法,也是无监督学习的代表算法之一。 聚类算法又叫做“无监督分类”,其目的是将

基于u-net网络的图像分割的mindstudio实践-多极客编程

摘要:本实践是基于Windows版MindStudio 5.0.RC3,远程连接ECS服务器使用,ECS是基于官方分享的CANN6.0.RC1_MindX_Vision3.0.RC3镜像创建的。本文分享自华为云社区《​​【MindStudio训练营第一季】基于U-Net网络的图像分割的MindStudio实践​​》,作者:Tianyi_Li 。1.U-Net网络介绍:U-Net模型基于二维图像分割

图计算引擎分析 ——gemini-多极客编程

作者:京东科技 王军前言Gemini 是目前 state-of-art 的分布式内存图计算引擎,由清华陈文光团队的朱晓伟博士于 2016 年发表的分布式静态数据分析引擎。Gemini 使用以计算为中心的共享内存图分布式 HPC 引擎。通过自适应选择双模式更新(pull/push),实现通信与计算负载均衡 [‎1]。图计算研究的图是数据结构中的图,非图片。实际应用中遇到的图,如社交网络中的好友关系、

【数据预处理】基于pandas的数据预处理技术【california-多极客编程

一.需求分析 前七个任务的解决方案,请查看上篇文章: 本文主题:基于Pandas的数据预处理技术 本次任务共分为16个任务,将其分为前七个任务和后9个任务,本文探讨其后9个任务。 本次实验内容: 对第一个特征(收入中位数)排序后画散点图 对第一个特征(收入中位数)画分位数图并分析 【选做】对所有特征画分位数图并进行分析 使用散点图、使用线性回归方法拟合第一个特征(收入中位数)并分