From f7a943f7fcb3b6f8ae58ca52be415d45c61b786a Mon Sep 17 00:00:00 2001 From: Anyang Peng <137014849+anyangml@users.noreply.github.com> Date: Fri, 1 Mar 2024 13:42:22 +0800 Subject: [PATCH 01/11] chore: add dev doc --- doc/development/create-a-model.md | 109 +++++++++++++++++++++++++++++- 1 file changed, 108 insertions(+), 1 deletion(-) diff --git a/doc/development/create-a-model.md b/doc/development/create-a-model.md index d71a6e519a..b64756062b 100644 --- a/doc/development/create-a-model.md +++ b/doc/development/create-a-model.md @@ -10,7 +10,10 @@ To incorporate your custom model you'll need to: ## Design a new component -When creating a new component, take descriptor as the example, you should inherit {py:class}`deepmd.tf.descriptor.descriptor.Descriptor` class and override several methods. Abstract methods such as {py:class}`deepmd.tf.descriptor.descriptor.Descriptor.build` must be implemented and others are not. You should keep arguments of these methods unchanged. +::::{tab-set} + +:::{tab-item} TensorFlow {{ tensorflow_icon }} +When creating a new component, take descriptor as the example, you should inherit from {py:class}`deepmd.tf.descriptor.descriptor.Descriptor` class and override several methods. Abstract methods such as {py:class}`deepmd.tf.descriptor.descriptor.Descriptor.build` must be implemented and others are not. You should keep arguments of these methods unchanged. After implementation, you need to register the component with a key: ```py @@ -22,7 +25,111 @@ class SomeDescript(Descriptor): def __init__(self, arg1: bool, arg2: float) -> None: pass ``` +::: + +:::{tab-item} PyTorch {{ pytorch_icon }} +The PyTorch backend follows a meticulously structured inheritance pattern. When creating a new descriptor, it is essential to inherit from both the {py:class}`deepmd.pt.model.descriptor.base_descriptor.BaseDescriptor` class and the {py:class}`torch.nn.Module` class. Abstract methods, including {py:class}`deepmd.pt.model.descriptor.base_descriptor.BaseDescriptor.fwd`, must be implemented, while others remain optional. It is crucial to adhere to the original method arguments without any modifications. Once the implementation is complete, the next step involves registering the component with a designated key: + +```py +from deepmd.pt.model.descriptor.base_descriptor import ( + BaseDescriptor, +) + +@BaseDescriptor.register("some_descrpt") +class SomeDescript(BaseDescriptor, torch.nn.Module): + def __init__(self, arg1: bool, arg2: float) -> None: + pass + + def forward( + self, + coord_ext: torch.Tensor, + atype_ext: torch.Tensor, + nlist: torch.Tensor, + mapping: Optional[torch.Tensor] = None, + ): + pass + + def serialize(self) -> dict: + pass + + @classmethod + def deserialize(cls, data: dict) -> "SomeDescript": + pass +``` +The serialize and deserialize methods are important for cross-backend model conversion. + +In many instances, there is no requirement to create a new fitting net. For fitting user-defined scalar properties, the {py:class}`deepmd.pt.model.task.ener.InvarFitting` class can be utilized. However, if there is a need for a new fitting net, one should inherit from both the {py:class}`deepmd.pt.model.task.base_fitting.BaseFitting` class and the {py:class}`torch.nn.Module` class. Alternatively, for a more straightforward approach, inheritance from the {py:class}`deepmd.pt.model.task.fitting.GeneralFitting` class is also an option. + +```py +from deepmd.pt.model.task.fitting import ( + GeneralFitting, +) +from deepmd.dpmodel import ( + FittingOutputDef + fitting_check_output, +) + +@GeneralFitting.register("some_fitting") +@fitting_check_output +class SomeFittingNet(GeneralFitting): + def __init__(self, arg1: bool, arg2: float) -> None: + pass + + def forward( + self, + descriptor: torch.Tensor, + atype: torch.Tensor, + gr: Optional[torch.Tensor] = None, + g2: Optional[torch.Tensor] = None, + h2: Optional[torch.Tensor] = None, + fparam: Optional[torch.Tensor] = None, + aparam: Optional[torch.Tensor] = None, + ): + pass + + def output_def(self) -> FittingOutputDef: + pass +``` + +The model architecture within the PyTorch backend is structured with multiple layers of abstraction to provide a high degree of flexibility. Generally, the process begins with an atomic model responsible for handling atom-wise property calculations. This atomic model should inherit from the {py:class}`deepmd.pt.model.atomic_model.base_atomic_model.BaseAtomicModel` class and the {py:class}`torch.nn.Module` class. + +Subsequently, the `AtomicModel` is encapsulated using the `make_model(AtomicModel)` function, employing the {py:class}`deepmd.pt.model.model.make_model.make_model`. The purpose of the `make_model` method is to facilitate the translation between the original system and the extended system. + +Finally, the entire model is wrapped within a `DPModel`, which must inherit from the {py:class}`deepmd.pt.model.model.model.BaseModel` class and include the aforementioned `make_model(AtomicModel)`. The user directly interacts with a wrapper built on top of the `DPModel` to seamlessly handle result translations. + +```py +from deepmd.pt.model.atomic_model.base_atomic_model import ( + BaseAtomicModel, +) +from deepmd.pt.model.model.make_model import ( + make_model, +) +from deepmd.pt.model.model.model import ( + BaseModel, +) + +class SomeAtomicModel(BaseAtomicModel, torch.nn.Module): + def __init__(self, arg1: bool, arg2: float) -> None: + pass + + def forward_atomic(self): + pass + +@BaseModel.register("some_model") +class SomeDPModel(make_model(SomeAtomicModel), BaseModel): + pass + +class SomeModel(SomeDPModel): + pass +``` + +::: +:::{tab-item} DPModel {{ dpmodel_icon }} + +The DPModel backend is implemented using pure NumPy, serving as a reference backend for maintaining consistency in tests. The design pattern closely mirrors that of the PyTorch backend, and developers can refer to the PyTorch development guide for detailed instructions. +::: +:::: ## Register new arguments To let someone uses your new component in their input file, you need to create a new method that returns some `Argument` of your new component, and then register new arguments. For example, the code below From 73fc0ecb5d5c6dfb610606211d47222f895c0e90 Mon Sep 17 00:00:00 2001 From: Anyang Peng <137014849+anyangml@users.noreply.github.com> Date: Fri, 1 Mar 2024 13:47:22 +0800 Subject: [PATCH 02/11] fix: precommit --- doc/development/create-a-model.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/development/create-a-model.md b/doc/development/create-a-model.md index b64756062b..15ae87b4b3 100644 --- a/doc/development/create-a-model.md +++ b/doc/development/create-a-model.md @@ -56,10 +56,13 @@ class SomeDescript(BaseDescriptor, torch.nn.Module): def deserialize(cls, data: dict) -> "SomeDescript": pass ``` + The serialize and deserialize methods are important for cross-backend model conversion. + In many instances, there is no requirement to create a new fitting net. For fitting user-defined scalar properties, the {py:class}`deepmd.pt.model.task.ener.InvarFitting` class can be utilized. However, if there is a need for a new fitting net, one should inherit from both the {py:class}`deepmd.pt.model.task.base_fitting.BaseFitting` class and the {py:class}`torch.nn.Module` class. Alternatively, for a more straightforward approach, inheritance from the {py:class}`deepmd.pt.model.task.fitting.GeneralFitting` class is also an option. + ```py from deepmd.pt.model.task.fitting import ( GeneralFitting, From bb757c3e6902937e963759dd1c5c99811c89c9e0 Mon Sep 17 00:00:00 2001 From: Anyang Peng <137014849+anyangml@users.noreply.github.com> Date: Fri, 1 Mar 2024 13:49:02 +0800 Subject: [PATCH 03/11] fix: precommit --- doc/development/create-a-model.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/development/create-a-model.md b/doc/development/create-a-model.md index 15ae87b4b3..0eeb2532bb 100644 --- a/doc/development/create-a-model.md +++ b/doc/development/create-a-model.md @@ -68,7 +68,7 @@ from deepmd.pt.model.task.fitting import ( GeneralFitting, ) from deepmd.dpmodel import ( - FittingOutputDef + FittingOutputDef, fitting_check_output, ) From 7747cc4f0d41f46b50bdd1547aff0957b86890ea Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 1 Mar 2024 05:49:40 +0000 Subject: [PATCH 04/11] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- doc/development/create-a-model.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/development/create-a-model.md b/doc/development/create-a-model.md index 0eeb2532bb..b7dbf724b9 100644 --- a/doc/development/create-a-model.md +++ b/doc/development/create-a-model.md @@ -35,6 +35,7 @@ from deepmd.pt.model.descriptor.base_descriptor import ( BaseDescriptor, ) + @BaseDescriptor.register("some_descrpt") class SomeDescript(BaseDescriptor, torch.nn.Module): def __init__(self, arg1: bool, arg2: float) -> None: @@ -72,6 +73,7 @@ from deepmd.dpmodel import ( fitting_check_output, ) + @GeneralFitting.register("some_fitting") @fitting_check_output class SomeFittingNet(GeneralFitting): @@ -111,6 +113,7 @@ from deepmd.pt.model.model.model import ( BaseModel, ) + class SomeAtomicModel(BaseAtomicModel, torch.nn.Module): def __init__(self, arg1: bool, arg2: float) -> None: pass @@ -118,10 +121,12 @@ class SomeAtomicModel(BaseAtomicModel, torch.nn.Module): def forward_atomic(self): pass + @BaseModel.register("some_model") class SomeDPModel(make_model(SomeAtomicModel), BaseModel): pass + class SomeModel(SomeDPModel): pass ``` From a69dcf7baf410c37f0998d6ce5a8b0f097c5e2e0 Mon Sep 17 00:00:00 2001 From: Anyang Peng <137014849+anyangml@users.noreply.github.com> Date: Fri, 1 Mar 2024 14:00:25 +0800 Subject: [PATCH 05/11] fix: update arg register --- doc/development/create-a-model.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/development/create-a-model.md b/doc/development/create-a-model.md index b7dbf724b9..f25cf47f8c 100644 --- a/doc/development/create-a-model.md +++ b/doc/development/create-a-model.md @@ -146,7 +146,7 @@ To let someone uses your new component in their input file, you need to create a from typing import List from dargs import Argument -from deepmd.tf.utils.argcheck import descrpt_args_plugin +from deepmd.utils.argcheck import descrpt_args_plugin @descrpt_args_plugin.register("some_descrpt") From b8e09dfb94e77f8a86d2a4628caf0c3fa69a929f Mon Sep 17 00:00:00 2001 From: Anyang Peng <137014849+anyangml@users.noreply.github.com> Date: Fri, 1 Mar 2024 14:16:27 +0800 Subject: [PATCH 06/11] fix: rephrase --- doc/development/create-a-model.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/development/create-a-model.md b/doc/development/create-a-model.md index f25cf47f8c..f8a3ba3330 100644 --- a/doc/development/create-a-model.md +++ b/doc/development/create-a-model.md @@ -98,7 +98,7 @@ class SomeFittingNet(GeneralFitting): The model architecture within the PyTorch backend is structured with multiple layers of abstraction to provide a high degree of flexibility. Generally, the process begins with an atomic model responsible for handling atom-wise property calculations. This atomic model should inherit from the {py:class}`deepmd.pt.model.atomic_model.base_atomic_model.BaseAtomicModel` class and the {py:class}`torch.nn.Module` class. -Subsequently, the `AtomicModel` is encapsulated using the `make_model(AtomicModel)` function, employing the {py:class}`deepmd.pt.model.model.make_model.make_model`. The purpose of the `make_model` method is to facilitate the translation between the original system and the extended system. +Subsequently, the `AtomicModel` is encapsulated using the `make_model(AtomicModel)` function, employing the `deepmd.pt.model.model.make_model.make_model` function. The purpose of the `make_model` wrapper is to facilitate the translation between the original system and the extended system. Finally, the entire model is wrapped within a `DPModel`, which must inherit from the {py:class}`deepmd.pt.model.model.model.BaseModel` class and include the aforementioned `make_model(AtomicModel)`. The user directly interacts with a wrapper built on top of the `DPModel` to seamlessly handle result translations. From 7e9db1da31d700f9d4239a06470107bcd0db858e Mon Sep 17 00:00:00 2001 From: Anyang Peng <137014849+anyangml@users.noreply.github.com> Date: Fri, 1 Mar 2024 15:57:19 +0800 Subject: [PATCH 07/11] fix: split tf/pt --- ...create-a-model.md => create-a-model-pt.md} | 70 +++++++++-------- doc/development/create-a-model-tf.md | 75 +++++++++++++++++++ doc/index.rst | 3 +- 3 files changed, 116 insertions(+), 32 deletions(-) rename doc/development/{create-a-model.md => create-a-model-pt.md} (53%) create mode 100644 doc/development/create-a-model-tf.md diff --git a/doc/development/create-a-model.md b/doc/development/create-a-model-pt.md similarity index 53% rename from doc/development/create-a-model.md rename to doc/development/create-a-model-pt.md index f8a3ba3330..289e6c83c1 100644 --- a/doc/development/create-a-model.md +++ b/doc/development/create-a-model-pt.md @@ -1,46 +1,51 @@ -# Create a model +# Create a model in PyTorch If you'd like to create a new model that isn't covered by the existing DeePMD-kit library, but reuse DeePMD-kit's other efficient modules such as data processing, trainner, etc, you may want to read this section. To incorporate your custom model you'll need to: -1. Register and implement new components (e.g. descriptor) in a Python file. You may also want to register new TensorFlow OPs if necessary. +1. Register and implement new components (e.g. descriptor) in a Python file. 2. Register new arguments for user inputs. 3. Package new codes into a Python package. 4. Test new models. ## Design a new component -::::{tab-set} +With DeePMD-kit v3, we have expanded support to include two additional backends alongside TensorFlow: the PyTorch backend and the DPModel backend. The PyTorch backend adopts a highly modularized design to provide flexibility and extensibility. It ensures a consistent experience for both training and inference, aligning with the TensorFlow backend. -:::{tab-item} TensorFlow {{ tensorflow_icon }} -When creating a new component, take descriptor as the example, you should inherit from {py:class}`deepmd.tf.descriptor.descriptor.Descriptor` class and override several methods. Abstract methods such as {py:class}`deepmd.tf.descriptor.descriptor.Descriptor.build` must be implemented and others are not. You should keep arguments of these methods unchanged. +The DPModel backend is implemented in pure NumPy, serving as a reference backend to ensure consistency in tests. Its design pattern closely parallels that of the PyTorch backend. -After implementation, you need to register the component with a key: -```py -from deepmd.tf.descriptor import Descriptor +### New descriptors - -@Descriptor.register("some_descrpt") -class SomeDescript(Descriptor): - def __init__(self, arg1: bool, arg2: float) -> None: - pass -``` -::: - -:::{tab-item} PyTorch {{ pytorch_icon }} -The PyTorch backend follows a meticulously structured inheritance pattern. When creating a new descriptor, it is essential to inherit from both the {py:class}`deepmd.pt.model.descriptor.base_descriptor.BaseDescriptor` class and the {py:class}`torch.nn.Module` class. Abstract methods, including {py:class}`deepmd.pt.model.descriptor.base_descriptor.BaseDescriptor.fwd`, must be implemented, while others remain optional. It is crucial to adhere to the original method arguments without any modifications. Once the implementation is complete, the next step involves registering the component with a designated key: +When creating a new descriptor, it is essential to inherit from both the {py:class}`deepmd.pt.model.descriptor.base_descriptor.BaseDescriptor` class and the {py:class}`torch.nn.Module` class. Abstract methods, including {py:class}`deepmd.pt.model.descriptor.base_descriptor.BaseDescriptor.forward`, must be implemented, while others remain optional. It is crucial to adhere to the original method arguments without any modifications. Once the implementation is complete, the next step involves registering the component with a designated key: ```py from deepmd.pt.model.descriptor.base_descriptor import ( BaseDescriptor, ) - @BaseDescriptor.register("some_descrpt") class SomeDescript(BaseDescriptor, torch.nn.Module): def __init__(self, arg1: bool, arg2: float) -> None: pass + def get_rcut(self) -> float: + pass + + def get_nnei(self) -> int: + pass + + def get_ntypes(self) -> int: + pass + + def get_dim_out(self) -> int: + pass + + def get_dim_emb(self) -> int: + pass + + def mixed_types(self) -> bool: + pass + def forward( self, coord_ext: torch.Tensor, @@ -53,13 +58,16 @@ class SomeDescript(BaseDescriptor, torch.nn.Module): def serialize(self) -> dict: pass - @classmethod def deserialize(cls, data: dict) -> "SomeDescript": pass + + def update_sel(cls, global_jdata: dict, local_jdata: dict): + pass ``` The serialize and deserialize methods are important for cross-backend model conversion. +### New fitting nets In many instances, there is no requirement to create a new fitting net. For fitting user-defined scalar properties, the {py:class}`deepmd.pt.model.task.ener.InvarFitting` class can be utilized. However, if there is a need for a new fitting net, one should inherit from both the {py:class}`deepmd.pt.model.task.base_fitting.BaseFitting` class and the {py:class}`torch.nn.Module` class. Alternatively, for a more straightforward approach, inheritance from the {py:class}`deepmd.pt.model.task.fitting.GeneralFitting` class is also an option. @@ -95,12 +103,13 @@ class SomeFittingNet(GeneralFitting): def output_def(self) -> FittingOutputDef: pass ``` +### New models +The PyTorch backend's model architecture is meticulously structured with multiple layers of abstraction, ensuring a high degree of flexibility. Typically, the process commences with an atomic model responsible for atom-wise property calculations. This atomic model inherits from both the {py:class}`deepmd.pt.model.atomic_model.base_atomic_model.BaseAtomicModel` class and the {py:class}`torch.nn.Module` class. -The model architecture within the PyTorch backend is structured with multiple layers of abstraction to provide a high degree of flexibility. Generally, the process begins with an atomic model responsible for handling atom-wise property calculations. This atomic model should inherit from the {py:class}`deepmd.pt.model.atomic_model.base_atomic_model.BaseAtomicModel` class and the {py:class}`torch.nn.Module` class. +Subsequently, the `AtomicModel` is encapsulated using the `make_model(AtomicModel)` function, which leverages the `deepmd.pt.model.model.make_model.make_model` function. The purpose of the `make_model` wrapper is to facilitate the translation between atomic property predictions and the extended property predictions and differentiation , e.g. the reduction of atomic energy contribution and the autodiff for calculating the forces and virial. The developers usually need to implement an `AtomicModel` not a `Model`. -Subsequently, the `AtomicModel` is encapsulated using the `make_model(AtomicModel)` function, employing the `deepmd.pt.model.model.make_model.make_model` function. The purpose of the `make_model` wrapper is to facilitate the translation between the original system and the extended system. +Finally, the entire model is enveloped within a `DPModel`, necessitating inheritance from the {py:class}`deepmd.pt.model.model.model.BaseModel` class and the inclusion of the aforementioned `make_model(AtomicModel)`. In most cases, there is no need to reconstruct a `DPModel`; one can directly utilize the {py:class}`deepmd.pt.model.model.dp_model.DPModel` class by providing the corresponding fitting net. For models without a fitting net, like the `PairTableModel`, a new `DPModel` needs to be designed. Users seamlessly interact with a wrapper built on top of the `DPModel` to handle key translations of the returned dictionary. -Finally, the entire model is wrapped within a `DPModel`, which must inherit from the {py:class}`deepmd.pt.model.model.model.BaseModel` class and include the aforementioned `make_model(AtomicModel)`. The user directly interacts with a wrapper built on top of the `DPModel` to seamlessly handle result translations. ```py from deepmd.pt.model.atomic_model.base_atomic_model import ( @@ -131,13 +140,6 @@ class SomeModel(SomeDPModel): pass ``` -::: -:::{tab-item} DPModel {{ dpmodel_icon }} - -The DPModel backend is implemented using pure NumPy, serving as a reference backend for maintaining consistency in tests. The design pattern closely mirrors that of the PyTorch backend, and developers can refer to the PyTorch development guide for detailed instructions. - -::: -:::: ## Register new arguments To let someone uses your new component in their input file, you need to create a new method that returns some `Argument` of your new component, and then register new arguments. For example, the code below @@ -187,4 +189,10 @@ where `deepmd_some_descrtpt` is the module of your codes. It is equivalent to `f If you place `SomeDescript` and `descrpt_some_args` into different modules, you are also expected to add `descrpt_some_args` to `entry_points`. -After you install your new package, you can now use `dp train` to run your new model. +After you install your new package, you can now use `dp --pt train` to run your new model. + +## Unit tests + +When transferring features from another backend to the PyTorch backend, it is essential to include a regression test in `/source/tests/consistent` to validate the consistency of the PyTorch backend with other backends. Presently, the regression tests cover self-consistency and cross-backend consistency between TensorFlow, PyTorch, and DPModel (Numpy) through the serialization/deserialization technique. + +During the development of new components within the PyTorch backend, it is necessary to provide a DPModel (Numpy) implementation and incorporate corresponding regression tests. For PyTorch components, developers are also required to include a unit test using `torch.jit`. diff --git a/doc/development/create-a-model-tf.md b/doc/development/create-a-model-tf.md new file mode 100644 index 0000000000..78e5fd3e42 --- /dev/null +++ b/doc/development/create-a-model-tf.md @@ -0,0 +1,75 @@ +# Create a model in TensorFlow + +If you'd like to create a new model that isn't covered by the existing DeePMD-kit library, but reuse DeePMD-kit's other efficient modules such as data processing, trainner, etc, you may want to read this section. + +To incorporate your custom model you'll need to: +1. Register and implement new components (e.g. descriptor) in a Python file. You may also want to register new TensorFlow OPs if necessary. +2. Register new arguments for user inputs. +3. Package new codes into a Python package. +4. Test new models. + +## Design a new component + +When creating a new component, take descriptor as the example, one should inherit from the {py:class}`deepmd.tf.descriptor.descriptor.Descriptor` class and override several methods. Abstract methods such as {py:class}`deepmd.tf.descriptor.descriptor.Descriptor.build` must be implemented and others are not. You should keep arguments of these methods unchanged. + +After implementation, you need to register the component with a key: +```py +from deepmd.tf.descriptor import Descriptor + + +@Descriptor.register("some_descrpt") +class SomeDescript(Descriptor): + def __init__(self, arg1: bool, arg2: float) -> None: + pass +``` + +## Register new arguments + +To let someone uses your new component in their input file, you need to create a new method that returns some `Argument` of your new component, and then register new arguments. For example, the code below + +```py +from typing import List + +from dargs import Argument +from deepmd.utils.argcheck import descrpt_args_plugin + + +@descrpt_args_plugin.register("some_descrpt") +def descrpt_some_args() -> List[Argument]: + return [ + Argument("arg1", bool, optional=False, doc="balabala"), + Argument("arg2", float, optional=True, default=6.0, doc="haha"), + ] +``` + +allows one to use your new descriptor as below: + +```json +"descriptor" :{ + "type": "some_descrpt", + "arg1": true, + "arg2": 6.0 +} +``` + +The arguments here should be consistent with the class arguments of your new component. + +## Package new codes + +You may use `setuptools` to package new codes into a new Python package. It's crucial to add your new component to `entry_points['deepmd']` in `setup.py`: + +```py +entry_points = ( + { + "deepmd": [ + "some_descrpt=deepmd_some_descrtpt:SomeDescript", + ], + }, +) +``` + +where `deepmd_some_descrtpt` is the module of your codes. It is equivalent to `from deepmd_some_descrtpt import SomeDescript`. + +If you place `SomeDescript` and `descrpt_some_args` into different modules, you are also expected to add `descrpt_some_args` to `entry_points`. + +After you install your new package, you can now use `dp train` to run your new model. \ No newline at end of file diff --git a/doc/index.rst b/doc/index.rst index d089507886..7bff8d3957 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -64,7 +64,8 @@ DeePMD-kit is a package written in Python/C++, designed to minimize the effort r :caption: Developer Guide development/cmake - development/create-a-model + development/create-a-model-tf + development/create-a-model-pt development/type-embedding development/coding-conventions development/cicd From 559806367014593f1e7b0022aa3d3a65e2a927d2 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 1 Mar 2024 07:58:01 +0000 Subject: [PATCH 08/11] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- doc/development/create-a-model-pt.md | 1 + doc/development/create-a-model-tf.md | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/development/create-a-model-pt.md b/doc/development/create-a-model-pt.md index 289e6c83c1..d24554817f 100644 --- a/doc/development/create-a-model-pt.md +++ b/doc/development/create-a-model-pt.md @@ -23,6 +23,7 @@ from deepmd.pt.model.descriptor.base_descriptor import ( BaseDescriptor, ) + @BaseDescriptor.register("some_descrpt") class SomeDescript(BaseDescriptor, torch.nn.Module): def __init__(self, arg1: bool, arg2: float) -> None: diff --git a/doc/development/create-a-model-tf.md b/doc/development/create-a-model-tf.md index 78e5fd3e42..7c4f5335ec 100644 --- a/doc/development/create-a-model-tf.md +++ b/doc/development/create-a-model-tf.md @@ -72,4 +72,4 @@ where `deepmd_some_descrtpt` is the module of your codes. It is equivalent to `f If you place `SomeDescript` and `descrpt_some_args` into different modules, you are also expected to add `descrpt_some_args` to `entry_points`. -After you install your new package, you can now use `dp train` to run your new model. \ No newline at end of file +After you install your new package, you can now use `dp train` to run your new model. From c8f3ab81ade60615664636f2e95edfbce5af3847 Mon Sep 17 00:00:00 2001 From: Anyang Peng <137014849+anyangml@users.noreply.github.com> Date: Fri, 1 Mar 2024 17:26:11 +0800 Subject: [PATCH 09/11] fix: remove section --- doc/development/create-a-model-pt.md | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/doc/development/create-a-model-pt.md b/doc/development/create-a-model-pt.md index d24554817f..f684cbca5e 100644 --- a/doc/development/create-a-model-pt.md +++ b/doc/development/create-a-model-pt.md @@ -172,26 +172,6 @@ allows one to use your new descriptor as below: The arguments here should be consistent with the class arguments of your new component. -## Package new codes - -You may use `setuptools` to package new codes into a new Python package. It's crucial to add your new component to `entry_points['deepmd']` in `setup.py`: - -```py -entry_points = ( - { - "deepmd": [ - "some_descrpt=deepmd_some_descrtpt:SomeDescript", - ], - }, -) -``` - -where `deepmd_some_descrtpt` is the module of your codes. It is equivalent to `from deepmd_some_descrtpt import SomeDescript`. - -If you place `SomeDescript` and `descrpt_some_args` into different modules, you are also expected to add `descrpt_some_args` to `entry_points`. - -After you install your new package, you can now use `dp --pt train` to run your new model. - ## Unit tests When transferring features from another backend to the PyTorch backend, it is essential to include a regression test in `/source/tests/consistent` to validate the consistency of the PyTorch backend with other backends. Presently, the regression tests cover self-consistency and cross-backend consistency between TensorFlow, PyTorch, and DPModel (Numpy) through the serialization/deserialization technique. From 9e4aabac07ee10449a08a2ce7903fb787a348129 Mon Sep 17 00:00:00 2001 From: Anyang Peng <137014849+anyangml@users.noreply.github.com> Date: Fri, 1 Mar 2024 19:02:34 +0800 Subject: [PATCH 10/11] fix: rephrase --- doc/development/create-a-model-pt.md | 26 ++++---------------------- 1 file changed, 4 insertions(+), 22 deletions(-) diff --git a/doc/development/create-a-model-pt.md b/doc/development/create-a-model-pt.md index f684cbca5e..847e38f816 100644 --- a/doc/development/create-a-model-pt.md +++ b/doc/development/create-a-model-pt.md @@ -10,9 +10,9 @@ To incorporate your custom model you'll need to: ## Design a new component -With DeePMD-kit v3, we have expanded support to include two additional backends alongside TensorFlow: the PyTorch backend and the DPModel backend. The PyTorch backend adopts a highly modularized design to provide flexibility and extensibility. It ensures a consistent experience for both training and inference, aligning with the TensorFlow backend. +With DeePMD-kit v3, we have expanded support to include two additional backends alongside TensorFlow: the PyTorch backend and the framework-independent backend (dpmodel). The PyTorch backend adopts a highly modularized design to provide flexibility and extensibility. It ensures a consistent experience for both training and inference, aligning with the TensorFlow backend. -The DPModel backend is implemented in pure NumPy, serving as a reference backend to ensure consistency in tests. Its design pattern closely parallels that of the PyTorch backend. +The framework-independent backend is implemented in pure NumPy, serving as a reference backend to ensure consistency in tests. Its design pattern closely parallels that of the PyTorch backend. ### New descriptors @@ -109,20 +109,10 @@ The PyTorch backend's model architecture is meticulously structured with multipl Subsequently, the `AtomicModel` is encapsulated using the `make_model(AtomicModel)` function, which leverages the `deepmd.pt.model.model.make_model.make_model` function. The purpose of the `make_model` wrapper is to facilitate the translation between atomic property predictions and the extended property predictions and differentiation , e.g. the reduction of atomic energy contribution and the autodiff for calculating the forces and virial. The developers usually need to implement an `AtomicModel` not a `Model`. -Finally, the entire model is enveloped within a `DPModel`, necessitating inheritance from the {py:class}`deepmd.pt.model.model.model.BaseModel` class and the inclusion of the aforementioned `make_model(AtomicModel)`. In most cases, there is no need to reconstruct a `DPModel`; one can directly utilize the {py:class}`deepmd.pt.model.model.dp_model.DPModel` class by providing the corresponding fitting net. For models without a fitting net, like the `PairTableModel`, a new `DPModel` needs to be designed. Users seamlessly interact with a wrapper built on top of the `DPModel` to handle key translations of the returned dictionary. - - ```py from deepmd.pt.model.atomic_model.base_atomic_model import ( BaseAtomicModel, ) -from deepmd.pt.model.model.make_model import ( - make_model, -) -from deepmd.pt.model.model.model import ( - BaseModel, -) - class SomeAtomicModel(BaseAtomicModel, torch.nn.Module): def __init__(self, arg1: bool, arg2: float) -> None: @@ -131,14 +121,6 @@ class SomeAtomicModel(BaseAtomicModel, torch.nn.Module): def forward_atomic(self): pass - -@BaseModel.register("some_model") -class SomeDPModel(make_model(SomeAtomicModel), BaseModel): - pass - - -class SomeModel(SomeDPModel): - pass ``` ## Register new arguments @@ -174,6 +156,6 @@ The arguments here should be consistent with the class arguments of your new com ## Unit tests -When transferring features from another backend to the PyTorch backend, it is essential to include a regression test in `/source/tests/consistent` to validate the consistency of the PyTorch backend with other backends. Presently, the regression tests cover self-consistency and cross-backend consistency between TensorFlow, PyTorch, and DPModel (Numpy) through the serialization/deserialization technique. +When transferring features from another backend to the PyTorch backend, it is essential to include a regression test in `/source/tests/consistent` to validate the consistency of the PyTorch backend with other backends. Presently, the regression tests cover self-consistency and cross-backend consistency between TensorFlow, PyTorch, and dpmodel (Numpy) through the serialization/deserialization technique. -During the development of new components within the PyTorch backend, it is necessary to provide a DPModel (Numpy) implementation and incorporate corresponding regression tests. For PyTorch components, developers are also required to include a unit test using `torch.jit`. +During the development of new components within the PyTorch backend, it is necessary to provide a dpmodel (Numpy) implementation and incorporate corresponding regression tests. For PyTorch components, developers are also required to include a unit test using `torch.jit`. From 2df3853f5bed7fbe2941ebb5bc70966c5c363598 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 1 Mar 2024 11:03:26 +0000 Subject: [PATCH 11/11] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- doc/development/create-a-model-pt.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/development/create-a-model-pt.md b/doc/development/create-a-model-pt.md index 847e38f816..fdb1defe8a 100644 --- a/doc/development/create-a-model-pt.md +++ b/doc/development/create-a-model-pt.md @@ -114,13 +114,13 @@ from deepmd.pt.model.atomic_model.base_atomic_model import ( BaseAtomicModel, ) + class SomeAtomicModel(BaseAtomicModel, torch.nn.Module): def __init__(self, arg1: bool, arg2: float) -> None: pass def forward_atomic(self): pass - ``` ## Register new arguments