TensorFlow.js — Nhận dạng chữ số viết tay bằng CNN

1. Giới thiệu

Trong hướng dẫn này, chúng ta sẽ xây dựng một mô hình TensorFlow.js để nhận dạng chữ số viết tay bằng mạng nơ-ron tích chập. Trước tiên, chúng ta sẽ huấn luyện bộ phân loại bằng cách để bộ phân loại "xem" hàng nghìn hình ảnh chữ số viết tay và nhãn của chúng. Sau đó, chúng ta sẽ đánh giá độ chính xác của trình phân loại bằng cách sử dụng dữ liệu kiểm thử mà mô hình chưa từng thấy.

Đây được coi là một nhiệm vụ phân loại vì chúng ta đang huấn luyện mô hình để chỉ định một danh mục (chữ số xuất hiện trong hình ảnh) cho hình ảnh đầu vào. Chúng ta sẽ huấn luyện mô hình bằng cách cho mô hình xem nhiều ví dụ về dữ liệu đầu vào cùng với dữ liệu đầu ra chính xác. Đây được gọi là học có giám sát.

Sản phẩm bạn sẽ tạo ra

Bạn sẽ tạo một trang web sử dụng TensorFlow.js để huấn luyện một mô hình trong trình duyệt. Với một hình ảnh đen trắng có kích thước cụ thể, mô hình này sẽ phân loại chữ số xuất hiện trong hình ảnh. Các bước liên quan là:

  • Tải dữ liệu.
  • Xác định cấu trúc của mô hình.
  • Huấn luyện mô hình và theo dõi hiệu suất của mô hình trong quá trình huấn luyện.
  • Đánh giá mô hình đã huấn luyện bằng cách đưa ra một số dự đoán.

Kiến thức bạn sẽ học được

  • Cú pháp TensorFlow.js để tạo các mô hình tích chập bằng cách sử dụng TensorFlow.js Layers API.
  • Xây dựng các tác vụ phân loại trong TensorFlow.js
  • Cách theo dõi quá trình huấn luyện trong trình duyệt bằng thư viện tfjs-vis.

Bạn cần có

  • Phiên bản mới nhất của Chrome hoặc một trình duyệt hiện đại khác hỗ trợ các mô-đun ES6.
  • Một trình chỉnh sửa văn bản, chạy cục bộ trên máy hoặc trên web thông qua một công cụ như Codepen hoặc Glitch.
  • Có kiến thức về HTML, CSS, JavaScript và Chrome DevTools (hoặc công cụ cho nhà phát triển của trình duyệt mà bạn muốn dùng).
  • Hiểu biết khái niệm ở cấp độ cao về Mạng nơ-ron. Nếu bạn cần tìm hiểu hoặc ôn lại, hãy cân nhắc xem video này của 3blue1brown hoặc video này về Học sâu bằng JavaScript của Ashi Krishnan.

Bạn cũng nên nắm vững nội dung trong hướng dẫn đào tạo đầu tiên của chúng tôi.

2. Bắt đầu thiết lập

Tạo một trang HTML và thêm JavaScript

96914ff65fc3b74c.pngSao chép mã sau đây vào một tệp html có tên là

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>

Tạo các tệp JavaScript cho dữ liệu và mã

  1. Trong cùng thư mục với tệp HTML ở trên, hãy tạo một tệp có tên là data.js rồi sao chép nội dung từ đường liên kết này vào tệp đó.
  2. Trong cùng thư mục như bước một, hãy tạo một tệp có tên là script.js rồi đặt đoạn mã sau vào đó.
console.log('Hello TensorFlow');

Dùng thử

Bây giờ bạn đã tạo xong các tệp HTML và JavaScript, hãy kiểm thử các tệp đó. Mở tệp index.html trong trình duyệt và mở bảng điều khiển công cụ cho nhà phát triển.

Nếu mọi thứ đều hoạt động, thì sẽ có 2 biến chung được tạo. tf là thông tin tham chiếu đến thư viện TensorFlow.js, tfvis là thông tin tham chiếu đến thư viện tfjs-vis.

Bạn sẽ thấy thông báo Hello TensorFlow. Nếu thấy thông báo này, bạn đã sẵn sàng chuyển sang bước tiếp theo.

3. Tải dữ liệu

Trong hướng dẫn này, bạn sẽ huấn luyện một mô hình để học cách nhận dạng các chữ số trong hình ảnh như những hình ảnh bên dưới. Đây là những hình ảnh thang độ xám 28x28px trong một tập dữ liệu có tên là MNIST.

mnist 4 mnist 3 mnist 8

Chúng tôi đã cung cấp mã để tải những hình ảnh này từ một tệp sprite đặc biệt (~10 MB) mà chúng tôi đã tạo cho bạn để có thể tập trung vào phần đào tạo.

Bạn có thể nghiên cứu tệp data.js để hiểu cách tải dữ liệu. Hoặc sau khi hoàn tất hướng dẫn này, hãy tạo phương pháp riêng để tải dữ liệu.

Mã được cung cấp chứa một lớp MnistData có 2 phương thức công khai:

  • nextTrainBatch(batchSize): trả về một lô hình ảnh ngẫu nhiên và nhãn của chúng từ tập dữ liệu huấn luyện.
  • nextTestBatch(batchSize): trả về một lô hình ảnh và nhãn của hình ảnh đó từ tập kiểm định

Lớp MnistData cũng thực hiện các bước quan trọng là trộnchuẩn hoá dữ liệu.

Có tổng cộng 65.000 hình ảnh, chúng ta sẽ sử dụng tối đa 55.000 hình ảnh để huấn luyện mô hình, lưu 10.000 hình ảnh mà chúng ta có thể sử dụng để kiểm thử hiệu suất của mô hình sau khi hoàn tất. Và chúng ta sẽ thực hiện tất cả những việc đó trong trình duyệt!

Hãy tải dữ liệu và kiểm tra xem dữ liệu có được tải đúng cách hay không.

96914ff65fc3b74c.png Thêm đoạn mã sau vào tệp 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);

Làm mới trang và sau vài giây, bạn sẽ thấy một bảng điều khiển ở bên trái có một số hình ảnh.

6dff857738b54eed.png

4. Lên ý tưởng cho nhiệm vụ của chúng ta

Dữ liệu đầu vào của chúng ta sẽ có dạng như sau.

6dff857738b54eed.png

Mục tiêu của chúng ta là huấn luyện một mô hình sẽ lấy một hình ảnh và học cách dự đoán điểm số cho từng trong số 10 lớp có thể có mà hình ảnh đó có thể thuộc về (các chữ số từ 0 đến 9).

Mỗi hình ảnh có chiều rộng 28px, chiều cao 28px và có 1 kênh màu vì đây là hình ảnh thang độ xám. Vì vậy, hình dạng của mỗi hình ảnh là [28, 28, 1].

Hãy nhớ rằng chúng ta thực hiện ánh xạ từ 1 đến 10, cũng như hình dạng của từng ví dụ đầu vào, vì điều này rất quan trọng đối với phần tiếp theo.

5. Xác định cấu trúc mô hình

Trong phần này, chúng ta sẽ viết mã để mô tả cấu trúc mô hình. Cấu trúc mô hình là một cách nói hoa mỹ cho "mô hình sẽ chạy những hàm nào khi đang thực thi", hoặc "mô hình sẽ dùng thuật toán nào để tính toán câu trả lời".

Trong học máy, chúng ta xác định một cấu trúc (hoặc thuật toán) và cho phép quy trình huấn luyện tìm hiểu các tham số của thuật toán đó.

96914ff65fc3b74c.png Thêm hàm sau vào

script.js để xác định cấu trúc mô hình

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;
}

Hãy cùng tìm hiểu kỹ hơn về vấn đề này.

Tích chập

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

Ở đây, chúng ta đang sử dụng một mô hình tuần tự.

Chúng tôi đang sử dụng một lớp conv2d thay vì một lớp dày đặc. Chúng ta không thể đi vào tất cả các chi tiết về cách hoạt động của tích chập, nhưng sau đây là một số tài nguyên giải thích hoạt động cơ bản:

Hãy phân tích từng đối số trong đối tượng cấu hình cho conv2d:

  • inputShape. Hình dạng của dữ liệu sẽ chuyển vào lớp đầu tiên của mô hình. Trong trường hợp này, các ví dụ về MNIST là hình ảnh đen trắng có kích thước 28x28 pixel. Định dạng chuẩn cho dữ liệu hình ảnh là [row, column, depth], vì vậy, ở đây chúng ta muốn định cấu hình một hình dạng của [28, 28, 1]. 28 hàng và cột cho số lượng pixel trong mỗi phương diện, và độ sâu là 1 vì hình ảnh của chúng ta chỉ có 1 kênh màu. Xin lưu ý rằng chúng tôi không chỉ định kích thước lô trong hình dạng đầu vào. Các lớp được thiết kế để không phụ thuộc vào kích thước lô, do đó, trong quá trình suy luận, bạn có thể truyền một tensor có kích thước lô bất kỳ.
  • kernelSize. Kích thước của các cửa sổ bộ lọc tích chập trượt sẽ được áp dụng cho dữ liệu đầu vào. Ở đây, chúng ta đặt kernelSize5, chỉ định một cửa sổ tích chập 5x5 hình vuông.
  • filters. Số lượng cửa sổ bộ lọc có kích thước kernelSize cần áp dụng cho dữ liệu đầu vào. Ở đây, chúng ta sẽ áp dụng 8 bộ lọc cho dữ liệu.
  • strides. "Kích cỡ bước" của cửa sổ trượt, tức là số lượng pixel mà bộ lọc sẽ dịch chuyển mỗi khi di chuyển trên hình ảnh. Ở đây, chúng ta chỉ định bước sải là 1, tức là bộ lọc sẽ trượt trên hình ảnh theo các bước 1 pixel.
  • activation. Hàm kích hoạt để áp dụng cho dữ liệu sau khi quá trình tích chập hoàn tất. Trong trường hợp này, chúng ta đang áp dụng hàm Đơn vị tuyến tính được chỉnh lưu (ReLU). Đây là một hàm kích hoạt rất phổ biến trong các mô hình học máy.
  • kernelInitializer. Phương thức dùng để khởi tạo ngẫu nhiên các trọng số mô hình, điều này rất quan trọng đối với động lực huấn luyện. Chúng ta sẽ không đi vào chi tiết về quá trình khởi tạo ở đây, nhưng VarianceScaling (được dùng ở đây) thường là một lựa chọn khởi tạo phù hợp.

Làm phẳng biểu diễn dữ liệu

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

Hình ảnh là dữ liệu có nhiều chiều và các hoạt động tích chập có xu hướng làm tăng kích thước của dữ liệu đã được đưa vào. Trước khi truyền dữ liệu đến lớp phân loại cuối cùng, chúng ta cần làm phẳng dữ liệu thành một mảng dài. Các lớp dày đặc (chúng ta dùng làm lớp cuối cùng) chỉ lấy tensor1d, vì vậy, bước này thường thấy trong nhiều nhiệm vụ phân loại.

Tính toán hàm phân phối xác suất cuối cùng

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

Chúng ta sẽ sử dụng một lớp dày đặc với hàm kích hoạt softmax để tính toán phân phối xác suất trên 10 lớp có thể có. Lớp có điểm số cao nhất sẽ là chữ số được dự đoán.

Chọn một trình tối ưu hoá và hàm tổn thất

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

Chúng ta biên dịch mô hình bằng cách chỉ định một trình tối ưu hoá, hàm tổn thất và các chỉ số mà chúng ta muốn theo dõi.

Khác với hướng dẫn đầu tiên, ở đây chúng ta sử dụng categoricalCrossentropy làm hàm tổn thất. Như tên gọi, hàm này được dùng khi đầu ra của mô hình là một phân phối xác suất. categoricalCrossentropy đo lường sai số giữa hàm phân phối xác suất do lớp cuối cùng của mô hình tạo ra và hàm phân phối xác suất do nhãn thực của chúng tôi cung cấp.

Ví dụ: nếu chữ số của chúng ta thực sự biểu thị số 7, chúng ta có thể có các kết quả sau

Chỉ mục

0

1

2

3

4

5

6

7

8

9

Nhãn thực

0

0

0

0

0

0

0

1

0

0

Dự đoán

0,1

0,01

0,01

0,01

0,2

0,01

0,01

0,60

0,03

0,02

Cross entropy phân loại sẽ tạo ra một con số duy nhất cho biết mức độ tương đồng giữa vectơ dự đoán và vectơ nhãn thực của chúng ta.

Phương thức biểu diễn dữ liệu được dùng ở đây cho các nhãn được gọi là mã one-hot và thường gặp trong các vấn đề về phân loại. Mỗi lớp có một xác suất liên kết với lớp đó cho từng ví dụ. Khi biết chính xác giá trị cần tìm, chúng ta có thể đặt xác suất đó thành 1 và các xác suất khác thành 0. Hãy xem trang này để biết thêm thông tin về mã one-hot.

Chỉ số khác mà chúng ta sẽ theo dõi là accuracy. Đối với vấn đề phân loại, đây là tỷ lệ phần trăm dự đoán chính xác trong tổng số dự đoán.

6. Huấn luyện mô hình

96914ff65fc3b74c.pngSao chép hàm sau vào tệp 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 Sau đó, hãy thêm mã sau vào

run .

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

Làm mới trang và sau vài giây, bạn sẽ thấy một số biểu đồ báo cáo tiến trình huấn luyện.

a2c7628dc47d465.png

Hãy xem xét vấn đề đó chi tiết hơn một chút.

Theo dõi các chỉ số

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

Tại đây, chúng ta quyết định những chỉ số mà chúng ta sẽ theo dõi. Chúng ta sẽ theo dõi mức độ tổn thất và độ chính xác trên tập dữ liệu huấn luyện cũng như mức độ tổn thất và độ chính xác trên tập xác nhận (val_loss và val_acc tương ứng). Chúng ta sẽ nói thêm về tập xác nhận ở bên dưới.

Chuẩn bị dữ liệu dưới dạng 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
  ];
});

Ở đây, chúng ta tạo 2 tập dữ liệu, một tập dữ liệu huấn luyện mà chúng ta sẽ huấn luyện mô hình và một tập xác nhận mà chúng ta sẽ kiểm thử mô hình vào cuối mỗi giai đoạn. Tuy nhiên, dữ liệu trong tập xác nhận sẽ không bao giờ được hiển thị cho mô hình trong quá trình huấn luyện.

Lớp dữ liệu mà chúng tôi cung cấp giúp bạn dễ dàng lấy các tensor từ dữ liệu hình ảnh. Tuy nhiên, chúng ta vẫn định hình lại các tensor thành hình dạng mà mô hình mong đợi, [num_examples, image_width, image_height, channels], trước khi có thể đưa các tensor này vào mô hình. Đối với mỗi tập dữ liệu, chúng ta đều có cả đầu vào (các Xs) và nhãn (các Y).

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

Chúng ta gọi model.fit để bắt đầu vòng lặp huấn luyện. Chúng ta cũng truyền một thuộc tính validationData để cho biết mô hình nên sử dụng dữ liệu nào để tự kiểm thử sau mỗi giai đoạn (nhưng không dùng để huấn luyện).

Nếu chúng ta làm tốt trên dữ liệu huấn luyện nhưng không làm tốt trên dữ liệu xác thực, thì có nghĩa là mô hình có khả năng khái quát hoá kém với dữ liệu huấn luyện và sẽ không tổng quát hoá tốt cho dữ liệu đầu vào mà mô hình chưa từng thấy trước đây.

7. Đánh giá mô hình của chúng tôi

Độ chính xác của quá trình xác thực cung cấp thông tin ước tính chính xác về hiệu suất của mô hình trên dữ liệu mà mô hình chưa từng thấy trước đây (miễn là dữ liệu đó giống với tập xác nhận theo một cách nào đó). Tuy nhiên, chúng ta có thể muốn xem bảng chi tiết hơn về hiệu suất trên các lớp khác nhau.

tfjs-vis có một số phương thức có thể giúp bạn thực hiện việc này.

96914ff65fc3b74c.png Thêm đoạn mã sau vào cuối tệp 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();
}

Mã này đang làm gì?

  • Đưa ra dự đoán.
  • Tính toán các chỉ số về độ chính xác.
  • Hiện các chỉ số

Hãy xem xét từng bước kỹ hơn.

Dự đoán

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];
}      

Trước tiên, chúng ta cần đưa ra một số dự đoán. Ở đây, chúng ta sẽ lấy 500 hình ảnh và dự đoán chữ số trong đó (bạn có thể tăng số lượng này sau để kiểm thử trên một tập hợp hình ảnh lớn hơn).

Đáng chú ý là hàm argmax cho chúng ta chỉ mục của lớp có xác suất cao nhất. Hãy nhớ rằng mô hình sẽ xuất ra một xác suất cho mỗi lớp. Ở đây, chúng ta tìm ra xác suất cao nhất và chỉ định sử dụng xác suất đó làm dự đoán.

Bạn cũng có thể nhận thấy rằng chúng ta có thể dự đoán trên cả 500 ví dụ cùng một lúc. Đây là sức mạnh của quá trình vectơ hoá mà TensorFlow.js cung cấp.

Hiện độ chính xác theo từng lớp

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();
}      

Với một tập hợp các dự đoán và nhãn, chúng ta có thể tính độ chính xác cho từng lớp.

Hiện ma trận nhầm lẫn

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();
}  

Ma trận nhầm lẫn tương tự như độ chính xác theo từng lớp nhưng được chia nhỏ hơn nữa để cho thấy các mẫu phân loại sai. Nhờ đó, bạn có thể biết liệu mô hình có nhầm lẫn về bất kỳ cặp lớp cụ thể nào hay không.

Hiển thị kết quả đánh giá

96914ff65fc3b74c.png Thêm mã sau vào cuối hàm run để cho thấy kết quả đánh giá.

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

Bạn sẽ thấy một màn hình tương tự như sau.

82458197bd5e7f52.png

Xin chúc mừng! Bạn vừa huấn luyện một mạng nơron tích chập!

8. Những điểm chính cần ghi nhớ

Dự đoán danh mục cho dữ liệu đầu vào được gọi là tác vụ phân loại.

Các tác vụ phân loại yêu cầu phải có một bản trình bày dữ liệu phù hợp cho nhãn

  • Các biểu diễn phổ biến của nhãn bao gồm mã one-hot của các danh mục

Chuẩn bị dữ liệu:

  • Bạn nên giữ lại một số dữ liệu mà mô hình không bao giờ thấy trong quá trình huấn luyện để có thể dùng dữ liệu đó đánh giá mô hình. Đây được gọi là tập xác nhận.

Tạo và chạy mô hình của bạn:

  • Các mô hình tích chập đã cho thấy hiệu suất tốt trong các tác vụ liên quan đến hình ảnh.
  • Các vấn đề về phân loại thường sử dụng entropy chéo phân loại cho các hàm tổn thất.
  • Theo dõi quá trình huấn luyện để xem tổn thất có giảm và độ chính xác có tăng hay không.

Đánh giá mô hình của bạn

  • Quyết định cách đánh giá mô hình sau khi mô hình được huấn luyện để xem mô hình hoạt động hiệu quả như thế nào đối với vấn đề ban đầu mà bạn muốn giải quyết.
  • Độ chính xác theo từng lớp và ma trận nhầm lẫn có thể cung cấp cho bạn thông tin chi tiết hơn về hiệu suất của mô hình so với chỉ độ chính xác tổng thể.