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

V2 API save and load param header #3619

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all 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
3 changes: 3 additions & 0 deletions paddle/api/PaddleAPI.h
Original file line number Diff line number Diff line change
Expand Up @@ -578,6 +578,9 @@ class Parameter {

bool load(const std::string& filename) const;

int getHeaderFormat();
void setHeaderFormat(int32_t fmt);

size_t getSize() const;

private:
Expand Down
6 changes: 6 additions & 0 deletions paddle/api/Parameter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,10 @@ bool Parameter::load(const std::string& filename) const {
return m->getPtr()->load(filename);
}

int Parameter::getHeaderFormat() { return m->getPtr()->getHeaderFormat(); }

void Parameter::setHeaderFormat(int32_t fmt) {
return m->getPtr()->setHeaderFormat(fmt);
}

size_t Parameter::getSize() const { return m->getPtr()->getSize(); }
2 changes: 1 addition & 1 deletion python/paddle/v2/inference.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def __init__(self, output_layer, parameters):
val = param.getBuf(api.PARAMETER_VALUE)
name = param.getName()
assert isinstance(val, api.Vector)
val.copyFromNumpyArray(parameters.get(name).flatten())
val.copyFromNumpyArray(parameters.get(name)[1].flatten())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里为什么需要改动inference的code?那么Training的时候也需要改吗?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

因为原来:value = parameter.get(name)
现在:header, value = parameter.get(name)
Training的时候,改在parameters.py里面了

# the setValueUpdated function is called in randomize, zeroMem,
# load function in paddle/parameter/Parameter.cpp. But in the
# inference mode, the setValueUpdated is never called, it will
Expand Down
71 changes: 44 additions & 27 deletions python/paddle/v2/parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ def create(layers):
class Parameters(object):
"""
Parameters is a dictionary contains Paddle's parameter. The key of
Parameters is the name of parameter. The value of Parameters is a plain
:code:`numpy.ndarry` .
Parameters is the name of parameter. The value of Parameters is a int type
and plain :code:`numpy.ndarry` .

Basically usage is

Expand All @@ -57,14 +57,16 @@ class Parameters(object):
parameters = paddle.parameters.create(out)

parameter_names = parameters.names()
fc_mat = parameters.get('fc')
print fc_mat
fc_header, fc_mat = parameters.get('fc')
print fc_header, fc_mat
"""

def __init__(self):
self.__param_conf__ = dict()
self.__gradient_machines__ = []
self.__tmp_params__ = dict()
# The key is the name of parameter, the value is header format.
self.__tmp_params_header__ = dict()

def __append_config__(self, param_conf):
"""
Expand Down Expand Up @@ -134,19 +136,21 @@ def __getter_inner(self, key, param_type):
if len(self.__gradient_machines__) == 0:
# create new parameter in python numpy.
if key in self.__tmp_params__:
return self.__tmp_params__[key]
return self.__tmp_params_header__[key], self.__tmp_params__[key]
else:
return np.ndarray(shape=shape, dtype=np.float32)
header_format = 0 # default
return header_format, np.ndarray(shape=shape, dtype=np.float32)
else:
for each_gradient_machine in self.__gradient_machines__:
param = __get_parameter_in_gradient_machine__(
each_gradient_machine, key)
# for simplify implementation now, we always copy from C++
assert isinstance(param, api.Parameter)
header_format = param.getHeaderFormat()
val = param.getBuf(param_type)
assert isinstance(val, api.Vector)
val = val.copyToNumpyArray()
return val
return header_format, val
# else continue

raise RuntimeError("Unexpected branch")
Expand Down Expand Up @@ -181,15 +185,17 @@ def get_shape(self, key):
dims = conf.dims if conf.dims else (1, conf.size)
return tuple(map(int, dims))

def __setitem__(self, key, value):
def __setitem__(self, key, value, header_format=0):
"""
Set parameter by parameter name & value. It use Python dict syntax.
Set parameter by parameter name & header format & value. It use Python dict syntax.

:note: It will always copy the parameter to C++ side.
:param key: Parameter name
:type key: basestring
:param value: Parameter matrix.
:type value: np.ndarray
:param header_format: Parameter header format, default is 0.
:type header_format: int
:return: Nothing
"""

Expand All @@ -203,10 +209,11 @@ def __setitem__(self, key, value):

if len(self.__gradient_machines__) == 0:
self.__tmp_params__[key] = value
self.__tmp_params_header__[key] = header_format
else:
for each_gradient_machine in self.__gradient_machines__:
__copy_parameter_to_gradient_machine__(each_gradient_machine,
key, value)
__copy_parameter_to_gradient_machine__(
each_gradient_machine, key, value, header_format)

def get(self, parameter_name):
"""
Expand All @@ -215,8 +222,8 @@ def get(self, parameter_name):
:note: It will always copy the parameter from C++ side.
:param parameter_name: parameter name
:type parameter_name: basestring
:return: The parameter matrix.
:rtype: np.ndarray
:return: The parameter header format & matrix.
:rtype: [int, np.ndarray]
"""
return self.__getitem__(key=parameter_name)

Expand All @@ -227,23 +234,26 @@ def get_grad(self, key):
:note: It will always copy the parameter from C++ side.
:param key: parameter name
:type key: basestring
:return: The grandient matrix.
:rtype: np.ndarray
:return: The header format & gradient matrix.
:rtype: [int, np.ndarray]
"""
import py_paddle.swig_paddle as api
return self.__getter_inner(key, api.PARAMETER_GRADIENT)

def set(self, parameter_name, value):
def set(self, parameter_name, value, header_format=0):
"""
Set parameter by parameter name & matrix.

:param parameter_name: parameter name
:type parameter_name: basestring
:param value: parameter matrix
:type value: np.ndarray
:param header_format: parameter header format, default is 0
:type header_format: int
:return: Nothing.
"""
self.__setitem__(key=parameter_name, value=value)
self.__setitem__(
key=parameter_name, value=value, header_format=header_format)

def append_gradient_machine(self, gradient_machine):
"""
Expand All @@ -261,8 +271,9 @@ def append_gradient_machine(self, gradient_machine):
if len(self.__tmp_params__) != 0:
for name, val in self.__tmp_params__.iteritems():
try:
__copy_parameter_to_gradient_machine__(gradient_machine,
name, val)
header_format = self.__tmp_params_header__[name]
__copy_parameter_to_gradient_machine__(
gradient_machine, name, val, header_format)
except ValueError:
# If no such parameter in gradient machine, then don't copy
pass
Expand All @@ -277,9 +288,9 @@ def serialize(self, name, f):
:type f: file
:return:
"""
param = self.get(name)
header_format, param = self.get(name)
size = reduce(lambda a, b: a * b, param.shape)
f.write(struct.pack("IIQ", 0, 4, size))
f.write(struct.pack("IIQ", header_format, 4, size))
param = param.astype(np.float32)
s = param.tostring()
wrote_size = 0
Expand All @@ -297,9 +308,10 @@ def deserialize(self, name, f):
:type f: file
:return:
"""
f.read(16) # header
header_format = f.read(4)
f.read(12)
arr = np.frombuffer(f.read(), dtype=np.float32)
self.set(name, arr.reshape(self.get_shape(name)))
self.set(name, arr.reshape(self.get_shape(name)), header_format)

def to_tar(self, f):
tar = tarfile.TarFile(fileobj=f, mode='w')
Expand Down Expand Up @@ -361,7 +373,8 @@ def init_from_tar(self, f):
tar_param = Parameters.from_tar(f)
for pname in tar_param.names():
if pname in self.names():
self.set(pname, tar_param.get(pname))
header_format, param_value = tar_param.get(pname)
self.set(pname, param_value, header_format)


def __get_parameter_in_gradient_machine__(gradient_machine, name):
Expand All @@ -384,20 +397,24 @@ def __get_parameter_in_gradient_machine__(gradient_machine, name):
return params[0]


def __copy_parameter_to_gradient_machine__(gradient_machine, name, arr):
def __copy_parameter_to_gradient_machine__(gradient_machine, name, format, arr):
"""
Copy a python ndarray into the gradient machine.

:param gradient_machine:
:type gradient_machine: api.GradientMachine
:param name:
:param arr:
:param name: parameter name
:type name: basestring
:param format: parameter header format
:type name: int
:param arr: parameter matrix
:type arr: np.ndarray
:return:
:rtype: api.Parameter
"""
import py_paddle.swig_paddle as api
param = __get_parameter_in_gradient_machine__(gradient_machine, name)
param.setHeaderFormat(format)
vec = param.getBuf(api.PARAMETER_VALUE)
assert isinstance(vec, api.Vector)
vec.copyFromNumpyArray(arr.flatten())
18 changes: 9 additions & 9 deletions python/paddle/v2/tests/test_parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def test_serialization(self):
params.__append_config__(__rand_param_config__("param_1"))

for name in params.names():
param = params.get(name)
param = params.get(name)[1]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里只测试了[1],有没有办法也测试[0]的值?代表的是header的值对吧

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[0]代表header的值。这里header默认都是0,所以都没做测试。可以等以后mkldnn的header进来,再另加一个单测。

param[:] = numpy.random.uniform(
-1.0, 1.0, size=params.get_shape(name))
params.set(name, param)
Expand All @@ -57,8 +57,8 @@ def test_serialization(self):

for name in params.names():
self.assertEqual(params.get_shape(name), params_dup.get_shape(name))
p0 = params.get(name)
p1 = params_dup.get(name)
p0 = params.get(name)[1]
p1 = params_dup.get(name)[1]
self.assertTrue(numpy.isclose(p0, p1).all())

def test_initializer(self):
Expand All @@ -75,7 +75,7 @@ def initializer(name):
param_attr=ParamAttr(
name="fc.w", initializer=initializer))
params = parameters.create(y)
val = params["fc.w"]
header, val = params["fc.w"]
assert val.shape == (3, 2)
expected = numpy.array([[1, 1], [1, 2], [1, 1]], numpy.float32)
assert numpy.logical_and.reduce(numpy.reshape(val == expected, 6))
Expand All @@ -86,7 +86,7 @@ def get_param(names, size):
for k, v in zip(names, size):
p.__append_config__(__rand_param_config__(k, v))
for name in p.names():
param = p.get(name)
param = p.get(name)[1]
param[:] = numpy.random.uniform(
-1.0, 1.0, size=p.get_shape(name))
p.set(name, param)
Expand All @@ -112,16 +112,16 @@ def get_parames():
p2.init_from_tar(file1)
for name in p1.names():
self.assertEqual(p1.get_shape(name), p2.get_shape(name))
v1 = p1.get(name)
v2 = p2.get(name)
v1 = p1.get(name)[1]
v2 = p2.get(name)[1]
self.assertTrue(numpy.isclose(v1, v2).all())

p1, file1, p2, file2 = get_parames()
p1.init_from_tar(file2)
for name in p1.names():
self.assertEqual(p1.get_shape(name), p2.get_shape(name))
v1 = p1.get(name)
v2 = p2.get(name)
v1 = p1.get(name)[1]
v2 = p2.get(name)[1]
self.assertTrue(numpy.isclose(v1, v2).all())


Expand Down