Files
2026-07-13 12:40:42 +08:00

180 lines
5.7 KiB
Python

# Copyright (c) 2023 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import logging
import numpy as np
from dist_amp_base import (
MLP,
RandomDataset,
compare_state_dict,
create_optimizer,
save_model_parameters,
)
import paddle
from paddle.distributed.fleet.utils.hybrid_parallel_util import (
fused_allreduce_gradients,
)
logging.basicConfig(level="INFO", format="%(message)s")
def train_mlp(
model, train_loader, use_pure_bf16=False, use_main_grad=False, acc_steps=1
):
logging.info(
f"-- Train Info: use_pure_bf16={use_pure_bf16}, use_main_grad={use_main_grad}, acc_steps={acc_steps}"
)
optimizer = create_optimizer(
model=model, use_pure_bf16=use_pure_bf16, use_main_grad=use_main_grad
)
if use_pure_bf16:
level = 'O2'
custom_white_list = None
model = paddle.amp.decorate(
models=model,
dtype="bfloat16",
level=level,
)
else:
level = 'O1'
custom_white_list = [
"matmul_v2",
"elementwise_add",
"relu",
]
model = paddle.DataParallel(model)
if not use_pure_bf16:
for param in model.parameters():
t = paddle.cast(
paddle.cast(param, dtype='bfloat16'), dtype='float32'
)
param.set_value(t)
local_rank = paddle.distributed.get_rank()
losses = []
epoch = 2
for eop in range(epoch):
model.train()
for batch_id, data in enumerate(train_loader()):
data.stop_gradient = True
enable_stats = False # eop == 0
if enable_stats:
logging.info("<<<<<<<<<<<< forward-backward >>>>>>>>>>>")
paddle.amp.debugging.enable_operator_stats_collection()
with model.no_sync():
with paddle.amp.auto_cast(
True,
level=level,
dtype="bfloat16",
custom_white_list=custom_white_list,
):
out = model(data)
# compute loss in float32
loss = paddle.mean(out.astype("float32"))
# normal implementation for gradient accumulation.
if acc_steps != 1:
loss = loss / acc_steps
losses.append(loss.item())
loss.backward()
logging.info(
f"-- [rank={local_rank}] epoch {eop}, batch {batch_id}, loss: {loss.astype(paddle.float32).numpy()}"
)
if enable_stats:
paddle.amp.debugging.disable_operator_stats_collection()
if (batch_id + 1) % acc_steps == 0:
if enable_stats:
logging.info(
"<<<<<<<<<<<< fused_allreduce_gradients >>>>>>>>>>>"
)
paddle.amp.debugging.enable_operator_stats_collection()
fused_allreduce_gradients(list(model.parameters()), None)
if enable_stats:
paddle.amp.debugging.disable_operator_stats_collection()
if enable_stats:
logging.info("<<<<<<<<<<<< optimizer >>>>>>>>>>>")
paddle.amp.debugging.enable_operator_stats_collection()
optimizer.step()
optimizer.clear_grad()
if enable_stats:
paddle.amp.debugging.disable_operator_stats_collection()
model_param_dict = save_model_parameters(model)
optimizer_state_dict = optimizer.state_dict()
return losses, model_param_dict, optimizer_state_dict
def test_dp_bf16():
if not paddle.amp.is_bfloat16_supported():
logging.info("BFloat16 is not supported!")
return
paddle.distributed.init_parallel_env()
local_rank = paddle.distributed.get_rank()
paddle.seed(2023 + local_rank)
np.random.seed(2023 + local_rank)
# For DataParallel, DataLoader should feed different data for different GPUs.
train_loader = paddle.io.DataLoader(
RandomDataset(),
batch_size=100,
shuffle=False,
drop_last=True,
num_workers=0,
)
single_mlp = MLP()
state_dict = single_mlp.state_dict()
def _compare_bf16_o1_vs_o2(acc_steps=1):
# dp bf16 O1 vs dp bf16 O2 main_grad
mlp1 = MLP()
mlp2 = MLP()
mlp1.set_state_dict(state_dict)
mlp2.set_state_dict(state_dict)
losses_o1, model_param_dict_o1, optimizer_state_dict_o1 = train_mlp(
mlp1, train_loader, use_pure_bf16=False, acc_steps=acc_steps
)
losses_o2, model_param_dict_o2, optimizer_state_dict_o2 = train_mlp(
mlp2,
train_loader,
use_pure_bf16=True,
use_main_grad=True,
acc_steps=acc_steps,
)
np.testing.assert_array_equal(losses_o2, losses_o1)
compare_state_dict(
model_param_dict_o1, model_param_dict_o2, optimizer_state_dict_o2
)
# no gradient accumulation
_compare_bf16_o1_vs_o2(acc_steps=1)
# gradient accumulation
_compare_bf16_o1_vs_o2(acc_steps=2)
if __name__ == '__main__':
test_dp_bf16()