深度学习Day01手工线性回归
考完研一段时间了,想着学点东西,看看能不能搞个项目应付一下复试。但作为深度学习小白,只能慢慢学了。今天学习的是手工线性回归的代码,浅浅记录一下。
线性回归的表示就是简单的一元函数y = w*x + b。
项目的核心目标
1 实现一个从零开始的线性回归算法
2 演示机器学习中参数估计的基本原理
3 验证模型能否准确恢复已知的真实参数
项目的具体功能
数据生成与批量处理
定义一个函数:create_data用于生成数据
def create_data(w, b, data_num): #生成数据
x = torch.normal(0, 1, (data_num, len(w)))
y = torch.matmul(x, w) + b #matmul表示矩阵相乘
noise = torch.normal(0, 0.01, y.shape) #噪声要加到y上
y += noise
return x, y
函数:data_provider是批量数据生成器
def data_provider(data, label, batchsize): #每次访问这个函数, 就能提供一批数据
length = len(label) # 数据集的长度
indices = list(range(length)) # 创建索引列表
#我不能按顺序取 把数据打乱
random.shuffle(indices) # 打乱数据列表
for each in range(0, length, batchsize):
get_indices = indices[each: each+batchsize]
get_data = data[get_indices]
get_label = label[get_indices]
yield get_data,get_label #有存档点的return
疑惑
学习过程中,不明白pytorch的作用,上网查了一下深度学习中使用pytorch而不是使用numpy的原因:
1、保持数据类型一致,如果混用类型会很麻烦
2、pytorch支持GPU加速,而numpy只支持CPU,效率很重要
3、自动梯度追踪
4、统一的深度学习工作流,保持代码一致性
torch.normal(0, 1, (data_num, len(w)))
torch.normal是数学高斯分布的计算机实现,也就是正态分布,因为考研学过概率论,所有我就理解为在N(μ, σ²) 中抽样的过程,这里就是在标准正态分布中采样,采样的样本数为data_num,而每个样本的特征数为len(w)。
noise = torch.normal(0, 0.01, y.shape)
y += noise
添加高斯噪声模拟真实世界的随机误差
噪声:均值0,标准差0.01
噪声形状与 y 相同
最终 y 包含了随机扰动
yield get_data, get_label
yield的作用与return相似,都是用于返回数据,但是有一定的区别。两者可类比为买东西付款,return就类似全款付款,一次性返回,而yield就类似分期付款,一次返回一部分。在函数中,for循环每次遍历都会通过yield返回本次的get_data和get_label。
模型与损失函数
模型:线性预测函数y=w*x+b
def fun(x, w, b):
pred_y = torch.matmul(x, w) + b
return pred_y
损失函数
def maeLoss(pre_y, y):
return torch.sum(abs(pre_y-y))/len(y)
疑惑
MAE
计算MAE (Mean Absolute Error):
$$ \text{MAE} = \frac{1}{n} \sum_{i=1}^{n} |y_i - \hat{y}_i| $$
拓展:MAE与MSE的区别
损失函数 公式 特点 MAE $$\frac{1}{n}\sum \left| y - \hat{y} \right|$$ 对异常值不敏感 MSE $$\frac{1}{n}\sum (y - \hat{y})^2$$ 惩罚大误差更严重
优化算法
函数sgd为随机梯度下降优化器
def sgd(paras, lr): #随机梯度下降,更新参数
with torch.no_grad(): #属于这句代码的部分,不计算梯度
for para in paras:
para -= para.grad * lr #不能写成 para = para - para.grad*lr
para.grad.zero_() #使用过的梯度,归0
疑惑
with torch.no_grad()
torch.no_grad()意味着让张量不追踪梯度,它与张量.requires_grad=FALSE的作用有点类似,但是它们之间有一定的区别:
特性 torch.no_grad()requires_grad=False作用对象 代码块内的所有运算 单个张量 作用范围 临时的、局部的 永久的、全局的 是否可恢复 ✅ 自动恢复 ❌ 需手动改 使用场景 推理、参数更新 定义常量、输入数据
para -= para.grad * lr
para -= para.grad * lr 与 para = para - para.grad * lr 的区别:
操作 效果 是否修改原对象 引用是否改变 para -= x就地修改 ✅ 是 ❌ 不变 para = para - x创建新对象 ❌ 否 ✅ 改变
para.grad.zero_()
para.grad.zero_()梯度归零,下划线表示就地操作。为什么要梯度清零呢?
假设不清零,第1次迭代:w.grad = 5,第2次反向传播:w.grad = w.grad + 3 = 8(累加!)
,错误的梯度会导致参数更新错误。
训练流程
参数初始化
lr = 0.03 # 学习率
w_0 = torch.normal(0, 0.01, true_w.shape, requires_grad=True) #这个w需要计算梯度
b_0 = torch.tensor(0.01, requires_grad=True)
print(w_0, b_0)
lr是指学习率(Learning Rate),它的作用是控制参数更新的步长,若设置过大,可能错过最优解,导致震荡;若设置过小,会导致收敛速度慢,训练时间长。
对参数w_0使用小的随机值进行初始化的作用是避免梯度消失/爆炸以及打破对称性(如果初始化为0,所有神经元会学到相同的特征)
训练循环架构
epochs = 50
for epoch in range(epochs):
data_loss = 0
for batch_x, batch_y in data_provider(X, Y, batchsize):
# 单个批次的训练步骤
# 1. 前向传播
pred_y = fun(batch_x,w_0, b_0)
# 2. 计算损失
loss = maeLoss(pred_y, batch_y)
# 3. 反向传播
loss.backward()
# 4. 参数更新
sgd([w_0, b_0], lr)
data_loss += loss # 累积每个batch的损失
print("epoch %03d: loss: %.6f"%(epoch, data_loss))
epochs = 50的意思是训练的轮次为50轮。
外部循环的次数就是遍历整个数据集的次数,每遍历一次(每一个epoch)都会打乱一次数据集。
数据集里面包含了很多样本,数据集又可以分为多个批次,而batchsize是指每一个批次的样本数量,因此数据集可分为(样本数量/batchsize)个批次。内部循环的作用就是对每个批次进行训练,步骤分为前向传播,计算损失,反向传播,参数更新。
完整的训练流程图
开始第1个Epoch
↓
打乱数据(data_provider内部)
↓
┌──────────────────────────┐
│ 取第1批数据(batch_x, batch_y) │
├──────────────────────────┤
│ 1. 前向传播:pred_y = fun(...) │
│ 2. 计算损失:loss = maeLoss(...) │
│ 3. 反向传播:loss.backward() │
│ 4. 参数更新:sgd([w_0, b_0], lr) │
└──────────────────────────┘
↓
取第2批数据...
↓
取第3批数据...
↓
...(直到所有批次处理完)
↓
打印本轮总损失
↓
开始第2个Epoch...
结果输出
print("真实的函数值是", true_w, true_b)
print("训练得到的参数值是", w_0, b_0)
真实的函数值是 tensor([8.1000, 2.0000, 2.0000, 4.0000]) tensor(1.1000)
训练得到的参数值是 tensor([8.1129, 2.0121, 1.9975, 4.0061], requires_grad=True) tensor(1.0900, requires_grad=True)
疑惑
loss.backward()
loss.backward()的作用:
1、自动求导:沿计算图反向传播,计算损失对每个参数的梯度
∂loss/∂w_0 → 保存到 w_0.grad
∂loss/∂b_0 → 保存到 b_0.grad2、梯度含义:
正梯度:参数增加会使损失增加 → 应该减小参数
负梯度:参数增加会使损失减小 → 应该增加参数
可视化反向传播:
loss
↑
maeLoss计算
↑
pred_y
↑
matmul + bias
↗ ↖
w_0 b_0 ← 梯度沿箭头反向流动
可视化结果

更多推荐
所有评论(0)