机器学习——上机3——线性回归(医疗保险费预测)

线性回归

任务1. 一元线性回归

任务介绍:

  • 自定义一元回归函数MyLinearRegression(),输入参数为x和y的数组xArr和yArr,输出为参数w1和w0,利用最小二乘法求得参数;
  • 使用美国医疗保险费数据insurance.csv中的输入特征age和目标特征charges,输入MyLinearRegression()函数,得到回归参数值w1和w0,并保留到小数点后两位;
  • 调用sklearn的LinearRegression()函数,比较其运行结果与上述自定义函数MyLinearRegression()的输出结果是否一致。
  • 利用age与charges绘制真实样本点,利用w1与w0计算预测值,再绘制age与预测值的点图,观察真实样本点与预测点之间的拟合程度。

补全代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression

insurance = pd.read_csv('insurance.csv')
age = insurance['age'].values
charges = insurance['charges'].values

# 请在下方作答
# 定义一元线性回归函数
def MyLinearRegression(xArr, yArr):

# x均值, y均值计算
mean_x = xArr.mean()
mean_y = yArr.mean()

# w0, w1计算,公式
numerator = np.sum((xArr - mean_x) * (yArr - mean_y))
denominator = np.sum((xArr - mean_x)**2)
w1 = numerator / denominator
w0 = mean_y - w1 * mean_x

return round(w0,2), round(w1,2)

print("模型训练,得到参数值")
w0, w1 = MyLinearRegression(age, charges)
print(w1,'\n', w0)

print("sklearn的训练结果")
lr = LinearRegression()
#线性回归模型训练
lr.fit(age.reshape(-1, 1), charges)
print(round(lr.coef_[0],2))
print(round(lr.intercept_,2))

# 观察真实样本点与预测点之间的拟合程度
plt.scatter(age, charges, marker='.') # 画样本点,随机散点
# 利用w1与w0计算预测值,绘制预测点
plt.scatter(age, w1 * age + w0, marker='+') # 画预测点,形成直线
plt.show()

# 请直接运行处结果,然后提交作业,该运行结果会自动一同提交上去
模型训练,得到参数值
257.72 
 3165.89
sklearn的训练结果
257.72
3165.89
png

最小二乘法求解公式

目标:最小化预测值与真实值的平方误差之和: $$ \min_{w_0, w_1} \sum_{i=1}^n (y_i - \hat{y}_i)^2 $$

闭式解(Normal Equation)
1. 斜率 ( w_1 )
$$ w_1 = \frac{\sum_{i=1}^n (x_i - \bar{x})(y_i - \bar{y})}{\sum_{i=1}^n (x_i - \bar{x})^2} $$
其中 ({x}) 和 ({y}) 分别是 (x) 和 (y) 的均值。

  1. 截距 ( w_0 )
    w0 =  − w1

round(w0, 2) 和 round(w1, 2) 的作用是对线性回归模型的参数进行四舍五入处理,保留两位小数。

这段代码使用 scikit-learnLinearRegression 类实现线性回归,并输出模型参数。以下是逐行解释: ### 1. 创建线性回归模型实例

1
lr = LinearRegression()
- LinearRegression()scikit-learn 中用于线性回归的类。 - lr 是该类的一个实例,后续通过它调用模型训练、预测等方法。 ### 2. 模型训练
1
lr.fit(age.reshape(-1, 1), charges)
- 作用:用输入数据 age(特征)和 charges(目标值)训练线性回归模型。 - 关键细节: - age 是一维数组(形状如 (n,)),但 scikit-learn 要求输入特征为二维数组(形状如 (n, 1))。 - age.reshape(-1, 1) 将一维数组转换为二维列向量(n 行 1 列),确保输入格式正确。 - charges 是目标值的一维数组,无需调整形状。 ### 3. 输出模型参数
1
2
print(round(lr.coef_[0], 2))
print(round(lr.intercept_, 2))
- lr.coef_: - 存储模型的回归系数(即 w1,特征权重)。 - 对于一元线性回归,coef_ 是一个包含单个元素的数组(如 [w1]),因此用 coef_[0] 提取数值。 - lr.intercept_: - 存储模型的截距项(即 w0)。 - 直接通过 intercept_ 访问,无需索引。 - round(..., 2):将参数四舍五入保留两位小数,便于与自定义函数结果对比。

任务2. 多元线性回归

任务介绍:

  • 自定义多元线性回归函数MyLinearRegression2(),输入参数为X和y的数组xArr和yArr,输出为参数ws,利用最小二乘法求得参数;
  • 使用美国医疗保险费数据insurance.csv中的输入特征age、bmi和children,目标特征charges,根据MyLinearRegression2()函数,得到回归参数值ws;注意判断(X^T X)^{-1}是否为满秩,如果满秩,则引入正则项,参数为alpha,目标函数变为岭回归问题。
  • 为了得到模型的截距,需要在矩阵X最后增加一列,并且该列所有行的值均为1。
  • 调用sklearn的LinearRegression()函数,比较其运行结果与上述自定义函数MyLinearRegression2()的输出结果是否一致。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
from sklearn.linear_model import LinearRegression
import numpy as np
from numpy import linalg, column_stack, ones, array
import pandas as pd
insurance = pd.read_csv('insurance.csv')

# 请在下方作答 #
# 定义多元线性回归函数
def MyLinearRegression2(xArr, yArr):

# 调用mat将Array转换为矩阵
xMat = np.asmatrix(xArr)
yMat = np.asmatrix(yArr).T # 转换为列向量
xTx = xMat.T*xMat
# 通过调用linalg计算行列式来判定协方差矩阵是否可逆
if linalg.det(xTx) < 1e-8:
print( "singular matrix, can't do inverse")
# 引入正则项,即 岭回归
alpha = 0.1
# 添加岭回归正则项(单位矩阵大小为特征数+1)
xTx += alpha * np.eye(xMat.shape[1])

# 计算参数向量
# 计算参数并转为一维数组
ws = xTx.I * (xMat.T * yMat)

return ws

# 模型训练,得到参数值
X = insurance[['age', 'bmi', 'children']].values
# 调用column_stack函数在矩阵X后增加一列,并且该列所有行的值均为1
# 添加截距列(全1)
X = column_stack((X, ones(X.shape[0])))
y = insurance['charges']
ws = MyLinearRegression2(X, y)
print("自定义的训练结果")
print(ws)
# sklearn的训练结果
lr = LinearRegression(fit_intercept=False) # 关键:禁用自动截距
#线性回归模型训练
lr.fit(X, y)
print("sklearn的训练结果")
print(lr.coef_)
print(lr.intercept_)

# 请直接运行处结果,然后提交作业,该运行结果会自动一同提交上去
自定义的训练结果
[[  239.99447429]
 [  332.0833645 ]
 [  542.86465225]
 [-6916.24334779]]
sklearn的训练结果
[  239.99447429   332.0833645    542.86465225 -6916.24334779]
0.0

任务3. 线性回归应用:预测医疗费用

任务介绍

  • 对insurance.csv中的名义型特征进行One-Hot编码,得到了数据变量insurance
  • 请使用自定义的多元回归函数MyLinearRegression2()得到回归模型参数ws和预测值y_pred,并计算R2分数
  • 比较使用sklearn进行模型训练和模型评价R2分数的结果

复用上一节实验中实现的代码,可以复制粘贴代替下面的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
import pandas as pd
import numpy as np
from sklearn import linear_model, metrics
from numpy import array, mean, ones
import matplotlib.pyplot as plt

# 调用get_dummies函数对非数值型特征进行 one-hot 编码处理,以便于运算
insurance = pd.read_csv('insurance.csv')
insurance = pd.get_dummies(insurance, drop_first=True) # One-Hot编码
print(insurance.shape)

# 从insurance中获取X与y
X = insurance.drop(['charges'], axis=1).values.astype(np.float64)
y = insurance['charges'].values.astype(np.float64)

# 对每个特征与y的关系进行可视化,观察与y的相关性
plt.figure(figsize=(20,10))
for i in range(X.shape[1]):
plt.subplot(2,6,i+1)
plt.scatter(array(X)[:,i],y,s=20)
plt.show()

def MyLinearRegression2(xArr, yArr):

# 调用mat将Array转换为矩阵
xMat = np.asmatrix(xArr)
yMat = np.asmatrix(yArr).T # 转换为列向量
xTx = xMat.T*xMat
# 通过调用linalg计算行列式来判定协方差矩阵是否可逆
if np.linalg.det(xTx) < 1e-8:
print( "singular matrix, can't do inverse")
# 引入正则项,即 岭回归
alpha = 0.1
# 添加岭回归正则项(单位矩阵大小为特征数+1)
xTx += alpha * np.eye(xMat.shape[1])

# 计算参数向量
# 计算参数并转为一维数组
ws = xTx.I * (xMat.T * yMat)

return ws

# 根据X、y和自定义函数MyLinearRegression2()训练模型参数ws,并计算X的预测值y_pred
ws = MyLinearRegression2(X, y)
y_pred = X.dot(ws)
y_pred = array(y_pred).reshape(y_pred.shape[0],) # 将矩阵转换为一行多列的array格式

# 调用metrics中的r2_score函数根据y和y_pred计算决定系数score
score = metrics.r2_score(y, y_pred)

# sklearn模型训练与预测
lr = linear_model.LinearRegression(fit_intercept=False)
# 模型训练
lr.fit(X, y)
# 计算X的预测值y_pred_sk与R2分数score_sk
y_pred_sk = lr.predict(X) # 使用训练好的sklearn模型进行预测
score_sk = metrics.r2_score(y, y_pred_sk) # 计算决定系数R²
print(score_sk)

# 请直接运行处结果,然后提交作业,该运行结果会自动一同提交上去
(1338, 9)
png
0.7235368166092777
1