梯度下降算法

1. 二次函数下的梯度下降

梯度下降算法不是一个机器学习算法,是一种基于搜索的最优化算法。目的是为了最小化损失函数。也有梯度上升法,它目的是最大化一个效用函数。
在这里插入图片描述
从起始点出发,每次都向导数减小的方向移动,最终当导数减小到0时,我们就得到了极值点。
其中 η \eta η 被称为学习率,决定每一次学习的“步伐的大小”在这里插入图片描述
有时候函数不止有一个极致点,从不同的点出发,会得到不同的极值点。因此初始点也是一个比较重要的参数。

1.1 对梯度下降法的模拟

# 获得一个二次函数的数据和图像
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

def gradient_descent(initial_theta, eta, epsilon=1e-8):
	# 梯度下降算法
    theta = initial_theta # 起始点
    theta_history.append(initial_theta) # 为了方便绘图储存的历史点
    
    while True:
        gradient = dJ(theta) # 计算导数
        last_theta = theta
        theta = theta - eta * gradient # 得到下一个theta值
        theta_history.append(theta)
		
        if (abs(J(theta) - J(last_theta)) < epsilon):  # 当损失函数的变化足够小时,结束循环
            break

def plot_theta_history():
    plt.plot(plot_x, J(plot_x))
    plt.plot(np.array(theta_history), J(np.array(theta_history)), color='r')
    plt.show()

现在来带入不同的值试一下

eta = 0.01  # 每次学习的步长为0.01
theta_history = []
gradient_descent(0., eta)
plot_theta_history()

在这里插入图片描述

eta = 0.8
theta_history = []
gradient_descent(0., eta)
plot_theta_history()

在这里插入图片描述
可以看到,不同的步长带来的学习方式不一样,但如果步长太大,那么程序会出现死循环,因为:

eta = 1.1
theta_history = []
gradient_descent(0., eta)
plot_theta_history()

在这里插入图片描述

2. 在线性回归中应用梯度下降算法

不同纬度下的梯度下降算法并没有本质的区别,只是把导数变为了偏导数。
在这里插入图片描述在这里插入图片描述
别人写的原理更好,这个是链接在这里插入图片描述

通过梯度下降的算法进行线性回归函数的求解

# 准备数据
import numpy as np
import matplotlib.pyplot as plt

np.random.seed(666)
x = 2 * np.random.random(size=100)
y = x * 3. + 4. + np.random.normal(size=100)
X = x.reshape(-1, 1)

plt.scatter(x, y)
plt.show()

在这里插入图片描述

def J(theta, X_b, y):
# 构建损失函数
    try:
        return np.sum((y - X_b.dot(theta))**2) / len(X_b)
    except:
        return float('inf')

def dJ(theta, X_b, y):
# 构建偏导数,根据上图中的公式撰写
    res = np.empty(len(theta))
    res[0] = np.sum(X_b.dot(theta) - y)
    
    for i in range(1, len(theta)):
        res[i] = (X_b.dot(theta) - y).dot(X_b[:,i])
        return res * 2 / len(X_b)

def gradient_descent(X_b, y, initial_theta, eta, epsilon=1e-8, n_iterns=1e8):
# 梯度下降算法,和低纬空间下基本没有区别
    theta = initial_theta
    i_iter = 0 # 迭代一定次数以后就可以停止迭代了
    
    while i_iter < n_iterns:
        gradient = dJ(theta, X_b, y)
        last_theta = theta
        theta = theta - eta * gradient

        if (abs(J(theta, X_b, y) - J(last_theta,X_b, y)) < epsilon):
            break
        i_iter += 1
        
    return theta

进行测试,发现计算无误。

X_b = np.hstack([np.ones((len(x), 1)), x.reshape(-1,1)]) # 在X中添加一列为1的向量作为截距项
initial_theta = np.zeros(X_b.shape[1]) # 
eta = 0.01

theta = gradient_descent(X_b, y, initial_theta, eta)
>>>[4.02145786 3.00706277]

2.1 算法优化

我们可以通过优化算法 ,让计算过程更加的简单,性能更好。
在这里插入图片描述
程序上只需要修改求偏导数计算的部分就可以了:

def dJ(theta, X_b, y):
	return X_b.T.dot(X_b.dot(theta)-y) * 2. / len(X_b)

2.2 数据归一化的重要性

一些维度的量纲很大,有些很小,这会使得学习的效率很低。比如当某一维度的量纲时“万”级别,此时学习步伐为0.1就不合适了。
我这里举波士顿房价的预测为例子:

  1. 首先用普通的线性回归:
    import numpy as np
    from sklearn import datasets
    
    boston = datasets.load_boston() # 加载数据
    x = boston.data
    y = boston.target
    
    from sklearn.model_selection import train_test_split
    x_train, x_test, y_train, y_test = train_test_split(x, y, random_state=100)
    
    from sklearn.linear_model import LinearRegression
    lin_reg1 = LinearRegression()
    %time lin_reg1.fit(x_train, y_train)
    lin_reg1.score(x_test, y_test)
    >>>
    CPU times: user 748 µs, sys: 314 µs, total: 1.06 ms
    Wall time: 794 µs
    Out[272]:
    0.7246154314616737
    
  2. 然后使用sklearn中的随机梯度下降法
    from sklearn.linear_model import SGDRegressor
    lin_reg2 = SGDRegressor()
    %time lin_reg2.fit(x_train, y_train)
    lin_reg2.score(x_test, y_test)
    >>>
    CPU times: user 4.23 ms, sys: 1.01 ms, total: 5.25 ms
    Wall time: 3.86 ms
    Out[273]:
    -9.895028367010746e+25
    
    会发现随机梯度下降算法下的结果并不理想,而且耗时较长
  3. 先进行数据归一化,然后再随机梯度下降
    from sklearn.preprocessing import StandardScaler
    standardScaler = StandardScaler() # 实例化“正态分布器”
    standardScaler.fit(x_train)
    
    x_train_standard = standardScaler.transform(x_train)
    lin_reg3 = SGDRegressor()
    
    %time lin_reg3.fit(x_train_standard, y_train)
    x_test_standard = standardScaler.transform(x_test) # 注意将x_test进行数据归一化时,采用的分布是x_test下的
    lin_reg3.score(x_test_standard, y_test)
    >>>
    CPU times: user 2.52 ms, sys: 498 µs, total: 3.01 ms
    Wall time: 2.47 ms
    0.721436613453759
    
    可以清晰的看到,数据归一化以后,无论是运行速度还是准确度,都有了极为明显的提升。

    3. 梯度下降法的改进

    3.1 批量、随机和小批量随机梯度下降法

    在上述的经典梯度下降算法中,我们采用所有训练数据的平均损失来近似目标函数
    L ( θ ) = 1 M ∑ i = 1 M L ( f ( x i , θ ) , y i ) L(\theta)=\frac{1}{M}\sum^M_{i=1}L(f(x_i,\theta ),y_i ) L(θ)=M1i=1ML(f(xi,θ),yi)
    但是,经典的梯度下降算法每次都需要对所有的样本进行遍历,需要极大的计算量,影响了算法的效率。为了解决这一问题,随机梯度下降法(Stochastic Gradient Descent用样本个体的的损失函数近似平均的损失,以此减轻计算量。
    L ( θ ; x i , y i ) = L ( f ( x i , θ ) , y i ) L(\theta;x_i,y_i)=L(f(x_i,\theta ),y_i ) L(θ;xi,yi)=L(f(xi,θ),yi)
    但是这又会带来一个新的问题,由于每一次计算使用的样本不一样,计算的结果也很不稳定。于是,我们在M个样本中随机挑选m个样本,以这m个样本的损失的平均值代替总体的M个样本的平均损失,这样计算量减小的同时,结算结果的稳定性也增加了,这种方法就是小批量梯度下降法 (Mini-Batch Gradient Descent)
    L ( θ ) = 1 m ∑ i = 1 M L ( f ( x i j , θ ) , y i j ) L(\theta)=\frac{1}{m}\sum^M_{i=1}L(f(x_{ij},\theta ),y_{ij} ) L(θ)=m1i=1ML(f(xij,θ),yij)
    其中m这个参数最好选去2的幂次方,比如16,32,64等,这样有助于充分利用矩阵的运算。学习的速率 α \alpha α在选择时先从大开始,当误差曲线进入平台期时再使用较小的数据。

    变体1: 动量法(Monmentum)

    梯度下降法可以想象为一个人闭着眼睛向山下走,大部分情况我们知道自己走的方向,但如果我们在下山的过程中进入了一个平台时,就会失去方向感。类似的,如果我们掉进了一个狭窄的陷阱,我们就只能在陷阱的墙壁之间来回的碰撞。
    假设下山的过程中,我们的步幅是 η \eta η,那么我门的下山公式是: θ i + 1 = θ t − η g t \theta_{i+1}=\theta_t - \eta g_t θi+1=θtηgt,其中 − g t -g_t gt表示步子的方向。
    为了快速的通过平原以及陷阱,我们可以加大自己的步伐,跨越陷阱并快速的沿着之前的方向越过平台。由此得出的下山公式为:
    v t = γ v t − 1 + η g t θ i + 1 = θ t − v t v_t = \gamma v_{t-1}+\eta g_t \\ \theta_{i+1}=\theta_t - v_t vt=γvt1+ηgtθi+1=θtvt
    前一时刻的速度会累积到这一时刻,也就是说大部分情况下,下山是一个加速的过程。

    变体2:AdaGrad方法

    我们可以对周围的环境进行感知来获得下山的路径,具体来说对于不同的维度,我们会有自己的判断。比如在文本处理时,如果周围的词出现的频率会影响我们对当下情况的判断。AdaGrad使用历史梯度平方和来研究不同参数梯度的稀疏性:
    θ i + 1 , j = θ i , j − η ∑ k = 0 t g k , i 2 + ϵ g i , j \theta_{i+1,j}=\theta_{i,j}-\frac{\eta}{\sqrt{\sum^t_{k=0}g^2_{k,i}+\epsilon}}g_{i,j} θi+1,j=θi,jk=0tgk,i2+ϵ ηgi,j

    变体3: Adam方法

    Adam方法是动量法和AdaGrad方法的结合,同时考虑了历史惯性保持和环境感知。具体的我以后再去学习吧。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值