メインコンテンツ

YOLOX ネットワークを使用したプリント基板の欠陥の検出

この例では、YOLOX オブジェクト検出器を使用してプリント回路基板 (PCB) の欠陥を検出、位置特定、および分類する方法を示します。

PCB には個々の電子デバイスとそれらの接続が含まれます。PCB に欠陥があると、パフォーマンスが低下したり、製品が故障したりする可能性があります。PCB の欠陥を検出することにより、製造ラインで不良 PCB を除外し、電子デバイスの高い品質を確保できます。

事前学習済み YOLOX 検出器のダウンロード

既定では、この例は、downloadTrainedNetwork 補助関数を使用して、YOLOX オブジェクト検出器 [1] の事前学習済みバージョンをダウンロードします。補助関数は、この例にサポート ファイルとして添付されています。事前学習済みのネットワークを使用することで、学習の完了を待たずに例全体を実行できます。

trainedPCBDefectDetectorNet_url = "https://ssd.mathworks.com/supportfiles/"+ ...
    "vision/data/trainedPCBDefectDetectorYOLOX.zip";
downloadTrainedNetwork(trainedPCBDefectDetectorNet_url,pwd);
load("trainedPCBDefectDetectorYOLOX.mat");

PCB 欠陥データ セットのダウンロード

この例では、PCB 欠陥データ セット [2] [3] を使用します。このデータ セットには、合成された欠陥をもつ PCB 素子に関する 1,386 個のイメージが含まれています。このデータには、6 種類の欠陥 (ホール欠損、欠け、断線、短絡、突起、残銅) が含まれています。各イメージでは、同じカテゴリに属する複数の欠陥がそれぞれ異なる場所に配置されています。データ セットには、すべてのイメージのそれぞれの欠陥について、境界ボックスと座標情報が格納されています。このデータ セットのサイズは 1.87 GB です。

データ セットの場所として dataDir を指定します。downloadPCBDefectData 補助関数を使用してデータ セットをダウンロードします。この関数は、この例にサポート ファイルとして添付されています。

dataDir = fullfile(tempdir,"PCBDefects");
downloadPCBDefectData(dataDir)

オブジェクト検出の実行

データセットからサンプル イメージを読み取ります。

sampleImage = imread(fullfile(dataDir,"PCB-DATASET-master","images", ...
    "Missing_hole","01_missing_hole_01.jpg"));
Warning: Division by zero when processing CompressedBitsPerPixel.  The value has been set to NaN.

detect関数を使用して、境界ボックス、ラベル、および各境界ボックスのクラス固有の信頼度スコアを予測します。

[bboxes,scores,labels] = detect(detector,sampleImage);

結果を表示します。

imshow(sampleImage)
showShape("rectangle",bboxes,Label=labels);
title("Predicted Defects")

学習用データの準備

イメージ データを読み取って管理するイメージ データストアを作成します。

imageDir = fullfile(dataDir,"PCB-DATASET-master","images");
imds = imageDatastore(imageDir,FileExtensions=".jpg",IncludeSubfolders=true);

XML ファイルから注釈データを読み取るファイル データストアを作成します。XML ファイルを解析して境界ボックス情報を抽出するカスタム読み取り関数を指定します。カスタム読み取り関数 readPCBDefectAnnotations は、サポート ファイルとしてこの例に添付されています。

annoDir = fullfile(dataDir,"PCB-DATASET-master","Annotations");
fds = fileDatastore(annoDir,ReadFcn=@readPCBDefectAnnotations, ...
    FileExtensions=".xml",IncludeSubfolders=true);

ラベル付けされた境界ボックス データをボックス ラベル データストアとして保存します。

annotations = readall(fds);
tbl = struct2table(vertcat(annotations{:}));
blds = boxLabelDatastore(tbl);

オブジェクト クラスの名前を categorical ベクトルとして取得します。

classNames = categories(blds.LabelData{1,2})
classNames = 6×1 cell
    {'missing_hole'   }
    {'mouse_bite'     }
    {'open_circuit'   }
    {'short'          }
    {'spur'           }
    {'spurious_copper'}

イメージ データストアとボックス ラベル データストアを組み合わせます。

ds = combine(imds,blds);

オブジェクト クラスの分布の解析

countEachLabel関数を使用して、データ セット内のクラス ラベルの分布を測定します。このデータ セット内のクラスはバランスが取れています。

countEachLabel(blds)
ans=6×3 table
         Label         Count    ImageCount
    _______________    _____    __________

    missing_hole        497        115    
    mouse_bite          492        115    
    open_circuit        482        116    
    short               491        116    
    spur                488        115    
    spurious_copper     503        116    

データの分割

データを分割する前に、グローバルな乱数の状態を既定の状態に設定して、結果のより高い再現性を確保します。

rng("default");

データ セットを学習セット、検証セット、テスト セットに分割します。イメージの総数が比較的少ないため、データの比較的大部分 (70%) を学習に割り当てます。検証用に 15% を割り当て、残りをテスト用に割り当てます。

numImages = ds.numpartitions;
numTrain = floor(0.7*numImages);
numVal = floor(0.15*numImages);

shuffledIndices = randperm(numImages);
dsTrain = subset(ds,shuffledIndices(1:numTrain));
dsVal = subset(ds,shuffledIndices(numTrain+1:numTrain+numVal));
dsTest = subset(ds,shuffledIndices(numTrain+numVal+1:end));

学習データの拡張

transform関数を、augmentDataForPCBDefectDetection 補助関数によって指定されたカスタム前処理演算と共に使用して、学習データを拡張します。この補助関数は、この例にサポート ファイルとして添付されています。augmentDataForPCBDefectDetection 関数は、入力データに以下の拡張を適用します。

  • 水平方向のランダムな反転 (鏡映)

  • 範囲 [1, 1.1] の倍率でのランダムなサイズ変更

  • 範囲 [-50, 50] ピクセルでの水平方向と垂直方向のランダムな平行移動

dsTrain = transform(dsTrain,@augmentDataForPCBDefectDetection);

YOLOX オブジェクト検出器のネットワーク アーキテクチャの定義

yoloxObjectDetector関数を使用して、YOLOX オブジェクト検出器を作成します。CSP-DarkNet-53 を使用して作成され、COCO データ セット [1] で学習させた事前学習済みネットワークをベース ネットワークとして指定します。クラス名とネットワーク入力サイズを指定します。

inputSize = [800 800 3];
detectorIn = yoloxObjectDetector("tiny-coco",classNames,InputSize=inputSize);

学習オプションの指定

関数trainingOptions (Deep Learning Toolbox)を使用してネットワーク学習オプションを指定します。SGDM ソルバーを使用して、オブジェクト検出器に最大 100 エポック学習させます。名前と値の引数 ValidationData を検証データとして指定します。学習終了時に、学習中の検証損失が最も低かったネットワークを取得するには、OutputNetwork"best-validation-loss" に設定します。

options = trainingOptions("sgdm", ...
    InitialLearnRate=5e-4, ...
    LearnRateSchedule="piecewise", ...
    LearnRateDropFactor=0.99, ...
    LearnRateDropPeriod=1, ...   
    MiniBatchSize=20, ...
    MaxEpochs=100, ...
    ExecutionEnvironment="auto", ...
    Shuffle="every-epoch", ...
    VerboseFrequency=25, ...
    ValidationFrequency=100, ...
    ValidationData=dsVal, ...
    ResetInputNormalization=false, ...
    OutputNetwork="best-validation-loss", ...
    GradientThreshold=30, ...
    L2Regularization=5e-4);

検出器の学習

検出器に学習させるには、doTraining 変数を true に設定します。trainYOLOXObjectDetector関数を使用して検出器に学習させます。

可能であれば、1 つ以上の GPU で学習を行います。GPU を使用するには、Parallel Computing Toolbox™ ライセンスと CUDA® 対応の NVIDIA® GPU が必要です。詳細については、GPU 計算の要件 (Parallel Computing Toolbox)を参照してください。学習には 24 GB のメモリを搭載した NVIDIA Titan RTX™ で約 7.5 時間を要します。

doTraining = false;
if doTraining       
    [detector,info] = trainYOLOXObjectDetector(dsTrain,detectorIn,options,"FreezeSubNetwork","none");
    modelDateTime = string(datetime("now",Format="yyyy-MM-dd-HH-mm-ss"));
    save(fullfile(tempdir,"trainedPCBDefectDetectorYoloX"+modelDateTime+".mat"), ...
        "detector");
else
    load("trainedPCBDefectDetectorYOLOX.mat");
end

検出器の評価

すべてのテスト イメージの境界ボックスを検出します。できるだけ多くのオブジェクトを検出するには、検出しきい値を低い値に設定します。これにより、検出スコア値の全範囲にわたって検出パフォーマンスを評価できます。

detectionResults = detect(detector,dsTest,Threshold=0.01);

evaluateObjectDetection関数を使用し、テスト セットの検出結果に基づいてオブジェクト検出メトリクスを計算します。

metrics = evaluateObjectDetection(detectionResults,dsTest);

各クラスの平均適合率 (AP) スコアを計算して表示します。適合率は、検出器がオブジェクトを正しく分類する能力を定量化したものです。

AP = averagePrecision(metrics);
table(classNames,AP)
ans=6×2 table
        classNames           AP   
    ___________________    _______

    {'missing_hole'   }    0.98092
    {'mouse_bite'     }     0.8571
    {'open_circuit'   }    0.94318
    {'short'          }    0.97348
    {'spur'           }    0.94269
    {'spurious_copper'}    0.88991

各欠陥オブジェクトの検出について、再現率と適合率の値を計算します。再現率は、検出器がクラスに関連するすべてのオブジェクトを検出する能力を定量化します。適合率/再現率 (PR) の曲線は、さまざまなレベルの再現率における検出器の適合率を示します。すべてのレベルの再現率で適合率が 1 になるのが理想的です。テスト データの PR 曲線をプロットします。

classNameStrings = metrics.ClassNames;
class = classNameStrings(4);
[precision, recall, ~] = precisionRecall(metrics,ClassName=class);
plot(recall{:},precision{:})
title(sprintf("Average Precision for '" + class + "' Defect: " + "%.2f",averagePrecision(metrics,ClassName=class)), interpreter="none")
xlabel("Recall")
ylabel("Precision")
grid on

オブジェクト サイズに基づく検出メトリクスの評価

指定した範囲のオブジェクト サイズに対してオブジェクト検出メトリクスを計算するmetricsByArea関数を使用して、オブジェクト サイズが検出器のパフォーマンスに及ぼす影響を調査します。サイズに基づくメトリクスを評価するには、カスタム範囲セットに基づいてオブジェクト サイズの範囲を定義できます。まず、テスト セットのオブジェクト面積分布の 33 パーセンタイルと 66 パーセンタイルのパーセンタイル境界に従って、テスト イメージの境界ボックスのサイズを小、中、大のオブジェクト サイズ カテゴリに分割します。

テスト セットのオブジェクト サイズ分布をプロットします。ここで、境界ボックスの面積はオブジェクトのサイズを定義します。

testSetObjects = dsTest.UnderlyingDatastores{2};
objectLabels = readall(testSetObjects);
boxes = objectLabels(:,1);
boxes = vertcat(boxes{:});
boxArea = prod(boxes(:,3:4),2);
histogram(boxArea)
title("Bounding Box Area Distribution")
xlabel("Box Area");
ylabel("Count")

境界ボックスの面積の範囲を定義し、metricsByArea を使用して、定義された面積の範囲のオブジェクト検出メトリクスを評価します。学習済みの検出器の平均適合率 (mAP) メトリクスは、小、中、大のオブジェクト サイズでほぼ同じパフォーマンスを発揮し、中サイズのオブジェクトではパフォーマンスがわずかに向上しています。

boxPrctileBoundaries = prctile(boxArea,100*[1/3,2/3]);
metricsByArea(metrics,[0, boxPrctileBoundaries, inf])
ans=3×4 table
       AreaRange        NumObjects    mAPOverlapAvg       mAP    
    ________________    __________    _____________    __________

         0    3573.7       144           0.84161       {[0.8416]}
    3573.7      5107       145           0.96052       {[0.9605]}
      5107       Inf       144           0.88542       {[0.8854]}

参考文献

[1] Ge, Zheng, Songtao Liu, Feng Wang, Zeming Li, and Jian Sun. "YOLOX: Exceeding YOLO Series in 2021", arXiv, August 6, 2021. https://arxiv.org/abs/2107.08430.

[2] Huang, Weibo, and Peng Wei. "A PCB Dataset for Defects Detection and Classification." Preprint, submitted January 23, 2019. https://arxiv.org/abs/1901.08204.

[3] PCB-DATASET. Accessed December 20, 2022. https://github.com/Ironbrotherstyle/PCB-DATASET.

参考

| | | | (Deep Learning Toolbox) |

トピック