目录
1、Tensor张量
2、自动微分
3、神经网络
4、pytorch图像分类器
5、数据并行处理
1、Tensor张量
#Tensors类似于NumPy的ndarrays,同时Tensors可以使用GPU进行计算
#张量就是按照任意维排列的一堆数字的推广
#其实标量,向量,矩阵它们三个也是张量,标量是零维的张量,向量是一维的张量,矩阵是二维的张量。
import torch
x=torch.empty(5,3)#构造一个5x3矩阵,不初始化。
x1=torch.rand(5,3)#构造一个随机初始化的矩阵
x2=torch.zeros(5,3,dtype=torch.long)#构造一个矩阵全为 0,而且数据类型是 long
x3=torch.tensor([5,5,3])#构造一个张量,直接使用数据
#创建一个tensor基于已经存在的tensor
x4=x3.new_ones(5,3,dtype=torch.double)
print(x4)
x5=torch.randn_like(x4)
print(x5)
#获取它的维度,torch.Size是一个元组,它支持左右的元组操作
print(x5.size())
#操作
#加法:方式一
y=torch.rand(5,3)
print(x+y)
#加法,方式二
print(torch.add(x,y))
#加法:提供一个输出tensor作为参数
result=torch.empty(5,3)
torch.add(x,y,out=result)
print(result)
#加法:in-place,adds x to y
#注意,任何使张量发生变化的操作都有一个前缀,如x.copy(y),x.t_(),将会改变x
y.add_(x)
print(y)
#可以使用标准的Numpy类似的索引操作,按行存储
print(x[:,1])
#改变大小:如果想改变一个tensor的大小或形状,可以使用torch.view
x=torch.randn(4,4)
y=x.view(16)
z=x.view(-1,8)#-1是从其他维度推断出来的
print(x.size(),y.size(),z.size())
#如果有一个元素的tensor,使用.item()来获取这个value
x=torch.randn(1)
print(x)
print(x.item())
print(x,x1,x2)
2、自动微分
"""
1、torch.Tensor 是包的核心类。如果将其属性 .requires_grad 设置为 True,则会开始跟踪针对 tensor的所有操作
2、可以调用 .backward() 来自动计算所有梯度。该张量的梯度将累积到.grad 属性中
3、要停止 tensor 历史记录的跟踪,您可以调用 .detach(),它将其与计算历史记录分离,并防止将来的计算被跟踪
4、要停止跟踪历史记录(和使用内存),您还可以将代码块使用 with torch.no_grad(): 包装起来。在评估模型时,这是特别有用
5、还有一个类对于 autograd 实现非常重要那就是 Function。Tensor 和 Function 互相连接并构建一个非循环图,它保存整个完整的计算过程的历史信息
6、每个张量都有一个 .grad_fn 属性保存着创建了张量的 Function 的引用
7、如果你想计算导数,你可以调用 Tensor.backward()。如果 Tensor 是标量(即它包含一个元素数据),则不需要指定任何参数backward()
8、如果它有更多元素,则需要指定一个gradient 参数来指定张量的形状
"""
import torch
#创建一个张量,设置requires_grad=True来跟踪与它相关的计算
x=torch.ones(2,2,requires_grad=True)
print(x)
#针对向量做一个操作
y=x+2
print(y)
#y作为操作的结果被创建,所以它有grad_fn
print(y.grad_fn)
#针对y做更多的操作
z=y*y*3
out=z.mean()
print(z,out)
#.requires_grad_(...)会改变张量的requires_grad标记。输入的标记默认是False,如果没有提供相应的参数
a=torch.randn(2,2)
a=((a*3)/(a-1))
print(a.requires_grad)
a.requires_grad_(True)
print(a.requires_grad)
b=(a*a).sum()
print(b.grad_fn)
#梯度
#现在向后传播,因为输出包含一个标量,out.backward()等同于out.backward(torch.tensor(1.))
out.backward()
#打印d(out)/dx
print(x.grad)
#看一个雅可比向量积的例子
x=torch.randn(3,requires_grad=True)
y=x*2
while y.data.norm()<1000:#它对张量y中的每个元素进行平方,然后对它们求和,最后取平方根.这些操作计算所谓的L2规范.
y=y*2
print(y)
"""
现在在这种情况下,y 不再是一个标量。torch.autograd 不能够直接计算整个雅可比,但是如果我
们只想要雅可比向量积,只需要简单的传递向量给 backward 作为参数。
"""
v=torch.tensor([0.1,1.0,0.0001],dtype=torch.float)
y.backward(v)
print(x.grad)
#可以通过将代码包裹在with torch.no_grad(),来停止对从跟踪历史中的.requires_grad=True的张量自动求导
print(x.requires_grad)
print((x**2).requires_grad)
with torch.no_grad():
print((x**2).requires_grad)
3、神经网络
input为1通道32*32,卷积核为5*5,输出6通道,另一个卷积为5*5,输出16通道,两个池化(下采样)为2*2滑动窗口,三个全连接层
卷积,激活,池化,卷积,激活,池化,全连接,激活,全连接,激活,全连接
import torch
import torch.nn as nn
import torch.nn.functional as F
"""
现在对于自动梯度(autograd)有一些了解,神经网络是基于自动梯度 (autograd)来定义一些模型。
一个 nn.Module 包括层和一个方法 forward(input) 它会返回输出(output)。
"""
"""
一个典型的神经网络训练过程包括以下几点:
1.定义一个包含可训练参数的神经网络
2.迭代整个输入
3.通过神经网络处理输入
4.计算损失(loss)
5.反向传播梯度到神经网络的参数
6.更新网络的参数,典型的用一个简单的更新方法:weight = weight - learning_rate *gradient
"""
class Net(nn.Module):
def __init__(self):
super(Net,self).__init__()
self.conv1=nn.Conv2d(1,6,5)#5*5的卷积核,1为输入通道,6为输出通道
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))#池化,2*2的滑动窗口,下采样,为了降维
x=F.max_pool2d(F.relu(self.conv2(x)),2)
x=x.view(-1,self.num_flat_features(x))
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:]
num_features=1
for s in size:
num_features*=s
return num_features
net=Net()
print(net)
#一个模型可训练的参数可以通过调用 net.parameters() 返回
params = list(net.parameters())
print(len(params))
print(params[0].size()) # conv1's .weight
#让我们尝试随机生成一个 32x32 的输入。注意:期望的输入维度是 32x32 。为了使用这个网络在MNIST 数据及上,你需要把数据集中的图片维度修改为 32x32
input = torch.randn(1, 1, 32, 32)
out = net(input)
print(input,input.size())
print(out)
#把所有参数梯度缓存器置零,用随机的梯度来反向传播
net.zero_grad()
out.backward(torch.randn(1,10))
#损失函数
"""
一个损失函数需要一对输入:模型输出和目标,然后计算一个值来评估输出距离目标有多远。
有一些不同的损失函数在 nn 包中。一个简单的损失函数就是 nn.MSELoss ,这计算了均方误差
"""
output=net(input)
target=torch.randn(10)
target=target.view(1,-1)#和输出保持相同的形状
criterion=nn.MSELoss()
loss=criterion(output,target)
print(loss)
"""
当我们调用 loss.backward(),整个图都会微分,而且所有的在图中的requires_grad=True
的张量将会让他们的 grad 张量累计梯度
"""
print(loss.grad_fn)
print(loss.grad_fn.next_functions[0][0])#Linear
print(loss.grad_fn.next_functions[0][0].next_functions[0][0])#Relu
#反向传播
"""
为了实现反向传播损失,我们所有需要做的事情仅仅是使用 loss.backward()。你需要清空现存的
梯度,要不然都将会和现存的梯度累计到一起。
现在我们调用 loss.backward() ,然后看一下 conv1 的偏置项在反向传播之前和之后的变化
"""
net.zero_grad()#所有的梯度清零
print('conv1.bias.grad before backward')
print(net.conv1.bias.grad)
loss.backward()
print('conv1.bias.grad after backward')
print(net.conv1.bias.grad)
"""
现在我们看到了,如何使用损失函数。
唯一剩下的事情就是更新神经网络的参数。
更新神经网络参数:
最简单的更新规则就是随机梯度下降。
weight = weight - learning_rate * gradient
"""
learning_rate=0.01
for f in net.parameters():
f.data.sub_(f.grad.data*learning_rate)
"""
尽管如此,如果你是用神经网络,你想使用不同的更新规则,类似于 SGD, Nesterov-SGD, Adam,
RMSProp, 等。为了让这可行,我们建立了一个小包:torch.optim 实现了所有的方法。使用它非常
的简单
"""
#创建优化器
optimizer=torch.optim.SGD(net.parameters(),lr=0.01)
#训练区域
optimizer.zero_grad()
output=net(input)
loss=criterion(output,target)
loss.backward()
optimizer.step()#权值更新
4、pytorch图像分类器
数据集
"""
现在你也许会想应该怎么处理数据?
通常来说,当你处理图像,文本,语音或者视频数据时,你可以使用标准 python 包将数据加载成
numpy 数组格式,然后将这个数组转换成 torch.*Tensor
对于图像,可以用 Pillow,OpenCV
对于语音,可以用 scipy,librosa
对于文本,可以直接用 Python 或 Cython 基础数据加载模块,或者用 NL TK 和 SpaCy
特别是对于视觉,我们已经创建了一个叫做 totchvision 的包,该包含有支持加载类似Imagenet,
CIFAR10,MNIST 等公共数据集的数据加载模块 torchvision.datasets 和支持加载图像数据数据转
换模块 torch.utils.data.DataLoader。
这提供了极大的便利,并且避免了编写“样板代码”。
对于本教程,我们将使用CIFAR10数据集,它包含十个类别:‘airplane’, ‘automobile’, ‘bird’, ‘cat’,
‘deer’, ‘dog’, ‘frog’, ‘horse’, ‘ship’, ‘truck’。CIFAR-10 中的图像尺寸为3*32*32,也就是RGB的3层颜色
通道,每层通道内的尺寸为32*32。
"""
"""
训练一个图像分类器
我们将按次序的做如下几步:
1、使用torchvision加载并且归一化CIFAR10的训练和测试数据集
2、定义一个卷积神经网络
3、定义一个损失函数
4、在训练样本数据上训练网络
5、在测试样本数据上测试网络
"""
import torch
import torchvision
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
import numpy as np
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
"""
加载并归一化 CIFAR10 使用 torchvision ,用它来加载 CIFAR10 数据非常简单
torchvision 数据集的输出是范围在[0,1]之间的 PILImage,我们将他们转换成归一化范围为[-1,1]之
间的张量 Tensors。
"""
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')
"""
定义一个卷积神经网络 在这之前先 从神经网络章节 复制神经网络,并修改它为3通道的图片(在此
之前它被定义为1通道)
"""
class Net(nn.Module):
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()
"""
我们怎么在GPU上跑这些神经网络?
在GPU上训练 就像你怎么把一个张量转移到GPU上一样,你要将神经网络转到GPU上。 如果CUDA
可以用,让我们首先定义下我们的设备为第一个可见的cuda设备。
"""
device=torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
print(device)
"""
本节剩余部分都会假定设备就是台CUDA设备。
接着这些方法会递归地遍历所有模块,并将它们的参数和缓冲器转换为CUDA张量。
"""
net.to(device)
"""
定义一个损失函数和优化器 让我们使用分类交叉熵Cross-Entropy 作损失函数,动量SGD做优化
器。
"""
criterion=nn.CrossEntropyLoss()
optimizer=optim.SGD(net.parameters(),lr=0.001,momentum=0.9)
"""
训练网络 这里事情开始变得有趣,我们只需要在数据迭代器上循环传给网络和优化器 输入就可
以。
"""
#--------------训练开始--------------
if __name__=='__main__':
print('Start Training')
for epoch in range(2):#对数据集进行多次迭代
running_loss=0.0
#获取索引列表
for i,data in enumerate(trainloader,0):#enumerate is useful for obtaining an indexed list: (0, seq[0]), (1, seq[1]), (2, seq[2])
#得到输入
inputs,labels=data
inputs,labels=inputs.to(device),labels.to(device)#将输入数据放到gpu上,如果放在cpu则删掉这条语句
#参数梯度清零
optimizer.zero_grad()
#前向+反向+梯度
outputs=net(inputs)
loss=criterion(outputs,labels)
loss.backward()
optimizer.step()
#打印统计数据
running_loss+=loss.item()
if i%2000==1999:#每2000小批打印一次
print('[%d,%5d] loss:%.3f'%(epoch+1,i+1,running_loss/2000))
running_loss=0.0
print('Finished Training')
#----------------训练结束--------------------
# 展示其中的一些训练图片
def imshow(img):
img = img / 2 + 0.5
npimg = img.cpu().numpy()#这里把img放到了gpu上,如果使用img.numpy()会报错,所以要把img.cpu()才不报错
plt.imshow(np.transpose(npimg, (1, 2, 0)))
plt.show()
if __name__ == '__main__':
# 得到一些随机的训练图片
dataiter = iter(trainloader)
images, labels = dataiter.next()
images,labels=images.to(device),labels.to(device)#将输入数据放到gpu上,如果放在cpu则删掉这条语句
# 展示图片
imshow(torchvision.utils.make_grid(images))
# 打印标签
print(' '.join('%5s' % classes[labels[j]] for j in range(4)))
"""
在测试集上测试网络 我们已经通过训练数据集对网络进行了2次训练,但是我们需要检查网络是否
已经学到了东西。
我们将用神经网络的输出作为预测的类标来检查网络的预测性能,用样本的真实类标来校对。如
果预测是正确的,我们将样本添加到正确预测的列表里
"""
outputs=net(images)
print(outputs)
"""
输出是预测与十个类的近似程度,与某一个类的近似程度越高,网络就越认为图像是属于这一类
别。所以让我们打印其中最相似类别类标
"""
_,predicted=torch.max(outputs,1)
print('Predicted:',' '.join('%5s' % classes[predicted[j]] for j in range(4)))
#结果看起开非常好,让我们看看网络在整个数据集上的表现
correct=0
total=0
with torch.no_grad():
for data in testloader:
images,labels=data
images,labels=images.to(device),labels.to(device)#将输入数据放到gpu上,如果放在cpu则删掉这条语句
outputs=net(images)
_,predicted=torch.max(outputs.data,1)
total+=labels.size(0)
correct+=(predicted==labels).sum().item()
print('Accuracy of the network on the 10000 test images:%d %%'%(100*correct/total))
#这看起来比随机预测要好,随机预测的准确率为10%(随机预测出为10类中的哪一类)。看来网络学到了东西
class_correct=list(0.for i in range(10))
class_total=list(0.for i in range(10))
with torch.no_grad():
for data in testloader:
images,labels=data
images,labels=images.to(device),labels.to(device)#将输入数据放到gpu上,如果放在cpu则删掉这条语句
outputs=net(images)
_,predicted=torch.max(outputs,1)
c=(predicted==labels).squeeze()
for i in range(4):
label=labels[i]
class_correct[label]+=c[i].item()
class_total[label]+=1
for i in range(10):
print('Accuracy of %5s : %2d %%'%(classes[i],100*class_correct[i]/class_total[i]))
5、数据并行处理
"""
在这个教程中,我们将学习如何用 DataParallel 来使用多 GPU。 通过 PyTorch 使用多个 GPU 非常
简单。你可以将模型放在一个 GPU:
device = torch.device("cuda:0")
model.to(device)
然后,你可以复制所有的张量到 GPU:
mytensor = my_tensor.to(device)
请注意,只是调用 my_tensor.to(device) 返回一个 my_tensor 新的复制在GPU上,而不是重写
my_tensor。你需要分配给他一个新的张量并且在 GPU 上使用这个张量。
"""
"""
在多 GPU 中执行前馈,后馈操作是非常自然的。尽管如此,PyTorch 默认只会使用一个 GPU。通
过使用 DataParallel 让你的模型并行运行,你可以很容易的在多 GPU 上运行你的操作。
model = nn.DataParallel(model)
"""
import torch
import torch.nn as nn
from torch.utils.data import Dataset,DataLoader
#参数
input_size=5
output_size=2
batch_size=30
data_size=100
device=torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
#生成一个实验数据,只需要实现getitem
class RandomDataset(Dataset):
def __init__(self,size,length):
self.len=length
self.data=torch.randn(length,size)#100*5
def __getitem__(self, index):
return self.data[index]
def __len__(self):
return self.len
rand_loader=DataLoader(dataset=RandomDataset(input_size,data_size),batch_size=batch_size,shuffle=True)
"""
简单模型
为了做一个小 demo,我们的模型只是获得一个输入,执行一个线性操作,然后给一个输出。尽管
如此,你可以使用 DataParallel 在任何模型(CNN, RNN, Capsule Net 等等.)
我们放置了一个输出声明在模型中来检测输出和输入张量的大小。请注意在 batch rank 0 中的输
出。
"""
class Model(nn.Module):
def __init__(self,input_size,output_size):
super(Model, self).__init__()
self.fc=nn.Linear(input_size,output_size)
def forward(self, input):
output=self.fc(input)
print("\tIn Model:input size",input.size(),"output size",output.size())
return output
"""
创建模型并且数据并行处理
这是整个教程的核心。首先我们需要一个模型的实例,然后验证我们是否有多个 GPU。如果我们
有多个 GPU,我们可以用nn.DataParallel来包裹我们的模型。然后我们使用 model.to(device)
把模型放到多 GPU 中。
"""
model=Model(input_size,output_size)
if torch.cuda.device_count()>1:
print("Let's use",torch.cuda.device_count(),"GPUs")
model=nn.DataParallel(model)#放到多个GPU上
model.to(device)
#运行模型:现在我们可以看到输入和输出张量的大小
for data in rand_loader:
input = data.to(device)
output = model(input)
print("Outside:input size", input.size(), "output_size", output.size())
"""
总结:
数据并行自动拆分了你的数据并且将任务单发送到多个 GPU 上。当每一个模型都完成自己的任务
之后,DataParallel 收集并且合并这些结果,然后再返回给你。
"""