硅珠检测——四分类监督学习

一、引言

对于制造业而言,质量检测是非常重要的一环。传统的人工检测方法费时费力,且误检率较高。随着机器视觉的发展与应用,机器检测的高效、可靠性愈来愈受到制造业的青睐。
具体到制笔行业,由于其零配件较小,人工质检的误检率尤为严重,寻求机器视觉的解决方案更为迫切。
本文从笔帽硅珠检测方向出发,探讨应用深度学习算法的可行性。

二、现有方案

目前采用的是传统机器视觉方案,主要采用的方法是预先设置标准模板,然后将采集到的图片选择感兴趣区域进行模板匹配,计算与模板的相似度;计算采集图像中心位置的平均亮度;比如相似度大于0.9,以及平均亮度在160~200之间的判定为良品,其余的判定为不良品。

2.1、现有方案存在的问题

1.基于算法局限性,对环境要求较高,如相机像素精度、光照强度等,导致项目成本升高
2.硅珠分为米黄、白色两种;每种硅珠对应多种颜色笔帽;笔帽又分为透明及不透明。以上多种情况组合,同光源下采集到的图像亮度偏差较大,模板不具有泛化能力,故需要针对不同情况分别设定模板,并进行参数微调,生产时需要对应选取。操作方式不够友好。
3.由于实际生产环境的复杂性,工台震动、光源强度变化是必须考虑的问题。现有方案在遇到相机镜头发生偏移,或者光源变化的情况下,就需要重新设定模板,进行校准。后期维护成本较高。

三、基于深度学习算法的任务分析

硅珠检测项目作为一个监督学习分类任务,首先是要确定分为几类。
根据产线实际生产情况考察,将它作为一个四分类任务,labels分别为为:

  • 0——良品
  • 1——有硅珠但歪斜的不良品
  • 2——无硅珠的不良品
  • 3——空座

四、分类模型常用评判指标

先看下混淆矩阵的概念,以下:

  • P代表真实正(阳性)样本(Positive)
  • N代表真实负(阴性)样本(Negative)
  • P ^ \widehat{P} P 代表预测正(阳性)样本
  • N ^ \widehat{N} N 代表预测负(阴性)样本
\ P ^ \widehat{P} P N ^ \widehat{N} N
PTPFN
NFPTN
  • 真阳性(True Positive,TP)代表实际为正样本,且预测结果也为正样本
  • 假阳性(False Positive,FP)代表实际为负样本,但预测结果为正样本
  • 假阴性(False Negative,FN)代表实际为正样本,但预测结果为负样本
  • 真阴性(True Negative,TN)代表实际为负样本,且预测结果也为负样本

如何评判一个模型是好是坏,是非常重要的。对于分类任务常见的评价指标有准确率(Accuracy)、精确率(Precision)、召回率(Recall)、F1 score、ROC曲线(Receiver Operating Characteristic Curve)等。

  • 准确率(Accuracy)
    分类正确的样本占总样本的比例

A c c u r a c y = T P + T N T P + F P + F N + T N Accuracy = \frac{TP + TN}{TP + FP + FN + TN} Accuracy=TP+FP+FN+TNTP+TN

  • 精确率(Precision):预测为正样本中实际也为正样本的比例

P r e c i s i o n = T P T P + F P Precision=\frac{TP}{TP+FP} Precision=TP+FPTP

  • 召回率(Recall):实际正样本中被预测为正样本的比例

R e c a l l = T P T P + F N Recall=\frac{TP}{TP+FN} Recall=TP+FNTP

  • F1-Score:精确率和召回率的调和平均值

F 1 = 2 ∗ P r e c i s i o n ∗ R e c a l l P r e c i s i o n + R e c a l l F1=\frac{2*Precision*Recall}{Precision+Recall} F1=Precision+Recall2PrecisionRecall

Precision体现了模型对负样本的区分能力,Precision越高,模型对负样本的区分能力越强;
Recall体现了模型对正样本的识别能力,Recall越高,模型对正样本的识别能力越强。
F1 score是两者的综合,F1 score越高,说明模型越稳健。F1 Score最大值是1,最小值为0.
F1 Score是 Fβ的特殊形式:

F β = ( 1 + β 2 ) ∗ P r e c i s i o n ∗ R e c a l l ( β 2 ∗ P r e c i s i o n ) + R e c a l l F_{β}=(1+β^2)*\frac{Precision*Recall}{(β^2*Precision)+Recall} Fβ=(1+β2)(β2Precision)+RecallPrecisionRecall

当β=1时,即为F1 Score,同等对待Precision和Recall;
当β=0时,F0=Precision;
当β= + ∞ +\infty +时,F + ∞ +\infty +=Recall;
可以调用sklearn.metrics库中的accuracy_score,recall_score,precision_score,f1_score

五、本项目评判指标

由于在工业生产中,精确率与召回率同等重要,故评判标准选择F1 Score

六、数据预处理

6.1、灰度图转化

由于本例中工业相机采集到的图像为灰度图(既channel为1,对比RGB图像,channel图像为3),故无需进行灰度图转化。(灰度图转化是常用的图像增强方法之一,提出图像颜色对结果的影响,同时也可以降低计算成本)

6.2、图像剪裁

基予产线的现有设备情况,工业相机采集到的图像一次包含两支笔帽,故处理数据的时候需要将这两只笔帽裁剪出来,分别进行预测

6.3、图像Resize

由于采集到的原图size为5496*3692,19.2MB大小,size过大,对算力要求过高,故需要将裁剪后的图像resize为模型要求的输入大小(一般为244*244);之所以选择裁剪后再resize,这样既一定程度上保证了图像精度,又降低了对算力的要求

6.4、数据集清洗及标注

使用百度EasyDL的EasyData对数据集进行清洗(去重),以及标注

可选的数据清洗手段包括去近似、去模糊、剪裁、旋转、镜像,这里我们只选择去近似,由于使用场景单一,故大多数图像比较相似,故先相似度在0.9以上的仅保留一张

可以看到出入的数据集有2646张图片,经过清洗后还有2625张图片,去重效果不明显,说明选取的相似度偏大,故重新清洗

相似度设为0.7,去重后还剩余1943张图片;
相似度设为0.5,去重后还剩余1498张图片;
相似度设为0.4,去重后还剩余898张图片;
相似度设为0.3,去重后还剩余178张图片;
最终选用相似度0.4时,处理后的数据集

使用EasyData在线标注功能进行图像标注分类

标注完成之后使用数据导出功能将数据集导出到本地,待使用

6.5、数据分布平衡化

在学术中,使用的大部分数据集都是平衡的。但实际中完全不是这样。还是以医学为例,将一个健康的人误诊为患病带来的伤害相对的有限的,但是将一个患病的人误诊为健康将使病人得不到治疗而延误病情,可能为此付出生命的代价。
同理,由于目前制笔工艺精良,不良品很少,不良品的采样就变得困难。但不良品的流出又是企业不能接受的。那么就需要采取一定措施处理数据不平衡的问题。
采样数据分布图如下:

针对采样不平衡问题,本例中通过修改图像亮度值进行数据集扩充

采用PIL.ImageEnhance模块的Brightness类进行亮度调整

from PIL import Image,ImageEnhance

img =Image.open(image_path)
# 调整图像亮度为原图的0.5倍,并生成一张新的图像
img1 = ImageEnhance.Brightness(img).enhance(0.5)

以下图像在原图基础上,将亮度值改为0.2,0.4,0.6,0.8,1.2,1.4,1.6,1.8倍,进行对比

针对label为1、3的数据集进行过采样,将数据集扩充8倍(亮度分别为原图像的0.2,0.4,0.6,0.8,1.2,1.4,1.6,1.8倍);
针对label为2的数据集进行过采样,将数据集扩充16倍(亮度分别为原图像的0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1.1,1.2,1.3,1.4,15,1.6,17,1.8倍);
扩充完数据集分布如下所示:

6.6、数据集划分

按照8:2比例随机划分训练集和验证集,并确保各label划分前后比例保持一致;
处理后的数据集文件夹tree结构:

七、训练平台及模型选取

  • 本项目基于百度AIStudio平台进行模型训练,默认Python3.7版本,PaddlePaddle1.7.1
  • 本项目采用的预训练模型为旷视科技在2018年提出的轻量级图像分类模型shufflenet_v2_imagenet,该模型通过pointwise group convolutionchannel shuffle两种方式,在保持精度的同时大大降低了模型的计算量。该PaddleHub Module结构为ShuffleNet V2,基于ImageNet-2012数据集训练,接受输入图片大小为224 x 224 x 3
  • 需要安装paddlehub
pip install paddlehub

八、模型训练

8.1、加载预训练模型

module_shufflenet_v2 = hub.Module(name="shufflenet_v2_imagenet")

8.2、加载数据集

class BeadsDataset(BaseCVDataset):	
   def __init__(self):	
       # 数据集存放位置
       
       self.dataset_dir = "dataset"
       super(BeadsDataset, self).__init__(
           base_path=self.dataset_dir,
           train_list_file="train_list.txt",
           validate_list_file="validate_list.txt",
           test_list_file="test_list.txt",
           label_list_file="label_list.txt",
           )
dataset = BeadsDataset()

8.3、生成数据读取器

data_reader = hub.reader.ImageClassificationReader(
    image_width=module_shufflenet_v2.get_expected_image_width(),        #预训练模型对应的图片尺寸宽度
    image_height=module_shufflenet_v2.get_expected_image_height(),      #预训练模型对应的图片尺寸高度
    images_mean=module_shufflenet_v2.get_pretrained_images_mean(),      #预训练模型对应的图片均值
    images_std=module_shufflenet_v2.get_pretrained_images_std(),        #预训练模型对应的图片方差
    dataset=dataset)

8.4、优化策略和运行配置

config = hub.RunConfig(
    log_interval=20,                                            # 进度日志打印间隔,默认每10个step打印一次;
    eval_interval=50,                                           # 模型评估的间隔,默认每10个step评估一次验证集;
    use_cuda=False,                                             # 是否使用GPU训练,默认为False;
    checkpoint_dir="shufflenet_v2_finetune",                    # 模型checkpoint保存路径;
    num_epoch=4,                                                # Fine-tune的轮数;
    batch_size=16,                                              # 训练的批大小,如果使用GPU,请根据实际情况调整batch_size;
    strategy=hub.finetune.strategy.DefaultFinetuneStrategy())   # Fine-tune优化策略;

PaddleHub提供了许多优化策略,如AdamWeightDecayStrategyULMFiTStrategyDefaultFinetuneStrategy等,详细信息参见策略

8.5、组建Finetune Task

# 获取module的上下文环境,包括输入和输出的变量,以及Paddle Program;
input_dict, output_dict, program = module_shufflenet_v2.context(trainable=True)
# 从输出变量中找到特征图提取层feature_map;
feature_map = output_dict["feature_map"]
# 模型的输入tensor的顺序
feed_list = [input_dict["image"].name]

task = BeadsClassifierTask(
    data_reader=data_reader,                                # 定义的数据读取器
    feed_list=feed_list,                                    # 模型的输入tensor
    feature=feature_map,                                    # 特征提取向量
    num_classes=dataset.num_labels,                         # 数据集对应的labels
    metrics_choices=['f1','precision','recall','acc'],      # 评估指标,这里f1_score作为模型训练的评估指标,precision、recall、acc只做计算,不参与模型训练
    config=config)                                          # 配置策略

需要说明的是BeadsClassifierTask是自定义的Task类,重写了_calculate_metrics方法,添加了precision、recall评估指标

from sklearn.metrics import accuracy_score,f1_score,recall_score,precision_score

......

for metric in self.metrics_choices:
    if metric == "acc":
        avg_acc = acc_sum / run_examples
        scores["acc"] = avg_acc
    elif metric == "f1":
        f1 = f1_score(all_labels,all_infers,average='weighted')
        # f1 = calculate_f1_np(all_infers, all_labels)
        scores["f1"] = f1
    elif metric == "matthews":
        matthews = matthews_corrcoef(all_infers, all_labels)
        scores["matthews"] = matthews
    elif metric == "precision":
        precision = precision_score(all_labels,all_infers,average='weighted')
        scores["precision"] = precision
    elif metric == "recall":
        recall = recall_score(all_labels,all_infers,average='weighted')
        scores["recall"] = recall
    else:
        raise ValueError("Not Support Metric: \"%s\"" % metric)

8.6、迁移学习Finetune

task.finetune_and_eval()

finetune之后会生成shufflenet_v2_finetune文件夹,里面的best_model既为模型训练中f1 score得分最高时对应的checkpoint

九、预测

9.1、Funetune后的模型转化为PaddleHub Module

  • 新建必要目录与文件
    创建一个finetuned_model_to_module的目录,并在finetuned_model_to_module目录下分别创建__init__.pymodule.py,其中:
文件名用途
__init__.py空文件
module.py主模块,提供Module的实现代码
ckpt文件利用PaddleHub Fine-tune得到的ckpt文件夹,其中必须包含best_model文件
(将上一步生成的shufflenet_v2_finetune文件夹移动到此目录下即可)
  • 编写Module处理代码(module.py)
import os
import sys
sys.path.append('../')
from Silicon_beads_task import BeadsClassifierTask

import numpy as np
from paddlehub.common.logger import logger
from paddlehub.module.module import moduleinfo, serving
import paddlehub as hub

# cpu有几核就设置几个
# 如果有GPU资源可以设置CUDA
os.environ['CPU_NUM'] = str(4)

'''
'继承了hub.Module的类存在,该类负责实现预测逻辑,并使用moduleinfo填写基本信息。
'当使用hub.Module(name="shufflenet_v2_finetune")加载Module时,PaddleHub会自动创建Shufflenet_V2_Finetuned的对象并返回
'
'''
@moduleinfo(
    name="Silicon_beads_quality_inspection",
    version="1.0.0",
    summary="Silicon Beads Quality Inspection which was fine-tuned based shufflenet_v2_imagenet.",
    author="anonymous",
    author_email="",
    type="cv/imageClassification_model")
class BeadsFinetuned(hub.Module):
    '''
    '行类的初始化不能使用默认的__init__接口,而是应该重载实现_initialize接口。
    '对象默认内置了directory属性,可以直接获取到Module所在路径。
    '使用Fine-tune保存的模型预测时,无需加载数据集Dataset,即Reader中的dataset参数可为None
    '''
    def _initialize(self,
                    ckpt_dir="shufflenet_v2_finetune",
                    num_class=4,
                    num_epoch=4,
                    log_interval=20,
                    eval_interval=50,
                    use_gpu=False,
                    batch_size=16):
        # 对象默认内置了directory属性,可以直接获取到Module所在路径。
        self.ckpt_dir = os.path.join(self.directory, ckpt_dir)
        self.num_class = num_class

        self.params_path = os.path.join(self.ckpt_dir, 'best_model')
        if not os.path.exists(self.params_path):
            logger.error(
                "%s doesn't contain the best_model file which saves the best parameters as fietuning."
            )
            exit()

        # Load Paddlehub shufflenet_v2_imagenet pretrained model
        self.module = hub.Module(name="shufflenet_v2_imagenet")
        input_dict, output_dict, program = self.module.context(trainable=True)
        # self.vocab_path = self.module.get_vocab_path()

        # For shufflenet_v2_imagenet, it use sub-word to tokenize chinese sentence
        # If not ernie tiny, sp_model_path and word_dict_path should be set None
        reader = hub.reader.ImageClassificationReader(
            image_width=self.module.get_expected_image_width(),
            image_height=self.module.get_expected_image_height(),
            images_mean=self.module.get_pretrained_images_mean(),
            images_std=self.module.get_pretrained_images_std(),
            dataset=None)

        # Setup runing config for PaddleHub Finetune API
        config = hub.RunConfig(
            use_cuda=use_gpu,
            num_epoch=num_epoch,
            checkpoint_dir=self.ckpt_dir,
            batch_size=batch_size,
            log_interval=log_interval,
            eval_interval=eval_interval,
            strategy=hub.finetune.strategy.DefaultFinetuneStrategy())

        img = input_dict["image"]
        feature_map = output_dict["feature_map"]
        feed_list = [img.name]

        metrics_choices = ['f1','precision','recall','acc']

        # Define a classfication finetune task by PaddleHub's API
        self.cls_task = BeadsClassifierTask(
            data_reader=reader,
            feature=feature_map,
            feed_list=feed_list,
            num_classes=self.num_class,
            config=config,
            metrics_choices=metrics_choices)

    # 如果不需要提供PaddleHub Serving部署预测服务,则可以不需要加上serving修饰
    # @serving
    def predict(self, data, return_result=False, accelerate_mode=True):
        """
        Get prediction results
        """
        run_states = self.cls_task.predict(
            data=data,
            return_result=return_result,
            accelerate_mode=accelerate_mode)
        results = [run_state.run_results for run_state in run_states]
        prediction = []
        for batch_result in results:
            # get predict index
            batch_result = np.argmax(batch_result, axis=2)[0]
            batch_result = batch_result.tolist()
            prediction += batch_result
        return prediction

9.2、调用自定义Moudle进行预测

  • 调用方法一
    cmd进入finetuned_model_to_module所在目录,执行:
    hub install finetuned_model_to_module
    安装成功后有如下显示:
    Successfully installed Silicon_beads_quality_inspection
import paddlehub as hub

beads_finetuned = hub.Module("Silicon_beads_quality_inspection")
data = ['E:/pycharm_workspace/Silicon_beads_quality_inspection/dataset/test/3/1.png',
        'E:/pycharm_workspace/Silicon_beads_quality_inspection/dataset/test/3/2.png',
        'E:/pycharm_workspace/Silicon_beads_quality_inspection/dataset/test/3/3.png',
        'E:/pycharm_workspace/Silicon_beads_quality_inspection/dataset/test/3/4.png']

predictions = beads_finetuned.predict(data=data)
for index, text in enumerate(data):
    print("%s\tpredict=%s" % (data[index][0], predictions[index]))
  • 调用方法二

    直接通过Hub.Module(directory=...)加载
beads_finetuned = hub.Module(directory='finetuned_model_to_module')
data = ['E:/pycharm_workspace/Silicon_beads_quality_inspection/dataset/test/3/1.png']

predictions = beads_finetuned.predict(data=data)
for index, text in enumerate(data):
    print("%s\tpredict=%s" % (data[index][0], predictions[index]))
  • 调用方法三

    finetuned_model_to_module作为路径加到环境变量中,直接加载ERNIETinyFinetuned对象.(此方法笔者没有验证)
export PYTHONPATH=finetuned_model_to_module:$PYTHONPATH
from finetuned_model_to_module.module import BeadsFinetuned
data = ['E:/pycharm_workspace/Silicon_beads_quality_inspection/dataset/test/3/1.png']

predictions = beads_finetuned.predict(data=data)
for index, text in enumerate(data):
    print("%s\tpredict=%s" % (data[index][0], predictions[index]))

十、参考资料

1.【数字图像处理系列二】亮度、对比度、饱和度、锐化、分辨率
2.【ImageEnhance】+【PIL模块】如何利用python实现图像增强
3.深度学习中数据集分布不平衡问题的解决方法
4.图像分类以及模型库
5.markdown中公式编辑教程
6.有道云Markdown输入数学公式
7.PaddleHub适配自定义数据完成FineTune
8.PaddleHub 图像分类
9.PaddleHub: 自定义Task
10.PaddleHub超参优化——图像分类
11.Fine-tune保存的模型如何转化为一个PaddleHub Module

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值