chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 12:40:42 +08:00
commit e25996e7db
15472 changed files with 3536181 additions and 0 deletions
+22
View File
@@ -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
+338
View File
@@ -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)
+466
View File
@@ -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)
+67
View File
@@ -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
+664
View File
@@ -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,
}
+211
View File
@@ -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()
+255
View File
@@ -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()