线性回归【机器学习笔记简摘】

定义与公式

线性回归(Linear regression)是利用回归方程(函数)一个或多个自变量(特征值)和因变量(目标值)之间关系进行建模的一种分析方式。

通用公式: h ( w ) = w 1 x 1 + w 2 x 2 + w 3 x 3 . . . + b = w T x + b h(w)=w_1x_1+w_2x_2+w_3x_3...+b=w^Tx+b h(w)=w1​x1​+w2​x2​+w3​x3​...+b=wTx+b

线性回归当中主要有两种模型,一种是线性关系,另一种是非线性关系。

线性回归的损失和优化

1.损失
    最小二乘法
2.优化
    正规方程
    梯度下降法
3.正规方程 -- 一蹴而就
    利用矩阵的逆,转置进行一步求解
    只是适合样本和特征比较少的情况
4.梯度下降法 -- 循序渐进
    举例:
        山  -- 可微分的函数
        山底 -- 函数的最小值
    梯度的概念
        单变量 -- 切线
        多变量 -- 向量
    梯度下降法中关注的两个参数
        α  -- 就是步长
            步长太小 -- 下山太慢
            步长太大 -- 容易跳过极小值点
        为什么梯度要加一个负号
            梯度方向是上升最快方向,负号就是下降最快方向
5.梯度下降法和正规方程对比:
    梯度下降               正规方程
    需要选择学习率          不需要
    需要迭代求解            一次运算得出
    特征数量较大可以使用     需要计算方程,时间复杂度高O(n3)
6.选择:
    小规模数据:
        LinearRegression(不能解决拟合问题)
        岭回归
    大规模数据:
        SGDRegressor

1 损失函数

J ( θ ) = ( h w ( x 1 ) − y 1 ) 2 + ( h w ( x 2 ) − y 2 ) 2 + . . . + ( h w ( x n ) − y n ) 2 J(\theta)=(h_w(x_1)-y_1)^2+(h_w(x_2)-y_2)^2+...+(h_w(x_n)-y_n)^2 J(θ)=(hw​(x1​)−y1​)2+(hw​(x2​)−y2​)2+...+(hw​(xn​)−yn​)2

​ = ∑ i = 1 n ( h w ( x i ) − y i ) 2 =\sum_{i=1}^{n}(h_w(x_i)-y_i)^2 =∑i=1n​(hw​(xi​)−yi​)2

  • yi为第i个训练样本的真实值
  • h(xi)为第i个训练样本特征值组合预测函数
  • 又称最小二乘法

2 优化算法

如何去求模型当中的W,使得损失最小?(目的是找到最小损失对应的W值)

线性回归经常使用的两种优化算法

2.1 正规方程

w = ( X T X ) − 1 X T y w=(X^TX)^{-1}X^Ty w=(XTX)−1XTy

理解:X为特征值矩阵,y为目标值矩阵。直接求到最好的结果

缺点:当特征过多过复杂时,求解速度太慢并且得不到结果

正规方程式的推导

J ( θ ) = ( h w ( x 1 ) − y 1 ) 2 + ( h w ( x 2 ) − y 2 ) 2 + . . . + ( h w ( x n ) − y n ) 2 J(\theta)=(h_w(x_1)-y_1)^2+(h_w(x_2)-y_2)^2+...+(h_w(x_n)-y_n)^2 J(θ)=(hw​(x1​)−y1​)2+(hw​(x2​)−y2​)2+...+(hw​(xn​)−yn​)2

​ = ∑ i = 1 n ( h w ( x i ) − y i ) 2 =\sum_{i=1}^{n}(h_w(x_i)-y_i)^2 =∑i=1n​(hw​(xi​)−yi​)2

​ = ( X w − y ) 2 =(Xw-y)^2 =(Xw−y)2

其中y是真实值矩阵,X是特征值矩阵,w是权重矩阵

对其求解关于w的最小值,起止y,X 均已知二次函数直接求导,导数为零的位置,即为最小值。

求导:
线性回归【机器学习笔记简摘】
注:式(1)到式(2)推导过程中, X是一个m行n列的矩阵,并不能保证其有逆矩阵,但是右乘XT把其变成一个方阵,保证其有逆矩阵。

式(5)到式(6)推导过程中,和上类似。

2.2 梯度下降(Gradient Descent)

1 全梯度下降算法(FG)
    在进行计算的时候,计算所有样本的误差平均值,作为我的目标函数
2 随机梯度下降算法(SG)
    每次只选择一个样本进行考核
3 小批量梯度下降算法(mini-bantch)
    选择一部分样本进行考核
4 随机平均梯度下降算法(SAG)
    会给每个样本都维持一个平均值,后期计算的时候,参考这个平均值
  • 1. 单变量函数的梯度下降

假设有一个单变量的函数 :J(θ) = θ2

函数的微分:▽J(θ) = 2θ

初始化,起点为: θ0 = 1

学习率:α = 0.4

进行梯度下降的迭代计算过程:
线性回归【机器学习笔记简摘】
经过四次的运算,也就是走了四步,基本就抵达了函数的最低点,也就是山底

  • 2.多变量函数的梯度下降

假设有一个目标函数 : J ( θ ) = θ 1 2 + θ 2 2 J(θ) = θ_1^2 + θ_2^2 J(θ)=θ12​+θ22​

现在要通过梯度下降法计算这个函数的最小值。我们通过观察就能发现最小值其实就是 (0,0)点。但是接下 来,我们会从梯度下降算法开始一步步计算到这个最小值! 我们假设初始的起点为: θ 0 θ^0 θ0 = (1, 3)

初始的学习率为:α = 0.1

函数的梯度为:▽J(θ) =< 2θ1 ,2θ2>

进行多次迭代:
线性回归【机器学习笔记简摘】
梯度下降公式
线性回归【机器学习笔记简摘】
α在梯度下降算法中被称作为学习率或者步长

梯度前加一个负号,就意味着朝着梯度相反的方向前进

  • 梯度下降和正规方程的对比
梯度下降 正规方程
需要选择学习率 不需要
需要迭代求解 一次运算得出
特征数量较大可以使用 需要计算方程,时间复杂度高O(n3)

选择:

  • 小规模数据:

    • LinearRegression(不能解决拟合问题)
    • 岭回归
  • 大规模数据:SGDRegressor

  • 全梯度下降算法(Full gradient descent),

  • 随机梯度下降算法(Stochastic gradient descent),

  • 随机平均梯度下降算法(Stochastic average gradient descent)

  • 小批量梯度下降算法(Mini-batch gradient descent)

全梯度下降算法(FG)
计算训练集所有样本误差,对其求和再取平均值作为目标函数。

权重向量沿其梯度相反的方向移动,从而使当前目标函数减少得最多。

因为在执行每次更新时,我们需要在整个数据集上计算所有的梯度,所以批梯度下降法的速度会很慢,同时,批梯度下降法无法处理超出内存容量限制的数据集。

批梯度下降法同样也不能在线更新模型,即在运行的过程中,不能增加新的样本。

其是在整个训练数据集上计算损失函数关于参数θ的梯度:
线性回归【机器学习笔记简摘】
随机梯度下降算法(SG)

由于FG每迭代更新一次权重都需要计算所有样本误差,而实际问题中经常有上亿的训练样本,故效率偏低,且容易陷入局部最优解,因此提出了随机梯度下降算法。

其每轮计算的目标函数不再是全体样本误差,而仅是单个样本误差,即每次只代入计算一个样本目标函数的梯度来更新权重,再取下一个样本重复此过程,直到损失函数值停止下降或损失函数值小于某个可以容忍的阈值。

此过程简单,高效,通常可以较好地避免更新迭代收敛到局部最优解。其迭代形式为
线性回归【机器学习笔记简摘】
每次只使用一个样本迭代,若遇上噪声则容易陷入局部最优解。

其中,x(i)表示一条训练样本的特征值,y(i)表示一条训练样本的标签值

但是由于,SG每次只使用一个样本迭代,若遇上噪声则容易陷入局部最优解。

小批量梯度下降算法(mini-bantch)

小批量梯度下降算法是FG和SG的折中方案,在一定程度上兼顾了以上两种方法的优点。

每次从训练样本集上随机抽取一个小样本集,在抽出来的小样本集上采用FG迭代更新权重。

被抽出的小样本集所含样本点的个数称为batch_size,通常设置为2的幂次方,更有利于GPU加速处理。

特别的,若batch_size=1,则变成了SG;若batch_size=n,则变成了FG.其迭代形式为
线性回归【机器学习笔记简摘】
随机平均梯度下降算法(SAG)

在SG方法中,虽然避开了运算成本大的问题,但对于大数据训练而言,SG效果常不尽如人意,因为每一轮梯度更新都完全与上一轮的数据和梯度无关。

随机平均梯度算法克服了这个问题,在内存中为每一个样本都维护一个旧的梯度,随机选择第i个样本来更新此样本的梯度,其他样本的梯度保持不变,然后求得所有梯度的平均值,进而更新了参数。

如此,每一轮更新仅需计算一个样本的梯度,计算成本等同于SG,但收敛速度快得多。

四种梯度比较

(1**)FG方法由于它每轮更新都要使用全体数据集,故花费的时间成本最多,内存存储最大。**

(2)SAG在训练初期表现不佳,优化速度较慢。这是因为我们常将初始梯度设为0,而SAG每轮梯度更新都结合了上一轮梯度值。

(3)综合考虑迭代次数和运行时间,SG表现性能都很好,能在训练初期快速摆脱初始梯度值,快速将平均损失函数降到很低。但要注意,在使用SG方法时要慎重选择步长,否则容易错过最优解。

(4)mini-batch结合了SG的“胆大”和FG的“心细”,它的表现也正好居于SG和FG二者之间。在目前的机器学习领域,mini-batch是使用最多的梯度下降算法,正是因为它避开了FG运算效率低成本大和SG收敛效果不稳定的缺点。

模拟梯度下降法

import numpy as np
import matplotlib.pyplot as plt

plot_x = np.linspace(-1,6,141)

plot_y = (plot_x-2.5)**2-1

plt.plot(plot_x,plot_y)
plt.show()

线性回归【机器学习笔记简摘】

#求导函数
def dJ(theta):
    return 2*(theta-2.5)


def J(theta):
    return (theta-2.5)**2-1

eta=0.1  #学习率
epsilon=1e-8

theta=0
theta_history=[theta]
while True:
    gradient=dJ(theta)
    last_theta=theta
    theta=theta-eta*gradient
    theta_history.append(theta)
    
    if abs(J(theta)-J(last_theta))<epsilon :
        break
print('最小值点的x坐标:',theta)
print('最小值点的y坐标:',J(theta))
plt.plot(plot_x,J(plot_x))
plt.plot(np.array(theta_history),J(np.array(theta_history)),color='r',marker='+') 

线性回归【机器学习笔记简摘】

LinearRegression 和 SGDRegressor

class sklearn.linear_model.LinearRegression(*, fit_intercept=True,normalize='deprecated', copy_X=True, n_jobs=None, positive=False)
  • 通过正规方程优化
  • fit_intercept:默认True,是否计算模型的截距,为False时,则数据中心化处理
    normalize:默认False,是否中心化,若fit_intercept参数设置False时,normalize参数无需设置;若normalize设置为True时,则输入的样本数据将(X-X均值)/||X||;若设置normalize=False时,在训练模型前, 可以使用sklearn.preprocessing.StandardScaler进行标准化处理。
    copy_X:默认True,否则X会被改写
    n_jobs:默认为1,表示使用CPU的个数。当-1时,代表使用全部CPU
  • coef_:回归系数
  • intercept_:偏置
  • predict(x):预测数据
  • score(X, y, sample_weight=None),其结果等于 1 − ( ( y _ t r u e − y _ p r e d ) ∗ 2 ) . s u m ( ) / ( y _ t r u e − y _ t r u e . m e a n ( ) ) ∗ 2 ) . s u m ( ) ) 1-((y \_true - y \_pred) *2).sum() / (y \_true - y \_true.mean()) * 2).sum()) 1−((y_true−y_pred)∗2).sum()/(y_true−y_true.mean())∗2).sum())
class sklearn.linear_model.SGDRegressor(loss='squared_error', *, penalty='l2',alpha=0.0001, l1_ratio=0.15, fit_intercept=True, max_iter=1000, tol=0.001, shuffle=True, verbose=0, epsilon=0.1, random_state=None, learning_rate='invscaling', eta0=0.01, power_t=0.25, early_stopping=False, validation_fraction=0.1, n_iter_no_change=5, warm_start=False, average=False)
  • SGDRegressor类实现了随机梯度下降学习,它支持不同的loss函数和正则化惩罚项来拟合线性回归模型。
  • loss:损失类型
    • loss=”squared_error”: 普通最小二乘法
  • fit_intercept:是否计算偏置
  • learning_rate : string, optional
    • 学习率填充
    • ’constant’: eta = eta0
    • ’optimal’: eta = 1.0 / (alpha * (t + t0)) [default]
    • ‘invscaling’: eta = eta0 / pow(t, power_t)
      • power_t=0.25:存在父类当中
    • 对于一个常数值的学习率来说,可以使用learning_rate=’constant’ ,并使用eta0来指定学习率。
    • eta0 : double,当learning_rate为’constant’或者’invscaling’时的初始学习率。默认值为0.0。如果learning_rate='optimal’则这个参数没有用。
  • SGDRegressor.coef_:回归系数
  • SGDRegressor.intercept_:偏置

SGD的优点是:
高效
容易实现(有许多机会进行代码调优)
SGD的缺点是:
SGD需要许多超参数:比如正则项参数、迭代数。
SGD对于特征归一化(feature scaling)是敏感的。

from sklearn.linear_model import LinearRegression

x = [[80, 86],
[82, 80],
[85, 78],
[90, 90],
[86, 82],
[82, 90],
[78, 80],
[92, 94]]
y = [84.2, 80.6, 80.1, 90, 83.2, 87.6, 79.4, 93.4]

# 实例化API
estimator = LinearRegression()
# 使用fit方法进行训练
estimator.fit(x,y)

print(estimator.coef_)
print(estimator.predict([[100, 80]]))

线性回归模型评估

线性回归模型评估
通过几个参数验证回归模型
SSE(和方差、误差平方和):The sum of squares due to error
MSE(均方差、方差):Mean squared error 1 m ∑ i = 1 m ( y i − y ^ i ) 2 \frac{1}{m} \displaystyle \sum_{i=1}^{m}(y_i-\hat y_i)^2 m1​i=1∑m​(yi​−y^​i​)2
RMSE(均方根、标准差):Root mean squared error 1 m ∑ i = 1 m ( y i − y ^ i ) 2 = M S E \sqrt{\frac{1}{m} \displaystyle \sum_{i=1}^{m}(y_i-\hat y_i)^2}=\sqrt{MSE} m1​i=1∑m​(yi​−y^​i​)2 ​=MSE

MAE(平均绝对误差) 1 m ∑ i = 1 m ∣ y i − y ^ i ∣ \frac{1}{m} \displaystyle \sum_{i=1}^{m} \lvert y_i-\hat y_i \rvert m1​i=1∑m​∣yi​−y^​i​∣

R-square(确定系数) Coefficient of determination
线性回归【机器学习笔记简摘】
公式变形

线性回归【机器学习笔记简摘】

其实“确定系数”是通过数据的变化来表征一个拟合的好坏。“确定系数”的正常取值范围为[0,1],越接近1,表明方程的变量对y的解释能力越强,这个模型对数据拟合的也较好

R 2 R^2 R2 越大越好,当自己的预测模型不犯任何错误时: R 2 = 1 R^2= 1 R2=1 ,如果 R 2 R^2 R2 < 0,说明学习到的模型还不如基准模型。
#注:很可能数据不存在任何线性关系

from sklearn.metrics import mean_squared_error #均方误差
from sklearn.metrics import mean_absolute_error #平方绝对误差
from sklearn.metrics import r2_score#R square
#调用
mean_squared_error(y_test,y_predict)
mean_absolute_error(y_test,y_predict)
r2_score(y_test,y_predict)
# 简单线性回归(一元线性回归)
# (1)数据示例
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
plt.rcParams['font.sans-serif'] = ['KaiTi']
plt.rcParams['axes.unicode_minus']=False

rng = np.random.RandomState(1)  
xtrain = 10 * rng.rand(30)
ytrain = 8 + 4 * xtrain + rng.rand(30)
# np.random.RandomState → 随机数种子,对于一个随机数发生器,只要该种子(seed)相同,产生的随机数序列就是相同的
# 生成随机数据x与y
# 样本关系:y = 8 + 4*x


fig = plt.figure(figsize =(12,3))
ax1 = fig.add_subplot(1,2,1)
plt.scatter(xtrain,ytrain,marker = '.',color = 'k')
plt.grid()
plt.title('样本数据散点图')
# 生成散点图

model = LinearRegression()
model.fit(xtrain[:,np.newaxis],ytrain)
# x[:,np.newaxis] → 将数组变成(n,1)形状

print('权重为:',model.coef_)
print('偏置为:',model.intercept_)

xtest = np.linspace(0,10,1000)
ytest = model.predict(xtest[:,np.newaxis])
# 创建测试数据xtest,并根据拟合曲线求出ytest
# model.predict → 预测

ax2 = fig.add_subplot(1,2,2)
plt.scatter(xtrain,ytrain,marker = '.',color = 'k')
plt.plot(xtest,ytest,color = 'r')
plt.grid()
plt.title('线性回归拟合')
# 绘制散点图、线性回归拟合直线

# 权重为: [4.00448414]
# 偏置为: 8.447659499431026

线性回归【机器学习笔记简摘】

# 简单线性回归(一元线性回归)
# (2)误差

rng = np.random.RandomState(8)
xtrain = 10 * rng.rand(15)
ytrain = 8 + 4 * xtrain + rng.rand(15) * 30
model.fit(xtrain[:,np.newaxis],ytrain)
xtest = np.linspace(0,10,1000)
ytest = model.predict(xtest[:,np.newaxis])
# 创建样本数据并进行拟合

plt.plot(xtest,ytest,color = 'r',linestyle = '--')  # 拟合直线
plt.scatter(xtrain,ytrain,marker = '.',color = 'k')  # 样本数据散点图
ytest2 = model.predict(xtrain[:,np.newaxis])  # 样本数据x在拟合直线上的y值
plt.scatter(xtrain,ytest2,marker = 'x',color = 'g')   # ytest2散点图
plt.plot([xtrain,xtrain],[ytrain,ytest2],color = 'gray')  # 误差线
plt.grid()
plt.title('误差')
# 绘制图表

线性回归【机器学习笔记简摘】

多项式回归

numpy.polyfit 实现一元多项式

numpy.polyfit(x, y, deg, rcond=None, full=False, w=None, cov=False)

【polyfit】多项式曲线拟合
【polyval】多项式曲线求值
【poly1d】得到多项式系数,按照阶数从高到低排列

import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression

x = np.random.uniform(-3, 3, size=100) #从一个均匀分布[low,high)中随机采样,注意定义域是左闭右开
X = x.reshape(-1, 1) # n行1列
y = 0.5 + x**2 + x + 2 + np.random.normal(0, 1, size=100)  #一元二次方程并添加噪音

coef2 = np.polyfit(x,y, 2) # n表示阶数
poly_fit2 = np.poly1d(coef2)

plt.scatter(x, y)
plt.plot(np.sort(x),poly_fit2(x)[np.argsort(x)], color='r',label="二阶拟合")

print(poly_fit2)

线性回归【机器学习笔记简摘】

import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['KaiTi']
plt.rcParams['axes.unicode_minus']=False
 
x = np.array([-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10])
y = np.array(2*(x**4) + x**2 + 9*x + 2) #假设因变量y刚好符合该公式
#y = np.array([300,500,0,-10,0,20,200,300,1000,800,4000,5000,10000,9000,22000])
 
# coef 为系数,poly_fit 拟合函数
coef1 = np.polyfit(x,y, 1)
poly_fit1 = np.poly1d(coef1)
plt.plot(x, poly_fit1(x), 'g',label="一阶拟合")
print(poly_fit1)
 
coef2 = np.polyfit(x,y, 2)
poly_fit2 = np.poly1d(coef2)
plt.plot(x, poly_fit2(x), 'b',label="二阶拟合")
print(poly_fit2)
 
coef3 = np.polyfit(x,y, 3)
poly_fit3 = np.poly1d(coef3)
plt.plot(x, poly_fit3(x), 'y',label="三阶拟合")
print(poly_fit3)
 
coef4 = np.polyfit(x,y, 4)
poly_fit4 = np.poly1d(coef4)
plt.plot(x, poly_fit4(x), 'k',label="四阶拟合")
print(poly_fit4)
 
coef5 = np.polyfit(x,y, 5)
poly_fit5 = np.poly1d(coef5)
plt.plot(x, poly_fit5(x), 'r:',label="五阶拟合")
print(poly_fit5)
 
plt.scatter(x, y, color='black')
plt.legend(loc=2)
plt.show()

线性回归【机器学习笔记简摘】

sklearn中的多项式回归

多项式回归可以看作是对数据进行预处理,给数据添加新的特征,所以调用的库在preprocessing中:

class sklearn.preprocessing.PolynomialFeatures(degree=2, *, interaction_only=False, include_bias=True, order='C')

使用 sklearn.preprocessing.PolynomialFeatures 这个类可以进行特征的构造,构造的方式就是特征与特征相乘(自己与自己,自己与其他人),这种方式叫做使用多项式的方式。
例如:有 a、b 两个特征,那么它的 2 次多项式的次数为 [ 1 , a , b , a 2 , a b , b 2 ] [1,a,b,a^2,ab,b^2] [1,a,b,a2,ab,b2] 。
PolynomialFeatures 这个类有 3 个参数:
degree:控制多项式的次数;
interaction_only:默认为 False,如果指定为 True,那么就不会有特征自己和自己结合的项,组合的特征中没有 a 2 a^2 a2 和 b 2 b^2 b2;
include_bias:默认为 True 。如果为 True 的话,那么结果中就会有 0 次幂项,即全为 1 这一列。

sklearn中一元多项式回归

import numpy as np
from sklearn.preprocessing import PolynomialFeatures

np.random.seed(1)
x = np.random.uniform(-3, 3, size=100) #从一个均匀分布[low,high)中随机采样,注意定义域是左闭右开
X = x.reshape(-1, 1) # n行1列
y = 0.5 + x**2 + x + 2 + np.random.normal(0, 1, size=100)
# 这个degree表示我们使用多少次幂的多项式
poly = PolynomialFeatures(degree=2)    
poly.fit(X)
X2 = poly.transform(X)
# X2.shape
# 输出:(100, 3)

X2[:5,:]
#array([[ 1.        , -0.49786797,  0.24787252],
#       [ 1.        ,  1.32194696,  1.74754377],
#       [ 1.        , -2.99931375,  8.99588298],
#       [ 1.        , -1.18600456,  1.40660683],
#       [ 1.        , -2.11946466,  4.49213042]])

X2的结果第一列常数项,可以看作是加入了一列x的0次方;第二列一次项系数(原来的样本X特征),第三列二次项系数(X平方前的特征)。

特征准备好之后进行训练:

reg = LinearRegression()
reg.fit(X2, y)
y_predict = reg.predict(X2)
plt.scatter(x, y)
plt.plot(np.sort(x), y_predict[np.argsort(x)], color='r')
plt.show()

print(reg.coef_)
print(reg.intercept_)

线性回归【机器学习笔记简摘】

sklearn中多元多项式回归

import numpy as np
from sklearn.preprocessing import PolynomialFeatures

X = np.arange(1, 11).reshape(5, 2)    # 5行2列 10个元素的矩阵
poly = PolynomialFeatures()
poly.fit(X)
# 将X转换成最多包含X二次幂的数据集
X2 = poly.transform(X)
# 5行6列

print(X2.shape)
print(X2)

>>>
(5, 6)
[[  1.   1.   2.   1.   2.   4.]
 [  1.   3.   4.   9.  12.  16.]
 [  1.   5.   6.  25.  30.  36.]
 [  1.   7.   8.  49.  56.  64.]
 [  1.   9.  10.  81.  90. 100.]]

可以看出当数据维度是2维的,经过多项式预处理生成了6维数据。

第一列很显然是0次项系数;第二列和第三列就是原本的X矩阵;第四列是第二列(原X的第一列)平方的结果;第五列是第二、三两列相乘的结果;第六列是第三列(原X的第二列)平方的结果。

由此可以猜想一下如果数据是3维的时候是什么情况:

poly = PolynomialFeatures(degree=3)
poly.fit(X)
x3 = poly.transform(X)
x3.shape  #(5, 10)
x3 

#输出
array([[   1.,    1.,    2.,    1.,    2.,    4.,    1.,    2.,    4.,    8.],
       [   1.,    3.,    4.,    9.,   12.,   16.,   27.,   36.,   48.,    64.],
       [   1.,    5.,    6.,   25.,   30.,   36.,  125.,  150.,  180.,    216.],
       [   1.,    7.,    8.,   49.,   56.,   64.,  343.,  392.,  448.,    512.],
       [   1.,    9.,   10.,   81.,   90.,  100.,  729.,  810.,  900.,    1000.]])

PolynomiaFeatures,将所有的可能组合,升维的方式呈指数型增长。这也会带来一定的问题。

Pipeline

在具体编程实践时,可以使用sklearn中的pipeline对操作进行整合。

首先我们回顾多项式回归的过程:

  • 将原始数据通过PolynomialFeatures生成相应的多项式特征
  • 多项式数据可能还要进行特征归一化处理
  • 将数据送给线性回归

Pipeline就是将这些步骤都放在一起。参数传入一个列表,列表中的每个元素是管道中的一个步骤。每个元素是一个元组,元组的第一个元素是名字(字符串),第二个元素是实例化。

import numpy as np
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import StandardScaler

np.random.seed(1)
x = np.random.uniform(-3, 3, size=100) 
X = x.reshape(-1, 1) # n行1列
y = 0.5 + x**2 + x + 2 + np.random.normal(0, 1, size=100)

poly_reg = Pipeline([
    ('poly', PolynomialFeatures(degree=2)),
    ('std_scale', StandardScaler()),
    ('lin_reg', LinearRegression())
])  
poly_reg.fit(X, y)
y_predict = poly_reg.predict(X)

plt.scatter(x, y)
plt.plot(np.sort(x), y_predict[np.argsort(x)], color='r')
plt.show()

线性回归【机器学习笔记简摘】
其实多项式回归在算法并没有什么新的地方,完全是使用线性回归的思路,关键在于为数据添加新的特征,而这些新的特征是原有的特征的多项式组合采用这样的方式就能解决非线性问题。

这样的思路跟PCA这种降维思想刚好相反,多项式回归则是升维,添加了新的特征之后,使得更好地拟合高维数据。
欠拟合

偏差与方差

偏差和方差的定义如下:

  • 偏差(bias):偏差衡量了模型的预测值与实际值之间的偏离关系。例如某模型的准确度为96%,则说明是低偏差;反之,如果准确度只有70%,则说明是高偏差。
  • 方差(variance):方差描述的是训练数据在不同迭代阶段的训练模型中,预测值的变化波动情况(或称之为离散情况)。从数学角度看,可以理解为每个预测值与预测均值差的平方和的再求平均数。通常在模型训练中,初始阶段模型复杂度不高,为低方差;随着训练量加大,模型逐步拟合训练数据,复杂度开始变高,此时方差会逐渐变高。

对应四种情况:

  • 低偏差,低方差:这是训练的理想模型,此时蓝色点集基本落在靶心范围内,且数据离散程度小,基本在靶心范围内;
  • 低偏差,高方差:这是深度学习面临的最大问题,过拟合了。也就是模型太贴合训练数据了,导致其泛化(或通用)能力差,若遇到测试集,则准确度下降的厉害;
  • 高偏差,低方差:这往往是训练的初始阶段;
  • 高偏差,高方差:这是训练最糟糕的情况,准确度差,数据的离散程度也差。

模型误差:

模型误差 = 偏差 + 方差 + 不可避免的误差(噪音)。一般来说,随着模型复杂度的增加,方差会逐渐增大,偏差会逐渐减小。

偏差方差产生的原因:

一个模型有偏差,主要的原因可能是对问题本身的假设是不正确的,或者欠拟合。如:针对非线性的问题使用线性回归;或者采用的特征和问题完全没有关系,如用学生姓名预测考试成绩,就会导致高偏差。

方差表现为数据的一点点扰动就会较大地影响模型。即模型没有完全学习到问题的本质,而学习到很多噪音。通常原因可能是使用的模型太复杂,如:使用高阶多项式回归,也就是过拟合。

有一些算法天生就是高方差的算法,如kNN算法。非参数学习算法通常都是高方差,因为不对数据进行任何假设。

有一些算法天生就是高偏差算法,如线性回归。参数学习算法通常都是高偏差算法,因为对数据有迹象。

偏差与方差的平衡:

偏差和方差通常是矛盾的。降低偏差,会提高方差;降低方差,会提高偏差。

这就需要在偏差和方差之间保持一个平衡。

以多项式回归模型为例,我们可以选择不同的多项式的次数,来观察多项式次数对模型偏差&方差的影响:
线性回归【机器学习笔记简摘】

我们要知道偏差和方差是无法完全避免的,只能尽量减少其影响。

  1. 在避免偏差时,需尽量选择正确的模型,一个非线性问题而我们一直用线性模型去解决,那无论如何,高偏差是无法避免的。
  2. 有了正确的模型,我们还要慎重选择数据集的大小,通常数据集越大越好,但大到数据集已经对整体所有数据有了一定的代表性后,再多的数据已经不能提升模型了,反而会带来计算量的增加。而训练数据太小一定是不好的,这会带来过拟合,模型复杂度太高,方差很大,不同数据集训练出来的模型变化非常大。
  3. 最后,要选择合适的模型复杂度,复杂度高的模型通常对训练数据有很好的拟合能力。

其实在机器学习领域,主要的挑战来自方差。处理高方差的手段有:

  • 降低模型复杂度
  • 减少数据维度;降噪
  • 增加样本数
  • 使用验证集

欠拟合和过拟合

欠拟合
    在训练集上表现不好,在测试集上表现不好
    解决方法:
        继续学习
        1.添加其他特征项
        2.添加多项式特征
过拟合
    在训练集上表现好,在测试集上表现不好
    解决方法:
        1.重新清洗数据集
        2.增大数据的训练量
        3.正则化
        4.减少特征维度
正则化
    通过限制高次项的系数进行防止过拟合
        L1正则化
            理解:直接把高次项前面的系数变为0
            Lasso回归
        L2正则化
            理解:把高次项前面的系数变成特别小的值
            岭回归

正则化线性模型

在学习的时候,数据提供的特征有些影响模型复杂度或者这个特征的数据点异常较多,所以算法在学习的时候尽量减少这个特征的影响(甚至删除某个特征的影响),这就是正则化

注:调整时候,算法并不知道某个特征影响,而是去调整参数得出优化的结果

岭回归和LASSO回归都是解决模型训练过程中的过拟合问题
1.Ridge Regression 岭回归
    就是把系数添加平方项
    然后限制系数值的大小
    α值越小,系数值越大,α越大,系数值越小
2.Lasso 回归
    对系数值进行绝对值处理
    由于绝对值在顶点处不可导,所以进行计算的过程中产生很多0,最后得到结果为:稀疏矩阵
3.Elastic Net 弹性网络
    是前两个内容的综合
    设置了一个r,如果r=0--岭回归;r=1--Lasso回归

1 Ridge Regression (岭回归,又名 Tikhonov regularization)

岭回归是线性回归的正则化版本,即在原来的线性回归的 cost function 中添加正则项(regularization term):
线性回归【机器学习笔记简摘】

以达到在拟合数据的同时,使模型权重尽可能小的目的,岭回归代价函数:

线性回归【机器学习笔记简摘】

  • α=0:岭回归退化为线性回归
  1. α :引入的新的超参数,平衡新的损失函数中两部分的关系;是代数式的系数,代表在模型正则化下新的损失函数中,让每一个 θ i θ_i θi​ 都尽可能的小,这个小的程度占整个优化损失函数程度的多少;
  • 如果 α = 0:表示目标函数中没有加入模型正则化;
  • 如果 α = +∞ :目标函数的另一部分 MSE 占整个目标函数的比重非常的小,主要的优化任务就是让每一个 θ i θ_i θi​ 都尽可能的小;

2 Lasso Regression(Lasso 回归)

Lasso 回归是线性回归的另一种正则化版本,正则项为权值向量的ℓ1范数。

Lasso回归的代价函数 :
线性回归【机器学习笔记简摘】
【注意 】

  • Lasso Regression 的代价函数在 θi=0处是不可导的.
  • 解决方法:在θi=0处用一个次梯度向量(subgradient vector)代替梯度,如下式
  • Lasso Regression 的次梯度向量

线性回归【机器学习笔记简摘】
Lasso Regression 有一个很重要的性质是:倾向于完全消除不重要的权重。

例如:当α 取值相对较大时,高阶多项式退化为二次甚至是线性:高阶多项式特征的权重被置为0。

也就是说,Lasso Regression 能够自动进行特征选择,并输出一个稀疏模型(只有少数特征的权重是非零的)。

3 Elastic Net (弹性网络)

弹性网络在岭回归和Lasso回归中进行了折中,通过 混合比(mix ratio) r 进行控制:

  • r=0:弹性网络变为岭回归
  • r=1:弹性网络便为Lasso回归

弹性网络的代价函数 :
线性回归【机器学习笔记简摘】
一般来说,我们应避免使用朴素线性回归,而应对模型进行一定的正则化处理,那如何选择正则化方法呢?

小结:

  • 常用:岭回归

  • 假设只有少部分特征是有用的:

    • 弹性网络
    • Lasso
    • 一般来说,弹性网络的使用更为广泛。因为在特征维度高于训练样本数,或者特征是强相关的情况下,Lasso回归的表现不太稳定。
  • api:

    • from sklearn.linear_model import Ridge, ElasticNet, Lasso
      

岭回归API

class sklearn.linear_model.Ridge(alpha=1.0, *, fit_intercept=True, normalize='deprecated', copy_X=True, max_iter=None, tol=0.001, solver='auto', positive=False, random_state=None)
  • 具有l2正则化的线性回归
  • alpha:正则化力度,也叫 λ,float类型,默认为1.0。正则化改善了问题的条件并减少了估计的方差。
  • solver:求解方法,str类型,默认为auto。可选参数为:auto、svd、cholesky、lsqr、sparse_cg、sag
    • auto根据数据类型自动选择求解器。
    • sag:如果数据集、特征都比较大,选择该随机梯度下降优化。当n_samples和n_feature都很大时,通常比其他求解器更快。注意,sag快速收敛仅在具有近似相同尺度的特征上被保证。您可以使用sklearn.preprocessing的缩放器预处理数据。
    • svd使用X的奇异值分解来计算Ridge系数。对于奇异矩阵比cholesky更稳定
    • cholesky使用标准的scipy.linalg.solve函数来获得闭合形式的解。
    • sparse_cg使用在scipy.sparse.linalg.cg中找到的共轭梯度求解器。作为迭代算法,这个求解器比大规模数据(设置tol和max_iter的可能性)的cholesky更合适。
    • lsqr使用专用的正则化最小二乘常数scipy.sparse.linalg.lsqr。它是最快的,但可能在旧的scipy版本不可用。它是使用迭代过程。
  • normalize:数据是否进行标准化
    • normalize=False:可以在fit之前调用preprocessing.StandardScaler标准化数据
  • Ridge.coef_:回归权重
  • Ridge.intercept_:回归偏置
    Ridge方法相当于SGDRegressor(penalty=‘l2’, loss=“squared_error”),只不过SGDRegressor实现了一个普通的随机梯度下降学习,推荐使用Ridge(实现了SAG)
class sklearn.linear_model.RidgeCV(alphas=(0.1, 1.0, 10.0), *, fit_intercept=True, normalize='deprecated', scoring=None, cv=None, gcv_mode=None, store_cv_values=False, alpha_per_target=False)

参数:

  • alphas

类型: numpy array of shape [n_alphas]
说明:α值的数组。正则化的力度,必须是正浮点数。正则化提升了问题的条件,减少了估计器的方差。较大的值指定了更强的正则化。在其他模型,比如LogisticRegression 或者LinearSVC,α对应 C − 1 C^{−1} C−1。

  • 用于交叉验证生成器的对象。
  • 一种可重复的生成序列。
  • 具有l2正则化的线性回归,可以进行交叉验证
    线性回归【机器学习笔记简摘】
  • 正则化力度越大,权重系数会越小
  • 正则化力度越小,权重系数会越大

模拟数据使用岭回归

import numpy as np
import matplotlib.pyplot as plt

np.random.seed(42)
# np.random.uniform(-3, 3, size=100):在 [-3, 3] 之间等分取 100 个数;
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()

线性回归【机器学习笔记简摘】

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

# 使用多项式回归的管道方法
def PolynomialRegression(degree):
    return Pipeline([
        ('poly', PolynomialFeatures(degree=degree)),
        ('std_scaler', StandardScaler()),
        ('lin_reg', LinearRegression())
    ])



np.random.seed(666)
X_train, X_test, y_train, y_test = train_test_split(X, y)

from sklearn.metrics import mean_squared_error

poly_reg = PolynomialRegression(degree=20)
poly_reg.fit(X_train, y_train)

y_poly_predict = poly_reg.predict(X_test)
print(mean_squared_error(y_test, y_poly_predict))



X_plot = np.linspace(-3, 3, 100).reshape(100, 1)
y_plot = poly_reg.predict(X_plot)

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

线性回归【机器学习笔记简摘】

import numpy as np
import matplotlib.pyplot as plt
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import PolynomialFeatures
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LinearRegression
from sklearn.linear_model import Ridge

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], model.predict(X_plot), color='r')
    plt.axis([-3, 3, 0, 6])
    plt.show()
    

#使用管道的方式使用岭回归方法    
def RidgeRegression(degree, alpha):
    return Pipeline([
        ('poly', PolynomialFeatures(degree=degree)),
        ('std_scaler', StandardScaler()),
        ('ridge_reg', Ridge(alpha=alpha))
    ])
#degree = 20、α = 0.0001

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

y1_predict = ridge1_reg.predict(X_test)
print(mean_squared_error(y_test, y1_predict))
# 输出:1.323349275406402(均方误差)
plot_model(ridge1_reg)

线性回归【机器学习笔记简摘】

# degree = 20、α = 1

ridge2_reg = RidgeRegression(20, 1)
ridge2_reg.fit(X_train, y_train)

y2_predict = ridge2_reg.predict(X_test)
print(mean_squared_error(y_test, y2_predict))
# 输出:1.1888759304218461(均方误差)
plot_model(ridge2_reg)

线性回归【机器学习笔记简摘】

# degree = 20、α = 100
ridge3_reg = RidgeRegression(20, 100)
ridge3_reg.fit(X_train, y_train)

y3_predict = ridge3_reg.predict(X_test)
print(mean_squared_error(y_test, y3_predict))
# 输出:1.3196456113086197(均方误差)

plot_model(ridge3_reg)

线性回归【机器学习笔记简摘】

#degree=20、alpha=1000000(相当于无穷大)

ridge4_reg = RidgeRegression(20, 1000000)
ridge4_reg.fit(X_train, y_train)

y4_predict = ridge4_reg.predict(X_test)
print(mean_squared_error(y_test, y4_predict))
# 输出:1.8404103153255003

plot_model(ridge4_reg)

线性回归【机器学习笔记简摘】
当 α = 1000000(相当于无穷大)时:拟合曲线几乎是一条水平的直线,因为当 α 非常大的时候,对目标函数的影响相当于只有添加的模型正则化在起作用

Lasso回归API

class sklearn.linear_model.Lasso(alpha=1.0, *, fit_intercept=True, normalize='deprecated', precompute=False, copy_X=True, max_iter=1000, tol=0.0001, warm_start=False, positive=False, random_state=None, selection='cyclic')

模拟数据使用 LASSO 回归

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)

from sklearn.model_selection import train_test_split

np.random.seed(666)
X_train, X_test, y_train, y_test = train_test_split(X, y)

#使用多项式回归拟合数据
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import PolynomialFeatures
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LinearRegression

def PolynomialRegression(degree):
    return Pipeline([
        ('poly', PolynomialFeatures(degree=degree)),
        ('std_scaler', StandardScaler()),
        ('lin_reg', LinearRegression())
    ])

#封装绘制代码
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], model.predict(X_plot), color='r')
    plt.axis([-3, 3, 0, 6])
    plt.show()
#多项式回归并绘图
from sklearn.metrics import mean_squared_error

poly_reg = PolynomialRegression(degree=20)
poly_reg.fit(X_train, y_train)

y_poly_predict = poly_reg.predict(X_test)
print(mean_squared_error(y_test, y_poly_predict))
# 输出:167.9401085999025(均方误差)

plot_model(poly_reg)

线性回归【机器学习笔记简摘】

#使用 LASSO Regression 改进算法模型

from sklearn.linear_model import Lasso

# 以管道的方式,使用 LASSO 回归的方法改进多项式回归的算法
def LassoRegression(degree, alpha):
    return Pipeline([
        ('poly', PolynomialFeatures(degree=degree)),
        ('std_scaler', StandardScaler()),
        ('lasso_reg', Lasso(alpha=alpha))
    ])
# degree = 20、α = 0.01

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

y1_predict = lasso1_reg.predict(X_test)
print(mean_squared_error(y_test, y1_predict))
# 输出:1.149608084325997(均方误差)

plot_model(lasso1_reg)

线性回归【机器学习笔记简摘】

#degree = 20、α = 0.1

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

y2_predict = lasso2_reg.predict(X_test)
print(mean_squared_error(y_test, y2_predict))
# 输出:1.1213911351818648(均方误差)

plot_model(lasso2_reg)

线性回归【机器学习笔记简摘】

#degree = 20、α = 1
lasso3_reg = LassoRegression(20, 1)
lasso3_reg.fit(X_train, y_train)

y3_predict = lasso3_reg.predict(X_test)
print(mean_squared_error(y_test, y3_predict))
# 输出:1.8408939659515595(均方误差)

plot_model(lasso3_reg)

分析

  • α = 0.01,比岭回归中的第一个 α 的取值大很多,因为对于 RidgeRegression(),正则化的那一项中是 θ^2,平方后的结果会比较大,所以需要让 α 值很小来调节正则项的大小;而对于LassoRegression(),正则化的那一项中是 |θ|,比岭回归中的正则化项小很多,所以在 LassoRegression() 中 α 的取值可以相对大一些;

  • 在具体进行机器学习算法的过程中,需要不断的试验不断的看结果,慢慢的形成经验,用各种不同的方法在调参时,对于不同的参数大概知道参数在哪个范围内进行选择会相应的比较好;

  • 当 α = 1 时,LassoRegression() 对应的正则化的程度已经比较高了;
    正则化的程度:拟合曲线的上下抖动幅度;

  • 现实机器学习的过程中,就是在完全不进行模型正则化和过度模型正则化之间选择一个程度最好的一情况;

比较 Ridge Regression 和 LASSO Regression

1)使用 Ridge 改进的多项式回归算法,随着 α 的改变,拟合曲线始终是各曲线,直到最后变成一条几乎水平的直线;也就是说,使用 Ridge 改造的多项式回归算法,得到的模型变量前还是有系数,因此很难得到一条斜的直线;

2)而使用 Lasso 改进的多项式回归算法,随着 α 的改变,拟合曲线会很快变成一条斜的直线,最后慢慢变成一条几乎水平的直线;模型更倾向于一条直线。

线性回归【机器学习笔记简摘】

  • LASSO 的特点:趋向于使得一部分 θ 值变为 0,也就是说 LASSO 认为与 θ = 0 对应的特征是完全没有用的,而剩下与 θ 不为 0 所对应的特征是有用的,所以 LASSO 可作为特征选择用

  • 在作为特征选择用时,LASSO 也可能将一些有用的特征的系数 θ 变为 0,会导致信息不准确;相比较来说,还是 Ridge 更准确。

  1. 但是如果样本特征非常的大,如使用多项式回归时 degree = 100,此种情况下使用 LASSO 可以使样本特征变小。
上一篇:ML01 LinearRegression


下一篇:WC2022 Solution Set