线性回归是一种用于建模和分析关系的线性方法。在简单线性回归中,我们考虑一个 自变量和一个因变量之间的关系,用一条直线进行建模。而在多元线性回归中,我们 可以使用多个自变量来建模,因此我们需要拟合的不再是一个简单的直线,而是在高 维空间上的一个超平面。每个样本的因变量(y)在多元线性回归中依赖于多个自变 量(x),这样的关系可以用一个超平面来表示,这个超平面被称为回归平面。因 此,在多元线性回归中,我们试图找到一个最适合数据的超平面,以最小化实际观测 值与模型预测值之间的差异。
一、数据集介绍
本例使用了一个房地产估价( Datasets - UCI Machine Learning Repository)数据 集,其中包含关于房地产估价的市场历史数据集收集自台湾新北市新店区。数据以 xlsx形式保存在dataset文件夹中,其中Real estate valuation data set.xlsx是数据, 以下是数据集的中文解释:
数据集地址
数据集信息
房地产估价的市场历史数据集收集自台湾新北市新店区。“房地产估价”是一个回归问题。数据集被随机分为训练数据集(2/3 个样本)和测试数据集(1/3 个样本)。
变量名称 | 角色 | 类型 | 描述 | 单位 | 缺失值 |
---|---|---|---|---|---|
不 | 身份证 | 整数 | 不 | ||
X1 交易日期 | 特征 | 连续的 | 例如,2013.250=2013 年 3 月、2013.500=2013 年 6 月等。 | 不 | |
X2 房屋年龄 | 特征 | 连续的 | 年 | 不 | |
到最近的地铁站的距离 X3 | 特征 | 连续的 | 米 | 不 | |
X4 便利店数量 | 特征 | 整数 | 步行生活圈便利店数量 | 整数 | 不 |
X5 纬度 | 特征 | 连续的 | 地理坐标, 纬度 | 度 | 不 |
X6 经度 | 特征 | 连续的 | 地理坐标, 经度 | 度 | 不 |
Y 房面积 | 目标 | 连续的 | 10000 新台币/Ping,其中 Ping 是本地单位,1 Ping = 3.3 平方米 | 10000 新台币/坪 | 不 |
其他变量信息:
输入如下 X1 = 交易日期(例如,2013.250=2013 年 3 月、2013.500=2013 年 6 月等)
X2 = 房龄(单位:年)
X3=到最近的地铁站的距离(单位:米)
X4=步行生活圈内的便利店数量(整数)
X5 = 地理坐标、纬度。(单位:度)
X6 = 地理坐标,经度。(单位:度)
输出如下
Y= 单位面积房价(10000 新台币/Ping,其中 Ping 为本地单位,1 Ping = 3.3 平方米)
二、读取数据集
import pandas as pd
df=pd.read_excel("Real estate valuation data set.xlsx")
df.head()
三、数据处理
对离散数据进行one-hot处理
df=pd.get_dummies(df,columns=['X4 number of convenience stores'])
确定自变量(x)和因变量(y),根据任务需求特征作为x,预测的房价作为y
x=df[['X1 transaction date', 'X2 house age',
'X3 distance to the nearest MRT station', 'X5 latitude', 'X6 longitude',
'X4 number of convenience stores_0',
'X4 number of convenience stores_1',
'X4 number of convenience stores_2',
'X4 number of convenience stores_3',
'X4 number of convenience stores_4',
'X4 number of convenience stores_5',
'X4 number of convenience stores_6',
'X4 number of convenience stores_7',
'X4 number of convenience stores_8',
'X4 number of convenience stores_9',
'X4 number of convenience stores_10']]
y=df['Y house price of unit area']
划分数据集和测试集
使用Scikit-learn中的train_test_split函数对数 据进行划分,其中test_size为测试集的比列,0.2表示20%,random_state是一个用 于控制随机性的参数。在机器学习中,许多算法都涉及到某种形式的随机性,例如数 据集划分、初始化模型参数等。为了使实验结果可重复,我们可以设置 random_state 参数的固定值。
from sklearn.model_selection import train_test_split
x_train,x_test,y_train,y_test=train_test_split(x,y,test_size=0.2,random_state=42)
或
from sklearn.utils import shuffle
x,y=shuffle(x,y,random_state=0)
x_train=x[:int(len(df)*0.8)]
x_test=x[int(len(df)*0.8):]
y_train=y[:int(len(df)*0.8)]
y_test=y[int(len(df)*0.8):]
标准化
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
scaler.fit(x_train)
x_train_scaled = scaler.transform(x_train)
x_test_scaled = scaler.transform(x_test)
转化为pytorch张量
import torch.nn as nn
import torch
X_train_tensor = torch.tensor(x_train_scaled, dtype=torch.float32)
y_train_tensor = torch.tensor(y_train.values, dtype=torch.float32).view(-1, 1)
X_test_tensor = torch.tensor(x_test_scaled, dtype=torch.float32)
y_test_tensor = torch.tensor(y_test.values, dtype=torch.float32).view(-1, 1)
四、模型训练
定义线性回归模型 ( LinearRegressionModel)
class LinerRegressionModel(nn.Module):
def __init__(self,input_size):
super().__init__()
self.liner=nn.Linear(input_size,1)
def forward(self,x):
x=self.liner(x)
return x
实例化模型
model=LinerRegressionModel(X_train_tensor.shape[1])
定义损失函数和优化器
使用均方误差损失 ( MSELoss) 作为损失函数,Adam 优 化器作为优化器,学习率为 0.1。
criterion=nn.MSELoss()
optimizer=torch.optim.Adam(model.parameters(),lr=0.01)
模型训练
for i in range(1,10001):
# 将模型设置为训练模式
model.train()
# 清空梯度
optimizer.zero_grad()
# 前向传播,得到损失
output = model(X_train_tensor)
loss = criterion(output, y_train_tensor)
# 根据得到的损失去进行反向传播
loss.backward()
# 根据计算得到的梯度,进行更新模型的参数
optimizer.step()
# 设置提示信息,显示训练过程中的一些数据
if (i + 1) % 1000 == 0:
print(f'Epoch [{i + 1}/{1000}], Loss: {loss.item():.4f}')
Epoch [1000/1000], Loss: 920.0674
Epoch [2000/1000], Loss: 505.8924
Epoch [3000/1000], Loss: 259.1480
Epoch [4000/1000], Loss: 133.5944
Epoch [5000/1000], Loss: 86.4354
Epoch [6000/1000], Loss: 76.4425
Epoch [7000/1000], Loss: 75.6872
Epoch [8000/1000], Loss: 75.6773
Epoch [9000/1000], Loss: 75.6773
Epoch [10000/1000], Loss: 75.6773
五、模型评估
设置为评估模式
model.eval()
进行前向传播,并计算测 试集的损失
with torch.no_grad():
# 将测试集的特征输入到模型中,得到预测值
predictions = model(X_test_tensor)
# 计算测试集上的损失,用于评估模型在未知数据上的表现
test_loss = criterion(predictions, y_test_tensor)
实际值和模型预测值之间的关系
plt.figure(0)
plt.scatter(y_test_numpy,prediction,color='blue')
plt.plot([min(y_test_numpy),max(y_test_numpy)],[min(y_test_numpy),max(y_test_numpy)],linestyle='-',color='red',linewidth=2)
plt.xlabel('Actual Values')
plt.ylabel('Predicted Values')
plt.title('Regression Results')
plt.figure(1,figsize=(20,8))
# 按照时间顺序对数据排序
sorted_indices=x_test.index.argsort()
y_test_sorted=y_test.iloc[sorted_indices]
y_pred_sorted=pd.Series(prediction.squeeze()).iloc[sorted_indices]
# 绘制实际值和预测值的曲线
plt.plot(y_test_sorted.values, label='Acatual Values', marker='o')
plt.plot(y_pred_sorted.values, label='Predicted Values', marker='*')
plt.xlabel('Sample Index (Sorted)')
plt.ylabel('Values')
plt.title('Actual vs Predicted Values in Linear Regression')
plt.legend()
plt.show()
六、完整代码
import pandas as pd
from matplotlib import pyplot as plt
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
import torch.nn as nn
import torch
# 从Excel文件加载数据集
df = pd.read_excel('Real estate valuation data set.xlsx')
# 对分类变量'X4 number of convenience stores'进行独热编码
df = pd.get_dummies(df, columns=['X4 number of convenience stores'])
# 定义特征和目标变量
x = df[['X1 transaction date', 'X2 house age',
'X3 distance to the nearest MRT station', 'X5 latitude', 'X6 longitude',
'X4 number of convenience stores_0',
'X4 number of convenience stores_1',
'X4 number of convenience stores_2',
'X4 number of convenience stores_3',
'X4 number of convenience stores_4',
'X4 number of convenience stores_5',
'X4 number of convenience stores_6',
'X4 number of convenience stores_7',
'X4 number of convenience stores_8',
'X4 number of convenience stores_9',
'X4 number of convenience stores_10']]
y = df['Y house price of unit area']
# 划分数据集为训练集和测试集
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=42)
# 标准化特征,去除均值并缩放为单位方差
scaler = StandardScaler()
scaler.fit(x_train) # 在训练数据上拟合缩放器
x_train_scaled = scaler.transform(x_train) # 转换训练数据
x_test_scaled = scaler.transform(x_test) # 转换测试数据
# 将缩放后的数组转换为PyTorch张量
X_train_tensor = torch.tensor(x_train_scaled, dtype=torch.float32) # 训练特征
y_train_tensor = torch.tensor(y_train.values, dtype=torch.float32).view(-1, 1) # 训练标签
X_test_tensor = torch.tensor(x_test_scaled, dtype=torch.float32) # 测试特征
y_test_tensor = torch.tensor(y_test.values, dtype=torch.float32).view(-1, 1) # 测试标签
# 定义线性回归模型类
class LinerRegressionModel(nn.Module):
def __init__(self, input_size):
super().__init__()
self.liner = nn.Linear(input_size, 1) # 定义线性层
def forward(self, x):
x = self.liner(x) # 应用线性层
return x
# 使用适当的输入大小实例化模型
model = LinerRegressionModel(X_train_tensor.shape[1])
# 定义损失函数和优化器
criterion = nn.MSELoss() # 均方误差损失
optimizer = torch.optim.Adam(model.parameters(), lr=0.01) # Adam优化器
# 训练循环
for i in range(1, 10001):
model.train() # 将模型设置为训练模式
optimizer.zero_grad() # 清空上一步的梯度
# 向前传播,通过模型计算预测值
output = model(X_train_tensor)
loss = criterion(output, y_train_tensor) # 计算损失
# 向后传播:计算损失对模型参数的梯度
loss.backward()
optimizer.step() # 更新模型参数
# 每1000轮打印训练状态
if (i + 1) % 1000 == 0:
print(f'Epoch [{i + 1}/{10000}], Loss: {loss.item():.4f}')
# 评估模型
model.eval() # 将模型设置为评估模式
with torch.no_grad(): # 禁用梯度计算以进行评估
predictions = model(X_test_tensor) # 对测试数据获取预测结果
test_loss = criterion(predictions, y_test_tensor) # 计算测试数据上的损失
# 将预测结果和真实值转换为numpy以进行绘图
prediction = predictions.numpy()
y_test_numpy = y_test_tensor.numpy()
# 绘制实际值与预测值的散点图
plt.figure(0)
plt.scatter(y_test_numpy, predictions, color='blue') # 实际值与预测值的散点图
plt.plot([min(y_test_numpy), max(y_test_numpy)], [min(y_test_numpy), max(y_test_numpy)],
linestyle='--', color='red', linewidth=2) # 对角线
plt.xlabel('实际值')
plt.ylabel('预测值')
plt.title('回归结果')
# 创建第二个图,绘制按时间顺序排列的值
plt.figure(1, figsize=(12, 6))
sorted_indices = x_test.index.argsort() # 获取测试数据的排序索引
y_test_sorted = y_test.iloc[sorted_indices] # 排序实际值
y_pred_sorted = pd.Series(prediction.squeeze()).iloc[sorted_indices] # 排序预测值
# 绘制排序后的实际值和预测值
plt.plot(y_test_sorted.values, label='实际值', marker='o')
plt.plot(y_pred_sorted.values, label='预测值', marker='*')
plt.xlabel('样本索引(已排序)')
plt.ylabel('值')
plt.title('线性回归中的实际值与预测值')
plt.legend()
plt.show()
七、设计思路
1. 导入模块库
首先,我们需要一些工具来处理数据、绘制图形、进行机器学习和深度学习。所以我们用 Python 导入了相关的库。
2. 加载数据
接着,我们从一个 Excel 文件中读取房地产的数据,这些数据包含了许多与房价相关的信息,比如房子的大小、位置等。
3. 选择特征和目标
- 特征(X): 我们选择了一些特征来帮助预测房价,比如房子的年龄、距离最近地铁的距离、经纬度,以及周围便利店的数量。
- 目标(y): 我们要预测的目标是每平方米的房价。
4. 划分数据集
然后,我们把数据分成两部分:80% 用于训练模型,20% 用于测试模型。这样可以评估模型的表现,看看它能否在看不见的数据上做出正确的预测。
5. 数据标准化
在训练模型之前,我们需要将特征标准化,使它们的数值范围相似。这样可以帮助模型更快更好地学习。
6. 转换为张量
将处理好的数据转换成 PyTorch 张量,因为我们需要用张量来训练模型。
7. 定义模型
我们创建了一个线性回归模型,这种模型可以帮助我们找到特征(例如房子的年龄)和房价之间的关系。模型中的主要部分是一个线性层,负责进行预测。
8. 设置损失函数和优化器
我们定义了一种损失函数来衡量模型的预测与真实值之间的差距,并选择了一种优化器来帮助模型学习,自动调整参数以降低这个差距。
9. 训练模型
我们进行模型训练,总共进行 10,000 次迭代:
- 在每次迭代中,我们让模型学习训练数据。
- 我们计算出模型的预测值和真实值之间的误差,并通过反向传播更新模型的参数。
- 每进行 1000 次迭代,我们输出一次当前的损失情况。
10. 评估模型
训练结束后,我们使用测试集对模型进行评估,看看它在新数据上的表现如何。通过禁用梯度计算,我们可以更快地获取模型的预测结果。
11. 绘制结果
最后,我们使用绘图工具展示模型的预测结果:
- 画出实际房价与模型预测房价的散点图,直观地看出模型的准确性。
- 同时,绘制实际房价和预测房价的曲线,以观察随样本索引变化的预测表现。