首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么`max_features=n_features`不使随机森林独立于树数?

为什么`max_features=n_features`不使随机森林独立于树数?
EN

Data Science用户
提问于 2017-02-07 10:12:01
回答 2查看 9.3K关注 0票数 10

考虑以下简单的分类问题(Python,scikit-learn)

代码语言:javascript
复制
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score

def get_product_data(size):
    '''
    Given a size(int), sets `log10(size)` features to be uniform 
    random variables `Xi` in [-1,1] and an target `y` given by 1 if 
    their product `P` is larger than 0.0 and zero otherwise. 
    Returns a pandas DataFrame.
    '''
    n_features = int(max(2, np.log10(size)))
    features = dict(('x%d' % i, 2*np.random.rand(size) - 1) for i in range(n_features))
    y = np.prod(list(features.values()), axis=0)
    y = y > 0.0
    features.update({'y': y.astype(int)})
    return pd.DataFrame(features)

# create random data
df = get_product_data(1000)

X = np.array(df.drop(df.columns[-1], axis=1))
y = df['y']

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, 
                                                        random_state=1)    

def predict(clf):
    '''
    Splits train/test with a fixed seed, fits, and returns the accuracy
    '''
    clf.fit(X_train, y_train)
    return accuracy_score(y_test, clf.predict(X_test))

和下列分类器:

代码语言:javascript
复制
foo10 = RandomForestClassifier(10, max_features=None, bootstrap=False)
foo100 = RandomForestClassifier(100, max_features=None, bootstrap=False)
foo200 = RandomForestClassifier(200, max_features=None, bootstrap=False)

为什么

代码语言:javascript
复制
predict(foo10)  # 0.906060606061
predict(foo100)  # 0.933333333333
predict(foo200)  # 0.915151515152

给出不同的分数?

具体来说,与

  1. max_features=None,为每棵树选择所有特性。
  2. bootstrap=False,没有自举的样本
  3. max_depth=None (默认),所有树都达到最大深度

我希望每棵树都是完全一样的。因此,不管森林有多少树,预测应该是相等的。在这个例子中,树的可变性从何而来?

我还需要在RandomForestClassifier.__init__中引入哪些进一步的参数,使foo*有相同的分数呢?

EN

回答 2

Data Science用户

回答已采纳

发布于 2017-02-07 11:39:33

真是有趣的谜题。

首先要做的是。DecisionTreeClassifier具有一定的随机性。例如,分离器码随机地遍历这些特性:

代码语言:javascript
复制
        f_j = rand_int(n_drawn_constants, f_i - n_found_constants,
                       random_state)

您的数据很小,来自相同的发行版。这意味着你将有很多相同的纯度分数,取决于迭代是如何完成的。如果你(a)增加你的数据,或者(b)让它更容易分离,你就会发现问题应该得到改善。

为了澄清:如果算法计算特征A的分数,然后计算特征B的分数并得到N,或者如果它首先计算特征B的分数,然后计算特征A的分数,然后它得到相同的分数N,您可以看到每个决策树是如何不同的,并且在测试期间有不同的分数,即使训练测试是相同的(当然,如果是max_depth=None,则是100%)。(你可以证实这一点。)

在我探索您的问题期间,我用我自己实现的随机森林生成了以下代码。因为花了我一段时间,我想我还是把它贴在这里好了。)说真的,它可能是有用的。您可以尝试从我的实现中禁用random_state,以了解我的意思。

代码语言:javascript
复制
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import accuracy_score
import numpy as np


class MyRandomForestClassifier:
    def __init__(self, n_estimators):
        self.n_estimators = n_estimators

    def fit(self, X, y):
        self.trees = [DecisionTreeClassifier(random_state=1).fit(X, y)
                      for _ in range(self.n_estimators)]
        return self

    def predict(self, X):
        yp = [tree.predict(X) for tree in self.trees]
        return ((np.sum(yp, 0) / len(self.trees)) > 0.5).astype(int)

    def score(self, X, y):
        return accuracy_score(y, self.predict(X))


for alpha in (1, 0.1, 0.01):
    np.random.seed(1)
    print('# alpha: %s' % str(alpha))
    N = 1000
    X = np.random.random((N, 10))
    y = np.r_[np.zeros(N//2, int), np.ones(N//2, int)]
    X[y == 1] = X[y == 1]*alpha
    Xtr, Xts, ytr, yts = train_test_split(X, y)

    print('## sklearn forest')
    for n_estimators in (1, 10, 100, 200, 500):
        m = RandomForestClassifier(
            n_estimators, max_features=None, bootstrap=False)
        m.fit(Xtr, ytr)
        print('%3d: %.4f' % (n_estimators, m.score(Xts, yts)))

    print('## my forest')
    for n_estimators in (1, 10, 100, 200, 500):
        m = MyRandomForestClassifier(n_estimators)
        m.fit(Xtr, ytr)
        print('%3d: %.4f' % (n_estimators, m.score(Xts, yts)))
    print()

摘要:每个DecisionTreeClassifier都是随机的,像您这样的数据很小,来自相同分布的数据必然会产生稍微不同的树,即使随机森林本身是确定性的。您可以通过向每个DecisionTreeClassifier传递相同的种子来解决这个问题,这可以使用random_state=something进行。RandomForestClassifier还有一个random_state参数,它沿着每个DecisionTreeClassifier传递这个参数。(这有点不正确,请参阅编辑。)

EDIT2:虽然这消除了训练中的随机性成分,但是决策树仍然是不同的。问题是,sklearn组合根据给出的随机状态为每个孩子生成一个新的随机种子。它们不传递相同的random_state

您可以通过从集成基模块(特别是_set_random_states )中检查这条线方法来看到这种情况,后者将random_state传播到组件的子模块中。

票数 8
EN

Data Science用户

发布于 2017-02-07 12:54:00

捡起里卡多·克鲁兹的答复

原因是DecisionTreeClassifier不是确定性分类器。具体来说,当在导致度量相同减少的两个特性之间分裂时,DecisionTreeClassifier会随机选择一个。问题中所观察到的波动是由这一点引起的。

这一观察的推论是,scikit-learn的随机森林将引导用于3种不同的事情:

  • 项目(样品不同项目)
  • 特征(样本不同特征)
  • 分裂(样本不同分割)

冻结items (使用bootstrap=False)和features (使用max_depth=None)不足以冻结splitting。冻结splitting的唯一方法是使用固定的random_state

请注意,仅使用相同的random_state并不足以冻结一切,即

代码语言:javascript
复制
foo10 = RandomForestClassifier(10, random_state=1)
foo100 = RandomForestClassifier(100, random_state=1)
foo200 = RandomForestClassifier(200, random_state=1)

不要给同样的分数。

因此,确保分类器为任何n_estimators提供相同结果的唯一方法是将其初始化为

代码语言:javascript
复制
RandomForestClassifier(n_estimators, max_features=None, bootstrap=False, random_state=1)

这对分类本身并不特别有帮助,但可以更好地理解引擎盖下正在进行的工作。

票数 4
EN
页面原文内容由Data Science提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://datascience.stackexchange.com/questions/16800

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档