今天是学习 AI
的第三天,装环境就不再赘述了,太简单了。
安装了环境,也装了一些 AI
算法库,比如 TensorFlow
、PyTorch
等,不过暂时都用不上。
根据 AI
制定的计划,3,4,5 天的内容一起。写下这篇文章,如何利用机器学习解决线性回归问题。
1. 线性回归问题
什么是线性回归问题呢?举个房价的简单例子。
房屋面积(平方米) | 价格(万元) |
---|---|
30 | 300 |
40 | 400 |
50 | 500 |
如上所示,不同的房屋面积,对应了不同的价格,我们可以很轻松的发现,上述表格包含的规律为
<math xmlns="http://www.w3.org/1998/Math/MathML"> y = 10 x y = 10x </math>y=10x
这样,我们就可以推测,当房屋面积为 60 平方米时,价格为 600 万元。
然而现实生活中,不可能会有这么有规律的数据,有可能会出现下面这种情况。
房屋面积(平方米) | 价格(万元) |
---|---|
30 | 280 |
40 | 410 |
50 | 490 |
这个时候,再让我们预测 60 平方米,价格是多少,就没有那么容易了,这个问题就不单是一个线性问题,而可以看作是一个线性回归问题。
通常这种情况下,我们会通过最小二乘法来得到一条里所有的点都最近的曲线。
ini
import numpy as np
# 定义面积数据(自变量)
x = np.array([30, 40, 50])
# 定义房价数据(因变量)
y = np.array([280, 410, 490])
# 计算 x 和 y 的均值
x_mean = np.mean(x)
y_mean = np.mean(y)
# 计算斜率 beta_1
numerator = np.sum((x - x_mean) * (y - y_mean))
denominator = np.sum((x - x_mean) ** 2)
beta_1 = numerator / denominator
# 计算截距 beta_0
beta_0 = y_mean - beta_1 * x_mean
# 定义要预测的面积
x_to_predict = 60
# 进行预测
predicted_price = beta_0 + beta_1 * x_to_predict
print(f"预测 60 平米的房价为: {predicted_price:.2f} 万元")
下面这幅图,可以形象的说明最小二乘法在找一个什么样的线性关系
2. 多变量的线性回归问题
上面这个例子比较简单,而实际中房价并不单单依靠面积决定,如果有多个变量,应该怎么找到对应的线性关系呢,在此基础上,拓展表格如下。
房屋面积(平方米) | 装修金额(万元) | 价格(万元) |
---|---|---|
30 | 5 | 280 |
40 | 25 | 410 |
50 | 50 | 540 |
可以看到我们增加了装修这个纬度,而且可以目测装修金额越高,最终价格也越高。那么到底如何找到预测的线性关系呢,为了不去了解复杂的计算过程,我们直接使用 sklearn
中的方法,来解决这个问题。
ini
import numpy as np
from sklearn.linear_model import LinearRegression
# 数据准备
X = np.array([[30, 5], [40, 25], [50, 50]]) # 特征矩阵,每一行是一个样本,每一列是一个特征
y = np.array([280, 410, 540]) # 因变量
# 创建并拟合模型
model = LinearRegression()
model.fit(X, y)
# 查看系数和截距
beta_1 = model.coef_ # 自变量系数
beta_0 = model.intercept_ # 截距
print(f"自变量系数: {beta_1}")
print(f"截距: {beta_0}")
# 预测新数据
new_X = np.array([[60, 30]]) # 假设要预测的房屋面积为 60 平方米,装修金额为 30 万元
predicted_price = model.predict(new_X)
print(f"预测的房价为: {predicted_price[0]:.2f} 万元")
这里面的数学原理涉及到到如何计算多变量的最小误差平方和,需要用到矩阵、转置矩阵,求导的知识才能够算出来,比较复杂,感谢封装好的 LinearRegression
模型。
3. 模型评估
在刚才的例子里,由于数据没有真实性,所以不具备评估的条件。正确的做法应该是,采集数据时,将数据分为训练集和测试集,以下以波士顿房价预测为例。
python
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error, r2_score
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['STHeiti'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号
# 加载波士顿房价数据集
# 注意:从scikit-learn 1.2版本开始,波士顿房价数据集已被移除
# 这里使用兼容的替代方式
try:
data_url = "http://lib.stat.cmu.edu/datasets/boston"
raw_df = pd.read_csv(data_url, sep="\s+", skiprows=22, header=None)
data = np.hstack([raw_df.values[::2, :], raw_df.values[1::2, :2]])
target = raw_df.values[1::2, 2]
except Exception as e:
print(f"数据加载出错: {e}")
raise
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(data, target, test_size=0.2, random_state=42)
# 创建线性回归模型
model = LinearRegression()
# 训练模型
model.fit(X_train, y_train)
# 在测试集上进行预测
y_pred = model.predict(X_test)
# 评估模型
mse = mean_squared_error(y_test, y_pred)
r2 = r2_score(y_test, y_pred)
print(f"均方误差 (MSE): {mse}")
print(f"决定系数 (R²): {r2}")
使用训练集训练后,测验集的X_test
,用训练好的模型预测得到 y_pred
, 最终用均方误差(MSE)和决定系数(R²)来评判模型是否预测准确。
均方误差很好理解:假设共预测 n 项,将每一项的 (预测值 - 实际值) 平方后求和,再除以 n 就可以了。
决定系数稍微复杂点:将设实际值的平均值为 10,使用 (实际值 - 10) 平方后求和作为分母,再使用 (预测值 - 10) 平方后求和作为分子,再拿 1 - (预测值 - 实际值) 平方后求和 / (实际值 - 10) 平方后求和
MSE 要越接近 0 ,R² 要越接近 1 时才能说明这是个好模型
上述代码运行结果为
均方误差 (MSE): 24.291119474973048
决定系数 (R²): 0.6687594935356385
是个不太理想的数值,所以我们需要对模型进行优化。
4. 模型优化
在不更换模型时(线性回归),优化有这些常用的方式:特征缩放、特征选择、处理异常值、特征组合等方法,优化的代码(部分)如下。
ini
# 特征缩放
from sklearn.preprocessing import StandardScaler
# 创建标准化器
scaler = StandardScaler()# 对训练集和测试集的特征进行标准化
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
ini
# 特征选择
from sklearn.feature_selection import SelectKBest, f_regression
# 选择相关性最高的前k个特征
selector = SelectKBest(score_func=f_regression, k=5)
X_train_selected = selector.fit_transform(X_train, y_train)
X_test_selected = selector.transform(X_test)
ini
# 处理异常值
# 计算每个特征的Z-score
z_scores = np.abs((data - np.mean(data, axis=0)) / np.std(data, axis=0))# 设定阈值,例如3
threshold = 3
outliers = np.where(z_scores > threshold)# 移除异常值
data_cleaned = np.delete(data, outliers[0], axis=0)
target_cleaned = np.delete(target, outliers[0], axis=0)
ini
# 特征组合
from sklearn.preprocessing import PolynomialFeatures
# 创建多项式特征
poly = PolynomialFeatures(degree=2)
X_train_poly = poly.fit_transform(X_train)
X_test_poly = poly.transform(X_test)
上面的例子使用 处理异常值 和 特征组合 ,可以将均方误差和决定系数改进为:
均方误差 (MSE): 8.196982138149128
决定系数 (R²): 0.827221026062241
由于波士顿房价数据,特征值特别多,所以下面用一个简化的数据来说明,什么情况应该采用怎么样的优化方法。
行号 | 房屋房龄(年) | 房屋面积(平方英尺) | 卧室数量 | 小区绿化覆盖率 | 周边商场距离(公里) | 房屋价格(千美元) |
---|---|---|---|---|---|---|
1 | 2.5 | 1500 | 3.2 | 12 | 0.2 | 250 |
2 | 3 | 1800 | 3.5 | 15 | 0.3 | 300 |
3 | 2.2 | 1600 | 3 | 10 | 0.25 | 230 |
4 | 1.8 | 1400 | 2.8 | 8 | 0.18 | 200 |
5 | 3.5 | 2000 | 4 | 20 | 0.4 | 350 |
6 | 4 | 2200 | 4.5 | 22 | 0.5 | 400 |
7 | 1.5 | 1200 | 2.5 | 6 | 0.15 | 180 |
8 | 2.8 | 1700 | 3.3 | 13 | 0.22 | 260 |
9 | 50 | 1900 | 3.8 | 18 | 0.35 | 320 |
10 | 3.2 | 1850 | 3.6 | 16 | 0.32 | 310 |
1. 特征缩放
-
数据特征:各特征的取值范围差异很大。例如 "房屋房龄(年)" 取值在 1.5 - 50 之间,而 "房屋面积(平方英尺)" 取值在 1200 - 2200 之间。
-
适用原因:在使用一些基于距离计算的算法(如 K - 近邻算法)或者梯度下降优化算法(如线性回归使用梯度下降求解参数)时,取值范围大的特征会在距离计算或者梯度更新中占据主导地位,影响模型的训练效果和收敛速度。通过特征缩放(如标准化或归一化),可以将各特征统一到相近的尺度,使模型能够更公平地对待每个特征,提升训练效率和准确性。
2. 特征选择
-
数据特征:特征之间存在冗余或者某些特征与目标变量(房屋价格)的相关性极低。比如 "小区绿化覆盖率" 和 "周边商场距离(公里)" 可能存在一定的相关性,同时 "周边商场距离(公里)" 与 "房屋价格(千美元)" 的相关性经计算发现非常弱。
-
适用原因:过多的特征会增加模型的复杂度,导致训练时间变长,并且容易出现过拟合现象。通过特征选择方法(如相关性分析、方差分析等),可以筛选出对目标变量影响较大的特征,去除冗余和无关特征,简化模型,提高模型的泛化能力。
3. 处理异常值
-
数据特征:数据中存在明显偏离其他数据点的值。例如第 9 行的 "房屋房龄(年)" 为 50,远高于其他行的房龄数据,这可能是数据录入错误或者特殊情况导致的异常值。
-
适用原因:异常值会对模型的训练和拟合产生较大干扰,使模型的参数估计出现偏差,降低模型的预测准确性。通过识别并处理异常值(如使用箱线图确定异常值范围,然后删除异常值或者进行修正),可以使模型更好地拟合正常数据,提高预测的稳定性和准确性。
4. 特征组合
- 数据特征:单个特征与目标变量的关系不明显,但特征之间组合可能产生新的有价值信息。例如 "房屋面积(平方英尺)" 和 "卧室数量" 单独来看与 "房屋价格(千美元)" 的关系不是特别清晰,但将它们组合成 "单位卧室面积(平方英尺 / 个)" 这个新特征,可能会与房屋价格有更显著的相关性。
- 适用原因:特征组合可以挖掘数据中潜在的信息和模式,创造出更具解释性和预测能力的新特征。新的特征组合可能能够更好地反映数据的内在规律,从而提升模型对目标变量的预测效果。
5. 挑战竞赛
想成为真正的预言家吗,可以参与 kaggle
举办的房价预测大赛,我让 deepseek
帮我写了一个默认的房价预测程序,获得了 3606 名的成绩。可见机器学习算法的学习依然任重道远。
ini
# -*- coding: utf-8 -*-
import pandas as pd
import numpy as np
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import mean_squared_error
from sklearn.impute import SimpleImputer
# 1. 加载数据
train_data = pd.read_csv('./train.csv')
test_data = pd.read_csv('./test.csv')
# 2. 初步数据探索
print("训练集形状:", train_data.shape)
print("测试集形状:", test_data.shape)
print("训练集缺失值统计:\n", train_data.isnull().sum().sort_values(ascending=False).head(10))
# 3. 特征选择(示例选取部分特征)
selected_features = [
'OverallQual', 'GrLivArea', 'TotalBsmtSF', '1stFlrSF',
'YearBuilt', 'GarageCars', 'FullBath', 'TotRmsAbvGrd'
]
# 4. 处理缺失值
# 对数值型特征用中位数填充,分类特征用众数填充(此处简化处理)
numerical_features = selected_features
categorical_features = [] # 此示例未选分类特征
# 训练集处理
X_train = train_data[selected_features]
y_train = train_data['SalePrice']
imputer = SimpleImputer(strategy='median')
X_train_imputed = pd.DataFrame(imputer.fit_transform(X_train), columns=X_train.columns)
# 测试集处理
X_test = test_data[selected_features]
X_test_imputed = pd.DataFrame(imputer.transform(X_test), columns=X_test.columns)
# 5. 特征标准化
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train_imputed)
X_test_scaled = scaler.transform(X_test_imputed)
# 6. 训练模型
model = LinearRegression()
model.fit(X_train_scaled, y_train)
# 7. 验证模型(使用训练集划分验证集)
X_train_part, X_val, y_train_part, y_val = train_test_split(
X_train_scaled, y_train, test_size=0.2, random_state=42
)
val_pred = model.predict(X_val)
rmse = np.sqrt(mean_squared_error(y_val, val_pred))
print(f"验证集RMSE: {rmse:.2f}")
# 8. 预测测试集并生成提交文件
test_pred = model.predict(X_test_scaled)
submission = pd.DataFrame({'Id': test_data['Id'], 'SalePrice': test_pred})
submission.to_csv('submission.csv', index=False)
print("提交文件已生成!")
6. 总结
通过这篇文章,我们从最简单的单变量线性回归出发,逐步探讨了多变量场景下的模型构建、评估与优化方法,并结合实战案例展示了线性回归的应用潜力。希望这些内容能帮助你:
- 理解核心概念:从数据规律到最小二乘法,掌握线性回归的本质。
- 快速上手实践 :通过代码示例熟悉
sklearn
的建模流程。 - 优化模型性能:学会特征工程与模型调优的常用技巧。
- 挑战真实问题:用 Kaggle 竞赛检验学习成果。
学习线性回归是踏入机器学习的第一步,但真正的提升还需动手实践与持续思考。如果这篇文章对你有所启发,欢迎点赞❤️收藏⭐️支持!也欢迎在评论区分享你的学习心得或疑问,我们一起进步!
下期预告:AI 学习之路(四)- 逻辑回归问题