PyTorch实现MLP的两种方法,以及nn.Conv1d, kernel_size=1和nn.Linear的区别

PyTorch实现MLP的两种方法,以及nn.Conv1d,kernel_size=1和nn.Linear的区别MLP(Multi-layerperceptron)实现MLP结构方法1:nn.Linear方法2:nn.Conv1d&kernel_size=1nn.Conv1d,kernel_size=1与nn.Linear不同MLP(Multi-layerperceptron)实现最近在看PointNet论文,其主要思想为利用MLP结构学习点云特征,并进行全局池化(构造一个对称函数,

大家好,又见面了,我是你们的朋友全栈君。

MLP(Multi-layer perceptron,多层感知机)实现

最近在看PointNet论文,其主要思想为利用MLP结构学习点云特征,并进行全局池化(构造一个对称函数,symmetric function),实现无序点集输入时特征提取的不变性。

转到代码实现时,原以为用nn.LinearPyTorch)这个方法创建网络结构(因为结构上CNN中的全连层FC Layer就是一个MLP,而其实现用的就是nn.Linear),但实际上用的是nn.Conv1d(注意kernel_size=1)实现的,一下就有些疑问了,nn.Conv1d也能实现MLP结构?

答案是肯定的,但输入数据形式存在不同

MLP结构

MLP应该是最简单的神经网络结构,下图(由NN-SVG生成)所示为一个输入层4节点隐含层8节点输出层3节点MLP
MLP结构
每一层的每个节点与前一层的所有节点进行连接(也即CNN中全连接的来由),节点的个数表示该层的特征维度,通过设置网络层数和节点个数,学习到输入数据的不同维度特征信息。

具体到数据处理形式上,MLP计算如下:
X = [ x 1 , x 2 , . . . , x m ] T X = [x_{1}, x_{2}, …, x_{m}]^{T} X=[x1,x2,...,xm]T
Y = [ y 1 , y 2 , . . . , y n ] T Y=[y_{1}, y_{2}, …, y_{n}]^{T} Y=[y1,y2,...,yn]T
h j = ∑ i = 1 m w i j x i h_{j}=\sum\limits_{i=1}^{m}w_{ij}x_{i} hj=i=1mwijxi
y j = g ( h j ) = g ( ∑ i = 1 m w i j x i ) y_{j}=g(h_j)=g(\sum\limits_{i=1}^{m}w_{ij}x_{i}) yj=g(hj)=g(i=1mwijxi)
其中:

  • X X X:输入层向量, m m m个维度/节点, Y Y Y:输出层向量, n n n个维度/节点,注意:此处输入层输出层指的是相邻两层前一层为输入层后一层为输出层与MLP的输入层和输出层概念不同
  • w w w:权重系数, w i j w_{ij} wij:输入层第 i i i个节点至输出层第 j j j个节点的权重
  • h j h_{j} hj:输出层第 j j j个节点的所有输入层节点加权之和
  • g ( ) g() g():激活函数
  • i = 1 , 2 , . . . , m i=1, 2, …, m i=1,2,...,m j = 1 , 2 , . . . , n j=1, 2, …, n j=1,2,...,n

需要注意的是,上述表示的是以向量Tensor维度为1)作为输入的计算过程,对于由多个向量构成的多维矩阵Tensor维度大于等于2),计算过程类似,保持向量的组合尺寸,只对向量的不同特征维度进行加权计算

例如,对于一个长度为100的点云100×3,tensor)进行MLP处理,经过一个3输入-10输出Layer计算后,输出结果仍为一个二维tensor100×10,tensor);同样,对于一个batch size为4,长度为100的点云数据包4×100×3,tensor),经过同样的Layer计算,输出为一个三维tensor4×100×10,tensor),如下图所示
计算数据流

方法1:nn.Linear

PyTorch官方文档中nn.Linear的描述如下:
nn.Linear介绍
对输入数据 x x x进行一个线性变化,与上文中 h h h的计算方式一致,具体含义:

  • in_features:每个输入样本的大小,对应MLP中当前层的输入节点数/特征维度
  • out_features:每个输出样本的大小,对应MLP中当前层的输出节点数/特征维度
  • 输入数据形式:形状为[N, *, in_features]的tensor,N为batch size,这个参数是PyTorch各个数据操作中都具备的,相似的,输出数据形式为[N, *, out_features]

需要注意的是输入输出数据形式中的*参数,其表示为任意维度,对于单个向量,*为空

代码A:利用nn.Linear对单个点云数据(向量)进行Layer计算

import torch
import torch.nn as nn
import torch.nn.functional as F

x = torch.randn(1, 3)		# 创建batch_size=1的单个点云
layer = nn.Linear(3, 10)	# 构造一个输入节点为3,输出节点为10的网络层
y = F.sigmoid(layer(x))		# 计算y,sigmoid激活函数

print(x.size())
print(y.size())
''' >>>torch.Size([1, 3]) >>>torch.Size([1, 10]) '''

代码B:利用nn.Linear对点云集进行Layer计算

import torch
import torch.nn as nn
import torch.nn.functional as F

x = torch.randn(1, 100, 3)		# 创建一个batch_size=1的点云,长度100
layer = nn.Linear(3, 10)	    # 构造一个输入节点为3,输出节点为10的网络层
y = F.sigmoid(layer(x))		    # 计算y,sigmoid激活函数

print(x.size())
print(y.size())
''' >>>torch.Size([1, 100, 3]) >>>torch.Size([1, 100, 10]) '''

代码C:利用nn.Linear对多批次点云集进行Layer计算

import torch
import torch.nn as nn
import torch.nn.functional as F

x = torch.randn(4, 100, 3)		# 创建一个batch_size=4的点云,长度100
layer = nn.Linear(3, 10)	    # 构造一个输入节点为3,输出节点为10的网络层
y = F.sigmoid(layer(x))		    # 计算y,sigmoid激活函数

print(x.size())
print(y.size())
''' >>>torch.Size([4, 100, 3]) >>>torch.Size([4, 100, 10]) '''

通过上述代码可以看出,nn.Linear作用在输入数据的最后一个维度上,这一点不同于以下的nn.Conv1d

方法2:nn.Conv1d & kernel_size=1

Pytorch官方文档中nn.Conv1d的描述如下:
nn.Conv1d介绍
关键参数:

  • in_channels:输入通道,MLP中决定Layer输入的节点
  • out_channels:输出通道,MLP中决定Layer输出的节点
  • kernel_size:卷积核的宽度,应用在MLP中必须为1
  • stride:每次卷积移动的步长,应用在MLP中必须为1
  • padding:序列两端补0的个数,应用在MLP中必须为0

与图像的二维卷积(可参考该博客中gif介绍)类似,一维卷积表示对序列数据进行卷积,如下图所示:
一维卷积示意
每个卷积核沿着数据长度方向对核内的数据进行卷积(根据卷积核权重累加),每移动一个步长获取一个值,所有的值构成输出的一个通道/特征维度;每个卷积核计算获得一个通道/特征维度

由nn.Conv1d的输出长度计算方式和上图示意可知:

kernel_size=1stride=1padding=0时,每个卷积核计算后输出数据和输入数据的长度相同,并且一一对应,即 h o j = ∑ s = 1 i c k s x j s h_{oj}=\sum\limits_{s=1}^{ic}k_{s}x_{js} hoj=s=1icksxjs o j oj oj为第 o o o个卷积核第 j j j个输出值, i c ic ic为输入数据的通道/特征维度, j s js js为输入数据第 j j j个中通道 s s s的位置,与MLP的节点计算方式一样,因此可以用nn.Conv1d进行MLP计算

代码D:利用nn.Conv1d对单个点云数据(向量)进行Layer计算

import torch
import torch.nn as nn
import torch.nn.functional as F

x = torch.randn(1, 3, 1)		            # 创建batch_size=1的单个点云
layer = nn.Conv1d(3, 10, kernel_size=1)	    # 构造一个输入节点为3,输出节点为10的网络层
y = F.sigmoid(layer(x))		                # 计算y,sigmoid激活函数

print(x.size())
print(y.size())
''' >>>torch.Size([1, 3, 1]) >>>torch.Size([1, 10, 1]) '''

代码E:利用nn.Conv1d对点云集进行Layer计算

import torch
import torch.nn as nn
import torch.nn.functional as F

x = torch.randn(1, 3, 100)		            # 创建一个batch_size=1的点云,长度100
layer = nn.Conv1d(3, 10, kernel_size=1)	    # 构造一个输入节点为3,输出节点为10的网络层
y = F.sigmoid(layer(x))		                 # 计算y,sigmoid激活函数

print(x.size())
print(y.size())
''' >>>torch.Size([1, 3, 100]) >>>torch.Size([1, 10, 100]) '''

代码F:利用nn.Conv1d对多批次点云集进行Layer计算

import torch
import torch.nn as nn
import torch.nn.functional as F

x = torch.randn(4, 3, 100)		            # 创建一个batch_size=4的点云,长度100
layer = nn.Conv1d(3, 10, kernel_size=1)	    # 构造一个输入节点为3,输出节点为10的网络层
y = F.sigmoid(layer(x))		                 # 计算y,sigmoid激活函数

print(x.size())
print(y.size())
''' >>>torch.Size([4, 3, 100]) >>>torch.Size([4, 10, 100]) '''

通过上述代码可以看出,nn.Conv1d的输入数据格式只能一个三维tensor[batch, channel, length],与nn.Linear输入数据格式不同;并且,nn.Conv1d的数据作用位置也不同,nn.Conv1d作用在第二个维度channel

nn.Conv1d, kernel_size=1与nn.Linear不同

从上述方法1和方法2可以看出,两者可以实现同样结构的MLP计算,但计算形式不同,具体为:

  • nn.Conv1d输入的是一个[batch, channel, length]3维tensor,而nn.Linear输入的是一个[batch, *, in_features]可变形状tensor,在进行等价计算时务必保证nn.Linear输入tensor为三维
  • nn.Conv1d作用在第二个维度位置channelnn.Linear作用在第三个维度位置in_features,对于一个 X X X,若要在两者之间进行等价计算,需要进行tensor.permute重新排列维度轴秩序

代码G:验证nn.Conv1d, kernel_size=1nn.Linear计算结果相同,代码来自stack overflow

import torch

def count_parameters(model):
    """Count the number of parameters in a model."""
    return sum([p.numel() for p in model.parameters()])

conv = torch.nn.Conv1d(8,32,1)
print(count_parameters(conv))
# 288

linear = torch.nn.Linear(8,32)
print(count_parameters(linear))
# 288

print(conv.weight.shape)
# torch.Size([32, 8, 1])
print(linear.weight.shape)
# torch.Size([32, 8])

# use same initialization
linear.weight = torch.nn.Parameter(conv.weight.squeeze(2))
linear.bias = torch.nn.Parameter(conv.bias)

tensor = torch.randn(128,256,8)
permuted_tensor = tensor.permute(0,2,1).clone().contiguous()	# 注意此处进行了维度重新排列

out_linear = linear(tensor)
print(out_linear.mean())
# tensor(0.0067, grad_fn=<MeanBackward0>)

out_conv = conv(permuted_tensor)
print(out_conv.mean())
# tensor(0.0067, grad_fn=<MeanBackward0>)
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/126003.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • 用fread函数或fwrite函数读写的文件必须是_FCGI_fwrite

    用fread函数或fwrite函数读写的文件必须是_FCGI_fwritefwrite和fread是以记录为单位的I/O函数,fread和fwrite函数一般用于二进制文件的输入输出。#includesize_tfread(void*ptr,size_tsize,size_tnmemb,FILE*stream);size_tfwrite(constvoid*ptr,size_tsize,size_tnmemb,FILE*s

  • 《大话数据结构》目录[通俗易懂]

    《大话数据结构》目录[通俗易懂]由于目录中有比较复杂的格式,所以只能用PDF的形式提供。http://files.cnblogs.com/cj723/%E7%9B%AE%E5%BD%95.pdf

  • mybatiscodehelperpro激活码[最新免费获取]

    (mybatiscodehelperpro激活码)2021最新分享一个能用的的激活码出来,希望能帮到需要激活的朋友。目前这个是能用的,但是用的人多了之后也会失效,会不定时更新的,大家持续关注此网站~IntelliJ2021最新激活注册码,破解教程可免费永久激活,亲测有效,下面是详细链接哦~https://javaforall.cn/100143.html…

  • 新东方app直播课_新东方录播课和直播课

    新东方app直播课_新东方录播课和直播课峰值人数达到10.8万,直播商品数共125件,直播销量19.8万件,直播销售总额达到1534.3万元,相较首播成绩翻了近三倍。假设按照目前粉丝日增70-80万人,若衰减速率不高,则在未来2周东方甄选粉丝数量有望达到千万人。…

  • laravel 在nginx服务器上除了首页其余都是404的问题

    laravel 在nginx服务器上除了首页其余都是404的问题

  • 什么是莫兰指数

    什么是莫兰指数什么是莫兰指数?根据百度百科的定义是“空间自相关系数的一种,其值分布在[-1,1],用于判别空间是否存在自相关。”简单的说就是判定一定范围内的空间实体相互之间是否存在相关关系,比如:一座座居民楼它们是聚集在一块还是离散分布在各处。莫兰指数数值分布在[-1,1],[0,1]说明各地理实体之间存在正相关的关系,[-1,0]之间说明存在负相关的关系,而0值则无相关…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号