假设你正在尝试预测房屋的价格。通常情况下,房子的面积越大,价格也越高;房间数量越多,价格往往也随之上升。但仅仅依靠“大概更贵”这样的判断是不够的——你需要一个可以量化、能给出具体数值的预测方法。
线性回归正是为此而生:它通过寻找一条最优直线,利用这条直线对未知数据进行连续数值的预测。
即使不学习,也能获得50分的基础分数。每增加1小时的学习时间,成绩平均提升10分。例如,学习5小时后的预计得分为:
50 + 10 × 5 = 100 分
成绩 = 50 + 10 × 学习时间
刚毕业时起薪为3000元,之后每多工作一年,工资上涨500元。这种稳定的增长趋势可以用一条直线来近似描述。
工资 = 3000 + 500 × 工作年限
在现实世界中,数据点通常不会完美地落在同一直线上。线性回归的目标是找到这样一条直线——使得所有实际数据点到该直线的垂直距离的平方和最小。
这条使误差最小化的直线就是我们建立的预测模型。
房价(万)
↑
| ●
| ●
|● ●
| ●
| ●
+------------------→ 面积(㎡)
线性回归的基本方程如下所示:
y = wx + b
通过编写基础代码来手动计算参数w和b,有助于深入掌握算法内部机制。
import numpy as np
import matplotlib.pyplot as plt
class SimpleLinearRegression:
def __init__(self):
self.w = 0 # 权重
self.b = 0 # 偏置
def fit(self, X, y):
"""
训练模型:找到最佳的w和b
使用最小二乘法
"""
# 将数据转换为numpy数组
X = np.array(X)
y = np.array(y)
# 最小二乘法公式:
# w = Σ[(xi - x?)(yi - ?)] / Σ[(xi - x?)?]
# b = ? - w*x?
x_mean = np.mean(X)
y_mean = np.mean(y)
numerator = np.sum((X - x_mean) * (y - y_mean))
denominator = np.sum((X - x_mean) ** 2)
self.w = numerator / denominator
self.b = y_mean - self.w * x_mean
print(f"训练完成!")
print(f"直线方程: y = {self.w:.2f}x + {self.b:.2f}")
def predict(self, X):
"""预测"""
return self.w * np.array(X) + self.b
def score(self, X, y):
"""计算R?决定系数(衡量拟合好坏)"""
X = np.array(X)
y = np.array(y)
predictions = self.predict(X)
# 总平方和
ss_tot = np.sum((y - np.mean(y)) ** 2)
# 残差平方和
ss_res = np.sum((y - predictions) ** 2)
r_squared = 1 - (ss_res / ss_tot)
return r_squared
# 准备房价数据:面积(㎡) -> 价格(万)
areas = [50, 80, 100, 120, 150, 180, 200, 220, 250, 280]
prices = [80, 120, 150, 180, 220, 260, 290, 320, 360, 400]
# 创建并训练模型
model = SimpleLinearRegression()
model.fit(areas, prices)
# 预测
test_areas = [60, 130, 200, 300]
predicted_prices = model.predict(test_areas)
print(f"\n预测结果:")
for area, price in zip(test_areas, predicted_prices):
print(f"面积{area}㎡ -> 预测价格{price:.1f}万")
# 评估模型
r2 = model.score(areas, prices)
print(f"\n模型表现:R? = {r2:.3f} (越接近1越好)")
运行后得到的结果如下:
训练完成!
直线方程: y = 1.43x + 8.57
预测结果:
面积60㎡ -> 预测价格94.5万
面积130㎡ -> 预测价格194.1万
面积200㎡ -> 预测价格295.0万
面积300㎡ -> 预测价格437.9万
模型表现:R? = 0.992 (越接近1越好)
在实际项目中,推荐使用成熟的机器学习库scikit-learn来快速构建模型,提高开发效率。
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error, r2_score
import matplotlib.pyplot as plt
# 准备数据(注意:sklearn要求二维数组)
X = np.array(areas).reshape(-1, 1) # 转换成n×1的矩阵
y = np.array(prices)
# 创建模型
sk_model = LinearRegression()
# 训练模型
sk_model.fit(X, y)
# 查看学到的参数
print(f"sklearn训练结果:")
print(f"权重w: {sk_model.coef_[0]:.2f}")
print(f"偏置b: {sk_model.intercept_:.2f}")
print(f"方程: y = {sk_model.coef_[0]:.2f}x + {sk_model.intercept_:.2f}")
# 预测
test_X = np.array(test_areas).reshape(-1, 1)
sk_predictions = sk_model.predict(test_X)
# 评估
sk_r2 = r2_score(y, sk_model.predict(X))
sk_mse = mean_squared_error(y, sk_model.predict(X))
print(f"\nsklearn模型表现:")
print(f"R? = {sk_r2:.3f}")
print(f"均方误差 = {sk_mse:.2f}")
# 可视化
plt.figure(figsize=(10, 6))
plt.scatter(areas, prices, color='blue', label='实际数据')
plt.plot(areas, sk_model.predict(X), color='red', linewidth=2, label='拟合直线')
plt.xlabel('面积(㎡)')
plt.ylabel('价格(万)')
plt.title('房价与面积的关系')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()
为了评估模型预测结果的好坏,我们需要一个衡量标准——这就是损失函数。
在线性回归中,最常用的损失函数是均方误差(MSE),其定义如下:
MSE = (1/n) × Σ(yi - ?i)?
MSE越小,说明模型的预测结果越接近真实值。
当问题变得复杂,无法直接求解闭式解时,我们可以采用迭代优化方法——梯度下降。
该方法通过不断调整参数w和b,沿着损失函数下降最快的方向逐步逼近最优解。
class GradientDescentLinearRegression:
def __init__(self, learning_rate=0.01, n_iterations=1000):
self.learning_rate = learning_rate
self.n_iterations = n_iterations
self.w = 0
self.b = 0
self.loss_history = []
def compute_loss(self, X, y):
"""计算均方误差"""
predictions = self.w * X + self.b
return np.mean((y - predictions) ** 2)
def fit(self, X, y):
"""使用梯度下降训练"""
X = np.array(X)
y = np.array(y)
n = len(X)
for i in range(self.n_iterations):
# 计算预测值
predictions = self.w * X + self.b
# 计算梯度
dw = (-2/n) * np.sum(X * (y - predictions)) # w的梯度
db = (-2/n) * np.sum(y - predictions) # b的梯度
# 更新参数
self.w -= self.learning_rate * dw
self.b -= self.learning_rate * db
# 记录损失
loss = self.compute_loss(X, y)
self.loss_history.append(loss)
# 每100次迭代打印进度
if i % 100 == 0:
print(f"迭代{i}: 损失 = {loss:.2f}, w = {self.w:.2f}, b = {self.b:.2f}")
def predict(self, X):
return self.w * np.array(X) + self.b
# 使用梯度下降版本
gd_model = GradientDescentLinearRegression(learning_rate=0.001, n_iterations=1000)
gd_model.fit(areas, prices)
print(f"\n梯度下降结果:")
print(f"方程: y = {gd_model.w:.2f}x + {gd_model.b:.2f}")
# 绘制损失下降过程
plt.figure(figsize=(10, 4))
plt.plot(gd_model.loss_history)
plt.xlabel('迭代次数')
plt.ylabel('损失值')
plt.title('梯度下降优化过程')
plt.grid(True, alpha=0.3)
plt.show()
在真实场景中,影响结果的因素往往是多个的。比如在房价预测中,除了面积和房间数,还可能包括楼层、朝向、地段等因素。
此时,模型扩展为多变量形式:
房价 = w?×面积 + w?×房间数 + w?×地段评分 + b
对应的向量表达式为:
# 多变量数据:面积、房间数、地段评分 -> 房价
X_multi = np.array([
[120, 3, 8], # 房子1
[80, 2, 6], # 房子2
[150, 4, 9], # 房子3
[90, 2, 7], # 房子4
[110, 3, 7], # 房子5
[200, 5, 9] # 房子6
])
y_multi = np.array([300, 180, 450, 220, 280, 520]) # 对应房价
# 使用sklearn的多变量线性回归
multi_model = LinearRegression()
multi_model.fit(X_multi, y_multi)
print("多变量线性回归结果:")
print(f"权重 (面积, 房间数, 地段): {multi_model.coef_}")
print(f"偏置: {multi_model.intercept_:.2f}")
print(f"方程: 房价 = {multi_model.coef_[0]:.2f}×面积 + {multi_model.coef_[1]:.2f}×房间数 + {multi_model.coef_[2]:.2f}×地段 + {multi_model.intercept_:.2f}")
# 预测新房子
new_house = np.array([[100, 2, 7]]) # 100㎡, 2室, 地段7分
predicted_price = multi_model.predict(new_house)[0]
print(f"\n预测新房子价格: {predicted_price:.1f}万")
# 非线性数据示例
np.random.seed(42)
X_nonlinear = np.linspace(0, 10, 100)
y_nonlinear = 2 * X_nonlinear + 3 * X_nonlinear**2 + np.random.normal(0, 10, 100)
# 用线性回归拟合非线性数据
linear_model = LinearRegression()
linear_model.fit(X_nonlinear.reshape(-1, 1), y_nonlinear)
# 可视化对比
plt.figure(figsize=(12, 4))
plt.subplot(1, 2, 1)
plt.scatter(X_nonlinear, y_nonlinear, alpha=0.6, label='真实数据')
plt.plot(X_nonlinear, linear_model.predict(X_nonlinear.reshape(-1, 1)),
color='red', label='线性拟合')
plt.legend()
plt.title('线性回归拟合非线性数据(效果差)')
# 多项式回归效果更好
from sklearn.preprocessing import PolynomialFeatures
from sklearn.pipeline import Pipeline
poly_model = Pipeline([
('poly', PolynomialFeatures(degree=2)),
('linear', LinearRegression())
])
poly_model.fit(X_nonlinear.reshape(-1, 1), y_nonlinear)
plt.subplot(1, 2, 2)
plt.scatter(X_nonlinear, y_nonlinear, alpha=0.6, label='真实数据')
plt.plot(X_nonlinear, poly_model.predict(X_nonlinear.reshape(-1, 1)),
color='green', label='多项式拟合')
plt.legend()
plt.title('多项式回归拟合非线性数据(效果好)')
plt.tight_layout()
plt.show()
# 加入异常值的影响
normal_areas = [50, 80, 100, 120, 150, 180, 200]
normal_prices = [80, 120, 150, 180, 220, 260, 290]
# 加入一个异常值:面积很小但价格极高
outlier_areas = normal_areas + [60] # 多加一个点
outlier_prices = normal_prices + [1000] # 这个房子60㎡却卖1000万!
# 分别训练模型
normal_model = LinearRegression().fit(
np.array(normal_areas).reshape(-1, 1), normal_prices)
outlier_model = LinearRegression().fit(
np.array(outlier_areas).reshape(-1, 1), outlier_prices)
# 可视化对比
test_x = np.linspace(40, 220, 100).reshape(-1, 1)
plt.figure(figsize=(10, 6))
plt.scatter(normal_areas, normal_prices, color='blue', s=50, label='正常数据')
plt.scatter([60], [1000], color='red', s=100, marker='x', label='异常值')
plt.plot(test_x, normal_model.predict(test_x), 'b-', label='正常数据拟合')
plt.plot(test_x, outlier_model.predict(test_x), 'r--', label='含异常值拟合')
plt.xlabel('面积(㎡)')
plt.ylabel('价格(万)')
plt.legend()
plt.title('异常值对线性回归的影响')
plt.grid(True, alpha=0.3)
plt.show()
print("正常数据模型的斜率:", normal_model.coef_[0])
print("含异常值模型的斜率:", outlier_model.coef_[0])
# 解释模型结果
print("=== 模型解释 ===")
print(f"面积每增加1㎡,房价平均增加{multi_model.coef_[0]:.1f}万")
print(f"房间数每增加1间,房价平均增加{multi_model.coef_[1]:.1f}万")
print(f"地段评分每增加1分,房价平均增加{multi_model.coef_[2]:.1f}万")
print(f"即使什么都没有(面积=0),基础房价是{multi_model.intercept_:.1f}万")
线性回归擅长预测连续值,但在许多现实问题中,我们需要的是分类判断:
逻辑回归正是解决这类分类任务的强大工具。我们将带你了解如何让机器学会“做选择题”,开启分类模型的学习之旅。
扫码加好友,拉您进群



收藏
