PyTorch学习笔记(2)神经网络

1. PyTorch的nn模块

神经网络的结构与原理相信大家已经非常熟悉,这里不再赘述。

PyTorch有一个专门用于神经网络的完整子模块:torch.nn。该子模块包含创建各种神经网络体系结构所需的构建块。这些构建块在PyTorch术语中称为module(模块),在其他框架中称为layer(层)。

PyTorch模块都是从基类nn.Module继承而来的Python类。模块可以具有一个或多个参数(Parameter)实例作为属性,这些参数就是在训练过程中需要优化的张量(在笔记1的线性模型中即w和b)。模块还可以具有一个或多个子模块(nn.Module的子类)属性,并且也可以追踪其参数。

可以找到一个名为nn.Linear的nn.Module子类,它对其输入进行仿射变换(通过参数属性weight和bias);它就相当于之前在笔记1中的温度计实验中实现的方法。现在,从上次中断的地方开始,将之前的代码转换为使用nn的形式。
代码中变量定义请参考笔记1
(原教程中不同小节变量名定义略有出入和错误,笔记中力图将之纠正)

import torch.nn as nn

linear_model = nn.Linear(1, 1) # 参数: input size, output size, bias(默认True)
linear_model(t_un_val) # t_un_val为切分出的验证集,并经过了normalization

nn.Linear的构造函数接受三个参数:输入特征的数量,输出特征的数量以及线性模型是否包含偏差(此处默认为True)。例如,如果在输入中同时使用了温度和气压,则在其中输入具有两个特征输入和而输出只有一个特征。

PyTorch的nn.Module及其子类被设计为可以同时处理多个样本。为了容纳多个样本,模型希望输入的第0维为这个批次中的样本数目。 因此,假设你需要对10个样本运行nn.Linear,则可以创建大小为 B x Nin 的输入张量,其中 B 是批次的大小,而 Nin 是输入特征的数量,然后在模型中同时运行:

x = torch.ones(10, 1)
linear_model(x)

下图显示了批处理图像数据的类似的情况。输入尺寸为 BxCxHxW,其中批处理大小(batch size)B为3(狗、鸟和汽车的图像),每张图像通道数C为3(红色,绿色和蓝色),高度 H 和宽度 W 的像素数未指定。
在这里插入图片描述
输出是大小为 B x Nout 的张量,其中 Nout 是输出特征的数量——在此例中为四个。

进行此批处理的原因是多方面的。一个主要动机是以使我们充分利用执行计算的计算资源。GPU是高度并行化的,因此在小型模型上的单个输入将使大多数计算单元处于空闲状态。通过提供成批的输入,可以将计算分散到其他闲置的计算单元上,这意味着成批的结果就像单个结果一样能够很快地返回。另一个好处是,某些高级模型将使用整个批次的统计信息,而当批次大小较大时那些统计数据将变得更准确。

回到之前的线性模型,需要将尺寸为 B 的输入reshape为 B x Nin,其中Nin为1。

t_c = [0.5,  14.0, 15.0, 28.0, 11.0,  8.0,  3.0, -4.0,  6.0, 13.0, 21.0]
t_u = [35.7, 55.9, 58.2, 81.9, 56.3, 48.9, 33.9, 21.8, 48.4, 60.4, 68.4]
t_c = torch.tensor(t_c).unsqueeze(1) # 增加一维
t_u = torch.tensor(t_u).unsqueeze(1) # 

将之前的手工模型替换为nn.Linear(1,1),然后将线性模型参数传递给优化器:

linear_model = nn.Linear(1, 1)
optimizer = optim.SGD(
    linear_model.parameters(), # 获取任何nn.Module或其子模块的参数列表
    lr=1e-2)

我们为优化器提供了一个require_grad = True张量列表。所有参数都是用这种方式定义的,因为它们需要通过梯度下降进行优化。调用raining_loss.backward()时,grad将累积在图的叶节点上,这些节点正是传递给优化器的参数。
调用optimizer.step()时,优化器将循环访问每个参数,并按与存储在其grad属性中的值成比例的量对其进行更改。
现在你不再明确地将params传递给model,因为model本身在内部保存有Parameters。

下面来看看修改后的完整代码:

import torch
import torch.optim as optim
import torch.nn as nn


def training_loop(n_epochs, optimizer, model, loss_fn,
                  t_u_train, t_u_val, t_c_train, t_c_val):
    for epoch in range(1, n_epochs+1):
        t_p_train = model(t_u_train)
        loss_train = loss_fn(t_p_train, t_c_train)
        
        t_p_val = model(t_u_val)
        loss_val = loss_fn(t_p_val, t_c_val)
        
        optimizer.zero_grad()
        loss_train.backward()
        optimizer.step()
        
        if epoch == 1 or epoch % 1000 == 0:
            print('Epoch %d, Training loss %.4f, Validation loss %.4f' % (
                    epoch, float(loss_train), float(loss_val)))
            

if __name__ == "__main__":
    t_c = [0.5,  14.0, 15.0, 28.0, 11.0,  8.0,  3.0, -4.0,  6.0, 13.0, 21.0] #实际结果(摄氏度)
    t_u = [35.7, 55.9, 58.2, 81.9, 56.3, 48.9, 33.9, 21.8, 48.4, 60.4, 68.4] #温度计读取的数值
    t_c = torch.tensor(t_c).unsqueeze(1) # 将数据增加一维
    t_u = torch.tensor(t_u).unsqueeze(1) 
    # 划分训练集与测试集
    n_samples = t_u.shape[0]
    n_val = int(0.2 * n_samples)  #切出20%为测试集   
    shuffled_indices = torch.randperm(n_samples)
    train_indices = shuffled_indices[:-n_val]
    val_indices = shuffled_indices[-n_val:]
    train_t_u = t_u[train_indices]
    train_t_c = t_c[train_indices]    
    val_t_u = t_u[val_indices]
    val_t_c = t_c[val_indices] 
    t_un_train = 0.1 * train_t_u
    t_un_val = 0.1 * val_t_u
	# 模型
    linear_model = nn.Linear(1, 1)# 参数:input size, output size, bias(默认True)
	# 优化器
    optimizer = optim.SGD(
        linear_model.parameters(),
        lr = 1e-2)
	# 训练循环
    training_loop(
        n_epochs = 3000,
        optimizer = optimizer,
        model = linear_model,
        loss_fn = nn.MSELoss(), # 不再使用自己定义的loss函数
        t_u_train = t_un_train,
        t_u_val = t_un_val,
        t_c_train = train_t_c,
        t_c_val = val_t_c)

还差最后一个步骤:用神经网络代替线性模型作为近似函数。
nn提供了一种通过nn.Sequential容器串联模块的简单方法:

seq_model = nn.Sequential(
            nn.Linear(1, 13),
            nn.Tanh(),
            nn.Linear(13, 1))

该模型将1个输入特征散开为13个隐藏特征,然后将通过tanh激活函数,最后将得到的13个数字线性组合为1个输出特征。
有关nn.Modules参数的一些注意事项:当你检查由几个子模块组成的模型的参数时,可以方便地通过其名称识别参数。这个方法叫做named_parameters:

for name, param in seq_model.named_parameters():
    print(name, param.shape)

输出

0.weight torch.Size([13, 1])
0.bias torch.Size([13])
2.weight torch.Size([1, 13])
2.bias torch.Size([1])

这些都是优化器所需的参数张量。同样,在调用model.backward()之后,所有参数都将被计算其grad,然后优化器会在调用optimizer.step()期间更新参数的值,这与之前的线性模型没有太大不同。毕竟这两个模型都是可微分的模型,可以通过梯度下降进行训练。

实际上,Sequential中每个模块的名称都是该模块在参数中出现的顺序。有趣的是,Sequential还可以接受OrderedDict作为参数,这样就可以给Sequential的每个模块命名:

    from collections import OrderedDict
    seq_model = nn.Sequential(OrderedDict([
        ('hidden_linear', nn.Linear(1, 8)),
        ('hidden_activation'), nn.Tanh(),
        ('output_linear', nn.Linear(8, 1))
    ]))
    for name, param in seq_model.named_parameters():
    	print(name, param.shape)

输出

hidden_linear.weight torch.Size([8, 1])
hidden_linear.bias torch.Size([8])
output_linear.weight torch.Size([1, 8])
output_linear.bias torch.Size([1])

你还可以通过访问子模块来访问特定的参数,就像它们是属性一样:

seq_model.output_linear.bias

输出:

Parameter containing:
tensor([-0.1786], requires_grad=True)

该代码对于检查参数或其梯度(例如在训练期间监视梯度)很有用。
最后画出输入数据(圆形),期望输出(叉号)和显示样本之间行为的连续曲线
在这里插入图片描述
完整代码如下:

import torch
import torch.optim as optim
import torch.nn as nn
from collections import OrderedDict
from matplotlib import pyplot as plt


def training_loop(n_epochs, optimizer, model, loss_fn,
                  t_u_train, t_u_val, t_c_train, t_c_val):
    for epoch in range(1, n_epochs+1):
        t_p_train = model(t_u_train)
        loss_train = loss_fn(t_p_train, t_c_train)
        
        t_p_val = model(t_u_val)
        loss_val = loss_fn(t_p_val, t_c_val)
        
        optimizer.zero_grad()
        loss_train.backward()
        optimizer.step()
        
        if epoch == 1 or epoch % 1000 == 0:
            print('Epoch %d, Training loss %.4f, Validation loss %.4f' % (
                    epoch, float(loss_train), float(loss_val)))
            

if __name__ == "__main__":
    # 实际结果(摄氏度)
    t_c = [0.5,  14.0, 15.0, 28.0, 11.0,  8.0,  3.0, -4.0,  6.0, 13.0, 21.0]
    # 温度计读取的数值(单位未知)
    t_u = [35.7, 55.9, 58.2, 81.9, 56.3, 48.9, 33.9, 21.8, 48.4, 60.4, 68.4]
    t_c = torch.tensor(t_c).unsqueeze(1) # 将数据增加一维
    t_u = torch.tensor(t_u).unsqueeze(1)

    
    # 划分训练集与测试集
    n_samples = t_u.shape[0]
    n_val = int(0.2 * n_samples)    
    shuffled_indices = torch.randperm(n_samples)
    train_indices = shuffled_indices[:-n_val]
    val_indices = shuffled_indices[-n_val:]
    train_t_u = t_u[train_indices]
    train_t_c = t_c[train_indices]    
    val_t_u = t_u[val_indices]
    val_t_c = t_c[val_indices] 
    t_un_train = 0.1 * train_t_u
    t_un_val = 0.1 * val_t_u

        
    seq_model = nn.Sequential(OrderedDict([
        ('hidden_linear', nn.Linear(1, 8)),
        ('hidden_activation', nn.Tanh()),
        ('output_linear', nn.Linear(8, 1))
    ]))

    optimizer = optim.SGD(
        seq_model.parameters(),
        lr = 1e-3)

    training_loop(
        n_epochs = 5000,
        optimizer = optimizer,
        model = seq_model,
        loss_fn = nn.MSELoss(), # 不再使用自己定义的loss
        t_u_train = t_un_train,
        t_u_val = t_un_val,
        t_c_train = train_t_c,
        t_c_val = val_t_c)

    print('output', seq_model(t_un_val))
    print('answer', val_t_c)
    # 打印出隐藏层线性部分的权重的梯度
    print('hidder', seq_model.hidden_linear.weight.grad)

    # 绘图
    t_range = torch.arange(20., 90.).unsqueeze(1)
    fig = plt.figure(dpi = 100)
    plt.xlabel("Fahrenheit")
    plt.ylabel("Celsius")
    plt.plot(t_u.numpy(), t_c.numpy(), 'o')
    plt.plot(t_range.numpy(), seq_model(0.1 * t_range).detach().numpy(), 'c-')
    plt.plot(t_u.numpy(), seq_model(0.1 * t_u).detach().numpy(), 'kx')
    plt.show()

2. nn的子类

对于更大,更复杂的项目,你需要将nn.Sequential放在一边,转而使用可以带来更大灵活性的东西:将nn.Module子类化。
要实现nn.Module的子类,至少需要定义一个forward()函数,该函数将接收模型输入并返回输出。如果你使用的是torch中的操作,那么autograd会自动处理反向传递。
在第一节中,我们已经使用了nn.Sequential来实现一个神经网络,并且用有序字典而不是列表作为输入为每一层添加标签以作改进。
进一步,可以自己定义nn.Module的子类来完全控制输入数据的处理方式。

import torch
import torch.optim as optim
import torch.nn as nn
from collections import OrderedDict
from matplotlib import pyplot as plt


class SubclassModel(nn.Module):
    def __init__(self):
        # super()用于调用父类的方法,可用来解决多重继承问题。
        super().__init__()
        self.hidden_linear = nn.Linear(1, 13)
        self.output_linear = nn.Linear(13, 1)

    # 需要定义一个forward()函数,该函数将接收模型输入并返回输出
    def forward(self, input):
        hidden_t = self.hidden_linear(input)
        activated_t = torch.tanh(hidden_t) # nn.Tanh对应的函数
        output_t = self.output_linear(activated_t)
        return output_t


def training_loop(n_epochs, optimizer, model, loss_fn,
                  t_u_train, t_u_val, t_c_train, t_c_val):
    for epoch in range(1, n_epochs+1):
        t_p_train = model(t_u_train)
        loss_train = loss_fn(t_p_train, t_c_train)
        
        t_p_val = model(t_u_val)
        loss_val = loss_fn(t_p_val, t_c_val)
        
        optimizer.zero_grad()
        loss_train.backward()
        optimizer.step()
        
        if epoch == 1 or epoch % 1000 == 0:
            print('Epoch %d, Training loss %.4f, Validation loss %.4f' % (
                    epoch, float(loss_train), float(loss_val)))
            

if __name__ == "__main__":
    # 实际结果(摄氏度)
    t_c = [0.5,  14.0, 15.0, 28.0, 11.0,  8.0,  3.0, -4.0,  6.0, 13.0, 21.0]
    # 温度计读取的数值(单位未知)
    t_u = [35.7, 55.9, 58.2, 81.9, 56.3, 48.9, 33.9, 21.8, 48.4, 60.4, 68.4]
    t_c = torch.tensor(t_c).unsqueeze(1) # 将数据增加一维
    t_u = torch.tensor(t_u).unsqueeze(1)
  
    # 划分训练集与测试集
    n_samples = t_u.shape[0]
    n_val = int(0.2 * n_samples)    
    shuffled_indices = torch.randperm(n_samples)
    train_indices = shuffled_indices[:-n_val]
    val_indices = shuffled_indices[-n_val:]
    train_t_u = t_u[train_indices]
    train_t_c = t_c[train_indices]    
    val_t_u = t_u[val_indices]
    val_t_c = t_c[val_indices] 
    t_un_train = 0.1 * train_t_u
    t_un_val = 0.1 * val_t_u

    subclass_model = SubclassModel()
    
    optimizer = optim.SGD(
        subclass_model.parameters(),
        lr = 1e-3)

    training_loop(
        n_epochs = 5000,
        optimizer = optimizer,
        model = subclass_model,
        loss_fn = nn.MSELoss(), # 不再使用自己定义的loss
        t_u_train = t_un_train,
        t_u_val = t_un_val,
        t_c_train = train_t_c,
        t_c_val = val_t_c)
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Pytorch机器学习中的一个重要框架,它与TensorFlow一起被认为是机器学习的两大框架。Pytorch学习可以从以下几个方面入手: 1. Pytorch基本语法:了解Pytorch的基本语法和操作,包括张量(Tensors)的创建、导入torch库、基本运算等\[2\]。 2. Pytorch中的autograd:了解autograd的概念和使用方法,它是Pytorch中用于自动计算梯度的工具,可以方便地进行反向传播\[2\]。 3. 使用Pytorch构建一个神经网络学习使用torch.nn库构建神经网络的典型流程,包括定义网络结构、损失函数、反向传播和更新网络参数等\[2\]。 4. 使用Pytorch构建一个分类器:了解如何使用Pytorch构建一个分类器,包括任务和数据介绍、训练分类器的步骤以及在GPU上进行训练等\[2\]。 5. Pytorch的安装:可以通过pip命令安装Pytorch,具体命令为"pip install torch torchvision torchaudio",这样就可以在Python环境中使用Pytorch了\[3\]。 以上是一些关于Pytorch学习笔记,希望对你有帮助。如果你需要更详细的学习资料,可以参考引用\[1\]中提到的网上帖子,或者查阅Pytorch官方文档。 #### 引用[.reference_title] - *1* [pytorch自学笔记](https://blog.csdn.net/qq_41597915/article/details/123415393)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [Pytorch学习笔记](https://blog.csdn.net/pizm123/article/details/126748381)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值