Random Forest

#Random Forest——隨機森林 上一篇是講到了決策樹,這篇就來說一下樹的集合,隨機森林。 #①Aggregation Model 隨機森林仍是沒有脫離聚合模型這塊,以前學過兩個aggregation model,bagging和decision tree,一個是邊learning邊uniform。首先是boostrap方式獲得數據D1,以後訓練作平均;另外一個也是邊learning可是作的是condition,直接用數據D作conditional切分。 git

bagging and decision tree
而bagging和decision tree各自都有一個很重要的特色。bagging能夠減小不一樣gt方差的variance的做用,由於bagging是經過各自投票,你們包含的相一樣本都比較多,因此大體不會差異太遠,後面uniform求平均也起到了求consensus的做用。可是decision tree有點不一樣,每一次的切割都是切分剩下的數據,意味着每一次若是數據是不同的切分方式就不同,因此對不一樣的數據會很敏感,因此不一樣的D獲得的variance會大一些。 可是以前說過,越不一樣的g是越能反應事物的自己,好比看一我的,單單從成績方面太狹隘了,成績和行動力結合起來纔會綜合,這就是像是兩個g結合在一塊兒。因此若是g越不同表達的方面就越不同。既然decision tree是variance大,bagging的variance小,結合起來講不定能夠取得更好的成績,因而random forest誕生了。

#②Random Forest github

因此random forest由兩方面組成,bagging和random forest。
random forest有三個優勢,1.決策樹能夠由不一樣的主機生成,效率高。2.隨機森林繼承了CART的有點。3.以bagging的形式結合在一塊兒,避免了過擬合。
上面所講的指數基本的random forest,經過boostrap抽樣獲得數據D1而後訓練decision tree,最後作uniform。除了這種方法以外,還有另一種方法,隨機選取數據的feature,如今有100個特徵,我選取30個來構成decision tree,又選擇另外的30個來構成新的一棵隨機數,每一棵樹都不同。好比如今是d維,那麼咱們只是選擇_d(_d < d)維來構建決策樹。也就是說_d維z空間其實就是d維x空間的一個隨機子空間(subspace),Random Forest算法的做者建議在構建CART每一個分支b(x)的時候,均可以從新選擇子特徵來訓練,從而獲得更具備多樣性的決策樹。
因此改進一下:
subspace
上面咱們講的是隨機抽取特徵,除此以外,還能夠將現有的特徵x,經過數組p進行線性組合,來保持多樣性:
這種方法獲得的就再也不是d的子空間集合了,而是獲得線性的組合,好比在二維平面上只能是x,y維度。可是轉換以後就是斜線。而不一樣的pi是不同的,並且向量pi中大部分元素爲零,由於咱們選擇的只是一部分特徵,這是一種低維映射:
因而,能力又強了一點,random-subspace變成了random-combination,這裏就很像是perceptron模型了。事實上我想了一會才感受到很像......
下面的代碼實現還不會用到這些,由於仍是麻煩了點,以後有時間再看看改進吧。 #③Out-Of-Bag Estimate 再來探討一下對於boostrap的一些內容和理解。 咱們經過了boostrap獲得了數據D1,數據D1裏面既有原數據D裏面有的,也可能沒有裏面有的。紅色的*就表明沒有,也叫作紅色表示的樣本被稱爲out-of-bag(OOB) example。
那麼他到底有多少?能夠根據有關公式算一下:
1/N就是抽中的機率。那麼抽不中就(1 - 1/N),屢次就是N次方了。化簡一下約等於1/e。其實這裏的≈不太準確,由於N -> lim是有(1 + 1/N)^N = e。 因此,是有大約百分之30的數據是抽不到的。 貌似是和以前學過validation有點像,來對比一下:
在validation裏面,Dtrain是用來獲得gt的,Dtrain和Dval是倍數關係,而且沒有交集,而OOB裏面在bag外的數據是*號的,也沒有參與到decision tree的create當中。那麼咱們是否能夠用OOB的數據來代替validation呢?其實徹底能夠的,由於OOB類比過去就是Dval數據。那麼整一個G的performance要怎麼計算?咱們能夠計算g的而後平均,好比當前有5個gt,咱們分別計算他手下的OOB的Eval做爲這個g的performance,平均便可。
這種self-validation相比於validation來講還有一個優勢就是它不須要重複訓練。以下圖左邊所示,在經過Dval選擇到表現最好的g以後,還須要在Dtrain和Dval組成的全部樣本集D上從新對該模型g訓練一次,以獲得最終的模型係數。可是self-validation在調整隨機森林算法相關係數並獲得最小的EooB以後,就完成了整個模型的創建,無需從新訓練模型。更重要的是,random forest的self-validation在衡量G的表現上一般至關準確。

#④Feature Selection 在feature選擇的過程當中,還有一類問題要注意。選擇的過程當中有可能遇到多餘的問題,好比抽取到生日和年齡,這就尷尬了。另外一種是不相關的。 算法

特徵選擇優勢: 提升效率,特徵越少,模型越簡單 正則化,防止特徵過多出現過擬合 去除無關特徵,保留相關性大的特徵,解釋性強 缺點: 篩選特徵的計算量較大 不一樣特徵組合過擬合是有可能出現的 容易選到無關特徵,解釋性差
在上一篇實現決策樹的裏面使用的decision stump就是一種feature selection。 問題來了,咱們要怎麼選擇有用的特徵?
其中的一種方法就是作特徵重要性的排序,選擇前幾個。這種方法若是在線性模型裏面是很好計算的,線性可分模型。 若是是非線性可分的話,權值不是一一對應的,由於一般是作了feature transform的,因此權值是交叉再一塊兒。因此非線性就會很難。
上圖是linear model可使用的,而且效果不差。只須要選擇最大的權值|W|就行了。 RF中,特徵選擇的核心思想是random test。random test的作法是對於某個特徵,若是用另一個隨機值替代它以後的表現比以前更差,則代表該特徵比較重要,所佔的權重應該較大,不能用一個隨機值替代。相反,若是隨機值替代後的表現沒有太大差異,則代表該特徵不那麼重要,無關緊要。就好像班裏面學習好的忽然有一天死了,老師立馬就能夠發現,可是對於成績差的掛了一個學期都不必定知道。因此,經過比較某特徵被隨機值替代先後的表現,就能推斷出該特徵的權重和重要性。 問題來了,咱們應該如何選擇隨機值來替代? ①是使用uniform或者是Gaussian插入隨機值。 ②是把數據的第i個特徵打亂了看看打亂以後效果差異大不大。 相比起來,其實第二種更加好,由於若是原本的不是高斯分佈可能就會打亂了數據的分佈方式,而第二種在大致上數據的分佈是不改變的。以後咱們能夠比較variance,大的那麼就是比較重要了。
這樣就又有了一個問題,咱們要如何衡量改變dimension後的D和原數據D的performance呢? ①咱們能夠延用以前OOB的作法,打亂i個特徵,對每個維度都要訓練,如何用OOB作validation評估performance與原數據D訓練出來的作比較。 ②RF的做者提出了一種方法,就是把permutation的操做從原來的training上移到了OOB validation上去,記爲Eoob(G(p))→E(p)oob(G)。也就是說,在訓練的時候仍然使用D,可是在OOB驗證的時候,將全部的OOB樣本的第i個特徵從新洗牌,驗證G的表現。 第二種方法用的不少,計算方便,主要是效果也很好啊。

#⑤代碼實現bootstrap

Random Forest in Action

最後,咱們經過實際的例子來看一下RF的特色。首先,仍然是一個二元分類的例子。以下圖所示,左邊是一個C&RT樹沒有使用bootstrap獲得的模型分類效果,其中不一樣特徵之間進行了隨機組合,因此有斜線做爲分類線;中間是由bootstrap(N’=N/2)後生成的一棵決策樹組成的隨機森林,圖中加粗的點表示被bootstrap選中的點;右邊是將一棵決策樹進行bagging後的分類模型,效果與中間圖是同樣的,都是一棵樹。 數組

當t=100,即選擇了100棵樹時,中間的模型是第100棵決策樹構成的,仍是隻有一棵樹;右邊的模型是由100棵決策樹bagging起來的,以下圖所示:
當t=200時:
當t=300時:
當t=400時:
當t=500時:
當t=600時:
當t=700時:
當t=800時:
當t=900時:
當t=1000時:
隨着樹木個數的增長,咱們發現,分界線愈來愈光滑並且獲得了large-margin-like boundary,和以前磕磕絆絆的相比光滑了很多,相似於SVM同樣的效果。也就是說,樹木越多,分類器的置信區間越大。

而後,咱們再來看一個比較複雜的例子,二維平面上分佈着許多離散點,分界線形如sin函數。當只有一棵樹的時候(t=1),下圖左邊表示單一樹組成的RF,右邊表示全部樹bagging組合起來構成的RF。由於只有一棵樹,因此左右兩邊效果一致。 bash

當t=6時:
當t=11時:
當t=16時:
當t=21時:
能夠看到,當RF由21棵樹構成的時候,分界線就比較平滑了,並且它的邊界比單一樹構成的RF要robust得多,更加平滑和穩定。 最後,基於上面的例子,再讓問題複雜一點:在平面上添加一些隨機噪聲。當t=1時,以下圖所示:
當t=6時:
當t=11時:
當t=16時:
當t=21時:
從上圖中,咱們發現21棵樹的時候,隨機noise的影響基本上可以修正和消除。這種bagging投票的機制可以保證較好的降噪性,從而獲得比較穩定的結果。 通過以上三個例子,咱們發現RF中,樹的個數越多,模型越穩定越能表現得好。在實際應用中,應該儘量選擇更多的樹。值得一提的是,RF的表現同時也與random seed有關,即隨機的初始值也會影響RF的表現。

以後就是真正的代碼實現了 這裏使用的仍是隨機選擇特徵的方法。 首先是一個特徵選擇函數:app

def choose_samples(self, data, k):
      '''choose the feature from data input:data, type = list output:k '''
      n, d = np.shape(data)
      feature = []
      for j in range(k):
          feature.append(rd.randint(0, d - 2))
      index = []
      for i in range(n):
          index.append(rd.randint(0, n-1))
      data_samples = []
      for i in range(n):
          data_tmp = []
          for fea in feature:
              data_tmp.append(data[i][fea])
          data_tmp.append(data[i][-1])
          data_samples.append(data_tmp)
          pass
      return data_samples, feature
      pass
複製代碼

在data數據裏面選擇出k維的數據。 以後就是隨機森林的創建了,使用的決策樹是上篇文章實現的決策樹,儘可能作到全是本身實現的:dom

def random_forest(self, data, trees_num):
      '''create a forest input:data, type = list output:trees_result, trees_feature '''
      decisionTree = tree.decision_tree()
      trees_result = []
      trees_feature = []
      d = np.shape(data)[1]
      if d > 2:
          k = int(math.log(d - 1, 2)) + 1
      else:
          k = 1

      for i in range(trees_num):
          print('The ', i, ' tree. ')
          data_samples, feature = self.choose_samples(data, k)
          t = decisionTree.build_tree(data_samples)
          trees_result.append(t)
          trees_feature.append(feature)
          pass
      return trees_result, trees_feature
複製代碼

其實都很常規,最後返回的是樹的數量和選取的特徵。 以後就是一個切割數據和加載數據的工具函數:函數

def split_data(data_train, feature):
  '''select the feature from data input:data, feature output:data, type = list '''
  m = np.shape(data_train)[0]
  data = []
  for i in range(m):
      data_tmp = []
      for x in feature:
          data_tmp.append(data_train[i][x])
      data_tmp.append(data_train[i][-1])
      data.append(data_tmp)
  return data

def load_data():
  '''use the boston dataset from sklearn'''
  print('loading data......')
  dataSet = load_breast_cancer()
  data = dataSet.data
  target = dataSet.target
  for i in range(len(target)):
      if target[i] == 0:
          target[i] = -1
  dataframe = pd.DataFrame(data)
  dataframe.insert(np.shape(data)[1], 'target', target)
  dataMat = np.mat(dataframe)
  X_train, X_test, y_train, y_test =  train_test_split(dataMat[:, 0:-1], dataMat[:, -1], test_size=0.3, random_state=0)
  data_train = np.hstack((X_train, y_train))
  data_train = data_train.tolist()
  X_test = X_test.tolist()

  return data_train, X_test, y_test
複製代碼

load_data是把數據3,7切分,測試和訓練。 而後就是預測函數和計算準確度的函數了:工具

def get_predict(self, trees_result, trees_feature, data_train):
      '''predict the result input:trees_result, trees_feature, data output:final_prediction '''
      decisionTree = tree.decision_tree()
      m_tree = len(trees_result)
      m = np.shape(data_train)[0]
      result = []
      for i in range(m_tree):
          clf = trees_result[i]
          feature = trees_feature[i]
          data = tool.split_data(data_train, feature)
          result_i = []
          for i in range(m):
              result_i.append( list((decisionTree.predict(data[i][0 : -1], clf).keys()))[0] )
          result.append(result_i)
      final_predict = np.sum(result, axis = 0)
      return final_predict

  def cal_correct_rate(self, target, final_predict):
      m = len(final_predict)
      corr = 0.0
      for i in range(m):
          if target[i] * final_predict[i] > 0:
              corr += 1
          pass
      return corr/m
      pass

複製代碼

這個和以前決策樹的差很少,也是調用了以前的代碼。 最後就是入口函數:

def running():
  '''entrance'''
  data_train, text, target = load_data()
  forest = randomForest()
  predic = []
  for i in range(1, 20):
      trees, features = forest.random_forest(data_train, i)
      predictions = forest.get_predict(trees, features, text)
      accuracy = forest.cal_correct_rate(target, predictions)
      print('The forest has ', i, 'tree', 'Accuracy : ' , accuracy)
      predic.append(accuracy)

  plt.xlabel('Number of tree')
  plt.ylabel('Accuracy')
  plt.title('The relationship between tree number and accuracy')
  plt.plot(range(1, 20), predic, color = 'orange')
  plt.show()
  pass

if __name__ == '__main__':
  running()
複製代碼

計算了1到20課樹他們以前準確率的變化,畫了對比圖。

大體趨勢仍是能夠看得出是一直不斷上升,最高應該是13到15這個區間吧,可是注意到2到5棵樹的時候存在了必定的顛婆,這顛婆有點厲害,我的猜想應該是採集到了某些沒有用的特徵致使的,後面效果好了是由於樹多了效果削弱了,而且有些的特徵也採集不到。這個數據維度是30維的。有時間再作一個特徵重要性的選擇了。

全部代碼GitHub上: github.com/GreenArrow2…

相關文章
相關標籤/搜索