AIツール作成に大量のデータからコードテスト用データ分離


前書き

今はやりのAI(機械学習やディープラーニング)のためツール作成するとき、よくある苦労の一つはコードテストである。その原因は、モデルの精度確認のため大量のデータ(以下、「全体データ」と呼ぶ)が用意されているからである。
AI用ツール作成において、コードテスティングの必要性が出てくる場面を考えると、例えば、新規ハイパーパラメータの導入、ネットワーク構成変更、転移学習における新規タイプの学習済みネットワークの導入うなどは勿論、その他にも、例えば、ロス関数のカスタム化、学習データのサンプリング、一部ラベルの除外または合体、Ensemble(複数のモデルの合同適用)など、様々なケースがある。ただし、どれにしても、コードを書いた後、そのテスティングのみの時は、特に学習精度ではなく、むしろ、変更したコードに何らかのバグや漏れがあるか、非対応性が発生しているかなどを確認したいだけが一般的な目標になる。その時、単体やシステムテストでいちいち全体データを回すより、そのほんの一部のみを回して動作確認することが時間節約のために望ましい。
そのために、例えば、全体データの数万枚画像より数百枚だけをコードテスト(学習や検証ではありません)のためにピックアップし、「コードテスト用データ」を作ればいいですが、全体データの画像にも定期的なアップデートや変更が発生するのがよくあることであり、そのアップデートや変更に伴い、コードテスト用データに使う数百枚の画像もアップデートしておくことが必要となる。例えば、全体データに新しいラベルに該当するデータが加わった場合、そのラベルに関係するコードのテストのために、コードテスト用データにその新規ラベルに該当する画像も数枚入れておく必要性が出てくる。
このように数百万枚の画像から数百枚画像をラベルごとのバランスよくピックアップすること、また、定期的にその更新を行うことのため、簡単なルーチンがあれば生活が楽になることは言うまでもない。
本投稿では、MATLABを使って簡単にそのようなUtilityの作り方を紹介する。
※ここでは画像の代わりにデータファイルも同じ考え方で使える。

環境

MATLABのR2019bバージョン
Windows10
MATLAB Toolbox: Image Processing Toolbox

プログラムのワークフロー

全体データよりコピー対象画像を選択し、コードテスト用データとしてそれを別の場所にコピーするのみの非常に簡単なワークフローになる。
1.ユーザーによる入力
全体画像データの場所やコードテスト用データの保存先(例えば、フォルダーの絶対パス)、何割(例えば、1%)をテストに使うかなどのパラメータを指定。

%元データのフォルダー
orgFldr = uigetdir('','元データのフォルダー:');
%保存先フォルダー
dstFldr = uigetdir('','保存先フォルダー:');
%何割をピックアップするかを指定 (例えば、この例では、2%)
perc = 2;

2.コピー対象画像選択
MATLABの場合、ImageDatastore関数で楽にできるようになっているため、助かる。
各々のラベルのバランスを保ちながら一定割合の画像(またはデータ)ファイルを選択できる。

%Image datastoreの作成(すべてのファイル名が入ります)
imds = imageDatastore(orgFldr,...
    'IncludeSubfolders',    true,...
    'LabelSource',          'foldernames');
% 指定したパーセントに合わせてランダムにファイル選択
N   = numel(imds.Files); %全体画像データの数
SN  = round(N*perc/100); %コードテスト用データの数 
sInd= randperm(N,SN);%ランダムにSN個分のインデックスを取得
orgFile = imds.Files(sInd);%コピー対象データの元ファイルパス
dstLabl = imds.Labels(sInd);%コードテスト用データのラベル
lblStrs = unique(dstLabl);%カテゴリー

3.ファイルのコピー
文字列処理で、各々の画像ファイル名などを変えず直上のフォルダーパスのみを変えて、画像をコピーする。このフォルダーのパスを変更するため文字列処理とセルデータ型に一発で適用できるcellfun()関数を効率用句活用できる。

% まずは、それぞれのカテゴリーのフォルダー作成
for k = 1 : numel(lblStrs)
    thisFldr= fullfile(dstFldr,string(lblStrs(k)));
    if ~exist(thisFldr,'dir')
        mkdir(thisFldr);%既にフォルダーがない場合のみ新規作成
    end
end

%ファイルのコピー(MATLABのcellfun関数を利用して簡単に実装)
dstFile = cellfun(@(x) strrep(x,orgFldr,dstFldr), orgFile,...
    'UniformOutput',false);%コピー対象データの保存先へのファイルパス
%ファイルを一括コピー
cellfun(@copyfile,orgFile,dstFile)

活用方法

ここで紹介したコードを関数またはスクリプトにまとめておき、必要に応じて実行すれば、すぐコードテスト用のデータ群を活用できる。