19-2. 梯度下降算法(程序实现)

一维梯度下降(with codes)

程序:使用梯度下降求解方程 y = x 2 2 x + 1 y=x^{2} - 2x + 1 的最小值。
观察学习率对梯度下降的影响。学习率不是越大越好,也不是越小越好。

import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
mpl.rcParams["font.family"] = "SimHei"
mpl.rcParams["axes.unicode_minus"]=False

# 定义原函数方程。
def f(x):
    return x ** 2 - 2 * x + 1

# 定义梯度方程(导函数方程)
def gradient(x):
    return 2 * x - 2

# 定义初始值,即梯度下降最开始的迭代点。
x = 10
# 保存每次调整后,x的值以及x所对应的y值。即调整过程中,x与y的移动轨迹。
x_list = []
y_list = []
# 定义学习率(每次进行调整的幅度系数)
# 关于学习率的设置:学习率要设置得当,既不是越大越好,也不是越小越好。
# 如果学习率设置过小,则每次更新的非常缓慢,需要迭代很多次才能到极值附近。
# 如果学习率设置过大,则会出现震荡发散的情况,导致跳过最优解!
eta = 0.1

# 进行迭代
for i in range(30):
    # 将每次调整的值加入到列表中。
    x_list.append(x)
    y_list.append(f(x))
    # 根据梯度值来调整x,使得调整的x,能够令f(x)的值更小。
    x -= eta * gradient(x)
# 输出更新的值(更新的轨迹)
print(y_list)
print(x_list)

在这里插入图片描述

注意:

  • 初始值是随机的
  • 损失函数公式:
    w = w - eta * 导函数 ,在代码中,w就是x,j(w)就是y
  • 脑海中回想回想损失函数的图,下图:
    自然得出结论:梯度下降过程汇总,梯度值是越来越小的
  • 看到代码输出结果:
  1. y_list里面的值是越来越小,x_list里面的值也是越来越小
  2. 下降的幅度越来越慢,也就是w的更新幅度越来越慢,因为梯度值是变小的,因为梯度值就是tan,tan越来越小,回想图,
    在这里插入图片描述
  • 原函数 y = x 2 2 x + 1 y=x^{2}-2x+1 ,开口向上的二次函数,极小值点取(1,0)
    观察结果,y值趋近于0,x值趋近于1
  • 所以梯度下降算法是可以求极小值的,不仅仅是之前的最小二乘法可以求极小值
  • 注意是沿着梯度的反方向越来越小
  • 梯度不是也是幅度吗?why要加上eta,因为梯度作为幅度太单一了,加上eta作为辅助的幅度

迭代次数增加:
在这里插入图片描述

%matplotlib qt
x = np.linspace(-9, 11, 200)
y = x ** 2 - 2 * x + 1
plt.plot(x, y)
# title中也支持Latex公式。
plt.title("函数$y=x^{2}-2x+1$的图像")
plt.plot(x_list, y_list, "ro--")
plt.show()

在这里插入图片描述

  • 如果将学习率调小,下降的步幅很慢很慢
    在这里插入图片描述

  • 将学习率调的话一定好吗?再打就有问题
    看值的话也是一正一副
    在这里插入图片描述
    在这里插入图片描述

  • x一正一副,且绝对值越来越大;如下图,连图像都没有了
    在这里插入图片描述

二维梯度下降(with codes)

程序:使用梯度下降求解方程 y = 0.2 ( x 1 + x 2 ) 2 0.3 x 1 x 2 + 0.4 y = 0.2(x1 + x2) ^ {2} - 0.3x1x2 + 0.4 的最小值。

import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
%matplotlib qt

mpl.rcParams["font.family"] = "SimHei"
mpl.rcParams["axes.unicode_minus"]=False

# 定义原函数
def f(x1, x2):
    return 0.2 * (x1 + x2) ** 2 - 0.3 * x1 * x2 + 0.4

# 针对x1求梯度值。
def gradient_x1(x1, x2):
    return 0.4 * (x1 + x2) - 0.3 * x2

# 针对x2求梯度值。
def gradient_x2(x1, x2):
    return 0.4 * (x1 + x2) - 0.3 * x1

# 定义学习率
alpha = 0.5
# 定义列表,存放x1,x2与y的移动轨迹。
x1_list = []
x2_list = []
y_list = []

# 定义初始点。
x1, x2 = 4.8, 4.5

for i in range(50):
    # 使用列表加入x1,x2,与y的轨迹信息。
    x1_list.append(x1)
    x2_list.append(x2)
    y_list.append(f(x1, x2))
    # 根据梯度值调整每个自变量。
    x1 -= alpha * gradient_x1(x1, x2)
    x2 -= alpha * gradient_x2(x1, x2)

print(x1_list)
print(x2_list)
print(y_list)

在这里插入图片描述
注意:

  • 存放list,为了可视化
  • 初始值,都可以以,只要是凸函数
  • 对谁调整,对谁求偏导
    wj = wj - η * (J(w)/Wj)
  • 二维与一维的区别,2各变量
X1 = np.arange(-5, 5, 0.1)
X2 = np.arange(-5, 5, 0.1)
X1, X2 = np.meshgrid(X1, X2)
Y = np.array([X1.ravel(), X2.ravel()]).T
Y = f(Y[:, 0], Y[:, 1])
Y = Y.reshape(X1.shape)

fig = plt.figure()
ax = Axes3D(fig)
# 绘制方程曲面。
surf = ax.plot_surface(X1, X2, Y, rstride=5, cstride=5, cmap="rainbow")
# 绘制x1,x2,y的移动轨迹。
ax.plot(x1_list, x2_list, y_list, 'bo--')
# 指定为哪一个图形生成颜色条。
fig.colorbar(surf)
plt.title("函数$y = 0.2(x1 + x2) ^ {2} - 0.3x1x2 + 0.4$")
plt.show()

在这里插入图片描述

plt.scatter(x1_list, x2_list, c="r")

在这里插入图片描述

# m = plt.contourf(X1, X2, Y, 10)
# 画出等高线。
# 第4个参数(level):用来指定等高线的数量与位置。
# 直观的讲,该值越大,则等高线越密,否则越稀疏。
# m = plt.contour(X1, X2, Y, 25)

# 绘制填充的等高线。
m = plt.contourf(X1, X2, Y, 15)
# 在等高线上绘制x1与x2的移动轨迹。
plt.scatter(x1_list, x2_list, c="r")
plt.colorbar(m)

在这里插入图片描述

更新

对于损失函数:
J ( w ) = 1 2 i = 1 m ( y ( i ) w T x ( i ) ) 2 J(w) = \frac{1}{2}\sum_{i=1}^{m}(y ^ {(i)} - w ^ {T}x ^ {(i)}) ^ {2}
我们可以使用梯度下降的方式,不断去调整权重w,进而去减小损失函数J(w)的值。经过不断迭代,最终求得最优的权重w,使得损失函数的值最小(近似最小)
调整方式为:
w j = w j η J ( w ) w j w_j = w_j - \eta\frac{\partial J(w)}{\partial w_j}
我们这里先单独对一个样本求梯度来演示,所有样本,只需要分别对每个式子进行求梯度,最后将每个求梯度的结果求和即可。

在这里插入图片描述
不断调整w

按照每个分量的偏量方向 调整
对每个自变量求偏导
在这里插入图片描述
梯度下降能求解,能作用于损失函数

梯度 向量 分量 取值 每个函数取偏导
认为是向量

梯度的特征:3点

特度更重要的记住:梯度是方向导数
取梯度的时候,梯度值
更重要的是 梯度值>0 则原函数是单调递增

找到w使得jw损失函数最小
梯度下降 求线性回归也是可以的
使用梯度下降求梯度下降

梯度值 凸函数 有 极小值
梯度下降的精髓:
在这里插入图片描述
数学里面不管,只管
上升和下降没怎么关

通常情况下梦不到
但是我们可以取调整

最开始给定的w是这个

在这里插入图片描述
我们可以往左调 可以往有调
取决于 梯度值 》0 <0 不看等于0 等于0就是季度之
一般是到不了极值点 越接近 越换

一维的时候 左右?
在这里插入图片描述
二维,有w1 w2 w1 有左右 w2上下

在这里插入图片描述
调整左还是右呢?
大于0 单调递增 w越大 梯度越大 我们得然他减小
应该往左

在这里插入图片描述
在这里插入图片描述

右边 > 0 继续减
左边 <0 w增大 让它往右

如此迭代反复,我们就能让它接近极值点

和我们增加最快,减小最快,其实一点关系都没有!!!

因为梯度的方向很可能并不能指向极值点,但是我们移动的时候靠近极值点

不是一条直线 梯度值都一样
在这里插入图片描述
因为我们只能保证这一点的梯度,所以跟上升最快下降最快一点关系没有

所以梯度 只是控制方向,梯度大小,只是跟学习率有关
我们可以卖一步很小 卖一步很大
Delta w取多少?

我们就以梯度为移动不服,不是为了快,而是为了方便

所以我们有eta系数

取决于梯度值 大于0 还是小于0
梯度值》0 我们
梯度值<0

所以deltaw沃恩应该是让它增加还是减小了呢?
所以我们用w的性质

根据w来的
所以我们可以用求偏导数来控制

0 减一个w

在这里插入图片描述
上面就是这个式子

所以梯度不是不符
梯度只是控制方向

不符和大小由eta控制

最后更新的式子
在这里插入图片描述

然后是程序的实例:

一维的

二维的

关于eta值,不易于过大,就越过了极值点

在这里插入图片描述

如果是多维的,就对每一个维度进行更新
更新x1的时候就对x1求偏导
X2 就是 x2求偏导

数据的维度,是和数组的维度一样吗?

嵌套的层数,数组的

数据集的维度,特征的数量,就是多少列,多少个维度,why?

Eg.人员

年龄,身高,体重,
三列,三个周,三个维度
在这里插入图片描述
一个坐标轴就是一个维度

可视化画图

画等高线 新学的
X1 x2 取不同值的时候,能让y相等

最后说了下

损失函数不同,难道每个w写一个梯度函数吗?
在这里插入图片描述
从通用角度来看,能不能有通用形式?对蓝求偏导
在这里插入图片描述

在这里插入图片描述
拆开 对 a+b 求导

标签求导0 后面一部分
对里面的一项 wj 求偏导 其他都是常数 0
在这里插入图片描述
最后的结果是xj
在这里插入图片描述

所以对w更新 有一个通用的公式

然后利用它不断迭代 能够求出来

在这里插入图片描述
在这里插入图片描述
生成数据集

然后拆分,拆不拆其实无所谓
还是拆一下

这是线性回归的结果:
在这里插入图片描述
然后我们用梯度下降解一下

SGD随机梯度下降
学习率 给定迭代次数 模仿下面的
在这里插入图片描述
放到相关的对象中

也定义一个方法
Wb

不可能是构造器里进行初始化的,都是fit学出来的,所以后面加上
就是命名惯例

初始化的构造器里面指定的就是超参数

就能知道参数到底是学习出来的还是指定出来的

对于每一个样本用这个公式
在这里插入图片描述
循环一次拿出值

平行遍历多个可迭代对象 用zip
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
套入公式后不断迭代

超参数 eta 慎重 给0.01 itertime 100
Diaoyong fit方法 xtrain ytrina 传进去

进行迭代一轮 一共迭代itertime轮

如果50轮达到很好的效果,就不用

加上self
在这里插入图片描述
训练完周后 w He b就像有了
在这里插入图片描述
梯度下降的方式也很不错
在这里插入图片描述
那么给我们一个未知的p就能预知
在这里插入图片描述
W跟新的时候没用循环 采用的时矢量化计算

现在可以预测了

预测的第一个样本的第一个特征

N个特征 m个样本
在这里插入图片描述
第m个样本的第n个特征,这就是带特征的预测集
一会儿传测试集

常规方式的话 每次用循环 一次提一个小样本

广播之后 矢量化计算
在这里插入图片描述
在这里插入图片描述
一次 对位相乘 再相加
最后的 yhat 。。
一下子 预测值都得出
所以我们就可以利用numpy的矩阵方法 dot方法,不用循环

多维和一维 w 和 b
在这里插入图片描述
解决方法 点乘+b就搞定了

模型最难的时fit fit学习参数

这个结果
在这里插入图片描述
还是差不多 有的用e表示 这就his预测表示的结果

别只记得score 还有
在这里插入图片描述
值传进来

把 ytrue ypredict穿过来 0.99 高 因为 noise小
在这里插入图片描述
最关键的 是用梯度下降进行更新

在这里插入图片描述
注意
在这里插入图片描述

这一个快 对 w0 求骗到 w0 * 1

更新b前面乘以1 等于没成

根据方式求梯度下降 求 wj 最后得到比较理想的结果

梯度下降非常有意义,因为后面的梯度下降就靠这个算法
梯度下降球迷 最好的解决放啊发

之前学的就是随机梯度下降

现在使用的时批量梯度下降

批量梯度下降的更新方式:
不同之处在于:样本集汇中具有m个样本

随机梯度下降 M个样本会更新m次 速度快
只会更新wj 因为要进行累计求和 再
在这里插入图片描述
训练集100轮迭代
在这里插入图片描述
100Epoch 100个epoch

随机梯度下降 SGD 会进行多少次更新?

SGD
BGD

一轮只更新一次
求和进行更新

对于批量梯度下降 一轮不管有多少个
100轮 更新100次
样本集大的,耗内存多

有差异性,带表损失函数不一致

一个样本一个样本更新,带有误差,噪声大 异常样本
样本和样本之间,更新的方向也会有影响

随机梯度下降,更新的方向会语无伦次

在这里插入图片描述
尽管是向着极值点进行起那就,但是多轮次

最后再极值点附近进行徘徊,因为每一个样本每一个样本更新

在这里插入图片描述
就会比较稳定

所以SGD BSD对比
评率快快 但是 波折性
Bsd 跟新慢 但更新起来基本会朝着稳定的方向

因为SGD根据每个样本嘛

SGD可能跳过局部最优点 凸函数 因为是晃荡的
在这里插入图片描述
在这里插入图片描述
能不能在SGD 和 BGD之间找到结合的?

小批量梯度下降 MBGD
在这里插入图片描述
M个样本
一次取k
0<=k<=m

小批量梯度下降
K=1 累加没了 就是随机
K=m 就是批量梯度下降

之前是手写梯度下降,但是主要是让大家明白原理,对线性回归也是促进作用

那我们看skleran中的

在这里插入图片描述
SGRegressor
一点一点来 慢慢来
Eta0 初始 随着迭代次数一点一点减少 学习率的衰减
在这里插入图片描述
在这里插入图片描述
Max iter指定,不指定警告
在这里插入图片描述

使用梯度下降求解之前的线性回归问题。

X, y, coef = make_regression(n_samples=1000, n_features=10, coef=True, bias=5.5, random_state=0, noise=10)

提示:
定义一个数组w,含有元素的数量与生成的特征数量一致。
定义一个偏置b。

梯度下降,需要自变量具有一个初始值,这里可以将w与b全部设置为初始值0,或者很小的数值。
自定义一个合适的学习了eta值。

再来一层循环【定义对所有的样本迭代的次数】
对X与y进行遍历,每次取出一个样本的数据(x)与对应的标签(target)【循环】
y_hat = w与每一个样本数据x进行对位相乘再相加, 再加上b。
w = w + eta * (y - y_hat)
再说一下 标准化处理

by梯度下降, 求解线性回归问题(with codes)

X, y, coef = make_regression(n_samples=1000, n_features=10, coef=True, bias=5.5, random_state=0, noise=10)

提示:
定义一个数组w,含有元素的数量与生成的特征数量一致。
定义一个偏置b。

梯度下降,需要自变量具有一个初始值,这里可以将w与b全部设置为初始值0,或者很小的数值。
自定义一个合适的学习了eta值。

再来一层循环【定义对所有的样本迭代的次数】
对X与y进行遍历,每次取出一个样本的数据(x)与对应的标签(target)【循环】
y_hat = w与每一个样本数据x进行对位相乘再相加, 再加上b。
w = w + eta * (y - y_hat)

from sklearn.datasets import make_regression
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
import numpy as np

# 生成数据集
X, y, coef = make_regression(n_samples=1000, n_features=10, coef=True, bias=5.5, random_state=0, noise=10)
print(f"真是的权重")
print(coef)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=0)
lr = LinearRegression()
lr.fit(X_train, y_train)
print("使用线性回归训练的权重与偏置:")
print(lr.coef_)
print(lr.intercept_)
# 使用梯度下降的方式进行求解回归问题。
class SGD:
    # 定义初始化方法。
    # eat: 学习率
    # iter_time: 迭代次数。
    def __init__(self, eta, iter_time):
        self.eta = eta
        self.iter_time = iter_time
    
    # 定义用于训练模型的方法。
    # X 样本训练数据。
    # y 样本对应的标签。
    # 简单说:fit方法就是用来求解(学习)w与b的。
    def fit(self, X, y):
        # 定义权重与偏置。
        # scikit-learn命名惯例:对于不是在构造器中指定,而是通过学习计算出来的属性,习惯在
        # 属性名后面加上下划线(_)。
        # w_的长度需要与数据集X的特征数量一致(列数)。这里全部初始化为0。
        self.w_ = np.zeros(X.shape[1])
        # b_的长度为1。这里初始化为0。
        self.b_ = 0
        
        # 对所有样本进行iter_time轮的迭代重复。
        for i in range(self.iter_time):
            # 从样本集(训练集)中取出一条样本数据(x)与该样本数据对应的标签(target)。
            for x, target in zip(X, y):
                # 计算预测值y_hat y_hat = wT * x + b
                y_hat = np.dot(self.w_, x) + self.b_
                # 根据公式 wj = wj - eta * [-(y - y_hat) * xj] = wj + eta * (y - y_hat) * xj
                self.w_ = self.w_ + self.eta * (target - y_hat) * x
                self.b_ = self.b_ + self.eta * (target - y_hat)
           
    # 预测方法。根据样本X,返回对应的标签y。
    def predict(self, X):
        return np.dot(X, self.w_) + self.b_
    
# def socre()
    
    
# 对SGD类进行测试。
sgd = SGD(0.01, 100)
sgd.fit(X_train, y_train)
print(coef)
print(sgd.w_)
print(sgd.b_)
y_hat = sgd.predict(X_test)
print(y_hat[:10])
print(y_test[:10])

from sklearn.metrics import r2_score
print(r2_score(y_test, y_hat))