Pytorch 基础组件介绍(Pytorch入门)

Pytorch 基础组件学习

注:本文为学习 DataWhale 开源教程《深入浅出 Pytorch》第三章所做学习笔记,仅记录笔者认为价值较高且之前接触较少的知识点

1. 数据读取

​ Pytorch 的数据读取通过 Dataset + DataLoader 实现,Dataset 定义了数据的格式和数据变换方式,DataLoader 定义了如何从数据集中加载每一批数据。

​ 定义 Dataset 类需要继承 Pytorch 自身的 Dataset 类,并相应定义自己的类方法,主要包括三个函数:

1. init:构造函数,定义样本集、构造数据集
1. getitem:定义如何读取数据集中的样本
1. len:返回数据集样本数

​ 可以根据本地数据所在的 csv 文件,定义自己的 Dataset 并定义相关方法,此处定义一个构建头条新闻文本的 Dataset:

class MyDataset(Dataset):
    def __init__(self, data_base):
        """
        Args:
            data_base: 读取的原始数据,list类型。
        """
        self.data, self.label = self.pre_change(data_base)

    def pre_change(self, data_base):
        # 用于对读入的数据进行预处理成DataFrame类型
        label_list = []
        # 存标签数据
        text_list = []
        # 存文本数据
        key_words_list = []
        # 存关键词文本
        for one_item in data_base:
            # 每一条数据是一个以_!_分割的字符串
            info_list = one_item.split("_!_")
            label_list.append(int(info_list[1]))
            text_list.append(info_list[3] + info_list[4])
        return pd.DataFrame(text_list, columns=["text"]), pd.DataFrame(label_list, columns=["label"])

    def __getitem__(self, index):
        """
        Args:
            index: the index of item
        Returns:
            text and its labels
        """
        text = self.data.iloc[index, 0]
        label = self.label.iloc[index, 0]
        return text, label

    def __len__(self):
        return self.data.shape[0]

​ 此处我们的源数据为 txt 类型,读入成列表之后进行了训练集、测试集与验证集的拆分。

​ Pytorch 在训练模型时,将使用 DataLoader 从 Dataset 中依次迭代一批数据(一个 batch_size),因此需要基于三个 Dataset 构造三个 DataLoader:

# 具体构建三种数据的数据集
train_dataset = MyDataset(train_data)
test_dataset = MyDataset(test_data)
eval_dataset = MyDataset(eval_data)

# 构建Dataloader
train_dataloader = DataLoader(train_dataset, batch_size=batch_size, num_workers=5, shuffle=True, drop_last=True)
# batch_size为批量;num_workers为读取数据的进程数;shuffle为是否对数据进行洗牌;drop_last为对最后不足一批的数据是否进行丢弃
test_dataloader = DataLoader(test_dataset, batch_size=batch_size, num_workers=5, shuffle=False)
eval_dataloader = DataLoader(eval_dataset, batch_size=batch_size, num_workers=5, shuffle=False)

2. 网络构建

​ Pytorch 基于 Module 类构造自己的神经网络模型,一个模型可以构建多个层,需要定义构造函数以及前向计算函数。

​ 首先我们定义几个常见的层。

  1. 自定义无参数层:

    class MyLayer(nn.Module):
    
        def __init__(self, **kwargs):
            # 构造函数
            super(MyLayer, self).__init__(**kwargs)
    
        def forward(self, x):
            # 前向计算函数,此处为减去向量均值
            return x - x.mean()  
    

    注:定义一个自定义层,主要需要定义构造函数、前向计算函数,在构造函数中实现该层的初始化,在前向计算函数中实现该层的计算功能。

  2. 自定义含参数层:

    class MyDictDense(nn.Module):
    
        def __init__(self):
            super(MyDictDense, self).__init__()
            self.params = nn.ParameterDict({
                    'linear1': nn.Parameter(torch.randn(4, 4)),
                    'linear2': nn.Parameter(torch.randn(4, 1))
            })
            self.params.update({'linear3': nn.Parameter(torch.randn(4, 2))}) # 新增
    
        def forward(self, x, choice='linear1'):
            # 默认使用 linear1 的参数
            return torch.mm(x, self.params[choice])
            # torch.mm 为矩阵相乘
    

    注:定义一个含参数层,可以使用 ParameterDict 构造参数字典,也可以使用 ParamterList 构造参数列表

  3. 卷积层

    # 卷积运算(二维互相关)
    def corr2d(X, K): 
        h, w = K.shape
        X, K = X.float(), K.float()
        Y = torch.zeros((X.shape[0] - h + 1, X.shape[1] - w + 1))
        for i in range(Y.shape[0]):
            for j in range(Y.shape[1]):
                Y[i, j] = (X[i: i + h, j: j + w] * K).sum()
        return Y
    
    # 二维卷积层
    class Conv2D(nn.Module):
        def __init__(self, kernel_size):
            super(Conv2D, self).__init__()
            self.weight = nn.Parameter(torch.randn(kernel_size))
            self.bias = nn.Parameter(torch.randn(1))
            # 二维卷积有两个参数
    
        def forward(self, x):
            return corr2d(x, self.weight) + self.bias
    

    注:卷积层是卷积神经网络的重要搭建部件,实现卷积层重点在于卷积操作的定义

  4. 池化层

    def pool2d(X, pool_size, mode='max'):
        p_h, p_w = pool_size
        Y = torch.zeros((X.shape[0] - p_h + 1, X.shape[1] - p_w + 1))
        for i in range(Y.shape[0]):
            for j in range(Y.shape[1]):
                if mode == 'max':
                    Y[i, j] = X[i: i + p_h, j: j + p_w].max()
                elif mode == 'avg':
                    Y[i, j] = X[i: i + p_h, j: j + p_w].mean()
        return Y
    

    注:池化是用于缓解过拟合的重要操作,可以选择最大池化、平均池化或其他

3. 参数初始化

​ Pytorch 性能的一个重要影响因素是参数初始化范围,Pytorch 提供了多种参数初始化的方法:

1. uniform_:均匀分布初始化
1. normal_:正态分布初始化
1. constant_:常数初始化
1. eye_:初始化为单位矩阵
1. sparse_:初始化为稀疏矩阵

​ 注:可以使用 isinstance 判断该层属于什么模块,然后对于不同的模块需要进行不同的初始化

​ 可以基于上述方法封装一个初始化函数:

def initialize_weights(self):
	for m in self.modules():
		# 判断是否属于Conv2d
		if isinstance(m, nn.Conv2d):
			torch.nn.init.xavier_normal_(m.weight.data)
			# 判断是否有偏置
			if m.bias is not None:
				torch.nn.init.constant_(m.bias.data,0.3)
		elif isinstance(m, nn.Linear):
			torch.nn.init.normal_(m.weight.data, 0.1)
			if m.bias is not None:
				torch.nn.init.zeros_(m.bias.data)
		elif isinstance(m, nn.BatchNorm2d):
			m.weight.data.fill_(1) 		 
			m.bias.data.zeros_()	

4. 损失函数

​ 损失函数是 Pytorch 训练的一个重要组成部分,此处仅列出各种常用的损失函数方法及名称,具体原理可参见《NNDL》书籍:

  1. 二分类交叉熵损失函数:weight 为每个类别 loss 的权重,size_average 为是否对 loss 取平均值,reduce 为是否返回标量,

    torch.nn.BCELoss(weight=None, size_average=None, reduce=None, reduction='mean')
    
  2. 交叉熵损失函数:ignore_index 为忽略某个类的损失

    torch.nn.CrossEntropyLoss(weight=None, size_average=None, ignore_index=-100, reduce=None, reduction='mean')
    
  3. L1 损失函数:即 MAE

    torch.nn.L1Loss(size_average=None, reduce=None, reduction='mean')
    
  4. MSE 损失函数

    torch.nn.MSELoss(size_average=None, reduce=None, reduction='mean')
    
  5. 平滑 L1 损失函数:即平滑之后的 L1 损失,beta 为平滑系数

    torch.nn.SmoothL1Loss(size_average=None, reduce=None, reduction='mean', beta=1.0)
    
  6. 多标签边界损失函数:用于多标签分类

    torch.nn.MultiLabelMarginLoss(size_average=None, reduce=None, reduction='mean')
    
  7. 二分类逻辑损失函数:

    torch.nn.SoftMarginLoss(size_average=None, reduce=None, reduction='mean')
    
  8. HingeEmbedding损失:专用于 Embedding,margin 为边界值

    torch.nn.HingeEmbeddingLoss(margin=1.0, size_average=None, reduce=None, reduction='mean')
    

5. 训练

​ Pytorch 中的训练有一个统一的流程:

def train(epoch):
    model.train()
    # 打开训练模式
    train_loss = 0
    # 训练损失
    for data, label in train_loader:
        # 一次训练一批样本
        data, label = data.cuda(), label.cuda()
        # 将样本和标签都加载到GPU
        optimizer.zero_grad()
        # 清零梯度值
        output = model(data)
        # 根据样本输出预测标签
        loss = criterion(label, output)
        # 根据预测标签计算损失函数
        loss.backward()
        # 反向传播
        optimizer.step()
        # 梯度下降
        train_loss += loss.item()*data.size(0)
        # 加上损失
    train_loss = train_loss/len(train_loader.dataset)
    # 计算这一个 epoch 的整体损失
		print('Epoch: {} \tTraining Loss: {:.6f}'.format(epoch, train_loss))

​ 训练和测试不同的地方在于:

1. 测试不需要计算梯度、反向传播
2. 测试不需要优化器置零
3. 测试不需要更新优化器

6. 优化器

​ 优化器会在一定程度上影响模型的收敛效果以及收敛速度,Pytorch 提供的常见优化器包括:ASGD、Adam、Adagrad、LBFGS、SGD等。

​ 常用的优化器方法:

1. zero_grad:梯度清零
2. step:执行一步梯度更新
3. load_state_dict:加载状态参数字典,可以断点续传
4. state_dict:获取当前状态参数字典

你可能感兴趣的:(神经网络,pytorch,深度学习,python)