以下は、「ml5-examples/p5js/FeatureExtractor/FeatureExtractor_Image_Regression」で公開されているサンプルの解説です。
次のリンクをクリックすると、このサンプルの動作が確認できます。
「MobileNetによる特徴抽出を使用した画像回帰サンプル」
上記ページに書かれている操作を行うと、赤い矩形が頭の動きについてきて、右やセンター、左に動くようになります。さて、これは何を意味しているのでしょう??? いや、その前に回帰とはいったい何のことなのでしょう?
「3_1_1:MobileNetによる特徴抽出を使用した画像分類。p5.jsを使用」で見た分類は、簡単に言うと、与えられたデータと正解(ラベル)の関係性を見つけて、未知のデータはどのラベルに分けられるかを言い当てることです。
これに対して回帰は、与えられたデータの関係性を見つけて、未知のデータとの関係性を数値で言い当てることです。たとえば最高気温とビールの売上本数のデータからその関係性を求め、計測していない最高気温の日の売上本数を予測する方法が回帰です(「5_1 エクセルでビール売上数を予測する」)。分類と異なり、予測は数値で表されます。
この「MobileNetによる特徴抽出を使用した画像回帰サンプル」で求めるのは、スライダの値0.5と頭が真ん中にある画像(の数値)、スライダの値0と頭が左端にある画像、スライダの値1と頭が右端にある画像の関係性です。
関係性を求める訓練を終えたモデルは、未知のデータ(カメラの前で頭を真ん中か左、右に置いたときの画像)に対して、予測の数値を出力します。それを赤い矩形の描画位置とスライダの値に設定すると、頭の動きに合わせて矩形とスライダのつまみが動いているように見えます。
HTML
<div id="videoContainer"></div>
<h6><span id="modelStatus">ベースモデルの読み込み中...</span> | <span id="videoStatus">ビデオの読み込み中...</span></h6>
<p>
<input type="range" name="slider" id="slider" min="0.01" max="1.0" step="0.01" value="0.5">
</p>
<br>
<p>
<button id="addSample">サンプルを追加</button>
<p><span id="amountOfSamples">0</span> サンプル画像</p>
</p>
<br/>
<p>
<button id="train">訓練</button><span id="loss"></span></p>
<br/>
<p>
<button id="buttonPredict">推測を開始</button>
<br>
</p>
<script src="sketch.js"></script>
sketch.js
let featureExtractor;
let regressor;
let video;
let loss;
let slider;
let samples = 0;
let positionX = 140;
function setup() {
// ドキュメント内にcanvas要素を作成し、縦横サイズをピクセル単位で設定する。
// https://p5js.org/reference/#/p5/createCanvas
createCanvas(340, 280);
// video要素を作成する。
// Webカメラからのオーディオ/ビデオフィードを含む、新しいHTML5 video要素を作成する。
// https://p5js.org/reference/#/p5/createCapture
video = createCapture(VIDEO);
// 現在の要素を隠す。実際には、スタイルのdisplay:noneを設定している。
// https://p5js.org/reference/#/p5.Element/hide
video.hide();
// MobileNetから特徴を抽出する。
featureExtractor = ml5.featureExtractor('MobileNet', modelReady);
// その特徴を使って新しい回帰を作成し、使用したいvideoを与える。
// .regression(?video, ?callback)
// https://ml5js.org/docs/FeatureExtractor
regressor = featureExtractor.regression(video, videoReady);
// UIボタンを作成する。
setupButtons();
}
// setup()の後すぐ呼び出され、プログラムが終了するかnoLoop()が呼び出されるまで、ブロック内のコード行をずっと実行する
// https://p5js.org/reference/#/p5/draw
function draw() {
// p5.js canvasにイメージを描く。
// image(img, x, y, [width], [height])
// https://p5js.org/reference/#/p5/image
image(video, 0, 0, 340, 280);
// ストローク(枠線)の描画を無効にする。
// https://p5js.org/reference/#/p5/noStroke
noStroke();
// シェイプ塗りに使用するカラーを設定する。
// https://p5js.org/reference/#/p5/fill
fill(255, 0, 0);
// 画面に矩形を描く。
// rect(x, y, w, h)
// https://p5js.org/reference/#/p5/rect
rect(positionX, 120, 50, 50);
}
// モデルがロードされたときに呼び出される関数
function modelReady() {
select('#modelStatus').html('モデルが読み込まれた');
}
// ビデオがロードされたときに呼び出される関数
function videoReady() {
select('#videoStatus').html('ビデオの準備完了');
}
// 現在のフレームを推測
function predict() {
regressor.predict(gotResults);
}
// UIボタンを作成する便利関数
function setupButtons() {
slider = select('#slider');
// [サンプルを追加]ボタンがクリックされたら、
select('#addSample').mousePressed(function() {
// .addImage(label, ?callback)
// https://ml5js.org/docs/FeatureExtractor
// スライダの値を、イメージに関連付けるラベルとして追加
// 回帰の場合、labelは数値である必要がある。
regressor.addImage(slider.value());
select('#amountOfSamples').html(samples++);
});
// [訓練]ボタン
select('#train').mousePressed(function() {
// .train(callback)
// 開始点としてモデルの元の特徴を用いて、与えられた画像とラベルでモデルを再訓練する。
// https://ml5js.org/docs/FeatureExtractor
regressor.train(function(lossValue) {
if (lossValue) {
loss = lossValue;
select('#loss').html('損失: ' + loss);
}
else {
select('#loss').html('訓練終了 最終的な損失: ' + loss);
}
});
});
// [推測]ボタン
select('#buttonPredict').mousePressed(predict);
}
// 結果を表示
function gotResults(err, result) {
if (err) {
console.error(err);
}
// ある範囲から別の範囲に数値を再配置する
// map(value, start1, stop1, start2, stop2, [withinBounds])
// https://p5js.org/reference/#/p5/map
positionX = map(result, 0, 1, 0, width);
slider.value(result);
predict();
}