这篇博客主要记录pytorch的常用操作,因此会持续更新。。。。。。
关于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
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第三个维度。依次类推。
我们随机创建两个tensor进行合并,这里需要注意 torch.cat() 和 torch.stack() 的区别:
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])
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])
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])
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])
这里主要用于调整数据的维度的顺序,例如在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])
在获取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]]
这里主要有两种不同的用法,一种是使用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()
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()
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:
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])
这里想说的是关于模型的中参数的梯度问题,在网络结构中,经常会让一部分参数不更新,一部分更新,具体做法如下:
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 及使用
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。
这个东西乍一看我也没整明白,后来看来官网的文档。其实只需要严格的套公式就可以了。
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 讲解的很好,看完这个应该会更好的理解这个算子的使用。
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]])
可以看出来就是简单的矩阵相乘。
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.]]])
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