首页 / 资讯中心 / 文章详情

随机森林实战:棉蚜等级预测与农业机器学习避坑指南

随机森林实战:棉蚜等级预测与农业机器学习避坑指南 ★ FEATURED ARTICLE
简介这份PPT课件面向计算机、农业信息化及相关专业的学习者与科研人员系统讲解机器学习、大数据与图像处理技术在农业病虫害预测预报中的应用适合具备一定算法基础、希望了解农业智能决策场景的读者。资源包共1个pptx文件约1.38MB内容以章节讲义形式组织便于课堂展示与自学查阅。课件围绕随机森林在棉蚜等级预测中的应用展开涵盖Bagging算法、随机子空间、袋外数据估计等原理并延伸至基于邻域核函数的局部支持向量机树木图像分类、Spark支持向量机小麦病害识别、Hadoop平台粒子群优化以及深度学习小麦蚜虫短期预测等案例。已有199人学习可帮助读者理解特征选择、模型构建与投票决策流程掌握将算法落地于农业预测的完整思路为课程汇报或科研选题提供参考。1. 从一份农业机器学习课件说起随机森林怎么落到棉蚜预测上棉蚜这种害虫山东滨州种棉花的农户最有发言权——发生时间长、繁殖速度快、危害重、难防治一旦爆发就是减产。传统预测靠统计法、实验法和观察法说白了就是看天吃饭加经验判断。这份《机器学习、大数据技术和图像处理技术在农业中的应用》课件核心讲的就是怎么用随机森林把棉蚜等级预测这件事从拍脑袋变成算出来。它适合谁做农业信息化、病虫害预测、或者想找一个真实场景练手机器学习分类任务的人。课件里给了完整的技术链路从数据不平衡处理、影响因子筛选到随机森林建模、OOB 估计再到 Spark 和 Hadoop 平台上的支持向量机应用。不是纯理论是带着滨州地区实际采集的气象和天敌数据在跑。下面我按能复现的标准把这份资源拆开讲。2. 随机森林做棉蚜等级预测从 Bagging 到 OOB 估计的完整链路2.1 为什么选随机森林而不是单棵决策树棉蚜等级预测本质上是一个分类问题给定一天的气象条件和天敌数量判断当天棉蚜发生等级属于哪一类。单棵决策树的问题在于它对训练数据太敏感——换一批样本树的结构可能完全变了方差大容易过拟合。随机森林的思路是用集体智慧来降方差。课件里讲得很清楚随机森林由 Leo Breiman 提出核心是把 Bagging 算法和随机子空间算法结合在一起。Bagging 做的是样本层面的随机化有放回地从 N 个样本中抽 n 个训练一棵树重复多次。随机子空间做的是特征层面的随机化每个节点分裂时不从全部 M 个特征里选最优而是先随机抽 m 个特征再从这 m 个里选最佳分裂属性。这两个随机化叠加的效果是每棵树看到的样本不同、用到的特征也不同树与树之间的相关性被压低。最后投票的时候即使个别树出错多数树的结果也能把方向拉回来。课件里给的分类公式是$$f(x_t) \text{majority vote}{h_i(x_t)}{i1}^{N{tree}}$$其中 $N_{tree}$ 是树的数量$h_i(x_t)$ 是第 i 棵树对样本 $x_t$ 的分类结果。这个公式看着简单但它是整个算法能工作的根基——多数投票的前提是每棵树的错误是相对独立的而两个随机化过程就是在保证这种独立性。实际调参时有两个关键参数树的数量 n 和每次分裂随机选取的特征数 m。课件的建议是 n 一般取比较大几百到上千m 取 $\sqrt{M}$ 或 $\text{int}(\log_2 M)$。这个经验值不是拍脑袋来的——m 太小每棵树的分类能力太弱m 太大树之间的差异性不够投票就失去意义。2.2 数据不平衡处理4:1 的类别比例为什么必须修课件里有一组很实在的数据。原始数据集里类别 1 有 198 条记录占 79.8%类别 2 只有 50 条占 20.2%。训练集和测试集也维持了这个约 4:1 的比例。这意味着什么如果模型偷懒把所有样本都预测为类别 1准确率也有 80%。但这样的模型对类别 2 的预测能力几乎为零而类别 2 恰恰是需要重点关注的那一类。课件指出了不平衡数据导致精度下降的两个原因一是算法目标是最小化总体误差小类对总体误差的贡献低模型没动力去学好它二是算法本身假设数据分布平衡假定不同类别的误差带来相同损失。这两个原因在逻辑回归、SVM 上同样成立不是随机森林独有的问题。处理方式课件选了过采样和欠采样结合。具体操作是对类别 2 的数据进行补充补充后类别 1 有 100 条49.3%类别 2 有 103 条50.7%比例接近 1:1。这里有个细节值得注意课件只对总体数据集做了补充没有单独对训练集和测试集做因为训练集和测试集的类别比例与总体一致补充总体等价于按比例补充了训练和测试。用 Python 复现这个过程的思路大致如下import pandas as pd from imblearn.over_sampling import SMOTE from imblearn.under_sampling import RandomUnderSampler from imblearn.pipeline import Pipeline # 假设 df 是原始数据target 是棉蚜等级列 X df.drop(columns[target]) y df[target] # 先过采样少数类到 1:1再欠采样多数类 over SMOTE(sampling_strategy0.8, random_state42) under RandomUnderSampler(sampling_strategy1.0, random_state42) pipeline Pipeline([(over, over), (under, under)]) X_balanced, y_balanced pipeline.fit_resample(X, y) print(y_balanced.value_counts())这段代码的逻辑是先用 SMOTE 把少数类扩充到多数类的 80%再用随机欠采样把多数类压到和少数类一样多。参数sampling_strategy控制目标比例random_state保证结果可复现。课件里用的是过采样和欠采样结合SMOTE 是过采样的一种常见实现如果数据量很小也可以用简单的随机过采样替代。注意平衡处理必须在划分训练集和测试集之前做还是之后做取决于你的评估策略。如果先划分再平衡训练集测试集保持原始分布评估结果更接近真实场景。课件是对总体数据做的平衡然后按比例划分两种做法各有适用场景。2.3 影响因子筛选15 个气象因子怎么砍到 12 个课件里列了滨州地区采集的 15 个影响因子20-20 时降水量、极大风速、平均本站气压、平均风速、平均气温、平均水汽压、平均相对湿度、日照时数、日最低本站气压、日最低气温、日最高本站气压、日最高气温、最大风速、最小相对湿度再加上天敌数据。15 个因子直接扔进模型不是不行但冗余变量会增加计算量还可能引入噪声。课件的做法是用皮尔逊相关性分析计算每个因子和棉蚜等级之间的相关系数。结果如下因子相关系数处理X1 20-20时降水量0.05保留影响相对湿度X2 极大风速0.12保留X3 平均本站气压-0.22保留X4 平均风速-0.019剔除X5 平均气温0.30保留X6 平均水汽压0.34保留X7 平均相对湿度0.13保留X8 日照时数-0.09剔除X9 日最低本站气压-0.19保留X10 日最低气温0.30保留X11 日最高本站气压-0.22保留X12 日最高气温0.23保留X13 最大风速-0.0019剔除X14 最小相对湿度0.19保留X15 天敌数据0.19保留剔除的是平均风速、日照时数、最大风速这三个相关系数偏小的因子。20-20 时降水量虽然相关系数只有 0.05但课件特意保留了它理由是降水会影响相对湿度而相对湿度对棉蚜增长影响较大——这是一种基于领域知识的因果推断不是纯看统计指标。天敌数据 X15 的相关系数是 0.19不算特别高但课件也保留了。原因是此前棉蚜预测的论文大多只考虑气象因子而天敌瓢虫、蜘蛛、食蚜蝇等对棉蚜种群的抑制作用在生物学上是明确的统计上不显著可能是因为采样频率或时间窗口的问题。筛选后的因子从 15 个降到 12 个。这个步骤在实际项目里很容易被跳过但课件把它单独拎出来讲说明在农业数据这种特征维度不高但领域知识密集的场景里特征筛选的价值不在于降维本身而在于把领域知识编码进模型。2.4 OOB 估计不额外划验证集也能评估泛化误差随机森林有一个很实用的特性每棵树训练时大约有 37% 的样本没有被抽到。这个比例来自 $(1-1/N)^N$当 N 足够大时收敛于 $1/e \approx 0.368$。这些没被抽到的样本叫袋外数据Out Of BagOOB。OOB 的用法是对每棵树用它没见过的那些 OOB 样本来测试统计错误率。把所有树的 OOB 错误率汇总就得到随机森林的泛化误差估计。课件里特别对比了 OOB 估计和交叉验证交叉验证需要反复划分和合并数据计算量大时间复杂度和空间复杂度都高OOB 估计在训练过程中顺便就完成了几乎不额外消耗资源。用 scikit-learn 的话OOB 误差可以直接拿到from sklearn.ensemble import RandomForestClassifier rf RandomForestClassifier( n_estimators500, # 树的数量 max_featuressqrt, # 每次分裂随机选取的特征数sqrt(M) oob_scoreTrue, # 开启 OOB 估计 random_state42, n_jobs-1 ) rf.fit(X_train, y_train) print(fOOB 得分: {rf.oob_score_:.4f})oob_scoreTrue是关键参数开启后拟合完成就能通过oob_score_拿到 OOB 准确率。max_featuressqrt对应课件里 m 取 $\sqrt{M}$ 的建议。n_estimators设 500 是常见起点如果 OOB 得分随树的数量增加还在明显上升可以继续加。提示OOB 估计的前提是样本量不能太小。如果 N 只有几十条37% 的 OOB 样本可能只有十几条估计的方差会很大。课件里的数据集有 248 条记录OOB 样本约 90 条这个量级做估计是够的。3. 从单机到集群Spark 和 Hadoop 平台上的支持向量机怎么跑3.1 为什么棉蚜预测之外还要上大数据平台课件里除了随机森林还讲了基于 Spark 的支持向量机在小麦病害图像识别中的应用以及 Hadoop 平台下基于粒子群的局部支持向量机。这两部分和棉蚜预测的场景不同——小麦病害图像识别的数据量远大于气象表格数据单机跑 SVM 训练会非常慢。Spark 的优势在于内存计算。SVM 的训练过程需要反复迭代每次迭代都要读取数据如果每次从磁盘读I/O 就是瓶颈。Spark 把数据缓存在内存里迭代时直接读内存速度提升明显。课件里用的是 Spark 的 MLlib 库它内置了线性 SVM 的实现。Hadoop 平台下的局部支持向量机则是另一个思路。局部 SVM 的核心思想是不对全局数据训练一个 SVM而是对每个测试样本找到它附近的训练样本用这些局部样本训练一个 SVM 来做预测。这样做的好处是能处理非线性的决策边界但计算量会随样本数增加而急剧上升。用粒子群算法PSO来优化局部 SVM 的参数是为了在可接受的时间内找到较好的参数组合。3.2 Spark 上跑 SVM 的关键配置如果要在 Spark 上复现小麦病害图像识别的流程大致分三步图像特征提取、数据加载、SVM 训练。课件没有给具体代码但按常见做法可以用 Spark 的VectorAssembler把图像特征拼成向量再用LinearSVC训练。from pyspark.ml.classification import LinearSVC from pyspark.ml.feature import VectorAssembler from pyspark.ml.evaluation import MulticlassClassificationEvaluator from pyspark.sql import SparkSession spark SparkSession.builder \ .appName(WheatDiseaseSVM) \ .config(spark.executor.memory, 4g) \ .config(spark.driver.memory, 2g) \ .getOrCreate() # 假设 df 已经包含图像特征列 feat1, feat2, ... 和标签列 label assembler VectorAssembler( inputCols[ffeat{i} for i in range(1, 101)], outputColfeatures ) df_vec assembler.transform(df) # 划分训练集和测试集 train, test df_vec.randomSplit([0.8, 0.2], seed42) # 训练线性 SVM svm LinearSVC( featuresColfeatures, labelCollabel, maxIter100, regParam0.01 ) model svm.fit(train) # 评估 predictions model.transform(test) evaluator MulticlassClassificationEvaluator( labelCollabel, predictionColprediction, metricNameaccuracy ) accuracy evaluator.evaluate(predictions) print(f测试集准确率: {accuracy:.4f})spark.executor.memory和spark.driver.memory根据集群规模调整单机伪分布式模式下 4g 和 2g 是常见配置。maxIter100是迭代次数上限regParam0.01是正则化参数控制过拟合。这两个参数需要根据实际数据调没有万能值。注意Spark MLlib 的LinearSVC只支持线性核。如果小麦病害图像的特征不是线性可分的需要先用核方法做特征映射或者换用SVMWithSGD已弃用或第三方库。课件里提到的基于 Spark 的支持向量机具体用的是哪个实现需要看原始代码才能确认。3.3 Hadoop 平台下 PSO 优化局部 SVM 的流程Hadoop 平台下的局部 SVM 加粒子群优化流程比 Spark 上的线性 SVM 复杂得多。大致步骤是用 HDFS 存储训练数据和测试数据。对每个测试样本用 MapReduce 任务找到它的 k 个最近邻训练样本。对每个测试样本的局部训练集用 PSO 优化 SVM 的惩罚参数 C 和核参数 gamma。用优化后的参数训练局部 SVM对测试样本做预测。汇总所有测试样本的预测结果计算准确率。这个流程的计算瓶颈在第 2 步和第 3 步。第 2 步的 k 近邻搜索在 MapReduce 里需要每个测试样本和所有训练样本计算距离数据量大时通信开销很高。第 3 步的 PSO 对每个测试样本都要跑一次优化如果测试样本有几千条计算量是惊人的。课件的价值在于给出了这个思路的可行性验证但实际部署时需要做很多工程优化比如用 KD 树或球树加速近邻搜索用 Spark 替代 Hadoop 做迭代计算或者对 PSO 的种群规模和迭代次数做限制。4. 避坑与排查农业机器学习项目里最容易翻车的五个地方4.1 数据不平衡处理完测试集分布变了现象平衡处理后在训练集上准确率很高但换一批新数据测试准确率掉得厉害。原因如果对全体数据做平衡再划分训练测试测试集的类别分布也被改了评估结果不能反映真实场景。真实场景里类别 2 就是少模型在类别 2 上的表现才是关键。解决先划分训练集和测试集只对训练集做平衡测试集保持原始分布。评估时除了看总体准确率还要看少数类的召回率和 F1。4.2 皮尔逊相关系数筛选因子把非线性关系筛掉了现象某个因子和标签的皮尔逊相关系数接近 0被剔除后模型效果反而下降。原因皮尔逊相关系数只衡量线性关系。如果因子和标签是非线性的比如温度过高和过低都抑制棉蚜呈倒 U 型相关系数可能接近 0但因子是有用的。解决筛选因子时不要只看皮尔逊相关系数结合互信息、随机森林的特征重要性、或领域知识综合判断。课件里保留降水量因子就是领域知识压过统计指标的案例。4.3 OOB 估计和交叉验证结果差距大现象OOB 得分 0.855 折交叉验证只有 0.78。原因OOB 估计假设样本是独立同分布的。如果数据有时间序列结构比如连续多天的气象数据相邻样本不独立OOB 估计会偏乐观。交叉验证如果也是随机划分同样有问题。解决时间序列数据用时间序列交叉验证按时间顺序划分不要用随机划分。OOB 估计在时间序列场景下只能作为参考不能作为最终评估。4.4 Spark 任务内存溢出现象Spark 任务跑到一半报OutOfMemoryError。原因spark.executor.memory设得太小或者数据倾斜导致某个 executor 处理的数据量远大于其他。解决先加大 executor 内存如果还不行检查数据分布是否均匀。图像特征数据容易倾斜因为不同类别的图像数量可能差很多。可以用repartition重新分区或者对少数类做采样。4.5 随机森林树的数量设太少投票不稳定现象每次重新训练模型准确率波动很大差的时候能差 5 个百分点。原因树的数量 n 太小比如只设了 10 或 20。树太少时个别树的随机性对投票结果影响大模型方差高。解决n 至少设 100一般 300 到 500 就足够稳定。可以画学习曲线看 OOB 得分随 n 的变化当得分趋于平稳时当前的 n 就够了。5. 进阶技巧用特征重要性反推农业影响因子比相关系数更靠谱课件里用皮尔逊相关系数筛选因子这个方法简单直观但前面说了它只捕捉线性关系。随机森林自带特征重要性评估能捕捉非线性关系而且不需要额外计算——训练完直接就能拿到。import numpy as np import matplotlib.pyplot as plt # 假设 rf 是训练好的 RandomForestClassifier importances rf.feature_importances_ indices np.argsort(importances)[::-1] # 假设 feature_names 是因子名称列表 feature_names [X1, X2, X3, X5, X6, X7, X9, X10, X11, X12, X14, X15] plt.figure(figsize(10, 6)) plt.title(棉蚜等级预测因子重要性排序) plt.bar(range(len(importances)), importances[indices], aligncenter) plt.xticks(range(len(importances)), [feature_names[i] for i in indices], rotation45) plt.tight_layout() plt.show() # 打印排序结果 for i in indices: print(f{feature_names[i]}: {importances[i]:.4f})feature_importances_返回的是每个特征在所有树中分裂时带来的不纯度减少的平均值归一化后总和为 1。这个指标的好处是不管特征和标签是线性还是非线性关系只要特征在分裂中被频繁使用且能有效降低不纯度重要性就高。我一般会把这个结果和皮尔逊相关系数对比。如果某个因子在随机森林里重要性很高但皮尔逊相关系数很低大概率是非线性关系值得保留。反过来如果皮尔逊相关系数高但随机森林重要性低可能是这个因子和其他因子高度相关信息冗余了。还有一个进阶用法是用 SHAP 值做单样本解释。随机森林的特征重要性是全局的SHAP 能告诉你对于某一个具体样本每个因子是推高了还是拉低了预测结果。这在农业场景里很有用——农户想知道今天为什么预测棉蚜等级高SHAP 能给出具体原因比如平均气温 26℃ 推高了预测等级天敌数量偏少也推高了预测等级。import shap explainer shap.TreeExplainer(rf) shap_values explainer.shap_values(X_test) # 对第一个测试样本做解释 shap.initjs() shap.force_plot(explainer.expected_value[1], shap_values[1][0], X_test.iloc[0], feature_namesfeature_names)TreeExplainer是专门为树模型优化的计算速度比通用的KernelExplainer快很多。shap_values[1]取的是类别 1 的 SHAP 值[0]是第一个测试样本。force_plot会生成一个可视化图红色表示推高预测的因子蓝色表示拉低的因子。从那以后我每次做完农业数据的分类模型都会强制走一遍特征重要性加 SHAP 解释的流程。因为农业场景里模型可解释性和准确率一样重要——农户不会信任一个黑匣子他们需要知道为什么。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站