Pytorch总结一
在PyTorch中, torch.Tensor 是存储和变换数据的主要⼯具。如果你之前⽤过NumPy,你会发现Tensor 和NumPy的多维数组⾮常类似。然⽽, Tensor 提供GPU计算和⾃动求梯度等更多功能,这些使 Tensor 更加适合深度学习。
1.数据操作
导入包
import torch
#1.数据操作
x=torch.empty(5,3) #创建5*3的未初始化tensor
print(x)
# tensor([[8.9082e-39, 1.0194e-38, 9.1837e-39],
# [4.6837e-39, 9.2755e-39, 1.0837e-38],
# [8.4490e-39, 1.0194e-38, 1.0194e-38],
# [4.8674e-39, 9.9184e-39, 9.0000e-39],
# [1.0561e-38, 1.0653e-38, 4.1327e-39]])
x=torch.rand(5,3) #随机初始化
print(x)
# tensor([[0.6869, 0.1009, 0.4414],
# [0.9740, 0.1829, 0.2010],
# [0.0281, 0.9740, 0.6624],
# [0.0604, 0.0146, 0.1782],
# [0.7365, 0.4841, 0.1844]])
x=torch.zeros(5,3,dtype=torch.long) #long全零tensor
print(x)
# tensor([[0, 0, 0],
# [0, 0, 0],
# [0, 0, 0],
# [0, 0, 0],
# [0, 0, 0]])
x=torch.tensor([5.5,3]) #直接数据创建
print(x)
#tensor([5.5000, 3.0000])
#还可以通过现有的 Tensor 来创建,此⽅法会默认᯿⽤输⼊ Tensor 的⼀些属性,
#例如数据类型,除⾮⾃定义数据类型。
x=x.new_ones(5,3,dtype=torch.float64)
print(x)
# tensor([[1., 1., 1.],
# [1., 1., 1.],
# [1., 1., 1.],
# [1., 1., 1.],
# [1., 1., 1.]], dtype=torch.float64)
x=torch.randn_like(x,dtype=torch.float) #指定新的数据类型
print(x)
# tensor([[-0.5007, -1.4208, 0.0125],
# [-1.2975, 1.5053, -0.2303],
# [-0.0361, 0.2742, 1.1563],
# [-0.2017, -1.2620, 1.8696],
# [ 0.0733, 1.0655, -1.0969]])
#通过 shape 或者 size() 来获取 Tensor 的形状:
print(x.size())
print(x.shape)
# torch.Size([5, 3])
# torch.Size([5, 3])
#others:
# ones(*size) 全1tensor
# eyes(*size) 对角线为1,其他为0
# arange(s,e,step) 从s 到e,步长为step
# linspace(s,e,steps) 从s到e,均匀切分成steps份
# rand/randn(*sizes) 均匀/标准分布
# normal(mean,std)/uniform(from,to) 正态分布/均匀分布
# randperm(m) 随机排列
2. 算术操作
#2.算术操作
#加法:以下形式结果相同
y=torch.rand(5,3)
y1=torch.add(x,y)
y2=x+y
y.add_(x)
y3=y
print(y1)
print(y2)
print(y3)
#均输出:
# tensor([[ 1.4572, 1.1814, 1.0476],
# [ 0.2844, -0.0457, -0.7777],
# [ 0.5250, 1.4636, -0.6049],
# [ 1.2933, -0.8186, 1.4611],
# [-1.0812, 0.7444, 0.5850]])
3. 索引
#3.索引 索引出来的结果与原数据共享内存,也即修改⼀个,另⼀个会跟着修改。
y=x[0,:]
print(y)
#tensor([ 0.2176, -0.4934, 0.0061])
y+=1
print(y)
print(x[0,:])
# tensor([1.4410, 1.5247, 1.0603])
# tensor([1.4410, 1.5247, 1.0603]) #源tensor也更改了
#others:
# torch.index_select(input,dim,index) 在指定维度dim上选取,比如某些行,某些列
# torch.masked_select(input,mask) a(a>0),使用Bytetensor进行选取
# torch.non_zero(input) 非0元素的下标
# torch.gather(input,dim,index) 根据index,在dim维度上选取数据,输出的size跟index一样
4. 改变形状
#4.改变形状,
y=x.view(15)
z=x.view(-1,5) #-1所指的维度可以根据其他维度的值推出来
print(x.size(),y.size(),z.size())
#torch.Size([5, 3]) torch.Size([15]) torch.Size([3, 5])
#注意 view() 返回的新tensor与源tensor共享内存(其实是同⼀个tensor),即一个变两个都变
x_cp=x.clone().view(15)
x-=1
print(x)
print(x_cp)
# tensor([[ 0.6842, 1.8774, -1.4890],
# [-2.3937, -0.7941, -1.1918],
# [-2.4145, -0.8577, -1.0604],
# [-1.4423, 0.2233, -1.1764],
# [-0.4029, -1.5999, -0.7380]])
# tensor([ 1.6842, 2.8774, -0.4890, -1.3937, 0.2059, -0.1918, -1.4145, 0.1423,
# -0.0604, -0.4423, 1.2233, -0.1764, 0.5971, -0.5999, 0.2620])
#item() , 它可以将⼀个标量 Tensor 转换成⼀个Python number:
x=torch.randn(1)
print(x)
print(x.item())
# tensor([-1.6042])
# -1.6041609048843384
5.线性代数
#5.线性代数
# torch.trace() 对角线元素之和,矩阵的迹
# torch.diag() 对角线元素
# torch.triu()/tril() 矩阵的上三角/下三角,可指定偏移量
# torch.mm()/torch.bmm() 矩阵乘法,batch的矩阵乘法
# torch.addmm()/addbmm/addmv/addr/badbmm 矩阵运算
# torch.t() 转置
# torch.dot()/torch.cross() 内积/外积
# torch.inverse() 求逆矩阵
# torch.svd() 奇异值分解
6. 广播机制
#6.广播机制 当对两个形状不同的 Tensor 按元素运算时,可能会触发⼴播(broadcasting)机制:
# 先适当复制元素使这两个 Tensor 形状相同后再按元素运算。
x=torch.arange(1,3).view(1,2)
print(x)
#tensor([[1, 2]])
y=torch.arange(1,4).view(3,1)
print(y)
print(x+y)
# tensor([[1],
# [2],
# [3]])
# tensor([[2, 3],
# [3, 4],
# [4, 5]])
7. 运算的内存开销
#7.运算的内存开销
x = torch.tensor([1, 2])
y = torch.tensor([3, 4])
id_before = id(y)
y = y + x #开辟新内存
print(id(y) == id_before) # False
x = torch.tensor([1, 2])
y = torch.tensor([3, 4])
id_before = id(y)
y[:] = y + x #不开辟新内存
print(id(y) == id_before) # True
#the same as:
x = torch.tensor([1, 2])
y = torch.tensor([3, 4])
id_before = id(y)
torch.add(x, y, out=y) # y += x, y.add_(x) #规定输出
print(id(y) == id_before) # True
8. 数据类型转换
#8.数据类型转换
tensor = torch.Tensor(3, 5)
print(tensor.type()) #torch.FloatTensor
# torch.long() 将tensor投射为long类型
newtensor = tensor.long()
print(newtensor.type()) #torch.LongTensor
#others:
newtensor = tensor.half()
newtensor = tensor.int()
newtensor = tensor.double()
newtensor = tensor.float()
newtensor = tensor.char()
newtensor = tensor.byte()
newtensor = tensor.short()
#8.1 tensor转numpy
a = torch.ones(5)
b = a.numpy()
print(a, b)
#tensor([1., 1., 1., 1., 1.]) [1. 1. 1. 1. 1.]
a += 1
print(a, b)
#tensor([2., 2., 2., 2., 2.]) [2. 2. 2. 2. 2.]
b += 1
print(a, b)
# tensor([3., 3., 3., 3., 3.]) [3. 3. 3. 3. 3.]
#8.2 numpy转tensor 使⽤ from_numpy() 将NumPy数组转换成 Tensor :
import numpy as np
a=np.ones(5)
b=torch.from_numpy(a)
print(a,b)
# [1. 1. 1. 1. 1.] tensor([1., 1., 1., 1., 1.], dtype=torch.float64)
a+=1
print(a,b)
#[2. 2. 2. 2. 2.] tensor([2., 2., 2., 2., 2.], dtype=torch.float64)
9. tersor on GPU
#9. tersor on GPU ⽤⽅法 to() 可以将 Tensor 在CPU和GPU(需要硬件⽀持)之间相互移动
# 以下代码只有在PyTorch GPU版本上才会执⾏
if torch.cuda.is_available():
device = torch.device("cuda") # GPU
y = torch.ones_like(x, device=device) # 直接创建⼀个在GPU上的Tensor
x = x.to(device) # 等价于 .to("cuda")
z = x + y
print(z)
print(z.to("cpu", torch.double)) # to()还可以同时更改数据类型
# tensor([2, 3], device='cuda:0')
# tensor([2., 3.], dtype=torch.float64)
10. 自动求梯度
#10. 自动求梯度
x = torch.ones(2, 2, requires_grad=True) #追踪在其上的所有操作
print(x)
print(x.grad_fn)
# tensor([[1., 1.],
# [1., 1.]], requires_grad=True)
# None
y=x+2
print(y)
print(y.grad_fn)
# tensor([[3., 3.],
# [3., 3.]], grad_fn=<AddBackward0>)
# <AddBackward0 object at 0x0000020A1B6770A0> #显示进行了加法操作
#像x这种直接创建的称为叶⼦节点,叶⼦节点对应的 grad_fn 是 None 。
print(x.is_leaf,y.is_leaf) #True False
z = y * y * 3
out = z.mean()
print(z, out)
# tensor([[27., 27.],
# [27., 27.]], grad_fn=<MulBackward0>) tensor(27., grad_fn=<MeanBackward0>)
#通过 .requires_grad_() 来⽤in-place的⽅式改变 requires_grad 属性
a = torch.randn(2, 2) # 缺失情况下默认 requires_grad = False
a = ((a * 3) / (a - 1))
print(a.requires_grad) # False
a.requires_grad_(True)
print(a.requires_grad) # True
b = (a * a).sum()
print(b.grad_fn)
# <SumBackward0 object at 0x000002513A0B70A0>
10.1. 梯度
out.backward() # 等价于 out.backward(torch.tensor(1.))
print(x.grad) #out关于x的梯度 d(out)/dx
# tensor([[4.5000, 4.5000],
# [4.5000, 4.5000]])
# 再来反向传播⼀次,注意grad是累加的
out2 = x.sum()
out2.backward()
print(x.grad)
# tensor([[5.5000, 5.5000],
# [5.5000, 5.5000]])
out3 = x.sum()
x.grad.data.zero_()
out3.backward()
print(x.grad)
# tensor([[1., 1.],
# [1., 1.]])
# 不允许张量对张量求导,只允许标量对张量求导,求导结果是和⾃变量同形的张量
x = torch.tensor([1.0, 2.0, 3.0, 4.0], requires_grad=True)
y = 2 * x
z = y.view(2, 2)
print(z)
# tensor([[2., 4.],
# [6., 8.]], grad_fn=<ViewBackward>)
#现在 y 不是⼀个标量,所以在调⽤ backward 时需要传⼊⼀个和 y 同形的权᯿向量进⾏加权求和得到⼀个标量v。
v = torch.tensor([[1.0, 0.1], [0.01, 0.001]], dtype=torch.float)
z.backward(v)
print(x.grad)
# tensor([2.0000, 0.2000, 0.0200, 0.0020]) #x.grad 是和 x 同形的张量。
#梯度追踪:
x = torch.tensor(1.0, requires_grad=True)
y1 = x ** 2
with torch.no_grad():
y2 = x ** 3
y3 = y1 + y2
print(x.requires_grad)
print(y1, y1.requires_grad) # True
print(y2, y2.requires_grad) # False
print(y3, y3.requires_grad) # True
# True
# tensor(1., grad_fn=<PowBackward0>) True
# tensor(1.) False
# tensor(2., grad_fn=<ThAddBackward>) True
#如果我们想要修改 tensor 的数值,但是⼜不希望被 autograd 记录(即不会影响反向传播),
#那么我么可以对 tensor.data 进⾏操作:
x = torch.ones(1,requires_grad=True)
print(x.data) # 还是⼀个tensor
print(x.data.requires_grad) # 但是已经是独⽴于计算图之外
# tensor([1.])
# False
y = 2 * x
x.data *= 100 # 只改变了值,不会记录在计算图,所以不会影响梯度传播
y.backward()
print(x) # 更改data的值也会影响tensor的值
print(x.grad)
# tensor([100.], requires_grad=True)
# tensor([2.])