本文主要是介绍【数模修炼之旅】07 随机森林模型 深度解析(教程+代码),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
【数模修炼之旅】07 随机森林算法 深度解析(教程+代码)
接下来 C君将会用至少30个小节来为大家深度解析数模领域常用的算法,大家可以关注这个专栏,持续学习哦,对于大家的能力提高会有极大的帮助。
1 随机森林算法介绍及应用
随机森林(Random Forest)是一种集成学习算法,由多个决策树组成,通过对多个决策树的预测结果进行综合,来提高预测的准确性和鲁棒性。它在分类、回归和特征选择等任务中表现优异。通常在数模竞赛中,应用于回归任务和特征选择任务。
1.1 主要特点和工作原理
决策树集成:
- 随机森林由多棵决策树构成,每棵决策树是通过对训练数据的随机子集进行训练生成的。
- 每棵决策树在训练过程中,只考虑特征的一个随机子集来进行分裂。
数据子集和特征子集:
- Bootstrap抽样:从训练集中通过有放回的抽样生成不同的子集(即每棵树的训练数据集),这有助于减小模型的方差。
- 特征随机选择:在每个节点的分裂时,从特征子集中选择最优特征进行分裂,这减少了树与树之间的相关性。
模型预测:
- 对于分类任务,随机森林通过投票的方式确定最终的类别,即所有决策树的预测结果中出现次数最多的类别作为最终分类结果。
- 对于回归任务,随机森林计算所有决策树预测值的平均值作为最终预测值。
1.2 在数模中的应用
预测建模:
- 回归分析:用于预测连续变量,比如房价预测、需求预测等。通过随机森林回归模型,可以处理复杂的非线性关系,并对特征间的交互作用进行建模。
- 分类分析:用于分类问题,比如疾病诊断、市场细分等。通过集成多个决策树,随机森林能够提高分类准确率,处理特征噪声和不平衡数据问题。
特征选择:
- 特征重要性评估:随机森林提供了特征重要性评分,这可以帮助识别对目标变量最有影响的特征。在数模中,这有助于简化模型,选择关键特征,避免过拟合。
- 降维:在高维数据集上,随机森林可以通过特征重要性来进行特征选择,从而降低维度,提高模型性能。
异常检测:
数据异常识别:通过分析模型对数据点的预测误差,随机森林可以用于检测异常点(如欺诈检测、故障诊断等)。
模型融合与优化:
- 集成学习:在复杂模型中,随机森林可以与其他机器学习算法结合使用,形成更强大的预测模型。例如,结合随机森林和神经网络,可以提高预测性能。
- 参数优化:通过交叉验证和网格搜索等方法,对随机森林模型的超参数进行优化,以提高模型的泛化能力。
不确定性量化:
预测不确定性估计:随机森林能够提供每棵树的预测结果,这些预测结果的分布可以用来估计模型的预测不确定性,帮助做出更可靠的决策。
2 随机森林算法的基本步骤
随机森林算法的基本步骤可以分为以下几个阶段:数据准备、模型训练、预测和评估。
1. 数据准备
在使用随机森林算法之前,需要对数据进行适当的处理:
- 数据清洗:处理缺失值、异常值,并确保数据质量。
- 特征选择和预处理:可以进行特征缩放、编码分类变量等。
- 划分数据集:将数据集分为训练集和测试集,以评估模型的性能。
2.2. 模型训练
随机森林的训练过程包括以下几个主要步骤:
a. Bootstrap抽样
- 创建多个子数据集:
- 从原始训练数据集中,通过有放回的抽样(即每次抽样后将样本放回)生成多个子数据集。每个子数据集将用于训练一棵决策树。
b. 建立决策树
- 训练决策树:
- 对于每个子数据集,训练一棵决策树。每棵决策树的训练过程包括:
- 选择分裂特征:在每个节点分裂时,不是考虑所有特征,而是从特征的随机子集中选择一个或几个特征进行分裂。这种方法叫做“特征随机选择”。
- 节点分裂:使用选择的特征来决定如何分裂节点。常用的分裂标准包括基尼系数(分类任务)或均方误差(回归任务)。
- 树的生长:决策树在训练过程中会尽可能深地生长,直到满足某个停止条件(例如,达到最大深度或节点中的样本数少于某个阈值)。
- 重复训练:
- 重复上述步骤,生成多棵决策树。每棵树都是在不同的训练子集和特征子集上训练的。
2.3. 模型预测
一旦训练完成,就可以使用随机森林进行预测:
- 分类任务:
- 对于新的样本数据,将其输入到每棵决策树中。
- 每棵决策树会输出一个分类结果。
- 投票机制:所有决策树的分类结果通过投票机制决定最终的分类结果,即选择出现次数最多的类别作为最终预测结果。
- 回归任务:
- 对于新的样本数据,将其输入到每棵决策树中。
- 每棵决策树会输出一个预测值。
- 平均机制:所有决策树的预测值取平均作为最终的回归预测值。
2.4. 模型评估
评估随机森林模型的性能是确保其有效性的关键步骤:
- 准确率和误差计算:
- 对于分类任务,计算准确率、精确度、召回率和F1分数等。
- 对于回归任务,计算均方误差(MSE)、均方根误差(RMSE)和决定系数(R²)等。
- 交叉验证:
- 使用交叉验证(如K折交叉验证)来评估模型的泛化能力,即模型在不同数据子集上的表现,以减少过拟合风险。
- 特征重要性分析:
- 通过分析各个特征的重要性评分,了解哪些特征对模型预测最重要,从而进行特征选择和降维。
2.5. 超参数调整(可选)
为了提高模型的性能,可以对随机森林的超参数进行调整:
- 决策树数量(n_estimators):调整森林中决策树的数量,通常更多的决策树能提高模型的准确性,但也可能增加计算开销。
- 树的最大深度(max_depth):控制每棵决策树的最大深度,以避免过拟合。
- 每个节点的最小样本数(min_samples_split):在节点分裂时,要求至少有多少样本。
- 每棵树使用的特征数量(max_features):控制每棵树在分裂节点时考虑的特征数量。
3 随机森林算法代码(matlab+python)
3.1 python
随机森林需要调整的参数有:
(1) 决策树的个数
(2) 特征属性的个数
(3) 递归次数(即决策树的深度)'''
from numpy import inf
from numpy import zeros
import numpy as np
from sklearn.model_selection import train_test_split#生成数据集。数据集包括标签,全包含在返回值的dataset上
def get_Datasets():from sklearn.datasets import make_classificationdataSet,classLabels=make_classification(n_samples=200,n_features=100,n_classes=2)#print(dataSet.shape,classLabels.shape)return np.concatenate((dataSet,classLabels.reshape((-1,1))),axis=1)#切分数据集,实现交叉验证。可以利用它来选择决策树个数。但本例没有实现其代码。
#原理如下:
#第一步,将训练集划分为大小相同的K份;
#第二步,我们选择其中的K-1分训练模型,将用余下的那一份计算模型的预测值,
#这一份通常被称为交叉验证集;第三步,我们对所有考虑使用的参数建立模型
#并做出预测,然后使用不同的K值重复这一过程。
#然后是关键,我们利用在不同的K下平均准确率最高所对应的决策树个数
#作为算法决策树个数def splitDataSet(dataSet,n_folds): #将训练集划分为大小相同的n_folds份;fold_size=len(dataSet)/n_foldsdata_split=[]begin=0end=fold_sizefor i in range(n_folds):data_split.append(dataSet[begin:end,:])begin=endend+=fold_sizereturn data_split
#构建n个子集
def get_subsamples(dataSet,n):subDataSet=[]for i in range(n):index=[] #每次都重新选择k个 索引for k in range(len(dataSet)): #长度是kindex.append(np.random.randint(len(dataSet))) #(0,len(dataSet)) 内的一个整数subDataSet.append(dataSet[index,:])return subDataSet# subDataSet=get_subsamples(dataSet,10)
##############################################################################根据某个特征及值对数据进行分类
def binSplitDataSet(dataSet,feature,value):mat0=dataSet[np.nonzero(dataSet[:,feature]>value)[0],:]mat1=dataSet[np.nonzero(dataSet[:,feature]<value)[0],:]return mat0,mat1'''feature=2 value=1dataSet=get_Datasets()mat0,mat1= binSplitDataSet(dataSet,2,1)
'''#计算方差,回归时使用
def regErr(dataSet):return np.var(dataSet[:,-1])*np.shape(dataSet)[0]#计算平均值,回归时使用
def regLeaf(dataSet):return np.mean(dataSet[:,-1])def MostNumber(dataSet): #返回多类#number=set(dataSet[:,-1])len0=len(np.nonzero(dataSet[:,-1]==0)[0])len1=len(np.nonzero(dataSet[:,-1]==1)[0])if len0>len1:return 0else:return 1#计算基尼指数 一个随机选中的样本在子集中被分错的可能性 是被选中的概率乘以被分错的概率
def gini(dataSet):corr=0.0for i in set(dataSet[:,-1]): #i 是这个特征下的 某个特征值corr+=(len(np.nonzero(dataSet[:,-1]==i)[0])/len(dataSet))**2return 1-corrdef select_best_feature(dataSet,m,alpha="huigui"):f=dataSet.shape[1] #拿过这个数据集,看这个数据集有多少个特征,即f个index=[]bestS=inf;bestfeature=0;bestValue=0;if alpha=="huigui":S=regErr(dataSet)else:S=gini(dataSet)for i in range(m):index.append(np.random.randint(f)) #在f个特征里随机,注意是随机!选择m个特征,然后在这m个特征里选择一个合适的分类特征。 for feature in index:for splitVal in set(dataSet[:,feature]): #set() 函数创建一个无序不重复元素集,用于遍历这个特征下所有的值mat0,mat1=binSplitDataSet(dataSet,feature,splitVal) if alpha=="huigui": newS=regErr(mat0)+regErr(mat1) #计算每个分支的回归方差else:newS=gini(mat0)+gini(mat1) #计算被分错率if bestS>newS:bestfeature=featurebestValue=splitValbestS=newS if (S-bestS)<0.001 and alpha=="huigui": # 对于回归来说,方差足够了,那就取这个分支的均值return None,regLeaf(dataSet)elif (S-bestS)<0.001:#print(S,bestS)return None,MostNumber(dataSet) #对于分类来说,被分错率足够下了,那这个分支的分类就是大多数所在的类。#mat0,mat1=binSplitDataSet(dataSet,feature,splitVal)return bestfeature,bestValuedef createTree(dataSet,alpha="huigui",m=20,max_level=10): #实现决策树,使用20个特征,深度为10,bestfeature,bestValue=select_best_feature(dataSet,m,alpha=alpha)if bestfeature==None:return bestValueretTree={}max_level-=1if max_level<0: #控制深度return regLeaf(dataSet)retTree['bestFeature']=bestfeatureretTree['bestVal']=bestValuelSet,rSet=binSplitDataSet(dataSet,bestfeature,bestValue) #lSet是根据特征bestfeature分到左边的向量,rSet是根据特征bestfeature分到右边的向量retTree['right']=createTree(rSet,alpha,m,max_level)retTree['left']=createTree(lSet,alpha,m,max_level) #每棵树都是二叉树,往下分类都是一分为二。#print('retTree:',retTree)return retTreedef RondomForest(dataSet,n,alpha="huigui"): #树的个数#dataSet=get_Datasets()Trees=[] # 设置一个空树集合for i in range(n):X_train, X_test, y_train, y_test = train_test_split(dataSet[:,:-1], dataSet[:,-1], test_size=0.33, random_state=42)X_train=np.concatenate((X_train,y_train.reshape((-1,1))),axis=1)Trees.append(createTree(X_train,alpha=alpha))return Trees # 生成好多树
####################################################################预测单个数据样本,重头!!如何利用已经训练好的随机森林对单个样本进行 回归或分类!
def treeForecast(trees,data,alpha="huigui"): if alpha=="huigui":if not isinstance(trees,dict): #isinstance() 函数来判断一个对象是否是一个已知的类型return float(trees)if data[trees['bestFeature']]>trees['bestVal']: # 如果数据的这个特征大于阈值,那就调用左支if type(trees['left'])=='float': #如果左支已经是节点了,就返回数值。如果左支还是字典结构,那就继续调用, 用此支的特征和特征值进行选支。 return trees['left']else:return treeForecast(trees['left'],data,alpha)else:if type(trees['right'])=='float':return trees['right']else:return treeForecast(trees['right'],data,alpha) else:if not isinstance(trees,dict): #分类和回归是同一道理return int(trees)if data[trees['bestFeature']]>trees['bestVal']:if type(trees['left'])=='int':return trees['left']else:return treeForecast(trees['left'],data,alpha)else:if type(trees['right'])=='int':return trees['right']else:return treeForecast(trees['right'],data,alpha) #随机森林 对 数据集打上标签 0、1 或者是 回归值
def createForeCast(trees,test_dataSet,alpha="huigui"):cm=len(test_dataSet) yhat=np.mat(zeros((cm,1)))for i in range(cm): #yhat[i,0]=treeForecast(trees,test_dataSet[i,:],alpha) #return yhat#随机森林预测
def predictTree(Trees,test_dataSet,alpha="huigui"): #trees 是已经训练好的随机森林 调用它!cm=len(test_dataSet) yhat=np.mat(zeros((cm,1))) for trees in Trees:yhat+=createForeCast(trees,test_dataSet,alpha) #把每次的预测结果相加if alpha=="huigui": yhat/=len(Trees) #如果是回归的话,每棵树的结果应该是回归值,相加后取平均else:for i in range(len(yhat)): #如果是分类的话,每棵树的结果是一个投票向量,相加后,#看每类的投票是否超过半数,超过半数就确定为1if yhat[i,0]>len(Trees)/2: yhat[i,0]=1else:yhat[i,0]=0return yhatif __name__ == '__main__' :dataSet=get_Datasets() print(dataSet[:,-1].T) #打印标签,与后面预测值对比 .T其实就是对一个矩阵的转置RomdomTrees=RondomForest(dataSet,4,alpha="fenlei") #这里我训练好了 很多树的集合,就组成了随机森林。一会一棵一棵的调用。print("---------------------RomdomTrees------------------------")#print(RomdomTrees[0])test_dataSet=dataSet #得到数据集和标签yhat=predictTree(RomdomTrees,test_dataSet,alpha="fenlei") # 调用训练好的那些树。综合结果,得到预测值。print(yhat.T)
#get_Datasets()print(dataSet[:,-1].T-yhat.T)
3.2 matlab
% 1. 数据准备
% 读取数据
data = readtable('data.csv'); % 替换为你的数据文件% 假设目标变量为'target',其余为特征
features = data(:, setdiff(data.Properties.VariableNames, 'target'));
target = data.target;% 划分数据集
cv = cvpartition(height(data), 'HoldOut', 0.3); % 70%训练,30%测试
trainData = data(training(cv), :);
testData = data(test(cv), :);% 提取特征和目标变量
trainFeatures = trainData(:, setdiff(data.Properties.VariableNames, 'target'));
trainTarget = trainData.target;
testFeatures = testData(:, setdiff(data.Properties.VariableNames, 'target'));
testTarget = testData.target;% 2. 训练随机森林模型(分类任务示例)
numTrees = 100; % 决策树数量
rfModel = TreeBagger(numTrees, trainFeatures, trainTarget, 'Method', 'classification');% 3. 模型预测
predictedLabels = predict(rfModel, testFeatures);% 4. 模型评估(分类任务)
% 计算分类准确率
accuracy = sum(strcmp(predictedLabels, testTarget)) / length(testTarget);
disp(['Accuracy: ', num2str(accuracy)]);% 计算混淆矩阵
confMat = confusionmat(testTarget, predictedLabels);
disp('Confusion Matrix:');
disp(confMat);% 5. 特征重要性分析(分类任务)
importance = rfModel.OOBPermutedPredictorDeltaError;
disp('Feature Importance:');
disp(importance);% ----
% 6. 若进行回归任务,替换以下部分:% 训练随机森林模型(回归任务示例)
% rfModel = TreeBagger(numTrees, trainFeatures, trainTarget, 'Method', 'regression');% 模型预测
% predictedValues = predict(rfModel, testFeatures);% 模型评估(回归任务)
% 计算均方误差
% mse = mean((predictedValues - testTarget).^2);
% disp(['Mean Squared Error: ', num2str(mse)]);% 计算决定系数(R²)
% ssRes = sum((predictedValues - testTarget).^2);
% ssTot = sum((testTarget - mean(testTarget)).^2);
% r2 = 1 - (ssRes / ssTot);
% disp(['R-squared: ', num2str(r2)]);% 特征重要性分析(回归任务)
% importance = rfModel.OOBPermutedPredictorDeltaError;
% disp('Feature Importance:');
% disp(importance);
代码注意事项:
需要参加数模竞赛的同学,可以看我的这个名片,会有最新的助攻哦:(大型比赛前会对名片进行更新)
这篇关于【数模修炼之旅】07 随机森林模型 深度解析(教程+代码)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!