Main Content

分類およびコード生成用の System object

この例では、学習済み分類モデルを使用して数字のイメージを分類する MATLAB® System object™ から C コードを生成する方法を示します。また、Simulink® での分類に System object を使用する方法も示します。MATLAB 関数ではなく System object を使用する利点は、System object の方が大量のストリーミング データの処理に適しているということです。詳細については、System object とはを参照してください。

この例は、HOG 特徴を使用した数字の分類 (Computer Vision Toolbox)の代替ワークフローであるイメージ分類用のコードの生成をベースとしています。

データの読み込み

digitimages を読み込みます。

load digitimages.mat

imagesuint16 整数の 28 x 28 x 3000 の配列です。各ページは数字のラスター イメージです。各要素はピクセル強度です。対応するラベルは 3000 行 1 列の数値ベクトル Y に格納されています。詳細については、コマンド ラインで Description を入力してください。

観測値の個数と予測子変数の個数を格納します。データの 20% ホールドアウトを指定するデータ分割を作成します。学習セットとテスト セットのインデックスをデータ分割から抽出します。

rng(1); % For reproducibility
n = size(images,3);
p = numel(images(:,:,1));
cvp = cvpartition(n,'Holdout',0.20);
idxTrn = training(cvp);
idxTest = test(cvp);

データの再スケーリング

各イメージ内で範囲が区間 [0,1] になるようにピクセル強度を再スケーリングします。具体的には、pij がイメージ i 内のピクセル強度 j であるとします。イメージ i について、次の式を使用してすべてのピクセル強度を再スケーリングします。

pˆij=pij-minj(pij)maxj(pij)-minj(pij).

X = double(images);

for i = 1:n
    minX = min(min(X(:,:,i)));
    maxX = max(max(X(:,:,i)));
    X(:,:,i) = (X(:,:,i) - minX)/(maxX - minX);
end

データの形状変更

コードを生成する場合、学習用の予測子データは数値変数の table または数値行列に格納されていなければなりません。

データの形状を行列に変更して、予測子変数を列に、イメージを行に対応させます。reshape は列単位で要素を処理するので、結果を転置します。

X = reshape(X,[p,n])';

分類モデルの学習と最適化

学習観測値に基づいて、SVM バイナリ学習器による ECOC モデルとランダム フォレストを交差検証します。5 分割交差検証を使用します。

ECOC モデルについて、予測子の標準化を指定し、ECOC の符号化設計と SVM のボックス制約に対して分類誤差を最適化します。次の値の組み合わせをすべて探索します。

  • ECOC の符号化設計については、1 対 1 と 1 対他を使用します。

  • SVM のボックス制約については、0.1 から 100 の範囲にある対数間隔の 3 つの値を使用します。すべてのモデルについて、5 分割の交差検証済み誤分類率を格納します。

coding = {'onevsone' 'onevsall'};
boxconstraint = logspace(-1,2,3);
cvLossECOC = nan(numel(coding),numel(boxconstraint)); % For preallocation

for i = 1:numel(coding)
    for j = 1:numel(boxconstraint)
        t = templateSVM('BoxConstraint',boxconstraint(j),'Standardize',true);
        CVMdl = fitcecoc(X(idxTrn,:),Y(idxTrn),'Learners',t,'KFold',5,...
            'Coding',coding{i});
        cvLossECOC(i,j) = kfoldLoss(CVMdl);
        fprintf('cvLossECOC = %f for model using %s coding and box constraint=%f\n',...
            cvLossECOC(i,j),coding{i},boxconstraint(j))
    end
end
cvLossECOC = 0.058333 for model using onevsone coding and box constraint=0.100000
cvLossECOC = 0.057083 for model using onevsone coding and box constraint=3.162278
cvLossECOC = 0.050000 for model using onevsone coding and box constraint=100.000000
cvLossECOC = 0.120417 for model using onevsall coding and box constraint=0.100000
cvLossECOC = 0.121667 for model using onevsall coding and box constraint=3.162278
cvLossECOC = 0.127917 for model using onevsall coding and box constraint=100.000000

ランダム フォレストについて、{32,33,...,3m} という数列の値を使用して最大分割数を変化させます。m は、3mn - 1 を超えない値です。無作為な予測子の選択を再現するため、'Reproducible',true を指定します。

n = size(X,1);
m = floor(log(n - 1)/log(3));
maxNumSplits = 3.^(2:m);
cvLossRF = nan(numel(maxNumSplits));
for i = 1:numel(maxNumSplits)
    t = templateTree('MaxNumSplits',maxNumSplits(i),'Reproducible',true);
    CVMdl = fitcensemble(X(idxTrn,:),Y(idxTrn),'Method','bag','Learners',t,...
        'KFold',5);
    cvLossRF(i) = kfoldLoss(CVMdl);
    fprintf('cvLossRF = %f for model using %d as the maximum number of splits\n',...
        cvLossRF(i),maxNumSplits(i))
end
cvLossRF = 0.319167 for model using 9 as the maximum number of splits
cvLossRF = 0.192917 for model using 27 as the maximum number of splits
cvLossRF = 0.066250 for model using 81 as the maximum number of splits
cvLossRF = 0.015000 for model using 243 as the maximum number of splits
cvLossRF = 0.013333 for model using 729 as the maximum number of splits
cvLossRF = 0.009583 for model using 2187 as the maximum number of splits

各アルゴリズムについて、誤分類率が最小になるハイパーパラメーターのインデックスを決定します。

minCVLossECOC = min(cvLossECOC(:))
minCVLossECOC = 0.0500
linIdx = find(cvLossECOC == minCVLossECOC,1);
[bestI,bestJ] = ind2sub(size(cvLossECOC),linIdx);
bestCoding = coding{bestI}
bestCoding = 
'onevsone'
bestBoxConstraint = boxconstraint(bestJ)
bestBoxConstraint = 100
minCVLossRF = min(cvLossRF(:))
minCVLossRF = 0.0096
linIdx = find(cvLossRF == minCVLossRF,1);
[bestI,bestJ] = ind2sub(size(cvLossRF),linIdx);
bestMNS = maxNumSplits(bestI)
bestMNS = 2187

ランダム フォレストの方が、交差検証された誤分類率が小さくなります。

学習データを使用して ECOC モデルとランダム フォレストに学習をさせます。最適なハイパーパラメーターの組み合わせを与えます。

t = templateSVM('BoxConstraint',bestBoxConstraint,'Standardize',true);
MdlECOC = fitcecoc(X(idxTrn,:),Y(idxTrn),'Learners',t,'Coding',bestCoding);
t = templateTree('MaxNumSplits',bestMNS);
MdlRF = fitcensemble(X(idxTrn,:),Y(idxTrn),'Method','bag','Learners',t);

テスト標本イメージ用の変数を作成し、学習済みモデルを使用してテスト標本のラベルを予測します。

testImages = X(idxTest,:);
testLabelsECOC = predict(MdlECOC,testImages);
testLabelsRF = predict(MdlRF,testImages);

ディスクへの分類モデルの保存

MdlECOCMdlRF は予測分類モデルですが、これらをコード生成用に準備する必要があります。saveLearnerForCoder を使用して MdlECOCMdlRF を現在の作業フォルダーに保存します。

saveLearnerForCoder(MdlECOC,'DigitImagesECOC');
saveLearnerForCoder(MdlRF,'DigitImagesRF');

予測用 System object の作成

ECOC モデル用とランダム フォレスト用に 1 つずつ、以下を行う 2 つの System object を作成します。

  • loadLearnerForCoder を使用して、以前に保存された学習済みモデルを読み込む。

  • step メソッドにより逐次予測を行う。

  • 入力データのサイズ変更を抑止する。

  • 倍精度のスカラー出力を強制する。

type ECOCClassifier.m % Display contents of ECOCClassifier.m file
classdef ECOCClassifier < matlab.System
    % ECOCCLASSIFIER Predict image labels from trained ECOC model
    %
    % ECOCCLASSIFIER loads the trained ECOC model from
    % |'DigitImagesECOC.mat'|, and predicts labels for new observations
    % based on the trained model.  The ECOC model in
    % |'DigitImagesECOC.mat'| was cross-validated using the training data
    % in the sample data |digitimages.mat|.

    properties(Access = private)
        CompactMdl % The compacted, trained ECOC model
    end
        
    methods(Access = protected)
        
        function setupImpl(obj)
            % Load ECOC model from file
            obj.CompactMdl = loadLearnerForCoder('DigitImagesECOC');
        end
        
        function y = stepImpl(obj,u)
            y = predict(obj.CompactMdl,u);
        end
        
        function flag = isInputSizeMutableImpl(obj,index)
            % Return false if input size is not allowed to change while
            % system is running
            flag = false;
        end
        
        function dataout = getOutputDataTypeImpl(~)
            dataout = 'double';
        end
        
        function sizeout = getOutputSizeImpl(~)
            sizeout = [1 1];
        end
    end
end
type RFClassifier.m % Display contents of RFClassifier.m file
classdef RFClassifier < matlab.System
    % RFCLASSIFIER Predict image labels from trained random forest
    %
    % RFCLASSIFIER loads the trained random forest from
    % |'DigitImagesRF.mat'|, and predicts labels for new observations based
    % on the trained model.  The random forest in |'DigitImagesRF.mat'|
    % was cross-validated using the training data in the sample data
    % |digitimages.mat|.

    properties(Access = private)
        CompactMdl % The compacted, trained random forest
    end
        
    methods(Access = protected)
        
        function setupImpl(obj)
            % Load random forest from file
            obj.CompactMdl = loadLearnerForCoder('DigitImagesRF');
        end
        
        function y = stepImpl(obj,u)
            y = predict(obj.CompactMdl,u);
        end
        
        function flag = isInputSizeMutableImpl(obj,index)
            % Return false if input size is not allowed to change while
            % system is running
            flag = false;
        end
        
        function dataout = getOutputDataTypeImpl(~)
            dataout = 'double';
        end
        
        function sizeout = getOutputSizeImpl(~)
            sizeout = [1 1];
        end
    end
end

メモ: このページの右上にあるボタンをクリックしてこの例を MATLAB® で開くと、MATLAB® で例のフォルダーが開きます。このフォルダーには、この例で使用しているファイルが含まれています。

System object の基本的な要件については、基本的な System object の定義を参照してください。

コード生成用の予測関数の定義

predictDigitECOCSO.m および predictDigitRFSO.m という名前の 2 つの MATLAB 関数を定義します。これらの関数では以下を行います。

  • コード生成命令 %#codegen を含める。

  • X に相応するイメージ データを受け入れる。

  • System object ECOCClassifier および RFClassifier をそれぞれ使用してラベルを予測する。

  • 予測したラベルを返す。

type predictDigitECOCSO.m % Display contents of predictDigitECOCSO.m file
function label = predictDigitECOCSO(X) %#codegen
%PREDICTDIGITECOCSO Classify digit in image using ECOC Model System object
%   PREDICTDIGITECOCSO classifies the 28-by-28 images in the rows of X
%   using the compact ECOC model in the System object ECOCClassifier, and
%   then returns class labels in label.
classifier = ECOCClassifier;
label = step(classifier,X); 
end
type predictDigitRFSO.m % Display contents of predictDigitRFSO.m file
function label = predictDigitRFSO(X) %#codegen
%PREDICTDIGITRFSO Classify digit in image using RF Model System object
%   PREDICTDIGITRFSO classifies the 28-by-28 images in the rows of X
%   using the compact random forest in the System object RFClassifier, and
%   then returns class labels in label.
classifier = RFClassifier;
label = step(classifier,X); 
end

MEX ファイルへの MATLAB 関数のコンパイル

codegen を使用して、テスト標本精度が優れている予測関数をコンパイルして MEX ファイルにします。引数 -args を使用してテスト セットのイメージを指定します。

if(minCVLossECOC <= minCVLossRF)
    codegen predictDigitECOCSO -args testImages    
else   
    codegen predictDigitRFSO -args testImages
end
Code generation successful.

生成された MEX ファイルが MATLAB 関数と同じ予測を行うことを確認します。

if(minCVLossECOC <= minCVLossRF)
    mexLabels = predictDigitECOCSO_mex(testImages);
    verifyMEX = sum(mexLabels == testLabelsECOC) == numel(testLabelsECOC)    
else   
    mexLabels = predictDigitRFSO_mex(testImages);
    verifyMEX = sum(mexLabels == testLabelsRF) == numel(testLabelsRF)    
end
verifyMEX = logical
   1

verifyMEX1 です。これは、生成された MEX ファイルおよび対応する MATLAB 関数で予測が同じであることを示します。

Simulink で System object を使用したラベルの予測

テスト セットのイメージをフレーム単位で表示するビデオ ファイルを作成します。

v = VideoWriter('testImages.avi','Uncompressed AVI');
v.FrameRate = 1;
open(v);
dim = sqrt(p)*[1 1];
for j = 1:size(testImages,1)
    writeVideo(v,reshape(testImages(j,:),dim));
end
close(v);

RGB イメージをグレースケールに変換してから、生成されたピクセル強度を値が区間 [0,1] に収まるようにスケーリングする、scalePixelIntensities.m という名前の関数を定義します。

type scalePixelIntensities.m % Display contents of scalePixelIntensities.m file
function x = scalePixelIntensities(imdat)
%SCALEPIXELINTENSITIES Scales image pixel intensities
%   SCALEPIXELINTENSITIES scales the pixel intensities of the image such
%   that the result x is a row vector of values in the interval [0,1].
imdat = rgb2gray(imdat);

minimdat = min(min(imdat));
maximdat = max(max(imdat));
x = (imdat - minimdat)/(maximdat - minimdat);
end

Simulink® モデル slexClassifyAndDisplayDigitImages.slx を読み込みます。

SimMdlName = 'slexClassifyAndDisplayDigitImages';
open_system(SimMdlName);

Simulink® モデルが Figure に表示されます。シミュレーションの最初に、From Multimedia File ブロックでテスト セットのイメージのビデオ ファイルが読み込まれます。ビデオの各イメージについて以下を行います。

  • From Multimedia File ブロックで、イメージを変換して 28 行 28 列のピクセル強度の行列を出力します。

  • Process Data ブロックで、scalePixelIntensities.m を使用してピクセル強度をスケーリングし、1 行 784 列のスケーリングされた強度のベクトルを出力します。

  • Classification Subsystem ブロックで、与えられた処理済みイメージ データからラベルを予測します。このブロックでは、分類誤差が最小になる System object を選択します。この場合、ランダム フォレストが選択されます。このブロックでは、倍精度スカラーのラベルを出力します。

  • Data Type Conversion ブロックで、ラベルを int32 スカラーに変換します。

  • Insert Text ブロックで、予測されたラベルを現在のフレームに埋め込みます。

  • To Video Display ブロックで、注釈付きのフレームを表示します。

モデルをシミュレートします。

sim(SimMdlName)

600 個のテスト セットのイメージおよびその予測が素早く表示されます。最後のイメージがビデオ表示に残ります。代わりに [ステップを進める] ボタンをクリックすると、1 つずつ予測を生成して対応するイメージと共に表示できます。

Simulink® Coder™ のライセンスもある場合、Simulink® の slexClassifyAndDisplayDigitImages.slx またはコマンド ラインから slbuild (Simulink) を使用して C コードを生成できます。詳細は、モデル用 C コードの生成 (Simulink Coder)を参照してください。

参考

| | |

関連するトピック