多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

用极端随机树实现遥感岩性识别:特征构建与调参实战

用极端随机树实现遥感岩性识别:特征构建与调参实战 简介针对遥感图像岩性识别中人工特征依赖与参数调优繁琐的痛点采用极端随机树模型并集成布谷鸟、粒子群等优化算法实现端到端自动调参所有工程代码基于Python编写是遥感地学与机器学习交叉方向的高分毕设工程。项目面向计算机、人工智能、遥感地信等相关专业在校生及企业算法人员尤其适合作为课程设计、毕业设计或算法对比实验的参照方案。包体共10个文件含8个Python源码、1个训练好的RF模型pickle文件及1份Markdown说明文档代码模块覆盖数据预处理、TXT转CSV、模型构建训练、布谷鸟与PSO寻优等完整流程压缩包仅63KB方便快速部署与二次开发。目前已有159人学习下载资源描述显示测试运行稳定、答辩评价高既可直接复现岩性识别流程也便于替换数据以拓展至其他遥感分类任务。整体轻量且模块化适合逐步跟踪调试。1. 用极端随机树做遥感岩性识别为什么这个方案值得你亲手跑一遍地质填图、矿产勘查甚至工程选址里最耗时的环节之一就是把遥感影像上的色调、纹理差异翻译成岩性界线。传统目视解译靠肉眼看波段组合一个图幅解译下来要两三天而且不同人圈出来的界线经常对不上。极端随机树模型把这件事变成“选特征、点样本、跑模型、出图”的自动化流水线每个像元被当成一个样本光谱特征加地形特征送进树模型输出岩性类别。这篇文章给你完整落地路径岩性识别为什么能用遥感数据做、极端随机树比随机森林强在哪、布谷鸟和粒子群两种优化算法怎么给模型调参以及全程 Python 代码和写文档说明时该突出的重点。适合手里有遥感影像、想用一份能复用代码直接跑通的人群新手能跟着做熟手能避开几个常见大坑。2. 岩性识别的光谱基础与极端随机树模型这对组合为什么靠谱2.1 遥感岩性识别的物理基础我们从影像里能看到什么岩石种类不同造岩矿物组合不同反射光谱就有差异。碳酸盐岩在 2.3 μm 附近有明显的吸收特征黏土矿物在 2.2 μm 附近有 Al-OH 吸收带铁氧化物在可见光红波段到蓝波段之间吸收强烈。Landsat 8 OLI 的第二个到第七个波段恰好覆盖这些区间所以可以用短波红外两个波段的比值识别黏土化用红/蓝比值识别铁染再叠上 DEM 派生的坡度和坡向一个像元就能组成十几维的特征向量。这就是整个岩性识别方案能成立的物理前提。要澄清一个定位遥感岩性识别和遥感图像目标检测不是一回事。目标检测在影像里找“哪里有矿坑、哪里有水体”输出外接框岩性识别做的是像元级分类每个像素被分到某个岩性类别输出是一张栅格图。既然是分类任务随机森林、支持向量机也都能做真正的问题是它们在二三十维光谱地形特征、十几万甚至几十万像元样本、类别又高度不平衡时精度和效率不好兼顾。这也是极端随机树在这类任务里被频繁选用的原因。2.2 极端随机树 vs 随机森林Extra-Trees 到底“极端”在哪里极端随机树Extremely Randomized Trees简称 Extra-Trees与随机森林的差别从名字就能看出比随机森林更随机。随机森林做两件随机的事——用 bootstrap 抽样构造每棵树的训练子集在每个节点从随机特征子集中寻找最优分裂阈值。Extra-Trees 把随机推到极致第一不抽样每棵树直接用全量训练集第二分裂阈值也是随机生成的算法在几个随机阈值里挑一个让不纯度下降最大的而不是遍历所有特征值找全局最优。这两处改动换来的是更低的方差和更快训练速度代价是单棵树的偏差略微变大靠多棵树集成把偏差补回来。在遥感岩性识别场景里这个特点非常合适。遥感像元样本在空间上高度自相关同一岩性单元里相邻像素光谱几乎一样bootstrap 抽样对降低方差的帮助有限特征又多——原始波段、比值、地形、纹理叠起来能到二三十维其中很多特征互相相关随机阈值分裂反而让树在特征选择上更均匀不容易被一两个强特征带偏。我拿同一份数据对比过随机森林和 Extra-Trees总体精度接近时极端随机树的训练时间大约少三分之一对特征噪声也更不敏感。2.3 为什么必须参数调优Extra-Trees 的门槛在超参数sklearn 里 ExtraTreesClassifier 的默认参数是通用经验值不是为遥感数据设计的。n_estimators 默认 100对像元级分类动辄几十万样本来说偏少max_features 默认 sqrt高维特征下每个节点只随机考察四五个特征树与树之间相关性变高集成效果打折扣min_samples_leaf 默认 1遥感数据里岩性边界处有大量混合像元标签本身带噪声叶子上只有一个样本很容易过拟合。这几个参数的合理取值和数据集规模、类别数、特征维度强相关手调很容易陷入“调一个、坏一个”的循环。用网格搜索又太贵一个 5 折交叉验证的 Extra-Trees 在中等规模数据上要跑几十秒三参数网格随便一搜就是上千次实验还都是独立实验完全浪费计算量。布谷鸟搜索和粒子群优化这类元启发式算法反而更现实——它们不要求目标函数可导把 Extra-Trees 的交叉验证精度当黑匣子用种群迭代的方式在参数空间里找好点。接下来两章先解决数据和样本问题第四章给出完整调参实现。3. 遥感图像标注与特征栈构建精度天花板其实在这一步先把结论放前面模型再强也救不了烂样本和弱特征。岩性识别项目的精度上限早在你加载影像、圈样本的那一刻就定死了。这一章把数据侧的问题一次讲透。3.1 数据源与特征层组合Landsat 8 DEM 常见搭配怎么搭我常用的特征层组合如下注意特征顺序必须固定第五章会讲为什么顺序错一位预测结果就面目全非。特征层来源/计算方式主要响应的岩性信息OLI 波段 B2-B7Landsat 8 表面反射率铁氧化物、黏土矿物的基本光谱轮廓铁氧化物比值B4 / B2火成岩区氧化铁含量差异黏土矿物比值B6 / B7黏土化、热液蚀变带识别坡度、坡向DEM 派生岩性抗风化能力导致的地形差异GLCM 纹理对第一主成分做 3×3 窗口区分厚层块状砂岩与薄层泥岩NDVI 掩膜植被指数不直接入特征用于剔除植被干扰Landsat 8 OLI 的 B6、B7 两个短波红外波段是岩性识别的核心因为大多数造岩矿物的诊断性吸收特征都在 1.6-2.4 μm 区间。B2-B5 提供铁氧化物和植被的上下文信息。DEM 的作用是辅助尤其在沉积岩地区陡坎、缓坡与岩性差异有明显的对应关系坡度特征能把“光谱相似但地形表现不同”的两类岩石分开。遥感图像标注的常见做法是打开影像套已有地质图把每个岩性单元手动圈几个多边形。这样能得到大量样本点但里面藏着一个大问题空间自相关。同一岩性单元内部相邻像素光谱高度相似它们并不是独立样本。如果把训练区和验证区从同一批多边形里随机切开验证集里全是训练样本的“近亲”交叉验证精度能报到 96% 以上把模型换到另一图幅预测直接掉到 70% 出头。解决思路是训练多边形和验证多边形在地理上物理隔开。比如某岩性单元在研究区有 5 个出露区块用其中 3 个区块训练剩下 2 个区块完全不出现在训练集里只做验证。这个逻辑一定要写进文档说明里答辩时老师最常问的“验证精度怎么来的”就在这里。3.3 样本均衡与掩膜先把水体、植被和阴影剔掉岩性类别天然不平衡。大面积沉积岩类——砂岩、泥岩——样本量轻松上万小面积侵入岩脉可能只有几百个像元。Extra-Trees 对不平衡的敏感度比 SVM 低但不处理照样会出问题。我一般做两件事先做掩膜把水体、浓密植被、云阴影对应的像元全部置为无效因为这些地物的光谱和岩石无关留着只会让模型学习“区分水和石头”这类无效规则再做类别加权设置 class_weight 参数让少数类在分裂时获得更高的权重。数据加载和特征矩阵构建的代码长这样import numpy as np import pandas as pd import rasterio from sklearn.preprocessing import LabelEncoder # 1. 读入训练区样本点坐标与标签 train_pts pd.read_csv(train_samples.csv) # 列: x, y, lithology # lithology 是岩性字符串例如 limestone / granite / sandstone # 2. 按坐标从多波段影像中提取光谱向量 def extract_pixels(src_path, pts): with rasterio.open(src_path) as src: rows, cols rasterio.transform.rowcol(src.transform, pts[x].values, pts[y].values) data src.read() # 形状 (bands, H, W) samples data[:, rows, cols].T # 转置为 (n_points, n_bands) return samples X_spectral extract_pixels(feature_stack.tif, train_pts) # 3. 拼接 DEM 派生的地形特征 X_dem train_pts[[slope, aspect]].values X np.hstack([X_spectral, X_dem]) # 4. 岩性字符串编码为整数类别 le LabelEncoder() y le.fit_transform(train_pts[lithology]) print(特征矩阵形状:, X.shape) # (样本数, 波段数 地形特征数) print(岩性类别:, le.classes_)逻辑说明第一步读样本点 CSV坐标必须和影像使用同一投影坐标系否则提取出的光谱全是错的。第二步用 rasterio.transform.rowcol 把投影坐标换算成影像的行列号一次性取出所有波段对应位置的像元值。这里用 numpy 数组索引代替 for 循环样本量上万时速度差异非常明显。第三步把坡度、坡向横向拼到光谱后面特征顺序从这一步就固定下来后面训练和预测必须保持一致。第四步用 LabelEncoder 把岩性字符串变成 0、1、2 等整数。参数说明src.read() 不带 window 会把整幅影像载入内存训练区一般图幅不大可以这么做如果影像有几个 GB必须用 windowed reading第六章给出分块方案。rowcol 返回的是整数行列号numpy 的整数数组索引可以直接用于 data[:, rows, cols]。注意数据清洗要在特征提取之前完成NDVI 掩膜最好在生成特征栈时就把无效值替换为 NaN 或统一标记不要留到建模阶段再处理。4. Python 实现极端随机树训练 布谷鸟/粒子群调参全流程到这里数据已经变成特征矩阵 X 和标签 y。这一章给出三块可直接抄的代码Extra-Trees 基线与交叉验证设置、布谷鸟搜索调参、粒子群调参。调参时注意只用训练区数据验证区碰都不能碰。4.1 极端随机树基线模型参数怎么设才不是默认值先搭建基线模型目的是拿到一个“调参前的分数”后面所有优化结果都要跟它对比from sklearn.ensemble import ExtraTreesClassifier from sklearn.model_selection import StratifiedKFold, cross_val_score # 基线 Extra-Trees et_base ExtraTreesClassifier( n_estimators300, max_features0.5, # 每个节点随机考察 50% 的特征 min_samples_leaf5, # 叶子节点至少 5 个样本 min_samples_split10, bootstrapFalse, # Extra-Trees 标准做法不抽样 n_jobs-1, random_state42 ) cv StratifiedKFold(n_splits5, shuffleTrue, random_state42) base_score cross_val_score(et_base, X_train, y_train, cvcv, scoringbalanced_accuracy).mean() print(f基线 balanced accuracy {base_score:.4f})逻辑说明这里用 balanced_accuracy 而不是普通 accuracy因为岩性类别不平衡普通精度会被大面积类别绑架少数类分错多少都看不出来。balanced accuracy 先逐类算召回率再取平均每一类权重相同。cross_val_score 内部会自动训练和评估返回 5 折的分数列表。参数说明n_estimators300 是考虑到像元级样本量通常很大默认 100 棵树不够稳max_features0.5 是关键改动遥感特征里波段和比值高度相关sqrt 模式下每棵树只看四五个特征容易漏掉波段比值之间的组合信息改成 0.5 让每棵树考察一半特征min_samples_leaf5 防止岩性边界上的混合像元把树逼到过拟合bootstrapFalse 是 Extra-Trees 区别于随机森林的典型设置改成 True 反而改变算法性质效果通常更差。4.2 布谷鸟搜索调参Lévy 飞行和 pa 丢弃率怎么实现布谷鸟搜索的核心是 Lévy 飞行新解不是朝固定方向走而是频繁出现短步长加偶尔长跳跃的行走模式长跳跃负责跳出局部最优。宿主鸟发现外来蛋后以概率 pa 丢弃部分解相当于每轮淘汰一批差解保持种群多样性。import numpy as np from math import gamma # 参数空间统一映射到 [0,1]解码时还原成真实参数 # 位置 pos[0] - n_estimators, pos[1] - max_features, pos[2] - min_samples_leaf def decode(pos): n_est int(pos[0] * (800 - 100) 100) # 100 ~ 800 max_feat pos[1] * 0.8 0.1 # 0.1 ~ 0.9 min_leaf int(pos[2] * 29 1) # 1 ~ 30 return n_est, max_feat, min_leaf def fitness(pos): n_est, max_feat, min_leaf decode(pos) model ExtraTreesClassifier( n_estimatorsn_est, max_featuresmax_feat, min_samples_leafmin_leaf, bootstrapFalse, n_jobs-1, random_state42 ) return cross_val_score(model, X_train, y_train, cv5, scoringbalanced_accuracy).mean() def levy_flight(beta1.5): sigma (gamma(1 beta) * np.sin(np.pi * beta / 2) / (gamma((1 beta) / 2) * beta * 2 ** ((beta - 1) / 2))) ** (1 / beta) u np.random.normal(0, sigma) v np.random.normal(0, 1) return u / (abs(v) ** (1 / beta)) def cuckoo_search(n_nests10, n_iter30, pa0.25): nests np.random.uniform(0, 1, (n_nests, 3)) best_score -np.inf best_pos None for it in range(n_iter): # 对所有巢做 Lévy 飞行更新 for i in range(n_nests): step levy_flight() other nests[np.random.choice(n_nests)] new_pos nests[i] 0.01 * step * (nests[i] - other) new_pos np.clip(new_pos, 0, 1) if fitness(new_pos) fitness(nests[i]): nests[i] new_pos # 宿主鸟发现布谷鸟蛋随机丢弃一部分解 for i in range(n_nests): if np.random.rand() pa: nests[i] np.random.uniform(0, 1, 3) # 更新全局最优 for i in range(n_nests): s fitness(nests[i]) if s best_score: best_score s best_pos nests[i].copy() print(f迭代 {it1}: 最优 balanced accuracy {best_score:.4f}) return decode(best_pos), best_score best_params, best_score cuckoo_search() print(布谷鸟最优参数:, best_params, 得分:, best_score)逻辑说明每个巢就是一个参数组合decode 把 0-1 之间的连续值映射到参数真实区间这样做是为了让粒子/巢穴在同一个量纲的空间里移动避免 n_estimators 范围几百、min_samples_leaf 范围只有几十导致搜索效率失衡。fitness 函数每次调用都做一次 5 折交叉验证返回 balanced accuracy这是整个优化过程的“黑匣子评估器”。参数说明步长缩放因子 0.01 控制 Lévy 跳跃的幅度太大容易在参数空间乱飞太小收敛慢pa0.25 是布谷鸟搜索的经典取值表示每轮约四分之一的解被随机重置这个值一般不用调。n_nests10、n_iter30 是兼顾计算量的常见设置注意一次 fitness 调用要训练 5 棵树模型30 轮乘 10 个巢就是 300 次评估跑完全量数据可能要几小时。实操建议先用下采样到 2 万样本的数据把调参流程跑通拿到最优参数区间后再放全量精修。4.3 粒子群优化调参惯性权重衰减和速度更新粒子群走的是另一条路每个粒子记住自己的历史最优位置 pbest种群共享全局最优 gbest速度和位置按公式更新。我把惯性权重 w 从 0.9 线性衰减到 0.4前期大权重让粒子广泛探索后期小权重让粒子在最优附近精细搜索避免一上来就收敛到局部最优。def pso_tune(n_particles12, n_iter30, w_start0.9, w_end0.4, c11.5, c21.5): dim 3 pos np.random.uniform(0, 1, (n_particles, dim)) vel np.random.uniform(-0.1, 0.1, (n_particles, dim)) pbest_pos pos.copy() pbest_score np.array([fitness(p) for p in pos]) gbest_idx np.argmax(pbest_score) gbest_score pbest_score[gbest_idx] gbest_pos pbest_pos[gbest_idx].copy() for it in range(n_iter): w w_start - (w_start - w_end) * it / n_iter # 惯性权重线性衰减 for i in range(n_particles): r1, r2 np.random.rand(2) # 速度更新惯性 个体认知 群体认知 vel[i] (w * vel[i] c1 * r1 * (pbest_pos[i] - pos[i]) c2 * r2 * (gbest_pos - pos[i])) # 位置更新并限制在 [0, 1] 边界内 pos[i] np.clip(pos[i] vel[i], 0, 1) s fitness(pos[i]) if s pbest_score[i]: pbest_score[i] s pbest_pos[i] pos[i].copy() if s gbest_score: gbest_score s gbest_pos pos[i].copy() print(f迭代 {it1}: gbest {gbest_score:.4f}) return decode(gbest_pos), gbest_score pso_params, pso_score pso_tune() print(粒子群最优参数:, pso_params, 得分:, pso_score)逻辑说明速度更新三项分别代表三个方向的力——惯性项保留上一轮运动趋势认知项把粒子拉向自己历史最优社会项把粒子拉向种群全局最优。通过调节 c1 和 c2 控制两股力的强弱c1 太大会让每个粒子只顾自己乱跑c2 太大会让种群过早抱团。位置更新后 np.clip 把粒子限制在 [0,1] 空间里解码函数的区间边界在那里等着。参数说明w 线性衰减是粒子群调参的经典技巧从 0.9 到 0.4 是经验区间c1c21.5 是比较保守的配置不会让粒子飞太猛。粒子数 n_particles12 三轮迭代的评估次数是 360 次和布谷鸟差不多。两种算法跑完后对比分数如果差异在 0.005 以内取更简单的那个参数组合不要迷信“看起来更高的那一位”。表布谷鸟搜索与粒子群调参设置对比项目布谷鸟搜索 CS粒子群 PSO种群大小10 巢12 粒子迭代轮数3030核心机制Lévy 飞行 pa 丢弃惯性权重 pbest/gbest 吸引关键参数步长 0.01pa0.25w 0.9→0.4c1c21.5典型适应性跳出局部最优能力强收敛速度快后期精细搜索4.4 调参结果回读与模型保存优化算法输出的只是一组参数坐标拿到后要重新训练一个完整模型并用空间隔离的验证区做最终评估from sklearn.metrics import balanced_accuracy_score import joblib best_model ExtraTreesClassifier( n_estimatorsbest_params[0], max_featuresbest_params[1], min_samples_leafbest_params[2], bootstrapFalse, n_jobs-1, random_state42 ) best_model.fit(X_train, y_train) train_score balanced_accuracy_score(y_train, best_model.predict(X_train)) val_score balanced_accuracy_score(y_val, best_model.predict(X_val)) print(f训练集 balanced acc {train_score:.4f}) print(f验证集 balanced acc {val_score:.4f}) joblib.dump(best_model, lithology_et_best.joblib) joblib.dump(le, label_encoder.joblib)逻辑说明train_score 和 val_score 的差距是判断过拟合的直接证据。两者相差超过 0.1说明参数搜出来的组合过分依赖训练区细节优先检查调参时是不是不小心把验证区样本卷进 fitness 评估了。joblib 保存的模型文件里包含完整树结构后面逐像元预测直接加载不用重新训练。参数说明这里用的是随机种子 42 固定复现如果你在其他论文里看到“以 0.87 为最优参数”但无法复现大概率是没固定 random_state。文档说明里写清随机种子和交叉验证折数是评阅老师最看重的可复现性细节。5. 岩性识别避坑5 个让分类结果翻车的常见问题这一章全部是踩过的坑按“现象 → 原因 → 解决”写每条都能对应到实际运行时的报错或异常结果。5.1 特征顺序错位训练跑得很好整幅图预测时全是噪声现象模型在训练集和验证集上的精度都很正常但用整幅影像预测时输出图像完全错乱花岗岩分布区变成了砂岩纹理像是被打乱的马赛克。原因预测阶段重建特征向量时特征顺序和训练时不一致。训练时你可能用“B2-B7 坡度 坡向”的顺序拼接预测时用了“坡度 坡向 B2-B7”或者漏了某个波段比值。Extra-Trees 对特征顺序不敏感但特征矩阵的列顺序必须和训练时完全一致树节点上记录的分裂特征索引是按列位置存的。解决把特征栈的构建写成一个函数训练和预测都调用同一个函数生成特征矩阵不要在两处各写一遍。我在代码里习惯把 band_list、terrain_features 定义成全局列表预测前打印一下 X.shape[1]和训练时对不上就直接停下排查。5.2 随机切分验证集精度虚高到 97%一换图幅就崩现象交叉验证精度 96% 以上模型在训练区附近看起来完美一旦预测到相邻图幅精度掉到 70% 左右。原因训练和验证样本来自同一批岩性出露区空间上强相关的像素被随机分到两侧验证集没有独立代表性。遥感像元不是独立样本同一岩性单元的相邻像素光谱几乎一样模型记住的是局部光谱模式而不是岩性规律。解决按地理区块划分训练和验证同一岩性单元的不同出露区块必须严格隔离。我见过的最简单做法是在 GIS 里按多边形编号切分区块 id 为奇数的进训练偶数的进验证。这样验证精度才有实际意义也禁得住答辩追问。5.3 调参时把验证区卷进来布谷鸟“优化”出的参数是假高分现象布谷鸟搜索报出的最优分数 0.93按最优参数重新训练后验证区分数只有 0.81差距大得离谱。原因fitness 函数里做交叉验证时用的是全量 X 而不是 X_train验证区样本混进调参过程优化算法等于提前“看过答案”。这比随机切分的坑更隐蔽因为表面上看交叉验证流程完整实际上数据泄漏已经发生。解决调参前严格区分 X_train 和 X_valfitness 内部只接收 X_train。一个检查技巧把调参后的最优参数用固定的随机种子重跑一遍直接看验证区分数如果明显低于调参时报出的分数立刻检查数据切割顺序。5.4 少数岩性类全灭花岗岩脉几百个像元全被划成砂岩现象混淆矩阵里大面积砂岩类精度 90%花岗岩类召回率只有 8%模型几乎没把它识别出来。原因类别不平衡加上 Extra-Trees 的分裂准则偏向样本量大的类。虽然基线模型用了 balanced_accuracy 评估但算法本身的分裂过程仍然按基尼系数最小化少数类提供的分裂收益太小经常被忽略。解决两层处理。第一用 class_weightbalanced_subsample 给少数类加权sklearn 的 Extra-Trees 直接支持第二对少数类做样本扩增——不是合成少数类样本而是对少数类多边形内的像元做轻度平移、旋转生成更多训练样本。扩增幅度控制在 1.5-2 倍以内过度扩增会引入虚假空间模式。5.5 整幅影像预测内存爆炸一次 read() 吃掉 16 GB 内存现象训练和调参都顺利到了整幅影像预测那一步程序直接卡死或报 MemoryError。原因rasterio 的 src.read() 把整幅影像一次性载入内存。以 7000×7000 像元、7 个波段的 GeoTIFF 为例float32 数据就是 1.3 GB 起步加上预测输出数组、特征工程中间结果16 GB 内存很快见底。解决分块预测。用 rasterio 的 Window 按 512×512 像元读取和写出第六章给出完整分块代码。这个坑最容易在最后一步翻车提前做好分块规划输出时再设定合理的压缩参数可以完全避免。6. 逐像元预测出图与 Kappa 验证把成果做成能上答辩台的交付物6.1 分块预测整幅影像rasterio 窗口读取的正确姿势import numpy as np import rasterio from rasterio.windows import Window def predict_map(model, src_path, out_path, block_size512): 按 512x512 窗口分块预测整幅影像避免内存溢出 with rasterio.open(src_path) as src: profile src.profile profile.update(dtypeuint8, count1, compressdeflate) height, width src.height, src.width with rasterio.open(out_path, w, **profile) as dst: for row in range(0, height, block_size): for col in range(0, width, block_size): win Window(col, row, min(block_size, width - col), min(block_size, height - row)) data src.read(windowwin) # (bands, h, w) n_bands, h, w data.shape # 每个像元展开成一个样本特征顺序与训练一致 flat data.reshape(n_bands, -1).T pred model.predict(flat).astype(uint8) dst.write(pred.reshape(h, w), 1, windowwin)逻辑说明外层循环按行、列步长 512 切窗口边界处用 min 限制窗口尺寸防止索引越界。每个窗口读取后先 reshape 成 (h×w, bands) 的样本矩阵预测后再 reshape 回二维栅格写入输出文件。模型是之前用 joblib 加载的特征矩阵的波段顺序必须与训练一致这也是第三章多次强调顺序的原因。参数说明block_size512 是内存与 IO 的折中窗口越大 IO 次数越少但内存峰值越高compressdeflate 能显著减小输出 GeoTIFF 体积岩性分类图类别少压缩比通常能达到 5:1 以上。如果影像有无效值预测前先做掩膜把无效像元直接赋 0不要送进模型。6.2 用空间隔离的验证区计算混淆矩阵和 Kappa出图只是交付物的骨架精度验证才是答辩时能拿出手的硬数据。对验证区样本计算混淆矩阵和 Kappa 系数from sklearn.metrics import confusion_matrix, cohen_kappa_score, classification_report y_val_pred best_model.predict(X_val) cm confusion_matrix(y_val, y_val_pred) kappa cohen_kappa_score(y_val, y_val_pred) print(混淆矩阵:\n, cm) print(fKappa 系数 {kappa:.4f}) print(classification_report(y_val, y_val_pred, target_namesle.classes_))逻辑说明混淆矩阵要按验证区的真实标签逐类看重点看对角线外的错误去向——花岗岩被误判成什么、砂岩和泥岩之间是否互相混淆。Kappa 系数在类别不平衡时比总体精度可靠得多一般认为 0.8 以上属于高度一致。如果 Kappa 比 balanced accuracy 低很多说明某些类的错误分布很不均匀模型可能在“用岩性大类掩盖小类错误”。参数说明classification_report 输出的 per-class precision、recall、F1 是文档说明里最该放的三列数据。答辩时老师问“哪两类最容易混淆”指着混淆矩阵说“砂岩和泥岩光谱相似错误主要发生在二者之间”比任何泛泛的话都有说服力。6.3 出图的三个细节固定类别颜色、图例和空间参照最后一个技巧是出图。岩性分类结果图最容易犯的错是类别颜色不固定——同一套数据画两张图绘图库自动配色花岗岩第一次是红色第二次变成绿色图例直接错位。正确做法是给每个类别固定一个 RGB 颜色字典全程复用。图例上标注类别名和对应地质年代空间参照信息从源影像的 profile 里继承保证输出 GeoTIFF 能直接叠加到 GIS 里和地质图对位置。我交这类作业前有个习惯出完图一定把结果和原始影像在 GIS 里叠着看一遍从目视解译的角度抽查几个岩性边界是否符合地形和色调的直觉。这个习惯救过我很多次——有一次模型把线性构造带上的花岗岩全错分成闪长岩就是因为训练样本里闪长岩多边形不干净混入了接触变质带上的混合像元光看精度指标完全看不出问题。边界不符合地质常识时先别怀疑模型回去检查训练样本问题多半在那里。希望帮到你。本文还有配套的精品资源点击获取
返回列表