张量(Tensor)简单介绍
Pytorch最基本的操作对象是Tensor(张量),它表示一个多维矩阵,张量类似于NumPy的ndarrays ,张量可以在GPU上使用以加速计算。
生成数据的常用方法以及基本数据类型:
构造一个随机初始化的矩阵 | torch.rand |
全 0 矩阵 | torch.zeros |
全 1 矩阵 | orch.ones |
直接从数据构造张量 | torch.tensor |
32位浮点型 | torch.float32 |
64位浮点型 | torch.float64 |
32位整型 | torch.int32 |
生成数据
x1 = torch.rand(2,3)
# 返回一个张量,包含了从区间[0,1)的均匀分布中抽取的一组随机数,形状由可变参数sizes定义
运行结果:
x2 = torch.randn(3,4)
#返回一个张量,包含了从标准正态分布(均值为 0,方差为 1,即高斯白噪声)中抽取一组随机数
运行结果:
x3 = torch.zeros(2,3)
print(x3)
x4 = torch.ones(2,3,4) # 生成2个3x4的全1矩阵。两个二维矩阵组成一个三维矩阵
x4
运行结果:
查看数据的形状:
print(x4.size())
x4.shape
# 二者效果一致。但是size()方法可以获取某个维度的长度
x4.size(0) # 查看x4的第一个维度的大小
运行结果:
基本数据类型和数据类型转换
32位浮点型:torch.float32
64位浮点型:torch.float64
16位整型:torch.int16
32位整型:torch.int32
64位整型:torch.int64
# (1)从列表直接创建一个tensor,并且制定数据类型为torch.float32
y1 = torch.tensor([6,2],dtype=torch.float32)
print('查看y1的数据类型',y1.type())
y1
运行情况:
y1 = y1.type(torch.int32) # 转换数据类型
print('转换y1的数据类型',y1.type())
y1
运行情况:
tensor与numpy
Numpy转化为Tensor:torch.from_numpy(numpy矩阵)
Tensor转化为numpy:Tensor矩阵.numpy()
import numpy as np
y2 = np.random.randn(2,3) # 使用Numpy生成的2x3的正态分布的随机数
print(y2)
y3 = torch.from_numpy(y2) # numpy —> tensor
print(y3)
y4 = y3.numpy() # tensor —> numpy
print(y4)
运行情况:
张量的运算
与numpy的运算规则类似:元素相加、广播、下划线代表就地改变。
调整张量的大小/形状,可以使用:torch.view。
单个元素张量,使用.item() 转换为python数据。
z1 = torch.rand(2,3)
z2 = torch.randn(2,3)
print(z1)
z2
运行结果:
z1+z2 #对应元素相加减
z1+3 # 也会进行广播运算
运行结果:
z1.add(z2) # 原有的数据没有改变
print(z1)
z1.add_(z2) # z1会改变
print(z1)
运行结果:
改变数据形状:
# 现在z1是2x3,可以使用view方法改变形状。view这个方法经常用于展平数据
z1.view(3,2) # 但是z1并没有改变
运行结果:
z1.view(-1,1) # (n,1)
运行结果:
其它方法:
z1.mean() # z1的平均值
z1_sum = z1.sum() # z1的和
print(z1_sum )
运行结果:
可以看到z1_sum只有单个数值,当我想把这单个数值转换成一个标量值的话,可以通过item方法返回一个python标量值, 适用于tensor只有一个数值的场景
print(z1_sum.item()) # item方法返回一个标量值,即python中的数值,而不是一个tensor
张量的自动微分
如果将torch.tensor的 .requires_grad属性 设置为True的话,PyTorch框架就会开始跟踪该张量的所有操作。
在完成了一系列计算之后,可以在最后的结果上去调用 .backward() ,它会自动计算所有的梯度,并将该张量的梯度累加到张量的 .grad 属性中。
Tensor在pytorch中的数据结构:由data、grad、grad_fn组成。
data:记录它原始的值。
grad:代表张量的梯度,当设置requires_grad=True时,通过计算,最后调用backwaed计算出它 的梯度,梯度放在grad属性中。
grad_fn:记录这个tensor的来源,即它是由什么函数计算出来的 。
a = torch.ones(2,2,requires_grad=True) #设置张量a的requires_grad属性为true
a.data
运行结果:
b = a+2 # 这个b是通过计算得来的,说明它有grad_fn属性
print(b)
b.grad_fn
运行结果:
c = b*b+3
out = c.mean()
c
# 在out上计算梯度,使用自动微分。这个结果其实就是 d out/dx。这个微分结果就记录在a.grad里面
# out = (a+2)^2+3,再求一个均值
out.backward()
a.grad
结果:
自动微分运算在我们模型工作中,经常在loss上调用backward方法,从而计算出每一个变量的梯度,然后对这个变量进行改变
在另外一些场景中,我们不需要跟踪这个计算,可以通过将这段代码包装在with torch.no_grad这个上下文中。另外,如果想得到a原有的值,不想要它的requires_grad,可以使用detach获得原有的值,即不包含梯度的值。也可使用 .detach() 来获得具有相同内容但不需要跟踪运算的新Tensor :
with torch.no_grad():
#这里面的计算并不会被跟踪
#无法计算它们的梯度
pass
d = a.detach() # 得到的是a不包含梯度的值,不需要被跟踪运算
d.requires_grad
结果:
当然,也可以使用 requires_grad_ 方法就地改变张量requires_grad属性。
d.requires_grad_(True)
附上篇文章单变量线性回归例子的分解写法
import torch
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from torch import nn
plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签
plt.rcParams['axes.unicode_minus']=False #用来正常显示负号
# 使用pandas的read_csv读取数据集
data = pd.read_csv('../2、深度学习基础与线性回归/daatset/Income1.csv')
data.info()
plt.figure(0)
plt.scatter(data.Education,data.Income)
plt.xlabel('受教育年限')
plt.ylabel('收入情况')
plt.show()
X = torch.from_numpy(data.Education.values.reshape(-1,1).astype(np.float32)) # 样本特征
Y = torch.from_numpy(data.Income.values.reshape(-1,1).astype(np.float32)) # 标签
# 分解写法
# 由于是线性问题,所以要先初始化w和b。这里使用正态分布的方式进行初始化。
w = torch.randn(1,requires_grad=True)
# w是优化的参数,所以要设置可以被跟踪。由于是单变量线性回归,所以形状是1
b = torch.zeros(1,requires_grad=True)
# 模型的公式:W@x+b
lr = 0.0001 # 学习速率
or epoch in range(5000): #对全部数据训练5000次
for x,y in zip(X,Y):
y_predict = torch.matmul(x,w)+b # matmul是矩阵乘法
loss = (y-y_predict).pow(2).mean()
# (y-y_predict).pow(2)是差的平方,mean再求均值。求均方误差
if not w.grad is None:
# 在反向传播计算w和b的梯度前,先要将变量的梯度清零,否则梯度会累计进去,而不
# 是覆盖掉。
w.grad.data.zero_() # w.grad.data是梯度的值,清零。
if not b.grad is None:
b.grad.data.zero_()
loss.backward() #进行反向传播,根据损失函数,从而优化参数
# 梯度的方向就是上升的方向
# 这样,w和b上就有梯度值了。为了让梯度下降,要将 参数的值 减去 学习率*求得到梯度。让参
# 数沿着梯度下降。
with torch.no_grad:
# 为了避免引起强烈的震荡,别把梯度全部减掉
# 这部分计算并不需要被跟踪,所以用了一个上下文。
w.data -= w.grad.data * learning_rate
b.data -= b.grad.data * learning_rate
print("w:",w)
print("b:",b)
结果:
plt.figure(1)
plt.scatter(data.Education,data.Income)
plt.plot(X.numpy(),(X*w+b).data.numpy(),c='r') # (X*w+b).data模型预测结果的值
plt.show()
结果: