Python實(shí)現(xiàn)的隨機(jī)森林算法與簡單總結(jié)
本文實(shí)例講述了Python實(shí)現(xiàn)的隨機(jī)森林算法。分享給大家供大家參考,具體如下:
隨機(jī)森林是數(shù)據(jù)挖掘中非常常用的分類預(yù)測算法,以分類或回歸的決策樹為基分類器。算法的一些基本要點(diǎn):
*對大小為m的數(shù)據(jù)集進(jìn)行樣本量同樣為m的有放回抽樣;
*對K個(gè)特征進(jìn)行隨機(jī)抽樣,形成特征的子集,樣本量的確定方法可以有平方根、自然對數(shù)等;
*每棵樹完全生成,不進(jìn)行剪枝;
*每個(gè)樣本的預(yù)測結(jié)果由每棵樹的預(yù)測投票生成(回歸的時(shí)候,即各棵樹的葉節(jié)點(diǎn)的平均)
著名的python機(jī)器學(xué)習(xí)包scikit learn的文檔對此算法有比較詳盡的介紹: http://scikit-learn.org/stable/modules/ensemble.html#random-forests
出于個(gè)人研究和測試的目的,基于經(jīng)典的Kaggle 101泰坦尼克號乘客的數(shù)據(jù)集,建立模型并進(jìn)行評估。比賽頁面及相關(guān)數(shù)據(jù)集的下載:https://www.kaggle.com/c/titanic
泰坦尼克號的沉沒,是歷史上非常著名的海難。突然感到,自己面對的不再是冷冰冰的數(shù)據(jù),而是用數(shù)據(jù)挖掘的方法,去研究具體的歷史問題,也是饒有興趣。言歸正傳,模型的主要的目標(biāo),是希望根據(jù)每個(gè)乘客的一系列特征,如性別、年齡、艙位、上船地點(diǎn)等,對其是否能生還進(jìn)行預(yù)測,是非常典型的二分類預(yù)測問題。數(shù)據(jù)集的字段名及實(shí)例如下:
PassengerId | Survived | Pclass | Name | Sex | Age | SibSp | Parch | Ticket | Fare | Cabin | Embarked |
1 | 0 | 3 | Braund, Mr. Owen Harris | male | 22 | 1 | 0 | A/5 21171 | 7.25 | S | |
2 | 1 | 1 | Cumings, Mrs. John Bradley (Florence Briggs Thayer) | female | 38 | 1 | 0 | PC 17599 | 71.2833 | C85 | C |
3 | 1 | 3 | Heikkinen, Miss. Laina | female | 26 | 0 | 0 | STON/O2. 3101282 | 7.925 | S | |
4 | 1 | 1 | Futrelle, Mrs. Jacques Heath (Lily May Peel) | female | 35 | 1 | 0 | 113803 | 53.1 | C123 | S |
5 | 0 | 3 | Allen, Mr. William Henry | male | 35 | 0 | 0 | 373450 | 8.05 | S |
值得說明的是,SibSp是指sister brother spouse,即某個(gè)乘客隨行的兄弟姐妹、丈夫、妻子的人數(shù),Parch指parents,children
下面給出整個(gè)數(shù)據(jù)處理及建模過程,基于ubuntu+python 3.4( anaconda科學(xué)計(jì)算環(huán)境已經(jīng)集成一系列常用包,pandas numpy sklearn等,這里強(qiáng)烈推薦)
懶得切換輸入法,寫的時(shí)候主要的注釋都是英文,中文的注釋是后來補(bǔ)充的:-)
# -*- 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']) #將性別轉(zhuǎn)化為01 training=training.join(SexCode,how='left',on=training.Sex) training=training.drop(['Name','Ticket','Embarked','Cabin','Sex'],axis=1)#刪去幾個(gè)不參與建模的變量,包括姓名、船票號,船艙號 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)
上述是隨機(jī)森林的代碼,如上所述,隨機(jī)森林是一系列決策樹的組合,決策樹每次分裂,用Gini系數(shù)衡量當(dāng)前節(jié)點(diǎn)的“不純凈度”,如果按照某個(gè)特征的某個(gè)分裂點(diǎn)對數(shù)據(jù)集劃分后,能夠讓數(shù)據(jù)集的Gini下降最多(顯著地減少了數(shù)據(jù)集輸出變量的不純度),則選為當(dāng)前最佳的分割特征及分割點(diǎn)。代碼如下:
# -*- 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
實(shí)際上,上面的代碼還有很大的效率提升的空間,數(shù)據(jù)集不是很大的情況下,如果選擇一個(gè)較大的輸入?yún)?shù),例如生成100棵樹,就會顯著地變慢;同時(shí),將預(yù)測結(jié)果提交至kaggle進(jìn)行評測,發(fā)現(xiàn)在測試集上的正確率不是很高,比使用sklearn里面相應(yīng)的包進(jìn)行預(yù)測的正確率(0.77512)要稍低一點(diǎn) :-( 如果要提升準(zhǔn)確率,兩個(gè)大方向: 構(gòu)造新的特征;調(diào)整現(xiàn)有模型的參數(shù)。
這里是拋磚引玉,歡迎大家對我的建模思路和算法的實(shí)現(xiàn)方法提出修改意見。
更多關(guān)于Python相關(guān)內(nèi)容感興趣的讀者可查看本站專題:《Python數(shù)據(jù)結(jié)構(gòu)與算法教程》、《Python編碼操作技巧總結(jié)》、《Python函數(shù)使用技巧總結(jié)》、《Python字符串操作技巧匯總》及《Python入門與進(jìn)階經(jīng)典教程》
希望本文所述對大家Python程序設(shè)計(jì)有所幫助。
相關(guān)文章
盤點(diǎn)Python中讀取和提取JSON文件的4種方法
JSON(JavaScript?Object?Notation)是一種輕量級的數(shù)據(jù)交換格式,Python中提供了多種方式來讀取和處理JSON文件,本文將詳細(xì)介紹四種常見的方法,希望對大家有所幫助2024-03-03python爬蟲多次請求超時(shí)的幾種重試方法(6種)
這篇文章主要介紹了python爬蟲多次請求超時(shí)的幾種重試方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12Python連接PostgreSQL數(shù)據(jù)庫的方法
大家應(yīng)該都有所了解,python可以操作多種數(shù)據(jù)庫,諸如SQLite、MySql、PostgreSQL等,這里不對所有的數(shù)據(jù)庫操作方法進(jìn)行贅述,只針對目前項(xiàng)目中用到的PostgreSQL做一下簡單介紹,主要是Python連接PostgreSQL數(shù)據(jù)庫的方法。有需要的朋友們可以參考借鑒,下面來一起看看吧。2016-11-11python獲取指定時(shí)間段內(nèi)特定規(guī)律的日期列表
這篇文章主要介紹了python獲取指定時(shí)間段內(nèi)特定規(guī)律的日期列表,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-04-04python中property屬性的介紹及其應(yīng)用詳解
這篇文章主要介紹了python中property屬性的介紹及其應(yīng)用詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-08-08Manjaro、pip、conda更換國內(nèi)源的方法
這篇文章主要介紹了Manjaro、pip、conda更換國內(nèi)源的方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11Python實(shí)現(xiàn)備份MySQL數(shù)據(jù)庫的方法示例
這篇文章主要介紹了Python實(shí)現(xiàn)備份MySQL數(shù)據(jù)庫的方法,涉及Python針對mysql數(shù)據(jù)庫的連接及基于mysqldump命令操作數(shù)據(jù)庫備份的相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下2018-01-01