目录
一、随机梯度下降法
1、批量梯度下降法
在上一小节我们可知,梯度下降法就是将我们想要的那个最优化损失函数相应在某一点θ的那个梯度值准确的求出来。通过下面推导的公式我们可以看出来,要想求出准确的梯度来,需要对每一项的所有样本进行求和计算,这样的梯度下降法也叫作批量梯度下降法,即每次都需要将样本中的所有信息进行批量计算。
但显然也出现了一个问题,如果m这个样本量非常大,那么计算梯度本身也是非常耗时的,对此有什么解决方案呢?
2、随机梯度下降法
基于批量梯度下降法,我们可以每一次只针对其中的一个样本进行计算,因此推导出以下式子并使用该式子来进行搜索的方向,这种思想也被称之为随机梯度下降法。“随机”意味着是要跳出局部最优解,并且有更快的运行速度。
由于随机梯度下降法的搜索过程不能保证每次搜索方向都是损失函数减小的方向,更不能保证一定是减小速度最快的方向,所以搜索路径会形成以下方式。但即使如此,随机梯度下降法最后也能差不多的来到整个损失函数最小值附近。并且针对于样本m非常大的情况,随着它的循环次数增加,其学习率是逐渐递减的。
代码示例:
#随机梯度函数
def dJ_sgd(theta,X_b_i,y_i):
return X_b_i.T.dot(X_b_i.dot(theta) - y_i)*2.
def sgd(X_b,y,initial_theta,n_iters):
t0 = 5
t1 = 50
def learning_rate(t):
return t0 / (t+t1)
theta = initial_theta
for cur_iter in range(n_iters):
rand_i = np.random.randint(len(X_b))
gradient = dJ_sgd(theta,X_b[rand_i],y[rand_i])
theta = theta - learning_rate(cur_iter) * gradient
return theta
测试:
import numpy as np
import matplotlib.pyplot as plt
#生产随机样本
m = 100000
x = 2 * np.random.random(size=m)
X = x.reshape(-1,1)
y = x * 4. + 3. + np.random.normal(0,3,size=m) #噪音均值为0方差为1
#训练
%%time
X_b = np.hstack([np.ones((len(X),1)),X])
initial_theta = np.zeros(X_b.shape[1])
theta = sgd(X_b,y,initial_theta,n_iters=len(X_b)//3) #随机循环1/3的样本数
执行结果:
二、sklearn中的随机梯度下降法
1、使用自定义函数SGD
代码示例:
def fit_sgd(self,X_train,y_train,n_iters=1e4,t0=5,t1=50):
assert X_train.shape[0] == y_train.shape[0], \
"the size of X_train must be equal to the size of y_train"
assert n_iters >= 1
def dJ_sgd(theta, X_b_i, y_i):
return X_b_i.T.dot(X_b_i.dot(theta) - y_i) * 2.
def sgd(X_b, y, initial_theta, n_iters,t0=5,t1=50):
def learning_rate(t):
return t0 / (t + t1)
theta = initial_theta
m = len(X_b)
for cur_iter in range(n_iters):
indexes = np.random.permutation(m)
X_b_new = X_b[indexes] #X_b乱序化结果
y_new = y[indexes]
for i in range(m):
gradient = dJ_sgd(theta, X_b_new[i], y_new[i])
theta = theta - learning_rate(cur_iter * m + i) * gradient
return theta
X_b = np.hstack([np.ones((len(X_train), 1)), X_train])
initial_theta = np.zeros(X_b.shape[1])
self._theta = sgd(X_b,y_train,initial_theta,n_iters,t0,t1)
self.interception_ = self._theta[0] # 截距
self.coef_ = self._theta[1:] # 斜率
return self
测试:
#波士顿房产数据
import numpy as np
from sklearn import datasets
boston = datasets.load_boston()
X = boston.data
y = boston.target
X = X[y < 50.0]
y = y[y < 50.0]
#数据归一化
from sklearn.model_selection import train_test_split
X_train,X_test,y_train,y_test = train_test_split(X,y,random_state = 666)
from sklearn.preprocessing import StandardScaler
standardScaler = StandardScaler()
standardScaler.fit(X_train)
X_train_standard = standardScaler.transform(X_train)
X_test_standard = standardScaler.transform(X_test)
#训练
from mySklearn.LinearRegression import LinearRegression
lin_reg = LinearRegression()
%time lin_reg.fit_sgd(X_train_standard,y_train,n_iters=2)
lin_reg.score(X_test_standard,y_test)
执行结果:
代码示例:
%time lin_reg.fit_sgd(X_train_standard,y_train,n_iters=50)
lin_reg.score(X_test_standard,y_test)
执行结果:
由此看出,调整最大迭代次数n_iters参数值,可以提高R方的值。
2、sklearn中的随机梯度下降法
代码示例:
from sklearn.linear_model import SGDRegressor
sgd_reg = SGDRegressor()
#sgd_reg = SGDRegressor(max_iter=100)
%time sgd_reg.fit(X_train_standard,y_train)
sgd_reg.score(X_test_standard,y_test)
执行结果:
值得注意的是,sklearn中实现随机梯度算法的过程比我们自己自定义函数实现会更加复杂,它有许多优化方案,因此性能也更优。
三、调试梯度下降法
代码示例:
import numpy as np
#生产随机样本
np.random.seed(666)
X = np.random.random(size=(1000,10))
true_theta = np.arange(1,12,dtype=float)
X_b = np.hstack([np.ones((len(X),1)),X])
y = X_b.dot(true_theta) + np.random.normal(size=1000)
#损失函数
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_math(theta,X_b,y):
return X_b.T.dot(X_b.dot(theta) - y)*2. / len(y)
#调式函数
def dJ_debug(theta,X_b,y,epsilon=0.01):
res = np.empty(len(theta))
for i in range(len(theta)):
theta_1 = theta.copy()
theta_1[i] += epsilon
theta_2 = theta.copy()
theta_2[i] -= epsilon
res[i] = (J(theta_1,X_b,y) - J(theta_2,X_b,y)) / (2*epsilon)
return res
%time
X_b = np.hstack([np.ones((len(X),1)),X])
initial_theta = np.zeros(X_b.shape[1])
eta = 0.01
#使用调试方式
theta = gradient_descent(dJ_debug,X_b,y,initial_theta,eta)
#使用数学推导梯度求解方式
%time theta = gradient_descent(dJ_math,X_b,y,initial_theta,eta)
theta
执行结果:
对比以上两个例子我们可以得知,通过dJ_debug的方式我们可以得到我们想要的机器学习正确结果,但是训练速度会慢很多。因此当机器学习中涉及到梯度的求法时,我们可以首先使用dJ_debug的方式先作为梯度的求法,得到想要的正确结果,然后再通过数学推导方式dJ_math得到我们想要求出的数学解,之后将实现的数学解带入到机器学习中,最后通过将最终得到的结果与dJ_debug得到的结果进行对比来验证我们推导的数学解是否是正确的推导。
四、梯度下降法总结
1、批量梯度下降法
优点:
- 稳定;
- 一定可以向我们的损失函数下降的最快的那个方向前进。
缺点:
- 求解的速度比较慢。
2、随机梯度下降法
优点:
- 计算的非常快。
缺点:
- 不稳定;
- 每一次的方向是不确定的。
3、小批量梯度下降法
综合以上两种梯度下降法的优缺点,我们可以得到一个新的梯度下降法——小批量的梯度下降法。小批量的梯度下降法的梯度计算推导跟批量梯度下降法推导是一样的,批量梯度下降法最终是把批量提度下降结果直接拿来,只是用在一行上;而小批量梯度下降法也是如此,只不过是用在k行上,因此相应的多了一个超参数k,即小批量到底是多少的批量。
最后,需要再次强调的是,梯度下降法它本身不是一个机器学习算法,而是一种基于搜索的最优化方法,它的作用是最小化一个损失函数,而梯度上升法是最大化一个效用函数,它们都是最优化一个目标函数的使用。