【机器学习】随机森林


【机器学习】随机森林_第1张图片

一、原理

集成学习

【机器学习】随机森林_第2张图片

【机器学习】随机森林_第3张图片

bootstrap抽样

【机器学习】随机森林_第4张图片

bagging 算法

【机器学习】随机森林_第5张图片

随机森林训练算法

【机器学习】随机森林_第6张图片

随机森林输出变量的重要性Gini方法和置换法

【机器学习】随机森林_第7张图片

二、示例代码 (5个)

2.1 多种弱分类器对比

【机器学习】随机森林_第8张图片

# 导入所需的库和模块
import numpy as np # 导入numpy库,用于进行科学计算
import matplotlib.pyplot as plt # 导入matplotlib库,用于进行数据可视化
from matplotlib.colors import ListedColormap # 导入ListedColormap模块,用于创建自定义的颜色映射
from sklearn.model_selection import train_test_split # 导入train_test_split模块,用于将数据集划分为训练集和测试集
from sklearn.preprocessing import StandardScaler # 导入StandardScaler模块,用于对数据进行标准化处理
from sklearn.datasets import make_moons, make_circles, make_classification # 导入make_moons, make_circles, make_classification模块,用于生成模拟的数据集
from sklearn.neighbors import KNeighborsClassifier # 导入KNeighborsClassifier模块,用于实现最近邻分类器
from sklearn.svm import SVC # 导入SVC模块,用于实现支持向量机分类器
from sklearn.tree import DecisionTreeClassifier # 导入DecisionTreeClassifier模块,用于实现决策树分类器
from sklearn.ensemble import RandomForestClassifier, AdaBoostClassifier # 导入RandomForestClassifier, AdaBoostClassifier模块,用于实现随机森林和AdaBoost分类器
from sklearn.naive_bayes import GaussianNB # 导入GaussianNB模块,用于实现高斯朴素贝叶斯分类器
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis as LDA # 导入LinearDiscriminantAnalysis模块,并将其简写为LDA,用于实现线性判别分析
from sklearn.discriminant_analysis import QuadraticDiscriminantAnalysis as QDA # 导入QuadraticDiscriminantAnalysis模块,并将其简写为QDA,用于实现二次判别分析


h = .02  # 设置网格的步长


# 定义分类器的名称和对象
names = ["Nearest Neighbors", "Linear SVM", "RBF SVM", "Decision Tree",
         "Random Forest", "AdaBoost", "Naive Bayes", "LDA", "QDA"]
classifiers = [
    KNeighborsClassifier(3), # 创建一个最近邻分类器,设置邻居数为3
    SVC(kernel="linear", C=0.025), # 创建一个线性核的支持向量机分类器,设置惩罚参数为0.025
    SVC(gamma=2, C=1), # 创建一个径向基核的支持向量机分类器,设置核函数参数为2,惩罚参数为1
    DecisionTreeClassifier(max_depth=5), # 创建一个决策树分类器,设置最大深度为5
    RandomForestClassifier(max_depth=5, n_estimators=10, max_features=1), # 创建一个随机森林分类器,设置最大深度为5,树的数量为10,每个树的最大特征数为1
    AdaBoostClassifier(), # 创建一个AdaBoost分类器,使用默认参数
    GaussianNB(), # 创建一个高斯朴素贝叶斯分类器,使用默认参数
    LDA(), # 创建一个线性判别分析分类器,使用默认参数
    QDA()] # 创建一个二次判别分析分类器,使用默认参数




# 生成一个具有两个特征,两个信息特征,无冗余特征,两个类别,每个类别一个簇的数据集
X, y = make_classification(n_features=2, n_redundant=0, n_informative=2,
                           random_state=1, n_clusters_per_class=1)
rng = np.random.RandomState(2) # 创建一个随机数生成器,设置随机种子为2
X += 2 * rng.uniform(size=X.shape) # 给数据集添加一些噪声
linearly_separable = (X, y) # 将数据集和标签组合成一个元组


# 定义三个数据集,分别是月亮形状的数据集,圆形状的数据集,和线性可分的数据集
datasets = [make_moons(noise=0.3, random_state=0), # 生成一个月亮形状的数据集,设置噪声为0.3,随机种子为0
            make_circles(noise=0.2, factor=0.5, random_state=1), # 生成一个圆形状的数据集,设置噪声为0.2,内外圆的半径比为0.5,随机种子为1
            linearly_separable # 使用之前生成的线性可分的数据集
            ]


# 创建一个图形对象,设置大小为27*9
figure = plt.figure(figsize=(27, 9))
i = 1 # 设置子图的索引
# 遍历数据集
for ds in datasets:
    # 预处理数据集,将其划分为训练集和测试集
    X, y = ds # 解包数据集和标签
    X = StandardScaler().fit_transform(X) # 对数据集进行标准化处理
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=.4) # 将数据集划分为训练集和测试集,设置测试集的比例为0.4


    # 获取数据集的最大值和最小值,用于绘制网格
    x_min, x_max = X[:, 0].min() - .5, X[:, 0].max() + .5
    y_min, y_max = X[:, 1].min() - .5, X[:, 1].max() + .5
    # 根据步长和最大值最小值,生成网格
    xx, yy = np.meshgrid(np.arange(x_min, x_max, h),
                         np.arange(y_min, y_max, h))


    # 先绘制数据集本身
    cm = plt.cm.RdBu # 定义一个颜色映射,用于绘制决策边界
    cm_bright = ListedColormap(['#FF0000', '#0000FF']) # 定义一个颜色映射,用于绘制数据点
    ax = plt.subplot(len(datasets), len(classifiers) + 1, i) # 创建一个子图对象,设置行数为数据集的数量,列数为分类器的数量加一,索引为i
    # 绘制训练集的数据点,使用颜色映射和标签来区分不同的类别
    ax.scatter(X_train[:, 0], X_train[:, 1], c=y_train, cmap=cm_bright)
    # 绘制测试集的数据点,使用颜色映射和标签来区分不同的类别,设置透明度为0.6
    ax.scatter(X_test[:, 0], X_test[:, 1], c=y_test, cmap=cm_bright, alpha=0.6)
    # 设置子图的横轴和纵轴的范围
    ax.set_xlim(xx.min(), xx.max())
    ax.set_ylim(yy.min(), yy.max())
    # 设置子图的横轴和纵轴的刻度为空
    ax.set_xticks(())
    ax.set_yticks(())
    i += 1 # 更新子图的索引


    # 遍历分类器列表
    for name, clf in zip(names, classifiers):
        # 在子图中绘制当前分类器的结果
        ax = plt.subplot(len(datasets), len(classifiers) + 1, i)
        # 使用训练集拟合分类器
        clf.fit(X_train, y_train)
        # 使用测试集评估分类器的准确率
        score = clf.score(X_test, y_test)
        # 绘制决策边界。为此,我们将为网格中的每个点 [x_min, x_max]x[y_min, y_max] 分配一个颜色。
        # 如果分类器有 decision_function 方法,就使用它计算网格中每个点的决策值
        if hasattr(clf, "decision_function"):# 如果分类器有decision_function属性,说明它可以输出每个样本属于正类的置信度
            Z = clf.decision_function(np.c_[xx.ravel(), yy.ravel()])
        else:# 否则,使用 predict_proba 方法计算网格中每个点属于正类的概率
            Z = clf.predict_proba(np.c_[xx.ravel(), yy.ravel()])[:, 1]


        # 将结果转换为颜色图
        Z = Z.reshape(xx.shape)
        # 使用颜色图和透明度填充子图
        ax.contourf(xx, yy, Z, cmap=cm, alpha=.8)


        # 绘制训练集中的点
        ax.scatter(X_train[:, 0], X_train[:, 1], c=y_train, cmap=cm_bright)
        # 绘制测试集中的点
        ax.scatter(X_test[:, 0], X_test[:, 1], c=y_test, cmap=cm_bright,
                alpha=0.6)


        # 设置子图的横纵坐标范围
        ax.set_xlim(xx.min(), xx.max())
        ax.set_ylim(yy.min(), yy.max())
        # 隐藏子图的横纵坐标刻度
        ax.set_xticks(())
        ax.set_yticks(())
        # 设置子图的标题为分类器的名称
        ax.set_title(name)
        # 在子图的右下角显示分类器的准确率,保留两位小数
        ax.text(xx.max() - .3, yy.min() + .3, ('%.2f' % score).lstrip('0'),
                size=15, horizontalalignment='right')
        # 更新子图的索引
        i += 1


# 调整子图之间的间距
figure.subplots_adjust(left=.02, right=.98)
# 显示所有子图
plt.show()

2.2 scikit-learn随机森林算法对iris数据集特征重要性的计算和可视化

# 导入所需的库
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.datasets import load_iris
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score


# 加载鸢尾花数据集
iris = load_iris()
X = iris.data
y = iris.target
feature_names = iris.feature_names
feature_names = np.array(feature_names)
# 创建随机森林分类器
rf = RandomForestClassifier(n_estimators=100, random_state=42)
rf.fit(X, y)


# 计算变量的重要性(Gini方法)
importances_gini = rf.feature_importances_
# 对变量的重要性进行排序
indices_gini = np.argsort(importances_gini)[::-1]
# 打印变量的重要性和排序(Gini方法)
print("Feature ranking (Gini method):")
for i in range(X.shape[1]):
    print("%d. %s (%f)" % (i + 1, feature_names[indices_gini[i]], importances_gini[indices_gini[i]]))


# 计算变量的重要性(置换法)
importances_perm = np.zeros(X.shape[1])
# 对每个变量进行置换
for i in range(X.shape[1]):
    # 复制原始数据
    X_perm = X.copy()
    # 随机打乱第i个变量的取值
    np.random.shuffle(X_perm[:, i])
    # 用打乱后的数据来预测
    y_pred = rf.predict(X_perm)
    # 计算预测精度的变化量
    importances_perm[i] = accuracy_score(y, rf.predict(X)) - accuracy_score(y, y_pred)
# 对变量的重要性进行排序
indices_perm = np.argsort(importances_perm)[::-1]
# 打印变量的重要性和排序(置换法)
print("Feature ranking (permutation method):")
for i in range(X.shape[1]):
    print("%d. %s (%f)" % (i + 1, feature_names[indices_perm[i]], importances_perm[indices_perm[i]]))


# 绘制变量的重要性柱状图(Gini方法和置换法)
plt.figure(figsize=(10, 5))
plt.subplot(121)
plt.title("Feature importances (Gini method)")
plt.bar(range(X.shape[1]), importances_gini[indices_gini], color="r", align="center")
plt.xticks(range(X.shape[1]), feature_names[indices_gini], rotation=45)
plt.xlim([-1, X.shape[1]])
plt.subplot(122)
plt.title("Feature importances (permutation method)")
plt.bar(range(X.shape[1]), importances_perm[indices_perm], color="b", align="center")
plt.xticks(range(X.shape[1]), feature_names[indices_perm], rotation=45)
plt.xlim([-1, X.shape[1]])
plt.show()

【机器学习】随机森林_第9张图片

【机器学习】随机森林_第10张图片

2.3  自定义随机森林算法

# 导入numpy库,用于科学计算
import numpy as np
# 导入utils模块,用于获取随机子集
from  utils import get_random_subsets
# 导入ClassificationTree类,用于构建决策树
from supervised_learning.decision_tree import ClassificationTree




# 定义一个函数,用于获取数据的随机子集(有放回或无放回)
def get_random_subsets(X, y, n_subsets, replacements=True):
    """ Return random subsets (with replacements) of the data """
    # 获取数据的样本数量
    n_samples = np.shape(X)[0]
    # 将特征矩阵X和标签向量y拼接在一起,并进行随机打乱
    X_y = np.concatenate((X, y.reshape((1, len(y))).T), axis=1)
    np.random.shuffle(X_y)
    # 初始化一个空列表,用于存储子集
    subsets = []


    # 默认使用50%的训练样本,无放回地抽取
    subsample_size = int(n_samples // 2)
    # 如果设置为有放回地抽取,则使用100%的训练样本
    if replacements:
        subsample_size = n_samples      # 100% with replacements


    # 循环生成指定数量的子集
    for _ in range(n_subsets):
        # 随机选择指定大小的样本索引,根据replacements参数决定是否有放回
        idx = np.random.choice(
            range(n_samples),
            size=np.shape(range(subsample_size)),
            replace=replacements)
        # 根据索引,从打乱后的数据中提取特征和标签
        X = X_y[idx][:, :-1]
        y = X_y[idx][:, -1]
        # 将特征和标签作为一个列表,添加到子集列表中
        subsets.append([X, y])
    # 返回子集列表
    return subsets


# 定义RandomForest类,实现随机森林算法
class RandomForest:
    """
    The implementation of random forest algorithm.


    Attributes:
        ...
    """
    # 定义初始化方法,设置随机森林的参数和属性
    def __init__(self, n_estimators=100, max_features=None,
                 min_samples_split=2, min_gain=0, max_depth=np.inf):
        self.n_estimators = n_estimators    # 设置森林中树的数量
        self.max_features = max_features    # 设置每棵树最多使用的特征数量
        self.min_samples_split = min_samples_split    # 设置每棵树分裂节点的最小样本数量
        self.min_gain = min_gain    # 设置每棵树分裂节点的最小信息增益
        self.max_depth = max_depth    # 设置每棵树的最大深度
        # 初始化所有树的列表
        self.trees = []
        # 循环创建指定数量的决策树对象,并添加到列表中
        for _ in range(n_estimators):
            self.trees.append(
                ClassificationTree(
                    min_samples_split=self.min_samples_split,
                    min_impurity=min_gain,
                    max_depth=self.max_depth))


    # 定义拟合方法,用于训练所有的树
    def fit(self, X_train, y_train):
        """ Fit all the trees. """
        # 获取训练集的特征数量
        n_features = np.shape(X_train)[1]
        # 如果没有指定每棵树最多使用的特征数量,则使用特征数量的平方根作为默认值
        if not self.max_features:
            self.max_features = int(np.sqrt(n_features))
        # 使用自助采样法(bootstrap)获取随机子集,作为每棵树的训练集
        subsets = get_random_subsets(X_train, y_train, self.n_estimators)
        # 循环训练每棵树
        for i in range(self.n_estimators):
            # 随机选择指定数量的特征索引,作为每棵树使用的特征
            idx = np.random.choice(range(n_features), self.max_features)
            # 获取第i个子集的特征和标签
            X_subset, y_subset = subsets[i]
            # 只保留选中的特征
            X_subset = X_subset[:, idx]
            # 训练第i棵树
            self.trees[i].fit(X_subset, y_subset)
            # 保存第i棵树使用的特征索引
            self.trees[i].feature_indices = idx


    # 定义预测方法,用于对测试集进行分类
    def predict(self, X_test):
        """ Use simple voting method to determinate class. """
        # 初始化一个零矩阵,用于存储每棵树的预测结果
        y_preds = np.zeros((np.shape(X_test)[0], len(self.trees)))
        # 循环对每棵树进行预测
        for i, tree in enumerate(self.trees):
            # 对测试集的选中特征进行预测,并将结果存入矩阵的第i列
            y_preds[:, i] = tree.predict(X_test[:, tree.feature_indices])
        # 初始化一个空列表,用于存储最终的预测结果
        y_pred = []
        # 循环对每个样本的预测结果进行投票
        for one_preds in y_preds:
            # 使用简单投票法,即选择票数最多的类别作为最终结果,并添加到列表中
            y_pred.append(np.bincount(one_preds.astype(int)).argmax())
        # 返回最终的预测结果
        return y_pred




# 定义一个函数,用于获取数据的随机子集(有放回或无放回)
def get_random_subsets(X, y, n_subsets, replacements=True):
    """ Return random subsets (with replacements) of the data """
    # 获取数据的样本数量
    n_samples = np.shape(X)[0]
    # 将特征矩阵X和标签向量y拼接在一起,并进行随机打乱
    X_y = np.concatenate((X, y.reshape((1, len(y))).T), axis=1)
    np.random.shuffle(X_y)
    # 初始化一个空列表,用于存储子集
    subsets = []


    # 默认使用50%的训练样本,无放回地抽取
    subsample_size = int(n_samples // 2)
    # 如果设置为有放回地抽取,则使用100%的训练样本
    if replacements:
        subsample_size = n_samples      # 100% with replacements


    # 循环生成指定数量的子集
    for _ in range(n_subsets):
        # 随机选择指定大小的样本索引,根据replacements参数决定是否有放回
        idx = np.random.choice(
            range(n_samples),
            size=np.shape(range(subsample_size)),
            replace=replacements)
        # 根据索引,从打乱后的数据中提取特征和标签
        X = X_y[idx][:, :-1]
        y = X_y[idx][:, -1]
        # 将特征和标签作为一个列表,添加到子集列表中
        subsets.append([X, y])
    # 返回子集列表
    return subsets
    
# 如果这个模块是直接运行的,则执行以下代码
if __name__ == '__main__':
    # 这里可以写一些测试或示例代码
    pass

2.4 使用随机森林算法对一个社交网络广告数据集进行分类,预测用户是否会购买某种产品。

代码的主要步骤如下:

  • 导入所需的库,包括numpy、matplotlib和pandas,用于科学计算、绘图和数据处理。

  • 导入数据集,从csv文件中读取数据,将第2列和第3列作为特征,将第4列作为标签。

  • 将数据集分为训练集和测试集,按照75%的比例划分,设置随机种子为0。

  • 对训练集和测试集的特征进行标准化缩放,使其符合标准正态分布。

  • 创建一个随机森林分类器对象,设置树的数量为10,划分的标准为信息熵,随机种子为0。

  • 使用训练集的特征和标签对分类器进行拟合,学习如何分类。

  • 使用测试集的特征对分类器进行预测,得到预测的标签。

  • 生成混淆矩阵,用于评估分类器的性能。

  • 可视化训练集和测试集的结果,使用不同的颜色表示不同的类别,绘制分类器的决策边界,显示图像。

# 导入所需的库
import numpy as np # 用于科学计算
import matplotlib.pyplot as plt # 用于绘图
import pandas as pd # 用于数据处理


# 导入数据集
dataset = pd.read_csv('datasets/Social_Network_Ads.csv') # 从csv文件中读取数据
X = dataset.iloc[:, [2, 3]].values # 取出数据集中的第2列和第3列作为自变量X
y = dataset.iloc[:, 4].values # 取出数据集中的第4列作为因变量y


# 将数据集分为训练集和测试集
from sklearn.model_selection import train_test_split # 导入数据集划分的工具
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.25, random_state = 0) # 将数据集按照75%的比例划分为训练集和测试集,设置随机种子为0


# 特征缩放
from sklearn.preprocessing import StandardScaler # 导入标准化缩放的工具
sc = StandardScaler() # 创建一个标准化缩放的对象
X_train = sc.fit_transform(X_train) # 对训练集的自变量进行拟合和转换,使其符合标准正态分布
X_test = sc.transform(X_test) # 对测试集的自变量进行转换,使用与训练集相同的缩放参数


# 使用随机森林对训练集进行拟合
from sklearn.ensemble import RandomForestClassifier # 导入随机森林分类器的工具
classifier = RandomForestClassifier(n_estimators = 10, criterion = 'entropy', random_state = 0) # 创建一个随机森林分类器的对象,设置树的数量为10,划分的标准为信息熵,随机种子为0
classifier.fit(X_train, y_train) # 对训练集进行拟合,学习如何分类


# 对测试集进行预测
y_pred = classifier.predict(X_test) # 使用训练好的分类器,对测试集的自变量进行预测,得到预测的因变量


# 生成混淆矩阵
from sklearn.metrics import confusion_matrix # 导入混淆矩阵的工具
cm = confusion_matrix(y_test, y_pred) # 根据测试集的真实因变量和预测的因变量,生成混淆矩阵,用于评估分类器的性能
print('混淆矩阵:\n{0}'.format(cm)) # 打印混淆矩阵
# 可视化训练集的结果
from matplotlib.colors import ListedColormap # 导入颜色列表的工具
X_set, y_set = X_train, y_train # 将训练集的自变量和因变量赋值给X_set和y_set
X1, X2 = np.meshgrid(np.arange(start = X_set[:, 0].min() - 1, stop = X_set[:, 0].max() + 1, step = 0.01), # 生成一个网格,覆盖X_set的第0列的最小值和最大值之间的区间,步长为0.01
                     np.arange(start = X_set[:, 1].min() - 1, stop = X_set[:, 1].max() + 1, step = 0.01)) # 生成一个网格,覆盖X_set的第1列的最小值和最大值之间的区间,步长为0.01
plt.contourf(X1, X2, classifier.predict(np.array([X1.ravel(), X2.ravel()]).T).reshape(X1.shape), # 对网格中的每个点,使用分类器进行预测,然后用不同的颜色填充区域,表示不同的分类结果
             alpha = 0.75, cmap = ListedColormap(('red', 'green'))) # 设置透明度为0.75,颜色列表为红色和绿色
plt.xlim(X1.min(), X1.max()) # 设置x轴的范围为X1的最小值和最大值
plt.ylim(X2.min(), X2.max()) # 设置y轴的范围为X2的最小值和最大值
for i, j in enumerate(np.unique(y_set)): # 对于y_set中的每个唯一的值,即0和1
    plt.scatter(X_set[y_set == j, 0], X_set[y_set == j, 1], # 绘制训练集中对应的点,用不同的颜色和标签表示不同的类别
                color = ListedColormap(('red', 'green'))(i), label = j)
plt.title('Random Forest Classification (Training set)') # 设置标题为随机森林分类(训练集)
plt.xlabel('Age') # 设置x轴的标签为年龄
plt.ylabel('Estimated Salary') # 设置y轴的标签为估计薪水
plt.legend() # 显示图例
plt.show() # 显示图像


# 可视化测试集的结果
from matplotlib.colors import ListedColormap # 导入颜色列表的工具
X_set, y_set = X_test, y_test # 将测试集的自变量和因变量赋值给X_set和y_set
X1, X2 = np.meshgrid(np.arange(start = X_set[:, 0].min() - 1, stop = X_set[:, 0].max() + 1, step = 0.01), # 生成一个网格,覆盖X_set的第0列的最小值和最大值之间的区间,步长为0.01
                     np.arange(start = X_set[:, 1].min() - 1, stop = X_set[:, 1].max() + 1, step = 0.01)) # 生成一个网格,覆盖X_set的第1列的最小值和最大值之间的区间,步长为0.01
plt.contourf(X1, X2, classifier.predict(np.array([X1.ravel(), X2.ravel()]).T).reshape(X1.shape), # 对网格中的每个点,使用分类器进行预测,然后用不同的颜色填充区域,表示不同的分类结果
             alpha = 0.75, cmap = ListedColormap(('red', 'green'))) # 设置透明度为0.75,颜色列表为红色和绿色
plt.xlim(X1.min(), X1.max()) # 设置x轴的范围为X1的最小值和最大值
plt.ylim(X2.min(), X2.max()) # 设置y轴的范围为X2的最小值和最大值
for i, j in enumerate(np.unique(y_set)): # 对于y_set中的每个唯一的值,即0和1
    plt.scatter(X_set[y_set == j, 0], X_set[y_set == j, 1], # 绘制测试集中对应的点,用不同的颜色和标签表示不同的类别
                color= ListedColormap(('red', 'green'))(i), label = j)
plt.title('Random Forest Classification (Test set)') # 设置标题为随机森林分类(测试集)
plt.xlabel('Age') # 设置x轴的标签为年龄
plt.ylabel('Estimated Salary') # 设置y轴的标签为估计薪水
plt.legend() # 显示图例
plt.show() # 显示图像

0621f44f1feb8414ec5e50831118fa48.png

【机器学习】随机森林_第11张图片

【机器学习】随机森林_第12张图片

2.5 使用随机森林算法对一个北京空气质量数据集进行分类,预测PM2.5的浓度

数据预处理:

  • 导入pandas和numpy库,用于数据处理和科学计算。

  • 读取csv文件,获取数据集,包含多个特征和目标变量(PM2.5)。

  • 对数据集进行预处理,包括删除目标变量为空值的行,用每一列的平均值填充其他空值,删除不需要的序号列,将字符串类型的特征转换为数字类型的特征。

  • 将数据集按照8:2的比例分为训练集和测试集,其中训练集再分为10份,进行10折交叉验证,用于评估模型的性能。

  • 返回10折交叉验证的结果和测试集,用于后续的模型训练和预测。

# 导入pandas库,用于数据处理
import pandas as pd
# 导入numpy库,用于科学计算
import numpy as np


# 读取数据文件,从csv文件中读取北京空气质量数据
data = pd.read_csv('Bagging\PRSA_data_2010.1.1-2014.12.31.csv')


# 因为Sklearn随机森林不能处理字符串形式,因此需要将字符串的特征独热编码
# 这里没有实现独热编码,而是使用数字编码,因为对决策树而言,独热编码不那么重要




'''第一部分:缺失值的处理'''
# 因为Pm2.5是目标数据,如有缺失值直接删除这一条记录


# 定义一个函数,用于删除目标值为空值的行,其他列为缺失值则自动填充,并将目标变量放置在数据集最后一列
def DeleteTargetNan(exdata, targetstr):
    # 首先判断目标字段是否有缺失值
    if exdata[targetstr].isnull().any():
        # 首先确定缺失值的行数
        loc = exdata[targetstr][data[targetstr].isnull().values == True].index.tolist()
        # 然后删除这些行
        exdata = exdata.drop(loc)
    # 用每一列的平均值替换空值后的数据集重新赋值给exdata,覆盖原来的数据集
    exdata = exdata.fillna(exdata.mean())
    # 将目标字段至放在最后的一列
    targetnum = exdata[targetstr].copy()
    del exdata[targetstr]
    exdata[targetstr] = targetnum
    return exdata






# 定义一个函数,用于删除原始数据中不需要的字段名(序号列)
def Shanchu(exdata, aiduan=['No']):
    for ai in aiduan:
        if ai in exdata.keys():
            del exdata[ai]
    return exdata




# 定义一个函数,用于将数据中的属性值为字符串的进行数字编码
# 定义一个函数,用于将数据中的字符串类型的特征转换为数字类型的特征
def Digit(eadata):
    # 判断是字符串
    for jj in eadata: # 循环遍历数据的每一列
        try: # 尝试执行以下代码
            eadata[jj].values[0] + 1 # 取第一行的值,加上1
        except TypeError: # 如果发生类型错误,说明该列是字符串类型
            # 需要转为数字编码
            numlist = list(set(list(eadata[jj].values))) # 将该列的所有不同的值转换为一个列表
            zhuan = [numlist.index(jj) for jj in eadata[jj].values] # 将该列的每个值替换为其在列表中的索引
            eadata[jj] = zhuan # 将该列的值更新为数字编码
    return eadata # 返回转换后的数据




# 数据处理后最终的数据集


first = DeleteTargetNan(data, 'pm2.5') # 调用删除目标值为空值的函数
two = Shanchu(first) # 调用删除不需要的字段名的函数,删除不需要的序号列
third = Digit(two) # 调用数字编码的函数


# 将数据集按照8:2的比例分为训练、预测数据集。其中训练数据集再分为K份,进行K折交叉验证


# 定义一个函数,用于将数据集分为训练集和测试集,并对训练集进行K折交叉验证
def fenge(exdata, k=10, per=[0.8, 0.2]):
    # 总长度
    lent = len(exdata) # 计算数据集的总长度,即样本的数量
    alist = np.arange(lent) # 生成一个从0到总长度减1的整数数组,表示样本的索引
    np.random.shuffle(alist) # 对数组进行随机打乱,打破原来的顺序


    # 训练集
    xunlian_sign = int(lent * per[0]) # 根据训练集和测试集的比例,计算训练集的大小,即需要抽取的样本的数量
    xunlian = np.random.choice(alist, xunlian_sign, replace=False) # 从打乱后的数组中无放回地随机选择训练集的索引,即不重复地抽取指定数量的整数


    # 预测集索引
    yuce = np.array([i for i in alist if i not in xunlian]) # 从打乱后的数组中选择剩余的索引作为测试集的索引,即没有被选中的整数


    # 再将训练数据集分为K折
    # 存储字典
    save_dict = {} # 初始化一个空字典,用于存储K折交叉验证的结果,每一折包含一个训练集和一个测试集
    for jj in range(k): # 循环K次,对每一折进行以下操作
        save_dict[jj] = {} # 初始化一个空字典,用于存储第jj折的训练集和测试集
        length = len(xunlian) # 计算训练集的长度,即样本的数量
        # 随机选
        yuzhi = int(length / k) # 计算每一折的大小,即需要抽取的样本的数量,等于训练集的长度除以K
        yan = np.random.choice(xunlian, yuzhi, replace=False) # 从训练集的索引中无放回地随机选择第jj折的测试集的索引,即不重复地抽取指定数量的整数
        tt = np.array([i for i in xunlian if i not in yan]) # 从训练集的索引中选择剩余的索引作为第jj折的训练集的索引,即没有被选中的整数
        save_dict[jj]['train'] = exdata[tt] # 根据索引,从数据集中提取第jj折的训练集,作为一个二维数组
        save_dict[jj]['test'] = exdata[yan] # 根据索引,从数据集中提取第jj折的测试集,作为一个二维数组


    return save_dict, exdata[yuce] # 返回K折交叉验证的结果字典和测试集,作为一个元组




deeer = fenge(third.values) # 调用分割函数


# K折交叉的训练数据
dt_data = deeer[0] # 获取K折划分的结果
# 预测数据
predict_data = deeer[1] # 获取测试集

随机森林(random forest)算法来预测PM2.5的代码的主要内容和功能:

  • 首先,导入了一个名为pm25_RF_Data的模块,这个模块应该是包含了PM2.5的数据和相关的函数的。

  • 然后,导入了sklearn.ensemble中的RandomForestRegressor类,这个类是用来实现随机森林回归的。

  • 接着,导入了sklearn.metrics中的mean_squared_error函数,这个函数是用来计算均方误差(MSE)的,MSE是一种衡量回归模型性能的指标。

  • 然后,导入了numpymatplotlib.pyplot两个常用的科学计算和绘图的库。

  • 接着,定义了一些参数,比如树的个数、特征的个数等,这些参数是用来控制随机森林模型的复杂度和随机性的。

  • 然后,定义了几个函数,分别是:

    • Train函数,这个函数是用来训练随机森林模型,并给出训练数据和验证数据的预测值和MSE的。

    • Zuhe函数,这个函数是用来确定最优的参数组合的,它通过遍历所有可能的参数组合,并用K折交叉验证的方法来评估每个组合的性能,最后选择MSE最小的组合作为最优参数。

    • duibi函数,这个函数是用来绘制不同参数组合下MSE的对比曲线的,它可以帮助我们直观地看到不同参数组合的效果,并找出最优的参数组合。

    • recspre函数,这个函数是用来绘制真实值和预测值的对比曲线的,它可以帮助我们直观地看到模型的预测效果,并评估模型的准确性和稳定性。

  • 最后,调用了Zuhe函数来确定最优的参数组合,然后调用了duibi函数和recspre函数来绘制对比曲线,并保存到指定的路径下。

# 引入数据
import pm25_RF_Data as data # 导入pm25_RF_Data模块,该模块包含了用于训练和预测的数据


# 引入模型
from sklearn.ensemble import  RandomForestRegressor as RF # 导入随机森林回归模型,简写为RF
from sklearn.metrics import mean_squared_error as mse # 导入均方误差函数,简写为mse
import numpy as np # 导入numpy模块,用于处理数组和矩阵运算,简写为np


# 绘制不同参数下MSE的对比曲线
from pylab import mpl
mpl.rcParams['font.sans-serif'] = ['FangSong']  # 设置显示中文的字体为仿宋
mpl.rcParams['axes.unicode_minus'] = False  # 设置显示负号
import matplotlib.pyplot as plt # 导入matplotlib.pyplot模块,用于绘制图形,简写为plt


# 根据K折交叉的结果确定比较好的参数组合,然后给出预测数据真实值和预测值的对比


# 对于回归而言,主要的参数就是随机森林中树的个数和特征的个数,其他参数均使用默认值


# 树的个数
trees = [50, 500]#, 1000, 2000, 4000, 7000 # 定义一个列表,存储不同的树的个数


# 随机选择的特征个数
tezheng = ['auto']  #  回归问题一般选用所有的特征,即auto


# 训练函数
def Train(data, treecount, tezh, yanzhgdata): # 定义一个训练函数,接收四个参数:训练数据,树的个数,特征个数,验证数据
    model = RF(n_estimators=treecount, max_features=tezh) # 创建一个随机森林回归模型,设置树的个数和特征个数
    model.fit(data[:, :-1], data[:, -1]) # 使用训练数据的特征和标签,拟合模型
    # 给出训练数据的预测值
    train_out = model.predict(data[:, :-1]) # 使用模型对训练数据的特征进行预测,得到预测值
    # 计算MSE
    train_mse = mse(data[:, -1], train_out) # 使用mse函数,计算训练数据的真实值和预测值之间的均方误差


    # 给出验证数据的预测值
    add_yan = model.predict(yanzhgdata[:, :-1]) # 使用模型对验证数据的特征进行预测,得到预测值
    # 计算MSE
    add_mse = mse(yanzhgdata[:, -1], add_yan) # 使用mse函数,计算验证数据的真实值和预测值之间的均方误差
    print(train_mse, add_mse) # 打印训练数据和验证数据的均方误差
    return train_mse, add_mse # 返回训练数据和验证数据的均方误差


# 最终确定组合的函数
def Zuhe(datadict, tre=trees, tezhen=tezheng): # 定义一个函数,用于确定最优的参数组合,接收三个参数:数据字典,树的个数列表,特征个数列表
    # 存储结果的字典
    savedict = {} # 创建一个空字典,用于存储不同参数组合的均方误差的均值
    # 存储序列的字典
    sacelist = {} # 创建一个空字典,用于存储不同参数组合的均方误差的序列
    for t in tre: # 遍历树的个数列表
        for te in tezhen: # 遍历特征个数列表
            print(t, te) # 打印当前的参数组合  50 auto 
            sumlist = [] # 创建一个空列表,用于存储当前参数组合下的均方误差之和
            # 因为要展示折数,因此要按序开始
            ordelist = sorted(list(datadict.keys())) # 对数据字典的键(即折数)进行排序,得到一个有序列表
            for jj in ordelist: # 遍历有序列表
                xun, ya = Train(datadict[jj]['train'], t, te, datadict[jj]['test']) # 调用训练函数,传入对应折数的训练数据和验证数据,以及当前的参数组合,得到训练数据和验证数据的均方误差
                sumlist.append(xun + ya) # 将训练数据和验证数据的均方误差之和添加到列表中
            sacelist['%s-%s' % (t, te)] = sumlist # 以当前的参数组合为键,以每折均方误差之和的列表为值,存储到序列字典中
            savedict['%s-%s' % (t, te)] = np.mean(np.array(sumlist)) # 以当前的参数组合为键,以均方误差之和的列表的均值为值,存储到结果字典中


    # 在结果字典中选择最小的
    zuixao = sorted(savedict.items(), key=lambda fu: fu[1])[0][0] # 对结果字典按值进行排序,得到一个列表,取第一个元素的键,即最小的均方误差的均值对应的参数组合
    # 然后再选出此方法中和值最小的折数
    xiao = sacelist[zuixao].index(min(sacelist[zuixao])) # 在序列字典中,根据最优的参数组合,找到对应的均方误差之和的列表,取其中最小的元素的索引,即最优的折数
    return zuixao, xiao, sacelist # 返回最优的参数组合,最优的折数,和序列字典


# 根据字典绘制曲线
def duibi(exdict, you): # 定义一个函数,用于根据序列字典绘制不同参数组合的MSE对比曲线,接收两个参数:序列字典,最优的参数组合
    plt.figure(figsize=(11, 7)) # 创建一个图形,设置大小为11*7
    for ii in exdict: # 遍历序列字典的键(即参数组合)
        plt.plot(list(range(len(exdict[ii]))), exdict[ii], \
                 label='%s%d折MSE均值:%.3f' % (ii, len(exdict[ii]), np.mean(np.array(exdict[ii]))), lw=2) # 绘制每个参数组合下的均方误差之和的曲线,设置标签为参数组合,折数,和均方误差的均值,设置线宽为2
    plt.legend() # 显示图例
    plt.title('不同参数的组合MSE对比曲线[最优:%s]' % you) # 设置标题为不同参数的组合MSE对比曲线,显示最优的参数组合
    plt.savefig(r'C:\Users\GWT9\Desktop\method.jpg') # 保存图形到指定路径
    return '不同方法对比完毕' # 返回一个字符串,表示对比完成


# 定义一个函数,用于根据最优的参数组合绘制真实和预测值的对比曲线
def recspre(exstr, predata, datadict, zhe): # 接受四个参数:exstr是一个字符串,表示随机森林模型的参数,包括最大树数和最大特征数;predata是一个二维数组,表示要预测的数据集,最后一列是真实值;datadict是一个字典,表示不同折数的训练数据集,每个键对应一个二维数组,最后一列是标签;zhe是一个整数,表示要使用的折数。
    tree, te = exstr.split('-') # 根据'-'符号,将exstr分割为两个字符串,分别赋值给tree和te,表示最大树数和最大特征数
    model = RF(n_estimators=int(tree), max_features=te) # 创建一个随机森林回归模型对象,设置树的个数和特征个数
    model.fit(datadict[zhe]['train'][:, :-1], datadict[zhe]['train'][:, -1]) # 使用datadict[zhe]中的训练数据的特征和标签,拟合模型


    # 预测
    yucede = model.predict(predata[:, :-1]) # 使用模型对predata中的特征进行预测,得到一个一维数组yucede,表示预测值
    # 为了便于展示,选100条数据进行展示
    zongleng = np.arange(len(yucede)) # 生成一个从0到yucede的长度减1的整数数组,表示预测值的索引
    randomnum = np.random.choice(zongleng, 100, replace=False) # 从索引数组中无放回地随机选择100个元素,作为展示的索引


    yucede_se = list(np.array(yucede)[randomnum]) # 根据展示的索引,从yucede中提取对应的100个预测值,存入一个列表yucede_se


    yuce_re = list(np.array(predata[:, -1])[randomnum]) # 根据展示的索引,从predata中的真实值中提取对应的100个真实值,存入一个列表yuce_re


    # 对比
    plt.figure(figsize=(17, 9)) # 创建一个图形,设置大小为17*9
    plt.subplot(2, 1, 1) # 创建一个2行1列的子图,选择第一个位置
    plt.plot(list(range(len(yucede_se))), yucede_se, c='r', marker='*', label='预测', lw=2) # 绘制预测值的折线图,设置颜色为红色,标记为星形,标签为预测,线宽为2
    plt.plot(list(range(len(yuce_re))), yuce_re, c='b', marker='.', label='真实', lw=2) # 绘制真实值的折线图,设置颜色为蓝色,标记为点形,标签为真实,线宽为2
    plt.legend() # 显示图例
    plt.title('预测和真实值对比[最大树数%d]' % int(tree)) # 设置标题为预测和真实值对比,显示最大树数


    plt.subplot(2, 1, 2) # 创建一个2行1列的子图,选择第二个位置
    plt.plot(list(range(len(yucede_se))), np.array(yuce_re) - np.array(yucede_se), 'k--', marker='s', label='真实-预测', lw=2) # 绘制真实值和预测值的相对误差的折线图,设置颜色为黑色,线型为虚线,标记为方形,标签为真实-预测,线宽为2
    plt.legend() # 显示图例
    plt.title('预测和真实值相对误差') # 设置标题为预测和真实值相对误差


    plt.savefig(r'C:\Users\GWT9\Desktop\duibi.jpg') # 保存图形到指定的路径
    return '预测真实对比完毕' # 返回一个字符串,表示预测和真实值对比完毕


# 主函数


if __name__ == "__main__": # 如果当前模块是主模块,即直接运行而非导入
    zijian, zhehsu, xulie = Zuhe(data.dt_data) # 调用Zuhe函数,对data.dt_data数据集进行分割,得到三个返回值:zijian是一个字符串,表示随机森林模型的最优参数;zhehsu是一个整数,表示最优的折数;xulie是一个列表,表示不同参数组合的结果


    duibi(xulie, zijian) # 调用duibi函数,用xulie和zijian作为参数,绘制不同参数组合的MSE对比曲线,并显示最优参数的位置
    recspre(zijian, data.predict_data, data.dt_data, zhehsu) # 调用recspre函数,用zijian,data.predict_data,data.dt_data,zhehsu作为参数,进行预测和对比,并返回一个字符串

输出结果:(运行很慢,只取了树个数50,500)

【机器学习】随机森林_第13张图片

【机器学习】随机森林_第14张图片

【机器学习】随机森林_第15张图片

三、随机森林应用

【机器学习】随机森林_第16张图片

【机器学习】随机森林_第17张图片

参考网址:

https://zhuanlan.zhihu.com/p/105941858

https://easyai.tech/

https://rstudio-pubs-static.s3.amazonaws.com/278745_60156813ccd2466ea4625725dcdf7cdd.html 

https://blog.csdn.net/lenglingling/article/details/106409067

https://archive.ics.uci.edu/datasets  UIC数据集

https://github.com/VaishnaviKrishna/adult-dataset/tree/master?tab=readme-ov-file#prediction-of-annual-income-using-adult-dataset

https://archive.ics.uci.edu/ml/datasets/Adult 

你可能感兴趣的:(机器学习,随机森林,人工智能,算法,数据挖掘)