×

PyTorch教程6.1之层和模块

消耗积分:0 | 格式:pdf | 大小:0.20 MB | 2023-06-05

李维嘉

分享资料个

当我们第一次引入神经网络时,我们专注于具有单一输出的线性模型。在这里,整个模型只包含一个神经元。请注意,单个神经元 (i) 接受一组输入;(ii) 生成相应的标量输出;(iii) 有一组相关参数,可以更新这些参数以优化一些感兴趣的目标函数。然后,一旦我们开始考虑具有多个输出的网络,我们就利用矢量化算法来表征整个神经元层。就像单个神经元一样,层 (i) 采用一组输入,(ii) 生成相应的输出,并且 (iii) 由一组可调参数描述。当我们进行 softmax 回归时,单层本身就是模型。然而,即使我们随后引入了 MLP,

有趣的是,对于 MLP,整个模型及其组成层都共享这种结构。整个模型接受原始输入(特征),生成输出(预测),并拥有参数(来自所有构成层的组合参数)。同样,每个单独的层摄取输入(由前一层提供)生成输出(后续层的输入),并拥有一组可调参数,这些参数根据从后续层向后流动的信号进行更新。

虽然您可能认为神经元、层和模型为我们提供了足够的抽象来开展我们的业务,但事实证明,我们经常发现谈论比单个层大但比整个模型小的组件很方便。例如,在计算机视觉领域广受欢迎的 ResNet-152 架构拥有数百层。这些层由层组的重复图案组成一次一层地实现这样的网络会变得乏味。这种担忧不仅仅是假设——这样的设计模式在实践中很常见。上面提到的 ResNet 架构赢得了 2015 年 ImageNet 和 COCO 计算机视觉识别和检测竞赛He et al. , 2016并且仍然是许多视觉任务的首选架构。层以各种重复模式排列的类似架构现在在其他领域无处不在,包括自然语言处理和语音。

为了实现这些复杂的网络,我们引入了神经网络模块的概念模块可以描述单个层、由多个层组成的组件或整个模型本身!使用模块抽象的一个好处是它们可以组合成更大的工件,通常是递归的。如图 6.1.1所示通过定义代码以按需生成任意复杂度的模块,我们可以编写出奇紧凑的代码并仍然实现复杂的神经网络。

https://file.elecfans.com/web2/M00/A9/C7/poYBAGR9NP2AcRNaAAJd7roQfBs959.svg

图 6.1.1多层组合成模块,形成更大模型的重复模式。

从编程的角度来看,模块由表示。它的任何子类都必须定义一个前向传播方法,将其输入转换为输出,并且必须存储任何必要的参数。请注意,某些模块根本不需要任何参数。最后,为了计算梯度,模块必须具有反向传播方法。幸运的是,由于自动微分(在2.5 节中介绍)在定义我们自己的模块时提供了一些幕后魔法,我们只需要担心参数和前向传播方法。

import torch
from torch import nn
from torch.nn import functional as F
from mxnet import np, npx
from mxnet.gluon import nn

npx.set_np()
from typing import List
import jax
from flax import linen as nn
from jax import numpy as jnp
from d2l import jax as d2l
No GPU/TPU found, falling back to CPU. (Set TF_CPP_MIN_LOG_LEVEL=0 and rerun for more info.)
import tensorflow as tf

首先,我们重新审视用于实现 MLP 的代码(第 5.1 节)。以下代码生成一个网络,该网络具有一个具有 256 个单元和 ReLU 激活的全连接隐藏层,后跟一个具有 10 个单元的全连接输出层(无激活函数)。

net = nn.Sequential(nn.LazyLinear(256), nn.ReLU(), nn.LazyLinear(10))

X = torch.rand(2, 20)
net(X).shape
torch.Size([2, 10])

在这个例子中,我们通过实例化一个 来构造我们的模型 nn.Sequential,层按照它们应该被执行的顺序作为参数传递。简而言之,nn.Sequential定义了一种特殊的Module,在 PyTorch 中呈现模块的类。它维护一个有序的 constituent 列表Module请注意,两个完全连接的层中的每一个都是该类的一个实例,Linear该类本身是 的子类Module前向传播 ( forward) 方法也非常简单:它将列表中的每个模块链接在一起,将每个模块的输出作为输入传递给下一个模块。请注意,到目前为止,我们一直在通过构造调用我们的模型 net(X)以获得它们的输出。这实际上只是 net.__call__(X).

net = nn.Sequential()
net.add(nn.Dense(256, activation='relu'))
net.add(nn.Dense(10))
net.initialize()

X = np.random.uniform(size=(2, 20))
net(X).shape
(2, 10)

In this example, we constructed our model by instantiating an nn.Sequential, assigning the returned object to the net variable. Next, we repeatedly call its add method, appending layers in the order that they should be executed. In short, nn.Sequential defines a special kind of Block, the class that presents a module in Gluon. It maintains an ordered list of constituent Blocks. The add method simply facilitates the addition of each successive Block to the list. Note that each layer is an instance of the Dense class which is itself a subclass of Block. The forward propagation (forward) method is also remarkably simple: it chains each Block in the list together, passing the output of each as input to the next. Note that until now, we have been invoking our models via the construction net(X) to obtain their outputs. This is actually just shorthand for net.forward(X), a slick Python trick achieved via the Block class’s __call__ method.

net = nn.Sequential([nn.Dense(256), nn.relu, nn.Dense(10)])

# get_key is a d2l saved function returning jax.random.PRNGKey(random_seed)
X = jax.random.uniform(d2l.get_key(), (2, 20))
params = net.init(d2l.get_key(), X)
net.apply(params, X).shape
(2, 10)
net = tf.keras.models.Sequential([
  tf.keras.layers.Dense(256, activation=tf.nn.relu),
  tf.keras.layers.Dense(10),
])

X = tf.random.uniform((2, 20))
net(X).shape
TensorShape([2, 10])

In this example, we constructed our model by instantiating an keras.models.Sequential, with layers in the order that they should be executed passed as arguments. In short, Sequential defines a special kind of keras.Model, the class that presents a module in Keras. It maintains an ordered list of constituent Models. Note that each of the two fully connected layers is an instance of the Dense class which is itself a subclass of Model. The forward propagation (call) method is also remarkably simple: it chains each module in the list together, passing the output of each as input to the next. Note that until now, we have been invoking our models via the construction net(X) to obtain their outputs. This is actually just shorthand for net.call(X), a slick Python trick achieved via the module class’s __call__ method.

6.1.1. 自定义模块

也许培养关于模块如何工作的直觉的最简单方法是我们自己实现一个。在我们实现自己的自定义模块之前,我们先简单总结一下每个模块必须提供的基本功能:

  1. 摄取输入数据作为其前向传播方法的参数。

  2. 通过让前向传播方法返回一个值来生成输出。请注意,输出可能具有与输入不同的形状。例如,我们上面模型中的第一个全连接层接收任意维度的输入,但返回 256 维度的输出。

  3. 计算其输出相对于其输入的梯度,可以通过其反向传播方法访问。通常这会自动发生。

  4. 存储并提供对执行前向传播计算所需的那些参数的访问。

  5. 根据需要初始化模型参数。

在下面的代码片段中,我们从头开始编写一个模块,对应于一个包含 256 个隐藏单元的隐藏层和一个 10 维输出层的 MLP。请注意,MLP下面的类继承了代表模块的类。我们将严重依赖父类的方法,仅提供我们自己的构造函数(__init__ Python 中的方法)和前向传播方法。

class MLP(nn.Module):
  def __init__(self):
    # Call the constructor of the parent class nn.Module to perform
    # the necessary initialization
    super().__init__()
    self.hidden = nn.LazyLinear(256)
    self.out = nn.LazyLinear(10)

  # Define the forward propagation of the model, that is, how to return the
  # required model output based on the input X
  def forward(self, X):
    return self.out(F.relu(self.hidden(X)))
class MLP(nn.Block):
  def __init__(self):
    # Call the constructor of the MLP parent class nn.Block to perform
    # the necessary initialization
    super().__init__()
    self.hidden = nn.Dense(256, activation='relu')
    self.out = nn.Dense(10)

  # Define the forward propagation of the model, that is, how to return the
  # required model output based on the input X
  def forward(self, X):
    return self.out(self.hidden(X))
class MLP(nn.Module):
  def setup(self):
    # Define the layers
    self.hidden = nn.Dense(256)
    self.out = nn.Dense(10)

  # Define the forward propagation of the model, that is, how to return the
  # required model output based on the input X
  def __call__(self, X):
    return self.out(nn.relu(self.hidden(X)))
class MLP(tf.keras.Model):
  def __init__(self):
    # Call the constructor of the parent class tf.keras.Model to perform
    # the necessary initialization
    super().__init__()
    self.hidden = tf.keras.layers.Dense(units=256, activation=tf.nn.relu)
    self.out = tf.keras.layers.Dense(units=10)

  # Define the forward propagation of the model, that is, how to return the
  # required model output based on the input X
  def call(self, X):
    return self.out(self.hidden((X)))

让我们首先关注前向传播方法。请注意,它以 X输入为输入,应用激活函数计算隐藏表示,并输出其对数。在这个MLP 实现中,两层都是实例变量。要了解为什么这是合理的,想象一下实例化两个 MLPnet1net2,并在不同的数据上训练它们。自然地,我们希望它们代表两种不同的学习模型。

我们在构造函数中实例化 MLP 的层,随后在每次调用前向传播方法时调用这些层。注意几个关键细节。首先,我们的自定义方法通过让我们免于重述适用于大多数模块的样板代码的痛苦来__init__调用父类的方法。然后我们实例化我们的两个完全连接的层,将它们分配给 请注意,除非我们实现一个新层,否则我们不必担心反向传播方法或参数初始化。系统会自动生成这些方法。让我们试试这个。__init__super().__init__()self.hidden


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

评论(0)
发评论

下载排行榜

全部0条评论

快来发表一下你的评论吧 !

'+ '

'+ '

'+ ''+ '
'+ ''+ ''+ '
'+ ''+ '' ); $.get('/article/vipdownload/aid/'+webid,function(data){ if(data.code ==5){ $(pop_this).attr('href',"/login/index.html"); return false } if(data.code == 2){ //跳转到VIP升级页面 window.location.href="//m.lene-v.com/vip/index?aid=" + webid return false } //是会员 if (data.code > 0) { $('body').append(htmlSetNormalDownload); var getWidth=$("#poplayer").width(); $("#poplayer").css("margin-left","-"+getWidth/2+"px"); $('#tips').html(data.msg) $('.download_confirm').click(function(){ $('#dialog').remove(); }) } else { var down_url = $('#vipdownload').attr('data-url'); isBindAnalysisForm(pop_this, down_url, 1) } }); }); //是否开通VIP $.get('/article/vipdownload/aid/'+webid,function(data){ if(data.code == 2 || data.code ==5){ //跳转到VIP升级页面 $('#vipdownload>span').text("开通VIP 免费下载") return false }else{ // 待续费 if(data.code == 3) { vipExpiredInfo.ifVipExpired = true vipExpiredInfo.vipExpiredDate = data.data.endoftime } $('#vipdownload .icon-vip-tips').remove() $('#vipdownload>span').text("VIP免积分下载") } }); }).on("click",".download_cancel",function(){ $('#dialog').remove(); }) var setWeixinShare={};//定义默认的微信分享信息,页面如果要自定义分享,直接更改此变量即可 if(window.navigator.userAgent.toLowerCase().match(/MicroMessenger/i) == 'micromessenger'){ var d={ title:'PyTorch教程6.1之层和模块',//标题 desc:$('[name=description]').attr("content"), //描述 imgUrl:'https://'+location.host+'/static/images/ele-logo.png',// 分享图标,默认是logo link:'',//链接 type:'',// 分享类型,music、video或link,不填默认为link dataUrl:'',//如果type是music或video,则要提供数据链接,默认为空 success:'', // 用户确认分享后执行的回调函数 cancel:''// 用户取消分享后执行的回调函数 } setWeixinShare=$.extend(d,setWeixinShare); $.ajax({ url:"//www.lene-v.com/app/wechat/index.php?s=Home/ShareConfig/index", data:"share_url="+encodeURIComponent(location.href)+"&format=jsonp&domain=m", type:'get', dataType:'jsonp', success:function(res){ if(res.status!="successed"){ return false; } $.getScript('https://res.wx.qq.com/open/js/jweixin-1.0.0.js',function(result,status){ if(status!="success"){ return false; } var getWxCfg=res.data; wx.config({ //debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。 appId:getWxCfg.appId, // 必填,公众号的唯一标识 timestamp:getWxCfg.timestamp, // 必填,生成签名的时间戳 nonceStr:getWxCfg.nonceStr, // 必填,生成签名的随机串 signature:getWxCfg.signature,// 必填,签名,见附录1 jsApiList:['onMenuShareTimeline','onMenuShareAppMessage','onMenuShareQQ','onMenuShareWeibo','onMenuShareQZone'] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2 }); wx.ready(function(){ //获取“分享到朋友圈”按钮点击状态及自定义分享内容接口 wx.onMenuShareTimeline({ title: setWeixinShare.title, // 分享标题 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享给朋友”按钮点击状态及自定义分享内容接口 wx.onMenuShareAppMessage({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 type: setWeixinShare.type, // 分享类型,music、video或link,不填默认为link dataUrl: setWeixinShare.dataUrl, // 如果type是music或video,则要提供数据链接,默认为空 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享到QQ”按钮点击状态及自定义分享内容接口 wx.onMenuShareQQ({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享到腾讯微博”按钮点击状态及自定义分享内容接口 wx.onMenuShareWeibo({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享到QQ空间”按钮点击状态及自定义分享内容接口 wx.onMenuShareQZone({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); }); }); } }); } function openX_ad(posterid, htmlid, width, height) { if ($(htmlid).length > 0) { var randomnumber = Math.random(); var now_url = encodeURIComponent(window.location.href); var ga = document.createElement('iframe'); ga.src = 'https://www1.elecfans.com/www/delivery/myafr.php?target=_blank&cb=' + randomnumber + '&zoneid=' + posterid+'&prefer='+now_url; ga.width = width; ga.height = height; ga.frameBorder = 0; ga.scrolling = 'no'; var s = $(htmlid).append(ga); } } openX_ad(828, '#berry-300', 300, 250);