热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

《深度学习框架PyTorch入门与实践》学习笔记第四章torch.nn

#-------神经网络工具箱nn------------------------------------------#torch.nn是专门为深度学习而设计的模块,

# -------神经网络工具箱nn------------------------------------------
# torch.nn是专门为深度学习而设计的模块,torch.nn的核心数据结构是Module
# 既可以表示神经网络中的某个层(layer),也可以表示包含很多层的神经网络
# 实际使用中,最常用的做法是继承nn.Module,撰写自己的网络/层,
# 全连接层有名仿射层,import torch as t
from torch import nn
import pylab# 全连接层的实现
# PyTorch的nn.Linear()是用于设置网络中的全连接层的,
# 需要注意在二维图像处理的任务中,全连接层的输入与输出一般都设置为二维张量,
# 形状通常为[batch_size, size],不同于卷积层要求输入输出是四维张量。
class Linear(nn.Module): # 继承nn.Module父类def __init__(self, in_features, out_features): # 必须初始化构造函数super(Linear, self).__init__() # 等价于nn.Module.__init__(self)self.w = nn.Parameter(t.randn(in_features, out_features)) # 自封装学习参数self.b = nn.Parameter(t.randn(out_features))def forward(self, x):x = x.mm(self.w)return x + self.b.expand_as(x)# 无需写后向传播过程,nn.Module会利用autograd自动实现反向传播# in_features指的是输入的二维张量的大小,即输入的[batch_size, size]中的size。
#   out_features指的是输出的二维张量的大小,
# 即输出的二维张量的形状为[batch_size,output_size],
# 当然,它也代表了该全连接层的神经元个数。
#   从输入输出的张量的shape角度来理解,
# 相当于一个输入为[batch_size, in_features]的张量
# 变换成了[batch_size, out_features]的输出张量。layer = Linear(4, 3) # in_features=4,out_features=3
input = t.randn(2, 4) # (batch_size,in_feature)
output = layer(input)
print(output)for name, parameter in layer.named_parameters():print(name, parameter) # w and b# 全连接层实现非常简单,但是需要注意以下几点:
# (1)自定义层Linear必须继承nn.Module,并且在其构造函数中需调用nn.Module的构造函数,
# 即super(Linear,self).__init__()
# (2)在构造函数__init__中必须自己定义课学习的参数,并封装成Parameter# ---------多层感知机---------------------------------------------------------
# 它由两个全连接层组成,采用sigmoid作为激活函数
class Perceptron(nn.Module): # 继承nn.Module父类def __init__(self, in_features, hidden_features, out_features):nn.Module.__init__(self) # 必须调用构造函数,初始化参数# 必须封装自定义参数self.layer1 = Linear(in_features, hidden_features) # 此处的Linear是前面自定义的全连接层self.layer2 = Linear(hidden_features, out_features)def forward(self, x):x = self.layer1(x)x = t.sigmoid(x)return self.layer2(x)perceptron = Perceptron(3, 4, 1)
for name, param in perceptron.named_parameters():print(name, param.size())
# layer1.w torch.Size([3, 4])
# layer1.b torch.Size([4])
# layer2.w torch.Size([4, 1])
# layer2.b torch.Size([1])# 构造函数__init__中,可利用前面自定义的Linear层(module)
# 作为当前module对象的一个子module,它的可学习参数,也会成为当前module的可学习参数# ---module中parameter的命名规范---------
# 输入输出的形状,如nn.linear的输入形状是(N,input_features),
# 输出为(N,output_features),N是batch_size
# 这些自定义Layer对输入形状都有形状,输入的不是单个数据,而是一个batch
# 输入只有一个数据,则必须调用tensor.unsqueeze(0)或tensor[None]将
# 将数据伪装成batch_size=1的batch# ------------4.1 常用神经网络层---------------------------------------------------
# ----------4.1.1 图像相关层-------------------------------
# 图像相关层主要包括卷积层(Conv)\池化层(Pool),这些层在实际使用中可分为
# 一维(1D),二维(2D),三维(3D),池化方式又分为平均池化(AvgPool),
# 最大值池化(MaxPool),自适应池化(Adaptive AvgPool)等
# 而卷积层除了常用的前向卷积之外,还有逆卷积(TransposeConcv)
# 一般来说,一维卷积用于文本数据,二维卷积用于图像数据,
# 对宽度和高度都进行卷积,三维卷积用于视频及3D图像处理领域(检测动作及人物行为),
# 对立方体的三个面进行卷积 。二维卷积的用处范围最广,在计算机视觉中广泛应用。
from PIL import Image
from torchvision.transforms import ToTensor, ToPILImage
import numpy as npto_tensor = ToTensor() # Img->tensor
to_pil = ToPILImage() # tensor->Img
lena = Image.open('lena.png')
print(lena)
# lena.show() # 显示图片
# 将PIL Image图片转换为numpy数组
im_arry = np.array(lena)
print(im_arry.size) # 40000
# 也可以np.asarray(im)区别是np.array()是深拷贝,np.asarray()是浅拷贝# 输入是一个batch,batch_size=1
# 大小为200*200
input = to_tensor(lena).unsqueeze(0)
# 锐化卷积核
kernel = t.ones(3, 3) / -9.
print('kernel:', kernel)
kernel[1][1] = 1
print('kernel:', kernel)
# 锐化卷积核
# kernel: tensor([[-0.1111, -0.1111, -0.1111],
# [-0.1111, 1.0000, -0.1111],
# [-0.1111, -0.1111, -0.1111]])
# 输入通道,输出通道,步长,卷积核大小,,是否添加偏置进行参数学习
conv = nn.Conv2d(1, 1, (3, 3), 1, bias=False)
conv.weight.data = kernel.view(1, 1, 3, 3)
# 这里卷积核w的参数个数是(3*3*3+1)*1,
# (输入通道3,卷积核大小3*3,一个偏置,输出通道1)
print(conv.weight.data)
out = conv(input)
# to_pil(out.data.squeeze(0)).show()# 池化层可以看作一种特殊的卷积层,用来下采样,
# # 但池化层没有可学习参数,其weight是固定的
pool = nn.AvgPool2d(2, 2) # 2维最大化操作
print(list(pool.parameters())) # []没有参数
out = pool(input)
# to_pil(out.data.squeeze(0)).show()
# 转化为一维数组才可以,squeeze(0),维度为1的压缩# 除了卷积层和池化层,深度学习还将常用到
# Linear:全连接层;
# BatchNorm:批规范化层,分为1D,2D,3D,
# 除了标准的BatchNorm之外,还有风格迁移中常用InstanceNorm层
# Dropout层,用来防止过拟合,同样分为1D,2D,3D
# 输入batch_size=2,维度3# (1)Linear层
input = t.randn(2, 3) # [-1,1]
linear = nn.Linear(3, 4) # 输入,输出
h = linear(input)
print(h)
# tensor([[-0.2813, -1.1104, -0.6374, 1.0212],
# [ 0.1217, -1.2646, -0.1236, 0.5959]], grad_fn=)# (2)BatchNorm---批规范化层
# 4 channel,初始化标准差为4,均值为0
bn = nn.BatchNorm1d(4)
bn.weight.data = t.ones(4) * 4
bn.bias.data = t.zeros(4)
bn_out = bn(h)
print(bn_out)
# tensor([[ 3.9999, -4.0000, -3.9995, 3.9931],
# [-3.9999, 4.0000, 3.9995, -3.9931]],
# grad_fn=)
print(bn_out.mean(0), "\n", bn_out.var(0, unbiased=False)) # 0/1代表维度
# 输出均值为0,方差为16(有正负)
# 方差是标准差的平方,计算无偏分差分母会减一
# 使用unbiased=False,分母不减一# (3)Dropout层
dropout = nn.Dropout(0.5) # 每个元素以0.5的概率舍弃
out = dropout(bn_out)
print(bn_out)
print(out) # 有一半左右的数变为0
# tensor([[-7.9998, 7.9998, -7.9998, -0.0000],
# [ 0.0000, -0.0000, 7.9998, 0.0000]], grad_fn=)# a=t.rand(2,4)*5
# print(a)
# tensor([[4.4786, 4.4752, 3.1746, 3.7649],
# [2.7463, 0.6550, 4.0261, 1.9380]])
# dropout = nn.Dropout(0.5)
# out_a=dropout(a)
# print(out_a)
# tensor([[0.0000, 8.9505, 0.0000, 0.0000],
# [5.4926, 0.0000, 8.0522, 0.0000]])# ----------------------4.1.2 激活函数-------------------------------------------------
relu = nn.ReLU(inplace=True)
input = t.randn(2, 3)
print(input)
output = relu(input) # max(0,x),小于0的都被截断为0
print(output) # 等价于input.clamp(min=0)
# 在以上的例子中,将每一层的输出直接作为下一层的输入,这种网络称为前馈传播网络
# feedforward neural network
# 对于此类网络如果每次都写复杂的forward函数比较,简化方式为ModuleList和Sequential
# 其中Sequential是一种特殊的Module,包含几个子Module,前向传播时会将输入一层接一层的传递下去
# ModuleList一个特殊的module,可以包含几个子module,可以像用list一样使用它
# 但不能直接把输入传给ModuleList# Sequential的三种写法
net1 = nn.Sequential()
net1.add_module('conv', nn.Conv2d(3, 3, 3)) # 输入通道,输出通道,卷积核大小
net1.add_module('batchnorm', nn.BatchNorm2d(3)) # 均值0,标准差3
net1.add_module('activation_layer', nn.ReLU()) # 激活层net2 = nn.Sequential(nn.Conv2d(3, 3, 3), nn.BatchNorm2d(3), nn.ReLU())from collections import OrderedDictnet3 = nn.Sequential(OrderedDict([('conv1', nn.Conv2d(3, 3, 3)),('bn1', nn.BatchNorm2d(3)),('relu1', nn.ReLU())
]))print('net1:', net1)
print('net2:', net2)
print('net3:', net3)
# net1: Sequential(
# (conv): Conv2d(3, 3, kernel_size=(3, 3), stride=(1, 1))
# (batchnorm): BatchNorm2d(3, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
# (activation_layer): ReLU()
# )
# net2: Sequential(
# (0): Conv2d(3, 3, kernel_size=(3, 3), stride=(1, 1))
# (1): BatchNorm2d(3, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
# (2): ReLU()
# )
# net3: Sequential(
# (conv1): Conv2d(3, 3, kernel_size=(3, 3), stride=(1, 1))
# (bn1): BatchNorm2d(3, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
# (relu1): ReLU()
# )
# BatchNorm1d(num_features, eps=1e-05, momentum=0.1, affine=True)
# num_features=batch_size,
# eps:为保持数值稳定性(分母不能趋近或取0),给分母加上的数值,默认为1e-5
# momentum:动态均值和动态方差使用的动量,默认为0.1
# affine:一个布尔值,当设为true,给该层添加可学习的仿射变换参数# 可根据名字或序号取出子module
print(net1.conv, "\n", net2[0], "\n", net3.conv1)
# Conv2d(3, 3, kernel_size=(3, 3), stride=(1, 1))
# Conv2d(3, 3, kernel_size=(3, 3), stride=(1, 1))
# Conv2d(3, 3, kernel_size=(3, 3), stride=(1, 1))input = t.rand(1, 3, 4, 4)
print(input)
print(net1(input)) # 4-3+1=2
# tensor([[[[0.9605, 0.9013],
# [0.0000, 0.0000]],
#
# [[0.0758, 1.5793],
# [0.0000, 0.0000]],
#
# [[0.0000, 1.2621],
# [0.0000, 0.5575]]]], grad_fn=)
output = net2(input)
output = net3(input)
output = net3.relu1(net1.batchnorm(net1.conv(input)))# ModuleList的写法
modellist = nn.ModuleList([nn.Linear(3, 4), nn.ReLU(), nn.Linear(4, 2)])
input = t.randn(1, 3)
for model in modellist:input = model(input)# 下面会报错,因为modelist没有实现forward方法
# output=modelist(input)# 不使用Python自带的list,因为Modulelist是Module的子类,
# 当在Module中它时,就能自动识别为子module
# 举例说明
class MyModule(nn.Module): # 继承父类Moduledef __init__(self):super(MyModule, self).__init__() # 必须初始化构造函数self.list = [nn.Linear(3, 4), nn.ReLU()]self.module_list = nn.ModuleList([nn.Conv2d(3, 3, 3), nn.ReLU()])def forward(self):passmodel = MyModule()
print(model)
# MyModule(
# (module_list): ModuleList(
# (0): Conv2d(3, 3, kernel_size=(3, 3), stride=(1, 1))
# (1): ReLU()
# )
# )
for name, param in model.named_parameters():print(name, param.size())
# module_list.0.weight torch.Size([3, 3, 3, 3])
# module_list.0.bias torch.Size([3])
# 可见,list的子Module不能被主module所识别,而Mdolelist中的子module能够被主module
# 所识别,这意味着如果用List保存子module,将无法调整参数,因为并未加入到主Module的参数中# ------------------------4.1.3 循环神经网络(RNN)------------------------------------------------
# 说明:LSTM有7个参数,前三个是必须输入的参数
# (1)input_size:输入的特征维度
# (2)hidden_size:隐状态的特征维度
# (3)num_layer:层数(和时序展开要区分开)
# (4)bias:如果False,那么LSTM不会使用b,默认为True
# (5)batch_first:如果为True,输入输出形状为(batch,seq,feature)
# (6)dropout-如果为非零的话,将在RNN每层后加一个dropout,最后一层除外
# (7)bidirectional如果为True,将会变成双向RNN,默认为False# 设置随机数种子
t.manual_seed(1000)
# 输入:batch_size=3,序列长度都为2,序列中每个元素占4维
input = t.randn(2, 3, 4)
# lstm输入层特征维度4维,隐藏层特征维度为3,共堆叠1层
lstm = nn.LSTM(4, 3, 1)
# lstm输入input,(h0,c0)
# h_0保存着batch中每个元素的初始化隐状态的tensor
# c_0保存着batch中每个元素的初始化细胞状态的tensor
# 初始状态:1层,batch_size=3,3个隐藏元
h0 = t.randn(1, 3, 3)
c0 = t.randn(1, 3, 3)
out, hn = lstm(input, (h0, c0))
print(out)
# tensor([[[-0.3610, -0.1643, 0.1631],
# [-0.0613, -0.4937, -0.1642],
# [ 0.5080, -0.4175, 0.2502]],
#
# [[-0.0703, -0.0393, -0.0429],
# [ 0.2085, -0.3005, -0.2686],
# [ 0.1482, -0.4728, 0.1425]]], grad_fn=)
t.manual_seed(1000)
input = t.randn(2, 3, 4)
# LSTMCell的输入参数
# input_size:输入的特征维度
# hidden_size:隐状态的维度
# bias:如果False,将不会使用bias,默认为True
# 一个LSTMCell对应的层数只能是一层
lstm = nn.LSTMCell(4, 3)
hx = t.randn(3, 3)
cx = t.randn(3, 3)
out = []
for i_ in input:hx, cx = lstm(i_, (hx, cx))out.append(hx)
print(t.stack(out))
# tensor([[[-0.3610, -0.1643, 0.1631],
# [-0.0613, -0.4937, -0.1642],
# [ 0.5080, -0.4175, 0.2502]],
#
# [[-0.0703, -0.0393, -0.0429],
# [ 0.2085, -0.3005, -0.2686],
# [ 0.1482, -0.4728, 0.1425]]], grad_fn=)# -----------------------------4.1.4 损失函数----------------------------------------------------------
# 深度学习中损失函数(loss function),也可以看做一种特殊的layer,
# PyTorch也将这些损失函数实现为nn.Module的子类,
# 然而实际使用中通常将这些loss function专门提取出来,与主模型相互独立# batch_size=3,计算对应每个类别的分数(只有两个类别)
score = t.randn(3, 2) # [正态分布]
print(score)
# 三个样本分别属于1,0,1类,label必须是LongTensor
label = t.Tensor([1, 0, 1]).long()
# loss与普通的layer无差异
criterion = nn.CrossEntropyLoss() # 交叉熵损失函数
loss = criterion(score, label)
print(loss)# --------------------------4.2 优化器--------------------------------------------------------------------
# PyTorch将深度学习中常用的优化方法封装在torch.optim中
# 所有的优化方法都是继承基类optim.Optimizer,并实现了自己的优化步骤
# 最基本的优化方法,随机梯度下降法(SGD)
# 主要内容分别是:
# (1)优化方法的基本使用方法
# (2)对模型的不同部分设置不同的学习率
# (3)调整学习率
# 首先定义一个LetNet网络
class Net(nn.Module): # 继承基类Moduledef __init__(self): # 初始化构造函数super(Net, self).__init__()self.features = nn.Sequential( # Sequential会自动将一层的输出到下一层的输入nn.Conv2d(3, 6, 5), # 输入通道,输出通道,卷积核大小nn.ReLU(),nn.MaxPool2d(2, 2), # 池化窗口大小nn.Conv2d(6, 16, 5), # 输入通道,输出通道,卷积核大小nn.ReLU(),nn.MaxPool2d(2, 2) # 池化窗口大小)self.classifier = nn.Sequential(nn.Linear(16 * 5 * 5, 120), # 输入维度,输出维度nn.ReLU(),nn.Linear(120, 84),nn.ReLU(),nn.Linear(84, 10))def forward(self, x):x = self.features(x)x = x.view(-1, 16 * 5 * 5)x = self.classifier(x)return xnet = Net()
from torch import optimoptimizer = optim.SGD(params=net.parameters(), lr=1)
optimizer.zero_grad() # 梯度清零,等价于net.zeros_grad()
input = t.randn(1, 3, 32, 32)
output = net(input)
output.backward(output) # fake backward
optimizer.step() # 执行优化
print("----------------------")
print(optimizer)
# SGD (
# Parameter Group 0
# dampening: 0
# lr: 1
# momentum: 0
# nesterov: False
# weight_decay: 0
# )
# ------------(1)为不同子网络设置不同的学习率,在finetune中经常用到---------------
# 如果对某个参数不指定学习率,就是用最外层的默认学习率
optimizer = optim.SGD([{'params': net.features.parameters()},{'params': net.classifier.parameters(), 'lr': 1e-2}
], lr=1e-5)
print("----------------------")
print(optimizer)
# SGD (
# Parameter Group 0
# dampening: 0
# lr: 1e-05
# momentum: 0
# nesterov: False
# weight_decay: 0
#
# Parameter Group 1
# dampening: 0
# lr: 0.01
# momentum: 0
# nesterov: False
# weight_decay: 0
# )
# --------------------(2)只为两个全连接层设置较大的学习率,其余层的学习率较小--------------
special_layers = nn.ModuleList([net.classifier[0], net.classifier[3]])
special_layers_params = list(map(id, special_layers.parameters()))
base_params = filter(lambda p: id(p) not in special_layers_params,net.parameters())
optimizer = t.optim.SGD([{'params': base_params},{'params': special_layers.parameters(), 'lr': 0.01}
], lr=0.001)
print("------------------")
print(optimizer)
# SGD (
# Parameter Group 0
# dampening: 0
# lr: 0.001
# momentum: 0
# nesterov: False
# weight_decay: 0
#
# Parameter Group 1
# dampening: 0
# lr: 0.01
# momentum: 0
# nesterov: False
# weight_decay: 0
# )# --------------调整学习率-----------------------
# 对于如何调整学习率,主要两种做法:
# (1)修改optimizer.params_groups中对应的学习率,
# (2)新建优化器--更简单,较为推荐
# 由于optimizer十分轻量级,构建开销很小,故而可以构建新的optimizer
# 但是后者对于使用动量的优化器,会丢失动量等状态信息,
# 可能会造成损失函数的收敛出现震荡等情况
# --------------方法1:调整学习率,新建一个optimizer------------
old_lr = 0.01
optimizer1 = optim.SGD([{'params': net.features.parameters()},{'params': net.classifier.parameters(), 'lr': old_lr * 0.1}
], lr=1e-5)
print(optimizer1)
# SGD (
# Parameter Group 0
# dampening: 0
# lr: 1e-05
# momentum: 0
# nesterov: False
# weight_decay: 0
#
# Parameter Group 1
# dampening: 0
# lr: 0.001
# momentum: 0
# nesterov: False
# weight_decay: 0
# )
# ----------方法2:调整学习率,手动decay,保存动量-------------------------
for param_group in optimizer.param_groups:param_group['lr'] *= 0.1 # 学习率为之前的0.1倍
print(optimizer)
# SGD (
# Parameter Group 0
# dampening: 0
# lr: 0.0001
# momentum: 0
# nesterov: False
# weight_decay: 0
#
# Parameter Group 1
# dampening: 0
# lr: 0.001
# momentum: 0
# nesterov: False
# weight_decay: 0
# )# -----------------------------4.3 nn.functional------------------------------------------------
# nn中很常用的模块,nn.functional,nn中多数layer在functional中都有一个与之相对应的函数
# nn.functional中的函数和nn.Module的主要区别在于,用nn.Module实现的layers是一个特殊的类
# 都是由class layer(nn.Module)定义,会自动提取可学习的参数,而nn.functional中的函数
# 更像一个纯函数,由def function(input)定义
input = t.randn(2, 3)
model = nn.Linear(3, 4)
output1 = model(input)
output2 = nn.functional.linear(input, model.weight, model.bias)
print(output1 == output2)
print(output1)
print(output2) # 输出一模一样b = nn.functional.relu(input)
b2 = nn.ReLU()(input)
print(b == b2) # 一样
# 如果模型(卷积,全连接)有可学习的参数,最好用nn.Module
# 否则(激活函数,池化)都可以,二者在性能上没有太大差异
# 另外虽然dropout没有可学习参数,但建议nn.Dropout
# 因为dropout在训练和测试两个阶段的行为有所差别,
# 使用nn.Module对象能够通过model.eval操作加以区分
# 下列在模型中搭配使用nn.Module和nn.functional
from torch.nn import functional as Fclass Net(nn.Module):def __init__(self):super(Net, self).__init__()self.conv1 = nn.Conv2d(3, 6, 5),self.conv2 = nn.Conv2d(6, 16, 5)self.fc1 = nn.Linear(16 * 5 * 5, 120)self.fc2 = nn.Linear(120, 84)self.fc3 = nn.Linear(84, 10)def forward(self, x):x = F.pool(F.relu(self.conv1(x)), 2)x = F.pool(F.relu(self.conv2(x)), 2)x = x.view(-1, 16 * 5 * 5)x = F.relu(self.fc1(x))x = F.relu(self.fc2(x))x = self.fc3(x)return x# 对于不具备可学习参数的(激活、池化),将它们用函数代替,这样可以不用放置在
# 构造函数__init__中,对于由可学习参数的模块,也可以用funciotnal代替
# 但是较为繁琐,需要自定义参数parameter,如前面的实现的自定义全连接层
# 可以将weight和bias两个参数单独拿出来,在构造函数中初始化parameter
# class MyLinear(nn.Module): # 继承基类Module
# def __init__(self): # 必须初始化构造函数
# super(MyLinear, self).__init__()
# self.weight = nn.Parameter(t.randn(3, 4))
# self.bias = nn.Parameter(t.zeros(3))
#
# def forward(self): ##----------!!!------------
# return F.linear(input, weight, bias)# --------------------------4.4 初始化策略-----------------------------------------------
# 深度学习中参数的初始化,良好的初始化能让模型更快收敛,糟糕的初始化可能是的模型迅速瘫痪
# PyTorch中nn.Module的模块参数采取了较为合理的初始化策略
# PyTorch中nn.init模块就是专门为初始化而设计,
# 如果某种初始化策略nn.init不提供,用户也可以自己直接初始化
# (1)利用nn.init初始化-----------
from torch.nn import initlinear = nn.Linear(3, 4)
# 设置随机数种子
t.manual_seed(1)
# 等价于 linear.weight.data.normal_(0,std)
# 用一个均匀分布生成之,填充输入的张量或变量,结果张量中的值采样子U(-bound,bound)
init.xavier_normal_(linear.weight)
print(linear.weight)
# (2)直接初始化------------------
import matht.manual_seed(1)
# xavier初始化的计算公式
std = math.sqrt(2) / math.sqrt(7.)
linear.weight.data.normal_(0, std)
print(linear.weight)# (3)对模型所有参数进行初始化
for name, params in net.named_parameters():if name.find('linear') != -1:# 初始化Linearparam[0] # weightparam[1] # biaselif name.find('conv') != -1:passelif name.find('norm') != -1:pass# ---------------------------4.5 mm.Module深入分析-----------------------------------------------------------
# nn.Module基类的构造函数
# def __init__(self):
# self._parameters=OrderedDict()
# self._modules=OrderedDict()
# self._buffers=OrderedDict()
# self._backward_hooks=OrderedDict()
# self._forward_hooks=OrderedDict()
# self.training=True
# 对每个属性解释如下:
# (1)_parameters:字典,保存用户直接设置的parameters,
# self.param1=nn.Parameter(t.randn(3,3))会被检测到,在字典中
# 加入一个key为‘param',value对应parameter的item,
# 而self.submodule=nn.Linear(3,4)中的parameter则不会存于此
# (2)_modules:子modules:子module,
# 通过slef.submodel=nn.Linear(3,4)指定的子module会保存于此
# 下面举例说明
class Net(nn.Module): # 继承基类(Module)def __init__(self): # 初始化构造函数super(Net, self).__init__()# 等价与self.register_parameter('param1',nn.Parameter(t.randn(3,3)))self.param1 = nn.Parameter(t.rand(3, 3))self.submodule1 = nn.Linear(3, 4)def forward(self, input):x = self.param1.mm(input)x = self.submodule1(x)return xnet = Net()
print(net)
# Net(
# (submodule1): Linear(in_features=3, out_features=4, bias=True)
# )
print(net._modules)
# OrderedDict([('submodule1', Linear(in_features=3, out_features=4, bias=True))])
print(net._parameters)
# tensor([[0.3398, 0.5239, 0.7981],
# [0.7718, 0.0112, 0.8100],
# [0.6397, 0.9743, 0.8300]], requires_grad=True))])
print(net.param1) # 等价于net._patameters['param1']
# Parameter containing:
# tensor([[0.3398, 0.5239, 0.7981],
# [0.7718, 0.0112, 0.8100],
# [0.6397, 0.9743, 0.8300]], requires_grad=True)
for name, param in net.named_parameters():print(name, param.size())
# param1 torch.Size([3, 3])
# submodule1.weight torch.Size([4, 3])
# submodule1.bias torch.Size([4])
for name, submodel in net.named_modules():print(name, submodel)
# Net(
# (submodule1): Linear(in_features=3, out_features=4, bias=True)
# )
# submodule1 Linear(in_features=3, out_features=4, bias=True)# (3)_buffers:缓存,------------------
# 如batchnorm使用momentum机制,每次前向传播需用到上一次前向传播的结果
# batchNorm1d是对小批量进行批标准化操作,在每个小批量数据中
# 计算输入各个维度的均值和标准差
# --在训练时--,该层计算每次输入的均值和方差,并进行移动平均,移动平均默认的动量值为0.1
# --在验证时--,训练求得的均值/方差用于标准化验证数据
# num_features:来自期望输入的特征数
# eps,为保证数值稳定性(分母不能趋近或取0),给分母加上值,默认为1e-5
# momentum:动态均值和动态方差所使用的动量,默认为0.1
# affine,一个布尔值,当设为true,该给层添加可学习的仿射变换参数
bn = nn.BatchNorm1d(2) # 批量规范化,2是输入的特征维度
input = t.rand(3, 2)
print(input)
# tensor([[0.4452, 0.0193],
# [0.2616, 0.7713],
# [0.3785, 0.9980]])
output = bn(input)
print(bn._buffers) # 缓存作用
# OrderedDict([('running_mean', tensor([0.0362, 0.0596])),# 输入平均值
# ('running_var', tensor([0.9009, 0.9262])),#输入方差
# ('num_batches_tracked', tensor(1))])# nn.Module在实际使用中可能层层嵌套,一个Module包含若干个子module,
# 每个子module又包含了更多的子module,
# 为方便用户访问各个子module,nn.Module实现了很多方法,如函数children可以查看直接子module
# 函数module可以查看所有的子module(包括当前module),
# 与之相对应的还有函数named_children和named_modules,
# 其能够在返回module列表的同时返回它们的名字# ---(4)training:BatchNorm和Dropout层在训练阶段和测试阶段中采取的策略不同,-----------
# 通过判断training值来决定前向传播策略
input = t.arange(0, 12).view(3, 4)
print(input)
model = nn.Dropout()
# 在训练阶段,会有一半左右的数被随机置为0
# model(input)
model.training = False
# 在测试阶段,dropout什么都不做
model(input)
# tensor([[ 0, 1, 2, 3],
# [ 4, 5, 6, 7],
# [ 8, 9, 10, 11]])
# 对于batchnorm,dropout,instancenorm等在训练阶段和测试阶段行为差异巨大的层
# 如果测试中不将其training值设为True,可能会有很大影响。
print(net.training, net.submodule1.training) # True True
net.eval()
print(net.training, net.submodule1.training) # False False
print(list(net.named_modules()))
# [('', Net(
# (submodule1): Linear(in_features=3, out_features=4, bias=True)
# )), ('submodule1', Linear(in_features=3, out_features=4, bias=True))]# (4——----(5)backward_hooks与__forward_hooks:钩子技术,用来提取中间变量,类似于variable的hook
# register_forward_hook与register_backward_hook,这两个函数功能类似于variable函数
# 的register_hook,可在module前向传播或反向传播时注册钩子,每次前向传播执行结束后悔执行钩子函数(hook)
# 前向传播的钩子函数具有如下形式,hook(module,input,output)->None,
# 反向传播如下形式:hook(module,grad_input,grad_output)->Tensor or None
# 钩子函数不应该修改输入和输出,并且在使用后应及时删除,以避免每次都运行钩子增加运行负载
# 钩子函数主要用在获取某些中间结果的情景,如中间某一层的输出或某一层的梯度,
# 这些结果本应写在forward函数中,但如果forward函数中专门加上这些处理,可能会使处理逻辑比较复杂
# 假设,有一个预训练好的模型,需要提取模型的某一层(不是最后一层)的输出作为特征进行分类
# 但有不希望修改其原有的模型定义文件,这时就可以利用钩子函数,下面为实现的伪代码
# mode# l=VGG()
# feat# ures=t.Tensor()
# def # hook(module,input,output):
# """把这层的输出拷贝到features"""
# features.copy_(output.data)
# handle=model.layer8.register+forward_hook(hook)
# _=model(input)
# 用完hook删除
# handle.remove()# --------setatter和getattr函数------------------
# nn.Module实现了自定义的__setattr__函数,当执行module.name=value时,
# 会在__setattr__中判断value是否为Parameter或者nn.Module对象,
# 如果是则将这些对象加到_parameters和modules两个字典中,而如果是其他类型的对象
# 如variable\list\dict等,则调用默认的操作,将这个值保存在__dict__中
module = nn.Module()
module.param = nn.Parameter(t.ones(2, 2))
print(module._parameters)
# OrderedDict([('param', Parameter containing:
# tensor([[1., 1.],
# [1., 1.]], requires_grad=True))])
submodule1 = nn.Linear(2, 2)
submodule2 = nn.Linear(2, 2)
module_list = [submodule1, submodule2]
# 对于List对象,调用building函数,保存在__dict__中
module.submodules = module_list
print('_modules:', module._modules)
print("__dict__['submodules']:", module.__dict__.get('submodules'))
# _modules: OrderedDict()
# __dict__['submodules']:
# [Linear(in_features=2, out_features=2, bias=True),
# Linear(in_features=2, out_features=2, bias=True)]module_list = nn.ModuleList(module_list) # 开始嵌套
module.submodules = module_list
# isinstance()函数,Python内置函数,用来判断一个函数是否为一个已知的类型
print('ModuleList is instance of nn.Module:', isinstance(module_list, nn.Module))
print('_modules:', module._modules)
print("__dict__['submodules']:", module.__dict__.get('submodules'))
# ModuleList is instance of nn.Module: True
# _modules: OrderedDict([('submodules', ModuleList(
# (0): Linear(in_features=2, out_features=2, bias=True)
# (1): Linear(in_features=2, out_features=2, bias=True)
# ))])
# __dict__['submodules']: None# 因为_modules和_parameters中的item为保存在__dict__中,所以默认的getatter方法无法获取它,
# 因为nn.Module实现了自定义__getatter__方法,如果磨人的getatter无法处理
# 就调用自定义的__getatter__方法,尝试从_modules\_parameters\_buffers这三个字典中获取
print(getattr(module, 'training')) # 等价于module.training
# True# ---------------4.6 nn和auotgrad的关系----------
# nn.Module利用的autograd技术,其主要工作实现前向传播,
# 在forward函数中,nn.Module对输入的tensor进行的各种操作,本质是用autograd技术
# autograd.Function和nn.Module之间的区别:
# (1)autograd.Function利用了Tensor对autograd技术的拓展,为autogtad实现了新的运算op,
# 不仅要实现前向传播还要手动实现反向传播
# (2)nn.Module利用了autograd技术,对nn的功能进行拓展,实现了深度学习中的更多层,
# 只需实现前向传播功能,autograd会自动实现反向传播
# (3)nn.functional是一些autograd操作的集合,是经过封装的函数
# 作为两大类的选取,如果某一个操作在autograd尚未支持,那么只能实现Function接口对应的前向传播和反向传播
# 如果某些利用autograd接口比较复杂,可以利用Funtion将多个操作聚合,实现优化
# 如果只是想在深度学习中增加某一层,使用nn.Module进行封装则更为简单高效# ---------------------------4.7 搭建ReSet-深度残差网络-------------------------------------------------------
# 考虑到Residual block和layer出现了多次,将其实现为一个子Module或函数,这里将Residual block
# 实现为一个子Module,而将Layer实现为一个函数,
# 跨层直连的shortcut
# ResNet中将一个跨层直连的单元称为Residual block
# 拥有多个Residual block单元的结构称之为layer,这里的layer是几个层的集合
# 下面为实现代码,规律总结如下:
# 1、对于模型中重复部分,实现为子Module或用函数生成相应的module make_layer
# 2、nn.Functianal和nn.Module结合使用
# 3、尽量使用nn.Sequrntial
from torch import nn
import torch as t
from torch.nn import functional as Fclass ResidualBlock(nn.Module): # 继承基类nn.Module"""实现子module:Residual Block"""# 输入通道,输出通道,步长def __init__(self, inchannel, outchannel, stride=1, shortcut=None):super(ResidualBlock, self).__init__() # 构造函数初始化self.left = nn.Sequential(# 输入通道,输出通道,卷积核大小,步长,不学习参数偏差biasnn.Conv2d(inchannel, outchannel, 3, stride, 1, bias=None),nn.BatchNorm2d(outchannel), # 批量规范化,计算平均值及标准差# 参数inplace=True,将会改变输入的数据,否则不会改变原输入,只会产生新的输出nn.ReLU(inplace=True),# 卷积层参数,padding图像四周填0的层数;dilation控制卷积核元素点之间的空间距离# groups分组卷积,默认输出输入的所有通道各为一组# 如果group=2,输入通道=32,输出通道=48,那么对应要将输入的32个通道分为2个16通道,# 将输出的通道分为2个24通道,对输出的2个24通道,第一个24通道与输入的第一个16通道进行全卷积# 第二个24通道与输入的第二个通道进行全卷积# 卷积核的深度=输入通道数# 卷积核的数量=输出通道数# 输出大小计算:输出大小=(输入大小-卷积核大小+2*填充值大小)/步长大小+1nn.Conv2d(outchannel, outchannel, 3, 1, 1, bias=False),nn.BatchNorm2d(outchannel) # 批量规范化)self.right = shortcutdef forward(self, x):out = self.left(x)residual = x if self.right is None else self.right(x)out += residualreturn F.relu(out)class ResNet(nn.Module): # 继承基类nn.Module"""实现主Module:ResNet34ResNet34包含多个layer,每个layer又包含多个residual block用子moudel实现residual block,用_make_layer函数来实现layer(多个Residual block单元的结构)"""def __init__(self, num_classes=1000):super(ResNet, self).__init__()# 前几层图像转换self.pre = nn.Sequential(nn.Conv2d(3, 64, 7, 2, 3, bias=False),nn.BatchNorm2d(64),nn.ReLU(inplace=True), # 替换输入值nn.MaxPool2d(3, 2, 1) # 窗口大小(默认为窗口大小),移动步长,补充0的层数)# 重复的layer,分别有3,4,6,3个residual blockself.layer1 = self._make_layer(64, 64, 3)self.layer2 = self._make_layer(64, 128, 4, stride=2)self.layer3 = self._make_layer(128, 256, 6, stride=2)self.layer4 = self._make_layer(256, 512, 3, stride=2)# 分类用的全连接self.fc = nn.Linear(512, num_classes)def _make_layer(self, inchannel, outchannel, block_num, stride=1):"""构建layer,包含多个residual block"""shortcut = nn.Sequential(nn.Conv2d(inchannel, outchannel, 1, stride, bias=False), # 卷积核大小为1,相当于没有nn.BatchNorm2d(outchannel))layers = [] # append函数会在数组后加上相应的元素layers.append(ResidualBlock(inchannel, outchannel, stride, shortcut)) # 会修改原值for i in range(1, block_num):layers.append(ResidualBlock(outchannel, outchannel))return nn.Sequential(*layers)def forward(self, x):x = self.pre(x)x = self.layer1(x)x = self.layer2(x)x = self.layer3(x)x = self.layer4(x)x = F.avg_pool2d(x, 7)x = x.view(x.size(0), -1)return self.fc(x)model = ResNet()
input = t.randn(1, 3, 224, 224)
o = model(input)
print(o)


推荐阅读
  • 本文介绍了H5游戏性能优化和调试技巧,包括从问题表象出发进行优化、排除外部问题导致的卡顿、帧率设定、减少drawcall的方法、UI优化和图集渲染等八个理念。对于游戏程序员来说,解决游戏性能问题是一个关键的任务,本文提供了一些有用的参考价值。摘要长度为183字。 ... [详细]
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • Python正则表达式学习记录及常用方法
    本文记录了学习Python正则表达式的过程,介绍了re模块的常用方法re.search,并解释了rawstring的作用。正则表达式是一种方便检查字符串匹配模式的工具,通过本文的学习可以掌握Python中使用正则表达式的基本方法。 ... [详细]
  • 前景:当UI一个查询条件为多项选择,或录入多个条件的时候,比如查询所有名称里面包含以下动态条件,需要模糊查询里面每一项时比如是这样一个数组条件:newstring[]{兴业银行, ... [详细]
  • 本文介绍了Android中的assets目录和raw目录的共同点和区别,包括获取资源的方法、目录结构的限制以及列出资源的能力。同时,还解释了raw目录中资源文件生成的ID,并说明了这些目录的使用方法。 ... [详细]
  • 微软头条实习生分享深度学习自学指南
    本文介绍了一位微软头条实习生自学深度学习的经验分享,包括学习资源推荐、重要基础知识的学习要点等。作者强调了学好Python和数学基础的重要性,并提供了一些建议。 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 本文介绍了Perl的测试框架Test::Base,它是一个数据驱动的测试框架,可以自动进行单元测试,省去手工编写测试程序的麻烦。与Test::More完全兼容,使用方法简单。以plural函数为例,展示了Test::Base的使用方法。 ... [详细]
  • 个人学习使用:谨慎参考1Client类importcom.thoughtworks.gauge.Step;importcom.thoughtworks.gauge.T ... [详细]
  • 本文介绍了UVALive6575题目Odd and Even Zeroes的解法,使用了数位dp和找规律的方法。阶乘的定义和性质被介绍,并给出了一些例子。其中,部分阶乘的尾零个数为奇数,部分为偶数。 ... [详细]
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
  • 也就是|小窗_卷积的特征提取与参数计算
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了卷积的特征提取与参数计算相关的知识,希望对你有一定的参考价值。Dense和Conv2D根本区别在于,Den ... [详细]
  • 本文介绍了响应式页面的概念和实现方式,包括针对不同终端制作特定页面和制作一个页面适应不同终端的显示。分析了两种实现方式的优缺点,提出了选择方案的建议。同时,对于响应式页面的需求和背景进行了讨论,解释了为什么需要响应式页面。 ... [详细]
  • 本文介绍了一个React Native新手在尝试将数据发布到服务器时遇到的问题,以及他的React Native代码和服务器端代码。他使用fetch方法将数据发送到服务器,但无法在服务器端读取/获取发布的数据。 ... [详细]
author-avatar
小么么和
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有