nn.functional和nn.Module
Pytorch的低阶API主要包括张量操作,动态计算图和自动微分。
如果把模型比作一个房子,那么低阶API就是【模型之砖】。
在低阶API层次上,可以把Pytorch当做一个增强版的numpy来使用。
Pytorch提供的方法比numpy更全面,运算速度更快,如果需要的话,还可以使用GPU进行加速。
前面几章我们对低阶API已经有了一个整体的认识,本章我们将重点详细介绍张量操作和动态计算图。
张量的操作主要包括张量的结构操作和张量的数学运算。
张量结构操作诸如:张量创建,索引切片,维度变换,合并分割。
张量数学运算主要有:标量运算,向量运算,矩阵运算。另外我们会介绍张量运算的广播机制。
动态计算图我们将主要介绍动态计算图的特性,计算图中的Function,计算图与反向传播。
本篇我们介绍动态计算图。
import os
import datetime
#打印时间
def printbar():
nowtime = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
print("n"+"=========="*8 + "%s"%nowtime)
#mac系统上pytorch和matplotlib在jupyter中同时跑需要更改环境变量
os.environ["KMP_DUPLICATE_LIB_OK"]="TRUE"
一,nn.functional 和 nn.Module
前面我们介绍了Pytorch的张量的结构操作和数学运算中的一些常用API。
利用这些张量的API我们可以构建出神经网络相关的组件(如激活函数,模型层,损失函数)。
Pytorch和神经网络相关的功能组件大多都封装在 torch.nn模块下。
这些功能组件的绝大部分既有函数形式实现,也有类形式实现。
其中nn.functional(一般引入后改名为F)有各种功能组件的函数实现。例如:
(激活函数)
- F.relu
- F.sigmoid
- F.tanh
- F.softmax
(模型层)
- F.linear
- F.conv2d
- F.max_pool2d
- F.dropout2d
- F.embedding
(损失函数)
- F.binary_cross_entropy
- F.mse_loss
- F.cross_entropy
为了便于对参数进行管理,一般通过继承 nn.Module 转换成为类的实现形式,并直接封装在 nn 模块下。例如:
(激活函数)
- nn.ReLU
- nn.Sigmoid
- nn.Tanh
- nn.Softmax
(模型层)
- nn.Linear
- nn.Conv2d
- nn.MaxPool2d
- nn.Dropout2d
- nn.Embedding
(损失函数)
- nn.BCELoss
- nn.MSELoss
- nn.CrossEntropyLoss
实际上nn.Module除了可以管理其引用的各种参数,还可以管理其引用的子模块,功能十分强大。
二,使用nn.Module来管理参数
在Pytorch中,模型的参数是需要被优化器训练的,因此,通常要设置参数为 requires_grad = True 的张量。
同时,在一个模型中,往往有许多的参数,要手动管理这些参数并不是一件容易的事情。
Pytorch一般将参数用nn.Parameter来表示,并且用nn.Module来管理其结构下的所有参数。
import torch
from torch import nn
import torch.nn.functional as F
from matplotlib import pyplot as plt
# nn.Parameter 具有 requires_grad = True 属性
w = nn.Parameter(torch.randn(2,2))
print(w)
print(w.requires_grad)
Parameter containing:
tensor([[ 0.3544, -1.1643],
[ 1.2302, 1.3952]], requires_grad=True)
True
# nn.ParameterList 可以将多个nn.Parameter组成一个列表
params_list = nn.ParameterList([nn.Parameter(torch.rand(8,i)) for i in range(1,3)])
print(params_list)
print(params_list[0].requires_grad)
ParameterList(
(0): Parameter containing: [torch.FloatTensor of size 8x1]
(1): Parameter containing: [torch.FloatTensor of size 8x2]
)
True
# nn.ParameterDict 可以将多个nn.Parameter组成一个字典
params_dict = nn.ParameterDict({"a":nn.Parameter(torch.rand(2,2)),
"b":nn.Parameter(torch.zeros(2))})
print(params_dict)
print(params_dict["a"].requires_grad)
ParameterDict(
(a): Parameter containing: [torch.FloatTensor of size 2x2]
(b): Parameter containing: [torch.FloatTensor of size 2]
)
True
# 可以用Module将它们管理起来
# module.parameters()返回一个生成器,包括其结构下的所有parameters
module = nn.Module()
module.w = w
module.params_list = params_list
module.params_dict = params_dict
num_param = 0
for param in module.parameters():
print(param,"n")
num_param = num_param + 1
print("number of Parameters =",num_param)
Parameter containing:
tensor([[ 0.3544, -1.1643],
[ 1.2302, 1.3952]], requires_grad=True)
Parameter containing:
tensor([[0.9391],
[0.7590],
[0.6899],
[0.4786],
[0.2392],
[0.9645],
[0.1968],
[0.1353]], requires_grad=True)
Parameter containing:
tensor([[0.8012, 0.9587],
[0.0276, 0.5995],
[0.7338, 0.5559],
[0.1704, 0.5814],
[0.7626, 0.1179],
[0.4945, 0.2408],
[0.7179, 0.0575],
[0.3418, 0.7291]], requires_grad=True)
Parameter containing:
tensor([[0.7729, 0.2383],
[0.7054, 0.9937]], requires_grad=True)
Parameter containing:
tensor([0., 0.], requires_grad=True)
number of Parameters = 5
#实践当中,一般通过继承nn.Module来构建模块类,并将所有含有需要学习的参数的部分放在构造函数中。
#以下范例为Pytorch中nn.Linear的源码的简化版本
#可以看到它将需要学习的参数放在了__init__构造函数中,并在forward中调用F.linear函数来实现计算逻辑。
class Linear(nn.Module):
__constants__ = ['in_features', 'out_features']
def __init__(self, in_features, out_features, bias=True):
super(Linear, self).__init__()
self.in_features = in_features
self.out_features = out_features
self.weight = nn.Parameter(torch.Tensor(out_features, in_features))
if bias:
self.bias = nn.Parameter(torch.Tensor(out_features))
else:
self.register_parameter('bias', None)
def forward(self, input):
return F.linear(input, self.weight, self.bias)
三,使用nn.Module来管理子模块
一般情况下,我们都很少直接使用 nn.Parameter来定义参数构建模型,而是通过一些拼装一些常用的模型层来构造模型。
这些模型层也是继承自nn.Module的对象,本身也包括参数,属于我们要定义的模块的子模块。
nn.Module提供了一些方法可以管理这些子模块。
- children() 方法: 返回生成器,包括模块下的所有子模块。
- named_children()方法:返回一个生成器,包括模块下的所有子模块,以及它们的名字。
- modules()方法:返回一个生成器,包括模块下的所有各个层级的模块,包括模块本身。
- named_modules()方法:返回一个生成器,包括模块下的所有各个层级的模块以及它们的名字,包括模块本身。
其中chidren()方法和named_children()方法较多使用。
modules()方法和named_modules()方法较少使用,其功能可以通过多个named_children()的嵌套使用实现。
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.embedding = nn.Embedding(num_embeddings = 10000,embedding_dim = 3,padding_idx = 1)
self.conv = nn.Sequential()
self.conv.add_module("conv_1",nn.Conv1d(in_channels = 3,out_channels = 16,kernel_size = 5))
self.conv.add_module("pool_1",nn.MaxPool1d(kernel_size = 2))
self.conv.add_module("relu_1",nn.ReLU())
self.conv.add_module("conv_2",nn.Conv1d(in_channels = 16,out_channels = 128,kernel_size = 2))
self.conv.add_module("pool_2",nn.MaxPool1d(kernel_size = 2))
self.conv.add_module("relu_2",nn.ReLU())
self.dense = nn.Sequential()
self.dense.add_module("flatten",nn.Flatten())
self.dense.add_module("linear",nn.Linear(6144,1))
self.dense.add_module("sigmoid",nn.Sigmoid())
def forward(self,x):
x = self.embedding(x).transpose(1,2)
x = self.conv(x)
y = self.dense(x)
return y
net = Net()
i = 0
for child in net.children():
i+=1
print(child,"n")
print("child number",i)
Embedding(10000, 3, padding_idx=1)
Sequential(
(conv_1): Conv1d(3, 16, kernel_size=(5,), stride=(1,))
(pool_1): MaxPool1d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
(relu_1): ReLU()
(conv_2): Conv1d(16, 128, kernel_size=(2,), stride=(1,))
(pool_2): MaxPool1d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
(relu_2): ReLU()
)
Sequential(
(flatten): Flatten()
(linear): Linear(in_features=6144, out_features=1, bias=True)
(sigmoid): Sigmoid()
)
child number 3
i = 0
for name,child in net.named_children():
i+=1
print(name,":",child,"n")
print("child number",i)
embedding : Embedding(10000, 3, padding_idx=1)
conv : Sequential(
(conv_1): Conv1d(3, 16, kernel_size=(5,), stride=(1,))
(pool_1): MaxPool1d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
(relu_1): ReLU()
(conv_2): Conv1d(16, 128, kernel_size=(2,), stride=(1,))
(pool_2): MaxPool1d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
(relu_2): ReLU()
)
dense : Sequential(
(flatten): Flatten()
(linear): Linear(in_features=6144, out_features=1, bias=True)
(sigmoid): Sigmoid()
)
child number 3
i = 0
for module in net.modules():
i+=1
print(module)
print("module number:",i)
Net(
(embedding): Embedding(10000, 3, padding_idx=1)
(conv): Sequential(
(conv_1): Conv1d(3, 16, kernel_size=(5,), stride=(1,))
(pool_1): MaxPool1d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
(relu_1): ReLU()
(conv_2): Conv1d(16, 128, kernel_size=(2,), stride=(1,))
(pool_2): MaxPool1d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
(relu_2): ReLU()
)
(dense): Sequential(
(flatten): Flatten()
(linear): Linear(in_features=6144, out_features=1, bias=True)
(sigmoid): Sigmoid()
)
)
Embedding(10000, 3, padding_idx=1)
Sequential(
(conv_1): Conv1d(3, 16, kernel_size=(5,), stride=(1,))
(pool_1): MaxPool1d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
(relu_1): ReLU()
(conv_2): Conv1d(16, 128, kernel_size=(2,), stride=(1,))
(pool_2): MaxPool1d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
(relu_2): ReLU()
)
Conv1d(3, 16, kernel_size=(5,), stride=(1,))
MaxPool1d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
ReLU()
Conv1d(16, 128, kernel_size=(2,), stride=(1,))
MaxPool1d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
ReLU()
Sequential(
(flatten): Flatten()
(linear): Linear(in_features=6144, out_features=1, bias=True)
(sigmoid): Sigmoid()
)
Flatten()
Linear(in_features=6144, out_features=1, bias=True)
Sigmoid()
module number: 13
下面我们通过named_children方法找到embedding层,并将其参数设置为不可训练(相当于冻结embedding层)。
children_dict = {name:module for name,module in net.named_children()}
print(children_dict)
embedding = children_dict["embedding"]
embedding.requires_grad_(False) #冻结其参数
{'embedding': Embedding(10000, 3, padding_idx=1), 'conv': Sequential(
(conv_1): Conv1d(3, 16, kernel_size=(5,), stride=(1,))
(pool_1): MaxPool1d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
(relu_1): ReLU()
(conv_2): Conv1d(16, 128, kernel_size=(2,), stride=(1,))
(pool_2): MaxPool1d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
(relu_2): ReLU()
), 'dense': Sequential(
(flatten): Flatten()
(linear): Linear(in_features=6144, out_features=1, bias=True)
(sigmoid): Sigmoid()
)}
#可以看到其第一层的参数已经不可以被训练了。
for param in embedding.parameters():
print(param.requires_grad)
print(param.numel())
False
30000
from torchkeras import summary
summary(net,input_shape = (200,),input_dtype = torch.LongTensor)
# 不可训练参数数量增加
----------------------------------------------------------------
Layer (type) Output Shape Param #
================================================================
Embedding-1 [-1, 200, 3] 30,000
Conv1d-2 [-1, 16, 196] 256
MaxPool1d-3 [-1, 16, 98] 0
ReLU-4 [-1, 16, 98] 0
Conv1d-5 [-1, 128, 97] 4,224
MaxPool1d-6 [-1, 128, 48] 0
ReLU-7 [-1, 128, 48] 0
Flatten-8 [-1, 6144] 0
Linear-9 [-1, 1] 6,145
Sigmoid-10 [-1, 1] 0
================================================================
Total params: 40,625
Trainable params: 10,625
Non-trainable params: 30,000
----------------------------------------------------------------
Input size (MB): 0.000763
Forward/backward pass size (MB): 0.287796
Params size (MB): 0.154972
Estimated Total Size (MB): 0.443531
----------------------------------------------------------------
- 更强悍的Silverlight: WCF RIA Services
- Java究竟该怎么学?文末有彩蛋!
- python-IDLE清屏和标记行数,其他推荐
- 从0到1:PostCSS 插件开发最佳实践
- VUE 入门基础(4)
- 实例演示Android异步加载图片
- 使用OData协议查询Windows日志
- Android之Notification介绍
- postcss-lazysprite: 一种生成CSS 雪碧图的懒惰姿势
- Activity间中使用Intent传值
- VUE 入门基础(3)
- ASP.NET MVC 2 转换工具
- 使用Sysinternals工具定时休眠Windows Server 2008 R2
- Android中BroadcastReceiver广播
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- leetcode树之二叉树的深度
- Nic*app的native层算法破解
- ActiveMq的顺序性消费问题
- 佳能遭严重勒索软件攻击,10TB数据被窃取,大量服务宕机
- 进程线程剖析(二)-进程组成、状态与特点
- 【Flink】基于 Flink 实时计算商品订单流失量
- 这就是你日日夜夜想要的docker!!!---------TLS加密远程连接Docker
- 【Flink】基于 Flink 的流式数据实时去重
- 从零开始安装穿透式检索
- 使用kind搭建kubernetes
- 如何设计一个牛逼的API接口
- 猿实战17——实现你未必知晓的运费模板
- 这 5 个 VSCode 扩展提高你的开发效率
- 猿实战18——商品发布之类目选择
- 树莓派基础实验1:双色LED灯实验