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 trình tăng tốc CPU và phần cứng, với độ chính xác của mô hình ít bị giảm. Bạn có thể định lượng hoá một mô hình nổi TensorFlow đã được huấn luyện khi chuyển đổi mô hình đó sang định dạng TensorFlow Lite bằng Trình chuyển đổi TensorFlow Lite.

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

Có một số cách định lượng sau khi đào tạo. Dưới đây là bảng tóm tắt các lựa chọn và lợi ích mà các lựa chọn đó mang lại:

Kỹ thuật Lợi ích Phần cứng
Lượng tử dải động Nhỏ hơn 4 lần, tăng tốc 2x-3 lần CPU
Lượng tử số nguyên đầy đủ Nhỏ hơn 4 lần, tăng tốc hơn 3 lần CPU, TPU cạnh, Bộ vi điều khiển
Định lượng Float16 Giảm 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 thức lượng tử hoá sau huấn luyện là phù hợp nhất cho trường hợp sử dụng của bạn:

tuỳ chọn tối ưu hoá sau đào tạo

Không định lượng

Bạn nên bắt đầu chuyển đổi sang mô hình TFLite mà không cần lượng tử hoá. Thao tác này sẽ tạo một mô hình TFLite nổi.

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 ở bước đầu tiên để 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 cơ sở để gỡ lỗi các lỗi định lượng được đưa ra trong các phương pháp lượng tử hoá sau khi huấn luyện tiếp theo. Ví dụ: nếu mô hình TFLite lượng tử hoá tạo ra kết quả không mong muốn, trong khi mô hình TFLite nổi là chính xác, chúng ta có thể thu hẹp vấn đề ở các lỗi do phiên bản lượng tử hoá của các toán tử TFLite đưa ra.

Lượng tử hoá dải động

Việc lượng tử hoá dải động giúp giảm mức sử dụng bộ nhớ và tính toán nhanh hơn mà bạn không cần phải cung cấp tập dữ liệu đại diện để hiệu chỉnh. Loại lượng tử hoá này chỉ định lượng 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, mang lại độ 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ẽ linh động định lượng các lượt kích hoạt dựa trên phạm vi của chúng thành 8 bit, đồng thời thực hiện các phép tính với trọng số và lệnh kích hoạt 8 bit. Tính năng tối ưu hoá này cung cấp độ trễ gần với suy luận điểm cố định hoàn toàn. Tuy nhiên, các dữ liệu đầu ra vẫn được lưu trữ bằng dấu phẩy động, vì vậy, tốc độ tăng của hoạt động dải động nhỏ hơn so với phép tính điểm 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ớ cao nhất và khả năng tương thích với các thiết bị phần cứng hoặc trình tăng tốc chỉ chứa số nguyên bằng cách đảm bảo rằng tất cả toán học của mô hình đều được lượng tử hoá số nguyên.

Để lượng tử hoá 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ả tensor dấu phẩy động trong mô hình. Không giống như các tensor không đổi như trọng số và độ chệch, các tensor biến thiên, chẳng hạn như đầu vào của mô hình, hoạt động (đầu ra của lớp trung gian) và đầu ra của 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 yêu cầu phải có một tập dữ liệu đại diện để hiệu chỉnh chúng. Tập dữ liệu này có thể là một tập con nhỏ (khoảng 100-500 mẫu) của dữ liệu huấn luyện hoặc xác thực. Hãy 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 nhất định, bạn có thể chỉ định nhiều tập dữ liệu bằng cách chỉ định các 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 đảo ngược.

Để thử nghiệm, 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ó số thực có độ chính xác đơn dự phòng (sử dụng dữ liệu đầ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 hãy sử dụng các toán tử độ chính xác đơn khi các toán tử này không 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

Việc tạo mô hình chỉ số nguyên là một trường hợp sử dụng phổ biến cho TensorFlow Lite cho Bộ vi điều khiểnTPU cạnh san hô.

Ngoài ra, để đảm bảo khả năng tương thích với các thiết bị chỉ dùng số nguyên (chẳng hạn như bộ vi điều khiển 8 bit) và trình tăng tốc (chẳng hạn như TPU San hô san hô), 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 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
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()

Định lượng Float16

Bạn có thể giảm kích thước của mô hình dấu phẩy động bằng cách lượng tử hoá các trọng số thành float16, tiêu chuẩn IEEE cho các số có dấu phẩy động 16 bit. Để bật tính năng định lượng trọng số float16, 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à các ưu điểm của phương pháp lượng tử float16:

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

Nhược điểm của phương pháp lượng tử hoá float16 như sau:

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

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

Đây là một lược đồ lượng tử thử nghiệm. 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 16 bit, trọng số được lượng tử hoá ở số nguyên 8 bit và độ chệ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 độ chính xác một cách đáng kể nhưng chỉ tăng một chút 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 này không hỗ trợ việc lượng tử hoá 16x8, thì mô hình vẫn có thể lượng tử hoá, nhưng các toán tử không được hỗ trợ vẫn được giữ ở dạng số thực. Bạn nên thêm tuỳ chọn sau vào target_spec để cho phép việc 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()

Ví dụ về các trường hợp sử dụng trong đó việc cải thiện độ chính xác nhờ lược đồ lượng tử này:

  • super-resolution,
  • xử lý tín hiệu âm thanh như khử tiếng ồn và tạo tia sáng,
  • khử nhiễu cho hình ảnh,
  • Tái tạo HDR từ một hình ảnh duy nhất.

Nhược điểm của cách 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 phương thức triển khai nhân được tối ưu hoá.
  • Hiện tại, API này không tương thích với các uỷ quyền TFLite tăng tốc phần cứng hiện có.

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

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

Vì trọng số được lượng tử hoá sau quá trình huấn luyện, nên có thể bị 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 lượng tử hoá hoàn toàn được huấn luyện trước sẽ được cung cấp cho các mạng cụ thể trên Mô hình Kaggle. Điều quan trọng là bạn phải kiểm tra độ chính xác của mô hình lượng tử hoá để xác minh rằng mọi suy giảm về độ 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 TensorFlow Lite.

Ngoài ra, nếu độ chính xác giảm quá cao, hãy cân nhắc sử dụng tính năng đào tạo có nhận biết lượng tử hoá. Tuy nhiên, việc này yêu cầu phải sửa đổi trong quá trình huấn luyện mô hình để thêm các nút lượng tử giả, trong khi các kỹ thuật lượng tử hoá 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.

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

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

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

Nội dung trình bày này có hai phần chính:

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

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

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