Machine Learning —— pytorch入门
torch简单操作
1、张量生成
from __future__ import print_function
import torch
x1 = torch.empty(5, 3)
#生成一个未初始化的5*3的张量,数值随机
print(x1)
x2 = torch.rand(5, 3)
#生成一个均匀分布的张量,元素从0-1
print(x2)
#其他随机张量生成函数:torch.randn()标准正态分布、torch.normal()离散正太分布、torch.linespace()线性间距向量
x3 = torch.zeros(5, 3, dtype = torch.long)
#初始化一个全零张量,可以指定元素的类型
print(x3)
x4 = torch.tensor([5.5, 3])
#将矩阵转化为张量
print(x4)
x5 = x.new_ones(5, 3, dtype = torch.double)
#利用已有张量创建新张量,新创建张量属性与原张量一致,若不设定值属性,则类型与原张量也一致
print(x5)
x6 = torch.randn_like(x, dtype = torch.float)
print(x6)
print(x.size())
#获取张量的形状,输出的torch.Size()是一个元组
2、张量的操作
y = torch.rand(5, 3)
#加法
print(x + y)
print(torch.add(x, y))
result = torch.empty(5, 3)
torch.add(x, y, out = result)
print(result)
y.add_(x)
#‘_’符合在所有替换自身操作符的末尾都有
#调整张量形状
x = torch.randn(4, 4)
y = x.view(16)
z = x.view(-1, 8)
#参数中的-1就代表这个位置由其他位置的数字来推断
#如果是torch.view(-1),则原张量会变成一维的结构
#查看张量的大小
x = torch.randn(1)
print(x)
print(x.item())
3、张量和numpy互换
#在CPU情况下,张量和array共享物理地址
a = torch.ones(5)
print(a)
b = a.numpy()
#张量转numpy
print(b)
a.add_(1)
print(a)
print(b)
import numpy as np
a = np.ones(5)
b = torch.from_numpy(a)
np.add(a, 1, out = a)
print(a)
print(b)
- GPU下的转换
#使用“torch.device”使用和禁止GPU
if torch.cuda.is_available():
device = torch.device('cuda') #创建一个cuda device对象
y = torch.ones_like(x, device = device) #在GPU上创建一个张量
x = x.to(device) #.to(device)=.to('cuda')
z = x + y
print(z)
print(z.to('cpu', torch.double)) #.to也可以直接转换类型
out:
tensor([1.3832], device='cuda:0')
tensor([1.3832], dtype=torch.float64)
Autograd: 自动求导(automatic differentiation)
PyTorch 中所有神经网络的核心是autograd包
- autograd包为张量上的所有操作提供了自动求导.它是一个运行时定义的框架,这意味着反向传播是根据你的代码如何运行来定义,并且每次迭代可以不同
变量(Variable)
- autograd.Variable是autograd包的核心类.它包装了张量(Tensor),支持几乎所有的张量上的操作.一旦你完成你的前向计算,可以通过.backward()方法来自动计算所有的梯度
- 可以通过.data属性来访问变量中的原始张量,关于这个变量的梯度被计算放入.grad属性中
- 对自动求导的实现还有一个非常重要的类,即函数(Function)
- 变量(Variable)和函数(Function)是相互联系的,并形成一个非循环图来构建一个完整的计算过程.每个变量有一个.grad_fn属性,它指向创建该变量的一个Function,用户自己创建的变量除外,它的grad_fn属性为None
- 计算导数,可以在一个变量上调用.backward().如果一个Variable是一个标量(它只有一个元素值),你不必给该方法指定任何的参数,但是该Variable有多个值,你需要指定一个和该变量相同形状的的grad_output参数
import torch
from torch.autograd import Variable
#创建一个变量
x = Variable(torch.ones(2, 2), requires_grad = True)
print(x)
#对变量进行操作
y = x + 2
print(y)
#y是通过x操作创建的,y有grad_fn,而x是用户创建的,所以x的grad_fn为None
print(x.grad_fn)
print(y.grad_fn)
#在y上执行操作
z = y * y *3
out = z.mean()
print(z, out)
梯度(Gradient)
现在我们来执行反向传播
out.backward()
#out.backward()相当于执行out.backward(torch.Tensor([1.0]))
#输出out对x的梯度 d(out)/dx
print(x.grad)
此时得到一个值全为4.5的矩阵,该结果的推导过程:
o
u
t
=
1
4
∑
i
z
i
out=\frac{1}{4}\sum_iz_i
out=41∑izi
z i = 3 ( x i + 2 ) 2 z_i=3(x_i+2)^2 zi=3(xi+2)2
∂ o u t ∂ x i = 3 2 ( x i + 2 ) \frac{\partial out}{\partial x_i}=\frac{3}{2}(x_i+2) ∂xi∂out=23(xi+2)
其他例子
x = torch.randn(3)
print(x)
x = Variable(x, requires_grad = True)
y = x * 2
while y.data.norm() < 1000:
#data.norm():L2范数
y = y * 2
print(y)
gradients = torch.FloatTensor([0.1, 1.0, 0.0001])
y.backward(gradients)
print(x.grad)
Neural network
可以使用torch.nn包来构建神经网络,torch.nn包依赖autograd包定义模型并求导,一个nn.module包含各个层和一个**faward(input)**方法,该方法返回output
神经网络的典型训练过程:
- 定义神经网络模型(权重等)
- 在数据集上迭代
- 通过神经网络处理输入
- 计算损失
- 将梯度反向传播调整网络参数
- 更新网络参数
w i + 1 = w i − η ∗ g r a d i e n t w_{i+1}=w_i-\eta *gradient wi+1=wi−η∗gradient
定义网络
#定义网络
import torch
from torch.autograd import Variable
import torch.nn as nn
import torch.nn.functional as F
class Net(nn.Module):
#定义Net的初始化函数,这个函数定义了神经网络的基本结构
def __init__(self):
super(Net, self).__init__()
#对继承自父类的属性进行初始化
#复制并使用Net的父类的初始化方法,即先运行nn.Module的初始化函数
#super(Net, self).__init__():首先找到Net的父类(比如是类NNet),然后把类Net的对象self转换为类NNet的对象,然后“被转换”的类NNet对象调用自己的init函数
self.conv1 = nn.Conv2d(1, 6, 5) #定义conv1函数的图像卷积函数:输入为图像(1个频道,即灰度图),输出为6张特征图,卷积核为5*5正方形
self.conv2 = nn.Conv2d(6, 16, 5) #定义conv2函数的图像卷积函数:输入6张特征图,输出16张特征图,卷积核为5*5正方形
self.fc1 = nn.Linear(16 * 5 *5, 120) #定义fc1全连接函数为线性函数:y=wx+b,将16*5*5个节点连接到120个节点上
self.fc2 = nn.Linear(120, 84) #定义fc2全连接函数为线性函数:y=wx+b,将1210个节点连接到84个节点上
self.fc3 = nn.Linear(84, 10) #定义fc3全连接函数为线性函数:y=wx+b,将84个节点连接到10个节点上
#定义该神经网络的前向传播函数,后向传播函数会随前向传播函数自动生成
def forward(self, x):
x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))#x输入经过卷积conv1,经过激活函数ReLU,使用2*2窗口进行最大池化,然后更新到x
x = F.max_pool2d(F.relu(self.conv2(x)), 2)
x = x.view(-1, self.num_flat_features(x)) #view函数将张量x变形成一维的向量,总特征数不变,为接下来的全连接做准备
x = F.relu(self.fc1(x)) #x输入后经过全连接1,再经过ReLU激活函数,然后更新到x
x = F.relu(self.fc2(x))
x = self.fc3(x) #x输入经过全连接3,然后更新x
#使用num_flat_features函数计算张量x的总特征量(每个数字都是一个特征)
def num_flat_features(self, x):
size = x.size()[1:] #pytorch只接收批输入,[1:]把注意力放在后三维
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))
for param in params:
print(param.size())
10
torch.Size([6, 1, 5, 5])
torch.Size([6])
torch.Size([16, 6, 5, 5])
torch.Size([16])
torch.Size([120, 400])
torch.Size([120])
torch.Size([84, 120])
torch.Size([84])
torch.Size([10, 84])
torch.Size([10])
forward的输入和输出都是autograd.Variable,以上网络的输入大小是3232,如果使用MNIST数据集训练网络,需要把图片大小重新调整到3232
input = Variable(torch.randn(1, 1, 32, 32))
out = net(input)
print(out)
将所有参数的梯度缓存清零,然后进行随机梯度反向传播(未完成)
net.zero_grad()
out.backward(torch.randn(1, 10))
注意
- torch.nn只支持小批量输入
- nn.Conv2d接收一个4维的张量,分别是s(Samples)x n(Channels)x Height x Width(样本数x通道数x高x宽)
- 如果有单个样本,需使用input.unsqueeze(0)来添加其它的维数
回顾类
- torch.Tensor:多维数组
- autograd.Variable:包装一个Tensor,记录在其上执行过的操作,除了拥有Tensor的API,还有类似backward()的API,也保持这个向量的梯度
- nn.Module:神经网络模块,封装参数,移到GPU上运行,导出,加载等
- nn.parameter:当把它赋值给一个Module时,被自动注册为一个参数
- autograd.Function:实现自动求导操作的前向和反向定义,每个变量操作至少创建一个函数节点
损失函数
一个损失函数接收(output,target)作为输入
在nn包中有几种不同的损失函数
out = net(input)
target = Variable(torch.arange(1, 11))
criterion = nn.MSELoss()
loss = criterion(out, target)
print(loss)
反向跟踪loss,使用它的.grad_fn属性,你会看到向下面这样的一个计算图:
input -> conv2d -> relu -> maxpool2d -> conv2d -> relu -> maxpool2d
-> view -> linear -> relu -> linear -> relu -> linear
-> MSELoss
-> loss
反向传播
更新权重
使用各种不同的更新规则,比如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 trainning loop:
optimizer.zero_grad() # zero the gradient buffers
output = net(input)
loss = criter(output, target)
loss.backward()
optimizer.setp() # does the update