Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[PIR save/load] Migrate static.save and static.load into pir #63749

Merged
merged 8 commits into from
Apr 25, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 64 additions & 21 deletions paddle/fluid/pybind/pir.cc
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <utility>

#include "paddle/common/flags.h"
#include "paddle/fluid/framework/executor.h"
#include "paddle/fluid/framework/ir/pass.h"
#include "paddle/fluid/framework/program_desc.h"
#include "paddle/fluid/ir_adaptor/translator/program_translator.h"
Expand Down Expand Up @@ -762,6 +763,24 @@ phi::DataType GetValueDtype(Value value) {
}
}

std::string GetValueName(Value value) {
if (auto param_op = value.defining_op<::pir::ParameterOp>()) {
return param_op.param_name();
} else if (auto data_op = value.defining_op<paddle::dialect::DataOp>()) {
return data_op.attribute<pir::StrAttribute>("name").AsString();
} else if (auto block_arg = value.dyn_cast<BlockArgument>()) {
if (block_arg.is_kwarg()) {
return block_arg.keyword();
} else {
return "arg_" + std::to_string(block_arg.index());
}
} else {
PADDLE_THROW(phi::errors::InvalidArgument(
"Currently, we can only get name of Value that "
"is persistable"));
}
}

const phi::DDim &GetValueDims(Value value) {
if (!value.type()) {
PADDLE_THROW(phi::errors::InvalidArgument("The type of value is nullptr."));
Expand Down Expand Up @@ -812,6 +831,18 @@ pir::Value apply(Value self, py::object func) {
}

#define DEF_VALUE_BOOL_PROPERTY(name) \
def_property( \
name, \
[](Value self) { \
auto bool_data = self.attribute<BoolAttribute>(name); \
return bool_data && bool_data.data(); \
}, \
[](Value self, bool bool_data) { \
self.set_attribute( \
name, BoolAttribute::get(pir::IrContext::Instance(), bool_data)); \
})

#define DEF_VALUE_STOP_GRADIENT_PROPERTY(name) \
def_property( \
name, \
[](Value self) { \
Expand Down Expand Up @@ -881,26 +912,8 @@ void BindValue(py::module *m) {
return ss.str();
}
})
.def_property_readonly(
"name",
[](Value self) {
if (auto param_op = self.defining_op<::pir::ParameterOp>()) {
return param_op.param_name();
} else if (auto data_op =
self.defining_op<paddle::dialect::DataOp>()) {
return data_op.attribute<pir::StrAttribute>("name").AsString();
} else if (auto block_arg = self.dyn_cast<BlockArgument>()) {
if (block_arg.is_kwarg()) {
return block_arg.keyword();
} else {
return "arg_" + std::to_string(block_arg.index());
}
} else {
PADDLE_THROW(phi::errors::InvalidArgument(
"Currently, we can only get name of Value that "
"is persistable"));
}
})
.def_property_readonly("name",
[](Value self) { return GetValueName(self); })
.def_property(
"shape",
[](Value self) { return phi::vectorize(GetValueDims(self)); },
Expand Down Expand Up @@ -937,7 +950,7 @@ void BindValue(py::module *m) {
return true;
}
})
.DEF_VALUE_BOOL_PROPERTY("stop_gradient")
.DEF_VALUE_STOP_GRADIENT_PROPERTY("stop_gradient")
.DEF_VALUE_BOOL_PROPERTY("trainable")
.DEF_VALUE_BOOL_PROPERTY("persistable")
.DEF_VALUE_BOOL_PROPERTY("need_clip")
Expand Down Expand Up @@ -1710,6 +1723,35 @@ pir::Type CreateDistDenseTensorTypeByDenseTensor(
}
}

static void inline CreateVariableIfNotExist(
const std::vector<pir::Value> &var_list,
const framework::Scope &scope,
const framework::Executor *exe = nullptr) {
size_t len = var_list.size();

for (size_t i = 0; i < len; ++i) {
pir::Value value = var_list[i];
std::string para_name = GetValueName(value);
auto var = scope.FindVar(para_name);
if (var == nullptr) {
PADDLE_ENFORCE_NOT_NULL(exe,
phi::errors::InvalidArgument(
"Parameter not Initialized, "
"Please set argument [executor] not None "
"or run startup program first"));
var = const_cast<framework::Scope *>(&scope)->Var(para_name);
auto *tensor_temp = var->GetMutable<phi::DenseTensor>();
tensor_temp->Resize(
common::make_ddim(phi::vectorize(GetValueDims(value))));
phi::DeviceContextPool &pool = phi::DeviceContextPool::Instance();
const phi::DeviceContext *dev_ctx = nullptr;
dev_ctx = pool.Get(exe->GetPlace());
dev_ctx->Alloc(tensor_temp, GetValueDtype(value));
}
}
return;
}

void ResetShadowOutputName(pir::Operation *op, const std::string &name) {
pir::IrContext *ctx = pir::IrContext::Instance();
if (op->isa<pir::ShadowOutputOp>()) {
Expand All @@ -1718,6 +1760,7 @@ void ResetShadowOutputName(pir::Operation *op, const std::string &name) {
}

void BindUtils(pybind11::module *m) {
m->def("create_loaded_parameter", CreateVariableIfNotExist);
m->def("clone_program", CloneProgram);
m->def("get_op_inplace_info", GetOpInplaceInfo);
m->def("reset_shadow_output_name", ResetShadowOutputName);
Expand Down
4 changes: 2 additions & 2 deletions python/paddle/base/executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ def pir_check_feed_shape_type(feed, name, target_shape, dtype, num_places=1):
"""
diff_shape = core.diff_tensor_shape(feed, target_shape, num_places)
if diff_shape is not None:
raise ValueError(
warnings.warn(
'The fed Variable %r should have dimensions = %d, shape = '
'%r, but received fed shape %r on each device'
% (name, len(target_shape), target_shape, diff_shape)
Expand All @@ -315,7 +315,7 @@ def pir_check_feed_shape_type(feed, name, target_shape, dtype, num_places=1):
if isinstance(feed._dtype(), core.VarDesc.VarType)
else feed._dtype()
)
raise ValueError(
warnings.warn(
f'The data type of fed Variable {name!r} must be {var_dtype_format!r}, but received {feed_dtype_format!r}'
)
return True
Expand Down
2 changes: 1 addition & 1 deletion python/paddle/nn/layer/layers.py
Original file line number Diff line number Diff line change
Expand Up @@ -1543,7 +1543,7 @@ def add_parameter(self, name, parameter):
elif hasattr(self, name) and name not in self._parameters:
raise KeyError(f"The parameter '{name}' already exists.")
elif parameter is not None and not isinstance(
parameter, framework.Parameter
parameter, (framework.Parameter, paddle.pir.Value)
):
raise TypeError(
f"The parameter to be added should be a Parameter, but received {type(parameter).__name__}."
Expand Down
64 changes: 29 additions & 35 deletions python/paddle/static/pir_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,15 +67,11 @@ def get_pir_parameters(program):
"""
params = []
opts = []
for op in program.global_block().ops:
if op.name() == "builtin.parameter" and "persistable" in op.attrs():
if op.attrs()['persistable'] == [True]:
name = op.attrs()["parameter_name"]
params.append(name)
elif op.name() == "pd_op.data" and "persistable" in op.attrs():
if op.attrs()['persistable'] == [True]:
name = op.attrs()["name"]
opts.append(name)
for var in program.list_vars():
if var.is_parameter and var.persistable:
params.append(var)
elif var.persistable and var.get_defining_op().name() == "pd_op.data":
opts.append(var)
return params, opts


Expand Down Expand Up @@ -308,7 +304,7 @@ def save_vars_pir(
return save_vars_pir(
main_program=main_program,
dirname=dirname,
vars=list(filter(predicate, vars_list)),
vars=vars_list, # list(filter(predicate, vars_list)),
filename=filename,
)
else:
Expand All @@ -321,18 +317,16 @@ def save_vars_pir(
return None

save_var_map = {}
for var_name in vars:
var = global_scope().find_var(var_name)
for v in vars:
var = global_scope().find_var(v.name)
# TODO(chenzhiyang): deal with RAW type and sparse
if filename is None and save_to_memory is False:
save_file_path = os.path.join(
os.path.normpath(dirname), var_name
)
save_file_path = os.path.join(os.path.normpath(dirname), v.name)
core.save_func(
var.get_tensor(), var_name, save_file_path, True, False
var.get_tensor(), v.name, save_file_path, True, False
)
else:
save_var_map[var_name] = var.get_tensor()
save_var_map[v.name] = var.get_tensor()

if filename is not None or save_to_memory:
save_var_list = []
Expand Down Expand Up @@ -416,7 +410,7 @@ def load_vars_pir(
load_vars_pir(
dirname=dirname,
main_program=main_program,
vars=list(filter(predicate, vars_list)),
vars=vars_list, # list(filter(predicate, vars_list)),
filename=filename,
)
else:
Expand All @@ -426,18 +420,18 @@ def load_vars_pir(
# TODO(chenzhiyang):save origin param shape, check vars
load_var_map = {}

for var_name in vars:
var = global_scope().find_var(var_name)
for v in vars:
var = global_scope().find_var(v.name)
assert isinstance(var, paddle.base.libpaddle.Variable)
if filename is None:
if dirname is None:
raise ValueError(
"The directory path and params cannot be None at the same time."
)
file_path = os.path.join(dirname, var_name)
file_path = os.path.join(dirname, v.name)
core.load_func(file_path, -1, [], False, var.get_tensor())
else:
load_var_map[var_name] = var
load_var_map[v.name] = var

if filename is not None:
load_var_list = []
Expand Down Expand Up @@ -500,14 +494,14 @@ def save_pir(program, model_path, protocol=4, **configs):
if dir_name and not os.path.exists(dir_name):
os.makedirs(dir_name)

def get_tensor(name):
t = global_scope().find_var(name).get_tensor()
def get_tensor(var):
t = global_scope().find_var(var.name).get_tensor()
return np.array(t)

# get parameters and optimizer variables
parameter_list, optimizer_param_list = get_pir_parameters(program)
param_dict = {name: get_tensor(name) for name in parameter_list}
opt_dict = {name: get_tensor(name) for name in optimizer_param_list}
param_dict = {var.name: get_tensor(var) for var in parameter_list}
opt_dict = {var.name: get_tensor(var) for var in optimizer_param_list}

# save parameters
param_dict = _unpack_saved_dict(param_dict, protocol)
Expand Down Expand Up @@ -581,11 +575,11 @@ def load_pir(program, model_path, executor=None, var_list=None):
else:
load_dict = _safe_load_pickle(f, encoding='latin1')
load_dict = _pack_loaded_dict(load_dict)
for name in parameter_list:
for var in parameter_list:
assert (
name in load_dict
), f"Can not find [{name}] in model file [{parameter_file_name}]"
set_var(name, load_dict[name])
var.name in load_dict
), f"Can not find [{var.name}] in model file [{parameter_file_name}]"
set_var(var.name, load_dict[var.name])

if len(optimizer_param_list) > 0:
opt_file_name = model_prefix + ".pdopt"
Expand All @@ -594,17 +588,17 @@ def load_pir(program, model_path, executor=None, var_list=None):
), f"Optimizer file [{opt_file_name}] not exits"

if executor:
paddle.base.core._create_loaded_parameter(
paddle.base.libpaddle.pir.create_loaded_parameter(
optimizer_param_list, global_scope(), executor._default_executor
)

with open(opt_file_name, 'rb') as f:
load_dict = _safe_load_pickle(f, encoding='latin1')
for name in optimizer_param_list:
for var in optimizer_param_list:
assert (
name in load_dict
), f"Can not find [{name}] in model file [{opt_file_name}]"
set_var(name, load_dict[name])
var.name in load_dict
), f"Can not find [{var.name}] in model file [{opt_file_name}]"
set_var(var.name, load_dict[var.name])


@static_only
Expand Down
8 changes: 4 additions & 4 deletions test/deprecated/legacy_test/test_fill_constant_op.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,12 @@
# See the License for the specific language governing permissions and
# limitations under the License.

import sys
import unittest

import numpy as np

sys.path.append("../../legacy_test")
from op import Operator
from op_test import OpTest, convert_float_to_uint16, paddle_static_guard

Expand Down Expand Up @@ -512,10 +515,7 @@ def test_shape_type():
fetch_list=[out],
)

with paddle.pir_utils.IrGuard():
pir_program = paddle.static.Program()
with paddle.static.program_guard(pir_program):
self.assertRaises(ValueError, test_shape_type)
# TODO(chenzhiyang): pir test_shape_dtype

changeyoung98 marked this conversation as resolved.
Show resolved Hide resolved

class TestFillConstantOp_ValueTensorBf16(OpTest):
Expand Down
Loading