随机森林算法的简单总结及python实现

  随机森林是数据挖掘中非常常用的分类预测算法,以分类或回归的决策树为基分类器。算法的一些基本要点:

  *对大小为m的数据集进行样本量同样为m的有放回抽样;

        *对K个特征进行随机抽样,形成特征的子集,样本量的确定方法可以有平方根、自然对数等;

        *每棵树完全生成,不进行剪枝;

        *每个样本的预测结果由每棵树的预测投票生成(回归的时候,即各棵树的叶节点的平均)


  著名的python机器学习包scikit learn的文档对此算法有比较详尽的介绍: http://scikit-learn.org/stable/modules/ensemble.html#random-forests

  出于个人研究和测试的目的,基于经典的Kaggle 101 泰坦尼克号乘客的数据集,建立模型并进行评估。比赛页面及相关数据集的下载:https://www.kaggle.com/c/titanic

  泰坦尼克号的沉没,是历史上非常著名的海难。突然感到,自己面对的不再是冷冰冰的数据,而是用数据挖掘的方法,去研究具体的历史问题,也是饶有兴趣。言归正传,模型的主要的目标,是希望根据每个乘客的一系列特征,如性别、年龄、舱位、上船地点等,对其是否能生还进行预测,是非常典型的二分类预测问题。数据集的字段名及实例如下:


PassengerIdSurvivedPclassNameSexAgeSibSpParchTicketFareCabinEmbarked
103Braund, Mr. Owen Harrismale2210A/5 211717.25 S
211Cumings, Mrs. John Bradley (Florence Briggs Thayer)female3810PC 1759971.2833C85C
313Heikkinen, Miss. Lainafemale2600STON/O2. 31012827.925 S
411Futrelle, Mrs. Jacques Heath (Lily May Peel)female351011380353.1C123S
503Allen, Mr. William Henrymale35003734508.05 S

值得说明的是,SibSp是指sister brother spouse,即某个乘客随行的兄弟姐妹、丈夫、妻子的人数,Parch指parents,children


下面给出整个数据处理及建模过程,基于ubuntu+python 3.4 ( anaconda科学计算环境已经集成一系列常用包,pandas numpy sklearn等,这里强烈推荐)

懒得切换输入法,写的时候主要的注释都是英文,中文的注释是后来补充的 :-)

# -*- coding: utf-8 -*-
"""
@author: kim
"""

from model import * #载入基分类器的代码

#ETL:same procedure to training set and test set
training=pd.read_csv('train.csv',index_col=0)
test=pd.read_csv('test.csv',index_col=0)
SexCode=pd.DataFrame([1,0],index=['female','male'],columns=['Sexcode'])  #将性别转化为01
training=training.join(SexCode,how='left',on=training.Sex)
training=training.drop(['Name','Ticket','Embarked','Cabin','Sex'],axis=1) #删去几个不参与建模的变量,包括姓名、船票号,船舱号
test=test.join(SexCode,how='left',on=test.Sex)
test=test.drop(['Name','Ticket','Embarked','Cabin','Sex'],axis=1)
print('ETL IS DONE!')


#MODEL FITTING
#===============PARAMETER AJUSTMENT============
min_leaf=1
min_dec_gini=0.0001
n_trees=5
n_fea=int(math.sqrt(len(training.columns)-1))
#==============================================

'''
BEST SCORE:0.83
min_leaf=30
min_dec_gini=0.001
n_trees=20
'''

#ESSEMBLE BY RANDOM FOREST
FOREST={}
tmp=list(training.columns)
tmp.pop(tmp.index('Survived'))
feaList=pd.Series(tmp)
for t in range(n_trees):
#    fea=[]
    feasample=feaList.sample(n=n_fea,replace=False)#select feature 
    fea=feasample.tolist()
    fea.append('Survived')
#        feaNew=fea.append(target)
    subset=training.sample(n=len(training),replace=True) #generate the dataset with replacement
    subset=subset[fea]
#    print(str(t)+' Classifier built on feature:')
#    print(list(fea))
    FOREST[t]=tree_grow(subset,'Survived',min_leaf,min_dec_gini) #save the tree


#MODEL PREDICTION
#======================
currentdata=training
output='submission_rf_20151116_30_0.001_20'
#======================

prediction={}
for r in currentdata.index:#a row
    prediction_vote={1:0,0:0}
    row=currentdata.get(currentdata.index==r)
    for n in range(n_trees):
        tree_dict=FOREST[n] #a tree
        p=model_prediction(tree_dict,row)
        prediction_vote[p]+=1
    vote=pd.Series(prediction_vote)
    prediction[r]=list(vote.order(ascending=False).index)[0]#the vote result
result=pd.Series(prediction,name='Survived_p')
#del prediction_vote
#del prediction


#result.to_csv(output)


t=training.join(result,how='left')
accuracy=round(len(t[t['Survived']==t['Survived_p']])/len(t),5)
print(accuracy)



上述是随机森林的代码,如上所述,随机森林是一系列决策树的组合,决策树每次分裂,用Gini系数衡量当前节点的“不纯净度”,如果按照某个特征的某个分裂点对数据集划分后,能够让数据集的Gini下降最多(显著地减少了数据集输出变量的不纯度),则选为当前最佳的分割特征及分割点。代码如下:


# -*- coding: utf-8 -*-
"""
@author: kim
"""

import pandas as pd
import numpy as np
#import sklearn as sk
import math


def tree_grow(dataframe,target,min_leaf,min_dec_gini):
    
    tree={} #renew a tree
    is_not_leaf=(len(dataframe)>min_leaf)
    if is_not_leaf:
        fea,sp,gd=best_split_col(dataframe,target)
        if gd>min_dec_gini:
            tree['fea']=fea
            tree['val']=sp
#            dataframe.drop(fea,axis=1) #1116 modified
            l,r=dataSplit(dataframe,fea,sp)
            l.drop(fea,axis=1)
            r.drop(fea,axis=1)
            tree['left']=tree_grow(l,target,min_leaf,min_dec_gini)
            tree['right']=tree_grow(r,target,min_leaf,min_dec_gini)
        else:#return a leaf
            return leaf(dataframe[target])
    else:
        return leaf(dataframe[target])

    return tree
        
        
def leaf(class_lable):
    
    tmp={}
    for i in class_lable:
        if i in tmp:
            tmp[i]+=1
        else:
            tmp[i]=1
    s=pd.Series(tmp)
    s.sort(ascending=False)
    
    return s.index[0]


def gini_cal(class_lable):
    
    p_1=sum(class_lable)/len(class_lable)
    p_0=1-p_1
    gini=1-(pow(p_0,2)+pow(p_1,2))
    
    return gini


def dataSplit(dataframe,split_fea,split_val):
    
    left_node=dataframe[dataframe[split_fea]<=split_val]
    right_node=dataframe[dataframe[split_fea]>split_val]
    
    return left_node,right_node


def best_split_col(dataframe,target_name):
    best_fea=''#modified 1116
    best_split_point=0 
    col_list=list(dataframe.columns)
    col_list.remove(target_name)
    gini_0=gini_cal(dataframe[target_name])
    n=len(dataframe)
    gini_dec=-99999999
    for col in col_list:
        node=dataframe[[col,target_name]]
        unique=node.groupby(col).count().index
        for split_point in unique: #unique value
            left_node,right_node=dataSplit(node,col,split_point)
            if len(left_node)>0 and len(right_node)>0:
                gini_col=gini_cal(left_node[target_name])*(len(left_node)/n)+gini_cal(right_node[target_name])*(len(right_node)/n)
                if (gini_0-gini_col)>gini_dec:                 
                    gini_dec=gini_0-gini_col#decrease of impurity
                    best_fea=col                    
                    best_split_point=split_point           
        #print(col,split_point,gini_0-gini_col)
                    
    return best_fea,best_split_point,gini_dec
    

def model_prediction(model,row): #row is a df

    fea=model['fea']
    val=model['val']
    left=model['left']
    right=model['right']
    if row[fea].tolist()[0]<=val:#get the value
        branch=left
    else:
        branch=right
    if ('dict' in str( type(branch) )):
        prediction=model_prediction(branch,row)
    else:
        prediction=branch

    return prediction

实际上,上面的代码还有很大的效率提升的空间,数据集不是很大的情况下,如果选择一个较大的输入参数,例如生成100棵树,就会显著地变慢;同时,将预测结果提交至kaggle进行评测,发现在测试集上的正确率不是很高,比使用sklearn里面相应的包进行预测的正确率(0.77512)要稍低一点 :-(  如果要提升准确率,两个大方向: 构造新的特征;调整现有模型的参数。

这里是抛砖引玉,欢迎大家对我的建模思路和算法的实现方法提出修改意见。

 

  • 6
    点赞
  • 43
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
好的,我了解了你的要求。对于你的问题,我会分两个部分来回答。 首先,关于随机森林算法的编写,我们可以使用Python中的NumPy库来实现。以下是一个简单的示例代码: ```python import numpy as np # 导入数据 data = fetch_california_housing() X = data.data y = data.target # 划分训练集和测试集 train_size = int(X.shape[0] * 0.7) X_train, y_train = X[:train_size], y[:train_size] X_test, y_test = X[train_size:], y[train_size:] # 构建随机森林回归模型 n_estimators = 100 max_depth = 10 min_samples_split = 2 min_samples_leaf = 1 max_features = int(np.sqrt(X.shape[1])) models = [] for i in range(n_estimators): # 随机采样训练集 idx = np.random.choice(train_size, train_size, replace=True) X_train_i, y_train_i = X_train[idx], y_train[idx] # 构建决策树 tree = DecisionTreeRegressor(max_depth=max_depth, min_samples_split=min_samples_split, min_samples_leaf=min_samples_leaf, max_features=max_features) # 拟合决策树 tree.fit(X_train_i, y_train_i) # 将决策树添加到模型列表中 models.append(tree) # 预测结果 y_pred = np.zeros(X_test.shape[0]) for tree in models: y_pred += tree.predict(X_test) y_pred /= n_estimators # 模型评分 score = r2_score(y_test, y_pred) print(f"模型评分:{score}") ``` 在这个示例中,我们使用了NumPy库来构建随机森林回归模型。我们首先将数据集划分为训练集和测试集,然后在训练集上随机采样,构建多个决策树。我们使用了scikit-learn库中的`DecisionTreeRegressor`类来构建决策树。在预测时,我们将所有决策树的预测结果取平均值作为最终预测结果。我们还使用了scikit-learn库中的`r2_score`函数来计算模型的评分。 接下来,我们来展示模型评分,并对比scikit-learn自带的评估器的建模结果。 对于加利福尼亚房价数据集,我们使用scikit-learn库中自带的随机森林回归器进行建模和预测: ```python from sklearn.ensemble import RandomForestRegressor from sklearn.datasets import fetch_california_housing from sklearn.model_selection import train_test_split from sklearn.metrics import r2_score # 导入数据 data = fetch_california_housing() X = data.data y = data.target # 划分训练集和测试集 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42) # 构建随机森林回归模型 rf = RandomForestRegressor(n_estimators=100, random_state=42) # 拟合模型 rf.fit(X_train, y_train) # 预测结果 y_pred = rf.predict(X_test) # 模型评分 score = r2_score(y_test, y_pred) print(f"模型评分:{score}") ``` 注意,在这个示例中,我们使用了scikit-learn库中的`train_test_split`函数将数据集划分为训练集和测试集。我们还使用了scikit-learn库中的`r2_score`函数来计算模型的评分。 总结一下,我们展示了如何使用NumPy库来编写随机森林算法,并在加利福尼亚房价数据集上进行了模型训练和预测。我们还展示了如何计算模型的评分,并与scikit-learn自带的评估器的建模结果进行了对比。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值