介绍了从torch的数据类型,基本操作,到如何使用torch进行模型构建、训练、测试的过程,算是写了个基本的模板,方便随时记忆。

一、数据类型§

Pytorch中的数据都是Tensor类型的,即张量,是向量、矩阵的扩展

1. 几种创建Tensor变量的方法§

torch.randn((3,2)) # 服从高斯分布的随机数randn
torch.randn(3,2)
# 上面两种都是一样的,会生成3行2列的默认数据类型为float32的Tensor (pytorch默认float32,是为了节省资源)
torch.randn((3,2),dtype=torch.float64)

下面给出了所有的dtype,但是要注意的是像上面的randn是指定不了dtype为torch.int的(会报错RuntimeError: "normal_kernel_cpu" not implemented for 'Int'),这是因为要符合高斯分布的原因,想要这样做可以选择torch.randint方法。解决方案可参考:RuntimeError: “normal_kernel_cpu” not implemented for ‘Byte’

========================== ===========================================   ===========================
Data type                  dtype                                         Legacy Constructors
========================== ===========================================   ===========================
32-bit floating point      ``torch.float32`` or ``torch.float``          ``torch.*.FloatTensor``
64-bit floating point      ``torch.float64`` or ``torch.double``         ``torch.*.DoubleTensor``
64-bit complex             ``torch.complex64`` or ``torch.cfloat``
128-bit complex            ``torch.complex128`` or ``torch.cdouble``
16-bit floating point [1]_ ``torch.float16`` or ``torch.half``           ``torch.*.HalfTensor``
16-bit floating point [2]_ ``torch.bfloat16``                            ``torch.*.BFloat16Tensor``
8-bit integer (unsigned)   ``torch.uint8``                               ``torch.*.ByteTensor``
8-bit integer (signed)     ``torch.int8``                                ``torch.*.CharTensor``
16-bit integer (signed)    ``torch.int16`` or ``torch.short``            ``torch.*.ShortTensor``
32-bit integer (signed)    ``torch.int32`` or ``torch.int``              ``torch.*.IntTensor``
64-bit integer (signed)    ``torch.int64`` or ``torch.long``             ``torch.*.LongTensor``
Boolean                    ``torch.bool``                                ``torch.*.BoolTensor``
========================== ===========================================   ===========================

除了randn常用的还有:

torch.zeros((3,2))
torch.ones((3,2))
torch.Tensor([[2.0,3.2],[3,2]])
torch.from_numpy(a) # a = np.random.randn((2,3)) 将numpy的narray类型转换为tensor类型
torch.arange(6).reshape(2,3)

2. 更改已创建变量的dtype§

a = torch.randint((3,2))
a = a.to(torch.float32) # to这个方法还挺常用的,包括可以给数据指定cpu还是gpu a.to("cuda:0")
a = a.int() #其实a.int()就是调用了a.to(torch.int32)

二、torch中的基本运算§

"""
对于相同大小维度的张量相加减乘的时候,就是对应位运算了,但也是可以利用传播机制,张量可以与标量进行运算
或者只要有一维相同就可以实现运算,下面假设a和b都是shape大小为(2,3)的tensor,且dtype相同。
"""
a+b 
a-b
a*b #对应位相乘 等价于a.mul(b)
a.pow(2)
torch.mm(a,b.reshape(3,2))# 矩阵相乘,n*m m*p -> n*p 这里需注意的是两张量的dtype需完全一致,即使是int32和int64也会报错

三、常用的张量维度变换方法§

  • 减少或增加一维
a = torch.randn((1,3,2,1))
a = a.squeeze() # a.shape (3,2) squeeze会将维度大小为1的维度干掉
a = a.unsqueeze(2) # a.shape (3,2,1) unsqueeze(d)指定增加第d维,最小为第0维
  • 转置
a = torch.randn((3,2))
a = a.T # a.shape (2,3) 最好只用于2维,高于二维目前会warning,在未来版本中会报错
a = a.transpose(0,1) # 交换第0和第1维,可以通过a.transpose_(0,1)直接改变a变量而不用赋值(在pytorch中很多操作都可以通过加入一个_来实现原地修改),在2-D的tensor下,等价于a.T,在高维下就是交换两个指定的维度
  • 交换维度
"""
可以感觉transpose方法显得有些鸡肋,所以使用permute会更为方便
"""
a = torch.arange(12).reshape(2,3,2)
a = a.permute(1,0,2) 
# 交换了第0和第1维,permute需要把所有维度列出来,其中的参数代表的就是原来的维度,重新排列就可以同时交换多维
# 上面的a.permute(1,0,2)可以等价于a.transpose(0,1)

四、梯度计算(自动微分)§

可以说,torch很重要的一点就是可以自动微分了,这也是后续神经网络进行反向传播更新参数的关键所在

举一个简单的例子:

求$y=\sum_{i=0}^{2}x^{2}{i}$对$x_i$在$x_0=2,x_1=3,x_2=4$点的微分$\frac{\partial y}{\partial x{i}}|_{x_0=2,x_1=3,x_2=4}$

"""
x [2.,3.,4.]
requires_grad 允许自动微分
dtype torch.float32 指定为浮点型
Only Tensors of floating point and complex dtype can require gradients
只有浮点型和复数类型可以允许求梯度
"""
x = torch.arange(2,5,dtype=torch.float32,requires_grad=True)
y = x.pow(2).sum() # 可以用y.grad_fn查看反向传播的函数
y.backward() # 反向传播,求梯度
x.grad # [4.,6.,8.]

五、使用Pytorch搭建神经网络并训练§

1. 使用Dataset构建数据集§

from torch.utils.data import Dataset

class myDataset(Dataset):
	super(myDataset,self).__init__()
	def __init__(self):
        # 读取数据X_data, y
		self.X_data = X_data
        self.y = y
    
    def __getitem__(self,idx):
        return X_data[idx],y[idx]
    
    def __len__(self):
        return len(self.y)

2. 使用nn.Module搭建神经网络结构§

import torch.nn as nn
import torch

class myNetwork(nn.Module):
    def __init__(self):
        super(myNetwork,self).__init__()
        # 这里使用nn.Sequential只是定义网络的一种方式,也可以单独拆开每个层,分别定义
        self.fc = nn.Sequential(  
            nn.Linear(3,6),
            nn.Sigmoid(),
            nn.Linear(6,1)
        )
    
    def forward(self,x):
        x = self.fc(x)

        return x

3. 训练模型§

from torch.utils.data import DataLoader,random_split
import torch.nn as nn
import torch

from mydataset import myDataset
from mynetwork import myNetwork

#定义超参数
n_epochs = 60 #迭代次数,每个epoch会对整个训练集遍历一遍
batch_size = 16 #一次加载的数据量,对一个epoch中的样本数的拆分
learning_rate = 0.001 #学习率,或者说步长

#加载数据
train_data = myDataset(is_train=True)
test_dataset = myDataset(is_train=False)
#按8:2划分训练集和验证集
train_size = int(len(train_data)*0.8)
validate_size = len(train_data) - train_size
train_dataset,validate_dataset = random_split(train_data,[train_size,validate_size])
#使用DataLoader加载数据集,转换为迭代器
train_dataloader = DataLoader(train_dataset,batch_size=batch_size,shuffle=True)
validate_dataloader = DataLoader(validate_dataset,batch_size=batch_size) #用于训练中验证模型效果,进而可以动态调整超参数,控制训练
test_dataloader = DataLoader(test_dataset,batch_size=batch_size)

#设置设备
device = "cuda" if torch.cuda.is_available() else "cpu"

#加载模型
model = myNetWork()
model.to(device)

#设置优化器
optimizer = torch.optim.SGD(model.parameters(),lr=learning_rate)

#设置损失函数
criterion = nn.MSEloss()

#训练模型
for epoch_idx in range(n_epochs):
    model.train()
    train_loss = 0.0
    for batch_idx,(X,y) in enumerate(train_dataloader):
        optimizer.zero_grad()
        X,y = X.to(device),y.to(device)
        pred = model(y)
        loss = criterion(pred,y)
        train_loss += loss.item()
        loss.backward()
        optimizer.step()
 	
    #验证模型
    model.eval()
    validate_loss = 0.0
    with torch.no_grad(): #不计算梯度,加快运算速度
        for batch_idx,(X,y) in enumerate(validate_dataloader):
            X,y = X.to(device),y.to(device)
            pred = model(y)
            loss = criterion(pred,y)
            validate_loss += loss.item()
    
    
    print(f"[epoch:{epoch_idx+1}/{epochs}] || train_loss:{train_loss:.2f} || validate_loss:{validate_loss:.2f}")
        

六、模型保存§

  • 只保存模型参数
#保存
torch.save(the_model.state_dict(), PATH)
#读取
the_model = TheModelClass(*args, **kwargs)
the_model.load_state_dict(torch.load(PATH))
  • 保存整个模型
#保存
torch.save(the_model, PATH)
#读取
the_model = torch.load(PATH)