chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from . import callbacks, hub, logger, progressbar, static_flops # noqa: F401
|
||||
from .dynamic_flops import flops # noqa: F401
|
||||
from .model import Model # noqa: F401
|
||||
from .model_summary import summary # noqa: F401
|
||||
|
||||
logger.setup_logger()
|
||||
|
||||
__all__ = []
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,338 @@
|
||||
# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
from __future__ import annotations
|
||||
|
||||
import warnings
|
||||
from typing import TYPE_CHECKING, TypeAlias
|
||||
|
||||
import numpy as np
|
||||
|
||||
import paddle
|
||||
from paddle import nn
|
||||
from paddle.jit.dy2static.program_translator import unwrap_decorators
|
||||
|
||||
from .static_flops import Table, static_flops
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from collections.abc import Callable
|
||||
|
||||
from paddle import Tensor
|
||||
from paddle.nn import Layer
|
||||
from paddle.static import Program
|
||||
|
||||
_CustomOpsAlias: TypeAlias = dict[type[Layer], Callable[..., None]]
|
||||
|
||||
__all__ = []
|
||||
|
||||
|
||||
def flops(
|
||||
net: Layer | Program,
|
||||
input_size: list[int],
|
||||
custom_ops: _CustomOpsAlias | None = None,
|
||||
print_detail: bool = False,
|
||||
) -> int:
|
||||
"""Print a table about the FLOPs of network.
|
||||
|
||||
Args:
|
||||
net (paddle.nn.Layer||paddle.static.Program): The network which could be a instance of paddle.nn.Layer in
|
||||
dygraph or paddle.static.Program in static graph.
|
||||
input_size (list): size of input tensor. Note that the batch_size in argument ``input_size`` only support 1.
|
||||
custom_ops (A dict of function, optional): A dictionary which key is the class of specific operation such as
|
||||
paddle.nn.Conv2D and the value is the function used to count the FLOPs of this operation. This
|
||||
argument only work when argument ``net`` is an instance of paddle.nn.Layer. The details could be found
|
||||
in following example code. Default is None.
|
||||
print_detail (bool, optional): Whether to print the detail information, like FLOPs per layer, about the net FLOPs.
|
||||
Default is False.
|
||||
|
||||
Returns:
|
||||
Int: A number about the FLOPs of total network.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> import paddle.nn as nn
|
||||
|
||||
>>> class LeNet(nn.Layer):
|
||||
... def __init__(self, num_classes=10):
|
||||
... super().__init__()
|
||||
... self.num_classes = num_classes
|
||||
... self.features = nn.Sequential(
|
||||
... nn.Conv2D(1, 6, 3, stride=1, padding=1),
|
||||
... nn.ReLU(),
|
||||
... nn.MaxPool2D(2, 2),
|
||||
... nn.Conv2D(6, 16, 5, stride=1, padding=0),
|
||||
... nn.ReLU(),
|
||||
... nn.MaxPool2D(2, 2),
|
||||
... )
|
||||
...
|
||||
... if num_classes > 0:
|
||||
... self.fc = nn.Sequential(
|
||||
... nn.Linear(400, 120),
|
||||
... nn.Linear(120, 84),
|
||||
... nn.Linear(84, 10),
|
||||
... )
|
||||
...
|
||||
... def forward(self, inputs):
|
||||
... x = self.features(inputs)
|
||||
...
|
||||
... if self.num_classes > 0:
|
||||
... x = paddle.flatten(x, 1)
|
||||
... x = self.fc(x)
|
||||
... return x
|
||||
>>> lenet = LeNet()
|
||||
>>> # m is the instance of nn.Layer, x is the input of layer, y is the output of layer.
|
||||
>>> def count_leaky_relu(m, x, y):
|
||||
... x = x[0]
|
||||
... nelements = x.numel()
|
||||
... m.total_ops += int(nelements)
|
||||
>>> FLOPs = paddle.flops(
|
||||
... lenet,
|
||||
... [1, 1, 28, 28],
|
||||
... custom_ops={nn.LeakyReLU: count_leaky_relu},
|
||||
... print_detail=True,
|
||||
... )
|
||||
>>> # doctest: +SKIP('numpy print with different version')
|
||||
<class 'paddle.nn.layer.conv.Conv2D'>'s flops has been counted
|
||||
<class 'paddle.nn.layer.activation.ReLU'>'s flops has been counted
|
||||
Cannot find suitable count function for <class 'paddle.nn.layer.pooling.MaxPool2D'>. Treat it as zero FLOPs.
|
||||
<class 'paddle.nn.layer.common.Linear'>'s flops has been counted
|
||||
+--------------+-----------------+-----------------+--------+--------+
|
||||
| Layer Name | Input Shape | Output Shape | Params | Flops |
|
||||
+--------------+-----------------+-----------------+--------+--------+
|
||||
| conv2d_0 | [1, 1, 28, 28] | [1, 6, 28, 28] | 60 | 47040 |
|
||||
| re_lu_0 | [1, 6, 28, 28] | [1, 6, 28, 28] | 0 | 0 |
|
||||
| max_pool2d_0 | [1, 6, 28, 28] | [1, 6, 14, 14] | 0 | 0 |
|
||||
| conv2d_1 | [1, 6, 14, 14] | [1, 16, 10, 10] | 2416 | 241600 |
|
||||
| re_lu_1 | [1, 16, 10, 10] | [1, 16, 10, 10] | 0 | 0 |
|
||||
| max_pool2d_1 | [1, 16, 10, 10] | [1, 16, 5, 5] | 0 | 0 |
|
||||
| linear_0 | [1, 400] | [1, 120] | 48120 | 48000 |
|
||||
| linear_1 | [1, 120] | [1, 84] | 10164 | 10080 |
|
||||
| linear_2 | [1, 84] | [1, 10] | 850 | 840 |
|
||||
+--------------+-----------------+-----------------+--------+--------+
|
||||
Total Flops: 347560 Total Params: 61610
|
||||
>>> # doctest: -SKIP
|
||||
>>> print(FLOPs)
|
||||
347560
|
||||
"""
|
||||
if isinstance(net, nn.Layer):
|
||||
# If net is a dy2stat model, net.forward is StaticFunction instance,
|
||||
# we set net.forward to original forward function.
|
||||
_, net.forward = unwrap_decorators(net.forward)
|
||||
|
||||
inputs = paddle.randn(input_size)
|
||||
return dynamic_flops(
|
||||
net, inputs=inputs, custom_ops=custom_ops, print_detail=print_detail
|
||||
)
|
||||
elif isinstance(net, paddle.static.Program):
|
||||
return static_flops(net, print_detail=print_detail)
|
||||
else:
|
||||
warnings.warn(
|
||||
"Your model must be an instance of paddle.nn.Layer or paddle.static.Program."
|
||||
)
|
||||
return -1
|
||||
|
||||
|
||||
def count_convNd(m, x, y):
|
||||
x = x[0]
|
||||
kernel_ops = np.prod(m.weight.shape[2:])
|
||||
bias_ops = 1 if m.bias is not None else 0
|
||||
total_ops = int(y.numel()) * (
|
||||
x.shape[1] / m._groups * kernel_ops + bias_ops
|
||||
)
|
||||
m.total_ops += abs(int(total_ops))
|
||||
|
||||
|
||||
def count_leaky_relu(m, x, y):
|
||||
x = x[0]
|
||||
nelements = x.numel()
|
||||
m.total_ops += int(nelements)
|
||||
|
||||
|
||||
def count_bn(m, x, y):
|
||||
x = x[0]
|
||||
nelements = x.numel()
|
||||
if not m.training:
|
||||
total_ops = 2 * nelements
|
||||
m.total_ops += abs(int(total_ops))
|
||||
|
||||
|
||||
def count_linear(m, x, y):
|
||||
total_mul = m.weight.shape[0]
|
||||
num_elements = y.numel()
|
||||
total_ops = total_mul * num_elements
|
||||
m.total_ops += abs(int(total_ops))
|
||||
|
||||
|
||||
def count_avgpool(m, x, y):
|
||||
kernel_ops = 1
|
||||
num_elements = y.numel()
|
||||
total_ops = kernel_ops * num_elements
|
||||
|
||||
m.total_ops += int(total_ops)
|
||||
|
||||
|
||||
def count_adap_avgpool(m, x, y):
|
||||
kernel = np.array(x[0].shape[2:]) // np.array(y.shape[2:])
|
||||
total_add = np.prod(kernel)
|
||||
total_div = 1
|
||||
kernel_ops = total_add + total_div
|
||||
num_elements = y.numel()
|
||||
total_ops = kernel_ops * num_elements
|
||||
m.total_ops += abs(int(total_ops))
|
||||
|
||||
|
||||
def count_zero_ops(m, x, y):
|
||||
m.total_ops += 0
|
||||
|
||||
|
||||
def count_parameters(m, x, y):
|
||||
total_params = 0
|
||||
for p in m.parameters():
|
||||
total_params += p.numel()
|
||||
m.total_params[0] = abs(int(total_params))
|
||||
|
||||
|
||||
def count_io_info(m, x, y):
|
||||
m.register_buffer('input_shape', paddle.to_tensor(x[0].shape))
|
||||
if isinstance(y, (list, tuple)):
|
||||
m.register_buffer('output_shape', paddle.to_tensor(y[0].shape))
|
||||
else:
|
||||
m.register_buffer('output_shape', paddle.to_tensor(y.shape))
|
||||
|
||||
|
||||
register_hooks = {
|
||||
nn.Conv1D: count_convNd,
|
||||
nn.Conv2D: count_convNd,
|
||||
nn.Conv3D: count_convNd,
|
||||
nn.Conv1DTranspose: count_convNd,
|
||||
nn.Conv2DTranspose: count_convNd,
|
||||
nn.Conv3DTranspose: count_convNd,
|
||||
nn.layer.norm.BatchNorm2D: count_bn,
|
||||
nn.BatchNorm: count_bn,
|
||||
nn.ReLU: count_zero_ops,
|
||||
nn.ReLU6: count_zero_ops,
|
||||
nn.LeakyReLU: count_leaky_relu,
|
||||
nn.Linear: count_linear,
|
||||
nn.Dropout: count_zero_ops,
|
||||
nn.AvgPool1D: count_avgpool,
|
||||
nn.AvgPool2D: count_avgpool,
|
||||
nn.AvgPool3D: count_avgpool,
|
||||
nn.AdaptiveAvgPool1D: count_adap_avgpool,
|
||||
nn.AdaptiveAvgPool2D: count_adap_avgpool,
|
||||
nn.AdaptiveAvgPool3D: count_adap_avgpool,
|
||||
}
|
||||
|
||||
|
||||
def dynamic_flops(
|
||||
model: Layer,
|
||||
inputs: Tensor,
|
||||
custom_ops: _CustomOpsAlias | None = None,
|
||||
print_detail: bool = False,
|
||||
) -> int:
|
||||
handler_collection = []
|
||||
types_collection = set()
|
||||
if custom_ops is None:
|
||||
custom_ops = {}
|
||||
|
||||
def add_hooks(m):
|
||||
if len(list(m.children())) > 0:
|
||||
return
|
||||
m.register_buffer('total_ops', paddle.zeros([1], dtype='int64'))
|
||||
m.register_buffer('total_params', paddle.zeros([1], dtype='int64'))
|
||||
m_type = type(m)
|
||||
|
||||
flops_fn = None
|
||||
if m_type in custom_ops:
|
||||
flops_fn = custom_ops[m_type]
|
||||
if m_type not in types_collection:
|
||||
print(f"Customize Function has been applied to {m_type}")
|
||||
elif m_type in register_hooks:
|
||||
flops_fn = register_hooks[m_type]
|
||||
if m_type not in types_collection:
|
||||
print(f"{m_type}'s flops has been counted")
|
||||
else:
|
||||
if m_type not in types_collection:
|
||||
print(
|
||||
f"Cannot find suitable count function for {m_type}. Treat it as zero FLOPs."
|
||||
)
|
||||
|
||||
if flops_fn is not None:
|
||||
flops_handler = m.register_forward_post_hook(flops_fn)
|
||||
handler_collection.append(flops_handler)
|
||||
params_handler = m.register_forward_post_hook(count_parameters)
|
||||
io_handler = m.register_forward_post_hook(count_io_info)
|
||||
handler_collection.append(params_handler)
|
||||
handler_collection.append(io_handler)
|
||||
types_collection.add(m_type)
|
||||
|
||||
training = model.training
|
||||
|
||||
model.eval()
|
||||
model.apply(add_hooks)
|
||||
|
||||
with paddle.framework.no_grad():
|
||||
model(inputs)
|
||||
|
||||
total_ops = 0
|
||||
total_params = 0
|
||||
for m in model.sublayers():
|
||||
if len(list(m.children())) > 0:
|
||||
continue
|
||||
if {
|
||||
'total_ops',
|
||||
'total_params',
|
||||
'input_shape',
|
||||
'output_shape',
|
||||
}.issubset(set(m._buffers.keys())):
|
||||
total_ops += m.total_ops
|
||||
total_params += m.total_params
|
||||
|
||||
if training:
|
||||
model.train()
|
||||
for handler in handler_collection:
|
||||
handler.remove()
|
||||
|
||||
table = Table(
|
||||
["Layer Name", "Input Shape", "Output Shape", "Params", "Flops"]
|
||||
)
|
||||
|
||||
for n, m in model.named_sublayers():
|
||||
if len(list(m.children())) > 0:
|
||||
continue
|
||||
if {
|
||||
'total_ops',
|
||||
'total_params',
|
||||
'input_shape',
|
||||
'output_shape',
|
||||
}.issubset(set(m._buffers.keys())):
|
||||
table.add_row(
|
||||
[
|
||||
m.full_name(),
|
||||
m.input_shape.numpy().tolist(),
|
||||
m.output_shape.numpy().tolist(),
|
||||
int(m.total_params),
|
||||
int(m.total_ops),
|
||||
]
|
||||
)
|
||||
m._buffers.pop("total_ops")
|
||||
m._buffers.pop("total_params")
|
||||
m._buffers.pop('input_shape')
|
||||
m._buffers.pop('output_shape')
|
||||
if print_detail:
|
||||
table.print_table()
|
||||
print(
|
||||
f'Total Flops: {int(total_ops)} Total Params: {int(total_params)}'
|
||||
)
|
||||
return int(total_ops)
|
||||
@@ -0,0 +1,466 @@
|
||||
# Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import contextlib
|
||||
import errno
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
import warnings
|
||||
import zipfile
|
||||
from typing import TYPE_CHECKING, Literal, TypeAlias
|
||||
from urllib.parse import urlparse
|
||||
|
||||
import paddle
|
||||
from paddle.utils.download import (
|
||||
_download,
|
||||
_safe_extract_zip,
|
||||
get_path_from_url,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import builtins
|
||||
from typing import Any
|
||||
|
||||
__all__ = []
|
||||
|
||||
_Source: TypeAlias = Literal["github", "gitee", "local"]
|
||||
|
||||
DEFAULT_CACHE_DIR: str = '~/.cache'
|
||||
VAR_DEPENDENCY: str = 'dependencies'
|
||||
MODULE_HUBCONF: str = 'hubconf.py'
|
||||
HUB_DIR: str = os.path.expanduser(os.path.join('~', '.cache', 'paddle', 'hub'))
|
||||
|
||||
|
||||
def _remove_if_exists(path):
|
||||
if os.path.exists(path):
|
||||
if os.path.isfile(path):
|
||||
os.remove(path)
|
||||
else:
|
||||
shutil.rmtree(path)
|
||||
|
||||
|
||||
def _import_module(name, repo_dir):
|
||||
sys.path.insert(0, repo_dir)
|
||||
try:
|
||||
hub_module = __import__(name)
|
||||
sys.modules.pop(name)
|
||||
except ImportError:
|
||||
sys.path.remove(repo_dir)
|
||||
raise RuntimeError(
|
||||
'Please make sure config exists or repo error messages above fixed when importing'
|
||||
)
|
||||
|
||||
sys.path.remove(repo_dir)
|
||||
|
||||
return hub_module
|
||||
|
||||
|
||||
def _git_archive_link(repo_owner, repo_name, branch, source):
|
||||
if source == 'github':
|
||||
return (
|
||||
f'https://github.com/{repo_owner}/{repo_name}/archive/{branch}.zip'
|
||||
)
|
||||
elif source == 'gitee':
|
||||
return f'https://gitee.com/{repo_owner}/{repo_name}/repository/archive/{branch}.zip'
|
||||
|
||||
|
||||
def _parse_repo_info(repo, source):
|
||||
branch = 'main' if source == 'github' else 'master'
|
||||
if ':' in repo:
|
||||
repo_info, branch = repo.split(':')
|
||||
else:
|
||||
repo_info = repo
|
||||
repo_owner, repo_name = repo_info.split('/')
|
||||
return repo_owner, repo_name, branch
|
||||
|
||||
|
||||
def _make_dirs(dirname):
|
||||
try:
|
||||
from pathlib import Path
|
||||
except ImportError:
|
||||
from pathlib2 import Path
|
||||
Path(dirname).mkdir(exist_ok=True)
|
||||
|
||||
|
||||
def _get_cache_or_reload(repo, force_reload, verbose=True, source='github'):
|
||||
# Setup hub_dir to save downloaded files
|
||||
hub_dir = HUB_DIR
|
||||
|
||||
_make_dirs(hub_dir)
|
||||
|
||||
# Parse github/gitee repo information
|
||||
repo_owner, repo_name, branch = _parse_repo_info(repo, source)
|
||||
# Github allows branch name with slash '/',
|
||||
# this causes confusion with path on both Linux and Windows.
|
||||
# Backslash is not allowed in Github branch name so no need to
|
||||
# to worry about it.
|
||||
normalized_br = branch.replace('/', '_')
|
||||
# Github renames folder repo/v1.x.x to repo-1.x.x
|
||||
# We don't know the repo name before downloading the zip file
|
||||
# and inspect name from it.
|
||||
# To check if cached repo exists, we need to normalize folder names.
|
||||
repo_dir = os.path.join(
|
||||
hub_dir, '_'.join([repo_owner, repo_name, normalized_br])
|
||||
)
|
||||
|
||||
use_cache = (not force_reload) and os.path.exists(repo_dir)
|
||||
|
||||
if use_cache:
|
||||
if verbose:
|
||||
sys.stderr.write(f'Using cache found in {repo_dir}\n')
|
||||
else:
|
||||
cached_file = os.path.join(hub_dir, normalized_br + '.zip')
|
||||
_remove_if_exists(cached_file)
|
||||
|
||||
url = _git_archive_link(repo_owner, repo_name, branch, source=source)
|
||||
|
||||
fpath = get_path_from_url(
|
||||
url,
|
||||
hub_dir,
|
||||
check_exist=not force_reload,
|
||||
decompress=False,
|
||||
)
|
||||
shutil.move(fpath, cached_file)
|
||||
|
||||
with zipfile.ZipFile(cached_file) as cached_zipfile:
|
||||
extracted_repo_name = cached_zipfile.infolist()[0].filename
|
||||
extracted_repo = os.path.join(hub_dir, extracted_repo_name)
|
||||
_remove_if_exists(extracted_repo)
|
||||
# Unzip the code and rename the base folder
|
||||
_safe_extract_zip(cached_zipfile, hub_dir)
|
||||
|
||||
_remove_if_exists(cached_file)
|
||||
_remove_if_exists(repo_dir)
|
||||
# Rename the repo
|
||||
shutil.move(extracted_repo, repo_dir)
|
||||
|
||||
return repo_dir
|
||||
|
||||
|
||||
def _load_entry_from_hubconf(m, name):
|
||||
'''load entry from hubconf'''
|
||||
if not isinstance(name, str):
|
||||
raise ValueError(
|
||||
'Invalid input: model should be a str of function name'
|
||||
)
|
||||
|
||||
func = getattr(m, name, None)
|
||||
|
||||
if func is None or not callable(func):
|
||||
raise RuntimeError(f'Cannot find callable {name} in hubconf')
|
||||
|
||||
return func
|
||||
|
||||
|
||||
def _check_module_exists(name):
|
||||
try:
|
||||
__import__(name)
|
||||
return True
|
||||
except ImportError:
|
||||
return False
|
||||
|
||||
|
||||
def _check_dependencies(m):
|
||||
dependencies = getattr(m, VAR_DEPENDENCY, None)
|
||||
|
||||
if dependencies is not None:
|
||||
missing_deps = [
|
||||
pkg for pkg in dependencies if not _check_module_exists(pkg)
|
||||
]
|
||||
if len(missing_deps):
|
||||
raise RuntimeError(
|
||||
'Missing dependencies: {}'.format(', '.join(missing_deps))
|
||||
)
|
||||
|
||||
|
||||
def list(
|
||||
repo_dir: str,
|
||||
source: _Source = 'github',
|
||||
force_reload: bool = False,
|
||||
) -> builtins.list[str]:
|
||||
r"""
|
||||
List all entrypoints available in `github` hubconf.
|
||||
|
||||
Args:
|
||||
repo_dir(str): Github or local path.
|
||||
|
||||
- github path (str): A string with format "repo_owner/repo_name[:tag_name]" with an optional
|
||||
tag/branch. The default branch is `main` if not specified.
|
||||
- local path (str): Local repo path.
|
||||
|
||||
source (str): `github` | `gitee` | `local`. Default is `github`.
|
||||
force_reload (bool, optional): Whether to discard the existing cache and force a fresh download. Default is `False`.
|
||||
|
||||
Returns:
|
||||
entrypoints: A list of available entrypoint names.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
|
||||
>>> paddle.hub.list('lyuwenyu/paddlehub_demo:main', source='github', force_reload=False)
|
||||
|
||||
"""
|
||||
if source not in ('github', 'gitee', 'local'):
|
||||
raise ValueError(
|
||||
f'Unknown source: "{source}". Allowed values: "github" | "gitee" | "local".'
|
||||
)
|
||||
|
||||
if source in ('github', 'gitee'):
|
||||
repo_dir = _get_cache_or_reload(
|
||||
repo_dir, force_reload, True, source=source
|
||||
)
|
||||
|
||||
hub_module = _import_module(MODULE_HUBCONF.split('.')[0], repo_dir)
|
||||
|
||||
entrypoints = [
|
||||
f
|
||||
for f in dir(hub_module)
|
||||
if callable(getattr(hub_module, f)) and not f.startswith('_')
|
||||
]
|
||||
|
||||
return entrypoints
|
||||
|
||||
|
||||
def help(
|
||||
repo_dir: str,
|
||||
model,
|
||||
source: _Source = 'github',
|
||||
force_reload: bool = False,
|
||||
) -> str:
|
||||
"""
|
||||
Show help information of model
|
||||
|
||||
Args:
|
||||
repo_dir(str): Github or local path.
|
||||
|
||||
- github path (str): A string with format "repo_owner/repo_name[:tag_name]" with an optional
|
||||
tag/branch. The default branch is `main` if not specified.
|
||||
- local path (str): Local repo path.
|
||||
|
||||
model (str): Model name.
|
||||
source (str): `github` | `gitee` | `local`. Default is `github`.
|
||||
force_reload (bool, optional): Default is `False`.
|
||||
|
||||
Returns:
|
||||
docs
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
|
||||
>>> paddle.hub.help('lyuwenyu/paddlehub_demo:main', model='MM', source='github')
|
||||
|
||||
"""
|
||||
if source not in ('github', 'gitee', 'local'):
|
||||
raise ValueError(
|
||||
f'Unknown source: "{source}". Allowed values: "github" | "gitee" | "local".'
|
||||
)
|
||||
|
||||
if source in ('github', 'gitee'):
|
||||
repo_dir = _get_cache_or_reload(
|
||||
repo_dir, force_reload, True, source=source
|
||||
)
|
||||
|
||||
hub_module = _import_module(MODULE_HUBCONF.split('.')[0], repo_dir)
|
||||
|
||||
entry = _load_entry_from_hubconf(hub_module, model)
|
||||
|
||||
return entry.__doc__
|
||||
|
||||
|
||||
def load(
|
||||
repo_dir: str,
|
||||
model: str,
|
||||
source: _Source = 'github',
|
||||
force_reload: bool = False,
|
||||
**kwargs: Any,
|
||||
) -> paddle.nn.Layer:
|
||||
"""
|
||||
Load model
|
||||
|
||||
Args:
|
||||
repo_dir(str): Github or local path.
|
||||
|
||||
- github path (str): A string with format "repo_owner/repo_name[:tag_name]" with an optional
|
||||
tag/branch. The default branch is `main` if not specified.
|
||||
- local path (str): Local repo path.
|
||||
|
||||
model (str): Model name.
|
||||
source (str): `github` | `gitee` | `local`. Default is `github`.
|
||||
force_reload (bool, optional): Default is `False`.
|
||||
**kwargs: Parameters using for model.
|
||||
|
||||
Returns:
|
||||
paddle model.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> paddle.hub.load('lyuwenyu/paddlehub_demo:main', model='MM', source='github')
|
||||
|
||||
"""
|
||||
if source not in ('github', 'gitee', 'local'):
|
||||
raise ValueError(
|
||||
f'Unknown source: "{source}". Allowed values: "github" | "gitee" | "local".'
|
||||
)
|
||||
|
||||
if source in ('github', 'gitee'):
|
||||
repo_dir = _get_cache_or_reload(
|
||||
repo_dir, force_reload, True, source=source
|
||||
)
|
||||
|
||||
hub_module = _import_module(MODULE_HUBCONF.split('.')[0], repo_dir)
|
||||
|
||||
_check_dependencies(hub_module)
|
||||
|
||||
entry = _load_entry_from_hubconf(hub_module, model)
|
||||
|
||||
return entry(**kwargs)
|
||||
|
||||
|
||||
def load_state_dict_from_url(
|
||||
url: str,
|
||||
model_dir: str | None = None,
|
||||
check_hash: bool = False,
|
||||
file_name: str | None = None,
|
||||
map_location: (
|
||||
Literal["cpu", "gpu", "xpu", "npu", "numpy", "np"] | None
|
||||
) = None,
|
||||
) -> Any:
|
||||
"""Download Paddle's model weights (i.e., state_dict)
|
||||
from the specified URL and extract the downloaded file if necessary
|
||||
|
||||
Args:
|
||||
url (str): URL of the object to download.
|
||||
model_dir (Optional[str], optional): Directory in which to save the object.
|
||||
check_hash (bool, optional): If True, the filename part of the URL should follow the naming convention filename-<sha256>.ext where <sha256> is the first eight or more digits of the SHA256 hash of the contents of the file. The hash is used to ensure unique names and to verify the contents of the file. Default: False.
|
||||
file_name (Optional[str], optional): Name for the downloaded file. Filename from URL will be used if not set.
|
||||
map_location (Optional[Literal["cpu", "gpu", "xpu", "npu", "numpy", "np"]], optional): Specifies how to remap storage locations. Default: None.
|
||||
|
||||
Returns:
|
||||
Any: A target object that can be used in Paddle.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> # doctest: +TIMEOUT(60)
|
||||
>>> import paddle
|
||||
>>> paddle.hub.load_state_dict_from_url(
|
||||
... url='https://paddle-hapi.bj.bcebos.com/models/resnet18.pdparams', model_dir="./paddle/test_load_from_url"
|
||||
... )
|
||||
>>> paddle.hub.load_state_dict_from_url(
|
||||
... url='https://x2paddle.bj.bcebos.com/resnet18.zip', model_dir="./paddle/test_file_is_zip"
|
||||
... )
|
||||
"""
|
||||
if model_dir is None:
|
||||
hub_dir = get_dir()
|
||||
model_dir = os.path.join(hub_dir, 'checkpoints')
|
||||
|
||||
try:
|
||||
os.makedirs(model_dir)
|
||||
except OSError as e:
|
||||
if e.errno == errno.EEXIST:
|
||||
pass
|
||||
else:
|
||||
raise
|
||||
parts = urlparse(url)
|
||||
filename = os.path.basename(parts.path)
|
||||
if file_name is not None:
|
||||
filename = file_name
|
||||
cached_file = os.path.join(model_dir, filename)
|
||||
if not os.path.exists(cached_file):
|
||||
sys.stderr.write(f'Downloading: "{url}" to {cached_file}\n')
|
||||
hash_prefix = None
|
||||
if check_hash:
|
||||
hash_prefix = check_hash
|
||||
_download(url, model_dir, hash_prefix)
|
||||
|
||||
if map_location:
|
||||
assert map_location in ["cpu", "gpu", "xpu", "npu", "numpy", "np"]
|
||||
if _is_legacy_zip_format(cached_file):
|
||||
return _legacy_zip_load(cached_file, model_dir, map_location)
|
||||
if map_location in ["numpy", "np"]:
|
||||
return paddle.load(cached_file, return_numpy=True)
|
||||
else:
|
||||
with device_guard(map_location):
|
||||
return paddle.load(cached_file)
|
||||
else:
|
||||
if _is_legacy_zip_format(cached_file):
|
||||
return _legacy_zip_load(cached_file, model_dir)
|
||||
return paddle.load(cached_file)
|
||||
|
||||
|
||||
def _is_legacy_zip_format(filename):
|
||||
if zipfile.is_zipfile(filename):
|
||||
infolist = zipfile.ZipFile(filename).infolist()
|
||||
return len(infolist) == 1 and not infolist[0].is_dir()
|
||||
return False
|
||||
|
||||
|
||||
def _legacy_zip_load(filename, model_dir, map_location=None):
|
||||
with zipfile.ZipFile(filename) as f:
|
||||
members = f.infolist()
|
||||
if len(members) != 1:
|
||||
raise RuntimeError(
|
||||
'Only one file(not dir) is allowed in the zipfile'
|
||||
)
|
||||
_safe_extract_zip(f, model_dir)
|
||||
extracted_name = members[0].filename
|
||||
extracted_file = os.path.join(model_dir, extracted_name)
|
||||
if map_location:
|
||||
if map_location in ["numpy", "np"]:
|
||||
return paddle.load(extracted_file, return_numpy=True)
|
||||
else:
|
||||
with device_guard(map_location):
|
||||
return paddle.load(extracted_file)
|
||||
else:
|
||||
return paddle.load(extracted_file)
|
||||
|
||||
|
||||
def get_dir():
|
||||
if os.getenv('PADDLE_HUB'):
|
||||
warnings.warn(
|
||||
'PADDLE_HUB is deprecated, please use env PADDLE_HOME instead'
|
||||
)
|
||||
return os.path.join(_get_paddle_home(), 'hub')
|
||||
|
||||
|
||||
def _get_paddle_home():
|
||||
paddle_home = os.path.expanduser(
|
||||
os.getenv(
|
||||
'PADDLE_HOME',
|
||||
os.path.join(os.getenv('XDG_CACHE_HOME', '~/.cache'), 'paddle'),
|
||||
)
|
||||
)
|
||||
return paddle_home
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def device_guard(device="cpu", dev_id=0):
|
||||
origin_device = paddle.device.get_device()
|
||||
if device == "cpu":
|
||||
paddle.set_device(device)
|
||||
elif device in ["gpu", "xpu", "npu"]:
|
||||
paddle.set_device(f"{device}:{dev_id}")
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
paddle.set_device(origin_device)
|
||||
@@ -0,0 +1,67 @@
|
||||
# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
|
||||
__all__ = []
|
||||
|
||||
|
||||
def setup_logger(output=None, name="hapi", log_level=logging.INFO):
|
||||
"""
|
||||
Initialize logger of hapi and set its verbosity level to "INFO".
|
||||
|
||||
Args:
|
||||
output (str): a file name or a directory to save log. If None, will not save log file.
|
||||
If ends with ".txt" or ".log", assumed to be a file name.
|
||||
Otherwise, logs will be saved to `output/log.txt`.
|
||||
name (str): the root module name of this logger. Default: 'hapi'.
|
||||
log_level (enum): log level. eg.'INFO', 'DEBUG', 'ERROR'. Default: logging.INFO.
|
||||
Returns:
|
||||
logging.Logger: a logger
|
||||
"""
|
||||
logger = logging.getLogger(name)
|
||||
logger.propagate = False
|
||||
logger.setLevel(log_level)
|
||||
|
||||
format_str = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
||||
# stdout logging: only local rank==0
|
||||
local_rank = int(os.getenv("PADDLE_TRAINER_ID", "0"))
|
||||
if local_rank == 0 and len(logger.handlers) == 0:
|
||||
ch = logging.StreamHandler(stream=sys.stdout)
|
||||
ch.setLevel(log_level)
|
||||
|
||||
ch.setFormatter(logging.Formatter(format_str))
|
||||
logger.addHandler(ch)
|
||||
|
||||
# file logging if output is not None: all workers
|
||||
if output is not None:
|
||||
if output.endswith(".txt") or output.endswith(".log"):
|
||||
filename = output
|
||||
else:
|
||||
filename = os.path.join(output, "log.txt")
|
||||
|
||||
if local_rank > 0:
|
||||
filename = filename + f".rank{local_rank}"
|
||||
|
||||
if not os.path.exists(os.path.dirname(filename)):
|
||||
os.makedirs(os.path.dirname(filename))
|
||||
|
||||
fh = logging.StreamHandler(filename)
|
||||
fh.setLevel(log_level)
|
||||
fh.setFormatter(logging.Formatter(format_str))
|
||||
logger.addHandler(fh)
|
||||
|
||||
return logger
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,664 @@
|
||||
# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import numbers
|
||||
import warnings
|
||||
from collections import OrderedDict
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import numpy as np
|
||||
from typing_extensions import TypedDict
|
||||
|
||||
import paddle
|
||||
from paddle import Tensor, nn
|
||||
from paddle.autograd import no_grad
|
||||
from paddle.static import InputSpec
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from collections.abc import Sequence
|
||||
|
||||
__all__ = []
|
||||
|
||||
|
||||
class ModelSummary(TypedDict):
|
||||
total_params: int
|
||||
trainable_params: int
|
||||
|
||||
|
||||
def summary(
|
||||
net: nn.Layer,
|
||||
input_size: (
|
||||
int
|
||||
| tuple[int, ...]
|
||||
| InputSpec
|
||||
| list[tuple[int, ...] | InputSpec]
|
||||
| None
|
||||
) = None,
|
||||
dtypes: str | Sequence[str] | None = None,
|
||||
input: Tensor | Sequence[Tensor] | dict[str, Tensor] | None = None,
|
||||
) -> ModelSummary:
|
||||
"""Prints a string summary of the network.
|
||||
|
||||
Args:
|
||||
net (Layer): The network which must be a subinstance of Layer.
|
||||
input_size (tuple|InputSpec|list[tuple|InputSpec]|None, optional): Size of input tensor. if model only
|
||||
have one input, input_size can be tuple or InputSpec. if model
|
||||
have multiple input, input_size must be a list which contain
|
||||
every input's shape. Note that input_size only dim of
|
||||
batch_size can be None or -1. Default: None. Note that
|
||||
input_size and input cannot be None at the same time.
|
||||
dtypes (str|Sequence[str]|None, optional): If dtypes is None, 'float32' will be used, Default: None.
|
||||
input (Tensor|Sequence[paddle.Tensor]|dict[str, paddle.Tensor]|None, optional): If input is given, input_size and dtype will be ignored, Default: None.
|
||||
|
||||
Returns:
|
||||
dict: A summary of the network including total params and total trainable params.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
:name: code-example-1
|
||||
|
||||
>>> # example 1: Single Input Demo
|
||||
>>> import paddle
|
||||
>>> import paddle.nn as nn
|
||||
>>> # Define Network
|
||||
>>> class LeNet(nn.Layer):
|
||||
... def __init__(self, num_classes=10):
|
||||
... super().__init__()
|
||||
... self.num_classes = num_classes
|
||||
... self.features = nn.Sequential(
|
||||
... nn.Conv2D(1, 6, 3, stride=1, padding=1),
|
||||
... nn.ReLU(),
|
||||
... nn.MaxPool2D(2, 2),
|
||||
... nn.Conv2D(6, 16, 5, stride=1, padding=0),
|
||||
... nn.ReLU(),
|
||||
... nn.MaxPool2D(2, 2),
|
||||
... )
|
||||
...
|
||||
... if num_classes > 0:
|
||||
... self.fc = nn.Sequential(nn.Linear(400, 120), nn.Linear(120, 84), nn.Linear(84, 10))
|
||||
...
|
||||
... def forward(self, inputs):
|
||||
... x = self.features(inputs)
|
||||
...
|
||||
... if self.num_classes > 0:
|
||||
... x = paddle.flatten(x, 1)
|
||||
... x = self.fc(x)
|
||||
... return x
|
||||
>>> lenet = LeNet()
|
||||
>>> params_info = paddle.summary(lenet, (1, 1, 28, 28)) # doctest: +NORMALIZE_WHITESPACE
|
||||
---------------------------------------------------------------------------
|
||||
Layer (type) Input Shape Output Shape Param #
|
||||
===========================================================================
|
||||
Conv2D-1 [[1, 1, 28, 28]] [1, 6, 28, 28] 60
|
||||
ReLU-1 [[1, 6, 28, 28]] [1, 6, 28, 28] 0
|
||||
MaxPool2D-1 [[1, 6, 28, 28]] [1, 6, 14, 14] 0
|
||||
Conv2D-2 [[1, 6, 14, 14]] [1, 16, 10, 10] 2,416
|
||||
ReLU-2 [[1, 16, 10, 10]] [1, 16, 10, 10] 0
|
||||
MaxPool2D-2 [[1, 16, 10, 10]] [1, 16, 5, 5] 0
|
||||
Linear-1 [[1, 400]] [1, 120] 48,120
|
||||
Linear-2 [[1, 120]] [1, 84] 10,164
|
||||
Linear-3 [[1, 84]] [1, 10] 850
|
||||
===========================================================================
|
||||
Total params: 61,610
|
||||
Trainable params: 61,610
|
||||
Non-trainable params: 0
|
||||
---------------------------------------------------------------------------
|
||||
Input size (MB): 0.00
|
||||
Forward/backward pass size (MB): 0.11
|
||||
Params size (MB): 0.24
|
||||
Estimated Total Size (MB): 0.35
|
||||
---------------------------------------------------------------------------
|
||||
<BLANKLINE>
|
||||
>>> print(params_info)
|
||||
{'total_params': 61610, 'trainable_params': 61610}
|
||||
|
||||
.. code-block:: pycon
|
||||
:name: code-example-2
|
||||
|
||||
>>> # example 2: multi input demo
|
||||
>>> import paddle
|
||||
>>> import paddle.nn as nn
|
||||
>>> class LeNetMultiInput(nn.Layer):
|
||||
... def __init__(self, num_classes=10):
|
||||
... super().__init__()
|
||||
... self.num_classes = num_classes
|
||||
... self.features = nn.Sequential(
|
||||
... nn.Conv2D(1, 6, 3, stride=1, padding=1),
|
||||
... nn.ReLU(),
|
||||
... nn.MaxPool2D(2, 2),
|
||||
... nn.Conv2D(6, 16, 5, stride=1, padding=0),
|
||||
... nn.ReLU(),
|
||||
... nn.MaxPool2D(2, 2),
|
||||
... )
|
||||
...
|
||||
... if num_classes > 0:
|
||||
... self.fc = nn.Sequential(nn.Linear(400, 120), nn.Linear(120, 84), nn.Linear(84, 10))
|
||||
...
|
||||
... def forward(self, inputs, y):
|
||||
... x = self.features(inputs)
|
||||
...
|
||||
... if self.num_classes > 0:
|
||||
... x = paddle.flatten(x, 1)
|
||||
... x = self.fc(x + y)
|
||||
... return x
|
||||
>>> lenet_multi_input = LeNetMultiInput()
|
||||
|
||||
>>> params_info = paddle.summary(
|
||||
... lenet_multi_input, [(1, 1, 28, 28), (1, 400)], dtypes=['float32', 'float32']
|
||||
... ) # doctest: +NORMALIZE_WHITESPACE
|
||||
---------------------------------------------------------------------------
|
||||
Layer (type) Input Shape Output Shape Param #
|
||||
===========================================================================
|
||||
Conv2D-1 [[1, 1, 28, 28]] [1, 6, 28, 28] 60
|
||||
ReLU-1 [[1, 6, 28, 28]] [1, 6, 28, 28] 0
|
||||
MaxPool2D-1 [[1, 6, 28, 28]] [1, 6, 14, 14] 0
|
||||
Conv2D-2 [[1, 6, 14, 14]] [1, 16, 10, 10] 2,416
|
||||
ReLU-2 [[1, 16, 10, 10]] [1, 16, 10, 10] 0
|
||||
MaxPool2D-2 [[1, 16, 10, 10]] [1, 16, 5, 5] 0
|
||||
Linear-1 [[1, 400]] [1, 120] 48,120
|
||||
Linear-2 [[1, 120]] [1, 84] 10,164
|
||||
Linear-3 [[1, 84]] [1, 10] 850
|
||||
===========================================================================
|
||||
Total params: 61,610
|
||||
Trainable params: 61,610
|
||||
Non-trainable params: 0
|
||||
---------------------------------------------------------------------------
|
||||
Input size (MB): 0.00
|
||||
Forward/backward pass size (MB): 0.11
|
||||
Params size (MB): 0.24
|
||||
Estimated Total Size (MB): 0.35
|
||||
---------------------------------------------------------------------------
|
||||
<BLANKLINE>
|
||||
>>> print(params_info)
|
||||
{'total_params': 61610, 'trainable_params': 61610}
|
||||
|
||||
.. code-block:: pycon
|
||||
:name: code-example-3
|
||||
|
||||
>>> # example 3: List Input Demo
|
||||
>>> import paddle
|
||||
>>> import paddle.nn as nn
|
||||
|
||||
>>> # list input demo
|
||||
>>> class LeNetListInput(nn.Layer):
|
||||
... def __init__(self, num_classes=10):
|
||||
... super().__init__()
|
||||
... self.num_classes = num_classes
|
||||
... self.features = nn.Sequential(
|
||||
... nn.Conv2D(1, 6, 3, stride=1, padding=1),
|
||||
... nn.ReLU(),
|
||||
... nn.MaxPool2D(2, 2),
|
||||
... nn.Conv2D(6, 16, 5, stride=1, padding=0),
|
||||
... nn.ReLU(),
|
||||
... nn.MaxPool2D(2, 2),
|
||||
... )
|
||||
...
|
||||
... if num_classes > 0:
|
||||
... self.fc = nn.Sequential(nn.Linear(400, 120), nn.Linear(120, 84), nn.Linear(84, 10))
|
||||
...
|
||||
... def forward(self, inputs):
|
||||
... x = self.features(inputs[0])
|
||||
...
|
||||
... if self.num_classes > 0:
|
||||
... x = paddle.flatten(x, 1)
|
||||
... x = self.fc(x + inputs[1])
|
||||
... return x
|
||||
>>> lenet_list_input = LeNetListInput()
|
||||
>>> input_data = [paddle.rand([1, 1, 28, 28]), paddle.rand([1, 400])]
|
||||
>>> params_info = paddle.summary(lenet_list_input, input=input_data) # doctest: +NORMALIZE_WHITESPACE
|
||||
---------------------------------------------------------------------------
|
||||
Layer (type) Input Shape Output Shape Param #
|
||||
===========================================================================
|
||||
Conv2D-1 [[1, 1, 28, 28]] [1, 6, 28, 28] 60
|
||||
ReLU-1 [[1, 6, 28, 28]] [1, 6, 28, 28] 0
|
||||
MaxPool2D-1 [[1, 6, 28, 28]] [1, 6, 14, 14] 0
|
||||
Conv2D-2 [[1, 6, 14, 14]] [1, 16, 10, 10] 2,416
|
||||
ReLU-2 [[1, 16, 10, 10]] [1, 16, 10, 10] 0
|
||||
MaxPool2D-2 [[1, 16, 10, 10]] [1, 16, 5, 5] 0
|
||||
Linear-1 [[1, 400]] [1, 120] 48,120
|
||||
Linear-2 [[1, 120]] [1, 84] 10,164
|
||||
Linear-3 [[1, 84]] [1, 10] 850
|
||||
===========================================================================
|
||||
Total params: 61,610
|
||||
Trainable params: 61,610
|
||||
Non-trainable params: 0
|
||||
---------------------------------------------------------------------------
|
||||
Input size (MB): 0.00
|
||||
Forward/backward pass size (MB): 0.11
|
||||
Params size (MB): 0.24
|
||||
Estimated Total Size (MB): 0.35
|
||||
---------------------------------------------------------------------------
|
||||
<BLANKLINE>
|
||||
>>> print(params_info)
|
||||
{'total_params': 61610, 'trainable_params': 61610}
|
||||
|
||||
|
||||
.. code-block:: pycon
|
||||
:name: code-example-4
|
||||
|
||||
>>> # example 4: Dict Input Demo
|
||||
>>> import paddle
|
||||
>>> import paddle.nn as nn
|
||||
|
||||
>>> # Dict input demo
|
||||
>>> class LeNetDictInput(nn.Layer):
|
||||
... def __init__(self, num_classes=10):
|
||||
... super().__init__()
|
||||
... self.num_classes = num_classes
|
||||
... self.features = nn.Sequential(
|
||||
... nn.Conv2D(1, 6, 3, stride=1, padding=1),
|
||||
... nn.ReLU(),
|
||||
... nn.MaxPool2D(2, 2),
|
||||
... nn.Conv2D(6, 16, 5, stride=1, padding=0),
|
||||
... nn.ReLU(),
|
||||
... nn.MaxPool2D(2, 2),
|
||||
... )
|
||||
...
|
||||
... if num_classes > 0:
|
||||
... self.fc = nn.Sequential(
|
||||
... nn.Linear(400, 120),
|
||||
... nn.Linear(120, 84),
|
||||
... nn.Linear(84, 10),
|
||||
... )
|
||||
...
|
||||
... def forward(self, inputs):
|
||||
... x = self.features(inputs['x1'])
|
||||
...
|
||||
... if self.num_classes > 0:
|
||||
... x = paddle.flatten(x, 1)
|
||||
... x = self.fc(x + inputs['x2'])
|
||||
... return x
|
||||
>>> lenet_dict_input = LeNetDictInput()
|
||||
>>> input_data = {'x1': paddle.rand([1, 1, 28, 28]), 'x2': paddle.rand([1, 400])}
|
||||
>>> # The module suffix number indicates its sequence in modules of the same type, used for differentiation identification
|
||||
>>> params_info = paddle.summary(lenet_dict_input, input=input_data) # doctest: +NORMALIZE_WHITESPACE
|
||||
---------------------------------------------------------------------------
|
||||
Layer (type) Input Shape Output Shape Param #
|
||||
===========================================================================
|
||||
Conv2D-1 [[1, 1, 28, 28]] [1, 6, 28, 28] 60
|
||||
ReLU-1 [[1, 6, 28, 28]] [1, 6, 28, 28] 0
|
||||
MaxPool2D-1 [[1, 6, 28, 28]] [1, 6, 14, 14] 0
|
||||
Conv2D-2 [[1, 6, 14, 14]] [1, 16, 10, 10] 2,416
|
||||
ReLU-2 [[1, 16, 10, 10]] [1, 16, 10, 10] 0
|
||||
MaxPool2D-2 [[1, 16, 10, 10]] [1, 16, 5, 5] 0
|
||||
Linear-1 [[1, 400]] [1, 120] 48,120
|
||||
Linear-2 [[1, 120]] [1, 84] 10,164
|
||||
Linear-3 [[1, 84]] [1, 10] 850
|
||||
===========================================================================
|
||||
Total params: 61,610
|
||||
Trainable params: 61,610
|
||||
Non-trainable params: 0
|
||||
---------------------------------------------------------------------------
|
||||
Input size (MB): 0.00
|
||||
Forward/backward pass size (MB): 0.11
|
||||
Params size (MB): 0.24
|
||||
Estimated Total Size (MB): 0.35
|
||||
---------------------------------------------------------------------------
|
||||
<BLANKLINE>
|
||||
>>> print(params_info)
|
||||
{'total_params': 61610, 'trainable_params': 61610}
|
||||
"""
|
||||
if input_size is None and input is None:
|
||||
raise ValueError("input_size and input cannot be None at the same time")
|
||||
|
||||
if input_size is None and input is not None:
|
||||
if paddle.is_tensor(input):
|
||||
input_size = tuple(input.shape)
|
||||
elif isinstance(input, (list, tuple)):
|
||||
input_size = []
|
||||
for x in input:
|
||||
input_size.append(tuple(x.shape))
|
||||
elif isinstance(input, dict):
|
||||
input_size = []
|
||||
for key in input.keys():
|
||||
input_size.append(tuple(input[key].shape))
|
||||
elif isinstance(input, paddle.base.framework.Variable):
|
||||
input_size = tuple(input.shape)
|
||||
else:
|
||||
raise ValueError(
|
||||
"Input is not tensor, list, tuple and dict, unable to determine input_size, please input input_size."
|
||||
)
|
||||
|
||||
if isinstance(input_size, InputSpec):
|
||||
_input_size = tuple(input_size.shape)
|
||||
elif isinstance(input_size, list):
|
||||
_input_size = []
|
||||
for item in input_size:
|
||||
if isinstance(item, int):
|
||||
item = (item,)
|
||||
assert isinstance(item, (tuple, InputSpec)), (
|
||||
f'When input_size is list, \
|
||||
expect item in input_size is a tuple or InputSpec, but got {type(item)}'
|
||||
)
|
||||
|
||||
if isinstance(item, InputSpec):
|
||||
_input_size.append(tuple(item.shape))
|
||||
else:
|
||||
_input_size.append(item)
|
||||
elif isinstance(input_size, int):
|
||||
_input_size = (input_size,)
|
||||
else:
|
||||
_input_size = input_size
|
||||
|
||||
if not paddle.in_dynamic_mode():
|
||||
warnings.warn(
|
||||
"Your model was created in static graph mode, this may not get correct summary information!"
|
||||
)
|
||||
in_train_mode = False
|
||||
else:
|
||||
in_train_mode = net.training
|
||||
|
||||
if in_train_mode:
|
||||
net.eval()
|
||||
|
||||
def _is_shape(shape):
|
||||
for item in shape:
|
||||
if isinstance(item, (list, tuple)):
|
||||
return False
|
||||
return True
|
||||
|
||||
def _check_shape(shape):
|
||||
num_unknown = 0
|
||||
new_shape = []
|
||||
for i in range(len(shape)):
|
||||
item = shape[i]
|
||||
if item is None or item == -1:
|
||||
num_unknown += 1
|
||||
if num_unknown > 1:
|
||||
raise ValueError(
|
||||
'Option input_size only the dim of batch_size can be None or -1.'
|
||||
)
|
||||
item = 1
|
||||
elif isinstance(item, numbers.Number):
|
||||
if item <= 0:
|
||||
raise ValueError(
|
||||
f"Expected element in input size greater than zero, but got {item}"
|
||||
)
|
||||
new_shape.append(item)
|
||||
return tuple(new_shape)
|
||||
|
||||
def _check_input(input_size):
|
||||
if isinstance(input_size, (list, tuple)) and _is_shape(input_size):
|
||||
return _check_shape(input_size)
|
||||
else:
|
||||
return [_check_input(i) for i in input_size]
|
||||
|
||||
_input_size = _check_input(_input_size)
|
||||
|
||||
result, params_info = summary_string(net, _input_size, dtypes, input)
|
||||
print(result)
|
||||
|
||||
if in_train_mode:
|
||||
net.train()
|
||||
|
||||
return params_info
|
||||
|
||||
|
||||
@no_grad()
|
||||
def summary_string(model, input_size=None, dtypes=None, input=None):
|
||||
def _all_is_number(items):
|
||||
for item in items:
|
||||
if not isinstance(item, numbers.Number):
|
||||
return False
|
||||
return True
|
||||
|
||||
def _build_dtypes(input_size, dtype):
|
||||
if dtype is None:
|
||||
dtype = 'float32'
|
||||
|
||||
if isinstance(input_size, (list, tuple)) and _all_is_number(input_size):
|
||||
return [dtype]
|
||||
else:
|
||||
return [_build_dtypes(i, dtype) for i in input_size]
|
||||
|
||||
if not isinstance(dtypes, (list, tuple)):
|
||||
dtypes = _build_dtypes(input_size, dtypes)
|
||||
|
||||
summary_str = ''
|
||||
|
||||
depth = len(list(model.sublayers()))
|
||||
|
||||
def _get_shape_from_tensor(x):
|
||||
if isinstance(x, (paddle.base.Variable, paddle.base.core.eager.Tensor)):
|
||||
return list(x.shape)
|
||||
elif isinstance(x, (list, tuple)):
|
||||
return [_get_shape_from_tensor(xx) for xx in x]
|
||||
|
||||
def _get_output_shape(output):
|
||||
if isinstance(output, (list, tuple)):
|
||||
output_shape = [_get_output_shape(o) for o in output]
|
||||
elif hasattr(output, 'shape'):
|
||||
output_shape = list(output.shape)
|
||||
else:
|
||||
output_shape = []
|
||||
return output_shape
|
||||
|
||||
def register_hook(layer):
|
||||
def hook(layer, input, output):
|
||||
class_name = str(layer.__class__).split(".")[-1].split("'")[0]
|
||||
|
||||
try:
|
||||
layer_idx = int(layer._full_name.split('_')[-1])
|
||||
except:
|
||||
layer_idx = len(summary)
|
||||
|
||||
m_key = f"{class_name}-{layer_idx + 1}"
|
||||
summary[m_key] = OrderedDict()
|
||||
|
||||
try:
|
||||
summary[m_key]["input_shape"] = _get_shape_from_tensor(input)
|
||||
except:
|
||||
warnings.warn('Get layer {} input shape failed!')
|
||||
summary[m_key]["input_shape"] = []
|
||||
|
||||
try:
|
||||
summary[m_key]["output_shape"] = _get_output_shape(output)
|
||||
except:
|
||||
warnings.warn('Get layer {} output shape failed!')
|
||||
summary[m_key]["output_shape"]
|
||||
|
||||
params = 0
|
||||
|
||||
if paddle.in_dynamic_mode():
|
||||
layer_state_dict = layer._parameters
|
||||
else:
|
||||
layer_state_dict = layer.state_dict()
|
||||
|
||||
summary[m_key]["trainable_params"] = 0
|
||||
trainable_flag = False
|
||||
for k, v in layer_state_dict.items():
|
||||
params += int(np.prod(v.shape))
|
||||
|
||||
try:
|
||||
if (getattr(layer, k).trainable) and (
|
||||
not getattr(layer, k).stop_gradient
|
||||
):
|
||||
summary[m_key]["trainable_params"] += int(
|
||||
np.prod(v.shape)
|
||||
)
|
||||
summary[m_key]["trainable"] = True
|
||||
trainable_flag = True
|
||||
elif not trainable_flag:
|
||||
summary[m_key]["trainable"] = False
|
||||
except:
|
||||
summary[m_key]["trainable"] = True
|
||||
|
||||
summary[m_key]["nb_params"] = params
|
||||
|
||||
if (
|
||||
not isinstance(layer, nn.Sequential)
|
||||
and not isinstance(layer, nn.LayerList)
|
||||
and (not (layer == model) or depth < 1)
|
||||
):
|
||||
hooks.append(layer.register_forward_post_hook(hook))
|
||||
# For rnn, gru and lstm layer
|
||||
elif hasattr(layer, 'could_use_cudnn') and layer.could_use_cudnn:
|
||||
hooks.append(layer.register_forward_post_hook(hook))
|
||||
|
||||
if isinstance(input_size, tuple):
|
||||
input_size = [input_size]
|
||||
|
||||
def build_input(input_size, dtypes):
|
||||
if isinstance(input_size, (list, tuple)) and _all_is_number(input_size):
|
||||
if isinstance(dtypes, (list, tuple)):
|
||||
dtype = dtypes[0]
|
||||
else:
|
||||
dtype = dtypes
|
||||
return paddle.cast(paddle.rand(list(input_size)), dtype)
|
||||
else:
|
||||
return [
|
||||
build_input(i, dtype) for i, dtype in zip(input_size, dtypes)
|
||||
]
|
||||
|
||||
# create properties
|
||||
summary = OrderedDict()
|
||||
hooks = []
|
||||
# register hook
|
||||
model.apply(register_hook)
|
||||
if input is not None:
|
||||
x = input
|
||||
model(x)
|
||||
else:
|
||||
x = build_input(input_size, dtypes)
|
||||
# make a forward pass
|
||||
model(*x)
|
||||
|
||||
# remove these hooks
|
||||
for h in hooks:
|
||||
h.remove()
|
||||
|
||||
def _get_str_length(summary):
|
||||
head_length = {
|
||||
'layer_width': 15,
|
||||
'input_shape_width': 20,
|
||||
'output_shape_width': 20,
|
||||
'params_width': 15,
|
||||
'table_width': 75,
|
||||
}
|
||||
|
||||
for layer in summary:
|
||||
if head_length['output_shape_width'] < len(
|
||||
str(summary[layer]["output_shape"])
|
||||
):
|
||||
head_length['output_shape_width'] = len(
|
||||
str(summary[layer]["output_shape"])
|
||||
)
|
||||
if head_length['input_shape_width'] < len(
|
||||
str(summary[layer]["input_shape"])
|
||||
):
|
||||
head_length['input_shape_width'] = len(
|
||||
str(summary[layer]["input_shape"])
|
||||
)
|
||||
if head_length['layer_width'] < len(str(layer)):
|
||||
head_length['layer_width'] = len(str(layer))
|
||||
if head_length['params_width'] < len(
|
||||
str(summary[layer]["nb_params"])
|
||||
):
|
||||
head_length['params_width'] = len(
|
||||
str(summary[layer]["nb_params"])
|
||||
)
|
||||
|
||||
_temp_width = 0
|
||||
for k, v in head_length.items():
|
||||
if k != 'table_width':
|
||||
_temp_width += v
|
||||
|
||||
if head_length['table_width'] < _temp_width + 5:
|
||||
head_length['table_width'] = _temp_width + 5
|
||||
|
||||
return head_length
|
||||
|
||||
table_width = _get_str_length(summary)
|
||||
|
||||
summary_str += "-" * table_width['table_width'] + "\n"
|
||||
line_new = "{:^{}} {:^{}} {:^{}} {:^{}}".format(
|
||||
"Layer (type)",
|
||||
table_width['layer_width'],
|
||||
"Input Shape",
|
||||
table_width['input_shape_width'],
|
||||
"Output Shape",
|
||||
table_width['output_shape_width'],
|
||||
"Param #",
|
||||
table_width['params_width'],
|
||||
)
|
||||
summary_str += line_new + "\n"
|
||||
summary_str += "=" * table_width['table_width'] + "\n"
|
||||
total_params = 0
|
||||
total_output = 0
|
||||
trainable_params = 0
|
||||
max_length = 0
|
||||
for layer in summary:
|
||||
# input_shape, output_shape, trainable, nb_params
|
||||
line_new = "{:^{}} {:^{}} {:^{}} {:^{}}".format(
|
||||
layer,
|
||||
table_width['layer_width'],
|
||||
str(summary[layer]["input_shape"]),
|
||||
table_width['input_shape_width'],
|
||||
str(summary[layer]["output_shape"]),
|
||||
table_width['output_shape_width'],
|
||||
"{:,}".format(summary[layer]["nb_params"]),
|
||||
table_width['params_width'],
|
||||
)
|
||||
total_params += summary[layer]["nb_params"]
|
||||
|
||||
try:
|
||||
total_output += int(
|
||||
np.sum(np.prod(summary[layer]["output_shape"], axis=-1))
|
||||
)
|
||||
except:
|
||||
for output_shape in summary[layer]["output_shape"]:
|
||||
total_output += int(np.sum(np.prod(output_shape, axis=-1)))
|
||||
|
||||
if "trainable" in summary[layer]:
|
||||
if summary[layer]["trainable"]:
|
||||
trainable_params += summary[layer]["trainable_params"]
|
||||
summary_str += line_new + "\n"
|
||||
|
||||
def _get_input_size(input_size, size):
|
||||
if isinstance(input_size, (list, tuple)) and _all_is_number(input_size):
|
||||
size = abs(int(np.prod(input_size)) * 4.0 / (1024**2.0))
|
||||
else:
|
||||
size = sum([_get_input_size(i, size) for i in input_size])
|
||||
return size
|
||||
|
||||
total_input_size = _get_input_size(input_size, 0)
|
||||
|
||||
total_output_size = abs(
|
||||
2.0 * total_output * 4.0 / (1024**2.0)
|
||||
) # x2 for gradients
|
||||
total_params_size = abs(total_params * 4.0 / (1024**2.0))
|
||||
total_size = total_params_size + total_output_size + total_input_size
|
||||
|
||||
summary_str += "=" * table_width['table_width'] + "\n"
|
||||
summary_str += f"Total params: {total_params:,}" + "\n"
|
||||
summary_str += f"Trainable params: {trainable_params:,}" + "\n"
|
||||
summary_str += (
|
||||
f"Non-trainable params: {total_params - trainable_params:,}" + "\n"
|
||||
)
|
||||
summary_str += "-" * table_width['table_width'] + "\n"
|
||||
summary_str += f"Input size (MB): {total_input_size:0.2f}" + "\n"
|
||||
summary_str += (
|
||||
f"Forward/backward pass size (MB): {total_output_size:0.2f}" + "\n"
|
||||
)
|
||||
summary_str += f"Params size (MB): {total_params_size:0.2f}" + "\n"
|
||||
summary_str += f"Estimated Total Size (MB): {total_size:0.2f}" + "\n"
|
||||
summary_str += "-" * table_width['table_width'] + "\n"
|
||||
|
||||
# return summary
|
||||
return summary_str, {
|
||||
'total_params': total_params,
|
||||
'trainable_params': trainable_params,
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import os
|
||||
import struct
|
||||
import sys
|
||||
import time
|
||||
|
||||
import numpy as np
|
||||
|
||||
__all__ = []
|
||||
|
||||
|
||||
class ProgressBar:
|
||||
"""progress bar"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
num=None,
|
||||
width=30,
|
||||
verbose=1,
|
||||
start=True,
|
||||
file=sys.stdout,
|
||||
name='step',
|
||||
):
|
||||
self._num = num
|
||||
if isinstance(num, int) and num <= 0:
|
||||
raise TypeError('num should be None or integer (> 0)')
|
||||
max_width = self._get_max_width()
|
||||
self._width = min(width, max_width)
|
||||
self._total_width = 0
|
||||
self._verbose = verbose
|
||||
self.file = file
|
||||
self._values = {}
|
||||
self._values_order = []
|
||||
if start:
|
||||
self._start = time.time()
|
||||
self._last_update = 0
|
||||
self.name = name
|
||||
|
||||
self._dynamic_display = (
|
||||
(hasattr(self.file, 'isatty') and self.file.isatty())
|
||||
or 'ipykernel' in sys.modules
|
||||
or 'posix' in sys.modules
|
||||
or 'PYCHARM_HOSTED' in os.environ
|
||||
)
|
||||
|
||||
def _get_max_width(self):
|
||||
from shutil import get_terminal_size
|
||||
|
||||
terminal_width, _ = get_terminal_size()
|
||||
terminal_width = terminal_width if terminal_width > 0 else 80
|
||||
max_width = min(int(terminal_width * 0.6), terminal_width - 50)
|
||||
return max_width
|
||||
|
||||
def start(self):
|
||||
self.file.flush()
|
||||
self._start = time.time()
|
||||
|
||||
def update(self, current_num, values={}):
|
||||
now = time.time()
|
||||
|
||||
def convert_uint16_to_float(in_list):
|
||||
in_list = np.asarray(in_list)
|
||||
out = np.vectorize(
|
||||
lambda x: struct.unpack('<f', struct.pack('<I', x << 16))[0],
|
||||
otypes=[np.float32],
|
||||
)(in_list.flat)
|
||||
return np.reshape(out, in_list.shape)
|
||||
|
||||
for i, (k, val) in enumerate(values):
|
||||
if k == "loss":
|
||||
if isinstance(val, list):
|
||||
scalar_val = val[0]
|
||||
else:
|
||||
scalar_val = val
|
||||
if isinstance(scalar_val, np.uint16):
|
||||
values[i] = ("loss", list(convert_uint16_to_float(val)))
|
||||
|
||||
if current_num:
|
||||
time_per_unit = (now - self._start) / current_num
|
||||
else:
|
||||
time_per_unit = 0
|
||||
|
||||
if time_per_unit >= 1 or time_per_unit == 0:
|
||||
fps = f' - {time_per_unit:.0f}s/{self.name}'
|
||||
elif time_per_unit >= 1e-3:
|
||||
fps = f' - {time_per_unit * 1e3:.0f}ms/{self.name}'
|
||||
else:
|
||||
fps = f' - {time_per_unit * 1e6:.0f}us/{self.name}'
|
||||
|
||||
info = ''
|
||||
if self._verbose == 1:
|
||||
prev_total_width = self._total_width
|
||||
|
||||
if self._dynamic_display:
|
||||
sys.stdout.write('\b' * prev_total_width)
|
||||
sys.stdout.write('\r')
|
||||
else:
|
||||
sys.stdout.write('\n')
|
||||
|
||||
if self._num is not None:
|
||||
numdigits = int(np.log10(self._num)) + 1
|
||||
|
||||
bar_chars = (self.name + ' %' + str(numdigits) + 'd/%d [') % (
|
||||
current_num,
|
||||
self._num,
|
||||
)
|
||||
prog = float(current_num) / self._num
|
||||
prog_width = int(self._width * prog)
|
||||
|
||||
if prog_width > 0:
|
||||
bar_chars += '=' * (prog_width - 1)
|
||||
if current_num < self._num:
|
||||
bar_chars += '>'
|
||||
else:
|
||||
bar_chars += '='
|
||||
bar_chars += '.' * (self._width - prog_width)
|
||||
bar_chars += ']'
|
||||
else:
|
||||
bar_chars = f'{self.name} {current_num:3}'
|
||||
|
||||
self._total_width = len(bar_chars)
|
||||
sys.stdout.write(bar_chars)
|
||||
|
||||
for k, val in values:
|
||||
info += f' - {k}:'
|
||||
val = val if isinstance(val, list) else [val]
|
||||
for i, v in enumerate(val):
|
||||
if isinstance(v, (float, np.float32, np.float64)):
|
||||
if abs(v) > 1e-3:
|
||||
info += f' {v:.4f}'
|
||||
else:
|
||||
info += f' {v:.4e}'
|
||||
else:
|
||||
info += f' {v}'
|
||||
|
||||
if self._num is not None and current_num < self._num:
|
||||
eta = int(time_per_unit * (self._num - current_num))
|
||||
if eta > 3600:
|
||||
eta_format = (
|
||||
f'{eta // 3600}:{(eta % 3600) // 60:02}:{eta % 60:02}'
|
||||
)
|
||||
elif eta > 60:
|
||||
eta_format = f'{eta // 60}:{eta % 60:02}'
|
||||
else:
|
||||
eta_format = f'{eta}s'
|
||||
|
||||
info += f' - ETA: {eta_format}'
|
||||
|
||||
info += fps
|
||||
self._total_width += len(info)
|
||||
if prev_total_width > self._total_width:
|
||||
info += ' ' * (prev_total_width - self._total_width)
|
||||
|
||||
# newline for another epoch
|
||||
if self._num is not None and current_num >= self._num:
|
||||
info += '\n'
|
||||
if self._num is None:
|
||||
info += '\n'
|
||||
|
||||
sys.stdout.write(info)
|
||||
sys.stdout.flush()
|
||||
self._last_update = now
|
||||
elif self._verbose == 2 or self._verbose == 3:
|
||||
if self._num:
|
||||
numdigits = int(np.log10(self._num)) + 1
|
||||
count = (self.name + ' %' + str(numdigits) + 'd/%d') % (
|
||||
current_num,
|
||||
self._num,
|
||||
)
|
||||
else:
|
||||
count = f'{self.name} {current_num:3}'
|
||||
info = count + info
|
||||
|
||||
for k, val in values:
|
||||
info += f' - {k}:'
|
||||
val = val if isinstance(val, list) else [val]
|
||||
for v in val:
|
||||
if isinstance(v, (float, np.float32, np.float64)):
|
||||
if abs(v) > 1e-3:
|
||||
info += f' {v:.4f}'
|
||||
else:
|
||||
info += f' {v:.4e}'
|
||||
elif (
|
||||
isinstance(v, np.ndarray)
|
||||
and v.size == 1
|
||||
and v.dtype in [np.float32, np.float64]
|
||||
):
|
||||
if abs(v.item()) > 1e-3:
|
||||
info += f' {v.item():.4f}'
|
||||
else:
|
||||
info += f' {v.item():.4e}'
|
||||
else:
|
||||
info += f' {v}'
|
||||
|
||||
info += fps
|
||||
info += '\n'
|
||||
sys.stdout.write(info)
|
||||
sys.stdout.flush()
|
||||
@@ -0,0 +1,255 @@
|
||||
# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from collections import OrderedDict
|
||||
|
||||
import numpy as np
|
||||
|
||||
from paddle.static import Program, Variable
|
||||
|
||||
__all__ = []
|
||||
|
||||
|
||||
class VarWrapper:
|
||||
def __init__(self, var, graph):
|
||||
assert isinstance(var, Variable)
|
||||
assert isinstance(graph, GraphWrapper)
|
||||
self._var = var
|
||||
self._graph = graph
|
||||
|
||||
def name(self):
|
||||
"""
|
||||
Get the name of the variable.
|
||||
"""
|
||||
return self._var.name
|
||||
|
||||
def shape(self):
|
||||
"""
|
||||
Get the shape of the variable.
|
||||
"""
|
||||
return self._var.shape
|
||||
|
||||
|
||||
class OpWrapper:
|
||||
def __init__(self, op, graph):
|
||||
assert isinstance(graph, GraphWrapper)
|
||||
self._op = op
|
||||
self._graph = graph
|
||||
|
||||
def type(self):
|
||||
"""
|
||||
Get the type of this operator.
|
||||
"""
|
||||
return self._op.type
|
||||
|
||||
def inputs(self, name):
|
||||
"""
|
||||
Get all the variables by the input name.
|
||||
"""
|
||||
if name in self._op.input_names:
|
||||
return [
|
||||
self._graph.var(var_name) for var_name in self._op.input(name)
|
||||
]
|
||||
else:
|
||||
return []
|
||||
|
||||
def outputs(self, name):
|
||||
"""
|
||||
Get all the variables by the output name.
|
||||
"""
|
||||
return [self._graph.var(var_name) for var_name in self._op.output(name)]
|
||||
|
||||
|
||||
class GraphWrapper:
|
||||
"""
|
||||
It is a wrapper of paddle.base.framework.IrGraph with some special functions
|
||||
for paddle slim framework.
|
||||
|
||||
Args:
|
||||
program(framework.Program): A program with
|
||||
in_nodes(dict): A dict to indicate the input nodes of the graph.
|
||||
The key is user-defined and human-readable name.
|
||||
The value is the name of Variable.
|
||||
out_nodes(dict): A dict to indicate the input nodes of the graph.
|
||||
The key is user-defined and human-readable name.
|
||||
The value is the name of Variable.
|
||||
"""
|
||||
|
||||
def __init__(self, program=None, in_nodes=[], out_nodes=[]):
|
||||
""" """
|
||||
super().__init__()
|
||||
self.program = Program() if program is None else program
|
||||
self.persistables = {}
|
||||
self.teacher_persistables = {}
|
||||
for var in self.program.list_vars():
|
||||
if var.persistable:
|
||||
self.persistables[var.name] = var
|
||||
self.compiled_graph = None
|
||||
in_nodes = [] if in_nodes is None else in_nodes
|
||||
out_nodes = [] if out_nodes is None else out_nodes
|
||||
self.in_nodes = OrderedDict(in_nodes)
|
||||
self.out_nodes = OrderedDict(out_nodes)
|
||||
self._attrs = OrderedDict()
|
||||
|
||||
def ops(self):
|
||||
"""
|
||||
Return all operator nodes included in the graph as a set.
|
||||
"""
|
||||
ops = []
|
||||
for block in self.program.blocks:
|
||||
for op in block.ops:
|
||||
ops.append(OpWrapper(op, self))
|
||||
return ops
|
||||
|
||||
def var(self, name):
|
||||
"""
|
||||
Get the variable by variable name.
|
||||
"""
|
||||
for block in self.program.blocks:
|
||||
if block.has_var(name):
|
||||
return VarWrapper(block.var(name), self)
|
||||
return None
|
||||
|
||||
|
||||
def count_convNd(op):
|
||||
filter_shape = op.inputs("Filter")[0].shape()
|
||||
filter_ops = np.prod(filter_shape[1:])
|
||||
bias_ops = 1 if len(op.inputs("Bias")) > 0 else 0
|
||||
output_numel = np.prod(op.outputs("Output")[0].shape()[1:])
|
||||
total_ops = output_numel * (filter_ops + bias_ops)
|
||||
total_ops = abs(total_ops)
|
||||
return total_ops
|
||||
|
||||
|
||||
def count_leaky_relu(op):
|
||||
total_ops = np.prod(op.outputs("Output")[0].shape()[1:])
|
||||
return total_ops
|
||||
|
||||
|
||||
def count_bn(op):
|
||||
output_numel = np.prod(op.outputs("Y")[0].shape()[1:])
|
||||
total_ops = 2 * output_numel
|
||||
total_ops = abs(total_ops)
|
||||
return total_ops
|
||||
|
||||
|
||||
def count_linear(op):
|
||||
total_mul = op.inputs("Y")[0].shape()[0]
|
||||
numel = np.prod(op.outputs("Out")[0].shape()[1:])
|
||||
total_ops = total_mul * numel
|
||||
total_ops = abs(total_ops)
|
||||
return total_ops
|
||||
|
||||
|
||||
def count_pool2d(op):
|
||||
input_shape = op.inputs("X")[0].shape()
|
||||
output_shape = op.outputs('Out')[0].shape()
|
||||
kernel = np.array(input_shape[2:]) // np.array(output_shape[2:])
|
||||
total_add = np.prod(kernel)
|
||||
total_div = 1
|
||||
kernel_ops = total_add + total_div
|
||||
num_elements = np.prod(output_shape[1:])
|
||||
total_ops = kernel_ops * num_elements
|
||||
total_ops = abs(total_ops)
|
||||
return total_ops
|
||||
|
||||
|
||||
def count_element_op(op):
|
||||
input_shape = op.inputs("X")[0].shape()
|
||||
total_ops = np.prod(input_shape[1:])
|
||||
total_ops = abs(total_ops)
|
||||
return total_ops
|
||||
|
||||
|
||||
def _graph_flops(graph, detail=False):
|
||||
assert isinstance(graph, GraphWrapper)
|
||||
flops = 0
|
||||
op_flops = 0
|
||||
table = Table(["OP Type", 'Param name', "Flops"])
|
||||
for op in graph.ops():
|
||||
param_name = ''
|
||||
if op.type() in ['conv2d', 'depthwise_conv2d']:
|
||||
op_flops = count_convNd(op)
|
||||
flops += op_flops
|
||||
param_name = op.inputs("Filter")[0].name()
|
||||
elif op.type() == 'pool2d':
|
||||
op_flops = count_pool2d(op)
|
||||
flops += op_flops
|
||||
|
||||
elif op.type() in ['mul', 'matmul']:
|
||||
op_flops = count_linear(op)
|
||||
flops += op_flops
|
||||
param_name = op.inputs("Y")[0].name()
|
||||
elif op.type() == 'batch_norm':
|
||||
op_flops = count_bn(op)
|
||||
flops += op_flops
|
||||
elif op.type().startswith('element'):
|
||||
op_flops = count_element_op(op)
|
||||
flops += op_flops
|
||||
if op_flops != 0:
|
||||
table.add_row([op.type(), param_name, op_flops])
|
||||
op_flops = 0
|
||||
if detail:
|
||||
table.print_table()
|
||||
return flops
|
||||
|
||||
|
||||
def static_flops(program, print_detail=False):
|
||||
graph = GraphWrapper(program)
|
||||
return _graph_flops(graph, detail=print_detail)
|
||||
|
||||
|
||||
class Table:
|
||||
def __init__(self, table_heads):
|
||||
self.table_heads = table_heads
|
||||
self.table_len = []
|
||||
self.data = []
|
||||
self.col_num = len(table_heads)
|
||||
for head in table_heads:
|
||||
self.table_len.append(len(head))
|
||||
|
||||
def add_row(self, row_str):
|
||||
if not isinstance(row_str, list):
|
||||
print('The row_str should be a list')
|
||||
if len(row_str) != self.col_num:
|
||||
print(
|
||||
f'The length of row data should be equal the length of table heads, but the data: {len(row_str)} is not equal table heads {self.col_num}'
|
||||
)
|
||||
for i in range(self.col_num):
|
||||
if len(str(row_str[i])) > self.table_len[i]:
|
||||
self.table_len[i] = len(str(row_str[i]))
|
||||
self.data.append(row_str)
|
||||
|
||||
def print_row(self, row):
|
||||
string = ''
|
||||
for i in range(self.col_num):
|
||||
string += '|' + str(row[i]).center(self.table_len[i] + 2)
|
||||
string += '|'
|
||||
print(string)
|
||||
|
||||
def print_shelf(self):
|
||||
string = ''
|
||||
for length in self.table_len:
|
||||
string += '+'
|
||||
string += '-' * (length + 2)
|
||||
string += '+'
|
||||
print(string)
|
||||
|
||||
def print_table(self):
|
||||
self.print_shelf()
|
||||
self.print_row(self.table_heads)
|
||||
self.print_shelf()
|
||||
for data in self.data:
|
||||
self.print_row(data)
|
||||
self.print_shelf()
|
||||
Reference in New Issue
Block a user