Daily AI 20251209 (PyTorch基础回顾1)

张量创建
import torch
import numpy as np

# 创建一个 2x3 的全 0 张量
a = torch.zeros(2, 3)
print(a)

# 创建一个 2x3 的全 1 张量
b = torch.ones(2, 3)
print(b)

# 创建一个 2x3 的随机数张量
c = torch.randn(2, 3)
print(c)

# 从 NumPy 数组创建张量
numpy_array = np.array([[1, 2], [3, 4]])
tensor_from_numpy = torch.from_numpy(numpy_array)
print(tensor_from_numpy)

# 在指定设备(CPU/GPU) 上创建张量
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
d = torch.randn(2, 3, device=device)
print(d)

张量常用操作
# 常用张量操作
# 张量逐元素相加
e = torch.tensor([[1,2,3],[4,5,6]])
f = torch.tensor([[1,2,3],[4,5,6]])
print(e + f)

# 逐元素乘法
print(e * f)

# 张量的转置
g = torch.randn(3, 2)
print(g,'\n',g.t()) # 或者 g.transpose(0, 1)

# 张量的形状
print(g.shape) # 返回张量形状

# torch.randn() 生出标准正态分布数据
# 基于独立变量方差可加性 h.sum() 服从N(0,1000^2)
h = torch.randn(1000,1000,dtype=torch.float64)
print(h.sum())
自动微分

PyTorch的张量支持自动微分,这是深度学习中的关键特性。创建一个需要梯度的张量时,PyTorch可以自动计算其梯度(采用动态图):

# 创建一个需要梯度的张量
tensor_requires_grad = torch.tensor([1.0], requires_grad=True)
# 进行张量操作
tensor_result = tensor_requires_grad * torch.tensor(2.0)
# 计算梯度
tensor_result.backward()
print(tensor_requires_grad.grad) # 输出梯度
创建NN

SimpleNN 是一个子类(Child Class),它继承自 nn.Module 父类

nn.Module 是所有 PyTorch 神经网络模块(Models and Layers)的基类。当创建一个 PyTorch 模型时,必须确保 nn.Module 中定义的内部机制能够被正确设置和初始化

nn.Module 负责跟踪模型中所有的权重(weight)和偏置(bias)参数,以便它们能被正确地注册到模型的状态字典(state_dict)中。

nn.Module 管理所定义的 self.fc1 和 self.fc2 等子层,使得 PyTorch 能够自动识别这些层并进行统一管理(例如,当调用 model.to(device) 时,所有子层都会被移动到指定的设备上)。

import torch
import torch.nn as nn
# 定义一个简单的神经网络模型
class SimpleNN(nn.Module):
    def __init__(self):
        super(SimpleNN, self).__init__()
        # 定义一个输入层到隐藏层的全连接层
        self.fc1 = nn. Linear(2, 2) # 输入 2 个特征,输出 2 个特征
        # 定义一个隐藏层到输出层的全连接层
        self.fc2 = nn.Linear(2, 1) # 输入 2 个特征,输出 1 个预测
    def forward(self, x):
        # 前向传播过程
        x = torch.relu(self.fc1(x)) # 使用ReLU 激活函数
        x = self.fc2(x) # 输出层
        return x
# 创建模型实例
model = SimpleNN()
# 打印模型
print(model)

'''
Python 实例化 SimpleNN 时调用自定义 __init__。
执行 super().__init__(),代码流暂时切换到 nn.Module 的 __init__ 方法内部。
nn.Module 完成所有基础设置:创建内部列表、字典、设置默认属性等。
代码流回到 SimpleNN 的 __init__ 方法
父类已经准备好了环境来接收和管理这些子模块
'''

super() 是 Python 中的内建函数,提供了一种访问父类(以及祖父类、更上层的基类等)的方法,而无需明确使用父类的名称。

在python3中,解释器能够知道当前所在的类(SimpleNN)和实例(self),所以super(SimpleNN, self).init()可以写成:

super().__init__()

super() 实际上返回了一个临时的代理对象(proxy object)。这个代理对象充当了当前子类和它所有父类之间的桥梁。

当在这个代理对象上调用方法(例如 .init())时:它不会在当前子类 SimpleNN 中查找 init。它会跳过 SimpleNN,直接在其父类(即 nn.Module)中查找并执行 init 方法。

因此,当定义 class SimpleNN(nn.Module): 时,子类(SimpleNN)负责定义网络的结构,而父类负责处理底层机制。

加载自定义dataset

torch.utils.data.Dataset 是一个抽象类,实现为提供的数据源中创建数据集。
需要继承该类并实现以下两个方法:

len(self):返回数据集中的样本数量。
getitem(self, idx):通过索引返回一个样本。

import torch
from torch.utils.data import Dataset
# 自定义数据集类
class MyDataset(Dataset):
    def __init__(self, X_data, Y_data):
        """
        初始化数据集, X_data 和 Y_data 是两个列表或数组
        X_data:输入特征
        Y_data:目标标签
        """
        self.X_data = X_data
        self.Y_data = Y_data
    def __len__(self):
        """返回数据集的大小"""
        return len(self.X_data)
    def __getitem__(self, idx):
        """返回指定索引的数据"""
        x = torch.tensor(self.X_data[idx], dtype=torch.float32)
        y = torch.tensor(self.Y_data[idx], dtype=torch.float32)
        return x, y
# 示例数据
X_data = [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10], [11, 12]] # 输入特征
Y_data = [1, 0, 1, 0, 1, 0] # 目标标签
# 创建数据集实例
dataset = MyDataset(X_data, Y_data)

# 调用 __len(self)__ 与 __getitem__(self,idx)
print(dataset.__getitem__(1) , '\n', dataset.__len__() )
加载数据

DataLoader 允许批量读取数据并进行多线程加载,从而提高训练效率

from torch.utils.data import DataLoader
# 创建 DataLoader 实例,batch_size 设置每次加载的样本数量
dataloader = DataLoader(dataset, batch_size=2, shuffle=True)
# 打印加载的数据
for epoch in range(1):
    for batch_idx, (inputs, labels) in enumerate(dataloader):
        print(f'Batch {batch_idx + 1}:')
        print(f'Inputs: {inputs}')
        print(f'Labels: {labels}')
'''
示例输出:
Batch 1:
Inputs: tensor([[ 3.,  4.],
        [11., 12.]])
Labels: tensor([0., 0.])
Batch 2:
Inputs: tensor([[7., 8.],
        [1., 2.]])
Labels: tensor([0., 1.])
Batch 3:
Inputs: tensor([[ 5.,  6.],
        [ 9., 10.]])
Labels: tensor([1., 1.])
'''
enumerate与range

enumerate在遍历(循环)列表或其他可迭代对象(如前所定义的 dataloader)时,同时获取元素的索引(下标)和元素本身。相比于range()更简洁。

enumerate(iterable, start=0)
'''
iterable: 一个可迭代对象(如列表 list、元组 tuple、字符串 string 等)。
start: (可选)计数开始的数字,默认为 0。
enumerate返回一个枚举对象(enumerate object)。
这是一个迭代器,每次生成一个包含 (index, value) 的元组。
'''

示例

fruits = ["Apple", "Banana", "Cherry"]

for i in range(len(fruits)):
    print(i, fruits[i])
'''
0 Apple
1 Banana
2 Cherry
'''
for index, fruit in enumerate(fruits):
    print(f"idx: {index}, val: {fruit}")
'''
idx: 0, val: Apple
idx: 1, val: Banana
idx: 2, val: Cherry
'''

enumerate 允许通过 start 参数自定义计数的起始值。这在生成排名、行号等场景非常有用

runners = ["Usain", "Tyson", "Asafa"]

# start=1 表示索引从 1 开始
for rank, name in enumerate(runners, start=1):
    print(f"第 {rank} 名: {name}")

# 输出:
# 第 1 名: Usain
# 第 2 名: Tyson
# 第 3 名: Asafa

enumerate 也可以用于其它对象:

# 字符串
word = "Python"
for i, char in enumerate(word):
    print(i, char)
# 输出:
# 0 P
# 1 y

# 字典
data = {'name': 'Alice', 'age': 25}

for i, key in enumerate(data):
    print(f"{i}: {key}")
# 输出:
# 0: name
# 1: age

# 元组
name_age_tuple = ( ('Bob', 24), ('Alice', 28) )

for idx, val in enumerate(name_age_tuple):
    print(f'Index:{idx}')
    print(f'Value: {val}')
'''
Index:0
Value: ('Bob', 24)
Index:1
Value: ('Alice', 28)
'''

for idx, (name,age) in enumerate(name_age_tuple):
    print(f'Index:{idx}')
    print(f'Name: {name}')
    print(f'Age: {age}')
'''
Index:0
Name: Bob
Age: 24
Index:1
Name: Alice
Age: 28
'''

注意,enumerate 返回的是一个迭代器对象,打印它只返回对象地址

colors = ["Red", "Green", "Blue"]
enum_obj = enumerate(colors, start=10)

# 直接打印对象
print(enum_obj) 
# 输出: <enumerate object at 0x...>

# 转换为列表查看结构
print(list(enum_obj)) 
# 输出: [(10, 'Red'), (11, 'Green'), (12, 'Blue')]

对于上面代码的 for batch_idx, (inputs, labels) in enumerate(dataloader):

for batch_idx, (inputs, labels) in enumerate(dataloader):
'''
dataloader 的工作:它是一个迭代器。每次迭代,它会从 dataset 中取出一组数据(根据 batch_size=2,每次取 2 个样本)。它返回的数据结构是:(Tensor([x1, x2]), Tensor([y1, y2])),也就是代码中的 (inputs, labels)。

enumerate 的工作:它将 dataloader 返回的数据包裹起来,加上一个索引。它返回的结构是:(索引, dataloader产生的数据)。具体形式为:(0, (inputs, labels))。

for 循环的解包:Python 将 enumerate 产生的元组分配给变量:第一个元素(索引) $\rightarrow$ 赋值给 batch_idx第二个元素(数据元组) $\rightarrow$ 赋值给 (inputs, labels)
'''
  • 注意到:(inputs, labels) 是为了匹配 dataloader 返回的元组结构
  • enumerate的性质帮助对于每个batch添加索引,便于了解当前训练进程
预处理、数据增强
import torchvision.transforms as transforms
# 定义数据预处理的流水线
transform = transforms.Compose([
    transforms.Resize((128, 128)), # 将图像调整为 128x128
    transforms.ToTensor(), # 将图像转换为张量
    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229,0.224,0.225
])

transforms.Compose():将多个变换操作组合在一起。
transforms.Resize():调整图像大小。
transforms.ToTensor():将图像转换为 PyTorch 张量,值会被归一化到 [0,1]范围。
transforms.Normalize():标准化图像数据,通常使用预训练模型时需要进行标准化处理。

transform = transforms.Compose([
    transforms.RandomHorizontalFlip(), # 随机水平翻转
    transforms.RandomRotation(30), # 随机旋转 30 度
    transforms.RandomResizedCrop(128), # 随机裁剪并调整为 128x128
    transforms.ToTensor(),
    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
])
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

idkmn_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值