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其他激活函数

  1. 恒等激活
  2. 阶跃函数,小于0为0,大于0为1,导数为0

  1. logistics函数/sigmoid函数
  2. tanh
  3. relu

  1. 负半轴出现问题,leakyrelu,负半轴 0.01 x 0.01x 0.01x
  2. α x \alpha x αx
  3. random x
  4. α ( e x − 1 ) \alpha(e^x-1) α(ex1)
    一般relu效果不好就选择leaky

隐藏层:1.relu
2.不好就leaky
3.注意大面积神经元死亡
4.少用sigmoid,可以尝试tanh

输出层:1.二分类选择sigmoid
2.多分类用softmax
3.回归用恒等激活(identity)

04激活函数总结

  1. 激活函数作用
    引入非线性元素
  2. 常见激活函数及其特点
    sigmoid,tanh,relu,softmax
  3. 激活函数的选择方法
    隐藏层: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=1nyilog[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^)(1y)log(1y^)
正样本 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=1nyifθ(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=1n(yifθ(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.5x2x0.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梯度下降算法

  1. 梯度下降算法回顾
    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ηwijE
    三个基础概念,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(t1)+(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=βSt1+(1β)wt
S t − 1 S_{t-1} St1历史梯度移动加权平均值, w t w_t wt当前时刻的梯度值, D t D_t Dt当前时刻的指数加权平均梯度值, β \beta β权重系数。
因为包含了历史梯度的信息,所以遇到鞍点时候,前边不是鞍点,所以避免了鞍点的问题。
跨过鞍点的问题,SGD(momentum=0.9)

08adagead

AdaGrad通过对不同参数分量使用不同的学习率,AdaGrad的学习率总体会逐渐减小。
步骤:

  1. 初始化学习率 α \alpha α,初始化参数 θ \theta θ,小常数 σ = 1 e − 6 \sigma=1e-6 σ=1e6
  2. 初始化梯度累积变量 s = 0 s=0 s=0
  3. 从训练集中采样 m m m个样本的小批量,计算梯度 g g g
  4. 累积平方梯度 s = s + g ⊙ g s=s+g\odot g s=s+gg ⊙ \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β)gg

10学习率衰减

等间隔,0-50,50-100,100-150
l r = l r ∗ g a m m a ( = 0.5 ) lr=lr*gamma(=0.5) lr=lrgamma(=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=lrgammaepoch
与momentum结合起来多。

12正则化

除了L1L2,所有缓解网络过拟合的方法

过拟合,算法过于复杂的情况下。
范数惩罚(L1L2);dropout;特殊网络层BN,

  1. dropout
    去掉某些神经元,例如去除h2和h4
    集成学习思想,
    神经元以超参数p的概率停止工作or激活,被置为0,未被置为0的进行缩放,缩放比例为 1 / ( 1 − p ) 1/(1-p) 1/(1p)
    子网络进行训练

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)。这会导致训练速度变慢,并且容易出现梯度消失或梯度爆炸的问题。批量归一化是一种有效的解决方法。

  1. 问题描述
    假设我们有一个神经网络的隐藏层输出为:
    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)的梯度非常小,从而出现梯度消失问题。

  2. 批量归一化的基本原理
    批量归一化的目的是对每一层的输入进行标准化,使其均值为 0,方差为 1。具体步骤如下:

  3. 计算均值和方差
    对于一个 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=1mxi
    σ 2 = 1 m ∑ i = 1 m ( x i − μ ) 2 \sigma^2 = \frac{1}{m} \sum_{i=1}^{m} (x_i - \mu)^2 σ2=m1i=1m(xiμ)2
    其中,( m ) 是 mini-batch 的大小。

  4. 标准化
    使用均值和方差对输入进行标准化:
    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} 108),用于防止除以零。

  5. 重新参数化
    为了使网络能够学习到合适的尺度和偏移量,引入两个可学习的参数 γ \gamma γ β \beta β
    y i = γ x ^ i + β y_i = \gamma \hat{x}_i + \beta yi=γx^i+β
    其中,$\gamma $ 和 β \beta β 是通过训练学习得到的参数。

  6. 批量归一化的好处

  • 加速训练:通过减少内部协变量偏移,使得每一层的输入分布更加稳定,从而加快训练速度。
  • 减少对初始化的依赖:由于输入数据的分布更加稳定,对初始权重的选择不再那么敏感。
  • 允许使用更高的学习率:由于梯度消失问题得到缓解,可以使用更高的学习率进行训练。
  • 正则化效果:批量归一化具有一定的正则化效果,可以减少过拟合。
  1. 批量归一化的实现
    在实际应用中,批量归一化通常在每一层的激活函数之前应用。例如,在一个全连接层之后,可以这样实现:
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)
  1. 注意事项
  • 训练和推理阶段的不同:在训练阶段,批量归一化使用 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)

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐