简介:岩石薄片自动鉴定传统上依赖人工经验,耗时且主观性强。这份资源面向机器学习与深度学习的课程设计、毕业设计等场景,针对岩石薄片图像识别问题给出完整的分类建模流程,并提供可运行的代码框架。压缩包共28个文件,大小仅758KB,包含15个py脚本(覆盖主程序、网络结构、数据集加载、训练器、GLCM/LBP/颜色特征统计与测试模块)、4个md文档、3张示例图片、结果表格及依赖列表等,源码按功能划分目录,便于读者按步骤复现和二次开发。已有42人学习下载,内容整合了传统特征工程与CNN、SVM等模型对比,输出准确率、召回率、F1分数等评估指标,并覆盖图像增强、去噪、标准化等预处理过程。对地质学相关专业学生而言,该资源既可作为入门深度学习的练习项目,也可作为算法对比实验的基座,方便课程设计与期末大作业直接参考。
1. 岩石薄片自动鉴定:把“肉眼经验”变成可训练的图像特征
资深岩矿鉴定师在偏光显微镜下看一片薄片,判断矿物种类、含量、结构,再定出岩石名称,一天最多处理几十片。而一个用迁移学习训练好的ResNet,一秒钟能跑完一片,准确率还能稳定在90%以上。这不是玄学,是把“干涉色、消光角、颗粒形态”这些肉眼经验,转成了图像特征和分类概率。这份基于机器学习的岩石薄片自动鉴定资源,包含完整数据集、预处理脚本、训练代码和评估模块,能让你直接从零复现一套可用的岩性分类模型。它尤其适合正在做毕业设计、课程设计或期末大作业的同学——不是给你看PPT,是给你一套能跑通、能讲清楚原理的深度学习流程。
2. 数据准备与预处理:用分组切分和轻量增广守住泛化底线
岩石薄片图像和自然照片最大的区别在于:它的特征高度依赖光学条件。单偏光下看颜色和突起,正交偏光下看干涉色和消光,同一块矿物在不同光性下表现完全不同。如果数据准备阶段不控制好这一点,后面模型训练得再漂亮,换一批切片就崩。
2.1 标注格式与目录组织:先用 ImageFolder 把图片归好类
拿到资源后,第一件事是打开 data 目录看结构。我推荐直接按 ImageFolder 方式组织,因为 PyTorch 的datasets.ImageFolder可以直接读取,不需要自己写 Dataset:
data/ train/ quartz/ feldspar/ mica/ amphibole/ val/ quartz/ feldspar/ mica/ amphibole/先跑一段脚本统计每类图片数量,确认有没有类别严重缺失:
import os from collections import Counter data_root = 'data/train' counts = Counter() for cls in os.listdir(data_root): cls_path = os.path.join(data_root, cls) if os.path.isdir(cls_path): counts[cls] = len(os.listdir(cls_path)) print(counts)这段代码遍历每个类目文件夹,统计图片数量并打印。如果发现某类只有几十张、另一类上千张,就需要在后续采样本时做加权或过采样。注意data_root要改成你实际的训练目录;如果资源里给的是 CSV 标注而不是文件夹结构,你需要先用os.makedirs把图片按标签移动成上述结构,再继续往下走。
2.2 光照归一化与增广策略:敢砍掉色彩抖动才有稳定识别
岩石薄片图像的光照不均非常常见。光源亮度、目镜差异、相机白平衡都会让同一矿物的颜色漂移。很多新手上来就用 ImageNet 的标准化,即mean=[0.485,0.456,0.406],但显微镜图像的色彩分布和自然图像差得远,我用下来更推荐先做全局归一化:
import cv2 import numpy as np def normalize_img(img): # img 是 BGR 图像,返回 float32,零均值单位方差 img = img.astype(np.float32) / 255.0 mean = np.mean(img, axis=(0, 1), keepdims=True) std = np.std(img, axis=(0, 1), keepdims=True) + 1e-6 return (img - mean) / std加1e-6是为了防止某张图某个通道标准差为 0 时除零报错。这个归一化是逐张图做的,能有效抑制不同显微镜光源强度带来的差异。
数据增强方面,我一般用这套组合:
import torchvision.transforms as T train_transform = T.Compose([ T.RandomHorizontalFlip(p=0.5), T.RandomVerticalFlip(p=0.5), T.RandomRotation(90), T.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.05, hue=0.02), T.Resize((256, 256)), T.CenterCrop(224), ])关键在ColorJitter的saturation=0.05和hue=0.02。岩石鉴定非常依赖干涉色,饱和度和色相一旦被大范围扰动,比如saturation=0.5,正交偏光下不同矿物的干涉色会被拉成同一个色,模型彻底学不到光性特征。我见过有人把hue设成 0.1,训练出来的模型在验证集上准确率 80%,但一换到另一台显微镜拍的照片,直接掉到 40%,就是增强把矿物颜色语义破坏了。所以只用亮度、对比度微调,彩色信息尽量保守。
2.3 数据集划分:按薄片分组,别让同一张岩石的补丁泄漏
岩石薄片图像通常把一片岩石裁成很多 patch 作为独立样本。如果随机切分,同一个薄片的 patch 可能同时出现在训练集和验证集,模型会记住背景纹理而表现虚高。正确做法是按薄片 ID 分组进行 GroupShuffleSplit:
import pandas as pd from sklearn.model_selection import GroupShuffleSplit # df 每行包含 image_path, label, slice_id,slice_id 是薄片编号 gss = GroupShuffleSplit(n_splits=1, test_size=0.2, random_state=42) train_idx, val_idx = next(gss.split(df.index, groups=df['slice_id'])) train_df = df.iloc[train_idx] val_df = df.iloc[val_idx] print(len(train_df), len(val_df))groups传入的是薄片 ID,n_splits=1表示只划分一次,test_size=0.2留出 20% 薄片做验证,random_state=42固定随机种子保证可复现。这样划分后,同一薄片的所有 patch 只会在同一边,模型无法通过“记住这个薄片特有的划痕、气泡”来作弊。
如果资源里没有slice_id字段,你也可以从文件名中提取,比如图片命名为slice12_patch03.jpg,用str.split('_')[0]取slice12作为分组键。这一步千万别省,我在一个课程设计里见过泄漏导致的 97% 验证准确率,换成组划分后真实准确率只有 71%,两种结果答辩老师一眼就能分辨哪个是虚高的。
3. 模型选型与训练:ResNet50 迁移学习下的三个关键参数
训练部分是整个项目的心脏。岩石薄片数据集通常规模不大,几千张到几万张,直接从头训练 CNN 很容易过拟合,而且训练周期长。这里最省事、最稳妥的做法是迁移学习。
3.1 为什么用迁移学习而不是从零训练
ImageNet 预训练模型已经在 1000 类自然图像上学过了边缘、角点、纹理、颜色渐变这些底层特征。岩石薄片图像的颗粒边界、解理缝、消光纹理,本质上都依赖这些底层特征。你只需要替换最后的全连接层,让模型把“已经学会的视觉基础”映射到石英、长石、云母这些类别上。
我通常选择 ResNet50 作为默认 backbone,因为它在 ImageNet 上预训练权重好找,推理速度适中,微调起来比 EfficientNet 更稳定。如果你显存紧张,可以换成 ResNet18;如果你追求精度且 GPU 够好,ResNet101 也行。但注意,薄片图像类别差异往往集中在纹理细节,单纯增加网络深度收益有限,不如把数据预处理做精细。
3.2 训练脚本与关键超参数
下面这段是基于 PyTorch 的迁移学习训练骨架,我按资源里的代码逻辑简化过,核心部分保留:
import torch import torch.nn as nn import torch.optim as optim from torch.utils.data import DataLoader from torchvision import models, datasets, transforms class_names = ['quartz', 'feldspar', 'mica', 'amphibole'] num_classes = len(class_names) # 加载预训练 ResNet50,替换全连接层 model = models.resnet50(weights=models.ResNet50_Weights.IMAGENET1K_V1) model.fc = nn.Linear(model.fc.in_features, num_classes) # 冻结 backbone,只训练 fc for param in model.parameters(): param.requires_grad = False for param in model.fc.parameters(): param.requires_grad = True device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') model = model.to(device) criterion = nn.CrossEntropyLoss() optimizer = optim.AdamW(model.fc.parameters(), lr=1e-3, weight_decay=1e-4) scheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=50) train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True, num_workers=4) val_loader = DataLoader(val_dataset, batch_size=32, shuffle=False, num_workers=4) for epoch in range(10): model.train() train_loss = 0.0 for images, labels in train_loader: images, labels = images.to(device), labels.to(device) optimizer.zero_grad() outputs = model(images) loss = criterion(outputs, labels) loss.backward() optimizer.step() train_loss += loss.item() * images.size(0) scheduler.step() print(f'Epoch {epoch+1}: train_loss={train_loss / len(train_loader.dataset):.4f}')这里有三个参数值得单独说。
第一个是lr=1e-3。由于冻结了 backbone,只有 fc 层在更新,参数不多,学习率可以从 1e-3 起步。如果训练 loss 来回震荡,就把学习率降到 3e-4 或 1e-4。
第二个是batch_size=32。如果你的 GPU 显存只有 6G,ResNet50 + 224 输入直接跑会爆显存,这时把 batch_size 降到 16 或 8,num_workers 保持 4 即可。岩石薄片原图可能很大,但送入模型的必须是 resize 后的 224 或 256 统一尺寸。
第三个是T_max=50,它表示余弦退火的学习率周期。我们这里只训练 10 个 epoch 来验证代码通不通,真正跑的时候可以设 50 个 epoch,让学习率从 1e-3 平滑降到接近 0,比固定学习率更稳。
3.3 损失函数与优化器选择:类别不平衡时用加权交叉熵
岩石薄片数据里常见的问题是某类矿物样本特别少,比如角闪石只有 100 多张,石英却有 2000 张。普通交叉熵会让模型偏向多数类,训练结束时稀有类 recall 几乎为 0。解决方法是给交叉熵加类别权重:
import numpy as np class_counts = np.bincount(y_train) class_weights = 1.0 / (class_counts + 1e-6) class_weights = class_weights / class_weights.sum() * len(class_counts) criterion = nn.CrossEntropyLoss(weight=torch.tensor(class_weights, dtype=torch.float).to(device))y_train是训练集的标签数组,np.bincount统计每个类别出现次数,倒数作为权重,再做归一化。这样样本少的类别在 loss 里占据更大比例,模型被迫重视它们。
优化器我用AdamW而不是Adam,因为它带了正确的权重衰减,weight_decay=1e-4能有效防止 fc 层在小数据上过拟合。如果你之后解冻 backbone 做微调,记得把 backbone 参数的学习率降到 1e-5,不然很容易破坏预训练的特征表达。
4. 避坑指南:五个岩石图像分类的经典翻车现场
这一章的内容全部来自我实际跑岩石薄片项目的血泪经验。每个坑我都给你说清现象、原因、解决,照着排查能省半天调试时间。
4.1 训练 loss 下降但验证精度不动
现象:训练集 loss 从 1.2 降到 0.3,但验证集准确率一直在 60% 徘徊,甚至偶尔下降。
原因:最常见的是学习率太大,fc 层震荡,无法收敛到局部最优;另一种可能是验证集存在强分类不平衡,准确率被多数类主导。
解决:先把学习率降到 1e-4,重跑 20 个 epoch 看趋势。如果验证 loss 还是不动,检查验证集标签顺序是否正确,以及是否在验证数据上跑到了未归一化的原图。
4.2 图像尺寸过大导致 GPU 显存溢出
现象:训练脚本一启动就报torch.cuda.OutOfMemoryError: CUDA out of memory。
原因:岩石薄片原图很多是 2048x1536 甚至更大,直接collate进 GPU 必然爆显存。
解决:在DataLoader前强制把图片统一到 224 或 256 尺寸。资源里的代码通常会加这两行:
T.Resize((256, 256)), T.CenterCrop((224, 224)),Resize 先降低分辨率,CenterCrop 裁掉边缘无关区域,既保住了矿物颗粒主体,又解决了尺寸问题。如果还爆,就降低 batch_size 到 8,并把num_workers设为 2 减少内存峰值。
4.3 数据增强把干涉色抹掉了
现象:训练集增强后,模型在训练集上准确率 95%,但验证集只有 60%。查看增强后的图片,发现矿物的干涉色已经变得面目全非。
原因:ColorJitter里saturation、hue参数设置过大,比如saturation=0.5,把石英的灰白干涉色变成了淡蓝色,模型学到的是“颜色漂移后的伪特征”。
解决:把 ColorJitter 改为只保留亮度和对比度,饱和度和色相设成 0 或很小。
T.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.0, hue=0.0)我建议多数项目直接不用色相增强。岩石鉴定的核心光性特征就是干涉色,色相稍微偏移,类别语义就变了。这条坑和 2.2 节里说的一致,但值得在训练阶段再强调一次。
4.4 验证指标虚高:同一薄片的 patch 泄漏
现象:验证集准确率 97%,但把模型用到新切片的照片上,准确率只有 70% 左右。查代码发现切分时没有按薄片分组。
原因:同一薄片的 patch 因为背景、划痕、胶层高度相似,被随机分割后,模型等于“见过”验证图像的兄弟版本,属于数据泄漏。
解决:回到 2.3 节,用GroupShuffleSplit按slice_id分组切分。如果项目里没给薄片编号,至少按文件名前缀分组。这个坑我称之为“最贵的 27 个点”,而且评审老师很容易通过查看你的混淆矩阵和实际测试集表现戳穿。
4.5 换一台显微镜就“失灵”:域漂移
现象:模型在 A 显微镜拍摄的验证集上 F1 是 0.88,换到 B 显微镜拍摄的一组新图片,F1 掉到 0.45。
原因:不同显微镜的光源色温、物镜透明度、相机矩阵存在差异,导致同一矿物的 RGB 分布整体漂移。模型学到了 A 显微镜特有的色彩偏移,而不是矿物本身的特征。
解决:在预处理阶段做逐图全局归一化(见 2.2),或者在训练集里混入至少两个不同显微镜来源的数据。如果资源里只有单批数据,可以在论文里明确说明该模型适用于同类型显微镜,并建议采集多源数据作为后续工作。
5. 评估与调参:混淆矩阵比 accuracy 更能暴露模型短板
很多同学训练结束只打印一个test accuracy: 0.87就完事了。但对于岩石薄片鉴定这种类别不平衡、且类间相似度高的多分类问题,单看 accuracy 会掩盖太多问题。
5.1 评估指标怎么选:宏 F1 和加权 F1
多分类场景下,我一般同时输出宏平均 F1 和加权平均 F1。宏平均对每个类一视同仁,稀有类表现差会直接拉低分数;加权平均则按样本数加权,能反映整体性能。两组对比能看出模型是否偏科。
from sklearn.metrics import classification_report, confusion_matrix import seaborn as sns import matplotlib.pyplot as plt # y_true 和 y_pred 是验证集真实标签和模型预测 report = classification_report(y_true, y_pred, target_names=class_names) print(report) cm = confusion_matrix(y_true, y_pred) sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', xticklabels=class_names, yticklabels=class_names) plt.savefig('confusion_matrix.png', dpi=150)classification_report会输出每个类别的 precision、recall、f1,以及macro avg和weighted avg。如果macro avg比weighted avg低很多,说明稀有类普遍不理想。
5.2 从混淆矩阵找模型短板
混淆矩阵里最醒目的往往不是对角线上的数字,而是那些非零的非对角元。比如石英和长石经常互相混淆,这是因为两者在单偏光下都是无色透明颗粒,仅靠颜色特征区分不开。看到这个,你就该知道下一步不是继续调学习率,而是考虑引入正交偏光图像,或者对矿物颗粒的轮廓、解理方向做形态学特征补充。如果云母偶尔被识别成角闪石,说明模型被两者的深色色调迷惑,可能需要检查训练集里云母图片是否有反光干扰。
5.3 调参路径与收敛判断
调参不要随机乱试。我一般按这个顺序:先固定迁移学习冻结 backbone,只调 fc 层的学习率(1e-3 起,看 loss 是否下降);然后调 batch_size(在显存允许范围内尽量大,能稳定梯度);最后调 weight_decay(1e-4 起步,过拟合明显时加到 1e-3)。
为了让训练在合适的时候停下来,我强烈建议加 Early Stopping:
best_val_loss = float('inf') patience = 10 counter = 0 for epoch in range(50): train_one_epoch() val_loss = evaluate() if val_loss < best_val_loss: best_val_loss = val_loss counter = 0 torch.save(model.state_dict(), 'best_model.pth') else: counter += 1 if counter >= patience: print('Early stopping at epoch', epoch + 1) breakpatience=10意味着验证 loss 连续 10 个 epoch 没有刷新最低值就停止训练,避免后期过拟合。保存的best_model.pth是验证 loss 最低时的权重,而不是最后一个 epoch 的权重,这个细节能让最终模型稳定好几个点。
6. 进阶验证:用 Grad-CAM 热力图让模型“说”出它看到了什么
模型训练完,准确率不错,但答辩老师一定会问:“你怎么知道模型学到了矿物特征,而不是记住了背景?”这时候 Grad-CAM 是你最好的解释工具。
pip install grad-camfrom pytorch_grad_cam import GradCAM from pytorch_grad_cam.utils.image import show_cam_on_image import torch model.eval() model = model.to('cuda' if torch.cuda.is_available() else 'cpu') # 选择 ResNet50 最后一个残差块作为目标层 target_layers = [model.layer4[-1]] cam = GradCAM(model=model, target_layers=target_layers) # input_tensor 是归一化后的单张图像,shape 为 (1, 3, 224, 224) grayscale_cam = cam(input_tensor=input_tensor, target_category=None) cam_image = show_cam_on_image(input_tensor_denormalized, grayscale_cam[0], use_rgb=True)target_category=None会把预测概率最高的类别作为目标,生成该类别对应的热力图。show_cam_on_image把热力图叠加回原图,红色区域表示模型重点关注的区域。
怎么看结果?如果热力图集中在一颗矿物颗粒的内部和边缘,比如石英颗粒的解理缝附近,说明模型学到了矿物的内部结构和边界纹理。如果热力图大幅落在图像角落、树脂胶层、气泡或者金属针尖上,说明模型在走捷径,它可能记住了切片制作工艺的痕迹。遇到这种情况,我一般回看数据清洗,把那些带明显划痕、气泡和胶层污染的图片剔除,或者做数据增广把背景干扰稀释掉。
从那以后,我每次提交模型前都会随机抽十张验证集图片跑 Grad-CAM,如果热力图聚焦在颗粒主体而不是背景,我才敢把结果写进毕业设计。毕竟一张热力图比一个孤零零的 accuracy 数字更有说服力,它能帮你和答辩老师快速建立信任。希望这整套流程能让你在复现岩石薄片自动鉴定项目时少走弯路,一次跑通。
本文还有配套的精品资源,点击获取