大部分来源 中文pytorch手册或英文pytorch手册及 PracticalAI 项目。
Pytorch是一个基于python的科学计算包,其目的是:
a、代替Numpy发挥GPU性能;
b、提供一个高度灵活和极具效率的深度学习研究平台;
1、基本概念
1.1、Tensor(张量)
pytorch的tensor本质上就是多维矩阵,pytorch的tensor可以和numpy的ndarray相互转换,唯一的不同是pytorch可以在GPU上运行。
Tensor的数据类型:
torch.FloatTensor #32位浮点型
torch.DoubleTensor #64位浮点型
torch.ShortTensor #16位整型
torch.IntTensor #32位整型
torch.LongTensor #64位整型
测试:
import torch
import numpy as np
x = torch.Tensor(5, 3)
print (x)
b = torch.LongTensor([[2,3], [4,8], [7,9]]) #64位整型tensor
print('b is : {}'.format(b))
c = torch.zeros((3,2)) #全0的3x2tensor
print('zero tensor : {}'.format(c))
d = torch.rand((3,2)) #随机的3x2 tensor,符合正态分布
print('normal random is:{}'.format(d))
numpy_b = b.numpy() #tensor转numpy
print('conver to numpy is \ {}'.format(numpy_b))
e = np.array([[2,3], [4,5]])
torch_e = torch.from_numpy(e) #numpy转tensor
print('from numpy to torch.Tensor is{}'.format(torch_e))
f_torch_e = torch_e.float() #数据类型转换,整型转float
print('change data type to float tensor:{}'.format(f_torch_e))
#使用GPU
if torch.cuda.is_available():
d_cuda = d.cuda()
print(d_cuda)
输出:
1.1.1、CUDA Tensor
支持GPU的torch版本才能使用,查询CUDA是否可用
# Is CUDA available?
print (torch.cuda.is_available())
创建CUDA Tensor
# Creating a zero tensor
x = torch.Tensor(3, 4).to("cuda")
print("Type: {}".format(x.type()))
输出:
1.2、变量(Variable)
Variable提供了自动求导的功能。Tensor和Variable本质上没有区别,不过Variable会被放入一个计算图中,然后进行前向传播,反向传播,自动求导。
Variable的属性如下图所示:
Variable的三个重要属性:
data: 通过data可以取出Variable的tensor的数值;
grad_fn: 表示得到这个Variable的操作,比如通过加减还是乘除来得到;
grad: 这个Variable的反向传播梯度;
标量求导例子:
import torch
from torch.autograd import Variable
#创建Variable
x = Variable(torch.Tensor([1]), requires_grad=True)
w = Variable(torch.Tensor([2]), requires_grad=True)
b = Variable(torch.Tensor([3]), requires_grad=True)
y = w * x + b
#计算梯度
y.backward()
print(x.grad)
print(w.grad)
print(b.grad)
输出:
构建Variable,传入参数“requires_grad=True”表示对这个变量求梯度,默认的False。
上面代码中: y.backward()
等价于y.backward(torch.FloatTensor([1])),只不过对标量求导里面的参数可以不写。
自动求导不需要我们明确的写明那个函数对哪个函数求导,直接通过这行代码就能对所有的需要梯度的变量进行求导,得到它们的梯度,然后再获取。
矩阵求导例子:
X = torch.randn(3)
X = Variable(X, requires_grad=True)
Y = X * 2
print(Y)
Y.backward(torch.FloatTensor([1, 0.1,0.01]))
print(X.grad)
输出:
对于向量的求导,不能直接写Y.backword(),否则报错。需要传入参数。
Y.backword(torch.FloatTensor([1,1,1])): 表示得到结果就是它们每个分量的梯度
Y.backward(torch.FloatTensor([1, 0.1,0.01])):表示得到的梯度是它们原本梯度分别乘上1,0.1,0.01
2、Tensor的索引、分割及拼接
2.1、索引
x = torch.randn(3, 4)
print("x: \n{}".format(x))
print ("x[:2]: \n{}".format(x[:2]))
print ("x[:1, 1:3]: \n{}".format(x[:1, 1:3]))
输出:
由例子明显知道:
X[:N]–取前N行,不包括第N行;
X[:1,1:3] – 取第0行的第1,2,列,不包括第3列
2.2、通过维度索引筛选元素
# Select with dimensional indicies
x = torch.randn(2, 3)
print("Values: \n{}".format(x))
col_indices = torch.LongTensor([0, 2])
chosen = torch.index_select(x, dim=1, index=col_indices) #dim=1 -- 按列取,取第0列及第2列
print("Values: \n{}".format(chosen))
row_indices = torch.LongTensor([0, 1])
chosen = x[row_indices, col_indices] # 按索引取。values from (0, 0) & (1, 2)
print("Values: \n{}".format(chosen))
输出:
3、pytorch搭建神经网络
pytorch使用torch.nn包来搭建各种神经网络。
nn.Model 包含神经网络层级结构及forward(input)方法,该方法返回output。(前向传播)
例:
import torch
import torch.nn as nn
import torch.nn.functional as F
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
# 1 input image channel, 6 output channels, 5x5 square convolution
# kernel
self.conv1 = nn.Conv2d(1, 6, 5)
self.conv2 = nn.Conv2d(6, 16, 5)
# an affine operation: y = Wx + b
self.fc1 = nn.Linear(16 * 5 * 5, 120) #输入样本[* x 400], 输出[* x 120] (*表示任意数字)
self.fc2 = nn.Linear(120, 84)
self.fc3 = nn.Linear(84, 10)
def forward(self, x):
# Max pooling over a (2, 2) window
x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))
# If the size is a square you can only specify a single number
x = F.max_pool2d(F.relu(self.conv2(x)), 2)
x = x.view(-1, self.num_flat_features(x)) #-1--表示自动推测其它维数
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
return x
def num_flat_features(self, x):
size = x.size()[1:] # all dimensions except the batch dimension
num_features = 1
for s in size:
num_features *= s
return num_features
net = Net()
print(net)
params = list(net.parameters()) #返回模型需要学习的参数
print(len(params))
#print(params[0].size()) #conv1的权重W ,size: [6,1,5,5] 6个kernel,每个一个channel,大小5x5
for param in params:
print(param.size())
输出:
《1》、nn.Conv2d函数原型:
torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True)
作用: 在一个由多个输入plane组成的信号上执行2D卷积运算
参数说明:
in_channels: 输入信号的channel数量。如RGB图像,3 channel。
out_channels: 输出信号的channel数量,既是卷积核的个数。32个卷积核时会有32 个 output(feature map)。
stride(整数或tuple类型):卷积的执行时的步长。可选参数,默认是1。
padding(整数或tuple类型):控制对输入的边界进行0填充的数量。可选参数,默认是0。
bias(bool类型):如果设置为true,则在计算输出时加入偏差。可选参数, 默认True。
《2》、nn.Linear函数原型:
torch.nn.Linear(in_features, out_features, bias=True)
作用:用于进行仿射变换(线性变换)
Parameters:
in_features – size of each input sample(输入样本尺寸的列数)
out_features – size of each output sample(输入样本尺寸的列数)
bias – If set to False, the layer will not learn an additive bias. Default: True
看内部实现才能很好的理解其参数的含义,其内部实现如下:
理解例子:
import torch
import torch.nn as nn
m = nn.Linear(20, 30)
input = torch.randn(128, 20)
print(input.size())
output = m(input)
print(output.size())
输出:
《3》、nn.functional.max_pool2d函数原型:
torch.nn.functional.max_pool2d(input, kernel_size, stride=None, padding=0, dilation=1, ceil_mode=False, return_indices=False)
《4》、view(*args)函数
返回一个有相同数据单大小不同的tensor。返回的tensor必须与输入tensor有相同的数据和相同的数目。
《5》、net.parameters()
返回模型所有需要学习的参数。
注意:
torch.nn只支持小批量输入。整个torch.nn包都只支持小批量样本而不支持单个样本。
如nn.Conv2d将接受一个4维张量,每一维分别是:samples * channel * height * width (样本数 * 通道数 * 高 * 宽)
《6》、计算损失函数
损失函数接受一对(net_output,target)作为输入,计算一个值来估计网络输出与目标值之间的差距(差距衡量有多种方法,如均方误差等)
input = torch.randn(1, 1, 32, 32)
output = net(input)
print(output)
target = torch.randn(10) # a dummy target, for example
target = target.view(1, -1) # make it the same shape as output
criterion = nn.MSELoss() #均方误差
loss = criterion(output, target)
print(loss)
输出:
《7》、反向传播
直接调用.backward()即可获取反向传播梯度。 需要注意的是,在获取梯度之前,必须清除已经存在的梯度,否则梯度将被累加到已存在的梯度。
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)
输出:
《8》、权重更新方式
a、自己写代码实现;
b、import torch.optim as 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()
print('conv1.bias befor update: ')
print(net.conv1.bias)
print('conv1.bias updated: ')
optimizer.step() # 更新 update
print(net.conv1.bias)
输出: