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
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
@@ -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:
6 changes: 6 additions & 0 deletions paddle/api/Parameter.cpp
Original file line number Diff line number Diff line change
@@ -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
@@ -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
71 changes: 44 additions & 27 deletions python/paddle/v2/parameters.py
Original file line number Diff line number Diff line change
@@ -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

@@ -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):
"""
@@ -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")
@@ -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
"""

@@ -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):
"""
@@ -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)

@@ -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):
"""
@@ -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
@@ -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
@@ -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')
@@ -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):
@@ -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
@@ -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)
@@ -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):
@@ -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))
@@ -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)
@@ -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())