Độ chính xác hỗn hợp

Xem trên TensorFlow.org Chạy trong Google Colab Xem nguồn trên GitHub Tải xuống sổ ghi chép

Tổng quat

Độ chính xác hỗn hợp là việc sử dụng cả hai loại dấu phẩy động 16 bit và 32 bit trong một mô hình trong quá trình đào tạo để làm cho nó chạy nhanh hơn và sử dụng ít bộ nhớ hơn. Bằng cách giữ một số phần nhất định của mô hình ở loại 32-bit để ổn định về số, mô hình sẽ có thời gian bước thấp hơn và đào tạo như nhau về các chỉ số đánh giá như độ chính xác. Hướng dẫn này mô tả cách sử dụng API độ chính xác hỗn hợp Keras để tăng tốc các mô hình của bạn. Sử dụng API này có thể cải thiện hiệu suất hơn 3 lần trên GPU hiện đại và 60% trên TPU.

Ngày nay, hầu hết các kiểu máy sử dụng kiểu float32, chiếm 32 bit bộ nhớ. Tuy nhiên, có hai kiểu có độ chính xác thấp hơn, float16 và bfloat16, thay vào đó, mỗi kiểu sẽ chiếm 16 bit bộ nhớ. Các bộ tăng tốc hiện đại có thể chạy các hoạt động nhanh hơn ở loại 16-bit, vì chúng có phần cứng chuyên dụng để chạy các phép tính 16-bit và loại 16-bit có thể được đọc từ bộ nhớ nhanh hơn.

GPU NVIDIA có thể chạy các hoạt động trong float16 nhanh hơn trong float32 và TPU có thể chạy các hoạt động trong bfloat16 nhanh hơn float32. Do đó, nên sử dụng các loại mẫu có độ chính xác thấp hơn này bất cứ khi nào có thể trên các thiết bị đó. Tuy nhiên, các biến và một số phép tính vẫn phải ở trong float32 vì lý do số để mô hình huấn luyện đến cùng chất lượng. API chính xác hỗn hợp Keras cho phép bạn sử dụng kết hợp float16 hoặc bfloat16 với float32, để nhận được các lợi ích về hiệu suất từ ​​float16 / bfloat16 và lợi ích về độ ổn định số từ float32.

Thành lập

import tensorflow as tf

from tensorflow import keras
from tensorflow.keras import layers
from tensorflow.keras import mixed_precision

Phần cứng được hỗ trợ

Mặc dù độ chính xác hỗn hợp sẽ chạy trên hầu hết phần cứng, nhưng nó sẽ chỉ tăng tốc các mô hình trên GPU NVIDIA và Cloud TPU gần đây. GPU NVIDIA hỗ trợ sử dụng kết hợp float16 và float32, trong khi TPU hỗ trợ kết hợp bfloat16 và float32.

Trong số các GPU NVIDIA, những người có khả năng tính toán 7.0 trở lên sẽ thấy lợi ích hiệu suất lớn nhất từ ​​độ chính xác hỗn hợp vì chúng có các đơn vị phần cứng đặc biệt, được gọi là Tensor Core, để tăng tốc các phép nhân và chập ma trận float16. Các GPU cũ hơn không mang lại lợi ích về hiệu suất toán học khi sử dụng độ chính xác hỗn hợp, tuy nhiên, việc tiết kiệm bộ nhớ và băng thông có thể giúp tăng tốc một số. Bạn có thể tra cứu khả năng tính toán cho GPU của mình tại trang web GPU CUDA của NVIDIA. Ví dụ về các GPU sẽ được hưởng lợi nhiều nhất từ ​​độ chính xác hỗn hợp bao gồm GPU RTX, V100 và A100.

Bạn có thể kiểm tra loại GPU của mình bằng cách sau. Lệnh chỉ tồn tại nếu trình điều khiển NVIDIA được cài đặt, vì vậy, nếu không, phần sau sẽ phát sinh lỗi.

nvidia-smi -L
GPU 0: Tesla V100-SXM2-16GB (UUID: GPU-99e10c4d-de77-42ee-4524-6c41c4e5e47d)

Tất cả các TPU trên đám mây đều hỗ trợ bfloat16.

Ngay cả trên CPU và GPU cũ hơn, nơi không mong đợi tăng tốc, các API chính xác hỗn hợp vẫn có thể được sử dụng để kiểm tra đơn vị, gỡ lỗi hoặc chỉ để thử API. Tuy nhiên, trên CPU, độ chính xác hỗn hợp sẽ chạy chậm hơn đáng kể.

Đặt chính sách loại

Để sử dụng độ chính xác hỗn hợp trong Keras, bạn cần tạo tf.keras.mixed_precision.Policy , thường được gọi là chính sách dtype . Chính sách loại chỉ định các lớp dtype sẽ chạy trong. Trong hướng dẫn này, bạn sẽ xây dựng một chính sách từ chuỗi 'mixed_float16' và đặt nó làm chính sách chung. Điều này sẽ khiến các lớp được tạo sau đó sử dụng độ chính xác hỗn hợp với sự kết hợp của float16 và float32.

policy = mixed_precision.Policy('mixed_float16')
mixed_precision.set_global_policy(policy)
INFO:tensorflow:Mixed precision compatibility check (mixed_float16): OK
Your GPU will likely run quickly with dtype policy mixed_float16 as it has compute capability of at least 7.0. Your GPU: Tesla V100-SXM2-16GB, compute capability 7.0

Nói tóm lại, bạn có thể chuyển trực tiếp một chuỗi tới set_global_policy , điều này thường được thực hiện trong thực tế.

# Equivalent to the two lines above
mixed_precision.set_global_policy('mixed_float16')

Chính sách chỉ định hai khía cạnh quan trọng của một lớp: loại dtype mà các tính toán của lớp được thực hiện và loại dtype của các biến của lớp. Ở trên, bạn đã tạo một chính sách mixed_float16 (nghĩa là một chính sách hỗn hợp_có chính mixed_precision.Policy được tạo bằng cách chuyển chuỗi 'mixed_float16' đến phương thức khởi tạo của nó). Với chính sách này, các lớp sử dụng các phép tính float16 và các biến float32. Các phép tính được thực hiện trong float16 để đảm bảo hiệu suất, nhưng các biến phải được giữ trong float32 để ổn định số. Bạn có thể truy vấn trực tiếp các thuộc tính này của chính sách.

print('Compute dtype: %s' % policy.compute_dtype)
print('Variable dtype: %s' % policy.variable_dtype)
Compute dtype: float16
Variable dtype: float32

Như đã đề cập trước đây, chính sách mixed_float16 sẽ cải thiện đáng kể hiệu suất trên GPU NVIDIA với khả năng tính toán ít nhất là 7.0. Chính sách này sẽ chạy trên các GPU và CPU khác nhưng có thể không cải thiện hiệu suất. Đối với TPU, chính sách mixed_bfloat16 nên được sử dụng thay thế.

Xây dựng mô hình

Tiếp theo, hãy bắt đầu xây dựng một mô hình đơn giản. Các mô hình đồ chơi rất nhỏ thường không được hưởng lợi từ độ chính xác hỗn hợp, vì chi phí từ thời gian chạy TensorFlow thường chi phối thời gian thực thi, khiến bất kỳ sự cải thiện hiệu suất nào trên GPU là không đáng kể. Do đó, hãy xây dựng hai lớp Dense với 4096 đơn vị mỗi lớp nếu sử dụng GPU.

inputs = keras.Input(shape=(784,), name='digits')
if tf.config.list_physical_devices('GPU'):
  print('The model will run with 4096 units on a GPU')
  num_units = 4096
else:
  # Use fewer units on CPUs so the model finishes in a reasonable amount of time
  print('The model will run with 64 units on a CPU')
  num_units = 64
dense1 = layers.Dense(num_units, activation='relu', name='dense_1')
x = dense1(inputs)
dense2 = layers.Dense(num_units, activation='relu', name='dense_2')
x = dense2(x)
The model will run with 4096 units on a GPU

Mỗi lớp có một chính sách và sử dụng chính sách toàn cục theo mặc định. Do đó, mỗi lớp Dense có chính sách mixed_float16 bởi vì bạn đã đặt chính sách toàn cục thành mixed_float16 trước đó. Điều này sẽ khiến các lớp dày đặc thực hiện các phép tính float16 và có các biến float32. Họ chuyển đầu vào của mình thành float16 để thực hiện các phép tính float16, kết quả là đầu ra của họ là float16. Các biến của chúng là float32 và sẽ được ép kiểu float16 khi các lớp được gọi để tránh lỗi do không khớp kiểu dtype.

print(dense1.dtype_policy)
print('x.dtype: %s' % x.dtype.name)
# 'kernel' is dense1's variable
print('dense1.kernel.dtype: %s' % dense1.kernel.dtype.name)
<Policy "mixed_float16">
x.dtype: float16
dense1.kernel.dtype: float32

Tiếp theo, tạo các dự đoán đầu ra. Thông thường, bạn có thể tạo các dự đoán đầu ra như sau, nhưng điều này không phải lúc nào cũng ổn định về mặt số học với float16.

# INCORRECT: softmax and model output will be float16, when it should be float32
outputs = layers.Dense(10, activation='softmax', name='predictions')(x)
print('Outputs dtype: %s' % outputs.dtype.name)
Outputs dtype: float16

Kích hoạt softmax ở cuối mô hình phải là float32. Bởi vì chính sách dtype là mixed_float16 , kích hoạt softmax thường sẽ có dtype tính toán float16 và đầu ra float16 tensors.

Điều này có thể được khắc phục bằng cách tách các lớp Dense và softmax, và bằng cách chuyển dtype='float32' đến lớp softmax:

# CORRECT: softmax and model output are float32
x = layers.Dense(10, name='dense_logits')(x)
outputs = layers.Activation('softmax', dtype='float32', name='predictions')(x)
print('Outputs dtype: %s' % outputs.dtype.name)
Outputs dtype: float32

Truyền dtype='float32' vào phương thức khởi tạo lớp softmax sẽ ghi đè chính sách dtype của lớp thành chính sách float32 , thực hiện tính toán và giữ các biến trong float32. Tương tự, thay vào đó, bạn có thể truyền dtype=mixed_precision.Policy('float32') ; các lớp luôn chuyển đổi đối số dtype thành một chính sách. Bởi vì lớp Activation không có biến, loại biến của chính sách bị bỏ qua, nhưng loại tính toán của chính sách là float32 gây ra softmax và đầu ra của mô hình là float32.

Thêm một softmax float16 vào giữa một mô hình là tốt, nhưng một softmax ở cuối mô hình phải ở trong float32. Lý do là nếu tensor trung gian chảy từ softmax đến điểm mất là float16 hoặc bfloat16, các vấn đề về số có thể xảy ra.

Bạn có thể ghi đè dtype của bất kỳ lớp nào thành float32 bằng cách chuyển dtype='float32' nếu bạn cho rằng nó sẽ không ổn định về mặt số học với các tính toán float16. Nhưng thông thường, điều này chỉ cần thiết trên lớp cuối cùng của mô hình, vì hầu hết các lớp có đủ độ chính xác với mixed_float16mixed_bfloat16 .

Ngay cả khi mô hình không kết thúc bằng softmax, đầu ra vẫn phải là float32. Mặc dù không cần thiết đối với mô hình cụ thể này, nhưng kết quả đầu ra của mô hình có thể được chuyển thành float32 với như sau:

# The linear activation is an identity function. So this simply casts 'outputs'
# to float32. In this particular case, 'outputs' is already float32 so this is a
# no-op.
outputs = layers.Activation('linear', dtype='float32')(outputs)

Tiếp theo, hoàn thành và biên dịch mô hình, đồng thời tạo dữ liệu đầu vào:

model = keras.Model(inputs=inputs, outputs=outputs)
model.compile(loss='sparse_categorical_crossentropy',
              optimizer=keras.optimizers.RMSprop(),
              metrics=['accuracy'])

(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()
x_train = x_train.reshape(60000, 784).astype('float32') / 255
x_test = x_test.reshape(10000, 784).astype('float32') / 255

Ví dụ này ép kiểu dữ liệu đầu vào từ int8 sang float32. Bạn không ép kiểu float16 vì phép chia cho 255 nằm trên CPU, hoạt động này chạy các hoạt động float16 chậm hơn các hoạt động float32. Trong trường hợp này, sự khác biệt về hiệu suất không đáng kể, nhưng nói chung, bạn nên chạy toán xử lý đầu vào trong float32 nếu nó chạy trên CPU. Lớp đầu tiên của mô hình sẽ truyền các đầu vào tới float16, vì mỗi lớp truyền các đầu vào dấu phẩy động cho loại máy tính của nó.

Các trọng số ban đầu của mô hình được truy xuất. Điều này sẽ cho phép đào tạo lại từ đầu bằng cách tải trọng lượng.

initial_weights = model.get_weights()

Đào tạo người mẫu với Model.fit

Tiếp theo, đào tạo mô hình:

history = model.fit(x_train, y_train,
                    batch_size=8192,
                    epochs=5,
                    validation_split=0.2)
test_scores = model.evaluate(x_test, y_test, verbose=2)
print('Test loss:', test_scores[0])
print('Test accuracy:', test_scores[1])
Epoch 1/5
6/6 [==============================] - 2s 78ms/step - loss: 4.9609 - accuracy: 0.4132 - val_loss: 0.6643 - val_accuracy: 0.8437
Epoch 2/5
6/6 [==============================] - 0s 34ms/step - loss: 0.7752 - accuracy: 0.7789 - val_loss: 0.3098 - val_accuracy: 0.9175
Epoch 3/5
6/6 [==============================] - 0s 34ms/step - loss: 0.3620 - accuracy: 0.8848 - val_loss: 0.3149 - val_accuracy: 0.8969
Epoch 4/5
6/6 [==============================] - 0s 34ms/step - loss: 0.2998 - accuracy: 0.9066 - val_loss: 0.2988 - val_accuracy: 0.9068
Epoch 5/5
6/6 [==============================] - 0s 33ms/step - loss: 0.2298 - accuracy: 0.9285 - val_loss: 0.5062 - val_accuracy: 0.8414
313/313 - 0s - loss: 0.5163 - accuracy: 0.8392
Test loss: 0.5163048505783081
Test accuracy: 0.8392000198364258

Lưu ý rằng mô hình in thời gian trên mỗi bước trong nhật ký: ví dụ: "25ms / bước". Kỷ nguyên đầu tiên có thể chậm hơn do TensorFlow dành một chút thời gian để tối ưu hóa mô hình, nhưng sau đó thời gian mỗi bước sẽ ổn định.

Nếu bạn đang chạy hướng dẫn này trong Colab, bạn có thể so sánh hiệu suất của độ chính xác hỗn hợp với float32. Để làm như vậy, hãy thay đổi chính sách từ mixed_float16 thành float32 trong phần "Thiết lập chính sách loại", sau đó chạy lại tất cả các ô cho đến thời điểm này. Trên GPU có khả năng tính toán 7.X, bạn sẽ thấy thời gian trên mỗi bước tăng lên đáng kể, cho thấy độ chính xác hỗn hợp đã tăng tốc cho mô hình. Đảm bảo thay đổi chính sách trở lại mixed_float16 và chạy lại các ô trước khi tiếp tục với hướng dẫn.

Trên các GPU có khả năng tính toán ít nhất là 8.0 (GPU Ampe trở lên), bạn có thể sẽ không thấy cải thiện hiệu suất trong mô hình đồ chơi trong hướng dẫn này khi sử dụng độ chính xác hỗn hợp so với float32. Điều này là do việc sử dụng TensorFloat-32 , tự động sử dụng phép toán có độ chính xác thấp hơn trong một số hoạt động float32 như tf.linalg.matmul . TensorFloat-32 mang lại một số lợi thế về hiệu suất của độ chính xác hỗn hợp khi sử dụng float32. Tuy nhiên, trong các mô hình thế giới thực, bạn vẫn thường thấy những cải thiện đáng kể về hiệu suất từ ​​độ chính xác hỗn hợp do tiết kiệm băng thông bộ nhớ và các hoạt động mà TensorFloat-32 không hỗ trợ.

Nếu chạy độ chính xác hỗn hợp trên TPU, bạn sẽ không thấy hiệu suất tăng nhiều so với chạy độ chính xác hỗn hợp trên GPU, đặc biệt là GPU tiền khuếch đại. Điều này là do TPU thực hiện một số hoạt động nhất định trong bfloat16 ngay cả với chính sách dtype mặc định là float32. Điều này tương tự như cách GPU Ampere sử dụng TensorFloat-32 theo mặc định. So với GPU Ampere, TPU thường ít tăng hiệu suất hơn với độ chính xác hỗn hợp trên các mô hình trong thế giới thực.

Đối với nhiều mô hình trong thế giới thực, độ chính xác hỗn hợp cũng cho phép bạn tăng gấp đôi kích thước lô mà không hết bộ nhớ, vì bộ căng float16 chiếm một nửa bộ nhớ. Tuy nhiên, điều này không áp dụng cho mô hình đồ chơi này, vì bạn có thể chạy mô hình ở bất kỳ loại nào trong đó mỗi lô bao gồm toàn bộ tập dữ liệu MNIST gồm 60.000 hình ảnh.

Tỷ lệ tổn thất

Chia tỷ lệ tổn thất là một kỹ thuật mà tf.keras.Model.fit tự động thực hiện với chính sách mixed_float16 để tránh dòng dưới dạng số. Phần này mô tả tỷ lệ tổn thất là gì và phần tiếp theo mô tả cách sử dụng nó với một vòng đào tạo tùy chỉnh.

Dòng chảy tràn và Dòng chảy tràn

Kiểu dữ liệu float16 có phạm vi động hẹp so với float32. Điều này có nghĩa là các giá trị trên \(65504\) sẽ tràn xuống vô cùng và các giá trị dưới \(6.0 \times 10^{-8}\) sẽ tràn xuống 0. float32 và bfloat16 có phạm vi động cao hơn nhiều để hiện tượng tràn và tràn không phải là vấn đề.

Ví dụ:

x = tf.constant(256, dtype='float16')
(x ** 2).numpy()  # Overflow
inf
x = tf.constant(1e-5, dtype='float16')
(x ** 2).numpy()  # Underflow
0.0

Trong thực tế, việc tràn float16 hiếm khi xảy ra. Ngoài ra, dòng chảy dưới cũng hiếm khi xảy ra trong quá trình chuyển tiếp. Tuy nhiên, trong quá trình chuyển đổi ngược lại, các gradient có thể tràn xuống 0. Chia tỷ lệ tổn thất là một kỹ thuật để ngăn chặn tình trạng thiếu này.

Tổng quan về tỷ lệ tổn thất

Khái niệm cơ bản về tỷ lệ tổn thất rất đơn giản: chỉ cần nhân tổn thất với một số lớn nào đó, chẳng hạn như \(1024\), và bạn nhận được giá trị tỷ lệ tổn thất . Điều này sẽ làm cho các gradient cũng mở rộng theo \(1024\) , làm giảm đáng kể khả năng dòng chảy dưới. Sau khi tính toán các gradient cuối cùng, hãy chia chúng cho \(1024\) để đưa chúng trở lại giá trị chính xác của chúng.

Mã giả cho quá trình này là:

loss_scale = 1024
loss = model(inputs)
loss *= loss_scale
# Assume `grads` are float32. You do not want to divide float16 gradients.
grads = compute_gradient(loss, model.trainable_variables)
grads /= loss_scale

Việc chọn thang điểm tổn thất có thể rất phức tạp. Nếu tỷ lệ tổn thất quá thấp, các gradient vẫn có thể tràn xuống 0. Nếu quá cao, vấn đề ngược lại sẽ xảy ra: các gradient có thể tràn đến vô cùng.

Để giải quyết vấn đề này, TensorFlow xác định động quy mô tổn thất để bạn không phải chọn một cách thủ công. Nếu bạn sử dụng tf.keras.Model.fit , quy mô tổn thất được thực hiện cho bạn, do đó bạn không phải thực hiện thêm bất kỳ công việc nào. Nếu bạn sử dụng vòng lặp đào tạo tùy chỉnh, bạn phải sử dụng rõ ràng trình bao bọc trình tối ưu hóa đặc biệt tf.keras.mixed_precision.LossScaleOptimizer để sử dụng tính năng chia tỷ lệ tổn thất. Nó được mô tả trong phần sau.

Đào tạo mô hình với một vòng đào tạo tùy chỉnh

Cho đến nay, bạn đã đào tạo một mô hình Keras với độ chính xác hỗn hợp bằng cách sử dụng tf.keras.Model.fit . Tiếp theo, bạn sẽ sử dụng độ chính xác hỗn hợp với một vòng lặp đào tạo tùy chỉnh. Nếu bạn chưa biết vòng lặp đào tạo tùy chỉnh là gì, trước tiên hãy đọc Hướng dẫn đào tạo tùy chỉnh .

Chạy một vòng lặp đào tạo tùy chỉnh với độ chính xác hỗn hợp yêu cầu hai thay đổi khi chạy nó trong float32:

  1. Xây dựng mô hình với độ chính xác hỗn hợp (bạn đã làm điều này)
  2. Sử dụng tỷ lệ tổn thất rõ ràng nếu mixed_float16 .

Đối với bước (2), bạn sẽ sử dụng lớp tf.keras.mixed_precision.LossScaleOptimizer , lớp này bao bọc một trình tối ưu hóa và áp dụng tỷ lệ tổn thất. Theo mặc định, nó tự động xác định quy mô tổn thất, do đó bạn không cần phải chọn một. Xây dựng LossScaleOptimizer như sau.

optimizer = keras.optimizers.RMSprop()
optimizer = mixed_precision.LossScaleOptimizer(optimizer)

Nếu bạn muốn, có thể chọn một tỷ lệ tổn thất rõ ràng hoặc tùy chỉnh hành vi chia tỷ lệ tổn thất, nhưng chúng tôi khuyên bạn nên giữ hành vi chia tỷ lệ tổn thất mặc định, vì nó đã được phát hiện là hoạt động tốt trên tất cả các mô hình đã biết. Hãy xem tài liệu tf.keras.mixed_precision.LossScaleOptimizer nếu bạn muốn tùy chỉnh hành vi chia tỷ lệ tổn thất.

Tiếp theo, xác định đối tượng mất mát và tf.data.Dataset s:

loss_object = tf.keras.losses.SparseCategoricalCrossentropy()
train_dataset = (tf.data.Dataset.from_tensor_slices((x_train, y_train))
                 .shuffle(10000).batch(8192))
test_dataset = tf.data.Dataset.from_tensor_slices((x_test, y_test)).batch(8192)

Tiếp theo, xác định chức năng bước đào tạo. Bạn sẽ sử dụng hai phương pháp mới từ trình tối ưu hóa tỷ lệ tổn thất để chia tỷ lệ tổn thất và hủy tỷ lệ chuyển màu:

  • get_scaled_loss(loss) : Nhân mức tổn thất với quy mô tổn thất
  • get_unscaled_gradients(gradients) : Đưa vào danh sách các gradient được chia tỷ lệ làm đầu vào và chia từng thứ theo tỷ lệ tổn thất để loại bỏ chúng

Các chức năng này phải được sử dụng để ngăn dòng chảy dưới trong các gradient. LossScaleOptimizer.apply_gradients sau đó sẽ áp dụng các gradient nếu không ai trong số chúng có Inf s hoặc NaN . Nó cũng sẽ cập nhật quy mô tổn thất, giảm một nửa nếu các gradient có Inf s hoặc NaN và có khả năng tăng nó theo cách khác.

@tf.function
def train_step(x, y):
  with tf.GradientTape() as tape:
    predictions = model(x)
    loss = loss_object(y, predictions)
    scaled_loss = optimizer.get_scaled_loss(loss)
  scaled_gradients = tape.gradient(scaled_loss, model.trainable_variables)
  gradients = optimizer.get_unscaled_gradients(scaled_gradients)
  optimizer.apply_gradients(zip(gradients, model.trainable_variables))
  return loss

LossScaleOptimizer có thể sẽ bỏ qua một số bước đầu tiên khi bắt đầu đào tạo. Quy mô tổn thất bắt đầu cao để có thể nhanh chóng xác định quy mô tổn thất tối ưu. Sau một vài bước, quy mô tổn thất sẽ ổn định và rất ít bước sẽ bị bỏ qua. Quá trình này diễn ra tự động và không ảnh hưởng đến chất lượng đào tạo.

Bây giờ, hãy xác định bước kiểm tra:

@tf.function
def test_step(x):
  return model(x, training=False)

Tải trọng lượng ban đầu của mô hình để bạn có thể đào tạo lại từ đầu:

model.set_weights(initial_weights)

Cuối cùng, chạy vòng lặp đào tạo tùy chỉnh:

for epoch in range(5):
  epoch_loss_avg = tf.keras.metrics.Mean()
  test_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(
      name='test_accuracy')
  for x, y in train_dataset:
    loss = train_step(x, y)
    epoch_loss_avg(loss)
  for x, y in test_dataset:
    predictions = test_step(x)
    test_accuracy.update_state(y, predictions)
  print('Epoch {}: loss={}, test accuracy={}'.format(epoch, epoch_loss_avg.result(), test_accuracy.result()))
Epoch 0: loss=4.869325160980225, test accuracy=0.7221999764442444
Epoch 1: loss=0.4893573224544525, test accuracy=0.878000020980835
Epoch 2: loss=0.36011582612991333, test accuracy=0.9440000057220459
Epoch 3: loss=0.27391332387924194, test accuracy=0.9318000078201294
Epoch 4: loss=0.247697651386261, test accuracy=0.933899998664856

Mẹo về hiệu suất GPU

Dưới đây là một số mẹo về hiệu suất khi sử dụng độ chính xác hỗn hợp trên GPU.

Tăng kích thước lô của bạn

Nếu nó không ảnh hưởng đến chất lượng mô hình, hãy thử chạy với kích thước lô gấp đôi khi sử dụng độ chính xác hỗn hợp. Vì tensors float16 sử dụng một nửa bộ nhớ, điều này thường cho phép bạn tăng gấp đôi kích thước hàng loạt của mình mà không hết bộ nhớ. Việc tăng kích thước lô thường làm tăng thông lượng đào tạo, tức là các phần tử đào tạo trên giây mà mô hình của bạn có thể chạy.

Đảm bảo sử dụng Lõi Tensor GPU

Như đã đề cập trước đây, các GPU NVIDIA hiện đại sử dụng một đơn vị phần cứng đặc biệt gọi là Tensor Cores có thể nhân ma trận float16 rất nhanh chóng. Tuy nhiên, Lõi Tensor yêu cầu các kích thước nhất định của tensors phải là bội số của 8. Trong các ví dụ dưới đây, đối số được in đậm nếu và chỉ khi nó cần là bội số của 8 để Lõi Tensor được sử dụng.

  • tf.keras.layers.Dense ( đơn vị = 64 )
  • tf.keras.layers.Conv2d ( bộ lọc = 48 , kernel_size = 7, sải chân = 3)
    • Và tương tự đối với các lớp phức hợp khác, chẳng hạn như tf.keras.layers.Conv3d
  • tf.keras.layers.LSTM ( đơn vị = 64 )
    • Và tương tự cho các RNN khác, chẳng hạn như tf.keras.layers.GRU
  • tf.keras.Model.fit (epochs = 2, batch_size = 128 )

Bạn nên cố gắng sử dụng Tensor Cores khi có thể. Nếu bạn muốn tìm hiểu thêm, hướng dẫn về hiệu suất học sâu của NVIDIA mô tả các yêu cầu chính xác để sử dụng Lõi Tensor cũng như thông tin hiệu suất khác liên quan đến Lõi Tensor.

XLA

XLA là một trình biên dịch có thể tăng hơn nữa hiệu suất chính xác hỗn hợp, cũng như hiệu suất float32 ở mức độ thấp hơn. Tham khảo hướng dẫn XLA để biết thêm chi tiết.

Mẹo về hiệu suất Cloud TPU

Giống như với GPU, bạn nên thử tăng gấp đôi kích thước lô của mình khi sử dụng Cloud TPU vì bộ căng bfloat16 sử dụng một nửa bộ nhớ. Tăng gấp đôi kích thước lô có thể tăng thông lượng đào tạo.

TPU không yêu cầu bất kỳ điều chỉnh chính xác cụ thể hỗn hợp nào khác để có được hiệu suất tối ưu. Họ đã yêu cầu sử dụng XLA. TPU được hưởng lợi từ việc có một số kích thước nhất định là bội số của \(128\), nhưng điều này áp dụng tương tự cho kiểu float32 như đối với độ chính xác hỗn hợp. Xem hướng dẫn hiệu suất Cloud TPU để biết các mẹo chung về hiệu suất TPU, áp dụng cho độ chính xác hỗn hợp cũng như bộ căng float32.

Bản tóm tắt

  • Bạn nên sử dụng độ chính xác hỗn hợp nếu bạn sử dụng TPU hoặc GPU NVIDIA với khả năng tính toán ít nhất là 7.0, vì nó sẽ cải thiện hiệu suất lên đến 3 lần.
  • Bạn có thể sử dụng độ chính xác hỗn hợp với các dòng sau:

    # On TPUs, use 'mixed_bfloat16' instead
    mixed_precision.set_global_policy('mixed_float16')
    
  • Nếu mô hình của bạn kết thúc bằng softmax, hãy đảm bảo rằng nó là float32. Và bất kể mô hình của bạn kết thúc bằng gì, hãy đảm bảo đầu ra là float32.

  • Nếu bạn sử dụng vòng lặp đào tạo tùy chỉnh với mixed_float16 , ngoài các dòng trên, bạn cần bao bọc trình tối ưu hóa của mình bằng tf.keras.mixed_precision.LossScaleOptimizer . Sau đó, gọi optimizer.get_scaled_loss để chia tỷ lệ tổn thất và optimizer.get_unscaled_gradients để hủy chia tỷ lệ các độ dốc.

  • Nhân đôi kích thước lô đào tạo nếu nó không làm giảm độ chính xác của đánh giá

  • Trên GPU, hãy đảm bảo hầu hết các kích thước tensor là bội số của \(8\) để tối đa hóa hiệu suất

Để biết thêm ví dụ về độ chính xác hỗn hợp bằng cách sử dụng API tf.keras.mixed_precision , hãy kiểm tra kho lưu trữ mô hình chính thức . Hầu hết các mô hình chính thức, chẳng hạn như ResNetTransformer , sẽ chạy bằng cách sử dụng độ chính xác hỗn hợp bằng cách chuyển --dtype=fp16 .