机器学习入门 06 多项式回归

本文介绍了多项式回归的基础知识及其实现,并通过对比线性回归展示了其优势。此外,文章还详细探讨了模型泛化能力的重要性,包括如何通过学习曲线、交叉验证等方法来评估模型的泛化能力。最后,针对过拟合问题,本文深入讲解了正则化的概念及其应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1 多项式回归

1.1 介绍

多项式回归,用来对非线性数据集进行处理,例如二次函数。

1.2 代码

1)多项式回归和线性回归比较

例:y=0.5x2+x+2+noisey=0.5{ x }^{ 2 }+x+2+noisey=0.5x2+x+2+noise

import numpy as np
import matplotlib.pyplot as plt

# 准备数据
x=np.random.uniform(-3,3,size=100)
xx=x.reshape(-1,1)
y=0.5*x**2+x+2+np.random.normal(0,1,size=100)

plt.scatter(x,y)
plt.show()

这里写图片描述

# 线性回归
from sklearn.linear_model import LinearRegression

lin_reg=LinearRegression()
lin_reg.fit(xx,y)
y_predict=lin_reg.predict(xx)

plt.scatter(x,y)
plt.plot(x,y_predict,color='r')
plt.show()

这里写图片描述

# 多项式回归,添加一个特征
xx2=np.hstack([xx,xx**2])

lin_reg2=LinearRegression()
lin_reg2.fit(xx2,y)
y_predict2=lin_reg2.predict(xx2)

plt.scatter(x,y)
plt.plot(np.sort(x),y_predict2[np.argsort(x)],color='r')
plt.show()
# 多项式的系数
lin_reg2.coef_ # array([0.95895082, 0.52687416])

这里写图片描述

2 模型泛化相关

2.1 模型复杂度曲线

这里写图片描述

测试数据集的意义:寻找泛化能力最好的地方。

2.2 学习曲线

含义:随着训练样本的逐渐增多,算法训练出的模型的表现能力。

问题:特定的测试数据集出现过拟合的情况。
解决方案:将样本数据集分为:训练、验证、测试数据集。

  • 训练数据集 -> 用来产生模型
  • 验证数据集 -> 调整超参数使用的数据集
  • 测试数据集 -> 衡量最终模型性能的数据集

代码演示:

import numpy as np
import matplotlib.pyplot as plt

# 准备数据
x=np.random.uniform(-3,3,size=100)
X=x.reshape(-1,1)
y=0.5*x**2+x+2+np.random.normal(0,1,size=100)

plt.scatter(x,y)
plt.show()

pic

# 学习曲线
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error

X_train,X_test,y_train,y_test=train_test_split(X,y,random_state=666)

train_score=[]
test_score=[]
for i in range(1,76):
    lin_reg=LinearRegression()
    lin_reg.fit(X_train[:i],y_train[:i])
    # 训练集的预测值
    y_train_predict=lin_reg.predict(X_train[:i])
    train_score.append(mean_squared_error(y_train[:i],y_train_predict))
    # 测试集的预测值
    y_test_predict=lin_reg.predict(X_test)
    test_score.append(mean_squared_error(y_test,y_test_predict))

plt.plot([i for i in range(1,76)],np.sqrt(train_score),label='train')
plt.plot([i for i in range(1,76)],np.sqrt(test_score),label='test')
plt.legend()
plt.show()

pic

2.3 交叉验证 (应对验证数据集中有极端值出现的情况)

将下图中n个模型的均值作为结果调参。

代码演示:

import numpy as np
from sklearn import datasets

# 加载数据集
digits=datasets.load_digits()
X=digits.data
y=digits.target

# 测试 train_test_split
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier

X_train,X_test,y_train,y_test=train_test_split(X,y,test_size=0.4,random_state=666)
best_score,best_p,best_k=0,0,0
for k in range(2,11):
    for p in range(1,6):
        knn_clf=KNeighborsClassifier(weights='distance',n_neighbors=k,p=p)
        knn_clf.fit(X_train,y_train)
        score=knn_clf.score(X_test,y_test)
        if score>best_score:
            best_score,best_p,best_k=score,p,k
            
print("best k= ",best_k)
print("best p= ",best_p)
print("best score= ",best_score)

结果:

best k=  3
best p=  4
best score=  0.9860917941585535
# 使用交叉验证
from sklearn.model_selection import cross_val_score

knn_clf=KNeighborsClassifier()
cross_val_score(knn_clf,X_train,y_train) # 生成了三个模型的评分 
# > array([0.98895028, 0.97777778, 0.96629213])
best_score,best_p,best_k=0,0,0
for k in range(2,11):
    for p in range(1,6):
        knn_clf=KNeighborsClassifier(weights='distance',n_neighbors=k,p=p)
        # 交叉验证
        scores=cross_val_score(knn_clf,X_train,y_train)
        score=np.mean(scores)
        if score>best_score:
            best_score,best_p,best_k=score,p,k
            
print("best k= ",best_k)
print("best p= ",best_p)
print("best score= ",best_score)
'''
> best k=  2
> best p=  2
> best score=  0.9823599874006478
'''
# 获得最佳的knn分类器
best_knn_clf=KNeighborsClassifier(weights='distance',n_neighbors=2,p=2)
best_knn_clf.fit(X_train,y_train)
best_knn_clf.score(X_test,y_test)
# > 0.980528511821975
2.4 偏差与方差

这里写图片描述
模型误差=偏差+方差+不可避免误差

导致偏差的主要原因:(欠拟合)

  • 对问题本身的假设不正确,如:非线性数据使用线性回归。

与方差相关:(过拟合)

  • 数据的一点点扰动都会较大地影响模型。
  • 通常原因,使用的模型太过复杂,如:高阶多项式回归。

注意:

  1. 有一些算法天生是高方差的算法,如kNN。非参数学习通常都是高方差算法,因为不对数据进行任何假设。
  2. 有一些算法天生是高偏差算法,如线性回归。参数学习通常都是高偏差算法,因为对数据具有极强的假设。
  3. 偏差和方差通常是矛盾的。降低偏差,会提高方差;降低方差,会提高偏差。
  4. 机器学习的主要挑战来自于方差。

解决高方差(过拟合)的通常手段:

  • 降低模型复杂度
  • 减少数据维度;降噪
  • 增加样本数
  • 使用验证集
  • 模型正则化
2.5 模型正则化(Regularization):限制参数的大小

目标:使 ∑i−1m(y(i)−θ0−θ1x1(i)−θ2x2(i)−⋯−θnxn(i))2\sum _{ i-1 }^{ m }{ { ({ y }^{ (i) }-{ \theta }_{ 0 }-{ \theta }_{ 1 }{ x }_{ 1 }^{ (i) }-{ \theta }_{ 2 }{ x }_{ 2 }^{ (i) }-\cdots -{ \theta }_{ n }{ x }_{ n }^{ (i) }) }^{ 2 } }i1m(y(i)θ0θ1x1(i)θ2x2(i)θnxn(i))2 尽肯能小

即,使 J(θ)=MSE(y,y^;θ)J(\theta )=MSE(y,\hat { y } ;\theta )J(θ)=MSE(y,y^;θ) 尽肯能小

1 岭回归(Ridge Regression)

目标:使 J(θ)=MSE(y,y^;θ)+α12∑i=1nθi2J(\theta )=MSE(y,\hat { y } ;\theta )+\alpha \frac { 1 }{ 2 } \sum _{ i=1 }^{ n }{ { \theta }_{ i }^{ 2 } }J(θ)=MSE(y,y^;θ)+α21i=1nθi2 尽肯能小

代码演示:

import numpy as np
import matplotlib.pyplot as plt

# 准备数据
np.random.seed(42)
x=np.random.uniform(-3.0,3.0,size=100)
X=x.reshape(-1,1)
y=0.5*x+3+np.random.normal(0,1,size=100)

plt.scatter(x,y)
plt.show()

pic

from sklearn.preprocessing import PolynomialFeatures
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error

def PolynomialRegression(degree):
    return Pipeline([
        ("poly",PolynomialFeatures(degree=degree)),
        ("std_scaler",StandardScaler()),
        ("lin_reg",LinearRegression())
    ])
# 分割数据集
X_train,X_test,y_train,y_test=train_test_split(X,y,random_state=666)
# 拟合
poly_reg=PolynomialRegression(degree=20)
poly_reg.fit(X_train,y_train)
# 预测
y_predict=poly_reg.predict(X_test)
# 使用多项式回归的均方误差
mean_squared_error(y_test,y_predict) # result:167.94010862165914

# 封装绘图函数
def plot_model(model):
    x_plot=np.linspace(-3,3,100).reshape(100,1)
    y_plot=model.predict(x_plot)

    plt.scatter(x,y)
    plt.plot(x_plot[:,0],y_plot,color='r')
    plt.axis([-3,3,0,6])
    plt.show()

plot_model(poly_reg)
'''
从图中可以看出模型过拟合了
'''

pic

# 岭回归
from sklearn.linear_model import Ridge

def RidgeRegression(degree,alpha):
    return Pipeline([
        ("poly",PolynomialFeatures(degree=degree)),
        ("std_scaler",StandardScaler()),
        ("ridge_reg",Ridge(alpha=alpha))
    ])

ridge1_reg=RidgeRegression(20,0.0001)
ridge1_reg.fit(X_train,y_train)

y1_predict=ridge1_reg.predict(X_test)
mean_squared_error(y_test,y1_predict) # result:1.323349275417008

plot_model(ridge1_reg)
'''
曲线比之前的要平滑一些
均方差也比之前小很多
'''

pic

# 更改alpha值,再次拟合
ridge2_reg=RidgeRegression(20,1)
ridge2_reg.fit(X_train,y_train)

y2_predict=ridge2_reg.predict(X_test)
mean_squared_error(y_test,y2_predict) # result:1.1888759304218446

plot_model(ridge2_reg)
'''
曲线更加平滑
均方差更小
'''

pic

2 LASSO Regression

  • LASSO趋向于使得一部分theta值变为0,所以可作为特征选择用。

目标:使 J(θ)=MSE(y,y^;θ)+α∑i=1n∣θi∣J(\theta )=MSE(y,\hat { y } ;\theta )+\alpha \sum _{ i=1 }^{ n }{ \left| { \theta }_{ i }^{ } \right| }J(θ)=MSE(y,y^;θ)+αi=1nθi 尽肯能小

代码示例:

from sklearn.linear_model import Lasso

def LassoRegression(degree,alpha):
    return Pipeline([
        ("poly",PolynomialFeatures(degree=degree)),
        ("std_scaler",StandardScaler()),
        ("lasso_reg",Lasso(alpha=alpha))
    ])

lasso1_reg=LassoRegression(20,0.01)
lasso1_reg.fit(X_train,y_train)

y1_predict=lasso1_reg.predict(X_test)
mean_squared_error(y_test,y1_predict) # result:1.1496080843259966

plot_model(lasso1_reg)

pic

lasso2_reg=LassoRegression(20,0.1)
lasso2_reg.fit(X_train,y_train)

y2_predict=lasso2_reg.predict(X_test)
mean_squared_error(y_test,y2_predict) # result:1.1213911351818648

plot_model(lasso2_reg)

pic

# 更改alpha值再拟合
lasso3_reg=LassoRegression(20,1)
lasso3_reg.fit(X_train,y_train)

y3_predict=lasso3_reg.predict(X_test)
mean_squared_error(y_test,y3_predict) # result:1.8408939659515595

plot_model(lasso3_reg) # 正则化过度

pic

3 弹性网(Elastic Net)

J(θ)=MSE(y,y^;θ)+rα∑i=1n∣θi∣+1−r2α∑i=1nθi2J(\theta )=MSE(y,\hat { y } ;\theta )+r\alpha \sum _{ i=1 }^{ n }{ \left| { \theta }_{ i }^{ } \right| } +\frac { 1-r }{ 2 } \alpha \sum _{ i=1 }^{ n }{ { { \theta }_{ i }^{ 2 } } }J(θ)=MSE(y,y^;θ)+rαi=1nθi+21rαi=1nθi2

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值