机器学习入门:用线性回归实现一个房价预测的模型(pytorch实现)

本文通过一个实际例子解释线性回归,利用梯度下降法拟合房屋价格模型。通过生成模拟数据,展示了如何用PyTorch实现线性回归的训练过程,包括损失函数、梯度计算和参数更新,最终使模型参数接近真实值,减少预测误差。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

import torch
# 导入库
torch.cuda.is_available()

为了解释线性回归,我们举一个实际的例子: 我们希望根据房屋的面积(平方英尺)和房龄(年)来估算房屋价格(美元)

目标(房屋价格)可以表示为特征(面积和房龄)的加权和,如下式子:

p r i c e = w a r e a × a r e a + w a g e × a g e + b price = w_{area} \times area + w_{age} \times age + b price=warea×area+wage×age+b

w a r e a w_{area} warea w a g e w_{age} wage称为权重(weight),权重决定了每个特征对我们预测值的影响。

b 称为偏置(bias)、偏移量(offset)或截距(intercept)。 偏置是指当所有特征都取值为0时,预测值应该为多少。 即使现实中不会有任何房子的面积是0或房龄正好是0年,我们仍然需要偏置项。 如果没有偏置项,我们模型的表达能力将受到限制。

Gradient descent summary

So far in this course, you have developed a linear model that predicts f w , b ( x ( i ) ) f_{w,b}(x^{(i)}) fw,b(x(i)):
f w , b ( x ( i ) ) = w x ( i ) + b (1) f_{w,b}(x^{(i)}) = wx^{(i)} + b \tag{1} fw,b(x(i))=wx(i)+b(1)
In linear regression, you utilize input training data to fit the parameters w w w, b b b by minimizing a measure of the error between our predictions f w , b ( x ( i ) ) f_{w,b}(x^{(i)}) fw,b(x(i)) and the actual data y ( i ) y^{(i)} y(i). The measure is called the c o s t cost cost, J ( w , b ) J(w,b) J(w,b). In training you measure the cost over all of our training samples x ( i ) , y ( i ) x^{(i)},y^{(i)} x(i),y(i)
J ( w , b ) = 1 2 m ∑ i = 0 m − 1 ( f w , b ( x ( i ) ) − y ( i ) ) 2 (2) J(w,b) = \frac{1}{2m} \sum\limits_{i = 0}^{m-1} (f_{w,b}(x^{(i)}) - y^{(i)})^2\tag{2} J(w,b)=2m1i=0m1(fw,b(x(i))y(i))2(2)

In lecture, gradient descent was described as:

repeat  until convergence:    {    w = w − α ∂ J ( w , b ) ∂ w    b = b − α ∂ J ( w , b ) ∂ b } \begin{align*} \text{repeat}&\text{ until convergence:} \; \lbrace \newline \; w &= w - \alpha \frac{\partial J(w,b)}{\partial w} \tag{3} \; \newline b &= b - \alpha \frac{\partial J(w,b)}{\partial b} \newline \rbrace \end{align*} repeatwb} until convergence:{=wαwJ(w,b)=bαbJ(w,b)(3)
where, parameters w w w, b b b are updated simultaneously.
The gradient is defined as:
∂ J ( w , b ) ∂ w = 1 m ∑ i = 0 m − 1 ( f w , b ( x ( i ) ) − y ( i ) ) x ( i ) ∂ J ( w , b ) ∂ b = 1 m ∑ i = 0 m − 1 ( f w , b ( x ( i ) ) − y ( i ) ) \begin{align} \frac{\partial J(w,b)}{\partial w} &= \frac{1}{m} \sum\limits_{i = 0}^{m-1} (f_{w,b}(x^{(i)}) - y^{(i)})x^{(i)} \tag{4}\\ \frac{\partial J(w,b)}{\partial b} &= \frac{1}{m} \sum\limits_{i = 0}^{m-1} (f_{w,b}(x^{(i)}) - y^{(i)}) \tag{5}\\ \end{align} wJ(w,b)bJ(w,b)=m1i=0m1(fw,b(x(i))y(i))x(i)=m1i=0m1(fw,b(x(i))y(i))(4)(5)

**Here simultaniously means that you calculate the partial derivatives for all the parameters before updating any of the parameters. **

In lecture, gradient descent was described as:

repeat  until convergence:    {    w = w − α ∂ J ( w , b ) ∂ w    b = b − α ∂ J ( w , b ) ∂ b } \begin{align*} \text{repeat}&\text{ until convergence:} \; \lbrace \newline \; w &= w - \alpha \frac{\partial J(w,b)}{\partial w} \tag{3} \; \newline b &= b - \alpha \frac{\partial J(w,b)}{\partial b} \newline \rbrace \end{align*} repeatwb} until convergence:{=wαwJ(w,b)=bαbJ(w,b)(3)
where, parameters w w w, b b b are updated simultaneously.
The gradient is defined as:
∂ J ( w , b ) ∂ w = 1 m ∑ i = 0 m − 1 ( f w , b ( x ( i ) ) − y ( i ) ) x ( i ) ∂ J ( w , b ) ∂ b = 1 m ∑ i = 0 m − 1 ( f w , b ( x ( i ) ) − y ( i ) ) \begin{align} \frac{\partial J(w,b)}{\partial w} &= \frac{1}{m} \sum\limits_{i = 0}^{m-1} (f_{w,b}(x^{(i)}) - y^{(i)})x^{(i)} \tag{4}\\ \frac{\partial J(w,b)}{\partial b} &= \frac{1}{m} \sum\limits_{i = 0}^{m-1} (f_{w,b}(x^{(i)}) - y^{(i)}) \tag{5}\\ \end{align} wJ(w,b)bJ(w,b)=m1i=0m1(fw,b(x(i))y(i))x(i)=m1i=0m1(fw,b(x(i))y(i))(4)(5)

**Here simultaniously means that you calculate the partial derivatives for all the parameters before updating any of the parameters. **

首先我们自己生成一些数据,这些数据也许跟现实没有任何联系,但是可以用它们指代一个线性关系:即房屋面积以及房龄两个自变量与房价(因变量)之间存在的某种关系。

# 生成数据(使用线性模型参数 w = [ 2 , − 3.4 ] ⊤ b = 4.2 和噪声项 ϵ 生成数据集及其标签):
# y = X w + b + ϵ 
# w, b, 样本数
def synthetic_data(w, b, num_examples):
    # X 为矩阵,表示生成 样本数*len(w) 的(0,1)正态分布的矩阵。
    # len(w) 表示特征数
    X = torch.normal(0, 1, (num_examples, len(w)))
    # X matrix (1000,2) (1000个样例,2个输入特征:面积、房龄)
    # w vector (2)
    y = torch.matmul(X, w) + b
    # y vector (1000)
    # y = Xw + b + 噪声,噪声呈现标准差为 0.01 的正态分布。
    y += torch.normal(0, 0.01, y.shape)
    # (-1, 1) 代表作为列向量返回(指定列数为1,行数自动确定。)
    # 在torch中,如果要区分行向量与列向量的区别,则必须用矩阵表示,对于计算机来说,单纯的一列或一行都是一个一维数组
    return X, y.reshape((-1, 1)) 
# 预先设置好真实的参数w,b(现实中我们可能是不知道这个参数的,需要从大量数据中发现这个规律,从而可以预测新的数据)
true_w = torch.tensor([2, -3.4])
true_b = 4.2
features, labels = synthetic_data(true_w, true_b, 1000)
print(f"features:{features[:3]},\n labels:{labels[:3]}")

查看前三个样本,其中第三个样本的含义就是,当房屋面积为1.3822、房龄为0.9644时,该房屋的房价为3.6739.
features:tensor([[-0.3011, -0.6250],
[ 0.5002, -0.3313],
[ 1.3822, 0.9644]]),
labels:tensor([[5.7323],
[6.3256],
[3.6739]])

注意,上述数据都是伪造的,包括真实的权重参数w和b,接下来我们的任务就是:假设我们不知道真实的权重参数,根据这些大量的样本,如何推断出权重参数(或者尽可能地逼近真实值),这就是线性回归的任务。

首先我们将w,b参数初始化为0

# 随机初始化参数,并计算梯度。
# 划重点: 需要 w,b 进行更新,所以才将 requires_grad设置为 True
w = torch.tensor([0., 0.], requires_grad=True)
b = torch.tensor([0.], requires_grad=True)
w,b

定义平方损失函数

# 计算样本总的损失
def calc_cost(X,w,b,labels,m):
    # 批量梯度下降,求取全部样本的损失相加
    prices = torch.matmul(X,w)+b
    cost = 0
    # 对标上面的公式(2)
    for i in range(m):
        cost += (prices[i] - labels[i])**2
    total_cost = 1 / (2 * m) * cost     

    return total_cost

定义批量梯度下降算法

# 批量梯度下降算法,参数,学习率
def sgd(params, lr):  
    # 更新时不参与梯度计算(simultaneously update,同时对w和b求偏导,然后再更新参数w,b)(对应公式(3))
    with torch.no_grad(): # 当requires_grad设置为False时,反向传播时就不会自动求导了,因此大大节约了显存或者说内存
        for param in params:
            param -= lr * param.grad
            print(f"参数更新为:{param}")
            param.grad.zero_()  # 每一个批次都要对每个参数的梯度进行清零(这里的一个批次就是全部样本)

训练过程如下,可以自行更改迭代次数和学习率。

# 训练
# 学习率
lr = 0.008
# 迭代次数
num_epochs = 600
loss = calc_cost

for epoch in range(num_epochs):
    total_cost = calc_cost(features,w,b,labels,len(labels))  # 批量总损失(对标公式(2))
    print(f"第{epoch+1}轮迭代的总损失:{total_cost}") 
    total_cost.backward() # 自动求导(对标公式(4)(5))
    print(f"梯度分别为:{w.grad},{b.grad}") # 临时偏导数(梯度)
    sgd([w,b],lr) # 根据梯度,更新参数(对应公式(3))
    print(f"面积权重、房龄权重的真实参数为:{true_w}\n偏置的真实参数为:{true_b}\n")
    

597轮迭代的总损失:tensor([0.0014], grad_fn=<MulBackward0>)
梯度分别为:tensor([-0.0192,  0.0320]),tensor([-0.0352])
参数更新为:tensor([ 1.9793, -3.3672], requires_grad=True)
参数更新为:tensor([4.1645], requires_grad=True)
面积权重、房龄权重的真实参数为:tensor([ 2.0000, -3.4000])
偏置的真实参数为:4.2598轮迭代的总损失:tensor([0.0014], grad_fn=<MulBackward0>)
梯度分别为:tensor([-0.0190,  0.0318]),tensor([-0.0350])
参数更新为:tensor([ 1.9795, -3.3674], requires_grad=True)
参数更新为:tensor([4.1648], requires_grad=True)
面积权重、房龄权重的真实参数为:tensor([ 2.0000, -3.4000])
偏置的真实参数为:4.2599轮迭代的总损失:tensor([0.0013], grad_fn=<MulBackward0>)
梯度分别为:tensor([-0.0189,  0.0315]),tensor([-0.0347])
参数更新为:tensor([ 1.9796, -3.3677], requires_grad=True)
参数更新为:tensor([4.1651], requires_grad=True)
面积权重、房龄权重的真实参数为:tensor([ 2.0000, -3.4000])
偏置的真实参数为:4.2600轮迭代的总损失:tensor([0.0013], grad_fn=<MulBackward0>)
梯度分别为:tensor([-0.0188,  0.0313]),tensor([-0.0344])
参数更新为:tensor([ 1.9798, -3.3679], requires_grad=True)
参数更新为:tensor([4.1653], requires_grad=True)
面积权重、房龄权重的真实参数为:tensor([ 2.0000, -3.4000])
偏置的真实参数为:4.2

可以看到,在训练的过程中,w,b参数在不断地根据梯度下降的方向进行调整,损失也随之不断减小,最终损失下降到接近0,w,b参数也调整至接近真实参数。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

海绵大星星

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值