目录

1. 普通学习 vs 增量学习

1.1 普通学习

1.2 增量学习

2. 增量学习在Kaggle数据上的应用


        sklearn作为早期开源的机器学习算法库,并未开放接入GPU进行运算的接口,即sklearn中的所有算法都不支持接入更多计算资源。因此当我们想要使用随机森林在巨量数据上进行运算时,很可能会遭遇计算资源短缺的情况。幸运的是,我们有两种方式解决这个问题:

        

大批量缺失使用随机森林预测 随机森林数据预处理_机器学习

 使用其它可以接入GPU的机器学习算法库实现随机森林,比如xgboost。        

大批量缺失使用随机森林预测 随机森林数据预处理_机器学习

 继续使用sklearn进行训练,但使用增量学习(incremental learning)。

        增量学习是机器学习中非常常见的方法,在有监督和无监督学习当中都普遍存在。增量学习允许算法不断接入新数据来拓展当前的模型,即允许巨量数据被分成若干个子集,分别输入模型进行训练。

1. 普通学习 vs 增量学习

1.1 普通学习

        通常来说,当一个模型经过一次训练之后,如果再使用新数据对模型进行训练,原始数据训练出的模型会被替代掉。举个例子,现导入两个数据集,一个是加利福尼亚房价数据集,一个是kaggle房价数据集。

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.ensemble import RandomForestRegressor as RFR
from sklearn.tree import DecisionTreeRegressor as DTR
from sklearn.model_selection import cross_validate,KFold
from sklearn.datasets import fetch_california_housing
from sklearn.metrics import mean_squared_error

# 加利福尼亚房价数据集
data=pd.read_csv('F:\\Jupyter Files\\机器学习进阶\\集成学习\\datasets\\House Price\\train_encode.csv',encoding='utf-8')
data.drop('Unnamed: 0', axis=1, inplace=True)
x=data.iloc[:,:-1]
y=data.iloc[:,-1]

x.shape  #(1460, 80)

# 加利福尼亚房价数据集
X_fc = fetch_california_housing().data
y_fc = fetch_california_housing().target

X_fc.shape  #(20640, 8)

大批量缺失使用随机森林预测 随机森林数据预处理_机器学习

 在加利福尼亚房价数据集上进行训练:

model = RFR(n_estimators=3, warm_start=False) #不支持增量学习的
model1 = model.fit(X_fc,y_fc)
#RMSE
(mean_squared_error(y_fc,model1.predict(X_fc)))**0.5


0.30123985583215596


大批量缺失使用随机森林预测 随机森林数据预处理_机器学习

 查看森林中所有树的情况,可以看到每一棵树的随机数种子:

model1.estimators_


[DecisionTreeRegressor(max_features='auto', random_state=1785210460), DecisionTreeRegressor(max_features='auto', random_state=121562514), DecisionTreeRegressor(max_features='auto', random_state=1271073231)]


大批量缺失使用随机森林预测 随机森林数据预处理_机器学习

 让model1继续在kaggle房价数据集x,y上进行训练:

model1 = model1.fit(x.iloc[:,:8],y)
#注意!! x有80个特征,X_fc只有8个特征,输入同一个模型的数据必须结构一致
model1.estimators_


[DecisionTreeRegressor(max_features='auto', random_state=349555903), DecisionTreeRegressor(max_features='auto', random_state=1253222501), DecisionTreeRegressor(max_features='auto', random_state=2145441582)]


model1中原始的树消失了,新的树替代了原始的树。

大批量缺失使用随机森林预测 随机森林数据预处理_机器学习

 再让model1对加利福尼亚房价数据集进行训练:

(mean_squared_error(y_fc,model1.predict(X_fc)))**0.5


235232.2375340384


RMSE异常巨大,模型现在已经不具备任何预测y_fc的能力了。非常明显,model1中原始的树消失了,基于kaggle数据集训练的树覆盖了原始的树,因此model1不再对本来见过的加利福尼亚房价数据报有记忆。

        sklearn的这一覆盖规则是交叉验证可以进行的基础,正因为每次训练都不会受到上次训练的影响,我们才可以使用模型进行交叉验证,否则就会存在数据泄露的情况。但在增量学习中,原始数据训练的树不会被替代掉,模型会一致记得之前训练过的数据。

1.2 增量学习

大批量缺失使用随机森林预测 随机森林数据预处理_机器学习

 在加利福尼亚房价数据集上进行训练:

# 增量学习
model = RFR(n_estimators=3, warm_start=True) #支持增量学习
model2 = model.fit(X_fc,y_fc)
model2.estimators_


[DecisionTreeRegressor(max_features=1.0, random_state=1192338237), DecisionTreeRegressor(max_features=1.0, random_state=506683268), DecisionTreeRegressor(max_features=1.0, random_state=654939120)]


(mean_squared_error(y_fc,model2.predict(X_fc)))**0.5


0.29385313927085455


大批量缺失使用随机森林预测 随机森林数据预处理_机器学习

 让model2继续在kaggle房价数据集x,y上进行训练:

model2 = model2.fit(x.iloc[:,:8],y)
model2.estimators_


[DecisionTreeRegressor(max_features=1.0, random_state=1192338237), DecisionTreeRegressor(max_features=1.0, random_state=506683268), DecisionTreeRegressor(max_features=1.0, random_state=654939120)]


在增量学习当中,树没有发生变化

大批量缺失使用随机森林预测 随机森林数据预处理_机器学习

 再让model2对加利福尼亚房价数据集进行训练:

(mean_squared_error(y_fc,model2.predict(X_fc)))**0.5


0.29385313927085455


即便已经对x和y进行了训练,但是model2中对加利福尼亚房价数据集的记忆还在,因此在对X_fc与y_fc进行预测时,依然能够取得不错的分数。

        在增量学习当中,树没有发生变化,已经训练过的结果会被保留。对于随机森林这样的Bagging模型来说,这意味着之前的数据训练出的树会被保留,新数据会训练出新的树,新旧树互不影响。

        不过,这里存在一个问题:虽然原来的树没有变化,但增量学习看起来并没有增加新的树,事实上,对于随机森林而言,需要手动增加新的树:

#调用模型的参数,可以通过这种方式修改模型的参数,而不需要重新实例化模型
model2.n_estimators += 2 #增加2棵树,用于增量学习
model2.fit(x.iloc[:,:8],y)
model2.estimators_ #原来的树还是没有变化,新增的树是基于新输入的数据进行训练的


[DecisionTreeRegressor(max_features=1.0, random_state=1192338237), DecisionTreeRegressor(max_features=1.0, random_state=506683268), DecisionTreeRegressor(max_features=1.0, random_state=654939120), DecisionTreeRegressor(max_features=1.0, random_state=1440840641), DecisionTreeRegressor(max_features=1.0, random_state=1050229920)]


2. 增量学习在Kaggle数据上的应用

        在面对大型数据时,我们采用循环模式分批读取巨大csv或数据库文件中的内容,并将数据分批进行预处理、再增量学习到一个模型当中。

STEP1:定义训练和测试数据地址

trainpath = r"F:\Jupyter Files\机器学习进阶\集成学习\datasets\Big data\bigdata_train.csv"
testpath = r"F:\Jupyter Files\机器学习进阶\集成学习\datasets\Big data\bigdata_test.csv"

STEP2:设法找出csv中的总数据量

当我们决定使用增量学习时,数据应该是巨大到不可能直接打开查看、不可能直接训练、甚至不可能直接导入的(比如,超过20个G)。但如果我们需要对数据进行循环导入,就必须知道真实的数据量大概有多少,因此我们可以从以下途径获得无法打开的csv中的数据量:

  • 如果是比赛数据集,一般可以在比赛页面找到相应的说明
  • 如果是数据库数据集,则可以在数据库中进行统计
  • 如果无法找到相应的说明,可以使用deque库导入csv文件的最后几行,查看索引
  • 如果数据没有索引,就只能够靠pandas尝试找出大致的数据范围了

方式一:数据集存在索引

#使用deque与StringIO辅助,导入csv文件最后的n行
from collections import deque #deque:双向队列
from io import StringIO
with open(trainpath, 'r') as data:
    q = deque(data, 5)
pd.read_csv(StringIO(''.join(q)), header=None)

0

1

2

3

4

5

6

7

8

9

...

101

102

103

104

105

106

107

108

109

110

0

995029

3.0

3.0

5.0

5.0

2.0

3.0

2.0

5.0

5.0

...

291658.0

666.0

469.0

37.0

1954.0

33.0

0.0

41.0

865.0

-70.6503

1

995030

2.0

4.0

4.0

2.0

4.0

2.0

4.0

4.0

4.0

...

968800.0

666.0

469.0

6.0

208.0

30.0

0.0

208.0

19838.0

-123.0867

2

995031

2.0

1.0

3.0

2.0

5.0

1.0

5.0

4.0

4.0

...

567037.0

93.0

541.0

596.0

2892.0

1602.0

0.0

144.0

2745.0

112.5000

3

995032

1.0

4.0

1.0

5.0

2.0

2.0

1.0

5.0

2.0

...

989963.0

57.0

441.0

13.0

520.0

29.0

0.0

208.0

10546.0

-97.0000

4

995033

3.0

2.0

4.0

3.0

4.0

2.0

4.0

3.0

4.0

...

443675.0

36.0

272.0

3.0

285.0

15.0

0.0

208.0

9322.0

-76.3729

可以看到最后一行的索引是995033,因此训练集中有99w条数据。

方式二:数据集没有索引

如果数据没有索引,则使用pandas中的skiprows与nrows进行尝试。skiprows: 本次导入跳过前skiprows行。nrows:本次导入只导入nrows行。例如,当skiprows=1000, nrows=1000时,pandas会导入1001~2000行。当skiprows超出数据量时,就会报空值错误EmptyDataError。

for i in range(0,10**7,100000):
    df = pd.read_csv(trainpath,skiprows=i, nrows=1)
    print(i)


0100000 200000 300000 400000 500000 600000 700000 800000 900000


---------------------------------------------------------------------------EmptyDataError Traceback (most recent call last)


可以看到90w顺利导入了,但是100w报错了,所以数据量在90-100w之间。还可以继续精确数据量的具体范围,但通常来说我们只要确认10w以内的区域就可以了。

STEP3:确认数据量后,准备循环范围

looprange = range(0,10**6,50000)

STEP4:建立增量学习使用的模型,定义测试集

reg = RFR(n_estimators=10
          ,random_state=1412
          ,warm_start=True
          ,verbose=True #增量学习的过程总是很漫长的,你可以选择展示学习过程
         )
#定义测试集
test = pd.read_csv(testpath,header="infer",index_col=0)
Xtest = test.iloc[:,:-1]
Ytest = test.iloc[:,-1]

STEP5:开始循环导入与增量学习

注:当skiprows+nrows超出数据量的时候,会导出全部剩下的数据。

for line in looprange:
    if line == 0:
        #首次读取时,保留列名,并且不增加树的数量
        header = "infer"
        newtree = 0
    else:
        #非首次读取时,不要列名,每次增加10棵树
        header = None
        newtree = 10
    
    trainsubset = pd.read_csv(trainpath, header = header, index_col=0, skiprows=line, nrows=50000)
    Xtrain = trainsubset.iloc[:,:-1]
    Ytrain = trainsubset.iloc[:,-1]
    reg.n_estimators += newtree
    reg = reg.fit(Xtrain,Ytrain)
    print("DONE",line+50000)
        
    #当训练集的数据量小于50000时,打断循环
    if Xtrain.shape[0] < 50000:
        break

全部的数据都已经训练完毕后,在测试集上进行测试:

reg.score(Xtest,Ytest)


0.9903482355083931


        当使用增量学习时,如果需要调参,我们则需要将增量学习循环打包成一个评估器或函数,以便在调参过程中不断调用,这个过程所需的计算量是异常大的,不过至少我们拥有了在CPU上训练巨大数据的方法。