由于实在是在pytorch相关的题上吃了太多亏了,我觉得无论如何也该找机会学一下,于是便学了
环境配置
首先在终端输入nvcc --version
确认当前的cuda版本
前往pytorch官网下载对应版本的pytorch
创建测试文件,测试pytorch是否安装完毕且能识别到GPU
1 2 3 4 5 6 7 8 9 10
| import torch
print("Is GPU available:", torch.cuda.is_available())
print("Number of GPUs:", torch.cuda.device_count())
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为预测结果。
反向传播
通过链式法则计算损失函数对参数的梯度:
输出层误差:
隐藏层误差反向传播:
参数梯度:

权重更新
根据优化算法更新参数(以梯度下降为例):
最基本的单位元——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
x = torch.empty(2, 3)
rand_tensor = torch.rand(2, 3)
zeros_tensor = torch.zeros(2, 3, dtype=torch.long)
data_tensor = torch.tensor([[1, 2], [3, 4]])
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}") print(f"数据类型: {t.dtype}") print(f"存储设备: {t.device}") print(f"维度数: {t.ndim}") print(f"元素数量: {t.numel()}")
|
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 d = a * 2 e = torch.matmul(a, b)
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) flattened = t.flatten()
|
tensor与NumPy互转
1 2 3 4 5 6 7
| t = torch.ones(2, 2) n = t.numpy()
n = np.ones(3) t = torch.from_numpy(n)
|
GPU加速
1 2 3 4 5 6 7
| if torch.cuda.is_available(): device = torch.device("cuda") gpu_tensor = torch.ones(2, 2, device=device) 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)
|
常用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.nn
和torch.nn.functional
是构建神经网络的两个核心模块。它们提供了构建深度学习模型所需的各种层、函数和工具。
torch.nn模块
torch.nn
是PyTorch中用于构建神经网络的主要模块,提供了面向对象的接口。它包含各种预定义的层(Layer)和模型容器。
常用神经网络层
1. 线性层(全连接层)
线性层(也称为全连接层)是神经网络中最基础的层类型,负责存储和操作层与层之间的权重矩阵与偏置向量。它在神经网络中扮演着核心角色,实现了输入特征到输出特征的线性变换。
1 2 3 4 5 6 7 8 9
| import torch.nn as nn
linear_layer = nn.Linear(10, 5)
input_data = torch.randn(3, 10) output = linear_layer(input_data) print(output.shape)
|
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
conv_layer = nn.Conv2d( in_channels=3, out_channels=16, kernel_size=3, stride=1, padding=1, dilation=1, groups=1, bias=True, padding_mode='zeros' )
input_data = torch.randn(4, 3, 32, 32)
output = conv_layer(input_data) print("输出形状:", output.shape)
|
3. 循环神经网络层
1 2 3 4 5 6 7
| lstm = nn.LSTM(input_size=100, hidden_size=50, num_layers=2, batch_first=True)
sequence = torch.randn(5, 10, 100) output, (hn, cn) = lstm(sequence) print(output.shape)
|
4. 其他常用层
1 2 3 4 5 6 7 8 9 10 11
| dropout = nn.Dropout(0.5)
batch_norm = nn.BatchNorm2d(16)
max_pool = nn.MaxPool2d(2, stride=2)
embedding = nn.Embedding(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) self.relu = nn.ReLU() self.pool = nn.MaxPool2d(2, 2) self.fc = nn.Linear(16 * 15 * 15, 10) 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) 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_out = F.relu(x)
sigmoid_out = F.sigmoid(x)
softmax_out = F.softmax(x, dim=1)
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_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) def forward(self, x): x = self.conv1(x) 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
|
模型训练流程概览
结合nn
和functional
的完整训练流程:
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) 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 import torch.nn as nn import torch.nn.functional as F from torchinfo import summary from PIL import Image import numpy as np
class SimpleCNN(nn.Module): def __init__(self): """ 模型初始化函数,定义网络层结构 """ super(SimpleCNN, self).__init__() self.conv1 = nn.Conv2d(in_channels=1, out_channels=32, kernel_size=3, padding=1) self.conv2 = nn.Conv2d(in_channels=32, out_channels=64, kernel_size=3, padding=1) self.fc1 = nn.Linear(3136, 128) self.fc2 = nn.Linear(128, 10)
def forward(self, x): """ 前向传播函数,定义数据在网络中的流动路径 x: 输入张量,形状应为 [batch_size, 1, height, width] """ x = self.conv1(x) 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) x = self.fc1(x) x = F.relu(x) x = self.fc2(x) return x
model = torch.load("password.pt", weights_only=False) model.eval()
for i in range(14): img = Image.open(f"flag/{i}.bmp").convert('L') summary(model, [1, 1, 28, 28]) inp = torch.tensor(np.array(img), dtype=torch.float32).unsqueeze(0) output = model(inp) ans = torch.argmax(output).item() print(ans, end='')
|
从目前的角度来看,并不算很难写的代码,但对于从未接触过的人来说,基本没有写出来的可能性( ´•̥̥̥ω•̥̥̥` )