news 2026/10/2 3:13:27

Python测井课设实战:岩性识别与曲线回归全流程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Python测井课设实战:岩性识别与曲线回归全流程

简介:这份资源面向计算机、人工智能、自动化、电子信息等专业的高校学生与科研人员,围绕人工智能在石油测井领域的应用,提供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(f"well_{i:02d}.las").df() for i in range(1, 6)] # 统一重采样到 0.1 米间隔 resampled = [] for w in wells: w = w[~w.index.duplicated(keep="first")] # 去重深度 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(method="linear").loc[new_index] resampled.append(w) # 合并,加井名列区分来源 for i, w in enumerate(resampled): w["WELL"] = f"well_{i+1:02d}" df_all = pd.concat(resampled, axis=0)

重采样的核心是reindex加interpolate,先把原索引和新索引取并集,插值后再截取新索引,这样不会丢边界。参数method="linear"对测井曲线足够用,如果曲线有突变(比如薄层),可以考虑method="nearest"保留原始值。加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_estimators=200, # 树的数量,课设数据量下 100-300 足够 max_depth=12, # 限制深度防过拟合 min_samples_leaf=5, # 叶节点最小样本,缓解类别不平衡 class_weight="balanced", # 自动按类别频率加权 random_state=42 ) rf.fit(X_train, y_train) y_pred = rf.predict(X_test) print(classification_report(y_test, y_pred, target_names=le.classes_)) print(confusion_matrix(y_test, y_pred))

参数上,n_estimators不是越大越好,超过 300 之后准确率提升很小但训练时间线性增长。class_weight="balanced"对岩性识别很关键,因为泥岩段通常远多于砂岩段,不加权的话模型会偏向多数类,少数类召回率惨不忍睹。min_samples_leaf=5是防止树把噪声当规律,测井曲线本身有测量误差,叶节点样本太少容易过拟合。

评估时不要只看准确率,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(subset=reg_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_estimators=200, max_depth=12, random_state=42), "XGBoost": XGBRegressor(n_estimators=300, learning_rate=0.05, max_depth=6, random_state=42) } for name, model in models.items(): model.fit(Xr_train, yr_train) pred = model.predict(Xr_test) rmse = mean_squared_error(yr_test, pred, squared=False) 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_case="preserve")。

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 和归一化 RMSE(RMSE / 目标均值),或者直接看 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], rotation=30) plt.title("Feature Importance for Lithology Classification") plt.tight_layout() plt.savefig("feature_importance.png", dpi=150)

另一个技巧是加交叉验证。课设里通常只做一次训练测试划分,结果波动大。用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_splits=5) scores = cross_val_score(rf, X, y, cv=gkf, groups=groups, scoring="f1_macro") print(f"GroupKFold F1: {scores.mean():.3f} ± {scores.std():.3f}")

GroupKFold保证同一口井的数据不会同时出现在训练折和验证折里,scoring="f1_macro"对类别不平衡更敏感。这两个改动加起来不到十行代码,但报告里的「实验设计」部分就从「随便跑了一下」变成「有意识地控制了数据泄漏」。

从那以后我每次拿到测井数据,第一件事不是急着建模,而是先画几条曲线的深度剖面图,肉眼确认曲线形态和岩性标签对得上,再往下走。这个习惯帮我省掉了至少三次「模型跑完才发现标签错位」的返工。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/2 3:12:29

迪士尼动画十二黄金法则深度拆解:动画师必修的表演底层逻辑

我是靠这个入的行。入行第一年,带我的组长丢给我一本翻得起了毛边的《生命的幻象》(The Illusion of Life),他原话是:“软件操作一个月能学会,这十二条动画黄金法则你一年都不一定吃得透。”当时我不服气&a…

作者头像 李华
网站建设 2026/10/2 3:11:15

基于Kingscada自带历史库的车间日报表与趋势曲线实现方案

车间上位机数据报表改造的事,我一开始差点绕远路。客户生产现场是三班倒,每班结束后都要看产量、温度、压力、电量的日报表,还要能随时调出任意一个变量的趋势曲线,用来分析设备状态和产品质量。我们用的上位机组态软件是 Kingsca…

作者头像 李华
网站建设 2026/10/2 3:11:04

Smartstore内容营销实战:博客、论坛与投票功能完整指南

Smartstore内容营销实战:博客、论坛与投票功能完整指南 【免费下载链接】Smartstore A modular, scalable and ultra-fast open-source all-in-one eCommerce platform built on ASP.NET Core 10 项目地址: https://gitcode.com/GitHub_Trending/smar/Smartstore …

作者头像 李华
网站建设 2026/10/2 3:10:16

AI论文写作工具实测:从选题到答辩的全流程效率指南

“AI写论文哪个软件最好”这个问题,最近真的快被问烂了。尤其是到了毕业季,后台私信里全是同款焦虑:到底用哪个工具?能不能一条龙搞定选题、开题、初稿、降重和答辩?会不会被导师一眼识破?我这阵子专门花了…

作者头像 李华
网站建设 2026/10/2 3:10:10

玉米叶病检测数据集COCO格式解析与训练避坑指南

简介:这份玉米叶病预测数据集面向计算机视觉与智慧农业方向的开发者,提供10884张真实玉米叶片图像,覆盖叶枯病、普通锈病、灰叶斑病和健康叶片四类目标,适合用于目标检测、实例分割等模型训练与验证,标注由人工完成并统…

作者头像 李华
网站建设 2026/10/2 3:09:45

QoS质量配置实战:从拥塞识别到MQC策略调优的完整指南

这几年我经手过的网络故障里,因为QoS质量配置翻车的案例排得上前三。最近一次是在一家做跨境电商的客户现场:白天办公一切正常,一到晚上自动数据同步任务启动,出口带宽立刻被占满,海外办公室的视频会议连着断了三场。查…

作者头像 李华