目录
一、张量的形状
1.reshape函数的用法
reshape可以在保证张量数据不变的前提下改变数据的维度,将其转换成指定的形状,在神经网络中,会经常使用该函数来调节数据的形状的操作,以便能够更好处理网络各层之间的数据连接。
torch.manual_seed(0)
data = torch.randint(0,10,(4,5))
print(data.size())
# 转换之后的形状元素个数要等于原来张量的元素个数
print(data.reshape(2,10))
# 使用-1代替省略的形状
new_data = data.reshape(5,-1)
print(new_data.size())
torch.Size([4, 5])
tensor([[4, 9, 3, 0, 3, 9, 7, 3, 7, 3],
[1, 6, 6, 9, 8, 6, 6, 8, 4, 3]])
torch.Size([5, 4])
2.transpose和permute的使用
transpose函数可以实现交换张量形状的指定维度,例如:一个张量的形状(2,3,4)可以通过transpose转换为(2,4,3)
permute可以一次转换更多的维度
# transpose
torch.manual_seed(0)
data = torch.randint(0,10,(3,4,5))
new_data = data.reshape(4,3,5) #重新计算
print(new_data.shape)
# 直接交换两个维度的值
new_data = torch.transpose(data, 0,1)
print(new_data.shape)
# 缺点:一次只能交换两个维度
#permute 一次交换多个维度
new_data = torch.permute(data,[1,2,0])
print(new_data.shape)
torch.Size([4, 3, 5])
torch.Size([4, 3, 5])
torch.Size([4, 5, 3])
3.view和contigous函数的用法
view函数也可以修改张量的形状,但是其用法比较局限,只能存储在整块内存中的张量。
# view
data = torch.tensor([[10,20,30],[40,50,60]])
data = data.view(3,2)
print(data.shape)
# is_contiguous 函数判断张量是否是连续内存空间
print(data.is_contiguous())
data = torch.transpose(data,1,0)
print(data.is_contiguous())
data = data.contiguous().view(3,2)
print(data)
torch.Size([3, 2])
True
False
tensor([[10, 30],
[50, 20],
[40, 60]])
4.squeeze 和unsqueeze
squeeze用于删除shape为1的维度,unsqueeze在每个维度添加1,以增加数据的形状
data = torch.randint(0,10,(1,3,1,5))
print(data.shape)
#维度压缩 默认压缩所有为1的维度
new_data = data.squeeze()
print(new_data.shape)
#指定去掉某个1的维度
new_data = data.squeeze(0)
print(new_data.shape)
# unsqueeze 指定哪个维度 -1代表最后一个维度
new_data = new_data.unsqueeze(-1)
print(new_data.shape)
torch.Size([1, 3, 1, 5])
torch.Size([3, 5])
torch.Size([3, 1, 5])
torch.Size([3, 1, 5, 1])
二、张量运算函数
#1 均值
torch.manual_seed(0)
data = torch.randint(0,10,[2,3]).double()
# print(data.dtype)
print(data)
print(data.mean())
# 按指定的维度计算均值
print(data.mean(dim=0)) # 按列
print('-'*10,'求和')
print(data.sum())
print(data.sum(dim=1))
print('-'*10,'平方')
print(data.pow(2))
print('-'*10,'平方根')
print(data.sqrt())
print('-'*10,'e的n次方')
print(data.exp())
print('-'*10,'对数')
print(data.log()) # 默认以e为底
print(data.log2()) # 以2为底
print(data.log10()) # 以10为底
tensor([[4., 9., 3.],
[0., 3., 9.]], dtype=torch.float64)
tensor(4.6667, dtype=torch.float64)
tensor([2., 6., 6.], dtype=torch.float64)
---------- 求和
tensor(28., dtype=torch.float64)
tensor([16., 12.], dtype=torch.float64)
---------- 平方
tensor([[16., 81., 9.],
[ 0., 9., 81.]], dtype=torch.float64)
---------- 平方根
tensor([[2.0000, 3.0000, 1.7321],
[0.0000, 1.7321, 3.0000]], dtype=torch.float64)
---------- e的n次方
tensor([[5.4598e+01, 8.1031e+03, 2.0086e+01],
[1.0000e+00, 2.0086e+01, 8.1031e+03]], dtype=torch.float64)
---------- 对数
tensor([[1.3863, 2.1972, 1.0986],
[ -inf, 1.0986, 2.1972]], dtype=torch.float64)
tensor([[2.0000, 3.1699, 1.5850],
[ -inf, 1.5850, 3.1699]], dtype=torch.float64)
tensor([[0.6021, 0.9542, 0.4771],
[ -inf, 0.4771, 0.9542]], dtype=torch.float64)
三、自动微分
自动微分模块对张量做了进一步的封装,具有自动求导的功能。自动微分模块是构成神经网络的必要模块,在神经网络的反向传播过程中,Autigrad模块基于正向计算的结果对当前的参数进行微分计算,从而实现网络权重参数的更新。
1.梯度基本计算
# 标量的梯度计算
#y = x**2+20
def test01():
#对于需要求导的张量需要设置 requires_grad=True
x = torch.tensor(10,requires_grad=True,dtype=torch.float64)
# 对x的中间计算
f = x ** 2 +20 # 2x
# 自动微分
f.backward()
# 访问梯度
print(x.grad)
# 2.向量的梯度计算
x = torch.tensor([10,20,30,40],requires_grad=True,dtype=torch.float64)
# 定义变量的计算过程
y1 = x ** 2 +20
#注意:自动微分的时候,必须是一个标量
y2 = y1.mean() # 1/4 * y1
#自动微分
y2.backward()
# 打印梯度值
print(x.grad)
# 3.多标量梯度计算
# y= x1 ** 2 + x2 ** 2 + x1*2
x1 = torch.tensor(10,requires_grad=True,dtype=torch.float64)
x2 = torch.tensor(20, requires_grad=True, dtype=torch.float64)
y= x1 ** 2 + x2 ** 2 + x1*x2
# 自动微分
y.backward()
#打印梯度值
print(x1.grad)
print(x2.grad)
# 4.多向量梯度计算
x1 = torch.tensor([10,20],requires_grad=True,dtype=torch.float64)
x2 = torch.tensor([30,40], requires_grad=True, dtype=torch.float64)
# 定义中间计算过程
y= x1 ** 2 + x2 ** 2 + x1*x2
#将输出结果变为标量
y = y.sum()
# 自动微分
y.backward()
#打印梯度值
print(x1.grad)
print(x2.grad)
tensor(20., dtype=torch.float64)
tensor([ 5., 10., 15., 20.], dtype=torch.float64)
tensor(40., dtype=torch.float64)
tensor(50., dtype=torch.float64)
tensor([50., 80.], dtype=torch.float64)
tensor([ 70., 100.], dtype=torch.float64)
2.控制梯度计算
# 1.控制梯度计算
def test01():
# 控制梯度计算
x = torch.tensor(10,requires_grad=True,dtype=torch.float64)
print(x.requires_grad)
#第一种方法
with torch.no_grad():
y = x**2 # 这个过程不参与梯度计算
print(y.requires_grad)
# 第二种方法
@torch.no_grad()
def my_func(x):
return x**2
y = my_func(x)
print(y.requires_grad)
# 第三种方法 全局的方式
torch.set_grad_enabled(False)
y = x ** 2
print(y.requires_grad)
def test02():
# 累计梯度和梯度清零
x = torch.tensor([10, 20, 30, 40], requires_grad=True, dtype=torch.float64)
# 当我们重复对x进行梯度计算的时候,时会将历史的梯度值累加到x.grad 属性中
for _ in range(3):
#对输入x的计算过程
f1 = x**2 + 20
# 将向量转换为标量
f2 = f1.mean()
# 梯度清零
if x.grad is not None:
x.grad.data.zero_()
#自动微分
f2.backward()
# 打印梯度值
print(x.grad)
# 梯度下降优化案例
def test03():
# y = x** 2
# 当x为什么值的情况下,y最小
x = torch.tensor(10, requires_grad=True, dtype=torch.float64)
for _ in range(5000):
#正向计算
y = x**2
#梯度清零
if x.grad is not None:
x.grad.data.zero_()
#自动微分
y.backward()
# 更新参数
x.data = x.data - 0.001* x.grad
# 打印x的值
print('%.10f'%x.data)
3.梯度计算注意
当设置requires_grad=True的张量使用numnpy函数进行转换时,会报错。
RuntimeError: Can't call numpy() on Tensor that requires grad. Use tensor.detach().numpy() instead.
需要使用detach函数将张量分离,在使用numpy函数。
注意:detach之后会产生一个新的张量,新的张量作为叶子节点,并且该张量和原来的张量共享数据,但是分离后的张量不需要计算梯度。
x1 = torch.tensor([10,20],requires_grad=True,dtype=torch.float64)
x2 = x1.detach()
print(id(x1.data),id(x2.data))
# 修改分离后的张量
x2[0] = 100
print(x1)
print(x2)
# x2张量不存在requires_grad=True
# 表示x1的任何计算都会影响到 x1的梯度计算
# 表示x2的任何计算都不会影响到x1的梯度计算
2316405416640 2316405416640
tensor([100., 20.], dtype=torch.float64, requires_grad=True)
tensor([100., 20.], dtype=torch.float64)