
简介这份资源面向计算机、人工智能、自动化、电子信息等专业的高校学生与科研人员围绕人工智能在石油测井领域的应用提供Python岩性识别与测井曲线回归的完整课程设计资料。项目代码经过测试可稳定运行适合作为课程设计、毕业设计、作业或项目初期立项演示也便于初学者进阶学习。压缩包共246个文件约174.51MB包含175个csv测井数据文件、28个ipynb实验笔记、23个xlsx表格、5个md说明、3个docx设计文档及少量py脚本、图片与许可证文件覆盖数据、代码与报告全流程。已有56人学习下载。读者可获取完整源码、设计报告与实验记录理解岩性识别与曲线回归的建模思路、数据处理流程及结果分析并在此基础上修改扩展实现其他测井解释功能。1. 从一份课设包说起Python 岩性识别与测井曲线回归能跑通什么如果你手头正压着一个「人工智能在石油测井中的应用」课程设计大概率会卡在同一个地方测井曲线数据拿到了岩性识别和曲线回归这两个任务也知道要做什么但真到写代码那一步数据怎么读、标签怎么对、模型怎么搭、报告怎么凑够工作量全是问号。这份课设包解决的正是这个断层——它把 Python 岩性识别和测井曲线回归两条主线打包在一起附带完整资料和报告属于那种「拿到就能跑、跑完能交」的资源。它适合三类人一是选了石油测井或人工智能交叉方向课设的本科生需要一份能复现、能改参数的完整流程二是想用真实工业数据练手 Python 机器学习的人测井数据比公开的鸢尾花、房价数据集更接近工程现场三是需要快速搭出「数据预处理 特征工程 模型训练 结果可视化」全链路的人。不适合指望直接抄完交差的人——报告和代码里的参数逻辑答辩时是要能讲清楚的。2. 测井数据怎么进 Python从 LAS 文件到建模可用的 DataFrame测井数据最常见的载体是 LAS 文件这是一种带表头元数据的文本格式记录了井名、曲线名、深度区间和采样间隔。很多人第一次用pandas.read_csv直接读 LAS结果表头全是注释行数值列错位这是最典型的翻车点。正确做法是用lasio库解析它能把曲线自动转成 DataFrame省掉手工切表头的麻烦。2.1 LAS 解析与曲线对齐import lasio import pandas as pd import numpy as np # 读取 LAS 文件注意不同版本 lasio 的 API 略有差异 las lasio.read(well_01.las) # 转成 DataFrame深度作为索引 df las.df() df.index.name DEPTH # 查看曲线清单和深度范围 print(las.curves.keys()) print(f深度范围: {df.index.min()} - {df.index.max()}, 采样点数: {len(df)}) # 常见曲线GR(自然伽马) RT(深电阻率) RHOB(密度) NPHI(中子孔隙度) DT(声波时差) # 统一列名避免不同井命名不一致 rename_map {GR: GR, ILD: RT, RHOB: RHOB, NPHI: NPHI, DTC: DT} df df.rename(columns{k: v for k, v in rename_map.items() if k in df.columns})这段代码的逻辑是先用lasio.read拿到 LAS 对象las.df()直接输出以深度为索引的 DataFrame。参数上要注意lasio默认把深度作为索引如果你的 LAS 里深度曲线名不是DEPT需要在读取时用index_unit或手动指定。重命名那一步是为了后续多井合并时列名统一否则concat之后会出现GR和GR_1两列模型输入维度对不上。2.2 多井合并与深度重采样单井数据往往不够训练课设里通常给的是多口井。不同井的采样间隔可能不一样有的是 0.125 米有的是 0.1 米直接按行拼接会导致深度错位。# 假设已经读入多口井存成列表 wells [lasio.read(fwell_{i:02d}.las).df() for i in range(1, 6)] # 统一重采样到 0.1 米间隔 resampled [] for w in wells: w w[~w.index.duplicated(keepfirst)] # 去重深度 w w.sort_index() new_index np.arange(w.index.min(), w.index.max(), 0.1) w w.reindex(w.index.union(new_index)).interpolate(methodlinear).loc[new_index] resampled.append(w) # 合并加井名列区分来源 for i, w in enumerate(resampled): w[WELL] fwell_{i1:02d} df_all pd.concat(resampled, axis0)重采样的核心是reindex加interpolate先把原索引和新索引取并集插值后再截取新索引这样不会丢边界。参数methodlinear对测井曲线足够用如果曲线有突变比如薄层可以考虑methodnearest保留原始值。加WELL列是为了后续按井划分训练集和测试集避免同一口井的数据同时出现在两边造成信息泄漏。提示如果 LAS 文件里某些曲线全是 -999.25 这类无效值插值前先替换成np.nan否则会把无效值当真实数据插进去。3. 岩性识别建模从标签编码到随机森林与混淆矩阵岩性识别本质是多分类问题输入是几条测井曲线输出是岩性类别砂岩、泥岩、灰岩等。课设包里通常已经给好了岩性标签列可能是岩性名称字符串也可能是数字编码。这一步的关键不是模型多复杂而是标签对齐和类别不平衡处理。3.1 标签编码与特征选择from sklearn.preprocessing import LabelEncoder from sklearn.model_selection import train_test_split # 假设 df_all 里已有 LITH 列是岩性名称 df_model df_all.dropna(subset[GR, RT, RHOB, NPHI, DT, LITH]).copy() le LabelEncoder() df_model[LITH_CODE] le.fit_transform(df_model[LITH]) print(dict(zip(le.classes_, le.transform(le.classes_)))) feature_cols [GR, RT, RHOB, NPHI, DT] X df_model[feature_cols].values y df_model[LITH_CODE].values # 按井划分避免同井数据泄漏 well_ids df_model[WELL].unique() train_wells well_ids[:4] test_wells well_ids[4:] train_mask df_model[WELL].isin(train_wells) X_train, y_train X[train_mask], y[train_mask] X_test, y_test X[~train_mask], y[~train_mask]LabelEncoder把岩性名称转成 0 到 N-1 的整数le.classes_的顺序就是编码顺序报告里要写清楚。特征选择这里用了五条常规曲线实际课设里可能还有更多但要注意不是曲线越多越好相关性高的曲线比如 RHOB 和 NPHI 在某些岩性段高度负相关同时放进去会增加冗余树模型虽然能扛但解释性会变差。按井划分而不是随机划分是因为同一口井相邻深度的数据高度相似随机划分会让测试集准确率虚高答辩时被问到「为什么测试集这么高」就尴尬了。3.2 随机森林训练与评估from sklearn.ensemble import RandomForestClassifier from sklearn.metrics import classification_report, confusion_matrix rf RandomForestClassifier( n_estimators200, # 树的数量课设数据量下 100-300 足够 max_depth12, # 限制深度防过拟合 min_samples_leaf5, # 叶节点最小样本缓解类别不平衡 class_weightbalanced, # 自动按类别频率加权 random_state42 ) rf.fit(X_train, y_train) y_pred rf.predict(X_test) print(classification_report(y_test, y_pred, target_namesle.classes_)) print(confusion_matrix(y_test, y_pred))参数上n_estimators不是越大越好超过 300 之后准确率提升很小但训练时间线性增长。class_weightbalanced对岩性识别很关键因为泥岩段通常远多于砂岩段不加权的话模型会偏向多数类少数类召回率惨不忍睹。min_samples_leaf5是防止树把噪声当规律测井曲线本身有测量误差叶节点样本太少容易过拟合。评估时不要只看准确率classification_report里的f1-score和recall更重要。如果某个岩性类的 recall 低于 0.5说明模型基本没学会这一类需要回头检查标签是否准确、该类样本是否太少。混淆矩阵能看出具体是哪两类在互相混淆比如砂岩和粉砂岩在 GR 曲线上区分度低这是数据本身的问题不是调参能解决的。注意课设报告里如果只放一个准确率数字工作量显得很单薄。把混淆矩阵热力图、特征重要性排序、不同n_estimators下的准确率曲线都放上去报告厚度和说服力完全不一样。4. 测井曲线回归用随机森林和 XGBoost 补全缺失曲线曲线回归的任务通常是用几条容易测的曲线去预测一条难测或缺失的曲线比如用 GR、RT、RHOB 预测声波时差 DT或者用常规曲线预测核磁孔隙度。这和岩性识别是两条线但共用同一套数据预处理流程课设包里一般会分开写两个脚本。4.1 回归目标构建与特征工程from sklearn.ensemble import RandomForestRegressor from sklearn.metrics import mean_squared_error, r2_score # 目标预测 DT特征用其他曲线 target_col DT reg_feature_cols [GR, RT, RHOB, NPHI] df_reg df_all.dropna(subsetreg_feature_cols [target_col]).copy() # 加一个深度特征有时深度本身携带压实趋势信息 df_reg[DEPTH_NORM] (df_reg.index - df_reg.index.min()) / (df_reg.index.max() - df_reg.index.min()) reg_feature_cols.append(DEPTH_NORM) Xr df_reg[reg_feature_cols].values yr df_reg[target_col].values # 同样按井划分 train_mask_r df_reg[WELL].isin(train_wells) Xr_train, yr_train Xr[train_mask_r], yr[train_mask_r] Xr_test, yr_test Xr[~train_mask_r], yr[~train_mask_r]把深度归一化后作为特征是因为声波时差随深度增加有压实趋势浅层和深层的 DT 基线不同。如果不加这个特征模型在训练井深度范围之外的井上预测会系统性偏高或偏低。这一步在课设报告里可以作为「特征工程」部分写一段解释为什么加、加了之后 RMSE 降了多少。4.2 模型对比与残差分析from xgboost import XGBRegressor models { RandomForest: RandomForestRegressor(n_estimators200, max_depth12, random_state42), XGBoost: XGBRegressor(n_estimators300, learning_rate0.05, max_depth6, random_state42) } for name, model in models.items(): model.fit(Xr_train, yr_train) pred model.predict(Xr_test) rmse mean_squared_error(yr_test, pred, squaredFalse) r2 r2_score(yr_test, pred) print(f{name}: RMSE{rmse:.2f}, R2{r2:.3f})XGBoost 的learning_rate设 0.05 配合 300 棵树是课设数据量下的稳妥组合。如果 RMSE 比随机森林低但 R2 反而低说明 XGBoost 在部分深度段预测偏差大这时候要看残差分布——把yr_test - pred按深度画出来如果残差在某个深度区间集中偏正或偏负说明模型没学到该段的规律可能需要加特征或分段建模。残差分析是课设报告里拉开差距的地方。大部分人只放一个 RMSE 数字如果你能画出「预测 vs 真实」散点图、残差随深度变化图、不同模型残差对比箱线图报告的技术深度立刻上一个档次。这些图用matplotlib几行就能画代码包里通常也有现成的。提示如果课设要求用深度学习把随机森林换成 1D-CNN 或 LSTM输入改成滑动窗口的曲线序列但数据量少的时候树模型往往更稳别为了用而用。5. 避坑与排查课设跑不通时先查这五条5.1 现象LAS 读取后全是 NaN原因LAS 文件版本不同lasio对某些版本的表头解析会失败或者曲线名大小写不匹配。解决先用文本编辑器打开 LAS 看~C段的曲线名读取后用las.curves.keys()核对必要时手动指定lasio.read(..., mnemonic_casepreserve)。5.2 现象模型准确率 0.99 但答辩被质疑原因随机划分导致同井相邻深度数据同时进训练集和测试集信息泄漏。解决改成按井划分准确率会降到合理区间通常 0.7-0.85但这个数字才经得起追问。5.3 现象XGBoost 训练报错或结果异常原因xgboost版本和scikit-learn版本不兼容或者输入数据里有inf。解决先df.replace([np.inf, -np.inf], np.nan).dropna()再检查xgboost.__version__课设包里一般有requirements.txt按它装。5.4 现象混淆矩阵里某一类全是 0原因该类样本在训练集里太少或者标签编码时该类没出现。解决检查le.classes_和各类样本数样本少于 30 的类考虑合并或过采样别硬训。5.5 现象回归 RMSE 很大但 R2 还行原因目标曲线量纲大比如 DT 在 200 左右RMSE 绝对值自然大。解决报告里同时给 RMSE 和归一化 RMSERMSE / 目标均值或者直接看 R2别只拿 RMSE 说事。6. 让课设报告多拿十分的两个技巧特征重要性解释与交叉验证特征重要性是树模型自带的东西但很多人只画个条形图就完了。真正能加分的做法是把特征重要性排序和地质认识对上。比如 GR 重要性最高说明自然伽马对岩性区分贡献最大这和砂岩低 GR、泥岩高 GR 的常识一致如果 RT 重要性异常高要检查是不是数据里 RT 有异常值主导了分裂。把这段解释写进报告老师一眼就能看出你不是无脑调包。import matplotlib.pyplot as plt importances rf.feature_importances_ idx np.argsort(importances)[::-1] plt.figure(figsize(8, 5)) plt.bar(range(len(feature_cols)), importances[idx]) plt.xticks(range(len(feature_cols)), [feature_cols[i] for i in idx], rotation30) plt.title(Feature Importance for Lithology Classification) plt.tight_layout() plt.savefig(feature_importance.png, dpi150)另一个技巧是加交叉验证。课设里通常只做一次训练测试划分结果波动大。用cross_val_score做 5 折交叉验证报告里写「5 折交叉验证准确率 0.78 ± 0.04」比单次结果可信得多。注意这里的折要按井分用GroupKFold而不是默认的KFold否则又回到信息泄漏的老路。from sklearn.model_selection import GroupKFold, cross_val_score groups df_model[WELL].values gkf GroupKFold(n_splits5) scores cross_val_score(rf, X, y, cvgkf, groupsgroups, scoringf1_macro) print(fGroupKFold F1: {scores.mean():.3f} ± {scores.std():.3f})GroupKFold保证同一口井的数据不会同时出现在训练折和验证折里scoringf1_macro对类别不平衡更敏感。这两个改动加起来不到十行代码但报告里的「实验设计」部分就从「随便跑了一下」变成「有意识地控制了数据泄漏」。从那以后我每次拿到测井数据第一件事不是急着建模而是先画几条曲线的深度剖面图肉眼确认曲线形态和岩性标签对得上再往下走。这个习惯帮我省掉了至少三次「模型跑完才发现标签错位」的返工。希望帮到你。本文还有配套的精品资源点击获取