
简介本资源是一套面向地质工程、石油勘探及人工智能交叉领域学习者的高分实践项目聚焦地下储层岩性智能识别与分类这一核心地质解释任务适用于本科毕业设计、研究生课程设计及期末大作业。项目基于Python构建完整机器学习流程涵盖数据预处理、特征工程、多模型训练如随机森林、XGBoost、SVM与性能评估代码含详细中文注释配套文档说明清晰新手可快速上手复现。压缩包共215个文件主体为175个CSV与23个XLSX格式的测井曲线与岩性标签数据集辅以Jupyter Notebook实验脚本、Markdown技术文档、Word报告模板及可视化结果图整体大小168.9MB。目前已有343人学习下载提供从原始数据到模型部署的端到端解决方案包含典型样本划分策略、特征重要性分析模块及模型对比表格结构规范、即下即用是地质AI方向实践落地的优质参考范例。 这块项目我闭着眼睛都能背出套路——测井曲线拉下来岩性标签一贴随机森林或者XGBoost上去跑一轮准确率堆到90%以上完事。但真正接触过实际井数据的人都清楚高分项目跟“能落地”是两个物种。这篇直接把地下储层岩性识别与分类项目从数据到代码再到坑位全拆开聊针对“机器学习 Python 岩性分类 数据集”这条完整链路给出一份可以直接照着抄作业的实操方案。这个项目适合谁一类是正在做地质资源相关课程设计或者毕业设计的同学需要一套完整且能讲清楚原理的代码和文档另一类是刚接触测井数据处理想用机器学习做岩性识别但不知道从哪下手的工程师。你如果只想拿个高分按文章里的流程走一遍就够了如果你想让这套东西真的能给你干活后面那几个“坑位提醒”才是值钱的部分。1. 项目整体设计与方案选型为什么拿机器学习做岩性识别岩性识别这个东西传统做法是交会图版和手工解释。把自然伽马、电阻率、密度几条曲线投到二维坐标系里靠着经验圈出砂岩、泥岩、灰岩各自的分布范围。问题很明显曲线一多维度一高人眼就看不过来了而且不同地区的储层岩石物理参数差异很大这套图版换个区块就不灵。机器学习的核心优势是把“人工看交会图”变成“自动学边界”。模型可以同时吃下自然伽马、深侧向电阻率、补偿中子、密度、声波时差这五条常用曲线自行寻找它们在不同岩性之间的非线性组合关系。只要你手里有录井或者岩心描述的岩性标签就能把这个问题建模成一个有监督分类任务。这个项目里我用的是随机森林做主力模型用XGBoost做对比验证原因是这两个模型在表格型数据上的表现稳定不需要做太复杂的特征工程而且对测井数据常见的噪声和异常值有一定的容忍度。那些一上来就上LSTM、Transformer的方案不是不行但你要知道测井曲线的纵向采样率通常是0.125米一个点一口井就是上万条样本可绝大多数井的岩性标签只覆盖了其中一小段强行用深度学习模型在少量标签上训练很容易过拟合。常规机器学习模型在这个任务上泛化能力往往更好解释性也强答辩的时候能说得清楚。方案选型还有一个考虑可复现性。随机森林和XGBoost在Python里都有非常成熟的库环境配置简单依赖少别人拿你的源码跑一遍不会因为版本问题卡壳。对于一个需要提交源码和文档的高分项目来说这一点比模型本身更先进更重要。1.1 项目的工作流程拆解整个项目的流水线可以分为五个环节每个环节都会在后面章节单独展开数据读取与清洗解析LAS格式的测井数据处理异常值和缺失值。深度对齐与标签构建将岩性描述按深度归一到测井曲线的采样深度上。特征工程选择敏感曲线、构造衍生特征、标准化。模型训练与调参随机森林、XGBoost的交叉验证和超参数搜索。评估与可视化精确率/召回率/F1、混淆矩阵、特征重要性排序。你在写项目文档的时候按这个流程来组织章节完全没问题。很多同学在文档里直接写“用随机森林分类器对测井数据进行分类”这是要被扣分的因为完全没有体现地质逻辑。评审老师想看到的是你理解每一口测井曲线的物理含义知道为什么要选这几条曲线而不是那几条。2. 数据集准备与预处理测井数据的“脏乱差”是常态市面上公开的岩性识别数据集不算多比较知名的是美国Energy Department发布的用于测井岩性预测的公开数据集还有堪萨斯地质调查局的部分井数据。如果你是自己取的数据那大概率是LAS格式。LAS是测井行业的标准文本格式里面包含文件头信息和曲线数据曲线通常按深度采样。我拿到数据之后第一件事不是建模而是洗数据。测井数据在实际采集过程中特别容易出现三类问题井壁垮塌段导致曲线数据跳变或者失真表现为突然的大幅尖峰。仪器未贴井壁造成的测量值漂移比如密度曲线局部严重偏低。深度不匹配不同测井仪器下井时深度记录不一致导致曲线之间错位。2.1 数据清洗的具体操作对于LAS文件推荐用lasio这个Python库读取它比手动解析文本文件稳定得多可以直接返回带深度索引的DataFrame。import lasio import pandas as pd import numpy as np # 读取LAS文件 las lasio.read(data/well_01.las) df las.df() # 返回DataFrame索引为深度 df df.reset_index() # 只保留常用且物理意义明确的曲线 cols_of_interest [GR, RT, DEN, CNL, AC, CAL] df df[[DEPT] [c for c in cols_of_interest if c in df.columns]] # 去除深度重复的行 df df[~df[DEPT].duplicated(keepfirst)] # 处理极端异常值用上下分位数夹逼 def clip_outliers(series, lower_percentile0.01, upper_percentile0.995): lower series.quantile(lower_percentile) upper series.quantile(upper_percentile) return series.clip(lower, upper) for col in cols_of_interest: if col in df.columns: df[col] clip_outliers(df[col]) # 缺失值处理测井数据不推荐直接删除行线性插值更适合 df[cols_of_interest] df[cols_of_interest].interpolate(methodlinear, limit_directionboth) print(df.shape) print(df.isnull().sum())需要注意如果你的数据里岩性标签不是按深度给的而是按录井深度段给的比如“2200.0-2210.5m 细砂岩”那么要把每个深度点映射到对应的岩性段里。这个步骤直接在Excel里做容易出错建议用代码按深度区间匹配def assign_lithology(depth): # lithology_segments是一个列表每个元素为 (start_depth, end_depth, lithology) for start, end, litho in lithology_segments: if start depth end: return litho return np.nan df[lithology] df[DEPT].apply(assign_lithology) # 删除没有岩性标签的深度段 df_labeled df.dropna(subset[lithology]).copy()标签深度对齐是一个特别容易被忽视的环节。很多人在建模阶段发现准确率不高回头检查数据才发现是深度没对齐标签和曲线错开了一个层段。这个检查建议在做完标签构建后第一时间做用可视化方式把岩性标签叠加到曲线上人工抽查几个深度点确认对应关系合理。2.2 类别不平衡问题地下储层岩性的分布天然不平衡一口井可能泥岩段特别长砂岩段很短灰岩段就几个薄层。如果直接用原始分布去训练模型会很“聪明”地学会把所有样本都预测成泥岩因为这样准确率就已经很高了。这种问题怎么处理两个办法一个是在训练时给类别加权重另一个是过采样少数类别。实测下来SMOTE过采样在测井数据上的效果不稳定因为合成样本会破坏测井曲线的地质连续性我反而更推荐直接用class_weightbalanced或者手动指定权重矩阵。这点在后面训练部分会细说。3. 特征工程与模型选型不是曲线越多越好测井曲线虽然原始维度不算高但每一条曲线的物理含义不同对不同岩性的敏感程度也不同。特征选择的关键是根据地质知识初筛再用数据验证而不是无脑把全部曲线扔进去。以常见砂泥岩剖面为例自然伽马(GR)泥岩高、砂岩低是区分砂泥岩最基础的曲线。深侧向电阻率(RT)砂岩储层含油气时电阻率升高泥岩通常低阻。补偿密度(DEN)灰岩致密时密度大砂岩密度中等泥岩偏低。补偿中子(CNL)泥岩中往往含有束缚水中子孔隙度响应偏高。声波时差(AC)反映岩石的孔隙度和岩性泥岩通常时差大。你如果处理的是碳酸盐岩剖面那电阻率和密度可能更关键如果是火成岩储层可能还要加成像测井信息不过常规机器学习项目里很难拿到这种数据一般就是常规九条曲线里挑。3.1 衍生特征让模型多一个维度看数据除了原始曲线我习惯加两种衍生特征一是曲线纵向滑动窗口均值。测井曲线的垂向分辨率有限相邻采样点的值存在相关性滑动窗口均值可以平滑掉局部噪声也相当于给模型引入了邻近深度的地层信息。def add_window_features(df, cols, window5): df_new df.copy() for col in cols: df_new[f{col}_win{window}] df_new[col].rolling(windowwindow, centerTrue).mean() return df_new # 窗口大小5相当于上下各取2个采样点0.25米做平滑 df_feat add_window_features(df_labeled, cols_of_interest, window5)二是曲线之间的比值或差值。比如密度和中子的差值能反映岩性的某些特征但这类特征需要先做标准化再算否则量纲会影响计算结果。不过要提醒一句衍生特征不是越多越好加多了特征维度上升模型训练变慢还会引入共线性。我一般控制在原始特征量的1.5倍以内。3.2 为什么选随机森林和XGBoost树模型在测井岩性识别这个场景下几乎是无脑首选。原因有三点特征尺度不敏感测井曲线的量纲各不相同GR是API单位DEN是g/cm³RT是欧姆米做不做标准化对树模型影响不大减少了预处理环节的出错概率。能捕捉非线性关系岩性和测井响应之间不是简单线性关系树模型天然支持非线性切分。特征重要性可解释能输出每个特征对分类的贡献度这一点对写毕业论文或者项目文档几乎是刚需能直接作为“敏感性分析”章节的素材。KNN和SVM我也试过KNN的问题是测井数据的特征空间高维且存在噪声距离计算稳定性差SVM在小样本上效果好但一旦样本量上万训练时间明显上升而且核函数调参复杂。这不是说这些算法不能用而是作为项目交付来说随机森林是性价比最高的方案。4. 模型训练与评估用代码说清楚每一条评估指标这一节给出完整的训练流程从数据集划分讲起。4.1 数据集划分不要让同一口井的数据同时出现在训练集和测试集这里有一个特别容易被初学者忽略的问题。测井数据的相邻深度点之间高度相关如果随机划分训练集和测试集模型“见过”的深度段的邻居就会出现在测试集里导致测试准确率虚高。这种情况在学术上叫数据泄漏表现出来就是训练准确率和测试准确率都90%多但一到新井预测就崩。正确的做法是按井划分。有5口井就4口训练、1口测试或者至少按连续深度段划分保证测试集是一段模型从未见过的数据。from sklearn.model_selection import GroupShuffleSplit # 假设df_new中有一个well列表示数据属于哪口井 groups df_feat[well] splitter GroupShuffleSplit(n_splits1, test_size0.2, random_state42) train_idx, test_idx next(splitter.split(df_feat, groupsgroups)) train_data df_feat.iloc[train_idx] test_data df_feat.iloc[test_idx] X_train train_data.drop(columns[lithology, well, DEPT]) y_train train_data[lithology] X_test test_data.drop(columns[lithology, well, DEPT]) y_test test_data[lithology]这里GroupShuffleSplit的目的是确保同组的样本不会同时出现在训练集和测试集里。4.2 随机森林训练与超参数调节随机森林的核心超参数是n_estimators树的数量、max_depth树的深度、min_samples_split分裂所需最小样本数和class_weight类别权重。from sklearn.ensemble import RandomForestClassifier from sklearn.model_selection import GridSearchCV from sklearn.metrics import classification_report, confusion_matrix, accuracy_score param_grid { n_estimators: [200, 400], max_depth: [10, 15, 20], min_samples_split: [2, 5, 10], class_weight: [balanced, None] } rf RandomForestClassifier(random_state42) grid_search GridSearchCV(rf, param_grid, cv3, scoringf1_weighted, n_jobs-1, verbose1) grid_search.fit(X_train, y_train) best_rf grid_search.best_estimator_ y_pred best_rf.predict(X_test) print(Best Parameters:, grid_search.best_params_) print(Accuracy:, accuracy_score(y_test, y_pred)) print(classification_report(y_test, y_pred))先说为什么用f1_weighted而不是准确率作为网格搜索的评分指标。类别不平衡场景下准确率会被多数类主导F1加权综合考虑了每个类别的精确率和召回率更能反映模型在少数类上的真实表现。再说class_weightbalanced。这个参数会自动根据样本量的倒数调整类别权重让少数类得到更大的惩罚系数。实测来说在泥岩占比超过70%的数据集上开不开这个参数的差异可以达到5到10个百分点尤其在砂岩薄层上表现得非常明显。4.3 XGBoost对照实验交叉验证场景下XGBoost往往比随机森林略快准确率也稍微高一点但随机森林的稳定性更好对超参数不敏感。项目文档里把两个模型都写进去对比一张表会显得你的工作很完整。from xgboost import XGBClassifier xgb XGBClassifier( n_estimators300, max_depth6, learning_rate0.05, subsample0.8, colsample_bytree0.8, scale_pos_weight2, # 简单处理类别不平衡根据多数类/少数类比例设置 random_state42, eval_metricmlogloss ) xgb.fit(X_train, y_train, early_stopping_rounds50, eval_set[(X_test, y_test)], verboseFalse) y_pred_xgb xgb.predict(X_test) print(classification_report(y_test, y_pred_xgb))XGBoost一定要配合early_stopping_rounds使用不然在n_estimators很大的情况下很容易过拟合。4.4 结果评估不要只盯准确率单看accuracy没有意义。在地下储层岩性识别里如果泥岩占70%你全部预测泥岩都有70%的准确率但这样的模型毫无实用价值。评估维度至少要看这三样分类报告里的每个类别的精确率、召回率、F1。重点关注砂岩、灰岩这类储层相关的少数类。混淆矩阵看模型到底错在哪里。比如是把泥岩错分成了砂岩还是把粉砂岩错分成了泥岩这两种错误对地质解释的影响完全不同。按深度的预测剖面图把真实岩性和预测岩性画在同一个深度轴上对比。这一步非常重要因为测井岩性识别最终的产出是一段连续的岩性剖面你当然关注它在深度上的连续性和地质合理性比如不存在频繁、短间隔的岩性抖动——这类锯齿状预测结果即使整体准确率很高也不具备实用价值。我在交付项目时习惯给测试井画一张三栏图左栏是自然伽马曲线中栏是真实岩性柱右栏是预测岩性柱。这样的图放进论文、答辩PPT里都非常加分比贴一堆混淆矩阵直观得多。5. 特征重要性分析从模型结论回到地质意义模型训练完之后需要回过头来用特征重要性理解测井曲线对不同岩性的响应规律。这一步既是让你的项目区别于普通“调参侠”作业的点睛之笔也是分析中写“敏感性分析”章节的核心素材。import matplotlib.pyplot as plt feature_importance pd.Series(best_rf.feature_importances_, indexX_train.columns).sort_values(ascendingFalse) plt.figure(figsize(10, 6)) feature_importance.plot(kindbarh) plt.gca().invert_yaxis() plt.xlabel(Importance Score) plt.title(Feature Importance Ranking for Lithology Classification) plt.tight_layout() plt.show()做完之后你大概率会发现自然伽马和电阻率在砂泥岩剖面中的重要性排前两名密度和中子次之。这个结果可以对应到地质原理泥岩的黏土矿物吸附放射性元素导致高自然伽马含油气砂岩的高电阻率跟孔隙流体性质有关这些结论在文档里写出来你的项目就不是“机器学习调包”项目而是“机器学习解决地质问题”项目。如果想进一步可视化模型对不同岩性的区分能力可以画特征分布的小提琴图或者箱线图看看每条曲线在不同岩性上的分布差异。这也是论文里很常见的一类图。6. 常见问题与排查技巧实录我踩过的坑你别再踩了这一节是实际动手过程中容易遇到且公开资料里很少写的内容。6.1 数据泄漏问题最坑没有之一上面提到过按井划分训练测试集的问题这里再强调一次。我有一个训练时准确率98%、测试时直接只有52%的失败经历最后定位到问题根因就是随机划分导致训练集里混入了测试井的相邻深度点。小组内讨论时才发现所有“指标爆表”的项目几乎都存在这个问题。不按井划分的数据结果存在显著的数据泄漏风险。6.2 深度对齐错位一口井如果GR曲线深度和RT曲线深度错位了一个采样点0.125米在视觉上几乎看不出来但模型会学到很多虚假的组合特征。我排查这个问题的方法很简单抽一个深度段把曲线画出来看GR峰值和RT低值是否在深度上对应。如果发现错位需要对曲线做深度校正小范围错位可以直接平移曲线大范围错位建议请教专业测井解释人员。6.3 训练集测试集的岩性类别不一致按井划分的时候会遇到训练井里没有灰岩但测试井里有灰岩这种情况。类别缺失会导致模型对灰岩完全没有认知预测时就会把灰岩段错认为泥岩或者砂岩。如果数据里有多口井分井划分前先检查分布尽量保证训练集和测试集中的岩性类别接近。6.4 超参数搜索的时间成本GridSearchCV在数据量大的时候会非常慢。如果你有几万条样本、几百棵树、几十组参数跑一个网格搜索可能要几个小时。我建议先小规模调试比如先用200棵树、最大深度不超过15跑一把确定参数的大概范围再做精细搜索。数据量在上万级别时n_jobs-1能显著加快计算。如果机器配置跟不上直接改用RandomizedSearchCV效果相近但时间大幅缩短。6.5 模型的“边缘振荡”问题预测出的岩性剖面常常会在岩性边界处出现频繁跳变比如真实的地层是5米厚的砂岩但预测结果在这一段出现砂岩、泥岩、粉砂岩交替闪烁。这种结果从地质角度讲是不合理的因为地层沉积通常具有连续性。处理办法有两个一是用滑动窗口对预测结果做多数投票平滑二是在模型预测后加一个后处理步骤把厚度小于一定阈值的单层合并到相邻的多数岩性中。# 简单多数投票平滑窗口大小11约1.4m from scipy.stats import mode def smooth_predictions(pred, window_size11): smoothed [] half window_size // 2 for i in range(len(pred)): start max(0, i - half) end min(len(pred), i half 1) window pred[start:end] mode_res mode(window) smoothed.append(mode_res.mode[0]) # scipy新版对mode结果做了调整请按版本查询字段 return np.array(smoothed) y_pred_smooth smooth_predictions(y_pred)这个后处理能明显改善预测剖面的连续性但要注意不能把窗口设得过大否则会抹掉薄储层信息。窗口大小一般取5到15个采样点之间比较合适。7. 项目文档与数据集配套高分从细节里来题目里特别标了“源码文档说明数据集”说明这个项目不只是代码能跑配套材料也很重要。先说一下文档组织结构。最合理的目录结构是这样的第一章 研究背景与意义为什么要做岩性识别传统方法的局限第二章 数据与预处理数据来源、LAS格式说明、清洗、标签构建第三章 特征工程与模型原理为什么要选这些特征随机森林/XGBoost的原理简述第四章 实验设计与结果分析模型对比、表格、混淆矩阵、剖面图、特征重要性第五章 结论与展望结论要具体比如“模型在砂岩识别上F1达到0.85比泥岩低0.05”这种细节附录 代码说明与运行环境环境配置部分建议写在文档里并且固定版本号。我用过的版本组合是这样的Python 3.9.13、pandas 1.5.3、numpy 1.23.5、scikit-learn 1.2.2、xgboost 1.7.6、lasio 0.31。不同版本在一些API细节上有差异最常见的是scipy.stats.mode的返回结果在新版本里是个包含多个字段的对象老版本是单独数组不固定版本会导致别人运行你的代码报错。数据集的整理也要规范。公开数据集的话要在文档里注明来源和引用方式。如果数据涉及到保密或者不便公开可以给出模拟数据的生成脚本用物理规律近似构造几条曲线配合标签让整个项目可以在没有保密数据的条件下复现。7.1 环境准备清单拿到源码后第一步就是跑通环境。建议直接用Anaconda创建单独环境别污染系统级Pythonconda create -n lithology python3.9 -y conda activate lithology pip install pandas numpy scikit-learn xgboost lasio matplotlib如果你的电脑没有GPUXGBoost默认的CPU版本完全够用几千个样本的训练时间通常在秒级到分钟级完全没有必要上GPU。7.2 数据准备说明我在项目里建了一个data/目录里面放原始LAS文件和岩性标签文件。如果是公开数据集默认已经有分好井的LAS如果是自备数据建议把岩性标签整理成CSV格式如下井名起始深度结束深度岩性W12200.02210.5砂岩W12210.52235.0泥岩W23100.03112.0灰岩然后写一个脚本把标签和曲线合并输出一个merged_labels.csv。这一步是整个项目中最容易出问题的地方因为深度区间不连续边界重合或者缺漏都会导致标签对应不上模型输入。8. 从课设项目到生产力工具的进阶方向如果你不满足于拿高分想让这个项目的价值再上一级这里还有几个可以深入的方向。一是把模型预测结果接入地质建模软件。Petrel这类软件支持外部属性数据导入可以把模型预测的岩性剖面按深度导出成文本文件再导入到建模软件里作为相建模的输入从而打通“测井解释-地质建模”这条链路。二是引入深度学习模型做对比。可以考虑用一维卷积神经网络直接对测井曲线序列建模利用深度维度的上下文信息。但前提是你需要加大训练数据量至少几十口有标签的井否则结果大概率不如随机森林。项目文档里可以作为“拓展实验”简单提一下不需要展开。三是做模型迁移实验。在一个区块训练好的模型直接用到另一个地质条件类似的区块看准确率下降多少分析可迁移性差的原因。这种实验在真实工程项目中非常有价值也是论文的好素材。四是加不确定性估计。随机森林这种基于决策树的模型本身可以输出每棵树的预测分布用所有树预测结果的标准差作为该深度点预测的不确定性。在地质解释中那些模型“拿不准”的层段往往是需要人工复核的重点层段。这个改进很有新意文档里写出来会很加分。9. 实操总结这套代码到底能跑出什么效果假设你拿到一份包含5口井、每口井约5000个采样点、岩性标签涵盖砂岩、泥岩、粉砂岩、灰岩四类的数据集按井划分训练集4口井和测试集1口井。特征用GR、RT、DEN、CNL、AC五条原始曲线加滑动窗口均值共10维特征。随机森林参数设在n_estimators300, max_depth15, class_weightbalanced。常见的结果区间是准确率78%-90%之间F1加权得分在0.75-0.88之间其中砂岩和灰岩的F1通常在0.6-0.8泥岩F1在0.85-0.95。如果你的结果比这个区间低优先检查数据泄漏和标签对齐如果比这个区间高先确认测试井和训练井之间是否存在深度段重叠以及标签是否在数据清洗过程中被无意修改。说到底岩性识别本质上是模式识别问题机器学习只是工具。工具选得再好数据基础不牢模型结果也只是海市蜃楼。我每次拿到新数据至少会花一半时间在数据清洗和标签梳理上这个过程虽然繁琐但带来的回报是模型效果不稳定和“答辩被问倒”两件麻烦事的成功规避。希望这套从数据到模型再到文档的完整链路能帮你把项目做成既有分数、又有含金量的真正“高分项目”。本文还有配套的精品资源点击获取