[Pytorch]深度学习-part1
一、Pytorch的安装
1.1打开cmd
1.2 输入nvidia-smi
可以查看到CUDA Version的版本是12.7
我们在安装的时候,需要注意,安装的cuda要小于等于我们电脑显示的CUDA版本
1.3 (可选)卸载之前版本Pytorch
通过上述方法,卸载之前安装的错误版本
1.4 正式安装
我这里选择安装12.4版本
>pip install torch==2.5.1 torchvision==0.20.1 torchaudio==2.5.1 --index-url https://download.pytorch.org/whl/cu124
1.5 检测是否安装成功
打开VS,使用安装了这个pytorch包的环境
这里可以看到成功安装:)
二、基本创建方式
2.1torch.tensor
# 1. 用标量创建张量
tensor = torch.tensor(5)
print(tensor.shape)# 2. 使用numpy随机一个数组创建张量
tensor = torch.tensor(np.random.randn(3, 5))
print(tensor)
print(tensor.shape)# 3. 根据list创建tensor
tensor = torch.tensor([[1, 2, 3], [4, 5, 6]])
print(tensor)
print(tensor.shape)
print(tensor.dtype)
1.2torch.Tensor
# 1. 根据形状创建张量
tensor1 = torch.Tensor(2, 3)
print(tensor1)
# 2. 也可以是具体的值
tensor2 = torch.Tensor([[1, 2, 3], [4, 5, 6]])
print(tensor2, tensor2.shape, tensor2.dtype)tensor3 = torch.Tensor([10])
print(tensor3, tensor3.shape, tensor3.dtype)
# 指定tensor数据类型
tensor1 = torch.Tensor([1,2,3]).short()
print(tensor1)
tensor1 = torch.Tensor([1,2,3]).int()
print(tensor1)
tensor1 = torch.Tensor([1,2,3]).float()
print(tensor1)
tensor1 = torch.Tensor([1,2,3]).double()
print(tensor1)
torch.Tensor与torch.tensor区别
特性 | torch.Tensor() | torch.tensor() |
---|---|---|
数据类型推断 | 强制转为 torch.float32 | 根据输入数据自动推断(如整数→int64 ) |
显式指定 dtype | 不支持 | 支持(如 dtype=torch.float64 ) |
设备指定 | 不支持 | 支持(如 device='cuda' ) |
输入为张量时的行为 | 创建新副本(不继承原属性) | 默认共享数据(除非 copy=True ) |
推荐使用场景 | 需要快速创建浮点张量 | 需要精确控制数据类型或设备 |
1.3torch.IntTensor
用于创建指定类型的张量,还有诸如Torch.FloatTensor、 torch.DoubleTensor、 torch.LongTensor......等。
如果数据类型不匹配,那么在创建的过程中会进行类型转换,要尽可能避免,防止数据丢失。
# 1. 创建指定形状的张量
tt1 = torch.IntTensor(2, 3)
print(tt1)tt2 = torch.FloatTensor(3, 3)
print(tt2, tt2.dtype)
tt3 = torch.DoubleTensor(3, 3)
print(tt3, tt3.dtype)
tt4 = torch.LongTensor(3, 3)
print(tt4, tt4.dtype)
tt5 = torch.ShortTensor(3, 3)
print(tt5, tt5.dtype)
三、创建线性和随机张量
3.1创建线性张量
# 1. 创建线性张量
r1 = torch.arange(0, 10, 2)
print(r1)
# 2. 在指定空间按照元素个数生成张量:等差
r2 = torch.linspace(3, 10, 10)
print(r2)
r2 = torch.linspace(3, 10000000, 10)
print(r2)
3.2随机张量
3.2.1随机数种子
使用随机数种子,生成器将在每次生成相同的数列
# 设置随机数种子
torch.manual_seed(123)# 获取随机数种子
print(torch.initial_seed())
3.2.2随机张量
# 1. 设置随机数种子
torch.manual_seed(123)# 2. 获取随机数种子,需要查看种子时调用
print(torch.initial_seed())# 3. 生成随机张量,均匀分布(范围 [0, 1))
# 创建2个样本,每个样本3个特征
print(torch.rand(2, 3))# 4. 生成随机张量:标准正态分布(均值 0,标准差 1)
print(torch.randn(2, 3))# 5. 原生服从正态分布:均值为2, 方差为3,形状为1*4的正态分布
print(torch.normal(mean=2, std=3, size=(1, 4)))
三、Tensor常见属性
张量常见属性:device,dtype,shape
3.1获取属性
data = torch.tensor([1, 2, 3])
print(data.dtype, data.device, data.shape)
3.2切换设备
默认cpu上运行,可以切换到GPU上
# 把数据切换到GPU进行运算
device = "cuda" if torch.cuda.is_available() else "cpu"
data = data.to(device)
print(data.device)
或者直接 data = data.cuda()或者data = data.to("cuda")
3.3类型转化
类型转化是张量的基本操作
# 1. 使用type进行类型转换
data = data.type(torch.float32)
print(data.dtype) # float32
data = data.type(torch.float16)
print(data.dtype) # float16# 2. 使用类型方法
data = data.float()
print(data.dtype) # float32
# 16 位浮点数,torch.float16,即半精度
data = data.half()
print(data.dtype) # float16
data = data.double()
print(data.dtype) # float64
data = data.long()
print(data.dtype) # int64
data = data.int()
print(data.dtype) # int32
四、Tensor数据转换
4.1张量转Numpy
4.1.1浅拷贝
调用numpy()方法可以把Tensor转化为Numpy,内存共享
# 1. 张量转numpy
data_tensor = torch.tensor([[1, 2, 3], [4, 5, 6]])
data_numpy = data_tensor.numpy()
print(type(data_tensor), type(data_numpy))
# 2. 他们内存是共享的
data_numpy[0, 0] = 100
print(data_tensor, data_numpy)
4.1.2深拷贝
copy()避免内存共享
4.2Numpy转张量
4.2.1浅拷贝
from_numpy方法转Tensor默认是内存共享的
可以看出[0,0]都变成了100
4.2.2深拷贝
用tensor就不会共享内存
五、Tensor常见操作
5.1获取元素值
tips:和Tensor的维度没有关系,如果有多个元素就会报错,只能在CPU上的张量,如果在GPU上就要先移动
5.2元素值运算
带“_”的会改变原始值
5.3阿达玛积
俩形状相同的矩阵或张量对应的元素相乘,阿达玛积是逐元素操作
5.4Tensor相乘
假设有两个矩阵:
矩阵 A的形状为 m×n(m行 n列)。
矩阵 B的形状为 n×p(n行 p列)。
矩阵 A和 B的乘积 C=A×B是一个形状为 m×p的矩阵,其中 C的每个元素 Cij,计算 A的第 i行与 B的第 j列的点积。计算公式为:
就像是线性代数那样,俩矩阵相乘。
5.5形状操作
5.5.1 reshape
5.5.2 view
view进行形状变换的特征:
张量在内存中是连续的;
返回的是原始张量视图,不重新分配内存,效率更高;
如果张量在内存中不连续,view 将无法执行,并抛出错误。
5.5.2.1 内存连续性
对于多维张量,内存布局通常按照最后一个维度优先的顺序存储,即先存列,后存行。
.view()
要求张量在内存中是连续存储的,否则会报错
5.5.2.2 和reshape比较
view:高效,但需要张量在内存中是连续的;
reshape:更灵活,但涉及内存复制;
5.5.2.3 view变形操作
5.5.3 transpose
通过transpose这个方法,将data数据的(3,4,5)其中的0,1位上的数据位置进行转变成(4,3,5)
5.5.4 permute
它通过重新排列张量的维度来返回一个新的张量,不改变张量的数据,只改变维度的顺序。
permute(1, 2, 0)
就是把原来的 (3, 4, 5)
张量整体转置成 (4, 5, 3)
,不改变数据内容,只改变维度的排列顺序。
5.5.5 升维和降维
unsqueeze:用于在指定位置插入一个大小为 1 的新维度。
squeeze:用于移除所有大小为 1 的维度,或者移除指定维度的大小为 1 的维度。
5.5.5.1 squeeze降维
torch.squeeze(input, dim=None)
input: 输入的张量。
dim (可选): 指定要移除的维度。如果指定了 dim,则只移除该维度(前提是该维度大小为 1);如果不指定,则移除所有大小为 1 的维度。
data.squeeze(0).squeeze(-1)表达第一维和最后一维,进行降维操作
5.5.5.2 unsqueeze升维
六、广播机制
6.1广播机制规则
张量维度至少为1
满足右对齐
6.2 广播案例
七、自动微分
7.1 基础概念
自动微分模块torch.autograd负责自动计算张量操作的梯度,具有自动求导功能。自动微分模块是构成神经网络训练的必要模块,可以实现网络权重参数的更新,使得反向传播算法的实现变得简单而高效。
7.1.1张量
Torch中一切皆为张量,属性requires_grad决定是否对其进行梯度计算。默认是 False,如需计算梯度则设置为True。
7.1.2 计算图
torch.autograd通过创建一个动态计算图来跟踪张量的操作,每个张量是计算图中的一个节点,节点之间的操作构成图的边。
在 PyTorch 中,当张量的 requires_grad=True 时,PyTorch 会自动跟踪与该张量相关的所有操作,并构建计算图。每个操作都会生成一个新的张量,并记录其依赖关系。当设置为 True
时,表示该张量在计算图中需要参与梯度计算,即在反向传播(Backpropagation)过程中会自动计算其梯度;当设置为 False
时,不会计算梯度。
在上述代码中,x 和 y 是输入张量,即叶子节点,z 是中间结果,loss 是最终输出。每一步操作都会记录依赖关系:
z = x * y:z 依赖于 x 和 y。
loss = z.sum():loss 依赖于 z。
叶子节点:
在 PyTorch 的自动微分机制中,叶子节点(leaf node) 是计算图中:
由用户直接创建的张量,并且它的 requires_grad=True。
这些张量是计算图的起始点,通常作为模型参数或输入变量。
特征:
没有由其他张量通过操作生成。
如果参与了计算,其梯度会存储在 leaf_tensor.grad 中。
默认情况下,叶子节点的梯度不会自动清零,需要显式调用 optimizer.zero_grad() 或 x.grad.zero_() 清除。
如何判断一个张量是否是叶子节点?
通过 tensor.is_leaf 属性,可以判断一个张量是否是叶子节点。
x = torch.tensor([1.0, 2.0, 3.0], requires_grad=True) # 叶子节点 y = x ** 2 # 非叶子节点(通过计算生成) z = y.sum() print(x.is_leaf) # True print(y.is_leaf) # False print(z.is_leaf) # False
叶子节点与非叶子节点的区别
特性 叶子节点 非叶子节点 创建方式 用户直接创建的张量 通过其他张量的运算生成 is_leaf 属性 True False 梯度存储 梯度存储在 .grad 属性中 梯度不会存储在 .grad,只能通过反向传播传递 是否参与计算图 是计算图的起点 是计算图的中间或终点 删除条件 默认不会被删除 在反向传播后,默认被释放(除非 retain_graph=True) detach():张量 x 从计算图中分离出来,返回一个新的张量,与 x 共享数据,但不包含计算图(即不会追踪梯度)。
特点:
返回的张量是一个新的张量,与原始张量共享数据。
对 x.detach() 的操作不会影响原始张量的梯度计算。
推荐使用 detach(),因为它更安全,且在未来版本的 PyTorch 中可能会取代 data。
x = torch.tensor([1.0, 2.0, 3.0], requires_grad=True) y = x.detach() # y 是一个新张量,不追踪梯度 y += 1 # 修改 y 不会影响 x 的梯度计算 print(x) # tensor([1., 2., 3.], requires_grad=True) print(y) # tensor([2., 3., 4.])
反向传播
使用tensor.backward()方法执行反向传播,从而计算张量的梯度。这个过程会自动计算每个张量对损失函数的梯度。例如:调用 loss.backward() 从输出节点 loss 开始,沿着计算图反向传播,计算每个节点的梯度。
梯度
计算得到的梯度通过tensor.grad访问,这些梯度用于优化模型参数,以最小化损失函数。
7.2 计算梯度
tensor.backward()方法执行反向传播,从而计算张量的梯度
7.2.1 标量梯度计算
7.2.2 向量梯度计算
7.2.3 多标量梯度计算
7.2.4 多向量梯度计算
7.3 梯度上下文控制
7.3.1 控制梯度计算
可以通过with torch.no_gard实现不计算y的梯度,还可以通过装饰器@torch.no_grad()实现不计算y的梯度。
7.3.2 累计梯度
7.3.3 梯度清零
7.3.4 求函数最小值
import torch
from matplotlib import pyplot as plt
import numpy as np
def test01():
x = np.linspace(-10, 10, 100)
y = x ** 2plt.plot(x, y)
plt.show()
def test02():
# 初始化自变量X
x = torch.tensor([3.0], requires_grad=True, dtype=torch.float)
# 迭代轮次
epochs = 50
# 学习率
lr = 0.1list = []
for i in range(epochs):
# 计算函数表达式
y = x ** 2# 梯度清零
if x.grad is not None:
x.grad.zero_()
# 反向传播
y.backward()
# 梯度下降,不需要计算梯度,为什么?
with torch.no_grad():
x -= lr * x.gradprint('epoch:', i, 'x:', x.item(), 'y:', y.item())
list.append((x.item(), y.item()))# 散点图,观察收敛效果
x_list = [l[0] for l in list]
y_list = [l[1] for l in list]plt.scatter(x=x_list, y=y_list)
plt.show()
if __name__ == "__main__":
test01()
test02()
![]() | ![]() |
用 PyTorch 的自动求导功能,手动实现对函数 y = x²
的最小值优化过程。
初始化变量
x = torch.tensor([3.0], requires_grad=True) # 起点 x=3
设置超参数
epochs = 50 # 迭代50轮
lr = 0.1 # 学习率0.1
主循环
for i in range(epochs):y = x ** 2 # 前向:计算 yx.grad.zero_() # 清零梯度(防止累加)y.backward() # 反向传播:计算 dy/dxwith torch.no_grad(): # 禁止梯度计算(避免追踪)x -= lr * x.grad # 更新 x:x = x - lr * dy/dx
7.3.5 函数参数求解
import torch
def test02():
x = torch.tensor([1, 2, 3, 4, 5], dtype=torch.float)
y = torch.tensor([3, 5, 7, 9, 11], dtype=torch.float)
a = torch.tensor([1.0], requires_grad=True) # 简化初始化
b = torch.tensor([1.0], requires_grad=True)
lr = 0.01
epochs = 100
for epoch in range(epochs):
# 每次迭代前清零梯度(无条件)
if a.grad is not None: # 确保梯度存在时清零
a.grad.zero_()
if b.grad is not None:
b.grad.zero_()
# 前向传播
y_pred = a * x + b
loss = ((y_pred - y) ** 2).mean()
# 反向传播
loss.backward()
# 梯度下降(不追踪计算图)
with torch.no_grad():
a -= lr * a.grad
b -= lr * b.grad
if (epoch + 1) % 10 == 0:
print(f'Epoch [{epoch + 1}/{epochs}], Loss: {loss.item():.4f}')
print(f'Final parameters: a = {a.item():.4f}, b = {b.item():.4f}')
if __name__ == '__main__':
test02()
输出结果接近期望的a=2 b=1的结果。