Pytorch学习笔记(四)

下面的若干段代码是循循渐进的,而且保证了每段代码只对前一段代码进行最小程度的修改,方便连续阅读,这也是我学习的过程,有错误的地方希望不吝赐教,共同进步。

纯手工实现线性回归

10个样本,每个样本特征数为1,需要训练的参数为一个特征权重和一个偏置,所以总共两个要训练的超参数。

采用批量梯度下降法,即使用全部样本的梯度更新权重和偏置。

实现代码

"""
手工实现 y=wx+b 线性回归(梯度下降法)
"""

import torch
import numpy as np

def generate_dataset(true_w, true_b, num_examples, random_seed=10) :
    """
    生成数据集
    :param true_w: 规定的准确权重
    :param true_b: 规定的准确偏置
    :param num_examples: 样本个数
    :param random_seed: 随机种子,保证每次执行都能生成相同的数据集
    :return: 特征与标签(真实值)
    """
    torch.manual_seed(random_seed)  # 为CPU设置随机种子
    np.random.seed(random_seed)  # Numpy module
    x = torch.randn(num_examples, dtype=torch.float32) # 随机生成若干个数据
    y = true_w * x + true_b # 因变量的值
    y += torch.normal(0, 0.01, size=y.size(), dtype=torch.float32) # 加入噪声
    return x, y

def model(x, w, b) :
    """
    模型,计算预测的值,即y_hat
    :param x: 特征
    :param w: 预测的权重
    :param b: 预测的偏置
    :return: 每个样本对应的预测值
    """
    return w * x + b

def loss(y_hat, y) :
    """
    损失函数,计算损失值,每个样本的预测值与真实值之间的误差的平方的均值
    :param y_hat: 预测值
    :param y: 真实值
    :return: 损失值
    """
    return ((y_hat - y) ** 2).mean()

def calculate_gradient(x, y, w, b) :
    """
    计算梯度值,即loss对w的偏导和loss对b的偏导
    :param x: 样本特征
    :param y: 真实值
    :param w: 预测的权重
    :param b: 预测的偏置
    :return: 返回loss对w的偏导和loss对b的偏导
    """
    y_hat = model(x, w, b) # 计算预测值
    dloss_dw = (2 * (y_hat - y) * x).mean() # loss对w的偏导
    dloss_db = (2 * (y_hat - y) * 1).mean() # loss对b的偏导
    return torch.tensor([dloss_dw, dloss_db], dtype=torch.float32) # 这里是将两个数合成一个tensor去操作

def tarin_model(num_epochs, learning_rate, params, x, y) :
    """
    模型训练,梯度下降法更新 w 和 b
    :param num_epochs: epoch次数
    :param learning_rate: 学习率
    :param params: w 和 b构成的一维tensor
    :param x: 样本特征
    :param y: 真实值
    :return: 最终训练完毕的 w 和 b
    """
    for epoch in range(1, num_epochs + 1) :
        w, b = params
        y_hat = model(x, w, b)
        loss_value = loss(y_hat, y)
        gradient = calculate_gradient(x, y, w, b)

        params -= learning_rate * gradient # 更新 w 和 b

        print("Epoch %d  Loss %f" % (epoch, loss_value.item()))

    return w, b

"""
生成数据集
"""
x, y = generate_dataset(true_w=2.5, true_b=4.2, num_examples=10)
# print(x, y)

"""
初始化 w 和 b
"""
w = 0
b = 0

"""
计算梯度
"""
grad_w, grad_b = calculate_gradient(x=x, y=y, w=w, b=b)

"""
反向传播,更新 w 和 b
"""
w, b = \
tarin_model(
    num_epochs=100,
    learning_rate=0.1,
    params=torch.tensor([w, b], dtype=torch.float32),
    x=x,
    y=y
)
print("After Training w = %f b = %f" % (w, b))

几点说明

  • 使用批量梯度下降,即使用全部样本更新权重和偏置。

  • 因为wb是两个数,所以可以使用torch.tensor([w, b])将两个数合并成一个tensor,但是如果wb本身就是tensor,是不可以这样合并成更高维度的tensor的,需要使用torch.stack([w, b])

  • 由于本数据集过于完美,所以收敛效果比较好,设置大的学习步长也没问题,但其他数据集就不一定了。

使用 backward() 实现线性回归

既然我们学习了backward(),那尝试用backward()实现一下。

首先,说明一下,下面代码中所用数据集为1000个样本,每个样本具有2个特征。为了计算方便,我将权重系数w和偏置b合在一起进行计算了,即代码中的w表示的就是w和b,所以代码中的w其实是三维的,同样的道理,样本本来是1000行2列,但由于w和b的合并,所以样本矩阵变为1000行3列了,新增的最后一列全1。

而且下面采用了小批量梯度下降法

实现代码

import torch
import numpy as np


def generate_dataset(true_w, num_examples, random_seed=10) :
    """
    生成数据集
    :param true_w: 规定的准确权重(含偏置)
    :param num_examples: 样本个数
    :param random_seed: 随机种子,保证每次执行都能生成相同的数据集
    :return: 特征与标签(真实值)
    """
    # ----- 保证每次的随机数都一样 -----
    torch.manual_seed(random_seed)  # 为CPU设置随机种子
    np.random.seed(random_seed)  # Numpy module

    # ----- 生成特征信息 -----
    x = torch.ones(size=(num_examples, 3), dtype=torch.float32) # 1000×3的全1张量 # 这里不用加requires_grad=True,因为我们最后是对w(含b)求导
    x[:,:2] = torch.randn(size=(num_examples, 2), dtype=torch.float32) # 随机生成num_examples个数据,每个数据两个特征,即将x的前两列改为特征,最后一列保持为1

    # ----- 生成标签信息 -----
    y = torch.mm(x, true_w) # 进行矩阵乘法 x * w => 1000×3 * 3×1 = 1000×1 # y和x同理,无需加requires_grad=True
    y += torch.normal(0, 0.01, size=y.size(), dtype=torch.float32) # 加入噪声

    return x, y

def model(x, w) :
    """
    模型,返回每个样本对应的预测值,即y_hat
    :param x: 特征+一列1
    :param w: 预测的权重(含偏置)
    :return: 每个样本对应的预测值
    """
    return torch.mm(x, w) # 矩阵乘法

def loss(y_hat, y) :
    """
    损失函数,计算损失值,每个样本的预测值与真实值之间的误差的平方的均值
    :param y_hat: 预测值
    :param y: 真实值
    :return: 损失值
    """
    return ((y_hat - y) ** 2).mean()

# 与“纯手写线性回归”不同,这里不需要写计算梯度的函数了,因为backward自动计算

def train_model(num_epochs, learning_rate, batch_size, w, x, y) :
    """
    训练模型,本质就是更新 w 。
    每个epoch内进行多次更新,每次更新都使用一个batch,循环完一次全部的样本才算一个epoch结束。
    :param num_epochs: epoch次数
    :param learning_rate: 学习率
    :param batch_size: 每个batch大小,即一个batch多少个样本
    :param w: 训练的权重(含偏置)
    :param x: 样本特征
    :param y: 样本标签(真实值)
    :return: 训练完成后的权重(含偏置)
    """
    num_examples = len(x) # 样本数就是x的行数 # 对于二维矩阵而言,len返回的是行的数量

    for epoch in range(1, num_epochs + 1) :

        disorderly_idx = list(np.arange(num_examples)) # 生成0~num_examples-1的序列,即索引
        np.random.shuffle(disorderly_idx) # 随机打乱索引,得到乱序索引

        for i in range(0, num_examples, batch_size) : # 每次循环访问batch_size个索引,作为一组batch

            batch_idx = disorderly_idx[i : min(i + batch_size, num_examples)] # 获取到这组batch的索引 # 之所以存在一个min,是因为防止出现最后一组不足一个batch的情况
            batch_x = x.index_select(0, torch.LongTensor(batch_idx)) # 取出对应行,索引必须为Int64
            batch_y = y.index_select(0, torch.LongTensor(batch_idx)) # 取出对应行,索引必须为Int64

            # ----- 使用这组 batch 更新 w
            loss_value = loss(model(batch_x, w), batch_y) # 有关小批量batch_x和batch_y的损失值
            loss_value.backward() # 反向传播,计算梯度!
            w_data = w.detach() # w是叶子节点,欲修改其值需要使用detach或data
            w_data -= learning_rate * w.grad # 小批量梯度下降(由于计算损失值的时候已经取过均值了,所以这里就不再除以batch_size了)

            w.grad.data.zero_() # 不要忘了梯度清零,而且要使用.data或.detach()

        print("Epoch %d  Loss %f" % (epoch, loss(model(x, w), y).item())) # 显示全部数据集用当前训练得到的w去预测,得到的损失值

    return w

"""
生成数据集
注意:样本的特征是两维(样本数×2),所以权重矩阵w本应为两维(样本数×2),
    但可以将偏置b放入权重矩阵w中,即w为三维(样本数×3),最终结果w的最后一维(列)就是b。
    对应地,样本矩阵也要扩展一维至三维,即最后一维(列)为全1。
"""
num_examples = 1000
num_features = 2 # 样本特征数为2,但特征矩阵列数为3,同样的权重矩阵列数也为3,所以要求num_features始终比true_w中元素个数少1
true_w = torch.tensor([2.5, -1.5, 3.5], dtype=torch.float32).view(-1, 1) # 转换成1列

x, y = \
generate_dataset(
    true_w=true_w,
    num_examples=num_examples
)


"""
初始化 w(含偏置b)
"""
w = torch.ones(size=(num_features + 1, 1), dtype=torch.float32, requires_grad=True) # 注意+1(要求num_features始终比true_w中元素个数少1) # 不要忘记requires_grad=True

"""
训练模型
其中包含反向传播、计算梯度、更新 w 等过程
"""
w = train_model(num_epochs=10, learning_rate=0.01, batch_size=10, w=w, x=x, y=y)

print("True wieght true_w = ", true_w.storage().tolist()) # tensor.stroage()相当于将张量拉伸成一维的,之后使用tolist()转换为列表
print("After Training w = " , w.data.storage().tolist())

几点说明

  • 明确哪些变量是要进行设置requires_grad,哪些不需要。

  • 注意叶子节点更新值的时候要使用.data.detach()

  • 学习如何实现小批量梯度下降。

使用神经网络框架实现线性回归

神经网络的基本框架

从最开始”纯手工实现线性回归“中需要自己写计算梯度的过程,需要自己求出导函数;到”使用 backward() 实现线性回归“中调用自带的 backward() 函数实现计算梯度的过程。我们发现调用现有的函数多是一件美事啊!下面就尝试调用更多的现有函数来实现线性回归

(如果有些函数或者类之前的学习笔记没有讲过,会在使用时补充到用到该函数的博客中,但至于会不会添加到学习笔记三中,另说。也就是说知识点的补充可能会比较零散)。

创建一个继承自 Pytorch 中的 nn.Module 的类来实现神经网络,这样可以使用 Pytorch 提供的许多高级 API,而无需自己实现。

基于 nn.Module的类的最低要求是覆盖__init__()方法和forward()方法。

基本形式如下:

class myModel(nn.Module):
    def __init__(self):
        # 继承父类构造函数
        super(myModel, self).__init__()
        # 这里我们定义一些层次实例。
        self.my_conv_layer = nn.Linear()

    def forward(self, x):
        # 这里我们调用在__init__中定义好的层次实例。
        y_hat = my_conv_layer(x)
        return y_hat

简单来说,在__int__函数内我们定义实例,相当于构建出整个网络的架构;在forward函数内只需要简单地调用定义好的实例就行,偶尔处理一下数据的维度等。

使用创建好的模型的方法:y_hat = myModel(x)

但是这个网络并不具有计算损失函数、进行更新等功能。因为这些功能都是网络结构之外的,我们只是定义了一个网络而已,也就是说,这个网络只有前向传播的能力,即进行预测。整体来看,和我们自定义的model函数没什么区别,都是输入样本x,预测值输出y_hat


不妨看个“高级”的网络模型:(千万不要把每一处都搞明白,这样浪费时间,看清楚结构即可)

class SimpleCNN(nn.Module):
    def __init__(self):
        #继承父类构造函数
        super(SimpleCNN, self).__init__()
        # 这里我们定义一些层次实例。
        # 比如:
        # 先卷积,再relu,再池化
        self.myconv1 = nn.Conv2d(in_channels=3, out_channels=32, kernel_size=(3, 3), padding=1, stride=1, bias=True)
        self.myrelu1 = nn.ReLU(inplace=True)
        self.mymaxpooling1 = nn.MaxPool2d(kernel_size=(2, 2), stride=1)

        # 先卷积,再relu,再池化
        self.myconv2 = nn.Conv2d(in_channels=32, out_channels=64, kernel_size=(3, 3), padding=1, stride=1, bias=True)
        self.myrelu2 = nn.ReLU(inplace=True)
        self.mymaxpooling2 = nn.MaxPool2d(kernel_size=(2, 2), stride=1)

        # 先卷积,再relu,再池化
        self.myconv3 = nn.Conv2d(in_channels=64, out_channels=128, kernel_size=(3, 3), padding=1, stride=1, bias=True)
        self.myrelu3 = nn.ReLU(inplace=True)
        self.mymaxpooling3 = nn.MaxPool2d(kernel_size=(2, 2), stride=1)

        # 开始全连接 2048 -> 512 -> 64 -> 10
        self.myfullconnected1 = nn.Linear(in_features=2048, out_features=512)
        self.myrelu4 = nn.ReLU(inplace=True)
        self.myfullconnected2 = nn.Linear(in_features=512, out_features=64)
        self.myrelu5 = nn.ReLU(inplace=True)
        self.myfullconnected3 = nn.Linear(in_features=64, out_features=10)

    def forward(self, x):
        # 这里我们调用在__init__中定义好的层次实例。
        conv1 = self.myconv1(x)
        relu1 = self.myrelu1(conv1)
        maxpooling1 = self.mymaxpooling1(relu1)

        conv2 = self.myconv2(maxpooling1)
        relu2 = self.myrelu2(conv2)
        maxpooling2 = self.mymaxpooling2(relu2)

        conv3 = self.myconv3(maxpooling2)
        relu3 = self.myrelu3(conv3)
        maxpooling3 = self.mymaxpooling3(relu3)

        output = maxpooling3.view(maxpooling3.size(0), -1) # 不要忘记Linear函数的输入必须是二维的!

        fullconnected1 = self.myfullconnected1(output)
        relu4 = self.myrelu4(fullconnected1)

        fullconnected2 = self.myfullconnected2(relu4)
        relu5 = self.myrelu5(fullconnected2)

        y_hat = self.myfullconnected3(relu5)

        return y_hat

基础好的已经看出这是个手写数字识别的网络了(单看最后10个输出就知道了)。

这里只是让大家看看“学习笔记三”中的一些函数可以如此用,整体上把握网络结构即可,现在还不用能自己写出来。

我觉得学习pytorch,最重要的是模仿,先模仿着写,再明白为什么。这和我们入门人生中的第一门语言C语言是一样的道理,最开始大家都是先去试着写printf,但其实并不知道用法。


实现代码

import torch
import numpy as np
import torch.nn as nn


"""
定义简单的线性神经网络。
输入样本具有二维特征,输出为一维预测标签,学习时含偏置。
"""
class MyLinearRegression(nn.Module) :
    def __init__(self): # 注意别把init写成int
        # 继承父类构造函数
        super(MyLinearRegression, self).__init__()
        # torch.nn.Linear(in_features, out_features, bias=True)
        # 输入样本的特征数为2,输出特征数(标签维度)为1,加上偏置
        self.linear = nn.Linear(in_features=2, out_features=1, bias=True)

    def forward(self, x):
        """
        前向传播。
        对于该类来说就是进行线性计算。
        :param x: 输入样本特征值
        :return: 返回计算结果,输出特征值,即预测标签
        """
        y_hat = self.linear(x)
        return y_hat

"""
生成数据集,该函数与上面代码的不同在于不再将偏置b算入权重w中了,因为Linear函数我们设置了bais=True,会将bais当作一个单独的可学习参数进行训练
"""
def generate_dataset(true_w, num_examples, random_seed=10) :
    """
    生成数据集
    :param true_w: 规定的准确权重(含偏置)
    :param num_examples: 样本个数
    :param random_seed: 随机种子,保证每次执行都能生成相同的数据集
    :return: 特征与标签(真实值)
    """
    # ----- 保证每次的随机数都一样 -----
    torch.manual_seed(random_seed)  # 为CPU设置随机种子
    np.random.seed(random_seed)  # Numpy module

    # ----- 生成特征信息 -----
    x = torch.randn(size=(num_examples, 2), dtype=torch.float32) # 随机生成num_examples个数据,每个数据两个特征

    # ----- 生成标签信息 -----
    y = torch.mm(x, true_w) # 进行矩阵乘法 x * w
    y += torch.normal(0, 0.01, size=y.size(), dtype=torch.float32) # 加入噪声

    return x, y



if __name__ == "__main__" :
    # 创建 MyLinearRegression() 的实例
    model = MyLinearRegression()
    print(model) # 显示模型层次

    # ----- 规定一些参数的值 -----
    num_examples = 1000             # 1000个样本
    batch_size = 10
    num_epochs = 10                 # 迭代次数
    learning_rate = 1e-2            # 学习率 0.01
    mse_loss = torch.nn.MSELoss()   # 损失函数,均方误差
    true_w = torch.tensor([2.5, -1.5], dtype=torch.float32).view(-1, 1)  # 转换成1列,为了方便进行矩阵乘法。其实放在generate_dataset函数内实现也可
    true_b = torch.tensor([3.5], dtype=torch.float32)
    # ----- -----

    optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)  # 优化函数

    # 生成数据集
    x, y = \ 
        generate_dataset(
            true_w=true_w,
            num_examples=num_examples
        )

    # 迭代更新
    for epoch in range(1, num_epochs+1) :

        disorderly_idx = list(np.arange(num_examples))  # 生成0~num_examples-1的序列,即索引
        np.random.shuffle(disorderly_idx)  # 随机打乱索引,得到乱序索引

        for i in range(0, num_examples, batch_size):
            # 生成batch
            batch_idx = disorderly_idx[i: min(i + batch_size, num_examples)]  # 获取到这组batch的索引 # 之所以存在一个min,是因为防止出现最后一组不足一个batch的情况
            batch_x = x.index_select(0, torch.LongTensor(batch_idx))  # 取出对应行,索引必须为Int64
            batch_y = y.index_select(0, torch.LongTensor(batch_idx))  # 取出对应行,索引必须为Int64

            # 预测
            batch_y_hat = model(batch_x)

            # 计算损失值
            loss_value = mse_loss(batch_y_hat, batch_y)

            # 反向传播
            loss_value.backward()

            # 一旦梯度被如backward()之类的函数计算好后,我们就可以调用这个函数更新参数
            optimizer.step()

            # 将可训练参数的梯度清零
            optimizer.zero_grad()

        print("Epoch %d  Loss %f" % (epoch, mse_loss(model(x), y).item()))

torch.optim

torch.optim是一个实现了各种优化算法的库。所谓的优化算法就是更新可学习参数的方法,比如梯度下降等等。

只讲解SGD,Adam不大理解。

torch.optim.SGD

torch.optim.SGD(params, lr=, momentum=0, dampening=0, weight_decay=0, nesterov=False):随机梯度下降

参数的讲解(由于篇幅比较长,所以选择放在另一篇博客中)

实例化:optimizer = torch.optim.SGD(传入参数)

optimizer.step()

这是大多数optimizer所支持的简化版本。一旦梯度被如backward()之类的函数计算好后,我们就可以调用这个函数来自动使用我们定义好的optimizer优化算法来更新权重。

使用方法:optimizer.step()

optimizer.zero_grad()

将梯度清零。

至于与model.zero_grad()的区别,网上几乎都是复制粘贴的,没什么参考价值,只找到了两篇相对有价值的讨论,但还是不理解。

Model.zero_grad() or optimizer.zero_grad()? - PyTorch Forums

PyTorch中的model.zero_grad() vs optimizer.zero_grad() - 简书

暂时这部分只能囫囵吞枣了。

使用优化算法的一般流程

optimizer = optim.SGD(model.parameters(), lr = 0.01, momentum=0.9) # 实例化

for epoch in range(1, num_epochs + 1) : # 迭代
    optimizer.zero_grad() # 梯度清零
    loss_value.backward() # 反向传播
    optimizer.step()	  # 更新权重

使用utils.data生成批量数据实现线性回归

torch.utils.data.Dataset:将tensor数据封装成数据集方便处理。生成的是索引型数据集。
torch.utils.data.DataLoader:生成迭代器,按自定义的方式读取数据集中的数据。生成的是迭代型数据集。

torch.utils.data.Dataset

功能:Dataset 是抽象类,所有自定义的 Dataset 都需要继承该类,并且重写__getitem()__方法和__len__()方法 。__getitem()__方法的作用是接收一个索引,返回索引对应的样本和标签,这是我们自己需要实现的逻辑。__len__()方法是返回所有样本的数量。

import torch
import numpy as np
from torch.utils.data import DataLoader, Dataset

class MyDataSet(Dataset) :
    # 重载了 __init__, __getitem__, __len__
    # 将 Tensor 数据封装成 Tensor 数据集
    # 通过索引可以获取某个样本信息,通过 len 可以获取样本个数

    def __init__(self, features, target) :
        self.x = features
        self.y = target

    def __getitem__(self, index) :
        return self.x[index], self.y[index]

    def __len__(self) :
        return self.x.size(0)

# 生成数据
num_examples = 1000     # 样本数
num_features = 3        # 样本特征数
x = torch.randn(num_examples, num_features)
y = torch.randn(num_examples)

# 将数据封装成 Dataset
mydataset = MyDataSet(x, y)

# 索引获取样本信息
print(mydataset[0])

# len获取样本数量
print(len(mydataset))
"""
(tensor([-0.1909,  0.9481,  0.1173]), tensor(1.0922))
1000
"""

torch.utils.data.TensorDataSet

torch.utils.data.TensorDataset(*tensors):将张量封装成数据集。

描述一下就是,可以传入多个不同维度的张量,但是它们的第一维必须相同。这些张量将被封装成一个多个元组,元组个数为第一维大小。

举两个例子理解:

Pytorch学习笔记(四)_第1张图片


Pytorch学习笔记(四)_第2张图片


TensorDataSet与Dataset的关系:

你不感觉TensorDataSet更像是DataSet的一种特殊情况吗?也就是说我们可以通过继承DataSet类实现处理数据并封装成数据集形式,但TensorDataSet的输入参数只能是满足一定条件张量,直接将输入张量打包封装,可实现的功能更局限,而且无法自定义其他功能。


torch.utils.data.DataLoader

torch.utils.data.DataLoader(dataset, batch_size=1, shuffle=False, sampler=None, batch_sampler=None, num_workers=0, collate_fn=None, pin_memory=False, drop_last=False, timeout=0, worker_init_fn=None, multiprocessing_context=None, generator=None, *, prefetch_factor=2, persistent_workers=False)

基础概念

Epoch、Iteration 和 Batchsize

  • Epoch:所有训练样本都已经输入到模型中,称为一个 Epoch
  • Iteration: 一批样本输入到模型中,称为一个 Iteration
  • Batchsize: 批大小,决定一个 iteration 有多少样本,也决定了一个 Epoch 有多少个 Iteration

假设样本总数有 80,设置 Batchsize 为 8,则共有 80÷8 个 Iteration。这里 1 Epoch = 10 Iteration。

重要参数讲解

  • dataset:(数据类型 Dataset)

    输入的数据类型,也是最重要的参数,它表示要加载数据的数据集对象。

  • batch_size:(数据类型 int)

    批处理样本的大小,默认为1。

  • shuffle:(数据类型 bool)

    在每轮迭代训练时是否将数据洗牌。默认设置为False。设置为True则是在每一轮中,输入数据的顺序将被打乱,这是为了使数据更有独立性,训练的时候一般都设置为True,若输入数据是有序的,就不要设置成True了。

  • collate_fn:(数据类型 callable可调用对象)

    将一小段数据合并成数据列表,默认设置是False。如果设置成True,系统会在返回前会将张量数据(Tensors)复制到CUDA内存中。

  • sampler:(数据类型 Sampler)

    采样,默认设置为None。根据定义的策略从数据集中采样输入。如果定义采样规则,则洗牌(shuffle)设置必须为False。

  • num_workers:(数据类型 Int)

    子进程数量,默认是0。使用多少个子进程来加载数据。0 就是使用主进程来加载数据。注意:这个数字必须是大于等于0的,该值的设置应该量内存大小而为。

  • pin_memory:(数据类型 bool)

    内存寄存,默认为False。在数据返回前,是否将数据复制到CUDA内存中。

  • drop_last:(数据类型 bool)

    丢弃最后数据,默认为False。设置了 batch_size 的数目后,最后一批数据未必是设置的数目,有可能会小些。这时你是否需要丢弃这批数据。

  • timeout:(数据类型 numeric)

    超时值,默认为0。是用来设置数据读取的超时时间,超过这个时间还没读取到数据的话就会报错。 所以,数值必须大于等于0。

简单代码方便理解DataLoader

"""
    批训练,把数据变成一小批一小批数据进行训练。
    DataLoader就是用来包装所使用的数据,每次抛出一批数据
"""
import torch
import torch.utils.data as Data

BATCH_SIZE = 5

x = torch.linspace(1, 10, 10)
y = torch.linspace(10, 1, 10)
# 把数据放在数据库中
torch_dataset = Data.TensorDataset(x, y)
loader = Data.DataLoader(
    # 从数据库中每次抽出batch size个样本
    dataset=torch_dataset,
    batch_size=BATCH_SIZE,
    shuffle=True,
    num_workers=2,
)


def show_batch():
    for epoch in range(3):
        for step, (batch_x, batch_y) in enumerate(loader):
            # training

            print("step:{}, batch_x:{}, batch_y:{}".format(step, batch_x, batch_y))

if __name__ == '__main__':
"""
step:0, batch_x:tensor([ 5.,  8.,  7.,  2., 10.]), batch_y:tensor([6., 3., 4., 9., 1.])
step:1, batch_x:tensor([1., 9., 4., 6., 3.]), batch_y:tensor([10.,  2.,  7.,  5.,  8.])
step:0, batch_x:tensor([3., 7., 9., 2., 5.]), batch_y:tensor([8., 4., 2., 9., 6.])
step:1, batch_x:tensor([ 4.,  8., 10.,  1.,  6.]), batch_y:tensor([ 7.,  3.,  1., 10.,  5.])
step:0, batch_x:tensor([ 3.,  6.,  9.,  2., 10.]), batch_y:tensor([8., 5., 2., 9., 1.])
step:1, batch_x:tensor([8., 5., 7., 1., 4.]), batch_y:tensor([ 3.,  6.,  4., 10.,  7.])
"""

你可以尝试着将 x x x改为更高维度的样本试试输出。

DataLodaer与DataSet类不同在于不能通过索引去访问每个样本信息,只能通过迭代的方式获取样本信息,更方便我们去遍历每一个batch。

实现代码

讲了这么多终于可以写使用这些库来生成batch了!

import torch
import numpy as np
import torch.nn as nn
import torch.utils.data as Data

"""
定义简单的线性神经网络。
输入样本具有二维特征,输出为一维预测标签,学习时含偏置。
"""
class MyLinearRegression(nn.Module) :
    def __init__(self):
        super(MyLinearRegression, self).__init__()
        self.linear = nn.Linear(in_features=2, out_features=1, bias=True)

    def forward(self, x):
        y_hat = self.linear(x)
        return y_hat

"""
生成数据集,该函数与上面代码的不同在于不再将偏置b算入权重w中了,因为Linear函数我们设置了bais=True,会将bais当作一个单独的可学习参数进行训练
"""
def generate_dataset(true_w, num_examples, random_seed=10) :
    torch.manual_seed(random_seed)
    np.random.seed(random_seed)

    x = torch.randn(size=(num_examples, 2), dtype=torch.float32)

    y = torch.mm(x, true_w)
    y += torch.normal(0, 0.01, size=y.size(), dtype=torch.float32)

    return x, y



if __name__ == "__main__" :
    # 创建 MyLinearRegression() 的实例
    model = MyLinearRegression()
    print(model) # 显示模型层次

    # ----- 规定一些参数的值 -----
    num_examples = 1000             # 1000个样本
    batch_size = 10
    num_epochs = 10                 # 迭代次数
    learning_rate = 1e-2            # 学习率 0.01
    mse_loss = torch.nn.MSELoss()   # 损失函数,均方误差
    true_w = torch.tensor([2.5, -1.5], dtype=torch.float32).view(-1, 1)  # 转换成1列,为了方便进行矩阵乘法。
    true_b = torch.tensor([3.5], dtype=torch.float32)
    # ----- -----

    optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)  # 优化函数

    # 生成数据集
    x, y = \
        generate_dataset(
            true_w=true_w,
            num_examples=num_examples
        )

    # 封装成DataSet
    dataset = Data.TensorDataset(x, y)

    # 生成迭代器
    loader = Data.DataLoader(
        dataset=dataset,
        batch_size=batch_size,
        shuffle=True,
        num_workers=2,
    )

    # 迭代更新
    for epoch in range(1, num_epochs+1) :

        for step, (batch_x, batch_y) in enumerate(loader): # 换成DataLoader的迭代方式
            # 预测
            batch_y_hat = model(batch_x)

            # 计算损失值
            loss_value = mse_loss(batch_y_hat, batch_y)

            # 反向传播
            loss_value.backward()

            # 一旦梯度被如backward()之类的函数计算好后,我们就可以调用这个函数更新参数
            optimizer.step()

            # 将可训练参数的梯度清零
            optimizer.zero_grad()

            print("Epoch %d Step %d Loss %f" % (epoch, step+1, mse_loss(model(x), y).item()))
"""
输出太多不打印了,自己运行一下就知道了
"""

REF

[1] 线性回归的从零开始实现 - Dive-into-DL-PyTorch

[2] PyTorch纯手工构建模型并训练 - CSDN博客

[3] pytorch 固定随机数种子 - CSDN博客

[4] 【小白学习PyTorch教程】四、基于nn.Module类实现线性回归模型 - CSDN博客

[5] 卷积神经网络中nn.Conv2d()和nn.MaxPool2d()以及卷积神经网络实现minist数据集分类 - 博客园

[6] PyTorch 学习笔记 2.1 DataLoader 与 DataSet - 知乎

[7] PyTorch中torch.utils.data.DataLoader加载数据 - CSDN博客

[8] Pytorch笔记05-自定义数据读取方式orch.utils.data.Dataset与Dataloader - 知乎

[9] Pytorch Sampler详解 - 博客园

你可能感兴趣的:(【Pytorch学习】,pytorch,深度学习,机器学习)