pytorch(一)

Tensor

tensor 是Pytorch重要的数据结构 ,可以认为是一个高纬的数组,它是一个(标量),一维数组(向量),二维矩阵(矩阵)以及更高维度的数组, Tesnor 跟numpy 的 ndarrays类似。

from __feature__ import  print_function
import torch as t


#构建一个(5,3) 的矩阵,只是分配了空间,未初始化
x = Tensor(5,3)
print(x)
#使用0-1均匀分布随机初始化二维数组
x = t.rand(5,3)
print(x)

out:tensor([[8.4490e-39, 9.6429e-39, 9.2755e-39],
[1.0286e-38, 9.0919e-39, 8.9082e-39],
[9.2755e-39, 8.4490e-39, 1.0194e-38],
[9.0919e-39, 8.4490e-39, 9.6429e-39],
[1.0653e-38, 9.6429e-39, 1.0745e-38]])
tensor([[ 0.0292, 0.5167, 0.9883],
[ 0.2294, 0.1510, 0.5729],
[ 0.6362, 0.2311, 0.9526],
[ 0.6667, 0.8822, 0.3827],
[ 0.3663, 0.3416, 0.4670]])

print(x.size())  #查看x 的形状
x.size()[1]  #查看列数 与下面的方法等价
x.size(1) #两者是相等的 torch.Size 是tuple对象的子类,它支持x.size()[1]

Tensor 的加法

y = t.rand(5,3)
#加法的一种写法
x+y
#第二种加法的写法
t.add(x,y)
#第三种加法的写法
result = t.Tensor(5,3) 先定义
t.add(x,y,out = result)
result

普通的加法并不会改变y的值

print("开始的y")
print(y)
y.add(x)
print("相加后的y")
print(y)

#第二种加法y的结果
y.add_(x)
print(yu)
y的结果变了

** 注意,函数名后面带下划线_ 的函数会修改Tensor本身。例如,x.add_(y)和x.t_()会改变 x,但x.add(y)和x.t()返回一个新的Tensor, 而x不变。

Tensor的选取过程跟numpy 类似

x[:,1] #所有行的1列
Tensor和Numpy的数组之间的互操作非常容易且快速。对于Tensor不支持的操作,可以先转为Numpy数组处理,之后再转回Tensor。
import numpy
import tensor as t
a = t.one(5) #创建一个全系的tensor
print(a)
b = a.numpy()  #Tensor --> Numpy
print(b)

c = t.from_numpy(a)  # Numpy -- > Tensor
Numpy与Tensor 共享内存,意味着一个发生改变另一个也会发生改变
b.add_(1)
print(a)  #out:[2. 2. 2. 2. 2.]
print(b) #out:tensor([2., 2., 2., 2., 2.], dtype=torch.float64)

Tensor 可以通过.cuda方法转化成GPU的Tensor 从而享受Tensor的加速运算

if t.cuda.is_available():
	x = x.cuda()
	y = y.,cuda()
	x  + y

CUDA 带来的速度提升 需要有大量的数据才能体现出来

Autograd 自动微分

深度学习的算法本质上是通过反向传播求导数,而PyTorch的Autograd模块则实现了此功能。在Tensor上的所有操作,Autograd都能为它们自动提供微分,避免了手动计算导数的复杂过程。

autograd.Variable是Autograd中的核心类,它简单封装了Tensor,并支持几乎所有Tensor有的操作。Tensor在被封装为Variable之后,可以调用它的.backward实现反向传播,自动计算所有梯度

Variable主要包含三个属性。

data:保存Variable所包含的Tensor
grad:保存data对应的梯度,grad也是个Variable,而不是Tensor,它和data的形状一样。
grad_fn:指向一个Function对象,这个Function用来反向传播计算输入的梯度

使用Tensor 创建一个Variable

from torch.autograd import Variable
x = Variable(t.ones(2,2),requires_grad = True

out:tensor([[1., 1.],
[1., 1.]], requires_grad=True)

y = x.sum()
print(y)

out:tensor(4., grad_fn=)

y.grad_fn

<SumBackward0 at 0x7fc14824b860>

y.backward() 反向传播,计算梯度
# y = sum(x) = x[0][0] + x[0][1] + x[1][0] + x[1][0]+x[1][1]
x.grad
#tensor([[1., 1.],
        #[1., 1.]])
注意:grad在反向传播过程中是累加的(accumulated),这意味着每一次运行反向传播,梯度都会累加之前的梯度,所以反向传播之前需把梯度清零。
y.backgrad()
x.grad()

y.backward()
x.grad

out: tensor([[2., 2.],
[2., 2.]])

Variable containing:
3 3
3 3
[torch.FloatTensor of size 2x2]

#以下划线结束的就是 inplace操作
x.grad.data.zero_()
y.backward()
x.grad

out :Variable containing:
1 1
1 1
[torch.FloatTensor of size 2x2]

定义一个网络

import torch.nn as nn
import torch.nn.functional as F

class Net(nn.Module):
    def __init__ (self):
        super(Net,self).__init__()
        #nn.Module子类 必须在构造函数中执行父类函数
        
        #等价于 nn.Module.__init__()
        # 卷积层'1'表示通道数量,6表示输出的通道数,5表示5个卷积核
        self.conv1 = nn.Conv2d(1,6,5)
        self.conv2 = nn.Conv2d(6,16,5)
        #全连接层 y = wx + b
        self.fc1 =  nn.Linear(15*5*5, 120)
        self.fc2 = nn.Linear(120,84)
        self.fc3 =  nn.Linear(84,10)
    def forward(self,x):
        #卷积 -- > 激活 -->池化
        x = F.max_pool2d(F.relu(self.conv1(x)),(2,2))
        x = F.max_pool2d(F.relu(self.conv2(x)),2)
		#reshape  “-1”表示 自适应
        x = x.view(x.size()[0],-1)
        x = F.relu(self.fc1(x))
        x = F.relu(slef.fc2(x))
        x = self.fc3(x)
        return  x
net = Net()
print(net)
		

out: 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=375, 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)
)

只要在nn.Module的子类中定义了forward函数,backward函数就会自动被实现(利用Autograd)。在forward 函数中可使用任何Variable支持的函数,还可以使用if、for循环、print、log等Python语法,写法和标准的Python写法一致。网络的可学习参数通过net.parameters()返回,net.named_parameters可同时返回可学习的参数及名称。

params = list(net.parameters())
print(len(params))

out:10

for name,parameters in net.named_parameters():
    print(name,':',parameters.size())

out:
conv1.weight : torch.Size([6, 1, 5, 5])
conv1.bias : torch.Size([6])
conv2.weight : torch.Size([16, 6, 5, 5])
conv2.bias : torch.Size([16])
fc1.weight : torch.Size([120, 400])
fc1.bias : torch.Size([120])
fc2.weight : torch.Size([84, 120])
fc2.bias : torch.Size([84])
fc3.weight : torch.Size([10, 84])
fc3.bias : torch.Size([10])

input = Variable(t.randn(1, 1, 32, 32))
out = net(input)
out.size()
net.zero_grad() # 所有参数的梯度清零
out.backward(Variable(t.ones(1,10))) # 反向传播  需要放入一个维度相同的
torch.Size([1, 10])

损失函数

nn实现了神经网络中大多数的损失函数,例如nn.MSELoss用来计算均方误差,nn.CrossEntropyLoss用来计算交叉熵损失。

output = net(input)
target = Variable(t.arange(0,10))  
criterion = nn.MSELoss()
loss = criterion(output, target)
loss

Variable containing:
28.5536
[torch.FloatTensor of size 1]

loss的计算图:

input -> conv2d -> relu -> maxpool2d -> conv2d -> relu -> maxpool2d  
      -> view -> linear -> relu -> linear -> relu -> linear 
      -> MSELoss
      -> loss
 当调用loss.backward()时,该图会动态生成并自动微分,也即会自动计算图中参数(Parameter)的导数。
# 运行.backward,观察调用之前和调用之后的grad
net.zero_grad() # 把net中所有可学习参数的梯度清零
print('反向传播之前 conv1.bias的梯度')
print(net.conv1.bias.grad)
loss.backward()
print('反向传播之后 conv1.bias的梯度')
print(net.conv1.bias.grad)

反向传播之前 conv1.bias的梯度
Variable containing:
0
0
0
0
0
0
[torch.FloatTensor of size 6]

反向传播之后 conv1.bias的梯度
Variable containing:
1.00000e-02 *
-4.2109
-2.7638
-5.8431
1.3761
-2.4141
-1.2015
[torch.FloatTensor of size 6]

在反向传播计算完所有参数的梯度后,还需要使用优化方法来更新网络的权重和参数,例如随机梯度下降法(SGD)的更新策略如下:

weight = weight - learning_rate * gradient

learning_rate = 0.01
for f in net.parameters():
    f.data.sub_(f.grad.data * learning_rate)# inplace 减法
import torch.optim as optim
#新建一个优化器,指定要调整的参数和学习率
optimizer = optim.SGD(net.parameters(), lr = 0.01)

# 在训练过程中
# 先梯度清零(与net.zero_grad()效果一样)
optimizer.zero_grad() 

# 计算损失
output = net(input)
loss = criterion(output, target)

#反向传播
loss.backward()

#更新参数
optimizer.step()
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章