Python機器學習(二十一)隨機森林算法


一、隨機森林算法簡介:

       在機器學習中,隨機森林是一個包含多個決策樹的分類器, 並且其輸出的類別是由個別樹輸出的類別的眾數而定。 Leo Breiman和Adele Cutler發展出推論出隨機森林的算法。而 "Random Forests" 是他們的商標。 這個術語是1995年由貝爾實驗室的Tin Kam Ho所提出的隨機決策森林(random decision forests)而來的。這個方法則是結合Breimans 的 "Bootstrap aggregating" 想法和 Ho 的"randomsubspace method"以建造決策樹的集合。

       根據下列算法而建造每棵樹 :

1.   用M來表示訓練用例(樣本)的個數,N表示特征數目。

2.   輸入特征數目n,用於確定決策樹上一個節點的決策結果;其中n應遠小於N。

3.   從M個訓練用例(樣本)中以有放回抽樣的方式,取樣k次,形成一個訓練集(即bootstrap取樣),並用未抽到的用例(樣本)作預測,評估其誤差。

4.   對於每一個節點,隨機選擇n個特征,每棵決策樹上每個節點的決定都是基於這些特征確定的。根據這n個特征,計算其最佳的分裂方式。

5.   每棵樹都會完整成長而不會剪枝,這有可能在建完一棵正常樹狀分類器后會被采用。

6.   最后測試數據,根據每棵樹,以多勝少方式決定分類。

     在構建隨機森林時,需要做到兩個方面:數據的隨機性選取,以及待選特征的隨機選取,來消除過擬合問題。

     首先,從原始的數據集中采取有放回的抽樣,構造子數據集,子數據集的數據量是和原始數據集相同的。不同子數據集的元素可以重復,同一個子數據集中的元素也可以重復。第二,利用子數據集來構建子決策樹,將這個數據放到每個子決策樹中,每個子決策樹輸出一個結果。最后,如果有了新的數據需要通過隨機森林得到分類結果,就可以通過對子決策樹的判斷結果的投票,得到隨機森林的輸出結果了。如下圖,假設隨機森林中有3棵子決策樹,2棵子樹的分類結果是A類,1棵子樹的分類結果是B類,那么隨機森林的分類結果就是A類。

     與數據集的隨機選取類似,隨機森林中的子樹的每一個分裂過程並未用到所有的待選特征,而是從所有的待選特征中隨機選取一定的特征,之后再在隨機選取的特征中選取最優的特征。這樣能夠使得隨機森林中的決策樹都能夠彼此不同,提升系統的多樣性,從而提升分類性能。

     優點:

    隨機森林的既可以用於回歸也可以用於分類任務,並且很容易查看模型的輸入特征的相對重要性。隨機森林算法被認為是一種非常方便且易於使用的算法,因為它是默認的超參數通常會產生一個很好的預測結果。超參數的數量也不是那么多,而且它們所代表的含義直觀易懂。

    隨機森林有足夠多的樹,分類器就不會產生過度擬合模型。

    缺點:

    由於使用大量的樹會使算法變得很慢,並且無法做到實時預測。一般而言,這些算法訓練速度很快,預測十分緩慢。越准確的預測需要越多的樹,這將導致模型越慢。在大多數現實世界的應用中,隨機森林算法已經足夠快,但肯定會遇到實時性要求很高的情況,那就只能首選其他方法。當然,隨機森林是一種預測性建模工具,而不是一種描述性工具。也就是說,如果您正在尋找關於數據中關系的描述,那建議首選其他方法。

    適用范圍:

    隨機森林算法可被用於很多不同的領域,如銀行,股票市場,醫葯和電子商務。在銀行領域,它通常被用來檢測那些比普通人更高頻率使用銀行服務的客戶,並及時償還他們的債務。同時,它也會被用來檢測那些想詐騙銀行的客戶。在金融領域,它可用於預測未來股票的趨勢。在醫療保健領域,它可用於識別葯品成分的正確組合,分析患者的病史以識別疾病。除此之外,在電子商務領域中,隨機森林可以被用來確定客戶是否真的喜歡某個產品。

二、sklearn中隨機森林算法應用舉例:

    (1)基本步驟:

    ①選擇數據:將你的數據分成三組:訓練數據、驗證數據和測試數據

    ②模型數據:使用訓練數據來構建使用相關特征的模型

    ③驗證模型:使用你的驗證數據接入你的模型

    ④測試模型:使用你的測試數據檢查被驗證的模型的表現

     ⑤使用模型:使用完全訓練好的模型在新數據上做預測

    ⑥調優模型:使用更多數據、不同的特征或調整過的參數來提升算法的性能表現

    為方便大家使用,代碼如下:

 

# 隨機森林需要調整的參數有:
# (1)    決策樹的個數
# (2)    特征屬性的個數
# (3)    遞歸次數(即決策樹的深度)

import numpy as np
from numpy import *
import random
from sklearn.model_selection  import train_test_split

#生成數據集。數據集包括標簽,全包含在返回值的dataset上
def get_Datasets():
    from sklearn.datasets import make_classification
    dataSet,classLabels=make_classification(n_samples=200,n_features=100,n_classes=2)
    #print(dataSet.shape,classLabels.shape)
    return np.concatenate((dataSet,classLabels.reshape((-1,1))),axis=1)

#切分數據集,實現交叉驗證。可以利用它來選擇決策樹個數。但本例沒有實現其代碼。
#原理如下:
#第一步,將訓練集划分為大小相同的K份;
#第二步,我們選擇其中的K-1分訓練模型,將用余下的那一份計算模型的預測值,
#這一份通常被稱為交叉驗證集;第三步,我們對所有考慮使用的參數建立模型
#並做出預測,然后使用不同的K值重復這一過程。
#然后是關鍵,我們利用在不同的K下平均准確率最高所對應的決策樹個數
#作為算法決策樹個數
def splitDataSet(dataSet,n_folds):
    fold_size=len(dataSet)/n_folds
    data_split=[]
    begin=0
    end=fold_size
    for i in range(n_folds):
        data_split.append(dataSet[begin:end,:])
        begin=end
        end+=fold_size
    return data_split


#構建n個子集
def get_subsamples(dataSet,n):
    subDataSet=[]
    for i in range(n):
        index=[]
        for k in range(len(dataSet)):
            index.append(np.random.randint(len(dataSet)))
        subDataSet.append(dataSet[index,:])
    return subDataSet

#划分數據集
def binSplitDataSet(dataSet,feature,value):
    mat0=dataSet[np.nonzero(dataSet[:,feature]>value)[0],:]
    mat1=dataSet[np.nonzero(dataSet[:,feature]<value)[0],:]
    return mat0,mat1

#計算方差,回歸時使用
def regErr(dataSet):
    return np.var(dataSet[:,-1])*shape(dataSet)[0]
#計算平均值,回歸時使用
def regLeaf(dataSet):
    return np.mean(dataSet[:,-1])
def MostNumber(dataSet):  #返回多類
    #number=set(dataSet[:,-1])
    len0=len(np.nonzero(dataSet[:,-1]==0)[0])
    len1=len(np.nonzero(dataSet[:,-1]==1)[0])
    if len0>len1:
        return 0
    else:
        return 1
#計算基尼指數
def gini(dataSet):
    corr=0.0
    for i in set(dataSet[:,-1]):
        corr+=(len(np.nonzero(dataSet[:,-1]==i)[0])/len(dataSet))**2
    return 1-corr

#選取任意的m個特征,在這m個特征中,選取分割時的最優特征
def select_best_feature(dataSet,m,alpha="huigui"):
    f=dataSet.shape[1]
    index=[]
    bestS=inf;bestfeature=0;bestValue=0;
    if alpha=="huigui":
        S=regErr(dataSet)
    else:
        S=gini(dataSet)
    for i in range(m):
        index.append(np.random.randint(f))
    for feature in index:
        for splitVal in set(dataSet[:,feature]):
            mat0,mat1=binSplitDataSet(dataSet,feature,splitVal)
            if alpha=="huigui":  newS=regErr(mat0)+regErr(mat1)
            else:
                newS=gini(mat0)+gini(mat1)
            if bestS>newS:
                bestfeature=feature
                bestValue=splitVal
                bestS=newS
    if (S-bestS)<0.001 and alpha=="huigui":    #如果誤差不大就退出
        return None,regLeaf(dataSet)
    elif (S-bestS)<0.001:
        #print(S,bestS)
        return None,MostNumber(dataSet)
    #mat0,mat1=binSplitDataSet(dataSet,feature,splitVal)
    return bestfeature,bestValue

def createTree(dataSet,alpha="huigui",m=20,max_level=10):   #實現決策樹,使用20個特征,深度為10
    bestfeature,bestValue=select_best_feature(dataSet,m,alpha=alpha)
    if bestfeature==None:
        return bestValue
    retTree={}
    max_level-=1
    if max_level<0:   #控制深度
        return regLeaf(dataSet)
    retTree['bestFeature']=bestfeature
    retTree['bestVal']=bestValue
    lSet,rSet=binSplitDataSet(dataSet,bestfeature,bestValue)
    retTree['right']=createTree(rSet,alpha,m,max_level)
    retTree['left']=createTree(lSet,alpha,m,max_level)
    #print('retTree:',retTree)
    return retTree

def RondomForest(dataSet,n,alpha="huigui"):   #樹的個數
    #dataSet=get_Datasets()
    Trees=[]
    for i in range(n):
        X_train, X_test, y_train, y_test = train_test_split(dataSet[:,:-1], dataSet[:,-1], test_size=0.33, random_state=42)
        X_train=np.concatenate((X_train,y_train.reshape((-1,1))),axis=1)
        Trees.append(createTree(X_train,alpha=alpha))
    return Trees

#預測單個數據樣本
def treeForecast(tree,data,alpha="huigui"):
    if alpha=="huigui":
        if not isinstance(tree,dict):
            return float(tree)
        if data[tree['bestFeature']]>tree['bestVal']:
            if type(tree['left'])=='float':
                return tree['left']
            else:
                return treeForecast(tree['left'],data,alpha)
        else:
            if type(tree['right'])=='float':
                return tree['right']
            else:
                return treeForecast(tree['right'],data,alpha)
    else:
        if not isinstance(tree,dict):
            return int(tree)
        if data[tree['bestFeature']]>tree['bestVal']:
            if type(tree['left'])=='int':
                return tree['left']
            else:
                return treeForecast(tree['left'],data,alpha)
        else:
            if type(tree['right'])=='int':
                return tree['right']
            else:
                return treeForecast(tree['right'],data,alpha)
#單棵樹預測測試集
def createForeCast(tree,dataSet,alpha="huigui"):
    m=len(dataSet)
    yhat=np.mat(zeros((m,1)))
    for i in range(m):
        yhat[i,0]=treeForecast(tree,dataSet[i,:],alpha)
    return yhat

#隨機森林預測
def predictTree(Trees,dataSet,alpha="huigui"):
    m=len(dataSet)
    yhat=np.mat(zeros((m,1)))
    for tree in Trees:
        yhat+=createForeCast(tree,dataSet,alpha)
    if alpha=="huigui": yhat/=len(Trees)
    else:
        for i in range(len(yhat)):
            if yhat[i,0]>len(Trees)/2:
                yhat[i,0]=1
            else:
                yhat[i,0]=0
    return yhat

if __name__ == '__main__' :
    dataSet=get_Datasets()  #得到數據集和標簽
    print(dataSet[:,-1].T)   #打印標簽,與后面預測值對比
    RomdomTrees=RondomForest(dataSet,4,alpha="fenlei")   #4棵樹,分類。
    print("---------------------RomdomTrees------------------------")
    #print(RomdomTrees[0])
    yhat=predictTree(RomdomTrees,dataSet,alpha="fenlei")
    print(yhat.T)
#get_Datasets()

執行結果:

C:\Anaconda3\python.exe "C:\Program Files\JetBrains\PyCharm 2019.1.1\helpers\pydev\pydevconsole.py" --mode=client --port=56305
import sys; print('Python %s on %s' % (sys.version, sys.platform))
sys.path.extend(['C:\\app\\PycharmProjects', 'C:/app/PycharmProjects'])
Python 3.7.6 (default, Jan  8 2020, 20:23:39) [MSC v.1916 64 bit (AMD64)]
Type 'copyright', 'credits' or 'license' for more information
IPython 7.12.0 -- An enhanced Interactive Python. Type '?' for help.
PyDev console: using IPython 7.12.0
Python 3.7.6 (default, Jan  8 2020, 20:23:39) [MSC v.1916 64 bit (AMD64)] on win32
runfile('C:/app/PycharmProjects/ArtificialIntelligence/test.py', wdir='C:/app/PycharmProjects/ArtificialIntelligence')
[1. 1. 0. 0. 0. 0. 0. 0. 1. 1. 1. 0. 1. 0. 0. 0. 0. 1. 1. 0. 1. 0. 0. 1.
 1. 1. 1. 1. 1. 1. 0. 0. 0. 1. 1. 0. 0. 0. 0. 0. 1. 1. 1. 1. 1. 1. 0. 0.
 0. 1. 0. 1. 1. 1. 0. 0. 1. 1. 0. 1. 0. 1. 1. 0. 1. 1. 1. 0. 1. 0. 1. 1.
 0. 0. 0. 1. 0. 1. 1. 1. 1. 1. 1. 0. 0. 1. 0. 1. 1. 0. 0. 1. 0. 0. 0. 0.
 1. 1. 0. 1. 0. 1. 1. 0. 0. 0. 0. 1. 1. 0. 1. 1. 0. 0. 0. 0. 1. 1. 0. 0.
 1. 1. 1. 0. 0. 1. 0. 1. 0. 1. 0. 1. 0. 0. 1. 1. 0. 0. 0. 0. 1. 1. 1. 0.
 0. 0. 0. 1. 0. 1. 0. 1. 0. 0. 1. 1. 1. 1. 1. 0. 0. 0. 0. 1. 0. 0. 1. 1.
 0. 1. 0. 0. 1. 1. 1. 0. 0. 0. 1. 0. 0. 1. 1. 0. 1. 1. 0. 1. 0. 1. 0. 0.
 1. 1. 1. 1. 0. 0. 1. 0.]
---------------------RomdomTrees------------------------
[[1. 1. 0. 0. 0. 0. 0. 0. 0. 1. 1. 0. 1. 0. 0. 0. 0. 1. 1. 0. 1. 0. 0. 1.
  1. 1. 0. 1. 1. 1. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 1. 1. 1. 1. 1. 1. 0. 0.
  0. 1. 0. 1. 1. 1. 0. 0. 0. 1. 0. 1. 0. 1. 1. 0. 1. 1. 1. 0. 1. 0. 1. 1.
  0. 0. 0. 1. 0. 1. 1. 0. 1. 1. 0. 0. 0. 0. 0. 1. 1. 0. 0. 1. 0. 0. 0. 0.
  1. 1. 0. 1. 0. 1. 1. 0. 0. 0. 0. 1. 0. 0. 1. 1. 0. 0. 0. 0. 0. 1. 0. 0.
  1. 1. 1. 0. 0. 0. 0. 1. 0. 1. 0. 1. 0. 0. 1. 0. 0. 0. 0. 0. 1. 1. 1. 0.
  0. 0. 0. 1. 0. 1. 0. 1. 0. 0. 1. 1. 1. 1. 0. 0. 0. 0. 0. 1. 0. 1. 1. 1.
  0. 1. 0. 0. 1. 1. 1. 0. 0. 0. 1. 0. 0. 1. 1. 0. 1. 1. 0. 1. 0. 1. 0. 0.
  1. 1. 1. 1. 0. 0. 1. 0.]]

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM