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.
参考
yoloxObjectDetector | trainYOLOXObjectDetector | detect | evaluateObjectDetection | trainingOptions (Deep Learning Toolbox) | transform
トピック
- オブジェクト検出のための YOLOX 入門
- オブジェクト検出器の選択
- MATLAB による深層学習 (Deep Learning Toolbox)
- 事前学習済みの深層ニューラル ネットワーク (Deep Learning Toolbox)

