pytorch入门笔记

Wang1r Lv3

由于实在是在pytorch相关的题上吃了太多亏了,我觉得无论如何也该找机会学一下,于是便学了

环境配置

首先在终端输入nvcc --version确认当前的cuda版本

前往pytorch官网下载对应版本的pytorch

创建测试文件,测试pytorch是否安装完毕且能识别到GPU

1
2
3
4
5
6
7
8
9
10
import torch

# 检查 GPU 是否可用
print("Is GPU available:", torch.cuda.is_available())

# 获取 GPU 设备数量
print("Number of GPUs:", torch.cuda.device_count())

# 获取当前 GPU 设备名称
print("Current GPU device name:", torch.cuda.get_device_name(0))

可以看到pytorch可以正常使用且识别到了GPU

然后就可以正式开始学习pytorch了

基础知识

在学习pytorch之前,首先需要对神经网络的知识有基本的了解

人工神经元模型

人工神经元是神经网络的基本单元,其数学模型为:

其中:

  • x_i为输入特征(i=1,2,…,n)
  • w_i为对应权重
  • b为偏置项
  • f(⋅)为激活函数

神经网络结构

多层神经网络由输入层、隐藏层和输出层构成。若定义:

  • L:网络总层数(含输出层)
  • a(l):第 l层输出向量(a(0)=x)
  • W(l):第 l 层权重矩阵(连接 l−1 层到 l 层)
  • b(l):第 l 层偏置向量

则网络可表示为层级变换:

激活函数

引入非线性的关键函数,常见类型:

  • Sigmoid

  • ReLU

  • Softmax(输出层):

优化算法

最小化损失函数 L 的迭代方法(如梯度下降):

其中:

  • θ:模型参数(权重/偏置)
  • η:学习率
  • t:迭代步数

前向传播

数据从输入层流向输出层的计算过程:

输出y为预测结果。

反向传播

通过链式法则计算损失函数对参数的梯度:

  1. 输出层误差:

  2. 隐藏层误差反向传播:

  3. 参数梯度:

权重更新

根据优化算法更新参数(以梯度下降为例):

最基本的单位元——tensor

PyTorch的核心数据结构是tensor(张量),类似于NumPy的ndarray,但支持GPU加速计算。tensor可以看作多维数组,是神经网络中数据的基本载体。

创建tensor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import torch

# 创建未初始化tensor
x = torch.empty(2, 3) # 2行3列

# 创建随机初始化tensor
rand_tensor = torch.rand(2, 3) # [0,1)均匀分布

# 创建全零tensor
zeros_tensor = torch.zeros(2, 3, dtype=torch.long)

# 从数据直接创建
data_tensor = torch.tensor([[1, 2], [3, 4]])

# 从NumPy数组创建
import numpy as np
numpy_array = np.array([[5, 6], [7, 8]])
tensor_from_np = torch.from_numpy(numpy_array)

tensor基本属性

1
2
3
4
5
6
7
t = torch.rand(2, 3, device="cuda")

print(f"形状: {t.shape}") # torch.Size([2, 3])
print(f"数据类型: {t.dtype}") # torch.float32
print(f"存储设备: {t.device}") # cuda:0
print(f"维度数: {t.ndim}") # 2
print(f"元素数量: {t.numel()}") # 6

tensor基本运算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
a = torch.tensor([1, 2, 3])
b = torch.tensor([4, 5, 6])

# 算术运算
c = a + b # tensor([5, 7, 9])
d = a * 2 # tensor([2, 4, 6])
e = torch.matmul(a, b) # 点积: 1*4 + 2*5 + 3*6 = 32

# 索引切片
t = torch.rand(4, 4)
print(t[0, 1]) # 第一行第二列元素
print(t[:, 1]) # 所有行第二列
print(t[1:3, :]) # 第二到三行所有列

# 形状变换
t = torch.arange(12)
reshaped = t.view(3, 4) # 改为3×4矩阵
flattened = t.flatten() # 展平为一维

tensor与NumPy互转

1
2
3
4
5
6
7
# tensor转NumPy
t = torch.ones(2, 2)
n = t.numpy() # 共享内存,修改t会影响n

# NumPy转tensor
n = np.ones(3)
t = torch.from_numpy(n) # 同样共享内存

GPU加速

1
2
3
4
5
6
7
# 将tensor移动到GPU
if torch.cuda.is_available():
device = torch.device("cuda")
gpu_tensor = torch.ones(2, 2, device=device)
# 或使用to方法
cpu_tensor = torch.ones(3, 3)
gpu_tensor = cpu_tensor.to(device)

自动求导机制

PyTorch的核心特性是自动微分,通过requires_grad属性启用:

1
2
3
4
5
x = torch.tensor(2.0, requires_grad=True)
y = x**2 + 3*x + 1

y.backward() # 自动计算梯度
print(x.grad) # dy/dx = 2x+3 = 7

常用tensor操作

操作 函数 说明
形状变换 view(), reshape() 改变tensor形状
维度调整 squeeze(), unsqueeze() 删除/添加维度
拼接 cat(), stack() 连接多个tensor
分割 split(), chunk() 分割tensor
数学运算 add(), mul(), matmul() 基本数学运算
统计 sum(), mean(), max() 统计计算

神经网络核心模块——torch.nn与torch.nn.functional

在PyTorch中,torch.nntorch.nn.functional是构建神经网络的两个核心模块。它们提供了构建深度学习模型所需的各种层、函数和工具。

torch.nn模块

torch.nn是PyTorch中用于构建神经网络的主要模块,提供了面向对象的接口。它包含各种预定义的层(Layer)和模型容器。

常用神经网络层

1. 线性层(全连接层)

线性层(也称为全连接层)是神经网络中最基础的层类型,负责存储和操作层与层之间的权重矩阵偏置向量。它在神经网络中扮演着核心角色,实现了输入特征到输出特征的线性变换。

1
2
3
4
5
6
7
8
9
import torch.nn as nn

# 定义线性层:输入特征数=10,输出特征数=5
linear_layer = nn.Linear(10, 5)

# 使用示例
input_data = torch.randn(3, 10) # 批量大小3,特征数10
output = linear_layer(input_data)
print(output.shape) # torch.Size([3, 5])

2. 卷积层

卷积层是卷积神经网络(CNN)的核心组件,专门用于处理具有网格结构的数据(如图像、音频、时间序列等)。它通过局部感受野和权重共享机制,高效地提取空间特征。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import torch
import torch.nn as nn

# 创建卷积层:输入通道=3,输出通道=16,卷积核3×3,步长=1,填充=1
conv_layer = nn.Conv2d(
in_channels=3, # 输入通道数(RGB图像为3)
out_channels=16, # 输出通道数/卷积核数量
kernel_size=3, # 卷积核尺寸(可设为整数或元组)
stride=1, # 卷积步长
padding=1, # 填充大小(保持空间分辨率)
dilation=1, # 空洞率(默认为1)
groups=1, # 分组卷积(默认为1)
bias=True, # 是否使用偏置项
padding_mode='zeros' # 填充模式(默认零填充)
)

# 生成输入数据:批量大小4,通道3,高度32,宽度32
input_data = torch.randn(4, 3, 32, 32) # 形状 [batch, channels, height, width]

# 前向传播
output = conv_layer(input_data)
print("输出形状:", output.shape) # torch.Size([4, 16, 32, 32])

3. 循环神经网络层

1
2
3
4
5
6
7
# LSTM层:输入大小=100,隐藏层大小=50,层数=2
lstm = nn.LSTM(input_size=100, hidden_size=50, num_layers=2, batch_first=True)

# 使用示例
sequence = torch.randn(5, 10, 100) # 批量大小5,序列长度10,特征数100
output, (hn, cn) = lstm(sequence)
print(output.shape) # torch.Size([5, 10, 50])

4. 其他常用层

1
2
3
4
5
6
7
8
9
10
11
# Dropout层:随机丢弃50%神经元
dropout = nn.Dropout(0.5)

# 批量归一化层
batch_norm = nn.BatchNorm2d(16)

# 最大池化层
max_pool = nn.MaxPool2d(2, stride=2)

# 嵌入层(用于NLP)
embedding = nn.Embedding(1000, 128) # 词汇表大小1000,嵌入维度128

模型容器:Module

nn.Module是所有神经网络模块的基类。自定义网络应继承此类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class SimpleCNN(nn.Module):
def __init__(self):
super(SimpleCNN, self).__init__()
self.conv1 = nn.Conv2d(3, 16, 3) # 输入3通道,输出16通道,卷积核3×3
self.relu = nn.ReLU()
self.pool = nn.MaxPool2d(2, 2)
self.fc = nn.Linear(16 * 15 * 15, 10) # 假设输入图像32×32,池化后16×16

def forward(self, x):
x = self.pool(self.relu(self.conv1(x)))
x = x.view(-1, 16 * 15 * 15) # 展平
x = self.fc(x)
return x

# 实例化模型
model = SimpleCNN()
print(model)

损失函数

torch.nn提供了多种损失函数:

1
2
3
4
5
6
7
8
9
10
11
# 分类任务常用损失函数
cross_entropy = nn.CrossEntropyLoss()

# 回归任务常用损失函数
mse_loss = nn.MSELoss()

# 使用示例
outputs = torch.randn(3, 5) # 3个样本,5个类别
targets = torch.tensor([1, 0, 4]) # 真实标签
loss = cross_entropy(outputs, targets)
print(loss)

torch.nn.functional模块

torch.nn.functional(通常简写为F)提供函数式接口,包含许多无状态的函数(即不包含可学习参数)。

激活函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import torch.nn.functional as F

x = torch.randn(2, 3)

# ReLU激活函数
relu_out = F.relu(x)

# Sigmoid激活函数
sigmoid_out = F.sigmoid(x)

# Softmax激活函数
softmax_out = F.softmax(x, dim=1)

# Tanh激活函数
tanh_out = F.tanh(x)

卷积操作

1
2
3
4
5
6
7
# 函数式卷积
input = torch.randn(1, 3, 32, 32)
weight = torch.randn(16, 3, 3, 3) # 输出通道×输入通道×高×宽
bias = torch.randn(16)

# 执行卷积(手动指定权重和偏置)
conv_out = F.conv2d(input, weight, bias, stride=1, padding=1)

池化操作

1
2
3
4
5
# 最大池化
max_pool_out = F.max_pool2d(input, kernel_size=2, stride=2)

# 平均池化
avg_pool_out = F.avg_pool2d(input, kernel_size=2, stride=2)

Dropout

1
2
# Dropout(训练时启用,评估时禁用)
dropout_out = F.dropout(input, p=0.5, training=True)

损失函数

1
2
3
4
5
# 函数式损失函数
loss = F.cross_entropy(outputs, targets)

# 等价于
loss = F.nll_loss(F.log_softmax(outputs, dim=1), targets)

nn与functional的区别与选择

特性 torch.nn torch.nn.functional
接口类型 面向对象(类) 函数式
包含状态 包含可学习参数 无状态
使用场景 包含参数的层(如Linear, Conv) 无参数操作(激活函数、损失等)
优点 模块化,易于管理参数 灵活,可直接在forward中使用
模型序列化 自动处理参数保存 需要手动处理参数
示例 self.conv = nn.Conv2d(...) F.relu(x)

混合使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class HybridModel(nn.Module):
def __init__(self):
super().__init__()
self.conv1 = nn.Conv2d(3, 16, 3)
self.conv2 = nn.Conv2d(16, 32, 3)
self.fc = nn.Linear(32 * 6 * 6, 10) # 假设输入32×32,经过两次池化后6×6

def forward(self, x):
# 使用nn模块的卷积层
x = self.conv1(x)
# 使用functional的激活函数和池化
x = F.relu(x)
x = F.max_pool2d(x, 2)

x = self.conv2(x)
x = F.relu(x)
x = F.max_pool2d(x, 2)

x = x.view(-1, 32 * 6 * 6)
x = self.fc(x)
return x

模型训练流程概览

结合nnfunctional的完整训练流程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import torch.optim as optim

# 实例化模型
model = HybridModel()

# 定义优化器
optimizer = optim.Adam(model.parameters(), lr=0.001)

# 训练循环
for epoch in range(10):
for inputs, labels in train_loader:
# 前向传播
outputs = model(inputs)

# 计算损失(使用functional)
loss = F.cross_entropy(outputs, labels)

# 反向传播
optimizer.zero_grad()
loss.backward()

# 更新参数
optimizer.step()

print(f"Epoch {epoch+1}, Loss: {loss.item():.4f}")

实例分析

之前软件安全赛在半决赛的时候,差一道手搓CNN的题就能进决赛,于是一直怀恨在心,所以就拿这道题练手

描述

各位员工:

为了提升公司的安全管理水平,从即日起,我司将引入AI技术对通行密码进行管理。相关的密码图片内容已整理并放入压缩包中,压缩包的密码将由各部门负责组织发放,请大家留意部门通知。

请注意公司内部AI模型的使用规范:
1.除最后一层外与池化层外其他隐藏层输出均需要通过激活函数
2.至少需要通过两次池化层
3.注意隐藏之间输出数据格式的匹配,必要时对数据张量进行重塑
4.为保证模型准确性,输入图片应转换为灰度图

感谢大家的配合与支持。如有疑问,请随时与人事部联系。

此致

附件

password.pt

flag—— 0.bmp

​ | — 1.bmp

​ | — …

​ |— 13.bmp

由于当时是线下赛,不能使用搜索和AI,所以很考验基本功手搓代码的能力。

给出的模型文件可以通过Netron查询

可以分析得到以下结果:

该CNN是一个序列结构,包含以下层(按顺序):

  • 两个卷积层(Conv2d):用于提取图像特征。
  • 一个最大池化层(MaxPool2d):用于降采样,减少特征图尺寸。
  • 两个全连接层(Linear):用于分类输出。

再结合题目描述,我们就可以进行如下代码的编写:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# 导入必要的库
import torch # PyTorch深度学习框架
import torch.nn as nn # 神经网络模块
import torch.nn.functional as F # 神经网络函数式接口
from torchinfo import summary # 模型结构可视化工具
from PIL import Image # 图像处理库
import numpy as np # 数值计算库

# 定义简单的CNN模型
class SimpleCNN(nn.Module):
def __init__(self):
"""
模型初始化函数,定义网络层结构
"""
super(SimpleCNN, self).__init__() # 调用父类构造函数

# 第一卷积层:输入1通道(灰度图),输出32通道,3x3卷积核,填充1保持尺寸不变
self.conv1 = nn.Conv2d(in_channels=1, out_channels=32, kernel_size=3, padding=1)

# 第二卷积层:输入32通道,输出64通道,3x3卷积核,填充1保持尺寸不变
self.conv2 = nn.Conv2d(in_channels=32, out_channels=64, kernel_size=3, padding=1)

# 第一全连接层:输入3136维特征,输出128维特征
# 3136 = 64通道 * 7高度 * 7宽度 (经过两次池化后尺寸)
self.fc1 = nn.Linear(3136, 128)

# 第二全连接层(输出层):输入128维特征,输出10个类别的概率
self.fc2 = nn.Linear(128, 10)

def forward(self, x):
"""
前向传播函数,定义数据在网络中的流动路径
x: 输入张量,形状应为 [batch_size, 1, height, width]
"""
# 第一卷积块
x = self.conv1(x) # 卷积操作 [batch, 32, 28, 28] (如果输入是28x28)
x = F.relu(x) # ReLU激活函数
x = F.max_pool2d(x, 2) # 2x2最大池化,尺寸减半 [batch, 32, 14, 14]

# 第二卷积块
x = self.conv2(x) # 卷积操作 [batch, 64, 14, 14]
x = F.relu(x) # ReLU激活函数
x = F.max_pool2d(x, 2) # 2x2最大池化,尺寸减半 [batch, 64, 7, 7]

# 展平操作:将多维特征图转换为一维向量
# x.view(-1) 会将所有元素展平为一个长向量,但会丢失批次信息
# 更好的做法是:x = x.view(x.size(0), -1) 这样会保留批次维度
x = x.view(-1) # 展平为单一向量 [batch_size * 64 * 7 * 7]

# 全连接层部分
x = self.fc1(x) # 全连接层1 [batch_size * 3136] -> [batch_size * 128]
x = F.relu(x) # ReLU激活函数
x = self.fc2(x) # 输出层 [batch_size * 128] -> [batch_size * 10]

return x

# 加载预训练模型
# weights_only=False 允许加载任意Python对象,但存在安全风险,仅用于可信来源
model = torch.load("password.pt", weights_only=False)
model.eval() # 设置为评估模式(关闭dropout等训练特定操作)

# 处理14张图像
for i in range(14):
# 加载并预处理图像
img = Image.open(f"flag/{i}.bmp").convert('L') # 打开图像并转换为灰度模式

# 打印模型结构信息(每次迭代都打印,可移到循环外优化性能)
# 参数:[1, 1, 28, 28] 表示批次大小1, 通道数1, 高度28, 宽度28
summary(model, [1, 1, 28, 28])

# 将图像转换为PyTorch张量
# 1. 转换为numpy数组
# 2. 转换为torch.Tensor
# 3. 添加一个维度作为通道维度 [1, 28, 28] -> [通道, 高, 宽]
inp = torch.tensor(np.array(img), dtype=torch.float32).unsqueeze(0)

# 模型预测
output = model(inp) # 前向传播获取输出

# 获取预测结果:取最大概率的类别索引
ans = torch.argmax(output).item() # .item()将单元素张量转为Python标量

# 打印结果(不换行)
print(ans, end='')

从目前的角度来看,并不算很难写的代码,但对于从未接触过的人来说,基本没有写出来的可能性( ´•̥̥̥ω•̥̥̥` )

  • 标题: pytorch入门笔记
  • 作者: Wang1r
  • 创建于 : 2025-06-26 10:17:20
  • 更新于 : 2025-06-27 21:17:10
  • 链接: https://wang1rrr.github.io/2025/06/26/pytorch入门笔记/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。