简介这是一份面向地质学与机器学习交叉领域学习者的岩石薄片自动鉴定项目包可支撑毕业设计、课程设计及期末大作业。项目围绕岩石薄片图像分类任务提供了完整的Python工程实现涵盖基于CNN的深度学习模型以及随机森林、SVM等传统机器学习方法并包含颜色统计、GLCM纹理、LBP特征提取、图像预处理、模型训练与评估等关键模块。压缩包共28个文件以15个py脚本为主体配合4个md说明文档、1个xlsx结果表及若干图像与配置文本整体体积仅758KB结构清晰、便于本地运行与二次开发。目前已有42人学习/浏览适合正在选做图像识别类课题的学生快速上手。通过该资源可掌握从岩石薄片数据准备到模型测试的完整流程借助README文档与配置脚本降低复现门槛还可参考注释与结果文件完成报告或答辩演示。1. 岩石薄片自动鉴定是什么从地质师的眼睛到机器学习模型的转换岩矿鉴定是地质、油气、材料、考古等方向最依赖经验的工作之一。一块岩石磨成 0.03mm 的薄片放到偏光显微镜下地质师要通过颜色、突起、解理、干涉色、消光角这些光学特征一个一个视场地认出石英、长石、角闪石、方解石最后结合含量和结构给岩石定名。这个流程熟练的人也要花上小半天碰上细粒火山岩或蚀变强烈的样品一天都下不来。这个项目标题做的事情就是把“看图认矿物”这一步交给机器学习输入偏光显微镜下采集的薄片图像输出矿物类别、含量估计甚至辅助岩石定名。适合想用机器学习解决细分领域图像识别问题的从业者也适合实验室里想做自动化升级的工程师——它能解决的痛点是大量重复视场筛选不是替代地质师做最终定名。2. 选型先把路走对图像分类能解决什么不能解决什么2.1 薄片鉴定的本质多偏振态、多视场、多标签的输出岩石薄片鉴定和普通图像分类有一个本质区别一张薄片不是一个样本。一片 2cm × 3cm 的岩石薄片在偏光显微镜 4× 或 10× 物镜下往往要采几十甚至上百个视场才能覆盖主要矿物。每个视场又有单偏光、正交偏光两种状态转动载物台还能在多个角度下观察。一套完整的自动鉴定系统输入是这些图像序列输出不是单一标签而是“这块视场是什么矿物”“整片薄片有哪些矿物、各占多少百分比”“结构是花岗结构还是斑状结构”——本质上是多视场 多偏振态的异步序列融合问题。这决定了项目的第一步不是选模型而是定义输出粒度。常见做法是先做视场级别的单矿物分类再把视场结果按照岩石定名的面积估算规则聚合成薄片级别的成分列表。如果你一上来就想端到端输出“岩石名称”效果通常不好因为岩石名称是矿物组合、含量、结构、蚀变程度共同决定的样本量不够时模型只能记住几条经验规则换个盆地的新样品就翻车。2.2 两条技术路线传统特征 SVM 还是端到端 CNN薄片图像数据量通常很小——一个研究组能攒出几千张标注视场图就算不错了并且标注还得靠地质师肉眼逐张做成本极高。这种情况下两条路线都有各自的场景传统特征 SVM/随机森林先用计算机视觉算法提取矿物的光学特征比如 LBP 纹理、HSV 颜色直方图、形态因子然后用小样本分类器学习。优势是特征可视、可控、解释性强地质师能看出“模型用的是颜色还是纹理”劣势是特征表达的上限低交代蚀变矿物或隐晶质结构容易失效。迁移学习 CNN用 ImageNet 预训练的 ResNet、EfficientNet 做特征提取再微调。优势是特征表达强细粒度的矿物光学差异也能学出来劣势是小样本容易过拟合而且眼底特征不可解释模型到底看了颜色还是看了裂隙边缘你只能猜。我一般建议两条都做先用传统特征跑一版基线证明数据和标注可靠再用 CNN 迁移学习上线。如果传统特征在验证集上的 F1 就很高说明类间差异明确CNN 微调之后通常还能再涨几个点如果传统特征烂得离谱先别急着上 CNN先检查标注和数据采集是不是出了系统性错误。2.3 先定输出格式单标签、多标签还是矿物含量回归很多第一次做这个方向的人直接把问题丢给分类模型输出“这个视场是哪种矿物”的类别概率。但在实际业务里一个视场里往往有两种甚至三种矿物共生单标签强行取 argmax等于逼模型在角闪石和黑云母之间二选一而其实两者都出现在视野里。有三个常见的输出设计单标签多分类每个视场标注为最主要的矿物适用于矿物粒度大、视场中通常只有一种主矿物的样品。多标签分类每个视场标注多个存在的矿物模型输出每个类别的独立概率阈值判定有就是有。适用于细粒岩石、基质矿物混生的视场。含量回归直接预测每个矿物的面积百分比。这个难度最高一般需要先做语义分割或目标检测再统计像素占比不适合作为第一版目标。对于这个项目第一版我强烈建议先跑单标签多分类把 pipeline 整个打通之后再加多标签和含量统计。因为下游的地质师只信“你能认出矿物”不信“你能算准含量”含量页面可以后置。3. 从标注到特征一套能复现的薄片图像处理流程3.1 数据与标注的常见组织方式拿到一个薄片自动鉴定项目压缩包常见的目录安排是images/ 下按薄片编号分文件夹每一片包含单偏光、正交偏光两组图像annotations 用 CSV 逐行记录文件名、矿物标签可能是中文矿物名或英文缩写、采集物镜倍数、偏振态另外有一个 train_val.txt 写明哪些薄片进训练集、哪些进验证集。这里最关键的一点划分训练集和验证集必须以薄片为单位不能用随机图像划分。同一张薄片的相邻视场高度相似随机划分会让验证集偷偷“记住”训练集里的背景和裂隙样式评估虚高到不真实。分组划分之后验证结果才是面向新样品的真实表现。标注时的标签体系建议用代码表比如 qtz石英kfs钾长石pl斜长石bt黑云母hbl角闪石cpx辉石ol橄榄石cal方解石cc隐晶质/基质。不要用中文名直接当类别名气候后面做数据增强和类别合并都方便。3.2 传统特征路线LBP HSV 直方图喂随机森林的完整脚本import numpy as np import cv2 from skimage import feature, color from sklearn.ensemble import RandomForestClassifier from sklearn.preprocessing import StandardScaler from sklearn.model_selection import GroupKFold, cross_val_predict from sklearn.metrics import classification_report def extract_features(img_path): # 读取单偏光或正交偏光下的薄片图像 img cv2.imread(img_path) img cv2.resize(img, (256, 256)) # 统一尺寸到256×256 # 灰度图用于LBP纹理特征 gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # R2表示采样半径2个像素P8表示同周采样8个邻域点 lbp feature.local_binary_pattern(gray, P8, R2, methoduniform) # uniform模式描述了从0到9共10个bin密度归一化使得特征与图像大小无关 hist_lbp, _ np.histogram(lbp.ravel(), binsnp.arange(0, 11), densityTrue) # HSV颜色特征矿物在单偏光下的色调和饱和度比RGB更稳定 hsv cv2.cvtColor(img, cv2.COLOR_BGR2HSV) hist_h, _ np.histogram(hsv[:, :, 0], bins36, range(0, 180), densityTrue) hist_s, _ np.histogram(hsv[:, :, 1], bins32, range(0, 255), densityTrue) # 拼接全部特征10维LBP 36维色调 32维饱和度 78维 feats np.concatenate([hist_lbp, hist_h, hist_s]) return feats # 读取标注CSV每行有image_path, label, thin_section_id import pandas as pd df pd.read_csv(annotations.csv) X np.vstack([extract_features(p) for p in df[image_path]]) y df[label] groups df[thin_section_id] # 按薄片编号分组 clf RandomForestClassifier( n_estimators300, # 300棵树小数据下一般够用 max_depth12, # 限制深度防止过拟合到个别薄片的背景 min_samples_leaf2, # 叶节点至少2个样本抑制噪声 n_jobs-1 ) # 用GroupKFold按薄片划分交叉验证避免同薄片视场泄露 cv GroupKFold(n_splits5) y_pred cross_val_predict(clf, X, y, cvcv, groupsgroups, n_jobs-1) print(classification_report(y, y_pred))LBP 的 P 和 R 参数是这组特征中最敏感的旋钮。P8、R2 适合颗粒矿物之间纹理边界清晰的情况如果样品是细粒基质或隐晶质把 R 增大到 3 或 4 能捕获更大范围的纹理模式但特征被平滑得更多。HSV 特征里 H 通道最重要单偏光下石英无色、长石淡褐、角闪石深绿的色调差异都能在 H 直方图上体现出来S 通道对黑云母这种强多色性矿物尤其敏感。随机森林的两个核心参数是 max_depth 和 min_samples_leaf。薄片图像里背景和胶结物占的比例变化很大树太深会把“某个视场角落有划痕”这种噪声学进去min_samples_leaf2 是保守起见的默认值如果验证集 F1 偏低且训练集 F1 接近 100%先把 min_samples_leaf 提到 4 或 6 再观察。特征提取耗时约每张图 30~50ms几千张图几分钟能跑完完全可以作为基线反复调试。3.3 深度学习路线ResNet18 迁移学习微调import torch import torch.nn as nn from torchvision import models, transforms from torch.utils.data import Dataset, DataLoader # 薄片图像被剪裁成小块再训练标签是CSV里的人工标注矿物名 class ThinSectionDataset(Dataset): def __init__(self, df, transformNone): self.df df self.transform transform def __len__(self): return len(self.df) def __getitem__(self, idx): row self.df.iloc[idx] img cv2.imread(row[image_path]) img cv2.cvtColor(img, cv2.COLOR_BGR2RGB) if self.transform: img self.transform(img) label class_map[row[label]] return img, label transform_train transforms.Compose([ transforms.ToPILImage(), transforms.RandomResizedCrop(224, scale(0.8, 1.0)), # 随机裁剪模拟不同视场取景 transforms.RandomRotation(15), # 旋转增强注意不超过15度 transforms.ColorJitter(brightness0.2, contrast0.2, saturation0.2), transforms.ToTensor(), transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) ]) # 加载ImageNet预训练的ResNet18替换最后一层为项目类别数 model models.resnet18(weightsmodels.ResNet18_Weights.IMAGENET1K_V1) num_ftrs model.fc.in_features num_classes len(class_map) model.fc nn.Linear(num_ftrs, num_classes) model model.to(device) # 关键冻结前两层BN和卷积只微调后半部分防止小样本把预训练特征破坏掉 params_to_update [] for name, param in model.named_parameters(): if layer3 in name or layer4 in name or fc in name: param.requires_grad True params_to_update.append(param) else: param.requires_grad False optimizer torch.optim.Adam(params_to_update, lr1e-4) criterion nn.CrossEntropyLoss()ResNet18 在薄片图像上的表现有个反直觉规律直接全量微调验证集 loss 震荡得厉害因为薄片图像和 ImageNet 自然图像的分布差距大前几层卷积学到的边缘纹理对矿物鉴定是有用的但高层特征需要重新组合。冻结前两层、只微调 layer3、layer4 和 fc 头能让学习过程稳定很多。Batch size 建议用 32lr 从 1e-4 起步每 5 个 epoch 不下降就乘以 0.5 衰减。薄片数据集通常只有几千张图30~50 个 epoch 就会过拟合所以每轮训练保存验证集最好的 check_point而不是训满固定轮数。数据增强里有一个坑RandomRotation 不要超过 15 度因为很多矿物有定向排列比如黑云母的解理方向、片岩的片理构造旋转太大等于告诉模型“方向无所谓”这对细粒变质岩薄片是灾难性的。4. 岩石薄片自动鉴定的五个避坑记录4.1 坑一薄片厚度不同同一种矿物干涉色地动山摇现象训练集 F1 高验证集 F1 直接掉到随机水平或者把不同实验室送来的薄片混合训练后同一个矿物在验证集里被系统性误判。原因岩石薄片的标准厚度是 0.03mm但手工磨片存在误差。厚度偏厚的薄片石英和长石的干涉色会从一级灰白变成一级黄方解石出现高级白双折射强的矿物整个色调偏移。模型学到的不是矿物本身而是“这种干涉色石英”换一批薄片就失效。解决采集时记录每片薄片的实际厚度或者至少记录制片批次。训练时如果数据混合了多个制片批次一定要按薄片分组做交叉验证让验证集覆盖没见过的批次。对厚度差异太大的样本宁可去掉也不要留着当噪声。最稳妥的做法是只用同一个人、同一台磨片机产出的薄片图像做训练和上线。4.2 坑二模型学到的是偏振态不是矿物本身现象模型在训练集上表现极好但实际使用中同一矿物在单偏光和正交偏光下的表现差别巨大模型经常把正交偏光下的石英认成方解石。原因如果把单偏光和正交偏光图像混在一个训练集里又没在标签或特征里区分偏振态模型很容易学到“颜色发白有干涉色某矿物”这种把偏振态信息当矿物特征的表象。更深层的问题是模型分不清矿物本身的颜色和因为偏振态改变产生的颜色。解决两种做法任选其一。第一严格区分偏振态单独训练两个模型一个只吃单偏光图像一个只吃正交偏光图像推断时两个模型概率融合第二把单偏光和正交偏光的同一视场做成双通道输入让模型同时看两种状态。前者结构简单适合起步后者信息更全但需要保证两种偏振态的视场严格对齐采集时就要同步采集。4.3 坑三背景胶和载玻片占了图像一大半现象模型在验证集上准确率 95%但把模型拿到新薄片上对视野角落的透明区域也给出矿物判断准确率崩到 60%。原因薄片图像里环氧树脂胶、载玻片边缘、气泡在单偏光下是浅色低纹理区域在多矿物、多视场的训练集里占了大量比例。模型统计上只要学会“低纹理亮区域输出石英”就能拿高分根本不需要看真正的矿物颗粒。解决训练前做简单的背景分割用 Otsu 阈值把浅色背景区域挖掉只保留有矿物颗粒的像素或者更省事的做法在标注时把明显只有胶的区域标成 background 类让模型学会区分。最推荐的是在采集阶段就避开——对焦时保证视场被矿物填充不要让胶结物区域进图。4.4 坑四稀有矿物类别严重不平衡现象石英、长石类样本几千张角闪石、橄榄石只有几十张训练时模型总是把所有深色矿物预测成黑云母。原因薄片里暗色矿物本身含量就少野外采集时也会自然偏向常见的浅色矿物。交叉熵损失在小样本类别上梯度贡献微弱模型为了整体准确率牺牲少数类。解决改用加权采样每个 epoch 重采样稀有类别让每批次里各类别出现次数接近或者用 Focal Loss把难分类的稀有矿物样本权重放大。换损失函数在 PyTorch 里几行就能实现我一般先试采样权重因为实现的侵入最小、结果最可解释。如果稀有类别只有个位数样本不要指望模型学会直接把这类矿物移除标记为“需要人工复核”。4.5 坑五直接 resize 把细粒度纹理丢光现象用 224×224 输入训练迁移模型石英和斜长石这两种无色的矿物频繁混淆怎么调参都上不去。原因石英和斜长石在单偏光下都是无色透明区分靠的是正交偏光下的干涉色级次和双晶纹细节。这些细节在 224×224 尺度下只有几个像素宽卷积核一次池化就滤没了。解决要么用更高的输入分辨率比如 448×448模型能保住干涉色纹理要么做 patch 采样把原图切成 224×224 的重叠小块再投入训练每个薄片视场生成多个 patch变相增加样本量。后者的代价是推断时也要做同样的切块融合复杂度更高。实际项目中我一般先试 patch 采样因为分辨率提升会显著增加显存占用和训练时间。5. 模型评估与落地参数准确率不是这个项目的考核标准5.1 按矿物分列 precision、recall、F1别只盯着总准确率岩石薄片鉴定这个业务场景里总准确率是最有欺骗性的指标。假设数据里石英占 60%模型把所有样本都预测成石英总准确率也有 60%看起来没那么不堪但在地质师眼里这个模型毫无价值——它把角闪石、橄榄石全部漏掉了。评估时我习惯直接打印 classification_report并且要求每类矿物的 F1 都要看。石英和长石这两类浅色矿物F1 小于 0.85 说明特征提取或标注有问题暗色矿物里角闪石和黑云母F1 能到 0.8 就算优秀。地质上漏判一个稀有矿物比误判一个常见矿物的代价更大因为野外勘探打漏一层目标层位可能意味着整个钻探方案修改。from sklearn.metrics import classification_report, confusion_matrix import matplotlib.pyplot as plt import seaborn as sns # 预测结果y_pred已由交叉验证得到 print(classification_report(y, y_pred, target_nameslist(class_map.keys()))) # 混淆矩阵可视化重点看哪两类矿物最容易互相混淆 cm confusion_matrix(y, y_pred) plt.figure(figsize(10, 8)) sns.heatmap(cm, annotTrue, fmtd, xticklabelslist(class_map.keys()), yticklabelslist(class_map.keys())) plt.tight_layout() plt.savefig(confusion_matrix.png, dpi200)打印出来的报告里需要特别关注两列查每个类别的 recall——它反映“真实存在的这类矿物里模型找到了多少”以及角闪石和辉石、斜长石和钾长石这种邻近矿物对的混淆计数。如果混淆集中在某一对矿物上通常不是模型的问题而是两者的标注本身就有主观分歧这时要回去找地质师确认标注标准是否统一。5.2 混淆矩阵重点看哪几对矿物薄片鉴定里矿物混淆不是均匀分布的几乎总是集中在光学性质接近的类别上。根据我的经验最多发的是这四组评估时单独拿出它们的混淆计数来复盘石英 vs 斜长石vs 钾长石无色矿物三角单偏光下全是无色透明区分靠干涉色和双晶patch 分辨率不足时必然混淆。角闪石 vs 黑云母都是深色解理方向都有明显线条而且经常共生。方解石 vs 白云石颜色、干涉色都接近需要看是否闪突起、是否染红单偏光图像几乎无法区分。辉石 vs 橄榄石蚀变后颜色和纹理相似。如果模型在这几对上混淆不要急着调网络结构。先问标注地质师要每个类别的标注依据说明——很多标注本身就是按“颜色像什么就标什么”拍的脑袋这种标签噪声会直接变成模型的上限。把混淆极高的样本逐张往回翻如果人眼都分不清这组类别在输出端就该合并而不是留着当两个类让模型猜。5.3 低置信度样本回流人工复核一个落地实用的阈值逻辑自动鉴定系统上线后真正投入生产时地质师不会完全相信模型但也不会逐张复查所有视场。最优的工作流是让模型给它自己“把关”设置一个置信度阈值低于阈值的样本打上“存疑”标签自动放进人工复核列表。这在实践中比追求模型准确率更容易落地的多——你可以接受模型实时处理 90% 的视场剩下的 10% 交给人工。import numpy as np # 随机森林/逻辑回归等模型有predict_proba方法 probs clf.predict_proba(X) max_proba np.max(probs, axis1) # 每个样本的最高类别概率 uncertainty 1 - max_proba # 阈值0.25概率低于0.75的样本都需要人工复核 low_confidence_idx np.where(uncertainty 0.25)[0] for i in low_confidence_idx: print(f样本 {df.iloc[i][image_path]}: f预测 {clf.classes_[np.argmax(probs[i])]} f置信度 {max_proba[i]:.2f}请人工复核)阈值取多少不是拍脑袋定的而是用验证集画一条“人工复核率 vs 漏检率”曲线来选。先看曲线膝部一般取 0.2 到 0.3。我把这个阈值说出来会触发很多入门读者问“超参数调优是不是用网格搜索”——在薄片项目里这个阈值的目标不是在验证集上得分最高而是让复查量匹配实验室人力的承受范围。如果地质师每周只愿意花半天复核你就把阈值调高让复查量变小如果有些样品要出正式报告阈值就降低到 0.15。这和模型训练是两套评判逻辑。6. 进阶技巧主动学习能把这个项目的人力成本再砍一半薄片鉴定项目最贵的资源从来不是算力而是地质师的标注时间。一个视场标注平均要 10 到 30 秒几千个视场的标注就是几十个小时的专家工时。传统做法是随机挑视场让地质师标注但随机采样的效率其实很低——模型早就学会石英了你还在给石英刷标签。主动学习的思路正好相反先训练一个初始模型然后让模型挑出它“最拿不准”的视场只把这些视场送给地质师标注下一轮迭代再重复这个过程。# 在第3章特征提取和初始训练之后加入主动学习采样 # uncertainty sampling: 挑出预测熵最高的K个样本 def entropy(probs): # 概率分布越平缓信息熵越高说明模型越不确定 return -np.sum(probs * np.log(probs 1e-10), axis1) probs clf.predict_proba(X_unlabeled) # 对未标注视场预测 entropy_scores entropy(probs) # 每轮挑熵最高最不确定的100个样本进入标注队列 top_k_idx np.argsort(entropy_scores)[-100:] # 导出这部分视场缩略图拼成联系表交给地质师批量标注 with open(need_annotation.csv, w) as f: f.write(image_path,label\n) for idx in top_k_idx: f.write(f{df_unlabeled.iloc[idx][image_path]},待标注\n)主动学习在岩石薄片项目里的效果非常明显第一轮用 200 个随机样本训练效果虽然差但模型已经能识别颜色最鲜明的石英和黑云母第二轮它挑出的是那些灰色地带——无色矿物里分不清长石还是石英的样本、深色矿物里分不清角闪石还是辉石的样本地质师每补 100 个视场模型就解决一片困惑。到第四五轮通常能用不到全量 40% 的标注样本达到接近全量监督训练的效果。我自己的习惯是主动学习和数据增强交替用。注意在采样时也要按薄片分组避免某一轮采样的视场全部来自同一块薄片导致模型被单块样品的岩性特征带偏。另外第一轮的种子标注集不要太小200 个视场是下限低于这个数模型连颜色分布都学不稳后面熵估计也就不准了。每轮采样后把新旧数据合在一起重训用第 5 章的 GroupKFold 协议重新评估如果连续两轮验证集 F1 提升不超过 1 个点说明标注预算差不多花到位了收手做交付。这套流程我从头到尾跑过不止一次最深的体会是岩石薄片鉴定这个领域的难点不在模型结构而在数据协议和标签质量。特征工程也好、迁移学习也好都是把地质师的经验编码成模型能学的东西这个编码过程需要不断回到显微镜下和地质师对答案。架构选型、超参调优只是把已经想清楚的信息表达好真正决定项目成败的是标注标准和样本批次的一致性。做薄片鉴定项目不要急着炫模型先把薄片分组、偏振态区分、背景剔除这三件事做扎实再谈准确率——顺序反了后面每一步都在为前面的偷懒还债。希望帮到你。本文还有配套的精品资源点击获取