线性回归

25.10.13

一.线性回归基础框架:

  1. 数据预处理

  2. 定义loss函数

  3. 定义优化器

  4. 训练 model.train() 需要记录梯度

4.1 前向传播计算loss

4.2 梯度清零                         optimizer. zero_grad()

4.3 反向传播计算梯度           loss. backward()

4.4 更新参数                         optimizer. step()

4.5评估 model.eval() 不需要记录梯度,计算total_loss,print


二.线性回归手敲代码实现:

import torch
from torch import nn
from torch.utils import data
'''
利用包来实现linear regression
'''
## 定义工具函数
def synthetic_data(w,b,num_samples,input_dim):
    X = torch.normal(0,1, size=(num_samples,input_dim))
    y = torch.matmul(X , w) + b
    y += torch.normal(0,0.01,y.shape)
    return X, y.reshape((-1,1))
 
## 生成数据集
input_dim = 2
output_dim = 1
true_w = torch.tensor([-2.0,4.0])##float才能有梯度,整数没有!!!
true_b = 4.2
num_samples = 1000
features, labels = synthetic_data(true_w,true_b,num_samples,input_dim)

## 读取数据集
def load_arry(data_arrays,batch_size,is_train=True):
    dataset = data. TensorDataset(*data_arrays)#调用TensorDataset的API,把数据和标签打包成数据集,相当于创建了一个 (特征, 标签) 的配对集合
    return data.DataLoader(dataset,batch_size,shuffle=is_train)#调用DataLoader的API,划分批次,并打乱顺序

batch_size = 5
num_epochs = 10
data_iter = load_arry((features,labels),batch_size,is_train=True)

## 定义模型
model = nn.Sequential(nn.Linear(input_dim,output_dim))#Sequential相当于装这些layers的容器,这里只有一层,其实也可以没有

##初始化模型参数,初始化网络的第一层net[0],使用weight.data,bias.data来访问参数
model[0].weight.data.normal_(0,0.01) # w = torch.normal(mean,std,size)---w.normal_(mean,std,size)
model[0].bias.data.fill_(0)# b = torch.tensor([0.0],requires_grad=True)这里一定是0.0,整数不能计算梯度 or b = torch.zeros(1, requires_grad=True)

## 训练:  1.选loss 2.选优化器 3.前向传播计算loss 4. 清零梯度 5.反向传播计算梯度 6.更新参数 
#         6.一个epoch循环结束,计算总loss,观察是否下降

#定义损失函数
criterion = nn.MSELoss()
#定义优化算法自动处理梯度(小梯度下降也就是SGD)更新参数用来
optimizer = torch.optim.SGD(model.parameters(),lr = 0.01)
'''
训练阶段,记录计算图是为了反向传播的时候,顺着反方向计算梯度
梯度清理,清掉的是梯度值,而非计算图,
也就是说反向传播的时候要顺着计算图,重新计算梯度,然后根据新的梯度,来更新参数
'''

for epoch in range (num_epochs):
    for X,y in data_iter:
        #训练阶段
        model.train()
        loss = criterion(model(X),y)         # 1. 前向传播计算loss,要记录计算图
        optimizer.zero_grad()                # 2. 梯度清零 zero_grad()
        loss.backward()                      # 3. 反向传播 backward()
        optimizer.step()                     # 4. 更新参数 step()
    #评估阶段
    model.eval()
    with torch.no_grad():
        total_loss = criterion(model(features),labels)
        print(f'epoch:{epoch+1}, loss:{total_loss.item():.6f}')

三.理解总结:

1. 训练阶段为什么要梯度清零

训练阶段,记录计算图是为了反向传播的时候,顺着反方向计算梯度

梯度清理,清掉的是梯度值,而非计算图,也就是说反向传播的时候要顺着计算图,重新计算梯度,然后根据新的梯度,来更新参数;

而评估阶段,为了节省内存,不需要记录梯度

2. model.train()和model.eval()

训练时:model.train() - 让Dropout、BatchNorm正常工作;评估/推理时:model.eval() - 获得稳定、可重复的结果,只是这里还未涉及

# 可复现实验设置:随机种子与CuDNN确定性

SEED = 2025

random.seed(SEED)

np.random.seed(SEED)

torch.manual_seed(SEED)

3. Pytorch(torch.) 中的常见包熟记功能

data包提供了数据处理工具,data. TensorDataset 将特征和标签一一配对;

nn有大量神经网络层和常见损失函数:nn.Sequential, nn.MSELoss

torch 中直接包含优化器:  torch. optim.Adam

4.如果将小批量的平均损失替换为小批量总损失,需要如何更改学习率?

我们说,反向传播过后要更新参数,也就是

\theta _{new}=\theta - \frac{\eta }{B}\sum_{i\epsilon B}^{}\partial _{\theta }l^{i}(\theta)

这里η=lr, 优化器默认为平均损失。

如果替换为总损失 \partial l_{\theta } total=\partial l_{\theta }\cdot batchsize, 那么lr变为 lr/batch_size。

5. 常见损失函数

a) 回归损失

nn.MSELoss()           # 均方误差

nn.L1Loss()            # 平均绝对误差

nn.SmoothL1Loss()      # 平滑L1损失(Huber损失)

nn.HuberLoss()         # Huber损失

b) 分类损失

nn.CrossEntropyLoss()  # 交叉熵损失(多分类)

nn.BCELoss()           # 二分类交叉熵

nn.BCEWithLogitsLoss() # 带sigmoid的二分类交叉熵

nn.NLLLoss()           # 负对数似然损失

c) 其他损失

nn.KLDivLoss()         # KL散度

nn.MarginRankingLoss() # 排序损失

nn.TripletMarginLoss() # 三元组损失

nn.CosineEmbeddingLoss() # 余弦嵌入损失

参考内容:

1.https://zh-v2.d2l.ai/chapter_linear-networks/linear-regression-concise.html

Logo

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

更多推荐