Transformers documentation

Gỡ lỗi

You are viewing main version, which requires installation from source. If you'd like regular pip install, checkout the latest stable version (v5.22.1).
Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

Gỡ lỗi

Việc gỡ lỗi các vấn đề trong huấn luyện phân tán thường rơi vào một trong các nhóm sau: vấn đề số học, lỗi liên lạc, lỗi thời gian chạy và lỗi biên dịch.

Phát hiện tràn dưới và tràn trên

Underflow và overflow xảy ra khi các activation hoặc weights đạt đến inf hoặc nan, hoặc khi loss=NaN. Để phát hiện các trường hợp này, hãy bật module DebugUnderflowOverflow trong TrainingArguments.debug(), hoặc import và thêm nó vào vòng lặp huấn luyện của bạn.

Trainer
Vòng lặp huấn luyện PyTorch
from transformers import TrainingArguments

args = TrainingArguments(
    debug="underflow_overflow",
    ...
)

DebugUnderflowOverflow chèn các hook vào mô hình để kiểm tra các biến đầu vào và đầu ra cùng với trọng số mô hình tương ứng sau mỗi lần gọi forward. Khi phát hiện inf hoặc nan trong ít nhất một phần tử của các activation hoặc trọng số, module sẽ in ra một báo cáo như bên dưới.

Ví dụ dưới đây dành cho huấn luyện độ chính xác hỗn hợp fp16 với google/mt5-small.

Detected inf/nan during batch_number=0
Last 21 forward frames:
abs min  abs max  metadata
                  encoder.block.1.layer.1.DenseReluDense.dropout Dropout
0.00e+00 2.57e+02 input[0]
0.00e+00 2.85e+02 output
[...]
                  encoder.block.2.layer.0 T5LayerSelfAttention
6.78e-04 3.15e+03 input[0]
2.65e-04 3.42e+03 output[0]
             None output[1]
2.25e-01 1.00e+04 output[2]
                  encoder.block.2.layer.1.layer_norm T5LayerNorm
8.69e-02 4.18e-01 weight
2.65e-04 3.42e+03 input[0]
1.79e-06 4.65e+00 output
                  encoder.block.2.layer.1.DenseReluDense.wi_0 Linear
2.17e-07 4.50e+00 weight
1.79e-06 4.65e+00 input[0]
2.68e-06 3.70e+01 output
                  encoder.block.2.layer.1.DenseReluDense.wi_1 Linear
8.08e-07 2.66e+01 weight
1.79e-06 4.65e+00 input[0]
1.27e-04 2.37e+02 output
                  encoder.block.2.layer.1.DenseReluDense.dropout Dropout
0.00e+00 8.76e+03 input[0]
0.00e+00 9.74e+03 output
                  encoder.block.2.layer.1.DenseReluDense.wo Linear
1.01e-06 6.44e+00 weight
0.00e+00 9.74e+03 input[0]
3.18e-04 6.27e+04 output
                  encoder.block.2.layer.1.DenseReluDense T5DenseGatedGeluDense
1.79e-06 4.65e+00 input[0]
3.18e-04 6.27e+04 output
                  encoder.block.2.layer.1.dropout Dropout
3.18e-04 6.27e+04 input[0]
0.00e+00      inf output

Dòng đầu tiên hiển thị số lô (batch) mà lỗi đã xảy ra. Trong trường hợp này, lỗi xảy ra ở lô 0.

Mỗi khung mô tả module mà nó báo cáo. Ví dụ, khung bên dưới báo cáo về encoder.block.2.layer.1.layer_norm, lớp chuẩn hóa trong lớp đầu tiên của khối thứ hai của encoder. Các lời gọi forward là đến T5LayerNorm.

                  encoder.block.2.layer.1.layer_norm T5LayerNorm
8.69e-02 4.18e-01 weight
2.65e-04 3.42e+03 input[0]
1.79e-06 4.65e+00 output

Khung cuối cùng báo cáo về hàm Dropout.forward, hàm này gọi thuộc tính dropout bên trong lớp DenseReluDense. Sự tràn (inf) xảy ra ở khối thứ hai của encoder trên batch đầu tiên. Phần tử đầu vào lớn nhất là 6.27e+04.

                  encoder.block.2.layer.1.DenseReluDense T5DenseGatedGeluDense
1.79e-06 4.65e+00 input[0]
3.18e-04 6.27e+04 output
                  encoder.block.2.layer.1.dropout Dropout
3.18e-04 6.27e+04 input[0]
0.00e+00      inf output

Hoạt động đầu ra của T5DenseGatedGeluDense.forward đạt giá trị tối đa là 6.27e+04, gần với giá trị tối đa 6.4e+04 của fp16. Ở bước tiếp theo, Dropout chuẩn hóa lại trọng số sau khi đặt một số phần tử bằng không, đẩy giá trị tối đa vượt quá 6.4e+04 và gây ra tràn số.

Bây giờ bạn đã biết lỗi xảy ra ở đâu, hãy kiểm tra mã mô hình hóa trong modeling_t5.py.

class T5DenseGatedGeluDense(nn.Module):
    def __init__(self, config):
        super().__init__()
        self.wi_0 = nn.Linear(config.d_model, config.d_ff, bias=False)
        self.wi_1 = nn.Linear(config.d_model, config.d_ff, bias=False)
        self.wo = nn.Linear(config.d_ff, config.d_model, bias=False)
        self.dropout = nn.Dropout(config.dropout_rate)
        self.gelu_act = ACT2FN["gelu_new"]

    def forward(self, hidden_states):
        hidden_gelu = self.gelu_act(self.wi_0(hidden_states))
        hidden_linear = self.wi_1(hidden_states)
        hidden_states = hidden_gelu * hidden_linear
        hidden_states = self.dropout(hidden_states)
        hidden_states = self.wo(hidden_states)
        return hidden_states

Một cách khắc phục là chuyển sang fp32 vài bước trước khi các giá trị trở nên quá lớn, để các số không bị tràn khi nhân hoặc cộng. Một lựa chọn khác là tạm thời tắt huấn luyện độ chính xác hỗn hợp (amp).

import torch

def forward(self, hidden_states):
    device_type = hidden_states.device.type
    if torch.is_autocast_enabled(device_type):
        with torch.amp.autocast(device_type, enabled=False):
            return self._forward(hidden_states)
    else:
        return self._forward(hidden_states)

Báo cáo chỉ bao gồm đầu vào và đầu ra của các khung hình hoàn chỉnh. Để phân tích các giá trị trung gian bên trong bất kỳ hàm forward nào, hãy thêm detect_overflow sau mỗi lần gọi forward để theo dõi inf hoặc nan trong forwarded_states.

from transformers.debug_utils import detect_overflow

class T5LayerFF(nn.Module):
    [...]

    def forward(self, hidden_states):
        forwarded_states = self.layer_norm(hidden_states)
        detect_overflow(forwarded_states, "after layer_norm")
        forwarded_states = self.DenseReluDense(forwarded_states)
        detect_overflow(forwarded_states, "after DenseReluDense")
        return hidden_states + self.dropout(forwarded_states)

Cấu hình số lượng khung hình được in ra bởi DebugUnderflowOverflow.

from transformers.debug_utils import DebugUnderflowOverflow

debug_overflow = DebugUnderflowOverflow(model, max_frames_to_save=100)

Truy vết theo lô

DebugUnderflowOverflow cũng có thể theo dõi giá trị tuyệt đối nhỏ nhất và lớn nhất trong mỗi batch với việc phát hiện underflow và overflow bị tắt. Điều này giúp bạn xác định nơi các giá trị bắt đầu phân kỳ trong mô hình của bạn.

Ví dụ bên dưới theo dõi các lô 1 và 3 (các lô được đánh chỉ số bắt đầu từ 0).

debug_overflow = DebugUnderflowOverflow(model, trace_batch_nums=[1, 3])
                  *** Starting batch number=1 ***
abs min  abs max  metadata
                  shared Embedding
1.01e-06 7.92e+02 weight
0.00e+00 2.47e+04 input[0]
5.36e-05 7.92e+02 output
[...]
                  decoder.dropout Dropout
1.60e-07 2.27e+01 input[0]
0.00e+00 2.52e+01 output
                  decoder T5Stack
     not a tensor output
                  lm_head Linear
1.01e-06 7.92e+02 weight
0.00e+00 1.11e+00 input[0]
6.06e-02 8.39e+01 output
                   T5ForConditionalGeneration
     not a tensor output

                  *** Starting batch number=3 ***
abs min  abs max  metadata
                  shared Embedding
1.01e-06 7.92e+02 weight
0.00e+00 2.78e+04 input[0]
5.36e-05 7.92e+02 output
[...]

DebugUnderflowOverflow báo cáo nhiều khung hình, giúp dễ dàng phát hiện nơi các giá trị bắt đầu phân kỳ. Nếu bạn biết vấn đề nằm quanh batch 150, hãy tập trung trace vào batch 149 và 150 để so sánh xem các con số bắt đầu khác nhau ở đâu.

Bạn cũng có thể dừng trace sau một số batch cụ thể, ví dụ như batch 3.

debug_overflow = DebugUnderflowOverflow(model, trace_batch_nums=[1, 3], abort_after_batch_num=3)

Giao tiếp

Huấn luyện phân tán yêu cầu giao tiếp giữa các tiến trình và giữa các nút, đây là nguồn gây lỗi phổ biến.

Tải xuống script bên dưới để chẩn đoán các vấn đề về mạng, sau đó chạy nó để kiểm tra giao tiếp giữa các GPU. Lệnh bên dưới kiểm tra hai GPU. Điều chỉnh --nproc_per_node và --nnodes cho hệ thống của bạn.

wget https://raw.githubusercontent.com/huggingface/transformers/main/scripts/distributed/torch-distributed-gpu-test.py
python -m torch.distributed.run --nproc_per_node 2 --nnodes 1 torch-distributed-gpu-test.py

Script in ra OK nếu cả hai GPU giao tiếp và cấp phát bộ nhớ thành công. Xem script chẩn đoán để biết thêm chi tiết và công thức chạy nó trong môi trường SLURM.

Đặt NCCL_DEBUG=INFO để nhận kết quả gỡ lỗi NCCL chi tiết.

NCCL_DEBUG=INFO python -m torch.distributed.run --nproc_per_node 2 --nnodes 1 torch-distributed-gpu-test.py

DeepSpeed

Khi gặp lỗi, hãy kiểm tra xem DeepSpeed có phải là nguyên nhân hay không. Thử lại cấu hình của bạn mà không dùng DeepSpeed, và nếu lỗi vẫn tiếp diễn, hãy báo cáo vấn đề. Đối với các vấn đề không liên quan đến tích hợp Transformers, hãy mở một issue trên repository của DeepSpeed.

Đối với các vấn đề liên quan đến tích hợp Transformers, hãy bao gồm các thông tin sau.

  • Tệp cấu hình DeepSpeed đầy đủ.

  • Các đối số dòng lệnh cho Trainer hoặc TrainingArguments nếu bạn tự viết script để thiết lập Trainer (đừng đổ toàn bộ TrainingArguments vì nó chứa nhiều mục không liên quan).

  • Đầu ra của các lệnh này.

    python -c 'import torch; print(f"torch: {torch.__version__}")'
    python -c 'import transformers; print(f"transformers: {transformers.__version__}")'
    python -c 'import deepspeed; print(f"deepspeed: {deepspeed.__version__}")'
  • Liên kết đến một notebook Google Colab để tái hiện vấn đề.

  • Bộ dữ liệu chuẩn hoặc không tùy chỉnh, hoặc một ví dụ hiện có để tái hiện vấn đề.

Quá trình bị kết thúc khi khởi động

Nếu tiến trình DeepSpeed bị kết thúc trong quá trình khởi động mà không có traceback, chương trình đã cố gắng cấp phát nhiều bộ nhớ CPU hơn mức khả dụng hoặc được cho phép. Lõi hệ điều hành sẽ kết thúc tiến trình trong cả hai trường hợp.

Kiểm tra xem tệp cấu hình của bạn có offload_optimizer, offload_param hoặc cả hai được cấu hình để offload sang CPU hay không.

Nếu bạn đã thiết lập NVMe và ZeRO-3, hãy thử offload sang NVMe thay vì bộ nhớ RAM. Trước tiên, hãy ước tính yêu cầu bộ nhớ của mô hình của bạn.

Loss NaN

Mất mát NaN thường xảy ra khi một mô hình được tiền huấn luyện ở bf16 sau đó được sử dụng với fp16 (điều này đặc biệt phổ biến với các mô hình được huấn luyện trên TPU). Hãy sử dụng fp32 hoặc bf16 nếu phần cứng của bạn hỗ trợ (TPU, GPU Ampere hoặc mới hơn).

fp16 cũng có thể gây ra tràn số. Nếu tệp cấu hình của bạn trông giống như bên dưới, bạn có thể thấy các lỗi tràn trong nhật ký.

{
    "fp16": {
        "enabled": "auto",
        "loss_scale": 0,
        "loss_scale_window": 1000,
        "initial_scale_power": 16,
        "hysteresis": 2,
        "min_loss_scale": 1
    }
}

Lỗi OVERFLOW! bên dưới có nghĩa là bộ điều chỉnh tỷ lệ loss của DeepSpeed không thể tìm được hệ số tỷ lệ để khắc phục tình trạng tràn loss. Hãy thử một giá trị initial_scale_power cao hơn (thường là 32 sẽ hoạt động).

0%|                                                                                                                             | 0/189 [00:00<?, ?it/s]
 [deepscale] OVERFLOW! Rank 0 Skipping step. Attempted loss scale: 262144, reducing to 262144
  1%|▌                                                                                                                    | 1/189 [00:00<01:26,  2.17it/s]
 [deepscale] OVERFLOW! Rank 0 Skipping step. Attempted loss scale: 262144, reducing to 131072.0
  1%|█▏
 [...]
 [deepscale] OVERFLOW! Rank 0 Skipping step. Attempted loss scale: 1, reducing to 1
 14%|████████████████▌                                                                                                   | 27/189 [00:14<01:13,  2.21it/s]
 [deepscale] OVERFLOW! Rank 0 Skipping step. Attempted loss scale: 1, reducing to 1
 15%|█████████████████▏                                                                                                  | 28/189 [00:14<01:13,  2.18it/s]
 [deepscale] OVERFLOW! Rank 0 Skipping step. Attempted loss scale: 1, reducing to 1
 15%|█████████████████▊                                                                                                  | 29/189 [00:15<01:13,  2.18it/s]
 [deepscale] OVERFLOW! Rank 0 Skipping step. Attempted loss scale: 1, reducing to 1
[...]

DeepSpeed CUDA

DeepSpeed biên dịch mã CUDA C++, đây là nguồn gây lỗi build phổ biến đối với các phần mở rộng PyTorch yêu cầu CUDA. Những lỗi này phụ thuộc vào cách CUDA được cài đặt trên hệ thống của bạn.

pip install deepspeed

Đối với các vấn đề cài đặt khác, hãy mở một issue với nhóm DeepSpeed.

Bộ công cụ không giống nhau

PyTorch được cung cấp kèm theo bộ công cụ CUDA của riêng nó, nhưng DeepSpeed yêu cầu phải cài đặt phiên bản CUDA giống hệt trên toàn hệ thống. Nếu bạn đã cài đặt PyTorch với cudatoolkit==10.2 trong môi trường Python của mình, bạn cũng sẽ cần cài đặt CUDA 10.2 ở mọi nơi.

Vị trí chính xác thay đổi tùy theo hệ thống, nhưng /usr/local/cuda-10.2 là đường dẫn phổ biến nhất trên các hệ thống Unix. Sau khi CUDA được cài đặt và thêm vào PATH của bạn, hãy tìm vị trí cài đặt bằng lệnh này.

which nvcc

Nhiều bộ công cụ

Hệ thống của bạn có thể đã cài đặt nhiều hơn một bộ công cụ CUDA.

/usr/local/cuda-10.2
/usr/local/cuda-11.0

Trình cài đặt gói thường đặt các đường dẫn trỏ đến phiên bản đã cài đặt gần nhất. Nếu quá trình build thất bại do không tìm thấy phiên bản CUDA phù hợp, hãy cấu hình PATH và LD_LIBRARY_PATH để trỏ đến đường dẫn chính xác.

Hãy kiểm tra các biến môi trường này trước.

echo $PATH
echo $LD_LIBRARY_PATH

PATH liệt kê các vị trí của tệp thực thi. LD_LIBRARY_PATH liệt kê các vị trí của thư viện dùng chung. Các mục nhập ở phía trước có độ ưu tiên cao hơn, và : dùng để phân tách nhiều mục nhập. Thêm đường dẫn CUDA đúng vào đầu để ưu tiên nó.

# adjust the version and full path if needed
export PATH=/usr/local/cuda-10.2/bin:$PATH
export LD_LIBRARY_PATH=/usr/local/cuda-10.2/lib64:$LD_LIBRARY_PATH

Cũng hãy kiểm tra xem các thư mục đã gán có tồn tại hay không. Thư mục con lib64 chứa các đối tượng CUDA .so như libcudart.so. Hãy kiểm tra tên tệp thực tế và cập nhật cho phù hợp.

Các phiên bản cũ hơn

Các phiên bản CUDA cũ hơn đôi khi yêu cầu các phiên bản trình biên dịch cũ hơn. Ví dụ, nếu CUDA yêu cầu gcc-7 nhưng hệ thống của bạn chỉ có gcc-9, quá trình build sẽ thất bại. Hãy cài đặt trình biên dịch cũ hơn cần thiết và tạo một symlink để hệ thống build của CUDA có thể tìm thấy nó.

# adjust the path to your system
sudo ln -s /usr/bin/gcc-7  /usr/local/cuda-10.2/bin/gcc
sudo ln -s /usr/bin/g++-7  /usr/local/cuda-10.2/bin/g++

Prebuild

Nếu bạn vẫn gặp khó khăn khi cài đặt DeepSpeed hoặc biên dịch nó lúc chạy, hãy biên dịch trước các module của DeepSpeed. Chạy các lệnh bên dưới để biên dịch cục bộ.

git clone https://github.com/deepspeedai/DeepSpeed/
cd DeepSpeed
rm -rf build
TORCH_CUDA_ARCH_LIST="8.6" DS_BUILD_CPU_ADAM=1 DS_BUILD_UTILS=1 pip install . \
--global-option="build_ext" --global-option="-j8" --no-cache -v \
--disable-pip-version-check 2>&1 | tee build.log

Thêm DS_BUILD_AIO=1 vào lệnh build để sử dụng NVMe offload. Đảm bảo bạn đã cài đặt gói libaio-dev trên toàn hệ thống.

Tiếp theo, hãy đặt kiến trúc GPU của bạn trong TORCH_CUDA_ARCH_LIST. Danh sách đầy đủ các GPU NVIDIA và kiến trúc tương ứng nằm trên trang CUDA GPUs. Để kiểm tra phiên bản PyTorch tương ứng với kiến trúc của bạn, hãy chạy lệnh bên dưới.

python -c "import torch; print(torch.cuda.get_arch_list())"

Tìm kiến trúc cho một GPU bằng lệnh sau.

cùng các GPU
GPU cụ thể
CUDA_VISIBLE_DEVICES=0 python -c "import torch; print(torch.cuda.get_device_capability())"

Với kết quả là 8, 6, hãy đặt TORCH_CUDA_ARCH_LIST="8.6". Đối với nhiều GPU có kiến trúc khác nhau, hãy liệt kê chúng như TORCH_CUDA_ARCH_LIST="6.1;8.6".

Bạn có thể bỏ qua TORCH_CUDA_ARCH_LIST và để chương trình build tự động phát hiện kiến trúc GPU, nhưng nó có thể không khớp với GPU thực tế trên máy đích. Việc thiết lập kiến trúc một cách tường minh sẽ đáng tin cậy hơn.

Để huấn luyện trên nhiều máy với cùng cấu hình, hãy xây dựng một bánh xe nhị phân (binary wheel).

git clone https://github.com/deepspeedai/DeepSpeed/
cd DeepSpeed
rm -rf build
TORCH_CUDA_ARCH_LIST="8.6" DS_BUILD_CPU_ADAM=1 DS_BUILD_UTILS=1 \
python setup.py build_ext -j8 bdist_wheel

Điều này tạo ra một bánh xe nhị phân như dist/deepspeed-0.3.13+8cd046f-cp38-cp38-linux_x86_64.whl. Cài đặt nó cục bộ hoặc trên một máy khác.

pip install deepspeed-0.3.13+8cd046f-cp38-cp38-linux_x86_64.whl
Update on GitHub