#1. 初めに
こんにちわ。Electric Blue Industries Ltd.という、ITで美を追求するファンキーでマニアックなIT企業のマッツと申します。このところ投稿が疎かになったので、何かお役に立てることをと思い、TensorFlow.jsの解説を不定期することにしました。
Deep Learningをおこなううえで最もメジャーな方法のひとつが「仮想環境やコンテナを作ってTensorFlow & Python」かと思います。Google Colabと言う素晴らしい環境もありますが、「はじめの一歩」にはまだまだ敷居が高いところがあり、初学者には気軽に取り組める状況ではないように思います。
そんな中で特に初学者にオススメしたいのが「TensorFlow.js」です。TensorFlow.jsはJavaScriptでDeep Learningをおこなうためのライブラリです。機能的にはPython版のTensorFlowと基本的に足並みを揃えています(TFGANのような目新しい関数はTensorFlow.jsにはなかったりしますが)。学習に際しての敷居が低く、教育現場での利用にも有効と認識しています。メリット・デメリットは下記かと思います。
####メリット
- Webブラウザがあれば即に動作させられます
- Webページを作成する際に慣れ親しんだ人も多いJavaScriptでの記述
- PythonとJavaScriptは記述に類似性があるので思考的に同時並行が楽
- GPUの使用もブラウザのWebGL経由で行える
- ウェブサイトへの組み込みが容易でウェブコンテンツ化がしやすい
- デバイスのカメラやマイクとの連動が簡単
- TensorBoardに相当するような可視化ライブラリ「TF-VIS」もあり視覚化できる
- Pythonとの相互変換(コンバーター)も使用可能
- モデルのインポートとエクスポートも可能
####デメリット
- 日進月歩のDeepLearning業界の進化ペースに微妙にキャッチアップし遅れている
- 大規模な学習処理にはやっぱりちょっと重いかも
#2. この投稿の目的
今後に複数回の投稿に分けて、TensorFlow.orgから公開されているTensorFlow.jsを用いたチュートリアルを、非常に基本的な事項についてまで細かく詳細に説明し、何をしているのか流れを理解するお手伝いをします。狙いとしては、どのようなデータ(配列・オブジェクト・テンソル等)がどのように処理されて何が行われるのかを理解できるようになることです。
#3. Deep Learningで頻出するJavaScriptのおさらい
次回からの各チュートリアルの解説をする前に、ウェブサイトでのJavaScriptではあまり登場しない処理について言及します。これらを理解していることが解説を読む上での前提となるので、不明事項はあらかじめ学習をお願いしたいです。
###3.1. 変数と宣言(var, let, const)
JavaScriptでは変数を用いる際には使い始めに変数を宣言してから用います。宣言のタイプにはいくつかあり、ウェブサイト内で記述する場合はほぼvarしか使いませんが、Deep Learningではletやconstが多用されます。
var : 通常の変数宣言(上書き可能)
let : ifやforといったブロックスコープ内でのみ有効(上書き可能)
const : ifやforといったブロックスコープ内でのみ有効(上書き不可能)
###3.2. 非同期処理(async, await)
asyncは指定された処理を非同期で行うことを宣言し、awaitはasyncで指定された処理が実行結果を返す(コールバック)するまで待つことを宣言します。これもウェブサイト作成では滅多に出てこないものです。
###3.3. データのマッピング(map)
JavaScriptのmapは配列データに使うメソッドであり、配列データの各要素1つずつに対し指定されたコールバック処理を実行し、その結果を新しい配列として返すことが出来ます(見かけからはピンと来ないが繰り返し処理をしている状況)。なお、mapと類似するメソッドにfilterとreduceがあるが、ここでは割愛する。
// 1から5までの5つの整数を格納した、要素が5つの配列をitemsと言う名称で作成。
const items = [1, 2, 3, 4, 5];
// 上記の配列itemsの各要素について、各配列要素をvalueとしてfunctionで規定された処理をおこなう。
const result = items.map(function(value) {
//配列の各要素を2倍
return value*2;
});
// ブラウザの「開発者ツール(inspect)」のconsole画面にresultの内容が出力されます。
console.log(result);
// resultは配列となり、下記の要素が格納される
// [2, 4, 6, 8, 10]
さらに、インデックス(配列の先頭要素(0番)から何番目か)の番号も使って下記のようにオブジェクトを要素に持たせることもでき、この形式はTensorFlow.jsでのデータ処理に頻出するものです。
const items = [1, 2, 3, 4, 5];
const result = items.map(function(value, index) {
//valueとindexを含むオブジェクトを要素とする配列を生成
return {
value,
index
};
});
console.log(result);
// resultは配列となり、下記のvalueとindexを含むオブジェクトを各要素が格納される
// [{value: 1, index: 0}, {value: 2, index: 1}, {value: 3, index: 2}, {value: 4, index: 3}, {value: 5, index: 4}]
###3.4 配列の生成(Array.from)
Array.from(x)は下記のように「配列っぽい(インデックス、要素、要素数が記録された)」オブジェクトから正真正銘の配列を生成するメソッドです。
const items = {0: 11, 1: 22, 2: 33, length: 3};
const result = Array.from(items);
console.log(result);
// resultには下記の配列が格納される
// [11, 22, 33]
配列の要素にオブジェクトを格納させるべく、上記で述べたmapと併用して用いる場合は
const items = {0: 11, 1: 22, 2: 33, length: 3};
const result = Array.from(items).map((value, index) => {
return {
x: value,
y: index
}
});
console.log(result);
// resultには下記のオブジェクトを要素とする配列が格納される
// [{x: 11, y: 0}, {x: 22, y: 1}, {x: 33, y: 2}]
のように記述します。この処理はDeep Learningの学習データとテストデータの生成に頻出する処理です。
さらに、オブジェクトの中にオブジェクトが入ったような形式の配列もどきの場合は、配列もどきオブジェクトの各要素に対して処理を行わせるべくmapと合わせると下記のようにハンドルできます。
const items = {0: {alpha: 11, bravo:111}, 1: {alpha: 22, bravo: 222} , 2: {alpha: 33, bravo: 333}, length: 3};
const result = Array.from(items).map(d => {
return {
x: d.alpha,
y: d.bravo
}
});
console.log(result);
// resultは下記のオブジェクトを要素とする配列になる。
// [{x: 11, y: 111}, {x: 22, y: 222}, {x: 33, y: 333}]
Deep Learningの学習で最も難しく感じるのは、このように「配列」「オブジェクト」が入れ子になり、かつ多次元行列の要素となってデータが扱われるところかと思います。データを処理する際には「このデータはどのような構造になっているか」を確認しながら処理を理解することが、その後の汎用的なDeep Learning実施に肝要です。
#4. TensorFlow.jsで行列を扱う
##4.1. ブラウザでTensorFlow.jsが動くようにする
ブラウザ(Google Chromeの最新版を推奨)でTensorFlow.jsが動くようにするには、下記のHTMLファイルを作成し、それと同じフォルダに置いた script.js というJavaScriptを書いたファイルにJaveScriptでコードを記述することで行います(もちろんファイル名は別の名称でも良いです)。実行結果は「開発者ツール(inspect)」のコンソール画面に表示されます。
<!DOCTYPE html>
<html>
<head>
<title>TensorFlow.js Tutorial</title>
<!-- TensorFlow.jsのインポート -->
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@1.0.0/dist/tf.js"></script>
<!-- JavaScriptを書くscript.jsのインポート -->
<script type="text/javascript" src="script.js"></script>
</head>
<body></body>
</html>
##4.2. 行列を作る
下記の方法1から3は全て同じ
a =
\begin{bmatrix}
1 & 2 \\
3 & 4 \\
5 & 6
\end{bmatrix}
という行列aを作ります。
方法1:各行ごとにまとめて指定することを機械的に繰り返す。
const a = tf.tensor([[1, 2], [3, 4], [5, 6]]);
方法2:行列の要素を左上から機械的に続けて指定し、行列の形(m行 x n列)を指定する。
const shape = [3, 2];
const a = tf.tensor([1, 2, 3, 4, 5, 6], shape);
方法3:方法1と方法2の合わせ技(なお、画像や音声の学習ではデータが浮動小数になるのでint32などと明示的に指定することも多いので、これは意外と使います)。
const a = tf.tensor([[1, 2], [3, 4], [5, 6]], [3, 2], 'int32');
##4.3. 行列の変形
- reshape: 上記で作成した行列aに対して、shapeでTensorの再形成を行い行列bを生成
// 2行3列に変形
const b = a.reshape([2, 3]);
b =
\begin{bmatrix}
1 & 2 & 3 \\
4 & 5 & 6
\end{bmatrix}
- transpose: 上記で作成した行列aに対して、transposeで行列の行と列を転置して転置行列bを生成する
// 行と列を対角線を軸としてくるっとひっくり返し(転置)
const b = a.transpose();
b =
\begin{bmatrix}
1 & 3 & 5 \\
2 & 4 & 6
\end{bmatrix}
##4.4. 行列の要素を得る
- arraySyncで行列aの各行を要素とする配列を得る
b = a.arraySync();
b =
\begin{bmatrix}
\begin{bmatrix}
1 & 2
\end{bmatrix} ,
\begin{bmatrix}
3 & 4
\end{bmatrix} ,
\begin{bmatrix}
5 & 6
\end{bmatrix}
\end{bmatrix}
- dataSyncで行列aの各値を要素とする配列を得る
b = a.dataSync();
b =
\begin{bmatrix}
1, 2, 3, 4, 5, 6
\end{bmatrix}
##4.5. 行列の算術計算
- 行列xの各要素の二乗を得る
const x = tf.tensor([1, 2, 3, 4]);
const y = x.square();
y =
\begin{bmatrix}
1, 4, 9, 16
\end{bmatrix}
ということで、今後のTensorFlow.jsの詳細解説を前に、今回はベースとなるJavaScriptのおさらいまでをしました。空いた時間をうまく使って、シュウイチくらいでは続きを投稿していく予定です。どうぞよろしくお願いします。
追伸: Machine Learning Tokyoと言うMachine Learningの日本最大のグループに参加しています。作業系の少人数会合を中心に顔を出しています。基本的に英語でのコミュニケーションとなっていますが、能力的にも人間的にもトップレベルの素晴らしい方々が参加されておられるので、機会がありましたら参加されることをオススメします。