深度学习基础D3-D4
本文总结了神经网络中的关键概念:1)激活函数类型及选择(sigmoid/tanh/ReLU等),隐藏层推荐ReLU,输出层按任务选择;2)参数初始化方法(Xavier/Kaiming等);3)模型构建流程,包括定义层结构、初始化参数和前向传播实现。重点比较了不同激活函数的特性(如sigmoid易梯度消失,ReLU计算简单但可能神经元死亡),并演示了使用PyTorch构建包含自定义初始化、3层全连接
文章目录
Day3
01内容回顾
输入层:数据
输出层:目标(加权和)
隐藏层:加权和+激活
FC
Linear
激活函数:sigmoid tanh relu
sigmoid:二分类输出层,[0,1],梯度小,梯度消失(5层)
tanh:隐藏层,[-1,1]关于0对称,导数相对sigmoid大,更新速度快,迭代次数少。[-3,3]以外梯度为0
relu:隐藏层最多;小于0为0,大于0本身;导数小于0为0,大于0为1,不会梯度消失。
神经元死亡,缓解过拟合;大部分死亡,leakyrelu
相对于sigmoid计算简单计算量小。
02softmax
s o f t m a x ( z i ) = e z i ∑ j e z j softmax(z_i)=\frac{e^{z_i}}{\sum_j e^{z_j}} softmax(zi)=∑jezjezi
z i z_i zi:logits输出层的加权和
import torch
scores = torch.tensor([0.2, 0.02, 0.15, 0.15, 1.3, 0.5, 0.06, 1.1, 0.05, 3.75])
print(torch.softmax(scores, dim=0))
03其他激活函数
- 恒等激活
- 阶跃函数,小于0为0,大于0为1,导数为0
- logistics函数/sigmoid函数
- tanh
- relu
- 负半轴出现问题,leakyrelu,负半轴 0.01 x 0.01x 0.01x
- α x \alpha x αx
- random x
- α ( e x − 1 ) \alpha(e^x-1) α(ex−1)
一般relu效果不好就选择leaky
隐藏层:1.relu
2.不好就leaky
3.注意大面积神经元死亡
4.少用sigmoid,可以尝试tanh
输出层:1.二分类选择sigmoid
2.多分类用softmax
3.回归用恒等激活(identity)
04激活函数总结
- 激活函数作用
引入非线性元素 - 常见激活函数及其特点
sigmoid,tanh,relu,softmax - 激活函数的选择方法
隐藏层:relu
输出层:二分类sigmoid,多分类softmax,回归恒等
05参数初始化
weight:
均匀分布
正态分布(均值,标准差)
bias:
全0
全1
固定值
新的:
kaiming初始化(HE初始化)
均匀分布
[-limit, limit],limit=sqrt(6/fan_in)神经元的个数
正态分布
stddev=sqrt( 2/fan_in )
考虑了输入的神经元个数
接下来+考虑输出神经元的个数
xavier初始化(glorot初始化)
正态分布:stddev=sqrt( 2/fan_in+fan_out )
均匀分布:[-limit, limit],limit=sqrt(6/fan_in+fan_out)
pytorch默认使用Xavier初始化
06模型构建
linear = nn.Linear(in_features=3, out_features=2)
init
activat
forward()
class类继承nn
import torch
import torch.nn as nn
# 类继承
class model(nn.Module):
# init:定义层
def __init__(self):
super().__init__()
self.layer1 = nn.Linear(in_features=3, out_features=3)
nn.init.kaiming_normal_(self.layer1.weight)
self.layer2 = nn.Linear(in_features=3, out_features=2)
nn.init.xavier_uniform_(self.layer2.weight)
self.out = nn.Linear(in_features=2, out_features=2)
nn.init.uniform_(self.out.weight)
# forward:前向传播
def forward(self, x):
x_layer1 = self.layer1(x)
x_layer1 = torch.sigmoid(x_layer1)
x_layer2 = self.layer2(x_layer1)
x_layer2 = torch.relu(x_layer2)
out = self.out(x_layer2)
out = torch.softmax(out, dim=-1)
return out
if __name__ == "__main__":
my_model = model()
x = torch.randn(10, 3)
out = my_model(x)
print(out.shape)
07参数量统计
第一层网络:输入是3,输出也是3,也就是3*3=9权重值,再加上3个偏差,总共12个。
总的参数量就是所有参数加起来,
神经网络的搭建方法
继承nn.Module的模型类
在__init__方法中定义网络中的层结构
在forward方法中定义数据传输方式
网络参数量的统计方法
权重和偏置
08神经网络的优缺点
优点:
精度高,由于其他机器学习方法,甚至超过人类
近似任意非线性函数
近年学界和业界受到热捧,大量的框架和库可以调用
缺点:
黑箱,难解释模型怎么工作
训练时间长,需要大量的计算力
网络结构复杂,需要调整超参数
小数据集表现不佳,容易发生过拟合
金融,军事,需要有原理支撑
C端用户产品出错,没有很大影响
Meta需要训练半年,GPU
超参数依赖于人工调整。
09损失函数
分类任务的损失函数
回归任务的损失函数
预测 y ^ \hat{y} y^,真实 y y y,损失 l o s s ( y ^ , y ) loss(\hat{y},y) loss(y^,y)
损失函数loss function,代价函数cost
目标函数(ML领域)objective,误差函数error
多分类损失函数
L = − ∑ i = 1 n y i log [ S ( f θ ( x i ) ) ] \mathcal{L} = -\sum_{i=1}^{n} y_i \log \left[ S(f_{\theta}(x_i)) \right] L=−i=1∑nyilog[S(fθ(xi))]
其中 y i y_i yi真实值,网络的输出结果(概率值) S ( ⋅ ) S(\cdot) S(⋅),
假设三种类别,真实值为山鸢尾,鸢尾花(山鸢尾,维吉尼亚鸢尾,变色鸢尾:0,1,2)经过one-hot编码(100,010,001)
0.1,0.2,0.7====>-[100][log0.1,log0.2,log0.7]^{T}=-log0.1,只有index位置留下来,概率值接近1就希望loss小,概率值接近0就希望loss大
10交叉熵损失
nn.CrossEntropyLoss()
11二分类交叉熵损失
L = − y log ( y ^ ) − ( 1 − y ) log ( 1 − y ^ ) L=-y\log(\hat y)-(1-y)\log (1-\hat y) L=−ylog(y^)−(1−y)log(1−y^)
正样本 y = 1 y=1 y=1计算第一个
对交叉熵的简化
sigmoid激活函数nn.BCELoss()
12回归损失函数
L = 1 n ∑ i = 1 n ∣ y i − f θ ( x i ) ∣ \mathcal L=\frac{1}{n}\sum_{i=1}^{n}|y_i-f_{\theta}(x_i)| L=n1i=1∑n∣yi−fθ(xi)∣
由于L1 Loss具有稀疏性,为了惩罚较大值,因此常常将其作为正则项添加到其他loss作为约束
最大问题就是梯度在0点不平滑,导致跳过极小值
L = 1 n ∑ i = 1 n ( y i − f θ ( x i ) ) 2 \mathcal L=\frac{1}{n}\sum_{i=1}^{n} (y_i-f_{\theta}(x_i))^2 L=n1i=1∑n(yi−fθ(xi))2
由于L2 Loss常常作为正则项
当预测值与目标值相差很大时,梯度容易爆炸
smooth L 1 ( x ) = { 0.5 x 2 if ∣ x ∣ < 1 , ∣ x ∣ − 0.5 otherwise . \text{smooth}_{L1}(x) = \begin{cases} 0.5x^2 & \text{if } |x| < 1, \\ |x| - 0.5 & \text{otherwise}. \end{cases} smoothL1(x)={0.5x2∣x∣−0.5if ∣x∣<1,otherwise.
其中 x = f ( x ) − y x=f(x)-y x=f(x)−y真实值与预测值之间的差值,
在[-1,1]之间实际是L2损失,解决了L1损失的不光滑问题
之外是L1损失,解决了梯度爆炸的问题
分类:损失函数
多分类的交叉损失函数
二分类的交叉熵损失函数
回归:损失函数
MAE,MSE,smoothL1
13梯度下降算法
- 梯度下降算法回顾
w i j n e w = w i j o l d − η ∂ E ∂ w i j w_{ij}^{new}=w_{ij}^{old}-\eta \frac{\partial E}{\partial w_{ij}} wijnew=wijold−η∂wij∂E
三个基础概念,epoch,batch_size(受限于计算资源,允许条件下越大越好),iteration(使用一个batch更新的过程)。
假设数据有50000个,batch_size=256,训练集有batch个=50000/256 +1=196。每个epoch具有iteration=196。例如有10个epoch总共有1960个。
| 梯度下降方式 | training set size | batch_size | number of batch |
|---|---|---|---|
| BGD(全梯度下降) 不现实 |
N | N | 1 |
| SGD(随机梯度下降) 只选一个样本 |
N | 1 | N |
| BGD(全梯度下降) 折中 |
N | B | N/B+1 |
思想:网络优化,使用损失函数最小的方法
训练网络的三个概念:epoch batch iter
Day4
01内容回顾
激活函数
选择方式
参数初始化xviar
初始化层,网络前向
损失函数,多分类交叉熵,回归mse/mae
优化方法:梯度下降,3个概念
02前向和反向过程
输入层=>隐藏层=>输出层
前向传播的过程需要预测结果,计算损失,
利用梯度下降算法更新参数,
前向传播=>计算损失,计算梯度,更新参数。
03前向过程
04反向输出层
05反向隐藏层
06指数加权平均
平缓的区域下降的很慢,参数优化变慢
鞍点:导数为0但不是极小值,参数无法更新
局部极小值,暂时还没有,好处提高泛化能力
解决方法:momentum,adam。adagrad,rmsprop。
指数加权平均:参考各数值,各数值的权重不同,距离越远的数字对平均数计算的贡献小,距离越近则对平均数计算贡献越大。
比如明天的气温如何,与昨天的气温有很大的关系,但是与一个月前的关系不大
S ( t ) = Y 1 , t = 0 β S ( t − 1 ) + ( 1 − β ) Y t , t > 0 S(t)=Y_1, \quad t=0\\ \beta S(t-1)+(1-\beta)Y_t, \quad t>0 S(t)=Y1,t=0βS(t−1)+(1−β)Yt,t>0
S ( t ) S(t) S(t)表示指数加权平均值, Y t Y_t Yt表示 t t t时刻的值, β \beta β调节权重系数,越大平均数越平缓。
07动量法
梯度的计算公式: D t = β S t − 1 + ( 1 − β ) w t D_t=\beta S_{t-1} + (1-\beta)w_t Dt=βSt−1+(1−β)wt
S t − 1 S_{t-1} St−1历史梯度移动加权平均值, w t w_t wt当前时刻的梯度值, D t D_t Dt当前时刻的指数加权平均梯度值, β \beta β权重系数。
因为包含了历史梯度的信息,所以遇到鞍点时候,前边不是鞍点,所以避免了鞍点的问题。
跨过鞍点的问题,SGD(momentum=0.9)
08adagead
AdaGrad通过对不同参数分量使用不同的学习率,AdaGrad的学习率总体会逐渐减小。
步骤:
- 初始化学习率 α \alpha α,初始化参数 θ \theta θ,小常数 σ = 1 e − 6 \sigma=1e-6 σ=1e−6
- 初始化梯度累积变量 s = 0 s=0 s=0
- 从训练集中采样 m m m个样本的小批量,计算梯度 g g g
- 累积平方梯度 s = s + g ⊙ g s=s+g\odot g s=s+g⊙g, ⊙ \odot ⊙表示各个分量相乘
学习率 α = α s + σ \alpha=\frac{\alpha}{\sqrt{s}+\sigma} α=s+σα,参数更新公式如下 θ = θ − α s + σ g \theta=\theta-\frac{\alpha}{\sqrt{s}+\sigma}g θ=θ−s+σαg
重复2-4步骤,即可完成网络的训练。
缺点就会使得学习率过量降低Adagrad()
09rmsprop
同上,但是引入了指数移动加权平均梯度 s = β s + ( 1 − β ) g ⊙ g s=\beta s+(1-\beta)g\odot g s=βs+(1−β)g⊙g
10学习率衰减
等间隔,0-50,50-100,100-150
l r = l r ∗ g a m m a ( = 0.5 ) lr=lr*gamma(=0.5) lr=lr∗gamma(=0.5)scheduler_lr = torch.optim.lr_scheduler.StepLR(optimizer, step_size=20, gamma=0.1)
11学习率衰减2
设置间隔的衰减scheduler_lr = torch.optim.lr_scheduler.MultiStepLR(optimizer, milestones=[20, 60, 90, 135, 180], gamma=0.1)
指数衰减
l r = l r ∗ g a m m a e p o c h lr=lr*gamma^{epoch} lr=lr∗gammaepoch
与momentum结合起来多。
12正则化
除了L1L2,所有缓解网络过拟合的方法
过拟合,算法过于复杂的情况下。
范数惩罚(L1L2);dropout;特殊网络层BN,
- dropout
去掉某些神经元,例如去除h2和h4
集成学习思想,
神经元以超参数p的概率停止工作or激活,被置为0,未被置为0的进行缩放,缩放比例为 1 / ( 1 − p ) 1/(1-p) 1/(1−p)
子网络进行训练
13BN
批量归一化
h 1 = a 1 x 1 + a 2 x 2 + ⋯ + a n x n h_1=a_1x_1+a_2x_2+ \cdots +a_nx_n h1=a1x1+a2x2+⋯+anxn
但是这里 h 1 h_1 h1可能会超出 [ − 6 , 6 ] [-6,6] [−6,6]之间,梯度消失问题,所以引入BN标准化
计算机视觉使用多
批量归一化(Batch Normalization,BN)
在深度学习中,神经网络的每一层的输入数据分布会随着训练的进行而发生变化,这种现象称为内部协变量偏移(Internal Covariate Shift)。这会导致训练速度变慢,并且容易出现梯度消失或梯度爆炸的问题。批量归一化是一种有效的解决方法。
-
问题描述
假设我们有一个神经网络的隐藏层输出为:
h 1 = a 1 x 1 + a 2 x 2 + ⋯ + a n x n h_1 = a_1x_1 + a_2x_2 + \cdots + a_nx_n h1=a1x1+a2x2+⋯+anxn
其中,( x_i ) 是输入特征,( a_i ) 是权重。然而,( h_1 ) 的值可能会超出 ([-6, 6]) 的范围,这会导致激活函数(如 Sigmoid 或 Tanh)的梯度非常小,从而出现梯度消失问题。 -
批量归一化的基本原理
批量归一化的目的是对每一层的输入进行标准化,使其均值为 0,方差为 1。具体步骤如下: -
计算均值和方差
对于一个 mini-batch 中的输入 x x x,计算其均值 μ \mu μ 和方差 σ 2 \sigma^2 σ2:
μ = 1 m ∑ i = 1 m x i \mu = \frac{1}{m} \sum_{i=1}^{m} x_i μ=m1i=1∑mxi
σ 2 = 1 m ∑ i = 1 m ( x i − μ ) 2 \sigma^2 = \frac{1}{m} \sum_{i=1}^{m} (x_i - \mu)^2 σ2=m1i=1∑m(xi−μ)2
其中,( m ) 是 mini-batch 的大小。 -
标准化
使用均值和方差对输入进行标准化:
x ^ i = x i − μ σ 2 + ϵ \hat{x}_i = \frac{x_i - \mu}{\sqrt{\sigma^2 + \epsilon}} x^i=σ2+ϵxi−μ
其中, ϵ \epsilon ϵ 是一个很小的常数(如 1 0 − 8 10^{-8} 10−8),用于防止除以零。 -
重新参数化
为了使网络能够学习到合适的尺度和偏移量,引入两个可学习的参数 γ \gamma γ 和 β \beta β:
y i = γ x ^ i + β y_i = \gamma \hat{x}_i + \beta yi=γx^i+β
其中,$\gamma $ 和 β \beta β 是通过训练学习得到的参数。 -
批量归一化的好处
- 加速训练:通过减少内部协变量偏移,使得每一层的输入分布更加稳定,从而加快训练速度。
- 减少对初始化的依赖:由于输入数据的分布更加稳定,对初始权重的选择不再那么敏感。
- 允许使用更高的学习率:由于梯度消失问题得到缓解,可以使用更高的学习率进行训练。
- 正则化效果:批量归一化具有一定的正则化效果,可以减少过拟合。
- 批量归一化的实现
在实际应用中,批量归一化通常在每一层的激活函数之前应用。例如,在一个全连接层之后,可以这样实现:
import torch
import torch.nn as nn
# 假设输入数据为 x
x = torch.randn(32, 10) # 32 是 mini-batch 大小,10 是特征数量
# 创建批量归一化层
bn = nn.BatchNorm1d(10) # 10 是特征数量
# 应用批量归一化
x_normalized = bn(x)
- 注意事项
- 训练和推理阶段的不同:在训练阶段,批量归一化使用 mini-batch 的均值和方差进行标准化;在推理阶段,通常使用整个训练集的均值和方差进行标准化。
- 参数更新:在训练过程中,( \gamma ) 和 ( \beta ) 会通过反向传播进行更新,同时也会更新均值和方差的移动平均值,用于推理阶段。
通过批量归一化,我们可以有效地缓解梯度消失问题,提高神经网络的训练效率和性能。
14手机价格分类
init方法
forward
隐藏层失活
注意最后一层输出层不需要
import os
import random
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
import torch
import torch.nn as nn
from torch.utils.data import TensorDataset, DataLoader
# ========== 1. 基础设置 ==========
# 固定随机种子(保证结果可复现)
def set_seed(seed=42):
random.seed(seed)
np.random.seed(seed)
torch.manual_seed(seed)
torch.cuda.manual_seed_all(seed)
set_seed(42)
# 设备选择
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(f"Using device: {device}")
# 路径管理
SAVE_PATH = "/kaggle/working/phone_price_best.pth"
# ========== 2. 数据处理 ==========
data = pd.read_csv("/kaggle/input/d/hxingw/phone-price001/phone_price.csv")
X = data.iloc[:, :-1].values
y = data.iloc[:, -1].values
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
# 转为 torch 数据
X_train = torch.tensor(X_train, dtype=torch.float32)
X_test = torch.tensor(X_test, dtype=torch.float32)
y_train = torch.tensor(y_train, dtype=torch.int64)
y_test = torch.tensor(y_test, dtype=torch.int64)
train_dataset = TensorDataset(X_train, y_train)
test_dataset = TensorDataset(X_test, y_test)
train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=32, shuffle=False)
# ========== 3. 模型定义 ==========
class PhonePriceNet(nn.Module):
def __init__(self, input_dim=20, num_classes=4):
super(PhonePriceNet, self).__init__()
self.fc1 = nn.Linear(input_dim, 64)
self.fc2 = nn.Linear(64, 128)
self.fc3 = nn.Linear(128, num_classes)
self.dropout = nn.Dropout(0.2)
def forward(self, x):
x = torch.relu(self.fc1(x))
x = self.dropout(x)
x = torch.relu(self.fc2(x))
x = self.dropout(x)
out = self.fc3(x)
return out
# ========== 4. 训练 & 验证 ==========
def train_and_validate(model, train_loader, test_loader, epochs=20, lr=0.01):
model.to(device)
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=lr, momentum=0.9)
best_acc = 0.0
for epoch in range(epochs):
model.train()
train_loss = 0.0
for xb, yb in train_loader:
xb, yb = xb.to(device), yb.to(device)
optimizer.zero_grad()
outputs = model(xb)
loss = criterion(outputs, yb)
loss.backward()
optimizer.step()
train_loss += loss.item()
# 验证
model.eval()
correct, total = 0, 0
with torch.no_grad():
for xb, yb in test_loader:
xb, yb = xb.to(device), yb.to(device)
outputs = model(xb)
preds = torch.argmax(outputs, dim=1)
correct += (preds == yb).sum().item()
total += yb.size(0)
acc = correct / total
avg_loss = train_loss / len(train_loader)
print(f"Epoch [{epoch+1}/{epochs}] "
f"Loss: {avg_loss:.4f} | Val Acc: {acc:.4f}")
# 保存最佳模型
if acc > best_acc:
best_acc = acc
torch.save(model.state_dict(), SAVE_PATH)
print(f"✅ Saved new best model with acc {best_acc:.4f}")
print(f"Training finished. Best validation accuracy: {best_acc:.4f}")
# ========== 5. 测试 ==========
def test_model(model, test_loader):
model.load_state_dict(torch.load(SAVE_PATH))
model.to(device)
model.eval()
correct, total = 0, 0
with torch.no_grad():
for xb, yb in test_loader:
xb, yb = xb.to(device), yb.to(device)
outputs = model(xb)
preds = torch.argmax(outputs, dim=1)
correct += (preds == yb).sum().item()
total += yb.size(0)
acc = correct / total
print(f"Final Test Accuracy: {acc:.4f}")
# ========== 6. 主程序 ==========
if __name__ == "__main__":
model = PhonePriceNet()
train_and_validate(model, train_loader, test_loader, epochs=30, lr=0.01)
test_model(model, test_loader)
更多推荐



所有评论(0)