从负无穷学习机器学习(二)广义线性模型

一、广义线性模型基本概念

在回归分析中,线性模型的一般表达公式如下所示:
在这里插入图片描述

其中x[0],x[1]…,x[p]为数据集中特征变量的数量,此数据集中有p个特征;w和b为模型的参数;y为预测值。对于只有一个特征值的模型,,公式变为y=w[0]·x[0]+b,看起来就是一条直线的方程,w为特征直线的斜率,b为截距,图形表示如下。

#导入基础科学计算库和图形库
import numpy as np
import matplotlib.pyplot as plt
#创建范围为-5到5,元素数量为100的等差数列(arithmetic progression)
x = np.linspace(-5,5,100)
y = x*2 + 3
plt.plot(x,y,c='orange')
plt.title("Straight Line")
plt.show()
#线性模型正是通过训练数据集确定自身系数(斜率)和截距的

在这里插入图片描述

(一)、线性模型的图形表示

假如我们有两个点(2,4)和(3,5),用线性模型显示(拟合)他们并获取直线方程。

#导入机器学习库中的线性回归模型
from sklearn.linear_model import LinearRegression
#输入两个坐标点的横坐标
X = [[2],[3]]
#输入两个坐标点的纵坐标
y = [4,5]
#用线性模型拟合这两个点
lr = LinearRegression().fit(X,y)
#画出两个点和直线的图形 线的范围为0~5,含有20个元素
z = np.linspace(0,5,20)
#散列化函数,两个点的大小为80pixel
plt.scatter(X,y,s=80)
plt.plot(z,lr.predict(z.reshape(-1,1)),c='k')
plt.title('Straight Line')
plt.show()
#输出这条直线的方程 .coef_[]输出系数(coefficient) .intercept_输出截距(intercept)
print('拟合的直线方程:','y = {:.3f}'.format(lr.coef_[0]),'x','+{:.3f}'.format(lr.intercept_))

在这里插入图片描述

当存在3个点的时候,我们再拟合一下。可以发现三点都不在直线上,而是位于离三点距离之和最小的位置。

#输入3个点的横坐标
X = [[3],[7],[4]]
#输入3个点的纵坐标
y = [6,4,9]
#用线性模型拟合这三个点
lr = LinearRegression().fit(X,y)
#画出这几个点和直线的图形
z = np.linspace(0,10,20)
plt.scatter(X,y,s=80)
plt.plot(z, lr.predict(z.reshape(-1,1)),c='k')
plt.title('Straight Line')
plt.show()
print('拟合的直线方程:','y={:.3f}'.format(lr.coef_[0]),'x','+{:.3f}'.format(lr.intercept_))

在这里插入图片描述

下面提供较多的数据给线性回归拟合看看效果,可以得出一条线性回归直线,这条直线就是离这么多个点的距离之和最小的线。

from sklearn.datasets import make_regression
#生成用于回归分析的数据集
X ,y = make_regression(n_samples=50, n_features=1, n_informative=1,
                       noise=80, random_state=1)
#使用线性模型拟合数据
lr = LinearRegression().fit(X, y)
#生成等差数列用于显示图形
z = np.linspace(-3,3,200).reshape(-1,1)
#将数据散列化,点的颜色为蓝色,大小为60pixel
plt.scatter(X, y,c='b',s=60)
plt.plot(z,lr.predict(z),c='k')
plt.title('Linear Regression')
plt.show()
print('拟合的直线方程:y={:.3f}'.format(lr.coef_[0]),'x','+{:.3f}'.format(lr.intercept_))

#我们可以发现coef_和intercept_这两个属性都是以下滑线结尾。其实这是scikit-learn的一个特点,它的目的是将训练集的属性和用户设置的参数区分开。

在这里插入图片描述

二、最基本的线性模型——线性回归

线性回归,又称最小二乘法(ordinary least square,OLS),它可找出训练数据集中y的预测值和真实值的平方差最小的直线。

#导入数据集拆分工具
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.datasets import make_regression
# 生成样品数量为100,特征数量为2的数据集
X, y = make_regression(n_samples=100, n_features=2, n_informative=2, random_state=38) 
# 将数据集分割成训练数据集(75%)和测试数据集(25%)
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=8)
lr = LinearRegression().fit(X_train, y_train)
#输出拟合直线的系数和截距
print('直线方程={:.3f}'.format(lr.coef_[0]),'x1+','{:.3f}'.format(lr.coef_[1]),'x2+','{}'.format(lr.intercept_))
#有两个特征,每个特征值对应coef_的NumPy数组中的元素

#线性回归的性能表现,查看一下拟合模型的得分
print('训练数据集得分:{:.3f}'.format(lr.score(X_train, y_train)))
print('测试数据集得分:{:.3f}'.format(lr.score(X_test, y_test)))
#都是满分耶!能得这么高分其实是没加噪声啦@_@

在这里插入图片描述

上面的示例没有添加噪声所以会满分,下面添加一定噪声看线性回归模型的数据集得分是多少。这次模型的分数下降了许多T_T,这是由于实际数据的复杂度比手工合成的数据会高很多,使其表现大幅下降,同时分数差异那么大也说明了标准线性回归出现过拟合的问题。为此需要找一个模型能够控制模型的复杂度,即岭回归。

#测试下糖尿病数据集
from sklearn.datasets import load_diabetes
#分别载入数据集的数据和目标分类到X和y
X, y = load_diabetes().data, load_diabetes().target
#将数据集拆分为训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=8)
#使用线性回归模型进行拟合
lr = LinearRegression().fit(X_train, y_train)

#查看得分
print('测试数据集得分:{:.3f}'.format(lr.score(X_train, y_train)))
print('训练数据集得分:{:.3f}'.format(lr.score(X_test, y_test)))

在这里插入图片描述

三、使用L2正则化的线性模型——岭回归

(一)、岭回归的原理

使用L2正则化的线性模型——岭回归,一种改良的OLS。这种保留全部特征变量,只改变特征变量的系数值来避免过拟合的方法被称为L2正则化。可以看到两者都比线性回归得分低,同时这两者得分竟然一致(JOJO哒,这也在你的计算之中吗)。岭回归模型相对线性回归复杂度低,降低过拟合的可能性。同时模型复杂度低,意味着在训练集中表现越差,但模型在泛化方面的表现会更好。

#导入岭回归模型
from sklearn.linear_model import Ridge
#使用岭回归对数据进行拟合
ridge = Ridge().fit(X_train, y_train)
print('测试数据集得分:{:.3f}'.format(ridge.score(X_train, y_train)))
print('训练数据集得分:{:.3f}'.format(ridge.score(X_test, y_test)))

在这里插入图片描述

(二)、岭回归的参数调节

岭回归是在模型的简单性(使系数coef_趋近于零)和它的训练集上的性能之间实现平衡的一种模型。我们可以通过改变模型参数alpha(默认为1)来改变模型复杂度(alpha↑complexity↓generalization↑)和训练集的性能(alpha↑performance↓)。

#我们将alpha值设为10看看效果
ridge10 = Ridge(alpha=10).fit(X_train, y_train)
print('alpha为1时的训练数据集得分:{:.3f}'.format(ridge10.score(X_train, y_train)))
print('alpha为1时的测试数据集得分:{:.3f}\n'.format(ridge10.score(X_test, y_test)))
#可以看到分数明显下降了,同时可以看到训练数据集比测试数据集分数要高,说明模型出现过拟合的现象了,可以通过升高alpha值来降低过拟合的程度。

#降低alpha值会降低对系数的限制,我们试试将alpha值降到0.1看看,这时候系统接近于线性拟合模型
ridge0_1 = Ridge(alpha=0.1).fit(X_train, y_train)
print('alpha为0.1时的训练数据集得分:{:.3f}'.format(ridge0_1.score(X_train, y_train)))
print('alpha为0.1时的测试数据集得分:{:.3f}'.format(ridge0_1.score(X_test, y_test)))

#可以看出alpha值为0.1时的模型比线性回归模型的训练数据集分数低一些,但测试数据集得分却有轻微提升。

在这里插入图片描述

为了能更直观地查看不同alpha值对应模型的不同之处,可通过matplotlib提供的画图工具显示对应模型的系数coef_。
其中横坐标是特征变量的序号,纵坐标是特征变量的数量级。
当alpha为10时特征变量系数大多在0附近。
当alpha为1时,特征变量系数明显增大
当alpha为0.1时,特征变量就更大了,而且大部分和线性回归模型的特征变量系数重叠在一起。
由于线性回归模型没有进行任何正则化处理,所以有些特征变量系数非常大,都快跑到地图外面去了:3

import matplotlib.pyplot as plt
#绘制alpha=1时模型系数
plt.plot(ridge.coef_, 's', label='Ridge alpha=1')
#绘制alpha=10时模型系数
plt.plot(ridge10.coef_, '^', label='Ridge alpha=10')
#绘制alpha=0.1时模型系数
plt.plot(ridge0_1.coef_, 'v', label='Ridge alpha=0.1')
#绘制线性回归模型系数
plt.plot(lr.coef_, 'o', label='Linear Regression')
plt.xlabel("coefficient index")
plt.ylabel("coefficient magnitude")
plt.hlines(0, 0, len(lr.coef_))
plt.legend()

在这里插入图片描述

另外一个更好理解正则化对模型影响的方法,就是固定alpha值,通过改变样本数量来看模型的评分曲线。使用糖尿病数据进行分析,用这些采样的数据子集对线性回归模型和alpha值固定为1的岭回归模型进行对比。使用matplotlib的pyplot进行绘图,获得一个随数据集大小不断改变的模型评分折线图(也被称为了学习曲线learning curve)


# 导入学习曲线和数据分割方法
from sklearn.model_selection import learning_curve, KFold
#定义一个绘制学习曲线的函数

def plot_learning_curve(est, X_, y_):
#将数据进行20次拆分并对模型进行评分
    training_set_size, train_scores, test_scores = learning_curve(
        est, X_, y_, train_sizes=np.linspace(.1,1,20), 
        cv=KFold(20,shuffle=True,random_state=1))
    estimator_name = est.__class__.__name__
    line = plt.plot(training_set_size, train_scores.mean(axis=1), '--',
                    label="training "+estimator_name)
    plt.plot(training_set_size,test_scores.mean(axis=1), '-',
             label='test ' + estimator_name, c=line[0].get_color())
    plt.xlabel('Training set size')
    plt.ylabel('Score')
    plt.ylim(0, 1.1)#设置y轴上限

plot_learning_curve(Ridge(alpha=1), X, y)
plot_learning_curve(LinearRegression(), X, y)
plt.legend(loc=(0, 1.05), ncol=2, fontsize=12)

在这里插入图片描述

可以看出两者的训练集得分都比测试集要高。岭回归训练集和测试集得分差异明显比线性回归的少。数据越多,两者得分趋于稳定且持平,说明了在数据量够多的时候,正则化就显得不那么重要了。同时可以看到,数据量越多,线性回归模型的训练集得分呈下降趋势,说明了随着数据增加,线性回归模型就越不容易产生过拟合,或者说越难记住这些数据。

三、使用L1正则化的线性模型——套索回归

(一)、岭回归的原理

套索回归的原理:和岭回归一样,会将系数限制在非常接近0的范围;但它限制的方式有所不同,它会使部分特征变量的系数等于0,即忽略部分特征变量,这有助于模型突出那些重要的特征变量且更容易理解。

from sklearn.linear_model import Lasso
#使用套索回归拟合数据
lasso = Lasso().fit(X_train, y_train)
print('套索回归的训练数据集得分:{:.3f}'.format(lasso.score(X_train, y_train)))
print('套索回归的测试数据集得分:{:.3f}'.format(lasso.score(X_test, y_test)))
print("套索回归的特征:{}".format(lasso.coef_))
print("套索回归所使用的的特征数:{}".format(np.sum(lasso.coef_!=0)))
# 可以看到,在10个特征中,套索回归只用了3个特征。

在这里插入图片描述

(二)、套索回归的参数调节

套索回归和岭回归一样,有可选参数alpha来确定约束特征变量参数为0的强度。可以通过降低alpha值来降低模型欠拟合的程度。

# 增大最大迭代(iteration)次数的默认值,否则系统会提示我们增大它的值
lasso01 = Lasso(alpha=0.1, max_iter=100000).fit(X_train, y_train)
print('套索回归的训练数据集得分:{:.3f}'.format(lasso01.score(X_train, y_train)))
print('套索回归的测试数据集得分:{:.3f}'.format(lasso01.score(X_test, y_test)))
print("套索回归的特征:{}".format(lasso01.coef_))
print("套索回归所使用的的特征数:{}".format(np.sum(lasso01.coef_!=0)))

lasso00001 = Lasso(alpha=0.0001, max_iter=100000).fit(X_train, y_train)
print('套索回归的训练数据集得分:{:.3f}'.format(lasso00001.score(X_train, y_train)))
print('套索回归的测试数据集得分:{:.3f}'.format(lasso00001.score(X_test, y_test)))
print("套索回归的特征:{}".format(lasso00001.coef_))
print("套索回归所使用的的特征数:{}".format(np.sum(lasso00001.coef_!=0)))

在这里插入图片描述
可以看到降低alpha值可以拟合出更复杂的模型,提升训练集和测试集的表现。
相比岭回归,套索回归只用了10个特征中的7个,更容易被理解。
但同时如果alpha值设得太低,则会大大削弱正则化的效果,会让他表现得和线性回归一样。
假如将alpha值设为0.001,会发现它和线性回归拟合的模型相差无几。

(三)、套索回归和岭回归的对比

可以看到alpha为1时,大部分系数趋于零;
当alpha为0.1时,趋于零的系数变少;
当alpha为0.0001时,模型基本没有被正则化,大部分系数都是非零的;

在实践中,岭回归是这两个模型中的优选,但当数据较多,且只想突出其中一部分特征或者让人更容易理解时,套索回归是更好的选择。

#绘制alpha值为1时的套索回归模型系数图形
plt.plot(lasso.coef_, 's', label="Lasso alpha=1")
#绘制alpha值为0.1时的套索回归模型系数图形
plt.plot(lasso01.coef_, '^', label="Lasso alpha=0.1")
#绘制alpha值为0.001时的套索回归模型系数图形
plt.plot(lasso00001.coef_, 'v', label="Lasso alpha=0.0001")
#绘制alpha值为0.1时的岭回归模型系数图形
plt.plot(ridge01.coef_, 'o', label="Ridge alpha=0.1")
plt.legend(ncol=2,loc=(0,1.05))
plt.ylim(-1000,1000)
plt.xlabel("Coefficient index")
plt.ylabel("Coefficient magnitude")

在这里插入图片描述

scikit-learn中还提供一种模型,称为弹性网模型(Elastic Net),它综合套索回归和岭回归的惩罚因子,不过在实际使用中需要调节L1和L2正则化参数。

参考资料:
《深入浅出python机器学习》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值