
1. 导入库
import torch
import matplotlib.pyplot as plt # 画图的
import random # 随机
torch:PyTorch 库,用于张量操作和自动求导。
matplotlib.pyplot:用于绘制图形,比如散点图和拟合曲线。
random:用于随机打乱数据索引。
2. 数据生成函数
def create_data(w, b, data_num): # 生成数据
x = torch.normal(0, 1, (data_num, len(w))) # 生成正态分布的输入数据
y = torch.matmul(x, w) + b # 计算目标值 y = x * w + b
noise = torch.normal(0, 0.01, y.shape) # 生成噪声
y += noise # 在目标值上添加噪声
return x, y
功能:生成线性回归的模拟数据。
参数:
w:真实的权重向量。
b:真实的偏置值。
data_num:生成的数据数量。
实现细节:
x 是从均值为 0、标准差为 1 的正态分布中生成的随机数据,形状为 (data_num, len(w))。
y 是通过线性公式 y = x * w + b 计算得到的。
添加噪声是为了模拟真实数据中的随机误差。
3. 生成数据
num = 500
true_w = torch.tensor([8.1, 2, 2, 4])
true_b = 1.1
X, Y = create_data(true_w, true_b, num)
功能:调用 create_data 函数生成数据。
参数:
true_w:真实的权重向量 [8.1, 2, 2, 4]。
true_b:真实的偏置值 1.1。
num:生成 500 个数据点。
返回值:
X:输入数据,形状为 (500, 4)。
Y:目标值,形状为 (500,)。
4. 数据可视化
plt.scatter(X[:, 3], Y, 1) # 绘制散点图
plt.show()
参数:
X[:, 3]:取 X 的所有行的第四列。必须单独取出某一列
Y:目标值。
1:点的大小。
5. 数据加载器
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 # 返回当前批次的数据和标签
功能:按批次提供数据,支持随机打乱。
参数:
data:输入数据。
label:目标值。
batchsize:批次大小。
实现细节:
使用 random.shuffle 打乱索引,确保每次迭代时数据的顺序是随机的。
使用 yield 返回当前批次的数据和标签,支持迭代。
6. 模型函数
def fun(x, w, b):
pred_y = torch.matmul(x, w) + b # 计算预测值 y = x * w + b
return pred_y
功能:定义线性模型,计算预测值。
参数:
x:输入数据。
w:权重。
b:偏置。
返回值:预测值 pred_y。
7. 损失函数
def maeLoss(pre_y, y):
return torch.sum(abs(pre_y - y)) / len(y) # 计算平均绝对误差
功能:计算预测值与真实值之间的平均绝对误差(MAE)。
参数:
pre_y:预测值。
y:真实值。
返回值:平均绝对误差。
8. 随机梯度下降(SGD)
def sgd(paras, lr): # 随机梯度下降,更新梯度
with torch.no_grad(): # 不计算梯度
for para in paras:
para -= para.grad * lr # 更新参数
para.grad.zero_() # 梯度清零
功能:使用随机梯度下降法更新模型参数。
参数:
paras:需要更新的参数列表(如 w 和 b)。
lr:学习率。
实现细节:
(1)with torch.no_grad():确保在更新参数时不计算梯度。
为什么需要:
1.PyTorch 默认会跟踪所有涉及张量的操作,以便自动计算梯度。
2.在更新参数时,我们不需要计算梯度(因为更新参数是一个纯数学操作,不涉及反向传播)。
3.使用 torch.no_grad() 可以节省内存并提高效率。
(2)para -= para.grad * lr:根据梯度和学习率更新参数。
注意 这里不能写成 para = para - para.grad * lr
-
随机梯度下降的更新公式为:
para=para−lr×grad -
为什么用
-=:-=是原地操作,直接修改para的值,而不是创建一个新的张量。
(3)para.grad.zero_():将梯度清零,防止梯度累积。
为什么需要?
-
PyTorch 会累积梯度(即每次调用
.backward()时,梯度会累加到.grad中)。 -
如果不清零,下一次反向传播时,梯度会与之前的梯度累加,导致错误。
-
因此,每次更新参数后,需要手动将梯度清零。
9. 初始化参数
lr = 0.001
w_0 = torch.normal(0, 0.01, true_w.shape, requires_grad=True)
b_0 = torch.tensor(0.01, requires_grad=True)
print(w_0, b_0)
功能:初始化模型参数。
参数:
w_0:权重,从均值为 0、标准差为 0.01 的正态分布中随机初始化。
b_0:偏置,初始化为 0.01。
requires_grad=True:表示需要计算梯度。
10. 训练模型
epochs = 50
for epoch in range(epochs):
data_loss = 0
for batch_x, batch_y in data_provider(X, Y, batchsize):
pred_y = fun(batch_x, w_0, b_0) # 计算预测值
loss = maeLoss(pred_y, batch_y) # 计算损失
loss.backward() # 反向传播,计算梯度
sgd([w_0, b_0], lr) # 更新参数
data_loss += loss # 累加损失 评估训练的好坏,要看loss值,将所有轮的loss加起来
print("epoch %03d: loss: %.6f" % (epoch, data_loss))
功能:训练模型。
实现细节:
每个 epoch 遍历所有数据批次。
计算预测值、损失,并进行反向传播和参数更新。
打印每个 epoch 的损失值。
11. 输出结果
print("真实的函数值是", true_w, true_b)
print("训练得到的参数值是", w_0, b_0)
功能:输出真实参数值和训练得到的参数值。
12. 可视化拟合结果
idx = 0
plt.plot(X[:, idx].detach().numpy(), X[:, idx].detach().numpy() * w_0[idx].detach().numpy() + b_0.detach().numpy())
plt.scatter(X[:, idx], Y, 1)
plt.show()
功能:绘制拟合直线和数据点的散点图。
实现细节:
X[:, idx]:取 X 的第一列。
w_0[idx] 和 b_0:训练得到的参数。
plt.plot:绘制拟合直线。
plt.scatter:绘制数据点。
注意:这里要加 .detach().numpy()
总结
这段代码实现了一个简单的线性回归模型,包括数据生成、模型定义、训练和结果可视化。通过随机梯度下降法优化模型参数,最终输出训练得到的参数值,并绘制拟合结果。
181






