pytorch常用操作和方法

  这篇博客主要记录pytorch的常用操作,因此会持续更新。。。。。。

0 概要和目录


  关于tensor,我们可以理解为是一个高纬度的数据,嗯,其实这个不影响我们的使用啦。

基础

1. 常用数据类型和转换
2. torch.cat,torch.stack
3. torch.sum,torch.mean
4. squeeze,unsqueeze
5. permute 重排序
6. tensor的数值

进阶

7. cuda和device
8. requires_grad
9. pack_padded_sequence和pad_packed_sequence
10. torch.gather
11. torch.mm,torch.bmm,torch.matmul


1 常用数据类型和转换


torch的常用数据类型有:torch.IntTensor、 torch.FloatTensor、 torch.LongTensor

torch.Tensor是默认的tensor类型默认的是 torch.FloatTensor。

我们来创建一个:

a = np.array([2, 2])
tensor = torch.from_numpy(a)
output:
tensor([2, 2], dtype=torch.int32)

  接着我们来转变他的数据类型:(举一个float其他同理)

tensor=tensor.float()
print(tensor,type(tensor))
output:
tensor([2., 2.]) <class 'torch.Tensor'>

  在介绍下面的操作之前,先说明一点,下面的所有的 dim 设置不做特殊说明时,都表示:0 第一个维度,1第二个维度,2第三个维度。依次类推。


2 torch.cat() , torch.stack()


  我们随机创建两个tensor进行合并,这里需要注意 torch.cat() 和 torch.stack() 的区别:

torch.cat()

a=torch.rand(2,3)
b=torch.randn(2,3)
print(a)
print(b)
c=torch.cat((a,b),0)
print(c)
print(c.size())
output:
tensor([[0.5070, 0.2374, 0.2489],
        [0.7007, 0.2080, 0.4985]])
tensor([[ 0.1958, -0.0674, -0.7950],
        [-1.1569, -0.8597,  0.8683]])
tensor([[ 0.5070,  0.2374,  0.2489],
        [ 0.7007,  0.2080,  0.4985],
        [ 0.1958, -0.0674, -0.7950],
        [-1.1569, -0.8597,  0.8683]])
torch.Size([4, 3])

torch.stack()

d=torch.stack((a,b), dim=0)
print(d)
print(d.size())
output:
tensor([[[ 0.7555,  0.1871,  0.2619],
         [ 0.5023,  0.4412,  0.7843]],

        [[-1.0808,  0.0563, -0.2942],
         [ 0.2736,  0.7614,  1.7735]]])
torch.Size([2, 2, 3])

3 torch.sum(),torch.mean()


a=torch.rand(2,3)
print(a)
print(torch.sum(a,dim=0))
c=torch.mean(a,0)
print(c)
output:
tensor([[0.1001, 0.9999, 0.9990],
        [0.2909, 0.8446, 0.6064]])
tensor([0.3910, 1.8445, 1.6054])
tensor([0.1955, 0.9223, 0.8027])

4 squeeze,unsqueeze


  unsqueeze是增加一个维度,squeeze是去除维度1的维度

a=torch.rand(2,3)
print(a)
a=a.unsqueeze(1)
print(a.size())
print(a.squeeze().size())
output:
tensor([[0.8247, 0.2986, 0.0938],
        [0.4890, 0.7971, 0.5439]])
torch.Size([2, 1, 3])
torch.Size([2, 3])

5 permute 重排序


  这里主要用于调整数据的维度的顺序,例如在lstm中的batch_first为true时对应(batch,seq_len,dim),当batch_first为false时候对应的为(seq_len,batch,dim)

a = torch.rand(2, 3, 4)
print(a)
a = a.permute(2, 1, 0)
print(a)
output:
tensor([[[0.1108, 0.4491, 0.9403, 0.9222],
         [0.8865, 0.5225, 0.0332, 0.9596],
         [0.8090, 0.7899, 0.1709, 0.8409]],

        [[0.2927, 0.4769, 0.1748, 0.0586],
         [0.0196, 0.0977, 0.3020, 0.9260],
         [0.1923, 0.9067, 0.0709, 0.7505]]]) torch.Size([2, 3, 4])
tensor([[[0.1108, 0.2927],
         [0.8865, 0.0196],
         [0.8090, 0.1923]],

        [[0.4491, 0.4769],
         [0.5225, 0.0977],
         [0.7899, 0.9067]],

        [[0.9403, 0.1748],
         [0.0332, 0.3020],
         [0.1709, 0.0709]],

        [[0.9222, 0.0586],
         [0.9596, 0.9260],
         [0.8409, 0.7505]]]) torch.Size([4, 3, 2])

6 tensor的数值


  在获取tensor的数值的时候,当只有一个元素时候使用 tensor.item(),否则使用tensor.tolist()

a = torch.randn(1,requires_grad=True,device='cuda')
print(a.item())
b=torch.randn(2,3,requires_grad=True,device='cuda')
print(b.tolist())
output:
1.2667691707611084
[[-0.7672975063323975, 0.1947876662015915, -0.5312148332595825], [-1.6979146003723145, -0.21547940373420715, -1.6267175674438477]]

7 cuda和device


  这里主要有两种不同的用法,一种是使用to指定设备另外一种是通过cuda来转换。第一种方法通过在config中判断设备之后可以直接调用,第二种就需要每次都判断一下。

device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")
a = torch.rand([2,3]).to(device)

if torch.cuda.is_available():
    a = torch.rand([2,3]).cuda()
  • tensor.to() and model.to()这里简单举例说明两者的区别,官方文档的说法是tensor.to()不是一个 in_place操作;而model.to()是一个in_place操作。
a = torch.rand(10)
b = a.to(torch.device("cuda"))
print(b is a)
c = b.to(torch.device("cuda"))
print(c is b)
output:
False
True

都在cuda上的时候,还是一样的,对于model.to()如下:

model = torch.nn.Sequential(torch.nn.Linear(10, 10))
model_new = model.to(torch.device("cuda"))
print(model_new is model)
output:
True

  还有一个问题就是tensor在设备之间进行转换的时候需要注意,当requires_grad=True的时候需要加上detach(),这样子更安全。

x  = torch.rand([3,3], device='cuda')
x_ = x.cpu().numpy()

x= torch.rand([3,3], requires_grad=True, device='cuda')
x = x.cpu().detach().numpy()
  • tensor.data and tensor.detach()
  • tensor.data 不会被自动求导系统跟踪会导致隐藏错误;而tensor.detach()会被跟踪,这样就会报错提示。这里采用参考文献的一个例子说明问题:
a = torch.tensor([7., 0, 0], requires_grad=True)
b = a + 2
print(b)
loss = torch.mean(b * b)
b_ = b.detach()
b_.zero_()
print(b)
# 修改 b_ , b 的值也变了
loss.backward()
output:
tensor([9., 2., 2.], grad_fn=<AddBackward0>)
tensor([0., 0., 0.], grad_fn=<AddBackward0>)
RuntimeError: one of the variables needed for gradient computation has been modified by an inplace operation:
  • tensor.data
a = torch.tensor([7., 0, 0], requires_grad=True)
b = a + 2
print(b)
# tensor([9., 2., 2.], grad_fn=)
loss = torch.mean(b * b)
b_ = b.data
b_.zero_()
print(b)
# tensor([0., 0., 0.], grad_fn=)
loss.backward()
print(a.grad)
tensor([0., 0., 0.])
# 其实正确的结果(将上面的b_.zero_()频掉)应该是:
# tensor([6.0000, 1.3333, 1.3333])

8 requires_grad

  这里想说的是关于模型的中参数的梯度问题,在网络结构中,经常会让一部分参数不更新,一部分更新,具体做法如下:

    def __init__(self, emb_weights, vocab_size, dim, config):
        super(lstmnet, self).__init__()

        self.config = config

        # embedding and LSTM layers
        self.embedding = nn.Embedding.from_pretrained(embeddings=emb_weights, freeze=config.emb_freeze)

        self.lstm = nn.LSTM(input_size=config.input_size,
                            hidden_size=config.hidden_size,
                            num_layers=config.num_layers,
                            batch_first=config.batch_first,
                            bidirectional=config.bidir)
        for p in self.parameters():
            p.requires_grad=False

        # dropout layer
        self.dropout = nn.Dropout(config.dropout)

        if config.bidir:
            self.l1 = nn.Linear(config.hidden_size * 2, config.num_classes)

        else:
            self.l1 = nn.Linear(config.hidden_size, config.num_classes)

  在上面的这段代码中通过对参数的遍历使得这个(p.requires_grad=False)代码之前的网络参数的requires_grad设置成false,之后的还是true。接着我们在使用的时候进行过滤:

    if config.emb_freeze:
        model_parameters = filter(lambda p: p.requires_grad, model.parameters())
    else:
        model_parameters = model.parameters()

    optimzier = torch.optim.Adam(model_parameters, lr=config.lr, weight_decay=config.weight_decay)

怎么知道是否成功呢?我们来test一下,test代码如下:

  for p in model.parameters():
        print(p.shape,p.requires_grad)
    print("-------------------------------")

    if config.emb_freeze:
        model_parameters = filter(lambda p: p.requires_grad, model.parameters())
    else:
        model_parameters = model.parameters()
    for p in model_parameters:
        print(p.shape)
    exit()
output:
torch.Size([64, 50]) False
torch.Size([400, 50]) False
torch.Size([400, 100]) False
torch.Size([400]) False
torch.Size([400]) False
torch.Size([400, 50]) False
torch.Size([400, 100]) False
torch.Size([400]) False
torch.Size([400]) False
torch.Size([2, 200]) True
torch.Size([2]) True
-------------------------------
torch.Size([2, 200])
torch.Size([2])

很明显还是有效果的啊。
另外,关于pytorch如何一步一步更新梯度,大家可以看一下: PyTorch 中的 tensor 及使用

9 pack_padded_sequence和pad_packed_sequence


pack_padded_sequence是对序列进行压缩,pad_packed_sequence是解压缩,这里先不解释,先看代码:

import torch.nn as nn
import torch
t=[]
l=[[1,3,4],[2,3,5],[5,4,3],[4,0,1]]
l1=[[1,3,4],[2,3,5],[5,4,3],[0,0,0]]
l2=[[1,3,4],[2,3,5],[0,0,0],[0,0,0]]
t.append(l)
t.append(l1)
t.append(l2)
yy=torch.FloatTensor(t)
print(yy)
k=[4,3,2]
x_packed = nn.utils.rnn.pack_padded_sequence(input=yy, lengths=k, batch_first=True)
print(x_packed)
lstm=nn.LSTM(input_size=3,hidden_size=10,num_layers=1,batch_first=True)
out,_=lstm(x_packed)
print(out)
c,_=torch.nn.utils.rnn.pad_packed_sequence(out,batch_first=True)
print(c.size())
output:
tensor([[[1., 3., 4.],
         [2., 3., 5.],
         [5., 4., 3.],
         [4., 0., 1.]],

        [[1., 3., 4.],
         [2., 3., 5.],
         [5., 4., 3.],
         [0., 0., 0.]],

        [[1., 3., 4.],
         [2., 3., 5.],
         [0., 0., 0.],
         [0., 0., 0.]]])
PackedSequence(data=tensor([[1., 3., 4.],
        [1., 3., 4.],
        [1., 3., 4.],
        [2., 3., 5.],
        [2., 3., 5.],
        [2., 3., 5.],
        [5., 4., 3.],
        [5., 4., 3.],
        [4., 0., 1.]]), batch_sizes=tensor([3, 3, 2, 1]), sorted_indices=None, unsorted_indices=None)
-------
PackedSequence(data=tensor([[ 0.0076,  0.0838, -0.1635,  0.1357, -0.1519,  0.0391, -0.0421, -0.0023,
          0.0880, -0.0821],
        [ 0.0076,  0.0838, -0.1635,  0.1357, -0.1519,  0.0391, -0.0421, -0.0023,
          0.0880, -0.0821],
        [ 0.0076,  0.0838, -0.1635,  0.1357, -0.1519,  0.0391, -0.0421, -0.0023,
          0.0880, -0.0821],
        [ 0.0089,  0.0943, -0.2057,  0.2117, -0.3167,  0.0308,  0.0038,  0.0939,
          0.2045, -0.1033],
        [ 0.0089,  0.0943, -0.2057,  0.2117, -0.3167,  0.0308,  0.0038,  0.0939,
          0.2045, -0.1033],
        [ 0.0089,  0.0943, -0.2057,  0.2117, -0.3167,  0.0308,  0.0038,  0.0939,
          0.2045, -0.1033],
        [ 0.0143,  0.0322,  0.0555,  0.0909, -0.5819,  0.1404,  0.1093,  0.1695,
          0.4254, -0.1481],
        [ 0.0143,  0.0322,  0.0555,  0.0909, -0.5819,  0.1404,  0.1093,  0.1695,
          0.4254, -0.1481],
        [ 0.0508,  0.0634,  0.0852,  0.2010, -0.5225,  0.1044,  0.3157,  0.0988,
          0.3785, -0.2072]], grad_fn=<CatBackward>), batch_sizes=tensor([3, 3, 2, 1]), sorted_indices=None, unsorted_indices=None)
torch.Size([3, 4, 10])

上面代码中是生成一个 3 * 4 * 3的tensor 其中0是pad;压缩之后其实就是统计每个batch不为0的行数,仔细看一下3,3,2,1.就是这么来的,也就是把pad去掉了,也就相当于告诉LSTM需要执行多少timestep。


10 torch.gather


这个东西乍一看我也没整明白,后来看来官网的文档。其实只需要严格的套公式就可以了。

  • For a 3-D tensor the output is specified by::
  • out[i][j][k] = input[index[i][j][k]][j][k] # if dim == 0
  • out[i][j][k] = input[i][index[i][j][k]][k] # if dim == 1
  • out[i][j][k] = input[i][j][index[i][j][k]] # if dim == 2
import torch
b = torch.Tensor([[1,2,3],[4,5,6]])
print (b)
index_1 = torch.LongTensor([[0,1],[2,0]])
index_2 = torch.LongTensor([[0,1,1],[0,0,0]])
print (torch.gather(b, dim=1, index=index_1))
print (torch.gather(b, dim=0, index=index_2))
output:
tensor([[1., 2., 3.],
        [4., 5., 6.]])
tensor([[1., 2.],
        [6., 4.]])
tensor([[1., 5., 6.],
        [1., 2., 3.]])

严格套公式,就可以得到上面的结果。
这里推荐一个博客讲解 pytorch实现seq2seq时如何对loss进行mask 讲解的很好,看完这个应该会更好的理解这个算子的使用。


11 torch.mm,torch.bmm,torch.matmul


  • torch.mm
l=[[1,2],[3,3]]
l1=[[2,2],[2,2],[1,1]]
a=torch.tensor(l1)
b=torch.tensor(l)
print(a)
print(b)
c=torch.mm(a,b)
print(c)
output:
tensor([[2, 2],
        [2, 2],
        [1, 1]])
tensor([[1, 2],
        [3, 3]])
tensor([[ 8, 10],
        [ 8, 10],
        [ 4,  5]])

可以看出来就是简单的矩阵相乘。

  • torch.bmm
a = torch.tensor([[[2., 3.], [1., 2.]], [[3., 4.], [0., 5.]]])
b = torch.tensor([[[3.], [1.]], [[2.], [4.]]])
print(a)
print(b)
out = torch.bmm(a, b)
print(out)
output:
tensor([[[2., 3.],
         [1., 2.]],

        [[3., 4.],
         [0., 5.]]])
tensor([[[3.],
         [1.]],

        [[2.],
         [4.]]])
tensor([[[ 9.],
         [ 5.]],

        [[22.],
         [20.]]])
  • torch.matmul
import torch
l=[[1,9],[2,2]]
l1=[1,3]
a=torch.tensor(l1)
b=torch.tensor(l)
print(a)
print(b)
c=torch.matmul(a,b)
print(c)
output:
tensor([1, 3])
tensor([[1, 9],
        [2, 2]])
tensor([ 7, 15])

这里是1 * 1+3 * 2=7       1 * 9+3 * 2=15
反过来第一个是2维,第二个是1维。

import torch
l=[[1,9],[2,2]]
l1=[1,3]
a=torch.tensor(l)
b=torch.tensor(l1)
print(a)
print(b)
c=torch.matmul(a,b)
print(c)
output:
tensor([[1, 9],
        [2, 2]])
tensor([1, 3])
tensor([28,  8])

这里是1 * 1+3 * 9 =28       2 * 1 + 2 * 3=8
若都是2维则是矩阵相乘和mm相同。

参考文献


https://blog.csdn.net/byron123456sfsfsfa/article/details/90609758

你可能感兴趣的:(deep,learning)