tensor是的含义是张量,简单的理解可以将其当成三维矩阵,pytorch中的张量是对数据的一种封装,也是数据结构中最核心的部分之一。对于pytorch中的张量,数组可能是更好的理解方法。
torch.Tensor(shape)
方法定义未初始化的张量,使用torch.rand(shape)
或torch.randn(shape)
定义随机张量import torch as pt
x = pt.Tensor(2,4)
print(x)
# 1.00000e-23 *
# 0.0000 0.0000 1.2028 0.0000
# 0.0000 0.0000 1.1517 0.0000
# [torch.FloatTensor of size 2x4]
x = pt.rand(5,3)
# 0.7609 0.5925 0.5840
# 0.1949 0.6366 0.3763
# 0.1802 0.8529 0.9373
# 0.6013 0.9685 0.9945
# 0.6555 0.1740 0.9884
# [torch.FloatTensor of size 5x3]
print(x,x.size()[0])
# 5
y = pt.rand(5,3)
torch.from_numpy(ndarray)
的方法,需要注意的是,这种情况下numpy矩阵和tensor会“绑定”,即修改任何一个的值,另一个的值也会发生变化a = np.ones(5)
b = pt.from_numpy(a)
print(a,b)
#[ 1. 1. 1. 1. 1.]
#1
#1
#1
#1
#1
#[torch.DoubleTensor of size 5]
Tensor和numpy中的ndarray相似,可以完成加减乘除等运算,常见的操作方法通常为Tensor.操作(参数)
或torch.操作(参数,out=输出tensor)
,以加法为例
result = pt.Tensor(5,3)
test = pt.add(x,y,out=result)
# print(result,test)
y.add_(x)
两种方法test = pt.add(x,y,out=result)
和y.add_(x)
都是相加,前者是相加后将结果交给一个新的Tensorresult
,而后者可以理解为y
自加x
Tensor还可以转换为numpy的对象ndarray,可以使用Tensor.numpy()
获得与Tensor
绑定的ndarray对象,修改Tensor时,ndarray对象也发生变化
a = pt.ones(5)
b = a.numpy()
# print(a,b)
a.add_(1)
# print(a,b)
使用Tensor = Tensor.cuda()
的方法可以讲Tensor
放到GPU上,通常的运算不支持从CPU到GPU的变换,因此若要在GPU上进行网络运算,网络声明完成后也要调用网络和输入的.cuda()
方法将网络和输入放在GPU上
a,b = pt.Tensor(2,2),pt.Tensor(2,2)
a = a.cuda()
b = b.cuda()
Variable与TensorFlow中的Variable一样,是构建神经网络和训练的核心类,使用Variable可以构建计算图,并在图中计算结果(正向传播)和微分(反向传播),Variable的一些运算符重载过,因此可以直接使用+-*/
运算符
x = Variable(pt.ones(2,2),requires_grad=True)
y = x + 2
z = y * y * 3
out = z.mean()
# Variable containing:
#27
#[torch.FloatTensor of size 1]
以上构建了一个计算图并计算了out
的值,完成前向传播,使用out.backward()
可以执行反向传播,就是计算微分。
out.backward()
print(x.grad)
#Variable containing:
# 4.5000 4.5000
# 4.5000 4.5000
#[torch.FloatTensor of size 2x2]
class Net(nn.Module):
"""docstring for Net"""
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(1,6,5)# input channel,6 output channel,5x5
self.conv2 = nn.Conv2d(6,16,5)
self.fc1 = nn.Linear(16*5*5,120)#input 16*5*5,output120
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)) #input,poolcore shape
x = F.max_pool2d(F.relu(self.conv2(x)), 2) #(2,2) => 2 because 2=2
x = x.view(-1, self.num_flat_features(x)) #reshape
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:] #remove batch size
num_f = 1
for s in size:
num_f *= s
return num_f
以上为构建一个简单的CNN的例子,其中
nn
来自import torch.nn as nn
这其中封装各种各样的网络层F
来自import torch.nn.functional as F
,这其中封装了各种各样的神经网络需要使用的函数在网络结构中
nn.Linear(input_size,output_size)
为线性连接层,为MLP的线性部分
-nn.Conv2d(input_channel,output_channel,shape)
表示卷积核函数中
F.max_pool2d(input,core_shape)
为池化层F.relu(input)
为ReLu激活函数另外,Variable.view()
为变形函数,其中的-1
表示不关心batch,而函数self.num_flat_features(x)
是为了获得x
的元素数量,这一步直接将x
拍扁成向量
定义网络后(以上的类)后,声明后可以直接调用
net = Net().cuda()
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 (400 -> 120)
# (fc2): Linear (120 -> 84)
# (fc3): Linear (84 -> 10)
#)
其中.cuda()
是将整个网络放到GPU上,如果直接使用net = Net()
网络位置在CPU上,将无法使用GPU加速。
定义网络后,直接传入输入即可完成前向传播
net.zero_grad() # Zero the gradient buffers of all parameters
inputdata = Variable(pt.randn(1,1,32,32)) #?(1,1,32,32) nSamples x nChannels x Height x Width
inputdata = inputdata.cuda()
# 1-batch 1-input channel 32,32
# print(inputdata)
# print(inputdata.unsqueeze(0)) #[torch.FloatTensor of size 1x1x1x32x32]
out = net(inputdata)
print(out)
其中net.zero_grad()
是为了清除梯度,起类似于初始化的作用。pytorch要求数据与网络的位置相同,因此若是网络声明在GPU上,数据也必须要GPU上加速。
网络的反向传播可以直接使用预先定义的代价函数的.backward()
方法实现
net.zero_grad()
print(net.conv1.bias.grad)
loss.backward()
print(net.conv1.bias.grad)
在更新权值的时候,可以手动指定更新的方法
for f in net.parameters():
f.data.sub_(f.grad.data * 0.01)
其中:
除了手动制定,也可以从import torch.optim as optim
中调用优化器
optimizer = optim.SGD(net.parameters(),lr=0.01)
optimizer.zero_grad()
out = net(inputdata)
loss = criterion(out,target)
loss.backward()
optimizer.step()
其中criterion()
为代价函数,loss
为代价函数的输出值,optimizer.step()
为调用一次优化
代价函数表示当前结果距离期望输出的“距离”,torch.nn
封装了一些代价函数,可以在训练的时候直接调用
target = Variable(pt.arange(1,11)).cuda()
# print(target)
criterion = nn.MSELoss().cuda()
loss = criterion(out,target) #out shape = 1*10 target shape = 10
这里调用的代价函数是MSELoss()
平方平均函数
教程提供的范例的训练集是CIFAR10数据集,该数据集提供了10种不同类型的图片,引入代码如下图
import torch
import torchvision
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
import numpy as np
from torch.autograd import Variable
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
transform = transforms.Compose(
[transforms.ToTensor(),
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])
trainset = torchvision.datasets.CIFAR10(root='./data', train=True,
download=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=4,
shuffle=True, num_workers=2)
testset = torchvision.datasets.CIFAR10(root='./data', train=False,
download=True, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=4,
shuffle=False, num_workers=2)
classes = ('plane', 'car', 'bird', 'cat',
'deer', 'dog', 'frog', 'horse', 'ship', 'truck')
这部分仅仅是下载并提供数据集,不必深究,需要注意的是从testloader
中获得数据即可
分类网络搭建使用两层conv+pool后接3层mlp层的结构,是个基本的卷积神经网络,构建类如下
class Net(nn.Module):
"""docstring for Net"""
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(3,6,5)
self.pool = nn.MaxPool2d(2,2)
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 = self.pool(F.relu(self.conv1(x)))
x = self.pool(F.relu(self.conv2(x)))
x = x.view(-1,16*5*5)
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
return x
net = Net().cuda()
这里将组件的定义放在了构造函数中,而将网络前馈部分放在了单独的forward()
函数中。另外,使用net = Net().cuda()
将网络放在了GPU上
分类网络的训练需要定义优化器和代价函数,剩下的就是将数据丢进神经网络中了,代码如下
criterion = nn.CrossEntropyLoss()
#声明使用交叉熵函数作为代价函数
optimizer = optim.SGD(net.parameters(),lr=0.001,momentum=0.9)
#声明使用学习率0.001的SGD优化器
for epoch in range(2):
running_loss = 0
for i,data in enumerate(trainloader,0):
inputs,labels = data
inputs,labels = Variable(inputs).cuda(),Variable(labels).cuda()
#获得数据并将其放在GPU上
optimizer.zero_grad()
#初始化梯度
outputs = net(inputs)
#前馈
loss = criterion(outputs,labels)
loss.backward()
optimizer.step()
#反馈计算梯度并更新权值
running_loss += loss.data[0]
if i % 200 == 0:
print('[%d, %5d] loss: %.3f' %
(epoch + 1, i + 1, running_loss / 2000))
running_loss = 0
#打印平均代价函数值
print('Finished Training')
总结一下,该部分代码总共做了以下几件事
执行网络训练部分,每次迭代包括以下操作
网络测试部分就是将所有的训练数据再投入网络中训练一次,看真实结果与预测结果是否相同,代码如下
corret,total = 0,0
for images,labels in testloader:
images = images.cuda()
labels = labels.cuda()
outputs = net(Variable(images))
_,predicted = torch.max(outputs.data,1)
total += labels.size(0)
corret += (predicted == labels).sum()
print('Accuracy of the network on the 10000 test images: %d %%' % (
100 * corret / total))
前馈得到预测结果后,使用_,predicted = torch.max(outputs.data,1)
在第一维看取出最大的数(丢弃)和最大数的位置(保留)后再与label相比即可进行测试