ML——基于梯度下降算法(SGD)下的多元线性回归问题

承接上篇批量梯度下降算法(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在时间消耗上肯定是有非常明显的优势的。

文章内容若有不妥之处,还请诸位大佬批评指正,谢谢了。

你可能感兴趣的:(ML,SGD,Python,PyCharm)