np&torch:sum运算符,高级索引,.detach()和.detach_()

python-numpy and torch

1. sum运算符

1.1 sum简介

按指定维求和, keepdim/keepdims(numpy中array数组只能用keepdims):保持维数不变
eg:形状为(a,b,c)的张量:
通过sum(0),指定第0维求和,shape变为(b,c)
通过sum(0, keepdim= True),shape变为(1,b,c)
通过sum(1),指定第0维求和,shape变为(a,c)
通过sum(1, keepdim= True),shape变为(a,1,c)

X = torch.arange(12)
X = X.reshape(2, 3, 2)
print(X)
# tensor([[[ 0,  1],
#          [ 2,  3],
#          [ 4,  5]],
# 
#         [[ 6,  7],
#          [ 8,  9],
#          [10, 11]]])
print(X.sum(0).shape, '\n', X.sum(0))
print(X.sum(0, keepdims=True).shape, '\n', X.sum(0, keepdims=True)) 
# torch.Size([3, 2])
#  tensor([[ 6,  8],                  [[ 0,  1]       [[ 6,  7]
#         [10, 12],            =      [ 2,  3]   +    [ 8,  9]
#         [14, 16]])                  [ 4,  5]]       [10, 11]]
# torch.Size([1, 3, 2]) 
#  tensor([[[ 6,  8],
#          [10, 12],
#          [14, 16]]])

1.2 简单实现softmax(sum运算符举例)

def Softmax(X)  # shape为(batch_size, num_class)
	X_e = torch.exp(X)
	Sum = X_e.sum(1, keepdim=True)  # 保持维度是为了后面做除法,shape为(batch_size, 1)
	return X_e / Sum  # 这里使用了广播机制(维数相同,shape不同:可以自动补齐,使得shape一样,然后做运算)

2.高级索引

2.1切片索引 :

‘ ,’ : 分开维度,从第0维开始,一刀一刀往下切

eg:

y = np.arange(24).reshape(2, 3, 4)
print(y)
# [[[ 0  1  2  3]
#   [ 4  5  6  7]
#   [ 8  9 10 11]]
# 
#  [[12 13 14 15]
#   [16 17 18 19]
#   [20 21 22 23]]]

(以下“第n个”就是index=n:)
y[0] :(0)第0维的第0个元素,shape为(3,4) : [[ 0 1 2 3],[ 4 5 6 7],[ 8 9 10 11]]

y[1, 0] : 第0维的第1个元素,第1维的第0个元素,shape为(4,): [12 13 14 15]

y[0,1,3]:第0维的第1个元素,第1维的第1个元素,第2维的第3个元素,shape为():7

y[:,1:3,0:4:2]:第0维的所有元素,第1维的第1个到第3个元素(不包括第3个),第2维的第0个到第4个元 素(不包括第4个,步长为2),shape为(2,2,2):[ [ [ 4 6],[ 8 10] ],[ [16 18],[20 22] ] ]

小结:索引从第0维开始,以 “ ,” 为分开维度的符号

2.2索引数组(整型):与切片不要混淆!!!

[注]:
切片是从第0维开始,依次执行下去,“一刀一刀切下去”
索引数组是每个维度同时选取(一一对应),进行索引

NumPy数组可以使用其他数组(或任何其他可以转换为数组的类似序列的对象,如列表,除元组之外的索引)

!!!每个维度数组对应的位置(可以用广播机制)做组合!!!:
y[np.array([0,1])] = y[ [0,1] ] : 约等于 [ y[0],y[1] ] ,结果为 y
y[ [0,1],2,[0,3] ]:用到了广播机制,约等于 [ y [0,2,0],y [ 1,2,3 ] ] ,结果为 [ 8,23 ]
y[ [0,1],[0,2 ] ]:约等于 [ y[0,0],y[1,2 ] ],结果为 [ [ 0 1 2 3],[20 21 22 23 ] ]

应用:
(1):依次找出矩阵的对角线值,对于3维数据,我们可以认为第一维是矩阵个数

y = np.arange(27).reshape(3, 3, 3)
print(y)
# [[[ 0  1  2]
#   [ 3  4  5]
#   [ 6  7  8]]
# 
#  [[ 9 10 11]
#   [12 13 14]
#   [15 16 17]]
# 
#  [[18 19 20]
#   [21 22 23]
#   [24 25 26]]]
print(y[range(len(y)), 1, 1])  # == y[[0, 1, 2], 1, 1]
# [ 4 13 22]
print(y[[range(len(y))], 1, 1])  # 或者 print(y[[range(y.shape[0])], 1, 1])
# [[ 4 13 22]]

(2):对于问题(1),依次取出第一个矩阵的第一行,第二个矩阵的第二行,第三个矩阵的第三行,并构成一个新的矩阵

new = y[range(len(y)), range(len(y))]
print(new)
# [[ 0  1  2]
#  [12 13 14]
#  [24 25 26]]

小结:索引数组可以和切片混合使用

2.3交叉熵损失的简单实现

y_true:[0,2,1],一共3个样本,第一个样本的类别为0,第二个样本的类别为2,第二个样本的类别为1

y_pre:[ [ 0.1, 0.2, 0.7],[0.35, 0.45, 0.2],[0.7, 0.2, 0.1 ] ],3个样本,第一个样本的预测概率为[ 0.1, 0.2, 0.7],类别为0的概率为0.1,类别为1的概率为0.2,类别为2的概率为0.7,

从y_pre的每个样本中取出正确类别的概率值!!!

print(y_pre[range(len(y_pre)), y_true])
# [0.1 0.2 0.2]
def Cross_Entropy(y_pre, y_true):
    y_ = y_pre[range(len(y_pre)), y_true]
    cross_entropy = -torch.log(y_)
    return cross_entropy

3. .detach()和.detach_()

Variable 中的两个方法:主要是为了从计算图中分离出来,即不参与梯度计算(backward)。在有些想要某些参数不变(不参与网络训练),我们就需要用到这两个方法。
eg1: .detach( )
经过detach()的变量, requires_grad会变成 false,即不追踪梯度,也就变成了常量。z = y * (w.detach()),可以认为只是将 w 变量中的tensor取出,作为常量参与运算,w本身是变量并没有改变。对于没有经过backward()反向传播的变量,grad=None,即w.grad=None。

def func():
    x = torch.ones(size=(2, 2), requires_grad=True)
    w = torch.ones(size=(2, 2), requires_grad=True)
    y = x ** 2
    print(w)
    print(w.detach())
    z = y * (w.detach())
    k = (z ** 2).sum()
    k.backward()
    print(w.grad)
    print(x.grad)
    print(w)    
# tensor([[1., 1.],
#         [1., 1.]], requires_grad=True)
# tensor([[1., 1.],
#         [1., 1.]])
# None
# tensor([[4., 4.],
#         [4., 4.]])
# tensor([[1., 1.],
#         [1., 1.]], requires_grad=True)

eg2: .detach_( )
注意, .detach_() 将会改变w本身,w的requires_grad变为了False。

def func():
    x = torch.ones(size=(2, 2), requires_grad=True)
    w = torch.ones(size=(2, 2), requires_grad=True)
    y = x ** 2
    print(w)
    print(w.detach_())
    print(w)
    z = y * w
    k = (z ** 2).sum()
    k.backward()
    print(w.grad)
    print(x.grad)
# tensor([[1., 1.],
#         [1., 1.]], requires_grad=True)
# tensor([[1., 1.],
#         [1., 1.]])
# tensor([[1., 1.],
#         [1., 1.]])
# None
# tensor([[4., 4.],
#         [4., 4.]])

在神经网络中,我们可以利用这两个方法对变量进行固定,即固定某些网络。
eg3: net中参数被固定,不参与训练(利用 .named_parameters() 可以利用名字灵活的固定指定网络 / 操作)

def eg3():
    net = nn.Sequential(nn.Conv2d(3, 1, 1, 1, 1), nn.ReLU())
    Net = nn.Sequential(nn.Conv2d(3, 1, 3, 1, 1), nn.ReLU(), nn.MaxPool2d(3), net, nn.MaxPool2d(3))
    # 固定 net
    for i in net.parameters():
        print(i)
        i.detach_()
        print(i)
# Parameter containing:
# tensor([[[[-0.5519]],
# 
#          [[ 0.5120]],
# 
#          [[ 0.3357]]]], requires_grad=True)
# Parameter containing:
# tensor([[[[-0.5519]],
# 
#          [[ 0.5120]],
# 
#          [[ 0.3357]]]])
# Parameter containing:
# tensor([-0.5076], requires_grad=True)
# Parameter containing:
# tensor([-0.5076])

注:
.detach(): 从变量分离出的 tensor 变化,原变量中的 tensor 也会跟着变化,说明都是同一块数据,只是将变量从计算图中分离出来, detach_()同理。

vector = torch.tensor([1., 2., 3.], requires_grad=True)
a = vector.detach()
print(a)
print(vector)
a += 1  # 注: a = a + 1 和 a += 1 的区别!(不懂评论区问,我再答!)
print(a)
print(vector)
# tensor([1., 2., 3.])
# tensor([1., 2., 3.], requires_grad=True)
# tensor([2., 3., 4.])
# tensor([2., 3., 4.], requires_grad=True)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值