From 50602e6afc70c00bba16bbbca7a2142da6b1e4a7 Mon Sep 17 00:00:00 2001 From: Helin Wang Date: Tue, 9 May 2017 17:37:46 -0700 Subject: [PATCH 01/15] Trainer Communication Library design doc, the Go interface part --- doc/design/cluster_train/trainer.md | 76 +++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 doc/design/cluster_train/trainer.md diff --git a/doc/design/cluster_train/trainer.md b/doc/design/cluster_train/trainer.md new file mode 100644 index 0000000000000..638cfb179b802 --- /dev/null +++ b/doc/design/cluster_train/trainer.md @@ -0,0 +1,76 @@ +# Design Doc: Trainer Communication Library + +For an overview of trainer's role, please refer to [distributed training design doc](README.md). In this design doc, we will discuss the trainer's communication library, which will manage communication with parameter servers and the [master server](master_server.md). The library will be implemented in [Go](https://golang.org/) and made available as a static or dynamic library with a C header file. + +## Go Interface + +The Go interface is the basic abstraction of communications with the master server and parameter servers. We will add another layer on top (add retry logic, polish interface with C idiom) before exposing the library with a [C interface](#c-interface). + +```go +// MasterClient is the client to the master server. +type MasterClient struct {} + +// GetTask gets a new task by telling the master server the finished task. +// Use nil as the finished task when getting the task for the first time. +func (*MasterClient) GetTask(finished master.Task) (master.Task, error) + +// ElementType is the type of elements of a Parameter. +type ElementType int + +// Different element types. +const ( + Int32 ElementType = iota + UInt32 + Int64 + UInt64 + Float32 + Float64 +) + +// Parameter is a piece data to sync with the parameter server. +type Parameter struct { + Name string + ElementType ElementType + Buffer []byte +} + +// Gradient is the gradient of the parameter. +type Gradient Parameter + +// PServerClient is the client to parameter servers. +type PServerClient struct {} + +// UpdateRule specifies the rule for updating parameters with gradients. +type UpdateRule struct { + UpdateMethod pserver.UpdateMethod + LearningRate float32 +} + +// ParamInitChans returns a send channel for parameter initialization. +// +// ParamInitChans will be called from multiple trainers, only one trainer should +// initialize the parameters on parameter servers, other trainers will instead +// get the initialized parameters from parameter servers using GetParam. +// +// If send channel is not nil, the trainer is selected to do the initialization, +// the trainer needs to signal for finishing initializing the parameters by +// closing the send channel. +func (*PServerClient) ParamInitChan() (send chan<- Parameter, err error) + +// SendGrad sends gradients to parameter servers. +func (*PServerClient) SendGrad(method UpdateMethod, grads []Gradient) error + +// GetParam gets parameters from parameter servers. +func (*PServerClient) GetParam(names []string) ([]Parameter, error) + +// Save indicates parameters to save the parameter to the given path. +// +// Path needs to be the path to a distributed file system which is visible +// to all parameter servers. +func (*PServerClient) Save(path string) error +``` +Please see [master server design doc](master_server.md) for the definition of `master.Task`. + +## C Interface + +TODO From 0ed68da9b36bbe388d678b9a56fbcca3090381b4 Mon Sep 17 00:00:00 2001 From: Helin Wang Date: Tue, 9 May 2017 17:56:09 -0700 Subject: [PATCH 02/15] update grammar --- doc/design/cluster_train/trainer.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/design/cluster_train/trainer.md b/doc/design/cluster_train/trainer.md index 638cfb179b802..c9c090dc98486 100644 --- a/doc/design/cluster_train/trainer.md +++ b/doc/design/cluster_train/trainer.md @@ -27,7 +27,7 @@ const ( Float64 ) -// Parameter is a piece data to sync with the parameter server. +// Parameter is a piece of data to sync with the parameter server. type Parameter struct { Name string ElementType ElementType From cd978b70f4162e454a1c1bbfa67a52250469a4d4 Mon Sep 17 00:00:00 2001 From: Helin Wang Date: Tue, 9 May 2017 18:02:51 -0700 Subject: [PATCH 03/15] add SetParam --- doc/design/cluster_train/trainer.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/doc/design/cluster_train/trainer.md b/doc/design/cluster_train/trainer.md index c9c090dc98486..bcb4a9c09dccc 100644 --- a/doc/design/cluster_train/trainer.md +++ b/doc/design/cluster_train/trainer.md @@ -57,9 +57,15 @@ type UpdateRule struct { // closing the send channel. func (*PServerClient) ParamInitChan() (send chan<- Parameter, err error) -// SendGrad sends gradients to parameter servers. +// SendGrad sends gradients to parameter servers for updating parameters. func (*PServerClient) SendGrad(method UpdateMethod, grads []Gradient) error +// SetParam sets parameters. +// +// SetParam can be used for the parameters that are not suitable for updating +// using gradients. +func (*PServerClient) SetParam(params []Paramter) error + // GetParam gets parameters from parameter servers. func (*PServerClient) GetParam(names []string) ([]Parameter, error) From 66a3cfe36f10c95dbccb1b3e2b08aafb02247437 Mon Sep 17 00:00:00 2001 From: Helin Wang Date: Wed, 10 May 2017 16:41:37 -0700 Subject: [PATCH 04/15] change to C API --- doc/design/cluster_train/pserver_client.md | 95 ++++++++++++++++++++++ doc/design/cluster_train/trainer.md | 82 ------------------- 2 files changed, 95 insertions(+), 82 deletions(-) create mode 100644 doc/design/cluster_train/pserver_client.md delete mode 100644 doc/design/cluster_train/trainer.md diff --git a/doc/design/cluster_train/pserver_client.md b/doc/design/cluster_train/pserver_client.md new file mode 100644 index 0000000000000..62edd349f3f98 --- /dev/null +++ b/doc/design/cluster_train/pserver_client.md @@ -0,0 +1,95 @@ +# Design Doc: The Client Library of Parameter Server + +For an overview of trainer's role, please refer to [distributed training design doc](README.md). In this design doc, we will discuss the parameter server's client library, which will manage communication with parameter servers. The library will be implemented in [Go](https://golang.org/) and made available as a static or dynamic library with a C header file. + +## C Interface + +```c +#define PADDLE_ELEMENT_TYPE_INT32 0 +#define PADDLE_ELEMENT_TYPE_UINT32 1 +#define PADDLE_ELEMENT_TYPE_INT64 2 +#define PADDLE_ELEMENT_TYPE_UINT64 3 +#define PADDLE_ELEMENT_TYPE_FLOAT32 4 +#define PADDLE_ELEMENT_TYPE_FLOAT64 5 + +typedef struct paddle_pserver_client paddle_pserver_client; + +/** + * @brief paddle_new_pserver_client creates a new parameter server + * client. + */ +paddle_pserver_client* paddle_new_pserver_client(); + +/** + * @brief paddle_pserver_client_release releases the parameter server + * client. + */ +void paddle_pserver_client_release(paddle_pserver_client* client); + +/** + * @brief paddle_begin_init_param begins to initialize parameters + * on parameter servers. + * + * paddle_begin_init_param will be called from multiple trainers, only + * one trainer will be selected to initialize the parameters on + * parameter servers. Other trainers will be blocked until the + * initialization is done, and they need to get the initialized + * parameters from parameter servers using @paddle_get_param. + * + * @return 1 if trainer is selected to initialize parameter + * servers, otherwise 0. + */ +int paddle_begin_init_param(paddle_pserver_client* client); + +/** + * @brief paddle_init_param initializes the parameter on parameter + * servers. + * + * @return 0 if successful, otherwise -1. On failure the trainer need + * to restart the entire initialization process starting from + * paddle_begin_init_param. Or simply exit the program and wait for + * cluster management system to restart trainer. + */ +int paddle_init_param(paddle_pserver_client* client, const char* name, int element_type, const void* content); + +/** + * @brief paddle_finish_init_param tells parameter servers client has + * sent all parameters to parameter servers as initialization. + * + * @return 0 if successful, otherwise -1. On failure the trainer need + * to restart the entire initialization process starting from + * paddle_begin_init_param. Or simply exit the program and wait for + * cluster management system to restart trainer. + */ +int paddle_finish_init_param(paddle_pserver_client* client); + +/** + * @brief paddle_send_grad sends gradients to parameter servers for + * updating parameters. + * + * @return 0 if successful, otherwise -1. + */ +int paddle_send_grad(paddle_pserver_client* client, const char* name, int element_type, const void* content); + +/** + * @brief paddle_set_param sets a parameter on parameter servers. + * + * @return 0 if successful, otherwise -1. + */ +int paddle_set_param(paddle_pserver_client* client, const char* name, int element_type, const void* content); + +/** + * @brief paddle_get_param gets the parameter from parameter servers. + * + * @return 0 if successful, otherwise -1. + */ +int paddle_get_param(paddle_pserver_client* client, const char* name, void** dst, int* dstLen); + +/** + * @brief paddle_save_model indicates parameters to save the parameter + * to the given path + * + * @return 0 if successful, otherwise -1. + */ +int paddle_save_model(paddle_pserver_client* client, const char* path); +``` diff --git a/doc/design/cluster_train/trainer.md b/doc/design/cluster_train/trainer.md deleted file mode 100644 index bcb4a9c09dccc..0000000000000 --- a/doc/design/cluster_train/trainer.md +++ /dev/null @@ -1,82 +0,0 @@ -# Design Doc: Trainer Communication Library - -For an overview of trainer's role, please refer to [distributed training design doc](README.md). In this design doc, we will discuss the trainer's communication library, which will manage communication with parameter servers and the [master server](master_server.md). The library will be implemented in [Go](https://golang.org/) and made available as a static or dynamic library with a C header file. - -## Go Interface - -The Go interface is the basic abstraction of communications with the master server and parameter servers. We will add another layer on top (add retry logic, polish interface with C idiom) before exposing the library with a [C interface](#c-interface). - -```go -// MasterClient is the client to the master server. -type MasterClient struct {} - -// GetTask gets a new task by telling the master server the finished task. -// Use nil as the finished task when getting the task for the first time. -func (*MasterClient) GetTask(finished master.Task) (master.Task, error) - -// ElementType is the type of elements of a Parameter. -type ElementType int - -// Different element types. -const ( - Int32 ElementType = iota - UInt32 - Int64 - UInt64 - Float32 - Float64 -) - -// Parameter is a piece of data to sync with the parameter server. -type Parameter struct { - Name string - ElementType ElementType - Buffer []byte -} - -// Gradient is the gradient of the parameter. -type Gradient Parameter - -// PServerClient is the client to parameter servers. -type PServerClient struct {} - -// UpdateRule specifies the rule for updating parameters with gradients. -type UpdateRule struct { - UpdateMethod pserver.UpdateMethod - LearningRate float32 -} - -// ParamInitChans returns a send channel for parameter initialization. -// -// ParamInitChans will be called from multiple trainers, only one trainer should -// initialize the parameters on parameter servers, other trainers will instead -// get the initialized parameters from parameter servers using GetParam. -// -// If send channel is not nil, the trainer is selected to do the initialization, -// the trainer needs to signal for finishing initializing the parameters by -// closing the send channel. -func (*PServerClient) ParamInitChan() (send chan<- Parameter, err error) - -// SendGrad sends gradients to parameter servers for updating parameters. -func (*PServerClient) SendGrad(method UpdateMethod, grads []Gradient) error - -// SetParam sets parameters. -// -// SetParam can be used for the parameters that are not suitable for updating -// using gradients. -func (*PServerClient) SetParam(params []Paramter) error - -// GetParam gets parameters from parameter servers. -func (*PServerClient) GetParam(names []string) ([]Parameter, error) - -// Save indicates parameters to save the parameter to the given path. -// -// Path needs to be the path to a distributed file system which is visible -// to all parameter servers. -func (*PServerClient) Save(path string) error -``` -Please see [master server design doc](master_server.md) for the definition of `master.Task`. - -## C Interface - -TODO From 0064fcb082930bbaf3d2f144697af677db0b0d6a Mon Sep 17 00:00:00 2001 From: Helin Wang Date: Wed, 10 May 2017 17:19:54 -0700 Subject: [PATCH 05/15] update C API --- doc/design/cluster_train/pserver_client.md | 49 +++++++++++++++------- 1 file changed, 34 insertions(+), 15 deletions(-) diff --git a/doc/design/cluster_train/pserver_client.md b/doc/design/cluster_train/pserver_client.md index 62edd349f3f98..7ecd4cff06587 100644 --- a/doc/design/cluster_train/pserver_client.md +++ b/doc/design/cluster_train/pserver_client.md @@ -12,6 +12,13 @@ For an overview of trainer's role, please refer to [distributed training design #define PADDLE_ELEMENT_TYPE_FLOAT32 4 #define PADDLE_ELEMENT_TYPE_FLOAT64 5 +typedef struct { + char* name; + int element_type; + void* content; + int content_len; +} paddle_parameter, paddle_gradient; + typedef struct paddle_pserver_client paddle_pserver_client; /** @@ -27,33 +34,36 @@ paddle_pserver_client* paddle_new_pserver_client(); void paddle_pserver_client_release(paddle_pserver_client* client); /** - * @brief paddle_begin_init_param begins to initialize parameters + * @brief paddle_begin_init_params begins to initialize parameters * on parameter servers. * - * paddle_begin_init_param will be called from multiple trainers, only + * paddle_begin_init_params will be called from multiple trainers, only * one trainer will be selected to initialize the parameters on * parameter servers. Other trainers will be blocked until the * initialization is done, and they need to get the initialized - * parameters from parameter servers using @paddle_get_param. + * parameters from parameter servers using @paddle_get_params. * - * @return 1 if trainer is selected to initialize parameter - * servers, otherwise 0. + * @param config_proto serialized parameter server configuration + * protobuffer. + * @return 1 if trainer is selected to initialize parameter servers, + * otherwise 0. */ -int paddle_begin_init_param(paddle_pserver_client* client); +int paddle_begin_init_params(paddle_pserver_client* client, const char* config_proto); /** * @brief paddle_init_param initializes the parameter on parameter * servers. * + * @param param the parameter to initialize. * @return 0 if successful, otherwise -1. On failure the trainer need * to restart the entire initialization process starting from * paddle_begin_init_param. Or simply exit the program and wait for * cluster management system to restart trainer. */ -int paddle_init_param(paddle_pserver_client* client, const char* name, int element_type, const void* content); +int paddle_init_param(paddle_pserver_client* client, paddle_parameter params); /** - * @brief paddle_finish_init_param tells parameter servers client has + * @brief paddle_finish_init_params tells parameter servers client has * sent all parameters to parameter servers as initialization. * * @return 0 if successful, otherwise -1. On failure the trainer need @@ -61,34 +71,43 @@ int paddle_init_param(paddle_pserver_client* client, const char* name, int eleme * paddle_begin_init_param. Or simply exit the program and wait for * cluster management system to restart trainer. */ -int paddle_finish_init_param(paddle_pserver_client* client); +int paddle_finish_init_params(paddle_pserver_client* client); /** - * @brief paddle_send_grad sends gradients to parameter servers for + * @brief paddle_send_grads sends gradients to parameter servers for * updating parameters. * + * @param grads the array of gradients to send. + * @param total the total number of gradient inside the gradient array. + * @param learning_rate the learning rate for the gradients. * @return 0 if successful, otherwise -1. */ -int paddle_send_grad(paddle_pserver_client* client, const char* name, int element_type, const void* content); +int paddle_send_grads(paddle_pserver_client* client, const paddle_gradient* grads, int total, double learning_rate); /** - * @brief paddle_set_param sets a parameter on parameter servers. + * @brief paddle_set_params sets parameters to parameter servers. * + * @param params the array of parameters to set to parameter servers. + * @param total number of parameters inside the parameter array. * @return 0 if successful, otherwise -1. */ -int paddle_set_param(paddle_pserver_client* client, const char* name, int element_type, const void* content); +int paddle_set_params(paddle_pserver_client* client, const paddle_parameter* params, int total); /** - * @brief paddle_get_param gets the parameter from parameter servers. + * @brief paddle_get_params gets parameters from parameter servers. * + * @param names the array of names of the parameters to get. + * @param dst the destination array of parameters to save to. + * @param total the total number of parameters to get. * @return 0 if successful, otherwise -1. */ -int paddle_get_param(paddle_pserver_client* client, const char* name, void** dst, int* dstLen); +int paddle_get_params(paddle_pserver_client* client, const char** names, paddle_parameter* dst, int total); /** * @brief paddle_save_model indicates parameters to save the parameter * to the given path * + * @param path the path to save parameters. * @return 0 if successful, otherwise -1. */ int paddle_save_model(paddle_pserver_client* client, const char* path); From 1525216078c6fbaa2f3cf201f52943ed11f81c63 Mon Sep 17 00:00:00 2001 From: Helin Wang Date: Wed, 10 May 2017 17:30:27 -0700 Subject: [PATCH 06/15] update documentation --- doc/design/cluster_train/pserver_client.md | 41 +++++++++------------- 1 file changed, 16 insertions(+), 25 deletions(-) diff --git a/doc/design/cluster_train/pserver_client.md b/doc/design/cluster_train/pserver_client.md index 7ecd4cff06587..8c4ed46199f44 100644 --- a/doc/design/cluster_train/pserver_client.md +++ b/doc/design/cluster_train/pserver_client.md @@ -21,32 +21,23 @@ typedef struct { typedef struct paddle_pserver_client paddle_pserver_client; -/** - * @brief paddle_new_pserver_client creates a new parameter server - * client. - */ paddle_pserver_client* paddle_new_pserver_client(); - -/** - * @brief paddle_pserver_client_release releases the parameter server - * client. - */ void paddle_pserver_client_release(paddle_pserver_client* client); /** - * @brief paddle_begin_init_params begins to initialize parameters - * on parameter servers. + * @brief paddle_begin_init_params begins to initialize parameters on + * parameter servers. * - * paddle_begin_init_params will be called from multiple trainers, only - * one trainer will be selected to initialize the parameters on + * paddle_begin_init_params will be called from multiple trainers, + * only one trainer will be selected to initialize the parameters on * parameter servers. Other trainers will be blocked until the * initialization is done, and they need to get the initialized * parameters from parameter servers using @paddle_get_params. * - * @param config_proto serialized parameter server configuration - * protobuffer. - * @return 1 if trainer is selected to initialize parameter servers, - * otherwise 0. + * @param config_proto serialized parameter server configuration in + * Protocol Buffers format. + * @return 1 if the trainer is selected to initialize parameter + * servers, otherwise 0. */ int paddle_begin_init_params(paddle_pserver_client* client, const char* config_proto); @@ -55,10 +46,10 @@ int paddle_begin_init_params(paddle_pserver_client* client, const char* config_p * servers. * * @param param the parameter to initialize. - * @return 0 if successful, otherwise -1. On failure the trainer need - * to restart the entire initialization process starting from - * paddle_begin_init_param. Or simply exit the program and wait for - * cluster management system to restart trainer. + * @return 0 if successful, otherwise -1. On failure, the trainer need + * to restart the entire initialization process (starting + * from @paddle_begin_init_param). Or simply exit the program and wait + * for cluster management system to restart trainer. */ int paddle_init_param(paddle_pserver_client* client, paddle_parameter params); @@ -66,10 +57,10 @@ int paddle_init_param(paddle_pserver_client* client, paddle_parameter params); * @brief paddle_finish_init_params tells parameter servers client has * sent all parameters to parameter servers as initialization. * - * @return 0 if successful, otherwise -1. On failure the trainer need - * to restart the entire initialization process starting from - * paddle_begin_init_param. Or simply exit the program and wait for - * cluster management system to restart trainer. + * @return 0 if successful, otherwise -1. On failure, the trainer need + * to restart the entire initialization process (starting + * from @paddle_begin_init_param). Or simply exit the program and wait + * for cluster management system to restart trainer. */ int paddle_finish_init_params(paddle_pserver_client* client); From 31654e267f1b2645de249b96ca5e8099aa030a32 Mon Sep 17 00:00:00 2001 From: Helin Wang Date: Wed, 10 May 2017 17:42:22 -0700 Subject: [PATCH 07/15] fix grammar --- doc/design/cluster_train/pserver_client.md | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/doc/design/cluster_train/pserver_client.md b/doc/design/cluster_train/pserver_client.md index 8c4ed46199f44..96cc649b2c2e2 100644 --- a/doc/design/cluster_train/pserver_client.md +++ b/doc/design/cluster_train/pserver_client.md @@ -46,10 +46,10 @@ int paddle_begin_init_params(paddle_pserver_client* client, const char* config_p * servers. * * @param param the parameter to initialize. - * @return 0 if successful, otherwise -1. On failure, the trainer need - * to restart the entire initialization process (starting - * from @paddle_begin_init_param). Or simply exit the program and wait - * for cluster management system to restart trainer. + * @return 0 if successful, otherwise -1. On failure, the trainer + * needs to restart the entire initialization process (starting from + * @paddle_begin_init_param). Or simply exit the program and wait for + * the cluster management system to restart the trainer. */ int paddle_init_param(paddle_pserver_client* client, paddle_parameter params); @@ -57,10 +57,10 @@ int paddle_init_param(paddle_pserver_client* client, paddle_parameter params); * @brief paddle_finish_init_params tells parameter servers client has * sent all parameters to parameter servers as initialization. * - * @return 0 if successful, otherwise -1. On failure, the trainer need - * to restart the entire initialization process (starting - * from @paddle_begin_init_param). Or simply exit the program and wait - * for cluster management system to restart trainer. + * @return 0 if successful, otherwise -1. On failure, the trainer + * needs to restart the entire initialization process (starting from + * @paddle_begin_init_param). Or simply exit the program and wait for + * the cluster management system to restart the trainer. */ int paddle_finish_init_params(paddle_pserver_client* client); @@ -79,7 +79,8 @@ int paddle_send_grads(paddle_pserver_client* client, const paddle_gradient* grad * @brief paddle_set_params sets parameters to parameter servers. * * @param params the array of parameters to set to parameter servers. - * @param total number of parameters inside the parameter array. + * @param total the total number of parameters inside the parameter + * array. * @return 0 if successful, otherwise -1. */ int paddle_set_params(paddle_pserver_client* client, const paddle_parameter* params, int total); From f1b89f93204abc0db45ed155af049374fbe4b75b Mon Sep 17 00:00:00 2001 From: Helin Wang Date: Wed, 10 May 2017 18:33:12 -0700 Subject: [PATCH 08/15] add illustration --- doc/design/cluster_train/pserver_client.md | 14 ++++++++++++++ doc/design/cluster_train/src/init_lock.graffle | Bin 0 -> 3090 bytes doc/design/cluster_train/src/init_lock.png | Bin 0 -> 26774 bytes .../cluster_train/src/pserver_init.graffle | Bin 0 -> 2622 bytes doc/design/cluster_train/src/pserver_init.png | Bin 0 -> 28853 bytes 5 files changed, 14 insertions(+) create mode 100644 doc/design/cluster_train/src/init_lock.graffle create mode 100644 doc/design/cluster_train/src/init_lock.png create mode 100644 doc/design/cluster_train/src/pserver_init.graffle create mode 100644 doc/design/cluster_train/src/pserver_init.png diff --git a/doc/design/cluster_train/pserver_client.md b/doc/design/cluster_train/pserver_client.md index 96cc649b2c2e2..c1cb93434e58a 100644 --- a/doc/design/cluster_train/pserver_client.md +++ b/doc/design/cluster_train/pserver_client.md @@ -2,6 +2,20 @@ For an overview of trainer's role, please refer to [distributed training design doc](README.md). In this design doc, we will discuss the parameter server's client library, which will manage communication with parameter servers. The library will be implemented in [Go](https://golang.org/) and made available as a static or dynamic library with a C header file. +## Parameter Initialization + +The parameters on parameter servers need to be initialized. To provide maximum flexibility, we need to allow trainer initialized the parameters. Only one trainer will do the initialization, the other trainers will wait for the completion of initialization and get the parameters from the parameter servers. + +To select the trainer for initialization, every trainer will try to get a distributed lock, whoever owns the lock will do the initialization. As illustrated below: + + +The select process is encapsulated in the C API function: +```c +int paddle_begin_init_params(paddle_pserver_client* client, const char* config_proto); +``` +The selected trainer's call to `paddle_begin_init_params` will return with 1, and the other trainers' call to `paddle_begin_init_params` will block until initialization is done, and return 0. As illustrated below: + + ## C Interface ```c diff --git a/doc/design/cluster_train/src/init_lock.graffle b/doc/design/cluster_train/src/init_lock.graffle new file mode 100644 index 0000000000000000000000000000000000000000..fa9149f21b1311eed48ef72ec55e556559d0fc94 GIT binary patch literal 3090 zcmV+t4DItDiwFP!000030PS4sbK1HR|J?o+zWI6*kl%Jsnlpv~fhHupLSQm;N7xFu zF*dbLNZQH&epfON8=Dk*)8`>G!4K`OR;%5gwX&?^e;&sU_dxu>_T2Xed=4FO#5Fz3 zc8Bi|I_>h2cJR;1+vES7UKZQEs}eVH>>%W>I)(FUkvllb=Z(q4A^H4i`;@ynueRD8 z$jIkQ^#kr;6o!*``TT4)%VA2DGrch-4DwgLHz9ubbPmFffFWmv)&W!#Nl&N)cP-lt zPu{*cz9&y7h8fxquh2%(s zY3Lc&$7v9z)aB)rz6EUF)B0Tt>Yj{jbICL}#GH&$1dFnzPG7zp@ko;nxg$jvo)5V% zq96;0oPbp2`8m-D%2;%8ke1OJ;R#vQzQ$8MT`??F7vimm$Xel(Lspav-J?Pf>D{kN zW5rSVsgGwXy;hplFXQTfotGwAoUKkGTLz@>dE+hIq=?-I99;VL&|W#n!l>wwLvicH zI9m2+lA@2gfc8L&$#bS-*Kq8in|6k@QjEGa!6t}U()$EEC!%tkXD16%xS&3Mio=vX z0^-#pBp>lShxm6qdWS^rF9d%n?WhpXU*|s~j_qyoZ7*}!A~s{JE>YBe7Q-; zz314;<2q~Ec1Zhal48RUE$g(*7f(HNIwo$o9>q)Zi7ETdeK?>%lM(evr(BDuSV)PQ zm{WM2N2;ibk|gr{A;)VA@nC9!1 zS{c-iH%pT?Nx+xU~YOL^YOUb#H&PWs^^m(oP?xQGlCJe;^vDJWW&-XT#DD64S263 zd%b~cp0cJxvUKI%w|;oX-(okg*^fCH^6<>KH9gbAv`9iybP-Gka4o6m?U&m-&ke(Y zb9;ww?_3*><_P-%2@wZi@jya&;?~KO{QV5HrssG*5dVGa6ASwIcIXr0(vyKRC2$0) z+X?oq+wf6V5d8AIzz&K=4>ZW=K|}N)ii||m8Hpq@5*aZP;HC$KXC#UWBvh{LyEvTs z*tz{=(i@>Y9EHTa!zO?odi$2OA`k`c*1SVp)wM$osPLGC05#7W9yyUwyAZhPh)eca z>@l@1;_#B36BI=jBxt4vjZ-7UL9)gxvZkp>QzY>ZK^*P15J%k|adafBG6Hu4x7M{i z;_MOUk4BsgkU*ntGa!KBtL~k*xt}RTMn5n6WzC&fWWu7bU{{lO&5_aM6(-;_`ByC(re`!{EoSXph#d%0jazQcGeYf zpKI)Mjh~QfVDr<|_6g^B=KUczpa)|8lsOJT)N(qK6iL=(iHAl>^yw(&Bu&>8g-3u# zyr@RFBmeHWBfb{ypdE2X768S09*IcibuBT*+QZHsc76`*1XI%_!Oxjm)uf!N2{5El zbQzi^t8BO<=5$pQbqUcR(WF0RxFft0?C86Wb~I6zWh4Wh>oDY@uetZIvxl8Oezfy1 zn_Gcg7Qhvy+(|o z-)*+(jltM7M%6~O?bNS&&X*&I4 z;bI`v{gKxw;?8}cRQph33?ShGB$RNsj&N6>_HJsjQEQcq?)~Y#*XlY$3)HGV3@Z9l z#4usio&nS8KzMvGhsDmwD1U@@6#L_zVf1ioRKwj;Np3TSPNn?R>sn8}n}XL7oARL2 zt$<``FqO>nR;6qYD%TCDc@#BTtc}dd2pZ)C{YDW83zr?fFfzww7yf6sTt}q)=ukBy zvzF?ojq*j&sCEpho-^rHK)uR9kkMS-MYudpdIK{(`O${@gIo)iZ044I$KxuBb`*)JL%p@cX3ilXUTx5i$Bgu<-L>s$xhn#F=X*RXTGAF z3bOWtomAR8>A%5A2e10T|G_>g?o;eE0WZ5jQSbhFm~$yCLYoK>QFT#D;YlWz6$@JIcc^PC@dxKQs#S+_nMg zuxyzo%8X%|Ez{f^8d?U)?VD@V>(+w)bux2i3@m*HLYkzf^Yac>v(!L!qcf{U3Gk>< ztVGiReP~$!Vc@prIxnsp#HroikBsX9W4QR-|9I2r(WKg>Kl!{|H9+#fCvjOQfSg1dOw7M1)SbAN>mC^A1KDB=B)Ovwm`1$J}rziFWsQ9)OpF~f7 z;7?1}zWiQHiLYkF8RZ53==qeqNNu_4n|quFI+Wwn@HWVxOH&QEqVl(*3ZRuFO&R#Z z!G@RaiB_kKlsRrlZVXVF+7=1U*>ZsO56~0f8l%3p1ACDE%sshwCz=hv!o$@v_Uv5} zqDRRX2#fF(3yHtQb^sp;nY6v+$M(yq@@Oud?u-$9^;35PA`ap=@$8JvxksYFI4=~} z@i?Q;Sp8gvS6#tn{5iB**3XKsZ8FO$-4r@dk2eiB2an;}?sG)CJZ^i#dW!l%WuC$vWIFf~i=Fuvq!p^N2(DP>%`{{nsUc23-3I7(F@ zpy*G~H6(}tbZZqd{v8wyXWqo#iar@XpyK_efJn#B1s=L2+=V`XfM|^f3DF5`p zW#7%Wb=hLe3;CV6Vatn15ZPhRiwp08(0wS*uxmMF>qgbRkZf;)OW%en5}on_jzrs* zY%QF^Qvv$*KG=em2JYrStG5bX7<%Ii?8i1viN6e8h`w_?943KbelDRT%&$-@s^QG@ z?a!Wz9njl$*Bmunk?C0!)H5o2aZGEBU;rlbgKo_+hebAs?C|~3;Ov@>@QN_PYYVkc z{car@)o#gXeFB>Dqcba3URM8{+=c&-c_<3KB>xJs6+iLrJVShX(dc4?r|^&;smZNs zvTC7z&?IzoCX(Ttze7hf=`ikl_C(v8d=K$S=w%&o=@J^m8%6<5f~`x}dHQ;EH@(@m zdl3QYRvQs9*lycl8i=9|lMb5EFd8#@dIXHp z`*M~2_`ogp7LR>?qebi9dGV0b6ggwZu+}K4O?<4jC~r<3;%_8?9va}U6?bhbKKfZc z`A|lS<}Ckh@c;k- literal 0 HcmV?d00001 diff --git a/doc/design/cluster_train/src/init_lock.png b/doc/design/cluster_train/src/init_lock.png new file mode 100644 index 0000000000000000000000000000000000000000..92404ee6d6c0f9a7727952bae3c869ba338ecd7f GIT binary patch literal 26774 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

?^QzZmlwkCBs^iLIKjO8EzT)3arMfVTO!Q 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 gH&f(Yg7l4S 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 Date: Wed, 10 May 2017 18:34:04 -0700 Subject: [PATCH 09/15] change img positioning --- doc/design/cluster_train/pserver_client.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/design/cluster_train/pserver_client.md b/doc/design/cluster_train/pserver_client.md index c1cb93434e58a..56469fc21535e 100644 --- a/doc/design/cluster_train/pserver_client.md +++ b/doc/design/cluster_train/pserver_client.md @@ -7,6 +7,7 @@ For an overview of trainer's role, please refer to [distributed training design The parameters on parameter servers need to be initialized. To provide maximum flexibility, we need to allow trainer initialized the parameters. Only one trainer will do the initialization, the other trainers will wait for the completion of initialization and get the parameters from the parameter servers. To select the trainer for initialization, every trainer will try to get a distributed lock, whoever owns the lock will do the initialization. As illustrated below: + The select process is encapsulated in the C API function: @@ -14,6 +15,7 @@ The select process is encapsulated in the C API function: int paddle_begin_init_params(paddle_pserver_client* client, const char* config_proto); ``` The selected trainer's call to `paddle_begin_init_params` will return with 1, and the other trainers' call to `paddle_begin_init_params` will block until initialization is done, and return 0. As illustrated below: + ## C Interface From c583447e900af61ac69fa6b0a41e9ab10cf3e90a Mon Sep 17 00:00:00 2001 From: Helin Wang Date: Wed, 10 May 2017 18:36:46 -0700 Subject: [PATCH 10/15] add subtitle --- doc/design/cluster_train/pserver_client.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/design/cluster_train/pserver_client.md b/doc/design/cluster_train/pserver_client.md index 56469fc21535e..ee40eb32c55a4 100644 --- a/doc/design/cluster_train/pserver_client.md +++ b/doc/design/cluster_train/pserver_client.md @@ -6,10 +6,14 @@ For an overview of trainer's role, please refer to [distributed training design The parameters on parameter servers need to be initialized. To provide maximum flexibility, we need to allow trainer initialized the parameters. Only one trainer will do the initialization, the other trainers will wait for the completion of initialization and get the parameters from the parameter servers. +### Trainer Selection + To select the trainer for initialization, every trainer will try to get a distributed lock, whoever owns the lock will do the initialization. As illustrated below: +### Selection Process + The select process is encapsulated in the C API function: ```c int paddle_begin_init_params(paddle_pserver_client* client, const char* config_proto); From 00d4d89a28bece66daea64989bed2990d5838df5 Mon Sep 17 00:00:00 2001 From: Helin Wang Date: Thu, 11 May 2017 17:37:02 -0700 Subject: [PATCH 11/15] add more sections --- doc/design/cluster_train/pserver_client.md | 59 +++++++++++++++------- 1 file changed, 42 insertions(+), 17 deletions(-) diff --git a/doc/design/cluster_train/pserver_client.md b/doc/design/cluster_train/pserver_client.md index ee40eb32c55a4..e1fa45263965f 100644 --- a/doc/design/cluster_train/pserver_client.md +++ b/doc/design/cluster_train/pserver_client.md @@ -2,6 +2,39 @@ For an overview of trainer's role, please refer to [distributed training design doc](README.md). In this design doc, we will discuss the parameter server's client library, which will manage communication with parameter servers. The library will be implemented in [Go](https://golang.org/) and made available as a static or dynamic library with a C header file. +## Parameter Partition + +Each parameter will be partitioned into parameter chunks to make the parameters evenly distributed on parameter servers. The partition is done automatically by the client library. The *sparse parameter* require a little different treatment: + +### Sparse Parameter + +The sparse parameter is a parameter that is updated sparsely. The name is somewhat misleading, it does not have a sparse representation, it is conceptually a dense vector. + +Because a sparse parameter is updated sparsely, the trainer will have to partition the sparse parameter. Because the parameter server will merge all sparse parameter shard into the same file when saving the parameter. It needs special naming convention: + +If a sparse parameter is partitioned into n shards, they should be named as: + +```text +name:sparse-0 +name:sparse-1 +... +name:sparse-n-1 +``` + +## Gradient Optimization + +There are two ways to perform model optimization according to gradients: + +- On Client + The client does forward and backward update multiple steps. In each step, the gradients are calculated each step and a new model is generated. After some steps, the client will calculate the difference between the newest model and the old model at step 0. The difference will be updated to parameter servers. Parameter servers will just update parameters according to the difference without any optimization using gradients (such as Adam and L1 regularization). + +- On Parameter Server + The client will send gradients to parameter servers, the parameter server will do the optimization using gradients. + +## L1 and L2 Regularization + +PaddlePaddle allows L1 or L2 regularizations to be specified per parameter, so when the trainer initializes the parameter. When the parameter server is doing the optimization, the trainer needs to pass a parameter configuration to parameter servers to indicate the Regularization. + ## Parameter Initialization The parameters on parameter servers need to be initialized. To provide maximum flexibility, we need to allow trainer initialized the parameters. Only one trainer will do the initialization, the other trainers will wait for the completion of initialization and get the parameters from the parameter servers. @@ -54,24 +87,25 @@ void paddle_pserver_client_release(paddle_pserver_client* client); * initialization is done, and they need to get the initialized * parameters from parameter servers using @paddle_get_params. * - * @param config_proto serialized parameter server configuration in + * @param pserver_config_proto serialized parameter server configuration in * Protocol Buffers format. * @return 1 if the trainer is selected to initialize parameter * servers, otherwise 0. */ -int paddle_begin_init_params(paddle_pserver_client* client, const char* config_proto); +int paddle_begin_init_params(paddle_pserver_client* client, const char* pserver_config_proto); /** * @brief paddle_init_param initializes the parameter on parameter * servers. * * @param param the parameter to initialize. + * @param param_config_proto the configuration for the parameter. * @return 0 if successful, otherwise -1. On failure, the trainer * needs to restart the entire initialization process (starting from * @paddle_begin_init_param). Or simply exit the program and wait for * the cluster management system to restart the trainer. */ -int paddle_init_param(paddle_pserver_client* client, paddle_parameter params); +int paddle_init_param(paddle_pserver_client* client, paddle_parameter params, const char* param_config_proto); /** * @brief paddle_finish_init_params tells parameter servers client has @@ -89,31 +123,22 @@ int paddle_finish_init_params(paddle_pserver_client* client); * updating parameters. * * @param grads the array of gradients to send. - * @param total the total number of gradient inside the gradient array. + * @param len the length of the gradient array. * @param learning_rate the learning rate for the gradients. * @return 0 if successful, otherwise -1. */ -int paddle_send_grads(paddle_pserver_client* client, const paddle_gradient* grads, int total, double learning_rate); - -/** - * @brief paddle_set_params sets parameters to parameter servers. - * - * @param params the array of parameters to set to parameter servers. - * @param total the total number of parameters inside the parameter - * array. - * @return 0 if successful, otherwise -1. - */ -int paddle_set_params(paddle_pserver_client* client, const paddle_parameter* params, int total); +int paddle_send_grads(paddle_pserver_client* client, const paddle_gradient* grads, int len); /** * @brief paddle_get_params gets parameters from parameter servers. * * @param names the array of names of the parameters to get. * @param dst the destination array of parameters to save to. - * @param total the total number of parameters to get. + * @param len the length of the names array and the paddle_parameter + * array. * @return 0 if successful, otherwise -1. */ -int paddle_get_params(paddle_pserver_client* client, const char** names, paddle_parameter* dst, int total); +int paddle_get_params(paddle_pserver_client* client, const char** names, paddle_parameter* dst, int len); /** * @brief paddle_save_model indicates parameters to save the parameter From 3da6c853400ab19557108b865f493f8ec73fb538 Mon Sep 17 00:00:00 2001 From: Helin Wang Date: Thu, 11 May 2017 17:40:51 -0700 Subject: [PATCH 12/15] use enum instead of define --- doc/design/cluster_train/pserver_client.md | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/doc/design/cluster_train/pserver_client.md b/doc/design/cluster_train/pserver_client.md index e1fa45263965f..0a45a86117713 100644 --- a/doc/design/cluster_train/pserver_client.md +++ b/doc/design/cluster_train/pserver_client.md @@ -58,18 +58,20 @@ The selected trainer's call to `paddle_begin_init_params` will return with 1, an ## C Interface ```c -#define PADDLE_ELEMENT_TYPE_INT32 0 -#define PADDLE_ELEMENT_TYPE_UINT32 1 -#define PADDLE_ELEMENT_TYPE_INT64 2 -#define PADDLE_ELEMENT_TYPE_UINT64 3 -#define PADDLE_ELEMENT_TYPE_FLOAT32 4 -#define PADDLE_ELEMENT_TYPE_FLOAT64 5 +typedef enum { + PADDLE_ELEMENT_TYPE_INT32 = 0, + PADDLE_ELEMENT_TYPE_UINT32 = 1, + PADDLE_ELEMENT_TYPE_INT64 = 2, + PADDLE_ELEMENT_TYPE_UINT64 = 3, + PADDLE_ELEMENT_TYPE_FLOAT32 = 4, + PADDLE_ELEMENT_TYPE_FLOAT64 = 5, +} paddle_element_type; typedef struct { - char* name; - int element_type; - void* content; - int content_len; + char* name; + paddle_element_type element_type; + void* content; + int content_len; } paddle_parameter, paddle_gradient; typedef struct paddle_pserver_client paddle_pserver_client; From 747994d00fbc9ab202bd362438b9d3f7c8976217 Mon Sep 17 00:00:00 2001 From: Helin Wang Date: Thu, 11 May 2017 17:54:41 -0700 Subject: [PATCH 13/15] polish wording --- doc/design/cluster_train/pserver_client.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/doc/design/cluster_train/pserver_client.md b/doc/design/cluster_train/pserver_client.md index 0a45a86117713..0531630fb8f7b 100644 --- a/doc/design/cluster_train/pserver_client.md +++ b/doc/design/cluster_train/pserver_client.md @@ -8,7 +8,7 @@ Each parameter will be partitioned into parameter chunks to make the parameters ### Sparse Parameter -The sparse parameter is a parameter that is updated sparsely. The name is somewhat misleading, it does not have a sparse representation, it is conceptually a dense vector. +The sparse parameter is a parameter that is updated sparsely. The name is somewhat misleading, it does not have a sparse representation, it has the same representation as a dense vector. Because a sparse parameter is updated sparsely, the trainer will have to partition the sparse parameter. Because the parameter server will merge all sparse parameter shard into the same file when saving the parameter. It needs special naming convention: @@ -21,14 +21,18 @@ name:sparse-1 name:sparse-n-1 ``` -## Gradient Optimization +The library is unaware of the partition, and treat each parameter independently. Only when saving parameters, the parameter servers will merge the sparse parameters according to the naming convention. -There are two ways to perform model optimization according to gradients: +## Model Optimization Using Gradient + +There are two ways to perform model optimization using gradients: - On Client - The client does forward and backward update multiple steps. In each step, the gradients are calculated each step and a new model is generated. After some steps, the client will calculate the difference between the newest model and the old model at step 0. The difference will be updated to parameter servers. Parameter servers will just update parameters according to the difference without any optimization using gradients (such as Adam and L1 regularization). + + The client does forward and backward update multiple steps. In each step, the gradients are calculated each step and a new model is generated. After some steps, the client will calculate the difference between the newest model and the old model at step 0. The difference will be updated to parameter servers. Parameter servers will just update parameters using the difference without any optimization using gradients (such as Adam and L1 regularization). - On Parameter Server + The client will send gradients to parameter servers, the parameter server will do the optimization using gradients. ## L1 and L2 Regularization From 7d7473842f2ad84bf00988b161ad25992b17b8c4 Mon Sep 17 00:00:00 2001 From: Helin Wang Date: Thu, 11 May 2017 18:00:55 -0700 Subject: [PATCH 14/15] polish wording --- doc/design/cluster_train/pserver_client.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/doc/design/cluster_train/pserver_client.md b/doc/design/cluster_train/pserver_client.md index 0531630fb8f7b..500894fac76ab 100644 --- a/doc/design/cluster_train/pserver_client.md +++ b/doc/design/cluster_train/pserver_client.md @@ -23,25 +23,25 @@ name:sparse-n-1 The library is unaware of the partition, and treat each parameter independently. Only when saving parameters, the parameter servers will merge the sparse parameters according to the naming convention. -## Model Optimization Using Gradient +## Model Optimization Using Gradients There are two ways to perform model optimization using gradients: - On Client - The client does forward and backward update multiple steps. In each step, the gradients are calculated each step and a new model is generated. After some steps, the client will calculate the difference between the newest model and the old model at step 0. The difference will be updated to parameter servers. Parameter servers will just update parameters using the difference without any optimization using gradients (such as Adam and L1 regularization). + The client does multiple steps of forward and backward update. In each step, the gradients are calculated and a new model is generated. After some steps, the client will calculate the difference between the newest model and the old model at step 0. The difference will be updated to parameter servers. Parameter servers will just update parameters using the difference without any optimization using gradients (such as Adam and L1 regularization). - On Parameter Server - The client will send gradients to parameter servers, the parameter server will do the optimization using gradients. + The client will send accumulated gradients to parameter servers, the parameter server will do the optimization using gradients. ## L1 and L2 Regularization -PaddlePaddle allows L1 or L2 regularizations to be specified per parameter, so when the trainer initializes the parameter. When the parameter server is doing the optimization, the trainer needs to pass a parameter configuration to parameter servers to indicate the Regularization. +PaddlePaddle allows L1 or L2 regularizations to be specified per parameter, so when the trainer initializes the parameter it needs include a parameter configuration when L1 or L2 regularization is necessary. ## Parameter Initialization -The parameters on parameter servers need to be initialized. To provide maximum flexibility, we need to allow trainer initialized the parameters. Only one trainer will do the initialization, the other trainers will wait for the completion of initialization and get the parameters from the parameter servers. +The parameters on parameter servers need to be initialized. To provide maximum flexibility, the trainer will initialize the parameters. Only one trainer will do the initialization, the other trainers will wait for the completion of initialization and get the parameters from the parameter servers. ### Trainer Selection @@ -49,9 +49,9 @@ To select the trainer for initialization, every trainer will try to get a distri -### Selection Process +### Trainer Selection Process -The select process is encapsulated in the C API function: +The trainer select process is encapsulated in the C API function: ```c int paddle_begin_init_params(paddle_pserver_client* client, const char* config_proto); ``` From 5081c691b1f706080dc7e87c6ca459162f4a8ca8 Mon Sep 17 00:00:00 2001 From: Helin Wang Date: Fri, 12 May 2017 11:14:11 -0700 Subject: [PATCH 15/15] rename parameter chunks to parameter blocks --- doc/design/cluster_train/pserver_client.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/design/cluster_train/pserver_client.md b/doc/design/cluster_train/pserver_client.md index 500894fac76ab..392bab25e9de6 100644 --- a/doc/design/cluster_train/pserver_client.md +++ b/doc/design/cluster_train/pserver_client.md @@ -4,7 +4,7 @@ For an overview of trainer's role, please refer to [distributed training design ## Parameter Partition -Each parameter will be partitioned into parameter chunks to make the parameters evenly distributed on parameter servers. The partition is done automatically by the client library. The *sparse parameter* require a little different treatment: +Each parameter will be partitioned into parameter blocks to make the parameters evenly distributed on parameter servers. The partition is done automatically by the client library. The *sparse parameter* require a little different treatment: ### Sparse Parameter