TensorFlow.js — การจดจำตัวเลขที่เขียนด้วยลายมือด้วย CNN

1. บทนำ

ในบทแนะนำนี้ เราจะสร้างโมเดล TensorFlow.js เพื่อจดจำตัวเลขที่เขียนด้วยลายมือผ่านโครงข่ายระบบประสาทเทียมแบบ Convolutional อันดับแรก เราจะฝึกตัวแยกประเภทโดยให้มี "ลักษณะ" รูปภาพตัวเลขที่เขียนด้วยลายมือและป้ายชื่อได้หลายพันภาพ จากนั้นเราจะประเมินความแม่นยำของตัวแยกประเภทโดยใช้ข้อมูลทดสอบที่โมเดลไม่เคยเห็นมาก่อน

งานนี้ถือเป็นงานการแยกประเภทเนื่องจากเรากำลังฝึกโมเดลให้กำหนดหมวดหมู่ (ตัวเลขที่ปรากฏในรูปภาพ) ให้กับรูปภาพอินพุต เราจะฝึกโมเดลด้วยการแสดงตัวอย่างอินพุตหลายๆ อย่างพร้อมกับเอาต์พุตที่ถูกต้อง ซึ่งเรียกว่าการเรียนรู้แบบมีการควบคุมดูแล

สิ่งที่คุณจะสร้าง

คุณจะได้สร้างหน้าเว็บที่ใช้ TensorFlow.js เพื่อฝึกโมเดลในเบราว์เซอร์ ด้วยภาพขาวดำที่มีขนาดเฉพาะ ระบบจะจำแนกว่าตัวเลขใดที่ปรากฏในภาพ ขั้นตอนที่เกี่ยวข้องมีดังนี้

  • โหลดข้อมูล
  • กำหนดสถาปัตยกรรมของโมเดล
  • ฝึกโมเดลและตรวจสอบประสิทธิภาพขณะฝึก
  • ประเมินโมเดลที่ฝึกโดยทำการคาดการณ์

สิ่งที่คุณจะได้เรียนรู้

  • ไวยากรณ์ TensorFlow.js สำหรับการสร้างโมเดล Convolutional โดยใช้ TensorFlow.js Layers API
  • การสร้างงานการแยกประเภทใน TensorFlow.js
  • วิธีตรวจสอบการฝึกทำงานในเบราว์เซอร์โดยใช้ไลบรารี tfjs-vis

สิ่งที่คุณต้องมี

และคุณควรคุ้นเคยกับเนื้อหาในบทแนะนำการฝึกอบรมแรกของเราด้วย

2. ตั้งค่า

สร้างหน้า HTML และรวม JavaScript

96914ff65fc3b74c.pngคัดลอกโค้ดต่อไปนี้ลงในไฟล์ HTML ชื่อ

index.html

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>TensorFlow.js Tutorial</title>

  <!-- Import TensorFlow.js -->
  <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@1.0.0/dist/tf.min.js"></script>
  <!-- Import tfjs-vis -->
  <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-vis@1.0.2/dist/tfjs-vis.umd.min.js"></script>

  <!-- Import the data file -->
  <script src="data.js" type="module"></script>

  <!-- Import the main script file -->
  <script src="script.js" type="module"></script>

</head>

<body>
</body>
</html>

สร้างไฟล์ JavaScript สำหรับข้อมูลและโค้ด

  1. ในโฟลเดอร์เดียวกับไฟล์ HTML ข้างต้น ให้สร้างไฟล์ชื่อ data.js แล้วคัดลอกเนื้อหาจากลิงก์นี้ไปยังไฟล์ดังกล่าว
  2. ในโฟลเดอร์เดียวกับขั้นตอนที่ 1 ให้สร้างไฟล์ชื่อ script.js และวางโค้ดต่อไปนี้ลงในไฟล์
console.log('Hello TensorFlow');

ทดสอบเลย

ตอนนี้คุณได้สร้างไฟล์ HTML และ JavaScript แล้ว โปรดทดสอบ เปิดไฟล์ index.html ในเบราว์เซอร์และเปิดคอนโซล Devtools

หากทุกอย่างทำงานเป็นปกติ ควรสร้างตัวแปรร่วม 2 รายการ tf คือการอ้างอิงไลบรารี TensorFlow.js ขณะที่ tfvis จะอ้างอิงไปยังไลบรารี tfjs-vis

คุณควรจะเห็นข้อความว่า Hello TensorFlow, หากเป็นเช่นนั้น คุณก็พร้อมที่จะทำขั้นตอนต่อไปแล้ว

3. โหลดข้อมูล

ในบทแนะนำนี้ คุณจะได้ฝึกโมเดลให้เรียนรู้การรู้จำตัวเลขในภาพเหมือนกับภาพด้านล่าง รูปภาพเหล่านี้เป็นรูปภาพโทนสีเทาขนาด 28x28 พิกเซลจากชุดข้อมูลที่เรียกว่า MNIST

Mnist 4 Mnist 3 Mnist 8

เราได้ให้โค้ดสำหรับโหลดรูปภาพเหล่านี้จากไฟล์สไปรท์พิเศษ (~10 MB) ที่เราสร้างไว้ให้คุณเพื่อให้เราสามารถมุ่งเน้นไปที่ส่วนที่ฝึกได้

คุณสามารถศึกษาไฟล์ data.js เพื่อทำความเข้าใจวิธีโหลดข้อมูลได้ หรือเมื่อคุณทำตามบทแนะนำนี้เสร็จแล้ว ให้สร้างวิธีการโหลดข้อมูลของคุณเอง

โค้ดที่ระบุมีคลาส MnistData ที่มีเมธอดสาธารณะ 2 วิธี ได้แก่

  • nextTrainBatch(batchSize): แสดงผลกลุ่มรูปภาพแบบสุ่มและป้ายกำกับจากชุดการฝึก
  • nextTestBatch(batchSize): แสดงกลุ่มรูปภาพและป้ายกำกับจากชุดทดสอบ

นอกจากนี้ คลาส MnistData ยังมีขั้นตอนสำคัญในการสับเปลี่ยนและการปรับมาตรฐานข้อมูลด้วย

มีรูปภาพทั้งหมด 65,000 รูป เราจะใช้รูปภาพสูงสุด 55,000 รูปในการฝึกโมเดล และจะบันทึกรูปภาพ 10,000 รูปที่เราใช้เพื่อทดสอบประสิทธิภาพของโมเดลเมื่อดำเนินการเสร็จ และเราจะทำแบบนั้นทั้งหมดในเบราว์เซอร์

มาโหลดข้อมูลและทดสอบว่าโหลดได้ถูกต้องหรือไม่

96914ff65fc3b74c.png เพิ่มโค้ดต่อไปนี้ลงในไฟล์ Script.js

import {MnistData} from './data.js';

async function showExamples(data) {
  // Create a container in the visor
  const surface =
    tfvis.visor().surface({ name: 'Input Data Examples', tab: 'Input Data'});  

  // Get the examples
  const examples = data.nextTestBatch(20);
  const numExamples = examples.xs.shape[0];
  
  // Create a canvas element to render each example
  for (let i = 0; i < numExamples; i++) {
    const imageTensor = tf.tidy(() => {
      // Reshape the image to 28x28 px
      return examples.xs
        .slice([i, 0], [1, examples.xs.shape[1]])
        .reshape([28, 28, 1]);
    });
    
    const canvas = document.createElement('canvas');
    canvas.width = 28;
    canvas.height = 28;
    canvas.style = 'margin: 4px;';
    await tf.browser.toPixels(imageTensor, canvas);
    surface.drawArea.appendChild(canvas);

    imageTensor.dispose();
  }
}

async function run() {  
  const data = new MnistData();
  await data.load();
  await showExamples(data);
}

document.addEventListener('DOMContentLoaded', run);

รีเฟรชหน้าเว็บ จากนั้นหลังจากผ่านไป 2-3 วินาที คุณจะเห็นแผงทางด้านซ้ายพร้อมด้วยรูปภาพจำนวนหนึ่ง

6dff857738b54eed.png

4. กำหนดแนวคิดของงานของเรา

ข้อมูลอินพุตของเรามีลักษณะดังนี้

6dff857738b54eed.png

เป้าหมายของเราคือการฝึกโมเดลที่ใช้รูปภาพ 1 รูปและเรียนรู้ที่จะคาดการณ์คะแนนของแต่ละคลาสที่เป็นไปได้ทั้ง 10 คลาสที่อาจมีรูปภาพดังกล่าว (ตัวเลข 0-9)

แต่ละภาพมีความกว้าง 28 พิกเซล สูง 28 พิกเซล และมีช่องสี 1 ช่องเช่นเดียวกับรูปภาพสีขาว-ดำ ดังนั้นรูปร่างของแต่ละรูปคือ [28, 28, 1]

อย่าลืมว่าเราสร้างแผนที่แบบ 1 ถึง 10 รวมถึงรูปร่างของตัวอย่างอินพุตแต่ละรายการด้วย เพราะเป็นส่วนสำคัญสำหรับส่วนถัดไป

5. กำหนดสถาปัตยกรรมโมเดล

ในส่วนนี้ เราจะเขียนโค้ดเพื่ออธิบายสถาปัตยกรรมโมเดล สถาปัตยกรรมโมเดลเป็นคำพูดที่ฟังดูเพลินตาในการบอกถึง "โมเดลจะเรียกใช้ฟังก์ชันใดเมื่อโมเดลกำลังทำงาน" หรืออีกทางเลือกหนึ่งคือ "โมเดลของเราจะใช้อัลกอริทึมใดในการคำนวณคำตอบ"

ในแมชชีนเลิร์นนิง เรากำหนดสถาปัตยกรรม (หรืออัลกอริทึม) และให้กระบวนการฝึกเรียนรู้พารามิเตอร์ของอัลกอริทึมนั้น

96914ff65fc3b74c.png เพิ่มฟังก์ชันต่อไปนี้ลงใน

script.js ไฟล์เพื่อกำหนดสถาปัตยกรรมโมเดล

function getModel() {
  const model = tf.sequential();
  
  const IMAGE_WIDTH = 28;
  const IMAGE_HEIGHT = 28;
  const IMAGE_CHANNELS = 1;  
  
  // In the first layer of our convolutional neural network we have 
  // to specify the input shape. Then we specify some parameters for 
  // the convolution operation that takes place in this layer.
  model.add(tf.layers.conv2d({
    inputShape: [IMAGE_WIDTH, IMAGE_HEIGHT, IMAGE_CHANNELS],
    kernelSize: 5,
    filters: 8,
    strides: 1,
    activation: 'relu',
    kernelInitializer: 'varianceScaling'
  }));

  // The MaxPooling layer acts as a sort of downsampling using max values
  // in a region instead of averaging.  
  model.add(tf.layers.maxPooling2d({poolSize: [2, 2], strides: [2, 2]}));
  
  // Repeat another conv2d + maxPooling stack. 
  // Note that we have more filters in the convolution.
  model.add(tf.layers.conv2d({
    kernelSize: 5,
    filters: 16,
    strides: 1,
    activation: 'relu',
    kernelInitializer: 'varianceScaling'
  }));
  model.add(tf.layers.maxPooling2d({poolSize: [2, 2], strides: [2, 2]}));
  
  // Now we flatten the output from the 2D filters into a 1D vector to prepare
  // it for input into our last layer. This is common practice when feeding
  // higher dimensional data to a final classification output layer.
  model.add(tf.layers.flatten());

  // Our last layer is a dense layer which has 10 output units, one for each
  // output class (i.e. 0, 1, 2, 3, 4, 5, 6, 7, 8, 9).
  const NUM_OUTPUT_CLASSES = 10;
  model.add(tf.layers.dense({
    units: NUM_OUTPUT_CLASSES,
    kernelInitializer: 'varianceScaling',
    activation: 'softmax'
  }));

  
  // Choose an optimizer, loss function and accuracy metric,
  // then compile and return the model
  const optimizer = tf.train.adam();
  model.compile({
    optimizer: optimizer,
    loss: 'categoricalCrossentropy',
    metrics: ['accuracy'],
  });

  return model;
}

มาดูรายละเอียดเพิ่มเติมกัน

คอนโวลูชัน

model.add(tf.layers.conv2d({
  inputShape: [IMAGE_WIDTH, IMAGE_HEIGHT, IMAGE_CHANNELS],
  kernelSize: 5,
  filters: 8,
  strides: 1,
  activation: 'relu',
  kernelInitializer: 'varianceScaling'
}));

เราจะใช้โมเดลตามลำดับ

เราใช้เลเยอร์ conv2d แทนเลเยอร์ที่หนาแน่น เราไม่สามารถลงรายละเอียดทั้งหมดเกี่ยวกับวิธีการทำงานของ Convolution ได้ แต่เรามีแหล่งข้อมูลบางส่วนต่อไปนี้ซึ่งอธิบายการดำเนินการที่สำคัญ

มาดูรายละเอียดของแต่ละอาร์กิวเมนต์ในออบเจ็กต์การกำหนดค่าสำหรับ conv2d กัน

  • inputShape รูปร่างของข้อมูลที่จะไหลเข้าสู่เลเยอร์แรกของโมเดล ในกรณีนี้ ตัวอย่างของ MNIST คือรูปภาพขาวดำขนาด 28x28 พิกเซล รูปแบบ Canonical สำหรับข้อมูลรูปภาพคือ [row, column, depth] เราจึงต้องการกำหนดค่ารูปร่าง [28, 28, 1] 28 แถวและคอลัมน์สำหรับจำนวนพิกเซลในแต่ละด้าน และความลึก 1 เนื่องจากรูปภาพของเรามีเพียงช่องสีเพียง 1 ช่อง โปรดทราบว่าเราไม่ได้ระบุขนาดกลุ่มในรูปร่างอินพุต เลเยอร์ได้รับการออกแบบมาให้รองรับขนาดกลุ่ม เพื่อให้คุณสามารถส่งผ่าน Tensor ขนาดกลุ่มใดก็ได้ในระหว่างการอนุมาน
  • kernelSize ขนาดของหน้าต่างตัวกรองคอนโวลูชันแบบเลื่อนที่จะนำไปใช้กับข้อมูลอินพุต เราตั้งค่า kernelSize เป็น 5 ซึ่งระบุหน้าต่างคอนโวลูชันแบบสี่เหลี่ยมจัตุรัส 5x5
  • filters จำนวนหน้าต่างตัวกรองขนาด kernelSize ที่จะใช้กับข้อมูลอินพุต ในที่นี้เราจะใช้ตัวกรอง 8 รายการกับข้อมูล
  • strides "ขนาดก้าว" ของหน้าต่างเลื่อน เช่น จำนวนพิกเซลที่ตัวกรองจะเปลี่ยนในแต่ละครั้งที่เลื่อนรูปภาพ ในที่นี้เราจะระบุระยะก้าวเป็น 1 ซึ่งหมายความว่าตัวกรองจะเลื่อนเหนือรูปภาพเป็นขั้นๆ ที่ 1 พิกเซล
  • activation ฟังก์ชันเปิดใช้งานที่จะใช้กับข้อมูลหลังจากคอนโวลูชันเสร็จสมบูรณ์ ในกรณีนี้ เราจะใช้ฟังก์ชัน Rectified Linear Unit (ReLU) ซึ่งเป็นฟังก์ชันเปิดใช้งานที่พบได้ทั่วไปในโมเดล ML
  • kernelInitializer เมธอดที่ใช้ในการเริ่มต้นแบบสุ่มให้น้ำหนักของโมเดล ซึ่งสำคัญมากต่อการฝึกไดนามิก เราจะไม่พูดถึงรายละเอียดของการเริ่มต้นที่นี่ แต่ VarianceScaling (ใช้ที่นี่) ถือเป็นตัวเลือกเริ่มต้นที่ดี

ทำให้การแสดงข้อมูลของเราเป็นจำนวนมากขึ้น

model.add(tf.layers.flatten());

รูปภาพเป็นข้อมูลที่มีมิติสูง และการดำเนินการ Convolution มักเพิ่มขนาดของข้อมูลที่มีผลกับรูป ก่อนที่จะส่งข้อมูลไปยังชั้นการจัดประเภทขั้นสุดท้าย เราต้องแยกข้อมูลให้เป็นอาร์เรย์แบบยาว 1 รายการ เลเยอร์ที่หนาแน่น (ซึ่งเราใช้เป็นเลเยอร์สุดท้าย) ใช้เวลาเพียง tensor1d วินาที ดังนั้นขั้นตอนนี้จึงมักเกิดขึ้นในงานแยกประเภทจำนวนมาก

คำนวณการแจกแจงความน่าจะเป็นสุดท้าย

const NUM_OUTPUT_CLASSES = 10;
model.add(tf.layers.dense({
  units: NUM_OUTPUT_CLASSES,
  kernelInitializer: 'varianceScaling',
  activation: 'softmax'
}));

เราจะใช้เลเยอร์แบบหนาแน่นที่มีการเปิดใช้งาน Softmax เพื่อคำนวณการแจกแจงความน่าจะเป็นของคลาสที่เป็นไปได้ทั้ง 10 คลาส ชั้นเรียนที่มีคะแนนสูงสุดจะเป็นตัวเลขที่คาดการณ์ไว้

เลือกฟังก์ชันการเพิ่มประสิทธิภาพและการสูญเสีย

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

เรารวบรวมโมเดลที่ระบุเครื่องมือเพิ่มประสิทธิภาพ ฟังก์ชันการสูญเสีย และเมตริกที่ต้องการติดตาม

เราใช้ categoricalCrossentropy เป็นฟังก์ชันการสูญเสียเนื้อหา ซึ่งแตกต่างจากบทแนะนำแรก ซึ่งชื่อจะบ่งบอกลักษณะนี้เมื่อเอาต์พุตของโมเดลเป็นการแจกแจงความน่าจะเป็น categoricalCrossentropy วัดข้อผิดพลาดระหว่างการกระจายความน่าจะเป็นที่สร้างโดยชั้นสุดท้ายของโมเดลของเรากับการกระจายความน่าจะเป็นที่กำหนดโดยป้ายกำกับจริงของเรา

ตัวอย่างเช่น หากตัวเลขของเราแสดงถึง 7 จริงๆ เราอาจได้ผลลัพธ์ต่อไปนี้

ดัชนี

0

1

2

3

4

5

6

7

8

9

ป้ายกำกับจริง

0

0

0

0

0

0

0

1

0

0

การคาดการณ์

0.1

0.01

0.01

0.01

0.20

0.01

0.01

0.60

0.03

0.02

ครอสเอนโทรปีเชิงหมวดหมู่จะสร้างตัวเลขที่ระบุความใกล้เคียงของเวกเตอร์การคาดการณ์กับเวกเตอร์ป้ายกำกับที่แท้จริงของเรา

การแสดงข้อมูลที่ใช้สำหรับป้ายกำกับที่นี่เรียกว่าการเข้ารหัสแบบฮอตเดียว และพบบ่อยในโจทย์การจัดประเภท แต่ละคลาสมีความน่าจะเป็นที่เกี่ยวข้องสำหรับแต่ละตัวอย่าง เมื่อเรารู้แน่ชัดแล้วว่าควรจะเป็นอะไร เราสามารถตั้งค่าความน่าจะเป็นเป็น 1 และความน่าจะเป็นอื่นๆ เป็น 0 ดูข้อมูลเพิ่มเติมเกี่ยวกับการเข้ารหัสแบบ One-hot ได้ในหน้านี้

เมตริกอื่นที่เราจะตรวจสอบคือ accuracy ซึ่งสำหรับปัญหาการจัดประเภทคือเปอร์เซ็นต์ของการคาดการณ์ที่ถูกต้องจากการคาดการณ์ทั้งหมด

6. ฝึกโมเดล

96914ff65fc3b74c.pngคัดลอกฟังก์ชันต่อไปนี้ไปยังไฟล์ Script.js

async function train(model, data) {
  const metrics = ['loss', 'val_loss', 'acc', 'val_acc'];
  const container = {
    name: 'Model Training', tab: 'Model', styles: { height: '1000px' }
  };
  const fitCallbacks = tfvis.show.fitCallbacks(container, metrics);
  
  const BATCH_SIZE = 512;
  const TRAIN_DATA_SIZE = 5500;
  const TEST_DATA_SIZE = 1000;

  const [trainXs, trainYs] = tf.tidy(() => {
    const d = data.nextTrainBatch(TRAIN_DATA_SIZE);
    return [
      d.xs.reshape([TRAIN_DATA_SIZE, 28, 28, 1]),
      d.labels
    ];
  });

  const [testXs, testYs] = tf.tidy(() => {
    const d = data.nextTestBatch(TEST_DATA_SIZE);
    return [
      d.xs.reshape([TEST_DATA_SIZE, 28, 28, 1]),
      d.labels
    ];
  });

  return model.fit(trainXs, trainYs, {
    batchSize: BATCH_SIZE,
    validationData: [testXs, testYs],
    epochs: 10,
    shuffle: true,
    callbacks: fitCallbacks
  });
}

96914ff65fc3b74c.png จากนั้นเพิ่มโค้ดต่อไปนี้ลงใน

run ฟังก์ชัน

const model = getModel();
tfvis.show.modelSummary({name: 'Model Architecture', tab: 'Model'}, model);
  
await train(model, data);

รีเฟรชหน้าเว็บ แล้วหลังจากนั้นสักครู่คุณจะเห็นกราฟรายงานความคืบหน้าของการฝึก

a2c7628dc47d465.png

มาดูรายละเอียดเพิ่มเติมกัน

ตรวจสอบเมตริก

const metrics = ['loss', 'val_loss', 'acc', 'val_acc'];

ในส่วนนี้ เราจะตัดสินใจว่าเมตริกใด ที่เราจะตรวจสอบ เราจะตรวจสอบการสูญเสียและความแม่นยำในชุดการฝึก รวมถึงการสูญเสียและความแม่นยำในชุดการตรวจสอบ (val_loss และ val_acc ตามลำดับ) เราจะพูดถึงชุดการตรวจสอบเพิ่มเติมด้านล่าง

เตรียมข้อมูลเป็น Tensor

const BATCH_SIZE = 512;
const TRAIN_DATA_SIZE = 5500;
const TEST_DATA_SIZE = 1000;

const [trainXs, trainYs] = tf.tidy(() => {
  const d = data.nextTrainBatch(TRAIN_DATA_SIZE);
  return [
    d.xs.reshape([TRAIN_DATA_SIZE, 28, 28, 1]),
    d.labels
  ];
});

const [testXs, testYs] = tf.tidy(() => {
  const d = data.nextTestBatch(TEST_DATA_SIZE);
  return [
    d.xs.reshape([TEST_DATA_SIZE, 28, 28, 1]),
    d.labels
  ];
});

เราสร้างชุดข้อมูล 2 ชุด ชุดการฝึกที่เราจะใช้ฝึกโมเดล และชุดการตรวจสอบที่เราจะทดสอบโมเดลในตอนท้ายของแต่ละ Epoch อย่างไรก็ตาม ข้อมูลในชุดการตรวจสอบจะไม่แสดงต่อโมเดลระหว่างการฝึก

คลาสข้อมูลที่เราให้ไว้ช่วยให้คุณรับ Tensor จากข้อมูลรูปภาพได้อย่างง่ายดาย แต่เรายังคงปรับเปลี่ยน Tensor ให้เป็นรูปร่างตามที่โมเดล [num_examples, image_width, image_height, channels] อยู่ก่อนที่จะป้อนสิ่งเหล่านี้ลงในโมเดล สำหรับชุดข้อมูลแต่ละชุด เรามีทั้งอินพุต (X) และป้ายกำกับ (Y)

return model.fit(trainXs, trainYs, {
  batchSize: BATCH_SIZE,
  validationData: [testXs, testYs],
  epochs: 10,
  shuffle: true,
  callbacks: fitCallbacks
});

เราเรียก model.fit เพื่อเริ่มลูปการฝึก นอกจากนี้เรายังผ่านพร็อพเพอร์ตี้ ValidData เพื่อระบุว่าโมเดลควรใช้ข้อมูลใดเพื่อทดสอบตัวเองหลังจากแต่ละ Epoch (แต่ไม่ใช้สำหรับการฝึก)

หากเราใช้ประโยชน์จากข้อมูลการฝึกได้ดี แต่ไม่ตรงกับข้อมูลการตรวจสอบ นั่นหมายความว่าโมเดลมีแนวโน้มที่จะไม่เหมาะกับข้อมูลการฝึก และจะทำให้ข้อมูลทั่วไปเป็นข้อมูลที่ยังไม่เคยเห็นมาก่อนได้

7. ประเมินโมเดล

ความแม่นยําในการตรวจสอบเป็นการคาดการณ์ที่ดีว่าโมเดลของเราจะดำเนินการกับข้อมูลที่ไม่เคยเห็นมาก่อนได้ดีเพียงใด (ตราบใดที่ข้อมูลดังกล่าวมีลักษณะคล้ายกับชุดการตรวจสอบบางอย่าง) อย่างไรก็ตาม เราอาจต้องการรายละเอียดเพิ่มเติมเกี่ยวกับประสิทธิภาพในชั้นเรียนต่างๆ

มี 2 วิธีใน tfjs-vis ที่ช่วยคุณได้

96914ff65fc3b74c.png เพิ่มโค้ดต่อไปนี้ที่ด้านล่างของไฟล์ Script.js

const classNames = ['Zero', 'One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight', 'Nine'];

function doPrediction(model, data, testDataSize = 500) {
  const IMAGE_WIDTH = 28;
  const IMAGE_HEIGHT = 28;
  const testData = data.nextTestBatch(testDataSize);
  const testxs = testData.xs.reshape([testDataSize, IMAGE_WIDTH, IMAGE_HEIGHT, 1]);
  const labels = testData.labels.argMax(-1);
  const preds = model.predict(testxs).argMax(-1);

  testxs.dispose();
  return [preds, labels];
}


async function showAccuracy(model, data) {
  const [preds, labels] = doPrediction(model, data);
  const classAccuracy = await tfvis.metrics.perClassAccuracy(labels, preds);
  const container = {name: 'Accuracy', tab: 'Evaluation'};
  tfvis.show.perClassAccuracy(container, classAccuracy, classNames);

  labels.dispose();
}

async function showConfusion(model, data) {
  const [preds, labels] = doPrediction(model, data);
  const confusionMatrix = await tfvis.metrics.confusionMatrix(labels, preds);
  const container = {name: 'Confusion Matrix', tab: 'Evaluation'};
  tfvis.render.confusionMatrix(container, {values: confusionMatrix, tickLabels: classNames});

  labels.dispose();
}

โค้ดนี้ใช้ทำอะไร

  • ทำการคาดคะเน
  • คำนวณเมตริกความแม่นยำ
  • แสดงเมตริก

มาดูรายละเอียดเกี่ยวกับแต่ละขั้นตอนกัน

คาดการณ์

function doPrediction(model, data, testDataSize = 500) {
  const IMAGE_WIDTH = 28;
  const IMAGE_HEIGHT = 28;
  const testData = data.nextTestBatch(testDataSize);
  const testxs = testData.xs.reshape([testDataSize, IMAGE_WIDTH, IMAGE_HEIGHT, 1]);
  const labels = testData.labels.argMax(-1);
  const preds = model.predict(testxs).argMax(-1);

  testxs.dispose();
  return [preds, labels];
}      

ก่อนอื่นเราต้องคาดการณ์ ในส่วนนี้เราจะถ่ายภาพ 500 ภาพและคาดการณ์ตัวเลขที่อยู่ในภาพ (คุณสามารถเพิ่มตัวเลขนี้ในภายหลังเพื่อทดสอบชุดภาพขนาดใหญ่)

ที่สำคัญคือฟังก์ชัน argmax คือสิ่งที่ทำให้เรามีดัชนีของคลาสความน่าจะเป็นสูงสุด อย่าลืมว่าโมเดลจะแสดงความน่าจะเป็นสำหรับแต่ละคลาส ในที่นี้เราจะค้นหาความน่าจะเป็นสูงสุด และกำหนดการใช้งานดังกล่าวเป็นการคาดการณ์

และคุณอาจสังเกตเห็นว่าเราคาดการณ์กับตัวอย่างทั้ง 500 รายการพร้อมกันได้ด้วย นี่คือพลังของการสร้างเวกเตอร์ที่ TensorFlow.js มอบให้

แสดงความแม่นยำตามชั้นเรียน

async function showAccuracy() {
  const [preds, labels] = doPrediction();
  const classAccuracy = await tfvis.metrics.perClassAccuracy(labels, preds);
  const container = { name: 'Accuracy', tab: 'Evaluation' };
  tfvis.show.perClassAccuracy(container, classAccuracy, classNames);

  labels.dispose();
}      

เราสามารถคำนวณความแม่นยำสำหรับแต่ละคลาสได้ด้วยชุดการคาดคะเนและป้ายกำกับ

แสดงเมทริกซ์ความสับสน

async function showConfusion() {
  const [preds, labels] = doPrediction();
  const confusionMatrix = await tfvis.metrics.confusionMatrix(labels, preds);
  const container = { name: 'Confusion Matrix', tab: 'Evaluation' };
  tfvis.render.confusionMatrix(container, {values: confusionMatrix, tickLabels: classNames});

  labels.dispose();
}  

เมทริกซ์ความสับสนคล้ายกับความแม่นยำต่อคลาส แต่จะแบ่งย่อยเพื่อแสดงรูปแบบของการจัดประเภทที่ไม่ถูกต้อง ซึ่งช่วยให้คุณดูได้ว่าโมเดลเกิดความสับสนเกี่ยวกับคลาสคู่ใดหรือไม่

แสดงการประเมิน

96914ff65fc3b74c.png เพิ่มโค้ดต่อไปนี้ไว้ที่ด้านล่างของฟังก์ชันการเรียกใช้เพื่อแสดงการประเมิน

await showAccuracy(model, data);
await showConfusion(model, data);

คุณจะเห็นจอแสดงผลที่มีลักษณะดังนี้

82458197bd5e7f52.png

ยินดีด้วย คุณเพิ่งฝึกโครงข่ายประสาทแบบ Convolutional

8. สรุปประเด็นสำคัญ

การคาดการณ์หมวดหมู่สำหรับข้อมูลอินพุตเรียกว่างานการแยกประเภท

งานการแยกประเภทต้องมีการแสดงข้อมูลที่เหมาะสมสำหรับป้ายกำกับ

  • การแสดงป้ายกำกับทั่วไปรวมถึงการเข้ารหัสหมวดหมู่แบบ Hot

เตรียมข้อมูลของคุณ:

  • คุณควรเก็บข้อมูลบางอย่างไว้ยกเว้นข้อมูลที่โมเดลไม่เคยเห็นระหว่างการฝึก ซึ่งคุณสามารถนำไปใช้เพื่อประเมินโมเดลได้ ซึ่งเรียกว่าชุดการตรวจสอบ

สร้างและเรียกใช้โมเดลโดยทำดังนี้

  • ระบบพบว่าโมเดลคอนโวลูชัน (Convolutional) ทำงานได้ดีในงานรูปภาพ
  • ปัญหาการจำแนกประเภทมักจะใช้ครอสเอนโทรปีเชิงหมวดหมู่สำหรับหน้าที่ที่สูญเสียไป
  • ตรวจสอบการฝึกเพื่อดูว่าการสูญเสียลดลงและความแม่นยำเพิ่มขึ้นหรือไม่

ประเมินโมเดล

  • ตัดสินใจเลือกวิธีประเมินโมเดลหลังจากที่ได้รับการฝึกเพื่อดูว่าโมเดลทำงานได้ดีแค่ไหนกับปัญหาแรกเริ่มที่คุณต้องการแก้ไข
  • เมทริกซ์ความแม่นยำและความสับสนตามคลาสสามารถดูรายละเอียดประสิทธิภาพของโมเดลได้ละเอียดยิ่งขึ้น ไม่ใช่แค่ความแม่นยำโดยรวม