从零开始:Mask-RCNN环境配置与自定义数据集训练全流程解析
1. 环境配置避开版本冲突的坑第一次配置Mask-RCNN环境时我花了整整三天时间解决各种依赖冲突。最头疼的就是CUDA、cuDNN和TensorFlow版本的三角关系。这里分享一个已验证可用的组合方案conda create -n mask python3.6 conda activate mask pip install tensorflow-gpu1.15.0 keras2.1.6关键细节如果你用的是RTX 30系显卡CUDA 11.0版本会导致tensorflow-gpu 1.x不兼容。实测发现CUDA 10.0 cuDNN 7.6.5是最稳定的组合安装完成后一定要验证GPU是否生效import tensorflow as tf print(tf.test.is_gpu_available()) # 应该返回True常见报错解决方案Could not create cudnn handle通常是cuDNN版本不匹配建议完全卸载后重装指定版本DLL load failed检查CUDA环境变量PATH是否包含bin和lib\x64路径ImportError: cannot import name Layer from keras这是keras和tensorflow版本冲突的典型表现2. 数据集标注Labelme的进阶用法很多教程只教了用Labelme画多边形但实际项目中会遇到更复杂的情况。比如我们需要标注医学图像中的病灶区域分享几个实用技巧批量标注加速方案使用快捷键W调出多边形工具CtrlZ撤销上一点对相似物体标注时复制已有标注JSON文件修改注意修改imagePath字段用labelme2coco.py脚本实现批量转换时添加--labels参数指定类别顺序处理特殊情况的代码片段# 处理重叠标注的mask生成 def blend_masks(masks): 合并多个重叠的mask blended np.zeros_like(masks[0]) for mask in masks: blended np.maximum(blended, mask) return blended3. 数据预处理比原版更高效的流程官方示例的数据转换流程需要多次文件拷贝我优化后的脚本效率提升3倍import json import os from tqdm import tqdm def convert_to_coco(json_dir, output_path): 一站式转换Labelme标注到COCO格式 annotations [] images [] categories [{id: 1, name: object}] # 修改为你的类别 for idx, json_file in enumerate(tqdm(os.listdir(json_dir))): with open(os.path.join(json_dir, json_file)) as f: data json.load(f) # 处理image信息 image_id idx 1 images.append({ id: image_id, file_name: data[imagePath], height: data[imageHeight], width: data[imageWidth] }) # 处理annotations for shape in data[shapes]: points np.array(shape[points]) x_min, y_min points.min(axis0) x_max, y_max points.max(axis0) annotations.append({ id: len(annotations) 1, image_id: image_id, category_id: 1, bbox: [x_min, y_min, x_max-x_min, y_max-y_min], area: (x_max-x_min)*(y_max-y_min), segmentation: [points.flatten().tolist()], iscrowd: 0 }) coco_format { images: images, annotations: annotations, categories: categories } with open(output_path, w) as f: json.dump(coco_format, f)4. 模型训练调参实战经验在自定义数据集上训练时这几个参数直接影响最终效果必须调整的配置项class CustomConfig(Config): NAME custom GPU_COUNT 1 IMAGES_PER_GPU 2 # 根据显存调整 NUM_CLASSES 1 2 # 背景类别数 IMAGE_MIN_DIM 512 # 输入图像最小尺寸 IMAGE_MAX_DIM 512 # 输入图像最大尺寸 RPN_ANCHOR_SCALES (32, 64, 128, 256, 512) # 根据目标大小调整 TRAIN_ROIS_PER_IMAGE 100 # 每张图的候选区域数 STEPS_PER_EPOCH 100 # 每个epoch的迭代次数 VALIDATION_STEPS 50 # 验证集迭代次数训练策略分阶段第一阶段冻结主干网络只训练头部model.train(dataset_train, dataset_val, learning_rateconfig.LEARNING_RATE, epochs20, layersheads)第二阶段解冻所有层微调model.train(dataset_train, dataset_val, learning_rateconfig.LEARNING_RATE/10, epochs40, layersall)5. 常见问题排查指南训练过程中的典型问题Loss值震荡剧烈检查学习率是否过大建议从1e-4开始确认标注数据是否存在错误可用visualize.display_instances可视化调整RPN_NMS_THRESHOLD默认0.7预测结果出现重叠框# 修改预测参数 results model.detect([image], verbose1, nms_threshold0.3, # 非极大值抑制阈值 score_threshold0.9) # 置信度阈值小目标检测效果差增大IMAGE_MIN_DIM调整RPN_ANCHOR_SCALES加入更小的anchor尺寸使用FPN特征金字塔网络的更多层级6. 模型部署优化技巧训练好的模型可以直接用于推理但实际部署时还需要优化模型压缩方案# 转换为冻结图 def freeze_model(model, output_dir): graph tf.Graph() with graph.as_default(): session tf.Session() with session.as_default(): model.keras_model.save_weights(temp.h5) model modellib.MaskRCNN(modeinference, configconfig) model.load_weights(temp.h5, by_nameTrue) output_names [ mrcnn_detection/Reshape_1, mrcnn_mask/Reshape_1 ] frozen_graph tf.graph_util.convert_variables_to_constants( session, session.graph.as_graph_def(), output_names) with open(os.path.join(output_dir, frozen_model.pb), wb) as f: f.write(frozen_graph.SerializeToString())加速推理的工程技巧使用TensorRT加速trtexec --onnxmodel.onnx --saveEnginemodel.engine --fp16批处理预测# 同时处理多张图片 images [image1, image2, image3] results model.detect(images, batch_sizelen(images))启用XLA编译config tf.ConfigProto() config.graph_options.optimizer_options.global_jit_level tf.OptimizerOptions.ON_17. 实际项目中的经验之谈在工业质检项目中我们发现几个教科书不会告诉你的实战经验数据增强的误区医学影像慎用几何变换旋转/翻转可能改变病理特征推荐使用albumentations的颜色空间增强import albumentations as A aug A.Compose([ A.RandomGamma(gamma_limit(80,120), p0.5), A.GaussNoise(var_limit(10,50), p0.3), A.CLAHE(clip_limit2.0, p0.5) ])处理类别不平衡# 修改损失函数权重 def get_weights(config): class_counts get_class_counts(dataset_train) # 自定义统计函数 median_freq np.median(list(class_counts.values())) return {cls_id: median_freq/count for cls_id, count in class_counts.items()} model.keras_model.losses[ mrcnn_class_loss].weights get_weights(config)边缘设备部署的坑树莓派上需要编译OpenCV时开启NEON加速Jetson Nano上建议使用torch2trt转换模型安卓端最好用TFLite格式converter tf.lite.TFLiteConverter.from_keras_model(model.keras_model) tflite_model converter.convert()