机器学习

1 案例 预测房屋价格

1.1 数据介绍

台湾新北市新店区的房地产估值的市场历史数据集。
字段介绍

X1 - 交易日期,例如2013.250表示2013年3月,2013.500表示2013年6月
X2 - 房屋年龄 (单位:年)
X3 - 到最近的捷运站的距离 (单位:米)
X4 - 步行生活圈中便利店的数量 (整数)
X5 - 地理坐标纬度 (单位:度)
X6 - 地理坐标经度 (单位:度)

标签Y表示单位面积房价 (10000新台币/ Ping,其中Ping是本地单位,1 Ping = 3.3平方米)

1.2 构建模型

导入数据

import pandas as pd
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error as MSE
from sklearn.metrics import r2_score

df = pd.read_excel('./datasets/house.xlsx')
df = df.drop(labels='No', axis=1)
df.head(3)
'''
	X1 transaction date 	X2 house age 	X3 distance to the nearest MRT station 	X4 number of convenience stores 	X5 latitude 	X6 longitude 	Y house price of unit area
0 	2012.916667 			32.0 			84.87882 								10 									24.98298 		121.54024 		37.9
1 	2012.916667 			19.5 			306.59470 								9 									24.98034 		121.53951 		42.2
2 	2013.583333 			13.3 			561.98450 								5 									24.98746 		121.54391 		47.3
'''
feature =  df.loc[:,df.columns != 'Y house price of unit area']
target = df['Y house price of unit area']

feature.shape  # (414, 6)

x_train, x_test, y_train, y_test = train_test_split(feature, target, test_size=0.2, random_state=2020)

lr = LinearRegression()
lr.fit(x_train,y_train)
1.3 评估模型
y_test_pred = lr.predict(x_test)

# 均方误差MSE
MSE(y_test, y_test_pred)  # 55.33406050090903
y_test.max()  # 63.2
y_test.min()  # 13.8

# R方
r2_score(y_test, y_test_pred)  # 0.6108181277767377
lr.score(x_test, y_test)  # 0.6108181277767377

用训练集评估模型

y_train_pred = lr.predict(x_train)

# 均方误差MSE
MSE(y_train, y_train_pred)  # 83.00347064630712
y_train.max()  # 117.5
y_train.min()  # 7.6

# R方
r2_score(y_train, y_train_pred)  # 0.5750984249253508
lr.score(x_train, y_train)  # 0.5750984249253508

发现模型在训练集和测试集中的表现都不好。
此时出现的问题称为欠拟合。

2 欠拟合和过拟合

  1. 欠拟合
    欠拟合(Underfitted)表现为模型在训练集和训练集的误差均较大。
    产生欠拟合的原因是模型学习到的特征过少,模型过于简单,导致模型的回归函数较为粗糙,不能对训练集做出准确拟合,因此误差较大。
    可以通过增加特征维度来解决欠拟合问题。

  2. 过拟合
    欠拟合(Overfitted)表现为模型在训练集上表现很好,但在测试集上存在较大的误差,即模型太过于依赖的训练集数据,泛化能力较差。
    产生过拟合的原因是模型学习到的特征过多,模型过于复杂,导致模型的回归函数虽然能够完美地对训练数据集进行拟合,但是对新数据的预测存在较大误差。
    可以通过正则化来解决过拟合问题。

  3. 泛化能力
    泛化能力(Generalization Ability)是指模型对未知数据进行预测或分类的表现。

在这里插入图片描述

3 解决欠拟合问题 - 多项式回归

3.1 介绍

欠拟合问题是由于模型过于简单导致的,即线性回归模型中特征与标签的关系过于简单,只能是线性关系。
需要通过增加特征维度来增加模型的复杂程度,回归问题中可以通过增加高次项特征来实现,这样线性回归模型就转化为多项式回归(Polynomial Regression)模型,多项式回归模型中每一项都可以理解为一个特征维度。

3.2 PolynomialFeatures
sklearn.preprocessing.PolynomialFeatures(degree=2, interaction_only=False, include_bias=True)

参数说明
假设存在 a a a b b b两个特征,2次多项式为 [ 1 , a , b , a 2 , b 2 ] [1, a, b, a^2, b^2] [1,a,b,a2,b2]

  1. degree:用于指定多项式的次数,默认为2;
  2. interaction_only:是否允许出现特征与自己结合的项,默认为False,如果指定interaction_only=True,上面例子中的2次多项式不会存在 a 2 a^2 a2 b 2 b^2 b2
  3. include_bias:是否允许出现0次幂的项,默认为True,如果指定include_bias=False,上面例子中的2次多项式不会存在 1 1 1
3.3 案例

使用多项式回归处理上面的案例。

3.3.1 准备数据
import pandas as pd
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error as MSE
from sklearn.metrics import r2_score

df = pd.read_excel('./datasets/house.xlsx')
df = df.drop(labels='No', axis=1)
feature =  df.loc[:,df.columns != 'Y house price of unit area']
target = df['Y house price of unit area']
3.3.2 增加高次项特征

向数据集中增加高次项特征,这里使用二次项。

from sklearn.preprocessing import PolynomialFeatures
p2 = PolynomialFeatures(degree=2)  # PolynomialFeatures(degree=2, include_bias=True, interaction_only=False, order='C')
d2_feature = p2.fit_transform(feature)
3.3.3 构建模型
x_train, x_test, y_train, y_test = train_test_split(d2_feature, target, test_size=0.2, random_state=2020)

lr = LinearRegression()
lr.fit(x_train,y_train)
3.3.4 评估模型
y_pred = lr.predict(x_test)

MSE(y_test, y_pred)  # 30.830183307250028
lr.score(x_test, y_test)  # 0.7831616123616155

4 解决过拟合问题 - 正则化

4.1 过拟合产生原因分析

上面的案例中,如果增加的特征维度设置过大,会导致模型处理测试集时产生的误差非常大。

from sklearn.preprocessing import PolynomialFeatures

p5 = PolynomialFeatures(degree=5)  # 维度设置为5
d5_feature = p5.fit_transform(feature)
x_train, x_test, y_train, y_test = train_test_split(d5_feature, target, test_size=0.2, random_state=2020)

lr = LinearRegression()
lr.fit(x_train, y_train)

# 测试集
y_test_pred = lr.predict(x_test)
MSE(y_test, y_pred)  # 996.1982154441408
lr.score(x_test, y_test)  # -6.00657575248168

# 训练集
y_train_pred = lr.predict(x_train)
MSE(y_train, y_train_pred)  # 29.21333530526558
lr.score(x_train, y_train)  # 0.8504545401808031

可以看出,模型在训练集上的表现非常好,但在测试集上的表现却很差。换句话说,模型过于依赖训练集,在训练集上可以做出非常好的拟合,但是处理新的数据会产生较大的误差,此时模型发生了过拟合现象。

4.2 正则化

发生过拟合的原因是模型过于复杂,包含了许多不必要的特征。
解决方法包括:

  1. 添加数据,使得数据量远大于特征数量;
  2. 通过特征选择或者特征提取减少特征数量;
  3. 正则化。

这里主要介绍正则化。
与正常拟合的曲线相比,过拟合的曲线包含了许多幅度较大的凹凸,因此直观的处理方法是降低曲线的凹凸幅度。曲线的凹凸一般是由模型中的高次项特征产生的,正则化就是通过不断的尝试来降低模型高次项特征的权重,使其不断趋近于0。
正则化的本质是对参数空间添加约束,正则化框架:
L ( w ) + α P ( w ) = ∣ ∣ y − X w ∣ ∣ 2 2 + α ∣ ∣ w ∣ ∣ 2 2 L(\boldsymbol{w}) + α P(\boldsymbol{w})= {||\boldsymbol{y-Xw}||}_2^2 + α||\boldsymbol{w}||^2_2 L(w)+αP(w)=yXw22+αw22
其中, L ( w ) L(w) L(w)是损失函数, P ( w ) P(w) P(w)是惩罚项, α α α是超参数。

正则化分类

  1. L1正则化,也称为Lasso回归, P ( w ) = ∣ ∣ w ∣ ∣ 1 P(\boldsymbol{w})=||\boldsymbol{w}||_1 P(w)=w1
  2. L2正则化,也称为Ridge回归,即岭回归, P ( w ) = ∣ ∣ w ∣ ∣ 2 2 = w T w P(\boldsymbol{w})=||\boldsymbol{w}||^2_2=\boldsymbol {w}^T\boldsymbol {w} P(w)=w22=wTw
4.3 岭回归
4.3.1 介绍

岭回归模型(Ridge Regression)是具备L2正则化的线性回归模型,通过引入一个特征参数的惩罚项来减小参数值。
arg min ⁡ w [ ∣ ∣ y − X w ∣ ∣ 2 2 + α ∣ ∣ w ∣ ∣ 2 2 ] \argmin_{\boldsymbol w} [{||\boldsymbol{y-Xw}||}_2^2 + α||\boldsymbol{w}||^2_2] wargmin[yXw22+αw22]
其中 α α α表示正则化的力度, α α α越大,高次项的权重越接近于0,过拟合曲线中的凹凸幅度越小。

w ^ = arg min ⁡ w [ ∣ ∣ y − X w ∣ ∣ 2 2 + α ∣ ∣ w ∣ ∣ 2 2 ] \hat{\boldsymbol w} = \argmin_{\boldsymbol w} [{||\boldsymbol{y-Xw}||}_2^2 + α||\boldsymbol{w}||^2_2] w^=wargmin[yXw22+αw22]
通过令上式对 w \boldsymbol w w的偏导为0,计算出
w ^ = ( X T X + α I ) − 1 X T y \hat{\boldsymbol w} = (\boldsymbol{X}^T\boldsymbol{X} + α\boldsymbol{I})^{-1}\boldsymbol{X}^T\boldsymbol{y} w^=(XTX+αI)1XTy
矩阵 X T X + α I \boldsymbol{X}^T\boldsymbol{X} + α\boldsymbol{I} XTX+αI一定是正定的(可逆的)。

使用最小二乘法直接对损失函数进行估计(上一节),得到
w ^ = ( X T X ) − 1 X T y \hat{\boldsymbol w} = (\boldsymbol{X}^T\boldsymbol{X})^{-1}\boldsymbol{X}^T\boldsymbol{y} w^=(XTX)1XTy
通过对比可以发现,带超参数 α α α的惩罚项的作用是对所有的参数造成衰减,因此岭回归也称为权值衰减。

4.3.2 代码实现
x_train = [[6], [8], [10], [14], [18]]
y_train = [[7], [9], [13], [17.5], [18]]

pf = PolynomialFeatures(degree=5)
d5_train = pf.fit_transform(x_train)

lr = LinearRegression()
lr.fit(d5_train, y_train)

pf.powers_
'''
array([[0], [1], [2], [3], [4], [5]], dtype=int64)
'''

lr.coef_
'''
array([[ 8.58514662e-11, -2.63443321e-01, -1.29176988e+00,
         2.66898824e-01, -1.79389838e-02,  3.97068908e-04]])
'''

使用岭回归模型,通过控制正则化力度参数alpha降低高次项特征的权重。

from sklearn.linear_model import Ridge

x_train = [[6], [8], [10], [14], [18]]
y_train = [[7], [9], [13], [17.5], [18]]

pf = PolynomialFeatures(degree=5)
d5_train = pf.fit_transform(x_train)

ridge = Ridge(alpha=0.5)
ridge.fit(d5_train, y_train)
ridge.coef_
'''
array([[ 0.00000000e+00, -8.97118441e-02, -4.38385511e-01,
         1.06492140e-01, -7.35242572e-03,  1.60866438e-04]])
'''

5 模型的保存与加载

使用pickle实现模型的保存与加载。

import pickle

with open('./ridge.pkl','wb') as fp:
    pickle.dump(ridge,fp)
import pickle

ridge = None
with open('./ridge.pkl','rb') as fp:
    ridge = pickle.load(fp)
    
print(ridge)  # Ridge(alpha=0.5, copy_X=True, fit_intercept=True, max_iter=None, normalize=False, random_state=None, solver='auto', tol=0.001)
Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐