はじめに
機械学習に興味はあるものの、Python と数式の壁で足が止まっている方は多いのではないでしょうか。
『初めてのTensorFlow.js ―JavaScriptで学ぶ機械学習』は、その壁をほぼ取り払い、ふだん使っている JavaScript とブラウザだけで機械学習の一連の流れを体験できる入門書です。
この記事では、設計・アーキテクチャに関心のあるエンジニアの視点で、本書の構成と学べること、読むうえでの注意点をまとめます。
本文の要約ではなく、あくまで私の言葉での書評です。細部は書籍でご確認ください。
書籍情報
| 項目 | 内容 |
|---|---|
| 書名 | 初めてのTensorFlow.js ―JavaScriptで学ぶ機械学習 |
| 著者 | Gant Laborde |
| 訳者 | あんどうやすし |
| 出版社 | オライリー・ジャパン(2022年) |
| 序文 | Laurence Moroney(Google AI Lead) |
| サンプルコード | https://github.com/GantMan/learn-tfjs |
書籍ページ: https://www.oreilly.co.jp/books/9784873119939
ひとことで言うと
**「機械学習モデルを、JavaScript の世界の部品として扱えるようになる本」**です。
数学的な理論を積み上げるタイプの本ではありません。
モデルを「入力テンソルを渡すと出力テンソルが返ってくる関数」として捉え、読み込む、使う、作る、訓練する、流用する、という順に手を動かしながら進みます。
訳者まえがきにもあるとおり、扱う題材は画像中心に絞られていて、その代わり本文300ページに満たない分量で、基礎からプロジェクト完遂までを一気通貫で体験できます。
想定読者と前提
- モダンな JavaScript(async/await、ES Modules など)を普通に読める
- 機械学習はほぼ初めて、または Python 中心の入門書で挫折した経験がある
- 動くものを作りながら理解したい
逆に、数式で厳密に理解したい方や、自然言語処理・時系列など画像以外を主戦場にしたい方には物足りない部分があります。
本書自身も、数学や理論に深入りしすぎないことを明言しています。
全体構成
12章と付録で構成されています。大づかみには次の流れです。
| 章 | テーマ | 一言メモ |
|---|---|---|
| 1 | AIは魔法 | AI・機械学習・深層学習の関係と基本用語 |
| 2 | TensorFlow.js | 導入方法と、パッケージ済みモデルの初体験 |
| 3 | テンソル | データ表現の土台。メモリ管理もここで登場 |
| 4 | 画像テンソル | 画像とテンソルの相互変換と操作 |
| 5 | モデル | モデルを直接読み込み、入出力を自分で扱う |
| 6 | 高度なモデルとUI | AI と Web の組み合わせを深掘り |
| 7 | モデルの作成 | ノーコードのツールなども使ったモデル作り |
| 8 | モデルの訓練 | 回帰から非線形、訓練の監視と改善 |
| 9 | 分類モデルとデータ解析 | 実データを使った分類 |
| 10 | 画像訓練 | 畳み込みニューラルネットワークの実装 |
| 11 | 転移学習 | 既存モデルの再利用 |
| 12 | サイコロ化:最終課題 | 学んだことの総動員 |
各章の末尾に復習問題と挑戦課題が付いていて、解答は付録にまとまっています。
読み流すだけで終わらず、手を動かす前提の作りです。
設計の視点で読む5つのポイント
ここからが本題です。機能紹介というより、「設計の考え方として持ち帰れるもの」を5つ挙げます。
1. モデルは「形の決まった入出力を持つ関数」
本書全体を貫いているのは、モデルをブラックボックスの関数として扱う視点です。
内部の仕組みが分からなくても、入力テンソルの形状と出力テンソルの意味さえ押さえれば使える、という割り切りが徹底されています。
5章の前半では、盤面ゲームの訓練済みモデルを題材に、複数の入力をスタックして1回の predict でまとめて推論します。
バッチ処理が前提になっているため、入力の次元がひとつ増える点は、API 設計でいう「単一リクエストとバルクリクエスト」の関係に近いと感じました。
// 任意の数のサンプルをまとめて推論する(形状はモデルの仕様に合わせる)
const batch = tf.stack(samples); // [N, ...]
const output = model.predict(batch); // [N, クラス数]
const best = output.argMax(-1).arraySync(); // 各サンプルの最有力クラス
tf.dispose([batch, output]);
2. テンソルは自動回収されない。リソースのライフサイクルを意識する
3章で強調されるのが、テンソルは JavaScript のガベージコレクションの対象ではないという点です。
変数を手放してもテンソル本体はメモリに残り続けるため、dispose() や tf.tidy() で明示的に解放する必要があります。
const before = tf.memory().numTensors;
const result = tf.tidy(() => {
const a = tf.tensor1d([1, 2, 3]);
return a.mul(2).add(1); // 返したテンソル以外は tidy の終了時に解放される
});
console.log(tf.memory().numTensors - before); // 1
result.dispose();
ファイルハンドルやコネクションを閉じ忘れる話と同じ種類の問題です。
tidy はスコープでリソース寿命を管理する仕組みなので、RAII や using に慣れた方なら直感的に受け入れられるはずです。
ただし tidy に渡す関数は同期関数である必要があり、非同期処理を挟む場合は自分で dispose を呼ぶ必要がある、という制約も本書で明示されています。
3. レイヤーモデルとグラフモデルは「拡張性」と「最適化」のトレードオフ
5章では、読み込めるモデルに2種類あることが説明されます。
- レイヤーモデル:内部の確認や変更、分解と再構成ができる
- グラフモデル:計算が最適化されている反面、中身をいじりにくい
// 後で改造・再訓練する可能性がある → レイヤーモデル
const layers = await tf.loadLayersModel(LAYERS_MODEL_URL);
// 推論専用で、読み込みと実行を軽くしたい → グラフモデル
const graph = await tf.loadGraphModel(HUB_MODEL_URL, { fromTFHub: true });
「差し替え可能性を取るか、実行時の最適化を取るか」という、よく見る設計判断です。
11章の転移学習では、この違いが実際の選択に直結します。変更不可のグラフモデルは特徴抽出器として使うのみ、レイヤーモデルは切り取って再構成できる、という使い分けが具体例として示されます。
4. 数値は Tensor、意味づけは JavaScript に残す
3章の推薦システムの例では、ユーザー名やジャンル名などのラベルは通常の JavaScript 配列に残し、数値計算だけをテンソルに任せています。
結果が返ってきたら、インデックスをラベルに引き直して表示します。
5章でも、画像分類の出力は大量の数値ですが、topk で上位だけを取り出し、ラベル配列と突き合わせて人間が読める形にします。
「計算の世界」と「意味の世界」の境界をどこに引くかという話で、前処理・後処理を薄い層として切り出す設計に通じます。
ラベルの国際化がしやすいという副次的な利点にも触れられていました。
5. 段階的に抽象度を下げていく構成
本書は、いきなり低レベルから入りません。
- ラッパーライブラリ経由でモデルを使う(2章)
- ラッパーを外し、モデルとテンソルを直接扱う(5章)
- 自分でモデルを定義し訓練する(8章以降)
- 既存モデルを分解して再利用する(11章)
使う側から作る側へ、抽象を一段ずつ剥がしていく流れです。
どの段階で止めても実務で使える知識になっているので、「まず動かして、必要になったら内側を見る」という進め方と相性が良いと感じました。
章ごとの要点
1〜2章:全体像と最初の一歩
1章は、AI・機械学習・深層学習が入れ子の関係にあることから始まり、教師あり・教師なし・半教師あり・強化学習の4分類を簡潔に紹介します。
そのうえで、JavaScript で機械学習をやる利点が整理されます。
- ブラウザで動くのでインストール不要で共有しやすい
- オフラインでも動かせる
- データが端末の外に出ないのでプライバシー面で有利
- JavaScript が動く環境ならどこでも使える
用語集(訓練セット、テストセット、検証セット、正規化、データ拡張、特徴量化など)は、読み進める間ずっと辞書代わりになります。
2章では、npm、script タグ、Node.js 版の導入手順を確認したあと、有害表現を検出する公開モデルを動かします。
ここで印象的だったのは、モデルの出力が true/false ではなく確率で返ってきて、閾値を呼び出し側が決める、という点です。
曖昧さを含む判定を扱うときの作法が、最初の例題の時点で示されています。
script タグで読み込む場合にバージョンを固定すべき、という注意も入っています。
依存の固定はどの分野でも同じですが、入門書でさらりと触れてあるのは好印象でした。
3〜4章:テンソルという共通通貨
3章ではテンソルの作成、型、形状、階数、そして前述のメモリ管理を学びます。
テンソルは作成後に変更できないため、型を変えたいときは asType で新しいテンソルを作る、という不変性の考え方も出てきます。
行列積(matMul)で好みのジャンルを集計し、topk で並べ替える小さな推薦システムは、「テンソルで一括計算する」利点を実感できる題材でした。
4章では画像をテンソルとして扱い、手作業での構築、既存画像との相互変換、操作の仕方を学びます。
5〜7章:モデルを「使う側」の技術
5章は、本書の中でも設計色が濃い章です。
- 読み込み先を URL、
localstorage://、indexeddb://、file://で切り替えられる - 大きなモデルは約4MB単位のシャードに分割され、並列取得とキャッシュができる
- 公開ハブ上のモデルは
fromTFHubオプションで読み込む - 物体の位置を矩形で返すモデルの出力を、canvas で画像に重ねて可視化する
モデルのホスティングも現実的な話題として扱われます。
JSON と重みファイルが配信対象に含まれず、わかりにくいエラーになる、といった「つまずきポイント」が先回りして書かれているのが助かります。
6章は AI と Web の組み合わせ、7章はモデルの作成とリサイズを扱います。
この記事では詳細に踏み込みませんが、「使う」から「作る」への橋渡しの位置づけです。
8〜10章:モデルを「作る側」の技術
8章では、1層1ニューロンの線形回帰から始め、活性化関数を入れた非線形の例に進みます。
モデルの定義、compile での最適化手法と損失関数の指定、fit での訓練、という流れが最小構成で身につきます。
const xs = tf.tensor1d([0, 1, 2, 3, 4]);
const ys = tf.tensor1d([1, 4, 7, 10, 13]); // y = 3x + 1
const model = tf.sequential();
model.add(tf.layers.dense({ inputShape: [1], units: 1 }));
model.compile({ optimizer: tf.train.sgd(0.01), loss: 'meanSquaredError' });
await model.fit(xs, ys, { epochs: 500, verbose: 0 });
model.predict(tf.tensor1d([10])).print(); // 31 に近い値が出れば成功
私が良いと思ったのは、改善の打ち手が段階的に示されることです。
- 損失をコールバックで監視する
- オプティマイザを文字列ひとつで差し替える
- バッチサイズを指定して訓練時間を短縮する
- 層とニューロンを増やす
「まず測る、次に設定を変える、最後に構造を変える」という順序は、性能改善全般に通じる姿勢です。
オプティマイザが文字列で差し替え可能という点は、フレームワークを使う利点として本書でも強調されています。
9章は実データを使った分類、10章は畳み込みニューラルネットワークの実装です。
いずれも、データの準備から訓練、評価までを通して体験する章になっています。
11章:転移学習
個人的に、この本でいちばん実務寄りだと感じた章です。
大量のデータで訓練済みのモデルから特徴抽出の部分だけを借り、小さなデータで自分の用途に合わせる、という手法を3つの方向から説明します。
- 特徴量ベクトルモデル + 小さな分類器(2モデル構成)
- レイヤーモデルを切り取って再構成(1モデルに統合)
- KNN による訓練不要の分類
// 分類ヘッドの手前で切って、特徴抽出器として使う
const base = await tf.loadLayersModel(BASE_MODEL_URL);
const cut = base.getLayer(FEATURE_LAYER_NAME);
const featureModel = tf.model({ inputs: base.inputs, outputs: cut.output });
// 自分のクラス数に合わせた小さな分類器を用意する
const head = tf.sequential({
layers: [
tf.layers.flatten({ inputShape: featureModel.outputs[0].shape.slice(1) }),
tf.layers.dense({ units: 64, activation: 'relu' }),
tf.layers.dense({ units: NUM_CLASSES, activation: 'softmax' }),
],
});
チェスの駒を分類する例では、駒ごとに数枚しか画像がなくても、検証の正解率が短いエポック数で大きく伸びる様子が示されます。
データが少ない現実の課題で、既存資産を活かすというアプローチの威力が分かりやすいです。
設計の観点では、次の点が参考になりました。
- 先に追加した層だけを訓練し、特徴抽出部分は凍結しておく
- その後で凍結を解いて全体を微調整する(ファインチューニング)
- 結合は訓練のあとにする。先に結合すると過剰適合しやすい
KNN は訓練の代わりに、判定時に近傍の例を探す「怠惰学習」です。
再訓練せずにクラスを追加できる反面、例が増えるほど保持するデータも増える、というトレードオフが明記されています。
また、類似度だけで判断するので「マスクの有無」のように一点に絞った判定には不向きという指摘もあり、手法の適用範囲を考える材料になります。
12章:最終課題「サイコロ化」
白黒画像を、6面サイコロの目の並びで再現する課題です。
- サイコロの目の向きを区別して、9パターンのクラスにする
- 12×12ピクセルのパターンを手作業で用意する
- 1ピクセルずつずらす(パディングして切り出す)ことで9倍に増やす
- ずらした画像同士を重ねてさらに増やし、重複を除いて各パターン200以上にする
- 訓練したモデルを使い、画像を細かく分割してサイコロ画像で組み立て直す
データの拡張に使う「2つの画像の暗いほうを残す」処理は、本書では tf.where と tf.less で書かれています。
同じ結果になる書き方として、tf.minimum を使うこともできます。
// where(a < b, a, b) と同じ結果になる
const overlay = tf.minimum(a, b);
もうひとつ印象に残ったのは、あえて畳み込み層を使わないという判断です。
今回の判定は、パッチ内の局所的な配置そのものに強く依存するため、位置に依存しない特徴を取り出す畳み込みは不要、と説明されます。
手法ありきではなく、問題の性質からアーキテクチャを選ぶ、という設計の基本が実例で示されています。
そして、その選択が正しいかどうかは試すまで分からないので、実験で確かめる、という姿勢も一貫しています。
良かった点
- 前提が軽い:JavaScript が書ければ、環境構築の壁がほとんどありません。
- 体験の順序が良い:先に動かして成功体験を得てから、中身を理解する流れです。
- 現実的な話題が多い:モデルの配信、キャッシュ、ウォームアップ、メモリ管理など、実運用で効いてくる話が最初から出てきます。
- 復習問題と挑戦課題:章ごとに理解を確認でき、手を動かす動機になります。
- 全体を通した一貫性:章をまたいで同じ題材や考え方が再利用され、知識がつながります。
注意点
- 題材が画像中心:テキストや時系列などは主題ではありません。
- バージョン依存:サンプルは刊行当時のバージョンやツールチェーンを前提にしています。実際に動かす際は、公式ドキュメントとサンプルリポジトリの最新状態を確認すると安心です。
- 理論は最小限:損失関数や最適化の数学的背景は深追いしません。理解を深めたい場合は別の教材との併用がおすすめです。
- 一部コードは省略:章によっては要点のみの掲載で、全体はサンプルリポジトリを参照する形になります。
本書は文体がかなりくだけていて、引用やジョークも多めです。
読みやすさにつながる一方で、淡々とした技術書が好みの方には好みが分かれるかもしれません。
どんな人におすすめか
- JavaScript/TypeScript が主戦場で、機械学習を実務に取り入れてみたい方
- ブラウザやエッジで、サーバーを介さずに推論させる設計を検討している方
- 「モデルをどう組み込むか」というアーキテクチャ面に関心のある方
- Python 入門書で数式や環境構築に疲れてしまった方
おわりに
本書の最大の価値は、機械学習を「特別な専門領域」から「いつもの開発の延長」に引き寄せてくれることだと思います。
データ、モデル、入出力の契約、リソース管理、段階的な最適化といった要素は、ふだんの設計の語彙でほぼそのまま説明できます。
画像に絞ったからこそ、読み終えたあとに「自分でひとつ作りきった」という感覚を得やすい一冊です。
次の一歩として、サンプルコードを手元で動かし、各章の挑戦課題に取り組んでみることをおすすめします。
参考
- 書籍ページ(オライリー・ジャパン): https://www.oreilly.co.jp/books/9784873119939
- サンプルコード: https://github.com/GantMan/learn-tfjs
- 日本語版サポートページ: https://github.com/oreilly-japan/learn-tfjs-ja
- TensorFlow.js 公式: https://www.tensorflow.org/js