目录
导入torch库:
import torch
一、数学运算
1、基本操作
-
floor: 向下取整;
-
ceil:向上取整;
-
round:四舍五入;
-
trunc:裁剪,只保留整数部分;
-
frac:只保留小数部分;
-
fix:向零方向舍入;
-
%:取余;
-
abs:取绝对值。
data = torch.tensor(
[
[1, 2, -3.5], # 1
[4, 5, 6], # 2
[10.5, 18.6, 19.6], # 3
[11.05, 19.3, 20.6], # 4
]
)
print(data)
# 向下取整(往小取 返回不大于每个元素的最大整数)
x1 = torch.floor(data)
print(x1)
# 向上取整(往大取 返回不小于每个元素的最小整数)
x2 = torch.ceil(data)
print(x2)
# 四舍五入 与python round()一致 四舍六入五看齐(看个位奇偶,奇进偶舍)
x3 = torch.round(data)
print(x3)
# 截断,只保留整数部分
x4 = torch.trunc(data)
print(x4)
# 截断,只保留小数部分
x5 = torch.frac(data)
print(x5)
# 向零方向舍入(舍入到最接近零的整数)
x6 = torch.fix(data)
print(x6)
# 取余
x7 = torch.fmod(data, 2)
print(x7)
# 绝对值
x8 = torch.abs(data)
print(x8)
2、三角函数
-
torch.cos(input,out=None)
-
torch.cosh(input,out=None) # 双曲余弦函数
-
torch.sin(input,out=None)
-
torch.sinh(input,out=None) # 双曲正弦函数
-
torch.tan(input,out=None)
-
torch.tanh(input,out=None) # 双曲正切函数
# torch.set_printoptions(sci_mode=False)
print(torch.pi) # 圆周率
deg = torch.pi/180
data = torch.tensor([0,90*deg,45*deg])
x1 = torch.sin(data)
print(x1)
x2 = torch.cos(data)
print(x2)
x3 = torch.sinh(data)
print(x3)
x4 = torch.cosh(data)
print(x4)
x5 = torch.tan(data)
print(x5)
x6 = torch.tanh(data)
print(x6)
3、统计学函数
-
torch.mean(): 计算张量的平均值。
-
torch.sum(): 计算张量的元素之和。
-
torch.std(): 计算张量的标准差。
-
torch.var(): 计算张量的方差。
-
torch.median(): 计算张量的中位数。
-
torch.max(): 计算张量的最大值。
-
torch.min(): 计算张量的最小值。
-
torch.mode(): 计算张量众数
-
torch.sort(): 对张量进行排序。 返回排序后的张量values和索引indices
-
torch.topk(): 返回张量中的前 k 个最大值或最小值。
-
torch.histc(): 计算张量的直方图。
-
torch.unique(): 返回张量中的唯一值。
-
torch.bincount(): 计算张量中每个元素的出现次数。
torch.manual_seed(66)
x = torch.tensor([1,2,2,3,3,3,4,4,5]).type(torch.float32)
print(x)
# 平均值
x1 = x.mean()
print(x1)
# 求和
x2 = x.sum()
print(x2)
# 标准差
x3 = x.std()
print(x3)
# 方差
x4 = x.var()
print(x4)
# 中位数
x5 = x.median()
print(x5)
# 最大值
x6 = x.max()
print(x6)
# 最小值
x7 = x.min()
print(x7)
# 众数
x8 = torch.mode(x)
print(x8)
# 排序 返回排序后的张量values和索引indices
x9 = torch.sort(x)
print(x9)
x = torch.tensor([1,2,2,3,3,3,4,4,5,2,3,4,6]).type(torch.float32)
print(torch.topk(x,3)) # 返回前3个最大值
print(torch.histc(x,bins=10, min=2,max=4)) # 区间[min,max]分成bins份 表示各个区间的元素计数
print(torch.unique(x)) # 返回分类的数据集中的数据类型
x = torch.tensor([1,2,2,3,3,3,4,4,5,2,3,4,6])
print(torch.bincount(x)) # 返回张量中每个元素的出现次数
二、保存和加载
# 保存
x = torch.tensor([1,2,3])
torch.save(x,'./data/tensor01.pth')
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
# 加载
y = torch.load('./data/tensor01.pth',map_location=device)
print(y)
print(y.device)
三、并行化
在 PyTorch 中,可以查看和设置用于 CPU 运算的线程数。PyTorch 使用多线程来加速 CPU 运算,但有时可能需要调整线程数来优化性能。
# torch.get_num_threads() 来查看当前 PyTorch 使用的线程数
def test01():
count = torch.get_num_threads()
print(count)
# torch.set_num_threads() 设置 PyTorch 使用的线程数
def test02():
torch.set_num_threads(4)
count = torch.get_num_threads()
print(count)
if __name__ == '__main__':
test01()
test02()
设置线程数时,确保考虑到你的 CPU 核心数和其他进程的资源需求,以获得最佳性能。
注意事项
-
线程数设置过高可能会导致线程竞争,反而降低性能;
-
线程数设置过低可能会导致计算资源未得到充分利用;
-
当使用 GPU 进行计算时,线程数设置对性能影响较小,因为 GPU 计算并不依赖于 CPU 线程数。
四、自动微分
自动微分模块torch.autograd负责自动计算张量操作的梯度,具有自动求导功能。自动微分模块是构成神经网络训练的必要模块,可以实现网络权重参数的更新,使得反向传播算法的实现变得简单而高效。
1、相关概念
-
张量
Torch中的对象都为张量,属性requires_grad决定是否对其进行梯度计算。默认是 False,如需计算梯度则设置为True。
-
计算图:
torch.autograd通过创建一个动态计算图来跟踪张量的操作,每个张量是计算图中的一个节点,节点之间的操作构成图的边。
-
反向传播
使用tensor.backward()方法执行反向传播,从而计算张量的梯度。这个过程会自动计算每个张量对损失函数的梯度。
-
梯度
计算得到的梯度通过tensor.grad访问,这些梯度用于优化模型参数,以最小化损失函数。
2、计算梯度
使用tensor.backward()方法执行反向传播,以计算张量的梯度。
1.标量梯度计算
# 创建张量:要计算梯度则必须为浮点类型
x = torch.tensor(3, require_grad=True, dtype=torch.float64)
# 定义函数
y = x**2 + 2*x - 5
# 计算梯度,也就是反向传播
y.backward() # # 梯度计算 1.y函数对x求导函数 y'=2*x+2 2.代入x的当前值 x=3 3.求出导数值 y'=2*3+2
# 读取梯度值
print(x.grad) # tensor(8.)
2.向量梯度计算
# 向量梯度计算
x = torch.tensor([1,2,3],requires_grad=True,dtype=torch.float64)
y = x**2 + 2*x - 5
print(y) # tensor([-2., 3., 10.], dtype=torch.float64, grad_fn=<SubBackward0>)
y = y.sum() # y必须是一个标量才能用这个标量对x求导 设置一个复合函数
y.backward() # y'=2x+2
print(x.grad) # tensor([4., 6., 8.], dtype=torch.float64)
3.多标量梯度计算
# 多标量的梯度计算
x1 = torch.tensor(1,requires_grad=True,dtype=torch.float64)
x2 = torch.tensor(2,requires_grad=True,dtype=torch.float64)
y = x1**2 + 3*x2 - 5
y.backward() # 偏导数
print(x1.grad)
print(x2.grad)
4.多向量梯度计算
# 多向量的梯度计算
x1 = torch.tensor([1,2,3],requires_grad=True,dtype=torch.float64)
x2 = torch.tensor([4,5,6],requires_grad=True,dtype=torch.float64)
y = x1**2 + 3*x2 - 5
y = y.sum()
y.backward() # 偏导数
print(x1.grad) # 2*x1 tensor(2., dtype=torch.float64)
print(x2.grad) # 3 tensor(3., dtype=torch.float64)
5.矩阵梯度计算
# 矩阵梯度计算
x = torch.tensor([[1,2],[3,4]],requires_grad=True,dtype=torch.float64)
y = x**2 + 2*x - 5
y = y.sum()
y.backward()
print(x.grad)
3、梯度上下文控制
梯度计算的上下文控制和设置对于管理计算图、内存消耗、以及计算效率至关重要。
1、梯度控制
梯度计算是有性能开销的,有些时候只是简单的运算,此时并不需要梯度。
# 正常情况下
def test01():
x = torch.tensor(10.5, requires_grad=True)
print(x.requires_grad) # True
# 默认y的requires_grad=True
y = x**2 + 2 * x + 3
print(y.requires_grad) # True
def test02():
x = torch.tensor(5, requires_grad=True,dtype=torch.float64)
# 关闭y的梯度计算 使用with进行上下文管理
with torch.no_grad():
y = x**2 + 2*x - 5
print(y.requires_grad) # False
# 使用装饰器
@torch.no_grad()
def test03():
x = torch.tensor([1,2,3], requires_grad=True,dtype=torch.float64)
y = x**2 + 2*x - 5
y = y.sum()
print(y.requires_grad) # False
# 自己创建装饰器
def my_no_grad(func):
def wrapper():
with torch.no_grad():
re = func()
return re
return wrapper
@my_no_grad
def test04():
x = torch.tensor([1,2,3], requires_grad=True,dtype=torch.float64)
y = x**2 + 2*x - 5
y = y.sum()
print(y.requires_grad) # False
# 全局设置关闭梯度计算
def test05():
x = torch.tensor([1,2,3], requires_grad=True,dtype=torch.float64)
torch.set_grad_enabled(False)
y = x**2 + 2*x - 5
y = y.sum()
y.backward()
print(x.requires_grad)
print(y.requires_grad)
print(x.grad)
# 累计梯度
def test06():
x = torch.tensor(1, requires_grad=True,dtype=torch.float64)
y = x**2 + 2*x - 5
y.backward()
print(x.grad)
y = 2*x**2 + 7
y.backward()
print(x.grad)
# 累计梯度02
def test07():
x = torch.tensor(4, requires_grad=True,dtype=torch.float64)
for _ in range(4):
y = x**2 + 2*x - 5
y.backward()
print(x.grad)
# 梯度清零
def test08():
x = torch.tensor(4, requires_grad=True,dtype=torch.float64)
y = x**2 + 2*x - 5
y.backward() # 2*x + 2
print(x.grad)
z = 3*x**2 + 7*x
# 清零
x.grad.zero_() # 不清零则为 10+31=41
z.backward() # 清零后为 6*4+7=31
print(x.grad)
# 综合梯度清零操作
for _ in range(3):
y = x**2 + 2 * x + 7
z = y.mean()
# 反向传播之前先对梯度进行清零
if x.grad is not None:
x.grad.zero_()
z.backward()
print(x.grad)
if __name__ == '__main__':
test01()
test02()
test03()
test04()
test05()
test06()
test07()
test08()
2、梯度更新
大多数情况下是不需要梯度累加的,反向传播之前可以先用 .zero_() 对梯度进行清零。
更新梯度时,注意更新的数据应是data,直接改变原tensor会导致其变成新的数据。
# 标量训练
def test01():
# 生成初始化w
w = torch.tensor(25., requires_grad=True)
# 训练参数
lr = 0.01
epoch = 5
for i in range(epoch):
# 生成损失函数
loss = 3*w**2 + 2*w - 5
# 梯度清零
if w.grad is not None:
w.grad.zero_()
# 反向传播 求当前w的导数值:梯度值,斜率
loss.backward()
# 当前斜率
print(w.grad)
# 更新梯度
# w的tensor不能修改 避免w变成新的数据 应修改w.data
w.data = w.data - lr*w.grad.data
print(w)
# 访问训练后的w的值
print(w.data)
# 向量训练
def test02():
# 生成初始化w
w = torch.tensor([10,20,30], requires_grad=True,dtype=torch.float64)
# 训练参数
lr = 0.01
epoch = 5
for i in range(epoch):
# 生成损失函数
loss = 3*w**2 + 2*w - 5
loss = loss.sum() # 设置一个复合函数
# 梯度清零
if w.grad is not None:
w.grad.zero_()
# 反向传播 求当前w的导数值:梯度值,斜率
loss.backward()
# 当前斜率
print(w.grad)
# 更新梯度
# w的tensor不能修改 避免w变成新的数据 应修改w.data
w.data = w.data - lr*w.grad.data
print(w)
# 访问训练后的w的值
print(w.data)
# 保存训练好的权重数据
torch.save(w.data,'./data/weight.pth')
# 加载训练好的权重数据
def detect():
w = torch.load('./data/weight.pth',map_location='cuda')
# 使用w
print(w)
if __name__ == '__main__':
# test01()
test02()
detect()
3、叶子节点
当想使用不含求导功能的tensor时,可以使用detach()创建张量,该张量是作为叶子结点存在的,并且该张量和原张量共享数据,只是该张量不需要计算梯度。
原因:可以求导的张量不能直接当作普通的tensor使用,如转换为numpy数组操作: .numpy()
# detach()产生的张量是作为叶子结点存在的,并且该张量和原张量共享数据,只是该张量不需要计算梯度。
def test01():
x = torch.tensor([1, 2, 3], requires_grad=True,dtype=torch.float32)
# x1 = x.numpy() # 报错 如果x是一个可以求导的张量,那么它就不能直接当作普通的tensor使用
def test02():
x = torch.tensor([1, 2, 3], requires_grad=True,dtype=torch.float32)
x2 = x.detach() # 创建一个叶子结点,并且和x共享数据,但是不需要计算梯度
print(x) # tensor([1., 2., 3.], requires_grad=True)
print(x2) # tensor([1., 2., 3.])
print(id(x),id(x2)) # 两个对象
print(id(x.data),id(x2.data)) # 数据共享
x2[0] = 5
print(x,x2)
if __name__ == '__main__':
test02()