소개
더 큰 딥 러닝 모델은 더 많은 컴퓨팅 파워와 메모리 자원이 필요합니다. 새로운 기법의 개발을 통해 딥 뉴럴 네트워크의 훈련 속도가 빨라졌습니다. FP32(정밀도 부동소수점 숫자 형식) 대신 FP16(반정밀도 부동소수점 숫자 형식)을 사용할 수 있으며, 연구자들은 이 둘을 함께 사용하는 것이 더 나은 선택이라는 것을 발견했습니다.
혼합 정밀도는 반정밀도 훈련을 가능하게 하면서도 단일 정밀도 네트워크의 정확도를 대부분 유지합니다. “혼합 정밀도 기법”이라는 용어는 이 방법이 단일 정밀도와 반정밀도 표현을 모두 사용한다는 사실을 나타냅니다.
이 PyTorch를 사용한 자동 혼합 정밀도(Amp) 훈련 개요에서는 이 기법이 어떻게 작동하는지 단계별로 설명하고, Amp 기법의 고급 응용 프로그램을 코드 스캐폴드를 통해 사용자들이 자신의 코드에 통합할 수 있도록 논의합니다.
전제 조건
PyTorch 기본 지식: 텐서, 모듈, 훈련 루프와 같은 PyTorch의 핵심 개념에 대한 친숙함.
딥 러닝 기본 개념 이해: 뉴럴 네트워크, 역전파, 최적화와 같은 개념.
이진 精度和 함께 사용하는 지식: 이진 精度을 사용하는 것의 장점과 단점을 알고 있다, 메모리 사용 감소와 더 빨라진 연산이 included.
인력 장치에 대한 准入: Tensor Cores를 지원하는 NVIDIA GPU(예: Volta, Turing, Ampere 구성)과 같은 이진 精度和를 지원하는 GPU.
Python과 CUDA 설정: PyTorch을 설치한 工作经验 Python 환경과 CUDA를 GPU 가속에 대한 구성.
이진 精度 개요
대부분의 深層 learning 프레임워크와 마찬가지로, PyTorch은 대략 32-bit 실수 데이터(FP32)를 사용하여 训练한다. 하지만 FP32은 언제든지 성공에 필요한 것이 아니다. 몇몇 操作에 16-bit 실수를 사용할 수 있으며, FP32는 시간과 메모리를 더 많이 소모한다.
따라서, NVIDIA 엔지니어는 일부 操作에 FP32로 이진 精度和 训练을 수행하면서, 대부분의 네트워크가 FP16로 동작하는 기술을 개발했습니다.
- 모델을 float16 데이터 유형을 사용하도록 변경합니다.
- float32 마스터 가중치를 보관하여 매 iteration에 가중치 갱신을 accumulate.
- loss scaling을 사용하여 작은 gradient 값을 보존합니다.
PyTorch의 이동 精위 merged-precision
mixed-precision 트레이닝을 위해, PyTorch에는 이미 내장되어 있는 많은 기능이 있습니다.
모듈의 パラメ터는 .half()
메서드를 호출할 때 FP16로 변환되며, 텐서의 데이터는 .half()
메서드를 호출할 때 FP16로 변환됩니다. 이러한 모듈 또는 텐서에 대한 的任何操作은 快速 FP16 算術을 사용하게 됩니다. NVIDIA 수학 라이브러리 (cuBLAS과 cuDNN)는 PyTorch와 良く 호환되어 있습니다. FP16 파이프라인에서 来た データ는 텐서 コア를 使用하여 GEMM과 畳み込み를 実行합니다. GEMM ([M, K] x [K, N] -> [M, N])의 次元은 8의 倍数이어야 Tensor Cores를 cuBLAS에서 使用할 수 있습니다.
Apex 소개
Apex의 mixed-precision 유틸리티는 single-precision 트레이닝의 精度과 안정성을 유지한채 트레이닝 速度를 향상시키는 것을 目的として 제공합니다. Apex는 FP16 또는 FP32로 操作을 수행할 수 있으며, 마스터 パラ미터 変換을 자동으로 처리하고 손실을 자동으로 缩放할 수 있습니다.
Apex은 研究者들이 mixed-precision 트레이닝을 모델에 포함하는 것을 더 쉽게 할 수 있게 만들어진 기술입니다. Amp(Automatic Mixed-Precision)는 Apex의 하나의 기능으로, 가볍고 시각적인 PyTorch 확장입니다. 사용자는 자신의 네트워크에 몇 行의 추가적인 수정으로 Amp를 사용하여 mixed precision training의 이점을 누리실 수 있습니다. Apex는 CVPR 2018에 발전되었으며, launching 이후부터 PyTorch 社区에서 Apex에 대한 强力的한 지지를 보였습니다.
Amp를 사용하여 실행 모델에 trivially 수정하여 mixed types를 worry-free로 만들 수 있습니다. mixed types를 사용하는 것을 인지하는 것을 방지하고 스크립트를 생성하거나 실행할 때에는 어느 정도 유용합니다. Amp의 仮가이IONS는 PyTorch를 少し 사용하는 것과 달리 적절하지 않을 수 있으며, 필요한 조건에 따라 조절할 수 있는 딜로이어가 있습니다.
Amp는 loss scaling 또는 type conversions을 명시적으로 관리하지 않아도 mixed-precision training의 이점을 모두 제공합니다. Apex GitHub 웹사이트에는 インストール 手順의 지침이 있으며, 공식 API 문서는 여기에 있습니다.
Amp의 작동 방법
Amp는 논리 层次에서 whitelist/blacklist Paradigm을 사용합니다. PyTorch의 Tensor 操作은 torch.nn.functional.conv2d과 같은 신경망 함수, torch.log과 같은 간단한 数学 함수, torch.Tensor. add__과 같은 Tensor 方法을 포함합니다. 이 계ledge universe는 세 가지 주요 범주의 함수를 포함합니다:
- Whitelist: FP16 数学의 속도 향상을 얻을 수 있는 함수들. 典型的적인 응용은 행렬 곱셈과 convolution입니다.
- Blacklist: 16 비트의 정밀도가 충분하지 않을 수 있는 함수에서 FP32로 입력이 되어야 합니다.
- 나머지 모든 것 ( reminder functions): FP16에서 실행할 수 있는 함수지만, FP32 -> FP16 キャスト하여 FP16로 실행하는 비용은 유용하지 않으며 성능 향상은 유용하지 않습니다.
Amp의 태스크는 理論적으로 간단합니다. Amp는 PyTorch 함수가 whitelist, blacklist, 또는 그렇지 않은지 determination을 하고 호출합니다. 모든 인자는 whitelist일 때 FP16로, blacklist일 때 FP32로 변환되어야 합니다. 그렇지 않으면, 모든 인자가 같은 형식이어야 합니다. 이 정책은 실제로 적용하기 보다는 보고나마 간단해 보입니다.
PyTorch 모델과 함께 Amp를 사용하는 방법
Amp를 현재的 PyTorch 스크립트에 포함하려면 다음 단계를 따라야 합니다.:
- Apex 라이브러리를 사용하여 Amp를 導入합니다.
- Amp를 초기화하여 모델, 최적화기, 및 PyTorch 내부 함수에 필요한 변경을 commit할 수 있도록 합니다.
- 回propagation (.backward())가 실제로 발생하는 곳을 Salvage하여 Amp가 loss를 스케일링하고 iteration 단위의 상태를 초기화할 수 있도록 합니다.
Step 1
첫 단계는 한 行의 코드로 구성됩니다:
Step 2
인공지능 네트워크 모델과 옵티마이저가 이미 지정되어 있어야 complete 단계를 완료할 수 있으며, 이 단계는 한 行의 코드로 구성됩니다.
추가적인 옵션은 Amp의 tensor와 操作 유형 조정을 精細 조정하는 수_{(}있습니다. 함수 amp.initialize()는 많은 인자를 받을 수 있지만, 그 중 세 가지를 지정할 것입니다:
- models (torch.nn.Module or list of torch.nn.Modules) – 수정하거나 캐스팅 할 모델을 의미합니다.) – 수정하거나 캐스팅 할 모델을 의미합니다.
- 최적화기 (선택적, torch.optim.Optimizer 또는 torch.optim.Optimizers의 리스트) – 수정하거나 캐스팅하는 최적화기. 훈련에 대해 필수적이며, 推論에 대해 선택적입니다.
- opt_level (문자열, 선택적, 기본값=“O1”) – purely or mixed precision optimization level. 허용되는 값은 “O0”, “O1”, “O2”, “O3″이며 상세히 설명되었습니다. 네 가지 최적화 수준이 있습니다:
O0 for FP32 training: 이것은 no-op입니다. 이미 FP32로 들어오는 모델이 있으므로 O0가 정확성 기준이 되는 것을 고려할 필요가 없습니다.
O1 for Mixed Precision (typical use에 적합한 것): Tensor과 Torch 方法的 whitelist-blacklist 입력 캐스팅 스키마를 사용하도록 모두 수정합니다. FP16로, Tensor Core-friendly 操作, 예를 들어 GEMMs와 컨볼루션을 실시합니다. Softmax은 FP32 精度的이 필요한 blacklist 操作입니다. O1이 dynamic loss scaling을 적용하는데 除非另有说明, 이에 대한 의사를 두고 있습니다.
O2 for “Almost FP16” Mixed Precision: O2는 모델 가중치를 FP16로 캐스팅하고, 모델의 forward 方法을 패치하여 입력 데이터를 FP16로 캐스팅하고, batchnorms를 FP32로 유지하고, FP32 주 가중치를 유지하고, 최적화기의 param_groups를 更新하여 최적화기.step()가 FP32 가중치에 직접 영향을 미칠 수 있도록 합니다. 동적 loss scaling을 구현하는데 (除非覆盖), O1과 다르게 Torch 方法 또는 Tensor methods를 패치하지 않습니다.
O3로 FP16 트레이닝: O3는 진짜 혼합 精度的 안정性을 compromise 할 수 있습니다. 따라서, モデル에 대한 기본 速度를 설정하는 것이 O1과 O2의 效率를 evaluate 할 수 있도록 도울 수 있습니다.
O3에서 keep_batchnorm_fp32=True 추가적인 속성 오버라이딩을 사용하여 모델이 배치 正規화를 사용하는 경우 “speed of light”를 결정할 수 있습니다. 이렇게 하면 cuDNN 배치 정규화를 사용할 수 있습니다.
O0과 O3는 진짜 mixed-precision가 아니지만, 각각 정확성과 速度의 기본 경계를 설정합니다. mixed-precision 구현은 O1과 O2로 정의됩니다.
이러한 둘 다 시도하여 특정 모델에서 パフォーマンス와 정확성을 가장 많이 改善시키는지 확인할 수 있습니다.
Step 3
코드에서 backward pass가 어디에 있는지 확인하십시오.
이러한 몇 行의 코드가 나타날 것입니다:
loss = criterion(…)
loss.backward()
optimizer.step()
Step4
Amp 컨텍스트 관리자를 사용하여 loss scaling를 사용하기 위해 역전파를 WRAP하십시오:
loss = criterion(…)
with amp.scale_loss(loss, optimizer) as scaled_loss:
scaled_loss.backward()
optimizer.step()
이것만. mixed-precision 트레이닝을 사용하도록 스크립트를 다시 실행하십시오.
함수 호출 캡쳐
PyTorch은 가까운 것이 같지만 고정적이지 않은 모델 객체나 그래프를 가지고 있지 않기 때문에 oven mentioned above에 대한 cast를 적용하기 어렵다. “monkey patching“이란 필요한 함수에 대한 것을 통해, Amp는 동적으로 인터셉터를 사용하여 パラメー터를 cast할 수 있다.
예를 들어, 다음과 같은 코드를 사용하여 torch.nn.functional.linear 메서드의 인자가 항상 fp16로 cast되는지 확인할 수 있다.
orig_linear = torch.nn.functional.linear
def wrapped_linear(*args):
casted_args = []
for arg in args:
if torch.is_tensor(arg) and torch.is_floating_point(arg):
casted_args.append(torch.cast(arg, torch.float16))
else:
casted_args.append(arg)
return orig_linear(*casted_args)
torch.nn.functional.linear = wrapped_linear
Amp는 코드를 더욱 유지하기 위한 수정을 추가하는지 마음이 들 수 있지만, Amp.init()를 호출하면 실제로 모든 관련 PyTorch 함수에 monkey patches가 삽입되며 runtime에서 인자를 correct casting 하도록 한다.
Casts 최소화
Amp는 모든 パラ미터 cast를 관리하는 내부 캐시를 持ち、必要할 때마다 재사용한다. 각 迭代, 역전파를 위한 컨텍스트 관리자가 Amp에게 캐시를 지웁니다.
PyTorch를 사용하여 Autocasting과 梯度 Scaling
“자동 mixed precision 트레이닝”란 torch.cuda.amp.autocast와 torch.cuda.amp.GradScaler를 결합한 것을 말합니다. torch.cuda.amp.autocast를 사용하면, 특정 영역에 대해 자동 casting을 설정할 수 있습니다. 자동 casting은 GPU 操作에 대해 정확도를 유지하면서 효율성을 최적화하기 위해 자동으로 精度的을 선택합니다.
torch.cuda.amp.GradScaler 인스턴스는 梯度 缩放 단계를 더 쉽게 실행하는 것을 도울 수 있습니다. 梯度 缩放은 梯度 하이드로우를 줄이는 것으로, float16 梯度를 사용하는 네트워크에서 更好的 결收敛을 도울 수 있습니다.
autocast()를 사용하여 PyTorch에서 자동 mixed precision를 얻는 방법을 보여주는 一些 코드가 다음과 같습니다:
# 기본 precision로 model과 optimizer를 생성합니다.
model = Net().cuda()
optimizer = optim.SGD(model.parameters(), ...)
# 트레이닝의 시작时에 GradScaler를 한 번 생성합니다.
scaler = GradScaler()
for epoch in epochs:
for input, target in data:
optimizer.zero_grad()
# autocast를 사용하여 전 send pass를 실행합니다.
with autocast(device_type='cuda', dtype=torch.float16):
output = model(input)
loss = loss_fn(output, target)
# backward operation은 相应 forward operation에 대해 autocast가 선택한 同样的 dtype로 실행됩니다.
scaler.scale(loss).backward()
# scaler.step()는 최초로 optimizer의 지정된 パラ미터의 梯度를 非缩放到하고 있습니다.
scaler.step(optimizer)
# 다음 iteration에 대한 缩放을 갱신합니다.
scaler.update()
하나의 operaion의 앞으로 전달되는 float16 입력이 있으면, 이 operation의 뒷면 전달은 float16 梯度를 생성하며, float16는 gradient의 작은 Magditude를 표현할 수 없을 수 있다.
이러한 값이 zero로 washed out되면(“underflow”), 관련 parameter의 update가 잃어버린다.
Gradient scaling은 network의 loss를 multiply하기 위해 scale factor를 사용하고, 확장된 loss로 backward pass를 수행하여 underflow를 避け는 기술이며, 同样의 계수로 network로 유지되는 gradient를 확장해야 한다. 그 结果, gradient value가 larger magnitude를 가지고, zero로 washed out되는 것을 防止한다.
parameter update하기 전, 각 parameter의 gradient(.grad attribute)를 unscaled 한다는 것은, scale factor가 learning rate와 interfere하지 않는다. autocast와 GradScaler가 modular이므로 독립적으로 사용할 수 있다.
Unscaled Gradients 사용
Gradient clipping
Scaler.scale(Loss).backward()
方法을 사용하여 모든 그라디언트를 크기 조절할 수 있습니다. backward()
과 scaler.step(optimizer)
사이에 .grad
속성은 변경하거나 inspec를 할 때 전 스케일을 해제해야 합니다. 글로벌 norm(torch.nn.utils.clip_grad_norm_()) 또는 가장 큰 Magintude(torch.nn.utils.clip_grad_value_())를 제한하려고 하면,“gradient clipping”이라고 불리는 기술을 사용할 수 있습니다.
스케일을 해제하지 않고 클리핑하면, 그라디언트의 법칙/가장 큰 Magintude가 스케일되는 것이 나타납니다. 요구한 threshold(스케일 안 되어 있는 그라디언트의 threshold)가 무엇인지 모를 것입니다. Gradients in the optimizer’s given parameters are unscaled by scaler.unscale (optimizer).
Another optimizer’s (such as optimizer1) gradients given previously can be unscaled by using scaler.unscale (optimizer1). We can illustrate this concept by adding two lines of codes:
# Unscales the gradients of optimizer's assigned params in-place
scaler.unscale_(optimizer)
# Since the gradients of optimizer's assigned params are unscaled, clips as usual:
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm)
스케일 그레이디언트 작업
그레이디언트 ACCUMULATION
그레이디언트 ACCUMULATION은 absurdly 기본적인 아이디어를 기반으로 동작합니다. 모델 파라미터를 갱신하는 것 대신, 그레이디언트를 uccessive batches 를 통해 쌓아 forth 하여 loss와 그레이디언트를 계산합니다.
certain number of batches 를 경과하면, accumulated gradient에 따라 파라미터를 갱신합니다. 다음은 gradient accumulation using pytorch을 사용하는 것을 示す 한 조각의 code입니다:
scaler = GradScaler()
for epoch in epochs:
for i, (input, target) in enumerate(data):
with autocast():
output = model(input)
loss = loss_fn(output, target)
# loss를 정규화하는 것
loss = loss / iters_to_accumulate
# scaled gradients을 Accumulates 합니다.
scaler.scale(loss).backward()
# weights update
if (i + 1) % iters_to_accumulate == 0:
# desired면 unscale_ 를 여기에 사용할 수 있습니다.
scaler.step(optimizer)
scaler.update()
optimizer.zero_grad()
- Gradient accumulation는 batch_ per_iter * iters_to_accumulate의 적절한 batch size를 통해 gradients을 더합니다.
scale는 유효한 batch를 위한 kalibration이 필요합니다; 이 것은 inf/NaN grades를 확인하는 것, 에러 감지되면 step을 건너뛰는 것, 유효한 batch의 granularity로 scale를 갱신하는 것입니다.
또한, 특정 유효한 batch의 grads를 더할 때, grads를 scaled 하고 일관성이 있는 scale factor를 유지하는 것이 매우 중요합니다.
그릇 전에 모두 누적이 끝나기 전에 graduating students (또는 스케일 係수가 변경되었다) unscaled (또는 다른 係수로 스케일 되어 있는) grads를 다음 이후ward pass에서 scaled grads를 추가하게 되면 (이 이후에는 누적되었던 unscaled grads를 recover할 수 없게 되므로) step을 적용해야 하는 unscaled grads step을 적용해야 한다.
- You can unscale grads by using unscale shortly before step, after all the scaled grads for the forthcoming step have been accumulated.
为了确保完整的有效批量,在每次迭代的末尾调用update, 你在之前调用step的地方 - enumerate(data) 函数允许我们在遍历数据时跟踪批量索引。
- 运行损失除以iters_to_accumulate(loss / iters_to_accumulate)。 这通过正则化损失,减小了我們正在处理的每个mini-batch的贡献。 如果你在每個批次内平均损失,那么除法已经是正确的,不需要进一步正则化。 这个步骤根据你是如何计算损失的,可能是不必要的。
scaler.scale(loss).backward()
를 사용할 때, PyTorch는 스케일 밸류를 모두 누적하고optimizer.zero_grad()
가 호출되기 전까지 그들을 보관합니다.
경사 벌금
경사 벌금을 구현하는 것을 때, torch.autograd.grad()를 사용하여 경사를 구축하고, 이를 결합하여 벌금 값을 생성하고 이를 손실에 더해줍니다. 스케일링 없이나 자동 キャ스팅 없는 L2 벌금은 아래 예시에 나타낼 수 있습니다.
for epoch in epochs:
for input, target in data:
optimizer.zero_grad()
output = model(input)
loss = loss_fn(output, target)
# 경사를 생성합니다
grad_prams = torch.autograd.grad(outputs=loss,
inputs=model.parameters(),
create_graph=True)
# 벌금 조건을 계산하고 손실에 추가합니다
grad_norm = 0
for grad in grad_prams:
grad_norm += grad.pow(2).sum()
grad_norm = grad_norm.sqrt()
loss = loss + grad_norm
loss.backward()
# 여기에 경사를 자르는 것이 가능합니다
optimizer.step()
torch.autograd.grad()로 제공되는 텐서는 경사 벌금을 구현하기 위해 스케일링되어야 합니다. 벌금 값을 얻기 전에 그들을 언스ケ일하는 것이 필요합니다. 벌금 조건 계산은 전방향 통과의 일부이며, 자동 キャ스팅 コン텍스트 안에서 이뤄지어야 합니다.
同様の L2 벌금을 위해서는 다음과 같습니다:
scaler = GradScaler()
for epoch in epochs:
for input, target in data:
optimizer.zero_grad()
with autocast():
output = model(input)
loss = loss_fn(output, target)
# autograd.grad의 バックpropagation에 대한 損失比例 scaling 실행, 결과 #scaled_grad_prams
scaled_grad_prams = torch.autograd.grad(outputs=scaler.scale(loss),
inputs=model.parameters(),
create_graph=True)
# 罚則을 computing 하기 전에 grad_prams를 Creates(grad_prams는 #unscaled 해야 함).
# 오timizer가 scaled_grad_prams을 소유하지 않으므로, scaler.unscale_를 대신 정통적인 분할을 사용한다:
inv_scaled = 1./scaler.get_scale()
grad_prams = [p * inv_scaled for p in scaled_grad_prams]
# 罚則 항목을 계산하고 손실에 추가한다.
with autocast():
grad_norm = 0
for grad in grad_prams:
grad_norm += grad.pow(2).sum()
grad_norm = grad_norm.sqrt()
loss = loss + grad_norm
# 역전파 호출에 대한 比例 적용
# 적절히 比例을 적용한 叶の gradient를 累計한다.
scaler.scale(loss).backward()
# 여기에 unscale_할 수 있습니다
# step()와 update()은 habitsual로 진행된다.
scaler.step(optimizer)
scaler.update()
다양한 모델, 손실, 및 오PTIMIZER를 사용하는 것
Scaler.scale 함수는 네트워크에 많은 손실이 있으면 각 손실에 대해 호출되어야 합니다.
네트워크에 많은 오픈이라면 scaler.unscale 함수를 어느 오픈에서나 실행할 수 있으며, 각 오픈에 대해 scaler.step 함수를 호출해야 합니다. 그러나 scaler.update 함수는 이 迭代에 사용된 모든 오픈의 스텝이 다 실행되고 나면 한번만 사용해야 합니다:
scaler = torch.cuda.amp.GradScaler()
for epoch in epochs:
for input, target in data:
optimizer1.zero_grad()
optimizer2.zero_grad()
with autocast():
output1 = model1(input)
output2 = model2(input)
loss1 = loss_fn(2 * output1 + 3 * output2, target)
loss2 = loss_fn(3 * output1 - 5 * output2, target)
# although retain graph는 amp와 관련이 없습니다, 이 예시에서 두 번의 backward() 호출이 某些한 그래프 영역을 공유하기 때문에 제시되고 있습니다.
scaler.scale(loss1).backward(retain_graph=True)
scaler.scale(loss2).backward()
# iteration에 사용된 모든 오픈의 기울기를 보거나 조정하려면, 명시적으로 unscaling을 받는 오픈을 지정할 수 있습니다. .
scaler.unscale_(optimizer1)
scaler.step(optimizer1)
scaler.step(optimizer2)
scaler.update()
각 오픈은 자신의 기울기를 검사하여 inf/NaN이 있는지 여부를 개인적으로 결정하고, 스텝을 건너뛰는지 여부를 결정합니다. 某些 오픈은 스텝을 건너뛰고, 다른 것은 그렇지 않을 수 있습니다. 스텝 건너뛰기는 数百 iterations에 한번씩이나 발생하므로, 收敛하는 것에 영향을 줄 수 있지 않습니다. 다중 오픈 모델에 대해서는, gradient scaling을 추가 한 후 나쁜 收敛하다가 convergence이 나쁜 것으로 나오면 문제를 보고할 수 있습니다.
다중 GPU 작업
딥 러닝 모델과 관련된 가장 중요한 문제 중 하나는 모델이 너무 커져서 단일 GPU에서 훈련하기 어렵다는 것입니다. 단일 GPU에서 모델을 훈련하는 데 시간이 너무 오래 걸릴 수 있으며, 모델을 가능한 한 빨리 준비하기 위해 다중 GPU 훈련이 필요합니다. 한 유명한 연구자는 ImageNet 훈련 기간을 2주에서 18분으로 단축하거나 가장 크고 가장 진보된 Transformer-XL을 4년 대신 2주 만에 훈련할 수 있었습니다.
DataParallel 및 DistributedDataParallel
品質 compromise 하지 않으면서, PyTorch는 사용하기 쉽고 控制在하기 容易한 가장 좋은 조합을 제공합니다. nn.DataParallel과 nn.parallel.DistributedDataParallel는 여러 GPU에 트레이닝을 분산시키기 위한 두 가지 PyTorch 기능입니다. 이러한 쉽고 사용하기 좋은 WRAPPER를 사용하고 변경하여 GPU를 다양하게 사용하여 네트워크 트레이닝을 할 수 있습니다.
单个 프로세스 DataParallel
单一 计算机에서 DataParallel는 다양한 GPU로 트레이닝을 분산시키는 것을 도울 수 있습니다.
DataParallel의 실제 동작 방식에 更に 가까이로 봐봅시다.
DataParallel를 사용하여 신경망 트레이닝을 行う 때, 다음과 같은 단계가 실행되ます:
- mini-batch가 GPU:0 上에서 분할되는 것입니다.
- mini-batch를 나누고 모든 사용 가능한 GPU에 분산시키는 것입니다.
- 모델을 GPUs에 복사합니다.
- 모든 GPU에서 전이 수행합니다.
- 모든 GPU에서 출력에 대한 손실을 계산하고, 손실을 다양한 GPU로 돌려 놓는 것입니다. 각 GPU에서 기울기를 계산해야 합니다.
- GPU:0에서 기울기를 합산하고 최적화기를 적용하여 모델을 업데이트합니다.
여기서 논의된 우려 사항은 autocast에만 적용된다는 점을 주목할 필요가 있습니다. GradScaler의 동작은 변하지 않습니다. torch.nn.DataParallel이 각 장치에 대해 스레드를 생성하여 전방 패스를 수행하든 상관없습니다. 각 스레드에 autocast 상태가 전달되며, 다음과 같이 작동합니다:
model = Model_m()
p_model = nn.DataParallel(model)
# 메인 스레드에서 autocast 설정
with autocast():
# p_model에서 autocasting이 있을 것입니다.
output = p_model(input)
# loss_fn도 autocast
loss = loss_fn(output)
DistributedDataParallel, 프로세스당 하나의 GPU
torch.nn.parallel.DistributedDataParallel 文档에서는 가장 좋은 パフォーマンス를 얻기 위해 每个プロセス당 GPU 하나를 사용하라고 안내하며, 이 경우 DistributedDataParallel 는 내부에서 スレッド를 시작하지 않으므로, autocast と GradScaler 的使用은 영향을 받지 않습니다.
DistributedDataParallel, 每个プロセス당 複数の GPU
torch.nn.parallel.DistributedDataParallel 는 torch.nn.DataParallel 과 마찬가지로 각 装置上で 前方传播(forward pass)을 실행하기 위해 サイドスレッド를 생성할 수 있습니다. 해결 办法은 같습니다: 모델의 前方(forward) 메서드의 일부로 autocast을 적용하여 サイドスレッド에서 활성화되도록 Ensure 합니다.
結論
이 記事で는 다음과 같은 내용을 見所学했습니다:
- Apex를 소개했습니다.
- Amps가 어떻게 작동하는지 보았습니다.
- 그리디언트 스케일링, 그리디언트 클립핑, 그리디언트 축적 및 그리디언트 罰则을 어떻게 수행하는지 보았습니다.
- 다중 모델, 손실 함수 및 최적화기로 일어날 수 있는 것을 보았습니다.
- 하나의 プロ세스로 다중 GPU를 사용하여 DataParallel을 수행하는 방법을 보았습니다.
참고 문헌
https://developer.nvidia.com/blog/apex-pytorch-easy-mixed-precision-training/
https://nvidia.github.io/apex/amp.html
https://discuss.pytorch.org/t/accumulating-gradients/30020
https://towardsdatascience.com/how-to-scale-training-on-multiple-gpus-dae1041f49d2
Source:
https://www.digitalocean.com/community/tutorials/automatic-mixed-precision-using-pytorch