TensorFlow.js - ट्रांसफ़र लर्निंग का इस्तेमाल करके ऑडियो की पहचान करना

1. परिचय

इस कोडलैब में, आपको आवाज़ की पहचान करने वाला नेटवर्क बनाना है. साथ ही, आवाज़ें करके ब्राउज़र में मौजूद किसी स्लाइडर को कंट्रोल करने के लिए, इस नेटवर्क का इस्तेमाल करना है. आपको JavaScript के लिए, मशीन लर्निंग की एक बेहतरीन और फ़्लेक्सिबल लाइब्रेरी, TensorFlow.js का इस्तेमाल करना होगा.

सबसे पहले, आपको पहले से ट्रेन किए गए मॉडल को लोड और रन करना होगा. यह मॉडल, आवाज़ से दिए गए 20 निर्देशों को पहचान सकता है. इसके बाद, माइक्रोफ़ोन का इस्तेमाल करके एक सामान्य न्यूरल नेटवर्क बनाया जाएगा. यह नेटवर्क, आपकी आवाज़ को पहचानकर स्लाइडर को बाईं या दाईं ओर ले जाएगा.

इस कोडलैब में, ऑडियो की पहचान करने वाले मॉडल के पीछे के सिद्धांत के बारे में नहीं बताया जाएगा. अगर आपको इसके बारे में ज़्यादा जानना है, तो यह ट्यूटोरियल देखें.

हमने मशीन लर्निंग से जुड़े शब्दों की एक शब्दकोश भी बनाया है. इसमें आपको इस कोडलैब में इस्तेमाल किए गए शब्दों के बारे में जानकारी मिलेगी.

आपको क्या सीखने को मिलेगा

  • पहले से ट्रेन किए गए स्पीच कमांड रिकग्निशन मॉडल को लोड करने का तरीका
  • माइक्रोफ़ोन का इस्तेमाल करके, रीयल-टाइम में अनुमान लगाने का तरीका
  • ब्राउज़र के माइक्रोफ़ोन का इस्तेमाल करके, ऑडियो पहचानने वाले कस्टम मॉडल को ट्रेन करने और उसका इस्तेमाल करने का तरीका

आइए, शुरू करते हैं.

2. ज़रूरी शर्तें

इस कोडलैब को पूरा करने के लिए, आपके पास ये चीज़ें होनी चाहिए:

  1. Chrome या किसी अन्य मॉडर्न ब्राउज़र का नया वर्शन.
  2. टेक्स्ट एडिटर, जो आपकी मशीन पर स्थानीय तौर पर चल रहा हो या वेब पर Codepen या Glitch जैसे किसी टूल के ज़रिए चल रहा हो.
  3. एचटीएमएल, सीएसएस, JavaScript, और Chrome DevTools (या आपके पसंदीदा ब्राउज़र के डेवलपर टूल) के बारे में जानकारी.
  4. न्यूरल नेटवर्क के बारे में बुनियादी जानकारी. अगर आपको इस बारे में जानकारी चाहिए या आपको इसे फिर से समझना है, तो 3blue1brown का यह वीडियो या आशी कृष्णन का JavaScript में डीप लर्निंग के बारे में यह वीडियो देखें.

3. TensorFlow.js और ऑडियो मॉडल लोड करना

किसी एडिटर में index.html खोलें और यह कॉन्टेंट जोड़ें:

<html>
  <head>
    <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs"></script>
    <script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/speech-commands"></script>
  </head>
  <body>
    <div id="console"></div>
    <script src="index.js"></script>
  </body>
</html>

पहले <script> टैग से TensorFlow.js लाइब्रेरी इंपोर्ट की जाती है. वहीं, दूसरे <script> टैग से पहले से ट्रेन किया गया Speech Commands मॉडल इंपोर्ट किया जाता है. <div id="console"> टैग का इस्तेमाल, मॉडल का आउटपुट दिखाने के लिए किया जाएगा.

4. रीयल-टाइम में अनुमान लगाना

इसके बाद, कोड एडिटर में index.js फ़ाइल खोलें या बनाएं. साथ ही, इसमें यह कोड शामिल करें:

let recognizer;

function predictWord() {
 // Array of words that the recognizer is trained to recognize.
 const words = recognizer.wordLabels();
 recognizer.listen(({scores}) => {
   // Turn scores into a list of (score,word) pairs.
   scores = Array.from(scores).map((s, i) => ({score: s, word: words[i]}));
   // Find the most probable word.
   scores.sort((s1, s2) => s2.score - s1.score);
   document.querySelector('#console').textContent = scores[0].word;
 }, {probabilityThreshold: 0.75});
}

async function app() {
 recognizer = speechCommands.create('BROWSER_FFT');
 await recognizer.ensureModelLoaded();
 predictWord();
}

app();

5. अनुमान की जांच करना

पक्का करें कि आपके डिवाइस में माइक्रोफ़ोन हो. ध्यान दें कि यह सुविधा, मोबाइल फ़ोन पर भी काम करेगी! वेबपेज चलाने के लिए, ब्राउज़र में index.html खोलें. अगर किसी लोकल फ़ाइल पर काम किया जा रहा है, तो माइक्रोफ़ोन को ऐक्सेस करने के लिए, आपको वेब सर्वर शुरू करना होगा और http://localhost:port/ का इस्तेमाल करना होगा.

पोर्ट 8000 पर एक सामान्य वेबसर्वर शुरू करने के लिए:

python -m SimpleHTTPServer

मॉडल को डाउनलोड होने में कुछ समय लग सकता है. इसलिए, कृपया इंतज़ार करें. मॉडल लोड होते ही, आपको पेज के सबसे ऊपर एक शब्द दिखेगा. इस मॉडल को 0 से 9 तक की संख्याओं और कुछ अन्य निर्देशों को पहचानने के लिए ट्रेन किया गया था. जैसे, "बाएं", "दाएं", "हां", "नहीं" वगैरह.

इनमें से कोई शब्द बोलें. क्या यह आपके शब्दों को सही तरीके से समझता है? probabilityThreshold के साथ खेलें. इससे यह कंट्रोल किया जा सकता है कि मॉडल कितनी बार ट्रिगर होता है. 0.75 का मतलब है कि मॉडल तब ट्रिगर होगा, जब उसे 75% से ज़्यादा भरोसा होगा कि उसने कोई शब्द सुना है.

स्पीच कमांड मॉडल और इसके एपीआई के बारे में ज़्यादा जानने के लिए, Github पर README.md देखें.

6. डेटा संग्रहित करें

इसे मज़ेदार बनाने के लिए, स्लाइडर को कंट्रोल करने के लिए पूरे शब्दों के बजाय छोटे-छोटे साउंड का इस्तेमाल करें!

आपको एक मॉडल को तीन अलग-अलग कमांड पहचानने के लिए ट्रेन करना है: "लेफ़्ट", "राइट", और "नॉइज़". इन कमांड से स्लाइडर को बाईं या दाईं ओर ले जाया जा सकेगा. आवाज़ पहचानने की सुविधा के लिए, "शोर" (कोई कार्रवाई ज़रूरी नहीं है) को पहचानना ज़रूरी है. ऐसा इसलिए, क्योंकि हम चाहते हैं कि स्लाइडर सिर्फ़ तब काम करे, जब हम सही आवाज़ निकालें. यह तब काम नहीं करना चाहिए, जब हम सामान्य तौर पर बोल रहे हों और घूम रहे हों.

  1. इसके लिए, हमें सबसे पहले डेटा इकट्ठा करना होगा. ऐप्लिकेशन में आसान यूज़र इंटरफ़ेस (यूआई) जोड़ने के लिए, <div id="console"> से पहले <body> टैग में यह कोड जोड़ें:
<button id="left" onmousedown="collect(0)" onmouseup="collect(null)">Left</button>
<button id="right" onmousedown="collect(1)" onmouseup="collect(null)">Right</button>
<button id="noise" onmousedown="collect(2)" onmouseup="collect(null)">Noise</button>
  1. इसे index.js में जोड़ें:
// One frame is ~23ms of audio.
const NUM_FRAMES = 3;
let examples = [];

function collect(label) {
 if (recognizer.isListening()) {
   return recognizer.stopListening();
 }
 if (label == null) {
   return;
 }
 recognizer.listen(async ({spectrogram: {frameSize, data}}) => {
   let vals = normalize(data.subarray(-frameSize * NUM_FRAMES));
   examples.push({vals, label});
   document.querySelector('#console').textContent =
       `${examples.length} examples collected`;
 }, {
   overlapFactor: 0.999,
   includeSpectrogram: true,
   invokeCallbackOnNoiseAndUnknown: true
 });
}

function normalize(x) {
 const mean = -100;
 const std = 10;
 return x.map(x => (x - mean) / std);
}
  1. app() से predictWord() को हटाने के लिए:
async function app() {
 recognizer = speechCommands.create('BROWSER_FFT');
 await recognizer.ensureModelLoaded();
 // predictWord() no longer called.
}

डांस पे चांस

यह कोड पहली बार में थोड़ा मुश्किल लग सकता है, इसलिए आइए इसे आसान बनाते हैं.

हमने अपने यूज़र इंटरफ़ेस (यूआई) में तीन बटन जोड़े हैं. इनके नाम "बाएं", "दाएं", और "शोर" हैं. ये बटन, उन तीन निर्देशों के हिसाब से हैं जिन्हें हम चाहते हैं कि हमारा मॉडल पहचान सके. इन बटन को दबाने पर, हाल ही में जोड़ा गया collect() फ़ंक्शन कॉल होता है. यह फ़ंक्शन, हमारे मॉडल के लिए ट्रेनिंग के उदाहरण बनाता है.

collect(), recognizer.listen() के आउटपुट के साथ label को जोड़ता है. includeSpectrogram सही है,, इसलिए recognizer.listen(), एक सेकंड के ऑडियो के लिए रॉ स्पेक्ट्रोग्राम (फ़्रीक्वेंसी डेटा) देता है. इसे 43 फ़्रेम में बांटा गया है. इसलिए, हर फ़्रेम ~23 मि॰से॰ का ऑडियो है:

recognizer.listen(async ({spectrogram: {frameSize, data}}) => {
...
}, {includeSpectrogram: true});

हमें स्लाइडर को कंट्रोल करने के लिए शब्दों के बजाय छोटी आवाज़ों का इस्तेमाल करना है. इसलिए, हम सिर्फ़ आखिरी तीन फ़्रेम (~70 मि॰से॰) को ध्यान में रख रहे हैं:

let vals = normalize(data.subarray(-frameSize * NUM_FRAMES));

संख्या से जुड़ी समस्याओं से बचने के लिए, हम डेटा को नॉर्मलाइज़ करते हैं, ताकि उसका औसत 0 और स्टैंडर्ड डेविएशन 1 हो. इस मामले में, स्पेक्ट्रोग्राम की वैल्यू आम तौर पर -100 के आस-पास की बड़ी नेगेटिव संख्याएं होती हैं. साथ ही, इनमें 10 का अंतर होता है:

const mean = -100;
const std = 10;
return x.map(x => (x - mean) / std);

आखिर में, ट्रेनिंग के हर उदाहरण में दो फ़ील्ड होंगे:

  • label****: "Left", "Right", और "Noise" के लिए, 0, 1, और 2.
  • vals****: फ़्रीक्वेंसी की जानकारी देने वाले 696 नंबर (स्पेक्ट्रोग्राम)

हम सभी डेटा को examples वैरिएबल में सेव करते हैं:

examples.push({vals, label});

7. डेटा कलेक्शन की जांच करना

ब्राउज़र में index.html खोलें. आपको तीन बटन दिखेंगे, जो तीन कमांड के हिसाब से होंगे. अगर किसी लोकल फ़ाइल पर काम किया जा रहा है, तो माइक्रोफ़ोन को ऐक्सेस करने के लिए, आपको वेबसर्वर शुरू करना होगा और http://localhost:port/ का इस्तेमाल करना होगा.

पोर्ट 8000 पर एक सामान्य वेबसर्वर शुरू करने के लिए:

python -m SimpleHTTPServer

हर कमांड के लिए उदाहरण इकट्ठा करने के लिए, हर बटन को 3 से 4 सेकंड तक दबाकर रखें. इस दौरान, एक जैसी आवाज़ बार-बार (या लगातार) करें. आपको हर लेबल के लिए, करीब 150 उदाहरण इकट्ठा करने चाहिए. उदाहरण के लिए, "बाएं" के लिए उंगलियां चटकाई जा सकती हैं, "दाएं" के लिए सीटी बजाई जा सकती है, और "शोर" के लिए बारी-बारी से चुप रहा जा सकता है और बोला जा सकता है.

ज़्यादा उदाहरण इकट्ठा करने पर, पेज पर दिखने वाला काउंटर बढ़ना चाहिए. कंसोल में मौजूद examples वैरिएबल पर console.log() को कॉल करके, डेटा की जांच भी की जा सकती है. इस चरण में, डेटा कलेक्शन की प्रोसेस की जांच करना ज़रूरी है. पूरे ऐप्लिकेशन की जांच करते समय, आपको बाद में डेटा फिर से इकट्ठा करना होगा.

8. किसी मॉडल को ट्रेन करना

  1. index.html में, बॉडी में मौजूद "Noise" बटन के ठीक बाद "Train" बटन जोड़ें:
<br/><br/>
<button id="train" onclick="train()">Train</button>
  1. index.js में मौजूद कोड में यह कोड जोड़ें:
const INPUT_SHAPE = [NUM_FRAMES, 232, 1];
let model;

async function train() {
 toggleButtons(false);
 const ys = tf.oneHot(examples.map(e => e.label), 3);
 const xsShape = [examples.length, ...INPUT_SHAPE];
 const xs = tf.tensor(flatten(examples.map(e => e.vals)), xsShape);

 await model.fit(xs, ys, {
   batchSize: 16,
   epochs: 10,
   callbacks: {
     onEpochEnd: (epoch, logs) => {
       document.querySelector('#console').textContent =
           `Accuracy: ${(logs.acc * 100).toFixed(1)}% Epoch: ${epoch + 1}`;
     }
   }
 });
 tf.dispose([xs, ys]);
 toggleButtons(true);
}

function buildModel() {
 model = tf.sequential();
 model.add(tf.layers.depthwiseConv2d({
   depthMultiplier: 8,
   kernelSize: [NUM_FRAMES, 3],
   activation: 'relu',
   inputShape: INPUT_SHAPE
 }));
 model.add(tf.layers.maxPooling2d({poolSize: [1, 2], strides: [2, 2]}));
 model.add(tf.layers.flatten());
 model.add(tf.layers.dense({units: 3, activation: 'softmax'}));
 const optimizer = tf.train.adam(0.01);
 model.compile({
   optimizer,
   loss: 'categoricalCrossentropy',
   metrics: ['accuracy']
 });
}

function toggleButtons(enable) {
 document.querySelectorAll('button').forEach(b => b.disabled = !enable);
}

function flatten(tensors) {
 const size = tensors[0].length;
 const result = new Float32Array(tensors.length * size);
 tensors.forEach((arr, i) => result.set(arr, i * size));
 return result;
}
  1. ऐप्लिकेशन लोड होने के बाद, buildModel() पर कॉल करें:
async function app() {
 recognizer = speechCommands.create('BROWSER_FFT');
 await recognizer.ensureModelLoaded();
 // Add this line.
 buildModel();
}

इस समय, ऐप्लिकेशन को रीफ़्रेश करने पर आपको नया "ट्रेन करें" बटन दिखेगा. डेटा को फिर से इकट्ठा करके और "ट्रेन करें" पर क्लिक करके, ट्रेनिंग की जांच की जा सकती है. इसके अलावा, अनुमान लगाने के साथ-साथ ट्रेनिंग की जांच करने के लिए, चरण 10 तक इंतज़ार किया जा सकता है.

बारीकी से समझना

हम दो काम कर रहे हैं: buildModel() मॉडल के आर्किटेक्चर को तय करता है और train() इकट्ठा किए गए डेटा का इस्तेमाल करके मॉडल को ट्रेन करता है.

मॉडल आर्किटेक्चर

इस मॉडल में चार लेयर होती हैं: एक कनवोल्यूशनल लेयर, जो ऑडियो डेटा को प्रोसेस करती है. इसे स्पेक्ट्रोग्राम के तौर पर दिखाया जाता है. दूसरी, मैक्स पूल लेयर. तीसरी, फ़्लैटन लेयर. चौथी, डेंस लेयर. यह तीन कार्रवाइयों के साथ मैप होती है:

model = tf.sequential();
 model.add(tf.layers.depthwiseConv2d({
   depthMultiplier: 8,
   kernelSize: [NUM_FRAMES, 3],
   activation: 'relu',
   inputShape: INPUT_SHAPE
 }));
 model.add(tf.layers.maxPooling2d({poolSize: [1, 2], strides: [2, 2]}));
 model.add(tf.layers.flatten());
 model.add(tf.layers.dense({units: 3, activation: 'softmax'}));

मॉडल का इनपुट शेप [NUM_FRAMES, 232, 1] है. इसमें हर फ़्रेम 23 मि॰से॰ का ऑडियो होता है. इसमें 232 नंबर होते हैं, जो अलग-अलग फ़्रीक्वेंसी से जुड़े होते हैं. 232 को इसलिए चुना गया, क्योंकि यह इंसानी आवाज़ को कैप्चर करने के लिए ज़रूरी फ़्रीक्वेंसी बकेट की संख्या है. इस कोडलैब में, हम तीन फ़्रेम वाले सैंपल (~70 मि॰से॰ के सैंपल) का इस्तेमाल कर रहे हैं. ऐसा इसलिए, क्योंकि हमें स्लाइडर को कंट्रोल करने के लिए पूरे शब्दों के बजाय आवाज़ें बनानी हैं.

हम अपने मॉडल को ट्रेनिंग के लिए तैयार करते हैं:

const optimizer = tf.train.adam(0.01);
 model.compile({
   optimizer,
   loss: 'categoricalCrossentropy',
   metrics: ['accuracy']
 });

हम डीप लर्निंग में इस्तेमाल होने वाले सामान्य ऑप्टिमाइज़र, ऐडम ऑप्टिमाइज़र का इस्तेमाल करते हैं. साथ ही, क्लासिफ़िकेशन के लिए इस्तेमाल होने वाले स्टैंडर्ड लॉस फ़ंक्शन, categoricalCrossEntropy का इस्तेमाल करते हैं. संक्षेप में कहें, तो इससे यह पता चलता है कि अनुमानित संभावनाएं (हर क्लास के लिए एक संभावना) सही क्लास में 100% संभावना और अन्य सभी क्लास के लिए 0% संभावना होने से कितनी दूर हैं. हम मॉनिटर करने के लिए, accuracy को मेट्रिक के तौर पर भी उपलब्ध कराते हैं. इससे हमें ट्रेनिंग के हर युग के बाद, मॉडल के सही जवाब देने वाले उदाहरणों का प्रतिशत पता चलेगा.

ट्रेनिंग

ट्रेनिंग, डेटा पर 10 बार (युग) होती है. इसमें बैच का साइज़ 16 होता है. इसका मतलब है कि एक बार में 16 उदाहरणों को प्रोसेस किया जाता है. साथ ही, यूज़र इंटरफ़ेस (यूआई) में मौजूदा सटीकता दिखती है:

await model.fit(xs, ys, {
   batchSize: 16,
   epochs: 10,
   callbacks: {
     onEpochEnd: (epoch, logs) => {
       document.querySelector('#console').textContent =
           `Accuracy: ${(logs.acc * 100).toFixed(1)}% Epoch: ${epoch + 1}`;
     }
   }
 });

9. स्लाइडर को रीयल-टाइम में अपडेट करना

अब जब हमारे पास मॉडल को ट्रेन करने की सुविधा है, तो आइए रीयल-टाइम में अनुमान लगाने और स्लाइडर को मूव करने के लिए कोड जोड़ें. इसे index.html में "Train" बटन के ठीक बाद जोड़ें:

<br/><br/>
<button id="listen" onclick="listen()">Listen</button>
<input type="range" id="output" min="0" max="10" step="0.1">

साथ ही, index.js में यह कोड जोड़ें:

async function moveSlider(labelTensor) {
 const label = (await labelTensor.data())[0];
 document.getElementById('console').textContent = label;
 if (label == 2) {
   return;
 }
 let delta = 0.1;
 const prevValue = +document.getElementById('output').value;
 document.getElementById('output').value =
     prevValue + (label === 0 ? -delta : delta);
}

function listen() {
 if (recognizer.isListening()) {
   recognizer.stopListening();
   toggleButtons(true);
   document.getElementById('listen').textContent = 'Listen';
   return;
 }
 toggleButtons(false);
 document.getElementById('listen').textContent = 'Stop';
 document.getElementById('listen').disabled = false;

 recognizer.listen(async ({spectrogram: {frameSize, data}}) => {
   const vals = normalize(data.subarray(-frameSize * NUM_FRAMES));
   const input = tf.tensor(vals, [1, ...INPUT_SHAPE]);
   const probs = model.predict(input);
   const predLabel = probs.argMax(1);
   await moveSlider(predLabel);
   tf.dispose([input, probs, predLabel]);
 }, {
   overlapFactor: 0.999,
   includeSpectrogram: true,
   invokeCallbackOnNoiseAndUnknown: true
 });
}

बारीकी से समझना

रीयल-टाइम में अनुमान

listen() माइक्रोफ़ोन की आवाज़ सुनता है और रीयल टाइम में अनुमान लगाता है. यह कोड, collect() तरीके से काफ़ी मिलता-जुलता है. यह रॉ स्पेक्ट्रोग्राम को सामान्य करता है और आखिरी NUM_FRAMES फ़्रेम को छोड़कर बाकी सभी फ़्रेम हटा देता है. इन दोनों में सिर्फ़ यह अंतर है कि हम अनुमान पाने के लिए, ट्रेन किए गए मॉडल को भी कॉल करते हैं:

const probs = model.predict(input);
const predLabel = probs.argMax(1);
await moveSlider(predLabel);

model.predict(input) का आउटपुट, [1, numClasses] शेप का एक Tensor होता है. यह क्लास की संख्या के हिसाब से प्रॉबबिलिटी डिस्ट्रिब्यूशन दिखाता है. आसान शब्दों में कहें, तो यह संभावित आउटपुट क्लास के लिए कॉन्फ़िडेंस का एक सेट है, जिसका योग 1 होता है. टेंसर का बाहरी डाइमेंशन 1 है, क्योंकि यह बैच का साइज़ है (एक उदाहरण).

संभाव्यता डिस्ट्रिब्यूशन को एक ऐसे पूर्णांक में बदलने के लिए जो सबसे संभावित क्लास को दिखाता है, हम probs.argMax(1) को कॉल करते हैं. यह सबसे ज़्यादा संभावना वाले क्लास इंडेक्स को दिखाता है. हम ऐक्सिस पैरामीटर के तौर पर "1" पास करते हैं, क्योंकि हमें आखिरी डाइमेंशन, numClasses पर argMax का हिसाब लगाना है.

स्लाइडर को अपडेट करना

अगर लेबल 0 ("बाईं ओर") है, तो moveSlider() स्लाइडर की वैल्यू को कम करता है. अगर लेबल 1 ("दाईं ओर") है, तो वैल्यू को बढ़ाता है. अगर लेबल 2 ("नॉइज़") है, तो वैल्यू को अनदेखा करता है.

टेंसर हटाना

GPU मेमोरी को खाली करने के लिए, हमें आउटपुट टेंसर पर tf.dispose() को मैन्युअल तरीके से कॉल करना होगा. मैन्युअल tf.dispose() के बजाय, फ़ंक्शन कॉल को tf.tidy() में रैप किया जा सकता है. हालांकि, इसका इस्तेमाल एसिंक फ़ंक्शन के साथ नहीं किया जा सकता.

   tf.dispose([input, probs, predLabel]);

10. फ़ाइनल ऐप्लिकेशन की जांच करना

अपने ब्राउज़र में index.html खोलें. इसके बाद, डेटा इकट्ठा करें. ऐसा ही आपने पिछले सेक्शन में किया था. इसमें तीन बटन, तीन कमांड के हिसाब से काम करते हैं. डेटा इकट्ठा करते समय, हर बटन को तीन से चार सेकंड तक दबाकर रखें.

उदाहरण इकट्ठा करने के बाद, "ट्रेन करें" बटन दबाएं. इससे मॉडल को ट्रेनिंग देने की प्रोसेस शुरू हो जाएगी. साथ ही, आपको मॉडल की सटीकता 90% से ज़्यादा दिखेगी. अगर आपको मॉडल की अच्छी परफ़ॉर्मेंस नहीं मिलती है, तो ज़्यादा डेटा इकट्ठा करने की कोशिश करें.

ट्रेनिंग पूरी होने के बाद, माइक्रोफ़ोन से अनुमान लगाने और स्लाइडर को कंट्रोल करने के लिए, "सुनें" बटन दबाएं!

ज़्यादा ट्यूटोरियल देखने के लिए, http://js.tensorflow.org/ पर जाएं.