大白话5分钟带你走进人工智能-第十一节梯度降低之手动实现梯度降低和随机梯度降低的代码(6)

                                                        第十一节梯度降低之手动实现梯度降低和随机梯度降低的代码(6)html

咱们回忆一下,以前我们讲什么了?梯度降低,那么梯度降低是一种什么算法呢?函数最优化算法。那么它作的是给我一个函数,拿到这个函数以后,我能够求这个函数的导函数,或者叫能够求这个函数的梯度。导函数是一个数儿,梯度是一组数,求出来梯度以后怎么用?把你瞎蒙出来的这组θ值,减去α乘以梯度向量,是否是就获得了新的θ,那么往复这么迭代下去的,是否是愈来愈小,愈来愈小,最后达到咱们的最优解的数值解? 你拿到数值解以后,咱们实际上就获得了咱们的一组最好的θ。python

回到咱们继续学习的场景来讲,咱们想要找到一组可以使损失函数最小的θ,那么我原来是经过解析解方式可以直接把这θ求出来,可是求的过程太慢了,有可能当你参数太多的时候,因此咱们经过梯度降低法能够获得咱们损失函数最小那一刻对应的W值,也就是完成了一个咱们这种参数型模型的训练。其实这个东西虽然我们只讲了一个线性回归,可是逻辑回归svm,岭回归,学了以后,你本质就会发现它是不一样的损失函数,仍是一样使用函数最优化的方法达到最低值,由于都是参数型模型,只要它有损失函数,最终你就能经过梯度降低的方式找到令损失函数最小的一组解,这组解就是你训练完了,想要拿到手,用来预测将来,好比放到拍人更美芯片里面的模型。哪怕深度神经网络也是这样。算法

在讲梯度降低背后的数学原理以前,咱们上午只是从直觉上来说,梯度为负的时候应该加一点数,梯度为正的时候应该减一点数,并且梯度越大证实我应该越多加一点数,只是这么来解释一下梯度降低,那么它背后其实是有它的理论所在,为何要直接把梯度拿过来直接乘上一个数,就能达到一个比较快的收敛的这么一个结果,它有它的理论所在的。数组

在讲这个以前咱们仍是先来到代码,咱们手工的实现一个batch_gradient_descent。批量梯度降低,还记得批量梯度降低和Stochastic_ gradient_descent什么关系吗?一个是随机梯度降低,一个是批量梯度降低,那么随机跟批量差在哪了?就是计算负梯度的时候,按理说应该用到全部数据,经过全部的数据各自算出一个结果,而后求平均值.如今我们改为了直接抽选一条数据,算出结果就直接当作负梯度来用了,,这样会更快一点,这是一个妥协。理论向实际的妥协,那么咱们先看看实现批量梯度降低来解决。网络

import numpy as np #固定随机种子 np.random.seed(1) #建立模拟训练集 X = 2 * np.random.rand(10000, 1) y = 4 + 3 * X + np.random.randn(10000, 1) X_b = np.c_[np.ones((10000, 1)), X] # print(X_b) learning_rate = 0.1 n_iterations = 500 #有100条样本 m = 10000 #初始化θ theta = np.random.randn(2, 1) count = 0 for iteration in range(n_iterations): count += 1 gradients = 1/m * X_b.T.dot(X_b.dot(theta)-y) theta = theta - learning_rate * gradients print(count) print(theta)
X = 2 * np.random.rand(10000, 1) y = 4 + 3 * X + np.random.randn(10000, 1)

上这两行代码仍然是生成一百个X,模拟出对应了一百个Y,这个代码里边咱们不掉现成的包,不像在这用了一个sklearn了,咱们不用sklearn的话,还有什么包帮你好心的生成出一个截距来,是否是没有了?因此咱们是否是仍是要手工的,在X这里面拼出一个全为1的向量做为X0,如今X_b是一个什么形状呢?100行2列,第一列是什么?是否是全是1,第二列是什么?随机生成的数。咱们解释下上面代码:app

learning_rate = 0.1 n_iterations = 500

咱们作梯度降低的时候,是否是有一个α?咱们命名它为学习率,拿一个变量给它接住,learning_rate=0.1。那么iterations什么意思? 迭代是吧,n_iterations就是说我最大迭代次数是1万次,一般这个超参数也是有的,由于在你若是万一你的学习率设的很差,这个程序是否是就变成死循环了?它一直在走,若是你不设一个终止条件的话,你这个东西一训练你有可能就再也停不下来了。因此一般都会有一个最大迭代次数的。当走了1万次以后没收敛,也给我停在这,最后给我报个警告说并无收敛,说明你这个东西有点问题。你应该增长点最大次数,或者你调一调你的学习率是否是过小了或者太大了,致使它还没走到或者说走过了震荡震荡出去了,你须要再去调整。M是这个数据的数量,这一会再说它是干吗的。dom

theta = np.random.randn(2, 1) count = 0

接下来θ,咱们上来梯度降低,是否是要先蒙出两个θ来,因而我生成两个随机数,np.random.randn(2, 1)这个的意思是生成一个两行一列的-1到+1之间的正态分布的随机数。接下来我就进入for循环:机器学习

for iteration in range(n_iterations): count += 1 gradients = 1/m * X_b.T.dot(X_b.dot(theta)-y) theta = theta - learning_rate * gradients

在python2中,若是你输入range(10000),会获得一个列表,从0一直到9999,就实实在在的是一个列表,你把这列表搁进去,进行for循环,会获得什么?第一次循环的时候,此次此时的iteration等于0,第二次等于1,由于列表中的每个元素要赋值到这个变量里面去。那么在python3里面range就再也不直接给你实实在在的生成一个列表了,而生成一个python里面独一无二的类型叫Generator,生成器。生成器是一个什么?你只是想借用这个东西去循环意思,循环一次,你有必要先放一个列表,在那占你的内存空间,没有必要?实际上它是一个懒加载的这么一个东西,你每次迭代第一次返回0第二次返回1,每次迭代就给你返回一个数,生成器本质它就变成一个函数了,你第一次调用它返回零,第二次掉他返回一,第三次调用它返回二,它里边记录了本身当前到哪了,而且生成规则,这样就没有必要去占用你的内存空间了,这个是range一般是用来作for循环用的,就为了有一个序号。 还有另外一种高级的用法是enumerate,假如你用一个enumerate(list),它会给你返回两个数,第一个是索引号,第二个是list中的自己的元素。在这用逗号能够把这两个变量分别复制给你指定的两个变量名。函数

那么i在此时实际上就是li1里面的第一个元素的索引号是零,第二个元素就是自己是什么什么,这个是一个很方便的技巧,可以帮你在for循环体内部既须要索引号来计算它的位置,又须要这个数据自己的时候能够直接用enumerate一次性的把它取出来,很简单的一个技巧。for i,a in enumerate(list): print(i,a)
gradients = 1/m * X_b.T.dot(X_b.dot(theta)-y) theta = theta - learning_rate * gradients

那么在这咱们只须要n_iterations给他作个计数器就行了,因此我在这儿只用它。那么咱们看这count默认是零,他是一个计数器,每次循环会本身加1,+=你们都应该能看懂,本身自加1,那么此时的gradients实际上就是X_b的转置。乘以它,再乘以1/m。gradients = 1/m * X_b.T.dot(X_b.dot(theta)-y)。这一步里面有没有加和?是批量梯度降低带加和的版本,仍是随机梯度降低,不带加和的版本?由于X_B是个矩阵,X_b^{\mathrm{T}} \cdot(X_b \bullet \theta-\mathrm{Y})实际上你看X_b是否是一个矩阵,Y是一个向量,咱们须要看Y是个行向量仍是列向量, X是一百行一列的,Y是一百行一列的,它是一个列向量。而后用最开始的X转置去乘以列向量,实际上矩阵乘以一个向量的时候,自己就拿第一行乘以第一列加第二行乘以第一列,自己就把加和融在矩阵乘法里面去了。因此实际上1/m * X_b.T.dot(X_b.dot(theta)-y)这个东西自己就已是带加和的了,并且如前面所说是否是必定要加一个M分之一?接下来实现梯度降低是否是很是简单,拿上一代的θ减去learnrate,乘以你计算出来的梯度就完事了! 也就是学习

theta = theta - learning_rate * gradients

咱们看梯度降低,虽然讲了半天感受很复杂,其实四行代码结束了,那么在执行完了这1万次迭代以后,咱们把θ给打印出来,看看结果,我没有实现那个tolerance,没有断定。假如这样我每一步都让它打印θ。

for iteration in range(n_iterations): count += 1 gradients = 1/m * X_b.T.dot(X_b.dot(theta)-y) theta = theta - learning_rate * gradients if count%20==0: print(theta)

咱们看看θ随着更新,它很早你发现是否是都已经收敛了其实?你看她经过多少次收敛的,上来是3.27,3.52,下次慢慢在变化变化,每一步都走的还比较稳健,到4.00,4.04,

是否是越走越慢了,你发现。你看第一部时候从3.27到3.51,到后来4.04,4.07是否是越走越慢了?4.10,4.12,为何会越走越慢,学过梯度降低,大家如今是否是应该知道了?由于越接近谷底,它的梯度值怎么样?越大仍是越小? 越小,它天然就越走越慢,越小走的越慢。那么最后到4.18,收敛了不再动,可是咱们循环是否是还在往下一直继续,只不过每次加的梯度都是怎么样?零。

再有一个问题,刚才个人数据集里面并无作归一化,那么实际上它须要作最大最小值归一化吗?本质上不太须要,由于它只有一个W,只有一个W的时候天然作归一化是无所谓的,若是你有多个W的状况下,你对每个X在预先处理以前作都须要作归一化,其实也是两行代码的事。咱们加入最大最小值归一化的方式:

X_b[:,1]=X_b[:,1]-X_b[:,1].mean()

X_b[:,1]这个冒号什么意思?这个冒号就是一个索引方式,我要取全部的行,我就打一个冒号:,你取全部的行的第一列,就写个1,X_b[:,1]实际上是一个一维数组,就是那一堆一百个随机数,那么X_b[:,1].mean()加一个.mean,你能够看到它的平均是多少?0.95。 若是你用X_b[:,1]=X_b[:,1]-X_b[:,1].mean(),会自动的帮咱们对位相减,每个数都减减去它。而后咱们此时在看咱们剪完了以后的结果,是否是就变成有正有负的了?

此时在作梯度降低,按理说速度应该更快一些,咱们看刚才咱们迭代次数是多少,取决于你初始化的点,若是你初始化的点好的话是没有区别的,若是初始化的点很差的话就有区别。咱们运行一下,你看一下,我期待它会有变化。须要多少次?331.刚才有多少次?500屡次,如今只须要300屡次,就证实刚才随机那个点,实际上对于能不能正负一块儿优化,是否是有实际的敏感度的,你如今作了一个均值归一化,实际上发生了什么问题? 迭代次数变得更好了,更少了,就更快地达到了收敛的值。可是你发现它W1和W0也变了,

确定会变,由于你的整个数值全都变了,但变了不要紧。怎么样才能继续用起来?你这变过以后的W,对你新预测的数据拿回来以后先作一样的归一化,你再去预测结果也是正确的。

   有了批量梯度降低的代码,咱们再看随机梯度降低的话,就简单多了。先上代码:

import numpy as np from sklearn.linear_model import SGDRegressor #固定随机种子 np.random.seed(1) #生成训练集 X = 2 * np.random.rand(100, 1) y = 4 + 3 * X + np.random.randn(100, 1) X_b = np.c_[np.ones((100, 1)), X] print(X_b) n_epochs = 1000#迭代次数 t0, t1 = 5, 50 m = 100 #设置可变学习率 def learning_schedule(t): return t0 / (t + t1) #初始化θ theta = np.random.randn(2, 1) #随机梯度降低 learning_rate = 0.1 for epoch in range(n_epochs): for i in range(m): random_index = np.random.randint(m) # 为了解决维度问题,在索引屈指 xi = X_b[random_index:random_index+1] yi = y[random_index:random_index+1] gradients = 2*xi.T.dot(xi.dot(theta)-yi) learning_rate = learning_schedule(epoch*m + i)#随着迭代次数增长,学习率逐渐减少。 theta = theta - learning_rate * gradients print(theta) SGDRegressor

解释下:np.random.seed(1),肯定随机种子,这是否是万年不变的老三样,没有什么变化,而后咱们仍是n_iterations,总共迭代一千次。n_epochs = 1000。为何是双重for循环?我细致的给你们说,首先我是否是随机梯度降低,我要有一个随机,我要随机选出一条数据来,我在这一部分

random_index = np.random.randint(m) xi = X_b[random_index:random_index+1] yi = y[random_index:random_index+1]

都是在随机的选X和Y,randint(m)表明从0到99随机选出一个数字来做为index,做为索引,选出来以后,个人X是否是要从X里边把这个随机位的索引给取出来,因此我取出来X的索引。那么Y是否是为了随机取出来索引,这两个xi = X_b[random_index:random_index+1], yi = y[random_index:random_index+1]就是把对应的那一条X和对应的Y给搞出来。那么梯度就经过那一个X乘以了一个Y,  gradients = 2*xi.T.dot(xi.dot(theta)-yi)获得了单个计算出来的梯度,你说这个表达式怎么没变,表达式是不用变,只不过原来的X矩阵是一百行两列,如今X矩阵变成一行两列,你表达式是不用变的,一行自动指出来一个数就再也不是一个向量了,那么此时用learning_rate,我原来是否是定死了就是0.1,而如今个人learning_rate变成了learning_schedule返回的一个结果,我看我定义的learning_schedule是什么?

def learning_schedule(t): return t0 / (t + t1)

定义了两个超参数,分别是t0和t1,你丢进一个t来,你看t越大,return这个值会怎么样? 越小,那么咱们看t总共能到多少?是否是epoch*m+i, epoch从哪来的?是否是从n_epochs来的,也就是上来循环第一次的时候它是多少?0,此时你看return结果这算算是多少,是否是就是零?那么你看此时的t是零,此时的learning_schedule返回的是一个多大的数,是否是0.1?也就是第一次执行的时候学习率是多少?0.1。当我内层循环第101次执行的时候此时epoch等于多少? 等于1。epoch*m+i愈来愈大,那么此时的学习率learning_schedule是上升了仍是降低了?变大了仍是变小了? 变小了一点,也就是说随着迭代的加深,epoch是否是愈来愈大?传到这里边数也愈来愈大,学习率是愈来愈小的,因此这个也是梯度降低的一种变种。它会把学习率随着迭代的次数推动,让学习率愈来愈小,这样保证你就能够设置一个初始的时候比较大的学习率,这样你学习率万一设大了,它也不会一直震荡越远,由于随着迭代越多,梯度愈来愈小。在咱们sklearn 里面的SGDRegressor函数是有相关的超参数能够设置的。

def __init__(self, loss="squared_loss", penalty="l2", alpha=0.0001, l1_ratio=0.15, fit_intercept=True, max_iter=None, tol=None, shuffle=True, verbose=0, epsilon=DEFAULT_EPSILON, random_state=None, learning_rate="invscaling", eta0=0.01, power_t=0.25, warm_start=False, average=False, n_iter=None): super(SGDRegressor, self).__init__(loss=loss, penalty=penalty, alpha=alpha, l1_ratio=l1_ratio, fit_intercept=fit_intercept, max_iter=max_iter, tol=tol, shuffle=shuffle, verbose=verbose, epsilon=epsilon, random_state=random_state, learning_rate=learning_rate, eta0=eta0, power_t=power_t, warm_start=warm_start, average=average, n_iter=n_iter)

loss="squared_loss",这个是什么? 就是说SGDRegressor是一个通用的机器学习的方式,你能够本身告诉他个人损失函数是什么,你甭管损失函数是什么,我给你经过SG的方向一直降低获得一个结果,你要把MSE传给我,你获得就是一个线性回归的结果,你要把一个mse加L2正则的损失函数给我,我获得的就是一个岭回归的结果,就损失函数不一样,你的算法其实就改变了,那么在SGD它是不捆绑算法自己的,你给我什么损失函数执行出来什么样,我就是一个帮你降低作计算题的机器。那么默认的就是squared_loss,alpha其实是指的l1跟l2中间的一个超参数,l1_ratio也同样,这两个超参数是依附在penalty之上的一个超参数,我们讲完了正则化以后,你就明它什么意思了。

fit_intercept=True,这个什么意思,截距是否是要帮你搞出来。max_iter=None,什么意思?最大迭代次数,它没设。tol是什么意思,收敛次数。shuffle=True,shuffle什么意思?shuffle实际上把数据乱序。你上来不是给了我一堆X吗?我帮你先洗个牌乱一下序再进行训练,对于我们这种算法来讲是否乱序不影响最终计算结果。random_state=None就是随机种子.learning_rate="invscaling", learning_rate是学习率,那么看learning_rate都有哪些能够选择的地方?constant什么意思?常数,那么此时的eta学习率一般也用eta表示就等于eta0,后边是否是还有一个eta0,若是你在这写一个learning_rate=constant,后边eta0赋一个值,实际上就是一个定值的学习率。而后它有两种变种的,一个叫optimal,优化的,用1.0/(alpha*(t+t0))。另一个是什么?T的power T次方,就是T的N次方,这种方式叫invscaling。它们都是差很少,它们都是想让这学习率越往下走越小,这么一种可变学习率的调整方式,那么默认是使用invscaling倒置缩放的这种方式来作的,也就是说实际上咱们如今就了解到,在sklearn中并无使用这种定值的学习率,默认的实现里面,并非使用固定的学习率来作梯度降低的,而是使用这种可变学习率来作的。

聊了这么多梯度降低的逻辑和过程,有没有对其底层原理感兴趣,因此下一节咱们将讲解梯度降低的底层原理。

相关文章
相关标签/搜索