☰
基于深度学习的海上捕鱼方式识别:围网、刺网、拖网三分类实战
2026/10/5 6:02:50 网站建设 项目流程

简介:这份资源面向深度学习与计算机视觉方向的初学者及中级实践者,聚焦海上渔民捕鱼方式识别这一具体图像分类任务,涵盖围网、刺网和拖网三类作业方式的判别。资源包共14个文件,以10个Python脚本为核心,辅以3个Markdown说明文档和1个Shell运行脚本,整体约30KB,结构紧凑,便于快速上手。内容围绕数据预处理、模型构建、训练优化、验证与测试等环节展开,涉及卷积神经网络及预训练模型微调思路,并配有特征提取、模型训练与指标评估等模块化代码,帮助读者理解从数据到模型落地的完整流程。目前已有266人学习下载,适合希望将深度学习应用于渔业管理与生态保护场景的读者参考,也可作为图像分类项目练手与技能提升的实践素材。

1. 海上捕鱼方式识别:一份能跑通围网、刺网、拖网三分类的深度学习资源

渔船在海上作业,光看船型很难判断它到底在干什么——围网船可能正在放网,也可能只是转场;拖网船收起网具后和普通货船在卫星图里几乎没差别。真正决定生态影响的不是船,而是作业方式:围网、刺网、拖网对副渔获物和底栖环境的破坏程度完全不同。这份「基于深度学习的海上渔民捕鱼方式检测」资源包,把三分类任务的数据集、特征提取脚本、深度学习模型和 LightGBM 对照实现全部打包在一起,目录里能看到DL_ClassifierModel.py、nnModel_train.py、lgbModel_train.py、nnFeature_extract.py这些文件,说明作者不只给了一个黑匣子,而是把「深度特征 + 传统树模型」两条路线都摆出来了。适合做渔业遥感、海洋目标识别、或者想找一个非标准图像分类数据集练手的人。下面按「资源结构 → 特征工程 → 模型训练 → 避坑 → 进阶验证」的顺序拆开讲。

2. 资源目录拆解:从run.sh到nnLayer.py的执行链路

2.1 文件分层与模块职责

拿到压缩包先别急着python main.py,这个项目的入口不止一个。根目录下main.py更像总调度,run.sh是 shell 层的启动封装,真正干活的是DL_ClassifierModel.py和lgbModel_train.py这两个训练脚本。lib目录放的是通用工具,utils.py里通常是路径拼接、日志、随机种子固定这类函数;metrics.py负责准确率、召回率、F1 的计算;nnLayer.py定义网络层,可能是自定义的卷积块或注意力模块。cache目录大概率存中间特征或预处理后的张量,model目录保存训练权重,nn和lgb两个子目录分别对应深度模型和 LightGBM 的配置或输出。

文件/目录作用是否必须改
main.py总入口,串联特征提取与训练看情况
run.sh一键执行脚本建议先读
DL_ClassifierModel.py深度模型定义与训练循环调参时改
nnModel_train.py深度模型训练专用脚本常改
lgbModel_train.pyLightGBM 训练脚本对照实验用
nnFeature_extract.py用网络提取特征换 backbone 时改
lgbFeature_extract.py为树模型提取特征特征工程时改
metrics.py评估指标一般不动
nnLayer.py自定义网络层改结构时动
utils.py工具函数路径相关要改
cache缓存目录确保可写
model权重保存确保可写

这个分层的好处是:你可以只跑 LightGBM 路线,不碰深度模型;也可以只用nnFeature_extract.py把特征抽出来,喂给别的分类器。常见做法是先把utils.py里的数据根路径改成你解压后的实际路径,再决定走哪条线。

2.2 环境依赖与run.sh的读法

项目没有给requirements.txt,但从文件名能反推依赖:深度学习部分需要 PyTorch 或 TensorFlow(DL_ClassifierModel.py和nnLayer.py的写法会暴露),LightGBM 部分需要lightgbm,数值计算需要numpy、pandas,图像处理需要PIL或opencv-python。run.sh里通常写死了 Python 解释器和脚本执行顺序,先看它调了哪些脚本,就知道作者预期的执行链路。

# 先看 run.sh 内容,不要直接执行 cat run.sh # 典型内容可能长这样: # python nnFeature_extract.py --data_root ./data --cache_dir ./cache # python nnModel_train.py --epochs 50 --batch_size 32 # python lgbFeature_extract.py --cache_dir ./cache # python lgbModel_train.py --data_root ./data

读run.sh的重点是看参数传递方式:是硬编码路径还是用argparse。如果是硬编码,你得手动改;如果是argparse,可以直接在命令行覆盖。我一般会先把run.sh复制成run_local.sh,改里面的路径,保留原文件不动,这样出问题还能对照。

提示:如果run.sh里用了source activate或conda activate,先确认你的环境名对得上,否则脚本会在第一步就退出。

2.3 数据集的目录假设与适配

项目正文说「内含数据集」,但没给具体目录结构。从nnFeature_extract.py和lgbFeature_extract.py的命名看,数据大概率按类别分文件夹,类似data/train/围网/、data/train/刺网/、data/train/拖网/。你需要先确认解压后的实际结构,再决定要不要写一个小的重组织脚本。

import os import shutil # 假设解压后是 data/raw/ 下面混着所有图片和标签文件 # 目标结构:data/train/{类别名}/xxx.jpg raw_dir = "./data/raw" target_dir = "./data/train" classes = ["围网", "刺网", "拖网"] for cls in classes: os.makedirs(os.path.join(target_dir, cls), exist_ok=True) # 如果标签在文件名或单独的 csv 里,按实际规则解析 # 这里演示按文件名前缀匹配 for fname in os.listdir(raw_dir): for cls in classes: if fname.startswith(cls): shutil.copy( os.path.join(raw_dir, fname), os.path.join(target_dir, cls, fname) ) break

这段脚本的逻辑是:先建好三个类别文件夹,再按文件名前缀把图片归位。参数raw_dir和target_dir按你实际解压路径改。如果标签在 CSV 里,就把startswith换成读 CSV 查表。注意别用move,用copy保留原始数据,后面排查问题时还能回溯。

3. 特征提取与模型训练:nnFeature_extract.py和lgbModel_train.py怎么配合

3.1 深度特征提取的入口与参数

nnFeature_extract.py的典型作用是加载一个预训练 backbone(ResNet、VGG 或项目自定义的 CNN),把每张图映射成一个固定维度的向量,存到cache目录。这样后续训练分类头或喂给 LightGBM 时不用重复跑卷积。关键参数通常包括--data_root、--cache_dir、--batch_size、--backbone。

# nnFeature_extract.py 的核心逻辑示意(按项目实际代码调整) import torch import torchvision.models as models import torchvision.transforms as T from torch.utils.data import DataLoader from PIL import Image import os import numpy as np # 1. 选择 backbone backbone = models.resnet50(pretrained=True) backbone = torch.nn.Sequential(*list(backbone.children())[:-1]) # 去掉 fc 层 backbone.eval() # 2. 图像预处理 transform = T.Compose([ T.Resize((224, 224)), T.ToTensor(), T.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) ]) # 3. 遍历数据,提取特征 data_root = "./data/train" cache_dir = "./cache" os.makedirs(cache_dir, exist_ok=True) features = [] labels = [] class_names = sorted(os.listdir(data_root)) with torch.no_grad(): for idx, cls in enumerate(class_names): cls_dir = os.path.join(data_root, cls) for fname in os.listdir(cls_dir): img_path = os.path.join(cls_dir, fname) img = Image.open(img_path).convert("RGB") x = transform(img).unsqueeze(0) feat = backbone(x).squeeze().numpy() features.append(feat) labels.append(idx) np.save(os.path.join(cache_dir, "features.npy"), np.array(features)) np.save(os.path.join(cache_dir, "labels.npy"), np.array(labels))

逻辑说明:backbone去掉全连接层后输出的是池化后的特征向量,ResNet50 是 2048 维。transform里的均值和标准差是 ImageNet 的惯例,如果你换 backbone 要对应调整。torch.no_grad()关掉梯度,省显存。最后把特征和标签存成.npy,lgbFeature_extract.py或lgbModel_train.py直接读这两个文件就行。

参数怎么改:batch_size在显存不够时降到 8 或 16;backbone换成resnet18特征维度变 512,训练更快但精度可能降;Resize尺寸要和 backbone 预期一致,ResNet 系列一般 224。

3.2 LightGBM 对照实验的配置

lgbModel_train.py读cache里的特征和标签,用 LightGBM 做三分类。这个路线的价值在于:如果深度模型调参成本高,树模型能快速给一个 baseline,而且特征重要性可以反推哪些维度对区分围网、刺网、拖网最有用。

# lgbModel_train.py 核心逻辑示意 import numpy as np import lightgbm as lgb from sklearn.model_selection import train_test_split from sklearn.metrics import classification_report # 1. 读特征 X = np.load("./cache/features.npy") y = np.load("./cache/labels.npy") # 2. 划分训练验证集 X_train, X_val, y_train, y_val = train_test_split( X, y, test_size=0.2, random_state=42, stratify=y ) # 3. 构造 LightGBM 数据集 train_data = lgb.Dataset(X_train, label=y_train) val_data = lgb.Dataset(X_val, label=y_val, reference=train_data) # 4. 参数设置 params = { "objective": "multiclass", "num_class": 3, "metric": "multi_logloss", "boosting_type": "gbdt", "num_leaves": 31, "learning_rate": 0.05, "feature_fraction": 0.8, "bagging_fraction": 0.8, "bagging_freq": 5, "verbose": -1 } # 5. 训练 model = lgb.train( params, train_data, num_boost_round=200, valid_sets=[val_data], callbacks=[lgb.early_stopping(20), lgb.log_evaluation(20)] ) # 6. 评估 y_pred = model.predict(X_val) y_pred_label = np.argmax(y_pred, axis=1) print(classification_report(y_val, y_pred_label, target_names=["围网", "刺网", "拖网"]))

逻辑说明:objective设为multiclass,num_class是 3。early_stopping(20)表示验证集指标 20 轮不提升就停,防止过拟合。feature_fraction和bagging_fraction是 LightGBM 的随机采样参数,小数据集上适当降低能提泛化。classification_report会输出每类的精确率、召回率和 F1,重点看拖网和刺网有没有混淆——这两种网具在图像上可能都表现为船尾有网具,区分度低。

参数怎么改:num_leaves从 31 降到 15 可以缓解过拟合;learning_rate从 0.05 降到 0.02 并增加num_boost_round到 500,精度可能更好但训练变慢;如果某类样本特别少,加is_unbalance=True或scale_pos_weight。

3.3 深度模型训练脚本的调用

nnModel_train.py和DL_ClassifierModel.py配合使用,前者是训练循环,后者定义模型结构。典型调用方式是:

python nnModel_train.py \ --data_root ./data/train \ --model_save ./model/best.pth \ --epochs 50 \ --batch_size 32 \ --lr 0.001

如果脚本没有argparse,就去文件里找if __name__ == "__main__":下面的硬编码变量,手动改。训练时重点看 loss 曲线:如果训练 loss 降但验证 loss 升,就是过拟合,加 dropout 或减层数;如果两个都不降,检查学习率是不是太大或数据标签有没有错。

4. 避坑与排查:数据、显存、类别不平衡的五个血泪经验

4.1 图片路径含中文导致PIL读取失败

现象:nnFeature_extract.py跑到某张图时报UnidentifiedImageError或直接FileNotFoundError,但文件明明存在。原因:PIL或opencv在部分系统上对中文路径支持不好,尤其是 Windows 下。解决:把数据目录改成纯英文路径,或者在读图前用cv2.imdecode(np.fromfile(path, dtype=np.uint8), -1)绕过路径编码问题。

4.2 显存溢出但batch_size已经调到 1

现象:CUDA out of memory,降到batch_size=1还报。原因:nnFeature_extract.py里可能没有用torch.no_grad(),或者模型没有.eval(),导致中间激活值一直保留。解决:确认提取特征时加了with torch.no_grad():和model.eval();如果还不行,用torch.cuda.empty_cache()在每个 batch 后清缓存,或者换 CPU 提取(慢但稳)。

4.3 三类样本数量悬殊导致模型只预测多数类

现象:classification_report里围网 F1 很高,刺网和拖网接近 0。原因:数据集里围网样本远多于另外两类,模型学会了「全猜围网」也能拿高准确率。解决:在 LightGBM 里设is_unbalance=True;在深度模型里用加权交叉熵,权重按类别频率倒数算;或者对少数类做数据增强(旋转、裁剪、颜色抖动)。

4.4cache目录权限或路径不对导致特征文件写不进去

现象:nnFeature_extract.py跑完没报错,但lgbModel_train.py读cache/features.npy时报FileNotFoundError。原因:cache_dir是相对路径,而脚本执行时的工作目录和你以为的不一样。解决:在utils.py里把路径统一转成绝对路径,或者执行前cd到项目根目录。我一般会在脚本开头加os.chdir(os.path.dirname(os.path.abspath(__file__)))。

4.5 训练集和验证集划分时没做分层抽样

现象:验证集里某一类只有一两个样本,指标波动极大。原因:train_test_split默认随机划分,小数据集上容易把某类全分到训练集。解决:加stratify=y,保证训练集和验证集的类别比例一致。如果某类样本少于 10 张,考虑用 K 折交叉验证代替单次划分。

5. 进阶验证:用混淆矩阵和特征重要性反推模型到底学到了什么

跑通训练只是第一步,真正判断这个模型能不能用,要看它错在哪。metrics.py里如果有confusion_matrix就直接调,没有就自己加。

from sklearn.metrics import confusion_matrix import seaborn as sns import matplotlib.pyplot as plt cm = confusion_matrix(y_val, y_pred_label) sns.heatmap(cm, annot=True, fmt="d", xticklabels=["围网", "刺网", "拖网"], yticklabels=["围网", "刺网", "拖网"]) plt.xlabel("预测") plt.ylabel("真实") plt.savefig("./model/confusion_matrix.png")

看混淆矩阵的重点:如果刺网和拖网互相错得多,说明特征空间里这两类重叠严重,可能需要引入时序信息(渔船轨迹)或增加网具细节的局部特征。如果围网被错分成另外两类,检查围网样本里是不是混了非作业状态的船。

LightGBM 还能输出特征重要性:

import matplotlib.pyplot as plt lgb.plot_importance(model, max_num_features=20) plt.savefig("./model/feature_importance.png")

如果最重要的特征集中在某几个维度,可以回到nnFeature_extract.py看这些维度对应 backbone 的哪一层输出,判断模型是不是在依赖背景(比如海面颜色)而不是船和网具本身。我一般会强制自己走一遍「混淆矩阵 → 特征重要性 → 抽查错分样本原图」这个链路,因为只看准确率太容易自欺。从那以后我每次训完分类模型,都强制走一遍混淆矩阵和错分样本抽查,希望帮到你。

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询