测试环境版本:
torch1.7.1 + CPU

python 3.6

1、Tensor

实际上就是一个高维数组。类似与numpy
Tensor

加法的几种写法:

import torch as t
x = t.Tensor(5,3)
y = t.Tensor(5,3)
z = t.Tensor(5,3) # z必须事先分配内存

1)

z = x + y

2)

z = t.add(x,y)

3)

t.add(x,y,out = z)

4)add() 与 add_()的区别:

x.add(y),等同于 x+y。 x的值不会改变
x.add_(y)等同于 x += y(且返回值是x的值)

Tensor与numpy的相互转化

与numpy相比,Tensor支持gpu运算。
numpy()方法可以将Tensor转化为numpy

In [23]: x = t.Tensor(3,4)

In [24]: y = x.numpy()
In [26]: x[1][1] = 1

In [27]: y
Out[27]:
array([[1.0102060e-38, 9.0000075e-39, 9.6428910e-39, 1.0653069e-38],
       [1.0102052e-38, 1.0000000e+00, 1.0653079e-38, 9.0000426e-39],
       [8.4490268e-39, 8.7245053e-39, 1.0102052e-38, 1.0653065e-38]],
      dtype=float32)

注意:Tensor与numpy共用内存,所以两者的转化非常快。但相对的,对其中一个的操作也会影响另一个

GPU加速运算

cuda方法可以将Tensor转化为支持cuda的Tensor

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

2、Autograd 自动微分

pytorch的Autograd模块实现了自动求微分的功能。

Autograd模块的核心类是autograd.Variable,他简单封装了Tensor,并支持几乎所有的tensor操作。

autograd.Variable的数据结构包含三部分:
{
data: 存储着Variable所包含的Tensor。
grad:保存data对应的梯度,它也是一个Variable,而不是Tensor。
grad_fn:指向一个function对象,这个function用来反向传播求导数。
}

在我的测试中,tensor和Variable是等同的。和书中所说不一样。
可能原因有:

  • 1、我安装的pytoch并不是cuda版本()不可以进行gpu运算
  • 2、仅仅是显示问题,实际上tensor和variable不同,只是我没有发现
  • 3、pytorch进行了更新,variable和tensor被改的一样了

(补充:后来我发现实际情况是3)

from torch.autograd import Variable

import torch as t

x = Variable(t.ones(2,2),requires_grad = True)

x

# tensor([[1., 1.],
#         [1., 1.]], requires_grad=True)

y = x.sum()

y

# tensor(4., grad_fn=<SumBackward0>)

# x.grad

# tensor([[1., 1.],
#         [1., 1.]])

y.backward()

x.grad

# tensor([[2., 2.],
#         [2., 2.]])

注意,grad在反向传播的过程中是累加的,反向传播之前需要把梯度清零。

x.grad.data.zero_()

x

# tensor([[1., 1.],
#         [1., 1.]], requires_grad=True)

x.grad

# tensor([[0., 0.],
#         [0., 0.]])

3、nn

nn(神经网络)是神经网络的一个模块化接口。

利用它可以便捷地搭建神经网络。

nn.Module是nn中最重要的类,可以看作一个神经网络的封装,包含着各层定义和forward方法

建立一个基础的前向传播网络:

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

class Net(nn.Module):
    def __init__(self):
        #下式等同于nn.Mudule.__init__(self)
        super(Net,self).__init__() # net继承了nn.Mudule以及其构造函数
        #两层卷积层:
        self.conv1 = nn.Conv2d(1,6,5)
        #参数解释:1:输入图片为单通道,RGB图像的输入通道为3
        #6:输出通道数
        #5:卷积核大小为5×5,  输入5与(5,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.max_pool2d(F.relu(self.conv1(x)),(2,2))
        x = F.max_pool2d(F.relu(self.conv2(x)),2)
        # reshape 自适应
        x = x.view(x.size()[0],-1)
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x
net = Net()
print(net)

以上构建的是一个这样的网络:
卷积-激活-池化-卷积-激活-池化-激活-全连接-激活-全连接-激活
其中激活函数使用ReLU

不需要学习参数的层可以不在init()中定义。 比如ReLU

只要在nn.Module的子类中定义了forward函数,backward函数就自动被实现。

可以用parameters方法查看各层的权重参数

params = net.parameters
print(params)

输出:
(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))

input_data = t.randn(1,1,32,32)
out = net(input_data)
print(out.size())

损失函数:
pytorch中封装了常用的损失函数

  • nn.MSEloss 均方误差
  • nn.CrossEntropyLoss 交叉熵误差
input_data = t.randn(1,1,32,32)
out = net(input_data)
# print(out.size())
arrtmp = np.array(list(range(10)),ndmin=2)
target = t.from_numpy(arrtmp).float()
criterion = nn.MSELoss()
loss_func = criterion(out,target)

由损失函数反向传播,自动获得梯度

loss_func.backward()
net.conv1.bias.grad

结果:tensor([-0.0280, -0.0164, 0.0484, 0.0696, 0.0301, -0.1436])

优化器(更新参数的策略)

以随机梯度下降法(SGD)为例:
其实现为:
weight = weight - learning_rate * gradient

如果在刚才的网络中,应该写一个这样代码:

learning_rate = 0.01
for f in net.parameters():
    f.data.sub_(f.grad.data * learning_rate)

而pytorch已经内置了常用的优化器,比如adam、SGD

import torch.optim as optim
optimizer = optim.SGD(net.parameters(),lr=0.01)
#梯度清零
optimizer.zero_grad()# 等同于net.zero_grad
out = net(input_data)
#反向传播
loss_func.backward()
#更新参数
optimizer.step()