Bắt đầu với vi điều khiển

Sử dụng bộ sưu tập để sắp xếp ngăn nắp các trang Lưu và phân loại nội dung dựa trên lựa chọn ưu tiên của bạn.

Tài liệu này giải thích cách huấn luyện mô hình và chạy suy luận bằng vi điều khiển.

Ví dụ về Hello World

Ví dụ Hello World được thiết kế để chứng minh những điều cơ bản tuyệt đối của việc sử dụng TensorFlow Lite cho Vi điều khiển. Chúng tôi đào tạo và chạy một mô hình sao chép một hàm sin, tức là, nó lấy một số duy nhất làm đầu vào của nó và xuất ra giá trị sin của số đó. Khi được triển khai cho bộ vi điều khiển, các dự đoán của nó được sử dụng để nhấp nháy đèn LED hoặc điều khiển hoạt ảnh.

Quy trình làm việc end-to-end bao gồm các bước sau:

  1. Đào tạo một mô hình (bằng Python): Một sổ ghi chép jupyter để đào tạo, chuyển đổi và tối ưu hóa một mô hình để sử dụng trên thiết bị.
  2. Chạy suy luận (trong C ++ 11): Một bài kiểm tra đơn vị end-to-end chạy suy luận trên mô hình bằng cách sử dụng thư viện C ++ .

Nhận một thiết bị được hỗ trợ

Ứng dụng mẫu mà chúng tôi sẽ sử dụng đã được thử nghiệm trên các thiết bị sau:

Tìm hiểu thêm về các nền tảng được hỗ trợ trong TensorFlow Lite dành cho Vi điều khiển .

Đào tạo một người mẫu

Sử dụng Google Colaboratory để đào tạo mô hình của riêng bạn . Để biết thêm chi tiết, hãy tham khảo README.md :

Hello World Training README.md

Chạy suy luận

Để chạy mô hình trên thiết bị của bạn, chúng tôi sẽ xem qua các hướng dẫn trong README.md :

Xin chào thế giới README.md

Các phần sau đây hướng dẫn về hello_world_test.cc , bài kiểm tra đơn vị của ví dụ, trình bày cách chạy suy luận bằng TensorFlow Lite dành cho Vi điều khiển. Nó tải mô hình và chạy suy luận nhiều lần.

1. Bao gồm các tiêu đề thư viện

Để sử dụng thư viện TensorFlow Lite for Microcontrollers, chúng tôi phải bao gồm các tệp tiêu đề sau:

#include "tensorflow/lite/micro/all_ops_resolver.h"
#include "tensorflow/lite/micro/micro_error_reporter.h"
#include "tensorflow/lite/micro/micro_interpreter.h"
#include "tensorflow/lite/schema/schema_generated.h"
#include "tensorflow/lite/version.h"

2. Bao gồm tiêu đề mô hình

Trình thông dịch TensorFlow Lite dành cho Bộ điều khiển mong đợi mô hình được cung cấp dưới dạng một mảng C ++. Mô hình được định nghĩa trong các tệp model.hmodel.cc . Tiêu đề được bao gồm với dòng sau:

#include "tensorflow/lite/micro/examples/hello_world/model.h"

3. Bao gồm tiêu đề khung kiểm tra đơn vị

Để tạo một bài kiểm tra đơn vị, chúng tôi đưa vào khung kiểm tra đơn vị TensorFlow Lite cho Bộ vi điều khiển bằng cách bao gồm dòng sau:

#include "tensorflow/lite/micro/testing/micro_test.h"

Kiểm tra được xác định bằng cách sử dụng các macro sau:

TF_LITE_MICRO_TESTS_BEGIN

TF_LITE_MICRO_TEST(LoadModelAndPerformInference) {
  . // add code here
  .
}

TF_LITE_MICRO_TESTS_END

Bây giờ chúng ta thảo luận về mã có trong macro ở trên.

4. Thiết lập ghi nhật ký

Để thiết lập ghi nhật ký, một con trỏ tflite::ErrorReporter được tạo bằng cách sử dụng một con trỏ tới một tflite::MicroErrorReporter :

tflite::MicroErrorReporter micro_error_reporter;
tflite::ErrorReporter* error_reporter = &micro_error_reporter;

Biến này sẽ được chuyển vào trình thông dịch, cho phép nó ghi nhật ký. Vì bộ vi điều khiển thường có nhiều cơ chế khác nhau để ghi nhật ký, việc triển khai tflite::MicroErrorReporter được thiết kế để tùy chỉnh cho thiết bị cụ thể của bạn.

5. Tải một mô hình

Trong đoạn mã sau, mô hình được khởi tạo bằng cách sử dụng dữ liệu từ mảng char , g_model , được khai báo trong model.h . Sau đó, chúng tôi kiểm tra mô hình để đảm bảo phiên bản giản đồ của nó tương thích với phiên bản chúng tôi đang sử dụng:

const tflite::Model* model = ::tflite::GetModel(g_model);
if (model->version() != TFLITE_SCHEMA_VERSION) {
  TF_LITE_REPORT_ERROR(error_reporter,
      "Model provided is schema version %d not equal "
      "to supported version %d.\n",
      model->version(), TFLITE_SCHEMA_VERSION);
}

6. Khởi tạo trình giải quyết hoạt động

Một cá thể AllOpsResolver được khai báo. Điều này sẽ được trình thông dịch sử dụng để truy cập các hoạt động được sử dụng bởi mô hình:

tflite::AllOpsResolver resolver;

AllOpsResolver tải tất cả các hoạt động có sẵn trong TensorFlow Lite dành cho Vi điều khiển, sử dụng nhiều bộ nhớ. Vì một mô hình nhất định sẽ chỉ sử dụng một tập hợp con của các hoạt động này, nên các ứng dụng trong thế giới thực chỉ tải các hoạt động cần thiết.

Điều này được thực hiện bằng cách sử dụng một lớp khác, MicroMutableOpResolver . Bạn có thể xem cách sử dụng nó trong micro_speech_test.cc của ví dụ về giọng nói Micro .

7. Cấp phát bộ nhớ

Chúng ta cần phân bổ trước một lượng bộ nhớ nhất định cho các mảng đầu vào, đầu ra và mảng trung gian. Điều này được cung cấp dưới dạng một mảng uint8_t có kích thước tensor_arena_size :

const int tensor_arena_size = 2 * 1024;
uint8_t tensor_arena[tensor_arena_size];

Kích thước yêu cầu sẽ phụ thuộc vào kiểu máy bạn đang sử dụng và có thể cần được xác định bằng thử nghiệm.

8. Phiên dịch tức thời

Chúng tôi tạo một cá thể tflite::MicroInterpreter , truyền vào các biến đã tạo trước đó:

tflite::MicroInterpreter interpreter(model, resolver, tensor_arena,
                                     tensor_arena_size, error_reporter);

9. Phân bổ các bộ căng

Chúng tôi yêu cầu trình thông dịch cấp phát bộ nhớ từ tensor_arena cho các tensor của mô hình:

interpreter.AllocateTensors();

10. Xác thực hình dạng đầu vào

Phiên MicroInterpreter có thể cung cấp cho chúng tôi một con trỏ tới tensor đầu vào của mô hình bằng cách gọi .input(0) , trong đó 0 đại diện cho tensor đầu vào đầu tiên (và duy nhất):

  // Obtain a pointer to the model's input tensor
  TfLiteTensor* input = interpreter.input(0);

Sau đó, chúng tôi kiểm tra bộ căng này để xác nhận rằng hình dạng và kiểu của nó là những gì chúng tôi đang mong đợi:

// Make sure the input has the properties we expect
TF_LITE_MICRO_EXPECT_NE(nullptr, input);
// The property "dims" tells us the tensor's shape. It has one element for
// each dimension. Our input is a 2D tensor containing 1 element, so "dims"
// should have size 2.
TF_LITE_MICRO_EXPECT_EQ(2, input->dims->size);
// The value of each element gives the length of the corresponding tensor.
// We should expect two single element tensors (one is contained within the
// other).
TF_LITE_MICRO_EXPECT_EQ(1, input->dims->data[0]);
TF_LITE_MICRO_EXPECT_EQ(1, input->dims->data[1]);
// The input is a 32 bit floating point value
TF_LITE_MICRO_EXPECT_EQ(kTfLiteFloat32, input->type);

Giá trị enum kTfLiteFloat32 là tham chiếu đến một trong các kiểu dữ liệu TensorFlow Lite và được định nghĩa common.h .

11. Cung cấp giá trị đầu vào

Để cung cấp đầu vào cho mô hình, chúng tôi đặt nội dung của tensor đầu vào, như sau:

input->data.f[0] = 0.;

Trong trường hợp này, chúng tôi nhập một giá trị dấu phẩy động đại diện cho 0 .

12. Chạy mô hình

Để chạy mô hình, chúng ta có thể gọi Invoke() trên cá thể tflite::MicroInterpreter :

TfLiteStatus invoke_status = interpreter.Invoke();
if (invoke_status != kTfLiteOk) {
  TF_LITE_REPORT_ERROR(error_reporter, "Invoke failed\n");
}

Chúng tôi có thể kiểm tra giá trị trả về, TfLiteStatus , để xác định xem quá trình chạy có thành công hay không. Các giá trị có thể có của TfLiteStatus , được định nghĩa trong common.h , là kTfLiteOkkTfLiteError .

Đoạn mã sau khẳng định rằng giá trị là kTfLiteOk , có nghĩa là suy luận đã được chạy thành công.

TF_LITE_MICRO_EXPECT_EQ(kTfLiteOk, invoke_status);

13. Đạt được đầu ra

Có thể lấy tensor đầu ra của mô hình bằng cách gọi output(0) trên tflite::MicroInterpreter , trong đó 0 đại diện cho tensor đầu ra đầu tiên (và duy nhất).

Trong ví dụ, đầu ra của mô hình là một giá trị dấu phẩy động duy nhất được chứa trong một tensor 2D:

TfLiteTensor* output = interpreter.output(0);
TF_LITE_MICRO_EXPECT_EQ(2, output->dims->size);
TF_LITE_MICRO_EXPECT_EQ(1, input->dims->data[0]);
TF_LITE_MICRO_EXPECT_EQ(1, input->dims->data[1]);
TF_LITE_MICRO_EXPECT_EQ(kTfLiteFloat32, output->type);

Chúng tôi có thể đọc giá trị trực tiếp từ tensor đầu ra và khẳng định rằng đó là những gì chúng tôi mong đợi:

// Obtain the output value from the tensor
float value = output->data.f[0];
// Check that the output value is within 0.05 of the expected value
TF_LITE_MICRO_EXPECT_NEAR(0., value, 0.05);

14. Chạy lại suy luận

Phần còn lại của mã chạy suy luận nhiều lần nữa. Trong mỗi trường hợp, chúng tôi gán một giá trị cho tensor đầu vào, gọi trình thông dịch và đọc kết quả từ tensor đầu ra:

input->data.f[0] = 1.;
interpreter.Invoke();
value = output->data.f[0];
TF_LITE_MICRO_EXPECT_NEAR(0.841, value, 0.05);

input->data.f[0] = 3.;
interpreter.Invoke();
value = output->data.f[0];
TF_LITE_MICRO_EXPECT_NEAR(0.141, value, 0.05);

input->data.f[0] = 5.;
interpreter.Invoke();
value = output->data.f[0];
TF_LITE_MICRO_EXPECT_NEAR(-0.959, value, 0.05);

15. Đọc mã ứng dụng

Khi bạn đã hoàn thành bài kiểm tra đơn vị này, bạn sẽ có thể hiểu mã ứng dụng của ví dụ, nằm trong main_functions.cc . Nó tuân theo một quy trình tương tự, nhưng tạo ra một giá trị đầu vào dựa trên số lượng suy luận đã được chạy và gọi một chức năng dành riêng cho thiết bị để hiển thị đầu ra của mô hình cho người dùng.