全连接神经网络完全指南:从零到PyTorch实战 一、从最简单的线性关系说起:y = wx + b 1.1 理解基本公式 想象你在预测地铁到站时间,发现一个规律:时间 = 速度 × 距离 + 基础时间 。这就是最简单的神经网络!
1 2 3 4 5 6 7 def forward (x ): return x * w x_data = [1 , 2 , 3 ] y_data = [2 , 4 , 6 ]
但问题来了:我怎么知道w应该是多少? 这就需要”学习”!
1.2 什么是损失(Loss) 损失就是”打分系统”,告诉你预测得有多烂:
1 2 3 4 5 def cost (x_in, y_in ): loss = 0 for x, y in zip (x_in, y_in): loss += (y - forward(x))**2 return loss / len (x_in)
核心思想 :预测值和真实值差距越大,loss越大,说明你的w选得很糟糕。
二、如何调整参数:梯度下降 2.1 梯度是什么 梯度就是”指南针”,告诉你:
往哪个方向调整w,loss会下降
调整幅度应该多大
1 2 3 4 5 def gradient (x_in, y_in ): grad = 0 for x, y in zip (x_in, y_in): grad += 2 * x * (w * x - y) return grad / len (x_in)
数学本质 :对损失函数求偏导数 → ∂Loss/∂w
2.2 手动梯度下降循环 1 2 3 4 5 6 w = 4 for epoch in range (100 ): loss = cost(x_data, y_data) grad = gradient(x_data, y_data) w = w - 0.01 * grad print (f'训练轮数{epoch} , w={w} , loss={loss} ' )
结果 :w从4逐渐收敛到2,loss从18降到0.00009 ✅
三、PyTorch自动梯度:告别手动求导 3.1 使用requires_grad自动追踪 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import torchx = torch.tensor([10.0 ], requires_grad=True ) lr = 0.1 for i in range (20 ): y = x*x - 4 *x y.backward() print (f'第{i} 步x的梯度为{x.grad} ' ) with torch.no_grad(): x -= x.grad * lr x.grad.zero_()
关键点 :
backward():自动计算所有梯度
no_grad():更新参数时不记录梯度(节省内存)
zero_():必须清零,否则梯度会累加
四、扩展到多维:矩阵运算 4.1 为什么需要矩阵 现实中,输入不是一个数字,而是很多特征(速度、距离、角度…):
1 2 3 4 5 6 7 8 9 x = torch.randn(1 , 3 ) W = torch.randn(3 , 5 , requires_grad=True ) b = torch.randn(5 , requires_grad=True ) y_manual = torch.matmul(x, W) + b print (f"输出形状: {y_manual.shape} " )
4.2 工业标准:nn.Linear PyTorch封装好了线性层:
1 2 3 4 5 6 7 8 9 10 import torch.nn as nnlinear_layer = nn.Linear(in_features=3 , out_features=5 ) y_auto = linear_layer(x) print (f"权重形状: {linear_layer.weight.shape} " ) print (f"偏置形状: {linear_layer.bias.shape} " )
五、构建多层神经网络 5.1 激活函数:打破线性 如果只堆叠线性层,相当于 y = W3(W2(W1x)) = 还是线性!
解决方案 :加入非线性激活函数(ReLU)
1 2 3 relu = nn.ReLU() print (f"ReLU(-5): {relu(torch.tensor([-5.0 ]))} " ) print (f"ReLU(5): {relu(torch.tensor([5.0 ]))} " )
ReLU规则:负数变0,正数保持不变。
5.2 完整网络定义 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class ObstacleNet (nn.Module): def __init__ (self ): super ().__init__() self .layer1 = nn.Linear(784 , 256 ) self .act = nn.ReLU() self .layer2 = nn.Linear(256 , 64 ) self .layer3 = nn.Linear(64 , 10 ) def forward (self, x ): x = self .layer1(x) x = self .act(x) x = self .layer2(x) x = self .act(x) out = self .layer3(x) return out model = ObstacleNet().cuda()
网络结构 :784 → 256 → 64 → 10(比如识别0-9数字)
六、训练五步法 6.1 准备工作 1 2 3 4 5 6 7 8 9 10 11 import torch.optim as optimcriterion = nn.CrossEntropyLoss() optimizer = optim.Adam(model.parameters(), lr=0.001 ) inputs = torch.randn(8 , 784 ).cuda() targets = torch.randint(0 , 10 , (8 ,)).cuda()
6.2 训练循环(核心!) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 for i in range (100 ): optimizer.zero_grad() outputs = model(inputs) loss = criterion(outputs, targets) loss.backward() optimizer.step() print (f"第{i} 轮 Loss: {loss.item():.4 f} " )
为什么要清零梯度? PyTorch默认梯度累加,不清零会导致梯度爆炸!
七、关于你提到的问题 7.1 Softmax vs Sigmoid 应用场景 :
Softmax :多分类(10个数字选1个)→ CrossEntropyLoss
Sigmoid :二分类(障碍物/正常)→ BCELoss
1 2 3 4 5 6 7 softmax = nn.Softmax(dim=1 ) probs = softmax(torch.randn(1 , 10 )) sigmoid = nn.Sigmoid() probs = sigmoid(torch.randn(1 , 10 ))
7.2 SGD vs Adam
优化器
特点
学习率
适用场景
SGD
简单粗暴
需手动调
简单任务、理论研究
Adam
自适应学习率
0.001(黄金值)
99%的深度学习任务
1 2 3 4 5 optimizer_sgd = optim.SGD(model.parameters(), lr=0.01 , momentum=0.9 ) optimizer_adam = optim.Adam(model.parameters(), lr=0.001 )
八、PyTorch张量操作总结 8.1 创建张量 1 2 3 4 5 6 7 8 9 10 11 12 13 a = torch.zeros(2 , 3 ) b = torch.ones(2 , 3 ) c = torch.randn(2 , 3 ) import numpy as npnumpy_array = np.array([[1 , 2 ], [3 , 4 ]]) tensor = torch.from_numpy(numpy_array) device = torch.device("cuda" if torch.cuda.is_available() else "cpu" ) d = torch.randn(2 , 3 , device=device)
8.2 张量运算 1 2 3 4 5 6 7 8 9 10 11 a = torch.randn(2 , 3 ) b = torch.randn(2 , 3 ) print (a * b) print (a + b) print (a.sum ()) print (a.max ()) print (a[1 , 2 ])
8.3 设备转移(CPU ↔ GPU) 1 2 3 4 5 6 7 8 9 10 cpu_tensor = torch.randn(2 , 3 , 4 , 4 ) if torch.cuda.is_available(): gpu_tensor = cpu_tensor.to('cuda' ) result = gpu_tensor + 100 back_to_cpu = result.to('cpu' )
重要 :形状 [2, 3, 4, 4] 表示:
2张图片
每张3个通道(RGB)
分辨率4×4(16个像素)
8.4 梯度相关操作 1 2 3 4 5 6 7 8 9 10 11 12 13 14 x = torch.tensor([1.0 ], requires_grad=True ) y = x * 2 y.backward() print (x.grad) with torch.no_grad(): x -= x.grad * 0.1 x.grad.zero_()
8.5 数据加载(DataLoader) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 from torch.utils.data import Dataset, DataLoaderclass SubwayDataset (Dataset ): def __init__ (self, num_samples=1000 ): self .x = torch.randn(num_samples, 784 ) self .y = torch.randint(0 , 2 , (num_samples,)) def __len__ (self ): return len (self .x) def __getitem__ (self, idx ): return self .x[idx], self .y[idx] train_dataset = SubwayDataset(1000 ) train_loader = DataLoader(train_dataset, batch_size=32 , shuffle=True ) for batch_x, batch_y in train_loader: pass
九、完整训练流程速查表 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 model = ObstacleNet().cuda() criterion = nn.CrossEntropyLoss() optimizer = optim.Adam(model.parameters(), lr=0.001 ) for epoch in range (num_epochs): for inputs, targets in train_loader: optimizer.zero_grad() outputs = model(inputs) loss = criterion(outputs, targets) loss.backward() optimizer.step() print (f"Epoch {epoch} , Loss: {loss.item()} " ) model.eval () with torch.no_grad(): predictions = model(test_inputs)
为什么要清零梯度? PyTorch默认梯度累加,不清零会导致梯度爆炸!
十、常见操作对比表
操作
代码
说明
创建中间层
self.layer1 = nn.Linear(784, 256)
784输入 → 256输出
梯度清零
optimizer.zero_grad()
每次训练前必须!
前向传播
outputs = model(inputs)
等价于 model.forward(inputs)
计算损失
loss = criterion(outputs, targets)
分类用CrossEntropyLoss
反向传播
loss.backward()
自动计算所有梯度
更新参数
optimizer.step()
根据梯度更新W和b
禁用梯度
with torch.no_grad():
预测或参数更新时用
清空梯度
x.grad.zero_()
手动清零(优化器会自动)
切换设备
tensor.to('cuda')
CPU ↔ GPU
评估模式
model.eval()
关闭Dropout等训练特性