不是一个机器学习算法,不能解决分类或回归问题
是一种基于搜索的最优化方法
作用:最小化一个损失函数
梯度上升法:最大化一个效用函数


并不是所有的函数都有唯一的极值点
解决方案:
多次运行,随机初始化点
梯度下降法的初始点也是一个超参数
一,梯度下降法模拟实现
线性回归的损失函数具有唯一的最优解
import numpy as np
importmatplotlib.pyplot as plt
plot_x =np.linspace(-1., 6., 141)
plot_y= (plot_x-2.5)**2 - 1. #假设的损失函数曲线
epsilon= 1e-8 #定义容许的误差
eta =0.1 #定义步长
theta= 0.0 #初始化theta
while True:
gradient = dJ(theta)
last_theta = theta
theta = theta - eta * gradient
if(abs(J(theta) - J(last_theta)) <epsilon):
break

eta的影响:
theta = 0.0
theta_history = [theta]#记录theta
while True:
gradient = dJ(theta)
last_theta = theta
theta = theta - eta * gradient
theta_history.append(theta)
if(abs(J(theta) - J(last_theta)) <epsilon):
break
plt.plot(plot_x,J(plot_x))
plt.plot(np.array(theta_history),J(np.array(theta_history)), color="r", marker='+')
plt.show()

eta = 0.8
eta = 1.1

二,线性回归中使用梯度下降算法


J的梯度就是n维空间中,损失函数下降速度最快的方向。

三,在线性回归中实现梯度下降算法
构造数据样本
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)

使用梯度下降法训练
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)
defgradient_descent(X_b, y, initial_theta, eta, n_iters = 1e4, epsilon=1e-8):
theta = initial_theta
cur_iter = 0
while cur_iter < n_iters:
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
cur_iter += 1
return theta
X_b =np.hstack([np.ones((len(x), 1)), x.reshape(-1,1)])
initial_theta =np.zeros(X_b.shape[1])
eta = 0.01
theta =gradient_descent(X_b, y, initial_theta, eta)

一步步纠正自己对机器学习原始的印象,机器学习并不是所有算法都是通过迭代训练参数的,目前为止大多数都是通过数学推导和简单的逻辑实现的。
四,梯度下降法的向量化和数据标准化
梯度下降算法的进一步变形:

最终损失函数的导数化为:

def dJ(theta, X_b, y):
return X_b.T.dot(X_b.dot(theta) - y) *2. / len(y)
预测波士房价
使用梯度下降法之前,最好进行数据的归一化
数据集的特征的数据量级不同,需要对数据进行预处理。如果不进行处理,步长的设定对某些特征的数据可能正好,但是对一些量级非常小的数据可能不合适,使得算法发散。
fromsklearn.preprocessing import StandardScaler
standardScaler =StandardScaler() #实例化这个函数
standardScaler.fit(X_train)
X_train_standard =standardScaler.transform(X_train)
lin_reg3 =LinearRegression()
%timelin_reg3.fit_gd(X_train_standard, y_train)
X_test_standard =standardScaler.transform(X_test)
lin_reg3.score(X_test_standard,y_test)
梯度下降法的优势
m = 1000
n = 5000
big_X =np.random.normal(size=(m, n))
true_theta =np.random.uniform(0.0, 100.0, size=n+1)
big_y =big_X.dot(true_theta[1:]) + true_theta[0] + np.random.normal(0., 10., size=m)

对特别大的矩阵,一般来说梯度下降法的速度更快一些,但当特别大时候,梯度下降法也很慢,因此引入随机梯度下降法。
五,随机梯度下降法StochasticGradient Descent
随机梯度下降就是采取,每次随机取出样本的一行,也就是一个样本的所有属性作为方向作为迭代,寻找损失函数的最小值。图中右侧的式子已经不是损失函数,只是作为一种搜索的方向


从图中可以看出,搜索过程中,每次的搜索方向都是随机的;常规的梯度下降是按照一个方向进行的,这个方向一定是损失函数下降速度最快的方向,但是对于大样本来说这样的算法计算速度太慢。随机梯度下降虽然不一定是按照下降速度最快的方向,但是对于m,也就是样本数量太大的情况可以明显加快计算速度。总之,随机梯度下降法的精度较低一些,但是速度快了很多,而传统的梯度下降算法精度足够,一定可以来到损失函数最低点,但是计算速度不高。
随机梯度下降法的步长是需要不断的改变的,应该随着学习次数的提高,学习率(步长)应该逐渐减少。

eta的分母+b的原因是为了缓解学习次数很小时,相邻学习次数对应的步长差距太大的影响
因此,随机梯度下降算法的超参数是a,b,我们可以取经验值,a=5,b=50
这种步长随着循环次数的增加而递减的思想是用到了模拟退火算法的思想
随机梯度下降算法的实现:
m =1000000 #数据规模非常大
x =np.random.normal(size=m)
X = x.reshape(-1,1)
y = 4.*x + 3. +np.random.normal(0, 3, size=m)
def dJ_sgd(theta,X_b_i, y_i):
return 2 * X_b_i.T.dot(X_b_i.dot(theta) -y_i)
def sgd(X_b, y,initial_theta, n_iters):
t0, t1 = 5, 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
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=m//3) #只需要检查很少的样本量
结果对比:
采取一般梯度下降法:

采取随机梯度下降法:

skikit-learn中的随机梯度下降法
fromsklearn.linear_model import SGDRegressor #在linear包中,只能解决线性问题
sgd_reg =SGDRegressor()
%timesgd_reg.fit(X_train_standard, y_train)
sgd_reg.score(X_test_standard,y_test)
机器学习通用过程:
调包->构建实例对象进行fit->验证训练模型的效果
六:调试梯度下降法

红点切线对应的斜率近似等于它旁边俩个点连线的斜率


问题是时间复杂度太高
因此我们可以采取选用小数据量的样本来使用上述的公式验证,看我们所写的梯度下降法是否有错误。
梯度调试的计算
产生数据:
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
defgradient_descent(dJ, X_b, y, initial_theta, eta, n_iters = 1e4, epsilon=1e-8):
theta = initial_theta
cur_iter = 0
while cur_iter < n_iters:
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
cur_iter += 1
return theta
验证:
X_b =np.hstack([np.ones((len(X), 1)), X])
initial_theta =np.zeros(X_b.shape[1])
eta = 0.01
%time theta =gradient_descent(dJ_debug, X_b, y, initial_theta, eta)
%time theta =gradient_descent(dJ_math, X_b, y, initial_theta, eta)
检查theta即可
七,总结:
批量梯度下降法 Batch Gradient Descent 精确但是慢
随机梯度下降法 Stochastic Gradient Descent 快但是不精确
小批量梯度下降法 Mini-Batch Gradient Descent 较快较精确
随机的优势:
跳出局部最优解
更快的运行速度
机器学习领域很多算法都要使用随机的特点
随即搜索:随机森林