1. 处理张量
1.1. torch.Tensor()和torch.tensor()
- 相同点:
生成新的张量
>>> a = torch.Tensor([1,2])
>>> a
tensor([1., 2.])
>>> a = torch.tensor([1,2])
>>> a
tensor([1, 2])
- 不同点
- torch.Tensor()是python类,是torch.FloatTensor()的别名,生成float类型的张量;
- torch.tensor()仅仅是python的函数,函数原型是torch.tensor(data, dtype=None, device=None, requires_grad=False),其中data可以是scalar,list,tuple,numpy array等等。torch.tensor会从data中的数据部分进行拷贝(而不是引用),根据原始数据类型生成相应的 torch.LongTensor、torch.FloatTensor和torch.DoubleTensor。比如:
>>> a = torch.tensor([1,2])
>>> a.type()
'torch.LongTensor'
>>> a = torch.tensor([1.,2.])
>>> a.type()
'torch.FloatTensor'
>>> a = np.zeros(2,dtype=np.float64)
>>> a = torch.tensor(a)
>>> a.type()
'torch.DoubleTensor'
1.2. 基本操作
# 创建一个3x3的随机张量
tensor = torch.rand(3, 3)
print("随机张量:", tensor)
# 获取张量的形状
print("张量形状:", tensor.shape)
# 获取张量的数据类型
print("张量数据类型:", tensor.dtype)
# 张量的加法操作
tensor_sum = tensor + tensor
print("张量相加:", tensor_sum)
# 张量的索引操作
print("张量索引:", tensor[0, 0])
1.3. 数学运算
# 创建两个张量
tensor1 = torch.tensor([[1, 2], [3, 4]])
tensor2 = torch.tensor([[5, 6], [7, 8]])
# 加法
addition = torch.add(tensor1, tensor2)
# 矩阵乘法
matrix_product = torch.matmul(tensor1, tensor2)
# 元素-wise 乘法
elementwise_product = torch.mul(tensor1, tensor2)
1.4. 形状操作
- 改变形状
tensor = torch.tensor([[1, 2, 3], [4, 5, 6]])
reshaped_tensor = tensor.view(3, 2)
reshaped_tensor2 = tensor.reshape(3, 2)
>>> tensor([[1, 2],
[3, 4],
[5, 6]])
- squeeze() 和 unsqueeze()
squeeze()方法用于删除大小为1的维度,而unsqueeze()方法用于在指定位置插入大小为1的维度。
tensor = torch.tensor([[1, 2, 3]])
# 使用squeeze删除维度
squeezed_tensor = tensor.squeeze()
# 使用unsqueeze插入维度
unsqueeze_tensor = tensor.unsqueeze(0)
- 在多维张量的不同位置插入大小为1的维度
# 创建一个大小为(2, 3)的张量
tensor = torch.tensor([[1, 2, 3], [4, 5, 6]])
# 在索引0处插入一个大小为1的维度
unsqueeze_dim0 = tensor.unsqueeze(0)
# 在索引1处插入一个大小为1的维度
unsqueeze_dim1 = tensor.unsqueeze(1)
# 在索引2处插入一个大小为1的维度
unsqueeze_dim2 = tensor.unsqueeze(2)
# 在负索引位置插入一个大小为1的维度
unsqueeze_tensor = tensor.unsqueeze(-1)
结果:
- unsqueeze_dim0 的形状是 (1, 2, 3),在原始张量的最外层添加了一个维度。
- unsqueeze_dim1 的形状是 (2, 1, 3),在原始张量的第二个维度(行)上添加了一个维度。
- unsqueeze_dim2 的形状是 (2, 3, 1),在原始张量的最内层(列)添加了一个维度。
- unsqueeze_tensor 的形状是 (2, 3, 1),它在原始张量的最内层添加了一个维度。
unsqueeze() 方法在指定位置插入大小为1的维度,这在处理需要扩展维度的情况下非常有用。
- permute(),transpose(),t()
permute()方法用于重新排列张量的维度顺序,而transpose()方法用于交换张量的两个维度。
tensor = torch.randn(2, 3, 4)
# 使用permute重新排列维度顺序
permuted_tensor = tensor.permute(1, 2, 0)
# permute(1, 2, 0) 将其重新排列为 (3, 4, 2)
# 使用transpose交换维度
transposed_tensor = tensor.transpose(0, 1)
# 使用t转置张量
transposed_tensor = tensor.t()
- .t() 属性仅适用于二维张量(矩阵),用于交换矩阵的维度。
- 对于二维张量,.t() 是 transpose(0, 1) 的快捷方式,即交换第一维和第二维。
- 索引和切片
tensor = torch.tensor([[1, 2, 3], [4, 5, 6]])
# 索引
print("索引结果:", tensor[0, 1]) # 获取第一行第二列的元素
# 切片
print("切片结果:", tensor[:, 1]) # 获取第二列的所有元素
2. 微调 fine-tuning
在加载了预训练模型参数之后,需要finetuning模型,可以使用不同的方式finetune:
- 局部微调:
加载了模型参数后,只想调节最后几层,其它层不训练,也就是不进行梯度计算,pytorch提供的requires_grad使得对训练的控制变得非常简单。
model = torchvision.models.resnet18(pretrained=True)
for param in model.parameters():
param.requires_grad = False
# 仅解冻最后的几层用于微调
for param in model.layer4.parameters():
param.requires_grad = True
# 替换最后的全连接层,因为原始模型的最后一层是针对原始数据集的类别
# 假设我们有一个新的数据集,有10个类别
num_ftrs = model.fc.in_features
model.fc = torch.nn.Linear(num_ftrs, 10) # 替换为新的全连接层,适应新的数据集
# 定义损失函数和优化器
criterion = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=0.001, momentum=0.9)
- 全局微调
对全局微调时,只不过我们希望改换过的层和其他层的学习速率不一样,这时候把其它层和新层在optimizer中单独赋予不同的学习速率。
model = torchvision.models.resnet18(pretrained=True)
for param in model.parameters():
param.requires_grad = False
# 替换最后的全连接层,因为原始模型的最后一层是针对原始数据集的类别
# 假设我们有一个新的数据集,有10个类别
num_ftrs = model.fc.in_features
model.fc = torch.nn.Linear(num_ftrs, 10) # 替换为新的全连接层,适应新的数据集
optimizer = optim.SGD(model.fc.parameters(), lr=1e-2, momentum=0.9)
3. 创建一个简单的神经网络模型
import torch
import torch.nn as nn
# 定义一个简单的全连接神经网络模型
class SimpleNN(nn.Module):
def __init__(self):
super(SimpleNN, self).__init__()
self.fc1 = nn.Linear(10, 5) # 输入维度为10,输出维度为5
self.fc2 = nn.Linear(5, 2) # 输入维度为5,输出维度为2
def forward(self, x):
x = torch.relu(self.fc1(x))
x = self.fc2(x)
return x
# 创建一个模型实例
model = SimpleNN()
print(model)
super()
是一个内置函数,用于调用父类的方法。在 Python 中,当你创建一个子类并且想要调用父类的方法时,可以使用super()
函数。- 在这个例子中,
super(SimpleNN, self).__init__()
的作用是调用SimpleNN
类的父类(通常是nn.Module
)的构造函数__init__()
方法。通过这种方式,你可以确保在创建SimpleNN
实例时,同时也会执行其父类的初始化操作,以确保所有必要的设置都已经完成。
4. 训练一个简单的神经网络模型
import torch
import torch.nn as nn
import torch.optim as optim
# 创建数据集
X_train = torch.rand(100, 10) # 100个样本,每个样本10个特征
y_train = torch.randint(0, 2, (100,)) # 二分类标签,0或1
# 定义模型、损失函数和优化器
model = SimpleNN()
criterion = nn.CrossEntropyLoss() # 交叉熵损失函数
optimizer = optim.SGD(model.parameters(), lr=0.01) # 随机梯度下降优化器
# 训练模型
num_epochs = 10
for epoch in range(num_epochs):
optimizer.zero_grad() # 梯度清零
outputs = model(X_train) # 模型前向传播
loss = criterion(outputs, y_train) # 计算损失
loss.backward() # 反向传播
optimizer.step() # 更新参数
print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {loss.item():.4f}')