本部分共分为5部分:
- 基本数据
- Autograd与计算图
- 神经网络工具箱:torch.nn
- 模型处理 torchvision.model
- 数据处理
pytorch最大的优势是可以使用tensor代替numpy进行对应的矩阵操作并且支持对应的GPU加速。
1.pytorch基本数据: tensor
tensor的使用类似于numpy
-
tensor创建与维度的查看
- torch.tensor()
- torch.zeros()
- torch.ones()
- torch.randn()
- torch.arrange()
- a.shape
- a.size()
-
tensor的组合与分块
- torch.cat()
- torch.stack()
- torch.chunk()
- torch.split()
-
tensor索引与变形
- torch.view()
- torch.resize()
- torch.reshape()
(上述三者功能一致,自由变形) - torch.transpose()
- torch.permute()
(各维度之间变换) - torch.squeeze()
- torch.unsqueeze()
(增加某全1维度,或删除全1维度)
-
tensor的排序与取极值
- a.sort( 0, true) [0]
(按0维排序,true为降序,[0] 表示值,[1]表示索引) - a.max(0)
(按0维度选取最大值,将每列最大值选出)
- a.sort( 0, true) [0]
-
自动广播机制与向量化
自动广播:表示不同形状的tensor(向量)能自动扩展到较大的形状,并进行相应的计算
广播机制前提:
- 任一tensor至少有一个维度
2.从尾部开始遍历(非从头对应),两者维度必须相等,要不其中为1,要不不存在
a = torch.one (3,1,2)
b = torch.one(2,1)
c = torch.one(2,3)
a + b 虽形状不同但可以相加
a + c 不行,尾部为2,3
- tensor的内存共享
即当原始数据改变时,拷贝得到的tensor也会进行相应的改变 - tensor直接初始化另一个: a = b , c = a.view
- 原地运算操作符 :a = b.add_()
- tensor与numpy可以互相转换: a.numpy() | b.from_numpy(a)
2.Autograd与计算图
默认为不求导,需要参数
a = torch.randn(2,2, requires_grad = True)
即可进行求导
3.神经网络工具箱 torch.nn
nn.model
- nn.parameter 函数默认需要求导,即requires_grad = True
- nn.Model自动利用反向传播,Autograd机制。
- nn.functional :也提供网络层和函数,但区别于nn.model,不可自动学习参数,需要用nn.parameter封装。
- nn.functional : 设计初衷针对不需要自动求导的网络层,如BN, 激活等
- nn.Model: 设计针对需要自动求导的网络层,如池化,卷积,全连接等
- nn.Sequential :不考虑中间结果的线性无脑搭建,类似keras
损失函数
主要的损失函数在nn 和nn.functional 库中
举例:
loss_functional = F.cross_entropy(output, label)
实践举例
用我基友的一个例子
import torch
from torch.autograd import Variable #自动求导
import torch.nn.functional as F
import matplotlib.pyplot as plt
x = torch.unsqueeze(torch.linspace(-1,1,100),dim=1)
#linspace : 线性生成器,从-1 到1,一共平均生成100个数,
#dim = 1 以第二维度,行维度,全部增加1 (0无法进行求导,大部分情况自动补1,作为可学习参数的初始化,这里指二分类的初始化)
y = x.pow(2) + 0.2*torch.rand(x.size())
# 以x的维度尺寸,随机数和x平方相加
x,y = Variable(x),Variable(y)
#打印图片
plt.scatter(x.data.numpy(),y.data.numpy())
plt.show()
#建立网络模型类
class Net(torch.nn.Module):
def __init__(self,n_features,n_hidden,n_output):
super(Net,self).__init__()
# 继承nn.module的构造函数初始化
self.hidden=torch.nn.Linear(n_features,n_hidden)
#建立线性层,输入,输出(隐藏层)
self.predit=torch.nn.Linear(n_hidden,n_output)
#建立向前传播的图(由于pytorch动态生成前向图的缘故)
def forward(self,x):
x = F.relu(self.hidden(x))
x = self.predit(x)
return x
optimizer = torch.optim.SGD(net.parameters(),lr=0.5)
loss_func = torch.nn.MSELoss()
#定义对应的优化器和损失函数
net = Net(1,10,1)
#构建对应的网络类,内部参数 n_features =1,n_hidden= 10 ,n_output =1
#故为二分类问题,隐藏层有10个
for t in range(100):
#迭代100次进行训练
prediction =net(x)
loss = loss_func(prediction,y)
# 损失函数参数: prediction 前向值,y 标签值
optimizer.zero_grad() #将所有参数的梯度全部降为0,梯度值保留在这个里面
loss.backward()
#自动反向传播过程
optimizer.step()
#优化器更新
if t%5==0:
#每5次打印
plt.cla()
plt.scatter(x.data.numpy(),y.data.numpy())
plt.plot(x.data.numpy(),prediction.data.numpy(),'r-',lw=5)
plt.text(0.5,0,'Loss=%.4f' % loss.data,fontdict={'size':20,'color':'red'})
plt.pause(0.1)
4.模型处理 torchvision.models
torchvision是一个独立的库,作用为可以快速搭物体检测网络并含有大量封装模型,例如:VGG.Resnet,Inception等等
加载自己的模型
加载模型,即加载对应的state_dict():
state_dict():记录模型状态的字典。由于在保存模型时不需要把所有代码都进行保存,仅需要保存可学习的参数即可,(例如卷积层,全连接等),所以需要有存在保存模型可学习参数(状态)的字典。
- key:每个模型层名字
- value: 该层对应的参数w,b等
每个模型都可以有自己的state_dict(),(优化器也有)
应用于模型加载的常用函数:
(1) torch.save(): 可保存模型,优化器等
(2) torch.load(‘你的模型路径’): 加载模型
(3) torch.nn.Model.load_state_dict():通过去序列化的state_dict来加载模型权重(去掉键值关系的key value)
5.数据加载
pytorch提供了标准化的数据集处理框架
继承Dataset 类 -------》增加数据变换 -------》继承Dataloader
Dataset继承
提供了torch.utils.data.Dataset 类 ,使用时需要重写_len_( ) 和 _ getitem _( )
from torch.utils.data import Dataset
import torch
class MyDateset(Dataset):
def __init__(self,num=10000,transform=None): #这里就可以写你的参数了,比如文件夹什么的。
self.len=num
self.transform=transform
def __len__(self):
return self.len
def __getitem__(self,idx): #用于按照索引读取每个元素的具体内容
data=torch.rand(3,3,5) #这里就是你的数据图像的话就是C*M*N的tensor,这里创建了一个3*3*5的张量
label=torch.LongTensor([1]) #label也是需要一个张量
if self.transform: #这里就是数据预处理的部分 、
data=self.transform(data) #处理完必须要返回torch.Tensor类型
return data,label
数据增强
提供了torchvision.transforms
dataloader
之前所说的Dataset类是读入数据集数据并且对读入的数据进行了索引。但是光有这个功能是不够用的,在实际的加载数据集的过程中,我们的数据量往往都很大,对此我们还需要一下几个功能:
- 可以分批次读取:batch-size
- 可以对数据进行随机读取,可以对数据进行洗牌操作(shuffling),打乱数据集内数据分布的顺序
- 可以并行加载数据(利用多核处理器加快载入数据的效率)
这时候就需要Dataloader类了,Dataloader这个类并不需要我们自己设计代码,我们只需要利用DataLoader类读取我们设计好的ShipDataset即可:
# 利用之前创建好的ShipDataset类去创建数据对象
ship_train_dataset = ShipDataset(data_path, augment=transform)
# 利用dataloader读取我们的数据对象,并设定batch-size和工作现场
ship_train_loader = DataLoader(ship_train_dataset, batch_size=16, num_workers=4, shuffle=False, **kwargs)
完整代码
dataset加载图片和标签,做成一个类,并完成迭代方法
from PIL import Image
import torch
class MyDataset(torch.utils.data.Dataset): #创建自己的类:MyDataset,这个类是继承的torch.utils.data.Dataset
def __init__(self,root, datatxt, transform=None, target_transform=None): #初始化一些需要传入的参数
super(MyDataset,self).__init__()
fh = open(root + datatxt, 'r') #按照传入的路径和txt文本参数,打开这个文本,并读取内容
imgs = [] #创建一个名为img的空列表,一会儿用来装东西
for line in fh: #按行循环txt文本中的内容
line = line.rstrip() # 删除 本行string 字符串末尾的指定字符,这个方法的详细介绍自己查询python
words = line.split() #通过指定分隔符对字符串进行切片,默认为所有的空字符,包括空格、换行、制表符等
imgs.append((words[0],int(words[1]))) #把txt里的内容读入imgs列表保存,具体是words几要看txt内容而定
# 很显然,根据我刚才截图所示txt的内容,words[0]是图片信息,words[1]是lable
self.imgs = imgs
self.transform = transform
self.target_transform = target_transform
def __getitem__(self, index): #这个方法是必须要有的,用于按照索引读取每个元素的具体内容
fn, label = self.imgs[index] #fn是图片path #fn和label分别获得imgs[index]也即是刚才每行中word[0]和word[1]的信息
img = Image.open(root+fn).convert('RGB') #按照path读入图片from PIL import Image # 按照路径读取图片
if self.transform is not None:
img = self.transform(img) #是否进行transform
return img,label #return很关键,return回哪些内容,那么我们在训练时循环读取每个batch时,就能获得哪些内容
def __len__(self): #这个函数也必须要写,它返回的是数据集的长度,也就是多少张图片,要和loader的长度作区分
return len(self.imgs)
#根据自己定义的那个MyDataset来创建数据集!注意是数据集!而不是loader迭代器
train_data=MyDataset(txt=root+'train.txt', transform=transforms.ToTensor())
test_data=MyDataset(txt=root+'test.txt', transform=transforms.ToTensor())
使用Dataloader,进行对应的batch读入,是否洗牌等
#然后就是调用DataLoader和刚刚创建的数据集,来创建dataloader,这里提一句,loader的长度是有多少个batch,所以和batch_size有关
train_loader = DataLoader(dataset=train_data, batch_size=64, shuffle=True)
test_loader = DataLoader(dataset=test_data, batch_size=64)
使用数据集时:
for batch_index, data, target in test_loader:
if use_cuda:
data, target = data.cuda(), target.cuda()
data, target = Variable(data, volatile=True), Variable(target)
_ getitem _( ) 作为迭代器,能直接进行使用
python迭代器:
- iter 创建对应迭代器
- next 给出下一个数据
tips : 在用 for…in… 迭代对象时,如果对象没有实现 __ iter __ , __ next __ 迭代器协议,Python的解释器就会去寻找__getitem__ 来迭代对象.
6.GPU加速
- torch.cuda.is_available() 判断GPU是否可用
- device = torch.device(“cuda : 1”) 指定转移到哪块GPU上
- CUDA_VISIBLE_DEVICES = 2 全局指定使用哪块GPU