Main Content

SVM の予測用の固定小数点コード生成

この例では、サポート ベクター マシン (SVM) モデルの予測用に固定小数点 C/C++ コードを生成する方法を示します。一般的な C/C++ コード生成ワークフローと比較すると、固定小数点コードの生成には予測に必要な変数の固定小数点データ型を定義する追加の手順が必要です。generateLearnerDataTypeFcn を使用して固定小数点データ型構造体を作成し、その構造体をエントリポイント関数で loadLearnerForCoder の入力引数として使用します。コードを生成する前に固定小数点データ型を最適化することもできます。

次のフロー チャートは固定小数点のコード生成ワークフローを示します。

  1. SVM モデルに学習させます。

  2. saveLearnerForCoderを使用して、学習済みモデルを保存します。

  3. generateLearnerDataTypeFcnによって生成されるデータ型関数を使用して、予測に必要な変数の固定小数点データ型を定義します。

  4. loadLearnerForCoderと構造体の両方を使用してモデルを読み込み、関数 predict を呼び出すエントリポイント関数を定義します。

  5. (オプション) 固定小数点データ型を最適化します。

  6. 固定小数点の C/C++ コードを生成します。

  7. 生成されたコードを検証します。

手順 5 は、生成された固定小数点コードのパフォーマンスを高めるための任意の手順です。これを行うには、コードのパフォーマンスに満足するまで次の 2 つの手順を繰り返します。

  1. buildInstrumentedMex (Fixed-Point Designer)を使用して、予測用の変数の最小値と最大値を記録します。

  2. showInstrumentationResults (Fixed-Point Designer)を使用してインストルメンテーションの結果を表示します。オーバーフローとアンダーフローを回避するために (必要な場合は) 固定小数点データ型を調整し、固定小数点コードの精度を向上させます。

このワークフローでは、generateLearnerDataTypeFcn によって生成されるデータ型関数を使用して固定小数点データ型を定義します。変数のデータ型をアルゴリズムから独立させると、テストが簡単になります。データ型関数の入力引数を使用して、プログラムにより浮動小数点と固定小数点の間でデータ型を切り替えることができます。また、このワークフローは手動による固定小数点の変換ワークフロー (Fixed-Point Designer)と互換性があります。

データの前処理

census1994 データ セットを読み込みます。このデータ セットは、個人の年収が $50,000 を超えるかどうかを予測するために使用される、米国勢調査局の人口統計データから構成されています。

load census1994

年齢、労働階級、教育レベル、資本利得および損失、および 1 週間の勤務時間が与えられた従業員の給与カテゴリを予測するモデルを考えます。対象とする変数を抽出し、table を使用して保存します。

tbl = adultdata(:,{'age','education_num','capital_gain','capital_loss','hours_per_week'});

table の概要を表示します。

summary(tbl)
Variables:

    age: 32561x1 double

        Values:

            Min          17   
            Median       37   
            Max          90   

    education_num: 32561x1 double

        Values:

            Min           1   
            Median       10   
            Max          16   

    capital_gain: 32561x1 double

        Values:

            Min            0  
            Median         0  
            Max        99999  

    capital_loss: 32561x1 double

        Values:

            Min            0  
            Median         0  
            Max         4356  

    hours_per_week: 32561x1 double

        Values:

            Min           1   
            Median       40   
            Max          99   

変数のスケールは一致していません。この場合、fitcsvm の名前と値のペアの引数 'Standardize' を指定することにより、標準化されたデータ セットを使用してモデルに学習させることができます。ただし、標準化の演算を固定小数点コードに追加すると、精度が下がり、メモリ使用量が増える可能性があります。代わりに、この例に示すようにデータ セットを手動で標準化できます。この例では、最後にメモリ使用量を確認する方法についても説明しています。

固定小数点コード生成では table または categorical 配列はサポートされていません。このため、数値行列を使用して予測子データ X を定義し、logical ベクトルを使用してクラス ラベル Y を定義します。logical ベクトルはバイナリ分類問題でメモリを最も効率よく使用します。

X = table2array(tbl);
Y = adultdata.salary == '<=50K';

観測値の重み w を定義します。

w = adultdata.fnlwgt;

モデル内のサポート ベクターの数が増加するにつれ、学習済みモデルのメモリの使用量が増加します。サポート ベクターの数を減らすには、学習させる際に名前と値のペアの引数 'BoxConstraint' を使用してボックス制約の値を大きくするか、学習用にサブサンプリングされた代表データ セットを使用することができます。ボックス制約の値を大きくすると学習時間が長くなる場合があり、また、サブサンプリングされたデータ セットを使用すると学習済みモデルの精度が低下する可能性があることに注意してください。この例では、データ セットから 1000 の観測値を無作為に抽出し、サブサンプリングされたデータを学習に使用します。

rng('default') % For reproducibility
[X_sampled,idx] = datasample(X,1000,'Replace',false);
Y_sampled = Y(idx);
w_sampled = w(idx);

名前と値のペアの引数 'Weight' および 'Standardize' を使用してモデルを学習させて、加重平均および標準偏差を求めます。

tempMdl = fitcsvm(X_sampled,Y_sampled,'Weight',w_sampled,'KernelFunction','gaussian','Standardize',true);
mu = tempMdl.Mu;
sigma = tempMdl.Sigma;

学習に名前と値のペアの引数 'Cost''Prior'、または 'Weight' を使用しない場合は、関数 zscore を使用して平均と標準偏差を求めることができます。

[standardizedX_sampled,mu,sigma] = zscore(X_sampled);

mu および sigma を使用して予測子データを標準化します。

standardizedX = (X-mu)./sigma;
standardizedX_sampled = standardizedX(idx,:);

テスト データ セットを使用して学習済みモデルを検証し、インストルメント化された MEX 関数をテストできます。mu および sigma を使用してテスト データ セットを指定し、テスト予測子データを標準化します。

XTest = table2array(adulttest(:,{'age','education_num','capital_gain','capital_loss','hours_per_week'}));
standardizedXTest = (XTest-mu)./sigma;
YTest = adulttest.salary == '<=50K';

モデルの学習

バイナリ SVM 分類モデルに学習させます。

Mdl = fitcsvm(standardizedX_sampled,Y_sampled,'Weight',w_sampled,'KernelFunction','gaussian');

MdlClassificationSVM モデルです。

学習データ セットおよびテストデータ セットについて分類エラーを計算します。

loss(Mdl,standardizedX_sampled,Y_sampled) 
ans = 0.1663
loss(Mdl,standardizedXTest,YTest)
ans = 0.1905

SVM 分類器は、学習データの約 17% とテスト データの 19% を誤分類しています。

モデルの保存

saveLearnerForCoderを使用して、SVM 分類モデルを myMdl.mat というファイルに保存します。

saveLearnerForCoder(Mdl,'myMdl');

固定小数点データ型の定義

generateLearnerDataTypeFcn を使用して、SVM モデルの予測に必要な変数の固定小数点データ型を定義する関数を生成します。使用可能なすべての予測子データを使用して固定小数点データ型の現実的な範囲を取得します。

generateLearnerDataTypeFcn('myMdl',[standardizedX; standardizedXTest])

generateLearnerDataTypeFcn は関数 myMdl_datatype を生成します。関数 type を使用して myMdl_datatype.m の内容を表示します。

type myMdl_datatype.m
function T = myMdl_datatype(dt)
%MYMDL_DATATYPE Define data types for fixed-point code generation
%   
%   T = MYMDL_DATATYPE(DT) returns the data type structure T, which defines
%   data types for the variables required to generate fixed-point C/C++ code
%   for prediction of a machine learning model. Each field of T contains a
%   fixed-point object returned by fi. The input argument dt specifies the
%   DataType property of the fixed-point object. Specify dt as 'Fixed' (default)
%   for fixed-point code generation or specify dt as 'Double' to simulate
%   floating-point behavior of the fixed-point code.
%   
%   Use the output structure T as both an input argument of an entry-point
%   function and the second input argument of loadLearnerForCoder within the
%   entry-point function. For more information, see loadLearnerForCoder.
     
%   File: myMdl_datatype.m
%   Statistics and Machine Learning Toolbox Version 24.1 (Release R2024a)
%   Generated by MATLAB, 25-Jan-2024 22:06:12
     
if nargin < 1
	dt = 'Fixed';
end

% Set fixed-point math settings
fm = fimath('RoundingMethod','Floor', ...
    'OverflowAction','Wrap', ...
    'ProductMode','FullPrecision', ...
    'MaxProductWordLength',128, ...
    'SumMode','FullPrecision', ...
    'MaxSumWordLength',128);

% Data type for predictor data
T.XDataType = fi([],true,16,11,fm,'DataType',dt);

% Data type for output score
T.ScoreDataType = fi([],true,16,14,fm,'DataType',dt);

% Internal variables
% Data type of the squared distance dist = (x-sv)^2 for the Gaussian kernel G(x,sv) = exp(-dist),
% where x is the predictor data for an observation and sv is a support vector
T.InnerProductDataType = fi([],true,16,6,fm,'DataType',dt);


end

関数 myMdl_datatype は既定の語長 (16) を使用し、最大の小数部の長さを提案して、オーバーフローを回避します。これは、各変数について、既定の語長 (16) と安全余裕 (10%) に基づいています。

myMdl_datatype を使用して、固定小数点データ型を定義する構造体 T を作成します。

T = myMdl_datatype('Fixed')
T = struct with fields:
               XDataType: [0x0 embedded.fi]
           ScoreDataType: [0x0 embedded.fi]
    InnerProductDataType: [0x0 embedded.fi]

構造体 T には、関数 predict の実行に必要な名前付きの内部変数に対するフィールドが含まれています。各フィールドには、fi (Fixed-Point Designer)で返される固定小数点オブジェクトが含まれています。たとえば、予測子データの固定小数点データ型プロパティを表示します。

T.XDataType
ans = 

[]

          DataTypeMode: Fixed-point: binary point scaling
            Signedness: Signed
            WordLength: 16
        FractionLength: 11

        RoundingMethod: Floor
        OverflowAction: Wrap
           ProductMode: FullPrecision
  MaxProductWordLength: 128
               SumMode: FullPrecision
      MaxSumWordLength: 128

学習させた分類器に既定以外のスコア変換関数 (doublelogitlogitsymmetriclogit など) を使用する場合は、構造体 T を更新して、スコア変換関数を近似するルックアップ テーブルを含めることができます。例については、スコア変換を近似するためのルックアップ テーブルの使用を参照してください。

生成された関数と構造体の詳細については、データ型関数を参照してください。

エントリポイント関数の定義

以下を行う myFixedPointPredict という名前のエントリポイント関数を定義します。

  • 予測子データ X および固定小数点データ型構造体 T を受け入れる。

  • loadLearnerForCoderおよび構造体 T の両方を使用して、学習済み SVM 分類モデルの固定小数点バージョンを読み込む。

  • 読み込まれたモデルを使用してラベルとスコアを予測する。

function [label,score] = myFixedPointPredict(X,T) %#codegen
Mdl = loadLearnerForCoder('myMdl','DataType',T);
[label,score] = predict(Mdl,X);
end

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

(オプション) 固定小数点データ型の最適化

buildInstrumentedMex および showInstrumentationResults を使用して固定小数点データ型を最適化します。buildInstrumentedMex を使用して、予測のためにすべての名前付きの内部変数の最小値と最大値を記録します。showInstrumentationResults を使用してインストルメンテーションの結果を表示します。次に、その結果に基づいて、変数の固定小数点データ型のプロパティを調整します。

エントリポイント関数の入力引数の型の指定

2 行 1 列の cell 配列を使用して myFixedPointPredict の入力引数の型を指定します。

ARGS = cell(2,1);

1 番目の入力引数は予測子データです。構造体 TXDataType フィールドは予測子データの固定小数点データ型を指定します。関数cast (Fixed-Point Designer)を使用して XT.XDataType で指定された型に変換します。

X_fx = cast(standardizedX,'like',T.XDataType);

テスト データ セットは学習データ セットと同じサイズではありません。coder.typeof (MATLAB Coder)を使用して ARGS{1} を指定し、MEX 関数が可変サイズの入力を取ることができるようにします。

ARGS{1} = coder.typeof(X_fx,size(standardizedX),[1,0]);

2 番目の入力引数は構造体 T であり、これはコンパイル時の定数でなければなりません。coder.Constant (MATLAB Coder)を使用して、コード生成中に T を定数として指定します。

ARGS{2} = coder.Constant(T);

インストルメント化された MEX 関数の作成

buildInstrumentedMex (Fixed-Point Designer)を使用してインストルメント化された MEX 関数を作成します。

  • -args オプションを使用してエントリポイント関数の入力引数の型を指定します。

  • -o オプションを使用して MEX 関数の名前を指定します。

  • -histogram オプションを使用してヒストグラムを計算します。

  • -coder オプションを使用して完全なコード生成サポートを許可します。

buildInstrumentedMex myFixedPointPredict -args ARGS -o myFixedPointPredict_instrumented -histogram -coder
Code generation successful.

インストルメント化された MEX 関数のテスト

インストルメント化された MEX 関数を実行してインストルメンテーションの結果を記録します。

[labels_fx1,scores_fx1] = myFixedPointPredict_instrumented(X_fx,T);

インストルメント化された MEX 関数を複数回実行して、さまざまなテスト データ セットからの結果を記録できます。standardizedXTest を使用してインストルメント化された MEX 関数を実行します。

Xtest_fx = cast(standardizedXTest,'like',T.XDataType);
[labels_fx1_test,scores_fx1_test] = myFixedPointPredict_instrumented(Xtest_fx,T);

インストルメント化された MEX 関数の結果の表示

showInstrumentationResults (Fixed-Point Designer)を呼び出し、インストルメンテーションの結果を含むレポートを開きます。シミュレーションの最小値と最大値、推定された小数部の長さ、現在の範囲の割合、0 または正の整数ステータスを表示します。

showInstrumentationResults('myFixedPointPredict_instrumented')

X の推定された語長と小数部の長さは、構造体 TXDataType のものと同じです。

[変数] タブの をクリックして、変数のヒストグラムを表示します。

ウィンドウには、ヒストグラム、オーバーフローとアンダーフローの可能性についての情報、および変数のデータ型が含まれています。

clearInstrumentationResults (Fixed-Point Designer)を使用して結果をクリアします。

clearInstrumentationResults('myFixedPointPredict_instrumented')

インストルメント化された MEX 関数の検証

predictmyFixedPointPredict_instrumented からの出力を比較します。

[labels,scores] = predict(Mdl,standardizedX);
verify_labels1 = isequal(labels,labels_fx1)
verify_labels1 = logical
   0

isequalは、labelslabels_fx1 が等しい場合、logical 1 (true) を返します。ラベルが等しくない場合、labelslabels_fx1 の間で一致しないラベルの割合を計算します。

diff_labels1 = sum(strcmp(string(labels_fx1),string(labels))==0)/length(labels_fx1)*100
diff_labels1 = 0.1228

スコア出力間の相対的な差分の最大値を求めます。

diff_scores1 = max(abs((double(scores_fx1(:,1))-scores(:,1))./scores(:,1)))
diff_scores1 = 49.8264

固定小数点データ型の調整

記録された結果がオーバーフローまたはアンダーフローを示す場合、または生成されたコードの精度を向上させる場合、固定小数点データ型を調整できます。関数 myMdl_datatype を更新して、新しい構造体を作成して固定小数点データ型を変更し、その新しい構造体を使用してコードを生成します。関数 myMdl_datatype を更新するには、関数ファイル (myMdl_datatype.m) の固定小数点データ型を手動で変更できます。または、この例に示すように、generateLearnerDataTypeFcn を使用し、より長い語長を指定して関数を生成できます。詳細は、ヒントを参照してください。

新しいデータ型関数を生成します。生成された関数について、語長 32 と名前 myMdl_datatype2 を指定します。

generateLearnerDataTypeFcn('myMdl',[standardizedX; standardizedXTest],'WordLength',32,'OutputFunctionName','myMdl_datatype2')

myMdl_datatype2.m の内容を表示します。

type myMdl_datatype2.m
function T = myMdl_datatype2(dt)
%MYMDL_DATATYPE2 Define data types for fixed-point code generation
%   
%   T = MYMDL_DATATYPE2(DT) returns the data type structure T, which defines
%   data types for the variables required to generate fixed-point C/C++ code
%   for prediction of a machine learning model. Each field of T contains a
%   fixed-point object returned by fi. The input argument dt specifies the
%   DataType property of the fixed-point object. Specify dt as 'Fixed' (default)
%   for fixed-point code generation or specify dt as 'Double' to simulate
%   floating-point behavior of the fixed-point code.
%   
%   Use the output structure T as both an input argument of an entry-point
%   function and the second input argument of loadLearnerForCoder within the
%   entry-point function. For more information, see loadLearnerForCoder.
     
%   File: myMdl_datatype2.m
%   Statistics and Machine Learning Toolbox Version 24.1 (Release R2024a)
%   Generated by MATLAB, 25-Jan-2024 22:07:00
     
if nargin < 1
	dt = 'Fixed';
end

% Set fixed-point math settings
fm = fimath('RoundingMethod','Floor', ...
    'OverflowAction','Wrap', ...
    'ProductMode','FullPrecision', ...
    'MaxProductWordLength',128, ...
    'SumMode','FullPrecision', ...
    'MaxSumWordLength',128);

% Data type for predictor data
T.XDataType = fi([],true,32,27,fm,'DataType',dt);

% Data type for output score
T.ScoreDataType = fi([],true,32,30,fm,'DataType',dt);

% Internal variables
% Data type of the squared distance dist = (x-sv)^2 for the Gaussian kernel G(x,sv) = exp(-dist),
% where x is the predictor data for an observation and sv is a support vector
T.InnerProductDataType = fi([],true,32,22,fm,'DataType',dt);


end

関数 myMdl_datatype2 は語長 32 を指定し、オーバーフローを回避するための最大の小数部の長さを推定します。

myMdl_datatype2 を使用して、固定小数点データ型を定義する構造体 T2 を作成します。

T2 = myMdl_datatype2('Fixed')
T2 = struct with fields:
               XDataType: [0x0 embedded.fi]
           ScoreDataType: [0x0 embedded.fi]
    InnerProductDataType: [0x0 embedded.fi]

buildInstrumentedMex および showInstrumentationResults を使用して、新しいインストルメント化された MEX 関数を作成し、結果を記録し、その結果を表示します。

X_fx2 = cast(standardizedX,'like',T2.XDataType);
buildInstrumentedMex myFixedPointPredict -args {X_fx2,coder.Constant(T2)} -o myFixedPointPredict_instrumented2 -histogram -coder
Code generation successful.
[labels_fx2,scores_fx2] = myFixedPointPredict_instrumented2(X_fx2,T2);
showInstrumentationResults('myFixedPointPredict_instrumented2')

インストルメンテーション レポートを表示し、その後結果をクリアします。

clearInstrumentationResults('myFixedPointPredict_instrumented2')

myFixedPointPredict_instrumented2 を検証します。

verify_labels2 = isequal(labels,labels_fx2)
verify_labels2 = logical
   0

diff_labels2 = sum(strcmp(string(labels_fx2),string(labels))==0)/length(labels_fx2)*100
diff_labels2 = 0.0031
diff_scores2 = max(abs((double(scores_fx2(:,1))-scores(:,1))./scores(:,1)))
diff_scores2 = 2.5820

一致しないラベルの割合 diff_labels2 とスコア値の相対差分 diff_scores2 の両方が、既定の語長 (16) を使用して生成された以前の MEX 関数からの値よりも小さくなっています。

MATLAB® コードのインストルメント化による固定小数点データ型の最適化の詳細については、buildInstrumentedMex (Fixed-Point Designer)showInstrumentationResults (Fixed-Point Designer)、およびclearInstrumentationResults (Fixed-Point Designer)のリファレンス ページと例最小/最大のインストルメンテーションを使用したデータ型の設定 (Fixed-Point Designer)を参照してください。

コードの生成

codegen を使用して、エントリポイント関数のコードを生成します。予測子データ セットに可変サイズの入力を指定するのではなく、coder.typeof を使用して固定サイズの入力を指定します。生成されるコードに渡す予測子データ セットのサイズがわかっている場合は、固定サイズの入力のコードを生成するほうが、コードを簡略化するには望ましいです。

codegen myFixedPointPredict -args {coder.typeof(X_fx2,[1,5],[0,0]),coder.Constant(T2)}
Code generation successful.

codegen は、プラットフォームに依存する拡張子をもつ MEX 関数 myFixedPointPredict_mex を生成します。

生成されたコードの確認

インストルメント化された MEX 関数を検証するのと同じ方法で、関数 myFixedPointPredict_mex を検証できます。詳細については「インストルメント化された MEX 関数の検証」セクションを参照してください。

[labels_sampled,scores_sampled] = predict(Mdl,standardizedX_sampled);
n = size(standardizedX_sampled,1);
labels_fx = true(n,1);
scores_fx = zeros(n,2);
for i = 1:n
    [labels_fx(i),scores_fx(i,:)] = myFixedPointPredict_mex(X_fx2(idx(i),:),T2);
end
verify_labels = isequal(labels_sampled,labels_fx)
verify_labels = logical
   1

diff_labels = sum(strcmp(string(labels_fx),string(labels_sampled))==0)/length(labels_fx)*100
diff_labels = 0
diff_scores = max(abs((double(scores_fx(:,1))-scores_sampled(:,1))./scores_sampled(:,1)))
diff_scores = 0.0635

メモリの使用

モデルを学習させる前に手動で予測子データを標準化することをお勧めします。代わりに名前と値のペアの引数 'Standardize' を使用する場合、生成される固定小数点コードには標準化演算が含まれます。これにより、精度の低下やメモリ使用量の増加が起こる可能性があります。

スタティック ライブラリを生成すると、コード生成レポートを使用することにより、生成されたコードのメモリ使用量がわかります。-config:lib を指定してスタティック ライブラリを生成し、-report オプションを使用してコード生成レポートを生成します。

codegen myFixedPointPredict -args {coder.typeof(X_fx2,[1,5],[0,0]),coder.Constant(T2)} -o myFixedPointPredict_lib -config:lib -report

コード生成レポートの [概要] タブで [コード メトリクス] をクリックします。[関数情報] セクションには累積スタック サイズが表示されます。

'Standardized','true' で学習されたモデルのメモリ使用量を求めるには、次のコードを実行できます。

Mdl = fitcsvm(X_sampled,Y_sampled,'Weight',w_sampled,'KernelFunction','gaussian','Standardize',true);
saveLearnerForCoder(Mdl,'myMdl');
generateLearnerDataTypeFcn('myMdl',[X; XTest],'WordLength',32,'OutputFunctionName','myMdl_standardize_datatype')
T3 = myMdl_standardize_datatype('Fixed');
X_fx3 = cast(X_sampled,'like',T3.XDataType);
codegen myFixedPointPredict -args {coder.typeof(X_fx3,[1,5],[0,0]),coder.Constant(T3)} -o myFixedPointPredict_standardize_lib -config:lib -report

参考

| | (Fixed-Point Designer) | (Fixed-Point Designer) | (Fixed-Point Designer) | (MATLAB Coder) | (Fixed-Point Designer) | | (Fixed-Point Designer)

関連するトピック