承接上篇批量梯度下降算法(BGD),文章见:https://blog.csdn.net/CYH00_/article/details/99674407
批量梯度随机下降算法虽然处理数据时考虑更加周全,数据结果更加准确可靠,毕竟BGD每一次数据计算都会遍历一次所有样本数据,所以复杂度是很大的,鉴于此,在数据集更加庞大时,一般很少选择BGD算法,因为效率低,耗时长。此时另一个合适的算法就是随机梯度下降算法(SGD),一次计算只用到一个样本(个人感觉SGD跟多元函数求极值的方法很像),虽然准确度可能达不到BGD那样,但是效率更高,而且因其“随机”,所以陷入局部最小值问题的可能性被大大降低,因此在某些情况下,SGD相比BGD性价比更高。
接下来同样以sklearn里的Boston数据集为例,这次尝试用随机梯度下降算法训练并预测数据。并且将BGD和SGD两种方法求得的结果做一个对比。
一.设计SGD程序如下:
import numpy as np
from linear.simpleline import simplelinear as sim
#采用随机梯度下降算法解多元线性规划
class sgd:
def __init__(self):
self.theat_ = None
self.theat_list = None
self.last_dJ = None
def dJ(self,theat,x_b_i,y_i):
'''计算梯度(传入数据为单个样本)'''
assert len(theat) == len(x_b_i)
theat = np.array(theat)
x_b_i = np.array(x_b_i)
y_i = np.array(y_i)
return 2 * (x_b_i.T) * (x_b_i .dot(theat) - y_i)
def sgradient(self,x_b,y,i_iters): #i_iters为次数
assert len(x_b) == len(y)
assert i_iters >= 1
def Eta(t):
t0 = 5
t1 = 50
return t0 / (t + t1)
x_b = np.array(x_b)
y = np.array(y)
x_b = np.hstack([np.ones((len(x_b),1)),x_b])
ini_theat = np.zeros(x_b.shape[1])
theat = ini_theat
self.theat_list = []
self.theat_list.append(theat)
for i in range(i_iters):
index = np.random.permutation(len(x_b))
x_b_new = x_b[index]
y_new = y[index]
for j in range(len(x_b)):
g = self.dJ(theat,x_b_new[j],y_new[j])
theat = theat - Eta(i * len(x_b) + j) * g
self.theat_list.append(theat)
self.last_dJ = g
self.theat_ = theat
return self.theat_list
def score(self,x_test,y_test): #计算线性相关程度R
assert x_test.shape[0] == y_test.shape[0]
x_test = np.hstack([np.ones((len(x_test),1)),x_test])
y_predict = x_test .dot(self.theat_)
def mse(y_test,y_predict):
return np.sum((y_test - y_predict) ** 2) / len(y_predict)
def R_R(y_test, y_predict):
s = mse(y_test, y_predict)
d = np.var(y_test)
return 1 - s / d
return R_R(y_test,y_predict)
这里解释为什么文章开头时随机二字打上了引号,因为一旦过于随机,比如n次都取到样本1(极端情况可能性很小,但是也有可能)或者取的样本都是前几个。总而言之就是随机取到的样本根本不完全,有很多样本都被随机忽略掉了,所以不具有说服力,因此在设计遍历函数时要考虑这个问题(具体见上述代码中的sgradient函数)。
二.以下是载入数据(Boston)并进行归一化处理:
from cyh.train_test_split import t_t_split #这里用的是自己设计的数据分类函数,也可以采用sklearn里面的train_test_split
from sklearn.preprocessing import StandardScaler
data = datasets.load_boston()
t = data.data
r = data.target
x = t[r < 50]
y = r[r < 50]
x_train,y_train,x_test,y_test = t_t_split(x,y,0.2,666) #自己做实验时最好设置一个随机种子,以免每次输出结果不一样
m = StandardScaler()
m.fit(x_train)
x_train = m.transform(x_train)
m.fit(x_test)
x_test = m.transform(x_test)
三.分别采用BGD和SGD进行预测,并比较结果:
from linear.bgd import gd
from linear.sgd import sgd
n = gd()
n.gra(x_train,y_train,0.01) #批量梯度下降算法
print(n.theat)
m = sgd()
m.sgradient(x_train,y_train,500) #随机梯度下降算法
print(m.theat_)
print(m.score(x_test,y_test)) #计算线性相关程度R
四.结果对比分析:
[21.55382653 -1.0510041 0.90432038 -0.28228354 0.17807247 -1.5876832
2.22136885 -0.68691468 -2.61226707 2.38322627 -2.36377392 -1.81554557
0.4816466 -2.57338312]
[21.55396184 -1.04997861 0.9021548 -0.28909283 0.17894546 -1.58628975
2.22141016 -0.68766406 -2.61303714 2.36859213 -2.34657215 -1.81477483
0.48119676 -2.57290246]
0.801699511338954
分析知BGD和SGD得到结果几乎是一样的,并且线性相关程度较好。在本例中,测试SGD比BGD耗时长(不过并未打脸之前说的,主要是因为为了追求精度,所以我把遍历次数设为500,比较大了,这跟SGD的本意就有点相违背了),但在数据量异常庞大时,SGD相比BGD在时间消耗上肯定是有非常明显的优势的。
文章内容若有不妥之处,还请诸位大佬批评指正,谢谢了。