Files
paddlepaddle--paddle/paddle/phi/infermeta/spmd_rules/optimizer.cc
T
2026-07-13 12:40:42 +08:00

306 lines
12 KiB
C++

/* 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. */
#include "paddle/phi/infermeta/spmd_rules/optimizer.h"
#include "glog/logging.h"
#include "paddle/phi/core/distributed/auto_parallel/dist_attr.h"
#include "paddle/phi/core/distributed/auto_parallel/reshard/reshard_utils.h"
#include "paddle/phi/core/distributed/auto_parallel/utils.h"
#include "paddle/phi/infermeta/spmd_rules/elementwise.h"
#include "paddle/phi/infermeta/spmd_rules/utils.h"
namespace phi::distributed {
SpmdInfo AdamInferSpmdDynamic(
const DistMetaTensor& param,
const DistMetaTensor& grad,
const DistMetaTensor& learning_rate,
const DistMetaTensor& moment1,
const DistMetaTensor& moment2,
const paddle::optional<DistMetaTensor>& moment2_max,
const DistMetaTensor& beta1_pow,
const DistMetaTensor& beta2_pow,
const DistMetaTensor& master_param,
const DistMetaTensor& skip_update,
const Scalar& beta1,
const Scalar& beta2,
const Scalar& epsilon,
bool lazy_mode,
int64_t min_row_size_to_use_multithread,
bool multi_precision,
bool use_global_beta_pow,
bool amsgrad) {
// shape check
PADDLE_ENFORCE(
param.dims().size() == grad.dims().size() &&
moment1.dims().size() == moment2.dims().size() &&
param.dims().size() == moment1.dims().size(),
errors::InvalidArgument(
"param, grad, momentum1 and momentum2 have different ndim."));
// Do spmd infer on param and grad in case of the param and grad
// has different dist attr. This difference may be caused by other spmd.
// No need do the spmd infer on the two momentum, since they are
// separated from the forward backward computation.
SpmdInfo param_grad_spmd = ElementwiseBinaryInferSpmd(param, grad);
TensorDistAttr param_dist_attr_spmd =
PADDLE_GET(TensorDistAttr, param_grad_spmd.first[0]);
TensorDistAttr grad_dist_attr_spmd =
PADDLE_GET(TensorDistAttr, param_grad_spmd.first[1]);
VLOG(3) << "The source dims mapping for param is: "
<< auto_parallel::str_join(param.dist_attr().dims_mapping());
VLOG(3) << "The source dims mapping for grad is: "
<< auto_parallel::str_join(grad.dist_attr().dims_mapping());
VLOG(3) << "The inter dims mapping for param after elementwise spmd is: "
<< auto_parallel::str_join(param.dist_attr().dims_mapping());
VLOG(3) << "The inter dims mapping for grad after elementwise spmd is: "
<< auto_parallel::str_join(grad.dist_attr().dims_mapping());
// create all output dist attrs
TensorDistAttr param_dist_attr =
CopyTensorDistAttrForOutput(param_dist_attr_spmd);
TensorDistAttr grad_dist_attr =
CopyTensorDistAttrForOutput(grad_dist_attr_spmd);
TensorDistAttr lr_dist_attr =
CopyTensorDistAttrForOutput(learning_rate.dist_attr());
TensorDistAttr moment1_dist_attr =
CopyTensorDistAttrForOutput(moment1.dist_attr());
TensorDistAttr moment2_dist_attr =
CopyTensorDistAttrForOutput(moment2.dist_attr());
TensorDistAttr moment2_max_dist_attr =
amsgrad ? CopyTensorDistAttrForOutput(moment2_max.get().dist_attr())
: TensorDistAttr();
TensorDistAttr beta1_pow_dist_attr =
CopyTensorDistAttrForOutput(beta1_pow.dist_attr());
TensorDistAttr beta2_pow_dist_attr =
CopyTensorDistAttrForOutput(beta2_pow.dist_attr());
TensorDistAttr master_param_dist_attr =
master_param.initialized()
? CopyTensorDistAttrForOutput(master_param.dist_attr())
: TensorDistAttr();
// If skip_update is on global_mesh, it should be reshard into
// local mesh. (currently occurs in static mode pipeline parallel)
auto skip_update_dist_attr = TensorDistAttr();
if (skip_update.initialized()) {
skip_update_dist_attr = skip_update.dist_attr();
PADDLE_ENFORCE_EQ(
skip_update_dist_attr.dims_mapping()[0],
-1,
errors::InvalidArgument(
"skip_update should be replicated, but got shard on mesh %d.",
skip_update_dist_attr.dims_mapping()[0]));
skip_update_dist_attr.clean_partial_status();
if (skip_update_dist_attr.process_mesh().ndim() > 1 &&
phi::distributed::IsSubMesh(skip_update_dist_attr.process_mesh(),
param_dist_attr.process_mesh())) {
skip_update_dist_attr.set_process_mesh(param_dist_attr.process_mesh());
}
}
// set the unchanged dims mapping
lr_dist_attr.set_dims_mapping(learning_rate.dist_attr().dims_mapping());
beta1_pow_dist_attr.set_dims_mapping(beta1_pow.dist_attr().dims_mapping());
beta2_pow_dist_attr.set_dims_mapping(beta2_pow.dist_attr().dims_mapping());
// set the changeable dims mapping
auto param_spmd_dims_mapping = param_dist_attr_spmd.dims_mapping();
auto grad_spmd_dims_mapping = grad_dist_attr_spmd.dims_mapping();
auto momentum1_src_dims_mapping = moment1.dist_attr().dims_mapping();
auto momentum2_src_dims_mapping = moment2.dist_attr().dims_mapping();
std::vector<int64_t> momentum2_max_src_dims_mapping;
if (amsgrad) {
momentum2_max_src_dims_mapping =
moment2_max.get().dist_attr().dims_mapping();
}
// Get the final dist attr for param, master_param, grad and momentum.
// Whatever the input dist attrs are, the output dist attr should be same.
// For a specific dim of the tensor:
// If the dim has been sharded on one or more tensors
// and these tensors use a same mesh to shard this dim,
// then this shard status should be kept on the shard tensors
// and should be brought to those unshard tensors.
// Otherwise, if the dim hasn't been sharded an any tensor,
// or different tensors use different meshes to shard the dim,
// then the shard status should be removed on the shard tensors
// and the unshard tensors should keep unshard status.
std::vector<int64_t> dst_dims_mapping;
for (int64_t i = 0; i < param.dims().size(); ++i) {
std::vector<int64_t> shard_status;
if (amsgrad) {
shard_status.assign({param_spmd_dims_mapping[i],
grad_spmd_dims_mapping[i],
momentum1_src_dims_mapping[i],
momentum2_src_dims_mapping[i],
momentum2_max_src_dims_mapping[i]});
} else {
shard_status.assign({param_spmd_dims_mapping[i],
grad_spmd_dims_mapping[i],
momentum1_src_dims_mapping[i],
momentum2_src_dims_mapping[i]});
}
int64_t dst_shard_status = -1;
for (auto status : shard_status) {
if (status == -1) {
// The dim i hasn't been sharded on current tensor.
continue;
} else {
// The dim i has been sharded on current tensor.
if (dst_shard_status == -1) {
dst_shard_status = status;
} else if (dst_shard_status != status) {
// Tensors use different meshes to shard dim i.
// The shard info should be removed.
dst_shard_status = -1;
break;
}
}
}
dst_dims_mapping.emplace_back(dst_shard_status);
}
VLOG(3) << "The source dims mapping for momentum1 is: "
<< auto_parallel::str_join(momentum1_src_dims_mapping);
VLOG(3) << "The source dims mapping for momentum2 is: "
<< auto_parallel::str_join(momentum2_src_dims_mapping);
if (master_param.initialized()) {
VLOG(3) << "The source dims mapping for master param is: "
<< auto_parallel::str_join(master_param.dist_attr().dims_mapping());
}
VLOG(3) << "The final dims mapping for param, master param (if available), "
"grad and momentum1, momentum 2 is: "
<< auto_parallel::str_join(dst_dims_mapping);
param_dist_attr.set_dims_mapping(dst_dims_mapping);
grad_dist_attr.set_dims_mapping(dst_dims_mapping);
if (master_param.initialized()) {
master_param_dist_attr.set_dims_mapping(dst_dims_mapping);
}
moment1_dist_attr.set_dims_mapping(dst_dims_mapping);
moment2_dist_attr.set_dims_mapping(dst_dims_mapping);
if (amsgrad) {
moment2_max_dist_attr.set_dims_mapping(dst_dims_mapping);
}
return {{param_dist_attr,
grad_dist_attr,
lr_dist_attr,
moment1_dist_attr,
moment2_dist_attr,
moment2_max_dist_attr,
beta1_pow_dist_attr,
beta2_pow_dist_attr,
master_param_dist_attr,
skip_update_dist_attr},
{param_dist_attr,
moment1_dist_attr,
moment2_dist_attr,
moment2_max_dist_attr,
beta1_pow_dist_attr,
beta2_pow_dist_attr,
master_param_dist_attr}};
}
SpmdInfo AdamwInferSpmdDynamic(
const DistMetaTensor& param,
const DistMetaTensor& grad,
const DistMetaTensor& learning_rate,
const DistMetaTensor& moment1,
const DistMetaTensor& moment2,
const paddle::optional<DistMetaTensor>& moment2_max,
const DistMetaTensor& beta1_pow,
const DistMetaTensor& beta2_pow,
const DistMetaTensor& master_param,
const DistMetaTensor& skip_update,
const Scalar& beta1,
const Scalar& beta2,
const Scalar& epsilon,
double lr_ratio,
double coeff,
bool with_decay,
bool lazy_mode,
int64_t min_row_size_to_use_multithread,
bool multi_precision,
bool use_global_beta_pow,
bool amsgrad) {
return AdamInferSpmdDynamic(param,
grad,
learning_rate,
moment1,
moment2,
moment2_max,
beta1_pow,
beta2_pow,
master_param,
skip_update,
beta1,
beta2,
epsilon,
lazy_mode,
min_row_size_to_use_multithread,
multi_precision,
use_global_beta_pow,
amsgrad);
}
SpmdInfo SgdInferSpmd(const DistMetaTensor& param,
const DistMetaTensor& learning_rate,
const DistMetaTensor& grad,
const DistMetaTensor& master_param,
bool multi_precision) {
SpmdInfo param_grad_spmd = ElementwiseBinaryInferSpmd(param, grad);
TensorDistAttr param_dist_attr_spmd =
PADDLE_GET(TensorDistAttr, param_grad_spmd.first[0]);
TensorDistAttr grad_dist_attr_spmd =
PADDLE_GET(TensorDistAttr, param_grad_spmd.first[1]);
VLOG(3) << "The source dims mapping for param is: "
<< auto_parallel::str_join(param.dist_attr().dims_mapping());
VLOG(3) << "The source dims mapping for grad is: "
<< auto_parallel::str_join(grad.dist_attr().dims_mapping());
VLOG(3) << "The inter dims mapping for param (master param if available) "
<< "after elementwise spmd is: "
<< auto_parallel::str_join(param.dist_attr().dims_mapping());
VLOG(3) << "The inter dims mapping for grad after elementwise spmd is: "
<< auto_parallel::str_join(grad.dist_attr().dims_mapping());
TensorDistAttr param_dist_attr =
CopyTensorDistAttrForOutput(param_dist_attr_spmd);
TensorDistAttr grad_dist_attr =
CopyTensorDistAttrForOutput(grad_dist_attr_spmd);
TensorDistAttr lr_dist_attr =
CopyTensorDistAttrForOutput(learning_rate.dist_attr());
TensorDistAttr master_param_dist_attr =
master_param.initialized()
? CopyTensorDistAttrForOutput(master_param.dist_attr())
: TensorDistAttr();
param_dist_attr.set_dims_mapping(param_dist_attr_spmd.dims_mapping());
grad_dist_attr.set_dims_mapping(grad_dist_attr_spmd.dims_mapping());
if (master_param.initialized()) {
master_param_dist_attr.set_dims_mapping(
param_dist_attr_spmd.dims_mapping());
}
lr_dist_attr.set_dims_mapping(learning_rate.dist_attr().dims_mapping());
return {
{param_dist_attr, lr_dist_attr, grad_dist_attr, master_param_dist_attr},
{param_dist_attr, master_param_dist_attr}};
}
} // namespace phi::distributed