1、归一化 Normalization
1.1、归一化目的
梯度下降的原理和应用,我们已经在前面课程中进行了学习。
不同方向的陡峭度是不一样的,即不同维度的数值大小是不同。也就是说梯度下降的快慢是不同的:
如果拿多元线性回归举例的话,因为多元线性回归的损失函数 MSE 是凸函数,所以我们可以把损失函数看成是一个碗。然后下面的图就是从碗上方去俯瞰!哪里是损失最小的地方呢?当然对应的就是碗底的地方!所以下图碗中心的地方颜色较浅的区域就是损失函数最小的地方。

上面两张图都是进行梯度下降,你有没有发现,略有不同啊?两张图形都是鸟瞰图,左边的图形做了归一化处理,右边是没有做归一化的俯瞰图。
啥是归一化呢?请带着疑问跟我走~
我们先来说一下为什么没做归一化是右侧图示,举个例子假如我们客户数据信息,有两个维度,一个是用户的年龄,一个是用户的月收入,目标变量是快乐程度。
| name | age | salary | happiness |
|---|---|---|---|
| 代码简史 | 36 | 7000 | 100 |
| 智哥 | 38 | 200000 | 180 |
| 大王 | 23 | 300000 | 164 |
| …… | …… | …… | …… |
我们可以里面写出线性回归公式,
我们把
这样是不是就比较好理解为什么之前右侧示图里为什么
因为
总结一下 ,根据上面得到的两个结论 ,它俩之间是互相矛盾的 ,意味着最后
结论:
归一化的一个目的是,使得梯度下降在不同维度

经过归一化处理,收敛的速度,明显快了!
1.2、归一化本质
做归一化的目的是要实现**"共同富裕"**,而之所以梯度下降优化时不能达到步调一致的根本原因其实还是
答案自然就出来了,就是把
1.3、最大值最小值归一化
也称为离差标准化,是对原始数据的线性变换,使结果值映射到[0 - 1]之间。转换函数如下:
其实我们很容易发现使用最大值最小值归一化(min-max标准化)的时候,优点是一定可以把数值归一到 0 ~ 1 之间,缺点是如果有一个(比如马云的财富),正如我们举的例子一样,会使得一个数值为 1,其它数值都几乎为 0,所以受离群值的影响比较大!
代码演示:
import numpy as np
x_1 = np.random.randint(1,10,size = 10)
x_2 = np.random.randint(100,300,size = 10)
x = np.c_[x_1,x_2]
print('归一化之前的数据:')
display(x)
x_ = (x - x.min(axis = 0)) / (x.max(axis = 0) - x.min(axis = 0))
print('归一化之后的数据:')
display(x_)使用scikit-learn函数:
import numpy as np
from sklearn.preprocessing import MinMaxScaler
x_1 = np.random.randint(1,10,size = 10)
x_2 = np.random.randint(100,300,size = 10)
x = np.c_[x_1,x_2]
print('归一化之前的数据:')
display(x)
min_max_scaler = MinMaxScaler()
x_ = min_max_scaler.fit_transform(x)
print('归一化之后的数据:')
display(x_)1.4、Z-score标准化
这种方法给予原始数据的均值(mean)和标准差(standard deviation)进行数据的标准化,叫做Z-score标准化。经过处理的数据符合标准正态分布,即均值为0,标准差为1,转化函数为:
其中μ为所有样本数据的均值,σ为所有样本数据的标准差。
相对于最大值最小值归一化来说,因为标准归一化除以了标准差,而标准差的计算会考虑到所有样本数据,所以受到离群值的影响会小一些,这就是除以方差的好处!但是,0-均值标准化不一定会把数据缩放到 0 ~ 1 之间了。既然是0均值,也就意味着,有正有负!
import numpy as np
x_1 = np.random.randint(1,10,size = 10)
x_2 = np.random.randint(100,300,size = 10)
x = np.c_[x_1,x_2]
print('归一化之前的数据:')
display(x)
x_ = (x - x.mean(axis = 0)) / x.std(axis = 0)
print('归一化之后的数据:')
display(x_)使用scikit-learn函数:
import numpy as np
from sklearn.preprocessing import StandardScaler
x_1 = np.random.randint(1,10,size = 10)
x_2 = np.random.randint(100,300,size = 10)
x = np.c_[x_1,x_2]
print('归一化之前的数据:')
display(x)
standard_scaler = StandardScaler()
x_ = standard_scaler.fit_transform(x)
print('归一化之后的数据:')
display(x_) 那为什么要减去均值呢?其实做均值归一化还有一个特殊的好处(对比最大值最小值归一化,全部是正数0~1),我们来看一下梯度下降的式子,你就会发现

那我们如何才能做到让
结论:0-均值标准化处理数据之后,属性有正有负,可以让梯度下降沿着最优路径进行~
注意:
我们在做特征工程的时候,很多时候如果对训练集的数据进行了预处理,比如这里讲的归一化,那么未来对测试集的时候,和模型上线来新的数据的时候,都要进行相同的数据预处理流程,而且所使用的均值和方差是来自当时训练集的均值和方差!
因为我们人工智能要干的事情就是从训练集数据中找规律,然后利用找到的规律去预测新产生的数据。这也就是说假设训练集和测试集以及未来新来的数据是属于同分布的!从代码上面来说如何去使用训练集的均值和方差呢?就需要把 scaler 对象持久化, 回头模型上线的时候再加载进来去对新来的数据进行处理。
# 训练数据
import numpy as np
from sklearn.preprocessing import StandardScaler
x_1 = np.random.randint(1,10,size = 10)
x_2 = np.random.randint(100,300,size = 10)
x = np.c_[x_1,x_2]
print('归一化之前的数据:')
display(x)
standard_scaler = StandardScaler() # 模型,归一化
x_ = standard_scaler.fit_transform(x)
print('归一化之后的数据:')
display(x_)2、正则化 Regularization
2.1、过拟合欠拟合
- 欠拟合(under fit):还没有拟合到位,训练集和测试集的准确率都还没有到达最高,学的还不到位。
- 过拟合(over fit):拟合过度,训练集的准确率升高的同时,测试集的准确率反而降低。学的过度了(走火入魔),做过的卷子都能再次答对(死记硬背),考试碰到新的没见过的题就考不好(不会举一反三)。
- 恰到好处(just right):过拟合前,训练集和测试集准确率都达到巅峰。好比,学习并不需要花费很多时间,理解的很好,考试的时候可以很好的把知识举一反三。

正则化就是防止过拟合,增加模型的鲁棒性,鲁棒是 Robust 的音译,也就是强壮的意思。就像计算机软件在面临攻击、网络过载等情况下能够不死机不崩溃,这就是软件的鲁棒性。鲁棒性调优就是让模型拥有更好的鲁棒性,也就是让模型的泛化能力和推广 能力更加的强大。
举例子说明:下面两个式子描述同一条直线哪个更好?
第一个更好,因为下面的公式是上面的十倍,当 w 越小公式的容错的能力就越好。因为把测试数据带入公式中如果测试集原来是 [32, 128] 在带入的时候发生了一些偏差,比如说变成 [30, 120] ,第二个模型结果就会比第一个模型结果的偏差大的多。公式中
所以正则化(鲁棒性调优)的本质就是牺牲模型在训练集上的正确率来提高推广、泛化能力, W 在数值上越小越好,这样能抵抗数值的扰动。同时为了保证模型的正确率 W 又不能极小。 故而人们将原来的损失函数加上一个惩罚项,这里面损失函数就是原来固有的损失函数,比如回归的话通常是 MSE,分类的话通常是 cross entropy 交叉熵,然后在加上一部分惩罚项来使得计算出来的模型 W 相对小一些来带来泛化能力。
常用的惩罚项有L1 正则项或者 L2 正则项:
对应曼哈顿距离 对应欧氏距离
其实 L1 和 L2 正则的公式数学里面的意义就是范数,代表空间中向量到原点的距离:

当我们把多元线性回归损失函数加上 L2 正则的时候,就诞生了 Ridge 岭回归。当我们把多元线性回归损失函数加上 L1 正则的时候,就孕育出来了 Lasso 回归。其实 L1 和 L2 正则项惩罚项可以加到任何算法的损失函数上面去提高计算出来模型的泛化能力的。
2.2、套索回归(Lasso)
先从线性回归开始,其损失函数如下:
L1正则化的损失函数,令
令
Lasso 回归的损失函数可以表示为:
上面公式意味着:原始损失函数
假设我们有一个篮球队,我们想要选出五名最佳球员组成首发阵容。我们可以根据他们的得分、篮板、助攻等统计数据来评估他们的表现。但是,我们还需要考虑球员的防守能力,以确保球队在比赛中的整体表现。因此,我们在评估球员的得分、篮板和助攻等统计数据(即原始损失函数
在这个例子中,我们可以将选择五名首发球员的过程看作是一个优化问题。我们希望找到一组球员,使得他们的得分、篮板、助攻等统计数据总和最高,同时还要满足一定的防守能力要求。这就是在原始损失函数(即球员的进攻数据)后添加 L1 正则项(即球员的防守数据)的目的。这样,我们在求解这个优化问题时,就需要在保证进攻数据最大化的同时,满足防守能力的要求。因此,我们可以说,在原始损失函数
在 Lasso 回归中,原始损失函数
注意到 L1正则化是权值的绝对值之和。

图中等值线是
在图中,当
而正则化前面的系数
代码演示
import matplotlib.pyplot as plt
# α 的值是:1
# 1 = x + y
# y = 1 -x
f = lambda x : 1- x
x = np.linspace(0,1,100)
plt.axis('equal')
plt.plot(x, f(x), color = 'green',label = '$\\alpha = 1$')
plt.plot(x, -f(x),color = 'green')
plt.plot(-x,f(x),color = 'green')
plt.plot(-x,-f(x),color = 'green')
# α 的值是:3
# 1 = 3 * x + 3 * y
# y = 1/3 -x
f2 = lambda x : 1/3 - x
x2 = np.linspace(0,1/3,100)
plt.plot(x2, f2(x2),color = 'red',label = '$\\alpha = 3$')
plt.plot(x2, -f2(x2),color = 'red')
plt.plot(-x2,f2(x2),color = 'red')
plt.plot(-x2,-f2(x2),color = 'red')
# 一些列设置
plt.xlim(-2,2)
plt.ylim(-2,2)
ax = plt.gca()
ax.spines['right'].set_color('None') # 将assets的右框隐藏
ax.spines['top'].set_color('None') # 将assets的上边框隐藏
ax.spines['bottom'].set_position(('data', 0)) # x轴出现在y轴的-1 位置
ax.spines['left'].set_position(('data', 0))
plt.legend()
权重更新规则如下:
- 损失函数:
- 更新规则:
其中
注意当
综上所述,L1正则化权重更新如下:
- Lasso回归和线性回归相比,多了一项:
- $\eta $ 大于零,表示梯度下降学习率
大于零,表示L1正则化系数 - 当
为正时候 ,直接减去 (大于0),所以正的 变小了,向0靠近 - 当
为负时候 ,相当于直接加上 (大于0),所以负的 变大了,绝对值变小,向0靠近
有的书本上公式会这样写,其中
2.3、岭回归(Ridge)
也是先从线性回归开始,其损失函数如下:
L2正则化的损失函数(对L2范数,进行了平方运算),令
令
同样可以画出他们在二维平面上的图形,如下:

二维平面下 L2 正则化的函数图形是个圆(绝对值的平方和,是个圆),与方形相比,被磨去了棱角。因此
权重更新规则如下:
- 损失函数:
- 更新规则:
其中
综上所述,L2正则化权重更新如下(
其中
有的书本上,公式写法可能不同:其中
3、线性回归衍生算法
接下来,我们一起学习一下scikit-learn中为我们提供的线性回归衍生算法,根据上面所学的原理,对比线性回归加深理解。
3.1、Lasso算法使用
这是scikit-learn官网给出的套索回归的,损失函数公式,注意,它用的矩阵表示,里面用到范数运算。
公式中多了一项:这是一个常数项,去掉之后,也不会影响损失函数公式计算。在岭回归中,就没有这项。
L1正则化和普通线性回归系数对比:
import numpy as np
from sklearn.linear_model import Lasso
from sklearn.linear_model import SGDRegressor
# 1、创建数据集X,y
X = 2*np.random.rand(100, 20)
w = np.random.randn(20,1)
b = np.random.randint(1,10,size = 1)
y = X.dot(w) + b + np.random.randn(100, 1)
print('原始方程的斜率:',w.ravel())
print('原始方程的截距:',b)
lasso = Lasso(alpha= 0.5)
lasso.fit(X, y)
print('套索回归求解的斜率:',lasso.coef_)
print('套索回归求解的截距:',lasso.intercept_)
# 线性回归梯度下降方法
sgd = SGDRegressor(penalty='l2',alpha=0, l1_ratio=0)
sgd.fit(X, y.reshape(-1,))
print('随机梯度下降求解的斜率是:',sgd.coef_)
print('随机梯度下降求解的截距是:',sgd.intercept_)结论:
- 和没有正则项约束线性回归对比,可知L1正则化,将方程系数进行了缩减,部分系数为0,产生稀疏模型
越大,模型稀疏性越强,越多的参数为0 - Lasso回归源码解析:
- alpha:正则项系数
- fit_intercept:是否计算
截距项 - normalize:是否做归一化
- precompute:bool 类型,默认值为False,决定是否提前计算Gram矩阵来加速计算
- max_iter:最大迭代次数
- tol:结果的精确度
- warm_start:bool类型,默认值为False。如果为True,那么使⽤用前⼀次训练结果继续训练。否则从头开始训练
3.2、Ridge算法使用
这是scikit-learn官网给出的岭回归的,损失函数公式,注意,它用的矩阵表示,里面用到范数运算。
L2正则化和普通线性回归系数对比:
import numpy as np
from sklearn.linear_model import Ridge
from sklearn.linear_model import SGDRegressor
# 1、创建数据集X,y
X = 2*np.random.rand(100, 5)
w = np.random.randint(1,10,size = (5,1))
b = np.random.randint(1,10,size = 1)
y = X.dot(w) + b + np.random.randn(100, 1)
print('原始方程的斜率:',w.ravel())
print('原始方程的截距:',b)
ridge = Ridge(alpha= 1, solver='sag')
ridge.fit(X, y)
print('岭回归求解的斜率:',ridge.coef_)
print('岭回归求解的截距:',ridge.intercept_)
# 线性回归梯度下降方法
sgd = SGDRegressor(penalty='l2',alpha=0,l1_ratio=0)
sgd.fit(X, y.reshape(-1,))
print('随机梯度下降求解的斜率是:',sgd.coef_)
print('随机梯度下降求解的截距是:',sgd.intercept_)结论:
- 和没有正则项约束线性回归对比,可知L2正则化,将方程系数进行了缩小
增大求解出来的方程斜率变小 - Ridge回归源码解析:
- alpha:正则项系数
- fit_intercept:是否计算
截距项 - normalize:是否做归一化
- max_iter:最大迭代次数
- tol:结果的精确度
- solver:优化算法的选择
3.3、Elastic-Net算法使用
这是scikit-learn官网给出的弹性网络回归的,损失函数公式,注意,它用的矩阵表示,里面用到范数运算。
Elastic-Net 回归,即岭回归和Lasso技术的混合。弹性网络是一种使用 L1, L2 范数作为先验正则项训练的线性回归模型。 这种组合允许学习到一个只有少量参数是非零稀疏的模型,就像 Lasso 一样,但是它仍然保持一些像 Ridge 的正则性质。我们可利用 l1_ratio 参数控制 L1 和 L2 的凸组合。
弹性网络在很多特征互相联系(相关性,比如身高和体重就很有关系)的情况下是非常有用的。Lasso 很可能只随机考虑这些特征中的一个,而弹性网络更倾向于选择两个。
在实践中,Lasso 和 Ridge 之间权衡的一个优势是它允许在迭代过程中继承 Ridge 的稳定性。
弹性网络回归和普通线性回归系数对比:
import numpy as np
from sklearn.linear_model import ElasticNet
from sklearn.linear_model import SGDRegressor
# 1、创建数据集X,y
X = 2*np.random.rand(100, 20)
w = np.random.randn(20,1)
b = np.random.randint(1,10,size = 1)
y = X.dot(w) + b + np.random.randn(100, 1)
print('原始方程的斜率:',w.ravel())
print('原始方程的截距:',b)
model = ElasticNet(alpha= 1, l1_ratio = 0.7)
model.fit(X, y)
print('弹性网络回归求解的斜率:',model.coef_)
print('弹性网络回归求解的截距:',model.intercept_)
# 线性回归梯度下降方法
sgd = SGDRegressor(penalty='l2',alpha=0, l1_ratio=0)
sgd.fit(X, y.reshape(-1,))
print('随机梯度下降求解的斜率是:',sgd.coef_)
print('随机梯度下降求解的截距是:',sgd.intercept_)结论:
- 和没有正则项约束线性回归对比,可知Elastic-Net网络模型,融合了L1正则化L2正则化
- Elastic-Net 回归源码解析:
- alpha:混合惩罚项的常数
- l1_ratio:弹性网混合参数,0 <= l1_ratio <= 1,对于 l1_ratio = 0,惩罚项是L2正则惩罚。对于 l1_ratio = 1是L1正则惩罚。对于 0
- fit_intercept:是否计算
截距项 - normalize:是否做归一化
- precompute:bool 类型,默认值为False,决定是否提前计算Gram矩阵来加速计算
- max_iter:最大迭代次数
- tol:结果的精确度
- warm_start:bool类型,默认值为False。如果为True,那么使⽤用前⼀次训练结果继续训练。否则从头开始训练
4、多项式回归
4.1、多项式回归基本概念
升维的目的是为了去解决欠拟合的问题的,也就是为了提高模型的准确率为目的的,因为当维度不够时,说白了就是对于预测结果考虑的因素少的话,肯定不能准确的计算出模型。

在做升维的时候,最常见的手段就是将已知维度进行相乘(或者自乘)来构建新的维度,如下图所示。普通线性方程,无法拟合规律,必须是多项式,才可以完美拟合曲线规律,图中是二次多项式。

对于多项式回归来说主要是为了扩展线性回归算法来适应更广泛的数据集,比如我们数据集有两个维度
此时拟合出来的方程就是曲线,可以解决一些线性回归的欠拟合问题!
4.2、多项式回归实战1.0(普通线性回归)
import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
# 1、创建数据,并进行可视化
X = np.linspace(-1,11,num = 100)
y = (X - 5)**2 + 3*X -12 + np.random.randn(100)
X = X.reshape(-1,1)
plt.scatter(X,y)
# 2、创建预测数据
X_test = np.linspace(-2,12,num = 200).reshape(-1,1)
# 3、不进行升维 + 普通线性回归
model_1 = LinearRegression()
model_1.fit(X,y)
y_test_1 = model_1.predict(X_test)
plt.plot(X_test,y_test,color = 'red')
# 4、多项式升维 + 普通线性回归
X = np.concatenate([X,X**2],axis = 1)
model_2 = LinearRegression()
model_2.fit(X,y)
# 5、测试数据处理,并预测
X_test = np.concatenate([X_test,X_test**2],axis = 1)
y_test_2 = model_2.predict(X_test)
# 6、数据可视化,切片操作
plt.plot(X_test[:,0],y_test_2,color = 'green')结论:
- 不进行多项式升维,拟合出来的曲线,是线性的直线,和目标曲线无法匹配
- 使用np.concatenate()进行简单的,幂次合并,注意数据合并的方向axis = 1
- 数据可视化时,注意切片,因为数据升维后,多了平方这一维

4.3、多项式回归实战(梯度下降)
import numpy as np
import matplotlib.pyplot as plt
from sklearn.preprocessing import PolynomialFeatures,StandardScaler
from sklearn.linear_model import SGDRegressor
# 1、创建数据,并进行可视化
X = np.linspace(-1,11,num = 100)
y = (X - 5)**2 + 3*X -12 + np.random.randn(100)
X = X.reshape(-1,1)
plt.scatter(X,y)
# 2、创建预测数据
X_test = np.linspace(-2,12,num = 200).reshape(-1,1)
# 3、使用PolynomialFeatures进行特征升维
poly = PolynomialFeatures()
poly.fit(X,y)
X = poly.transform(X)
s = StandardScaler()
X = s.fit_transform(X)
# model = SGDRegressor(penalty='l2',eta0 = 0.0001,max_iter = 10000)
model = SGDRegressor(penalty='l2',eta0 = 0.01)
model.fit(X,y)
# 4、预测数据
X_test = poly.transform(X_test)
X_test_norm = s.transform(X_test)
y_test = model.predict(X_test_norm)
plt.plot(X_test[:,1],y_test,color = 'green')结论:
- eta0表示学习率,设置合适的学习率,才能拟合成功
- 多项式升维,需要对数据进行Z-score归一化处理,效果更佳出色
- SGD随机梯度下降需要调整参数,以使模型适应数据
5、代码实战天猫双十一销量预测
天猫双十一,从2009年开始举办,第一届成交额仅仅0.5亿,后面呈现了爆发式的增长,那么这些增长是否有规律呢?是怎么样的规律,该如何分析呢?我们使用多项式回归一探究竟!

数据可视化,历年天猫双十一销量数据:
import numpy as np
from sklearn.linear_model import SGDRegressor
import matplotlib.pyplot as plt
plt.rcParams['font.size'] = 18
plt.figure(figsize=(9,6))
# 创建数据,年份数据2009 ~ 2019
X = np.arange(2009,2020)
y = np.array([0.5,9.36,52,191,350,571,912,1207,1682,2135,2684])
plt.bar(X,y,width = 0.5,color = 'green')
plt.plot(X,y,color = 'red')
_ = plt.xticks(ticks = X)
有图可知,在一定时间内,随着经济的发展,天猫双十一销量与年份的关系是多项式关系!假定,销量和年份之间关系是三次幂关系:
import numpy as np
from sklearn.linear_model import SGDRegressor
import matplotlib.pyplot as plt
from sklearn.preprocessing import PolynomialFeatures
from sklearn.preprocessing import StandardScaler
plt.figure(figsize=(12,9))
# 1、创建数据,年份数据2009 ~ 2019
X = np.arange(2009,2020)
y = np.array([0.5,9.36,52,191,350,571,912,1207,1682,2135,2684])
# 2、年份数据,均值移除,防止某一个特征列数据天然的数值太大而影响结果
X = X - X.mean()
X = X.reshape(-1,1)
# 3、构建多项式特征,3次幂
poly = PolynomialFeatures(degree=3)
X = poly.fit_transform(X)
s = StandardScaler()
X_norm = s.fit_transform(X)
# 4、创建模型
model = SGDRegressor(penalty='l2',eta0 = 0.5,max_iter = 5000)
model.fit(X_norm,y)
# 5、数据预测
X_test = np.linspace(-5,6,100).reshape(-1,1)
X_test = poly.transform(X_test)
X_test_norm = s.transform(X_test)
y_test = model.predict(X_test_norm)
# 6、数据可视化
plt.plot(X_test[:,1],y_test,color = 'green')
plt.bar(X[:,1],y)
plt.bar(6,y_test[-1],color = 'red')
plt.ylim(0,4096)
plt.text(6,y_test[-1] + 100,round(y_test[-1],1),ha = 'center')
_ = plt.xticks(np.arange(-5,7),np.arange(2009,2021))
结论:
- 数据预处理,均值移除。如果特征基准值和分散度不同在某些算法(例如回归算法,KNN等)上可能会大大影响了模型的预测能力。通过均值移除,大大增强数据的离散化程度。
- 多项式升维,需要对数据进行Z-score归一化处理,效果更佳出色
- SGD随机梯度下降需要调整参数,以使模型适应多项式数据
- 从2020年开始,天猫双十一统计的成交额改变了规则为11.1日~11.11日的成交数据(之前的数据为双十一当天的数据),2020年成交额为4980亿元
- 可以,经济发展有其客观规律,前11年高速发展(曲线基本可以反应销售规律),到2020年是一个转折点
6、代码实战中国人寿保费预测
6.1、数据加载与介绍
import numpy as np
import pandas as pd
data = pd.read_excel('./中国人寿.xlsx')
print(data.shape)
data.head()数据介绍:
- 共计1338条保险数据,每条数据7个属性
- 最后一列charges是保费
- 前面6列是特征,分别为:年龄、性别、体重指数、小孩数量、是否抽烟、所在地区
6.2、EDA数据探索
EDA(Exploratory Data Analysis,数据探索分析)是数据分析的第一步,它旨在了解数据的特征、结构和潜在规律,为进一步分析和建模提供基础。
import seaborn as sns
# 性别对保费影响
sns.kdeplot(data['charges'],shade = True,hue = data['sex'])
# 地区对保费影响
sns.kdeplot(data['charges'],shade = True,hue = data['region'])
# 吸烟对保费影响
sns.kdeplot(data['charges'],shade = True,hue = data['smoker'])
# 孩子数量对保费影响
sns.kdeplot(data['charges'],shade = True,hue = data['children'],palette='Set1')总结:
- 不同性别对保费影响不大,不同性别的保费的概率分布曲线基本重合,因此这个特征无足轻重,可以删除
- 地区同理
- 吸烟与否对保费的概率分布曲线差别很大,整体来说不吸烟更加健康,那么保费就低,这个特征很重要
- 家庭孩子数量对保费有一定影响
6.3、特征工程
data = data.drop(['region', 'sex'], axis=1)
data.head() # 删除不重要特征
# 体重指数,离散化转换,体重两种情况:标准、肥胖
def convert(df,bmi):
df['bmi'] = 'fat' if df['bmi'] >= bmi else 'standard'
return df
data = data.apply(convert, axis = 1, args=(30,))
data.head()
# 特征提取,离散型数据转换为数值型数据
data = pd.get_dummies(data)
data.head()
# 特征和目标值抽取
X = data.drop('charges', axis=1) # 训练数据
y = data['charges'] # 目标值
X.head()6.4、特征升维
from sklearn.linear_model import LinearRegression
from sklearn.linear_model import ElasticNet
from sklearn.metrics import mean_squared_error,mean_squared_log_error
# 数据拆分
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import PolynomialFeatures
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
# 特征升维
poly = PolynomialFeatures(degree= 2, include_bias = False)
X_train_poly = poly.fit_transform(X_train)
X_test_poly = poly.fit_transform(X_test)6.5、模型训练与评估
普通线性回归:
# 升维数据
model = LinearRegression()
model.fit(X_train_poly,y_train)
print('训练数据表现:',model.score(X_train_poly,y_train))
print('测试数据表现:',model.score(X_test_poly,y_test))
print('训练数据MSE:',mean_squared_error(y_train,model.predict(X_train_poly)))
print('测试数据MSE:',mean_squared_error(y_test, model.predict(X_test_poly)))
print('训练数据MSLE:',mean_squared_log_error(y_train,model.predict(X_train_poly)))
print('测试数据MSLE:',mean_squared_log_error(y_test, model.predict(X_test_poly)))未升维数据对比(表现差强人意,说明升维更棒)
# 未进行特征升维建模预测表现
model = LinearRegression()
model.fit(X_train,y_train)
print('训练数据表现:',model.score(X_train,y_train))
print('测试数据表现:',model.score(X_test,y_test))
print('训练数据MSE:',mean_squared_error(y_train,model.predict(X_train)))
print('测试数据MSE:',mean_squared_error(y_test, model.predict(X_test)))
print('训练数据MSLE:',mean_squared_log_error(y_train,model.predict(X_train)))
print('测试数据MSLE:',mean_squared_log_error(y_test, model.predict(X_test)))弹性网络回归:
# 升维后的数据,特征变多了
import warnings
warnings.filterwarnings('ignore')
from sklearn.preprocessing import StandardScaler
# X_train_poly_norm = scaler.fit_transform(X_train_poly)
# X_test_poly_norm = scaler.transform(X_test_poly)
model = ElasticNet(alpha=0.1,l1_ratio=0.3,max_iter=1000)
model.fit(X_train_poly,y_train)
print('训练数据表现:',model.score(X_train_poly,y_train))
print('测试数据表现:',model.score(X_test_poly,y_test))
print('训练数据MSE:',mean_squared_error(y_train,model.predict(X_train_poly)))
print('测试数据MSE:',mean_squared_error(y_test, model.predict(X_test_poly)))
print('训练数据MSLE:',mean_squared_log_error(y_train,model.predict(X_train_poly)))
print('测试数据MSLE:',mean_squared_log_error(y_test, model.predict(X_test_poly)))未升维数据对比(表现差强人意,说明升维更棒)
# 未进行特征升维建模预测表现
model = ElasticNet(alpha=0.1,l1_ratio=0.3,max_iter=1000)
model.fit(X_train,y_train)
print('训练数据表现:',model.score(X_train,y_train))
print('测试数据表现:',model.score(X_test,y_test))
print('训练数据MSE:',mean_squared_error(y_train,model.predict(X_train)))
print('测试数据MSE:',mean_squared_error(y_test, model.predict(X_test)))
print('训练数据MSLE:',mean_squared_log_error(y_train,model.predict(X_train)))
print('测试数据MSLE:',mean_squared_log_error(y_test, model.predict(X_test)))结论:
- 进行EDA数据探索,可以查看无关紧要特征
- 进行特征工程:删除无用特征、特征离散化、特征提取。这对机器学习都至关重要
- 对于简单的数据(特征比较少)进行线性回归,一般需要进行特征升维
- 选择不同的算法,进行训练和评估,从中筛选优秀算法