面试题

描述梯度下降算法思想?

这个问题在面试中出现的概率很高。面试官问“梯度下降算法的思想”,通常不是想听你背公式,而是想考察你是否真正理解了这个“让模型学习”的核心逻辑。

如果我是面试者,我会按照以下逻辑来回答,既有直观比喻,又有数学内核,还能体现出对关键点的把握。

核心回答思路

我会把回答分成四个层次,从直观到深入,层层递进:

1. 一句话概括(展现宏观理解)

“梯度下降是一种一阶迭代优化算法,核心思想是:沿着目标函数(通常是损失函数)下降最快的方向,也就是负梯度方向,逐步更新模型参数,直到找到函数的最小值(或局部最小值)。”

2. 生活化比喻(展现沟通能力)

“可以想象这样一个场景:你一个人深夜被蒙着眼睛扔在山里,要摸黑走回山底的村庄。你的拐杖只能帮你探测到脚下这一小块地方哪个方向是向下的,你就往那个方向迈一步。每走一步,就再探测、再迈步。虽然每次迈步的方向不一定完全对准山底,但只要重复这个过程,最终就能走下山。”

  • 蒙眼的人:模型,不知道全局最优解在哪。

  • 脚下的坡度:当前点的梯度。

  • 迈一步:一次参数更新。

  • 步长大小:学习率。

3. 数学抽象(展现专业硬实力)

“把这个过程抽象成数学公式,就是:

4. 三个关键细节(展现思考深度)

在讲完基本思想后,如果能主动提出下面这三点,会让面试官觉得你考虑得很周全:

  • 学习率的重要性:步子迈太大,容易直接跨过山谷甚至飞出山区(无法收敛);步子迈太小,下山太慢,可能困在小土包上就以为到山底了(陷入局部最优或训练缓慢)。

  • 局部最优 vs 全局最优:梯度下降本质上是个“近视眼”,它只能保证走到脚下的“谷底”,不一定是整座山的最低点。这在非凸函数中是常见挑战。

  • 计算量的变体:

    • 批量梯度下降:每次下山前,要把全山的路都探测一遍(计算所有样本),走得很稳,但太慢。

    • 随机梯度下降:只随机看脚下一个小土块就决定方向(每次随机一个样本),走得快,但路径曲折。

    • 小批量梯度下降:结合两者,看周围一小片区域(一个小批量数据),综合决定方向,是工程中最常用的方式。

总结

面试官问这个问题,本质上是在问:“你觉得机器是怎么学习的?”

通过回答这个问题,你可以展现出以下能力:

  1. 抽象能力:把数学公式还原成物理过程。

  2. 逻辑能力:理解“目标 -> 测量 -> 调整 -> 迭代”的闭环。

  3. 工程敏感度:意识到步长(学习率)和效率(批量选择)的重要性。

最后可以用一句话收尾,展示对前沿的了解:“虽然现在有很多像Adam这样的自适应优化器,但它们的内核思想依然是基于梯度下降。”

聊一下欠拟合和过拟合?

欠拟合是表现在训练集和测试集都不能很好的拟合数据。表现为预测的准确率不足。原因是因为模型过于简单,更深层的原因通常是因为训练量不足或特征值过少。一般欠拟合可以通过增加特征值(组合、泛化、相关性)或增加训练量来矫正。

过拟合是表现在训练集拟合度高,而测试集拟合度不足。原因是因为模型过于复杂,更深层原因是由于训练中特征值过多,且受异常值影响过大导致的模型过于复杂。一般过拟合需要通过筛选特征值或者清洗异常数据,或者正则化来矫正

线性回归

线性回归介绍(Linear Regressor):
    概述/目的:
        用线性公式 来描述 多个自变量(特征)  和 1个因变量(标签)之间 关系的, 对其关系进行建模, 基于特征 预测 标签.
        线性回归属于: 有监督学习, 即: 有特征, 有标签, 且标签是连续的.
    分类:
        一元线性回归:  1个特征列 + 1个标签列
        多元线性回归:  多个特征列 + 1个标签列
    公式:
        一元线性回归:
            y = kx + b => wx + b
                k: 数学中叫斜率, 在机器学习中Weight(权重), 简称: w
                b: 数学中叫截距, 在机器学习中Bias(偏置), 简称: b
        多元线性回归:
            y = w1x1 + w2x2 + w3x3 + ... + wnxn + b
              = w的转置 * x + b

线性回归API
实例化模型 estimator = LinearRegression()
训练模型 estimator.fit(x, y)
模型预测: estimator.predict([[1.0]])

    误差 = 预测值 - 真实值
    损失函数(Loss Function, 也叫成本函数, 代价函数, 目标函数, Cost Function):
        用于描述 每个样本点 和 其预测值之间关系的, 让损失函数最小, 就是让 误差和小, 线性回归效率越高, 评估越高.
    问题: 如何让损失函数最小?
    答案:
        思路1: 正规方程法.
                存在问题1:如果运算量过大,可能造成内存溢出;
                存在问题2:假设矩阵没有逆,则可能无解.
        思路2: 梯度下降法.
                下个点 = 当前点 - 学习率 * 损失函数的导数
    损失函数分类:
        最小二乘: 每个样本点误差的平方和
        MSE(Mean Square Error, 均方误差): 每个样本点误差的平方和 / 样本个数
        RMSE(Root Mean Square Error, 均方根误差): 均方误差 开平方根
        MAE(Mean Absolute Error, 均绝对误差): 每个样本点误差的绝对值和 / 样本个数


矩阵相关:
    1范数 = 向量中各元素 绝对值 之和. 
    2范数 = 向量的模长, 即: 各个元素平方总和, 开平方根
多元线性回归的正规方程

梯度概念
梯度的方向就是上升最快的方向
梯度:单变量函数就是导数;多变量函数就是偏导数

梯度下降公式:下个点 = 当前点 - 学习率 * 损失函数的导数

   学习率 俗称步长
   学习率太小学的慢;学习率太大,梯度下降中抖动、震荡、易错过最优解

梯度下降算法分类
全梯度下降:使用全部样本的梯度值
随机梯度下降:随机选择一个样本的梯度值更新权重
小批量梯度下降:随机选择小部分样本的梯度值更新权重
随机平均梯度下降:使用以前产生的梯度值来指导当前的梯度计算

线性回归模型评估 – MAE、MSE、RMSE三种指标对比
一般使用MAE 和 RMSE 这两个指标
MAE反应的是“真实”的平均误差,
RMSE会将误差大的数据点放大 MAE 不能体现出误差大的数据点,
RMSE放大大误差的数据点对指标的影响, 但是对异常数据比较敏感

都能反映出预测值和真实值之间的误差,MAE对误差大小不敏感,RMSE会放大预测误差较大的样本的影响,RMSE对异常数据敏感

正规方程
sklearn.linear_model.LinearRegression()
梯度下降
sklearn.linear_model.SGDRegressor()
梯线性回归性能评估
sklearn.metrics.mean_squared_error

欠拟合:在训练集上表现不好,在测试集上表现不好
解决方法,继续学习 1 添加其他特征项 2 添加多项式特征
过拟合:在训练集上表现好,在测试集上表现不好
解决方法 1 重新清洗数据集 2 增大数据的训练量 3 正则化 4 减少特征维度

正则化:异常点数据会造成权重系数过大、过小。尽量减少这些特征的影响(甚至删除某个特征的影响),这就是正则化。为了减少过拟合的影响,控制模型的参数。尤其是高次项的权重参数
目的/思路:
    都是基于 惩罚系数 来修改(特征列的)权重的, 惩罚系数越大, 则修改力度就越大, 对应的权重就越小.
区别:
    L1正则化, 可以实现让权重变为0, 从而达到 特征选择的目的.
    L2正则化, 只能让权重无限趋近于0, 但是不能为0.
    L1正则化:会直接把高次项前面的系数变为0,Lasso回归
    L2正则化:把高次项前面的系数变成特别小的值,岭回归

"""
线性回归介绍(Linear Regressor):
    概述/目的:
        用线性公式 来描述 多个自变量(特征)  和 1个因变量(标签)之间 关系的, 对其关系进行建模, 基于特征 预测 标签.
        线性回归属于: 有监督学习, 即: 有特征, 有标签, 且标签是连续的.
    分类:
        一元线性回归:  1个特征列 + 1个标签列
        多元线性回归:  多个特征列 + 1个标签列
    公式:
        一元线性回归:
            y = kx + b => wx + b
                k: 数学中叫斜率, 在机器学习中Weight(权重), 简称: w
                b: 数学中叫截距, 在机器学习中Bias(偏置), 简称: b
        多元线性回归:
            y = w1x1 + w2x2 + w3x3 + ... + wnxn + b
              = w的转置 * x + b

    误差 = 预测值 - 真实值
    损失函数(Loss Function, 也叫成本函数, 代价函数, 目标函数, Cost Function):
        用于描述 每个样本点 和 其预测值之间关系的, 让损失函数最小, 就是让 误差和小, 线性回归效率, 评估就越高.
    问题: 如何让损失函数最小?
    答案:
        思路1: 正规方程法.
        思路2: 梯度下降法.

    损失函数分类:
        最小二乘: 每个样本点误差的平方和
        MSE(Mean Square Error, 均方误差): 每个样本点误差的平方和 / 样本个数
        RMSE(Root Mean Square Error, 均方根误差): 均方误差 开平方根
        MAE(Mean Absolute Error, 均绝对误差): 每个样本点误差的绝对值和 / 样本个数

矩阵相关:
    1范数 = 向量中各元素 绝对值 之和.
    2范数 = 向量的模长, 即: 各个元素平方总和, 开平方根
"""
# 导包
from sklearn.linear_model import LinearRegression

# 案例: 演示线性回归API入门.

# 1. 准备数据.
x_train = [[160], [166], [172], [174], [180]]       # 训练集的特征
y_train = [56.3, 60.6, 65.1, 68.5, 75]              # 训练集的标签
x_test = [[176]]                                    # 测试集的特征

# 2. 数据的预处理, 这里不需要.
# 3. 特征工程(特征提取, 特征预处理), 这里不需要.

# 4. 模型训练
# 4.1 创建模型对象.
estimator = LinearRegression()
# 4.2 具体的训练动作.
estimator.fit(x_train, y_train)
# 4.3 因为是线性回归模型, 我们可以查看下: 斜率(w, 权重), 截距(b, 偏置)
print(f'权重: {estimator.coef_}')         # 0.92942177
print(f'偏置: {estimator.intercept_}')    # -93.27346938775514

# 5. 模型预测.
y_pre = estimator.predict(x_test)
print(f'预测值为: {y_pre}')               # 70.3047619

# 6. 模型评估.

线性回归相关概念图解

1.损失函数

2.导数复习

3.矩阵复习

4.一元线性回归-正规方程法

一元线性回归-正规方程法总结:

将预测值 (wx+b) 与实际值 y 代入损失函数(最小二乘)内,分别对w(权重)和b(偏置)求偏导,利用偏导等于0的特性,代入所有的训练数据, 求出损失函数的 最小值时 关于w和b的二元一次表达式,然后求出 w 和 b 的值.

5.多元线性回归-正规方程法

6.多元线性回归-正规方程法(带入参)

多元线性回归-正规方程法总结:

将预测值 (wx+b) 与实际值 y 代入损失函数(最小二乘)内,

因为是多元线性回归,每一列特征都有其权重值 w ,

一行样本预测值表示为:w的转置*x+b

多行样本预测值表示为:Xw (矩阵乘法前提是A列=B行,w不可转置为1列,否则无法开展计算)

最终针对 Xw-y 的二范数求对 w 的导数,

利用导数等于 0 的特性,变换公式求出结论:

其中 X 是特征列矩阵,代入特征列矩阵的数据,求出权重 w 的值.

7.单变量梯度下降

8.多变量梯度下降

9.多变量梯度下降推导

房价预测之正规方程法

案例:
    演示 正规方程法 线性回归对象 完成 波士顿房价预测案例.

回顾:
    线性回归算法 属于 有监督学习之  有特征, 有标签, 且标签是连续的.
    线性回归分类:
        一元线性回归: 1个特征列, 1个标签列.
        多元线性回归: 多个特征列, 1个标签列.
    线性回归大白话解释:
       它是用线性公式来描述 特征 和 标签之间关系的, 方便做预测, 公式如下:
       一元线性回归: y = w * x + b
       多元线性回归: y = w1 * x1 + w2 * x2 + w3 * x3 + ... + wn * xn + b = w的转置 * x + b
    如何衡量线性回归模型的好坏?
        思路:
            预测值和真实值之间的误差, 误差越小, 模型越好 => 损失函数
        具体的方案:
            1. 最小二乘.        每个(样本)误差平方和
            2. 均方误差(MSE)    每个(样本)误差平方和 / 样本总数
            3. 均方根误差(RMSE)  每个(样本)误差平方和 / 样本总数 的 平方根
            4. 平均绝对误差(MAE) 每个(样本)误差绝对值和 / 样本总数
    如何让损失函数最小?
        思路1: 梯度下降法.     =>   全梯度下降(Full Gradient Descent, FGD), 随机梯度下降(SGD), 小批量梯度下降(推荐, Min-Batch), 随机平均梯度下降(SAG)
        思路2: 正规方程法.

    机器学习开发流程:
        1. 加载数据.
        2. 数据的预处理.
        3. 特征工程(特征提取, 特征预处理...)
        4. 模型训练.
        5. 模型预测.
        6. 模型评估
"""
案例:
    演示 正规方程法 线性回归对象 完成 波士顿房价预测案例.

回顾:
    线性回归算法 属于 有监督学习之  有特征, 有标签, 且标签是连续的.
    线性回归分类:
        一元线性回归: 1个特征列, 1个标签列.
        多元线性回归: 多个特征列, 1个标签列.
    线性回归大白话解释:
       它是用线性公式来描述 特征 和 标签之间关系的, 方便做预测, 公式如下:
       一元线性回归: y = w * x + b
       多元线性回归: y = w1 * x1 + w2 * x2 + w3 * x3 + ... + wn * xn + b = w的转置 * x + b
    如何衡量线性回归模型的好坏?
        思路:
            预测值和真实值之间的误差, 误差越小, 模型越好 => 损失函数
        具体的方案:
            1. 最小二乘.        每个(样本)误差平方和
            2. 均方误差(MSE)    每个(样本)误差平方和 / 样本总数
            3. 均方根误差(RMSE)  每个(样本)误差平方和 / 样本总数 的 平方根
            4. 平均绝对误差(MAE) 每个(样本)误差绝对值和 / 样本总数
    如何让损失函数最小?
        思路1: 梯度下降法.     =>   全梯度下降(Full Gradient Descent, FGD), 随机梯度下降(SGD), 小批量梯度下降(推荐, Min-Batch), 随机平均梯度下降(SAG)
        思路2: 正规方程法.

    机器学习开发流程:
        1. 加载数据.
        2. 数据的预处理.
        3. 特征工程(特征提取, 特征预处理...)
        4. 模型训练.
        5. 模型预测.
        6. 模型评估
"""

# 导包
# from sklearn.datasets import load_boston                # 数据
from sklearn.preprocessing import StandardScaler        # 特征处理
from sklearn.model_selection import train_test_split    # 数据集划分
from sklearn.linear_model import LinearRegression       # 正规方程的回归模型
from sklearn.linear_model import SGDRegressor           # 梯度下降的回归模型
from sklearn.metrics import mean_squared_error, root_mean_squared_error, mean_absolute_error  # 均方误差评估, RMSE, MAE
from sklearn.linear_model import Ridge, RidgeCV

import pandas as pd
import numpy as np

# 1. 加载 波士顿房价数据.
data_url = "http://lib.stat.cmu.edu/datasets/boston"
raw_df = pd.read_csv(data_url, sep="\\s+", skiprows=22, header=None)
data = np.hstack([raw_df.values[::2, :], raw_df.values[1::2, :2]])  # hstack()函数作用: 水平拼接数组
target = raw_df.values[1::2, 2]
# print(f'特征: {data.shape}')      # (506, 13)
# print(f'标签: {target.shape}')    # (506,)
#
# print(f'特征数据: {data[:5]}')
# print(f'标签数据: {target[:5]}')

# 2. 数据的预处理.  切分训练集 和 测试集.
# 参1: 特征数据. 参2: 标签数据. 参3: 测试集占训练集的比例. 参4: 随机种子.
x_train, x_test, y_train, y_test = train_test_split(data, target, test_size=0.2, random_state=23)

# 3. 特征工程(特征提取, 特征预处理...)
# 3.1 创建标准化对象.
transfer = StandardScaler()
# 3.2 对训练集进行标准化.
x_train = transfer.fit_transform(x_train)
x_test = transfer.transform(x_test)

# 4. 模型训练.
# 4.1 创建 线性回归 正规方程 模型对象.
estimator = LinearRegression(fit_intercept=True)    # fit_intercept: 是否需要截距(Bias, 偏置), 默认是True
# 4.2 模型训练.
estimator.fit(x_train, y_train)
# 4.3 打印模型计算出的 w(权重, weight) 和 b(偏置, bias).
print(f'权重: {estimator.coef_}')
print(f'偏置: {estimator.intercept_}')

# 5. 模型预测.
y_pre = estimator.predict(x_test)
print(f'预测结果为: {y_pre}')

# 6. 模型评估
# 参1: 测试集的标签数据. 参2: 预测结果.
print(f'均方误差为: {mean_squared_error(y_test, y_pre)}')            # MSE: 均方误差, 公式: 每个样本的误差平方和 / 样本总数
print(f'均方根误差为: {root_mean_squared_error(y_test, y_pre)}')     # RMSE: 均方根误差, 公式: 每个样本的误差平方和 / 样本总数, 开平方根
print(f'平均绝对误差为: {mean_absolute_error(y_test, y_pre)}')        # MAE: 平均绝对误差, 公式: 每个样本的误差 绝对值和 / 样本总数

房价预测之随机梯度下降

案例:
    演示 随机梯度下降法 线性回归对象 完成 波士顿房价预测案例.

回顾:
    线性回归算法 属于 有监督学习之  有特征, 有标签, 且标签是连续的.
    线性回归分类:
        一元线性回归: 1个特征列, 1个标签列.
        多元线性回归: 多个特征列, 1个标签列.
    线性回归大白话解释:
       它是用线性公式来描述 特征 和 标签之间关系的, 方便做预测, 公式如下:
       一元线性回归: y = w * x + b
       多元线性回归: y = w1 * x1 + w2 * x2 + w3 * x3 + ... + wn * xn + b = w的转置 * x + b
    如何衡量线性回归模型的好坏?
        思路:
            预测值和真实值之间的误差, 误差越小, 模型越好 => 损失函数
        具体的方案:
            1. 最小二乘.        每个(样本)误差平方和
            2. 均方误差(MSE)    每个(样本)误差平方和 / 样本总数
            3. 均方根误差(RMSE)  每个(样本)误差平方和 / 样本总数 的 平方根
            4. 平均绝对误差(MAE) 每个(样本)误差绝对值和 / 样本总数
    如何让损失函数最小?
        思路1: 梯度下降法.     =>   全梯度下降(Full Gradient Descent, FGD), 随机梯度下降(SGD), 小批量梯度下降(推荐, Min-Batch), 随机平均梯度下降(SAG)
        思路2: 正规方程法.

    机器学习开发流程:
        1. 加载数据.
        2. 数据的预处理.
        3. 特征工程(特征提取, 特征预处理...)
        4. 模型训练.
        5. 模型预测.
        6. 模型评估
"""
案例:
    演示 随机梯度下降法 线性回归对象 完成 波士顿房价预测案例.

回顾:
    线性回归算法 属于 有监督学习之  有特征, 有标签, 且标签是连续的.
    线性回归分类:
        一元线性回归: 1个特征列, 1个标签列.
        多元线性回归: 多个特征列, 1个标签列.
    线性回归大白话解释:
       它是用线性公式来描述 特征 和 标签之间关系的, 方便做预测, 公式如下:
       一元线性回归: y = w * x + b
       多元线性回归: y = w1 * x1 + w2 * x2 + w3 * x3 + ... + wn * xn + b = w的转置 * x + b
    如何衡量线性回归模型的好坏?
        思路:
            预测值和真实值之间的误差, 误差越小, 模型越好 => 损失函数
        具体的方案:
            1. 最小二乘.        每个(样本)误差平方和
            2. 均方误差(MSE)    每个(样本)误差平方和 / 样本总数
            3. 均方根误差(RMSE)  每个(样本)误差平方和 / 样本总数 的 平方根
            4. 平均绝对误差(MAE) 每个(样本)误差绝对值和 / 样本总数
    如何让损失函数最小?
        思路1: 梯度下降法.     =>   全梯度下降(Full Gradient Descent, FGD), 随机梯度下降(SGD), 小批量梯度下降(推荐, Min-Batch), 随机平均梯度下降(SAG)
        思路2: 正规方程法.

    机器学习开发流程:
        1. 加载数据.
        2. 数据的预处理.
        3. 特征工程(特征提取, 特征预处理...)
        4. 模型训练.
        5. 模型预测.
        6. 模型评估
"""

# 导包
from sklearn.preprocessing import StandardScaler        # 特征处理
from sklearn.model_selection import train_test_split    # 数据集划分
from sklearn.linear_model import LinearRegression       # 正规方程的回归模型
from sklearn.linear_model import SGDRegressor           # 梯度下降的回归模型
from sklearn.metrics import mean_squared_error, root_mean_squared_error, mean_absolute_error  # 均方误差评估, RMSE, MAE
from sklearn.linear_model import Ridge, RidgeCV

import pandas as pd
import numpy as np

# 1. 加载 波士顿房价数据.
data_url = "http://lib.stat.cmu.edu/datasets/boston"
raw_df = pd.read_csv(data_url, sep="\\s+", skiprows=22, header=None)
data = np.hstack([raw_df.values[::2, :], raw_df.values[1::2, :2]])  # hstack()函数作用: 水平拼接数组
target = raw_df.values[1::2, 2]
# print(f'特征: {data.shape}')      # (506, 13)
# print(f'标签: {target.shape}')    # (506,)
#
# print(f'特征数据: {data[:5]}')
# print(f'标签数据: {target[:5]}')

# 2. 数据的预处理.  按照8:2 切分 训练集和测试集.
x_train, x_test, y_train, y_test = train_test_split(data, target, test_size=0.2, random_state=23)

# 3. 特征工程(特征提取, 特征预处理...)
# 3.1 创建 标准化对象.
transfer = StandardScaler()
# 3.2 对训练集和测试集进行标准化处理.
x_train = transfer.fit_transform(x_train)
x_test = transfer.transform(x_test)

# 4. 模型训练.
# estimator = LinearRegression(fit_intercept=True)    # 正规方程法 线性回归对象.

# 4.1 创建 梯度下降 线性回归 模型对象.
# 参1: fit_intercept: 是否计算截距.
# 参2: learning_rate: 学习率模式 -> 常量, 即: 不会发生改变.
# 参3: eta0: 学习率.
estimator = SGDRegressor(fit_intercept=True, learning_rate='constant', eta0=0.01)

# 4.2 模型训练.
estimator.fit(x_train, y_train)
# 4.3 打印 权重 和 偏置.
print(f'权重: {estimator.coef_}')
print(f'偏置: {estimator.intercept_}')

# 5. 模型预测.
y_pre = estimator.predict(x_test)

# 6. 模型评估
# MSE: 均方误差, 每个误差的平方和 / 样本总数
print(f'均方误差: {mean_squared_error(y_test, y_pre)}')          # 参1: 测试集的真实标签, 参2: 测试集的预测标签

# RMSE: 均方根误差, 均方误差的平方根
print(f'均方根误差: {root_mean_squared_error(y_test, y_pre)}')   # 参1: 测试集的真实标签, 参2: 测试集的预测标签

# MAE: 平均绝对误差, 每个误差绝对值和 / 样本总数
print(f'平均绝对误差: {mean_absolute_error(y_test, y_pre)}')     # 参1: 测试集的真实标签, 参2: 测试集的预测标签

L1和L2正则化解决过拟合问题

案例:
    演示 欠拟合, 正好拟合, 过拟合, L1正则化, L2正则化的 效果图.

回顾:
    欠拟合:    模型在训练集 和 测试集表现效果都不好.
    正好拟合:   模型在训练集 和 测试集表现效果都好.
    过拟合:    模型在训练集表现好, 测试集表现不好.

过拟合, 欠拟合解释:
    产生原因:
        欠拟合: 模型简单.
        过拟合: 模型复杂.
    解决方案:
        欠拟合: 增加特征, 从而增加 模型的复杂度.
        过拟合: 减少模型复杂度, 手动筛选(减少)特征, L1和L2正则化.

L1和L2正则化介绍:
    目的/思路:
        都是基于 惩罚系数 来修改(特征列的)权重的, 惩罚系数越大, 则修改力度就越大, 对应的权重就越小.
    区别:
        L1正则化, 可以实现让权重变为0, 从而达到 特征选择的目的.
        L2正则化, 只能让权重无限趋近于0, 但是不能为0.
    大白话:
        我要去爬山, 带了个小包, 装了: 登山杖, 水, 面包, 衣服, 雨伞, 鞋子...  发现包装不下了.
        L1正则化: 可以实现去掉一些不是必选的, 例如: 当天去, 当前回, 且天气晴朗 -> 不带雨伞, 鞋子, 即: 权重为0
        L2正则化: 换一个非常非常大的包, 还是那些物品, 但是空间占用(权重)就变小了...
"""
案例:
    演示 欠拟合, 正好拟合, 过拟合, L1正则化, L2正则化的 效果图.

回顾:
    欠拟合:    模型在训练集 和 测试集表现效果都不好.
    正好拟合:   模型在训练集 和 测试集表现效果都好.
    过拟合:    模型在训练集表现好, 测试集表现不好.

过拟合, 欠拟合解释:
    产生原因:
        欠拟合: 模型简单.
        过拟合: 模型复杂.
    解决方案:
        欠拟合: 增加特征, 从而增加 模型的复杂度.
        过拟合: 减少模型复杂度, 手动筛选(减少)特征, L1和L2正则化.

L1和L2正则化介绍:
    目的/思路:
        都是基于 惩罚系数 来修改(特征列的)权重的, 惩罚系数越大, 则修改力度就越大, 对应的权重就越小.
    区别:
        L1正则化, 可以实现让权重变为0, 从而达到 特征选择的目的.
        L2正则化, 只能让权重无限趋近于0, 但是不能为0.
    大白话:
        我要去爬山, 带了个小包, 装了: 登山杖, 水, 面包, 衣服, 雨伞, 鞋子...  发现包装不下了.
        L1正则化: 可以实现去掉一些不是必选的, 例如: 当天去, 当前回, 且天气晴朗 -> 不带雨伞, 鞋子, 即: 权重为0
        L2正则化: 换一个非常非常大的包, 还是那些物品, 但是空间占用(权重)就变小了...
"""

# 导包
import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression       # 正规方程的回归模型
from sklearn.metrics import mean_squared_error, root_mean_squared_error, mean_absolute_error  # 均方误差评估, RMSE, MAE
from sklearn.linear_model import Lasso, Ridge           # L1正则化, L2正则化


# 1. 定义函数, 模拟: 欠拟合.
def dm01_under_fitting():
    # 1. 准备数据.
    # 1.1 指定随机种子, 则每次生成(噪声)的数据都是固定的.
    np.random.seed(23)
    # 1.2 随机生成x轴 100个数据, 模拟: 特征.
    x = np.random.uniform(-3, 3, 100)   # 参1: 最小值, 参2: 最大值, 参3: 生成个数
    # 1.3 基于x轴值, 通过线性公式, 生成y轴 100个数据, 模拟: 标签.
    # 线性公式: y = kx + b = 0.5 * x ** 2 + x + 2 + 噪声
    y = 0.5 * x ** 2 + x + 2 + np.random.normal(0, 1, 100)  # 参1: 平均值, 参2: 标准差, 参3: 生成个数
    # 1.4 查看生成的 x轴(特征) 和 y轴(标签)的数据.
    print(f'特征(x): {x[:5]}')        # [1, 2, 3, 4, 5]
    print(f'标签(y): {y[:5]}')

    # 2. 数据预处理, 把x轴(特征)转成 多行1列的形式.
    X = x.reshape(-1, 1)
    print(f'处理后的特征: {X[:5]}')    # [[1], [2], [3], [4], [5]]

    # 3. 特征工程, 这里不做了, 直接用100条数据, 先训练, 后预测.

    # 4. 模型训练.
    # 4.1 创建模型对象.
    estimator = LinearRegression()      # 正规方程 线性回归模型.
    # 4.2 模型训练.
    estimator.fit(X, y)                 # 参1: 处理后的特征数据. 参2: 标签数据.

    # 5. 模型预测.
    y_predict = estimator.predict(X)    # 处理后的特征数据

    # 6. 模型评估.
    print(f'均方误差: {mean_squared_error(y, y_predict)}')      # 参1: 真实值, 参2: 预测值.

    # 7. 绘图.
    plt.scatter(x, y)                           # 以散点图的形式绘制 真实值.
    plt.plot(x, y_predict, color='red')   # 以线图的形式绘制 预测值.
    plt.show()

# 2. 定义函数, 模拟: 正好拟合.
def dm02_just_fitting():
    # 1. 准备数据.
    # 1.1 指定随机种子, 则每次生成(噪声)的数据都是固定的.
    np.random.seed(23)
    # 1.2 随机生成x轴 100个数据, 模拟: 特征.
    x = np.random.uniform(-3, 3, 100)  # 参1: 最小值, 参2: 最大值, 参3: 生成个数
    # 1.3 基于x轴值, 通过线性公式, 生成y轴 100个数据, 模拟: 标签.
    # 线性公式: y = kx + b = 0.5 * x ** 2 + x + 2 + 噪声
    y = 0.5 * x ** 2 + x + 2 + np.random.normal(0, 1, 100)  # 参1: 平均值, 参2: 标准差, 参3: 生成个数
    # 1.4 查看生成的 x轴(特征) 和 y轴(标签)的数据.
    print(f'特征(x): {x[:5]}')  # [1, 2, 3, 4, 5]
    print(f'标签(y): {y[:5]}')

    # 2. 数据预处理, 把x轴(特征)转成 多行1列的形式.
    # 2.1 把上述的x轴(特征)转成 多行1列的形式, 即: [[1], [2], [3], [4], [5]]
    X = x.reshape(-1, 1)
    print(f'处理后的特征: {X[:5]}')  # [[1], [2], [3], [4], [5]]

    # 2.2 因为目前特征列只有1列, 模型过于简单, 会出现欠拟合的问题, 我们增加1列 特征列, 从而增加模型的复杂度.
    # 即: 把数据从  [[1], [2], [3], [4], [5]] =>  [[1, 1], [2, 4], [3, 9], [4, 16], [5, 25]]
    X2 = np.hstack([X, X ** 2])  # 该函数作用: 横向拼接, 即: 拼接2个数组, 拼接后数组的行数不变, 拼接后数组的列数等于拼接前数组的列数之和.
    print(f'处理后的特征: {X2[:5]}')

    # 3. 特征工程, 这里不做了, 直接用100条数据, 先训练, 后预测.

    # 4. 模型训练.
    # 4.1 创建模型对象.
    estimator = LinearRegression()  # 正规方程 线性回归模型.
    # 4.2 模型训练.
    estimator.fit(X2, y)  # 参1: 处理后的特征数据. 参2: 标签数据.

    # 5. 模型预测.
    y_predict = estimator.predict(X2)  # 处理后的特征数据

    # 6. 模型评估.
    print(f'均方误差: {mean_squared_error(y, y_predict)}')  # 参1: 真实值, 参2: 预测值.

    # 7. 绘图.
    plt.scatter(x, y)  # 以散点图的形式绘制 真实值.
    # np.sort(x): 对x轴(特征)排序, 默认是: 升序.
    # np.argsort(x): 对x轴(特征)排序, 返回排序后的索引.
    # 例如:   排序前x轴是 [11, 33, 22] -> 对应索引: [0, 1, 2]
    # 排序后: x轴是      [11, 22, 33] -> 对应索引: [0, 2, 1]
    plt.plot(np.sort(x), y_predict[np.argsort(x)], color='red')  # 以线图的形式绘制 预测值.
    plt.show()


# 3. 定义函数, 模拟: 过拟合.
def dm03_over_fitting():
    # 1. 准备数据.
    # 1.1 指定随机种子, 则每次生成(噪声)的数据都是固定的.
    np.random.seed(23)
    # 1.2 随机生成x轴 100个数据, 模拟: 特征.
    x = np.random.uniform(-3, 3, 100)  # 参1: 最小值, 参2: 最大值, 参3: 生成个数
    # 1.3 基于x轴值, 通过线性公式, 生成y轴 100个数据, 模拟: 标签.
    # 线性公式: y = kx + b = 0.5 * x ** 2 + x + 2 + 噪声
    y = 0.5 * x ** 2 + x + 2 + np.random.normal(0, 1, 100)  # 参1: 平均值, 参2: 标准差, 参3: 生成个数
    # 1.4 查看生成的 x轴(特征) 和 y轴(标签)的数据.
    print(f'特征(x): {x[:5]}')  # [1, 2, 3, 4, 5]
    print(f'标签(y): {y[:5]}')

    # 2. 数据预处理, 把x轴(特征)转成 多行1列的形式.
    # 2.1 把上述的x轴(特征)转成 多行1列的形式, 即: [[1], [2], [3], [4], [5]]
    X = x.reshape(-1, 1)
    print(f'处理后的特征: {X[:5]}')  # [[1], [2], [3], [4], [5]]

    # 2.2 因为目前特征列只有1列, 模型过于简单, 为了模拟过拟合, 我们增加9列 特征列, 从而增加模型的复杂度.
    # 即: 把数据从  [[1], [2], [3], [4], [5]] =>  [[1, 1**2, 1**3, 1**4, 1**5...], [2, 2**2, 2**3, 2**4, 2**5], [3...]...]
    # 该函数作用: 横向拼接, 即: 拼接多个数组, 拼接后数组的行数不变, 拼接后数组的列数等于拼接前数组的列数之和.
    X3 = np.hstack([X, X ** 2, X ** 3, X ** 4, X ** 5, X ** 6, X ** 7, X ** 8, X ** 9, X ** 10])
    print(f'处理后的特征: {X3[:5]}')

    # 3. 特征工程, 这里不做了, 直接用100条数据, 先训练, 后预测.

    # 4. 模型训练.
    # 4.1 创建模型对象.
    estimator = LinearRegression()  # 正规方程 线性回归模型.
    # 4.2 模型训练.
    estimator.fit(X3, y)  # 参1: 处理后的特征数据. 参2: 标签数据.

    # 5. 模型预测.
    y_predict = estimator.predict(X3)  # 处理后的特征数据

    # 6. 模型评估.
    print(f'均方误差: {mean_squared_error(y, y_predict)}')  # 参1: 真实值, 参2: 预测值.

    # 7. 绘图.
    plt.scatter(x, y)  # 以散点图的形式绘制 真实值.
    # np.sort(x): 对x轴(特征)排序, 默认是: 升序.
    # np.argsort(x): 对x轴(特征)排序, 返回排序后的索引.
    # 例如:   排序前x轴是 [11, 33, 22] -> 对应索引: [0, 1, 2]
    # 排序后: x轴是      [11, 22, 33] -> 对应索引: [0, 2, 1]
    plt.plot(np.sort(x), y_predict[np.argsort(x)], color='red')  # 以线图的形式绘制 预测值.
    plt.show()

# 4. 定义函数, 模拟: L1正则化.
def dm04_l1_regularization():
    # 1. 准备数据.
    # 1.1 指定随机种子, 则每次生成(噪声)的数据都是固定的.
    np.random.seed(23)
    # 1.2 随机生成x轴 100个数据, 模拟: 特征.
    x = np.random.uniform(-3, 3, 100)  # 参1: 最小值, 参2: 最大值, 参3: 生成个数
    # 1.3 基于x轴值, 通过线性公式, 生成y轴 100个数据, 模拟: 标签.
    # 线性公式: y = kx + b = 0.5 * x ** 2 + x + 2 + 噪声
    y = 0.5 * x ** 2 + x + 2 + np.random.normal(0, 1, 100)  # 参1: 平均值, 参2: 标准差, 参3: 生成个数
    # 1.4 查看生成的 x轴(特征) 和 y轴(标签)的数据.
    print(f'特征(x): {x[:5]}')  # [1, 2, 3, 4, 5]
    print(f'标签(y): {y[:5]}')

    # 2. 数据预处理, 把x轴(特征)转成 多行1列的形式.
    # 2.1 把上述的x轴(特征)转成 多行1列的形式, 即: [[1], [2], [3], [4], [5]]
    X = x.reshape(-1, 1)
    print(f'处理后的特征: {X[:5]}')  # [[1], [2], [3], [4], [5]]

    # 2.2 因为目前特征列只有1列, 模型过于简单, 为了模拟过拟合, 我们增加9列 特征列, 从而增加模型的复杂度.
    # 即: 把数据从  [[1], [2], [3], [4], [5]] =>  [[1, 1**2, 1**3, 1**4, 1**5...], [2, 2**2, 2**3, 2**4, 2**5], [3...]...]
    # 该函数作用: 横向拼接, 即: 拼接多个数组, 拼接后数组的行数不变, 拼接后数组的列数等于拼接前数组的列数之和.
    X3 = np.hstack([X, X ** 2, X ** 3, X ** 4, X ** 5, X ** 6, X ** 7, X ** 8, X ** 9, X ** 10])
    print(f'处理后的特征: {X3[:5]}')

    # 3. 特征工程, 这里不做了, 直接用100条数据, 先训练, 后预测.

    # 4. 模型训练.
    # 4.1 创建模型对象.
    # estimator = LinearRegression()  # 正规方程 线性回归模型.
    # 改为创建 L1正则化对象.
    estimator = Lasso(alpha=0.1)    # alpha: 正则化系数(惩罚系数), 默认是: 1.

    # 4.2 模型训练.
    estimator.fit(X3, y)  # 参1: 处理后的特征数据. 参2: 标签数据.

    # 5. 模型预测.
    y_predict = estimator.predict(X3)  # 处理后的特征数据

    # 6. 模型评估.
    print(f'均方误差: {mean_squared_error(y, y_predict)}')  # 参1: 真实值, 参2: 预测值.

    # 7. 绘图.
    plt.scatter(x, y)  # 以散点图的形式绘制 真实值.
    # np.sort(x): 对x轴(特征)排序, 默认是: 升序.
    # np.argsort(x): 对x轴(特征)排序, 返回排序后的索引.
    # 例如:   排序前x轴是 [11, 33, 22] -> 对应索引: [0, 1, 2]
    # 排序后: x轴是      [11, 22, 33] -> 对应索引: [0, 2, 1]
    plt.plot(np.sort(x), y_predict[np.argsort(x)], color='red')  # 以线图的形式绘制 预测值.
    plt.show()

# 5. 定义函数, 模拟: L2正则化.
def dm05_l2_regularization():
    # 1. 准备数据.
    # 1.1 指定随机种子, 则每次生成(噪声)的数据都是固定的.
    np.random.seed(23)
    # 1.2 随机生成x轴 100个数据, 模拟: 特征.
    x = np.random.uniform(-3, 3, 100)  # 参1: 最小值, 参2: 最大值, 参3: 生成个数
    # 1.3 基于x轴值, 通过线性公式, 生成y轴 100个数据, 模拟: 标签.
    # 线性公式: y = kx + b = 0.5 * x ** 2 + x + 2 + 噪声
    y = 0.5 * x ** 2 + x + 2 + np.random.normal(0, 1, 100)  # 参1: 平均值, 参2: 标准差, 参3: 生成个数
    # 1.4 查看生成的 x轴(特征) 和 y轴(标签)的数据.
    print(f'特征(x): {x[:5]}')  # [1, 2, 3, 4, 5]
    print(f'标签(y): {y[:5]}')

    # 2. 数据预处理, 把x轴(特征)转成 多行1列的形式.
    # 2.1 把上述的x轴(特征)转成 多行1列的形式, 即: [[1], [2], [3], [4], [5]]
    X = x.reshape(-1, 1)
    print(f'处理后的特征: {X[:5]}')  # [[1], [2], [3], [4], [5]]

    # 2.2 因为目前特征列只有1列, 模型过于简单, 为了模拟过拟合, 我们增加9列 特征列, 从而增加模型的复杂度.
    # 即: 把数据从  [[1], [2], [3], [4], [5]] =>  [[1, 1**2, 1**3, 1**4, 1**5...], [2, 2**2, 2**3, 2**4, 2**5], [3...]...]
    # 该函数作用: 横向拼接, 即: 拼接多个数组, 拼接后数组的行数不变, 拼接后数组的列数等于拼接前数组的列数之和.
    X3 = np.hstack([X, X ** 2, X ** 3, X ** 4, X ** 5, X ** 6, X ** 7, X ** 8, X ** 9, X ** 10])
    print(f'处理后的特征: {X3[:5]}')

    # 3. 特征工程, 这里不做了, 直接用100条数据, 先训练, 后预测.

    # 4. 模型训练.
    # 4.1 创建模型对象.
    # estimator = LinearRegression()  # 正规方程 线性回归模型.

    # 改为创建 L1正则化对象.
    # estimator = Lasso(alpha=0.1)    # alpha: 正则化系数(惩罚系数), 默认是: 1.

    # 改为创建 L2正则化对象.
    estimator = Ridge(alpha=10)

    # 4.2 模型训练.
    estimator.fit(X3, y)  # 参1: 处理后的特征数据. 参2: 标签数据.

    # 5. 模型预测.
    y_predict = estimator.predict(X3)  # 处理后的特征数据

    # 6. 模型评估.
    print(f'均方误差: {mean_squared_error(y, y_predict)}')  # 参1: 真实值, 参2: 预测值.

    # 7. 绘图.
    plt.scatter(x, y)  # 以散点图的形式绘制 真实值.
    # np.sort(x): 对x轴(特征)排序, 默认是: 升序.
    # np.argsort(x): 对x轴(特征)排序, 返回排序后的索引.
    # 例如:   排序前x轴是 [11, 33, 22] -> 对应索引: [0, 1, 2]
    # 排序后: x轴是      [11, 22, 33] -> 对应索引: [0, 2, 1]
    plt.plot(np.sort(x), y_predict[np.argsort(x)], color='red')  # 以线图的形式绘制 预测值.
    plt.show()


# 6. 测试
if __name__ == '__main__':
    # dm01_under_fitting()
    # dm02_just_fitting()
    # dm03_over_fitting()
    # dm04_l1_regularization()
    dm05_l2_regularization()

更多推荐