赞
踩
注:可以不用Variable的表示方法了?不需要以下写法?
from torch.autograd import Variable
x=Variable(torch.ones(1,3),requires_grad=True)
y=x+2
y.creator # 该属性名已改成grad_fn
x.requires_grad=True
: 会开始跟踪针对 tensor x
的所有操作, 完成所有计算后( x->y
), 可以调用 y.backward()
来自动计算所有梯度, tensor x
的梯度将累积到 x.grad
属性中。
'''
.requires_grad_( ... ) 会改变张量的 requires_grad 标记,输入的标记默认为 False
'''
a=torch.randn(2,2)
a=((a*3)/(a-1))
print(a.requires_grad)
a.requires_grad_(True)
print(a.requires_grad)
b=(a*a).sum() # 注意 这里的张量乘法是哈达马积m*n和m*n(不是矩阵乘法m*p和p*n),是每个位置上对应数值的乘积。
print(b.grad_fn)
'''
False
True
<SumBackward0 object at 0x7fc6d00db4a8>
'''
注意:在 y.backward()
时,如果y是标量,则不需要为backward()传入任何参数,否则,需要传入一个与y同形的 tensor 。[原因]
# 雅可比向量举例
x=torch.randn(3,requires_grad=True)
y=x*2
while y.data.norm()<1000:
y=y*2
print(y)
'''
tensor([ 87.0814, 1119.1162, 55.7230], grad_fn=<MulBackward0>)
'''
v=torch.tensor([0.1,1.0,0.0001],dtype=torch.float)
y.backward(v)
print(x.grad)
'''
tensor([5.1200e+01, 5.1200e+02, 5.1200e-02])
'''
关于y.data.norm()<1000: L2范数 (欧几里得范数)
等价于 torch.sqrt(torch.sum(torch.pow(y, 2)))
.detach()
:取消对 tensor 的梯度追踪,将其与计算历史记录分离,并防止将来的计算被跟踪。
print(x.requires_grad)
# y和x数据相同,不需要求导
# y不是x的拷贝,对y的修改也会影响x,如果直接令y=x,那么是不会取消追踪的
y=x.detach()
print(y.requires_grad)
print(x.eq(y).all()) # 对比全部数据
'''
True
False
tensor(True)
'''
还可以用 withtorch.no_grad()
将不想被追踪的操作代码包裹起来,适用于模型评估。
x=torch.randn(3,requires_grad=True)
print(x.requires_grad)
print((x**2).requires_grad)
with torch.no_grad():
print((x**2).requires_grad)
print((x**2).requires_grad)
'''
True
True
False
True
'''
Tensor 和 Function 互相连接并构建一个非循环图,它保存整个完整的计算过程的历史信息。
除了用户基于数据直接创建的 tensor ( eg. a=torch.Tensor([1, 2, 3,])
) ,其他的Tensor必然是根据某些Tensor通过运算得到的,Function类就记录了这一运算过程,并存储在 .grad_fn
属性中。
import torch x=torch.ones(2,2,requires_grad=True) print(x) ''' tensor([[1., 1.], [1., 1.]], requires_grad=True) ''' print(x.grad_fn) ''' None ''' y=x+2 print(y) print(y.grad_fn) ''' tensor([[3., 3.], [3., 3.]], grad_fn=<AddBackward0>) <AddBackward0 object at 0x7fbed1b13f28> ''' z=y*y*3 out=z.mean() print(z,out) ''' tensor([[27., 27.], [27., 27.]], grad_fn=<MulBackward0>) tensor(27., grad_fn=<MeanBackward0>) ''' out.backward() print(x.grad) ''' tensor([[4.5000, 4.5000], [4.5000, 4.5000]]) '''
More about autograd 和 Function
注意:torch.nn 只接受小批量的数据
整个torch.nn包只接受那种小批量样本的数据,而非单个样本。 例如,nn.Conv2d
能够结构一个四维的 TensornSamples x nChannels x Height x Width
。
如果你拿的是单个样本,使用 input.unsqueeze(0)
来加一个假维度就可以了。
import torch import torch.nn as nn import torch.nn.functional as tf class Net(nn.Module): def __init__(self): super(Net,self).__init__() # 复制并使用Net的父类nn.Module的初始化方法 self.conv1=nn.Conv2d(1,6,5) # 定义conv1函数的是图像(2d)卷积函数:输入为图像(1 channel,即灰度图),输出为6 channels, 卷积核为5x5的正方形 self.conv2=nn.Conv2d(6,16,5) # an affine operation: y=Wx+b self.fc1=nn.Linear(16*5*5,120) # 定义fc1为线性函数:y=Wx+b,并将16*5*5个节点连接到120个节点上。 self.fc2=nn.Linear(120,84) self.fc3=nn.Linear(84,10) # 定义该神经网络的向前传播函数,该函数必须定义,一旦定义成功,向后传播函数也会自动生成(autograd) def forward(self,x): x=tf.max_pool2d(tf.relu(self.conv1(x)),(2,2)) x=tf.max_pool2d(tf.relu(self.conv2(x)),2) x=x.view(-1,self.num_flat_features(x)) # 将张量x变形成一维的向量形式,总特征数并不改变,为接下来的全连接作准备。 x=tf.relu(self.fc1(x)) # 注意要把x传到fc1中 x=tf.relu(self.fc2(x)) x=self.fc3(x) return x # 计算张量x的总特征量(把每个数字都看出是一个特征,即特征总量) def num_flat_features(self,x): size=x.size()[1:] # 这里为什么要使用[1:],是因为pytorch只接受批输入,也就是说一次性输入好几张图片,那么输入数据张量的维度自然上升到了4维,[1:]让我们把注意力放在后3维(实际每张图像的三个维度) num_features=1 for s in size: num_features*=s return num_features net=Net() print(net) ''' Net( (conv1): Conv2d(1, 6, kernel_size=(5, 5), stride=(1, 1)) (conv2): Conv2d(6, 16, kernel_size=(5, 5), stride=(1, 1)) (fc1): Linear(in_features=400, out_features=120, bias=True) (fc2): Linear(in_features=120, out_features=84, bias=True) (fc3): Linear(in_features=84, out_features=10, bias=True) ) '''
net.parameters()
返回:params=list(net.parameters()) print(len(params)) print(params[0].size()) # conv1's .weight ''' 10 torch.Size([6, 1, 5, 5]) ''' # 输出每一层的参数数 sum=0 for i in params: num=1 print("该层的结构:"+str(list(i.size()))) for j in i.size(): num*=j print("参数和:"+str(num)) sum+=num print("总参数和:"+str(sum)) ''' 该层的结构:[6, 1, 5, 5] 参数和:150 该层的结构:[6] 参数和:6 该层的结构:[16, 6, 5, 5] 参数和:2400 该层的结构:[16] 参数和:16 该层的结构:[120, 400] 参数和:48000 该层的结构:[120] 参数和:120 该层的结构:[84, 120] 参数和:10080 该层的结构:[84] 参数和:84 该层的结构:[10, 84] 参数和:840 该层的结构:[10] 参数和:10 总参数和:61706 '''
net=Net()
#print(net)
input=torch.randn(1,1,32,32)
out=net(input)
print(out)
'''
十个数字的概率张量:
tensor([[ 0.0587, -0.0765, 0.0846, 0.1052, -0.0052, 0.0298, 0.0342, 0.0129,
-0.0719, 0.0846]], grad_fn=<AddmmBackward>)
'''
net.zero_grad() # 对所有的参数的梯度缓冲区进行归零
out.backward(torch.randn(1,10)) # 使用随机的梯度进行反向传播
损失函数的输入:模型输出值和目标真实值,并评估两者之间的差值。
input = torch.randn(1,1,32,32)
output=net(input)
target=torch.randn(10) # 假设的目标真实值
target=target.view(1,-1) # 使其形状与输出相同 10x1
print(target)
criterion=nn.MSELoss()
loss=criterion(output,target)
print(loss)
'''
tensor(1.6512, grad_fn=<MseLossBackward>)
'''
跟随loss
从后往前看,调用 .creator
属性你可以看到这样的一个计算流程图:
'''
input -> conv2d -> relu -> maxpool2d -> conv2d -> relu -> maxpool2d
-> view -> linear -> relu -> linear -> relu -> linear
-> MSELoss
-> loss
'''
print(loss.creator)
'''
报错:AttributeError: 'Tensor' object has no attribute 'creator'
原因:creator属性名称已经改为grad_fn
grad_fn的值可以得知该变量是否是一个计算结果,也就是说该变量是不是一个函数的输出值。若是,则grad_fn返回一个与该函数相关的对象,否则是None
'''
因此当我们调用 loss.backward()
时,整个图都会微分,且所有的在图中的requires_grad=True 的张量都会以 .grad
来累积梯度。
print(loss.grad_fn) # MSELoss print(loss.grad_fn.next_functions[0][0]) # Linear print(loss.grad_fn.next_functions[0][0].next_functions[0][0]) # ReLU ''' <MseLossBackward object at 0x7fb753c6d358> <AddmmBackward object at 0x7fb7543f65c0> <AccumulateGrad object at 0x7fb753c6d358> ''' net.zero_grad() # zeroes the gradient buffers of all parameters print('conv1.bias.grad before backward') print(net.conv1.bias.grad) loss.backward() # 进行反向传播 print('conv1.bias.grad after backward') print(net.conv1.bias.grad) ''' conv1.bias.grad before backward tensor([0., 0., 0., 0., 0., 0.]) conv1.bias.grad after backward tensor([ 0.0100, -0.0213, 0.0175, 0.0126, 0.0111, -0.0109]) '''
最简单的更新规则就是随机梯度下降(SGD):
# weight=weight-learning_rate*gradient
learning_rate=0.01
for f in net.parameters():
f.data.sub_(f.grad.data*learning_rate)
还有其他的更新方式,例如 SGD, Nesterov-SGD, Adam, RMSProp等,可以通过 torch.optim
来直接调用相应方法。
import torch.optim as optim
# create your optimizer
optimizer=optim.SGD(net.parameters(),lr=0.01)
# in your training loop:
optimizer.zero_grad() # zero the gradient buffers
output=net(input)
loss=criterion(output,target)
loss.backward()
optimizer.step() # Does the update
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。