计算机系统应用教程网站

网站首页 > 技术文章 正文

Python实现机器学习算法——随机森林

btikc 2024-10-11 11:21:21 技术文章 4 ℃ 0 评论

Bagging与随机森林

Bagging是并行式集成学习方法最典型的代表框架。其核心概念在于自助采样(Bootstrap Sampling),给定包含m个样本的数据集,有放回的随机抽取一个样本放入采样集中,经过m次采样,可得到一个和原始数据集一样大小的采样集。

我们可以采样得到T个包含m个样本的采样集,然后基于每个采样集训练出一个基学习器,最后将这些基学习器进行组合。这便是Bagging的主要思想。Bagging与Boosting图示如下:

可以清楚的看到,Bagging是并行的框架,而Boosting则是序列框架(但也可以实现并行)。

所谓随机森林,就是有很多棵决策树构建起来的森林,因为构建过程中的随机性,故而称之为随机森林。随机森林算法是Bagging框架的一个典型代表。

这里我们直接阐述随机森林的算法过程,简单来说就是两个随机性。具体如下:

  • 假设有M个样本,有放回的随机选择M个样本(每次随机选择一个放回后继续选)。
  • 假设样本有N个特征,在决策时的每个节点需要分裂时,随机地从这N个特征中选取n个特征,满足n<<N,从这n个特征中选择特征进行节点分裂。
  • 基于抽样的M个样本n个特征按照节点分裂的方式构建决策树。
  • 按照1~3步构建大量决策树组成随机森林,然后将每棵树的结果进行综合(分类使用投票法,回归可使用均值法)。

所以,当我们熟悉了Bagging的基本思想和决策树构建的过程后,随机森林就很好理解了。



随机森林算法实现

本文我们使用numpy来手动实现一个随机森林算法。随机森林算法本身是实现思路我们是非常清晰的,但其原始构建需要大量搭建决策树的工作,比如定义树节点、构建基本决策树、在基本决策树基础上构建分类树和回归树等。

在此基础上,随机森林算法的构建主要包括随机选取样本、随机选取特征、构造森林并拟合其中的每棵树、基于每棵树的预测结果给出随机森林的预测结果。

导入相关模块并生成模拟数据集。

import numpy as np
# 该模块为自定义模块,封装了构建决策树的基本方法
from ClassificationTree import *
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
# 树的棵数
n_estimators = 10
# 列抽样最大特征数
max_features = 15
# 生成模拟二分类数据集
X, y = make_classification(n_samples=1000, n_features=20, n_redundant=0, n_informative=2,
                           random_state=1, n_clusters_per_class=1)
rng = np.random.RandomState(2)
X += 2 * rng.uniform(size=X.shape)
# 划分数据集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)
print(X_train.shape, y_train.shape, X_test.shape, y_test.shape)

定义第一个随机性,行抽样选取样本:

# 自助抽样选择训练数据子集
def bootstrap_sampling(X, y):
    X_y = np.concatenate([X, y.reshape(-1,1)], axis=1)
    np.random.shuffle(X_y)
    n_samples = X.shape[0]
    sampling_subsets = []


    for _ in range(n_estimators):
        # 第一个随机性,行抽样
        idx1 = np.random.choice(n_samples, n_samples, replace=True)
        bootstrap_Xy = X_y[idx1, :]
        bootstrap_X = bootstrap_Xy[:, :-1]
        bootstrap_y = bootstrap_Xy[:, -1]
        sampling_subsets.append([bootstrap_X, bootstrap_y])
    return sampling_subsets

然后基于分类树构建随机森林:

trees = []
# 基于决策树构建森林
for _ in range(n_estimators):
    tree = ClassificationTree(min_samples_split=2, min_impurity=0,
                              max_depth=3)
    trees.append(tree)

定义训练函数,对随机森林中每棵树进行拟合。

# 随机森林训练
def fit(X, y):
    # 对森林中每棵树训练一个双随机抽样子集
    n_features = X.shape[1]
    sub_sets = bootstrap_sampling(X, y)
    for i in range(n_estimators):
        sub_X, sub_y = sub_sets[i]
        # 第二个随机性,列抽样
        idx2 = np.random.choice(n_features, max_features, replace=True)
        sub_X = sub_X[:, idx2]
        trees[i].fit(sub_X, sub_y)
        trees[i].feature_indices = idx2
        print('The {}th tree is trained done...'.format(i+1))

我们将上述过程进行封装,分别定义自助抽样方法、随机森林训练方法和预测方法。完整代码如下:

class RandomForest():
    def __init__(self, n_estimators=100, min_samples_split=2, min_gain=0,
                 max_depth=float("inf"), max_features=None):
        # 树的棵树
        self.n_estimators = n_estimators
        # 树最小分裂样本数
        self.min_samples_split = min_samples_split
        # 最小增益
        self.min_gain = min_gain
        # 树最大深度
        self.max_depth = max_depth
        # 所使用最大特征数
        self.max_features = max_features


        self.trees = []
        # 基于决策树构建森林
        for _ in range(self.n_estimators):
            tree = ClassificationTree(min_samples_split=self.min_samples_split, min_impurity=self.min_gain,
                                      max_depth=self.max_depth)
            self.trees.append(tree)
            
    # 自助抽样
    def bootstrap_sampling(self, X, y):
        X_y = np.concatenate([X, y.reshape(-1,1)], axis=1)
        np.random.shuffle(X_y)
        n_samples = X.shape[0]
        sampling_subsets = []


        for _ in range(self.n_estimators):
            # 第一个随机性,行抽样
            idx1 = np.random.choice(n_samples, n_samples, replace=True)
            bootstrap_Xy = X_y[idx1, :]
            bootstrap_X = bootstrap_Xy[:, :-1]
            bootstrap_y = bootstrap_Xy[:, -1]
            sampling_subsets.append([bootstrap_X, bootstrap_y])
        return sampling_subsets
            
    # 随机森林训练
    def fit(self, X, y):
        # 对森林中每棵树训练一个双随机抽样子集
        sub_sets = self.bootstrap_sampling(X, y)
        n_features = X.shape[1]
        # 设置max_feature
        if self.max_features == None:
            self.max_features = int(np.sqrt(n_features))
        
        for i in range(self.n_estimators):
            # 第二个随机性,列抽样
            sub_X, sub_y = sub_sets[i]
            idx2 = np.random.choice(n_features, self.max_features, replace=True)
            sub_X = sub_X[:, idx2]
            self.trees[i].fit(sub_X, sub_y)
            # 保存每次列抽样的列索引,方便预测时每棵树调用
            self.trees[i].feature_indices = idx2
            print('The {}th tree is trained done...'.format(i+1))
    
    # 随机森林预测
    def predict(self, X):
        y_preds = []
        for i in range(self.n_estimators):
            idx = self.trees[i].feature_indices
            sub_X = X[:, idx]
            y_pred = self.trees[i].predict(sub_X)
            y_preds.append(y_pred)
            
        y_preds = np.array(y_preds).T
        res = []
        for j in y_preds:
            res.append(np.bincount(j.astype('int')).argmax())
        return res

基于上述随机森林算法封装来对模拟数据集进行训练并验证:

rf = RandomForest(n_estimators=10, max_features=15)
rf.fit(X_train, y_train)
y_pred = rf.predict(X_test)
print(accuracy_score(y_test, y_pred))
0.78

sklearn也为我们提供了随机森林算法的api,我们也尝试一下与numpy手写的进行效果对比:

from sklearn.ensemble import RandomForestClassifier
clf = RandomForestClassifier(max_depth=3, random_state=0)
clf.fit(X_train, y_train)
y_pred = clf.predict(X_test)
print(accuracy_score(y_test, y_pred))
0.8

可以看到sklearn的预测结果要略高于我们手写的结果。当然我们的训练结果还可以经过调参进一步提高的

参考资料:

机器学习 周志华

原文链接:

https://mp.weixin.qq.com/s/pABSPKrqlpnWOf-teTLOrg

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表