代码+实例:深度学习中的“轴”全解

在深度学习中,轴,指的就是张量的层级,一般通过参数 axis/dim 来设定。很多张量的运算、神经网络的构建,都会涉及到轴,但到底取哪个轴,却不是那么容易把握。

下面会针对轴/axis/dim,基于 PyTorch 的代码和实例,尝试去理清张量运算中轴/axis/dim 的设定。

 

轴的概念

对于一个张量,它的 shape 有几维,就对应有几个轴,也就对应着张量的层级,最直观的可以通过看最前面的方括号数量来判断。

import torch
a = torch.Tensor([[1,2,3], [4,5,6]])
b = torch.Tensor([[7,8,9], [10,11,12]])
c = torch.Tensor([[[1,2,3], [4,5,6]], [[7,8,9], [10,11,12]]])
print(a.shape)

# torch.Size([2, 3])

上面的张量 a 和 b,都对应两个轴。axis/dim=0 对应 shape [2, 3] 中的 2,axis/dim=1 对应 shape [2, 3] 中的 3,而张量 c 有三个轴。

张量运算时对轴参数的设定非常常见,在 Numpy 中一般是参数 axis,在 Pytorch 中一般是参数 dim,但它们含义是一样的。

 

轴的使用

在做张量的拼接操作时,axis/dim 设定了哪个轴,那对应的轴在拼接之后张量数会发生变化

>> torch.cat((a,b), dim=0)
tensor([[ 1.,  2.,  3.],
        [ 4.,  5.,  6.],
        [ 7.,  8.,  9.],
        [10., 11., 12.]])

>> torch.cat((a,b), dim=1)
tensor([[ 1.,  2.,  3.,  7.,  8.,  9.],
        [ 4.,  5.,  6., 10., 11., 12.]])

对于上面 torch 中的 cat 操作,当设定 dim=0 时,两个维度是 (2,3) 的张量合并成了一个 (4,3) 的张量,在第 0 维,张量数从 2 变成了 4,第 1 维没有变化;当设定 dim=1 时,在第 1 维,张量数从 3 变成了 6,第 0 维没有变化。

在做张量的运算操作时,axis/dim 设定了哪个轴,就会遍历这个轴去做运算,其他轴顺序不变。

>> torch.softmax(a, dim=0)
tensor([[0.0474, 0.0474, 0.0474],
        [0.9526, 0.9526, 0.9526]])

>> torch.softmax(a, dim=1)
tensor([[0.0900, 0.2447, 0.6652],
        [0.0900, 0.2447, 0.6652]])

对于上面 torch 中的 softmax 操作,当设定 dim=0 时,就是其他轴不变,单次遍历 dim=0 轴的所有元素去做运算,上例中就相当于分别取了张量 a 中的第 0 列、第 1 列、第 2 列去做计算。

换一个角度,假设用 for 循环去遍历一个张量,那运算中设定的 dim 就是被放在最内层的 for 循环,其它的轴保持正常的顺序。

可以用下面的例子作为验证,这里 tensor  c  的 shape 是 (m,n,p),用 for 循环去计算 torch.softmax(c, dim=1) 。

# for循环计算方式
c = torch.Tensor([[[1,2,3], [4,5,6]], [[7,8,9], [10,11,12]]])    # shape (2,2,3)
m,n,p = c.shape
res = torch.zeros((m,n,p))
for i in range(m):
    for j in range(p):
        res[i,:,j] = torch.softmax(torch.tensor([c[i,k,j] for k in range(n)]), dim=0)  #这里对应最内层的for循环

# 库函数设定轴计算方式
res1 = torch.softmax(c, dim=1)
print(res.equal(res1))        # True

尝试numpy版本的维度拼接:

轴0拼接

>>> import numpy as np
>>> a = np.array([[1,2,3], [4,5,6]])
>>> b = np.array([[7,8,9], [10,11,12]])
>>> c = np.array([[[1,2,3], [4,5,6]], [[7,8,9], [10,11,12]]])
>>> cat_dim_0 = np.concatenate([a,b],axis=0)
>>> cat_dim_0
array([[ 1,  2,  3],
       [ 4,  5,  6],
       [ 7,  8,  9],
       [10, 11, 12]])
>>> m,n = a.shape
>>> m,n
(2, 3)
>>> res = np.zeros((2*m,n))

>>> for i in range(n):
    res[:,i] = np.concatenate([a[:,i],b[:,i]],axis=0)    
>>> res
array([[ 1.,  2.,  3.],
       [ 4.,  5.,  6.],
       [ 7.,  8.,  9.],
       [10., 11., 12.]])

轴1拼接:

>>> res_axis_1 = np.zeros((m,2*n))
>>> for j in range(m):
    res_axis_1[j,:] = np.concatenate([a[j,:],b[j,:]],axis=0) 
>>> res_axis_1
array([[ 1.,  2.,  3.,  7.,  8.,  9.],
       [ 4.,  5.,  6., 10., 11., 12.]])

axis/dim 使用小总结:

  1. 在做张量的拼接操作时,axis/dim 设定了哪个轴,那对应的轴在拼接之后樟树会发生变化axis/dim 设定了哪个轴,那对应的轴在拼接之后的张量数会发生变化

  2. 在做张量的运算操作时,axis/dim 设定了哪个轴,就会遍历这个轴去做运算,其他轴顺序不变

实际上,第一条拼接操作也可以用第二条去理解,但拼接的轴张量数会发生变化更好理解和记忆。

 

轴的实例

其实一个轴设定的变化,会带来很大的差异,最典型的就是 BatchNorm 和 LayerNorm 了。

BatchNorm 和 LayerNorm 是针对数据的不同轴去做 norm,假设输入数据的维度是(N,C,H,W),分别对应 batch 数,核数,高,宽,BatchNorm 就对应 dim=0,LayerNorm 就对应 dim=1,在不考虑移动平均等具体细节问题时,两者在形式上可以统一,只有一个 dim 参数的差别。

Pytorch 的实现(简化版)如下:

class Norm(nn.Module):
    def __init__(self, num_features, variance_epsilon=1e-12):
        super(Norm, self).__init__()
        self.gamma = nn.Parameter(torch.ones(num_features))
        self.beta = nn.Parameter(torch.zeros(num_features))
        self.variance_epsilon = variance_epsilon    # 一个很小的常数,防止除0

    def forward(self, x, dim):
        u = x.mean(dim, keepdim=True)
        s = (x - u).pow(2).mean(dim, keepdim=True)
        x_norm = (x - u) / torch.sqrt(s + self.variance_epsilon)
        return self.gamma * x_norm + self.beta

当然,不仅仅是在深度学习里面,在 Numpy,Pandas中,轴的设定都经常会遇到,但它们都是相通的,希望本文能帮你更好的理解它 —> 轴。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值