Số lượng hoá sau khi đào tạo

Lượng tử hoá sau huấn luyện là một kỹ thuật chuyển đổi có thể giảm kích thước mô hình, đồng thời cải thiện độ trễ của CPU và bộ tăng tốc phần cứng mà ít làm giảm độ chính xác của mô hình. Bạn có thể lượng tử hoá một mô hình TensorFlow số thực đã được huấn luyện khi chuyển đổi mô hình đó sang định dạng LiteRT bằng Trình chuyển đổi LiteRT.

Phương pháp tối ưu hoá

Có một số lựa chọn về lượng tử hoá sau huấn luyện để bạn lựa chọn. Dưới đây là bảng tóm tắt các lựa chọn và lợi ích mà chúng mang lại:

Kỹ thuật Lợi ích Phần cứng
Lượng tử hoá dải tần nhạy sáng Nhỏ hơn 4 lần, tăng tốc từ 2 đến 3 lần CPU
Lượng tử hoá số nguyên đầy đủ Nhỏ hơn 4 lần, tăng tốc gấp 3 lần trở lên CPU, Edge TPU, Vi điều khiển
Lượng tử hoá Float16 Nhỏ hơn 2 lần, tăng tốc GPU CPU, GPU

Cây quyết định sau đây có thể giúp xác định phương pháp lượng tử hoá sau huấn luyện phù hợp nhất cho trường hợp sử dụng của bạn:

các lựa chọn tối ưu hoá sau huấn luyện

Không có lượng tử hoá

Chuyển đổi sang mô hình TFLite mà không cần định lượng là điểm bắt đầu được đề xuất. Thao tác này sẽ tạo ra một mô hình TFLite số thực.

import tensorflow as tf
converter = tf.lite.TFLiteConverter.from_saved_model(saved_model_dir)
tflite_quant_model = converter.convert()

Bạn nên thực hiện việc này như một bước ban đầu để xác minh rằng các toán tử của mô hình TF ban đầu tương thích với TFLite và cũng có thể được dùng làm đường cơ sở để gỡ lỗi định lượng do các phương thức định lượng sau huấn luyện tiếp theo gây ra. Ví dụ: nếu một mô hình TFLite được lượng tử hoá tạo ra kết quả không mong muốn, trong khi mô hình TFLite dấu phẩy động lại chính xác, thì chúng ta có thể thu hẹp vấn đề thành các lỗi do phiên bản được lượng tử hoá của toán tử TFLite gây ra.

Lượng tử hoá dải tần nhạy sáng

Lượng tử hoá dải động giúp giảm mức sử dụng bộ nhớ và tăng tốc độ tính toán mà bạn không cần phải cung cấp một tập dữ liệu đại diện để hiệu chuẩn. Loại lượng tử hoá này chỉ lượng tử hoá tĩnh các trọng số từ dấu phẩy động sang số nguyên tại thời điểm chuyển đổi, cung cấp độ chính xác 8 bit:

import tensorflow as tf
converter = tf.lite.TFLiteConverter.from_saved_model(saved_model_dir)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
tflite_quant_model = converter.convert()

Để giảm thêm độ trễ trong quá trình suy luận, các toán tử "dải động" sẽ định lượng động các lượt kích hoạt dựa trên dải của chúng thành 8 bit và thực hiện các phép tính với trọng số và lượt kích hoạt 8 bit. Việc tối ưu hoá này giúp giảm độ trễ gần với suy luận hoàn toàn bằng số cố định. Tuy nhiên, các đầu ra vẫn được lưu trữ bằng dấu phẩy động, do đó, tốc độ tăng của các thao tác dải động thấp hơn so với tính toán dấu phẩy cố định đầy đủ.

Lượng tử hoá số nguyên đầy đủ

Bạn có thể cải thiện thêm độ trễ, giảm mức sử dụng bộ nhớ tối đa và khả năng tương thích với các thiết bị hoặc bộ tăng tốc phần cứng chỉ có số nguyên bằng cách đảm bảo tất cả các phép toán mô hình đều được định lượng số nguyên.

Để định lượng số nguyên đầy đủ, bạn cần hiệu chỉnh hoặc ước tính phạm vi, tức là (tối thiểu, tối đa) của tất cả các tensor dấu phẩy động trong mô hình. Không giống như các tensor hằng số (chẳng hạn như trọng số và độ lệch), các tensor biến (chẳng hạn như đầu vào mô hình, các lượt kích hoạt (đầu ra của các lớp trung gian) và đầu ra mô hình) không thể được hiệu chỉnh trừ phi chúng ta chạy một vài chu kỳ suy luận. Do đó, bộ chuyển đổi cần một tập dữ liệu đại diện để hiệu chỉnh. Tập dữ liệu này có thể là một tập hợp con nhỏ (khoảng 100 đến 500 mẫu) của dữ liệu huấn luyện hoặc dữ liệu xác thực. Tham khảo hàm representative_dataset() bên dưới.

Từ phiên bản TensorFlow 2.7, bạn có thể chỉ định tập dữ liệu đại diện thông qua một chữ ký như ví dụ sau:

def representative_dataset():
  for data in dataset:
    yield {
      "image": data.image,
      "bias": data.bias,
    }

Nếu có nhiều chữ ký trong mô hình TensorFlow đã cho, bạn có thể chỉ định nhiều tập dữ liệu bằng cách chỉ định khoá chữ ký:

def representative_dataset():
  # Feed data set for the "encode" signature.
  for data in encode_signature_dataset:
    yield (
      "encode", {
        "image": data.image,
        "bias": data.bias,
      }
    )

  # Feed data set for the "decode" signature.
  for data in decode_signature_dataset:
    yield (
      "decode", {
        "image": data.image,
        "hint": data.hint,
      },
    )

Bạn có thể tạo tập dữ liệu đại diện bằng cách cung cấp danh sách tensor đầu vào:

def representative_dataset():
  for data in tf.data.Dataset.from_tensor_slices((images)).batch(1).take(100):
    yield [tf.dtypes.cast(data, tf.float32)]

Kể từ phiên bản TensorFlow 2.7, bạn nên sử dụng phương pháp dựa trên chữ ký thay vì phương pháp dựa trên danh sách tensor đầu vào vì thứ tự tensor đầu vào có thể dễ dàng bị đảo ngược.

Để kiểm thử, bạn có thể sử dụng một tập dữ liệu giả như sau:

def representative_dataset():
    for _ in range(100):
      data = np.random.rand(1, 244, 244, 3)
      yield [data.astype(np.float32)]
 

Số nguyên có dự phòng số thực (sử dụng đầu vào/đầu ra số thực mặc định)

Để định lượng hoàn toàn một mô hình số nguyên, nhưng sử dụng các toán tử dấu phẩy động khi chúng không có một cách triển khai số nguyên (để đảm bảo quá trình chuyển đổi diễn ra suôn sẻ), hãy làm theo các bước sau:

import tensorflow as tf
converter = tf.lite.TFLiteConverter.from_saved_model(saved_model_dir)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.representative_dataset = representative_dataset
tflite_quant_model = converter.convert()

Chỉ số nguyên

Tạo các mô hình chỉ có số nguyên là một trường hợp sử dụng phổ biến cho LiteRT cho Vi điều khiểnCoral Edge TPU.

Ngoài ra, để đảm bảo khả năng tương thích với các thiết bị chỉ có số nguyên (chẳng hạn như vi điều khiển 8 bit) và các bộ tăng tốc (chẳng hạn như Coral Edge TPU), bạn có thể thực thi quá trình lượng tử hoá số nguyên đầy đủ cho tất cả các hoạt động, bao gồm cả đầu vào và đầu ra, bằng cách thực hiện các bước sau:

import tensorflow as tf
converter = tf.lite.TFLiteConverter.from_saved_model(saved_model_dir)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.representative_dataset = representative_dataset
converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS_INT8]
converter.inference_input_type = tf.int8  # or tf.uint8
converter.inference_output_type = tf.int8  # or tf.uint8
tflite_quant_model = converter.convert()

Lượng tử hoá Float16

Bạn có thể giảm kích thước của một mô hình dấu phẩy động bằng cách lượng tử hoá trọng số thành float16, tiêu chuẩn IEEE cho số dấu phẩy động 16 bit. Để bật lượng tử hoá float16 của các trọng số, hãy làm theo các bước sau:

import tensorflow as tf
converter = tf.lite.TFLiteConverter.from_saved_model(saved_model_dir)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.target_spec.supported_types = [tf.float16]
tflite_quant_model = converter.convert()

Sau đây là những lợi thế của việc định lượng float16:

  • Phương pháp này giảm kích thước mô hình xuống một nửa (vì tất cả trọng số đều giảm xuống một nửa kích thước ban đầu).
  • Điều này giúp giảm thiểu sai sót.
  • Thư viện này hỗ trợ một số đại biểu (ví dụ: đại biểu GPU) có thể hoạt động trực tiếp trên dữ liệu float16, giúp thực thi nhanh hơn so với các phép tính float32.

Sau đây là những nhược điểm của việc định lượng float16:

  • Việc này không làm giảm độ trễ nhiều như việc lượng tử hoá thành phép toán số học dấu phẩy cố định.
  • Theo mặc định, một mô hình được lượng tử hoá float16 sẽ "giải lượng tử hoá" các giá trị trọng số thành float32 khi chạy trên CPU. (Xin lưu ý rằng uỷ quyền GPU sẽ không thực hiện việc giảm lượng tử hoá này, vì uỷ quyền này có thể hoạt động trên dữ liệu float16.)

Chỉ số nguyên: 16 bit kích hoạt với trọng số 8 bit (thử nghiệm)

Đây là một lược đồ định lượng thử nghiệm. Phương pháp này tương tự như lược đồ "chỉ số nguyên", nhưng các lượt kích hoạt được lượng tử hoá dựa trên phạm vi của chúng thành 16 bit, trọng số được lượng tử hoá thành số nguyên 8 bit và độ lệch được lượng tử hoá thành số nguyên 64 bit. Đây được gọi là lượng tử hoá 16x8.

Ưu điểm chính của việc lượng tử hoá này là có thể cải thiện đáng kể độ chính xác, nhưng chỉ tăng nhẹ kích thước mô hình.

import tensorflow as tf
converter = tf.lite.TFLiteConverter.from_saved_model(saved_model_dir)
converter.representative_dataset = representative_dataset
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.target_spec.supported_ops = [tf.lite.OpsSet.EXPERIMENTAL_TFLITE_BUILTINS_ACTIVATIONS_INT16_WEIGHTS_INT8]
tflite_quant_model = converter.convert()

Nếu một số toán tử trong mô hình không hỗ trợ lượng tử hoá 16x8, thì mô hình vẫn có thể được lượng tử hoá, nhưng các toán tử không được hỗ trợ sẽ được giữ ở dạng số thực. Bạn nên thêm lựa chọn sau vào target_spec để cho phép điều này.

import tensorflow as tf
converter = tf.lite.TFLiteConverter.from_saved_model(saved_model_dir)
converter.representative_dataset = representative_dataset
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.target_spec.supported_ops = [tf.lite.OpsSet.EXPERIMENTAL_TFLITE_BUILTINS_ACTIVATIONS_INT16_WEIGHTS_INT8,
tf.lite.OpsSet.TFLITE_BUILTINS]
tflite_quant_model = converter.convert()

Sau đây là ví dụ về các trường hợp sử dụng mà độ chính xác được cải thiện nhờ phương thức lượng tử hoá này:

  • siêu phân giải,
  • xử lý tín hiệu âm thanh, chẳng hạn như khử tiếng ồn và tạo chùm tia,
  • giảm nhiễu hình ảnh,
  • Tái tạo HDR từ một hình ảnh duy nhất.

Nhược điểm của phương pháp lượng tử hoá này là:

  • Hiện tại, suy luận chậm hơn đáng kể so với số nguyên đầy đủ 8 bit do thiếu việc triển khai hạt nhân được tối ưu hoá.
  • Hiện tại, tính năng này không tương thích với các đại biểu TFLite được tăng tốc phần cứng hiện có.

Bạn có thể xem hướng dẫn về chế độ định lượng này tại đây.

Độ chính xác của mô hình

Vì các trọng số được lượng tử hoá sau khi huấn luyện, nên có thể xảy ra tình trạng mất độ chính xác, đặc biệt là đối với các mạng nhỏ hơn. Các mô hình được lượng tử hoá hoàn toàn đã được huấn luyện trước được cung cấp cho các mạng cụ thể trên Kaggle Models. Điều quan trọng là phải kiểm tra độ chính xác của mô hình được lượng tử hoá để xác minh rằng mọi sự suy giảm độ chính xác đều nằm trong giới hạn chấp nhận được. Có các công cụ để đánh giá độ chính xác của mô hình LiteRT.

Ngoài ra, nếu độ chính xác giảm quá nhiều, hãy cân nhắc sử dụng phương pháp huấn luyện nhận biết lượng tử hoá. Tuy nhiên, việc này đòi hỏi phải sửa đổi trong quá trình huấn luyện mô hình để thêm các nút định lượng giả, trong khi các kỹ thuật định lượng sau huấn luyện trên trang này sử dụng một mô hình được huấn luyện trước hiện có.

Biểu diễn cho các tensor được lượng tử hoá

Lượng tử hoá 8 bit xấp xỉ các giá trị dấu phẩy động bằng công thức sau.

\[real\_value = (int8\_value - zero\_point) \times scale\]

Biểu diễn này có 2 phần chính:

  • Trọng số trên mỗi trục (còn gọi là trên mỗi kênh) hoặc trên mỗi tensor được biểu thị bằng các giá trị bù hai của int8 trong phạm vi [-127, 127] với điểm 0 bằng 0.

  • Các hoạt động/đầu vào trên mỗi tensor được biểu thị bằng các giá trị bù hai của int8 trong dải [-128, 127], với điểm 0 trong dải [-128, 127].

Để xem chi tiết về lược đồ lượng tử hoá của chúng tôi, vui lòng xem quy cách lượng tử hoá. Các nhà cung cấp phần cứng muốn kết nối với giao diện uỷ quyền của TensorFlow Lite nên triển khai lược đồ lượng tử hoá được mô tả ở đó.