1. Hinweis
Die Verwendung von TensorFlow.js-Modellen hat in den letzten Jahren exponentiell zugenommen. Viele JavaScript-Entwickler möchten vorhandene hochmoderne Modelle neu trainieren, damit sie mit benutzerdefinierten Daten funktionieren, die für ihre Branche einzigartig sind. Wenn Sie ein vorhandenes Modell (oft als Basismodell bezeichnet) für einen ähnlichen, aber anderen Bereich verwenden, spricht man von Transfer Learning.
Der Lerntransfer bietet viele Vorteile gegenüber einem komplett leeren Modell. Sie können bereits erworbenes Wissen aus einem zuvor trainierten Modell wiederverwenden und benötigen weniger Beispiele für das neue Element, das Sie klassifizieren möchten. Außerdem ist das Training oft deutlich schneller, da nur die letzten Schichten der Modellarchitektur neu trainiert werden müssen und nicht das gesamte Netzwerk. Aus diesem Grund eignet sich Transfer Learning sehr gut für die Webbrowserumgebung, in der die Ressourcen je nach Gerät variieren können, aber auch direkter Zugriff auf die Sensoren besteht, um Daten einfach zu erfassen.
In diesem Codelab erfahren Sie, wie Sie eine Webanwendung von Grund auf neu erstellen und die beliebte Google-Website Teachable Machine nachbilden. Auf der Website können Sie eine funktionale Web-App erstellen, mit der jeder Nutzer ein benutzerdefiniertes Objekt anhand von nur wenigen Beispielbildern von seiner Webcam erkennen kann. Die Website ist bewusst minimal gehalten, damit Sie sich auf die Aspekte des maschinellen Lernens in diesem Codelab konzentrieren können. Wie bei der ursprünglichen Teachable Machine-Website gibt es jedoch viel Spielraum, um Ihre vorhandenen Webentwicklerkenntnisse zur Verbesserung der Nutzerfreundlichkeit einzusetzen.
Voraussetzungen
Dieses Codelab richtet sich an Webentwickler, die mit vorgefertigten TensorFlow.js-Modellen und der grundlegenden API-Nutzung vertraut sind und mit Transfer Learning in TensorFlow.js beginnen möchten.
- Für dieses Lab werden grundlegende Kenntnisse von TensorFlow.js, HTML5, CSS und JavaScript vorausgesetzt.
Wenn Sie noch keine Erfahrung mit Tensorflow.js haben, empfehlen wir Ihnen, zuerst diesen kostenlosen Kurs für Anfänger zu absolvieren. Für diesen Kurs sind keine Vorkenntnisse im Bereich maschinelles Lernen oder TensorFlow.js erforderlich. Sie lernen alles, was Sie wissen müssen, in kleineren Schritten.
Lerninhalte
- Was TensorFlow.js ist und warum Sie es in Ihrer nächsten Webanwendung verwenden sollten.
- So erstellen Sie eine vereinfachte HTML-/CSS-/JS-Webseite, die der Benutzeroberfläche von Teachable Machine entspricht.
- Verwendung von TensorFlow.js zum Laden eines vortrainierten Basismodells, insbesondere MobileNet, zum Generieren von Bildfunktionen, die beim Transfer Learning verwendet werden können.
- So erheben Sie Daten von der Webcam eines Nutzers für mehrere Datenklassen, die Sie erkennen möchten.
- So erstellen und definieren Sie ein mehrschichtiges Perzeptron, das die Bildmerkmale verwendet und lernt, neue Objekte anhand dieser Merkmale zu klassifizieren.
Los gehts…
Voraussetzungen
- Für die folgenden Schritte ist ein Glitch.com-Konto empfehlenswert. Alternativ können Sie auch eine Webhosting-Umgebung verwenden, in der Sie selbst Änderungen vornehmen und die Sie selbst ausführen können.
2. Was ist TensorFlow.js?

TensorFlow.js ist eine Open-Source-Bibliothek für maschinelles Lernen, die überall ausgeführt werden kann, wo JavaScript ausgeführt werden kann. Sie basiert auf der ursprünglichen TensorFlow-Bibliothek, die in Python geschrieben wurde, und zielt darauf ab, diese Entwicklerumgebung und diese APIs für das JavaScript-Ökosystem neu zu erstellen.
Wo kann sie verwendet werden?
Da JavaScript portabel ist, können Sie jetzt in einer Sprache schreiben und problemlos maschinelles Lernen auf allen folgenden Plattformen ausführen:
- Clientseitig im Webbrowser mit nativem JavaScript
- Serverseitig und sogar auf IoT-Geräten wie Raspberry Pi mit Node.js
- Desktop-Apps mit Electron
- Native mobile Apps mit React Native
TensorFlow.js unterstützt auch mehrere Back-Ends in jeder dieser Umgebungen (die tatsächlichen Hardware-basierten Umgebungen, in denen es ausgeführt werden kann, z. B. die CPU oder WebGL). Ein „Backend“ bedeutet in diesem Zusammenhang nicht eine serverseitige Umgebung. Das Backend für die Ausführung kann beispielsweise clientseitig in WebGL sein, um die Kompatibilität zu gewährleisten und die Ausführung zu beschleunigen. Derzeit unterstützt TensorFlow.js Folgendes:
- WebGL-Ausführung auf der Grafikkarte (GPU) des Geräts: Dies ist die schnellste Methode, um größere Modelle (über 3 MB) mit GPU-Beschleunigung auszuführen.
- WebAssembly-Ausführung (WASM) auf der CPU: Damit wird die CPU-Leistung auf allen Geräten verbessert, auch auf älteren Smartphones. Diese Methode eignet sich besser für kleinere Modelle (unter 3 MB), die aufgrund des Aufwands für das Hochladen von Inhalten auf einen Grafikprozessor mit WASM auf der CPU schneller ausgeführt werden können als mit WebGL.
- CPU-Ausführung: Der Fallback, wenn keine der anderen Umgebungen verfügbar ist. Diese Methode ist die langsamste der drei, steht Ihnen aber immer zur Verfügung.
Hinweis:Sie können eines dieser Backends erzwingen, wenn Sie wissen, auf welchem Gerät Sie die Ausführung durchführen, oder Sie können TensorFlow.js die Entscheidung überlassen, wenn Sie dies nicht angeben.
Clientseitige Superkräfte
Die Ausführung von TensorFlow.js im Webbrowser auf dem Clientcomputer kann mehrere Vorteile haben, die es wert sind, berücksichtigt zu werden.
Datenschutz
Sie können Daten auf dem Clientcomputer trainieren und klassifizieren, ohne Daten an einen Webserver eines Drittanbieters zu senden. Dies kann erforderlich sein, um lokale Gesetze wie die DSGVO einzuhalten oder um Daten zu verarbeiten, die der Nutzer auf seinem Gerät behalten und nicht an Dritte senden möchte.
Geschwindigkeit
Da Sie keine Daten an einen Remote-Server senden müssen, kann die Inferenz (die Klassifizierung der Daten) schneller erfolgen. Noch besser: Sie haben direkten Zugriff auf die Sensoren des Geräts, z. B. Kamera, Mikrofon, GPS und Beschleunigungsmesser, wenn der Nutzer Ihnen den Zugriff gewährt.
Reichweite und Skalierung
Mit einem Klick kann jeder auf der Welt einen Link anklicken, den Sie ihm senden, die Webseite in seinem Browser öffnen und Ihre Inhalte nutzen. Sie benötigen keine komplexe serverseitige Linux-Einrichtung mit CUDA-Treibern und vielem mehr, um das System für maschinelles Lernen zu verwenden.
Kosten
Da keine Server erforderlich sind, müssen Sie nur für ein CDN bezahlen, um Ihre HTML-, CSS-, JS- und Modelldateien zu hosten. Die Kosten für ein CDN sind viel geringer als die Kosten für einen Server, der rund um die Uhr läuft (möglicherweise mit einer Grafikkarte).
Serverseitige Funktionen
Durch die Nutzung der Node.js-Implementierung von TensorFlow.js werden die folgenden Funktionen ermöglicht.
Volle CUDA-Unterstützung
Auf der Serverseite müssen Sie zur Beschleunigung der Grafikkarte die NVIDIA CUDA-Treiber installieren, damit TensorFlow mit der Grafikkarte arbeiten kann (im Gegensatz zum Browser, der WebGL verwendet – keine Installation erforderlich). Mit vollständiger CUDA-Unterstützung können Sie jedoch die Low-Level-Funktionen der Grafikkarte voll ausschöpfen, was zu schnelleren Trainings- und Inferenzzeiten führt. Die Leistung ist mit der Python-TensorFlow-Implementierung vergleichbar, da beide dasselbe C++-Backend verwenden.
Modellgröße
Bei hochmodernen Modellen aus der Forschung arbeiten Sie möglicherweise mit sehr großen Modellen, die mehrere Gigabyte groß sind. Diese Modelle können derzeit aufgrund der Einschränkungen der Arbeitsspeichernutzung pro Browser-Tab nicht im Webbrowser ausgeführt werden. Um diese größeren Modelle auszuführen, können Sie Node.js auf Ihrem eigenen Server mit den Hardwareanforderungen verwenden, die für die effiziente Ausführung eines solchen Modells erforderlich sind.
IoT
Node.js wird auf beliebten Einplatinencomputern wie dem Raspberry Pi unterstützt. Das bedeutet, dass Sie TensorFlow.js-Modelle auch auf solchen Geräten ausführen können.
Geschwindigkeit
Node.js ist in JavaScript geschrieben, was bedeutet, dass es von der Just-in-Time-Kompilierung profitiert. Das bedeutet, dass Sie bei der Verwendung von Node.js oft eine Leistungssteigerung feststellen werden, da es zur Laufzeit optimiert wird, insbesondere für die Vorverarbeitung. Ein gutes Beispiel dafür ist diese Fallstudie, in der beschrieben wird, wie Hugging Face mit Node.js die Leistung seines Modells für die Verarbeitung natürlicher Sprache verdoppelt hat.
Nachdem Sie nun die Grundlagen von TensorFlow.js, die Ausführungsumgebung und einige Vorteile kennen, können wir mit der praktischen Anwendung beginnen.
3. Lerntransfer
Was genau ist Lerntransfer?
Beim Lerntransfer wird bereits erworbenes Wissen genutzt, um etwas anderes, aber Ähnliches zu lernen.
Wir Menschen tun das ständig. Ihr Gehirn enthält einen ganzen Lebenszyklus an Erfahrungen, die Sie nutzen können, um neue Dinge zu erkennen, die Sie noch nie zuvor gesehen haben. Sehen wir uns zum Beispiel diese Weide an:

Je nachdem, wo Sie sich auf der Welt befinden, haben Sie diese Art von Baum möglicherweise noch nie gesehen.
Wenn ich Sie jedoch frage, ob auf dem neuen Bild unten Weiden zu sehen sind, können Sie sie wahrscheinlich ziemlich schnell erkennen, auch wenn sie aus einem anderen Blickwinkel aufgenommen wurden und sich leicht von dem Originalbild unterscheiden, das ich Ihnen gezeigt habe.

Sie haben bereits eine Reihe von Neuronen in Ihrem Gehirn, die wissen, wie man baumartige Objekte erkennt, und andere Neuronen, die gut darin sind, lange gerade Linien zu finden. Sie können dieses Wissen wiederverwenden, um schnell eine Weide zu klassifizieren. Eine Weide ist ein baumähnliches Objekt mit vielen langen, geraden, vertikalen Ästen.
Wenn Sie beispielsweise ein Machine-Learning-Modell haben, das bereits für eine bestimmte Aufgabe trainiert wurde, z. B. für die Bilderkennung, können Sie es für eine andere, aber ähnliche Aufgabe wiederverwenden.
Das ist auch mit einem fortschrittlichen Modell wie MobileNet möglich. MobileNet ist ein sehr beliebtes Forschungsmodell, das die Bilderkennung für 1.000 verschiedene Objekttypen durchführen kann. Das Modell wurde mit einem riesigen Dataset namens ImageNet trainiert, das Millionen von Bildern mit Labels enthält – von Hunden bis hin zu Autos.
In dieser Animation sehen Sie die große Anzahl von Ebenen im MobileNet V1-Modell:

Während des Trainings hat dieses Modell gelernt, gemeinsame Features zu extrahieren, die für alle 1.000 Objekte wichtig sind. Viele der Features auf niedrigerer Ebene, die es zur Identifizierung solcher Objekte verwendet, können auch nützlich sein, um neue Objekte zu erkennen, die es noch nie zuvor gesehen hat. Schließlich ist alles nur eine Kombination aus Linien, Texturen und Formen.
Sehen wir uns eine herkömmliche CNN-Architektur (Convolutional Neural Network, ähnlich wie MobileNet) an und wie mit Transfer Learning dieses trainierte Netzwerk genutzt werden kann, um etwas Neues zu lernen. Das Bild unten zeigt die typische Modellarchitektur eines CNN, das in diesem Fall darauf trainiert wurde, handgeschriebene Ziffern von 0 bis 9 zu erkennen:

Wenn Sie die vortrainierten unteren Ebenen eines vorhandenen trainierten Modells (siehe links) von den Klassifizierungsebenen am Ende des Modells (siehe rechts, manchmal auch als Klassifizierungskopf des Modells bezeichnet) trennen könnten, könnten Sie die unteren Ebenen verwenden, um Ausgabefeatures für ein beliebiges Bild auf Grundlage der Originaldaten zu erstellen, mit denen das Modell trainiert wurde. Hier sehen Sie dasselbe Netzwerk ohne den Klassifizierungskopf:

Wenn das neue Objekt, das Sie erkennen möchten, auch solche Ausgabefunktionen nutzen kann, die das vorherige Modell gelernt hat, besteht eine gute Chance, dass sie für einen neuen Zweck wiederverwendet werden können.
Im obigen Diagramm wurde dieses hypothetische Modell mit Ziffern trainiert. Möglicherweise lässt sich das, was über Ziffern gelernt wurde, auch auf Buchstaben wie a, b und c anwenden.
Sie können jetzt einen neuen Klassifikations-Head hinzufügen, der stattdessen a, b oder c vorhersagt, wie hier gezeigt:

Die unteren Ebenen sind eingefroren und werden nicht trainiert. Nur der neue Klassifizierungskopf wird aktualisiert, um aus den Funktionen zu lernen, die vom vortrainierten, zerlegten Modell auf der linken Seite bereitgestellt werden.
Dieser Vorgang wird als Transfer Learning bezeichnet und ist das, was Teachable Machine im Hintergrund macht.
Da das mehrschichtige Perzeptron nur ganz am Ende des Netzwerks trainiert werden muss, geht das viel schneller, als wenn Sie das gesamte Netzwerk von Grund auf trainieren müssten.
Aber wie können Sie auf Unterabschnitte eines Modells zugreifen? Das erfahren Sie im nächsten Abschnitt.
4. TensorFlow Hub – Basismodelle
Geeignetes Basismodell auswählen
Für komplexere und beliebte Forschungsmodelle wie MobileNet können Sie TensorFlow Hub aufrufen und dann nach Modellen filtern, die für TensorFlow.js geeignet sind und die MobileNet v3-Architektur verwenden, um Ergebnisse wie die hier gezeigten zu erhalten:

Einige dieser Ergebnisse sind vom Typ „Bildklassifizierung“ (detailliert oben links in jedem Modellkarten-Ergebnis), andere vom Typ „Bild-Feature-Vektor“.
Diese Ergebnisse für Bild-Featurevektoren sind im Grunde die vorab zerlegten Versionen von MobileNet, mit denen Sie die Bild-Featurevektoren anstelle der endgültigen Klassifizierung abrufen können.
Solche Modelle werden oft als „Basismodelle“ bezeichnet. Sie können sie dann wie im vorherigen Abschnitt beschrieben für das Transfer Learning verwenden, indem Sie einen neuen Klassifizierungskopf hinzufügen und ihn mit Ihren eigenen Daten trainieren.
Als Nächstes sollten Sie prüfen, in welchem TensorFlow.js-Format das Modell für ein bestimmtes Basismodell veröffentlicht wird. Wenn Sie die Seite für eines dieser Feature-Vektor-MobileNet v3-Modelle öffnen, sehen Sie in der JS-Dokumentation, dass es sich um ein auf dem Beispielcode-Snippet in der Dokumentation basierendes Graphmodell handelt, in dem tf.loadGraphModel() verwendet wird.

Wenn Sie ein Modell im Layer-Format anstelle des Diagrammformats finden, können Sie auswählen, welche Layer für das Training eingefroren und welche aufgetaut werden sollen. Das kann sehr nützlich sein, wenn Sie ein Modell für eine neue Aufgabe erstellen, was oft als „Transfermodell“ bezeichnet wird. Für diese Anleitung verwenden Sie jedoch den Standardgraphenmodelltyp, als den die meisten TF Hub-Modelle bereitgestellt werden. Weitere Informationen zur Arbeit mit Layers-Modellen finden Sie im TensorFlow.js-Kurs für Anfänger.
Vorteile des Lerntransfers
Welche Vorteile bietet der Lerntransfer im Vergleich zum Training der gesamten Modellarchitektur von Grund auf?
Erstens ist die Trainingszeit ein wichtiger Vorteil der Verwendung eines Transfer-Learning-Ansatzes, da Sie bereits ein trainiertes Basismodell haben, auf dem Sie aufbauen können.
Zweitens müssen Sie aufgrund des bereits erfolgten Trainings viel weniger Beispiele für das neue Element zeigen, das Sie klassifizieren möchten.
Das ist besonders nützlich, wenn Sie nur wenig Zeit und Ressourcen haben, um Beispieldaten für die zu klassifizierende Sache zu sammeln, und schnell einen Prototyp erstellen müssen, bevor Sie weitere Trainingsdaten sammeln, um das Modell robuster zu machen.
Da weniger Daten erforderlich sind und das Training eines kleineren Netzwerks schneller geht, ist Transfer Learning weniger ressourcenintensiv. Das macht es sehr gut für die Browserumgebung geeignet, da es auf einem modernen Computer nur wenige Sekunden statt Stunden, Tage oder Wochen für das vollständige Modelltraining benötigt.
Das wars! Nachdem Sie nun wissen, was Transfer Learning ist, können Sie Ihre eigene Version von Teachable Machine erstellen. Los geht's!
5. Code schreiben
Voraussetzungen
- Einen modernen Webbrowser.
- Grundkenntnisse in HTML, CSS, JavaScript und den Chrome-Entwicklertools (Anzeigen der Konsolenausgabe)
Einstieg ins Programmieren
Für Glitch.com oder Codepen.io wurden Boilerplate-Vorlagen erstellt, die als Ausgangspunkt dienen können. Sie können eine der beiden Vorlagen mit nur einem Klick als Ausgangszustand für dieses Codelab klonen.
Klicken Sie in Glitch auf die Schaltfläche remix this (Diesen Remix erstellen), um das Projekt zu forken und einen neuen Satz von Dateien zu erstellen, die Sie bearbeiten können.
Alternativ können Sie auf CodePen rechts unten auf dem Bildschirm auf Fork klicken.
Dieses sehr einfache Gerüst enthält die folgenden Dateien:
- HTML-Seite (index.html)
- Stylesheet (style.css)
- Datei zum Schreiben unseres JavaScript-Codes (script.js)
Zur Vereinfachung wurde in der HTML-Datei ein Import für die TensorFlow.js-Bibliothek hinzugefügt. Sie sieht so aus:
index.html
<!-- Import TensorFlow.js library -->
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs/dist/tf.min.js" type="text/javascript"></script>
Alternative: Bevorzugten Web-Editor verwenden oder lokal arbeiten
Wenn Sie den Code herunterladen und lokal oder in einem anderen Online-Editor bearbeiten möchten, erstellen Sie einfach die oben genannten drei Dateien im selben Verzeichnis und kopieren Sie den Code aus unserem Glitch-Boilerplate in jede Datei.
6. App-HTML-Boilerplate
Wo soll ich anfangen?
Für alle Prototypen ist ein grundlegendes HTML-Gerüst erforderlich, auf dem Sie Ihre Ergebnisse rendern können. Richte das jetzt ein. Sie fügen Folgendes hinzu:
- Ein Titel für die Seite.
- Einige beschreibende Texte.
- Ein Statusabsatz.
- Ein Video, das den Webcam-Feed enthält, sobald er fertig ist.
- Mehrere Schaltflächen zum Starten der Kamera, zum Erfassen von Daten oder zum Zurücksetzen der Funktion.
- Importe für TensorFlow.js und JS-Dateien, die Sie später codieren.
Öffnen Sie index.html und fügen Sie den folgenden Code ein, um die oben genannten Funktionen einzurichten:
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<title>Transfer Learning - TensorFlow.js</title>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Import the webpage's stylesheet -->
<link rel="stylesheet" href="/style.css">
</head>
<body>
<h1>Make your own "Teachable Machine" using Transfer Learning with MobileNet v3 in TensorFlow.js using saved graph model from TFHub.</h1>
<p id="status">Awaiting TF.js load</p>
<video id="webcam" autoplay muted></video>
<button id="enableCam">Enable Webcam</button>
<button class="dataCollector" data-1hot="0" data-name="Class 1">Gather Class 1 Data</button>
<button class="dataCollector" data-1hot="1" data-name="Class 2">Gather Class 2 Data</button>
<button id="train">Train & Predict!</button>
<button id="reset">Reset</button>
<!-- Import TensorFlow.js library -->
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@3.11.0/dist/tf.min.js" type="text/javascript"></script>
<!-- Import the page's JavaScript to do some stuff -->
<script type="module" src="/script.js"></script>
</body>
</html>
Hier gehts los
Sehen wir uns einige der oben genannten HTML-Codeabschnitte an, um einige wichtige Punkte hervorzuheben, die Sie hinzugefügt haben.
- Sie haben ein
<h1>-Tag für den Seitentitel sowie ein<p>-Tag mit der ID „status“ hinzugefügt. Dort werden Informationen ausgegeben, da Sie verschiedene Teile des Systems verwenden, um Ausgaben anzusehen. - Sie haben ein
<video>-Element mit der ID „webcam“ hinzugefügt, in dem Sie später Ihren Webcam-Stream rendern. - Sie haben 5
<button>-Elemente hinzugefügt. Mit dem ersten Element mit der ID „enableCam“ wird die Kamera aktiviert. Die nächsten beiden Schaltflächen haben die Klasse „dataCollector“. Damit können Sie Beispielbilder für die Objekte erfassen, die Sie erkennen möchten. Der Code, den Sie später schreiben, wird so konzipiert, dass Sie beliebig viele dieser Schaltflächen hinzufügen können und sie automatisch wie vorgesehen funktionieren.
Diese Schaltflächen haben auch ein spezielles benutzerdefiniertes Attribut namens „data-1hot“ mit einem ganzzahligen Wert, der für die erste Klasse mit 0 beginnt. Dies ist der numerische Index, den Sie verwenden, um die Daten einer bestimmten Klasse darzustellen. Der Index wird verwendet, um die Ausgabeklassen korrekt mit einer numerischen Darstellung anstelle eines Strings zu codieren, da ML-Modelle nur mit Zahlen arbeiten können.
Es gibt auch ein data-name-Attribut, das den für diese Klasse zu verwendenden lesbaren Namen enthält. So können Sie dem Nutzer einen aussagekräftigeren Namen anstelle eines numerischen Indexwerts aus der 1-Hot-Codierung zur Verfügung stellen.
Außerdem gibt es eine Schaltfläche zum Trainieren und eine zum Zurücksetzen, mit denen Sie das Training starten können, sobald Daten erfasst wurden, bzw. die App zurücksetzen können.
- Außerdem haben Sie zwei
<script>-Importe hinzugefügt. Einen für TensorFlow.js und den anderen für „script.js“, den Sie gleich definieren.
7. Stil hinzufügen
Elementstandardeinstellungen
Fügen Sie Stile für die gerade hinzugefügten HTML-Elemente hinzu, damit sie richtig gerendert werden. Hier sind einige Stile, die hinzugefügt werden, um Elemente richtig zu positionieren und zu skalieren. Nichts Besonderes. Sie können das später noch ergänzen, um die Nutzerfreundlichkeit zu verbessern, wie im Video zur Teachable Machine zu sehen ist.
style.css
body {
font-family: helvetica, arial, sans-serif;
margin: 2em;
}
h1 {
font-style: italic;
color: #FF6F00;
}
video {
clear: both;
display: block;
margin: 10px;
background: #000000;
width: 640px;
height: 480px;
}
button {
padding: 10px;
float: left;
margin: 5px 3px 5px 10px;
}
.removed {
display: none;
}
#status {
font-size:150%;
}
Sehr gut! Das ist alles, was Sie brauchen. Wenn Sie sich die Ausgabe jetzt ansehen, sollte sie in etwa so aussehen:

8. JavaScript: Schlüsselkonstanten und Listener
Wichtige Konstanten definieren
Fügen Sie zuerst einige wichtige Konstanten hinzu, die Sie in der gesamten App verwenden werden. Ersetzen Sie dazu den Inhalt von script.js durch diese Konstanten:
script.js
const STATUS = document.getElementById('status');
const VIDEO = document.getElementById('webcam');
const ENABLE_CAM_BUTTON = document.getElementById('enableCam');
const RESET_BUTTON = document.getElementById('reset');
const TRAIN_BUTTON = document.getElementById('train');
const MOBILE_NET_INPUT_WIDTH = 224;
const MOBILE_NET_INPUT_HEIGHT = 224;
const STOP_DATA_GATHER = -1;
const CLASS_NAMES = [];
Sehen wir uns an, wofür sie verwendet werden:
STATUSenthält lediglich einen Verweis auf das Absatz-Tag, in das Sie Statusaktualisierungen schreiben.VIDEOenthält einen Verweis auf das HTML-Videoelement, das den Webcam-Feed rendert.- Mit
ENABLE_CAM_BUTTON,RESET_BUTTONundTRAIN_BUTTONwerden DOM-Referenzen zu allen wichtigen Schaltflächen der HTML-Seite abgerufen. MOBILE_NET_INPUT_WIDTHundMOBILE_NET_INPUT_HEIGHTdefinieren die erwartete Eingabebreite bzw. -höhe des MobileNet-Modells. Wenn Sie diesen Wert in einer Konstanten oben in der Datei speichern, können Sie ihn später leichter aktualisieren, falls Sie eine andere Version verwenden möchten. Sie müssen ihn dann nur einmal ändern, anstatt an vielen verschiedenen Stellen.STOP_DATA_GATHERist auf -1 gesetzt. Hier wird ein Statuswert gespeichert, damit Sie wissen, wann der Nutzer aufgehört hat, auf eine Schaltfläche zu klicken, um Daten aus dem Webcam-Feed zu erfassen. Wenn Sie dieser Zahl einen aussagekräftigeren Namen geben, wird der Code später lesbarer.CLASS_NAMESdient als Nachschlagevorgang und enthält die für Menschen lesbaren Namen für die möglichen Klassenprognosen. Dieses Array wird später gefüllt.
Nachdem Sie nun Referenzen zu wichtigen Elementen haben, ist es an der Zeit, einige Event-Listener damit zu verknüpfen.
Schlüsselereignis-Listener hinzufügen
Fügen Sie zuerst Click-Event-Handler für wichtige Schaltflächen hinzu, wie hier gezeigt:
script.js
ENABLE_CAM_BUTTON.addEventListener('click', enableCam);
TRAIN_BUTTON.addEventListener('click', trainAndPredict);
RESET_BUTTON.addEventListener('click', reset);
function enableCam() {
// TODO: Fill this out later in the codelab!
}
function trainAndPredict() {
// TODO: Fill this out later in the codelab!
}
function reset() {
// TODO: Fill this out later in the codelab!
}
ENABLE_CAM_BUTTON: Ruft die Funktion „enableCam“ auf, wenn darauf geklickt wird.
TRAIN_BUTTON – ruft „trainAndPredict“ auf, wenn darauf geklickt wird.
RESET_BUTTON: Anrufe werden bei Klick zurückgesetzt.
Schließlich finden Sie in diesem Abschnitt alle Schaltflächen mit der Klasse „dataCollector“ mit document.querySelectorAll(). Dadurch wird ein Array von Elementen zurückgegeben, die im Dokument gefunden wurden und die folgenden Kriterien erfüllen:
script.js
let dataCollectorButtons = document.querySelectorAll('button.dataCollector');
for (let i = 0; i < dataCollectorButtons.length; i++) {
dataCollectorButtons[i].addEventListener('mousedown', gatherDataForClass);
dataCollectorButtons[i].addEventListener('mouseup', gatherDataForClass);
// Populate the human readable names for classes.
CLASS_NAMES.push(dataCollectorButtons[i].getAttribute('data-name'));
}
function gatherDataForClass() {
// TODO: Fill this out later in the codelab!
}
Erläuterung zum Code:
Anschließend durchlaufen Sie die gefundenen Schaltflächen und weisen jeder zwei Event-Listener zu. Einer für „mousedown“ und einer für „mouseup“. So können Sie Aufnahmen so lange aufzeichnen, wie die Taste gedrückt wird. Das ist nützlich für die Datenerhebung.
Bei beiden Ereignissen wird eine gatherDataForClass-Funktion aufgerufen, die Sie später definieren.
An dieser Stelle können Sie auch die gefundenen für Menschen lesbaren Klassennamen aus dem HTML-Schaltflächenattribut „data-name“ in das CLASS_NAMES-Array übertragen.
Fügen Sie als Nächstes einige Variablen hinzu, um wichtige Dinge zu speichern, die später verwendet werden.
script.js
let mobilenet = undefined;
let gatherDataState = STOP_DATA_GATHER;
let videoPlaying = false;
let trainingDataInputs = [];
let trainingDataOutputs = [];
let examplesCount = [];
let predict = false;
Sehen wir uns diese an.
Zuerst haben Sie eine Variable mobilenet, in der das geladene MobileNet-Modell gespeichert wird. Setzen Sie diesen Wert anfangs auf „undefined“.
Als Nächstes haben Sie eine Variable namens gatherDataState. Wenn eine „dataCollector“-Schaltfläche gedrückt wird, ändert sich dies in die 1 Hot-ID dieser Schaltfläche, wie im HTML-Code definiert. So wissen Sie, welche Datenklasse Sie in diesem Moment erheben. Anfangs ist dieser Wert auf STOP_DATA_GATHER gesetzt, damit in der später geschriebenen Datenerfassungsschleife keine Daten erfasst werden, wenn keine Tasten gedrückt werden.
videoPlaying verfolgt, ob der Webcam-Stream erfolgreich geladen wird und wiedergegeben wird und ob er verwendet werden kann. Anfangs ist der Wert auf false festgelegt, da die Webcam erst aktiviert wird, wenn Sie die ENABLE_CAM_BUTTON. drücken.
Als Nächstes definieren Sie zwei Arrays, trainingDataInputs und trainingDataOutputs. Darin werden die gesammelten Trainingsdatenwerte gespeichert, wenn Sie auf die Schaltflächen „dataCollector“ für die Eingabe-Features klicken, die vom MobileNet-Basismodell generiert wurden, und die jeweils ausgewählte Ausgabeklasse.
Ein letztes Array, examplesCount,, wird dann definiert, um zu verfolgen, wie viele Beispiele für jede Klasse enthalten sind, sobald Sie sie hinzufügen.
Schließlich haben Sie eine Variable namens predict, die Ihren Vorhersage-Loop steuert. Sie ist anfangs auf false festgelegt. Bis dahin sind keine Vorhersagen möglich.true
Nachdem alle wichtigen Variablen definiert wurden, laden wir das vorab zerlegte MobileNet V3-Basismodell, das Bild-Featurevektoren anstelle von Klassifizierungen bereitstellt.
9. MobileNet-Basismodell laden
Definieren Sie zuerst eine neue Funktion namens loadMobileNetFeatureModel, wie unten dargestellt. Dies muss eine asynchrone Funktion sein, da das Laden eines Modells asynchron erfolgt:
script.js
/**
* Loads the MobileNet model and warms it up so ready for use.
**/
async function loadMobileNetFeatureModel() {
const URL =
'https://tfhub.dev/google/tfjs-model/imagenet/mobilenet_v3_small_100_224/feature_vector/5/default/1';
mobilenet = await tf.loadGraphModel(URL, {fromTFHub: true});
STATUS.innerText = 'MobileNet v3 loaded successfully!';
// Warm up the model by passing zeros through it once.
tf.tidy(function () {
let answer = mobilenet.predict(tf.zeros([1, MOBILE_NET_INPUT_HEIGHT, MOBILE_NET_INPUT_WIDTH, 3]));
console.log(answer.shape);
});
}
// Call the function immediately to start loading.
loadMobileNetFeatureModel();
In diesem Code definieren Sie URL, wo sich das zu ladende Modell befindet. Diese Informationen stammen aus der TFHub-Dokumentation.
Anschließend können Sie das Modell mit await tf.loadGraphModel() laden. Denken Sie daran, die spezielle Property fromTFHub auf true zu setzen, da Sie ein Modell von dieser Google-Website laden. Dies ist ein Sonderfall nur für die Verwendung von Modellen, die auf TF Hub gehostet werden, bei denen diese zusätzliche Property festgelegt werden muss.
Sobald das Laden abgeschlossen ist, können Sie das Attribut innerText des Elements STATUS mit einer Nachricht festlegen, damit Sie visuell sehen, dass es richtig geladen wurde und Sie mit dem Erfassen von Daten beginnen können.
Jetzt muss das Modell nur noch aufgewärmt werden. Bei größeren Modellen wie diesem kann es beim ersten Verwenden des Modells einen Moment dauern, bis alles eingerichtet ist. Daher ist es hilfreich, Nullen durch das Modell zu leiten, um Wartezeiten in der Zukunft zu vermeiden, wenn das Timing wichtiger sein könnte.
Sie können tf.zeros() in einem tf.tidy() verwenden, um sicherzustellen, dass Tensoren mit einer Batchgröße von 1 und der richtigen Höhe und Breite, die Sie zu Beginn in Ihren Konstanten definiert haben, korrekt verworfen werden. Schließlich geben Sie auch die Farbkanäle an, in diesem Fall 3, da das Modell RGB-Bilder erwartet.
Als Nächstes protokollieren Sie die resultierende Form des zurückgegebenen Tensors mit answer.shape(), um die Größe der von diesem Modell erstellten Bild-Features zu ermitteln.
Nachdem Sie diese Funktion definiert haben, können Sie sie sofort aufrufen, um den Modell-Download beim Laden der Seite zu starten.
Wenn Sie sich jetzt die Live-Vorschau ansehen, ändert sich der Statustext nach einigen Momenten von „Awaiting TF.js load“ (Warte auf TF.js-Ladevorgang) zu „MobileNet v3 loaded successfully!“ (MobileNet v3 wurde erfolgreich geladen). Prüfen Sie, ob das funktioniert, bevor Sie fortfahren.

Sie können auch die Konsolenausgabe prüfen, um die gedruckte Größe der Ausgabefunktionen zu sehen, die dieses Modell erzeugt. Nachdem Sie Nullen durch das MobileNet-Modell ausgeführt haben, wird eine Form von [1, 1024] ausgegeben. Das erste Element ist nur die Batchgröße 1. Es werden 1.024 Merkmale zurückgegeben, die Sie dann verwenden können, um neue Objekte zu klassifizieren.
10. Neuen Modellkopf definieren
Jetzt ist es an der Zeit, den Modellkopf zu definieren. Das ist im Grunde ein sehr minimales mehrschichtiges Perzeptron.
script.js
let model = tf.sequential();
model.add(tf.layers.dense({inputShape: [1024], units: 128, activation: 'relu'}));
model.add(tf.layers.dense({units: CLASS_NAMES.length, activation: 'softmax'}));
model.summary();
// Compile the model with the defined optimizer and specify a loss function to use.
model.compile({
// Adam changes the learning rate over time which is useful.
optimizer: 'adam',
// Use the correct loss function. If 2 classes of data, must use binaryCrossentropy.
// Else categoricalCrossentropy is used if more than 2 classes.
loss: (CLASS_NAMES.length === 2) ? 'binaryCrossentropy': 'categoricalCrossentropy',
// As this is a classification problem you can record accuracy in the logs too!
metrics: ['accuracy']
});
Sehen wir uns diesen Code an. Zuerst definieren Sie ein tf.sequential-Modell, dem Sie Modell-Layer hinzufügen.
Fügen Sie als Nächstes eine Dense-Ebene als Eingabeebene für dieses Modell hinzu. Die Eingabeform ist 1024, da die Ausgaben der MobileNet V3-Funktionen diese Größe haben. Das haben Sie im vorherigen Schritt herausgefunden, nachdem Sie Einsen durch das Modell geleitet haben. Diese Ebene hat 128 Neuronen, die die ReLU-Aktivierungsfunktion verwenden.
Wenn Sie noch nicht mit Aktivierungsfunktionen und Modellschichten vertraut sind, empfehlen wir Ihnen, den Kurs zu absolvieren, der zu Beginn dieses Workshops beschrieben wird, um zu verstehen, was diese Eigenschaften im Hintergrund bewirken.
Als Nächstes fügen wir die Ausgabeschicht hinzu. Die Anzahl der Neuronen sollte der Anzahl der Klassen entsprechen, die Sie vorhersagen möchten. Mit CLASS_NAMES.length können Sie herausfinden, wie viele Klassen Sie klassifizieren möchten. Das entspricht der Anzahl der Schaltflächen zum Erfassen von Daten in der Benutzeroberfläche. Da es sich um ein Klassifizierungsproblem handelt, verwenden Sie die softmax-Aktivierung für diese Ausgabeschicht. Sie muss verwendet werden, wenn Sie ein Modell erstellen möchten, um Klassifizierungsprobleme anstelle von Regression zu lösen.
Geben Sie nun model.summary() ein, um die Übersicht des neu definierten Modells in der Konsole auszugeben.
Kompilieren Sie das Modell schließlich, damit es trainiert werden kann. Hier ist der Optimierer auf adam festgelegt. Der Verlust ist entweder binaryCrossentropy, wenn CLASS_NAMES.length gleich 2 ist, oder es wird categoricalCrossentropy verwendet, wenn es mindestens drei zu klassifizierende Klassen gibt. Außerdem werden Genauigkeitsmesswerte angefordert, damit sie später in den Logs zu Debugging-Zwecken überwacht werden können.
In der Konsole sollte in etwa Folgendes angezeigt werden:

Dieses Modell hat über 130.000 trainierbare Parameter. Da es sich aber um eine einfache dichte Schicht regulärer Neuronen handelt, wird sie recht schnell trainiert.
Nach Abschluss des Projekts können Sie beispielsweise die Anzahl der Neuronen in der ersten Ebene ändern, um zu sehen, wie niedrig Sie sie machen können, ohne dass die Leistung darunter leidet. Oft ist bei Machine Learning ein gewisses Maß an Versuch und Irrtum erforderlich, um optimale Parameterwerte zu finden, die das beste Verhältnis zwischen Ressourcennutzung und Geschwindigkeit bieten.
11. Webcam aktivieren
Jetzt ist es an der Zeit, die zuvor definierte Funktion enableCam() zu erweitern. Fügen Sie eine neue Funktion mit dem Namen hasGetUserMedia() ein, wie unten dargestellt, und ersetzen Sie dann den Inhalt der zuvor definierten Funktion enableCam() durch den entsprechenden Code unten.
script.js
function hasGetUserMedia() {
return !!(navigator.mediaDevices && navigator.mediaDevices.getUserMedia);
}
function enableCam() {
if (hasGetUserMedia()) {
// getUsermedia parameters.
const constraints = {
video: true,
width: 640,
height: 480
};
// Activate the webcam stream.
navigator.mediaDevices.getUserMedia(constraints).then(function(stream) {
VIDEO.srcObject = stream;
VIDEO.addEventListener('loadeddata', function() {
videoPlaying = true;
ENABLE_CAM_BUTTON.classList.add('removed');
});
});
} else {
console.warn('getUserMedia() is not supported by your browser');
}
}
Erstellen Sie zuerst eine Funktion namens hasGetUserMedia(), um zu prüfen, ob der Browser getUserMedia() unterstützt. Dazu wird geprüft, ob wichtige Browser-API-Eigenschaften vorhanden sind.
Verwenden Sie in der Funktion enableCam() die Funktion hasGetUserMedia(), die Sie gerade oben definiert haben, um zu prüfen, ob sie unterstützt wird. Wenn nicht, geben Sie eine Warnung in der Konsole aus.
Wenn das Gerät dies unterstützt, können Sie einige Einschränkungen für Ihren getUserMedia()-Aufruf definieren, z. B. dass Sie nur den Videostream möchten und dass die width des Videos 640 Pixel und die height 480 Pixel betragen soll. Warum? Es macht wenig Sinn, ein Video zu erhalten, das größer als diese Größe ist, da es auf 224 × 224 Pixel skaliert werden müsste, um in das MobileNet-Modell eingespeist zu werden. Sie können auch Rechenressourcen sparen, indem Sie eine geringere Auflösung anfordern. Die meisten Kameras unterstützen eine Auflösung dieser Größe.
Rufen Sie als Nächstes navigator.mediaDevices.getUserMedia() mit der oben beschriebenen constraints auf und warten Sie, bis stream zurückgegeben wird. Sobald stream zurückgegeben wird, können Sie das VIDEO-Element abrufen, um stream abzuspielen, indem Sie es als srcObject-Wert festlegen.
Außerdem sollten Sie dem VIDEO-Element einen EventListener hinzufügen, um zu erfahren, wann das stream geladen wurde und erfolgreich wiedergegeben wird.
Sobald der Stream geladen ist, können Sie videoPlaying auf „true“ setzen und ENABLE_CAM_BUTTON entfernen, um zu verhindern, dass darauf geklickt wird. Dazu setzen Sie die Klasse auf „removed“.
Führen Sie nun Ihren Code aus, klicken Sie auf die Schaltfläche „Kamera aktivieren“ und erlauben Sie den Zugriff auf die Webcam. Wenn Sie die Funktion zum ersten Mal verwenden, sollten Sie sich wie unten dargestellt im Videoelement auf der Seite sehen:

Ok, jetzt ist es an der Zeit, eine Funktion hinzuzufügen, die sich um die Klicks auf die Schaltfläche dataCollector kümmert.
12. Event-Handler für Schaltfläche zur Datenerhebung
Jetzt müssen Sie die derzeit leere Funktion gatherDataForClass(). ausfüllen. Das ist die Funktion, die Sie zu Beginn des Codelabs als Event-Handler-Funktion für dataCollector-Schaltflächen zugewiesen haben.
script.js
/**
* Handle Data Gather for button mouseup/mousedown.
**/
function gatherDataForClass() {
let classNumber = parseInt(this.getAttribute('data-1hot'));
gatherDataState = (gatherDataState === STOP_DATA_GATHER) ? classNumber : STOP_DATA_GATHER;
dataGatherLoop();
}
Prüfen Sie zuerst das Attribut data-1hot des aktuell geklickten Buttons, indem Sie this.getAttribute() mit dem Namen des Attributs, in diesem Fall data-1hot, als Parameter aufrufen. Da es sich um einen String handelt, können Sie ihn mit parseInt() in eine Ganzzahl umwandeln und das Ergebnis einer Variablen mit dem Namen classNumber. zuweisen.
Legen Sie als Nächstes die Variable gatherDataState entsprechend fest. Wenn der aktuelle gatherDataState gleich STOP_DATA_GATHER ist (was Sie auf -1 festgelegt haben), bedeutet das, dass Sie derzeit keine Daten erheben und ein mousedown-Ereignis ausgelöst wurde. Legen Sie gatherDataState auf den Wert classNumber fest, den Sie gerade gefunden haben.
Andernfalls bedeutet das, dass Sie derzeit Daten erfassen und das ausgelöste Ereignis ein mouseup-Ereignis war. Sie möchten nun die Datenerfassung für diese Klasse beenden. Setzen Sie sie einfach wieder auf den Status STOP_DATA_GATHER zurück, um den Datenerfassungszyklus zu beenden, den Sie gleich definieren werden.
Rufen Sie schließlich dataGatherLoop(), auf, um die Aufzeichnung der Kursdaten zu starten.
13. Datenerhebung
Definieren Sie nun die Funktion dataGatherLoop(). Diese Funktion ist dafür zuständig, Bilder aus dem Webcam-Video zu entnehmen, sie durch das MobileNet-Modell zu leiten und die Ausgaben dieses Modells (die 1.024 Featurevektoren) zu erfassen.
Die Daten werden dann zusammen mit der gatherDataState-ID der aktuell gedrückten Schaltfläche gespeichert, damit Sie wissen, welche Klasse diese Daten repräsentieren.
So gehts:
script.js
function dataGatherLoop() {
if (videoPlaying && gatherDataState !== STOP_DATA_GATHER) {
let imageFeatures = tf.tidy(function() {
let videoFrameAsTensor = tf.browser.fromPixels(VIDEO);
let resizedTensorFrame = tf.image.resizeBilinear(videoFrameAsTensor, [MOBILE_NET_INPUT_HEIGHT,
MOBILE_NET_INPUT_WIDTH], true);
let normalizedTensorFrame = resizedTensorFrame.div(255);
return mobilenet.predict(normalizedTensorFrame.expandDims()).squeeze();
});
trainingDataInputs.push(imageFeatures);
trainingDataOutputs.push(gatherDataState);
// Intialize array index element if currently undefined.
if (examplesCount[gatherDataState] === undefined) {
examplesCount[gatherDataState] = 0;
}
examplesCount[gatherDataState]++;
STATUS.innerText = '';
for (let n< = 0; n CLASS_NAMES.length; n++) {
STATUS.innerText += CLASS_NAMES[n] + ' data count: ' + examplesCount[n] + '. ';
}
window.requestAnimationFrame(dataGatherLoop);
}
}
Sie setzen die Ausführung dieser Funktion nur fort, wenn videoPlaying wahr ist, d. h. die Webcam aktiv ist, und gatherDataState nicht gleich STOP_DATA_GATHER ist und derzeit eine Schaltfläche zum Erfassen von Klassendaten gedrückt wird.
Schließen Sie Ihren Code als Nächstes in ein tf.tidy() ein, um alle erstellten Tensoren im folgenden Code zu verwerfen. Das Ergebnis dieser tf.tidy()-Codeausführung wird in einer Variablen mit dem Namen imageFeatures gespeichert.
Sie können jetzt mit tf.browser.fromPixels() einen Frame der Webcam VIDEO abrufen. Der resultierende Tensor mit den Bilddaten wird in einer Variablen namens videoFrameAsTensor gespeichert.
Als Nächstes passen Sie die Größe der Variablen videoFrameAsTensor an, damit sie die richtige Form für die Eingabe des MobileNet-Modells hat. Verwenden Sie einen tf.image.resizeBilinear()-Aufruf mit dem Tensor, den Sie umformen möchten, als ersten Parameter und dann eine Form, die die neue Höhe und Breite definiert, wie durch die Konstanten, die Sie bereits zuvor erstellt haben. Legen Sie schließlich den dritten Parameter auf „true“ fest, um Ausrichtungsprobleme beim Ändern der Größe zu vermeiden. Das Ergebnis dieser Größenänderung wird in einer Variablen namens resizedTensorFrame gespeichert.
Bei dieser primitiven Größenanpassung wird das Bild gestreckt, da Ihr Webcam-Bild eine Größe von 640 × 480 Pixel hat und das Modell ein quadratisches Bild mit 224 × 224 Pixel benötigt.
Für diese Demo sollte das kein Problem sein. Nachdem Sie dieses Codelab durchgearbeitet haben, können Sie versuchen, ein quadratisches Bild aus diesem Bild zuzuschneiden, um noch bessere Ergebnisse für alle Produktionssysteme zu erzielen, die Sie später erstellen.
Als Nächstes normalisieren Sie die Bilddaten. Bilddaten liegen bei Verwendung von tf.browser.frompixels() immer im Bereich von 0 bis 255. Sie können resizedTensorFrame also einfach durch 255 teilen, um sicherzustellen, dass alle Werte zwischen 0 und 1 liegen. Das ist das, was das MobileNet-Modell als Eingabe erwartet.
Im Abschnitt tf.tidy() des Codes wird dieser normalisierte Tensor schließlich durch das geladene Modell geleitet, indem mobilenet.predict() aufgerufen wird. An diese Funktion wird die erweiterte Version von normalizedTensorFrame mit expandDims() übergeben, sodass sie ein Batch von 1 ist, da das Modell einen Batch von Eingaben für die Verarbeitung erwartet.
Sobald das Ergebnis zurückgegeben wird, können Sie squeeze() für dieses Ergebnis aufrufen, um es wieder auf einen 1D-Tensor zu reduzieren. Diesen geben Sie dann zurück und weisen ihn der Variablen imageFeatures zu, die das Ergebnis von tf.tidy() enthält.
Nachdem Sie die imageFeatures aus dem MobileNet-Modell haben, können Sie sie aufzeichnen, indem Sie sie in das zuvor definierte trainingDataInputs-Array einfügen.
Sie können auch aufzeichnen, was diese Eingabe darstellt, indem Sie den aktuellen gatherDataState in das trainingDataOutputs-Array einfügen.
Die Variable gatherDataState wäre in der zuvor definierten Funktion gatherDataForClass() auf die numerische ID der aktuellen Klasse festgelegt worden, für die Sie Daten erfassen, als auf die Schaltfläche geklickt wurde.
An diesem Punkt können Sie auch die Anzahl der Beispiele für eine bestimmte Klasse erhöhen. Prüfen Sie dazu zuerst, ob der Index im examplesCount-Array bereits initialisiert wurde. Wenn er nicht definiert ist, setzen Sie ihn auf 0, um den Zähler für die numerische ID einer bestimmten Klasse zu initialisieren. Anschließend können Sie examplesCount für die aktuelle gatherDataState inkrementieren.
Aktualisieren Sie nun den Text des STATUS-Elements auf der Webseite, damit die aktuellen Anzahlwerte für jede Klasse angezeigt werden, sobald sie erfasst werden. Dazu durchlaufen Sie das CLASS_NAMES-Array und geben den lesbaren Namen zusammen mit der Anzahl der Daten am selben Index in examplesCount aus.
Rufen Sie schließlich window.requestAnimationFrame() mit dataGatherLoop als Parameter auf, um diese Funktion rekursiv aufzurufen. Es werden weiterhin Frames aus dem Video analysiert, bis die mouseup des Buttons erkannt wird und gatherDataState auf STOP_DATA_GATHER, gesetzt wird. Dann wird die Datenerfassungsschleife beendet.
Wenn Sie Ihren Code jetzt ausführen, sollten Sie auf die Schaltfläche „Kamera aktivieren“ klicken können, bis die Webcam geladen ist. Klicken Sie dann auf jede der Schaltflächen zum Erfassen von Daten und halten Sie sie gedrückt, um Beispiele für jede Datenklasse zu erfassen. Hier sehen Sie, wie ich Daten für mein Smartphone und meine Hand erfasse.

Der Statustext sollte aktualisiert werden, da alle Tensoren im Arbeitsspeicher gespeichert werden (siehe Screenshot oben).
14. Trainieren und vorhersagen
Als Nächstes müssen Sie Code für Ihre derzeit leere trainAndPredict()-Funktion implementieren, in der das Transfer Learning stattfindet. Sehen wir uns den Code an:
script.js
async function trainAndPredict() {
predict = false;
tf.util.shuffleCombo(trainingDataInputs, trainingDataOutputs);
let outputsAsTensor = tf.tensor1d(trainingDataOutputs, 'int32');
let oneHotOutputs = tf.oneHot(outputsAsTensor, CLASS_NAMES.length);
let inputsAsTensor = tf.stack(trainingDataInputs);
let results = await model.fit(inputsAsTensor, oneHotOutputs, {shuffle: true, batchSize: 5, epochs: 10,
callbacks: {onEpochEnd: logProgress} });
outputsAsTensor.dispose();
oneHotOutputs.dispose();
inputsAsTensor.dispose();
predict = true;
predictLoop();
}
function logProgress(epoch, logs) {
console.log('Data for epoch ' + epoch, logs);
}
Stellen Sie zuerst sicher, dass keine aktuellen Vorhersagen mehr ausgeführt werden, indem Sie predict auf false setzen.
Mischen Sie als Nächstes Ihre Eingabe- und Ausgabearrays mit tf.util.shuffleCombo(), damit die Reihenfolge keine Probleme beim Training verursacht.
Konvertieren Sie Ihr Ausgabearray trainingDataOutputs, in einen Tensor1d vom Typ int32, damit es in einer One-Hot-Codierung verwendet werden kann. Dieser Wert wird in einer Variablen mit dem Namen outputsAsTensor gespeichert.
Verwenden Sie die Funktion tf.oneHot() mit dieser outputsAsTensor-Variablen und der maximalen Anzahl der zu codierenden Klassen, also CLASS_NAMES.length. Die One-Hot-codierten Ausgaben werden jetzt in einem neuen Tensor namens oneHotOutputs gespeichert.
Derzeit ist trainingDataInputs ein Array mit aufgezeichneten Tensoren. Damit Sie diese für das Training verwenden können, müssen Sie das Tensor-Array in einen regulären 2D-Tensor umwandeln.
Dazu gibt es eine tolle Funktion in der TensorFlow.js-Bibliothek namens tf.stack().
Diese Funktion nimmt ein Array von Tensoren entgegen und stapelt sie, um einen Tensor mit höherer Dimension als Ausgabe zu erzeugen. In diesem Fall wird ein 2D-Tensor zurückgegeben, also ein Batch mit eindimensionalen Eingaben, die jeweils eine Länge von 1.024 haben und die aufgezeichneten Features enthalten. Das ist genau das, was Sie für das Training benötigen.
Als Nächstes await model.fit(), um den benutzerdefinierten Modell-Head zu trainieren. Hier übergeben Sie die Variable inputsAsTensor zusammen mit oneHotOutputs, um die Trainingsdaten für Beispiel- und Zielausgaben darzustellen. Legen Sie im Konfigurationsobjekt für den dritten Parameter shuffle auf true fest, verwenden Sie batchSize von 5 mit epochs auf 10 und geben Sie dann einen callback für onEpochEnd für die logProgress-Funktion an, die Sie in Kürze definieren werden.
Schließlich können Sie die erstellten Tensoren verwerfen, da das Modell jetzt trainiert ist. Anschließend können Sie predict wieder auf true setzen, damit Vorhersagen wieder möglich sind, und dann die Funktion predictLoop() aufrufen, um mit der Vorhersage von Live-Webcambildern zu beginnen.
Sie können auch die Funktion logProcess() definieren, um den Status des Trainings zu protokollieren. Diese Funktion wird oben in model.fit() verwendet und gibt nach jeder Trainingsrunde Ergebnisse in der Konsole aus.
Du hast es fast geschafft. Es ist an der Zeit, die predictLoop()-Funktion hinzuzufügen, um Vorhersagen zu treffen.
Zentrale Vorhersageschleife
Hier implementieren Sie die Hauptvorhersageschleife, die Frames von einer Webcam abruft und kontinuierlich vorhersagt, was sich in jedem Frame befindet. Die Ergebnisse werden in Echtzeit im Browser angezeigt.
Sehen wir uns den Code an:
script.js
function predictLoop() {
if (predict) {
tf.tidy(function() {
let videoFrameAsTensor = tf.browser.fromPixels(VIDEO).div(255);
let resizedTensorFrame = tf.image.resizeBilinear(videoFrameAsTensor,[MOBILE_NET_INPUT_HEIGHT,
MOBILE_NET_INPUT_WIDTH], true);
let imageFeatures = mobilenet.predict(resizedTensorFrame.expandDims());
let prediction = model.predict(imageFeatures).squeeze();
let highestIndex = prediction.argMax().arraySync();
let predictionArray = prediction.arraySync();
STATUS.innerText = 'Prediction: ' + CLASS_NAMES[highestIndex] + ' with ' + Math.floor(predictionArray[highestIndex] * 100) + '% confidence';
});
window.requestAnimationFrame(predictLoop);
}
}
Prüfen Sie zuerst, ob predict wahr ist, damit Vorhersagen erst erstellt werden, wenn ein Modell trainiert wurde und zur Verwendung verfügbar ist.
Als Nächstes können Sie die Bild-Features für das aktuelle Bild abrufen, wie Sie es in der Funktion dataGatherLoop() getan haben. Dazu wird im Grunde ein Frame von der Webcam mit tf.browser.from pixels() abgerufen, normalisiert, auf 224 × 224 Pixel skaliert und dann durch das MobileNet-Modell geleitet, um die resultierenden Bildfunktionen zu erhalten.
Jetzt können Sie den neu trainierten Modellkopf jedoch verwenden, um tatsächlich eine Vorhersage zu treffen. Dazu übergeben Sie die resultierende imageFeatures, die Sie gerade gefunden haben, über die predict()-Funktion des trainierten Modells. Anschließend können Sie den resultierenden Tensor komprimieren, um ihn wieder eindimensional zu machen, und ihn einer Variablen mit dem Namen prediction zuweisen.
Mit dieser prediction können Sie den Index mit dem höchsten Wert mithilfe von argMax() ermitteln und den resultierenden Tensor dann mit arraySync() in ein Array konvertieren, um auf die zugrunde liegenden Daten in JavaScript zuzugreifen und die Position des Elements mit dem höchsten Wert zu ermitteln. Dieser Wert wird in der Variablen highestIndex gespeichert.
Sie können die tatsächlichen Konfidenzwerte für die Vorhersage auf dieselbe Weise abrufen, indem Sie arraySync() direkt für den prediction-Tensor aufrufen.
Sie haben jetzt alles, was Sie benötigen, um den STATUS-Text mit den prediction-Daten zu aktualisieren. Um den menschenlesbaren String für die Klasse zu erhalten, können Sie einfach highestIndex im Array CLASS_NAMES nachschlagen und dann den Konfidenzwert aus predictionArray abrufen. Um das Ergebnis als Prozentsatz darzustellen, multiplizieren Sie es einfach mit 100 und math.floor() das Ergebnis.
Schließlich können Sie window.requestAnimationFrame() verwenden, um predictionLoop() noch einmal aufzurufen, sobald Sie bereit sind, um eine Echtzeitklassifizierung Ihres Videostreams zu erhalten. Das geht so lange, bis predict auf false gesetzt wird, wenn Sie ein neues Modell mit neuen Daten trainieren.
Damit kommen Sie zum letzten Puzzleteil. Implementieren der Schaltfläche zum Zurücksetzen
15. Schaltfläche zum Zurücksetzen implementieren
Fast fertig! Das letzte Puzzleteil ist die Implementierung einer Schaltfläche zum Zurücksetzen, um von vorn zu beginnen. Der Code für Ihre derzeit leere reset()-Funktion ist unten zu sehen. Aktualisieren Sie sie so:
script.js
/**
* Purge data and start over. Note this does not dispose of the loaded
* MobileNet model and MLP head tensors as you will need to reuse
* them to train a new model.
**/
function reset() {
predict = false;
examplesCount.length = 0;
for (let i = 0; i < trainingDataInputs.length; i++) {
trainingDataInputs[i].dispose();
}
trainingDataInputs.length = 0;
trainingDataOutputs.length = 0;
STATUS.innerText = 'No data collected';
console.log('Tensors in memory: ' + tf.memory().numTensors);
}
Beenden Sie zuerst alle laufenden Vorhersageschleifen, indem Sie predict auf false setzen. Löschen Sie als Nächstes alle Inhalte im examplesCount-Array, indem Sie die Länge auf 0 setzen. So lassen sich alle Inhalte aus einem Array löschen.
Gehen Sie nun alle aktuell aufgezeichneten trainingDataInputs durch und dispose() Sie jeden darin enthaltenen Tensor, um wieder Speicherplatz freizugeben, da Tensoren nicht vom JavaScript-Garbage Collector bereinigt werden.
Danach können Sie die Arraylänge für die Arrays trainingDataInputs und trainingDataOutputs auf 0 setzen, um auch diese zu leeren.
Legen Sie schließlich den STATUS-Text auf einen sinnvollen Wert fest und geben Sie die im Speicher verbliebenen Tensoren zur Überprüfung aus.
Beachten Sie, dass sich noch einige Hundert Tensoren im Arbeitsspeicher befinden, da sowohl das MobileNet-Modell als auch das von Ihnen definierte mehrschichtige Perzeptron nicht verworfen werden. Wenn Sie nach dem Zurücksetzen noch einmal trainieren möchten, müssen Sie sie mit neuen Trainingsdaten wiederverwenden.
16. Probieren wir es aus
Jetzt ist es an der Zeit, Ihre eigene Version von Teachable Machine auszuprobieren.
Rufen Sie die Live-Vorschau auf, aktivieren Sie die Webcam, erfassen Sie mindestens 30 Stichproben für Klasse 1 für ein Objekt in Ihrem Raum und wiederholen Sie den Vorgang für Klasse 2 für ein anderes Objekt. Klicken Sie dann auf „Trainieren“ und sehen Sie sich das Konsolenprotokoll an, um den Fortschritt zu verfolgen. Das Training sollte recht schnell abgeschlossen sein:

Sobald das Modell trainiert wurde, halten Sie die Objekte vor die Kamera, um Live-Vorhersagen zu erhalten, die im Statusbereich oben auf der Webseite angezeigt werden. Wenn Sie Probleme haben, sehen Sie sich meinen vollständigen funktionierenden Code an, um zu prüfen, ob Sie etwas vergessen haben.
17. Glückwunsch
Glückwunsch! Sie haben gerade Ihr erstes Beispiel für Transfer Learning mit TensorFlow.js live im Browser abgeschlossen.
Probieren Sie es aus und testen Sie es mit verschiedenen Objekten. Sie werden feststellen, dass einige Dinge schwieriger zu erkennen sind als andere, insbesondere wenn sie etwas anderem ähneln. Möglicherweise müssen Sie weitere Klassen oder Trainingsdaten hinzufügen, um sie unterscheiden zu können.
Zusammenfassung
In diesem Codelab haben Sie Folgendes gelernt:
- Was Lerntransfer ist und welche Vorteile er gegenüber dem Training eines vollständigen Modells bietet.
- So rufen Sie Modelle zur Wiederverwendung aus TensorFlow Hub ab.
- Eine Web-App für Transfer Learning einrichten
- So laden und verwenden Sie ein Basismodell, um Bild-Features zu generieren.
- So trainieren Sie einen neuen Vorhersage-Head, der benutzerdefinierte Objekte aus Webcam-Bildern erkennen kann.
- Wie Sie die resultierenden Modelle verwenden, um Daten in Echtzeit zu klassifizieren.
Nächste Schritte
Jetzt haben Sie eine funktionierende Grundlage. Welche kreativen Ideen haben Sie, um diese Boilerplate für ein Machine-Learning-Modell für einen realen Anwendungsfall zu erweitern, an dem Sie vielleicht gerade arbeiten? Vielleicht könnten Sie die Branche, in der Sie derzeit arbeiten, revolutionieren und Ihren Kollegen helfen, Modelle zu trainieren, um Dinge zu klassifizieren, die für ihre tägliche Arbeit wichtig sind. Die Möglichkeiten sind praktisch endlos.
Dieser kostenlose Kurs zeigt Ihnen, wie Sie die beiden Modelle, die Sie derzeit in diesem Codelab haben, zu einem einzigen Modell kombinieren können, um die Effizienz zu steigern.
Wenn Sie mehr über die Theorie hinter der ursprünglichen Teachable Machine-Anwendung erfahren möchten, sehen Sie sich diese Anleitung an.
Teilen Sie uns mit, was Sie erstellen
Sie können Ihre heutigen Kreationen ganz einfach für andere kreative Anwendungsfälle erweitern. Wir möchten Sie ermutigen, kreativ zu werden und weiter zu experimentieren.
Vergessen Sie nicht, uns in den sozialen Medien mit dem Hashtag #MadeWithTFJS zu taggen. Vielleicht wird Ihr Projekt dann in unserem TensorFlow-Blog oder sogar bei zukünftigen Veranstaltungen vorgestellt. Wir sind gespannt auf deine Kreationen.
Websites, die Sie sich ansehen sollten
- Offizielle TensorFlow.js-Website
- Vorgefertigte TensorFlow.js-Modelle
- TensorFlow.js API
- TensorFlow.js Show & Tell: Lassen Sie sich inspirieren und sehen Sie sich an, was andere erstellt haben.